summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ABOUT-NLS1101
-rw-r--r--LICENSE201
-rw-r--r--Makefile.am40
-rw-r--r--Makefile.in885
-rw-r--r--NOTICE96
-rw-r--r--README147
-rw-r--r--VERSION1
-rw-r--r--aclocal.m41005
-rw-r--r--alpine/Makefile.am48
-rw-r--r--alpine/Makefile.in823
-rw-r--r--alpine/addrbook.c7433
-rw-r--r--alpine/addrbook.h69
-rw-r--r--alpine/adrbkcmd.c7694
-rw-r--r--alpine/adrbkcmd.h64
-rw-r--r--alpine/after.c276
-rw-r--r--alpine/after.h44
-rw-r--r--alpine/alpine.c3587
-rw-r--r--alpine/alpine.h41
-rw-r--r--alpine/arg.c1325
-rw-r--r--alpine/arg.h52
-rwxr-xr-xalpine/brk2pine.sh67
-rw-r--r--alpine/busy.c473
-rw-r--r--alpine/busy.h30
-rw-r--r--alpine/colorconf.c2788
-rw-r--r--alpine/colorconf.h79
-rw-r--r--alpine/confscroll.c5968
-rw-r--r--alpine/confscroll.h114
-rw-r--r--alpine/conftype.h141
-rw-r--r--alpine/context.c1019
-rw-r--r--alpine/context.h51
-rw-r--r--alpine/dispfilt.c454
-rw-r--r--alpine/dispfilt.h30
-rw-r--r--alpine/flagmaint.c332
-rw-r--r--alpine/flagmaint.h55
-rw-r--r--alpine/folder.c6922
-rw-r--r--alpine/folder.h39
-rw-r--r--alpine/headers.h68
-rw-r--r--alpine/help.c1408
-rw-r--r--alpine/help.h51
-rw-r--r--alpine/imap.c3011
-rw-r--r--alpine/imap.h51
-rw-r--r--alpine/init.c396
-rw-r--r--alpine/init.h27
-rw-r--r--alpine/kblock.c196
-rw-r--r--alpine/kblock.h27
-rw-r--r--alpine/keymenu.c3961
-rw-r--r--alpine/keymenu.h670
-rwxr-xr-xalpine/ldap32.dllbin0 -> 138752 bytes
-rw-r--r--alpine/ldapconf.c2432
-rw-r--r--alpine/ldapconf.h34
-rw-r--r--alpine/listsel.c332
-rw-r--r--alpine/listsel.h47
-rw-r--r--alpine/mailcmd.c9472
-rw-r--r--alpine/mailcmd.h107
-rw-r--r--alpine/mailindx.c3644
-rw-r--r--alpine/mailindx.h114
-rw-r--r--alpine/mailpart.c3981
-rw-r--r--alpine/mailpart.h43
-rwxr-xr-xalpine/mailtrfc.sh124
-rw-r--r--alpine/mailview.c5662
-rw-r--r--alpine/mailview.h129
-rw-r--r--alpine/makefile.wnt89
-rw-r--r--alpine/newmail.c333
-rw-r--r--alpine/newmail.h22
-rw-r--r--alpine/newuser.c204
-rw-r--r--alpine/newuser.h28
-rw-r--r--alpine/osdep/Makefile.am22
-rw-r--r--alpine/osdep/Makefile.in544
-rw-r--r--alpine/osdep/ReadMe14
-rw-r--r--alpine/osdep/alpine-splash.bmpbin0 -> 532014 bytes
-rw-r--r--alpine/osdep/alpine.icobin0 -> 3262 bytes
-rw-r--r--alpine/osdep/chnge_pw.c65
-rw-r--r--alpine/osdep/chnge_pw.h25
-rw-r--r--alpine/osdep/debuging.c604
-rw-r--r--alpine/osdep/debuging.h30
-rw-r--r--alpine/osdep/diskquot78
-rw-r--r--alpine/osdep/diskquot.a328
-rw-r--r--alpine/osdep/diskquot.a416
-rw-r--r--alpine/osdep/diskquot.hpp9
-rw-r--r--alpine/osdep/diskquot.non.c46
-rw-r--r--alpine/osdep/diskquot.ptx9
-rw-r--r--alpine/osdep/diskquot.sgi8
-rw-r--r--alpine/osdep/diskquot.so59
-rw-r--r--alpine/osdep/diskquot.sun8
-rw-r--r--alpine/osdep/diskquot.sv49
-rw-r--r--alpine/osdep/execview.c555
-rw-r--r--alpine/osdep/execview.h27
-rw-r--r--alpine/osdep/fltrname.c119
-rw-r--r--alpine/osdep/fltrname.h26
-rw-r--r--alpine/osdep/jobcntrl.c55
-rw-r--r--alpine/osdep/jobcntrl.h25
-rw-r--r--alpine/osdep/makefile.wnt66
-rw-r--r--alpine/osdep/mclosed.icobin0 -> 3262 bytes
-rw-r--r--alpine/osdep/mswin.def17
-rw-r--r--alpine/osdep/mswin.rc605
-rw-r--r--alpine/osdep/mswinver.c74
-rw-r--r--alpine/osdep/newmail.icobin0 -> 3262 bytes
-rw-r--r--alpine/osdep/print.c529
-rw-r--r--alpine/osdep/print.h28
-rw-r--r--alpine/osdep/resource.h1
-rw-r--r--alpine/osdep/solquota174
-rw-r--r--alpine/osdep/sunquota136
-rw-r--r--alpine/osdep/termin.gen.c1252
-rw-r--r--alpine/osdep/termin.gen.h48
-rw-r--r--alpine/osdep/termin.unx.c752
-rw-r--r--alpine/osdep/termin.unx.h29
-rw-r--r--alpine/osdep/termin.wnt.c352
-rw-r--r--alpine/osdep/termin.wnt.h32
-rw-r--r--alpine/osdep/termout.gen.c584
-rw-r--r--alpine/osdep/termout.gen.h54
-rw-r--r--alpine/osdep/termout.unx.c1002
-rw-r--r--alpine/osdep/termout.unx.h24
-rw-r--r--alpine/osdep/termout.win127
-rw-r--r--alpine/osdep/termout.wnt.c1862
-rw-r--r--alpine/osdep/termout.wnt.h47
-rw-r--r--alpine/osdep/windlg.h10
-rw-r--r--alpine/pattern.c223
-rw-r--r--alpine/pattern.h25
-rw-r--r--alpine/pine-use.c180
-rw-r--r--alpine/pine-win.lnk11
-rw-r--r--alpine/pipe.c150
-rw-r--r--alpine/pipe.h29
-rw-r--r--alpine/print.c1288
-rw-r--r--alpine/print.h30
-rw-r--r--alpine/radio.c918
-rw-r--r--alpine/radio.h81
-rw-r--r--alpine/remote.c347
-rw-r--r--alpine/remote.h27
-rw-r--r--alpine/reply.c2549
-rw-r--r--alpine/reply.h44
-rw-r--r--alpine/roleconf.c8187
-rw-r--r--alpine/roleconf.h33
-rw-r--r--alpine/rpdump.c818
-rw-r--r--alpine/rpload.c1008
-rw-r--r--alpine/send.c7155
-rw-r--r--alpine/send.h53
-rw-r--r--alpine/setup.c1131
-rw-r--r--alpine/setup.h32
-rw-r--r--alpine/signal.c921
-rw-r--r--alpine/signal.h44
-rw-r--r--alpine/smime.c1151
-rw-r--r--alpine/smime.h34
-rw-r--r--alpine/status.c1282
-rw-r--r--alpine/status.h32
-rw-r--r--alpine/takeaddr.c3520
-rw-r--r--alpine/takeaddr.h40
-rw-r--r--alpine/talk.h37
-rw-r--r--alpine/titlebar.c1236
-rw-r--r--alpine/titlebar.h55
-rw-r--r--autom4te.cache/output.021685
-rw-r--r--autom4te.cache/output.121687
-rw-r--r--autom4te.cache/output.221687
-rw-r--r--autom4te.cache/requests666
-rw-r--r--autom4te.cache/traces.06015
-rw-r--r--autom4te.cache/traces.11893
-rw-r--r--autom4te.cache/traces.26015
-rw-r--r--build.bat223
-rwxr-xr-xconfig.guess1511
-rwxr-xr-xconfig.rpath614
-rwxr-xr-xconfig.sub1705
-rwxr-xr-xconfigure21687
-rw-r--r--configure.ac1665
-rw-r--r--contrib/bitmaps/pine.icon43
-rwxr-xr-xcontrib/bitmaps/pine2.icon33
-rw-r--r--contrib/bitmaps/unixware/README20
-rw-r--r--contrib/bitmaps/unixware/pine.cdb11
-rw-r--r--contrib/bitmaps/unixware/pine.icon45
-rw-r--r--contrib/bitmaps/unixware/pine48.icon59
-rw-r--r--contrib/carmel/README34
-rw-r--r--contrib/carmel/c-client/Makefile.patch49
-rw-r--r--contrib/carmel/c-client/bzk2cml.c103
-rw-r--r--contrib/carmel/c-client/carmel-purge.sh89
-rw-r--r--contrib/carmel/c-client/carmel.c1232
-rw-r--r--contrib/carmel/c-client/carmel.h77
-rw-r--r--contrib/carmel/c-client/carmel2.c4412
-rw-r--r--contrib/carmel/c-client/carmel2.h200
-rw-r--r--contrib/carmel/doc/carmel-driver157
-rw-r--r--contrib/carmel/doc/todo43
-rw-r--r--contrib/carmel/imapd/imapd.c.patch.old110
-rw-r--r--contrib/carmel/pine/filter.c.patch409
-rw-r--r--contrib/carmel/pine/mailview.c.patch39
-rw-r--r--contrib/carmel/pine/makefile.sun.patch19
-rw-r--r--contrib/carmel/pine/makefile.ult.patch19
-rw-r--r--contrib/keypad.enable/ReadMe7
-rw-r--r--contrib/keypad.enable/keypad.enable.diff122
-rw-r--r--contrib/ports/aos/README20
-rw-r--r--contrib/ports/aos/aos.diff125
-rw-r--r--contrib/ports/sequent_ptx_4.4.6188
-rw-r--r--contrib/ports/vms/readme.1st9
-rw-r--r--contrib/ports/vms/readme.vms102
-rw-r--r--contrib/ports/vms/vms_link.opt1
-rw-r--r--contrib/ports/vms/vms_multinet_link.opt1
-rw-r--r--contrib/ports/vms/vms_netlib_link.opt1
-rw-r--r--contrib/ports/vms/vmsbuild.com31
-rw-r--r--contrib/ports/vms/vmsbuild_cclient.com103
-rw-r--r--contrib/smime/README65
-rw-r--r--contrib/smime/pine-smime-20030115.diff3265
-rwxr-xr-xcontrib/utils/ansiprt.c60
-rwxr-xr-xcontrib/utils/brk2pine.sh74
-rwxr-xr-xcontrib/utils/mailtrfc.sh130
-rwxr-xr-xcontrib/utils/pwd2pine73
-rwxr-xr-xcontrib/utils/sendit.sh49
-rw-r--r--contrib/utils/sendtoall49
-rwxr-xr-xcontrib/utils/txtcc.sh85
-rwxr-xr-xdepcomp630
-rw-r--r--doc/alpine.1356
-rw-r--r--doc/brochure.txt74
-rw-r--r--doc/mailcap.unx131
-rw-r--r--doc/mime.types28
-rw-r--r--doc/pico.1175
-rw-r--r--doc/pilot.184
-rw-r--r--doc/rpdump.138
-rw-r--r--doc/rpload.149
-rw-r--r--doc/tech-notes.txt11840
-rw-r--r--doc/tech-notes/Makefile70
-rw-r--r--doc/tech-notes/background.html350
-rw-r--r--doc/tech-notes/cmd-line.html553
-rw-r--r--doc/tech-notes/config-notes.html1681
-rw-r--r--doc/tech-notes/config.html12523
-rw-r--r--doc/tech-notes/for.pnuts9
-rw-r--r--doc/tech-notes/index.html122
-rw-r--r--doc/tech-notes/installation.html577
-rw-r--r--doc/tech-notes/introduction.html122
-rw-r--r--doc/tech-notes/low-level.html975
-rwxr-xr-xdoc/tech-notes/pn4tn7
-rwxr-xr-xdoc/tech-notes/pnuts.4tech-notes134
-rw-r--r--doc/tech-notes/porting.html336
-rw-r--r--imap/CONTENTS75
-rw-r--r--imap/LICENSE.txt201
-rw-r--r--imap/Makefile737
-rw-r--r--imap/NOTICE17
-rw-r--r--imap/README74
-rw-r--r--imap/SUPPORT22
-rw-r--r--imap/docs/BUILD491
-rw-r--r--imap/docs/CONFIG181
-rw-r--r--imap/docs/FAQ.html4226
-rw-r--r--imap/docs/FAQ.txt2993
-rw-r--r--imap/docs/IPv6.txt131
-rw-r--r--imap/docs/RELNOTES787
-rw-r--r--imap/docs/SSLBUILD267
-rw-r--r--imap/docs/Y2K145
-rw-r--r--imap/docs/bugs.txt234
-rw-r--r--imap/docs/calendar.txt332
-rw-r--r--imap/docs/commndmt.txt101
-rw-r--r--imap/docs/draft/README19
-rw-r--r--imap/docs/draft/i18n.txt1140
-rw-r--r--imap/docs/draft/sort.txt885
-rw-r--r--imap/docs/drivers.txt189
-rw-r--r--imap/docs/formats.txt217
-rw-r--r--imap/docs/imaprc.txt613
-rw-r--r--imap/docs/internal.txt2988
-rw-r--r--imap/docs/locking.txt417
-rw-r--r--imap/docs/md5.txt91
-rw-r--r--imap/docs/mixfmt.txt363
-rw-r--r--imap/docs/naming.txt143
-rw-r--r--imap/docs/rfc/README70
-rw-r--r--imap/docs/rfc/rfc1732.txt283
-rw-r--r--imap/docs/rfc/rfc1733.txt171
-rw-r--r--imap/docs/rfc/rfc2061.txt171
-rw-r--r--imap/docs/rfc/rfc2062.txt451
-rw-r--r--imap/docs/rfc/rfc2087.txt283
-rw-r--r--imap/docs/rfc/rfc2088.txt115
-rw-r--r--imap/docs/rfc/rfc2177.txt227
-rw-r--r--imap/docs/rfc/rfc2180.txt787
-rw-r--r--imap/docs/rfc/rfc2193.txt507
-rw-r--r--imap/docs/rfc/rfc2195.txt283
-rw-r--r--imap/docs/rfc/rfc2221.txt283
-rw-r--r--imap/docs/rfc/rfc2342.txt563
-rw-r--r--imap/docs/rfc/rfc2683.txt1291
-rw-r--r--imap/docs/rfc/rfc2971.txt451
-rw-r--r--imap/docs/rfc/rfc3348.txt339
-rw-r--r--imap/docs/rfc/rfc3501.txt6052
-rw-r--r--imap/docs/rfc/rfc3502.txt395
-rw-r--r--imap/docs/rfc/rfc3503.txt507
-rw-r--r--imap/docs/rfc/rfc3516.txt451
-rw-r--r--imap/docs/rfc/rfc3656.txt1067
-rw-r--r--imap/docs/rfc/rfc3691.txt283
-rw-r--r--imap/docs/rfc/rfc4314.txt1515
-rw-r--r--imap/docs/rfc/rfc4315.txt451
-rw-r--r--imap/docs/rfc/rfc4422.txt1851
-rw-r--r--imap/docs/rfc/rfc4466.txt955
-rw-r--r--imap/docs/rfc/rfc4467.txt1011
-rw-r--r--imap/docs/rfc/rfc4468.txt787
-rw-r--r--imap/docs/rfc/rfc4469.txt731
-rw-r--r--imap/docs/rfc/rfc4505.txt507
-rw-r--r--imap/docs/rfc/rfc4549.txt1963
-rw-r--r--imap/docs/rfc/rfc4551.txt1403
-rw-r--r--imap/docs/rfc/rfc4616.txt619
-rw-r--r--imap/docs/rfc/rfc4731.txt451
-rw-r--r--imap/docs/rfc/rfc4752.txt563
-rw-r--r--imap/docs/rfc/rfc4790.txt1459
-rw-r--r--imap/docs/rfc/rfc4959.txt395
-rw-r--r--imap/docs/rfc/rfc4978.txt507
-rw-r--r--imap/docs/rfc/rfc5032.txt283
-rw-r--r--imap/docs/rfc/rfc5051.txt395
-rw-r--r--imap/docs/rfc/rfc5092.txt1795
-rw-r--r--imap/docs/rfc/rfc5161.txt395
-rw-r--r--imap/docs/rfc/rfc5162.txt1291
-rw-r--r--imap/docs/rfc/rfc5234.txt899
-rw-r--r--imap/makefile.nt76
-rw-r--r--imap/makefile.ntk76
-rw-r--r--imap/makefile.os259
-rw-r--r--imap/makefile.w2k77
-rw-r--r--imap/makefile.wce64
-rw-r--r--imap/src/ansilib/memmove.c40
-rw-r--r--imap/src/ansilib/memmove2.c49
-rw-r--r--imap/src/ansilib/memset.c40
-rw-r--r--imap/src/ansilib/strpbrk.c40
-rw-r--r--imap/src/ansilib/strstr.c45
-rw-r--r--imap/src/ansilib/strtok.c67
-rw-r--r--imap/src/ansilib/strtoul.c73
-rw-r--r--imap/src/c-client/auth_ext.c96
-rw-r--r--imap/src/c-client/auth_gss.c423
-rw-r--r--imap/src/c-client/auth_log.c117
-rw-r--r--imap/src/c-client/auth_md5.c495
-rw-r--r--imap/src/c-client/auth_pla.c133
-rw-r--r--imap/src/c-client/c-client.h55
-rw-r--r--imap/src/c-client/env.h45
-rw-r--r--imap/src/c-client/flstring.c91
-rw-r--r--imap/src/c-client/flstring.h30
-rw-r--r--imap/src/c-client/fs.h34
-rw-r--r--imap/src/c-client/ftl.h32
-rw-r--r--imap/src/c-client/imap4r1.c5672
-rw-r--r--imap/src/c-client/imap4r1.h281
-rw-r--r--imap/src/c-client/mail.c6337
-rw-r--r--imap/src/c-client/mail.h1838
-rw-r--r--imap/src/c-client/misc.c475
-rw-r--r--imap/src/c-client/misc.h110
-rw-r--r--imap/src/c-client/netmsg.c104
-rw-r--r--imap/src/c-client/netmsg.h32
-rw-r--r--imap/src/c-client/newsrc.c510
-rw-r--r--imap/src/c-client/newsrc.h43
-rw-r--r--imap/src/c-client/nl.h34
-rw-r--r--imap/src/c-client/nntp.c2224
-rw-r--r--imap/src/c-client/nntp.h56
-rw-r--r--imap/src/c-client/pop3.c1091
-rw-r--r--imap/src/c-client/rfc822.c2373
-rw-r--r--imap/src/c-client/rfc822.h127
-rw-r--r--imap/src/c-client/smanager.c129
-rw-r--r--imap/src/c-client/smtp.c793
-rw-r--r--imap/src/c-client/smtp.h76
-rw-r--r--imap/src/c-client/sslio.h70
-rw-r--r--imap/src/c-client/tcp.h59
-rw-r--r--imap/src/c-client/utf8.c2554
-rw-r--r--imap/src/c-client/utf8.h584
-rw-r--r--imap/src/c-client/utf8aux.c449
-rw-r--r--imap/src/c-client/utf8aux.h44
-rw-r--r--imap/src/charset/big5.c2016
-rw-r--r--imap/src/charset/cns11643.c8590
-rw-r--r--imap/src/charset/decomtab.c2909
-rw-r--r--imap/src/charset/gb_12345.c1114
-rw-r--r--imap/src/charset/gb_2312.c2795
-rw-r--r--imap/src/charset/ibm.c347
-rw-r--r--imap/src/charset/iso_8859.c308
-rw-r--r--imap/src/charset/jis_0208.c1092
-rw-r--r--imap/src/charset/jis_0212.c962
-rw-r--r--imap/src/charset/koi8_r.c48
-rw-r--r--imap/src/charset/koi8_u.c48
-rw-r--r--imap/src/charset/ksc_5601.c2673
-rw-r--r--imap/src/charset/tis_620.c51
-rw-r--r--imap/src/charset/tmap.c1487
-rw-r--r--imap/src/charset/viscii.c67
-rw-r--r--imap/src/charset/widths.c4136
-rw-r--r--imap/src/charset/windows.c228
-rw-r--r--imap/src/dmail/Makefile53
-rw-r--r--imap/src/dmail/dmail.1121
-rw-r--r--imap/src/dmail/dmail.c661
-rw-r--r--imap/src/dmail/dquota.c44
-rw-r--r--imap/src/dmail/dquota.h32
-rw-r--r--imap/src/imapd/Makefile68
-rw-r--r--imap/src/imapd/imapd.848
-rw-r--r--imap/src/imapd/imapd.c4608
-rw-r--r--imap/src/imapd/makefile.nt55
-rw-r--r--imap/src/imapd/makefile.ntk58
-rw-r--r--imap/src/imapd/makefile.w2k56
-rw-r--r--imap/src/ipopd/Makefile58
-rw-r--r--imap/src/ipopd/ipop2d.c711
-rw-r--r--imap/src/ipopd/ipop3d.c1082
-rw-r--r--imap/src/ipopd/ipopd.875
-rw-r--r--imap/src/ipopd/makefile.nt57
-rw-r--r--imap/src/ipopd/makefile.ntk58
-rw-r--r--imap/src/ipopd/makefile.w2k56
-rw-r--r--imap/src/mailutil/Makefile51
-rw-r--r--imap/src/mailutil/mailutil.1264
-rw-r--r--imap/src/mailutil/mailutil.c942
-rw-r--r--imap/src/mailutil/makefile.nt50
-rw-r--r--imap/src/mailutil/makefile.ntk51
-rw-r--r--imap/src/mailutil/makefile.w2k49
-rw-r--r--imap/src/mlock/Makefile51
-rw-r--r--imap/src/mlock/mlock.c175
-rw-r--r--imap/src/mtest/Makefile53
-rw-r--r--imap/src/mtest/makefile.nt49
-rw-r--r--imap/src/mtest/makefile.ntk51
-rw-r--r--imap/src/mtest/makefile.os253
-rw-r--r--imap/src/mtest/makefile.w2k49
-rw-r--r--imap/src/mtest/mtest.c813
-rw-r--r--imap/src/osdep/amiga/Makefile231
-rw-r--r--imap/src/osdep/amiga/ckp_std.c42
-rwxr-xr-ximap/src/osdep/amiga/drivers36
-rw-r--r--imap/src/osdep/amiga/dummy.c809
-rw-r--r--imap/src/osdep/amiga/dummy.h43
-rw-r--r--imap/src/osdep/amiga/env_ami.c1282
-rw-r--r--imap/src/osdep/amiga/env_ami.h95
-rw-r--r--imap/src/osdep/amiga/fdstring.c99
-rw-r--r--imap/src/osdep/amiga/fdstring.h39
-rw-r--r--imap/src/osdep/amiga/fs_ami.c71
-rw-r--r--imap/src/osdep/amiga/ftl_ami.c38
-rw-r--r--imap/src/osdep/amiga/gethstid.c38
-rw-r--r--imap/src/osdep/amiga/gr_waitp.c39
-rw-r--r--imap/src/osdep/amiga/log_std.c44
-rw-r--r--imap/src/osdep/amiga/mbx.c1855
-rw-r--r--imap/src/osdep/amiga/mh.c1283
-rw-r--r--imap/src/osdep/amiga/mix.c2834
-rwxr-xr-ximap/src/osdep/amiga/mkauths40
-rw-r--r--imap/src/osdep/amiga/mmdf.c2549
-rw-r--r--imap/src/osdep/amiga/mtx.c1371
-rw-r--r--imap/src/osdep/amiga/mx.c1287
-rw-r--r--imap/src/osdep/amiga/news.c738
-rw-r--r--imap/src/osdep/amiga/nl_ami.c92
-rw-r--r--imap/src/osdep/amiga/os_ami.c80
-rw-r--r--imap/src/osdep/amiga/os_ami.h54
-rw-r--r--imap/src/osdep/amiga/phile.c553
-rw-r--r--imap/src/osdep/amiga/pmatch.c89
-rw-r--r--imap/src/osdep/amiga/pseudo.c36
-rw-r--r--imap/src/osdep/amiga/pseudo.h30
-rw-r--r--imap/src/osdep/amiga/scandir.c81
-rw-r--r--imap/src/osdep/amiga/ssl_none.c141
-rw-r--r--imap/src/osdep/amiga/tcp_ami.c797
-rw-r--r--imap/src/osdep/amiga/tcp_ami.h48
-rw-r--r--imap/src/osdep/amiga/tenex.c1470
-rw-r--r--imap/src/osdep/amiga/tz_bsd.c38
-rw-r--r--imap/src/osdep/amiga/unix.c2708
-rw-r--r--imap/src/osdep/amiga/unix.h219
-rw-r--r--imap/src/osdep/amiga/write.c59
-rw-r--r--imap/src/osdep/dos/bezrkdos.c901
-rw-r--r--imap/src/osdep/dos/drivers.bat33
-rw-r--r--imap/src/osdep/dos/drivraux.bat28
-rw-r--r--imap/src/osdep/dos/dummy.h43
-rw-r--r--imap/src/osdep/dos/dummydos.c689
-rw-r--r--imap/src/osdep/dos/env_dos.c300
-rw-r--r--imap/src/osdep/dos/env_dos.h68
-rw-r--r--imap/src/osdep/dos/fdstring.c99
-rw-r--r--imap/src/osdep/dos/fdstring.h39
-rw-r--r--imap/src/osdep/dos/fs_dos.c62
-rw-r--r--imap/src/osdep/dos/ftl_dos.c38
-rw-r--r--imap/src/osdep/dos/makefile98
-rw-r--r--imap/src/osdep/dos/mkautaux.bat29
-rw-r--r--imap/src/osdep/dos/mkauths.bat33
-rw-r--r--imap/src/osdep/dos/mtestdbw.bat27
-rw-r--r--imap/src/osdep/dos/mtestdnf.bat27
-rw-r--r--imap/src/osdep/dos/mtestdnv.bat27
-rw-r--r--imap/src/osdep/dos/mtestdpc.bat27
-rw-r--r--imap/src/osdep/dos/mtestdwa.bat27
-rw-r--r--imap/src/osdep/dos/mtestwsk.bat27
-rw-r--r--imap/src/osdep/dos/mtxdos.c875
-rw-r--r--imap/src/osdep/dos/nl_dos.c61
-rw-r--r--imap/src/osdep/dos/os_dbw.c91
-rw-r--r--imap/src/osdep/dos/os_dbw.h43
-rw-r--r--imap/src/osdep/dos/os_dnf.c95
-rw-r--r--imap/src/osdep/dos/os_dnf.h44
-rw-r--r--imap/src/osdep/dos/os_dnv.c95
-rw-r--r--imap/src/osdep/dos/os_dnv.h43
-rw-r--r--imap/src/osdep/dos/os_dpc.c102
-rw-r--r--imap/src/osdep/dos/os_dpc.h41
-rw-r--r--imap/src/osdep/dos/os_dwa.c72
-rw-r--r--imap/src/osdep/dos/os_dwa.h43
-rw-r--r--imap/src/osdep/dos/os_wsk.c45
-rw-r--r--imap/src/osdep/dos/os_wsk.h48
-rw-r--r--imap/src/osdep/dos/pmatch.c89
-rw-r--r--imap/src/osdep/dos/tcp_dos.c434
-rw-r--r--imap/src/osdep/dos/tcp_dos.h51
-rw-r--r--imap/src/osdep/dos/tcp_dwa.c344
-rw-r--r--imap/src/osdep/dos/tcp_dwa.h45
-rw-r--r--imap/src/osdep/dos/tcp_wsk.c818
-rw-r--r--imap/src/osdep/dos/tcp_wsk.h50
-rw-r--r--imap/src/osdep/dos/write.c59
-rw-r--r--imap/src/osdep/mac/dummy.h43
-rw-r--r--imap/src/osdep/mac/dummymac.c295
-rw-r--r--imap/src/osdep/mac/env_mac.c236
-rw-r--r--imap/src/osdep/mac/env_mac.h58
-rw-r--r--imap/src/osdep/mac/fs_mac.c62
-rw-r--r--imap/src/osdep/mac/ftl_mac.c39
-rw-r--r--imap/src/osdep/mac/linkage.c37
-rw-r--r--imap/src/osdep/mac/linkage.h36
-rw-r--r--imap/src/osdep/mac/mtest.sit.hqx171
-rw-r--r--imap/src/osdep/mac/nl_mac.c74
-rw-r--r--imap/src/osdep/mac/os_mac.c82
-rw-r--r--imap/src/osdep/mac/os_mac.h69
-rw-r--r--imap/src/osdep/mac/osdep.h29
-rw-r--r--imap/src/osdep/mac/pmatch.c89
-rw-r--r--imap/src/osdep/mac/tcp_mac.c557
-rw-r--r--imap/src/osdep/mac/tcp_mac.h49
-rwxr-xr-ximap/src/osdep/nt/drivers.bat33
-rwxr-xr-ximap/src/osdep/nt/drivraux.bat30
-rw-r--r--imap/src/osdep/nt/dummy.h43
-rw-r--r--imap/src/osdep/nt/dummynt.c724
-rw-r--r--imap/src/osdep/nt/env_nt.c774
-rw-r--r--imap/src/osdep/nt/env_nt.h68
-rw-r--r--imap/src/osdep/nt/fdstring.c99
-rw-r--r--imap/src/osdep/nt/fdstring.h39
-rw-r--r--imap/src/osdep/nt/fs_nt.c62
-rw-r--r--imap/src/osdep/nt/ftl_nt.c38
-rw-r--r--imap/src/osdep/nt/ip4_nt.c184
-rw-r--r--imap/src/osdep/nt/ip6_nt.c288
-rw-r--r--imap/src/osdep/nt/kerb_mit.c74
-rw-r--r--imap/src/osdep/nt/kerb_w2k.c699
-rw-r--r--imap/src/osdep/nt/mailfile.h29
-rw-r--r--imap/src/osdep/nt/makefile.nt118
-rw-r--r--imap/src/osdep/nt/makefile.ntk118
-rw-r--r--imap/src/osdep/nt/makefile.w2k119
-rw-r--r--imap/src/osdep/nt/mbxnt.c1694
-rwxr-xr-ximap/src/osdep/nt/mkautaux.bat31
-rwxr-xr-ximap/src/osdep/nt/mkauths.bat33
-rw-r--r--imap/src/osdep/nt/mtxnt.c1232
-rw-r--r--imap/src/osdep/nt/nl_nt.c61
-rw-r--r--imap/src/osdep/nt/os_nt.c48
-rw-r--r--imap/src/osdep/nt/os_nt.h60
-rw-r--r--imap/src/osdep/nt/os_ntk.c51
-rw-r--r--imap/src/osdep/nt/os_old.c48
-rw-r--r--imap/src/osdep/nt/os_w2k.c49
-rw-r--r--imap/src/osdep/nt/pmatch.c89
-rw-r--r--imap/src/osdep/nt/pseudo.c36
-rw-r--r--imap/src/osdep/nt/pseudo.h30
-rwxr-xr-ximap/src/osdep/nt/setproto.bat29
-rw-r--r--imap/src/osdep/nt/ssl_none.c141
-rw-r--r--imap/src/osdep/nt/ssl_nt.c721
-rw-r--r--imap/src/osdep/nt/ssl_old.c625
-rw-r--r--imap/src/osdep/nt/ssl_w2k.c683
-rw-r--r--imap/src/osdep/nt/tcp_nt.c916
-rw-r--r--imap/src/osdep/nt/tcp_nt.h51
-rw-r--r--imap/src/osdep/nt/tenexnt.c1310
-rw-r--r--imap/src/osdep/nt/unixnt.c2297
-rw-r--r--imap/src/osdep/nt/unixnt.h161
-rw-r--r--imap/src/osdep/nt/write.c59
-rw-r--r--imap/src/osdep/nt/yunchan.c286
-rw-r--r--imap/src/osdep/nt/yunchan.h86
-rw-r--r--imap/src/osdep/os2/auths.cmd48
-rw-r--r--imap/src/osdep/os2/drivers.cmd45
-rw-r--r--imap/src/osdep/os2/dummy.h43
-rw-r--r--imap/src/osdep/os2/dummyos2.c714
-rw-r--r--imap/src/osdep/os2/env_os2.c318
-rw-r--r--imap/src/osdep/os2/env_os2.h62
-rw-r--r--imap/src/osdep/os2/fs_os2.c62
-rw-r--r--imap/src/osdep/os2/ftl_os2.c38
-rw-r--r--imap/src/osdep/os2/makefile.os2109
-rw-r--r--imap/src/osdep/os2/mbxnt.c1694
-rw-r--r--imap/src/osdep/os2/mtxnt.c1232
-rw-r--r--imap/src/osdep/os2/nl_os2.c61
-rw-r--r--imap/src/osdep/os2/os_os2.c100
-rw-r--r--imap/src/osdep/os2/os_os2.h38
-rw-r--r--imap/src/osdep/os2/pmatch.c89
-rw-r--r--imap/src/osdep/os2/pseudo.c36
-rw-r--r--imap/src/osdep/os2/pseudo.h30
-rw-r--r--imap/src/osdep/os2/setproto.cmd34
-rw-r--r--imap/src/osdep/os2/tcp_os2.c434
-rw-r--r--imap/src/osdep/os2/tcp_os2.h51
-rw-r--r--imap/src/osdep/os2/tenexnt.c1310
-rw-r--r--imap/src/osdep/os2/unixnt.c2297
-rw-r--r--imap/src/osdep/os2/unixnt.h161
-rw-r--r--imap/src/osdep/os2/write.c59
-rw-r--r--imap/src/osdep/tops-20/build.ctl63
-rw-r--r--imap/src/osdep/tops-20/dummy.h43
-rw-r--r--imap/src/osdep/tops-20/dummyt20.c294
-rw-r--r--imap/src/osdep/tops-20/env_t20.c226
-rw-r--r--imap/src/osdep/tops-20/env_t20.h73
-rw-r--r--imap/src/osdep/tops-20/fs_t20.c62
-rw-r--r--imap/src/osdep/tops-20/ftl_t20.c38
-rw-r--r--imap/src/osdep/tops-20/linkage.c37
-rw-r--r--imap/src/osdep/tops-20/linkage.h36
-rw-r--r--imap/src/osdep/tops-20/log_t20.c80
-rw-r--r--imap/src/osdep/tops-20/nl_t20.c61
-rw-r--r--imap/src/osdep/tops-20/os_t20.c106
-rw-r--r--imap/src/osdep/tops-20/os_t20.h52
-rw-r--r--imap/src/osdep/tops-20/pmatch.c89
-rw-r--r--imap/src/osdep/tops-20/shortsym.h608
-rw-r--r--imap/src/osdep/tops-20/tcp_t20.c365
-rw-r--r--imap/src/osdep/tops-20/tcp_t20.h65
-rw-r--r--imap/src/osdep/unix/Makefile1078
-rw-r--r--imap/src/osdep/unix/Makefile.gss39
-rw-r--r--imap/src/osdep/unix/ckp_1st.c53
-rw-r--r--imap/src/osdep/unix/ckp_2nd.c36
-rw-r--r--imap/src/osdep/unix/ckp_3rd.c32
-rw-r--r--imap/src/osdep/unix/ckp_a41.c52
-rw-r--r--imap/src/osdep/unix/ckp_afs.c71
-rw-r--r--imap/src/osdep/unix/ckp_bsi.c50
-rw-r--r--imap/src/osdep/unix/ckp_cyg.c64
-rw-r--r--imap/src/osdep/unix/ckp_dce.c83
-rw-r--r--imap/src/osdep/unix/ckp_gss.c90
-rw-r--r--imap/src/osdep/unix/ckp_nul.c40
-rw-r--r--imap/src/osdep/unix/ckp_os4.c78
-rw-r--r--imap/src/osdep/unix/ckp_pam.c136
-rw-r--r--imap/src/osdep/unix/ckp_pmb.c128
-rw-r--r--imap/src/osdep/unix/ckp_psx.c101
-rw-r--r--imap/src/osdep/unix/ckp_sce.c51
-rw-r--r--imap/src/osdep/unix/ckp_sec.c50
-rw-r--r--imap/src/osdep/unix/ckp_ssn.c56
-rw-r--r--imap/src/osdep/unix/ckp_std.c42
-rw-r--r--imap/src/osdep/unix/ckp_sv4.c90
-rw-r--r--imap/src/osdep/unix/ckp_svo.c89
-rw-r--r--imap/src/osdep/unix/ckp_ult.c40
-rw-r--r--imap/src/osdep/unix/crx_nfs.c79
-rw-r--r--imap/src/osdep/unix/crx_std.c45
-rwxr-xr-ximap/src/osdep/unix/drivers36
-rw-r--r--imap/src/osdep/unix/dummy.c809
-rw-r--r--imap/src/osdep/unix/dummy.h43
-rw-r--r--imap/src/osdep/unix/env_unix.c1847
-rw-r--r--imap/src/osdep/unix/env_unix.h95
-rw-r--r--imap/src/osdep/unix/fdstring.c99
-rw-r--r--imap/src/osdep/unix/fdstring.h39
-rw-r--r--imap/src/osdep/unix/flockcyg.c92
-rw-r--r--imap/src/osdep/unix/flockcyg.h50
-rw-r--r--imap/src/osdep/unix/flocklnx.c76
-rw-r--r--imap/src/osdep/unix/flocksim.c920
-rw-r--r--imap/src/osdep/unix/flocksim.h117
-rw-r--r--imap/src/osdep/unix/fs_unix.c71
-rw-r--r--imap/src/osdep/unix/fsync.c39
-rw-r--r--imap/src/osdep/unix/ftl_unix.c39
-rw-r--r--imap/src/osdep/unix/gethstid.c38
-rw-r--r--imap/src/osdep/unix/getspnam.c65
-rw-r--r--imap/src/osdep/unix/gr_wait.c46
-rw-r--r--imap/src/osdep/unix/gr_wait4.c39
-rw-r--r--imap/src/osdep/unix/gr_waitp.c39
-rw-r--r--imap/src/osdep/unix/ip4_unix.c184
-rw-r--r--imap/src/osdep/unix/ip6_unix.c288
-rw-r--r--imap/src/osdep/unix/ipo_unix.c181
-rw-r--r--imap/src/osdep/unix/kerb_mit.c111
-rw-r--r--imap/src/osdep/unix/log_bsi.c55
-rw-r--r--imap/src/osdep/unix/log_cyg.c46
-rw-r--r--imap/src/osdep/unix/log_old.c39
-rw-r--r--imap/src/osdep/unix/log_os4.c58
-rw-r--r--imap/src/osdep/unix/log_sec.c44
-rw-r--r--imap/src/osdep/unix/log_std.c44
-rw-r--r--imap/src/osdep/unix/log_sv4.c43
-rw-r--r--imap/src/osdep/unix/mbx.c1855
-rw-r--r--imap/src/osdep/unix/mh.c1283
-rw-r--r--imap/src/osdep/unix/mix.c2834
-rwxr-xr-ximap/src/osdep/unix/mkauths40
-rw-r--r--imap/src/osdep/unix/mmdf.c2549
-rw-r--r--imap/src/osdep/unix/mtx.c1371
-rw-r--r--imap/src/osdep/unix/mx.c1287
-rw-r--r--imap/src/osdep/unix/news.c738
-rw-r--r--imap/src/osdep/unix/nl_unix.c92
-rw-r--r--imap/src/osdep/unix/opendir.c79
-rw-r--r--imap/src/osdep/unix/os_a32.c60
-rw-r--r--imap/src/osdep/unix/os_a32.h50
-rw-r--r--imap/src/osdep/unix/os_a41.c61
-rw-r--r--imap/src/osdep/unix/os_a41.h50
-rw-r--r--imap/src/osdep/unix/os_a52.c63
-rw-r--r--imap/src/osdep/unix/os_a52.h53
-rw-r--r--imap/src/osdep/unix/os_aix.c64
-rw-r--r--imap/src/osdep/unix/os_aix.h47
-rw-r--r--imap/src/osdep/unix/os_aos.c63
-rw-r--r--imap/src/osdep/unix/os_aos.h50
-rw-r--r--imap/src/osdep/unix/os_art.c86
-rw-r--r--imap/src/osdep/unix/os_art.h81
-rw-r--r--imap/src/osdep/unix/os_asv.c70
-rw-r--r--imap/src/osdep/unix/os_asv.h76
-rw-r--r--imap/src/osdep/unix/os_aux.c62
-rw-r--r--imap/src/osdep/unix/os_aux.h51
-rw-r--r--imap/src/osdep/unix/os_bsd.c63
-rw-r--r--imap/src/osdep/unix/os_bsd.h51
-rw-r--r--imap/src/osdep/unix/os_bsf.c54
-rw-r--r--imap/src/osdep/unix/os_bsf.h46
-rw-r--r--imap/src/osdep/unix/os_bsi.c54
-rw-r--r--imap/src/osdep/unix/os_bsi.h43
-rw-r--r--imap/src/osdep/unix/os_cvx.c57
-rw-r--r--imap/src/osdep/unix/os_cvx.h45
-rw-r--r--imap/src/osdep/unix/os_cyg.c71
-rw-r--r--imap/src/osdep/unix/os_cyg.h71
-rw-r--r--imap/src/osdep/unix/os_d-g.c54
-rw-r--r--imap/src/osdep/unix/os_d-g.h56
-rw-r--r--imap/src/osdep/unix/os_do4.c58
-rw-r--r--imap/src/osdep/unix/os_do4.h47
-rw-r--r--imap/src/osdep/unix/os_drs.c57
-rw-r--r--imap/src/osdep/unix/os_drs.h59
-rw-r--r--imap/src/osdep/unix/os_dyn.c64
-rw-r--r--imap/src/osdep/unix/os_dyn.h61
-rw-r--r--imap/src/osdep/unix/os_hpp.c77
-rw-r--r--imap/src/osdep/unix/os_hpp.h63
-rw-r--r--imap/src/osdep/unix/os_isc.c68
-rw-r--r--imap/src/osdep/unix/os_isc.h70
-rw-r--r--imap/src/osdep/unix/os_lnx.c54
-rw-r--r--imap/src/osdep/unix/os_lnx.h67
-rw-r--r--imap/src/osdep/unix/os_lyn.c54
-rw-r--r--imap/src/osdep/unix/os_lyn.h44
-rw-r--r--imap/src/osdep/unix/os_mct.c52
-rw-r--r--imap/src/osdep/unix/os_mct.h44
-rw-r--r--imap/src/osdep/unix/os_mnt.c53
-rw-r--r--imap/src/osdep/unix/os_mnt.h51
-rw-r--r--imap/src/osdep/unix/os_nto.c76
-rw-r--r--imap/src/osdep/unix/os_nto.h75
-rw-r--r--imap/src/osdep/unix/os_nxt.c54
-rw-r--r--imap/src/osdep/unix/os_nxt.h50
-rw-r--r--imap/src/osdep/unix/os_os4.c57
-rw-r--r--imap/src/osdep/unix/os_os4.h51
-rw-r--r--imap/src/osdep/unix/os_osf.c55
-rw-r--r--imap/src/osdep/unix/os_osf.h51
-rw-r--r--imap/src/osdep/unix/os_osx.c54
-rw-r--r--imap/src/osdep/unix/os_osx.h56
-rw-r--r--imap/src/osdep/unix/os_ptx.c115
-rw-r--r--imap/src/osdep/unix/os_ptx.h72
-rw-r--r--imap/src/osdep/unix/os_pyr.c61
-rw-r--r--imap/src/osdep/unix/os_pyr.h58
-rw-r--r--imap/src/osdep/unix/os_qnx.c77
-rw-r--r--imap/src/osdep/unix/os_qnx.h62
-rw-r--r--imap/src/osdep/unix/os_s40.c64
-rw-r--r--imap/src/osdep/unix/os_s40.h33
-rw-r--r--imap/src/osdep/unix/os_sc5.c63
-rw-r--r--imap/src/osdep/unix/os_sc5.h76
-rw-r--r--imap/src/osdep/unix/os_sco.c66
-rw-r--r--imap/src/osdep/unix/os_sco.h79
-rw-r--r--imap/src/osdep/unix/os_sgi.c57
-rw-r--r--imap/src/osdep/unix/os_sgi.h59
-rw-r--r--imap/src/osdep/unix/os_shp.c79
-rw-r--r--imap/src/osdep/unix/os_shp.h63
-rw-r--r--imap/src/osdep/unix/os_slx.c56
-rw-r--r--imap/src/osdep/unix/os_slx.h67
-rw-r--r--imap/src/osdep/unix/os_sol.c71
-rw-r--r--imap/src/osdep/unix/os_soln.h87
-rw-r--r--imap/src/osdep/unix/os_solo.h84
-rw-r--r--imap/src/osdep/unix/os_sos.c57
-rw-r--r--imap/src/osdep/unix/os_sos.h51
-rw-r--r--imap/src/osdep/unix/os_sua.c54
-rw-r--r--imap/src/osdep/unix/os_sua.h50
-rw-r--r--imap/src/osdep/unix/os_sun.c64
-rw-r--r--imap/src/osdep/unix/os_sun.h51
-rw-r--r--imap/src/osdep/unix/os_sv2.c128
-rw-r--r--imap/src/osdep/unix/os_sv2.h120
-rw-r--r--imap/src/osdep/unix/os_sv4.c68
-rw-r--r--imap/src/osdep/unix/os_sv4.h78
-rw-r--r--imap/src/osdep/unix/os_ult.c52
-rw-r--r--imap/src/osdep/unix/os_ult.h42
-rw-r--r--imap/src/osdep/unix/os_vu2.c82
-rw-r--r--imap/src/osdep/unix/os_vu2.h79
-rw-r--r--imap/src/osdep/unix/phile.c553
-rw-r--r--imap/src/osdep/unix/pmatch.c89
-rw-r--r--imap/src/osdep/unix/pseudo.c36
-rw-r--r--imap/src/osdep/unix/pseudo.h30
-rw-r--r--imap/src/osdep/unix/rename.c44
-rw-r--r--imap/src/osdep/unix/scandir.c81
-rw-r--r--imap/src/osdep/unix/setpgrp.c39
-rw-r--r--imap/src/osdep/unix/sig_bsd.c40
-rw-r--r--imap/src/osdep/unix/sig_psx.c51
-rw-r--r--imap/src/osdep/unix/sig_sv4.c40
-rw-r--r--imap/src/osdep/unix/ssl_none.c141
-rw-r--r--imap/src/osdep/unix/ssl_unix.c821
-rw-r--r--imap/src/osdep/unix/sslstdio.c169
-rw-r--r--imap/src/osdep/unix/strerror.c37
-rw-r--r--imap/src/osdep/unix/tcp_unix.c1043
-rw-r--r--imap/src/osdep/unix/tcp_unix.h48
-rw-r--r--imap/src/osdep/unix/tenex.c1470
-rw-r--r--imap/src/osdep/unix/truncate.c43
-rw-r--r--imap/src/osdep/unix/tz_bsd.c38
-rw-r--r--imap/src/osdep/unix/tz_nul.c36
-rw-r--r--imap/src/osdep/unix/tz_sv4.c39
-rw-r--r--imap/src/osdep/unix/unix.c2708
-rw-r--r--imap/src/osdep/unix/unix.h161
-rw-r--r--imap/src/osdep/unix/utime.c45
-rw-r--r--imap/src/osdep/unix/write.c59
-rw-r--r--imap/src/osdep/vms/build.com99
-rw-r--r--imap/src/osdep/vms/clean.com26
-rw-r--r--imap/src/osdep/vms/dummy.h43
-rw-r--r--imap/src/osdep/vms/dummyvms.c295
-rw-r--r--imap/src/osdep/vms/env_vms.c174
-rw-r--r--imap/src/osdep/vms/env_vms.h60
-rw-r--r--imap/src/osdep/vms/fs_vms.c62
-rw-r--r--imap/src/osdep/vms/ftl_vms.c38
-rw-r--r--imap/src/osdep/vms/link.opt1
-rw-r--r--imap/src/osdep/vms/link_mnt.opt1
-rw-r--r--imap/src/osdep/vms/link_nlb.opt1
-rw-r--r--imap/src/osdep/vms/linkage.c37
-rw-r--r--imap/src/osdep/vms/linkage.h36
-rw-r--r--imap/src/osdep/vms/nl_vms.c92
-rw-r--r--imap/src/osdep/vms/os_vms.c76
-rw-r--r--imap/src/osdep/vms/os_vms.h52
-rw-r--r--imap/src/osdep/vms/pmatch.c89
-rw-r--r--imap/src/osdep/vms/tcp_vms.h52
-rw-r--r--imap/src/osdep/vms/tcp_vmsl.c378
-rw-r--r--imap/src/osdep/vms/tcp_vmsm.c479
-rw-r--r--imap/src/osdep/vms/tcp_vmsn.c222
-rw-r--r--imap/src/osdep/wce/drivers.bat33
-rw-r--r--imap/src/osdep/wce/drivraux.bat30
-rw-r--r--imap/src/osdep/wce/dummy.h43
-rw-r--r--imap/src/osdep/wce/dummywce.c301
-rw-r--r--imap/src/osdep/wce/env_wce.c301
-rw-r--r--imap/src/osdep/wce/env_wce.h70
-rw-r--r--imap/src/osdep/wce/fs_wce.c62
-rw-r--r--imap/src/osdep/wce/ftl_wce.c38
-rw-r--r--imap/src/osdep/wce/makefile.wce96
-rw-r--r--imap/src/osdep/wce/mkautaux.bat31
-rw-r--r--imap/src/osdep/wce/mkauths.bat33
-rw-r--r--imap/src/osdep/wce/nl_wce.c61
-rw-r--r--imap/src/osdep/wce/os_wce.c45
-rw-r--r--imap/src/osdep/wce/os_wce.h53
-rw-r--r--imap/src/osdep/wce/pmatch.c89
-rw-r--r--imap/src/osdep/wce/setproto.bat29
-rw-r--r--imap/src/osdep/wce/tcp_wce.c818
-rw-r--r--imap/src/osdep/wce/tcp_wce.h46
-rw-r--r--imap/src/tmail/Makefile53
-rw-r--r--imap/src/tmail/tmail.1205
-rw-r--r--imap/src/tmail/tmail.c800
-rw-r--r--imap/src/tmail/tquota.c45
-rw-r--r--imap/src/tmail/tquota.h32
-rw-r--r--imap/tools/Makefile36
-rwxr-xr-ximap/tools/an41
-rwxr-xr-ximap/tools/ua45
-rw-r--r--imap/tools/uahelper.c262
-rw-r--r--include/config.h.in649
-rw-r--r--include/config.wnt.h581
-rw-r--r--include/general.h134
-rw-r--r--include/system.h392
-rwxr-xr-xinstall-sh520
-rwxr-xr-xldap/binaries/debug/ldap32.dllbin0 -> 204800 bytes
-rwxr-xr-xldap/binaries/debug/ldap32.libbin0 -> 81202 bytes
-rwxr-xr-xldap/binaries/debug/libldap.dllbin0 -> 279236 bytes
-rwxr-xr-xldap/binaries/debug/libldap.libbin0 -> 14336 bytes
-rwxr-xr-xldap/binaries/debug/ltest.exebin0 -> 64892 bytes
-rwxr-xr-xldap/binaries/debug/ltest32.exebin0 -> 92160 bytes
-rwxr-xr-xldap/binaries/release/ldap32.dllbin0 -> 138752 bytes
-rwxr-xr-xldap/binaries/release/ldap32.libbin0 -> 79332 bytes
-rwxr-xr-xldap/binaries/release/libldap.dllbin0 -> 106496 bytes
-rwxr-xr-xldap/binaries/release/libldap.libbin0 -> 14336 bytes
-rwxr-xr-xldap/disptmpl.cfg677
-rwxr-xr-xldap/inckit/disptmpl.h330
-rwxr-xr-xldap/inckit/lber.h184
-rwxr-xr-xldap/inckit/ldap.h592
-rwxr-xr-xldap/inckit/msdos.h133
-rwxr-xr-xldap/inckit/proto-lb.h102
-rwxr-xr-xldap/inckit/proto-ld.h276
-rwxr-xr-xldap/inckit/srchpref.h106
-rwxr-xr-xldap/kbind.c368
-rw-r--r--ldap/ldap32.gidbin0 -> 8628 bytes
-rwxr-xr-xldap/ldap32.hlpbin0 -> 72697 bytes
-rwxr-xr-xldap/ldfilter.cfg105
-rwxr-xr-xldap/ldfriend.cfg242
l---------ldap/lib1
-rwxr-xr-xldap/libldap.hlpbin0 -> 76104 bytes
-rwxr-xr-xldap/libldap.mak667
-rwxr-xr-xldap/readme.txt218
-rwxr-xr-xldap/srchpref.cfg153
-rwxr-xr-xltmain.sh8406
-rw-r--r--m4/ChangeLog56
-rw-r--r--m4/Makefile.am1
-rw-r--r--m4/Makefile.in401
-rw-r--r--m4/acx_pthread.m4242
-rw-r--r--m4/codeset.m421
-rw-r--r--m4/gettext.m4419
-rw-r--r--m4/glibc2.m430
-rw-r--r--m4/glibc21.m430
-rw-r--r--m4/iconv.m4101
-rw-r--r--m4/intdiv0.m470
-rw-r--r--m4/intl.m4259
-rw-r--r--m4/intldir.m419
-rw-r--r--m4/intmax.m433
-rw-r--r--m4/inttypes-pri.m436
-rw-r--r--m4/inttypes.m425
-rw-r--r--m4/inttypes_h.m426
-rw-r--r--m4/isc-posix.m424
-rw-r--r--m4/lcmessage.m430
-rw-r--r--m4/lib-ld.m4110
-rw-r--r--m4/lib-link.m4644
-rw-r--r--m4/lib-prefix.m4185
-rw-r--r--m4/libtool.m47365
-rw-r--r--m4/lock.m4311
-rw-r--r--m4/longdouble.m431
-rw-r--r--m4/longlong.m448
-rw-r--r--m4/ltoptions.m4368
-rw-r--r--m4/ltsugar.m4123
-rw-r--r--m4/ltversion.m423
-rw-r--r--m4/lt~obsolete.m492
-rw-r--r--m4/nls.m431
-rw-r--r--m4/po.m4428
-rw-r--r--m4/printf-posix.m444
-rw-r--r--m4/progtest.m492
-rw-r--r--m4/size_max.m462
-rw-r--r--m4/stdint_h.m426
-rw-r--r--m4/uintmax_t.m430
-rw-r--r--m4/ulonglong.m448
-rw-r--r--m4/visibility.m452
-rw-r--r--m4/wchar_t.m420
-rw-r--r--m4/wint_t.m420
-rw-r--r--m4/xsize.m413
-rw-r--r--mapi/ReadmeMapi32.txt102
-rw-r--r--mapi/instmapi.c406
-rwxr-xr-xmapi/makefile72
-rw-r--r--mapi/pmapi.c2929
-rw-r--r--mapi/pmapi.def17
-rw-r--r--mapi/pmapi.h244
-rw-r--r--mapi/pmapi.rc153
-rw-r--r--mapi/resource.h19
-rw-r--r--mapi/rfc1522.c557
-rw-r--r--mapi/smapi.c653
-rwxr-xr-xmissing376
-rwxr-xr-xmkinstalldirs162
-rw-r--r--packages/README81
-rw-r--r--packages/alpine.spec70
-rw-r--r--packages/debian/changelog5
-rw-r--r--packages/debian/control22
-rw-r--r--packages/debian/menu2
-rw-r--r--packages/debian/postinst5
-rw-r--r--packages/debian/postrm13
-rw-r--r--packages/debian/preinst9
-rwxr-xr-xpackages/debian/rules79
-rwxr-xr-xpackages/windows/alpine.iss60
-rw-r--r--packages/windows/binstaller.bat45
-rw-r--r--pico/Makefile.am37
-rw-r--r--pico/Makefile.in777
-rw-r--r--pico/attach.c1515
-rw-r--r--pico/basic.c955
-rw-r--r--pico/bind.c406
-rw-r--r--pico/blddate.c53
-rw-r--r--pico/browse.c2915
-rw-r--r--pico/buffer.c354
-rw-r--r--pico/composer.c4823
-rw-r--r--pico/display.c3306
-rw-r--r--pico/ebind.h167
-rw-r--r--pico/edef.h166
-rw-r--r--pico/efunc.h258
-rw-r--r--pico/estruct.h366
-rw-r--r--pico/file.c1090
-rw-r--r--pico/fileio.c157
-rw-r--r--pico/headers.h68
-rw-r--r--pico/keydefs.h164
-rw-r--r--pico/line.c778
-rw-r--r--pico/main.c871
-rw-r--r--pico/makefile.wnt71
-rw-r--r--pico/mode.h50
-rw-r--r--pico/msmem.c1301
-rw-r--r--pico/mswin.def16
-rw-r--r--pico/mswinver.c66
-rw-r--r--pico/osdep/Makefile.am20
-rw-r--r--pico/osdep/Makefile.in549
-rw-r--r--pico/osdep/altedit.c712
-rw-r--r--pico/osdep/altedit.h29
-rw-r--r--pico/osdep/chkpoint.c98
-rw-r--r--pico/osdep/chkpoint.h24
-rw-r--r--pico/osdep/color.c1797
-rw-r--r--pico/osdep/color.h38
-rw-r--r--pico/osdep/filesys.c1027
-rw-r--r--pico/osdep/filesys.h43
-rw-r--r--pico/osdep/fsync.c31
-rw-r--r--pico/osdep/fsync.h30
-rw-r--r--pico/osdep/getkey.c565
-rw-r--r--pico/osdep/getkey.h36
-rw-r--r--pico/osdep/makefile.wnt69
-rw-r--r--pico/osdep/mouse.c446
-rw-r--r--pico/osdep/mouse.h33
-rw-r--r--pico/osdep/msdlg.c1050
-rw-r--r--pico/osdep/msmenu.h97
-rw-r--r--pico/osdep/mswin.bmpbin0 -> 2166 bytes
-rw-r--r--pico/osdep/mswin.c12448
-rw-r--r--pico/osdep/mswin.h404
-rw-r--r--pico/osdep/mswin.icobin0 -> 3262 bytes
-rw-r--r--pico/osdep/mswin.rc236
-rw-r--r--pico/osdep/mswin_aspell.c427
-rw-r--r--pico/osdep/mswin_aspell.h30
-rw-r--r--pico/osdep/mswin_spell.DLG18
-rw-r--r--pico/osdep/mswin_spell.c547
-rw-r--r--pico/osdep/mswin_spell.h9
-rw-r--r--pico/osdep/mswin_tw.c765
-rw-r--r--pico/osdep/mswin_tw.h58
-rw-r--r--pico/osdep/mswinhnd.curbin0 -> 326 bytes
-rw-r--r--pico/osdep/newmail.c66
-rw-r--r--pico/osdep/newmail.h25
-rw-r--r--pico/osdep/os-win.h183
-rw-r--r--pico/osdep/os-wnt.h181
-rw-r--r--pico/osdep/pico.icobin0 -> 2238 bytes
-rw-r--r--pico/osdep/popen.c68
-rw-r--r--pico/osdep/popen.h26
-rw-r--r--pico/osdep/raw.c449
-rw-r--r--pico/osdep/raw.h38
-rw-r--r--pico/osdep/read.c223
-rw-r--r--pico/osdep/read.h35
-rw-r--r--pico/osdep/resource.h189
-rw-r--r--pico/osdep/shell.c171
-rw-r--r--pico/osdep/shell.h27
-rw-r--r--pico/osdep/signals.c181
-rw-r--r--pico/osdep/signals.h62
-rw-r--r--pico/osdep/spell.c323
-rw-r--r--pico/osdep/spell.h25
-rw-r--r--pico/osdep/terminal.c1762
-rw-r--r--pico/osdep/terminal.h50
-rw-r--r--pico/osdep/truncate.c20
-rw-r--r--pico/osdep/truncate.h21
-rw-r--r--pico/osdep/tty.c372
-rw-r--r--pico/osdep/tty.h36
-rw-r--r--pico/pico-win.lnk6
-rw-r--r--pico/pico.c1939
-rw-r--r--pico/pico.h452
-rw-r--r--pico/picolib.def94
-rw-r--r--pico/pilot.c472
-rw-r--r--pico/random.c427
-rw-r--r--pico/region.c364
-rw-r--r--pico/search.c1037
-rw-r--r--pico/utf8stub.c86
-rw-r--r--pico/utf8stub.h29
-rw-r--r--pico/window.c47
-rw-r--r--pico/word.c1179
-rw-r--r--pith/IO.ReadMe122
-rw-r--r--pith/Makefile.am47
-rw-r--r--pith/Makefile.in815
-rw-r--r--pith/abdlc.c2066
-rw-r--r--pith/abdlc.h41
-rw-r--r--pith/ablookup.c1629
-rw-r--r--pith/ablookup.h106
-rw-r--r--pith/addrbook.c369
-rw-r--r--pith/addrbook.h30
-rw-r--r--pith/addrstring.c452
-rw-r--r--pith/addrstring.h37
-rw-r--r--pith/adjtime.c61
-rw-r--r--pith/adjtime.h35
-rw-r--r--pith/adrbklib.c6028
-rw-r--r--pith/adrbklib.h857
-rw-r--r--pith/atttype.h46
-rw-r--r--pith/bitmap.h39
-rw-r--r--pith/bldaddr.c1263
-rw-r--r--pith/bldaddr.h122
-rw-r--r--pith/busy.h38
-rw-r--r--pith/charconv/Makefile.am19
-rw-r--r--pith/charconv/Makefile.in527
-rw-r--r--pith/charconv/filesys.c721
-rw-r--r--pith/charconv/filesys.h50
-rw-r--r--pith/charconv/makefile.wnt58
-rw-r--r--pith/charconv/utf8.c2512
-rw-r--r--pith/charconv/utf8.h106
-rw-r--r--pith/charset.c929
-rw-r--r--pith/charset.h56
-rw-r--r--pith/color.c234
-rw-r--r--pith/color.h87
-rw-r--r--pith/conf.c8241
-rw-r--r--pith/conf.h895
-rw-r--r--pith/conftype.h709
-rw-r--r--pith/context.c788
-rw-r--r--pith/context.h52
-rw-r--r--pith/copyaddr.c70
-rw-r--r--pith/copyaddr.h25
-rw-r--r--pith/debug.h58
-rw-r--r--pith/detach.c837
-rw-r--r--pith/detach.h69
-rw-r--r--pith/detoken.c567
-rw-r--r--pith/detoken.h29
-rw-r--r--pith/editorial.c198
-rw-r--r--pith/editorial.h28
-rw-r--r--pith/escapes.c69
-rw-r--r--pith/escapes.h24
-rw-r--r--pith/filter.c11305
-rw-r--r--pith/filter.h222
-rw-r--r--pith/filttype.h75
-rw-r--r--pith/flag.c758
-rw-r--r--pith/flag.h155
-rw-r--r--pith/folder.c2759
-rw-r--r--pith/folder.h134
-rw-r--r--pith/foldertype.h163
-rw-r--r--pith/handle.c169
-rw-r--r--pith/handle.h90
-rw-r--r--pith/headers.h63
-rw-r--r--pith/help.c369
-rw-r--r--pith/help.h61
-rw-r--r--pith/help_c_gen.c291
-rw-r--r--pith/help_h_gen.c92
-rw-r--r--pith/helpindx.c132
-rw-r--r--pith/hist.c218
-rw-r--r--pith/hist.h53
-rw-r--r--pith/icache.c452
-rw-r--r--pith/icache.h56
-rw-r--r--pith/imap.c1111
-rw-r--r--pith/imap.h135
-rw-r--r--pith/indxtype.h249
-rw-r--r--pith/init.c546
-rw-r--r--pith/init.h42
-rw-r--r--pith/keyword.c441
-rw-r--r--pith/keyword.h48
-rw-r--r--pith/ldap.c1798
-rw-r--r--pith/ldap.h186
-rw-r--r--pith/list.c188
-rw-r--r--pith/list.h32
-rw-r--r--pith/mailcap.c976
-rw-r--r--pith/mailcap.h40
-rw-r--r--pith/mailcmd.c2741
-rw-r--r--pith/mailcmd.h77
-rw-r--r--pith/mailindx.c6434
-rw-r--r--pith/mailindx.h60
-rw-r--r--pith/maillist.c210
-rw-r--r--pith/maillist.h57
-rw-r--r--pith/mailpart.h51
-rw-r--r--pith/mailview.c3271
-rw-r--r--pith/mailview.h155
-rw-r--r--pith/makefile.wnt88
-rw-r--r--pith/margin.c138
-rw-r--r--pith/margin.h26
-rw-r--r--pith/mimedesc.c879
-rw-r--r--pith/mimedesc.h37
-rw-r--r--pith/mimetype.c374
-rw-r--r--pith/mimetype.h51
-rw-r--r--pith/msgno.c941
-rw-r--r--pith/msgno.h207
-rw-r--r--pith/newmail.c948
-rw-r--r--pith/newmail.h59
-rw-r--r--pith/news.c459
-rw-r--r--pith/news.h37
-rw-r--r--pith/options.h228
-rw-r--r--pith/osdep/Makefile.am22
-rw-r--r--pith/osdep/Makefile.in558
-rw-r--r--pith/osdep/ReadMe13
-rw-r--r--pith/osdep/bldpath.c190
-rw-r--r--pith/osdep/bldpath.h29
-rw-r--r--pith/osdep/canaccess.c160
-rw-r--r--pith/osdep/canaccess.h51
-rw-r--r--pith/osdep/canonicl.c71
-rw-r--r--pith/osdep/canonicl.h26
-rw-r--r--pith/osdep/collate.c170
-rw-r--r--pith/osdep/collate.h32
-rw-r--r--pith/osdep/color.c93
-rw-r--r--pith/osdep/color.h98
-rw-r--r--pith/osdep/coredump.c31
-rw-r--r--pith/osdep/coredump.h26
-rw-r--r--pith/osdep/creatdir.c55
-rw-r--r--pith/osdep/creatdir.h26
-rw-r--r--pith/osdep/debugtime.c133
-rw-r--r--pith/osdep/debugtime.h31
-rw-r--r--pith/osdep/domnames.c145
-rw-r--r--pith/osdep/domnames.h26
-rw-r--r--pith/osdep/err_desc.c44
-rw-r--r--pith/osdep/err_desc.h26
-rw-r--r--pith/osdep/fgetpos.c50
-rw-r--r--pith/osdep/fgetpos.h27
-rw-r--r--pith/osdep/filesize.c123
-rw-r--r--pith/osdep/filesize.h31
-rw-r--r--pith/osdep/fnexpand.c96
-rw-r--r--pith/osdep/fnexpand.h26
-rw-r--r--pith/osdep/forkwait.h39
-rw-r--r--pith/osdep/hostname.c119
-rw-r--r--pith/osdep/hostname.h26
-rw-r--r--pith/osdep/lstcmpnt.c103
-rw-r--r--pith/osdep/lstcmpnt.h28
-rw-r--r--pith/osdep/makefile.wnt65
-rw-r--r--pith/osdep/mimedisp.c473
-rw-r--r--pith/osdep/mimedisp.h28
-rw-r--r--pith/osdep/pipe.c811
-rw-r--r--pith/osdep/pipe.h117
-rw-r--r--pith/osdep/pithosd.h43
-rw-r--r--pith/osdep/pw_stuff.c207
-rw-r--r--pith/osdep/pw_stuff.h29
-rw-r--r--pith/osdep/rename.c69
-rw-r--r--pith/osdep/rename.h26
-rw-r--r--pith/osdep/temp_nam.c345
-rw-r--r--pith/osdep/temp_nam.h28
-rw-r--r--pith/osdep/tempfile.c55
-rw-r--r--pith/osdep/tempfile.h28
-rw-r--r--pith/osdep/writ_dir.c54
-rw-r--r--pith/osdep/writ_dir.h27
-rw-r--r--pith/pattern.c8226
-rw-r--r--pith/pattern.h417
-rw-r--r--pith/pine.hlp35307
-rw-r--r--pith/pineelt.h60
-rw-r--r--pith/pipe.c102
-rw-r--r--pith/pipe.h33
-rw-r--r--pith/readfile.c71
-rw-r--r--pith/readfile.h35
-rw-r--r--pith/remote.c2820
-rw-r--r--pith/remote.h95
-rw-r--r--pith/remtype.h60
-rw-r--r--pith/repltype.h80
-rw-r--r--pith/reply.c3575
-rw-r--r--pith/reply.h106
-rw-r--r--pith/rfc2231.c313
-rw-r--r--pith/rfc2231.h32
-rw-r--r--pith/save.c1777
-rw-r--r--pith/save.h54
-rw-r--r--pith/savetype.h38
-rw-r--r--pith/search.c70
-rw-r--r--pith/search.h25
-rw-r--r--pith/send.c5901
-rw-r--r--pith/send.h271
-rw-r--r--pith/sequence.c460
-rw-r--r--pith/sequence.h33
-rw-r--r--pith/signal.h29
-rw-r--r--pith/smime.c2377
-rw-r--r--pith/smime.h58
-rw-r--r--pith/smkeys.c925
-rw-r--r--pith/smkeys.h61
-rw-r--r--pith/sort.c710
-rw-r--r--pith/sort.h49
-rw-r--r--pith/sorttype.h32
-rw-r--r--pith/state.c318
-rw-r--r--pith/state.h374
-rw-r--r--pith/status.c163
-rw-r--r--pith/status.h54
-rw-r--r--pith/store.c1021
-rw-r--r--pith/store.h80
-rw-r--r--pith/stream.c3392
-rw-r--r--pith/stream.h471
-rw-r--r--pith/string.c2862
-rw-r--r--pith/string.h151
-rw-r--r--pith/strlst.c51
-rw-r--r--pith/strlst.h25
-rw-r--r--pith/takeaddr.c2228
-rw-r--r--pith/takeaddr.h114
-rw-r--r--pith/tempfile.c87
-rw-r--r--pith/tempfile.h26
-rw-r--r--pith/text.c964
-rw-r--r--pith/text.h58
-rw-r--r--pith/thread.c1561
-rw-r--r--pith/thread.h111
-rw-r--r--pith/url.c542
-rw-r--r--pith/url.h31
-rw-r--r--pith/user.h30
-rw-r--r--pith/util.c40
-rw-r--r--pith/util.h68
-rw-r--r--po/ChangeLog12
-rw-r--r--po/Makefile.in384
-rw-r--r--po/Makefile.in.in403
-rw-r--r--po/Makevars41
-rw-r--r--po/Makevars.template41
-rw-r--r--po/POTFILES.in191
-rw-r--r--po/Rules-quot47
-rw-r--r--po/alpine.pot12549
-rw-r--r--po/boldquot.sed10
-rw-r--r--po/en@boldquot.header25
-rw-r--r--po/en@quot.header22
-rw-r--r--po/howto27
-rw-r--r--po/insert-header.sin23
-rw-r--r--po/quot.sed6
-rw-r--r--po/remove-potcdate.sin19
-rw-r--r--regex/Makefile.am19
-rw-r--r--regex/Makefile.in530
-rw-r--r--regex/ReadMe39
-rw-r--r--regex/cclass.h70
-rw-r--r--regex/cname.h141
-rw-r--r--regex/engine.c1053
-rw-r--r--regex/makefile.wnt58
-rw-r--r--regex/re_format.doc264
-rw-r--r--regex/regcomp.c1621
-rw-r--r--regex/regerror.c172
-rw-r--r--regex/regex.doc462
-rw-r--r--regex/regex.h117
-rw-r--r--regex/regex2.h173
-rw-r--r--regex/regexec.c176
-rw-r--r--regex/regfree.c79
-rw-r--r--regex/utils.h60
-rw-r--r--web/INSTALL361
-rw-r--r--web/README81
-rw-r--r--web/bin/README20
l---------web/bin/alpine.tcl1
-rwxr-xr-xweb/bin/launch.tcl51
l---------web/bin/tclsh1
-rwxr-xr-xweb/bin/usage.tcl24
-rw-r--r--web/cgi/.htaccess32
l---------web/cgi/alpine.tcl1
-rwxr-xr-xweb/cgi/alpine/1.0/addrbook.tcl583
-rwxr-xr-xweb/cgi/alpine/1.0/addredit.tcl224
-rwxr-xr-xweb/cgi/alpine/1.0/addrpick.tcl134
-rwxr-xr-xweb/cgi/alpine/1.0/addrsave.tcl244
l---------web/cgi/alpine/1.0/alpine.tcl1
-rwxr-xr-xweb/cgi/alpine/1.0/attach.tcl96
-rwxr-xr-xweb/cgi/alpine/1.0/cledit.tcl232
-rwxr-xr-xweb/cgi/alpine/1.0/cmdfunc.tcl639
-rwxr-xr-xweb/cgi/alpine/1.0/common.tcl289
-rwxr-xr-xweb/cgi/alpine/1.0/compose.tcl804
-rwxr-xr-xweb/cgi/alpine/1.0/comview.tcl184
-rwxr-xr-xweb/cgi/alpine/1.0/conf_process.tcl1358
-rwxr-xr-xweb/cgi/alpine/1.0/detach.tcl183
-rw-r--r--web/cgi/alpine/1.0/do_help.tcl48
-rwxr-xr-xweb/cgi/alpine/1.0/do_open.tcl126
-rwxr-xr-xweb/cgi/alpine/1.0/do_quit.tcl98
-rwxr-xr-xweb/cgi/alpine/1.0/do_view.tcl190
-rwxr-xr-xweb/cgi/alpine/1.0/docancel.tcl56
-rwxr-xr-xweb/cgi/alpine/1.0/dosend.tcl99
-rwxr-xr-xweb/cgi/alpine/1.0/export.tcl165
-rw-r--r--web/cgi/alpine/1.0/exporting.tcl187
-rwxr-xr-xweb/cgi/alpine/1.0/filtedit.tcl704
-rwxr-xr-xweb/cgi/alpine/1.0/filter.tcl63
-rwxr-xr-xweb/cgi/alpine/1.0/flags.tcl112
-rwxr-xr-xweb/cgi/alpine/1.0/fldrbrowse.tcl335
-rwxr-xr-xweb/cgi/alpine/1.0/fldrsavenew.tcl202
-rwxr-xr-xweb/cgi/alpine/1.0/folders.tcl727
-rwxr-xr-xweb/cgi/alpine/1.0/fr_addrbrowse.tcl66
-rw-r--r--web/cgi/alpine/1.0/fr_addredit.tcl86
-rwxr-xr-xweb/cgi/alpine/1.0/fr_cledit.tcl79
-rwxr-xr-xweb/cgi/alpine/1.0/fr_compose.tcl144
-rwxr-xr-xweb/cgi/alpine/1.0/fr_filtedit.tcl136
-rwxr-xr-xweb/cgi/alpine/1.0/fr_flags.tcl67
-rw-r--r--web/cgi/alpine/1.0/fr_fldrbrowse.tcl71
-rw-r--r--web/cgi/alpine/1.0/fr_fldrsavenew.tcl65
-rwxr-xr-xweb/cgi/alpine/1.0/fr_help.tcl91
-rwxr-xr-xweb/cgi/alpine/1.0/fr_index.tcl58
-rwxr-xr-xweb/cgi/alpine/1.0/fr_ldapbrowse.tcl61
-rw-r--r--web/cgi/alpine/1.0/fr_ldapquery.tcl75
-rwxr-xr-xweb/cgi/alpine/1.0/fr_main.tcl34
-rwxr-xr-xweb/cgi/alpine/1.0/fr_promptsave.tcl68
-rwxr-xr-xweb/cgi/alpine/1.0/fr_queryattach.tcl70
-rwxr-xr-xweb/cgi/alpine/1.0/fr_queryauth.tcl71
-rwxr-xr-xweb/cgi/alpine/1.0/fr_querycreate.tcl42
-rwxr-xr-xweb/cgi/alpine/1.0/fr_querydelfldr.tcl43
-rwxr-xr-xweb/cgi/alpine/1.0/fr_queryexpunge.tcl71
-rw-r--r--web/cgi/alpine/1.0/fr_queryimport.tcl44
-rwxr-xr-xweb/cgi/alpine/1.0/fr_querynewdir.tcl43
-rwxr-xr-xweb/cgi/alpine/1.0/fr_querynewfldr.tcl43
-rwxr-xr-xweb/cgi/alpine/1.0/fr_querynewfoldir.tcl43
-rw-r--r--web/cgi/alpine/1.0/fr_queryprune.tcl66
-rw-r--r--web/cgi/alpine/1.0/fr_queryquit.tcl70
-rwxr-xr-xweb/cgi/alpine/1.0/fr_queryrenfldr.tcl43
-rwxr-xr-xweb/cgi/alpine/1.0/fr_querysave.tcl68
-rwxr-xr-xweb/cgi/alpine/1.0/fr_resume.tcl104
-rwxr-xr-xweb/cgi/alpine/1.0/fr_seldate.tcl70
-rwxr-xr-xweb/cgi/alpine/1.0/fr_select.tcl71
-rwxr-xr-xweb/cgi/alpine/1.0/fr_selstat.tcl71
-rwxr-xr-xweb/cgi/alpine/1.0/fr_seltext.tcl71
-rwxr-xr-xweb/cgi/alpine/1.0/fr_spellcheck.tcl71
-rw-r--r--web/cgi/alpine/1.0/fr_split.tcl58
-rw-r--r--web/cgi/alpine/1.0/fr_take.tcl70
-rw-r--r--web/cgi/alpine/1.0/fr_takeedit.tcl79
-rw-r--r--web/cgi/alpine/1.0/fr_takesame.tcl79
-rwxr-xr-xweb/cgi/alpine/1.0/fr_tconfig.tcl69
-rwxr-xr-xweb/cgi/alpine/1.0/fr_view.tcl30
-rwxr-xr-xweb/cgi/alpine/1.0/genvars.tcl114
-rwxr-xr-xweb/cgi/alpine/1.0/header.tcl226
-rwxr-xr-xweb/cgi/alpine/1.0/help.tcl36
-rw-r--r--web/cgi/alpine/1.0/help/about.tcl30
-rw-r--r--web/cgi/alpine/1.0/help/addrbook.html61
-rw-r--r--web/cgi/alpine/1.0/help/addrbrowse.html27
-rw-r--r--web/cgi/alpine/1.0/help/addredit.html59
-rw-r--r--web/cgi/alpine/1.0/help/attach.html37
-rw-r--r--web/cgi/alpine/1.0/help/compose.html147
-rw-r--r--web/cgi/alpine/1.0/help/create_save.tcl17
-rw-r--r--web/cgi/alpine/1.0/help/expunge.html20
-rw-r--r--web/cgi/alpine/1.0/help/filtconf.html62
-rw-r--r--web/cgi/alpine/1.0/help/filtedit.html240
-rw-r--r--web/cgi/alpine/1.0/help/folders.html39
-rw-r--r--web/cgi/alpine/1.0/help/foldiradd.html30
-rw-r--r--web/cgi/alpine/1.0/help/forward.tcl16
-rw-r--r--web/cgi/alpine/1.0/help/index.html281
-rw-r--r--web/cgi/alpine/1.0/help/index.tcl.1301
-rw-r--r--web/cgi/alpine/1.0/help/release.html83
-rw-r--r--web/cgi/alpine/1.0/help/reply.tcl17
-rw-r--r--web/cgi/alpine/1.0/help/resume.html28
-rw-r--r--web/cgi/alpine/1.0/help/secure.html119
-rw-r--r--web/cgi/alpine/1.0/help/takeaddr.html34
-rw-r--r--web/cgi/alpine/1.0/help/takeedit.html63
-rw-r--r--web/cgi/alpine/1.0/help/tech-notes.html471
-rw-r--r--web/cgi/alpine/1.0/help/view.html195
-rw-r--r--web/cgi/alpine/1.0/help/wpsys.jpegbin0 -> 17284 bytes
-rwxr-xr-xweb/cgi/alpine/1.0/helpbody.tcl200
-rwxr-xr-xweb/cgi/alpine/1.0/helpindex.tcl102
-rwxr-xr-xweb/cgi/alpine/1.0/index.tcl1972
-rwxr-xr-xweb/cgi/alpine/1.0/ldapbrowse.tcl220
-rwxr-xr-xweb/cgi/alpine/1.0/ldapentry.tcl315
-rwxr-xr-xweb/cgi/alpine/1.0/ldappick.tcl76
-rwxr-xr-xweb/cgi/alpine/1.0/ldapquery.tcl144
-rwxr-xr-xweb/cgi/alpine/1.0/ldapresult.tcl375
-rwxr-xr-xweb/cgi/alpine/1.0/main.tcl84
-rwxr-xr-xweb/cgi/alpine/1.0/open.tcl33
-rwxr-xr-xweb/cgi/alpine/1.0/post.tcl651
-rwxr-xr-xweb/cgi/alpine/1.0/promptsave.tcl149
-rwxr-xr-xweb/cgi/alpine/1.0/prune.tcl74
-rwxr-xr-xweb/cgi/alpine/1.0/queryattach.tcl145
-rwxr-xr-xweb/cgi/alpine/1.0/querycreate.tcl114
-rwxr-xr-xweb/cgi/alpine/1.0/querydelfldr.tcl121
-rwxr-xr-xweb/cgi/alpine/1.0/queryexpunge.tcl203
-rwxr-xr-xweb/cgi/alpine/1.0/queryimport.tcl146
-rwxr-xr-xweb/cgi/alpine/1.0/querynewdir.tcl113
-rwxr-xr-xweb/cgi/alpine/1.0/querynewfldr.tcl112
-rwxr-xr-xweb/cgi/alpine/1.0/querynewfoldir.tcl131
-rwxr-xr-xweb/cgi/alpine/1.0/querynick.tcl171
-rwxr-xr-xweb/cgi/alpine/1.0/queryprune.tcl170
-rwxr-xr-xweb/cgi/alpine/1.0/queryquit.tcl196
-rwxr-xr-xweb/cgi/alpine/1.0/queryrenfldr.tcl109
-rwxr-xr-xweb/cgi/alpine/1.0/querysave.tcl98
-rwxr-xr-xweb/cgi/alpine/1.0/resume.tcl169
-rwxr-xr-xweb/cgi/alpine/1.0/ripcord.tcl64
-rwxr-xr-xweb/cgi/alpine/1.0/seldate.tcl193
-rwxr-xr-xweb/cgi/alpine/1.0/select.tcl303
-rwxr-xr-xweb/cgi/alpine/1.0/select2.tcl318
-rwxr-xr-xweb/cgi/alpine/1.0/selstat.tcl156
-rwxr-xr-xweb/cgi/alpine/1.0/seltext.tcl181
-rwxr-xr-xweb/cgi/alpine/1.0/spellcheck.tcl399
-rwxr-xr-xweb/cgi/alpine/1.0/takeaddr.tcl215
l---------web/cgi/alpine/1.0/tclsh1
-rwxr-xr-xweb/cgi/alpine/1.0/tconfig.tcl1183
-rwxr-xr-xweb/cgi/alpine/1.0/view.tcl920
-rwxr-xr-xweb/cgi/alpine/1.0/wp.tcl135
-rw-r--r--web/cgi/alpine/2.0/.htaccess7
l---------web/cgi/alpine/2.0/alpine.tcl1
-rwxr-xr-xweb/cgi/alpine/2.0/browse336
-rw-r--r--web/cgi/alpine/2.0/common.tcl905
-rwxr-xr-xweb/cgi/alpine/2.0/compose817
-rwxr-xr-xweb/cgi/alpine/2.0/conduit/apply.tcl76
-rwxr-xr-xweb/cgi/alpine/2.0/conduit/attach.tcl101
-rwxr-xr-xweb/cgi/alpine/2.0/conduit/cert.tcl48
-rwxr-xr-xweb/cgi/alpine/2.0/conduit/complete.tcl54
-rwxr-xr-xweb/cgi/alpine/2.0/conduit/contactlist.tcl306
-rwxr-xr-xweb/cgi/alpine/2.0/conduit/empty.tcl73
-rwxr-xr-xweb/cgi/alpine/2.0/conduit/exists.tcl65
-rwxr-xr-xweb/cgi/alpine/2.0/conduit/expand.tcl84
-rwxr-xr-xweb/cgi/alpine/2.0/conduit/export168
-rwxr-xr-xweb/cgi/alpine/2.0/conduit/flag.tcl77
-rwxr-xr-xweb/cgi/alpine/2.0/conduit/folderlist.tcl255
-rwxr-xr-xweb/cgi/alpine/2.0/conduit/getcontact.tcl53
-rwxr-xr-xweb/cgi/alpine/2.0/conduit/import73
-rwxr-xr-xweb/cgi/alpine/2.0/conduit/mark.tcl77
-rwxr-xr-xweb/cgi/alpine/2.0/conduit/newmail.tcl56
-rwxr-xr-xweb/cgi/alpine/2.0/conduit/post.tcl333
-rwxr-xr-xweb/cgi/alpine/2.0/conduit/query.tcl83
-rwxr-xr-xweb/cgi/alpine/2.0/conduit/settings.tcl184
-rwxr-xr-xweb/cgi/alpine/2.0/conduit/storecontact.tcl67
-rwxr-xr-xweb/cgi/alpine/2.0/conduit/take.tcl84
l---------web/cgi/alpine/2.0/conduit/tclsh1
-rwxr-xr-xweb/cgi/alpine/2.0/contacts201
-rw-r--r--web/cgi/alpine/2.0/css/cbn/contactdialog.css108
-rw-r--r--web/cgi/alpine/2.0/css/cbn/contacts.css87
-rw-r--r--web/cgi/alpine/2.0/css/cbn/folderdialog.css96
-rw-r--r--web/cgi/alpine/2.0/css/cbn/folders.css99
-rw-r--r--web/cgi/alpine/2.0/css/cbn/screen.css2084
-rw-r--r--web/cgi/alpine/2.0/css/help.css119
-rw-r--r--web/cgi/alpine/2.0/css/help_popup.css114
-rw-r--r--web/cgi/alpine/2.0/css/menu.css71
-rw-r--r--web/cgi/alpine/2.0/css/print.css0
-rwxr-xr-xweb/cgi/alpine/2.0/detach172
-rw-r--r--web/cgi/alpine/2.0/foldercache.tcl177
-rwxr-xr-xweb/cgi/alpine/2.0/folders240
l---------web/cgi/alpine/2.0/forward1
-rw-r--r--web/cgi/alpine/2.0/help/alpha-index.html225
-rw-r--r--web/cgi/alpine/2.0/help/compose.html102
-rw-r--r--web/cgi/alpine/2.0/help/contacts.html80
-rw-r--r--web/cgi/alpine/2.0/help/folders.html96
-rw-r--r--web/cgi/alpine/2.0/help/graphics/attach_sm.gifbin0 -> 235 bytes
-rw-r--r--web/cgi/alpine/2.0/help/graphics/compose.gifbin0 -> 14390 bytes
-rw-r--r--web/cgi/alpine/2.0/help/graphics/fwd.gifbin0 -> 202 bytes
-rw-r--r--web/cgi/alpine/2.0/help/graphics/new.gifbin0 -> 236 bytes
-rw-r--r--web/cgi/alpine/2.0/help/graphics/parts.gifbin0 -> 19744 bytes
-rw-r--r--web/cgi/alpine/2.0/help/graphics/replied.gifbin0 -> 207 bytes
-rw-r--r--web/cgi/alpine/2.0/help/graphics/replied_and_fwd.gifbin0 -> 216 bytes
-rw-r--r--web/cgi/alpine/2.0/help/graphics/screen-header.gifbin0 -> 8054 bytes
-rw-r--r--web/cgi/alpine/2.0/help/graphics/star.gifbin0 -> 573 bytes
-rw-r--r--web/cgi/alpine/2.0/help/help_home.htm46
-rw-r--r--web/cgi/alpine/2.0/help/help_index.htm67
-rw-r--r--web/cgi/alpine/2.0/help/help_settings.htm66
-rw-r--r--web/cgi/alpine/2.0/help/inbox.html145
-rw-r--r--web/cgi/alpine/2.0/help/popup/_notes/dwsync.xml4
-rw-r--r--web/cgi/alpine/2.0/help/popup/help_set_display.htm36
-rw-r--r--web/cgi/alpine/2.0/help/quick-start.html136
-rw-r--r--web/cgi/alpine/2.0/help/read.html145
-rw-r--r--web/cgi/alpine/2.0/help/search.html77
-rw-r--r--web/cgi/alpine/2.0/help/settings.html124
-rw-r--r--web/cgi/alpine/2.0/help/topic-list.html101
-rw-r--r--web/cgi/alpine/2.0/img/cbn/Thumbs.dbbin0 -> 147456 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/_notes/dwsync.xml91
-rw-r--r--web/cgi/alpine/2.0/img/cbn/addcontact_sm.gifbin0 -> 1000 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/addfolder.gifbin0 -> 384 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/alert.gifbin0 -> 2139 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/alpinelogo.gifbin0 -> 2306 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/attach.gifbin0 -> 248 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/attach_sm.gifbin0 -> 235 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/base.gifbin0 -> 372 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/blank.gifbin0 -> 43 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/border-bl.gifbin0 -> 813 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/border-br.gifbin0 -> 812 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/border-ft.gifbin0 -> 815 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/border-lt-2.gifbin0 -> 959 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/border-lt.gifbin0 -> 426 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/border-rt-2.gifbin0 -> 961 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/border-rt.gifbin0 -> 426 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/border-tl.gifbin0 -> 295 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/border-tr.gifbin0 -> 295 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/btnbg.gifbin0 -> 283 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/btnhi.gifbin0 -> 283 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/but_back.gifbin0 -> 151 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/ccme.gifbin0 -> 112 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/check2.gifbin0 -> 1151 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/checkmail.gifbin0 -> 1249 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/close.gifbin0 -> 177 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/close2.gifbin0 -> 1013 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/close3.gifbin0 -> 958 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/col.gifbin0 -> 95 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/colsel.gifbin0 -> 146 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/compose.gifbin0 -> 642 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/contacts.gifbin0 -> 1071 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/dawg.gifbin0 -> 377 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/dbar.gifbin0 -> 825 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/delete.gifbin0 -> 256 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/detach.gifbin0 -> 417 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/div.gifbin0 -> 45 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/div2.gifbin0 -> 45 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/dn.gifbin0 -> 59 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/edit.gifbin0 -> 564 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/exclaim.gifbin0 -> 90 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/f_contacts.gifbin0 -> 1046 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/f_delete.gifbin0 -> 244 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/f_drafts.gifbin0 -> 349 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/f_folder.gifbin0 -> 212 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/f_inbox.gifbin0 -> 237 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/f_manage.gifbin0 -> 241 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/f_minus.gifbin0 -> 201 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/f_plus.gifbin0 -> 204 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/f_search.gifbin0 -> 242 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/f_sent.gifbin0 -> 348 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/f_spam.gifbin0 -> 364 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/fldsel.gifbin0 -> 149 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/folder.gifbin0 -> 372 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/folderopen.gifbin0 -> 376 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/foldersopen.gifbin0 -> 400 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/forward.gifbin0 -> 652 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/fwd.gifbin0 -> 202 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/group_contact.gifbin0 -> 1121 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/help.gifbin0 -> 1037 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/help_sm.gifbin0 -> 631 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/high.gifbin0 -> 906 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/highest.gifbin0 -> 938 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/inbox.gifbin0 -> 1169 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/info.gifbin0 -> 111 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/infomsg.gifbin0 -> 1044 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/lo.gifbin0 -> 150 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/logo.gifbin0 -> 4117 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/logo.pngbin0 -> 9271 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/logoff.gifbin0 -> 1092 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/lookup.gifbin0 -> 413 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/low.gifbin0 -> 99 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/menu.gifbin0 -> 81 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/msglist.gifbin0 -> 413 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/msglist.gif.bakbin0 -> 413 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/musicfolder.gifbin0 -> 633 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/navbg.gifbin0 -> 283 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/new.gifbin0 -> 236 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/page.gifbin0 -> 582 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/parent.gifbin0 -> 374 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/partly.gifbin0 -> 371 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/pg_down.gifbin0 -> 133 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/pg_first.gifbin0 -> 213 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/pg_last.gifbin0 -> 217 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/pg_next.gifbin0 -> 144 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/pg_prev.gifbin0 -> 139 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/pg_up.gifbin0 -> 136 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/print.gifbin0 -> 252 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/question.gifbin0 -> 1538 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/question.jpgbin0 -> 1278 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/question.pngbin0 -> 3304 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/question2.jpgbin0 -> 1599 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/remove.gifbin0 -> 1031 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/rename.gifbin0 -> 161 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/replied.gifbin0 -> 207 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/replied_and_fwd.gifbin0 -> 216 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/reply.gifbin0 -> 669 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/replyall.gifbin0 -> 671 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/return.gifbin0 -> 75 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/rt.gifbin0 -> 71 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/save.gifbin0 -> 259 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/search.gifbin0 -> 718 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/send.gifbin0 -> 407 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/settings.gifbin0 -> 996 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/sound.gifbin0 -> 1014 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/spam.gifbin0 -> 623 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/spam2.gifbin0 -> 1426 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/spell.gifbin0 -> 166 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/spritelib.gifbin0 -> 10907 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/spritelib.pngbin0 -> 24911 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/star.gifbin0 -> 573 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/starred.gifbin0 -> 541 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/statbak.gifbin0 -> 145 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/statbakend.gifbin0 -> 89 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/statbakl.gifbin0 -> 93 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/statbakr.gifbin0 -> 93 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/sunny.gifbin0 -> 360 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/tome.gifbin0 -> 122 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/trans.gifbin0 -> 55 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/up.gifbin0 -> 59 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/uw.gifbin0 -> 1569 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/uwlogo.gifbin0 -> 2458 bytes
-rw-r--r--web/cgi/alpine/2.0/img/cbn/wbar.gifbin0 -> 94 bytes
-rw-r--r--web/cgi/alpine/2.0/lib/INSTALL3
-rw-r--r--web/cgi/alpine/2.0/lib/common.js1586
-rw-r--r--web/cgi/alpine/2.0/lib/compose.js833
-rw-r--r--web/cgi/alpine/2.0/lib/contacts.js121
-rw-r--r--web/cgi/alpine/2.0/lib/folders.js258
-rw-r--r--web/cgi/alpine/2.0/lib/mailbox.js1076
-rw-r--r--web/cgi/alpine/2.0/lib/settings.js183
l---------web/cgi/alpine/2.0/lib/yui1
l---------web/cgi/alpine/2.0/mailto1
-rw-r--r--web/cgi/alpine/2.0/messagelist.tcl622
-rw-r--r--web/cgi/alpine/2.0/messageview.tcl731
-rwxr-xr-xweb/cgi/alpine/2.0/newlist.tcl477
-rwxr-xr-xweb/cgi/alpine/2.0/newview.tcl306
l---------web/cgi/alpine/2.0/reply1
l---------web/cgi/alpine/2.0/replyall1
l---------web/cgi/alpine/2.0/resume1
-rwxr-xr-xweb/cgi/alpine/2.0/settings734
l---------web/cgi/alpine/2.0/tclsh1
-rwxr-xr-xweb/cgi/alpine/2.0/view237
l---------web/cgi/alpine/alpine.tcl1
-rwxr-xr-xweb/cgi/alpine/farewell.tcl40
l---------web/cgi/alpine/tclsh1
-rwxr-xr-xweb/cgi/alpine/whackatch.tcl46
l---------web/cgi/detach1
-rwxr-xr-xweb/cgi/favicon.icobin0 -> 5694 bytes
-rwxr-xr-xweb/cgi/greeting.tcl21
-rw-r--r--web/cgi/images/Lavender_Chiffon.gifbin0 -> 2578 bytes
-rw-r--r--web/cgi/images/b_minus.gifbin0 -> 64 bytes
-rw-r--r--web/cgi/images/b_plus.gifbin0 -> 81 bytes
-rw-r--r--web/cgi/images/barblank.gifbin0 -> 57 bytes
-rw-r--r--web/cgi/images/barclose.gifbin0 -> 96 bytes
-rw-r--r--web/cgi/images/barclose_mid.gifbin0 -> 65 bytes
-rw-r--r--web/cgi/images/barmsg.gifbin0 -> 71 bytes
-rw-r--r--web/cgi/images/baropen.gifbin0 -> 91 bytes
-rw-r--r--web/cgi/images/baropen_mid.gifbin0 -> 65 bytes
-rw-r--r--web/cgi/images/barvert.gifbin0 -> 73 bytes
-rw-r--r--web/cgi/images/barvertmsg.gifbin0 -> 76 bytes
-rw-r--r--web/cgi/images/bg_index.gifbin0 -> 46 bytes
-rw-r--r--web/cgi/images/blackdot.gifbin0 -> 35 bytes
-rw-r--r--web/cgi/images/book.gifbin0 -> 279 bytes
-rw-r--r--web/cgi/images/but_abook.gifbin0 -> 265 bytes
-rw-r--r--web/cgi/images/but_cancel.gifbin0 -> 213 bytes
-rw-r--r--web/cgi/images/but_create.gifbin0 -> 211 bytes
-rw-r--r--web/cgi/images/but_folddel.gifbin0 -> 269 bytes
-rw-r--r--web/cgi/images/but_foldexp.gifbin0 -> 279 bytes
-rw-r--r--web/cgi/images/but_foldren.gifbin0 -> 302 bytes
-rw-r--r--web/cgi/images/but_remove.gifbin0 -> 145 bytes
-rw-r--r--web/cgi/images/but_resume.gifbin0 -> 218 bytes
-rw-r--r--web/cgi/images/but_rnd_block.gifbin0 -> 2067 bytes
-rw-r--r--web/cgi/images/but_rnd_first3.gifbin0 -> 911 bytes
-rw-r--r--web/cgi/images/but_rnd_last3.gifbin0 -> 905 bytes
-rw-r--r--web/cgi/images/but_rnd_next3.gifbin0 -> 908 bytes
-rw-r--r--web/cgi/images/but_rnd_prev3.gifbin0 -> 923 bytes
-rw-r--r--web/cgi/images/but_s_do.gifbin0 -> 158 bytes
-rw-r--r--web/cgi/images/but_save.gifbin0 -> 197 bytes
-rw-r--r--web/cgi/images/buttons/silver/allmsgs.gifbin0 -> 2882 bytes
-rw-r--r--web/cgi/images/buttons/silver/attach.gifbin0 -> 2805 bytes
-rw-r--r--web/cgi/images/buttons/silver/cancel.gifbin0 -> 2807 bytes
-rw-r--r--web/cgi/images/buttons/silver/compose.gifbin0 -> 2893 bytes
-rw-r--r--web/cgi/images/buttons/silver/compose8.gifbin0 -> 680 bytes
-rw-r--r--web/cgi/images/buttons/silver/config.gifbin0 -> 2889 bytes
-rw-r--r--web/cgi/images/buttons/silver/delete.gifbin0 -> 2807 bytes
-rw-r--r--web/cgi/images/buttons/silver/delete2.gifbin0 -> 2076 bytes
-rw-r--r--web/cgi/images/buttons/silver/done.gifbin0 -> 2722 bytes
-rw-r--r--web/cgi/images/buttons/silver/exit.gifbin0 -> 2896 bytes
-rw-r--r--web/cgi/images/buttons/silver/expunge.gifbin0 -> 2863 bytes
-rw-r--r--web/cgi/images/buttons/silver/firstpage.gifbin0 -> 2575 bytes
-rw-r--r--web/cgi/images/buttons/silver/firstpage2.gifbin0 -> 1706 bytes
-rw-r--r--web/cgi/images/buttons/silver/firstpage4.gifbin0 -> 1703 bytes
-rw-r--r--web/cgi/images/buttons/silver/folders.gifbin0 -> 2903 bytes
-rw-r--r--web/cgi/images/buttons/silver/forward.gifbin0 -> 2908 bytes
-rw-r--r--web/cgi/images/buttons/silver/lastpage.gifbin0 -> 2595 bytes
-rw-r--r--web/cgi/images/buttons/silver/lastpage2.gifbin0 -> 1717 bytes
-rw-r--r--web/cgi/images/buttons/silver/lastpage4.gifbin0 -> 1691 bytes
-rw-r--r--web/cgi/images/buttons/silver/logout.gifbin0 -> 2832 bytes
-rw-r--r--web/cgi/images/buttons/silver/messages.gifbin0 -> 2894 bytes
-rw-r--r--web/cgi/images/buttons/silver/nextpage.gifbin0 -> 2520 bytes
-rw-r--r--web/cgi/images/buttons/silver/nextpage2.gifbin0 -> 2611 bytes
-rw-r--r--web/cgi/images/buttons/silver/ok.gifbin0 -> 2640 bytes
-rw-r--r--web/cgi/images/buttons/silver/prevpage.gifbin0 -> 2485 bytes
-rw-r--r--web/cgi/images/buttons/silver/prevpage2.gifbin0 -> 1672 bytes
-rw-r--r--web/cgi/images/buttons/silver/quit.gifbin0 -> 2689 bytes
-rw-r--r--web/cgi/images/buttons/silver/reply.gifbin0 -> 2805 bytes
-rw-r--r--web/cgi/images/buttons/silver/replyall.gifbin0 -> 2932 bytes
-rw-r--r--web/cgi/images/buttons/silver/save.gifbin0 -> 2733 bytes
-rw-r--r--web/cgi/images/buttons/silver/send.gifbin0 -> 2738 bytes
-rw-r--r--web/cgi/images/buttons/silver/undelete.gifbin0 -> 2864 bytes
-rw-r--r--web/cgi/images/caution.gifbin0 -> 134 bytes
-rw-r--r--web/cgi/images/cf_add.gifbin0 -> 150 bytes
-rw-r--r--web/cgi/images/cf_delete.gifbin0 -> 166 bytes
-rw-r--r--web/cgi/images/cf_edit.gifbin0 -> 148 bytes
-rw-r--r--web/cgi/images/cf_help.gifbin0 -> 65 bytes
-rw-r--r--web/cgi/images/cf_shdown.gifbin0 -> 102 bytes
-rw-r--r--web/cgi/images/cf_shup.gifbin0 -> 103 bytes
-rw-r--r--web/cgi/images/decreas4.gifbin0 -> 900 bytes
-rw-r--r--web/cgi/images/dot.gifbin0 -> 49 bytes
-rw-r--r--web/cgi/images/dot2.gifbin0 -> 43 bytes
-rw-r--r--web/cgi/images/dotblink.gifbin0 -> 261 bytes
-rw-r--r--web/cgi/images/dstripe.gifbin0 -> 73 bytes
-rw-r--r--web/cgi/images/env/d_new.gifbin0 -> 142 bytes
-rw-r--r--web/cgi/images/env/d_newans.gifbin0 -> 157 bytes
-rw-r--r--web/cgi/images/env/d_newansdel.gifbin0 -> 209 bytes
-rw-r--r--web/cgi/images/env/d_newdel.gifbin0 -> 235 bytes
-rw-r--r--web/cgi/images/env/d_newimp.gifbin0 -> 169 bytes
-rw-r--r--web/cgi/images/env/d_newimpans.gifbin0 -> 219 bytes
-rw-r--r--web/cgi/images/env/d_newimpansdel.gifbin0 -> 241 bytes
-rw-r--r--web/cgi/images/env/d_newimpdel.gifbin0 -> 222 bytes
-rw-r--r--web/cgi/images/env/d_newyou.gifbin0 -> 156 bytes
-rw-r--r--web/cgi/images/env/d_newyouans.gifbin0 -> 173 bytes
-rw-r--r--web/cgi/images/env/d_newyouansdel.gifbin0 -> 225 bytes
-rw-r--r--web/cgi/images/env/d_newyoudel.gifbin0 -> 177 bytes
-rw-r--r--web/cgi/images/env/d_read.gifbin0 -> 159 bytes
-rw-r--r--web/cgi/images/env/d_readans.gifbin0 -> 203 bytes
-rw-r--r--web/cgi/images/env/d_readansdel.gifbin0 -> 221 bytes
-rw-r--r--web/cgi/images/env/d_readdel.gifbin0 -> 282 bytes
-rw-r--r--web/cgi/images/env/d_readimp.gifbin0 -> 187 bytes
-rw-r--r--web/cgi/images/env/d_readimpans.gifbin0 -> 234 bytes
-rw-r--r--web/cgi/images/env/d_readimpansdel.gifbin0 -> 250 bytes
-rw-r--r--web/cgi/images/env/d_readimpdel.gifbin0 -> 310 bytes
-rw-r--r--web/cgi/images/env/d_readyou.gifbin0 -> 171 bytes
-rw-r--r--web/cgi/images/env/d_readyouans.gifbin0 -> 218 bytes
-rw-r--r--web/cgi/images/env/d_readyouansdel.gifbin0 -> 236 bytes
-rw-r--r--web/cgi/images/env/d_readyoudel.gifbin0 -> 292 bytes
-rw-r--r--web/cgi/images/env/new.gifbin0 -> 144 bytes
-rw-r--r--web/cgi/images/env/newans.gifbin0 -> 196 bytes
-rw-r--r--web/cgi/images/env/newansdel.gifbin0 -> 216 bytes
-rw-r--r--web/cgi/images/env/newdel.gifbin0 -> 162 bytes
-rw-r--r--web/cgi/images/env/newimp.gifbin0 -> 174 bytes
-rw-r--r--web/cgi/images/env/newimpans.gifbin0 -> 200 bytes
-rw-r--r--web/cgi/images/env/newimpansdel.gifbin0 -> 245 bytes
-rw-r--r--web/cgi/images/env/newimpdel.gifbin0 -> 220 bytes
-rw-r--r--web/cgi/images/env/newyou.gifbin0 -> 161 bytes
-rw-r--r--web/cgi/images/env/newyouans.gifbin0 -> 208 bytes
-rw-r--r--web/cgi/images/env/newyouansdel.gifbin0 -> 231 bytes
-rw-r--r--web/cgi/images/env/newyoudel.gifbin0 -> 204 bytes
-rw-r--r--web/cgi/images/env/read.gifbin0 -> 165 bytes
-rw-r--r--web/cgi/images/env/readans.gifbin0 -> 182 bytes
-rw-r--r--web/cgi/images/env/readansdel.gifbin0 -> 229 bytes
-rw-r--r--web/cgi/images/env/readdel.gifbin0 -> 184 bytes
-rw-r--r--web/cgi/images/env/readimp.gifbin0 -> 192 bytes
-rw-r--r--web/cgi/images/env/readimpans.gifbin0 -> 212 bytes
-rw-r--r--web/cgi/images/env/readimpansdel.gifbin0 -> 256 bytes
-rw-r--r--web/cgi/images/env/readimpdel.gifbin0 -> 235 bytes
-rw-r--r--web/cgi/images/env/readyou.border.gifbin0 -> 209 bytes
-rw-r--r--web/cgi/images/env/readyou.gifbin0 -> 173 bytes
-rw-r--r--web/cgi/images/env/readyouans.gifbin0 -> 196 bytes
-rw-r--r--web/cgi/images/env/readyouansdel.gifbin0 -> 238 bytes
-rw-r--r--web/cgi/images/env/readyoudel.gifbin0 -> 217 bytes
-rw-r--r--web/cgi/images/hdr.gifbin0 -> 95 bytes
-rw-r--r--web/cgi/images/hdrless.gifbin0 -> 164 bytes
-rw-r--r--web/cgi/images/hdrmore.gifbin0 -> 170 bytes
-rw-r--r--web/cgi/images/hdrnon.gifbin0 -> 108 bytes
-rw-r--r--web/cgi/images/help_trans.gifbin0 -> 125 bytes
-rw-r--r--web/cgi/images/ibarmsg.gifbin0 -> 137 bytes
-rw-r--r--web/cgi/images/ibarvertmsg.gifbin0 -> 76 bytes
-rw-r--r--web/cgi/images/if_blank.gifbin0 -> 325 bytes
-rw-r--r--web/cgi/images/if_left.gifbin0 -> 329 bytes
-rw-r--r--web/cgi/images/if_narrow.gifbin0 -> 408 bytes
-rw-r--r--web/cgi/images/if_narrow2.gifbin0 -> 328 bytes
-rw-r--r--web/cgi/images/if_remove.gifbin0 -> 318 bytes
-rw-r--r--web/cgi/images/if_right.gifbin0 -> 330 bytes
-rw-r--r--web/cgi/images/if_widen.gifbin0 -> 412 bytes
-rw-r--r--web/cgi/images/if_wider.gifbin0 -> 328 bytes
-rw-r--r--web/cgi/images/increas4.gifbin0 -> 838 bytes
-rw-r--r--web/cgi/images/indexhdr.gifbin0 -> 143 bytes
-rw-r--r--web/cgi/images/logo/alpine/back.gifbin0 -> 251 bytes
-rw-r--r--web/cgi/images/logo/alpine/big.gifbin0 -> 9059 bytes
-rw-r--r--web/cgi/images/logo/alpine/small-blank.gifbin0 -> 3352 bytes
-rw-r--r--web/cgi/images/logo/alpine/small.gifbin0 -> 3067 bytes
-rw-r--r--web/cgi/images/markall3.gifbin0 -> 189 bytes
-rw-r--r--web/cgi/images/marknone3.gifbin0 -> 165 bytes
-rw-r--r--web/cgi/images/minus2.gifbin0 -> 64 bytes
-rw-r--r--web/cgi/images/nondither10x10.gifbin0 -> 6829 bytes
-rw-r--r--web/cgi/images/plus2.gifbin0 -> 73 bytes
-rw-r--r--web/cgi/images/postmark.gifbin0 -> 562 bytes
-rw-r--r--web/cgi/images/printer2.gifbin0 -> 145 bytes
-rw-r--r--web/cgi/images/slidein.gifbin0 -> 126 bytes
-rw-r--r--web/cgi/images/slideout.gifbin0 -> 125 bytes
-rw-r--r--web/cgi/images/tabless.gifbin0 -> 90 bytes
-rw-r--r--web/cgi/images/tabmore.gifbin0 -> 102 bytes
-rw-r--r--web/cgi/images/tabs/abdtab.gifbin0 -> 550 bytes
-rw-r--r--web/cgi/images/tabs/abtab.gifbin0 -> 265 bytes
-rw-r--r--web/cgi/images/tabs/blank.gifbin0 -> 130 bytes
-rw-r--r--web/cgi/images/tabs/cdtab.gifbin0 -> 493 bytes
-rw-r--r--web/cgi/images/tabs/ctab.gifbin0 -> 214 bytes
-rw-r--r--web/cgi/images/tabs/fdtab.gifbin0 -> 461 bytes
-rw-r--r--web/cgi/images/tabs/ftab.gifbin0 -> 190 bytes
-rw-r--r--web/cgi/images/tabs/gdtab.gifbin0 -> 471 bytes
-rw-r--r--web/cgi/images/tabs/gtab.gifbin0 -> 196 bytes
-rw-r--r--web/cgi/images/tabs/mldtab.gifbin0 -> 529 bytes
-rw-r--r--web/cgi/images/tabs/mltab.gifbin0 -> 251 bytes
-rw-r--r--web/cgi/images/tabs/mvdtab.gifbin0 -> 540 bytes
-rw-r--r--web/cgi/images/tabs/mvtab.gifbin0 -> 255 bytes
-rw-r--r--web/cgi/images/tabs/rdtab.gifbin0 -> 437 bytes
-rw-r--r--web/cgi/images/tabs/rtab.gifbin0 -> 173 bytes
-rw-r--r--web/cgi/images/tabs/tabbg.gifbin0 -> 236 bytes
-rw-r--r--web/cgi/images/tabs/tabmid.gifbin0 -> 64 bytes
-rw-r--r--web/cgi/motd.sample5
l---------web/cgi/pub/alpine.tcl1
-rwxr-xr-xweb/cgi/pub/getach.tcl87
-rw-r--r--web/cgi/pub/standard.css45
-rw-r--r--web/cgi/pub/standard.js414
l---------web/cgi/pub/tclsh1
-rw-r--r--web/cgi/session/.htaccess28
-rw-r--r--web/cgi/session/_htaccess28
l---------web/cgi/session/alpine.tcl1
-rwxr-xr-xweb/cgi/session/greeting.tcl395
-rwxr-xr-xweb/cgi/session/init.tcl218
-rwxr-xr-xweb/cgi/session/logon.tcl169
-rwxr-xr-xweb/cgi/session/logout.tcl67
l---------web/cgi/session/logout/alpine.tcl1
-rwxr-xr-xweb/cgi/session/logout/logout.tcl51
l---------web/cgi/session/logout/tclsh1
-rwxr-xr-xweb/cgi/session/monitor.tcl282
-rwxr-xr-xweb/cgi/session/queryauth.tcl120
-rwxr-xr-xweb/cgi/session/setauth.tcl68
-rwxr-xr-xweb/cgi/session/setauth2.tcl58
-rwxr-xr-xweb/cgi/session/setpassphrase.tcl52
-rwxr-xr-xweb/cgi/session/startup.tcl33
l---------web/cgi/session/tclsh1
-rw-r--r--web/cgi/sounds/ding.wavbin0 -> 11598 bytes
-rw-r--r--web/cgi/sounds/mail_msg.wavbin0 -> 23022 bytes
l---------web/cgi/tclsh1
-rw-r--r--web/config/alpine.tcl1148
-rw-r--r--web/config/conf.deskmail55
-rw-r--r--web/config/pine.conf52
l---------web/detach1
-rw-r--r--web/lib/README8
-rwxr-xr-xweb/lib/pkgcreate3
-rw-r--r--web/src/Makefile.am22
-rw-r--r--web/src/Makefile.in421
-rw-r--r--web/src/alpined.d/Makefile.am52
-rw-r--r--web/src/alpined.d/Makefile.in695
-rw-r--r--web/src/alpined.d/alpined.c16404
-rw-r--r--web/src/alpined.d/alpined.h49
-rw-r--r--web/src/alpined.d/alpineldap.c181
-rw-r--r--web/src/alpined.d/busy.c49
-rw-r--r--web/src/alpined.d/color.c678
-rw-r--r--web/src/alpined.d/color.h25
-rw-r--r--web/src/alpined.d/debug.c151
-rw-r--r--web/src/alpined.d/debug.h52
-rw-r--r--web/src/alpined.d/imap.c516
-rw-r--r--web/src/alpined.d/imap.h23
-rw-r--r--web/src/alpined.d/ldap.c241
-rw-r--r--web/src/alpined.d/ldap.h48
-rw-r--r--web/src/alpined.d/remote.c78
-rw-r--r--web/src/alpined.d/signal.c280
-rw-r--r--web/src/alpined.d/signal.h15
-rw-r--r--web/src/alpined.d/status.c78
-rw-r--r--web/src/alpined.d/stubs.c169
-rw-r--r--web/src/alpined.d/stubs.h25
-rw-r--r--web/src/alpined.d/wpcomm.c197
-rw-r--r--web/src/cgi.tcl-1.10/HISTORY644
-rw-r--r--web/src/cgi.tcl-1.10/INSTALL96
-rw-r--r--web/src/cgi.tcl-1.10/Makefile.in273
-rw-r--r--web/src/cgi.tcl-1.10/PATCH.UW230
-rw-r--r--web/src/cgi.tcl-1.10/README140
-rw-r--r--web/src/cgi.tcl-1.10/README.UW23
-rw-r--r--web/src/cgi.tcl-1.10/cgi.tcl.in2659
-rw-r--r--web/src/cgi.tcl-1.10/cgi.tcl.man36
-rwxr-xr-xweb/src/cgi.tcl-1.10/configure2291
-rw-r--r--web/src/cgi.tcl-1.10/configure.in52
-rw-r--r--web/src/cgi.tcl-1.10/doc/ref.txt1651
-rw-r--r--web/src/cgi.tcl-1.10/example/README77
-rwxr-xr-xweb/src/cgi.tcl-1.10/example/cookie.cgi45
-rwxr-xr-xweb/src/cgi.tcl-1.10/example/creditcard.cgi137
-rwxr-xr-xweb/src/cgi.tcl-1.10/example/display-in-frame.cgi31
-rwxr-xr-xweb/src/cgi.tcl-1.10/example/display.cgi44
-rwxr-xr-xweb/src/cgi.tcl-1.10/example/download.cgi36
-rwxr-xr-xweb/src/cgi.tcl-1.10/example/error.cgi31
-rwxr-xr-xweb/src/cgi.tcl-1.10/example/evaljs.cgi36
-rw-r--r--web/src/cgi.tcl-1.10/example/example.tcl82
-rwxr-xr-xweb/src/cgi.tcl-1.10/example/examples.cgi81
-rwxr-xr-xweb/src/cgi.tcl-1.10/example/form-tour-result.cgi69
-rwxr-xr-xweb/src/cgi.tcl-1.10/example/form-tour.cgi123
-rwxr-xr-xweb/src/cgi.tcl-1.10/example/format-tour.cgi101
-rwxr-xr-xweb/src/cgi.tcl-1.10/example/frame.cgi32
-rwxr-xr-xweb/src/cgi.tcl-1.10/example/image.cgi29
-rwxr-xr-xweb/src/cgi.tcl-1.10/example/img.cgi39
-rwxr-xr-xweb/src/cgi.tcl-1.10/example/kill.cgi69
-rw-r--r--web/src/cgi.tcl-1.10/example/nistguest102
-rwxr-xr-xweb/src/cgi.tcl-1.10/example/nistguest.cgi130
-rw-r--r--web/src/cgi.tcl-1.10/example/oratcl.cgi33
-rwxr-xr-xweb/src/cgi.tcl-1.10/example/parray.cgi48
-rwxr-xr-xweb/src/cgi.tcl-1.10/example/passwd-form.cgi39
-rwxr-xr-xweb/src/cgi.tcl-1.10/example/passwd.cgi119
-rw-r--r--web/src/cgi.tcl-1.10/example/passwd.tcl10
-rwxr-xr-xweb/src/cgi.tcl-1.10/example/push.cgi37
-rw-r--r--web/src/cgi.tcl-1.10/example/rm.cgi59
-rwxr-xr-xweb/src/cgi.tcl-1.10/example/stopwatch.cgi64
-rwxr-xr-xweb/src/cgi.tcl-1.10/example/unimail.cgi58
-rwxr-xr-xweb/src/cgi.tcl-1.10/example/upload.cgi59
-rw-r--r--web/src/cgi.tcl-1.10/example/utf.cgi17
-rwxr-xr-xweb/src/cgi.tcl-1.10/example/validate.cgi76
-rwxr-xr-xweb/src/cgi.tcl-1.10/example/vclock-src-frame.cgi23
-rwxr-xr-xweb/src/cgi.tcl-1.10/example/vclock.cgi73
-rw-r--r--web/src/cgi.tcl-1.10/example/vclock.pl59
-rw-r--r--web/src/cgi.tcl-1.10/example/version.cgi30
-rwxr-xr-xweb/src/cgi.tcl-1.10/example/visitor.cgi30
-rwxr-xr-xweb/src/cgi.tcl-1.10/example/visitor.cnt1
-rwxr-xr-xweb/src/cgi.tcl-1.10/example/vote.cgi190
-rw-r--r--web/src/cgi.tcl-1.10/example/vote.cnt30
-rwxr-xr-xweb/src/cgi.tcl-1.10/fixline113
-rwxr-xr-xweb/src/cgi.tcl-1.10/install-sh238
-rw-r--r--web/src/cgi.tcl-1.10/install.mac70
-rw-r--r--web/src/cgi.tcl-1.10/install.win105
-rwxr-xr-xweb/src/cgi.tcl-1.10/mkinstalldirs32
-rwxr-xr-xweb/src/cgi.tcl-1.10/pkgcreate9
-rw-r--r--web/src/cgi.tcl-1.10/version.in2
-rw-r--r--web/src/pubcookie/INSTALL90
-rw-r--r--web/src/pubcookie/Makefile.am37
-rw-r--r--web/src/pubcookie/Makefile.in621
-rw-r--r--web/src/pubcookie/README137
-rw-r--r--web/src/pubcookie/_htaccess_session4
-rw-r--r--web/src/pubcookie/_htaccess_session_logout8
-rw-r--r--web/src/pubcookie/auth_gss_proxy.c380
-rwxr-xr-xweb/src/pubcookie/debug.cgi58
-rw-r--r--web/src/pubcookie/id_table.c294
-rw-r--r--web/src/pubcookie/id_table.h58
-rw-r--r--web/src/pubcookie/wp_gssapi_proxy.c412
-rw-r--r--web/src/pubcookie/wp_tclsh.c189
-rw-r--r--web/src/pubcookie/wp_uidmapper.c312
-rw-r--r--web/src/pubcookie/wp_uidmapper_lib.c178
-rw-r--r--web/src/pubcookie/wp_uidmapper_lib.h25
-rw-r--r--web/src/pubcookie/wp_umc.c63
1893 files changed, 829982 insertions, 0 deletions
diff --git a/ABOUT-NLS b/ABOUT-NLS
new file mode 100644
index 00000000..ec20977e
--- /dev/null
+++ b/ABOUT-NLS
@@ -0,0 +1,1101 @@
+1 Notes on the Free Translation Project
+***************************************
+
+Free software is going international! The Free Translation Project is
+a way to get maintainers of free software, translators, and users all
+together, so that free software will gradually become able to speak many
+languages. A few packages already provide translations for their
+messages.
+
+ If you found this `ABOUT-NLS' file inside a distribution, you may
+assume that the distributed package does use GNU `gettext' internally,
+itself available at your nearest GNU archive site. But you do _not_
+need to install GNU `gettext' prior to configuring, installing or using
+this package with messages translated.
+
+ Installers will find here some useful hints. These notes also
+explain how users should proceed for getting the programs to use the
+available translations. They tell how people wanting to contribute and
+work on translations can contact the appropriate team.
+
+ When reporting bugs in the `intl/' directory or bugs which may be
+related to internationalization, you should tell about the version of
+`gettext' which is used. The information can be found in the
+`intl/VERSION' file, in internationalized packages.
+
+1.1 Quick configuration advice
+==============================
+
+If you want to exploit the full power of internationalization, you
+should configure it using
+
+ ./configure --with-included-gettext
+
+to force usage of internationalizing routines provided within this
+package, despite the existence of internationalizing capabilities in the
+operating system where this package is being installed. So far, only
+the `gettext' implementation in the GNU C library version 2 provides as
+many features (such as locale alias, message inheritance, automatic
+charset conversion or plural form handling) as the implementation here.
+It is also not possible to offer this additional functionality on top
+of a `catgets' implementation. Future versions of GNU `gettext' will
+very likely convey even more functionality. So it might be a good idea
+to change to GNU `gettext' as soon as possible.
+
+ So you need _not_ provide this option if you are using GNU libc 2 or
+you have installed a recent copy of the GNU gettext package with the
+included `libintl'.
+
+1.2 INSTALL Matters
+===================
+
+Some packages are "localizable" when properly installed; the programs
+they contain can be made to speak your own native language. Most such
+packages use GNU `gettext'. Other packages have their own ways to
+internationalization, predating GNU `gettext'.
+
+ By default, this package will be installed to allow translation of
+messages. It will automatically detect whether the system already
+provides the GNU `gettext' functions. If not, the included GNU
+`gettext' library will be used. This library is wholly contained
+within this package, usually in the `intl/' subdirectory, so prior
+installation of the GNU `gettext' package is _not_ required.
+Installers may use special options at configuration time for changing
+the default behaviour. The commands:
+
+ ./configure --with-included-gettext
+ ./configure --disable-nls
+
+will, respectively, bypass any pre-existing `gettext' to use the
+internationalizing routines provided within this package, or else,
+_totally_ disable translation of messages.
+
+ When you already have GNU `gettext' installed on your system and run
+configure without an option for your new package, `configure' will
+probably detect the previously built and installed `libintl.a' file and
+will decide to use this. This might not be desirable. You should use
+the more recent version of the GNU `gettext' library. I.e. if the file
+`intl/VERSION' shows that the library which comes with this package is
+more recent, you should use
+
+ ./configure --with-included-gettext
+
+to prevent auto-detection.
+
+ The configuration process will not test for the `catgets' function
+and therefore it will not be used. The reason is that even an
+emulation of `gettext' on top of `catgets' could not provide all the
+extensions of the GNU `gettext' library.
+
+ Internationalized packages usually have many `po/LL.po' files, where
+LL gives an ISO 639 two-letter code identifying the language. Unless
+translations have been forbidden at `configure' time by using the
+`--disable-nls' switch, all available translations are installed
+together with the package. However, the environment variable `LINGUAS'
+may be set, prior to configuration, to limit the installed set.
+`LINGUAS' should then contain a space separated list of two-letter
+codes, stating which languages are allowed.
+
+1.3 Using This Package
+======================
+
+As a user, if your language has been installed for this package, you
+only have to set the `LANG' environment variable to the appropriate
+`LL_CC' combination. Here `LL' is an ISO 639 two-letter language code,
+and `CC' is an ISO 3166 two-letter country code. For example, let's
+suppose that you speak German and live in Germany. At the shell
+prompt, merely execute `setenv LANG de_DE' (in `csh'),
+`export LANG; LANG=de_DE' (in `sh') or `export LANG=de_DE' (in `bash').
+This can be done from your `.login' or `.profile' file, once and for
+all.
+
+ You might think that the country code specification is redundant.
+But in fact, some languages have dialects in different countries. For
+example, `de_AT' is used for Austria, and `pt_BR' for Brazil. The
+country code serves to distinguish the dialects.
+
+ The locale naming convention of `LL_CC', with `LL' denoting the
+language and `CC' denoting the country, is the one use on systems based
+on GNU libc. On other systems, some variations of this scheme are
+used, such as `LL' or `LL_CC.ENCODING'. You can get the list of
+locales supported by your system for your language by running the
+command `locale -a | grep '^LL''.
+
+ Not all programs have translations for all languages. By default, an
+English message is shown in place of a nonexistent translation. If you
+understand other languages, you can set up a priority list of languages.
+This is done through a different environment variable, called
+`LANGUAGE'. GNU `gettext' gives preference to `LANGUAGE' over `LANG'
+for the purpose of message handling, but you still need to have `LANG'
+set to the primary language; this is required by other parts of the
+system libraries. For example, some Swedish users who would rather
+read translations in German than English for when Swedish is not
+available, set `LANGUAGE' to `sv:de' while leaving `LANG' to `sv_SE'.
+
+ Special advice for Norwegian users: The language code for Norwegian
+bokma*l changed from `no' to `nb' recently (in 2003). During the
+transition period, while some message catalogs for this language are
+installed under `nb' and some older ones under `no', it's recommended
+for Norwegian users to set `LANGUAGE' to `nb:no' so that both newer and
+older translations are used.
+
+ In the `LANGUAGE' environment variable, but not in the `LANG'
+environment variable, `LL_CC' combinations can be abbreviated as `LL'
+to denote the language's main dialect. For example, `de' is equivalent
+to `de_DE' (German as spoken in Germany), and `pt' to `pt_PT'
+(Portuguese as spoken in Portugal) in this context.
+
+1.4 Translating Teams
+=====================
+
+For the Free Translation Project to be a success, we need interested
+people who like their own language and write it well, and who are also
+able to synergize with other translators speaking the same language.
+Each translation team has its own mailing list. The up-to-date list of
+teams can be found at the Free Translation Project's homepage,
+`http://www.iro.umontreal.ca/contrib/po/HTML/', in the "National teams"
+area.
+
+ If you'd like to volunteer to _work_ at translating messages, you
+should become a member of the translating team for your own language.
+The subscribing address is _not_ the same as the list itself, it has
+`-request' appended. For example, speakers of Swedish can send a
+message to `sv-request@li.org', having this message body:
+
+ subscribe
+
+ Keep in mind that team members are expected to participate
+_actively_ in translations, or at solving translational difficulties,
+rather than merely lurking around. If your team does not exist yet and
+you want to start one, or if you are unsure about what to do or how to
+get started, please write to `translation@iro.umontreal.ca' to reach the
+coordinator for all translator teams.
+
+ The English team is special. It works at improving and uniformizing
+the terminology in use. Proven linguistic skills are praised more than
+programming skills, here.
+
+1.5 Available Packages
+======================
+
+Languages are not equally supported in all packages. The following
+matrix shows the current state of internationalization, as of October
+2006. The matrix shows, in regard of each package, for which languages
+PO files have been submitted to translation coordination, with a
+translation percentage of at least 50%.
+
+ Ready PO files af am ar az be bg bs ca cs cy da de el en en_GB eo
+ +----------------------------------------------------+
+ GNUnet | [] |
+ a2ps | [] [] [] [] [] |
+ aegis | () |
+ ant-phone | () |
+ anubis | [] |
+ ap-utils | |
+ aspell | [] [] [] [] [] |
+ bash | [] [] [] |
+ batchelor | [] |
+ bfd | |
+ bibshelf | [] |
+ binutils | [] |
+ bison | [] [] |
+ bison-runtime | |
+ bluez-pin | [] [] [] [] [] |
+ cflow | [] |
+ clisp | [] [] |
+ console-tools | [] [] |
+ coreutils | [] [] [] |
+ cpio | |
+ cpplib | [] [] [] |
+ cryptonit | [] |
+ darkstat | [] () [] |
+ dialog | [] [] [] [] [] [] |
+ diffutils | [] [] [] [] [] [] |
+ doodle | [] |
+ e2fsprogs | [] [] |
+ enscript | [] [] [] [] |
+ error | [] [] [] [] |
+ fetchmail | [] [] () [] |
+ fileutils | [] [] |
+ findutils | [] [] [] |
+ flex | [] [] [] |
+ fslint | [] |
+ gas | |
+ gawk | [] [] [] |
+ gbiff | [] |
+ gcal | [] |
+ gcc | [] |
+ gettext-examples | [] [] [] [] [] |
+ gettext-runtime | [] [] [] [] [] |
+ gettext-tools | [] [] |
+ gimp-print | [] [] [] [] |
+ gip | [] |
+ gliv | [] |
+ glunarclock | [] |
+ gmult | [] [] |
+ gnubiff | () |
+ gnucash | () () [] |
+ gnucash-glossary | [] () |
+ gnuedu | |
+ gnulib | [] [] [] [] [] [] |
+ gnunet-gtk | |
+ gnutls | |
+ gpe-aerial | [] [] |
+ gpe-beam | [] [] |
+ gpe-calendar | |
+ gpe-clock | [] [] |
+ gpe-conf | [] [] |
+ gpe-contacts | |
+ gpe-edit | [] |
+ gpe-filemanager | |
+ gpe-go | [] |
+ gpe-login | [] [] |
+ gpe-ownerinfo | [] [] |
+ gpe-package | |
+ gpe-sketchbook | [] [] |
+ gpe-su | [] [] |
+ gpe-taskmanager | [] [] |
+ gpe-timesheet | [] |
+ gpe-today | [] [] |
+ gpe-todo | |
+ gphoto2 | [] [] [] [] |
+ gprof | [] [] |
+ gpsdrive | () () |
+ gramadoir | [] [] |
+ grep | [] [] [] [] [] [] |
+ gretl | |
+ gsasl | |
+ gss | |
+ gst-plugins | [] [] [] [] |
+ gst-plugins-base | [] [] [] |
+ gst-plugins-good | [] [] [] [] [] [] [] |
+ gstreamer | [] [] [] [] [] [] [] |
+ gtick | () |
+ gtkam | [] [] [] |
+ gtkorphan | [] [] |
+ gtkspell | [] [] [] [] |
+ gutenprint | [] |
+ hello | [] [] [] [] [] |
+ id-utils | [] [] |
+ impost | |
+ indent | [] [] [] |
+ iso_3166 | [] [] |
+ iso_3166_2 | |
+ iso_4217 | [] |
+ iso_639 | [] [] |
+ jpilot | [] |
+ jtag | |
+ jwhois | |
+ kbd | [] [] [] [] |
+ keytouch | |
+ keytouch-editor | |
+ keytouch-keyboa... | |
+ latrine | () |
+ ld | [] |
+ leafpad | [] [] [] [] [] |
+ libc | [] [] [] [] [] |
+ libexif | [] |
+ libextractor | [] |
+ libgpewidget | [] [] [] |
+ libgpg-error | [] |
+ libgphoto2 | [] [] |
+ libgphoto2_port | [] [] |
+ libgsasl | |
+ libiconv | [] [] |
+ libidn | [] [] |
+ lifelines | [] () |
+ lilypond | [] |
+ lingoteach | |
+ lynx | [] [] [] [] |
+ m4 | [] [] [] [] |
+ mailutils | [] |
+ make | [] [] |
+ man-db | [] () [] [] |
+ minicom | [] [] [] |
+ mysecretdiary | [] [] |
+ nano | [] [] [] |
+ nano_1_0 | [] () [] [] |
+ opcodes | [] |
+ parted | |
+ pilot-qof | [] |
+ psmisc | [] |
+ pwdutils | |
+ python | |
+ qof | |
+ radius | [] |
+ recode | [] [] [] [] [] [] |
+ rpm | [] [] |
+ screem | |
+ scrollkeeper | [] [] [] [] [] [] [] [] |
+ sed | [] [] [] |
+ sh-utils | [] [] |
+ shared-mime-info | [] [] [] [] |
+ sharutils | [] [] [] [] [] [] |
+ shishi | |
+ silky | |
+ skencil | [] () |
+ sketch | [] () |
+ solfege | |
+ soundtracker | [] [] |
+ sp | [] |
+ stardict | [] |
+ system-tools-ba... | [] [] [] [] [] [] [] [] [] |
+ tar | [] |
+ texinfo | [] [] [] |
+ textutils | [] [] [] |
+ tin | () () |
+ tp-robot | [] |
+ tuxpaint | [] [] [] [] [] |
+ unicode-han-tra... | |
+ unicode-transla... | |
+ util-linux | [] [] [] [] |
+ vorbis-tools | [] [] [] [] |
+ wastesedge | () |
+ wdiff | [] [] [] [] |
+ wget | [] [] |
+ xchat | [] [] [] [] [] [] |
+ xkeyboard-config | |
+ xpad | [] [] |
+ +----------------------------------------------------+
+ af am ar az be bg bs ca cs cy da de el en en_GB eo
+ 10 0 1 2 9 22 1 42 41 2 60 95 16 1 17 16
+
+ es et eu fa fi fr ga gl gu he hi hr hu id is it
+ +--------------------------------------------------+
+ GNUnet | |
+ a2ps | [] [] [] () |
+ aegis | |
+ ant-phone | [] |
+ anubis | [] |
+ ap-utils | [] [] |
+ aspell | [] [] [] |
+ bash | [] [] [] |
+ batchelor | [] [] |
+ bfd | [] |
+ bibshelf | [] [] [] |
+ binutils | [] [] [] |
+ bison | [] [] [] [] [] [] |
+ bison-runtime | [] [] [] [] [] |
+ bluez-pin | [] [] [] [] [] |
+ cflow | [] |
+ clisp | [] [] |
+ console-tools | |
+ coreutils | [] [] [] [] [] [] |
+ cpio | [] [] [] |
+ cpplib | [] [] |
+ cryptonit | [] |
+ darkstat | [] () [] [] [] |
+ dialog | [] [] [] [] [] [] [] [] |
+ diffutils | [] [] [] [] [] [] [] [] [] |
+ doodle | [] [] |
+ e2fsprogs | [] [] [] |
+ enscript | [] [] [] |
+ error | [] [] [] [] [] |
+ fetchmail | [] |
+ fileutils | [] [] [] [] [] [] |
+ findutils | [] [] [] [] |
+ flex | [] [] [] |
+ fslint | [] |
+ gas | [] [] |
+ gawk | [] [] [] [] |
+ gbiff | [] |
+ gcal | [] [] |
+ gcc | [] |
+ gettext-examples | [] [] [] [] [] [] |
+ gettext-runtime | [] [] [] [] [] [] |
+ gettext-tools | [] [] [] |
+ gimp-print | [] [] |
+ gip | [] [] [] |
+ gliv | () |
+ glunarclock | [] [] [] |
+ gmult | [] [] [] |
+ gnubiff | () () |
+ gnucash | () () () |
+ gnucash-glossary | [] [] |
+ gnuedu | [] |
+ gnulib | [] [] [] [] [] [] [] [] |
+ gnunet-gtk | |
+ gnutls | |
+ gpe-aerial | [] [] |
+ gpe-beam | [] [] |
+ gpe-calendar | |
+ gpe-clock | [] [] [] [] |
+ gpe-conf | [] |
+ gpe-contacts | [] [] |
+ gpe-edit | [] [] [] [] |
+ gpe-filemanager | [] |
+ gpe-go | [] [] [] |
+ gpe-login | [] [] [] |
+ gpe-ownerinfo | [] [] [] [] [] |
+ gpe-package | [] |
+ gpe-sketchbook | [] [] |
+ gpe-su | [] [] [] [] |
+ gpe-taskmanager | [] [] [] |
+ gpe-timesheet | [] [] [] [] |
+ gpe-today | [] [] [] [] |
+ gpe-todo | [] |
+ gphoto2 | [] [] [] [] [] |
+ gprof | [] [] [] [] |
+ gpsdrive | () () [] () |
+ gramadoir | [] [] |
+ grep | [] [] [] [] [] [] [] [] [] [] [] [] |
+ gretl | [] [] [] |
+ gsasl | [] [] |
+ gss | [] |
+ gst-plugins | [] [] [] |
+ gst-plugins-base | [] [] |
+ gst-plugins-good | [] [] [] |
+ gstreamer | [] [] [] |
+ gtick | [] |
+ gtkam | [] [] [] [] |
+ gtkorphan | [] [] |
+ gtkspell | [] [] [] [] [] [] |
+ gutenprint | [] |
+ hello | [] [] [] [] [] [] [] [] [] [] [] [] [] |
+ id-utils | [] [] [] [] [] |
+ impost | [] [] |
+ indent | [] [] [] [] [] [] [] [] [] [] |
+ iso_3166 | [] [] [] |
+ iso_3166_2 | [] |
+ iso_4217 | [] [] [] [] |
+ iso_639 | [] [] [] [] [] |
+ jpilot | [] [] |
+ jtag | [] |
+ jwhois | [] [] [] [] [] |
+ kbd | [] [] |
+ keytouch | [] |
+ keytouch-editor | [] |
+ keytouch-keyboa... | [] |
+ latrine | [] [] [] |
+ ld | [] [] |
+ leafpad | [] [] [] [] [] [] |
+ libc | [] [] [] [] [] |
+ libexif | [] |
+ libextractor | [] |
+ libgpewidget | [] [] [] [] [] |
+ libgpg-error | |
+ libgphoto2 | [] [] [] |
+ libgphoto2_port | [] [] |
+ libgsasl | [] [] |
+ libiconv | [] [] |
+ libidn | [] [] |
+ lifelines | () |
+ lilypond | [] |
+ lingoteach | [] [] [] |
+ lynx | [] [] [] |
+ m4 | [] [] [] [] |
+ mailutils | [] [] |
+ make | [] [] [] [] [] [] [] [] |
+ man-db | () |
+ minicom | [] [] [] [] |
+ mysecretdiary | [] [] [] |
+ nano | [] [] [] [] [] [] |
+ nano_1_0 | [] [] [] [] [] |
+ opcodes | [] [] [] [] |
+ parted | [] [] [] [] |
+ pilot-qof | |
+ psmisc | [] [] [] |
+ pwdutils | |
+ python | |
+ qof | [] |
+ radius | [] [] |
+ recode | [] [] [] [] [] [] [] [] |
+ rpm | [] [] |
+ screem | |
+ scrollkeeper | [] [] [] |
+ sed | [] [] [] [] [] |
+ sh-utils | [] [] [] [] [] [] [] |
+ shared-mime-info | [] [] [] [] [] [] |
+ sharutils | [] [] [] [] [] [] [] [] |
+ shishi | |
+ silky | [] |
+ skencil | [] [] |
+ sketch | [] [] |
+ solfege | [] |
+ soundtracker | [] [] [] |
+ sp | [] |
+ stardict | [] |
+ system-tools-ba... | [] [] [] [] [] [] [] [] |
+ tar | [] [] [] [] [] [] [] |
+ texinfo | [] [] |
+ textutils | [] [] [] [] [] |
+ tin | [] () |
+ tp-robot | [] [] [] [] |
+ tuxpaint | [] [] |
+ unicode-han-tra... | |
+ unicode-transla... | [] [] |
+ util-linux | [] [] [] [] [] [] [] |
+ vorbis-tools | [] [] |
+ wastesedge | () |
+ wdiff | [] [] [] [] [] [] [] [] |
+ wget | [] [] [] [] [] [] [] [] |
+ xchat | [] [] [] [] [] [] [] [] |
+ xkeyboard-config | [] [] [] [] |
+ xpad | [] [] [] |
+ +--------------------------------------------------+
+ es et eu fa fi fr ga gl gu he hi hr hu id is it
+ 88 22 14 2 40 115 61 14 1 8 1 6 59 31 0 52
+
+ ja ko ku ky lg lt lv mk mn ms mt nb ne nl nn no
+ +-------------------------------------------------+
+ GNUnet | |
+ a2ps | () [] [] () |
+ aegis | () |
+ ant-phone | [] |
+ anubis | [] [] [] |
+ ap-utils | [] |
+ aspell | [] [] |
+ bash | [] |
+ batchelor | [] [] |
+ bfd | |
+ bibshelf | [] |
+ binutils | |
+ bison | [] [] [] |
+ bison-runtime | [] [] [] |
+ bluez-pin | [] [] [] |
+ cflow | |
+ clisp | [] |
+ console-tools | |
+ coreutils | [] |
+ cpio | |
+ cpplib | [] |
+ cryptonit | [] |
+ darkstat | [] [] |
+ dialog | [] [] |
+ diffutils | [] [] [] |
+ doodle | |
+ e2fsprogs | [] |
+ enscript | [] |
+ error | [] |
+ fetchmail | [] [] |
+ fileutils | [] [] |
+ findutils | [] |
+ flex | [] [] |
+ fslint | [] [] |
+ gas | |
+ gawk | [] [] |
+ gbiff | [] |
+ gcal | |
+ gcc | |
+ gettext-examples | [] [] |
+ gettext-runtime | [] [] [] |
+ gettext-tools | [] [] |
+ gimp-print | [] [] |
+ gip | [] [] |
+ gliv | [] |
+ glunarclock | [] [] |
+ gmult | [] [] |
+ gnubiff | |
+ gnucash | () () |
+ gnucash-glossary | [] |
+ gnuedu | |
+ gnulib | [] [] [] [] |
+ gnunet-gtk | |
+ gnutls | |
+ gpe-aerial | [] |
+ gpe-beam | [] |
+ gpe-calendar | [] |
+ gpe-clock | [] [] [] |
+ gpe-conf | [] [] |
+ gpe-contacts | [] |
+ gpe-edit | [] [] [] |
+ gpe-filemanager | [] [] |
+ gpe-go | [] [] [] |
+ gpe-login | [] [] [] |
+ gpe-ownerinfo | [] [] |
+ gpe-package | [] [] |
+ gpe-sketchbook | [] [] |
+ gpe-su | [] [] [] |
+ gpe-taskmanager | [] [] [] [] |
+ gpe-timesheet | [] |
+ gpe-today | [] [] |
+ gpe-todo | [] |
+ gphoto2 | [] [] |
+ gprof | |
+ gpsdrive | () () () |
+ gramadoir | () |
+ grep | [] [] [] [] |
+ gretl | |
+ gsasl | [] |
+ gss | |
+ gst-plugins | [] |
+ gst-plugins-base | |
+ gst-plugins-good | [] |
+ gstreamer | [] |
+ gtick | |
+ gtkam | [] |
+ gtkorphan | [] |
+ gtkspell | [] [] |
+ gutenprint | |
+ hello | [] [] [] [] [] [] |
+ id-utils | [] |
+ impost | |
+ indent | [] [] |
+ iso_3166 | [] |
+ iso_3166_2 | [] |
+ iso_4217 | [] [] [] |
+ iso_639 | [] [] |
+ jpilot | () () () |
+ jtag | |
+ jwhois | [] |
+ kbd | [] |
+ keytouch | [] |
+ keytouch-editor | |
+ keytouch-keyboa... | |
+ latrine | [] |
+ ld | |
+ leafpad | [] [] |
+ libc | [] [] [] [] [] |
+ libexif | |
+ libextractor | |
+ libgpewidget | [] |
+ libgpg-error | |
+ libgphoto2 | [] |
+ libgphoto2_port | [] |
+ libgsasl | [] |
+ libiconv | |
+ libidn | [] [] |
+ lifelines | [] |
+ lilypond | |
+ lingoteach | [] |
+ lynx | [] [] |
+ m4 | [] [] |
+ mailutils | |
+ make | [] [] [] |
+ man-db | () |
+ minicom | [] |
+ mysecretdiary | [] |
+ nano | [] [] [] |
+ nano_1_0 | [] [] [] |
+ opcodes | [] |
+ parted | [] [] |
+ pilot-qof | |
+ psmisc | [] [] [] |
+ pwdutils | |
+ python | |
+ qof | |
+ radius | |
+ recode | [] |
+ rpm | [] [] |
+ screem | [] |
+ scrollkeeper | [] [] [] [] |
+ sed | [] [] |
+ sh-utils | [] [] |
+ shared-mime-info | [] [] [] [] [] |
+ sharutils | [] [] |
+ shishi | |
+ silky | [] |
+ skencil | |
+ sketch | |
+ solfege | |
+ soundtracker | |
+ sp | () |
+ stardict | [] [] |
+ system-tools-ba... | [] [] [] [] |
+ tar | [] [] [] |
+ texinfo | [] [] [] |
+ textutils | [] [] [] |
+ tin | |
+ tp-robot | [] |
+ tuxpaint | [] |
+ unicode-han-tra... | |
+ unicode-transla... | |
+ util-linux | [] [] |
+ vorbis-tools | [] |
+ wastesedge | [] |
+ wdiff | [] [] |
+ wget | [] [] |
+ xchat | [] [] [] [] |
+ xkeyboard-config | [] |
+ xpad | [] [] [] |
+ +-------------------------------------------------+
+ ja ko ku ky lg lt lv mk mn ms mt nb ne nl nn no
+ 52 24 2 2 1 3 0 2 3 21 0 15 1 97 5 1
+
+ nso or pa pl pt pt_BR rm ro ru rw sk sl sq sr sv ta
+ +------------------------------------------------------+
+ GNUnet | |
+ a2ps | () [] [] [] [] [] [] |
+ aegis | () () |
+ ant-phone | [] [] |
+ anubis | [] [] [] |
+ ap-utils | () |
+ aspell | [] [] |
+ bash | [] [] [] |
+ batchelor | [] [] |
+ bfd | |
+ bibshelf | [] |
+ binutils | [] [] |
+ bison | [] [] [] [] [] |
+ bison-runtime | [] [] [] [] |
+ bluez-pin | [] [] [] [] [] [] [] [] [] |
+ cflow | [] |
+ clisp | [] |
+ console-tools | [] |
+ coreutils | [] [] [] [] |
+ cpio | [] [] [] |
+ cpplib | [] |
+ cryptonit | [] [] |
+ darkstat | [] [] [] [] [] [] |
+ dialog | [] [] [] [] [] [] [] [] [] |
+ diffutils | [] [] [] [] [] [] |
+ doodle | [] [] |
+ e2fsprogs | [] [] |
+ enscript | [] [] [] [] [] |
+ error | [] [] [] [] |
+ fetchmail | [] [] [] |
+ fileutils | [] [] [] [] [] |
+ findutils | [] [] [] [] [] [] |
+ flex | [] [] [] [] [] |
+ fslint | [] [] [] [] |
+ gas | |
+ gawk | [] [] [] [] |
+ gbiff | [] |
+ gcal | [] |
+ gcc | [] |
+ gettext-examples | [] [] [] [] [] [] [] [] |
+ gettext-runtime | [] [] [] [] [] [] [] [] |
+ gettext-tools | [] [] [] [] [] [] [] |
+ gimp-print | [] [] |
+ gip | [] [] [] [] |
+ gliv | [] [] [] [] |
+ glunarclock | [] [] [] [] [] [] |
+ gmult | [] [] [] [] |
+ gnubiff | () |
+ gnucash | () [] |
+ gnucash-glossary | [] [] [] |
+ gnuedu | |
+ gnulib | [] [] [] [] [] |
+ gnunet-gtk | [] |
+ gnutls | [] [] |
+ gpe-aerial | [] [] [] [] [] [] [] |
+ gpe-beam | [] [] [] [] [] [] [] |
+ gpe-calendar | [] |
+ gpe-clock | [] [] [] [] [] [] [] [] |
+ gpe-conf | [] [] [] [] [] [] [] |
+ gpe-contacts | [] [] [] [] [] |
+ gpe-edit | [] [] [] [] [] [] [] [] |
+ gpe-filemanager | [] [] |
+ gpe-go | [] [] [] [] [] [] |
+ gpe-login | [] [] [] [] [] [] [] [] |
+ gpe-ownerinfo | [] [] [] [] [] [] [] [] |
+ gpe-package | [] [] |
+ gpe-sketchbook | [] [] [] [] [] [] [] [] |
+ gpe-su | [] [] [] [] [] [] [] [] |
+ gpe-taskmanager | [] [] [] [] [] [] [] [] |
+ gpe-timesheet | [] [] [] [] [] [] [] [] |
+ gpe-today | [] [] [] [] [] [] [] [] |
+ gpe-todo | [] [] [] [] |
+ gphoto2 | [] [] [] [] [] |
+ gprof | [] [] [] |
+ gpsdrive | [] [] [] |
+ gramadoir | [] [] |
+ grep | [] [] [] [] [] [] [] [] |
+ gretl | [] |
+ gsasl | [] [] [] |
+ gss | [] [] [] |
+ gst-plugins | [] [] [] [] |
+ gst-plugins-base | [] |
+ gst-plugins-good | [] [] [] [] |
+ gstreamer | [] [] [] |
+ gtick | [] |
+ gtkam | [] [] [] [] |
+ gtkorphan | [] |
+ gtkspell | [] [] [] [] [] [] [] [] |
+ gutenprint | [] |
+ hello | [] [] [] [] [] [] [] [] |
+ id-utils | [] [] [] [] |
+ impost | [] |
+ indent | [] [] [] [] [] [] |
+ iso_3166 | [] [] [] [] [] [] |
+ iso_3166_2 | |
+ iso_4217 | [] [] [] [] |
+ iso_639 | [] [] [] [] |
+ jpilot | |
+ jtag | [] |
+ jwhois | [] [] [] [] |
+ kbd | [] [] [] |
+ keytouch | [] |
+ keytouch-editor | [] |
+ keytouch-keyboa... | [] |
+ latrine | [] [] |
+ ld | [] |
+ leafpad | [] [] [] [] [] [] |
+ libc | [] [] [] [] [] |
+ libexif | [] |
+ libextractor | [] [] |
+ libgpewidget | [] [] [] [] [] [] [] |
+ libgpg-error | [] [] |
+ libgphoto2 | [] |
+ libgphoto2_port | [] [] [] |
+ libgsasl | [] [] [] [] |
+ libiconv | [] [] |
+ libidn | [] [] () |
+ lifelines | [] [] |
+ lilypond | |
+ lingoteach | [] |
+ lynx | [] [] [] |
+ m4 | [] [] [] [] [] |
+ mailutils | [] [] [] [] |
+ make | [] [] [] [] |
+ man-db | [] [] |
+ minicom | [] [] [] [] [] |
+ mysecretdiary | [] [] [] [] |
+ nano | [] [] [] |
+ nano_1_0 | [] [] [] [] |
+ opcodes | [] [] |
+ parted | [] |
+ pilot-qof | [] |
+ psmisc | [] [] |
+ pwdutils | [] [] |
+ python | |
+ qof | [] [] |
+ radius | [] [] |
+ recode | [] [] [] [] [] [] [] |
+ rpm | [] [] [] [] |
+ screem | |
+ scrollkeeper | [] [] [] [] [] [] [] |
+ sed | [] [] [] [] [] [] [] [] [] |
+ sh-utils | [] [] [] |
+ shared-mime-info | [] [] [] [] [] |
+ sharutils | [] [] [] [] |
+ shishi | [] |
+ silky | [] |
+ skencil | [] [] [] |
+ sketch | [] [] [] |
+ solfege | [] |
+ soundtracker | [] [] |
+ sp | |
+ stardict | [] [] [] |
+ system-tools-ba... | [] [] [] [] [] [] [] [] [] |
+ tar | [] [] [] [] [] |
+ texinfo | [] [] [] [] |
+ textutils | [] [] [] |
+ tin | () |
+ tp-robot | [] |
+ tuxpaint | [] [] [] [] [] |
+ unicode-han-tra... | |
+ unicode-transla... | |
+ util-linux | [] [] [] [] |
+ vorbis-tools | [] [] |
+ wastesedge | |
+ wdiff | [] [] [] [] [] [] |
+ wget | [] [] [] [] |
+ xchat | [] [] [] [] [] [] [] |
+ xkeyboard-config | [] [] |
+ xpad | [] [] [] |
+ +------------------------------------------------------+
+ nso or pa pl pt pt_BR rm ro ru rw sk sl sq sr sv ta
+ 0 2 3 58 30 54 5 73 72 4 40 46 11 50 128 2
+
+ tg th tk tr uk ven vi wa xh zh_CN zh_HK zh_TW zu
+ +---------------------------------------------------+
+ GNUnet | [] | 2
+ a2ps | [] [] [] | 19
+ aegis | | 0
+ ant-phone | [] [] | 6
+ anubis | [] [] [] | 11
+ ap-utils | () [] | 4
+ aspell | [] [] [] | 15
+ bash | [] | 11
+ batchelor | [] [] | 9
+ bfd | | 1
+ bibshelf | [] | 7
+ binutils | [] [] [] | 9
+ bison | [] [] [] | 19
+ bison-runtime | [] [] [] | 15
+ bluez-pin | [] [] [] [] [] [] | 28
+ cflow | [] [] | 5
+ clisp | | 6
+ console-tools | [] [] | 5
+ coreutils | [] [] | 16
+ cpio | [] [] [] | 9
+ cpplib | [] [] [] [] | 11
+ cryptonit | | 5
+ darkstat | [] () () | 15
+ dialog | [] [] [] [] [] | 30
+ diffutils | [] [] [] [] | 28
+ doodle | [] | 6
+ e2fsprogs | [] [] | 10
+ enscript | [] [] [] | 16
+ error | [] [] [] [] | 18
+ fetchmail | [] [] | 12
+ fileutils | [] [] [] | 18
+ findutils | [] [] [] | 17
+ flex | [] [] | 15
+ fslint | [] | 9
+ gas | [] | 3
+ gawk | [] [] | 15
+ gbiff | [] | 5
+ gcal | [] | 5
+ gcc | [] [] [] | 6
+ gettext-examples | [] [] [] [] [] [] | 27
+ gettext-runtime | [] [] [] [] [] [] | 28
+ gettext-tools | [] [] [] [] [] | 19
+ gimp-print | [] [] | 12
+ gip | [] [] | 12
+ gliv | [] [] | 8
+ glunarclock | [] [] [] | 15
+ gmult | [] [] [] [] | 15
+ gnubiff | [] | 1
+ gnucash | () | 2
+ gnucash-glossary | [] [] | 9
+ gnuedu | [] | 2
+ gnulib | [] [] [] [] [] | 28
+ gnunet-gtk | | 1
+ gnutls | | 2
+ gpe-aerial | [] [] | 14
+ gpe-beam | [] [] | 14
+ gpe-calendar | [] | 3
+ gpe-clock | [] [] [] [] | 21
+ gpe-conf | [] [] | 14
+ gpe-contacts | [] [] | 10
+ gpe-edit | [] [] [] [] | 20
+ gpe-filemanager | [] | 6
+ gpe-go | [] [] | 15
+ gpe-login | [] [] [] [] [] | 21
+ gpe-ownerinfo | [] [] [] [] | 21
+ gpe-package | [] | 6
+ gpe-sketchbook | [] [] | 16
+ gpe-su | [] [] [] | 20
+ gpe-taskmanager | [] [] [] | 20
+ gpe-timesheet | [] [] [] [] | 18
+ gpe-today | [] [] [] [] [] | 21
+ gpe-todo | [] | 7
+ gphoto2 | [] [] [] [] | 20
+ gprof | [] [] | 11
+ gpsdrive | | 4
+ gramadoir | [] | 7
+ grep | [] [] [] [] | 34
+ gretl | | 4
+ gsasl | [] [] | 8
+ gss | [] | 5
+ gst-plugins | [] [] [] | 15
+ gst-plugins-base | [] [] [] | 9
+ gst-plugins-good | [] [] [] [] [] | 20
+ gstreamer | [] [] [] | 17
+ gtick | [] | 3
+ gtkam | [] | 13
+ gtkorphan | [] | 7
+ gtkspell | [] [] [] [] [] [] | 26
+ gutenprint | | 3
+ hello | [] [] [] [] [] | 37
+ id-utils | [] [] | 14
+ impost | [] | 4
+ indent | [] [] [] [] | 25
+ iso_3166 | [] [] [] [] | 16
+ iso_3166_2 | | 2
+ iso_4217 | [] [] | 14
+ iso_639 | [] | 14
+ jpilot | [] [] [] [] | 7
+ jtag | [] | 3
+ jwhois | [] [] [] | 13
+ kbd | [] [] | 12
+ keytouch | [] | 4
+ keytouch-editor | | 2
+ keytouch-keyboa... | [] | 3
+ latrine | [] [] | 8
+ ld | [] [] [] [] | 8
+ leafpad | [] [] [] [] | 23
+ libc | [] [] [] | 23
+ libexif | [] | 4
+ libextractor | [] | 5
+ libgpewidget | [] [] [] | 19
+ libgpg-error | [] | 4
+ libgphoto2 | [] | 8
+ libgphoto2_port | [] [] [] | 11
+ libgsasl | [] | 8
+ libiconv | [] | 7
+ libidn | [] [] | 10
+ lifelines | | 4
+ lilypond | | 2
+ lingoteach | [] | 6
+ lynx | [] [] [] | 15
+ m4 | [] [] [] | 18
+ mailutils | [] | 8
+ make | [] [] [] | 20
+ man-db | [] | 6
+ minicom | [] | 14
+ mysecretdiary | [] [] | 12
+ nano | [] [] | 17
+ nano_1_0 | [] [] [] | 18
+ opcodes | [] [] | 10
+ parted | [] [] [] | 10
+ pilot-qof | [] | 3
+ psmisc | [] | 10
+ pwdutils | [] | 3
+ python | | 0
+ qof | [] | 4
+ radius | [] | 6
+ recode | [] [] [] | 25
+ rpm | [] [] [] [] | 14
+ screem | [] | 2
+ scrollkeeper | [] [] [] [] | 26
+ sed | [] [] [] | 22
+ sh-utils | [] | 15
+ shared-mime-info | [] [] [] [] | 24
+ sharutils | [] [] [] | 23
+ shishi | | 1
+ silky | [] | 4
+ skencil | [] | 7
+ sketch | | 6
+ solfege | | 2
+ soundtracker | [] [] | 9
+ sp | [] | 3
+ stardict | [] [] [] [] | 11
+ system-tools-ba... | [] [] [] [] [] [] [] | 37
+ tar | [] [] [] [] | 20
+ texinfo | [] [] [] | 15
+ textutils | [] [] [] | 17
+ tin | | 1
+ tp-robot | [] [] [] | 10
+ tuxpaint | [] [] [] | 16
+ unicode-han-tra... | | 0
+ unicode-transla... | | 2
+ util-linux | [] [] [] | 20
+ vorbis-tools | [] [] | 11
+ wastesedge | | 1
+ wdiff | [] [] | 22
+ wget | [] [] [] | 19
+ xchat | [] [] [] [] | 29
+ xkeyboard-config | [] [] [] [] | 11
+ xpad | [] [] [] | 14
+ +---------------------------------------------------+
+ 77 teams tg th tk tr uk ven vi wa xh zh_CN zh_HK zh_TW zu
+ 170 domains 0 1 1 77 39 0 136 10 1 48 5 54 0 2028
+
+ Some counters in the preceding matrix are higher than the number of
+visible blocks let us expect. This is because a few extra PO files are
+used for implementing regional variants of languages, or language
+dialects.
+
+ For a PO file in the matrix above to be effective, the package to
+which it applies should also have been internationalized and
+distributed as such by its maintainer. There might be an observable
+lag between the mere existence a PO file and its wide availability in a
+distribution.
+
+ If October 2006 seems to be old, you may fetch a more recent copy of
+this `ABOUT-NLS' file on most GNU archive sites. The most up-to-date
+matrix with full percentage details can be found at
+`http://www.iro.umontreal.ca/contrib/po/HTML/matrix.html'.
+
+1.6 Using `gettext' in new packages
+===================================
+
+If you are writing a freely available program and want to
+internationalize it you are welcome to use GNU `gettext' in your
+package. Of course you have to respect the GNU Library General Public
+License which covers the use of the GNU `gettext' library. This means
+in particular that even non-free programs can use `libintl' as a shared
+library, whereas only free software can use `libintl' as a static
+library or use modified versions of `libintl'.
+
+ Once the sources are changed appropriately and the setup can handle
+the use of `gettext' the only thing missing are the translations. The
+Free Translation Project is also available for packages which are not
+developed inside the GNU project. Therefore the information given above
+applies also for every other Free Software Project. Contact
+`translation@iro.umontreal.ca' to make the `.pot' files available to
+the translation teams.
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..349bd53f
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 00000000..6ae813a9
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,40 @@
+## Process this file with automake to produce Makefile.in
+## Use aclocal -I m4; automake
+
+# ========================================================================
+# Copyright 2006-2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+EXTRA_DIST = config.rpath VERSION README LICENSE
+
+SUBDIRS = m4 po $(REGEX_BUILD) pith pico alpine $(WEB_PUBCOOKIE_BUILD) $(WEB_BUILD)
+
+BUILT_SOURCES = c-client.d c-client $(WEB_PUBCOOKIE_LINK)
+
+CLEANFILES = c-client
+
+.PHONY: c-client.d c-client gssapi_proxy.l
+
+c-client.d:
+ if test ! -L c-client ; then $(LN_S) imap/c-client c-client ; fi
+
+c-client:
+ $(C_CLIENT_WITH_IPV6)
+ cd imap && $(MAKE) $(C_CLIENT_TARGET) $(C_CLIENT_CFLAGS) $(C_CLIENT_LDFLAGS) $(C_CLIENT_GCCOPTLEVEL) EXTRASPECIALS="$(C_CLIENT_SPECIALS)"
+
+gssapi_proxy.l:
+ if test ! -L c-client/auth_gss_proxy.c ; then $(LN_S) @abs_top_srcdir@/web/src/pubcookie/auth_gss_proxy.c c-client/auth_gss_proxy.c ; fi
+
+clean-local:
+ cd imap && $(MAKE) clean
+
+man_MANS = doc/alpine.1 doc/pico.1 doc/pilot.1 doc/rpdump.1 doc/rpload.1
+
+ACLOCAL_AMFLAGS = -I m4
diff --git a/Makefile.in b/Makefile.in
new file mode 100644
index 00000000..a8d6756e
--- /dev/null
+++ b/Makefile.in
@@ -0,0 +1,885 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# ========================================================================
+# Copyright 2006-2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = .
+DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in $(top_srcdir)/configure \
+ $(top_srcdir)/include/config.h.in ABOUT-NLS config.guess \
+ config.rpath config.sub depcomp install-sh ltmain.sh missing \
+ mkinstalldirs
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/acx_pthread.m4 \
+ $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
+ $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/VERSION \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
+ configure.lineno config.status.lineno
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/include/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+SOURCES =
+DIST_SOURCES =
+RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
+ html-recursive info-recursive install-data-recursive \
+ install-dvi-recursive install-exec-recursive \
+ install-html-recursive install-info-recursive \
+ install-pdf-recursive install-ps-recursive install-recursive \
+ installcheck-recursive installdirs-recursive pdf-recursive \
+ ps-recursive uninstall-recursive
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+man1dir = $(mandir)/man1
+am__installdirs = "$(DESTDIR)$(man1dir)"
+NROFF = nroff
+MANS = $(man_MANS)
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \
+ $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \
+ distdir dist dist-all distcheck
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+distdir = $(PACKAGE)-$(VERSION)
+top_distdir = $(distdir)
+am__remove_distdir = \
+ { test ! -d "$(distdir)" \
+ || { find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \
+ && rm -fr "$(distdir)"; }; }
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+DIST_ARCHIVES = $(distdir).tar.gz
+GZIP_ENV = --best
+distuninstallcheck_listfiles = find . -type f -print
+distcleancheck_listfiles = find . -type f -print
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_CFLAGS = @AM_CFLAGS@
+AM_LDFLAGS = @AM_LDFLAGS@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CP = @CP@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+C_CLIENT_CFLAGS = @C_CLIENT_CFLAGS@
+C_CLIENT_GCCOPTLEVEL = @C_CLIENT_GCCOPTLEVEL@
+C_CLIENT_LDFLAGS = @C_CLIENT_LDFLAGS@
+C_CLIENT_SPECIALS = @C_CLIENT_SPECIALS@
+C_CLIENT_TARGET = @C_CLIENT_TARGET@
+C_CLIENT_WITH_IPV6 = @C_CLIENT_WITH_IPV6@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISPELLPROG = @ISPELLPROG@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN = @LN@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKE = @MAKE@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NPA_PROG = @NPA_PROG@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+POSUB = @POSUB@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+PWPROG = @PWPROG@
+RANLIB = @RANLIB@
+REGEX_BUILD = @REGEX_BUILD@
+RM = @RM@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPELLPROG = @SPELLPROG@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEB_BINDIR = @WEB_BINDIR@
+WEB_BUILD = @WEB_BUILD@
+WEB_PUBCOOKIE_BUILD = @WEB_PUBCOOKIE_BUILD@
+WEB_PUBCOOKIE_LIB = @WEB_PUBCOOKIE_LIB@
+WEB_PUBCOOKIE_LINK = @WEB_PUBCOOKIE_LINK@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+acx_pthread_config = @acx_pthread_config@
+alpine_interactive_spellcheck = @alpine_interactive_spellcheck@
+alpine_simple_spellcheck = @alpine_simple_spellcheck@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+EXTRA_DIST = config.rpath VERSION README LICENSE
+SUBDIRS = m4 po $(REGEX_BUILD) pith pico alpine $(WEB_PUBCOOKIE_BUILD) $(WEB_BUILD)
+BUILT_SOURCES = c-client.d c-client $(WEB_PUBCOOKIE_LINK)
+CLEANFILES = c-client
+man_MANS = doc/alpine.1 doc/pico.1 doc/pilot.1 doc/rpdump.1 doc/rpload.1
+ACLOCAL_AMFLAGS = -I m4
+all: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) all-recursive
+
+.SUFFIXES:
+am--refresh:
+ @:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \
+ $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ echo ' $(SHELL) ./config.status'; \
+ $(SHELL) ./config.status;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ $(SHELL) ./config.status --recheck
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ $(am__cd) $(srcdir) && $(AUTOCONF)
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS)
+$(am__aclocal_m4_deps):
+
+include/config.h: include/stamp-h1
+ @if test ! -f $@; then \
+ rm -f include/stamp-h1; \
+ $(MAKE) $(AM_MAKEFLAGS) include/stamp-h1; \
+ else :; fi
+
+include/stamp-h1: $(top_srcdir)/include/config.h.in $(top_builddir)/config.status
+ @rm -f include/stamp-h1
+ cd $(top_builddir) && $(SHELL) ./config.status include/config.h
+$(top_srcdir)/include/config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ ($(am__cd) $(top_srcdir) && $(AUTOHEADER))
+ rm -f include/stamp-h1
+ touch $@
+
+distclean-hdr:
+ -rm -f include/config.h include/stamp-h1
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+distclean-libtool:
+ -rm -f libtool config.lt
+install-man1: $(man_MANS)
+ @$(NORMAL_INSTALL)
+ test -z "$(man1dir)" || $(MKDIR_P) "$(DESTDIR)$(man1dir)"
+ @list=''; test -n "$(man1dir)" || exit 0; \
+ { for i in $$list; do echo "$$i"; done; \
+ l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+ sed -n '/\.1[a-z]*$$/p'; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man1:
+ @$(NORMAL_UNINSTALL)
+ @list=''; test -n "$(man1dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+ sed -n '/\.1[a-z]*$$/p'; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ test -z "$$files" || { \
+ echo " ( cd '$(DESTDIR)$(man1dir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(man1dir)" && rm -f $$files; }
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run `make' without going through this Makefile.
+# To change the values of `make' variables: instead of editing Makefiles,
+# (1) if the variable is set in `config.status', edit `config.status'
+# (which will cause the Makefiles to be regenerated when you run `make');
+# (2) otherwise, pass the desired values on the `make' command line.
+$(RECURSIVE_TARGETS):
+ @fail= failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+$(RECURSIVE_CLEAN_TARGETS):
+ @fail= failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ rev=''; for subdir in $$list; do \
+ if test "$$subdir" = "."; then :; else \
+ rev="$$subdir $$rev"; \
+ fi; \
+ done; \
+ rev="$$rev ."; \
+ target=`echo $@ | sed s/-recursive//`; \
+ for subdir in $$rev; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done && test -z "$$fail"
+tags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+ done
+ctags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
+ done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @list='$(MANS)'; if test -n "$$list"; then \
+ list=`for p in $$list; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ if test -f "$$d$$p"; then echo "$$d$$p"; else :; fi; done`; \
+ if test -n "$$list" && \
+ grep 'ab help2man is required to generate this page' $$list >/dev/null; then \
+ echo "error: found man pages containing the \`missing help2man' replacement text:" >&2; \
+ grep -l 'ab help2man is required to generate this page' $$list | sed 's/^/ /' >&2; \
+ echo " to fix them, install help2man, remove and regenerate the man pages;" >&2; \
+ echo " typically \`make maintainer-clean' will remove them" >&2; \
+ exit 1; \
+ else :; fi; \
+ else :; fi
+ $(am__remove_distdir)
+ test -d "$(distdir)" || mkdir "$(distdir)"
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+ -test -n "$(am__skip_mode_fix)" \
+ || find "$(distdir)" -type d ! -perm -755 \
+ -exec chmod u+rwx,go+rx {} \; -o \
+ ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
+ ! -type d ! -perm -400 -exec chmod a+r {} \; -o \
+ ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \
+ || chmod -R a+r "$(distdir)"
+dist-gzip: distdir
+ tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
+ $(am__remove_distdir)
+
+dist-bzip2: distdir
+ tardir=$(distdir) && $(am__tar) | bzip2 -9 -c >$(distdir).tar.bz2
+ $(am__remove_distdir)
+
+dist-lzma: distdir
+ tardir=$(distdir) && $(am__tar) | lzma -9 -c >$(distdir).tar.lzma
+ $(am__remove_distdir)
+
+dist-xz: distdir
+ tardir=$(distdir) && $(am__tar) | xz -c >$(distdir).tar.xz
+ $(am__remove_distdir)
+
+dist-tarZ: distdir
+ tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z
+ $(am__remove_distdir)
+
+dist-shar: distdir
+ shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz
+ $(am__remove_distdir)
+
+dist-zip: distdir
+ -rm -f $(distdir).zip
+ zip -rq $(distdir).zip $(distdir)
+ $(am__remove_distdir)
+
+dist dist-all: distdir
+ tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
+ $(am__remove_distdir)
+
+# This target untars the dist file and tries a VPATH configuration. Then
+# it guarantees that the distribution is self-contained by making another
+# tarfile.
+distcheck: dist
+ case '$(DIST_ARCHIVES)' in \
+ *.tar.gz*) \
+ GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\
+ *.tar.bz2*) \
+ bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\
+ *.tar.lzma*) \
+ lzma -dc $(distdir).tar.lzma | $(am__untar) ;;\
+ *.tar.xz*) \
+ xz -dc $(distdir).tar.xz | $(am__untar) ;;\
+ *.tar.Z*) \
+ uncompress -c $(distdir).tar.Z | $(am__untar) ;;\
+ *.shar.gz*) \
+ GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\
+ *.zip*) \
+ unzip $(distdir).zip ;;\
+ esac
+ chmod -R a-w $(distdir); chmod a+w $(distdir)
+ mkdir $(distdir)/_build
+ mkdir $(distdir)/_inst
+ chmod a-w $(distdir)
+ test -d $(distdir)/_build || exit 0; \
+ dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \
+ && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \
+ && am__cwd=`pwd` \
+ && $(am__cd) $(distdir)/_build \
+ && ../configure --srcdir=.. --prefix="$$dc_install_base" \
+ $(DISTCHECK_CONFIGURE_FLAGS) \
+ && $(MAKE) $(AM_MAKEFLAGS) \
+ && $(MAKE) $(AM_MAKEFLAGS) dvi \
+ && $(MAKE) $(AM_MAKEFLAGS) check \
+ && $(MAKE) $(AM_MAKEFLAGS) install \
+ && $(MAKE) $(AM_MAKEFLAGS) installcheck \
+ && $(MAKE) $(AM_MAKEFLAGS) uninstall \
+ && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \
+ distuninstallcheck \
+ && chmod -R a-w "$$dc_install_base" \
+ && ({ \
+ (cd ../.. && umask 077 && mkdir "$$dc_destdir") \
+ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \
+ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \
+ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \
+ distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \
+ } || { rm -rf "$$dc_destdir"; exit 1; }) \
+ && rm -rf "$$dc_destdir" \
+ && $(MAKE) $(AM_MAKEFLAGS) dist \
+ && rm -rf $(DIST_ARCHIVES) \
+ && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \
+ && cd "$$am__cwd" \
+ || exit 1
+ $(am__remove_distdir)
+ @(echo "$(distdir) archives ready for distribution: "; \
+ list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
+ sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x'
+distuninstallcheck:
+ @$(am__cd) '$(distuninstallcheck_dir)' \
+ && test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \
+ || { echo "ERROR: files left after uninstall:" ; \
+ if test -n "$(DESTDIR)"; then \
+ echo " (check DESTDIR support)"; \
+ fi ; \
+ $(distuninstallcheck_listfiles) ; \
+ exit 1; } >&2
+distcleancheck: distclean
+ @if test '$(srcdir)' = . ; then \
+ echo "ERROR: distcleancheck can only run from a VPATH build" ; \
+ exit 1 ; \
+ fi
+ @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \
+ || { echo "ERROR: files left in build directory after distclean:" ; \
+ $(distcleancheck_listfiles) ; \
+ exit 1; } >&2
+check-am: all-am
+check: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) check-recursive
+all-am: Makefile $(MANS)
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(man1dir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool clean-local mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f $(am__CONFIG_DISTCLEAN_FILES)
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-hdr \
+ distclean-libtool distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am: install-man
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man: install-man1
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f $(am__CONFIG_DISTCLEAN_FILES)
+ -rm -rf $(top_srcdir)/autom4te.cache
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-man
+
+uninstall-man: uninstall-man1
+
+.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) all check \
+ ctags-recursive install install-am install-strip \
+ tags-recursive
+
+.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \
+ all all-am am--refresh check check-am clean clean-generic \
+ clean-libtool clean-local ctags ctags-recursive dist dist-all \
+ dist-bzip2 dist-gzip dist-lzma dist-shar dist-tarZ dist-xz \
+ dist-zip distcheck distclean distclean-generic distclean-hdr \
+ distclean-libtool distclean-tags distcleancheck distdir \
+ distuninstallcheck dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-man1 install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs installdirs-am maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-recursive \
+ uninstall uninstall-am uninstall-man uninstall-man1
+
+
+.PHONY: c-client.d c-client gssapi_proxy.l
+
+c-client.d:
+ if test ! -L c-client ; then $(LN_S) imap/c-client c-client ; fi
+
+c-client:
+ $(C_CLIENT_WITH_IPV6)
+ cd imap && $(MAKE) $(C_CLIENT_TARGET) $(C_CLIENT_CFLAGS) $(C_CLIENT_LDFLAGS) $(C_CLIENT_GCCOPTLEVEL) EXTRASPECIALS="$(C_CLIENT_SPECIALS)"
+
+gssapi_proxy.l:
+ if test ! -L c-client/auth_gss_proxy.c ; then $(LN_S) @abs_top_srcdir@/web/src/pubcookie/auth_gss_proxy.c c-client/auth_gss_proxy.c ; fi
+
+clean-local:
+ cd imap && $(MAKE) clean
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 00000000..6e844312
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,96 @@
+Alpine notices:
+
+This product is based on the Pine email system developed by
+the University of Washington (http://www.washington.edu/).
+
+Pine, Pico, and Pilot software and its included text are Copyright
+1989-2005 by the University of Washington.
+
+This product is in part based on The Elm Mail System 2.13
+ ***********************************************************************
+ * The Elm Mail System - Revision: 2.13 *
+ * *
+ * Copyright (c) 1986, 1987 Dave Taylor *
+ * Copyright (c) 1988, 1989 USENET Community Trust *
+ ***********************************************************************
+
+This product is in part based on MicroEMACS 3.6, written by Dave G.
+Conroy; modified by Steve Wilhite, and George Jones; and greatly
+modified by Daniel Lawrence.
+
+Some of the code dealing with temporary files in this product was dervived
+from 4.3 BSD code, which was developed by the University of California.
+
+
+ *********************************************************************
+ This code is derived from software contributed to Berkeley by
+ Chris Torek.
+
+ Copyright (c) 1990, 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. 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.
+ *********************************************************************
+
+The code dealing with regular expressions in pith/regex is
+
+ Copyright (c) 1992 Henry Spencer.
+ Copyright (c) 1992, 1993
+ The Regents of the University of California. All rights reserved.
+
+ This code is derived from software contributed to Berkeley by
+ Henry Spencer of the University of Toronto.
+
+ 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.
diff --git a/README b/README
new file mode 100644
index 00000000..fb3b83e2
--- /dev/null
+++ b/README
@@ -0,0 +1,147 @@
+
+
+-----------------------------------------------------------------------
+ Alpine/Pico/Pilot/Web Alpine/Imapd Distribution
+-----------------------------------------------------------------------
+
+
+/* ====================================================================
+ * Copyright 2006-2009 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ====================================================================
+*/
+
+For the latest info about Alpine, see http://www.washington.edu/alpine
+
+-----------------------------------------------------------------------
+DISTRIBUTION CONTENTS
+-----------------------------------------------------------------------
+
+This Alpine distribution includes:
+
+alpine - The Alpine source directory
+configure - Script to set system-specific configuration, defaults
+contrib - Contributed ports and additions
+doc - Documentation directory. The main documentation is
+ tech-notes.txt
+imap - Source tree containing C-Client IMAP implementation
+m4 - macros used in the configure process
+packages - Scripts for building packages for various Linux
+ distributions
+pico - The Pico and Pilot source directory
+pith - Core functions common to Alpine/Web Alpine
+po - localization data
+web - Source and scripts for web-based version of Alpine
+
+Most of the documentation is in doc/tech-notes.txt. It is not user
+level documentation, but there are things in it some users might find
+useful. The directory doc/tech-notes contains source for
+doc/tech-notes.txt in HTML format which can be viewed via a Web
+pbrowser by opening doc/tech-notes/index.html.
+
+User level documentation for Alpine is contained in the programs
+themselves in the form of context-sensitive help.
+
+
+-----------------------------------------------------------------------
+PRELIMINARIES
+-----------------------------------------------------------------------
+
+If you are reading this, you have presumably succeeded in extracting
+the distribution from the compressed tar archive file, via the
+following command, or equivalent:
+
+ tar zxf alpine.tar.z
+
+Some of the instructions that follow assume that your current workding
+directory is the alpine-X.XX directory created by the un-tar process
+above.
+
+
+-----------------------------------------------------------------------
+BUILD PROCESS
+-----------------------------------------------------------------------
+
+The Alpine build process is based on GNU autotools. On most Unix
+systems, generating a suitable Alpine binary from the source
+distribution should be as simple as typing the commands:
+
+ ./configure
+ make
+
+For a list of configuration options and default Alpine settings type:
+
+ ./configure --help
+
+Note, the included UW IMAP Toolkit used for mailbox access does not
+make use of GNU autotools. However, in most cases Alpine's configure
+script should set the appropriate make target and options. The
+targetted OS can be set from Alpine's configure command line, but in
+rare cases more significant manual intervention may be required. If
+problems are encountered, see imap/README for more details.
+
+The PC-Alpine build is based on the Microsoft C compiler and
+libraries. The Alpine Team bases builds on Visual Studio 8 from the
+command line using the static build.bat batch and makefiles to
+generate suitable binaries.
+
+The Web Alpine application requires a few extra, manual steps to get
+all the components built and installed. See web/README for an
+explanation of the various components and web/INSTALL for a basic
+installation recipe.
+
+
+-----------------------------------------------------------------------
+RESULTING EXECUTABLES
+-----------------------------------------------------------------------
+
+The executables produced are:
+
+ alpine The Alpine mailer. Once compiled this should work just fine on
+ your system with no other files than this binary, and no
+ modifications to your system. Optionally you may create two
+ configuration files, /usr/local/lib/pine.conf and
+ /usr/local/lib/pine.info. See the documentation for details.
+
+ pico The standalone editor similar to the Alpine message composer.
+ This is a very simple straight forward text editor.
+
+ pilot The standalone file system navigator.
+
+ alpined
+ The Web Alpine serveret that is the primary component of
+ Web Alpine
+
+ imapd The IMAP daemon. If you want to run alpine in client/server
+ mode, this is the daemon to run on the server. Installing this
+ requires system privileges and modifications to /etc/services.
+ See doc/tech-notes for more details.
+
+ mtest The test IMAP client, an absolutely minimal mail client, useful
+ for debugging.
+
+ rpload Utility for uploading a local pinerc or address book to an IMAP
+ server.
+
+ rpdump Utility for downloading a pinerc or address book to the
+ local machine.
+
+ mailutil
+ Utility for performing various operations on mailboxes,
+ be they local or remote.
+
+In general "make install" should place alpine, pico and pilot, and
+their corresponding man pages, in the proper directory for your
+system. As the remaining binaries are intended for specific uses or
+are a component of a larger package, their installation is typically
+done by hand.
+
+--
+alpine.tar.z README
+$Id: README 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
diff --git a/VERSION b/VERSION
new file mode 100644
index 00000000..37989bd1
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+2.10
diff --git a/aclocal.m4 b/aclocal.m4
new file mode 100644
index 00000000..897d223d
--- /dev/null
+++ b/aclocal.m4
@@ -0,0 +1,1005 @@
+# generated automatically by aclocal 1.11.1 -*- Autoconf -*-
+
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+# 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+m4_ifndef([AC_AUTOCONF_VERSION],
+ [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
+m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.68],,
+[m4_warning([this file was generated for autoconf 2.68.
+You have another version of autoconf. It may work, but is not guaranteed to.
+If you have problems, you may need to regenerate the build system entirely.
+To do so, use the procedure documented by the package, typically `autoreconf'.])])
+
+# Copyright (C) 2002, 2003, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_AUTOMAKE_VERSION(VERSION)
+# ----------------------------
+# Automake X.Y traces this macro to ensure aclocal.m4 has been
+# generated from the m4 files accompanying Automake X.Y.
+# (This private macro should not be called outside this file.)
+AC_DEFUN([AM_AUTOMAKE_VERSION],
+[am__api_version='1.11'
+dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
+dnl require some minimum version. Point them to the right macro.
+m4_if([$1], [1.11.1], [],
+ [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
+])
+
+# _AM_AUTOCONF_VERSION(VERSION)
+# -----------------------------
+# aclocal traces this macro to find the Autoconf version.
+# This is a private macro too. Using m4_define simplifies
+# the logic in aclocal, which can simply ignore this definition.
+m4_define([_AM_AUTOCONF_VERSION], [])
+
+# AM_SET_CURRENT_AUTOMAKE_VERSION
+# -------------------------------
+# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
+# This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
+AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
+[AM_AUTOMAKE_VERSION([1.11.1])dnl
+m4_ifndef([AC_AUTOCONF_VERSION],
+ [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
+_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
+
+# AM_AUX_DIR_EXPAND -*- Autoconf -*-
+
+# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets
+# $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to
+# `$srcdir', `$srcdir/..', or `$srcdir/../..'.
+#
+# Of course, Automake must honor this variable whenever it calls a
+# tool from the auxiliary directory. The problem is that $srcdir (and
+# therefore $ac_aux_dir as well) can be either absolute or relative,
+# depending on how configure is run. This is pretty annoying, since
+# it makes $ac_aux_dir quite unusable in subdirectories: in the top
+# source directory, any form will work fine, but in subdirectories a
+# relative path needs to be adjusted first.
+#
+# $ac_aux_dir/missing
+# fails when called from a subdirectory if $ac_aux_dir is relative
+# $top_srcdir/$ac_aux_dir/missing
+# fails if $ac_aux_dir is absolute,
+# fails when called from a subdirectory in a VPATH build with
+# a relative $ac_aux_dir
+#
+# The reason of the latter failure is that $top_srcdir and $ac_aux_dir
+# are both prefixed by $srcdir. In an in-source build this is usually
+# harmless because $srcdir is `.', but things will broke when you
+# start a VPATH build or use an absolute $srcdir.
+#
+# So we could use something similar to $top_srcdir/$ac_aux_dir/missing,
+# iff we strip the leading $srcdir from $ac_aux_dir. That would be:
+# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"`
+# and then we would define $MISSING as
+# MISSING="\${SHELL} $am_aux_dir/missing"
+# This will work as long as MISSING is not called from configure, because
+# unfortunately $(top_srcdir) has no meaning in configure.
+# However there are other variables, like CC, which are often used in
+# configure, and could therefore not use this "fixed" $ac_aux_dir.
+#
+# Another solution, used here, is to always expand $ac_aux_dir to an
+# absolute PATH. The drawback is that using absolute paths prevent a
+# configured tree to be moved without reconfiguration.
+
+AC_DEFUN([AM_AUX_DIR_EXPAND],
+[dnl Rely on autoconf to set up CDPATH properly.
+AC_PREREQ([2.50])dnl
+# expand $ac_aux_dir to an absolute path
+am_aux_dir=`cd $ac_aux_dir && pwd`
+])
+
+# AM_CONDITIONAL -*- Autoconf -*-
+
+# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005, 2006, 2008
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 9
+
+# AM_CONDITIONAL(NAME, SHELL-CONDITION)
+# -------------------------------------
+# Define a conditional.
+AC_DEFUN([AM_CONDITIONAL],
+[AC_PREREQ(2.52)dnl
+ ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])],
+ [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
+AC_SUBST([$1_TRUE])dnl
+AC_SUBST([$1_FALSE])dnl
+_AM_SUBST_NOTMAKE([$1_TRUE])dnl
+_AM_SUBST_NOTMAKE([$1_FALSE])dnl
+m4_define([_AM_COND_VALUE_$1], [$2])dnl
+if $2; then
+ $1_TRUE=
+ $1_FALSE='#'
+else
+ $1_TRUE='#'
+ $1_FALSE=
+fi
+AC_CONFIG_COMMANDS_PRE(
+[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then
+ AC_MSG_ERROR([[conditional "$1" was never defined.
+Usually this means the macro was only invoked conditionally.]])
+fi])])
+
+# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 10
+
+# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be
+# written in clear, in which case automake, when reading aclocal.m4,
+# will think it sees a *use*, and therefore will trigger all it's
+# C support machinery. Also note that it means that autoscan, seeing
+# CC etc. in the Makefile, will ask for an AC_PROG_CC use...
+
+
+# _AM_DEPENDENCIES(NAME)
+# ----------------------
+# See how the compiler implements dependency checking.
+# NAME is "CC", "CXX", "GCJ", or "OBJC".
+# We try a few techniques and use that to set a single cache variable.
+#
+# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was
+# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular
+# dependency, and given that the user is not expected to run this macro,
+# just rely on AC_PROG_CC.
+AC_DEFUN([_AM_DEPENDENCIES],
+[AC_REQUIRE([AM_SET_DEPDIR])dnl
+AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl
+AC_REQUIRE([AM_MAKE_INCLUDE])dnl
+AC_REQUIRE([AM_DEP_TRACK])dnl
+
+ifelse([$1], CC, [depcc="$CC" am_compiler_list=],
+ [$1], CXX, [depcc="$CXX" am_compiler_list=],
+ [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
+ [$1], UPC, [depcc="$UPC" am_compiler_list=],
+ [$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'],
+ [depcc="$$1" am_compiler_list=])
+
+AC_CACHE_CHECK([dependency style of $depcc],
+ [am_cv_$1_dependencies_compiler_type],
+[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+ # We make a subdir and do the tests there. Otherwise we can end up
+ # making bogus files that we don't know about and never remove. For
+ # instance it was reported that on HP-UX the gcc test will end up
+ # making a dummy file named `D' -- because `-MD' means `put the output
+ # in D'.
+ mkdir conftest.dir
+ # Copy depcomp to subdir because otherwise we won't find it if we're
+ # using a relative directory.
+ cp "$am_depcomp" conftest.dir
+ cd conftest.dir
+ # We will build objects and dependencies in a subdirectory because
+ # it helps to detect inapplicable dependency modes. For instance
+ # both Tru64's cc and ICC support -MD to output dependencies as a
+ # side effect of compilation, but ICC will put the dependencies in
+ # the current directory while Tru64 will put them in the object
+ # directory.
+ mkdir sub
+
+ am_cv_$1_dependencies_compiler_type=none
+ if test "$am_compiler_list" = ""; then
+ am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp`
+ fi
+ am__universal=false
+ m4_case([$1], [CC],
+ [case " $depcc " in #(
+ *\ -arch\ *\ -arch\ *) am__universal=true ;;
+ esac],
+ [CXX],
+ [case " $depcc " in #(
+ *\ -arch\ *\ -arch\ *) am__universal=true ;;
+ esac])
+
+ for depmode in $am_compiler_list; do
+ # Setup a source with many dependencies, because some compilers
+ # like to wrap large dependency lists on column 80 (with \), and
+ # we should not choose a depcomp mode which is confused by this.
+ #
+ # We need to recreate these files for each test, as the compiler may
+ # overwrite some of them when testing with obscure command lines.
+ # This happens at least with the AIX C compiler.
+ : > sub/conftest.c
+ for i in 1 2 3 4 5 6; do
+ echo '#include "conftst'$i'.h"' >> sub/conftest.c
+ # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
+ # Solaris 8's {/usr,}/bin/sh.
+ touch sub/conftst$i.h
+ done
+ echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+ # We check with `-c' and `-o' for the sake of the "dashmstdout"
+ # mode. It turns out that the SunPro C++ compiler does not properly
+ # handle `-M -o', and we need to detect this. Also, some Intel
+ # versions had trouble with output in subdirs
+ am__obj=sub/conftest.${OBJEXT-o}
+ am__minus_obj="-o $am__obj"
+ case $depmode in
+ gcc)
+ # This depmode causes a compiler race in universal mode.
+ test "$am__universal" = false || continue
+ ;;
+ nosideeffect)
+ # after this tag, mechanisms are not by side-effect, so they'll
+ # only be used when explicitly requested
+ if test "x$enable_dependency_tracking" = xyes; then
+ continue
+ else
+ break
+ fi
+ ;;
+ msvisualcpp | msvcmsys)
+ # This compiler won't grok `-c -o', but also, the minuso test has
+ # not run yet. These depmodes are late enough in the game, and
+ # so weak that their functioning should not be impacted.
+ am__obj=conftest.${OBJEXT-o}
+ am__minus_obj=
+ ;;
+ none) break ;;
+ esac
+ if depmode=$depmode \
+ source=sub/conftest.c object=$am__obj \
+ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
+ >/dev/null 2>conftest.err &&
+ grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
+ ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+ # icc doesn't choke on unknown options, it will just issue warnings
+ # or remarks (even with -Werror). So we grep stderr for any message
+ # that says an option was ignored or not supported.
+ # When given -MP, icc 7.0 and 7.1 complain thusly:
+ # icc: Command line warning: ignoring option '-M'; no argument required
+ # The diagnosis changed in icc 8.0:
+ # icc: Command line remark: option '-MP' not supported
+ if (grep 'ignoring option' conftest.err ||
+ grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+ am_cv_$1_dependencies_compiler_type=$depmode
+ break
+ fi
+ fi
+ done
+
+ cd ..
+ rm -rf conftest.dir
+else
+ am_cv_$1_dependencies_compiler_type=none
+fi
+])
+AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type])
+AM_CONDITIONAL([am__fastdep$1], [
+ test "x$enable_dependency_tracking" != xno \
+ && test "$am_cv_$1_dependencies_compiler_type" = gcc3])
+])
+
+
+# AM_SET_DEPDIR
+# -------------
+# Choose a directory name for dependency files.
+# This macro is AC_REQUIREd in _AM_DEPENDENCIES
+AC_DEFUN([AM_SET_DEPDIR],
+[AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl
+])
+
+
+# AM_DEP_TRACK
+# ------------
+AC_DEFUN([AM_DEP_TRACK],
+[AC_ARG_ENABLE(dependency-tracking,
+[ --disable-dependency-tracking speeds up one-time build
+ --enable-dependency-tracking do not reject slow dependency extractors])
+if test "x$enable_dependency_tracking" != xno; then
+ am_depcomp="$ac_aux_dir/depcomp"
+ AMDEPBACKSLASH='\'
+fi
+AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
+AC_SUBST([AMDEPBACKSLASH])dnl
+_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl
+])
+
+# Generate code to set up dependency tracking. -*- Autoconf -*-
+
+# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2008
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+#serial 5
+
+# _AM_OUTPUT_DEPENDENCY_COMMANDS
+# ------------------------------
+AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
+[{
+ # Autoconf 2.62 quotes --file arguments for eval, but not when files
+ # are listed without --file. Let's play safe and only enable the eval
+ # if we detect the quoting.
+ case $CONFIG_FILES in
+ *\'*) eval set x "$CONFIG_FILES" ;;
+ *) set x $CONFIG_FILES ;;
+ esac
+ shift
+ for mf
+ do
+ # Strip MF so we end up with the name of the file.
+ mf=`echo "$mf" | sed -e 's/:.*$//'`
+ # Check whether this is an Automake generated Makefile or not.
+ # We used to match only the files named `Makefile.in', but
+ # some people rename them; so instead we look at the file content.
+ # Grep'ing the first line is not enough: some people post-process
+ # each Makefile.in and add a new line on top of each file to say so.
+ # Grep'ing the whole file is not good either: AIX grep has a line
+ # limit of 2048, but all sed's we know have understand at least 4000.
+ if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
+ dirpart=`AS_DIRNAME("$mf")`
+ else
+ continue
+ fi
+ # Extract the definition of DEPDIR, am__include, and am__quote
+ # from the Makefile without running `make'.
+ DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
+ test -z "$DEPDIR" && continue
+ am__include=`sed -n 's/^am__include = //p' < "$mf"`
+ test -z "am__include" && continue
+ am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
+ # When using ansi2knr, U may be empty or an underscore; expand it
+ U=`sed -n 's/^U = //p' < "$mf"`
+ # Find all dependency output files, they are included files with
+ # $(DEPDIR) in their names. We invoke sed twice because it is the
+ # simplest approach to changing $(DEPDIR) to its actual value in the
+ # expansion.
+ for file in `sed -n "
+ s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
+ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
+ # Make sure the directory exists.
+ test -f "$dirpart/$file" && continue
+ fdir=`AS_DIRNAME(["$file"])`
+ AS_MKDIR_P([$dirpart/$fdir])
+ # echo "creating $dirpart/$file"
+ echo '# dummy' > "$dirpart/$file"
+ done
+ done
+}
+])# _AM_OUTPUT_DEPENDENCY_COMMANDS
+
+
+# AM_OUTPUT_DEPENDENCY_COMMANDS
+# -----------------------------
+# This macro should only be invoked once -- use via AC_REQUIRE.
+#
+# This code is only required when automatic dependency tracking
+# is enabled. FIXME. This creates each `.P' file that we will
+# need in order to bootstrap the dependency handling code.
+AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
+[AC_CONFIG_COMMANDS([depfiles],
+ [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS],
+ [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"])
+])
+
+# Do all the work for Automake. -*- Autoconf -*-
+
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+# 2005, 2006, 2008, 2009 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 16
+
+# This macro actually does too much. Some checks are only needed if
+# your package does certain things. But this isn't really a big deal.
+
+# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE])
+# AM_INIT_AUTOMAKE([OPTIONS])
+# -----------------------------------------------
+# The call with PACKAGE and VERSION arguments is the old style
+# call (pre autoconf-2.50), which is being phased out. PACKAGE
+# and VERSION should now be passed to AC_INIT and removed from
+# the call to AM_INIT_AUTOMAKE.
+# We support both call styles for the transition. After
+# the next Automake release, Autoconf can make the AC_INIT
+# arguments mandatory, and then we can depend on a new Autoconf
+# release and drop the old call support.
+AC_DEFUN([AM_INIT_AUTOMAKE],
+[AC_PREREQ([2.62])dnl
+dnl Autoconf wants to disallow AM_ names. We explicitly allow
+dnl the ones we care about.
+m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
+AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
+AC_REQUIRE([AC_PROG_INSTALL])dnl
+if test "`cd $srcdir && pwd`" != "`pwd`"; then
+ # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
+ # is not polluted with repeated "-I."
+ AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl
+ # test to see if srcdir already configured
+ if test -f $srcdir/config.status; then
+ AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
+ fi
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+ if (cygpath --version) >/dev/null 2>/dev/null; then
+ CYGPATH_W='cygpath -w'
+ else
+ CYGPATH_W=echo
+ fi
+fi
+AC_SUBST([CYGPATH_W])
+
+# Define the identity of the package.
+dnl Distinguish between old-style and new-style calls.
+m4_ifval([$2],
+[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
+ AC_SUBST([PACKAGE], [$1])dnl
+ AC_SUBST([VERSION], [$2])],
+[_AM_SET_OPTIONS([$1])dnl
+dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT.
+m4_if(m4_ifdef([AC_PACKAGE_NAME], 1)m4_ifdef([AC_PACKAGE_VERSION], 1), 11,,
+ [m4_fatal([AC_INIT should be called with package and version arguments])])dnl
+ AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
+ AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
+
+_AM_IF_OPTION([no-define],,
+[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package])
+ AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl
+
+# Some tools Automake needs.
+AC_REQUIRE([AM_SANITY_CHECK])dnl
+AC_REQUIRE([AC_ARG_PROGRAM])dnl
+AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version})
+AM_MISSING_PROG(AUTOCONF, autoconf)
+AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version})
+AM_MISSING_PROG(AUTOHEADER, autoheader)
+AM_MISSING_PROG(MAKEINFO, makeinfo)
+AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
+AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl
+AC_REQUIRE([AM_PROG_MKDIR_P])dnl
+# We need awk for the "check" target. The system "awk" is bad on
+# some platforms.
+AC_REQUIRE([AC_PROG_AWK])dnl
+AC_REQUIRE([AC_PROG_MAKE_SET])dnl
+AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])],
+ [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])],
+ [_AM_PROG_TAR([v7])])])
+_AM_IF_OPTION([no-dependencies],,
+[AC_PROVIDE_IFELSE([AC_PROG_CC],
+ [_AM_DEPENDENCIES(CC)],
+ [define([AC_PROG_CC],
+ defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_CXX],
+ [_AM_DEPENDENCIES(CXX)],
+ [define([AC_PROG_CXX],
+ defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_OBJC],
+ [_AM_DEPENDENCIES(OBJC)],
+ [define([AC_PROG_OBJC],
+ defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])dnl
+])
+_AM_IF_OPTION([silent-rules], [AC_REQUIRE([AM_SILENT_RULES])])dnl
+dnl The `parallel-tests' driver may need to know about EXEEXT, so add the
+dnl `am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This macro
+dnl is hooked onto _AC_COMPILER_EXEEXT early, see below.
+AC_CONFIG_COMMANDS_PRE(dnl
+[m4_provide_if([_AM_COMPILER_EXEEXT],
+ [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl
+])
+
+dnl Hook into `_AC_COMPILER_EXEEXT' early to learn its expansion. Do not
+dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further
+dnl mangled by Autoconf and run in a shell conditional statement.
+m4_define([_AC_COMPILER_EXEEXT],
+m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])])
+
+
+# When config.status generates a header, we must update the stamp-h file.
+# This file resides in the same directory as the config header
+# that is generated. The stamp files are numbered to have different names.
+
+# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the
+# loop where config.status creates the headers, so we can generate
+# our stamp files there.
+AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK],
+[# Compute $1's index in $config_headers.
+_am_arg=$1
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+ case $_am_header in
+ $_am_arg | $_am_arg:* )
+ break ;;
+ * )
+ _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+ esac
+done
+echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
+
+# Copyright (C) 2001, 2003, 2005, 2008 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_PROG_INSTALL_SH
+# ------------------
+# Define $install_sh.
+AC_DEFUN([AM_PROG_INSTALL_SH],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+if test x"${install_sh}" != xset; then
+ case $am_aux_dir in
+ *\ * | *\ *)
+ install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
+ *)
+ install_sh="\${SHELL} $am_aux_dir/install-sh"
+ esac
+fi
+AC_SUBST(install_sh)])
+
+# Copyright (C) 2003, 2005 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 2
+
+# Check whether the underlying file-system supports filenames
+# with a leading dot. For instance MS-DOS doesn't.
+AC_DEFUN([AM_SET_LEADING_DOT],
+[rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+ am__leading_dot=.
+else
+ am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+AC_SUBST([am__leading_dot])])
+
+# Add --enable-maintainer-mode option to configure. -*- Autoconf -*-
+# From Jim Meyering
+
+# Copyright (C) 1996, 1998, 2000, 2001, 2002, 2003, 2004, 2005, 2008
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 5
+
+# AM_MAINTAINER_MODE([DEFAULT-MODE])
+# ----------------------------------
+# Control maintainer-specific portions of Makefiles.
+# Default is to disable them, unless `enable' is passed literally.
+# For symmetry, `disable' may be passed as well. Anyway, the user
+# can override the default with the --enable/--disable switch.
+AC_DEFUN([AM_MAINTAINER_MODE],
+[m4_case(m4_default([$1], [disable]),
+ [enable], [m4_define([am_maintainer_other], [disable])],
+ [disable], [m4_define([am_maintainer_other], [enable])],
+ [m4_define([am_maintainer_other], [enable])
+ m4_warn([syntax], [unexpected argument to AM@&t@_MAINTAINER_MODE: $1])])
+AC_MSG_CHECKING([whether to am_maintainer_other maintainer-specific portions of Makefiles])
+ dnl maintainer-mode's default is 'disable' unless 'enable' is passed
+ AC_ARG_ENABLE([maintainer-mode],
+[ --][am_maintainer_other][-maintainer-mode am_maintainer_other make rules and dependencies not useful
+ (and sometimes confusing) to the casual installer],
+ [USE_MAINTAINER_MODE=$enableval],
+ [USE_MAINTAINER_MODE=]m4_if(am_maintainer_other, [enable], [no], [yes]))
+ AC_MSG_RESULT([$USE_MAINTAINER_MODE])
+ AM_CONDITIONAL([MAINTAINER_MODE], [test $USE_MAINTAINER_MODE = yes])
+ MAINT=$MAINTAINER_MODE_TRUE
+ AC_SUBST([MAINT])dnl
+]
+)
+
+AU_DEFUN([jm_MAINTAINER_MODE], [AM_MAINTAINER_MODE])
+
+# Check to see how 'make' treats includes. -*- Autoconf -*-
+
+# Copyright (C) 2001, 2002, 2003, 2005, 2009 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 4
+
+# AM_MAKE_INCLUDE()
+# -----------------
+# Check to see how make treats includes.
+AC_DEFUN([AM_MAKE_INCLUDE],
+[am_make=${MAKE-make}
+cat > confinc << 'END'
+am__doit:
+ @echo this is the am__doit target
+.PHONY: am__doit
+END
+# If we don't find an include directive, just comment out the code.
+AC_MSG_CHECKING([for style of include used by $am_make])
+am__include="#"
+am__quote=
+_am_result=none
+# First try GNU make style include.
+echo "include confinc" > confmf
+# Ignore all kinds of additional output from `make'.
+case `$am_make -s -f confmf 2> /dev/null` in #(
+*the\ am__doit\ target*)
+ am__include=include
+ am__quote=
+ _am_result=GNU
+ ;;
+esac
+# Now try BSD make style include.
+if test "$am__include" = "#"; then
+ echo '.include "confinc"' > confmf
+ case `$am_make -s -f confmf 2> /dev/null` in #(
+ *the\ am__doit\ target*)
+ am__include=.include
+ am__quote="\""
+ _am_result=BSD
+ ;;
+ esac
+fi
+AC_SUBST([am__include])
+AC_SUBST([am__quote])
+AC_MSG_RESULT([$_am_result])
+rm -f confinc confmf
+])
+
+# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*-
+
+# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2004, 2005, 2008
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 6
+
+# AM_MISSING_PROG(NAME, PROGRAM)
+# ------------------------------
+AC_DEFUN([AM_MISSING_PROG],
+[AC_REQUIRE([AM_MISSING_HAS_RUN])
+$1=${$1-"${am_missing_run}$2"}
+AC_SUBST($1)])
+
+
+# AM_MISSING_HAS_RUN
+# ------------------
+# Define MISSING if not defined so far and test if it supports --run.
+# If it does, set am_missing_run to use it, otherwise, to nothing.
+AC_DEFUN([AM_MISSING_HAS_RUN],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+AC_REQUIRE_AUX_FILE([missing])dnl
+if test x"${MISSING+set}" != xset; then
+ case $am_aux_dir in
+ *\ * | *\ *)
+ MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
+ *)
+ MISSING="\${SHELL} $am_aux_dir/missing" ;;
+ esac
+fi
+# Use eval to expand $SHELL
+if eval "$MISSING --run true"; then
+ am_missing_run="$MISSING --run "
+else
+ am_missing_run=
+ AC_MSG_WARN([`missing' script is too old or missing])
+fi
+])
+
+# Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_PROG_MKDIR_P
+# ---------------
+# Check for `mkdir -p'.
+AC_DEFUN([AM_PROG_MKDIR_P],
+[AC_PREREQ([2.60])dnl
+AC_REQUIRE([AC_PROG_MKDIR_P])dnl
+dnl Automake 1.8 to 1.9.6 used to define mkdir_p. We now use MKDIR_P,
+dnl while keeping a definition of mkdir_p for backward compatibility.
+dnl @MKDIR_P@ is magic: AC_OUTPUT adjusts its value for each Makefile.
+dnl However we cannot define mkdir_p as $(MKDIR_P) for the sake of
+dnl Makefile.ins that do not define MKDIR_P, so we do our own
+dnl adjustment using top_builddir (which is defined more often than
+dnl MKDIR_P).
+AC_SUBST([mkdir_p], ["$MKDIR_P"])dnl
+case $mkdir_p in
+ [[\\/$]]* | ?:[[\\/]]*) ;;
+ */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;;
+esac
+])
+
+# Helper functions for option handling. -*- Autoconf -*-
+
+# Copyright (C) 2001, 2002, 2003, 2005, 2008 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 4
+
+# _AM_MANGLE_OPTION(NAME)
+# -----------------------
+AC_DEFUN([_AM_MANGLE_OPTION],
+[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])])
+
+# _AM_SET_OPTION(NAME)
+# ------------------------------
+# Set option NAME. Presently that only means defining a flag for this option.
+AC_DEFUN([_AM_SET_OPTION],
+[m4_define(_AM_MANGLE_OPTION([$1]), 1)])
+
+# _AM_SET_OPTIONS(OPTIONS)
+# ----------------------------------
+# OPTIONS is a space-separated list of Automake options.
+AC_DEFUN([_AM_SET_OPTIONS],
+[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])])
+
+# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET])
+# -------------------------------------------
+# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
+AC_DEFUN([_AM_IF_OPTION],
+[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
+
+# Check to make sure that the build environment is sane. -*- Autoconf -*-
+
+# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005, 2008
+# Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 5
+
+# AM_SANITY_CHECK
+# ---------------
+AC_DEFUN([AM_SANITY_CHECK],
+[AC_MSG_CHECKING([whether build environment is sane])
+# Just in case
+sleep 1
+echo timestamp > conftest.file
+# Reject unsafe characters in $srcdir or the absolute working directory
+# name. Accept space and tab only in the latter.
+am_lf='
+'
+case `pwd` in
+ *[[\\\"\#\$\&\'\`$am_lf]]*)
+ AC_MSG_ERROR([unsafe absolute working directory name]);;
+esac
+case $srcdir in
+ *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*)
+ AC_MSG_ERROR([unsafe srcdir value: `$srcdir']);;
+esac
+
+# Do `set' in a subshell so we don't clobber the current shell's
+# arguments. Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+ set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
+ if test "$[*]" = "X"; then
+ # -L didn't work.
+ set X `ls -t "$srcdir/configure" conftest.file`
+ fi
+ rm -f conftest.file
+ if test "$[*]" != "X $srcdir/configure conftest.file" \
+ && test "$[*]" != "X conftest.file $srcdir/configure"; then
+
+ # If neither matched, then we have a broken ls. This can happen
+ # if, for instance, CONFIG_SHELL is bash and it inherits a
+ # broken ls alias from the environment. This has actually
+ # happened. Such a system could not be considered "sane".
+ AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken
+alias in your environment])
+ fi
+
+ test "$[2]" = conftest.file
+ )
+then
+ # Ok.
+ :
+else
+ AC_MSG_ERROR([newly created file is older than distributed files!
+Check your system clock])
+fi
+AC_MSG_RESULT(yes)])
+
+# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_PROG_INSTALL_STRIP
+# ---------------------
+# One issue with vendor `install' (even GNU) is that you can't
+# specify the program used to strip binaries. This is especially
+# annoying in cross-compiling environments, where the build's strip
+# is unlikely to handle the host's binaries.
+# Fortunately install-sh will honor a STRIPPROG variable, so we
+# always use install-sh in `make install-strip', and initialize
+# STRIPPROG with the value of the STRIP variable (set by the user).
+AC_DEFUN([AM_PROG_INSTALL_STRIP],
+[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
+# Installed binaries are usually stripped using `strip' when the user
+# run `make install-strip'. However `strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the `STRIP' environment variable to overrule this program.
+dnl Don't test for $cross_compiling = yes, because it might be `maybe'.
+if test "$cross_compiling" != no; then
+ AC_CHECK_TOOL([STRIP], [strip], :)
+fi
+INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
+AC_SUBST([INSTALL_STRIP_PROGRAM])])
+
+# Copyright (C) 2006, 2008 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 2
+
+# _AM_SUBST_NOTMAKE(VARIABLE)
+# ---------------------------
+# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in.
+# This macro is traced by Automake.
+AC_DEFUN([_AM_SUBST_NOTMAKE])
+
+# AM_SUBST_NOTMAKE(VARIABLE)
+# ---------------------------
+# Public sister of _AM_SUBST_NOTMAKE.
+AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
+
+# Check how to create a tarball. -*- Autoconf -*-
+
+# Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# serial 2
+
+# _AM_PROG_TAR(FORMAT)
+# --------------------
+# Check how to create a tarball in format FORMAT.
+# FORMAT should be one of `v7', `ustar', or `pax'.
+#
+# Substitute a variable $(am__tar) that is a command
+# writing to stdout a FORMAT-tarball containing the directory
+# $tardir.
+# tardir=directory && $(am__tar) > result.tar
+#
+# Substitute a variable $(am__untar) that extract such
+# a tarball read from stdin.
+# $(am__untar) < result.tar
+AC_DEFUN([_AM_PROG_TAR],
+[# Always define AMTAR for backward compatibility.
+AM_MISSING_PROG([AMTAR], [tar])
+m4_if([$1], [v7],
+ [am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'],
+ [m4_case([$1], [ustar],, [pax],,
+ [m4_fatal([Unknown tar format])])
+AC_MSG_CHECKING([how to create a $1 tar archive])
+# Loop over all known methods to create a tar archive until one works.
+_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none'
+_am_tools=${am_cv_prog_tar_$1-$_am_tools}
+# Do not fold the above two line into one, because Tru64 sh and
+# Solaris sh will not grok spaces in the rhs of `-'.
+for _am_tool in $_am_tools
+do
+ case $_am_tool in
+ gnutar)
+ for _am_tar in tar gnutar gtar;
+ do
+ AM_RUN_LOG([$_am_tar --version]) && break
+ done
+ am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
+ am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
+ am__untar="$_am_tar -xf -"
+ ;;
+ plaintar)
+ # Must skip GNU tar: if it does not support --format= it doesn't create
+ # ustar tarball either.
+ (tar --version) >/dev/null 2>&1 && continue
+ am__tar='tar chf - "$$tardir"'
+ am__tar_='tar chf - "$tardir"'
+ am__untar='tar xf -'
+ ;;
+ pax)
+ am__tar='pax -L -x $1 -w "$$tardir"'
+ am__tar_='pax -L -x $1 -w "$tardir"'
+ am__untar='pax -r'
+ ;;
+ cpio)
+ am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
+ am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
+ am__untar='cpio -i -H $1 -d'
+ ;;
+ none)
+ am__tar=false
+ am__tar_=false
+ am__untar=false
+ ;;
+ esac
+
+ # If the value was cached, stop now. We just wanted to have am__tar
+ # and am__untar set.
+ test -n "${am_cv_prog_tar_$1}" && break
+
+ # tar/untar a dummy directory, and stop if the command works
+ rm -rf conftest.dir
+ mkdir conftest.dir
+ echo GrepMe > conftest.dir/file
+ AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
+ rm -rf conftest.dir
+ if test -s conftest.tar; then
+ AM_RUN_LOG([$am__untar <conftest.tar])
+ grep GrepMe conftest.dir/file >/dev/null 2>&1 && break
+ fi
+done
+rm -rf conftest.dir
+
+AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
+AC_MSG_RESULT([$am_cv_prog_tar_$1])])
+AC_SUBST([am__tar])
+AC_SUBST([am__untar])
+]) # _AM_PROG_TAR
+
+m4_include([m4/acx_pthread.m4])
+m4_include([m4/gettext.m4])
+m4_include([m4/iconv.m4])
+m4_include([m4/lib-ld.m4])
+m4_include([m4/lib-link.m4])
+m4_include([m4/lib-prefix.m4])
+m4_include([m4/libtool.m4])
+m4_include([m4/ltoptions.m4])
+m4_include([m4/ltsugar.m4])
+m4_include([m4/ltversion.m4])
+m4_include([m4/lt~obsolete.m4])
+m4_include([m4/nls.m4])
+m4_include([m4/po.m4])
+m4_include([m4/progtest.m4])
diff --git a/alpine/Makefile.am b/alpine/Makefile.am
new file mode 100644
index 00000000..c80a97ff
--- /dev/null
+++ b/alpine/Makefile.am
@@ -0,0 +1,48 @@
+## Process this file with automake to produce Makefile.in
+## Use aclocal -I m4; automake
+
+# ========================================================================
+# Copyright 2006-2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+SUBDIRS = osdep
+
+bin_PROGRAMS = alpine rpdump rpload
+
+alpine_SOURCES = addrbook.c adrbkcmd.c after.c alpine.c arg.c busy.c colorconf.c \
+ confscroll.c context.c dispfilt.c flagmaint.c folder.c help.c imap.c \
+ init.c kblock.c keymenu.c ldapconf.c listsel.c mailcmd.c mailindx.c \
+ mailpart.c mailview.c newuser.c pattern.c pipe.c print.c radio.c remote.c reply.c \
+ roleconf.c send.c setup.c signal.c status.c takeaddr.c titlebar.c smime.c newmail.c
+
+alpine_LDADD = $(LDADD) $(INTLLIBS)
+
+nodist_alpine_SOURCES = date.c
+
+rpdump_SOURCES = rpdump.c
+
+rpload_SOURCES = rpload.c
+
+BUILT_SOURCES = date.c
+
+LDADD = ../pico/libpico.a ../pico/osdep/libpicoosd.a \
+ ../pith/libpith.a ../pith/osdep/libpithosd.a \
+ ../pith/charconv/libpithcc.a \
+ osdep/libpineosd.a ../c-client/c-client.a
+
+AM_CPPFLAGS = -I@top_builddir@/include -I@top_srcdir@/include -DLOCALEDIR=\"$(localedir)\"
+
+AM_LDFLAGS = `cat @top_srcdir@/c-client/LDFLAGS`
+
+CLEANFILES = date.c
+
+date.c:
+ echo "char datestamp[]="\"`date`\"";" > date.c
+ echo "char hoststamp[]="\"`hostname`\"";" >> date.c
diff --git a/alpine/Makefile.in b/alpine/Makefile.in
new file mode 100644
index 00000000..4bb886ec
--- /dev/null
+++ b/alpine/Makefile.in
@@ -0,0 +1,823 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# ========================================================================
+# Copyright 2006-2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+bin_PROGRAMS = alpine$(EXEEXT) rpdump$(EXEEXT) rpload$(EXEEXT)
+subdir = alpine
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/acx_pthread.m4 \
+ $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
+ $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/VERSION \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/include/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(bindir)"
+PROGRAMS = $(bin_PROGRAMS)
+am_alpine_OBJECTS = addrbook.$(OBJEXT) adrbkcmd.$(OBJEXT) \
+ after.$(OBJEXT) alpine.$(OBJEXT) arg.$(OBJEXT) busy.$(OBJEXT) \
+ colorconf.$(OBJEXT) confscroll.$(OBJEXT) context.$(OBJEXT) \
+ dispfilt.$(OBJEXT) flagmaint.$(OBJEXT) folder.$(OBJEXT) \
+ help.$(OBJEXT) imap.$(OBJEXT) init.$(OBJEXT) kblock.$(OBJEXT) \
+ keymenu.$(OBJEXT) ldapconf.$(OBJEXT) listsel.$(OBJEXT) \
+ mailcmd.$(OBJEXT) mailindx.$(OBJEXT) mailpart.$(OBJEXT) \
+ mailview.$(OBJEXT) newuser.$(OBJEXT) pattern.$(OBJEXT) \
+ pipe.$(OBJEXT) print.$(OBJEXT) radio.$(OBJEXT) \
+ remote.$(OBJEXT) reply.$(OBJEXT) roleconf.$(OBJEXT) \
+ send.$(OBJEXT) setup.$(OBJEXT) signal.$(OBJEXT) \
+ status.$(OBJEXT) takeaddr.$(OBJEXT) titlebar.$(OBJEXT) \
+ smime.$(OBJEXT) newmail.$(OBJEXT)
+nodist_alpine_OBJECTS = date.$(OBJEXT)
+alpine_OBJECTS = $(am_alpine_OBJECTS) $(nodist_alpine_OBJECTS)
+am__DEPENDENCIES_1 =
+alpine_DEPENDENCIES = $(LDADD) $(am__DEPENDENCIES_1)
+am_rpdump_OBJECTS = rpdump.$(OBJEXT)
+rpdump_OBJECTS = $(am_rpdump_OBJECTS)
+rpdump_LDADD = $(LDADD)
+rpdump_DEPENDENCIES = ../pico/libpico.a ../pico/osdep/libpicoosd.a \
+ ../pith/libpith.a ../pith/osdep/libpithosd.a \
+ ../pith/charconv/libpithcc.a osdep/libpineosd.a \
+ ../c-client/c-client.a
+am_rpload_OBJECTS = rpload.$(OBJEXT)
+rpload_OBJECTS = $(am_rpload_OBJECTS)
+rpload_LDADD = $(LDADD)
+rpload_DEPENDENCIES = ../pico/libpico.a ../pico/osdep/libpicoosd.a \
+ ../pith/libpith.a ../pith/osdep/libpithosd.a \
+ ../pith/charconv/libpithcc.a osdep/libpineosd.a \
+ ../c-client/c-client.a
+DEFAULT_INCLUDES =
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+SOURCES = $(alpine_SOURCES) $(nodist_alpine_SOURCES) $(rpdump_SOURCES) \
+ $(rpload_SOURCES)
+DIST_SOURCES = $(alpine_SOURCES) $(rpdump_SOURCES) $(rpload_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
+ html-recursive info-recursive install-data-recursive \
+ install-dvi-recursive install-exec-recursive \
+ install-html-recursive install-info-recursive \
+ install-pdf-recursive install-ps-recursive install-recursive \
+ installcheck-recursive installdirs-recursive pdf-recursive \
+ ps-recursive uninstall-recursive
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \
+ $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \
+ distdir
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_CFLAGS = @AM_CFLAGS@
+AM_LDFLAGS = `cat @top_srcdir@/c-client/LDFLAGS`
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CP = @CP@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+C_CLIENT_CFLAGS = @C_CLIENT_CFLAGS@
+C_CLIENT_GCCOPTLEVEL = @C_CLIENT_GCCOPTLEVEL@
+C_CLIENT_LDFLAGS = @C_CLIENT_LDFLAGS@
+C_CLIENT_SPECIALS = @C_CLIENT_SPECIALS@
+C_CLIENT_TARGET = @C_CLIENT_TARGET@
+C_CLIENT_WITH_IPV6 = @C_CLIENT_WITH_IPV6@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISPELLPROG = @ISPELLPROG@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN = @LN@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKE = @MAKE@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NPA_PROG = @NPA_PROG@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+POSUB = @POSUB@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+PWPROG = @PWPROG@
+RANLIB = @RANLIB@
+REGEX_BUILD = @REGEX_BUILD@
+RM = @RM@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPELLPROG = @SPELLPROG@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEB_BINDIR = @WEB_BINDIR@
+WEB_BUILD = @WEB_BUILD@
+WEB_PUBCOOKIE_BUILD = @WEB_PUBCOOKIE_BUILD@
+WEB_PUBCOOKIE_LIB = @WEB_PUBCOOKIE_LIB@
+WEB_PUBCOOKIE_LINK = @WEB_PUBCOOKIE_LINK@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+acx_pthread_config = @acx_pthread_config@
+alpine_interactive_spellcheck = @alpine_interactive_spellcheck@
+alpine_simple_spellcheck = @alpine_simple_spellcheck@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SUBDIRS = osdep
+alpine_SOURCES = addrbook.c adrbkcmd.c after.c alpine.c arg.c busy.c colorconf.c \
+ confscroll.c context.c dispfilt.c flagmaint.c folder.c help.c imap.c \
+ init.c kblock.c keymenu.c ldapconf.c listsel.c mailcmd.c mailindx.c \
+ mailpart.c mailview.c newuser.c pattern.c pipe.c print.c radio.c remote.c reply.c \
+ roleconf.c send.c setup.c signal.c status.c takeaddr.c titlebar.c smime.c newmail.c
+
+alpine_LDADD = $(LDADD) $(INTLLIBS)
+nodist_alpine_SOURCES = date.c
+rpdump_SOURCES = rpdump.c
+rpload_SOURCES = rpload.c
+BUILT_SOURCES = date.c
+LDADD = ../pico/libpico.a ../pico/osdep/libpicoosd.a \
+ ../pith/libpith.a ../pith/osdep/libpithosd.a \
+ ../pith/charconv/libpithcc.a \
+ osdep/libpineosd.a ../c-client/c-client.a
+
+AM_CPPFLAGS = -I@top_builddir@/include -I@top_srcdir@/include -DLOCALEDIR=\"$(localedir)\"
+CLEANFILES = date.c
+all: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign alpine/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign alpine/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p || test -f $$p1; \
+ then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(bindir)" && rm -f $$files
+
+clean-binPROGRAMS:
+ @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+alpine$(EXEEXT): $(alpine_OBJECTS) $(alpine_DEPENDENCIES)
+ @rm -f alpine$(EXEEXT)
+ $(LINK) $(alpine_OBJECTS) $(alpine_LDADD) $(LIBS)
+rpdump$(EXEEXT): $(rpdump_OBJECTS) $(rpdump_DEPENDENCIES)
+ @rm -f rpdump$(EXEEXT)
+ $(LINK) $(rpdump_OBJECTS) $(rpdump_LDADD) $(LIBS)
+rpload$(EXEEXT): $(rpload_OBJECTS) $(rpload_DEPENDENCIES)
+ @rm -f rpload$(EXEEXT)
+ $(LINK) $(rpload_OBJECTS) $(rpload_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/addrbook.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/adrbkcmd.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/after.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alpine.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/arg.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/busy.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/colorconf.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/confscroll.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/context.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/date.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dispfilt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flagmaint.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/folder.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/help.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/init.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kblock.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/keymenu.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldapconf.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/listsel.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailcmd.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailindx.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailpart.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailview.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/newmail.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/newuser.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pattern.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pipe.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/print.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/radio.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/remote.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/reply.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/roleconf.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpdump.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rpload.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/send.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/setup.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/signal.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/smime.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/status.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/takeaddr.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/titlebar.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run `make' without going through this Makefile.
+# To change the values of `make' variables: instead of editing Makefiles,
+# (1) if the variable is set in `config.status', edit `config.status'
+# (which will cause the Makefiles to be regenerated when you run `make');
+# (2) otherwise, pass the desired values on the `make' command line.
+$(RECURSIVE_TARGETS):
+ @fail= failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+$(RECURSIVE_CLEAN_TARGETS):
+ @fail= failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ rev=''; for subdir in $$list; do \
+ if test "$$subdir" = "."; then :; else \
+ rev="$$subdir $$rev"; \
+ fi; \
+ done; \
+ rev="$$rev ."; \
+ target=`echo $@ | sed s/-recursive//`; \
+ for subdir in $$rev; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done && test -z "$$fail"
+tags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+ done
+ctags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
+ done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) check-recursive
+all-am: Makefile $(PROGRAMS)
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(bindir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+clean: clean-recursive
+
+clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am: install-binPROGRAMS
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS
+
+.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) all check \
+ ctags-recursive install install-am install-strip \
+ tags-recursive
+
+.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \
+ all all-am check check-am clean clean-binPROGRAMS \
+ clean-generic clean-libtool ctags ctags-recursive distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-binPROGRAMS install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs installdirs-am maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-recursive uninstall uninstall-am \
+ uninstall-binPROGRAMS
+
+
+date.c:
+ echo "char datestamp[]="\"`date`\"";" > date.c
+ echo "char hoststamp[]="\"`hostname`\"";" >> date.c
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/alpine/addrbook.c b/alpine/addrbook.c
new file mode 100644
index 00000000..ceb746d5
--- /dev/null
+++ b/alpine/addrbook.c
@@ -0,0 +1,7433 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: addrbook.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2009 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ addrbook.c
+ display, browse and edit the address book.
+ ====*/
+
+
+#include "headers.h"
+#include "addrbook.h"
+#include "adrbkcmd.h"
+#include "mailindx.h"
+#include "mailcmd.h"
+#include "status.h"
+#include "confscroll.h"
+#include "keymenu.h"
+#include "radio.h"
+#include "titlebar.h"
+#include "help.h"
+#include "folder.h"
+#include "signal.h"
+#include "alpine.h"
+#include "../pith/adrbklib.h"
+#include "../pith/abdlc.h"
+#include "../pith/addrbook.h"
+#include "../pith/ablookup.h"
+#include "../pith/addrstring.h"
+#include "../pith/bldaddr.h"
+#include "../pith/state.h"
+#include "../pith/bitmap.h"
+#include "../pith/newmail.h"
+#include "../pith/remote.h"
+#include "../pith/util.h"
+#include "../pith/thread.h"
+#include "../pith/hist.h"
+#include "../pith/list.h"
+
+
+HelpType gAbookHelp = NO_HELP;
+
+
+/* internal prototypes */
+void end_adrbks(void);
+int init_disp_form_prefix(PerAddrBook *, int *);
+void display_book(int, int, int, int, Pos *);
+void paint_line(int, long, int, Pos *);
+void redraw_addr_screen(void);
+char *addr_book(AddrBookArg, char *, char **);
+void ab_zoom(EXPANDED_S *, int *);
+void ab_unzoom(int *);
+void empty_warning(long);
+void clickable_warning(long);
+void no_tabs_warning(void);
+int ab_apply_cmd(struct pine *, AdrBk *, long, int);
+void ab_select(struct pine *, AdrBk *, long, int, int *);
+int ab_select_type(AdrBk *, int);
+int ab_select_text(AdrBk *, int);
+int match_check(AdrBk_Entry *, int, char *);
+void ab_goto_folder(int);
+long ab_whereis(int *, int);
+int any_addrs_avail(long);
+int entry_is_clickable(long);
+int entry_is_clickable_title(long);
+int is_empty(long);
+int entry_is_listent(long);
+int entry_is_addkey(long);
+int entry_is_askserver(long);
+int add_is_global(long);
+int next_selectable_line(long, long *);
+int prev_selectable_line(long, long *);
+void erase_checks(void);
+int search_book(long, int, long *, int *, int *);
+int find_in_book(long, char *, long *, int *);
+int search_in_one_line(AddrScrn_Disp *, AdrBk_Entry *, char *, char *);
+int abook_select_tool(struct pine *, int, CONF_S **, unsigned);
+char *choose_an_address_with_this_prefix(COMPLETE_S *);
+#ifdef _WINDOWS
+int addr_scroll_up(long);
+int addr_scroll_down(long);
+int addr_scroll_to_pos(long);
+int addr_scroll_callback(int, long);
+char *pcpine_help_addrbook(char *);
+#endif
+
+
+/* TRANSLATORS: This is a placeholder for a list of addresses in address book */
+#define CLICKHERE _("[ Address List ]")
+/* TRANSLATORS: This is an empty address list */
+#define EMPTY _("[ Empty ]")
+#define ZOOM_EMPTY _("[ No Selected Entries in this Address Book ]")
+/* TRANSLATORS: Move here means to move the cursor to this line of the screen */
+#define ADD_PERSONAL _(" [ Move here to add a Personal Address Book ]")
+/* TRANSLATORS: A global address book is a shared address book */
+#define ADD_GLOBAL _(" [ Move here to add a Global Address Book ]")
+/* TRANSLATORS: A heading for an address book distribution list */
+#define DISTLIST _("DISTRIBUTION LIST:")
+#define NOABOOKS _("[ No Address Book Configured ]")
+/* TRANSLATORS: Select here means to move the cursor to this line and then type
+ the Select command. */
+#define CLICKHERECMB _("[ Select Here to See Expanded List ]")
+
+
+
+/*
+ * Returns the index of the current address book.
+ */
+int
+cur_addr_book(void)
+{
+ return(adrbk_num_from_lineno(as.top_ent + as.cur_row));
+}
+
+
+/*
+ * Returns 1 if current abook is open, else 0.
+ */
+int
+cur_is_open(void)
+{
+ int current, ret = 0;
+
+ if(as.initialized &&
+ as.n_addrbk > 0 &&
+ (current = cur_addr_book()) >= 0 &&
+ current < as.n_addrbk &&
+ !entry_is_askserver(as.top_ent+as.cur_row))
+ ret = (as.adrbks[current].ostatus == Open);
+
+ return(ret);
+}
+
+
+void
+end_adrbks(void)
+{
+ int i;
+
+ dprint((2, "- end_adrbks -\n"));
+
+ if(!as.initialized)
+ return;
+
+ for(i = 0; i < as.n_addrbk; i++)
+ init_abook(&as.adrbks[i], Closed);
+
+ as.selections = 0;
+ as.checkboxes = 0;
+ ab_nesting_level = 0;
+}
+
+
+
+/*
+ * We have to call this to set up the format of the columns. There is a
+ * separate format for each addrbook, so we need to call this for each
+ * addrbook. We call it when the pab's are built. It also depends on
+ * whether or not as.checkboxes is set, so if we go into a Select mode
+ * from the address book maintenance screen we need to re-call this. Since
+ * we can't go back out of ListMode we don't have that problem. Restore_state
+ * has to call it because of the as.checkboxes possibly being different in
+ * the two states.
+ */
+void
+init_disp_form(PerAddrBook *pab, char **list, int addrbook_num)
+{
+ addrbook_new_disp_form(pab, list, addrbook_num, init_disp_form_prefix);
+}
+
+
+int
+init_disp_form_prefix(PerAddrBook *pab, int *columnp)
+{
+ int rv = 0;
+
+ if(as.checkboxes){
+ pab->disp_form[(*columnp)].wtype = Fixed;
+ pab->disp_form[(*columnp)].req_width = 3;
+ pab->disp_form[(*columnp)++].type = Checkbox;
+ }
+ else if(as.selections){
+ if(F_ON(F_SELECTED_SHOWN_BOLD, ps_global) && StartBold()){
+ EndBold();
+ rv = 1;
+ }
+ else{
+ pab->disp_form[(*columnp)].wtype = Fixed;
+ pab->disp_form[(*columnp)].req_width = 1;
+ pab->disp_form[(*columnp)++].type = Selected;
+ }
+ }
+
+ return(rv);
+}
+
+
+/*
+ * save_and_restore - single interface to save_state and restore_state
+ */
+void
+save_and_restore(int cmd, SAVE_STATE_S *state)
+{
+ switch(cmd){
+ case SAR_SAVE :
+ save_state(state);
+ break;
+ case SAR_RESTORE :
+ restore_state(state);
+ break;
+ }
+}
+
+
+/*
+ * Save the screen state and the Open or Closed status of the addrbooks.
+ */
+void
+save_state(SAVE_STATE_S *state)
+{
+ int i;
+ DL_CACHE_S *dlc;
+
+ dprint((9, "- save_state -\n"));
+
+ /* allocate space for saving the screen structure and save it */
+ state->savep = (AddrScrState *)fs_get(sizeof(AddrScrState));
+ *(state->savep) = as; /* copy the struct */
+
+
+ if(as.initialized){
+ /* allocate space for saving the ostatus for each addrbook */
+ state->stp = (OpenStatus *)fs_get(as.n_addrbk * sizeof(OpenStatus));
+
+ for(i = 0; i < as.n_addrbk; i++)
+ (state->stp)[i] = as.adrbks[i].ostatus;
+
+
+ state->dlc_to_warp_to = (DL_CACHE_S *)fs_get(sizeof(DL_CACHE_S));
+ dlc = get_dlc(as.top_ent + as.cur_row);
+ *(state->dlc_to_warp_to) = *dlc; /* copy the struct */
+ }
+}
+
+
+/*
+ * Restore the state.
+ *
+ * Side effect: Flushes addrbook entry cache entries so they need to be
+ * re-fetched afterwords. This only applies to entries obtained since
+ * the call to save_state.
+ * Also flushes all dlc cache entries, so dlist calls need to be repeated.
+ */
+void
+restore_state(SAVE_STATE_S *state)
+{
+ int i;
+
+ dprint((9, "- restore_state -\n"));
+
+ as = *(state->savep); /* put back cur_row and all that */
+
+ if(as.initialized){
+ /* restore addressbook OpenStatus to what it was before */
+ for(i = 0; i < as.n_addrbk; i++){
+ init_disp_form(&as.adrbks[i], ps_global->VAR_ABOOK_FORMATS, i);
+ init_abook(&as.adrbks[i], (state->stp)[i]);
+ }
+
+ /*
+ * jump cache back to where we were
+ */
+ warp_to_dlc(state->dlc_to_warp_to, as.top_ent+as.cur_row);
+
+ fs_give((void **)&state->dlc_to_warp_to);
+ fs_give((void **)&state->stp);
+ }
+
+ if(state->savep)
+ fs_give((void **)&state->savep);
+}
+
+
+/*
+ * Returns the addrbook entry for this display row.
+ */
+AdrBk_Entry *
+ae(long int row)
+{
+ PerAddrBook *pab;
+ LineType type;
+ AddrScrn_Disp *dl;
+
+ dl = dlist(row);
+ type = dl->type;
+ if(!(type == Simple || type == ListHead ||
+ type == ListEnt || type == ListClickHere))
+ return((AdrBk_Entry *)NULL);
+
+ pab = &as.adrbks[adrbk_num_from_lineno(row)];
+
+ return(adrbk_get_ae(pab->address_book, (a_c_arg_t) dl->elnum));
+}
+
+
+/*
+ * Args: start_disp -- line to start displaying on when redrawing, 0 is
+ * the top_of_screen
+ * cur_line -- current line number (0 is 1st line we display)
+ * old_line -- old line number
+ * redraw -- flag requesting redraw as opposed to update of
+ * current line
+ * start_pos -- return position where highlighted text begins here
+ *
+ * Result: lines painted on the screen
+ *
+ * It either redraws the screen from line "start_disp" down or
+ * moves the cursor from one field to another.
+ */
+void
+display_book(int start_disp, int cur_line, int old_line, int redraw, Pos *start_pos)
+{
+ int screen_row, highlight;
+ long global_row;
+ Pos sp;
+
+ dprint((9,
+ "- display_book() -\n top %d start %d cur_line %d old_line %d redraw %d\n",
+ as.top_ent, start_disp, cur_line, old_line, redraw));
+
+ if(start_pos){
+ start_pos->row = 0;
+ start_pos->col = 0;
+ }
+
+ if(as.l_p_page <= 0)
+ return;
+
+#ifdef _WINDOWS
+ mswin_beginupdate();
+#endif
+ if(redraw){
+ /*--- Repaint all of the screen or bottom part of screen ---*/
+ global_row = as.top_ent + start_disp;
+ for(screen_row = start_disp;
+ screen_row < as.l_p_page;
+ screen_row++, global_row++){
+
+ highlight = (screen_row == cur_line);
+ ClearLine(screen_row + HEADER_ROWS(ps_global));
+ paint_line(screen_row + HEADER_ROWS(ps_global), global_row,
+ highlight, &sp);
+ if(start_pos && highlight)
+ *start_pos = sp;
+ }
+ }
+ else{
+
+ /*--- Only update current, or move the cursor ---*/
+ if(cur_line != old_line){
+
+ /*--- Repaint old position to erase "cursor" ---*/
+ paint_line(old_line + HEADER_ROWS(ps_global), as.top_ent + old_line,
+ 0, &sp);
+ }
+
+ /*--- paint the position with the cursor ---*/
+ paint_line(cur_line + HEADER_ROWS(ps_global), as.top_ent + cur_line,
+ 1, &sp);
+ if(start_pos)
+ *start_pos = sp;
+ }
+
+#ifdef _WINDOWS
+ scroll_setpos(as.top_ent);
+ mswin_endupdate();
+#endif
+ fflush(stdout);
+}
+
+
+/*
+ * Paint a line on the screen
+ *
+ * Args: line -- Line on screen to paint
+ * global_row -- Row number of line to paint
+ * highlight -- Line should be highlighted
+ * start_pos -- return position where text begins here
+ *
+ * Result: Line is painted
+ *
+ * The three field widths for the formatting are passed in. There is an
+ * implicit 2 spaces between the fields.
+ *
+ * | fld_width[0] chars |__| fld_width[1] |__| fld_width[2] | ...
+ */
+void
+paint_line(int line, long int global_row, int highlight, Pos *start_pos)
+{
+ int scol, bolden = 0;
+ char *start_hilite_here, *end_hilite_here;
+ AddrScrn_Disp *dl;
+ char lbuf[6*MAX_SCREEN_COLS + 1];
+ register char *p;
+
+ dprint((10, "- paint_line(%d, %d) -\n", line, highlight));
+
+ dl = dlist(global_row);
+ start_pos->row = line;
+ start_pos->col = 0; /* default */
+
+ switch(dl->type){
+ case Beginning:
+ case End:
+ return;
+
+ default:
+ break;
+ }
+
+ p = get_abook_display_line(global_row, line == HEADER_ROWS(ps_global),
+ highlight ? &start_hilite_here : NULL,
+ highlight ? &end_hilite_here : NULL,
+ &scol, lbuf, sizeof(lbuf));
+
+ if(as.selections &&
+ as.do_bold &&
+ (dl->type == ListHead || dl->type == Simple)){
+ PerAddrBook *pab;
+
+ pab = &as.adrbks[adrbk_num_from_lineno(global_row)];
+ if(entry_is_selected(pab->address_book->selects, (a_c_arg_t)dl->elnum)){
+ bolden++;
+ StartBold();
+ }
+ }
+
+ if(p && *p){
+ if(highlight){
+ char save_char;
+
+ MoveCursor(line, 0);
+
+ /*
+ * print part before highlight starts
+ */
+ if(start_hilite_here != NULL){
+ save_char = *start_hilite_here;
+ *start_hilite_here = '\0';
+ Write_to_screen(p);
+ *start_hilite_here = save_char;
+ }
+
+ /*
+ * print highlighted part
+ */
+
+ if(end_hilite_here != NULL){
+ save_char = *end_hilite_here;
+ *end_hilite_here = '\0';
+ }
+
+ StartInverse();
+ Write_to_screen(start_hilite_here ? start_hilite_here : p);
+ EndInverse();
+
+ /*
+ * print part after highlight ends
+ */
+ if(end_hilite_here != NULL){
+ *end_hilite_here = save_char;
+ Write_to_screen(end_hilite_here);
+ }
+ }
+ else
+ PutLine0(line, 0, p);
+ }
+
+ if(bolden)
+ EndBold();
+
+ if(scol > 0)
+ start_pos->col = scol;
+}
+
+
+/*
+ * Assemble a line suitable for displaying on screen
+ *
+ * Args: global_row -- Row number of line to assemble.
+ * continuation -- This is the top line of screen display
+ * s_hilite -- Location in the returned line where highlight
+ * should start, if desired
+ * e_hilite -- Location in the returned line where highlight
+ * should end, if desired
+ * retcol -- Return column where text begins here.
+ * lbuf -- Put the output here. Lbuf should be at least
+ * size 6*screen_cols+1.
+ * lbufsize -- Size of lbuf array
+ *
+ * Result: Pointer to lbuf is returned.
+ *
+ * There is an implicit 2 spaces between the fields.
+ *
+ * | fld_width[0] chars |__| fld_width[1] |__| fld_width[2] | ...
+ */
+char *
+get_abook_display_line(long int global_row, int continuation, char **s_hilite,
+ char **e_hilite, int *retcol, char *lbuf, size_t lbufsize)
+{
+ int fld_col[NFIELDS],
+ fld_width[NFIELDS],
+ screen_width, width,
+ col, special_col = 0,
+ width_consumed,
+ scol = -1,
+ fld;
+ char *string, *writeptr;
+ char special[6*MAX_SCREEN_COLS-1];
+ AddrScrn_Disp *dl;
+ AdrBk_Entry *abe;
+ PerAddrBook *pab;
+#define LSPACE() (lbufsize - (writeptr - lbuf) - 1)
+
+ dprint((10, "- get_display_line(%d) -\n", global_row));
+
+ dl = dlist(global_row);
+ if(retcol)
+ *retcol = 0; /* default */
+
+ if(s_hilite){
+ *s_hilite = NULL; /* NULL in these means to hilight whole line */
+ *e_hilite = NULL;
+ }
+
+ writeptr = lbuf;
+ memset(writeptr, 0, lbufsize);
+ memset(special, 0, sizeof(special));
+
+ switch(dl->type){
+ case Beginning:
+ case End:
+ return lbuf;
+
+ default:
+ break;
+ }
+
+ screen_width = ps_global->ttyo->screen_cols;
+
+ /* the types in this set span all columns */
+ switch(dl->type){
+ /* center these */
+ case Text:
+ case Title:
+ case TitleCmb:
+ case ClickHereCmb:
+ case Empty:
+ case ZoomEmpty:
+ case NoAbooks:
+ if(dl->type == Empty)
+ string = EMPTY;
+ else if(dl->type == ZoomEmpty)
+ string = ZOOM_EMPTY;
+ else if(dl->type == NoAbooks)
+ string = NOABOOKS;
+ else if(dl->type == ClickHereCmb)
+ string = CLICKHERECMB;
+ else
+ string = dl->usst;
+
+ /* center it */
+ col = (screen_width - (int) utf8_width(string))/2;
+ col = MAX(col, 0);
+ width_consumed = 0;
+
+ /* col spaces to start */
+ if(col > 0 && LSPACE() >= col){
+ memset(writeptr, ' ', col);
+ writeptr += col;
+ width_consumed += col;
+ }
+
+ if((width=utf8_width(string)) <= screen_width-col){
+ strncpy(writeptr, string, LSPACE());
+ width_consumed += width;
+ }
+ else{
+ utf8_pad_to_width(writeptr, string, LSPACE(), screen_width-col, 1);
+ width_consumed = screen_width;
+ }
+
+ if(s_hilite && *s_hilite == NULL){
+ *s_hilite = writeptr;
+ *e_hilite = writeptr + strlen(writeptr);
+ }
+
+ writeptr += strlen(writeptr);
+ if(width_consumed < screen_width)
+ memset(writeptr, ' ', screen_width-width_consumed);
+
+ if(retcol)
+ *retcol = col;
+
+ return lbuf;
+
+ /* left adjust these */
+ case AddFirstPers:
+ case AddFirstGlob:
+ case AskServer:
+ if(dl->type == AddFirstPers)
+ string = ADD_PERSONAL;
+ else if(dl->type == AddFirstGlob)
+ string = ADD_GLOBAL;
+ else
+ string = dl->usst;
+
+ /* left adjust it */
+ col = 0;
+ width_consumed = 0;
+ if((width=utf8_width(string)) <= screen_width){
+ strncpy(writeptr, string, LSPACE());
+ width_consumed += width;
+ }
+ else{
+ utf8_pad_to_width(writeptr, string, LSPACE(), screen_width, 1);
+ width_consumed = screen_width;
+ }
+
+ if(s_hilite && *s_hilite == NULL){
+ *s_hilite = writeptr;
+ *e_hilite = writeptr + strlen(writeptr);
+ }
+
+ writeptr += strlen(writeptr);
+ if(width_consumed < screen_width)
+ memset(writeptr, ' ', screen_width-width_consumed);
+
+ if(retcol)
+ *retcol = col;
+
+ return lbuf;
+
+ default:
+ break;
+ }
+
+ pab = &as.adrbks[adrbk_num_from_lineno(global_row)];
+ for(fld = 0; fld < NFIELDS; fld++)
+ fld_width[fld] = pab->disp_form[fld].width;
+
+ fld_col[0] = 0;
+ for(fld = 1; fld < NFIELDS; fld++)
+ fld_col[fld] = MIN(fld_col[fld-1]+fld_width[fld-1]+2, screen_width);
+
+ width_consumed = 0;
+
+ /* fill in the fields */
+ for(fld = 0; fld < NFIELDS; fld++){
+ if(fld_width[fld] == 0
+ && !(pab->disp_form[fld].type == Addr
+ && (dl->type == ListClickHere || dl->type == ListEmpty)))
+ continue;
+
+ switch(pab->disp_form[fld].type){
+ case Notused:
+ break;
+
+ case Nickname:
+ switch(dl->type){
+ case Simple:
+ case ListHead:
+ abe = ae(global_row);
+ string = (abe && abe->nickname) ? abe->nickname : "";
+ if(scol == -1)
+ scol = fld_col[fld];
+
+ /* left adjust string in field */
+ utf8_pad_to_width(writeptr, string, LSPACE(), fld_width[fld], 1);
+ writeptr += strlen(writeptr);
+ width_consumed += fld_width[fld];
+ break;
+
+ default:
+ break;
+ }
+
+ break;
+
+ case Fullname:
+ switch(dl->type){
+ case Simple:
+ case ListHead:
+ abe = ae(global_row);
+ string = (abe && abe->fullname) ? abe->fullname : "";
+ if(scol == -1)
+ scol = fld_col[fld];
+
+ /* left adjust string in field */
+ utf8_pad_to_width(writeptr, string, LSPACE(), fld_width[fld], 1);
+ writeptr += strlen(writeptr);
+ width_consumed += fld_width[fld];
+ break;
+
+ case ListEnt:
+ /* continuation line */
+ if(continuation){
+ int width1, width2;
+
+ width2 = MIN(11, fld_width[fld]);
+ width1 = MAX(fld_width[fld] - width2 - 1, 0);
+
+ abe = ae(global_row);
+ string = (abe && abe->fullname) ? abe->fullname : "";
+
+ if(width1){
+ utf8_pad_to_width(writeptr, string, LSPACE(), width1, 1);
+ writeptr += strlen(writeptr);
+ if(LSPACE() > 0)
+ *writeptr++ = ' ';
+ }
+
+ /* TRANSLATORS: continuation line meaning there is more on the next page */
+ if(width2 && LSPACE() >= strlen(_("(continued)"))){
+ strncpy(writeptr, _("(continued)"), width2);
+ lbuf[lbufsize-1] = '\0';
+ writeptr += strlen(writeptr);
+ }
+
+ width_consumed += fld_width[fld];
+
+ lbuf[lbufsize-1] = '\0';
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ break;
+
+ case Addr:
+ switch(dl->type){
+ case ListClickHere:
+ case ListEmpty:
+ if(dl->type == ListClickHere)
+ string = CLICKHERE;
+ else
+ string = EMPTY;
+
+ if((width=utf8_width(string)) <= fld_width[fld]){
+
+ strncpy(writeptr, string, LSPACE());
+
+ if(s_hilite && *s_hilite == NULL){
+ *s_hilite = writeptr;
+ *e_hilite = writeptr + strlen(writeptr);
+ }
+
+ writeptr += strlen(writeptr);
+ width_consumed += width;
+
+ if(scol == -1)
+ scol = fld_col[fld];
+ }
+ else{
+ /*
+ * Place the string in special array and overlay it
+ * onto the right edge of the screen at the end.
+ */
+ if(width <= screen_width){
+ strncpy(special, string, sizeof(special));
+ special_col = screen_width - width;
+ }
+ else{
+ utf8_pad_to_width(special, string, sizeof(special), screen_width, 1);
+ special_col = 0;
+ }
+
+ special[sizeof(special)-1] = '\0';
+ }
+
+ break;
+
+ case ListHead:
+ if(scol == -1)
+ scol = fld_col[fld];
+
+ string = DISTLIST;
+ utf8_pad_to_width(writeptr, string, LSPACE(), fld_width[fld], 1);
+ writeptr += strlen(writeptr);
+ width_consumed += fld_width[fld];
+ break;
+
+ case Simple:
+ abe = ae(global_row);
+#ifdef ENABLE_LDAP
+ if(abe && abe->addr.addr &&
+ !strncmp(abe->addr.addr, QRUN_LDAP, LEN_QRL))
+ string = LDAP_DISP;
+ else
+#endif
+ string = (abe && abe->tag == Single && abe->addr.addr) ?
+ abe->addr.addr : "";
+ if(scol == -1)
+ scol = fld_col[fld];
+
+ utf8_pad_to_width(writeptr, string, LSPACE(), fld_width[fld], 1);
+ writeptr += strlen(writeptr);
+ width_consumed += fld_width[fld];
+ break;
+
+ case ListEnt:
+ string = listmem(global_row) ? listmem(global_row) : "";
+ if(scol == -1)
+ scol = fld_col[fld];
+
+ utf8_pad_to_width(writeptr, string, LSPACE(), fld_width[fld], 1);
+
+ if(s_hilite && *s_hilite == NULL){
+ *s_hilite = writeptr;
+ *e_hilite = writeptr + strlen(writeptr);
+ }
+
+ writeptr += strlen(writeptr);
+ width_consumed += fld_width[fld];
+
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case Filecopy:
+ case Comment:
+ switch(dl->type){
+ case Simple:
+ case ListHead:
+ abe = ae(global_row);
+ if(pab->disp_form[fld].type == Filecopy)
+ string = (abe && abe->fcc) ? abe->fcc : "";
+ else
+ string = (abe && abe->extra) ? abe->extra : "";
+
+ if(scol == -1)
+ scol = fld_col[fld];
+
+ utf8_pad_to_width(writeptr, string, LSPACE(), fld_width[fld], 1);
+ writeptr += strlen(writeptr);
+ width_consumed += fld_width[fld];
+ break;
+
+ default:
+ break;
+ }
+
+ break;
+
+ case Checkbox:
+ switch(dl->type){
+ case Simple:
+ case ListHead:
+ if(entry_is_checked(pab->address_book->checks,
+ (a_c_arg_t)dl->elnum))
+ string = "[X]";
+ else
+ string = "[ ]";
+
+ if(scol == -1)
+ scol = fld_col[fld];
+
+ utf8_pad_to_width(writeptr, string, LSPACE(), fld_width[fld], 1);
+ writeptr += strlen(writeptr);
+ width_consumed += fld_width[fld];
+ break;
+
+ default:
+ break;
+ }
+
+ break;
+
+ case Selected:
+ switch(dl->type){
+ case Simple:
+ case ListHead:
+ if(entry_is_selected(pab->address_book->selects,
+ (a_c_arg_t)dl->elnum))
+ string = "X";
+ else
+ string = " ";
+
+ if(scol == -1)
+ scol = fld_col[fld];
+
+ utf8_pad_to_width(writeptr, string, LSPACE(), fld_width[fld], 1);
+ writeptr += strlen(writeptr);
+ width_consumed += fld_width[fld];
+ break;
+
+ default:
+ break;
+ }
+
+ break;
+
+ case WhenNoAddrDisplayed:
+ switch(dl->type){
+ case ListClickHere:
+ case ListEmpty:
+ case ListEnt:
+ if(dl->type == ListClickHere)
+ string = CLICKHERE;
+ else if(dl->type == ListEmpty)
+ string = EMPTY;
+ else
+ string = listmem(global_row) ? listmem(global_row) : "";
+
+ if((width=utf8_width(string)) <= fld_width[fld])
+ strncpy(special, string, sizeof(special));
+ else
+ utf8_pad_to_width(special, string, sizeof(special), fld_width[fld], 1);
+
+ special[sizeof(special)-1] = '\0';
+ special_col = screen_width - fld_width[fld];
+ special_col = MAX(0, special_col);
+ break;
+
+ default:
+ break;
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ if(fld < NFIELDS-1)
+ while(width_consumed < fld_col[fld+1] && LSPACE() > 0){
+ *writeptr++ = ' ';
+ width_consumed++;
+ }
+ }
+
+ /*
+ * Right adjust the special string in lbuf, writing over
+ * anything in our way.
+ */
+ if(special[0]){
+ unsigned got_width;
+
+ writeptr = utf8_count_forw_width(lbuf, special_col, &got_width);
+
+ strncpy(writeptr, special, LSPACE());
+
+ if(scol == -1)
+ scol = got_width;
+
+ if(s_hilite && *s_hilite == NULL){
+ *s_hilite = writeptr;
+ *e_hilite = writeptr + strlen(writeptr);
+ }
+
+ writeptr += strlen(writeptr);
+
+ width_consumed = (int) utf8_width(lbuf);
+ while(width_consumed++ < screen_width && LSPACE() > 0)
+ *writeptr++ = ' ';
+
+ if(LSPACE() > 0)
+ *writeptr = '\0';
+ }
+
+ if(scol > 0 && retcol)
+ *retcol = scol;
+
+ lbuf[lbufsize-1] = '\0';
+ return lbuf;
+}
+
+
+/*
+ * Set field widths for the columns of the display. The idea is to
+ * try to come up with something that works pretty well. Getting it just
+ * right isn't important.
+ *
+ * Col1 and col2 are arrays which contain some widths useful for
+ * formatting the screen. The 0th element is the max
+ * width in that column. The 1st element is the max of the third largest
+ * width in each addressbook (yup, strange).
+ *
+ * The info above applies to the default case (AllAuto). The newer methods
+ * where the user specifies the format is the else part of the big if and
+ * is quite a bit different. It's all sort of ad hoc when we're asked
+ * to calculate one of the fields for the user.
+ *
+ * Returns non-zero if the widths changed since the last time called.
+ */
+int
+calculate_field_widths(void)
+{
+ int space_left, i, j, screen_width;
+ int ret = 0;
+ PerAddrBook *pab;
+ WIDTH_INFO_S *widths;
+ int max_nick, max_full, max_addr, third_full, third_addr, third_fcc;
+ int col1[5], col2[5];
+ int nick = -1, full = -1, addr = -1;
+#define SEP 2 /* space between columns */
+
+ dprint((9, "- calculate_field_widths -\n"));
+
+ screen_width = ps_global->ttyo->screen_cols;
+
+ /* calculate widths for each addrbook independently */
+ for(j = 0; j < as.n_addrbk; j++){
+ pab = &as.adrbks[j];
+
+ max_nick = 0;
+ max_full = 2;
+ max_addr = 2;
+ third_full = 2;
+ third_addr = 2;
+ third_fcc = 2;
+
+ if(pab->address_book){
+ widths = &pab->address_book->widths;
+ max_nick = MIN(MAX(max_nick, widths->max_nickname_width), 25);
+ max_full = MAX(max_full, widths->max_fullname_width);
+ max_addr = MAX(max_addr, widths->max_addrfield_width);
+ third_full = MAX(third_full, widths->third_biggest_fullname_width);
+ if(third_full == 2)
+ third_full = MAX(third_full, 2*max_full/3);
+
+ third_addr = MAX(third_addr, widths->third_biggest_addrfield_width);
+ if(third_addr == 2)
+ third_addr = MAX(third_addr, 2*max_addr/3);
+
+ third_fcc = MAX(third_fcc, widths->third_biggest_fccfield_width);
+ if(third_fcc == 2)
+ third_fcc = MAX(third_fcc, 2*widths->max_fccfield_width/3);
+ }
+
+ /* figure out which order they're in and reset widths */
+ for(i = 0; i < NFIELDS; i++){
+ pab->disp_form[i].width = 0;
+ switch(pab->disp_form[i].type){
+ case Nickname:
+ nick = i;
+ break;
+
+ case Fullname:
+ full = i;
+ break;
+
+ case Addr:
+ addr = i;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /* Compute default format */
+ if(pab->disp_form[1].wtype == AllAuto){
+
+ col1[0] = max_full;
+ col2[0] = max_addr;
+ col1[1] = third_full;
+ col2[1] = third_addr;
+ col1[2] = 3;
+ col2[2] = 3;
+ col1[3] = 2;
+ col2[3] = 2;
+ col1[4] = 1;
+ col2[4] = 1;
+
+ space_left = screen_width;
+
+ if(pab->disp_form[0].type == Selected ||
+ pab->disp_form[0].type == Checkbox){
+ pab->disp_form[0].width = MIN(pab->disp_form[0].req_width,space_left);
+ space_left = MAX(space_left-(pab->disp_form[0].width + SEP), 0);
+ }
+
+ /*
+ * All of the nickname field should be visible,
+ * and make it at least 3.
+ */
+ pab->disp_form[nick].width = MIN(MAX(max_nick, 3), space_left);
+
+ /*
+ * The SEP is for two blank columns between nickname and next field.
+ * Those blank columns are taken automatically in paint_line().
+ */
+ space_left -= (pab->disp_form[nick].width + SEP);
+
+ if(space_left > 0){
+ for(i = 0; i < 5; i++){
+ /* try fitting most of each field in if possible */
+ if(col1[i] + SEP + col2[i] <= space_left){
+ int extra;
+
+ extra = space_left - col1[i] - SEP - col2[i];
+ /*
+ * try to stabilize nickname column shifts
+ * so that screen doesn't jump around when we make changes
+ */
+ if(i == 0 && pab->disp_form[nick].width < 7 &&
+ extra >= (7 - pab->disp_form[nick].width)){
+ extra -= (7 - pab->disp_form[nick].width);
+ space_left -= (7 - pab->disp_form[nick].width);
+ pab->disp_form[nick].width = 7;
+ }
+
+ pab->disp_form[addr].width = col2[i] + extra/2;
+ pab->disp_form[full].width =
+ space_left - SEP - pab->disp_form[addr].width;
+ break;
+ }
+ }
+
+ /*
+ * None of them would fit. Toss addr field.
+ */
+ if(i == 5){
+ pab->disp_form[full].width = space_left;
+ pab->disp_form[addr].width = 0;
+ }
+ }
+ else{
+ pab->disp_form[full].width = 0;
+ pab->disp_form[addr].width = 0;
+ }
+
+ dprint((10, "Using %s choice: %d %d %d", enth_string(i+1),
+ pab->disp_form[nick].width, pab->disp_form[full].width,
+ pab->disp_form[addr].width));
+ }
+ else{ /* non-default case */
+ int some_to_calculate = 0;
+ int columns = 0;
+ int used = 0;
+ int avail_screen;
+ int all_percents = 1;
+ int pc_tot;
+
+ /*
+ * First count how many fields there are.
+ * Fill in all the Fixed's while we're at it.
+ */
+ for(i = 0; i < NFIELDS && pab->disp_form[i].type != Notused; i++){
+ if(pab->disp_form[i].wtype == Fixed){
+ pab->disp_form[i].width = pab->disp_form[i].req_width;
+ all_percents = 0;
+ }
+ else if(pab->disp_form[i].wtype == WeCalculate){
+ pab->disp_form[i].width = pab->disp_form[i].req_width; /* for now */
+ some_to_calculate++;
+ all_percents = 0;
+ }
+
+ if(pab->disp_form[i].wtype != Special){
+ used += pab->disp_form[i].width;
+ columns++;
+ }
+ }
+
+ used += ((columns-1) * SEP);
+ avail_screen = screen_width - used;
+
+ /*
+ * Now that we know how much space we've got, we can
+ * calculate the Percent columns.
+ */
+ if(avail_screen > 0){
+ for(i = 0; i < NFIELDS && pab->disp_form[i].type != Notused; i++){
+ if(pab->disp_form[i].wtype == Percent){
+ /* The 2, 200, and +100 are because we're rounding */
+ pab->disp_form[i].width =
+ ((2*pab->disp_form[i].req_width*avail_screen)+100) / 200;
+ used += pab->disp_form[i].width;
+ }
+ }
+ }
+
+ space_left = screen_width - used;
+
+ if(space_left < 0){
+ /*
+ * If they're all percentages, and the percentages add up to 100,
+ * then we should fix the rounding problem.
+ */
+ pc_tot = 0;
+ if(all_percents){
+ for(i = 0; i < NFIELDS && pab->disp_form[i].type != Notused; i++)
+ if(pab->disp_form[i].wtype == Percent)
+ pc_tot += pab->disp_form[i].req_width;
+ }
+
+ /* fix the rounding problem */
+ if(all_percents && pc_tot <= 100){
+ int col = columns;
+ int this_col = 0;
+ int fix = used - screen_width;
+
+ while(fix--){
+ if(col < 0)
+ col = columns;
+
+ /* find col'th column */
+ for(i=0, this_col=0; i < NFIELDS; i++){
+ if(pab->disp_form[i].wtype == Percent){
+ if(col == ++this_col)
+ break;
+ }
+ }
+
+ pab->disp_form[i].width--;
+ col--;
+ }
+ }
+ /*
+ * Assume they meant to have them add up to over 100%, so we
+ * just truncate the right hand edge.
+ */
+ else{
+ int this_fix, space_over;
+
+ /* have to reduce space_over down to zero. */
+ space_over = used - screen_width;
+ for(i=NFIELDS-1; i >= 0 && space_over > 0; i--){
+ if(pab->disp_form[i].type != Notused){
+ this_fix = MIN(pab->disp_form[i].width, space_over);
+ pab->disp_form[i].width -= this_fix;
+ space_over -= this_fix;
+ }
+ }
+ }
+ }
+ else if(space_left > 0){
+ if(some_to_calculate){
+ /* make nickname big enough to show all nicknames */
+ if(nick >= 0 && pab->disp_form[nick].wtype == WeCalculate){
+ --some_to_calculate;
+ if(pab->disp_form[nick].width != max_nick){
+ int this_fix;
+
+ this_fix = MIN(max_nick-pab->disp_form[nick].width, space_left);
+ pab->disp_form[nick].width += this_fix;
+ space_left -= this_fix;
+ }
+ }
+
+ if(!some_to_calculate && space_left > 0)
+ goto none_to_calculate;
+
+ if(space_left > 0){
+ int weight = 0;
+ int used_wt = 0;
+
+ /* add up total weight */
+ for(i = 0; i < NFIELDS && pab->disp_form[i].type != Notused; i++){
+ if(i != nick && pab->disp_form[i].wtype == WeCalculate){
+ switch(pab->disp_form[i].type){
+ case Fullname:
+ weight += MAX(third_full, pab->disp_form[i].width);
+ used_wt += pab->disp_form[i].width;
+ break;
+
+ case Addr:
+ weight += MAX(third_addr, pab->disp_form[i].width);
+ used_wt += pab->disp_form[i].width;
+ break;
+
+ case Filecopy:
+ weight += MAX(third_fcc, pab->disp_form[i].width);
+ used_wt += pab->disp_form[i].width;
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ if(weight > 0){
+ int this_fix;
+
+ if(weight - used_wt <= space_left){
+ for(i = 0;
+ i < NFIELDS && pab->disp_form[i].type != Notused;
+ i++){
+ if(i != nick && pab->disp_form[i].wtype == WeCalculate){
+ switch(pab->disp_form[i].type){
+ case Fullname:
+ this_fix = third_full - pab->disp_form[i].width;
+ space_left -= this_fix;
+ pab->disp_form[i].width += this_fix;
+ break;
+
+ case Addr:
+ this_fix = third_addr - pab->disp_form[i].width;
+ space_left -= this_fix;
+ pab->disp_form[i].width += this_fix;
+ break;
+
+ case Filecopy:
+ this_fix = third_fcc - pab->disp_form[i].width;
+ space_left -= this_fix;
+ pab->disp_form[i].width += this_fix;
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ /* if still space left and a comment field, all to comment */
+ if(space_left){
+ for(i = 0;
+ i < NFIELDS && pab->disp_form[i].type != Notused;
+ i++){
+ if(pab->disp_form[i].type == Comment &&
+ pab->disp_form[i].wtype == WeCalculate){
+ pab->disp_form[i].width += space_left;
+ space_left = 0;
+ }
+ }
+ }
+ }
+ else{ /* not enough space, dole out weighted pieces */
+ int was_sl = space_left;
+
+ for(i = 0;
+ i < NFIELDS && pab->disp_form[i].type != Notused;
+ i++){
+ if(i != nick && pab->disp_form[i].wtype == WeCalculate){
+ switch(pab->disp_form[i].type){
+ case Fullname:
+ /* round down */
+ this_fix = (third_full * was_sl)/weight;
+ space_left -= this_fix;
+ pab->disp_form[i].width += this_fix;
+ break;
+
+ case Addr:
+ this_fix = (third_addr * was_sl)/weight;
+ space_left -= this_fix;
+ pab->disp_form[i].width += this_fix;
+ break;
+
+ case Filecopy:
+ this_fix = (third_fcc * was_sl)/weight;
+ space_left -= this_fix;
+ pab->disp_form[i].width += this_fix;
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* give out rest */
+ while(space_left > 0){
+ for(i=NFIELDS-1; i >= 0 && space_left > 0; i--){
+ if(i != nick && pab->disp_form[i].wtype == WeCalculate){
+ pab->disp_form[i].width++;
+ space_left--;
+ }
+ }
+ }
+ }
+ }
+ else{
+ /*
+ * If they're all percentages, and the percentages add up to 100,
+ * then we just have to fix a rounding problem. Otherwise, we'll
+ * assume the user meant to have them add up to less than 100.
+ */
+none_to_calculate:
+ pc_tot = 0;
+ if(all_percents){
+ for(i = 0; i < NFIELDS && pab->disp_form[i].type != Notused; i++)
+ if(pab->disp_form[i].wtype == Percent)
+ pc_tot += pab->disp_form[i].req_width;
+ }
+
+ if(all_percents && pc_tot >= 100){
+ int col = columns;
+ int this_col = 0;
+ int fix = screen_width - used;
+
+ while(fix--){
+ if(col < 0)
+ col = columns;
+
+ /* find col'th column */
+ for(i=0, this_col=0; i < NFIELDS; i++){
+ if(pab->disp_form[i].wtype == Percent){
+ if(col == ++this_col)
+ break;
+ }
+ }
+
+ pab->disp_form[i].width++;
+ col--;
+ }
+ }
+ /* else, user specified less than 100%, leave it */
+ }
+ }
+ /* else space_left == zero, nothing to do */
+
+ /*
+ * Check for special case. If we find it, this is the case where
+ * we want to display the list entry field even though there is no
+ * address field displayed. All of the display width is probably
+ * used up by now, so we just need to pick some arbitrary width
+ * for these lines. Since these lines are separate from the other
+ * lines we've been calculating, we don't have to worry about running
+ * into them, except for list continuation lines which we're not
+ * going to worry about.
+ */
+ for(i = 0; i < NFIELDS && pab->disp_form[i].type != Notused; i++){
+ if(pab->disp_form[i].wtype == Special){
+ pab->disp_form[i].width = MIN(utf8_width(CLICKHERE), screen_width);
+ break;
+ }
+ }
+ }
+
+ /* check for width changes */
+ for(i = 0; i < NFIELDS && pab->disp_form[i].type != Notused; i++){
+ if(pab->disp_form[i].width != pab->disp_form[i].old_width){
+ ret++; /* Tell the caller the screen changed */
+ pab->disp_form[i].old_width = pab->disp_form[i].width;
+ }
+ }
+
+ pab->nick_is_displayed = 0;
+ pab->full_is_displayed = 0;
+ pab->addr_is_displayed = 0;
+ pab->fcc_is_displayed = 0;
+ pab->comment_is_displayed = 0;
+ for(i = 0; i < NFIELDS && pab->disp_form[i].type != Notused; i++){
+ if(pab->disp_form[i].width > 0){
+ switch(pab->disp_form[i].type){
+ case Nickname:
+ pab->nick_is_displayed++;
+ break;
+ case Fullname:
+ pab->full_is_displayed++;
+ break;
+ case Addr:
+ pab->addr_is_displayed++;
+ break;
+ case Filecopy:
+ pab->fcc_is_displayed++;
+ break;
+ case Comment:
+ pab->comment_is_displayed++;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ if(ret)
+ dprint((9, " some widths changed\n"));
+
+ return(ret);
+}
+
+
+void
+redraw_addr_screen(void)
+{
+ dprint((7, "- redraw_addr_screen -\n"));
+
+ ab_resize();
+ if(as.l_p_page <= 0)
+ return;
+
+ (void)calculate_field_widths();
+ display_book(0, as.cur_row, -1, 1, (Pos *)NULL);
+}
+
+
+/*
+ * Little front end for address book screen so it can be called out
+ * of the main command loop in alpine.c
+ */
+void
+addr_book_screen(struct pine *pine_state)
+{
+ dprint((3, "\n\n --- ADDR_BOOK_SCREEN ---\n\n"));
+
+ mailcap_free(); /* free resources we won't be using for a while */
+
+ if(setjmp(addrbook_changed_unexpectedly)){
+ /* TRANSLATORS: a warning message telling the user that the address book
+ is being reset (re-sychronized, restarted) */
+ q_status_message(SM_ORDER, 5, 10, _("Resetting address book..."));
+ dprint((1, "RESETTING address book... addr_book_screen!\n"));
+ addrbook_reset();
+ }
+
+ ab_nesting_level = 1; /* come here only from main menu */
+
+ /* TRANSLATORS: a screen title for address book screen. Almost all of
+ the translatable strings which are all upper case are screen titles. */
+ (void)addr_book(AddrBookScreen, _("ADDRESS BOOK"), NULL);
+ end_adrbks();
+
+ pine_state->prev_screen = addr_book_screen;
+}
+
+
+void
+addr_book_config(struct pine *pine_state, int edit_exceptions)
+{
+ if(edit_exceptions){
+ q_status_message(SM_ORDER, 3, 7,
+ _("Exception Setup not implemented for address books"));
+ return;
+ }
+
+ dprint((3, "\n\n --- ADDR_BOOK_CONFIG ---\n\n"));
+
+ mailcap_free(); /* free resources we won't be using for a while */
+
+ if(setjmp(addrbook_changed_unexpectedly)){
+ q_status_message(SM_ORDER, 5, 10, _("Resetting address book..."));
+ dprint((1, "RESETTING address book... addr_book_config!\n"));
+ addrbook_reset();
+ }
+
+ ab_nesting_level = 1;
+
+ /* TRANSLATORS: A screen title */
+ (void)addr_book(AddrBookConfig, _("SETUP ADDRESS BOOKS"), NULL);
+ end_adrbks();
+
+ pine_state->prev_screen = addr_book_screen;
+}
+
+
+/*
+ * Return a single address
+ *
+ * Returns: pointer to returned address, or NULL if nothing returned
+ */
+char *
+addr_book_oneaddr(void)
+{
+ char *p;
+ jmp_buf save_jmp_buf;
+ int *save_nesting_level;
+
+ dprint((3, "--- addr_book_oneaddr ---\n"));
+
+ save_nesting_level = cpyint(ab_nesting_level);
+ memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
+ if(setjmp(addrbook_changed_unexpectedly)){
+ q_status_message(SM_ORDER, 5, 10, _("Resetting address book..."));
+ dprint((1, "RESETTING address book... addr_book_oneaddr!\n"));
+ addrbook_reset();
+ ab_nesting_level = *save_nesting_level;
+ }
+
+ ab_nesting_level++;
+
+ /* TRANSLATORS: a screen title */
+ p = addr_book(SelectAddr, _("SELECT ADDRESS"), NULL);
+
+ if(ab_nesting_level <= 1)
+ end_adrbks();
+ else
+ ab_nesting_level--;
+
+ memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
+ if(save_nesting_level)
+ fs_give((void **)&save_nesting_level);
+
+ return(p);
+}
+
+
+/*
+ * Return a list of addresses without fullname
+ *
+ * Returns: pointer to returned address, or NULL if nothing returned
+ */
+char *
+addr_book_multaddr_nf(void)
+{
+ char *p;
+ jmp_buf save_jmp_buf;
+ int *save_nesting_level;
+
+ dprint((3, "--- addr_book_multaddr_nf ---\n"));
+
+ save_nesting_level = cpyint(ab_nesting_level);
+ memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
+ if(setjmp(addrbook_changed_unexpectedly)){
+ q_status_message(SM_ORDER, 5, 10, _("Resetting address book..."));
+ dprint((1, "RESETTING address book... addr_book_multaddr_nf!\n"));
+ addrbook_reset();
+ ab_nesting_level = *save_nesting_level;
+ }
+
+ ab_nesting_level++;
+
+ p = addr_book(SelectMultNoFull, _("SELECT ADDRESS"), NULL);
+
+ if(ab_nesting_level <= 1)
+ end_adrbks();
+ else
+ ab_nesting_level--;
+
+ memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
+ if(save_nesting_level)
+ fs_give((void **)&save_nesting_level);
+
+ return(p);
+}
+
+
+/*
+ * Return a single address without fullname phrase
+ *
+ * Returns: pointer to returned address, or NULL if nothing returned
+ */
+char *
+addr_book_oneaddr_nf(void)
+{
+ char *p;
+ jmp_buf save_jmp_buf;
+ int *save_nesting_level;
+
+ dprint((3, "--- addr_book_oneaddr_nf ---\n"));
+
+ save_nesting_level = cpyint(ab_nesting_level);
+ memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
+ if(setjmp(addrbook_changed_unexpectedly)){
+ q_status_message(SM_ORDER, 5, 10, _("Resetting address book..."));
+ dprint((1, "RESETTING address book... addr_book_oneaddr_nf!\n"));
+ addrbook_reset();
+ ab_nesting_level = *save_nesting_level;
+ }
+
+ ab_nesting_level++;
+
+ p = addr_book(SelectAddrNoFull, _("SELECT ADDRESS"), NULL);
+
+ if(ab_nesting_level <= 1)
+ end_adrbks();
+ else
+ ab_nesting_level--;
+
+ memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
+ if(save_nesting_level)
+ fs_give((void **)&save_nesting_level);
+
+ return(p);
+}
+
+
+/*
+ * Call address book from message composer
+ *
+ * Args: error_mess -- pointer to return error messages in (unused here)
+ *
+ * Returns: pointer to returned address, or NULL if nothing returned
+ */
+char *
+addr_book_compose(char **error)
+{
+ char *p;
+ jmp_buf save_jmp_buf;
+ int *save_nesting_level;
+
+ dprint((3, "--- addr_book_compose ---\n"));
+
+ save_nesting_level = cpyint(ab_nesting_level);
+ memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
+ if(setjmp(addrbook_changed_unexpectedly)){
+ q_status_message(SM_ORDER, 5, 10, _("Resetting address book..."));
+ dprint((1, "RESETTING address book... addr_book_compose!\n"));
+ addrbook_reset();
+ ab_nesting_level = *save_nesting_level;
+ }
+
+ ab_nesting_level++;
+
+ /* TRANSLATORS: a screen title */
+ p = addr_book(SelectNicksCom, _("COMPOSER: SELECT ADDRESS"), error);
+
+ if(ab_nesting_level <= 1)
+ end_adrbks();
+ else
+ ab_nesting_level--;
+
+ memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
+ if(save_nesting_level)
+ fs_give((void **)&save_nesting_level);
+
+ return(p);
+}
+
+
+/*
+ * Call address book from message composer for Lcc line
+ *
+ * Args: error_mess -- pointer to return error messages in (unused here)
+ *
+ * Returns: pointer to returned address, or NULL if nothing returned
+ */
+char *
+addr_book_compose_lcc(char **error)
+{
+ char *p;
+ jmp_buf save_jmp_buf;
+ int *save_nesting_level;
+
+ dprint((3, "--- addr_book_compose_lcc ---\n"));
+
+ save_nesting_level = cpyint(ab_nesting_level);
+ memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
+ if(setjmp(addrbook_changed_unexpectedly)){
+ q_status_message(SM_ORDER, 5, 10, _("Resetting address book..."));
+ dprint((1, "RESETTING address book... addr_book_compose_lcc!\n"));
+ addrbook_reset();
+ ab_nesting_level = *save_nesting_level;
+ }
+
+ ab_nesting_level++;
+
+ /*
+ * We used to use SelectAddrLccCom here but decided it wasn't necessary
+ * to restrict the selection to a list.
+ */
+ /* TRANSLATORS: a screen title, user is composing a message and should select
+ a distribution list from a list of addresses in the address book. */
+ p = addr_book(SelectNicksCom, _("COMPOSER: SELECT LIST"), error);
+
+ if(ab_nesting_level <= 1)
+ end_adrbks();
+ else
+ ab_nesting_level--;
+
+ memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
+ if(save_nesting_level)
+ fs_give((void **)&save_nesting_level);
+
+ return(p);
+}
+
+
+/*
+ * Call address book from message composer for Lcc line
+ *
+ * Args: error_mess -- pointer to return error messages in (unused here)
+ *
+ * Returns: pointer to returned address, or NULL if nothing returned
+ */
+char *
+addr_book_change_list(char **error)
+{
+ char *p;
+ jmp_buf save_jmp_buf;
+ int *save_nesting_level;
+
+ dprint((3, "--- addr_book_change_list ---\n"));
+
+ save_nesting_level = cpyint(ab_nesting_level);
+ memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
+ if(setjmp(addrbook_changed_unexpectedly)){
+ q_status_message(SM_ORDER, 5, 10, _("Resetting address book..."));
+ dprint((1, "RESETTING address book... addr_book_change_list!\n"));
+ addrbook_reset();
+ ab_nesting_level = *save_nesting_level;
+ }
+
+ ab_nesting_level++;
+
+ /* TRANSLATORS: a screen title */
+ p = addr_book(SelectNicksCom, _("ADDRESS BOOK (Update): SELECT ADDRESSES"),
+ error);
+
+ if(ab_nesting_level <= 1)
+ end_adrbks();
+ else
+ ab_nesting_level--;
+
+ memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
+ if(save_nesting_level)
+ fs_give((void **)&save_nesting_level);
+
+ return(p);
+}
+
+
+/*
+ * Call address book from pine_simple_send
+ *
+ * Returns: pointer to returned address, or NULL if nothing returned
+ */
+char *
+addr_book_bounce(void)
+{
+ char *p;
+ jmp_buf save_jmp_buf;
+ int *save_nesting_level;
+
+ dprint((3, "- addr_book_bounce -\n"));
+
+ save_nesting_level = cpyint(ab_nesting_level);
+ memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
+ if(setjmp(addrbook_changed_unexpectedly)){
+ q_status_message(SM_ORDER, 5, 10, _("Resetting address book..."));
+ dprint((1, "RESETTING address book...addr_book_bounce!\n"));
+ addrbook_reset();
+ ab_nesting_level = *save_nesting_level;
+ }
+
+ ab_nesting_level++;
+
+ p = addr_book(SelectManyNicks, _("SELECT ADDRESSES"), NULL);
+
+ if(ab_nesting_level <= 1)
+ end_adrbks();
+ else
+ ab_nesting_level--;
+
+ memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
+ if(save_nesting_level)
+ fs_give((void **)&save_nesting_level);
+
+ return(p);
+}
+
+
+/*
+ * Call address book from take address screen
+ *
+ * Returns: pointer to returned nickname, or NULL if nothing returned
+ */
+char *
+addr_book_takeaddr(void)
+{
+ char *p;
+ jmp_buf save_jmp_buf;
+ int *save_nesting_level;
+
+ dprint((3, "- addr_book_takeaddr -\n"));
+
+ save_nesting_level = cpyint(ab_nesting_level);
+ memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
+ if(setjmp(addrbook_changed_unexpectedly)){
+ q_status_message(SM_ORDER, 5, 10, _("Resetting address book..."));
+ dprint((1, "RESETTING address book...addr_book_takeaddr!\n"));
+ addrbook_reset();
+ ab_nesting_level = *save_nesting_level;
+ }
+
+ ab_nesting_level++;
+
+ /* TRANSLATORS: a screen title */
+ p = addr_book(SelectNickTake, _("TAKEADDR: SELECT NICKNAME"), NULL);
+
+ if(ab_nesting_level <= 1)
+ end_adrbks();
+ else
+ ab_nesting_level--;
+
+ memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
+ if(save_nesting_level)
+ fs_give((void **)&save_nesting_level);
+
+ return(p);
+}
+
+
+/*
+ * Call address book from editing screen for nickname field.
+ *
+ * Returns: pointer to returned nickname, or NULL if nothing returned
+ */
+char *
+addr_book_nick_for_edit(char **error)
+{
+ char *p;
+ jmp_buf save_jmp_buf;
+ int *save_nesting_level;
+ int save_n_serv;
+
+ dprint((3, "- addr_book_nick_for_edit -\n"));
+
+ save_n_serv = as.n_serv;
+
+ save_nesting_level = cpyint(ab_nesting_level);
+ memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
+ if(setjmp(addrbook_changed_unexpectedly)){
+ q_status_message(SM_ORDER, 5, 10, _("Resetting address book..."));
+ dprint((1, "RESETTING address book...addr_book_nick_for_edit!\n"));
+ addrbook_reset();
+ ab_nesting_level = *save_nesting_level;
+ }
+
+ ab_nesting_level++;
+
+ /*
+ * This is kind of hoaky. We want to prevent the Directory Query
+ * options from turning on when we're coming in looking for a nickname
+ * and this seemed to be the easiest way to accomplish that.
+ */
+ as.n_serv = 0;
+ /* TRANSLATORS: a screen title, user selecting a nickname from the address book */
+ p = addr_book(SelectNickCom, _("SELECT NICKNAME"), error);
+ as.n_serv = save_n_serv;
+
+ if(ab_nesting_level <= 1)
+ end_adrbks();
+ else
+ ab_nesting_level--;
+
+ memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
+ if(save_nesting_level)
+ fs_give((void **)&save_nesting_level);
+
+ return(p);
+}
+
+
+/*
+ * Call address book for generic nickname select
+ *
+ * Returns: pointer to returned nickname, or NULL if nothing returned
+ */
+char *
+addr_book_selnick(void)
+{
+ char *p;
+ jmp_buf save_jmp_buf;
+ int *save_nesting_level;
+
+ dprint((3, "- addr_book_selnick -\n"));
+
+ save_nesting_level = cpyint(ab_nesting_level);
+ memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
+ if(setjmp(addrbook_changed_unexpectedly)){
+ q_status_message(SM_ORDER, 5, 10, _("Resetting address book..."));
+ dprint((1, "RESETTING address book...addr_book_selnick!\n"));
+ addrbook_reset();
+ ab_nesting_level = *save_nesting_level;
+ }
+
+ ab_nesting_level++;
+
+ p = addr_book(SelectNick, _("SELECT NICKNAME"), NULL);
+
+ if(ab_nesting_level <= 1)
+ end_adrbks();
+ else
+ ab_nesting_level--;
+
+ memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
+ if(save_nesting_level)
+ fs_give((void **)&save_nesting_level);
+
+ return(p);
+}
+
+
+/*
+ * Main address book screen
+ *
+ * Control loop for address book. Commands are executed out of a big
+ * switch and screen painting is done.
+ * The style argument controls whether or not it is called to return an address
+ * to the composer, a nickname to the TakeAddr screen, or just for address
+ * book maintenance.
+ *
+ * Args: style -- how we were called
+ *
+ * Return: might return a string for the composer to edit, or a nickname, ...
+ */
+char *
+addr_book(AddrBookArg style, char *title, char **error_message)
+{
+ UCS c;
+ int cmd, r, i,
+ command_line,
+ did_delete,
+ quit, /* loop control */
+ km_popped, /* menu is popped up in blank menu mode */
+ current_changed_flag, /* only current row needs update */
+ was_clickable, is_clickable,
+ was_collapsible_listent, is_collapsible_listent,
+ was_global_config, is_global_config,
+ was_addkey, is_addkey,
+ was_opened, is_opened,
+ was_custom_title, is_custom_title,
+ setall_changed,
+ start_disp, /* Paint from this line down (0 is top) */
+ rdonly, /* cur addrbook read only */
+ empty, /* cur addrbook empty */
+ are_selecting, /* called as ^T selector */
+#ifdef ENABLE_LDAP
+ directory_ok, /* called from composer, not Lcc */
+#endif
+ from_composer, /* from composer */
+ listmode_ok, /* ok to do ListMode with this style */
+ selecting_one_nick,
+ selecting_mult_nicks,
+ checkedn, /* how many are checked */
+ def_key, /* default key */
+ warped; /* we warped through hyperspace to a
+ new location in the display list */
+ long fl,
+ new_top_ent, /* entry on top of screen after oper */
+ new_line; /* new line number after operation */
+ char *addr;
+ char *utf8str;
+ bitmap_t bitmap;
+ struct key_menu *km;
+ OtherMenu what;
+ PerAddrBook *pab = NULL;
+ AddrScrn_Disp *dl;
+ struct pine *ps;
+ Pos cursor_pos;
+
+ dprint((2, "--- addr_book --- (%s)\n",
+ style==AddrBookScreen ? "AddrBookScreen" :
+ style==SelectAddrLccCom ? "SelectAddrLccCom" :
+ style==SelectNicksCom ? "SelectNicksCom" :
+ style==SelectNick ? "SelectNick" :
+ style==SelectNickTake ? "SelectNickTake" :
+ style==SelectNickCom ? "SelectNickCom" :
+ style==AddrBookConfig ? "AddrBookConfig" :
+ style==SelectManyNicks ? "SelectManyNicks" :
+ style==SelectAddr ? "SelectAddr" :
+ style==SelectAddrNoFull ? "SelectAddrNoFull" :
+ style==SelectMultNoFull ? "SelectMultNoFull":
+ "UnknownStyle"));
+
+ km = &ab_keymenu;
+ ps = ps_global;
+ ps->next_screen = SCREEN_FUN_NULL;
+
+ from_composer = (style == SelectAddrLccCom
+ || style == SelectNicksCom
+ || style == SelectNickCom);
+ are_selecting = (style != AddrBookScreen
+ && style != AddrBookConfig);
+ selecting_one_nick = (style == SelectNick
+ || style == SelectNickTake
+ || style == SelectNickCom);
+ selecting_mult_nicks = (style == SelectAddrLccCom
+ || style == SelectNicksCom
+ || style == SelectManyNicks);
+ listmode_ok = (style == SelectAddrLccCom
+ || style == SelectNicksCom
+ || style == SelectManyNicks
+ || style == SelectMultNoFull);
+ as.config = (style == AddrBookConfig);
+#ifdef ENABLE_LDAP
+ directory_ok = (style == SelectNicksCom
+ || style == AddrBookScreen
+ || style == SelectManyNicks);
+#endif
+
+ /* Coming in from the composer, may need to reset the window */
+ if(from_composer){
+ fix_windsize(ps);
+ init_sigwinch();
+ mark_status_dirty();
+ mark_titlebar_dirty();
+ mark_keymenu_dirty();
+ }
+
+ command_line = -FOOTER_ROWS(ps); /* third line from the bottom */
+ what = FirstMenu;
+ c = 'x'; /* For display_message the first time through */
+
+ if(ps->remote_abook_validity > 0)
+ (void)adrbk_check_and_fix_all(ab_nesting_level == 1, 0, 0);
+
+ if(!init_addrbooks(HalfOpen, 1, !as.config, !are_selecting)){
+ if(are_selecting){
+ q_status_message(SM_ORDER | SM_DING, 0, 4,
+ _("No Address Book Configured"));
+ display_message(c);
+ sleep(2);
+ return NULL;
+ }
+ else if(!as.config){
+ ps->next_screen = main_menu_screen;
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("No Address Book Configured, Use SETUP Addressbook screen"));
+ ps->mangled_screen = 1;
+ return NULL;
+ }
+ }
+ else if(style == AddrBookScreen && as.n_addrbk == 1 && as.n_serv == 0){
+ if(as.adrbks[0].access == ReadOnly)
+ q_status_message(SM_ORDER, 0, 4, _("AddressBook is Read Only"));
+ else if(as.adrbks[0].access == NoAccess)
+ q_status_message(SM_ORDER, 0, 4,
+ _("AddressBook not accessible, permission denied"));
+ }
+
+ if(as.l_p_page < 1){
+ q_status_message(SM_ORDER, 3, 3,
+ _("Screen too small to use Address book"));
+ return NULL;
+ }
+
+ erase_checks();
+ as.selections = 0;
+ as.zoomed = 0;
+
+ (void) calculate_field_widths();
+
+ quit = 0;
+ km_popped = 0;
+ ps->mangled_screen = 1;
+ current_changed_flag = 0;
+ start_disp = 0;
+ was_clickable = 0;
+ is_clickable = 0;
+ was_collapsible_listent = 0;
+ is_collapsible_listent = 0;
+ was_global_config = 0;
+ is_global_config = 0;
+ was_addkey = 0;
+ is_addkey = 0;
+ was_opened = 0;
+ is_opened = 0;
+ was_custom_title = 0;
+ is_custom_title = 0;
+ setall_changed = 0;
+ checkedn = 0;
+
+
+ while(!quit){
+ ps->user_says_cancel = 0;
+ if(km_popped){
+ km_popped--;
+ if(km_popped == 0){
+ clearfooter(ps);
+ /*
+ * Have to repaint from earliest change down, including
+ * at least the last two body lines.
+ */
+ if(ps->mangled_body) /* it was already mangled */
+ start_disp = MIN(start_disp, as.l_p_page -2);
+ else if(current_changed_flag){
+ ps->mangled_body = 1;
+ start_disp = MIN(MIN(as.cur_row, as.l_p_page -2),
+ as.old_cur_row);
+ }
+ else{
+ ps->mangled_body = 1;
+ start_disp = as.l_p_page -2;
+ }
+ }
+ }
+
+ ps->redrawer = redraw_addr_screen;
+
+ if(new_mail(0, NM_TIMING(c), NM_STATUS_MSG | NM_DEFER_SORT) >= 0)
+ ps->mangled_header = 1;
+
+ if(streams_died())
+ ps->mangled_header = 1;
+
+ if(ps->mangled_screen){
+ ps->mangled_header = 1;
+ ps->mangled_body = 1;
+ ps->mangled_footer = 1;
+ start_disp = 0;
+ current_changed_flag = 0;
+ ps->mangled_screen = 0;
+ }
+
+ if(ps->mangled_body){
+
+ if(calculate_field_widths())
+ start_disp = 0;
+
+ display_book(start_disp,
+ as.cur_row,
+ as.old_cur_row,
+ 1,
+ &cursor_pos);
+
+ as.old_cur_row = as.cur_row;
+ ps->mangled_body = 0;
+ start_disp = 0;
+ as.cur = cur_addr_book();
+ pab = (as.n_addrbk &&
+ as.cur >= 0 && as.cur < as.n_addrbk &&
+ !entry_is_askserver(as.top_ent+as.cur_row))
+ ? &as.adrbks[as.cur] : NULL;
+
+#ifdef _WINDOWS
+ {
+ long i, last_sel;
+
+ for(i = as.top_ent; dlist(i)->type != End; i++)
+ next_selectable_line(i,&last_sel);
+ as.last_ent = i;
+
+ scroll_setrange(as.l_p_page, last_sel);
+ }
+#endif
+ }
+ /* current entry has been changed */
+ else if(current_changed_flag){
+ int need_redraw;
+
+ need_redraw = calculate_field_widths();
+
+ /*---------- Update the current entry, (move or change) -------*/
+ display_book(need_redraw ? 0 : as.cur_row,
+ as.cur_row,
+ as.old_cur_row,
+ need_redraw,
+ &cursor_pos);
+
+ as.old_cur_row = as.cur_row;
+ current_changed_flag = 0;
+ as.cur = cur_addr_book();
+ pab = (as.n_addrbk &&
+ as.cur >= 0 && as.cur < as.n_addrbk &&
+ !entry_is_askserver(as.top_ent+as.cur_row))
+ ? &as.adrbks[as.cur] : NULL;
+ }
+
+ is_custom_title = (F_OFF(F_CMBND_ABOOK_DISP,ps_global) &&
+ style == AddrBookScreen &&
+ as.n_addrbk > 1 &&
+ cur_is_open() &&
+ pab->abnick &&
+ pab->abnick[0]);
+ if(( was_custom_title && !is_custom_title) ||
+ (!was_custom_title && is_custom_title)){
+ ps->mangled_header = 1;
+ was_custom_title = is_custom_title;
+ }
+
+
+ if(ps->mangled_header){
+ char buf[80], *bp;
+
+ if(style == AddrBookScreen){
+ if(F_ON(F_CMBND_ABOOK_DISP,ps_global)){
+ if(as.n_addrbk > 1)
+ snprintf(buf, sizeof(buf), _("ADDRESS BOOKS"));
+ else
+ snprintf(buf, sizeof(buf), _("ADDRESS BOOK"));
+ }
+ else
+ /* TRANSLATORS: a screen title, the %s arguments are for a custom part
+ of the title. Move them as a group. There are two normal cases.
+ Either ADDRESS BOOK LIST when a list of names of address books is
+ displayed, or ADDRESS BOOK <name of address book> when one is open. */
+ snprintf(buf, sizeof(buf), _("ADDRESS BOOK%s%s%s"),
+ /* TRANSLATORS: This is the LIST that goes with title above */
+ is_custom_title ? " <" : cur_is_open() ? "" : _(" LIST"),
+ is_custom_title ? pab->abnick : "",
+ is_custom_title ? ">" : "");
+
+ buf[sizeof(buf)-1] = '\0';
+
+ bp = buf;
+ }
+ else
+ bp = title;
+
+ set_titlebar(bp, ps->mail_stream,
+ ps->context_current, ps->cur_folder,
+ ps->msgmap, 1,
+ FolderName, 0, 0, NULL);
+ ps->mangled_header = 0;
+ }
+
+ dprint((9, "addr_book: top of loop, addrbk %d top_ent %ld cur_row %d\n", as.cur, as.top_ent, as.cur_row));
+
+ /*
+ * This is a check to catch when a move from one row to another
+ * should cause the list of commands to change.
+ */
+ is_clickable = entry_is_clickable(as.top_ent+as.cur_row);
+ is_collapsible_listent = F_OFF(F_EXPANDED_DISTLISTS,ps) &&
+ entry_is_listent(as.top_ent+as.cur_row);
+ is_global_config = as.config && pab && pab->type & GLOBAL;
+ is_addkey = entry_is_addkey(as.top_ent+as.cur_row);
+ is_opened = (F_OFF(F_CMBND_ABOOK_DISP,ps_global) &&
+ cur_is_open());
+ if(( was_clickable && !is_clickable) ||
+ (!was_clickable && is_clickable) ||
+ ( was_collapsible_listent && !is_collapsible_listent) ||
+ (!was_collapsible_listent && is_collapsible_listent) ||
+ ( was_global_config && !is_global_config) ||
+ (!was_global_config && is_global_config) ||
+ ( was_addkey && !is_addkey) ||
+ (!was_addkey && is_addkey) ||
+ ( was_opened && !is_opened) ||
+ (!was_opened && is_opened) ||
+ ( setall_changed)){
+
+ ps->mangled_footer = 1;
+ was_clickable = is_clickable;
+ was_collapsible_listent = is_collapsible_listent;
+ was_global_config = is_global_config;
+ was_addkey = is_addkey;
+ was_opened = is_opened;
+ setall_changed = 0;
+ }
+
+
+ if(ps->mangled_footer){
+
+ setbitmap(bitmap);
+ menu_clear_binding(km, '>');
+ menu_clear_binding(km, '.');
+ menu_clear_binding(km, KEY_RIGHT);
+ menu_clear_binding(km, ctrl('M'));
+ menu_clear_binding(km, ctrl('J'));
+ menu_clear_binding(km, KEY_LEFT);
+ menu_clear_binding(km, '<');
+ menu_clear_binding(km, ',');
+ def_key = THREE_KEY; /* default default key */
+ if(as.config){
+ km->how_many = 1;
+
+ clrbitn(OTHER_KEY, bitmap);
+ /* TRANSLATORS: a command name for a particular key */
+ menu_init_binding(km, 'E', MC_EXIT, "E", N_("Exit Setup"), TWO_KEY);
+ KS_OSDATASET(&km->keys[TWO_KEY], KS_EXITMODE);
+
+ /*
+ * Don't show Delete or Shuffle command if there is nothing
+ * to delete or shuffle.
+ */
+ if(is_addkey){
+ clrbitn(DELETE_KEY, bitmap);
+ clrbitn(SENDTO_KEY, bitmap);
+ clrbitn(THREE_KEY, bitmap);
+ menu_init_binding(km, 'A', MC_ADDABOOK, "A",
+ add_is_global(as.top_ent+as.cur_row)
+ /* TRANSLATORS: Add Global Address book (one defined by someone else) */
+ ? "[" N_("Add Glob Abook") "]"
+ /* TRANSLATORS: Add Personal Address book */
+ : "[" N_("Add Pers Abook") "]",
+ ADD_KEY);
+ def_key = ADD_KEY;
+ }
+ else{
+ /* TRANSLATORS: Delete Address book command */
+ menu_init_binding(km, 'D', MC_DELABOOK, "D", N_("Del Abook"),
+ DELETE_KEY);
+ /* TRANSLATORS: Shuffle refers to shuffling the order of things,
+ that is, changing the order */
+ menu_init_binding(km, '$', MC_SHUFFLE, "$", N_("Shuffle"),
+ SENDTO_KEY);
+ /* TRANSLATORS: Change is a command meaning Change some item,
+ or Edit some item to change it */
+ menu_init_binding(km, 'C', MC_EDITABOOK, "C", "[" N_("Change") "]",
+ THREE_KEY);
+ menu_init_binding(km, 'A', MC_ADDABOOK, "A",
+ add_is_global(as.top_ent+as.cur_row)
+ ? N_("Add Glob Abook")
+ : N_("Add Pers Abook"),
+ ADD_KEY);
+ }
+ }
+ else if(are_selecting){
+ km->how_many = 1;
+
+ /*
+ * The OTHER_KEY is used as the Exit key in selection mode.
+ * This is because the TWO_KEY is being used for < actions.
+ */
+ /* TRANSLATORS: Command to Exit the Select screen. This would
+ be a way to make no Selection and go back to where you
+ came from. */
+ menu_init_binding(km, 'E', MC_EXIT, "E", N_("ExitSelect"),
+ OTHER_KEY);
+ KS_OSDATASET(&km->keys[OTHER_KEY], KS_EXITMODE);
+
+ /*
+ * Use the TWO_KEY for the go back key.
+ */
+ if(cur_is_open() && (as.n_addrbk > 1 || as.n_serv)){
+ if(F_OFF(F_EXPANDED_DISTLISTS,ps) &&
+ entry_is_listent(as.top_ent+as.cur_row))
+ cmd = MC_UNEXPAND;
+ else if(F_OFF(F_CMBND_ABOOK_DISP,ps_global))
+ cmd = MC_POPUP;
+ else
+ cmd = MC_NONE;
+
+ if(cmd == MC_NONE)
+ clrbitn(TWO_KEY, bitmap);
+ else{
+ menu_init_binding(km, '<', cmd, "<",
+ cmd == MC_POPUP ? N_("AddressBkList")
+ /* TRANSLATORS: Unexpand is the opposite of Expand.
+ We might expand an address book distribution
+ list to see all the members of the list. */
+ : N_("Unexpand"),
+ TWO_KEY);
+ menu_add_binding(km, ',', cmd);
+ if(F_ON(F_ARROW_NAV,ps))
+ menu_add_binding(km, KEY_LEFT, cmd);
+ }
+ }
+ else if(as.checkboxes && (as.n_addrbk > 1 || as.n_serv)){
+ if(checkedn){
+ if(entry_is_clickable_title(as.top_ent+as.cur_row)){
+ menu_init_binding(km, 'S', MC_CHOICE, "S",
+ /* TRANSLATORS: Select something, choose something */
+ N_("Select"), TWO_KEY);
+ }
+ else{
+ menu_init_binding(km, 'S', MC_CHOICE, "S",
+ "[" N_("Select") "]", TWO_KEY);
+ def_key = TWO_KEY;
+ }
+ }
+ else
+ menu_init_binding(km, 'S', MC_CHOICE, "S", N_("Select"),
+ TWO_KEY);
+ }
+ else
+ clrbitn(TWO_KEY, bitmap);
+
+ /*
+ * The THREE_KEY is used as the select key in selection mode,
+ * but it doesn't show up at the top-level. Instead, the
+ * key becomes the ViewAbook key.
+ */
+ if(entry_is_askserver(as.top_ent+as.cur_row) && !as.checkboxes){
+ menu_init_binding(km, '>', MC_QUERY_SERV, ">", "[" N_("Search") "]",
+ THREE_KEY);
+ menu_add_binding(km, 's', MC_QUERY_SERV);
+ menu_add_binding(km, '.', MC_QUERY_SERV);
+ if(F_ON(F_ARROW_NAV,ps))
+ menu_add_binding(km, KEY_RIGHT, MC_QUERY_SERV);
+ }
+ else if(entry_is_clickable_title(as.top_ent+as.cur_row)){
+ /* TRANSLATORS: View this address book */
+ menu_init_binding(km, '>', MC_OPENABOOK, ">", "[" N_("ViewAbook") "]",
+ THREE_KEY);
+ menu_add_binding(km, 'v', MC_OPENABOOK);
+ menu_add_binding(km, '.', MC_OPENABOOK);
+ if(F_ON(F_ARROW_NAV,ps))
+ menu_add_binding(km, KEY_RIGHT, MC_OPENABOOK);
+ }
+ else if(cur_is_open()){
+ menu_init_binding(km, 'S', MC_CHOICE, "S", "[" N_("Select") "]",
+ THREE_KEY);
+ }
+ else
+ clrbitn(THREE_KEY, bitmap);
+
+ KS_OSDATASET(&km->keys[THREE_KEY], KS_NONE);
+
+ /*
+ * The Expand command gets stuck out in right field.
+ */
+ if(entry_is_clickable(as.top_ent+as.cur_row) &&
+ !entry_is_clickable_title(as.top_ent+as.cur_row)){
+ menu_init_binding(km, '>', MC_EXPAND, ">", N_("Expand"),
+ SENDTO_KEY);
+ menu_add_binding(km, '.', MC_EXPAND);
+ if(F_ON(F_ARROW_NAV,ps))
+ menu_add_binding(km, KEY_RIGHT, MC_EXPAND);
+ }
+ else
+ clrbitn(SENDTO_KEY, bitmap);
+
+ if(cur_is_open() && as.checkboxes){
+ /* TRANSLATORS: Set/Unset means that this particular command
+ will toggle between setting something (turning it on) and
+ unsetting it (turning it off). For example, it might be
+ a program option that can be turned on or off or it might
+ be a way to mark which addresses to send a message to. */
+ menu_init_binding(km, 'X', MC_TOGGLE, "X", N_("Set/Unset"),
+ DELETE_KEY);
+
+ }
+ else if(cur_is_open() && listmode_ok){
+ /* TRANSLATORS: List mode is a type of screen in pine that
+ allows the user to select several of something. This is
+ the name of the command to go into the List mode style
+ of operating. */
+ menu_init_binding(km, 'L', MC_LISTMODE, "L", N_("ListMode"),
+ DELETE_KEY);
+ }
+ else
+ clrbitn(DELETE_KEY, bitmap);
+
+ if(cur_is_open() && as.checkboxes){
+ menu_init_binding(km, 'A', MC_SELALL, "A",
+ /* TRANSLATORS: when selecting from a list of items
+ the unsetall (unset all) means to start over
+ with nothing selected.
+ The set all command means select everything
+ in the list. */
+ checkedn ? N_("unsetAll") : N_("setAll"),
+ ADD_KEY);
+ }
+ else
+ clrbitn(ADD_KEY, bitmap);
+
+ KS_OSDATASET(&km->keys[DELETE_KEY], KS_NONE);
+ }
+ else{
+ /*
+ * Reset first Other key. Selection screen may have
+ * blasted it. Do this by hand because menu_init_binding
+ * will remove the other two OTHER CMDS bindings.
+ * Should figure out how to do this correctly with a
+ * reasonable function call.
+ */
+ km->keys[OTHER_KEY].name = "O";
+ /* TRANSLATORS: This is the name of the command that will show
+ which other commands are available. 12 commands are shown at
+ the bottom of the screen, this command would show the next set
+ of 12 */
+ km->keys[OTHER_KEY].label = N_("OTHER CMDS");
+ km->keys[OTHER_KEY].bind.cmd = MC_OTHER;
+ km->keys[OTHER_KEY].bind.ch[0] = 'O';
+ km->keys[OTHER_KEY].bind.nch = 1;
+ KS_OSDATASET(&km->keys[OTHER_KEY], KS_NONE);
+
+ km->how_many = 2;
+
+ if(F_ON(F_CMBND_ABOOK_DISP,ps_global)){
+ if(F_ON(F_ENABLE_AGG_OPS, ps))
+ km->how_many = 3;
+
+ /*
+ * The TWO_KEY is used as the go back key in
+ * non-selection mode.
+ */
+ if(F_OFF(F_EXPANDED_DISTLISTS,ps) &&
+ entry_is_listent(as.top_ent+as.cur_row)){
+ cmd = MC_UNEXPAND;
+ menu_init_binding(km, '<', cmd, "<", N_("Unexpand"),
+ TWO_KEY);
+ KS_OSDATASET(&km->keys[TWO_KEY], KS_NONE);
+ }
+ else{
+ cmd = MC_MAIN;
+ menu_init_binding(km, 'M', cmd, "<", N_("Main Menu"),
+ TWO_KEY);
+ KS_OSDATASET(&km->keys[TWO_KEY], KS_MAINMENU);
+ }
+
+ if(cur_is_open()){
+ /*
+ * Add or delete entries from this address book.
+ */
+ /* TRANSLATORS: Add a new entry (this is a command) */
+ menu_init_binding(km, '@', MC_ADD, "@", N_("AddNew"),
+ ADD_KEY);
+ menu_init_binding(km, 'D', MC_DELETE, "D", N_("Delete"),
+ DELETE_KEY);
+ /* TRANSLATORS: Compose a message to be sent to the current address
+ book entry */
+ menu_init_binding(km, 'C', MC_COMPOSE, "C", N_("ComposeTo"),
+ SENDTO_KEY);
+ KS_OSDATASET(&km->keys[SENDTO_KEY], KS_COMPOSER);
+ menu_init_binding(km, '#', MC_ROLE, "#", N_("Role"),
+ RCOMPOSE_KEY);
+ }
+ else{
+ clrbitn(ADD_KEY, bitmap);
+ clrbitn(DELETE_KEY, bitmap);
+ clrbitn(SENDTO_KEY, bitmap);
+ clrbitn(RCOMPOSE_KEY, bitmap);
+ clrbitn(SAVE_KEY, bitmap);
+ clrbitn(TAKE_KEY, bitmap);
+ clrbitn(FORW_KEY, bitmap);
+ }
+
+ clrbitn(SECONDARY_MAIN_KEY, bitmap);
+ }
+ else if(cur_is_open()){
+ if(F_ON(F_ENABLE_AGG_OPS, ps))
+ km->how_many = 3;
+
+ /*
+ * The TWO_KEY is used as the go back key in
+ * non-selection mode.
+ */
+ if(F_OFF(F_EXPANDED_DISTLISTS,ps) &&
+ entry_is_listent(as.top_ent+as.cur_row)){
+ cmd = MC_UNEXPAND;
+ menu_init_binding(km, '<', cmd, "<", N_("Unexpand"),
+ TWO_KEY);
+ KS_OSDATASET(&km->keys[TWO_KEY], KS_NONE);
+ }
+ else{
+ if(as.n_addrbk > 1 || as.n_serv){
+ cmd = MC_POPUP;
+ menu_init_binding(km, '<', cmd, "<",
+ N_("AddressBkList"), TWO_KEY);
+ KS_OSDATASET(&km->keys[TWO_KEY], KS_NONE);
+ }
+ else{
+ cmd = MC_MAIN;
+ menu_init_binding(km, 'M', cmd, "<", N_("Main Menu"),
+ TWO_KEY);
+ KS_OSDATASET(&km->keys[TWO_KEY], KS_MAINMENU);
+ }
+ }
+
+ if(pab->access != NoAccess){
+ /*
+ * Add or delete entries from this address book.
+ */
+ menu_init_binding(km, '@', MC_ADD, "@", N_("AddNew"),
+ ADD_KEY);
+ menu_init_binding(km, 'D', MC_DELETE, "D", N_("Delete"),
+ DELETE_KEY);
+ }
+ else{
+ clrbitn(ADD_KEY, bitmap);
+ clrbitn(DELETE_KEY, bitmap);
+ }
+
+ /* Find someplace to put Main Menu command */
+ if(cmd == MC_POPUP){
+ menu_init_binding(km, 'M', MC_MAIN, "M", N_("Main Menu"),
+ SECONDARY_MAIN_KEY);
+ KS_OSDATASET(&km->keys[SECONDARY_MAIN_KEY],KS_MAINMENU);
+ }
+ else
+ clrbitn(SECONDARY_MAIN_KEY, bitmap);
+
+ menu_init_binding(km, 'C', MC_COMPOSE, "C", N_("ComposeTo"),
+ SENDTO_KEY);
+ KS_OSDATASET(&km->keys[SENDTO_KEY], KS_COMPOSER);
+ menu_init_binding(km, '#', MC_ROLE, "#", N_("Role"),
+ RCOMPOSE_KEY);
+ }
+ else{
+ /*
+ * The TWO_KEY is used as the go back key in
+ * non-selection mode.
+ */
+ cmd = MC_MAIN;
+ menu_init_binding(km, 'M', cmd, "<", N_("Main Menu"),
+ TWO_KEY);
+ KS_OSDATASET(&km->keys[TWO_KEY], KS_MAINMENU);
+
+ clrbitn(SENDTO_KEY, bitmap);
+ clrbitn(RCOMPOSE_KEY, bitmap);
+ clrbitn(ADD_KEY, bitmap);
+ clrbitn(DELETE_KEY, bitmap);
+ clrbitn(SECONDARY_MAIN_KEY, bitmap);
+ clrbitn(SAVE_KEY, bitmap);
+ clrbitn(TAKE_KEY, bitmap);
+ clrbitn(FORW_KEY, bitmap);
+ }
+
+ /* can't be on third menu if we just reduced to 2 */
+ if(km->how_many == 2 && km->which == 2)
+ km->which = 0;
+
+ menu_add_binding(km, '<', cmd);
+ menu_add_binding(km, ',', cmd);
+ if(F_ON(F_ARROW_NAV,ps))
+ menu_add_binding(km, KEY_LEFT, cmd);
+
+ KS_OSDATASET(&km->keys[DELETE_KEY], KS_DELETE);
+
+ /*
+ * The THREE_KEY is the burrow into the hierarchy key.
+ */
+ if(entry_is_askserver(as.top_ent+as.cur_row))
+ cmd = MC_QUERY_SERV;
+ else if(entry_is_clickable(as.top_ent+as.cur_row)){
+ if(entry_is_clickable_title(as.top_ent+as.cur_row))
+ cmd = MC_OPENABOOK;
+ else
+ cmd = MC_EXPAND;
+ }
+ else
+ cmd = MC_VIEW_ENTRY;
+
+ menu_init_binding(km, '>', cmd, ">",
+ cmd == MC_EXPAND ? "[" N_("Expand") "]" :
+ cmd == MC_QUERY_SERV ? "[" N_("Search") "]" :
+ /* TRANSLATORS: In the address book the user can
+ view a particular address book entry. It is
+ called View/Update because the way to update
+ an entry is to first view it and then there
+ will be an opportunity to update it from there. */
+ cur_is_open() ? "[" N_("View/Update") "]"
+ : "[" N_("ViewAbook") "]",
+ THREE_KEY);
+
+ if(cmd == MC_QUERY_SERV)
+ menu_add_binding(km, 's', cmd);
+ else if(cmd == MC_OPENABOOK || cmd == MC_VIEW_ENTRY)
+ menu_add_binding(km, 'v', cmd);
+
+ menu_add_binding(km, '.', cmd);
+ if(F_ON(F_ARROW_NAV,ps))
+ menu_add_binding(km, KEY_RIGHT, cmd);
+ }
+
+ menu_add_binding(km, ctrl('M'), km->keys[def_key].bind.cmd);
+ menu_add_binding(km, ctrl('J'), km->keys[def_key].bind.cmd);
+
+ if(km_popped){
+ FOOTER_ROWS(ps) = 3;
+ clearfooter(ps);
+ }
+
+ draw_keymenu(km, bitmap, ps->ttyo->screen_cols,
+ 1-FOOTER_ROWS(ps), 0, what);
+ ps->mangled_footer = 0;
+ what = SameMenu;
+ if(km_popped){
+ FOOTER_ROWS(ps) = 1;
+ mark_keymenu_dirty();
+ }
+ }
+
+ rdonly = (pab && pab->access == ReadOnly);
+ empty = is_empty(as.cur_row+as.top_ent);
+
+ /*------------ display any status messages ------------------*/
+ if(km_popped){
+ FOOTER_ROWS(ps) = 3;
+ mark_status_unknown();
+ }
+
+ display_message(c);
+ if(km_popped){
+ FOOTER_ROWS(ps) = 1;
+ mark_status_unknown();
+ }
+
+ if(F_OFF(F_SHOW_CURSOR, ps)){
+ /* reset each time through to catch screen size changes */
+ cursor_pos.row = ps->ttyo->screen_rows-FOOTER_ROWS(ps);
+ cursor_pos.col = 0;
+ }
+
+ MoveCursor(cursor_pos.row, cursor_pos.col);
+
+
+ /*---------------- Get command and validate -------------------*/
+#ifdef MOUSE
+ mouse_in_content(KEY_MOUSE, -1, -1, 0, 0);
+ register_mfunc(mouse_in_content, HEADER_ROWS(ps), 0,
+ ps->ttyo->screen_rows-(FOOTER_ROWS(ps)+1),
+ ps->ttyo->screen_cols);
+#endif
+
+ /* sort out what help needs to be displayed if asked for */
+ if(as.config)
+ gAbookHelp = h_abook_config;
+ else if(are_selecting){
+ if(cur_is_open()){
+ if(style == SelectNickTake)
+ gAbookHelp = h_abook_select_nicks_take;
+ else if(selecting_one_nick)
+ gAbookHelp = h_abook_select_nick;
+ else if(as.checkboxes)
+ gAbookHelp = h_abook_select_checks;
+ else if(listmode_ok)
+ gAbookHelp = h_abook_select_listmode;
+ else
+ gAbookHelp = h_abook_select_addr;
+ }
+ else
+ gAbookHelp = h_abook_select_top;
+ }
+ else
+ gAbookHelp = cur_is_open() ? h_abook_opened : h_abook_top;
+
+#ifdef _WINDOWS
+ mswin_setscrollcallback(addr_scroll_callback);
+ mswin_sethelptextcallback(pcpine_help_addrbook);
+#endif
+ c = READ_COMMAND(&utf8str);
+#ifdef MOUSE
+ clear_mfunc(mouse_in_content);
+#endif
+#ifdef _WINDOWS
+ mswin_setscrollcallback(NULL);
+ mswin_sethelptextcallback(NULL);
+#endif
+ cmd = menu_command(c, km);
+
+ dprint((2, "Addrbook command: %d (0x%x %s)\n", cmd,
+ c, pretty_command(c)));
+
+ /* this may be a safe place to update addrbooks */
+ if(ps->remote_abook_validity > 0 &&
+ adrbk_check_and_fix_all(ab_nesting_level < 2 &&
+ !any_ab_open() && checkedn == 0, 0, 0))
+ ps->mangled_footer = 1;
+
+ if(km_popped)
+ switch(cmd){
+ case MC_NONE:
+ case MC_OTHER:
+ case MC_RESIZE:
+ case MC_REPAINT:
+ km_popped++;
+ break;
+
+ default:
+ clearfooter(ps);
+ break;
+ }
+
+ /*------------- execute command ----------------*/
+ switch(cmd){
+
+ /*------------ Noop (new mail check) --------------*/
+ case MC_NONE:
+ break;
+
+
+ /*----------- Help -------------------*/
+ case MC_HELP:
+ if(FOOTER_ROWS(ps) == 1 && km_popped == 0){
+ km_popped = 2;
+ ps->mangled_footer = 1;
+ break;
+ }
+
+ if(as.config)
+ helper(gAbookHelp, _("HELP ON CONFIGURING ADDRESS BOOKS"),
+ HLPD_NONE);
+ else if(are_selecting)
+ helper(gAbookHelp, _("HELP ON ADDRESS BOOK"),
+ HLPD_SIMPLE | HLPD_NEWWIN);
+ else /* general maintenance screen */
+ helper(gAbookHelp, _("HELP ON ADDRESS BOOK"), HLPD_NONE);
+
+ /*
+ * Helper() may have a Main Menu key. If user types that
+ * they'll set next_screen. We don't have to do anything
+ * special but we want to make sure that that doesn't happen
+ * when we're selecting, even though it shouldn't be
+ * possible because HLPD_SIMPLE is set.
+ */
+ if(are_selecting)
+ ps->next_screen = SCREEN_FUN_NULL; /* probably not needed */
+
+ ps->mangled_screen = 1;
+ break;
+
+
+ /*---------- display other key bindings ------*/
+ case MC_OTHER:
+ warn_other_cmds();
+ what = NextMenu;
+ ps->mangled_footer = 1;
+ break;
+
+
+ /*------------ Unexpand list -----------------*/
+ case MC_UNEXPAND:
+ if(F_OFF(F_EXPANDED_DISTLISTS,ps) &&
+ entry_is_listent(as.top_ent+as.cur_row)){
+ DL_CACHE_S *dlc_to_flush;
+ long global_row_num;
+
+ /*
+ * unexpand list
+ */
+
+ /*
+ * redraw screen starting with first ListEnt
+ */
+ dl = dlist(as.top_ent+as.cur_row);
+ dlc_to_flush = get_dlc(as.top_ent+as.cur_row);
+
+ /*
+ * New cur_row should be the line after the ListHead line
+ * that takes the place of the first address in the list.
+ */
+ as.cur_row = as.cur_row - dl->l_offset;
+
+ /*
+ * If the list header for the new row is off the screen,
+ * adjust it.
+ */
+ if(as.cur_row < 0){ /* center it */
+ global_row_num = dlc_to_flush->global_row -
+ dlc_to_flush->dlcoffset;
+ as.top_ent = first_line(global_row_num - as.l_p_page/2L);
+ as.cur_row = global_row_num - as.top_ent;
+ start_disp = 0;
+ }
+ else if(as.cur_row == 0){ /* just slide up in this case */
+ as.top_ent -= 1;
+ as.cur_row = 1;
+ start_disp = 0;
+ }
+ else
+ start_disp = as.cur_row;
+
+ exp_unset_expanded(pab->address_book->exp,
+ (a_c_arg_t)dlc_to_flush->dlcelnum);
+ flush_dlc_from_cache(dlc_to_flush);
+ ps->mangled_body = 1;
+ ps->mangled_footer = 1;
+ }
+ else
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ "Can't happen in MC_UNEXPAND");
+
+ break;
+
+ /*------ Popup to top level display ----------*/
+ case MC_POPUP:
+ if(F_ON(F_EXPANDED_DISTLISTS,ps) ||
+ !entry_is_listent(as.top_ent+as.cur_row)){
+ DL_CACHE_S dlc_restart;
+ long new_row;
+
+ (void)init_addrbooks((checkedn || as.selections)
+ ? ThreeQuartOpen : HalfOpen,
+ 0, 0, !are_selecting);
+ dlc_restart.adrbk_num = as.cur;
+ dlc_restart.type = DlcTitle;
+ warp_to_dlc(&dlc_restart, 0L);
+ /*
+ * Put the current entry in a nice spot on the screen.
+ * Will everything above fit and still leave ours on screen?
+ */
+ new_row = LINES_PER_ABOOK * as.cur +
+ (((pab->type & GLOBAL) &&
+ (as.how_many_personals > 0 || as.config))
+ ? XTRA_LINES_BETWEEN : 0) +
+ ((as.how_many_personals == 0 && as.config)
+ ? LINES_PER_ADD_LINE : 0);
+ new_row = MAX(MIN(new_row, as.l_p_page-VIS_LINES_PER_ABOOK), 0);
+
+ as.cur_row = new_row;
+ as.top_ent = 0L - as.cur_row;
+ start_disp = 0;
+ ps->mangled_screen = 1;
+ }
+ else
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ "Can't happen in MC_POPUP");
+
+ break;
+
+
+ /*------------- Back to main menu or exit to caller -------*/
+ case MC_EXIT:
+ case MC_MAIN:
+ if(!are_selecting)
+ ps->next_screen = main_menu_screen;
+
+ if(!(are_selecting && as.checkboxes && checkedn > 0)
+ /* TRANSLATORS: we are asking for confirmation about abandonding selections
+ in the address book. */
+ || want_to(_("Really abandon your selections "),
+ 'y', 'x', NO_HELP, WT_NORM) == 'y')
+ quit++;
+ else
+ ps->next_screen = SCREEN_FUN_NULL;
+
+ break;
+
+
+ /*------- Open an address book ----------*/
+ case MC_OPENABOOK:
+openabook:
+ if(entry_is_clickable_title(as.top_ent+as.cur_row)){
+ DL_CACHE_S *dlc_to_flush;
+
+ dlc_to_flush = get_dlc(as.top_ent+as.cur_row);
+ if(dlc_to_flush->type == DlcTitle ||
+ dlc_to_flush->type == DlcClickHereCmb){
+
+ /*
+ * open this addrbook and fill in display list
+ */
+
+ init_abook(pab, Open);
+
+ if(pab->access == ReadWrite || pab->access == ReadOnly){
+ if(!are_selecting && pab->access == ReadOnly)
+ q_status_message1(SM_ORDER, 0, 4, _("AddressBook %s is Read Only"), pab->abnick);
+
+ /*
+ * successful open, burrow into addrbook
+ */
+ if(F_OFF(F_CMBND_ABOOK_DISP,ps_global)){
+ warp_to_top_of_abook(as.cur);
+ as.top_ent = 0L;
+ as.cur_row = 0L;
+ start_disp = 0;
+ ps->mangled_screen = 1;
+ }
+ else{
+ flush_dlc_from_cache(dlc_to_flush);
+ start_disp = as.cur_row;
+ ps->mangled_footer = 1;
+ ps->mangled_body = 1;
+ }
+ }
+ else{ /* open failed */
+ /*
+ * Flush the title line so that it will change into
+ * a permission denied line,
+ */
+ flush_dlc_from_cache(dlc_to_flush);
+ start_disp = as.cur_row;
+ ps->mangled_footer = 1;
+ ps->mangled_body = 1;
+ }
+ }
+ else if(dlc_to_flush->type == DlcTitleNoPerm)
+ q_status_message(SM_ORDER, 0, 4,
+ _("Cannot access address book."));
+ }
+ else
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ "Can't happen in MC_OPENABOOK");
+
+ break;
+
+
+ /*------- Expand addresses in List ------*/
+ case MC_EXPAND:
+expand:
+ if(entry_is_clickable(as.top_ent+as.cur_row)){
+ DL_CACHE_S *dlc_to_flush;
+
+ dlc_to_flush = get_dlc(as.top_ent+as.cur_row);
+ if(dlc_to_flush->type == DlcListClickHere){
+ start_disp = as.cur_row; /* will redraw from here down */
+
+ /*
+ * Mark this list expanded, then flush the
+ * current line from dlc cache. When we get the
+ * line again it will notice the expanded flag and change
+ * the type to DlcListEnt (if any entries).
+ */
+
+ if(F_OFF(F_EXPANDED_DISTLISTS,ps))
+ exp_set_expanded(pab->address_book->exp,
+ (a_c_arg_t)dlc_to_flush->dlcelnum);
+
+ flush_dlc_from_cache(dlc_to_flush);
+ dlc_to_flush = get_dlc(as.top_ent+as.cur_row);
+ /*
+ * If list is empty, back cursor up one.
+ */
+ if(dlc_to_flush->type == DlcListEmpty){
+ as.cur_row--;
+ start_disp--;
+ if(as.cur_row < 0){
+ as.top_ent--;
+ as.cur_row = 0;
+ start_disp = 0;
+ }
+ }
+
+ ps->mangled_body = 1;
+ }
+ }
+ else
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ "Can't happen in MC_EXPAND");
+
+ break;
+
+
+ /*------- Select ---------------*/
+ case MC_CHOICE:
+select:
+ if(are_selecting){
+ /* Select an entry to mail to or a nickname to add to */
+ if(!any_addrs_avail(as.top_ent+as.cur_row)){
+ q_status_message(SM_ORDER | SM_DING, 0, 4,
+ _("No entries in address book. Use ExitSelect to leave address books"));
+ break;
+ }
+
+ if(as.checkboxes || is_addr(as.top_ent+as.cur_row)){
+ BuildTo bldto;
+ char *to = NULL;
+ char *error = NULL;
+ AdrBk_Entry *abe;
+
+ dl = dlist(as.top_ent+as.cur_row);
+
+ if(selecting_one_nick){
+ char nickbuf[MAX_NICKNAME + 1];
+
+ strncpy(nickbuf,
+ ae(as.top_ent+as.cur_row)->nickname,
+ sizeof(nickbuf)-1);
+ nickbuf[sizeof(nickbuf)-1] = '\0';
+ return(cpystr(nickbuf));
+ }
+ else if(as.checkboxes && checkedn <= 0){
+ q_status_message(SM_ORDER, 0, 1,
+ _("Use \"X\" to mark addresses or lists"));
+ break;
+ }
+ else if(as.checkboxes){
+ size_t incr = 100, avail, alloced;
+
+ /*
+ * Have to run through all of the checked entries
+ * in all of the address books.
+ * Put the nicknames together into one long
+ * string with comma separators and let
+ * our_build_address handle the parsing.
+ */
+ to = (char *)fs_get(incr);
+ *to = '\0';
+ avail = incr;
+ alloced = incr;
+
+ for(i = 0; i < as.n_addrbk; i++){
+ EXPANDED_S *next_one;
+ adrbk_cntr_t num;
+ AddrScrn_Disp fake_dl;
+ char *a_string;
+
+ pab = &as.adrbks[i];
+ if(pab->address_book)
+ next_one = pab->address_book->checks;
+ else
+ continue;
+
+ while((num = entry_get_next(&next_one)) != NO_NEXT){
+ abe = adrbk_get_ae(pab->address_book,
+ (a_c_arg_t) num);
+ /*
+ * Since we're picking up address book entries
+ * directly from the address books and have
+ * no knowledge of the display lines they came
+ * from, we don't know the dl's that go with
+ * them. We need to pass a dl to abe_to_nick
+ * but it really is only going to use the
+ * type in this case.
+ */
+ dl = &fake_dl;
+ dl->type = (abe->tag == Single) ? Simple
+ : ListHead;
+ a_string = abe_to_nick_or_addr_string(abe, dl, i);
+
+ while(abe && avail < (size_t)strlen(a_string)+1){
+ alloced += incr;
+ avail += incr;
+ fs_resize((void **)&to, alloced);
+ }
+
+ if(!*to){
+ strncpy(to, a_string, alloced);
+ to[alloced-1] = '\0';
+ }
+ else{
+ strncat(to, ",", alloced-strlen(to)-1);
+ strncat(to, a_string, alloced-strlen(to)-1);
+ }
+
+ avail -= (strlen(a_string) + 1);
+ }
+ }
+
+ /*
+ * Return the nickname list for lcc so that the
+ * correct fullname can make it to the To line.
+ * If we expand it ahead of time, the list name
+ * and first user's fullname will get mushed together.
+ * If an entry doesn't have a nickname then we're
+ * out of luck as far as getting the right entry
+ * in the To line goes.
+ */
+ if(selecting_mult_nicks)
+ return(to);
+
+ bldto.type = Str;
+ bldto.arg.str = to;
+ }
+ else{
+ /* Select an address, but not using checkboxes */
+ if(selecting_mult_nicks){
+ if(dl->type != ListHead && style == SelectAddrLccCom){
+ q_status_message(SM_ORDER, 0, 4,
+ _("You may only select lists for Lcc, use Bcc for other addresses"));
+ break;
+ }
+ else{
+ /*
+ * Even though we're supposedly selecting
+ * nicknames, we have a special case here to
+ * select a single member of a distribution
+ * list. This happens with style SelectNicksCom
+ * which is the regular ^T entry from the
+ * composer, and it allows somebody to mail to
+ * a single member of a distribution list.
+ */
+ abe = ae(as.top_ent+as.cur_row);
+ return(abe_to_nick_or_addr_string(abe, dl, as.cur));
+ }
+ }
+ else{
+ if(dl->type == ListEnt){
+ bldto.type = Str;
+ bldto.arg.str =
+ listmem_from_dl(pab->address_book, dl);
+ }
+ else{
+ bldto.type = Abe;
+ bldto.arg.abe = ae(as.top_ent+as.cur_row);
+ }
+ }
+ }
+
+ (void)our_build_address(bldto, &addr, &error, NULL, save_and_restore);
+ /* Have to rfc1522_decode the addr */
+ if(addr){
+ char *tmp_a_string, *p;
+ ADDRESS *a = NULL;
+
+ if(style == SelectAddrNoFull){
+ tmp_a_string = cpystr(addr);
+ rfc822_parse_adrlist(&a,tmp_a_string,ps->maildomain);
+ fs_give((void **)&tmp_a_string);
+ if(a){
+ fs_give((void **)&addr);
+ addr = cpystr(simple_addr_string(a, tmp_20k_buf,
+ SIZEOF_20KBUF));
+ mail_free_address(&a);
+ }
+ }
+ else if(style == SelectMultNoFull){
+ tmp_a_string = cpystr(addr);
+ rfc822_parse_adrlist(&a,tmp_a_string,ps->maildomain);
+ fs_give((void **)&tmp_a_string);
+ if(a){
+ fs_give((void **)&addr);
+ addr = cpystr(simple_mult_addr_string(a,
+ tmp_20k_buf,
+ SIZEOF_20KBUF,
+ ","));
+ mail_free_address(&a);
+ }
+ }
+ else{
+ size_t len;
+ len = 4*strlen(addr)+1;
+ p = (char *)fs_get(len * sizeof(char));
+ if(rfc1522_decode_to_utf8((unsigned char *)p, len, addr) == (unsigned char *)p){
+ fs_give((void **)&addr);
+ addr = p;
+ }
+ else
+ fs_give((void **)&p);
+ }
+ }
+
+ if(to)
+ fs_give((void **)&to);
+
+ if(error){
+ q_status_message1(SM_ORDER, 3, 4, "%s", error);
+ fs_give((void **)&error);
+ }
+
+ return(addr); /* Caller frees this */
+ }
+ else{
+ if(entry_is_clickable(as.top_ent+as.cur_row))
+ clickable_warning(as.top_ent+as.cur_row);
+ else if(entry_is_askserver(as.top_ent+as.cur_row))
+ q_status_message(SM_ORDER, 3, 4, _("Use select to select an address or addresses from address books"));
+ else
+ q_status_message(SM_ORDER, 3, 4, _("No address selected"));
+
+ break;
+ }
+ }
+ else
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ "Can't happen in MC_CHOICE");
+
+ break;
+
+
+ /*----- View an entry --------------------*/
+ case MC_VIEW_ENTRY:
+view:
+ if(empty){
+ empty_warning(as.top_ent+as.cur_row);
+ break;
+ }
+
+ view_abook_entry(ps, as.top_ent+as.cur_row);
+ break;
+
+
+ /*----- Add new ---------*/
+ case MC_ADD:
+ {long old_l_p_p, old_top_ent, old_cur_row;
+
+ if(adrbk_check_all_validity_now()){
+ if(resync_screen(pab, style, checkedn)){
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Address book changed. AddNew cancelled. Try again."));
+ ps->mangled_screen = 1;
+ break;
+ }
+ }
+
+ if(rdonly){
+ q_status_message(SM_ORDER, 0, 4, _("AddressBook is Read Only"));
+ break;
+ }
+
+ warped = 0;
+ dprint((9,
+ "Calling edit_entry to add entry manually\n"));
+ /* TRANSLATORS: add as in add a new entry to something */
+ edit_entry(pab->address_book, (AdrBk_Entry *)NULL, NO_NEXT,
+ NotSet, 0, &warped, _("add"));
+
+ /*
+ * Warped means we got plopped down somewhere in the display
+ * list so that we don't know where we are relative to where
+ * we were before we warped. The current line number will
+ * be zero, since that is what the warp would have set.
+ */
+ if(warped){
+ as.top_ent = first_line(0L - as.l_p_page/2L);
+ as.cur_row = 0L - as.top_ent;
+ }
+ else{
+ /*
+ * If we didn't warp, that means we didn't change at all,
+ * so keep old screen.
+ */
+ old_l_p_p = as.l_p_page;
+ old_top_ent = as.top_ent;
+ old_cur_row = as.cur_row;
+ }
+
+ /* Window size may have changed while in pico. */
+ ab_resize();
+
+ /* fix up what ab_resize messed up */
+ if(!warped && old_l_p_p == as.l_p_page){
+ as.top_ent = old_top_ent;
+ as.cur_row = old_cur_row;
+ as.old_cur_row = old_cur_row;
+ }
+ }
+
+ ps->mangled_screen = 1;
+ break;
+
+
+ /*---------- Add a new address book -------------------*/
+ case MC_ADDABOOK:
+ {int old_abook_num, new_abook_num, new_row, global;
+ int stay_put, old_cur_row;
+
+add_abook:
+ stay_put = (dlist(as.top_ent+as.cur_row)->type == AddFirstPers ||
+ dlist(as.top_ent+as.cur_row)->type == AddFirstGlob);
+ old_cur_row = as.cur_row;
+ old_abook_num = adrbk_num_from_lineno(as.top_ent+as.cur_row);
+ global = (dlist(as.top_ent+as.cur_row)->type == AddFirstGlob ||
+ (dlist(as.top_ent+as.cur_row)->type != AddFirstPers &&
+ (old_abook_num >= as.how_many_personals) &&
+ as.n_addrbk > 0));
+ if((new_abook_num =
+ ab_add_abook(global,
+ stay_put ? -1
+ : adrbk_num_from_lineno(as.top_ent+as.cur_row)))
+ >= 0){
+ DL_CACHE_S dlc_restart;
+
+ (void)init_addrbooks(HalfOpen, 0, 0, !are_selecting);
+
+ erase_checks();
+ erase_selections();
+
+ dlc_restart.adrbk_num = new_abook_num;
+ dlc_restart.type = DlcTitle;
+ warp_to_dlc(&dlc_restart, 0L);
+
+ /*
+ * Put the current entry in a nice spot on the screen.
+ */
+ new_row = old_cur_row +
+ (stay_put
+ ? 0
+ : LINES_PER_ABOOK*(new_abook_num - old_abook_num));
+
+ if(new_row >= 0 &&
+ new_row <= (as.l_p_page-VIS_LINES_PER_ABOOK))/* ok, use it */
+ as.cur_row = new_row;
+ else{
+ /*
+ * Will everything above fit and still leave ours on screen?
+ */
+ new_row = LINES_PER_ABOOK * new_abook_num +
+ ((global &&
+ (as.how_many_personals > 0 || as.config))
+ ? XTRA_LINES_BETWEEN : 0) +
+ ((as.how_many_personals == 0 && as.config)
+ ? LINES_PER_ADD_LINE : 0);
+ new_row = MAX(MIN(new_row,
+ as.l_p_page - VIS_LINES_PER_ABOOK), 0);
+ as.cur_row = new_row;
+ }
+
+ as.top_ent = 0L - as.cur_row;
+ dprint((5, "addrbook added: %s\n",
+ as.adrbks[new_abook_num].filename
+ ? as.adrbks[new_abook_num].filename : "?"));
+ }
+
+ ps->mangled_screen = 1;
+
+ }
+
+ break;
+
+
+ /*---------- Change address book config -------------------*/
+ case MC_EDITABOOK:
+change_abook:
+ if(entry_is_clickable_title(as.top_ent+as.cur_row)){
+ int abook_num, old_cur_row, global;
+ long old_top_ent;
+ DL_CACHE_S dlc_restart, *dlc;
+ char *serv = NULL, *folder = NULL, *p, *q;
+ char *nick = NULL, *file = NULL;
+
+ abook_num = adrbk_num_from_lineno(as.top_ent+as.cur_row);
+ global = (abook_num >= as.how_many_personals);
+ old_cur_row = as.cur_row;
+ old_top_ent = as.top_ent;
+ dlc = get_dlc(as.top_ent+as.cur_row);
+ dlc_restart = *dlc;
+
+ if(global)
+ q = ps_global->VAR_GLOB_ADDRBOOK[abook_num -
+ as.how_many_personals];
+ else
+ q = ps_global->VAR_ADDRESSBOOK[abook_num];
+
+ get_pair(q, &nick, &file, 0, 0);
+
+ if(nick && !*nick)
+ fs_give((void **)&nick);
+
+ if(file && *file == '{'){
+ q = file + 1;
+ if((p = strindex(file, '}'))){
+ *p = '\0';
+ serv = q;
+ folder = p+1;
+ }
+ else{
+ q_status_message1(SM_ORDER|SM_DING, 0, 4,
+ _("Missing \"}\" in config: %s"), q);
+ if(nick)
+ fs_give((void **)&nick);
+ if(file)
+ fs_give((void **)&file);
+
+ break;
+ }
+ }
+ else
+ folder = file;
+
+ if(ab_edit_abook(global, abook_num, serv, folder, nick) >= 0){
+ (void)init_addrbooks(HalfOpen, 0, 0, !are_selecting);
+
+ erase_checks();
+ erase_selections();
+
+ dlc_restart.type = DlcTitle;
+ warp_to_dlc(&dlc_restart, old_top_ent+(long)old_cur_row);
+ as.cur_row = old_cur_row;
+ as.top_ent = old_top_ent;
+
+ dprint((5, "addrbook config edited: %s\n",
+ as.adrbks[dlc_restart.adrbk_num].filename
+ ? as.adrbks[dlc_restart.adrbk_num].filename : "?"));
+ }
+
+ if(nick)
+ fs_give((void **)&nick);
+ if(file)
+ fs_give((void **)&file);
+
+ ps->mangled_screen = 1;
+ }
+ else
+ /* TRANSLATORS: the user tried to change the current line of the address
+ book but the line could not be changed */
+ q_status_message(SM_ORDER, 0, 4, _("Not a changeable line"));
+
+ break;
+
+
+ /*---------- Delete an address book -------------------*/
+ case MC_DELABOOK:
+ if(as.n_addrbk == 0){
+ q_status_message(SM_ORDER, 0, 4, _("Nothing to delete"));
+ break;
+ }
+
+ {char *err = NULL;
+
+ if(ab_del_abook(as.top_ent+as.cur_row, command_line, &err) >= 0){
+ DL_CACHE_S dlc_restart;
+ int new_abook_num, old_abook_num, old_pers, old_glob, new_row;
+
+ /*
+ * Careful, these are only ok because ab_del_abook didn't
+ * mess with the as globals, like as.how_many_personals.
+ * The addrbook_reset does reset them, of course.
+ */
+ old_abook_num = adrbk_num_from_lineno(as.top_ent+as.cur_row);
+ old_pers = as.how_many_personals;
+ old_glob = as.n_addrbk - as.how_many_personals;
+ addrbook_reset();
+ (void)init_addrbooks(HalfOpen, 0, 0, !are_selecting);
+
+ erase_checks();
+ erase_selections();
+
+ if(old_abook_num >= as.n_addrbk) /* we deleted last addrbook */
+ new_abook_num = as.n_addrbk - 1;
+ else
+ new_abook_num = old_abook_num;
+
+ /*
+ * Pick a line to highlight and center
+ */
+ if(as.how_many_personals == 0 && old_pers == 1)
+ dlc_restart.type = DlcPersAdd;
+ else if((as.n_addrbk - as.how_many_personals) == 0 &&
+ old_glob == 1)
+ dlc_restart.type = DlcGlobAdd;
+ else if(as.n_addrbk == 0)
+ dlc_restart.type = DlcPersAdd;
+ else{
+ dlc_restart.adrbk_num = new_abook_num;
+ dlc_restart.type = DlcTitle;
+ }
+
+ warp_to_dlc(&dlc_restart, 0L);
+
+ /*
+ * Will everything above fit and still leave ours on screen?
+ */
+ if(dlc_restart.type == DlcTitle)
+ new_row = LINES_PER_ABOOK * new_abook_num +
+ (((as.adrbks[new_abook_num].type & GLOBAL) &&
+ (as.how_many_personals > 0 || as.config))
+ ? XTRA_LINES_BETWEEN : 0) +
+ ((as.how_many_personals == 0 && as.config)
+ ? LINES_PER_ADD_LINE : 0);
+ else if(dlc_restart.type == DlcGlobAdd)
+ new_row = LINES_PER_ABOOK * as.n_addrbk +
+ ((as.how_many_personals > 0 || as.config)
+ ? XTRA_LINES_BETWEEN : 0) +
+ ((as.how_many_personals == 0 && as.config)
+ ? LINES_PER_ADD_LINE : 0);
+ else
+ new_row = 0;
+
+ new_row = MAX(MIN(new_row, as.l_p_page-VIS_LINES_PER_ABOOK), 0);
+ as.cur_row = new_row;
+ as.top_ent = 0L - as.cur_row;
+ start_disp = 0;
+ ps->mangled_body = 1;
+ ps->mangled_footer = 1;
+ /* TRANSLATORS: This is just comforting confirmation that the
+ address book being deleted was successfully deleted */
+ q_status_message(SM_ORDER, 0, 3, _("Address book deleted"));
+ }
+ else{
+ if(err){
+ q_status_message(SM_ORDER, 0, 4, err);
+ dprint((5, "addrbook delete failed: %s\n",
+ err ? err : "?"));
+ }
+ }
+
+ }
+
+ break;
+
+
+ /*---- Reorder an addressbook list ---------*/
+ case MC_SHUFFLE:
+ if(entry_is_addkey(as.top_ent+as.cur_row)){
+ q_status_message(SM_ORDER, 0, 4,
+ _("Highlight entry you wish to shuffle"));
+ break;
+ }
+
+ {int slide;
+ char *msg = NULL;
+ int ret, new_anum;
+
+ if((ret = ab_shuffle(pab, &slide, command_line, &msg)) > 0){
+ DL_CACHE_S dlc_restart;
+ int new_row;
+
+ new_anum = ret - 1; /* see ab_shuffle return value */
+ addrbook_reset();
+ (void)init_addrbooks(HalfOpen, 0, 0, !are_selecting);
+
+ erase_checks();
+ erase_selections();
+
+ /* put cursor on new_anum */
+ dlc_restart.adrbk_num = new_anum;
+ dlc_restart.type = DlcTitle;
+ warp_to_dlc(&dlc_restart, 0L);
+ /*
+ * Will everything above fit and still leave ours on screen?
+ */
+ new_row = LINES_PER_ABOOK * new_anum +
+ (((as.adrbks[new_anum].type & GLOBAL) &&
+ (as.how_many_personals > 0 || as.config))
+ ? XTRA_LINES_BETWEEN : 0) +
+ ((as.how_many_personals == 0 && as.config)
+ ? LINES_PER_ADD_LINE : 0);
+ new_row = MAX(MIN(new_row, as.l_p_page-VIS_LINES_PER_ABOOK), 0);
+ as.cur_row = new_row;
+ as.top_ent = 0L - as.cur_row;
+ start_disp = 0;
+ ps->mangled_body = 1;
+ }
+ else if(ret == 0){
+ DL_CACHE_S *dlc_to_flush;
+
+ if(slide < 0){ /* moved it up */
+ as.cur_row += slide;
+ start_disp = MAX(as.cur_row - 1, 0);
+ dlc_to_flush = get_dlc(as.top_ent+as.cur_row);
+ /*
+ * If we backed off the top of the screen, just
+ * inch the display up so we can see it.
+ */
+ if(as.cur_row < 0){
+ as.top_ent += as.cur_row; /* cur_row is negative */
+ as.cur_row = 0;
+ start_disp = 0;
+ }
+ }
+ else{ /* moved it down */
+ start_disp = as.cur_row;
+ dlc_to_flush = get_dlc(as.top_ent+as.cur_row);
+ as.cur_row += slide;
+ if(as.cur_row > as.l_p_page - VIS_LINES_PER_ABOOK){
+ as.top_ent += (as.cur_row -
+ (as.l_p_page - VIS_LINES_PER_ABOOK));
+ as.cur_row = MAX(as.l_p_page-VIS_LINES_PER_ABOOK, 0);
+ start_disp = 0;
+ }
+ }
+
+ flush_dlc_from_cache(dlc_to_flush);
+ ps->mangled_body = 1;
+ }
+
+ q_status_message(SM_ORDER, 0, 3,
+ msg ? msg :
+ (ret < 0) ? _("Shuffle failed") :
+ _("Address books shuffled"));
+ if(ret < 0)
+ dprint((5, "addrbook shuffle failed: %s\n",
+ msg ? msg : "?"));
+
+ if(msg)
+ fs_give((void **)&msg);
+
+ }
+
+ break;
+
+
+ /*----------------------- Move Up ---------------------*/
+ case MC_CHARUP:
+ case MC_PREVITEM:
+ r = prev_selectable_line(as.cur_row+as.top_ent, &new_line);
+ if(r == 0){
+ /* find first line so everything is displayed */
+ new_top_ent = as.cur_row+as.top_ent;
+ for(dl=dlist(new_top_ent-1);
+ dl->type != Beginning;
+ dl = dlist((--new_top_ent) - 1))
+ ;
+
+ if(new_top_ent == as.top_ent ||
+ (as.cur_row + (as.top_ent-new_top_ent) > as.l_p_page - 1)){
+ q_status_message(SM_INFO, 0, 1, _("Already on first line."));
+ }
+ else{
+ as.cur_row += (as.top_ent - new_top_ent);
+ as.top_ent = new_top_ent;
+ start_disp = 0;
+ ps->mangled_body = 1;
+ }
+
+ break;
+ }
+
+ i = ((cmd == MC_CHARUP)
+ && dlist(as.top_ent - 1L)->type != Beginning)
+ ? MIN(HS_MARGIN(ps), as.cur_row) : 0;
+ as.cur_row = new_line - as.top_ent;
+ if(as.cur_row - i < 0){
+ if(cmd == MC_CHARUP){
+ /*-- Past top of page --*/
+ as.top_ent += (as.cur_row - i);
+ as.cur_row = i;
+ }
+ else{
+ new_top_ent = first_line(as.top_ent - as.l_p_page);
+ as.cur_row += (as.top_ent - new_top_ent);
+ as.top_ent = new_top_ent;
+ /* if it is still off screen */
+ if(as.cur_row - i < 0){
+ as.top_ent += (as.cur_row - i);
+ as.cur_row = i;
+ }
+ }
+
+ start_disp = 0;
+ ps->mangled_body = 1;
+ }
+ else
+ current_changed_flag++;
+
+ break;
+
+
+ /*------------------- Move Down -------------------*/
+ case MC_CHARDOWN:
+ case MC_NEXTITEM:
+ r = next_selectable_line(as.cur_row+as.top_ent, &new_line);
+ if(r == 0){
+ long new_end_line;
+
+ /* find last line so everything is displayed */
+ new_end_line = as.cur_row+as.top_ent;
+ for(dl=dlist(new_end_line+1);
+ dl->type != End;
+ dl = dlist((++new_end_line)+1))
+ ;
+
+ if(new_end_line - as.top_ent <= as.l_p_page - 1 ||
+ as.cur_row - (new_end_line-as.top_ent-(as.l_p_page-1)) < 0){
+ q_status_message(SM_INFO, 0, 1, _("Already on last line."));
+ }
+ else{
+ as.cur_row -= (new_end_line-as.top_ent-(as.l_p_page-1));
+ as.top_ent += (new_end_line-as.top_ent-(as.l_p_page-1));
+ start_disp = 0;
+ ps->mangled_body = 1;
+ }
+
+ break;
+ }
+
+ /* adjust for scrolling margin */
+ i = (cmd == MC_CHARDOWN) ? HS_MARGIN(ps) : 0;
+ as.cur_row = new_line - as.top_ent;
+ if(as.cur_row >= as.l_p_page - i){
+ if(cmd == MC_CHARDOWN){
+ /*-- Past bottom of page --*/
+ as.top_ent += (as.cur_row - (as.l_p_page - i) + 1);
+ as.cur_row = (as.l_p_page - i) - 1;
+ }
+ else{
+ /*-- Changed pages --*/
+ as.top_ent += as.l_p_page;
+ as.cur_row -= as.l_p_page;
+ /* if it is still off screen */
+ if(as.cur_row >= as.l_p_page - i){
+ as.top_ent += (as.cur_row - (as.l_p_page - i) + 1);
+ as.cur_row = (as.l_p_page - i) - 1;
+ }
+ }
+
+ start_disp = 0;
+ ps->mangled_body = 1;
+ }
+ else
+ current_changed_flag++;
+
+ break;
+
+
+#ifdef MOUSE
+ case MC_MOUSE:
+ {
+ MOUSEPRESS mp;
+
+ /*
+ * Get the mouse down. Convert to content row number.
+ * If the row is selectable, do the single or double click
+ * operation.
+ */
+ mouse_get_last(NULL, &mp);
+ mp.row -= HEADER_ROWS(ps);
+ if(line_is_selectable(as.top_ent + mp.row)){
+ if(mp.button == M_BUTTON_LEFT){
+ if(mp.doubleclick){
+ /*
+ * A mouse double click does the default action to
+ * the selected line. Since we need to do a goto
+ * to get there, we have this ugly bit of code.
+ *
+ * On the first mouse click we go around the loop
+ * once and set def_key appropriately for the
+ * line as.top_ent+mp.row, which will then be
+ * the same as as.top_ent+as.cur_row.
+ */
+ switch(km->keys[def_key].bind.cmd){
+ case MC_CHOICE:
+ if (as.checkboxes) goto togglex;
+ else goto select;
+ case MC_OPENABOOK:
+ goto openabook;
+ case MC_EXPAND:
+ goto expand;
+ case MC_TOGGLE:
+ goto togglex;
+ case MC_SELALL:
+ goto selall;
+ case MC_VIEW_ENTRY:
+ goto view;
+ case MC_ADDABOOK:
+ goto add_abook;
+ case MC_EDITABOOK:
+ goto change_abook;
+#ifdef ENABLE_LDAP
+ case MC_QUERY_SERV:
+ goto q_server;
+#endif
+ default:
+ q_status_message(SM_INFO, 0, 1,
+ "Can't happen in MC_MOUSE");
+ break;
+ }
+ }
+
+ as.cur_row = mp.row;
+ current_changed_flag++;
+ }
+ else if(mp.button == M_BUTTON_RIGHT){
+#ifdef _WINDOWS
+ int need_redraw;
+#endif
+ as.cur_row = mp.row;
+ current_changed_flag++;
+
+
+#ifdef _WINDOWS
+ need_redraw = calculate_field_widths();
+
+ /*---------- Update the current entry, (move or change) -------*/
+ display_book(need_redraw ? 0 : as.cur_row,
+ as.cur_row,
+ as.old_cur_row,
+ need_redraw,
+ &cursor_pos);
+
+ as.old_cur_row = as.cur_row;
+ current_changed_flag = 0;
+ as.cur = cur_addr_book();
+ pab = (as.n_addrbk &&
+ !entry_is_askserver(as.top_ent+as.cur_row))
+ ? &as.adrbks[as.cur] : NULL;
+
+ if(!mp.doubleclick){
+ MPopup addr_pu[20];
+ int n, i;
+
+ /* Make what they clicked "current" */
+ memset(addr_pu, 0, 20 * sizeof(MPopup));
+
+ addr_pu[n = 0].type = tQueue;
+ addr_pu[n].data.val = km->keys[def_key].bind.ch[0];
+ addr_pu[n].label.style = lNormal;
+ addr_pu[n++].label.string = km->keys[def_key].label;
+
+ if((i = menu_clear_binding(km, '<')) != MC_UNKNOWN){
+ menu_add_binding(km, '<', i);
+
+ if((i = menu_binding_index(km, i)) >= 0){
+ addr_pu[n++].type = tSeparator;
+
+ addr_pu[n].type = tQueue;
+ addr_pu[n].data.val = km->keys[i].bind.ch[0];
+ addr_pu[n].label.style = lNormal;
+ addr_pu[n++].label.string = km->keys[i].label;
+ }
+ }
+
+ addr_pu[n].type = tTail;
+
+ mswin_popup(addr_pu);
+ }
+#endif
+ }
+ }
+ }
+ break;
+#endif
+
+
+ /*------------- Page Up or Down --------*/
+ case MC_PAGEUP:
+ case MC_PAGEDN:
+ if(cmd == MC_PAGEUP){
+ /* find first line on prev page */
+ new_top_ent = first_line(as.top_ent - as.l_p_page);
+ if(new_top_ent == NO_LINE)
+ break;
+
+ /* find first selectable line */
+ fl = first_selectable_line(new_top_ent);
+
+ /* If we didn't move, we'd better move now */
+ if(fl == as.top_ent+as.cur_row){
+ if(!prev_selectable_line(as.cur_row+as.top_ent, &new_line)){
+ long lineno;
+
+ /* find first line so everything is displayed */
+ lineno = as.cur_row+as.top_ent;
+ for(dl=dlist(lineno);
+ dl->type != Beginning;
+ dl = dlist(--lineno))
+ ;
+
+ /*
+ * If this new_top_ent is the same as the old_top_ent
+ * we'll get the warning message.
+ */
+ new_top_ent = first_line(lineno);
+ if(fl - new_top_ent >= as.l_p_page)
+ new_top_ent += (fl - new_top_ent - as.l_p_page + 1);
+ }
+ else
+ fl = new_line;
+ }
+
+ if(fl == NO_LINE)
+ break;
+
+ if(as.top_ent == new_top_ent && as.cur_row == (fl-as.top_ent)){
+ q_status_message(SM_INFO, 0, 1, _("Already on first page."));
+ break;
+ }
+
+ if(as.top_ent == new_top_ent)
+ current_changed_flag++;
+ else
+ as.top_ent = new_top_ent;
+ }
+ else{ /* Down */
+ /* find first selectable line on next page */
+ fl = first_selectable_line(as.top_ent + as.l_p_page);
+ if(fl == NO_LINE)
+ break;
+
+ /* if there is another page, scroll */
+ if(fl - as.top_ent >= as.l_p_page){
+ new_top_ent = as.top_ent + as.l_p_page;
+ }
+ /* on last page already */
+ else{
+ new_top_ent = as.top_ent;
+ if(as.cur_row == (fl - as.top_ent)){ /* no change */
+ long new_end_line;
+
+ /* find last line so everything is displayed */
+ new_end_line = as.cur_row+as.top_ent;
+ for(dl=dlist(new_end_line+1);
+ dl->type != End;
+ dl = dlist((++new_end_line)+1))
+ ;
+
+ if(new_end_line - as.top_ent <= as.l_p_page - 1 ||
+ as.cur_row -
+ (new_end_line-as.top_ent-(as.l_p_page-1)) < 0){
+ q_status_message(SM_INFO, 0, 1,
+ _("Already on last page."));
+ }
+ else{
+ as.cur_row -=
+ (new_end_line-as.top_ent-(as.l_p_page-1));
+ as.top_ent +=
+ (new_end_line-as.top_ent-(as.l_p_page-1));
+ start_disp = 0;
+ ps->mangled_body = 1;
+ }
+
+ break;
+ }
+ }
+
+ if(as.top_ent == new_top_ent)
+ current_changed_flag++;
+ else
+ as.top_ent = new_top_ent;
+ }
+
+ /*
+ * Stuff in common for up or down.
+ */
+ as.cur_row = fl - as.top_ent;
+
+ /* if it is still off screen */
+ if(as.cur_row < 0){
+ as.top_ent += as.cur_row;
+ as.cur_row = 0;
+ }
+ else if(as.cur_row >= as.l_p_page){
+ as.top_ent += (as.cur_row - as.l_p_page + 1);
+ as.cur_row = as.l_p_page - 1;
+ }
+
+ if(!current_changed_flag){
+ ps->mangled_body = 1;
+ start_disp = 0;
+ }
+
+ break;
+
+
+ /*------------- Delete item from addrbook ---------*/
+ case MC_DELETE:
+ if(adrbk_check_all_validity_now()){
+ if(resync_screen(pab, style, checkedn)){
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Address book changed. Delete cancelled. Try again."));
+ ps->mangled_screen = 1;
+ break;
+ }
+ }
+
+ if(!any_addrs_avail(as.top_ent+as.cur_row)){
+ q_status_message(SM_ORDER, 0, 4, _("No entries to delete"));
+ break;
+ }
+
+ if(entry_is_clickable(as.top_ent+as.cur_row)){
+ clickable_warning(as.top_ent+as.cur_row);
+ break;
+ }
+
+ if(rdonly){
+ q_status_message(SM_ORDER, 0, 4, _("AddressBook is Read Only"));
+ break;
+ }
+
+ if(empty){
+ empty_warning(as.top_ent+as.cur_row);
+ break;
+ }
+
+ warped = 0;
+ did_delete = single_entry_delete(pab->address_book,
+ as.cur_row+as.top_ent,
+ &warped);
+ ps->mangled_footer = 1;
+ if(did_delete){
+ if(warped){
+ as.top_ent = first_line(0L - as.l_p_page/2L);
+ as.cur_row = 0L - as.top_ent;
+ start_disp = 0;
+ }
+ else{
+ /*
+ * In case the line we're now at is not a selectable
+ * field.
+ */
+ new_line = first_selectable_line(as.cur_row+as.top_ent);
+ if(new_line != NO_LINE
+ && new_line != as.cur_row+as.top_ent){
+ as.cur_row = new_line - as.top_ent;
+ if(as.cur_row < 0){
+ as.top_ent -= as.l_p_page;
+ as.cur_row += as.l_p_page;
+ }
+ else if(as.cur_row >= as.l_p_page){
+ as.top_ent += as.l_p_page;
+ as.cur_row -= as.l_p_page;
+ }
+ }
+
+ start_disp = MIN(as.cur_row, as.old_cur_row);
+ }
+
+ ps->mangled_body = 1;
+ }
+
+ break;
+
+
+ /*------------- Toggle checkbox ---------*/
+ case MC_TOGGLE:
+togglex:
+ if(!any_addrs_avail(as.top_ent+as.cur_row)){
+ q_status_message(SM_ORDER, 0, 4, _("No entries to select"));
+ break;
+ }
+
+ if(entry_is_clickable(as.top_ent+as.cur_row)){
+ clickable_warning(as.top_ent+as.cur_row);
+ break;
+ }
+
+ if(empty){
+ empty_warning(as.top_ent+as.cur_row);
+ break;
+ }
+
+ if(is_addr(as.top_ent+as.cur_row)){
+ dl = dlist(as.top_ent+as.cur_row);
+
+ if(style == SelectAddrLccCom && dl->type == ListEnt)
+ q_status_message(SM_ORDER, 0, 4,
+ _("You may only select whole lists for Lcc"));
+ else if(style == SelectAddrLccCom && dl->type != ListHead)
+ q_status_message(SM_ORDER, 0, 4,
+ _("You may only select lists for Lcc, use Bcc for personal entries"));
+ else if(dl->type == ListHead || dl->type == Simple){
+ current_changed_flag++;
+ if(entry_is_checked(pab->address_book->checks,
+ (a_c_arg_t)dl->elnum)){
+ entry_unset_checked(pab->address_book->checks,
+ (a_c_arg_t)dl->elnum);
+ checkedn--;
+ if(checkedn == 0)
+ setall_changed++;
+ }
+ else{
+ entry_set_checked(pab->address_book->checks,
+ (a_c_arg_t)dl->elnum);
+ if(checkedn == 0)
+ setall_changed++;
+
+ checkedn++;
+ }
+ }
+ else
+ q_status_message(SM_ORDER, 0, 4,
+ _("You may not select list members, only whole lists or personal entries"));
+ }
+ else
+ q_status_message(SM_ORDER, 0, 4,
+ _("You may only select addresses or lists"));
+
+ break;
+
+
+ /*------ Turn all checkboxes on ---------*/
+ case MC_SELALL:
+selall:
+ if(!any_addrs_avail(as.top_ent+as.cur_row)){
+ q_status_message(SM_ORDER, 0, 4, _("No entries to select"));
+ break;
+ }
+
+ if(entry_is_clickable(as.top_ent+as.cur_row)){
+ clickable_warning(as.top_ent+as.cur_row);
+ break;
+ }
+
+ if(empty){
+ empty_warning(as.top_ent+as.cur_row);
+ break;
+ }
+
+ {
+ adrbk_cntr_t num, ab_count;
+
+ ab_count = adrbk_count(pab->address_book);
+ setall_changed++;
+ if(checkedn){ /* unset All */
+ for(num = 0; num < ab_count; num++){
+ if(entry_is_checked(pab->address_book->checks,
+ (a_c_arg_t)num)){
+ entry_unset_checked(pab->address_book->checks,
+ (a_c_arg_t)num);
+ checkedn--;
+ }
+ }
+ }
+ else{ /* set All */
+ for(num = 0; num < ab_count; num++){
+ if(!entry_is_checked(pab->address_book->checks,
+ (a_c_arg_t)num)){
+ entry_set_checked(pab->address_book->checks,
+ (a_c_arg_t)num);
+ checkedn++;
+ }
+ }
+ }
+
+ ps->mangled_body = 1;
+ start_disp = 0;
+ }
+
+ break;
+
+
+ /*---------- Turn on ListMode -----------*/
+ case MC_LISTMODE:
+ as.checkboxes = 1;
+ for(i = 0; i < as.n_addrbk; i++){
+ pab = &as.adrbks[i];
+ init_disp_form(pab, ps->VAR_ABOOK_FORMATS, i);
+ }
+
+ (void)calculate_field_widths();
+ ps->mangled_footer = 1;
+ ps->mangled_body = 1;
+ start_disp = 0;
+ q_status_message(SM_ORDER, 0, 4,
+ _("Use \"X\" to select addresses or lists"));
+ break;
+
+
+ /*--------- Compose -----------*/
+ case MC_COMPOSE:
+ (void)ab_compose_to_addr(as.top_ent+as.cur_row, 0, 0);
+ break;
+
+
+ /*--------- Alt Compose -----------*/
+ case MC_ROLE:
+ (void)ab_compose_to_addr(as.top_ent+as.cur_row, 0, 1);
+ break;
+
+
+#ifdef ENABLE_LDAP
+ /*------ Query Directory ------*/
+ case MC_QUERY_SERV:
+q_server:
+ {char *err_mess = NULL, **err_mess_p;
+ int query_type;
+
+ if(!directory_ok){
+ q_status_message(SM_ORDER, 0, 4,
+ (style == SelectAddrLccCom)
+ ? "Can't search server for Lcc"
+ : "Can't search server from here");
+ break;
+ }
+ else if(as.checkboxes){
+ q_status_message(SM_ORDER, 0, 4,
+ "Can't search server when using ListMode");
+ break;
+ }
+
+ if(error_message)
+ err_mess_p = error_message;
+ else
+ err_mess_p = &err_mess;
+
+ /*
+ * The query lines are indexed by their adrbk_num. (Just using
+ * that because it was handy and not used for addrbooks.)
+ */
+ query_type = adrbk_num_from_lineno(as.top_ent+as.cur_row);
+
+ if((addr = query_server(ps, are_selecting, &quit, query_type,
+ err_mess_p)) != NULL){
+ if(are_selecting)
+ return(addr);
+
+ fs_give((void **)&addr);
+ }
+
+ if(err_mess_p && *err_mess_p && !error_message){
+ q_status_message(SM_ORDER, 0, 4, *err_mess_p);
+ fs_give((void **)err_mess_p);
+ }
+ }
+
+ break;
+#endif /* ENABLE_LDAP */
+
+
+ /*----------- Where is (search) ----------------*/
+ case MC_WHEREIS:
+ warped = 0;
+ new_top_ent = ab_whereis(&warped, command_line);
+
+ if(new_top_ent != NO_LINE){
+ if(warped || new_top_ent != as.top_ent){
+ as.top_ent = new_top_ent;
+ start_disp = 0;
+ ps->mangled_body = 1;
+ }
+ else
+ current_changed_flag++;
+ }
+
+ ps->mangled_footer = 1;
+ break;
+
+
+ /*----- Select entries to work on --*/
+ case MC_SELECT:
+ if(!any_addrs_avail(as.top_ent+as.cur_row)){
+ q_status_message(SM_ORDER, 0, 4, _("No entries to select"));
+ break;
+ }
+
+ if(!cur_is_open()){
+ if(entry_is_askserver(as.top_ent+as.cur_row))
+ q_status_message(SM_ORDER, 0, 4,
+ _("Select is only available from within an expanded address book"));
+ else
+ clickable_warning(as.top_ent+as.cur_row);
+
+ break;
+ }
+
+ dl = dlist(as.top_ent+as.cur_row);
+ if(dl->type == Empty){
+ empty_warning(as.top_ent+as.cur_row);
+ break;
+ }
+
+ {int were_selections = as.selections;
+
+ ab_select(ps, pab ? pab->address_book : NULL,
+ as.top_ent+as.cur_row, command_line, &start_disp);
+
+ if((!were_selections && as.selections)
+ || (were_selections && !as.selections)){
+ ps->mangled_footer = 1;
+ for(i = 0; i < as.n_addrbk; i++)
+ init_disp_form(&as.adrbks[i],
+ ps->VAR_ABOOK_FORMATS, i);
+ }
+ }
+
+ break;
+
+
+ /*----------- Select current entry ----------*/
+ case MC_SELCUR:
+ if(!any_addrs_avail(as.top_ent+as.cur_row)){
+ q_status_message(SM_ORDER, 0, 4, _("No entries to select"));
+ break;
+ }
+
+ if(entry_is_clickable(as.top_ent+as.cur_row)){
+ clickable_warning(as.top_ent+as.cur_row);
+ break;
+ }
+
+ if(empty){
+ empty_warning(as.top_ent+as.cur_row);
+ break;
+ }
+
+ if(is_addr(as.top_ent+as.cur_row)){
+ dl = dlist(as.top_ent+as.cur_row);
+
+ if(dl->type == ListHead || dl->type == Simple){
+ int do_init_disp = 0;
+ long ll;
+
+ current_changed_flag++;
+
+ if(entry_is_selected(pab->address_book->selects,
+ (a_c_arg_t)dl->elnum)){
+ DL_CACHE_S *dlc, dlc_restart;
+
+ as.selections--;
+ if(as.selections == 0)
+ do_init_disp++;
+
+ entry_unset_selected(pab->address_book->selects,
+ (a_c_arg_t)dl->elnum);
+
+ if(as.zoomed){
+ dlc = get_dlc(as.top_ent+as.cur_row);
+ if(as.selections){
+ flush_dlc_from_cache(dlc);
+ dlc = get_dlc(as.top_ent+as.cur_row);
+ ps->mangled_body = 1;
+ if(dlc->type == DlcEnd){
+ r = prev_selectable_line(as.cur_row +
+ as.top_ent,
+ &new_line);
+ if(r){
+ as.cur_row = new_line - as.top_ent;
+ if(as.cur_row < 0){
+ as.top_ent += as.cur_row;
+ as.cur_row = 0;
+ start_disp = 0;
+ }
+ else
+ start_disp = as.cur_row;
+ }
+ }
+ else
+ start_disp = MAX(as.cur_row-1,0);
+ }
+ else{
+ dlc_restart = *dlc;
+ as.zoomed = 0;
+ q_status_message(SM_ORDER, 0, 2,
+ _("Zoom Mode is now off, no entries selected"));
+
+ warp_to_dlc(&dlc_restart, 0L);
+ /* put current entry in middle of screen */
+ as.top_ent =
+ first_line(0L - (long)as.l_p_page/2L);
+ as.cur_row = 0L - as.top_ent;
+ ps->mangled_body = 1;
+ start_disp = 0;
+ }
+ }
+ else if(F_OFF(F_UNSELECT_WONT_ADVANCE,ps_global)){
+
+ r = next_selectable_line(as.cur_row+as.top_ent,
+ &new_line);
+ if(r){
+
+ for(ll = new_line;
+ (dl=dlist(ll))->type != End;
+ ll++)
+ if(dl->type == ListHead || dl->type == Simple)
+ break;
+
+ if(dl->type != End)
+ new_line = ll;
+
+ as.cur_row = new_line - as.top_ent;
+ if(as.cur_row >= as.l_p_page){
+ /*-- Changed pages --*/
+ as.top_ent += as.l_p_page;
+ as.cur_row -= as.l_p_page;
+ /* if it is still off screen */
+ if(as.cur_row >= as.l_p_page){
+ as.top_ent += (as.cur_row-as.l_p_page+1);
+ as.cur_row = (as.l_p_page - 1);
+ }
+
+ start_disp = 0;
+ ps->mangled_body = 1;
+ }
+ }
+ }
+ }
+ else{
+ if(as.selections == 0)
+ do_init_disp++;
+
+ as.selections++;
+
+ entry_set_selected(pab->address_book->selects,
+ (a_c_arg_t)dl->elnum);
+ r = next_selectable_line(as.cur_row+as.top_ent,
+ &new_line);
+ if(r){
+
+ for(ll = new_line;
+ (dl=dlist(ll))->type != End;
+ ll++)
+ if(dl->type == ListHead || dl->type == Simple)
+ break;
+
+ if(dl->type != End)
+ new_line = ll;
+
+ as.cur_row = new_line - as.top_ent;
+ if(as.cur_row >= as.l_p_page){
+ /*-- Changed pages --*/
+ as.top_ent += as.l_p_page;
+ as.cur_row -= as.l_p_page;
+ /* if it is still off screen */
+ if(as.cur_row >= as.l_p_page){
+ as.top_ent += (as.cur_row-as.l_p_page+1);
+ as.cur_row = (as.l_p_page - 1);
+ }
+
+ start_disp = 0;
+ ps->mangled_body = 1;
+ }
+ }
+ }
+
+ /*
+ * If we switch from selected to non-selected or
+ * vice versa, we have to init_disp_form() for all
+ * the addrbooks, in case we're using the X instead
+ * of bold.
+ */
+ if(do_init_disp){
+ ps->mangled_footer = 1;
+ for(i = 0; i < as.n_addrbk; i++)
+ init_disp_form(&as.adrbks[i],
+ ps->VAR_ABOOK_FORMATS, i);
+ }
+ }
+ else
+ q_status_message(SM_ORDER, 0, 4,
+ _("You may not select list members, only whole lists or personal entries"));
+ }
+ else
+ q_status_message(SM_ORDER, 0, 4,
+ _("You may only select addresses or lists"));
+
+ break;
+
+
+ /*--- Zoom in and look only at selected entries (or zoom out) --*/
+ case MC_ZOOM:
+ as.zoomed = (1 - as.zoomed);
+ if(as.zoomed)
+ ab_zoom((pab && pab->address_book) ? pab->address_book->selects
+ : NULL,
+ &start_disp);
+ else{
+ q_status_message(SM_ORDER, 0, 2, _("Zoom Mode is now off"));
+ ab_unzoom(&start_disp);
+ }
+
+ break;
+
+
+ /*--- Apply a command -----------*/
+ case MC_APPLY:
+ if(as.selections){
+ if(((ab_apply_cmd(ps, pab ? pab->address_book : NULL,
+ as.top_ent+as.cur_row, command_line) &&
+ F_ON(F_AUTO_UNZOOM, ps)) || !as.selections) && as.zoomed){
+
+ ab_unzoom(NULL);
+ ps_global->mangled_body = 1;
+ }
+
+ /*
+ * In case the line we're now at is not a selectable
+ * field.
+ *
+ * We set start_disp to zero here but rely on the called
+ * routine to set mangled_body if appropriate.
+ */
+ start_disp = 0;
+ new_line = first_selectable_line(as.cur_row+as.top_ent);
+ if(new_line != NO_LINE
+ && new_line != as.cur_row+as.top_ent){
+ as.cur_row = new_line - as.top_ent;
+ if(as.cur_row < 0){
+ as.top_ent += as.cur_row;
+ as.cur_row = 0;
+ }
+ else if(as.cur_row >= as.l_p_page){
+ as.top_ent += (as.cur_row - as.l_p_page + 1);
+ as.cur_row = as.l_p_page - 1;
+ }
+ }
+ }
+ else
+ q_status_message(SM_ORDER, 0, 2,
+ _("No selected entries to apply command to"));
+
+ break;
+
+
+ /*--------- QUIT pine -----------*/
+ case MC_QUIT:
+ dprint((7, "Quitting pine from addrbook\n"));
+ ps->next_screen = quit_screen;
+ break;
+
+
+ /*--------- Top of Folder list -----------*/
+ case MC_COLLECTIONS:
+ dprint((7, "Goto folder lister from addrbook\n"));
+ ps->next_screen = folder_screen;
+ break;
+
+
+ /*---------- Open specific new folder ----------*/
+ case MC_GOTO:
+ dprint((7, "Goto from addrbook\n"));
+ ab_goto_folder(command_line);
+ break;
+
+
+ /*--------- Index -----------*/
+ case MC_INDEX:
+ dprint((7, "Goto message index from addrbook\n"));
+ if(THREADING()
+ && sp_viewing_a_thread(ps->mail_stream)
+ && unview_thread(ps, ps->mail_stream, ps->msgmap)){
+ ps->next_screen = mail_index_screen;
+ ps->view_skipped_index = 0;
+ ps->mangled_screen = 1;
+ }
+
+ ps->next_screen = mail_index_screen;
+ break;
+
+
+ /*----------------- Print --------------------*/
+ case MC_PRINTTXT:
+ (void)ab_print(0);
+ ps->mangled_screen = 1;
+ break;
+
+
+ /*------ Copy entries into an abook ----*/
+ case MC_SAVE:
+ if(!any_addrs_avail(as.top_ent+as.cur_row)){
+ q_status_message(SM_ORDER, 0, 4, _("No entries to save"));
+ break;
+ }
+
+ if(entry_is_clickable(as.top_ent+as.cur_row)){
+ clickable_warning(as.top_ent+as.cur_row);
+ break;
+ }
+
+ if(empty){
+ empty_warning(as.top_ent+as.cur_row);
+ break;
+ }
+
+ (void)ab_save(ps, pab ? pab->address_book : NULL,
+ as.top_ent+as.cur_row, command_line, 0);
+ break;
+
+
+ /*------ Forward an entry in mail -----------*/
+ case MC_FORWARD:
+ if(!any_addrs_avail(as.top_ent+as.cur_row)){
+ q_status_message(SM_ORDER, 0, 4, _("No entries to forward"));
+ break;
+ }
+
+ if(entry_is_clickable(as.top_ent+as.cur_row)){
+ clickable_warning(as.top_ent+as.cur_row);
+ break;
+ }
+
+ if(empty){
+ empty_warning(as.top_ent+as.cur_row);
+ break;
+ }
+
+ if(!is_addr(as.top_ent+as.cur_row)){
+ q_status_message(SM_ORDER, 0, 4, _("Nothing to forward"));
+ break;
+ }
+
+ dl = dlist(as.top_ent+as.cur_row);
+ if(dl->type != ListHead && dl->type != Simple){
+ q_status_message(SM_ORDER, 0, 4,
+ _("Can only forward whole entries"));
+ break;
+ }
+
+ (void)ab_forward(ps, as.top_ent+as.cur_row, 0);
+ ps->mangled_footer = 1;
+ break;
+
+
+ case MC_REPAINT:
+ /* ^L attempts to resynchronize with changed addrbooks */
+ if(adrbk_check_all_validity_now())
+ (void)resync_screen(pab, style, checkedn);
+
+ /* fall through */
+
+ case MC_RESIZE:
+ mark_status_dirty();
+ mark_titlebar_dirty();
+ mark_keymenu_dirty();
+ ClearBody();
+ ps->mangled_screen = 1;
+ if(c == KEY_RESIZE)
+ ab_resize();
+
+ break;
+
+
+ case MC_UTF8:
+ bogus_utf8_command(utf8str, F_ON(F_USE_FK, ps) ? "F1" : "?");
+ break;
+
+
+ /*------ Some back compatibility messages -----*/
+ case MC_UNKNOWN:
+ if(c == 'e' && !are_selecting){
+ q_status_message(SM_ORDER | SM_DING, 0, 2,
+ _("Command \"E\" not defined. Use \"View/Update\" to edit an entry"));
+ break;
+ }
+ else if(c == 's'
+ && !(are_selecting || entry_is_clickable(as.top_ent+as.cur_row))){
+ q_status_message(SM_ORDER | SM_DING, 0, 2,
+ _("Command \"S\" not defined. Use \"AddNew\" to create a list"));
+ break;
+ }
+ else if(c == 'z' && !are_selecting){
+ q_status_message(SM_ORDER | SM_DING, 0, 2,
+ _("Command \"Z\" not defined. Use \"View/Update\" to add to a list"));
+ break;
+ }
+ /* else, fall through */
+
+ default:
+ bogus_command(c, F_ON(F_USE_FK, ps) ? "F1" : "?");
+ break;
+ }
+
+ if(ps->next_screen != SCREEN_FUN_NULL)
+ quit++;
+ }
+
+ erase_selections();
+ return NULL;
+}
+
+
+/*
+ * Turn on zoom mode and zoom in if applicable.
+ *
+ * Args selecteds -- tells which entries are selected in current abook
+ * start_disp -- Passed in so we can set it back in the caller
+ */
+void
+ab_zoom(EXPANDED_S *selecteds, int *start_disp)
+{
+ AddrScrn_Disp *dl;
+ DL_CACHE_S *dlc, dlc_restart;
+
+ as.zoomed = 1;
+
+ if(as.selections){
+ q_status_message(SM_ORDER, 0, 2, _("Zoom Mode is now on"));
+ if(cur_is_open()){
+ dl = dlist(as.top_ent+as.cur_row);
+ if((dl->type == ListHead ||
+ dl->type == Simple ||
+ dl->type == ListEmpty ||
+ dl->type == ListClickHere ||
+ dl->type == ListEnt) &&
+ entry_is_selected(selecteds, (a_c_arg_t)dl->elnum)){
+ dlc = get_dlc(as.top_ent+as.cur_row);
+ dlc_restart = *dlc;
+ warp_to_dlc(&dlc_restart, 0L);
+ /* put current entry in middle of screen */
+ as.top_ent = first_line(0L - (long)as.l_p_page/2L);
+ as.cur_row = 0L - as.top_ent;
+ }
+ else{
+ long new_ent;
+
+ warp_to_top_of_abook(as.cur);
+ as.top_ent = 0L;
+ new_ent = first_selectable_line(0L);
+ if(new_ent == NO_LINE)
+ as.cur_row = 0L;
+ else
+ as.cur_row = new_ent;
+
+ /* if it is off screen */
+ if(as.cur_row >= as.l_p_page){
+ as.top_ent += (as.cur_row - as.l_p_page + 1);
+ as.cur_row = (as.l_p_page - 1);
+ }
+ }
+ }
+ else{
+ dlc = get_dlc(as.top_ent+as.cur_row);
+ dlc_restart = *dlc;
+ warp_to_dlc(&dlc_restart, 0L);
+ /* put current entry in middle of screen */
+ as.top_ent = first_line(0L - (long)as.l_p_page/2L);
+ as.cur_row = 0L - as.top_ent;
+ }
+
+ ps_global->mangled_body = 1;
+ *start_disp = 0;
+ }
+ else{
+ as.zoomed = 0;
+ q_status_message(SM_ORDER, 0, 2, _("No selected entries to zoom on"));
+ }
+}
+
+
+/*
+ * Turn off zoom mode and zoom out if applicable.
+ *
+ * Args start_disp -- Passed in so we can set it back in the caller
+ */
+void
+ab_unzoom(int *start_disp)
+{
+ DL_CACHE_S *dlc, dlc_restart;
+
+ as.zoomed = 0;
+ dlc = get_dlc(as.top_ent+as.cur_row);
+ if(dlc->type == DlcZoomEmpty){
+ long new_ent;
+
+ warp_to_beginning();
+ as.top_ent = 0L;
+ new_ent = first_selectable_line(0L);
+ if(new_ent == NO_LINE)
+ as.cur_row = 0L;
+ else
+ as.cur_row = new_ent;
+
+ /* if it is off screen */
+ if(as.cur_row >= as.l_p_page){
+ as.top_ent += (as.cur_row - as.l_p_page + 1);
+ as.cur_row = (as.l_p_page - 1);
+ }
+ }
+ else{
+ dlc_restart = *dlc;
+ warp_to_dlc(&dlc_restart, 0L);
+ /* put current entry in middle of screen */
+ as.top_ent = first_line(0L - (long)as.l_p_page/2L);
+ as.cur_row = 0L - as.top_ent;
+ }
+
+ ps_global->mangled_body = 1;
+ if(start_disp)
+ *start_disp = 0;
+}
+
+
+/*
+ * Post an empty addrbook warning.
+ *
+ * Args: cur_line -- The current line position (in global display list)
+ * of cursor
+ */
+void
+empty_warning(long int cur_line)
+{
+ register AddrScrn_Disp *dl;
+
+ dl = dlist(cur_line);
+ if(dl->type == NoAbooks)
+ q_status_message(SM_ORDER, 0, 4,
+ _("No address books configured, use Setup"));
+ else if(dl->type == Empty)
+ q_status_message(SM_ORDER, 0, 4, _("Address Book is Empty"));
+ else
+ q_status_message(SM_ORDER, 0, 4, _("Distribution List is Empty"));
+}
+
+
+/*
+ * Tell user to click on this to expand.
+ *
+ * Args: cur_line -- The current line position (in global display list)
+ * of cursor
+ */
+void
+clickable_warning(long int cur_line)
+{
+ register AddrScrn_Disp *dl;
+
+ dl = dlist(cur_line);
+ if(dl->type == Title || dl->type == ClickHereCmb)
+ q_status_message(SM_ORDER, 0, 4, _("Address Book not expanded, use \">\" to expand"));
+ else
+ q_status_message(SM_ORDER, 0, 4, _("Distribution List not expanded, use \">\" to expand"));
+}
+
+
+/*
+ * Post a no tabs warning.
+ */
+void
+no_tabs_warning(void)
+{
+ q_status_message(SM_ORDER, 0, 4, "Tabs not allowed in address book");
+}
+
+
+/*
+ * Prompt for command to apply to selected entries
+ *
+ * Returns: 1 if the entries are successfully commanded.
+ * 0 otherwise
+ */
+int
+ab_apply_cmd(struct pine *ps, AdrBk *abook, long int cur_line, int command_line)
+{
+ int ret = 0;
+ static ESCKEY_S opts[] = {
+ {'c', 'c', "C", "ComposeTo"},
+ {'d', 'd', "D", "Delete"},
+ {'%', '%', "%", "Print"},
+ {'f', 'f', "F", "Forward"},
+ {'s', 's', "S", "Save"},
+ {'#', '#', "#", "Role"},
+ { 0, '%', "", ""},
+ {-1, 0, NULL, NULL}};
+#define PHANTOM_PRINT 6
+
+ dprint((7, "- ab_apply_cmd -\n"));
+
+
+ opts[PHANTOM_PRINT].ch = (F_ON(F_ENABLE_PRYNT, ps_global)) ? 'y' : -1;
+
+ switch(radio_buttons("APPLY command : ", command_line, opts, 'z', 'x',
+ NO_HELP, RB_NORM)){
+ case 'c':
+ ret = ab_compose_to_addr(cur_line, 1, 0);
+ break;
+
+ case '#':
+ ret = ab_compose_to_addr(cur_line, 1, 1);
+ break;
+
+ case 'd':
+ ret = ab_agg_delete(ps, 1);
+ break;
+
+ case '%':
+ ret = ab_print(1);
+ break;
+
+ case 'f':
+ ret = ab_forward(ps, cur_line, 1);
+ break;
+
+ case 's':
+ ret = ab_save(ps, abook, cur_line, command_line, 1);
+ break;
+
+ case 'x':
+ cmd_cancelled("Apply command");
+ break;
+
+ case 'z':
+ q_status_message(SM_INFO, 0, 2,
+ "Cancelled, there is no default command");
+ break;
+ }
+
+ ps_global->mangled_footer = 1;
+
+ return(ret);
+}
+
+
+/*
+ * Allow user to mark some entries "selected".
+ */
+void
+ab_select(struct pine *ps, AdrBk *abook, long int cur_line, int command_line, int *start_disp)
+{
+ static ESCKEY_S sel_opts1[] = {
+ {'a', 'a', "A", "unselect All"},
+ { 0 , 'c', "C", NULL},
+ {'b', 'b', "B", "Broaden selctn"},
+ {'n', 'n', "N", "Narrow selctn"},
+ {'f', 'f', "F", "Flip selected"},
+ {-1, 0, NULL, NULL}
+ };
+ static char *sel_pmt1 = "ALTER selection : ";
+ static ESCKEY_S sel_opts2[] = {
+ {'a', 'a', "A", "select All"},
+ {'c', 'c', "C", "select Cur"},
+ {'t', 't', "T", "Text"},
+ {'s', 's', "S", "Status"},
+ {-1, 0, NULL, NULL}
+ };
+ static char *sel_pmt2 = "SELECT criteria : ";
+ ESCKEY_S *sel_opts;
+ HelpType help = NO_HELP;
+ adrbk_cntr_t num, ab_count;
+ int q = 0, rv = 0, narrow = 0,
+ do_flush = 0, do_warp = 0, prevsel,
+ move_current = 0, do_beginning = 0;
+ long new_ent;
+ AddrScrn_Disp *dl;
+ DL_CACHE_S *dlc, dlc_restart;
+
+ dprint((5, "- ab_select -\n"));
+
+ if(cur_is_open()){ /* select applies only to this addrbook */
+
+ ps->mangled_footer = 1;
+ prevsel = as.selections;
+ dl = dlist(cur_line);
+ sel_opts = sel_opts2;
+
+ /*
+ * If already some selected, ask how to alter that selection.
+ */
+ if(as.selections){
+ sel_opts += 2; /* don't offer all or current below */
+ if(dl && (dl->type == ListHead || dl->type == Simple)){
+ sel_opts1[1].label = entry_is_selected(abook->selects,
+ (a_c_arg_t)dl->elnum)
+ ? "unselect Cur"
+ : "select Cur";
+ sel_opts1[1].ch = 'c';
+ }
+ else
+ sel_opts1[1].ch = -2; /* don't offer this choice */
+
+ switch(q = radio_buttons(sel_pmt1, command_line, sel_opts1,
+ 'a', 'x', help, RB_NORM)){
+ case 'n': /* narrow selection */
+ narrow++;
+ case 'b': /* broaden selection */
+ q = 0; /* but don't offer criteria prompt */
+ break;
+
+ case 'c': /* select or unselect current */
+ case 'a': /* select or unselect all */
+ case 'f': /* flip selections */
+ case 'x': /* cancel */
+ break;
+
+ default:
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ "Unsupported Select option");
+ return;
+ }
+ }
+
+ if(abook && dl &&
+ (dl->type == ListHead || dl->type == Simple)){
+ sel_opts1[1].label = entry_is_selected(abook->selects,
+ (a_c_arg_t)dl->elnum)
+ ? "unselect Cur"
+ : "select Cur";
+ sel_opts1[1].ch = 'c';
+ }
+ else
+ sel_opts1[1].ch = -2; /* don't offer this choice */
+
+ if(!q)
+ q = radio_buttons(sel_pmt2, command_line, sel_opts,
+ 'c', 'x', help, RB_NORM);
+
+ *start_disp = 0;
+ dlc = get_dlc(cur_line);
+ dlc_restart = *dlc;
+ ab_count = adrbk_count(abook);
+
+ switch(q){
+ case 'x': /* cancel */
+ cmd_cancelled("Select command");
+ break;
+
+ case 'c': /* select/unselect current */
+ ps->mangled_body = 1;
+ if(entry_is_selected(abook->selects, (a_c_arg_t)dl->elnum)){
+ entry_unset_selected(abook->selects, (a_c_arg_t)dl->elnum);
+ as.selections--;
+
+ if(as.selections == 0 && as.zoomed){
+ as.zoomed = 0;
+ q_status_message(SM_ORDER, 0, 2,
+ "Zoom Mode is now off, no entries selected");
+ do_warp++;
+ }
+ else if(as.zoomed){
+ move_current++;
+ do_flush++;
+ }
+ else
+ do_flush++;
+ }
+ else{
+ entry_set_selected(abook->selects, (a_c_arg_t)dl->elnum);
+ as.selections++;
+
+ if(as.selections == 1 && !as.zoomed && F_ON(F_AUTO_ZOOM, ps)){
+ as.zoomed = 1;
+ as.top_ent = dlc_restart.global_row;
+ as.cur_row = 0;
+ do_warp++;
+ }
+ else
+ do_flush++;
+ }
+
+ break;
+
+ case 'a':
+ ps->mangled_body = 1;
+ if(any_selected(abook->selects)){ /* unselect all */
+ for(num = 0; num < ab_count; num++){
+ if(entry_is_selected(abook->selects, (a_c_arg_t)num)){
+ as.selections--;
+ entry_unset_selected(abook->selects, (a_c_arg_t)num);
+ }
+ }
+
+ if(as.selections == 0 && as.zoomed){
+ as.zoomed = 0;
+ q_status_message(SM_ORDER, 0, 2,
+ "Zoom Mode is now off, all entries UNselected");
+ do_warp++;
+ }
+ else{
+ char bb[100];
+
+ snprintf(bb, sizeof(bb), "%s entries UNselected%s%s%s",
+ comatose(prevsel-as.selections),
+ as.selections ? ", still " : "",
+ as.selections ? comatose(as.selections) : "",
+ as.selections ? " selected in other addrbooks" : "");
+ bb[sizeof(bb)-1] = '\0';
+ q_status_message(SM_ORDER, 0, 2, bb);
+ if(as.zoomed)
+ do_beginning++;
+ else
+ do_flush++;
+ }
+ }
+ else{ /* select all */
+ for(num = 0; num < ab_count; num++){
+ if(!entry_is_selected(abook->selects, (a_c_arg_t)num)){
+ as.selections++;
+ entry_set_selected(abook->selects, (a_c_arg_t)num);
+ }
+ }
+
+ q_status_message1(SM_ORDER, 0, 2, "All %s entries selected",
+ comatose(ab_count));
+ if(prevsel == 0 && as.selections > 0 &&
+ !as.zoomed && F_ON(F_AUTO_ZOOM, ps)){
+ as.zoomed = 1;
+ as.top_ent = dlc_restart.global_row - as.cur_row;
+ do_warp++;
+ }
+ else if(dlc_restart.type == DlcZoomEmpty &&
+ as.selections > prevsel)
+ do_beginning++;
+ else if(as.zoomed)
+ do_warp++;
+ else
+ do_flush++;
+ }
+
+ break;
+
+ case 'f': /* flip selections in this abook */
+ ps->mangled_body = 1;
+ for(num = 0; num < ab_count; num++){
+ if(entry_is_selected(abook->selects, (a_c_arg_t)num)){
+ entry_unset_selected(abook->selects, (a_c_arg_t)num);
+ as.selections--;
+ }
+ else{
+ entry_set_selected(abook->selects, (a_c_arg_t)num);
+ as.selections++;
+ }
+ }
+
+ if(as.zoomed){
+ if(as.selections)
+ do_beginning++;
+ else{
+ as.zoomed = 0;
+ q_status_message(SM_ORDER, 0, 2, "Zoom Mode is now off");
+ do_warp++;
+ }
+ }
+ else
+ do_warp++;
+
+ q_status_message1(SM_ORDER, 0, 2, "%s entries now selected",
+ comatose(as.selections));
+
+ break;
+
+ case 't':
+ case 's':
+ switch(q){
+ case 't':
+ rv = ab_select_text(abook, narrow);
+ break;
+
+ case 's':
+ rv = ab_select_type(abook, narrow);
+ break;
+ }
+
+ if(!rv){
+ ps->mangled_body = 1;
+ if(dlc_restart.type == DlcZoomEmpty &&
+ as.selections > prevsel)
+ do_beginning++;
+ else if(as.zoomed){
+ if(as.selections == 0){
+ as.zoomed = 0;
+ q_status_message(SM_ORDER, 0, 2,
+ "Zoom Mode is now off");
+ do_warp++;
+ }
+ else
+ do_beginning++;
+ }
+ else{
+ if(prevsel == 0 && as.selections > 0 &&
+ !as.zoomed && F_ON(F_AUTO_ZOOM, ps)){
+ as.zoomed = 1;
+ do_beginning++;
+ }
+ else
+ do_warp++;
+ }
+
+ if(prevsel == as.selections && prevsel > 0){
+ if(as.selections == 1)
+ q_status_message(SM_ORDER, 0, 2,
+ "No change resulted, 1 entry remains selected");
+ else
+ q_status_message1(SM_ORDER, 0, 2,
+ "No change resulted, %s entries remain selected",
+ comatose(as.selections));
+ }
+ else if(prevsel == 0){
+ if(as.selections == 1)
+ q_status_message(SM_ORDER, 0, 2,
+ "Select matched 1 entry");
+ else if(as.selections > 1)
+ q_status_message1(SM_ORDER, 0, 2,
+ "Select matched %s entries",
+ comatose(as.selections));
+ else
+ q_status_message(SM_ORDER, 0, 2,
+ "Select failed! No entries selected");
+ }
+ else if(as.selections == 0){
+ if(prevsel == 1)
+ q_status_message(SM_ORDER, 0, 2,
+ "The single selected entry is UNselected");
+ else
+ q_status_message1(SM_ORDER, 0, 2,
+ "All %s entries UNselected",
+ comatose(prevsel));
+ }
+ else if(narrow){
+ if(as.selections == 1 && (prevsel-as.selections) == 1)
+ q_status_message(SM_ORDER, 0, 2,
+ "1 entry now selected, 1 entry was UNselected");
+ else if(as.selections == 1)
+ q_status_message1(SM_ORDER, 0, 2,
+ "1 entry now selected, %s entries were UNselected",
+ comatose(prevsel-as.selections));
+ else if((prevsel-as.selections) == 1)
+ q_status_message1(SM_ORDER, 0, 2,
+ "%s entries now selected, 1 entry was UNselected",
+ comatose(as.selections));
+ else
+ q_status_message2(SM_ORDER, 0, 2,
+ "%s entries now selected, %s entries were UNselected",
+ comatose(as.selections),
+ comatose(prevsel-as.selections));
+ }
+ else{
+ if((as.selections-prevsel) == 1)
+ q_status_message1(SM_ORDER, 0, 2,
+ "1 new entry selected, %s entries now selected",
+ comatose(as.selections));
+ else if(as.selections == 1)
+ q_status_message1(SM_ORDER, 0, 2,
+ "%s new entries selected, 1 entry now selected",
+ comatose(as.selections-prevsel));
+ else
+ q_status_message2(SM_ORDER, 0, 2,
+ "%s new entries selected, %s entries now selected",
+ comatose(as.selections-prevsel),
+ comatose(as.selections));
+ }
+ }
+
+ break;
+
+ default :
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ "Unsupported Select option");
+ break;
+ }
+ }
+ else{
+ if(F_ON(F_CMBND_ABOOK_DISP,ps_global))
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ "Select is only available from within an expanded address book");
+ else
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ "Select is only available when viewing an individual address book");
+
+ return;
+ }
+
+ if(rv)
+ return;
+
+ if(do_beginning){
+ warp_to_beginning(); /* just go to top */
+ as.top_ent = 0L;
+ new_ent = first_selectable_line(0L);
+ if(new_ent == NO_LINE)
+ as.cur_row = 0L;
+ else
+ as.cur_row = new_ent;
+
+ /* if it is off screen */
+ if(as.cur_row >= as.l_p_page){
+ as.top_ent += (as.cur_row - as.l_p_page + 1);
+ as.cur_row = (as.l_p_page - 1);
+ }
+ }
+ else if(do_flush)
+ flush_dlc_from_cache(&dlc_restart);
+ else if(do_warp)
+ warp_to_dlc(&dlc_restart, dlc_restart.global_row);
+
+ if(move_current){
+ dlc = get_dlc(cur_line);
+ if(dlc->type == DlcEnd){
+ as.cur_row--;
+ if(as.cur_row < 0){
+ as.top_ent += as.cur_row; /* plus a negative number */
+ as.cur_row = 0;
+ *start_disp = 0;
+ }
+ else
+ *start_disp = as.cur_row;
+
+ }
+ else
+ *start_disp = as.cur_row;
+ }
+}
+
+
+/*
+ * Selects based on whether an entry is a list or not.
+ *
+ * Returns: 0 if search went ok
+ * -1 if there was a problem
+ */
+int
+ab_select_type(AdrBk *abook, int narrow)
+{
+ static ESCKEY_S ab_sel_type_opt[] = {
+ {'s', 's', "S", "Simple"},
+ {'l', 'l', "L", "List"},
+ {-1, 0, NULL, NULL}
+ };
+ static char *ab_sel_type = "Select Lists or Simples (non Lists) ? ";
+ int type;
+ adrbk_cntr_t num, ab_count;
+
+ dprint((6, "- ab_select_type -\n"));
+
+ if(!abook)
+ return -1;
+
+ switch(type = radio_buttons(ab_sel_type, -FOOTER_ROWS(ps_global),
+ ab_sel_type_opt, 'l', 'x', NO_HELP, RB_NORM)){
+ case 'l':
+ break;
+
+ case 's':
+ break;
+
+ case 'x':
+ cmd_cancelled("Select");
+ return -1;
+
+ default:
+ dprint((1,"\n - BOTCH: ab_select_type unknown option\n"));
+ return -1;
+ }
+
+ ab_count = adrbk_count(abook);
+ for(num = 0; num < ab_count; num++){
+ AdrBk_Entry *abe;
+ int matched;
+
+ /*
+ * If it won't possibly change state, don't look at it.
+ */
+ if((narrow && !entry_is_selected(abook->selects, (a_c_arg_t)num)) ||
+ (!narrow && entry_is_selected(abook->selects, (a_c_arg_t)num)))
+ continue;
+
+ abe = adrbk_get_ae(abook, (a_c_arg_t) num);
+ matched = ((type == 's' && abe->tag == Single) ||
+ (type == 'l' && abe->tag == List));
+
+ if(narrow && !matched){
+ entry_unset_selected(abook->selects, (a_c_arg_t)num);
+ as.selections--;
+ }
+ else if(!narrow && matched){
+ entry_set_selected(abook->selects, (a_c_arg_t)num);
+ as.selections++;
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ * Selects based on string matches in various addrbook fields.
+ *
+ * Returns: 0 if search went ok
+ * -1 if there was a problem
+ */
+int
+ab_select_text(AdrBk *abook, int narrow)
+{
+ static ESCKEY_S ab_sel_text_opt[] = {
+ {'n', 'n', "N", "Nickname"},
+ {'a', 'a', "A", "All Text"},
+ {'f', 'f', "F", "Fullname"},
+ {'e', 'e', "E", "Email Addrs"},
+ {'c', 'c', "C", "Comment"},
+ {'z', 'z', "Z", "Fcc"},
+ {-1, 0, NULL, NULL}
+ };
+ static char *ab_sel_text =
+ "Select based on Nickname, All text, Fullname, Addrs, Comment, or Fcc ? ";
+ HelpType help = NO_HELP;
+ int type, r;
+ char sstring[80+1], prompt[80];
+ adrbk_cntr_t num, ab_count;
+ char *fmt = "String in \"%s\" to match : ";
+
+ dprint((6, "- ab_select_text -\n"));
+
+ if(!abook)
+ return -1;
+
+ switch(type = radio_buttons(ab_sel_text, -FOOTER_ROWS(ps_global),
+ ab_sel_text_opt, 'a', 'x', NO_HELP, RB_NORM)){
+ case 'n':
+ snprintf(prompt, sizeof(prompt), fmt, "Nickname");
+ break;
+ case 'a':
+ snprintf(prompt, sizeof(prompt), fmt, "All Text");
+ break;
+ case 'f':
+ snprintf(prompt, sizeof(prompt), fmt, "Fullname");
+ break;
+ case 'e':
+ snprintf(prompt, sizeof(prompt), fmt, "addresses");
+ break;
+ case 'c':
+ snprintf(prompt, sizeof(prompt), fmt, "Comment");
+ break;
+ case 'z':
+ snprintf(prompt, sizeof(prompt), fmt, "Fcc");
+ break;
+ case 'x':
+ break;
+ default:
+ dprint((1,"\n - BOTCH: ab_select_text unknown option\n"));
+ return -1;
+ }
+
+ prompt[sizeof(prompt)-1] = '\0';
+
+ sstring[0] = '\0';
+ while(type != 'x'){
+ int flags = OE_APPEND_CURRENT;
+
+ r = optionally_enter(sstring, -FOOTER_ROWS(ps_global), 0,
+ sizeof(sstring), prompt, NULL, help, &flags);
+ switch(r){
+ case 3: /* BUG, no help */
+ case 4:
+ continue;
+
+ default:
+ break;
+ }
+
+ if(r == 1 || sstring[0] == '\0')
+ r = 'x';
+
+ break;
+ }
+
+ if(type == 'x' || r == 'x'){
+ cmd_cancelled("Select");
+ return -1;
+ }
+
+ ab_count = adrbk_count(abook);
+ for(num = 0; num < ab_count; num++){
+ AdrBk_Entry *abe;
+ int matched;
+
+ /*
+ * If it won't possibly change state, don't look at it.
+ */
+ if((narrow && !entry_is_selected(abook->selects, (a_c_arg_t)num)) ||
+ (!narrow && entry_is_selected(abook->selects, (a_c_arg_t)num)))
+ continue;
+
+ abe = adrbk_get_ae(abook, (a_c_arg_t) num);
+ matched = match_check(abe, type, sstring);
+
+ if(narrow && !matched){
+ entry_unset_selected(abook->selects, (a_c_arg_t)num);
+ as.selections--;
+ }
+ else if(!narrow && matched){
+ entry_set_selected(abook->selects, (a_c_arg_t)num);
+ as.selections++;
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ * Returns: 1 if a match is found for the entry
+ * 0 no match
+ * -1 error
+ */
+int
+match_check(AdrBk_Entry *abe, int type, char *string)
+{
+ static int err = -1;
+ static int match = 1;
+ static int nomatch = 0;
+ unsigned int checks;
+#define CK_NICKNAME 0x01
+#define CK_FULLNAME 0x02
+#define CK_ADDRESSES 0x04
+#define CK_FCC 0x08
+#define CK_COMMENT 0x10
+#define CK_ALL 0x1f
+
+ checks = 0;
+
+ switch(type){
+ case 'n': /* Nickname */
+ checks |= CK_NICKNAME;
+ break;
+
+ case 'f': /* Fullname */
+ checks |= CK_FULLNAME;
+ break;
+
+ case 'e': /* Addrs */
+ checks |= CK_ADDRESSES;
+ break;
+
+ case 'a': /* All Text */
+ checks |= CK_ALL;
+ break;
+
+ case 'z': /* Fcc */
+ checks |= CK_FCC;
+ break;
+
+ case 'c': /* Comment */
+ checks |= CK_COMMENT;
+ break;
+
+ default:
+ q_status_message(SM_ORDER | SM_DING, 3, 3, "Unknown type");
+ return(err);
+ }
+
+ if(checks & CK_NICKNAME){
+ if(abe && abe->nickname && srchstr(abe->nickname, string))
+ return(match);
+ }
+
+ if(checks & CK_FULLNAME){
+ if(abe &&
+ abe->fullname &&
+ abe->fullname[0] &&
+ srchstr((char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, abe->fullname),
+ string))
+ return(match);
+ }
+
+ if(checks & CK_ADDRESSES){
+ if(abe &&
+ abe->tag == Single &&
+ abe->addr.addr &&
+ abe->addr.addr[0] &&
+ srchstr((char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, abe->addr.addr),
+ string))
+ return(match);
+ else if(abe &&
+ abe->tag == List &&
+ abe->addr.list){
+ char **p;
+
+ for(p = abe->addr.list; p != NULL && *p != NULL; p++){
+ if(srchstr((char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, *p),
+ string))
+ return(match);
+ }
+ }
+ }
+
+ if(checks & CK_FCC){
+ if(abe &&
+ abe->fcc &&
+ abe->fcc[0] &&
+ srchstr((char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, abe->fcc),
+ string))
+ return(match);
+ }
+
+ if(checks & CK_COMMENT && abe && abe->extra && abe->extra[0]){
+ size_t n, len;
+ unsigned char *p, *tmp = NULL;
+ int found_it = 0;
+
+ if((n = 4*strlen(abe->extra)) > SIZEOF_20KBUF-1){
+ len = n+1;
+ p = tmp = (unsigned char *)fs_get(len * sizeof(char));
+ }
+ else{
+ len = SIZEOF_20KBUF;
+ p = (unsigned char *)tmp_20k_buf;
+ }
+
+ if(srchstr((char *)rfc1522_decode_to_utf8(p, len, abe->extra), string))
+ found_it++;
+
+ if(tmp)
+ fs_give((void **)&tmp);
+
+ if(found_it)
+ return(match);
+ }
+
+ return(nomatch);
+}
+
+
+/*
+ * Go to folder.
+ *
+ * command_line -- The screen line on which to prompt
+ */
+void
+ab_goto_folder(int command_line)
+{
+ char *go_folder;
+ CONTEXT_S *tc;
+ int notrealinbox = 0;
+
+ dprint((2, "- ab_goto_folder -\n"));
+
+ tc = ps_global->context_current;
+
+ go_folder = broach_folder(command_line, 1, &notrealinbox, &tc);
+
+ if(go_folder != NULL)
+ visit_folder(ps_global, go_folder, tc, NULL, notrealinbox ? 0L : DB_INBOXWOCNTXT);
+}
+
+
+/*
+ * Execute whereis command.
+ *
+ * Returns value of the new top entry, or NO_LINE if cancelled.
+ */
+long
+ab_whereis(int *warped, int command_line)
+{
+ int rc, wrapped = 0;
+ long new_top_ent, new_line;
+
+ dprint((5, "- ab_whereis -\n"));
+
+ rc = search_book(as.top_ent+as.cur_row, command_line,
+ &new_line, &wrapped, warped);
+
+ new_top_ent = NO_LINE;
+
+ if(rc == -2)
+ q_status_message(SM_INFO, 0, 2, _("Address book search cancelled"));
+
+ else if(rc == -1)
+ q_status_message(SM_ORDER, 0, 4, _("Word not found"));
+
+ else if(rc == 0){ /* search succeeded */
+
+ if(wrapped == 1)
+ q_status_message(SM_INFO, 0, 2, _("Search wrapped to beginning"));
+ else if(wrapped == 2)
+ q_status_message(SM_INFO, 0, 2,
+ _("Current line contains the only match"));
+
+ /* know match is on the same page */
+ if(!*warped &&
+ new_line >= as.top_ent &&
+ new_line < as.top_ent+as.l_p_page)
+ new_top_ent = as.top_ent;
+ /* don't know whether it is or not, reset top_ent */
+ else
+ new_top_ent = first_line(new_line - as.l_p_page/2);
+
+ as.cur_row = new_line - new_top_ent;
+ }
+
+ return(new_top_ent);
+}
+
+
+/*
+ * recalculate display parameters for window size change
+ */
+void
+ab_resize(void)
+{
+ long new_line;
+ int old_l_p_p;
+ DL_CACHE_S dlc_buf, *dlc_restart;
+
+ old_l_p_p = as.l_p_page;
+ as.l_p_page = ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)
+ - HEADER_ROWS(ps_global);
+
+ dprint((9, "- ab_resize -\n l_p_p was %d, now %d\n",
+ old_l_p_p, as.l_p_page));
+
+ if(as.l_p_page <= 0){
+ as.no_op_possbl++;
+ return;
+ }
+ else
+ as.no_op_possbl = 0;
+
+ new_line = as.top_ent + as.cur_row;
+ as.top_ent = first_line(new_line - as.l_p_page/2);
+ as.cur_row = new_line - as.top_ent;
+ as.old_cur_row = as.cur_row;
+
+ /* need this to re-initialize Text and Title lines in display */
+ /* get the old current line (which may be the wrong width) */
+ dlc_restart = get_dlc(new_line);
+ /* flush it from cache */
+ flush_dlc_from_cache(dlc_restart);
+ /* re-get it (should be right now) */
+ dlc_restart = get_dlc(new_line);
+ /* copy it to local storage */
+ dlc_buf = *dlc_restart;
+ dlc_restart = &dlc_buf;
+ /* flush everything from cache and add that one line back in */
+ warp_to_dlc(dlc_restart, new_line);
+}
+
+
+/*
+ * Returns 0 if we know for sure that there are no
+ * addresses available in any of the addressbooks.
+ *
+ * Easiest would be to start at 0 and go through the addrbook, but that will
+ * be very slow for big addrbooks if we're not close to 0 already. Instead,
+ * starting_hint is a hint at a good place to start looking.
+ */
+int
+any_addrs_avail(long int starting_hint)
+{
+ register AddrScrn_Disp *dl;
+ long lineno;
+
+ /*
+ * Look from lineno backwards first, in hopes of finding it in cache.
+ */
+ lineno = starting_hint;
+ for(dl=dlist(lineno);
+ dl->type != Beginning;
+ dl = dlist(--lineno)){
+ if(dl->type == NoAbooks)
+ return 0;
+
+ switch(dl->type){
+ case Simple:
+ case ListEnt:
+ case ListHead:
+ case ZoomEmpty:
+ case Title:
+ case ListClickHere:
+ case ClickHereCmb:
+ return 1;
+ default:
+ break;
+ }
+ }
+
+ /* search from here forward if we still don't know */
+ lineno = starting_hint;
+ for(dl=dlist(lineno);
+ dl->type != End;
+ dl = dlist(++lineno)){
+ if(dl->type == NoAbooks)
+ return 0;
+
+ switch(dl->type){
+ case Simple:
+ case ListEnt:
+ case ListHead:
+ case ZoomEmpty:
+ case Title:
+ case ListClickHere:
+ case ClickHereCmb:
+ return 1;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ * Returns 1 if this line is a clickable line.
+ */
+int
+entry_is_clickable(long int lineno)
+{
+ register AddrScrn_Disp *dl;
+
+ if((dl = dlist(lineno)) &&
+ (dl->type == Title || dl->type == ListClickHere ||
+ dl->type == ClickHereCmb))
+ return 1;
+
+ return 0;
+}
+
+
+/*
+ * Returns 1 if this line is a clickable Title line.
+ */
+int
+entry_is_clickable_title(long int lineno)
+{
+ register AddrScrn_Disp *dl;
+
+ if((dl = dlist(lineno)) && (dl->type == Title || dl->type == ClickHereCmb))
+ return 1;
+
+ return 0;
+}
+
+
+/*
+ * Returns 1 if an address or list is selected.
+ */
+int
+is_addr(long int lineno)
+{
+ register AddrScrn_Disp *dl;
+
+ if((dl = dlist(lineno)) && (dl->type == ListHead ||
+ dl->type == ListEnt ||
+ dl->type == Simple))
+ return 1;
+
+ return 0;
+}
+
+
+/*
+ * Returns 1 if type of line is Empty.
+ */
+int
+is_empty(long int lineno)
+{
+ register AddrScrn_Disp *dl;
+
+ if((dl = dlist(lineno)) &&
+ (dl->type == Empty || dl->type == ListEmpty || dl->type == ZoomEmpty))
+ return 1;
+
+ return 0;
+}
+
+
+/*
+ * Returns 1 if lineno is a list entry
+ */
+int
+entry_is_listent(long int lineno)
+{
+ register AddrScrn_Disp *dl;
+
+ if((dl = dlist(lineno)) && dl->type == ListEnt)
+ return 1;
+
+ return 0;
+}
+
+
+/*
+ * Returns 1 if lineno is a fake addrbook for config screen add
+ * or if it is an AskServer line for LDAP query
+ */
+int
+entry_is_addkey(long int lineno)
+{
+ register AddrScrn_Disp *dl;
+
+ if((dl = dlist(lineno)) && (dl->type == AddFirstPers ||
+ dl->type == AddFirstGlob ||
+ dl->type == AskServer))
+ return 1;
+
+ return 0;
+}
+
+
+/*
+ * Returns 1 if lineno is the line to ask for directory server query
+ */
+int
+entry_is_askserver(long int lineno)
+{
+ register AddrScrn_Disp *dl;
+
+ if((dl = dlist(lineno)) && dl->type == AskServer)
+ return 1;
+
+ return 0;
+}
+
+
+/*
+ * Returns 1 if an add abook here would be global, not personal
+ */
+int
+add_is_global(long int lineno)
+{
+ register AddrScrn_Disp *dl;
+
+ dl = dlist(lineno);
+
+ if(dl){
+ if(dl->type == Title){
+ register PerAddrBook *pab;
+
+ pab = &as.adrbks[as.cur];
+ if(pab && pab->type & GLOBAL)
+ return 1;
+ }
+ else if(dl->type == AddFirstGlob)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Find the first line greater than or equal to line. (Any line, not
+ * necessarily selectable.)
+ *
+ * Returns the line number of the found line or NO_LINE if there is none.
+ *
+ * Warning: This just starts at the passed in line and goes forward until
+ * it runs into a line that isn't a Beginning line. If the line passed in
+ * is not in the dlc cache, it will have no way to know when it gets to the
+ * real beginning.
+ */
+long
+first_line(long int line)
+{
+ long lineno;
+ register PerAddrBook *pab;
+ int i;
+
+ for(lineno=line;
+ dlist(lineno)->type == Beginning;
+ lineno++)
+ ;/* do nothing */
+
+ if(dlist(lineno)->type != End)
+ return(lineno);
+ else{
+ for(i = 0; i < as.n_addrbk; i++){
+ pab = &as.adrbks[i];
+ if(pab->ostatus != Open &&
+ pab->ostatus != HalfOpen &&
+ pab->ostatus != ThreeQuartOpen)
+ return NO_LINE;
+ }
+
+ as.no_op_possbl++;
+ return(NO_LINE);
+ }
+}
+
+
+/*
+ * Find the line number of the next selectable line.
+ *
+ * Args: cur_line -- The current line position (in global display list)
+ * of cursor
+ * new_line -- Return value: new line position
+ *
+ * Result: The new line number is set.
+ * The value 1 is returned if OK or 0 if there is no next line.
+ */
+int
+next_selectable_line(long int cur_line, long int *new_line)
+{
+ /* skip over non-selectable lines */
+ for(cur_line++;
+ !line_is_selectable(cur_line) && dlist(cur_line)->type != End;
+ cur_line++)
+ ;/* do nothing */
+
+ if(dlist(cur_line)->type == End)
+ return 0;
+
+ *new_line = cur_line;
+ return 1;
+}
+
+
+/*
+ * Find the line number of the previous selectable line.
+ *
+ * Args: cur_line -- The current line position (in global display list)
+ * of cursor
+ * new_line -- Return value: new line position
+ *
+ * Result: The new line number is set.
+ * The value 1 is returned if OK or 0 if there is no previous line.
+ */
+int
+prev_selectable_line(long int cur_line, long int *new_line)
+{
+ /* skip backwards over non-selectable lines */
+ for(cur_line--;
+ !line_is_selectable(cur_line) && dlist(cur_line)->type != Beginning;
+ cur_line--)
+ ;/* do nothing */
+
+ if(dlist(cur_line)->type == Beginning)
+ return 0;
+
+ *new_line = cur_line;
+
+ return 1;
+}
+
+
+/*
+ * Resync the display with the addrbooks, which were just discovered
+ * to be out of sync with the display.
+ *
+ * Returns 1 -- current address book had to be resynced
+ * 0 -- current address book not resynced
+ */
+int
+resync_screen(PerAddrBook *pab, AddrBookArg style, int checkedn)
+{
+ AddrScrn_Disp *dl;
+ int current_resynced = 0;
+ DL_CACHE_S dlc_restart, *dlc = NULL;
+
+ /*
+ * The test below gives conditions under which it is safe to go ahead
+ * and resync all the addrbooks that are out of sync now, and the
+ * display won't change. Otherwise, we have to be careful to preserve
+ * some of our state so that we can attempt to restore the screen to
+ * a state that is as close as possible to what we have now. If the
+ * currently opened address book (pab) is out of date we will lose
+ * the expanded state of its distribution lists, which is no big deal.
+ * Since resyncing also loses the checked status if we're selecting with
+ * ListMode, we don't even attempt it in that case.
+ */
+ if((ab_nesting_level < 2 && !cur_is_open() && checkedn == 0) ||
+ (style == AddrBookScreen &&
+ pab &&
+ pab->address_book &&
+ !(pab->address_book->flags & FILE_OUTOFDATE ||
+ (pab->address_book->rd &&
+ pab->address_book->rd->flags & REM_OUTOFDATE)))){
+
+ if(F_ON(F_CMBND_ABOOK_DISP,ps_global)){
+ dlc = get_dlc(as.top_ent+as.cur_row);
+ dlc_restart = *dlc;
+ }
+
+ if(adrbk_check_and_fix_all(1, 0, 1)){
+ ps_global->mangled_footer = 1; /* why? */
+ if(F_ON(F_CMBND_ABOOK_DISP,ps_global)){
+ warp_to_dlc(&dlc_restart, 0L);
+ /* put current entry in middle of screen */
+ as.top_ent = first_line(0L - (long)as.l_p_page/2L);
+ as.cur_row = 0L - as.top_ent;
+ ps_global->mangled_screen = 1;
+ }
+ }
+ }
+ else if(style == AddrBookScreen){
+ char *savenick = NULL;
+ AdrBk_Entry *abe;
+ adrbk_cntr_t old_entry_num, new_entry_num;
+ long old_global_row;
+
+ current_resynced++;
+
+ /*
+ * We're going to try to get the nickname of the current
+ * entry and find it again after the resync.
+ */
+ dl = dlist(as.top_ent+as.cur_row);
+ if(dl->type == ListEnt ||
+ dl->type == ListEmpty ||
+ dl->type == ListClickHere ||
+ dl->type == ListHead ||
+ dl->type == Simple){
+ abe = ae(as.top_ent+as.cur_row);
+ old_entry_num = dl->elnum;
+ old_global_row = as.top_ent+as.cur_row;
+ if(abe && abe->nickname && abe->nickname[0])
+ savenick = cpystr(abe->nickname);
+ }
+
+ /* this will close and re-open current addrbook */
+ (void)adrbk_check_and_fix_all(1, 0, 1);
+
+ abe = NULL;
+ if(savenick){
+ abe = adrbk_lookup_by_nick(pab->address_book, savenick,
+ &new_entry_num);
+ fs_give((void **)&savenick);
+ }
+
+ if(abe){ /* If we found the same nickname, move to it */
+ dlc_restart.adrbk_num = as.cur;
+ dlc_restart.dlcelnum = new_entry_num;
+ dlc_restart.type = (abe->tag == Single)
+ ? DlcSimple : DlcListHead;
+ if(old_entry_num == new_entry_num)
+ warp_to_dlc(&dlc_restart, old_global_row);
+ else
+ warp_to_dlc(&dlc_restart, 0L);
+ }
+ else
+ warp_to_top_of_abook(as.cur);
+
+ if(!abe || old_entry_num != new_entry_num){
+ /* put current entry in middle of screen */
+ as.top_ent = first_line(0L - (long)as.l_p_page/2L);
+ as.cur_row = 0L - as.top_ent;
+ }
+ }
+
+ return(current_resynced);
+}
+
+
+/*
+ * Erase all the check marks.
+ */
+void
+erase_checks(void)
+{
+ int i;
+ PerAddrBook *pab;
+
+ for(i = 0; i < as.n_addrbk; i++){
+ pab = &as.adrbks[i];
+ if(pab->address_book && pab->address_book->checks)
+ exp_free(pab->address_book->checks);
+
+ init_disp_form(pab, ps_global->VAR_ABOOK_FORMATS, i);
+ }
+}
+
+
+/*
+ * Erase all the selections.
+ */
+void
+erase_selections(void)
+{
+ int i;
+ PerAddrBook *pab;
+
+ for(i = 0; i < as.n_addrbk; i++){
+ pab = &as.adrbks[i];
+ if(pab->address_book && pab->address_book->selects)
+ exp_free(pab->address_book->selects);
+ }
+
+ for(i = 0; i < as.n_addrbk; i++){
+ pab = &as.adrbks[i];
+ init_disp_form(pab, ps_global->VAR_ABOOK_FORMATS, i);
+ }
+
+ as.selections = 0;
+}
+
+
+/*
+ * return values of search_in_one_line are or'd combination of these
+ */
+#define MATCH_NICK 0x1 /* match in field 0 */
+#define MATCH_FULL 0x2 /* match in field 1 */
+#define MATCH_ADDR 0x4 /* match in field 2 */
+#define MATCH_FCC 0x8 /* match in fcc field */
+#define MATCH_COMMENT 0x10 /* match in comment field */
+#define MATCH_BIGFIELD 0x20 /* match in one of the fields that crosses the
+ whole screen, like a Title field */
+#define MATCH_LISTMEM 0x40 /* match list member */
+/*
+ * Prompt user for search string and call find_in_book.
+ *
+ * Args: cur_line -- The current line position (in global display list)
+ * of cursor
+ * command_line -- The screen line to prompt on
+ * new_line -- Return value: new line position
+ * wrapped -- Wrapped to beginning of display, tell user
+ * warped -- Warped to a new location in the addrbook
+ *
+ * Result: The new line number is set if the search is successful.
+ * Returns 0 if found, -1 if not, -2 if cancelled.
+ *
+ */
+int
+search_book(long int cur_line, int command_line, long int *new_line, int *wrapped, int *warped)
+{
+ int i=0, find_result, rc, flags, ku;
+ static HISTORY_S *history = NULL;
+ char search_string[MAX_SEARCH + 1];
+ char prompt[MAX_SEARCH + 50], nsearch_string[MAX_SEARCH+1], *p;
+ HelpType help;
+ ESCKEY_S ekey[6];
+ PerAddrBook *pab;
+ long nl;
+
+ dprint((7, "- search_book -\n"));
+
+ init_hist(&history, HISTSIZE);
+
+ search_string[0] = '\0';
+ if((p = get_prev_hist(history, "", 0, NULL)) != NULL){
+ strncpy(search_string, p, sizeof(search_string));
+ search_string[sizeof(search_string)-1] = '\0';
+ }
+
+ snprintf(prompt, sizeof(prompt), _("Word to search for [%.*s]: "), MAX_SEARCH, search_string);
+ prompt[sizeof(prompt)-1] = '\0';
+ help = NO_HELP;
+ nsearch_string[0] = '\0';
+
+ ekey[i].ch = 0;
+ ekey[i].rval = 0;
+ ekey[i].name = "";
+ ekey[i++].label = "";
+
+ ekey[i].ch = ctrl('Y');
+ ekey[i].rval = 10;
+ ekey[i].name = "^Y";
+ /* TRANSLATORS: User is searching in address book. One of the options is to
+ search for the First Address */
+ ekey[i++].label = _("First Adr");
+
+ ekey[i].ch = ctrl('V');
+ ekey[i].rval = 11;
+ ekey[i].name = "^V";
+ /* TRANSLATORS: Last Address */
+ ekey[i++].label = _("Last Adr");
+
+ ekey[i].ch = KEY_UP;
+ ekey[i].rval = 30;
+ ekey[i].name = "";
+ ku = i;
+ ekey[i++].label = "";
+
+ ekey[i].ch = KEY_DOWN;
+ ekey[i].rval = 31;
+ ekey[i].name = "";
+ ekey[i++].label = "";
+
+ ekey[i].ch = -1;
+
+ flags = OE_APPEND_CURRENT | OE_KEEP_TRAILING_SPACE;
+ while(1){
+
+ /*
+ * 2 is really 1 because there will be one real entry and
+ * one entry of "" because of the get_prev_hist above.
+ */
+ if(items_in_hist(history) > 2){
+ ekey[ku].name = HISTORY_UP_KEYNAME;
+ ekey[ku].label = HISTORY_KEYLABEL;
+ ekey[ku+1].name = HISTORY_DOWN_KEYNAME;
+ ekey[ku+1].label = HISTORY_KEYLABEL;
+ }
+ else{
+ ekey[ku].name = "";
+ ekey[ku].label = "";
+ ekey[ku+1].name = "";
+ ekey[ku+1].label = "";
+ }
+
+ rc = optionally_enter(nsearch_string, command_line, 0,
+ sizeof(nsearch_string),
+ prompt, ekey, help, &flags);
+ if(rc == 3){
+ help = help == NO_HELP ? h_oe_searchab : NO_HELP;
+ continue;
+ }
+ else if(rc == 10){
+ *warped = 1;
+ warp_to_beginning(); /* go to top of addrbooks */
+ if((nl=first_selectable_line(0L)) != NO_LINE){
+ *new_line = nl;
+ q_status_message(SM_INFO, 0, 2, _("Searched to first entry"));
+ return 0;
+ }
+ else{
+ q_status_message(SM_INFO, 0, 2, _("No entries"));
+ return -1;
+ }
+ }
+ else if(rc == 11){
+ *warped = 1;
+ warp_to_end(); /* go to bottom */
+ if((nl=first_selectable_line(0L)) != NO_LINE){
+ *new_line = nl;
+ q_status_message(SM_INFO, 0, 2, _("Searched to last entry"));
+ return 0;
+ }
+ else{
+ q_status_message(SM_INFO, 0, 2, _("No entries"));
+ return -1;
+ }
+ }
+ else if(rc == 30){
+ if((p = get_prev_hist(history, nsearch_string, 0, NULL)) != NULL){
+ strncpy(nsearch_string, p, sizeof(nsearch_string));
+ nsearch_string[sizeof(nsearch_string)-1] = '\0';
+ }
+ else
+ Writechar(BELL, 0);
+
+ continue;
+ }
+ else if(rc == 31){
+ if((p = get_next_hist(history, nsearch_string, 0, NULL)) != NULL){
+ strncpy(nsearch_string, p, sizeof(nsearch_string));
+ nsearch_string[sizeof(nsearch_string)-1] = '\0';
+ }
+ else
+ Writechar(BELL, 0);
+
+ continue;
+ }
+
+ if(rc != 4){ /* 4 is redraw */
+ save_hist(history, nsearch_string, 0, NULL);
+ break;
+ }
+ }
+
+
+ if(rc == 1 || (search_string[0] == '\0' && nsearch_string[0] == '\0'))
+ return -2;
+
+ if(nsearch_string[0] != '\0'){
+ strncpy(search_string, nsearch_string, sizeof(search_string)-1);
+ search_string[sizeof(search_string)-1] = '\0';
+ }
+
+ find_result = find_in_book(cur_line, search_string, new_line, wrapped);
+
+ if(*wrapped == 1)
+ *warped = 1;
+
+ if(find_result){
+ int also = 0, notdisplayed = 0;
+
+ pab = &as.adrbks[adrbk_num_from_lineno(*new_line)];
+ if(find_result & MATCH_NICK){
+ if(pab->nick_is_displayed)
+ also++;
+ else
+ notdisplayed++;
+ }
+
+ if(find_result & MATCH_FULL){
+ if(pab->full_is_displayed)
+ also++;
+ else
+ notdisplayed++;
+ }
+
+ if(find_result & MATCH_ADDR){
+ if(pab->addr_is_displayed)
+ also++;
+ else
+ notdisplayed++;
+ }
+
+ if(find_result & MATCH_FCC){
+ if(pab->fcc_is_displayed)
+ also++;
+ else
+ notdisplayed++;
+ }
+
+ if(find_result & MATCH_COMMENT){
+ if(pab->comment_is_displayed)
+ also++;
+ else
+ notdisplayed++;
+ }
+
+ if(find_result & MATCH_LISTMEM){
+ AddrScrn_Disp *dl;
+
+ dl = dlist(*new_line);
+ if(F_OFF(F_EXPANDED_DISTLISTS,ps_global)
+ && !exp_is_expanded(pab->address_book->exp, (a_c_arg_t)dl->elnum))
+ notdisplayed++;
+ }
+
+ if(notdisplayed > 1 && *wrapped == 0){
+ if(also)
+ /* TRANSLATORS: These "matched" messages are advisory messages explaining
+ how a search command matched an entry */
+ q_status_message1(SM_ORDER,0,4, _("Also matched string in %s other fields"),
+ comatose(notdisplayed));
+ else
+ q_status_message1(SM_ORDER,0,4, _("Matched string in %s fields"),
+ comatose(notdisplayed));
+ }
+ else if(notdisplayed == 1 && *wrapped == 0){
+ if(also){
+ if(find_result & MATCH_NICK && !pab->nick_is_displayed)
+ q_status_message(SM_ORDER,0,4, _("Also matched string in Nickname field"));
+ else if(find_result & MATCH_FULL && !pab->full_is_displayed)
+ q_status_message(SM_ORDER,0,4, _("Also matched string in Fullname field"));
+ else if(find_result & MATCH_ADDR && !pab->addr_is_displayed)
+ q_status_message(SM_ORDER,0,4, _("Also matched string in Address field"));
+ else if(find_result & MATCH_FCC && !pab->fcc_is_displayed)
+ q_status_message(SM_ORDER,0,4, _("Also matched string in Fcc field"));
+ else if(find_result & MATCH_COMMENT && !pab->comment_is_displayed)
+ q_status_message(SM_ORDER,0,4, _("Also matched string in Comment field"));
+ else if(find_result & MATCH_LISTMEM)
+ q_status_message(SM_ORDER,0,4, _("Also matched string in list member address"));
+ else
+ q_status_message(SM_ORDER,0,4, _("Also matched string in ?"));
+ }
+ else{
+ if(find_result & MATCH_NICK && !pab->nick_is_displayed)
+ q_status_message(SM_ORDER,0,4, _("Matched string in Nickname field"));
+ else if(find_result & MATCH_FULL && !pab->full_is_displayed)
+ q_status_message(SM_ORDER,0,4, _("Matched string in Fullname field"));
+ else if(find_result & MATCH_ADDR && !pab->addr_is_displayed)
+ q_status_message(SM_ORDER,0,4, _("Matched string in Address field"));
+ else if(find_result & MATCH_FCC && !pab->fcc_is_displayed)
+ q_status_message(SM_ORDER,0,4, _("Matched string in Fcc field"));
+ else if(find_result & MATCH_COMMENT && !pab->comment_is_displayed)
+ q_status_message(SM_ORDER,0,4, _("Matched string in Comment field"));
+ else if(find_result & MATCH_LISTMEM)
+ q_status_message(SM_ORDER,0,4, _("Matched string in list member address"));
+ else
+ q_status_message(SM_ORDER,0,4, _("Matched string in ?"));
+ }
+ }
+
+
+ /* be sure to be on a selectable field */
+ if(!line_is_selectable(*new_line))
+ if((nl=first_selectable_line(*new_line+1)) != NO_LINE)
+ *new_line = nl;
+ }
+
+ return(find_result ? 0 : -1);
+}
+
+
+/*
+ * Search the display list for the given string.
+ *
+ * Args: cur_line -- The current line position (in global display list)
+ * of cursor
+ * string -- String to search for
+ * new_line -- Return value: new line position
+ * wrapped -- Wrapped to beginning of display during search
+ *
+ * Result: The new line number is set if the search is successful.
+ * Returns 0 -- string not found
+ * Otherwise, a bitmask of which fields the string was found in.
+ */
+int
+find_in_book(long int cur_line, char *string, long int *new_line, int *wrapped)
+{
+ register AddrScrn_Disp *dl;
+ long nl, nl_save;
+ int fields;
+ AdrBk_Entry *abe;
+ char *listaddr = NULL;
+ DL_CACHE_S *dlc,
+ dlc_save; /* a local copy */
+
+
+ dprint((9, "- find_in_book -\n"));
+
+ /*
+ * Save info to allow us to get back to where we were if we can't find
+ * the string. Also used to stop our search if we wrap back to the
+ * start and search forward.
+ */
+
+ nl_save = cur_line;
+ dlc = get_dlc(nl_save);
+ dlc_save = *dlc;
+
+ *wrapped = 0;
+ nl = cur_line + 1L;
+
+ /* start with next line and search to the end of the disp_list */
+ dl = dlist(nl);
+ while(dl->type != End){
+ if(dl->type == Simple ||
+ dl->type == ListHead ||
+ dl->type == ListEnt ||
+ dl->type == ListClickHere){
+ abe = ae(nl);
+ if(dl->type == ListEnt)
+ listaddr = listmem(nl);
+ }
+ else
+ abe = (AdrBk_Entry *)NULL;
+
+ if((fields=search_in_one_line(dl, abe, listaddr, string)) != 0)
+ goto found;
+
+ dl = dlist(++nl);
+ }
+
+
+ /*
+ * Wrap back to the start of the addressbook and search forward
+ * from there.
+ */
+ warp_to_beginning(); /* go to top of addrbooks */
+ nl = 0L; /* line number is always 0 after warp_to_beginning */
+ *wrapped = 1;
+
+ dlc = get_dlc(nl);
+ while(!matching_dlcs(&dlc_save, dlc) && dlc->type != DlcEnd){
+
+ fill_in_dl_field(dlc);
+ dl = &dlc->dl;
+
+ if(dl->type == Simple ||
+ dl->type == ListHead ||
+ dl->type == ListEnt ||
+ dl->type == ListClickHere){
+ abe = ae(nl);
+ if(dl->type == ListEnt)
+ listaddr = listmem(nl);
+ }
+ else
+ abe = (AdrBk_Entry *)NULL;
+
+ if((fields=search_in_one_line(dl, abe, listaddr, string)) != 0)
+ goto found;
+
+ dlc = get_dlc(++nl);
+ }
+
+ /* see if it is in the current line */
+ fill_in_dl_field(dlc);
+ dl = &dlc->dl;
+
+ if(dl->type == Simple ||
+ dl->type == ListHead ||
+ dl->type == ListEnt ||
+ dl->type == ListClickHere){
+ abe = ae(nl);
+ if(dl->type == ListEnt)
+ listaddr = listmem(nl);
+ }
+ else
+ abe = (AdrBk_Entry *)NULL;
+
+ fields = search_in_one_line(dl, abe, listaddr, string);
+ if(dl->usst &&
+ (dl->type == Text || dl->type == Title || dl->type == TitleCmb))
+ fs_give((void **)&dl->usst);
+
+ /* jump cache back to where we started */
+ *wrapped = 0;
+ warp_to_dlc(&dlc_save, nl_save);
+ if(fields){
+ *new_line = nl_save; /* because it was in current line */
+ *wrapped = 2;
+ }
+
+ nl = *new_line;
+
+found:
+ *new_line = nl;
+ return(fields);
+}
+
+
+/*
+ * Look in line dl for string.
+ *
+ * Args: dl -- the display list for this line
+ * abe -- AdrBk_Entry if it is an address type
+ * listaddr -- list member if it is of type ListEnt
+ * string -- look for this string
+ *
+ * Result: 0 -- string not found
+ * Otherwise, a bitmask of which fields the string was found in.
+ * MATCH_NICK 0x1
+ * MATCH_FULL 0x2
+ * MATCH_ADDR 0x4
+ * MATCH_FCC 0x8
+ * MATCH_COMMENT 0x10
+ * MATCH_BIGFIELD 0x20
+ * MATCH_LISTMEM 0x40
+ */
+int
+search_in_one_line(AddrScrn_Disp *dl, AdrBk_Entry *abe, char *listaddr, char *string)
+{
+ register int c;
+ int ret_val = 0;
+ char **lm;
+
+ for(c = 0; c < 5; c++){
+ switch(c){
+ case 0:
+ switch(dl->type){
+ case Simple:
+ case ListHead:
+ if(srchstr(abe->nickname, string))
+ ret_val |= MATCH_NICK;
+
+ break;
+
+ case Text:
+ case Title:
+ case TitleCmb:
+ case AskServer:
+ if(srchstr(dl->usst, string))
+ ret_val |= MATCH_BIGFIELD;
+
+ default:
+ break;
+ }
+ break;
+
+ case 1:
+ switch(dl->type){
+ case Simple:
+ case ListHead:
+ if(abe && srchstr(
+ (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, abe->fullname),
+ string))
+ ret_val |= MATCH_FULL;
+
+ default:
+ break;
+ }
+ break;
+
+ case 2:
+ switch(dl->type){
+ case Simple:
+ if(srchstr((abe && abe->tag == Single) ?
+ abe->addr.addr : NULL, string))
+ ret_val |= MATCH_ADDR;
+
+ break;
+
+ case ListEnt:
+ if(srchstr(
+ (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, listaddr), string))
+ ret_val |= MATCH_LISTMEM;
+
+ break;
+
+ case ListClickHere:
+ if(abe)
+ for(lm = abe->addr.list;
+ !(ret_val & MATCH_LISTMEM) && *lm; lm++)
+ if(srchstr(*lm, string))
+ ret_val |= MATCH_LISTMEM;
+
+ break;
+
+ case Empty:
+ case ListEmpty:
+ if(srchstr(EMPTY, string))
+ ret_val |= MATCH_BIGFIELD;
+
+ break;
+
+ case AddFirstPers:
+ if(srchstr(ADD_PERSONAL, string))
+ ret_val |= MATCH_BIGFIELD;
+
+ break;
+
+ case AddFirstGlob:
+ if(srchstr(ADD_GLOBAL, string))
+ ret_val |= MATCH_BIGFIELD;
+
+ break;
+
+ case NoAbooks:
+ if(srchstr(NOABOOKS, string))
+ ret_val |= MATCH_BIGFIELD;
+
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case 3: /* fcc */
+ switch(dl->type){
+ case Simple:
+ case ListHead:
+ if(abe && srchstr(abe->fcc, string))
+ ret_val |= MATCH_FCC;
+
+ default:
+ break;
+ }
+ break;
+
+ case 4: /* comment */
+ switch(dl->type){
+ case Simple:
+ case ListHead:
+ if(abe){
+ size_t n, len;
+ unsigned char *p, *tmp = NULL;
+
+ if((n = 4*strlen(abe->extra)) > SIZEOF_20KBUF-1){
+ len = n+1;
+ p = tmp = (unsigned char *)fs_get(len * sizeof(char));
+ }
+ else{
+ len = SIZEOF_20KBUF;
+ p = (unsigned char *)tmp_20k_buf;
+ }
+
+ if(srchstr((char *)rfc1522_decode_to_utf8(p, len, abe->extra),
+ string))
+ ret_val |= MATCH_COMMENT;
+
+ if(tmp)
+ fs_give((void **)&tmp);
+
+ }
+
+ default:
+ break;
+ }
+ break;
+ }
+ }
+
+ return(ret_val);
+}
+
+
+/*
+ * These chars in nicknames will mess up parsing.
+ *
+ * Returns 0 if ok, 1 if not.
+ * Returns an allocated error message on error.
+ */
+int
+nickname_check(char *nickname, char **error)
+{
+ register char *t;
+ char buf[100];
+
+ if((t = strindex(nickname, SPACE)) ||
+ (t = strindex(nickname, ',')) ||
+ (t = strindex(nickname, '"')) ||
+ (t = strindex(nickname, ';')) ||
+ (t = strindex(nickname, ':')) ||
+ (t = strindex(nickname, '@')) ||
+ (t = strindex(nickname, '(')) ||
+ (t = strindex(nickname, ')')) ||
+ (t = strindex(nickname, '\\')) ||
+ (t = strindex(nickname, '[')) ||
+ (t = strindex(nickname, ']')) ||
+ (t = strindex(nickname, '<')) ||
+ (t = strindex(nickname, '>'))){
+ char s[4];
+ s[0] = '"';
+ s[1] = *t;
+ s[2] = '"';
+ s[3] = '\0';
+ if(error){
+ /*
+ * TRANSLATORS: this is telling the user that one of the characters
+ * they have included in a nickname will not work. It will say something like
+ * Blank spaces not allowed in nicknames or
+ * Commas not...
+ * etc.
+ */
+ snprintf(buf, sizeof(buf), _("%s not allowed in nicknames"),
+ *t == SPACE ?
+ _("Blank spaces") :
+ *t == ',' ?
+ _("Commas") :
+ *t == '"' ?
+ _("Quotes") :
+ s);
+ buf[sizeof(buf)-1] = '\0';
+ *error = cpystr(buf);
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+
+char *
+abook_select_screen(struct pine *ps)
+{
+ CONF_S *ctmp = NULL, *first_line = NULL;
+ OPT_SCREEN_S screen;
+ int adrbknum;
+ char *helptitle;
+ HelpType help;
+ PerAddrBook *pab;
+ char *abook = NULL;
+
+ helptitle = _("HELP FOR SELECTING AN ADDRESS BOOK");
+ help = h_role_abook_select;
+
+ init_ab_if_needed();
+
+ for(adrbknum = 0; adrbknum < as.n_addrbk; adrbknum++){
+ new_confline(&ctmp);
+ if(!first_line)
+ first_line = ctmp;
+
+ pab = &as.adrbks[adrbknum];
+
+ ctmp->value = cpystr((pab && pab->abnick)
+ ? pab->abnick
+ : (pab && pab->filename)
+ ? pab->filename
+ : "?");
+
+ ctmp->d.b.selected = &abook;
+ ctmp->d.b.abookname = ctmp->value;
+ ctmp->keymenu = &abook_select_km;
+ ctmp->help = help;
+ ctmp->help_title = helptitle;
+ ctmp->tool = abook_select_tool;
+ ctmp->flags = CF_STARTITEM;
+ ctmp->valoffset = 4;
+ }
+
+ if(first_line){
+ memset(&screen, 0, sizeof(screen));
+ (void) conf_scroll_screen(ps, &screen, first_line,
+ /* TRANSLATORS: Print something1 using something2.
+ abooks is something1 */
+ _("SELECT ADDRESS BOOK"), _("abooks"), 0);
+ }
+ else
+ q_status_message(SM_ORDER|SM_DING, 3, 3, _("No address books defined!"));
+
+ ps->mangled_screen = 1;
+ return(abook);
+}
+
+
+int
+abook_select_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ int retval;
+
+ switch(cmd){
+ case MC_CHOICE :
+ *((*cl)->d.b.selected) = cpystr((*cl)->d.b.abookname);
+ retval = simple_exit_cmd(flags);
+ break;
+
+ case MC_EXIT :
+ retval = simple_exit_cmd(flags);
+ break;
+
+ default:
+ retval = -1;
+ break;
+ }
+
+ if(retval > 0)
+ ps->mangled_body = 1;
+
+ return(retval);
+}
+
+
+/*
+ * This isn't actually just a nickname completion anymore. The user types
+ * in a prefix of either a nickname, or a full address, or the addr@mailbox
+ * part of an address and we look in all of the address books for matches
+ * like that. We return the longest unambiguous match in answer.
+ *
+ * Args prefix -- The part of the "nickname" that has been typed so far
+ * answer -- The answer is returned here.
+ * tabtab -- If the answer returned from adrbk_list_of_completions is
+ * ambiguous and tabtab is set, then offer up a
+ * selector screen for all the possible answers.
+ * flags -- ANC_AFTERCOMMA -- This means that the passed in
+ * prefix may be a list of comma
+ * separated addresses and we're only
+ * completing the last one.
+ *
+ * Returns 0 -- no matches at all
+ * 1 -- more than one nickname (or address) begins with
+ * the answer being returned
+ * 2 -- the returned answer is a complete answer, either a
+ * nickname or a complete address, and there are
+ * no longer matches for the prefix
+ *
+ * Allocated answer is returned in answer argument.
+ * Caller needs to free the answer.
+ */
+int
+abook_nickname_complete(char *prefix, char **answer, int tabtab, unsigned flags)
+{
+ int ambiguity;
+ COMPLETE_S *completions, *cp;
+ char *saved_beginning = NULL;
+ char *potential_answer = NULL;
+
+ wp_exit = wp_nobail = 0;
+
+ /* there shouldn't be a case where answer is NULL */
+ if(answer)
+ *answer = NULL;
+
+ /*
+ * If we need to handle case where no prefix is passed in,
+ * figure that out when we need it.
+ */
+ if(!(prefix && prefix[0]))
+ return(0);
+
+ if(flags & ANC_AFTERCOMMA){
+ char *lastnick;
+
+ /*
+ * Find last comma, save the part before that, operate
+ * only on the last address.
+ */
+ if((lastnick = strrchr(prefix ? prefix : "", ',')) != NULL){
+ lastnick++;
+ while(!(*lastnick & 0x80) && isspace((unsigned char) (*lastnick)))
+ lastnick++;
+
+ saved_beginning = cpystr(prefix);
+ saved_beginning[lastnick-prefix] = '\0';
+ prefix = lastnick;
+ }
+ }
+
+ if(!(prefix && prefix[0]))
+ return(0);
+
+ completions = adrbk_list_of_completions(prefix,
+ ps_global->cur_uid_stream, ps_global->cur_uid,
+ ALC_INCLUDE_ADDRS | ((strlen(prefix) >= 3) ? ALC_INCLUDE_LDAP : 0));
+
+ if(!completions)
+ ambiguity = 0;
+ else if(completions && completions->next)
+ ambiguity = 1;
+ else
+ ambiguity = 2;
+
+ if(ambiguity == 2){
+ if(completions->full_address && completions->full_address[0])
+ potential_answer = cpystr(completions->full_address);
+ else if(completions->nickname && completions->nickname[0])
+ potential_answer = cpystr(completions->nickname);
+ else if(completions->addr && completions->addr[0])
+ potential_answer = cpystr(completions->addr);
+ else
+ potential_answer = cpystr(prefix);
+ }
+ /* answer is ambiguous and caller wants a choose list in that case */
+ else if(ambiguity == 1 && tabtab){
+ potential_answer = choose_an_address_with_this_prefix(completions);
+
+ if(potential_answer)
+ ambiguity = 2;
+ else{
+ ambiguity = 1;
+ potential_answer = cpystr(prefix);
+ }
+ }
+ else if(ambiguity == 1){
+ int k;
+ char cand1_kth_char, cand2_kth_char;
+ char unambig[1000];
+
+ /* find the longest unambiguous prefix */
+ strncpy(unambig, prefix, sizeof(unambig));
+ unambig[sizeof(unambig)-1] = '\0';
+ k = strlen(unambig);
+
+ /*
+ * First verify that they all match through prefix. LDAP sometimes gives
+ * weird, inexplicable answers that don't seem to match at all.
+ */
+ for(cp = completions; cp; cp = cp->next)
+ if(!( /* not a match */
+/* no NICK bit OR nickname matches prefix */
+ (!(cp->matches_bitmap & ALC_NICK) || (cp->nickname && strlen(cp->nickname) >= k && !struncmp(unambig, cp->nickname, k)))
+/* AND no ADDR bit OR addr matches prefix */
+ && (!(cp->matches_bitmap & ALC_ADDR) || (cp->addr && strlen(cp->addr) >= k && !struncmp(unambig, cp->addr, k)))
+/* AND neither FULL bit is set OR one of the two fulls matches prefix */
+ && (!(cp->matches_bitmap & (ALC_FULL | ALC_REVFULL)) || ((cp->matches_bitmap & ALC_FULL && cp->full_address && strlen(cp->full_address) >= k && !struncmp(unambig, cp->full_address, k)) || (cp->matches_bitmap & ALC_REVFULL && cp->rev_fullname && strlen(cp->rev_fullname) >= k && !struncmp(unambig, cp->rev_fullname, k))))
+ ))
+ break;
+
+ /* if cp that means there was not a universal match up through prefix, stop */
+ if(!cp)
+ do{
+ cand1_kth_char = cand2_kth_char = '\0';
+ if(completions->matches_bitmap & ALC_NICK && completions->nickname && strlen(completions->nickname) >= k)
+ cand1_kth_char = completions->nickname[k];
+ else if(completions->matches_bitmap & ALC_ADDR && completions->addr && strlen(completions->addr) >= k)
+ cand1_kth_char = completions->addr[k];
+ else{
+ if(completions->matches_bitmap & ALC_FULL && completions->full_address && strlen(completions->full_address) >= k)
+ cand1_kth_char = completions->full_address[k];
+
+ if(completions->matches_bitmap & ALC_REVFULL && completions->rev_fullname && strlen(completions->rev_fullname) >= k)
+ cand2_kth_char = completions->rev_fullname[k];
+ }
+
+ /*
+ * You'll want a wide screen to read this. There are two possible
+ * candidate chars for the next position. One or the other of them
+ * has to match in all of the possible completions. We consider it
+ * a match if either of the fullname completions for this entry is
+ * a match. That may not match what the user expects but it may.
+ */
+ for(cp = completions; cp; cp = cp->next){
+ if(!( /* candidate 1 is not a match */
+ /* candidate 1 is defined */
+ cand1_kth_char && cand1_kth_char != ','
+ /* AND no NICK bit OR nickname char is a match */
+ && (!(cp->matches_bitmap & ALC_NICK) || (cp->nickname && strlen(cp->nickname) >= k && cp->nickname[k] == cand1_kth_char))
+ /* AND no ADDR bit OR addr char is a match */
+ && (!(cp->matches_bitmap & ALC_ADDR) || (cp->addr && strlen(cp->addr) >= k && cp->addr[k] == cand1_kth_char))
+ /* AND neither FULL bit is set OR one of the two full chars is a match */
+ && (!(cp->matches_bitmap & (ALC_FULL | ALC_REVFULL)) || ((cp->matches_bitmap & ALC_FULL && cp->full_address && strlen(cp->full_address) >= k && cp->full_address[k] == cand1_kth_char) || (cp->matches_bitmap & ALC_REVFULL && cp->rev_fullname && strlen(cp->rev_fullname) >= k && cp->rev_fullname[k] == cand1_kth_char)))
+ ))
+ cand1_kth_char = '\0'; /* mark that it isn't a match */
+
+ if(!cand1_kth_char && !( /* cand1 is not a match AND cand2 is not a match */
+ /* candidate 2 is defined */
+ cand2_kth_char && cand2_kth_char != ','
+ /* AND no NICK bit OR nickname char is a match */
+ && (!(cp->matches_bitmap & ALC_NICK) || (cp->nickname && strlen(cp->nickname) >= k && cp->nickname[k] == cand2_kth_char))
+ /* AND no ADDR bit OR addr char is a match */
+ && (!(cp->matches_bitmap & ALC_ADDR) || (cp->addr && strlen(cp->addr) >= k && cp->addr[k] == cand2_kth_char))
+ /* AND neither FULL bit is set OR one of the two full chars is a match */
+ && (!(cp->matches_bitmap & (ALC_FULL | ALC_REVFULL)) || ((cp->matches_bitmap & ALC_FULL && cp->full_address && strlen(cp->full_address) >= k && cp->full_address[k] == cand2_kth_char) || (cp->matches_bitmap & ALC_REVFULL && cp->rev_fullname && strlen(cp->rev_fullname) >= k && cp->rev_fullname[k] == cand2_kth_char)))
+ ))
+ cand2_kth_char = '\0'; /* mark that it isn't a match */
+
+ if(!cand1_kth_char && !cand2_kth_char)
+ break; /* no match so break */
+ }
+
+ if(!cp) /* they all matched */
+ unambig[k++] = cand1_kth_char ? cand1_kth_char : cand2_kth_char;
+
+ }while(!cp && k < sizeof(unambig)-1);
+
+ unambig[k] = '\0';
+ unambig[sizeof(unambig)-1] = '\0';
+
+ /* don't return answer with trailing space */
+ while(--k >= 0 && isspace((unsigned char) unambig[k]))
+ unambig[k] = '\0';
+
+ potential_answer = cpystr(unambig);
+ }
+
+ if(completions)
+ free_complete_s(&completions);
+
+ if(answer && ambiguity != 0){
+ if(potential_answer){
+ if(saved_beginning){
+ size_t l1, l2;
+
+ l1 = strlen(saved_beginning);
+ l2 = strlen(potential_answer);
+ *answer = (char *) fs_get((l1+l2+1) * sizeof(char));
+ strncpy(*answer, saved_beginning, l1+l2);
+ strncpy(*answer+l1, potential_answer, l2);
+ (*answer)[l1+l2] = '\0';
+ }
+ else{
+ *answer = potential_answer;
+ potential_answer = NULL;
+ }
+ }
+ else{
+ /* this can't happen */
+ ambiguity = 0;
+ }
+ }
+
+ if(saved_beginning)
+ fs_give((void **) &saved_beginning);
+
+ if(potential_answer)
+ fs_give((void **) &potential_answer);
+
+ return(ambiguity);
+}
+
+
+/*
+ * Returns an allocated nickname choice from user that begins with
+ * prefix.
+ */
+char *
+choose_an_address_with_this_prefix(COMPLETE_S *completions)
+{
+ char buf[1000];
+ char *chosen_address = NULL;
+ char **lp, **da, **possible_addrs = NULL, **display_addrs = NULL;
+ COMPLETE_S *cp;
+ size_t cnt = 0;
+ int show_nick, show_revfull;
+
+ /*
+ * Count how many and allocate an array for choose_item_from_list().
+ */
+ for(cnt = 0, cp = completions; cp; cp = cp->next)
+ cnt++;
+
+ /*
+ * Copy completions into an array.
+ */
+ if(cnt > 0){
+ lp = possible_addrs = (char **) fs_get((cnt+1) * sizeof(*possible_addrs));
+ memset(possible_addrs, 0, (cnt+1) * sizeof(*possible_addrs));
+ da = display_addrs = (char **) fs_get((cnt+1) * sizeof(*display_addrs));
+ memset(display_addrs, 0, (cnt+1) * sizeof(*display_addrs));
+ for(cp = completions; cp; cp = cp->next){
+ show_nick = (cp->matches_bitmap & ALC_NICK) && cp->nickname && cp->nickname[0];
+ show_revfull = 0;
+ if(!show_nick && !(cp->matches_bitmap & (ALC_NICK | ALC_ADDR | ALC_FULL))
+ && (cp->matches_bitmap & ALC_REVFULL)
+ && cp->rev_fullname && cp->rev_fullname[0])
+ show_revfull = 1;
+
+ snprintf(buf, sizeof(buf), "%s%s%s%s%s",
+ cp->full_address ? cp->full_address : "?",
+ (show_nick || show_revfull) ? " (" : "",
+ show_nick ? cp->nickname : "",
+ show_revfull ? cp->rev_fullname : "",
+ (show_nick || show_revfull) ? ")" : "");
+ *da++ = cpystr(buf);
+ *lp++ = cpystr(cp->full_address ? cp->full_address : "?");
+ }
+ }
+
+ if(possible_addrs){
+ chosen_address = choose_item_from_list(possible_addrs, display_addrs,
+ _("SELECT AN ADDRESS"),
+ _("addresses"),
+ h_select_address_screen,
+ _("HELP FOR SELECTING AN ADDRESS"),
+ NULL);
+ free_list_array(&possible_addrs);
+ }
+
+ if(display_addrs)
+ free_list_array(&display_addrs);
+
+ return(chosen_address);
+}
+
+
+#ifdef _WINDOWS
+/*
+ * addr_scroll_up - adjust the global screen state struct such that pine's
+ * window on the data is shifted DOWN (i.e., the data's
+ * scrolled up).
+ */
+int
+addr_scroll_up(count)
+ long count;
+{
+ int next;
+
+ if(count < 0)
+ return(addr_scroll_down(-count));
+ else if(count){
+ long i;
+
+ i=count;
+ as.cur_row += as.top_ent;
+ while(i && as.top_ent + 1 < as.last_ent){
+ if(line_is_selectable(as.top_ent)){
+ if(next_selectable_line(as.top_ent,&next)){
+ as.cur_row = next;
+ i--;
+ as.top_ent++;
+ }
+ else i = 0;
+ }
+ else {
+ i--;
+ as.top_ent++;
+ }
+ }
+ as.cur_row = as.cur_row - as.top_ent; /* must always be positive */
+
+ as.old_cur_row = as.cur_row;
+ }
+
+ return(1);
+}
+
+
+/*
+ * addr_scroll_down - adjust the global screen state struct such that pine's
+ * window on the data is shifted UP (i.e., the data's
+ * scrolled down).
+ */
+int
+addr_scroll_down(count)
+ long count;
+{
+ if(count < 0)
+ return(addr_scroll_up(-count));
+ else if(count){
+ long i;
+
+ for(i = count; i && as.top_ent; i--, as.top_ent--)
+ as.cur_row++;
+
+ while (as.cur_row >= as.l_p_page){
+ prev_selectable_line(as.cur_row+as.top_ent, &as.cur_row);
+ as.cur_row = as.cur_row - as.top_ent;
+ }
+
+ as.old_cur_row = as.cur_row;
+ }
+
+ return(1);
+}
+
+
+/*
+ * addr_scroll_to_pos - scroll the address book data in pine's window such
+ * tthat the given "line" is at the top of the page.
+ */
+int
+addr_scroll_to_pos(line)
+ long line;
+{
+ return(addr_scroll_up(line - as.top_ent));
+}
+
+
+/*----------------------------------------------------------------------
+ MSWin scroll callback. Called during scroll message processing.
+
+
+
+ Args: cmd - what type of scroll operation.
+ scroll_pos - parameter for operation.
+ used as position for SCROLL_TO operation.
+
+ Returns: TRUE - did the scroll operation.
+ FALSE - was not able to do the scroll operation.
+ ----*/
+int
+addr_scroll_callback (cmd, scroll_pos)
+ int cmd;
+ long scroll_pos;
+{
+ int paint = TRUE;
+
+ switch (cmd) {
+ case MSWIN_KEY_SCROLLUPLINE:
+ paint = addr_scroll_down (scroll_pos);
+ break;
+
+ case MSWIN_KEY_SCROLLDOWNLINE:
+ paint = addr_scroll_up (scroll_pos);
+ break;
+
+ case MSWIN_KEY_SCROLLUPPAGE:
+ paint = addr_scroll_down (as.l_p_page);
+ break;
+
+ case MSWIN_KEY_SCROLLDOWNPAGE:
+ paint = addr_scroll_up (as.l_p_page);
+ break;
+
+ case MSWIN_KEY_SCROLLTO:
+ paint = addr_scroll_to_pos (scroll_pos);
+ break;
+ }
+
+ if(paint)
+ display_book(0, as.cur_row, -1, 1, (Pos *)NULL);
+
+ return(paint);
+}
+
+
+char *
+pcpine_help_addrbook(title)
+ char *title;
+{
+ /*
+ * Title is size 256. Fix this to pass the titlelen.
+ */
+ if(title)
+ strncpy(title, (as.config)
+ ? _("Alpine CONFIGURING ADDRESS BOOKS Help")
+ : _("Alpine ADDRESS_BOOK Help"), 256);
+
+ return(pcpine_help(gAbookHelp));
+}
+#endif /* _WINDOWS */
diff --git a/alpine/addrbook.h b/alpine/addrbook.h
new file mode 100644
index 00000000..d93ef12d
--- /dev/null
+++ b/alpine/addrbook.h
@@ -0,0 +1,69 @@
+/*
+ * $Id: addrbook.h 1012 2008-03-26 00:44:22Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_ADDRBOOK_INCLUDED
+#define PINE_ADDRBOOK_INCLUDED
+
+
+#include "../pith/adrbklib.h"
+#include "../pith/state.h"
+
+
+/*
+ * Flags to abook_nickname_complete().
+ * ANC_AFTERCOMMA means the passed in prefix
+ * looks like "stuff, stuff, prefix" and
+ * we are to peel off the stuff before the prefix,
+ * look for matches, then put the stuff back before
+ * returning the answer.
+ */
+#define ANC_AFTERCOMMA 0x1
+
+
+/* exported protoypes */
+int cur_is_open(void);
+void init_disp_form(PerAddrBook *, char **, int);
+void init_abook_screen(void);
+void save_and_restore(int, SAVE_STATE_S *);
+void save_state(SAVE_STATE_S *);
+void restore_state(SAVE_STATE_S *);
+AdrBk_Entry *ae(long);
+char *get_abook_display_line(long, int, char **, char **, int *, char *, size_t);
+void addr_book_screen(struct pine *);
+void addr_book_config(struct pine *, int);
+char *addr_book_oneaddr(void);
+char *addr_book_multaddr_nf(void);
+char *addr_book_oneaddr_nf(void);
+char *addr_book_compose(char **);
+char *addr_book_compose_lcc(char **);
+char *addr_book_change_list(char **);
+char *addr_book_bounce(void);
+char *addr_book_takeaddr(void);
+char *addr_book_nick_for_edit(char **);
+char *addr_book_selnick(void);
+char *abook_select_screen(struct pine *);
+int abook_nickname_complete(char *, char **, int, unsigned);
+int is_addr(long);
+long first_line(long);
+void ab_resize(void);
+int cur_addr_book(void);
+int resync_screen(PerAddrBook *, AddrBookArg, int);
+int nickname_check(char *, char **);
+int calculate_field_widths(void);
+void erase_selections(void);
+
+
+#endif /* PINE_ADDRBOOK_INCLUDED */
diff --git a/alpine/adrbkcmd.c b/alpine/adrbkcmd.c
new file mode 100644
index 00000000..9320160d
--- /dev/null
+++ b/alpine/adrbkcmd.c
@@ -0,0 +1,7694 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: adrbkcmd.c 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ adrbkcmds.c
+ Commands called from the addrbook screens.
+ ====*/
+
+
+#include "headers.h"
+#include "adrbkcmd.h"
+#include "addrbook.h"
+#include "takeaddr.h"
+#include "status.h"
+#include "keymenu.h"
+#include "mailview.h"
+#include "mailcmd.h"
+#include "mailindx.h"
+#include "radio.h"
+#include "folder.h"
+#include "reply.h"
+#include "help.h"
+#include "titlebar.h"
+#include "signal.h"
+#include "roleconf.h"
+#include "send.h"
+#include "../pith/adrbklib.h"
+#include "../pith/addrbook.h"
+#include "../pith/abdlc.h"
+#include "../pith/ablookup.h"
+#include "../pith/bldaddr.h"
+#include "../pith/ldap.h"
+#include "../pith/state.h"
+#include "../pith/filter.h"
+#include "../pith/conf.h"
+#include "../pith/msgno.h"
+#include "../pith/addrstring.h"
+#include "../pith/remote.h"
+#include "../pith/url.h"
+#include "../pith/util.h"
+#include "../pith/detoken.h"
+#include "../pith/stream.h"
+#include "../pith/send.h"
+#include "../pith/list.h"
+#include "../pith/busy.h"
+#include "../pith/icache.h"
+#include "../pith/osdep/color.h"
+
+
+/* internal prototypes */
+int url_hilite_abook(long, char *, LT_INS_S **, void *);
+int process_abook_view_cmd(int, MSGNO_S *, SCROLL_S *);
+int expand_addrs_for_pico(struct headerentry *, char ***);
+char *view_message_for_pico(char **);
+int verify_nick(char *, char **, char **, BUILDER_ARG *, int *);
+int verify_addr(char *, char **, char **, BUILDER_ARG *, int *);
+int pico_sendexit_for_adrbk(struct headerentry *, void(*)(void), int, char **);
+char *pico_cancelexit_for_adrbk(char *, void (*)(void));
+char *pico_cancel_for_adrbk_take(void (*)(void));
+char *pico_cancel_for_adrbk_edit(void (*)(void));
+int ab_modify_abook_list(int, int, int, char *, char *, char *);
+int convert_abook_to_remote(struct pine *, PerAddrBook *, char *, size_t, int);
+int any_rule_files_to_warn_about(struct pine *);
+int verify_folder_name(char *,char **,char **,BUILDER_ARG *, int *);
+int verify_server_name(char *,char **,char **,BUILDER_ARG *, int *);
+int verify_abook_nick(char *, char **,char **,BUILDER_ARG *, int *);
+int do_the_shuffle(int *, int, int, char **);
+void ab_compose_internal(BuildTo, int);
+int ab_export(struct pine *, long, int, int);
+VCARD_INFO_S *prepare_abe_for_vcard(struct pine *, AdrBk_Entry *, int);
+void write_single_tab_entry(gf_io_t, VCARD_INFO_S *);
+int percent_done_copying(void);
+int cmp_action_list(const qsort_t *, const qsort_t *);
+void set_act_list_member(ACTION_LIST_S *, a_c_arg_t, PerAddrBook *, PerAddrBook *, char *);
+void convert_pinerc_to_remote(struct pine *, char *);
+
+#ifdef ENABLE_LDAP
+typedef struct _saved_query {
+ char *qq,
+ *cn,
+ *sn,
+ *gn,
+ *mail,
+ *org,
+ *unit,
+ *country,
+ *state,
+ *locality,
+ *custom;
+} SAVED_QUERY_S;
+
+int process_ldap_cmd(int, MSGNO_S *, SCROLL_S *);
+int pico_simpleexit(struct headerentry *, void (*)(void), int, char **);
+char *pico_simplecancel(void (*)(void));
+void save_query_parameters(SAVED_QUERY_S *);
+SAVED_QUERY_S *copy_query_parameters(SAVED_QUERY_S *);
+void free_query_parameters(SAVED_QUERY_S **);
+int restore_query_parameters(struct headerentry *, char ***);
+
+static char *expander_address;
+#endif /* ENABLE_LDAP */
+
+static char *fakedomain = "@";
+
+
+#define VIEW_ABOOK_NONE 0
+#define VIEW_ABOOK_EDITED 1
+#define VIEW_ABOOK_WARPED 2
+
+/*
+ * View an addrbook entry.
+ * Call scrolltool to do the work.
+ */
+void
+view_abook_entry(struct pine *ps, long int cur_line)
+{
+ AdrBk_Entry *abe;
+ STORE_S *in_store, *out_store;
+ char *string, *errstr;
+ SCROLL_S sargs;
+ HANDLE_S *handles = NULL;
+ URL_HILITE_S uh;
+ gf_io_t pc, gc;
+ int cmd, abook_indent;
+ long offset = 0L;
+ char b[500];
+
+ dprint((5, "- view_abook_entry -\n"));
+
+ if(is_addr(cur_line)){
+ abe = ae(cur_line);
+ if(!abe){
+ q_status_message(SM_ORDER, 0, 3, _("Error reading entry"));
+ return;
+ }
+ }
+ else{
+ q_status_message(SM_ORDER, 0, 3, _("Nothing to view"));
+ return;
+ }
+
+ if(!(in_store = so_get(CharStar, NULL, EDIT_ACCESS))){
+ q_status_message(SM_ORDER | SM_DING, 3, 3, _("Error allocating space."));
+ return;
+ }
+
+ abook_indent = utf8_width(_("Nickname")) + 2;
+
+ /* TRANSLATORS: Nickname is a shorthand name for something */
+ utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Nickname"));
+ so_puts(in_store, b);
+ if(abe->nickname)
+ so_puts(in_store, abe->nickname);
+
+ so_puts(in_store, "\015\012");
+
+ /* TRANSLATORS: Full name is the name that goes with an email address.
+ For example, in
+ Fred Flintstone <fred@bedrock.org>
+ Fred Flintstone is the Full Name. */
+ utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Fullname"));
+ so_puts(in_store, b);
+ if(abe->fullname)
+ so_puts(in_store, abe->fullname);
+
+ so_puts(in_store, "\015\012");
+
+ /* TRANSLATORS: Fcc is an abbreviation for File carbon copy. It is like
+ a cc which is a copy of a message that goes to somebody other than the
+ main recipient, only this is a copy of a message which is put into a
+ file on the user's computer. */
+ utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Fcc"));
+ so_puts(in_store, b);
+ if(abe->fcc)
+ so_puts(in_store, abe->fcc);
+
+ so_puts(in_store, "\015\012");
+
+ utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, AB_COMMENT_STR);
+ so_puts(in_store, b);
+ if(abe->extra)
+ so_puts(in_store, abe->extra);
+
+ so_puts(in_store, "\015\012");
+
+ /* TRANSLATORS: Addresses refers to email Addresses */
+ utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Addresses"));
+ so_puts(in_store, b);
+ if(abe->tag == Single){
+ char *tmp = abe->addr.addr ? abe->addr.addr : "";
+#ifdef ENABLE_LDAP
+ if(!strncmp(tmp, QRUN_LDAP, LEN_QRL))
+ string = LDAP_DISP;
+ else
+#endif
+ string = tmp;
+
+ so_puts(in_store, string);
+ }
+ else{
+ char **ll;
+
+ for(ll = abe->addr.list; ll && *ll; ll++){
+ if(ll != abe->addr.list){
+ so_puts(in_store, "\015\012");
+ so_puts(in_store, repeat_char(abook_indent+2, SPACE));
+ }
+
+#ifdef ENABLE_LDAP
+ if(!strncmp(*ll, QRUN_LDAP, LEN_QRL))
+ string = LDAP_DISP;
+ else
+#endif
+ string = *ll;
+
+ so_puts(in_store, string);
+
+ if(*(ll+1)) /* not the last one */
+ so_puts(in_store, ",");
+ }
+ }
+
+ so_puts(in_store, "\015\012");
+
+ do{
+ if(!(out_store = so_get(CharStar, NULL, EDIT_ACCESS))){
+ so_give(&in_store);
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("Error allocating space."));
+ return;
+ }
+
+ so_seek(in_store, 0L, 0);
+
+ init_handles(&handles);
+ gf_filter_init();
+
+ if(F_ON(F_VIEW_SEL_URL, ps_global)
+ || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
+ || F_ON(F_SCAN_ADDR, ps_global))
+ gf_link_filter(gf_line_test,
+ gf_line_test_opt(url_hilite_abook,
+ gf_url_hilite_opt(&uh,&handles,0)));
+
+ gf_link_filter(gf_wrap, gf_wrap_filter_opt(ps->ttyo->screen_cols - 4,
+ ps->ttyo->screen_cols,
+ NULL, abook_indent+2,
+ GFW_HANDLES));
+ gf_link_filter(gf_nvtnl_local, NULL);
+
+ gf_set_so_readc(&gc, in_store);
+ gf_set_so_writec(&pc, out_store);
+
+ if((errstr = gf_pipe(gc, pc)) != NULL){
+ so_give(&in_store);
+ so_give(&out_store);
+ free_handles(&handles);
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ /* TRANSLATORS: %s is the error message */
+ _("Can't format entry: %s"), errstr);
+ return;
+ }
+
+ gf_clear_so_writec(out_store);
+ gf_clear_so_readc(in_store);
+
+ memset(&sargs, 0, sizeof(SCROLL_S));
+ sargs.text.text = so_text(out_store);
+ sargs.text.src = CharStar;
+ sargs.text.desc = _("expanded entry");
+ sargs.text.handles = handles;
+
+ if(offset){ /* resize? preserve paging! */
+ sargs.start.on = Offset;
+ sargs.start.loc.offset = offset;
+ offset = 0L;
+ }
+
+ /* TRANSLATORS: a screen title. We are viewing an address book */
+ sargs.bar.title = _("ADDRESS BOOK (View)");
+ sargs.bar.style = TextPercent;
+ sargs.proc.tool = process_abook_view_cmd;
+ sargs.proc.data.i = VIEW_ABOOK_NONE;
+ sargs.resize_exit = 1;
+ sargs.help.text = h_abook_view;
+ /* TRANSLATORS: help screen title */
+ sargs.help.title = _("HELP FOR ADDRESS BOOK VIEW");
+ sargs.keys.menu = &abook_view_keymenu;
+ setbitmap(sargs.keys.bitmap);
+
+ if(handles)
+ sargs.keys.menu->how_many = 2;
+ else{
+ sargs.keys.menu->how_many = 1;
+ clrbitn(OTHER_KEY, sargs.keys.bitmap);
+ }
+
+ if((cmd = scrolltool(&sargs)) == MC_RESIZE)
+ offset = sargs.start.loc.offset;
+
+ so_give(&out_store);
+ free_handles(&handles);
+ }
+ while(cmd == MC_RESIZE);
+
+ so_give(&in_store);
+
+ if(sargs.proc.data.i != VIEW_ABOOK_NONE){
+ long old_l_p_p, old_top_ent, old_cur_row;
+
+ if(sargs.proc.data.i == VIEW_ABOOK_WARPED){
+ /*
+ * Warped means we got plopped down somewhere in the display
+ * list so that we don't know where we are relative to where
+ * we were before we warped. The current line number will
+ * be zero, since that is what the warp would have set.
+ */
+ as.top_ent = first_line(0L - as.l_p_page/2L);
+ as.cur_row = 0L - as.top_ent;
+ }
+ else if(sargs.proc.data.i == VIEW_ABOOK_EDITED){
+ old_l_p_p = as.l_p_page;
+ old_top_ent = as.top_ent;
+ old_cur_row = as.cur_row;
+ }
+
+ /* Window size may have changed while in pico. */
+ ab_resize();
+
+ /* fix up what ab_resize messed up */
+ if(sargs.proc.data.i != VIEW_ABOOK_WARPED && old_l_p_p == as.l_p_page){
+ as.top_ent = old_top_ent;
+ as.cur_row = old_cur_row;
+ as.old_cur_row = old_cur_row;
+ }
+
+ cur_line = as.top_ent+as.cur_row;
+ }
+
+ ps->mangled_screen = 1;
+}
+
+
+int
+url_hilite_abook(long int linenum, char *line, LT_INS_S **ins, void *local)
+{
+ register char *lp;
+
+ if((lp = strchr(line, ':')) &&
+ !strncmp(line, AB_COMMENT_STR, strlen(AB_COMMENT_STR)))
+ (void) url_hilite(linenum, lp + 1, ins, local);
+
+ return(0);
+}
+
+
+int
+process_abook_view_cmd(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
+{
+ int rv = 1, i;
+ PerAddrBook *pab;
+ AddrScrn_Disp *dl;
+ static ESCKEY_S text_or_vcard[] = {
+ /* TRANSLATORS: Text refers to plain old text, probably the text of
+ an email message */
+ {'t', 't', "T", N_("Text")},
+ /* TRANSLATORS: A VCard is kind of like an electronic business card. It is not
+ something specific to alpine, it is universal. */
+ {'v', 'v', "V", N_("VCard")},
+ {-1, 0, NULL, NULL}};
+
+ ps_global->next_screen = SCREEN_FUN_NULL;
+
+ switch(cmd){
+ case MC_EDIT :
+ /*
+ * MC_EDIT works a little differently from the other cmds here.
+ * The others return 0 to scrolltool so that we are still in
+ * the view screen. This one is different because we may have
+ * changed what we're viewing. We handle that by returning 1
+ * to scrolltool and setting the sparms opt union's gint
+ * to the value below.
+ *
+ * (Late breaking news. Now we're going to return 1 from all these
+ * commands and then in the caller we're going to bounce back out
+ * to the index view instead of the view of the individual entry.
+ * So there is some dead code around for now.)
+ *
+ * Then, in the view_abook_entry function we check the value
+ * of this field on scrolltool's return and if it is one of
+ * the two special values below view_abook_entry resets the
+ * current line if necessary, flushes the display cache (in
+ * ab_resize) and loops back and starts over, effectively
+ * putting us back in the view screen but with updated
+ * contents. A side effect is that the screen above that (the
+ * abook index) will also have been flushed and corrected by
+ * the ab_resize.
+ */
+ pab = &as.adrbks[cur_addr_book()];
+ if(pab && pab->access == ReadOnly){
+ /* TRANSLATORS: Address book can be viewed but not changed */
+ q_status_message(SM_ORDER, 0, 4, _("AddressBook is Read Only"));
+ rv = 0;
+ break;
+ }
+
+ if(adrbk_check_all_validity_now()){
+ if(resync_screen(pab, AddrBookScreen, 0)){
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ /* TRANSLATORS: The address book was changed by some other
+ process. The user is being told that their change (their
+ Update) has been canceled and they should try again. */
+ _("Address book changed. Update cancelled. Try again."));
+ ps_global->mangled_screen = 1;
+ break;
+ }
+ }
+
+ if(pab && pab->access == ReadOnly){
+ q_status_message(SM_ORDER, 0, 4, _("AddressBook is Read Only"));
+ rv = 0;
+ break;
+ }
+
+ /*
+ * Arguments all come from globals, not from the arguments to
+ * process_abook_view_cmd. It would be a little cleaner if the
+ * information was contained in att, I suppose.
+ */
+ if(is_addr(as.cur_row+as.top_ent)){
+ AdrBk_Entry *abe, *abe_copy;
+ a_c_arg_t entry;
+ int warped = 0;
+
+ dl = dlist(as.top_ent+as.cur_row);
+ entry = dl->elnum;
+ abe = adrbk_get_ae(pab->address_book, entry);
+ abe_copy = copy_ae(abe);
+ dprint((9,"Calling edit_entry to edit from view\n"));
+ /* TRANSLATORS: update as in update an address book entry */
+ edit_entry(pab->address_book, abe_copy, entry,
+ abe->tag, 0, &warped, _("update"));
+ /*
+ * The ABOOK_EDITED case doesn't mean that we necessarily
+ * changed something, just that we might have but we know
+ * we didn't change the sort order (causing the warp).
+ */
+ sparms->proc.data.i = warped
+ ? VIEW_ABOOK_WARPED : VIEW_ABOOK_EDITED;
+
+ free_ae(&abe_copy);
+ rv = 1; /* causes scrolltool to return */
+ }
+ else{
+ q_status_message(SM_ORDER, 0, 4,
+ "Something wrong, entry not updateable");
+ rv = 0;
+ break;
+ }
+
+ break;
+
+ case MC_SAVE :
+ pab = &as.adrbks[cur_addr_book()];
+ /*
+ * Arguments all come from globals, not from the arguments to
+ * process_abook_view_cmd. It would be a little cleaner if the
+ * information was contained in att, I suppose.
+ */
+ (void)ab_save(ps_global, pab ? pab->address_book : NULL,
+ as.top_ent+as.cur_row, -FOOTER_ROWS(ps_global), 0);
+ rv = 1;
+ break;
+
+ case MC_COMPOSE :
+ (void)ab_compose_to_addr(as.top_ent+as.cur_row, 0, 0);
+ rv = 1;
+ break;
+
+ case MC_ROLE :
+ (void)ab_compose_to_addr(as.top_ent+as.cur_row, 0, 1);
+ rv = 1;
+ break;
+
+ case MC_FORWARD :
+ rv = 1;
+ /* TRANSLATORS: A question with two choices for the answer. Forward
+ as text means to include the text of the message being forwarded
+ in the new message. Forward as a Vcard attachment means to
+ attach it to the message in a special format so it is recognizable
+ as a Vcard. */
+ i = radio_buttons(_("Forward as text or forward as Vcard attachment ? "),
+ -FOOTER_ROWS(ps_global), text_or_vcard, 't', 'x',
+ h_ab_text_or_vcard, RB_NORM);
+ switch(i){
+ case 'x':
+ q_status_message(SM_INFO, 0, 2, _("Address book forward cancelled"));
+ rv = 0;
+ break;
+
+ case 't':
+ forward_text(ps_global, sparms->text.text, sparms->text.src);
+ break;
+
+ case 'v':
+ (void)ab_forward(ps_global, as.top_ent+as.cur_row, 0);
+ break;
+
+ default:
+ q_status_message(SM_ORDER, 3, 3,
+ "can't happen in process_abook_view_cmd");
+ break;
+ }
+
+ break;
+
+ default:
+ panic("Unexpected command in process_abook_view_cmd");
+ break;
+ }
+
+ return(rv);
+}
+
+
+/*
+ * Give expanded view of this address entry.
+ * Call scrolltool to do the work.
+ *
+ * Args: headents -- The headerentry array from pico.
+ * s -- Unused here.
+ *
+ * Returns -- Always 0.
+ */
+int
+expand_addrs_for_pico(struct headerentry *headents, char ***s)
+{
+ BuildTo bldto;
+ STORE_S *store;
+ char *error = NULL, *addr = NULL, *fullname = NULL, *address = NULL;
+ SAVE_STATE_S state;
+ ADDRESS *adrlist = NULL, *a;
+ int j, address_index = -1, fullname_index = -1, no_a_fld = 0;
+ void (*redraw)(void) = ps_global->redrawer;
+
+ char *tmp, *tmp2, *tmp3;
+ SCROLL_S sargs;
+ AdrBk_Entry abe;
+ char fakeaddrpmt[500];
+ int abook_indent;
+
+ dprint((5, "- expand_addrs_for_pico -\n"));
+
+ abook_indent = utf8_width(_("Nickname")) + 2;
+ utf8_snprintf(fakeaddrpmt, sizeof(fakeaddrpmt), "%-*.*w: ", abook_indent, abook_indent, _("Address"));
+
+ if(s)
+ *s = NULL;
+
+ ps_global->redrawer = NULL;
+ fix_windsize(ps_global);
+
+ ab_nesting_level++;
+ save_state(&state);
+
+ for(j=0;
+ headents[j].name != NULL && (address_index < 0 || fullname_index < 0);
+ j++){
+ if(!strncmp(headents[j].name, "Address", 7) || !strncmp(headents[j].name, _("Address"), strlen(_("Address"))))
+ address_index = j;
+ else if(!strncmp(headents[j].name, "Fullname", 8) || !strncmp(headents[j].name, _("Fullname"), strlen(_("Fullname"))))
+ fullname_index = j;
+ }
+
+ if(address_index >= 0)
+ address = *headents[address_index].realaddr;
+ else{
+ address_index = 1000; /* a big number */
+ no_a_fld++;
+ }
+
+ if(fullname_index >= 0)
+ fullname = adrbk_formatname(*headents[fullname_index].realaddr,
+ NULL, NULL);
+
+ memset(&abe, 0, sizeof(abe));
+ if(fullname)
+ abe.fullname = cpystr(fullname);
+
+ if(address){
+ char *tmp_a_string;
+
+ tmp_a_string = cpystr(address);
+ rfc822_parse_adrlist(&adrlist, tmp_a_string, fakedomain);
+ fs_give((void **)&tmp_a_string);
+ if(adrlist && adrlist->next){
+ int cnt = 0;
+
+ abe.tag = List;
+ for(a = adrlist; a; a = a->next)
+ cnt++;
+
+ abe.addr.list = (char **)fs_get((cnt+1) * sizeof(char *));
+ cnt = 0;
+ for(a = adrlist; a; a = a->next){
+ if(a->host && a->host[0] == '@')
+ abe.addr.list[cnt++] = cpystr(a->mailbox);
+ else if(a->host && a->host[0] && a->mailbox && a->mailbox[0])
+ abe.addr.list[cnt++] =
+ cpystr(simple_addr_string(a, tmp_20k_buf,
+ SIZEOF_20KBUF));
+ }
+
+ abe.addr.list[cnt] = '\0';
+ }
+ else{
+ abe.tag = Single;
+ abe.addr.addr = address;
+ }
+
+ if(adrlist)
+ mail_free_address(&adrlist);
+
+ bldto.type = Abe;
+ bldto.arg.abe = &abe;
+ our_build_address(bldto, &addr, &error, NULL, NULL);
+ if(error){
+ q_status_message1(SM_ORDER, 3, 4, "%s", error);
+ fs_give((void **)&error);
+ }
+
+ if(addr){
+ tmp_a_string = cpystr(addr);
+ rfc822_parse_adrlist(&adrlist, tmp_a_string, ps_global->maildomain);
+ fs_give((void **)&tmp_a_string);
+ }
+ }
+#ifdef ENABLE_LDAP
+ else if(no_a_fld && expander_address){
+ WP_ERR_S wp_err;
+
+ memset(&wp_err, 0, sizeof(wp_err));
+ adrlist = wp_lookups(expander_address, &wp_err, 0);
+ if(fullname && *fullname){
+ if(adrlist){
+ if(adrlist->personal)
+ fs_give((void **)&adrlist->personal);
+
+ adrlist->personal = cpystr(fullname);
+ }
+ }
+
+ if(wp_err.error){
+ q_status_message1(SM_ORDER, 3, 4, "%s", wp_err.error);
+ fs_give((void **)&wp_err.error);
+ }
+ }
+#endif
+
+ if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
+ q_status_message(SM_ORDER | SM_DING, 3, 3, _("Error allocating space."));
+ restore_state(&state);
+ return(0);
+ }
+
+ for(j = 0; j < address_index && headents[j].name != NULL; j++){
+ if((tmp = fold(*headents[j].realaddr,
+ ps_global->ttyo->screen_cols,
+ ps_global->ttyo->screen_cols,
+ headents[j].prompt,
+ repeat_char(headents[j].prwid, SPACE), FLD_NONE)) != NULL){
+ so_puts(store, tmp);
+ fs_give((void **)&tmp);
+ }
+ }
+
+ /*
+ * There is an assumption that Addresses is the last field.
+ */
+ tmp3 = cpystr(repeat_char(headents[0].prwid + 2, SPACE));
+ if(adrlist){
+ for(a = adrlist; a; a = a->next){
+ ADDRESS *next_addr;
+ char *bufp;
+ size_t len;
+
+ next_addr = a->next;
+ a->next = NULL;
+ len = est_size(a);
+ bufp = (char *) fs_get(len * sizeof(char));
+ (void) addr_string(a, bufp, len);
+ a->next = next_addr;
+
+ /*
+ * Another assumption, all the prwids are the same.
+ */
+ if((tmp = fold(bufp,
+ ps_global->ttyo->screen_cols,
+ ps_global->ttyo->screen_cols,
+ (a == adrlist) ? (no_a_fld
+ ? fakeaddrpmt
+ : headents[address_index].prompt)
+ : tmp3+2,
+ tmp3, FLD_NONE)) != NULL){
+ so_puts(store, tmp);
+ fs_give((void **) &tmp);
+ }
+
+ fs_give((void **) &bufp);
+ }
+ }
+ else{
+ tmp2 = NULL;
+ if((tmp = fold(tmp2=cpystr("<none>"),
+ ps_global->ttyo->screen_cols,
+ ps_global->ttyo->screen_cols,
+ no_a_fld ? fakeaddrpmt
+ : headents[address_index].prompt,
+ tmp3, FLD_NONE)) != NULL){
+ so_puts(store, tmp);
+ fs_give((void **) &tmp);
+ }
+
+ if(tmp2)
+ fs_give((void **)&tmp2);
+ }
+
+ if(tmp3)
+ fs_give((void **)&tmp3);
+
+ memset(&sargs, 0, sizeof(SCROLL_S));
+ sargs.text.text = so_text(store);
+ sargs.text.src = CharStar;
+ sargs.text.desc = _("expanded entry");
+ /* TRANSLATORS: screen title for viewing an address book entry in Rich
+ view mode. Rich just means it is expanded to include everything. */
+ sargs.bar.title = _("ADDRESS BOOK (Rich View)");
+ sargs.bar.style = TextPercent;
+ sargs.keys.menu = &abook_text_km;
+ setbitmap(sargs.keys.bitmap);
+
+ scrolltool(&sargs);
+ so_give(&store);
+
+ restore_state(&state);
+ ab_nesting_level--;
+
+ if(addr)
+ fs_give((void **)&addr);
+
+ if(adrlist)
+ mail_free_address(&adrlist);
+
+ if(fullname)
+ fs_give((void **)&fullname);
+
+ if(abe.fullname)
+ fs_give((void **)&abe.fullname);
+
+ if(abe.tag == List && abe.addr.list)
+ free_list_array(&(abe.addr.list));
+
+ ps_global->redrawer = redraw;
+
+ return(0);
+}
+
+
+/*
+ * Callback from TakeAddr editing screen to see message that was being
+ * viewed. Call scrolltool to do the work.
+ */
+char *
+view_message_for_pico(char **error)
+{
+ STORE_S *store;
+ gf_io_t pc;
+ void (*redraw)(void) = ps_global->redrawer;
+ SourceType src = CharStar;
+ SCROLL_S sargs;
+
+ dprint((5, "- view_message_for_pico -\n"));
+
+ ps_global->redrawer = NULL;
+ fix_windsize(ps_global);
+
+#ifdef DOS
+ src = TmpFileStar;
+#endif
+
+ if(!(store = so_get(src, NULL, EDIT_ACCESS))){
+ q_status_message(SM_ORDER | SM_DING, 3, 3, _("Error allocating space."));
+ return(NULL);
+ }
+
+ gf_set_so_writec(&pc, store);
+
+ format_message(msgno_for_pico_callback, env_for_pico_callback,
+ body_for_pico_callback, NULL, FM_NEW_MESS | FM_DISPLAY, pc);
+
+ gf_clear_so_writec(store);
+
+ memset(&sargs, 0, sizeof(SCROLL_S));
+ sargs.text.text = so_text(store);
+ sargs.text.src = src;
+ sargs.text.desc = _("expanded entry");
+ /* TRANSLATORS: this is a screen title */
+ sargs.bar.title = _("MESSAGE TEXT");
+ sargs.bar.style = TextPercent;
+ sargs.keys.menu = &abook_text_km;
+ setbitmap(sargs.keys.bitmap);
+
+ scrolltool(&sargs);
+
+ so_give(&store);
+
+ ps_global->redrawer = redraw;
+
+ return(NULL);
+}
+
+
+/*
+prompt::name::help::prwid::maxlen::realaddr::
+builder::affected_entry::next_affected::selector::key_label::fileedit::nickcmpl
+display_it::break_on_comma::is_attach::rich_header::only_file_chars::
+single_space::sticky::dirty::start_here::blank::KS_ODATAVAR
+*/
+static struct headerentry headents_for_edit[]={
+ {"Nickname : ", N_("Nickname"), h_composer_abook_nick, 12, 0, NULL,
+ /* TRANSLATORS: To AddrBk is a command that takes the user to
+ the address book screen to select an entry from there. */
+ verify_nick, NULL, NULL, addr_book_nick_for_edit, N_("To AddrBk"), NULL, abook_nickname_complete,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"Fullname : ", N_("Fullname"), h_composer_abook_full, 12, 0, NULL,
+ NULL, NULL, NULL, view_message_for_pico, N_("To Message"), NULL, NULL,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ /* TRANSLATORS: a File Copy is a copy of a sent message saved in a regular
+ file on the computer's disk */
+ {"Fcc : ", N_("FileCopy"), h_composer_abook_fcc, 12, 0, NULL,
+ /* TRANSLATORS: To Folders */
+ NULL, NULL, NULL, folders_for_fcc, N_("To Fldrs"), NULL, NULL,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"Comment : ", N_("Comment"), h_composer_abook_comment, 12, 0, NULL,
+ NULL, NULL, NULL, view_message_for_pico, N_("To Message"), NULL, NULL,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"Addresses : ", N_("Addresses"), h_composer_abook_addrs, 12, 0, NULL,
+ verify_addr, NULL, NULL, addr_book_change_list, N_("To AddrBk"), NULL, abook_nickname_complete,
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {NULL, NULL, NO_HELP, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}
+};
+#define NNN_NICK 0
+#define NNN_FULL 1
+#define NNN_FCC 2
+#define NNN_COMMENT 3
+#define NNN_ADDR 4
+#define NNN_END 5
+
+static char *nick_saved_for_pico_check;
+static AdrBk *abook_saved_for_pico_check;
+
+/*
+ * Args: abook -- Address book handle
+ * abe -- AdrBk_Entry of old entry to work on. If NULL, this will
+ * be a new entry. This has to be a pointer to a copy of
+ * an abe that won't go away until we finish this function.
+ * In other words, don't pass in a pointer to an abe in
+ * the cache, copy it first. The tag on this abe is only
+ * used to decide whether to read abe->addr.list or
+ * abe->addr.addr, not to determine what the final result
+ * will be. That's determined solely by how many addresses
+ * there are after the user edits.
+ * entry -- The entry number of the old entry that we will be changing.
+ * old_tag -- If we're changing an old entry, then this is the tag of
+ * that old entry.
+ * readonly -- Call pico with readonly flag
+ * warped -- We warped to a new part of the addrbook
+ * (We also overload warped in a couple places and use it's
+ * being set as an indicator of whether we are Taking or
+ * not. It will be NULL if we are Taking.)
+ */
+void
+edit_entry(AdrBk *abook, AdrBk_Entry *abe, a_c_arg_t entry, Tag old_tag, int readonly, int *warped, char *cmd)
+{
+ AdrBk_Entry local_abe;
+ struct headerentry *he;
+ PICO pbf;
+ STORE_S *msgso;
+ adrbk_cntr_t old_entry_num, new_entry_num = NO_NEXT;
+ int rc = 0, resort_happened = 0, list_changed = 0, which_addrbook;
+ int editor_result, i = 0, add, n_end, ldap = 0;
+ char *nick, *full, *fcc, *comment, *fname, *pp;
+ char **orig_addrarray = NULL;
+ char **new_addrarray = NULL;
+ char *comma_sep_addr = NULL;
+ char **p, **q;
+ Tag new_tag;
+ char titlebar[40];
+ char nickpmt[100], fullpmt[100], fccpmt[100], cmtpmt[100], addrpmt[100];
+ int abook_indent;
+ long length;
+ SAVE_STATE_S state; /* For saving state of addrbooks temporarily */
+
+ dprint((2, "- edit_entry -\n"));
+
+ old_entry_num = (adrbk_cntr_t) entry;
+ save_state(&state);
+ abook_saved_for_pico_check = abook;
+
+ add = (abe == NULL); /* doing add or change? */
+ if(add){
+ local_abe.nickname = "";
+ local_abe.fullname = "";
+ local_abe.fcc = "";
+ local_abe.extra = "";
+ local_abe.addr.addr = "";
+ local_abe.tag = NotSet;
+ abe = &local_abe;
+ old_entry_num = NO_NEXT;
+ }
+
+ new_tag = abe->tag;
+
+#ifdef ENABLE_LDAP
+ expander_address = NULL;
+ if(abe->tag == Single &&
+ abe->addr.addr &&
+ !strncmp(abe->addr.addr,QRUN_LDAP,LEN_QRL)){
+ ldap = 1;
+ expander_address = cpystr(abe->addr.addr);
+ removing_double_quotes(expander_address);
+ }
+ else if(abe->tag == List &&
+ abe->addr.list &&
+ abe->addr.list[0] &&
+ !abe->addr.list[1] &&
+ !strncmp(abe->addr.list[0],QRUN_LDAP,LEN_QRL)){
+ ldap = 2;
+ expander_address = cpystr(abe->addr.list[0]);
+ removing_double_quotes(expander_address);
+ }
+#endif
+
+ standard_picobuf_setup(&pbf);
+ pbf.exittest = pico_sendexit_for_adrbk;
+ pbf.canceltest = warped ? pico_cancel_for_adrbk_edit
+ : pico_cancel_for_adrbk_take;
+ pbf.expander = expand_addrs_for_pico;
+ pbf.ctrlr_label = _("RichView");
+ /* xgettext: c-format */
+ if(readonly)
+ /* TRANSLATORS: screen titles */
+ snprintf(titlebar, sizeof(titlebar), _("ADDRESS BOOK (View)"));
+ else
+ snprintf(titlebar, sizeof(titlebar), _("ADDRESS BOOK (%c%s)"),
+ islower((unsigned char)(*cmd))
+ ? toupper((unsigned char)*cmd)
+ : *cmd, (cmd+1));
+
+ pbf.pine_anchor = set_titlebar(titlebar,
+ ps_global->mail_stream,
+ ps_global->context_current,
+ ps_global->cur_folder,ps_global->msgmap,
+ 0, FolderName, 0, 0, NULL);
+ pbf.pine_flags |= P_NOBODY;
+ if(readonly)
+ pbf.pine_flags |= P_VIEW;
+
+ /* An informational message */
+ if((msgso = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
+ pbf.msgtext = (void *)so_text(msgso);
+ /*
+ * It's nice if we can make it so these lines make sense even if
+ * they don't all make it on the screen, because the user can't
+ * scroll down to see them. So just make each line a whole sentence
+ * that doesn't need the others below it to make sense.
+ */
+ if(add){
+ so_puts(msgso,
+/*
+ * TRANSLATORS: The following lines go together to form a screen of
+ * explanation about how to edit an address book entry.
+ */
+_("\n Fill in the fields. It is ok to leave fields blank."));
+ so_puts(msgso,
+_("\n To form a list, just enter multiple comma-separated addresses."));
+ }
+ else{
+ so_puts(msgso,
+/* TRANSLATORS: Same here, but a different version of the screen. */
+_("\n Change any of the fields. It is ok to leave fields blank."));
+ if(ldap)
+ so_puts(msgso,
+_("\n Since this entry does a directory lookup you may not edit the address field."));
+ else
+ so_puts(msgso,
+_("\n Additional comma-separated addresses may be entered in the address field."));
+ }
+
+ so_puts(msgso,
+_("\n Press \"^X\" to save the entry, \"^C\" to cancel, \"^G\" for help."));
+ so_puts(msgso,
+_("\n If you want to use quotation marks inside the Fullname field, it is best"));
+ so_puts(msgso,
+_("\n to use single quotation marks; for example: George 'Husky' Washington."));
+ }
+
+ he = (struct headerentry *) fs_get((NNN_END+1) * sizeof(struct headerentry));
+ memset((void *)he, 0, (NNN_END+1) * sizeof(struct headerentry));
+ pbf.headents = he;
+
+ abook_indent = utf8_width(_("Nickname")) + 2;
+
+ /* make a copy of each field */
+ nick = cpystr(abe->nickname ? abe->nickname : "");
+ removing_leading_and_trailing_white_space(nick);
+ nick_saved_for_pico_check = cpystr(nick);
+ he[NNN_NICK] = headents_for_edit[NNN_NICK];
+ he[NNN_NICK].realaddr = &nick;
+ utf8_snprintf(nickpmt, sizeof(nickpmt), "%-*.*w: ", abook_indent, abook_indent, _("Nickname"));
+ he[NNN_NICK].prompt = nickpmt;
+ he[NNN_NICK].prwid = abook_indent+2;
+ if(F_OFF(F_ENABLE_TAB_COMPLETE,ps_global))
+ he[NNN_NICK].nickcmpl = NULL;
+
+ full = cpystr(abe->fullname ? abe->fullname : "");
+ removing_leading_and_trailing_white_space(full);
+ he[NNN_FULL] = headents_for_edit[NNN_FULL];
+ he[NNN_FULL].realaddr = &full;
+ utf8_snprintf(fullpmt, sizeof(fullpmt), "%-*.*w: ", abook_indent, abook_indent, _("Fullname"));
+ he[NNN_FULL].prompt = fullpmt;
+ he[NNN_FULL].prwid = abook_indent+2;
+
+ fcc = cpystr(abe->fcc ? abe->fcc : "");
+ removing_leading_and_trailing_white_space(fcc);
+ he[NNN_FCC] = headents_for_edit[NNN_FCC];
+ he[NNN_FCC].realaddr = &fcc;
+ utf8_snprintf(fccpmt, sizeof(fccpmt), "%-*.*w: ", abook_indent, abook_indent, _("Fcc"));
+ he[NNN_FCC].prompt = fccpmt;
+ he[NNN_FCC].prwid = abook_indent+2;
+
+ comment = cpystr(abe->extra ? abe->extra : "");
+ removing_leading_and_trailing_white_space(comment);
+ he[NNN_COMMENT] = headents_for_edit[NNN_COMMENT];
+ he[NNN_COMMENT].realaddr = &comment;
+ utf8_snprintf(cmtpmt, sizeof(cmtpmt), "%-*.*w: ", abook_indent, abook_indent, _("Comment"));
+ he[NNN_COMMENT].prompt = cmtpmt;
+ he[NNN_COMMENT].prwid = abook_indent+2;
+
+ n_end = NNN_END;
+ if(ldap)
+ n_end--;
+
+ if(!ldap){
+ he[NNN_ADDR] = headents_for_edit[NNN_ADDR];
+ he[NNN_ADDR].realaddr = &comma_sep_addr;
+ utf8_snprintf(addrpmt, sizeof(addrpmt), "%-*.*w: ", abook_indent, abook_indent, _("Addresses"));
+ he[NNN_ADDR].prompt = addrpmt;
+ he[NNN_ADDR].prwid = abook_indent+2;
+ if(F_OFF(F_ENABLE_TAB_COMPLETE,ps_global))
+ he[NNN_NICK].nickcmpl = NULL;
+
+ if(abe->tag == Single){
+ if(abe->addr.addr){
+ orig_addrarray = (char **) fs_get(2 * sizeof(char *));
+ orig_addrarray[0] = cpystr(abe->addr.addr);
+ orig_addrarray[1] = NULL;
+ }
+ }
+ else if(abe->tag == List){
+ if(listmem_count_from_abe(abe) > 0){
+ orig_addrarray = (char **) fs_get(
+ (size_t)(listmem_count_from_abe(abe) + 1)
+ * sizeof(char *));
+ for(q = orig_addrarray, p = abe->addr.list; p && *p; p++, q++)
+ *q = cpystr(*p);
+
+ *q = NULL;
+ }
+ }
+
+ /* figure out how large a string we need to allocate */
+ length = 0L;
+ for(p = orig_addrarray; p && *p; p++)
+ length += (strlen(*p) + 2);
+
+ if(length)
+ length -= 2L;
+
+ pp = comma_sep_addr = (char *) fs_get((size_t)(length+1L) * sizeof(char));
+ *pp = '\0';
+ for(p = orig_addrarray; p && *p; p++){
+ sstrncpy(&pp, *p, length-(pp-comma_sep_addr));
+ if(*(p+1))
+ sstrncpy(&pp, ", ", length-(pp-comma_sep_addr));
+ }
+
+ comma_sep_addr[length] = '\0';
+
+ if(verify_addr(comma_sep_addr, NULL, NULL, NULL, NULL) < 0)
+ he[NNN_ADDR].start_here = 1;
+ }
+
+ he[n_end] = headents_for_edit[NNN_END];
+ for(i = 0; i < n_end; i++){
+ /* no callbacks in some cases */
+ if(readonly || ((i == NNN_FULL || i == NNN_COMMENT) && !env_for_pico_callback)){
+ he[i].selector = NULL;
+ he[i].key_label = NULL;
+ }
+
+ /* no builders for readonly */
+ if(readonly)
+ he[i].builder = NULL;
+ }
+
+ /* pass to pico and let user change them */
+ editor_result = pico(&pbf);
+ ps_global->mangled_screen = 1;
+ standard_picobuf_teardown(&pbf);
+
+ if(editor_result & COMP_GOTHUP)
+ hup_signal();
+ else{
+ fix_windsize(ps_global);
+ init_signals();
+ }
+
+ if(editor_result & COMP_CANCEL){
+ if(!readonly)
+ /* TRANSLATOR: Something like
+ Address book save cancelled */
+ q_status_message1(SM_INFO, 0, 2, _("Address book %s cancelled"), cmd);
+ }
+ else if(editor_result & COMP_EXIT){
+ if(pico_usingcolor())
+ clear_index_cache(ps_global->mail_stream, 0);
+ removing_leading_and_trailing_white_space(nick);
+ removing_leading_and_trailing_white_space(full);
+ removing_leading_and_trailing_white_space(fcc);
+ removing_leading_and_trailing_white_space(comment);
+ removing_leading_and_trailing_white_space(comma_sep_addr);
+
+ /* not needed if pico is returning UTF-8 */
+ convert_possibly_encoded_str_to_utf8(&nick);
+ convert_possibly_encoded_str_to_utf8(&full);
+ convert_possibly_encoded_str_to_utf8(&fcc);
+ convert_possibly_encoded_str_to_utf8(&comment);
+ convert_possibly_encoded_str_to_utf8(&comma_sep_addr);
+
+ /* don't allow adding null entry */
+ if(add && !*nick && !*full && !*fcc && !*comment && !*comma_sep_addr)
+ goto outtahere;
+
+ /*
+ * comma_sep_addr is now the string which has been edited
+ */
+ if(comma_sep_addr)
+ new_addrarray = parse_addrlist(comma_sep_addr);
+
+ if(!ldap && (!new_addrarray || !new_addrarray[0]))
+ q_status_message(SM_ORDER, 3, 5, _("Warning: entry has no addresses"));
+
+ if(!new_addrarray || !new_addrarray[0] || !new_addrarray[1])
+ new_tag = Single; /* one or zero addresses means its a Single */
+ else
+ new_tag = List; /* more than one addresses means its a List */
+
+ if(new_tag == List && old_tag == List){
+ /*
+ * If Taking, make sure we write it even if user didn't edit
+ * it any further.
+ */
+ if(!warped)
+ list_changed++;
+ else if(he[NNN_ADDR].dirty)
+ for(q = orig_addrarray, p = new_addrarray; p && *p && q && *q; p++, q++)
+ if(strcmp(*p, *q) != 0){
+ list_changed++;
+ break;
+ }
+
+ if(!list_changed && he[NNN_ADDR].dirty
+ && ((!(p && *p) && (q && *q)) || ((p && *p) && !(q && *q))))
+ list_changed++;
+
+ if(list_changed){
+ /*
+ * need to delete old list members and add new members below
+ */
+ rc = adrbk_listdel_all(abook, (a_c_arg_t) old_entry_num);
+ }
+ else{
+ /* don't need new_addrarray */
+ free_list_array(&new_addrarray);
+ }
+
+ if(comma_sep_addr)
+ fs_give((void **) &comma_sep_addr);
+ }
+ else if((new_tag == List && old_tag == Single)
+ || (new_tag == Single && old_tag == List)){
+ /* delete old entry */
+ rc = adrbk_delete(abook, (a_c_arg_t) old_entry_num, 0, 0, 0, 0);
+ old_entry_num = NO_NEXT;
+ if(comma_sep_addr && new_tag == List)
+ fs_give((void **) &comma_sep_addr);
+ }
+
+ /*
+ * This will be an edit in the cases where the tag didn't change
+ * and an add in the cases where it did.
+ */
+ if(rc == 0)
+ rc = adrbk_add(abook,
+ (a_c_arg_t)old_entry_num,
+ nick,
+ he[NNN_FULL].dirty ? full : abe->fullname,
+ new_tag == Single ? (ldap == 1 ? abe->addr.addr :
+ ldap == 2 ? abe->addr.list[0] :
+ comma_sep_addr)
+ : NULL,
+ fcc,
+ he[NNN_COMMENT].dirty ? comment : abe->extra,
+ new_tag,
+ &new_entry_num,
+ &resort_happened,
+ 1,
+ 0,
+ (new_tag != List || !new_addrarray));
+ }
+
+ if(rc == 0 && new_tag == List && new_addrarray)
+ rc = adrbk_nlistadd(abook, (a_c_arg_t) new_entry_num, &new_entry_num,
+ &resort_happened, new_addrarray, 1, 0, 1);
+
+ restore_state(&state);
+
+ if(rc == -2 || rc == -3){
+ q_status_message1(SM_ORDER | SM_DING, 3, 4,
+ _("Error updating address book: %s"),
+ rc == -2 ? error_description(errno) : "Alpine bug");
+ }
+ else if(rc == 0
+ && strucmp(nick, nick_saved_for_pico_check) != 0
+ && (editor_result & COMP_EXIT)){
+ int added_to;
+
+ for(added_to = 0; added_to < as.n_addrbk; added_to++)
+ if(abook_saved_for_pico_check == as.adrbks[added_to].address_book)
+ break;
+
+ if(added_to >= as.n_addrbk)
+ added_to = -1;
+
+ fname = addr_lookup(nick, &which_addrbook, added_to);
+ if(fname){
+ q_status_message4(SM_ORDER, 5, 9,
+ /* TRANSLATORS: The first %s is the nickname the user is
+ trying to use that exists in another address book.
+ The second %s is the name of the other address book.
+ The third %s is " as " because it will say something
+ like also exists in <name> as <description>. */
+ _("Warning! Nickname %s also exists in \"%s\"%s%s"),
+ nick, as.adrbks[which_addrbook].abnick,
+ (fname && *fname) ? _(" as ") : "",
+ (fname && *fname) ? fname : "");
+ fs_give((void **)&fname);
+ }
+ }
+
+ if(resort_happened || list_changed){
+ DL_CACHE_S dlc_restart;
+
+ dlc_restart.adrbk_num = as.cur;
+ dlc_restart.dlcelnum = new_entry_num;
+ switch(new_tag){
+ case Single:
+ dlc_restart.type = DlcSimple;
+ break;
+
+ case List:
+ dlc_restart.type = DlcListHead;
+ break;
+
+ default:
+ break;
+ }
+
+ warp_to_dlc(&dlc_restart, 0L);
+ if(warped)
+ *warped = 1;
+ }
+
+outtahere:
+ if(he)
+ free_headents(&he);
+
+ if(msgso)
+ so_give(&msgso);
+
+ if(nick)
+ fs_give((void **)&nick);
+ if(full)
+ fs_give((void **)&full);
+ if(fcc)
+ fs_give((void **)&fcc);
+ if(comment)
+ fs_give((void **)&comment);
+
+ if(comma_sep_addr)
+ fs_give((void **)&comma_sep_addr);
+ if(nick_saved_for_pico_check)
+ fs_give((void **)&nick_saved_for_pico_check);
+
+ free_list_array(&orig_addrarray);
+ free_list_array(&new_addrarray);
+#ifdef ENABLE_LDAP
+ if(expander_address)
+ fs_give((void **) &expander_address);
+#endif
+}
+
+
+/*ARGSUSED*/
+int
+verify_nick(char *given, char **expanded, char **error, BUILDER_ARG *fcc, int *mangled)
+{
+ char *tmp;
+
+ dprint((7, "- verify_nick - (%s)\n", given ? given : "nul"));
+
+ tmp = cpystr(given);
+ removing_leading_and_trailing_white_space(tmp);
+
+ if(nickname_check(tmp, error)){
+ fs_give((void **)&tmp);
+ if(mangled){
+ if(ps_global->mangled_screen)
+ *mangled |= BUILDER_SCREEN_MANGLED;
+ else if(ps_global->mangled_footer)
+ *mangled |= BUILDER_FOOTER_MANGLED;
+ }
+
+ return -2;
+ }
+
+ if(ps_global->remote_abook_validity > 0 &&
+ adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0) && mangled)
+ *mangled |= BUILDER_SCREEN_MANGLED;
+
+ ab_nesting_level++;
+ if(strucmp(tmp, nick_saved_for_pico_check) != 0
+ && adrbk_lookup_by_nick(abook_saved_for_pico_check,
+ tmp, (adrbk_cntr_t *)NULL)){
+ if(error){
+ char buf[MAX_NICKNAME + 80];
+
+ /* TRANSLATORS: The %s is the nickname of an entry that is already
+ in the address book */
+ snprintf(buf, sizeof(buf), _("\"%s\" already in address book."), tmp);
+ buf[sizeof(buf)-1] = '\0';
+ *error = cpystr(buf);
+ }
+
+ ab_nesting_level--;
+ fs_give((void **)&tmp);
+ if(mangled){
+ if(ps_global->mangled_screen)
+ *mangled |= BUILDER_SCREEN_MANGLED;
+ else if(ps_global->mangled_footer)
+ *mangled |= BUILDER_FOOTER_MANGLED;
+ }
+
+ return -2;
+ }
+
+ ab_nesting_level--;
+ if(expanded)
+ *expanded = tmp;
+ else
+ fs_give((void **)&tmp);
+
+ /* This is so pico will erase any old message */
+ if(error)
+ *error = cpystr("");
+
+ if(mangled){
+ if(ps_global->mangled_screen)
+ *mangled |= BUILDER_SCREEN_MANGLED;
+ else if(ps_global->mangled_footer)
+ *mangled |= BUILDER_FOOTER_MANGLED;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Args: to -- the passed in line to parse
+ * full_to -- Address of a pointer to return the full address in.
+ * This will be allocated here and freed by the caller.
+ * However, this function is just going to copy "to".
+ * (special case for the route-addr-hack in build_address_int)
+ * We're just looking for the error messages.
+ * error -- Address of a pointer to return an error message in.
+ * This will be allocated here and freed by the caller.
+ * fcc -- This should be passed in NULL.
+ * This builder doesn't support affected_entry's.
+ *
+ * Result: 0 is returned if address was OK,
+ * -2 if address wasn't OK.
+ *
+ * Side effect: Can flush addrbook entry cache entries so they need to be
+ * re-fetched afterwords.
+ */
+int
+verify_addr(char *to, char **full_to, char **error, BUILDER_ARG *fcc, int *mangled)
+{
+ register char *p;
+ int ret_val;
+ BuildTo bldto;
+ jmp_buf save_jmp_buf;
+ int *save_nesting_level;
+
+ dprint((7, "- verify_addr - (%s)\n", to ? to : "nul"));
+
+ /* check to see if to string is empty to avoid work */
+ for(p = to; p && *p && isspace((unsigned char)(*p)); p++)
+ ;/* do nothing */
+
+ if(!p || !*p){
+ if(full_to)
+ *full_to = cpystr(to ? to : ""); /* because pico does a strcmp() */
+
+ return 0;
+ }
+
+ if(full_to != NULL)
+ *full_to = (char *)NULL;
+
+ if(error != NULL)
+ *error = (char *)NULL;
+
+ /*
+ * If we end up jumping back here because somebody else changed one of
+ * our addrbooks out from underneath us, we may well leak some memory.
+ * That's probably ok since this will be very rare.
+ */
+ memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
+ save_nesting_level = cpyint(ab_nesting_level);
+ if(setjmp(addrbook_changed_unexpectedly)){
+ if(full_to && *full_to)
+ fs_give((void **)full_to);
+
+ /* TRANSLATORS: This is sort of an error, something unexpected has
+ happened and alpine is re-initializing the address book. */
+ q_status_message(SM_ORDER, 3, 5, _("Resetting address book..."));
+ dprint((1,
+ "RESETTING address book... verify_addr(%s)!\n", to ? to : "?"));
+ addrbook_reset();
+ ab_nesting_level = *save_nesting_level;
+ }
+
+ bldto.type = Str;
+ bldto.arg.str = to;
+
+ if(ps_global->remote_abook_validity > 0 &&
+ adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0) && mangled)
+ *mangled |= BUILDER_SCREEN_MANGLED;
+
+ ab_nesting_level++;
+
+ ret_val = build_address_internal(bldto, full_to, error, NULL, NULL, NULL,
+ save_and_restore, 1, mangled);
+
+ ab_nesting_level--;
+ if(save_nesting_level)
+ fs_give((void **)&save_nesting_level);
+
+ if(full_to && *full_to && ret_val >= 0)
+ removing_leading_and_trailing_white_space(*full_to);
+
+ /* This is so pico will erase the old message */
+ if(error != NULL && *error == NULL)
+ *error = cpystr("");
+
+ if(ret_val < 0)
+ ret_val = -2; /* cause pico to stay on same header line */
+
+ memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
+ return(ret_val);
+}
+
+
+/*
+ * Call back for pico to prompt the user for exit confirmation
+ *
+ * Returns: either NULL if the user accepts exit, or string containing
+ * reason why the user declined.
+ */
+int
+pico_sendexit_for_adrbk(struct headerentry *he, void (*redraw_pico)(void),
+ int allow_flowed, char **result)
+{
+ char *rstr = NULL;
+ void (*redraw)(void) = ps_global->redrawer;
+
+ ps_global->redrawer = redraw_pico;
+ fix_windsize(ps_global);
+
+ /* TRANSLATORS: A question */
+ switch(want_to(_("Exit and save changes "), 'y', 0, NO_HELP, WT_NORM)){
+ case 'y':
+ break;
+
+ case 'n':
+ rstr = _("Use ^C to abandon changes you've made");
+ break;
+ }
+
+ if(result)
+ *result = rstr;
+
+ ps_global->redrawer = redraw;
+ return((rstr == NULL) ? 0 : 1);
+}
+
+
+/*
+ * Call back for pico to prompt the user for exit confirmation
+ *
+ * Returns: either NULL if the user accepts exit, or string containing
+ * reason why the user declined.
+ */
+char *
+pico_cancelexit_for_adrbk(char *word, void (*redraw_pico)(void))
+{
+ char prompt[90];
+ char *rstr = NULL;
+ void (*redraw)(void) = ps_global->redrawer;
+
+ /* TRANSLATORS: A question. The %s is a noun describing what is being cancelled. */
+ snprintf(prompt, sizeof(prompt), _("Cancel %s (answering \"Yes\" will abandon any changes made) "), word);
+ ps_global->redrawer = redraw_pico;
+ fix_windsize(ps_global);
+
+ switch(want_to(prompt, 'y', 'x', NO_HELP, WT_NORM)){
+ case 'y':
+ rstr = "";
+ break;
+
+ case 'n':
+ case 'x':
+ break;
+ }
+
+ ps_global->redrawer = redraw;
+ return(rstr);
+}
+
+
+char *
+pico_cancel_for_adrbk_take(void (*redraw_pico)(void))
+{
+ return(pico_cancelexit_for_adrbk(_("take"), redraw_pico));
+}
+
+
+char *
+pico_cancel_for_adrbk_edit(void (*redraw_pico)(void))
+{
+ return(pico_cancelexit_for_adrbk(_("changes"), redraw_pico));
+}
+
+
+/*
+prompt::name::help::prwid::maxlen::realaddr::
+builder::affected_entry::next_affected::selector::key_label::fileedit::
+display_it::break_on_comma::is_attach::rich_header::only_file_chars::
+single_space::sticky::dirty::start_here::blank::KS_ODATAVAR
+*/
+static struct headerentry headents_for_add[]={
+ {"Server Name : ", N_("Server"), h_composer_abook_add_server, 14, 0, NULL,
+ verify_server_name, NULL, NULL, NULL, NULL, NULL, NULL,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"Folder Name : ", N_("Folder"), h_composer_abook_add_folder, 14, 0, NULL,
+ verify_folder_name, NULL, NULL, NULL, NULL, NULL, NULL,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"NickName : ", N_("Nickname"), h_composer_abook_add_nick, 14, 0, NULL,
+ verify_abook_nick, NULL, NULL, NULL, NULL, NULL, NULL,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {NULL, NULL, NO_HELP, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}
+};
+#define NN_SERVER 0
+#define NN_FOLDER 1
+#define NN_NICK 2
+#define NN_END 3
+
+/*
+ * Args: global -- Add a global address book, not personal.
+ * add_after_this -- This is the addrbook number which should come
+ * right before the new addrbook we're adding, if
+ * that makes sense. If this is -1, append to end
+ * of the list.
+ *
+ * Returns: addrbook number of new addrbook, or
+ * -1, no addrbook added
+ */
+int
+ab_add_abook(int global, int add_after_this)
+{
+ int ret;
+
+ dprint((2, "- ab_add_abook -\n"));
+
+ ret = ab_modify_abook_list(0, global, add_after_this, NULL, NULL, NULL);
+
+ if(ret >= 0)
+ q_status_message(SM_ORDER, 0, 3,
+ _("New address book added. Use \"$\" to adjust order"));
+
+ return(ret);
+}
+
+
+/*
+ * Args: global -- Add a global address book, not personal.
+ * abook_num -- Abook num of the entry we are editing.
+ * serv -- Default server.
+ * folder -- Default folder.
+ * nick -- Default nickname.
+ *
+ * Returns: abook_num if successful,
+ * -1, if not
+ */
+int
+ab_edit_abook(int global, int abook_num, char *serv, char *folder, char *nick)
+{
+ dprint((2, "- ab_edit_abook -\n"));
+
+ return(ab_modify_abook_list(1, global, abook_num, serv, folder, nick));
+}
+
+static int the_one_were_editing;
+
+
+/*
+ * Args: edit -- Edit existing entry
+ * global -- Add a global address book, not personal.
+ * abook_num -- This is the addrbook number which should come
+ * right before the new addrbook we're adding, if
+ * that makes sense. If this is -1, append to end
+ * of the list.
+ * If we are editing instead of adding, this is
+ * the abook number of the entry we are editing.
+ * def_serv -- Default server.
+ * def_fold -- Default folder.
+ * def_nick -- Default nickname.
+ *
+ * Returns: addrbook number of new addrbook, or
+ * -1, no addrbook added
+ */
+int
+ab_modify_abook_list(int edit, int global, int abook_num, char *def_serv, char *def_fold, char *def_nick)
+{
+ struct headerentry *he;
+ PICO pbf;
+ STORE_S *msgso;
+ int editor_result, i, how_many_in_list, new_abook_num, num_in_list;
+ int ret = 0;
+ char *server, *folder, *nickname;
+ char *new_item = NULL;
+ EditWhich ew;
+ AccessType remember_access_result;
+ PerAddrBook *pab;
+ char titlebar[100];
+ char **list, **new_list = NULL;
+ char tmp[1000+MAXFOLDER];
+ int abook_indent;
+ char servpmt[100], foldpmt[100], nickpmt[100];
+ struct variable *vars = ps_global->vars;
+
+ dprint((2, "- ab_modify_abook_list -\n"));
+
+ if(ps_global->readonly_pinerc){
+ if(edit)
+ /* TRANSLATORS: Change was the name of the command the user was
+ trying to perform. It is what was cancelled. */
+ q_status_message(SM_ORDER, 0, 3, _("Change cancelled: config file not changeable"));
+ else
+ /* TRANSLATORS: Add was the command that is being cancelled. */
+ q_status_message(SM_ORDER, 0, 3, _("Add cancelled: config file not changeable"));
+
+ return -1;
+ }
+
+ ew = Main;
+
+ if((global && vars[V_GLOB_ADDRBOOK].is_fixed) ||
+ (!global && vars[V_ADDRESSBOOK].is_fixed)){
+ if(global)
+ /* TRANSLATORS: Operation was cancelled because the system management
+ does not allow the changing of global address books */
+ q_status_message(SM_ORDER, 0, 3, _("Cancelled: Sys. Mgmt. does not allow changing global address books"));
+ else
+ q_status_message(SM_ORDER, 0, 3, _("Cancelled: Sys. Mgmt. does not allow changing address books"));
+
+ return -1;
+ }
+
+ init_ab_if_needed();
+
+ if(edit){
+ if((!global &&
+ (abook_num < 0 || abook_num >= as.how_many_personals)) ||
+ (global &&
+ (abook_num < as.how_many_personals ||
+ abook_num >= as.n_addrbk))){
+ dprint((1, "Programming botch in ab_modify_abook_list: global=%d abook_num=%d n_addrbk=%d\n", global, abook_num, as.n_addrbk));
+ q_status_message(SM_ORDER, 0, 3, "Programming botch, bad abook_num");
+ return -1;
+ }
+
+ the_one_were_editing = abook_num;
+ }
+ else
+ the_one_were_editing = -1;
+
+ standard_picobuf_setup(&pbf);
+ pbf.exittest = pico_sendexit_for_adrbk;
+ pbf.canceltest = pico_cancel_for_adrbk_edit;
+ if(edit)
+ /* TRANSLATORS: screen title */
+ strncpy(titlebar, _("CHANGE ADDRESS BOOK"), sizeof(titlebar));
+ else
+ /* TRANSLATORS: screen title */
+ strncpy(titlebar, _("ADD ADDRESS BOOK"), sizeof(titlebar));
+
+ titlebar[sizeof(titlebar)-1] = '\0';
+ pbf.pine_anchor = set_titlebar(titlebar,
+ ps_global->mail_stream,
+ ps_global->context_current,
+ ps_global->cur_folder,ps_global->msgmap,
+ 0, FolderName, 0, 0, NULL);
+ pbf.pine_flags |= P_NOBODY;
+
+ /* An informational message */
+ if((msgso = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
+ int lines_avail;
+char *t1 =
+/* TRANSLATORS: The next few lines go together to explain how to add
+ the address book entry the user is working on. */
+_(" To add a local address book that will be accessed *only* by Alpine running\n on this machine, leave the server field blank.");
+char *t2 =
+_(" To add an address book that will be accessed by IMAP, fill in the\n server name.");
+char *t3 =
+_(" (NOTE: An address book cannot be accessed by IMAP from\n one Alpine and as a local file from another. It is either always accessed\n by IMAP or it is a local address book which is never accessed by IMAP.)");
+char *t4 =
+_(" In the Folder field, type the remote folder name or local file name.");
+char *t5 =
+_(" In the Nickname field, give the address book a nickname or leave it blank.");
+char *t6 =
+_(" To get help specific to an item, press ^G.");
+char *t7 =
+_(" To exit and save the configuration, press ^X. To cancel, press ^C.");
+
+ pbf.msgtext = (void *)so_text(msgso);
+ /*
+ * It's nice if we can make it so these lines make sense even if
+ * they don't all make it on the screen, because the user can't
+ * scroll down to see them.
+ *
+ * The 3 is the number of fields to be defined, the 1 is for a
+ * single blank line after the field definitions.
+ */
+ lines_avail = ps_global->ttyo->screen_rows - HEADER_ROWS(ps_global) -
+ FOOTER_ROWS(ps_global) - 3 - 1;
+
+ if(lines_avail >= 15){ /* extra blank line */
+ so_puts(msgso, "\n");
+ lines_avail--;
+ }
+
+ if(lines_avail >= 2){
+ so_puts(msgso, t1);
+ lines_avail -= 2;
+ }
+
+ if(lines_avail >= 5){
+ so_puts(msgso, "\n\n");
+ so_puts(msgso, t2);
+ so_puts(msgso, t3);
+ lines_avail -= 5;
+ }
+ else if(lines_avail >= 3){
+ so_puts(msgso, "\n\n");
+ so_puts(msgso, t2);
+ lines_avail -= 3;
+ }
+
+ if(lines_avail >= 2){
+ so_puts(msgso, "\n\n");
+ so_puts(msgso, t4);
+ lines_avail -= 2;
+ }
+
+ if(lines_avail >= 2){
+ so_puts(msgso, "\n\n");
+ so_puts(msgso, t5);
+ lines_avail -= 2;
+ }
+
+ if(lines_avail >= 3){
+ so_puts(msgso, "\n\n");
+ so_puts(msgso, t6);
+ so_puts(msgso, "\n");
+ so_puts(msgso, t7);
+ }
+ else if(lines_avail >= 2){
+ so_puts(msgso, "\n\n");
+ so_puts(msgso, t7);
+ }
+ }
+
+ he = (struct headerentry *)fs_get((NN_END+1) * sizeof(struct headerentry));
+ memset((void *)he, 0, (NN_END+1) * sizeof(struct headerentry));
+ pbf.headents = he;
+
+ abook_indent = utf8_width(_("Server Name")) + 2;
+
+ /* make a copy of each field */
+ server = cpystr(def_serv ? def_serv : "");
+ he[NN_SERVER] = headents_for_add[NN_SERVER];
+ he[NN_SERVER].realaddr = &server;
+ utf8_snprintf(servpmt, sizeof(servpmt), "%-*.*w: ", abook_indent, abook_indent, _("Server Name"));
+ he[NN_SERVER].prompt = servpmt;
+ he[NN_SERVER].prwid = abook_indent+2;
+
+ folder = cpystr(def_fold ? def_fold : "");
+ he[NN_FOLDER] = headents_for_add[NN_FOLDER];
+ he[NN_FOLDER].realaddr = &folder;
+ utf8_snprintf(foldpmt, sizeof(foldpmt), "%-*.*w: ", abook_indent, abook_indent, _("Folder Name"));
+ he[NN_FOLDER].prompt = foldpmt;
+ he[NN_FOLDER].prwid = abook_indent+2;
+
+ nickname = cpystr(def_nick ? def_nick : "");
+ he[NN_NICK] = headents_for_add[NN_NICK];
+ he[NN_NICK].realaddr = &nickname;
+ utf8_snprintf(nickpmt, sizeof(nickpmt), "%-*.*w: ", abook_indent, abook_indent, _("Nickname"));
+ he[NN_NICK].prompt = nickpmt;
+ he[NN_NICK].prwid = abook_indent+2;
+
+ he[NN_END] = headents_for_add[NN_END];
+
+ /* pass to pico and let user change them */
+ editor_result = pico(&pbf);
+ standard_picobuf_teardown(&pbf);
+
+ if(editor_result & COMP_GOTHUP){
+ ret = -1;
+ hup_signal();
+ }
+ else{
+ fix_windsize(ps_global);
+ init_signals();
+ }
+
+ if(editor_result & COMP_CANCEL){
+ ret = -1;
+ if(edit)
+ q_status_message(SM_ORDER, 0, 3, _("Address book change is cancelled"));
+ else
+ q_status_message(SM_ORDER, 0, 3, _("Address book add is cancelled"));
+ }
+ else if(editor_result & COMP_EXIT){
+ if(edit &&
+ !strcmp(server, def_serv ? def_serv : "") &&
+ !strcmp(folder, def_fold ? def_fold : "") &&
+ !strcmp(nickname, def_nick ? def_nick : "")){
+ ret = -1;
+ if(edit)
+ q_status_message(SM_ORDER, 0, 3, _("No change: Address book change is cancelled"));
+ else
+ q_status_message(SM_ORDER, 0, 3, _("No change: Address book add is cancelled"));
+ }
+ else{
+ if(global){
+ list = VAR_GLOB_ADDRBOOK;
+ how_many_in_list = as.n_addrbk - as.how_many_personals;
+ if(edit)
+ new_abook_num = abook_num;
+ else if(abook_num < 0)
+ new_abook_num = as.n_addrbk;
+ else
+ new_abook_num = MAX(MIN(abook_num + 1, as.n_addrbk),
+ as.how_many_personals);
+
+ num_in_list = new_abook_num - as.how_many_personals;
+ }
+ else{
+ list = VAR_ADDRESSBOOK;
+ how_many_in_list = as.how_many_personals;
+ new_abook_num = abook_num;
+ if(edit)
+ new_abook_num = abook_num;
+ else if(abook_num < 0)
+ new_abook_num = as.how_many_personals;
+ else
+ new_abook_num = MIN(abook_num + 1, as.how_many_personals);
+
+ num_in_list = new_abook_num;
+ }
+
+ if(!edit)
+ how_many_in_list++; /* for new abook */
+
+ removing_leading_and_trailing_white_space(server);
+ removing_leading_and_trailing_white_space(folder);
+ removing_leading_and_trailing_white_space(nickname);
+
+ /* convert nickname to UTF-8 */
+ if(nickname){
+ char *conv;
+
+ conv = convert_to_utf8(nickname, NULL, 0);
+ if(conv){
+ fs_give((void **) &nickname);
+ nickname = conv;
+ }
+ }
+
+ /* eliminate surrounding brackets */
+ if(server[0] == '{' && server[strlen(server)-1] == '}'){
+ char *p;
+
+ server[strlen(server)-1] = '\0';
+ for(p = server; *p; p++)
+ *p = *(p+1);
+ }
+
+ snprintf(tmp, sizeof(tmp), "%s%s%s%.*s",
+ *server ? "{" : "",
+ *server ? server : "",
+ *server ? "}" : "",
+ MAXFOLDER, folder);
+ tmp[sizeof(tmp)-1] = '\0';
+
+ new_item = put_pair(nickname, tmp);
+
+ if(!new_item || *new_item == '\0'){
+ if(edit)
+ q_status_message(SM_ORDER, 0, 3, _("Address book change is cancelled"));
+ else
+ q_status_message(SM_ORDER, 0, 3, _("Address book add is cancelled"));
+
+ ret = -1;
+ goto get_out;
+ }
+
+ /* allocate for new list */
+ new_list = (char **)fs_get((how_many_in_list + 1) * sizeof(char *));
+
+ /* copy old list up to where we will insert new entry */
+ for(i = 0; i < num_in_list; i++)
+ new_list[i] = cpystr(list[i]);
+
+ /* insert the new entry */
+ new_list[i++] = cpystr(new_item);
+
+ /* copy rest of old list, skip current if editing */
+ for(; i < how_many_in_list; i++)
+ new_list[i] = cpystr(list[edit ? i : (i-1)]);
+
+ new_list[i] = NULL;
+
+ /* this frees old variable contents for us */
+ if(set_variable_list(global ? V_GLOB_ADDRBOOK : V_ADDRESSBOOK,
+ new_list, TRUE, ew)){
+ if(edit)
+ q_status_message(SM_ORDER, 0, 3, _("Change cancelled: couldn't save configuration file"));
+ else
+ q_status_message(SM_ORDER, 0, 3, _("Add cancelled: couldn't save configuration file"));
+
+ set_current_val(&vars[global ? V_GLOB_ADDRBOOK : V_ADDRESSBOOK],
+ TRUE, FALSE);
+ ret = -1;
+ goto get_out;
+ }
+
+ ret = new_abook_num;
+ set_current_val(&vars[global ? V_GLOB_ADDRBOOK : V_ADDRESSBOOK],
+ TRUE, FALSE);
+
+ addrbook_reset();
+ init_ab_if_needed();
+
+ /*
+ * Test to see if this definition is going to work.
+ * Error messages are a good side effect.
+ */
+ pab = &as.adrbks[num_in_list];
+ init_abook(pab, NoDisplay);
+ remember_access_result = pab->access;
+ addrbook_reset();
+ init_ab_if_needed();
+ /* if we had trouble, give a clue to user (other than error msg) */
+ if(remember_access_result == NoAccess){
+ pab = &as.adrbks[num_in_list];
+ pab->access = remember_access_result;
+ }
+ }
+ }
+
+get_out:
+
+ if(he)
+ free_headents(&he);
+
+ if(new_list)
+ free_list_array(&new_list);
+
+ if(new_item)
+ fs_give((void **)&new_item);
+
+ if(msgso)
+ so_give(&msgso);
+
+ if(server)
+ fs_give((void **)&server);
+ if(folder)
+ fs_give((void **)&folder);
+ if(nickname)
+ fs_give((void **)&nickname);
+
+ return(ret);
+}
+
+
+int
+any_addrbooks_to_convert(struct pine *ps)
+{
+ PerAddrBook *pab;
+ int i, count = 0;
+
+ init_ab_if_needed();
+
+ for(i = 0; i < as.n_addrbk; i++){
+ pab = &as.adrbks[i];
+ if(pab && !(pab->type & REMOTE_VIA_IMAP) && !(pab->type & GLOBAL))
+ count++;
+ }
+
+ return(count);
+}
+
+
+int
+convert_addrbooks_to_remote(struct pine *ps, char *rem_folder_prefix, size_t len)
+{
+ PerAddrBook *pab;
+ int i, count = 0, ret = 0;
+
+ init_ab_if_needed();
+
+ for(i = 0; i < as.n_addrbk; i++){
+ pab = &as.adrbks[i];
+ if(pab && !(pab->type & REMOTE_VIA_IMAP) && !(pab->type & GLOBAL))
+ count++;
+ }
+
+ for(i = 0; ret != -1 && i < as.n_addrbk; i++){
+ pab = &as.adrbks[i];
+ if(pab && !(pab->type & REMOTE_VIA_IMAP) && !(pab->type & GLOBAL))
+ ret = convert_abook_to_remote(ps, pab, rem_folder_prefix, len, count);
+ }
+
+ return(ret);
+}
+
+
+/*
+ * Returns -1 if cancelled, -2 on error, 0 otherwise.
+ */
+int
+convert_abook_to_remote(struct pine *ps, PerAddrBook *pab, char *rem_folder_prefix, size_t len, int count)
+{
+#define DEF_ABOOK_NAME "remote_addrbook"
+ char local_file[MAILTMPLEN];
+ char rem_abook[MAILTMPLEN+3], prompt[MAILTMPLEN], old_nick[MAILTMPLEN];
+ char *p = NULL, *err_msg = NULL, *q;
+ char *serv = NULL, *nick = NULL, *file = NULL, *folder = NULL;
+ int ans, rc, offset, i, abook_num = -1, flags = OE_APPEND_CURRENT;
+ HelpType help;
+
+ snprintf(old_nick, sizeof(old_nick), "%s%s%s",
+ count > 1 ? " \"" : "",
+ count > 1 ? pab->abnick : "",
+ count > 1 ? "\"" : "");
+ old_nick[sizeof(old_nick)-1] = '\0';
+
+ snprintf(prompt, sizeof(prompt), _("Convert addressbook%s to a remote addrbook "), old_nick);
+ prompt[sizeof(prompt)-1] = '\0';
+ if((ans=want_to(prompt, 'y', 'x', h_convert_abook, WT_NORM)) != 'y')
+ return(ans == 'n' ? 0 : -1);
+
+ /* make sure the addrbook has been opened before, so that the file exists */
+ if(pab->ostatus == Closed || pab->ostatus == HalfOpen){
+ (void)init_addrbooks(NoDisplay, 0, 0, 0);
+ (void)init_addrbooks(Closed, 0, 0, 0);
+ }
+
+ if(pab->filename){
+ strncpy(local_file, pab->filename, sizeof(local_file)-1);
+ local_file[sizeof(local_file)-1] = '\0';
+#if defined(DOS)
+ p = strrindex(pab->filename, '\\');
+#else
+ p = strrindex(pab->filename, '/');
+#endif
+ }
+
+ strncpy(rem_abook, rem_folder_prefix, sizeof(rem_abook)-3);
+ if(!*rem_abook){
+ /* TRANSLATORS: The user is defining an address book which will be
+ stored on another server. This is called a remote addrbook and
+ this is a question asking for the name of the server. */
+ snprintf(prompt, sizeof(prompt), _("Name of server to contain remote addrbook : "));
+ prompt[sizeof(prompt)-1] = '\0';
+ help = NO_HELP;
+ while(1){
+ rc = optionally_enter(rem_abook, -FOOTER_ROWS(ps), 0,
+ sizeof(rem_abook), prompt, NULL,
+ help, &flags);
+ removing_leading_and_trailing_white_space(rem_abook);
+ if(rc == 3){
+ help = help == NO_HELP ? h_convert_pinerc_server : NO_HELP;
+ }
+ else if(rc == 1){
+ cmd_cancelled(NULL);
+ return(-1);
+ }
+ else if(rc == 0){
+ if(*rem_abook){
+ /* add brackets */
+ offset = strlen(rem_abook);
+ for(i = offset; i >= 0; i--)
+ rem_abook[i+1] = rem_abook[i];
+
+ rem_abook[0] = '{';
+ rem_abook[++offset] = '}';
+ rem_abook[++offset] = '\0';
+ break;
+ }
+ }
+ }
+ }
+
+ if(*rem_abook){
+ if(p && count > 1)
+ strncat(rem_abook, p+1,
+ sizeof(rem_abook)-1-strlen(rem_abook));
+ else
+ strncat(rem_abook, DEF_ABOOK_NAME,
+ sizeof(rem_abook)-1-strlen(rem_abook));
+ }
+
+ if(*rem_abook){
+ file = cpystr(rem_abook);
+ if(pab->abnick){
+ nick = (char *)fs_get((MAX(strlen(pab->abnick),strlen("Address Book"))+8) * sizeof(char));
+ snprintf(nick, sizeof(nick), "Remote %s",
+ (pab->abnick && !strcmp(pab->abnick, DF_ADDRESSBOOK))
+ ? "Address Book" : pab->abnick);
+ nick[sizeof(nick)-1] = '\0';
+ }
+ else
+ nick = cpystr("Remote Address Book");
+
+ if(file && *file == '{'){
+ q = file + 1;
+ if((p = strindex(file, '}'))){
+ *p = '\0';
+ serv = q;
+ folder = p+1;
+ }
+ else if(file)
+ fs_give((void **)&file);
+ }
+ else
+ folder = file;
+ }
+
+ q_status_message(SM_ORDER, 3, 5,
+ _("You now have a chance to change the name of the remote addrbook..."));
+ abook_num = ab_modify_abook_list(0, 0, -1, serv, folder, nick);
+
+ /* extract folder name of new abook so we can copy to it */
+ if(abook_num >= 0){
+ char **lval;
+ EditWhich ew = Main;
+
+ lval = LVAL(&ps->vars[V_ADDRESSBOOK], ew);
+ get_pair(lval[abook_num], &nick, &file, 0, 0);
+ if(nick)
+ fs_give((void **)&nick);
+
+ if(file){
+ strncpy(rem_abook, file, sizeof(rem_abook)-1);
+ rem_abook[sizeof(rem_abook)-1] = '\0';
+ fs_give((void **)&file);
+ }
+ }
+
+ /* copy the abook */
+ if(abook_num >= 0 && copy_abook(local_file, rem_abook, &err_msg)){
+ if(err_msg){
+ q_status_message(SM_ORDER | SM_DING, 7, 10, err_msg);
+ fs_give((void **)&err_msg);
+ }
+
+ return(-2);
+ }
+ else if(abook_num >= 0){ /* give user some info */
+ STORE_S *store;
+ SCROLL_S sargs;
+ char *beg, *end;
+
+ /*
+ * Save the hostname in rem_folder_prefix so we can use it again
+ * for other conversions if needed.
+ */
+ if((beg = rem_abook)
+ && (*beg == '{' || (*beg == '*' && *++beg == '{'))
+ && (end = strindex(rem_abook, '}'))){
+ rem_folder_prefix[0] = '{';
+ strncpy(rem_folder_prefix+1, beg+1, MIN(end-beg,len-2));
+ rem_folder_prefix[MIN(end-beg,len-2)] = '}';
+ rem_folder_prefix[MIN(end-beg+1,len-1)] = '\0';
+ }
+
+ if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
+ q_status_message(SM_ORDER | SM_DING, 7, 10,
+ _("Error allocating space for message."));
+ return(-2);
+ }
+
+ /* TRANSLATORS: Several lines in a row here that go together. */
+ snprintf(prompt, sizeof(prompt), _("\nYour addressbook%s has been copied to the"), old_nick);
+ prompt[sizeof(prompt)-1] = '\0';
+ so_puts(store, prompt);
+ so_puts(store, _("\nremote folder \""));
+ so_puts(store, rem_abook);
+ so_puts(store, "\".");
+ so_puts(store, _("\nA definition for this remote address book has been added to your list"));
+ so_puts(store, _("\nof address books. The definition for the address book it was copied"));
+ so_puts(store, _("\nfrom is also still there. You may want to remove that after you"));
+ so_puts(store, _("\nare confident that the new address book is complete and working."));
+ so_puts(store, _("\nUse the Setup/AddressBooks command to do that.\n"));
+
+ memset(&sargs, 0, sizeof(SCROLL_S));
+ sargs.text.text = so_text(store);
+ sargs.text.src = CharStar;
+ sargs.text.desc = _("Remote Address Book Information");
+ /* TRANSLATORS: a screen title */
+ sargs.bar.title = _("ABOUT REMOTE ABOOK");
+ sargs.help.text = NO_HELP;
+ sargs.help.title = NULL;
+
+ scrolltool(&sargs);
+
+ so_give(&store); /* free resources associated with store */
+ ps->mangled_screen = 1;
+ }
+
+ return(0);
+}
+
+
+int
+any_sigs_to_convert(struct pine *ps)
+{
+ char *sigfile, *litsig;
+ long rflags;
+ PAT_STATE pstate;
+ PAT_S *pat;
+ PAT_LINE_S *patline;
+
+ /* first check main signature file */
+ sigfile = ps->VAR_SIGNATURE_FILE;
+ litsig = ps->VAR_LITERAL_SIG;
+
+ if(sigfile && *sigfile && !litsig && sigfile[strlen(sigfile)-1] != '|' &&
+ !IS_REMOTE(sigfile))
+ return(1);
+
+ rflags = (ROLE_DO_ROLES | PAT_USE_MAIN);
+ if(any_patterns(rflags, &pstate)){
+ set_pathandle(rflags);
+ for(patline = *cur_pat_h ? (*cur_pat_h)->patlinehead : NULL;
+ patline; patline = patline->next){
+ for(pat = patline->first; pat; pat = pat->next){
+
+ /*
+ * See detoken() for when a sig file is used with a role.
+ */
+ sigfile = pat->action ? pat->action->sig : NULL;
+ litsig = pat->action ? pat->action->litsig : NULL;
+
+ if(sigfile && *sigfile && !litsig &&
+ sigfile[strlen(sigfile)-1] != '|' &&
+ !IS_REMOTE(sigfile))
+ return(1);
+ }
+ }
+ }
+
+ return(0);
+}
+
+
+int
+any_rule_files_to_warn_about(struct pine *ps)
+{
+ long rflags;
+ PAT_STATE pstate;
+ PAT_S *pat;
+
+ rflags = (ROLE_DO_ROLES | ROLE_DO_INCOLS | ROLE_DO_SCORES |
+ ROLE_DO_FILTER | ROLE_DO_OTHER | ROLE_DO_SRCH | PAT_USE_MAIN);
+ if(any_patterns(rflags, &pstate)){
+ for(pat = first_pattern(&pstate);
+ pat;
+ pat = next_pattern(&pstate)){
+ if(pat->patline && pat->patline->type == File)
+ break;
+ }
+
+ if(pat)
+ return(1);
+ }
+
+ return(0);
+}
+
+
+int
+convert_sigs_to_literal(struct pine *ps, int interactive)
+{
+ EditWhich ew = Main;
+ char *sigfile, *litsig, *cstring_version, *nick, *src = NULL;
+ char prompt[MAILTMPLEN];
+ STORE_S *store;
+ SCROLL_S sargs;
+ long rflags;
+ int ans;
+ PAT_STATE pstate;
+ PAT_S *pat;
+ PAT_LINE_S *patline;
+
+ /* first check main signature file */
+ sigfile = ps->VAR_SIGNATURE_FILE;
+ litsig = ps->VAR_LITERAL_SIG;
+
+ if(sigfile && *sigfile && !litsig && sigfile[strlen(sigfile)-1] != '|' &&
+ !IS_REMOTE(sigfile)){
+ if(interactive){
+ snprintf(prompt,sizeof(prompt),
+ /* TRANSLATORS: A literal sig is a way to store a signature
+ in alpine. It isn't a very descriptive name. Instead of
+ storing it in its own file it is stored in the configuration
+ file. */
+ _("Convert signature file \"%s\" to a literal sig "),
+ sigfile);
+ prompt[sizeof(prompt)-1] = '\0';
+ ClearBody();
+ ps->mangled_body = 1;
+ if((ans=want_to(prompt, 'y', 'x', h_convert_sig, WT_NORM)) == 'x'){
+ cmd_cancelled(NULL);
+ return(-1);
+ }
+ }
+ else
+ ans = 'y';
+
+ if(ans == 'y' && (src = get_signature_file(sigfile, 0, 0, 0)) != NULL){
+ cstring_version = string_to_cstring(src);
+ set_variable(V_LITERAL_SIG, cstring_version, 0, 0, ew);
+
+ if(cstring_version)
+ fs_give((void **)&cstring_version);
+
+ fs_give((void **)&src);
+
+ if(interactive){
+ if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
+ q_status_message(SM_ORDER | SM_DING, 7, 10,
+ _("Error allocating space for message."));
+ return(-1);
+ }
+
+ snprintf(prompt, sizeof(prompt),
+ /* TRANSLATORS: following lines go together */
+ _("\nYour signature file \"%s\" has been converted"), sigfile);
+ prompt[sizeof(prompt)-1] = '\0';
+ so_puts(store, prompt);
+ so_puts(store,
+ _("\nto a literal signature, which means it is contained in your"));
+ so_puts(store,
+ _("\nAlpine configuration instead of being in a file of its own."));
+ so_puts(store,
+ _("\nIf that configuration is copied to a remote folder then the"));
+ so_puts(store,
+ _("\nsignature will be available remotely also."));
+ so_puts(store,
+ _("\nChanges to the signature file itself will no longer have any"));
+ so_puts(store,
+ _("\neffect on Alpine but you may still edit the signature with the"));
+ so_puts(store,
+ _("\nSetup/Signature command.\n"));
+
+ memset(&sargs, 0, sizeof(SCROLL_S));
+ sargs.text.text = so_text(store);
+ sargs.text.src = CharStar;
+ sargs.text.desc = _("Literal Signature Information");
+ /* TRANSLATORS: screen title */
+ sargs.bar.title = _("ABOUT LITERAL SIG");
+ sargs.help.text = NO_HELP;
+ sargs.help.title = NULL;
+
+ scrolltool(&sargs);
+
+ so_give(&store);
+ ps->mangled_screen = 1;
+ }
+ }
+ }
+
+ rflags = (ROLE_DO_ROLES | PAT_USE_MAIN);
+ if(any_patterns(rflags, &pstate)){
+ set_pathandle(rflags);
+ for(patline = *cur_pat_h ? (*cur_pat_h)->patlinehead : NULL;
+ patline; patline = patline->next){
+ for(pat = patline->first; pat; pat = pat->next){
+
+ /*
+ * See detoken() for when a sig file is used with a role.
+ */
+ sigfile = pat->action ? pat->action->sig : NULL;
+ litsig = pat->action ? pat->action->litsig : NULL;
+ nick = (pat->action && pat->action->nick && pat->action->nick[0]) ? pat->action->nick : NULL;
+
+ if(sigfile && *sigfile && !litsig &&
+ sigfile[strlen(sigfile)-1] != '|' &&
+ !IS_REMOTE(sigfile)){
+ if(interactive){
+ snprintf(prompt,sizeof(prompt),
+ /* TRANSLATORS: asking whether a signature file should be converted to what
+ we call a literal signature, which is one contained in the regular
+ configuration file. Think of the set of 4 %s arguments as a
+ single argument which is the name of the signature file. */
+ _("Convert signature file \"%s\"%s%s%s to a literal sig "),
+ sigfile,
+ nick ? " in role \"" : "",
+ nick ? nick : "",
+ nick ? "\"" : "");
+ prompt[sizeof(prompt)-1] = '\0';
+ ClearBody();
+ ps->mangled_body = 1;
+ if((ans=want_to(prompt, 'y', 'x',
+ h_convert_sig, WT_NORM)) == 'x'){
+ cmd_cancelled(NULL);
+ return(-1);
+ }
+ }
+ else
+ ans = 'y';
+
+ if(ans == 'y' &&
+ (src = get_signature_file(sigfile,0,0,0)) != NULL){
+
+ cstring_version = string_to_cstring(src);
+
+ if(pat->action->litsig)
+ fs_give((void **)&pat->action->litsig);
+
+ pat->action->litsig = cstring_version;
+ fs_give((void **)&src);
+
+ set_pathandle(rflags);
+ if(patline->type == Literal)
+ (*cur_pat_h)->dirtypinerc = 1;
+ else
+ patline->dirty = 1;
+
+ if(write_patterns(rflags) == 0){
+ if(interactive){
+ /*
+ * Flush out current_vals of anything we've
+ * possibly changed.
+ */
+ close_patterns(ROLE_DO_ROLES | PAT_USE_CURRENT);
+
+ if(!(store=so_get(CharStar,NULL,EDIT_ACCESS))){
+ q_status_message(SM_ORDER | SM_DING, 7, 10,
+ _("Error allocating space for message."));
+ return(-1);
+ }
+
+ snprintf(prompt, sizeof(prompt),
+ /* TRANSLATORS: Keep the %s's together, they are sort of
+ the name of the file. */
+ _("Your signature file \"%s\"%s%s%s has been converted"),
+ sigfile,
+ nick ? " in role \"" : "",
+ nick ? nick : "",
+ nick ? "\"" : "");
+ prompt[sizeof(prompt)-1] = '\0';
+ so_puts(store, prompt);
+ so_puts(store,
+ /* TRANSLATORS: several lines that go together */
+ _("\nto a literal signature, which means it is contained in your"));
+ so_puts(store,
+ _("\nAlpine configuration instead of being in a file of its own."));
+ so_puts(store,
+ _("\nIf that configuration is copied to a remote folder then the"));
+ so_puts(store,
+ _("\nsignature will be available remotely also."));
+ so_puts(store,
+ _("\nChanges to the signature file itself will no longer have any"));
+ so_puts(store,
+ _("\neffect on Alpine. You may edit the signature with the"));
+ so_puts(store,
+ _("\nSetup/Rules/Roles command.\n"));
+
+ memset(&sargs, 0, sizeof(SCROLL_S));
+ sargs.text.text = so_text(store);
+ sargs.text.src = CharStar;
+ sargs.text.desc =
+ _("Literal Signature Information");
+ /* TRANSLATORS: a screen title */
+ sargs.bar.title = _("ABOUT LITERAL SIG");
+ sargs.help.text = NO_HELP;
+ sargs.help.title = NULL;
+
+ scrolltool(&sargs);
+
+ so_give(&store);
+ ps->mangled_screen = 1;
+ }
+ }
+ else if(interactive){
+ q_status_message(SM_ORDER | SM_DING, 7, 10,
+ /* TRANSLATORS: config is an abbreviation for configuration */
+ _("Error writing rules config."));
+ }
+ else{
+ /* TRANSLATORS: sig is signature */
+ fprintf(stderr, _("Error converting role sig\n"));
+ return(-1);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return(0);
+}
+
+
+void
+warn_about_rule_files(struct pine *ps)
+{
+ STORE_S *store;
+ SCROLL_S sargs;
+
+ if(any_rule_files_to_warn_about(ps)){
+ if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
+ q_status_message(SM_ORDER | SM_DING, 7, 10,
+ _("Error allocating space for message."));
+ return;
+ }
+
+ /* TRANSLATORS: several lines that go together */
+ so_puts(store, _("\nSome of your Rules are contained in Rule files instead of being directly"));
+ so_puts(store, _("\ncontained in your Alpine configuration file. To make those rules"));
+ so_puts(store, _("\navailable remotely you will need to move them out of the files."));
+ so_puts(store, _("\nThat can be done using the Shuffle command in the appropriate"));
+ so_puts(store, _("\nSetup/Rules subcommands.\n"));
+
+ memset(&sargs, 0, sizeof(SCROLL_S));
+ sargs.text.text = so_text(store);
+ sargs.text.src = CharStar;
+ sargs.text.desc = _("Rule Files Information");
+ /* TRANSLATORS: a screen title */
+ sargs.bar.title = _("ABOUT RULE FILES");
+ sargs.help.text = NO_HELP;
+ sargs.help.title = NULL;
+
+ scrolltool(&sargs);
+
+ so_give(&store);
+ ps->mangled_screen = 1;
+ }
+}
+
+
+void
+convert_to_remote_config(struct pine *ps, int edit_exceptions)
+{
+ char rem_pinerc_prefix[MAILTMPLEN];
+ char *beg, *end;
+ CONTEXT_S *context;
+ int abooks, sigs;
+
+ if(edit_exceptions){
+ /* TRANSLATORS: The exceptions command (X) was typed but it doesn't make sense */
+ q_status_message(SM_ORDER, 3, 5,
+ _("eXceptions does not make sense with this command"));
+ return;
+ }
+
+ if(!ps->prc)
+ panic("NULL prc in convert_to_remote_config");
+
+ dprint((2, "convert_to_remote_config\n"));
+
+ if(ps->prc->type == RemImap){ /* pinerc is already remote */
+ char prompt[MAILTMPLEN];
+
+ /*
+ * Check to see if there is anything at all to do. If there are
+ * address books to convert, sigfiles to convert, or rule files
+ * to comment on, we have something to do. Otherwise, just bail.
+ */
+ abooks = any_addrbooks_to_convert(ps);
+ sigs = any_sigs_to_convert(ps);
+
+ if(abooks || sigs){
+ if(abooks && sigs)
+ /* TRANSLATORS: AddressBooks is Address Books */
+ snprintf(prompt, sizeof(prompt), _("Config is already remote, convert AddressBooks and signature files "));
+ else if(abooks)
+ snprintf(prompt, sizeof(prompt), _("Config is already remote, convert AddressBooks "));
+ else
+ snprintf(prompt, sizeof(prompt), _("Config is already remote, convert signature files "));
+
+ prompt[sizeof(prompt)-1] = '\0';
+ if(want_to(prompt, 'y', 'x',
+ (abooks && sigs) ? h_convert_abooks_and_sigs :
+ abooks ? h_convert_abooks :
+ sigs ? h_convert_sigs : NO_HELP,
+ WT_NORM) != 'y'){
+ cmd_cancelled(NULL);
+ return;
+ }
+ }
+ }
+
+ /*
+ * Figure out a good default for where to put the remote config.
+ * If the default collection is remote we'll take the hostname and
+ * and modifiers from there. If not, we'll try to get the hostname from
+ * the inbox-path. In either case, we use the home directory on the
+ * server, not the directory where the folder collection is (if different).
+ * If we don't have a clue, we'll ask user.
+ */
+ if((context = default_save_context(ps->context_list)) != NULL &&
+ IS_REMOTE(context_apply(rem_pinerc_prefix, context, "",
+ sizeof(rem_pinerc_prefix)))){
+ /* just use the host from the default collection, not the whole path */
+ if((end = strrindex(rem_pinerc_prefix, '}')) != NULL)
+ *(end + 1) = '\0';
+ }
+ else{
+ /* use host from inbox path */
+ rem_pinerc_prefix[0] = '\0';
+ if((beg = ps->VAR_INBOX_PATH)
+ && (*beg == '{' || (*beg == '*' && *++beg == '{'))
+ && (end = strindex(ps->VAR_INBOX_PATH, '}'))){
+ rem_pinerc_prefix[0] = '{';
+ strncpy(rem_pinerc_prefix+1, beg+1,
+ MIN(end-beg, sizeof(rem_pinerc_prefix)-2));
+ rem_pinerc_prefix[MIN(end-beg, sizeof(rem_pinerc_prefix)-2)] = '}';
+ rem_pinerc_prefix[MIN(end-beg+1,sizeof(rem_pinerc_prefix)-1)]='\0';
+ }
+ }
+
+ /* ask about converting addrbooks to remote abooks */
+ if(ps->prc->type != RemImap || abooks)
+ if(convert_addrbooks_to_remote(ps, rem_pinerc_prefix,
+ sizeof(rem_pinerc_prefix)) == -1){
+ cmd_cancelled(NULL);
+ return;
+ }
+
+ /* ask about converting sigfiles to literal sigs */
+ if(ps->prc->type != RemImap || sigs)
+ if(convert_sigs_to_literal(ps, 1) == -1){
+ cmd_cancelled(NULL);
+ return;
+ }
+
+ warn_about_rule_files(ps);
+
+ /* finally, copy the config file */
+ if(ps->prc->type == Loc)
+ convert_pinerc_to_remote(ps, rem_pinerc_prefix);
+ else if(!(abooks || sigs))
+ q_status_message(SM_ORDER, 3, 5,
+ _("Cannot copy config file since it is already remote."));
+}
+
+
+void
+convert_pinerc_to_remote(struct pine *ps, char *rem_pinerc_prefix)
+{
+#define DEF_FOLDER_NAME "remote_pinerc"
+ char prompt[MAILTMPLEN], rem_pinerc[MAILTMPLEN];
+ char *err_msg = NULL;
+ int i, rc, offset;
+ HelpType help;
+ int flags = OE_APPEND_CURRENT;
+
+ ClearBody();
+ ps->mangled_body = 1;
+ strncpy(rem_pinerc, rem_pinerc_prefix, sizeof(rem_pinerc)-1);
+ rem_pinerc[sizeof(rem_pinerc)-1] = '\0';
+
+ if(*rem_pinerc == '\0'){
+ snprintf(prompt, sizeof(prompt), _("Name of server to contain remote Alpine config : "));
+ prompt[sizeof(prompt)-1] = '\0';
+ help = NO_HELP;
+ while(1){
+ rc = optionally_enter(rem_pinerc, -FOOTER_ROWS(ps), 0,
+ sizeof(rem_pinerc), prompt, NULL,
+ help, &flags);
+ removing_leading_and_trailing_white_space(rem_pinerc);
+ if(rc == 3){
+ help = help == NO_HELP ? h_convert_pinerc_server : NO_HELP;
+ }
+ else if(rc == 1){
+ cmd_cancelled(NULL);
+ return;
+ }
+ else if(rc == 0){
+ if(*rem_pinerc){
+ /* add brackets */
+ offset = strlen(rem_pinerc);
+ for(i = offset; i >= 0; i--)
+ if(i+1 < sizeof(rem_pinerc))
+ rem_pinerc[i+1] = rem_pinerc[i];
+
+ rem_pinerc[0] = '{';
+ if(offset+2 < sizeof(rem_pinerc)){
+ rem_pinerc[++offset] = '}';
+ rem_pinerc[++offset] = '\0';
+ }
+
+ break;
+ }
+ }
+ }
+ }
+
+ rem_pinerc[sizeof(rem_pinerc)-1] = '\0';
+
+ /*
+ * Add a default folder name.
+ */
+ if(*rem_pinerc){
+ /*
+ * Add /user= to modify hostname so that user won't be asked who they
+ * are each time they login.
+ */
+ if(!strstr(rem_pinerc, "/user=") && ps->VAR_USER_ID &&
+ ps->VAR_USER_ID[0]){
+ char *p;
+
+ p = rem_pinerc + strlen(rem_pinerc) - 1;
+ if(*p == '}') /* this should be the case */
+ snprintf(p, sizeof(rem_pinerc)-(p-rem_pinerc), "/user=\"%s\"}", ps->VAR_USER_ID);
+
+ rem_pinerc[sizeof(rem_pinerc)-1] = '\0';
+ }
+
+ strncat(rem_pinerc, DEF_FOLDER_NAME,
+ sizeof(rem_pinerc) - strlen(rem_pinerc) - 1);
+ rem_pinerc[sizeof(rem_pinerc)-1] = '\0';
+ }
+
+ /* ask user about folder name for remote config */
+ snprintf(prompt, sizeof(prompt), _("Folder to contain remote config : "));
+ prompt[sizeof(prompt)-1] = '\0';
+ help = NO_HELP;
+ while(1){
+ rc = optionally_enter(rem_pinerc, -FOOTER_ROWS(ps), 0,
+ sizeof(rem_pinerc), prompt, NULL, help, &flags);
+ removing_leading_and_trailing_white_space(rem_pinerc);
+ if(rc == 0 && *rem_pinerc){
+ break;
+ }
+
+ if(rc == 3){
+ help = (help == NO_HELP) ? h_convert_pinerc_folder : NO_HELP;
+ }
+ else if(rc == 1 || rem_pinerc[0] == '\0'){
+ cmd_cancelled(NULL);
+ return;
+ }
+ }
+
+#ifndef _WINDOWS
+ /*
+ * If we are on a Unix system, writing to a remote config, we want the
+ * remote config to work smoothly from a PC, too. If we don't have a
+ * user-id on the PC then we will be asked for our password.
+ * So add user-id to the pinerc before we copy it.
+ */
+ if(!ps->vars[V_USER_ID].main_user_val.p && ps->VAR_USER_ID)
+ ps->vars[V_USER_ID].main_user_val.p = cpystr(ps->VAR_USER_ID);
+
+ ps->vars[V_USER_ID].is_used = 1; /* so it will write to pinerc */
+ ps->prc->outstanding_pinerc_changes = 1;
+#endif
+
+ if(ps->prc->outstanding_pinerc_changes)
+ write_pinerc(ps, Main, WRP_NONE);
+
+#ifndef _WINDOWS
+ ps->vars[V_USER_ID].is_used = 0;
+#endif
+
+ /* copy the pinerc */
+ if(copy_pinerc(ps->prc->name, rem_pinerc, &err_msg)){
+ if(err_msg){
+ q_status_message(SM_ORDER | SM_DING, 7, 10, err_msg);
+ fs_give((void **)&err_msg);
+ }
+
+ return;
+ }
+
+ /* tell user about command line flags */
+ if(ps->prc->type != RemImap){
+ STORE_S *store;
+ SCROLL_S sargs;
+
+ if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
+ q_status_message(SM_ORDER | SM_DING, 7, 10,
+ _("Error allocating space for message."));
+ return;
+ }
+
+ /* TRANSLATORS: several lines that go together */
+ so_puts(store, _("\nYou may want to save a copy of this information!"));
+ so_puts(store, _("\n\nYour Alpine configuration data has been copied to"));
+ so_puts(store, "\n\n ");
+ so_puts(store, rem_pinerc);
+ so_puts(store, "\n");
+ so_puts(store, _("\nTo use that remote configuration from this computer you will"));
+ so_puts(store, _("\nhave to change the way you start Alpine by using the command line option"));
+ so_puts(store, _("\n\"alpine -p <remote_folder>\". The command should probably be"));
+#ifdef _WINDOWS
+ so_puts(store, "\n\n ");
+ so_puts(store, "alpine -p ");
+ so_puts(store, rem_pinerc);
+ so_puts(store, "\n");
+ so_puts(store, _("\nWith PC-Alpine, you may want to create a shortcut which"));
+ so_puts(store, _("\nhas the required arguments."));
+#else
+ so_puts(store, "\n\n ");
+ so_puts(store, "alpine -p \"");
+ so_puts(store, rem_pinerc);
+ so_puts(store, "\"\n");
+ so_puts(store, _("\nThe quotes are there around the last argument to protect the special"));
+ so_puts(store, _("\ncharacters in the folder name (like braces) from the command shell"));
+ so_puts(store, _("\nyou use. If you are not running Alpine from a command shell which knows"));
+ so_puts(store, _("\nabout quoting, it is possible you will have to remove those quotes"));
+ so_puts(store, _("\nfrom the command. For example, if you also use PC-Alpine you will probably"));
+ so_puts(store, _("\nwant to create a shortcut, and you would not need the quotes there."));
+ so_puts(store, _("\nWithout the quotes, the command might look like"));
+ so_puts(store, "\n\n ");
+ so_puts(store, "alpine -p ");
+ so_puts(store, rem_pinerc);
+ so_puts(store, "\n");
+ so_puts(store, _("\nConsider creating an alias or shell script to execute this command to make"));
+ so_puts(store, _("\nit more convenient."));
+#endif
+ so_puts(store, _("\n\nIf you want to use your new remote configuration for this session, quit"));
+ so_puts(store, _("\nAlpine now and restart with the changed command line options mentioned above.\n"));
+
+ memset(&sargs, 0, sizeof(SCROLL_S));
+ sargs.text.text = so_text(store);
+ sargs.text.src = CharStar;
+ sargs.text.desc = _("Remote Config Information");
+ /* TRANSLATORS: a screen title */
+ sargs.bar.title = _("ABOUT REMOTE CONFIG");
+ sargs.help.text = NO_HELP;
+ sargs.help.title = NULL;
+
+ scrolltool(&sargs);
+
+ so_give(&store); /* free resources associated with store */
+ ps->mangled_screen = 1;
+ }
+}
+
+
+int
+verify_folder_name(char *given, char **expanded, char **error, BUILDER_ARG *fcc, int *mangled)
+{
+ char *tmp;
+
+ tmp = cpystr(given ? given : "");
+ removing_leading_and_trailing_white_space(tmp);
+
+ if(expanded)
+ *expanded = tmp;
+ else
+ fs_give((void **)&tmp);
+
+ if(error)
+ *error = cpystr("");
+
+ return 0;
+}
+
+
+int
+verify_server_name(char *given, char **expanded, char **error, BUILDER_ARG *fcc, int *mangled)
+{
+ char *tmp;
+
+ tmp = cpystr(given ? given : "");
+ removing_leading_and_trailing_white_space(tmp);
+
+ if(*tmp){
+ /*
+ * could try to verify the hostname here
+ */
+ }
+
+ if(expanded)
+ *expanded = tmp;
+ else
+ fs_give((void **)&tmp);
+
+ if(error)
+ *error = cpystr("");
+
+ return 0;
+}
+
+
+int
+verify_abook_nick(char *given, char **expanded, char **error, BUILDER_ARG *fcc, int *mangled)
+{
+ int i;
+ char *tmp;
+
+ tmp = cpystr(given ? given : "");
+ removing_leading_and_trailing_white_space(tmp);
+
+ if(strindex(tmp, '"')){
+ fs_give((void **)&tmp);
+ if(error)
+ /* TRANSLATORS: Double quote refers to the " character */
+ *error = cpystr(_("Double quote not allowed in nickname"));
+
+ return -2;
+ }
+
+ for(i = 0; i < as.n_addrbk; i++)
+ if(i != the_one_were_editing && !strcmp(tmp, as.adrbks[i].abnick))
+ break;
+
+ if(i < as.n_addrbk){
+ fs_give((void **)&tmp);
+
+ if(error)
+ *error = cpystr(_("Nickname is already being used"));
+
+ return -2;
+ }
+
+ if(expanded)
+ *expanded = tmp;
+ else
+ fs_give((void **)&tmp);
+
+ if(error)
+ *error = cpystr("");
+
+ return 0;
+}
+
+
+/*
+ * Delete an addressbook.
+ *
+ * Args: cur_line -- The current line position (in global display list)
+ * of cursor
+ * command_line -- The screen line on which to prompt
+ * err -- Points to error message
+ *
+ * Returns -- 0, deleted addrbook
+ * -1, addrbook not deleted
+ */
+int
+ab_del_abook(long int cur_line, int command_line, char **err)
+{
+ int abook_num, varnum, delete_data = 0,
+ num_in_list, how_many_in_list, i, cnt, warn_about_revert = 0;
+ char **list, **new_list, **t, **lval;
+ char tmp[200];
+ PerAddrBook *pab;
+ struct variable *vars = ps_global->vars;
+ EditWhich ew;
+ enum {NotSet,
+ Modify,
+ RevertToDefault,
+ OverRideDefault,
+ DontChange} modify_config;
+
+ /* restrict address book config to normal config file */
+ ew = Main;
+
+ if(ps_global->readonly_pinerc){
+ if(err)
+ *err = _("Delete cancelled: config file not changeable");
+
+ return -1;
+ }
+
+ abook_num = adrbk_num_from_lineno(cur_line);
+
+ pab = &as.adrbks[abook_num];
+
+ dprint((2, "- ab_del_abook(%s) -\n",
+ pab->abnick ? pab->abnick : "?"));
+
+ varnum = (pab->type & GLOBAL) ? V_GLOB_ADDRBOOK : V_ADDRESSBOOK;
+
+ if(vars[varnum].is_fixed){
+ if(err){
+ if(pab->type & GLOBAL)
+ *err =
+ _("Cancelled: Sys. Mgmt. does not allow changing global address book config");
+ else
+ *err =
+ _("Cancelled: Sys. Mgmt. does not allow changing address book config");
+ }
+
+ return -1;
+ }
+
+ /*
+ * Deal with reverting to default values of the address book
+ * variables, or with user deleting a default value.
+ */
+ modify_config = NotSet;
+
+ /* First count how many address books are in the user's config. */
+ cnt = 0;
+ lval = LVAL(&vars[varnum], ew);
+ if(lval && lval[0])
+ for(t = lval; *t != NULL; t++)
+ cnt++;
+
+ /*
+ * Easy case, we can just delete one from the user's list.
+ */
+ if(cnt > 1){
+ modify_config = Modify;
+ }
+ /*
+ * Also easy. We'll revert to the default if it exists, and warn
+ * the user about that.
+ */
+ else if(cnt == 1){
+ modify_config = RevertToDefault;
+ /* see if there's a default to revert to */
+ cnt = 0;
+ if(vars[varnum].global_val.l && vars[varnum].global_val.l[0])
+ for(t = vars[varnum].global_val.l; *t != NULL; t++)
+ cnt++;
+
+ warn_about_revert = cnt;
+ }
+ /*
+ * User is already using the default. Split it into two cases. If there
+ * is one address book in default ask user if they want to delete that
+ * default from their config. If there is more than one, ask them if
+ * they want to ignore all the defaults or just delete this one.
+ */
+ else{
+ /* count how many in default */
+ cnt = 0;
+ if(vars[varnum].global_val.l && vars[varnum].global_val.l[0])
+ for(t = vars[varnum].global_val.l; *t != NULL; t++)
+ cnt++;
+
+ if(cnt > 1){
+ static ESCKEY_S opts[] = {
+ /* TRANSLATORS: Ignore All means ignore all of the default values,
+ and Remove One means just remove this one default value. */
+ {'i', 'i', "I", N_("Ignore All")},
+ {'r', 'r', "R", N_("Remove One")},
+ {-1, 0, NULL, NULL}};
+
+ snprintf(tmp, sizeof(tmp),
+ /* TRANSLATORS: %s is an adjective modifying address books */
+ _("Ignore all default %s address books or just remove this one ? "),
+ /* TRANSLATORS: global or personal address books */
+ pab->type & GLOBAL ? _("global") : _("personal"));
+ tmp[sizeof(tmp)-1] = '\0';
+ switch(radio_buttons(tmp, command_line, opts, 'i', 'x',
+ h_ab_del_ignore, RB_NORM)){
+ case 'i':
+ modify_config = OverRideDefault;
+ break;
+
+ case 'r':
+ modify_config = Modify;
+ break;
+
+ case 'x':
+ if(err)
+ *err = _("Delete cancelled");
+
+ return -1;
+ }
+ }
+ else{
+ /* TRANSLATORS: a question */
+ switch(want_to(_("Delete this default address book from config "),
+ 'n', 'x', h_ab_del_default, WT_NORM)){
+ case 'n':
+ case 'x':
+ if(err)
+ *err = _("Delete cancelled");
+
+ return -1;
+
+ case 'y':
+ modify_config = OverRideDefault;
+ break;
+ }
+ }
+ }
+
+ /*
+ * ReadWrite means it exists and MaybeRorW means it is remote and we
+ * haven't selected it yet to know our access permissions. The remote
+ * folder should have been created, though, unless we didn't even have
+ * permissions for that, in which case we got some error messages earlier.
+ */
+ if(pab->access == ReadWrite || pab->access == MaybeRorW){
+ static ESCKEY_S o[] = {
+ /* TRANSLATORS: user is asked whether to remove just data files, just configuration,
+ or both for an address book. */
+ {'d', 'd', "D", N_("Data")},
+ {'c', 'c', "C", N_("Config")},
+ {'b', 'b', "B", N_("Both")},
+ {-1, 0, NULL, NULL}};
+
+ switch(radio_buttons(_("Delete data, config, or both ? "),
+ command_line, o, 'c', 'x',
+ (modify_config == RevertToDefault)
+ ? h_ab_del_data_revert
+ : h_ab_del_data_modify,
+ RB_NORM)){
+ case 'b': /* Delete Both */
+ delete_data = 1;
+ break;
+
+ case 'd': /* Delete only Data */
+ modify_config = DontChange;
+ delete_data = 1;
+ break;
+
+ case 'c': /* Delete only Config */
+ break;
+
+ case 'x': /* Cancel */
+ default:
+ if(err)
+ *err = _("Delete cancelled");
+
+ return -1;
+ }
+ }
+ else{
+ /*
+ * Deleting config for address book which doesn't yet exist (hasn't
+ * ever been opened).
+ */
+ /* TRANSLATORS: a question */
+ switch(want_to(_("Delete configuration for highlighted addressbook "),
+ 'n', 'x',
+ (modify_config == RevertToDefault)
+ ? h_ab_del_config_revert
+ : h_ab_del_config_modify,
+ WT_NORM)){
+ case 'n':
+ case 'x':
+ default:
+ if(err)
+ *err = _("Delete cancelled");
+
+ return -1;
+
+ case 'y':
+ break;
+ }
+ }
+
+ if(delete_data){
+ char warning[800];
+
+ dprint((5, "deleting addrbook data\n"));
+ warning[0] = '\0';
+
+ /*
+ * In order to delete the address book it is easiest if we open
+ * it first. That fills in the filenames we want to delete.
+ */
+ if(pab->address_book == NULL){
+ warning[300] = '\0';
+ pab->address_book = adrbk_open(pab, ps_global->home_dir,
+ &warning[300], sizeof(warning)-300,
+ AB_SORT_RULE_NONE);
+ /*
+ * Couldn't get it open.
+ */
+ if(pab->address_book == NULL){
+ if(warning[300])
+ /* TRANSLATORS: %s is an error message */
+ snprintf(warning, 300, _("Can't delete data: %s"), &warning[300]);
+ else
+ strncpy(warning, _("Can't delete address book data"), 100);
+ }
+ }
+
+ /*
+ * If we have it open, set the delete bits and close to get the
+ * local copies. Delete the remote folder by hand.
+ */
+ if(pab->address_book){
+ char *file, *origfile = NULL;
+ int f=0, o=0;
+
+ /*
+ * We're about to destroy addrbook data, better ask again.
+ */
+ if(pab->address_book->count > 0){
+ char prompt[100];
+
+ /* TRANSLATORS: a question */
+ snprintf(prompt, sizeof(prompt),
+ _("About to delete the contents of address book (%ld entries), really delete "), (long) adrbk_count(pab->address_book));
+ prompt[sizeof(prompt)-1] = '\0';
+
+ switch(want_to(prompt, 'n', 'n', h_ab_really_delete, WT_NORM)){
+ case 'y':
+ break;
+
+ case 'n':
+ default:
+ if(err)
+ *err = _("Delete cancelled");
+
+ return -1;
+ }
+ }
+
+ pab->address_book->flags |= DEL_FILE;
+ file = cpystr(pab->address_book->filename);
+ if(pab->type & REMOTE_VIA_IMAP)
+ origfile = cpystr(pab->address_book->orig_filename);
+
+ /*
+ * In order to avoid locking problems when we delete the
+ * remote folder, we need to actually close the remote stream
+ * instead of just putting it back in the stream pool.
+ * So we will remove this stream from the re-usable portion
+ * of the stream pool by clearing the SP_USEPOOL flag.
+ * Init_abook(pab, TotallyClosed) via rd_close_remdata is
+ * going to pine_mail_close it.
+ */
+ if(pab->type && REMOTE_VIA_IMAP
+ && pab->address_book
+ && pab->address_book->type == Imap
+ && pab->address_book->rd
+ && rd_stream_exists(pab->address_book->rd)){
+
+ sp_unflag(pab->address_book->rd->t.i.stream, SP_USEPOOL);
+ }
+
+ /* This deletes the files because of DEL_ bits we set above. */
+ init_abook(pab, TotallyClosed);
+
+ /*
+ * Delete the remote folder.
+ */
+ if(pab->type & REMOTE_VIA_IMAP){
+ REMDATA_S *rd;
+ int exists;
+
+ ps_global->c_client_error[0] = '\0';
+ if(!pine_mail_delete(NULL, origfile) &&
+ ps_global->c_client_error[0] != '\0'){
+ dprint((1, "%s: %s\n", origfile ? origfile : "?",
+ ps_global->c_client_error));
+ }
+
+ /* delete line from metadata */
+ rd = rd_new_remdata(RemImap, origfile, NULL);
+ rd_write_metadata(rd, 1);
+ rd_close_remdata(&rd);
+
+ /* Check to see if it's still there */
+ if((exists=folder_exists(NULL, origfile)) &&
+ (exists != FEX_ERROR)){
+ o++;
+ dprint((1, "Trouble deleting %s\n",
+ origfile ? origfile : "?"));
+ }
+ }
+
+ if(can_access(file, ACCESS_EXISTS) == 0){
+ f++;
+ dprint((1, "Trouble deleting %s\n",
+ file ? file : "?"));
+ }
+
+ if(f || o){
+ snprintf(warning, sizeof(warning), _("Trouble deleting data %s%s%s%s"),
+ f ? file : "",
+ (f && o) ? (o ? ", " : " and ") : "",
+ o ? " and " : "",
+ o ? origfile : "");
+ warning[sizeof(warning)-1] = '\0';
+ }
+
+ fs_give((void **) &file);
+ if(origfile)
+ fs_give((void **) &origfile);
+ }
+
+ if(*warning){
+ q_status_message(SM_ORDER, 3, 3, warning);
+ dprint((1, "%s\n", warning));
+ display_message(NO_OP_COMMAND);
+ }
+ else if(modify_config == DontChange)
+ q_status_message(SM_ORDER, 0, 1, _("Addressbook data deleted"));
+ }
+
+ if(modify_config == DontChange){
+ /*
+ * We return -1 to indicate that the addrbook wasn't deleted (as far
+ * as we're concerned) but we don't fill in err so that no error
+ * message will be printed.
+ * Since the addrbook is still an addrbook we need to reinitialize it.
+ */
+ pab->access = adrbk_access(pab);
+ if(pab->type & GLOBAL && pab->access != NoAccess)
+ pab->access = ReadOnly;
+
+ init_abook(pab, HalfOpen);
+ return -1;
+ }
+ else if(modify_config == Modify){
+ list = vars[varnum].current_val.l;
+ if(pab->type & GLOBAL){
+ how_many_in_list = as.n_addrbk - as.how_many_personals - 1;
+ num_in_list = abook_num - as.how_many_personals;
+ }
+ else{
+ how_many_in_list = as.how_many_personals - 1;
+ num_in_list = abook_num;
+ }
+ }
+ else if(modify_config == OverRideDefault)
+ how_many_in_list = 1;
+ else if(modify_config == RevertToDefault)
+ how_many_in_list = 0;
+ else
+ q_status_message(SM_ORDER, 3, 3, "can't happen in ab_del_abook");
+
+ /* allocate for new list */
+ if(how_many_in_list)
+ new_list = (char **)fs_get((how_many_in_list + 1) * sizeof(char *));
+ else
+ new_list = NULL;
+
+ /*
+ * This case is both for modifying the users user_val and for the
+ * case where the user wants to modify the global_val default and
+ * use the modified version for his or her new user_val. We just
+ * copy from the existing global_val, deleting the one addrbook
+ * and put the result in user_val.
+ */
+ if(modify_config == Modify){
+ /* copy old list up to where we will delete entry */
+ for(i = 0; i < num_in_list; i++)
+ new_list[i] = cpystr(list[i]);
+
+ /* copy rest of old list */
+ for(; i < how_many_in_list; i++)
+ new_list[i] = cpystr(list[i+1]);
+
+ new_list[i] = NULL;
+ }
+ else if(modify_config == OverRideDefault){
+ new_list[0] = cpystr("");
+ new_list[1] = NULL;
+ }
+
+ /* this also frees old variable contents for us */
+ if(set_variable_list(varnum, new_list, TRUE, ew)){
+ if(err)
+ *err = _("Delete cancelled: couldn't save pine configuration file");
+
+ set_current_val(&vars[varnum], TRUE, FALSE);
+ free_list_array(&new_list);
+
+ return -1;
+ }
+
+ set_current_val(&vars[varnum], TRUE, FALSE);
+
+ if(warn_about_revert){
+ /* TRANSLATORS: the %s may be "global " or nothing */
+ snprintf(tmp, sizeof(tmp), _("Reverting to default %saddress books"),
+ pab->type & GLOBAL ? _("global ") : "");
+ tmp[sizeof(tmp)-1] = '\0';
+ q_status_message(SM_ORDER, 3, 4, tmp);
+ }
+
+ free_list_array(&new_list);
+
+ return 0;
+}
+
+
+/*
+ * Shuffle addrbooks.
+ *
+ * Args: pab -- Pab from current addrbook.
+ * slide -- return value, tells how far to slide the cursor. If slide
+ * is negative, slide it up, if positive, slide it down.
+ * command_line -- The screen line on which to prompt
+ * msg -- Points to returned message, if any. Should be freed by
+ * caller.
+ *
+ * Result: Two address books are swapped in the display order. If the shuffle
+ * crosses the Personal/Global boundary, then instead of swapping
+ * two address books the highlighted abook is moved from one section
+ * to the other.
+ * >= 0 on success.
+ * < 0 on failure, no changes.
+ * > 0 If the return value is greater than zero it means that we've
+ * reverted one of the variables to its default value. That
+ * means we've added at least one new addrbook, so the caller
+ * should reset. The value returned is the number of the
+ * moved addrbook + 1 (+1 so it won't be confused with zero).
+ * = 0 If the return value is zero we've just moved addrbooks around.
+ * No reset need be done.
+ */
+int
+ab_shuffle(PerAddrBook *pab, int *slide, int command_line, char **msg)
+{
+ ESCKEY_S opts[3];
+ char tmp[200];
+ int i, deefault, rv, target = 0;
+ int up_into_empty = 0, down_into_empty = 0;
+ HelpType help;
+ struct variable *vars = ps_global->vars;
+
+ dprint((2, "- ab_shuffle() -\n"));
+
+ *slide = 0;
+
+ if(ps_global->readonly_pinerc){
+ if(msg)
+ *msg = cpystr(_("Shuffle cancelled: config file not changeable"));
+
+ return -1;
+ }
+
+ /* Move it up or down? */
+ i = 0;
+ opts[i].ch = 'u';
+ opts[i].rval = 'u';
+ opts[i].name = "U";
+ /* TRANSLATORS: shuffle something Up or Down in a list */
+ opts[i++].label = N_("Up");
+
+ opts[i].ch = 'd';
+ opts[i].rval = 'd';
+ opts[i].name = "D";
+ opts[i++].label = N_("Down");
+
+ opts[i].ch = -1;
+ deefault = 'u';
+
+ if(pab->type & GLOBAL){
+ if(vars[V_GLOB_ADDRBOOK].is_fixed){
+ if(msg)
+ *msg = cpystr(_("Cancelled: Sys. Mgmt. does not allow changing global address book config"));
+
+ return -1;
+ }
+
+ if(as.cur == 0){
+ if(as.config)
+ up_into_empty++;
+ else{ /* no up */
+ opts[0].ch = -2;
+ deefault = 'd';
+ }
+ }
+
+ if(as.cur == as.n_addrbk - 1) /* no down */
+ opts[1].ch = -2;
+ }
+ else{
+ if(vars[V_ADDRESSBOOK].is_fixed){
+ if(msg)
+ *msg = cpystr(_("Cancelled: Sys. Mgmt. does not allow changing address book config"));
+
+ return -1;
+ }
+
+ if(as.cur == 0){ /* no up */
+ opts[0].ch = -2;
+ deefault = 'd';
+ }
+
+ if(as.cur == as.n_addrbk - 1){
+ if(as.config)
+ down_into_empty++;
+ else
+ opts[1].ch = -2; /* no down */
+ }
+ }
+
+ snprintf(tmp, sizeof(tmp), _("Shuffle \"%s\" %s%s%s ? "),
+ pab->abnick,
+ (opts[0].ch != -2) ? _("UP") : "",
+ (opts[0].ch != -2 && opts[1].ch != -2) ? " or " : "",
+ (opts[1].ch != -2) ? _("DOWN") : "");
+ tmp[sizeof(tmp)-1] = '\0';
+ help = (opts[0].ch == -2) ? h_ab_shuf_down
+ : (opts[1].ch == -2) ? h_ab_shuf_up
+ : h_ab_shuf;
+
+ rv = radio_buttons(tmp, command_line, opts, deefault, 'x',
+ help, RB_NORM);
+
+ ps_global->mangled_footer = 1;
+
+ if((rv == 'u' && up_into_empty) || (rv == 'd' && down_into_empty))
+ target = -1;
+ else
+ target = as.cur + (rv == 'u' ? -1 : 1);
+
+ if(rv == 'x'){
+ if(msg)
+ *msg = cpystr(_("Shuffle cancelled"));
+
+ return -1;
+ }
+ else
+ return(do_the_shuffle(slide, as.cur, target, msg));
+}
+
+
+/*
+ * Actually shuffle the config variables and address books structures around.
+ *
+ * Args: anum1, anum2 -- The numbers of the address books
+ * msg -- Points to returned message, if any.
+ *
+ * Returns: >= 0 on success.
+ * < 0 on failure, no changes.
+ * > 0 If the return value is greater than zero it means that we've
+ * reverted one of the variables to its default value. That
+ * means we've added at least one new addrbook, so the caller
+ * should reset. The value returned is the number of the
+ * moved addrbook + 1 (+1 so it won't be confused with zero).
+ * = 0 If the return value is zero we've just moved addrbooks around.
+ * No reset need be done.
+ *
+ * Anum1 is the one that we want to move, anum2 is the one that it will be
+ * swapped with. When anum1 and anum2 are on the opposite sides of the
+ * Personal/Global boundary then instead of swapping we just move anum1 to
+ * the other side of the boundary.
+ *
+ * Anum2 of -1 means it is a swap into the other type of address book, which
+ * is currently empty.
+ */
+int
+do_the_shuffle(int *slide, int anum1, int anum2, char **msg)
+{
+ PerAddrBook *pab;
+ enum {NotSet, Pers, Glob, Empty} type1, type2;
+ int i, j, retval = -1;
+ struct variable *vars = ps_global->vars;
+ char **lval;
+ EditWhich ew;
+ char *cancel_msg = _("Shuffle cancelled: couldn't save configuration file");
+
+ dprint((5, "- do_the_shuffle(%d, %d) -\n", anum1, anum2));
+
+ /* restrict address book config to normal config file */
+ ew = Main;
+
+ if(anum1 == -1)
+ type1 = Empty;
+ else{
+ pab = &as.adrbks[anum1];
+ type1 = (pab->type & GLOBAL) ? Glob : Pers;
+ }
+
+ if(type1 == Empty){
+ if(msg)
+ *msg =
+ cpystr(_("Shuffle cancelled: highlight entry you wish to shuffle"));
+
+ return(retval);
+ }
+
+ if(anum2 == -1)
+ type2 = Empty;
+ else{
+ pab = &as.adrbks[anum2];
+ type2 = (pab->type & GLOBAL) ? Glob : Pers;
+ }
+
+ if(type2 == Empty)
+ type2 = (type1 == Pers) ? Glob : Pers;
+
+ if((type1 == Pers || type2 == Pers) && vars[V_ADDRESSBOOK].is_fixed){
+ if(msg)
+ *msg = cpystr(_("Cancelled: Sys. Mgmt. does not allow changing address book configuration"));
+
+ return(retval);
+ }
+
+ if((type1 == Glob || type2 == Glob) && vars[V_GLOB_ADDRBOOK].is_fixed){
+ if(msg)
+ *msg = cpystr(_("Cancelled: Sys. Mgmt. does not allow changing global address book config"));
+
+ return(retval);
+ }
+
+ /*
+ * There are two cases. If the shuffle is two address books within the
+ * same variable, then they just swap places. If it is a shuffle of an
+ * addrbook from one side of the boundary to the other, just that one
+ * is moved.
+ */
+ if((type1 == Glob && type2 == Glob) ||
+ (type1 == Pers && type2 == Pers)){
+ int how_many_in_list, varnum;
+ int anum1_rel, anum2_rel; /* position in specific list */
+ char **list, **new_list;
+ PerAddrBook tmppab;
+
+ *slide = (anum1 < anum2) ? LINES_PER_ABOOK : -1 * LINES_PER_ABOOK;
+
+ if(type1 == Pers){
+ how_many_in_list = as.how_many_personals;
+ list = VAR_ADDRESSBOOK;
+ varnum = V_ADDRESSBOOK;
+ anum1_rel = anum1;
+ anum2_rel = anum2;
+ }
+ else{
+ how_many_in_list = as.n_addrbk - as.how_many_personals;
+ list = VAR_GLOB_ADDRBOOK;
+ varnum = V_GLOB_ADDRBOOK;
+ anum1_rel = anum1 - as.how_many_personals;
+ anum2_rel = anum2 - as.how_many_personals;
+ }
+
+ /* allocate for new list, same size as old list */
+ new_list = (char **)fs_get((how_many_in_list + 1) * sizeof(char *));
+
+ /* fill in new_list */
+ for(i = 0; i < how_many_in_list; i++){
+ /* swap anum1 and anum2 */
+ if(i == anum1_rel)
+ j = anum2_rel;
+ else if(i == anum2_rel)
+ j = anum1_rel;
+ else
+ j = i;
+
+ new_list[i] = cpystr(list[j]);
+ }
+
+ new_list[i] = NULL;
+
+ if(set_variable_list(varnum, new_list, TRUE, ew)){
+ if(msg)
+ *msg = cpystr(cancel_msg);
+
+ /* restore old values */
+ set_current_val(&vars[varnum], TRUE, FALSE);
+ free_list_array(&new_list);
+ return(retval);
+ }
+
+ retval = 0;
+ set_current_val(&vars[varnum], TRUE, FALSE);
+ free_list_array(&new_list);
+
+ /* Swap PerAddrBook structs */
+ tmppab = as.adrbks[anum1];
+ as.adrbks[anum1] = as.adrbks[anum2];
+ as.adrbks[anum2] = tmppab;
+ }
+ else if((type1 == Pers && type2 == Glob) ||
+ (type1 == Glob && type2 == Pers)){
+ int how_many_in_srclist, how_many_in_dstlist;
+ int srcvarnum, dstvarnum, srcanum;
+ int cnt, warn_about_revert = 0;
+ char **t;
+ char **new_src, **new_dst, **srclist, **dstlist;
+ char tmp[200];
+ enum {NotSet, Modify, RevertToDefault, OverRideDefault} modify_config;
+
+ /*
+ * how_many_in_srclist = # in orig src list (Pers or Glob list).
+ * how_many_in_dstlist = # in orig dst list
+ * srcanum = # of highlighted addrbook that is being shuffled
+ */
+ if(type1 == Pers){
+ how_many_in_srclist = as.how_many_personals;
+ how_many_in_dstlist = as.n_addrbk - as.how_many_personals;
+ srclist = VAR_ADDRESSBOOK;
+ dstlist = VAR_GLOB_ADDRBOOK;
+ srcvarnum = V_ADDRESSBOOK;
+ dstvarnum = V_GLOB_ADDRBOOK;
+ srcanum = as.how_many_personals - 1;
+ *slide = (how_many_in_srclist == 1)
+ ? (LINES_PER_ADD_LINE + XTRA_LINES_BETWEEN)
+ : XTRA_LINES_BETWEEN;
+ }
+ else{
+ how_many_in_srclist = as.n_addrbk - as.how_many_personals;
+ how_many_in_dstlist = as.how_many_personals;
+ srclist = VAR_GLOB_ADDRBOOK;
+ dstlist = VAR_ADDRESSBOOK;
+ srcvarnum = V_GLOB_ADDRBOOK;
+ dstvarnum = V_ADDRESSBOOK;
+ srcanum = as.how_many_personals;
+ *slide = (how_many_in_dstlist == 0)
+ ? (LINES_PER_ADD_LINE + XTRA_LINES_BETWEEN)
+ : XTRA_LINES_BETWEEN;
+ *slide = -1 * (*slide);
+ }
+
+
+ modify_config = Modify;
+ if(how_many_in_srclist == 1){
+ /*
+ * Deal with reverting to default values of the address book
+ * variables, or with user deleting a default value.
+ */
+ modify_config = NotSet;
+
+ /*
+ * Count how many address books are in the user's config.
+ * This has to be one or zero, because how_many_in_srclist == 1.
+ */
+ cnt = 0;
+ lval = LVAL(&vars[srcvarnum], ew);
+ if(lval && lval[0])
+ for(t = lval; *t != NULL; t++)
+ cnt++;
+
+ /*
+ * We'll revert to the default if it exists, and warn
+ * the user about that.
+ */
+ if(cnt == 1){
+ modify_config = RevertToDefault;
+ /* see if there's a default to revert to */
+ cnt = 0;
+ if(vars[srcvarnum].global_val.l &&
+ vars[srcvarnum].global_val.l[0])
+ for(t = vars[srcvarnum].global_val.l; *t != NULL; t++)
+ cnt++;
+
+ warn_about_revert = cnt;
+ if(warn_about_revert > 1 && type1 == Pers)
+ *slide = LINES_PER_ABOOK * warn_about_revert +
+ XTRA_LINES_BETWEEN;
+ }
+ /*
+ * User is already using the default.
+ */
+ else if(cnt == 0){
+ modify_config = OverRideDefault;
+ }
+ }
+
+ /*
+ * We're adding one to the dstlist, so need how_many + 1 + 1.
+ */
+ new_dst = (char **)fs_get((how_many_in_dstlist + 2) * sizeof(char *));
+ j = 0;
+
+ /*
+ * Because the Personal list comes before the Global list, when
+ * we move to Global we're inserting a new first element into
+ * the global list (the dstlist).
+ *
+ * When we move from Global to Personal, we're appending a new
+ * last element onto the personal list (the dstlist).
+ */
+ if(type2 == Glob)
+ new_dst[j++] = cpystr(srclist[how_many_in_srclist-1]);
+
+ for(i = 0; i < how_many_in_dstlist; i++)
+ new_dst[j++] = cpystr(dstlist[i]);
+
+ if(type2 == Pers)
+ new_dst[j++] = cpystr(srclist[0]);
+
+ new_dst[j] = NULL;
+
+ /*
+ * The srclist is complicated by the reverting to default
+ * behaviors.
+ */
+ if(modify_config == Modify){
+ /*
+ * In this case we're just removing one from the srclist
+ * so the new_src is of size how_many -1 +1.
+ */
+ new_src = (char **)fs_get((how_many_in_srclist) * sizeof(char *));
+ j = 0;
+
+ for(i = 0; i < how_many_in_srclist-1; i++)
+ new_src[j++] = cpystr(srclist[i + ((type1 == Glob) ? 1 : 0)]);
+
+ new_src[j] = NULL;
+ }
+ else if(modify_config == OverRideDefault){
+ /*
+ * We were using default and will now revert to nothing.
+ */
+ new_src = (char **)fs_get(2 * sizeof(char *));
+ new_src[0] = cpystr("");
+ new_src[1] = NULL;
+ }
+ else if(modify_config == RevertToDefault){
+ /*
+ * We are moving our last user variable out and reverting
+ * to the default value for this variable.
+ */
+ new_src = NULL;
+ }
+
+ if(set_variable_list(dstvarnum, new_dst, TRUE, ew) ||
+ set_variable_list(srcvarnum, new_src, TRUE, ew)){
+ if(msg)
+ *msg = cpystr(cancel_msg);
+
+ /* restore old values */
+ set_current_val(&vars[dstvarnum], TRUE, FALSE);
+ set_current_val(&vars[srcvarnum], TRUE, FALSE);
+ free_list_array(&new_dst);
+ free_list_array(&new_src);
+ return(retval);
+ }
+
+ set_current_val(&vars[dstvarnum], TRUE, FALSE);
+ set_current_val(&vars[srcvarnum], TRUE, FALSE);
+ free_list_array(&new_dst);
+ free_list_array(&new_src);
+
+ retval = (type1 == Pers && warn_about_revert)
+ ? (warn_about_revert + 1) : (srcanum + 1);
+
+ /*
+ * This is a tough case. We're adding one or more new address books
+ * in this case so we need to reset the addrbooks and start over.
+ * We return the number of the address book we just moved after the
+ * reset so that the caller can focus attention on the moved one.
+ * Actually, we return 1+the number so that we can tell it apart
+ * from a return of zero, which just means everything is ok.
+ */
+ if(warn_about_revert){
+ snprintf(tmp, sizeof(tmp),
+ "This address book now %s, reverting to default %s address %s",
+ (type1 == Glob) ? "Personal" : "Global",
+ (type1 == Glob) ? "Global" : "Personal",
+ warn_about_revert > 1 ? "books" : "book");
+ tmp[sizeof(tmp)-1] = '\0';
+ if(msg)
+ *msg = cpystr(tmp);
+ }
+ else{
+ /*
+ * Modify PerAddrBook struct and adjust boundary.
+ * In this case we aren't swapping two addrbooks, but just modifying
+ * one from being global to personal or the reverse. It will
+ * still be the same element in the as.adrbks array.
+ */
+ pab = &as.adrbks[srcanum];
+ if(type2 == Glob){
+ as.how_many_personals--;
+ pab->type |= GLOBAL;
+ if(pab->access != NoAccess)
+ pab->access = ReadOnly;
+ }
+ else{
+ as.how_many_personals++;
+ pab->type &= ~GLOBAL;
+ if(pab->access != NoAccess && pab->access != MaybeRorW)
+ pab->access = ReadWrite;
+ }
+
+ snprintf(tmp, sizeof(tmp),
+ "This address book now %s",
+ (type1 == Glob) ? "Personal" : "Global");
+ tmp[sizeof(tmp)-1] = '\0';
+ if(msg)
+ *msg = cpystr(tmp);
+ }
+ }
+
+ return(retval);
+}
+
+
+int
+ab_compose_to_addr(long int cur_line, int agg, int allow_role)
+{
+ AddrScrn_Disp *dl;
+ AdrBk_Entry *abe;
+ SAVE_STATE_S state;
+ BuildTo bldto;
+
+ dprint((2, "- ab_compose_to_addr -\n"));
+
+ save_state(&state);
+
+ bldto.type = Str;
+ bldto.arg.str = NULL;
+
+ if(agg){
+ int i;
+ size_t incr = 100, avail, alloced;
+ char *to = NULL;
+
+ to = (char *)fs_get(incr);
+ *to = '\0';
+ avail = incr;
+ alloced = incr;
+
+ /*
+ * Run through all of the selected entries
+ * in all of the address books.
+ * Put the nicknames together into one long
+ * string with comma separators.
+ */
+ for(i = 0; i < as.n_addrbk; i++){
+ adrbk_cntr_t num;
+ PerAddrBook *pab;
+ EXPANDED_S *next_one;
+
+ pab = &as.adrbks[i];
+ if(pab->address_book)
+ next_one = pab->address_book->selects;
+ else
+ continue;
+
+ while((num = entry_get_next(&next_one)) != NO_NEXT){
+ char *a_string;
+ AddrScrn_Disp fake_dl;
+
+ abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) num);
+
+ /*
+ * Since we're picking up address book entries
+ * directly from the address books and have
+ * no knowledge of the display lines they came
+ * from, we don't know the dl's that go with
+ * them. We need to pass a dl to abe_to_nick
+ * but it really is only going to use the
+ * type in this case.
+ */
+ dl = &fake_dl;
+ dl->type = (abe->tag == Single) ? Simple : ListHead;
+ a_string = abe_to_nick_or_addr_string(abe, dl, i);
+
+ while(abe && avail < (size_t)strlen(a_string)+1){
+ alloced += incr;
+ avail += incr;
+ fs_resize((void **)&to, alloced);
+ }
+
+ if(!*to){
+ strncpy(to, a_string, alloced);
+ to[alloced-1] = '\0';
+ }
+ else{
+ strncat(to, ",", alloced-strlen(to)-1);
+ to[alloced-1] = '\0';
+ strncat(to, a_string, alloced-strlen(to)-1);
+ to[alloced-1] = '\0';
+ }
+
+ avail -= (strlen(a_string) + 1);
+ fs_give((void **)&a_string);
+ }
+ }
+
+ bldto.type = Str;
+ bldto.arg.str = to;
+ }
+ else{
+ if(is_addr(cur_line)){
+
+ dl = dlist(cur_line);
+ abe = ae(cur_line);
+
+ if(dl->type == ListEnt){
+ bldto.type = Str;
+ bldto.arg.str = cpystr(listmem(cur_line));
+ }
+ else{
+ bldto.type = Abe;
+ bldto.arg.abe = abe;
+ }
+ }
+ }
+
+ if(bldto.type == Str && bldto.arg.str == NULL)
+ bldto.arg.str = cpystr("");
+
+ ab_compose_internal(bldto, allow_role);
+
+ restore_state(&state);
+
+ if(bldto.type == Str && bldto.arg.str)
+ fs_give((void **)&bldto.arg.str);
+
+ /*
+ * Window size may have changed in composer.
+ * Pine_send will have reset the window size correctly,
+ * but we still have to reset our address book data structures.
+ */
+ ab_resize();
+ ps_global->mangled_screen = 1;
+ return(1);
+}
+
+
+/*
+ * Used by the two compose routines.
+ */
+void
+ab_compose_internal(BuildTo bldto, int allow_role)
+{
+ int good_addr;
+ char *addr, *fcc, *error = NULL;
+ ACTION_S *role = NULL;
+ void (*prev_screen)(struct pine *) = ps_global->prev_screen,
+ (*redraw)(void) = ps_global->redrawer;
+
+ if(allow_role)
+ ps_global->redrawer = NULL;
+
+ ps_global->next_screen = SCREEN_FUN_NULL;
+
+ fcc = NULL;
+ addr = NULL;
+
+ good_addr = (our_build_address(bldto, &addr, &error, &fcc, NULL) >= 0);
+
+ if(error){
+ q_status_message1(SM_ORDER, 3, 4, "%s", error);
+ fs_give((void **)&error);
+ }
+
+ if(!good_addr && addr && *addr)
+ fs_give((void **)&addr); /* relying on fs_give setting addr to NULL */
+
+ if(allow_role){
+ /* Setup role */
+ if(role_select_screen(ps_global, &role, MC_COMPOSE) < 0){
+ cmd_cancelled("Composition");
+ ps_global->next_screen = prev_screen;
+ ps_global->redrawer = redraw;
+ return;
+ }
+
+ /*
+ * If default role was selected (NULL) we need to make up a role which
+ * won't do anything, but will cause compose_mail to think there's
+ * already a role so that it won't try to confirm the default.
+ */
+ if(role)
+ role = copy_action(role);
+ else{
+ role = (ACTION_S *)fs_get(sizeof(*role));
+ memset((void *)role, 0, sizeof(*role));
+ role->nick = cpystr("Default Role");
+ }
+ }
+
+ compose_mail(addr, fcc, role, NULL, NULL);
+
+ if(addr)
+ fs_give((void **)&addr);
+
+ if(fcc)
+ fs_give((void **)&fcc);
+}
+
+
+/*
+ * Export addresses into a file.
+ *
+ * Args: cur_line -- The current line position (in global display list)
+ * of cursor
+ * command_line -- The screen line on which to prompt
+ *
+ * Returns -- 1 if the export is done
+ * 0 if not
+ */
+int
+ab_export(struct pine *ps, long int cur_line, int command_line, int agg)
+{
+ int ret = 0, i, retflags = GER_NONE;
+ int r, orig_errno, failure = 0;
+ struct variable *vars = ps->vars;
+ char filename[MAXPATH+1], full_filename[MAXPATH+1];
+ STORE_S *store;
+ gf_io_t pc;
+ long start_of_append;
+ char *addr = NULL, *error = NULL;
+ BuildTo bldto;
+ char *p;
+ int good_addr, plur, vcard = 0, tab = 0;
+ static HISTORY_S *history = NULL;
+ AdrBk_Entry *abe;
+ VCARD_INFO_S *vinfo;
+ static ESCKEY_S ab_export_opts[] = {
+ {ctrl('T'), 10, "^T", N_("To Files")},
+ {-1, 0, NULL, NULL},
+ {-1, 0, NULL, NULL}};
+ static ESCKEY_S vcard_or_addresses[] = {
+ {'a', 'a', "A", N_("Address List")},
+ {'v', 'v', "V", N_("VCard")},
+ /* TRANSLATORS: TabSep is a command key label meaning Tab Separated List */
+ {'t', 't', "T", N_("TabSep")},
+ {-1, 0, NULL, NULL}};
+
+
+ dprint((2, "- ab_export -\n"));
+
+ if(ps->restricted){
+ q_status_message(SM_ORDER, 0, 3,
+ "Alpine demo can't export addresses to files");
+ return(ret);
+ }
+
+ while(1){
+ i = radio_buttons(_("Export list of addresses, vCard format, or Tab Separated ? "),
+ command_line, vcard_or_addresses, 'a', 'x',
+ NO_HELP, RB_NORM|RB_RET_HELP);
+ if(i == 3){
+ /* TRANSLATORS: a screen title */
+ helper(h_ab_export_vcard, _("HELP FOR EXPORT FORMAT"),
+ HLPD_SIMPLE);
+ ps_global->mangled_screen = 1;
+ }
+ else
+ break;
+ }
+
+ switch(i){
+ case 'x':
+ q_status_message(SM_INFO, 0, 2, _("Address book export cancelled"));
+ return(ret);
+
+ case 'a':
+ break;
+
+ case 'v':
+ vcard++;
+ break;
+
+ case 't':
+ tab++;
+ break;
+
+ default:
+ q_status_message(SM_ORDER, 3, 3, "can't happen in ab_export");
+ return(ret);
+ }
+
+ if(agg)
+ plur = 1;
+ else{
+ abe = ae(cur_line);
+ plur = (abe && abe->tag == List);
+ }
+
+ filename[0] = '\0';
+ r = 0;
+
+ if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
+ ab_export_opts[++r].ch = ctrl('I');
+ ab_export_opts[r].rval = 11;
+ ab_export_opts[r].name = "TAB";
+ ab_export_opts[r].label = N_("Complete");
+ }
+
+ ab_export_opts[++r].ch = -1;
+
+ r = get_export_filename(ps, filename, NULL, full_filename, sizeof(filename),
+ plur ? _("addresses") : _("address"),
+ _("EXPORT"), ab_export_opts,
+ &retflags, command_line, GE_IS_EXPORT, &history);
+
+ if(r < 0){
+ switch(r){
+ case -1:
+ q_status_message(SM_INFO, 0, 2, _("Address book export cancelled"));
+ break;
+
+ case -2:
+ q_status_message1(SM_ORDER, 0, 2,
+ _("Can't export to file outside of %s"), VAR_OPER_DIR);
+ break;
+ }
+
+ goto fini;
+ }
+
+ dprint((5, "Opening file \"%s\" for export\n",
+ full_filename ? full_filename : "?"));
+
+ if(!(store = so_get(FileStar, full_filename, WRITE_ACCESS|WRITE_TO_LOCALE))){
+ q_status_message2(SM_ORDER | SM_DING, 3, 4,
+ _("Error opening file \"%s\" for address export: %s"),
+ full_filename, error_description(errno));
+ goto fini;
+ }
+
+ /*
+ * The write_single_vcard_entry function wants a pc.
+ */
+ if(vcard || tab)
+ gf_set_so_writec(&pc, store);
+
+ start_of_append = so_tell(store);
+
+ if(agg){
+ for(i = 0; !failure && i < as.n_addrbk; i++){
+ adrbk_cntr_t num;
+ PerAddrBook *pab;
+ EXPANDED_S *next_one;
+
+ pab = &as.adrbks[i];
+ if(pab->address_book)
+ next_one = pab->address_book->selects;
+ else
+ continue;
+
+ while(!failure && (num = entry_get_next(&next_one)) != NO_NEXT){
+
+ abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) num);
+ if((vcard || tab) && abe){
+ /*
+ * There is no place to store the charset information
+ * so we don't ask for it.
+ */
+ if(!(vinfo=prepare_abe_for_vcard(ps, abe, 1)))
+ failure++;
+ else{
+ if(vcard)
+ write_single_vcard_entry(ps, pc, vinfo);
+ else
+ write_single_tab_entry(pc, vinfo);
+
+ free_vcard_info(&vinfo);
+ }
+ }
+ else if(abe){
+ bldto.type = Abe;
+ bldto.arg.abe = abe;
+ error = NULL;
+ addr = NULL;
+ good_addr = (our_build_address(bldto,&addr,&error,NULL,NULL) >= 0);
+
+ if(error){
+ q_status_message1(SM_ORDER, 0, 4, "%s", error);
+ fs_give((void **)&error);
+ }
+
+ /* rfc1522_decode the addr */
+ if(addr){
+ size_t len;
+
+ len = 4*strlen(addr)+1;
+ p = (char *)fs_get(len * sizeof(char));
+ if(rfc1522_decode_to_utf8((unsigned char *)p,len,addr) == (unsigned char *)p){
+ fs_give((void **)&addr);
+ addr = p;
+ }
+ else
+ fs_give((void **)&p);
+ }
+
+ if(good_addr){
+ int quoted = 0;
+
+ /*
+ * Change the unquoted commas into newlines.
+ * Not worth it to do complicated quoting,
+ * just consider double quotes.
+ */
+ for(p = addr; *p; p++){
+ if(*p == '"')
+ quoted = !quoted;
+ else if(!quoted && *p == ','){
+ *p++ = '\n';
+ removing_leading_white_space(p);
+ p--;
+ }
+ }
+
+ if(!so_puts(store, addr) || !so_puts(store, NEWLINE)){
+ orig_errno = errno;
+ failure = 1;
+ }
+ }
+
+ if(addr)
+ fs_give((void **)&addr);
+ }
+ }
+ }
+ }
+ else{
+ AddrScrn_Disp *dl;
+
+ dl = dlist(cur_line);
+ abe = ae(cur_line);
+ if((vcard || tab) && abe){
+ if(!(vinfo=prepare_abe_for_vcard(ps, abe, 1)))
+ failure++;
+ else{
+ if(vcard)
+ write_single_vcard_entry(ps, pc, vinfo);
+ else
+ write_single_tab_entry(pc, vinfo);
+
+ free_vcard_info(&vinfo);
+ }
+ }
+ else{
+
+ if(dl->type == ListHead && listmem_count_from_abe(abe) == 0){
+ error = _("List is empty, nothing to export!");
+ good_addr = 0;
+ }
+ else if(dl->type == ListEnt){
+ bldto.type = Str;
+ bldto.arg.str = listmem(cur_line);
+ good_addr = (our_build_address(bldto,&addr,&error,NULL,NULL) >= 0);
+ }
+ else{
+ bldto.type = Abe;
+ bldto.arg.abe = abe;
+ good_addr = (our_build_address(bldto,&addr,&error,NULL,NULL) >= 0);
+ }
+
+ if(error){
+ q_status_message1(SM_ORDER, 3, 4, "%s", error);
+ fs_give((void **)&error);
+ }
+
+ /* Have to rfc1522_decode the addr */
+ if(addr){
+ size_t len;
+ len = 4*strlen(addr)+1;
+ p = (char *)fs_get(len * sizeof(char));
+ if(rfc1522_decode_to_utf8((unsigned char *)p,len,addr) == (unsigned char *)p){
+ fs_give((void **)&addr);
+ addr = p;
+ }
+ else
+ fs_give((void **)&p);
+ }
+
+ if(good_addr){
+ int quoted = 0;
+
+ /*
+ * Change the unquoted commas into newlines.
+ * Not worth it to do complicated quoting,
+ * just consider double quotes.
+ */
+ for(p = addr; *p; p++){
+ if(*p == '"')
+ quoted = !quoted;
+ else if(!quoted && *p == ','){
+ *p++ = '\n';
+ removing_leading_white_space(p);
+ p--;
+ }
+ }
+
+ if(!so_puts(store, addr) || !so_puts(store, NEWLINE)){
+ orig_errno = errno;
+ failure = 1;
+ }
+ }
+
+ if(addr)
+ fs_give((void **)&addr);
+ }
+ }
+
+ if(vcard || tab)
+ gf_clear_so_writec(store);
+
+ if(so_give(&store)) /* release storage */
+ failure++;
+
+ if(failure){
+ our_truncate(full_filename, (off_t)start_of_append);
+ dprint((1, "FAILED Export: file \"%s\" : %s\n",
+ full_filename ? full_filename : "?",
+ error_description(orig_errno)));
+ q_status_message2(SM_ORDER | SM_DING, 3, 4,
+ _("Error exporting to \"%s\" : %s"),
+ filename, error_description(orig_errno));
+ }
+ else{
+ ret = 1;
+ q_status_message3(SM_ORDER,0,3,
+ "%s %s to file \"%s\"",
+ (vcard || tab) ? (agg ? "Entries" : "Entry")
+ : (plur ? "Addresses" : "Address"),
+ retflags & GER_OVER
+ ? "overwritten"
+ : retflags & GER_APPEND ? "appended" : "exported",
+ filename);
+ }
+
+fini:
+ ps->mangled_footer = 1;
+ return(ret);
+}
+
+
+/*
+ * Forward an address book entry or entries via email attachment.
+ *
+ * We use the vCard standard to send entries. We group multiple entries
+ * using the BEGIN/END construct of vCard, not with multiple MIME parts.
+ * A limitation of vCard is that there can be only one charset for the
+ * whole group we send, so we might lose that information.
+ *
+ * Args: cur_line -- The current line position (in global display list)
+ * of cursor
+ * command_line -- The screen line on which to prompt
+ */
+int
+ab_forward(struct pine *ps, long int cur_line, int agg)
+{
+ AddrScrn_Disp *dl;
+ AdrBk_Entry *abe;
+ ENVELOPE *outgoing = NULL;
+ BODY *pb, *body = NULL;
+ PART **pp;
+ char *sig;
+ gf_io_t pc;
+ int i, ret = 0;
+ VCARD_INFO_S *vinfo;
+ ACTION_S *role = NULL;
+
+ dprint((2, "- ab_forward -\n"));
+
+ if(!agg){
+ dl = dlist(cur_line);
+ if(dl->type != ListHead && dl->type != Simple)
+ return(ret);
+
+ abe = ae(cur_line);
+ if(!abe){
+ q_status_message(SM_ORDER, 3, 3, _("Trouble accessing current entry"));
+ return(ret);
+ }
+ }
+
+ outgoing = mail_newenvelope();
+ outgoing->message_id = generate_message_id();
+ if(agg && as.selections > 1)
+ outgoing->subject = cpystr("Forwarded address book entries from Alpine");
+ else
+ outgoing->subject = cpystr("Forwarded address book entry from Alpine");
+
+ body = mail_newbody();
+ body->type = TYPEMULTIPART;
+ /*---- The TEXT part/body ----*/
+ body->nested.part = mail_newbody_part();
+ body->nested.part->body.type = TYPETEXT;
+ /*--- Allocate an object for the body ---*/
+ if((body->nested.part->body.contents.text.data =
+ (void *)so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
+ int did_sig = 0;
+ long rflags = ROLE_COMPOSE;
+ PAT_STATE dummy;
+
+ pp = &(body->nested.part->next);
+
+ if(nonempty_patterns(rflags, &dummy)){
+ /*
+ * This is really more like Compose, even though it
+ * is called Forward.
+ */
+ if(confirm_role(rflags, &role))
+ role = combine_inherited_role(role);
+ else{ /* cancel */
+ role = NULL;
+ cmd_cancelled("Composition");
+ goto bomb;
+ }
+ }
+
+ if(role)
+ q_status_message1(SM_ORDER, 3, 4, _("Composing using role \"%s\""),
+ role->nick);
+
+ if((sig = detoken(role, NULL, 2, 0, 1, NULL, NULL)) != NULL){
+ if(*sig){
+ so_puts((STORE_S *)body->nested.part->body.contents.text.data,
+ sig);
+ did_sig++;
+ }
+
+ fs_give((void **)&sig);
+ }
+
+ /* so we don't have an empty part */
+ if(!did_sig)
+ so_puts((STORE_S *)body->nested.part->body.contents.text.data, "\n");
+ }
+ else{
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Problem creating space for message text"));
+ goto bomb;
+ }
+
+
+ /*---- create the attachment, and write abook entry into it ----*/
+ *pp = mail_newbody_part();
+ pb = &((*pp)->body);
+ pb->type = TYPETEXT;
+ pb->encoding = ENCOTHER; /* let data decide */
+ pb->id = generate_message_id();
+ pb->subtype = cpystr("DIRECTORY");
+ if(agg && as.selections > 1)
+ pb->description = cpystr("Alpine addressbook entries");
+ else
+ pb->description = cpystr("Alpine addressbook entry");
+
+ pb->parameter = NULL;
+ set_parameter(&pb->parameter, "profile", "vCard");
+
+ if((pb->contents.text.data = (void *)so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
+ int are_some_unqualified = 0, expand_nicks = 0;
+ adrbk_cntr_t num;
+ PerAddrBook *pab;
+ EXPANDED_S *next_one;
+
+ gf_set_so_writec(&pc, (STORE_S *) pb->contents.text.data);
+
+ if(agg){
+ for(i = 0; i < as.n_addrbk && !are_some_unqualified; i++){
+
+ pab = &as.adrbks[i];
+ if(pab->address_book)
+ next_one = pab->address_book->selects;
+ else
+ continue;
+
+ while((num = entry_get_next(&next_one)) != NO_NEXT &&
+ !are_some_unqualified){
+
+ abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) num);
+ if(abe->tag == Single){
+ if(abe->addr.addr && abe->addr.addr[0]
+ && !strindex(abe->addr.addr, '@'))
+ are_some_unqualified++;
+ }
+ else{
+ char **ll;
+
+ for(ll = abe->addr.list; ll && *ll; ll++){
+ if(!strindex(*ll, '@')){
+ are_some_unqualified++;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ else{
+ /*
+ * Search through the addresses to see if there are any
+ * that are unqualified, and so would be different if
+ * expanded.
+ */
+ if(abe->tag == Single){
+ if(abe->addr.addr && abe->addr.addr[0]
+ && !strindex(abe->addr.addr, '@'))
+ are_some_unqualified++;
+ }
+ else{
+ char **ll;
+
+ for(ll = abe->addr.list; ll && *ll; ll++){
+ if(!strindex(*ll, '@')){
+ are_some_unqualified++;
+ break;
+ }
+ }
+ }
+ }
+
+ if(are_some_unqualified){
+ switch(want_to(_("Expand nicknames"), 'y', 'x', h_ab_forward,WT_NORM)){
+ case 'x':
+ gf_clear_so_writec((STORE_S *) pb->contents.text.data);
+ q_status_message(SM_INFO, 0, 2, _("Address book forward cancelled"));
+ goto bomb;
+
+ case 'y':
+ expand_nicks = 1;
+ break;
+
+ case 'n':
+ expand_nicks = 0;
+ break;
+ }
+
+ ps->mangled_footer = 1;
+ }
+
+ if(agg){
+ for(i = 0; i < as.n_addrbk; i++){
+
+ pab = &as.adrbks[i];
+ if(pab->address_book)
+ next_one = pab->address_book->selects;
+ else
+ continue;
+
+ while((num = entry_get_next(&next_one)) != NO_NEXT){
+
+ abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) num);
+ if(!(vinfo=prepare_abe_for_vcard(ps, abe, expand_nicks))){
+ gf_clear_so_writec((STORE_S *) pb->contents.text.data);
+ goto bomb;
+ }
+ else{
+ write_single_vcard_entry(ps, pc, vinfo);
+ free_vcard_info(&vinfo);
+ }
+ }
+ }
+ }
+ else{
+ if(!(vinfo=prepare_abe_for_vcard(ps, abe, expand_nicks))){
+ gf_clear_so_writec((STORE_S *) pb->contents.text.data);
+ goto bomb;
+ }
+ else{
+ write_single_vcard_entry(ps, pc, vinfo);
+ free_vcard_info(&vinfo);
+ }
+ }
+
+ /* This sets parameter charset, if necessary, and encoding */
+ set_mime_type_by_grope(pb);
+ set_charset_possibly_to_ascii(pb, "UTF-8");
+ pb->size.bytes =
+ strlen((char *)so_text((STORE_S *)pb->contents.text.data));
+ }
+ else{
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Problem creating space for message text"));
+ goto bomb;
+ }
+
+ gf_clear_so_writec((STORE_S *) pb->contents.text.data);
+
+ pine_send(outgoing, &body, _("FORWARDING ADDRESS BOOK ENTRY"), role, NULL,
+ NULL, NULL, NULL, NULL, 0);
+
+ ps->mangled_screen = 1;
+ ret = 1;
+
+bomb:
+ if(outgoing)
+ mail_free_envelope(&outgoing);
+
+ if(body)
+ pine_free_body(&body);
+
+ free_action(&role);
+ return(ret);
+}
+
+
+/*
+ * Given an Adrbk_Entry fill in some of the fields in a VCARD_INFO_S
+ * for use by write_single_vcard_entry. The returned structure is freed
+ * by the caller.
+ */
+VCARD_INFO_S *
+prepare_abe_for_vcard(struct pine *ps, AdrBk_Entry *abe, int expand_nicks)
+{
+ VCARD_INFO_S *vinfo = NULL;
+ char *init_addr = NULL, *addr = NULL, *astring;
+ int cnt;
+ ADDRESS *adrlist = NULL;
+
+ if(!abe)
+ return(vinfo);
+
+ vinfo = (VCARD_INFO_S *) fs_get(sizeof(*vinfo));
+ memset((void *) vinfo, 0, sizeof(*vinfo));
+
+ if(abe->nickname && abe->nickname[0]){
+ vinfo->nickname = (char **) fs_get((1+1) * sizeof(char *));
+ vinfo->nickname[0] = cpystr(abe->nickname);
+ vinfo->nickname[1] = NULL;
+ }
+
+ if(abe->fcc && abe->fcc[0]){
+ vinfo->fcc = (char **) fs_get((1+1) * sizeof(char *));
+ vinfo->fcc[0] = cpystr(abe->fcc);
+ vinfo->fcc[1] = NULL;
+ }
+
+ if(abe->extra && abe->extra[0]){
+ vinfo->note = (char **) fs_get((1+1) * sizeof(char *));
+ vinfo->note[0] = cpystr(abe->extra);
+ vinfo->note[1] = NULL;
+ }
+
+ if(abe->fullname && abe->fullname[0]){
+ char *fn, *last = NULL, *middle = NULL, *first = NULL;
+
+ fn = adrbk_formatname(abe->fullname, &first, &last);
+ if(fn){
+ if(*fn){
+ vinfo->fullname = (char **)fs_get((1+1) * sizeof(char *));
+ vinfo->fullname[0] = fn;
+ vinfo->fullname[1] = NULL;
+ }
+ else
+ fs_give((void **)&fn);
+ }
+
+ if(last && *last){
+ if(first && (middle=strindex(first, ' '))){
+ *middle++ = '\0';
+ middle = skip_white_space(middle);
+ }
+
+ vinfo->last = last;
+ vinfo->first = first;
+ vinfo->middle = middle ? cpystr(middle) : NULL;
+ first = NULL;
+ last = NULL;
+ }
+
+ if(last)
+ fs_give((void **)&last);
+ if(first)
+ fs_give((void **)&first);
+ }
+
+ /* expand nicknames and fully-qualify unqualified names */
+ if(expand_nicks){
+ char *error = NULL;
+ BuildTo bldto;
+ ADDRESS *a;
+
+ if(abe->tag == Single)
+ init_addr = cpystr(abe->addr.addr);
+ else{
+ char **ll;
+ char *p;
+ long length;
+
+ /* figure out how large a string we need to allocate */
+ length = 0L;
+ for(ll = abe->addr.list; ll && *ll; ll++)
+ length += (strlen(*ll) + 2);
+
+ if(length)
+ length -= 2L;
+
+ init_addr = (char *)fs_get((size_t)(length+1L) * sizeof(char));
+ p = init_addr;
+
+ for(ll = abe->addr.list; ll && *ll; ll++){
+ sstrncpy(&p, *ll, length-(p-init_addr));
+ if(*(ll+1))
+ sstrncpy(&p, ", ", length-(p-init_addr));
+ }
+
+ init_addr[length] = '\0';
+ }
+
+ bldto.type = Str;
+ bldto.arg.str = init_addr;
+ our_build_address(bldto, &addr, &error, NULL, NULL);
+ if(error){
+ q_status_message1(SM_ORDER, 3, 4, "%s", error);
+ fs_give((void **)&error);
+ free_vcard_info(&vinfo);
+ return(NULL);
+ }
+
+ if(addr)
+ rfc822_parse_adrlist(&adrlist, addr, ps->maildomain);
+
+ for(cnt = 0, a = adrlist; a; a = a->next)
+ cnt++;
+
+ vinfo->email = (char **)fs_get((cnt+1) * sizeof(char *));
+
+ for(cnt = 0, a = adrlist; a; a = a->next){
+ char *bufp;
+ ADDRESS *next_addr;
+ size_t len;
+
+ next_addr = a->next;
+ a->next = NULL;
+ len = est_size(a);
+ bufp = (char *) fs_get(len * sizeof(char));
+ astring = addr_string(a, bufp, len);
+ a->next = next_addr;
+ vinfo->email[cnt++] = cpystr(astring ? astring : "");
+ fs_give((void **)&bufp);
+ }
+
+ vinfo->email[cnt] = '\0';
+ }
+ else{ /* don't expand or qualify */
+ if(abe->tag == Single){
+ astring =
+ (abe->addr.addr && abe->addr.addr[0]) ? abe->addr.addr : "";
+ vinfo->email = (char **)fs_get((1+1) * sizeof(char *));
+ vinfo->email[0] = cpystr(astring);
+ vinfo->email[1] = '\0';
+ }
+ else{
+ char **ll;
+
+ for(cnt = 0, ll = abe->addr.list; ll && *ll; ll++)
+ cnt++;
+
+ vinfo->email = (char **)fs_get((cnt+1) * sizeof(char *));
+ for(cnt = 0, ll = abe->addr.list; ll && *ll; ll++)
+ vinfo->email[cnt++] = cpystr(*ll);
+
+ vinfo->email[cnt] = '\0';
+ }
+ }
+
+ return(vinfo);
+}
+
+
+void
+free_vcard_info(VCARD_INFO_S **vinfo)
+{
+ if(vinfo && *vinfo){
+ if((*vinfo)->nickname)
+ free_list_array(&(*vinfo)->nickname);
+ if((*vinfo)->fullname)
+ free_list_array(&(*vinfo)->fullname);
+ if((*vinfo)->fcc)
+ free_list_array(&(*vinfo)->fcc);
+ if((*vinfo)->note)
+ free_list_array(&(*vinfo)->note);
+ if((*vinfo)->title)
+ free_list_array(&(*vinfo)->title);
+ if((*vinfo)->tel)
+ free_list_array(&(*vinfo)->tel);
+ if((*vinfo)->email)
+ free_list_array(&(*vinfo)->email);
+
+ if((*vinfo)->first)
+ fs_give((void **)&(*vinfo)->first);
+ if((*vinfo)->middle)
+ fs_give((void **)&(*vinfo)->middle);
+ if((*vinfo)->last)
+ fs_give((void **)&(*vinfo)->last);
+
+ fs_give((void **)vinfo);
+ }
+}
+
+
+/*
+ *
+ */
+void
+write_single_vcard_entry(struct pine *ps, gf_io_t pc, VCARD_INFO_S *vinfo)
+{
+ char *decoded, *tmp2, *tmp = NULL, *hdr;
+ char **ll;
+ int i, did_fn = 0, did_n = 0;
+ int cr;
+ char eol[3];
+#define FOLD_BY 75
+
+ if(!vinfo)
+ return;
+
+#if defined(DOS) || defined(OS2)
+ cr = 1;
+#else
+ cr = 0;
+#endif
+
+ if(cr)
+ strncpy(eol, "\r\n", sizeof(eol));
+ else
+ strncpy(eol, "\n", sizeof(eol));
+
+ eol[sizeof(eol)-1] = '\0';
+
+ gf_puts("BEGIN:VCARD", pc);
+ gf_puts(eol, pc);
+ gf_puts("VERSION:3.0", pc);
+ gf_puts(eol, pc);
+
+ for(i = 0; i < 7; i++){
+ switch(i){
+ case 0:
+ ll = vinfo->nickname;
+ hdr = "NICKNAME:";
+ break;
+
+ case 1:
+ ll = vinfo->fullname;
+ hdr = "FN:";
+ break;
+
+ case 2:
+ ll = vinfo->email;
+ hdr = "EMAIL:";
+ break;
+
+ case 3:
+ ll = vinfo->title;
+ hdr = "TITLE:";
+ break;
+
+ case 4:
+ ll = vinfo->note;
+ hdr = "NOTE:";
+ break;
+
+ case 5:
+ ll = vinfo->fcc;
+ hdr = "X-FCC:";
+ break;
+
+ case 6:
+ ll = vinfo->tel;
+ hdr = "TEL:";
+ break;
+
+ default:
+ panic("can't happen in write_single_vcard_entry");
+ }
+
+ for(; ll && *ll; ll++){
+ decoded = (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf), SIZEOF_20KBUF, *ll);
+
+ tmp = vcard_escape(decoded);
+ if(tmp){
+ if((tmp2 = fold(tmp, FOLD_BY, FOLD_BY, hdr, " ", FLD_PWS | (cr ? FLD_CRLF : 0))) != NULL){
+ gf_puts(tmp2, pc);
+ fs_give((void **)&tmp2);
+ if(i == 1)
+ did_fn++;
+ }
+
+ fs_give((void **)&tmp);
+ }
+ }
+ }
+
+ if(vinfo->last && vinfo->last[0]){
+ char *pl, *pf, *pm;
+
+ pl = vcard_escape(vinfo->last);
+ pf = (vinfo->first && *vinfo->first) ? vcard_escape(vinfo->first)
+ : NULL;
+ pm = (vinfo->middle && *vinfo->middle) ? vcard_escape(vinfo->middle)
+ : NULL;
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s%s%s",
+ (pl && *pl) ? pl : "",
+ ((pf && *pf) || (pm && *pm)) ? ";" : "",
+ (pf && *pf) ? pf : "",
+ (pm && *pm) ? ";" : "",
+ (pm && *pm) ? pm : "");
+
+ if((tmp2 = fold(tmp_20k_buf, FOLD_BY, FOLD_BY, "N:", " ",
+ FLD_PWS | (cr ? FLD_CRLF : 0))) != NULL){
+ gf_puts(tmp2, pc);
+ fs_give((void **)&tmp2);
+ did_n++;
+ }
+
+ if(pl)
+ fs_give((void **)&pl);
+ if(pf)
+ fs_give((void **)&pf);
+ if(pm)
+ fs_give((void **)&pm);
+ }
+
+ /*
+ * These two types are required in draft-ietf-asid-mime-vcard-06, which
+ * is April 98 and is in last call.
+ */
+ if(!did_fn || !did_n){
+ if(did_n){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s%s%s",
+ (vinfo->first && *vinfo->first) ? vinfo->first : "",
+ (vinfo->first && *vinfo->first &&
+ vinfo->middle && *vinfo->middle) ? " " : "",
+ (vinfo->middle && *vinfo->middle) ? vinfo->middle : "",
+ (((vinfo->first && *vinfo->first) ||
+ (vinfo->middle && *vinfo->middle)) &&
+ vinfo->last && *vinfo->last) ? " " : "",
+ (vinfo->last && *vinfo->last) ? vinfo->last : "");
+
+ tmp = vcard_escape(tmp_20k_buf);
+ if(tmp){
+ if((tmp2 = fold(tmp, FOLD_BY, FOLD_BY, "FN:", " ",
+ FLD_PWS | (cr ? FLD_CRLF : 0))) != NULL){
+ gf_puts(tmp2, pc);
+ fs_give((void **)&tmp2);
+ did_n++;
+ }
+
+ fs_give((void **)&tmp);
+ }
+ }
+ else{
+ if(!did_fn){
+ gf_puts("FN:<Unknown>", pc);
+ gf_puts(eol, pc);
+ }
+
+ gf_puts("N:<Unknown>", pc);
+ gf_puts(eol, pc);
+ }
+ }
+
+ gf_puts("END:VCARD", pc);
+ gf_puts(eol, pc);
+}
+
+
+/*
+ *
+ */
+void
+write_single_tab_entry(gf_io_t pc, VCARD_INFO_S *vinfo)
+{
+ char *decoded, *tmp = NULL;
+ char **ll;
+ int i, first;
+ char *eol;
+
+ if(!vinfo)
+ return;
+
+#if defined(DOS) || defined(OS2)
+ eol = "\r\n";
+#else
+ eol = "\n";
+#endif
+
+ for(i = 0; i < 4; i++){
+ switch(i){
+ case 0:
+ ll = vinfo->nickname;
+ break;
+
+ case 1:
+ ll = vinfo->fullname;
+ break;
+
+ case 2:
+ ll = vinfo->email;
+ break;
+
+ case 3:
+ ll = vinfo->note;
+ break;
+
+ default:
+ panic("can't happen in write_single_tab_entry");
+ }
+
+ if(i)
+ gf_puts("\t", pc);
+
+ for(first = 1; ll && *ll; ll++){
+
+ decoded = (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf), SIZEOF_20KBUF, *ll);
+ tmp = vcard_escape(decoded);
+ if(tmp){
+ if(i == 2 && !first)
+ gf_puts(",", pc);
+ else
+ first = 0;
+
+ gf_puts(tmp, pc);
+ fs_give((void **)&tmp);
+ }
+ }
+ }
+
+ gf_puts(eol, pc);
+}
+
+
+/*
+ * for ab_save percent done
+ */
+static int total_to_copy;
+static int copied_so_far;
+int
+percent_done_copying(void)
+{
+ return((copied_so_far * 100) / total_to_copy);
+}
+
+int
+cmp_action_list(const qsort_t *a1, const qsort_t *a2)
+{
+ ACTION_LIST_S *x = (ACTION_LIST_S *)a1;
+ ACTION_LIST_S *y = (ACTION_LIST_S *)a2;
+
+ if(x->pab != y->pab)
+ return((x->pab > y->pab) ? 1 : -1); /* order doesn't matter */
+
+ /*
+ * The only one that matters is when both x and y have dup lit.
+ * For the others, just need to be consistent so sort will terminate.
+ */
+ if(x->dup){
+ if(y->dup)
+ return((x->num_in_dst > y->num_in_dst) ? -1
+ : (x->num_in_dst == y->num_in_dst) ? 0 : 1);
+ else
+ return(-1);
+ }
+ else if(y->dup)
+ return(1);
+ else
+ return((x->num > y->num) ? -1 : (x->num == y->num) ? 0 : 1);
+}
+
+
+/*
+ * Copy a bunch of address book entries to a particular address book.
+ *
+ * Args abook -- the current addrbook handle
+ * cur_line -- the current line the cursor is on
+ * command_line -- the line to prompt on
+ * agg -- 1 if this is an aggregate copy
+ *
+ * Returns 1 if successful, 0 if not
+ */
+int
+ab_save(struct pine *ps, AdrBk *abook, long int cur_line, int command_line, int agg)
+{
+ PerAddrBook *pab_dst, *pab;
+ SAVE_STATE_S state; /* For saving state of addrbooks temporarily */
+ int rc, i;
+ int how_many_dups = 0, how_many_to_copy = 0, skip_dups = 0;
+ int how_many_no_action = 0, ret = 1;
+ int err = 0, need_write = 0, we_cancel = 0;
+ int act_list_size, special_case = 0;
+ adrbk_cntr_t num, new_entry_num;
+ char warn[2][MAX_NICKNAME+1];
+ char warning[MAX_NICKNAME+1];
+ char tmp[MAX(200,2*MAX_NICKNAME+80)];
+ ACTION_LIST_S *action_list = NULL, *al;
+ static ESCKEY_S save_or_export[] = {
+ {'s', 's', "S", N_("Save")},
+ {'e', 'e', "E", N_("Export")},
+ {-1, 0, NULL, NULL}};
+
+ if(!agg)
+ snprintf(tmp, sizeof(tmp), _("Save highlighted entry to address book or Export to filesystem ? "));
+ else if(as.selections > 1)
+ snprintf(tmp, sizeof(tmp), _("Save selected entries to address book or Export to filesystem ? "));
+ else if(as.selections == 1)
+ snprintf(tmp, sizeof(tmp), _("Save selected entry to address book or Export to filesystem ? "));
+ else
+ snprintf(tmp, sizeof(tmp), _("Save to address book or Export to filesystem ? "));
+
+ i = radio_buttons(tmp, -FOOTER_ROWS(ps), save_or_export, 's', 'x',
+ h_ab_save_exp, RB_NORM);
+ switch(i){
+ case 'x':
+ q_status_message(SM_INFO, 0, 2, _("Address book save cancelled"));
+ return(0);
+
+ case 'e':
+ return(ab_export(ps, cur_line, command_line, agg));
+
+ case 's':
+ break;
+
+ default:
+ q_status_message(SM_ORDER, 3, 3, "can't happen in ab_save");
+ return(0);
+ }
+
+ pab_dst = setup_for_addrbook_add(&state, command_line, _("Save"));
+ if(!pab_dst)
+ goto get_out;
+
+ pab = &as.adrbks[as.cur];
+
+ dprint((2, "- ab_save: %s -> %s (agg=%d)-\n",
+ pab->abnick ? pab->abnick : "?",
+ pab_dst->abnick ? pab_dst->abnick : "?", agg));
+
+ if(agg)
+ act_list_size = as.selections;
+ else
+ act_list_size = 1;
+
+ action_list = (ACTION_LIST_S *)fs_get((act_list_size+1) *
+ sizeof(ACTION_LIST_S));
+ memset((void *)action_list, 0, (act_list_size+1) * sizeof(ACTION_LIST_S));
+ al = action_list;
+
+ if(agg){
+
+ for(i = 0; i < as.n_addrbk; i++){
+ EXPANDED_S *next_one;
+
+ pab = &as.adrbks[i];
+ if(pab->address_book)
+ next_one = pab->address_book->selects;
+ else
+ continue;
+
+ while((num = entry_get_next(&next_one)) != NO_NEXT){
+ if(pab != pab_dst &&
+ pab->ostatus != Open &&
+ pab->ostatus != NoDisplay)
+ init_abook(pab, NoDisplay);
+
+ if(pab->ostatus != Open && pab->ostatus != NoDisplay){
+ q_status_message1(SM_ORDER, 0, 4,
+ _("Can't re-open address book %s to save from"),
+ pab->abnick);
+ err++;
+ goto get_out;
+ }
+
+ set_act_list_member(al, (a_c_arg_t)num, pab_dst, pab, warning);
+ if(al->skip)
+ how_many_no_action++;
+ else{
+ if(al->dup){
+ if(how_many_dups < 2 && warning[0]){
+ strncpy(warn[how_many_dups], warning, MAX_NICKNAME);
+ warn[how_many_dups][MAX_NICKNAME] = '\0';
+ }
+
+ how_many_dups++;
+ }
+
+ how_many_to_copy++;
+ }
+
+ al++;
+ }
+ }
+ }
+ else{
+ if(is_addr(cur_line)){
+ AddrScrn_Disp *dl;
+
+ dl = dlist(cur_line);
+
+ if(dl->type == ListEnt)
+ special_case++;
+
+ if(pab && dl){
+ num = dl->elnum;
+ set_act_list_member(al, (a_c_arg_t)num, pab_dst, pab, warning);
+ }
+ else
+ al->skip = 1;
+
+ if(al->skip)
+ how_many_no_action++;
+ else{
+ if(al->dup){
+ if(how_many_dups < 2 && warning[0]){
+ strncpy(warn[how_many_dups], warning, MAX_NICKNAME);
+ warn[how_many_dups][MAX_NICKNAME] = '\0';
+ }
+
+ how_many_dups++;
+ }
+
+ how_many_to_copy++;
+ }
+ }
+ else{
+ q_status_message(SM_ORDER, 0, 4, _("No current entry to save"));
+ goto get_out;
+ }
+ }
+
+ if(how_many_to_copy == 0 && how_many_no_action == 1 && act_list_size == 1)
+ special_case++;
+
+ if(special_case){
+ TA_STATE_S tas, *tasp;
+
+ /* Not going to use the action_list now */
+ if(action_list)
+ fs_give((void **)&action_list);
+
+ tasp = &tas;
+ tas.state = state;
+ tas.pab = pab_dst;
+ take_this_one_entry(ps, &tasp, abook, cur_line);
+
+ /*
+ * If take_this_one_entry or its children didn't do this for
+ * us, we do it here.
+ */
+ if(tas.pab)
+ restore_state(&(tas.state));
+
+ /*
+ * We don't have enough information to know what to return.
+ */
+ return(0);
+ }
+
+ /* nothing to do (except for special Take case below) */
+ if(how_many_to_copy == 0){
+ if(how_many_no_action == 0){
+ err++;
+ goto get_out;
+ }
+ else{
+ restore_state(&state);
+
+ if(how_many_no_action > 1)
+ snprintf(tmp, sizeof(tmp), _("Saved %d entries to %s"), how_many_no_action, pab_dst->abnick);
+ else
+ snprintf(tmp, sizeof(tmp), _("Saved %d entry to %s"), how_many_no_action, pab_dst->abnick);
+
+ tmp[sizeof(tmp)-1] = '\0';
+ q_status_message(SM_ORDER, 0, 4, tmp);
+ if(action_list)
+ fs_give((void **)&action_list);
+
+ return(ret);
+ }
+ }
+
+ /*
+ * If there are some nicknames which already exist in the selected
+ * abook, ask user what to do.
+ */
+ if(how_many_dups > 0){
+ if(how_many_dups == 1)
+ snprintf(tmp, sizeof(tmp), _("Entry with nickname \"%.*s\" already exists, replace "),
+ MAX_NICKNAME, warn[0]);
+ else if(how_many_dups == 2)
+ snprintf(tmp, sizeof(tmp),
+ _("Nicknames \"%.*s\" and \"%.*s\" already exist, replace "),
+ MAX_NICKNAME, warn[0], MAX_NICKNAME, warn[1]);
+ else
+ snprintf(tmp, sizeof(tmp), _("%d of the nicknames already exist, replace "),
+ how_many_dups);
+
+ tmp[sizeof(tmp)-1] = '\0';
+
+ switch(want_to(tmp, 'n', 'x', h_ab_copy_dups, WT_NORM)){
+ case 'n':
+ skip_dups++;
+ if(how_many_to_copy == how_many_dups){
+ restore_state(&state);
+ if(action_list)
+ fs_give((void **)&action_list);
+
+ q_status_message(SM_INFO, 0, 2, _("Address book save cancelled"));
+ return(ret);
+ }
+
+ break;
+
+ case 'y':
+ break;
+
+ case 'x':
+ err++;
+ goto get_out;
+ }
+ }
+
+ /*
+ * Because the deletes happen immediately we have to delete from high
+ * entry number towards lower entry numbers so that we are deleting
+ * the correct entries. In order to do that we'll sort the action_list
+ * to give us a safe order.
+ */
+ if(!skip_dups && how_many_dups > 1)
+ qsort((qsort_t *)action_list, (size_t)as.selections, sizeof(*action_list),
+ cmp_action_list);
+
+ /*
+ * Set up the busy alarm percent counters.
+ */
+ total_to_copy = how_many_to_copy - (skip_dups ? how_many_dups : 0);
+ copied_so_far = 0;
+ we_cancel = busy_cue(_("Saving entries"),
+ (total_to_copy > 4) ? percent_done_copying : NULL, 0);
+
+ /*
+ * Add the list of entries to the destination abook.
+ */
+ for(al = action_list; al && al->pab; al++){
+ AdrBk_Entry *abe;
+
+ if(al->skip || (skip_dups && al->dup))
+ continue;
+
+ if(!(abe = adrbk_get_ae(al->pab->address_book, (a_c_arg_t) al->num))){
+ q_status_message1(SM_ORDER | SM_DING, 3, 5,
+ _("Error saving entry: %s"),
+ error_description(errno));
+ err++;
+ goto get_out;
+ }
+
+ /*
+ * Delete existing dups and replace them.
+ */
+ if(al->dup){
+
+ /* delete the existing entry */
+ rc = 0;
+ if(adrbk_delete(pab_dst->address_book,
+ (a_c_arg_t)al->num_in_dst, 1, 0, 0, 0) == 0){
+ need_write++;
+ }
+ else{
+ q_status_message2(SM_ORDER | SM_DING, 3, 5,
+ _("Error replacing entry in %s: %s"),
+ pab_dst->abnick,
+ error_description(errno));
+ err++;
+ goto get_out;
+ }
+ }
+
+ /*
+ * Now we have a clean slate to work with.
+ * Add (sorted in correctly) or append abe to the destination
+ * address book.
+ */
+ if(total_to_copy <= 1)
+ rc = adrbk_add(pab_dst->address_book,
+ NO_NEXT,
+ abe->nickname,
+ abe->fullname,
+ abe->tag == Single ? abe->addr.addr : NULL,
+ abe->fcc,
+ abe->extra,
+ abe->tag,
+ &new_entry_num,
+ (int *)NULL,
+ 0,
+ 0,
+ 0);
+ else
+ rc = adrbk_append(pab_dst->address_book,
+ abe->nickname,
+ abe->fullname,
+ abe->tag == Single ? abe->addr.addr : NULL,
+ abe->fcc,
+ abe->extra,
+ abe->tag,
+ &new_entry_num);
+
+ if(rc == 0)
+ need_write++;
+
+ /*
+ * If the entry we copied is a list, we also have to add
+ * the list members to the copy.
+ */
+ if(rc == 0 && abe->tag == List){
+ int save_sort_rule;
+
+ /*
+ * We want it to copy the list in the exact order
+ * without sorting it.
+ */
+ save_sort_rule = pab_dst->address_book->sort_rule;
+ pab_dst->address_book->sort_rule = AB_SORT_RULE_NONE;
+
+ rc = adrbk_nlistadd(pab_dst->address_book,
+ (a_c_arg_t)new_entry_num, NULL, NULL,
+ abe->addr.list,
+ 0, 0, 0);
+
+ pab_dst->address_book->sort_rule = save_sort_rule;
+ }
+
+ if(rc != 0){
+ if(abe && abe->nickname)
+ q_status_message2(SM_ORDER | SM_DING, 3, 5, _("Error saving %s: %s"), abe->nickname, error_description(errno));
+ else
+ q_status_message1(SM_ORDER | SM_DING, 3, 5, _("Error saving entry: %s"), error_description(errno));
+ err++;
+ goto get_out;
+ }
+
+ copied_so_far++;
+ }
+
+ if(need_write){
+ int sort_happened = 0;
+
+ if(adrbk_write(pab_dst->address_book, 0, NULL, &sort_happened, 0, 1)){
+ err++;
+ goto get_out;
+ }
+
+ if(sort_happened)
+ ps_global->mangled_screen = 1;
+ }
+
+get_out:
+ if(we_cancel)
+ cancel_busy_cue(1);
+
+ restore_state(&state);
+ if(action_list)
+ fs_give((void **)&action_list);
+
+ ps_global->mangled_footer = 1;
+
+ if(err){
+ ret = 0;
+ if(need_write)
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Save only partially completed"));
+ else
+ q_status_message(SM_INFO, 0, 2, _("Address book save cancelled"));
+ }
+ else if (how_many_to_copy + how_many_no_action -
+ (skip_dups ? how_many_dups : 0) > 0){
+
+ ret = 1;
+ snprintf(tmp, sizeof(tmp), "Saved %d %s to %s",
+ how_many_to_copy + how_many_no_action -
+ (skip_dups ? how_many_dups : 0),
+ ((how_many_to_copy + how_many_no_action -
+ (skip_dups ? how_many_dups : 0)) > 1) ? "entries" : "entry",
+ pab_dst->abnick);
+ tmp[sizeof(tmp)-1] = '\0';
+ q_status_message(SM_ORDER, 0, 4, tmp);
+ }
+
+ return(ret);
+}
+
+
+/*
+ * Warn should point to an array of size MAX_NICKNAME+1.
+ */
+void
+set_act_list_member(ACTION_LIST_S *al, a_c_arg_t numarg, PerAddrBook *pab_dst, PerAddrBook *pab, char *warn)
+{
+ AdrBk_Entry *abe1, *abe2;
+ adrbk_cntr_t num;
+
+ num = (adrbk_cntr_t)numarg;
+
+ al->pab = pab;
+ al->num = num;
+
+ /* skip if they're copying from and to same addrbook */
+ if(pab == pab_dst)
+ al->skip = 1;
+ else{
+ abe1 = adrbk_get_ae(pab->address_book, numarg);
+ if(abe1 && abe1->nickname && abe1->nickname[0]){
+ adrbk_cntr_t dst_enum;
+
+ abe2 = adrbk_lookup_by_nick(pab_dst->address_book,
+ abe1->nickname, &dst_enum);
+ /*
+ * This nickname already exists in the destn address book.
+ */
+ if(abe2){
+ /* If it isn't different, no problem. Check it out. */
+ if(abes_are_equal(abe1, abe2))
+ al->skip = 1;
+ else{
+ strncpy(warn, abe1->nickname, MAX_NICKNAME);
+ warn[MAX_NICKNAME] = '\0';
+ al->dup = 1;
+ al->num_in_dst = dst_enum;
+ }
+ }
+ }
+ }
+}
+
+
+/*
+ * Print out the display list.
+ */
+int
+ab_print(int agg)
+{
+ int do_entry = 0, curopen;
+ char *prompt;
+
+ dprint((2, "- ab_print -\n"));
+
+ curopen = cur_is_open();
+ if(!agg && curopen){
+ static ESCKEY_S prt[] = {
+ {'a', 'a', "A", N_("AddressBook")},
+ {'e', 'e', "E", N_("Entry")},
+ {-1, 0, NULL, NULL}};
+
+ prompt = _("Print Address Book or just this Entry? ");
+ switch(radio_buttons(prompt, -FOOTER_ROWS(ps_global), prt, 'a', 'x',
+ NO_HELP, RB_NORM)){
+ case 'x' :
+ q_status_message(SM_INFO, 0, 2, _("Address book print cancelled"));
+ ps_global->mangled_footer = 1;
+ return 0;
+
+ case 'e':
+ do_entry = 1;
+ break;
+
+ default:
+ case 'a':
+ break;
+ }
+ }
+
+ /* TRANSLATORS: This is input for
+ Print something1 using something2. The thing we're
+ defining here is something1. */
+ if(agg)
+ prompt = _("selected entries");
+ else{
+ if(!curopen)
+ prompt = _("address book list");
+ else if(do_entry)
+ prompt = _("entry");
+ else
+ prompt = _("address book");
+ }
+
+ if(open_printer(prompt) == 0){
+ DL_CACHE_S dlc_buf, *match_dlc;
+ AddrScrn_Disp *dl;
+ AdrBk_Entry *abe;
+ long save_line;
+ char *addr;
+ char spaces[100];
+ char more_spaces[100];
+ char b[500];
+ int abook_indent;
+
+ save_line = as.top_ent + as.cur_row;
+ match_dlc = get_dlc(save_line);
+ dlc_buf = *match_dlc;
+ match_dlc = &dlc_buf;
+
+ if(do_entry){ /* print an individual addrbook entry */
+
+ abook_indent = utf8_width(_("Nickname")) + 2;
+
+ snprintf(spaces, sizeof(spaces), "%*.*s", abook_indent+2, abook_indent+2, "");
+ snprintf(more_spaces, sizeof(more_spaces), "%*.*s",
+ abook_indent+4, abook_indent+4, "");
+
+ dl = dlist(save_line);
+ abe = ae(save_line);
+
+ if(abe){
+ int are_some_unqualified = 0, expand_nicks = 0;
+ char *string, *tmp;
+ ADDRESS *adrlist = NULL;
+
+ /*
+ * Search through the addresses to see if there are any
+ * that are unqualified, and so would be different if
+ * expanded.
+ */
+ if(abe->tag == Single){
+ if(abe->addr.addr && abe->addr.addr[0]
+ && !strindex(abe->addr.addr, '@'))
+ are_some_unqualified++;
+ }
+ else{
+ char **ll;
+
+ for(ll = abe->addr.list; ll && *ll; ll++){
+ if(!strindex(*ll, '@')){
+ are_some_unqualified++;
+ break;
+ }
+ }
+ }
+
+ if(are_some_unqualified){
+ switch(want_to("Expand nicknames", 'y', 'x', h_ab_forward,
+ WT_NORM)){
+ case 'x':
+ q_status_message(SM_INFO, 0, 2, _("Address book print cancelled"));
+ ps_global->mangled_footer = 1;
+ return 0;
+
+ case 'y':
+ expand_nicks = 1;
+ break;
+
+ case 'n':
+ expand_nicks = 0;
+ break;
+ }
+ }
+
+ /* expand nicknames and fully-qualify unqualified names */
+ if(expand_nicks){
+ char *error = NULL;
+ BuildTo bldto;
+ char *init_addr = NULL;
+
+ if(abe->tag == Single)
+ init_addr = cpystr(abe->addr.addr);
+ else{
+ char **ll;
+ char *p;
+ long length;
+
+ /* figure out how large a string we need to allocate */
+ length = 0L;
+ for(ll = abe->addr.list; ll && *ll; ll++)
+ length += (strlen(*ll) + 2);
+
+ if(length)
+ length -= 2L;
+
+ init_addr = (char *)fs_get((size_t)(length+1L) *
+ sizeof(char));
+ p = init_addr;
+
+ for(ll = abe->addr.list; ll && *ll; ll++){
+ sstrncpy(&p, *ll, length-(p-init_addr));
+ if(*(ll+1))
+ sstrncpy(&p, ", ", length-(p-init_addr));
+ }
+
+ init_addr[length] = '\0';
+ }
+
+ bldto.type = Str;
+ bldto.arg.str = init_addr;
+ our_build_address(bldto, &addr, &error, NULL, NULL);
+ if(init_addr)
+ fs_give((void **)&init_addr);
+
+ if(error){
+ q_status_message1(SM_ORDER, 0, 4, "%s", error);
+ fs_give((void **)&error);
+
+ ps_global->mangled_footer = 1;
+ return 0;
+ }
+
+ if(addr){
+ rfc822_parse_adrlist(&adrlist, addr,
+ ps_global->maildomain);
+ fs_give((void **)&addr);
+ }
+
+ /* Will use adrlist to do the printing below */
+ }
+
+ tmp = abe->nickname ? abe->nickname : "";
+ string = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, tmp);
+ utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Nickname"));
+ if((tmp = fold(string, 80, 80, b, spaces, FLD_NONE)) != NULL){
+ print_text(tmp);
+ fs_give((void **)&tmp);
+ }
+
+ tmp = abe->fullname ? abe->fullname : "";
+ string = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, tmp);
+ utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Fullname"));
+ if((tmp = fold(string, 80, 80, b, spaces, FLD_NONE)) != NULL){
+ print_text(tmp);
+ fs_give((void **)&tmp);
+ }
+
+ tmp = abe->fcc ? abe->fcc : "";
+ string = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, tmp);
+ utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Fcc"));
+ if((tmp = fold(string, 80, 80, b, spaces, FLD_NONE)) != NULL){
+ print_text(tmp);
+ fs_give((void **)&tmp);
+ }
+
+ tmp = abe->extra ? abe->extra : "";
+ {unsigned char *p, *bb = NULL;
+ size_t n, len;
+ if((n = 4*strlen(tmp)) > SIZEOF_20KBUF-1){
+ len = n+1;
+ p = bb = (unsigned char *)fs_get(len * sizeof(char));
+ }
+ else{
+ len = SIZEOF_20KBUF;
+ p = (unsigned char *)tmp_20k_buf;
+ }
+
+ string = (char *)rfc1522_decode_to_utf8(p, len, tmp);
+ utf8_snprintf(b, len, "%-*.*w: ", abook_indent, abook_indent, _("Comment"));
+ if((tmp = fold(string, 80, 80, b, spaces, FLD_NONE)) != NULL){
+ print_text(tmp);
+ fs_give((void **)&tmp);
+ }
+
+ if(bb)
+ fs_give((void **)&bb);
+ }
+
+ /*
+ * Print addresses
+ */
+
+ if(expand_nicks){
+ ADDRESS *a;
+
+ for(a = adrlist; a; a = a->next){
+ char *bufp;
+ ADDRESS *next_addr;
+ size_t len;
+
+ next_addr = a->next;
+ a->next = NULL;
+ len = est_size(a);
+ bufp = (char *) fs_get(len * sizeof(char));
+ tmp = addr_string(a, bufp, len);
+ a->next = next_addr;
+ string = (char *)rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf+10000,
+ SIZEOF_20KBUF-10000, tmp);
+ utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Addresses"));
+ if((tmp = fold(string, 80, 80,
+ (a == adrlist) ? b : spaces,
+ more_spaces, FLD_NONE)) != NULL){
+ print_text(tmp);
+ fs_give((void **)&tmp);
+ }
+
+ fs_give((void **)&bufp);
+ }
+
+ if(adrlist)
+ mail_free_address(&adrlist);
+ else{
+ utf8_snprintf(b, sizeof(b), "%-*.*w:\n", abook_indent, abook_indent, _("Addresses"));
+ print_text(b);
+ }
+ }
+ else{ /* don't expand or qualify */
+ if(abe->tag == Single){
+ tmp = abe->addr.addr ? abe->addr.addr : "";
+ string = (char *)rfc1522_decode_to_utf8((unsigned char *) (tmp_20k_buf+10000),
+ SIZEOF_20KBUF-10000, tmp);
+ utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Addresses"));
+ if((tmp = fold(string, 80, 80, b,
+ more_spaces, FLD_NONE)) != NULL){
+ print_text(tmp);
+ fs_give((void **)&tmp);
+ }
+ }
+ else{
+ char **ll;
+
+ if(!abe->addr.list || !abe->addr.list[0]){
+ utf8_snprintf(b, sizeof(b), "%-*.*w:\n", abook_indent, abook_indent, _("Addresses"));
+ print_text(b);
+ }
+
+ for(ll = abe->addr.list; ll && *ll; ll++){
+ string = (char *)rfc1522_decode_to_utf8((unsigned char *) (tmp_20k_buf+10000),
+ SIZEOF_20KBUF-10000, *ll);
+ utf8_snprintf(b, sizeof(b), "%-*.*w: ", abook_indent, abook_indent, _("Addresses"));
+ if((tmp = fold(string, 80, 80,
+ (ll == abe->addr.list)
+ ? b : spaces,
+ more_spaces, FLD_NONE)) != NULL){
+ print_text(tmp);
+ fs_give((void **)&tmp);
+ }
+ }
+ }
+ }
+ }
+ }
+ else{
+ long lineno;
+ char lbuf[6*MAX_SCREEN_COLS + 1];
+ char *p;
+ int i, savecur, savezoomed;
+ OpenStatus savestatus;
+ PerAddrBook *pab;
+
+ if(agg){ /* print all selected entries */
+ if(F_ON(F_CMBND_ABOOK_DISP,ps_global)){
+ savezoomed = as.zoomed;
+ /*
+ * Fool display code into thinking display is zoomed, so
+ * we'll skip the unselected entries. Since this feature
+ * causes all abooks to be displayed we don't have to
+ * step through each addrbook.
+ */
+ as.zoomed = 1;
+ warp_to_beginning();
+ lineno = 0L;
+ for(dl = dlist(lineno);
+ dl->type != End;
+ dl = dlist(++lineno)){
+
+ switch(dl->type){
+ case Beginning:
+ case ListClickHere:
+ case ListEmpty:
+ case ClickHereCmb:
+ case Text:
+ case TitleCmb:
+ case Empty:
+ case ZoomEmpty:
+ case AskServer:
+ continue;
+ default:
+ break;
+ }
+
+ p = get_abook_display_line(lineno, 0, NULL, NULL,
+ NULL, lbuf, sizeof(lbuf));
+ print_text1("%s\n", p);
+ }
+
+ as.zoomed = savezoomed;
+ }
+ else{ /* print all selected entries */
+ savecur = as.cur;
+ savezoomed = as.zoomed;
+ as.zoomed = 1;
+
+ for(i = 0; i < as.n_addrbk; i++){
+ pab = &as.adrbks[i];
+ if(!(pab->address_book &&
+ any_selected(pab->address_book->selects)))
+ continue;
+
+ /*
+ * Print selected entries from addrbook i.
+ * We have to put addrbook i into Open state so
+ * that the display code will work right.
+ */
+ as.cur = i;
+ savestatus = pab->ostatus;
+ init_abook(pab, Open);
+ init_disp_form(pab, ps_global->VAR_ABOOK_FORMATS, i);
+ (void)calculate_field_widths();
+ warp_to_beginning();
+ lineno = 0L;
+
+ for(dl = dlist(lineno);
+ dl->type != End;
+ dl = dlist(++lineno)){
+
+ switch(dl->type){
+ case Beginning:
+ case ListClickHere:
+ case ClickHereCmb:
+ continue;
+ default:
+ break;
+ }
+
+ p = get_abook_display_line(lineno, 0, NULL, NULL,
+ NULL, lbuf,sizeof(lbuf));
+ print_text1("%s\n", p);
+ }
+
+ init_abook(pab, savestatus);
+ }
+
+ as.cur = savecur;
+ as.zoomed = savezoomed;
+ /* restore the display for the current addrbook */
+ init_disp_form(&as.adrbks[as.cur],
+ ps_global->VAR_ABOOK_FORMATS, as.cur);
+ }
+ }
+ else{ /* print either the abook list or a single abook */
+ int anum;
+ DL_CACHE_S *dlc;
+
+ savezoomed = as.zoomed;
+ as.zoomed = 0;
+
+ if(curopen){ /* print a single address book */
+ anum = adrbk_num_from_lineno(as.top_ent+as.cur_row);
+ warp_to_top_of_abook(anum);
+ if(F_ON(F_CMBND_ABOOK_DISP,ps_global))
+ lineno = 0L - XTRA_TITLE_LINES_IN_OLD_ABOOK_DISP;
+ else{
+ print_text(" ");
+ print_text(_("ADDRESS BOOK"));
+ print_text1(" %s\n\n", as.adrbks[as.cur].abnick);
+ lineno = 0L;
+ }
+ }
+ else{ /* print the list of address books */
+ warp_to_beginning();
+ lineno = 0L;
+ }
+
+ for(dl = dlist(lineno);
+ dl->type != End && (!curopen ||
+ (anum==adrbk_num_from_lineno(lineno) &&
+ (as.n_serv == 0 ||
+ ((dlc=get_dlc(lineno)) &&
+ dlc->type != DlcDirDelim1))));
+ dl = dlist(++lineno)){
+
+ switch(dl->type){
+ case Beginning:
+ case ListClickHere:
+ case ClickHereCmb:
+ continue;
+ default:
+ break;
+ }
+
+ p = get_abook_display_line(lineno, 0, NULL, NULL, NULL,
+ lbuf, sizeof(lbuf));
+ print_text1("%s\n", p);
+ }
+
+ as.zoomed = savezoomed;
+ }
+ }
+
+ close_printer();
+
+ /*
+ * jump cache back to where we started so that the next
+ * request won't cause us to page through the whole thing
+ */
+ if(!do_entry)
+ warp_to_dlc(match_dlc, save_line);
+
+ ps_global->mangled_screen = 1;
+ }
+
+ ps_global->mangled_footer = 1;
+ return 1;
+}
+
+
+/*
+ * Delete address book entries.
+ */
+int
+ab_agg_delete(struct pine *ps, int agg)
+{
+ int ret = 0, i, ch, rc = 0;
+ PerAddrBook *pab;
+ adrbk_cntr_t num, ab_count;
+ char prompt[80];
+
+ dprint((2, "- ab_agg_delete -\n"));
+
+ if(agg){
+ snprintf(prompt, sizeof(prompt), _("Really delete %d selected entries"), as.selections);
+ prompt[sizeof(prompt)-1] = '\0';
+ ch = want_to(prompt, 'n', 'n', NO_HELP, WT_NORM);
+ if(ch == 'y'){
+ adrbk_cntr_t newelnum, flushelnum = NO_NEXT;
+ DL_CACHE_S dlc_save, dlc_restart, *dlc;
+ int we_cancel = 0;
+ int top_level_display;
+
+ /*
+ * We want to try to put the cursor in a reasonable position
+ * on the screen when we're done. If we are in the top-level
+ * display, then we can leave it the same. If we have an
+ * addrbook opened, then we want to see if we can get back to
+ * the same entry we are currently on.
+ */
+ if(!(top_level_display = !any_ab_open())){
+ dlc = get_dlc(as.top_ent+as.cur_row);
+ dlc_save = *dlc;
+ newelnum = dlc_save.dlcelnum;
+ }
+
+ we_cancel = busy_cue(NULL, NULL, 1);
+
+ for(i = 0; i < as.n_addrbk && rc != -5; i++){
+ int orig_selected, selected;
+
+ pab = &as.adrbks[i];
+ if(!pab->address_book)
+ continue;
+
+ ab_count = adrbk_count(pab->address_book);
+ rc = 0;
+ selected = howmany_selected(pab->address_book->selects);
+ orig_selected = selected;
+ /*
+ * Because deleting an entry causes the addrbook to be
+ * immediately updated, we need to delete from higher entry
+ * numbers to lower numbers. That way, entry number n is still
+ * entry number n in the updated address book because we've
+ * only deleted entries higher than n.
+ */
+ for(num = ab_count-1; selected > 0 && rc == 0; num--){
+ if(entry_is_selected(pab->address_book->selects,
+ (a_c_arg_t)num)){
+ rc = adrbk_delete(pab->address_book, (a_c_arg_t)num,
+ 1, 0, 0, 0);
+
+ selected--;
+
+ /*
+ * This is just here to help us reposition the cursor.
+ */
+ if(!top_level_display && as.cur == i && rc == 0){
+ if(num >= newelnum)
+ flushelnum = num;
+ else
+ newelnum--;
+ }
+ }
+ }
+
+ if(rc == 0 && orig_selected > 0){
+ int sort_happened = 0;
+
+ rc = adrbk_write(pab->address_book, 0, NULL, &sort_happened, 1, 0);
+ if(sort_happened)
+ ps_global->mangled_screen = 1;
+ }
+
+ if(rc && rc != -5){
+ q_status_message2(SM_ORDER | SM_DING, 3, 5,
+ "Error updating %s: %s",
+ (as.n_addrbk > 1) ? pab->abnick
+ : "address book",
+ error_description(errno));
+ dprint((1, "Error updating %s: %s\n",
+ pab->filename ? pab->filename : "?",
+ error_description(errno)));
+ }
+ }
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ if(rc == 0){
+ q_status_message(SM_ORDER, 0, 2, _("Deletions completed"));
+ ret = 1;
+ erase_selections();
+ }
+
+ if(!top_level_display){
+ int lost = 0;
+ long new_ent;
+
+ if(flushelnum != dlc_save.dlcelnum){
+ /*
+ * We didn't delete current so restart there. The elnum
+ * may have changed if we deleted entries above it.
+ */
+ dlc_restart = dlc_save;
+ dlc_restart.dlcelnum = newelnum;
+ }
+ else{
+ /*
+ * Current was deleted.
+ */
+ dlc_restart.adrbk_num = as.cur;
+ pab = &as.adrbks[as.cur];
+ ab_count = adrbk_count(pab->address_book);
+ if(ab_count == 0)
+ dlc_restart.type = DlcEmpty;
+ else{
+ AdrBk_Entry *abe;
+
+ dlc_restart.dlcelnum = MIN(newelnum, ab_count-1);
+ abe = adrbk_get_ae(pab->address_book,
+ (a_c_arg_t) dlc_restart.dlcelnum);
+ if(abe && abe->tag == Single)
+ dlc_restart.type = DlcSimple;
+ else if(abe && abe->tag == List)
+ dlc_restart.type = DlcListHead;
+ else
+ lost++;
+ }
+ }
+
+ if(lost){
+ warp_to_top_of_abook(as.cur);
+ as.top_ent = 0L;
+ new_ent = first_selectable_line(0L);
+ if(new_ent == NO_LINE)
+ as.cur_row = 0L;
+ else
+ as.cur_row = new_ent;
+
+ /* if it is off screen */
+ if(as.cur_row >= as.l_p_page){
+ as.top_ent += (as.cur_row - as.l_p_page + 1);
+ as.cur_row = (as.l_p_page - 1);
+ }
+ }
+ else if(dlc_restart.type != DlcEmpty &&
+ dlc_restart.dlcelnum == dlc_save.dlcelnum &&
+ (F_OFF(F_CMBND_ABOOK_DISP,ps_global) || as.cur == 0)){
+ /*
+ * Didn't delete any before this line.
+ * Leave screen about the same. (May have deleted current.)
+ */
+ warp_to_dlc(&dlc_restart, as.cur_row+as.top_ent);
+ }
+ else{
+ warp_to_dlc(&dlc_restart, 0L);
+ /* put in middle of screen */
+ as.top_ent = first_line(0L - (long)as.l_p_page/2L);
+ as.cur_row = 0L - as.top_ent;
+ }
+
+ ps->mangled_body = 1;
+ }
+ }
+ else
+ cmd_cancelled("Apply Delete command");
+ }
+
+ return(ret);
+}
+
+
+/*
+ * Delete an entry from the address book
+ *
+ * Args: abook -- The addrbook handle into access library
+ * command_line -- The screen line on which to prompt
+ * cur_line -- The entry number in the display list
+ * warped -- We warped to a new part of the addrbook
+ *
+ * Result: returns 1 if an entry was deleted, 0 if not.
+ *
+ * The main routine above knows what to repaint because it's always the
+ * current entry that's deleted. Here confirmation is asked of the user
+ * and the appropriate adrbklib functions are called.
+ */
+int
+single_entry_delete(AdrBk *abook, long int cur_line, int *warped)
+{
+ char ch, *cmd, *dname;
+ char prompt[200];
+ int rc;
+ register AddrScrn_Disp *dl;
+ AdrBk_Entry *abe;
+ DL_CACHE_S *dlc_to_flush;
+
+ dprint((2, "- single_entry_delete -\n"));
+
+ if(warped)
+ *warped = 0;
+
+ dl = dlist(cur_line);
+ abe = adrbk_get_ae(abook, (a_c_arg_t) dl->elnum);
+
+ switch(dl->type){
+ case Simple:
+ dname = (abe->fullname && abe->fullname[0])
+ ? (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, abe->fullname)
+ : abe->nickname ? abe->nickname : "";
+ cmd = _("Really delete \"%s\"");
+ break;
+
+ case ListHead:
+ dname = (abe->fullname && abe->fullname[0])
+ ? (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, abe->fullname)
+ : abe->nickname ? abe->nickname : "";
+ cmd = _("Really delete ENTIRE list \"%s\"");
+ break;
+
+ case ListEnt:
+ dname = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, listmem_from_dl(abook, dl));
+ cmd = _("Really delete \"%s\" from list");
+ break;
+
+ default:
+ break;
+ }
+
+ dname = dname ? dname : "";
+ cmd = cmd ? cmd : "";
+
+ snprintf(prompt, sizeof(prompt), cmd, dname);
+ prompt[sizeof(prompt)-1] = '\0';
+ ch = want_to(prompt, 'n', 'n', NO_HELP, WT_NORM);
+ if(ch == 'y'){
+ dlc_to_flush = get_dlc(cur_line);
+ if(dl->type == Simple || dl->type == ListHead){
+ /*--- Kill a single entry or an entire list ---*/
+ rc = adrbk_delete(abook, (a_c_arg_t)dl->elnum, 1, 1, 0, 1);
+ }
+ else if(listmem_count_from_abe(abe) > 2){
+ /*---- Kill an entry out of a list ----*/
+ rc = adrbk_listdel(abook, (a_c_arg_t)dl->elnum,
+ listmem_from_dl(abook, dl));
+ }
+ else{
+ char *nick, *full, *addr, *fcc, *comment;
+ adrbk_cntr_t new_entry_num = NO_NEXT;
+
+ /*---- Convert a List to a Single entry ----*/
+
+ /* Save old info to be transferred */
+ nick = cpystr(abe->nickname);
+ full = cpystr(abe->fullname);
+ fcc = cpystr(abe->fcc);
+ comment = cpystr(abe->extra);
+ if(listmem_count_from_abe(abe) == 2)
+ addr = cpystr(abe->addr.list[1 - dl->l_offset]);
+ else
+ addr = cpystr("");
+
+ rc = adrbk_delete(abook, (a_c_arg_t)dl->elnum, 0, 1, 0, 0);
+ if(rc == 0)
+ adrbk_add(abook,
+ NO_NEXT,
+ nick,
+ full,
+ addr,
+ fcc,
+ comment,
+ Single,
+ &new_entry_num,
+ (int *)NULL,
+ 1,
+ 0,
+ 1);
+
+ fs_give((void **)&nick);
+ fs_give((void **)&full);
+ fs_give((void **)&fcc);
+ fs_give((void **)&comment);
+ fs_give((void **)&addr);
+
+ if(rc == 0){
+ DL_CACHE_S dlc_restart;
+
+ dlc_restart.adrbk_num = as.cur;
+ dlc_restart.dlcelnum = new_entry_num;
+ dlc_restart.type = DlcSimple;
+ warp_to_dlc(&dlc_restart, 0L);
+ *warped = 1;
+ return 1;
+ }
+ }
+
+ if(rc == 0){
+ q_status_message(SM_ORDER, 0, 3,
+ _("Entry deleted, address book updated"));
+ dprint((5, "abook: Entry %s\n",
+ (dl->type == Simple || dl->type == ListHead) ? "deleted"
+ : "modified"));
+ /*
+ * Remove deleted line and everything after it from
+ * the dlc cache. Next time we try to access those lines they
+ * will get filled in with the right info.
+ */
+ flush_dlc_from_cache(dlc_to_flush);
+ return 1;
+ }
+ else{
+ PerAddrBook *pab;
+
+ if(rc != -5)
+ q_status_message1(SM_ORDER | SM_DING, 3, 5,
+ _("Error updating address book: %s"),
+ error_description(errno));
+ pab = &as.adrbks[as.cur];
+ dprint((1, "Error deleting entry from %s (%s): %s\n",
+ pab->abnick ? pab->abnick : "?",
+ pab->filename ? pab->filename : "?",
+ error_description(errno)));
+ }
+
+ return 0;
+ }
+ else{
+ q_status_message(SM_INFO, 0, 2, _("Entry not deleted"));
+ return 0;
+ }
+}
+
+
+void
+free_headents(struct headerentry **head)
+{
+ struct headerentry *he;
+ PrivateTop *pt;
+
+ if(head && *head){
+ for(he = *head; he->name; he++)
+ if(he->bldr_private){
+ pt = (PrivateTop *)he->bldr_private;
+ free_privatetop(&pt);
+ }
+
+ fs_give((void **)head);
+ }
+}
+
+
+#ifdef ENABLE_LDAP
+/*
+prompt::name::help::prwid::maxlen::realaddr::
+builder::affected_entry::next_affected::selector::key_label::fileedit::
+display_it::break_on_comma::is_attach::rich_header::only_file_chars::
+single_space::sticky::dirty::start_here::blank::KS_ODATAVAR
+*/
+static struct headerentry headents_for_query[]={
+ {"Normal Search : ", "NormalSearch", h_composer_qserv_qq, 16, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"Name : ", "Name", h_composer_qserv_cn, 16, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"Surname : ", "SurName", h_composer_qserv_sn, 16, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"Given Name : ", "GivenName", h_composer_qserv_gn, 16, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"Email Address : ", "EmailAddress", h_composer_qserv_mail, 16, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"Organization : ", "Organization", h_composer_qserv_org, 16, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"Org Unit : ", "OrganizationalUnit", h_composer_qserv_unit, 16, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"Country : ", "Country", h_composer_qserv_country, 16, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"State : ", "State", h_composer_qserv_state, 16, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"Locality : ", "Locality", h_composer_qserv_locality, 16, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {" ", "BlankLine", NO_HELP, 1, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, KS_NONE},
+ {"Custom Filter : ", "CustomFilter", h_composer_qserv_custom, 16, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {NULL, NULL, NO_HELP, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}
+};
+#define QQ_QQ 0
+#define QQ_CN 1
+#define QQ_SN 2
+#define QQ_GN 3
+#define QQ_MAIL 4
+#define QQ_ORG 5
+#define QQ_UNIT 6
+#define QQ_COUNTRY 7
+#define QQ_STATE 8
+#define QQ_LOCALITY 9
+#define QQ_BLANK 10
+#define QQ_CUSTOM 11
+#define QQ_END 12
+
+static SAVED_QUERY_S *saved_params;
+
+
+/*
+ * Formulate a query for LDAP servers and send it and view it.
+ * This is called from the Address Book screen, either from ^T from the
+ * composer (selecting) or just when browsing in the address book screen
+ * (selecting == 0).
+ *
+ * Args ps -- Pine struct
+ * selecting -- This is set if we're just selecting an address, as opposed
+ * to browsing on an LDAP server
+ * who -- Tells us which server to query
+ * error -- An error message allocated here and freed by caller.
+ *
+ * Returns -- Null if not selecting, possibly an address if selecting.
+ * The address is 1522 decoded and should be freed by the caller.
+ */
+char *
+query_server(struct pine *ps, int selecting, int *exit, int who, char **error)
+{
+ struct headerentry *he = NULL;
+ PICO pbf;
+ STORE_S *msgso = NULL;
+ int i, lret, editor_result;
+ int r = 4, flags;
+ HelpType help = NO_HELP;
+#define FILTSIZE 1000
+ char fbuf[FILTSIZE+1];
+ char *ret = NULL;
+ LDAP_CHOOSE_S *winning_e = NULL;
+ LDAP_SERV_RES_S *free_when_done = NULL;
+ SAVED_QUERY_S *sq = NULL;
+ static ESCKEY_S ekey[] = {
+ /* TRANSLATORS: go to more complex search screen */
+ {ctrl('T'), 10, "^T", N_("To complex search")},
+ {-1, 0, NULL, NULL}
+ };
+
+ dprint((2, "- query_server(%s) -\n", selecting?"Selecting":""));
+
+ if(!(ps->VAR_LDAP_SERVERS && ps->VAR_LDAP_SERVERS[0] &&
+ ps->VAR_LDAP_SERVERS[0][0])){
+ if(error)
+ *error = cpystr(_("No LDAP server available for lookup"));
+
+ return(ret);
+ }
+
+ fbuf[0] = '\0';
+ flags = OE_APPEND_CURRENT | OE_KEEP_TRAILING_SPACE;
+ while(r == 4 || r == 3){
+ /* TRANSLATORS: we're asking for a character string to search for */
+ r = optionally_enter(fbuf, -FOOTER_ROWS(ps), 0, sizeof(fbuf),
+ _("String to search for : "),
+ ekey, help, &flags);
+ if(r == 3)
+ help = help == NO_HELP ? h_dir_comp_search : NO_HELP;
+ }
+
+ /* strip quotes that user typed by mistake */
+ (void)removing_double_quotes(fbuf);
+
+ if(r == 1 || (r != 10 && fbuf[0] == '\0')){
+ ps->mangled_footer = 1;
+ if(error)
+ *error = cpystr(_("Cancelled"));
+
+ return(ret);
+ }
+
+ editor_result = COMP_EXIT; /* just to get right logic below */
+
+ memset((void *)&pbf, 0, sizeof(pbf));
+
+ if(r == 10){
+ standard_picobuf_setup(&pbf);
+ pbf.exittest = pico_simpleexit;
+ pbf.exit_label = _("Search");
+ pbf.canceltest = pico_simplecancel;
+ if(saved_params){
+ pbf.expander = restore_query_parameters;
+ pbf.ctrlr_label = _("Restore");
+ }
+
+ pbf.pine_anchor = set_titlebar(_("SEARCH DIRECTORY SERVER"),
+ ps_global->mail_stream,
+ ps_global->context_current,
+ ps_global->cur_folder,
+ ps_global->msgmap,
+ 0, FolderName, 0, 0, NULL);
+ pbf.pine_flags |= P_NOBODY;
+
+ /* An informational message */
+ if((msgso = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
+ pbf.msgtext = (void *)so_text(msgso);
+ /*
+ * It's nice if we can make it so these lines make sense even if
+ * they don't all make it on the screen, because the user can't
+ * scroll down to see them. So just make each line a whole sentence
+ * that doesn't need the others below it to make sense.
+ */
+ so_puts(msgso,
+_("\n Fill in some of the fields above to create a query."));
+ so_puts(msgso,
+_("\n The match will be for the exact string unless you include wildcards (*)."));
+ so_puts(msgso,
+_("\n All filled-in fields must match in order to be counted as a match."));
+ so_puts(msgso,
+_("\n Press \"^R\" to restore previous query values (if you've queried previously)."));
+ so_puts(msgso,
+_("\n \"^G\" for help specific to each item. \"^X\" to make the query, or \"^C\" to cancel."));
+ }
+
+ he = (struct headerentry *)fs_get((QQ_END+1) *
+ sizeof(struct headerentry));
+ memset((void *)he, 0, (QQ_END+1) * sizeof(struct headerentry));
+ for(i = QQ_QQ; i <= QQ_END; i++)
+ he[i] = headents_for_query[i];
+
+ pbf.headents = he;
+
+ sq = copy_query_parameters(NULL);
+ he[QQ_QQ].realaddr = &sq->qq;
+ he[QQ_CN].realaddr = &sq->cn;
+ he[QQ_SN].realaddr = &sq->sn;
+ he[QQ_GN].realaddr = &sq->gn;
+ he[QQ_MAIL].realaddr = &sq->mail;
+ he[QQ_ORG].realaddr = &sq->org;
+ he[QQ_UNIT].realaddr = &sq->unit;
+ he[QQ_COUNTRY].realaddr = &sq->country;
+ he[QQ_STATE].realaddr = &sq->state;
+ he[QQ_LOCALITY].realaddr = &sq->locality;
+ he[QQ_CUSTOM].realaddr = &sq->custom;
+
+ /* pass to pico and let user set them */
+ editor_result = pico(&pbf);
+ ps->mangled_screen = 1;
+ standard_picobuf_teardown(&pbf);
+
+ if(editor_result & COMP_GOTHUP)
+ hup_signal();
+ else{
+ fix_windsize(ps_global);
+ init_signals();
+ }
+ }
+
+ if(editor_result & COMP_EXIT &&
+ ((r == 0 && *fbuf) ||
+ (r == 10 && sq &&
+ (*sq->qq || *sq->cn || *sq->sn || *sq->gn || *sq->mail ||
+ *sq->org || *sq->unit || *sq->country || *sq->state ||
+ *sq->locality || *sq->custom)))){
+ LDAPLookupStyle style;
+ WP_ERR_S wp_err;
+ int need_and, mangled;
+ char *string;
+ CUSTOM_FILT_S *filter;
+ SAVED_QUERY_S *s;
+
+ s = copy_query_parameters(sq);
+ save_query_parameters(s);
+
+ if(r == 0){
+ string = fbuf;
+ filter = NULL;
+ }
+ else{
+ int categories = 0;
+
+ categories = ((*sq->cn != '\0') ? 1 : 0) +
+ ((*sq->sn != '\0') ? 1 : 0) +
+ ((*sq->gn != '\0') ? 1 : 0) +
+ ((*sq->mail != '\0') ? 1 : 0) +
+ ((*sq->org != '\0') ? 1 : 0) +
+ ((*sq->unit != '\0') ? 1 : 0) +
+ ((*sq->country != '\0') ? 1 : 0) +
+ ((*sq->state != '\0') ? 1 : 0) +
+ ((*sq->locality != '\0') ? 1 : 0);
+ need_and = (categories > 1);
+
+ if((sq->cn ? strlen(sq->cn) : 0 +
+ sq->sn ? strlen(sq->sn) : 0 +
+ sq->gn ? strlen(sq->gn) : 0 +
+ sq->mail ? strlen(sq->mail) : 0 +
+ sq->org ? strlen(sq->org) : 0 +
+ sq->unit ? strlen(sq->unit) : 0 +
+ sq->country ? strlen(sq->country) : 0 +
+ sq->state ? strlen(sq->state) : 0 +
+ sq->locality ? strlen(sq->locality) : 0) > FILTSIZE - 100){
+ if(error)
+ *error = cpystr(_("Search strings too long"));
+
+ goto all_done;
+ }
+
+ if(categories > 0){
+
+ snprintf(fbuf, sizeof(fbuf),
+ "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ need_and ? "(&" : "",
+ *sq->cn ? "(cn=" : "",
+ *sq->cn ? sq->cn : "",
+ *sq->cn ? ")" : "",
+ *sq->sn ? "(sn=" : "",
+ *sq->sn ? sq->sn : "",
+ *sq->sn ? ")" : "",
+ *sq->gn ? "(givenname=" : "",
+ *sq->gn ? sq->gn : "",
+ *sq->gn ? ")" : "",
+ *sq->mail ? "(mail=" : "",
+ *sq->mail ? sq->mail : "",
+ *sq->mail ? ")" : "",
+ *sq->org ? "(o=" : "",
+ *sq->org ? sq->org : "",
+ *sq->org ? ")" : "",
+ *sq->unit ? "(ou=" : "",
+ *sq->unit ? sq->unit : "",
+ *sq->unit ? ")" : "",
+ *sq->country ? "(c=" : "",
+ *sq->country ? sq->country : "",
+ *sq->country ? ")" : "",
+ *sq->state ? "(st=" : "",
+ *sq->state ? sq->state : "",
+ *sq->state ? ")" : "",
+ *sq->locality ? "(l=" : "",
+ *sq->locality ? sq->locality : "",
+ *sq->locality ? ")" : "",
+ need_and ? ")" : "");
+ fbuf[sizeof(fbuf)-1] = '\0';
+ }
+
+ if(categories > 0 || *sq->custom)
+ filter = (CUSTOM_FILT_S *)fs_get(sizeof(CUSTOM_FILT_S));
+
+ /* combine the configed filters with this filter */
+ if(*sq->custom){
+ string = "";
+ filter->filt = sq->custom;
+ filter->combine = 0;
+ }
+ else if(*sq->qq && categories > 0){
+ string = sq->qq;
+ filter->filt = fbuf;
+ filter->combine = 1;
+ }
+ else if(categories > 0){
+ string = "";
+ filter->filt = fbuf;
+ filter->combine = 0;
+ }
+ else{
+ string = sq->qq;
+ filter = NULL;
+ }
+ }
+
+ mangled = 0;
+ memset(&wp_err, 0, sizeof(wp_err));
+ wp_err.mangled = &mangled;
+ style = selecting ? AlwaysDisplayAndMailRequired : AlwaysDisplay;
+
+ /* maybe coming from composer */
+ fix_windsize(ps_global);
+ init_sigwinch();
+ clear_cursor_pos();
+
+ lret = ldap_lookup_all(string, who, 0, style, filter, &winning_e,
+ &wp_err, &free_when_done);
+
+ if(filter)
+ fs_give((void **)&filter);
+
+ if(wp_err.mangled)
+ ps->mangled_screen = 1;
+
+ if(wp_err.error){
+ if(status_message_remaining() && error)
+ *error = wp_err.error;
+ else
+ fs_give((void **)&wp_err.error);
+ }
+
+ if(lret == 0 && winning_e && selecting){
+ ADDRESS *addr;
+
+ addr = address_from_ldap(winning_e);
+ if(addr){
+ if(!addr->host){
+ addr->host = cpystr("missing-hostname");
+ if(error){
+ if(*error)
+ fs_give((void **)error);
+
+ *error = cpystr(_("Missing hostname in LDAP address"));
+ }
+ }
+
+ ret = addr_list_string(addr, NULL, 1);
+ if(!ret || !ret[0]){
+ if(ret)
+ fs_give((void **)&ret);
+
+ if(exit)
+ *exit = 1;
+
+ if(error && !*error){
+ char buf[200];
+
+ snprintf(buf, sizeof(buf), _("No email address available for \"%s\""),
+ (addr->personal && *addr->personal)
+ ? addr->personal
+ : "selected entry");
+ buf[sizeof(buf)-1] = '\0';
+ *error = cpystr(buf);
+ }
+ }
+
+ mail_free_address(&addr);
+ }
+ }
+ else if(lret == -1 && exit)
+ *exit = 1;
+ }
+
+all_done:
+ if(he)
+ free_headents(&he);
+
+ if(free_when_done)
+ free_ldap_result_list(&free_when_done);
+
+ if(winning_e)
+ fs_give((void **)&winning_e);
+
+ if(msgso)
+ so_give(&msgso);
+
+ if(sq)
+ free_query_parameters(&sq);
+
+ return(ret);
+}
+
+
+/*
+ * View all fields of an LDAP entry while browsing.
+ *
+ * Args ps -- Pine struct
+ * winning_e -- The struct containing the information about the entry
+ * to be viewed.
+ */
+void
+view_ldap_entry(struct pine *ps, LDAP_CHOOSE_S *winning_e)
+{
+ STORE_S *srcstore = NULL;
+ SourceType srctype = CharStar;
+ SCROLL_S sargs;
+ HANDLE_S *handles = NULL;
+
+ dprint((9, "- view_ldap_entry -\n"));
+
+ if((srcstore=prep_ldap_for_viewing(ps,winning_e,srctype,&handles)) != NULL){
+ memset(&sargs, 0, sizeof(SCROLL_S));
+ sargs.text.text = so_text(srcstore);
+ sargs.text.src = srctype;
+ sargs.text.desc = _("expanded entry");
+ sargs.text.handles= handles;
+ sargs.bar.title = _("DIRECTORY ENTRY");
+ sargs.proc.tool = process_ldap_cmd;
+ sargs.proc.data.p = (void *) winning_e;
+ sargs.help.text = h_ldap_view;
+ sargs.help.title = _("HELP FOR DIRECTORY VIEW");
+ sargs.keys.menu = &ldap_view_keymenu;
+ setbitmap(sargs.keys.bitmap);
+
+ if(handles)
+ sargs.keys.menu->how_many = 2;
+ else{
+ sargs.keys.menu->how_many = 1;
+ clrbitn(OTHER_KEY, sargs.keys.bitmap);
+ }
+
+ scrolltool(&sargs);
+
+ ps->mangled_screen = 1;
+ so_give(&srcstore);
+ free_handles(&handles);
+ }
+ else
+ q_status_message(SM_ORDER, 0, 2, _("Error allocating space"));
+}
+
+
+/*
+ * Compose a message to the email addresses contained in an LDAP entry.
+ *
+ * Args ps -- Pine struct
+ * winning_e -- The struct containing the information about the entry
+ * to be viewed.
+ */
+void
+compose_to_ldap_entry(struct pine *ps, LDAP_CHOOSE_S *e, int allow_role)
+{
+ char **elecmail = NULL,
+ **mail = NULL,
+ **cn = NULL,
+ **sn = NULL,
+ **givenname = NULL,
+ **ll;
+ size_t len = 0;
+
+ dprint((9, "- compose_to_ldap_entry -\n"));
+
+ if(e){
+ char *a;
+ BerElement *ber;
+
+ for(a = ldap_first_attribute(e->ld, e->selected_entry, &ber);
+ a != NULL;
+ a = ldap_next_attribute(e->ld, e->selected_entry, ber)){
+
+ if(strcmp(a, e->info_used->mailattr) == 0){
+ if(!mail)
+ mail = ldap_get_values(e->ld, e->selected_entry, a);
+ }
+ else if(strcmp(a, "electronicmail") == 0){
+ if(!elecmail)
+ elecmail = ldap_get_values(e->ld, e->selected_entry, a);
+ }
+ else if(strcmp(a, e->info_used->cnattr) == 0){
+ if(!cn)
+ cn = ldap_get_values(e->ld, e->selected_entry, a);
+ }
+ else if(strcmp(a, e->info_used->gnattr) == 0){
+ if(!givenname)
+ givenname = ldap_get_values(e->ld, e->selected_entry, a);
+ }
+ else if(strcmp(a, e->info_used->snattr) == 0){
+ if(!sn)
+ sn = ldap_get_values(e->ld, e->selected_entry, a);
+ }
+
+ our_ldap_memfree(a);
+ }
+ }
+
+ if(elecmail){
+ if(elecmail[0] && elecmail[0][0] && !mail)
+ mail = elecmail;
+ else
+ ldap_value_free(elecmail);
+
+ elecmail = NULL;
+ }
+
+ for(ll = mail; ll && *ll; ll++)
+ len += strlen(*ll) + 1;
+
+ if(len){
+ char *p, *address, *fn = NULL;
+ BuildTo bldto;
+ SAVE_STATE_S state;
+
+ address = (char *)fs_get(len * sizeof(char));
+ p = address;
+ ll = mail;
+ while(*ll){
+ sstrncpy(&p, *ll, len-(p-address));
+ ll++;
+ if(*ll)
+ sstrncpy(&p, ",", len-(p-address));
+ }
+
+ address[len-1] = '\0';
+
+ /*
+ * If we have a fullname and there is only a single address and
+ * the address doesn't seem to have a fullname with it, add it.
+ */
+ if(mail && mail[0] && mail[0][0] && !mail[1]){
+ if(cn && cn[0] && cn[0][0])
+ fn = cpystr(cn[0]);
+ else if(sn && sn[0] && sn[0][0] &&
+ givenname && givenname[0] && givenname[0][0]){
+ size_t l;
+
+ l = strlen(givenname[0]) + strlen(sn[0]) + 1;
+ fn = (char *) fs_get((l+1) * sizeof(char));
+ snprintf(fn, l+1, "%s %s", givenname[0], sn[0]);
+ fn[l] = '\0';
+ }
+ }
+
+ if(mail && mail[0] && mail[0][0] && !mail[1] && fn){
+ ADDRESS *adrlist = NULL;
+ char *tmp_a_string;
+
+ tmp_a_string = cpystr(address);
+ rfc822_parse_adrlist(&adrlist, tmp_a_string, fakedomain);
+ fs_give((void **)&tmp_a_string);
+ if(adrlist && !adrlist->next && !adrlist->personal){
+ char *new_address;
+ size_t len;
+ RFC822BUFFER rbuf;
+
+ adrlist->personal = cpystr(fn);
+ len = est_size(adrlist);
+ new_address = (char *) fs_get(len * sizeof(char));
+ new_address[0] ='\0';
+ rbuf.f = dummy_soutr;
+ rbuf.s = NULL;
+ rbuf.beg = new_address;
+ rbuf.cur = new_address;
+ rbuf.end = new_address+len-1;
+ /* this will quote it if it needs quoting */
+ rfc822_output_address_list(&rbuf, adrlist, 0L, NULL);
+ *rbuf.cur = '\0';
+ fs_give((void **)&address);
+ address = new_address;
+ }
+
+ if(adrlist)
+ mail_free_address(&adrlist);
+ }
+
+ bldto.type = Str;
+ bldto.arg.str = address;
+
+ save_state(&state);
+ ab_compose_internal(bldto, allow_role);
+ restore_state(&state);
+ fs_give((void **)&address);
+ if(fn)
+ fs_give((void **)&fn);
+
+ /*
+ * Window size may have changed in composer.
+ * Pine_send will have reset the window size correctly,
+ * but we still have to reset our address book data structures.
+ */
+ if(as.initialized)
+ ab_resize();
+
+ ps_global->mangled_screen = 1;
+ }
+ else
+ q_status_message(SM_ORDER, 0, 4, _("No address to compose to"));
+
+ if(mail)
+ ldap_value_free(mail);
+ if(cn)
+ ldap_value_free(cn);
+ if(sn)
+ ldap_value_free(sn);
+ if(givenname)
+ ldap_value_free(givenname);
+}
+
+
+/*
+ * Forward the text of an LDAP entry via email (not a vcard attachment)
+ *
+ * Args ps -- Pine struct
+ * winning_e -- The struct containing the information about the entry
+ * to be viewed.
+ */
+void
+forward_ldap_entry(struct pine *ps, LDAP_CHOOSE_S *winning_e)
+{
+ STORE_S *srcstore = NULL;
+ SourceType srctype = CharStar;
+
+ dprint((9, "- forward_ldap_entry -\n"));
+
+ if((srcstore = prep_ldap_for_viewing(ps,winning_e,srctype,NULL)) != NULL){
+ forward_text(ps, so_text(srcstore), srctype);
+ ps->mangled_screen = 1;
+ so_give(&srcstore);
+ }
+ else
+ q_status_message(SM_ORDER, 0, 2, _("Error allocating space"));
+}
+
+
+STORE_S *
+prep_ldap_for_viewing(struct pine *ps, LDAP_CHOOSE_S *winning_e, SourceType srctype, HANDLE_S **handlesp)
+{
+ STORE_S *store = NULL;
+ char *a, *tmp;
+ BerElement *ber;
+ int i, width;
+#define W (1000)
+#define INDENTHERE (22)
+ char obuf[W+10];
+ char hdr[6*INDENTHERE+1], hdr2[6*INDENTHERE+1];
+ char **cn = NULL;
+ int indent = INDENTHERE;
+
+ if(!(store = so_get(srctype, NULL, EDIT_ACCESS)))
+ return(store);
+
+ /* for mailto handles so user can select individual email addrs */
+ if(handlesp)
+ init_handles(handlesp);
+
+ width = MAX(ps->ttyo->screen_cols, 25);
+
+ snprintf(hdr2, sizeof(hdr2), "%-*.*s: ", indent-2,indent-2, "");
+ hdr2[sizeof(hdr2)-1] = '\0';
+
+ if(sizeof(obuf) > ps->ttyo->screen_cols+1){
+ memset((void *)obuf, '-', ps->ttyo->screen_cols * sizeof(char));
+ obuf[ps->ttyo->screen_cols] = '\n';
+ obuf[ps->ttyo->screen_cols+1] = '\0';
+ }
+
+ a = ldap_get_dn(winning_e->ld, winning_e->selected_entry);
+
+ so_puts(store, obuf);
+ if((tmp = fold(a, width, width, "", " ", FLD_NONE)) != NULL){
+ so_puts(store, tmp);
+ fs_give((void **)&tmp);
+ }
+
+ so_puts(store, obuf);
+ so_puts(store, "\n");
+
+ our_ldap_dn_memfree(a);
+
+ for(a = ldap_first_attribute(winning_e->ld, winning_e->selected_entry, &ber);
+ a != NULL;
+ a = ldap_next_attribute(winning_e->ld, winning_e->selected_entry, ber)){
+
+ if(a && *a){
+ char **vals;
+ char *fn = NULL;
+
+ vals = ldap_get_values(winning_e->ld, winning_e->selected_entry, a);
+
+ /* save this for mailto */
+ if(handlesp && !cn && !strcmp(a, winning_e->info_used->cnattr))
+ cn = ldap_get_values(winning_e->ld, winning_e->selected_entry, a);
+
+ if(vals){
+ int do_mailto;
+
+ do_mailto = (handlesp &&
+ !strcmp(a, winning_e->info_used->mailattr));
+
+ utf8_snprintf(hdr, sizeof(hdr), "%-*.*w: ", indent-2,indent-2,
+ ldap_translate(a, winning_e->info_used));
+ hdr[sizeof(hdr)-1] = '\0';
+ for(i = 0; vals[i] != NULL; i++){
+ if(do_mailto){
+ ADDRESS *ad = NULL;
+ HANDLE_S *h;
+ char buf[20];
+ char *tmp_a_string;
+ char *addr, *new_addr, *enc_addr;
+ char *path = NULL;
+
+ addr = cpystr(vals[i]);
+ if(cn && cn[0] && cn[0][0])
+ fn = cpystr(cn[0]);
+
+ if(fn){
+ tmp_a_string = cpystr(addr);
+ rfc822_parse_adrlist(&ad, tmp_a_string, "@");
+ fs_give((void **)&tmp_a_string);
+ if(ad && !ad->next && !ad->personal){
+ RFC822BUFFER rbuf;
+ size_t len;
+
+ ad->personal = cpystr(fn);
+ len = est_size(ad);
+ new_addr = (char *) fs_get(len * sizeof(char));
+ new_addr[0] = '\0';
+ /* this will quote it if it needs quoting */
+ rbuf.f = dummy_soutr;
+ rbuf.s = NULL;
+ rbuf.beg = new_addr;
+ rbuf.cur = new_addr;
+ rbuf.end = new_addr+len-1;
+ rfc822_output_address_list(&rbuf, ad, 0L, NULL);
+ *rbuf.cur = '\0';
+ fs_give((void **) &addr);
+ addr = new_addr;
+ }
+
+ if(ad)
+ mail_free_address(&ad);
+
+ fs_give((void **)&fn);
+ }
+
+ if((enc_addr = rfc1738_encode_mailto(addr)) != NULL){
+ size_t l;
+
+ l = strlen(enc_addr) + 7;
+ path = (char *) fs_get((l+1) * sizeof(char));
+ snprintf(path, l+1, "mailto:%s", enc_addr);
+ path[l] = '\0';
+ fs_give((void **)&enc_addr);
+ }
+
+ fs_give((void **)&addr);
+
+ if(path){
+ h = new_handle(handlesp);
+ h->type = URL;
+ h->h.url.path = path;
+ snprintf(buf, sizeof(buf), "%d", h->key);
+ buf[sizeof(buf)-1] = '\0';
+
+ /*
+ * Don't try to fold this address. Just put it on
+ * one line and let scrolltool worry about
+ * cutting it off before it goes past the
+ * right hand edge. Otherwise, we have to figure
+ * out how to handle the wrapped handle.
+ */
+ snprintf(obuf, sizeof(obuf), "%c%c%c%s%s%c%c%c%c",
+ TAG_EMBED, TAG_HANDLE,
+ strlen(buf), buf, vals[i],
+ TAG_EMBED, TAG_BOLDOFF,
+ TAG_EMBED, TAG_INVOFF);
+ obuf[sizeof(obuf)-1] = '\0';
+
+ so_puts(store, (i==0) ? hdr : hdr2);
+ so_puts(store, obuf);
+ so_puts(store, "\n");
+ }
+ else{
+ snprintf(obuf, sizeof(obuf), "%s", vals[i]);
+ obuf[sizeof(obuf)-1] = '\0';
+
+ if((tmp = fold(obuf, width, width,
+ (i==0) ? hdr : hdr2,
+ repeat_char(indent+2, SPACE),
+ FLD_NONE)) != NULL){
+ so_puts(store, tmp);
+ fs_give((void **)&tmp);
+ }
+ }
+ }
+ else{
+ snprintf(obuf, sizeof(obuf), "%s", vals[i]);
+ obuf[sizeof(obuf)-1] = '\0';
+
+ if((tmp = fold(obuf, width, width,
+ (i==0) ? hdr : hdr2,
+ repeat_char(indent+2, SPACE),
+ FLD_NONE)) != NULL){
+ so_puts(store, tmp);
+ fs_give((void **)&tmp);
+ }
+ }
+ }
+
+ ldap_value_free(vals);
+ }
+ else{
+ utf8_snprintf(obuf, sizeof(obuf), "%-*.*w\n", indent-1,indent-1,
+ ldap_translate(a, winning_e->info_used));
+ obuf[sizeof(obuf)-1] = '\0';
+ so_puts(store, obuf);
+ }
+ }
+
+ our_ldap_memfree(a);
+ }
+
+ if(cn)
+ ldap_value_free(cn);
+
+ return(store);
+}
+
+
+int
+process_ldap_cmd(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
+{
+ int rv = 1;
+
+ ps_global->next_screen = SCREEN_FUN_NULL;
+
+ switch(cmd){
+ case MC_SAVE :
+ save_ldap_entry(ps_global, sparms->proc.data.p, 0);
+ rv = 0;
+ break;
+
+ case MC_COMPOSE :
+ compose_to_ldap_entry(ps_global, sparms->proc.data.p, 0);
+ rv = 0;
+ break;
+
+ case MC_ROLE :
+ compose_to_ldap_entry(ps_global, sparms->proc.data.p, 1);
+ rv = 0;
+ break;
+
+ default:
+ panic("Unexpected command in process_ldap_cmd");
+ break;
+ }
+
+ return(rv);
+}
+
+
+int
+pico_simpleexit(struct headerentry *he, void (*redraw_pico)(void), int allow_flowed,
+ char **result)
+{
+ if(result)
+ *result = NULL;
+
+ return(0);
+}
+
+char *
+pico_simplecancel(void (*redraw_pico)(void))
+{
+ return("Cancelled");
+}
+
+
+/*
+ * Store query parameters so that they can be recalled by user later with ^R.
+ */
+void
+save_query_parameters(SAVED_QUERY_S *params)
+{
+ free_saved_query_parameters();
+ saved_params = params;
+}
+
+
+SAVED_QUERY_S *
+copy_query_parameters(SAVED_QUERY_S *params)
+{
+ SAVED_QUERY_S *sq;
+
+ sq = (SAVED_QUERY_S *)fs_get(sizeof(SAVED_QUERY_S));
+ memset((void *)sq, 0, sizeof(SAVED_QUERY_S));
+
+ if(params && params->qq)
+ sq->qq = cpystr(params->qq);
+ else
+ sq->qq = cpystr("");
+
+ if(params && params->cn)
+ sq->cn = cpystr(params->cn);
+ else
+ sq->cn = cpystr("");
+
+ if(params && params->sn)
+ sq->sn = cpystr(params->sn);
+ else
+ sq->sn = cpystr("");
+
+ if(params && params->gn)
+ sq->gn = cpystr(params->gn);
+ else
+ sq->gn = cpystr("");
+
+ if(params && params->mail)
+ sq->mail = cpystr(params->mail);
+ else
+ sq->mail = cpystr("");
+
+ if(params && params->org)
+ sq->org = cpystr(params->org);
+ else
+ sq->org = cpystr("");
+
+ if(params && params->unit)
+ sq->unit = cpystr(params->unit);
+ else
+ sq->unit = cpystr("");
+
+ if(params && params->country)
+ sq->country = cpystr(params->country);
+ else
+ sq->country = cpystr("");
+
+ if(params && params->state)
+ sq->state = cpystr(params->state);
+ else
+ sq->state = cpystr("");
+
+ if(params && params->locality)
+ sq->locality = cpystr(params->locality);
+ else
+ sq->locality = cpystr("");
+
+ if(params && params->custom)
+ sq->custom = cpystr(params->custom);
+ else
+ sq->custom = cpystr("");
+
+ return(sq);
+}
+
+
+void
+free_saved_query_parameters(void)
+{
+ if(saved_params)
+ free_query_parameters(&saved_params);
+}
+
+
+void
+free_query_parameters(SAVED_QUERY_S **parm)
+{
+ if(parm){
+ if(*parm){
+ if((*parm)->qq)
+ fs_give((void **)&(*parm)->qq);
+ if((*parm)->cn)
+ fs_give((void **)&(*parm)->cn);
+ if((*parm)->sn)
+ fs_give((void **)&(*parm)->sn);
+ if((*parm)->gn)
+ fs_give((void **)&(*parm)->gn);
+ if((*parm)->mail)
+ fs_give((void **)&(*parm)->mail);
+ if((*parm)->org)
+ fs_give((void **)&(*parm)->org);
+ if((*parm)->unit)
+ fs_give((void **)&(*parm)->unit);
+ if((*parm)->country)
+ fs_give((void **)&(*parm)->country);
+ if((*parm)->state)
+ fs_give((void **)&(*parm)->state);
+ if((*parm)->locality)
+ fs_give((void **)&(*parm)->locality);
+ if((*parm)->custom)
+ fs_give((void **)&(*parm)->custom);
+
+ fs_give((void **)parm);
+ }
+ }
+}
+
+
+/*
+ * A callback from pico to restore the saved query parameters.
+ *
+ * Args he -- Unused.
+ * s -- The place to return the allocated array of values.
+ *
+ * Returns -- 1 if there are parameters to return, 0 otherwise.
+ */
+int
+restore_query_parameters(struct headerentry *he, char ***s)
+{
+ int retval = 0, i = 0;
+
+ if(s)
+ *s = NULL;
+
+ if(saved_params && s){
+ *s = (char **)fs_get((QQ_END + 1) * sizeof(char *));
+ (*s)[i++] = cpystr(saved_params->qq ? saved_params->qq : "");
+ (*s)[i++] = cpystr(saved_params->cn ? saved_params->cn : "");
+ (*s)[i++] = cpystr(saved_params->sn ? saved_params->sn : "");
+ (*s)[i++] = cpystr(saved_params->gn ? saved_params->gn : "");
+ (*s)[i++] = cpystr(saved_params->mail ? saved_params->mail : "");
+ (*s)[i++] = cpystr(saved_params->org ? saved_params->org : "");
+ (*s)[i++] = cpystr(saved_params->unit ? saved_params->unit : "");
+ (*s)[i++] = cpystr(saved_params->country ? saved_params->country : "");
+ (*s)[i++] = cpystr(saved_params->state ? saved_params->state : "");
+ (*s)[i++] = cpystr(saved_params->locality ? saved_params->locality :"");
+ (*s)[i++] = cpystr(saved_params->custom ? saved_params->custom :"");
+ (*s)[i] = 0;
+ retval = 1;
+ }
+
+ return(retval);
+}
+
+
+/*
+ * Internal handler for viewing an LDAP url.
+ */
+int
+url_local_ldap(char *url)
+{
+ LDAP *ld;
+ struct timeval t;
+ int ld_err, mangled = 0, we_cancel, retval = 0, proto = 3;
+ int we_turned_on = 0;
+ char ebuf[300];
+ LDAPMessage *result;
+ LDAP_SERV_S *info;
+ LDAP_SERV_RES_S *serv_res = NULL;
+ LDAPURLDesc *ldapurl = NULL;
+ WP_ERR_S wp_err;
+
+ dprint((2, "url_local_ldap(%s)\n", url ? url : "?"));
+
+ ld_err = ldap_url_parse(url, &ldapurl);
+ if(ld_err || !ldapurl){
+ snprintf(ebuf, sizeof(ebuf), "URL parse failed for %s", url);
+ ebuf[sizeof(ebuf)-1] = '\0';
+ q_status_message(SM_ORDER, 3, 5, ebuf);
+ return(retval);
+ }
+
+ if(!ldapurl->lud_host){
+ /* TRNASLATORS: No host in <url> */
+ snprintf(ebuf, sizeof(ebuf), _("No host in %s"), url);
+ ebuf[sizeof(ebuf)-1] = '\0';
+ q_status_message(SM_ORDER, 3, 5, ebuf);
+ ldap_free_urldesc(ldapurl);
+ return(retval);
+ }
+
+ we_turned_on = intr_handling_on();
+ we_cancel = busy_cue(_("Searching for LDAP url"), NULL, 0);
+ ps_global->mangled_footer = 1;
+
+#if (LDAPAPI >= 11)
+ if((ld = ldap_init(ldapurl->lud_host, ldapurl->lud_port)) == NULL)
+#else
+ if((ld = ldap_open(ldapurl->lud_host, ldapurl->lud_port)) == NULL)
+#endif
+ {
+ if(we_cancel){
+ cancel_busy_cue(-1);
+ we_cancel = 0;
+ }
+
+ q_status_message(SM_ORDER,3,5, _("LDAP search failed: can't initialize"));
+ }
+ else if(!ps_global->intr_pending){
+ if(ldap_v3_is_supported(ld) &&
+ our_ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &proto) == 0){
+ dprint((5, "ldap: using version 3 protocol\n"));
+ }
+
+ /*
+ * If we don't set RESTART then the select() waiting for the answer
+ * in libldap will be interrupted and stopped by our busy_cue.
+ */
+ our_ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
+
+ t.tv_sec = 30; t.tv_usec = 0;
+ ld_err = ldap_search_st(ld, ldapurl->lud_dn, ldapurl->lud_scope,
+ ldapurl->lud_filter, ldapurl->lud_attrs,
+ 0, &t, &result);
+ if(ld_err != LDAP_SUCCESS){
+ if(we_cancel){
+ cancel_busy_cue(-1);
+ we_cancel = 0;
+ }
+
+ snprintf(ebuf, sizeof(ebuf), _("LDAP search failed: %s"), ldap_err2string(ld_err));
+ ebuf[sizeof(ebuf)-1] = '\0';
+ q_status_message(SM_ORDER, 3, 5, ebuf);
+ ldap_unbind(ld);
+ }
+ else if(!ps_global->intr_pending){
+ if(we_cancel){
+ cancel_busy_cue(-1);
+ we_cancel = 0;
+ }
+
+ if(we_turned_on){
+ intr_handling_off();
+ we_turned_on = 0;
+ }
+
+ if(ldap_count_entries(ld, result) == 0){
+ q_status_message(SM_ORDER, 3, 5, _("No matches found for url"));
+ ldap_unbind(ld);
+ if(result)
+ ldap_msgfree(result);
+ }
+ else{
+ serv_res = (LDAP_SERV_RES_S *)fs_get(sizeof(LDAP_SERV_RES_S));
+ memset((void *)serv_res, 0, sizeof(*serv_res));
+ serv_res->ld = ld;
+ serv_res->res = result;
+ info = (LDAP_SERV_S *)fs_get(sizeof(LDAP_SERV_S));
+ memset((void *)info, 0, sizeof(*info));
+ info->mailattr = cpystr(DEF_LDAP_MAILATTR);
+ info->snattr = cpystr(DEF_LDAP_SNATTR);
+ info->gnattr = cpystr(DEF_LDAP_GNATTR);
+ info->cnattr = cpystr(DEF_LDAP_CNATTR);
+ serv_res->info_used = info;
+ memset(&wp_err, 0, sizeof(wp_err));
+ wp_err.mangled = &mangled;
+
+ ask_user_which_entry(serv_res, NULL, NULL, &wp_err, DisplayForURL);
+ if(wp_err.error){
+ q_status_message(SM_ORDER, 3, 5, wp_err.error);
+ fs_give((void **)&wp_err.error);
+ }
+
+ if(mangled)
+ ps_global->mangled_screen = 1;
+
+ free_ldap_result_list(&serv_res);
+ retval = 1;
+ }
+ }
+ }
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ if(we_turned_on)
+ intr_handling_off();
+
+ if(ldapurl)
+ ldap_free_urldesc(ldapurl);
+
+ return(retval);
+}
+#endif /* ENABLE_LDAP */
diff --git a/alpine/adrbkcmd.h b/alpine/adrbkcmd.h
new file mode 100644
index 00000000..3a0b1752
--- /dev/null
+++ b/alpine/adrbkcmd.h
@@ -0,0 +1,64 @@
+/*
+ * $Id: adrbkcmd.h 1012 2008-03-26 00:44:22Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_ADRBKCMD_INCLUDED
+#define PITH_ADRBKCMD_INCLUDED
+
+
+#include "../pith/adrbklib.h"
+#include "../pith/state.h"
+#include "../pith/ldap.h"
+#include "../pith/handle.h"
+#include "../pith/store.h"
+
+
+#define AB_COMMENT_STR _("Comment")
+
+
+/* exported protoypes */
+void view_abook_entry(struct pine *, long);
+void edit_entry(AdrBk *, AdrBk_Entry *, a_c_arg_t, Tag, int, int *, char *);
+int ab_add_abook(int, int);
+int ab_edit_abook(int, int, char *, char *, char *);
+int any_addrbooks_to_convert(struct pine *);
+int convert_addrbooks_to_remote(struct pine *, char *, size_t);
+int any_sigs_to_convert(struct pine *);
+int convert_sigs_to_literal(struct pine *, int);
+void warn_about_rule_files(struct pine *);
+void convert_to_remote_config(struct pine *, int);
+int ab_del_abook(long, int, char **);
+int ab_shuffle(PerAddrBook *, int *, int, char **);
+int ab_compose_to_addr(long, int, int);
+int ab_forward(struct pine *, long, int);
+int ab_save(struct pine *, AdrBk *, long, int, int);
+int ab_print(int);
+int ab_agg_delete(struct pine *, int);
+int single_entry_delete(AdrBk *, long, int *);
+char *query_server(struct pine *, int, int *, int, char **);
+void free_headents(struct headerentry **);
+void write_single_vcard_entry(struct pine *, gf_io_t, VCARD_INFO_S *);
+void free_vcard_info(VCARD_INFO_S **);
+#ifdef ENABLE_LDAP
+void view_ldap_entry(struct pine *, LDAP_CHOOSE_S *);
+void compose_to_ldap_entry(struct pine *, LDAP_CHOOSE_S *,int);
+void forward_ldap_entry(struct pine *, LDAP_CHOOSE_S *);
+STORE_S *prep_ldap_for_viewing(struct pine *, LDAP_CHOOSE_S *, SourceType, HANDLE_S **);
+void free_saved_query_parameters(void);
+int url_local_ldap(char *);
+#endif
+
+
+#endif /* PITH_ADRBKCMD_INCLUDED */
diff --git a/alpine/after.c b/alpine/after.c
new file mode 100644
index 00000000..c2e2af71
--- /dev/null
+++ b/alpine/after.c
@@ -0,0 +1,276 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: after.c 138 2006-09-22 22:12:03Z mikes@u.washington.edu $";
+#endif
+
+/* ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ Implement asynchronous start_after() call
+ ====*/
+
+
+#include <system.h>
+#include <general.h>
+
+#include "../pith/debug.h"
+#include "../pith/osdep/err_desc.h"
+
+#include "../pico/utf8stub.h"
+
+#include "after.h"
+
+
+/* internal state */
+int after_active;
+
+#if defined(HAVE_PTHREAD) && defined(HAVE_NANOSLEEP)
+static pthread_t after_thread;
+static pthread_mutex_t status_message_mutex;
+#endif
+
+
+/* internal prototypes */
+#if defined(HAVE_PTHREAD) && defined(HAVE_NANOSLEEP)
+void *do_after(void *);
+#else
+void *cleanup_data;
+#endif
+
+void cleanup_after(void *);
+
+
+/*
+ * start_after - pause and/or loop calling passed function
+ * without getting in the way of main thread
+ *
+ */
+void
+start_after(AFTER_S *a)
+{
+ if(a){
+#if defined(HAVE_PTHREAD) && defined(HAVE_NANOSLEEP)
+ pthread_attr_t attr;
+ int rc;
+ size_t stack;
+
+ if(after_active)
+ stop_after(1);
+
+ /* Initialize and set thread detached attribute */
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+#if defined(PTHREAD_STACK_MIN)
+ stack = PTHREAD_STACK_MIN + 0x10000;
+ pthread_attr_setstacksize(&attr, stack);
+#endif
+
+ if((rc = pthread_create(&after_thread, &attr, do_after, (void *) a)) != 0){
+ after_active = 0;
+ dprint((1, "start_after: pthread_create failed %d (%d)", rc, errno));
+ }
+ else
+ after_active = 1;
+
+ pthread_attr_destroy(&attr);
+ dprint((9, "start_after() created %x: done", after_thread));
+#else /* !(defined(HAVE_PTHREAD) && defined(HAVE_NANOSLEEP)) */
+ /*
+ * Just call the first function
+ */
+ if(!a->delay)
+ (void) (*a->f)(a->data); /* do the thing */
+
+ cleanup_data = (void *) a;
+ after_active = 1;
+#endif
+ }
+}
+
+
+/*
+ * stop_after - stop the thread
+ */
+void
+stop_after(int join)
+{
+#if defined(HAVE_PTHREAD) && defined(HAVE_NANOSLEEP)
+ int rv;
+
+ dprint((9, "stop_after(join=%d) tid=%x", join, pthread_self()));
+
+ if(after_active){
+ if((rv = pthread_cancel(after_thread)) != 0){ /* tell thread to end */
+ dprint((1, "pthread_cancel: %d (%s)\n", rv, error_description(errno)));
+ }
+
+ if(join){
+ if((rv = pthread_join(after_thread, NULL)) != 0){ /* wait for it to end */
+ dprint((1, "pthread_join: %d (%s)\n", rv, error_description(errno)));
+ }
+ }
+ else if((rv = pthread_detach(after_thread)) != 0){ /* mark thread for deletion */
+ dprint((1, "pthread_detach: %d (%s)\n", rv, error_description(errno)));
+ }
+ }
+
+ /* not literally true uless "join" set */
+ after_active = 0;
+
+#else /* !(defined(HAVE_PTHREAD) && defined(HAVE_NANOSLEEP)) */
+
+ cleanup_after((void *) cleanup_data);
+ cleanup_data = NULL;
+ after_active = 0;
+
+#endif
+}
+
+
+#if defined(HAVE_PTHREAD) && defined(HAVE_NANOSLEEP)
+/*
+ * do_after - loop thru list of pause/loop functions
+ */
+void *
+do_after(void *data)
+{
+ AFTER_S *a;
+ struct timespec ts;
+ int loop;
+ sigset_t sigs;
+
+#if defined(SIGCHLD) || defined(SIGWINCH)
+ sigemptyset(&sigs);
+#if defined(SIGCHLD)
+ /* make sure we don't end up with SIGCHLD */
+ sigaddset(&sigs, SIGCHLD);
+#endif /* SIGCHLD */
+#if defined(SIGCHLD)
+ /* or with SIGWINCH */
+ sigaddset(&sigs, SIGWINCH);
+#endif /* SIGWINCH */
+ pthread_sigmask(SIG_BLOCK, &sigs, NULL);
+#endif
+
+ /* prepare for the finish */
+ pthread_cleanup_push(cleanup_after, data);
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+ pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
+
+ /* and jump in */
+ for(a = (AFTER_S *) data; a != NULL && a->f != NULL; a = a->next){
+ if(a->delay){
+ ts.tv_sec = a->delay / 100; /* seconds */
+ ts.tv_nsec = (a->delay % 100) * 10000000;
+
+ if(nanosleep(&ts, NULL))
+ pthread_exit(NULL); /* interrupted */
+ }
+
+ while(1){
+ /* after waking, make sure we're still wanted */
+ pthread_testcancel();
+
+ loop = (*a->f)(a->data); /* do the thing */
+
+ if(loop > 0){
+ ts.tv_sec = loop / 100;
+ ts.tv_nsec = (loop % 100) * 10000000;
+
+ if(nanosleep(&ts, NULL))
+ pthread_exit(NULL); /* interrupted */
+ }
+ else
+ break;
+ }
+ }
+
+ pthread_cleanup_pop(1);
+ pthread_exit(NULL);
+}
+
+#endif /* defined(HAVE_PTHREAD) && defined(HAVE_NANOSLEEP) */
+
+
+/*
+ * cleanup_after - give start_after caller opportunity to clean up
+ * their data, then free up AFTER_S list
+ */
+void
+cleanup_after(void *data)
+{
+ AFTER_S *a, *an;
+
+#if defined(HAVE_PTHREAD) && defined(HAVE_NANOSLEEP)
+ dprint((9, "cleanup_after() tid=%x", pthread_self()));
+#endif
+
+ /* free linked list of AFTER_S's */
+ a = (AFTER_S *) data;
+ while(a != NULL){
+ an = a->next;
+
+ if(a->cf)
+ (*a->cf)(a->data);
+
+ free((void *) a);
+
+ a = an;
+ }
+}
+
+
+AFTER_S *
+new_afterstruct(void)
+{
+ AFTER_S *a;
+
+ if((a = (AFTER_S *)malloc(sizeof(AFTER_S))) == NULL){
+ fatal("Out of memory");
+ }
+
+ memset((void *) a, 0, sizeof(AFTER_S));
+
+ return(a);
+}
+
+
+void
+status_message_lock_init(void)
+{
+#if defined(HAVE_PTHREAD) && defined(HAVE_NANOSLEEP)
+ pthread_mutex_init(&status_message_mutex, NULL);
+#endif
+}
+
+
+int
+status_message_lock(void)
+{
+#if defined(HAVE_PTHREAD) && defined(HAVE_NANOSLEEP)
+ return(pthread_mutex_lock(&status_message_mutex));
+#else
+ return(0);
+#endif
+}
+
+
+int
+status_message_unlock(void)
+{
+#if defined(HAVE_PTHREAD) && defined(HAVE_NANOSLEEP)
+ return(pthread_mutex_unlock(&status_message_mutex));
+#else
+ return(0);
+#endif
+}
diff --git a/alpine/after.h b/alpine/after.h
new file mode 100644
index 00000000..0e27be06
--- /dev/null
+++ b/alpine/after.h
@@ -0,0 +1,44 @@
+/*
+ * $Id: after.h 137 2006-09-22 21:34:06Z mikes@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_AFTER_INCLUDED
+#define PINE_AFTER_INCLUDED
+
+/*
+ * start_after() arguments
+ */
+typedef struct _after_s {
+ int delay; /* call "f" after "delay" 1/100's of sec */
+ int (*f)(void *); /* 0 if done, else repeat in ret'd 1/100's */
+ void (*cf)(void *); /* called when done to clean up "data" etc */
+ void *data; /* hook to pass args and such to "f" and "cf" */
+ struct _after_s *next; /* next function to pause or repeat */
+} AFTER_S;
+
+
+extern int after_active;
+
+
+/* exported prototypes */
+void start_after(AFTER_S *);
+void stop_after(int);
+AFTER_S *new_afterstruct(void);
+void status_message_lock_init(void);
+int status_message_lock(void);
+int status_message_unlock(void);
+
+
+#endif /* PINE_AFTER_INCLUDED */
diff --git a/alpine/alpine.c b/alpine/alpine.c
new file mode 100644
index 00000000..76f66950
--- /dev/null
+++ b/alpine/alpine.c
@@ -0,0 +1,3587 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: alpine.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "headers.h"
+
+#include "../pith/newmail.h"
+#include "../pith/init.h"
+#include "../pith/sort.h"
+#include "../pith/options.h"
+#include "../pith/list.h"
+#include "../pith/conf.h"
+
+#include "osdep/debuging.h"
+#include "osdep/termout.gen.h"
+#include "osdep/chnge_pw.h"
+
+#include "alpine.h"
+#include "mailindx.h"
+#include "mailcmd.h"
+#include "addrbook.h"
+#include "reply.h"
+#include "arg.h"
+#include "keymenu.h"
+#include "status.h"
+#include "context.h"
+#include "mailview.h"
+#include "imap.h"
+#include "radio.h"
+#include "folder.h"
+#include "send.h"
+#include "help.h"
+#include "titlebar.h"
+#include "takeaddr.h"
+#include "dispfilt.h"
+#include "init.h"
+#include "remote.h"
+#include "pattern.h"
+#include "newuser.h"
+#include "setup.h"
+#include "adrbkcmd.h"
+#include "signal.h"
+#include "kblock.h"
+#include "ldapconf.h"
+#include "roleconf.h"
+#include "colorconf.h"
+#include "print.h"
+#include "after.h"
+#include "smime.h"
+#include "newmail.h"
+#ifndef _WINDOWS
+#include "../pico/osdep/raw.h" /* for STD*_FD */
+#endif
+
+
+#define PIPED_FD 5 /* Some innocuous desc */
+
+
+/* look for my_timer_period in pico directory for an explanation */
+int my_timer_period = ((IDLE_TIMEOUT + 1)*1000);
+
+/* byte count used by our gets routine to keep track */
+static unsigned long gets_bytes;
+
+
+/*
+ * Internal prototypes
+ */
+void convert_args_to_utf8(struct pine *, ARGDATA_S *);
+void preopen_stayopen_folders(void);
+int read_stdin_char(char *);
+void main_redrawer(void);
+void show_main_screen(struct pine *, int, OtherMenu, struct key_menu *, int, Pos *);
+void do_menu(int, Pos *, struct key_menu *);
+int choose_setup_cmd(int, MSGNO_S *, SCROLL_S *);
+int setup_menu(struct pine *);
+void do_setup_task(int);
+void queue_init_errors(struct pine *);
+void process_init_cmds(struct pine *, char **);
+void goodnight_gracey(struct pine *, int);
+void pine_read_progress(GETS_DATA *, unsigned long);
+int remote_pinerc_failure(void);
+void dump_supported_options(void);
+int prune_folders_ok(void);
+#ifdef WIN32
+char *pine_user_callback(void);
+#endif
+#ifdef _WINDOWS
+int fkey_mode_callback(int, long);
+void imap_telemetry_on(void);
+void imap_telemetry_off(void);
+char *pcpine_help_main(char *);
+int pcpine_main_cursor(int, long);
+#define main app_main
+#endif
+
+
+typedef struct setup_return_val {
+ int cmd;
+ int exc;
+}SRV_S;
+
+
+/*
+ * strlen of longest label from keymenu, of labels corresponding to
+ * commands in the middle of the screen. 9 is length of ListFldrs
+ */
+#define LONGEST_LABEL 9 /* length of longest label from keymenu */
+
+#define EDIT_EXCEPTION (0x100)
+
+
+static int in_panic = 0;
+
+
+/*----------------------------------------------------------------------
+ main routine -- entry point
+
+ Args: argv, argc -- The command line arguments
+
+
+ Initialize pine, parse arguments and so on
+
+ If there is a user address on the command line go into send mode and exit,
+ otherwise loop executing the various screens in Alpine.
+
+ NOTE: The Windows port def's this to "app_main"
+ ----*/
+
+int
+main(int argc, char **argv)
+{
+ ARGDATA_S args;
+ int rv;
+ long rvl;
+ struct pine *pine_state;
+ gf_io_t stdin_getc = NULL;
+ char *args_for_debug = NULL, *init_pinerc_debugging = NULL;
+
+ /*----------------------------------------------------------------------
+ Set up buffering and some data structures
+ ----------------------------------------------------------------------*/
+
+ pine_state = new_pine_struct();
+ ps_global = pine_state;
+
+ /*
+ * fill in optional pith-offered behavior hooks
+ */
+ pith_opt_read_msg_prompt = read_msg_prompt;
+ pith_opt_paint_index_hline = paint_index_hline;
+ pith_opt_rfc2369_editorial = rfc2369_editorial;
+ pith_opt_condense_thread_cue = condensed_thread_cue;
+ pith_opt_truncate_sfstr = truncate_subj_and_from_strings;
+ pith_opt_save_and_restore = save_and_restore;
+ pith_opt_newmail_announce = newmail_status_message;
+ pith_opt_newmail_check_cue = newmail_check_cue;
+ pith_opt_checkpoint_cue = newmail_check_point_cue;
+ pith_opt_icon_text = icon_text;
+ pith_opt_rd_metadata_name = rd_metadata_name;
+ pith_opt_remote_pinerc_failure = remote_pinerc_failure;
+ pith_opt_reopen_folder = ask_mailbox_reopen;
+ pith_opt_expunge_prompt = expunge_prompt;
+ pith_opt_begin_closing = expunge_and_close_begins;
+ pith_opt_replyto_prompt = reply_using_replyto_query;
+ pith_opt_reply_to_all_prompt = reply_to_all_query;
+ pith_opt_save_create_prompt = create_for_save_prompt;
+ pith_opt_daemon_confirm = confirm_daemon_send;
+ pith_opt_save_size_changed_prompt = save_size_changed_prompt;
+ pith_opt_save_index_state = setup_index_state;
+ pith_opt_filter_pattern_cmd = pattern_filter_command;
+ pith_opt_get_signature_file = get_signature_file;
+ pith_opt_pretty_var_name = pretty_var_name;
+ pith_opt_pretty_feature_name = pretty_feature_name;
+ pith_opt_closing_stream = titlebar_stream_closing;
+ pith_opt_current_expunged = mm_expunged_current;
+#ifdef SMIME
+ pith_opt_smime_get_passphrase = smime_get_passphrase;
+#endif
+#ifdef ENABLE_LDAP
+ pith_opt_save_ldap_entry = save_ldap_entry;
+#endif
+
+ status_message_lock_init();
+
+#if HAVE_SRANDOM
+ /*
+ * Seed the random number generator with the date & pid. Random
+ * numbers are used for new mail notification and bug report id's
+ */
+ srandom(getpid() + time(0));
+#endif
+
+ /* need home directory early */
+ get_user_info(&ps_global->ui);
+
+ if(!(pine_state->home_dir = our_getenv("HOME")))
+ pine_state->home_dir = cpystr(ps_global->ui.homedir);
+
+#ifdef _WINDOWS
+ {
+ char *p;
+
+ /* normalize path delimiters */
+ for(p = pine_state->home_dir; p = strchr(p, '/'); p++)
+ *p='\\';
+ }
+#endif /* _WINDOWS */
+
+#ifdef DEBUG
+ { size_t len = 0;
+ int i;
+ char *p;
+ char *no_args = " <no args>";
+
+ for(i = 0; i < argc; i++)
+ len += (strlen(argv[i] ? argv[i] : "")+3);
+
+ if(argc == 1)
+ len += strlen(no_args);
+
+ p = args_for_debug = (char *)fs_get((len+2) * sizeof(char));
+ *p++ = '\n';
+ *p = '\0';
+
+ for(i = 0; i < argc; i++){
+ snprintf(p, len+2-(p-args_for_debug), "%s\"%s\"", i ? " " : "", argv[i] ? argv[i] : "");
+ args_for_debug[len+2-1] = '\0';
+ p += strlen(p);
+ }
+
+ if(argc == 1){
+ strncat(args_for_debug, no_args, len+2-strlen(args_for_debug)-1);
+ args_for_debug[len+2-1] = '\0';
+ }
+ }
+#endif
+
+ /*----------------------------------------------------------------------
+ Parse arguments and initialize debugging
+ ----------------------------------------------------------------------*/
+ pine_args(pine_state, argc, argv, &args);
+
+#ifndef _WINDOWS
+ if(!isatty(0)){
+ /*
+ * monkey with descriptors so our normal tty i/o routines don't
+ * choke...
+ */
+ dup2(STDIN_FD, PIPED_FD); /* redirected stdin to new desc */
+ dup2(STDERR_FD, STDIN_FD); /* rebind stdin to the tty */
+ stdin_getc = read_stdin_char;
+ if(stdin_getc && args.action == aaURL){
+ display_args_err(
+ "Cannot read stdin when using -url\nFor mailto URLs, use \'body=\' instead",
+ NULL, 1);
+ args_help();
+ exit(-1);
+ }
+ }
+
+#else /* _WINDOWS */
+ /*
+ * We now have enough information to do some of the basic registry settings.
+ */
+ if(ps_global->update_registry != UREG_NEVER_SET){
+ mswin_reg(MSWR_OP_SET
+ | ((ps_global->update_registry == UREG_ALWAYS_SET)
+ ? MSWR_OP_FORCE : 0),
+ MSWR_PINE_DIR, ps_global->pine_dir, (size_t)NULL);
+ mswin_reg(MSWR_OP_SET
+ | ((ps_global->update_registry == UREG_ALWAYS_SET)
+ ? MSWR_OP_FORCE : 0),
+ MSWR_PINE_EXE, ps_global->pine_name, (size_t)NULL);
+ }
+
+#endif /* _WINDOWS */
+
+ if(ps_global->convert_sigs &&
+ (!ps_global->pinerc || !ps_global->pinerc[0])){
+ fprintf(stderr, "Use -p <pinerc> with -convert_sigs\n");
+ exit(-1);
+ }
+
+ mail_parameters(NULL, SET_QUOTA, (void *) pine_parse_quota);
+ /* set some default timeouts in case pinerc is remote */
+ mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long)30);
+ mail_parameters(NULL, SET_READTIMEOUT, (void *)(long)15);
+ mail_parameters(NULL, SET_TIMEOUT, (void *) pine_tcptimeout);
+ /* could be TO_BAIL_THRESHOLD, 15 seems more appropriate for now */
+ pine_state->tcp_query_timeout = 15;
+
+ mail_parameters(NULL, SET_SENDCOMMAND, (void *) pine_imap_cmd_happened);
+ mail_parameters(NULL, SET_FREESTREAMSPAREP, (void *) sp_free_callback);
+ mail_parameters(NULL, SET_FREEELTSPAREP, (void *) free_pine_elt);
+#ifdef SMIME
+ mail_parameters(NULL, SET_FREEBODYSPAREP, (void *) free_smime_body_sparep);
+#endif
+
+ init_pinerc(pine_state, &init_pinerc_debugging);
+
+#ifdef DEBUG
+ /* Since this is specific debugging we don't mind if the
+ ifdef is the type of system.
+ */
+#if defined(HAVE_SMALLOC) || defined(NXT)
+ if(ps_global->debug_malloc)
+ malloc_debug(ps_global->debug_malloc);
+#endif
+#ifdef CSRIMALLOC
+ if(ps_global->debug_malloc)
+ mal_debug(ps_global->debug_malloc);
+#endif
+
+ if(!ps_global->convert_sigs
+#ifdef _WINDOWS
+ && !ps_global->install_flag
+#endif /* _WINDOWS */
+ )
+ init_debug();
+
+ if(args_for_debug){
+ dprint((0, " %s (PID=%ld)\n\n", args_for_debug,
+ (long) getpid()));
+ fs_give((void **)&args_for_debug);
+ }
+
+ {
+ char *env_to_free;
+ if((env_to_free = our_getenv("HOME")) != NULL){
+ dprint((2, "Setting home dir from $HOME: \"%s\"\n",
+ env_to_free));
+ fs_give((void **)&env_to_free);
+ }
+ else{
+ dprint((2, "Setting home dir: \"%s\"\n",
+ pine_state->home_dir ? pine_state->home_dir : "<?>"));
+ }
+ }
+
+ /* Watch out. Sensitive information in debug file. */
+ if(ps_global->debug_imap > 4)
+ mail_parameters(NULL, SET_DEBUGSENSITIVE, (void *) TRUE);
+
+#ifndef DEBUGJOURNAL
+ if(ps_global->debug_tcp)
+#endif
+ mail_parameters(NULL, SET_TCPDEBUG, (void *) TRUE);
+
+#ifdef _WINDOWS
+ mswin_setdebug(debug, debugfile);
+ mswin_setdebugoncallback (imap_telemetry_on);
+ mswin_setdebugoffcallback (imap_telemetry_off);
+ mswin_enableimaptelemetry(ps_global->debug_imap != 0);
+#endif
+#endif /* DEBUG */
+
+#ifdef _WINDOWS
+ mswin_setsortcallback(index_sort_callback);
+ mswin_setflagcallback(flag_callback);
+ mswin_sethdrmodecallback(header_mode_callback);
+ mswin_setselectedcallback(any_selected_callback);
+ mswin_setzoomodecallback(zoom_mode_callback);
+ mswin_setfkeymodecallback(fkey_mode_callback);
+#endif
+
+ /*------- Set up c-client drivers -------*/
+#include "../c-client/linkage.c"
+
+ /*------- ... then tune the drivers just installed -------*/
+#ifdef _WINDOWS
+ if(_tgetenv(TEXT("HOME")))
+ mail_parameters(NULL, SET_HOMEDIR, (void *) pine_state->home_dir);
+
+ mail_parameters(NULL, SET_USERPROMPT, (void *) pine_user_callback);
+
+ /*
+ * Sniff the environment for timezone offset. We need to do this
+ * here since Windows needs help figuring out UTC, and will adjust
+ * what time() returns based on TZ. THIS WILL SCREW US because
+ * we use time() differences to manage status messages. So, if
+ * rfc822_date, which calls localtime() and thus needs tzset(),
+ * is called while a status message is displayed, it's possible
+ * for time() to return a time *before* what we remember as the
+ * time we put the status message on the display. Sheesh.
+ */
+ tzset();
+#else /* !_WINDOWS */
+ /*
+ * We used to let c-client do this for us automatically, but it declines
+ * to do so for root. This forces c-client to establish an environment,
+ * even if the uid is 0.
+ */
+ env_init(ps_global->ui.login, ps_global->ui.homedir);
+
+ /*
+ * Install callback to let us know the progress of network reads...
+ */
+ (void) mail_parameters(NULL, SET_READPROGRESS, (void *)pine_read_progress);
+#endif /* !_WINDOWS */
+
+ /*
+ * Install callback to handle certificate validation failures,
+ * allowing the user to continue if they wish.
+ */
+ mail_parameters(NULL, SET_SSLCERTIFICATEQUERY, (void *) pine_sslcertquery);
+ mail_parameters(NULL, SET_SSLFAILURE, (void *) pine_sslfailure);
+
+ if(init_pinerc_debugging){
+ dprint((2, init_pinerc_debugging));
+ fs_give((void **)&init_pinerc_debugging);
+ }
+
+ /*
+ * Initial allocation of array of stream pool pointers.
+ * We do this before init_vars so that we can re-use streams used for
+ * remote config files. These sizes may get changed later.
+ */
+ ps_global->s_pool.max_remstream = 2;
+ dprint((9,
+ "Setting initial max_remstream to %d for remote config re-use\n",
+ ps_global->s_pool.max_remstream));
+
+ init_vars(pine_state, process_init_cmds);
+
+#ifdef SMIME
+ if(F_ON(F_DONT_DO_SMIME, ps_global))
+ smime_deinit();
+#endif /* SMIME */
+
+#ifdef ENABLE_NLS
+ /*
+ * LC_CTYPE is already set from the set_collation call above.
+ *
+ * We can't use gettext calls before we do this stuff so it doesn't
+ * help to translate strings that come before this in the program.
+ * Maybe we could rearrange things to accomodate that.
+ */
+ setlocale(LC_MESSAGES, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ bind_textdomain_codeset(PACKAGE, "UTF-8");
+ textdomain(PACKAGE);
+#endif /* ENABLE_NLS */
+
+ convert_args_to_utf8(pine_state, &args);
+
+ if(args.action == aaFolder){
+ pine_state->beginning_of_month = first_run_of_month();
+ pine_state->beginning_of_year = first_run_of_year();
+ }
+
+ /* Set up optional for user-defined display filtering */
+ pine_state->tools.display_filter = dfilter;
+ pine_state->tools.display_filter_trigger = dfilter_trigger;
+
+#ifdef _WINDOWS
+ if(ps_global->install_flag){
+ init_install_get_vars();
+
+ if(ps_global->prc)
+ free_pinerc_s(&ps_global->prc);
+
+ exit(0);
+ }
+#endif
+
+ if(ps_global->convert_sigs){
+ if(convert_sigs_to_literal(ps_global, 0) == -1){
+ /* TRANSLATORS: sigs refers to signatures, which the user was trying to convert */
+ fprintf(stderr, _("trouble converting sigs\n"));
+ exit(-1);
+ }
+
+ if(ps_global->prc){
+ if(ps_global->prc->outstanding_pinerc_changes)
+ write_pinerc(ps_global, Main, WRP_NONE);
+
+ free_pinerc_s(&pine_state->prc);
+ }
+
+ exit(0);
+ }
+
+ /*
+ * Set up a c-client read timeout and timeout handler. In general,
+ * it shouldn't happen, but a server crash or dead link can cause
+ * pine to appear wedged if we don't set this up...
+ */
+ rv = 30;
+ if(pine_state->VAR_TCPOPENTIMEO)
+ (void)SVAR_TCP_OPEN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF);
+ mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long)rv);
+
+ rv = 15;
+ if(pine_state->VAR_TCPREADWARNTIMEO)
+ (void)SVAR_TCP_READWARN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF);
+ mail_parameters(NULL, SET_READTIMEOUT, (void *)(long)rv);
+
+ rv = 0;
+ if(pine_state->VAR_TCPWRITEWARNTIMEO){
+ if(!SVAR_TCP_WRITEWARN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF))
+ if(rv == 0 || rv > 4) /* making sure */
+ mail_parameters(NULL, SET_WRITETIMEOUT, (void *)(long)rv);
+ }
+
+ mail_parameters(NULL, SET_TIMEOUT, (void *) pine_tcptimeout);
+
+ rv = 15;
+ if(pine_state->VAR_RSHOPENTIMEO){
+ if(!SVAR_RSH_OPEN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF))
+ if(rv == 0 || rv > 4) /* making sure */
+ mail_parameters(NULL, SET_RSHTIMEOUT, (void *)(long)rv);
+ }
+
+ rv = 15;
+ if(pine_state->VAR_SSHOPENTIMEO){
+ if(!SVAR_SSH_OPEN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF))
+ if(rv == 0 || rv > 4) /* making sure */
+ mail_parameters(NULL, SET_SSHTIMEOUT, (void *)(long)rv);
+ }
+
+ rvl = 60L;
+ if(pine_state->VAR_MAILDROPCHECK){
+ if(!SVAR_MAILDCHK(pine_state, rvl, tmp_20k_buf, SIZEOF_20KBUF)){
+ if(rvl == 0L)
+ rvl = (60L * 60L * 24L * 100L); /* 100 days */
+
+ if(rvl >= 60L) /* making sure */
+ mail_parameters(NULL, SET_SNARFINTERVAL, (void *) rvl);
+ }
+ }
+
+ /*
+ * Lookups of long login names which don't exist are very slow in aix.
+ * This would normally get set in system-wide config if not needed.
+ */
+ if(F_ON(F_DISABLE_SHARED_NAMESPACES, ps_global))
+ mail_parameters(NULL, SET_DISABLEAUTOSHAREDNS, (void *) TRUE);
+
+ if(F_ON(F_HIDE_NNTP_PATH, ps_global))
+ mail_parameters(NULL, SET_NNTPHIDEPATH, (void *) TRUE);
+
+ if(F_ON(F_MAILDROPS_PRESERVE_STATE, ps_global))
+ mail_parameters(NULL, SET_SNARFPRESERVE, (void *) TRUE);
+
+ rvl = 0L;
+ if(pine_state->VAR_NNTPRANGE){
+ if(!SVAR_NNTPRANGE(pine_state, rvl, tmp_20k_buf, SIZEOF_20KBUF))
+ if(rvl > 0L)
+ mail_parameters(NULL, SET_NNTPRANGE, (void *) rvl);
+ }
+
+ /*
+ * Tell c-client not to be so aggressive about uid mappings
+ */
+ mail_parameters(NULL, SET_UIDLOOKAHEAD, (void *) 20);
+
+ /*
+ * Setup referral handling
+ */
+ mail_parameters(NULL, SET_IMAPREFERRAL, (void *) imap_referral);
+ mail_parameters(NULL, SET_MAILPROXYCOPY, (void *) imap_proxycopy);
+
+ /*
+ * Setup multiple newsrc transition
+ */
+ mail_parameters(NULL, SET_NEWSRCQUERY, (void *) pine_newsrcquery);
+
+ /*
+ * Disable some drivers if requested.
+ */
+ if(ps_global->VAR_DISABLE_DRIVERS &&
+ ps_global->VAR_DISABLE_DRIVERS[0] &&
+ ps_global->VAR_DISABLE_DRIVERS[0][0]){
+ char **t;
+
+ for(t = ps_global->VAR_DISABLE_DRIVERS; t[0] && t[0][0]; t++)
+ if(mail_parameters(NULL, DISABLE_DRIVER, (void *)(*t))){
+ dprint((2, "Disabled mail driver \"%s\"\n", *t));
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,
+ _("Failed to disable mail driver \"%s\": name not found"),
+ *t);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ init_error(ps_global, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ }
+ }
+
+ /*
+ * Disable some authenticators if requested.
+ */
+ if(ps_global->VAR_DISABLE_AUTHS &&
+ ps_global->VAR_DISABLE_AUTHS[0] &&
+ ps_global->VAR_DISABLE_AUTHS[0][0]){
+ char **t;
+
+ for(t = ps_global->VAR_DISABLE_AUTHS; t[0] && t[0][0]; t++)
+ if(mail_parameters(NULL, DISABLE_AUTHENTICATOR, (void *)(*t))){
+ dprint((2,"Disabled SASL authenticator \"%s\"\n", *t));
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,
+ _("Failed to disable SASL authenticator \"%s\": name not found"),
+ *t);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ init_error(ps_global, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ }
+ }
+
+ /*
+ * setup alternative authentication driver preference for IMAP opens
+ */
+ if(F_ON(F_PREFER_ALT_AUTH, ps_global))
+ mail_parameters(NULL, SET_IMAPTRYALT, (void *) TRUE);
+
+ /*
+ * Install handler to let us know about potential delays
+ */
+ (void) mail_parameters(NULL, SET_BLOCKNOTIFY, (void *) pine_block_notify);
+
+ if(ps_global->dump_supported_options){
+ dump_supported_options();
+ exit(0);
+ }
+
+ /*
+ * Install extra headers to fetch along with all the other stuff
+ * mail_fetch_structure and mail_fetch_overview requests.
+ */
+ calc_extra_hdrs();
+ if(get_extra_hdrs())
+ (void) mail_parameters(NULL, SET_IMAPEXTRAHEADERS,
+ (void *) get_extra_hdrs());
+
+ if(init_username(pine_state) < 0){
+ fprintf(stderr, _("Who are you? (Unable to look up login name)\n"));
+ exit(-1);
+ }
+
+ if(init_userdir(pine_state) < 0)
+ exit(-1);
+
+ if(init_hostname(pine_state) < 0)
+ exit(-1);
+
+ /*
+ * Verify mail dir if we're not in send only mode...
+ */
+ if(args.action == aaFolder && init_mail_dir(pine_state) < 0)
+ exit(-1);
+
+ init_signals();
+
+ /*--- input side ---*/
+ if(init_tty_driver(pine_state)){
+#ifndef _WINDOWS /* always succeeds under _WINDOWS */
+ fprintf(stderr, _("Can't access terminal or input is not a terminal. Redirection of\nstandard input is not allowed. For example \"pine < file\" doesn't work.\n%c"), BELL);
+ exit(-1);
+#endif /* !_WINDOWS */
+ }
+
+
+ /*--- output side ---*/
+ rv = config_screen(&(pine_state->ttyo));
+#ifndef _WINDOWS /* always succeeds under _WINDOWS */
+ if(rv){
+ switch(rv){
+ case -1:
+ printf(_("Terminal type (environment variable TERM) not set.\n"));
+ break;
+ case -2:
+ printf(_("Terminal type \"%s\" is unknown.\n"), getenv("TERM"));
+ break;
+ case -3:
+ printf(_("Can't open terminal capabilities database.\n"));
+ break;
+ case -4:
+ printf(_("Your terminal, of type \"%s\", is lacking functions needed to run alpine.\n"), getenv("TERM"));
+ break;
+ }
+
+ printf("\r");
+ end_tty_driver(pine_state);
+ exit(-1);
+ }
+#endif /* !_WINDOWS */
+
+ if(F_ON(F_BLANK_KEYMENU,ps_global))
+ FOOTER_ROWS(ps_global) = 1;
+
+ init_screen();
+ init_keyboard(pine_state->orig_use_fkeys);
+ strncpy(pine_state->inbox_name, INBOX_NAME,
+ sizeof(pine_state->inbox_name)-1);
+ init_folders(pine_state); /* digest folder spec's */
+
+ pine_state->in_init_seq = 0; /* so output (& ClearScreen) show up */
+ pine_state->dont_use_init_cmds = 1; /* don't use up initial_commands yet */
+ ClearScreen();
+
+ /* initialize titlebar in case we use it */
+ set_titlebar("", NULL, NULL, NULL, NULL, 0, FolderName, 0, 0, NULL);
+
+ /*
+ * Prep storage object driver for PicoText
+ */
+ so_register_external_driver(pine_pico_get, pine_pico_give, pine_pico_writec, pine_pico_readc,
+ pine_pico_puts, pine_pico_seek, NULL, NULL);
+
+#ifdef DEBUG
+ if(ps_global->debug_imap > 4 || debug > 9){
+ q_status_message(SM_ORDER | SM_DING, 5, 9,
+ _("Warning: sensitive authentication data included in debug file"));
+ flush_status_messages(0);
+ }
+#endif
+
+ if(args.action == aaPrcCopy || args.action == aaAbookCopy){
+ int exit_val = -1;
+ char *err_msg = NULL;
+
+ /*
+ * Don't translate these into UTF-8 because we'll be using them
+ * before we translate next time. User should use ascii.
+ */
+ if(args.data.copy.local && args.data.copy.remote){
+ switch(args.action){
+ case aaPrcCopy:
+ exit_val = copy_pinerc(args.data.copy.local,
+ args.data.copy.remote, &err_msg);
+ break;
+
+ case aaAbookCopy:
+ exit_val = copy_abook(args.data.copy.local,
+ args.data.copy.remote, &err_msg);
+ break;
+
+ default:
+ break;
+ }
+ }
+ if(err_msg){
+ q_status_message(SM_ORDER | SM_DING, 3, 4, err_msg);
+ fs_give((void **)&err_msg);
+ }
+ goodnight_gracey(pine_state, exit_val);
+ }
+
+ if(args.action == aaFolder
+ && (pine_state->first_time_user || pine_state->show_new_version)){
+ pine_state->mangled_header = 1;
+ show_main_screen(pine_state, 0, FirstMenu, &main_keymenu, 0,
+ (Pos *) NULL);
+ new_user_or_version(pine_state);
+ ClearScreen();
+ }
+
+ /* put back in case we need to suppress output */
+ pine_state->in_init_seq = pine_state->save_in_init_seq;
+
+ /* queue any init errors so they get displayed in a screen below */
+ queue_init_errors(ps_global);
+
+ /* "Page" the given file? */
+ if(args.action == aaMore){
+ int dice = 1, redir = 0;
+
+ if(pine_state->in_init_seq){
+ pine_state->in_init_seq = pine_state->save_in_init_seq = 0;
+ clear_cursor_pos();
+ if(pine_state->free_initial_cmds)
+ fs_give((void **)&(pine_state->free_initial_cmds));
+
+ pine_state->initial_cmds = NULL;
+ }
+
+ /*======= Requested that we simply page the given file =======*/
+ if(args.data.file){ /* Open the requested file... */
+ SourceType src;
+ STORE_S *store = NULL;
+ char *decode_error = NULL;
+ char filename[MAILTMPLEN];
+
+ if(args.data.file[0] == '\0'){
+ HelpType help = NO_HELP;
+
+ pine_state->mangled_footer = 1;
+ filename[0] = '\0';
+ while(1){
+ int flags = OE_APPEND_CURRENT;
+
+ rv = optionally_enter(filename, -FOOTER_ROWS(pine_state),
+ 0, sizeof(filename),
+ /* TRANSLATORS: file is computer data */
+ _("File to open : "),
+ NULL, help, &flags);
+ if(rv == 3){
+ help = (help == NO_HELP) ? h_no_F_arg : NO_HELP;
+ continue;
+ }
+
+ if(rv != 4)
+ break;
+ }
+
+ if(rv == 1){
+ q_status_message(SM_ORDER, 0, 2, _("Cancelled"));
+ goodnight_gracey(pine_state, -1);
+ }
+
+ if(*filename){
+ removing_trailing_white_space(filename);
+ removing_leading_white_space(filename);
+ if(is_absolute_path(filename))
+ fnexpand(filename, sizeof(filename));
+
+ args.data.file = filename;
+ }
+
+ if(!*filename){
+ /* TRANSLATORS: file is computer data */
+ q_status_message(SM_ORDER, 0, 2 ,_("No file to open"));
+ goodnight_gracey(pine_state, -1);
+ }
+ }
+
+ if(stdin_getc){
+ redir++;
+ src = CharStar;
+ if(isatty(0) && (store = so_get(src, NULL, EDIT_ACCESS))){
+ gf_io_t pc;
+
+ gf_set_so_writec(&pc, store);
+ gf_filter_init();
+ if((decode_error = gf_pipe(stdin_getc, pc)) != NULL){
+ dice = 0;
+ q_status_message1(SM_ORDER, 3, 4,
+ _("Problem reading standard input: %s"),
+ decode_error);
+ }
+
+ gf_clear_so_writec(store);
+ }
+ else
+ dice = 0;
+ }
+ else{
+ src = FileStar;
+ strncpy(ps_global->cur_folder, args.data.file,
+ sizeof(ps_global->cur_folder)-1);
+ ps_global->cur_folder[sizeof(ps_global->cur_folder)-1] = '\0';
+ if(!(store = so_get(src, args.data.file, READ_ACCESS|READ_FROM_LOCALE)))
+ dice = 0;
+ }
+
+ if(dice){
+ SCROLL_S sargs;
+
+ memset(&sargs, 0, sizeof(SCROLL_S));
+ sargs.text.text = so_text(store);
+ sargs.text.src = src;
+ /* TRANSLATORS: file is computer file being read by user */
+ sargs.text.desc = _("file");
+ /* TRANSLATORS: this is in the title bar at top of screen */
+ sargs.bar.title = _("FILE VIEW");
+ sargs.bar.style = FileTextPercent;
+ sargs.keys.menu = &simple_file_keymenu;
+ setbitmap(sargs.keys.bitmap);
+ scrolltool(&sargs);
+
+ printf("\n\n");
+ so_give(&store);
+ }
+ }
+
+ if(!dice){
+ q_status_message2(SM_ORDER, 3, 4,
+ _("Can't display \"%s\": %s"),
+ (redir) ? _("Standard Input")
+ : args.data.file ? args.data.file : "NULL",
+ error_description(errno));
+ }
+
+ goodnight_gracey(pine_state, 0);
+ }
+ else if(args.action == aaMail || (stdin_getc && (args.action != aaURL))){
+ /*======= address on command line/send one message mode ============*/
+ char *to = NULL, *error = NULL, *addr = NULL;
+ int len, good_addr = 1;
+ int exit_val = 0;
+ BUILDER_ARG fcc;
+
+ if(pine_state->in_init_seq){
+ pine_state->in_init_seq = pine_state->save_in_init_seq = 0;
+ clear_cursor_pos();
+ if(pine_state->free_initial_cmds)
+ fs_give((void **) &(pine_state->free_initial_cmds));
+
+ pine_state->initial_cmds = NULL;
+ }
+
+ /*----- Format the To: line with commas for the composer ---*/
+ if(args.data.mail.addrlist){
+ STRLIST_S *p;
+
+ for(p = args.data.mail.addrlist, len = 0; p; p = p->next)
+ len += strlen(p->name) + 2;
+
+ to = (char *) fs_get((len + 5) * sizeof(char));
+ for(p = args.data.mail.addrlist, *to = '\0'; p; p = p->next){
+ if(*to){
+ strncat(to, ", ", len+5-strlen(to)-1);
+ to[len+5-1] = '\0';
+ }
+
+ strncat(to, p->name, len+5-strlen(to)-1);
+ to[len+5-1] = '\0';
+ }
+
+ memset((void *)&fcc, 0, sizeof(BUILDER_ARG));
+ dprint((2, "building addr: -->%s<--\n", to ? to : "?"));
+ good_addr = (build_address(to, &addr, &error, &fcc, NULL) >= 0);
+ dprint((2, "mailing to: -->%s<--\n", addr ? addr : "?"));
+ free_strlist(&args.data.mail.addrlist);
+ }
+ else
+ memset(&fcc, 0, sizeof(fcc));
+
+ if(good_addr){
+ compose_mail(addr, fcc.tptr, NULL,
+ args.data.mail.attachlist, stdin_getc);
+ }
+ else{
+ /* TRANSLATORS: refers to bad email address */
+ q_status_message1(SM_ORDER, 3, 4, _("Bad address: %s"), error);
+ exit_val = -1;
+ }
+
+ if(addr)
+ fs_give((void **) &addr);
+
+ if(fcc.tptr)
+ fs_give((void **) &fcc.tptr);
+
+ if(args.data.mail.attachlist)
+ free_attachment_list(&args.data.mail.attachlist);
+
+ if(to)
+ fs_give((void **) &to);
+
+ if(error)
+ fs_give((void **) &error);
+
+ goodnight_gracey(pine_state, exit_val);
+ }
+ else{
+ char int_mail[MAXPATH+1];
+ struct key_menu *km = &main_keymenu;
+
+ /*========== Normal pine mail reading mode ==========*/
+
+ pine_state->mail_stream = NULL;
+ pine_state->mangled_screen = 1;
+
+ if(args.action == aaURL){
+ url_tool_t f;
+
+ if(pine_state->in_init_seq){
+ pine_state->in_init_seq = pine_state->save_in_init_seq = 0;
+ clear_cursor_pos();
+ if(pine_state->free_initial_cmds)
+ fs_give((void **) &(pine_state->free_initial_cmds));
+ pine_state->initial_cmds = NULL;
+ }
+ if((f = url_local_handler(args.url)) != NULL){
+ if(args.data.mail.attachlist){
+ if(f == url_local_mailto){
+ if(!(url_local_mailto_and_atts(args.url,
+ args.data.mail.attachlist)
+ && pine_state->next_screen))
+ free_attachment_list(&args.data.mail.attachlist);
+ goodnight_gracey(pine_state, 0);
+ }
+ else {
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Only mailto URLs are allowed with file attachments"));
+ goodnight_gracey(pine_state, -1); /* no return */
+ }
+ }
+ else if(!((*f)(args.url) && pine_state->next_screen))
+ goodnight_gracey(pine_state, 0); /* no return */
+ }
+ else{
+ q_status_message1(SM_ORDER | SM_DING, 3, 4,
+ _("Unrecognized URL \"%s\""), args.url);
+ goodnight_gracey(pine_state, -1); /* no return */
+ }
+ }
+ else if(!pine_state->start_in_index){
+ /* flash message about executing initial commands */
+ if(pine_state->in_init_seq){
+ pine_state->in_init_seq = 0;
+ clear_cursor_pos();
+ pine_state->mangled_header = 1;
+ pine_state->mangled_footer = 1;
+ pine_state->mangled_screen = 0;
+ /* show that this is Alpine */
+ show_main_screen(pine_state, 0, FirstMenu, km, 0, (Pos *)NULL);
+ pine_state->mangled_screen = 1;
+ pine_state->painted_footer_on_startup = 1;
+ if(MIN(4, pine_state->ttyo->screen_rows - 4) > 1){
+ char buf1[6*MAX_SCREEN_COLS+1];
+ char buf2[6*MAX_SCREEN_COLS+1];
+ int wid;
+
+ /* TRANSLATORS: Initial Keystroke List is the literal name of an option */
+ strncpy(buf1, _("Executing Initial Keystroke List......"), sizeof(buf1));
+ buf1[sizeof(buf1)-1] = '\0';
+ wid = utf8_width(buf1);
+ if(wid > ps_global->ttyo->screen_cols){
+ utf8_pad_to_width(buf2, buf1, sizeof(buf2), ps_global->ttyo->screen_cols, 1);
+ PutLine0(MIN(4, pine_state->ttyo->screen_rows - 4), 0, buf2);
+ }
+ else{
+ PutLine0(MIN(4, pine_state->ttyo->screen_rows - 4),
+ MAX(MIN(11, pine_state->ttyo->screen_cols - wid), 0), buf1);
+ }
+ }
+
+ pine_state->in_init_seq = 1;
+ }
+ else{
+ show_main_screen(pine_state, 0, FirstMenu, km, 0, (Pos *)NULL);
+ pine_state->painted_body_on_startup = 1;
+ pine_state->painted_footer_on_startup = 1;
+ }
+ }
+ else{
+ /* cancel any initial commands, overridden by cmd line */
+ if(pine_state->in_init_seq){
+ pine_state->in_init_seq = 0;
+ pine_state->save_in_init_seq = 0;
+ clear_cursor_pos();
+ if(pine_state->initial_cmds){
+ if(pine_state->free_initial_cmds)
+ fs_give((void **)&(pine_state->free_initial_cmds));
+
+ pine_state->initial_cmds = NULL;
+ }
+
+ F_SET(F_USE_FK,pine_state, pine_state->orig_use_fkeys);
+ }
+
+ (void) do_index_border(pine_state->context_current,
+ pine_state->cur_folder,
+ pine_state->mail_stream,
+ pine_state->msgmap, MsgIndex, NULL,
+ INDX_CLEAR|INDX_HEADER|INDX_FOOTER);
+ pine_state->painted_footer_on_startup = 1;
+ if(MIN(4, pine_state->ttyo->screen_rows - 4) > 1){
+ char buf1[6*MAX_SCREEN_COLS+1];
+ char buf2[6*MAX_SCREEN_COLS+1];
+ int wid;
+
+ strncpy(buf1, _("Please wait, opening mail folder......"), sizeof(buf1));
+ buf1[sizeof(buf1)-1] = '\0';
+ wid = utf8_width(buf1);
+ if(wid > ps_global->ttyo->screen_cols){
+ utf8_pad_to_width(buf2, buf1, sizeof(buf2), ps_global->ttyo->screen_cols, 1);
+ PutLine0(MIN(4, pine_state->ttyo->screen_rows - 4), 0, buf2);
+ }
+ else{
+ PutLine0(MIN(4, pine_state->ttyo->screen_rows - 4),
+ MAX(MIN(11, pine_state->ttyo->screen_cols - wid), 0), buf1);
+ }
+ }
+ }
+
+ fflush(stdout);
+
+#if !defined(_WINDOWS) && !defined(LEAVEOUTFIFO)
+ if(ps_global->VAR_FIFOPATH && ps_global->VAR_FIFOPATH[0])
+ init_newmailfifo(ps_global->VAR_FIFOPATH);
+#endif
+
+ if(pine_state->in_init_seq){
+ pine_state->in_init_seq = 0;
+ clear_cursor_pos();
+ }
+
+ if(args.action == aaFolder && args.data.folder){
+ CONTEXT_S *cntxt = NULL, *tc = NULL;
+ char foldername[MAILTMPLEN];
+ int notrealinbox = 0;
+
+ if(args.data.folder[0] == '\0'){
+ char *fldr;
+ unsigned save_def_goto_rule;
+
+ foldername[0] = '\0';
+ save_def_goto_rule = pine_state->goto_default_rule;
+ pine_state->goto_default_rule = GOTO_FIRST_CLCTN;
+ tc = default_save_context(pine_state->context_list);
+ fldr = broach_folder(-FOOTER_ROWS(pine_state), 1, &notrealinbox, &tc);
+ pine_state->goto_default_rule = save_def_goto_rule;
+ if(fldr){
+ strncpy(foldername, fldr, sizeof(foldername)-1);
+ foldername[sizeof(foldername)-1] = '\0';
+ }
+
+ if(*foldername){
+ removing_trailing_white_space(foldername);
+ removing_leading_white_space(foldername);
+ args.data.folder = cpystr(foldername);
+ }
+
+ if(!*foldername){
+ q_status_message(SM_ORDER, 0, 2 ,_("No folder to open"));
+ goodnight_gracey(pine_state, -1);
+ }
+ }
+
+ if(tc)
+ cntxt = tc;
+ else if((rv = pine_state->init_context) < 0)
+ /*
+ * As with almost all the folder vars in the pinerc,
+ * we subvert the collection "breakout" here if the
+ * folder name given looks like an asolute path on
+ * this system...
+ */
+ cntxt = (is_absolute_path(args.data.folder))
+ ? NULL : pine_state->context_current;
+ else if(rv == 0)
+ cntxt = NULL;
+ else
+ for(cntxt = pine_state->context_list;
+ rv > 1 && cntxt->next;
+ rv--, cntxt = cntxt->next)
+ ;
+
+ if(pine_state && pine_state->ttyo){
+ blank_keymenu(pine_state->ttyo->screen_rows - 2, 0);
+ pine_state->painted_footer_on_startup = 0;
+ pine_state->mangled_footer = 1;
+ }
+
+ if(do_broach_folder(args.data.folder, cntxt, NULL, notrealinbox ? 0L : DB_INBOXWOCNTXT) <= 0){
+ q_status_message1(SM_ORDER, 3, 4,
+ _("Unable to open folder \"%s\""), args.data.folder);
+
+ goodnight_gracey(pine_state, -1);
+ }
+ }
+ else if(args.action == aaFolder){
+#ifdef _WINDOWS
+ /*
+ * need to ask for the inbox name if no default under DOS
+ * since there is no "inbox"
+ */
+
+ if(!pine_state->VAR_INBOX_PATH || !pine_state->VAR_INBOX_PATH[0]
+ || strucmp(pine_state->VAR_INBOX_PATH, "inbox") == 0){
+ HelpType help = NO_HELP;
+ static ESCKEY_S ekey[] = {{ctrl(T), 2, "^T", "To Fldrs"},
+ {-1, 0, NULL, NULL}};
+
+ pine_state->mangled_footer = 1;
+ int_mail[0] = '\0';
+ while(1){
+ int flags = OE_APPEND_CURRENT;
+
+ rv = optionally_enter(int_mail, -FOOTER_ROWS(pine_state),
+ 0, sizeof(int_mail),
+ _("No inbox! Folder to open as inbox : "),
+ /* ekey */ NULL, help, &flags);
+ if(rv == 3){
+ help = (help == NO_HELP) ? h_sticky_inbox : NO_HELP;
+ continue;
+ }
+
+ if(rv != 4)
+ break;
+ }
+
+ if(rv == 1){
+ q_status_message(SM_ORDER, 0, 2 ,_("Folder open cancelled"));
+ rv = 0; /* reset rv */
+ }
+ else if(rv == 2){
+ show_main_screen(pine_state,0,FirstMenu,km,0,(Pos *)NULL);
+ }
+
+ if(*int_mail){
+ removing_trailing_white_space(int_mail);
+ removing_leading_white_space(int_mail);
+ if((!pine_state->VAR_INBOX_PATH
+ || strucmp(pine_state->VAR_INBOX_PATH, "inbox") == 0)
+ /* TRANSLATORS: Inbox-Path and PINERC are literal, not to be translated */
+ && want_to(_("Preserve folder as \"Inbox-Path\" in PINERC"),
+ 'y', 'n', NO_HELP, WT_NORM) == 'y'){
+ set_variable(V_INBOX_PATH, int_mail, 1, 1, Main);
+ }
+ else{
+ if(pine_state->VAR_INBOX_PATH)
+ fs_give((void **)&pine_state->VAR_INBOX_PATH);
+
+ pine_state->VAR_INBOX_PATH = cpystr(int_mail);
+ }
+
+ if(pine_state && pine_state->ttyo){
+ blank_keymenu(pine_state->ttyo->screen_rows - 2, 0);
+ pine_state->painted_footer_on_startup = 0;
+ pine_state->mangled_footer = 1;
+ }
+
+ do_broach_folder(pine_state->inbox_name,
+ pine_state->context_list, NULL, DB_INBOXWOCNTXT);
+ }
+ else
+ q_status_message(SM_ORDER, 0, 2 ,_("No folder opened"));
+
+ }
+ else
+
+#endif /* _WINDOWS */
+ if(F_ON(F_PREOPEN_STAYOPENS, ps_global))
+ preopen_stayopen_folders();
+
+ if(pine_state && pine_state->ttyo){
+ blank_keymenu(pine_state->ttyo->screen_rows - 2, 0);
+ pine_state->painted_footer_on_startup = 0;
+ pine_state->mangled_footer = 1;
+ }
+
+ /* open inbox */
+ do_broach_folder(pine_state->inbox_name,
+ pine_state->context_list, NULL, DB_INBOXWOCNTXT);
+ }
+
+ if(pine_state->mangled_footer)
+ pine_state->painted_footer_on_startup = 0;
+
+ if(args.action == aaFolder
+ && pine_state->mail_stream
+ && expire_sent_mail())
+ pine_state->painted_footer_on_startup = 0;
+
+ /*
+ * Initialize the defaults. Initializing here means that
+ * if they're remote, the user isn't prompted for an imap login
+ * before the display's drawn, AND there's the chance that
+ * we can climb onto the already opened folder's stream...
+ */
+ if(ps_global->first_time_user)
+ init_save_defaults(); /* initialize default save folders */
+
+ build_path(int_mail,
+ ps_global->VAR_OPER_DIR ? ps_global->VAR_OPER_DIR
+ : pine_state->home_dir,
+ INTERRUPTED_MAIL, sizeof(int_mail));
+ if(args.action == aaFolder
+ && (folder_exists(NULL, int_mail) & FEX_ISFILE))
+ q_status_message(SM_ORDER | SM_DING, 4, 5,
+ _("Use Compose command to continue interrupted message."));
+
+#if defined(USE_QUOTAS)
+ {
+ long q;
+ int over;
+ q = disk_quota(pine_state->home_dir, &over);
+ if(q > 0 && over){
+ q_status_message2(SM_ASYNC | SM_DING, 4, 5,
+ _("WARNING! Over your disk quota by %s bytes (%s)"),
+ comatose(q),byte_string(q));
+ }
+ }
+#endif
+
+ pine_state->in_init_seq = pine_state->save_in_init_seq;
+ pine_state->dont_use_init_cmds = 0;
+ clear_cursor_pos();
+
+ if(pine_state->give_fixed_warning)
+ q_status_message(SM_ASYNC, 0, 10,
+/* TRANSLATORS: config is an abbreviation for configuration */
+_("Note: some of your config options conflict with site policy and are ignored"));
+
+ if(!prune_folders_ok())
+ q_status_message(SM_ASYNC, 0, 10,
+ /* TRANSLATORS: Pruned-Folders is literal */
+ _("Note: ignoring Pruned-Folders outside of default collection for saves"));
+
+ if(get_input_timeout() == 0 &&
+ ps_global->VAR_INBOX_PATH &&
+ ps_global->VAR_INBOX_PATH[0] == '{')
+ q_status_message(SM_ASYNC, 0, 10,
+_("Note: Mail-Check-Interval=0 may cause IMAP server connection to time out"));
+
+#ifdef _WINDOWS
+ mswin_setnewmailwidth(ps_global->nmw_width);
+#endif
+
+
+ /*-------------------------------------------------------------------
+ Loop executing the commands
+
+ This is done like this so that one command screen can cause
+ another one to execute it with out going through the main menu.
+ ------------------------------------------------------------------*/
+ if(!pine_state->next_screen)
+ pine_state->next_screen = pine_state->start_in_index
+ ? mail_index_screen : main_menu_screen;
+ while(1){
+ if(pine_state->next_screen == SCREEN_FUN_NULL)
+ pine_state->next_screen = main_menu_screen;
+
+ (*(pine_state->next_screen))(pine_state);
+ }
+ }
+
+ exit(0);
+}
+
+
+/*
+ * The arguments need to be converted to UTF-8 for our internal use.
+ * Not all arguments are converted because some are used before we
+ * are able to do the conversion, like the pinerc name.
+ */
+void
+convert_args_to_utf8(struct pine *ps, ARGDATA_S *args)
+{
+ char *fromcharset = NULL;
+ char *conv;
+
+ if(args){
+ if(ps->keyboard_charmap && strucmp(ps->keyboard_charmap, "UTF-8")
+ && strucmp(ps->keyboard_charmap, "US-ASCII"))
+ fromcharset = ps->keyboard_charmap;
+ else if(ps->display_charmap && strucmp(ps->display_charmap, "UTF-8")
+ && strucmp(ps->display_charmap, "US-ASCII"))
+ fromcharset = ps->display_charmap;
+#ifndef _WINDOWS
+ else if(ps->VAR_OLD_CHAR_SET && strucmp(ps->VAR_OLD_CHAR_SET, "UTF-8")
+ && strucmp(ps->VAR_OLD_CHAR_SET, "US-ASCII"))
+ fromcharset = ps->VAR_OLD_CHAR_SET;
+#endif /* ! _WINDOWS */
+
+ if(args->action == aaURL && args->url){
+ conv = convert_to_utf8(args->url, fromcharset, 0);
+ if(conv){
+ fs_give((void **) &args->url);
+ args->url = conv;
+ }
+ }
+
+ if(args->action == aaFolder && args->data.folder){
+ conv = convert_to_utf8(args->data.folder, fromcharset, 0);
+ if(conv){
+ fs_give((void **) &args->data.folder);
+ args->data.folder = conv;
+ }
+ }
+
+ if(args->action == aaMore && args->data.file){
+ conv = convert_to_utf8(args->data.file, fromcharset, 0);
+ if(conv){
+ fs_give((void **) &args->data.file);
+ args->data.file = conv;
+ }
+ }
+
+ if(args->action == aaURL || args->action == aaMail){
+ if(args->data.mail.addrlist){
+ STRLIST_S *p;
+
+ for(p = args->data.mail.addrlist; p; p=p->next){
+ if(p->name){
+ conv = convert_to_utf8(p->name, fromcharset, 0);
+ if(conv){
+ fs_give((void **) &p->name);
+ p->name = conv;
+ }
+ }
+ }
+ }
+
+ if(args->data.mail.attachlist){
+ PATMT *p;
+
+ for(p = args->data.mail.attachlist; p; p=p->next){
+ if(p->filename){
+ conv = convert_to_utf8(p->filename, fromcharset, 0);
+ if(conv){
+ fs_give((void **) &p->filename);
+ p->filename = conv;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+void
+preopen_stayopen_folders(void)
+{
+ char **open_these;
+
+ for(open_these = ps_global->VAR_PERMLOCKED;
+ open_these && *open_these; open_these++)
+ (void) do_broach_folder(*open_these, ps_global->context_list,
+ NULL, DB_NOVISIT);
+}
+
+
+/*
+ * read_stdin_char - simple function to return a character from
+ * redirected stdin
+ */
+int
+read_stdin_char(char *c)
+{
+ int rv;
+
+ /* it'd probably be a good idea to fix this to pre-read blocks */
+ while(1){
+ rv = read(PIPED_FD, c, 1);
+ if(rv < 0){
+ if(errno == EINTR){
+ dprint((2, "read_stdin_char: read interrupted, restarting\n"));
+ continue;
+ }
+ else
+ dprint((1, "read_stdin_char: read FAILED: %s\n",
+ error_description(errno)));
+ }
+ break;
+ }
+ return(rv);
+}
+
+
+/* this default is from the array of structs below */
+#define DEFAULT_MENU_ITEM ((unsigned) 3) /* LIST FOLDERS */
+#define ABOOK_MENU_ITEM ((unsigned) 4) /* ADDRESS BOOK */
+#define MAX_MENU_ITEM ((unsigned) 6)
+/*
+ * Skip this many spaces between rows of main menu screen.
+ * We have MAX_MENU_ITEM+1 = # of commands in menu
+ * 1 = copyright line
+ * MAX_MENU_ITEM = rows between commands
+ * 1 = extra row above commands
+ * 1 = row between commands and copyright
+ *
+ * To make it simple, if there is enough room for all of that include all the
+ * extra space, if not, cut it all out.
+ */
+#define MNSKIP(X) (((HEADER_ROWS(X)+FOOTER_ROWS(X)+(MAX_MENU_ITEM+1)+1+MAX_MENU_ITEM+1+1) <= (X)->ttyo->screen_rows) ? 1 : 0)
+
+static unsigned menu_index = DEFAULT_MENU_ITEM;
+
+/*
+ * One of these for each line that gets printed in the middle of the
+ * screen in the main menu.
+ */
+static struct menu_key {
+ char *key_and_name,
+ *news_addition;
+ int key_index; /* index into keymenu array for this cmd */
+} mkeys[] = {
+ /*
+ * TRANSLATORS: These next few are headings on the Main alpine menu.
+ * It's nice if the dashes can be made to line up vertically.
+ */
+ {N_(" %s HELP - Get help using Alpine"),
+ NULL, MAIN_HELP_KEY},
+ {N_(" %s COMPOSE MESSAGE - Compose and send%s a message"),
+ /* TRANSLATORS: We think of sending an email message or posting a news message.
+ The message is shown as Compose and send/post a message */
+ N_("/post"), MAIN_COMPOSE_KEY},
+ {N_(" %s MESSAGE INDEX - View messages in current folder"),
+ NULL, MAIN_INDEX_KEY},
+ {N_(" %s FOLDER LIST - Select a folder%s to view"),
+ /* TRANSLATORS: When news is supported the message above becomes
+ Select a folder OR news group to view */
+ N_(" OR news group"), MAIN_FOLDER_KEY},
+ {N_(" %s ADDRESS BOOK - Update address book"),
+ NULL, MAIN_ADDRESS_KEY},
+ {N_(" %s SETUP - Configure Alpine Options"),
+ NULL, MAIN_SETUP_KEY},
+ /* TRANSLATORS: final Main menu line */
+ {N_(" %s QUIT - Leave the Alpine program"),
+ NULL, MAIN_QUIT_KEY}
+};
+
+
+
+/*----------------------------------------------------------------------
+ display main menu and execute main menu commands
+
+ Args: The usual pine structure
+
+ Result: main menu commands are executed
+
+
+ M A I N M E N U S C R E E N
+
+ Paint the main menu on the screen, get the commands and either execute
+the function or pass back the name of the function to execute for the menu
+selection. Only simple functions that always return here can be executed
+here.
+
+This functions handling of new mail, redrawing, errors and such can
+serve as a template for the other screen that do much the same thing.
+
+There is a loop that fetchs and executes commands until a command to leave
+this screen is given. Then the name of the next screen to display is
+stored in next_screen member of the structure and this function is exited
+with a return.
+
+First a check for new mail is performed. This might involve reading the new
+mail into the inbox which might then cause the screen to be repainted.
+
+Then the general screen painting is done. This is usually controlled
+by a few flags and some other position variables. If they change they
+tell this part of the code what to repaint. This will include cursor
+motion and so on.
+ ----*/
+void
+main_menu_screen(struct pine *pine_state)
+{
+ UCS ch;
+ int cmd, just_a_navigate_cmd, setup_command, km_popped;
+ int notrealinbox;
+ char *new_folder, *utf8str;
+ CONTEXT_S *tc;
+ struct key_menu *km;
+ OtherMenu what;
+ Pos curs_pos;
+
+ ps_global = pine_state;
+ just_a_navigate_cmd = 0;
+ km_popped = 0;
+ menu_index = DEFAULT_MENU_ITEM;
+ what = FirstMenu; /* which keymenu to display */
+ ch = 'x'; /* For display_message 1st time through */
+ pine_state->next_screen = SCREEN_FUN_NULL;
+ pine_state->prev_screen = main_menu_screen;
+ curs_pos.row = pine_state->ttyo->screen_rows-FOOTER_ROWS(pine_state);
+ curs_pos.col = 0;
+ km = &main_keymenu;
+
+ mailcap_free(); /* free resources we won't be using for a while */
+
+ if(!pine_state->painted_body_on_startup
+ && !pine_state->painted_footer_on_startup){
+ pine_state->mangled_screen = 1;
+ }
+
+ dprint((1, "\n\n ---- MAIN_MENU_SCREEN ----\n"));
+
+ while(1){
+ if(km_popped){
+ km_popped--;
+ if(km_popped == 0){
+ clearfooter(pine_state);
+ pine_state->mangled_body = 1;
+ }
+ }
+
+ /*
+ * fix up redrawer just in case some submenu caused it to get
+ * reassigned...
+ */
+ pine_state->redrawer = main_redrawer;
+
+ /*----------- Check for new mail -----------*/
+ if(new_mail(0, NM_TIMING(ch), NM_STATUS_MSG | NM_DEFER_SORT) >= 0)
+ pine_state->mangled_header = 1;
+
+ if(streams_died())
+ pine_state->mangled_header = 1;
+
+ show_main_screen(pine_state, just_a_navigate_cmd, what, km,
+ km_popped, &curs_pos);
+ just_a_navigate_cmd = 0;
+ what = SameMenu;
+
+ /*---- This displays new mail notification, or errors ---*/
+ if(km_popped){
+ FOOTER_ROWS(pine_state) = 3;
+ mark_status_dirty();
+ }
+
+ display_message(ch);
+ if(km_popped){
+ FOOTER_ROWS(pine_state) = 1;
+ mark_status_dirty();
+ }
+
+ if(F_OFF(F_SHOW_CURSOR, ps_global)){
+ curs_pos.row =pine_state->ttyo->screen_rows-FOOTER_ROWS(pine_state);
+ curs_pos.col =0;
+ }
+
+ MoveCursor(curs_pos.row, curs_pos.col);
+
+ /*------ Read the command from the keyboard ----*/
+#ifdef MOUSE
+ mouse_in_content(KEY_MOUSE, -1, -1, 0, 0);
+ register_mfunc(mouse_in_content, HEADER_ROWS(pine_state), 0,
+ pine_state->ttyo->screen_rows-(FOOTER_ROWS(pine_state)+1),
+ pine_state->ttyo->screen_cols);
+#endif
+#if defined(DOS) || defined(OS2)
+ /*
+ * AND pre-build header lines. This works just fine under
+ * DOS since we wait for characters in a loop. Something will
+ * will have to change under UNIX if we want to do the same.
+ */
+ /* while_waiting = build_header_cache; */
+#ifdef _WINDOWS
+ mswin_sethelptextcallback(pcpine_help_main);
+ mswin_mousetrackcallback(pcpine_main_cursor);
+#endif
+#endif
+ ch = READ_COMMAND(&utf8str);
+#ifdef MOUSE
+ clear_mfunc(mouse_in_content);
+#endif
+#if defined(DOS) || defined(OS2)
+/* while_waiting = NULL; */
+#ifdef _WINDOWS
+ mswin_sethelptextcallback(NULL);
+ mswin_mousetrackcallback(NULL);
+#endif
+#endif
+
+ /* No matter what, Quit here always works */
+ if(ch == 'q' || ch == 'Q'){
+ cmd = MC_QUIT;
+ }
+#ifdef DEBUG
+ else if(debug && ch && ch < 0x80 && strchr("123456789", ch)){
+ int olddebug;
+
+ olddebug = debug;
+ debug = ch - '0';
+ if(debug > 7)
+ ps_global->debug_timestamp = 1;
+ else
+ ps_global->debug_timestamp = 0;
+
+ if(debug > 7)
+ ps_global->debug_imap = 4;
+ else if(debug > 6)
+ ps_global->debug_imap = 3;
+ else if(debug > 4)
+ ps_global->debug_imap = 2;
+ else if(debug > 2)
+ ps_global->debug_imap = 1;
+ else
+ ps_global->debug_imap = 0;
+
+ if(ps_global->mail_stream){
+ if(ps_global->debug_imap > 0){
+ mail_debug(ps_global->mail_stream);
+#ifdef _WINDOWS
+ mswin_enableimaptelemetry(TRUE);
+#endif
+ }
+ else{
+ mail_nodebug(ps_global->mail_stream);
+#ifdef _WINDOWS
+ mswin_enableimaptelemetry(FALSE);
+#endif
+ }
+ }
+
+ if(debug > 7 && olddebug <= 7)
+ mail_parameters(NULL, SET_TCPDEBUG, (void *) TRUE);
+ else if(debug <= 7 && olddebug > 7 && !ps_global->debugmem)
+ mail_parameters(NULL, SET_TCPDEBUG, (void *) FALSE);
+
+ dprint((1, "*** Debug level set to %d ***\n", debug));
+ if(debugfile)
+ fflush(debugfile);
+
+ q_status_message1(SM_ORDER, 0, 1, _("Debug level set to %s"),
+ int2string(debug));
+ continue;
+ }
+#endif /* DEBUG */
+ else{
+ cmd = menu_command(ch, km);
+
+ if(km_popped)
+ switch(cmd){
+ case MC_NONE :
+ case MC_OTHER :
+ case MC_RESIZE :
+ case MC_REPAINT :
+ km_popped++;
+ break;
+
+ default:
+ clearfooter(pine_state);
+ break;
+ }
+ }
+
+ /*------ Execute the command ------*/
+ switch (cmd){
+help_case :
+ /*------ HELP ------*/
+ case MC_HELP :
+
+ if(FOOTER_ROWS(pine_state) == 1 && km_popped == 0){
+ km_popped = 2;
+ pine_state->mangled_footer = 1;
+ }
+ else{
+ /* TRANSLATORS: This is a screen title */
+ helper(main_menu_tx, _("HELP FOR MAIN MENU"), 0);
+ pine_state->mangled_screen = 1;
+ }
+
+ break;
+
+
+ /*---------- display other key bindings ------*/
+ case MC_OTHER :
+ if(ch == 'o')
+ warn_other_cmds();
+
+ what = NextMenu;
+ pine_state->mangled_footer = 1;
+ break;
+
+
+ /*---------- Previous item in menu ----------*/
+ case MC_PREVITEM :
+ if(menu_index > 0) {
+ menu_index--;
+ pine_state->mangled_body = 1;
+ if(km->which == 0)
+ pine_state->mangled_footer = 1;
+
+ just_a_navigate_cmd++;
+ }
+ else
+ /* TRANSLATORS: list refers to list of commands in main menu */
+ q_status_message(SM_ORDER, 0, 2, _("Already at top of list"));
+
+ break;
+
+
+ /*---------- Next item in menu ----------*/
+ case MC_NEXTITEM :
+ if(menu_index < MAX_MENU_ITEM){
+ menu_index++;
+ pine_state->mangled_body = 1;
+ if(km->which == 0)
+ pine_state->mangled_footer = 1;
+
+ just_a_navigate_cmd++;
+ }
+ else
+ q_status_message(SM_ORDER, 0, 2, _("Already at bottom of list"));
+
+ break;
+
+
+ /*---------- Release Notes ----------*/
+ case MC_RELNOTES :
+ /* TRANSLATORS: This is a screen title */
+ helper(h_news, _("ALPINE RELEASE NOTES"), 0);
+ pine_state->mangled_screen = 1;
+ break;
+
+
+#ifdef KEYBOARD_LOCK
+ /*---------- Keyboard lock ----------*/
+ case MC_KBLOCK :
+ (void) lock_keyboard();
+ pine_state->mangled_screen = 1;
+ break;
+#endif /* KEYBOARD_LOCK */
+
+
+ /*---------- Quit pine ----------*/
+ case MC_QUIT :
+ pine_state->next_screen = quit_screen;
+ return;
+
+
+ /*---------- Go to composer ----------*/
+ case MC_COMPOSE :
+ pine_state->next_screen = compose_screen;
+ return;
+
+
+ /*---- Go to alternate composer ------*/
+ case MC_ROLE :
+ pine_state->next_screen = alt_compose_screen;
+ return;
+
+
+ /*---------- Top of Folder list ----------*/
+ case MC_COLLECTIONS :
+ pine_state->next_screen = folder_screen;
+ return;
+
+
+ /*---------- Goto new folder ----------*/
+ case MC_GOTO :
+ tc = ps_global->context_current;
+ new_folder = broach_folder(-FOOTER_ROWS(pine_state), 1, &notrealinbox, &tc);
+ if(new_folder)
+ visit_folder(ps_global, new_folder, tc, NULL, notrealinbox ? 0L : DB_INBOXWOCNTXT);
+
+ return;
+
+
+ /*---------- Go to index ----------*/
+ case MC_INDEX :
+ if(THREADING()
+ && sp_viewing_a_thread(pine_state->mail_stream)
+ && unview_thread(pine_state, pine_state->mail_stream,
+ pine_state->msgmap)){
+ pine_state->view_skipped_index = 0;
+ pine_state->mangled_screen = 1;
+ }
+
+ pine_state->next_screen = mail_index_screen;
+ return;
+
+
+ /*---------- Review Status Messages ----------*/
+ case MC_JOURNAL :
+ review_messages();
+ pine_state->mangled_screen = 1;
+ break;
+
+
+ /*---------- Setup mini menu ----------*/
+ case MC_SETUP :
+setup_case :
+ setup_command = setup_menu(pine_state);
+ pine_state->mangled_footer = 1;
+ do_setup_task(setup_command);
+ if(ps_global->next_screen != main_menu_screen)
+ return;
+
+ break;
+
+
+ /*---------- Go to address book ----------*/
+ case MC_ADDRBOOK :
+ pine_state->next_screen = addr_book_screen;
+ return;
+
+
+ /*------ Repaint the works -------*/
+ case MC_RESIZE :
+ case MC_REPAINT :
+ ClearScreen();
+ pine_state->mangled_screen = 1;
+ break;
+
+
+#ifdef MOUSE
+ /*------- Mouse event ------*/
+ case MC_MOUSE :
+ {
+ MOUSEPRESS mp;
+ unsigned ndmi;
+ struct pine *ps = pine_state;
+
+ mouse_get_last (NULL, &mp);
+
+#ifdef _WINDOWS
+ if(mp.button == M_BUTTON_RIGHT){
+ if(!mp.doubleclick){
+ static MPopup main_popup[] = {
+ {tQueue, {"Folder List", lNormal}, {'L'}},
+ {tQueue, {"Message Index", lNormal}, {'I'}},
+ {tSeparator},
+ {tQueue, {"Address Book", lNormal}, {'A'}},
+ {tQueue, {"Setup Options", lNormal}, {'S'}},
+ {tTail}
+ };
+
+ mswin_popup(main_popup);
+ }
+ }
+ else {
+#endif
+ if (mp.row >= (HEADER_ROWS(ps) + MNSKIP(ps)))
+ ndmi = (mp.row+1 - HEADER_ROWS(ps) - (MNSKIP(ps)+1))/(MNSKIP(ps)+1);
+
+ if (mp.row >= (HEADER_ROWS(ps) + MNSKIP(ps))
+ && !(MNSKIP(ps) && (mp.row+1) & 0x01)
+ && ndmi <= MAX_MENU_ITEM
+ && FOOTER_ROWS(ps) + (ndmi+1)*(MNSKIP(ps)+1)
+ + MNSKIP(ps) + FOOTER_ROWS(ps) <= ps->ttyo->screen_rows){
+ if(mp.doubleclick){
+ switch(ndmi){ /* fake main_screen request */
+ case 0 :
+ goto help_case;
+
+ case 1 :
+ pine_state->next_screen = compose_screen;
+ return;
+
+ case 2 :
+ pine_state->next_screen = mail_index_screen;
+ return;
+
+ case 3 :
+ pine_state->next_screen = folder_screen;
+ return;
+
+ case 4 :
+ pine_state->next_screen = addr_book_screen;
+ return;
+
+ case 5 :
+ goto setup_case;
+
+ case 6 :
+ pine_state->next_screen = quit_screen;
+ return;
+
+ default: /* no op */
+ break;
+ }
+ }
+ else{
+ menu_index = ndmi;
+ pine_state->mangled_body = 1;
+ if(km->which == 0)
+ pine_state->mangled_footer = 1;
+
+ just_a_navigate_cmd++;
+ }
+ }
+#ifdef _WINDOWS
+ }
+#endif
+ }
+
+ break;
+#endif
+
+
+ /*------ Input timeout ------*/
+ case MC_NONE :
+ break; /* noop for timeout loop mail check */
+
+
+ /*------ Bogus Input ------*/
+ case MC_UNKNOWN :
+ if(ch == 'm' || ch == 'M'){
+ q_status_message(SM_ORDER, 0, 1, "Already in Main Menu");
+ break;
+ }
+
+ default:
+ bogus_command(ch, F_ON(F_USE_FK,pine_state) ? "F1" : "?");
+ break;
+
+ case MC_UTF8:
+ bogus_utf8_command(utf8str, F_ON(F_USE_FK, pine_state) ? "F1" : "?");
+ break;
+ } /* the switch */
+ } /* the BIG while loop! */
+}
+
+
+/*----------------------------------------------------------------------
+ Re-Draw the main menu
+
+ Args: none.
+
+ Result: main menu is re-displayed
+ ----*/
+void
+main_redrawer(void)
+{
+ struct key_menu *km = &main_keymenu;
+
+ ps_global->mangled_screen = 1;
+ show_main_screen(ps_global, 0, FirstMenu, km, 0, (Pos *)NULL);
+}
+
+
+/*----------------------------------------------------------------------
+ Draw the main menu
+
+ Args: pine_state - the usual struct
+ quick_draw - tells do_menu() it can skip some drawing
+ what - tells which section of keymenu to draw
+ km - the keymenu
+ cursor_pos - returns a good position for the cursor to be located
+
+ Result: main menu is displayed
+ ----*/
+void
+show_main_screen(struct pine *ps, int quick_draw, OtherMenu what,
+ struct key_menu *km, int km_popped, Pos *cursor_pos)
+{
+ if(ps->painted_body_on_startup || ps->painted_footer_on_startup){
+ ps->mangled_screen = 0; /* only worry about it here */
+ ps->mangled_header = 1; /* we have to redo header */
+ if(!ps->painted_body_on_startup)
+ ps->mangled_body = 1; /* make sure to paint body*/
+
+ if(!ps->painted_footer_on_startup)
+ ps->mangled_footer = 1; /* make sure to paint footer*/
+
+ ps->painted_body_on_startup = 0;
+ ps->painted_footer_on_startup = 0;
+ }
+
+ if(ps->mangled_screen){
+ ps->mangled_header = 1;
+ ps->mangled_body = 1;
+ ps->mangled_footer = 1;
+ ps->mangled_screen = 0;
+ }
+
+#ifdef _WINDOWS
+ /* Reset the scroll range. Main screen never scrolls. */
+ scroll_setrange (0L, 0L);
+ mswin_beginupdate();
+#endif
+
+ /* paint the titlebar if needed */
+ if(ps->mangled_header){
+ /* TRANSLATORS: screen title */
+ set_titlebar(_("MAIN MENU"), ps->mail_stream, ps->context_current,
+ ps->cur_folder, ps->msgmap, 1, FolderName, 0, 0, NULL);
+ ps->mangled_header = 0;
+ }
+
+ /* paint the body if needed */
+ if(ps->mangled_body){
+ if(!quick_draw)
+ ClearBody();
+
+ do_menu(quick_draw, cursor_pos, km);
+ ps->mangled_body = 0;
+ }
+
+ /* paint the keymenu if needed */
+ if(km && ps->mangled_footer){
+ static char label[LONGEST_LABEL + 2 + 1], /* label + brackets + \0 */
+ name[8];
+ bitmap_t bitmap;
+
+ setbitmap(bitmap);
+
+#ifdef KEYBOARD_LOCK
+ if(ps_global->restricted || F_ON(F_DISABLE_KBLOCK_CMD,ps_global))
+#endif
+ clrbitn(MAIN_KBLOCK_KEY, bitmap);
+
+ menu_clear_binding(km, '>');
+ menu_clear_binding(km, '.');
+ menu_clear_binding(km, KEY_RIGHT);
+ menu_clear_binding(km, ctrl('M'));
+ menu_clear_binding(km, ctrl('J'));
+ km->keys[MAIN_DEFAULT_KEY].bind
+ = km->keys[mkeys[menu_index].key_index].bind;
+ km->keys[MAIN_DEFAULT_KEY].label
+ = km->keys[mkeys[menu_index].key_index].label;
+
+ /* put brackets around the default action */
+ snprintf(label, sizeof(label), "[%s]", km->keys[mkeys[menu_index].key_index].label);
+ label[sizeof(label)-1] = '\0';
+ strncpy(name, ">", sizeof(name));
+ name[sizeof(name)-1] = '\0';
+ km->keys[MAIN_DEFAULT_KEY].label = label;
+ km->keys[MAIN_DEFAULT_KEY].name = name;
+ menu_add_binding(km, '>', km->keys[MAIN_DEFAULT_KEY].bind.cmd);
+ menu_add_binding(km, '.', km->keys[MAIN_DEFAULT_KEY].bind.cmd);
+ menu_add_binding(km, ctrl('M'), km->keys[MAIN_DEFAULT_KEY].bind.cmd);
+ menu_add_binding(km, ctrl('J'), km->keys[MAIN_DEFAULT_KEY].bind.cmd);
+
+ if(F_ON(F_ARROW_NAV,ps_global))
+ menu_add_binding(km, KEY_RIGHT, km->keys[MAIN_DEFAULT_KEY].bind.cmd);
+
+ if(km_popped){
+ FOOTER_ROWS(ps) = 3;
+ clearfooter(ps);
+ }
+
+ draw_keymenu(km, bitmap, ps_global->ttyo->screen_cols,
+ 1-FOOTER_ROWS(ps_global), 0, what);
+ ps->mangled_footer = 0;
+ if(km_popped){
+ FOOTER_ROWS(ps) = 1;
+ mark_keymenu_dirty();
+ }
+ }
+
+#ifdef _WINDOWS
+ mswin_endupdate();
+#endif
+}
+
+
+/*----------------------------------------------------------------------
+ Actually display the main menu
+
+ Args: quick_draw - just a next or prev command was typed so we only have
+ to redraw the highlighting
+ cursor_pos - a place to return a good value for cursor location
+
+ Result: Main menu is displayed
+ ---*/
+void
+do_menu(int quick_draw, Pos *cursor_pos, struct key_menu *km)
+{
+ struct pine *ps = ps_global;
+ int dline, indent, longest = 0, cmd;
+ char buf[4*MAX_SCREEN_COLS+1];
+ char buf2[4*MAX_SCREEN_COLS+1];
+ static int last_inverse = -1;
+ Pos pos;
+
+ /* find the longest command */
+ for(cmd = 0; cmd < sizeof(mkeys)/(sizeof(mkeys[1])); cmd++){
+ memset((void *) buf, ' ', sizeof(buf));
+ snprintf(buf, sizeof(buf), mkeys[cmd].key_and_name[0] ? _(mkeys[cmd].key_and_name) : "",
+ (F_OFF(F_USE_FK,ps)
+ && km->keys[mkeys[cmd].key_index].name)
+ ? km->keys[mkeys[cmd].key_index].name : "",
+ (ps->VAR_NEWS_SPEC && mkeys[cmd].news_addition && mkeys[cmd].news_addition[0])
+ ? _(mkeys[cmd].news_addition) : "");
+ buf[sizeof(buf)-1] = '\0';
+
+ if(longest < (indent = utf8_width(buf)))
+ longest = indent;
+ }
+
+ indent = MAX(((ps->ttyo->screen_cols - longest)/2) - 1, 0);
+
+ dline = HEADER_ROWS(ps) + MNSKIP(ps);
+ for(cmd = 0; cmd < sizeof(mkeys)/(sizeof(mkeys[1])); cmd++){
+ /* leave room for copyright and footer */
+ if(dline + MNSKIP(ps) + 1 + FOOTER_ROWS(ps) >= ps->ttyo->screen_rows)
+ break;
+
+ if(quick_draw && !(cmd == last_inverse || cmd == menu_index)){
+ dline += (1 + MNSKIP(ps));
+ continue;
+ }
+
+ if(cmd == menu_index)
+ StartInverse();
+
+ memset((void *) buf, ' ', sizeof(buf));
+ snprintf(buf, sizeof(buf), mkeys[cmd].key_and_name[0] ? _(mkeys[cmd].key_and_name) : "",
+ (F_OFF(F_USE_FK,ps)
+ && km->keys[mkeys[cmd].key_index].name)
+ ? km->keys[mkeys[cmd].key_index].name : "",
+ (ps->VAR_NEWS_SPEC && mkeys[cmd].news_addition && mkeys[cmd].news_addition[0])
+ ? _(mkeys[cmd].news_addition) : "");
+ buf[sizeof(buf)-1] = '\0';
+
+ utf8_pad_to_width(buf2, buf, sizeof(buf2),
+ MIN(ps->ttyo->screen_cols-indent,longest+1), 1);
+ pos.row = dline++;
+ pos.col = indent;
+ PutLine0(pos.row, pos.col, buf2);
+
+ if(MNSKIP(ps))
+ dline++;
+
+ if(cmd == menu_index){
+ if(cursor_pos){
+ cursor_pos->row = pos.row;
+ /* 6 is 1 for the letter plus 5 spaces */
+ cursor_pos->col = pos.col + 6;
+ if(F_OFF(F_USE_FK,ps))
+ cursor_pos->col++;
+
+ cursor_pos->col = MIN(cursor_pos->col, ps->ttyo->screen_cols);
+ }
+
+ EndInverse();
+ }
+ }
+
+
+ last_inverse = menu_index;
+
+ if(!quick_draw && FOOTER_ROWS(ps)+1 < ps->ttyo->screen_rows){
+ utf8_to_width(buf2, LEGAL_NOTICE, sizeof(buf2),
+ ps->ttyo->screen_cols-3, NULL);
+ PutLine0(ps->ttyo->screen_rows - (FOOTER_ROWS(ps)+1),
+ MAX(0, ((ps->ttyo->screen_cols-utf8_width(buf2))/2)),
+ buf2);
+ }
+
+ fflush(stdout);
+}
+
+
+int
+choose_setup_cmd(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
+{
+ int rv = 1;
+ SRV_S *srv;
+
+ if(!(srv = (SRV_S *)sparms->proc.data.p)){
+ sparms->proc.data.p = (SRV_S *)fs_get(sizeof(*srv));
+ srv = (SRV_S *)sparms->proc.data.p;
+ memset(srv, 0, sizeof(*srv));
+ }
+
+ ps_global->next_screen = SCREEN_FUN_NULL;
+
+ switch(cmd){
+ case MC_PRINTER :
+ srv->cmd = 'p';
+ break;
+
+ case MC_PASSWD :
+ srv->cmd = 'n';
+ break;
+
+ case MC_CONFIG :
+ srv->cmd = 'c';
+ break;
+
+ case MC_SIG :
+ srv->cmd = 's';
+ break;
+
+ case MC_ABOOKS :
+ srv->cmd = 'a';
+ break;
+
+ case MC_CLISTS :
+ srv->cmd = 'l';
+ break;
+
+ case MC_RULES :
+ srv->cmd = 'r';
+ break;
+
+ case MC_DIRECTORY :
+ srv->cmd = 'd';
+ break;
+
+ case MC_KOLOR :
+ srv->cmd = 'k';
+ break;
+
+ case MC_REMOTE :
+ srv->cmd = 'z';
+ break;
+
+ case MC_SECURITY : /* S/MIME setup screen */
+ srv->cmd = 'm';
+ break;
+
+ case MC_EXCEPT :
+ srv->exc = !srv->exc;
+ menu_clear_binding(sparms->keys.menu, 'x');
+ if(srv->exc){
+ if(sparms->bar.title) fs_give((void **)&sparms->bar.title);
+ /* TRANSLATORS: screen title */
+ sparms->bar.title = cpystr(_("SETUP EXCEPTIONS"));
+ ps_global->mangled_header = 1;
+ /* TRANSLATORS: The reason the X is upper case in eXceptions
+ is because the command key is X. It isn't necessary, just
+ nice if it works. */
+ menu_init_binding(sparms->keys.menu, 'x', MC_EXCEPT, "X",
+ N_("not eXceptions"), SETUP_EXCEPT);
+ }
+ else{
+ if(sparms->bar.title) fs_give((void **)&sparms->bar.title);
+ /* TRANSLATORS: screen title */
+ sparms->bar.title = cpystr(_("SETUP"));
+ ps_global->mangled_header = 1;
+ menu_init_binding(sparms->keys.menu, 'x', MC_EXCEPT, "X",
+ N_("eXceptions"), SETUP_EXCEPT);
+ }
+
+ if(sparms->keys.menu->which == 1)
+ ps_global->mangled_footer = 1;
+
+ rv = 0;
+ break;
+
+ case MC_NO_EXCEPT :
+#if defined(DOS) || defined(OS2)
+ q_status_message(SM_ORDER, 0, 2, _("Need argument \"-x <except_config>\" or \"PINERCEX\" file to use eXceptions"));
+#else
+ q_status_message(SM_ORDER, 0, 2, _("Need argument \"-x <except_config>\" or \".pinercex\" file to use eXceptions"));
+#endif
+ rv = 0;
+ break;
+
+ default:
+ panic("Unexpected command in choose_setup_cmd");
+ break;
+ }
+
+ return(rv);
+}
+
+
+int
+setup_menu(struct pine *ps)
+{
+ int ret = 0, exceptions = 0;
+ int printer = 0, passwd = 0, config = 0, sig = 0, dir = 0, smime = 0, exc = 0;
+ SCROLL_S sargs;
+ SRV_S *srv;
+ STORE_S *store;
+
+ if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
+ q_status_message(SM_ORDER | SM_DING, 3, 3, _("Error allocating space."));
+ return(ret);
+ }
+
+#if !defined(DOS)
+ if(!ps_global->vars[V_PRINTER].is_fixed) /* printer can be changed */
+ printer++;
+#endif
+
+#ifdef PASSWD_PROG
+ if(F_OFF(F_DISABLE_PASSWORD_CMD,ps_global)) /* password is allowed */
+ passwd++;
+#endif
+
+ if(F_OFF(F_DISABLE_CONFIG_SCREEN,ps_global)) /* config allowed */
+ config++;
+
+ if(F_OFF(F_DISABLE_SIGEDIT_CMD,ps_global)) /* .sig editing is allowed */
+ sig++;
+
+#ifdef ENABLE_LDAP
+ dir++;
+#endif
+
+#ifdef SMIME
+ smime++;
+#endif
+
+ if(ps_global->post_prc)
+ exc++;
+
+ /* TRANSLATORS: starting here we have a whole screen of help text */
+ so_puts(store, _("This is the Setup screen for Alpine. Choose from the following commands:\n"));
+
+ so_puts(store, "\n");
+ so_puts(store, _("(E) Exit Setup:\n"));
+ so_puts(store, _(" This puts you back at the Main Menu.\n"));
+
+ if(exc){
+ so_puts(store, "\n");
+ so_puts(store, _("(X) eXceptions:\n"));
+ so_puts(store, _(" This command is different from the rest. It is not actually a command\n"));
+ so_puts(store, _(" itself. Instead, it is a toggle which modifies the behavior of the\n"));
+ so_puts(store, _(" other commands. You toggle Exceptions editing on and off with this\n"));
+ so_puts(store, _(" command. When it is off you will be editing (changing) your regular\n"));
+ so_puts(store, _(" configuration file. When it is on you will be editing your exceptions\n"));
+ so_puts(store, _(" configuration file. For example, you might want to type the command \n"));
+ so_puts(store, _(" \"eXceptions\" followed by \"Kolor\" to setup different screen colors\n"));
+ so_puts(store, _(" on a particular platform.\n"));
+ so_puts(store, _(" (Note: this command does not show up on the keymenu at the bottom of\n"));
+ so_puts(store, _(" the screen unless you press \"O\" for \"Other Commands\" --but you don't\n"));
+ so_puts(store, _(" need to press the \"O\" in order to invoke the command.)\n"));
+ }
+
+ if(printer){
+ so_puts(store, "\n");
+ so_puts(store, _("(P) Printer:\n"));
+ so_puts(store, _(" Allows you to set a default printer and to define custom\n"));
+ so_puts(store, _(" print commands.\n"));
+ }
+
+ if(passwd){
+ so_puts(store, "\n");
+ so_puts(store, _("(N) Newpassword:\n"));
+ so_puts(store, _(" Change your password.\n"));
+ }
+
+ if(config){
+ so_puts(store, "\n");
+ so_puts(store, _("(C) Config:\n"));
+ so_puts(store, _(" Allows you to set or unset many features of Alpine.\n"));
+ so_puts(store, _(" You may also set the values of many options with this command.\n"));
+ }
+
+ if(sig){
+ so_puts(store, "\n");
+ so_puts(store, _("(S) Signature:\n"));
+ so_puts(store, _(" Enter or edit a custom signature which will\n"));
+ so_puts(store, _(" be included with each new message you send.\n"));
+ }
+
+ so_puts(store, "\n");
+ so_puts(store, _("(A) AddressBooks:\n"));
+ so_puts(store, _(" Define a non-default address book.\n"));
+
+ so_puts(store, "\n");
+ so_puts(store, _("(L) collectionLists:\n"));
+ so_puts(store, _(" You may define groups of folders to help you better organize your mail.\n"));
+
+ so_puts(store, "\n");
+ so_puts(store, _("(R) Rules:\n"));
+ so_puts(store, _(" This has up to six sub-categories: Roles, Index Colors, Filters,\n"));
+ so_puts(store, _(" SetScores, Search, and Other. If the Index Colors option is\n"));
+ so_puts(store, _(" missing you may turn it on (if possible) with Setup/Kolor.\n"));
+ so_puts(store, _(" If Roles is missing it has probably been administratively disabled.\n"));
+
+ if(dir){
+ so_puts(store, "\n");
+ so_puts(store, _("(D) Directory:\n"));
+ so_puts(store, _(" Define an LDAP Directory server for Alpine's use. A directory server is\n"));
+ so_puts(store, _(" similar to an address book, but it is usually maintained by an\n"));
+ so_puts(store, _(" organization. It is similar to a telephone directory.\n"));
+ }
+
+ so_puts(store, "\n");
+ so_puts(store, _("(K) Kolor:\n"));
+ so_puts(store, _(" Set custom colors for various parts of the Alpine screens. For example, the\n"));
+ so_puts(store, _(" command key labels, the titlebar at the top of each page, and quoted\n"));
+ so_puts(store, _(" sections of messages you are viewing.\n"));
+
+ if(smime){
+ so_puts(store, "\n");
+ so_puts(store, _("(M) S/MIME:\n"));
+ so_puts(store, _(" Setup for using S/MIME to verify signed messages, decrypt\n"));
+ so_puts(store, _(" encrypted messages, and to sign or encrypt outgoing messages.\n"));
+ }
+
+ so_puts(store, "\n");
+ so_puts(store, _("(Z) RemoteConfigSetup:\n"));
+ so_puts(store, _(" This is a command you will probably only want to use once, if at all.\n"));
+ so_puts(store, _(" It helps you transfer your Alpine configuration data to an IMAP server,\n"));
+ so_puts(store, _(" where it will be accessible from any of the computers you read mail\n"));
+ so_puts(store, _(" from (using Alpine). The idea behind a remote configuration is that you\n"));
+ so_puts(store, _(" can change your configuration in one place and have that change show\n"));
+ so_puts(store, _(" up on all of the computers you use.\n"));
+ so_puts(store, _(" (Note: this command does not show up on the keymenu at the bottom of\n"));
+ so_puts(store, _(" the screen unless you press \"O\" for \"Other Commands\" --but you don't\n"));
+ so_puts(store, _(" need to press the \"O\" in order to invoke the command.)\n"));
+
+ /* put this down here for people who don't have exceptions */
+ if(!exc){
+ so_puts(store, "\n");
+ so_puts(store, _("(X) eXceptions:\n"));
+ so_puts(store, _(" This command is different from the rest. It is not actually a command\n"));
+ so_puts(store, _(" itself. Instead, it is a toggle which modifies the behavior of the\n"));
+ so_puts(store, _(" other commands. You toggle Exceptions editing on and off with this\n"));
+ so_puts(store, _(" command. When it is off you will be editing (changing) your regular\n"));
+ so_puts(store, _(" configuration file. When it is on you will be editing your exceptions\n"));
+ so_puts(store, _(" configuration file. For example, you might want to type the command \n"));
+ so_puts(store, _(" \"eXceptions\" followed by \"Kolor\" to setup different screen colors\n"));
+ so_puts(store, _(" on a particular platform.\n"));
+ so_puts(store, _(" (Note: this command does not do anything unless you have a configuration\n"));
+ so_puts(store, _(" with exceptions enabled (you don't have that). Common ways to enable an\n"));
+ so_puts(store, _(" exceptions config are the command line argument \"-x <exception_config>\";\n"));
+ so_puts(store, _(" or the existence of the file \".pinercex\" for Unix Alpine, or \"PINERCEX\")\n"));
+ so_puts(store, _(" for PC-Alpine.)\n"));
+ so_puts(store, _(" (Another note: this command does not show up on the keymenu at the bottom\n"));
+ so_puts(store, _(" of the screen unless you press \"O\" for \"Other Commands\" --but you\n"));
+ so_puts(store, _(" don't need to press the \"O\" in order to invoke the command.)\n"));
+ }
+
+ memset(&sargs, 0, sizeof(SCROLL_S));
+ sargs.text.text = so_text(store);
+ sargs.text.src = CharStar;
+ sargs.text.desc = _("Information About Setup Command");
+ sargs.bar.title = cpystr(_("SETUP"));
+ sargs.proc.tool = choose_setup_cmd;
+ sargs.help.text = NO_HELP;
+ sargs.help.title = NULL;
+ sargs.keys.menu = &choose_setup_keymenu;
+ sargs.keys.menu->how_many = 2;
+
+ setbitmap(sargs.keys.bitmap);
+ if(!printer)
+ clrbitn(SETUP_PRINTER, sargs.keys.bitmap);
+
+ if(!passwd)
+ clrbitn(SETUP_PASSWD, sargs.keys.bitmap);
+
+ if(!config)
+ clrbitn(SETUP_CONFIG, sargs.keys.bitmap);
+
+ if(!sig)
+ clrbitn(SETUP_SIG, sargs.keys.bitmap);
+
+ if(!dir)
+ clrbitn(SETUP_DIRECTORY, sargs.keys.bitmap);
+
+ if(!smime)
+ clrbitn(SETUP_SMIME, sargs.keys.bitmap);
+
+ if(exc)
+ menu_init_binding(sargs.keys.menu, 'x', MC_EXCEPT, "X",
+ N_("eXceptions"), SETUP_EXCEPT);
+ else
+ menu_init_binding(sargs.keys.menu, 'x', MC_NO_EXCEPT, "X",
+ N_("eXceptions"), SETUP_EXCEPT);
+
+
+ scrolltool(&sargs);
+
+ ps->mangled_screen = 1;
+
+ srv = (SRV_S *)sargs.proc.data.p;
+
+ exceptions = srv ? srv->exc : 0;
+
+ so_give(&store);
+
+ if(sargs.bar.title) fs_give((void**)&sargs.bar.title);
+ if(srv){
+ ret = srv->cmd;
+ fs_give((void **)&sargs.proc.data.p);
+ }
+ else
+ ret = 'e';
+
+ return(ret | (exceptions ? EDIT_EXCEPTION : 0));
+}
+
+
+/*----------------------------------------------------------------------
+
+Args: command -- command char to perform
+
+ ----*/
+void
+do_setup_task(int command)
+{
+ char *err = NULL;
+ int rtype;
+ int edit_exceptions = 0;
+ int do_lit_sig = 0;
+
+ if(command & EDIT_EXCEPTION){
+ edit_exceptions = 1;
+ command &= ~EDIT_EXCEPTION;
+ }
+
+ switch(command) {
+ /*----- EDIT SIGNATURE -----*/
+ case 's':
+ if(ps_global->VAR_LITERAL_SIG)
+ do_lit_sig = 1;
+ else {
+ char sig_path[MAXPATH+1];
+
+ if(!signature_path(ps_global->VAR_SIGNATURE_FILE, sig_path, MAXPATH))
+ do_lit_sig = 1;
+ else if((!IS_REMOTE(ps_global->VAR_SIGNATURE_FILE)
+ && can_access(sig_path, READ_ACCESS) == 0)
+ ||(IS_REMOTE(ps_global->VAR_SIGNATURE_FILE)
+ && (folder_exists(NULL, sig_path) & FEX_ISFILE)))
+ do_lit_sig = 0;
+ else if(!ps_global->vars[V_SIGNATURE_FILE].main_user_val.p
+ && !ps_global->vars[V_SIGNATURE_FILE].cmdline_val.p
+ && !ps_global->vars[V_SIGNATURE_FILE].fixed_val.p)
+ do_lit_sig = 1;
+ else
+ do_lit_sig = 0;
+ }
+
+ if(do_lit_sig){
+ char *result = NULL;
+ char **apval;
+ EditWhich ew;
+ int readonly = 0;
+
+ ew = edit_exceptions ? ps_global->ew_for_except_vars : Main;
+
+ if(ps_global->restricted)
+ readonly = 1;
+ else switch(ew){
+ case Main:
+ readonly = ps_global->prc->readonly;
+ break;
+ case Post:
+ readonly = ps_global->post_prc->readonly;
+ break;
+ default:
+ break;
+ }
+
+ if(readonly)
+ err = cpystr(ps_global->restricted
+ ? "Alpine demo can't change config file"
+ : _("Config file not changeable"));
+
+ if(!err){
+ apval = APVAL(&ps_global->vars[V_LITERAL_SIG], ew);
+ if(!apval)
+ err = cpystr(_("Problem accessing configuration"));
+ else{
+ char *input;
+
+ input = (char *)fs_get((strlen(*apval ? *apval : "")+1) *
+ sizeof(char));
+ input[0] = '\0';
+ cstring_to_string(*apval, input);
+ err = signature_edit_lit(input, &result,
+ _("SIGNATURE EDITOR"),
+ h_composer_sigedit);
+ fs_give((void **)&input);
+ }
+ }
+
+ if(!err){
+ char *cstring_version;
+
+ cstring_version = string_to_cstring(result);
+
+ set_variable(V_LITERAL_SIG, cstring_version, 0, 0, ew);
+
+ if(cstring_version)
+ fs_give((void **)&cstring_version);
+ }
+
+ if(result)
+ fs_give((void **)&result);
+ }
+ else
+ err = signature_edit(ps_global->VAR_SIGNATURE_FILE,
+ _("SIGNATURE EDITOR"));
+
+ if(err){
+ q_status_message(SM_ORDER, 3, 4, err);
+ fs_give((void **)&err);
+ }
+
+ ps_global->mangled_screen = 1;
+ break;
+
+ /*----- ADD ADDRESSBOOK ----*/
+ case 'a':
+ addr_book_config(ps_global, edit_exceptions);
+ menu_index = ABOOK_MENU_ITEM;
+ ps_global->mangled_screen = 1;
+ break;
+
+#ifdef ENABLE_LDAP
+ /*--- ADD DIRECTORY SERVER --*/
+ case 'd':
+ directory_config(ps_global, edit_exceptions);
+ ps_global->mangled_screen = 1;
+ break;
+#endif
+
+#ifdef SMIME
+ /*--- S/MIME --*/
+ case 'm':
+ smime_config_screen(ps_global, edit_exceptions);
+ ps_global->mangled_screen = 1;
+ break;
+#endif
+
+ /*----- CONFIGURE OPTIONS -----*/
+ case 'c':
+ option_screen(ps_global, edit_exceptions);
+ ps_global->mangled_screen = 1;
+ break;
+
+ /*----- COLLECTION LIST -----*/
+ case 'l':
+ folder_config_screen(ps_global, edit_exceptions);
+ ps_global->mangled_screen = 1;
+ break;
+
+ /*----- RULES -----*/
+ case 'r':
+ rtype = rule_setup_type(ps_global, RS_RULES | RS_INCFILTNOW,
+ _("Type of rule setup : "));
+ switch(rtype){
+ case 'r':
+ case 's':
+ case 'i':
+ case 'f':
+ case 'o':
+ case 'c':
+ role_config_screen(ps_global, (rtype == 'r') ? ROLE_DO_ROLES :
+ (rtype == 's') ? ROLE_DO_SCORES :
+ (rtype == 'o') ? ROLE_DO_OTHER :
+ (rtype == 'f') ? ROLE_DO_FILTER :
+ (rtype == 'c') ? ROLE_DO_SRCH :
+ ROLE_DO_INCOLS,
+ edit_exceptions);
+ break;
+
+ case 'Z':
+ q_status_message(SM_ORDER | SM_DING, 3, 5,
+ _("Try turning on color with the Setup/Kolor command."));
+ break;
+
+ case 'n':
+ role_process_filters();
+ break;
+
+ default:
+ cmd_cancelled(NULL);
+ break;
+ }
+
+ ps_global->mangled_screen = 1;
+ break;
+
+ /*----- COLOR -----*/
+ case 'k':
+ color_config_screen(ps_global, edit_exceptions);
+ ps_global->mangled_screen = 1;
+ break;
+
+ case 'z':
+ convert_to_remote_config(ps_global, edit_exceptions);
+ ps_global->mangled_screen = 1;
+ break;
+
+ /*----- EXIT -----*/
+ case 'e':
+ break;
+
+ /*----- NEW PASSWORD -----*/
+ case 'n':
+#ifdef PASSWD_PROG
+ if(ps_global->restricted){
+ q_status_message(SM_ORDER, 3, 5,
+ "Password change unavailable in restricted demo version of Alpine.");
+ }else {
+ change_passwd();
+ ClearScreen();
+ ps_global->mangled_screen = 1;
+ }
+#else
+ q_status_message(SM_ORDER, 0, 5,
+ _("Password changing not configured for this version of Alpine."));
+ display_message('x');
+#endif /* DOS */
+ break;
+
+#if !defined(DOS)
+ /*----- CHOOSE PRINTER ------*/
+ case 'p':
+ select_printer(ps_global, edit_exceptions);
+ ps_global->mangled_screen = 1;
+ break;
+#endif
+ }
+}
+
+
+int
+rule_setup_type(struct pine *ps, int flags, char *prompt)
+{
+ ESCKEY_S opts[9];
+ int ekey_num = 0, deefault = 0;
+
+ if(flags & RS_INCADDR){
+ deefault = 'a';
+ opts[ekey_num].ch = 'a';
+ opts[ekey_num].rval = 'a';
+ opts[ekey_num].name = "A";
+ opts[ekey_num++].label = "Addrbook";
+ }
+
+ if(flags & RS_RULES){
+
+ if(F_OFF(F_DISABLE_ROLES_SETUP,ps)){ /* roles are allowed */
+ if(deefault != 'a')
+ deefault = 'r';
+
+ opts[ekey_num].ch = 'r';
+ opts[ekey_num].rval = 'r';
+ opts[ekey_num].name = "R";
+ opts[ekey_num++].label = "Roles";
+ }
+ else if(deefault != 'a')
+ deefault = 's';
+
+ opts[ekey_num].ch = 's';
+ opts[ekey_num].rval = 's';
+ opts[ekey_num].name = "S";
+ opts[ekey_num++].label = "SetScores";
+
+#ifndef _WINDOWS
+ if(ps->color_style != COL_NONE && pico_hascolor()){
+#endif
+ if(deefault != 'a')
+ deefault = 'i';
+
+ opts[ekey_num].ch = 'i';
+ opts[ekey_num].rval = 'i';
+ opts[ekey_num].name = "I";
+ opts[ekey_num++].label = "Indexcolor";
+#ifndef _WINDOWS
+ }
+ else{
+ opts[ekey_num].ch = 'i';
+ opts[ekey_num].rval = 'Z'; /* notice this rval! */
+ opts[ekey_num].name = "I";
+ opts[ekey_num++].label = "Indexcolor";
+ }
+#endif
+
+ opts[ekey_num].ch = 'f';
+ opts[ekey_num].rval = 'f';
+ opts[ekey_num].name = "F";
+ opts[ekey_num++].label = "Filters";
+
+ opts[ekey_num].ch = 'o';
+ opts[ekey_num].rval = 'o';
+ opts[ekey_num].name = "O";
+ opts[ekey_num++].label = "Other";
+
+ opts[ekey_num].ch = 'c';
+ opts[ekey_num].rval = 'c';
+ opts[ekey_num].name = "C";
+ opts[ekey_num++].label = "searCh";
+
+ }
+
+ if(flags & RS_INCEXP){
+ opts[ekey_num].ch = 'e';
+ opts[ekey_num].rval = 'e';
+ opts[ekey_num].name = "E";
+ opts[ekey_num++].label = "Export";
+ }
+
+ if(flags & RS_INCFILTNOW){
+ opts[ekey_num].ch = 'n';
+ opts[ekey_num].rval = 'n';
+ opts[ekey_num].name = "N";
+ opts[ekey_num++].label = "filterNow";
+ }
+
+ opts[ekey_num].ch = -1;
+
+ return(radio_buttons(prompt, -FOOTER_ROWS(ps), opts,
+ deefault, 'x', NO_HELP, RB_NORM));
+}
+
+
+
+/*
+ * Process the command list, changing function key notation into
+ * lexical equivalents.
+ */
+void
+process_init_cmds(struct pine *ps, char **list)
+{
+ char **p;
+ int i = 0;
+ int j;
+ int lpm1;
+#define MAX_INIT_CMDS 500
+ /* this is just a temporary stack array, the real one is allocated below */
+ int i_cmds[MAX_INIT_CMDS];
+ int fkeys = 0;
+ int not_fkeys = 0;
+
+ if(list){
+ for(p = list; *p; p++){
+ if(i >= MAX_INIT_CMDS){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,
+ "Initial keystroke list too long at \"%s\"", *p);
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ break;
+ }
+
+
+ /* regular character commands */
+ if(strlen(*p) == 1){
+ i_cmds[i++] = **p;
+ not_fkeys++;
+ }
+
+ /* special commands */
+ else if(strucmp(*p, "SPACE") == 0)
+ i_cmds[i++] = ' ';
+ else if(strucmp(*p, "CR") == 0)
+ i_cmds[i++] = '\n';
+ else if(strucmp(*p, "TAB") == 0)
+ i_cmds[i++] = '\t';
+ else if(strucmp(*p, "UP") == 0)
+ i_cmds[i++] = KEY_UP;
+ else if(strucmp(*p, "DOWN") == 0)
+ i_cmds[i++] = KEY_DOWN;
+ else if(strucmp(*p, "LEFT") == 0)
+ i_cmds[i++] = KEY_LEFT;
+ else if(strucmp(*p, "RIGHT") == 0)
+ i_cmds[i++] = KEY_RIGHT;
+
+ /* control chars */
+ else if(strlen(*p) == 2 && **p == '^')
+ i_cmds[i++] = ctrl(*((*p)+1));
+
+ /* function keys */
+ else if(**p == 'F' || **p == 'f'){
+ int v;
+
+ fkeys++;
+ v = atoi((*p)+1);
+ if(v >= 1 && v <= 12)
+ i_cmds[i++] = PF1 + v - 1;
+ else
+ i_cmds[i++] = KEY_JUNK;
+ }
+
+ /* literal string */
+ else if(**p == '"' && (*p)[lpm1 = strlen(*p) - 1] == '"'){
+ if(lpm1 + i - 1 > MAX_INIT_CMDS){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,
+ "Initial keystroke list too long, truncated at %s\n", *p);
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ break; /* Bail out of this loop! */
+ } else
+ for(j = 1; j < lpm1; j++)
+ i_cmds[i++] = (*p)[j];
+ }
+ else {
+ snprintf(tmp_20k_buf,SIZEOF_20KBUF,
+ "Bad initial keystroke \"%.500s\" (missing comma?)", *p);
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ break;
+ }
+ }
+ }
+
+ /*
+ * We don't handle the case where function keys are used to specify the
+ * commands but some non-function key input is also required. For example,
+ * you might want to jump to a specific message number and view it
+ * on start up. To do that, you need to use character commands instead
+ * of function key commands in the initial-keystroke-list.
+ */
+ if(fkeys && not_fkeys){
+ init_error(ps, SM_ORDER | SM_DING, 3, 5,
+"Mixed characters and function keys in \"initial-keystroke-list\", skipping.");
+ i = 0;
+ }
+
+ if(fkeys && !not_fkeys)
+ F_TURN_ON(F_USE_FK,ps);
+ if(!fkeys && not_fkeys)
+ F_TURN_OFF(F_USE_FK,ps);
+
+ if(i > 0){
+ ps->initial_cmds = (int *)fs_get((i+1) * sizeof(int));
+ ps->free_initial_cmds = ps->initial_cmds;
+ for(j = 0; j < i; j++)
+ ps->initial_cmds[j] = i_cmds[j];
+
+ ps->initial_cmds[i] = 0;
+ ps->in_init_seq = ps->save_in_init_seq = 1;
+ }
+}
+
+
+UCS *
+user_wordseps(char **list)
+ {
+ char **p;
+ int i = 0;
+ int j;
+#define MAX_SEPARATORS 500
+ /*
+ * This is just a temporary stack array, the real one is allocated below.
+ * This is supposed to be way large enough.
+ */
+ UCS seps[MAX_SEPARATORS+1];
+ UCS *u;
+ UCS *return_array = NULL;
+ size_t l;
+
+ seps[0] = '\0';
+
+ if(list){
+ for(p = list; *p; p++){
+ if(i >= MAX_SEPARATORS){
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ "Warning: composer-word-separators list is too long");
+ break;
+ }
+
+ u = utf8_to_ucs4_cpystr(*p);
+
+ if(u){
+ if(ucs4_strlen(u) == 1)
+ seps[i++] = *u;
+ else if(*u == '"' && u[l = ucs4_strlen(u) - 1] == '"'){
+ if(l + i - 1 > MAX_SEPARATORS){
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ "Warning: composer-word-separators list is too long");
+ break; /* Bail out of this loop! */
+ }
+ else{
+ for(j = 1; j < l; j++)
+ seps[i++] = u[j];
+ }
+ }
+ else{
+ l = ucs4_strlen(u);
+ if(l + i > MAX_SEPARATORS){
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ "Warning: composer-word-separators list is too long");
+ break; /* Bail out of this loop! */
+ }
+ else{
+ for(j = 0; j < l; j++)
+ seps[i++] = u[j];
+ }
+ }
+
+ fs_give((void **) &u);
+ }
+ }
+ }
+
+ seps[i] = '\0';
+
+ if(i > 0)
+ return_array = ucs4_cpystr(seps);
+
+ return(return_array);
+}
+
+
+/*
+ * Make sure any errors during initialization get queued for display
+ */
+void
+queue_init_errors(struct pine *ps)
+{
+ int i;
+
+ if(ps->init_errs){
+ for(i = 0; (ps->init_errs)[i].message; i++){
+ q_status_message((ps->init_errs)[i].flags,
+ (ps->init_errs)[i].min_time,
+ (ps->init_errs)[i].max_time,
+ (ps->init_errs)[i].message);
+ fs_give((void **)&(ps->init_errs)[i].message);
+ }
+
+ fs_give((void **)&ps->init_errs);
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Quit pine if the user wants to
+
+ Args: The usual pine structure
+
+ Result: User is asked if she wants to quit, if yes then execute quit.
+
+ Q U I T S C R E E N
+
+Not really a full screen. Just count up deletions and ask if we really
+want to quit.
+ ----*/
+void
+quit_screen(struct pine *pine_state)
+{
+ int quit = 0;
+
+ dprint((1, "\n\n ---- QUIT SCREEN ----\n"));
+
+ if(F_ON(F_CHECK_MAIL_ONQUIT,ps_global)
+ && new_mail(1, VeryBadTime, NM_STATUS_MSG | NM_DEFER_SORT) > 0
+ && (quit = want_to(_("Quit even though new mail just arrived"), 'y', 0,
+ NO_HELP, WT_NORM)) != 'y'){
+ refresh_sort(pine_state->mail_stream, pine_state->msgmap, SRT_VRB);
+ pine_state->next_screen = pine_state->prev_screen;
+ return;
+ }
+
+ if(quit != 'y'
+ && F_OFF(F_QUIT_WO_CONFIRM,pine_state)
+ && want_to(_("Really quit Alpine"), 'y', 0, NO_HELP, WT_NORM) != 'y'){
+ pine_state->next_screen = pine_state->prev_screen;
+ return;
+ }
+
+ goodnight_gracey(pine_state, 0);
+}
+
+
+/*----------------------------------------------------------------------
+ The nuts and bolts of actually cleaning up and exitting pine
+
+ Args: ps -- the usual pine structure,
+ exit_val -- what to tell our parent
+
+ Result: This never returns
+
+ ----*/
+void
+goodnight_gracey(struct pine *pine_state, int exit_val)
+{
+ int i, cnt_user_streams = 0;
+ char *final_msg = NULL;
+ char msg[MAX_SCREEN_COLS+1];
+ char *pf = _("Alpine finished");
+ MAILSTREAM *m;
+ extern KBESC_T *kbesc;
+
+ dprint((2, "goodnight_gracey:\n"));
+
+ /* We want to do this here before we close up the streams */
+ trim_remote_adrbks();
+
+ for(i = 0; i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR))
+ cnt_user_streams++;
+ }
+
+ /* clean up open streams */
+
+ if(pine_state->mail_stream
+ && sp_flagged(pine_state->mail_stream, SP_LOCKED)
+ && sp_flagged(pine_state->mail_stream, SP_USERFLDR)){
+ dprint((5, "goodnight_gracey: close current stream\n"));
+ expunge_and_close(pine_state->mail_stream,
+ (cnt_user_streams <= 1) ? &final_msg : NULL, EC_NONE);
+ cnt_user_streams--;
+ }
+
+ pine_state->mail_stream = NULL;
+ pine_state->redrawer = (void (*)(void))NULL;
+
+ dprint((5,
+ "goodnight_gracey: close other stream pool streams\n"));
+ for(i = 0; i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ /*
+ * fix global for functions that depend(ed) on it sort_folder.
+ * Hopefully those will get phased out.
+ */
+ ps_global->mail_stream = m;
+ if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR)
+ && !sp_flagged(m, SP_INBOX)){
+ sp_set_expunge_count(m, 0L);
+ expunge_and_close(m, (cnt_user_streams <= 1) ? &final_msg : NULL,
+ EC_NONE);
+ cnt_user_streams--;
+ }
+ }
+
+ for(i = 0; i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ /*
+ * fix global for functions that depend(ed) on it (sort_folder).
+ * Hopefully those will get phased out.
+ */
+ ps_global->mail_stream = m;
+ if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR)
+ && sp_flagged(m, SP_INBOX)){
+ dprint((5,
+ "goodnight_gracey: close inbox stream stream\n"));
+ sp_set_expunge_count(m, 0L);
+ expunge_and_close(m, (cnt_user_streams <= 1) ? &final_msg : NULL,
+ EC_NONE);
+ cnt_user_streams--;
+ }
+ }
+
+#ifdef _WINDOWS
+ if(ps_global->ttyo)
+ (void)get_windsize(ps_global->ttyo);
+#endif
+
+ dprint((7, "goodnight_gracey: close config files\n"));
+
+#ifdef SMIME
+ smime_deinit();
+#endif
+
+ free_pinerc_strings(&pine_state);
+
+ strncpy(msg, pf, sizeof(msg));
+ msg[sizeof(msg)-1] = '\0';
+ if(final_msg){
+ strncat(msg, " -- ", sizeof(msg)-strlen(msg)-1);
+ msg[sizeof(msg)-1] = '\0';
+ strncat(msg, final_msg, sizeof(msg)-strlen(msg)-1);
+ msg[sizeof(msg)-1] = '\0';
+ fs_give((void **)&final_msg);
+ }
+
+ dprint((7, "goodnight_gracey: sp_end\n"));
+ ps_global->noshow_error = 1;
+ sp_end();
+
+ /* after sp_end, which might call a filter */
+ completely_done_with_adrbks();
+
+ dprint((7, "goodnight_gracey: end_screen\n"));
+ end_screen(msg, exit_val);
+ dprint((7, "goodnight_gracey: end_titlebar\n"));
+ end_titlebar();
+ dprint((7, "goodnight_gracey: end_keymenu\n"));
+ end_keymenu();
+
+ dprint((7, "goodnight_gracey: end_keyboard\n"));
+ end_keyboard(F_ON(F_USE_FK,pine_state));
+ dprint((7, "goodnight_gracey: end_ttydriver\n"));
+ end_tty_driver(pine_state);
+#if !defined(DOS) && !defined(OS2)
+ kbdestroy(kbesc);
+#if !defined(LEAVEOUTFIFO)
+ close_newmailfifo();
+#endif
+#endif
+ end_signals(0);
+ if(filter_data_file(0))
+ our_unlink(filter_data_file(0));
+
+ imap_flush_passwd_cache(TRUE);
+ free_newsgrp_cache();
+ mailcap_free();
+ close_every_pattern();
+ free_extra_hdrs();
+ free_contexts(&ps_global->context_list);
+ free_charsetchecker();
+ dprint((7, "goodnight_gracey: free more memory\n"));
+#ifdef ENABLE_LDAP
+ free_saved_query_parameters();
+#endif
+
+ free_pine_struct(&pine_state);
+
+ free_histlist();
+
+#ifdef DEBUG
+ if(debugfile){
+ if(debug >= 2)
+ fputs("goodnight_gracey finished\n", debugfile);
+
+ fclose(debugfile);
+ }
+#endif
+
+ exit(exit_val);
+}
+
+
+/*----------------------------------------------------------------------
+ Call back for c-client to feed us back the progress of network reads
+
+ Input:
+
+ Result:
+ ----*/
+void
+pine_read_progress(GETS_DATA *md, long unsigned int count)
+{
+ gets_bytes += count; /* update counter */
+}
+
+
+/*----------------------------------------------------------------------
+ Function to fish the current byte count from a c-client fetch.
+
+ Input: reset -- flag telling us to reset the count
+
+ Result: Returns the number of bytes read by the c-client so far
+ ----*/
+unsigned long
+pine_gets_bytes(int reset)
+{
+ if(reset)
+ gets_bytes = 0L;
+
+ return(gets_bytes);
+}
+
+
+/*----------------------------------------------------------------------
+ Panic pine - call on detected programmatic errors to exit pine
+
+ Args: message -- message to record in debug file and to be printed for user
+
+ Result: The various tty modes are restored
+ If debugging is active a core dump will be generated
+ Exits Alpine
+
+ This is also called from imap routines and fs_get and fs_resize.
+ ----*/
+void
+panic(char *message)
+{
+ char buf[256];
+
+ /* global variable in .../pico/edef.h */
+ in_panic = 1;
+
+ if(ps_global->ttyo){
+ end_screen(NULL, -1);
+ end_keyboard(ps_global != NULL ? F_ON(F_USE_FK,ps_global) : 0);
+ end_tty_driver(ps_global);
+ end_signals(1);
+ }
+ if(filter_data_file(0))
+ our_unlink(filter_data_file(0));
+
+ dprint((1, "\n===========================================\n\n"));
+ dprint((1, " Alpine Panic: %s\n\n", message ? message : "?"));
+ dprint((1, "===========================================\n\n"));
+
+ /* intercept c-client "free storage" errors */
+ if(strstr(message, "free storage"))
+ snprintf(buf, sizeof(buf), _("No more available memory.\nAlpine Exiting"));
+ else
+ snprintf(buf, sizeof(buf), _("Problem detected: \"%s\".\nAlpine Exiting."), message);
+
+ buf[sizeof(buf)-1] = '\0';
+
+#ifdef _WINDOWS
+ /* Put up a message box. */
+ mswin_messagebox (buf, 1);
+#else
+ fprintf(stderr, "\n\n%s\n", buf);
+#endif
+
+#ifdef DEBUG
+ if(debugfile){
+ save_debug_on_crash(debugfile, recent_keystroke);
+ }
+
+ coredump(); /*--- If we're debugging get a core dump --*/
+#endif
+
+ exit(-1);
+ fatal("ffo"); /* BUG -- hack to get fatal out of library in right order*/
+}
+
+
+/*
+ * panicking - function to test whether or not we're exiting under stress.
+ *
+ */
+int
+panicking(void)
+{
+ return(in_panic);
+}
+
+
+/*----------------------------------------------------------------------
+ exceptional_exit - called to exit under unusual conditions (with no core)
+
+ Args: message -- message to record in debug file and to be printed for user
+ ev -- exit value
+
+ ----*/
+void
+exceptional_exit(char *message, int ev)
+{
+ fprintf(stderr, "%s\n", message);
+ exit(ev);
+}
+
+
+/*
+ * PicoText Storage Object Support Routines
+ */
+
+STORE_S *
+pine_pico_get(void)
+{
+ return((STORE_S *)pico_get());
+}
+
+int
+pine_pico_give(STORE_S **sop)
+{
+ pico_give((void *)sop);
+ return(1);
+}
+
+int
+pine_pico_writec(int c, STORE_S *so)
+{
+ unsigned char ch = (unsigned char) c;
+
+ return(pico_writec(so->txt, ch, PICOREADC_NONE));
+}
+
+int
+pine_pico_writec_noucs(int c, STORE_S *so)
+{
+ unsigned char ch = (unsigned char) c;
+
+ return(pico_writec(so->txt, ch, PICOREADC_NOUCS));
+}
+
+int
+pine_pico_readc(unsigned char *c, STORE_S *so)
+{
+ return(pico_readc(so->txt, c, PICOREADC_NONE));
+}
+
+int
+pine_pico_readc_noucs(unsigned char *c, STORE_S *so)
+{
+ return(pico_readc(so->txt, c, PICOREADC_NOUCS));
+}
+
+int
+pine_pico_puts(STORE_S *so, char *s)
+{
+ return(pico_puts(so->txt, s, PICOREADC_NONE));
+}
+
+int
+pine_pico_puts_noucs(STORE_S *so, char *s)
+{
+ return(pico_puts(so->txt, s, PICOREADC_NOUCS));
+}
+
+int
+pine_pico_seek(STORE_S *so, long pos, int orig)
+{
+ return(pico_seek((void *)so, pos, orig));
+}
+
+
+int
+remote_pinerc_failure(void)
+{
+#ifdef _WINDOWS
+ if(ps_global->install_flag) /* just exit silently */
+ exit(0);
+#endif /* _WINDOWS */
+
+ if(ps_global->exit_if_no_pinerc){
+ exceptional_exit("Exiting because -bail option is set and config file not readable.", -1);
+ }
+
+ if(want_to("Trouble reading remote configuration! Continue anyway ",
+ 'n', 'n', NO_HELP, WT_FLUSH_IN) != 'y'){
+ return(0);
+ }
+
+ return(1);
+}
+
+
+void
+dump_supported_options(void)
+{
+ char **config;
+
+ config = get_supported_options();
+ if(config){
+ display_args_err(NULL, config, 0);
+ free_list_array(&config);
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Check pruned-folders for validity, making sure they are in the
+ same context as sent-mail.
+
+ ----*/
+int
+prune_folders_ok(void)
+{
+ char **p;
+
+ for(p = ps_global->VAR_PRUNED_FOLDERS; p && *p && **p; p++)
+ if(!context_isambig(*p))
+ return(0);
+
+ return(1);
+}
+
+
+#ifdef WIN32
+char *
+pine_user_callback()
+{
+ if(ps_global->VAR_USER_ID && ps_global->VAR_USER_ID[0]){
+ return(ps_global->VAR_USER_ID);
+ }
+ else{
+ /* SHOULD PROMPT HERE! */
+ return(NULL);
+ }
+}
+#endif
+
+#ifdef _WINDOWS
+/*
+ * windows callback to get/set function keys mode state
+ */
+int
+fkey_mode_callback(set, args)
+ int set;
+ long args;
+{
+ return(F_ON(F_USE_FK, ps_global) != 0);
+}
+
+
+void
+imap_telemetry_on()
+{
+ if(ps_global->mail_stream)
+ mail_debug(ps_global->mail_stream);
+}
+
+
+void
+imap_telemetry_off()
+{
+ if(ps_global->mail_stream)
+ mail_nodebug(ps_global->mail_stream);
+}
+
+
+char *
+pcpine_help_main(title)
+ char *title;
+{
+ if(title)
+ strncpy(title, _("PC-Alpine MAIN MENU Help"), 256);
+
+ return(pcpine_help(main_menu_tx));
+}
+
+
+int
+pcpine_main_cursor(col, row)
+ int col;
+ long row;
+{
+ unsigned ndmi;
+
+ if (row >= (HEADER_ROWS(ps_global) + MNSKIP(ps_global)))
+ ndmi = (row+1 - HEADER_ROWS(ps_global) - (MNSKIP(ps_global)+1))/(MNSKIP(ps_global)+1);
+
+ if (row >= (HEADER_ROWS(ps_global) + MNSKIP(ps_global))
+ && !(MNSKIP(ps_global) && (row+1) & 0x01)
+ && ndmi <= MAX_MENU_ITEM
+ && FOOTER_ROWS(ps_global) + (ndmi+1)*(MNSKIP(ps_global)+1)
+ + MNSKIP(ps_global) + FOOTER_ROWS(ps_global) <= ps_global->ttyo->screen_rows)
+ return(MSWIN_CURSOR_HAND);
+ else
+ return(MSWIN_CURSOR_ARROW);
+}
+#endif /* _WINDOWS */
diff --git a/alpine/alpine.h b/alpine/alpine.h
new file mode 100644
index 00000000..cef14db9
--- /dev/null
+++ b/alpine/alpine.h
@@ -0,0 +1,41 @@
+/*
+ * $Id: alpine.h 767 2007-10-24 00:03:59Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef ALPINE_ALPINE_INCLUDED
+#define ALPINE_ALPINE_INCLUDED
+
+
+#include "../pith/state.h"
+
+
+/* exported protoypes */
+void main_menu_screen(struct pine *);
+unsigned long pine_gets_bytes(int);
+void quit_screen(struct pine *);
+int panicking(void);
+int rule_setup_type(struct pine *ps, int flags, char *prompt);
+UCS *user_wordseps(char **);
+STORE_S *pine_pico_get(void);
+int pine_pico_give(STORE_S **);
+int pine_pico_writec(int, STORE_S *);
+int pine_pico_writec_noucs(int, STORE_S *);
+int pine_pico_readc(unsigned char *, STORE_S *);
+int pine_pico_readc_noucs(unsigned char *, STORE_S *);
+int pine_pico_puts(STORE_S *, char *);
+int pine_pico_puts_noucs(STORE_S *, char *);
+int pine_pico_seek(STORE_S *, long, int);
+
+
+#endif /* ALPINE_ALPINE_INCLUDED */
diff --git a/alpine/arg.c b/alpine/arg.c
new file mode 100644
index 00000000..e23853c4
--- /dev/null
+++ b/alpine/arg.c
@@ -0,0 +1,1325 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: arg.c 900 2008-01-05 01:13:26Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ Command line argument parsing functions
+
+ ====*/
+
+#include "headers.h"
+
+#include "../pith/state.h"
+#include "../pith/init.h"
+#include "../pith/conf.h"
+#include "../pith/list.h"
+#include "../pith/util.h"
+#include "../pith/help.h"
+
+#include "imap.h"
+
+#include "arg.h"
+
+
+int process_debug_str(char *);
+void args_add_attach(PATMT **, char *, int);
+int pinerc_cmdline_opt(char *);
+
+
+/*
+ * Name started as to invoke function key mode
+ */
+#define ALPINE_FKEY_NAME "alpinef"
+
+/*
+ * Various error and informational strings..
+ */
+/* TRANSLATORS: This is a list of errors printed when something goes wrong
+ early on with the argument list. Be careful not to change literal
+ option names mentioned in the strings. */
+static char args_err_missing_pinerc[] = N_("missing argument for option \"-pinerc\" (use - for standard out)");
+#if defined(DOS) || defined(OS2)
+static char args_err_missing_aux[] = N_("missing argument for option \"-aux\"");
+#endif
+#ifdef PASSFILE
+static char args_err_missing_passfile[] = N_("missing argument for option \"-passfile\"");
+static char args_err_non_abs_passfile[] = N_("argument to \"-passfile\" should be fully-qualified");
+#endif
+static char args_err_missing_sort[] = N_("missing argument for option \"-sort\"");
+static char args_err_missing_flag_arg[] = N_("missing argument for flag \"%c\"");
+static char args_err_missing_flag_num[] = N_("Non numeric argument for flag \"%c\"");
+static char args_err_missing_debug_num[] = N_("Non numeric argument for \"%s\"");
+static char args_err_missing_url[] = N_("missing URL for \"-url\"");
+static char args_err_missing_attachment[] = N_("missing attachment for \"%s\"");
+static char args_err_conflict[] = N_("conflicting action: \"%s\"");
+static char args_err_unknown[] = N_("unknown flag \"%c\"");
+static char args_err_I_error[] = N_("-I argument \"%s\": %s");
+static char args_err_d_error[] = N_("-d argument \"%s\": %s");
+static char args_err_internal[] = "%s";
+static char args_err_missing_copyprc[] = N_("missing argument for option \"-copy_pinerc\"\nUsage: pine -copy_pinerc <local_pinerc> <remote_pinerc>");
+static char args_err_missing_copyabook[] = N_("missing argument for option \"-copy_abook\"\nUsage: pine -copy_abook <local_abook> <remote_abook>");
+
+
+static char *args_pine_args[] = {
+N_("Possible Starting Arguments for Alpine program:"),
+"",
+N_(" Argument\tMeaning"),
+N_(" <addrs>...\tGo directly into composer sending to given address"),
+N_("\t\tList multiple addresses with a single space between them."),
+N_("\t\tStandard input redirection is allowed with addresses."),
+N_("\t\tNote: Places addresses in the \"To\" field only."),
+N_(" -attach <file>\tGo directly into composer with given file"),
+N_(" -attachlist <file-list>"),
+N_(" -attach_and_delete <file>"),
+N_("\t\tGo to composer, attach file, delete when finished"),
+N_("\t\tNote: Attach options can't be used if -f, -F"),
+N_("\t\tadded to Attachment list. Attachlist must be the last"),
+N_("\t\toption on the command line"),
+N_(" -bail\t\tExit if pinerc file doesn't already exist"),
+#ifdef DEBUG
+N_(" -d n\t\tDebug - set debug level to 'n', or use the following:"),
+N_(" -d keywords...\tflush,timestamp,imap=0..4,tcp,numfiles=0..31,verbose=0..9"),
+#endif
+N_(" -f <folder>\tFolder - give folder name to open"),
+N_(" -c <number>\tContext - which context to apply to -f arg"),
+N_(" -F <file>\tFile - give file name to open and page through and"),
+N_("\t\tforward as email."),
+N_(" -h \t\tHelp - give this list of options"),
+N_(" -k \t\tKeys - Force use of function keys"),
+N_(" -z \t\tSuspend - allow use of ^Z suspension"),
+N_(" -r \t\tRestricted - can only send mail to oneself"),
+N_(" -sort <sort>\tSort - Specify sort order of folder:"),
+N_("\t\t\tarrival, subject, threaded, orderedsubject, date,"),
+N_("\t\t\tfrom, size, score, to, cc, /reverse"),
+N_(" -i\t\tIndex - Go directly to index, bypassing main menu"),
+N_(" -I <keystroke_list> Initial keystrokes to be executed"),
+N_(" -n <number>\tEntry in index to begin on"),
+N_(" -o \t\tReadOnly - Open first folder read-only"),
+N_(" -conf\t\tConfiguration - Print out fresh global configuration. The"),
+N_("\t\tvalues of your global configuration affect all Alpine users"),
+N_("\t\ton your system unless they have overridden the values in their"),
+N_("\t\tpinerc files."),
+N_(" -pinerc <file>\tConfiguration - Put fresh pinerc configuration in <file>"),
+N_(" -p <pinerc>\tUse alternate .pinerc file"),
+#if !defined(DOS) && !defined(OS2)
+N_(" -P <pine.conf>\tUse alternate pine.conf file"),
+#else
+N_(" -aux <aux_files_dir>\tUse this with remote pinerc"),
+N_(" -P <pine.conf>\tUse pine.conf file for default settings"),
+N_(" -nosplash \tDisable the PC-Alpine splash screen"),
+#endif
+
+#if defined(APPLEKEYCHAIN) || (WINCRED > 0)
+N_(" -erase_stored_passwords\tEliminate any stored passwords"),
+#endif
+
+#ifdef PASSFILE
+N_(" -passfile <fully_qualified_filename>\tSet the password file to something other"),
+N_("\t\tthan the default"),
+#endif /* PASSFILE */
+
+#ifdef LOCAL_PASSWD_CACHE
+N_(" -nowrite_password_cache\tRead from a password cache if there is one, but"),
+N_("\t\t\t\tnever offer to write a password to the cache"),
+#endif /* LOCAL_PASSWD_CACHE */
+
+N_(" -x <config>\tUse configuration exceptions in <config>."),
+N_("\t\tExceptions are used to override your default pinerc"),
+N_("\t\tsettings for a particular platform, can be a local file or"),
+N_("\t\ta remote folder."),
+N_(" -v \t\tVersion - show version information"),
+N_(" -version\tVersion - show version information"),
+N_(" -supported\tList supported options"),
+N_(" -url <url>\tOpen the given URL"),
+N_("\t\tNote: Can't be used if -f, -F"),
+N_("\t\tStandard input redirection is not allowed with URLs."),
+N_("\t\tFor mailto URLs, 'body='text should be used in place of"),
+N_("\t\tinput redirection."),
+N_(" -copy_pinerc <local_pinerc> <remote_pinerc> copy local pinerc to remote"),
+N_(" -copy_abook <local_abook> <remote_abook> copy local addressbook to remote"),
+N_(" -convert_sigs -p <pinerc> convert signatures to literal signatures"),
+#if defined(_WINDOWS)
+N_(" -install \tPrompt for some basic setup information"),
+N_(" -uninstall \tRemove traces of Alpine from Windows system settings"),
+N_(" -registry <cmd>\tWhere cmd is set,noset,clear,clearsilent,dump"),
+#endif
+" -<option>=<value> Assign <value> to the pinerc option <option>",
+"\t\t e.g. -signature-file=sig1",
+"\t\t e.g. -color-style=no-color",
+"\t\t e.g. -feature-list=enable-sigdashes",
+"\t\t Note: feature-list is additive.",
+"\t\t You may leave off the \"feature-list=\" part of that,",
+"\t\t e.g. -enable-sigdashes",
+NULL
+};
+
+
+
+/*
+ * Parse the command line args.
+ *
+ * Args: pine_state -- The pine_state structure to put results in
+ * argc, argv -- The obvious
+ * addrs -- Pointer to address list that we set for caller
+ *
+ * Result: command arguments parsed
+ * possible printing of help for command line
+ * various flags in pine_state set
+ * returns the string name of the first folder to open
+ * addrs is set
+ */
+void
+pine_args(struct pine *pine_state, int argc, char **argv, ARGDATA_S *args)
+{
+ register int ac;
+ register char **av;
+ int c;
+ char *str;
+ char *cmd_list = NULL;
+ char *debug_str = NULL;
+ char *sort = NULL;
+ char *pinerc_file = NULL;
+ char *lc = NULL;
+ int do_help = 0;
+ int do_conf = 0;
+ int usage = 0;
+ int do_use_fk = 0;
+ int do_can_suspend = 0;
+ int do_version = 0;
+ struct variable *vars = pine_state->vars;
+
+ ac = argc;
+ av = argv;
+ memset(args, 0, sizeof(ARGDATA_S));
+ args->action = aaFolder;
+
+ pine_state->pine_name = (lc = last_cmpnt(argv[0])) ? lc : (lc = argv[0]);
+#ifdef DOS
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%.*s", pine_state->pine_name - argv[0], argv[0]);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ pine_state->pine_dir = cpystr(tmp_20k_buf);
+#endif
+
+ /* while more arguments with leading - */
+Loop: while(--ac > 0)
+ if(**++av == '-'){
+ /* while more chars in this argument */
+ while(*++*av){
+ /* check for pinerc options */
+ if(pinerc_cmdline_opt(*av)){
+ goto Loop; /* done with this arg, go to next */
+ }
+ /* then other multi-char options */
+ else if(strcmp(*av, "conf") == 0){
+ do_conf = 1;
+ goto Loop; /* done with this arg, go to next */
+ }
+ else if(strcmp(*av, "pinerc") == 0){
+ if(--ac)
+ pinerc_file = *++av;
+ else{
+ display_args_err(_(args_err_missing_pinerc), NULL, 1);
+ ++usage;
+ }
+
+ goto Loop;
+ }
+#if defined(DOS) || defined(OS2)
+ else if(strcmp(*av, "aux") == 0){
+ if(--ac){
+ if((str = *++av) != NULL)
+ pine_state->aux_files_dir = cpystr(str);
+ }
+ else{
+ display_args_err(_(args_err_missing_aux), NULL, 1);
+ ++usage;
+ }
+
+ goto Loop;
+ }
+ else if(strcmp(*av, "nosplash") == 0)
+ goto Loop; /* already taken care of in WinMain */
+#endif
+
+#if defined(APPLEKEYCHAIN) || (WINCRED > 0)
+ else if(strcmp(*av, "erase_stored_passwords") == 0){
+#if (WINCRED > 0)
+ erase_windows_credentials();
+#else
+ macos_erase_keychain();
+#endif
+ goto Loop;
+ }
+#endif /* defined(APPLEKEYCHAIN) || (WINCRED > 0) */
+
+#ifdef PASSFILE
+ else if(strcmp(*av, "passfile") == 0){
+ if(--ac){
+ if((str = *++av) != NULL){
+ if(!is_absolute_path(str)){
+ display_args_err(_(args_err_non_abs_passfile),
+ NULL, 1);
+ ++usage;
+ }
+ else{
+ if(pine_state->passfile)
+ fs_give((void **)&pine_state->passfile);
+
+ pine_state->passfile = cpystr(str);
+ }
+ }
+ }
+ else{
+ display_args_err(_(args_err_missing_passfile), NULL, 1);
+ ++usage;
+ }
+
+ goto Loop;
+ }
+#endif /* PASSFILE */
+
+#ifdef LOCAL_PASSWD_CACHE
+ else if(strcmp(*av, "nowrite_password_cache") == 0){
+ ps_global->nowrite_password_cache = 1;
+ goto Loop;
+ }
+#endif /* LOCAL_PASSWD_CACHE */
+
+ else if(strcmp(*av, "convert_sigs") == 0){
+ ps_global->convert_sigs = 1;
+ goto Loop;
+ }
+ else if(strcmp(*av, "supported") == 0){
+ ps_global->dump_supported_options = 1;
+ goto Loop;
+ }
+ else if(strcmp(*av, "copy_pinerc") == 0){
+ if(args->action == aaFolder && !args->data.folder){
+ args->action = aaPrcCopy;
+ if(ac > 2){
+ ac -= 2;
+ args->data.copy.local = *++av;
+ args->data.copy.remote = *++av;
+ }
+ else{
+ display_args_err(_(args_err_missing_copyprc), NULL, 1);
+ ++usage;
+ }
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), "-copy_pinerc");
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ display_args_err(tmp_20k_buf, NULL, 1);
+ ++usage;
+ }
+
+ goto Loop;
+ }
+ else if(strcmp(*av, "copy_abook") == 0){
+ if(args->action == aaFolder && !args->data.folder){
+ args->action = aaAbookCopy;
+ if(ac > 2){
+ ac -= 2;
+ args->data.copy.local = *++av;
+ args->data.copy.remote = *++av;
+ }
+ else{
+ display_args_err(_(args_err_missing_copyabook), NULL, 1);
+ ++usage;
+ }
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), "-copy_abook");
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ display_args_err(tmp_20k_buf, NULL, 1);
+ ++usage;
+ }
+
+ goto Loop;
+ }
+ else if(strcmp(*av, "sort") == 0){
+ if(--ac){
+ sort = *++av;
+ COM_SORT_KEY = cpystr(sort);
+ }
+ else{
+ display_args_err(_(args_err_missing_sort), NULL, 1);
+ ++usage;
+ }
+
+ goto Loop;
+ }
+ else if(strcmp(*av, "url") == 0){
+ if(args->action == aaFolder && !args->data.folder){
+ args->action = aaURL;
+ if(--ac){
+ args->url = cpystr(*++av);
+ }
+ else{
+ display_args_err(_(args_err_missing_url), NULL, 1);
+ ++usage;
+ }
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), "-url");
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ display_args_err(tmp_20k_buf, NULL, 1);
+ ++usage;
+ }
+
+ goto Loop;
+ }
+ else if(strcmp(*av, "attach") == 0){
+ if((args->action == aaFolder && !args->data.folder)
+ || args->action == aaMail
+ || args->action == aaURL){
+ if(args->action != aaURL)
+ args->action = aaMail;
+ if(--ac){
+ args_add_attach(&args->data.mail.attachlist,
+ *++av, FALSE);
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_attachment), "-attach");
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ display_args_err(tmp_20k_buf, NULL, 1);
+ ++usage;
+ }
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), "-attach");
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ display_args_err(tmp_20k_buf, NULL, 1);
+ ++usage;
+ }
+
+ goto Loop;
+ }
+ else if(strcmp(*av, "attachlist") == 0){
+ if((args->action == aaFolder && !args->data.folder)
+ || args->action == aaMail
+ || args->action == aaURL){
+ if(args->action != aaURL)
+ args->action = aaMail;
+ if(ac - 1){
+ do{
+ if(can_access(*(av+1), READ_ACCESS) == 0){
+ ac--;
+ args_add_attach(&args->data.mail.attachlist,
+ *++av, FALSE);
+ }
+ else
+ break;
+ }
+ while(ac);
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_attachment), "-attachList");
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ display_args_err(tmp_20k_buf, NULL, 1);
+ ++usage;
+ }
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), "-attachList");
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ display_args_err(tmp_20k_buf, NULL, 1);
+ ++usage;
+ }
+
+ goto Loop;
+ }
+ else if(strcmp(*av, "attach_and_delete") == 0){
+ if((args->action == aaFolder && !args->data.folder)
+ || args->action == aaMail
+ || args->action == aaURL){
+ if(args->action != aaURL)
+ args->action = aaMail;
+ if(--ac){
+ args_add_attach(&args->data.mail.attachlist,
+ *++av, TRUE);
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_attachment), "-attach_and_delete");
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ display_args_err(tmp_20k_buf, NULL, 1);
+ ++usage;
+ }
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), "-attach_and_delete");
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ display_args_err(tmp_20k_buf, NULL, 1);
+ ++usage;
+ }
+
+ goto Loop;
+ }
+ else if(strcmp(*av, "bail") == 0){
+ pine_state->exit_if_no_pinerc = 1;
+ goto Loop;
+ }
+ else if(strcmp(*av, "version") == 0){
+ do_version = 1;
+ goto Loop;
+ }
+#ifdef _WINDOWS
+ else if(strcmp(*av, "install") == 0){
+ pine_state->install_flag = 1;
+ pine_state->update_registry = UREG_ALWAYS_SET;
+ goto Loop;
+ }
+ else if(strcmp(*av, "uninstall") == 0){
+ /*
+ * Blast password cache, clear registry settings
+ */
+#if (WINCRED > 0)
+ erase_windows_credentials();
+#endif
+ mswin_reg(MSWR_OP_BLAST, MSWR_NULL, NULL, 0);
+ exit(0);
+ }
+ else if(strcmp(*av, "registry") == 0){
+ if(--ac){
+ if(!strucmp(*++av, "set")){
+ pine_state->update_registry = UREG_ALWAYS_SET;
+ }
+ else if(!strucmp(*av, "noset")){
+ pine_state->update_registry = UREG_NEVER_SET;
+ }
+ else if(!strucmp(*av, "clear")){
+ if(!mswin_reg(MSWR_OP_BLAST, MSWR_NULL, NULL, 0))
+ display_args_err(
+ _("Alpine related Registry values removed."),
+ NULL, 0);
+ else
+ display_args_err(
+ _("Not all Alpine related Registry values could be removed"),
+ NULL, 0);
+ exit(0);
+ }
+ else if(!strucmp(*av, "clearsilent")){
+ mswin_reg(MSWR_OP_BLAST, MSWR_NULL, NULL, 0);
+ exit(0);
+ }
+ else if(!strucmp(*av, "dump")){
+ char **pRegistry = mswin_reg_dump();
+
+ if(pRegistry){
+ display_args_err(NULL, pRegistry, 0);
+ free_list_array(&pRegistry);
+ }
+ exit(0);
+ }
+ else{
+ display_args_err(_("unknown registry command"),
+ NULL, 1);
+ ++usage;
+ }
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_flag_arg), c);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ display_args_err(tmp_20k_buf, NULL, 1);
+ ++usage;
+ }
+
+ goto Loop;
+ }
+#endif
+ /* single char flags */
+ else{
+ switch(c = **av){
+ case 'h':
+ do_help = 1;
+ break; /* break back to inner-while */
+ case 'k':
+ do_use_fk = 1;
+ break;
+ case 'z':
+ do_can_suspend = 1;
+ break;
+ case 'r':
+ pine_state->restricted = 1;
+ break;
+ case 'o':
+ pine_state->open_readonly_on_startup = 1;
+ break;
+ case 'i':
+ pine_state->start_in_index = 1;
+ break;
+ case 'v':
+ do_version = 1;
+ break; /* break back to inner-while */
+ /* these take arguments */
+ case 'f': case 'F': case 'p': case 'I':
+ case 'c': case 'd': case 'P': case 'x': /* string args */
+ case 'n': /* integer args */
+ if(*++*av)
+ str = *av;
+ else if(--ac)
+ str = *++av;
+ else if(c == 'f' || c == 'F')
+ str = "";
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_flag_arg), c);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ display_args_err(tmp_20k_buf, NULL, 1);
+ ++usage;
+ goto Loop;
+ }
+
+ switch(c){
+ case 'f':
+ if(args->action == aaFolder && !args->data.folder){
+ args->data.folder = cpystr(str);
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), "-f");
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ display_args_err(tmp_20k_buf, NULL, 1);
+ usage++;
+ }
+
+ break;
+ case 'F':
+ if(args->action == aaFolder && !args->data.folder){
+ args->action = aaMore;
+ args->data.file = cpystr(str);
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), "-F");
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ display_args_err(tmp_20k_buf, NULL, 1);
+ usage++;
+ }
+
+ break;
+ case 'd':
+ debug_str = str;
+ break;
+ case 'I':
+ cmd_list = str;
+ break;
+ case 'p':
+ if(str){
+ char path[MAXPATH], dir[MAXPATH];
+
+ if(IS_REMOTE(str) || is_absolute_path(str)){
+ strncpy(path, str, sizeof(path)-1);
+ path[sizeof(path)-1] = '\0';
+ }
+ else{
+ getcwd(dir, sizeof(path));
+ build_path(path, dir, str, sizeof(path));
+ }
+
+ /*
+ * Pinerc used to be the name of the pinerc
+ * file. Now, since the pinerc can be remote,
+ * we've replaced the variable pinerc with the
+ * structure prc. Unfortunately, other parts of
+ * pine rely on the fact that pinerc is the
+ * name of the pinerc _file_, and use the
+ * directory that the pinerc file is located
+ * in for their own purposes. We keep that so
+ * things will keep working.
+ */
+
+ if(!IS_REMOTE(path)){
+ if(pine_state->pinerc)
+ fs_give((void **)&pine_state->pinerc);
+
+ pine_state->pinerc = cpystr(path);
+ }
+
+ /*
+ * Last one wins. This would be the place where
+ * we put multiple pinercs in a list if we
+ * were to allow that.
+ */
+ if(pine_state->prc)
+ free_pinerc_s(&pine_state->prc);
+
+ pine_state->prc = new_pinerc_s(path);
+ }
+
+ break;
+ case 'P':
+ if(str){
+ char path[MAXPATH], dir[MAXPATH];
+
+ if(IS_REMOTE(str) || is_absolute_path(str)){
+ strncpy(path, str, sizeof(path)-1);
+ path[sizeof(path)-1] = '\0';
+ }
+ else{
+ getcwd(dir, sizeof(path));
+ build_path(path, dir, str, sizeof(path));
+ }
+
+ if(pine_state->pconf)
+ free_pinerc_s(&pine_state->pconf);
+
+ pine_state->pconf = new_pinerc_s(path);
+ }
+
+ break;
+ case 'x':
+ if(str)
+ pine_state->exceptions = cpystr(str);
+
+ break;
+ case 'c':
+ if(!isdigit((unsigned char)str[0])){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,
+ _(args_err_missing_flag_num), c);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ display_args_err(tmp_20k_buf, NULL, 1);
+ ++usage;
+ break;
+ }
+
+ pine_state->init_context = (short) atoi(str);
+ break;
+
+ case 'n':
+ if(!isdigit((unsigned char)str[0])){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,
+ _(args_err_missing_flag_num), c);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ display_args_err(tmp_20k_buf, NULL, 1);
+ ++usage;
+ break;
+ }
+
+ pine_state->start_entry = atoi(str);
+ if(pine_state->start_entry < 1)
+ pine_state->start_entry = 1;
+
+ break;
+ }
+
+ goto Loop;
+
+ default:
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_unknown), c);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ display_args_err(tmp_20k_buf, NULL, 1);
+ ++usage;
+ break;
+ }
+ }
+ }
+ }
+ else if(args->action == aaMail
+ || (args->action == aaFolder && !args->data.folder)){
+ STRLIST_S *stp, **slp;
+
+ args->action = aaMail;
+
+ stp = new_strlist(*av);
+
+ for(slp = &args->data.mail.addrlist; *slp; slp = &(*slp)->next)
+ ;
+
+ *slp = stp;
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_conflict), *av);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ display_args_err(tmp_20k_buf, NULL, 1);
+ usage++;
+ }
+
+ if(cmd_list){
+ int commas = 0;
+ char *p = cmd_list;
+ char *error = NULL;
+
+ while(*p++)
+ if(*p == ',')
+ ++commas;
+
+ COM_INIT_CMD_LIST = parse_list(cmd_list, commas+1, 0, &error);
+ if(error){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_I_error), cmd_list, error);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ display_args_err(tmp_20k_buf, NULL, 1);
+ exit(-1);
+ }
+ }
+
+#ifdef DEBUG
+ pine_state->debug_nfiles = NUMDEBUGFILES;
+#endif
+ if(debug_str && process_debug_str(debug_str))
+ usage++;
+
+ if(lc && strncmp(lc, ALPINE_FKEY_NAME, sizeof(ALPINE_FKEY_NAME) - 1) == 0)
+ do_use_fk = 1;
+
+ if(do_use_fk || do_can_suspend){
+ char list[500];
+ int commas = 0;
+ char *p = list;
+ char *error = NULL;
+
+ list[0] = '\0';
+
+ if(do_use_fk){
+ if(list[0]){
+ strncat(list, ",", sizeof(list)-strlen(list)-1);
+ list[sizeof(list)-1] = '\0';
+ }
+
+ strncat(list, "use-function-keys", sizeof(list)-strlen(list)-1);
+ list[sizeof(list)-1] = '\0';
+ }
+
+ if(do_can_suspend){
+ if(list[0]){
+ strncat(list, ",", sizeof(list)-strlen(list)-1);
+ list[sizeof(list)-1] = '\0';
+ }
+
+ strncat(list, "enable-suspend", sizeof(list)-strlen(list)-1);
+ list[sizeof(list)-1] = '\0';
+ }
+
+ while(*p++)
+ if(*p == ',')
+ ++commas;
+
+ pine_state->feat_list_back_compat = parse_list(list,commas+1,0,&error);
+ if(error){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, args_err_internal, error);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ display_args_err(tmp_20k_buf, NULL, 1);
+ exit(-1);
+ }
+ }
+
+ if(((do_conf ? 1 : 0)+(pinerc_file ? 1 : 0)) > 1){
+ display_args_err(_("May only have one of -conf and -pinerc"),
+ NULL, 1);
+ exit(-1);
+ }
+
+ if(do_help || usage)
+ args_help();
+
+ if(usage)
+ exit(-1);
+
+ if(do_version){
+ extern char datestamp[], hoststamp[];
+ char rev[128];
+
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Alpine %s (%s %s) built %s on %s",
+ ALPINE_VERSION,
+ SYSTYPE ? SYSTYPE : "?",
+ get_alpine_revision_string(rev, sizeof(rev)),
+ datestamp, hoststamp);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ display_args_err(tmp_20k_buf, NULL, 0);
+ exit(0);
+ }
+
+ if(do_conf)
+ dump_global_conf();
+
+ if(pinerc_file)
+ dump_new_pinerc(pinerc_file);
+
+ /*
+ * Don't NULL out argv[0] or we might crash in unexpected ways. In OS X, we were
+ * crashing when opening attachments because of this.
+ */
+ if(ac <= 0 && av != argv)
+ *av = NULL;
+}
+
+
+/*
+ * Returns 0 if ok, -1 if error.
+ */
+int
+process_debug_str(char *debug_str)
+{
+ int i, usage = 0;
+ int commas = 0;
+ int new_style_debug_arg = 0;
+ char *q = debug_str;
+ char *error = NULL;
+ char **list, **p;
+
+#ifdef DEBUG
+ if(debug_str){
+ if(!isdigit((unsigned char)debug_str[0]))
+ new_style_debug_arg++;
+
+ if(new_style_debug_arg){
+ while(*q++)
+ if(*q == ',')
+ ++commas;
+
+ list = parse_list(debug_str, commas+1, 0, &error);
+ if(error){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_d_error), debug_str, error);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ display_args_err(tmp_20k_buf, NULL, 1);
+ return(-1);
+ }
+
+ if(list){
+ for(p = list; *p; p++){
+ if(struncmp(*p, "timestamp", 9) == 0){
+ ps_global->debug_timestamp = 1;
+ }
+ else if(struncmp(*p, "imap", 4) == 0){
+ q = *p + 4;
+ if(!*q || !*(q+1) || *q != '=' ||
+ !isdigit((unsigned char)*(q+1))){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_debug_num), *p);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ display_args_err(tmp_20k_buf, NULL, 1);
+ usage = -1;
+ }
+ else{
+ i = atoi(q+1);
+ ps_global->debug_imap = MIN(5,MAX(0,i));
+ }
+ }
+ else if(struncmp(*p, "flush", 5) == 0){
+ ps_global->debug_flush = 1;
+ }
+ else if(struncmp(*p, "tcp", 3) == 0){
+ ps_global->debug_tcp = 1;
+ }
+ else if(struncmp(*p, "verbose", 7) == 0){
+ q = *p + 7;
+ if(!*q || !*(q+1) || *q != '=' ||
+ !isdigit((unsigned char)*(q+1))){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_debug_num), *p);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ display_args_err(tmp_20k_buf, NULL, 1);
+ usage = -1;
+ }
+ else
+ debug = atoi(q+1);
+ }
+ else if(struncmp(*p, "numfiles", 8) == 0){
+ q = *p + 8;
+ if(!*q || !*(q+1) || *q != '=' ||
+ !isdigit((unsigned char)*(q+1))){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_debug_num), *p);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ display_args_err(tmp_20k_buf, NULL, 1);
+ usage = -1;
+ }
+ else{
+ i = atoi(q+1);
+ ps_global->debug_nfiles = MIN(31,MAX(0,i));
+ }
+ }
+ else if(struncmp(*p, "malloc", 6) == 0){
+ q = *p + 6;
+ if(!*q || !*(q+1) || *q != '=' ||
+ !isdigit((unsigned char)*(q+1))){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_debug_num), *p);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ display_args_err(tmp_20k_buf, NULL, 1);
+ usage = -1;
+ }
+ else{
+ i = atoi(q+1);
+ ps_global->debug_malloc = MIN(63,MAX(0,i));
+ }
+ }
+#if defined(ENABLE_LDAP) && defined(LDAP_DEBUG)
+ else if(struncmp(*p, "ldap", 4) == 0){
+ q = *p + 4;
+ if(!*q || !*(q+1) || *q != '=' ||
+ !isdigit((unsigned char)*(q+1))){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _(args_err_missing_debug_num), *p);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ display_args_err(tmp_20k_buf, NULL, 1);
+ usage = -1;
+ }
+ else{
+ i = atoi(q+1);
+ ldap_debug = i;
+ }
+ }
+#endif /* LDAP */
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("unknown debug keyword \"%s\""), *p);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ display_args_err(tmp_20k_buf, NULL, 1);
+ usage = -1;
+ }
+ }
+
+ free_list_array(&list);
+ }
+ }
+ else{
+ debug = atoi(debug_str);
+ if(debug > 9)
+ ps_global->debug_imap = 5;
+ else if(debug > 7)
+ ps_global->debug_imap = 4;
+ else if(debug > 6)
+ ps_global->debug_imap = 3;
+ else if(debug > 4)
+ ps_global->debug_imap = 2;
+ else if(debug > 2)
+ ps_global->debug_imap = 1;
+
+ if(debug > 7)
+ ps_global->debug_timestamp = 1;
+
+ if(debug > 8)
+ ps_global->debug_flush = 1;
+ }
+ }
+
+ if(!new_style_debug_arg){
+#ifdef CSRIMALLOC
+ ps_global->debug_malloc =
+ (debug <= DEFAULT_DEBUG) ? 1 : (debug < 9) ? 2 : 3;
+#else /* !CSRIMALLOC */
+#ifdef HAVE_SMALLOC
+ if(debug > 8)
+ ps_global->debug_malloc = 2;
+#else /* !HAVE_SMALLOC */
+#ifdef NXT
+ if(debug > 8)
+ ps_global->debug_malloc = 32;
+#endif /* NXT */
+#endif /* HAVE_SMALLOC */
+#endif /* CSRIMALLOC */
+ }
+
+#else /* !DEBUG */
+
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("unknown flag \"d\", debugging not compiled in"));
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ display_args_err(tmp_20k_buf, NULL, 1);
+ usage = -1;
+
+#endif /* !DEBUG */
+
+ return(usage);
+}
+
+
+void
+args_add_attach(PATMT **alpp, char *s, int deleted)
+{
+ PATMT *atmp, **atmpp;
+
+ atmp = (PATMT *) fs_get(sizeof(PATMT));
+ memset(atmp, 0, sizeof(PATMT));
+ atmp->filename = cpystr(s);
+
+#if defined(DOS) || defined(OS2)
+ (void) removing_quotes(atmp->filename);
+#endif
+
+ if(deleted)
+ atmp->flags |= A_TMP;
+
+ for(atmpp = alpp; *atmpp; atmpp = &(*atmpp)->next)
+ ;
+
+ *atmpp = atmp;
+}
+
+
+/*----------------------------------------------------------------------
+ print a few lines of help for command line arguments
+
+ Args: none
+
+ Result: prints help messages
+ ----------------------------------------------------------------------*/
+void
+args_help(void)
+{
+ char *pp[2];
+#ifndef _WINDOWS
+ char **a;
+#endif
+
+ pp[1] = NULL;
+
+ /** print out possible starting arguments... **/
+
+ /*
+ * display_args_err expects already translated input
+ * so we need to translate a line at a time. Since
+ * the 1st and 3rd args are zero it is ok to call it
+ * a line at a time.
+ *
+ * Windows expects the full block of text so we'll pass
+ * it as such.
+ */
+#ifndef _WINDOWS
+ for(a=args_pine_args; a && *a; a++){
+ pp[0] = _(*a);
+ display_args_err(NULL, pp, 0);
+ }
+#else
+ display_args_err(NULL, args_pine_args, 0);
+#endif
+
+ exit(1);
+}
+
+
+/*----------------------------------------------------------------------
+ write argument error to the display...
+
+ Args: none
+
+ Result: prints help messages
+ ----------------------------------------------------------------------*/
+void
+display_args_err(char *s, char **a, int err)
+{
+ char errstr[256], *errp;
+ FILE *fp = err ? stderr : stdout;
+
+
+ if(err && s){
+ snprintf(errp = errstr, sizeof(errstr), "%s: %s", _("Argument Error"), s);
+ errstr[sizeof(errstr)-1] = '\0';
+ }
+ else
+ errp = s;
+
+#ifdef _WINDOWS
+ if(errp)
+ mswin_messagebox(errp, err);
+
+ if(a && *a){
+ os_argsdialog(a);
+ }
+#else
+ if(errp)
+ fprintf(fp, "%s\n", errp);
+
+ while(a && *a)
+ fprintf(fp, "%s\n", *a++);
+#endif
+}
+
+
+/*
+ * The argument is an argument from the command line. We check to see
+ * if it is specifying an alternate value for one of the options that is
+ * normally set in pinerc. If so, we return 1 and set the appropriate
+ * values in the variables array.
+ * The arg can be
+ * varname=value
+ * varname=value1,value2,value3
+ * feature-list=featurename these are just a special
+ * feature-list=featurename1,featurename2 case of above
+ * featurename This is equivalent to above
+ * no-featurename
+ */
+int
+pinerc_cmdline_opt(char *arg)
+{
+ struct variable *v;
+ char *p1 = NULL,
+ *value,
+ **oldlvalue = NULL,
+ **lvalue;
+ int i, count;
+
+ if(!arg || !arg[0])
+ return 0;
+
+ for(v = ps_global->vars; v->name != NULL; v++){
+ if(v->is_used && struncmp(v->name, arg, strlen(v->name)) == 0){
+ p1 = arg + strlen(v->name);
+
+ /*----- Skip to '=' -----*/
+ while(*p1 && (*p1 == '\t' || *p1 == ' '))
+ p1++;
+
+ if(*p1 != '='){
+ char buf[MAILTMPLEN];
+
+ snprintf(buf, sizeof(buf), _("Missing \"=\" after -%s\n"), v->name);
+ buf[sizeof(buf)-1] = '\0';
+ exceptional_exit(buf, -1);
+ }
+
+ p1++;
+ break;
+ }
+ }
+
+ /* no match, check for a feature name used directly */
+ if(v->name == NULL){
+ FEATURE_S *feat;
+ char *featname;
+
+ if(struncmp(arg, "no-", 3) == 0)
+ featname = arg+3;
+ else
+ featname = arg;
+
+ for(i = 0; (feat = feature_list(i)) != NULL; i++){
+ if(strucmp(featname, feat->name) == 0){
+ v = &(ps_global->vars)[V_FEATURE_LIST];
+ p1 = arg;
+ break;
+ }
+ }
+ }
+
+ if(!p1)
+ return 0;
+
+ if(v->is_obsolete || !v->is_user){
+ char buf[MAILTMPLEN];
+
+ if(v->is_obsolete)
+ snprintf(buf, sizeof(buf), _("Option \"%s\" is obsolete\n"), v->name);
+ else
+ snprintf(buf, sizeof(buf), _("Option \"%s\" is not user settable\n"), v->name);
+
+ exceptional_exit(buf, -1);
+ }
+
+ /* free mem */
+ if(v->is_list){
+ oldlvalue = v->cmdline_val.l;
+ v->cmdline_val.l = NULL;
+ }
+ else if(v->cmdline_val.p)
+ fs_give((void **) &(v->cmdline_val.p));
+
+ /*----- Matched a variable, get its value ----*/
+ while(*p1 == ' ' || *p1 == '\t')
+ p1++;
+ value = p1;
+
+ if(*value == '\0'){
+ if(v->is_list){
+ v->cmdline_val.l = (char **)fs_get(2 * sizeof(char *));
+ /*
+ * we let people leave off the quotes on command line so that
+ * they don't have to mess with shell quoting
+ */
+ v->cmdline_val.l[0] = cpystr("");
+ v->cmdline_val.l[1] = NULL;
+ if(oldlvalue)
+ free_list_array(&oldlvalue);
+
+ }else{
+ v->cmdline_val.p = cpystr("");
+ }
+ return 1;
+ }
+
+ /*--value is non-empty--*/
+ if(*value == '"' && !v->is_list){
+ value++;
+ for(p1 = value; *p1 && *p1 != '"'; p1++);
+ if(*p1 == '"')
+ *p1 = '\0';
+ else
+ removing_trailing_white_space(value);
+ }else{
+ removing_trailing_white_space(value);
+ }
+
+ if(v->is_list){
+ int was_quoted = 0;
+ char *error = NULL;
+
+ count = 1;
+ for(p1=value; *p1; p1++){ /* generous count of list elements */
+ if(*p1 == '"') /* ignore ',' if quoted */
+ was_quoted = (was_quoted) ? 0 : 1;
+
+ if(*p1 == ',' && !was_quoted)
+ count++;
+ }
+
+ lvalue = parse_list(value, count, 0, &error);
+ if(error){
+ char buf[MAILTMPLEN];
+
+ snprintf(buf, sizeof(buf), "%s in %s = \"%s\"\n", error, v->name, value);
+ buf[sizeof(buf)-1] = '\0';
+ exceptional_exit(buf, -1);
+ }
+ /*
+ * Special case: turn "" strings into empty strings.
+ * This allows users to turn off default lists. For example,
+ * if smtp-server is set then a user could override smtp-server
+ * with smtp-server="".
+ */
+ for(i = 0; lvalue[i]; i++)
+ if(lvalue[i][0] == '"' &&
+ lvalue[i][1] == '"' &&
+ lvalue[i][2] == '\0')
+ lvalue[i][0] = '\0';
+ }
+
+ if(v->is_list){
+ if(oldlvalue){
+ char **combinedlvalue;
+ int j;
+
+ /* combine old and new cmdline lists */
+ for(count = 0, i = 0; oldlvalue[i]; i++, count++)
+ ;
+
+ for(i = 0; lvalue && lvalue[i]; i++, count++)
+ ;
+
+ combinedlvalue = (char **) fs_get((count+1) * sizeof(char *));
+ memset(combinedlvalue, 0, (count+1) * sizeof(*combinedlvalue));
+
+ for(i = 0, j = 0; oldlvalue[i]; i++, j++)
+ combinedlvalue[j] = cpystr(oldlvalue[i]);
+
+ for(i = 0; lvalue && lvalue[i]; i++, j++)
+ combinedlvalue[j] = cpystr(lvalue[i]);
+
+ v->cmdline_val.l = combinedlvalue;
+
+ fs_give((void **) &oldlvalue);
+ if(lvalue)
+ fs_give((void **) &lvalue);
+ }
+ else
+ v->cmdline_val.l = lvalue;
+ }
+ else
+ v->cmdline_val.p = cpystr(value);
+
+ return 1;
+}
diff --git a/alpine/arg.h b/alpine/arg.h
new file mode 100644
index 00000000..339e793b
--- /dev/null
+++ b/alpine/arg.h
@@ -0,0 +1,52 @@
+/*
+ * $Id: arg.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_ARG_INCLUDED
+#define PINE_ARG_INCLUDED
+
+
+#include "../pith/state.h"
+#include "../pith/string.h"
+
+
+/*
+ * Used by pine_args to tell caller what was found;
+ */
+typedef struct argdata {
+ enum {aaFolder = 0, aaMore, aaURL, aaMail,
+ aaPrcCopy, aaAbookCopy} action;
+ union {
+ char *folder;
+ char *file;
+ struct {
+ STRLIST_S *addrlist;
+ PATMT *attachlist;
+ } mail;
+ struct {
+ char *local;
+ char *remote;
+ } copy;
+ } data;
+ char *url;
+} ARGDATA_S;
+
+
+/* exported protoypes */
+void pine_args(struct pine *, int, char **, ARGDATA_S *);
+void display_args_err(char *, char **, int);
+void args_help(void);
+
+
+#endif /* PINE_ARG_INCLUDED */
diff --git a/alpine/brk2pine.sh b/alpine/brk2pine.sh
new file mode 100755
index 00000000..5b4bff09
--- /dev/null
+++ b/alpine/brk2pine.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+#
+# $Id: brk2pine.sh 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+#
+# T H E P I N E M A I L S Y S T E M
+#
+# Laurence Lundblade and Mike Seibel
+# Networks and Distributed Computing
+# Computing and Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, Washington, 98195, USA
+# Internet: lgl@CAC.Washington.EDU
+# mikes@CAC.Washington.EDU
+#
+# Please address all bugs and comments to "pine-bugs@cac.washington.edu"
+#
+#
+# Pine and Pico are registered trademarks of the University of Washington.
+# No commercial use of these trademarks may be made without prior written
+# permission of the University of Washington.
+#
+# Pine, Pico, and Pilot software and its included text are Copyright
+# 1989-1996 by the University of Washington.
+#
+# The full text of our legal notices is contained in the file called
+# CPYRIGHT, included with this distribution.
+#
+#
+# Pine is in part based on The Elm Mail System:
+# ***********************************************************************
+# * The Elm Mail System - Revision: 2.13 *
+# * *
+# * Copyright (c) 1986, 1987 Dave Taylor *
+# * Copyright (c) 1988, 1989 USENET Community Trust *
+# ***********************************************************************
+#
+#
+
+
+#
+# A filter to convert personal mail aliases in a .mailrc file into
+# pine address book format.
+#
+# Usage: program [.mailrc] >> .addressbook
+#
+# Corey Satten, corey@cac.washington.edu, 9/25/91
+#
+sed -n '
+# first fold continued lines (ending in \) into a single long line
+ /\\[ ]*$/ {
+ : more
+ s/\\//g
+ N
+ s/\n/ /
+ /\\/b more
+ }
+# next convert all sequences of whitespace into single space
+ s/[ ][ ]*/ /g
+# finally, reformat and print lines containing alias as the first word
+ /^ *alias / {
+ s/^ *alias \([!-~][!-~]*\) \(.*\)$/\1 \1 (\2)/
+ s/ /,/g
+ s/(\([^,]*\))/\1/
+ p
+ }
+' ${*-$HOME/.mailrc}
diff --git a/alpine/busy.c b/alpine/busy.c
new file mode 100644
index 00000000..a0c71ab0
--- /dev/null
+++ b/alpine/busy.c
@@ -0,0 +1,473 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: signal.c 138 2006-09-22 22:12:03Z mikes@u.washington.edu $";
+#endif
+
+/* ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ Implement busy_cue spinner
+ ====*/
+
+
+#include <system.h>
+#include <general.h>
+
+#include "../c-client/c-client.h"
+
+#include "../pith/conf.h"
+#include "../pith/state.h"
+#include "../pith/status.h"
+#include "../pith/busy.h"
+#include "../pith/debug.h"
+#include "../pith/help.h"
+
+#include "../pith/charconv/utf8.h"
+
+#ifdef _WINDOWS
+#include "../pico/osdep/mswin.h"
+#endif
+
+#include "status.h"
+#include "after.h"
+#include "busy.h"
+
+
+static int dotcount;
+static char busy_message[MAX_BM + 1];
+static int busy_cue_pause;
+static int busy_width;
+static int final_message;
+static int final_message_pri;
+
+static percent_done_t percent_done_ptr;
+
+#define MAX_SPINNER_WIDTH 32
+#define MAX_SPINNER_ELEMENTS 64
+
+static int spinner = 0;
+static struct _spinner {
+ int width,
+ elements;
+ char *bars[MAX_SPINNER_ELEMENTS];
+ unsigned used_this_round:1;
+} spinners[] = {
+ {4, 4, {"<|> ", "</> ", "<-> ", "<\\> "}},
+ {8, 34, {"~~~~~~~~", "~~~~~~~~", "/~~~~~~~", "/~~~~~~~", "_/~~~~~~",
+ "_/~~~~~~", "__/~~~~~", "__/~~~~~", "___/~~~~", "___/~~~~",
+ "\\___/~~~", "\\___/~~~", "~\\___/~~", "~\\___/~~", "~~\\___/~",
+ "~~\\___/~", "~~~\\___/", "~~~\\___/", "^~~~\\___", "^~~~\\___",
+ "~^~~~\\__", "~^~~~\\__", "~~^~~~\\_", "~~^~~~\\_", "~~~^~~~\\",
+ "~~~^~~~\\", "~~~~^~~~", "~~~~^~~~", "~~~~~^~~", "~~~~~^~~",
+ "~~~~~~^~", "~~~~~~^~", "~~~~~~~^", "~~~~~~~^"}},
+ {9, 14 , {"| |", "| |", "-| |-", "-| |-",
+ "--| |--", "--| |--", "---||---", "---||---",
+ "--|WA|--", "--|WA|--", "-|WAIT|-", "-|WAIT|-",
+ "| WAIT |", "| WAIT |"}},
+ {9, 24 , {"o | ", "o | ", " o | ", " o | ",
+ " o | ", " o | ", " o| ", " o| ",
+ " \\ ", " \\ ", " -o ", " -o ",
+ " / o ", " / o ", " | o ", " | o ",
+ " \\ o", " \\ o", " - ", " - ",
+ " / ", " / ", " | ", " | "}},
+ {8, 38, {"~~~~~~~~", "~~~~~~~~", "/~~~~~~~", "/~~~~~~~", "_/~~~~~~",
+ "_/~~~~~~", "__/~~~~~", "__/~~~~~", "___/~~~~", "___/~~~~",
+ "\\___/~~~", "\\___/~~~", "~\\___/~~", "~\\___/~~", "~~\\___/~",
+ "~~\\___/~", "~~~\\___/", "~~~\\___/", "~~~~\\___", "~~~~\\___",
+ "\\~~~~\\__", "\\~~~~\\__", "/\\~~~~\\_", "/\\~~~~\\_", "~/\\~~~~\\",
+ "~/\\~~~~\\", "~~/\\~~~~", "~~/\\~~~~", "~~~/\\~~~", "~~~/\\~~~",
+ "~~~~/\\~~", "~~~~/\\~~", "~~~~~/\\~", "~~~~~/\\~", "~~~~~~/\\",
+ "~~~~~~/\\", "~~~~~~~/", "~~~~~~~/"}},
+ {6, 10, {"> ", "<> ", "><> ", " ><> ", " ><> ",
+ " ><> ", " ><> ", " ><>", " ><", " >"}},
+ {6, 10, {" <", " <>", " <><", " <>< ", " <>< ",
+ " <>< ", " <>< ", "<>< ", ">< ", "< "}},
+ {6, 10, {"> <", "<> <>", "><> <><", " ><><>< ", " ><>< ",
+ " <><> ", " <><><> ", "<>< ><>", ">< ><", "< >"}},
+ {11, 4, {"--|-(o)-|--", "--/-(o)-\\--", "----(o)----", "--\\-(o)-/--"}},
+ {6, 7, {"\\____/", "_\\__/_", "__\\/__", "__/\\__",
+ "_/__\\_", "/____\\", "|____|"}},
+ {4, 4, {"<|> ", "<\\> ", "<-> ", "</> "}},
+ {4, 10,{"| ", " / ", " _ ", " \\ ", " | ", " | ", " \\ ",
+ " _ ", " / ", "| "}},
+ {4, 8, {"_ _ ", "\\ \\ ", " | |", " / /", " _ _", " / /", " | |", "\\ \\ "}},
+ {4, 8, {"_ ", "\\ ", " | ", " / ", " _ ", " \\ ", " | ", "/ "}},
+ {4, 8, {"_ ", "\\ ", " | ", " / ", " _ ", " / ", " | ", "\\ "}},
+ {4, 4, {" . ", " o ", " O ", " o "}},
+ {4, 5, {"....", " ...", ". ..", ".. .", "... "}},
+ {4, 5, {" ", ". ", " . ", " . ", " ."}},
+ {4, 4, {".oOo", "oOo.", "Oo.o", "o.oO"}},
+ {4, 11,{"____", "\\___", "|\\__", "||\\_", "|||\\", "||||", "/|||",
+ "_/||", "__/|", "___/", "____"}},
+ {7, 9, {". .", " . . ", " . . ",
+ " . ", " + ", " * ", " X ",
+ " # ", " "}},
+ {4, 4, {". O ", "o o ", "O . ", "o o "}},
+ {4, 26,{"| ", "/ ", "_ ", "\\ ", " | ", " / ", " _ ", " \\ ",
+ " | ", " / ", " _ ", " \\ ", " |", " |", " \\ ", " _ ", " / ",
+ " | ", " \\ ", " _ ", " / ", " | ", "\\ ", "_ ", "/ ", "| "}},
+ {4, 8, {"* ", "-* ", "--* ", " --*", " --", " -", " ", " "}},
+ {4, 2, {"\\/\\/", "/\\/\\"}},
+ {4, 4, {"\\|/|", "|\\|/", "/|\\|", "|/|\\"}}
+};
+
+
+
+/*
+ * various pauses in 100th's of second
+ */
+#define BUSY_PERIOD_PERCENT 25 /* percent done update */
+#define BUSY_MSG_DONE 0 /* no more updates */
+#define BUSY_MSG_RETRY 25 /* message line conflict */
+#define BUSY_DELAY_PERCENT 33 /* pause before showing % */
+#define BUSY_DELAY_SPINNER 100 /* second pause before spinner */
+
+
+/* internal prototypes */
+int do_busy_cue(void *);
+void done_busy_cue(void *);
+
+
+/*
+ * Turn on a busy cue.
+ *
+ * msg -- the busy message to print in status line
+ * pc_f -- if non-null, call this function to get the percent done,
+ * (an integer between 0 and 100). If null, append dots.
+ * delay -- seconds to delay output of delay notification
+ *
+ * Returns: 0 If busy cue was already set up before we got here
+ * 1 If busy cue was not already set up.
+ *
+ * NOTE: busy_cue and cancel_busy_cue MUST be called symetrically in the
+ * same lexical scope.
+ */
+int
+busy_cue(char *msg, percent_done_t pc_f, int delay)
+{
+ AFTER_S *a = NULL, **ap;
+
+ dprint((9, "busy_cue(%s, %p, %d)\n", msg ? msg : "Busy", pc_f, delay));
+
+ if(!(ps_global && ps_global->ttyo)){
+ dprint((9, "busy_cue returns No (ttyo)"));
+ return(0);
+ }
+
+ /*
+ * If we're already busy'ing, but a cue is invoked that
+ * supplies more useful info, use it...
+ */
+ if(after_active){
+ if(msg || pc_f)
+ stop_after(1); /* uninstall old handler */
+ else
+ return(0); /* nothing to add, return */
+ }
+
+ /* get ready to set up list of AFTER_S's */
+ ap = &a;
+
+ dotcount = 0;
+ percent_done_ptr = pc_f;
+
+ if(msg){
+ strncpy(busy_message, msg, sizeof(busy_message));
+ final_message = 1;
+ }
+ else{
+ strncpy(busy_message, "Busy", sizeof(busy_message));
+ final_message = 0;
+ }
+
+ busy_message[sizeof(busy_message)-1] = '\0';
+ busy_width = utf8_width(busy_message);
+
+ if(!delay){
+ char progress[MAX_SCREEN_COLS+1];
+ int space_left, slots_used;
+
+ final_message = 1;
+ space_left = (ps_global->ttyo ? ps_global->ttyo->screen_cols
+ : 80) - busy_width - 2; /* 2 is for [] */
+ slots_used = MAX(0, MIN(space_left-3, 10));
+
+ if(percent_done_ptr && slots_used >= 4){
+ snprintf(progress, sizeof(progress), "%s |%*s|", busy_message, slots_used, "");
+ progress[sizeof(progress)-1] = '\0';
+ }
+ else{
+ dotcount++;
+ snprintf(progress, sizeof(progress), "%s%*s", busy_message,
+ spinners[spinner].width + 1, "");
+ progress[sizeof(progress)-1] = '\0';
+ }
+
+
+ if(status_message_remaining()){
+ char buf[sizeof(progress) + 30];
+ char *append = " [not actually shown]";
+
+ strncpy(buf, progress, sizeof(buf)-1);
+ buf[sizeof(buf)-1] = '\0';
+
+ strncat(buf, append, sizeof(buf) - strlen(buf) - 1);
+ buf[sizeof(buf)-1] = '\0';
+
+ add_review_message(buf, -1);
+ }
+ else{
+ q_status_message(SM_ORDER, 0, 1, progress);
+
+ /*
+ * We use display_message so that the initial message will
+ * be forced out only if there is not a previous message
+ * currently being displayed that hasn't been displayed for
+ * its min display time yet. In that case, we don't want
+ * to force out the initial message.
+ */
+ display_message('x');
+ }
+
+ fflush(stdout);
+ }
+
+ /*
+ * Randomly select one of the animations, even taking care
+ * to run through all of them before starting over!
+ * The user won't actually see them all because most of them
+ * will never show up before they are canceled, but it
+ * should still help.
+ */
+ spinner = -1;
+ if(F_OFF(F_USE_BORING_SPINNER,ps_global) && ps_global->active_status_interval > 0){
+ int arrsize, eligible, pick_this_one, i, j;
+
+ arrsize = sizeof(spinners)/sizeof(struct _spinner);
+
+ /* how many of them are eligible to be used this round? */
+ for(eligible = i = 0; i < arrsize; i++)
+ if(!spinners[i].used_this_round)
+ eligible++;
+
+ if(eligible == 0) /* reset */
+ for(eligible = i = 0; i < arrsize; i++){
+ spinners[i].used_this_round = 0;
+ eligible++;
+ }
+
+ if(eligible > 0){ /* it will be */
+ pick_this_one = random() % eligible;
+ for(j = i = 0; i < arrsize && spinner < 0; i++)
+ if(!spinners[i].used_this_round){
+ if(j == pick_this_one)
+ spinner = i;
+ else
+ j++;
+ }
+ }
+ }
+
+ if(spinner < 0 || spinner > sizeof(spinners)/sizeof(struct _spinner) -1)
+ spinner = 0;
+
+ *ap = new_afterstruct();
+ (*ap)->delay = (pc_f) ? BUSY_DELAY_PERCENT : BUSY_DELAY_SPINNER;
+ (*ap)->f = do_busy_cue;
+ (*ap)->cf = done_busy_cue;
+ ap = &(*ap)->next;
+
+ start_after(a); /* launch cue handler */
+
+#ifdef _WINDOWS
+ mswin_setcursor(MSWIN_CURSOR_BUSY);
+#endif
+
+ return(1);
+}
+
+
+/*
+ * If final_message was set when busy_cue was called:
+ * and message_pri = -1 -- no final message queued
+ * else final message queued with min equal to message_pri
+ */
+void
+cancel_busy_cue(int message_pri)
+{
+ dprint((9, "cancel_busy_cue(%d)\n", message_pri));
+
+ final_message_pri = message_pri;
+
+ stop_after(0);
+}
+
+/*
+ * suspend_busy_cue - continue previously installed busy_cue.
+ */
+void
+suspend_busy_cue(void)
+{
+ dprint((9, "suspend_busy_cue\n"));
+
+ if(after_active)
+ busy_cue_pause = 1;
+}
+
+
+/*
+ * resume_busy_cue - continue previously installed busy_cue.
+ */
+void
+resume_busy_cue(unsigned int pause)
+{
+ dprint((9, "resume_busy_cue\n"));
+
+ if(after_active)
+ busy_cue_pause = 0;
+}
+
+
+/*
+ * do_busy_cue - paint the busy cue and return how long caller
+ * should pause before calling us again
+ */
+int
+do_busy_cue(void *data)
+{
+ int space_left, slots_used, period;
+ char dbuf[MAX_SCREEN_COLS+1];
+
+ /* Don't wipe out any displayed status message prematurely */
+ if(status_message_remaining() || busy_cue_pause)
+ return(BUSY_MSG_RETRY);
+
+ space_left = (ps_global->ttyo ? ps_global->ttyo->screen_cols : 80) -
+ busy_width - 2; /* 2 is for [] */
+ slots_used = MAX(0, MIN(space_left-3, 10));
+
+ if(percent_done_ptr && slots_used >= 4){
+ int completed, pd;
+ char *s;
+
+ pd = (*percent_done_ptr)();
+ pd = MIN(MAX(0, pd), 100);
+
+ completed = (pd * slots_used) / 100;
+ snprintf(dbuf, sizeof(dbuf), "%s |%s%s%*s|", busy_message,
+ completed > 1 ? repeat_char(completed-1, pd==100 ? ' ' : '-') : "",
+ (completed > 0 && pd != 100) ? ">" : "",
+ slots_used - completed, "");
+ dbuf[sizeof(dbuf)-1] = '\0';
+
+ if(slots_used == 10){
+ s = dbuf + strlen(dbuf) - 8;
+ if(pd < 10){
+ s++; s++;
+ *s++ = '0' + pd;
+ }
+ else if(pd < 100){
+ s++;
+ *s++ = '0' + pd / 10;
+ *s++ = '0' + pd % 10;
+ }
+ else{
+ *s++ = '1';
+ *s++ = '0';
+ *s++ = '0';
+ }
+
+ *s = '%';
+ }
+
+ period = BUSY_PERIOD_PERCENT;
+ }
+ else{
+ char b[MAX_SPINNER_WIDTH + 2];
+ int ind;
+
+ ind = (dotcount % spinners[spinner].elements);
+
+ spinners[spinner].used_this_round = 1;
+ if(space_left >= spinners[spinner].width + 1){
+ b[0] = SPACE;
+ strncpy(b+1,
+ (ps_global->active_status_interval > 0)
+ ? spinners[spinner].bars[ind] : "... ", sizeof(b)-1);
+ b[sizeof(b)-1] = '\0';
+ }
+ else if(space_left >= 2 && space_left < sizeof(b)){
+ b[0] = '.';
+ b[1] = '.';
+ b[2] = '.';
+ b[space_left] = '\0';
+ }
+ else
+ b[0] = '\0';
+
+ snprintf(dbuf, sizeof(dbuf), "%s%s", busy_message, b);
+ dbuf[sizeof(dbuf)-1] = '\0';
+
+ /* convert interval to delay in 100ths of second */
+ period = (ps_global->active_status_interval > 0)
+ ? (100 / MIN(10, ps_global->active_status_interval)) : BUSY_MSG_DONE;
+ }
+
+ status_message_write(dbuf, 1);
+ dotcount++;
+ fflush(stdout);
+
+ return(period);
+}
+
+
+void
+done_busy_cue(void *data)
+{
+ int space_left, slots_used;
+
+ if(final_message && final_message_pri >= 0){
+ char progress[MAX_SCREEN_COLS+1];
+
+ /* 2 is for [] */
+ space_left = (ps_global->ttyo ? ps_global->ttyo->screen_cols : 80) - busy_width - 2;
+ slots_used = MAX(0, MIN(space_left-3, 10));
+
+ if(percent_done_ptr && slots_used >= 4){
+ int left, right;
+
+ right = (slots_used - 4)/2;
+ left = slots_used - 4 - right;
+ snprintf(progress, sizeof(progress), "%s |%*s100%%%*s|",
+ busy_message, left, "", right, "");
+ progress[sizeof(progress)-1] = '\0';
+ q_status_message(SM_ORDER,
+ final_message_pri>=2 ? MAX(final_message_pri,3) : 0,
+ final_message_pri+2, progress);
+ }
+ else{
+ int padding;
+
+ padding = MAX(0, MIN(space_left-5, spinners[spinner].width-4));
+
+ snprintf(progress, sizeof(progress), "%s %*sDONE", busy_message,
+ padding, "");
+ progress[sizeof(progress)-1] = '\0';
+ q_status_message(SM_ORDER,
+ final_message_pri>=2 ? MAX(final_message_pri,3) : 0,
+ final_message_pri+2, progress);
+ }
+ }
+
+ mark_status_dirty();
+}
diff --git a/alpine/busy.h b/alpine/busy.h
new file mode 100644
index 00000000..63602867
--- /dev/null
+++ b/alpine/busy.h
@@ -0,0 +1,30 @@
+/*
+ * $Id: busy.h 137 2006-09-22 21:34:06Z mikes@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_BUSY_INCLUDED
+#define PINE_BUSY_INCLUDED
+
+
+#define MAX_BM 150 /* max length of busy message */
+
+
+/* exported prototypes */
+
+void suspend_busy_cue(void);
+void resume_busy_cue(unsigned);
+
+
+#endif /* PINE_BUSY_INCLUDED */
diff --git a/alpine/colorconf.c b/alpine/colorconf.c
new file mode 100644
index 00000000..e0aee596
--- /dev/null
+++ b/alpine/colorconf.c
@@ -0,0 +1,2788 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: colorconf.c 934 2008-02-23 00:44:29Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#include "headers.h"
+#include "colorconf.h"
+#include "keymenu.h"
+#include "status.h"
+#include "confscroll.h"
+#include "radio.h"
+#include "mailview.h"
+#include "../pith/state.h"
+#include "../pith/util.h"
+#include "../pith/color.h"
+#include "../pith/icache.h"
+#include "../pith/mailcmd.h"
+#include "../pith/list.h"
+
+
+/*
+ * Internal prototypes
+ */
+char *color_setting_text_line(struct pine *, struct variable *);
+void revert_to_saved_color_config(struct pine *, SAVED_CONFIG_S *);
+SAVED_CONFIG_S *save_color_config_vars(struct pine *);
+void free_saved_color_config(struct pine *, SAVED_CONFIG_S **);
+void color_config_init_display(struct pine *, CONF_S **, CONF_S **);
+void add_header_color_line(struct pine *, CONF_S **, char *, int);
+int is_rgb_color(char *);
+char *new_color_line(char *, int, int, int);
+int color_text_tool(struct pine *, int, CONF_S **, unsigned);
+int offer_normal_color_for_var(struct pine *, struct variable *);
+int offer_none_color_for_var(struct pine *, struct variable *);
+void color_update_selected(struct pine *, CONF_S *, char *, char *, int);
+int color_edit_screen(struct pine *, CONF_S **);
+
+
+int treat_color_vars_as_text;
+
+
+void
+color_config_screen(struct pine *ps, int edit_exceptions)
+{
+ CONF_S *ctmp = NULL, *first_line = NULL;
+ SAVED_CONFIG_S *vsave;
+ OPT_SCREEN_S screen;
+ int readonly_warning = 0;
+
+ ps->next_screen = SCREEN_FUN_NULL;
+
+ mailcap_free(); /* free resources we won't be using for a while */
+
+ if(ps->fix_fixed_warning)
+ offer_to_fix_pinerc(ps);
+
+ ew = edit_exceptions ? ps_global->ew_for_except_vars : Main;
+
+ if(ps->restricted)
+ readonly_warning = 1;
+ else{
+ PINERC_S *prc = NULL;
+
+ switch(ew){
+ case Main:
+ prc = ps->prc;
+ break;
+ case Post:
+ prc = ps->post_prc;
+ break;
+ default:
+ break;
+ }
+
+ readonly_warning = prc ? prc->readonly : 1;
+ if(prc && prc->quit_to_edit){
+ quit_to_edit_msg(prc);
+ return;
+ }
+ }
+
+ color_config_init_display(ps, &ctmp, &first_line);
+
+ vsave = save_color_config_vars(ps);
+
+ memset(&screen, 0, sizeof(screen));
+ screen.deferred_ro_warning = readonly_warning;
+ switch(conf_scroll_screen(ps, &screen, first_line,
+ edit_exceptions ? _("SETUP COLOR EXCEPTIONS")
+ : _("SETUP COLOR"),
+ /* TRANSLATORS: Print something1 using something2.
+ configuration is something1 */
+ _("configuration"), 0)){
+ case 0:
+ break;
+
+ case 1:
+ write_pinerc(ps, ew, WRP_NONE);
+ break;
+
+ case 10:
+ revert_to_saved_color_config(ps, vsave);
+ break;
+
+ default:
+ q_status_message(SM_ORDER, 7, 10,
+ _("conf_scroll_screen bad ret in color_config"));
+ break;
+ }
+
+ free_saved_color_config(ps, &vsave);
+
+#ifdef _WINDOWS
+ mswin_set_quit_confirm (F_OFF(F_QUIT_WO_CONFIRM, ps_global));
+#endif
+}
+
+
+char *
+sample_text(struct pine *ps, struct variable *v)
+{
+ char *ret = SAMP2;
+ char *pvalfg, *pvalbg;
+
+ pvalfg = PVAL(v, ew);
+ pvalbg = PVAL(v+1, ew);
+
+ if((v && v->name &&
+ srchstr(v->name, "-foreground-color") &&
+ pvalfg && pvalfg[0] && pvalbg && pvalbg[0]) ||
+ (v == &ps->vars[V_VIEW_HDR_COLORS] || v == &ps->vars[V_KW_COLORS]))
+ ret = SAMP1;
+
+ return(ret);
+}
+
+
+char *
+sampleexc_text(struct pine *ps, struct variable *v)
+{
+ char *ret = "";
+ char *pvalfg, *pvalbg;
+
+ pvalfg = PVAL(v, Post);
+ pvalbg = PVAL(v+1, Post);
+ if(v && color_holding_var(ps, v) &&
+ srchstr(v->name, "-foreground-color")){
+ if(ew == Main && pvalfg && pvalfg[0] && pvalbg && pvalbg[0])
+ ret = SAMPEXC;
+ }
+
+ return(ret);
+}
+
+
+char *
+color_setting_text_line(struct pine *ps, struct variable *v)
+{
+ char *p;
+ char tmp[1200];
+
+ p = sampleexc_text(ps, v);
+
+ /*
+ * Don't need to use utf8_snprintf if we're not trying to
+ * control the widths of fields.
+ */
+ snprintf(tmp, sizeof(tmp), "%s %s%*s%s%s", SAMPLE_LEADER,
+ sample_text(ps,v), *p ? SBS : 0, "", p,
+ color_parenthetical(v));
+ return(cpystr(tmp));
+}
+
+
+/*
+ * Compare saved user_val with current user_val to see if it changed.
+ * If any have changed, change it back and take the appropriate action.
+ */
+void
+revert_to_saved_color_config(struct pine *ps, SAVED_CONFIG_S *vsave)
+{
+ struct variable *vreal;
+ SAVED_CONFIG_S *v;
+ int i, n;
+ int changed = 0;
+ char *pval, **apval, **lval, ***alval;
+
+ v = vsave;
+ for(vreal = ps->vars; vreal->name; vreal++,v++){
+ if(!(color_related_var(ps, vreal) || vreal==&ps->vars[V_FEATURE_LIST]))
+ continue;
+
+ if(vreal->is_list){
+ lval = LVAL(vreal, ew);
+ alval = ALVAL(vreal, ew);
+
+ if((v->saved_user_val.l && !lval)
+ || (!v->saved_user_val.l && lval))
+ changed++;
+ else if(!v->saved_user_val.l && !lval)
+ ;/* no change, nothing to do */
+ else
+ for(i = 0; v->saved_user_val.l[i] || lval[i]; i++)
+ if((v->saved_user_val.l[i]
+ && (!lval[i]
+ || strcmp(v->saved_user_val.l[i], lval[i])))
+ ||
+ (!v->saved_user_val.l[i] && lval[i])){
+ changed++;
+ break;
+ }
+
+ if(changed){
+ char **list;
+
+ if(alval){
+ if(*alval)
+ free_list_array(alval);
+
+ /* copy back the original one */
+ if(v->saved_user_val.l){
+ list = v->saved_user_val.l;
+ n = 0;
+ /* count how many */
+ while(list[n])
+ n++;
+
+ *alval = (char **)fs_get((n+1) * sizeof(char *));
+
+ for(i = 0; i < n; i++)
+ (*alval)[i] = cpystr(v->saved_user_val.l[i]);
+
+ (*alval)[n] = NULL;
+ }
+ }
+ }
+ }
+ else{
+ pval = PVAL(vreal, ew);
+ apval = APVAL(vreal, ew);
+
+ if((v->saved_user_val.p &&
+ (!pval || strcmp(v->saved_user_val.p, pval))) ||
+ (!v->saved_user_val.p && pval)){
+ /* It changed, fix it */
+ changed++;
+ if(apval){
+ /* free the changed value */
+ if(*apval)
+ fs_give((void **)apval);
+
+ if(v->saved_user_val.p)
+ *apval = cpystr(v->saved_user_val.p);
+ }
+ }
+ }
+
+ if(changed){
+ if(vreal == &ps->vars[V_FEATURE_LIST])
+ set_feature_list_current_val(vreal);
+ else
+ set_current_val(vreal, TRUE, FALSE);
+
+ fix_side_effects(ps, vreal, 1);
+ }
+ }
+
+ if(changed){
+ set_current_color_vals(ps);
+ ClearScreen();
+ ps->mangled_screen = 1;
+ }
+}
+
+
+SAVED_CONFIG_S *
+save_color_config_vars(struct pine *ps)
+{
+ struct variable *vreal;
+ SAVED_CONFIG_S *vsave, *v;
+
+ vsave = (SAVED_CONFIG_S *)fs_get((V_LAST_VAR+1)*sizeof(SAVED_CONFIG_S));
+ memset((void *)vsave, 0, (V_LAST_VAR+1)*sizeof(SAVED_CONFIG_S));
+ v = vsave;
+ for(vreal = ps->vars; vreal->name; vreal++,v++){
+ if(!(color_related_var(ps, vreal) || vreal==&ps->vars[V_FEATURE_LIST]))
+ continue;
+
+ if(vreal->is_list){
+ int n, i;
+ char **list;
+
+ if(LVAL(vreal, ew)){
+ /* count how many */
+ n = 0;
+ list = LVAL(vreal, ew);
+ while(list[n])
+ n++;
+
+ v->saved_user_val.l = (char **)fs_get((n+1) * sizeof(char *));
+ memset((void *)v->saved_user_val.l, 0, (n+1)*sizeof(char *));
+ for(i = 0; i < n; i++)
+ v->saved_user_val.l[i] = cpystr(list[i]);
+
+ v->saved_user_val.l[n] = NULL;
+ }
+ }
+ else{
+ if(PVAL(vreal, ew))
+ v->saved_user_val.p = cpystr(PVAL(vreal, ew));
+ }
+ }
+
+ return(vsave);
+}
+
+
+void
+free_saved_color_config(struct pine *ps, SAVED_CONFIG_S **vsavep)
+{
+ struct variable *vreal;
+ SAVED_CONFIG_S *v;
+
+ if(vsavep && *vsavep){
+ for(v = *vsavep, vreal = ps->vars; vreal->name; vreal++,v++){
+ if(!(color_related_var(ps, vreal) ||
+ (vreal == &ps->vars[V_FEATURE_LIST])))
+ continue;
+
+ if(vreal->is_list){ /* free saved_user_val.l */
+ if(v && v->saved_user_val.l)
+ free_list_array(&v->saved_user_val.l);
+ }
+ else if(v && v->saved_user_val.p)
+ fs_give((void **)&v->saved_user_val.p);
+ }
+
+ fs_give((void **)vsavep);
+ }
+}
+
+
+void
+color_config_init_display(struct pine *ps, CONF_S **ctmp, CONF_S **first_line)
+{
+ char **lval;
+ int i, saw_first_index = 0;
+ struct variable *vtmp;
+ char *dashes = "--------------";
+
+#ifndef _WINDOWS
+ vtmp = &ps->vars[V_COLOR_STYLE];
+
+ new_confline(ctmp);
+ (*ctmp)->flags |= (CF_NOSELECT | CF_STARTITEM);
+ (*ctmp)->keymenu = &config_radiobutton_keymenu;
+ (*ctmp)->tool = NULL;
+ (*ctmp)->varname = cpystr("Color Style");
+ (*ctmp)->varnamep = *ctmp;
+
+ standard_radio_setup(ps, ctmp, vtmp, first_line);
+
+ new_confline(ctmp);
+ /* Blank line */
+ (*ctmp)->flags |= (CF_NOSELECT | CF_B_LINE);
+
+ if(!pico_usingcolor()){
+ /* add a line explaining that color is not turned on */
+ new_confline(ctmp);
+ (*ctmp)->help = NO_HELP;
+ (*ctmp)->flags |= CF_NOSELECT;
+ (*ctmp)->value = cpystr(COLORNOSET);
+
+ new_confline(ctmp);
+ /* Blank line */
+ (*ctmp)->flags |= (CF_NOSELECT | CF_B_LINE);
+ }
+
+#endif
+
+ vtmp = &ps->vars[V_INDEX_COLOR_STYLE];
+
+ new_confline(ctmp);
+ (*ctmp)->flags |= (CF_NOSELECT | CF_STARTITEM);
+ (*ctmp)->keymenu = &config_radiobutton_keymenu;
+ (*ctmp)->tool = NULL;
+ (*ctmp)->varname = cpystr(_("Current Indexline Style"));
+ (*ctmp)->varnamep = *ctmp;
+
+ standard_radio_setup(ps, ctmp, vtmp, NULL);
+
+ new_confline(ctmp);
+ /* Blank line */
+ (*ctmp)->flags |= (CF_NOSELECT | CF_B_LINE);
+
+ vtmp = &ps->vars[V_TITLEBAR_COLOR_STYLE];
+
+ new_confline(ctmp);
+ (*ctmp)->flags |= (CF_NOSELECT | CF_STARTITEM);
+ (*ctmp)->keymenu = &config_radiobutton_keymenu;
+ (*ctmp)->tool = NULL;
+ (*ctmp)->varname = cpystr(_("Titlebar Color Style"));
+ (*ctmp)->varnamep = *ctmp;
+
+ standard_radio_setup(ps, ctmp, vtmp, NULL);
+
+ new_confline(ctmp);
+ /* Blank line */
+ (*ctmp)->flags |= (CF_NOSELECT | CF_B_LINE);
+
+ new_confline(ctmp);
+ /* title before general colors */
+ (*ctmp)->help = NO_HELP;
+ (*ctmp)->flags |= CF_NOSELECT;
+ (*ctmp)->value = cpystr(dashes);
+ new_confline(ctmp);
+ (*ctmp)->help = NO_HELP;
+ (*ctmp)->flags |= CF_NOSELECT;
+ (*ctmp)->value = cpystr(_("GENERAL COLORS"));
+ new_confline(ctmp);
+ (*ctmp)->help = NO_HELP;
+ (*ctmp)->flags |= CF_NOSELECT;
+ (*ctmp)->value = cpystr(dashes);
+
+ for(vtmp = ps->vars; vtmp->name; vtmp++){
+ if(!color_holding_var(ps, vtmp))
+ continue;
+
+ /* If not foreground, skip it */
+ if(!srchstr(vtmp->name, "-foreground-color"))
+ continue;
+
+ /* skip this for now and include it with HEADER COLORS */
+ if(vtmp == &ps->vars[V_HEADER_GENERAL_FORE_COLOR])
+ continue;
+
+ if(!saw_first_index && !struncmp(vtmp->name, "index-", 6)){
+ saw_first_index++;
+ new_confline(ctmp); /* Blank line */
+ (*ctmp)->flags |= (CF_NOSELECT | CF_B_LINE);
+ new_confline(ctmp);
+ (*ctmp)->help = NO_HELP;
+ (*ctmp)->flags |= CF_NOSELECT;
+ (*ctmp)->value = cpystr(dashes);
+ new_confline(ctmp);
+ (*ctmp)->help = NO_HELP;
+ (*ctmp)->flags |= CF_NOSELECT;
+ (*ctmp)->value = cpystr(_("INDEX COLORS"));
+ new_confline(ctmp);
+ (*ctmp)->help = NO_HELP;
+ (*ctmp)->flags |= CF_NOSELECT;
+ (*ctmp)->value = cpystr(dashes);
+ }
+
+ new_confline(ctmp);
+ /* Blank line */
+ (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
+
+ new_confline(ctmp)->var = vtmp;
+ if(first_line && !*first_line)
+ *first_line = *ctmp;
+
+ (*ctmp)->varnamep = *ctmp;
+ (*ctmp)->keymenu = &color_setting_keymenu;
+ (*ctmp)->help = config_help(vtmp - ps->vars, 0);
+ (*ctmp)->tool = color_setting_tool;
+ (*ctmp)->flags |= (CF_STARTITEM | CF_COLORSAMPLE | CF_POT_SLCTBL);
+ if(!pico_usingcolor())
+ (*ctmp)->flags |= CF_NOSELECT;
+
+ (*ctmp)->value = pretty_value(ps, *ctmp);
+ (*ctmp)->valoffset = COLOR_INDENT;
+ }
+
+ /*
+ * custom header colors
+ */
+ new_confline(ctmp); /* Blank line */
+ (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
+ new_confline(ctmp);
+ (*ctmp)->help = NO_HELP;
+ (*ctmp)->flags |= CF_NOSELECT;
+ (*ctmp)->value = cpystr(dashes);
+ new_confline(ctmp);
+ (*ctmp)->help = NO_HELP;
+ (*ctmp)->flags |= CF_NOSELECT;
+ (*ctmp)->value = cpystr(_("HEADER COLORS"));
+ new_confline(ctmp);
+ (*ctmp)->help = NO_HELP;
+ (*ctmp)->flags |= CF_NOSELECT;
+ (*ctmp)->value = cpystr(dashes);
+
+ vtmp = &ps->vars[V_HEADER_GENERAL_FORE_COLOR];
+ new_confline(ctmp);
+ /* Blank line */
+ (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
+
+ new_confline(ctmp)->var = vtmp;
+
+ (*ctmp)->varnamep = *ctmp;
+ (*ctmp)->keymenu = &color_setting_keymenu;
+ (*ctmp)->help = config_help(vtmp - ps->vars, 0);
+ (*ctmp)->tool = color_setting_tool;
+ (*ctmp)->flags |= (CF_STARTITEM | CF_COLORSAMPLE | CF_POT_SLCTBL);
+ if(!pico_usingcolor())
+ (*ctmp)->flags |= CF_NOSELECT;
+
+ (*ctmp)->value = pretty_value(ps, *ctmp);
+ (*ctmp)->valoffset = COLOR_INDENT;
+
+ vtmp = &ps->vars[V_VIEW_HDR_COLORS];
+ lval = LVAL(vtmp, ew);
+
+ if(lval && lval[0] && lval[0][0]){
+ for(i = 0; lval && lval[i]; i++)
+ add_header_color_line(ps, ctmp, lval[i], i);
+ }
+ else{
+ new_confline(ctmp); /* Blank line */
+ (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
+ new_confline(ctmp);
+ (*ctmp)->help = NO_HELP;
+ (*ctmp)->flags |= CF_NOSELECT;
+ (*ctmp)->value = cpystr(_(ADDHEADER_COMMENT));
+ (*ctmp)->valoffset = COLOR_INDENT;
+ }
+
+
+ /*
+ * custom keyword colors
+ */
+ new_confline(ctmp); /* Blank line */
+ (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
+ new_confline(ctmp);
+ (*ctmp)->help = NO_HELP;
+ (*ctmp)->flags |= CF_NOSELECT;
+ (*ctmp)->value = cpystr(dashes);
+ new_confline(ctmp);
+ (*ctmp)->help = NO_HELP;
+ (*ctmp)->flags |= CF_NOSELECT;
+ (*ctmp)->value = cpystr(KW_COLORS_HDR);
+ new_confline(ctmp);
+ (*ctmp)->help = NO_HELP;
+ (*ctmp)->flags |= CF_NOSELECT;
+ (*ctmp)->value = cpystr(dashes);
+
+
+ if(ps->keywords){
+ KEYWORD_S *kw;
+ char *name, *comment, *word;
+ int j, lv = 0, lc = 0, ltot = 0, eq_col = EQ_COL;
+
+ vtmp = &ps->vars[V_KW_COLORS];
+
+ /* first figure out widths for display */
+ for(kw = ps->keywords; kw; kw = kw->next){
+ word = kw->nick ? kw->nick : kw->kw ? kw->kw : "";
+ i = utf8_width(word);
+ if(lv < i)
+ lv = i;
+
+ j = 0;
+ if(kw->nick && kw->kw && kw->kw[0]){
+ word = kw->kw;
+ j = utf8_width(word) + 2;
+ if(lc < j)
+ lc = j;
+ }
+
+ if(ltot < (i + (j > 2 ? j : 0)))
+ ltot = (i + (j > 2 ? j : 0));
+ }
+
+ lv = MIN(lv, 100);
+ lc = MIN(lc, 100);
+ ltot = MIN(ltot, 100);
+
+ /*
+ * SC is width of " Color"
+ * SS is space between nickname and keyword value
+ * SW is space between words and Sample
+ */
+#define SC 6
+#define SS 1
+#define SW 3
+ if(COLOR_INDENT + SC + ltot + (lc>0?SS:0) > eq_col - SW){
+ eq_col = MIN(MAX(ps->ttyo->screen_cols - 10, 20),
+ COLOR_INDENT + SC + ltot + (lc>0?SS:0) + SW);
+ if(COLOR_INDENT + SC + ltot + (lc>0?SS:0) > eq_col - SW){
+ eq_col = MIN(MAX(ps->ttyo->screen_cols - 10, 20),
+ COLOR_INDENT + SC + lv + (lc>0?SS:0) + lc + SW);
+ if(COLOR_INDENT + SC + lv + (lc>0?SS:0) + lc > eq_col - SW){
+ lc = MIN(lc, MAX(eq_col - SW - COLOR_INDENT - SC - lv - SS, 7));
+ if(COLOR_INDENT + SC + lv + (lc>0?SS:0) + lc > eq_col - SW){
+ lc = 0;
+ if(COLOR_INDENT + SC + lv > eq_col - SW){
+ lv = MAX(eq_col - SW - COLOR_INDENT - SC, 7);
+ }
+ }
+ }
+ }
+ }
+
+ lval = LVAL(vtmp, ew);
+ if(lval && lval[0] && !strcmp(lval[0], INHERIT)){
+ new_confline(ctmp);
+ (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
+
+ new_confline(ctmp)->var = vtmp;
+ (*ctmp)->varnamep = *ctmp;
+ (*ctmp)->keymenu = &kw_color_setting_keymenu;
+ (*ctmp)->help = config_help(vtmp - ps->vars, 0);
+ (*ctmp)->tool = color_setting_tool;
+ (*ctmp)->varmem = CFC_SET_COLOR(i, 0);
+ (*ctmp)->valoffset = COLOR_INDENT;
+
+ (*ctmp)->flags = (CF_NOSELECT | CF_INHERIT);
+ }
+
+ /* now create the config lines */
+ for(kw = ps->keywords, i = 0; kw; kw = kw->next, i++){
+ char tmp[2000];
+
+ /* Blank line */
+ new_confline(ctmp);
+ (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
+
+ new_confline(ctmp)->var = vtmp;
+ (*ctmp)->varnamep = *ctmp;
+ (*ctmp)->keymenu = &kw_color_setting_keymenu;
+ (*ctmp)->help = config_help(vtmp - ps->vars, 0);
+ (*ctmp)->tool = color_setting_tool;
+ (*ctmp)->flags |= (CF_STARTITEM | CF_COLORSAMPLE | CF_POT_SLCTBL);
+ if(!pico_usingcolor())
+ (*ctmp)->flags |= CF_NOSELECT;
+
+ /*
+ * Not actually a color in this case, it is an index into
+ * the keywords list.
+ */
+ (*ctmp)->varmem = CFC_SET_COLOR(i, 0);
+
+ name = short_str(kw->nick ? kw->nick : kw->kw ? kw->kw : "",
+ tmp_20k_buf+10000, 1000, lv, EndDots);
+ if(lc > 0 && kw->nick && kw->kw && kw->kw[0])
+ comment = short_str(kw->kw, tmp_20k_buf+11000, SIZEOF_20KBUF - 11000, lc, EndDots);
+ else
+ comment = NULL;
+
+ utf8_snprintf(tmp, sizeof(tmp), "%.*w%*s%s%.*w%s Color%*s %s%s",
+ lv, name,
+ (lc > 0 && comment) ? SS : 0, "",
+ (lc > 0 && comment) ? "(" : "",
+ (lc > 0 && comment) ? lc : 0, (lc > 0 && comment) ? comment : "",
+ (lc > 0 && comment) ? ")" : "",
+ MAX(MIN(eq_col - COLOR_INDENT - MIN(lv,utf8_width(name))
+ - SC - 1
+ - ((lc > 0 && comment)
+ ? (SS+2+MIN(lc,utf8_width(comment))) : 0), 100), 0), "",
+ sample_text(ps,vtmp),
+ color_parenthetical(vtmp));
+
+ (*ctmp)->value = cpystr(tmp);
+ (*ctmp)->valoffset = COLOR_INDENT;
+ }
+ }
+ else{
+ new_confline(ctmp); /* Blank line */
+ (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
+ new_confline(ctmp);
+ (*ctmp)->help = NO_HELP;
+ (*ctmp)->flags |= CF_NOSELECT;
+ (*ctmp)->value = cpystr(_("[ Use Setup/Config command to add Keywords ]"));
+ (*ctmp)->valoffset = COLOR_INDENT;
+ }
+}
+
+
+char *
+color_parenthetical(struct variable *var)
+{
+ int norm, exc, exc_inh;
+ char **lval, *ret = "";
+
+ if(var == &ps_global->vars[V_VIEW_HDR_COLORS]
+ || var == &ps_global->vars[V_KW_COLORS]){
+ norm = (LVAL(var, Main) != NULL);
+ exc = (LVAL(var, ps_global->ew_for_except_vars) != NULL);
+ exc_inh = ((lval=LVAL(var, ps_global->ew_for_except_vars)) &&
+ lval[0] && !strcmp(INHERIT, lval[0]));
+
+ /* editing normal but there is an exception config */
+ if((ps_global->ew_for_except_vars != Main) && (ew == Main)){
+ if((exc && !exc_inh))
+ ret = _(" (overridden by exceptions)");
+ else if(exc && exc_inh)
+ ret = _(" (more in exceptions)");
+ }
+ /* editing exception config */
+ else if((ps_global->ew_for_except_vars != Main) &&
+ (ew == ps_global->ew_for_except_vars)){
+ if(exc && exc_inh && norm)
+ ret = _(" (more in main config)");
+ }
+ }
+
+ return(ret);
+}
+
+
+void
+add_header_color_line(struct pine *ps, CONF_S **ctmp, char *val, int which)
+{
+ struct variable *vtmp;
+ SPEC_COLOR_S *hc;
+ char tmp[100+1];
+ int l;
+
+ vtmp = &ps->vars[V_VIEW_HDR_COLORS];
+ l = strlen(HEADER_WORD);
+
+ /* Blank line */
+ new_confline(ctmp);
+ (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
+
+ new_confline(ctmp)->var = vtmp;
+ (*ctmp)->varnamep = *ctmp;
+ (*ctmp)->keymenu = &custom_color_setting_keymenu;
+ (*ctmp)->help = config_help(vtmp - ps->vars, 0);
+ (*ctmp)->tool = color_setting_tool;
+ (*ctmp)->flags |= (CF_STARTITEM | CF_COLORSAMPLE | CF_POT_SLCTBL);
+ if(!pico_usingcolor())
+ (*ctmp)->flags |= CF_NOSELECT;
+
+ /* which is an index into the variable list */
+ (*ctmp)->varmem = CFC_SET_COLOR(which, 0);
+
+ hc = spec_color_from_var(val, 0);
+ if(hc && hc->inherit)
+ (*ctmp)->flags = (CF_NOSELECT | CF_INHERIT);
+ else{
+ /*
+ * This isn't quite right because of the assumption that the
+ * first character of spec fits in one octet. I haven't tried
+ * to figure out what we're really trying to accomplish
+ * with all this. It probably doesn't happen in real life.
+ */
+ utf8_snprintf(tmp, sizeof(tmp), "%s%c%.*w Color%*w %s%s",
+ HEADER_WORD,
+ (hc && hc->spec) ? (islower((unsigned char)hc->spec[0])
+ ? toupper((unsigned char)hc->spec[0])
+ : hc->spec[0]) : '?',
+ MIN(utf8_width((hc && hc->spec && hc->spec[0]) ? hc->spec+1 : ""),30-l),
+ (hc && hc->spec && hc->spec[0]) ? hc->spec+1 : "",
+ MAX(EQ_COL - COLOR_INDENT -1 -
+ MIN(utf8_width((hc && hc->spec && hc->spec[0]) ? hc->spec+1 : ""),30-l)
+ - l - 6 - 1, 0), "",
+ sample_text(ps,vtmp),
+ color_parenthetical(vtmp));
+ tmp[sizeof(tmp)-1] = '\0';
+ (*ctmp)->value = cpystr(tmp);
+ }
+
+ (*ctmp)->valoffset = COLOR_INDENT;
+
+ if(hc)
+ free_spec_colors(&hc);
+}
+
+
+/*
+ * Set up the standard color setting display for one color.
+ *
+ * Args fg -- the current foreground color
+ * bg -- the current background color
+ * def -- default box should be checked
+ */
+void
+add_color_setting_disp(struct pine *ps, CONF_S **ctmp, struct variable *var,
+ CONF_S *varnamep, struct key_menu *km,
+ struct key_menu *cb_km, HelpType help, int indent,
+ int which, char *fg, char *bg, int def)
+{
+ int i, lv, count, trans_is_on, transparent;
+ char tmp[100+1];
+ char *title = _("HELP FOR SETTING UP COLOR");
+ int fg_is_custom = 1, bg_is_custom = 1;
+#ifdef _WINDOWS
+ CONF_S *cl_custom = NULL;
+#else
+ char *pvalfg, *pvalbg;
+#endif
+
+
+ /* find longest value's name */
+ count = pico_count_in_color_table();
+ lv = COLOR_BLOB_LEN;
+
+ trans_is_on = pico_trans_is_on();
+
+ /* put a title before list */
+ new_confline(ctmp);
+ (*ctmp)->varnamep = varnamep;
+ (*ctmp)->keymenu = km;
+ (*ctmp)->help = NO_HELP;
+ (*ctmp)->tool = color_setting_tool;
+ (*ctmp)->valoffset = indent;
+ (*ctmp)->flags |= CF_NOSELECT;
+ (*ctmp)->varmem = 0;
+
+ /*
+ * The width of Foreground plus the spaces before Background should
+ * be about lv + 5 + SPACE_BETWEEN_DOUBLEVARS.
+ */
+ (*ctmp)->value = cpystr("Foreground Background");
+
+ new_confline(ctmp)->var = var;
+ (*ctmp)->varnamep = varnamep;
+ (*ctmp)->keymenu = km;
+ (*ctmp)->help = NO_HELP;
+ (*ctmp)->tool = color_setting_tool;
+ (*ctmp)->valoffset = indent;
+ (*ctmp)->flags |= (CF_COLORSAMPLE | CF_NOSELECT);
+ (*ctmp)->varmem = CFC_SET_COLOR(which, 0);
+ (*ctmp)->value = color_setting_text_line(ps, var);
+
+ for(i = 0; i < count; i++){
+ new_confline(ctmp)->var = var;
+ (*ctmp)->varnamep = varnamep;
+ (*ctmp)->keymenu = km;
+ (*ctmp)->help = help;
+ (*ctmp)->help_title = title;
+ (*ctmp)->tool = color_setting_tool;
+ (*ctmp)->valoffset = indent;
+ /* 5 is length of "( ) " */
+ (*ctmp)->val2offset = indent + lv + 5 + SPACE_BETWEEN_DOUBLEVARS;
+ (*ctmp)->flags |= CF_DOUBLEVAR;
+ (*ctmp)->varmem = CFC_SET_COLOR(which, i);
+
+ transparent = (trans_is_on && i == count-1);
+ if(transparent)
+ (*ctmp)->help = h_config_usetransparent_color;
+
+ if(fg_is_custom && fg && !strucmp(color_to_canonical_name(fg), colorx(i)))
+ fg_is_custom = 0;
+
+ if(bg_is_custom && bg && !strucmp(color_to_canonical_name(bg), colorx(i)))
+ bg_is_custom = 0;
+
+ (*ctmp)->value = new_color_line(transparent ? COLOR_BLOB_TRAN
+ : COLOR_BLOB,
+ fg &&
+ !strucmp(color_to_canonical_name(fg), colorx(i)),
+ bg &&
+ !strucmp(color_to_canonical_name(bg), colorx(i)),
+ lv);
+ }
+
+#ifdef _WINDOWS
+ new_confline(ctmp)->var = var;
+ (*ctmp)->varnamep = varnamep;
+ (*ctmp)->keymenu = (km == &custom_color_changing_keymenu)
+ ? &custom_rgb_keymenu :
+ (km == &kw_color_changing_keymenu)
+ ? &kw_rgb_keymenu : &color_rgb_keymenu;
+ (*ctmp)->help = help;
+ (*ctmp)->help_title = title;
+ (*ctmp)->tool = color_setting_tool;
+ (*ctmp)->valoffset = indent;
+ /* 5 is length of "( ) " */
+ (*ctmp)->val2offset = indent + lv + 5 + SPACE_BETWEEN_DOUBLEVARS;
+ (*ctmp)->flags |= CF_DOUBLEVAR;
+ (*ctmp)->varmem = CFC_SET_COLOR(which, i);
+ cl_custom = (*ctmp);
+#endif
+
+ if(offer_normal_color_for_var(ps, var)){
+ new_confline(ctmp)->var = var;
+ (*ctmp)->varnamep = varnamep;
+ (*ctmp)->keymenu = km;
+ (*ctmp)->help = h_config_usenormal_color;
+ (*ctmp)->help_title = title;
+ (*ctmp)->tool = color_setting_tool;
+ (*ctmp)->valoffset = indent;
+ /* 5 is length of "( ) " */
+ (*ctmp)->val2offset = indent + lv + 5 + SPACE_BETWEEN_DOUBLEVARS;
+ (*ctmp)->flags |= CF_DOUBLEVAR;
+ (*ctmp)->varmem = CFC_SET_COLOR(which, CFC_ICOLOR_NORM);
+ if(fg_is_custom && fg && !struncmp(fg, MATCH_NORM_COLOR, RGBLEN))
+ fg_is_custom = 0;
+
+ if(bg_is_custom && bg && !struncmp(bg, MATCH_NORM_COLOR, RGBLEN))
+ bg_is_custom = 0;
+
+ (*ctmp)->value = new_color_line(COLOR_BLOB_NORM,
+ fg && !struncmp(fg, MATCH_NORM_COLOR, RGBLEN),
+ bg && !struncmp(bg, MATCH_NORM_COLOR, RGBLEN),
+ lv);
+ }
+
+ if(offer_none_color_for_var(ps, var)){
+ new_confline(ctmp)->var = var;
+ (*ctmp)->varnamep = varnamep;
+ (*ctmp)->keymenu = km;
+ (*ctmp)->help = h_config_usenone_color;
+ (*ctmp)->help_title = title;
+ (*ctmp)->tool = color_setting_tool;
+ (*ctmp)->valoffset = indent;
+ /* 5 is length of "( ) " */
+ (*ctmp)->val2offset = indent + lv + 5 + SPACE_BETWEEN_DOUBLEVARS;
+ (*ctmp)->flags |= CF_DOUBLEVAR;
+ (*ctmp)->varmem = CFC_SET_COLOR(which, CFC_ICOLOR_NONE);
+ if(fg_is_custom && fg && !struncmp(fg, MATCH_NONE_COLOR, RGBLEN))
+ fg_is_custom = 0;
+
+ if(bg_is_custom && bg && !struncmp(bg, MATCH_NONE_COLOR, RGBLEN))
+ bg_is_custom = 0;
+
+ (*ctmp)->value = new_color_line(COLOR_BLOB_NONE,
+ fg && !struncmp(fg, MATCH_NONE_COLOR, RGBLEN),
+ bg && !struncmp(bg, MATCH_NONE_COLOR, RGBLEN),
+ lv);
+ }
+
+#ifdef _WINDOWS
+ if(cl_custom)
+ cl_custom->value = new_color_line("Custom", fg_is_custom, bg_is_custom, lv);
+#endif
+
+ new_confline(ctmp)->var = var;
+ (*ctmp)->varnamep = varnamep;
+ (*ctmp)->keymenu = cb_km;
+ (*ctmp)->help = h_config_dflt_color;
+ (*ctmp)->help_title = title;
+ (*ctmp)->tool = color_setting_tool;
+ (*ctmp)->valoffset = indent;
+ (*ctmp)->varmem = CFC_SET_COLOR(which, 0);
+#ifdef _WINDOWS
+ snprintf(tmp, sizeof(tmp), "[%c] %s", def ? 'X' : ' ', "Default");
+ tmp[sizeof(tmp)-1] = '\0';
+#else
+ pvalfg = PVAL(var,Main);
+ pvalbg = PVAL(var+1,Main);
+ if(!var->is_list &&
+ ((var == &ps->vars[V_NORM_FORE_COLOR]) ||
+ (ew == Post && pvalfg && pvalfg[0] && pvalbg && pvalbg[0]) ||
+ (var->global_val.p && var->global_val.p[0] &&
+ (var+1)->global_val.p && (var+1)->global_val.p[0])))
+ snprintf(tmp, sizeof(tmp), "[%c] %s", def ? 'X' : ' ', "Default");
+ else if(var == &ps->vars[V_REV_FORE_COLOR])
+ snprintf(tmp, sizeof(tmp), "[%c] %s", def ? 'X' : ' ',
+ "Default (terminal's standout mode, usually reverse video)");
+ else if(var == &ps->vars[V_SLCTBL_FORE_COLOR])
+ snprintf(tmp, sizeof(tmp), "[%c] %s", def ? 'X' : ' ',
+ "Default (Bold Normal Color)");
+ else if(var == &ps->vars[V_TITLECLOSED_FORE_COLOR])
+ snprintf(tmp, sizeof(tmp), "[%c] %s", def ? 'X' : ' ',
+ "Default (same as Title Color)");
+ else if(var_defaults_to_rev(var))
+ snprintf(tmp, sizeof(tmp), "[%c] %s", def ? 'X' : ' ',
+ "Default (same as Reverse Color)");
+ else if(km == &kw_color_changing_keymenu)
+ snprintf(tmp, sizeof(tmp), "[%c] %s", def ? 'X' : ' ',
+ "Default (same as Indexline Color)");
+ else
+ snprintf(tmp, sizeof(tmp), "[%c] %s", def ? 'X' : ' ',
+ "Default (same as Normal Color)");
+
+ tmp[sizeof(tmp)-1] = '\0';
+#endif
+ (*ctmp)->value = cpystr(tmp);
+
+ /*
+ * Add a checkbox to turn bold on or off for selectable-item color.
+ */
+ if(var == &ps->vars[V_SLCTBL_FORE_COLOR]){
+ char **lval;
+
+ new_confline(ctmp)->var = var;
+ (*ctmp)->varnamep = varnamep;
+ (*ctmp)->keymenu = &selectable_bold_checkbox_keymenu;
+ (*ctmp)->help = h_config_bold_slctbl;
+ (*ctmp)->help_title = title;
+ (*ctmp)->tool = color_setting_tool;
+ (*ctmp)->valoffset = indent;
+ (*ctmp)->varmem = feature_list_index(F_SLCTBL_ITEM_NOBOLD);
+
+ lval = LVAL(&ps->vars[V_FEATURE_LIST], ew);
+ /*
+ * We don't use checkbox_pretty_value here because we just want
+ * the word Bold instead of the name of the variable and because
+ * we are actually using the negative of the feature. That is,
+ * the feature is really NOBOLD and we are using Bold.
+ */
+ snprintf(tmp, sizeof(tmp), "[%c] %s",
+ test_feature(lval, feature_list_name(F_SLCTBL_ITEM_NOBOLD), 0)
+ ? ' ' : 'X', "Bold");
+ tmp[sizeof(tmp)-1] = '\0';
+
+ (*ctmp)->value = cpystr(tmp);
+ }
+}
+
+
+int
+is_rgb_color(char *color)
+{
+ int i, j;
+
+ for(i = 0; i < 3; i++){
+ if(i && *color++ != ',')
+ return(FALSE);
+
+ for(j = 0; j < 3; j++, color++)
+ if(!isdigit((unsigned char) *color))
+ return(FALSE);
+ }
+
+ return(TRUE);
+}
+
+
+char *
+new_color_line(char *color, int fg, int bg, int len)
+{
+ char tmp[256];
+
+ snprintf(tmp, sizeof(tmp), "(%c) %-*.*s%*s(%c) %-*.*s",
+ fg ? R_SELD : ' ', len, len, color, SPACE_BETWEEN_DOUBLEVARS, "",
+ bg ? R_SELD : ' ', len, len, color);
+ tmp[sizeof(tmp)-1] = '\0';
+ return(cpystr(tmp));
+}
+
+
+int
+color_text_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ int rv = 0, i;
+ struct variable v, *save_var;
+ SPEC_COLOR_S *hc, *hcolors;
+ char *starting_val, *val, tmp[100], ***alval, **apval;
+
+ if(cmd == MC_EXIT)
+ return(simple_exit_cmd(flags));
+
+ alval = ALVAL((*cl)->var, ew);
+ if(!alval || !*alval)
+ return(rv);
+
+ hcolors = spec_colors_from_varlist(*alval, 0);
+
+ for(hc = hcolors, i=0; hc; hc = hc->next, i++)
+ if(CFC_ICUST(*cl) == i)
+ break;
+
+ starting_val = (hc && hc->val) ? pattern_to_string(hc->val) : NULL;
+
+ memset(&v, 0, sizeof(v));
+ v.is_used = 1;
+ v.is_user = 1;
+ snprintf(tmp, sizeof(tmp), "\"%c%s Pattern\"",
+ islower((unsigned char)hc->spec[0])
+ ? toupper((unsigned char)hc->spec[0])
+ : hc->spec[0],
+ hc->spec[1] ? hc->spec + 1 : "");
+ tmp[sizeof(tmp)-1] = '\0';
+ v.name = tmp;
+ /* have to use right part of v so text_tool will work right */
+ apval = APVAL(&v, ew);
+ *apval = starting_val ? cpystr(starting_val) : NULL;
+ set_current_val(&v, FALSE, FALSE);
+
+ save_var = (*cl)->var;
+ (*cl)->var = &v;
+ rv = text_tool(ps, cmd, cl, flags);
+
+ if(rv == 1){
+ val = *apval;
+ *apval = NULL;
+ if(val)
+ removing_leading_and_trailing_white_space(val);
+
+ if(hc->val)
+ fs_give((void **)&hc->val);
+
+ hc->val = string_to_pattern(val);
+
+ (*cl)->var = save_var;
+
+ if((*alval)[CFC_ICUST(*cl)])
+ fs_give((void **)&(*alval)[CFC_ICUST(*cl)]);
+
+ (*alval)[CFC_ICUST(*cl)] = var_from_spec_color(hc);
+ set_current_color_vals(ps);
+ ps->mangled_screen = 1;
+ }
+ else
+ (*cl)->var = save_var;
+
+ if(hcolors)
+ free_spec_colors(&hcolors);
+ if(*apval)
+ fs_give((void **)apval);
+ if(v.current_val.p)
+ fs_give((void **)&v.current_val.p);
+ if(starting_val)
+ fs_give((void **)&starting_val);
+
+ return(rv);
+}
+
+
+/*
+ * Test whether or not a var is one of the vars which might have a
+ * color value stored in it.
+ *
+ * returns: 1 if it is a color var, 0 otherwise
+ */
+int
+color_holding_var(struct pine *ps, struct variable *var)
+{
+ if(treat_color_vars_as_text)
+ return(0);
+ else
+ return(var && var->name &&
+ (srchstr(var->name, "-foreground-color") ||
+ srchstr(var->name, "-background-color") ||
+ var == &ps->vars[V_VIEW_HDR_COLORS] ||
+ var == &ps->vars[V_KW_COLORS]));
+}
+
+
+/*
+ * test whether or not a var is one of the vars having to do with color
+ *
+ * returns: 1 if it is a color var, 0 otherwise
+ */
+int
+color_related_var(struct pine *ps, struct variable *var)
+{
+ return(
+#ifndef _WINDOWS
+ var == &ps->vars[V_COLOR_STYLE] ||
+#endif
+ var == &ps->vars[V_INDEX_COLOR_STYLE] ||
+ var == &ps->vars[V_TITLEBAR_COLOR_STYLE] ||
+ color_holding_var(ps, var));
+}
+
+
+int
+offer_normal_color_for_var(struct pine *ps, struct variable *var)
+{
+ return(color_holding_var(ps, var)
+ && var != &ps->vars[V_NORM_FORE_COLOR]
+ && var != &ps->vars[V_NORM_BACK_COLOR]
+ && var != &ps->vars[V_REV_FORE_COLOR]
+ && var != &ps->vars[V_REV_BACK_COLOR]
+ && var != &ps->vars[V_SLCTBL_FORE_COLOR]
+ && var != &ps->vars[V_SLCTBL_BACK_COLOR]);
+}
+
+
+int
+offer_none_color_for_var(struct pine *ps, struct variable *var)
+{
+ return(color_holding_var(ps, var)
+ && (!struncmp(var->name, "index-", 6)
+ || var == &ps->vars[V_KW_COLORS]));
+}
+
+
+int
+color_setting_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ int rv = 0, i, cancel = 0, deefault;
+ int curcolor, prevcolor, nextcolor, another;
+ CONF_S *ctmp, *first_line, *beg = NULL, *end = NULL,
+ *cur_beg, *cur_end, *prev_beg, *prev_end,
+ *next_beg, *next_end;
+ struct variable *v, *fgv, *bgv, *setv = NULL, *otherv;
+ SPEC_COLOR_S *hc = NULL, *new_hcolor;
+ SPEC_COLOR_S *hcolors = NULL;
+ KEYWORD_S *kw;
+ char *old_val, *confline = NULL;
+ char prompt[100], sval[MAXPATH+1];
+ char **lval, **apval, ***alval, **t;
+ char **apval1, **apval2;
+ HelpType help;
+ ESCKEY_S opts[3];
+#ifdef _WINDOWS
+ char *pval;
+#endif
+
+ sval[0] = '\0';
+
+ switch(cmd){
+ case MC_CHOICE : /* set a color */
+
+ if(((*cl)->flags & CF_VAR2 && fixed_var((*cl)->var+1, NULL, NULL)) ||
+ (!((*cl)->flags & CF_VAR2) && fixed_var((*cl)->var, NULL, NULL))){
+ if(((*cl)->var->post_user_val.p ||
+ ((*cl)->var+1)->post_user_val.p ||
+ (*cl)->var->main_user_val.p ||
+ ((*cl)->var+1)->main_user_val.p)
+ && want_to(_("Delete old unused personal option setting"),
+ 'y', 'n', NO_HELP, WT_FLUSH_IN) == 'y'){
+ delete_user_vals((*cl)->var);
+ delete_user_vals((*cl)->var+1);
+ q_status_message(SM_ORDER, 0, 3, _("Deleted"));
+ rv = 1;
+ }
+
+ return(rv);
+ }
+
+ fgv = (*cl)->var; /* foreground color */
+ bgv = (*cl)->var+1; /* background color */
+ v = ((*cl)->flags & CF_VAR2) ? bgv : fgv; /* var being changed */
+
+ apval = APVAL(v, ew);
+ old_val = apval ? *apval : NULL;
+
+ if(apval){
+ if(CFC_ICOLOR(*cl) < pico_count_in_color_table())
+ *apval = cpystr(colorx(CFC_ICOLOR(*cl)));
+ else if(CFC_ICOLOR(*cl) == CFC_ICOLOR_NORM)
+ *apval = cpystr(MATCH_NORM_COLOR);
+ else if(CFC_ICOLOR(*cl) == CFC_ICOLOR_NONE)
+ *apval = cpystr(MATCH_NONE_COLOR);
+ else if(old_val)
+ *apval = cpystr(is_rgb_color(old_val)
+ ? old_val : color_to_asciirgb(old_val));
+ else if(v->current_val.p)
+ *apval = cpystr(is_rgb_color(v->current_val.p)
+ ? v->current_val.p
+ : color_to_asciirgb(v->current_val.p));
+ else if(v == fgv)
+ *apval = cpystr(color_to_asciirgb(colorx(COL_BLACK)));
+ else
+ *apval = cpystr(color_to_asciirgb(colorx(COL_WHITE)));
+ }
+
+ if(old_val)
+ fs_give((void **)&old_val);
+
+ set_current_val(v, TRUE, FALSE);
+
+ /*
+ * If the user sets one of foreground/background and the
+ * other is not yet set, set the other.
+ */
+ if(PVAL(v, ew)){
+ if(v == fgv && !PVAL(bgv, ew)){
+ setv = bgv;
+ otherv = fgv;
+ }
+ else if(v == bgv && !PVAL(fgv, ew)){
+ setv = fgv;
+ otherv = bgv;
+ }
+ }
+
+ if(setv){
+ if((apval = APVAL(setv, ew)) != NULL){
+ if(setv->current_val.p)
+ *apval = cpystr(setv->current_val.p);
+ else if (setv == fgv && ps_global->VAR_NORM_FORE_COLOR)
+ *apval = cpystr(ps_global->VAR_NORM_FORE_COLOR);
+ else if (setv == bgv && ps_global->VAR_NORM_BACK_COLOR)
+ *apval = cpystr(ps_global->VAR_NORM_BACK_COLOR);
+ else if(!strucmp(color_to_canonical_name(otherv->current_val.p),
+ colorx(COL_WHITE)))
+ *apval = cpystr(colorx(COL_BLACK));
+ else
+ *apval = cpystr(colorx(COL_WHITE));
+ }
+
+ set_current_val(setv, TRUE, FALSE);
+ }
+
+ fix_side_effects(ps, v, 0);
+ set_current_color_vals(ps);
+
+ /*
+ * Turn on selected *'s for default selections, if any, and
+ * for ones we forced on.
+ */
+ color_update_selected(ps, *cl, PVAL(fgv, ew), PVAL(bgv, ew), TRUE);
+
+ ClearScreen();
+ rv = ps->mangled_screen = 1;
+ break;
+
+ case MC_CHOICEB : /* set a custom hdr color */
+ /*
+ * Find the SPEC_COLOR_S for header.
+ */
+ lval = LVAL((*cl)->var, ew);
+ hcolors = spec_colors_from_varlist(lval, 0);
+ for(hc = hcolors, i=0; hc; hc = hc->next, i++)
+ if(CFC_ICUST(*cl) == i)
+ break;
+
+ if(hc){
+ if((*cl)->flags & CF_VAR2){
+ old_val = hc->bg;
+ if(CFC_ICOLOR(*cl) < pico_count_in_color_table())
+ hc->bg = cpystr(colorx(CFC_ICOLOR(*cl)));
+ else if(CFC_ICOLOR(*cl) == CFC_ICOLOR_NORM)
+ hc->bg = cpystr(MATCH_NORM_COLOR);
+ else if(CFC_ICOLOR(*cl) == CFC_ICOLOR_NONE)
+ hc->bg = cpystr(MATCH_NONE_COLOR);
+ else if(old_val)
+ hc->bg = cpystr(is_rgb_color(old_val)
+ ? old_val
+ : color_to_asciirgb(old_val));
+ else
+ hc->bg = cpystr(color_to_asciirgb(colorx(COL_WHITE)));
+
+ if(old_val)
+ fs_give((void **) &old_val);
+
+ /*
+ * If the user sets one of foreground/background and the
+ * other is not yet set, set it.
+ */
+ if(!(hc->fg && hc->fg[0])){
+ if(hc->fg)
+ fs_give((void **)&hc->fg);
+
+ hc->fg = cpystr(ps->VAR_NORM_FORE_COLOR);
+ }
+ }
+ else{
+ old_val = hc->fg;
+
+ if(CFC_ICOLOR(*cl) < pico_count_in_color_table())
+ hc->fg = cpystr(colorx(CFC_ICOLOR(*cl)));
+ else if(CFC_ICOLOR(*cl) == CFC_ICOLOR_NORM)
+ hc->fg = cpystr(MATCH_NORM_COLOR);
+ else if(CFC_ICOLOR(*cl) == CFC_ICOLOR_NONE)
+ hc->fg = cpystr(MATCH_NONE_COLOR);
+ else if(old_val)
+ hc->fg = cpystr(is_rgb_color(old_val)
+ ? old_val
+ : color_to_asciirgb(old_val));
+ else
+ hc->fg = cpystr(color_to_asciirgb(colorx(COL_BLACK)));
+
+ if(old_val)
+ fs_give((void **) &old_val);
+
+ if(!(hc->bg && hc->bg[0])){
+ if(hc->bg)
+ fs_give((void **)&hc->bg);
+
+ hc->bg = cpystr(ps->VAR_NORM_BACK_COLOR);
+ }
+ }
+ }
+
+ /*
+ * Turn on selected *'s for default selections, if any, and
+ * for ones we forced on.
+ */
+ color_update_selected(ps, *cl,
+ (hc && hc->fg && hc->fg[0]
+ && hc->bg && hc->bg[0])
+ ? hc->fg : ps->VAR_NORM_FORE_COLOR,
+ (hc && hc->fg && hc->fg[0]
+ && hc->bg && hc->bg[0])
+ ? hc->bg : ps->VAR_NORM_BACK_COLOR,
+ TRUE);
+
+ if(hc && lval && lval[i]){
+ fs_give((void **)&lval[i]);
+ lval[i] = var_from_spec_color(hc);
+ }
+
+ if(hcolors)
+ free_spec_colors(&hcolors);
+
+ set_current_color_vals(ps);
+ ClearScreen();
+ rv = ps->mangled_screen = 1;
+ break;
+
+
+ case MC_CHOICEC : /* set a custom keyword color */
+ /* find keyword associated with color we are editing */
+ for(kw=ps->keywords, i=0; kw; kw=kw->next, i++)
+ if(CFC_ICUST(*cl) == i)
+ break;
+
+ if(!kw){ /* can't happen */
+ dprint((1,
+ "This can't happen, kw not set when setting keyword color\n"));
+ break;
+ }
+
+ hcolors = spec_colors_from_varlist(LVAL((*cl)->var, ew), 0);
+
+ /*
+ * Look through hcolors, derived from lval, to find this keyword
+ * and its current color.
+ */
+ for(hc = hcolors; hc; hc = hc->next)
+ if(hc->spec && ((kw->nick && !strucmp(kw->nick, hc->spec))
+ || (kw->kw && !strucmp(kw->kw, hc->spec))))
+ break;
+
+ if(!hc){ /* this keyword didn't have a color set, add to list */
+ SPEC_COLOR_S *new;
+
+ new = (SPEC_COLOR_S *) fs_get(sizeof(*hc));
+ memset((void *) new, 0, sizeof(*new));
+ new->spec = cpystr(kw->kw);
+ new->fg = cpystr(ps->VAR_NORM_FORE_COLOR);
+ new->bg = cpystr(ps->VAR_NORM_BACK_COLOR);
+
+ if(hcolors){
+ for(hc = hcolors; hc->next; hc = hc->next)
+ ;
+
+ hc->next = new;
+ }
+ else
+ hcolors = new;
+
+ hc = new;
+ }
+
+ if(hc){
+ if((*cl)->flags & CF_VAR2){
+ old_val = hc->bg;
+ if(CFC_ICOLOR(*cl) < pico_count_in_color_table())
+ hc->bg = cpystr(colorx(CFC_ICOLOR(*cl)));
+ else if(CFC_ICOLOR(*cl) == CFC_ICOLOR_NORM)
+ hc->bg = cpystr(MATCH_NORM_COLOR);
+ else if(CFC_ICOLOR(*cl) == CFC_ICOLOR_NONE)
+ hc->bg = cpystr(MATCH_NONE_COLOR);
+ else if(old_val)
+ hc->bg = cpystr(is_rgb_color(old_val)
+ ? old_val
+ : color_to_asciirgb(old_val));
+ else
+ hc->bg = cpystr(color_to_asciirgb(colorx(COL_WHITE)));
+
+ if(old_val)
+ fs_give((void **) &old_val);
+
+ /*
+ * If the user sets one of foreground/background and the
+ * other is not yet set, set it.
+ */
+ if(!(hc->fg && hc->fg[0])){
+ if(hc->fg)
+ fs_give((void **)&hc->fg);
+
+ hc->fg = cpystr(ps->VAR_NORM_FORE_COLOR);
+ }
+ }
+ else{
+ old_val = hc->fg;
+
+ if(CFC_ICOLOR(*cl) < pico_count_in_color_table())
+ hc->fg = cpystr(colorx(CFC_ICOLOR(*cl)));
+ else if(CFC_ICOLOR(*cl) == CFC_ICOLOR_NORM)
+ hc->fg = cpystr(MATCH_NORM_COLOR);
+ else if(CFC_ICOLOR(*cl) == CFC_ICOLOR_NONE)
+ hc->fg = cpystr(MATCH_NONE_COLOR);
+ else if(old_val)
+ hc->fg = cpystr(is_rgb_color(old_val)
+ ? old_val
+ : color_to_asciirgb(old_val));
+ else
+ hc->fg = cpystr(color_to_asciirgb(colorx(COL_BLACK)));
+
+ if(old_val)
+ fs_give((void **) &old_val);
+
+ if(!(hc->bg && hc->bg[0])){
+ if(hc->bg)
+ fs_give((void **)&hc->bg);
+
+ hc->bg = cpystr(ps->VAR_NORM_BACK_COLOR);
+ }
+ }
+ }
+
+ /*
+ * Turn on selected *'s for default selections, if any, and
+ * for ones we forced on.
+ */
+ color_update_selected(ps, *cl,
+ (hc && hc->fg && hc->fg[0]
+ && hc->bg && hc->bg[0])
+ ? hc->fg : ps->VAR_NORM_FORE_COLOR,
+ (hc && hc->fg && hc->fg[0]
+ && hc->bg && hc->bg[0])
+ ? hc->bg : ps->VAR_NORM_BACK_COLOR,
+ TRUE);
+
+ alval = ALVAL((*cl)->var, ew);
+ free_list_array(alval);
+ *alval = varlist_from_spec_colors(hcolors);
+
+ if(hcolors)
+ free_spec_colors(&hcolors);
+
+ fix_side_effects(ps, (*cl)->var, 0);
+ set_current_color_vals(ps);
+ ClearScreen();
+ rv = ps->mangled_screen = 1;
+ break;
+
+ case MC_TOGGLE : /* toggle default on or off */
+ fgv = (*cl)->var; /* foreground color */
+ bgv = (*cl)->var+1; /* background color */
+
+ if((*cl)->value[1] == 'X'){ /* turning default off */
+ (*cl)->value[1] = ' ';
+ /*
+ * Take whatever color is the current_val and suck it
+ * into the user_val. Same colors remain checked.
+ */
+ apval1 = APVAL(fgv, ew);
+ if(apval1){
+ if(*apval1)
+ fs_give((void **)apval1);
+ }
+
+ apval2 = APVAL(bgv, ew);
+ if(apval2){
+ if(*apval2)
+ fs_give((void **)apval2);
+ }
+
+ /* editing normal but there is an exception config */
+ if((ps->ew_for_except_vars != Main) && (ew == Main)){
+ COLOR_PAIR *newc;
+
+ /* use global_val if it is set */
+ if(fgv && fgv->global_val.p && fgv->global_val.p[0] &&
+ bgv && bgv->global_val.p && bgv->global_val.p[0]){
+ *apval1 = cpystr(fgv->global_val.p);
+ *apval2 = cpystr(bgv->global_val.p);
+ }
+ else if(var_defaults_to_rev(fgv) &&
+ (newc = pico_get_rev_color())){
+ *apval1 = cpystr(newc->fg);
+ *apval2 = cpystr(newc->bg);
+ }
+ else{
+ *apval1 = cpystr(ps->VAR_NORM_FORE_COLOR);
+ *apval2 = cpystr(ps->VAR_NORM_BACK_COLOR);
+ }
+ }
+ else{ /* editing outermost config */
+ /* just use current_vals */
+ if(fgv->current_val.p)
+ *apval1 = cpystr(fgv->current_val.p);
+ if(bgv->current_val.p)
+ *apval2 = cpystr(bgv->current_val.p);
+ }
+ }
+ else{ /* turning default on */
+ char *starred_fg = NULL, *starred_bg = NULL;
+
+ (*cl)->value[1] = 'X';
+ apval = APVAL(fgv, ew);
+ if(apval && *apval)
+ fs_give((void **)apval);
+
+ apval = APVAL(bgv, ew);
+ if(apval && *apval)
+ fs_give((void **)apval);
+
+ if(fgv->cmdline_val.p)
+ fs_give((void **)&fgv->cmdline_val.p);
+
+ if(bgv->cmdline_val.p)
+ fs_give((void **)&bgv->cmdline_val.p);
+
+ set_current_color_vals(ps);
+
+ if(fgv == &ps->vars[V_SLCTBL_FORE_COLOR]){
+ F_TURN_OFF(F_SLCTBL_ITEM_NOBOLD, ps);
+ (*cl)->next->value[1] = 'X';
+ }
+
+ /* editing normal but there is an exception config */
+ if((ps->ew_for_except_vars != Main) && (ew == Main)){
+ COLOR_PAIR *newc;
+
+ /* use global_val if it is set */
+ if(fgv && fgv->global_val.p && fgv->global_val.p[0] &&
+ bgv && bgv->global_val.p && bgv->global_val.p[0]){
+ starred_fg = fgv->global_val.p;
+ starred_bg = bgv->global_val.p;
+ }
+ else if(var_defaults_to_rev(fgv) &&
+ (newc = pico_get_rev_color())){
+ starred_fg = newc->fg;
+ starred_bg = newc->bg;
+ }
+ else{
+ starred_fg = ps->VAR_NORM_FORE_COLOR;
+ starred_bg = ps->VAR_NORM_BACK_COLOR;
+ }
+ }
+ else{ /* editing outermost config */
+ starred_fg = fgv->current_val.p;
+ starred_bg = bgv->current_val.p;
+ }
+
+ /*
+ * Turn on selected *'s for default selections.
+ */
+ color_update_selected(ps, prev_confline(*cl),
+ starred_fg, starred_bg, FALSE);
+
+ ps->mangled_body = 1;
+ }
+
+ fix_side_effects(ps, fgv, 0);
+ rv = 1;
+ break;
+
+ case MC_TOGGLEB : /* toggle default on or off, hdr color */
+ /*
+ * Find the SPEC_COLOR_S for header.
+ */
+ rv = 1;
+ lval = LVAL((*cl)->var, ew);
+ hcolors = spec_colors_from_varlist(lval, 0);
+ for(hc = hcolors, i=0; hc; hc = hc->next, i++)
+ if(CFC_ICUST(*cl) == i)
+ break;
+
+ if((*cl)->value[1] == 'X'){ /* turning default off */
+ (*cl)->value[1] = ' ';
+ /*
+ * Take whatever color is the default value and suck it
+ * into the hc structure.
+ */
+ if(hc){
+ if(hc->bg)
+ fs_give((void **)&hc->bg);
+ if(hc->fg)
+ fs_give((void **)&hc->fg);
+
+ if(ps->VAR_NORM_FORE_COLOR &&
+ ps->VAR_NORM_FORE_COLOR[0] &&
+ ps->VAR_NORM_BACK_COLOR &&
+ ps->VAR_NORM_BACK_COLOR[0]){
+ hc->fg = cpystr(ps->VAR_NORM_FORE_COLOR);
+ hc->bg = cpystr(ps->VAR_NORM_BACK_COLOR);
+ }
+
+ if(lval && lval[i]){
+ fs_give((void **)&lval[i]);
+ lval[i] = var_from_spec_color(hc);
+ }
+ }
+ }
+ else{ /* turning default on */
+ (*cl)->value[1] = 'X';
+ /* Remove current colors, leaving val */
+ if(hc){
+ if(hc->bg)
+ fs_give((void **)&hc->bg);
+ if(hc->fg)
+ fs_give((void **)&hc->fg);
+
+ if(lval && lval[i]){
+ fs_give((void **)&lval[i]);
+ lval[i] = var_from_spec_color(hc);
+ }
+ }
+
+ set_current_color_vals(ps);
+ ClearScreen();
+ ps->mangled_screen = 1;
+
+ }
+
+ if(hcolors)
+ free_spec_colors(&hcolors);
+
+ /*
+ * Turn on selected *'s for default selections.
+ */
+ color_update_selected(ps, prev_confline(*cl),
+ ps->VAR_NORM_FORE_COLOR,
+ ps->VAR_NORM_BACK_COLOR,
+ FALSE);
+
+ break;
+
+ case MC_TOGGLEC : /* toggle default on or off, keyword color */
+ for(kw=ps->keywords, i=0; kw; kw=kw->next, i++)
+ if(CFC_ICUST(*cl) == i)
+ break;
+
+ if(!kw){ /* can't happen */
+ dprint((1,
+ "This can't happen, kw not set when togglec keyword color\n"));
+ break;
+ }
+
+ hcolors = spec_colors_from_varlist(LVAL((*cl)->var, ew), 0);
+
+ /*
+ * Look through hcolors, derived from lval, to find this keyword
+ * and its current color.
+ */
+ for(hc = hcolors; hc; hc = hc->next)
+ if(hc->spec && ((kw->nick && !strucmp(kw->nick, hc->spec))
+ || (kw->kw && !strucmp(kw->kw, hc->spec))))
+ break;
+
+ /* Remove this color from list */
+ if(hc){
+ SPEC_COLOR_S *tmp;
+
+ if(hc == hcolors){
+ hcolors = hc->next;
+ hc->next = NULL;
+ free_spec_colors(&hc);
+ }
+ else{
+ for(tmp = hcolors; tmp->next; tmp = tmp->next)
+ if(tmp->next == hc)
+ break;
+
+ if(tmp->next){
+ tmp->next = hc->next;
+ hc->next = NULL;
+ free_spec_colors(&hc);
+ }
+ }
+ }
+
+ if((*cl)->value[1] == 'X')
+ (*cl)->value[1] = ' ';
+ else
+ (*cl)->value[1] = 'X';
+
+ /*
+ * Turn on selected *'s for default selections, if any, and
+ * for ones we forced on.
+ */
+ color_update_selected(ps, prev_confline(*cl),
+ ps->VAR_NORM_FORE_COLOR,
+ ps->VAR_NORM_BACK_COLOR,
+ FALSE);
+
+ alval = ALVAL((*cl)->var, ew);
+ free_list_array(alval);
+ *alval = varlist_from_spec_colors(hcolors);
+
+ if(hcolors)
+ free_spec_colors(&hcolors);
+
+ fix_side_effects(ps, (*cl)->var, 0);
+ set_current_color_vals(ps);
+ ClearScreen();
+ rv = ps->mangled_screen = 1;
+ break;
+
+ case MC_TOGGLED : /* toggle selectable item bold on or off */
+ toggle_feature_bit(ps, feature_list_index(F_SLCTBL_ITEM_NOBOLD),
+ &ps->vars[V_FEATURE_LIST], *cl, 1);
+ ps->mangled_body = 1; /* to fix Sample Text */
+ rv = 1;
+ break;
+
+ case MC_DEFAULT : /* restore default values */
+
+ /* First, confirm that user wants to restore all default colors */
+ if(want_to(_("Really restore all colors to default values"),
+ 'y', 'n', NO_HELP, WT_NORM) != 'y'){
+ cmd_cancelled("RestoreDefs");
+ return(rv);
+ }
+
+ /* get rid of all user set colors */
+ for(v = ps->vars; v->name; v++){
+ if(!color_holding_var(ps, v)
+ || v == &ps->vars[V_VIEW_HDR_COLORS]
+ || v == &ps->vars[V_KW_COLORS])
+ continue;
+
+ apval = APVAL(v, ew);
+ if(apval && *apval)
+ fs_give((void **)apval);
+
+ if(v->cmdline_val.p)
+ fs_give((void **)&v->cmdline_val.p);
+ }
+
+ /*
+ * For custom header colors, we want to remove the color values
+ * but leave the spec value so that it is easy to reset.
+ */
+ alval = ALVAL(&ps->vars[V_VIEW_HDR_COLORS], ew);
+ if(alval && *alval){
+ SPEC_COLOR_S *global_hcolors = NULL, *hcg;
+
+ v = &ps->vars[V_VIEW_HDR_COLORS];
+ if(v->global_val.l && v->global_val.l[0])
+ global_hcolors = spec_colors_from_varlist(v->global_val.l, 0);
+
+ hcolors = spec_colors_from_varlist(*alval, 0);
+ for(hc = hcolors; hc; hc = hc->next){
+ if(hc->fg)
+ fs_give((void **)&hc->fg);
+ if(hc->bg)
+ fs_give((void **)&hc->bg);
+
+ for(hcg = global_hcolors; hcg; hcg = hcg->next){
+ if(hc->spec && hcg->spec && !strucmp(hc->spec, hcg->spec)){
+ hc->fg = hcg->fg;
+ hcg->fg = NULL;
+ hc->bg = hcg->bg;
+ hcg->bg = NULL;
+ if(hc->val && !hcg->val)
+ fs_give((void **) &hc->val);
+ }
+ }
+
+ if(global_hcolors)
+ free_spec_colors(&global_hcolors);
+ }
+
+ free_list_array(alval);
+ *alval = varlist_from_spec_colors(hcolors);
+
+ if(hcolors)
+ free_spec_colors(&hcolors);
+ }
+
+ /*
+ * Same for keyword colors.
+ */
+ alval = ALVAL(&ps->vars[V_KW_COLORS], ew);
+ if(alval && *alval){
+ hcolors = spec_colors_from_varlist(*alval, 0);
+ for(hc = hcolors; hc; hc = hc->next){
+ if(hc->fg)
+ fs_give((void **)&hc->fg);
+ if(hc->bg)
+ fs_give((void **)&hc->bg);
+ }
+
+ free_list_array(alval);
+ *alval = varlist_from_spec_colors(hcolors);
+
+ if(hcolors)
+ free_spec_colors(&hcolors);
+ }
+
+ /* set bold for selectable items */
+ F_TURN_OFF(F_SLCTBL_ITEM_NOBOLD, ps);
+ lval = LVAL(&ps->vars[V_FEATURE_LIST], ew);
+ if(test_feature(lval, feature_list_name(F_SLCTBL_ITEM_NOBOLD), 0))
+ toggle_feature_bit(ps, feature_list_index(F_SLCTBL_ITEM_NOBOLD),
+ &ps->vars[V_FEATURE_LIST], *cl, 1);
+
+ set_current_color_vals(ps);
+ clear_index_cache(ps->mail_stream, 0);
+
+ /* redo config display */
+ *cl = first_confline(*cl);
+ free_conflines(cl);
+ opt_screen->top_line = NULL;
+ first_line = NULL;
+ color_config_init_display(ps, cl, &first_line);
+ *cl = first_line;
+ ClearScreen();
+ ps->mangled_screen = 1;
+ rv = 1;
+ break;
+
+ case MC_ADD : /* add custom header color */
+ /* get header field name */
+ help = NO_HELP;
+ while(1){
+ i = optionally_enter(sval, -FOOTER_ROWS(ps), 0, sizeof(sval),
+ _("Enter the name of the header field to be added: "),
+ NULL, help, NULL);
+ if(i == 0)
+ break;
+ else if(i == 1){
+ cmd_cancelled("Add");
+ cancel = 1;
+ break;
+ }
+ else if(i == 3){
+ help = help == NO_HELP ? h_config_add_custom_color : NO_HELP;
+ continue;
+ }
+ else
+ break;
+ }
+
+ ps->mangled_footer = 1;
+
+ removing_leading_and_trailing_white_space(sval);
+ if(sval[strlen(sval)-1] == ':') /* remove trailing colon */
+ sval[strlen(sval)-1] = '\0';
+
+ removing_trailing_white_space(sval);
+
+ if(cancel || !sval[0])
+ break;
+
+ new_hcolor = (SPEC_COLOR_S *)fs_get(sizeof(*new_hcolor));
+ memset((void *)new_hcolor, 0, sizeof(*new_hcolor));
+ new_hcolor->spec = cpystr(sval);
+ confline = var_from_spec_color(new_hcolor);
+
+ /* add it to end of list */
+ alval = ALVAL(&ps->vars[V_VIEW_HDR_COLORS], ew);
+ if(alval){
+ /* get rid of possible empty value first */
+ if((t = *alval) && t[0] && !t[0][0] && !(t+1)[0])
+ free_list_array(alval);
+
+ for(t = *alval, i=0; t && t[0]; t++)
+ i++;
+
+ if(i)
+ fs_resize((void **)alval, sizeof(char *) * (i+1+1));
+ else
+ *alval = (char **)fs_get(sizeof(char *) * (i+1+1));
+
+ (*alval)[i] = confline;
+ (*alval)[i+1] = NULL;
+ }
+
+ set_current_color_vals(ps);
+
+ /* go to end of display */
+ for(ctmp = *cl; ctmp && ctmp->next; ctmp = next_confline(ctmp))
+ ;
+
+ /* back up to the KEYWORD COLORS title line */
+ for(; ctmp && (!ctmp->value || strcmp(ctmp->value, KW_COLORS_HDR))
+ && ctmp->prev;
+ ctmp = prev_confline(ctmp))
+ ;
+
+ /*
+ * Back up to last header line, or comment line if no header lines.
+ * One of many in a long line of dangerous moves in the config
+ * screens.
+ */
+ ctmp = prev_confline(ctmp); /* ------- line */
+ ctmp = prev_confline(ctmp); /* blank line */
+ ctmp = prev_confline(ctmp); /* the line */
+
+ *cl = ctmp;
+
+ /* delete the comment line if there were no headers before this */
+ if(i == 0){
+ beg = ctmp->prev;
+ end = ctmp;
+
+ *cl = beg ? beg->prev : NULL;
+
+ if(beg && beg->prev) /* this will always be true */
+ beg->prev->next = end ? end->next : NULL;
+
+ if(end && end->next)
+ end->next->prev = beg ? beg->prev : NULL;
+
+ if(end)
+ end->next = NULL;
+
+ if(beg == opt_screen->top_line || end == opt_screen->top_line)
+ opt_screen->top_line = NULL;
+
+ free_conflines(&beg);
+ }
+
+ add_header_color_line(ps, cl, confline, i);
+
+ /* be sure current is on selectable line */
+ for(; *cl && ((*cl)->flags & CF_NOSELECT); *cl = next_confline(*cl))
+ ;
+ for(; *cl && ((*cl)->flags & CF_NOSELECT); *cl = prev_confline(*cl))
+ ;
+
+ rv = ps->mangled_body = 1;
+ break;
+
+ case MC_DELETE : /* delete custom header color */
+ if((*cl)->var != &ps->vars[V_VIEW_HDR_COLORS]){
+ q_status_message(SM_ORDER, 0, 2,
+ _("Can't delete this color setting"));
+ break;
+ }
+
+ if(want_to(_("Really delete header color from config"),
+ 'y', 'n', NO_HELP, WT_NORM) != 'y'){
+ cmd_cancelled("Delete");
+ return(rv);
+ }
+
+ alval = ALVAL((*cl)->var, ew);
+ if(alval){
+ int n, j;
+
+ for(t = *alval, n=0; t && t[0]; t++)
+ n++;
+
+ j = CFC_ICUST(*cl);
+
+ if(n > j){ /* it better be */
+ if((*alval)[j])
+ fs_give((void **)&(*alval)[j]);
+
+ for(i = j; i < n; i++)
+ (*alval)[i] = (*alval)[i+1];
+ }
+ }
+
+ set_current_color_vals(ps);
+
+ /*
+ * Note the conf lines that go with this header. That's the
+ * blank line before and the current line.
+ */
+ beg = (*cl)->prev;
+ end = *cl;
+
+ another = 0;
+ /* reset current line */
+ if(end && end->next && end->next->next &&
+ end->next->next->var == &ps->vars[V_VIEW_HDR_COLORS]){
+ *cl = end->next->next; /* next Header Color */
+ another++;
+ }
+ else if(beg && beg->prev &&
+ beg->prev->var == &ps->vars[V_VIEW_HDR_COLORS]){
+ *cl = beg->prev; /* prev Header Color */
+ another++;
+ }
+
+ /* adjust SPEC_COLOR_S index (varmem) values */
+ for(ctmp = end; ctmp; ctmp = next_confline(ctmp))
+ if(ctmp->var == &ps->vars[V_VIEW_HDR_COLORS])
+ ctmp->varmem = CFC_ICUST_DEC(ctmp);
+
+ /*
+ * If that was the last header color line, add in the comment
+ * line placeholder. If there is another, just delete the
+ * old conf lines.
+ */
+ if(another){
+ if(beg && beg->prev) /* this will always be true */
+ beg->prev->next = end ? end->next : NULL;
+
+ if(end && end->next)
+ end->next->prev = beg ? beg->prev : NULL;
+
+ if(end)
+ end->next = NULL;
+
+ if(beg == opt_screen->top_line || end == opt_screen->top_line)
+ opt_screen->top_line = NULL;
+
+ free_conflines(&beg);
+ }
+ else if(end){
+ if(end->varname)
+ fs_give((void **) &end->varname);
+
+ if(end->value)
+ fs_give((void **) &end->value);
+
+ end->flags = CF_NOSELECT;
+ end->help = NO_HELP;
+ end->value = cpystr(_(ADDHEADER_COMMENT));
+ end->valoffset = COLOR_INDENT;
+ end->varnamep = NULL;
+ end->varmem = 0;
+ end->keymenu = NULL;
+ end->tool = NULL;
+ }
+
+ /* if not selectable, find next selectable line */
+ for(; *cl && ((*cl)->flags & CF_NOSELECT) && next_confline(*cl); *cl = next_confline(*cl))
+ ;
+ /* if no next selectable line, search backwards for one */
+ for(; *cl && ((*cl)->flags & CF_NOSELECT) && prev_confline(*cl); *cl = prev_confline(*cl))
+ ;
+
+ rv = ps->mangled_body = 1;
+ q_status_message(SM_ORDER, 0, 3, _("header color deleted"));
+ break;
+
+ case MC_SHUFFLE : /* shuffle order of custom headers */
+ if((*cl)->var != &ps->vars[V_VIEW_HDR_COLORS]){
+ q_status_message(SM_ORDER, 0, 2,
+ _("Can't shuffle this color setting"));
+ break;
+ }
+
+ alval = ALVAL((*cl)->var, ew);
+ if(!alval)
+ return(rv);
+
+ curcolor = CFC_ICUST(*cl);
+ prevcolor = curcolor-1;
+ nextcolor = curcolor+1;
+ if(!*alval || !(*alval)[nextcolor])
+ nextcolor = -1;
+
+ if((prevcolor < 0 && nextcolor < 0) || !*alval){
+ q_status_message(SM_ORDER, 0, 3,
+ _("Shuffle only makes sense when there is more than one Header Color defined"));
+ return(rv);
+ }
+
+ /* Move it up or down? */
+ i = 0;
+ opts[i].ch = 'u';
+ opts[i].rval = 'u';
+ opts[i].name = "U";
+ opts[i++].label = _("Up");
+
+ opts[i].ch = 'd';
+ opts[i].rval = 'd';
+ opts[i].name = "D";
+ opts[i++].label = _("Down");
+
+ opts[i].ch = -1;
+ deefault = 'u';
+
+ if(prevcolor < 0){ /* no up */
+ opts[0].ch = -2;
+ deefault = 'd';
+ }
+ else if(nextcolor < 0)
+ opts[1].ch = -2; /* no down */
+
+ snprintf(prompt, sizeof(prompt), _("Shuffle %s%s%s ? "),
+ (opts[0].ch != -2) ? _("UP") : "",
+ (opts[0].ch != -2 && opts[1].ch != -2) ? " or " : "",
+ (opts[1].ch != -2) ? _("DOWN") : "");
+ prompt[sizeof(prompt)-1] = '\0';
+ help = (opts[0].ch == -2) ? h_hdrcolor_shuf_down
+ : (opts[1].ch == -2) ? h_hdrcolor_shuf_up
+ : h_hdrcolor_shuf;
+
+ i = radio_buttons(prompt, -FOOTER_ROWS(ps), opts, deefault, 'x',
+ help, RB_NORM);
+
+ switch(i){
+ case 'x':
+ cmd_cancelled("Shuffle");
+ return(rv);
+
+ case 'u':
+ case 'd':
+ break;
+ }
+
+ /* swap order */
+ if(i == 'd'){
+ old_val = (*alval)[curcolor];
+ (*alval)[curcolor] = (*alval)[nextcolor];
+ (*alval)[nextcolor] = old_val;
+ }
+ else if(i == 'u'){
+ old_val = (*alval)[curcolor];
+ (*alval)[curcolor] = (*alval)[prevcolor];
+ (*alval)[prevcolor] = old_val;
+ }
+ else /* can't happen */
+ return(rv);
+
+ set_current_color_vals(ps);
+
+ /*
+ * Swap the conf lines.
+ */
+
+ cur_beg = (*cl)->prev;
+ cur_end = *cl;
+
+ if(i == 'd'){
+ next_beg = cur_end->next;
+ next_end = next_beg ? next_beg->next : NULL;
+
+ if(next_end->next)
+ next_end->next->prev = cur_end;
+ cur_end->next = next_end->next;
+ next_end->next = cur_beg;
+ if(cur_beg->prev)
+ cur_beg->prev->next = next_beg;
+ next_beg->prev = cur_beg->prev;
+ cur_beg->prev = next_end;
+
+ /* adjust SPEC_COLOR_S index values */
+ cur_beg->varmem = CFC_ICUST_INC(cur_beg);
+ cur_beg->next->varmem = CFC_ICUST_INC(cur_beg->next);
+
+ next_beg->varmem = CFC_ICUST_DEC(next_beg);
+ next_beg->next->varmem = CFC_ICUST_DEC(next_beg->next);
+
+ if(opt_screen->top_line == cur_end)
+ opt_screen->top_line = next_end;
+ else if(opt_screen->top_line == cur_beg)
+ opt_screen->top_line = next_beg;
+ }
+ else{
+ prev_end = cur_beg->prev;
+ prev_beg = prev_end ? prev_end->prev : NULL;
+
+ if(prev_beg && prev_beg->prev)
+ prev_beg->prev->next = cur_beg;
+ cur_beg->prev = prev_beg->prev;
+ prev_beg->prev = cur_end;
+ if(cur_end->next)
+ cur_end->next->prev = prev_end;
+ prev_end->next = cur_end->next;
+ cur_end->next = prev_beg;
+
+ /* adjust SPEC_COLOR_S index values */
+ cur_beg->varmem = CFC_ICUST_DEC(cur_beg);
+ cur_beg->next->varmem = CFC_ICUST_DEC(cur_beg->next);
+
+ prev_beg->varmem = CFC_ICUST_INC(prev_beg);
+ prev_beg->next->varmem = CFC_ICUST_INC(prev_beg->next);
+
+ if(opt_screen->top_line == prev_end)
+ opt_screen->top_line = cur_end;
+ else if(opt_screen->top_line == prev_beg)
+ opt_screen->top_line = cur_beg;
+ }
+
+ rv = ps->mangled_body = 1;
+ q_status_message(SM_ORDER, 0, 3, _("Header Colors shuffled"));
+ break;
+
+ case MC_EDIT:
+ rv = color_edit_screen(ps, cl);
+ if((*cl)->value && (*cl)->var &&
+ srchstr((*cl)->var->name, "-foreground-color")){
+ fs_give((void **)&(*cl)->value);
+ (*cl)->value = pretty_value(ps, *cl);
+ }
+
+ break;
+
+ case MC_EXIT: /* exit */
+ if((*cl)->keymenu == &color_changing_keymenu ||
+ (*cl)->keymenu == &kw_color_changing_keymenu ||
+ (*cl)->keymenu == &custom_color_changing_keymenu ||
+ ((*cl)->prev &&
+ ((*cl)->prev->keymenu == &color_changing_keymenu ||
+ (*cl)->prev->keymenu == &kw_color_changing_keymenu ||
+ (*cl)->prev->keymenu == &custom_color_changing_keymenu)) ||
+ ((*cl)->prev->prev &&
+ ((*cl)->prev->prev->keymenu == &color_changing_keymenu ||
+ (*cl)->prev->prev->keymenu == &kw_color_changing_keymenu ||
+ (*cl)->prev->prev->keymenu == &custom_color_changing_keymenu)))
+ rv = simple_exit_cmd(flags);
+ else
+ rv = config_exit_cmd(flags);
+
+ break;
+
+#ifdef _WINDOWS
+ case MC_RGB1 :
+ fgv = (*cl)->var;
+ bgv = (*cl)->var+1;
+ v = (*cl)->var;
+ if((*cl)->flags & CF_VAR2)
+ v += 1;
+
+ pval = PVAL(v, ew);
+ apval = APVAL(v, ew);
+ if(old_val = mswin_rgbchoice(pval ? pval : v->current_val.p)){
+ if(*apval)
+ fs_give((void **)apval);
+
+ *apval = old_val;
+ set_current_val(v, TRUE, FALSE);
+ fix_side_effects(ps, v, 0);
+ set_current_color_vals(ps);
+ color_update_selected(ps, *cl, PVAL(fgv, ew), PVAL(bgv, ew), TRUE);
+ rv = ps->mangled_screen = 1;
+ }
+
+ break;
+
+ case MC_RGB2 :
+ /*
+ * Find the SPEC_COLOR_S for header.
+ */
+ alval = ALVAL((*cl)->var, ew);
+ hcolors = spec_colors_from_varlist(*alval, 0);
+
+ for(hc = hcolors, i = 0; hc; hc = hc->next, i++)
+ if(CFC_ICUST(*cl) == i){
+ char **pc = ((*cl)->flags & CF_VAR2) ? &hc->bg : &hc->fg;
+
+ if(old_val = mswin_rgbchoice(*pc)){
+ fs_give((void **) pc);
+ *pc = old_val;
+ color_update_selected(ps, *cl,
+ (hc->fg && hc->fg[0]
+ && hc->bg && hc->bg[0])
+ ? hc->fg : ps->VAR_NORM_FORE_COLOR,
+ (hc->fg && hc->fg[0]
+ && hc->bg && hc->bg[0])
+ ? hc->bg : ps->VAR_NORM_BACK_COLOR,
+ TRUE);
+
+ if(hc && *alval && (*alval)[i]){
+ fs_give((void **)&(*alval)[i]);
+ (*alval)[i] = var_from_spec_color(hc);
+ }
+
+ if(hcolors)
+ free_spec_colors(&hcolors);
+
+ set_current_color_vals(ps);
+ ClearScreen();
+ rv = ps->mangled_screen = 1;
+ }
+
+ break;
+ }
+
+ break;
+
+ case MC_RGB3 :
+ /*
+ * Custom colored keywords.
+ */
+ for(kw=ps->keywords, i=0; kw; kw=kw->next, i++)
+ if(CFC_ICUST(*cl) == i)
+ break;
+
+ if(!kw){ /* can't happen */
+ dprint((1,
+ "This can't happen, kw not set in MC_RGB3\n"));
+ break;
+ }
+
+ hcolors = spec_colors_from_varlist(LVAL((*cl)->var, ew), 0);
+
+ /*
+ * Look through hcolors, derived from lval, to find this keyword
+ * and its current color.
+ */
+ for(hc = hcolors; hc; hc = hc->next)
+ if(hc->spec && ((kw->nick && !strucmp(kw->nick, hc->spec))
+ || (kw->kw && !strucmp(kw->kw, hc->spec))))
+ break;
+
+ if(!hc){ /* this keyword didn't have a color set, add to list */
+ SPEC_COLOR_S *new;
+
+ new = (SPEC_COLOR_S *) fs_get(sizeof(*hc));
+ memset((void *) new, 0, sizeof(*new));
+ new->spec = cpystr(kw->kw);
+ new->fg = cpystr(ps->VAR_NORM_FORE_COLOR);
+ new->bg = cpystr(ps->VAR_NORM_BACK_COLOR);
+
+ if(hcolors){
+ for(hc = hcolors; hc->next; hc = hc->next)
+ ;
+
+ hc->next = new;
+ }
+ else
+ hcolors = new;
+
+ hc = new;
+ }
+
+ if(hc){
+ char **pc = ((*cl)->flags & CF_VAR2) ? &hc->bg : &hc->fg;
+
+ if(old_val = mswin_rgbchoice(*pc)){
+ fs_give((void **) pc);
+ *pc = old_val;
+
+ /*
+ * Turn on selected *'s for default selections, if any, and
+ * for ones we forced on.
+ */
+ color_update_selected(ps, *cl,
+ (hc && hc->fg && hc->fg[0]
+ && hc->bg && hc->bg[0])
+ ? hc->fg : ps->VAR_NORM_FORE_COLOR,
+ (hc && hc->fg && hc->fg[0]
+ && hc->bg && hc->bg[0])
+ ? hc->bg : ps->VAR_NORM_BACK_COLOR,
+ TRUE);
+
+ alval = ALVAL((*cl)->var, ew);
+ free_list_array(alval);
+ *alval = varlist_from_spec_colors(hcolors);
+ fix_side_effects(ps, (*cl)->var, 0);
+ set_current_color_vals(ps);
+ ClearScreen();
+ rv = 1;
+ }
+ }
+
+ if(hcolors)
+ free_spec_colors(&hcolors);
+
+ ps->mangled_screen = 1;
+ break;
+#endif
+
+ default :
+ rv = -1;
+ break;
+ }
+
+
+ if(rv == 1)
+ exception_override_warning((*cl)->var);
+
+ return(rv);
+}
+
+
+/*
+ * Turn on selected *'s for default selections, if any, and
+ * for ones we forced on.
+ * Adjust the Sample line right above the color selection lines.
+ */
+void
+color_update_selected(struct pine *ps, CONF_S *cl, char *fg, char *bg, int cleardef)
+{
+ int i, fg_is_custom = 1, bg_is_custom = 1;
+#ifdef _WINDOWS
+ CONF_S *cl_custom = NULL;
+#endif
+
+ /* back up to header line */
+ for(; cl && (cl->flags & CF_DOUBLEVAR); cl = prev_confline(cl))
+ ;
+
+ /* adjust sample line */
+ if(cl && cl->var && cl->flags & CF_COLORSAMPLE){
+ if(cl->value)
+ fs_give((void **)&cl->value);
+
+ cl->value = color_setting_text_line(ps, cl->var);
+ }
+
+ for(i = 0, cl = next_confline(cl);
+ i < pico_count_in_color_table() && cl;
+ i++, cl = next_confline(cl)){
+ if(fg && !strucmp(color_to_canonical_name(fg), colorx(i))){
+ cl->value[1] = R_SELD;
+ fg_is_custom = 0;
+ }
+ else
+ cl->value[1] = ' ';
+
+ if(bg && !strucmp(color_to_canonical_name(bg), colorx(i))){
+ cl->value[cl->val2offset - cl->valoffset + 1] = R_SELD;
+ bg_is_custom = 0;
+ }
+ else
+ cl->value[cl->val2offset - cl->valoffset + 1] = ' ';
+ }
+
+#ifdef _WINDOWS
+ cl_custom = cl;
+ cl = next_confline(cl);
+#endif
+
+ if(cl && cl->var && offer_normal_color_for_var(ps, cl->var)){
+ if(fg && !struncmp(color_to_canonical_name(fg), MATCH_NORM_COLOR, RGBLEN)){
+ cl->value[1] = R_SELD;
+ fg_is_custom = 0;
+ }
+ else
+ cl->value[1] = ' ';
+
+ if(bg && !struncmp(color_to_canonical_name(bg), MATCH_NORM_COLOR, RGBLEN)){
+ cl->value[cl->val2offset - cl->valoffset + 1] = R_SELD;
+ bg_is_custom = 0;
+ }
+ else
+ cl->value[cl->val2offset - cl->valoffset + 1] = ' ';
+
+ cl = next_confline(cl);
+ }
+
+ if(cl && cl->var && offer_none_color_for_var(ps, cl->var)){
+ if(fg && !struncmp(color_to_canonical_name(fg), MATCH_NONE_COLOR, RGBLEN)){
+ cl->value[1] = R_SELD;
+ fg_is_custom = 0;
+ }
+ else
+ cl->value[1] = ' ';
+
+ if(bg && !struncmp(color_to_canonical_name(bg), MATCH_NONE_COLOR, RGBLEN)){
+ cl->value[cl->val2offset - cl->valoffset + 1] = R_SELD;
+ bg_is_custom = 0;
+ }
+ else
+ cl->value[cl->val2offset - cl->valoffset + 1] = ' ';
+
+ cl = next_confline(cl);
+ }
+
+ /* Turn off Default X */
+ if(cleardef)
+ cl->value[1] = ' ';
+
+#ifdef _WINDOWS
+ /* check for a custom setting */
+ if(cl_custom){
+ cl_custom->value[1] = fg_is_custom ? R_SELD : ' ';
+ cl_custom->value[cl_custom->val2offset - cl_custom->valoffset + 1]
+ = bg_is_custom ? R_SELD : ' ';
+ }
+#endif
+}
+
+
+int
+color_edit_screen(struct pine *ps, CONF_S **cl)
+{
+ OPT_SCREEN_S screen, *saved_screen;
+ CONF_S *ctmp = NULL, *first_line = NULL, *ctmpb;
+ int rv, is_index = 0, is_hdrcolor = 0, indent = 12;
+ int is_general = 0, is_keywordcol = 0;
+ char tmp[1200+1], name[1200], *p;
+ struct variable *vtmp, v;
+ int i, def;
+ COLOR_PAIR *color = NULL;
+ SPEC_COLOR_S *hc = NULL, *hcolors = NULL;
+ KEYWORD_S *kw;
+
+ vtmp = (*cl)->var;
+ if(vtmp == &ps->vars[V_VIEW_HDR_COLORS])
+ is_hdrcolor++;
+ else if(vtmp == &ps->vars[V_KW_COLORS])
+ is_keywordcol++;
+ else if(color_holding_var(ps, vtmp)){
+ if(!struncmp(vtmp->name, "index-", 6))
+ is_index++;
+ else
+ is_general++;
+ }
+
+ new_confline(&ctmp);
+ /* Blank line */
+ ctmp->flags |= CF_NOSELECT | CF_B_LINE;
+
+ first_line = ctmp;
+
+ new_confline(&ctmp)->var = vtmp;
+
+ name[0] = '\0';
+ if(is_general){
+ p = srchstr(vtmp->name, "-foreground-color");
+ snprintf(name, sizeof(name), "%.*s", p ? MIN(p - vtmp->name, 30) : 30, vtmp->name);
+ name[sizeof(name)-1] = '\0';
+ if(islower((unsigned char)name[0]))
+ name[0] = toupper((unsigned char)name[0]);
+ }
+ else if(is_index){
+ p = srchstr(vtmp->name, "-foreground-color");
+ snprintf(name, sizeof(name), "%.*s Symbol",
+ p ? MIN(p - vtmp->name, 30) : 30, vtmp->name);
+ name[sizeof(name)-1] = '\0';
+ if(islower((unsigned char)name[0]))
+ name[0] = toupper((unsigned char)name[0]);
+ }
+ else if(is_hdrcolor){
+ char **lval;
+
+ lval = LVAL(vtmp, ew);
+ hcolors = spec_colors_from_varlist(lval, 0);
+
+ for(hc = hcolors, i = 0; hc; hc = hc->next, i++)
+ if(CFC_ICUST(*cl) == i)
+ break;
+
+ if(hc){
+ snprintf(name, sizeof(name), "%s%s", HEADER_WORD, hc->spec);
+ name[sizeof(name)-1] = '\0';
+ i = sizeof(HEADER_WORD) - 1;
+ if(islower((unsigned char) name[i]))
+ name[i] = toupper((unsigned char) name[i]);
+ }
+ }
+ else if(is_keywordcol){
+ char **lval;
+
+ for(kw=ps->keywords, i=0; kw; kw=kw->next, i++)
+ if(CFC_ICUST(*cl) == i)
+ break;
+
+ if(kw){
+ char *nm, *comment = NULL;
+
+ nm = kw->nick ? kw->nick : kw->kw ? kw->kw : "";
+ if(utf8_width(nm) > 60)
+ nm = short_str(nm, tmp_20k_buf, SIZEOF_20KBUF, 60, EndDots);
+
+ if(kw->nick && kw->kw && kw->kw[0])
+ comment = kw->kw;
+
+ if(utf8_width(nm) + (comment ? utf8_width(comment) : 0) < 60)
+ utf8_snprintf(name, sizeof(name), "%.50w%s%.50w%s",
+ nm,
+ comment ? " (" : "",
+ comment ? comment : "",
+ comment ? ")" : "");
+ else
+ snprintf(name, sizeof(name), "%s", nm);
+
+ name[sizeof(name)-1] = '\0';
+
+ lval = LVAL(vtmp, ew);
+ hcolors = spec_colors_from_varlist(lval, 0);
+ if(kw && hcolors)
+ if(!(kw->nick && kw->nick[0]
+ && (color=hdr_color(kw->nick, NULL, hcolors))))
+ if(kw->kw && kw->kw[0])
+ color = hdr_color(kw->kw, NULL, hcolors);
+ }
+ }
+
+ snprintf(tmp, sizeof(tmp), "%s Color =", name[0] ? name : "?");
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmpb = ctmp;
+ ctmp->flags |= (CF_STARTITEM | CF_NOSELECT);
+ ctmp->keymenu = &color_changing_keymenu;
+
+ if(is_hdrcolor){
+ char **apval;
+
+ def = !(hc && hc->fg && hc->fg[0] && hc->bg && hc->bg[0]);
+
+ add_color_setting_disp(ps, &ctmp, vtmp, ctmpb,
+ &custom_color_changing_keymenu,
+ &hdr_color_checkbox_keymenu,
+ config_help(vtmp - ps->vars, 0),
+ indent, CFC_ICUST(*cl),
+ def ? ps_global->VAR_NORM_FORE_COLOR
+ : hc->fg,
+ def ? ps_global->VAR_NORM_BACK_COLOR
+ : hc->bg,
+ def);
+
+ /* optional string to match in header value */
+ new_confline(&ctmp);
+ ctmp->varnamep = ctmpb;
+ ctmp->keymenu = &color_pattern_keymenu;
+ ctmp->help = h_config_customhdr_pattern;
+ ctmp->tool = color_text_tool;
+ ctmp->varoffset = indent-5;
+ ctmp->varname = cpystr(_("Pattern to match ="));
+ ctmp->valoffset = indent-5 + strlen(ctmp->varname) + 1;
+ ctmp->varmem = (*cl)->varmem;
+
+ /*
+ * This is really ugly. This is just to get the value correct.
+ */
+ memset(&v, 0, sizeof(v));
+ v.is_used = 1;
+ v.is_user = 1;
+ apval = APVAL(&v, ew);
+ if(hc && hc->val && apval)
+ *apval = pattern_to_string(hc->val);
+
+ set_current_val(&v, FALSE, FALSE);
+ ctmp->var = &v;
+ ctmp->value = pretty_value(ps, ctmp);
+ ctmp->var = vtmp;
+ if(apval && *apval)
+ fs_give((void **)apval);
+
+ if(v.current_val.p)
+ fs_give((void **)&v.current_val.p);
+
+ if(hcolors)
+ free_spec_colors(&hcolors);
+ }
+ else if(is_keywordcol){
+
+ def = !(color && color->fg && color->fg[0]
+ && color->bg && color->bg[0]);
+
+ add_color_setting_disp(ps, &ctmp, vtmp, ctmpb,
+ &kw_color_changing_keymenu,
+ &kw_color_checkbox_keymenu,
+ config_help(vtmp - ps->vars, 0),
+ indent, CFC_ICUST(*cl),
+ def ? ps_global->VAR_NORM_FORE_COLOR
+ : color->fg,
+ def ? ps_global->VAR_NORM_BACK_COLOR
+ : color->bg,
+ def);
+
+ if(hcolors)
+ free_spec_colors(&hcolors);
+ }
+ else{
+ char *pvalfg, *pvalbg;
+ int def;
+ COLOR_PAIR *newc;
+
+ pvalfg = PVAL(vtmp, ew);
+ pvalbg = PVAL(vtmp+1, ew);
+ def = !(pvalfg && pvalfg[0] && pvalbg && pvalbg[0]);
+ if(def){
+ /* display default val, if there is one */
+ pvalfg = PVAL(vtmp, Main);
+ pvalbg = PVAL(vtmp+1, Main);
+ if(ew == Post && pvalfg && pvalfg[0] && pvalbg && pvalbg[0]){
+ ;
+ }
+ else if(vtmp && vtmp->global_val.p && vtmp->global_val.p[0] &&
+ (vtmp+1)->global_val.p && (vtmp+1)->global_val.p[0]){
+ pvalfg = vtmp->global_val.p;
+ pvalbg = (vtmp+1)->global_val.p;
+ }
+ else{
+ if(var_defaults_to_rev(vtmp) && (newc = pico_get_rev_color())){
+ pvalfg = newc->fg;
+ pvalbg = newc->bg;
+ }
+ else{
+ pvalfg = NULL;
+ pvalbg = NULL;
+ }
+ }
+ }
+
+ add_color_setting_disp(ps, &ctmp, vtmp, ctmpb,
+ &color_changing_keymenu,
+ &config_checkbox_keymenu,
+ config_help(vtmp - ps->vars, 0),
+ indent, 0, pvalfg, pvalbg, def);
+ }
+
+ first_line = first_sel_confline(first_line);
+
+ saved_screen = opt_screen;
+ memset(&screen, 0, sizeof(screen));
+ screen.ro_warning = saved_screen ? saved_screen->deferred_ro_warning : 0;
+ rv = conf_scroll_screen(ps, &screen, first_line,
+ ew == Post ? _("SETUP COLOR EXCEPTIONS")
+ : _("SETUP COLOR"),
+ _("configuration"), 1);
+
+ opt_screen = saved_screen;
+ ps->mangled_screen = 1;
+ return(rv);
+}
diff --git a/alpine/colorconf.h b/alpine/colorconf.h
new file mode 100644
index 00000000..501e31e5
--- /dev/null
+++ b/alpine/colorconf.h
@@ -0,0 +1,79 @@
+/*
+ * $Id: colorconf.h 769 2007-10-24 00:15:40Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_COLORCONF_INCLUDED
+#define PINE_COLORCONF_INCLUDED
+
+
+#include "conftype.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+
+
+#define HEADER_WORD "Header "
+#define KW_COLORS_HDR "KEYWORD COLORS"
+#define ADDHEADER_COMMENT N_("[ Use the AddHeader command to add colored headers in MESSAGE TEXT screen ]")
+#define EQ_COL 37
+
+#define SPACE_BETWEEN_DOUBLEVARS 3
+#define SAMPLE_LEADER "---------------------------"
+#define SAMP1 "[Sample ]"
+#define SAMP2 "[Default]"
+#define SAMPEXC "[Exception]"
+#define SBS 1 /* space between samples */
+#define COLOR_BLOB "< >"
+#define COLOR_BLOB_TRAN "<TRAN>"
+#define COLOR_BLOB_NORM "<NORM>"
+#define COLOR_BLOB_NONE "<NONE>"
+#define COLOR_BLOB_LEN 6
+#define COLOR_INDENT 3
+#define COLORNOSET " [ Colors below may not be set until color is turned on above ]"
+
+
+/*
+ * The CONF_S's varmem field serves dual purposes. The low two bytes
+ * are reserved for the pre-defined color index (though only 8 are
+ * defined for the nonce, and the high order bits are for the index
+ * of the particular SPEC_COLOR_S this CONF_S is associated with.
+ * Capiche?
+ */
+#define CFC_ICOLOR(V) ((V)->varmem & 0xffff)
+#define CFC_ICUST(V) ((V)->varmem >> 16)
+#define CFC_SET_COLOR(I, C) (((I) << 16) | (C))
+#define CFC_ICUST_INC(V) CFC_SET_COLOR(CFC_ICUST(V) + 1, CFC_ICOLOR(V))
+#define CFC_ICUST_DEC(V) CFC_SET_COLOR(CFC_ICUST(V) - 1, CFC_ICOLOR(V))
+
+#define CFC_ICOLOR_NORM (1000)
+#define CFC_ICOLOR_NONE (1001)
+
+
+extern int treat_color_vars_as_text;
+
+
+/* exported protoypes */
+void color_config_screen(struct pine *, int);
+int color_setting_tool(struct pine *, int, CONF_S **, unsigned);
+char *sampleexc_text(struct pine *, struct variable *);
+int color_related_var(struct pine *, struct variable *);
+int color_holding_var(struct pine *, struct variable *);
+char *sample_text(struct pine *, struct variable *);
+char *color_parenthetical(struct variable *);
+void add_color_setting_disp(struct pine *, CONF_S **, struct variable *, CONF_S *,
+ struct key_menu *, struct key_menu *, HelpType,
+ int, int, char *, char *, int);
+
+
+#endif /* PINE_COLORCONF_INCLUDED */
diff --git a/alpine/confscroll.c b/alpine/confscroll.c
new file mode 100644
index 00000000..0ada8a79
--- /dev/null
+++ b/alpine/confscroll.c
@@ -0,0 +1,5968 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: confscroll.c 1169 2008-08-27 06:42:06Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "headers.h"
+#include "confscroll.h"
+#include "keymenu.h"
+#include "status.h"
+#include "titlebar.h"
+#include "help.h"
+#include "radio.h"
+#include "print.h"
+#include "ldapconf.h"
+#include "roleconf.h"
+#include "colorconf.h"
+#include "mailview.h"
+#include "mailcmd.h"
+#include "mailindx.h"
+#include "talk.h"
+#include "setup.h"
+#include "smime.h"
+#include "../pith/state.h"
+#include "../pith/flag.h"
+#include "../pith/list.h"
+#include "../pith/conf.h"
+#include "../pith/util.h"
+#include "../pith/newmail.h"
+#include "../pith/sort.h"
+#include "../pith/thread.h"
+#include "../pith/color.h"
+#include "../pith/hist.h"
+#include "../pith/icache.h"
+#include "../pith/conf.h"
+#include "../pith/init.h"
+#include "../pith/folder.h"
+#include "../pith/busy.h"
+#include "../pith/tempfile.h"
+#include "../pith/pattern.h"
+#include "../pith/charconv/utf8.h"
+
+
+#define CONFIG_SCREEN_HELP_TITLE _("HELP FOR SETUP CONFIGURATION")
+
+/* TRANSLATORS: Empty Value is what is shown in the configuration
+ screen when the user not only does not set an option but also
+ wants to explicitly not use the default value. Empty value means
+ an option with no value. */
+char *empty_val = N_("Empty Value");
+char *empty_val2 = N_("<Empty Value>");
+/* TRANSLATORS: No Value set is similar to Empty Value, but the
+ user has not explicitly decided to not use the default. It is
+ just an option which the user has left at the default value. */
+char *no_val = N_("No Value Set");
+/* TRANSLATORS: Value is Fixed is what is displayed in the config
+ screen when the system managers have set an option to a specific
+ value and they don't allow the user to change it. The value
+ is fixed to a certain value. This isn't the same word as
+ Repaired, it means Unchanging. */
+char *fixed_val = N_("Value is Fixed");
+char yesstr[] = "Yes";
+char nostr[] = "No";
+
+
+EditWhich ew = Main;
+
+
+OPT_SCREEN_S *opt_screen;
+
+
+/*
+ * This is pretty ugly. Some of the routines operate differently depending
+ * on which variable they are operating on. Sometimes those variables are
+ * global (real alpine.h V_ style variables) and sometimes they are just
+ * local variables (as in role_config_edit_screen). These pointers are here
+ * so that the routines can figure out which variable they are operating
+ * on and do the right thing.
+ */
+struct variable *score_act_global_ptr,
+ *scorei_pat_global_ptr,
+ *age_pat_global_ptr,
+ *size_pat_global_ptr,
+ *cati_global_ptr,
+ *cat_cmd_global_ptr,
+ *cat_lim_global_ptr,
+ *startup_ptr,
+ *role_comment_ptr,
+ *role_forw_ptr,
+ *role_repl_ptr,
+ *role_fldr_ptr,
+ *role_afrom_ptr,
+ *role_filt_ptr,
+ *role_status1_ptr,
+ *role_status2_ptr,
+ *role_status3_ptr,
+ *role_status4_ptr,
+ *role_status5_ptr,
+ *role_status6_ptr,
+ *role_status7_ptr,
+ *role_status8_ptr,
+ *msg_state1_ptr,
+ *msg_state2_ptr,
+ *msg_state3_ptr,
+ *msg_state4_ptr;
+
+
+typedef NAMEVAL_S *(*PTR_TO_RULEFUNC)(int);
+
+
+/*
+ * Internal prototypes
+ */
+PTR_TO_RULEFUNC rulefunc_from_var(struct pine *, struct variable *);
+void set_radio_pretty_vals(struct pine *, CONF_S **);
+int save_include(struct pine *, struct variable *, int);
+void config_scroll_up(long);
+void config_scroll_down(long);
+void config_scroll_to_pos(long);
+CONF_S *config_top_scroll(struct pine *, CONF_S *);
+void update_option_screen(struct pine *, OPT_SCREEN_S *, Pos *);
+void print_option_screen(OPT_SCREEN_S *, char *);
+void option_screen_redrawer(void);
+char *text_pretty_value(struct pine *, CONF_S *);
+char *checkbox_pretty_value(struct pine *, CONF_S *);
+char *yesno_pretty_value(struct pine *, CONF_S *);
+char *radio_pretty_value(struct pine *, CONF_S *);
+char *sigfile_pretty_value(struct pine *, CONF_S *);
+char *color_pretty_value(struct pine *, CONF_S *);
+char *sort_pretty_value(struct pine *, CONF_S *);
+int longest_feature_name(void);
+COLOR_PAIR *sample_color(struct pine *, struct variable *);
+COLOR_PAIR *sampleexc_color(struct pine *, struct variable *);
+void clear_feature(char ***, char *);
+CONF_S *last_confline(CONF_S *);
+#ifdef _WINDOWS
+int config_scroll_callback(int, long);
+#endif
+
+
+/*
+ * We test for this same set of vars in a few places.
+ */
+int
+standard_radio_var(struct pine *ps, struct variable *v)
+{
+ return(v == &ps->vars[V_SAVED_MSG_NAME_RULE] ||
+ v == &ps->vars[V_FCC_RULE] ||
+ v == &ps->vars[V_GOTO_DEFAULT_RULE] ||
+ v == &ps->vars[V_INCOMING_STARTUP] ||
+ v == &ps->vars[V_PRUNING_RULE] ||
+ v == &ps->vars[V_REOPEN_RULE] ||
+ v == &ps->vars[V_THREAD_DISP_STYLE] ||
+ v == &ps->vars[V_THREAD_INDEX_STYLE] ||
+ v == &ps->vars[V_FLD_SORT_RULE] ||
+#ifndef _WINDOWS
+ v == &ps->vars[V_COLOR_STYLE] ||
+#endif
+ v == &ps->vars[V_INDEX_COLOR_STYLE] ||
+ v == &ps->vars[V_TITLEBAR_COLOR_STYLE] ||
+ v == &ps->vars[V_AB_SORT_RULE]);
+}
+
+
+PTR_TO_RULEFUNC
+rulefunc_from_var(struct pine *ps, struct variable *v)
+{
+ PTR_TO_RULEFUNC rulefunc = NULL;
+
+ if(v == &ps->vars[V_SAVED_MSG_NAME_RULE])
+ rulefunc = save_msg_rules;
+ else if(v == &ps->vars[V_FCC_RULE])
+ rulefunc = fcc_rules;
+ else if(v == &ps->vars[V_GOTO_DEFAULT_RULE])
+ rulefunc = goto_rules;
+ else if(v == &ps->vars[V_INCOMING_STARTUP])
+ rulefunc = incoming_startup_rules;
+ else if(v == startup_ptr)
+ rulefunc = startup_rules;
+ else if(v == &ps->vars[V_PRUNING_RULE])
+ rulefunc = pruning_rules;
+ else if(v == &ps->vars[V_REOPEN_RULE])
+ rulefunc = reopen_rules;
+ else if(v == &ps->vars[V_THREAD_DISP_STYLE])
+ rulefunc = thread_disp_styles;
+ else if(v == &ps->vars[V_THREAD_INDEX_STYLE])
+ rulefunc = thread_index_styles;
+ else if(v == &ps->vars[V_FLD_SORT_RULE])
+ rulefunc = fld_sort_rules;
+ else if(v == &ps->vars[V_AB_SORT_RULE])
+ rulefunc = ab_sort_rules;
+ else if(v == &ps->vars[V_INDEX_COLOR_STYLE])
+ rulefunc = index_col_style;
+ else if(v == &ps->vars[V_TITLEBAR_COLOR_STYLE])
+ rulefunc = titlebar_col_style;
+#ifndef _WINDOWS
+ else if(v == &ps->vars[V_COLOR_STYLE])
+ rulefunc = col_style;
+#endif
+
+ return(rulefunc);
+}
+
+
+void
+standard_radio_setup(struct pine *ps, CONF_S **cl, struct variable *v, CONF_S **first_line)
+{
+ int i, rindent = 12;
+ CONF_S *ctmpb;
+ PTR_TO_RULEFUNC rulefunc;
+ NAMEVAL_S *f;
+ char b[100];
+
+ if(!(cl && *cl))
+ return;
+
+ rulefunc = rulefunc_from_var(ps, v);
+ ctmpb = (*cl);
+
+ (*cl)->flags |= CF_NOSELECT;
+ (*cl)->keymenu = &config_radiobutton_keymenu;
+ (*cl)->tool = NULL;
+
+ /* put a nice delimiter before list */
+ new_confline(cl)->var = NULL;
+ (*cl)->varnamep = ctmpb;
+ (*cl)->keymenu = &config_radiobutton_keymenu;
+ (*cl)->help = NO_HELP;
+ (*cl)->tool = radiobutton_tool;
+ (*cl)->valoffset = rindent;
+ (*cl)->flags |= CF_NOSELECT;
+ /* TRANSLATORS: Set and Rule Values are the headings for an option
+ that can take one of several values. Underneath the Set heading
+ will be a column where one possibility is turned on (is Set).
+ The other column will be very short descriptions of what
+ the possibilities are (the Rule Values). */
+ utf8_snprintf(b, sizeof(b), "%-5.5w %s", _("Set"), _("Rule Values"));
+ (*cl)->value = cpystr(b);
+
+ new_confline(cl)->var = NULL;
+ (*cl)->varnamep = ctmpb;
+ (*cl)->keymenu = &config_radiobutton_keymenu;
+ (*cl)->help = NO_HELP;
+ (*cl)->tool = radiobutton_tool;
+ (*cl)->valoffset = rindent;
+ (*cl)->flags |= CF_NOSELECT;
+ (*cl)->value = cpystr("--- ----------------------");
+
+ if(rulefunc)
+ for(i = 0; (f = (*rulefunc)(i)); i++){
+ new_confline(cl)->var = v;
+ if(first_line && !*first_line && !pico_usingcolor())
+ *first_line = (*cl);
+
+ (*cl)->varnamep = ctmpb;
+ (*cl)->keymenu = &config_radiobutton_keymenu;
+ (*cl)->help = (v == startup_ptr)
+ ? h_config_other_startup
+ : config_help(v - ps->vars,0);
+ (*cl)->tool = radiobutton_tool;
+ (*cl)->valoffset = rindent;
+ (*cl)->varmem = i;
+ (*cl)->value = pretty_value(ps, *cl);
+ }
+}
+
+
+/*
+ * Reset the displayed values for all of the lines for this
+ * variable because others besides this line may change.
+ */
+void
+set_radio_pretty_vals(struct pine *ps, CONF_S **cl)
+{
+ CONF_S *ctmp;
+
+ if(!(cl && *cl &&
+ ((*cl)->var == &ps->vars[V_SORT_KEY] ||
+ standard_radio_var(ps, (*cl)->var) ||
+ (*cl)->var == startup_ptr)))
+ return;
+
+ /* hunt backwards */
+ for(ctmp = *cl;
+ ctmp && !(ctmp->flags & CF_NOSELECT) && !ctmp->varname;
+ ctmp = prev_confline(ctmp)){
+ if(ctmp->value)
+ fs_give((void **)&ctmp->value);
+
+ ctmp->value = pretty_value(ps, ctmp);
+ }
+
+ /* hunt forwards */
+ for(ctmp = *cl;
+ ctmp && !ctmp->varname && !(ctmp->flags & CF_NOSELECT);
+ ctmp = next_confline(ctmp)){
+ if(ctmp->value)
+ fs_give((void **)&ctmp->value);
+
+ ctmp->value = pretty_value(ps, ctmp);
+ }
+}
+
+
+/*
+ * test whether or not a var is
+ *
+ * returns: 1 if it should be excluded, 0 otw
+ */
+int
+exclude_config_var(struct pine *ps, struct variable *var, int allow_hard_to_config_remotely)
+{
+ if((ew != Main && (var->is_onlymain)) ||
+ (ew != ps_global->ew_for_except_vars && var->is_outermost))
+ return(1);
+
+ if(allow_hard_to_config_remotely)
+ return(!(var->is_user && var->is_used && !var->is_obsolete));
+
+ switch(var - ps->vars){
+ case V_MAIL_DIRECTORY :
+ case V_INCOMING_FOLDERS :
+ case V_FOLDER_SPEC :
+ case V_NEWS_SPEC :
+ case V_STANDARD_PRINTER :
+ case V_LAST_TIME_PRUNE_QUESTION :
+ case V_LAST_VERS_USED :
+ case V_ADDRESSBOOK :
+ case V_GLOB_ADDRBOOK :
+ case V_DISABLE_DRIVERS :
+ case V_DISABLE_AUTHS :
+ case V_REMOTE_ABOOK_METADATA :
+ case V_REMOTE_ABOOK_HISTORY :
+ case V_REMOTE_ABOOK_VALIDITY :
+ case V_OPER_DIR :
+ case V_USERINPUTTIMEO :
+ case V_TCPOPENTIMEO :
+ case V_TCPREADWARNTIMEO :
+ case V_TCPWRITEWARNTIMEO :
+ case V_TCPQUERYTIMEO :
+ case V_RSHCMD :
+ case V_RSHPATH :
+ case V_RSHOPENTIMEO :
+ case V_SSHCMD :
+ case V_SSHPATH :
+ case V_SSHOPENTIMEO :
+ case V_SENDMAIL_PATH :
+ case V_NEW_VER_QUELL :
+ case V_PATTERNS :
+ case V_PAT_ROLES :
+ case V_PAT_FILTS :
+ case V_PAT_SCORES :
+ case V_PAT_INCOLS :
+ case V_PAT_OTHER :
+ case V_PAT_SRCH :
+ case V_PRINTER :
+ case V_PERSONAL_PRINT_COMMAND :
+ case V_PERSONAL_PRINT_CATEGORY :
+ case V_RSS_NEWS :
+ case V_RSS_WEATHER :
+ case V_WP_INDEXHEIGHT :
+ case V_WP_INDEXLINES :
+ case V_WP_AGGSTATE :
+ case V_WP_STATE :
+ case V_WP_COLUMNS :
+#ifndef _WINDOWS
+ case V_OLD_CHAR_SET :
+#endif /* ! _WINDOWS */
+#if defined(DOS) || defined(OS2)
+ case V_UPLOAD_CMD :
+ case V_UPLOAD_CMD_PREFIX :
+ case V_DOWNLOAD_CMD :
+ case V_DOWNLOAD_CMD_PREFIX :
+#ifdef _WINDOWS
+ case V_FONT_NAME :
+ case V_FONT_SIZE :
+ case V_FONT_STYLE :
+ case V_FONT_CHAR_SET :
+ case V_PRINT_FONT_NAME :
+ case V_PRINT_FONT_SIZE :
+ case V_PRINT_FONT_STYLE :
+ case V_PRINT_FONT_CHAR_SET :
+ case V_WINDOW_POSITION :
+ case V_CURSOR_STYLE :
+#endif /* _WINDOWS */
+#endif /* DOS */
+#ifdef ENABLE_LDAP
+ case V_LDAP_SERVERS :
+#endif /* ENABLE_LDAP */
+ return(1);
+
+ default:
+ break;
+ }
+
+ return(!(var->is_user && var->is_used && !var->is_obsolete &&
+#ifdef SMIME
+ !smime_related_var(ps, var) &&
+#endif /* SMIME */
+ !color_related_var(ps, var)));
+}
+
+
+/*
+ * Test to indicate what should be saved in case user wants to abandon
+ * changes.
+ */
+int
+save_include(struct pine *ps, struct variable *v, int allow_hard_to_config_remotely)
+{
+ return(!exclude_config_var(ps, v, allow_hard_to_config_remotely)
+ || (v->is_user
+ && v->is_used
+ && !v->is_obsolete
+ && (v == &ps->vars[V_PERSONAL_PRINT_COMMAND]
+#ifdef ENABLE_LDAP
+ || v == &ps->vars[V_LDAP_SERVERS]
+#endif
+ )));
+}
+
+
+/*
+ * Handles screen painting and motion. Passes other commands to
+ * custom tools.
+ *
+ * Tool return values: Tools should return the following:
+ * 0 nothing changed
+ * -1 unrecognized command
+ * 1 something changed, conf_scroll_screen should remember that
+ * 2 tells conf_scroll_screen to return with value 1 or 0 depending
+ * on whether or not it has previously gotten a 1 from some tool.
+ * 3 tells conf_scroll_screen to return 1 (like 1 and 2 combined)
+ * ? Other tool-specific values can be used. They will cause
+ * conf_scroll_screen to return that value.
+ *
+ * Return values:
+ * 0 if nothing happened. That is, a tool returned 2 and we hadn't
+ * previously noted a return of 1
+ * 1 if something happened. That is, a tool returned 2 and we had
+ * previously noted a return of 1
+ * ? Tool-returned value different from -1, 0, 1, 2, or 3. This is it.
+ *
+ * Special proviso: If first_line->flags has CF_CHANGES set on entry, then
+ * that will cause things to behave like a change was made since entering
+ * this function.
+ */
+int
+conf_scroll_screen(struct pine *ps, OPT_SCREEN_S *screen, CONF_S *start_line, char *title, char *pdesc, int multicol)
+{
+ char tmp[MAXPATH+1];
+ char *utf8str;
+ UCS ch = 'x';
+ int cmd, i, j, done = 0, changes = 0;
+ int retval = 0;
+ int km_popped = 0, stay_in_col = 0;
+ struct key_menu *km = NULL;
+ CONF_S *ctmpa = NULL, *ctmpb = NULL;
+ Pos cursor_pos;
+ OtherMenu what_keymenu = FirstMenu;
+ void (*prev_redrawer)(void);
+
+ dprint((7, "conf_scroll_screen()\n"));
+
+ if(BODY_LINES(ps) < 1){
+ q_status_message(SM_ORDER | SM_DING, 3, 3, _("Screen too small"));
+ return(0);
+ }
+
+ if(screen && screen->ro_warning)
+ q_status_message1(SM_ORDER, 1, 3,
+ /* TRANSLATORS: "Config file not changeable," is what replaces the %s */
+ _("%s can't change options or settings"),
+ ps_global->restricted ? "Alpine demo"
+ : _("Config file not changeable,"));
+
+ screen->current = start_line;
+ if(start_line && start_line->flags & CF_CHANGES)
+ changes++;
+
+ opt_screen = screen;
+ ps->mangled_screen = 1;
+ ps->redrawer = option_screen_redrawer;
+
+ while(!done){
+ ps->user_says_cancel = 0;
+ if(km_popped){
+ km_popped--;
+ if(km_popped == 0){
+ clearfooter(ps);
+ ps->mangled_body = 1;
+ }
+ }
+
+ if(ps->mangled_screen){
+ ps->mangled_header = 1;
+ ps->mangled_footer = 1;
+ ps->mangled_body = 1;
+ ps->mangled_screen = 0;
+ }
+
+ /*----------- Check for new mail -----------*/
+ if(new_mail(0, NM_TIMING(ch), NM_STATUS_MSG | NM_DEFER_SORT) >= 0)
+ ps->mangled_header = 1;
+
+ if(ps->mangled_header){
+ set_titlebar(title, ps->mail_stream,
+ ps->context_current,
+ ps->cur_folder, ps->msgmap, 1, FolderName, 0, 0, NULL);
+ ps->mangled_header = 0;
+ }
+
+ update_option_screen(ps, screen, &cursor_pos);
+
+ if(F_OFF(F_SHOW_CURSOR, ps)){
+ cursor_pos.row = ps->ttyo->screen_rows - FOOTER_ROWS(ps);
+ cursor_pos.col = 0;
+ }
+
+ /*---- This displays new mail notification, or errors ---*/
+ if(km_popped){
+ FOOTER_ROWS(ps) = 3;
+ mark_status_unknown();
+ }
+
+ display_message(ch);
+ if(km_popped){
+ FOOTER_ROWS(ps) = 1;
+ mark_status_unknown();
+ }
+
+ if(ps->mangled_footer || km != screen->current->keymenu){
+ bitmap_t bitmap;
+
+ setbitmap(bitmap);
+
+ ps->mangled_footer = 0;
+ km = screen->current->keymenu;
+
+ if(multicol &&
+ (F_OFF(F_ARROW_NAV, ps_global) ||
+ F_ON(F_RELAXED_ARROW_NAV, ps_global))){
+ menu_clear_binding(km, KEY_LEFT);
+ menu_clear_binding(km, KEY_RIGHT);
+ menu_clear_binding(km, KEY_UP);
+ menu_clear_binding(km, KEY_DOWN);
+ menu_add_binding(km, KEY_UP, MC_CHARUP);
+ menu_add_binding(km, KEY_DOWN, MC_CHARDOWN);
+ menu_add_binding(km, KEY_LEFT, MC_PREVITEM);
+ menu_add_binding(km, ctrl('B'), MC_PREVITEM);
+ menu_add_binding(km, KEY_RIGHT, MC_NEXTITEM);
+ menu_add_binding(km, ctrl('F'), MC_NEXTITEM);
+ }
+ else{
+ menu_clear_binding(km, KEY_LEFT);
+ menu_clear_binding(km, KEY_RIGHT);
+ menu_clear_binding(km, KEY_UP);
+ menu_clear_binding(km, KEY_DOWN);
+
+ /*
+ * Fix up arrow nav mode if necessary...
+ */
+ if(F_ON(F_ARROW_NAV, ps_global)){
+ int cmd;
+
+ if((cmd = menu_clear_binding(km, '<')) != MC_UNKNOWN){
+ menu_add_binding(km, '<', cmd);
+ menu_add_binding(km, KEY_LEFT, cmd);
+ }
+
+ if((cmd = menu_clear_binding(km, '>')) != MC_UNKNOWN){
+ menu_add_binding(km, '>', cmd);
+ menu_add_binding(km, KEY_RIGHT, cmd);
+ }
+
+ if((cmd = menu_clear_binding(km, 'p')) != MC_UNKNOWN){
+ menu_add_binding(km, 'p', cmd);
+ menu_add_binding(km, KEY_UP, cmd);
+ }
+
+ if((cmd = menu_clear_binding(km, 'n')) != MC_UNKNOWN){
+ menu_add_binding(km, 'n', cmd);
+ menu_add_binding(km, KEY_DOWN, cmd);
+ }
+ }
+ }
+
+ if(km_popped){
+ FOOTER_ROWS(ps) = 3;
+ clearfooter(ps);
+ }
+
+ draw_keymenu(km, bitmap, ps->ttyo->screen_cols,
+ 1-FOOTER_ROWS(ps), 0, what_keymenu);
+ what_keymenu = SameMenu;
+
+ if(km_popped){
+ FOOTER_ROWS(ps) = 1;
+ mark_keymenu_dirty();
+ }
+ }
+
+ MoveCursor(cursor_pos.row, cursor_pos.col);
+#ifdef MOUSE
+ mouse_in_content(KEY_MOUSE, -1, -1, 0, 0); /* prime the handler */
+ register_mfunc(mouse_in_content, HEADER_ROWS(ps_global), 0,
+ ps_global->ttyo->screen_rows -(FOOTER_ROWS(ps_global)+1),
+ ps_global->ttyo->screen_cols);
+#endif
+#ifdef _WINDOWS
+ mswin_setscrollcallback(config_scroll_callback);
+#endif
+ /*------ Read the command from the keyboard ----*/
+ ch = READ_COMMAND(&utf8str);
+#ifdef MOUSE
+ clear_mfunc(mouse_in_content);
+#endif
+#ifdef _WINDOWS
+ mswin_setscrollcallback(NULL);
+#endif
+
+ cmd = menu_command(ch, km);
+
+ if(km_popped)
+ switch(cmd){
+ case MC_NONE:
+ case MC_OTHER:
+ case MC_RESIZE:
+ case MC_REPAINT:
+ km_popped++;
+ break;
+
+ default:
+ clearfooter(ps);
+ break;
+ }
+
+ switch(cmd){
+ case MC_OTHER :
+ what_keymenu = NextMenu;
+ ps->mangled_footer = 1;
+ break;
+
+ case MC_HELP: /* help! */
+ if(FOOTER_ROWS(ps) == 1 && km_popped == 0){
+ km_popped = 2;
+ ps->mangled_footer = 1;
+ break;
+ }
+
+ if(screen->current->help != NO_HELP){
+ prev_redrawer = ps_global->redrawer;
+ helper(screen->current->help,
+ (screen->current->help_title)
+ ? screen->current->help_title
+ : CONFIG_SCREEN_HELP_TITLE,
+ HLPD_SIMPLE);
+ ps_global->redrawer = prev_redrawer;
+ ps->mangled_screen = 1;
+ }
+ else
+ q_status_message(SM_ORDER,0,3,_("No help yet."));
+
+ break;
+
+
+ case MC_NEXTITEM: /* next list element */
+ case MC_CHARDOWN:
+ stay_in_col = 0;
+ if(screen->current->flags & CF_DOUBLEVAR){
+ /* if going from col1 to col2, it's simple */
+ if(!(screen->current->flags & CF_VAR2) && cmd == MC_NEXTITEM){
+ screen->current->flags |= CF_VAR2;
+ break;
+ }
+
+ /* otherwise we fall through to normal next */
+ stay_in_col = (screen->current->flags & CF_VAR2 &&
+ cmd == MC_CHARDOWN);
+ screen->current->flags &= ~CF_VAR2;
+ }
+
+ for(ctmpa = next_confline(screen->current), i = 1;
+ ctmpa && (ctmpa->flags & CF_NOSELECT);
+ ctmpa = next_confline(ctmpa), i++)
+ ;
+
+ if(ctmpa){
+ screen->current = ctmpa;
+ if(screen->current->flags & CF_DOUBLEVAR && stay_in_col)
+ screen->current->flags |= CF_VAR2;
+
+ if(cmd == MC_CHARDOWN){
+ for(ctmpa = screen->top_line,
+ j = BODY_LINES(ps) - 1 - HS_MARGIN(ps);
+ j > 0 && ctmpa && ctmpa != screen->current;
+ ctmpa = next_confline(ctmpa), j--)
+ ;
+
+ if(!j && ctmpa){
+ for(i = 0;
+ ctmpa && ctmpa != screen->current;
+ ctmpa = next_confline(ctmpa), i++)
+ ;
+
+ if(i)
+ config_scroll_up(i);
+ }
+ }
+ }
+ else{
+ /*
+ * Scroll screen a bit so we show the non-selectable
+ * lines at the bottom.
+ */
+
+ /* set ctmpa to the bottom line on the screen */
+ for(ctmpa = screen->top_line, j = BODY_LINES(ps) - 1;
+ j > 0 && ctmpa;
+ ctmpa = next_confline(ctmpa), j--)
+ ;
+
+ i = 0;
+ if(ctmpa){
+ for(ctmpa = next_confline(ctmpa);
+ ctmpa &&
+ (ctmpa->flags & (CF_NOSELECT | CF_B_LINE)) ==
+ CF_NOSELECT;
+ ctmpa = next_confline(ctmpa), i++)
+ ;
+ }
+
+ if(i)
+ config_scroll_up(i);
+ else
+ q_status_message(SM_ORDER,0,1, _("Already at end of screen"));
+ }
+
+ break;
+
+ case MC_PREVITEM: /* prev list element */
+ case MC_CHARUP:
+ stay_in_col = 0;
+ if(screen->current->flags & CF_DOUBLEVAR){
+ if(screen->current->flags & CF_VAR2 && cmd == MC_PREVITEM){
+ screen->current->flags &= ~CF_VAR2;
+ break;
+ }
+
+ /* otherwise we fall through to normal prev */
+ stay_in_col = (!(screen->current->flags & CF_VAR2) &&
+ cmd == MC_CHARUP);
+ screen->current->flags &= ~CF_VAR2;
+ }
+ else if(cmd == MC_CHARUP)
+ stay_in_col = 1;
+
+ ctmpa = screen->current;
+ i = 0;
+ do
+ if(ctmpa == config_top_scroll(ps, screen->top_line))
+ i = 1;
+ else if(i)
+ i++;
+ while((ctmpa = prev_confline(ctmpa))
+ && (ctmpa->flags&CF_NOSELECT));
+
+ if(ctmpa){
+ screen->current = ctmpa;
+ if(screen->current->flags & CF_DOUBLEVAR && !stay_in_col)
+ screen->current->flags |= CF_VAR2;
+
+ if((cmd == MC_CHARUP) && i)
+ config_scroll_down(i);
+ }
+ else
+ q_status_message(SM_ORDER, 0, 1,
+ _("Already at start of screen"));
+
+ break;
+
+ case MC_PAGEDN: /* page forward */
+ screen->current->flags &= ~CF_VAR2;
+ for(ctmpa = screen->top_line, i = BODY_LINES(ps);
+ i > 0 && ctmpa;
+ ctmpb = ctmpa, ctmpa = next_confline(ctmpa), i--)
+ ;
+
+ if(ctmpa){ /* first line off bottom of screen */
+ ctmpb = ctmpa;
+ ps->mangled_body = 1;
+ /* find first selectable line on next page */
+ for(screen->top_line = ctmpa;
+ ctmpa && (ctmpa->flags & CF_NOSELECT);
+ ctmpa = next_confline(ctmpa))
+ ;
+
+ /*
+ * No selectable lines on next page. Slide up to first
+ * selectable.
+ */
+ if(!ctmpa){
+ for(ctmpa = prev_confline(ctmpb);
+ ctmpa && (ctmpa->flags & CF_NOSELECT);
+ ctmpa = prev_confline(ctmpa))
+ ;
+
+ if(ctmpa)
+ screen->top_line = ctmpa;
+ }
+ }
+ else{ /* on last screen */
+ /* just move current down to last entry on screen */
+ if(ctmpb){ /* last line of data */
+ for(ctmpa = ctmpb, i = BODY_LINES(ps);
+ i > 0 && ctmpa && (ctmpa->flags & CF_NOSELECT);
+ ctmpa = prev_confline(ctmpa), i--)
+ ;
+
+ if(ctmpa == screen->current){
+ q_status_message(SM_ORDER,0,1,
+ _("Already at end of screen"));
+ goto no_down;
+ }
+
+ ps->mangled_body = 1;
+ }
+ }
+
+ if(ctmpa)
+ screen->current = ctmpa;
+no_down:
+ break;
+
+ case MC_PAGEUP: /* page backward */
+ ps->mangled_body = 1;
+ screen->current->flags &= ~CF_VAR2;
+ if(!(ctmpa=prev_confline(screen->top_line)))
+ ctmpa = screen->current;
+
+ for(i = BODY_LINES(ps) - 1;
+ i > 0 && prev_confline(ctmpa);
+ i--, ctmpa = prev_confline(ctmpa))
+ ;
+
+ for(screen->top_line = ctmpa;
+ ctmpa && (ctmpa->flags & CF_NOSELECT);
+ ctmpa = next_confline(ctmpa))
+ ;
+
+ if(ctmpa){
+ if(ctmpa == screen->current){
+ /*
+ * We get to here if there was nothing selectable on
+ * the previous page. There still may be something
+ * selectable further back than the previous page,
+ * so look for that.
+ */
+ for(ctmpa = prev_confline(screen->top_line);
+ ctmpa && (ctmpa->flags & CF_NOSELECT);
+ ctmpa = prev_confline(ctmpa))
+ ;
+
+ if(!ctmpa){
+ ctmpa = screen->current;
+ q_status_message(SM_ORDER, 0, 1,
+ _("Already at start of screen"));
+ }
+ }
+
+ screen->current = ctmpa;
+ }
+
+ break;
+
+#ifdef MOUSE
+ case MC_MOUSE:
+ {
+ MOUSEPRESS mp;
+
+ mouse_get_last (NULL, &mp);
+ mp.row -= HEADER_ROWS(ps);
+ ctmpa = screen->top_line;
+
+ while (mp.row && ctmpa != NULL) {
+ --mp.row;
+ ctmpa = ctmpa->next;
+ }
+
+ if (ctmpa != NULL && !(ctmpa->flags & CF_NOSELECT)){
+ if(screen->current->flags & CF_DOUBLEVAR)
+ screen->current->flags &= ~CF_VAR2;
+
+ screen->current = ctmpa;
+
+ if(screen->current->flags & CF_DOUBLEVAR &&
+ mp.col >= screen->current->val2offset)
+ screen->current->flags |= CF_VAR2;
+
+ update_option_screen(ps, screen, &cursor_pos);
+
+ if(mp.button == M_BUTTON_LEFT && mp.doubleclick){
+
+ if(screen->current->tool){
+ unsigned flags;
+ int default_cmd;
+
+ flags = screen->current->flags;
+ flags |= (changes ? CF_CHANGES : 0);
+
+ default_cmd = menu_command(ctrl('M'), km);
+ switch(i=(*screen->current->tool)(ps, default_cmd,
+ &screen->current, flags)){
+ case -1:
+ case 0:
+ break;
+
+ case 1:
+ changes = 1;
+ break;
+
+ case 2:
+ retval = changes;
+ done++;
+ break;
+
+ case 3:
+ retval = 1;
+ done++;
+ break;
+
+ default:
+ retval = i;
+ done++;
+ break;
+ }
+ }
+ }
+#ifdef _WINDOWS
+ else if(mp.button == M_BUTTON_RIGHT) {
+ MPopup other_popup[20];
+ int n = -1, cmd, i;
+ struct key_menu *sckm = screen->current->keymenu; /* only for popup */
+
+ if((cmd = menu_command(ctrl('M'), sckm)) != MC_UNKNOWN){
+ i = menu_binding_index(sckm, cmd);
+ other_popup[++n].type = tQueue;
+ other_popup[n].label.style = lNormal;
+ other_popup[n].label.string = sckm->keys[i].label;
+ other_popup[n].data.val = ctrl('M');
+ }
+ else if((cmd = menu_command('>', sckm)) != MC_UNKNOWN){
+ i = menu_binding_index(sckm, cmd);
+ other_popup[++n].type = tQueue;
+ other_popup[n].label.style = lNormal;
+ other_popup[n].label.string = sckm->keys[i].label;
+ other_popup[n].data.val = '>';
+ }
+
+ if(((i = menu_binding_index(sckm, MC_RGB1)) >= 0) ||
+ ((i = menu_binding_index(sckm, MC_RGB2)) >= 0)){
+ other_popup[++n].type = tQueue;
+ other_popup[n].label.style = lNormal;
+ other_popup[n].label.string = sckm->keys[i].label;
+ other_popup[n].data.val =
+ sckm->keys[i].bind.ch[0];
+ }
+
+ if((cmd = menu_command('<', sckm)) != MC_UNKNOWN){
+ i = menu_binding_index(sckm, cmd);
+ other_popup[++n].type = tQueue;
+ other_popup[n].label.style = lNormal;
+ other_popup[n].label.string = sckm->keys[i].label;
+ other_popup[n].data.val = '<';
+ }
+ else if((i = menu_binding_index(sckm, MC_EXIT)) >= 0){
+ other_popup[++n].type = tQueue;
+ other_popup[n].label.style = lNormal;
+ other_popup[n].label.string = sckm->keys[i].label;
+ other_popup[n].data.val =
+ sckm->keys[i].bind.ch[0];
+ }
+
+ if((i = menu_binding_index(sckm, MC_HELP)) >= 0){
+ if(n > 0)
+ other_popup[++n].type = tSeparator;
+
+ other_popup[++n].type = tQueue;
+ other_popup[n].label.style = lNormal;
+ other_popup[n].label.string = sckm->keys[i].label;
+ other_popup[n].data.val = sckm->keys[i].bind.ch[0];
+ }
+
+ if(n > 0){
+ other_popup[++n].type = tTail;
+ mswin_popup(other_popup);
+ }
+ }
+ }
+ else if(mp.button == M_BUTTON_RIGHT) {
+ MPopup other_popup[20];
+ int n = -1, cmd, i;
+ struct key_menu *sckm = screen->current->keymenu; /* only for popup */
+
+ if((cmd = menu_command('<', sckm)) != MC_UNKNOWN){
+ i = menu_binding_index(sckm, cmd);
+ other_popup[++n].type = tQueue;
+ other_popup[n].label.style = lNormal;
+ other_popup[n].label.string = sckm->keys[i].label;
+ other_popup[n].data.val = '<';
+ }
+ else if((i = menu_binding_index(sckm, MC_EXIT)) >= 0){
+ other_popup[++n].type = tQueue;
+ other_popup[n].label.style = lNormal;
+ other_popup[n].label.string = sckm->keys[i].label;
+ other_popup[n].data.val = sckm->keys[i].bind.ch[0];
+ }
+
+ other_popup[++n].type = tTail;
+
+ if(n > 0)
+ mswin_popup(other_popup);
+#endif
+ }
+ }
+ break;
+#endif
+
+ case MC_PRINTTXT: /* print screen */
+ print_option_screen(screen, pdesc ? pdesc : "");
+ break;
+
+ case MC_WHEREIS: /* whereis */
+ /*--- get string ---*/
+ {int rc, found = 0;
+#define FOUND_IT 0x01
+#define FOUND_CURRENT 0x02
+#define FOUND_WRAPPED 0x04
+#define FOUND_NOSELECT 0x08
+#define FOUND_ABOVE 0x10
+ char *result = NULL, buf[64];
+ char *p, last[64];
+ static HISTORY_S *history = NULL;
+ HelpType help;
+ static ESCKEY_S ekey[] = {
+ {0, 0, "", ""},
+ /* TRANSLATORS: go to Top of screen */
+ {ctrl('Y'), 10, "^Y", N_("Top")},
+ {ctrl('V'), 11, "^V", N_("Bottom")},
+ {KEY_UP, 30, "", ""},
+ {KEY_DOWN, 31, "", ""},
+ {-1, 0, NULL, NULL}};
+#define KU_WI (3) /* index of KEY_UP */
+
+ init_hist(&history, HISTSIZE);
+ last[0] = '\0';
+ if((p = get_prev_hist(history, "", 0, NULL)) != NULL){
+ strncpy(last, p, sizeof(last));
+ last[sizeof(last)-1] = '\0';
+ }
+
+ ps->mangled_footer = 1;
+ buf[0] = '\0';
+ snprintf(tmp, sizeof(tmp), "Word to find %s%s%s: ",
+ (last[0]) ? "[" : "",
+ (last[0]) ? last : "",
+ (last[0]) ? "]" : "");
+ tmp[sizeof(tmp)-1] = '\0';
+ help = NO_HELP;
+ while(1){
+ int flags = OE_APPEND_CURRENT;
+
+ /*
+ * 2 is really 1 because there will be one real entry and
+ * one entry of "" because of the get_prev_hist above.
+ */
+ if(items_in_hist(history) > 2){
+ ekey[KU_WI].name = HISTORY_UP_KEYNAME;
+ ekey[KU_WI].label = HISTORY_KEYLABEL;
+ ekey[KU_WI+1].name = HISTORY_DOWN_KEYNAME;
+ ekey[KU_WI+1].label = HISTORY_KEYLABEL;
+ }
+ else{
+ ekey[KU_WI].name = "";
+ ekey[KU_WI].label = "";
+ ekey[KU_WI+1].name = "";
+ ekey[KU_WI+1].label = "";
+ }
+
+ rc = optionally_enter(buf,-FOOTER_ROWS(ps),0,sizeof(buf),
+ tmp,ekey,help,&flags);
+ if(rc == 3)
+ help = help == NO_HELP ? h_config_whereis : NO_HELP;
+ else if(rc == 30){
+ if((p = get_prev_hist(history, buf, 0, NULL)) != NULL){
+ strncpy(buf, p, sizeof(buf));
+ buf[sizeof(buf)-1] = '\0';
+ }
+ else
+ Writechar(BELL, 0);
+
+ continue;
+ }
+ else if(rc == 31){
+ if((p = get_next_hist(history, buf, 0, NULL)) != NULL){
+ strncpy(buf, p, sizeof(buf));
+ buf[sizeof(buf)-1] = '\0';
+ }
+ else
+ Writechar(BELL, 0);
+
+ continue;
+ }
+ else if(rc == 0 || rc == 1 || rc == 10 || rc == 11 || !buf[0]){
+ if(rc == 0 && !buf[0] && last[0])
+ strncpy(buf, last, 64);
+
+ break;
+ }
+ }
+
+ screen->current->flags &= ~CF_VAR2;
+ if(rc == 0 && buf[0]){
+ CONF_S *started_here;
+
+ save_hist(history, buf, 0, NULL);
+
+ ch = KEY_DOWN;
+ ctmpa = screen->current;
+ /*
+ * Skip over the unselectable lines of this "item"
+ * before starting search so that we don't find the
+ * same one again.
+ */
+ while((ctmpb = next_confline(ctmpa)) &&
+ (ctmpb->flags & CF_NOSELECT) &&
+ !(ctmpb->flags & CF_STARTITEM))
+ ctmpa = ctmpb;
+
+ started_here = next_confline(ctmpa);
+ while((ctmpa = next_confline(ctmpa)) != NULL)
+ if(srchstr(ctmpa->varname, buf)
+ || srchstr(ctmpa->value, buf)){
+
+ found = FOUND_IT;
+ /*
+ * If this line is not selectable, back up to the
+ * previous selectable line, but not past the
+ * start of this "entry".
+ */
+ if(ctmpa->flags & CF_NOSELECT)
+ found |= FOUND_NOSELECT;
+
+ while((ctmpa->flags & CF_NOSELECT) &&
+ !(ctmpa->flags & CF_STARTITEM) &&
+ (ctmpb = prev_confline(ctmpa)))
+ ctmpa = ctmpb;
+
+ /*
+ * If that isn't selectable, better search forward
+ * for something that is.
+ */
+ while((ctmpa->flags & CF_NOSELECT) &&
+ (ctmpb = next_confline(ctmpa))){
+ ctmpa = ctmpb;
+ found |= FOUND_ABOVE;
+ }
+
+ /*
+ * If that still isn't selectable, better search
+ * backwards for something that is.
+ */
+ while((ctmpa->flags & CF_NOSELECT) &&
+ (ctmpb = prev_confline(ctmpa))){
+ ctmpa = ctmpb;
+ found &= ~FOUND_ABOVE;
+ }
+
+ break;
+ }
+
+ if(!found){
+ found = FOUND_WRAPPED;
+ ctmpa = first_confline(screen->current);
+
+ while(ctmpa != started_here)
+ if(srchstr(ctmpa->varname, buf)
+ || srchstr(ctmpa->value, buf)){
+
+ found |= FOUND_IT;
+ if(ctmpa->flags & CF_NOSELECT)
+ found |= FOUND_NOSELECT;
+
+ while((ctmpa->flags & CF_NOSELECT) &&
+ !(ctmpa->flags & CF_STARTITEM) &&
+ (ctmpb = prev_confline(ctmpa)))
+ ctmpa = ctmpb;
+
+ while((ctmpa->flags & CF_NOSELECT) &&
+ (ctmpb = next_confline(ctmpa))){
+ ctmpa = ctmpb;
+ found |= FOUND_ABOVE;
+ }
+
+ if(ctmpa == screen->current)
+ found |= FOUND_CURRENT;
+
+ break;
+ }
+ else
+ ctmpa = next_confline(ctmpa);
+ }
+ }
+ else if(rc == 10){
+ screen->current = first_confline(screen->current);
+ if(screen->current && screen->current->flags & CF_NOSELECT){
+ for(ctmpa = next_confline(screen->current);
+ ctmpa && (ctmpa->flags & CF_NOSELECT);
+ ctmpa = next_confline(ctmpa))
+ ;
+
+ if(ctmpa)
+ screen->current = ctmpa;
+ }
+
+ /* TRANSLATORS: Searched to ... is the result of the search, searched
+ to top means the search went past the bottom of the screen and
+ wrapped back around to the top. */
+ result = _("Searched to top");
+ }
+ else if(rc == 11){
+ screen->current = last_confline(screen->current);
+ if(screen->current && screen->current->flags & CF_NOSELECT){
+ for(ctmpa = prev_confline(screen->current);
+ ctmpa && (ctmpa->flags & CF_NOSELECT);
+ ctmpa = prev_confline(ctmpa))
+ ;
+
+ if(ctmpa)
+ screen->current = ctmpa;
+ }
+
+ result = _("Searched to bottom");
+ }
+ else
+ result = _("WhereIs cancelled");
+
+ if((found & FOUND_IT) && ctmpa){
+ strncpy(last, buf, 64);
+ result =
+ (found & FOUND_CURRENT && found & FOUND_WRAPPED && found & FOUND_NOSELECT)
+ ? _("Current item contains the only match")
+ : (found & FOUND_CURRENT && found & FOUND_WRAPPED)
+ ? _("Current line contains the only match")
+ : (found & FOUND_NOSELECT && found & FOUND_WRAPPED)
+ ? ((found & FOUND_ABOVE)
+ ? _("Search wrapped: word found in text above current line")
+ : _("Search wrapped: word found in text below current line"))
+ : (found & FOUND_WRAPPED)
+ ? _("Search wrapped to beginning: word found")
+ : (found & FOUND_NOSELECT)
+ ? ((found & FOUND_ABOVE)
+ ? _("Word found in text above current line")
+ : _("Word found in text below current line"))
+ : _("Word found");
+ screen->current = ctmpa;
+ }
+
+ q_status_message(SM_ORDER,0,3,result ? result : _("Word not found"));
+ }
+
+ break;
+
+ case MC_HOMEKEY:
+ screen->current = first_confline(screen->current);
+ if(screen->current && screen->current->flags & CF_NOSELECT){
+ for(ctmpa = next_confline(screen->current);
+ ctmpa && (ctmpa->flags & CF_NOSELECT);
+ ctmpa = next_confline(ctmpa))
+ ;
+
+ if(ctmpa)
+ screen->current = ctmpa;
+ }
+
+ q_status_message(SM_ORDER,0,3, _("Moved to top"));
+ break;
+
+ case MC_ENDKEY:
+ screen->current = last_confline(screen->current);
+ if(screen->current && screen->current->flags & CF_NOSELECT){
+ for(ctmpa = prev_confline(screen->current);
+ ctmpa && (ctmpa->flags & CF_NOSELECT);
+ ctmpa = prev_confline(ctmpa))
+ ;
+
+ if(ctmpa)
+ screen->current = ctmpa;
+ }
+
+ q_status_message(SM_ORDER,0,3, _("Moved to bottom"));
+ break;
+
+ case MC_REPAINT: /* redraw the display */
+ case MC_RESIZE:
+ ClearScreen();
+ ps->mangled_screen = 1;
+ break;
+
+ default:
+ if(screen && screen->ro_warning){
+ if(cmd == MC_EXIT){
+ retval = 0;
+ done++;
+ }
+ else
+ q_status_message1(SM_ORDER|SM_DING, 1, 3,
+ _("%s can't change options or settings"),
+ ps_global->restricted ? "Alpine demo"
+ : _("Config file not changeable,"));
+ }
+ else if(screen->current->tool){
+ unsigned flags;
+
+ flags = screen->current->flags;
+ flags |= (changes ? CF_CHANGES : 0);
+
+ switch(i=(*screen->current->tool)(ps, cmd,
+ &screen->current, flags)){
+ case -1:
+ q_status_message2(SM_ORDER, 0, 2,
+ /* TRANSLATORS: Command <command letter> not defined here.
+ Leave the trailing %s which might be a parenthetical
+ remark. */
+ _("Command \"%s\" not defined here.%s"),
+ pretty_command(ch),
+ F_ON(F_BLANK_KEYMENU,ps) ? "" : " See key menu below.");
+ break;
+
+ case 0:
+ break;
+
+ case 1:
+ changes = 1;
+ break;
+
+ case 2:
+ retval = changes;
+ done++;
+ break;
+
+ case 3:
+ retval = 1;
+ done++;
+ break;
+
+ default:
+ retval = i;
+ done++;
+ break;
+ }
+ }
+
+ break;
+
+ case MC_UTF8:
+ bogus_utf8_command(utf8str, "?");
+ break;
+
+ case MC_NONE: /* simple timeout */
+ break;
+ }
+ }
+
+ screen->current = first_confline(screen->current);
+ free_conflines(&screen->current);
+ return(retval);
+}
+
+
+/*
+ *
+ */
+void
+config_scroll_up(long int n)
+{
+ CONF_S *ctmp = opt_screen->top_line;
+ int cur_found = 0;
+
+ if(n < 0)
+ config_scroll_down(-n);
+ else if(n){
+ for(; n>0 && ctmp->next; n--){
+ ctmp = next_confline(ctmp);
+ if(prev_confline(ctmp) == opt_screen->current)
+ cur_found++;
+ }
+
+ opt_screen->top_line = ctmp;
+ ps_global->mangled_body = 1;
+ if(cur_found){
+ for(ctmp = opt_screen->top_line;
+ ctmp && (ctmp->flags & CF_NOSELECT);
+ ctmp = next_confline(ctmp))
+ ;
+
+ if(ctmp)
+ opt_screen->current = opt_screen->prev = ctmp;
+ else {
+ while(opt_screen->top_line->flags & CF_NOSELECT)
+ opt_screen->top_line = prev_confline(opt_screen->top_line);
+ opt_screen->current = opt_screen->prev = opt_screen->top_line;
+ }
+ }
+ }
+}
+
+
+/*
+ * config_scroll_down -
+ */
+void
+config_scroll_down(long int n)
+{
+ CONF_S *ctmp = opt_screen->top_line, *last_sel = NULL;
+ int i;
+
+ if(n < 0)
+ config_scroll_up(-n);
+ else if(n){
+ for(; n>0 && ctmp->prev; n--)
+ ctmp = prev_confline(ctmp);
+
+ opt_screen->top_line = ctmp;
+ ps_global->mangled_body = 1;
+ for(ctmp = opt_screen->top_line, i = BODY_LINES(ps_global);
+ i > 0 && ctmp && ctmp != opt_screen->current;
+ ctmp = next_confline(ctmp), i--)
+ if(!(ctmp->flags & CF_NOSELECT))
+ last_sel = ctmp;
+
+ if(!i && last_sel)
+ opt_screen->current = opt_screen->prev = last_sel;
+ }
+}
+
+
+/*
+ * config_scroll_to_pos -
+ */
+void
+config_scroll_to_pos(long int n)
+{
+ CONF_S *ctmp;
+
+ for(ctmp = first_confline(opt_screen->current);
+ n && ctmp && ctmp != opt_screen->top_line;
+ ctmp = next_confline(ctmp), n--)
+ ;
+
+ if(n == 0)
+ while(ctmp && ctmp != opt_screen->top_line)
+ if((ctmp = next_confline(ctmp)) != NULL)
+ n--;
+
+ config_scroll_up(n);
+}
+
+
+/*
+ * config_top_scroll - return pointer to the
+ */
+CONF_S *
+config_top_scroll(struct pine *ps, CONF_S *topline)
+{
+ int i;
+ CONF_S *ctmp;
+
+ for(ctmp = topline, i = HS_MARGIN(ps);
+ ctmp && i;
+ ctmp = next_confline(ctmp), i--)
+ ;
+
+ return(ctmp ? ctmp : topline);
+}
+
+
+int
+text_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ return(text_toolit(ps, cmd, cl, flags, 0));
+}
+
+
+/*
+ * simple text variable handler
+ *
+ * note, things get a little involved due to the
+ * screen struct <--> variable mapping. (but, once its
+ * running it shouldn't need changing ;).
+ *
+ * look_for_backslash == 1 means that backslash is an escape character.
+ * In particular, \, can be used to put a literal comma
+ * into a value. The value will still have the backslash
+ * in it, but the comma after the backslash won't be treated
+ * as an item separator.
+ *
+ * returns: -1 on unrecognized cmd, 0 if no change, 1 if change
+ * returns what conf_exit_cmd returns for exit command.
+ */
+int
+text_toolit(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags, int look_for_backslash)
+{
+ char prompt[81], *sval, *tmp, *swap_val, **newval = NULL;
+ char *pval, **apval, **lval, ***alval;
+ char *olddefval = NULL;
+ int rv = 0, skip_to_next = 0, after = 0, i = 4, j, k;
+ int lowrange, hirange, incr, oeflags, oebufsize;
+ int numval, repeat_key = 0;
+ int curindex, previndex, nextindex, deefault;
+ HelpType help;
+ ESCKEY_S ekey[6];
+
+ if((*cl)->var->is_list){
+ lval = LVAL((*cl)->var, ew);
+ alval = ALVAL((*cl)->var, ew);
+ }
+ else{
+ pval = PVAL((*cl)->var, ew);
+ apval = APVAL((*cl)->var, ew);
+ }
+
+ oebufsize = 6*MAXPATH;
+ sval = (char *) fs_get(oebufsize*sizeof(char));
+ sval[0] = '\0';
+
+ if(flags&CF_NUMBER){ /* only happens if !is_list */
+ incr = 1;
+ if((*cl)->var == &ps->vars[V_FILLCOL]){
+ lowrange = 1;
+ hirange = MAX_FILLCOL;
+ }
+ else if((*cl)->var == &ps->vars[V_OVERLAP]
+ || (*cl)->var == &ps->vars[V_MARGIN]){
+ lowrange = 0;
+ hirange = 20;
+ }
+ else if((*cl)->var == &ps->vars[V_QUOTE_SUPPRESSION]){
+ lowrange = -(Q_SUPP_LIMIT-1);
+ hirange = 1000;
+ }
+ else if((*cl)->var == &ps->vars[V_MAXREMSTREAM]){
+ lowrange = 0;
+ hirange = 15;
+ }
+ else if((*cl)->var == &ps->vars[V_STATUS_MSG_DELAY]){
+ lowrange = -10;
+ hirange = 30;
+ }
+ else if((*cl)->var == &ps->vars[V_ACTIVE_MSG_INTERVAL]){
+ lowrange = 0;
+ hirange = 20;
+ }
+ else if((*cl)->var == &ps->vars[V_MAILCHECK] ||
+ (*cl)->var == &ps->vars[V_INCCHECKINTERVAL] ||
+ (*cl)->var == &ps->vars[V_INC2NDCHECKINTERVAL] ||
+ (*cl)->var == &ps->vars[V_MAILCHECKNONCURR]){
+ lowrange = 0;
+ hirange = 25000;
+ incr = 15;
+ }
+ else if((*cl)->var == &ps->vars[V_DEADLETS]){
+ lowrange = 0;
+ hirange = 9;
+ }
+ else if((*cl)->var == &ps->vars[V_NMW_WIDTH]){
+ lowrange = 20;
+ hirange = MAX_SCREEN_COLS;
+ }
+ else if((*cl)->var == score_act_global_ptr){
+ lowrange = -100;
+ hirange = 100;
+ }
+ else if((*cl)->var == &ps->vars[V_TCPOPENTIMEO] ||
+ (*cl)->var == &ps->vars[V_TCPREADWARNTIMEO] ||
+ (*cl)->var == &ps->vars[V_TCPQUERYTIMEO]){
+ lowrange = 5;
+ hirange = 1000;
+ }
+ else if((*cl)->var == &ps->vars[V_TCPWRITEWARNTIMEO] ||
+ (*cl)->var == &ps->vars[V_RSHOPENTIMEO] ||
+ (*cl)->var == &ps->vars[V_SSHOPENTIMEO] ||
+ (*cl)->var == &ps->vars[V_USERINPUTTIMEO]){
+ lowrange = 0;
+ hirange = 1000;
+ }
+ else if((*cl)->var == &ps->vars[V_INCCHECKTIMEO]){
+ lowrange = 1;
+ hirange = 1000;
+ }
+ else if((*cl)->var == &ps->vars[V_MAILDROPCHECK]){
+ lowrange = 0;
+ hirange = 1000000;
+ incr = 60;
+ }
+ else if((*cl)->var == &ps->vars[V_NNTPRANGE]){
+ lowrange = 0;
+ hirange = 1000000;
+ incr = 100;
+ }
+ else if((*cl)->var == &ps->vars[V_REMOTE_ABOOK_VALIDITY]){
+ lowrange = -1;
+ hirange = 25000;
+ }
+ else if((*cl)->var == &ps->vars[V_REMOTE_ABOOK_HISTORY]){
+ lowrange = 0;
+ hirange = 100;
+ }
+ else if((*cl)->var == cat_lim_global_ptr){
+ lowrange = -1;
+ hirange = 10000000;
+ }
+ else{
+ lowrange = 0;
+ hirange = 25000;
+ }
+
+ ekey[0].ch = -2;
+ ekey[0].rval = 'x';
+ ekey[0].name = "";
+ ekey[0].label = "";
+ ekey[1].ch = ctrl('P');
+ ekey[1].rval = ctrl('P');
+ ekey[1].name = "^P";
+ ekey[1].label = N_("Decrease");
+ ekey[2].ch = ctrl('N');
+ ekey[2].rval = ctrl('N');
+ ekey[2].name = "^N";
+ ekey[2].label = N_("Increase");
+ ekey[3].ch = KEY_DOWN;
+ ekey[3].rval = ctrl('P');
+ ekey[3].name = "";
+ ekey[3].label = "";
+ ekey[4].ch = KEY_UP;
+ ekey[4].rval = ctrl('N');
+ ekey[4].name = "";
+ ekey[4].label = "";
+ ekey[5].ch = -1;
+ }
+
+ switch(cmd){
+ case MC_ADD: /* add to list */
+ if(fixed_var((*cl)->var, "add to", NULL)){
+ break;
+ }
+ else if(!(*cl)->var->is_list && pval){
+ q_status_message(SM_ORDER, 3, 3,
+ _("Only single value allowed. Use \"Change\"."));
+ }
+ else{
+ int maxwidth;
+ char *p;
+
+ if((*cl)->var->is_list
+ && lval && lval[0] && lval[0][0]
+ && (*cl)->value){
+ char tmpval[101];
+ /* regular add to an existing list */
+
+ strncpy(tmpval, (*cl)->value, sizeof(tmpval));
+ tmpval[sizeof(tmpval)-1] = '\0';
+ removing_trailing_white_space(tmpval);
+
+ /* 33 is the number of chars other than the value */
+ maxwidth = MIN(80, ps->ttyo->screen_cols) - 15;
+ k = MIN(18, MAX(maxwidth-33,0));
+ if(utf8_width(tmpval) > k && k >= 3){
+ (void) utf8_truncate(tmpval, k-3);
+ strncat(tmpval, "...", sizeof(tmpval)-strlen(tmpval)-1);
+ tmpval[sizeof(tmpval)-1] = '\0';
+ }
+
+ utf8_snprintf(prompt, sizeof(prompt),
+ _("Enter text to insert before \"%.*w\": "), k, tmpval);
+ prompt[sizeof(prompt)-1] = '\0';
+ }
+ else if((*cl)->var->is_list
+ && !lval
+ && (*cl)->var->current_val.l){
+ /* Add to list which doesn't exist, but default does exist */
+ ekey[0].ch = 'r';
+ ekey[0].rval = 'r';
+ ekey[0].name = "R";
+ ekey[0].label = N_("Replace");
+ ekey[1].ch = 'a';
+ ekey[1].rval = 'a';
+ ekey[1].name = "A";
+ ekey[1].label = N_("Add To");
+ ekey[2].ch = -1;
+ strncpy(prompt, _("Replace or Add To default value ? "), sizeof(prompt));
+ prompt[sizeof(prompt)-1] = '\0';
+ switch(radio_buttons(prompt, -FOOTER_ROWS(ps), ekey, 'a', 'x',
+ h_config_replace_add, RB_NORM)){
+ case 'a':
+ p = sval;
+ for(j = 0; (*cl)->var->current_val.l[j]; j++){
+ sstrncpy(&p, (*cl)->var->current_val.l[j], oebufsize-(p-sval));
+ if(oebufsize-(p-sval) > 2){
+ *p++ = ',';
+ *p++ = ' ';
+ }
+
+ if(oebufsize-(p-sval) > 0)
+ *p = '\0';
+ }
+
+ sval[oebufsize-1] = '\0';
+
+add_text:
+ if(flags & CF_NUMBER)
+ snprintf(prompt, sizeof(prompt), _("Enter the numeric text to be added : "));
+ else
+ snprintf(prompt, sizeof(prompt), _("Enter the text to be added : "));
+
+ break;
+
+ case 'r':
+replace_text:
+ if(olddefval){
+ strncpy(sval, olddefval, oebufsize);
+ sval[oebufsize-1] = '\0';
+ }
+
+ if(flags & CF_NUMBER)
+ snprintf(prompt, sizeof(prompt), _("Enter the numeric replacement text : "));
+ else
+ snprintf(prompt, sizeof(prompt), _("Enter the replacement text : "));
+
+ break;
+
+ case 'x':
+ i = 1;
+ cmd_cancelled("Add");
+ break;
+ }
+ }
+ else{
+ if(flags & CF_NUMBER)
+ snprintf(prompt, sizeof(prompt), _("Enter the numeric text to be added : "));
+ else
+ snprintf(prompt, sizeof(prompt), _("Enter the text to be added : "));
+ }
+
+ prompt[sizeof(prompt)-1] = '\0';
+
+ ps->mangled_footer = 1;
+
+ if(i == 1)
+ break;
+
+ help = NO_HELP;
+ while(1){
+ if((*cl)->var->is_list
+ && lval && lval[0] && lval[0][0]
+ && (*cl)->value){
+ ekey[0].ch = ctrl('W');
+ ekey[0].rval = 5;
+ ekey[0].name = "^W";
+ /* TRANSLATORS: Insert new item before current item */
+ ekey[0].label = after ? N_("InsertBefore") : N_("InsertAfter");
+ ekey[1].ch = -1;
+ }
+ else if(!(flags&CF_NUMBER))
+ ekey[0].ch = -1;
+
+ oeflags = OE_APPEND_CURRENT;
+ i = optionally_enter(sval, -FOOTER_ROWS(ps), 0, oebufsize,
+ prompt,
+ (ekey[0].ch != -1) ? ekey : NULL,
+ help, &oeflags);
+ if(i == 0){
+ rv = 1;
+ if((*cl)->var->is_list)
+ ps->mangled_body = 1;
+ else
+ ps->mangled_footer = 1;
+
+ removing_leading_and_trailing_white_space(sval);
+ /*
+ * Coerce "" and <Empty Value> to empty string input.
+ * Catch <No Value Set> as a substitute for deleting.
+ */
+ if((*sval == '\"' && *(sval+1) == '\"' && *(sval+2) == '\0')
+ || !struncmp(sval, _(empty_val), strlen(_(empty_val)))
+ || (*sval == '<'
+ && !struncmp(sval+1, _(empty_val), strlen(_(empty_val)))))
+ *sval = '\0';
+ else if(!struncmp(sval, _(no_val), strlen(_(no_val)))
+ || (*sval == '<'
+ && !struncmp(sval+1, _(no_val), strlen(_(no_val)))))
+ goto delete;
+
+ if((*cl)->var->is_list){
+ if(*sval || !lval){
+ char **ltmp;
+ int i;
+
+ i = 0;
+ for(tmp = sval; *tmp; tmp++)
+ if(*tmp == ',')
+ i++; /* conservative count of ,'s */
+
+ if(!i){
+ ltmp = (char **)fs_get(2 * sizeof(char *));
+ ltmp[0] = cpystr(sval);
+ ltmp[1] = NULL;
+ }
+ else
+ ltmp = parse_list(sval, i + 1,
+ look_for_backslash
+ ? PL_COMMAQUOTE : 0,
+ NULL);
+
+ if(ltmp[0]){
+ config_add_list(ps, cl, ltmp, &newval, after);
+ if(after)
+ skip_to_next = 1;
+ }
+ else{
+ q_status_message1(SM_ORDER, 0, 3,
+ _("Can't add %s to list"), _(empty_val));
+ rv = ps->mangled_body = 0;
+ }
+
+ fs_give((void **)&ltmp);
+ }
+ else{
+ q_status_message1(SM_ORDER, 0, 3,
+ _("Can't add %s to list"), _(empty_val));
+ }
+ }
+ else{
+ if(flags&CF_NUMBER && sval[0]
+ && !(isdigit((unsigned char)sval[0])
+ || sval[0] == '-' || sval[0] == '+')){
+ q_status_message(SM_ORDER,3,3,
+ _("Entry must be numeric"));
+ i = 3; /* to keep loop going */
+ continue;
+ }
+
+ if(apval && *apval)
+ fs_give((void **)apval);
+
+ if(!(olddefval && !strcmp(sval, olddefval))
+ || ((*cl)->var == &ps->vars[V_POST_CHAR_SET])
+ || want_to(_("Leave unset and use default "),
+ 'y', 'y', NO_HELP, WT_FLUSH_IN) == 'n')
+ *apval = cpystr(sval);
+
+ newval = &(*cl)->value;
+ }
+ }
+ else if(i == 1){
+ cmd_cancelled("Add");
+ }
+ else if(i == 3){
+ help = help == NO_HELP ? h_config_add : NO_HELP;
+ continue;
+ }
+ else if(i == 4){ /* no redraw, yet */
+ continue;
+ }
+ else if(i == 5){ /* change from/to prepend to/from append */
+ char tmpval[101];
+
+ after = after ? 0 : 1;
+ strncpy(tmpval, (*cl)->value, sizeof(tmpval));
+ tmpval[sizeof(tmpval)-1] = '\0';
+ removing_trailing_white_space(tmpval);
+ /* 33 is the number of chars other than the value */
+ maxwidth = MIN(80, ps->ttyo->screen_cols) - 15;
+ k = MIN(18, MAX(maxwidth-33,0));
+ if(utf8_width(tmpval) > k && k >= 3){
+ (void) utf8_truncate(tmpval, k-3);
+ strncat(tmpval, "...", sizeof(tmpval)-strlen(tmpval)-1);
+ tmpval[sizeof(tmpval)-1] = '\0';
+ }
+
+ if(after)
+ snprintf(prompt, sizeof(prompt), _("Enter text to insert after \"%.*s\": "), k, tmpval);
+ else
+ snprintf(prompt, sizeof(prompt), _("Enter text to insert before \"%.*s\": "), k, tmpval);
+
+ continue;
+ }
+ else if(i == ctrl('P')){
+ if(sval[0])
+ numval = atoi(sval);
+ else{
+ if(pval)
+ numval = atoi(pval);
+ else
+ numval = lowrange + 1;
+ }
+
+ if(numval == lowrange){
+ /*
+ * Protect user from repeating arrow key that
+ * causes message to appear over and over.
+ */
+ if(++repeat_key > 0){
+ q_status_message1(SM_ORDER,3,3,
+ _("Minimum value is %s"), comatose(lowrange));
+ repeat_key = -5;
+ }
+ }
+ else
+ repeat_key = 0;
+
+ numval = MAX(numval - incr, lowrange);
+ snprintf(sval, oebufsize, "%d", numval);
+ sval[oebufsize-1] = '\0';
+ continue;
+ }
+ else if(i == ctrl('N')){
+ if(sval[0])
+ numval = atoi(sval);
+ else{
+ if(pval)
+ numval = atoi(pval);
+ else
+ numval = lowrange + 1;
+ }
+
+ if(numval == hirange){
+ if(++repeat_key > 0){
+ q_status_message1(SM_ORDER,3,3,
+ _("Maximum value is %s"), comatose(hirange));
+ repeat_key = -5;
+ }
+ }
+ else
+ repeat_key = 0;
+
+ numval = MIN(numval + incr, hirange);
+ snprintf(sval, oebufsize, "%d", numval);
+ sval[oebufsize-1] = '\0';
+ continue;
+ }
+
+ break;
+ }
+ }
+
+ break;
+
+ case MC_DELETE: /* delete */
+delete:
+ if(!(*cl)->var->is_list
+ && apval && !*apval
+ && (*cl)->var->current_val.p){
+ char pmt[80];
+
+ snprintf(pmt, sizeof(pmt), _("Override default with %s"), _(empty_val2));
+ pmt[sizeof(pmt)-1] = '\0';
+ if(want_to(pmt, 'n', 'n', NO_HELP, WT_FLUSH_IN) == 'y'){
+ sval[0] = '\0';
+ *apval = cpystr(sval);
+ newval = &(*cl)->value;
+ rv = ps->mangled_footer = 1;
+ }
+ }
+ else if((*cl)->var->is_list
+ && alval && !lval
+ && (*cl)->var->current_val.l){
+ char pmt[80];
+
+ snprintf(pmt, sizeof(pmt), _("Override default with %s"), _(empty_val2));
+ pmt[sizeof(pmt)-1] = '\0';
+ if(want_to(pmt, 'n', 'n', NO_HELP, WT_FLUSH_IN) == 'y'){
+ char **ltmp;
+
+ sval[0] = '\0';
+ ltmp = (char **)fs_get(2 * sizeof(char *));
+ ltmp[0] = cpystr(sval);
+ ltmp[1] = NULL;
+ config_add_list(ps, cl, ltmp, &newval, 0);
+ fs_give((void **)&ltmp);
+ rv = ps->mangled_body = 1;
+ }
+ }
+ else if(((*cl)->var->is_list && !lval)
+ || (!(*cl)->var->is_list && !pval)){
+ q_status_message(SM_ORDER, 0, 3, _("No set value to delete"));
+ }
+ else{
+ if((*cl)->var->is_fixed)
+ snprintf(prompt, sizeof(prompt), _("Delete (unused) %s from %s "),
+ (*cl)->var->is_list
+ ? (!*lval[(*cl)->varmem])
+ ? _(empty_val2)
+ : lval[(*cl)->varmem]
+ : (pval)
+ ? (!*pval)
+ ? _(empty_val2)
+ : pval
+ : "<NULL VALUE>",
+ (*cl)->var->name);
+ else
+ snprintf(prompt, sizeof(prompt), _("Really delete %s%s from %s "),
+ (*cl)->var->is_list ? "item " : "",
+ (*cl)->var->is_list
+ ? int2string((*cl)->varmem + 1)
+ : (pval)
+ ? (!*pval)
+ ? _(empty_val2)
+ : pval
+ : "<NULL VALUE>",
+ (*cl)->var->name);
+
+ prompt[sizeof(prompt)-1] = '\0';
+
+
+ ps->mangled_footer = 1;
+ if(want_to(prompt, 'n', 'n', NO_HELP, WT_FLUSH_IN) == 'y'){
+ rv = 1;
+ if((*cl)->var->is_list)
+ ps->mangled_body = 1;
+ else
+ ps->mangled_footer = 1;
+
+ if((*cl)->var->is_list){
+ if(lval[(*cl)->varmem])
+ fs_give((void **)&lval[(*cl)->varmem]);
+
+ config_del_list_item(cl, &newval);
+ }
+ else{
+ if(apval && *apval)
+ fs_give((void **)apval);
+
+ newval = &(*cl)->value;
+ }
+ }
+ else
+ q_status_message(SM_ORDER, 0, 3, _("Value not deleted"));
+ }
+
+ break;
+
+ case MC_EDIT: /* edit/change list option */
+ if(fixed_var((*cl)->var, NULL, NULL)){
+ break;
+ }
+ else if(((*cl)->var->is_list
+ && !lval
+ && (*cl)->var->current_val.l)
+ ||
+ (!(*cl)->var->is_list
+ && !pval
+ && (*cl)->var->current_val.p)){
+
+ /*
+ * In non-list case, offer default value for editing.
+ */
+ if(!(*cl)->var->is_list
+ && (*cl)->var != &ps->vars[V_REPLY_INTRO]
+ && (*cl)->var->current_val.p[0]
+ && strcmp(VSTRING,(*cl)->var->current_val.p)){
+ int quote_it;
+ size_t len;
+
+ olddefval = (char *) fs_get(strlen((*cl)->var->current_val.p)+3);
+
+ if(!strncmp((*cl)->var->current_val.p,
+ DSTRING,
+ (len=strlen(DSTRING)))){
+ /* strip DSTRING and trailing paren */
+ strncpy(olddefval, (*cl)->var->current_val.p+len,
+ strlen((*cl)->var->current_val.p)-len-1);
+ olddefval[strlen((*cl)->var->current_val.p)-len-1] = '\0';
+ }
+ else{
+ /* quote it if there are trailing spaces */
+ quote_it = ((*cl)->var->current_val.p[strlen((*cl)->var->current_val.p)-1] == SPACE);
+ snprintf(olddefval, strlen((*cl)->var->current_val.p)+3, "%s%s%s", quote_it ? "\"" : "", (*cl)->var->current_val.p, quote_it ? "\"" : "");
+ }
+
+ olddefval[strlen((*cl)->var->current_val.p)+3-1] = '\0';
+ }
+
+ goto replace_text;
+ }
+ else if(((*cl)->var->is_list
+ && !lval
+ && !(*cl)->var->current_val.l)
+ ||
+ (!(*cl)->var->is_list
+ && !pval
+ && !(*cl)->var->current_val.p)){
+ goto add_text;
+ }
+ else{
+ HelpType help;
+ char *clptr;
+
+ if(sval)
+ fs_give((void **)&sval);
+ if((*cl)->var->is_list){
+ snprintf(prompt, sizeof(prompt), _("Change field %s list entry : "),
+ (*cl)->var->name);
+ prompt[sizeof(prompt)-1] = '\0';
+ clptr = lval[(*cl)->varmem] ? lval[(*cl)->varmem] : NULL;
+ }
+ else{
+ if(flags & CF_NUMBER)
+ snprintf(prompt, sizeof(prompt), _("Change numeric field %s value : "), (*cl)->var->name);
+ else
+ snprintf(prompt, sizeof(prompt), _("Change field %s value : "), (*cl)->var->name);
+
+ clptr = pval ? pval : NULL;
+ }
+
+ oebufsize = clptr ? (int) MAX(MAXPATH, 50+strlen(clptr)) : MAXPATH;
+ sval = (char *) fs_get(oebufsize * sizeof(char));
+ snprintf(sval, oebufsize, "%s", clptr ? clptr : "");
+ sval[oebufsize-1] = '\0';
+
+ ps->mangled_footer = 1;
+ help = NO_HELP;
+ while(1){
+ if(!(flags&CF_NUMBER))
+ ekey[0].ch = -1;
+
+ oeflags = OE_APPEND_CURRENT;
+ i = optionally_enter(sval, -FOOTER_ROWS(ps), 0, oebufsize,
+ prompt,
+ (ekey[0].ch != -1) ? ekey : NULL,
+ help, &oeflags);
+ if(i == 0){
+ removing_leading_and_trailing_white_space(sval);
+ /*
+ * Coerce "" and <Empty Value> to empty string input.
+ * Catch <No Value Set> as a substitute for deleting.
+ */
+ if((*sval == '\"' && *(sval+1) == '\"' && *(sval+2) == '\0')
+ || !struncmp(sval, _(empty_val), strlen(_(empty_val)))
+ || (*sval == '<'
+ && !struncmp(sval+1, _(empty_val), strlen(_(empty_val)))))
+ *sval = '\0';
+ else if(!struncmp(sval, _(no_val), strlen(_(no_val)))
+ || (*sval == '<'
+ && !struncmp(sval+1, _(no_val), strlen(_(no_val)))))
+ goto delete;
+
+ rv = 1;
+ if((*cl)->var->is_list)
+ ps->mangled_body = 1;
+ else
+ ps->mangled_footer = 1;
+
+ if((*cl)->var->is_list){
+ char **ltmp = NULL;
+ int i;
+
+ if(lval[(*cl)->varmem])
+ fs_give((void **)&lval[(*cl)->varmem]);
+
+ i = 0;
+ for(tmp = sval; *tmp; tmp++)
+ if(*tmp == ',')
+ i++; /* conservative count of ,'s */
+
+ if(i)
+ ltmp = parse_list(sval, i + 1,
+ look_for_backslash
+ ? PL_COMMAQUOTE : 0,
+ NULL);
+
+ if(ltmp && !ltmp[0]) /* only commas */
+ goto delete;
+ else if(!i || (ltmp && !ltmp[1])){ /* only one item */
+ lval[(*cl)->varmem] = cpystr(sval);
+ newval = &(*cl)->value;
+
+ if(ltmp && ltmp[0])
+ fs_give((void **)&ltmp[0]);
+ }
+ else if(ltmp){
+ /*
+ * Looks like the value was changed to a
+ * list, so delete old value, and insert
+ * new list...
+ *
+ * If more than one item in existing list and
+ * current is end of existing list, then we
+ * have to delete and append instead of
+ * deleting and prepending.
+ */
+ if(((*cl)->varmem > 0 || lval[1])
+ && !(lval[(*cl)->varmem+1])){
+ after = 1;
+ skip_to_next = 1;
+ }
+
+ config_del_list_item(cl, &newval);
+ config_add_list(ps, cl, ltmp, &newval, after);
+ }
+
+ if(ltmp)
+ fs_give((void **)&ltmp);
+ }
+ else{
+ if(flags&CF_NUMBER && sval[0]
+ && !(isdigit((unsigned char)sval[0])
+ || sval[0] == '-' || sval[0] == '+')){
+ q_status_message(SM_ORDER,3,3,
+ _("Entry must be numeric"));
+ continue;
+ }
+
+ if(apval && *apval)
+ fs_give((void **)apval);
+
+ if(sval[0] && apval)
+ *apval = cpystr(sval);
+
+ newval = &(*cl)->value;
+ }
+ }
+ else if(i == 1){
+ cmd_cancelled("Change");
+ }
+ else if(i == 3){
+ help = help == NO_HELP ? h_config_change : NO_HELP;
+ continue;
+ }
+ else if(i == 4){ /* no redraw, yet */
+ continue;
+ }
+ else if(i == ctrl('P')){
+ numval = atoi(sval);
+ if(numval == lowrange){
+ /*
+ * Protect user from repeating arrow key that
+ * causes message to appear over and over.
+ */
+ if(++repeat_key > 0){
+ q_status_message1(SM_ORDER,3,3,
+ _("Minimum value is %s"), comatose(lowrange));
+ repeat_key = -5;
+ }
+ }
+ else
+ repeat_key = 0;
+
+ numval = MAX(numval - incr, lowrange);
+ snprintf(sval, oebufsize, "%d", numval);
+ sval[oebufsize-1] = '\0';
+ continue;
+ }
+ else if(i == ctrl('N')){
+ numval = atoi(sval);
+ if(numval == hirange){
+ if(++repeat_key > 0){
+ q_status_message1(SM_ORDER,3,3,
+ _("Maximum value is %s"), comatose(hirange));
+ repeat_key = -5;
+ }
+ }
+ else
+ repeat_key = 0;
+
+ numval = MIN(numval + incr, hirange);
+ snprintf(sval, oebufsize, "%d", numval);
+ sval[oebufsize-1] = '\0';
+ continue;
+ }
+
+ break;
+ }
+ }
+
+ break;
+
+ case MC_SHUFFLE:
+ if(!((*cl)->var && (*cl)->var->is_list)){
+ q_status_message(SM_ORDER, 0, 2,
+ _("Can't shuffle single-valued setting"));
+ break;
+ }
+
+ if(!alval)
+ break;
+
+ curindex = (*cl)->varmem;
+ previndex = curindex-1;
+ nextindex = curindex+1;
+ if(!*alval || !(*alval)[nextindex])
+ nextindex = -1;
+
+ if((previndex < 0 && nextindex < 0) || !*alval){
+ q_status_message(SM_ORDER, 0, 3,
+ _("Shuffle only makes sense when there is more than one value defined"));
+ break;
+ }
+
+ /* Move it up or down? */
+ i = 0;
+ ekey[i].ch = 'u';
+ ekey[i].rval = 'u';
+ ekey[i].name = "U";
+ ekey[i++].label = N_("Up");
+
+ ekey[i].ch = 'd';
+ ekey[i].rval = 'd';
+ ekey[i].name = "D";
+ ekey[i++].label = N_("Down");
+
+ ekey[i].ch = -1;
+ deefault = 'u';
+
+ if(previndex < 0){ /* no up */
+ ekey[0].ch = -2;
+ deefault = 'd';
+ }
+ else if(nextindex < 0)
+ ekey[1].ch = -2; /* no down */
+
+ snprintf(prompt, sizeof(prompt), "Shuffle %s%s%s ? ",
+ (ekey[0].ch != -2) ? "UP" : "",
+ (ekey[0].ch != -2 && ekey[1].ch != -2) ? " or " : "",
+ (ekey[1].ch != -2) ? "DOWN" : "");
+ help = (ekey[0].ch == -2) ? h_hdrcolor_shuf_down
+ : (ekey[1].ch == -2) ? h_hdrcolor_shuf_up
+ : h_hdrcolor_shuf;
+ prompt[sizeof(prompt)-1] = '\0';
+
+ i = radio_buttons(prompt, -FOOTER_ROWS(ps), ekey, deefault, 'x',
+ help, RB_NORM);
+
+ switch(i){
+ case 'x':
+ cmd_cancelled("Shuffle");
+ return(rv);
+
+ case 'u':
+ case 'd':
+ break;
+ }
+
+ /* swap order */
+ if(i == 'd'){
+ swap_val = (*alval)[curindex];
+ (*alval)[curindex] = (*alval)[nextindex];
+ (*alval)[nextindex] = swap_val;
+ }
+ else if(i == 'u'){
+ swap_val = (*alval)[curindex];
+ (*alval)[curindex] = (*alval)[previndex];
+ (*alval)[previndex] = swap_val;
+ }
+ else /* can't happen */
+ break;
+
+ /*
+ * Fix the conf line values.
+ */
+
+ if((*cl)->value)
+ fs_give((void **)&(*cl)->value);
+
+ (*cl)->value = pretty_value(ps, *cl);
+
+ if(i == 'd'){
+ if((*cl)->next->value)
+ fs_give((void **)&(*cl)->next->value);
+
+ (*cl)->next->value = pretty_value(ps, (*cl)->next);
+ *cl = next_confline(*cl);
+ }
+ else{
+ if((*cl)->prev->value)
+ fs_give((void **)&(*cl)->prev->value);
+
+ (*cl)->prev->value = pretty_value(ps, (*cl)->prev);
+ *cl = prev_confline(*cl);
+ }
+
+ rv = ps->mangled_body = 1;
+ break;
+
+ case MC_EXIT: /* exit */
+ rv = config_exit_cmd(flags);
+ break;
+
+ default:
+ rv = -1;
+ break;
+ }
+
+ if(skip_to_next)
+ *cl = next_confline(*cl);
+
+ /*
+ * At this point, if changes occurred, var->user_val.X is set.
+ * So, fix the current_val, and handle special cases...
+ *
+ * NOTE: we don't worry about the "fixed variable" case here, because
+ * editing such vars should have been prevented above...
+ */
+ if(rv == 1){
+ /*
+ * Now go and set the current_val based on user_val changes
+ * above. Turn off command line settings...
+ */
+ set_current_val((*cl)->var, TRUE, FALSE);
+ fix_side_effects(ps, (*cl)->var, 0);
+
+ /*
+ * Delay setting the displayed value until "var.current_val" is set
+ * in case current val get's changed due to a special case above.
+ */
+ if(newval){
+ if(*newval)
+ fs_give((void **) newval);
+
+ *newval = pretty_value(ps, *cl);
+ }
+
+ exception_override_warning((*cl)->var);
+ }
+
+ if(sval)
+ fs_give((void **) &sval);
+
+ if(olddefval)
+ fs_give((void **) &olddefval);
+
+ return(rv);
+}
+
+
+int
+config_exit_cmd(unsigned int flags)
+{
+ return(screen_exit_cmd(flags, "Configuration"));
+}
+
+
+int
+simple_exit_cmd(unsigned int flags)
+{
+ return(2);
+}
+
+
+/*
+ * screen_exit_cmd - basic config/flag screen exit logic
+ */
+int
+screen_exit_cmd(unsigned int flags, char *cmd)
+{
+ if(flags & CF_CHANGES){
+ switch(want_to(EXIT_PMT, 'y', 'x', h_config_undo, WT_FLUSH_IN)){
+ case 'y':
+ q_status_message1(SM_ORDER,0,3,"%s changes saved", cmd);
+ return(2);
+
+ case 'n':
+ q_status_message1(SM_ORDER,3,5,"No %s changes saved", cmd);
+ return(10);
+
+ case 'x': /* ^C */
+ default :
+ q_status_message(SM_ORDER,3,5,"Changes not yet saved");
+ return(0);
+ }
+ }
+ else
+ return(2);
+}
+
+
+/*
+ *
+ */
+void
+config_add_list(struct pine *ps, CONF_S **cl, char **ltmp, char ***newval, int after)
+{
+ int items, i;
+ char *tmp, ***alval;
+ CONF_S *ctmp;
+
+ for(items = 0, i = 0; ltmp[i]; i++) /* count list items */
+ items++;
+
+ alval = ALVAL((*cl)->var, ew);
+
+ if(alval && (*alval)){
+ if((*alval)[0] && (*alval)[0][0]){
+ /*
+ * Since we were already a list, make room
+ * for the new member[s] and fall thru to
+ * actually fill them in below...
+ */
+ for(i = 0; (*alval)[i]; i++)
+ ;
+
+ fs_resize((void **)alval, (i + items + 1) * sizeof(char *));
+
+ /*
+ * move the ones that will be bumped down to the bottom of the list
+ */
+ for(; i >= (*cl)->varmem + (after?1:0); i--)
+ (*alval)[i+items] = (*alval)[i];
+
+ i = 0;
+ }
+ else if(alval){
+ (*cl)->varmem = 0;
+ if(*alval)
+ free_list_array(alval);
+
+ *alval = (char **)fs_get((items+1)*sizeof(char *));
+ memset((void *)(*alval), 0, (items+1)*sizeof(char *));
+ (*alval)[0] = ltmp[0];
+ if(newval)
+ *newval = &(*cl)->value;
+
+ if((*cl)->value)
+ fs_give((void **)&(*cl)->value);
+
+ i = 1;
+ }
+ }
+ else if(alval){
+ /*
+ * since we were previously empty, we want
+ * to replace the first CONF_S's value with
+ * the first new value, and fill the other
+ * in below if there's a list...
+ *
+ * first, make sure we're at the beginning of this config
+ * section and dump the config lines for the default list,
+ * except for the first one, which we will over-write.
+ */
+ *cl = (*cl)->varnamep;
+ while((*cl)->next && (*cl)->next->varnamep == (*cl)->varnamep)
+ snip_confline(&(*cl)->next);
+
+ /*
+ * now allocate the new user_val array and fill in the first entry.
+ */
+ *alval = (char **)fs_get((items+1)*sizeof(char *));
+ memset((void *)(*alval), 0, (items+1) * sizeof(char *));
+ (*alval)[(*cl)->varmem=0] = ltmp[0];
+ if(newval)
+ *newval = &(*cl)->value;
+
+ if((*cl)->value)
+ fs_give((void **)&(*cl)->value);
+
+ i = 1;
+ }
+
+ /*
+ * Make new cl's to fit in the new space. Move the value from the current
+ * line if inserting before it, else leave it where it is.
+ */
+ for(; i < items ; i++){
+ (*alval)[i+(*cl)->varmem + (after?1:0)] = ltmp[i];
+ tmp = (*cl)->value;
+ new_confline(cl);
+ if(after)
+ (*cl)->value = NULL;
+ else
+ (*cl)->value = tmp;
+
+ (*cl)->var = (*cl)->prev->var;
+ (*cl)->valoffset = (*cl)->prev->valoffset;
+ (*cl)->varoffset = (*cl)->prev->varoffset;
+ (*cl)->headingp = (*cl)->prev->headingp;
+ (*cl)->keymenu = (*cl)->prev->keymenu;
+ (*cl)->help = (*cl)->prev->help;
+ (*cl)->tool = (*cl)->prev->tool;
+ (*cl)->varnamep = (*cl)->prev->varnamep;
+ *cl = (*cl)->prev;
+ if(!after)
+ (*cl)->value = NULL;
+
+ if(newval){
+ if(after)
+ *newval = &(*cl)->next->value;
+ else
+ *newval = &(*cl)->value;
+ }
+ }
+
+ /*
+ * now fix up varmem values and fill in new values that have been
+ * left NULL
+ */
+ for(ctmp = (*cl)->varnamep, i = 0;
+ (*alval)[i];
+ ctmp = ctmp->next, i++){
+ ctmp->varmem = i;
+ if(!ctmp->value){
+ /* BUG: We should be able to do this without the temp
+ * copy...
+ */
+ char *ptmp = pretty_value(ps, ctmp);
+ ctmp->value = (ctmp->varnamep->flags & CF_PRINTER) ? printer_name(ptmp) : cpystr(ptmp);
+ fs_give((void **)&ptmp);
+ }
+ }
+}
+
+
+/*
+ *
+ */
+void
+config_del_list_item(CONF_S **cl, char ***newval)
+{
+ char **bufp, ***alval;
+ int i;
+ CONF_S *ctmp;
+
+ alval = ALVAL((*cl)->var, ew);
+
+ if((*alval)[(*cl)->varmem + 1]){
+ for(bufp = &(*alval)[(*cl)->varmem];
+ (*bufp = *(bufp+1)) != NULL; bufp++)
+ ;
+
+ if(*cl == (*cl)->varnamep){ /* leading value */
+ if((*cl)->value)
+ fs_give((void **)&(*cl)->value);
+
+ ctmp = (*cl)->next;
+ (*cl)->value = ctmp->value;
+ ctmp->value = NULL;
+ }
+ else{
+ ctmp = *cl; /* blast the confline */
+ *cl = (*cl)->next;
+ if(ctmp == opt_screen->top_line)
+ opt_screen->top_line = *cl;
+ }
+
+ snip_confline(&ctmp);
+
+ for(ctmp = (*cl)->varnamep, i = 0; /* now fix up varmem values */
+ (*alval)[i];
+ ctmp = ctmp->next, i++)
+ ctmp->varmem = i;
+ }
+ else if((*cl)->varmem){ /* blasted last in list */
+ ctmp = *cl;
+ *cl = (*cl)->prev;
+ if(ctmp == opt_screen->top_line)
+ opt_screen->top_line = *cl;
+
+ snip_confline(&ctmp);
+ }
+ else{ /* blasted last remaining */
+ if(alval && *alval)
+ fs_give((void **)alval);
+
+ *newval = &(*cl)->value;
+ }
+}
+
+
+/*
+ * feature list manipulation tool
+ *
+ *
+ * returns: -1 on unrecognized cmd, 0 if no change, 1 if change
+ */
+int
+checkbox_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ int rv = 0;
+
+ switch(cmd){
+ case MC_TOGGLE: /* mark/unmark feature */
+ if((*cl)->var == &ps->vars[V_FEATURE_LIST]){
+ rv = 1;
+ toggle_feature_bit(ps, (*cl)->varmem, (*cl)->var, *cl, 0);
+ }
+ else
+ q_status_message(SM_ORDER | SM_DING, 3, 6,
+ "Programmer botch! Unknown checkbox type.");
+
+ break;
+
+ case MC_EXIT: /* exit */
+ rv = config_exit_cmd(flags);
+ break;
+
+ default :
+ rv = -1;
+ break;
+ }
+
+ return(rv);
+}
+
+
+/*
+ * simple radio-button style variable handler
+ */
+int
+radiobutton_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ char **apval;
+ int rv = 0;
+ NAMEVAL_S *rule = NULL;
+#ifndef _WINDOWS
+ int old_uc, old_cs;
+ CONF_S *ctmp;
+#endif
+
+ apval = APVAL((*cl)->var, ew);
+
+ switch(cmd){
+ case MC_CHOICE : /* set/unset feature */
+
+ if(fixed_var((*cl)->var, NULL, NULL)){
+ if(((*cl)->var->post_user_val.p || (*cl)->var->main_user_val.p)
+ && want_to(_("Delete old unused personal option setting"),
+ 'y', 'n', NO_HELP, WT_FLUSH_IN) == 'y'){
+ delete_user_vals((*cl)->var);
+ q_status_message(SM_ORDER, 0, 3, _("Deleted"));
+ rv = 1;
+ }
+
+ return(rv);
+ }
+
+ if(standard_radio_var(ps, (*cl)->var) || (*cl)->var == startup_ptr){
+ PTR_TO_RULEFUNC rulefunc;
+
+#ifndef _WINDOWS
+ if((*cl)->var == &ps->vars[V_COLOR_STYLE]){
+ old_uc = pico_usingcolor();
+ old_cs = ps->color_style;
+ }
+#endif
+
+ if((*cl)->var->cmdline_val.p)
+ fs_give((void **)&(*cl)->var->cmdline_val.p);
+
+ if(apval && *apval)
+ fs_give((void **)apval);
+
+ rulefunc = rulefunc_from_var(ps, (*cl)->var);
+ if(rulefunc)
+ rule = (*rulefunc)((*cl)->varmem);
+
+ if(apval && rule)
+ *apval = cpystr(S_OR_L(rule));
+
+ cur_rule_value((*cl)->var, TRUE, TRUE);
+ set_radio_pretty_vals(ps, cl);
+
+ if((*cl)->var == &ps->vars[V_AB_SORT_RULE])
+ addrbook_redo_sorts();
+ else if((*cl)->var == &ps->vars[V_THREAD_DISP_STYLE]){
+ clear_index_cache(ps->mail_stream, 0);
+ }
+ else if((*cl)->var == &ps->vars[V_THREAD_INDEX_STYLE]){
+ MAILSTREAM *m;
+ int i;
+
+ clear_index_cache(ps->mail_stream, 0);
+ /* clear all hidden and collapsed flags */
+ set_lflags(ps->mail_stream, ps->msgmap, MN_COLL | MN_CHID, 0);
+
+ if(SEP_THRDINDX()
+ && SORT_IS_THREADED(ps->msgmap)
+ && unview_thread(ps, ps->mail_stream, ps->msgmap)){
+ ps->next_screen = mail_index_screen;
+ ps->view_skipped_index = 0;
+ ps->mangled_screen = 1;
+ }
+
+ if(SORT_IS_THREADED(ps->msgmap)
+ && (SEP_THRDINDX() || COLL_THRDS()))
+ collapse_threads(ps->mail_stream, ps->msgmap, NULL);
+
+ for(i = 0; i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ if(m)
+ sp_set_viewing_a_thread(m, 0);
+ }
+
+ adjust_cur_to_visible(ps->mail_stream, ps->msgmap);
+ }
+#ifndef _WINDOWS
+ else if((*cl)->var == &ps->vars[V_COLOR_STYLE]){
+ if(old_cs != ps->color_style){
+ pico_toggle_color(0);
+ switch(ps->color_style){
+ case COL_NONE:
+ case COL_TERMDEF:
+ pico_set_color_options(pico_trans_color() ? COLOR_TRANS_OPT : 0);
+ break;
+ case COL_ANSI8:
+ pico_set_color_options(COLOR_ANSI8_OPT|COLOR_TRANS_OPT);
+ break;
+ case COL_ANSI16:
+ pico_set_color_options(COLOR_ANSI16_OPT|COLOR_TRANS_OPT);
+ break;
+ case COL_ANSI256:
+ pico_set_color_options(COLOR_ANSI256_OPT|COLOR_TRANS_OPT);
+ break;
+ }
+
+ if(ps->color_style != COL_NONE)
+ pico_toggle_color(1);
+ }
+
+ if(pico_usingcolor())
+ pico_set_normal_color();
+
+ if(!old_uc && pico_usingcolor()){
+
+ /*
+ * remove the explanatory warning line and a blank line
+ */
+
+ /* first find the first blank line */
+ for(ctmp = *cl; ctmp; ctmp = next_confline(ctmp))
+ if(ctmp->flags & CF_NOSELECT)
+ break;
+
+ if(ctmp && ctmp->flags & CF_NOSELECT &&
+ ctmp->prev && !(ctmp->prev->flags & CF_NOSELECT) &&
+ ctmp->next && ctmp->next->flags & CF_NOSELECT &&
+ ctmp->next->next &&
+ ctmp->next->next->flags & CF_NOSELECT){
+ ctmp->prev->next = ctmp->next->next;
+ ctmp->next->next->prev = ctmp->prev;
+ ctmp->next->next = NULL;
+ free_conflines(&ctmp);
+ }
+
+ /* make all the colors selectable */
+ for(ctmp = *cl; ctmp; ctmp = next_confline(ctmp))
+ if(ctmp->flags & CF_POT_SLCTBL)
+ ctmp->flags &= ~CF_NOSELECT;
+ }
+ else if(old_uc && !pico_usingcolor()){
+
+ /*
+ * add the explanatory warning line and a blank line
+ */
+
+ /* first find the existing blank line */
+ for(ctmp = *cl; ctmp; ctmp = next_confline(ctmp))
+ if(ctmp->flags & CF_NOSELECT)
+ break;
+
+ /* add the explanatory warning line */
+ new_confline(&ctmp);
+ ctmp->help = NO_HELP;
+ ctmp->flags |= CF_NOSELECT;
+ ctmp->value = cpystr(COLORNOSET);
+
+ /* and add another blank line */
+ new_confline(&ctmp);
+ ctmp->flags |= (CF_NOSELECT | CF_B_LINE);
+
+ /* make all the colors non-selectable */
+ for(ctmp = *cl; ctmp; ctmp = next_confline(ctmp))
+ if(ctmp->flags & CF_POT_SLCTBL)
+ ctmp->flags |= CF_NOSELECT;
+ }
+
+ clear_index_cache(ps->mail_stream, 0);
+ ClearScreen();
+ ps->mangled_screen = 1;
+ }
+#endif
+
+ ps->mangled_body = 1; /* BUG: redraw it all for now? */
+ rv = 1;
+ }
+ else if((*cl)->var == &ps->vars[V_SORT_KEY]){
+ SortOrder def_sort;
+ int def_sort_rev;
+
+ def_sort_rev = (*cl)->varmem >= (short) EndofList;
+ def_sort = (SortOrder) ((*cl)->varmem - (def_sort_rev
+ * EndofList));
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s", sort_name(def_sort),
+ (def_sort_rev) ? "/Reverse" : "");
+
+ if((*cl)->var->cmdline_val.p)
+ fs_give((void **)&(*cl)->var->cmdline_val.p);
+
+ if(apval){
+ if(*apval)
+ fs_give((void **)apval);
+
+ *apval = cpystr(tmp_20k_buf);
+ }
+
+ set_current_val((*cl)->var, TRUE, TRUE);
+ if(decode_sort(ps->VAR_SORT_KEY, &def_sort, &def_sort_rev) != -1){
+ ps->def_sort = def_sort;
+ ps->def_sort_rev = def_sort_rev;
+ }
+
+ set_radio_pretty_vals(ps, cl);
+ ps->mangled_body = 1; /* BUG: redraw it all for now? */
+ rv = 1;
+ }
+ else
+ q_status_message(SM_ORDER | SM_DING, 3, 6,
+ "Programmer botch! Unknown radiobutton type.");
+
+ break;
+
+ case MC_EXIT: /* exit */
+ rv = config_exit_cmd(flags);
+ break;
+
+ default :
+ rv = -1;
+ break;
+ }
+
+ if(rv == 1)
+ exception_override_warning((*cl)->var);
+
+ return(rv);
+}
+
+
+/*
+ * simple yes/no style variable handler
+ */
+int
+yesno_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ int rv = 0, yes = 0;
+ char *pval, **apval;
+
+ pval = PVAL((*cl)->var, ew);
+ apval = APVAL((*cl)->var, ew);
+
+ switch(cmd){
+ case MC_TOGGLE: /* toggle yes to no and back */
+ if(fixed_var((*cl)->var, NULL, NULL)){
+ if(((*cl)->var->post_user_val.p || (*cl)->var->main_user_val.p)
+ && want_to(_("Delete old unused personal option setting"),
+ 'y', 'n', NO_HELP, WT_FLUSH_IN) == 'y'){
+ delete_user_vals((*cl)->var);
+ q_status_message(SM_ORDER, 0, 3, _("Deleted"));
+ rv = 1;
+ }
+
+ return(rv);
+ }
+
+ rv = 1;
+ yes = ((pval && !strucmp(pval, yesstr)) ||
+ (!pval && (*cl)->var->current_val.p &&
+ !strucmp((*cl)->var->current_val.p, yesstr)));
+ fs_give((void **)&(*cl)->value);
+
+ if(apval){
+ if(*apval)
+ fs_give((void **)apval);
+
+ if(yes)
+ *apval = cpystr(nostr);
+ else
+ *apval = cpystr(yesstr);
+ }
+
+ set_current_val((*cl)->var, FALSE, FALSE);
+ if((*cl)->value)
+ fs_give((void **)&(*cl)->value);
+
+ (*cl)->value = pretty_value(ps, *cl);
+ fix_side_effects(ps, (*cl)->var, 0);
+
+ break;
+
+ case MC_EXIT: /* exit */
+ rv = config_exit_cmd(flags);
+ break;
+
+ default :
+ rv = -1;
+ break;
+ }
+
+ return(rv);
+}
+
+
+/*
+ * Manage display of the config/options menu body.
+ */
+void
+update_option_screen(struct pine *ps, OPT_SCREEN_S *screen, Pos *cursor_pos)
+{
+ int dline, w, save = '\0';
+ CONF_S *top_line, *ctmp;
+ char *value;
+ unsigned got_width;
+ int want_width, first_width;
+ char *saveptr = NULL;
+
+#ifdef _WINDOWS
+ int last_selectable;
+ mswin_beginupdate();
+#endif
+ if(screen == NULL)
+ return;
+
+ if(cursor_pos){
+ cursor_pos->col = 0;
+ cursor_pos->row = -1; /* to tell us if we've set it yet */
+ }
+
+ /*
+ * calculate top line of display for reframing if the current field
+ * is off the display defined by screen->top_line...
+ */
+ if((ctmp = screen->top_line) != NULL)
+ for(dline = BODY_LINES(ps);
+ dline && ctmp && ctmp != screen->current;
+ ctmp = next_confline(ctmp), dline--)
+ ;
+
+ if(!ctmp || !dline){ /* force reframing */
+ dline = 0;
+ ctmp = top_line = first_confline(screen->current);
+ do
+ if(((dline++)%BODY_LINES(ps)) == 0)
+ top_line = ctmp;
+ while(ctmp != screen->current && (ctmp = next_confline(ctmp)));
+ }
+ else
+ top_line = screen->top_line;
+
+#ifdef _WINDOWS
+ /*
+ * Figure out how far down the top line is from the top and how many
+ * total lines there are. Dumb to loop every time thru, but
+ * there aren't that many lines, and it's cheaper than rewriting things
+ * to maintain a line count in each structure...
+ */
+ for(dline = 0, ctmp = prev_confline(top_line); ctmp; ctmp = prev_confline(ctmp))
+ dline++;
+
+ scroll_setpos(dline);
+ last_selectable = dline;
+ for(ctmp = next_confline(top_line); ctmp ; ctmp = next_confline(ctmp)){
+ dline++;
+ if (!(ctmp->flags & CF_NOSELECT))
+ last_selectable = dline;
+ }
+ dline = last_selectable;
+ scroll_setrange(BODY_LINES(ps), dline);
+#endif
+
+ /* mangled body or new page, force redraw */
+ if(ps->mangled_body || screen->top_line != top_line)
+ screen->prev = NULL;
+
+ /* loop thru painting what's needed */
+ for(dline = 0, ctmp = top_line;
+ dline < BODY_LINES(ps);
+ dline++, ctmp = next_confline(ctmp)){
+
+ /*
+ * only fall thru painting if something needs painting...
+ */
+ if(!(!screen->prev || ctmp == screen->prev || ctmp == screen->current
+ || ctmp == screen->prev->varnamep
+ || ctmp == screen->current->varnamep
+ || ctmp == screen->prev->headingp
+ || ctmp == screen->current->headingp))
+ continue;
+
+ ClearLine(dline + HEADER_ROWS(ps));
+
+ if(ctmp){
+ if(ctmp->flags & CF_B_LINE)
+ continue;
+
+ if(ctmp->varname && !(ctmp->flags & CF_INVISIBLEVAR)){
+ if(ctmp == screen->current && cursor_pos)
+ cursor_pos->row = dline + HEADER_ROWS(ps);
+
+ if((ctmp == screen->current
+ || ctmp == screen->current->varnamep
+ || ctmp == screen->current->headingp)
+ && !(ctmp->flags & CF_NOHILITE))
+ StartInverse();
+
+ if(ctmp->flags & CF_H_LINE){
+ MoveCursor(dline + HEADER_ROWS(ps), 0);
+ Write_to_screen(repeat_char(ps->ttyo->screen_cols, '-'));
+ }
+
+ if(ctmp->flags & CF_CENTERED){
+ int offset = ps->ttyo->screen_cols/2
+ - (utf8_width(ctmp->varname)/2);
+ MoveCursor(dline + HEADER_ROWS(ps),
+ (offset > 0) ? offset : 0);
+ }
+ else if(ctmp->varoffset)
+ MoveCursor(dline+HEADER_ROWS(ps), ctmp->varoffset);
+
+ Write_to_screen(ctmp->varname);
+ if((ctmp == screen->current
+ || ctmp == screen->current->varnamep
+ || ctmp == screen->current->headingp)
+ && !(ctmp->flags & CF_NOHILITE))
+ EndInverse();
+ }
+
+ value = (ctmp->flags & CF_INHERIT) ? INHERIT : ctmp->value;
+ if(value){
+ char *p;
+ int i, j;
+
+ memset(tmp_20k_buf, '\0',
+ (6*ps->ttyo->screen_cols + 1) * sizeof(char));
+ if(ctmp == screen->current){
+ if(!(ctmp->flags & CF_DOUBLEVAR && ctmp->flags & CF_VAR2))
+ StartInverse();
+
+ if(cursor_pos)
+ cursor_pos->row = dline + HEADER_ROWS(ps);
+ }
+
+ if(ctmp->flags & CF_H_LINE)
+ memset(tmp_20k_buf, '-',
+ ps->ttyo->screen_cols * sizeof(char));
+
+ if(ctmp->flags & CF_CENTERED){
+ int offset = ps->ttyo->screen_cols/2
+ - (utf8_width(value)/2);
+ /* BUG: tabs screw us figuring length above */
+ if(offset > 0){
+ char *q;
+
+ p = tmp_20k_buf + offset;
+ if(!*(q = tmp_20k_buf))
+ while(q < p)
+ *q++ = ' ';
+ }
+ }
+ else
+ p = tmp_20k_buf;
+
+ /*
+ * Copy the value to a temp buffer expanding tabs, and
+ * making sure not to write beyond screen right...
+ */
+ for(i = 0, j = ctmp->valoffset; value[i]; i++){
+ if(value[i] == ctrl('I')){
+ do
+ *p++ = ' ';
+ while((++j) & 0x07);
+ }
+ else{
+ *p++ = value[i];
+ j++;
+ }
+ }
+
+ if(ctmp == screen->current && cursor_pos){
+ if(ctmp->flags & CF_DOUBLEVAR && ctmp->flags & CF_VAR2)
+ cursor_pos->col = ctmp->val2offset;
+ else
+ cursor_pos->col = ctmp->valoffset;
+
+ if(ctmp->tool == radiobutton_tool
+#ifdef ENABLE_LDAP
+ || ctmp->tool==ldap_radiobutton_tool
+#endif
+ || ctmp->tool==role_radiobutton_tool
+ || ctmp->tool==checkbox_tool
+ || (ctmp->tool==color_setting_tool &&
+ ctmp->valoffset != COLOR_INDENT))
+ cursor_pos->col++;
+ }
+
+ if(ctmp->flags & CF_DOUBLEVAR){
+ long l;
+
+ p = tmp_20k_buf;
+ first_width = ctmp->val2offset - ctmp->valoffset - SPACE_BETWEEN_DOUBLEVARS;
+ if((l=utf8_width(p)) > first_width && first_width >= 0){
+ saveptr = utf8_count_forw_width(p, first_width, &got_width);
+ /*
+ * got_width != first_width indicates there's a problem
+ * that should not happen. Ignore it.
+ */
+ if(saveptr){
+ save = *saveptr;
+ *saveptr = '\0';
+ }
+ }
+ else
+ save = '\0';
+
+ /*
+ * If this is a COLOR_BLOB line we do special coloring.
+ * The current object inverse hilite is only on the
+ * checkbox part, the exact format comes from the
+ * new_color_line function. If we change that we'll have
+ * to change this to get the coloring right.
+ */
+ if(p[0] == '(' && p[2] == ')' &&
+ p[3] == ' ' && p[4] == ' ' &&
+ (!strncmp(p+5, COLOR_BLOB, COLOR_BLOB_LEN)
+ || !strncmp(p+5, COLOR_BLOB_TRAN, COLOR_BLOB_LEN)
+ || !strncmp(p+5, COLOR_BLOB_NORM, COLOR_BLOB_LEN))){
+ COLOR_PAIR *lastc = NULL, *newc = NULL;
+
+ MoveCursor(dline+HEADER_ROWS(ps), ctmp->valoffset);
+ Write_to_screen_n(p, 3);
+ if(!(ctmp->flags & CF_VAR2) && ctmp == screen->current)
+ EndInverse();
+
+ Write_to_screen_n(p+3, 3);
+ newc = new_color_pair(colorx(CFC_ICOLOR(ctmp)),
+ colorx(CFC_ICOLOR(ctmp)));
+ if(newc){
+ lastc = pico_get_cur_color();
+ (void)pico_set_colorp(newc, PSC_NONE);
+ free_color_pair(&newc);
+ }
+
+ Write_to_screen_n(p+6, COLOR_BLOB_LEN-2);
+
+ if(lastc){
+ (void)pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+
+ Write_to_screen(p+6+COLOR_BLOB_LEN-2);
+ }
+ else{
+ PutLine0(dline+HEADER_ROWS(ps), ctmp->valoffset, p);
+ if(!(ctmp->flags & CF_VAR2) && ctmp == screen->current)
+ EndInverse();
+ }
+
+ if(saveptr)
+ *saveptr = save;
+
+ PutLine0(dline+HEADER_ROWS(ps),
+ ctmp->val2offset - SPACE_BETWEEN_DOUBLEVARS,
+ repeat_char(SPACE_BETWEEN_DOUBLEVARS, SPACE));
+
+ if(l > ctmp->val2offset - ctmp->valoffset && ctmp->val2offset - ctmp->valoffset >= 0)
+ p = saveptr + SPACE_BETWEEN_DOUBLEVARS;
+
+ if(p > tmp_20k_buf){
+ if(ctmp->flags & CF_VAR2 && ctmp == screen->current)
+ StartInverse();
+
+ if(p[0] == '(' && p[2] == ')' &&
+ p[3] == ' ' && p[4] == ' ' &&
+ (!strncmp(p+5, COLOR_BLOB, COLOR_BLOB_LEN)
+ || !strncmp(p+5, COLOR_BLOB_TRAN, COLOR_BLOB_LEN)
+ || !strncmp(p+5, COLOR_BLOB_NORM, COLOR_BLOB_LEN))){
+ COLOR_PAIR *lastc = NULL, *newc = NULL;
+
+ MoveCursor(dline+HEADER_ROWS(ps), ctmp->val2offset);
+ Write_to_screen_n(p, 3);
+ if(ctmp->flags & CF_VAR2 && ctmp == screen->current)
+ EndInverse();
+
+ Write_to_screen_n(p+3, 3);
+ newc = new_color_pair(colorx(CFC_ICOLOR(ctmp)),
+ colorx(CFC_ICOLOR(ctmp)));
+ if(newc){
+ lastc = pico_get_cur_color();
+ (void)pico_set_colorp(newc, PSC_NONE);
+ free_color_pair(&newc);
+ }
+
+ Write_to_screen_n(p+6, COLOR_BLOB_LEN-2);
+
+ if(lastc){
+ (void)pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+
+ Write_to_screen(p+6+COLOR_BLOB_LEN-2);
+ }
+ else{
+ PutLine0(dline+HEADER_ROWS(ps),ctmp->val2offset,p);
+ if(ctmp->flags & CF_VAR2 && ctmp == screen->current)
+ EndInverse();
+ }
+ }
+ }
+ else{
+ char *q, *first_space, *sample, *ptr;
+ COLOR_PAIR *lastc, *newc;
+ int invert;
+
+
+ if(ctmp->flags & CF_COLORSAMPLE &&
+ pico_usingcolor() &&
+ ((q = strstr(tmp_20k_buf, SAMPLE_LEADER)) ||
+ (q = strstr(tmp_20k_buf, "Color"))) &&
+ (first_space = strindex(q, SPACE)) &&
+ (strstr(value, SAMP1) ||
+ strstr(value, SAMP2))){
+
+ ptr = tmp_20k_buf;
+
+ /* write out first part */
+ *first_space = '\0';
+ PutLine0(dline+HEADER_ROWS(ps), ctmp->valoffset,
+ ptr);
+ *first_space = SPACE;
+ ptr = first_space;
+
+ if(ctmp == screen->current)
+ EndInverse();
+
+ sample = skip_white_space(ptr);
+ /* if there's enough room to put some sample up */
+ save = *sample;
+ *sample = '\0';
+ w = utf8_width(tmp_20k_buf);
+ *sample = save;
+ if(ctmp->valoffset + w < ps->ttyo->screen_cols){
+
+ sample++; /* for `[' at edge of sample */
+
+ save = *ptr;
+ *ptr = '\0';
+ w = utf8_width(tmp_20k_buf);
+ *ptr = save;
+
+ save = *sample;
+ *sample = '\0';
+ /* spaces and bracket before sample1 */
+ PutLine0(dline+HEADER_ROWS(ps), ctmp->valoffset+w, ptr);
+ *sample = save;
+
+ ptr = sample;
+
+ /* then the color sample */
+ if(ctmp->var == &ps->vars[V_VIEW_HDR_COLORS]){
+ SPEC_COLOR_S *hc, *hcolors;
+
+ lastc = newc = NULL;
+
+ hcolors =
+ spec_colors_from_varlist(LVAL(ctmp->var, ew),
+ 0);
+ for(hc = hcolors, i=0; hc; hc = hc->next, i++)
+ if(CFC_ICUST(ctmp) == i)
+ break;
+
+ if(hc && hc->fg && hc->fg[0] && hc->bg &&
+ hc->bg[0])
+ newc = new_color_pair(hc->fg, hc->bg);
+
+ if(newc){
+ lastc = pico_get_cur_color();
+ (void)pico_set_colorp(newc, PSC_NONE);
+ free_color_pair(&newc);
+ }
+
+ if(hcolors)
+ free_spec_colors(&hcolors);
+
+
+ /* print out sample1 */
+
+ save = *ptr;
+ *ptr = '\0';
+ w = utf8_width(tmp_20k_buf);
+ *ptr = save;
+
+ want_width = MIN(utf8_width(SAMP1)-2, ps->ttyo->screen_cols - w - ctmp->valoffset);
+ saveptr = utf8_count_forw_width(ptr, want_width, &got_width);
+ if(saveptr){
+ save = *saveptr;
+ *saveptr = '\0';
+ }
+
+ PutLine0(dline+HEADER_ROWS(ps), ctmp->valoffset + w, ptr);
+ if(saveptr)
+ *saveptr = save;
+
+ ptr = strindex(ptr, ']');
+
+ if(lastc){
+ (void)pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+ }
+ else if(ctmp->var == &ps->vars[V_KW_COLORS]){
+ KEYWORD_S *kw;
+ SPEC_COLOR_S *kw_col = NULL;
+
+ lastc = newc = NULL;
+
+ /* find keyword associated with this line */
+ for(kw=ps->keywords, i=0; kw; kw=kw->next, i++)
+ if(CFC_ICUST(ctmp) == i)
+ break;
+
+ if(kw)
+ kw_col =
+ spec_colors_from_varlist(LVAL(ctmp->var,ew),
+ 0);
+
+ /* color for this keyword */
+ if(kw && kw_col
+ && ((kw->nick && kw->nick[0]
+ && (newc=hdr_color(kw->nick, NULL,
+ kw_col)))
+ ||
+ (kw->kw && kw->kw[0]
+ && (newc=hdr_color(kw->kw, NULL,
+ kw_col))))){
+ lastc = pico_get_cur_color();
+ (void)pico_set_colorp(newc, PSC_NONE);
+ free_color_pair(&newc);
+ }
+
+ if(kw_col)
+ free_spec_colors(&kw_col);
+
+ /* print out sample1 */
+
+ save = *ptr;
+ *ptr = '\0';
+ w = utf8_width(tmp_20k_buf);
+ *ptr = save;
+
+ want_width = MIN(utf8_width(SAMP1)-2, ps->ttyo->screen_cols - w - ctmp->valoffset);
+ saveptr = utf8_count_forw_width(ptr, want_width, &got_width);
+ if(saveptr){
+ save = *saveptr;
+ *saveptr = '\0';
+ }
+
+ PutLine0(dline+HEADER_ROWS(ps), ctmp->valoffset + w, ptr);
+ if(saveptr)
+ *saveptr = save;
+
+ ptr = strindex(ptr, ']');
+
+ if(lastc){
+ (void)pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+ }
+ else{
+ lastc = NULL;
+ invert = 0;
+ newc = sample_color(ps, ctmp->var);
+ if(newc){
+ if((lastc = pico_get_cur_color()) != NULL)
+ (void)pico_set_colorp(newc, PSC_NONE);
+
+ free_color_pair(&newc);
+ }
+ else if(var_defaults_to_rev(ctmp->var)){
+ if((newc = pico_get_rev_color()) != NULL){
+ /*
+ * Note, don't have to free newc.
+ */
+ if((lastc = pico_get_cur_color()) != NULL)
+ (void)pico_set_colorp(newc, PSC_NONE);
+ }
+ else{
+ StartInverse();
+ invert = 1;
+ }
+ }
+
+ if(ctmp->var==&ps->vars[V_SLCTBL_FORE_COLOR] &&
+
+ (F_OFF(F_SLCTBL_ITEM_NOBOLD, ps) ||
+ !(PVAL(ctmp->var,ew) &&
+ PVAL(ctmp->var+1,ew))))
+ StartBold();
+
+ /* print out sample1 */
+
+ save = *ptr;
+ *ptr = '\0';
+ w = utf8_width(tmp_20k_buf);
+ *ptr = save;
+
+ want_width = MIN(utf8_width(SAMP1)-2, ps->ttyo->screen_cols - w - ctmp->valoffset);
+ saveptr = utf8_count_forw_width(ptr, want_width, &got_width);
+ if(saveptr){
+ save = *saveptr;
+ *saveptr = '\0';
+ }
+
+ PutLine0(dline+HEADER_ROWS(ps), ctmp->valoffset + w, ptr);
+ if(saveptr)
+ *saveptr = save;
+
+ ptr = strindex(ptr, ']');
+
+ if(ctmp->var==&ps->vars[V_SLCTBL_FORE_COLOR] &&
+
+ (F_OFF(F_SLCTBL_ITEM_NOBOLD, ps) ||
+ !(PVAL(ctmp->var,ew) &&
+ PVAL(ctmp->var+1,ew))))
+ EndBold();
+
+ if(lastc){
+ (void)pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+ else if(invert)
+ EndInverse();
+ }
+
+ /*
+ * Finish sample1 with the right bracket.
+ */
+ save = *ptr;
+ *ptr = '\0';
+ w = utf8_width(tmp_20k_buf);
+ *ptr = save;
+ if(ctmp->valoffset + w < ps->ttyo->screen_cols){
+ save = *(ptr+1);
+ *(ptr+1) = '\0';
+ PutLine0(dline+HEADER_ROWS(ps), ctmp->valoffset + w, ptr);
+ *(ptr+1) = save;
+ ptr++;
+ w++;
+ }
+
+ /*
+ * Now check for an exception sample and paint it.
+ */
+ if(ctmp->valoffset + w + SBS + 1 < ps->ttyo->screen_cols && (q = strstr(ptr, SAMPEXC))){
+ /* spaces + `[' */
+ save = ptr[SBS+1];
+ ptr[SBS+1] = '\0';
+ PutLine0(dline+HEADER_ROWS(ps), ctmp->valoffset + w, ptr);
+ ptr[SBS+1] = save;
+ ptr += (SBS+1);
+
+ /*
+ * Figure out what color to paint it.
+ * This only happens with normal variables,
+ * not with V_VIEW_HDR_COLORS.
+ */
+ lastc = NULL;
+ invert = 0;
+ newc = sampleexc_color(ps, ctmp->var);
+ if(newc){
+ if((lastc = pico_get_cur_color()) != NULL)
+ (void)pico_set_colorp(newc, PSC_NONE);
+
+ free_color_pair(&newc);
+ }
+ else if(var_defaults_to_rev(ctmp->var)){
+ if((newc = pico_get_rev_color()) != NULL){
+ /*
+ * Note, don't have to free newc.
+ */
+ if((lastc = pico_get_cur_color()) != NULL)
+ (void)pico_set_colorp(newc, PSC_NONE);
+ }
+ else{
+ StartInverse();
+ invert = 1;
+ }
+ }
+
+ if(ctmp->var==&ps->vars[V_SLCTBL_FORE_COLOR] &&
+
+ (F_OFF(F_SLCTBL_ITEM_NOBOLD, ps) ||
+ !(PVAL(ctmp->var,Post) &&
+ PVAL(ctmp->var+1,Post))))
+ StartBold();
+
+ /* sample2 */
+ save = *ptr;
+ *ptr = '\0';
+ w = utf8_width(tmp_20k_buf);
+ *ptr = save;
+
+ want_width = MIN(utf8_width(SAMPEXC)-2, ps->ttyo->screen_cols - w - ctmp->valoffset);
+ saveptr = utf8_count_forw_width(ptr, want_width, &got_width);
+ if(saveptr){
+ save = *saveptr;
+ *saveptr = '\0';
+ }
+
+ PutLine0(dline+HEADER_ROWS(ps), ctmp->valoffset + w, ptr);
+ if(saveptr)
+ *saveptr = save;
+
+ ptr = strindex(ptr, ']');
+
+ /* turn off bold and color */
+ if(ctmp->var==&ps->vars[V_SLCTBL_FORE_COLOR] &&
+
+ (F_OFF(F_SLCTBL_ITEM_NOBOLD, ps) ||
+ !(PVAL(ctmp->var,Post) &&
+ PVAL(ctmp->var+1,Post))))
+ EndBold();
+
+ if(lastc){
+ (void)pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+ else if(invert)
+ EndInverse();
+
+ /*
+ * Finish sample2 with the right bracket.
+ */
+ save = *ptr;
+ *ptr = '\0';
+ w = utf8_width(tmp_20k_buf);
+ *ptr = save;
+ if(ctmp->valoffset + w < ps->ttyo->screen_cols){
+ save = *(ptr+1);
+ *(ptr+1) = '\0';
+ PutLine0(dline+HEADER_ROWS(ps), ctmp->valoffset + w, ptr);
+ *(ptr+1) = save;
+ ptr++;
+ w++;
+ }
+ }
+
+ /* paint rest of the line if there is any left */
+ if(ctmp->valoffset + w < ps->ttyo->screen_cols && *ptr){
+ want_width = ps->ttyo->screen_cols - w - ctmp->valoffset;
+ saveptr = utf8_count_forw_width(ptr, want_width, &got_width);
+ if(saveptr){
+ save = *saveptr;
+ *saveptr = '\0';
+ }
+
+ PutLine0(dline+HEADER_ROWS(ps), ctmp->valoffset + w, ptr);
+ if(saveptr)
+ *saveptr = save;
+ }
+ }
+ }
+ else{
+ w = utf8_width(tmp_20k_buf);
+ want_width = ps->ttyo->screen_cols - ctmp->valoffset;
+ if(w > want_width){
+ saveptr = utf8_count_forw_width(tmp_20k_buf, want_width, &got_width);
+ if(saveptr)
+ *saveptr = '\0';
+ }
+
+ PutLine0(dline+HEADER_ROWS(ps), ctmp->valoffset, tmp_20k_buf);
+ if(ctmp == screen->current)
+ EndInverse();
+ }
+ }
+ }
+ }
+ }
+
+ ps->mangled_body = 0;
+ screen->top_line = top_line;
+ screen->prev = screen->current;
+#ifdef _WINDOWS
+ mswin_endupdate();
+#endif
+}
+
+
+
+/*
+ *
+ */
+void
+print_option_screen(OPT_SCREEN_S *screen, char *prompt)
+{
+ CONF_S *ctmp;
+ int so_far;
+ char line[500];
+
+ if(open_printer(prompt) == 0){
+ for(ctmp = first_confline(screen->current);
+ ctmp;
+ ctmp = next_confline(ctmp)){
+
+ so_far = 0;
+ if(ctmp->varname && !(ctmp->flags & CF_INVISIBLEVAR)){
+
+ snprintf(line, sizeof(line), "%*s%s", ctmp->varoffset, "",
+ ctmp->varname);
+ line[sizeof(line)-1] = '\0';
+ print_text(line);
+ so_far = ctmp->varoffset + utf8_width(ctmp->varname);
+ }
+
+ if(ctmp && ctmp->value){
+ char *p = tmp_20k_buf;
+ int i, j, spaces;
+
+ /* Copy the value to a temp buffer expanding tabs. */
+ for(i = 0, j = ctmp->valoffset; ctmp->value[i]; i++){
+ if(ctmp->value[i] == ctrl('I')){
+ do
+ *p++ = ' ';
+ while((++j) & 0x07);
+
+ }
+ else{
+ *p++ = ctmp->value[i];
+ j++;
+ }
+ }
+
+ *p = '\0';
+ removing_trailing_white_space(tmp_20k_buf);
+
+ spaces = MAX(ctmp->valoffset - so_far, 0);
+ snprintf(line, sizeof(line), "%*s%s\n", spaces, "", tmp_20k_buf);
+ line[sizeof(line)-1] = '\0';
+ print_text(line);
+ }
+ }
+
+ close_printer();
+ }
+}
+
+
+
+/*
+ *
+ */
+void
+option_screen_redrawer(void)
+{
+ ps_global->mangled_body = 1;
+ update_option_screen(ps_global, opt_screen, (Pos *)NULL);
+}
+
+
+
+/*
+ * pretty_value - given the line, return an
+ * alloc'd string for line's value...
+ */
+char *
+pretty_value(struct pine *ps, CONF_S *cl)
+{
+ struct variable *v;
+
+ v = cl->var;
+
+ if(v == &ps->vars[V_FEATURE_LIST])
+ return(checkbox_pretty_value(ps, cl));
+ else if(standard_radio_var(ps, v) || v == startup_ptr)
+ return(radio_pretty_value(ps, cl));
+ else if(v == &ps->vars[V_SORT_KEY])
+ return(sort_pretty_value(ps, cl));
+ else if(v == &ps->vars[V_SIGNATURE_FILE])
+ return(sigfile_pretty_value(ps, cl));
+ else if(v == &ps->vars[V_USE_ONLY_DOMAIN_NAME])
+ return(yesno_pretty_value(ps, cl));
+ else if(color_holding_var(ps, v))
+ return(color_pretty_value(ps, cl));
+ else
+ return(text_pretty_value(ps, cl));
+}
+
+
+char *
+text_pretty_value(struct pine *ps, CONF_S *cl)
+{
+ char tmp[6*MAX_SCREEN_COLS+20], *pvalnorm, **lvalnorm, *pvalexc, **lvalexc;
+ char *p, *pval, **lval, lastchar = '\0';
+ int editing_except, fixed, uvalset, uvalposlen;
+ unsigned got_width;
+ int comments, except_set, avail_width;
+ int norm_with_except = 0, norm_with_except_inherit = 0;
+ int inherit_line = 0;
+
+ editing_except = (ew == ps_global->ew_for_except_vars);
+ fixed = cl->var->is_fixed;
+ if((ps_global->ew_for_except_vars != Main) && (ew == Main))
+ norm_with_except++; /* editing normal and except config exists */
+
+ if(cl->var->is_list){
+ lvalnorm = LVAL(cl->var, Main);
+ lvalexc = LVAL(cl->var, ps_global->ew_for_except_vars);
+ if(editing_except){
+ uvalset = lvalexc != NULL;
+ uvalposlen = uvalset && lvalexc[0] && lvalexc[0][0];
+ lval = lvalexc;
+ }
+ else{
+ uvalset = lvalnorm != NULL;
+ uvalposlen = uvalset && lvalnorm[0] && lvalnorm[0][0];
+ lval = lvalnorm;
+ }
+
+ except_set = lvalexc != NULL;
+ comments = cl->var->current_val.l != NULL;
+ if(norm_with_except && except_set && lvalexc[0] &&
+ !strcmp(lvalexc[0],INHERIT))
+ norm_with_except_inherit++;
+
+ if(uvalset && !strcmp(lval[0], INHERIT)){
+ if(cl->varmem == 0){
+ inherit_line++;
+ comments = 0;
+ }
+ }
+
+ /* only add extra comments on last member of list */
+ if(uvalset && !inherit_line && lval && lval[cl->varmem] &&
+ lval[cl->varmem + 1])
+ comments = 0;
+ }
+ else{
+ pvalnorm = PVAL(cl->var, Main);
+ pvalexc = PVAL(cl->var, ps_global->ew_for_except_vars);
+ if(editing_except){
+ uvalset = pvalexc != NULL;
+ uvalposlen = uvalset && *pvalexc;
+ pval = pvalexc;
+ }
+ else{
+ uvalset = pvalnorm != NULL;
+ uvalposlen = uvalset && *pvalnorm;
+ pval = pvalnorm;
+ }
+
+ except_set = pvalexc != NULL;
+ comments = cl->var->current_val.p != NULL;
+ }
+
+ memset(tmp, 0, sizeof(tmp));
+ p = tmp;
+ *p = '\0';
+
+ avail_width = ps->ttyo->screen_cols - cl->valoffset;
+
+ if(fixed || !uvalset || !uvalposlen){
+ p += utf8_to_width(p, "<", sizeof(tmp)-(p-tmp), avail_width, &got_width);
+ avail_width -= got_width;
+ }
+
+ if(fixed){
+ p += utf8_to_width(p, _(fixed_val), sizeof(tmp)-(p-tmp), avail_width, &got_width);
+ avail_width -= got_width;
+ }
+ else if(!uvalset){
+ p += utf8_to_width(p, _(no_val), sizeof(tmp)-(p-tmp), avail_width, &got_width);
+ avail_width -= got_width;
+ }
+ else if(!uvalposlen){
+ p += utf8_to_width(p, _(empty_val), sizeof(tmp)-(p-tmp), avail_width, &got_width);
+ avail_width -= got_width;
+ }
+ else if(inherit_line){
+ p += utf8_to_width(p, INHERIT, sizeof(tmp)-(p-tmp), avail_width, &got_width);
+ avail_width -= got_width;
+ }
+ else{
+ if(cl->var->is_list){
+ p += utf8_to_width(p, lval[cl->varmem], sizeof(tmp)-(p-tmp), avail_width, &got_width);
+ avail_width -= got_width;
+ }
+ else{
+ p += utf8_to_width(p, pval, sizeof(tmp)-(p-tmp), avail_width, &got_width);
+ avail_width -= got_width;
+ }
+ }
+
+ if(comments && (fixed || !uvalset || (norm_with_except && except_set))){
+ if(fixed || !uvalset){
+ p += utf8_to_width(p, ": using ", sizeof(tmp)-(p-tmp), avail_width, &got_width);
+ avail_width -= got_width;
+ }
+
+ if(norm_with_except && except_set){
+ if(!uvalset){
+ p += utf8_to_width(p, "exception ", sizeof(tmp)-(p-tmp), avail_width, &got_width);
+ avail_width -= got_width;
+ }
+ else if(!fixed){
+ if(!uvalposlen){
+ p += utf8_to_width(p, ": ", sizeof(tmp)-(p-tmp), avail_width, &got_width);
+ avail_width -= got_width;
+ }
+ else{
+ p += utf8_to_width(p, " (", sizeof(tmp)-(p-tmp), avail_width, &got_width);
+ avail_width -= got_width;
+ }
+
+ if(norm_with_except_inherit){
+ p += utf8_to_width(p, "added to by exception ", sizeof(tmp)-(p-tmp), avail_width, &got_width);
+ avail_width -= got_width;
+ }
+ else{
+ p += utf8_to_width(p, "overridden by exception ", sizeof(tmp)-(p-tmp), avail_width, &got_width);
+ avail_width -= got_width;
+ }
+ }
+ }
+
+ if(avail_width >= 7){
+ if(cl->var == &ps_global->vars[V_POST_CHAR_SET]){
+ p += utf8_to_width(p, "most specific (see help)", sizeof(tmp)-(p-tmp), avail_width, &got_width);
+ avail_width -= got_width;
+ }
+ else{
+ sstrncpy(&p, "\"", sizeof(tmp)-(p-tmp));
+ avail_width--;
+ if(cl->var->is_list){
+ char **the_list;
+
+ the_list = cl->var->current_val.l;
+
+ if(norm_with_except && except_set)
+ the_list = lvalexc;
+
+ if(the_list && the_list[0] && !strcmp(the_list[0], INHERIT))
+ the_list++;
+
+ for(lval = the_list; avail_width-(p-tmp) > 0 && *lval; lval++){
+ if(lval != the_list){
+ p += utf8_to_width(p, ",", sizeof(tmp)-(p-tmp), avail_width, &got_width);
+ avail_width -= got_width;
+ }
+
+ p += utf8_to_width(p, *lval, sizeof(tmp)-(p-tmp), avail_width, &got_width);
+ avail_width -= got_width;
+ }
+ }
+ else{
+ p += utf8_to_width(p, cl->var->current_val.p, sizeof(tmp)-(p-tmp), avail_width, &got_width);
+ avail_width -= got_width;
+ }
+
+ if(p-tmp+2 < sizeof(tmp)){
+ *p++ = '\"';
+ *p = '\0';
+ }
+ }
+ }
+ else if(*(p-1) == SPACE)
+ *--p = '\0';
+ }
+
+ tmp[sizeof(tmp)-1] = '\0';
+
+ if(fixed || !uvalset || !uvalposlen)
+ lastchar = '>';
+ else if(comments && norm_with_except && except_set)
+ lastchar = ')';
+
+ if(lastchar){
+ if(p-tmp+2 < sizeof(tmp)){
+ *p++ = lastchar;
+ *p = '\0';
+ }
+ }
+
+ tmp[sizeof(tmp)-1] = '\0';
+ avail_width = ps->ttyo->screen_cols - cl->valoffset;
+
+ if(utf8_width(tmp) < avail_width)
+ snprintf(tmp+strlen(tmp), sizeof(tmp)-strlen(tmp), "%*s", avail_width-utf8_width(tmp), "");
+
+ tmp[sizeof(tmp)-1] = '\0';
+
+ return(cpystr(tmp));
+}
+
+
+char *
+checkbox_pretty_value(struct pine *ps, CONF_S *cl)
+{
+ char tmp[6*MAXPATH];
+ char *comment = NULL;
+ int indent, x, screen_width, need;
+ int longest_featname, longest_comment;
+ int nwidcomm; /* name width with comment */
+ int nwidnocomm; /* and without comment */
+ FEATURE_S *feature;
+
+ screen_width = (ps && ps->ttyo) ? ps->ttyo->screen_cols : 80;
+ tmp[0] = '\0';
+
+ longest_featname = longest_feature_name();
+ longest_comment = longest_feature_comment(ps, ew);
+ indent = feature_indent();
+
+ nwidcomm = longest_featname;
+ nwidnocomm = longest_featname + 2 + longest_comment;
+
+ if((need = (indent + 5 + longest_featname + 2 + longest_comment) - screen_width) > 0){
+ if(need < 10){
+ nwidcomm -= need;
+ nwidnocomm -= need;
+ }
+ else{
+ longest_comment = 0;
+ nwidnocomm = longest_featname;
+ }
+ }
+
+ feature = feature_list(cl->varmem);
+
+ x = feature_gets_an_x(ps, cl->var, feature, &comment, ew);
+
+ if(longest_comment && comment && *comment){
+ utf8_snprintf(tmp, sizeof(tmp), "[%c] %-*.*w %-*.*w", x ? 'X' : ' ',
+ nwidcomm, nwidcomm,
+ pretty_feature_name(feature->name, nwidcomm),
+ longest_comment, longest_comment, comment ? comment : "");
+ }
+ else{
+ utf8_snprintf(tmp, sizeof(tmp), "[%c] %-*.*w", x ? 'X' : ' ',
+ nwidnocomm, nwidnocomm,
+ pretty_feature_name(feature->name, nwidnocomm));
+ }
+
+ return(cpystr(tmp));
+}
+
+
+int
+longest_feature_name(void)
+{
+ static int lv = -1;
+ int i, j;
+ FEATURE_S *feature;
+
+ if(lv < 0){
+ for(lv = 0, i = 0; (feature = feature_list(i)); i++)
+ if(feature_list_section(feature)
+ && lv < (j = utf8_width(pretty_feature_name(feature->name, -1))))
+ lv = j;
+
+ lv = MIN(lv, 100);
+ }
+
+ return(lv);
+}
+
+
+int
+feature_indent(void)
+{
+ return(6);
+}
+
+
+char *
+yesno_pretty_value(struct pine *ps, CONF_S *cl)
+{
+ char tmp[6*MAXPATH], *pvalnorm, *pvalexc;
+ char *p, *pval, lastchar = '\0';
+ int editing_except, fixed, norm_with_except, uvalset;
+ int curval, except_set;
+
+ editing_except = (ew == ps_global->ew_for_except_vars);
+ fixed = cl->var->is_fixed;
+ if((ps_global->ew_for_except_vars == Main) ||
+ (ew == ps_global->ew_for_except_vars))
+ norm_with_except = 0;
+ else
+ norm_with_except = 1; /* editing normal and except config exists */
+
+ pvalnorm = PVAL(cl->var, Main);
+ pvalexc = PVAL(cl->var, ps_global->ew_for_except_vars);
+ if(editing_except){
+ uvalset = (pvalexc != NULL &&
+ (!strucmp(pvalexc,yesstr) || !strucmp(pvalexc,nostr)));
+ pval = pvalexc;
+ }
+ else{
+ uvalset = (pvalnorm != NULL &&
+ (!strucmp(pvalnorm,yesstr) || !strucmp(pvalnorm,nostr)));
+ pval = pvalnorm;
+ }
+
+ except_set = (pvalexc != NULL &&
+ (!strucmp(pvalexc,yesstr) || !strucmp(pvalexc,nostr)));
+ curval = (cl->var->current_val.p != NULL &&
+ (!strucmp(cl->var->current_val.p,yesstr) ||
+ !strucmp(cl->var->current_val.p,nostr)));
+
+ p = tmp;
+ *p = '\0';
+
+ if(fixed || !uvalset)
+ sstrncpy(&p, "<", sizeof(tmp)-(p-tmp));
+
+ if(fixed)
+ sstrncpy(&p, _(fixed_val), sizeof(tmp)-(p-tmp));
+ else if(!uvalset)
+ sstrncpy(&p, _(no_val), sizeof(tmp)-(p-tmp));
+ else if(!strucmp(pval, yesstr))
+ sstrncpy(&p, yesstr, sizeof(tmp)-(p-tmp));
+ else
+ sstrncpy(&p, nostr, sizeof(tmp)-(p-tmp));
+
+ if(curval && (fixed || !uvalset || (norm_with_except && except_set))){
+ if(fixed || !uvalset)
+ sstrncpy(&p, ": using ", sizeof(tmp)-(p-tmp));
+
+ if(norm_with_except && except_set){
+ if(!uvalset)
+ sstrncpy(&p, "exception ", sizeof(tmp)-(p-tmp));
+ else if(!fixed){
+ sstrncpy(&p, " (", sizeof(tmp)-(p-tmp));
+ sstrncpy(&p, "overridden by exception ", sizeof(tmp)-(p-tmp));
+ }
+ }
+
+ sstrncpy(&p, "\"", sizeof(tmp)-(p-tmp));
+ sstrncpy(&p, !strucmp(cl->var->current_val.p,yesstr) ? yesstr : nostr, sizeof(tmp)-(p-tmp));
+ sstrncpy(&p, "\"", sizeof(tmp)-(p-tmp));
+ }
+
+ if(fixed || !uvalset)
+ lastchar = '>';
+ else if(curval && norm_with_except && except_set)
+ lastchar = ')';
+
+ if(lastchar && sizeof(tmp)-(p-tmp) > 1){
+ *p++ = lastchar;
+ *p = '\0';
+ }
+
+ tmp[sizeof(tmp)-1] = '\0';
+
+ if(utf8_width(tmp) < ps->ttyo->screen_cols - cl->valoffset)
+ snprintf(tmp+strlen(tmp), sizeof(tmp)-strlen(tmp),
+ "%*s", ps->ttyo->screen_cols - cl->valoffset - utf8_width(tmp), "");
+
+ tmp[sizeof(tmp)-1] = '\0';
+
+ return(cpystr(tmp));
+}
+
+
+char *
+radio_pretty_value(struct pine *ps, CONF_S *cl)
+{
+ char tmp[6*MAXPATH];
+ char *pvalnorm, *pvalexc, *pval;
+ int editing_except_which_isnt_normal, editing_normal_which_isnt_except;
+ int fixed, is_set_for_this_level = 0, is_the_one, the_exc_one;
+ int i, j, lv = 0;
+ NAMEVAL_S *rule = NULL, *f;
+ PTR_TO_RULEFUNC rulefunc;
+ struct variable *v;
+
+ tmp[0] = '\0';
+ v = cl->var;
+
+ editing_except_which_isnt_normal = (ew == ps_global->ew_for_except_vars &&
+ ew != Main);
+ editing_normal_which_isnt_except = (ew == Main &&
+ ew != ps_global->ew_for_except_vars);
+ fixed = cl->var->is_fixed;
+ pvalnorm = PVAL(v, Main);
+ pvalexc = PVAL(v, ps_global->ew_for_except_vars);
+
+ rulefunc = rulefunc_from_var(ps, v);
+ rule = rulefunc ? (*rulefunc)(cl->varmem) : NULL;
+
+ /* find longest name */
+ if(rulefunc)
+ for(lv = 0, i = 0; (f = (*rulefunc)(i)); i++)
+ if(lv < (j = utf8_width(f->name)))
+ lv = j;
+
+ lv = MIN(lv, 100);
+
+ if(editing_except_which_isnt_normal)
+ pval = pvalexc;
+ else
+ pval = pvalnorm;
+
+ if(pval)
+ is_set_for_this_level++;
+
+ if(fixed){
+ pval = v->fixed_val.p;
+ is_the_one = (pval && !strucmp(pval, S_OR_L(rule)));
+
+ utf8_snprintf(tmp, sizeof(tmp), "(%c) %-*.*w%s",
+ is_the_one ? R_SELD : ' ',
+ lv, lv, rule->name, is_the_one ? " (value is fixed)" : "");
+ }
+ else if(is_set_for_this_level){
+ is_the_one = (pval && !strucmp(pval, S_OR_L(rule)));
+ the_exc_one = (editing_normal_which_isnt_except && pvalexc &&
+ !strucmp(pvalexc, S_OR_L(rule)));
+ utf8_snprintf(tmp, sizeof(tmp), "(%c) %-*.*w%s",
+ is_the_one ? R_SELD : ' ',
+ lv, lv, rule->name,
+ (!is_the_one && the_exc_one) ? " (value set in exceptions)" :
+ (is_the_one && the_exc_one) ? " (also set in exceptions)" :
+ (is_the_one &&
+ editing_normal_which_isnt_except &&
+ pvalexc &&
+ !the_exc_one) ? " (overridden by exceptions)" :
+ "");
+ }
+ else{
+ if(pvalexc){
+ is_the_one = !strucmp(pvalexc, S_OR_L(rule));
+ utf8_snprintf(tmp, sizeof(tmp), "(%c) %-*.*w%s",
+ is_the_one ? R_SELD : ' ',
+ lv, lv, rule->name,
+ is_the_one ? " (value set in exceptions)" : "");
+ }
+ else{
+ pval = v->current_val.p;
+ is_the_one = (pval && !strucmp(pval, S_OR_L(rule)));
+ utf8_snprintf(tmp, sizeof(tmp), "(%c) %-*.*w%s",
+ is_the_one ? R_SELD : ' ',
+ lv, lv, rule->name,
+ is_the_one ? ((editing_except_which_isnt_normal && pvalnorm) ? " (default from regular config)" : " (default)") : "");
+ }
+ }
+
+ tmp[sizeof(tmp)-1] = '\0';
+
+ return(cpystr(tmp));
+}
+
+
+char *
+sigfile_pretty_value(struct pine *ps, CONF_S *cl)
+{
+ if(cl && cl->var == &ps->vars[V_SIGNATURE_FILE] &&
+ cl->prev && cl->prev->var == &ps->vars[V_LITERAL_SIG]){
+ if(cl->prev->var->current_val.p){
+ cl->flags |= CF_NOSELECT; /* side effect */
+ return(cpystr(_("<Ignored: using Literal-Signature instead>")));
+ }
+ else{
+ cl->flags &= ~CF_NOSELECT;
+ return(text_pretty_value(ps, cl));
+ }
+ }
+ else
+ return(cpystr(""));
+}
+
+
+char *
+color_pretty_value(struct pine *ps, CONF_S *cl)
+{
+ char tmp[6*MAXPATH];
+ char *p, *q;
+ struct variable *v;
+ int is_index;
+
+ tmp[0] = '\0';
+ v = cl->var;
+
+ if(v && color_holding_var(ps, v) &&
+ (p=srchstr(v->name, "-foreground-color"))){
+
+ is_index = !struncmp(v->name, "index-", 6);
+
+ q = sampleexc_text(ps, v);
+ utf8_snprintf(tmp, sizeof(tmp), "%c%.*s %sColor%*.50s %.20w%*s%.20w%.20w",
+ islower((unsigned char)v->name[0])
+ ? toupper((unsigned char)v->name[0])
+ : v->name[0],
+ MIN(p-v->name-1,30), v->name+1,
+ is_index ? "Symbol " : "",
+ MAX(EQ_COL - COLOR_INDENT -1 - MIN(p-v->name-1,30)
+ - 6 - (is_index ? 7 : 0) - 1,0), "",
+ sample_text(ps,v), *q ? SBS : 0, "", q,
+ color_parenthetical(v));
+ }
+
+ tmp[sizeof(tmp)-1] = '\0';
+
+ return(cpystr(tmp));
+}
+
+
+char *
+sort_pretty_value(struct pine *ps, CONF_S *cl)
+{
+ return(generalized_sort_pretty_value(ps, cl, 1));
+}
+
+
+char *
+generalized_sort_pretty_value(struct pine *ps, CONF_S *cl, int default_ok)
+{
+ char tmp[6*MAXPATH];
+ char *pvalnorm, *pvalexc, *pval;
+ int editing_except_which_isnt_normal, editing_normal_which_isnt_except;
+ int fixed, is_set_for_this_level = 0, is_the_one, the_exc_one;
+ int i, j, lv = 0;
+ struct variable *v;
+ SortOrder line_sort, var_sort, exc_sort;
+ int line_sort_rev, var_sort_rev, exc_sort_rev;
+
+ tmp[0] = '\0';
+ v = cl->var;
+
+ editing_except_which_isnt_normal = (ew == ps_global->ew_for_except_vars &&
+ ew != Main);
+ editing_normal_which_isnt_except = (ew == Main &&
+ ew != ps_global->ew_for_except_vars);
+ fixed = cl->var->is_fixed;
+ pvalnorm = PVAL(v, Main);
+ pvalexc = PVAL(v, ps_global->ew_for_except_vars);
+
+ /* find longest value's name */
+ for(lv = 0, i = 0; ps->sort_types[i] != EndofList; i++)
+ if(lv < (j = utf8_width(sort_name(ps->sort_types[i]))))
+ lv = j;
+
+ lv = MIN(lv, 100);
+
+ if(editing_except_which_isnt_normal)
+ pval = pvalexc;
+ else
+ pval = pvalnorm;
+
+ if(pval)
+ is_set_for_this_level++;
+
+ /* the config line we're talking about */
+ if(cl->varmem >= 0){
+ line_sort_rev = cl->varmem >= (short)EndofList;
+ line_sort = (SortOrder)(cl->varmem - (line_sort_rev * EndofList));
+ }
+
+ if(cl->varmem < 0){
+ utf8_snprintf(tmp, sizeof(tmp), "(%c) %-*w",
+ (pval == NULL) ? R_SELD : ' ',
+ lv, "Default");
+ }
+ else if(fixed){
+ pval = v->fixed_val.p;
+ decode_sort(pval, &var_sort, &var_sort_rev);
+ is_the_one = (var_sort_rev == line_sort_rev && var_sort == line_sort);
+
+ utf8_snprintf(tmp, sizeof(tmp), "(%c) %s%-*w%*s%s",
+ is_the_one ? R_SELD : ' ',
+ line_sort_rev ? "Reverse " : "",
+ lv, sort_name(line_sort),
+ line_sort_rev ? 0 : 8, "",
+ is_the_one ? " (value is fixed)" : "");
+ }
+ else if(is_set_for_this_level){
+ decode_sort(pval, &var_sort, &var_sort_rev);
+ is_the_one = (var_sort_rev == line_sort_rev && var_sort == line_sort);
+ decode_sort(pvalexc, &exc_sort, &exc_sort_rev);
+ the_exc_one = (editing_normal_which_isnt_except && pvalexc &&
+ exc_sort_rev == line_sort_rev && exc_sort == line_sort);
+ utf8_snprintf(tmp, sizeof(tmp), "(%c) %s%-*w%*s%s",
+ is_the_one ? R_SELD : ' ',
+ line_sort_rev ? "Reverse " : "",
+ lv, sort_name(line_sort),
+ line_sort_rev ? 0 : 8, "",
+ (!is_the_one && the_exc_one) ? " (value set in exceptions)" :
+ (is_the_one && the_exc_one) ? " (also set in exceptions)" :
+ (is_the_one &&
+ editing_normal_which_isnt_except &&
+ pvalexc &&
+ !the_exc_one) ? " (overridden by exceptions)" :
+ "");
+ }
+ else{
+ if(pvalexc){
+ decode_sort(pvalexc, &exc_sort, &exc_sort_rev);
+ is_the_one = (exc_sort_rev == line_sort_rev &&
+ exc_sort == line_sort);
+ utf8_snprintf(tmp, sizeof(tmp), "( ) %s%-*w%*s%s",
+ line_sort_rev ? "Reverse " : "",
+ lv, sort_name(line_sort),
+ line_sort_rev ? 0 : 8, "",
+ is_the_one ? " (value set in exceptions)" : "");
+ }
+ else{
+ pval = v->current_val.p;
+ decode_sort(pval, &var_sort, &var_sort_rev);
+ is_the_one = ((pval || default_ok) &&
+ var_sort_rev == line_sort_rev &&
+ var_sort == line_sort);
+ utf8_snprintf(tmp, sizeof(tmp), "(%c) %s%-*w%*s%s",
+ is_the_one ? R_SELD : ' ',
+ line_sort_rev ? "Reverse " : "",
+ lv, sort_name(line_sort),
+ line_sort_rev ? 0 : 8, "",
+ is_the_one ? ((editing_except_which_isnt_normal && pvalnorm) ? " (default from regular config)" : " (default)") : "");
+ }
+ }
+
+ return(cpystr(tmp));
+}
+
+
+COLOR_PAIR *
+sample_color(struct pine *ps, struct variable *v)
+{
+ COLOR_PAIR *cp = NULL;
+ char *pvalefg, *pvalebg;
+ char *pvalmfg, *pvalmbg;
+
+ pvalefg = PVAL(v, ew);
+ pvalebg = PVAL(v+1, ew);
+ pvalmfg = PVAL(v, Main);
+ pvalmbg = PVAL(v+1, Main);
+ if(v && color_holding_var(ps, v) &&
+ srchstr(v->name, "-foreground-color")){
+ if(pvalefg && pvalefg[0] && pvalebg && pvalebg[0])
+ cp = new_color_pair(pvalefg, pvalebg);
+ else if(ew == Post && pvalmfg && pvalmfg[0] && pvalmbg && pvalmbg[0])
+ cp = new_color_pair(pvalmfg, pvalmbg);
+ else if(v->global_val.p && v->global_val.p[0] &&
+ (v+1)->global_val.p && (v+1)->global_val.p[0])
+ cp = new_color_pair(v->global_val.p, (v+1)->global_val.p);
+ }
+
+ return(cp);
+}
+
+
+COLOR_PAIR *
+sampleexc_color(struct pine *ps, struct variable *v)
+{
+ COLOR_PAIR *cp = NULL;
+ char *pvalfg, *pvalbg;
+
+ pvalfg = PVAL(v, Post);
+ pvalbg = PVAL(v+1, Post);
+ if(v && color_holding_var(ps, v) &&
+ srchstr(v->name, "-foreground-color") &&
+ pvalfg && pvalfg[0] && pvalbg && pvalbg[0])
+ cp = new_color_pair(pvalfg, pvalbg);
+
+ return(cp);
+}
+
+
+void
+clear_feature(char ***l, char *f)
+{
+ char **list = l ? *l : NULL;
+ int count = 0;
+
+ for(; list && *list; list++, count++){
+ if(f && !strucmp(((!struncmp(*list,"no-",3)) ? *list + 3 : *list), f)){
+ fs_give((void **)list);
+ f = NULL;
+ }
+
+ if(!f) /* shift */
+ *list = *(list + 1);
+ }
+
+ /*
+ * this is helpful to keep the array from growing if a feature
+ * get's set and unset repeatedly
+ */
+ if(!f)
+ fs_resize((void **)l, count * sizeof(char *));
+}
+
+
+/*
+ *
+ */
+void
+toggle_feature_bit(struct pine *ps, int index, struct variable *var, CONF_S *cl, int just_flip_value)
+{
+ FEATURE_S *f;
+ int og, on_before;
+ char *p, **vp;
+
+ f = feature_list(index);
+
+ og = test_old_growth_bits(ps, f->id);
+
+ /*
+ * if this feature is in the fixed set, or old-growth is in the fixed
+ * set and this feature is in the old-growth set, don't alter it...
+ */
+ for(vp = var->fixed_val.l; vp && *vp; vp++){
+ p = (struncmp(*vp, "no-", 3)) ? *vp : *vp + 3;
+ if(!strucmp(p, f->name) || (og && !strucmp(p, "old-growth"))){
+ q_status_message(SM_ORDER, 3, 3,
+ _("Can't change value fixed by sys-admin."));
+ return;
+ }
+ }
+
+ on_before = F_ON(f->id, ps);
+
+ toggle_feature(ps, var, f, just_flip_value, ew);
+
+ /*
+ * Handle any alpine-specific features that need attention here. Features
+ * that aren't alpine-specific should be handled in toggle_feature instead.
+ */
+ if(on_before != F_ON(f->id, ps))
+ switch(f->id){
+ case F_CMBND_ABOOK_DISP :
+ addrbook_reset();
+ break;
+
+ case F_PRESERVE_START_STOP :
+ /* toggle raw mode settings to make tty driver aware of new setting */
+ PineRaw(0);
+ PineRaw(1);
+ break;
+
+ case F_USE_FK :
+ ps->orig_use_fkeys = F_ON(F_USE_FK, ps);
+ ps->mangled_footer = 1;
+ mark_keymenu_dirty();
+ break;
+
+ case F_SHOW_SORT :
+ ps->mangled_header = 1;
+ break;
+
+ case F_BLANK_KEYMENU :
+ if(F_ON(f->id, ps)){
+ FOOTER_ROWS(ps) = 1;
+ ps->mangled_body = 1;
+ }
+ else{
+ FOOTER_ROWS(ps) = 3;
+ ps->mangled_footer = 1;
+ }
+
+ clearfooter(ps);
+ break;
+
+ case F_ENABLE_INCOMING :
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ "Folder List changes will take effect your next Alpine session.");
+ break;
+
+#ifdef _WINDOWS
+ case F_SHOW_CURSOR :
+ mswin_showcaret(F_ON(f->id,ps));
+ break;
+
+ case F_ENABLE_TRAYICON :
+ mswin_trayicon(F_ON(f->id,ps));
+ break;
+#endif
+
+#if !defined(DOS) && !defined(OS2)
+ case F_ALLOW_TALK :
+ if(F_ON(f->id, ps))
+ allow_talk(ps);
+ else
+ disallow_talk(ps);
+
+ break;
+#endif
+
+ case F_PASS_CONTROL_CHARS :
+ ps->pass_ctrl_chars = F_ON(F_PASS_CONTROL_CHARS,ps_global) ? 1 : 0;
+ break;
+
+ case F_PASS_C1_CONTROL_CHARS :
+ ps->pass_c1_ctrl_chars = F_ON(F_PASS_C1_CONTROL_CHARS,ps_global) ? 1 : 0;
+ break;
+
+#ifdef MOUSE
+ case F_ENABLE_MOUSE :
+ if(F_ON(f->id, ps)){
+ init_mouse();
+ if(!mouseexist())
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ "Mouse tracking still off ($DISPLAY variable set?)");
+ }
+ else
+ end_mouse();
+
+ break;
+#endif
+ }
+
+ if(just_flip_value){
+ if(cl->value && cl->value[0])
+ cl->value[1] = (cl->value[1] == ' ') ? 'X' : ' ';
+ }
+ else{
+ /*
+ * This fork is only called from the checkbox_tool, which has
+ * varmem set to index correctly and cl->var set correctly.
+ */
+ if(cl->value)
+ fs_give((void **)&cl->value);
+
+ cl->value = pretty_value(ps, cl);
+ }
+}
+
+
+/*
+ * new_confline - create new CONF_S zero it out, and insert it after current.
+ * NOTE current gets set to the new CONF_S too!
+ */
+CONF_S *
+new_confline(CONF_S **current)
+{
+ CONF_S *p;
+
+ p = (CONF_S *)fs_get(sizeof(CONF_S));
+ memset((void *)p, 0, sizeof(CONF_S));
+ if(current){
+ if(*current){
+ p->next = (*current)->next;
+ (*current)->next = p;
+ p->prev = *current;
+ if(p->next)
+ p->next->prev = p;
+ }
+
+ *current = p;
+ }
+
+ return(p);
+}
+
+
+/*
+ *
+ */
+void
+snip_confline(CONF_S **p)
+{
+ CONF_S *q;
+
+ /*
+ * Be careful. We need this line because the
+ * q->prev->next = ...
+ * may change q itself if &q == &q->prev->next.
+ * Then the use of q in the next line is wrong.
+ * That's what happens if we pass in the address of
+ * some ->next and use *p directly instead of q.
+ */
+ q = *p;
+
+ if(q){
+ /* Yank it from the linked list */
+ if(q->prev)
+ q->prev->next = q->next;
+
+ if(q->next)
+ q->next->prev = q->prev;
+
+ /* Then free up it's memory */
+ q->prev = q->next = NULL;
+ free_conflines(&q);
+ }
+}
+
+
+/*
+ *
+ */
+void
+free_conflines(CONF_S **p)
+{
+ if(*p){
+ free_conflines(&(*p)->next);
+
+ if((*p)->varname)
+ fs_give((void **) &(*p)->varname);
+
+ if((*p)->value)
+ fs_give((void **) &(*p)->value);
+
+ fs_give((void **) p);
+ }
+}
+
+
+/*
+ *
+ */
+CONF_S *
+first_confline(CONF_S *p)
+{
+ while(p && p->prev)
+ p = p->prev;
+
+ return(p);
+}
+
+
+/*
+ * First selectable confline.
+ */
+CONF_S *
+first_sel_confline(CONF_S *p)
+{
+ for(p = first_confline(p); p && (p->flags&CF_NOSELECT); p=next_confline(p))
+ ;/* do nothing */
+
+ return(p);
+}
+
+
+/*
+ *
+ */
+CONF_S *
+last_confline(CONF_S *p)
+{
+ while(p && p->next)
+ p = p->next;
+
+ return(p);
+}
+
+
+/*
+ *
+ */
+int
+fixed_var(struct variable *v, char *action, char *name)
+{
+ char **lval;
+
+ if(v && v->is_fixed
+ && (!v->is_list
+ || ((lval=v->fixed_val.l) && lval[0]
+ && strcmp(INHERIT, lval[0]) != 0))){
+ q_status_message2(SM_ORDER, 3, 3,
+ "Can't %s sys-admin defined %s.",
+ action ? action : "change", name ? name : "value");
+ return(1);
+ }
+
+ return(0);
+}
+
+
+void
+exception_override_warning(struct variable *v)
+{
+ char **lval;
+
+ /* if exceptions config file exists and we're not editing it */
+ if(v && (ps_global->ew_for_except_vars != Main) && (ew == Main)){
+ if((!v->is_list && PVAL(v, ps_global->ew_for_except_vars)) ||
+ (v->is_list && (lval=LVAL(v, ps_global->ew_for_except_vars)) &&
+ lval[0] && strcmp(INHERIT, lval[0]) != 0))
+ q_status_message1(SM_ORDER, 3, 3,
+ _("Warning: \"%s\" is overridden in your exceptions configuration"),
+ v->name);
+ }
+}
+
+
+void
+offer_to_fix_pinerc(struct pine *ps)
+{
+ struct variable *v;
+ char prompt[300];
+ char *p, *q;
+ char **list;
+ char **list_fixed;
+ int rv = 0, write_main = 0, write_post = 0;
+ int i, k, j, need, exc;
+ char *clear = ": delete it";
+ char ***plist;
+
+ dprint((4, "offer_to_fix_pinerc()\n"));
+
+ ps->fix_fixed_warning = 0; /* so we only ask first time */
+
+ if(ps->readonly_pinerc)
+ return;
+
+ set_titlebar(_("FIXING PINERC"), ps->mail_stream,
+ ps->context_current,
+ ps->cur_folder, ps->msgmap, 1, FolderName, 0, 0, NULL);
+
+ if(want_to(_("Some of your options conflict with site policy. Investigate"),
+ 'y', 'n', NO_HELP, WT_FLUSH_IN) != 'y')
+ return;
+
+/* space want_to requires in addition to the string you pass in */
+#define WANTTO_SPACE 6
+ need = WANTTO_SPACE + utf8_width(clear);
+
+ for(v = ps->vars; v->name; v++){
+ if(!v->is_fixed ||
+ !v->is_user ||
+ v->is_obsolete ||
+ v == &ps->vars[V_FEATURE_LIST]) /* handle feature-list below */
+ continue;
+
+ prompt[0] = '\0';
+
+ if(v->is_list &&
+ (v->post_user_val.l || v->main_user_val.l)){
+ char **active_list;
+
+ active_list = v->post_user_val.l ? v->post_user_val.l
+ : v->main_user_val.l;
+ if(*active_list){
+ snprintf(prompt, sizeof(prompt), _("Your setting for %s is "), v->name);
+ prompt[sizeof(prompt)-1] = '\0';
+ p = prompt + strlen(prompt);
+ for(i = 0; active_list[i]; i++){
+ if(utf8_width(prompt) > ps->ttyo->screen_cols - need)
+ break;
+ if(i && sizeof(prompt)-(p-prompt) > 0)
+ *p++ = ',';
+
+ sstrncpy(&p, active_list[i], sizeof(prompt)-(p-prompt));
+ if(sizeof(prompt)-(p-prompt) > 0)
+ *p = '\0';
+
+ prompt[sizeof(prompt)-1] = '\0';
+ }
+
+ if(sizeof(prompt)-(p-prompt) > 0)
+ *p = '\0';
+ }
+ else
+ snprintf(prompt, sizeof(prompt), _("Your setting for %s is %s"), v->name, _(empty_val2));
+ }
+ else{
+ if(v->post_user_val.p || v->main_user_val.p){
+ char *active_var;
+
+ active_var = v->post_user_val.p ? v->post_user_val.p
+ : v->main_user_val.p;
+ if(*active_var){
+ snprintf(prompt, sizeof(prompt), _("Your setting for %s is %s"),
+ v->name, active_var);
+ }
+ else{
+ snprintf(prompt, sizeof(prompt), _("Your setting for %s is %s"),
+ v->name, _(empty_val2));
+ }
+ }
+ }
+
+ prompt[sizeof(prompt)-1] = '\0';
+
+ if(*prompt){
+ if(utf8_width(prompt) > ps->ttyo->screen_cols - need)
+ (void) utf8_truncate(prompt, ps->ttyo->screen_cols - need);
+
+ (void) strncat(prompt, clear, sizeof(prompt)-strlen(prompt)-1);
+ prompt[sizeof(prompt)-1] = '\0';
+ if(want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){
+ if(v->is_list){
+ if(v->main_user_val.l)
+ write_main++;
+ if(v->post_user_val.l)
+ write_post++;
+ }
+ else{
+ if(v->main_user_val.p)
+ write_main++;
+ if(v->post_user_val.p)
+ write_post++;
+ }
+
+ if(delete_user_vals(v))
+ rv++;
+ }
+ }
+ }
+
+
+ /*
+ * As always, feature-list has to be handled separately.
+ */
+ exc = (ps->ew_for_except_vars != Main);
+ v = &ps->vars[V_FEATURE_LIST];
+ list_fixed = v->fixed_val.l;
+
+ for(j = 0; j < 2; j++){
+ plist = (j==0) ? &v->main_user_val.l : &v->post_user_val.l;
+ list = *plist;
+ if(list){
+ for(i = 0; list[i]; i++){
+ p = list[i];
+ if(!struncmp(p, "no-", 3))
+ p += 3;
+ for(k = 0; list_fixed && list_fixed[k]; k++){
+ q = list_fixed[k];
+ if(!struncmp(q, "no-", 3))
+ q += 3;
+ if(!strucmp(q, p) && strucmp(list[i], list_fixed[k])){
+ snprintf(prompt, sizeof(prompt), "Your %s is %s%s, fixed value is %s",
+ p, p == list[i] ? _("ON") : _("OFF"),
+ exc ? ((plist == &v->main_user_val.l) ? ""
+ : " in postload-config")
+ : "",
+ q == list_fixed[k] ? _("ON") : _("OFF"));
+
+ prompt[sizeof(prompt)-1] = '\0';
+ if(utf8_width(prompt) > ps->ttyo->screen_cols - need)
+ (void) utf8_truncate(prompt, ps->ttyo->screen_cols - need);
+
+ (void) strncat(prompt, clear, sizeof(prompt)-strlen(prompt)-1);
+ prompt[sizeof(prompt)-1] = '\0';
+ if(want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){
+ rv++;
+
+ if(plist == &v->main_user_val.l)
+ write_main++;
+ else
+ write_post++;
+
+ /*
+ * Clear the feature from the user's pinerc
+ * so that we'll stop bothering them when they
+ * start up Pine.
+ */
+ clear_feature(plist, p);
+
+ /*
+ * clear_feature scoots the list up, so if list[i] was
+ * the last one going in, now it is the end marker. We
+ * just decrement i so that it will get incremented and
+ * then test == 0 in the for loop. We could just goto
+ * outta_here to accomplish the same thing.
+ */
+ if(!list[i])
+ i--;
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ if(write_main)
+ write_pinerc(ps, Main, WRP_NONE);
+ if(write_post)
+ write_pinerc(ps, Post, WRP_NONE);
+
+ return;
+}
+
+
+/*
+ * Adjust side effects that happen because variable changes values.
+ *
+ * Var->user_val should be set to the new value before calling this.
+ */
+void
+fix_side_effects(struct pine *ps, struct variable *var, int revert)
+{
+ int val = 0;
+ char **v, *q, **apval;
+ struct variable *vars = ps->vars;
+
+ /* move this up here so we get the Using default message */
+ if(var == &ps->vars[V_PERSONAL_NAME]){
+ if(!(var->main_user_val.p ||
+ var->post_user_val.p) && ps->ui.fullname){
+ if(var->current_val.p)
+ fs_give((void **)&var->current_val.p);
+
+ var->current_val.p = cpystr(ps->ui.fullname);
+ }
+ }
+
+ if(!revert
+ && ((!var->is_fixed
+ && !var->is_list
+ && !(var->main_user_val.p ||
+ var->post_user_val.p)
+ && var->current_val.p)
+ ||
+ (!var->is_fixed
+ && var->is_list
+ && !(var->main_user_val.l ||
+ var->post_user_val.l)
+ && var->current_val.l)))
+ q_status_message(SM_ORDER,0,3,_("Using default value"));
+
+ if(var == &ps->vars[V_USER_DOMAIN]){
+ char *p, *q;
+
+ if(ps->VAR_USER_DOMAIN
+ && ps->VAR_USER_DOMAIN[0]
+ && (p = strrindex(ps->VAR_USER_DOMAIN, '@'))){
+ if(*(++p)){
+ if(!revert)
+ q_status_message2(SM_ORDER, 3, 5,
+ _("User-Domain (%s) cannot contain \"@\"; using %s"),
+ ps->VAR_USER_DOMAIN, p);
+ q = ps->VAR_USER_DOMAIN;
+ while((*q++ = *p++) != '\0')
+ ;/* do nothing */
+ }
+ else{
+ if(!revert)
+ q_status_message1(SM_ORDER, 3, 5,
+ _("User-domain (%s) cannot contain \"@\"; deleting"),
+ ps->VAR_USER_DOMAIN);
+
+ if(ps->vars[V_USER_DOMAIN].post_user_val.p){
+ fs_give((void **)&ps->vars[V_USER_DOMAIN].post_user_val.p);
+ set_current_val(&vars[V_USER_DOMAIN], TRUE, TRUE);
+ }
+
+ if(ps->VAR_USER_DOMAIN
+ && ps->VAR_USER_DOMAIN[0]
+ && (p = strrindex(ps->VAR_USER_DOMAIN, '@'))){
+ if(ps->vars[V_USER_DOMAIN].main_user_val.p){
+ fs_give((void **)&ps->vars[V_USER_DOMAIN].main_user_val.p);
+ set_current_val(&vars[V_USER_DOMAIN], TRUE, TRUE);
+ }
+ }
+ }
+ }
+
+ /*
+ * Reset various pointers pertaining to domain name and such...
+ */
+ init_hostname(ps);
+ }
+ else if(var == &ps->vars[V_INBOX_PATH]){
+ /*
+ * fixup the inbox path based on global/default values...
+ */
+ init_inbox_mapping(ps->VAR_INBOX_PATH, ps->context_list);
+
+ if(!strucmp(ps->cur_folder, ps->inbox_name) && ps->mail_stream
+ && strcmp(ps->VAR_INBOX_PATH, ps->mail_stream->mailbox)){
+ /*
+ * If we currently have "inbox" open and the mailbox name
+ * doesn't match, reset the current folder's name and
+ * remove the SP_INBOX flag.
+ */
+ strncpy(ps->cur_folder, ps->mail_stream->mailbox,
+ sizeof(ps->cur_folder)-1);
+ ps->cur_folder[sizeof(ps->cur_folder)-1] = '\0';
+ sp_set_fldr(ps->mail_stream, ps->cur_folder);
+ sp_unflag(ps->mail_stream, SP_INBOX);
+ ps->mangled_header = 1;
+ }
+ else if(sp_inbox_stream()
+ && strcmp(ps->VAR_INBOX_PATH, sp_inbox_stream()->original_mailbox)){
+ MAILSTREAM *m = sp_inbox_stream();
+
+ /*
+ * if we don't have inbox directly open, but have it
+ * open for new mail notification, close the stream like
+ * any other ordinary folder, and clean up...
+ */
+ if(m){
+ sp_unflag(m, SP_PERMLOCKED | SP_INBOX);
+ sp_set_fldr(m, m->mailbox);
+ expunge_and_close(m, NULL, EC_NONE);
+ }
+ }
+ }
+ else if(var == &ps->vars[V_INCCHECKTIMEO]){
+ int old_value = ps->inc_check_timeout;
+
+ if(SVAR_INC_CHECK_TIMEO(ps, old_value, tmp_20k_buf, SIZEOF_20KBUF)){
+ if(!revert)
+ q_status_message(SM_ORDER, 3, 5, tmp_20k_buf);
+ }
+ else
+ ps->inc_check_timeout = old_value;
+
+ if(!revert && (F_OFF(F_ENABLE_INCOMING, ps) || F_OFF(F_ENABLE_INCOMING_CHECKING, ps)))
+ q_status_message(SM_ORDER, 0, 3, _("This option has no effect without Enable-Incoming-Folders-Checking"));
+ }
+ else if(var == &ps->vars[V_INCCHECKINTERVAL]){
+ int old_value = ps->inc_check_interval;
+
+ if(SVAR_INC_CHECK_INTERV(ps, old_value, tmp_20k_buf, SIZEOF_20KBUF)){
+ if(!revert)
+ q_status_message(SM_ORDER, 3, 5, tmp_20k_buf);
+ }
+ else
+ ps->inc_check_interval = old_value;
+
+ if(!revert && (F_OFF(F_ENABLE_INCOMING, ps) || F_OFF(F_ENABLE_INCOMING_CHECKING, ps)))
+ q_status_message(SM_ORDER, 0, 3, _("This option has no effect without Enable-Incoming-Folders-Checking"));
+ }
+ else if(var == &ps->vars[V_INC2NDCHECKINTERVAL]){
+ int old_value = ps->inc_second_check_interval;
+
+ if(SVAR_INC_2NDCHECK_INTERV(ps, old_value, tmp_20k_buf, SIZEOF_20KBUF)){
+ if(!revert)
+ q_status_message(SM_ORDER, 3, 5, tmp_20k_buf);
+ }
+ else
+ ps->inc_second_check_interval = old_value;
+
+ if(!revert && (F_OFF(F_ENABLE_INCOMING, ps) || F_OFF(F_ENABLE_INCOMING_CHECKING, ps)))
+ q_status_message(SM_ORDER, 0, 3, _("This option has no effect without Enable-Incoming-Folders-Checking"));
+ }
+ else if(var == &ps->vars[V_INCCHECKLIST]){
+ if(ps->context_list && ps->context_list->use & CNTXT_INCMNG)
+ reinit_incoming_folder_list(ps, ps->context_list);
+
+ if(!revert && (F_OFF(F_ENABLE_INCOMING, ps) || F_OFF(F_ENABLE_INCOMING_CHECKING, ps)))
+ q_status_message(SM_ORDER, 0, 3, _("This option has no effect without Enable-Incoming-Folders-Checking"));
+ }
+ else if(var == &ps->vars[V_ADDRESSBOOK] ||
+ var == &ps->vars[V_GLOB_ADDRBOOK] ||
+#ifdef ENABLE_LDAP
+ var == &ps->vars[V_LDAP_SERVERS] ||
+#endif
+ var == &ps->vars[V_ABOOK_FORMATS]){
+ addrbook_reset();
+ }
+ else if(var == &ps->vars[V_INDEX_FORMAT]){
+ reset_index_format();
+ clear_index_cache(ps->mail_stream, 0);
+ }
+ else if(var == &ps->vars[V_DEFAULT_FCC] ||
+ var == &ps->vars[V_DEFAULT_SAVE_FOLDER]){
+ init_save_defaults();
+ }
+ else if(var == &ps->vars[V_KW_BRACES] ||
+ var == &ps->vars[V_OPENING_SEP] ||
+ var == &ps->vars[V_ALT_ADDRS]){
+ clear_index_cache(ps->mail_stream, 0);
+ }
+ else if(var == &ps->vars[V_KEYWORDS]){
+ if(ps_global->keywords)
+ free_keyword_list(&ps_global->keywords);
+
+ if(var->current_val.l && var->current_val.l[0])
+ ps_global->keywords = init_keyword_list(var->current_val.l);
+
+ clear_index_cache(ps->mail_stream, 0);
+ }
+ else if(var == &ps->vars[V_INIT_CMD_LIST]){
+ if(!revert)
+ q_status_message(SM_ASYNC, 0, 3,
+ _("Initial command changes will affect your next Alpine session."));
+ }
+ else if(var == &ps->vars[V_VIEW_HEADERS]){
+ ps->view_all_except = 0;
+ if(ps->VAR_VIEW_HEADERS)
+ for(v = ps->VAR_VIEW_HEADERS; (q = *v) != NULL; v++)
+ if(q[0]){
+ char *p;
+
+ removing_leading_white_space(q);
+ /* look for colon or space or end */
+ for(p = q; *p && !isspace((unsigned char)*p) && *p != ':'; p++)
+ ;/* do nothing */
+
+ *p = '\0';
+ if(strucmp(q, ALL_EXCEPT) == 0)
+ ps->view_all_except = 1;
+ }
+ }
+ else if(var == &ps->vars[V_OVERLAP]){
+ int old_value = ps->viewer_overlap;
+
+ if(SVAR_OVERLAP(ps, old_value, tmp_20k_buf, SIZEOF_20KBUF)){
+ if(!revert)
+ q_status_message(SM_ORDER, 3, 5, tmp_20k_buf);
+ }
+ else
+ ps->viewer_overlap = old_value;
+ }
+#ifdef SMIME
+ else if(smime_related_var(ps, var)){
+ smime_deinit();
+ }
+#endif /* SMIME */
+ else if(var == &ps->vars[V_MAXREMSTREAM]){
+ int old_value = ps->s_pool.max_remstream;
+
+ if(SVAR_MAXREMSTREAM(ps, old_value, tmp_20k_buf, SIZEOF_20KBUF)){
+ if(!revert )
+ q_status_message(SM_ORDER, 3, 5, tmp_20k_buf);
+ }
+ else
+ ps->s_pool.max_remstream = old_value;
+
+ dprint((9, "max_remstream goes to %d\n",
+ ps->s_pool.max_remstream));
+ }
+#ifndef _WINDOWS
+ else if(var == &ps->vars[V_CHAR_SET]){
+ char *err = NULL;
+
+ if(F_ON(F_USE_SYSTEM_TRANS, ps)){
+ if(!revert)
+ q_status_message(SM_ORDER, 5, 5, _("This change has no effect because feature Use-System-Translation is on"));
+ }
+ else{
+ if(reset_character_set_stuff(&err) == -1)
+ panic(err ? err : "trouble with Character-Set");
+ else if(err){
+ q_status_message(SM_ORDER | SM_DING, 3, 5, err);
+ fs_give((void **) &err);
+ }
+ }
+ }
+ else if(var == &ps->vars[V_KEY_CHAR_SET]){
+ char *err = NULL;
+
+ if(F_ON(F_USE_SYSTEM_TRANS, ps)){
+ if(!revert)
+ q_status_message(SM_ORDER, 5, 5, _("This change has no effect because feature Use-System-Translation is on"));
+ }
+ else{
+ if(reset_character_set_stuff(&err) == -1)
+ panic(err ? err : "trouble with Character-Set");
+ else if(err){
+ q_status_message(SM_ORDER | SM_DING, 3, 5, err);
+ fs_give((void **) &err);
+ }
+ }
+ }
+#endif /* ! _WINDOWS */
+ else if(var == &ps->vars[V_POST_CHAR_SET]){
+ update_posting_charset(ps, revert);
+ }
+ else if(var == &ps->vars[V_MARGIN]){
+ int old_value = ps->scroll_margin;
+
+ if(SVAR_MARGIN(ps, old_value, tmp_20k_buf, SIZEOF_20KBUF)){
+ if(!revert)
+ q_status_message(SM_ORDER, 3, 5, tmp_20k_buf);
+ }
+ else
+ ps->scroll_margin = old_value;
+ }
+ else if(var == &ps->vars[V_DEADLETS]){
+ int old_value = ps->deadlets;
+
+ if(SVAR_DEADLETS(ps, old_value, tmp_20k_buf, SIZEOF_20KBUF)){
+ if(!revert)
+ q_status_message(SM_ORDER, 3, 5, tmp_20k_buf);
+ }
+ else
+ ps->deadlets = old_value;
+ }
+ else if(var == &ps->vars[V_FILLCOL]){
+ if(SVAR_FILLCOL(ps, ps->composer_fillcol, tmp_20k_buf, SIZEOF_20KBUF)){
+ if(!revert)
+ q_status_message(SM_ORDER, 3, 5, tmp_20k_buf);
+ }
+ }
+ else if(var == &ps->vars[V_QUOTE_SUPPRESSION]){
+ val = ps->quote_suppression_threshold;
+ if(val < Q_SUPP_LIMIT && val > 0)
+ val = -val;
+
+ if(ps->VAR_QUOTE_SUPPRESSION
+ && SVAR_QUOTE_SUPPRESSION(ps, val, tmp_20k_buf, SIZEOF_20KBUF)){
+ if(!revert)
+ q_status_message(SM_ORDER, 3, 5, tmp_20k_buf);
+ }
+ else{
+ if(val > 0 && val < Q_SUPP_LIMIT){
+ if(!revert){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Ignoring Quote-Suppression-Threshold value of %s, see help"), ps->VAR_QUOTE_SUPPRESSION);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ q_status_message(SM_ORDER, 3, 5, tmp_20k_buf);
+ }
+ }
+ else{
+ if(val < 0 && val != Q_DEL_ALL)
+ ps->quote_suppression_threshold = -val;
+ else
+ ps->quote_suppression_threshold = val;
+ }
+ }
+ }
+ else if(var == &ps->vars[V_STATUS_MSG_DELAY]){
+ if(SVAR_MSGDLAY(ps, ps->status_msg_delay, tmp_20k_buf, SIZEOF_20KBUF)){
+ if(!revert)
+ q_status_message(SM_ORDER, 3, 5, tmp_20k_buf);
+ }
+ }
+ else if(var == &ps->vars[V_ACTIVE_MSG_INTERVAL]){
+ if(SVAR_ACTIVEINTERVAL(ps, ps->active_status_interval, tmp_20k_buf, SIZEOF_20KBUF)){
+ if(!revert)
+ q_status_message(SM_ORDER, 3, 5, tmp_20k_buf);
+ }
+ else{
+ busy_cue(_("Active Example"), NULL, 0);
+ sleep(5);
+ cancel_busy_cue(-1);
+ }
+ }
+#if !defined(DOS) && !defined(OS2) && !defined(LEAVEOUTFIFO)
+ else if(var == &ps->vars[V_FIFOPATH]){
+ init_newmailfifo(ps->VAR_FIFOPATH);
+ }
+#endif
+ else if(var == &ps->vars[V_NMW_WIDTH]){
+ int old_value = ps->nmw_width;
+
+ if(SVAR_NMW_WIDTH(ps, old_value, tmp_20k_buf, SIZEOF_20KBUF)){
+ if(!revert )
+ q_status_message(SM_ORDER, 3, 5, tmp_20k_buf);
+ }
+ else{
+#ifdef _WINDOWS
+ if(old_value != ps->nmw_width)
+ mswin_setnewmailwidth(old_value); /* actually the new value */
+#endif
+ ps->nmw_width = old_value;
+ }
+ }
+ else if(var == &ps->vars[V_TCPOPENTIMEO]){
+ val = 30;
+ if(!revert)
+ if(ps->VAR_TCPOPENTIMEO && SVAR_TCP_OPEN(ps, val, tmp_20k_buf, SIZEOF_20KBUF))
+ q_status_message(SM_ORDER, 3, 5, tmp_20k_buf);
+ }
+ else if(var == &ps->vars[V_TCPREADWARNTIMEO]){
+ val = 15;
+ if(!revert)
+ if(ps->VAR_TCPREADWARNTIMEO && SVAR_TCP_READWARN(ps,val,tmp_20k_buf, SIZEOF_20KBUF))
+ q_status_message(SM_ORDER, 3, 5, tmp_20k_buf);
+ }
+ else if(var == &ps->vars[V_TCPWRITEWARNTIMEO]){
+ val = 0;
+ if(!revert)
+ if(ps->VAR_TCPWRITEWARNTIMEO && SVAR_TCP_WRITEWARN(ps,val,tmp_20k_buf, SIZEOF_20KBUF))
+ q_status_message(SM_ORDER, 3, 5, tmp_20k_buf);
+ }
+ else if(var == &ps->vars[V_TCPQUERYTIMEO]){
+ val = 60;
+ if(!revert)
+ if(ps->VAR_TCPQUERYTIMEO && SVAR_TCP_QUERY(ps, val, tmp_20k_buf, SIZEOF_20KBUF))
+ q_status_message(SM_ORDER, 3, 5, tmp_20k_buf);
+ }
+ else if(var == &ps->vars[V_RSHOPENTIMEO]){
+ val = 15;
+ if(!revert)
+ if(ps->VAR_RSHOPENTIMEO && SVAR_RSH_OPEN(ps, val, tmp_20k_buf, SIZEOF_20KBUF))
+ q_status_message(SM_ORDER, 3, 5, tmp_20k_buf);
+ }
+ else if(var == &ps->vars[V_SSHOPENTIMEO]){
+ val = 15;
+ if(!revert)
+ if(ps->VAR_SSHOPENTIMEO && SVAR_SSH_OPEN(ps, val, tmp_20k_buf, SIZEOF_20KBUF))
+ q_status_message(SM_ORDER, 3, 5, tmp_20k_buf);
+ }
+ else if(var == &ps->vars[V_SIGNATURE_FILE]){
+ if(ps->VAR_OPER_DIR && ps->VAR_SIGNATURE_FILE &&
+ is_absolute_path(ps->VAR_SIGNATURE_FILE) &&
+ !in_dir(ps->VAR_OPER_DIR, ps->VAR_SIGNATURE_FILE)){
+ char *e;
+ size_t l;
+
+ l = strlen(ps->VAR_OPER_DIR) + 100;
+ e = (char *) fs_get((l+1) * sizeof(char));
+ snprintf(e, l+1, _("Warning: Sig file can't be outside of %s"),
+ ps->VAR_OPER_DIR);
+ e[l] = '\0';
+ q_status_message(SM_ORDER, 3, 6, e);
+ fs_give((void **)&e);
+ }
+ }
+ else if(var == &ps->vars[V_OPER_DIR]){
+ if(ps->VAR_OPER_DIR && !ps->VAR_OPER_DIR[0]){
+ q_status_message(SM_ORDER, 3, 5, "Operating-dir is turned off.");
+ fs_give((void **)&ps->vars[V_OPER_DIR].current_val.p);
+ if(ps->vars[V_OPER_DIR].fixed_val.p)
+ fs_give((void **)&ps->vars[V_OPER_DIR].fixed_val.p);
+ if(ps->vars[V_OPER_DIR].global_val.p)
+ fs_give((void **)&ps->vars[V_OPER_DIR].global_val.p);
+ if(ps->vars[V_OPER_DIR].cmdline_val.p)
+ fs_give((void **)&ps->vars[V_OPER_DIR].cmdline_val.p);
+ if(ps->vars[V_OPER_DIR].post_user_val.p)
+ fs_give((void **)&ps->vars[V_OPER_DIR].post_user_val.p);
+ if(ps->vars[V_OPER_DIR].main_user_val.p)
+ fs_give((void **)&ps->vars[V_OPER_DIR].main_user_val.p);
+ }
+ }
+ else if(var == &ps->vars[V_MAILCHECK]){
+ int timeo = 15;
+ if(SVAR_MAILCHK(ps, timeo, tmp_20k_buf, SIZEOF_20KBUF)){
+ set_input_timeout(15);
+ if(!revert)
+ q_status_message(SM_ORDER, 3, 5, tmp_20k_buf);
+ }
+ else{
+ set_input_timeout(timeo);
+ if(get_input_timeout() == 0 && !revert){
+ q_status_message(SM_ORDER, 4, 6,
+ _("Warning: automatic new mail checking and mailbox checkpointing is disabled"));
+ if(ps->VAR_INBOX_PATH && ps->VAR_INBOX_PATH[0] == '{')
+ q_status_message(SM_ASYNC, 3, 6,
+ _("Warning: Mail-Check-Interval=0 may cause IMAP server connection to time out"));
+ }
+ }
+ }
+ else if(var == &ps->vars[V_MAILCHECKNONCURR]){
+ val = (int) ps->check_interval_for_noncurr;
+ if(ps->VAR_MAILCHECKNONCURR
+ && SVAR_MAILCHKNONCURR(ps, val, tmp_20k_buf, SIZEOF_20KBUF)){
+ if(!revert)
+ q_status_message(SM_ORDER, 3, 5, tmp_20k_buf);
+ }
+ else
+ ps->check_interval_for_noncurr = (time_t) val;
+ }
+ else if(var == &ps->vars[V_MAILDROPCHECK]){
+ long rvl;
+
+ rvl = 60L;
+ if(ps->VAR_MAILDROPCHECK && SVAR_MAILDCHK(ps, rvl, tmp_20k_buf, SIZEOF_20KBUF))
+ q_status_message(SM_ORDER, 3, 5, tmp_20k_buf);
+ else{
+ if(rvl == 0L)
+ rvl = (60L * 60L * 24L * 100L); /* 100 days */
+
+ if(rvl >= 60L)
+ mail_parameters(NULL, SET_SNARFINTERVAL, (void *) rvl);
+ }
+ }
+ else if(var == &ps->vars[V_NNTPRANGE]){
+ long rvl;
+
+ rvl = 0L;
+ if(ps->VAR_NNTPRANGE && SVAR_NNTPRANGE(ps, rvl, tmp_20k_buf, SIZEOF_20KBUF))
+ q_status_message(SM_ORDER, 3, 5, tmp_20k_buf);
+ else{
+ if(rvl >= 0L)
+ mail_parameters(NULL, SET_NNTPRANGE, (void *) rvl);
+ }
+ }
+ else if(var == &ps->vars[V_CUSTOM_HDRS] || var == &ps->vars[V_COMP_HDRS]){
+ /* this will give warnings about headers that can't be changed */
+ if(!revert && var->current_val.l && var->current_val.l[0])
+ customized_hdr_setup(NULL, var->current_val.l, UseAsDef);
+ }
+#if defined(DOS) || defined(OS2)
+ else if(var == &ps->vars[V_FOLDER_EXTENSION]){
+ mail_parameters(NULL, SET_EXTENSION,
+ (void *)var->current_val.p);
+ }
+ else if(var == &ps->vars[V_NEWSRC_PATH]){
+ if(var->current_val.p && var->current_val.p[0])
+ mail_parameters(NULL, SET_NEWSRC,
+ (void *)var->current_val.p);
+ }
+#endif
+ else if(revert && standard_radio_var(ps, var)){
+
+ cur_rule_value(var, TRUE, FALSE);
+ if(var == &ps_global->vars[V_AB_SORT_RULE])
+ addrbook_redo_sorts();
+ else if(var == &ps_global->vars[V_THREAD_INDEX_STYLE]){
+ clear_index_cache(ps_global->mail_stream, 0);
+ set_lflags(ps_global->mail_stream, ps_global->msgmap,
+ MN_COLL | MN_CHID, 0);
+ if(SORT_IS_THREADED(ps_global->msgmap)
+ && (SEP_THRDINDX() || COLL_THRDS()))
+ collapse_threads(ps_global->mail_stream, ps_global->msgmap, NULL);
+
+ adjust_cur_to_visible(ps_global->mail_stream, ps_global->msgmap);
+ }
+#ifndef _WINDOWS
+ else if(var == &ps->vars[V_COLOR_STYLE]){
+ pico_toggle_color(0);
+ switch(ps->color_style){
+ case COL_NONE:
+ case COL_TERMDEF:
+ pico_set_color_options(pico_trans_color() ? COLOR_TRANS_OPT : 0);
+ break;
+ case COL_ANSI8:
+ pico_set_color_options(COLOR_ANSI8_OPT|COLOR_TRANS_OPT);
+ break;
+ case COL_ANSI16:
+ pico_set_color_options(COLOR_ANSI16_OPT|COLOR_TRANS_OPT);
+ break;
+ case COL_ANSI256:
+ pico_set_color_options(COLOR_ANSI256_OPT|COLOR_TRANS_OPT);
+ break;
+ }
+
+ if(ps->color_style != COL_NONE)
+ pico_toggle_color(1);
+
+ if(pico_usingcolor())
+ pico_set_normal_color();
+
+ clear_index_cache(ps_global->mail_stream, 0);
+ ClearScreen();
+ ps->mangled_screen = 1;
+ }
+#endif
+ }
+ else if(revert && var == &ps->vars[V_SORT_KEY]){
+ int def_sort_rev;
+
+ decode_sort(VAR_SORT_KEY, &ps->def_sort, &def_sort_rev);
+ ps->def_sort_rev = def_sort_rev;
+ }
+ else if(var == &ps->vars[V_THREAD_MORE_CHAR] ||
+ var == &ps->vars[V_THREAD_EXP_CHAR] ||
+ var == &ps->vars[V_THREAD_LASTREPLY_CHAR]){
+
+ if(var == &ps->vars[V_THREAD_LASTREPLY_CHAR] &&
+ !(var->current_val.p && var->current_val.p[0])){
+ if(var->current_val.p)
+ fs_give((void **) &var->current_val.p);
+
+ q_status_message1(SM_ORDER, 3, 5,
+ _("\"%s\" can't be Empty, using default"), var->name);
+
+ apval = APVAL(var, ew);
+ if(*apval)
+ fs_give((void **)apval);
+
+ set_current_val(var, FALSE, FALSE);
+
+ if(!(var->current_val.p && var->current_val.p[0]
+ && !var->current_val.p[1])){
+ if(var->current_val.p)
+ fs_give((void **) &var->current_val.p);
+
+ var->current_val.p = cpystr(DF_THREAD_LASTREPLY_CHAR);
+ }
+ }
+
+ if(var == &ps->vars[V_THREAD_MORE_CHAR] ||
+ var == &ps->vars[V_THREAD_EXP_CHAR] ||
+ var == &ps->vars[V_THREAD_LASTREPLY_CHAR]){
+ if(var->current_val.p && var->current_val.p[0] &&
+ var->current_val.p[1]){
+ q_status_message1(SM_ORDER, 3, 5,
+ "Only first character of \"%s\" is used",
+ var->name);
+ var->current_val.p[1] = '\0';
+ }
+
+ if(var->main_user_val.p && var->main_user_val.p[0] &&
+ var->main_user_val.p[1])
+ var->main_user_val.p[1] = '\0';
+
+ if(var->post_user_val.p && var->post_user_val.p[0] &&
+ var->post_user_val.p[1])
+ var->post_user_val.p[1] = '\0';
+ }
+
+ clear_index_cache(ps_global->mail_stream, 0);
+ set_need_format_setup(ps_global->mail_stream);
+ }
+ else if(var == &ps->vars[V_NNTP_SERVER]){
+ free_contexts(&ps_global->context_list);
+ init_folders(ps_global);
+ }
+ else if(var == &ps->vars[V_USE_ONLY_DOMAIN_NAME]){
+ init_hostname(ps);
+ }
+ else if(var == &ps->vars[V_PRINTER]){
+ if(!revert && ps->vars[V_PERSONAL_PRINT_COMMAND].is_fixed){
+ if(printer_value_check_and_adjust())
+ q_status_message1(SM_ORDER, 3, 5,
+ _("Can't set \"%s\" to that value, see Setup/Printer"),
+ pretty_var_name(var->name));
+ }
+ }
+ else if(var == &ps->vars[V_KW_COLORS] ||
+ var == &ps->vars[V_IND_PLUS_FORE_COLOR] ||
+ var == &ps->vars[V_IND_IMP_FORE_COLOR] ||
+ var == &ps->vars[V_IND_DEL_FORE_COLOR] ||
+ var == &ps->vars[V_IND_ANS_FORE_COLOR] ||
+ var == &ps->vars[V_IND_NEW_FORE_COLOR] ||
+ var == &ps->vars[V_IND_UNS_FORE_COLOR] ||
+ var == &ps->vars[V_IND_HIPRI_FORE_COLOR]||
+ var == &ps->vars[V_IND_LOPRI_FORE_COLOR]||
+ var == &ps->vars[V_IND_ARR_FORE_COLOR] ||
+ var == &ps->vars[V_IND_REC_FORE_COLOR] ||
+ var == &ps->vars[V_IND_FWD_FORE_COLOR] ||
+ var == &ps->vars[V_IND_OP_FORE_COLOR] ||
+ var == &ps->vars[V_IND_FROM_FORE_COLOR] ||
+ var == &ps->vars[V_IND_SUBJ_FORE_COLOR] ||
+ var == &ps->vars[V_IND_PLUS_BACK_COLOR] ||
+ var == &ps->vars[V_IND_IMP_BACK_COLOR] ||
+ var == &ps->vars[V_IND_DEL_BACK_COLOR] ||
+ var == &ps->vars[V_IND_ANS_BACK_COLOR] ||
+ var == &ps->vars[V_IND_NEW_BACK_COLOR] ||
+ var == &ps->vars[V_IND_UNS_BACK_COLOR] ||
+ var == &ps->vars[V_IND_ARR_BACK_COLOR] ||
+ var == &ps->vars[V_IND_REC_BACK_COLOR] ||
+ var == &ps->vars[V_IND_FWD_BACK_COLOR] ||
+ var == &ps->vars[V_IND_OP_BACK_COLOR] ||
+ var == &ps->vars[V_IND_FROM_BACK_COLOR] ||
+ var == &ps->vars[V_IND_SUBJ_BACK_COLOR]){
+ clear_index_cache(ps_global->mail_stream, 0);
+ }
+ else if(var == score_act_global_ptr){
+ int score;
+
+ score = atoi(var->current_val.p);
+ if(score < SCORE_MIN || score > SCORE_MAX){
+ q_status_message2(SM_ORDER, 3, 5,
+ _("Score Value must be in range %s to %s"),
+ comatose(SCORE_MIN), comatose(SCORE_MAX));
+ apval = APVAL(var, ew);
+ if(*apval)
+ fs_give((void **)apval);
+
+ set_current_val(var, FALSE, FALSE);
+ }
+ }
+ else if(var == scorei_pat_global_ptr || var == age_pat_global_ptr
+ || var == size_pat_global_ptr || var == cati_global_ptr){
+ apval = APVAL(var, ew);
+ if(*apval){
+ INTVL_S *iv;
+ iv = parse_intvl(*apval);
+ if(iv){
+ fs_give((void **) apval);
+ *apval = stringform_of_intvl(iv);
+ free_intvl(&iv);
+ }
+ else
+ fs_give((void **) apval);
+ }
+
+ set_current_val(var, FALSE, FALSE);
+ }
+ else if(var == &ps->vars[V_FEATURE_LIST]){
+ process_feature_list(ps, var->current_val.l, 0, 0, 0);
+ }
+ else if(!revert && (var == &ps->vars[V_LAST_TIME_PRUNE_QUESTION] ||
+ var == &ps->vars[V_REMOTE_ABOOK_HISTORY] ||
+ var == &ps->vars[V_REMOTE_ABOOK_VALIDITY] ||
+ var == &ps->vars[V_USERINPUTTIMEO] ||
+ var == &ps->vars[V_NEWS_ACTIVE_PATH] ||
+ var == &ps->vars[V_NEWS_SPOOL_DIR] ||
+ var == &ps->vars[V_INCOMING_FOLDERS] ||
+ var == &ps->vars[V_FOLDER_SPEC] ||
+ var == &ps->vars[V_NEWS_SPEC] ||
+ var == &ps->vars[V_DISABLE_DRIVERS] ||
+ var == &ps->vars[V_DISABLE_AUTHS] ||
+ var == &ps->vars[V_RSHPATH] ||
+ var == &ps->vars[V_RSHCMD] ||
+ var == &ps->vars[V_SSHCMD] ||
+ var == &ps->vars[V_SSHPATH])){
+ q_status_message2(SM_ASYNC, 0, 3,
+ _("Changes%s%s will affect your next Alpine session."),
+ var->name ? " to " : "", var->name ? var->name : "");
+ }
+
+ if(!revert && (var == &ps->vars[V_TCPOPENTIMEO] ||
+ var == &ps->vars[V_TCPREADWARNTIMEO] ||
+ var == &ps->vars[V_TCPWRITEWARNTIMEO] ||
+ var == &ps->vars[V_TCPQUERYTIMEO] ||
+ var == &ps->vars[V_RSHOPENTIMEO] ||
+ var == &ps->vars[V_SSHOPENTIMEO]))
+ q_status_message(SM_ASYNC, 0, 3,
+ _("Timeout changes will affect your next Alpine session."));
+}
+
+
+/*
+ * Compare saved user_val with current user_val to see if it changed.
+ * If any have changed, change it back and take the appropriate action.
+ */
+void
+revert_to_saved_config(struct pine *ps, SAVED_CONFIG_S *vsave, int allow_hard_to_config_remotely)
+{
+ struct variable *vreal;
+ SAVED_CONFIG_S *v;
+ int i, n;
+ int changed = 0;
+ char *pval, **apval, **lval, ***alval;
+
+ v = vsave;
+ for(vreal = ps->vars; vreal->name; vreal++,v++){
+ if(!save_include(ps, vreal, allow_hard_to_config_remotely))
+ continue;
+
+ changed = 0;
+ if(vreal->is_list){
+ lval = LVAL(vreal, ew);
+ alval = ALVAL(vreal, ew);
+
+ if((v->saved_user_val.l && !lval)
+ || (!v->saved_user_val.l && lval))
+ changed++;
+ else if(!v->saved_user_val.l && !lval)
+ ;/* no change, nothing to do */
+ else
+ for(i = 0; v->saved_user_val.l[i] || lval[i]; i++)
+ if((v->saved_user_val.l[i]
+ && (!lval[i]
+ || strcmp(v->saved_user_val.l[i], lval[i])))
+ ||
+ (!v->saved_user_val.l[i] && lval[i])){
+ changed++;
+ break;
+ }
+
+ if(changed){
+ char **list;
+
+ if(alval){
+ if(*alval)
+ free_list_array(alval);
+
+ /* copy back the original one */
+ if(v->saved_user_val.l){
+ list = v->saved_user_val.l;
+ n = 0;
+ /* count how many */
+ while(list[n])
+ n++;
+
+ *alval = (char **)fs_get((n+1) * sizeof(char *));
+
+ for(i = 0; i < n; i++)
+ (*alval)[i] = cpystr(v->saved_user_val.l[i]);
+
+ (*alval)[n] = NULL;
+ }
+ }
+ }
+ }
+ else{
+ pval = PVAL(vreal, ew);
+ apval = APVAL(vreal, ew);
+
+ if((v->saved_user_val.p &&
+ (!pval || strcmp(v->saved_user_val.p, pval))) ||
+ (!v->saved_user_val.p && pval)){
+ /* It changed, fix it */
+ changed++;
+ if(apval){
+ /* free the changed value */
+ if(*apval)
+ fs_give((void **)apval);
+
+ if(v->saved_user_val.p)
+ *apval = cpystr(v->saved_user_val.p);
+ }
+ }
+ }
+
+ if(changed){
+ if(vreal == &ps->vars[V_FEATURE_LIST])
+ set_feature_list_current_val(vreal);
+ else
+ set_current_val(vreal, TRUE, FALSE);
+
+ fix_side_effects(ps, vreal, 1);
+ }
+ }
+}
+
+
+SAVED_CONFIG_S *
+save_config_vars(struct pine *ps, int allow_hard_to_config_remotely)
+{
+ struct variable *vreal;
+ SAVED_CONFIG_S *vsave, *v;
+
+ vsave = (SAVED_CONFIG_S *)fs_get((V_LAST_VAR+1)*sizeof(SAVED_CONFIG_S));
+ memset((void *)vsave, 0, (V_LAST_VAR+1)*sizeof(SAVED_CONFIG_S));
+ v = vsave;
+ for(vreal = ps->vars; vreal->name; vreal++,v++){
+ if(!save_include(ps, vreal, allow_hard_to_config_remotely))
+ continue;
+
+ if(vreal->is_list){
+ int n, i;
+ char **list;
+
+ if(LVAL(vreal, ew)){
+ /* count how many */
+ n = 0;
+ list = LVAL(vreal, ew);
+ while(list[n])
+ n++;
+
+ v->saved_user_val.l = (char **)fs_get((n+1) * sizeof(char *));
+ memset((void *)v->saved_user_val.l, 0, (n+1)*sizeof(char *));
+ for(i = 0; i < n; i++)
+ v->saved_user_val.l[i] = cpystr(list[i]);
+
+ v->saved_user_val.l[n] = NULL;
+ }
+ }
+ else{
+ if(PVAL(vreal, ew))
+ v->saved_user_val.p = cpystr(PVAL(vreal, ew));
+ }
+ }
+
+ return(vsave);
+}
+
+
+void
+free_saved_config(struct pine *ps, SAVED_CONFIG_S **vsavep, int allow_hard_to_config_remotely)
+{
+ struct variable *vreal;
+ SAVED_CONFIG_S *v;
+
+ if(vsavep && *vsavep){
+ for(v = *vsavep, vreal = ps->vars; vreal->name; vreal++,v++){
+ if(!save_include(ps, vreal, allow_hard_to_config_remotely))
+ continue;
+
+ if(vreal->is_list){ /* free saved_user_val.l */
+ if(v && v->saved_user_val.l)
+ free_list_array(&v->saved_user_val.l);
+ }
+ else if(v && v->saved_user_val.p)
+ fs_give((void **)&v->saved_user_val.p);
+ }
+
+ fs_give((void **)vsavep);
+ }
+}
+
+
+/*
+ * Returns positive if any thing was actually deleted.
+ */
+int
+delete_user_vals(struct variable *v)
+{
+ int rv = 0;
+
+ if(v){
+ if(v->is_list){
+ if(v->post_user_val.l){
+ rv++;
+ free_list_array(&v->post_user_val.l);
+ }
+ if(v->main_user_val.l){
+ rv++;
+ free_list_array(&v->main_user_val.l);
+ }
+ }
+ else{
+ if(v->post_user_val.p){
+ rv++;
+ fs_give((void **)&v->post_user_val.p);
+ }
+ if(v->main_user_val.p){
+ rv++;
+ fs_give((void **)&v->main_user_val.p);
+ }
+ }
+ }
+
+ return(rv);
+}
+
+
+/*
+ * ../pith/conf.c required function
+ */
+int
+unexpected_pinerc_change(void)
+{
+ Writechar(BELL, 0);
+ if(want_to("Unexpected pinerc change! Overwrite with current config",
+ 'n', 0, NO_HELP, WT_FLUSH_IN) == 'n'){
+ return(-1); /* abort pinerc write */
+ }
+
+ return(0); /* overwrite */
+}
+
+
+#ifdef _WINDOWS
+
+/*----------------------------------------------------------------------
+ MSWin scroll callback. Called during scroll message processing.
+
+
+
+ Args: cmd - what type of scroll operation.
+ scroll_pos - paramter for operation.
+ used as position for SCROLL_TO operation.
+
+ Returns: TRUE - did the scroll operation.
+ FALSE - was not able to do the scroll operation.
+ ----*/
+int
+config_scroll_callback (cmd, scroll_pos)
+int cmd;
+long scroll_pos;
+{
+ switch (cmd) {
+ case MSWIN_KEY_SCROLLUPLINE:
+ config_scroll_down (scroll_pos);
+ break;
+
+ case MSWIN_KEY_SCROLLDOWNLINE:
+ config_scroll_up (scroll_pos);
+ break;
+
+ case MSWIN_KEY_SCROLLUPPAGE:
+ config_scroll_down (BODY_LINES(ps_global));
+ break;
+
+ case MSWIN_KEY_SCROLLDOWNPAGE:
+ config_scroll_up (BODY_LINES(ps_global));
+ break;
+
+ case MSWIN_KEY_SCROLLTO:
+ config_scroll_to_pos (scroll_pos);
+ break;
+ }
+
+ option_screen_redrawer();
+ fflush(stdout);
+
+ return(TRUE);
+}
+
+#endif /* _WINDOWS */
diff --git a/alpine/confscroll.h b/alpine/confscroll.h
new file mode 100644
index 00000000..4315c8e1
--- /dev/null
+++ b/alpine/confscroll.h
@@ -0,0 +1,114 @@
+/*
+ * $Id: confscroll.h 812 2007-11-10 01:00:15Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_CONFSCROLL_INCLUDED
+#define PINE_CONFSCROLL_INCLUDED
+
+
+#include "conftype.h"
+#include "../pith/conf.h"
+#include "../pith/state.h"
+
+
+#define BODY_LINES(X) ((X)->ttyo->screen_rows -HEADER_ROWS(X)-FOOTER_ROWS(X))
+
+#define R_SELD '*'
+
+#define EXIT_PMT "Commit changes (\"Yes\" replaces settings, \"No\" abandons changes)"
+
+
+/* another in a long line of hacks in this stuff */
+#define DSTRING "default ("
+#define VSTRING "value from fcc-name-rule"
+
+
+#define next_confline(p) ((p) ? (p)->next : NULL)
+#define prev_confline(p) ((p) ? (p)->prev : NULL)
+
+
+extern char *empty_val;
+extern char *empty_val2;
+
+
+extern struct variable *score_act_global_ptr,
+ *scorei_pat_global_ptr,
+ *age_pat_global_ptr,
+ *size_pat_global_ptr,
+ *cati_global_ptr,
+ *cat_cmd_global_ptr,
+ *cat_lim_global_ptr,
+ *startup_ptr,
+ *role_comment_ptr,
+ *role_forw_ptr,
+ *role_repl_ptr,
+ *role_fldr_ptr,
+ *role_afrom_ptr,
+ *role_filt_ptr,
+ *role_status1_ptr,
+ *role_status2_ptr,
+ *role_status3_ptr,
+ *role_status4_ptr,
+ *role_status5_ptr,
+ *role_status6_ptr,
+ *role_status7_ptr,
+ *role_status8_ptr,
+ *msg_state1_ptr,
+ *msg_state2_ptr,
+ *msg_state3_ptr,
+ *msg_state4_ptr;
+
+
+extern OPT_SCREEN_S *opt_screen;
+
+
+extern EditWhich ew;
+
+
+/* exported protoypes */
+int conf_scroll_screen(struct pine *, OPT_SCREEN_S *, CONF_S *, char *, char *, int);
+void standard_radio_setup(struct pine *, CONF_S **, struct variable *, CONF_S **);
+int standard_radio_var(struct pine *, struct variable *);
+int delete_user_vals(struct variable *);
+CONF_S *new_confline(CONF_S **);
+void free_conflines(CONF_S **);
+CONF_S *first_confline(CONF_S *);
+CONF_S *first_sel_confline(CONF_S *);
+void snip_confline(CONF_S **);
+char *pretty_value(struct pine *, CONF_S *);
+int feature_indent(void);
+SAVED_CONFIG_S *save_config_vars(struct pine *, int);
+int text_tool(struct pine *, int, CONF_S **, unsigned);
+int checkbox_tool(struct pine *, int, CONF_S **, unsigned);
+int radiobutton_tool(struct pine *, int, CONF_S **, unsigned);
+int yesno_tool(struct pine *, int, CONF_S **, unsigned);
+int text_toolit(struct pine *, int, CONF_S **, unsigned, int);
+char *generalized_sort_pretty_value(struct pine *, CONF_S *, int);
+int exclude_config_var(struct pine *, struct variable *, int);
+int config_exit_cmd(unsigned);
+int simple_exit_cmd(unsigned);
+int screen_exit_cmd(unsigned, char *);
+void config_add_list(struct pine *, CONF_S **, char **, char ***, int);
+void config_del_list_item(CONF_S **, char ***);
+void toggle_feature_bit(struct pine *, int, struct variable *, CONF_S *, int);
+int fixed_var(struct variable *, char *, char *);
+void exception_override_warning(struct variable *);
+void offer_to_fix_pinerc(struct pine *);
+void fix_side_effects(struct pine *, struct variable *, int);
+void revert_to_saved_config(struct pine *, SAVED_CONFIG_S *, int);
+void free_saved_config(struct pine *, SAVED_CONFIG_S **, int);
+
+
+#endif /* PINE_CONFSCROLL_INCLUDED */
diff --git a/alpine/conftype.h b/alpine/conftype.h
new file mode 100644
index 00000000..976df5c2
--- /dev/null
+++ b/alpine/conftype.h
@@ -0,0 +1,141 @@
+/*
+ * $Id: conftype.h 1012 2008-03-26 00:44:22Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_CONFTYPE_INCLUDED
+#define PINE_CONFTYPE_INCLUDED
+
+
+#include "keymenu.h"
+#include "help.h"
+#include "flagmaint.h"
+#include "context.h"
+#include "listsel.h"
+#include "../pith/pattern.h"
+#include "../pith/conf.h"
+#include "../pith/ldap.h"
+
+
+typedef enum {ListMode, SingleMode} ScreenMode;
+
+
+typedef struct edit_arb {
+ struct variable *v;
+ ARBHDR_S *a;
+ struct edit_arb *next;
+} EARB_S;
+
+
+typedef struct conf_line {
+ char *varname, /* alloc'd var name string */
+ *value; /* alloc'd var value string */
+ short varoffset; /* offset from screen left */
+ short valoffset; /* offset from screen left */
+ short val2offset; /* offset from screen left */
+ struct variable *var; /* pointer to pinerc var */
+ long varmem; /* value's index, if list */
+ /* tool to manipulate values */
+ int (*tool)(struct pine *, int, struct conf_line **, unsigned);
+ struct key_menu *keymenu; /* tool-specific keymenu */
+ HelpType help; /* variable's help text */
+ char *help_title;
+ unsigned flags;
+ struct conf_line *varnamep; /* pointer to varname */
+ struct conf_line *headingp; /* pointer to heading */
+ struct conf_line *next, *prev;
+ union flag_or_context_data {
+ struct flag_conf {
+ struct flag_table **ftbl; /* address of start of table */
+ struct flag_table *fp; /* pointer into table for each row */
+ } f;
+ struct context_and_screen {
+ CONTEXT_S *ct;
+ CONT_SCR_S *cs;
+ } c;
+ struct role_conf {
+ PAT_LINE_S *patline;
+ PAT_S *pat;
+ PAT_S **selected;
+ int *change_def;
+ } r;
+ struct abook_conf {
+ char **selected;
+ char *abookname;
+ } b;
+ EARB_S **earb;
+ struct list_select {
+ LIST_SEL_S *lsel;
+ ScreenMode *listmode;
+ } l;
+#ifdef ENABLE_LDAP
+ struct entry_and_screen {
+ LDAP *ld;
+ LDAPMessage *entry;
+ LDAP_SERV_S *info_used;
+ char *serv;
+ ADDR_CHOOSE_S *ac;
+ } a;
+#endif
+ struct take_export_val {
+ int selected;
+ char *exportval;
+ ScreenMode *listmode;
+ } t;
+ } d;
+} CONF_S;
+
+
+/*
+ * Valid for flags argument of config screen tools or flags field in CONF_S
+ */
+#define CF_CHANGES 0x0001 /* Have been earlier changes */
+#define CF_NOSELECT 0x0002 /* This line is unselectable */
+#define CF_NOHILITE 0x0004 /* Don't highlight varname */
+#define CF_NUMBER 0x0008 /* Input should be numeric */
+#define CF_INVISIBLEVAR 0x0010 /* Don't show the varname */
+#define CF_PRINTER 0x0020 /* Printer config line */
+#define CF_H_LINE 0x0040 /* Horizontal line */
+#define CF_B_LINE 0x0080 /* Blank line */
+#define CF_CENTERED 0x0100 /* Centered text */
+#define CF_STARTITEM 0x0200 /* Start of an "item" */
+#define CF_PRIVATE 0x0400 /* Private flag for tool */
+#define CF_DOUBLEVAR 0x0800 /* Line has 2 settable vars */
+#define CF_VAR2 0x1000 /* Cursor on 2nd of 2 vars */
+#define CF_COLORSAMPLE 0x2000 /* Show color sample here */
+#define CF_POT_SLCTBL 0x4000 /* Potentially selectable */
+#define CF_INHERIT 0x8000 /* Inherit Defaults line */
+
+
+typedef struct save_config {
+ union {
+ char *p;
+ char **l;
+ } saved_user_val;
+} SAVED_CONFIG_S;
+
+
+typedef struct conf_screen {
+ CONF_S *current,
+ *prev,
+ *top_line;
+ int ro_warning,
+ deferred_ro_warning;
+} OPT_SCREEN_S;
+
+
+/* exported protoypes */
+
+
+#endif /* PINE_CONFTYPE_INCLUDED */
diff --git a/alpine/context.c b/alpine/context.c
new file mode 100644
index 00000000..c3d4dc9e
--- /dev/null
+++ b/alpine/context.c
@@ -0,0 +1,1019 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: context.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "headers.h"
+#include "context.h"
+#include "confscroll.h"
+#include "status.h"
+#include "folder.h"
+#include "radio.h"
+#include "alpine.h"
+#include "mailindx.h"
+#include "mailcmd.h"
+#include "send.h"
+#include "../pith/list.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/thread.h"
+
+
+/*
+ * Internal prototypes
+ */
+int context_config_tool(struct pine *, int, CONF_S **, unsigned);
+int context_config_add(struct pine *, CONF_S **);
+int context_config_shuffle(struct pine *, CONF_S **);
+int context_config_edit(struct pine *, CONF_S **);
+int context_config_delete(struct pine *, CONF_S **);
+int ccs_var_delete(struct pine *, CONTEXT_S *);
+int ccs_var_insert(struct pine *, char *, struct variable *, char **, int);
+int context_select_tool(struct pine *, int, CONF_S **, unsigned);
+
+
+/*
+ * Setup CollectionLists. Build a context list on the fly from the config
+ * variable and edit it. This won't include Incoming-Folders because that
+ * is a pseudo collection, but that's ok since we can't do the operations
+ * on it, anyway. Reset real config list at the end.
+ */
+void
+context_config_screen(struct pine *ps, CONT_SCR_S *cs, int edit_exceptions)
+{
+ CONTEXT_S *top, **clist, *cp;
+ CONF_S *ctmpa, *first_line, *heading;
+ OPT_SCREEN_S screen;
+ int i, readonly_warning, some_defined, ret;
+ int reinit_contexts = 0, prime;
+ char **lval, **lval1, **lval2, ***alval;
+ struct variable fake_fspec_var, fake_nspec_var;
+ struct variable *fake_fspec, *fake_nspec;
+
+ ew = edit_exceptions ? ps_global->ew_for_except_vars : Main;
+
+ if(ps->restricted)
+ readonly_warning = 1;
+ else{
+ PINERC_S *prc = NULL;
+
+ switch(ew){
+ case Main:
+ prc = ps->prc;
+ break;
+ case Post:
+ prc = ps->post_prc;
+ break;
+ default:
+ break;
+ }
+
+ readonly_warning = prc ? prc->readonly : 1;
+ if(prc && prc->quit_to_edit){
+ quit_to_edit_msg(prc);
+ return;
+ }
+ }
+
+go_again:
+ top = NULL; ctmpa = NULL; first_line = NULL;
+ some_defined = 0, prime = 0;
+ fake_fspec = &fake_fspec_var;
+ fake_nspec = &fake_nspec_var;
+ memset((void *)fake_fspec, 0, sizeof(*fake_fspec));
+ memset((void *)fake_nspec, 0, sizeof(*fake_nspec));
+
+ /* so fixed_var() will work right */
+ fake_fspec->is_list = 1;
+ fake_nspec->is_list = 1;
+ if((ps->vars[V_FOLDER_SPEC]).is_fixed){
+ fake_fspec->is_fixed = 1;
+ if((ps->vars[V_FOLDER_SPEC]).fixed_val.l
+ && (ps->vars[V_FOLDER_SPEC]).fixed_val.l[0]){
+ fake_fspec->fixed_val.l = (char **) fs_get(2 * sizeof(char *));
+ fake_fspec->fixed_val.l[0]
+ = cpystr((ps->vars[V_FOLDER_SPEC]).fixed_val.l[0]);
+ fake_fspec->fixed_val.l[1] = NULL;
+ }
+ }
+
+ if((ps->vars[V_NEWS_SPEC]).is_fixed){
+ fake_nspec->is_fixed = 1;
+ if((ps->vars[V_NEWS_SPEC]).fixed_val.l
+ && (ps->vars[V_NEWS_SPEC]).fixed_val.l[0]){
+ fake_nspec->fixed_val.l = (char **) fs_get(2 * sizeof(char *));
+ fake_nspec->fixed_val.l[0] = cpystr(INHERIT);
+ fake_nspec->fixed_val.l[0]
+ = cpystr((ps->vars[V_NEWS_SPEC]).fixed_val.l[0]);
+ fake_nspec->fixed_val.l[1] = NULL;
+ }
+ }
+
+ clist = &top;
+ lval1 = LVAL(&ps->vars[V_FOLDER_SPEC], ew);
+ lval2 = LVAL(&ps->vars[V_NEWS_SPEC], ew);
+
+ alval = ALVAL(fake_fspec, ew);
+ if(lval1)
+ *alval = copy_list_array(lval1);
+ else if(!edit_exceptions && ps->VAR_FOLDER_SPEC && ps->VAR_FOLDER_SPEC[0] &&
+ ps->VAR_FOLDER_SPEC[0][0])
+ *alval = copy_list_array(ps->VAR_FOLDER_SPEC);
+ else
+ fake_fspec = NULL;
+
+ if(fake_fspec){
+ lval = LVAL(fake_fspec, ew);
+ for(i = 0; lval && lval[i]; i++){
+ cp = NULL;
+ if(i == 0 && !strcmp(lval[i], INHERIT)){
+ cp = (CONTEXT_S *)fs_get(sizeof(*cp));
+ memset((void *)cp, 0, sizeof(*cp));
+ cp->use = CNTXT_INHERIT;
+ cp->label = cpystr("Default collections are inherited");
+ }
+ else if((cp = new_context(lval[i], &prime)) != NULL){
+ cp->var.v = fake_fspec;
+ cp->var.i = i;
+ }
+
+ if(cp){
+ *clist = cp; /* add it to list */
+ clist = &cp->next; /* prepare for next */
+ }
+ }
+
+ set_current_val(fake_fspec, FALSE, FALSE);
+ }
+
+ alval = ALVAL(fake_nspec, ew);
+ if(lval2)
+ *alval = copy_list_array(lval2);
+ else if(!edit_exceptions && ps->VAR_NEWS_SPEC && ps->VAR_NEWS_SPEC[0] &&
+ ps->VAR_NEWS_SPEC[0][0])
+ *alval = copy_list_array(ps->VAR_NEWS_SPEC);
+ else
+ fake_nspec = NULL;
+
+ if(fake_nspec){
+ lval = LVAL(fake_nspec, ew);
+ for(i = 0; lval && lval[i]; i++){
+ cp = NULL;
+ if(i == 0 && !strcmp(lval[i], INHERIT)){
+ cp = (CONTEXT_S *)fs_get(sizeof(*cp));
+ memset((void *)cp, 0, sizeof(*cp));
+ cp->use = CNTXT_INHERIT;
+ cp->label = cpystr("Default collections are inherited");
+ }
+ else if((cp = new_context(lval[i], &prime)) != NULL){
+ cp->var.v = fake_nspec;
+ cp->var.i = i;
+ }
+
+ if(cp){
+ *clist = cp; /* add it to list */
+ clist = &cp->next; /* prepare for next */
+ }
+ }
+
+ set_current_val(fake_nspec, FALSE, FALSE);
+ }
+
+ for(cp = top; cp; cp = cp->next)
+ if(!(cp->use & CNTXT_INHERIT)){
+ some_defined++;
+ break;
+ }
+
+ if(edit_exceptions && !some_defined){
+ q_status_message(SM_ORDER, 3, 7,
+ _("No exceptions to edit. First collection exception must be set by editing file"));
+ free_contexts(&top);
+ if(reinit_contexts){
+ free_contexts(&ps_global->context_list);
+ init_folders(ps_global);
+ }
+
+ return;
+ }
+
+
+ /* fix up prev pointers */
+ for(cp = top; cp; cp = cp->next)
+ if(cp->next)
+ cp->next->prev = cp;
+
+ new_confline(&ctmpa); /* blank line */
+ ctmpa->keymenu = cs->keymenu;
+ ctmpa->help = cs->help.text;
+ ctmpa->help_title = cs->help.title;
+ ctmpa->tool = context_config_tool;
+ ctmpa->flags |= (CF_NOSELECT | CF_B_LINE);
+
+ for(cp = top; cp; cp = cp->next){
+ new_confline(&ctmpa);
+ heading = ctmpa;
+ if(!(cp->use & CNTXT_INHERIT))
+ ctmpa->value = cpystr(cp->nickname ? cp->nickname : cp->context);
+
+ ctmpa->var = cp->var.v;
+ ctmpa->keymenu = cs->keymenu;
+ ctmpa->help = cs->help.text;
+ ctmpa->help_title = cs->help.title;
+ ctmpa->tool = context_config_tool;
+ ctmpa->flags |= CF_STARTITEM;
+ ctmpa->valoffset = 4;
+ ctmpa->d.c.ct = cp;
+ ctmpa->d.c.cs = cs;
+ if(cp->use & CNTXT_INHERIT)
+ ctmpa->flags |= CF_INHERIT | CF_NOSELECT;
+
+ if((!first_line && !(cp->use & CNTXT_INHERIT)) ||
+ (!(cp->use & CNTXT_INHERIT) &&
+ cp->var.v &&
+ (cs->starting_var == cp->var.v) &&
+ (cs->starting_varmem == cp->var.i)))
+ first_line = ctmpa;
+
+ /* Add explanatory text */
+ new_confline(&ctmpa);
+ ctmpa->value = cpystr(cp->label ? cp->label : "* * *");
+ ctmpa->keymenu = cs->keymenu;
+ ctmpa->help = cs->help.text;
+ ctmpa->help_title = cs->help.title;
+ ctmpa->tool = context_config_tool;
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->valoffset = 8;
+
+ /* Always add blank line, make's shuffling a little easier */
+ new_confline(&ctmpa);
+ heading->headingp = ctmpa; /* use headingp to mark end */
+ ctmpa->keymenu = cs->keymenu;
+ ctmpa->help = cs->help.text;
+ ctmpa->help_title = cs->help.title;
+ ctmpa->tool = context_config_tool;
+ ctmpa->flags |= CF_NOSELECT | CF_B_LINE;
+ ctmpa->valoffset = 0;
+ }
+
+ cs->starting_var = NULL;
+
+
+ memset(&screen, 0, sizeof(screen));
+ screen.ro_warning = readonly_warning;
+ ret = conf_scroll_screen(ps, &screen, first_line, cs->title,
+ cs->print_string, 0);
+
+ free_contexts(&top);
+
+ if(ret)
+ reinit_contexts++;
+
+ /*
+ * 15 means the tool wants us to reset and go again. The config var
+ * has been changed. The contexts will be built again from the
+ * config variable and the real contexts will be rebuilt below.
+ * This is easier and safer than having the tools rebuild the context
+ * list and the display correct. It's difficult to do because of all
+ * the inheriting and defaulting going on.
+ */
+ if(ret == 15){
+ if(edit_exceptions && !LVAL(fake_fspec, ew) && !LVAL(fake_nspec, ew)){
+ if(want_to(_("Really delete last exceptional collection"),
+ 'n', 'n', h_config_context_del_except,
+ WT_FLUSH_IN) != 'y'){
+ free_variable_values(fake_fspec);
+ free_variable_values(fake_nspec);
+ goto go_again;
+ }
+ }
+
+ /* resolve variable changes */
+ if((lval1 && !equal_list_arrays(lval1, LVAL(fake_fspec, ew))) ||
+ (fake_fspec && !equal_list_arrays(ps->VAR_FOLDER_SPEC,
+ LVAL(fake_fspec, ew)))){
+ i = set_variable_list(V_FOLDER_SPEC,
+ LVAL(fake_fspec, ew), TRUE, ew);
+ set_current_val(&ps->vars[V_FOLDER_SPEC], TRUE, FALSE);
+
+ if(i)
+ q_status_message(SM_ORDER, 3, 3,
+ _("Trouble saving change, cancelled"));
+ else if(!edit_exceptions && lval1 && !LVAL(fake_fspec, ew)){
+ cs->starting_var = fake_fspec;
+ cs->starting_varmem = 0;
+ q_status_message(SM_ORDER, 3, 3,
+ _("Deleted last Folder-Collection, reverting to default"));
+ }
+ else if(!edit_exceptions && !lval1 && !LVAL(fake_fspec, ew)){
+ cs->starting_var = fake_fspec;
+ cs->starting_varmem = 0;
+ q_status_message(SM_ORDER, 3, 3,
+ _("Deleted default Folder-Collection, reverting back to default"));
+ }
+ }
+
+ if((lval2 && !equal_list_arrays(lval2, LVAL(fake_nspec, ew))) ||
+ (fake_nspec && !equal_list_arrays(ps->VAR_NEWS_SPEC,
+ LVAL(fake_nspec, ew)))){
+ i = set_variable_list(V_NEWS_SPEC,
+ LVAL(fake_nspec, ew), TRUE, ew);
+ set_news_spec_current_val(TRUE, FALSE);
+
+ if(i)
+ q_status_message(SM_ORDER, 3, 3,
+ _("Trouble saving change, cancelled"));
+ else if(!edit_exceptions && lval2 && !LVAL(fake_nspec, ew) &&
+ ps->VAR_NEWS_SPEC && ps->VAR_NEWS_SPEC[0] &&
+ ps->VAR_NEWS_SPEC[0][0]){
+ cs->starting_var = fake_nspec;
+ cs->starting_varmem = 0;
+ q_status_message(SM_ORDER, 3, 3,
+ _("Deleted last News-Collection, reverting to default"));
+ }
+ else if(!edit_exceptions && !lval2 && !LVAL(fake_nspec, ew) &&
+ ps->VAR_NEWS_SPEC && ps->VAR_NEWS_SPEC[0] &&
+ ps->VAR_NEWS_SPEC[0][0]){
+ cs->starting_var = fake_nspec;
+ cs->starting_varmem = 0;
+ q_status_message(SM_ORDER, 3, 3,
+ _("Deleted default News-Collection, reverting back to default"));
+ }
+ }
+
+ free_variable_values(fake_fspec);
+ free_variable_values(fake_nspec);
+ goto go_again;
+ }
+
+ ps->mangled_screen = 1;
+
+ /* make the real context list match the changed config variables */
+ if(reinit_contexts){
+ free_contexts(&ps_global->context_list);
+ init_folders(ps_global);
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Function to display/manage collections
+
+ ----*/
+CONTEXT_S *
+context_select_screen(struct pine *ps, CONT_SCR_S *cs, int ro_warn)
+{
+ CONTEXT_S *cp;
+ CONF_S *ctmpa = NULL, *first_line = NULL, *heading;
+ OPT_SCREEN_S screen, *saved_screen;
+ int readonly_warning = 0;
+
+ /* restrict to normal config */
+ ew = Main;
+
+ if(!cs->edit)
+ readonly_warning = 0;
+ else if(ps->restricted)
+ readonly_warning = 1;
+ else{
+ PINERC_S *prc = NULL;
+
+ switch(ew){
+ case Main:
+ prc = ps->prc;
+ break;
+ case Post:
+ prc = ps->post_prc;
+ break;
+ default:
+ break;
+ }
+
+ readonly_warning = prc ? prc->readonly : 1;
+ if(ro_warn && prc && prc->quit_to_edit){
+ quit_to_edit_msg(prc);
+ return(NULL);
+ }
+ }
+
+ readonly_warning *= ro_warn;
+
+ /*
+ * Loop thru available contexts, setting up for display
+ * (note: if no "cp" we're hosed. should never happen ;)
+ */
+ for(cp = *cs->contexts; cp->prev; cp = cp->prev)
+ ;
+
+ /* delimiter for Mail Collections */
+ new_confline(&ctmpa); /* blank line */
+ ctmpa->keymenu = cs->keymenu;
+ ctmpa->help = cs->help.text;
+ ctmpa->help_title = cs->help.title;
+ ctmpa->tool = context_select_tool;
+ ctmpa->flags |= CF_NOSELECT | CF_B_LINE;
+
+ do{
+ new_confline(&ctmpa);
+ heading = ctmpa;
+ ctmpa->value = cpystr(cp->nickname ? cp->nickname : cp->context);
+ ctmpa->var = cp->var.v;
+ ctmpa->keymenu = cs->keymenu;
+ ctmpa->help = cs->help.text;
+ ctmpa->help_title = cs->help.title;
+ ctmpa->tool = context_select_tool;
+ ctmpa->flags |= CF_STARTITEM;
+ ctmpa->valoffset = 4;
+ ctmpa->d.c.ct = cp;
+ ctmpa->d.c.cs = cs;
+
+ if(!first_line || cp == cs->start)
+ first_line = ctmpa;
+
+ /* Add explanatory text */
+ new_confline(&ctmpa);
+ ctmpa->value = cpystr(cp->label ? cp->label : "* * *");
+ ctmpa->keymenu = cs->keymenu;
+ ctmpa->help = cs->help.text;
+ ctmpa->help_title = cs->help.title;
+ ctmpa->tool = context_select_tool;
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->valoffset = 8;
+
+ /* Always add blank line, make's shuffling a little easier */
+ new_confline(&ctmpa);
+ heading->headingp = ctmpa;
+ ctmpa->keymenu = cs->keymenu;
+ ctmpa->help = cs->help.text;
+ ctmpa->help_title = cs->help.title;
+ ctmpa->tool = context_select_tool;
+ ctmpa->flags |= CF_NOSELECT | CF_B_LINE;
+ ctmpa->valoffset = 0;
+ }
+ while((cp = cp->next) != NULL);
+
+
+ saved_screen = opt_screen;
+ memset(&screen, 0, sizeof(screen));
+ screen.ro_warning = readonly_warning;
+ (void) conf_scroll_screen(ps, &screen, first_line, cs->title,
+ cs->print_string, 0);
+ opt_screen = saved_screen;
+ ps->mangled_screen = 1;
+ return(cs->selected);
+}
+
+
+int
+context_config_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ int retval = 0;
+
+ switch(cmd){
+ case MC_DELETE :
+ if(!fixed_var((*cl)->var, "delete", "collection"))
+ retval = context_config_delete(ps, cl);
+
+ break;
+
+ case MC_EDIT :
+ if(!fixed_var((*cl)->var, "change", "collection"))
+ retval = context_config_edit(ps, cl);
+
+ break;
+
+ case MC_ADD :
+ if(!fixed_var((*cl)->var, "add to", "collection"))
+ retval = context_config_add(ps, cl);
+
+ break;
+
+ case MC_SHUFFLE :
+ if(!fixed_var((*cl)->var, "shuffle", "collection"))
+ retval = context_config_shuffle(ps, cl);
+
+ break;
+
+ case MC_EXIT :
+ retval = simple_exit_cmd(flags);
+ break;
+
+ default:
+ retval = -1;
+ break;
+ }
+
+ if(retval > 0)
+ ps->mangled_body = 1;
+
+ return(retval);
+}
+
+
+int
+context_config_add(struct pine *ps, CONF_S **cl)
+{
+ char *raw_ctxt;
+ struct variable *var;
+ char **lval;
+ int count;
+
+ if((raw_ctxt = context_edit_screen(ps, "ADD", NULL, NULL, NULL, NULL)) != NULL){
+
+ /*
+ * If var is non-NULL we add to the end of that var.
+ * If it is NULL, that means we're adding to the current_val, so
+ * we'll have to soak up the default values from there into our
+ * own variable.
+ */
+ if((*cl)->var){
+ var = (*cl)->var;
+ lval = LVAL((*cl)->var, ew);
+ }
+ else{
+ q_status_message(SM_ORDER|SM_DING, 3, 3,
+ "Programmer botch in context_config_add");
+ return(0);
+ }
+
+ for(count = 0; lval && lval[count]; count++)
+ ;
+
+ if(!ccs_var_insert(ps, raw_ctxt, var, lval, count)){
+ fs_give((void **)&raw_ctxt);
+ q_status_message(SM_ORDER|SM_DING, 3, 3,
+ _("Error adding new collection"));
+ return(0);
+ }
+
+ fs_give((void **)&raw_ctxt);
+
+ (*cl)->d.c.cs->starting_var = var;
+ (*cl)->d.c.cs->starting_varmem = count;
+ q_status_message(SM_ORDER, 0, 3,
+ _("New collection added. Use \"$\" to adjust order."));
+ return(15);
+ }
+
+ ps->mangled_screen = 1;
+ return(0);
+}
+
+
+int
+context_config_shuffle(struct pine *ps, CONF_S **cl)
+{
+ char prompt[256];
+ int n = 0, cmd, i1, i2, count = 0, insert_num, starting_varmem;
+ int news_problem = 0, deefault = 0;
+ ESCKEY_S ekey[3];
+ CONTEXT_S *cur_ctxt, *other_ctxt;
+ char *tmp, **lval, **lval1, **lval2;
+ struct variable *cur_var, *other_var;
+
+ if(!((*cl)->d.c.ct && (*cl)->var))
+ return(0);
+
+ /* enable UP? */
+ if((*cl)->d.c.ct->prev && !((*cl)->d.c.ct->prev->use & CNTXT_INHERIT)){
+ /*
+ * Don't allow shuffling news collection up to become
+ * the primary collection. That would happen if prev is primary
+ * and this one is news.
+ */
+ if((*cl)->d.c.ct->prev->use & CNTXT_SAVEDFLT &&
+ (*cl)->d.c.ct->use & CNTXT_NEWS)
+ news_problem++;
+ else{
+ ekey[n].ch = 'u';
+ ekey[n].rval = 'u';
+ ekey[n].name = "U";
+ ekey[n++].label = N_("Up");
+ deefault = 'u';
+ }
+ }
+
+ /* enable DOWN? */
+ if((*cl)->d.c.ct->next && !((*cl)->d.c.ct->next->use & CNTXT_INHERIT)){
+ /*
+ * Don't allow shuffling down past news collection if this
+ * is primary collection.
+ */
+ if((*cl)->d.c.ct->use & CNTXT_SAVEDFLT &&
+ (*cl)->d.c.ct->next->use & CNTXT_NEWS)
+ news_problem++;
+ else{
+ ekey[n].ch = 'd';
+ ekey[n].rval = 'd';
+ ekey[n].name = "D";
+ ekey[n++].label = N_("Down");
+ if(!deefault)
+ deefault = 'd';
+ }
+ }
+
+ if(n){
+ ekey[n].ch = -1;
+ snprintf(prompt, sizeof(prompt), _("Shuffle selected context %s%s%s? "),
+ (ekey[0].ch == 'u') ? _("UP") : "",
+ (n > 1) ? " or " : "",
+ (ekey[0].ch == 'd' || n > 1) ? _("DOWN") : "");
+ prompt[sizeof(prompt)-1] = '\0';
+
+ cmd = radio_buttons(prompt, -FOOTER_ROWS(ps), ekey,
+ deefault, 'x', NO_HELP, RB_NORM);
+ switch(cmd){
+ case 'x':
+ default:
+ cmd_cancelled("Shuffle");
+ return(0);
+
+ case 'u':
+ case 'd':
+ break;
+ }
+
+ /*
+ * This is complicated by the fact that there are two
+ * vars involved, the folder-collections and the news-collections.
+ * We may have to shuffle across collections.
+ */
+ cur_ctxt = (*cl)->d.c.ct;
+ if(cmd == 'd')
+ other_ctxt = (*cl)->d.c.ct->next;
+ else if(cmd == 'u')
+ other_ctxt = (*cl)->d.c.ct->prev;
+
+ cur_var = cur_ctxt->var.v;
+ other_var = other_ctxt->var.v;
+
+ /* swap elements of config var */
+ if(cur_var == other_var){
+ i1 = cur_ctxt->var.i;
+ i2 = other_ctxt->var.i;
+ lval = LVAL(cur_var, ew);
+ if(lval){
+ tmp = lval[i1];
+ lval[i1] = lval[i2];
+ lval[i2] = tmp;
+ }
+
+ starting_varmem = i2;
+ }
+ else{
+ /* swap into the other_var */
+ i1 = cur_ctxt->var.i;
+ i2 = other_ctxt->var.i;
+ lval1 = LVAL(cur_var, ew);
+ lval2 = LVAL(other_var, ew);
+ /* count */
+ for(count = 0; lval2 && lval2[count]; count++)
+ ;
+ if(cmd == 'd')
+ insert_num = count ? 1 : 0;
+ else{
+ insert_num = count ? count - 1 : count;
+ }
+
+ starting_varmem = insert_num;
+ if(ccs_var_insert(ps,lval1[i1],other_var,lval2,insert_num)){
+ if(!ccs_var_delete(ps, cur_ctxt)){
+ q_status_message(SM_ORDER|SM_DING, 3, 3,
+ _("Error deleting shuffled context"));
+ return(0);
+ }
+ }
+ else{
+ q_status_message(SM_ORDER, 3, 3,
+ _("Trouble shuffling, cancelled"));
+ return(0);
+ }
+ }
+
+ (*cl)->d.c.cs->starting_var = other_var;
+ (*cl)->d.c.cs->starting_varmem = starting_varmem;
+
+ q_status_message(SM_ORDER, 0, 3, _("Collections shuffled"));
+ return(15);
+ }
+
+ if(news_problem)
+ /* TRANSLATORS: Sorry, can't move news group collections above email collections */
+ q_status_message(SM_ORDER, 0, 3, _("Sorry, cannot Shuffle news to top"));
+ else
+ q_status_message(SM_ORDER, 0, 3, _("Sorry, nothing to Shuffle"));
+
+ return(0);
+}
+
+
+int
+context_config_edit(struct pine *ps, CONF_S **cl)
+{
+ char *raw_ctxt, tpath[MAILTMPLEN], *p, **lval;
+ struct variable *var;
+ int i;
+
+ if(!(*cl)->d.c.ct)
+ return(0);
+
+ /* Undigest the context */
+ strncpy(tpath, ((*cl)->d.c.ct->context[0] == '{'
+ && (p = strchr((*cl)->d.c.ct->context, '}')))
+ ? ++p
+ : (*cl)->d.c.ct->context, sizeof(tpath)-1);
+ tpath[sizeof(tpath)-1] = '\0';
+
+ if((p = strstr(tpath, "%s")) != NULL)
+ *p = '\0';
+
+ if((raw_ctxt = context_edit_screen(ps, "EDIT", (*cl)->d.c.ct->nickname,
+ (*cl)->d.c.ct->server, tpath,
+ (*cl)->d.c.ct->dir ?
+ (*cl)->d.c.ct->dir->view.user
+ : NULL)) != NULL){
+
+ if((*cl)->var){
+ var = (*cl)->var;
+ lval = LVAL(var, ew);
+ i = (*cl)->d.c.ct->var.i;
+
+ if(lval && lval[i] && !strcmp(lval[i], raw_ctxt))
+ q_status_message(SM_ORDER, 0, 3, _("No change"));
+ else if(lval){
+ if(lval[i])
+ fs_give((void **) &lval[i]);
+
+ lval[i] = raw_ctxt;
+ raw_ctxt = NULL;
+
+ q_status_message(SM_ORDER, 0, 3, _("Collection list entry updated"));
+ }
+
+ (*cl)->d.c.cs->starting_var = var;
+ (*cl)->d.c.cs->starting_varmem = i;
+
+ if(raw_ctxt)
+ fs_give((void **) &raw_ctxt);
+ }
+ else{
+ q_status_message(SM_ORDER|SM_DING, 3, 3,
+ "Programmer botch in context_config_edit");
+ return(0);
+ }
+
+ return(15);
+ }
+
+ ps->mangled_screen = 1;
+ return(0);
+}
+
+
+int
+context_config_delete(struct pine *ps, CONF_S **cl)
+{
+ char tmp[MAILTMPLEN];
+
+ if(!(*cl)->var){
+ q_status_message(SM_ORDER|SM_DING, 3, 3,
+ "Programmer botch in context_config_delete");
+ return(0);
+ }
+
+ if((*cl)->d.c.ct->use & CNTXT_SAVEDFLT &&
+ (*cl)->d.c.ct->next &&
+ (*cl)->d.c.ct->next->use & CNTXT_NEWS &&
+ (*cl)->d.c.ct->var.v == (*cl)->d.c.ct->next->var.v){
+ q_status_message(SM_ORDER|SM_DING, 3, 3,
+ _("Sorry, cannot Delete causing news to move to top"));
+ return(0);
+ }
+
+ snprintf(tmp, sizeof(tmp), _("Delete the collection definition for \"%s\""),
+ (*cl)->value);
+ tmp[sizeof(tmp)-1] = '\0';
+ if(want_to(tmp, 'n', 'n', NO_HELP, WT_FLUSH_IN) == 'y'){
+
+ (*cl)->d.c.cs->starting_var = (*cl)->var;
+ (*cl)->d.c.cs->starting_varmem = (*cl)->d.c.ct->var.i;
+ if((*cl)->d.c.ct->next){
+ if((*cl)->d.c.ct->next->var.v != (*cl)->var){
+ (*cl)->d.c.cs->starting_var = (*cl)->d.c.ct->next->var.v;
+ (*cl)->d.c.cs->starting_varmem = 0;
+ }
+ else{
+ (*cl)->d.c.cs->starting_var = (*cl)->var;
+ (*cl)->d.c.cs->starting_varmem = (*cl)->d.c.ct->var.i;
+ }
+ }
+ else{
+ if((*cl)->d.c.ct->var.i > 0){
+ (*cl)->d.c.cs->starting_var = (*cl)->var;
+ (*cl)->d.c.cs->starting_varmem = (*cl)->d.c.ct->var.i - 1;
+ }
+ else{
+ if((*cl)->d.c.ct->prev){
+ (*cl)->d.c.cs->starting_var = (*cl)->d.c.ct->prev->var.v;
+ (*cl)->d.c.cs->starting_varmem = (*cl)->d.c.ct->prev->var.i;
+ }
+ }
+ }
+
+ /* Remove from var list */
+ if(!ccs_var_delete(ps, (*cl)->d.c.ct)){
+ q_status_message(SM_ORDER|SM_DING, 3, 3,
+ _("Error deleting renamed context"));
+ return(0);
+ }
+
+ q_status_message(SM_ORDER, 0, 3, _("Collection deleted"));
+
+ return(15);
+ }
+
+ q_status_message(SM_ORDER, 0, 3, _("No collections deleted"));
+ return(0);
+}
+
+
+int
+ccs_var_delete(struct pine *ps, CONTEXT_S *ctxt)
+{
+ int count, i;
+ char **newl = NULL, **lval, **lp, ***alval;
+
+ if(ctxt)
+ lval = LVAL(ctxt->var.v, ew);
+ else
+ lval = NULL;
+
+ for(count = 0; lval && lval[count]; count++)
+ ; /* sum the list */
+
+ if(count > 1){
+ newl = (char **) fs_get(count * sizeof(char *));
+ for(i = 0, lp = newl; lval[i]; i++)
+ if(i != ctxt->var.i)
+ *lp++ = cpystr(lval[i]);
+
+ *lp = NULL;
+ }
+
+ alval = ALVAL(ctxt->var.v, ew);
+ if(alval){
+ free_list_array(alval);
+ if(newl){
+ for(i = 0; newl[i] ; i++) /* count elements */
+ ;
+
+ *alval = (char **) fs_get((i+1) * sizeof(char *));
+
+ for(i = 0; newl[i] ; i++)
+ (*alval)[i] = cpystr(newl[i]);
+
+ (*alval)[i] = NULL;
+ }
+ }
+
+ free_list_array(&newl);
+ return(1);
+}
+
+
+/*
+ * Insert into "var", which currently has values "oldvarval", the "newline"
+ * at position "insert".
+ */
+int
+ccs_var_insert(struct pine *ps, char *newline, struct variable *var, char **oldvarval, int insert)
+{
+ int count, i, offset;
+ char **newl, ***alval;
+
+ for(count = 0; oldvarval && oldvarval[count]; count++)
+ ;
+
+ if(insert < 0 || insert > count){
+ q_status_message(SM_ORDER,3,5, "unexpected problem inserting folder");
+ return(0);
+ }
+
+ newl = (char **)fs_get((count + 2) * sizeof(char *));
+ newl[insert] = cpystr(newline);
+ newl[count + 1] = NULL;
+ for(i = offset = 0; oldvarval && oldvarval[i]; i++){
+ if(i == insert)
+ offset = 1;
+
+ newl[i + offset] = cpystr(oldvarval[i]);
+ }
+
+ alval = ALVAL(var, ew);
+ if(alval){
+ free_list_array(alval);
+ if(newl){
+ for(i = 0; newl[i] ; i++) /* count elements */
+ ;
+
+ *alval = (char **) fs_get((i+1) * sizeof(char *));
+
+ for(i = 0; newl[i] ; i++)
+ (*alval)[i] = cpystr(newl[i]);
+
+ (*alval)[i] = NULL;
+ }
+ }
+
+ free_list_array(&newl);
+ return(1);
+}
+
+
+int
+context_select_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ int retval = 0;
+
+ switch(cmd){
+ case MC_CHOICE :
+ (*cl)->d.c.cs->selected = (*cl)->d.c.ct;
+ retval = simple_exit_cmd(flags);
+ break;
+
+ case MC_EXIT :
+ retval = simple_exit_cmd(flags);
+ break;
+
+ case MC_MAIN :
+ retval = simple_exit_cmd(flags);
+ ps_global->next_screen = main_menu_screen;
+ break;
+
+ case MC_INDEX :
+ if(THREADING()
+ && sp_viewing_a_thread(ps_global->mail_stream)
+ && unview_thread(ps_global, ps_global->mail_stream, ps_global->msgmap)){
+ ps_global->next_screen = mail_index_screen;
+ ps_global->view_skipped_index = 0;
+ ps_global->mangled_screen = 1;
+ }
+
+ retval = simple_exit_cmd(flags);
+ ps_global->next_screen = mail_index_screen;
+ break;
+
+ case MC_COMPOSE :
+ retval = simple_exit_cmd(flags);
+ ps_global->next_screen = compose_screen;
+ break;
+
+ case MC_ROLE :
+ retval = simple_exit_cmd(flags);
+ ps_global->next_screen = alt_compose_screen;
+ break;
+
+ case MC_GOTO :
+ {
+ int notrealinbox;
+ CONTEXT_S *c = (*cl)->d.c.ct;
+ char *new_fold = broach_folder(-FOOTER_ROWS(ps), 0, &notrealinbox, &c);
+
+ if(new_fold && do_broach_folder(new_fold, c, NULL, notrealinbox ? 0L : DB_INBOXWOCNTXT) > 0){
+ ps_global->next_screen = mail_index_screen;
+ retval = simple_exit_cmd(flags);
+ }
+ else
+ ps->mangled_footer = 1;
+ }
+
+ break;
+
+ case MC_QUIT :
+ retval = simple_exit_cmd(flags);
+ ps_global->next_screen = quit_screen;
+ break;
+
+ default:
+ retval = -1;
+ break;
+ }
+
+ if(retval > 0)
+ ps->mangled_body = 1;
+
+ return(retval);
+}
diff --git a/alpine/context.h b/alpine/context.h
new file mode 100644
index 00000000..a7c8d641
--- /dev/null
+++ b/alpine/context.h
@@ -0,0 +1,51 @@
+/*
+ * $Id: context.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_CONTEXT_INCLUDED
+#define PINE_CONTEXT_INCLUDED
+
+
+#include "../pith/context.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "help.h"
+#include "keymenu.h"
+
+
+/*
+ * Structures to control the collection list screen
+ */
+typedef struct context_screen {
+ unsigned edit:1;
+ char *title, *print_string;
+ CONTEXT_S *start, /* for context_select_screen */
+ *selected,
+ **contexts;
+ struct variable *starting_var; /* another type of start for config */
+ int starting_varmem;
+ struct {
+ HelpType text;
+ char *title;
+ } help;
+ struct key_menu *keymenu;
+} CONT_SCR_S;
+
+
+/* exported protoypes */
+void context_config_screen(struct pine *, CONT_SCR_S *, int);
+CONTEXT_S *context_select_screen(struct pine *, CONT_SCR_S *, int);
+
+
+#endif /* PINE_CONTEXT_INCLUDED */
diff --git a/alpine/dispfilt.c b/alpine/dispfilt.c
new file mode 100644
index 00000000..be46fe60
--- /dev/null
+++ b/alpine/dispfilt.c
@@ -0,0 +1,454 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: dispfilt.c 1012 2008-03-26 00:44:22Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ dispfilt.c
+ Display filter technology ;)
+
+ ====*/
+
+#include "headers.h"
+
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/filter.h"
+#include "../pith/store.h"
+#include "../pith/addrstring.h"
+#include "../pith/mimedesc.h"
+#include "../pith/list.h"
+#include "../pith/detach.h"
+
+#include "mailview.h"
+#include "signal.h"
+#include "busy.h"
+#include "dispfilt.h"
+
+
+/* internal prototypes */
+int df_valid_test(BODY *, char *);
+
+
+
+/*
+ * dfilter - pipe the data from the given storage object thru the
+ * global display filter and into whatever the putchar's
+ * function points to.
+ *
+ * Input is assumed to be UTF-8.
+ * That's converted to user's locale and passed to rawcmd.
+ * That's converted back to UTF-8 and passed through aux_filters.
+ */
+char *
+dfilter(char *rawcmd, STORE_S *input_so, gf_io_t output_pc, FILTLIST_S *aux_filters)
+{
+ char *status = NULL, *cmd, *resultf = NULL, *tmpfile = NULL;
+ int key = 0;
+
+ if((cmd = expand_filter_tokens(rawcmd,NULL,&tmpfile,&resultf,NULL,&key,NULL)) != NULL){
+ suspend_busy_cue();
+#ifndef _WINDOWS
+ ps_global->mangled_screen = 1;
+ ClearScreen();
+ fflush(stdout);
+#endif
+
+ /*
+ * If it was requested that the interaction take place via
+ * a tmpfile, fill it with text from our input_so, and let
+ * system_pipe handle the rest. Session key and tmp file
+ * mode support additions based loosely on a patch
+ * supplied by Thomas Stroesslin <thomas.stroesslin@epfl.ch>
+ */
+ if(tmpfile){
+ PIPE_S *filter_pipe;
+ FILE *fp;
+ gf_io_t gc, pc;
+ STORE_S *tmpf_so;
+
+ /* write the tmp file */
+ so_seek(input_so, 0L, 0);
+ if((tmpf_so = so_get(FileStar, tmpfile, WRITE_ACCESS|OWNER_ONLY|WRITE_TO_LOCALE)) != NULL){
+ if(key){
+ so_puts(tmpf_so, filter_session_key());
+ so_puts(tmpf_so, NEWLINE);
+ }
+ /* copy input to tmp file */
+ gf_set_so_readc(&gc, input_so);
+ gf_set_so_writec(&pc, tmpf_so);
+ gf_filter_init();
+ status = gf_pipe(gc, pc);
+ gf_clear_so_readc(input_so);
+ gf_clear_so_writec(tmpf_so);
+ if(so_give(&tmpf_so) != 0 && status == NULL)
+ status = error_description(errno);
+
+ /* prepare the terminal in case the filter uses it */
+ if(status == NULL){
+ if((filter_pipe = open_system_pipe(cmd, NULL, NULL,
+ PIPE_USER | PIPE_RESET,
+ 0, pipe_callback, NULL)) != NULL){
+ if(close_system_pipe(&filter_pipe, NULL, pipe_callback) == 0){
+ /* pull result out of tmp file */
+ if((fp = our_fopen(tmpfile, "rb")) != NULL){
+ gf_set_readc(&gc, fp, 0L, FileStar, READ_FROM_LOCALE);
+ gf_filter_init();
+ if(aux_filters)
+ for( ; aux_filters->filter; aux_filters++)
+ gf_link_filter(aux_filters->filter,
+ aux_filters->data);
+
+ status = gf_pipe(gc, output_pc);
+ fclose(fp);
+ }
+ else
+ status = "Can't read result of display filter";
+ }
+ else
+ status = "Filter command returned error.";
+ }
+ else
+ status = "Can't open pipe for display filter";
+ }
+
+ our_unlink(tmpfile);
+ }
+ else
+ status = "Can't open display filter tmp file";
+ }
+ else if((status = gf_filter(cmd, key ? filter_session_key() : NULL,
+ input_so, output_pc, aux_filters,
+ F_ON(F_DISABLE_TERM_RESET_DISP, ps_global),
+ pipe_callback)) != NULL){
+ unsigned long ch;
+
+ fprintf(stdout,"\r\n%s Hit return to continue.", status);
+ fflush(stdout);
+ while((ch = read_char(300)) != ctrl('M') && ch != NO_OP_IDLE)
+ putchar(BELL);
+ }
+
+ if(resultf){
+ if(name_file_size(resultf) > 0L)
+ display_output_file(resultf, "Filter", NULL, DOF_BRIEF);
+
+ fs_give((void **)&resultf);
+ }
+
+ resume_busy_cue(0);
+#ifndef _WINDOWS
+ ClearScreen();
+#endif
+ fs_give((void **)&cmd);
+ }
+
+ return(status);
+}
+
+
+/*
+ * expand_filter_tokens - return an alloc'd string with any special tokens
+ * in the given filter expanded, NULL otherwise.
+ */
+char *
+expand_filter_tokens(char *filter, ENVELOPE *env, char **tmpf, char **resultf,
+ char **mtypef, int *key, int *hdrs)
+{
+ char **array, **q;
+ char *bp, *cmd = NULL, *p = NULL,
+ *tfn = NULL, *rfn = NULL, *dfn = NULL, *mfn = NULL,
+ *freeme_tfn = NULL, *freeme_rfn = NULL, *freeme_mfn = NULL;
+ int n = 0;
+ size_t len;
+
+ /*
+ * break filter into words delimited by whitespace so that we can
+ * look for tokens. First we count how many words.
+ */
+ if((bp = cpystr(filter)) != NULL)
+ p = strtok(bp, " \t");
+
+ if(p){
+ n++;
+ while(strtok(NULL, " \t") != NULL)
+ n++;
+ }
+
+ if(!n){
+ dprint((1, "Unexpected failure creating sending_filter\n"));
+ if(bp)
+ fs_give((void **)&bp);
+
+ return(cmd);
+ }
+
+ q = array = (char **) fs_get((n+1) * sizeof(*array));
+ memset(array, 0, (n+1) * sizeof(*array));
+ /* restore bp and form the array */
+ strncpy(bp, filter, strlen(filter)+1);
+ if((p = strtok(bp, " \t")) != NULL){
+ if(q-array < n+1)
+ *q++ = cpystr(p);
+
+ while((p = strtok(NULL, " \t")) != NULL && (q-array < n+1))
+ *q++ = cpystr(p);
+ }
+
+ if(bp)
+ fs_give((void **)&bp);
+
+ for(q = array; *q != NULL; q++){
+ if(!strcmp(*q, "_RECIPIENTS_")){
+ char *rl = NULL;
+
+ if(env){
+ size_t l;
+
+ char *to_l = addr_list_string(env->to,
+ simple_addr_string, 0),
+ *cc_l = addr_list_string(env->cc,
+ simple_addr_string, 0),
+ *bcc_l = addr_list_string(env->bcc,
+ simple_addr_string, 0);
+
+ l = strlen(to_l) + strlen(cc_l) + strlen(bcc_l) + 2;
+ rl = (char *) fs_get((l+1) * sizeof(char));
+ snprintf(rl, l+1, "%s %s %s", to_l, cc_l, bcc_l);
+ fs_give((void **)&to_l);
+ fs_give((void **)&cc_l);
+ fs_give((void **)&bcc_l);
+ for(to_l = rl; *to_l; to_l++) /* to_l overloaded! */
+ if(*to_l == ',') /* space delim'd list */
+ *to_l = ' ';
+ }
+
+ fs_give((void **)q);
+ *q = rl ? rl : cpystr("");
+ }
+ else if(!strcmp(*q, "_TMPFILE_")){
+ if(!tfn){
+ tfn = temp_nam(NULL, "sf"); /* send filter file */
+ if(!tfn)
+ dprint((1, "FAILED creat of _TMPFILE_\n"));
+ }
+
+ if(tmpf)
+ *tmpf = tfn;
+ else
+ freeme_tfn = tfn;
+
+ fs_give((void **)q);
+ *q = cpystr(tfn ? tfn : "");
+ }
+ else if(!strcmp(*q, "_RESULTFILE_")){
+ if(!rfn){
+ rfn = temp_nam(NULL, "rf");
+ /*
+ * We don't create the result file, the user does.
+ * That means we have to remove it after temp_nam creates it.
+ */
+ if(rfn)
+ our_unlink(rfn);
+ else
+ dprint((1, "FAILED creat of _RESULTFILE_\n"));
+ }
+
+ if(resultf)
+ *resultf = rfn;
+ else
+ freeme_rfn = rfn;
+
+ fs_give((void **)q);
+ *q = cpystr(rfn ? rfn : "");
+ }
+ else if(!strcmp(*q, "_MIMETYPE_")){
+ if(!mfn){
+ mfn = temp_nam(NULL, "mt");
+ /*
+ * We don't create the mimetype file, the user does.
+ * That means we have to remove it after temp_nam creates it.
+ */
+ if(mfn)
+ our_unlink(mfn);
+ else
+ dprint((1, "FAILED creat of _MIMETYPE_\n"));
+ }
+
+ if(mtypef)
+ *mtypef = mfn;
+ else
+ freeme_mfn = mfn;
+
+ fs_give((void **)q);
+ *q = cpystr(mfn ? mfn : "");
+ }
+ else if(!strcmp(*q, "_DATAFILE_")){
+ if((dfn = filter_data_file(1)) == NULL) /* filter data file */
+ dprint((1, "FAILED creat of _DATAFILE_\n"));
+
+ fs_give((void **)q);
+ *q = cpystr(dfn ? dfn : "");
+ }
+ else if(!strcmp(*q, "_PREPENDKEY_")){
+ (*q)[0] = '\0';
+ if(key)
+ *key = 1;
+ }
+ else if(!strcmp(*q, "_INCLUDEALLHDRS_")){
+ (*q)[0] = '\0';
+ if(hdrs)
+ *hdrs = 1;
+ }
+ }
+
+ /* count up required length */
+ for(len = 0, q = array; *q != NULL; q++)
+ len += (strlen(*q)+1);
+
+ cmd = fs_get((len+1) * sizeof(char));
+ cmd[len] = '\0';
+
+ /* cat together all the args */
+ p = cmd;
+ for(q = array; *q != NULL; q++){
+ sstrncpy(&p, *q, len+1-(p-cmd));
+ sstrncpy(&p, " ", len+1-(p-cmd));
+ }
+
+ cmd[len] = '\0';
+
+ if(freeme_rfn)
+ fs_give((void **) &freeme_rfn);
+
+ if(freeme_tfn){ /* this shouldn't happen */
+ our_unlink(freeme_tfn);
+ fs_give((void **) &freeme_tfn);
+ }
+
+ if(freeme_mfn)
+ fs_give((void **) &freeme_mfn);
+
+ free_list_array(&array);
+
+ return(cmd);
+}
+
+
+/*
+ * filter_session_key - function to return randomly generated number
+ * representing a key for this session. The idea is
+ * the display/sending filter could use it to muddle
+ * up any pass phrase or such stored in the
+ * "_DATAFILE_".
+ */
+char *
+filter_session_key(void)
+{
+ static char *key = NULL;
+
+ if(!key){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%ld", random());
+ key = cpystr(tmp_20k_buf);
+ }
+
+ return(key);
+}
+
+
+
+
+/*
+ * filter_data_file - function to return filename of scratch file for
+ * display and sending filters. This file is created
+ * the first time it's needed, and persists until pine
+ * exits.
+ */
+char *
+filter_data_file(int create_it)
+{
+ static char *fn = NULL;
+
+ if(!fn && create_it)
+ fn = temp_nam(NULL, "df");
+
+ return(fn);
+}
+
+
+/*
+ * df_static_trigger - look thru the display filter list and set the
+ * filter to any triggers that don't require scanning
+ * the message segment.
+ */
+char *
+dfilter_trigger(struct mail_bodystruct *body, char *cmdbuf, size_t cmdbuflen)
+{
+ int passed = 0;
+ char **l, *test, *cmd;
+
+ for(l = ps_global->VAR_DISPLAY_FILTERS ; l && *l && !passed; l++){
+
+ get_pair(*l, &test, &cmd, 1, 1);
+
+ dprint((5, "static trigger: \"%s\" --> \"%s\" and \"%s\"",
+ (l && *l) ? *l : "?",
+ test ? test : "<NULL>", cmd ? cmd : "<NULL>"));
+
+ if((passed = (df_valid_test(body, test) && valid_filter_command(&cmd))) != 0){
+ strncpy(cmdbuf, cmd, cmdbuflen);
+ cmdbuf[cmdbuflen-1] = '\0';
+ }
+
+ fs_give((void **) &test);
+ fs_give((void **) &cmd);
+ }
+
+ return(passed ? cmdbuf : NULL);
+}
+
+
+
+int
+df_valid_test(struct mail_bodystruct *body, char *test)
+{
+ int passed = 0;
+
+ if(!(passed = !test)){ /* NO test always wins */
+ if(!*test){
+ passed++; /* NULL test also wins! */
+ }
+ else if(body && !struncmp(test, "_CHARSET(", 9)){
+ char *p = strrindex(test, ')');
+
+ if(p){
+ *p = '\0'; /* tie off user charset */
+ if((p = parameter_val(body->parameter,"charset")) != NULL){
+ passed = !strucmp(test + 9, p);
+ fs_give((void **) &p);
+ }
+ else
+ passed = !strucmp(test + 9, "us-ascii");
+ }
+ else
+ dprint((1,
+ "filter trigger: malformed test: %s\n",
+ test ? test : "?"));
+ }
+ }
+
+ return(passed);
+}
diff --git a/alpine/dispfilt.h b/alpine/dispfilt.h
new file mode 100644
index 00000000..bac20821
--- /dev/null
+++ b/alpine/dispfilt.h
@@ -0,0 +1,30 @@
+/*
+ * $Id: dispfilt.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_DISPFILT_INCLUDED
+#define PINE_DISPFILT_INCLUDED
+
+
+
+/* exported protoypes */
+char *dfilter(char *, STORE_S *, gf_io_t, FILTLIST_S *);
+char *dfilter_trigger(BODY *, char *, size_t);
+char *expand_filter_tokens(char *, ENVELOPE *, char **, char **, char **, int *, int *);
+char *filter_session_key(void);
+char *filter_data_file(int);
+
+
+
+#endif /* PINE_DISPFILT_INCLUDED */
diff --git a/alpine/flagmaint.c b/alpine/flagmaint.c
new file mode 100644
index 00000000..c2385692
--- /dev/null
+++ b/alpine/flagmaint.c
@@ -0,0 +1,332 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: flagmaint.c 807 2007-11-09 01:21:33Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "headers.h"
+#include "flagmaint.h"
+#include "confscroll.h"
+#include "../pith/state.h"
+#include "../pith/flag.h"
+#include "../pith/status.h"
+#include "../pith/mailcmd.h"
+#include "../pith/icache.h"
+
+
+#define FLAG_ADD_RETURN 15
+
+
+/*
+ * Internal prototypes
+ */
+int flag_checkbox_tool(struct pine *, int, CONF_S **, unsigned);
+
+
+/*----------------------------------------------------------------------
+ Function to control flag set/clearing
+
+ Basically, turn the flags into a fake list of features...
+
+ Returns 0 unless user has added a keyword, then 1.
+
+ ----*/
+int
+flag_maintenance_screen(struct pine *ps, struct flag_screen *flags)
+{
+ int i, lv, lc, maxwidth, offset, need, rv = 0;
+ char tmp[1200], **p, *spacer;
+ CONF_S *ctmpa, *first_line;
+ struct flag_table *fp;
+ OPT_SCREEN_S screen;
+
+try_again:
+ maxwidth = MAX(MIN((ps->ttyo ? ps->ttyo->screen_cols : 80), 150), 30);
+ first_line = NULL;
+ ctmpa = NULL;
+
+ for(p = flags->explanation; p && *p; p++){
+ new_confline(&ctmpa);
+ ctmpa->keymenu = &flag_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = flag_checkbox_tool;
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->valoffset = 0;
+ ctmpa->value = cpystr(_(*p));
+ }
+
+ /* Now wire flags checkboxes together */
+ for(lv = 0, lc = 0, fp = (flags->flag_table ? *flags->flag_table : NULL);
+ fp && fp->name; fp++){ /* longest name */
+ if(fp->flag != F_COMMENT){
+ if(lv < (i = utf8_width(_(fp->name))))
+ lv = i;
+ if(fp->comment && lc < (i = utf8_width(fp->comment)))
+ lc = i;
+ }
+ }
+
+ lv = MIN(lv,100);
+ lc = MIN(lc,100);
+ if(lc > 0)
+ spacer = " ";
+ else
+ spacer = "";
+
+ offset = 6;
+ if((need = offset + 5 + lv + strlen(spacer) + lc) > maxwidth){
+ offset -= (need - maxwidth);
+ offset = MAX(0,offset);
+ if((need = offset + 5 + lv + strlen(spacer) + lc) > maxwidth){
+ spacer = " ";
+ if((need = offset + 5 + lv + strlen(spacer) + lc) > maxwidth){
+ lc -= (need - maxwidth);
+ lc = MAX(0,lc);
+ if(lc == 0)
+ spacer = "";
+ }
+ }
+ }
+
+ new_confline(&ctmpa);
+ ctmpa->keymenu = &flag_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = flag_checkbox_tool;
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->valoffset = 0;
+ ctmpa->value = cpystr("");
+
+ new_confline(&ctmpa);
+ ctmpa->keymenu = &flag_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = flag_checkbox_tool;
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->valoffset = 0;
+ utf8_snprintf(tmp, sizeof(tmp), "%*.*w %s", offset+3, offset+3, _("Set"), _("Flag/Keyword Name"));
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmpa->value = cpystr(tmp);
+
+ new_confline(&ctmpa);
+ ctmpa->keymenu = &flag_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = flag_checkbox_tool;
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->valoffset = 0;
+ snprintf(tmp, sizeof(tmp), "%*.*s--- %.*s",
+ offset, offset, "",
+ lv+lc+strlen(spacer), repeat_char(lv+lc+strlen(spacer), '-'));
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmpa->value = cpystr(tmp);
+
+ for(fp = (flags->flag_table ? *flags->flag_table : NULL);
+ fp && fp->name; fp++){ /* build the list */
+ new_confline(&ctmpa);
+ if(!first_line && (fp->flag != F_COMMENT))
+ first_line = ctmpa;
+
+ ctmpa->keymenu = &flag_keymenu;
+ ctmpa->tool = flag_checkbox_tool;
+ ctmpa->valoffset = offset;
+
+ if(fp->flag == F_COMMENT){
+ ctmpa->help = NO_HELP;
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->value = cpystr(fp->name);
+ }
+ else{
+ ctmpa->help = fp->help;
+ ctmpa->d.f.ftbl = flags->flag_table;
+ ctmpa->d.f.fp = fp;
+
+ utf8_snprintf(tmp, sizeof(tmp), "[%c] %-*.*w%s%-*.*w",
+ (fp->set == 0) ? ' ' : (fp->set == 1) ? 'X' : '?',
+ lv, lv, _(fp->name),
+ spacer, lc, lc, fp->comment ? fp->comment : "");
+ ctmpa->value = cpystr(tmp);
+ }
+ }
+
+ memset(&screen, 0, sizeof(screen));
+ /*
+ * TRANSLATORS: FLAG MAINTENANCE is a screen title.
+ * Print something1 using something2. configuration is something1
+ */
+ if(conf_scroll_screen(ps, &screen, first_line,
+ _("FLAG MAINTENANCE"),
+ _("configuration"), 0) == FLAG_ADD_RETURN){
+ int flags, r;
+ char keyword[500];
+ char nickname[500];
+ char prompt[500];
+ char *error = NULL;
+ KEYWORD_S *kw;
+ HelpType help;
+
+ /*
+ * User is asking to add a new keyword. We will add it to the
+ * mailbox if necessary and add it to the keywords list from
+ * Setup/Config. Then we will modify the flag_table and present
+ * the flag modification screen again.
+ */
+
+ ps->mangled_screen = 1;
+ keyword[0] = '\0';
+ flags = OE_APPEND_CURRENT;
+ help = NO_HELP;
+
+ do{
+ if(error){
+ q_status_message(SM_ORDER, 3, 4, error);
+ fs_give((void **) &error);
+ }
+
+ strncpy(prompt, _("Keyword to be added : "), sizeof(prompt)-1);
+ prompt[sizeof(prompt)-1] = '\0';
+ r = optionally_enter(keyword, -FOOTER_ROWS(ps_global), 0,
+ sizeof(keyword), prompt, NULL, help, &flags);
+
+ if(r == 3)
+ help = help == NO_HELP ? h_type_keyword : NO_HELP;
+ else if(r == 1){
+ cmd_cancelled("Add Keyword");
+ goto try_again;
+ }
+
+ removing_leading_and_trailing_white_space(keyword);
+
+ }while(r == 3 || keyword_check(keyword, &error));
+
+ for(kw = ps->keywords; kw; kw = kw->next){
+ if(kw->kw && !strucmp(kw->kw, keyword)){
+ q_status_message(SM_ORDER, 3, 4, _("Keyword already configured, changing nickname"));
+ break;
+ }
+ }
+
+ snprintf(prompt, sizeof(prompt), _("Optional nickname for \"%s\" : "), keyword);
+
+ nickname[0] = '\0';
+ help = NO_HELP;
+
+ do{
+ r = optionally_enter(nickname, -FOOTER_ROWS(ps_global), 0,
+ sizeof(nickname), prompt, NULL, help, &flags);
+
+ if(r == 3)
+ help = help == NO_HELP ? h_type_keyword_nickname : NO_HELP;
+ else if(r == 1){
+ cmd_cancelled("Add Keyword");
+ goto try_again;
+ }
+
+ removing_leading_and_trailing_white_space(nickname);
+
+ }while(r == 3);
+
+ if(keyword[0]){
+ char ***alval;
+ int offset = -1;
+ struct variable *var;
+
+ var = &ps_global->vars[V_KEYWORDS];
+ alval = ALVAL(var, Main);
+
+ for(kw = ps->keywords; kw; kw = kw->next){
+ offset++;
+ if(kw->kw && !strucmp(kw->kw, keyword)){
+ /* looks like it should already exist at offset */
+ break;
+ }
+ }
+
+ if(!kw)
+ offset = -1;
+
+ if(offset >= 0 && (*alval) && (*alval)[offset]){
+ fs_give((void **) &(*alval)[offset]);
+ (*alval)[offset] = put_pair(nickname, keyword);
+ }
+ else if(!*alval){
+ offset = 0;
+ *alval = (char **) fs_get(2*sizeof(char *));
+ (*alval)[offset] = put_pair(nickname, keyword);
+ (*alval)[offset+1] = NULL;
+ }
+ else{
+ for(offset=0; (*alval)[offset]; offset++);
+ ;
+
+ fs_resize((void **) alval, (offset + 2) * sizeof(char *));
+ (*alval)[offset] = put_pair(nickname, keyword);
+ (*alval)[offset+1] = NULL;
+ }
+
+ set_current_val(var, TRUE, FALSE);
+ if(ps_global->prc)
+ ps_global->prc->outstanding_pinerc_changes = 1;
+
+ if(ps_global->keywords)
+ free_keyword_list(&ps_global->keywords);
+
+ if(var->current_val.l && var->current_val.l[0])
+ ps_global->keywords = init_keyword_list(var->current_val.l);
+
+ clear_index_cache(ps_global->mail_stream, 0);
+
+ rv = 1;
+ }
+ }
+
+ ps->mangled_screen = 1;
+
+ return(rv);
+}
+
+
+/*
+ * Message flag manipulation tool
+ *
+ *
+ * returns: -1 on unrecognized cmd, 0 if no change, 1 if change
+ */
+int
+flag_checkbox_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ int rv = 0, state;
+
+ switch(cmd){
+ case MC_TOGGLE: /* mark/unmark feature */
+ state = (*cl)->d.f.fp->set;
+ state = (state == 1) ? 0 : (!state && ((*cl)->d.f.fp->ukn)) ? 2 : 1;
+ (*cl)->value[1] = (state == 0) ? ' ' : ((state == 1) ? 'X': '?');
+ (*cl)->d.f.fp->set = state;
+ rv = 1;
+ break;
+
+ case MC_ADD:
+ rv = FLAG_ADD_RETURN;
+ break;
+
+ case MC_EXIT: /* exit */
+ rv = simple_exit_cmd(flags);
+ break;
+
+ default :
+ rv = -1;
+ break;
+ }
+
+ return(rv);
+}
diff --git a/alpine/flagmaint.h b/alpine/flagmaint.h
new file mode 100644
index 00000000..dbc9dfbf
--- /dev/null
+++ b/alpine/flagmaint.h
@@ -0,0 +1,55 @@
+/*
+ * $Id: flagmaint.h 807 2007-11-09 01:21:33Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_FLAGMAINT_INCLUDED
+#define PINE_FLAGMAINT_INCLUDED
+
+
+#include "../pith/state.h"
+
+
+/*
+ * Structures to control flag maintenance screen
+ */
+struct flag_table {
+ char *name; /* flag's name or keyword's nickname */
+ HelpType help; /* help text */
+ long flag; /* flag tag (i.e., F_DEL ) */
+ unsigned set:2; /* its state (set, unset, unknown) */
+ unsigned ukn:1; /* allow unknown state */
+ char *keyword; /* only for user-defined, could be same as name */
+ char *comment; /* comment about the name, keyword name if there is a nick */
+};
+
+
+struct flag_screen {
+ char **explanation;
+ struct flag_table **flag_table;
+};
+
+/*
+ * Some defs to help keep flag setting straight...
+ */
+#define CMD_FLAG_CLEAR FALSE
+#define CMD_FLAG_SET TRUE
+#define CMD_FLAG_UNKN 2
+
+
+/* exported protoypes */
+int flag_maintenance_screen(struct pine *, struct flag_screen *);
+
+
+#endif /* PINE_FLAGMAINT_INCLUDED */
diff --git a/alpine/folder.c b/alpine/folder.c
new file mode 100644
index 00000000..02a73b71
--- /dev/null
+++ b/alpine/folder.c
@@ -0,0 +1,6922 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: folder.c 1144 2008-08-14 16:53:34Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ folder.c
+
+ Screen to display and manage all the users folders
+
+This puts up a list of all the folders in the users mail directory on
+the screen spacing it nicely. The arrow keys move from one to another
+and the user can delete the folder or select it to change to or copy a
+message to. The dispay lets the user scroll up or down a screen full,
+or search for a folder name.
+ ====*/
+
+
+#include "headers.h"
+#include "folder.h"
+#include "keymenu.h"
+#include "status.h"
+#include "context.h"
+#include "mailview.h"
+#include "mailindx.h"
+#include "mailcmd.h"
+#include "titlebar.h"
+#include "alpine.h"
+#include "send.h"
+#include "help.h"
+#include "imap.h"
+#include "signal.h"
+#include "reply.h"
+#include "setup.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/folder.h"
+#include "../pith/flag.h"
+#include "../pith/filter.h"
+#include "../pith/msgno.h"
+#include "../pith/thread.h"
+#include "../pith/util.h"
+#include "../pith/stream.h"
+#include "../pith/save.h"
+#include "../pith/busy.h"
+#include "../pith/list.h"
+
+
+#define SUBSCRIBE_PMT \
+ _("Enter newsgroup name (or partial name to get a list): ")
+#define LISTMODE_GRIPE _("Use \"X\" to mark selections in list mode")
+#define SEL_ALTER_PMT _("ALTER folder selection : ")
+#define SEL_TEXT_PMT _("Select by folder Name or Contents ? ")
+#define SEL_PROP_PMT _("Select by which folder property ? ")
+#define DIR_FOLD_PMT \
+ _("Folder and directory of the same name will be deleted. Continue")
+
+
+/*
+ * folder_list_write
+ */
+#define FLW_NONE 0x00
+#define FLW_LUNK 0x01 /* Using handles */
+#define FLW_SLCT 0x02 /* Some folder is selected, may need X to show it */
+#define FLW_LIST 0x04 /* Allow for ListMode for subscribing */
+#define FLW_UNSEEN 0x08 /* Add (unseen) */
+
+
+
+/*----------------------------------------------------------------------
+ The data needed to redraw the folders screen, including the case where the
+screen changes size in which case it may recalculate the folder_display.
+ ----*/
+
+
+/*
+ * Struct managing folder_lister arguments and holding state
+ * for various internal methods
+ */
+typedef struct _folder_screen {
+ CONTEXT_S *context; /* current collection */
+ CONTEXT_S *list_cntxt; /* list mode collection */
+ MAILSTREAM **cache_streamp; /* cached mailstream */
+ char first_folder[MAXFOLDER];
+ unsigned first_dir:1; /* first_folder is a dir */
+ unsigned combined_view:1; /* display flat folder list */
+ unsigned no_dirs:1; /* no dirs in this screen */
+ unsigned no_empty_dirs:1; /* no empty dirs on this screen */
+ unsigned relative_path:1; /* return fully-qual'd specs */
+ unsigned save_sel:1;
+ unsigned force_intro:1;
+ unsigned agg_ops:1;
+ unsigned include_unseen_cnt:1;
+ struct key_menu *km; /* key label/command bindings */
+ struct _func_dispatch {
+ int (*valid)(FOLDER_S *, struct _folder_screen *);
+ struct {
+ HelpType text;
+ char *title;
+ } help;
+ struct {
+ char *bar;
+ TitleBarType style;
+ } title;
+ } f;
+} FSTATE_S;
+
+
+/*
+ * Struct mananging folder_lister metadata as it get's passed
+ * in and back up thru scrolltool
+ */
+typedef struct _folder_proc {
+ FSTATE_S *fs;
+ STRLIST_S *rv;
+ unsigned done:1; /* done listing folders?... */
+ unsigned all_done:1; /* ...and will list no more forever */
+} FPROC_S;
+
+#define FPROC(X) ((FPROC_S *)(X)->proc.data.p)
+
+
+typedef struct _scanarg {
+ MAILSTREAM *stream;
+ int newstream;
+ CONTEXT_S *context;
+ char *pattern;
+ char type;
+} SCANARG_S;
+
+
+typedef struct _statarg {
+ MAILSTREAM *stream;
+ int newstream;
+ CONTEXT_S *context;
+ long flags;
+ long nmsgs;
+ int cmp;
+} STATARG_S;
+
+
+/*
+ * Internal prototypes
+ */
+STRLIST_S *folders_for_subscribe(struct pine *, CONTEXT_S *, char *);
+int folders_for_post(struct pine *, CONTEXT_S **, char *);
+int folder_selector(struct pine *, FSTATE_S *, char *, CONTEXT_S **);
+void folder_sublist_context(char *, CONTEXT_S *, CONTEXT_S *, FDIR_S **, int);
+CONTEXT_S *context_screen(CONTEXT_S *, struct key_menu *, int);
+int exit_collection_add(struct headerentry *, void (*)(void), int, char **);
+char *cancel_collection_add(void (*)(void));
+char *cancel_collection_edit(void (*)(void));
+char *cancel_collection_editing(char *, void (*)(void));
+int build_namespace(char *, char **, char **, BUILDER_ARG *, int *);
+int fl_val_gen(FOLDER_S *, FSTATE_S *);
+int fl_val_writable(FOLDER_S *, FSTATE_S *);
+int fl_val_subscribe(FOLDER_S *, FSTATE_S *);
+STRLIST_S *folder_lister(struct pine *, FSTATE_S *);
+int folder_list_text(struct pine *, FPROC_S *, gf_io_t, HANDLE_S **, int);
+int folder_list_write(gf_io_t, HANDLE_S **, CONTEXT_S *, int, char *, int);
+int folder_list_write_prefix(FOLDER_S *, int, gf_io_t);
+int folder_list_write_middle(FOLDER_S *fp, CONTEXT_S *ctxt, gf_io_t pc, HANDLE_S *);
+int folder_list_write_suffix(FOLDER_S *, int, gf_io_t);
+int color_monitored_unseen(FOLDER_S *, int);
+int folder_list_ith(int, CONTEXT_S *);
+char *folder_list_center_space(char *, int);
+HANDLE_S *folder_list_handle(FSTATE_S *, HANDLE_S *);
+int folder_processor(int, MSGNO_S *, SCROLL_S *);
+int folder_lister_clickclick(SCROLL_S *);
+int folder_lister_choice(SCROLL_S *);
+int folder_lister_finish(SCROLL_S *, CONTEXT_S *, int);
+int folder_lister_addmanually(SCROLL_S *);
+void folder_lister_km_manager(SCROLL_S *, int);
+void folder_lister_km_sel_manager(SCROLL_S *, int);
+void folder_lister_km_sub_manager(SCROLL_S *, int);
+int folder_select(struct pine *, CONTEXT_S *, int);
+int folder_lister_select(FSTATE_S *, CONTEXT_S *, int, int);
+int folder_lister_parent(FSTATE_S *, CONTEXT_S *, int, int);
+char *folder_lister_fullname(FSTATE_S *, char *);
+void folder_export(SCROLL_S *);
+int folder_import(SCROLL_S *, char *, size_t);
+int folder_select_toggle(CONTEXT_S *, int, int (*)(CONTEXT_S *, int));
+char *end_bracket_no_nest(char *);
+int group_subscription(char *, size_t, CONTEXT_S *);
+int rename_folder(CONTEXT_S *, int, char *, size_t, MAILSTREAM *);
+int delete_folder(CONTEXT_S *, int, char *, size_t, MAILSTREAM **);
+void print_folders(FPROC_S *);
+int scan_get_pattern(char *, char *, int);
+int folder_select_text(struct pine *, CONTEXT_S *, int);
+int foreach_do_scan(FOLDER_S *, void *);
+int scan_scan_folder(MAILSTREAM *, CONTEXT_S *, FOLDER_S *, char *);
+int folder_select_props(struct pine *, CONTEXT_S *, int);
+int folder_select_count(long *, int *);
+int foreach_do_stat(FOLDER_S *, void *);
+int foreach_folder(CONTEXT_S *, int, int (*)(FOLDER_S *, void *), void *);
+int folder_delimiter(char *);
+int shuffle_incoming_folders(CONTEXT_S *, int);
+int swap_incoming_folders(int, int, FLIST *);
+int search_folder_list(void *, char *);
+char *get_post_list(char **);
+char *quote_brackets_if_needed(char *);
+#ifdef _WINDOWS
+int folder_list_popup(SCROLL_S *, int);
+int folder_list_select_popup(SCROLL_S *, int);
+void folder_popup_config(FSTATE_S *, struct key_menu *,MPopup *);
+#endif
+
+
+/*----------------------------------------------------------------------
+ Front end to folder lister when it's called from the main menu
+
+ Args: ps -- The general pine_state data structure
+
+ Result: runs context and folder listers
+
+ ----*/
+void
+folder_screen(struct pine *ps)
+{
+ int n = 1;
+ CONTEXT_S *cntxt = ps->context_current;
+ STRLIST_S *folders;
+ FSTATE_S fs;
+ MAILSTREAM *cache_stream = NULL;
+
+ dprint((1, "=== folder_screen called ====\n"));
+ mailcap_free(); /* free resources we won't be using for a while */
+ ps->next_screen = SCREEN_FUN_NULL;
+
+ /* Initialize folder state and dispatches */
+ memset(&fs, 0, sizeof(FSTATE_S));
+ fs.context = cntxt;
+ fs.cache_streamp = &cache_stream;
+ fs.combined_view = F_ON(F_CMBND_FOLDER_DISP, ps_global) != 0;
+ fs.agg_ops = F_ON(F_ENABLE_AGG_OPS, ps_global) != 0;
+ fs.relative_path = 1;
+ fs.include_unseen_cnt = 1;
+ fs.f.valid = fl_val_gen;
+ /* TRANSLATORS: The all upper case things are screen titles */
+ fs.f.title.bar = _("FOLDER LIST");
+ fs.f.title.style = FolderName;
+ fs.f.help.text = h_folder_maint;
+ fs.f.help.title = _("HELP FOR FOLDERS");
+ fs.km = &folder_km;
+
+ if(context_isambig(ps->cur_folder)
+ && (IS_REMOTE(ps->cur_folder) || !is_absolute_path(ps->cur_folder)
+ || (cntxt && cntxt->context && cntxt->context[0] == '{'))){
+ if(strlen(ps_global->cur_folder) < MAXFOLDER - 1){
+ strncpy(fs.first_folder, ps_global->cur_folder, MAXFOLDER);
+ fs.first_folder[MAXFOLDER-1] = '\0';
+ }
+
+ /*
+ * If we're asked to start in the folder list of the current
+ * folder and it looks like the current folder is part of the
+ * current context, try to start in the list of folders in the
+ * current context.
+ */
+ if(ps->start_in_context || fs.combined_view){
+ char tmp[MAILTMPLEN], *p, *q;
+ FDIR_S *fp;
+
+ ps->start_in_context = 0;
+ n = 0;
+
+ if(!(NEWS_TEST(cntxt) || (cntxt->use & CNTXT_INCMNG))
+ && cntxt->dir->delim
+ && strchr(ps->cur_folder, cntxt->dir->delim)){
+ for(p = strchr(q = ps->cur_folder, cntxt->dir->delim);
+ p;
+ p = strchr(q = ++p, cntxt->dir->delim)){
+ strncpy(tmp, q, MIN(p - q, sizeof(tmp)-1));
+ tmp[MIN(p - q, sizeof(tmp)-1)] = '\0';
+
+ fp = next_folder_dir(cntxt, tmp, FALSE, fs.cache_streamp);
+
+ fp->desc = folder_lister_desc(cntxt, fp);
+
+ /* Insert new directory into list */
+ fp->delim = cntxt->dir->delim;
+ fp->prev = cntxt->dir;
+ fp->status |= CNTXT_SUBDIR;
+ cntxt->dir = fp;
+ }
+ }
+ }
+ }
+
+ while(ps->next_screen == SCREEN_FUN_NULL
+ && ((n++) ? (cntxt = context_screen(cntxt,&c_mgr_km,1)) != NULL :1)){
+
+ fs.context = cntxt;
+ if(F_ON(F_ENABLE_INCOMING_CHECKING, ps) && ps->VAR_INCOMING_FOLDERS && ps->VAR_INCOMING_FOLDERS[0])
+ ps->in_folder_screen = 1;
+
+ if((folders = folder_lister(ps, &fs)) != NULL){
+
+ ps->in_folder_screen = 0;
+
+ if(ps && ps->ttyo){
+ blank_keymenu(ps->ttyo->screen_rows - 2, 0);
+ ps->mangled_footer = 1;
+ }
+
+ if(do_broach_folder((char *) folders->name,
+ fs.context, fs.cache_streamp
+ && *fs.cache_streamp ? fs.cache_streamp
+ : NULL, 0L) == 1){
+ reset_context_folders(ps->context_list);
+ ps->next_screen = mail_index_screen;
+ }
+
+ if(fs.cache_streamp)
+ *fs.cache_streamp = NULL;
+ free_strlist(&folders);
+ }
+
+ ps->in_folder_screen = 0;
+ }
+
+ if(fs.cache_streamp && *fs.cache_streamp)
+ pine_mail_close(*fs.cache_streamp);
+
+ ps->prev_screen = folder_screen;
+}
+
+
+/*----------------------------------------------------------------------
+ Front end to folder lister when it's called from the main menu
+
+ Args: ps -- The general pine_state data structure
+
+ Result: runs context and folder listers
+
+ ----*/
+void
+folder_config_screen(struct pine *ps, int edit_exceptions)
+{
+ CONT_SCR_S css;
+ char title[50], htitle[50];
+
+ dprint((1, "=== folder_config_screen called ====\n"));
+ mailcap_free(); /* free resources we won't be using for a while */
+
+ if(edit_exceptions){
+ snprintf(title, sizeof(title), _("SETUP EXCEPTIONS COLLECTION LIST"));
+ snprintf(htitle, sizeof(htitle), _("HELP FOR SETUP EXCEPTIONS COLLECTIONS"));
+ }
+ else{
+ snprintf(title, sizeof(title), _("SETUP COLLECTION LIST"));
+ snprintf(htitle, sizeof(htitle), _("HELP FOR SETUP COLLECTIONS"));
+ }
+
+ memset(&css, 0, sizeof(CONT_SCR_S));
+ css.title = title;
+ /* TRANSLATORS: Print something1 using something2.
+ contexts is something1 */
+ css.print_string = _("contexts");
+ css.contexts = &ps_global->context_list;
+ css.help.text = h_collection_maint;
+ css.help.title = htitle;
+ css.keymenu = &c_cfg_km;
+ css.edit = 1;
+
+ /*
+ * Use conf_scroll_screen to manage display/selection
+ * of contexts
+ */
+ context_config_screen(ps_global, &css, edit_exceptions);
+}
+
+
+/*----------------------------------------------------------------------
+ Browse folders for ^T selection from the Goto Prompt
+
+ Args: ps --
+ cntxtp -- pointer to addr of context to start in, list, and return
+ folder -- pointer to buffer inwhich to return selected folder
+
+ Returns: 1 if we have something valid in cntxtp and folder
+ 0 if problem or user cancelled
+
+ ----*/
+int
+folders_for_goto(struct pine *ps, CONTEXT_S **cntxtp, char *folder, int sublist)
+{
+ int rv;
+ CONTEXT_S fake_context;
+ FDIR_S *fake_dir = NULL;
+ FSTATE_S fs;
+
+ dprint((1, "=== folders_for_goto called ====\n"));
+
+ /* Initialize folder state and dispatches */
+ memset(&fs, 0, sizeof(FSTATE_S));
+ fs.context = *cntxtp;
+ fs.combined_view = !sublist && F_ON(F_CMBND_FOLDER_DISP, ps_global) != 0;
+ fs.f.valid = fl_val_gen;
+ fs.f.title.bar = _("GOTO: SELECT FOLDER");
+ fs.f.title.style = FolderName;
+ fs.f.help.text = h_folder_open;
+ fs.f.help.title = _("HELP FOR OPENING FOLDERS");
+ fs.km = &folder_sel_km;
+
+ /* If we were provided a string,
+ * dummy up a context for a substring match
+ */
+ if(sublist && *folder && context_isambig(folder)){
+ if((*cntxtp)->use & CNTXT_INCMNG){
+ q_status_message(SM_ORDER, 0, 3,
+ _("All folders displayed for Incoming Collection"));
+ }
+ else{
+ folder_sublist_context(folder, *cntxtp, &fake_context,
+ &fake_dir, sublist);
+ fs.context = &fake_context;
+ fs.relative_path = 1;
+ fs.force_intro = 1;
+ cntxtp = &fs.context;
+ }
+ }
+
+ rv = folder_selector(ps, &fs, folder, cntxtp);
+
+ if(fake_dir)
+ free_fdir(&fake_dir, TRUE);
+
+ return(rv);
+}
+
+
+/*----------------------------------------------------------------------
+ Browse folders for ^T selection from the Save Prompt
+
+ Args: ps --
+ cntxtp -- pointer to addr of context to start in, list, and return
+ folder -- pointer to buffer inwhich to return selected folder
+
+ Returns: 1 if we have something valid in cntxtp and folder
+ 0 if problem or user cancelled
+
+ ----*/
+int
+folders_for_save(struct pine *ps, CONTEXT_S **cntxtp, char *folder, int sublist)
+{
+ int rv;
+ CONTEXT_S fake_context;
+ FDIR_S *fake_dir = NULL;
+ FSTATE_S fs;
+
+ dprint((1, "=== folders_for_save called ====\n"));
+
+ /* Initialize folder state and dispatches */
+ memset(&fs, 0, sizeof(FSTATE_S));
+ fs.context = *cntxtp;
+ fs.combined_view = F_ON(F_CMBND_FOLDER_DISP, ps_global) != 0;
+ fs.f.valid = fl_val_gen;
+ fs.f.title.bar = _("SAVE: SELECT FOLDER");
+ fs.f.title.style = MessageNumber;
+ fs.f.help.text = h_folder_save;
+ fs.f.help.title = _("HELP FOR SAVING MESSAGES TO FOLDERS");
+ fs.km = &folder_sela_km;
+
+ /* If we were provided a string,
+ * dummy up a context for a substring match
+ */
+ if(sublist && *folder && context_isambig(folder)){
+ if((*cntxtp)->use & CNTXT_INCMNG){
+ q_status_message(SM_ORDER, 0, 3,
+ _("All folders displayed for Incoming Collection"));
+ }
+ else{
+ folder_sublist_context(folder, *cntxtp, &fake_context,
+ &fake_dir, sublist);
+ fs.context = &fake_context;
+ fs.relative_path = 1;
+ fs.force_intro = 1;
+ cntxtp = &fs.context;
+ }
+ }
+
+ rv = folder_selector(ps, &fs, folder, cntxtp);
+
+ if(fake_dir)
+ free_fdir(&fake_dir, TRUE);
+
+ return(rv);
+}
+
+
+/*----------------------------------------------------------------------
+ Browse folders for ^T selection from the Subscribe Prompt
+
+ Args: ps --
+ cntxtp -- pointer to addr of context to start in, list, and return
+ folder -- pointer to buffer inwhich to return selected folder
+
+ Returns: 1 if we have something valid in cntxtp and folder
+ 0 if problem or user cancelled
+
+ ----*/
+STRLIST_S *
+folders_for_subscribe(struct pine *ps, CONTEXT_S *cntxt, char *folder)
+{
+ STRLIST_S *folders = NULL;
+ FSTATE_S fs;
+ void (*redraw)(void);
+
+ dprint((1, "=== folders_for_sub called ====\n"));
+
+ /* Initialize folder state and dispatches */
+ memset(&fs, 0, sizeof(FSTATE_S));
+ fs.context = cntxt;
+ fs.f.valid = fl_val_subscribe;
+ fs.f.title.bar = _("SUBSCRIBE: SELECT FOLDER");
+ fs.f.title.style = FolderName;
+ fs.f.help.text = h_folder_subscribe;
+ fs.f.help.title = _("HELP SELECTING NEWSGROUP TO SUBSCRIBE TO");
+ fs.km = &folder_sub_km;
+ fs.force_intro = 1;
+
+ fs.context = cntxt;
+ redraw = ps_global->redrawer;
+ folders = folder_lister(ps, &fs);
+ ps_global->redrawer = redraw;
+ if(ps_global->redrawer)
+ (*ps_global->redrawer)();
+
+ return(folders);
+}
+
+
+/*----------------------------------------------------------------------
+ Browse folders for ^T selection for posting
+
+ Args: ps --
+ cntxtp -- pointer to addr of context to start in, list, and return
+ folder -- pointer to buffer inwhich to return selected folder
+
+ Returns: 1 if we have something valid in cntxtp and folder
+ 0 if problem or user cancelled
+
+ ----*/
+int
+folders_for_post(struct pine *ps, CONTEXT_S **cntxtp, char *folder)
+{
+ FSTATE_S fs;
+
+ dprint((1, "=== folders_for_post called ====\n"));
+
+ /* Initialize folder state and dispatches */
+ memset(&fs, 0, sizeof(FSTATE_S));
+ fs.context = *cntxtp;
+ fs.f.valid = fl_val_subscribe;
+ fs.f.title.bar = _("NEWS: SELECT GROUP");
+ fs.f.title.style = FolderName;
+ fs.f.help.text = h_folder_postnews;
+ fs.f.help.title = _("HELP FOR SELECTING NEWSGROUP TO POST TO");
+ fs.km = &folder_post_km;
+
+ return(folder_selector(ps, &fs, folder, cntxtp));
+}
+
+
+int
+folder_selector(struct pine *ps, FSTATE_S *fs, char *folder, CONTEXT_S **cntxtp)
+{
+ int rv = 0;
+ STRLIST_S *folders;
+
+ do{
+ fs->context = *cntxtp;
+ if((folders = folder_lister(ps, fs)) != NULL){
+ strncpy(folder, (char *) folders->name, MAILTMPLEN-1);
+ folder[MAILTMPLEN-1] = '\0';
+ free_strlist(&folders);
+ *cntxtp = fs->context;
+ rv++;
+ break;
+ }
+ else if(!(fs->context
+ && (fs->context->next || fs->context->prev))
+ || fs->combined_view)
+ break;
+ }
+ while((*cntxtp = context_screen(*cntxtp, &c_sel_km, 0)) != NULL);
+
+ return(rv);
+}
+
+
+void
+folder_sublist_context(char *folder, CONTEXT_S *cntxt, CONTEXT_S *new_cntxt, FDIR_S **new_dir, int lev)
+{
+ char *p, *q, *ref, *wildcard;
+
+ *new_cntxt = *cntxt;
+ new_cntxt->next = new_cntxt->prev = NULL;
+ new_cntxt->dir = *new_dir = (FDIR_S *) fs_get(sizeof(FDIR_S));
+ memset(*new_dir, 0, sizeof(FDIR_S));
+ (*new_dir)->status |= CNTXT_NOFIND;
+ (*new_dir)->folders = init_folder_entries();
+ if(!((*new_dir)->delim = cntxt->dir->delim)){
+ /* simple LIST to acquire delimiter, doh */
+ build_folder_list(NULL, new_cntxt, "", NULL,
+ NEWS_TEST(new_cntxt) ? BFL_LSUB : BFL_NONE);
+ new_cntxt->dir->status = CNTXT_NOFIND;
+ }
+
+ wildcard = NEWS_TEST(new_cntxt) ? "*" : "%";
+
+ if((p = strrindex(folder, (*new_dir)->delim)) != NULL){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s", p + 1, wildcard);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ (*new_dir)->view.internal = cpystr(tmp_20k_buf);
+ for(ref = tmp_20k_buf, q = new_cntxt->context;
+ (*ref = *q) != '\0' && !(*q == '%' && *(q+1) == 's');
+ ref++, q++)
+ ;
+
+ for(q = folder; q <= p; q++, ref++)
+ *ref = *q;
+
+ *ref = '\0';
+ (*new_dir)->ref = cpystr(tmp_20k_buf);
+
+ (*new_dir)->status |= CNTXT_SUBDIR;
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
+ (lev > 1) ? wildcard : "", folder, wildcard);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ (*new_dir)->view.internal = cpystr(tmp_20k_buf);
+ /* leave (*new_dir)->ref == NULL */
+ }
+
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("List of folders matching \"%s*\""), folder);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ (*new_dir)->desc = cpystr(tmp_20k_buf);
+}
+
+
+/*----------------------------------------------------------------------
+ Browse folders for ^T selection from the composer
+
+ Args: error_mess -- pointer to place to return an error message
+
+ Returns: result if folder selected, NULL if not
+ Composer expects the result to be alloc'd here
+
+ ----*/
+char *
+folders_for_fcc(char **errmsg)
+{
+ char *rs = NULL;
+ STRLIST_S *folders;
+ FSTATE_S fs;
+
+ dprint((1, "=== folders_for_fcc called ====\n"));
+
+ /* Coming back from composer */
+ fix_windsize(ps_global);
+ init_sigwinch();
+
+ /* Initialize folder state and dispatches */
+ memset(&fs, 0, sizeof(FSTATE_S));
+ fs.combined_view = F_ON(F_CMBND_FOLDER_DISP, ps_global) != 0;
+ fs.f.valid = fl_val_gen;
+ fs.f.title.bar = _("FCC: SELECT FOLDER");
+ fs.f.title.style = FolderName;
+ fs.f.help.text = h_folder_fcc;
+ fs.f.help.title = _("HELP FOR SELECTING THE FCC");
+ fs.km = &folder_sela_km;
+
+ /* start in the default save context */
+ fs.context = default_save_context(ps_global->context_list);
+
+ do{
+ if((folders = folder_lister(ps_global, &fs)) != NULL){
+ char *name;
+
+ /* replace nickname with full name */
+ if(!(name = folder_is_nick((char *) folders->name,
+ FOLDERS(fs.context), 0)))
+ name = (char *) folders->name;
+
+ if(context_isambig(name) && !((fs.context->use) & CNTXT_SAVEDFLT)){
+ char path_in_context[MAILTMPLEN];
+
+ context_apply(path_in_context, fs.context, name,
+ sizeof(path_in_context));
+ if(!(IS_REMOTE(path_in_context)
+ || is_absolute_path(path_in_context))){
+ /*
+ * Name is relative to the home directory,
+ * so have to add that. Otherwise, the sender will
+ * assume it is in the primary collection since it
+ * will still be ambiguous.
+ */
+ build_path(tmp_20k_buf, ps_global->ui.homedir,
+ path_in_context, SIZEOF_20KBUF);
+ rs = cpystr(tmp_20k_buf);
+ }
+ else
+ rs = cpystr(path_in_context);
+ }
+ else
+ rs = cpystr(name);
+
+ free_strlist(&folders);
+ break;
+ }
+ else if(!(fs.context && (fs.context->next || fs.context->prev))
+ || fs.combined_view)
+ break;
+ }
+ while((fs.context = context_screen(fs.context, &c_fcc_km, 0)) != NULL);
+
+ return(rs);
+}
+
+
+/*----------------------------------------------------------------------
+ Browse folders for ^T selection from the role editor
+
+ Returns: result if folder selected, NULL if not
+ Tesult is alloc'd here
+
+ ----*/
+char *
+folder_for_config(int flags)
+{
+ char *rs = NULL;
+ STRLIST_S *folders;
+ FSTATE_S fs;
+
+ dprint((1, "=== folder_for_config called ====\n"));
+
+ /* Initialize folder state and dispatches */
+ memset(&fs, 0, sizeof(FSTATE_S));
+ fs.combined_view = F_ON(F_CMBND_FOLDER_DISP, ps_global) != 0;
+ fs.f.valid = fl_val_gen;
+ fs.f.title.bar = _("SELECT FOLDER");
+ fs.f.title.style = FolderName;
+ fs.km = &folder_sela_km;
+ if(flags & FOR_PATTERN){
+ fs.f.help.text = h_folder_pattern_roles;
+ fs.f.help.title = _("HELP FOR SELECTING CURRENT FOLDER");
+ }
+ else if(flags & FOR_OPTIONSCREEN){
+ fs.f.help.text = h_folder_stayopen_folders;
+ fs.f.help.title = _("HELP FOR SELECTING FOLDER");
+ }
+ else{
+ fs.f.help.text = h_folder_action_roles;
+ fs.f.help.title = _("HELP FOR SELECTING FOLDER");
+ }
+
+ /* start in the current context */
+ fs.context = ps_global->context_current;
+
+ do{
+ if((folders = folder_lister(ps_global, &fs)) != NULL){
+ char *name = NULL;
+
+ /* replace nickname with full name */
+ if(!(flags & (FOR_PATTERN | FOR_OPTIONSCREEN)))
+ name = folder_is_nick((char *) folders->name,
+ FOLDERS(fs.context), 0);
+
+ if(!name)
+ name = (char *) folders->name;
+
+ if(context_isambig(name) &&
+ !(flags & (FOR_PATTERN | FOR_OPTIONSCREEN) &&
+ folder_is_nick(name, FOLDERS(fs.context), 0))){
+ char path_in_context[MAILTMPLEN];
+
+ context_apply(path_in_context, fs.context, name,
+ sizeof(path_in_context));
+
+ /*
+ * We may still have a non-fully-qualified name. In the
+ * action case, that will be interpreted in the primary
+ * collection instead of as a local name.
+ * Qualify that name the same way we
+ * qualify it in match_pattern_folder_specific.
+ */
+ if(!(IS_REMOTE(path_in_context) ||
+ path_in_context[0] == '#')){
+ if(strlen(path_in_context) < (MAILTMPLEN/2)){
+ char tmp[MAX(MAILTMPLEN,NETMAXMBX)];
+ char *t;
+
+ t = mailboxfile(tmp, path_in_context);
+ rs = cpystr(t);
+ }
+ else{ /* the else part should never happen */
+ build_path(tmp_20k_buf, ps_global->ui.homedir,
+ path_in_context, SIZEOF_20KBUF);
+ rs = cpystr(tmp_20k_buf);
+ }
+ }
+ else
+ rs = cpystr(path_in_context);
+ }
+ else
+ rs = cpystr(name);
+
+ free_strlist(&folders);
+ break;
+ }
+ else if(!(fs.context && (fs.context->next || fs.context->prev))
+ || fs.combined_view)
+ break;
+ }
+ while((fs.context = context_screen(fs.context, &c_fcc_km, 0)) != NULL);
+
+ return(rs);
+}
+
+
+/*
+ * offer screen with list of contexts to select and some sort
+ * of descriptions
+ */
+CONTEXT_S *
+context_screen(CONTEXT_S *start, struct key_menu *km, int edit_config)
+{
+ /* If a list, let the user tell us what to do */
+ if(F_OFF(F_CMBND_FOLDER_DISP, ps_global)
+ && ps_global->context_list
+ && ps_global->context_list->next){
+ CONT_SCR_S css;
+
+ memset(&css, 0, sizeof(CONT_SCR_S));
+ css.title = _("COLLECTION LIST");
+ css.print_string = _("contexts");
+ css.start = start;
+ css.contexts = &ps_global->context_list;
+ css.help.text = h_collection_screen;
+ css.help.title = _("HELP FOR COLLECTION LIST");
+ css.keymenu = km;
+ css.edit = edit_config;
+
+ /*
+ * Use conf_scroll_screen to manage display/selection
+ * of contexts
+ */
+ return(context_select_screen(ps_global, &css, 0));
+ }
+
+ return(ps_global->context_list);
+}
+
+
+static struct headerentry headents_templ[]={
+ /* TRANSLATORS: these are the headings for setting up a collection of
+ folders, PATH is a filesystem path, VIEW is sort of a technical
+ term that can be used to restrict the View to fewer folders */
+ {"Nickname : ", N_("Nickname"), h_composer_cntxt_nick, 12, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"Server : ", N_("Server"), h_composer_cntxt_server, 12, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"Path : ", N_("Path"), h_composer_cntxt_path, 12, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"View : ", N_("View"), h_composer_cntxt_view, 12, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {NULL, NULL, NO_HELP, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}
+};
+#define AC_NICK 0
+#define AC_SERV 1
+#define AC_PATH 2
+#define AC_VIEW 3
+
+
+char *
+context_edit_screen(struct pine *ps, char *func, char *def_nick,
+ char *def_serv, char *def_path, char *def_view)
+{
+ int editor_result, i, j;
+ char nickpart[MAILTMPLEN], servpart[MAILTMPLEN], new_cntxt[MAILTMPLEN];
+ char pathpart[MAILTMPLEN], allbutnick[MAILTMPLEN];
+ char tmp[MAILTMPLEN], *nick, *serv, *path, *view,
+ *return_cntxt = NULL, *val, *p, new[MAILTMPLEN];
+ char nickpmt[100], servpmt[100], pathpmt[100], viewpmt[100];
+ int indent;
+ PICO pbf;
+ STORE_S *msgso;
+ NETMBX mb;
+
+ standard_picobuf_setup(&pbf);
+ pbf.pine_flags |= P_NOBODY;
+ pbf.exittest = exit_collection_add;
+ pbf.canceltest = (func && !strucmp(func, "EDIT")) ? cancel_collection_edit
+ : cancel_collection_add;
+ snprintf(tmp, sizeof(tmp), _("FOLDER COLLECTION %s"), func);
+ tmp[sizeof(tmp)-1] = '\0';
+ pbf.pine_anchor = set_titlebar(tmp, ps_global->mail_stream,
+ ps_global->context_current,
+ ps_global->cur_folder,ps_global->msgmap,
+ 0, FolderName, 0, 0, NULL);
+
+ /* An informational message */
+ if((msgso = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
+ pbf.msgtext = (void *) so_text(msgso);
+ so_puts(msgso,
+ _("\n Fill in the fields above to add a Folder Collection to your"));
+ so_puts(msgso,
+ _("\n COLLECTION LIST screen."));
+ so_puts(msgso,
+ _("\n Use the \"^G\" command to get help specific to each item, and"));
+ so_puts(msgso,
+ _("\n use \"^X\" when finished."));
+ }
+
+
+ pbf.headents = (struct headerentry *)fs_get((sizeof(headents_templ)
+ /sizeof(struct headerentry))
+ * sizeof(struct headerentry));
+ memset((void *) pbf.headents, 0,
+ (sizeof(headents_templ)/sizeof(struct headerentry))
+ * sizeof(struct headerentry));
+
+ for(i = 0; headents_templ[i].prompt; i++)
+ pbf.headents[i] = headents_templ[i];
+
+ indent = utf8_width(_("Nickname")) + 2;
+
+ nick = cpystr(def_nick ? def_nick : "");
+ pbf.headents[AC_NICK].realaddr = &nick;
+ pbf.headents[AC_NICK].maxlen = strlen(nick);
+ utf8_snprintf(nickpmt, sizeof(nickpmt), "%-*.*w: ", indent, indent, _("Nickname"));
+ pbf.headents[AC_NICK].prompt = nickpmt;
+ pbf.headents[AC_NICK].prwid = indent+2;
+
+ serv = cpystr(def_serv ? def_serv : "");
+ pbf.headents[AC_SERV].realaddr = &serv;
+ pbf.headents[AC_SERV].maxlen = strlen(serv);
+ utf8_snprintf(servpmt, sizeof(servpmt), "%-*.*w: ", indent, indent, _("Server"));
+ pbf.headents[AC_SERV].prompt = servpmt;
+ pbf.headents[AC_SERV].prwid = indent+2;
+
+ path = cpystr(def_path ? def_path : "");
+ pbf.headents[AC_PATH].realaddr = &path;
+ pbf.headents[AC_PATH].maxlen = strlen(path);
+ pbf.headents[AC_PATH].bldr_private = (void *) 0;
+ utf8_snprintf(pathpmt, sizeof(pathpmt), "%-*.*w: ", indent, indent, _("Path"));
+ pbf.headents[AC_PATH].prompt = pathpmt;
+ pbf.headents[AC_PATH].prwid = indent+2;
+
+ view = cpystr(def_view ? def_view : "");
+ pbf.headents[AC_VIEW].realaddr = &view;
+ pbf.headents[AC_VIEW].maxlen = strlen(view);
+ utf8_snprintf(viewpmt, sizeof(viewpmt), "%-*.*w: ", indent, indent, _("View"));
+ pbf.headents[AC_VIEW].prompt = viewpmt;
+ pbf.headents[AC_VIEW].prwid = indent+2;
+
+ /*
+ * If this is new context, setup to query IMAP server
+ * for location of personal namespace.
+ */
+ if(!(def_nick || def_serv || def_path || def_view)){
+ pbf.headents[AC_SERV].builder = build_namespace;
+ pbf.headents[AC_SERV].affected_entry = &pbf.headents[AC_PATH];
+ pbf.headents[AC_SERV].bldr_private = (void *) 0;
+ }
+
+ /* pass to pico and let user change them */
+ editor_result = pico(&pbf);
+
+ if(editor_result & COMP_GOTHUP){
+ hup_signal();
+ }
+ else{
+ fix_windsize(ps_global);
+ init_signals();
+ }
+
+ if(editor_result & COMP_CANCEL){
+ cmd_cancelled(func);
+ }
+ else if(editor_result & COMP_EXIT){
+ servpart[0] = pathpart[0] = new_cntxt[0] = allbutnick[0] = '\0';
+ if(serv && *serv){
+ if(serv[0] == '{' && serv[strlen(serv)-1] == '}'){
+ strncpy(servpart, serv, sizeof(servpart)-1);
+ servpart[sizeof(servpart)-1] = '\0';
+ }
+ else
+ snprintf(servpart, sizeof(servpart), "{%s}", serv);
+
+ if(mail_valid_net_parse(servpart, &mb)){
+ if(!struncmp(mb.service, "nntp", 4)
+ && (!path || strncmp(path, "#news.", 6)))
+ strncat(servpart, "#news.", sizeof(servpart)-1-strlen(servpart));
+ }
+ else
+ panic("Unexpected invalid server");
+ }
+ else
+ servpart[0] = '\0';
+
+ servpart[sizeof(servpart)-1] = '\0';
+
+ new_cntxt[0] = '\0';
+ if(nick && *nick){
+ val = quote_if_needed(nick);
+ if(val){
+ strncpy(new_cntxt, val, sizeof(new_cntxt)-2);
+ new_cntxt[sizeof(new_cntxt)-2] = '\0';
+ if(val != nick)
+ fs_give((void **)&val);
+
+ strncat(new_cntxt, " ", sizeof(new_cntxt)-strlen(new_cntxt)-1);
+ new_cntxt[sizeof(new_cntxt)-1] = '\0';
+ }
+ }
+
+ p = allbutnick;
+ sstrncpy(&p, servpart, sizeof(allbutnick)-1-(p-allbutnick));
+ allbutnick[sizeof(allbutnick)-1] = '\0';
+
+ if(path){
+ val = quote_brackets_if_needed(path);
+ if(val){
+ strncpy(pathpart, val, sizeof(pathpart)-1);
+ pathpart[sizeof(pathpart)-1] = '\0';
+ if(val != path)
+ fs_give((void **)&val);
+ }
+
+ if(pbf.headents[AC_PATH].bldr_private != (void *) 0){
+ strncat(pathpart, (char *) pbf.headents[AC_PATH].bldr_private,
+ sizeof(pathpart)-strlen(pathpart)-1);
+ pathpart[sizeof(pathpart)-1] = '\0';
+ }
+ }
+
+ sstrncpy(&p, pathpart, sizeof(allbutnick)-1-(p-allbutnick));
+ allbutnick[sizeof(allbutnick)-1] = '\0';
+
+ if(view[0] != '[' && sizeof(allbutnick)-1-(p-allbutnick) > 0){
+ *p++ = '[';
+ *p = '\0';
+ }
+
+ sstrncpy(&p, view, sizeof(allbutnick)-1-(p-allbutnick));
+ allbutnick[sizeof(allbutnick)-1] = '\0';
+ if((j=strlen(view)) < 2 || (view[j-1] != ']' &&
+ sizeof(allbutnick)-1-(p-allbutnick) > 0)){
+ *p++ = ']';
+ *p = '\0';
+ }
+
+ val = quote_if_needed(allbutnick);
+ if(val){
+ strncat(new_cntxt, val, sizeof(new_cntxt)-1-strlen(new_cntxt));
+ new_cntxt[sizeof(new_cntxt)-1] = '\0';
+
+ if(val != allbutnick)
+ fs_give((void **)&val);
+ }
+
+ return_cntxt = cpystr(new_cntxt);
+ }
+
+ for(i = 0; headents_templ[i].prompt; i++)
+ fs_give((void **) pbf.headents[i].realaddr);
+
+ if(pbf.headents[AC_PATH].bldr_private != (void *) 0)
+ fs_give(&pbf.headents[AC_PATH].bldr_private);
+
+ fs_give((void **) &pbf.headents);
+
+ standard_picobuf_teardown(&pbf);
+
+ if(msgso)
+ so_give(&msgso);
+
+ return(return_cntxt);
+}
+
+
+/*
+ * Doubles up '[' and ']' characters and returns either
+ * an allocated string or a pointer to the source string
+ * if no quoting is needed.
+ */
+char *
+quote_brackets_if_needed(char *src)
+{
+ char *step1 = NULL, *step2 = NULL, *ret;
+
+ ret = src;
+
+ if((strpbrk(src, "[]") != NULL)
+ && ((step1 = add_escapes(src, "[", '[', "", "")) != NULL)
+ && ((step2 = add_escapes(step1, "]", ']', "", ""))))
+ ret = step2;
+
+ if(step1)
+ fs_give((void **) &step1);
+
+ return(ret);
+}
+
+
+/*
+ * Call back for pico to prompt the user for exit confirmation
+ *
+ * Returns: either NULL if the user accepts exit, or string containing
+ * reason why the user declined.
+ */
+int
+exit_collection_add(struct headerentry *he, void (*redraw_pico)(void), int allow_flowed,
+ char **result)
+{
+ char prompt[256], tmp[MAILTMPLEN], tmpnodel[MAILTMPLEN], *server, *path,
+ delim = '\0', *rstr = NULL, *p;
+ int exists = 0, i;
+ void (*redraw)(void) = ps_global->redrawer;
+ NETMBX mb;
+
+ ps_global->redrawer = redraw_pico;
+ fix_windsize(ps_global);
+
+ server = *he[AC_SERV].realaddr;
+ removing_trailing_white_space(server);
+ removing_leading_white_space(server);
+
+ path = *he[AC_PATH].realaddr;
+
+ if(*server){
+ /* No brackets? */
+ if(server[0] == '{' && server[strlen(server)-1] == '}'){
+ strncpy(tmp, server, sizeof(tmp)-1);
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+ else /* add them */
+ snprintf(tmp, sizeof(tmp), "{%.*s}", sizeof(tmp)-3, server);
+
+ if(mail_valid_net_parse(tmp, &mb)){ /* news? verify namespace */
+ if(!struncmp(mb.service, "nntp", 4) && strncmp(path, "#news.", 6))
+ strncat(tmp, "#news.", sizeof(tmp)-1-strlen(tmp));
+ }
+ else
+ rstr = "Invalid Server entry";
+ }
+ else
+ tmp[0] = '\0';
+
+ tmp[sizeof(tmp)-1] = '\0';
+
+ if(!rstr){
+ /*
+ * Delimiter acquisition below serves dual purposes. One's to
+ * get the delimiter so we can make sure it's hanging off the end
+ * of the path so we can test for the directory existence. The
+ * second's to make sure the server (and any requested service)
+ * name we were given exists. It should be handled by the folder
+ * existence test futher below, but it doesn't work with news...
+ *
+ * Update. Now we are stripping the delimiter in the tmpnodel version
+ * so that we can pass that to folder_exists. Cyrus does not answer
+ * that the folder exists if we leave the trailing delimiter.
+ * Hubert 2004-12-17
+ */
+ strncat(tmp, path, sizeof(tmp)-1-strlen(tmp));
+ tmp[sizeof(tmp)-1] = '\0';
+ strncpy(tmpnodel, tmp, sizeof(tmpnodel)-1);
+ tmpnodel[sizeof(tmpnodel)-1] = '\0';
+
+ if(he[AC_PATH].bldr_private != (void *) 0)
+ fs_give(&he[AC_PATH].bldr_private);
+
+ ps_global->mm_log_error = 0;
+ if((delim = folder_delimiter(tmp)) != '\0'){
+ if(*path){
+ if(tmp[(i = strlen(tmp)) - 1] == delim)
+ tmpnodel[i-1] = '\0';
+ else{
+ tmp[i] = delim;
+ tmp[i+1] = '\0';
+ he[AC_PATH].bldr_private = (void *) cpystr(&tmp[i]);
+ }
+ }
+ }
+ else if(ps_global->mm_log_error && ps_global->last_error)
+ /* We used to bail, but this was changed with 4.10
+ * as some users wanted to add clctn before the server
+ * was actually around and built.
+ */
+ flush_status_messages(0); /* mail_create gripes */
+ else
+ dprint((1, "exit_col_test: No Server Hierarchy!\n"));
+ }
+
+ if(!rstr){
+ if(!*tmp
+ || !delim
+ || ((*(p = tmp) == '#'
+ || (*tmp == '{' && (p = strchr(tmp, '}')) && *++p))
+ && !struncmp(p, "#news.", 6))
+ || (*tmp == '{' && (p = strchr(tmp, '}')) && !*++p)){
+ exists = 1;
+ }
+ else if((i = folder_exists(NULL, tmpnodel)) & FEX_ERROR){
+ if(!(rstr = ps_global->last_error))
+ rstr = _("Problem testing for directory existence");
+ }
+ else
+ exists = (i & FEX_ISDIR);
+
+ if(exists)
+ snprintf(prompt, sizeof(prompt), _("Exit and save changes"));
+ else
+ snprintf(prompt, sizeof(prompt), _("Exit, saving changes and creating Path"));
+
+ if(want_to(prompt, 'y', 0, NO_HELP, WT_NORM) == 'y'){
+ if(!exists && !pine_mail_create(NULL, tmp)){
+ flush_status_messages(1); /* mail_create gripes */
+ if(!(rstr = ps_global->last_error))
+ rstr = "";
+ }
+ }
+ else
+ rstr = _("Use ^C to abandon changes you've made");
+ }
+
+ if(result)
+ *result = rstr;
+
+ ps_global->redrawer = redraw;
+ return((rstr == NULL) ? 0 : 1);
+}
+
+
+char *
+cancel_collection_add(void (*redraw_pico)(void))
+{
+ return(cancel_collection_editing(_("Add"), redraw_pico));
+}
+
+
+char *
+cancel_collection_edit(void (*redraw_pico)(void))
+{
+ return(cancel_collection_editing(_("Edit"), redraw_pico));
+}
+
+
+char *
+cancel_collection_editing(char *func, void (*redraw_pico)(void))
+{
+ char *rstr = NULL;
+ void (*redraw)(void) = ps_global->redrawer;
+ static char rbuf[20];
+ char prompt[256];
+#define CCA_PROMPT \
+ _("Cancel Add (answering \"Yes\" will abandon any changes made) ")
+
+ snprintf(prompt, sizeof(prompt), _("Cancel %s (answering \"Yes\" will abandon any changes made) "), func ? func : "Add");
+ snprintf(rbuf, sizeof(rbuf), _("%s Cancelled) "), func ? func : "Add");
+
+ ps_global->redrawer = redraw_pico;
+ fix_windsize(ps_global);
+
+ switch(want_to(prompt, 'y', 'x', NO_HELP, WT_NORM)){
+ case 'y':
+ rstr = rbuf;
+ break;
+
+ case 'n':
+ case 'x':
+ break;
+ }
+
+ ps_global->redrawer = redraw;
+ return(rstr);
+}
+
+
+int
+build_namespace(char *server, char **server_too, char **error, BUILDER_ARG *barg, int *mangled)
+{
+ char *p, *name;
+ int we_cancel = 0;
+ MAILSTREAM *stream;
+ NAMESPACE ***namespace;
+ size_t len;
+
+ dprint((5, "- build_namespace - (%s)\n",
+ server ? server : "nul"));
+
+ if(*barg->me){ /* only call this once! */
+ if(server_too)
+ *server_too = cpystr(server ? server : "");
+
+ return(0);
+ }
+ else
+ *barg->me = (void *) 1;
+
+ if((p = server) != NULL) /* empty string? */
+ while(*p && isspace((unsigned char) *p))
+ p++;
+
+ if(p && *p){
+ if(server_too)
+ *server_too = cpystr(p);
+
+ len = strlen(p) + 2;
+ name = (char *) fs_get((len + 1) * sizeof(char));
+ snprintf(name, len+1, "{%s}", p);
+ }
+ else{
+ if(server_too)
+ *server_too = cpystr("");
+
+ return(0);
+ }
+
+ *mangled |= BUILDER_SCREEN_MANGLED;
+ fix_windsize(ps_global);
+ init_sigwinch();
+ clear_cursor_pos();
+
+ we_cancel = busy_cue(_("Fetching default directory"), NULL, 1);
+
+ if((stream = pine_mail_open(NULL, name,
+ OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE,
+ NULL)) != NULL){
+ if((namespace = mail_parameters(stream, GET_NAMESPACE, NULL))
+ && *namespace && (*namespace)[0]
+ && (*namespace)[0]->name && (*namespace)[0]->name[0]){
+ if(barg->tptr)
+ fs_give((void **)&barg->tptr);
+
+ barg->tptr = cpystr((*namespace)[0]->name);
+ }
+
+ pine_mail_close(stream);
+ }
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ fs_give((void **) &name);
+
+ return(1);
+}
+
+
+int
+fl_val_gen (FOLDER_S *f, FSTATE_S *fs)
+{
+ return(f && FLDR_NAME(f));
+}
+
+
+int
+fl_val_writable (FOLDER_S *f, FSTATE_S *fs)
+{
+ return(1);
+}
+
+
+int
+fl_val_subscribe (FOLDER_S *f, FSTATE_S *fs)
+{
+ if(f->subscribed){
+ q_status_message1(SM_ORDER, 0, 4, _("Already subscribed to \"%s\""),
+ FLDR_NAME(f));
+ return(0);
+ }
+
+ return(1);
+}
+
+
+/*----------------------------------------------------------------------
+ Business end of displaying and operating on a collection of folders
+
+ Args: ps -- The pine_state data structure
+ fs -- Folder screen state structure
+
+ Result: A string list containing folders selected,
+ NULL on Cancel, Error or other problem
+
+ ----*/
+STRLIST_S *
+folder_lister(struct pine *ps, FSTATE_S *fs)
+{
+ int fltrv, we_cancel = 0;
+ int first_time_through = 1;
+ HANDLE_S *handles = NULL;
+ STORE_S *screen_text = NULL;
+ FPROC_S folder_proc_data;
+ gf_io_t pc;
+
+ dprint((1, "\n\n ---- FOLDER LISTER ----\n"));
+
+ memset(&folder_proc_data, 0, sizeof(FPROC_S));
+ folder_proc_data.fs = fs;
+
+ while(!folder_proc_data.done){
+ if((screen_text = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
+ gf_set_so_writec(&pc, screen_text);
+ }
+ else{
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ "Formatting Error: Can't create space for list");
+ return(NULL);
+ }
+
+ we_cancel = busy_cue(_("Fetching folder data"), NULL, 1);
+ fltrv = folder_list_text(ps, &folder_proc_data, pc, &handles,
+ ps->ttyo->screen_cols);
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ if(fltrv){
+
+ SCROLL_S sargs;
+ struct key_menu km;
+ struct key keys[36];
+
+ memset(&sargs, 0, sizeof(SCROLL_S));
+ sargs.text.text = so_text(screen_text);
+ sargs.text.src = CharStar;
+ sargs.text.desc = "folder list";
+ if((sargs.text.handles = folder_list_handle(fs, handles)) != NULL)
+ sargs.start.on = Handle;
+
+ sargs.bar.title = fs->f.title.bar;
+ sargs.bar.style = fs->f.title.style;
+
+ sargs.proc.tool = folder_processor;
+ sargs.proc.data.p = (void *) &folder_proc_data;
+
+ sargs.quell_first_view = first_time_through ? 0 : 1;
+ first_time_through = 0;
+
+ sargs.resize_exit = 1;
+ sargs.vert_handle = 1;
+ sargs.srch_handle = 1;
+
+ sargs.help.text = fs->f.help.text;
+ sargs.help.title = fs->f.help.title;
+
+ sargs.keys.menu = &km;
+ km = *fs->km;
+ km.keys = keys;
+ memcpy(&keys[0], fs->km->keys,
+ (km.how_many * 12) * sizeof(struct key));
+ setbitmap(sargs.keys.bitmap);
+
+ if(fs->km == &folder_km){
+ sargs.keys.each_cmd = folder_lister_km_manager;
+#ifdef _WINDOWS
+ sargs.mouse.popup = folder_list_popup;
+#endif
+ }
+ else{
+#ifdef _WINDOWS
+ sargs.mouse.popup = folder_list_select_popup;
+#endif
+ if(fs->km == &folder_sel_km || fs->km == &folder_sela_km)
+ sargs.keys.each_cmd = folder_lister_km_sel_manager;
+ else if(fs->km == &folder_sub_km)
+ sargs.keys.each_cmd = folder_lister_km_sub_manager;
+ }
+
+ sargs.mouse.clickclick = folder_lister_clickclick;
+
+ switch(scrolltool(&sargs)){
+ case MC_MAIN : /* just leave */
+ folder_proc_data.done = 1;
+ break;
+
+ case MC_RESIZE : /* loop around rebuilding screen */
+ if(sargs.text.handles){
+ FOLDER_S *fp;
+
+ fp = folder_entry(sargs.text.handles->h.f.index,
+ FOLDERS(sargs.text.handles->h.f.context));
+ if(fp && strlen(FLDR_NAME(fp)) < MAXFOLDER -1){
+ strncpy(fs->first_folder, FLDR_NAME(fp), MAXFOLDER);
+ fs->first_folder[MAXFOLDER-1] = '\0';
+ }
+
+ fs->context = sargs.text.handles->h.f.context;
+ }
+
+ break;
+
+ /*--------- EXIT menu -----------*/
+ case MC_EXIT :
+ case MC_EXITQUERY :
+ fs->list_cntxt = NULL;
+ folder_proc_data.done = folder_proc_data.all_done = 1;
+ break;
+
+ default :
+ break;
+ }
+
+ ps->noticed_change_in_unseen = 0;
+
+
+ if(F_ON(F_BLANK_KEYMENU,ps))
+ FOOTER_ROWS(ps) = 1;
+
+ gf_clear_so_writec(screen_text);
+ so_give(&screen_text);
+ free_handles(&handles);
+ }
+ else
+ folder_proc_data.done = 1;
+ }
+
+ reset_context_folders(fs->context);
+
+ if(folder_proc_data.all_done)
+ fs->context = NULL;
+
+ if(fs->cache_streamp && *fs->cache_streamp){
+ int i;
+
+ /*
+ * check stream pool to see if currently cached
+ * stream went away
+ */
+ for(i = 0; i < ps->s_pool.nstream; i++)
+ if(ps->s_pool.streams[i] == *fs->cache_streamp)
+ break;
+ if(i == ps->s_pool.nstream)
+ *fs->cache_streamp = NULL;
+ }
+
+ return(folder_proc_data.rv);
+}
+
+
+/*
+ * folder_list_text - format collection's contents for display
+ */
+int
+folder_list_text(struct pine *ps, FPROC_S *fp, gf_io_t pc, HANDLE_S **handlesp, int cols)
+{
+ int rv = 1, i, j, ftotal, fcount, slot_width, slot_rows,
+ slot_cols, index, findex, width, shown, selected;
+ CONTEXT_S *c_list;
+ char lbuf[6*MAX_SCREEN_COLS+1];
+
+ /* disarm this trigger that gets us out of scrolltool */
+ ps->noticed_change_in_unseen = 0;
+
+ if(handlesp)
+ init_handles(handlesp);
+
+ c_list = fp->fs->context;
+ if(fp->fs->combined_view
+ && (F_ON(F_CMBND_SUBDIR_DISP, ps_global) || !c_list->dir->prev))
+ while(c_list->prev) /* rewind to start */
+ c_list = c_list->prev;
+
+ do{
+ ps->user_says_cancel = 0;
+
+ /* If we're displaying folders, fetch the list */
+ if((shown = (c_list == fp->fs->context
+ || (c_list->dir->status & CNTXT_NOFIND) == 0
+ || F_ON(F_EXPANDED_FOLDERS, ps_global))) != 0){
+ /*
+ * if select is allowed, flag context so any that are
+ * are remembered even after the list is destroyed
+ */
+ if(fp->fs->agg_ops)
+ c_list->use |= CNTXT_PRESRV;
+
+ /* Make sure folder list filled in */
+ refresh_folder_list(c_list, fp->fs->no_dirs, FALSE, fp->fs->cache_streamp);
+ }
+
+ /* Insert any introductory text here */
+ if(c_list->next
+ || c_list->prev
+ || c_list->dir->prev
+ || fp->fs->force_intro){
+
+ /* Leading horizontal line? */
+ if(fp->fs->combined_view
+ && (F_ON(F_CMBND_SUBDIR_DISP,ps_global)
+ || !c_list->dir->prev)){
+ if(c_list->prev)
+ gf_puts("\n", pc); /* blank line */
+
+ gf_puts(repeat_char(cols, '-'), pc);
+ gf_puts("\n", pc);
+ }
+
+ /* nickname or description */
+ if(F_ON(F_CMBND_FOLDER_DISP, ps_global)
+ && (!c_list->dir->prev
+ || F_ON(F_CMBND_SUBDIR_DISP, ps_global))){
+ char buf[6*MAX_SCREEN_COLS + 1];
+
+ if(cols < 40){
+ snprintf(buf, sizeof(buf), "%.*s", cols,
+ strsquish(tmp_20k_buf, SIZEOF_20KBUF,
+ (c_list->nickname)
+ ? c_list->nickname
+ : (c_list->label ? c_list->label : ""),
+ cols));
+ gf_puts(folder_list_center_space(buf, cols), pc);
+ }
+ else{
+ int wid;
+
+ snprintf(buf, sizeof(buf), "%s-Collection <",
+ NEWS_TEST(c_list) ? "News" : "Folder");
+ wid = utf8_width(buf)+1;
+ wid = MAX(0, cols-wid);
+ snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%.*s>", wid,
+ strsquish(tmp_20k_buf, SIZEOF_20KBUF,
+ (c_list->nickname)
+ ? c_list->nickname
+ : (c_list->label ? c_list->label : ""),
+ wid));
+ }
+
+ gf_puts(buf, pc);
+ gf_puts("\n", pc);
+ }
+ else if(c_list->label){
+ if(utf8_width(c_list->label) > ps_global->ttyo->screen_cols)
+ utf8_pad_to_width(lbuf, c_list->label, sizeof(lbuf), ps_global->ttyo->screen_cols, 1);
+ else
+ strncpy(lbuf, c_list->label, sizeof(lbuf));
+
+ lbuf[sizeof(lbuf)-1] = '\0';
+
+ gf_puts(folder_list_center_space(lbuf, cols), pc);
+ gf_puts(lbuf, pc);
+ gf_puts("\n", pc);
+ }
+
+ if(c_list->comment){
+ if(utf8_width(c_list->comment) > ps_global->ttyo->screen_cols)
+ utf8_pad_to_width(lbuf, c_list->comment, sizeof(lbuf), ps_global->ttyo->screen_cols, 1);
+ else
+ strncpy(lbuf, c_list->comment, sizeof(lbuf));
+
+ lbuf[sizeof(lbuf)-1] = '\0';
+
+ gf_puts(folder_list_center_space(lbuf, cols), pc);
+ gf_puts(lbuf, pc);
+ gf_puts("\n", pc);
+ }
+
+ if(c_list->dir->desc){
+ char buf[6*MAX_SCREEN_COLS + 1];
+
+ strncpy(buf, strsquish(tmp_20k_buf,SIZEOF_20KBUF,c_list->dir->desc,cols),
+ sizeof(buf)-1);
+ buf[sizeof(buf)-1] = '\0';
+ gf_puts(folder_list_center_space(buf, cols), pc);
+ gf_puts(buf, pc);
+ gf_puts("\n", pc);
+ }
+
+ if(c_list->use & CNTXT_ZOOM){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "[ ZOOMED on %d (of %d) %ss ]",
+ selected_folders(c_list),
+ folder_total(FOLDERS(c_list)),
+ (c_list->use & CNTXT_NEWS) ? "Newsgroup" : "Folder");
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+
+ if(utf8_width(tmp_20k_buf) > ps_global->ttyo->screen_cols)
+ utf8_pad_to_width(lbuf, tmp_20k_buf, sizeof(lbuf), ps_global->ttyo->screen_cols, 1);
+ else
+ strncpy(lbuf, tmp_20k_buf, sizeof(lbuf));
+
+ lbuf[sizeof(lbuf)-1] = '\0';
+
+ gf_puts(folder_list_center_space(lbuf, cols), pc);
+ gf_puts(lbuf, pc);
+ gf_puts("\n", pc);
+ }
+
+ gf_puts(repeat_char(cols, '-'), pc);
+ gf_puts("\n\n", pc);
+ }
+
+ if(shown){
+ /* Run thru list formatting as necessary */
+ if((ftotal = folder_total(FOLDERS(c_list))) != 0){
+ /* If previously selected, mark members of new list */
+ selected = selected_folders(c_list);
+
+ /* How many screen cells per cell for each folder name? */
+ slot_width = 1;
+ for(fcount = i = 0; i < ftotal; i++){
+ FOLDER_S *f = folder_entry(i, FOLDERS(c_list));
+ unsigned char *fname;
+
+ ps->user_says_cancel = 0;
+
+ if((c_list->use & CNTXT_ZOOM) && !f->selected)
+ continue;
+
+ fcount++;
+
+ fname = folder_name_decoded((unsigned char *)FLDR_NAME(f));
+
+ width = utf8_width(fname ? (char *)fname : FLDR_NAME(f));
+
+ if(fname)
+ fs_give((void **)&fname);
+
+ if(f->isdir)
+ width += (f->isfolder) ? 3 : 1;
+
+ if(NEWS_TEST(c_list) && (c_list->use & CNTXT_FINDALL))
+ /* assume listmode so we don't have to reformat */
+ /* if listmode is actually entered */
+ width += 4;
+
+ if(selected){
+ if(F_OFF(F_SELECTED_SHOWN_BOLD, ps_global))
+ width += 4; /* " X " */
+ }
+ else if(c_list == fp->fs->list_cntxt)
+ width += 4; /* "[X] " */
+
+ if(fp->fs->include_unseen_cnt
+ && c_list->use & CNTXT_INCMNG
+ && F_ON(F_ENABLE_INCOMING_CHECKING, ps_global)
+ && ps_global->VAR_INCOMING_FOLDERS
+ && ps_global->VAR_INCOMING_FOLDERS[0]){
+
+ update_folder_unseen(f, c_list, UFU_ANNOUNCE, NULL);
+ if(F_ON(F_INCOMING_CHECKING_RECENT, ps_global)
+ && f->unseen_valid
+ && (f->new > 0L
+ || F_ON(F_INCOMING_CHECKING_TOTAL, ps_global))){
+ width += (strlen(tose(f->new)) + 3);
+ if(F_ON(F_INCOMING_CHECKING_TOTAL, ps_global))
+ width += (strlen(tose(f->total)) + 1);
+ }
+ else if(F_OFF(F_INCOMING_CHECKING_RECENT, ps_global)
+ && f->unseen_valid
+ && (f->unseen > 0L
+ || F_ON(F_INCOMING_CHECKING_TOTAL, ps_global))){
+ width += (strlen(tose(f->unseen)) + 3);
+ if(F_ON(F_INCOMING_CHECKING_TOTAL, ps_global))
+ width += (strlen(tose(f->total)) + 1);
+ }
+ else if(!f->unseen_valid && f->last_unseen_update != LUU_NEVERCHK)
+ width += 4; /* " (?)" */
+ }
+
+ if(slot_width < width)
+ slot_width = width;
+ }
+
+ if(F_ON(F_SINGLE_FOLDER_LIST, ps_global)){
+ slot_cols = 1;
+ slot_rows = fcount;
+ }
+ else{
+ /* fit as many columns as possible */
+ slot_cols = 1;
+ while(((slot_cols+1) * slot_width) + slot_cols <= cols)
+ slot_cols++;
+
+ switch(slot_cols){
+ case 0 :
+ slot_cols = 1;
+ /* fall through */
+
+ case 1 :
+ slot_rows = fcount;
+ break;
+
+ default :
+ /*
+ * Make the slot_width as large as possible.
+ * Slot_width is the width of the column, not counting
+ * the space between columns.
+ */
+ while((slot_cols * (slot_width+1)) + slot_cols-1 <= cols)
+ slot_width++;
+
+ slot_rows = (fcount / slot_cols)
+ + ((fcount % slot_cols) ? 1 : 0);
+ break;
+ }
+ }
+
+ for(i = index = 0; i < slot_rows; i++){
+ if(i)
+ gf_puts("\n", pc);
+
+ for(j = width = 0; j < slot_cols; j++, index++){
+ if(width){
+ gf_puts(repeat_char(slot_width + 1 - width, ' '), pc);
+ width = 0;
+ }
+
+ if(F_ON(F_VERTICAL_FOLDER_LIST, ps_global))
+ index = i + (j * slot_rows);
+
+ findex = index;
+
+ if(c_list->use & CNTXT_ZOOM)
+ findex = folder_list_ith(index, c_list);
+
+ if(findex < ftotal){
+ int flags = (handlesp) ? FLW_LUNK : FLW_NONE;
+
+ if(c_list == fp->fs->list_cntxt)
+ flags |= FLW_LIST;
+ else if(selected)
+ flags |= FLW_SLCT;
+ else if(F_ON(F_SINGLE_FOLDER_LIST, ps_global)
+ || ((c_list->use & CNTXT_FINDALL)
+ && NEWS_TEST(c_list)))
+ gf_puts(" ", pc);
+
+ if(fp->fs->include_unseen_cnt
+ && c_list->use & CNTXT_INCMNG
+ && F_ON(F_ENABLE_INCOMING_CHECKING, ps_global)
+ && ps_global->VAR_INCOMING_FOLDERS
+ && ps_global->VAR_INCOMING_FOLDERS[0])
+ flags |= FLW_UNSEEN;
+
+ width = folder_list_write(pc, handlesp, c_list,
+ findex, NULL, flags);
+ }
+ }
+ }
+ }
+ else if(fp->fs->combined_view
+ && (F_ON(F_CMBND_SUBDIR_DISP, ps_global)
+ || !c_list->dir->prev)){
+ char *emptiness = N_("[No Folders in Collection]");
+
+ if(utf8_width(_(emptiness)) > ps_global->ttyo->screen_cols)
+ utf8_pad_to_width(lbuf, _(emptiness), sizeof(lbuf), ps_global->ttyo->screen_cols, 1);
+ else
+ strncpy(lbuf, _(emptiness), sizeof(lbuf));
+
+ lbuf[sizeof(lbuf)-1] = '\0';
+
+ gf_puts(folder_list_center_space(lbuf, cols), pc);
+ (void) folder_list_write(pc, handlesp, c_list, -1, lbuf,
+ (handlesp) ? FLW_LUNK : FLW_NONE);
+ }
+ }
+ else if(fp->fs->combined_view
+ && (F_ON(F_CMBND_SUBDIR_DISP, ps_global)
+ || !c_list->dir->prev)){
+ char *unexpanded = N_("[Select Here to See Expanded List]");
+
+ if(utf8_width(_(unexpanded)) > ps_global->ttyo->screen_cols)
+ utf8_pad_to_width(lbuf, _(unexpanded), sizeof(lbuf), ps_global->ttyo->screen_cols, 1);
+ else
+ strncpy(lbuf, _(unexpanded), sizeof(lbuf));
+
+ lbuf[sizeof(lbuf)-1] = '\0';
+
+ gf_puts(folder_list_center_space(lbuf, cols), pc);
+ (void) folder_list_write(pc, handlesp, c_list, -1, lbuf,
+ (handlesp) ? FLW_LUNK : FLW_NONE);
+ }
+
+ gf_puts("\n", pc); /* blank line */
+
+ }
+ while(fp->fs->combined_view
+ && (F_ON(F_CMBND_SUBDIR_DISP, ps_global) || !c_list->dir->prev)
+ && (c_list = c_list->next));
+
+ return(rv);
+}
+
+
+int
+folder_list_write(gf_io_t pc, HANDLE_S **handlesp, CONTEXT_S *ctxt, int fnum, char *alt_name, int flags)
+{
+ char buf[256];
+ int width = 0, lprefix = 0, lmiddle = 0, lsuffix = 0;
+ FOLDER_S *fp;
+ HANDLE_S *h1 = NULL, *h2 = NULL;
+
+ if(flags & FLW_LUNK){
+ h1 = new_handle(handlesp);
+ h1->type = Folder;
+ h1->h.f.index = fnum;
+ h1->h.f.context = ctxt;
+ h1->force_display = 1;
+
+ snprintf(buf, sizeof(buf), "%d", h1->key);
+ buf[sizeof(buf)-1] = '\0';
+ }
+
+ fp = (fnum < 0) ? NULL : folder_entry(fnum, FOLDERS(ctxt));
+
+ if(flags & FLW_LUNK && h1 && fp && fp->isdir && fp->isfolder){
+ h2 = new_handle(handlesp);
+ h2->type = Folder;
+ h2->h.f.index = fnum;
+ h2->h.f.context = ctxt;
+ h2->force_display = 1;
+
+ h1->is_dual_do_open = 1;
+ }
+
+ if(h1){
+ /* color unseen? */
+ if(color_monitored_unseen(fp, flags)){
+ h1->color_unseen = 1;
+ if(h2)
+ h2->color_unseen = 1;
+ }
+ }
+
+ /* embed handle pointer */
+ if(((h1 && h1->color_unseen) ?
+ gf_puts(color_embed(ps_global->VAR_INCUNSEEN_FORE_COLOR,
+ ps_global->VAR_INCUNSEEN_BACK_COLOR), pc) : 1)
+ && (h1 ? ((*pc)(TAG_EMBED) && (*pc)(TAG_HANDLE)
+ && (*pc)(strlen(buf)) && gf_puts(buf, pc)) : 1)
+ && (fp ? ((lprefix = folder_list_write_prefix(fp, flags, pc)) >= 0
+ && (lmiddle = folder_list_write_middle(fp, ctxt, pc, h2)) >= 0
+ && ((lsuffix = folder_list_write_suffix(fp, flags, pc)) >= 0))
+ : (alt_name ? gf_puts(alt_name, pc) : 0))
+ && (h1 ? ((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)
+ && (*pc)(TAG_EMBED) && (*pc)(TAG_INVOFF)) : 1)
+ && ((h1 && h1->color_unseen) ?
+ gf_puts(color_embed(ps_global->VAR_NORM_FORE_COLOR,
+ ps_global->VAR_NORM_BACK_COLOR), pc) : 1)){
+ if(fp)
+ width = lprefix + lmiddle + lsuffix;
+ else if(alt_name)
+ width = utf8_width(alt_name);
+ }
+
+ return(width);
+}
+
+
+int
+folder_list_write_prefix(FOLDER_S *f, int flags, gf_io_t pc)
+{
+ int rv = 0;
+
+ if(flags & FLW_SLCT){
+ if(F_OFF(F_SELECTED_SHOWN_BOLD, ps_global) || !(flags & FLW_LUNK)){
+ rv = 4;
+ if(f->selected){
+ gf_puts(" X ", pc);
+ }
+ else{
+ gf_puts(" ", pc);
+ }
+ }
+ else
+ rv = ((*pc)(TAG_EMBED)
+ && (*pc)((f->selected) ? TAG_BOLDON : TAG_BOLDOFF)) ? 0 : -1;
+ }
+ else if(flags & FLW_LIST){
+ rv = 4;
+ /* screen width of "SUB " is 4 */
+ gf_puts(f->subscribed ? "SUB " : (f->selected ? "[X] " : "[ ] "), pc);
+ }
+
+ return(rv);
+}
+
+
+int
+folder_list_write_middle(FOLDER_S *fp, CONTEXT_S *ctxt, gf_io_t pc, HANDLE_S *h2)
+{
+ int rv = -1;
+ char buf[256];
+ unsigned char *fname;
+
+ if(h2){
+ snprintf(buf, sizeof(buf), "%d", h2->key);
+ buf[sizeof(buf)-1] = '\0';
+ }
+
+ if(!fp)
+ return(rv);
+
+ fname = folder_name_decoded((unsigned char *)FLDR_NAME(fp));
+
+ if(gf_puts(fname ? (char *)fname : FLDR_NAME(fp), pc)
+ && (h2 ? ((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF) /* tie off handle 1 */
+ && (*pc)(TAG_EMBED) && (*pc)(TAG_INVOFF)) : 1)
+ && (h2 ? ((*pc)(TAG_EMBED) && (*pc)(TAG_HANDLE) /* start handle 2 */
+ && (*pc)(strlen(buf)) && gf_puts(buf, pc)) : 1)
+ && ((fp->isdir && fp->isfolder) ? (*pc)('[') : 1)
+ && ((fp->isdir) ? (*pc)(ctxt->dir->delim) : 1)
+ && ((fp->isdir && fp->isfolder) ? (*pc)(']') : 1)){
+ rv = utf8_width(fname ? (char *)fname : FLDR_NAME(fp));
+ if(fp->isdir)
+ rv += (fp->isfolder) ? 3 : 1;
+ }
+
+ if(fname)
+ fs_give((void **)&fname);
+
+ return(rv);
+}
+
+
+int
+folder_list_write_suffix(FOLDER_S *f, int flags, gf_io_t pc)
+{
+ int rv = 0;
+
+ if(flags & FLW_UNSEEN){
+ char buf[100];
+
+ buf[0] = '\0';
+ if(F_ON(F_INCOMING_CHECKING_RECENT, ps_global)
+ && f->unseen_valid
+ && (f->new > 0L
+ || (F_ON(F_INCOMING_CHECKING_TOTAL, ps_global)
+ && f->total > 0L))){
+ snprintf(buf, sizeof(buf), " (%s%s%s)",
+ tose(f->new),
+ F_ON(F_INCOMING_CHECKING_TOTAL, ps_global) ? "/" : "",
+ F_ON(F_INCOMING_CHECKING_TOTAL, ps_global) ? tose(f->total) : "");
+ }
+ else if(F_OFF(F_INCOMING_CHECKING_RECENT, ps_global)
+ && f->unseen_valid
+ && (f->unseen > 0L
+ || (F_ON(F_INCOMING_CHECKING_TOTAL, ps_global)
+ && f->total > 0L))){
+ snprintf(buf, sizeof(buf), " (%s%s%s)",
+ tose(f->unseen),
+ F_ON(F_INCOMING_CHECKING_TOTAL, ps_global) ? "/" : "",
+ F_ON(F_INCOMING_CHECKING_TOTAL, ps_global) ? tose(f->total) : "");
+ }
+ else if(!f->unseen_valid && f->last_unseen_update != LUU_NEVERCHK){
+ snprintf(buf, sizeof(buf), " (?)");
+ }
+
+ rv = strlen(buf);
+ if(rv)
+ gf_puts(buf, pc);
+ }
+
+ return(rv);
+}
+
+
+int
+color_monitored_unseen(FOLDER_S *f, int flags)
+{
+ return((flags & FLW_UNSEEN) && f && f->unseen_valid
+ && ((F_ON(F_INCOMING_CHECKING_RECENT, ps_global) && f->new > 0L)
+ || (F_OFF(F_INCOMING_CHECKING_RECENT, ps_global) && f->unseen > 0L))
+ && pico_usingcolor()
+ && pico_is_good_color(ps_global->VAR_INCUNSEEN_FORE_COLOR)
+ && pico_is_good_color(ps_global->VAR_INCUNSEEN_BACK_COLOR)
+ && (colorcmp(ps_global->VAR_INCUNSEEN_FORE_COLOR,
+ ps_global->VAR_NORM_FORE_COLOR)
+ || colorcmp(ps_global->VAR_INCUNSEEN_BACK_COLOR,
+ ps_global->VAR_NORM_BACK_COLOR)));
+}
+
+
+int
+folder_list_ith(int n, CONTEXT_S *cntxt)
+{
+ int index, ftotal;
+ FOLDER_S *f;
+
+ for(index = 0, ftotal = folder_total(FOLDERS(cntxt));
+ index < ftotal
+ && (f = folder_entry(index, FOLDERS(cntxt)))
+ && !(f->selected && !n--);
+ index++)
+ ;
+
+ return(index);
+}
+
+
+char *
+folder_list_center_space(char *s, int width)
+{
+ int l;
+
+ return(((l = utf8_width(s)) < width) ? repeat_char((width - l)/2, ' ') : "");
+}
+
+
+/*
+ * folder_list_handle - return pointer in handle list
+ * corresponding to "start"
+ */
+HANDLE_S *
+folder_list_handle(FSTATE_S *fs, HANDLE_S *handles)
+{
+ char *p, *name = NULL;
+ HANDLE_S *h, *h_found = NULL;
+ FOLDER_S *fp;
+
+ if(handles && fs->context){
+ if(!(NEWS_TEST(fs->context) || (fs->context->use & CNTXT_INCMNG))
+ && fs->context->dir->delim)
+ for(p = strchr(fs->first_folder, fs->context->dir->delim);
+ p;
+ p = strchr(p, fs->context->dir->delim))
+ name = ++p;
+
+ for(h = handles; h; h = h->next)
+ if(h->h.f.context == fs->context){
+ if(!h_found) /* match at least given context */
+ h_found = h;
+
+ if(!fs->first_folder[0]
+ || ((fp = folder_entry(h->h.f.index, FOLDERS(h->h.f.context)))
+ && ((fs->first_dir && fp->isdir)
+ || (!fs->first_dir && fp->isfolder))
+ && ((fp->nickname && !strcmp(name ? name : fs->first_folder, fp->nickname))
+ || (fp->name && !strcmp(name ? name : fs->first_folder, fp->name))))){
+ h_found = h;
+ break;
+ }
+ }
+
+ fs->first_folder[0] = '\0';
+ }
+
+ return(h_found ? h_found : handles);
+}
+
+
+int
+folder_processor(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
+{
+ int rv = 0;
+ char tmp_output[MAILTMPLEN];
+
+ switch(cmd){
+ case MC_FINISH :
+ FPROC(sparms)->done = rv = 1;;
+ break;
+
+ /*---------- Select or enter a View ----------*/
+ case MC_CHOICE :
+ rv = folder_lister_choice(sparms);
+ break;
+
+ /*--------- Hidden "To Fldrs" command -----------*/
+ case MC_LISTMODE :
+ if(sparms->text.handles
+ && folder_total(FOLDERS(sparms->text.handles->h.f.context))){
+ if(!FPROC(sparms)->fs->list_cntxt){
+ FPROC(sparms)->fs->list_cntxt
+ = sparms->text.handles->h.f.context;
+ rv = scroll_add_listmode(sparms->text.handles->h.f.context,
+ folder_total(FOLDERS(sparms->text.handles->h.f.context)));
+ if(!rv){
+ /* need to set the subscribe key ourselves */
+ sparms->keys.menu->keys[SB_SUB_KEY].name = "S";
+ sparms->keys.menu->keys[SB_SUB_KEY].label = N_("Subscribe");
+ sparms->keys.menu->keys[SB_SUB_KEY].bind.cmd = MC_CHOICE;
+ sparms->keys.menu->keys[SB_SUB_KEY].bind.ch[0] = 's';
+ setbitn(SB_SUB_KEY, sparms->keys.bitmap);
+ ps_global->mangled_screen = 1;
+
+ sparms->keys.menu->keys[SB_EXIT_KEY].bind.cmd = MC_EXITQUERY;
+ }
+ q_status_message(SM_ORDER, 0, 1, LISTMODE_GRIPE);
+ }
+ else
+ q_status_message(SM_ORDER, 0, 4, _("Already in List Mode"));
+ }
+ else
+ q_status_message(SM_ORDER, 0, 4,
+ _("No Folders! Can't enter List Mode"));
+
+ break;
+
+
+ /*--------- Visit parent directory -----------*/
+ case MC_PARENT :
+ if(folder_lister_parent(FPROC(sparms)->fs,
+ (sparms->text.handles)
+ ? sparms->text.handles->h.f.context
+ : FPROC(sparms)->fs->context,
+ (sparms->text.handles)
+ ? sparms->text.handles->h.f.index : -1, 0))
+ rv = 1; /* leave scrolltool to rebuild screen */
+
+ break;
+
+
+ /*--------- Open the selected folder -----------*/
+ case MC_OPENFLDR :
+ if(sparms->text.handles
+ && folder_total(FOLDERS(sparms->text.handles->h.f.context)))
+ rv = folder_lister_finish(sparms, sparms->text.handles->h.f.context,
+ sparms->text.handles->h.f.index);
+ else
+ q_status_message(SM_ORDER, 0, 4,
+ _("No Folders! Nothing to View"));
+
+ break;
+
+
+ /*--------- Export the selected folder -----------*/
+ case MC_EXPORT :
+ folder_export(sparms);
+ break;
+
+
+ /*--------- Import the selected folder -----------*/
+ case MC_IMPORT :
+ {
+ CONTEXT_S *cntxt = (sparms->text.handles)
+ ? sparms->text.handles->h.f.context
+ : FPROC(sparms)->fs->context;
+ char new_file[2*MAXFOLDER+10];
+ int r;
+
+ new_file[0] = '\0';
+
+ r = folder_import(sparms, new_file, sizeof(new_file));
+
+ if(r && (cntxt->use & CNTXT_INCMNG || context_isambig(new_file))){
+ rv = 1; /* rebuild display! */
+ FPROC(sparms)->fs->context = cntxt;
+ if(strlen(new_file) < MAXFOLDER - 1){
+ strncpy(FPROC(sparms)->fs->first_folder, new_file, MAXFOLDER);
+ FPROC(sparms)->fs->first_folder[MAXFOLDER-1] = '\0';
+ }
+ }
+ else
+ ps_global->mangled_footer++;
+ }
+
+ break;
+
+
+ /*--------- Return to the Collections Screen -----------*/
+ case MC_COLLECTIONS :
+ FPROC(sparms)->done = rv = 1;
+ break;
+
+
+ /*--------- QUIT pine -----------*/
+ case MC_QUIT :
+ ps_global->next_screen = quit_screen;
+ FPROC(sparms)->done = rv = 1;
+ break;
+
+
+ /*--------- Compose -----------*/
+ case MC_COMPOSE :
+ ps_global->next_screen = compose_screen;
+ FPROC(sparms)->done = rv = 1;
+ break;
+
+
+ /*--------- Alt Compose -----------*/
+ case MC_ROLE :
+ ps_global->next_screen = alt_compose_screen;
+ FPROC(sparms)->done = rv = 1;
+ break;
+
+
+ /*--------- Message Index -----------*/
+ case MC_INDEX :
+ if(THREADING()
+ && sp_viewing_a_thread(ps_global->mail_stream)
+ && unview_thread(ps_global, ps_global->mail_stream, ps_global->msgmap)){
+ ps_global->next_screen = mail_index_screen;
+ ps_global->view_skipped_index = 0;
+ ps_global->mangled_screen = 1;
+ }
+
+ ps_global->next_screen = mail_index_screen;
+ FPROC(sparms)->done = rv = 1;
+ break;
+
+
+ /*----------------- Add a new folder name -----------*/
+ case MC_ADDFLDR :
+ {
+ CONTEXT_S *cntxt = (sparms->text.handles)
+ ? sparms->text.handles->h.f.context
+ : FPROC(sparms)->fs->context;
+ char new_file[2*MAXFOLDER+10];
+ int r;
+
+ if(NEWS_TEST(cntxt))
+ r = group_subscription(new_file, sizeof(new_file), cntxt);
+ else{
+ r = add_new_folder(cntxt, Main, V_INCOMING_FOLDERS, new_file,
+ sizeof(new_file),
+ FPROC(sparms)->fs->cache_streamp
+ ? *FPROC(sparms)->fs->cache_streamp : NULL,
+ NULL);
+ if(ps_global->prc && ps_global->prc->outstanding_pinerc_changes)
+ write_pinerc(ps_global, Main, WRP_NONE);
+ }
+
+ if(r && (cntxt->use & CNTXT_INCMNG || context_isambig(new_file))){
+ rv = 1; /* rebuild display! */
+ FPROC(sparms)->fs->context = cntxt;
+ if(strlen(new_file) < MAXFOLDER - 1){
+ strncpy(FPROC(sparms)->fs->first_folder, new_file, MAXFOLDER);
+ FPROC(sparms)->fs->first_folder[MAXFOLDER-1] = '\0';
+ }
+ }
+ else
+ ps_global->mangled_footer++;
+ }
+
+ break;
+
+
+ /*------ Type in new folder name, e.g., for save ----*/
+ case MC_ADD :
+ rv = folder_lister_addmanually(sparms);
+ break;
+
+
+ /*--------------- Rename folder ----------------*/
+ case MC_RENAMEFLDR :
+ if(sparms->text.handles
+ && folder_total(FOLDERS(sparms->text.handles->h.f.context))){
+ char new_file[MAXFOLDER+1];
+ int r;
+
+ r = rename_folder(sparms->text.handles->h.f.context,
+ sparms->text.handles->h.f.index, new_file,
+ sizeof(new_file),
+ FPROC(sparms)->fs->cache_streamp
+ ? *FPROC(sparms)->fs->cache_streamp : NULL);
+
+ if(r){
+ /* repaint, placing cursor on new folder! */
+ rv = 1;
+ if(context_isambig(new_file)){
+ FPROC(sparms)->fs->context
+ = sparms->text.handles->h.f.context;
+ if(strlen(new_file) < MAXFOLDER - 1){
+ strncpy(FPROC(sparms)->fs->first_folder, new_file, MAXFOLDER);
+ FPROC(sparms)->fs->first_folder[MAXFOLDER-1] = '\0';
+ }
+ }
+ }
+
+ ps_global->mangled_footer++;
+ }
+ else
+ q_status_message(SM_ORDER | SM_DING, 0, 4,
+ _("Empty folder collection. No folder to rename!"));
+
+ break;
+
+
+ /*-------------- Delete --------------------*/
+ case MC_DELETE :
+ if(!(sparms->text.handles
+ && folder_total(FOLDERS(sparms->text.handles->h.f.context)))){
+ q_status_message(SM_ORDER | SM_DING, 0, 4,
+ _("Empty folder collection. No folder to delete!"));
+ }
+ else{
+ char next_folder[MAILTMPLEN+1];
+
+ next_folder[0] = '\0';
+ if(delete_folder(sparms->text.handles->h.f.context,
+ sparms->text.handles->h.f.index,
+ next_folder, sizeof(next_folder),
+ FPROC(sparms)->fs->cache_streamp
+ && *FPROC(sparms)->fs->cache_streamp
+ ? FPROC(sparms)->fs->cache_streamp : NULL)){
+
+ /* repaint, placing cursor on adjacent folder! */
+ rv = 1;
+ if(next_folder[0] && strlen(next_folder) < MAXFOLDER - 1){
+ strncpy(FPROC(sparms)->fs->first_folder, next_folder, MAXFOLDER);
+ FPROC(sparms)->fs->first_folder[MAXFOLDER-1] = '\0';
+ }
+ else
+ sparms->text.handles->h.f.context->use &= ~CNTXT_ZOOM;
+ }
+ else
+ ps_global->mangled_footer++;
+ }
+
+ break;
+
+
+ /*----------------- Shuffle incoming folder list -----------*/
+ case MC_SHUFFLE :
+ {
+ CONTEXT_S *cntxt = (sparms->text.handles)
+ ? sparms->text.handles->h.f.context : NULL;
+
+ if(!(cntxt && cntxt->use & CNTXT_INCMNG))
+ q_status_message(SM_ORDER, 0, 4,
+ _("May only shuffle Incoming-Folders."));
+ else if(folder_total(FOLDERS(cntxt)) == 0)
+ q_status_message(SM_ORDER, 0, 4,
+ _("No folders to shuffle."));
+ else if(folder_total(FOLDERS(cntxt)) < 2)
+ q_status_message(SM_ORDER, 0, 4,
+ _("Shuffle only makes sense with more than one folder."));
+ else{
+ if(FPROC(sparms) && FPROC(sparms)->fs &&
+ FPROC(sparms)->fs && sparms->text.handles &&
+ sparms->text.handles->h.f.index >= 0 &&
+ sparms->text.handles->h.f.index <
+ folder_total(FOLDERS(cntxt))){
+ strncpy(FPROC(sparms)->fs->first_folder,
+ FLDR_NAME(folder_entry(sparms->text.handles->h.f.index,
+ FOLDERS(cntxt))), MAXFOLDER);
+ FPROC(sparms)->fs->first_folder[MAXFOLDER-1] = '\0';
+ }
+
+ rv = shuffle_incoming_folders(cntxt,
+ sparms->text.handles->h.f.index);
+ }
+ }
+
+ break;
+
+
+ /*-------------- Goto Folder Prompt --------------------*/
+ case MC_GOTO :
+ {
+ int notrealinbox;
+ CONTEXT_S *c = (sparms->text.handles)
+ ? sparms->text.handles->h.f.context
+ : FPROC(sparms)->fs->context;
+ char *new_fold = broach_folder(-FOOTER_ROWS(ps_global), 0, &notrealinbox, &c);
+
+ if(ps_global && ps_global->ttyo){
+ blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
+ ps_global->mangled_footer = 1;
+ }
+
+ if(new_fold && do_broach_folder(new_fold, c, NULL, notrealinbox ? 0L : DB_INBOXWOCNTXT) > 0){
+ ps_global->next_screen = mail_index_screen;
+ FPROC(sparms)->done = rv = 1;
+ }
+ else
+ ps_global->mangled_footer = 1;
+
+ if((c = ((sparms->text.handles)
+ ? sparms->text.handles->h.f.context
+ : FPROC(sparms)->fs->context))->dir->status & CNTXT_NOFIND)
+ refresh_folder_list(c, FPROC(sparms)->fs->no_dirs, TRUE, FPROC(sparms)->fs->cache_streamp);
+ }
+
+ break;
+
+
+ /*------------- Print list of folders ---------*/
+ case MC_PRINTFLDR :
+ print_folders(FPROC(sparms));
+ ps_global->mangled_footer++;
+ break;
+
+
+ /*----- Select the current folder, or toggle checkbox -----*/
+ case MC_SELCUR :
+ /*---------- Select set of folders ----------*/
+ case MC_SELECT :
+ if(sparms->text.handles
+ && folder_total(FOLDERS(sparms->text.handles->h.f.context))){
+ if(cmd == MC_SELCUR){
+ rv = folder_select_toggle(sparms->text.handles->h.f.context,
+ sparms->text.handles->h.f.index,
+ (NEWS_TEST(sparms->text.handles->h.f.context)
+ && FPROC(sparms)->fs->list_cntxt)
+ ? ng_scroll_edit : folder_select_update);
+ if(!rv) ps_global->mangled_screen = 1;
+ }
+ else
+ switch(folder_select(ps_global,
+ sparms->text.handles->h.f.context,
+ sparms->text.handles->h.f.index)){
+ case 1 :
+ rv = 1; /* rebuild screen */
+
+ case 0 :
+ default :
+ ps_global->mangled_screen++;
+ break;
+ }
+
+ if((sparms->text.handles->h.f.context->use & CNTXT_ZOOM)
+ && !selected_folders(sparms->text.handles->h.f.context)){
+ sparms->text.handles->h.f.context->use &= ~CNTXT_ZOOM;
+ rv = 1; /* make sure to redraw */
+ }
+
+ if(rv){ /* remember where to start */
+ FOLDER_S *fp;
+
+ FPROC(sparms)->fs->context = sparms->text.handles->h.f.context;
+ if((fp = folder_entry(sparms->text.handles->h.f.index,
+ FOLDERS(sparms->text.handles->h.f.context))) != NULL){
+ if(strlen(FLDR_NAME(fp)) < MAXFOLDER - 1){
+ strncpy(FPROC(sparms)->fs->first_folder, FLDR_NAME(fp), MAXFOLDER);
+ FPROC(sparms)->fs->first_folder[MAXFOLDER-1] = '\0';
+ }
+ }
+ }
+ }
+ else
+ q_status_message(SM_ORDER | SM_DING, 0, 4,
+ _("Empty folder collection. No folder to select!"));
+
+ break;
+
+
+ /*---------- Display folders ----------*/
+ case MC_ZOOM :
+ if(sparms->text.handles
+ && folder_total(FOLDERS(sparms->text.handles->h.f.context))){
+ FOLDER_S *fp;
+ int n;
+
+ if((n = selected_folders(sparms->text.handles->h.f.context)) != 0){
+ if(sparms->text.handles->h.f.context->use & CNTXT_ZOOM){
+ sparms->text.handles->h.f.context->use &= ~CNTXT_ZOOM;
+ q_status_message(SM_ORDER, 0, 3,
+ _("Folder List Zoom mode is now off"));
+ }
+ else{
+ q_status_message1(SM_ORDER, 0, 3,
+ _("In Zoomed list of %s folders. Use \"Z\" to restore regular list"),
+ int2string(n));
+ sparms->text.handles->h.f.context->use |= CNTXT_ZOOM;
+ }
+
+ /* exit scrolltool to rebuild screen */
+ rv = 1;
+
+ /* Set where to start after it's rebuilt */
+ FPROC(sparms)->fs->context = sparms->text.handles->h.f.context;
+ FPROC(sparms)->fs->first_folder[0] = '\0';
+ if((fp = folder_entry(sparms->text.handles->h.f.index,
+ FOLDERS(sparms->text.handles->h.f.context)))
+ && !((sparms->text.handles->h.f.context->use & CNTXT_ZOOM)
+ && !fp->selected)
+ && strlen(FLDR_NAME(fp)) < MAXFOLDER - 1){
+ strncpy(FPROC(sparms)->fs->first_folder, FLDR_NAME(fp), MAXFOLDER);
+ FPROC(sparms)->fs->first_folder[MAXFOLDER-1] = '\0';
+ }
+ }
+ else
+ q_status_message(SM_ORDER, 0, 3,
+ _("No selected folders to Zoom on"));
+ }
+ else
+ q_status_message(SM_ORDER, 0, 4, _("No Folders to Zoom on!"));
+
+ break;
+
+ /*----- Ask user to abandon selection before exiting -----*/
+ case MC_EXITQUERY :
+ if(sparms->text.handles
+ && FOLDERS(sparms->text.handles->h.f.context)){
+ int i, folder_n;
+ FOLDER_S *fp;
+
+ folder_n = folder_total(FOLDERS(sparms->text.handles->h.f.context));
+ /* any selected? */
+ for(i = 0; i < folder_n; i++){
+ fp = folder_entry(i, FOLDERS(sparms->text.handles->h.f.context));
+ if(fp->selected)
+ break;
+ }
+
+ if(i < folder_n /* some selections have been made */
+ && want_to(_("Really abandon your selections "),
+ 'y', 'x', NO_HELP, WT_NORM) != 'y'){
+ break;
+ }
+ }
+ rv = 1;
+ break;
+
+ /*------------New Msg command --------------*/
+ case MC_CHK_RECENT:
+ /*
+ * Derived from code provided by
+ * Rostislav Neplokh neplokh@andrew.cmu.edu and
+ * Robert Siemborski (rjs3@andrew).
+ */
+ if(sparms->text.handles
+ && folder_total(FOLDERS(sparms->text.handles->h.f.context))){
+ FOLDER_S *folder;
+
+ folder = folder_entry(sparms->text.handles->h.f.index,
+ FOLDERS(sparms->text.handles->h.f.context));
+
+ if(!folder){
+ strncpy(tmp_output, _("Invalid Folder Name"), sizeof(tmp_output)-1);
+ tmp_output[sizeof(tmp_output)-1] = '\0';
+ }
+ else if(folder->isdir && !folder->isfolder){
+ snprintf(tmp_output, sizeof(tmp_output), _("\"%s\" is a directory"), folder->name);
+ }
+ else{
+ char mailbox_name[MAXPATH+1];
+ unsigned long tot, rec;
+ int we_cancel;
+
+ context_apply(mailbox_name,
+ sparms->text.handles->h.f.context,
+ folder->name, MAXPATH+1);
+
+ we_cancel = busy_cue(NULL, NULL, 1);
+
+ if(get_recent_in_folder(mailbox_name, &rec, NULL, &tot, NULL))
+ snprintf(tmp_output, sizeof(tmp_output),
+ _("%lu total message%s, %lu of them recent"),
+ tot, plural(tot), rec);
+ else
+ snprintf(tmp_output, sizeof(tmp_output),
+ _("%s: Trouble checking for recent mail"), folder->name);
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+ }
+ }
+ else{
+ strncpy(tmp_output, _("No folder to check! Can't get recent info"),
+ sizeof(tmp_output)-1);
+ tmp_output[sizeof(tmp_output)-1] = '\0';
+ }
+
+ q_status_message(SM_ORDER, 0, 3, tmp_output);
+ break;
+
+
+ /*--------------- Invalid Command --------------*/
+ default:
+ q_status_message1(SM_ORDER, 0, 2, "fix this: cmd = %s", comatose(cmd));
+ break;
+ }
+
+ return(rv);
+}
+
+
+int
+folder_lister_clickclick(SCROLL_S *sparms)
+{
+ if(!FPROC(sparms)->fs->list_cntxt)
+ return(folder_lister_choice(sparms));
+ else
+ return(folder_processor(MC_SELCUR, ps_global->msgmap, sparms));
+}
+
+int
+folder_lister_choice(SCROLL_S *sparms)
+{
+ int rv = 0, empty = 0;
+ int index = (sparms->text.handles)
+ ? sparms->text.handles->h.f.index : 0;
+ CONTEXT_S *cntxt = (sparms->text.handles)
+ ? sparms->text.handles->h.f.context : NULL;
+
+ if(cntxt){
+
+ FPROC(sparms)->fs->context = cntxt;
+
+ if(cntxt->dir->status & CNTXT_NOFIND){
+ rv = 1; /* leave scrolltool to rebuild screen */
+ FPROC(sparms)->fs->context = cntxt;
+ FPROC(sparms)->fs->first_folder[0] = '\0';
+ }
+ else if(folder_total(FOLDERS(cntxt))){
+ if(folder_lister_select(FPROC(sparms)->fs, cntxt, index,
+ sparms->text.handles ?
+ sparms->text.handles->is_dual_do_open : 0)){
+ rv = 1; /* leave scrolltool to rebuild screen */
+ }
+ else if(FPROC(sparms)->fs->list_cntxt == cntxt){
+ int n = 0, i, folder_n;
+ FOLDER_S *fp;
+ STRLIST_S *sl = NULL, **slp;
+
+ /* Scan folder list for selected items */
+ folder_n = folder_total(FOLDERS(cntxt));
+ slp = &sl;
+ for(i = 0; i < folder_n; i++){
+ fp = folder_entry(i, FOLDERS(cntxt));
+ if(fp->selected){
+ n++;
+ if((*FPROC(sparms)->fs->f.valid)(fp,
+ FPROC(sparms)->fs)){
+ *slp = new_strlist(NULL);
+ (*slp)->name = folder_lister_fullname(
+ FPROC(sparms)->fs, FLDR_NAME(fp));
+
+ slp = &(*slp)->next;
+ }
+ else{
+ free_strlist(&sl);
+ break;
+ }
+ }
+ }
+
+ if((FPROC(sparms)->rv = sl) != NULL)
+ FPROC(sparms)->done = rv = 1;
+ else if(!n)
+ q_status_message(SM_ORDER, 0, 1, LISTMODE_GRIPE);
+ }
+ else
+ rv = folder_lister_finish(sparms, cntxt, index);
+ }
+ else
+ empty++;
+ }
+ else
+ empty++;
+
+ if(empty)
+ q_status_message(SM_ORDER | SM_DING, 3, 3, _("Empty folder list!"));
+
+ return(rv);
+}
+
+
+int
+folder_lister_finish(SCROLL_S *sparms, CONTEXT_S *cntxt, int index)
+{
+ FOLDER_S *f = folder_entry(index, FOLDERS(cntxt));
+ int rv = 0;
+
+ if((*FPROC(sparms)->fs->f.valid)(f, FPROC(sparms)->fs)){
+ /*
+ * Package up the selected folder names and return...
+ */
+ FPROC(sparms)->fs->context = cntxt;
+ FPROC(sparms)->rv = new_strlist(NULL);
+ FPROC(sparms)->rv->name = folder_lister_fullname(FPROC(sparms)->fs,
+ FLDR_NAME(f));
+ FPROC(sparms)->done = rv = 1;
+ }
+
+ return(rv);
+}
+
+
+/*
+ * This is so that when you Save and use ^T to go to the folder list, and
+ * you're in a directory with no folders, you have a way to add a new
+ * folder there. The add actually gets done by the caller. This is just a
+ * way to let the user type in the name.
+ */
+int
+folder_lister_addmanually(SCROLL_S *sparms)
+{
+ int rc, flags = OE_APPEND_CURRENT, cnt = 0, rv = 0;
+ char addname[MAXFOLDER+1];
+ HelpType help;
+ CONTEXT_S *cntxt = (sparms->text.handles)
+ ? sparms->text.handles->h.f.context
+ : FPROC(sparms)->fs->context;
+
+ /*
+ * Get the foldername from the user.
+ */
+ addname[0] = '\0';
+ help = NO_HELP;
+ while(1){
+ rc = optionally_enter(addname, -FOOTER_ROWS(ps_global), 0,
+ sizeof(addname), _("Name of new folder : "),
+ NULL, help, &flags);
+ removing_leading_and_trailing_white_space(addname);
+
+ if(rc == 3)
+ help = (help == NO_HELP) ? h_save_addman : NO_HELP;
+ else if(rc == 1)
+ return(rv);
+ else if(rc == 0){
+ if(F_OFF(F_ENABLE_DOT_FOLDERS,ps_global) && *addname == '.'){
+ if(cnt++ <= 0)
+ q_status_message(SM_ORDER,3,3,
+ _("Folder name can't begin with dot"));
+ else
+ q_status_message1(SM_ORDER,3,3,
+ _("Config feature \"%s\" enables names beginning with dot"),
+ pretty_feature_name(feature_list_name(F_ENABLE_DOT_FOLDERS), -1));
+
+ display_message(NO_OP_COMMAND);
+ continue;
+ }
+ else if(!strucmp(addname, ps_global->inbox_name)){
+ q_status_message1(SM_ORDER, 3, 3,
+ _("Can't add folder named %s"),
+ ps_global->inbox_name);
+ continue;
+ }
+
+ break;
+ }
+ }
+
+ if(*addname){
+ FPROC(sparms)->fs->context = cntxt;
+ FPROC(sparms)->rv = new_strlist(NULL);
+ FPROC(sparms)->rv->name = folder_lister_fullname(FPROC(sparms)->fs,
+ addname);
+ FPROC(sparms)->done = rv = 1;
+ }
+
+ return(rv);
+}
+
+
+void
+folder_lister_km_manager(SCROLL_S *sparms, int handle_hidden)
+{
+ FOLDER_S *fp;
+
+ /* if we're "in" a sub-directory, offer way out */
+ if((sparms->text.handles)
+ ? sparms->text.handles->h.f.context->dir->prev
+ : FPROC(sparms)->fs->context->dir->prev){
+
+ /*
+ * Leave the command characters alone and just change
+ * the labels and the bind.cmd for KM_COL_KEY.
+ * Also, leave KM_MAIN_KEY alone instead of trying to
+ * turn it off in the else clause when it is redundant.
+ */
+ sparms->keys.menu->keys[KM_COL_KEY].label = N_("ParentDir");
+ sparms->keys.menu->keys[KM_COL_KEY].bind.cmd = MC_PARENT;
+ }
+ else if((FPROC(sparms)->fs->context->next
+ || FPROC(sparms)->fs->context->prev)
+ && !FPROC(sparms)->fs->combined_view){
+ sparms->keys.menu->keys[KM_COL_KEY].label = N_("ClctnList");
+ sparms->keys.menu->keys[KM_COL_KEY].bind.cmd = MC_EXIT;
+ }
+ else{
+ sparms->keys.menu->keys[KM_COL_KEY].label = N_("Main Menu");
+ sparms->keys.menu->keys[KM_COL_KEY].bind.cmd = MC_MAIN;
+ }
+
+ if(F_OFF(F_ENABLE_AGG_OPS,ps_global)){
+ clrbitn(KM_ZOOM_KEY, sparms->keys.bitmap);
+ clrbitn(KM_SELECT_KEY, sparms->keys.bitmap);
+ clrbitn(KM_SELCUR_KEY, sparms->keys.bitmap);
+ }
+
+ if(sparms->text.handles
+ && (fp = folder_entry(sparms->text.handles->h.f.index,
+ FOLDERS(sparms->text.handles->h.f.context)))){
+ if(fp->isdir && !sparms->text.handles->is_dual_do_open){
+ sparms->keys.menu->keys[KM_SEL_KEY].label = "[" N_("View Dir") "]";
+ menu_add_binding(sparms->keys.menu, 'v', MC_CHOICE);
+ menu_add_binding(sparms->keys.menu, ctrl('M'), MC_CHOICE);
+ menu_add_binding(sparms->keys.menu, ctrl('J'), MC_CHOICE);
+ setbitn(KM_SEL_KEY, sparms->keys.bitmap);
+ clrbitn(KM_ALTVIEW_KEY, sparms->keys.bitmap);
+ clrbitn(KM_EXPORT_KEY, sparms->keys.bitmap);
+ clrbitn(KM_IMPORT_KEY, sparms->keys.bitmap);
+ }
+ else{
+ sparms->keys.menu->keys[KM_SEL_KEY].label = "[" N_("View Fldr") "]";
+ menu_add_binding(sparms->keys.menu, 'v', MC_CHOICE);
+ menu_add_binding(sparms->keys.menu, ctrl('M'), MC_CHOICE);
+ menu_add_binding(sparms->keys.menu, ctrl('J'), MC_CHOICE);
+ setbitn(KM_SEL_KEY, sparms->keys.bitmap);
+ clrbitn(KM_ALTVIEW_KEY, sparms->keys.bitmap);
+ setbitn(KM_EXPORT_KEY, sparms->keys.bitmap);
+ setbitn(KM_IMPORT_KEY, sparms->keys.bitmap);
+ }
+ }
+ else if(FPROC(sparms)->fs->combined_view
+ && sparms->text.handles && sparms->text.handles->h.f.context
+ && !sparms->text.handles->h.f.context->dir->prev){
+ sparms->keys.menu->keys[KM_SEL_KEY].label = "[" N_("View Cltn") "]";
+ menu_add_binding(sparms->keys.menu, 'v', MC_CHOICE);
+ menu_add_binding(sparms->keys.menu, ctrl('M'), MC_CHOICE);
+ menu_add_binding(sparms->keys.menu, ctrl('J'), MC_CHOICE);
+ setbitn(KM_SEL_KEY, sparms->keys.bitmap);
+ clrbitn(KM_ALTVIEW_KEY, sparms->keys.bitmap);
+ clrbitn(KM_EXPORT_KEY, sparms->keys.bitmap);
+ clrbitn(KM_IMPORT_KEY, sparms->keys.bitmap);
+ }
+ else{
+ clrbitn(KM_SEL_KEY, sparms->keys.bitmap);
+ clrbitn(KM_ALTVIEW_KEY, sparms->keys.bitmap);
+ clrbitn(KM_EXPORT_KEY, sparms->keys.bitmap);
+ clrbitn(KM_IMPORT_KEY, sparms->keys.bitmap);
+ }
+
+ if((sparms->text.handles &&
+ sparms->text.handles->h.f.context &&
+ sparms->text.handles->h.f.context->use & CNTXT_INCMNG) ||
+ (FPROC(sparms) && FPROC(sparms)->fs &&
+ FPROC(sparms)->fs->context &&
+ FPROC(sparms)->fs->context->use & CNTXT_INCMNG))
+ setbitn(KM_SHUFFLE_KEY, sparms->keys.bitmap);
+ else
+ clrbitn(KM_SHUFFLE_KEY, sparms->keys.bitmap);
+
+ if(F_ON(F_TAB_CHK_RECENT, ps_global)){
+ menu_clear_binding(sparms->keys.menu, TAB);
+ menu_init_binding(sparms->keys.menu, TAB, MC_CHK_RECENT, "Tab",
+ /* TRANSLATORS: New Messages */
+ N_("NewMsgs"), KM_RECENT_KEY);
+ setbitn(KM_RECENT_KEY, sparms->keys.bitmap);
+ }
+ else{
+ menu_clear_binding(sparms->keys.menu, TAB);
+ menu_add_binding(sparms->keys.menu, TAB, MC_NEXT_HANDLE);
+ clrbitn(KM_RECENT_KEY, sparms->keys.bitmap);
+ }
+
+ /* May have to "undo" what scrolltool "did" */
+ if(F_ON(F_ARROW_NAV, ps_global)){
+ if(F_ON(F_RELAXED_ARROW_NAV, ps_global)){
+ menu_clear_binding(sparms->keys.menu, KEY_LEFT);
+ menu_add_binding(sparms->keys.menu, KEY_LEFT, MC_PREV_HANDLE);
+ menu_clear_binding(sparms->keys.menu, KEY_RIGHT);
+ menu_add_binding(sparms->keys.menu, KEY_RIGHT, MC_NEXT_HANDLE);
+ }
+ else{
+ menu_clear_binding(sparms->keys.menu, KEY_UP);
+ menu_add_binding(sparms->keys.menu, KEY_UP, MC_PREV_HANDLE);
+ menu_clear_binding(sparms->keys.menu, KEY_DOWN);
+ menu_add_binding(sparms->keys.menu, KEY_DOWN, MC_NEXT_HANDLE);
+ }
+ }
+}
+
+
+void
+folder_lister_km_sel_manager(SCROLL_S *sparms, int handle_hidden)
+{
+ FOLDER_S *fp;
+
+ /* if we're "in" a sub-directory, offer way out */
+ if((sparms->text.handles)
+ ? sparms->text.handles->h.f.context->dir->prev
+ : FPROC(sparms)->fs->context->dir->prev){
+ sparms->keys.menu->keys[FC_COL_KEY].name = "<";
+ /* TRANSLATORS: go to parent directory one level up */
+ sparms->keys.menu->keys[FC_COL_KEY].label = N_("ParentDir");
+ sparms->keys.menu->keys[FC_COL_KEY].bind.cmd = MC_PARENT;
+ sparms->keys.menu->keys[FC_COL_KEY].bind.ch[0] = '<';
+ sparms->keys.menu->keys[FC_COL_KEY].bind.ch[1] = ',';
+ if(F_ON(F_ARROW_NAV,ps_global)){
+ sparms->keys.menu->keys[FC_COL_KEY].bind.nch = 3;
+ sparms->keys.menu->keys[FC_COL_KEY].bind.ch[2] = KEY_LEFT;
+ }
+ else
+ sparms->keys.menu->keys[FC_COL_KEY].bind.nch = 2;
+
+ /* ExitSelect in position 1 */
+ setbitn(FC_EXIT_KEY, sparms->keys.bitmap);
+ }
+ else if((FPROC(sparms)->fs->context->next
+ || FPROC(sparms)->fs->context->prev)
+ && !FPROC(sparms)->fs->combined_view){
+ sparms->keys.menu->keys[FC_COL_KEY].name = "<";
+ /* TRANSLATORS: go to Collection List */
+ sparms->keys.menu->keys[FC_COL_KEY].label = N_("ClctnList");
+ sparms->keys.menu->keys[FC_COL_KEY].bind.cmd = MC_COLLECTIONS;
+ sparms->keys.menu->keys[FC_COL_KEY].bind.ch[0] = '<';
+ sparms->keys.menu->keys[FC_COL_KEY].bind.ch[1] = ',';
+ if(F_ON(F_ARROW_NAV,ps_global)){
+ sparms->keys.menu->keys[FC_COL_KEY].bind.nch = 3;
+ sparms->keys.menu->keys[FC_COL_KEY].bind.ch[2] = KEY_LEFT;
+ }
+ else
+ sparms->keys.menu->keys[FC_COL_KEY].bind.nch = 2;
+
+ /* ExitSelect in position 1 */
+ setbitn(FC_EXIT_KEY, sparms->keys.bitmap);
+ }
+ else if(FPROC(sparms)->fs->combined_view){
+ /*
+ * This can't be a menu_init_binding() because we don't want
+ * to remove the ExitSelect command in position FC_EXIT_KEY.
+ * We just turn it off until we need it again.
+ */
+ sparms->keys.menu->keys[FC_COL_KEY].name = "E";
+ sparms->keys.menu->keys[FC_COL_KEY].label = N_("ExitSelect");
+ sparms->keys.menu->keys[FC_COL_KEY].bind.cmd = MC_EXIT;
+ sparms->keys.menu->keys[FC_COL_KEY].bind.nch = 1;
+ sparms->keys.menu->keys[FC_COL_KEY].bind.ch[0] = 'e';
+
+ /* turn off ExitSelect in position 1, it's in 2 now */
+ clrbitn(FC_EXIT_KEY, sparms->keys.bitmap);
+ }
+
+ /* clean up per-entry bindings */
+ clrbitn(FC_SEL_KEY, sparms->keys.bitmap);
+ clrbitn(FC_ALTSEL_KEY, sparms->keys.bitmap);
+ menu_clear_binding(sparms->keys.menu, ctrl('M'));
+ menu_clear_binding(sparms->keys.menu, ctrl('J'));
+ menu_clear_binding(sparms->keys.menu, '>');
+ menu_clear_binding(sparms->keys.menu, '.');
+ menu_clear_binding(sparms->keys.menu, 's');
+ if(F_ON(F_ARROW_NAV,ps_global))
+ menu_clear_binding(sparms->keys.menu, KEY_RIGHT);
+
+ /* and then re-assign them as needed */
+ if(sparms->text.handles
+ && (fp = folder_entry(sparms->text.handles->h.f.index,
+ FOLDERS(sparms->text.handles->h.f.context)))){
+ setbitn(FC_SEL_KEY, sparms->keys.bitmap);
+ if(fp->isdir){
+ sparms->keys.menu->keys[FC_SEL_KEY].name = ">";
+ menu_add_binding(sparms->keys.menu, '>', MC_CHOICE);
+ menu_add_binding(sparms->keys.menu, '.', MC_CHOICE);
+ if(F_ON(F_ARROW_NAV,ps_global))
+ menu_add_binding(sparms->keys.menu, KEY_RIGHT, MC_CHOICE);
+
+ if(fp->isfolder){
+ sparms->keys.menu->keys[FC_SEL_KEY].label = N_("View Dir");
+ setbitn(FC_ALTSEL_KEY, sparms->keys.bitmap);
+ menu_add_binding(sparms->keys.menu, 's', MC_OPENFLDR);
+ menu_add_binding(sparms->keys.menu, ctrl('M'), MC_OPENFLDR);
+ menu_add_binding(sparms->keys.menu, ctrl('J'), MC_OPENFLDR);
+ }
+ else{
+ sparms->keys.menu->keys[FC_SEL_KEY].label = "[" N_("View Dir") "]";
+ menu_add_binding(sparms->keys.menu, ctrl('M'), MC_CHOICE);
+ menu_add_binding(sparms->keys.menu, ctrl('J'), MC_CHOICE);
+ }
+ }
+ else{
+ sparms->keys.menu->keys[FC_SEL_KEY].name = "S";
+ sparms->keys.menu->keys[FC_SEL_KEY].label = "[" N_("Select") "]";
+
+ menu_add_binding(sparms->keys.menu, 's', MC_CHOICE);
+ menu_add_binding(sparms->keys.menu, ctrl('M'), MC_CHOICE);
+ menu_add_binding(sparms->keys.menu, ctrl('J'), MC_CHOICE);
+ }
+
+ if(F_ON(F_ARROW_NAV,ps_global))
+ menu_add_binding(sparms->keys.menu, KEY_RIGHT, MC_CHOICE);
+ }
+ else if(FPROC(sparms)->fs->combined_view
+ && sparms->text.handles && sparms->text.handles->h.f.context
+ && !sparms->text.handles->h.f.context->dir->prev){
+ setbitn(FC_SEL_KEY, sparms->keys.bitmap);
+ sparms->keys.menu->keys[FC_SEL_KEY].name = ">";
+ sparms->keys.menu->keys[FC_SEL_KEY].label = "[" N_("View Cltn") "]";
+ menu_add_binding(sparms->keys.menu, '>', MC_CHOICE);
+ menu_add_binding(sparms->keys.menu, '.', MC_CHOICE);
+ if(F_ON(F_ARROW_NAV,ps_global))
+ menu_add_binding(sparms->keys.menu, KEY_RIGHT, MC_CHOICE);
+
+ menu_add_binding(sparms->keys.menu, ctrl('M'), MC_CHOICE);
+ menu_add_binding(sparms->keys.menu, ctrl('J'), MC_CHOICE);
+ }
+
+ /* May have to "undo" what scrolltool "did" */
+ if(F_ON(F_ARROW_NAV, ps_global)){
+ if(F_ON(F_RELAXED_ARROW_NAV, ps_global)){
+ menu_clear_binding(sparms->keys.menu, KEY_LEFT);
+ menu_add_binding(sparms->keys.menu, KEY_LEFT, MC_PREV_HANDLE);
+ menu_clear_binding(sparms->keys.menu, KEY_RIGHT);
+ menu_add_binding(sparms->keys.menu, KEY_RIGHT, MC_NEXT_HANDLE);
+ }
+ else{
+ menu_clear_binding(sparms->keys.menu, KEY_UP);
+ menu_add_binding(sparms->keys.menu, KEY_UP, MC_PREV_HANDLE);
+ menu_clear_binding(sparms->keys.menu, KEY_DOWN);
+ menu_add_binding(sparms->keys.menu, KEY_DOWN, MC_NEXT_HANDLE);
+ }
+ }
+}
+
+
+void
+folder_lister_km_sub_manager(SCROLL_S *sparms, int handle_hidden)
+{
+ /*
+ * Folder_processor() also modifies the keymenu.
+ */
+ if(FPROC(sparms)->fs->list_cntxt){
+ clrbitn(SB_LIST_KEY, sparms->keys.bitmap);
+ sparms->keys.menu->keys[SB_SEL_KEY].name = "X";
+ sparms->keys.menu->keys[SB_SEL_KEY].label = "[" N_("Set/Unset") "]";
+ sparms->keys.menu->keys[SB_SEL_KEY].bind.cmd = MC_SELCUR;
+ sparms->keys.menu->keys[SB_SEL_KEY].bind.ch[0] = 'x';
+ }
+ else{
+ clrbitn(SB_SUB_KEY, sparms->keys.bitmap);
+ sparms->keys.menu->keys[SB_SEL_KEY].name = "S";
+ sparms->keys.menu->keys[SB_SEL_KEY].label = "[" N_("Subscribe") "]";
+ sparms->keys.menu->keys[SB_SEL_KEY].bind.cmd = MC_CHOICE;
+ sparms->keys.menu->keys[SB_SEL_KEY].bind.ch[0] = 's';
+ }
+
+ /* May have to "undo" what scrolltool "did" */
+ if(F_ON(F_ARROW_NAV, ps_global)){
+ if(F_ON(F_RELAXED_ARROW_NAV, ps_global)){
+ menu_clear_binding(sparms->keys.menu, KEY_LEFT);
+ menu_add_binding(sparms->keys.menu, KEY_LEFT, MC_PREV_HANDLE);
+ menu_clear_binding(sparms->keys.menu, KEY_RIGHT);
+ menu_add_binding(sparms->keys.menu, KEY_RIGHT, MC_NEXT_HANDLE);
+ }
+ else{
+ menu_clear_binding(sparms->keys.menu, KEY_UP);
+ menu_add_binding(sparms->keys.menu, KEY_UP, MC_PREV_HANDLE);
+ menu_clear_binding(sparms->keys.menu, KEY_DOWN);
+ menu_add_binding(sparms->keys.menu, KEY_DOWN, MC_NEXT_HANDLE);
+ }
+ }
+}
+
+
+
+int
+folder_select(struct pine *ps, CONTEXT_S *context, int cur_index)
+{
+ int i, j, n, total, old_tot, diff,
+ q = 0, rv = 0, narrow = 0;
+ HelpType help = NO_HELP;
+ ESCKEY_S *sel_opts;
+ FOLDER_S *f;
+ static ESCKEY_S self_opts2[] = {
+ /* TRANSLATORS: keymenu descriptions, select all folders, current folder, select
+ based on folder properties, or select based on text contents in folders */
+ {'a', 'a', "A", N_("select All")},
+ {'c', 'c', "C", N_("select Cur")},
+ {'p', 'p', "P", N_("Properties")},
+ {'t', 't', "T", N_("Text")},
+ {-1, 0, NULL, NULL},
+ {-1, 0, NULL, NULL}
+ };
+ extern ESCKEY_S sel_opts1[];
+ extern char *sel_pmt2;
+#define N_RECENT 4
+
+ f = folder_entry(cur_index, FOLDERS(context));
+
+ sel_opts = self_opts2;
+ if((old_tot = selected_folders(context)) != 0){
+ sel_opts1[1].label = f->selected ? N_("unselect Cur") : N_("select Cur");
+ sel_opts += 2; /* disable extra options */
+ switch(q = radio_buttons(SEL_ALTER_PMT, -FOOTER_ROWS(ps_global),
+ sel_opts1, 'c', 'x', help, RB_NORM)){
+ case 'f' : /* flip selection */
+ n = folder_total(FOLDERS(context));
+ for(total = i = 0; i < n; i++)
+ if((f = folder_entry(i, FOLDERS(context))) != NULL)
+ f->selected = !f->selected;
+
+ return(1); /* repaint */
+
+ case 'n' : /* narrow selection */
+ narrow++;
+ case 'b' : /* broaden selection */
+ q = 0; /* but don't offer criteria prompt */
+ if(context->use & CNTXT_INCMNG){
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("Select \"%s\" not supported in Incoming Folders"),
+ narrow ? "Narrow" : "Broaden");
+ return(0);
+ }
+
+ break;
+
+ case 'c' : /* Un/Select Current */
+ case 'a' : /* Unselect All */
+ case 'x' : /* cancel */
+ break;
+
+ default :
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("Unsupported Select option"));
+ return(0);
+ }
+ }
+
+ if(context->use & CNTXT_INCMNG && F_ON(F_ENABLE_INCOMING_CHECKING, ps_global)){
+ if(F_ON(F_INCOMING_CHECKING_RECENT, ps_global)){
+ self_opts2[N_RECENT].ch = 'r';
+ self_opts2[N_RECENT].rval = 'r';
+ self_opts2[N_RECENT].name = "R";
+ self_opts2[N_RECENT].label = N_("Recent");
+ }
+ else{
+ self_opts2[N_RECENT].ch = 'u';
+ self_opts2[N_RECENT].rval = 'u';
+ self_opts2[N_RECENT].name = "U";
+ self_opts2[N_RECENT].label = N_("Unseen");
+ }
+ }
+ else{
+ self_opts2[N_RECENT].ch = -1;
+ self_opts2[N_RECENT].rval = 0;
+ self_opts2[N_RECENT].name = NULL;
+ self_opts2[N_RECENT].label = NULL;
+ }
+
+ if(!q)
+ q = radio_buttons(sel_pmt2, -FOOTER_ROWS(ps_global),
+ sel_opts, 'c', 'x', help, RB_NORM);
+
+ /*
+ * NOTE: See note about MESSAGECACHE "searched" bits above!
+ */
+ switch(q){
+ case 'x': /* cancel */
+ cmd_cancelled("Select command");
+ return(0);
+
+ case 'c' : /* toggle current's selected state */
+ return(folder_select_toggle(context, cur_index, folder_select_update));
+
+ case 'a' : /* select/unselect all */
+ n = folder_total(FOLDERS(context));
+ for(total = i = 0; i < n; i++)
+ folder_entry(i, FOLDERS(context))->selected = old_tot == 0;
+
+ q_status_message4(SM_ORDER, 0, 2,
+ "%s%s folder%s %sselected",
+ old_tot ? "" : "All ",
+ comatose(old_tot ? old_tot : n),
+ plural(old_tot ? old_tot : n), old_tot ? "UN" : "");
+ return(1);
+
+ case 't' : /* Text */
+ if(!folder_select_text(ps, context, narrow))
+ rv++;
+
+ break;
+
+ case 'p' : /* Properties */
+ if(!folder_select_props(ps, context, narrow))
+ rv++;
+
+ break;
+
+ case 'r' :
+ n = folder_total(FOLDERS(context));
+ for(i = 0; i < n; i++){
+ f = folder_entry(i, FOLDERS(context));
+ if(f->unseen_valid && f->new > 0L)
+ f->selected = 1;
+ }
+
+ break;
+
+ case 'u' :
+ n = folder_total(FOLDERS(context));
+ for(i = 0; i < n; i++){
+ f = folder_entry(i, FOLDERS(context));
+ if(f->unseen_valid && f->unseen > 0L)
+ f->selected = 1;
+ }
+
+ break;
+
+ default :
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("Unsupported Select option"));
+ return(0);
+ }
+
+ if(rv)
+ return(0);
+
+ /* rectify the scanned vs. selected folders */
+ n = folder_total(FOLDERS(context));
+ for(total = i = j = 0; i < n; i++)
+ if(folder_entry(i, FOLDERS(context))->scanned)
+ break;
+
+ /*
+ * Any matches at all? If not, the selected set remains the same.
+ * Note that when Narrowing, only matches in the intersection will
+ * show up as scanned. We need to reset i to 0 to erase any already
+ * selected messages which aren't in the intersection.
+ */
+ if(i < n)
+ for(i = 0; i < n; i++)
+ if((f = folder_entry(i, FOLDERS(context))) != NULL){
+ if(narrow){
+ if(f->selected){
+ f->selected = f->scanned;
+ j++;
+ }
+ }
+ else if(f->scanned)
+ f->selected = 1;
+ }
+
+ if(!(diff = (total = selected_folders(context)) - old_tot)){
+ if(narrow)
+ q_status_message4(SM_ORDER, 0, 2,
+ "%s. %s folder%s remain%s selected.",
+ j ? _("No change resulted")
+ : _("No messages in intersection"),
+ comatose(old_tot), plural(old_tot),
+ (old_tot == 1L) ? "s" : "");
+ else if(old_tot && j)
+ q_status_message(SM_ORDER, 0, 2,
+ _("No change resulted. Matching folders already selected."));
+ else
+ q_status_message1(SM_ORDER | SM_DING, 0, 2,
+ "Select failed! No %sfolders selected.",
+ old_tot ? "additional " : "");
+ }
+ else if(old_tot){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,
+ "Select matched %d folder%s. %s %sfolder%s %sselected.",
+ (diff > 0) ? diff : old_tot + diff,
+ plural((diff > 0) ? diff : old_tot + diff),
+ comatose((diff > 0) ? total : -diff),
+ (diff > 0) ? "total " : "",
+ plural((diff > 0) ? total : -diff),
+ (diff > 0) ? "" : "UN");
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ q_status_message(SM_ORDER, 0, 2, tmp_20k_buf);
+ }
+ else
+ q_status_message2(SM_ORDER, 0, 2, "Select matched %s folder%s.",
+ comatose(diff), plural(diff));
+
+ return(1);
+}
+
+
+
+
+int
+folder_lister_select(FSTATE_S *fs, CONTEXT_S *context, int index, int is_dual_do_open)
+{
+ int rv = 0;
+ FDIR_S *fp;
+ FOLDER_S *f = folder_entry(index, FOLDERS(context));
+
+ /*--- Entering a directory? ---*/
+ if(f->isdir && !is_dual_do_open){
+ fp = next_folder_dir(context, f->name, TRUE, fs->cache_streamp);
+
+ /* Provide context in new collection header */
+ fp->desc = folder_lister_desc(context, fp);
+
+ /* Insert new directory into list */
+ free_folder_list(context);
+ fp->prev = context->dir;
+ fp->status |= CNTXT_SUBDIR;
+ context->dir = fp;
+ q_status_message2(SM_ORDER, 0, 3, "Now in %sdirectory: %s",
+ folder_total(FOLDERS(context))
+ ? "" : "EMPTY ", fp->ref);
+ rv++;
+ }
+ else
+ rv = folder_lister_parent(fs, context, index, 1);
+
+ return(rv);
+}
+
+
+int
+folder_lister_parent(FSTATE_S *fs, CONTEXT_S *context, int index, int force_parent)
+{
+ int rv = 0;
+ FDIR_S *fp;
+
+ if(!force_parent && (fp = context->dir->prev)){
+ char *s, oldir[MAILTMPLEN];
+
+ folder_select_preserve(context);
+ oldir[0] = '\0';
+ if((s = strrindex(context->dir->ref, context->dir->delim)) != NULL){
+ *s = '\0';
+ if((s = strrindex(context->dir->ref, context->dir->delim)) != NULL){
+ strncpy(oldir, s+1, sizeof(oldir)-1);
+ oldir[sizeof(oldir)-1] = '\0';
+ }
+ }
+
+ if(*oldir){
+ /* remember current directory for hiliting in new list */
+ fs->context = context;
+ if(strlen(oldir) < MAXFOLDER - 1){
+ strncpy(fs->first_folder, oldir, MAXFOLDER);
+ fs->first_folder[MAXFOLDER-1] = '\0';
+ fs->first_dir = 1;
+ }
+ }
+
+ free_fdir(&context->dir, 0);
+ fp->status |= CNTXT_NOFIND;
+
+ context->dir = fp;
+
+ if(fp->status & CNTXT_SUBDIR)
+ q_status_message1(SM_ORDER, 0, 3, _("Now in directory: %s"),
+ strsquish(tmp_20k_buf + 500, SIZEOF_20KBUF-500, fp->ref,
+ ps_global->ttyo->screen_cols - 22));
+ else
+ q_status_message(SM_ORDER, 0, 3,
+ _("Returned to collection's top directory"));
+
+ rv++;
+ }
+
+ return(rv);
+}
+
+
+char *
+folder_lister_fullname(FSTATE_S *fs, char *name)
+{
+ if(fs->context->dir->status & CNTXT_SUBDIR){
+ char tmp[2*MAILTMPLEN], tmp2[2*MAILTMPLEN], *p;
+
+ if(fs->context->dir->ref){
+ snprintf(tmp, sizeof(tmp), "%.*s%.*s",
+ sizeof(tmp)/2,
+ ((fs->relative_path || (fs->context->use & CNTXT_SAVEDFLT))
+ && (p = strstr(fs->context->context, "%s")) && !*(p+2)
+ && !strncmp(fs->context->dir->ref, fs->context->context,
+ p - fs->context->context))
+ ? fs->context->dir->ref + (p - fs->context->context)
+ : fs->context->dir->ref,
+ sizeof(tmp)/2, name);
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+
+ /*
+ * If the applied name is still ambiguous (defined
+ * that way by the user (i.e., "mail/[]"), then
+ * we better fix it up...
+ */
+ if(context_isambig(tmp)
+ && !fs->relative_path
+ && !(fs->context->use & CNTXT_SAVEDFLT)){
+ /* if it's in the primary collection, the names relative */
+ if(fs->context->dir->ref){
+ if(IS_REMOTE(fs->context->context)
+ && (p = strrindex(fs->context->context, '}'))){
+ snprintf(tmp2, sizeof(tmp2), "%.*s%.*s",
+ MIN(p - fs->context->context + 1, sizeof(tmp2)/2),
+ fs->context->context,
+ sizeof(tmp2)/2, tmp);
+ tmp2[sizeof(tmp2)-1] = '\0';
+ }
+ else
+ build_path(tmp2, ps_global->ui.homedir, tmp, sizeof(tmp2));
+ }
+ else
+ (void) context_apply(tmp2, fs->context, tmp, sizeof(tmp2));
+
+ return(cpystr(tmp2));
+ }
+
+ return(cpystr(tmp));
+ }
+
+ return(cpystr(name));
+}
+
+
+/*
+ * Export a folder from pine space to user's space. It will still be a regular
+ * mail folder but it will be in the user's home directory or something like
+ * that.
+ */
+void
+folder_export(SCROLL_S *sparms)
+{
+ FOLDER_S *f;
+ MAILSTREAM *stream, *ourstream = NULL;
+ char expanded_file[MAILTMPLEN], *p,
+ tmp[MAILTMPLEN], *fname, *fullname = NULL,
+ filename[MAXPATH+1], full_filename[MAXPATH+1],
+ deefault[MAXPATH+1];
+ int open_inbox = 0, we_cancel = 0, width,
+ index = (sparms && sparms->text.handles)
+ ? sparms->text.handles->h.f.index : 0;
+ CONTEXT_S *savecntxt,
+ *cntxt = (sparms && sparms->text.handles)
+ ? sparms->text.handles->h.f.context : NULL;
+ static HISTORY_S *history = NULL;
+
+ dprint((4, "\n - folder export -\n"));
+
+ if(cntxt){
+ if(folder_total(FOLDERS(cntxt))){
+ f = folder_entry(index, FOLDERS(cntxt));
+ if((*FPROC(sparms)->fs->f.valid)(f, FPROC(sparms)->fs)){
+ savecntxt = FPROC(sparms)->fs->context; /* necessary? */
+ FPROC(sparms)->fs->context = cntxt;
+ strncpy(deefault, FLDR_NAME(f), sizeof(deefault)-1);
+ deefault[sizeof(deefault)-1] = '\0';
+ fname = folder_lister_fullname(FPROC(sparms)->fs, FLDR_NAME(f));
+ FPROC(sparms)->fs->context = savecntxt;
+
+ /*
+ * We have to allow for INBOX and nicknames in
+ * the incoming collection. Mimic what happens in
+ * do_broach_folder.
+ */
+ strncpy(expanded_file, fname, sizeof(expanded_file));
+ expanded_file[sizeof(expanded_file)-1] = '\0';
+
+ if(strucmp(fname, ps_global->inbox_name) == 0
+ || strucmp(fname, ps_global->VAR_INBOX_PATH) == 0)
+ open_inbox++;
+
+ if(!open_inbox && cntxt && context_isambig(fname)){
+ if((p=folder_is_nick(fname, FOLDERS(cntxt), 0)) != NULL){
+ strncpy(expanded_file, p, sizeof(expanded_file));
+ expanded_file[sizeof(expanded_file)-1] = '\0';
+ }
+ else if ((cntxt->use & CNTXT_INCMNG)
+ && (folder_index(fname, cntxt, FI_FOLDER) < 0)
+ && !is_absolute_path(fname)){
+ q_status_message1(SM_ORDER, 3, 4,
+ _("Can't find Incoming Folder %s."), fname);
+ return;
+ }
+ }
+
+ if(open_inbox)
+ fullname = ps_global->VAR_INBOX_PATH;
+ else{
+ /*
+ * We don't want to interpret this name in the context
+ * of the reference string, that was already done
+ * above in folder_lister_fullname(), just in the
+ * regular context. We also don't want to lose the
+ * reference string because we will still be in the
+ * subdirectory after this operation completes. So
+ * temporarily zero out the reference.
+ */
+ FDIR_S *tmpdir;
+
+ tmpdir = (cntxt ? cntxt->dir : NULL);
+ cntxt->dir = NULL;
+ fullname = context_apply(tmp, cntxt, expanded_file,
+ sizeof(tmp));
+ cntxt->dir = tmpdir;
+ }
+
+ width = MAX(20,
+ ps_global->ttyo ? ps_global->ttyo->screen_cols : 80);
+ stream = sp_stream_get(fullname, SP_MATCH | SP_RO_OK);
+ if(!stream && fullname){
+ /*
+ * Just using filename and full_filename as convenient
+ * temporary buffers here.
+ */
+ snprintf(filename, sizeof(filename), "Opening \"%s\"",
+ short_str(fullname, full_filename, sizeof(full_filename),
+ MAX(10, width-17),
+ MidDots));
+ filename[sizeof(filename)-1] = '\0';
+ we_cancel = busy_cue(filename, NULL, 0);
+ stream = pine_mail_open(NULL, fullname,
+ OP_READONLY|SP_USEPOOL|SP_TEMPUSE,
+ NULL);
+ if(we_cancel)
+ cancel_busy_cue(0);
+
+ ourstream = stream;
+ }
+
+ /*
+ * We have a stream for the folder we want to
+ * export.
+ */
+ if(stream && stream->nmsgs > 0L){
+ int r = 1;
+ static ESCKEY_S eopts[] = {
+ {ctrl('T'), 10, "^T", N_("To Files")},
+ {-1, 0, NULL, NULL},
+ {-1, 0, NULL, NULL}};
+
+ if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
+ eopts[r].ch = ctrl('I');
+ eopts[r].rval = 11;
+ eopts[r].name = "TAB";
+ /* TRANSLATORS: Complete is a verb, complete the name of a folder */
+ eopts[r].label = _("Complete");
+ }
+
+ eopts[++r].ch = -1;
+
+ filename[0] = '\0';
+ full_filename[0] = '\0';
+
+ r = get_export_filename(ps_global, filename, deefault,
+ full_filename,
+ sizeof(filename)-20, fname, NULL,
+ eopts, NULL,
+ -FOOTER_ROWS(ps_global),
+ GE_IS_EXPORT | GE_NO_APPEND, &history);
+ if(r < 0){
+ switch(r){
+ default:
+ case -1:
+ cmd_cancelled("Export folder");
+ break;
+
+ case -2:
+ q_status_message1(SM_ORDER, 0, 2,
+ _("Can't export to file outside of %s"),
+ ps_global->VAR_OPER_DIR);
+ break;
+ }
+ }
+ else{
+ int ok;
+ char *ff;
+
+ ps_global->mm_log_error = 0;
+
+ /*
+ * Do the creation in standard unix format, so it
+ * is readable by all.
+ */
+ rplstr(full_filename, sizeof(full_filename), 0, "#driver.unix/");
+ ok = pine_mail_create(NULL, full_filename) != 0L;
+ /*
+ * ff points to the original filename, without the
+ * driver prefix. Only mail_create knows how to
+ * handle driver prefixes.
+ */
+ ff = full_filename + strlen("#driver.unix/");
+
+ if(!ok){
+ if(!ps_global->mm_log_error)
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("Error creating file"));
+ }
+ else{
+ long l, snmsgs;
+ MSGNO_S *tmpmap = NULL;
+
+ snmsgs = stream->nmsgs;
+ mn_init(&tmpmap, snmsgs);
+ for(l = 1L; l <= snmsgs; l++)
+ if(l == 1L)
+ mn_set_cur(tmpmap, l);
+ else
+ mn_add_cur(tmpmap, l);
+
+ blank_keymenu(ps_global->ttyo->screen_rows-2, 0);
+ we_cancel = busy_cue(_("Copying folder"), NULL, 0);
+ l = save(ps_global, stream, NULL, ff, tmpmap, 0);
+ if(we_cancel)
+ cancel_busy_cue(0);
+
+ mn_give(&tmpmap);
+
+ if(l == snmsgs)
+ q_status_message2(SM_ORDER, 0, 3,
+ "Folder %s exported to %s",
+ fname, filename);
+ else{
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("Error exporting to %s"),
+ filename);
+ dprint((2,
+ "Error exporting to %s: expected %ld msgs, saved %ld\n",
+ filename, snmsgs, l));
+ }
+ }
+ }
+ }
+ else if(stream)
+ q_status_message1(SM_ORDER|SM_DING, 3, 3,
+ _("No messages in %s to export"), fname);
+ else
+ q_status_message(SM_ORDER|SM_DING, 3, 3,
+ _("Can't open folder for exporting"));
+
+ if(fname)
+ fs_give((void **) &fname);
+
+ if(ourstream)
+ pine_mail_close(ourstream);
+ }
+ }
+ }
+ else
+ q_status_message(SM_ORDER | SM_DING, 3, 3, _("Empty folder list!"));
+}
+
+
+/*
+ * Import a folder from user's space back to pine space.
+ * We're just importing a regular mail folder, and saving all the messages
+ * to another folder. It may seem more magical to the user but it isn't.
+ * The import folder is a local folder, the new one can be remote or whatever.
+ * Args sparms
+ * add_folder -- return new folder name here
+ * len -- length of add_folder
+ *
+ * Returns 1 if we may have to redraw screen, 0 otherwise
+ */
+int
+folder_import(SCROLL_S *sparms, char *add_folder, size_t len)
+{
+ MAILSTREAM *istream = NULL;
+ int r = 1, rv = 0;
+ char filename[MAXPATH+1], full_filename[MAXPATH+1];
+ static HISTORY_S *history = NULL;
+ static ESCKEY_S eopts[] = {
+ {ctrl('T'), 10, "^T", N_("To Files")},
+ {-1, 0, NULL, NULL},
+ {-1, 0, NULL, NULL}};
+
+ if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
+ eopts[r].ch = ctrl('I');
+ eopts[r].rval = 11;
+ eopts[r].name = "TAB";
+ eopts[r].label = N_("Complete");
+ }
+
+ eopts[++r].ch = -1;
+
+ filename[0] = '\0';
+ full_filename[0] = '\0';
+
+ /* get a folder to import */
+ r = get_export_filename(ps_global, filename, NULL, full_filename,
+ sizeof(filename)-20, "messages", "IMPORT",
+ eopts, NULL,
+ -FOOTER_ROWS(ps_global), GE_IS_IMPORT, &history);
+ if(r < 0){
+ switch(r){
+ default:
+ case -1:
+ cmd_cancelled("Import folder");
+ break;
+
+ case -2:
+ q_status_message1(SM_ORDER, 0, 2,
+ _("Can't import file outside of %s"),
+ ps_global->VAR_OPER_DIR);
+ break;
+ }
+ }
+ else{
+ ps_global->mm_log_error = 0;
+ if(full_filename && full_filename[0])
+ istream = pine_mail_open(NULL, full_filename,
+ OP_READONLY | SP_TEMPUSE, NULL);
+
+ if(istream && istream->nmsgs > 0L){
+ long l;
+ int we_cancel = 0;
+ char newfolder[MAILTMPLEN], nmsgs[32];
+ MSGNO_S *tmpmap = NULL;
+ CONTEXT_S *cntxt, *ourcntxt;
+
+ cntxt = (sparms && sparms->text.handles)
+ ? sparms->text.handles->h.f.context : NULL;
+ ourcntxt = cntxt;
+ newfolder[0] = '\0';
+ snprintf(nmsgs, sizeof(nmsgs), "%s msgs ", comatose(istream->nmsgs));
+ nmsgs[sizeof(nmsgs)-1] = '\0';
+
+ /*
+ * Select a folder to save the messages to.
+ */
+ if(save_prompt(ps_global, &cntxt, newfolder, sizeof(newfolder),
+ nmsgs, NULL, 0L, NULL, NULL, NULL)){
+
+ if((cntxt == ourcntxt) && newfolder[0]){
+ rv = 1;
+ strncpy(add_folder, newfolder, len-1);
+ add_folder[len-1] = '\0';
+ free_folder_list(cntxt);
+ }
+
+ mn_init(&tmpmap, istream->nmsgs);
+ for(l = 1; l <= istream->nmsgs; l++)
+ if(l == 1L)
+ mn_set_cur(tmpmap, l);
+ else
+ mn_add_cur(tmpmap, l);
+
+ blank_keymenu(ps_global->ttyo->screen_rows-2, 0);
+ we_cancel = busy_cue("Importing messages", NULL, 0);
+ l = save(ps_global, istream, cntxt, newfolder, tmpmap,
+ SV_INBOXWOCNTXT);
+ if(we_cancel)
+ cancel_busy_cue(0);
+
+ mn_give(&tmpmap);
+
+ if(l == istream->nmsgs)
+ q_status_message2(SM_ORDER, 0, 3,
+ "Folder %s imported to %s",
+ full_filename, newfolder);
+ else
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("Error importing to %s"),
+ newfolder);
+ }
+ }
+ else if(istream)
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("No messages in file %s"),
+ full_filename);
+ else if(!ps_global->mm_log_error)
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("Can't open file %s for import"), full_filename);
+ }
+
+ if(istream)
+ pine_mail_close(istream);
+
+ return(rv);
+}
+
+
+int
+folder_select_toggle(CONTEXT_S *context, int index, int (*func) (CONTEXT_S *, int))
+{
+ FOLDER_S *f;
+
+ if((f = folder_entry(index, FOLDERS(context))) != NULL){
+ f->selected = !f->selected;
+ return((*func)(context, index));
+ }
+ return 1;
+}
+
+
+/*
+ * Find the next '}' that isn't part of a "${"
+ * that appear for environment variables. We need
+ * this for configuration, because we want to edit
+ * the original pinerc entry, and not the digested
+ * value.
+ */
+char *
+end_bracket_no_nest(char *str)
+{
+ char *p;
+
+ for(p = str; *p; p++){
+ if(*p == '$' && *(p+1) == '{'){
+ while(*p && *p != '}')
+ p++;
+ if(!*p)
+ return NULL;
+ }
+ else if(*p == '}')
+ return p;
+ }
+ /* if we get here then there was no trailing '}' */
+ return NULL;
+}
+
+
+/*----------------------------------------------------------------------
+ Create a new folder
+
+ Args: context -- context we're adding in
+ which -- which config file to operate on
+ varnum -- which varnum to operate on
+ add_folder -- return new folder here
+ add_folderlen -- length of add_folder
+ possible_stream -- possible stream for re-use
+ def -- default value to start out with for add_folder
+ (for now, only for inbox-path editing)
+
+ Result: returns nonzero on successful add, 0 otherwise
+ ----*/
+int
+add_new_folder(CONTEXT_S *context, EditWhich which, int varnum, char *add_folder,
+ size_t add_folderlen, MAILSTREAM *possible_stream, char *def)
+{
+ char tmp[MAX(MAXFOLDER,6*MAX_SCREEN_COLS)+1], nickname[32],
+ *p = NULL, *return_val = NULL, buf[MAILTMPLEN],
+ buf2[MAILTMPLEN], def_in_prompt[MAILTMPLEN];
+ HelpType help;
+ PINERC_S *prc = NULL;
+ int i, rc, offset, exists, cnt = 0, isdir = 0;
+ int maildrop = 0, flags = 0, inbox = 0, require_a_subfolder = 0;
+ char *maildropfolder = NULL, *maildroplongname = NULL;
+ char *default_mail_drop_host = NULL,
+ *default_mail_drop_folder = NULL,
+ *default_dstn_host = NULL,
+ *default_dstn_folder = NULL,
+ *copydef = NULL,
+ *dstnmbox = NULL;
+ char mdmbox[MAILTMPLEN], ctmp[MAILTMPLEN];
+ MAILSTREAM *create_stream = NULL;
+ FOLDER_S *f;
+ static ESCKEY_S add_key[] = {{ctrl('X'),12,"^X", NULL},
+ {-1, 0, NULL, NULL}};
+
+ dprint((4, "\n - add_new_folder - \n"));
+
+ add_folder[0] = '\0';
+ nickname[0] = '\0';
+ inbox = (varnum == V_INBOX_PATH);
+
+ if(inbox || context->use & CNTXT_INCMNG){
+ char inbox_host[MAXPATH], *beg, *end = NULL;
+ int readonly = 0;
+ static ESCKEY_S host_key[4];
+
+ if(ps_global->restricted)
+ readonly = 1;
+ else{
+ switch(which){
+ case Main:
+ prc = ps_global->prc;
+ break;
+ case Post:
+ prc = ps_global->post_prc;
+ break;
+ case None:
+ break;
+ }
+
+ readonly = prc ? prc->readonly : 1;
+ }
+
+ if(prc && prc->quit_to_edit){
+ quit_to_edit_msg(prc);
+ return(FALSE);
+ }
+
+ if(readonly){
+ q_status_message(SM_ORDER,3,5,
+ _("Cancelled: config file not editable"));
+ return(FALSE);
+ }
+
+ /*
+ * When adding an Incoming folder we can't supply the inbox host
+ * as a default, because then the user has no way to type just
+ * a plain RETURN to mean "no host, I want a local folder".
+ * So we supply it as a ^X command instead. We could supply it
+ * as the initial value of the string...
+ *
+ * When editing inbox-path we will supply the default value as an
+ * initial value of the string which can be edited. They can edit it
+ * or erase it entirely to mean no host.
+ */
+
+ if(inbox && def){
+
+ copydef = cpystr(def);
+ (void) removing_double_quotes(copydef);
+
+ if(check_for_move_mbox(copydef, mdmbox, sizeof(mdmbox), &dstnmbox)){
+
+ /*
+ * Current inbox is using a mail drop. Get the default
+ * host value for the maildrop.
+ */
+
+ if(mdmbox
+ && (mdmbox[0] == '{'
+ || (mdmbox[0] == '*' && mdmbox[1] == '{'))
+ && (end = end_bracket_no_nest(mdmbox+1))
+ && end-mdmbox < add_folderlen){
+ *end = '\0';
+ if(mdmbox[0] == '{')
+ default_mail_drop_host = cpystr(mdmbox+1);
+ else
+ default_mail_drop_host = cpystr(mdmbox+2);
+ }
+
+ if(!default_mail_drop_host)
+ default_mail_drop_folder = cpystr(mdmbox);
+ else if(end && *(end+1))
+ default_mail_drop_folder = cpystr(end+1);
+
+ end = NULL;
+ if(dstnmbox
+ && (*dstnmbox == '{'
+ || (*dstnmbox == '*' && *++dstnmbox == '{'))
+ && (end = end_bracket_no_nest(dstnmbox+1))
+ && end-dstnmbox < add_folderlen){
+ *end = '\0';
+ default_dstn_host = cpystr(dstnmbox+1);
+ }
+
+ if(!default_dstn_host)
+ default_dstn_folder = cpystr(dstnmbox);
+ else if(end && *(end+1))
+ default_dstn_folder = cpystr(end+1);
+
+ maildrop++;
+ }
+ else{
+ end = NULL;
+ dstnmbox = copydef;
+ if(dstnmbox
+ && (*dstnmbox == '{'
+ || (*dstnmbox == '*' && *++dstnmbox == '{'))
+ && (end = end_bracket_no_nest(dstnmbox+1))
+ && end-dstnmbox < add_folderlen){
+ *end = '\0';
+ default_dstn_host = cpystr(dstnmbox+1);
+ }
+
+ if(!default_dstn_host)
+ default_dstn_folder = cpystr(dstnmbox);
+ else if(end && *(end+1))
+ default_dstn_folder = cpystr(end+1);
+ }
+
+ if(copydef)
+ fs_give((void **) &copydef);
+ }
+
+get_folder_name:
+
+ i = 0;
+ host_key[i].ch = 0;
+ host_key[i].rval = 0;
+ host_key[i].name = "";
+ host_key[i++].label = "";
+
+ inbox_host[0] = '\0';
+ if(!inbox && (beg = ps_global->VAR_INBOX_PATH)
+ && (*beg == '{' || (*beg == '*' && *++beg == '{'))
+ && (end = strindex(ps_global->VAR_INBOX_PATH, '}'))){
+ strncpy(inbox_host, beg+1, end - beg);
+ inbox_host[end - beg - 1] = '\0';
+ host_key[i].ch = ctrl('X');
+ host_key[i].rval = 12;
+ host_key[i].name = "^X";
+ host_key[i++].label = N_("Use Inbox Host");
+ }
+ else{
+ host_key[i].ch = 0;
+ host_key[i].rval = 0;
+ host_key[i].name = "";
+ host_key[i++].label = "";
+ }
+
+ if(!maildrop && !maildropfolder){
+ host_key[i].ch = ctrl('W');
+ host_key[i].rval = 13;
+ host_key[i].name = "^W";
+ /* TRANSLATORS: a mail drop is a place where mail is copied to so you
+ can read it. */
+ host_key[i++].label = N_("Use a Mail Drop");
+ }
+ else if(maildrop){
+ host_key[i].ch = ctrl('W');
+ host_key[i].rval = 13;
+ host_key[i].name = "^W";
+ host_key[i++].label = N_("Do Not use a Mail Drop");
+ }
+
+ host_key[i].ch = -1;
+ host_key[i].rval = 0;
+ host_key[i].name = NULL;
+ host_key[i].label = NULL;
+
+ if(maildrop)
+ snprintf(tmp, sizeof(tmp), _("Name of Mail Drop server : "));
+ else if(maildropfolder)
+ snprintf(tmp, sizeof(tmp), _("Name of server to contain destination folder : "));
+ else if(inbox)
+ snprintf(tmp, sizeof(tmp), _("Name of Inbox server : "));
+ else
+ snprintf(tmp, sizeof(tmp), _("Name of server to contain added folder : "));
+
+ tmp[sizeof(tmp)-1] = '\0';
+
+ help = NO_HELP;
+
+ /* set up defaults */
+ if(inbox && def){
+ flags = OE_APPEND_CURRENT;
+ if(maildrop && default_mail_drop_host){
+ strncpy(add_folder, default_mail_drop_host, add_folderlen);
+ add_folder[add_folderlen-1] = '\0';
+ }
+ else if(!maildrop && default_dstn_host){
+ strncpy(add_folder, default_dstn_host, add_folderlen);
+ add_folder[add_folderlen-1] = '\0';
+ }
+ else
+ add_folder[0] = '\0';
+ }
+ else{
+ flags = 0;
+ add_folder[0] = '\0';
+ }
+
+ while(1){
+ rc = optionally_enter(add_folder, -FOOTER_ROWS(ps_global), 0,
+ add_folderlen, tmp, host_key, help, &flags);
+ removing_leading_and_trailing_white_space(add_folder);
+
+ /*
+ * User went for the whole enchilada and entered a maildrop
+ * completely without going through the steps.
+ * Split it up as if they did and then skip over
+ * some of the code.
+ */
+ if(check_for_move_mbox(add_folder, mdmbox, sizeof(mdmbox),
+ &dstnmbox)){
+ maildrop = 1;
+ if(maildropfolder)
+ fs_give((void **) &maildropfolder);
+
+ maildropfolder = cpystr(mdmbox);
+
+ strncpy(add_folder, dstnmbox, add_folderlen);
+ add_folder[add_folderlen-1] = '\0';
+ offset = 0;
+ goto skip_over_folder_input;
+ }
+
+ /*
+ * Now check to see if they entered a whole c-client
+ * spec that isn't a mail drop.
+ */
+ if(add_folder[0] == '{'
+ && add_folder[1] != '\0'
+ && (p = srchstr(add_folder, "}"))
+ && *(p+1) != '\0'){
+ offset = p+1 - add_folder;
+ goto skip_over_folder_input;
+ }
+
+ /*
+ * And check to see if they entered "INBOX".
+ */
+ if(!strucmp(add_folder, ps_global->inbox_name)){
+ offset = 0;
+ goto skip_over_folder_input;
+ }
+
+ /* remove surrounding braces */
+ if(add_folder[0] == '{' && add_folder[1] != '\0'){
+ char *q;
+
+ q = add_folder + strlen(add_folder) - 1;
+ if(*q == '}'){
+ *q = '\0';
+ for(q = add_folder; *q; q++)
+ *q = *(q+1);
+ }
+ }
+
+ if(rc == 3){
+ if(maildropfolder && inbox)
+ helper(h_inbox_add_maildrop_destn,
+ _("HELP FOR DESTINATION SERVER "), HLPD_SIMPLE);
+ else if(maildropfolder && !inbox)
+ helper(h_incoming_add_maildrop_destn,
+ _("HELP FOR DESTINATION SERVER "), HLPD_SIMPLE);
+ else if(maildrop && inbox)
+ helper(h_inbox_add_maildrop, _("HELP FOR MAILDROP NAME "),
+ HLPD_SIMPLE);
+ else if(maildrop && !inbox)
+ helper(h_incoming_add_maildrop, _("HELP FOR MAILDROP NAME "),
+ HLPD_SIMPLE);
+ else if(inbox)
+ helper(h_incoming_add_inbox, _("HELP FOR INBOX SERVER "),
+ HLPD_SIMPLE);
+ else
+ helper(h_incoming_add_folder_host, _("HELP FOR SERVER NAME "),
+ HLPD_SIMPLE);
+
+ ps_global->mangled_screen = 1;
+ }
+ else if(rc == 12){
+ strncpy(add_folder, inbox_host, add_folderlen);
+ flags |= OE_APPEND_CURRENT;
+ }
+ else if(rc == 13){
+ if(maildrop){
+ maildrop = 0;
+ if(maildropfolder)
+ fs_give((void **) &maildropfolder);
+ }
+ else{
+ maildrop++;
+ if(inbox && def){
+ default_mail_drop_host = default_dstn_host;
+ default_dstn_host = NULL;
+ default_mail_drop_folder = default_dstn_folder;
+ default_dstn_folder = NULL;
+ }
+ }
+
+ goto get_folder_name;
+ }
+ else if(rc == 1){
+ q_status_message(SM_ORDER,0,2,
+ inbox ? _("INBOX change cancelled")
+ : _("Addition of new folder cancelled"));
+ if(maildropfolder)
+ fs_give((void **) &maildropfolder);
+
+ if(inbox && def){
+ if(default_mail_drop_host)
+ fs_give((void **) &default_mail_drop_host);
+
+ if(default_mail_drop_folder)
+ fs_give((void **) &default_mail_drop_folder);
+
+ if(default_dstn_host)
+ fs_give((void **) &default_dstn_host);
+
+ if(default_dstn_folder)
+ fs_give((void **) &default_dstn_folder);
+ }
+
+ return(FALSE);
+ }
+ else if(rc == 0)
+ break;
+ }
+ }
+
+ /* set up default folder, if any */
+ def_in_prompt[0] = '\0';
+ if(inbox && def){
+ if(maildrop && default_mail_drop_folder){
+ strncpy(def_in_prompt, default_mail_drop_folder,
+ sizeof(def_in_prompt));
+ def_in_prompt[sizeof(def_in_prompt)-1] = '\0';
+ }
+ else if(!maildrop && default_dstn_folder){
+ strncpy(def_in_prompt, default_dstn_folder,
+ sizeof(def_in_prompt));
+ def_in_prompt[sizeof(def_in_prompt)-1] = '\0';
+ }
+ }
+
+ if((offset = strlen(add_folder)) != 0){ /* must be host for incoming */
+ int i;
+ if(maildrop)
+ snprintf(tmp, sizeof(tmp),
+ "Maildrop folder on \"%s\" to copy mail from%s%s%s : ",
+ short_str(add_folder, buf, sizeof(buf), 15, EndDots),
+ def_in_prompt[0] ? " [" : "",
+ short_str(def_in_prompt, buf2, sizeof(buf2), 20, MidDots),
+ def_in_prompt[0] ? "]" : "");
+ else if(maildropfolder)
+ snprintf(tmp, sizeof(tmp),
+ "Folder on \"%s\" to copy mail to%s%s%s : ",
+ short_str(add_folder, buf, sizeof(buf), 20, EndDots),
+ def_in_prompt[0] ? " [" : "",
+ short_str(def_in_prompt, buf2, sizeof(buf2), 20, MidDots),
+ def_in_prompt[0] ? "]" : "");
+ else if(inbox)
+ snprintf(tmp, sizeof(tmp),
+ "Folder on \"%s\" to use for INBOX%s%s%s : ",
+ short_str(add_folder, buf, sizeof(buf), 20, EndDots),
+ def_in_prompt[0] ? " [" : "",
+ short_str(def_in_prompt, buf2, sizeof(buf2), 20, MidDots),
+ def_in_prompt[0] ? "]" : "");
+ else
+ snprintf(tmp, sizeof(tmp),
+ "Folder on \"%s\" to add%s%s%s : ",
+ short_str(add_folder, buf, sizeof(buf), 25, EndDots),
+ def_in_prompt[0] ? " [" : "",
+ short_str(def_in_prompt, buf2, sizeof(buf2), 20, MidDots),
+ def_in_prompt[0] ? "]" : "");
+
+ tmp[sizeof(tmp)-1] = '\0';
+
+ for(i = offset;i >= 0; i--)
+ add_folder[i+1] = add_folder[i];
+
+ add_folder[0] = '{';
+ add_folder[++offset] = '}';
+ add_folder[++offset] = '\0'; /* +2, total */
+ }
+ else{
+ if(maildrop)
+ snprintf(tmp, sizeof(tmp),
+ "Folder to copy mail from%s%s%s : ",
+ def_in_prompt[0] ? " [" : "",
+ short_str(def_in_prompt, buf2, sizeof(buf2), 20, MidDots),
+ def_in_prompt[0] ? "]" : "");
+ else if(maildropfolder)
+ snprintf(tmp, sizeof(tmp),
+ "Folder to copy mail to%s%s%s : ",
+ def_in_prompt[0] ? " [" : "",
+ short_str(def_in_prompt, buf2, sizeof(buf2), 20, MidDots),
+ def_in_prompt[0] ? "]" : "");
+ else if(inbox)
+ snprintf(tmp, sizeof(tmp),
+ "Folder name to use for INBOX%s%s%s : ",
+ def_in_prompt[0] ? " [" : "",
+ short_str(def_in_prompt, buf2, sizeof(buf2), 20, MidDots),
+ def_in_prompt[0] ? "]" : "");
+ else
+ snprintf(tmp, sizeof(tmp),
+ "Folder name to add%s%s%s : ",
+ def_in_prompt[0] ? " [" : "",
+ short_str(def_in_prompt, buf2, sizeof(buf2), 20, MidDots),
+ def_in_prompt[0] ? "]" : "");
+
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+
+ help = NO_HELP;
+ while(1){
+
+ p = NULL;
+ if(isdir){
+ add_key[0].label = N_("Create Folder");
+ if(tmp[0] == 'F')
+ rplstr(tmp, sizeof(tmp), 6, N_("Directory"));
+ }
+ else{
+ add_key[0].label = N_("Create Directory");
+ if(tmp[0] == 'D')
+ rplstr(tmp, sizeof(tmp), 9, N_("Folder"));
+ }
+
+ flags = OE_APPEND_CURRENT;
+ rc = optionally_enter(&add_folder[offset], -FOOTER_ROWS(ps_global), 0,
+ add_folderlen - offset, tmp,
+ ((context->dir->delim) && !maildrop)
+ ? add_key : NULL,
+ help, &flags);
+ /* use default */
+ if(rc == 0 && def_in_prompt[0] && !add_folder[offset]){
+ strncpy(&add_folder[offset], def_in_prompt, add_folderlen-offset);
+ add_folder[add_folderlen-1] = '\0';
+ }
+
+ removing_leading_and_trailing_white_space(&add_folder[offset]);
+
+ if(rc == 0 && !(inbox || context->use & CNTXT_INCMNG)
+ && check_for_move_mbox(add_folder, NULL, 0L, NULL)){
+ q_status_message(SM_ORDER, 6, 6,
+ _("#move folders may only be the INBOX or in the Incoming Collection"));
+ display_message(NO_OP_COMMAND);
+ continue;
+ }
+
+ if(rc == 0 && add_folder[offset]){
+ if(F_OFF(F_ENABLE_DOT_FOLDERS,ps_global)
+ && add_folder[offset] == '.'){
+ if(cnt++ <= 0)
+ q_status_message(SM_ORDER,3,3,
+ _("Folder name can't begin with dot"));
+ else
+ q_status_message1(SM_ORDER,3,3,
+ _("Config feature \"%s\" enables names beginning with dot"),
+ pretty_feature_name(feature_list_name(F_ENABLE_DOT_FOLDERS), -1));
+
+ display_message(NO_OP_COMMAND);
+ continue;
+ }
+
+ /* if last char is delim, blast from new folder */
+ for(p = &add_folder[offset]; *p && *(p + 1); p++)
+ ; /* find last char in folder */
+
+ if(isdir){
+ if(*p && *p != context->dir->delim){
+ *++p = context->dir->delim;
+ *(p+1) = '\0';
+ }
+
+ if(F_ON(F_QUELL_EMPTY_DIRS, ps_global))
+ require_a_subfolder++;
+ }
+ else if(*p == context->dir->delim){
+ q_status_message(SM_ORDER|SM_DING, 3, 3,
+ _("Can't have trailing directory delimiters!"));
+ display_message('X');
+ continue;
+ }
+
+ break;
+ }
+
+ if(rc == 12){
+ isdir = !isdir; /* toggle directory creation */
+ }
+ else if(rc == 3){
+ helper(h_incoming_add_folder_name, _("HELP FOR FOLDER NAME "),
+ HLPD_SIMPLE);
+ }
+ else if(rc == 1 || add_folder[0] == '\0') {
+ q_status_message(SM_ORDER,0,2,
+ inbox ? _("INBOX change cancelled")
+ : _("Addition of new folder cancelled"));
+ if(maildropfolder)
+ fs_give((void **) &maildropfolder);
+
+ if(inbox && def){
+ if(default_mail_drop_host)
+ fs_give((void **) &default_mail_drop_host);
+
+ if(default_mail_drop_folder)
+ fs_give((void **) &default_mail_drop_folder);
+
+ if(default_dstn_host)
+ fs_give((void **) &default_dstn_host);
+
+ if(default_dstn_folder)
+ fs_give((void **) &default_dstn_folder);
+ }
+
+ return(FALSE);
+ }
+ }
+
+ if(maildrop && !maildropfolder){
+ maildropfolder = cpystr(add_folder);
+ maildrop = 0;
+ goto get_folder_name;
+ }
+
+skip_over_folder_input:
+
+ if(require_a_subfolder){
+ /* add subfolder name to directory name */
+ offset = strlen(add_folder);
+ tmp[0] = '\0';
+
+ if(offset > 0){ /* it had better be */
+ char save_delim;
+
+ save_delim = add_folder[offset-1];
+ add_folder[offset-1] = '\0';
+
+ snprintf(tmp, sizeof(tmp),
+ "Name of subfolder to add in \"%s\" : ",
+ short_str(add_folder, buf, sizeof(buf), 15, FrontDots));
+
+ tmp[sizeof(tmp)-1] = '\0';
+ add_folder[offset-1] = save_delim;
+ }
+
+ while(1){
+ flags = OE_APPEND_CURRENT;
+ rc = optionally_enter(&add_folder[offset], -FOOTER_ROWS(ps_global), 0,
+ add_folderlen - offset, tmp,
+ NULL, NO_HELP, &flags);
+
+ removing_leading_and_trailing_white_space(&add_folder[offset]);
+
+ /* use default */
+ if(rc == 0 && !add_folder[offset]){
+ q_status_message(SM_ORDER, 4, 4,
+ _("A subfolder name is required, there is no default subfolder name"));
+ continue;
+ }
+
+ if(rc == 0 && add_folder[offset]){
+ break;
+ }
+
+ if(rc == 3){
+ helper(h_emptydir_subfolder_name, _("HELP FOR SUBFOLDER NAME "),
+ HLPD_SIMPLE);
+ }
+ else if(rc == 1 || add_folder[0] == '\0') {
+ q_status_message(SM_ORDER,0,2, _("Addition of new folder cancelled"));
+ return(FALSE);
+ }
+ }
+
+ /* the directory is implicit now */
+ isdir = 0;
+ }
+
+ if(context == ps_global->context_list
+ && !(context->dir && context->dir->ref)
+ && !strucmp(ps_global->inbox_name, add_folder)){
+ q_status_message1(SM_ORDER,3,3,
+ _("Cannot add folder %s in current context"),
+ add_folder);
+ return(FALSE);
+ }
+
+ create_stream = sp_stream_get(context_apply(ctmp, context, add_folder,
+ sizeof(ctmp)),
+ SP_SAME);
+
+ if(!create_stream && possible_stream)
+ create_stream = context_same_stream(context, add_folder, possible_stream);
+
+ help = NO_HELP;
+ if(!inbox && context->use & CNTXT_INCMNG){
+ snprintf(tmp, sizeof(tmp), _("Nickname for folder \"%s\" : "), &add_folder[offset]);
+ tmp[sizeof(tmp)-1] = '\0';
+ while(1){
+ int flags = OE_APPEND_CURRENT;
+
+ rc = optionally_enter(nickname, -FOOTER_ROWS(ps_global), 0,
+ sizeof(nickname), tmp, NULL, help, &flags);
+ removing_leading_and_trailing_white_space(nickname);
+ if(rc == 0){
+ if(strucmp(ps_global->inbox_name, nickname))
+ break;
+ else{
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("Nickname cannot be \"%s\""), nickname);
+ }
+ }
+
+ if(rc == 3){
+ help = help == NO_HELP
+ ? h_incoming_add_folder_nickname : NO_HELP;
+ }
+ else if(rc == 1 || (rc != 3 && !*nickname)){
+ q_status_message(SM_ORDER,0,2,
+ inbox ? _("INBOX change cancelled")
+ : _("Addition of new folder cancelled"));
+ if(maildropfolder)
+ fs_give((void **) &maildropfolder);
+
+ if(inbox && def){
+ if(default_mail_drop_host)
+ fs_give((void **) &default_mail_drop_host);
+
+ if(default_mail_drop_folder)
+ fs_give((void **) &default_mail_drop_folder);
+
+ if(default_dstn_host)
+ fs_give((void **) &default_dstn_host);
+
+ if(default_dstn_folder)
+ fs_give((void **) &default_dstn_folder);
+ }
+
+ return(FALSE);
+ }
+ }
+
+ /*
+ * Already exist? First, make sure this name won't collide with
+ * anything else in the list. Next, quickly test to see if it
+ * the actual mailbox exists so we know any errors from
+ * context_create() are really bad...
+ */
+ for(offset = 0; offset < folder_total(FOLDERS(context)); offset++){
+ f = folder_entry(offset, FOLDERS(context));
+ if(!strucmp(FLDR_NAME(f), nickname[0] ? nickname : add_folder)){
+ q_status_message1(SM_ORDER | SM_DING, 0, 3,
+ _("Incoming folder \"%s\" already exists"),
+ nickname[0] ? nickname : add_folder);
+ if(maildropfolder)
+ fs_give((void **) &maildropfolder);
+
+ if(inbox && def){
+ if(default_mail_drop_host)
+ fs_give((void **) &default_mail_drop_host);
+
+ if(default_mail_drop_folder)
+ fs_give((void **) &default_mail_drop_folder);
+
+ if(default_dstn_host)
+ fs_give((void **) &default_dstn_host);
+
+ if(default_dstn_folder)
+ fs_give((void **) &default_dstn_folder);
+ }
+
+ return(FALSE);
+ }
+ }
+
+ ps_global->c_client_error[0] = '\0';
+ exists = folder_exists(context, add_folder);
+ if(exists == FEX_ERROR){
+ if(ps_global->c_client_error[0] != '\0')
+ q_status_message1(SM_ORDER, 3, 3, "%s",
+ ps_global->c_client_error);
+ else
+ q_status_message1(SM_ORDER, 3, 3, _("Error checking for %s"), add_folder);
+ }
+ }
+ else if(!inbox)
+ exists = FEX_NOENT;
+ else{
+ exists = FEX_ISFILE;
+ /*
+ * If inbox is a maildropfolder, try to create the destination
+ * folder. But it shouldn't cause a fatal error.
+ */
+ if(maildropfolder && (folder_exists(NULL, add_folder) == FEX_NOENT))
+ context_create(NULL, NULL, add_folder);
+ }
+
+ if(exists == FEX_ERROR
+ || (exists == FEX_NOENT
+ && !context_create(context, create_stream, add_folder)
+ && !((context->use & CNTXT_INCMNG)
+ && !context_isambig(add_folder)))){
+ if(maildropfolder)
+ fs_give((void **) &maildropfolder);
+
+ if(inbox && def){
+ if(default_mail_drop_host)
+ fs_give((void **) &default_mail_drop_host);
+
+ if(default_mail_drop_folder)
+ fs_give((void **) &default_mail_drop_folder);
+
+ if(default_dstn_host)
+ fs_give((void **) &default_dstn_host);
+
+ if(default_dstn_folder)
+ fs_give((void **) &default_dstn_folder);
+ }
+
+ return(FALSE); /* c-client should've reported error */
+ }
+
+ if(isdir && p) /* whack off trailing delim */
+ *p = '\0';
+
+ if(inbox || context->use & CNTXT_INCMNG){
+ char **apval;
+ char ***alval;
+
+ if(inbox){
+ apval = APVAL(&ps_global->vars[varnum], which);
+ if(apval && *apval)
+ fs_give((void **) apval);
+ }
+ else{
+ alval = ALVAL(&ps_global->vars[varnum], which);
+ if(!*alval){
+ offset = 0;
+ *alval = (char **) fs_get(2*sizeof(char *));
+ }
+ else{
+ for(offset=0; (*alval)[offset]; offset++)
+ ;
+
+ fs_resize((void **) alval, (offset + 2) * sizeof(char *));
+ }
+ }
+
+ /*
+ * If we're using a Mail Drop we have to assemble the correct
+ * c-client string to do that. Mail drop syntax looks like
+ *
+ * #move <DELIM> <FromMailbox> <DELIM> <ToMailbox>
+ *
+ * DELIM is any character which does not appear in either of
+ * the mailbox names.
+ *
+ * And then the nickname is still in front of that mess.
+ */
+ if(maildropfolder){
+ char *delims = " +-_:!|ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ char *c;
+ size_t len;
+
+ len = 5 + 2 + strlen(maildropfolder) + strlen(add_folder);
+ maildroplongname = (char *) fs_get((len+1) * sizeof(char));
+
+ for(c = delims; *c; c++){
+ if(!strindex(maildropfolder, *c) &&
+ !strindex(add_folder, *c))
+ break;
+ }
+
+ if(*c){
+ snprintf(maildroplongname, len+1, "#move%c%s%c%s",
+ *c, maildropfolder, *c, add_folder);
+ if(strlen(maildroplongname) < add_folderlen){
+ strncpy(add_folder, maildroplongname, add_folderlen);
+ add_folder[add_folderlen-1] = '\0';
+ }
+ }
+ else{
+ q_status_message2(SM_ORDER,0,2,
+ "Can't find delimiter for \"#move %s %s\"",
+ maildropfolder, add_folder);
+ dprint((2,
+ "Can't find delimiter for \"#move %s %s\"",
+ maildropfolder ? maildropfolder : "?",
+ add_folder ? add_folder : "?"));
+
+ if(maildropfolder)
+ fs_give((void **) &maildropfolder);
+
+ if(inbox && def){
+ if(default_mail_drop_host)
+ fs_give((void **) &default_mail_drop_host);
+
+ if(default_mail_drop_folder)
+ fs_give((void **) &default_mail_drop_folder);
+
+ if(default_dstn_host)
+ fs_give((void **) &default_dstn_host);
+
+ if(default_dstn_folder)
+ fs_give((void **) &default_dstn_folder);
+ }
+
+ return(FALSE);
+ }
+
+ if(maildroplongname)
+ fs_give((void **) &maildroplongname);
+ }
+
+ if(inbox)
+ *apval = cpystr(add_folder);
+ else{
+ (*alval)[offset] = put_pair(nickname, add_folder);
+ (*alval)[offset+1] = NULL;
+ }
+
+ set_current_val(&ps_global->vars[varnum], TRUE, FALSE);
+ if(prc)
+ prc->outstanding_pinerc_changes = 1;
+
+ if(context->use & CNTXT_INCMNG){
+ if(!inbox && add_folder && add_folder[0] && alval && *alval && (*alval)[offset]){
+ /*
+ * Instead of re-initing we try to insert the
+ * added folder so that we preserve the last_unseen_update
+ * information.
+ */
+ f = new_folder(add_folder, line_hash((*alval)[offset]));
+ f->isfolder = 1;
+ if(nickname && nickname[0]){
+ f->nickname = cpystr(nickname);
+ f->name_len = strlen(f->nickname);
+ }
+
+ if(F_ON(F_ENABLE_INCOMING_CHECKING, ps_global)
+ && !ps_global->VAR_INCCHECKLIST)
+ f->last_unseen_update = LUU_INIT;
+ else
+ f->last_unseen_update = LUU_NEVERCHK;
+
+ folder_insert(folder_total(FOLDERS(context)), f, FOLDERS(context));
+ }
+ else
+ /* re-init to make sure we got it right */
+ reinit_incoming_folder_list(ps_global, context);
+ }
+
+ if(nickname[0]){
+ strncpy(add_folder, nickname, add_folderlen-1); /* known by new name */
+ add_folder[add_folderlen-1] = '\0';
+ }
+
+ if(!inbox)
+ q_status_message1(SM_ORDER, 0, 3, "Folder \"%s\" created",
+ add_folder);
+ return_val = add_folder;
+ }
+ else if(context_isambig(add_folder)){
+ free_folder_list(context);
+ q_status_message2(SM_ORDER, 0, 3, "%s \"%s\" created",
+ isdir ? "Directory" : "Folder", add_folder);
+ return_val = add_folder;
+ }
+ else
+ q_status_message1(SM_ORDER, 0, 3,
+ "Folder \"%s\" created outside current collection",
+ add_folder);
+
+ if(maildropfolder)
+ fs_give((void **) &maildropfolder);
+
+ if(inbox && def){
+ if(default_mail_drop_host)
+ fs_give((void **) &default_mail_drop_host);
+
+ if(default_mail_drop_folder)
+ fs_give((void **) &default_mail_drop_folder);
+
+ if(default_dstn_host)
+ fs_give((void **) &default_dstn_host);
+
+ if(default_dstn_folder)
+ fs_give((void **) &default_dstn_folder);
+ }
+
+ return(return_val != NULL);
+}
+
+
+/*----------------------------------------------------------------------
+ Subscribe to a news group
+
+ Args: folder -- last folder added
+ cntxt -- The context the subscription is for
+
+ Result: returns the name of the folder subscribed too
+
+
+This builds a complete context for the entire list of possible news groups.
+It also build a context to find the newly created news groups as
+determined by data kept in .pinerc. When the find of these new groups is
+done the subscribed context is searched and the items marked as new.
+A list of new board is never actually created.
+
+ ----*/
+int
+group_subscription(char *folder, size_t len, CONTEXT_S *cntxt)
+{
+ STRLIST_S *folders = NULL;
+ int rc = 0, last_rc, i, n, flags,
+ last_find_partial = 0, we_cancel = 0;
+ CONTEXT_S subscribe_cntxt;
+ HelpType help;
+ ESCKEY_S subscribe_keys[3];
+
+ subscribe_keys[i = 0].ch = ctrl('T');
+ subscribe_keys[i].rval = 12;
+ subscribe_keys[i].name = "^T";
+ subscribe_keys[i++].label = N_("To All Grps");
+
+ if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
+ subscribe_keys[i].ch = ctrl('I');
+ subscribe_keys[i].rval = 11;
+ subscribe_keys[i].name = "TAB";
+ subscribe_keys[i++].label = N_("Complete");
+ }
+
+ subscribe_keys[i].ch = -1;
+
+ /*---- Build a context to find all news groups -----*/
+
+ subscribe_cntxt = *cntxt;
+ subscribe_cntxt.use |= CNTXT_FINDALL;
+ subscribe_cntxt.use &= ~(CNTXT_PSEUDO | CNTXT_ZOOM | CNTXT_PRESRV);
+ subscribe_cntxt.next = NULL;
+ subscribe_cntxt.prev = NULL;
+ subscribe_cntxt.dir = new_fdir(cntxt->dir->ref,
+ cntxt->dir->view.internal, '*');
+ FOLDERS(&subscribe_cntxt) = init_folder_entries();
+ /*
+ * Prompt for group name.
+ */
+ folder[0] = '\0';
+ help = NO_HELP;
+ while(1){
+ flags = OE_APPEND_CURRENT;
+ last_rc = rc;
+ rc = optionally_enter(folder, -FOOTER_ROWS(ps_global), 0, len,
+ SUBSCRIBE_PMT, subscribe_keys, help, &flags);
+ removing_trailing_white_space(folder);
+ removing_leading_white_space(folder);
+ if((rc == 0 && folder[0]) || rc == 11 || rc == 12){
+ we_cancel = busy_cue(_("Fetching newsgroup list"), NULL, 1);
+
+ if(last_find_partial){
+ /* clean up any previous find results */
+ free_folder_list(&subscribe_cntxt);
+ last_find_partial = 0;
+ }
+
+ if(rc == 11){ /* Tab completion! */
+ if(folder_complete_internal(&subscribe_cntxt,
+ folder, len, &n, FC_FORCE_LIST)){
+ continue;
+ }
+ else{
+ if(!(n && last_rc == 11 && !(flags & OE_USER_MODIFIED))){
+ Writechar(BELL, 0);
+ continue;
+ }
+ }
+ }
+
+ if(rc == 12){ /* list the whole enchilada */
+ build_folder_list(NULL, &subscribe_cntxt, NULL, NULL,BFL_NONE);
+ }
+ else if(strlen(folder)){
+ char tmp[MAILTMPLEN];
+
+ snprintf(tmp, sizeof(tmp), "%s%.*s*", (rc == 11) ? "" : "*",
+ sizeof(tmp)-3, folder);
+ tmp[sizeof(tmp)-1] = '\0';
+ build_folder_list(NULL, &subscribe_cntxt, tmp, NULL, BFL_NONE);
+ subscribe_cntxt.dir->status &= ~(CNTXT_PARTFIND|CNTXT_NOFIND);
+ }
+ else{
+ q_status_message(SM_ORDER, 0, 2,
+ _("No group substring to match! Use ^T to list all news groups."));
+ continue;
+ }
+
+ /*
+ * If we did a partial find on matches, then we faked a full
+ * find which will cause this to just return.
+ */
+ if((i = folder_total(FOLDERS(&subscribe_cntxt))) != 0){
+ char *f;
+
+ /*
+ * fake that we've found everything there is to find...
+ */
+ subscribe_cntxt.use &= ~(CNTXT_NOFIND|CNTXT_PARTFIND);
+ last_find_partial = 1;
+
+ if(i == 1){
+ f = folder_entry(0, FOLDERS(&subscribe_cntxt))->name;
+ if(!strcmp(f, folder)){
+ rc = 1; /* success! */
+ break;
+ }
+ else{ /* else complete the group */
+ strncpy(folder, f, len-1);
+ folder[len-1] = '\0';
+ continue;
+ }
+ }
+ else if(!(flags & OE_USER_MODIFIED)){
+ /*
+ * See if there wasn't an exact match in the lot.
+ */
+ while(i-- > 0){
+ f = folder_entry(i,FOLDERS(&subscribe_cntxt))->name;
+ if(!strcmp(f, folder))
+ break;
+ else
+ f = NULL;
+ }
+
+ /* if so, then the user picked it from the list the
+ * last time and didn't change it at the prompt.
+ * Must mean they're accepting it...
+ */
+ if(f){
+ rc = 1; /* success! */
+ break;
+ }
+ }
+ }
+ else{
+ if(rc == 12)
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("No groups to select from!"));
+ else
+ q_status_message1(SM_ORDER, 3, 3,
+ _("News group \"%s\" didn't match any existing groups"),
+ folder);
+ free_folder_list(&subscribe_cntxt);
+
+ continue;
+ }
+
+ /*----- Mark groups that are currently subscribed too ------*/
+ /* but first make sure they're found */
+ build_folder_list(NULL, cntxt, "*", NULL, BFL_LSUB);
+ for(i = 0 ; i < folder_total(FOLDERS(&subscribe_cntxt)); i++) {
+ FOLDER_S *f = folder_entry(i, FOLDERS(&subscribe_cntxt));
+
+ f->subscribed = search_folder_list(FOLDERS(cntxt),
+ f->name) != 0;
+ }
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ /*----- Call the folder lister to do all the work -----*/
+ folders = folders_for_subscribe(ps_global,
+ &subscribe_cntxt, folder);
+
+ if(folders){
+ /* Multiple newsgroups OR Auto-select */
+ if(folders->next || F_ON(F_SELECT_WO_CONFIRM,ps_global))
+ break;
+
+ strncpy(folder, (char *) folders->name, len-1);
+ folder[len-1] = '\0';
+ free_strlist(&folders);
+ }
+ }
+ else if(rc == 3){
+ help = help == NO_HELP ? h_news_subscribe : NO_HELP;
+ }
+ else if(rc == 1 || folder[0] == '\0'){
+ rc = -1;
+ break;
+ }
+ }
+
+ if(rc < 0){
+ folder[0] = '\0'; /* make sure not to return partials */
+ if(rc == -1)
+ q_status_message(SM_ORDER, 0, 3, _("Subscribe cancelled"));
+ }
+ else{
+ MAILSTREAM *sub_stream;
+ int sclose = 0, errors = 0;
+
+ if(folders){ /*------ Actually do the subscription -----*/
+ STRLIST_S *flp;
+ int n = 0;
+
+ /* subscribe one at a time */
+ folder[0] = '\0';
+ /*
+ * Open stream before subscribing so c-client knows what newsrc
+ * to use, along with other side-effects.
+ */
+ if((sub_stream = mail_cmd_stream(&subscribe_cntxt, &sclose)) != NULL){
+ for(flp = folders; flp; flp = flp->next){
+ (void) context_apply(tmp_20k_buf, &subscribe_cntxt,
+ (char *) flp->name, SIZEOF_20KBUF);
+ if(mail_subscribe(sub_stream, tmp_20k_buf) == 0L){
+ /*
+ * This message may not make it to the screen,
+ * because a c-client message about the failure
+ * will be there. Probably best not to string
+ * together a whole bunch of errors if there is
+ * something wrong.
+ */
+ q_status_message1(errors ?SM_INFO : SM_ORDER,
+ errors ? 0 : 3, 3,
+ _("Error subscribing to \"%s\""),
+ (char *) flp->name);
+ errors++;
+ }
+ else{
+ n++;
+ if(!folder[0]){
+ strncpy(folder, (char *) flp->name, len-1);
+ folder[len-1] = '\0';
+ }
+
+ /*---- Update the screen display data structures -----*/
+ if(ALL_FOUND(cntxt)){
+ if(cntxt->use & CNTXT_PSEUDO){
+ folder_delete(0, FOLDERS(cntxt));
+ cntxt->use &= ~CNTXT_PSEUDO;
+ }
+
+ folder_insert(-1, new_folder((char *) flp->name, 0),
+ FOLDERS(cntxt));
+ }
+ }
+ }
+ if(sclose)
+ pine_mail_close(sub_stream);
+ }
+ else
+ errors++;
+
+ if(n == 0)
+ q_status_message(SM_ORDER | SM_DING, 3, 5,
+ _("Subscriptions failed, subscribed to no new groups"));
+ else
+ q_status_message3(SM_ORDER | (errors ? SM_DING : 0),
+ errors ? 3 : 0,3,
+ "Subscribed to %s new groups%s%s",
+ comatose((long)n),
+ errors ? ", failed on " : "",
+ errors ? comatose((long)errors) : "");
+
+ free_strlist(&folders);
+ }
+ else{
+ if((sub_stream = mail_cmd_stream(&subscribe_cntxt, &sclose)) != NULL){
+ (void) context_apply(tmp_20k_buf, &subscribe_cntxt, folder,
+ SIZEOF_20KBUF);
+ if(mail_subscribe(sub_stream, tmp_20k_buf) == 0L){
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("Error subscribing to \"%s\""), folder);
+ errors++;
+ }
+ else if(ALL_FOUND(cntxt)){
+ /*---- Update the screen display data structures -----*/
+ if(cntxt->use & CNTXT_PSEUDO){
+ folder_delete(0, FOLDERS(cntxt));
+ cntxt->use &= ~CNTXT_PSEUDO;
+ }
+
+ folder_insert(-1, new_folder(folder, 0), FOLDERS(cntxt));
+ }
+ if(sclose)
+ pine_mail_close(sub_stream);
+ }
+ else{
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("Error subscribing to \"%s\""), folder);
+ errors++;
+ }
+ }
+
+ if(!errors && folder[0])
+ q_status_message1(SM_ORDER, 0, 3, _("Subscribed to \"%s\""), folder);
+ }
+
+ free_fdir(&subscribe_cntxt.dir, 1);
+ return(folder[0]);
+}
+
+
+/*----------------------------------------------------------------------
+ Rename folder
+
+ Args: new_name -- buffer to contain new file name
+ len -- length of new_name buffer
+ index -- index of folder in folder list to rename
+ context -- collection of folders making up folder list
+ possible_stream -- may be able to use this stream
+
+ Result: returns the new name of the folder, or NULL if nothing happened.
+
+ When either the sent-mail or saved-message folders are renamed, immediately
+create a new one in their place so they always exist. The main loop above also
+detects this and makes the rename look like an add of the sent-mail or
+saved-messages folder. (This behavior may not be optimal, but it keeps things
+consistent.
+
+ ----*/
+int
+rename_folder(CONTEXT_S *context, int index, char *new_name, size_t len, MAILSTREAM *possible_stream)
+{
+ char *folder, prompt[64], *name_p = NULL;
+ HelpType help;
+ FOLDER_S *new_f;
+ PINERC_S *prc = NULL;
+ int rc, ren_cur, cnt = 0, isdir = 0, readonly = 0;
+ EditWhich ew;
+ MAILSTREAM *strm;
+
+ dprint((4, "\n - rename folder -\n"));
+
+ if(NEWS_TEST(context)){
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("Can't rename news groups!"));
+ return(0);
+ }
+ else if(!folder_total(FOLDERS(context))){
+ q_status_message(SM_ORDER | SM_DING, 0, 4,
+ _("Empty folder collection. No folder to rename!"));
+ return(0);
+ }
+ else if((new_f = folder_entry(index, FOLDERS(context)))
+ && context == ps_global->context_list
+ && !(context->dir && context->dir->ref)
+ && !strucmp(FLDR_NAME(new_f), ps_global->inbox_name)) {
+ q_status_message1(SM_ORDER | SM_DING, 3, 4,
+ _("Can't change special folder name \"%s\""),
+ ps_global->inbox_name);
+ return(0);
+ }
+
+ ew = config_containing_inc_fldr(new_f);
+ if(ps_global->restricted)
+ readonly = 1;
+ else{
+ switch(ew){
+ case Main:
+ prc = ps_global->prc;
+ break;
+ case Post:
+ prc = ps_global->post_prc;
+ break;
+ case None:
+ break;
+ }
+
+ readonly = prc ? prc->readonly : 1;
+ }
+
+ if(prc && prc->quit_to_edit && (context->use & CNTXT_INCMNG)){
+ quit_to_edit_msg(prc);
+ return(0);
+ }
+
+ if(readonly && (context->use & CNTXT_INCMNG)){
+ q_status_message(SM_ORDER,3,5,
+ _("Rename cancelled: folder not in editable config file"));
+ return(0);
+ }
+
+ if(context->use & CNTXT_INCMNG){
+ if(!(folder = new_f->nickname))
+ folder = ""; /* blank nickname */
+ }
+ else
+ folder = FLDR_NAME(new_f);
+
+ ren_cur = strcmp(folder, ps_global->cur_folder) == 0;
+
+ snprintf(prompt, sizeof(prompt), "%s %s to : ", _("Rename"),
+ (context->use & CNTXT_INCMNG)
+ ? _("nickname")
+ : (isdir = new_f->isdir)
+ ? _("directory") : _("folder"));
+ prompt[sizeof(prompt)-1] = '\0';
+ help = NO_HELP;
+ strncpy(new_name, folder, len-1);
+ new_name[len-1] = '\0';
+ while(1) {
+ int flags = OE_APPEND_CURRENT;
+
+ rc = optionally_enter(new_name, -FOOTER_ROWS(ps_global), 0,
+ len, prompt, NULL, help, &flags);
+ if(rc == 3) {
+ help = help == NO_HELP ? h_oe_foldrename : NO_HELP;
+ continue;
+ }
+
+ removing_leading_and_trailing_white_space(new_name);
+
+ if(rc == 0 && (*new_name || (context->use & CNTXT_INCMNG))) {
+ /* verify characters */
+ if(F_OFF(F_ENABLE_DOT_FOLDERS,ps_global) && *new_name == '.'){
+ if(cnt++ <= 0)
+ q_status_message(SM_ORDER,3,3,
+ _("Folder name can't begin with dot"));
+ else
+ q_status_message1(SM_ORDER,3,3,
+ _("Config feature \"s\" enables names beginning with dot"),
+ pretty_feature_name(feature_list_name(F_ENABLE_DOT_FOLDERS), -1));
+
+ display_message(NO_OP_COMMAND);
+ continue;
+ }
+
+ if(folder_index(new_name, context, FI_ANY|FI_RENAME) >= 0){
+ q_status_message1(SM_ORDER, 3, 3,
+ _("Folder \"%s\" already exists"),
+ pretty_fn(new_name));
+ display_message(NO_OP_COMMAND);
+ continue;
+ }
+ else if(context == ps_global->context_list
+ && !(context->dir && context->dir->ref)
+ && !strucmp(new_name, ps_global->inbox_name)){
+ if(context->use & CNTXT_INCMNG)
+ q_status_message1(SM_ORDER, 3, 3, _("Can't rename incoming folder to %s"),
+ new_name);
+ else
+ q_status_message1(SM_ORDER, 3, 3, _("Can't rename folder to %s"),
+ new_name);
+
+ display_message(NO_OP_COMMAND);
+ continue;
+ }
+ }
+
+ if(rc != 4) /* redraw */
+ break; /* no redraw */
+
+ }
+
+ if(rc != 1 && isdir){ /* add trailing delim? */
+ for(name_p = new_name; *name_p && *(name_p+1) ; name_p++)
+ ;
+
+ if(*name_p == context->dir->delim) /* lop off delim */
+ *name_p = '\0';
+ }
+
+ if(rc == 1
+ || !(*new_name || (context->use & CNTXT_INCMNG))
+ || !strcmp(new_name, folder)){
+ q_status_message(SM_ORDER, 0, 2, _("Folder rename cancelled"));
+ return(0);
+ }
+
+ if(context->use & CNTXT_INCMNG){
+ char **new_list, **lp, ***alval;
+ int i;
+
+ alval = ALVAL(&ps_global->vars[V_INCOMING_FOLDERS], ew);
+ for(i = 0; (*alval)[i]; i++)
+ ;
+
+ new_list = (char **) fs_get((i + 1) * sizeof(char *));
+
+ for(lp = new_list, i = 0; (*alval)[i]; i++){
+ /* figure out if this is the one we're renaming */
+ expand_variables(tmp_20k_buf, SIZEOF_20KBUF, (*alval)[i], 0);
+ if(new_f->varhash == line_hash(tmp_20k_buf)){
+ char *folder_string = NULL, *nickname = NULL;
+
+ if(new_f->nickname)
+ fs_give((void **) &new_f->nickname);
+
+ if(*new_name)
+ new_f->nickname = cpystr(new_name);
+
+ /*
+ * Parse folder line for nickname and folder name.
+ * No nickname on line is OK.
+ */
+ get_pair(tmp_20k_buf, &nickname, &folder_string, 0, 0);
+
+ if(nickname)
+ fs_give((void **)&nickname);
+
+ *lp = put_pair(new_name, folder_string);
+
+ new_f->varhash = line_hash(*lp++);
+ }
+ else
+ *lp++ = cpystr((*alval)[i]);
+ }
+
+ *lp = NULL;
+
+ set_variable_list(V_INCOMING_FOLDERS, new_list, TRUE, ew);
+ free_list_array(&new_list);
+
+ return(1);
+ }
+
+ /* Can't rename open streams */
+ if((strm = context_already_open_stream(context, folder, AOS_NONE))
+ || (ren_cur && (strm=ps_global->mail_stream))){
+ if(possible_stream == strm)
+ possible_stream = NULL;
+
+ pine_mail_actually_close(strm);
+ }
+
+ if(possible_stream
+ && !context_same_stream(context, new_name, possible_stream))
+ possible_stream = NULL;
+
+ if((rc = context_rename(context, possible_stream, folder, new_name)) != 0){
+ if(name_p && *name_p == context->dir->delim)
+ *name_p = '\0'; /* blat trailing delim */
+
+ /* insert new name? */
+ if(!strindex(new_name, context->dir->delim)){
+ new_f = new_folder(new_name, 0);
+ new_f->isdir = isdir;
+ folder_insert(-1, new_f, FOLDERS(context));
+ }
+
+ if(strcmp(ps_global->VAR_DEFAULT_FCC, folder) == 0
+ || strcmp(ps_global->VAR_DEFAULT_SAVE_FOLDER, folder) == 0) {
+ /* renaming sent-mail or saved-messages */
+ if(context_create(context, NULL, folder)){
+ q_status_message3(SM_ORDER,0,3,
+ "Folder \"%s\" renamed to \"%s\". New \"%s\" created",
+ folder, new_name,
+ pretty_fn(
+ (strcmp(ps_global->VAR_DEFAULT_SAVE_FOLDER,
+ folder) == 0)
+ ? ps_global->VAR_DEFAULT_SAVE_FOLDER
+ : ps_global->VAR_DEFAULT_FCC));
+
+ }
+ else{
+ q_status_message1(SM_ORDER | SM_DING, 3, 4,
+ "Error creating new \"%s\"", folder);
+
+ dprint((1, "Error creating \"%s\" in %s context\n",
+ folder ? folder : "?",
+ context->context ? context->context : "?"));
+ }
+ }
+ else
+ q_status_message2(SM_ORDER, 0, 3,
+ "Folder \"%s\" renamed to \"%s\"",
+ pretty_fn(folder), pretty_fn(new_name));
+
+ free_folder_list(context);
+ }
+
+ if(ren_cur) {
+ if(ps_global && ps_global->ttyo){
+ blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
+ ps_global->mangled_footer = 1;
+ }
+
+ /* No reopen the folder we just had open */
+ do_broach_folder(new_name, context, NULL, 0L);
+ }
+
+ return(rc);
+}
+
+
+/*----------------------------------------------------------------------
+ Confirm and delete a folder
+
+ Args: fs -- folder screen state
+
+ Result: return 0 if not delete, 1 if deleted.
+ ----*/
+int
+delete_folder(CONTEXT_S *context, int index, char *next_folder, size_t len, MAILSTREAM **possible_streamp)
+{
+ char *folder, ques_buf[MAX_SCREEN_COLS+1], *target = NULL,
+ *fnamep, buf[1000];
+ MAILSTREAM *del_stream = NULL, *sub_stream, *strm = NULL;
+ FOLDER_S *fp;
+ EditWhich ew;
+ PINERC_S *prc = NULL;
+ int ret, unsub_opened = 0, close_opened = 0, blast_folder = 1,
+ readonly;
+
+ if(!context){
+ cmd_cancelled("Missing context in Delete");
+ return(0);
+ }
+
+ if(NEWS_TEST(context)){
+ folder = folder_entry(index, FOLDERS(context))->name;
+ snprintf(ques_buf, sizeof(ques_buf), _("Really unsubscribe from \"%s\""), folder);
+ ques_buf[sizeof(ques_buf)-1] = '\0';
+
+ ret = want_to(ques_buf, 'n', 'x', NO_HELP, WT_NORM);
+ switch(ret) {
+ /* ^C */
+ case 'x':
+ Writechar(BELL, 0);
+ /* fall through */
+ case 'n':
+ return(0);
+ }
+
+ dprint((2, "deleting folder \"%s\" in context \"%s\"\n",
+ folder ? folder : "?",
+ context->context ? context->context : "?"));
+
+ if((sub_stream = mail_cmd_stream(context, &unsub_opened)) != NULL){
+ (void) context_apply(tmp_20k_buf, context, folder, SIZEOF_20KBUF);
+ if(!mail_unsubscribe(sub_stream, tmp_20k_buf)){
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("Error unsubscribing from \"%s\""),folder);
+ if(unsub_opened)
+ pine_mail_close(sub_stream);
+ return(0);
+ }
+ if(unsub_opened)
+ pine_mail_close(sub_stream);
+ }
+
+ /*
+ * Fix up the displayed list
+ */
+ folder_delete(index, FOLDERS(context));
+ return(1);
+ }
+
+ fp = folder_entry(index, FOLDERS(context));
+ if(!fp){
+ cmd_cancelled("Can't find folder to Delete!");
+ return(0);
+ }
+
+ if(!((context->use & CNTXT_INCMNG) && fp->name
+ && check_for_move_mbox(fp->name, NULL, 0, &target))){
+ target = NULL;
+ }
+
+ folder = FLDR_NAME(fp);
+ dprint((4, "=== delete_folder(%s) ===\n",
+ folder ? folder : "?"));
+
+ ew = config_containing_inc_fldr(fp);
+ if(ps_global->restricted)
+ readonly = 1;
+ else{
+ switch(ew){
+ case Main:
+ prc = ps_global->prc;
+ break;
+ case Post:
+ prc = ps_global->post_prc;
+ break;
+ case None:
+ break;
+ }
+
+ readonly = prc ? prc->readonly : 1;
+ }
+
+ if(prc && prc->quit_to_edit && (context->use & CNTXT_INCMNG)){
+ quit_to_edit_msg(prc);
+ return(0);
+ }
+
+ if(context == ps_global->context_list
+ && !(context->dir && context->dir->ref)
+ && strucmp(folder, ps_global->inbox_name) == 0){
+ q_status_message1(SM_ORDER | SM_DING, 3, 4,
+ _("Can't delete special folder \"%s\"."),
+ ps_global->inbox_name);
+ return(0);
+ }
+ else if(readonly && (context->use & CNTXT_INCMNG)){
+ q_status_message(SM_ORDER,3,5,
+ _("Deletion cancelled: folder not in editable config file"));
+ return(0);
+ }
+ else if((fp->name
+ && (strm=context_already_open_stream(context,fp->name,AOS_NONE)))
+ ||
+ (target
+ && (strm=context_already_open_stream(NULL,target,AOS_NONE)))){
+ if(strm == ps_global->mail_stream)
+ close_opened++;
+ }
+ else if(fp->isdir || fp->isdual){ /* NO DELETE if directory isn't EMPTY */
+ FDIR_S *fdirp = next_folder_dir(context,folder,TRUE,possible_streamp);
+
+ if(fp->haschildren)
+ ret = 1;
+ else if(fp->hasnochildren)
+ ret = 0;
+ else{
+ ret = folder_total(fdirp->folders) > 0;
+ free_fdir(&fdirp, 1);
+ }
+
+ if(ret){
+ q_status_message2(SM_ORDER | SM_DING, 3, 4,
+ _("Can't delete non-empty directory \"%s\"%s."),
+ folder, (fp->isfolder && fp->isdual) ? " (or folder of same name)" : "");
+ return(0);
+ }
+
+ /*
+ * Folder by the same name exist? If so, server's probably going
+ * to delete it as well. Punt?
+ */
+ if(fp->isdual
+ && (ret = want_to(DIR_FOLD_PMT,'n','x',NO_HELP,WT_NORM)) != 'y'){
+ q_status_message(SM_ORDER,0,3, (ret == 'x') ? _("Delete cancelled")
+ : _("No folder deleted"));
+ return(0);
+ }
+ }
+
+ if(context->use & CNTXT_INCMNG){
+ static ESCKEY_S delf_opts[] = {
+ {'n', 'n', "N", N_("Nickname only")},
+ {'b', 'b', "B", N_("Both Folder and Nickname")},
+ {-1, 0, NULL, NULL}
+ };
+#define DELF_PROMPT _("DELETE only Nickname or Both nickname and folder? ")
+
+ switch(radio_buttons(DELF_PROMPT, -FOOTER_ROWS(ps_global),
+ delf_opts,'n','x',NO_HELP,RB_NORM)){
+ case 'n' :
+ blast_folder = 0;
+ break;
+
+ case 'x' :
+ cmd_cancelled("Delete");
+ return(0);
+
+ default :
+ break;
+ }
+ }
+ else{
+ unsigned char *fname = folder_name_decoded((unsigned char *)folder);
+ snprintf(ques_buf, sizeof(ques_buf), "DELETE \"%s\"%s", fname ? (char *) fname : folder,
+ close_opened ? " (the currently open folder)" :
+ (fp->isdir && !(fp->isdual || fp->isfolder
+ || (folder_index(folder, context, FI_FOLDER) >= 0)))
+ ? " (a directory)" : "");
+ if(fname) fs_give((void **)&fname);
+ ques_buf[sizeof(ques_buf)-1] = '\0';
+
+ if((ret = want_to(ques_buf, 'n', 'x', NO_HELP, WT_NORM)) != 'y'){
+ q_status_message(SM_ORDER,0,3, (ret == 'x') ? _("Delete cancelled")
+ : _("Nothing deleted"));
+ return(0);
+ }
+ }
+
+ if(blast_folder){
+ dprint((2,"deleting \"%s\" (%s) in context \"%s\"\n",
+ fp->name ? fp->name : "?",
+ fp->nickname ? fp->nickname : "",
+ context->context ? context->context : "?"));
+ if(strm){
+ /*
+ * Close it, NULL the pointer, and let do_broach_folder fixup
+ * the rest...
+ */
+ pine_mail_actually_close(strm);
+ if(close_opened){
+ if(ps_global && ps_global->ttyo){
+ blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
+ ps_global->mangled_footer = 1;
+ }
+
+ ps_global->mangled_header = 1;
+ do_broach_folder(ps_global->inbox_name,
+ ps_global->context_list, NULL, DB_INBOXWOCNTXT);
+ }
+ }
+
+ /*
+ * Use fp->name since "folder" may be a nickname...
+ */
+ if(possible_streamp && *possible_streamp
+ && context_same_stream(context, fp->name, *possible_streamp))
+ del_stream = *possible_streamp;
+
+ fnamep = fp->name;
+
+ if(!context_delete(context, del_stream, fnamep)){
+/*
+ * BUG: what if sent-mail or saved-messages????
+ */
+ q_status_message1(SM_ORDER,3,3,"Delete of \"%s\" Failed!", folder);
+ return(0);
+ }
+ }
+
+ snprintf(buf, sizeof(buf), "%s\"%s\" deleted.",
+ !blast_folder ? "Nickname " :
+ fp->isdual ? "Folder/Directory " :
+ (fp->isdir && fp->isfolder) ? "Folder " :
+ fp->isdir ? "Directory " :
+ "Folder ",
+ folder);
+ buf[sizeof(buf)-1] = '\0';
+
+ q_status_message(SM_ORDER, 0, 3, buf);
+
+ if(context->use & CNTXT_INCMNG){
+ char **new_list, **lp, ***alval;
+ int i;
+
+ alval = ALVAL(&ps_global->vars[V_INCOMING_FOLDERS], ew);
+ for(i = 0; (*alval)[i]; i++)
+ ;
+
+ /*
+ * Make it one too big in case we don't find the match for
+ * some unknown reason.
+ */
+ new_list = (char **) fs_get((i + 1) * sizeof(char *));
+
+ /*
+ * Copy while figuring out which one to skip.
+ */
+ for(lp = new_list, i = 0; (*alval)[i]; i++){
+ expand_variables(tmp_20k_buf, SIZEOF_20KBUF, (*alval)[i], 0);
+ if(fp->varhash != line_hash(tmp_20k_buf))
+ *lp++ = cpystr((*alval)[i]);
+ }
+
+ *lp = NULL;
+
+ set_variable_list(V_INCOMING_FOLDERS, new_list, TRUE, ew);
+ free_list_array(&new_list);
+ }
+
+ /*
+ * Fix up the displayed list.
+ */
+ folder_delete(index, FOLDERS(context));
+
+ /*
+ * Take a guess at what should get hilited next.
+ */
+ if(index < (ret = folder_total(FOLDERS(context)))
+ || ((index = ret - 1) >= 0)){
+ if((fp = folder_entry(index, FOLDERS(context)))
+ && strlen(FLDR_NAME(fp)) < len - 1)
+ strncpy(next_folder, FLDR_NAME(fp), len-1);
+ next_folder[len-1] = '\0';
+ }
+
+ if(!(context->use & CNTXT_INCMNG)){
+ /*
+ * Then cause the list to get rebuild 'cause we may've blasted
+ * some folder that's also a directory or vice versa...
+ */
+ free_folder_list(context);
+ }
+
+ return(1);
+}
+
+
+/*----------------------------------------------------------------------
+ Print the list of folders on paper
+
+ Args: list -- The current list of folders
+ lens -- The list of lengths of the current folders
+ display -- The current folder display structure
+
+ Result: list printed on paper
+
+If the display list was created for 80 columns it is used, otherwise
+a new list is created for 80 columns
+
+ ----*/
+void
+print_folders(FPROC_S *fp)
+{
+ if(open_printer(_("folder list")) == 0){
+ (void) folder_list_text(ps_global, fp, print_char, NULL, 80);
+
+ close_printer();
+ }
+}
+
+
+int
+scan_get_pattern(char *kind, char *pat, int len)
+{
+ char prompt[256];
+ int flags;
+
+ pat[0] = '\0';
+ snprintf(prompt, sizeof(prompt), _("String in folder %s to match : "), kind);
+ prompt[sizeof(prompt)-1] = '\0';
+
+ while(1){
+ flags = OE_APPEND_CURRENT | OE_DISALLOW_HELP;
+ switch(optionally_enter(pat, -FOOTER_ROWS(ps_global), 0, len,
+ prompt, NULL, NO_HELP, &flags)){
+
+ case 4 :
+ if(ps_global->redrawer)
+ (*ps_global->redrawer)();
+
+ break;
+
+ case 0 :
+ if(*pat)
+ return(1);
+
+ case 1 :
+ cmd_cancelled("Select");
+
+ default :
+ return(0);
+ }
+ }
+}
+
+
+int
+folder_select_text(struct pine *ps, CONTEXT_S *context, int selected)
+{
+ char pattern[MAILTMPLEN], type = '\0';
+ static ESCKEY_S scan_opts[] = {
+ {'n', 'n', "N", N_("Name Select")},
+ {'c', 'c', "C", N_("Content Select")},
+ {-1, 0, NULL, NULL}
+ };
+
+ if(context->use & CNTXT_INCMNG){
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("Select \"Text\" not supported in Incoming Folders"));
+ return(0);
+ }
+
+ switch(radio_buttons(SEL_TEXT_PMT, -FOOTER_ROWS(ps_global),
+ scan_opts, 'n', 'x', NO_HELP, RB_NORM)){
+ case 'n' : /* Name search */
+ if(scan_get_pattern("NAME", pattern, sizeof(pattern)))
+ type = 'n';
+
+ break;
+
+ case 'c' : /* content search */
+ if(scan_get_pattern("CONTENTS", pattern, sizeof(pattern)))
+ type = 'c';
+
+ break;
+
+ case 'x' :
+ default :
+ break;
+ }
+
+ if(type){
+ int rv;
+ char tmp[MAILTMPLEN];
+ SCANARG_S args;
+
+ memset(&args, 0, sizeof(SCANARG_S));
+ args.pattern = pattern;
+ args.type = type;
+ args.context = context;
+
+ if(type == 'c'){
+ args.stream = sp_stream_get(context_apply(tmp, context,
+ "xxx", sizeof(tmp)),
+ SP_SAME);
+ if(!args.stream){
+ args.stream = pine_mail_open(NULL, tmp,
+ OP_SILENT|OP_HALFOPEN|SP_USEPOOL|SP_TEMPUSE,
+ NULL);
+ args.newstream = (args.stream != NULL);
+ }
+ }
+
+ rv = foreach_folder(context, selected,
+ foreach_do_scan, (void *) &args);
+
+ if(args.newstream)
+ pine_mail_close(args.stream);
+
+ if(rv)
+ return(1);
+ }
+
+ cmd_cancelled("Select");
+ return(0);
+}
+
+
+int
+foreach_do_scan(FOLDER_S *f, void *d)
+{
+ SCANARG_S *sa = (SCANARG_S *) d;
+
+ return((sa->type == 'n' && srchstr(FLDR_NAME(f), sa->pattern))
+ || (sa->type == 'c'
+ && scan_scan_folder(sa->stream, sa->context, f, sa->pattern)));
+}
+
+
+int
+scan_scan_folder(MAILSTREAM *stream, CONTEXT_S *context, FOLDER_S *f, char *pattern)
+{
+ MM_LIST_S ldata;
+ LISTRES_S response;
+ int we_cancel = 0;
+ char *folder, *ref = NULL, tmp[MAILTMPLEN];
+
+ folder = f->name;
+ snprintf(tmp, sizeof(tmp), "Scanning \"%s\"", FLDR_NAME(f));
+ tmp[sizeof(tmp)-1] = '\0';
+ we_cancel = busy_cue(tmp, NULL, 1);
+
+ mm_list_info = &ldata; /* tie down global reference */
+ memset(&ldata, 0, sizeof(MM_LIST_S));
+ ldata.filter = mail_list_response;
+ memset(ldata.data = &response, 0, sizeof(LISTRES_S));
+
+ /*
+ * If no preset reference string, must be at top of context
+ */
+ if(context && context_isambig(folder) && !(ref = context->dir->ref)){
+ char *p;
+
+ if((p = strstr(context->context, "%s")) != NULL){
+ if(!*(p+2)){
+ snprintf(tmp, sizeof(tmp), "%.*s", MIN(p - context->context, sizeof(tmp)-1),
+ context->context);
+ tmp[sizeof(tmp)-1] = '\0';
+ ref = tmp;
+ }
+ else{
+ snprintf(tmp, sizeof(tmp), context->context, folder);
+ tmp[sizeof(tmp)-1] = '\0';
+ folder = tmp;
+ ref = "";
+ }
+ }
+ else
+ ref = context->context;
+ }
+
+ mail_scan(stream, ref, folder, pattern);
+
+ if(context && context->dir && response.delim)
+ context->dir->delim = response.delim;
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ return(((response.isfile) ? FEX_ISFILE : 0)
+ | ((response.isdir) ? FEX_ISDIR : 0));
+}
+
+
+int
+folder_select_props(struct pine *ps, CONTEXT_S *context, int selected)
+{
+ int cmp = 0;
+ long flags = 0L, count;
+ static ESCKEY_S prop_opts[] = {
+ {'u', 'u', "U", N_("Unseen msgs")},
+ {'n', 'n', "N", N_("New msgs")},
+ {'c', 'c', "C", N_("msg Count")},
+ {-1, 0, NULL, NULL}
+ };
+
+ if(context->use & CNTXT_INCMNG){
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("Select \"Properties\" not supported in Incoming Folders"));
+ return(0);
+ }
+
+ switch(radio_buttons(SEL_PROP_PMT, -FOOTER_ROWS(ps_global),
+ prop_opts, 'n', 'x', h_folder_prop, RB_NORM)){
+ case 'c' : /* message count */
+ if(folder_select_count(&count, &cmp))
+ flags = SA_MESSAGES;
+
+ break;
+
+ case 'n' : /* folders with new */
+ flags = SA_RECENT;
+ break;
+
+ case 'u' : /* folders with unseen */
+ flags = SA_UNSEEN;
+ break;
+
+ case 'x' :
+ default :
+ break;
+ }
+
+ if(flags){
+ int rv;
+ char tmp[MAILTMPLEN];
+ STATARG_S args;
+
+ memset(&args, 0, sizeof(STATARG_S));
+ args.flags = flags;
+ args.context = context;
+ args.nmsgs = count;
+ args.cmp = cmp;
+
+ args.stream = sp_stream_get(context_apply(tmp, context,
+ "xxx", sizeof(tmp)),
+ SP_SAME);
+ if(!args.stream){
+ args.stream = pine_mail_open(NULL, tmp,
+ OP_SILENT|OP_HALFOPEN|SP_USEPOOL|SP_TEMPUSE,
+ NULL);
+ args.newstream = (args.stream != NULL);
+ }
+
+ rv = foreach_folder(context, selected,
+ foreach_do_stat, (void *) &args);
+
+ if(args.newstream)
+ pine_mail_close(args.stream);
+
+ if(rv)
+ return(1);
+ }
+
+ cmd_cancelled("Select");
+ return(0);
+}
+
+
+int
+folder_select_count(long int *count, int *cmp)
+{
+ int r, flags;
+ char number[32], prompt[128];
+ static char *tense[] = {"EQUAL TO", "LESS THAN", "GREATER THAN"};
+ static ESCKEY_S sel_num_opt[] = {
+ {ctrl('W'), 14, "^W", N_("Toggle Comparison")},
+ {-1, 0, NULL, NULL}
+ };
+
+ *count = 0L;
+ while(1){
+ flags = OE_APPEND_CURRENT | OE_DISALLOW_HELP;
+ snprintf(number, sizeof(number), "%ld", *count);
+ number[sizeof(number)-1] = '\0';
+ snprintf(prompt, sizeof(prompt), "Select folders with messages %s : ", tense[*cmp]);
+ prompt[sizeof(prompt)-1] = '\0';
+ r = optionally_enter(number, -FOOTER_ROWS(ps_global), 0, sizeof(number),
+ prompt, sel_num_opt, NO_HELP, &flags);
+ switch (r){
+ case 0 :
+ if(!*number)
+ break;
+ else if((*count = atol(number)) < 0L)
+ q_status_message(SM_ORDER, 3, 3,
+ "Can't have NEGATIVE message count!");
+ else
+ return(1); /* success */
+
+ case 3 : /* help */
+ case 4 : /* redraw */
+ continue;
+
+ case 14 : /* toggle comparison */
+ *cmp = ++(*cmp) % 3;
+ continue;
+
+ case -1 : /* cancel */
+ default:
+ break;
+ }
+
+ break;
+ }
+
+ return(0); /* return failure */
+}
+
+
+int
+foreach_do_stat(FOLDER_S *f, void *d)
+{
+ STATARG_S *sa = (STATARG_S *) d;
+ int rv = 0;
+
+ if(ps_global->context_current == sa->context
+ && !strcmp(ps_global->cur_folder, FLDR_NAME(f))){
+ switch(sa->flags){
+ case SA_MESSAGES :
+ switch(sa->cmp){
+ case 0 : /* equals */
+ if(ps_global->mail_stream->nmsgs == sa->nmsgs)
+ rv = 1;
+
+ break;
+
+ case 1 : /* less than */
+ if(ps_global->mail_stream->nmsgs < sa->nmsgs)
+ rv = 1;
+
+ break;
+
+ case 2 :
+ if(ps_global->mail_stream->nmsgs > sa->nmsgs)
+ rv = 1;
+
+ break;
+
+ default :
+ break;
+ }
+
+ break;
+
+ case SA_RECENT :
+ if(count_flagged(ps_global->mail_stream, F_RECENT))
+ rv = 1;
+
+ break;
+
+ case SA_UNSEEN :
+ if(count_flagged(ps_global->mail_stream, F_UNSEEN))
+ rv = 1;
+
+ break;
+
+ default :
+ break;
+ }
+ }
+ else{
+ int we_cancel = 0;
+ char msg_buf[MAX_BM+1];
+ extern MAILSTATUS mm_status_result;
+
+ snprintf(msg_buf, sizeof(msg_buf), "Checking %s for %s", FLDR_NAME(f),
+ (sa->flags == SA_UNSEEN)
+ ? "unseen messages"
+ : (sa->flags == SA_MESSAGES) ? "message count"
+ : "recent messages");
+ msg_buf[sizeof(msg_buf)-1] = '\0';
+ we_cancel = busy_cue(msg_buf, NULL, 0);
+
+ if(!context_status(sa->context, sa->stream, f->name, sa->flags))
+ mm_status_result.flags = 0L;
+
+ if(we_cancel)
+ cancel_busy_cue(0);
+
+ if(sa->flags & mm_status_result.flags)
+ switch(sa->flags){
+ case SA_MESSAGES :
+ switch(sa->cmp){
+ case 0 : /* equals */
+ if(mm_status_result.messages == sa->nmsgs)
+ rv = 1;
+
+ break;
+
+ case 1 : /* less than */
+ if(mm_status_result.messages < sa->nmsgs)
+ rv = 1;
+
+ break;
+
+ case 2 :
+ if(mm_status_result.messages > sa->nmsgs)
+ rv = 1;
+
+ break;
+
+ default :
+ break;
+ }
+
+ break;
+
+ case SA_RECENT :
+ if(mm_status_result.recent)
+ rv = 1;
+
+ break;
+
+ case SA_UNSEEN :
+ if(mm_status_result.unseen)
+ rv = 1;
+
+ break;
+
+ default :
+ break;
+ }
+ }
+
+ return(rv);
+}
+
+
+int
+foreach_folder(CONTEXT_S *context, int selected, int (*test) (FOLDER_S *, void *), void *args)
+{
+ int i, n, rv = 1;
+ int we_turned_on = 0;
+ FOLDER_S *fp;
+
+ we_turned_on = intr_handling_on();
+
+ for(i = 0, n = folder_total(FOLDERS(context)); i < n; i++){
+ if(ps_global->intr_pending){
+ for(; i >= 0; i--)
+ folder_entry(i, FOLDERS(context))->scanned = 0;
+
+ cmd_cancelled("Select");
+ rv = 0;
+ break;
+ }
+
+ fp = folder_entry(i, FOLDERS(context));
+ fp->scanned = 0;
+ if((!selected || fp->selected) && fp->isfolder && (*test)(fp, args))
+ fp->scanned = 1;
+ }
+
+ if(we_turned_on)
+ intr_handling_off();
+
+ return(rv);
+}
+
+
+/*----------------------------------------------------------------------
+ Return the path delimiter for the given folder on the given server
+
+ Args: folder -- folder type for delimiter
+
+ ----*/
+int
+folder_delimiter(char *folder)
+{
+ int rv, we_cancel = 0;
+
+ we_cancel = busy_cue(NULL, NULL, 1);
+
+ rv = get_folder_delimiter(folder);
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ return(rv);
+}
+
+
+/*
+ * next_folder - given a current folder in a context, return the next in
+ * the list, or NULL if no more or there's a problem.
+ *
+ * Args streamp -- If set, try to re-use this stream for checking.
+ * next -- Put return value here, return points to this
+ * nextlen -- Length of next
+ * current -- Current folder, so we know where to start looking
+ * cntxt --
+ * find_recent -- Returns the number of recent here. The presence of
+ * this arg also indicates that we should do the calls
+ * to figure out whether there is a next interesting folder
+ * or not.
+ * did_cancel -- Tell caller if user canceled. Only used if find_recent
+ * is also set. Also, user will only be given the
+ * opportunity to cancel if this is set. If it isn't
+ * set, we just plow ahead when there is an error or
+ * when the folder does not exist.
+ */
+char *
+next_folder(MAILSTREAM **streamp, char *next, size_t nextlen, char *current, CONTEXT_S *cntxt, long int *find_recent, int *did_cancel)
+{
+ int index, recent = 0, failed_status = 0, try_fast;
+ char prompt[128];
+ FOLDER_S *f = NULL;
+ char tmp[MAILTMPLEN];
+
+
+ /* note: find_folders may assign "stream" */
+ build_folder_list(streamp, cntxt, NULL, NULL,
+ NEWS_TEST(cntxt) ? BFL_LSUB : BFL_NONE);
+
+ try_fast = (F_ON(F_ENABLE_FAST_RECENT, ps_global) &&
+ F_OFF(F_TAB_USES_UNSEEN, ps_global));
+ if(find_recent)
+ *find_recent = 0L;
+
+ for(index = folder_index(current, cntxt, FI_FOLDER) + 1;
+ index > 0
+ && index < folder_total(FOLDERS(cntxt))
+ && (f = folder_entry(index, FOLDERS(cntxt)))
+ && !f->isdir;
+ index++)
+ if(find_recent){
+ MAILSTREAM *stream = NULL;
+ int rv, we_cancel = 0, match;
+ char msg_buf[MAX_BM+1];
+
+ /* must be a folder and it can't be the current one */
+ if(ps_global->context_current == ps_global->context_list
+ && !strcmp(ps_global->cur_folder, FLDR_NAME(f)))
+ continue;
+
+ /*
+ * If we already have the folder open, short circuit all this
+ * stuff.
+ */
+ match = 0;
+ if((stream = sp_stream_get(context_apply(tmp, cntxt, f->name,
+ sizeof(tmp)),
+ SP_MATCH)) != NULL
+ || (!IS_REMOTE(tmp) && (stream = already_open_stream(tmp, AOS_NONE)) != NULL)){
+ (void) pine_mail_ping(stream);
+
+ if(F_ON(F_TAB_USES_UNSEEN, ps_global)){
+ /*
+ * Just fall through and let the status call below handle
+ * the already open stream. If we were doing this the
+ * same as the else case, we would figure out how many
+ * unseen are in this open stream by doing a search.
+ * Instead of repeating that code that is already in
+ * pine_mail_status_full, fall through and note the
+ * special case by lighting the match variable.
+ */
+ match++;
+ }
+ else{
+ *find_recent = sp_recent_since_visited(stream);
+ if(*find_recent){
+ recent++;
+ break;
+ }
+
+ continue;
+ }
+ }
+
+ snprintf(msg_buf, sizeof(msg_buf), "Checking %s for %s messages",
+ FLDR_NAME(f), F_ON(F_TAB_USES_UNSEEN, ps_global) ? "unseen" : "recent");
+ msg_buf[sizeof(msg_buf)-1] = '\0';
+ we_cancel = busy_cue(msg_buf, NULL, 0);
+
+ /* First, get a stream for the test */
+ if(!stream && streamp && *streamp){
+ if(context_same_stream(cntxt, f->name, *streamp)){
+ stream = *streamp;
+ }
+ else{
+ pine_mail_close(*streamp);
+ *streamp = NULL;
+ }
+ }
+
+ if(!stream){
+ stream = sp_stream_get(context_apply(tmp, cntxt, f->name,
+ sizeof(tmp)),
+ SP_SAME);
+ }
+
+ /*
+ * If interestingness is indeterminate or we're
+ * told to explicitly, look harder...
+ */
+
+ /*
+ * We could make this more efficient in the cases where we're
+ * opening a new stream or using streamp by having folder_exists
+ * cache the stream. The change would require a folder_exists()
+ * that caches streams, but most of the time folder_exists just
+ * uses the inbox stream or ps->mail_stream.
+ *
+ * Another thing to consider is that maybe there should be an
+ * option to try to LIST a folder before doing a STATUS (or SELECT).
+ * This isn't done by default for the case where a folder is
+ * SELECTable but not LISTable, but on some servers doing an
+ * RLIST first tells the server that we support mailbox referrals.
+ */
+ if(!try_fast
+ || !((rv = folder_exists(cntxt,f->name))
+ & (FEX_ISMARKED | FEX_UNMARKED))){
+ extern MAILSTATUS mm_status_result;
+
+ if(try_fast && (rv == 0 || rv & FEX_ERROR)){
+ failed_status = 1;
+ mm_status_result.flags = 0L;
+ }
+ else{
+ if(stream){
+ if(!context_status_full(cntxt, match ? NULL : stream,
+ f->name,
+ F_ON(F_TAB_USES_UNSEEN, ps_global)
+ ? SA_UNSEEN : SA_RECENT,
+ &f->uidvalidity,
+ &f->uidnext)){
+ failed_status = 1;
+ mm_status_result.flags = 0L;
+ }
+ }
+ else{
+ /* so we can re-use the stream */
+ if(!context_status_streamp_full(cntxt, streamp, f->name,
+ F_ON(F_TAB_USES_UNSEEN, ps_global)
+ ? SA_UNSEEN : SA_RECENT,
+ &f->uidvalidity,
+ &f->uidnext)){
+ failed_status = 1;
+ mm_status_result.flags = 0L;
+ }
+ }
+ }
+
+ if(F_ON(F_TAB_USES_UNSEEN, ps_global)){
+ rv = ((mm_status_result.flags & SA_UNSEEN)
+ && (*find_recent = mm_status_result.unseen))
+ ? FEX_ISMARKED : 0;
+ }
+ else{
+ rv = ((mm_status_result.flags & SA_RECENT)
+ && (*find_recent = mm_status_result.recent))
+ ? FEX_ISMARKED : 0;
+ }
+
+ /* we don't know how many in this case */
+ if(try_fast)
+ *find_recent = 0L; /* consistency, boy! */
+ }
+
+ if(we_cancel)
+ cancel_busy_cue(0);
+
+ if(failed_status && did_cancel){
+ char buf1[6*MAX_SCREEN_COLS+1];
+ int wid1, wid2;
+
+ snprintf(prompt, sizeof(prompt), _("Check of folder %s failed. Continue "), FLDR_NAME(f));
+ if(utf8_width(prompt) > MAXPROMPT){
+ snprintf(prompt, sizeof(prompt), _("Check of %s failed. Continue "), FLDR_NAME(f));
+ if((wid1=utf8_width(prompt)) > MAXPROMPT){
+ if((wid2=utf8_width(FLDR_NAME(f))) > wid1-MAXPROMPT)
+ snprintf(prompt, sizeof(prompt), _("Check of %s failed. Continue "), strsquish(buf1, sizeof(buf1), FLDR_NAME(f), wid2-(wid1-MAXPROMPT)));
+ else
+ snprintf(prompt, sizeof(prompt), _("Check failed. Continue "));
+ }
+ }
+
+ if(want_to(prompt, 'y', 0, NO_HELP, WT_NORM) == 'n'){
+ *did_cancel = 1;
+ break;
+ }
+ else
+ /* have to reset this lower-level cancel marker */
+ ps_global->user_says_cancel = 0;
+ }
+
+ failed_status = 0;
+
+ if(rv & FEX_ISMARKED){
+ recent++;
+ break;
+ }
+ }
+
+ if(f && (!find_recent || recent)){
+ strncpy(next, FLDR_NAME(f), nextlen);
+ next[nextlen-1] = '\0';
+ }
+ else if(nextlen > 0)
+ *next = '\0';
+
+ /* BUG: how can this be made smarter so we cache the list? */
+ free_folder_list(cntxt);
+ return((*next) ? next : NULL);
+}
+
+
+/*----------------------------------------------------------------------
+ Shuffle order of incoming folders
+ ----*/
+int
+shuffle_incoming_folders(CONTEXT_S *context, int index)
+{
+ int tot, i, deefault, rv, inheriting = 0;
+ int new_index, index_within_var, new_index_within_var;
+ int readonly = 0;
+ char tmp[200];
+ HelpType help;
+ ESCKEY_S opts[3];
+ char ***alval;
+ EditWhich ew;
+ FOLDER_S *fp;
+ PINERC_S *prc = NULL;
+
+ dprint((4, "shuffle_incoming_folders\n"));
+
+ if(!(context->use & CNTXT_INCMNG) ||
+ (tot = folder_total(FOLDERS(context))) < 2 ||
+ index < 0 || index >= tot)
+ return(0);
+
+ if(index == 0){
+ q_status_message(SM_ORDER,0,3, _("Cannot shuffle INBOX"));
+ return(0);
+ }
+
+ fp = folder_entry(index, FOLDERS(context));
+ ew = config_containing_inc_fldr(fp);
+
+ if(ps_global->restricted)
+ readonly = 1;
+ else{
+ switch(ew){
+ case Main:
+ prc = ps_global->prc;
+ break;
+ case Post:
+ prc = ps_global->post_prc;
+ break;
+ case None:
+ break;
+ }
+
+ readonly = prc ? prc->readonly : 1;
+ }
+
+ if(prc && prc->quit_to_edit){
+ quit_to_edit_msg(prc);
+ return(0);
+ }
+
+ if(readonly){
+ q_status_message(SM_ORDER,3,5,
+ _("Shuffle cancelled: config file not editable"));
+ return(0);
+ }
+
+ alval = ALVAL(&ps_global->vars[V_INCOMING_FOLDERS], ew);
+
+ if(!(alval && *alval))
+ return(0);
+
+ i = 0;
+ opts[i].ch = 'b';
+ opts[i].rval = 'b';
+ opts[i].name = "B";
+ opts[i++].label = N_("Back");
+
+ opts[i].ch = 'f';
+ opts[i].rval = 'f';
+ opts[i].name = "F";
+ opts[i++].label = N_("Forward");
+
+ opts[i].ch = -1;
+ deefault = 'b';
+
+ /* find where this entry is in the particular config list */
+ index_within_var = -1;
+ for(i = 0; (*alval)[i]; i++){
+ expand_variables(tmp_20k_buf, SIZEOF_20KBUF, (*alval)[i], 0);
+ if(i == 0 && !strcmp(tmp_20k_buf, INHERIT))
+ inheriting = 1;
+ else if(fp->varhash == line_hash(tmp_20k_buf)){
+ index_within_var = i;
+ break;
+ }
+ }
+
+ if(index_within_var == -1){ /* didn't find it */
+ q_status_message(SM_ORDER,3,5,
+ _("Shuffle cancelled: unexpected trouble shuffling"));
+ return(0);
+ }
+
+ if(index_within_var == 0 || (inheriting && index_within_var == 1)){
+ opts[0].ch = -2; /* no back */
+ deefault = 'f';
+ }
+
+ if(!(*alval)[i+1]) /* no forward */
+ opts[1].ch = -2;
+
+ if(opts[0].ch == -2 && opts[1].ch == -2){
+ q_status_message(SM_ORDER, 0, 4,
+ _("Cannot shuffle from one config file to another."));
+ return(0);
+ }
+
+ snprintf(tmp, sizeof(tmp), "Shuffle \"%s\" %s%s%s ? ",
+ FLDR_NAME(folder_entry(index, FOLDERS(context))),
+ (opts[0].ch != -2) ? "BACK" : "",
+ (opts[0].ch != -2 && opts[1].ch != -2) ? " or " : "",
+ (opts[1].ch != -2) ? "FORWARD" : "");
+ tmp[sizeof(tmp)-1] = '\0';
+ help = (opts[0].ch == -2) ? h_incoming_shuf_down
+ : (opts[1].ch == -2) ? h_incoming_shuf_up
+ : h_incoming_shuf;
+
+ rv = radio_buttons(tmp, -FOOTER_ROWS(ps_global), opts, deefault, 'x',
+ help, RB_NORM);
+
+ new_index = index;
+ new_index_within_var = index_within_var;
+
+ switch(rv){
+ case 'x':
+ cmd_cancelled("Shuffle");
+ return(0);
+
+ case 'b':
+ new_index_within_var--;
+ new_index--;
+ break;
+
+ case 'f':
+ new_index_within_var++;
+ new_index++;
+ break;
+ }
+
+ if(swap_incoming_folders(index, new_index, FOLDERS(context))){
+ char *stmp;
+
+ /* swap them in the config variable, too */
+ stmp = (*alval)[index_within_var];
+ (*alval)[index_within_var] = (*alval)[new_index_within_var];
+ (*alval)[new_index_within_var] = stmp;
+
+ set_current_val(&ps_global->vars[V_INCOMING_FOLDERS], TRUE, FALSE);
+ write_pinerc(ps_global, ew, WRP_NONE);
+
+ return(1);
+ }
+ else
+ return(0);
+}
+
+
+int
+swap_incoming_folders(int index1, int index2, FLIST *flist)
+{
+ FOLDER_S *ftmp;
+
+ if(!flist)
+ return(0);
+
+ if(index1 == index2)
+ return(1);
+
+ if(index1 < 0 || index1 >= flist->used){
+ dprint((1, "Error in swap_incoming_folders: index1=%d, used=%d\n", index1, flist->used));
+ return(0);
+ }
+
+ if(index2 < 0 || index2 >= flist->used){
+ dprint((1, "Error in swap_incoming_folders: index2=%d, used=%d\n", index2, flist->used));
+ return(0);
+ }
+
+ ftmp = flist->folders[index1];
+ flist->folders[index1] = flist->folders[index2];
+ flist->folders[index2] = ftmp;
+
+ return(1);
+}
+
+
+/*----------------------------------------------------------------------
+ Find an entry in the folder list by matching names
+ ----*/
+int
+search_folder_list(void *list, char *name)
+{
+ int i;
+ char *n;
+
+ for(i = 0; i < folder_total(list); i++) {
+ n = folder_entry(i, list)->name;
+ if(strucmp(name, n) == 0)
+ return(1); /* Found it */
+ }
+ return(0);
+}
+
+
+static CONTEXT_S *post_cntxt = NULL;
+
+/*----------------------------------------------------------------------
+ Browse list of newsgroups available for posting
+
+ Called from composer when ^T is typed in newsgroups field
+
+Args: none
+
+Returns: pointer to selected newsgroup, or NULL.
+ Selector call in composer expects this to be alloc'd here.
+
+ ----*/
+char *
+news_group_selector(char **error_mess)
+{
+ CONTEXT_S *tc;
+ char *post_folder;
+ int rc;
+ char *em;
+
+ /* Coming back from composer */
+ fix_windsize(ps_global);
+ init_sigwinch();
+
+ post_folder = fs_get((size_t)MAILTMPLEN);
+
+ /*--- build the post_cntxt -----*/
+ em = get_post_list(ps_global->VAR_NNTP_SERVER);
+ if(em != NULL){
+ if(error_mess != NULL)
+ *error_mess = cpystr(em);
+
+ cancel_busy_cue(-1);
+ return(NULL);
+ }
+
+ /*----- Call the browser -------*/
+ tc = post_cntxt;
+ if((rc = folders_for_post(ps_global, &tc, post_folder)) != 0)
+ post_cntxt = tc;
+
+ cancel_busy_cue(-1);
+
+ if(rc <= 0)
+ return(NULL);
+
+ return(post_folder);
+}
+
+
+/*----------------------------------------------------------------------
+ Get the list of news groups that are possible for posting
+
+Args: post_host -- host name for posting
+
+Returns NULL if list is retrieved, pointer to error message if failed
+
+This is kept in a standards "CONTEXT" for a acouple of reasons. First
+it makes it very easy to use the folder browser to display the
+newsgroup for selection on ^T from the composer. Second it will allow
+the same mechanism to be used for all folder lists on memory tight
+systems like DOS. The list is kept for the life of the session because
+fetching it is a expensive.
+
+ ----*/
+char *
+get_post_list(char **post_host)
+{
+ char *post_context_string;
+
+ if(!post_host || !post_host[0]) {
+ /* BUG should assume inews and get this from active file */
+ return(_("Can't post messages, NNTP server needs to be configured"));
+ }
+
+ if(!post_cntxt){
+ int we_cancel;
+ size_t l;
+
+ we_cancel = busy_cue(_("Getting full list of groups for posting"), NULL, 1);
+
+ l = strlen(post_host[0]) + 20;
+ post_context_string = (char *) fs_get((l+1) * sizeof(char));
+ snprintf(post_context_string, l+1, "{%s/nntp}#news.[]", post_host[0]);
+ post_context_string[l] = '\0';
+
+ post_cntxt = new_context(post_context_string, NULL);
+ post_cntxt->use |= CNTXT_FINDALL;
+ post_cntxt->dir->status |= CNTXT_NOFIND;
+ post_cntxt->next = NULL;
+
+ build_folder_list(NULL, post_cntxt, NULL, NULL,
+ NEWS_TEST(post_cntxt) ? BFL_LSUB : BFL_NONE);
+ if(we_cancel)
+ cancel_busy_cue(-1);
+ }
+ return(NULL);
+}
+
+
+#ifdef _WINDOWS
+int
+folder_list_popup(sparms, in_handle)
+ SCROLL_S *sparms;
+ int in_handle;
+{
+ MPopup fldr_popup[20];
+
+ memset(fldr_popup, 0, 20 * sizeof(MPopup));
+ fldr_popup[0].type = tTail;
+ if(in_handle){
+ int i, n = 0;
+ HANDLE_S *h = get_handle(sparms->text.handles, in_handle);
+ FOLDER_S *fp = (h) ? folder_entry(h->h.f.index,
+ FOLDERS(h->h.f.context))
+ : NULL;
+
+ if((i = menu_binding_index(sparms->keys.menu, MC_CHOICE)) >= 0){
+ fldr_popup[n].type = tQueue;
+ fldr_popup[n].data.val = sparms->keys.menu->keys[i].bind.ch[0];
+ fldr_popup[n].label.style = lNormal;
+ fldr_popup[n++].label.string = (fp && fp->isdir)
+ ? "&View Directory"
+ : "&View Folder";
+ }
+
+ if((i = menu_binding_index(sparms->keys.menu, MC_SELCUR)) >= 0
+ && bitnset(i, sparms->keys.bitmap)){
+ fldr_popup[n].type = tQueue;
+ fldr_popup[n].data.val = sparms->keys.menu->keys[i].bind.ch[0];
+ fldr_popup[n].label.style = lNormal;
+ fldr_popup[n++].label.string = (fp && fp->isdir)
+ ? "&Select Directory"
+ : "&Select Folder";
+ }
+
+ if((i = menu_binding_index(sparms->keys.menu, MC_DELETE)) >= 0
+ && bitnset(i, sparms->keys.bitmap)){
+ fldr_popup[n].type = tQueue;
+ fldr_popup[n].data.val = sparms->keys.menu->keys[i].bind.ch[0];
+ fldr_popup[n].label.style = lNormal;
+ fldr_popup[n++].label.string = "Delete Folder";
+ }
+
+ if((i = menu_binding_index(sparms->keys.menu, MC_EXPORT)) >= 0
+ && bitnset(i, sparms->keys.bitmap)){
+ fldr_popup[n].type = tQueue;
+ fldr_popup[n].data.val = sparms->keys.menu->keys[i].bind.ch[0];
+ fldr_popup[n].label.style = lNormal;
+ fldr_popup[n++].label.string = "Export Folder";
+ }
+
+ if((i = menu_binding_index(sparms->keys.menu, MC_CHK_RECENT)) >= 0
+ && bitnset(i, sparms->keys.bitmap)){
+ fldr_popup[n].type = tQueue;
+ fldr_popup[n].data.val = sparms->keys.menu->keys[i].bind.ch[0];
+ fldr_popup[n].label.style = lNormal;
+ fldr_popup[n++].label.string = "Check New Messages";
+ }
+
+ if(n)
+ fldr_popup[n++].type = tSeparator;
+
+ folder_popup_config(((FPROC_S *)sparms->proc.data.p)->fs,
+ sparms->keys.menu, &fldr_popup[n]);
+ }
+ else
+ folder_popup_config(((FPROC_S *)sparms->proc.data.p)->fs,
+ sparms->keys.menu, fldr_popup);
+
+ if(fldr_popup[0].type != tTail)
+ mswin_popup(fldr_popup);
+
+ return(0);
+}
+
+
+int
+folder_list_select_popup(sparms, in_handle)
+ SCROLL_S *sparms;
+ int in_handle;
+{
+ MPopup fldr_popup[20];
+
+ memset(fldr_popup, 0, 20 * sizeof(MPopup));
+ fldr_popup[0].type = tTail;
+ if(in_handle){
+ int i, n = 0;
+ HANDLE_S *h = get_handle(sparms->text.handles, in_handle);
+ FOLDER_S *fp = (h) ? folder_entry(h->h.f.index,FOLDERS(h->h.f.context))
+ : NULL;
+
+ if((i = menu_binding_index(sparms->keys.menu, MC_CHOICE)) >= 0){
+ fldr_popup[n].type = tQueue;
+ fldr_popup[n].data.val = sparms->keys.menu->keys[i].bind.ch[0];
+ fldr_popup[n].label.style = lNormal;
+ fldr_popup[n++].label.string = (fp && fp->isdir)
+ ? "&View Directory"
+ : "&Select";
+
+ fldr_popup[n++].type = tSeparator;
+ }
+
+ folder_popup_config(((FPROC_S *)sparms->proc.data.p)->fs,
+ sparms->keys.menu, &fldr_popup[n]);
+ }
+ else
+ folder_popup_config(((FPROC_S *)sparms->proc.data.p)->fs,
+ sparms->keys.menu, fldr_popup);
+
+ if(fldr_popup[0].type != tTail)
+ mswin_popup(fldr_popup);
+
+ return(0);
+}
+
+
+/*
+ * Just a little something to simplify assignments
+ */
+#define FLDRPOPUP(p, c, s) { \
+ (p)->type = tQueue; \
+ (p)->data.val = c; \
+ (p)->label.style = lNormal; \
+ (p)->label.string = s; \
+ }
+
+
+/*----------------------------------------------------------------------
+ Popup Menu configurator
+
+ ----*/
+void
+folder_popup_config(fs, km, popup)
+ FSTATE_S *fs;
+ struct key_menu *km;
+ MPopup *popup;
+{
+ int i;
+
+ if((i = menu_binding_index(km, MC_PARENT)) >= 0){
+ FLDRPOPUP(popup, km->keys[i].bind.ch[0], "Parent Directory");
+ popup++;
+ }
+
+ if(fs->km == &folder_km){
+ if((fs->context->next || fs->context->prev) && !fs->combined_view){
+ FLDRPOPUP(popup, '<', "Collection List");
+ popup++;
+ }
+ }
+ else if((i = menu_binding_index(km, MC_COLLECTIONS)) >= 0){
+ FLDRPOPUP(popup, km->keys[i].bind.ch[0], "Collection List");
+ popup++;
+ }
+
+ if((i = menu_binding_index(km, MC_INDEX)) >= 0){
+ FLDRPOPUP(popup, km->keys[i].bind.ch[0], "Current Folder Index");
+ popup++;
+ }
+
+ if((i = menu_binding_index(km, MC_MAIN)) >= 0){
+ FLDRPOPUP(popup, km->keys[i].bind.ch[0], "Main Menu");
+ popup++;
+ }
+
+ popup->type = tTail; /* tie off the array */
+}
+#endif /* _WINDOWS */
diff --git a/alpine/folder.h b/alpine/folder.h
new file mode 100644
index 00000000..6e9fbee5
--- /dev/null
+++ b/alpine/folder.h
@@ -0,0 +1,39 @@
+/*
+ * $Id: folder.h 767 2007-10-24 00:03:59Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_FOLDER_INCLUDED
+#define PINE_FOLDER_INCLUDED
+
+
+#include "context.h"
+#include "../pith/folder.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+
+
+/* exported protoypes */
+void folder_screen(struct pine *);
+void folder_config_screen(struct pine *, int);
+int folders_for_goto(struct pine *, CONTEXT_S **, char *, int);
+int folders_for_save(struct pine *, CONTEXT_S **, char *, int);
+char *folders_for_fcc(char **);
+char *folder_for_config(int);
+char *context_edit_screen(struct pine *, char *, char *, char *, char *, char *);
+int add_new_folder(CONTEXT_S *, EditWhich, int, char *, size_t, MAILSTREAM *, char *);
+char *next_folder(MAILSTREAM **, char *, size_t, char *, CONTEXT_S *, long *, int *);
+char *news_group_selector(char **);
+
+
+#endif /* PINE_FOLDER_INCLUDED */
diff --git a/alpine/headers.h b/alpine/headers.h
new file mode 100644
index 00000000..759b7879
--- /dev/null
+++ b/alpine/headers.h
@@ -0,0 +1,68 @@
+/*
+ * $Id: headers.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef ALPINE_HEADERS_INCLUDED
+#define ALPINE_HEADERS_INCLUDED
+
+
+/*----------------------------------------------------------------------
+ Include files
+ ----*/
+
+#include "../pith/headers.h"
+
+#include "../pico/headers.h"
+
+
+/*
+ * Redefinition to help pico storage object use more clear
+ */
+#define PicoText ExternalText
+
+
+#if !defined(DOS) && !defined(OS2) && !defined(LEAVEOUTFIFO)
+ /*
+ * If LEAVEOUTFIFO is set in os.h, then we leave it out.
+ * If it isn't set, we still might leave it out. We'll decide
+ * based on whether or not O_NONBLOCK is defined or not.
+ * It's just a guess. Safer would be to change the polarity of the
+ * test and define something like INCLUDEFIFO instead of LEAVEOUTFIFO
+ * and only define it where we know. Since we don't really know
+ * we'd rather run the risk of being wrong and finding out that
+ * way instead of just having people not know about it.
+ */
+#if !defined(O_NONBLOCK)
+#define LEAVEOUTFIFO 1
+#endif
+#endif
+
+/* include osdep protos and def'ns */
+#include "osdep/debuging.h"
+#include "osdep/execview.h"
+#include "osdep/fltrname.h"
+#include "osdep/jobcntrl.h"
+#include "osdep/print.h"
+#include "osdep/termin.gen.h"
+#include "osdep/termout.gen.h"
+#ifndef _WINDOWS
+#include "osdep/termin.unx.h"
+#include "osdep/termout.unx.h"
+#else /* _WINDOWS */
+#include "osdep/termin.wnt.h"
+#include "osdep/termout.wnt.h"
+#endif /* _WINDOWS */
+
+
+#endif /* ALPINE_HEADERS_INCLUDED */
diff --git a/alpine/help.c b/alpine/help.c
new file mode 100644
index 00000000..b291defd
--- /dev/null
+++ b/alpine/help.c
@@ -0,0 +1,1408 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: help.c 1032 2008-04-11 00:30:04Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "headers.h"
+#include "help.h"
+#include "keymenu.h"
+#include "status.h"
+#include "mailview.h"
+#include "mailindx.h"
+#include "mailcmd.h"
+#include "reply.h"
+#include "signal.h"
+#include "radio.h"
+#include "send.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/filter.h"
+#include "../pith/msgno.h"
+#include "../pith/pipe.h"
+#include "../pith/util.h"
+#include "../pith/detoken.h"
+#include "../pith/list.h"
+#include "../pith/margin.h"
+
+
+typedef struct _help_scroll {
+ unsigned keys_formatted:1; /* Has full keymenu been formatted? */
+ char **help_source; /* Source of displayed help text */
+} HELP_SCROLL_S;
+
+
+static struct {
+ unsigned crlf:1;
+ char **line,
+ *offset;
+} g_h_text;
+
+
+typedef struct _help_print_state {
+ int page;
+ char *title;
+ int title_len;
+} HPRT_S;
+
+static HPRT_S *g_hprt;
+
+
+static char att_cur_msg[] = "\
+ Reporting a bug...\n\
+\n\
+ If you think that the \"current\" message may be related to the bug you\n\
+ are reporting you may include it as an attachment. If you want to\n\
+ include a message but you aren't sure if it is the current message,\n\
+ cancel this bug report, go to the folder index, place the cursor on\n\
+ the message you wish to include, then return to the main menu and run\n\
+ the bug report command again. Answer \"Y\" when asked the question\n\
+ \"Attach current message to report?\"\n\
+\n\
+ This bug report will also automatically include your pine\n\
+ configuration file, which is helpful when investigating the problem.";
+
+
+#define GRIPE_OPT_CONF 0x01
+#define GRIPE_OPT_MSG 0x02
+#define GRIPE_OPT_LOCAL 0x04
+#define GRIPE_OPT_KEYS 0x08
+
+
+/*
+ * Internal prototypes
+ */
+int helper_internal(HelpType, char *, char *, int);
+int help_processor(int, MSGNO_S *, SCROLL_S *);
+void help_keymenu_tweek(SCROLL_S *, int);
+void print_all_help(void);
+void print_help_page_title(char *, size_t, HPRT_S *);
+int print_help_page_break(long, char *, LT_INS_S **, void *);
+int help_bogus_input(UCS);
+int gripe_newbody(struct pine *, BODY **, long, int);
+ADDRESS *gripe_token_addr(char *);
+char *gripe_id(char *);
+void att_cur_drawer(void);
+int journal_processor(int, MSGNO_S *, SCROLL_S *);
+int help_popup(SCROLL_S *, int);
+#ifdef _WINDOWS
+int help_subsection_popup(SCROLL_S *, int);
+#endif
+
+
+
+/*----------------------------------------------------------------------
+ Get the help text in the proper format and call scroller
+
+ Args: text -- The help text to display (from pine.help --> helptext.c)
+ title -- The title of the help text
+
+ Result: format text and call scroller
+
+ The pages array contains the line number of the start of the pages in
+the text. Page 1 is in the 0th element of the array.
+The list is ended with a page line number of -1. Line number 0 is also
+the first line in the text.
+ -----*/
+int
+helper_internal(HelpType text, char *frag, char *title, int flags)
+{
+ char **shown_text;
+ int cmd = MC_NONE;
+ long offset = 0L;
+ char *error = NULL, tmp_title[MAX_SCREEN_COLS + 1];
+ STORE_S *store;
+ HANDLE_S *handles = NULL, *htmp;
+ HELP_SCROLL_S hscroll;
+ gf_io_t pc;
+
+ dprint((1, "\n\n ---- HELPER ----\n"));
+
+ /* assumption here is that HelpType is char ** */
+ shown_text = text;
+
+ if(F_ON(F_BLANK_KEYMENU,ps_global)){
+ FOOTER_ROWS(ps_global) = 3;
+ clearfooter(ps_global);
+ ps_global->mangled_screen = 1;
+ }
+
+ if(flags & HLPD_NEWWIN){
+ fix_windsize(ps_global);
+ init_sigwinch();
+ }
+
+ /*
+ * At this point, shown_text is a charstarstar with html
+ * Turn it into a charstar with digested html
+ */
+ do{
+ init_helper_getc(shown_text);
+ init_handles(&handles);
+
+ memset(&hscroll, 0, sizeof(HELP_SCROLL_S));
+ hscroll.help_source = shown_text;
+ if((store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
+ gf_set_so_writec(&pc, store);
+ gf_filter_init();
+
+ if(!struncmp(shown_text[0], "<html>", 6))
+ gf_link_filter(gf_html2plain,
+ gf_html2plain_opt("x-alpine-help:",
+ ps_global->ttyo->screen_cols,
+ non_messageview_margin(),
+ &handles, NULL, GFHP_LOCAL_HANDLES));
+ else
+ gf_link_filter(gf_wrap, gf_wrap_filter_opt(
+ ps_global->ttyo->screen_cols,
+ ps_global->ttyo->screen_cols,
+ NULL, 0, GFW_HANDLES | GFW_SOFTHYPHEN));
+
+ error = gf_pipe(helper_getc, pc);
+
+ gf_clear_so_writec(store);
+
+ if(!error){
+ SCROLL_S sargs;
+ struct key_menu km;
+ struct key keys[24];
+
+ for(htmp = handles; htmp; htmp = htmp->next)
+ if(htmp->type == URL
+ && htmp->h.url.path
+ && (htmp->h.url.path[0] == 'x'
+ || htmp->h.url.path[0] == '#'))
+ htmp->force_display = 1;
+
+ /* This is mostly here to get the curses variables
+ * for line and column in sync with where the
+ * cursor is on the screen. This gets warped when
+ * the composer is called because it does it's own
+ * stuff
+ */
+ ClearScreen();
+
+ memset(&sargs, 0, sizeof(SCROLL_S));
+ sargs.text.text = so_text(store);
+ sargs.text.src = CharStar;
+ sargs.text.desc = _("help text");
+ if((sargs.text.handles = handles) != NULL)
+ while(sargs.text.handles->type == URL
+ && !sargs.text.handles->h.url.path
+ && sargs.text.handles->next)
+ sargs.text.handles = sargs.text.handles->next;
+
+ if(!(sargs.bar.title = title)){
+ if(!struncmp(shown_text[0], "<html>", 6)){
+ char *p;
+ int i;
+
+ /* if we're looking at html, look for a <title>
+ * in the <head>... */
+ for(i = 1;
+ shown_text[i]
+ && struncmp(shown_text[i], "</head>", 7);
+ i++)
+ if(!struncmp(shown_text[i], "<title>", 7)){
+ strncpy(tmp_20k_buf, &shown_text[i][7], SIZEOF_20KBUF);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ if((p = strchr(tmp_20k_buf, '<')) != NULL)
+ *p = '\0';
+
+ snprintf(sargs.bar.title = tmp_title, sizeof(tmp_title),
+ "%s -- %.*s", _("HELP"),
+ ps_global->ttyo->screen_cols-10,
+ strsquish(tmp_20k_buf + 500, SIZEOF_20KBUF,
+ _(tmp_20k_buf),
+ ps_global->ttyo->screen_cols / 3));
+ tmp_title[sizeof(tmp_title)-1] = '\0';
+ break;
+ }
+ }
+
+ if(!sargs.bar.title)
+ sargs.bar.title = _("HELP TEXT");
+ }
+
+ sargs.bar.style = TextPercent;
+ sargs.proc.tool = help_processor;
+ sargs.proc.data.p = (void *) &hscroll;
+ sargs.resize_exit = 1;
+ sargs.help.text = h_special_help_nav;
+ sargs.help.title = _("HELP FOR HELP TEXT");
+ sargs.keys.menu = &km;
+ km = help_keymenu;
+ km.keys = keys;
+ memcpy(&keys[0], help_keymenu.keys,
+ (help_keymenu.how_many * 12) * sizeof(struct key));
+ setbitmap(sargs.keys.bitmap);
+ if(flags & HLPD_FROMHELP){
+ km.keys[HLP_EXIT_KEY].name = "P";
+ km.keys[HLP_EXIT_KEY].label = _("Prev Help");
+ km.keys[HLP_EXIT_KEY].bind.cmd = MC_FINISH;
+ km.keys[HLP_EXIT_KEY].bind.ch[0] = 'p';
+
+ km.keys[HLP_SUBEXIT_KEY].name = "E";
+ km.keys[HLP_SUBEXIT_KEY].label = _("Exit Help");
+ km.keys[HLP_SUBEXIT_KEY].bind.cmd = MC_EXIT;
+ km.keys[HLP_SUBEXIT_KEY].bind.ch[0] = 'e';
+ }
+ else if(text == h_special_help_nav){
+ km.keys[HLP_EXIT_KEY].name = "P";
+ km.keys[HLP_EXIT_KEY].label = _("Prev Help");
+ km.keys[HLP_EXIT_KEY].bind.cmd = MC_FINISH;
+ km.keys[HLP_EXIT_KEY].bind.ch[0] = 'p';
+
+ clrbitn(HLP_MAIN_KEY, sargs.keys.bitmap);
+ clrbitn(HLP_SUBEXIT_KEY, sargs.keys.bitmap);
+ }
+ else{
+ km.keys[HLP_EXIT_KEY].name = "E";
+ km.keys[HLP_EXIT_KEY].label = _("Exit Help");
+ km.keys[HLP_EXIT_KEY].bind.cmd = MC_EXIT;
+ km.keys[HLP_EXIT_KEY].bind.ch[0] = 'e';
+
+ km.keys[HLP_SUBEXIT_KEY].name = "?";
+ /* TRANSLATORS: this is the label of a command where
+ the user is asking for Help about the Help command */
+ km.keys[HLP_SUBEXIT_KEY].label = _("Help Help");
+ km.keys[HLP_SUBEXIT_KEY].bind.cmd = MC_HELP;
+ km.keys[HLP_SUBEXIT_KEY].bind.ch[0] = '?';
+ }
+
+ if(flags & HLPD_SIMPLE){
+ clrbitn(HLP_MAIN_KEY, sargs.keys.bitmap);
+ }
+ else
+ sargs.bogus_input = help_bogus_input;
+
+ if(handles){
+ sargs.keys.each_cmd = help_keymenu_tweek;
+ hscroll.keys_formatted = 0;
+ }
+ else{
+ clrbitn(HLP_VIEW_HANDLE, sargs.keys.bitmap);
+ clrbitn(HLP_PREV_HANDLE, sargs.keys.bitmap);
+ clrbitn(HLP_NEXT_HANDLE, sargs.keys.bitmap);
+ }
+
+ if(text != main_menu_tx
+ && text != h_mainhelp_pinehelp)
+ clrbitn(HLP_ALL_KEY, sargs.keys.bitmap);
+
+ if(frag){
+ sargs.start.on = Fragment;
+ sargs.start.loc.frag = frag;
+ frag = NULL; /* ignore next time */
+ }
+ else if(offset){
+ sargs.start.on = Offset;
+ sargs.start.loc.offset = offset;
+ }
+ else
+ sargs.start.on = FirstPage;
+
+#ifdef _WINDOWS
+ sargs.mouse.popup = (flags & HLPD_FROMHELP)
+ ? help_subsection_popup : help_popup;
+#endif
+
+ cmd = scrolltool(&sargs);
+
+ offset = sargs.start.loc.offset;
+
+ if(F_ON(F_BLANK_KEYMENU,ps_global))
+ FOOTER_ROWS(ps_global) = 1;
+
+ ClearScreen();
+ }
+
+ so_give(&store);
+ }
+
+ free_handles(&handles);
+ }
+ while(cmd == MC_RESIZE);
+
+ return(cmd);
+}
+
+
+/*
+ * helper -- compatibility function around newer helper_internal
+ */
+int
+helper(HelpType text, char *title, int flags)
+{
+ return(helper_internal(text, NULL, title, flags));
+}
+
+
+void
+init_helper_getc(char **help_txt)
+{
+ g_h_text.crlf = 0;
+ g_h_text.line = help_txt;
+ g_h_text.offset = *g_h_text.line;
+ if(g_h_text.offset && g_h_text.offset[0])
+ g_h_text.offset = _(g_h_text.offset);
+}
+
+
+int
+helper_getc(char *c)
+{
+ if(g_h_text.crlf){
+ *c = '\012';
+ g_h_text.crlf = 0;
+ return(1);
+ }
+ else if(g_h_text.offset && *g_h_text.line){
+ if(!(*c = *g_h_text.offset++)){
+ g_h_text.offset = *++g_h_text.line;
+ if(g_h_text.offset && g_h_text.offset[0])
+ g_h_text.offset = _(g_h_text.offset);
+
+ *c = '\015';
+ g_h_text.crlf = 1;
+ }
+
+ return(1);
+ }
+
+ return(0);
+}
+
+
+int
+help_processor(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
+{
+ int rv = 0;
+ char message[64];
+ struct help_texts *t;
+
+ switch(cmd){
+ /*----------- Print all the help ------------*/
+ case MC_PRINTALL :
+ print_all_help();
+ break;
+
+ case MC_PRINTMSG :
+ snprintf(message, sizeof(message), "%s", STYLE_NAME(sparms));
+ message[sizeof(message)-1] = '\0';
+ if(open_printer(message) == 0){
+ print_help(((HELP_SCROLL_S *)sparms->proc.data.p)->help_source);
+ close_printer();
+ }
+
+ break;
+
+ case MC_EXPORT: /* reuse old definition, so as not to patch pine.h */
+ {char help_name[40];
+ help_name[0] = '\0';
+ for(t = h_texts; t->help_text != NO_HELP; t++)
+ if(t->help_text == ((HELP_SCROLL_S *)sparms->proc.data.p)->help_source){
+ strcpy(help_name,t->tag);
+ break;
+ }
+ if(help_name[0])
+ q_status_message1(SM_ORDER, 0, 2,
+ "Internal Name: x-alpine-help:%s", help_name);
+ else
+ q_status_message(SM_ORDER|SM_DING, 1, 2,
+ "Can not find link for text help");
+ }
+ break;
+
+ case MC_FINISH :
+ rv = 1;
+ break;
+
+ default :
+ panic("Unhandled case");
+ }
+
+ return(rv);
+}
+
+
+void
+help_keymenu_tweek(SCROLL_S *sparms, int handle_hidden)
+{
+ if(handle_hidden){
+ sparms->keys.menu->keys[HLP_VIEW_HANDLE].name = "";
+ sparms->keys.menu->keys[HLP_VIEW_HANDLE].label = "";
+ }
+ else{
+ if(!((HELP_SCROLL_S *)sparms->proc.data.p)->keys_formatted){
+ /* If label's always been blank, force reformatting */
+ mark_keymenu_dirty();
+ sparms->keys.menu->width = 0;
+ ((HELP_SCROLL_S *)sparms->proc.data.p)->keys_formatted = 1;
+ }
+
+ sparms->keys.menu->keys[HLP_VIEW_HANDLE].name = "V";
+ sparms->keys.menu->keys[HLP_VIEW_HANDLE].label = "[" N_("View Link") "]";
+ }
+}
+
+
+/*
+ * print_help - send the raw array of lines to printer
+ */
+void
+print_help(char **text)
+{
+ char *error, buf[256];
+ HPRT_S help_data;
+
+ init_helper_getc(text);
+
+ memset(g_hprt = &help_data, 0, sizeof(HPRT_S));
+
+ help_data.page = 1;
+
+ gf_filter_init();
+
+ if(!struncmp(text[0], "<html>", 6)){
+ int i;
+ char *p;
+
+ gf_link_filter(gf_html2plain,
+ gf_html2plain_opt(NULL,80,non_messageview_margin(),
+ NULL,NULL,GFHP_STRIPPED));
+ for(i = 1; i <= 5 && text[i]; i++)
+ if(!struncmp(text[i], "<title>", 7)
+ && (p = srchstr(text[i] + 7, "</title>"))
+ && p - text[i] > 7){
+ help_data.title = text[i] + 7;
+ help_data.title_len = p - help_data.title;
+ break;
+ }
+ }
+ else
+ gf_link_filter(gf_wrap, gf_wrap_filter_opt(80, 80, NULL, 0, GFW_NONE));
+
+ gf_link_filter(gf_line_test,
+ gf_line_test_opt(print_help_page_break, NULL));
+ gf_link_filter(gf_nvtnl_local, NULL);
+
+ print_help_page_title(buf, sizeof(buf), &help_data);
+ print_text(buf);
+ print_text(NEWLINE); /* terminate it */
+ print_text(NEWLINE); /* and write two blank links */
+ print_text(NEWLINE);
+
+ if((error = gf_pipe(helper_getc, print_char)) != NULL)
+ q_status_message1(SM_ORDER | SM_DING, 3, 3, _("Printing Error: %s"), error);
+
+ print_char(ctrl('L')); /* new page. */
+}
+
+
+void
+print_all_help(void)
+{
+ struct help_texts *t;
+ char **h;
+ int we_turned_on = 0;
+
+ if(open_printer(_("all 150+ pages of help text")) == 0) {
+ we_turned_on = intr_handling_on();
+ for(t = h_texts; (h = t->help_text) != NO_HELP; t++) {
+ if(ps_global->intr_pending){
+ q_status_message(SM_ORDER, 3, 3,
+ _("Print of all help cancelled"));
+ break;
+ }
+
+ print_help(h);
+ }
+
+ if(we_turned_on)
+ intr_handling_off();
+
+ close_printer();
+ }
+}
+
+
+/*
+ * print_help_page_title --
+ */
+void
+print_help_page_title(char *buf, size_t buflen, HPRT_S *hprt)
+{
+ snprintf(buf, buflen, " Alpine Help%s%.*s%*s%d",
+ hprt->title_len ? ": " : " Text",
+ MIN(55, hprt->title_len), hprt->title_len ? hprt->title : "",
+ 59 - (hprt->title_len ? MIN(55, hprt->title_len) : 5),
+ "Page ", hprt->page);
+ buf[buflen-1] = '\0';
+}
+
+
+/*
+ * print_help_page_break -- insert page breaks and such for printed
+ * help text
+ */
+int
+print_help_page_break(long int linenum, char *line, LT_INS_S **ins, void *local)
+{
+ char buf[256];
+
+ if(((linenum + (g_hprt->page * 3)) % 62) == 0){
+ g_hprt->page++; /* start on new page */
+ buf[0] = ctrl('L');
+ print_help_page_title(buf + 1, sizeof(buf)-1, g_hprt);
+ strncat(buf, "\015\012\015\012\015\012", sizeof(buf)-strlen(buf)-1);
+ buf[sizeof(buf)-1] = '\0';
+ ins = gf_line_test_new_ins(ins, line, buf, strlen(buf));
+ }
+
+ return(0);
+}
+
+
+/*
+ * help_bogus_input - used by scrolltool to complain about
+ * invalid user input.
+ */
+int
+help_bogus_input(UCS ch)
+{
+ bogus_command(ch, NULL);
+ return(0);
+}
+
+
+int
+url_local_helper(char *url)
+{
+ if((!struncmp(url, "x-alpine-help:", 14) && *(url += 14))
+ || (!struncmp(url, "x-pine-help:", 12) && *(url += 12))){
+ char *frag;
+ HelpType newhelp;
+
+ /* internal fragment reference? */
+ if((frag = strchr(url, '#')) != NULL){
+ size_t len;
+
+ if((len = frag - url) != 0){
+ newhelp = help_name2section(url, len);
+ }
+ else{
+ url_local_fragment(url);
+ return(1);
+ }
+ }
+ else
+ newhelp = help_name2section(url, strlen(url));
+
+
+ if(newhelp != NO_HELP){
+ int rv;
+
+ rv = helper_internal(newhelp, frag, _("HELP SUB-SECTION"),
+ HLPD_NEWWIN | HLPD_SIMPLE | HLPD_FROMHELP);
+ ps_global->mangled_screen = 1;
+ return((rv == MC_EXIT) ? 2 : 1);
+ }
+ }
+
+ q_status_message1(SM_ORDER | SM_DING, 0, 3,
+ _("Unrecognized Internal help: \"%s\""), url);
+ return(0);
+}
+
+
+int
+url_local_config(char *url)
+{
+ if(!struncmp(url, "x-alpine-config:", 16)){
+ char **config;
+ int rv;
+
+ config = get_supported_options();
+ if(config){
+ /* TRANSLATORS: Help for configuration */
+ rv = helper_internal(config, NULL, _("HELP CONFIG"),
+ HLPD_NEWWIN | HLPD_SIMPLE | HLPD_FROMHELP);
+ free_list_array(&config);
+ }
+
+ ps_global->mangled_screen = 1;
+ return((rv == MC_EXIT) ? 2 : 1);
+ }
+
+ q_status_message1(SM_ORDER | SM_DING, 0, 3,
+ _("Unrecognized Internal help: \"%s\""), url);
+ return(0);
+}
+
+
+/*----------------------------------------------------------------------
+ Review latest status messages
+ -----*/
+void
+review_messages(void)
+{
+ SCROLL_S sargs;
+ STORE_S *in_store = NULL, *out_store = NULL;
+ gf_io_t gc, pc;
+ int jo, lo, hi, donejo, donelo, donehi;
+ RMCat rmcat;
+ int cmd, timestamps=0, show_level=-1;
+ char debugkeylabel[20];
+ /* TRANSLATORS: command label asking pine to include time stamps in output */
+ char timestampkeylabel[] = N_("Timestamps");
+ /* TRANSLATORS: do not include time stamps in output */
+ char *notimestampkeylabel = N_("NoTimestamps");
+
+ if((rmjofirst < 0 && rmlofirst < 0 && rmhifirst < 0)
+ || rmjofirst >= RMJLEN || rmjolast >= RMJLEN
+ || rmlofirst >= RMLLEN || rmlolast >= RMLLEN
+ || rmhifirst >= RMHLEN || rmhilast >= RMHLEN
+ || (rmjofirst >= 0 && rmjolast < 0)
+ || (rmlofirst >= 0 && rmlolast < 0)
+ || (rmhifirst >= 0 && rmhilast < 0))
+ return;
+
+ do{
+
+ if(!(in_store = so_get(CharStar, NULL, EDIT_ACCESS)) ||
+ !(out_store = so_get(CharStar, NULL, EDIT_ACCESS))){
+ if(in_store)
+ so_give(&in_store);
+
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Failed allocating memory"));
+ return;
+ }
+
+ add_review_message(_("Turning off new messages while reviewing"), 0);
+ rm_not_right_now = 1;
+
+ donejo = donehi = donelo = 0;
+ jo = rmjofirst;
+ if(jo < 0)
+ donejo = 1;
+
+ lo = rmlofirst;
+ if(lo < 0)
+ donelo = 1;
+
+ hi = rmhifirst;
+ if(hi < 0)
+ donehi = 1;
+
+ while(!(donejo && donelo && donehi)){
+ REV_MSG_S *pjo, *plo, *phi, *p;
+
+ if(!donejo)
+ pjo = &rmjoarray[jo];
+ else
+ pjo = NULL;
+
+ if(!donelo)
+ plo = &rmloarray[lo];
+ else
+ plo = NULL;
+
+ if(!donehi)
+ phi = &rmhiarray[hi];
+ else
+ phi = NULL;
+
+ if(pjo && (!plo || pjo->seq <= plo->seq)
+ && (!phi || pjo->seq <= phi->seq))
+ rmcat = Jo;
+ else if(plo && (!phi || plo->seq <= phi->seq))
+ rmcat = Lo;
+ else if(phi)
+ rmcat = Hi;
+ else
+ rmcat = No;
+
+ switch(rmcat){
+ case Jo:
+ p = pjo;
+ if(jo == rmjofirst && (((rmjolast + 1) % RMJLEN) == rmjofirst))
+ so_puts(in_store,
+ _("**** Journal entries prior to this point have been trimmed. ****\n"));
+ break;
+
+ case Lo:
+ p = plo;
+ if(show_level >= 0 &&
+ lo == rmlofirst && (((rmlolast + 1) % RMLLEN) == rmlofirst))
+ so_puts(in_store,
+ _("**** Debug 0-4 entries prior to this point have been trimmed. ****\n"));
+ break;
+
+ case Hi:
+ p = phi;
+ if(show_level >= 5 &&
+ hi == rmhifirst && (((rmhilast + 1) % RMHLEN) == rmhifirst))
+ so_puts(in_store,
+ _("**** Debug 5-9 entries prior to this point have been trimmed. ****\n"));
+ break;
+
+ default:
+ p = NULL;
+ break;
+ }
+
+ if(p){
+ if(p->level <= show_level){
+ if(timestamps && p->timestamp && p->timestamp[0]){
+ so_puts(in_store, p->timestamp);
+ so_puts(in_store, ": ");
+ }
+
+ if(p->message && p->message[0]){
+ if(p->continuation)
+ so_puts(in_store, ">");
+
+ so_puts(in_store, p->message);
+ so_puts(in_store, "\n");
+ }
+ }
+ }
+
+ switch(rmcat){
+ case Jo:
+ if(jo == rmjolast)
+ donejo++;
+ else
+ jo = (jo + 1) % RMJLEN;
+
+ break;
+
+ case Lo:
+ if(lo == rmlolast)
+ donelo++;
+ else
+ lo = (lo + 1) % RMLLEN;
+
+ break;
+
+ case Hi:
+ if(hi == rmhilast)
+ donehi++;
+ else
+ hi = (hi + 1) % RMHLEN;
+
+ break;
+
+ default:
+ donejo++;
+ donelo++;
+ donehi++;
+ break;
+ }
+ }
+
+
+ so_seek(in_store, 0L, 0);
+ gf_filter_init();
+ gf_link_filter(gf_wrap,
+ gf_wrap_filter_opt(ps_global->ttyo->screen_cols - 4,
+ ps_global->ttyo->screen_cols,
+ NULL, show_level < 0 ? 2 : 0, GFW_NONE));
+ gf_set_so_readc(&gc, in_store);
+ gf_set_so_writec(&pc, out_store);
+ gf_pipe(gc, pc);
+ gf_clear_so_writec(out_store);
+ gf_clear_so_readc(in_store);
+
+ memset(&sargs, 0, sizeof(SCROLL_S));
+ sargs.text.text = so_text(out_store);
+ sargs.text.src = CharStar;
+ sargs.text.desc = _("journal");
+ sargs.keys.menu = &rev_msg_keymenu;
+ sargs.proc.tool = journal_processor;
+ sargs.start.on = LastPage;
+ sargs.resize_exit = 1;
+ sargs.proc.data.p = (void *)&show_level;
+ setbitmap(sargs.keys.bitmap);
+
+#ifdef DEBUG
+#ifdef DEBUGJOURNAL
+ sargs.jump_is_debug = 1;
+ /* TRANSLATORS: these are some screen titles */
+ sargs.help.title = _("HELP FOR DEBUG JOURNAL");
+ sargs.help.text = h_debugjournal;
+ sargs.bar.title = _("REVIEW DEBUGGING");
+#else /* !DEBUGJOURNAL */
+ clrbitn(DEBUG_KEY, sargs.keys.bitmap);
+ sargs.help.title = _("HELP FOR JOURNAL");
+ sargs.help.text = h_journal;
+ sargs.bar.title = _("REVIEW RECENT MESSAGES");
+#endif /* !DEBUGJOURNAL */
+#else /* !DEBUG */
+ clrbitn(DEBUG_KEY, sargs.keys.bitmap);
+ clrbitn(TIMESTAMP_KEY, sargs.keys.bitmap);
+ sargs.help.title = _("HELP FOR JOURNAL");
+ sargs.help.text = h_journal;
+ sargs.bar.title = _("REVIEW RECENT MESSAGES");
+#endif /* !DEBUG */
+
+ if(timestamps)
+ rev_msg_keys[TIMESTAMP_KEY].label = notimestampkeylabel;
+ else
+ rev_msg_keys[TIMESTAMP_KEY].label = timestampkeylabel;
+
+ if(show_level >= 0)
+ /* TRANSLATORS: shows what numeric level Debug output is displayed at */
+ snprintf(debugkeylabel, sizeof(debugkeylabel), _("Debug (%d)"), show_level);
+ else
+ /* TRANSLATORS: include debug output in journal */
+ strncpy(debugkeylabel, _("DebugView"), sizeof(debugkeylabel));
+
+ debugkeylabel[sizeof(debugkeylabel)-1] = '\0';
+
+ rev_msg_keys[DEBUG_KEY].label = debugkeylabel;
+ KS_OSDATASET(&rev_msg_keys[DEBUG_KEY], KS_NONE);
+
+ if((cmd = scrolltool(&sargs)) == MC_TOGGLE)
+ timestamps = !timestamps;
+
+ so_give(&in_store);
+ so_give(&out_store);
+
+ }while(cmd != MC_EXIT);
+
+ rm_not_right_now = 0;
+ add_review_message("Done reviewing", 0);
+}
+
+
+int
+journal_processor(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
+{
+ switch(cmd){
+ case MC_TOGGLE: /* turn timestamps on or off */
+ break;
+
+ default:
+ panic("Unexpected command in journal_processor");
+ break;
+ }
+
+ return(1);
+}
+
+
+/*
+ * standard type of storage object used for body parts...
+ */
+#ifdef DOS
+#define PART_SO_TYPE TmpFileStar
+#else
+#define PART_SO_TYPE CharStar
+#endif
+
+
+int
+gripe_gripe_to(url)
+ char *url;
+{
+ char *composer_title, *url_copy, *optstr, *p;
+ int opts = 0;
+ BODY *body = NULL;
+ ENVELOPE *outgoing = NULL;
+ REPLY_S fake_reply;
+ PINEFIELD *pf = NULL;
+ long msgno = mn_m2raw(ps_global->msgmap,
+ mn_get_cur(ps_global->msgmap));
+
+ url_copy = cpystr(url + strlen("x-alpine-gripe:"));
+ if((optstr = strchr(url_copy, '?')) != NULL)
+ *optstr++ = '\0';
+
+ outgoing = mail_newenvelope();
+ outgoing->message_id = generate_message_id();
+
+ if((outgoing->to = gripe_token_addr(url_copy)) != NULL){
+ composer_title = _("COMPOSE TO LOCAL SUPPORT");
+ dprint((1,
+ "\n\n -- Send to local support(%s@%s) --\n",
+ outgoing->to->mailbox ? outgoing->to->mailbox : "NULL",
+ outgoing->to->host ? outgoing->to->host : "NULL"));
+ }
+ else{ /* must be global */
+ composer_title = _("REQUEST FOR ASSISTANCE");
+ rfc822_parse_adrlist(&outgoing->to, url_copy, ps_global->maildomain);
+ }
+
+ /*
+ * Sniff thru options
+ */
+ while(optstr){
+ if((p = strchr(optstr, '?')) != NULL) /* tie off list item */
+ *p++ = '\0';
+
+ if(!strucmp(optstr, "config"))
+ opts |= GRIPE_OPT_CONF;
+ else if(!strucmp(optstr, "curmsg"))
+ opts |= GRIPE_OPT_MSG;
+ else if(!strucmp(optstr, "local"))
+ opts |= GRIPE_OPT_LOCAL;
+ else if(!strucmp(optstr, "keys"))
+ opts |= GRIPE_OPT_KEYS;
+
+ optstr = p;
+ }
+
+ /* build body and hand off to composer... */
+ if(gripe_newbody(ps_global, &body, msgno, opts) == 0){
+ pf = (PINEFIELD *) fs_get(sizeof(PINEFIELD));
+ memset(pf, 0, sizeof(PINEFIELD));
+ pf->name = cpystr("X-Generated-Via");
+ pf->type = FreeText;
+ pf->textbuf = gripe_id("Alpine Bug Report screen");
+ memset((void *)&fake_reply, 0, sizeof(fake_reply));
+ fake_reply.pseudo = 1;
+ fake_reply.data.pico_flags = P_HEADEND;
+ pine_send(outgoing, &body, composer_title, NULL, NULL,
+ &fake_reply, NULL, NULL, pf, PS_STICKY_TO);
+ }
+
+ ps_global->mangled_screen = 1;
+ mail_free_envelope(&outgoing);
+
+ if(body)
+ pine_free_body(&body);
+
+ fs_give((void **) &url_copy);
+
+ return(10);
+}
+
+
+int
+gripe_newbody(ps, body, msgno, flags)
+ struct pine *ps;
+ BODY **body;
+ long msgno;
+ int flags;
+{
+ BODY *pb;
+ PART **pp;
+ STORE_S *store;
+ gf_io_t pc;
+ static char *err = "Problem creating space for message text.";
+ int i;
+ char tmp[MAILTMPLEN], *p;
+
+ if((store = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
+ *body = mail_newbody();
+
+ if((p = detoken(NULL, NULL, 2, 0, 1, NULL, NULL)) != NULL){
+ if(*p)
+ so_puts(store, p);
+
+ fs_give((void **) &p);
+ }
+ }
+ else{
+ q_status_message(SM_ORDER | SM_DING, 3, 4, err);
+ return(-1);
+ }
+
+ if(flags){
+ /*---- Might have multiple parts ----*/
+ (*body)->type = TYPEMULTIPART;
+ /*---- The TEXT part/body ----*/
+ (*body)->nested.part = mail_newbody_part();
+ (*body)->nested.part->body.type = TYPETEXT;
+ (*body)->nested.part->body.contents.text.data = (void *) store;
+
+ /*---- create object, and write current config into it ----*/
+ pp = &((*body)->nested.part->next);
+
+ if(flags & GRIPE_OPT_CONF){
+ *pp = mail_newbody_part();
+ pb = &((*pp)->body);
+ pp = &((*pp)->next);
+ pb->type = TYPETEXT;
+ pb->id = generate_message_id();
+ pb->description = cpystr("Alpine Configuration Data");
+ pb->parameter = mail_newbody_parameter();
+ pb->parameter->attribute = cpystr("name");
+ pb->parameter->value = cpystr("config.txt");
+
+ if((store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
+ extern char datestamp[], hoststamp[];
+
+ pb->contents.text.data = (void *) store;
+ gf_set_so_writec(&pc, store);
+ gf_puts("Alpine built ", pc);
+ gf_puts(datestamp, pc);
+ gf_puts(" on host: ", pc);
+ gf_puts(hoststamp, pc);
+ gf_puts("\n", pc);
+
+#ifdef DEBUG
+ dump_pine_struct(ps, pc);
+ dump_config(ps, pc, 0);
+#endif /* DEBUG */
+
+ pb->size.bytes = strlen((char *) so_text(store));
+ gf_clear_so_writec(store);
+ }
+ else{
+ q_status_message(SM_ORDER | SM_DING, 3, 4, err);
+ return(-1);
+ }
+ }
+
+ if(flags & GRIPE_OPT_KEYS){
+ *pp = mail_newbody_part();
+ pb = &((*pp)->body);
+ pp = &((*pp)->next);
+ pb->type = TYPETEXT;
+ pb->id = generate_message_id();
+ pb->description = cpystr("Recent User Input");
+ pb->parameter = mail_newbody_parameter();
+ pb->parameter->attribute = cpystr("name");
+ pb->parameter->value = cpystr("uinput.txt");
+
+ if((store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
+ pb->contents.text.data = (void *) store;
+
+ so_puts(store, "User's most recent input:\n");
+
+ /* dump last n keystrokes */
+ so_puts(store, "========== Latest keystrokes ==========\n");
+ while((i = key_playback(0)) != -1){
+ snprintf(tmp, sizeof(tmp), "\t%s\t(0x%x)\n", pretty_command(i), i);
+ tmp[sizeof(tmp)-1] = '\0';
+ so_puts(store, tmp);
+ }
+
+ pb->size.bytes = strlen((char *) so_text(store));
+ }
+ else{
+ q_status_message(SM_ORDER | SM_DING, 3, 4, err);
+ return(-1);
+ }
+ }
+
+ /* check for local debugging info? */
+ if((flags & GRIPE_OPT_LOCAL)
+ && ps_global->VAR_BUGS_EXTRAS
+ && can_access(ps_global->VAR_BUGS_EXTRAS, EXECUTE_ACCESS) == 0){
+ char *error = NULL;
+
+ *pp = mail_newbody_part();
+ pb = &((*pp)->body);
+ pp = &((*pp)->next);
+ pb->type = TYPETEXT;
+ pb->id = generate_message_id();
+ pb->description = cpystr("Local Configuration Data");
+ pb->parameter = mail_newbody_parameter();
+ pb->parameter->attribute = cpystr("name");
+ pb->parameter->value = cpystr("lconfig.txt");
+
+ if((store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
+ PIPE_S *syspipe;
+ gf_io_t gc;
+
+ pb->contents.text.data = (void *) store;
+ gf_set_so_writec(&pc, store);
+ if((syspipe = open_system_pipe(ps_global->VAR_BUGS_EXTRAS,
+ NULL, NULL,
+ PIPE_READ | PIPE_STDERR | PIPE_USER,
+ 0, pipe_callback, pipe_report_error)) != NULL){
+ gf_set_readc(&gc, (void *)syspipe, 0, PipeStar, 0);
+ gf_filter_init();
+ error = gf_pipe(gc, pc);
+ (void) close_system_pipe(&syspipe, NULL, pipe_callback);
+ }
+ else
+ error = "executing config collector";
+
+ gf_clear_so_writec(store);
+ }
+
+ if(error){
+ q_status_message1(SM_ORDER | SM_DING, 3, 4,
+ "Problem %s", error);
+ return(-1);
+ }
+ else /* fixup attachment's size */
+ pb->size.bytes = strlen((char *) so_text(store));
+ }
+
+ if((flags & GRIPE_OPT_MSG) && mn_get_total(ps->msgmap) > 0L){
+ int ch = 0;
+
+ ps->redrawer = att_cur_drawer;
+ att_cur_drawer();
+
+ if((ch = one_try_want_to("Attach current message to report",
+ 'y','x',NO_HELP,
+ WT_FLUSH_IN|WT_SEQ_SENSITIVE)) == 'y'){
+ *pp = mail_newbody_part();
+ pb = &((*pp)->body);
+ pp = &((*pp)->next);
+ pb->type = TYPEMESSAGE;
+ pb->id = generate_message_id();
+ snprintf(tmp, sizeof(tmp), "Problem Message (%ld of %ld)",
+ mn_get_cur(ps->msgmap), mn_get_total(ps->msgmap));
+ tmp[sizeof(tmp)-1] = '\0';
+ pb->description = cpystr(tmp);
+
+ /*---- Package each message in a storage object ----*/
+ if((store = so_get(PART_SO_TYPE, NULL, EDIT_ACCESS)) != NULL){
+ pb->contents.text.data = (void *) store;
+ }
+ else{
+ q_status_message(SM_ORDER | SM_DING, 3, 4, err);
+ return(-1);
+ }
+
+ /* write the header */
+ if((p = mail_fetch_header(ps->mail_stream, msgno, NIL, NIL,
+ NIL, FT_PEEK)) && *p)
+ so_puts(store, p);
+ else
+ return(-1);
+
+ pb->size.bytes = strlen(p);
+ so_puts(store, "\015\012");
+
+ if((p = pine_mail_fetch_text(ps->mail_stream,
+ msgno, NULL, NULL, NIL))
+ && *p)
+ so_puts(store, p);
+ else
+ return(-1);
+
+ pb->size.bytes += strlen(p);
+ }
+ else if(ch == 'x'){
+ q_status_message(SM_ORDER, 0, 3, "Bug report cancelled.");
+ return(-1);
+ }
+ }
+ }
+ else{
+ /*---- Only one part! ----*/
+ (*body)->type = TYPETEXT;
+ (*body)->contents.text.data = (void *) store;
+ }
+
+ return(0);
+}
+
+
+ADDRESS *
+gripe_token_addr(token)
+ char *token;
+{
+ char *p;
+ ADDRESS *a = NULL;
+
+ if(token && *token++ == '_'){
+ if(!strcmp(token, "LOCAL_ADDRESS_")){
+ p = (ps_global->VAR_LOCAL_ADDRESS
+ && ps_global->VAR_LOCAL_ADDRESS[0])
+ ? ps_global->VAR_LOCAL_ADDRESS
+ : "postmaster";
+ a = rfc822_parse_mailbox(&p, ps_global->maildomain);
+ a->personal = cpystr((ps_global->VAR_LOCAL_FULLNAME
+ && ps_global->VAR_LOCAL_FULLNAME[0])
+ ? ps_global->VAR_LOCAL_FULLNAME
+ : "Place to report Alpine Bugs");
+ }
+ else if(!strcmp(token, "BUGS_ADDRESS_")){
+ p = (ps_global->VAR_BUGS_ADDRESS
+ && ps_global->VAR_BUGS_ADDRESS[0])
+ ? ps_global->VAR_BUGS_ADDRESS : "postmaster";
+ a = rfc822_parse_mailbox(&p, ps_global->maildomain);
+ a->personal = cpystr((ps_global->VAR_BUGS_FULLNAME
+ && ps_global->VAR_BUGS_FULLNAME[0])
+ ? ps_global->VAR_BUGS_FULLNAME
+ : "Place to report Alpine Bugs");
+ }
+ }
+
+ return(a);
+}
+
+
+char *
+gripe_id(key)
+ char *key;
+{
+ int i,j,k,l;
+
+ /*
+ * Build our contribution to the subject; part constant string
+ * and random 4 character alpha numeric string.
+ */
+ i = (int)(random() % 36L);
+ j = (int)(random() % 36L);
+ k = (int)(random() % 36L);
+ l = (int)(random() % 36L);
+ tmp_20k_buf[0] = '\0';
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s (ID %c%c%d%c%c)", key,
+ (i < 10) ? '0' + i : 'A' + (i - 10),
+ (j < 10) ? '0' + j : 'A' + (j - 10),
+ (int)(random() % 10L),
+ (k < 10) ? '0' + k : 'A' + (k - 10),
+ (l < 10) ? '0' + l : 'A' + (l - 10));
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ return(cpystr(tmp_20k_buf));
+}
+
+
+/*
+ * Used by gripe_tool.
+ */
+void
+att_cur_drawer(void)
+{
+ int i, dline, j;
+ char buf[256+1];
+
+ /* blat helpful message to screen */
+ ClearBody();
+ j = 0;
+ for(dline = 2;
+ dline < ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global);
+ dline++){
+ for(i = 0; i < 256 && att_cur_msg[j] && att_cur_msg[j] != '\n'; i++)
+ buf[i] = att_cur_msg[j++];
+
+ buf[i] = '\0';
+ if(att_cur_msg[j])
+ j++;
+ else if(!i)
+ break;
+
+ PutLine0(dline, 1, buf);
+ }
+}
+
+
+#ifdef _WINDOWS
+
+/*
+ *
+ */
+char *
+pcpine_help(HelpType section)
+{
+ char **help_lines, *help_text = NULL;
+ STORE_S *store;
+ gf_io_t pc;
+
+ /* assumption here is that HelpType is char ** */
+ help_lines = section;
+ if (help_lines != NULL){
+ init_helper_getc(help_lines);
+ if(store = so_get(CharStar, NULL, EDIT_ACCESS)){
+ gf_set_so_writec(&pc, store);
+ gf_filter_init();
+
+ gf_link_filter(gf_local_nvtnl, NULL);
+
+ gf_link_filter(gf_html2plain,
+ gf_html2plain_opt(NULL,
+ ps_global->ttyo->screen_cols,
+ non_messageview_margin(), NULL, NULL, GFHP_STRIPPED));
+
+ if(!gf_pipe(helper_getc, pc)){
+ help_text = (char *) store->txt;
+ store->txt = (void *) NULL;
+ }
+
+ gf_clear_so_writec(store);
+ so_give(&store);
+ }
+ }
+
+ return(help_text);
+}
+
+
+/*
+ *
+ */
+int
+help_popup(SCROLL_S *sparms, int in_handle)
+{
+ MPopup hp_menu[10];
+ int i = -1;
+
+ if(in_handle){
+ hp_menu[++i].type = tQueue;
+ hp_menu[i].label.style = lNormal;
+ hp_menu[i].label.string = "View Help Section";
+ hp_menu[i].data.val = 'V';
+ }
+
+ hp_menu[++i].type = tQueue;
+ hp_menu[i].label.style = lNormal;
+ hp_menu[i].label.string = "Exit Help";
+ hp_menu[i].data.val = 'E';
+
+ hp_menu[++i].type = tTail;
+
+ mswin_popup(hp_menu);
+
+ return(0);
+}
+
+
+/*
+ *
+ */
+int
+help_subsection_popup(SCROLL_S *sparms, int in_handle)
+{
+ MPopup hp_menu[10];
+ int i = -1;
+
+ if(in_handle){
+ hp_menu[++i].type = tQueue;
+ hp_menu[i].label.style = lNormal;
+ hp_menu[i].label.string = "View Help Section";
+ hp_menu[i].data.val = 'V';
+ }
+
+ hp_menu[++i].type = tQueue;
+ hp_menu[i].label.style = lNormal;
+ hp_menu[i].label.string = "Previous Help Section";
+ hp_menu[i].data.val = 'P';
+
+ hp_menu[++i].type = tQueue;
+ hp_menu[i].label.style = lNormal;
+ hp_menu[i].label.string = "Exit Help";
+ hp_menu[i].data.val = 'E';
+
+ hp_menu[++i].type = tTail;
+
+ if(mswin_popup(hp_menu) == (in_handle ? 1 : 0))
+ /*(void) helper_internal()*/;
+
+ return(0);
+}
+
+#endif /* _WINDOWS */
diff --git a/alpine/help.h b/alpine/help.h
new file mode 100644
index 00000000..51cd5d0f
--- /dev/null
+++ b/alpine/help.h
@@ -0,0 +1,51 @@
+/*
+ * $Id: help.h 769 2007-10-24 00:15:40Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_HELP_INCLUDED
+#define PINE_HELP_INCLUDED
+
+
+#include "../pith/help.h"
+
+
+/*
+ * Flags to help manage help display
+ */
+#define HLPD_NONE 0
+#define HLPD_NEWWIN 0x01
+#define HLPD_SECONDWIN 0x02
+#define HLPD_SIMPLE 0x04
+#define HLPD_FROMHELP 0x08
+
+
+/* exported protoypes */
+int url_local_helper(char *);
+int url_local_config(char *);
+void init_helper_getc(char **);
+int helper_getc(char *);
+int helper(HelpType, char *, int);
+void review_messages(void);
+void print_help(char **);
+#ifdef _WINDOWS
+char *pcpine_help(HelpType);
+#endif
+#ifdef DEBUG
+void dump_config(struct pine *, gf_io_t, int);
+void dump_pine_struct(struct pine *, gf_io_t);
+#endif
+
+
+#endif /* PINE_HELP_INCLUDED */
diff --git a/alpine/imap.c b/alpine/imap.c
new file mode 100644
index 00000000..074b9f6d
--- /dev/null
+++ b/alpine/imap.c
@@ -0,0 +1,3011 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: imap.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2009 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ imap.c
+ The call back routines for the c-client/imap
+ - handles error messages and other notification
+ - handles prelimirary notification of new mail and expunged mail
+ - prompting for imap server login and password
+
+ ====*/
+
+#include "headers.h"
+#include "alpine.h"
+#include "imap.h"
+#include "status.h"
+#include "mailview.h"
+#include "mailcmd.h"
+#include "radio.h"
+#include "keymenu.h"
+#include "signal.h"
+#include "mailpart.h"
+#include "mailindx.h"
+#include "arg.h"
+#include "busy.h"
+#include "titlebar.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/msgno.h"
+#include "../pith/filter.h"
+#include "../pith/news.h"
+#include "../pith/util.h"
+#include "../pith/list.h"
+#include "../pith/margin.h"
+#ifdef SMIME
+#include "../pith/smime.h"
+#endif /* SMIME */
+
+#if (WINCRED > 0)
+#include <wincred.h>
+#define TNAME "UWash_Alpine_"
+#define TNAMESTAR "UWash_Alpine_*"
+
+/*
+ * WinCred Function prototypes
+ */
+typedef BOOL (WINAPI CREDWRITEW) ( __in PCREDENTIALW Credential, __in DWORD Flags );
+typedef BOOL (WINAPI CREDENUMERATEW) ( __in LPCWSTR Filter, __reserved DWORD Flags,
+ __out DWORD *Count, __deref_out_ecount(*Count) PCREDENTIALW **Credential );
+typedef BOOL (WINAPI CREDDELETEW) ( __in LPCWSTR TargetName, __in DWORD Type,
+ __reserved DWORD Flags );
+typedef VOID (WINAPI CREDFREE) ( __in PVOID Buffer );
+
+/*
+ * WinCred functions
+ */
+int g_CredInited = 0; /* 1 for loaded successfully,
+ * -1 for not available.
+ * 0 for not initialized yet.
+ */
+CREDWRITEW *g_CredWriteW;
+CREDENUMERATEW *g_CredEnumerateW;
+CREDDELETEW *g_CredDeleteW;
+CREDFREE *g_CredFree;
+
+#endif /* WINCRED */
+
+#ifdef APPLEKEYCHAIN
+#include <Security/SecKeychain.h>
+#include <Security/SecKeychainItem.h>
+#include <Security/SecKeychainSearch.h>
+#define TNAME "UWash_Alpine"
+#define TNAMEPROMPT "UWash_Alpine_Prompt_For_Password"
+
+int macos_store_pass_prompt(void);
+void macos_set_store_pass_prompt(int);
+
+static int storepassprompt = -1;
+#endif /* APPLEKEYCHAIN */
+
+
+/*
+ * Internal prototypes
+ */
+void mm_login_alt_cue(NETMBX *);
+long pine_tcptimeout_noscreen(long, long, char *);
+int answer_cert_failure(int, MSGNO_S *, SCROLL_S *);
+
+#ifdef LOCAL_PASSWD_CACHE
+int read_passfile(char *, MMLOGIN_S **);
+void write_passfile(char *, MMLOGIN_S *);
+int preserve_prompt(void);
+void update_passfile_hostlist(char *, char *, STRLIST_S *, int);
+
+static MMLOGIN_S *passfile_cache = NULL;
+static int using_passfile = -1;
+int save_password = 1;
+#endif /* LOCAL_PASSWD_CACHE */
+
+#ifdef PASSFILE
+char xlate_in(int);
+char xlate_out(char);
+char *passfile_name(char *, char *, size_t);
+int line_get(char *, size_t, char **);
+#endif /* PASSFILE */
+
+#if (WINCRED > 0)
+void ask_erase_credentials(void);
+int init_wincred_funcs(void);
+#endif /* WINCRED */
+
+
+static char *details_cert, *details_host, *details_reason;
+
+
+/*----------------------------------------------------------------------
+ recieve notification from IMAP
+
+ Args: stream -- Mail stream message is relavant to
+ string -- The message text
+ errflg -- Set if it is a serious error
+
+ Result: message displayed in status line
+
+ The facility is for general notices, such as connection to server;
+ server shutting down etc... It is used infrequently.
+ ----------------------------------------------------------------------*/
+void
+mm_notify(MAILSTREAM *stream, char *string, long int errflg)
+{
+ time_t now;
+ struct tm *tm_now;
+
+ now = time((time_t *)0);
+ tm_now = localtime(&now);
+
+ /* be sure to log the message... */
+#ifdef DEBUG
+ if(ps_global->debug_imap || ps_global->debugmem)
+ dprint((errflg == TCPDEBUG ? 7 : 2,
+ "IMAP %2.2d:%2.2d:%2.2d %d/%d mm_notify %s: %s: %s\n",
+ tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec,
+ tm_now->tm_mon+1, tm_now->tm_mday,
+ (!errflg) ? "babble" :
+ (errflg == ERROR) ? "error" :
+ (errflg == WARN) ? "warning" :
+ (errflg == PARSE) ? "parse" :
+ (errflg == TCPDEBUG) ? "tcp" :
+ (errflg == BYE) ? "bye" : "unknown",
+ (stream && stream->mailbox) ? stream->mailbox : "-no folder-",
+ string ? string : "?"));
+#endif
+
+ snprintf(ps_global->last_error, sizeof(ps_global->last_error), "%s : %.*s",
+ (stream && stream->mailbox) ? stream->mailbox : "-no folder-",
+ MIN(MAX_SCREEN_COLS, sizeof(ps_global->last_error)-70),
+ string);
+ ps_global->last_error[ps_global->ttyo ? ps_global->ttyo->screen_cols
+ : sizeof(ps_global->last_error)-1] = '\0';
+
+ /*
+ * Then either set special bits in the pine struct or
+ * display the message if it's tagged as an "ALERT" or
+ * its errflg > NIL (i.e., WARN, or ERROR)
+ */
+ if(errflg == BYE)
+ /*
+ * We'd like to sp_mark_stream_dead() here but we can't do that because
+ * that might call mail_close and we are already in a c-client callback.
+ * So just set the dead bit and clean it up later.
+ */
+ sp_set_dead_stream(stream, 1);
+ else if(!strncmp(string, "[TRYCREATE]", 11))
+ ps_global->try_to_create = 1;
+ else if(!strncmp(string, "[REFERRAL ", 10))
+ ; /* handled in the imap_referral() callback */
+ else if(!strncmp(string, "[ALERT]", 7))
+ q_status_message2(SM_MODAL, 3, 3,
+ _("Alert received while accessing \"%s\": %s"),
+ (stream && stream->mailbox)
+ ? stream->mailbox : "-no folder-",
+ rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+10000),
+ SIZEOF_20KBUF-10000, string));
+ else if(!strncmp(string, "[UNSEEN ", 8)){
+ char *p;
+ long n = 0;
+
+ for(p = string + 8; isdigit(*p); p++)
+ n = (n * 10) + (*p - '0');
+
+ sp_set_first_unseen(stream, n);
+ }
+ else if(!strncmp(string, "[READ-ONLY]", 11)
+ && !(stream && stream->mailbox && IS_NEWS(stream)))
+ q_status_message2(SM_ORDER | SM_DING, 3, 3, "%s : %s",
+ (stream && stream->mailbox)
+ ? stream->mailbox : "-no folder-",
+ string + 11);
+ else if((errflg && errflg != BYE && errflg != PARSE)
+ && !ps_global->noshow_error
+ && !(errflg == WARN
+ && (ps_global->noshow_warn || (stream && stream->unhealthy))))
+ q_status_message(SM_ORDER | ((errflg == ERROR) ? SM_DING : 0),
+ 3, 6, ps_global->last_error);
+}
+
+
+/*----------------------------------------------------------------------
+ Queue imap log message for display in the message line
+
+ Args: string -- The message
+ errflg -- flag set to 1 if pertains to an error
+
+ Result: Message queued for display
+
+ The c-client/imap reports most of it's status and errors here
+ ---*/
+void
+mm_log(char *string, long int errflg)
+{
+ char message[sizeof(ps_global->c_client_error)];
+ char *occurence;
+ int was_capitalized;
+ static char saw_kerberos_init_warning;
+ time_t now;
+ struct tm *tm_now;
+
+ now = time((time_t *)0);
+ tm_now = localtime(&now);
+
+ dprint((((errflg == TCPDEBUG) && ps_global->debug_tcp) ? 1 :
+ (errflg == TCPDEBUG) ? 10 : 2,
+ "IMAP %2.2d:%2.2d:%2.2d %d/%d mm_log %s: %s\n",
+ tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec,
+ tm_now->tm_mon+1, tm_now->tm_mday,
+ (!errflg) ? "babble" :
+ (errflg == ERROR) ? "error" :
+ (errflg == WARN) ? "warning" :
+ (errflg == PARSE) ? "parse" :
+ (errflg == TCPDEBUG) ? "tcp" :
+ (errflg == BYE) ? "bye" : "unknown",
+ string ? string : "?"));
+
+ if(errflg == ERROR && !strncmp(string, "[TRYCREATE]", 11)){
+ ps_global->try_to_create = 1;
+ return;
+ }
+ else if(ps_global->try_to_create
+ || !strncmp(string, "[CLOSED]", 8)
+ || (sp_dead_stream(ps_global->mail_stream) && strstr(string, "No-op")))
+ /*
+ * Don't display if creating new folder OR
+ * warning about a dead stream ...
+ */
+ return;
+
+ strncpy(message, string, sizeof(message));
+ message[sizeof(message) - 1] = '\0';
+
+ if(errflg == WARN && srchstr(message, "try running kinit") != NULL){
+ if(saw_kerberos_init_warning)
+ return;
+
+ saw_kerberos_init_warning = 1;
+ }
+
+ /*---- replace all "mailbox" with "folder" ------*/
+ occurence = srchstr(message, "mailbox");
+ while(occurence) {
+ if(!*(occurence+7)
+ || isspace((unsigned char) *(occurence+7))
+ || *(occurence+7) == ':'){
+ was_capitalized = isupper((unsigned char) *occurence);
+ rplstr(occurence, sizeof(message)-(occurence-message), 7, (errflg == PARSE ? "address" : "folder"));
+ if(was_capitalized)
+ *occurence = (errflg == PARSE ? 'A' : 'F');
+ }
+ else
+ occurence += 7;
+
+ occurence = srchstr(occurence, "mailbox");
+ }
+
+ /*---- replace all "GSSAPI" with "Kerberos" ------*/
+ occurence = srchstr(message, "GSSAPI");
+ while(occurence) {
+ if(!*(occurence+6)
+ || isspace((unsigned char) *(occurence+6))
+ || *(occurence+6) == ':')
+ rplstr(occurence, sizeof(message)-(occurence-message), 6, "Kerberos");
+ else
+ occurence += 6;
+
+ occurence = srchstr(occurence, "GSSAPI");
+ }
+
+ if(errflg == ERROR)
+ ps_global->mm_log_error = 1;
+
+ if(errflg == PARSE || (errflg == ERROR && ps_global->noshow_error))
+ strncpy(ps_global->c_client_error, message,
+ sizeof(ps_global->c_client_error));
+
+ if(ps_global->noshow_error
+ || (ps_global->noshow_warn && errflg == WARN)
+ || !(errflg == ERROR || errflg == WARN))
+ return; /* Only care about errors; don't print when asked not to */
+
+ /*---- Display the message ------*/
+ q_status_message((errflg == ERROR) ? (SM_ORDER | SM_DING) : SM_ORDER,
+ 3, 5, message);
+ strncpy(ps_global->last_error, message, sizeof(ps_global->last_error));
+ ps_global->last_error[sizeof(ps_global->last_error) - 1] = '\0';
+}
+
+
+void
+mm_login_work(NETMBX *mb, char *user, char *pwd, long int trial,
+ char *usethisprompt, char *altuserforcache)
+{
+ char prompt[1000], *last;
+ char port[20], non_def_port[20], insecure[20];
+ char defuser[NETMAXUSER];
+ char hostleadin[80], hostname[200], defubuf[200];
+ char logleadin[80], pwleadin[50];
+ char hostlist0[MAILTMPLEN], hostlist1[MAILTMPLEN];
+ /* TRANSLATORS: when logging in, this text is added to the prompt to show
+ that the password will be sent unencrypted over the network. This is
+ just a warning message that gets added parenthetically when the user
+ is asked for a password. */
+ char *insec = _(" (INSECURE)");
+ /* TRANSLATORS: Retrying is shown when the user is being asked for a password
+ after having already failed at least once. */
+ char *retry = _("Retrying - ");
+ /* TRANSLATORS: A label for the hostname that the user is logging in on */
+ char *hostlabel = _("HOST");
+ /* TRANSLATORS: user is logging in as a particular user (a particular
+ login name), this is just labelling that user name. */
+ char *userlabel = _("USER");
+ STRLIST_S hostlist[2];
+ HelpType help ;
+ int len, rc, q_line, flags;
+ int oespace, avail, need, save_dont_use;
+ int save_in_init;
+ struct servent *sv;
+#if defined(_WINDOWS) || defined(LOCAL_PASSWD_CACHE)
+ int preserve_password = -1;
+#endif
+
+ dprint((9, "mm_login_work trial=%ld user=%s service=%s%s%s%s%s\n",
+ trial, mb->user ? mb->user : "(null)",
+ mb->service ? mb->service : "(null)",
+ mb->port ? " port=" : "",
+ mb->port ? comatose(mb->port) : "",
+ altuserforcache ? " altuserforcache =" : "",
+ altuserforcache ? altuserforcache : ""));
+ q_line = -(ps_global->ttyo ? ps_global->ttyo->footer_rows : 3);
+
+ save_in_init = ps_global->in_init_seq;
+ ps_global->in_init_seq = 0;
+ ps_global->no_newmail_check_from_optionally_enter = 1;
+
+ /* make sure errors are seen */
+ if(ps_global->ttyo)
+ flush_status_messages(0);
+
+ /*
+ * Add port number to hostname if going through a tunnel or something
+ */
+ non_def_port[0] = '\0';
+ if(mb->port && mb->service &&
+ (sv = getservbyname(mb->service, "tcp")) &&
+ (mb->port != ntohs(sv->s_port))){
+ snprintf(non_def_port, sizeof(non_def_port), ":%lu", mb->port);
+ non_def_port[sizeof(non_def_port)-1] = '\0';
+ dprint((9, "mm_login: using non-default port=%s\n",
+ non_def_port ? non_def_port : "?"));
+ }
+
+ /*
+ * set up host list for sybil servers...
+ */
+ if(*non_def_port){
+ strncpy(hostlist0, mb->host, sizeof(hostlist0)-1);
+ hostlist0[sizeof(hostlist0)-1] = '\0';
+ strncat(hostlist0, non_def_port, sizeof(hostlist0)-strlen(hostlist0)-1);
+ hostlist0[sizeof(hostlist0)-1] = '\0';
+ hostlist[0].name = hostlist0;
+ if(mb->orighost && mb->orighost[0] && strucmp(mb->host, mb->orighost)){
+ strncpy(hostlist1, mb->orighost, sizeof(hostlist1)-1);
+ hostlist1[sizeof(hostlist1)-1] = '\0';
+ strncat(hostlist1, non_def_port, sizeof(hostlist1)-strlen(hostlist1)-1);
+ hostlist1[sizeof(hostlist1)-1] = '\0';
+ hostlist[0].next = &hostlist[1];
+ hostlist[1].name = hostlist1;
+ hostlist[1].next = NULL;
+ }
+ else
+ hostlist[0].next = NULL;
+ }
+ else{
+ hostlist[0].name = mb->host;
+ if(mb->orighost && mb->orighost[0] && strucmp(mb->host, mb->orighost)){
+ hostlist[0].next = &hostlist[1];
+ hostlist[1].name = mb->orighost;
+ hostlist[1].next = NULL;
+ }
+ else
+ hostlist[0].next = NULL;
+ }
+
+ if(hostlist[0].name){
+ dprint((9, "mm_login: host=%s\n",
+ hostlist[0].name ? hostlist[0].name : "?"));
+ if(hostlist[0].next && hostlist[1].name){
+ dprint((9, "mm_login: orighost=%s\n", hostlist[1].name));
+ }
+ }
+
+ /*
+ * Initialize user name with either
+ * 1) /user= value in the stream being logged into,
+ * or 2) the user name we're running under.
+ *
+ * Note that VAR_USER_ID is not yet initialized if this login is
+ * the one to access the remote config file. In that case, the user
+ * can supply the username in the config file name with /user=.
+ */
+ if(trial == 0L && !altuserforcache){
+ strncpy(user, (*mb->user) ? mb->user :
+ ps_global->VAR_USER_ID ? ps_global->VAR_USER_ID : "",
+ NETMAXUSER);
+ user[NETMAXUSER-1] = '\0';
+
+ /* try last working password associated with this host. */
+ if(imap_get_passwd(mm_login_list, pwd, user, hostlist,
+ (mb->sslflag||mb->tlsflag))){
+ dprint((9, "mm_login: found a password to try\n"));
+ ps_global->no_newmail_check_from_optionally_enter = 0;
+ ps_global->in_init_seq = save_in_init;
+ return;
+ }
+
+#ifdef LOCAL_PASSWD_CACHE
+ /* check to see if there's a password left over from last session */
+ if(get_passfile_passwd(ps_global->pinerc, pwd,
+ user, hostlist, (mb->sslflag||mb->tlsflag))){
+ imap_set_passwd(&mm_login_list, pwd, user,
+ hostlist, (mb->sslflag||mb->tlsflag), 0, 0);
+ update_passfile_hostlist(ps_global->pinerc, user, hostlist,
+ (mb->sslflag||mb->tlsflag));
+ dprint((9, "mm_login: found a password in passfile to try\n"));
+ ps_global->no_newmail_check_from_optionally_enter = 0;
+ ps_global->in_init_seq = save_in_init;
+ return;
+ }
+#endif /* LOCAL_PASSWD_CACHE */
+
+ /*
+ * If no explicit user name supplied and we've not logged in
+ * with our local user name, see if we've visited this
+ * host before as someone else.
+ */
+ if(!*mb->user &&
+ ((last = imap_get_user(mm_login_list, hostlist))
+#ifdef LOCAL_PASSWD_CACHE
+ ||
+ (last = get_passfile_user(ps_global->pinerc, hostlist))
+#endif /* LOCAL_PASSWD_CACHE */
+ )){
+ strncpy(user, last, NETMAXUSER);
+ user[NETMAXUSER-1] = '\0';
+ dprint((9, "mm_login: found user=%s\n",
+ user ? user : "?"));
+
+ /* try last working password associated with this host/user. */
+ if(imap_get_passwd(mm_login_list, pwd, user, hostlist,
+ (mb->sslflag||mb->tlsflag))){
+ dprint((9,
+ "mm_login: found a password for user=%s to try\n",
+ user ? user : "?"));
+ ps_global->no_newmail_check_from_optionally_enter = 0;
+ ps_global->in_init_seq = save_in_init;
+ return;
+ }
+
+#ifdef LOCAL_PASSWD_CACHE
+ /* check to see if there's a password left over from last session */
+ if(get_passfile_passwd(ps_global->pinerc, pwd,
+ user, hostlist, (mb->sslflag||mb->tlsflag))){
+ imap_set_passwd(&mm_login_list, pwd, user,
+ hostlist, (mb->sslflag||mb->tlsflag), 0, 0);
+ update_passfile_hostlist(ps_global->pinerc, user, hostlist,
+ (mb->sslflag||mb->tlsflag));
+ dprint((9,
+ "mm_login: found a password for user=%s in passfile to try\n",
+ user ? user : "?"));
+ ps_global->no_newmail_check_from_optionally_enter = 0;
+ ps_global->in_init_seq = save_in_init;
+ return;
+ }
+#endif /* LOCAL_PASSWD_CACHE */
+ }
+
+#if !defined(DOS) && !defined(OS2)
+ if(!*mb->user && !*user &&
+ (last = (ps_global->ui.login && ps_global->ui.login[0])
+ ? ps_global->ui.login : NULL)
+ ){
+ strncpy(user, last, NETMAXUSER);
+ user[NETMAXUSER-1] = '\0';
+ dprint((9, "mm_login: found user=%s\n",
+ user ? user : "?"));
+
+ /* try last working password associated with this host. */
+ if(imap_get_passwd(mm_login_list, pwd, user, hostlist,
+ (mb->sslflag||mb->tlsflag))){
+ dprint((9, "mm_login:ui: found a password to try\n"));
+ ps_global->no_newmail_check_from_optionally_enter = 0;
+ ps_global->in_init_seq = save_in_init;
+ return;
+ }
+
+#ifdef LOCAL_PASSWD_CACHE
+ /* check to see if there's a password left over from last session */
+ if(get_passfile_passwd(ps_global->pinerc, pwd,
+ user, hostlist, (mb->sslflag||mb->tlsflag))){
+ imap_set_passwd(&mm_login_list, pwd, user,
+ hostlist, (mb->sslflag||mb->tlsflag), 0, 0);
+ update_passfile_hostlist(ps_global->pinerc, user, hostlist,
+ (mb->sslflag||mb->tlsflag));
+ dprint((9, "mm_login:ui: found a password in passfile to try\n"));
+ ps_global->no_newmail_check_from_optionally_enter = 0;
+ ps_global->in_init_seq = save_in_init;
+ return;
+ }
+#endif /* LOCAL_PASSWD_CACHE */
+ }
+#endif
+ }
+
+ user[NETMAXUSER-1] = '\0';
+
+ if(trial == 0)
+ retry = "";
+
+ /*
+ * Even if we have a user now, user gets a chance to change it.
+ */
+ ps_global->mangled_footer = 1;
+ if(!*mb->user && !altuserforcache){
+
+ help = NO_HELP;
+
+ /*
+ * Instead of offering user with a value that the user can edit,
+ * we offer [user] as a default so that the user can type CR to
+ * use it. Otherwise, the user has to type in whole name.
+ */
+ strncpy(defuser, user, sizeof(defuser)-1);
+ defuser[sizeof(defuser)-1] = '\0';
+ user[0] = '\0';
+
+ /*
+ * Need space for "Retrying - "
+ * "+ HOST: "
+ * hostname
+ * " (INSECURE)"
+ * ENTER LOGIN NAME
+ * " [defuser] : "
+ * about 15 chars for input
+ */
+
+ snprintf(hostleadin, sizeof(hostleadin), "%s%s: ",
+ (!ps_global->ttyo && (mb->sslflag||mb->tlsflag)) ? "+ " : "", hostlabel);
+ hostleadin[sizeof(hostleadin)-1] = '\0';
+
+ strncpy(hostname, mb->host, sizeof(hostname)-1);
+ hostname[sizeof(hostname)-1] = '\0';
+
+ /*
+ * Add port number to hostname if going through a tunnel or something
+ */
+ if(*non_def_port)
+ strncpy(port, non_def_port, sizeof(port));
+ else
+ port[0] = '\0';
+
+ insecure[0] = '\0';
+ /* if not encrypted and SSL/TLS is supported */
+ if(!(mb->sslflag||mb->tlsflag) &&
+ mail_parameters(NIL, GET_SSLDRIVER, NIL))
+ strncpy(insecure, insec, sizeof(insecure));
+
+ /* TRANSLATORS: user is being asked to type in their login name */
+ snprintf(logleadin, sizeof(logleadin), " %s", _("ENTER LOGIN NAME"));
+
+ snprintf(defubuf, sizeof(defubuf), "%s%s%s : ", (*defuser) ? " [" : "",
+ (*defuser) ? defuser : "",
+ (*defuser) ? "]" : "");
+ defubuf[sizeof(defubuf)-1] = '\0';
+ /* space reserved after prompt */
+ oespace = MAX(MIN(15, (ps_global->ttyo ? ps_global->ttyo->screen_cols : 80)/5), 6);
+
+ avail = ps_global->ttyo ? ps_global->ttyo->screen_cols : 80;
+ need = utf8_width(retry) + utf8_width(hostleadin) + strlen(hostname) + strlen(port) +
+ utf8_width(insecure) + utf8_width(logleadin) + strlen(defubuf) + oespace;
+
+ /* If we're retrying cut the hostname back to the first word. */
+ if(avail < need && trial > 0){
+ char *p;
+
+ len = strlen(hostname);
+ if((p = strchr(hostname, '.')) != NULL){
+ *p = '\0';
+ need -= (len - strlen(hostname));
+ }
+ }
+
+ if(avail < need){
+ need -= utf8_width(retry);
+ retry = "";
+
+ if(avail < need){
+
+ /* reduce length of logleadin */
+ len = utf8_width(logleadin);
+ /* TRANSLATORS: An abbreviated form of ENTER LOGIN NAME because
+ longer version doesn't fit on screen */
+ snprintf(logleadin, sizeof(logleadin), " %s", _("LOGIN"));
+ need -= (len - utf8_width(logleadin));
+
+ if(avail < need){
+ /* get two spaces from hostleadin */
+ len = utf8_width(hostleadin);
+ snprintf(hostleadin, sizeof(hostleadin), "%s%s:",
+ (!ps_global->ttyo && (mb->sslflag||mb->tlsflag)) ? "+" : "", hostlabel);
+ hostleadin[sizeof(hostleadin)-1] = '\0';
+ need -= (len - utf8_width(hostleadin));
+
+ /* get rid of port */
+ if(avail < need && strlen(port) > 0){
+ need -= strlen(port);
+ port[0] = '\0';
+ }
+
+ if(avail < need){
+ int reduce_to;
+
+ /*
+ * Reduce space for hostname. Best we can do is 6 chars
+ * with hos...
+ */
+ reduce_to = (need - avail < strlen(hostname) - 6) ? (strlen(hostname)-(need-avail)) : 6;
+ len = strlen(hostname);
+ strncpy(hostname+reduce_to-3, "...", 4);
+ need -= (len - strlen(hostname));
+
+ if(avail < need && strlen(insecure) > 0){
+ if(need - avail <= 3 && !strcmp(insecure," (INSECURE)")){
+ need -= 3;
+ insecure[strlen(insecure)-4] = ')';
+ insecure[strlen(insecure)-3] = '\0';
+ }
+ else{
+ need -= utf8_width(insecure);
+ insecure[0] = '\0';
+ }
+ }
+
+ if(avail < need){
+ if(strlen(defubuf) > 3){
+ len = strlen(defubuf);
+ strncpy(defubuf, " [..] :", 9);
+ need -= (len - strlen(defubuf));
+ }
+
+ if(avail < need)
+ strncpy(defubuf, ":", 2);
+
+ /*
+ * If it still doesn't fit, optionally_enter gets
+ * to worry about it.
+ */
+ }
+ }
+ }
+ }
+ }
+
+ snprintf(prompt, sizeof(prompt), "%s%s%s%s%s%s%s",
+ retry, hostleadin, hostname, port, insecure, logleadin, defubuf);
+ prompt[sizeof(prompt)-1] = '\0';
+
+ while(1) {
+ if(ps_global->ttyo)
+ mm_login_alt_cue(mb);
+
+ flags = OE_APPEND_CURRENT;
+ save_dont_use = ps_global->dont_use_init_cmds;
+ ps_global->dont_use_init_cmds = 1;
+#ifdef _WINDOWS
+ if(!*user && *defuser){
+ strncpy(user, defuser, NETMAXUSER);
+ user[NETMAXUSER-1] = '\0';
+ }
+
+ rc = os_login_dialog(mb, user, NETMAXUSER, pwd, NETMAXPASSWD,
+#ifdef LOCAL_PASSWD_CACHE
+ is_using_passfile() ? 1 :
+#endif /* LOCAL_PASSWD_CACHE */
+ 0, 0, &preserve_password);
+ ps_global->dont_use_init_cmds = save_dont_use;
+ if(rc == 0 && *user && *pwd)
+ goto nopwpmt;
+#else /* !_WINDOWS */
+ rc = optionally_enter(user, q_line, 0, NETMAXUSER,
+ prompt, NULL, help, &flags);
+#endif /* !_WINDOWS */
+ ps_global->dont_use_init_cmds = save_dont_use;
+
+ if(rc == 3) {
+ help = help == NO_HELP ? h_oe_login : NO_HELP;
+ continue;
+ }
+
+ /* default */
+ if(rc == 0 && !*user){
+ strncpy(user, defuser, NETMAXUSER);
+ user[NETMAXUSER-1] = '\0';
+ }
+
+ if(rc != 4)
+ break;
+ }
+
+ if(rc == 1 || !user[0]) {
+ ps_global->user_says_cancel = (rc == 1);
+ user[0] = '\0';
+ pwd[0] = '\0';
+ }
+ }
+ else{
+ strncpy(user, mb->user, NETMAXUSER);
+ user[NETMAXUSER-1] = '\0';
+ }
+
+ user[NETMAXUSER-1] = '\0';
+ pwd[NETMAXPASSWD-1] = '\0';
+
+ if(!(user[0] || altuserforcache)){
+ ps_global->no_newmail_check_from_optionally_enter = 0;
+ ps_global->in_init_seq = save_in_init;
+ return;
+ }
+
+ /*
+ * Now that we have a user, we can check in the cache again to see
+ * if there is a password there. Try last working password associated
+ * with this host and user.
+ */
+ if(trial == 0L && !*mb->user && !altuserforcache){
+ if(imap_get_passwd(mm_login_list, pwd, user, hostlist,
+ (mb->sslflag||mb->tlsflag))){
+ ps_global->no_newmail_check_from_optionally_enter = 0;
+ ps_global->in_init_seq = save_in_init;
+ return;
+ }
+
+#ifdef LOCAL_PASSWD_CACHE
+ if(get_passfile_passwd(ps_global->pinerc, pwd,
+ user, hostlist, (mb->sslflag||mb->tlsflag))){
+ imap_set_passwd(&mm_login_list, pwd, user,
+ hostlist, (mb->sslflag||mb->tlsflag), 0, 0);
+ ps_global->no_newmail_check_from_optionally_enter = 0;
+ ps_global->in_init_seq = save_in_init;
+ return;
+ }
+#endif /* LOCAL_PASSWD_CACHE */
+ }
+ else if(trial == 0 && altuserforcache){
+ if(imap_get_passwd(mm_login_list, pwd, altuserforcache, hostlist,
+ (mb->sslflag||mb->tlsflag))){
+ ps_global->no_newmail_check_from_optionally_enter = 0;
+ ps_global->in_init_seq = save_in_init;
+ return;
+ }
+
+#ifdef LOCAL_PASSWD_CACHE
+ if(get_passfile_passwd(ps_global->pinerc, pwd,
+ altuserforcache, hostlist, (mb->sslflag||mb->tlsflag))){
+ imap_set_passwd(&mm_login_list, pwd, altuserforcache,
+ hostlist, (mb->sslflag||mb->tlsflag), 0, 0);
+ ps_global->no_newmail_check_from_optionally_enter = 0;
+ ps_global->in_init_seq = save_in_init;
+ return;
+ }
+#endif /* LOCAL_PASSWD_CACHE */
+ }
+
+ /*
+ * Didn't find password in cache or this isn't the first try. Ask user.
+ */
+ help = NO_HELP;
+
+ /*
+ * Need space for "Retrying - "
+ * "+ HOST: "
+ * hostname
+ * " (INSECURE) "
+ * " USER: "
+ * user
+ * " ENTER PASSWORD: "
+ * about 15 chars for input
+ */
+
+ snprintf(hostleadin, sizeof(hostleadin), "%s%s: ",
+ (!ps_global->ttyo && (mb->sslflag||mb->tlsflag)) ? "+ " : "", hostlabel);
+
+ strncpy(hostname, mb->host, sizeof(hostname)-1);
+ hostname[sizeof(hostname)-1] = '\0';
+
+ /*
+ * Add port number to hostname if going through a tunnel or something
+ */
+ if(*non_def_port)
+ strncpy(port, non_def_port, sizeof(port));
+ else
+ port[0] = '\0';
+
+ insecure[0] = '\0';
+
+ /* if not encrypted and SSL/TLS is supported */
+ if(!(mb->sslflag||mb->tlsflag) &&
+ mail_parameters(NIL, GET_SSLDRIVER, NIL))
+ strncpy(insecure, insec, sizeof(insecure));
+
+ if(usethisprompt){
+ strncpy(logleadin, usethisprompt, sizeof(logleadin));
+ logleadin[sizeof(logleadin)-1] = '\0';
+ defubuf[0] = '\0';
+ user[0] = '\0';
+ }
+ else{
+ snprintf(logleadin, sizeof(logleadin), " %s: ", userlabel);
+
+ strncpy(defubuf, user, sizeof(defubuf)-1);
+ defubuf[sizeof(defubuf)-1] = '\0';
+ }
+
+ /* TRANSLATORS: user is being asked to type in their password */
+ snprintf(pwleadin, sizeof(pwleadin), " %s: ", _("ENTER PASSWORD"));
+
+ /* space reserved after prompt */
+ oespace = MAX(MIN(15, (ps_global->ttyo ? ps_global->ttyo->screen_cols : 80)/5), 6);
+
+ avail = ps_global->ttyo ? ps_global->ttyo->screen_cols : 80;
+ need = utf8_width(retry) + utf8_width(hostleadin) + strlen(hostname) + strlen(port) +
+ utf8_width(insecure) + utf8_width(logleadin) + strlen(defubuf) +
+ utf8_width(pwleadin) + oespace;
+
+ if(avail < need && trial > 0){
+ char *p;
+
+ len = strlen(hostname);
+ if((p = strchr(hostname, '.')) != NULL){
+ *p = '\0';
+ need -= (len - strlen(hostname));
+ }
+ }
+
+ if(avail < need){
+ need -= utf8_width(retry);
+ retry = "";
+
+ if(avail < need){
+
+ if(!usethisprompt){
+ snprintf(logleadin, sizeof(logleadin), " %s: ", userlabel);
+ need--;
+ }
+
+ rplstr(pwleadin, sizeof(pwleadin), 1, "");
+ need--;
+
+ if(avail < need){
+ /* get two spaces from hostleadin */
+ len = utf8_width(hostleadin);
+ snprintf(hostleadin, sizeof(hostleadin), "%s%s:",
+ (!ps_global->ttyo && (mb->sslflag||mb->tlsflag)) ? "+" : "", hostlabel);
+ hostleadin[sizeof(hostleadin)-1] = '\0';
+ need -= (len - utf8_width(hostleadin));
+
+ /* get rid of port */
+ if(avail < need && strlen(port) > 0){
+ need -= strlen(port);
+ port[0] = '\0';
+ }
+
+ if(avail < need){
+ len = utf8_width(pwleadin);
+ /* TRANSLATORS: An abbreviated form of ENTER PASSWORD */
+ snprintf(pwleadin, sizeof(pwleadin), " %s: ", _("PASSWORD"));
+ need -= (len - utf8_width(pwleadin));
+ }
+ }
+
+ if(avail < need){
+ int reduce_to;
+
+ /*
+ * Reduce space for hostname. Best we can do is 6 chars
+ * with hos...
+ */
+ reduce_to = (need - avail < strlen(hostname) - 6) ? (strlen(hostname)-(need-avail)) : 6;
+ len = strlen(hostname);
+ strncpy(hostname+reduce_to-3, "...", 4);
+ need -= (len - strlen(hostname));
+
+ if(avail < need && strlen(insecure) > 0){
+ if(need - avail <= 3 && !strcmp(insecure," (INSECURE)")){
+ need -= 3;
+ insecure[strlen(insecure)-4] = ')';
+ insecure[strlen(insecure)-3] = '\0';
+ }
+ else{
+ need -= utf8_width(insecure);
+ insecure[0] = '\0';
+ }
+ }
+
+ if(avail < need){
+ len = utf8_width(logleadin);
+ strncpy(logleadin, " ", sizeof(logleadin));
+ logleadin[sizeof(logleadin)-1] = '\0';
+ need -= (len - utf8_width(logleadin));
+
+ if(avail < need){
+ reduce_to = (need - avail < strlen(defubuf) - 6) ? (strlen(defubuf)-(need-avail)) : 0;
+ if(reduce_to)
+ strncpy(defubuf+reduce_to-3, "...", 4);
+ else
+ defubuf[0] = '\0';
+ }
+ }
+ }
+ }
+ }
+
+ snprintf(prompt, sizeof(prompt), "%s%s%s%s%s%s%s%s",
+ retry, hostleadin, hostname, port, insecure, logleadin, defubuf, pwleadin);
+ prompt[sizeof(prompt)-1] = '\0';
+
+ *pwd = '\0';
+ while(1) {
+ if(ps_global->ttyo)
+ mm_login_alt_cue(mb);
+
+ save_dont_use = ps_global->dont_use_init_cmds;
+ ps_global->dont_use_init_cmds = 1;
+ flags = F_ON(F_QUELL_ASTERISKS, ps_global) ? OE_PASSWD_NOAST : OE_PASSWD;
+#ifdef _WINDOWS
+ rc = os_login_dialog(mb, user, NETMAXUSER, pwd, NETMAXPASSWD, 0, 1,
+ &preserve_password);
+#else /* !_WINDOWS */
+ rc = optionally_enter(pwd, q_line, 0, NETMAXPASSWD,
+ prompt, NULL, help, &flags);
+#endif /* !_WINDOWS */
+ ps_global->dont_use_init_cmds = save_dont_use;
+
+ if(rc == 3) {
+ help = help == NO_HELP ? h_oe_passwd : NO_HELP;
+ }
+ else if(rc == 4){
+ }
+ else
+ break;
+ }
+
+ if(rc == 1 || !pwd[0]) {
+ ps_global->user_says_cancel = (rc == 1);
+ user[0] = pwd[0] = '\0';
+ ps_global->no_newmail_check_from_optionally_enter = 0;
+ ps_global->in_init_seq = save_in_init;
+ return;
+ }
+
+#ifdef _WINDOWS
+ nopwpmt:
+#endif
+ /* remember the password for next time */
+ if(F_OFF(F_DISABLE_PASSWORD_CACHING,ps_global))
+ imap_set_passwd(&mm_login_list, pwd,
+ altuserforcache ? altuserforcache : user, hostlist,
+ (mb->sslflag||mb->tlsflag), 0, 0);
+#ifdef LOCAL_PASSWD_CACHE
+ /* if requested, remember it on disk for next session */
+ if(save_password)
+ set_passfile_passwd(ps_global->pinerc, pwd,
+ altuserforcache ? altuserforcache : user, hostlist,
+ (mb->sslflag||mb->tlsflag),
+ (preserve_password == -1 ? 0
+ : (preserve_password == 0 ? 2 :1)));
+#endif /* LOCAL_PASSWD_CACHE */
+
+ ps_global->no_newmail_check_from_optionally_enter = 0;
+}
+
+
+void
+mm_login_alt_cue(NETMBX *mb)
+{
+ if(ps_global->ttyo){
+ COLOR_PAIR *lastc;
+
+ lastc = pico_set_colors(ps_global->VAR_TITLE_FORE_COLOR,
+ ps_global->VAR_TITLE_BACK_COLOR,
+ PSC_REV | PSC_RET);
+
+ mark_titlebar_dirty();
+ PutLine0(0, ps_global->ttyo->screen_cols - 1,
+ (mb->sslflag||mb->tlsflag) ? "+" : " ");
+
+ if(lastc){
+ (void)pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+
+ fflush(stdout);
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Receive notification of an error writing to disk
+
+ Args: stream -- The stream the error occured on
+ errcode -- The system error code (errno)
+ serious -- Flag indicating error is serious (mail may be lost)
+
+Result: If error is non serious, the stream is marked as having an error
+ and deletes are disallowed until error clears
+ If error is serious this goes modal, allowing the user to retry
+ or get a shell escape to fix the condition. When the condition is
+ serious it means that mail existing in the mailbox will be lost
+ if Pine exits without writing, so we try to induce the user to
+ fix the error, go get someone that can fix the error, or whatever
+ and don't provide an easy way out.
+ ----*/
+long
+mm_diskerror (MAILSTREAM *stream, long int errcode, long int serious)
+{
+ int i, j;
+ char *p, *q, *s;
+ static ESCKEY_S de_opts[] = {
+ {'r', 'r', "R", "Retry"},
+ {'f', 'f', "F", "FileBrowser"},
+ {'s', 's', "S", "ShellPrompt"},
+ {-1, 0, NULL, NULL}
+ };
+#define DE_COLS (ps_global->ttyo->screen_cols)
+#define DE_LINE (ps_global->ttyo->screen_rows - 3)
+
+#define DE_FOLDER(X) (((X) && (X)->mailbox) ? (X)->mailbox : "<no folder>")
+#define DE_PMT \
+ "Disk error! Choose Retry, or the File browser or Shell to clean up: "
+#define DE_STR1 "SERIOUS DISK ERROR WRITING: \"%s\""
+#define DE_STR2 \
+ "The reported error number is %s. The last reported mail error was:"
+ static char *de_msg[] = {
+ "Please try to correct the error preventing Alpine from saving your",
+ "mail folder. For example if the disk is out of space try removing",
+ "unneeded files. You might also contact your system administrator.",
+ "",
+ "Both Alpine's File Browser and an option to enter the system's",
+ "command prompt are offered to aid in fixing the problem. When",
+ "you believe the problem is resolved, choose the \"Retry\" option.",
+ "Be aware that messages may be lost or this folder left in an",
+ "inaccessible condition if you exit or kill Alpine before the problem",
+ "is resolved.",
+ NULL};
+ static char *de_shell_msg[] = {
+ "\n\nPlease attempt to correct the error preventing saving of the",
+ "mail folder. If you do not know how to correct the problem, contact",
+ "your system administrator. To return to Alpine, type \"exit\".",
+ NULL};
+
+ dprint((0,
+ "\n***** DISK ERROR on stream %s. Error code %ld. Error is %sserious\n",
+ DE_FOLDER(stream), errcode, serious ? "" : "not "));
+ dprint((0, "***** message: \"%s\"\n\n",
+ ps_global->last_error ? ps_global->last_error : "?"));
+
+ if(!serious) {
+ sp_set_io_error_on_stream(stream, 1);
+ return (1) ;
+ }
+
+ while(1){
+ /* replace pine's body display with screen full of explanatory text */
+ ClearLine(2);
+ PutLine1(2, MAX((DE_COLS - sizeof(DE_STR1)
+ - strlen(DE_FOLDER(stream)))/2, 0),
+ DE_STR1, DE_FOLDER(stream));
+ ClearLine(3);
+ PutLine1(3, 4, DE_STR2, long2string(errcode));
+
+ PutLine0(4, 0, " \"");
+ removing_leading_white_space(ps_global->last_error);
+ for(i = 4, p = ps_global->last_error; *p && i < DE_LINE; ){
+ for(s = NULL, q = p; *q && q - p < DE_COLS - 16; q++)
+ if(isspace((unsigned char)*q))
+ s = q;
+
+ if(*q && s)
+ q = s;
+
+ while(p < q)
+ Writechar(*p++, 0);
+
+ if(*(p = q)){
+ ClearLine(++i);
+ PutLine0(i, 0, " ");
+ while(*p && isspace((unsigned char)*p))
+ p++;
+ }
+ else{
+ Writechar('\"', 0);
+ CleartoEOLN();
+ break;
+ }
+ }
+
+ ClearLine(++i);
+ for(j = ++i; i < DE_LINE && de_msg[i-j]; i++){
+ ClearLine(i);
+ PutLine0(i, 0, " ");
+ Write_to_screen(de_msg[i-j]);
+ }
+
+ while(i < DE_LINE)
+ ClearLine(i++);
+
+ switch(radio_buttons(DE_PMT, -FOOTER_ROWS(ps_global), de_opts,
+ 'r', 0, NO_HELP, RB_FLUSH_IN | RB_NO_NEWMAIL)){
+ case 'r' : /* Retry! */
+ ps_global->mangled_screen = 1;
+ return(0L);
+
+ case 'f' : /* File Browser */
+ {
+ char full_filename[MAXPATH+1], filename[MAXPATH+1];
+
+ filename[0] = '\0';
+ build_path(full_filename, ps_global->home_dir, filename,
+ sizeof(full_filename));
+ file_lister("DISK ERROR", full_filename, sizeof(full_filename),
+ filename, sizeof(filename), FALSE, FB_SAVE);
+ }
+
+ break;
+
+ case 's' :
+ EndInverse();
+ end_keyboard(ps_global ? F_ON(F_USE_FK,ps_global) : 0);
+ end_tty_driver(ps_global);
+ for(i = 0; de_shell_msg[i]; i++)
+ puts(de_shell_msg[i]);
+
+ /*
+ * Don't use our piping mechanism to spawn a subshell here
+ * since it will the server (thus reentering c-client).
+ * Bad thing to do.
+ */
+#ifdef _WINDOWS
+#else
+ system("csh");
+#endif
+ init_tty_driver(ps_global);
+ init_keyboard(F_ON(F_USE_FK,ps_global));
+ break;
+ }
+
+ if(ps_global->redrawer)
+ (*ps_global->redrawer)();
+ }
+}
+
+
+long
+pine_tcptimeout_noscreen(long int elapsed, long int sincelast, char *host)
+{
+ long rv = 1L;
+ char pmt[128];
+
+#ifdef _WINDOWS
+ mswin_killsplash();
+#endif
+
+ if(elapsed >= (long)ps_global->tcp_query_timeout){
+ snprintf(pmt, sizeof(pmt),
+ _("No reply in %s seconds from server %s. Break connection"),
+ long2string(elapsed), host);
+ pmt[sizeof(pmt)-1] = '\0';
+ if(want_to(pmt, 'n', 'n', NO_HELP, WT_FLUSH_IN) == 'y'){
+ ps_global->user_says_cancel = 1;
+ return(0L);
+ }
+ }
+
+ return(rv);
+}
+
+
+/*
+ * -------------------------------------------------------------
+ * These are declared in pith/imap.h as mandatory to implement.
+ * -------------------------------------------------------------
+ */
+
+
+/*
+ * pine_tcptimeout - C-client callback to handle tcp-related timeouts.
+ */
+long
+pine_tcptimeout(long int elapsed, long int sincelast, char *host)
+{
+ long rv = 1L; /* keep trying by default */
+ unsigned long ch;
+
+#ifdef DEBUG
+ dprint((1, "tcptimeout: waited %s seconds\n",
+ long2string(elapsed)));
+ if(debugfile)
+ fflush(debugfile);
+#endif
+
+#ifdef _WINDOWS
+ mswin_killsplash();
+#endif
+
+ if(ps_global->noshow_timeout)
+ return(rv);
+
+ if(!ps_global->ttyo)
+ return(pine_tcptimeout_noscreen(elapsed, sincelast, host));
+
+ suspend_busy_cue();
+
+ /*
+ * Prompt after a minute (since by then things are probably really bad)
+ * A prompt timeout means "keep trying"...
+ */
+ if(elapsed >= (long)ps_global->tcp_query_timeout){
+ int clear_inverse;
+
+ ClearLine(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global));
+ if((clear_inverse = !InverseState()) != 0)
+ StartInverse();
+
+ Writechar(BELL, 0);
+
+ PutLine2(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global), 0,
+ _("No reply in %s seconds from server %s. Break connection?"),
+ long2string(elapsed), host);
+ CleartoEOLN();
+ fflush(stdout);
+ flush_input();
+ ch = read_char(7);
+ if(ch == 'y' || ch == 'Y'){
+ ps_global->user_says_cancel = 1;
+ rv = 0L;
+ }
+
+ if(clear_inverse)
+ EndInverse();
+
+ ClearLine(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global));
+ }
+
+ if(rv == 1L){ /* just warn 'em something's up */
+ q_status_message2(SM_ORDER, 0, 0,
+ _("No reply in %s seconds from server %s. Still Waiting..."),
+ long2string(elapsed), host);
+ flush_status_messages(0); /* make sure it's seen */
+ }
+
+ mark_status_dirty(); /* make sure it get's cleared */
+
+ resume_busy_cue((rv == 1) ? 3 : 0);
+
+ return(rv);
+}
+
+QUOTALIST *pine_quotalist_copy (QUOTALIST *pquota)
+{
+ QUOTALIST *cquota = NULL;
+
+ if(pquota){
+ cquota = mail_newquotalist();
+ if (pquota->name && *pquota->name)
+ cquota->name = cpystr(pquota->name);
+ cquota->usage = pquota->usage;
+ cquota->limit = pquota->limit;
+ if (pquota->next)
+ cquota->next = pine_quotalist_copy(pquota->next);
+ }
+ return cquota;
+}
+
+
+/* c-client callback to handle quota */
+
+void
+pine_parse_quota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
+{
+ ps_global->quota = pine_quotalist_copy (pquota);
+}
+
+/*
+ * C-client callback to handle SSL/TLS certificate validation failures
+ *
+ * Returning 0 means error becomes fatal
+ * Non-zero means certificate problem is ignored and SSL session is
+ * established
+ *
+ * We remember the answer and won't re-ask for subsequent open attempts to
+ * the same hostname.
+ */
+long
+pine_sslcertquery(char *reason, char *host, char *cert)
+{
+ char tmp[500];
+ char *unknown = "<unknown>";
+ long rv = 0L;
+ STRLIST_S hostlist;
+ int ok_novalidate = 0, warned = 0;
+
+ dprint((1, "sslcertificatequery: host=%s reason=%s cert=%s\n",
+ host ? host : "?", reason ? reason : "?",
+ cert ? cert : "?"));
+
+ hostlist.name = host ? host : "";
+ hostlist.next = NULL;
+
+ /*
+ * See if we've been asked about this host before.
+ */
+ if(imap_get_ssl(cert_failure_list, &hostlist, &ok_novalidate, &warned)){
+ /* we were asked before, did we say Yes? */
+ if(ok_novalidate)
+ rv++;
+
+ if(rv){
+ dprint((5,
+ "sslcertificatequery: approved automatically\n"));
+ return(rv);
+ }
+
+ dprint((1, "sslcertificatequery: we were asked before and said No, so ask again\n"));
+ }
+
+ if(ps_global->ttyo){
+ SCROLL_S sargs;
+ STORE_S *in_store, *out_store;
+ gf_io_t pc, gc;
+ HANDLE_S *handles = NULL;
+ int the_answer = 'n';
+
+ if(!(in_store = so_get(CharStar, NULL, EDIT_ACCESS)) ||
+ !(out_store = so_get(CharStar, NULL, EDIT_ACCESS)))
+ goto try_wantto;
+
+ so_puts(in_store, "<HTML><P>");
+ so_puts(in_store, _("There was a failure validating the SSL/TLS certificate for the server"));
+
+ so_puts(in_store, "<P><CENTER>");
+ so_puts(in_store, host ? host : unknown);
+ so_puts(in_store, "</CENTER>");
+
+ so_puts(in_store, "<P>");
+ so_puts(in_store, _("The reason for the failure was"));
+
+ /* squirrel away details */
+ if(details_host)
+ fs_give((void **)&details_host);
+ if(details_reason)
+ fs_give((void **)&details_reason);
+ if(details_cert)
+ fs_give((void **)&details_cert);
+
+ details_host = cpystr(host ? host : unknown);
+ details_reason = cpystr(reason ? reason : unknown);
+ details_cert = cpystr(cert ? cert : unknown);
+
+ so_puts(in_store, "<P><CENTER>");
+ snprintf(tmp, sizeof(tmp), "%s (<A HREF=\"X-Alpine-Cert:\">details</A>)",
+ reason ? reason : unknown);
+ tmp[sizeof(tmp)-1] = '\0';
+
+ so_puts(in_store, tmp);
+ so_puts(in_store, "</CENTER>");
+
+ so_puts(in_store, "<P>");
+ so_puts(in_store, _("We have not verified the identity of your server. If you ignore this certificate validation problem and continue, you could end up connecting to an imposter server."));
+
+ so_puts(in_store, "<P>");
+ so_puts(in_store, _("If the certificate validation failure was expected and permanent you may avoid seeing this warning message in the future by adding the option"));
+
+ so_puts(in_store, "<P><CENTER>");
+ so_puts(in_store, "/novalidate-cert");
+ so_puts(in_store, "</CENTER>");
+
+ so_puts(in_store, "<P>");
+ so_puts(in_store, _("to the name of the folder you attempted to access. In other words, wherever you see the characters"));
+
+ so_puts(in_store, "<P><CENTER>");
+ so_puts(in_store, host ? host : unknown);
+ so_puts(in_store, "</CENTER>");
+
+ so_puts(in_store, "<P>");
+ so_puts(in_store, _("in your configuration, replace those characters with"));
+
+ so_puts(in_store, "<P><CENTER>");
+ so_puts(in_store, host ? host : unknown);
+ so_puts(in_store, "/novalidate-cert");
+ so_puts(in_store, "</CENTER>");
+
+ so_puts(in_store, "<P>");
+ so_puts(in_store, _("Answer \"Yes\" to ignore the warning and continue, \"No\" to cancel the open of this folder."));
+
+ so_seek(in_store, 0L, 0);
+ init_handles(&handles);
+ gf_filter_init();
+ gf_link_filter(gf_html2plain,
+ gf_html2plain_opt(NULL,
+ ps_global->ttyo->screen_cols, non_messageview_margin(),
+ &handles, NULL, GFHP_LOCAL_HANDLES));
+ gf_set_so_readc(&gc, in_store);
+ gf_set_so_writec(&pc, out_store);
+ gf_pipe(gc, pc);
+ gf_clear_so_writec(out_store);
+ gf_clear_so_readc(in_store);
+
+ memset(&sargs, 0, sizeof(SCROLL_S));
+ sargs.text.handles = handles;
+ sargs.text.text = so_text(out_store);
+ sargs.text.src = CharStar;
+ sargs.text.desc = _("help text");
+ sargs.bar.title = _("SSL/TLS CERTIFICATE VALIDATION FAILURE");
+ sargs.proc.tool = answer_cert_failure;
+ sargs.proc.data.p = (void *)&the_answer;
+ sargs.keys.menu = &ans_certquery_keymenu;
+ /* don't want to re-enter c-client */
+ sargs.quell_newmail = 1;
+ setbitmap(sargs.keys.bitmap);
+ sargs.help.text = h_tls_validation_failure;
+ sargs.help.title = _("HELP FOR CERT VALIDATION FAILURE");
+
+ scrolltool(&sargs);
+
+ if(the_answer == 'y')
+ rv++;
+
+ ps_global->mangled_screen = 1;
+ ps_global->painted_body_on_startup = 0;
+ ps_global->painted_footer_on_startup = 0;
+ so_give(&in_store);
+ so_give(&out_store);
+ free_handles(&handles);
+ if(details_host)
+ fs_give((void **)&details_host);
+ if(details_reason)
+ fs_give((void **)&details_reason);
+ if(details_cert)
+ fs_give((void **)&details_cert);
+ }
+ else{
+ /*
+ * If screen hasn't been initialized yet, use want_to.
+ */
+try_wantto:
+ memset((void *)tmp, 0, sizeof(tmp));
+ strncpy(tmp,
+ reason ? reason : _("SSL/TLS certificate validation failure"),
+ sizeof(tmp));
+ tmp[sizeof(tmp)-1] = '\0';
+ strncat(tmp, _(": Continue anyway "), sizeof(tmp)-strlen(tmp)-1);
+
+ if(want_to(tmp, 'n', 'x', NO_HELP, WT_NORM) == 'y')
+ rv++;
+ }
+
+ if(rv == 0)
+ q_status_message1(SM_ORDER, 1, 3, _("Open of %s cancelled"),
+ host ? host : unknown);
+
+ imap_set_passwd(&cert_failure_list, "", "", &hostlist, 0, rv ? 1 : 0, 0);
+
+ dprint((5, "sslcertificatequery: %s\n",
+ rv ? "approved" : "rejected"));
+
+ return(rv);
+}
+
+
+char *
+pine_newsrcquery(MAILSTREAM *stream, char *mulname, char *name)
+{
+ char buf[MAILTMPLEN];
+
+ if((can_access(mulname, ACCESS_EXISTS) == 0)
+ || !(can_access(name, ACCESS_EXISTS) == 0))
+ return(mulname);
+
+ snprintf(buf, sizeof(buf),
+ _("Rename newsrc \"%s%s\" for use as new host-specific newsrc"),
+ last_cmpnt(name),
+ strlen(last_cmpnt(name)) > 15 ? "..." : "");
+ buf[sizeof(buf)-1] = '\0';
+ if(want_to(buf, 'n', 'n', NO_HELP, WT_NORM) == 'y')
+ rename_file(name, mulname);
+ return(mulname);
+}
+
+
+int
+url_local_certdetails(char *url)
+{
+ if(!struncmp(url, "x-alpine-cert:", 14)){
+ STORE_S *store;
+ SCROLL_S sargs;
+ char *folded;
+
+ if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
+ q_status_message(SM_ORDER | SM_DING, 7, 10,
+ _("Error allocating space for details."));
+ return(0);
+ }
+
+ so_puts(store, _("Host given by user:\n\n "));
+ so_puts(store, details_host);
+ so_puts(store, _("\n\nReason for failure:\n\n "));
+ so_puts(store, details_reason);
+ so_puts(store, _("\n\nCertificate being verified:\n\n"));
+ folded = fold(details_cert, ps_global->ttyo->screen_cols, ps_global->ttyo->screen_cols, " ", " ", FLD_NONE);
+ so_puts(store, folded);
+ fs_give((void **)&folded);
+ so_puts(store, "\n");
+
+ memset(&sargs, 0, sizeof(SCROLL_S));
+ sargs.text.text = so_text(store);
+ sargs.text.src = CharStar;
+ sargs.text.desc = _("Details");
+ sargs.bar.title = _("CERT VALIDATION DETAILS");
+ sargs.help.text = NO_HELP;
+ sargs.help.title = NULL;
+ sargs.quell_newmail = 1;
+ sargs.help.text = h_tls_failure_details;
+ sargs.help.title = _("HELP FOR CERT VALIDATION DETAILS");
+
+ scrolltool(&sargs);
+
+ so_give(&store); /* free resources associated with store */
+ ps_global->mangled_screen = 1;
+ return(1);
+ }
+
+ return(0);
+}
+
+
+/*
+ * C-client callback to handle SSL/TLS certificate validation failures
+ */
+void
+pine_sslfailure(char *host, char *reason, long unsigned int flags)
+{
+ SCROLL_S sargs;
+ STORE_S *store;
+ int the_answer = 'n', indent, len, cols;
+ char buf[500], buf2[500];
+ char *folded;
+ char *hst = host ? host : "<unknown>";
+ char *rsn = reason ? reason : "<unknown>";
+ char *notls = "/notls";
+ STRLIST_S hostlist;
+ int ok_novalidate = 0, warned = 0;
+
+
+ dprint((1, "sslfailure: host=%s reason=%s\n",
+ hst ? hst : "?",
+ rsn ? rsn : "?"));
+
+ if(flags & NET_SILENT)
+ return;
+
+ hostlist.name = host ? host : "";
+ hostlist.next = NULL;
+
+ /*
+ * See if we've been told about this host before.
+ */
+ if(imap_get_ssl(cert_failure_list, &hostlist, &ok_novalidate, &warned)){
+ /* we were told already */
+ if(warned){
+ snprintf(buf, sizeof(buf), _("SSL/TLS failure for %s: %s"), hst, rsn);
+ buf[sizeof(buf)-1] = '\0';
+ mm_log(buf, ERROR);
+ return;
+ }
+ }
+
+ cols = ps_global->ttyo ? ps_global->ttyo->screen_cols : 80;
+ cols--;
+
+ if(!(store = so_get(CharStar, NULL, EDIT_ACCESS)))
+ return;
+
+ strncpy(buf, _("There was an SSL/TLS failure for the server"), sizeof(buf));
+ folded = fold(buf, cols, cols, "", "", FLD_NONE);
+ so_puts(store, folded);
+ fs_give((void **)&folded);
+ so_puts(store, "\n");
+
+ if((len=strlen(hst)) <= cols){
+ if((indent=((cols-len)/2)) > 0)
+ so_puts(store, repeat_char(indent, SPACE));
+
+ so_puts(store, hst);
+ so_puts(store, "\n");
+ }
+ else{
+ strncpy(buf, hst, sizeof(buf));
+ buf[sizeof(buf)-1] = '\0';
+ folded = fold(buf, cols, cols, "", "", FLD_NONE);
+ so_puts(store, folded);
+ fs_give((void **)&folded);
+ }
+
+ so_puts(store, "\n");
+
+ strncpy(buf, _("The reason for the failure was"), sizeof(buf));
+ folded = fold(buf, cols, cols, "", "", FLD_NONE);
+ so_puts(store, folded);
+ fs_give((void **)&folded);
+ so_puts(store, "\n");
+
+ if((len=strlen(rsn)) <= cols){
+ if((indent=((cols-len)/2)) > 0)
+ so_puts(store, repeat_char(indent, SPACE));
+
+ so_puts(store, rsn);
+ so_puts(store, "\n");
+ }
+ else{
+ strncpy(buf, rsn, sizeof(buf));
+ buf[sizeof(buf)-1] = '\0';
+ folded = fold(buf, cols, cols, "", "", FLD_NONE);
+ so_puts(store, folded);
+ fs_give((void **)&folded);
+ }
+
+ so_puts(store, "\n");
+
+ strncpy(buf, _("This is just an informational message. With the current setup, SSL/TLS will not work. If this error re-occurs every time you run Alpine, your current setup is not compatible with the configuration of your mail server. You may want to add the option"), sizeof(buf));
+ folded = fold(buf, cols, cols, "", "", FLD_NONE);
+ so_puts(store, folded);
+ fs_give((void **)&folded);
+ so_puts(store, "\n");
+
+ if((len=strlen(notls)) <= cols){
+ if((indent=((cols-len)/2)) > 0)
+ so_puts(store, repeat_char(indent, SPACE));
+
+ so_puts(store, notls);
+ so_puts(store, "\n");
+ }
+ else{
+ strncpy(buf, notls, sizeof(buf));
+ buf[sizeof(buf)-1] = '\0';
+ folded = fold(buf, cols, cols, "", "", FLD_NONE);
+ so_puts(store, folded);
+ fs_give((void **)&folded);
+ }
+
+ so_puts(store, "\n");
+
+ strncpy(buf, _("to the name of the mail server you are attempting to access. In other words, wherever you see the characters"),
+ sizeof(buf));
+ folded = fold(buf, cols, cols, "", "", FLD_NONE);
+ so_puts(store, folded);
+ fs_give((void **)&folded);
+ so_puts(store, "\n");
+
+ if((len=strlen(hst)) <= cols){
+ if((indent=((cols-len)/2)) > 0)
+ so_puts(store, repeat_char(indent, SPACE));
+
+ so_puts(store, hst);
+ so_puts(store, "\n");
+ }
+ else{
+ strncpy(buf, hst, sizeof(buf));
+ buf[sizeof(buf)-1] = '\0';
+ folded = fold(buf, cols, cols, "", "", FLD_NONE);
+ so_puts(store, folded);
+ fs_give((void **)&folded);
+ }
+
+ so_puts(store, "\n");
+
+ strncpy(buf, _("in your configuration, replace those characters with"),
+ sizeof(buf));
+ folded = fold(buf, cols, cols, "", "", FLD_NONE);
+ so_puts(store, folded);
+ fs_give((void **)&folded);
+ so_puts(store, "\n");
+
+ snprintf(buf2, sizeof(buf2), "%s%s", hst, notls);
+ buf2[sizeof(buf2)-1] = '\0';
+ if((len=strlen(buf2)) <= cols){
+ if((indent=((cols-len)/2)) > 0)
+ so_puts(store, repeat_char(indent, SPACE));
+
+ so_puts(store, buf2);
+ so_puts(store, "\n");
+ }
+ else{
+ strncpy(buf, buf2, sizeof(buf));
+ buf[sizeof(buf)-1] = '\0';
+ folded = fold(buf, cols, cols, "", "", FLD_NONE);
+ so_puts(store, folded);
+ fs_give((void **)&folded);
+ }
+
+ so_puts(store, "\n");
+
+ if(ps_global->ttyo){
+ strncpy(buf, _("Type RETURN to continue."), sizeof(buf));
+ folded = fold(buf, cols, cols, "", "", FLD_NONE);
+ so_puts(store, folded);
+ fs_give((void **)&folded);
+ }
+
+ memset(&sargs, 0, sizeof(SCROLL_S));
+ sargs.text.text = so_text(store);
+ sargs.text.src = CharStar;
+ sargs.text.desc = _("help text");
+ sargs.bar.title = _("SSL/TLS FAILURE");
+ sargs.proc.tool = answer_cert_failure;
+ sargs.proc.data.p = (void *)&the_answer;
+ sargs.keys.menu = &ans_certfail_keymenu;
+ setbitmap(sargs.keys.bitmap);
+ /* don't want to re-enter c-client */
+ sargs.quell_newmail = 1;
+ sargs.help.text = h_tls_failure;
+ sargs.help.title = _("HELP FOR TLS/SSL FAILURE");
+
+ if(ps_global->ttyo)
+ scrolltool(&sargs);
+ else{
+ char **q, **qp;
+ char *p;
+ unsigned char c;
+ int cnt = 0;
+
+ /*
+ * The screen isn't initialized yet, which should mean that this
+ * is the result of a -p argument. Display_args_err knows how to deal
+ * with the uninitialized screen, so we mess with the data to get it
+ * in shape for display_args_err. This is pretty hacky.
+ */
+
+ so_seek(store, 0L, 0); /* rewind */
+ /* count the lines */
+ while(so_readc(&c, store))
+ if(c == '\n')
+ cnt++;
+
+ qp = q = (char **)fs_get((cnt+1) * sizeof(char *));
+ memset(q, 0, (cnt+1) * sizeof(char *));
+
+ so_seek(store, 0L, 0); /* rewind */
+ p = buf;
+ while(so_readc(&c, store)){
+ if(c == '\n'){
+ *p = '\0';
+ *qp++ = cpystr(buf);
+ p = buf;
+ }
+ else
+ *p++ = c;
+ }
+
+ display_args_err(NULL, q, 0);
+ free_list_array(&q);
+ }
+
+ ps_global->mangled_screen = 1;
+ ps_global->painted_body_on_startup = 0;
+ ps_global->painted_footer_on_startup = 0;
+ so_give(&store);
+
+ imap_set_passwd(&cert_failure_list, "", "", &hostlist, 0, ok_novalidate, 1);
+}
+
+
+int
+answer_cert_failure(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
+{
+ int rv = 1;
+
+ ps_global->next_screen = SCREEN_FUN_NULL;
+
+ switch(cmd){
+ case MC_YES :
+ *(int *)(sparms->proc.data.p) = 'y';
+ break;
+
+ case MC_NO :
+ *(int *)(sparms->proc.data.p) = 'n';
+ break;
+
+ default:
+ panic("Unexpected command in answer_cert_failure");
+ break;
+ }
+
+ return(rv);
+}
+
+
+/*----------------------------------------------------------------------
+ This can be used to prevent the flickering of the check_cue char
+ caused by numerous (5000+) fetches by c-client. Right now, the only
+ practical use found is newsgroup subsciption.
+
+ check_cue_display will check if this global is set, and won't clear
+ the check_cue_char if set.
+ ----*/
+void
+set_read_predicted(int i)
+{
+ ps_global->read_predicted = i==1;
+#ifdef _WINDOWS
+ if(!i && F_ON(F_SHOW_DELAY_CUE, ps_global))
+ check_cue_display(" ");
+#endif
+}
+
+
+/*----------------------------------------------------------------------
+ Exported method to retrieve logged in user name associated with stream
+
+ Args: host -- host to find associated login name with.
+
+ Result:
+ ----*/
+void *
+pine_block_notify(int reason, void *data)
+{
+ switch(reason){
+ case BLOCK_SENSITIVE: /* sensitive code, disallow alarms */
+ break;
+
+ case BLOCK_NONSENSITIVE: /* non-sensitive code, allow alarms */
+ break;
+
+ case BLOCK_TCPWRITE: /* blocked on TCP write */
+ case BLOCK_FILELOCK: /* blocked on file locking */
+#ifdef _WINDOWS
+ if(F_ON(F_SHOW_DELAY_CUE, ps_global))
+ check_cue_display(">");
+
+ mswin_setcursor(MSWIN_CURSOR_BUSY);
+#endif
+ break;
+
+ case BLOCK_DNSLOOKUP: /* blocked on DNS lookup */
+ case BLOCK_TCPOPEN: /* blocked on TCP open */
+ case BLOCK_TCPREAD: /* blocked on TCP read */
+ case BLOCK_TCPCLOSE: /* blocked on TCP close */
+#ifdef _WINDOWS
+ if(F_ON(F_SHOW_DELAY_CUE, ps_global))
+ check_cue_display("<");
+
+ mswin_setcursor(MSWIN_CURSOR_BUSY);
+#endif
+ break;
+
+ default :
+ case BLOCK_NONE: /* not blocked */
+#ifdef _WINDOWS
+ if(F_ON(F_SHOW_DELAY_CUE, ps_global))
+ check_cue_display(" ");
+#endif
+ break;
+
+ }
+
+ return(NULL);
+}
+
+
+void
+mm_expunged_current(long unsigned int rawno)
+{
+ /* expunged something we're viewing? */
+ if(!ps_global->expunge_in_progress
+ && (mn_is_cur(ps_global->msgmap, mn_raw2m(ps_global->msgmap, (long) rawno))
+ && (ps_global->prev_screen == mail_view_screen
+ || ps_global->prev_screen == attachment_screen))){
+ ps_global->next_screen = mail_index_screen;
+ q_status_message(SM_ORDER | SM_DING , 3, 3,
+ "Message you were viewing is gone!");
+ }
+}
+
+
+#ifdef PASSFILE
+
+/*
+ * Specific functions to support caching username/passwd/host
+ * triples on disk for use from one session to the next...
+ */
+
+#define FIRSTCH 0x20
+#define LASTCH 0x7e
+#define TABSZ (LASTCH - FIRSTCH + 1)
+
+static int xlate_key;
+
+
+/*
+ * xlate_in() - xlate_in the given character
+ */
+char
+xlate_in(int c)
+{
+ register int eti;
+
+ eti = xlate_key;
+ if((c >= FIRSTCH) && (c <= LASTCH)){
+ eti += (c - FIRSTCH);
+ eti -= (eti >= 2*TABSZ) ? 2*TABSZ : (eti >= TABSZ) ? TABSZ : 0;
+ return((xlate_key = eti) + FIRSTCH);
+ }
+ else
+ return(c);
+}
+
+
+/*
+ * xlate_out() - xlate_out the given character
+ */
+char
+xlate_out(char c)
+{
+ register int dti;
+ register int xch;
+
+ if((c >= FIRSTCH) && (c <= LASTCH)){
+ xch = c - (dti = xlate_key);
+ xch += (xch < FIRSTCH-TABSZ) ? 2*TABSZ : (xch < FIRSTCH) ? TABSZ : 0;
+ dti = (xch - FIRSTCH) + dti;
+ dti -= (dti >= 2*TABSZ) ? 2*TABSZ : (dti >= TABSZ) ? TABSZ : 0;
+ xlate_key = dti;
+ return(xch);
+ }
+ else
+ return(c);
+}
+
+
+char *
+passfile_name(char *pinerc, char *path, size_t len)
+{
+ struct stat sbuf;
+ char *p = NULL;
+ int i, j;
+
+ if(!path || !((pinerc && pinerc[0]) || ps_global->passfile))
+ return(NULL);
+
+ if(ps_global->passfile)
+ strncpy(path, ps_global->passfile, len-1);
+ else{
+ if((p = last_cmpnt(pinerc)) && *(p-1) && *(p-1) != PASSFILE[0])
+ for(i = 0; pinerc < p && i < len; pinerc++, i++)
+ path[i] = *pinerc;
+ else
+ i = 0;
+
+ for(j = 0; (i < len) && (path[i] = PASSFILE[j]); i++, j++)
+ ;
+
+ }
+
+ path[len-1] = '\0';
+
+ dprint((9, "Looking for passfile \"%s\"\n",
+ path ? path : "?"));
+
+#if defined(DOS) || defined(OS2)
+ return((our_stat(path, &sbuf) == 0
+ && ((sbuf.st_mode & S_IFMT) == S_IFREG))
+ ? path : NULL);
+#else
+ /* First, make sure it's ours and not sym-linked */
+ if(our_lstat(path, &sbuf) == 0
+ && ((sbuf.st_mode & S_IFMT) == S_IFREG)
+ && sbuf.st_uid == getuid()){
+ /* if too liberal permissions, fix them */
+ if((sbuf.st_mode & 077) != 0)
+ if(our_chmod(path, sbuf.st_mode & ~077) != 0)
+ return(NULL);
+
+ return(path);
+ }
+ else
+ return(NULL);
+#endif
+}
+
+#endif /* PASSFILE */
+
+
+#ifdef LOCAL_PASSWD_CACHE
+
+
+int
+line_get(char *tmp, size_t len, char **textp)
+{
+ char *s, c;
+
+ tmp[0] = '\0';
+ if (*textp == NULL)
+ return 0;
+ s = strchr(*textp, '\n');
+ if(s != NULL){
+ *s = '\0';
+ if(*(s-1) == '\r')
+ *(s-1) = '\0';
+ if(strlen(*textp) < len - 1)
+ strcpy(tmp, *textp);
+ else
+ return 0;
+ strcat(tmp, "\n");
+ *textp = s+1;
+ }
+ else
+ return 0;
+
+ return 1;
+}
+/*
+ * For UNIX:
+ * Passfile lines are
+ *
+ * passwd TAB user TAB hostname TAB flags [ TAB orig_hostname ] \n
+ *
+ * In pine4.40 and before there was no orig_hostname, and there still isn't
+ * if it is the same as hostname.
+ *
+ * else for WINDOWS:
+ * Use Windows credentials. The TargetName of the credential is
+ * UWash_Alpine_<hostname:port>\tuser\taltflag
+ * and the blob consists of
+ * passwd\torighost (if different from host)
+ *
+ * We don't use anything fancy we just copy out all the credentials which
+ * begin with TNAME and put them into our cache, so we don't lookup based
+ * on the TargetName or anything like that. That was so we could re-use
+ * the existing code and so that orighost data could be easily used.
+ */
+int
+read_passfile(pinerc, l)
+ char *pinerc;
+ MMLOGIN_S **l;
+{
+#ifdef WINCRED
+# if (WINCRED > 0)
+ LPCTSTR lfilter = TEXT(TNAMESTAR);
+ DWORD count, k;
+ PCREDENTIAL *pcred;
+ char *tmp, *blob, *target = NULL;
+ char *host, *user, *sflags, *passwd, *orighost;
+ char *ui[5];
+ int i, j;
+
+ if(using_passfile == 0)
+ return(using_passfile);
+
+ if(!g_CredInited){
+ if(init_wincred_funcs() != 1){
+ using_passfile = 0;
+ return(using_passfile);
+ }
+ }
+
+ dprint((9, "read_passfile\n"));
+
+ using_passfile = 1;
+
+ if(g_CredEnumerateW(lfilter, 0, &count, &pcred)){
+ if(pcred){
+ for(k = 0; k < count; k++){
+
+ host = user = sflags = passwd = orighost = NULL;
+ ui[0] = ui[1] = ui[2] = ui[3] = ui[4] = NULL;
+
+ target = lptstr_to_utf8(pcred[k]->TargetName);
+ tmp = srchstr(target, TNAME);
+
+ if(tmp){
+ tmp += strlen(TNAME);
+ for(i = 0, j = 0; tmp[i] && j < 3; j++){
+ for(ui[j] = &tmp[i]; tmp[i] && tmp[i] != '\t'; i++)
+ ; /* find end of data */
+
+ if(tmp[i])
+ tmp[i++] = '\0'; /* tie off data */
+ }
+
+ host = ui[0];
+ user = ui[1];
+ sflags = ui[2];
+ }
+
+ blob = (char *) pcred[k]->CredentialBlob;
+ if(blob){
+ for(i = 0, j = 3; blob[i] && j < 5; j++){
+ for(ui[j] = &blob[i]; blob[i] && blob[i] != '\t'; i++)
+ ; /* find end of data */
+
+ if(blob[i])
+ blob[i++] = '\0'; /* tie off data */
+ }
+
+ passwd = ui[3];
+ orighost = ui[4];
+ }
+
+ if(passwd && host && user){ /* valid field? */
+ STRLIST_S hostlist[2];
+ int flags = sflags ? atoi(sflags) : 0;
+
+ hostlist[0].name = host;
+ if(orighost){
+ hostlist[0].next = &hostlist[1];
+ hostlist[1].name = orighost;
+ hostlist[1].next = NULL;
+ }
+ else{
+ hostlist[0].next = NULL;
+ }
+
+ imap_set_passwd(l, passwd, user, hostlist, flags & 0x01, 0, 0);
+ }
+
+ if(target)
+ fs_give((void **) &target);
+ }
+
+ g_CredFree((PVOID) pcred);
+ }
+ }
+
+ return(1);
+
+# else /* old windows */
+ using_passfile = 0;
+ return(0);
+# endif
+
+#elif APPLEKEYCHAIN
+
+ char target[MAILTMPLEN];
+ char *tmp, *host, *user, *sflags, *passwd, *orighost;
+ char *ui[5];
+ int i, j, k, rc;
+ SecKeychainAttributeList attrList;
+ SecKeychainSearchRef searchRef = NULL;
+ SecKeychainAttribute attrs[] = {
+ { kSecAccountItemAttr, strlen(TNAME), TNAME }
+ };
+
+ if(using_passfile == 0)
+ return(using_passfile);
+
+ dprint((9, "read_passfile\n"));
+
+
+ /* search for only our items in the keychain */
+ attrList.count = 1;
+ attrList.attr = attrs;
+
+ using_passfile = 1;
+ if(!(rc=SecKeychainSearchCreateFromAttributes(NULL,
+ kSecGenericPasswordItemClass,
+ &attrList,
+ &searchRef))){
+ dprint((10, "read_passfile: SecKeychainSearchCreate succeeded\n"));
+ if(searchRef){
+ SecKeychainItemRef itemRef = NULL;
+ SecKeychainAttributeInfo info;
+ SecKeychainAttributeList *attrList = NULL;
+ UInt32 blength = 0;
+ char *blob = NULL;
+ char *blobcopy = NULL; /* NULL terminated copy */
+
+ UInt32 tags[] = {kSecAccountItemAttr,
+ kSecServiceItemAttr};
+ UInt32 formats[] = {0,0};
+
+ dprint((10, "read_passfile: searchRef not NULL\n"));
+ info.count = 2;
+ info.tag = tags;
+ info.format = formats;
+
+ /*
+ * Go through each item we found and put it
+ * into our list.
+ */
+ while(!(rc=SecKeychainSearchCopyNext(searchRef, &itemRef)) && itemRef){
+ dprint((10, "read_passfile: SecKeychainSearchCopyNext got one\n"));
+ rc = SecKeychainItemCopyAttributesAndData(itemRef,
+ &info, NULL,
+ &attrList,
+ &blength,
+ (void **) &blob);
+ if(rc == 0 && attrList){
+ dprint((10, "read_passfile: SecKeychainItemCopyAttributesAndData succeeded, count=%d\n", attrList->count));
+
+ blobcopy = (char *) fs_get((blength + 1) * sizeof(char));
+ strncpy(blobcopy, (char *) blob, blength);
+ blobcopy[blength] = '\0';
+
+ /*
+ * I'm not real clear on how this works. It seems to be
+ * necessary to combine the attributes from two passes
+ * (attrList->count == 2) in order to get the full set
+ * of attributes we inserted into the keychain in the
+ * first place. So, we reset host...orighost outside of
+ * the following for loop, not inside.
+ */
+ host = user = sflags = passwd = orighost = NULL;
+ ui[0] = ui[1] = ui[2] = ui[3] = ui[4] = NULL;
+
+ for(k = 0; k < attrList->count; k++){
+
+ if(attrList->attr[k].length){
+ strncpy(target,
+ (char *) attrList->attr[k].data,
+ MIN(attrList->attr[k].length,sizeof(target)));
+ target[MIN(attrList->attr[k].length,sizeof(target)-1)] = '\0';
+ }
+
+ tmp = target;
+ for(i = 0, j = 0; tmp[i] && j < 3; j++){
+ for(ui[j] = &tmp[i]; tmp[i] && tmp[i] != '\t'; i++)
+ ; /* find end of data */
+
+ if(tmp[i])
+ tmp[i++] = '\0'; /* tie off data */
+ }
+
+ if(ui[0])
+ host = ui[0];
+
+ if(ui[1])
+ user = ui[1];
+
+ if(ui[2])
+ sflags = ui[2];
+
+ for(i = 0, j = 3; blobcopy[i] && j < 5; j++){
+ for(ui[j] = &blobcopy[i]; blobcopy[i] && blobcopy[i] != '\t'; i++)
+ ; /* find end of data */
+
+ if(blobcopy[i])
+ blobcopy[i++] = '\0'; /* tie off data */
+ }
+
+ if(ui[3])
+ passwd = ui[3];
+
+ if(ui[4])
+ orighost = ui[4];
+
+ dprint((10, "read_passfile: host=%s user=%s sflags=%s passwd=%s orighost=%s\n", host?host:"", user?user:"", sflags?sflags:"", passwd?passwd:"", orighost?orighost:""));
+ }
+
+ if(passwd && host && user){ /* valid field? */
+ STRLIST_S hostlist[2];
+ int flags = sflags ? atoi(sflags) : 0;
+
+ hostlist[0].name = host;
+ if(orighost){
+ hostlist[0].next = &hostlist[1];
+ hostlist[1].name = orighost;
+ hostlist[1].next = NULL;
+ }
+ else{
+ hostlist[0].next = NULL;
+ }
+
+ imap_set_passwd(l, passwd, user, hostlist, flags & 0x01, 0, 0);
+ }
+
+ if(blobcopy)
+ fs_give((void **) & blobcopy);
+
+ SecKeychainItemFreeAttributesAndData(attrList, (void *) blob);
+ }
+ else{
+ using_passfile = 0;
+ dprint((10, "read_passfile: SecKeychainItemCopyAttributesAndData failed, rc=%d\n", rc));
+ }
+
+ CFRelease(itemRef);
+ itemRef = NULL;
+ }
+
+ CFRelease(searchRef);
+ }
+ else{
+ using_passfile = 0;
+ dprint((10, "read_passfile: searchRef NULL\n"));
+ }
+ }
+ else{
+ using_passfile = 0;
+ dprint((10, "read_passfile: SecKeychainSearchCreateFromAttributes failed, rc=%d\n", rc));
+ }
+
+ return(using_passfile);
+
+#else /* PASSFILE */
+
+ char tmp[MAILTMPLEN], *ui[5];
+ int i, j, n;
+#ifdef SMIME
+ char tmp2[MAILTMPLEN];
+ char *text = NULL, *text2 = NULL;
+ int encrypted = 0;
+#endif /* SMIME */
+ FILE *fp;
+
+ if(using_passfile == 0)
+ return(using_passfile);
+
+ dprint((9, "read_passfile\n"));
+
+ /* if there's no password to read, bag it!! */
+ if(!passfile_name(pinerc, tmp, sizeof(tmp)) || !(fp = our_fopen(tmp, "rb"))){
+ using_passfile = 0;
+ return(using_passfile);
+ };
+
+#ifdef SMIME
+ tmp2[0] = '\0';
+ fgets(tmp2, sizeof(tmp2), fp);
+ if(strcmp(tmp2, "-----BEGIN PKCS7-----\n")){
+ fclose(fp);
+ if(encrypt_file(tmp, NULL))
+ encrypted++;
+ }
+ else{
+ fclose(fp);
+ encrypted++;
+ }
+
+ /*
+ * if password file is encrypted we attemtp to decrypt. We ask the
+ * user for the password to unlock the password file. If the user
+ * enters the password and it unlocks the file, use it and keep saving
+ * passwords in it. If the user enters the wrong passwords and does
+ * not unlock it, we will not see that here, but in decrypt_file, so
+ * the only other possibility is that the user cancels. In that case
+ * we will see i == -1. In that case, we will let the user attempt
+ * manual login to the server they want to login, but passwords will
+ * not be saved so that the password file will not be saved
+ * unencrypted and rewritten again.
+ */
+ if(encrypted){
+ text = text2 = decrypt_file(tmp, &i);
+ switch(i){
+ case 1 : save_password = 1;
+ break;
+
+ case -1: save_password = 0;
+ break;
+
+ default: break;
+ }
+ }
+ else
+ fp = our_fopen(tmp, "rb"); /* reopen to read data */
+#endif /* SMIME */
+
+ using_passfile = 1;
+#ifdef SMIME
+ for(n = 0; encrypted ? line_get(tmp, sizeof(tmp), &text2)
+ : (fgets(tmp, sizeof(tmp), fp) != NULL); n++){
+#else /* SMIME */
+ for(n = 0; fgets(tmp, sizeof(tmp), fp); n++){
+#endif /* SMIME */
+ /*** do any necessary DEcryption here ***/
+ xlate_key = n;
+ for(i = 0; tmp[i]; i++)
+ tmp[i] = xlate_out(tmp[i]);
+
+ if(i && tmp[i-1] == '\n')
+ tmp[i-1] = '\0'; /* blast '\n' */
+
+ dprint((10, "read_passfile: %s\n", tmp ? tmp : "?"));
+ ui[0] = ui[1] = ui[2] = ui[3] = ui[4] = NULL;
+ for(i = 0, j = 0; tmp[i] && j < 5; j++){
+ for(ui[j] = &tmp[i]; tmp[i] && tmp[i] != '\t'; i++)
+ ; /* find end of data */
+
+ if(tmp[i])
+ tmp[i++] = '\0'; /* tie off data */
+ }
+
+ dprint((10, "read_passfile: calling imap_set_passwd\n"));
+ if(ui[0] && ui[1] && ui[2]){ /* valid field? */
+ STRLIST_S hostlist[2];
+ int flags = ui[3] ? atoi(ui[3]) : 0;
+
+ hostlist[0].name = ui[2];
+ if(ui[4]){
+ hostlist[0].next = &hostlist[1];
+ hostlist[1].name = ui[4];
+ hostlist[1].next = NULL;
+ }
+ else{
+ hostlist[0].next = NULL;
+ }
+
+ imap_set_passwd(l, ui[0], ui[1], hostlist, flags & 0x01, 0, 0);
+ }
+ }
+
+#ifdef SMIME
+ if (text) fs_give((void **)&text);
+#else /* SMIME */
+ fclose(fp);
+#endif /* SMIME */
+ return(1);
+#endif /* PASSFILE */
+}
+
+
+
+void
+write_passfile(pinerc, l)
+ char *pinerc;
+ MMLOGIN_S *l;
+{
+#ifdef WINCRED
+# if (WINCRED > 0)
+ char target[MAILTMPLEN];
+ char blob[MAILTMPLEN];
+ CREDENTIAL cred;
+ LPTSTR ltarget = 0;
+
+ if(using_passfile == 0)
+ return;
+
+ dprint((9, "write_passfile\n"));
+
+ for(; l; l = l->next){
+ snprintf(target, sizeof(target), "%s%s\t%s\t%d",
+ TNAME,
+ (l->hosts && l->hosts->name) ? l->hosts->name : "",
+ l->user ? l->user : "",
+ l->altflag);
+ ltarget = utf8_to_lptstr((LPSTR) target);
+
+ if(ltarget){
+ snprintf(blob, sizeof(blob), "%s%s%s",
+ l->passwd ? l->passwd : "",
+ (l->hosts && l->hosts->next && l->hosts->next->name)
+ ? "\t" : "",
+ (l->hosts && l->hosts->next && l->hosts->next->name)
+ ? l->hosts->next->name : "");
+ memset((void *) &cred, 0, sizeof(cred));
+ cred.Flags = 0;
+ cred.Type = CRED_TYPE_GENERIC;
+ cred.TargetName = ltarget;
+ cred.CredentialBlobSize = strlen(blob)+1;
+ cred.CredentialBlob = (LPBYTE) &blob;
+ cred.Persist = CRED_PERSIST_ENTERPRISE;
+ g_CredWriteW(&cred, 0);
+
+ fs_give((void **) &ltarget);
+ }
+ }
+ #endif /* WINCRED > 0 */
+
+#elif APPLEKEYCHAIN
+
+ int rc;
+ char target[MAILTMPLEN];
+ char blob[MAILTMPLEN];
+ SecKeychainItemRef itemRef = NULL;
+
+ if(using_passfile == 0)
+ return;
+
+ dprint((9, "write_passfile\n"));
+
+ for(; l; l = l->next){
+ snprintf(target, sizeof(target), "%s\t%s\t%d",
+ (l->hosts && l->hosts->name) ? l->hosts->name : "",
+ l->user ? l->user : "",
+ l->altflag);
+
+ snprintf(blob, sizeof(blob), "%s%s%s",
+ l->passwd ? l->passwd : "",
+ (l->hosts && l->hosts->next && l->hosts->next->name)
+ ? "\t" : "",
+ (l->hosts && l->hosts->next && l->hosts->next->name)
+ ? l->hosts->next->name : "");
+
+ dprint((10, "write_passfile: SecKeychainAddGenericPassword(NULL, %d, %s, %d, %s, %d, %s, NULL)\n", strlen(target), target, strlen(TNAME), TNAME, strlen(blob), blob));
+
+ rc = SecKeychainAddGenericPassword(NULL,
+ strlen(target), target,
+ strlen(TNAME), TNAME,
+ strlen(blob), blob,
+ NULL);
+ if(rc==0){
+ dprint((10, "write_passfile: SecKeychainAddGenericPassword succeeded\n"));
+ }
+ else{
+ dprint((10, "write_passfile: SecKeychainAddGenericPassword returned rc=%d\n", rc));
+ }
+
+ if(rc == errSecDuplicateItem){
+ /* fix existing entry */
+ dprint((10, "write_passfile: SecKeychainAddGenericPassword found existing entry\n"));
+ itemRef = NULL;
+ if(!(rc=SecKeychainFindGenericPassword(NULL,
+ strlen(target), target,
+ strlen(TNAME), TNAME,
+ NULL, NULL,
+ &itemRef)) && itemRef){
+
+ rc = SecKeychainItemModifyAttributesAndData(itemRef, NULL, strlen(blob), blob);
+ if(!rc){
+ dprint((10, "write_passfile: SecKeychainItemModifyAttributesAndData returned rc=%d\n", rc));
+ }
+ }
+ else{
+ dprint((10, "write_passfile: SecKeychainFindGenericPassword returned rc=%d\n", rc));
+ }
+ }
+ }
+
+#else /* PASSFILE */
+
+ char tmp[MAILTMPLEN];
+ int i, n;
+ FILE *fp;
+#ifdef SMIME
+ char *text = NULL, tmp2[MAILTMPLEN];
+ int len = 0;
+#endif
+
+ if(using_passfile == 0)
+ return;
+
+ dprint((9, "write_passfile\n"));
+
+ /* if there's no passfile to read, bag it!! */
+ if(!passfile_name(pinerc, tmp, sizeof(tmp)) || !(fp = our_fopen(tmp, "wb"))){
+ using_passfile = 0;
+ return;
+ }
+
+#ifdef SMIME
+ strcpy(tmp2, tmp);
+#endif /* SMIME */
+
+ for(n = 0; l; l = l->next, n++){
+ /*** do any necessary ENcryption here ***/
+ snprintf(tmp, sizeof(tmp), "%s\t%s\t%s\t%d%s%s\n", l->passwd, l->user,
+ l->hosts->name, l->altflag,
+ (l->hosts->next && l->hosts->next->name) ? "\t" : "",
+ (l->hosts->next && l->hosts->next->name) ? l->hosts->next->name
+ : "");
+ dprint((10, "write_passfile: %s", tmp ? tmp : "?"));
+ xlate_key = n;
+ for(i = 0; tmp[i]; i++)
+ tmp[i] = xlate_in(tmp[i]);
+
+#ifdef SMIME
+ if(len == 0){
+ len = strlen(tmp) + 1;
+ text = fs_get(len*sizeof(char));
+ *text = '\0';
+ }
+ if(strlen(text) + strlen(tmp) > len){
+ len = strlen(text) + strlen(tmp) + 1;
+ fs_resize((void **)&text, len*sizeof(char));
+ }
+ strcat(text, tmp);
+#else /* SMIME */
+ fputs(tmp, fp);
+#endif /* SMIME */
+ }
+
+ fclose(fp);
+#ifdef SMIME
+ if(encrypt_file(tmp2, text) == 0){
+ if((fp = our_fopen(tmp2, "wb")) != NULL){
+ fputs(text, fp);
+ fclose(fp);
+ }
+ }
+ fs_give((void **)&text);
+#endif /* SMIME */
+#endif /* PASSFILE */
+}
+
+#endif /* LOCAL_PASSWD_CACHE */
+
+
+#if (WINCRED > 0)
+void
+erase_windows_credentials(void)
+{
+ LPCTSTR lfilter = TEXT(TNAMESTAR);
+ DWORD count, k;
+ PCREDENTIAL *pcred;
+
+ if(!g_CredInited){
+ if(init_wincred_funcs() != 1)
+ return;
+ }
+
+ if(g_CredEnumerateW(lfilter, 0, &count, &pcred)){
+ if(pcred){
+ for(k = 0; k < count; k++)
+ g_CredDeleteW(pcred[k]->TargetName, CRED_TYPE_GENERIC, 0);
+
+ g_CredFree((PVOID) pcred);
+ }
+ }
+}
+
+void
+ask_erase_credentials(void)
+{
+ if(want_to(_("Erase previously preserved passwords"), 'y', 'x', NO_HELP, WT_NORM) == 'y'){
+ erase_windows_credentials();
+ q_status_message(SM_ORDER, 3, 3, "All preserved passwords have been erased");
+ }
+ else
+ q_status_message(SM_ORDER, 3, 3, "Previously preserved passwords will not be erased");
+}
+
+#endif /* WINCRED */
+
+
+#ifdef LOCAL_PASSWD_CACHE
+
+/*
+ * get_passfile_passwd - return the password contained in the special passord
+ * cache. The file is assumed to be in the same directory
+ * as the pinerc with the name defined above.
+ */
+int
+get_passfile_passwd(pinerc, passwd, user, hostlist, altflag)
+ char *pinerc, *passwd, *user;
+ STRLIST_S *hostlist;
+ int altflag;
+{
+ dprint((10, "get_passfile_passwd\n"));
+ return((passfile_cache || read_passfile(pinerc, &passfile_cache))
+ ? imap_get_passwd(passfile_cache, passwd,
+ user, hostlist, altflag)
+ : 0);
+}
+
+int
+is_using_passfile()
+{
+ return(using_passfile == 1);
+}
+
+/*
+ * Just trying to guess the username the user might want to use on this
+ * host, the user will confirm.
+ */
+char *
+get_passfile_user(pinerc, hostlist)
+ char *pinerc;
+ STRLIST_S *hostlist;
+{
+ return((passfile_cache || read_passfile(pinerc, &passfile_cache))
+ ? imap_get_user(passfile_cache, hostlist)
+ : NULL);
+}
+
+
+int
+preserve_prompt(void)
+{
+#ifdef WINCRED
+# if (WINCRED > 0)
+ /*
+ * This prompt was going to be able to be turned on and off via a registry
+ * setting controlled from the config menu. We decided to always use the
+ * dialog for login, and there the prompt is unobtrusive enough to always
+ * be in there. As a result, windows should never reach this, but now
+ * OS X somewhat uses the behavior just described.
+ */
+ if(mswin_store_pass_prompt()
+ && (want_to(_("Preserve password for next login"),
+ 'y', 'x', NO_HELP, WT_NORM)
+ == 'y'))
+ return(1);
+ else
+ return(0);
+# else
+ return(0);
+# endif
+
+#elif APPLEKEYCHAIN
+
+ int rc;
+ if(rc = macos_store_pass_prompt()){
+ if(want_to(_("Preserve password for next login"),
+ 'y', 'x', NO_HELP, WT_NORM)
+ == 'y'){
+ if(rc == -1){
+ macos_set_store_pass_prompt(1);
+ q_status_message(SM_ORDER, 4, 4,
+ _("Stop \"Preserve passwords?\" prompts by deleting Alpine Keychain entry"));
+ }
+ return(1);
+ }
+ else if(rc == -1){
+ macos_set_store_pass_prompt(0);
+ q_status_message(SM_ORDER, 4, 4,
+ _("Restart \"Preserve passwords?\" prompts by deleting Alpine Keychain entry"));
+ }
+ return(0);
+ }
+ return(0);
+#else /* PASSFILE */
+ return(want_to(_("Preserve password on DISK for next login"),
+ 'y', 'x', NO_HELP, WT_NORM)
+ == 'y');
+#endif /* PASSFILE */
+}
+
+#endif /* LOCAL_PASSWD_CACHE */
+
+
+#ifdef APPLEKEYCHAIN
+
+/*
+ * Returns:
+ * 1 if store pass prompt is set in the "registry" to on
+ * 0 if set to off
+ * -1 if not set to anything
+ */
+int
+macos_store_pass_prompt(void)
+{
+ char *data = NULL;
+ UInt32 len = 0;
+ int rc = -1;
+ int val;
+
+ if(storepassprompt == -1){
+ if(!(rc=SecKeychainFindGenericPassword(NULL, 0, NULL,
+ strlen(TNAMEPROMPT),
+ TNAMEPROMPT, &len,
+ (void **) &data, NULL))){
+ val = (len == 1 && data && data[0] == '1');
+ }
+ }
+
+ if(storepassprompt == -1 && !rc){
+ if(val)
+ storepassprompt = 1;
+ else
+ storepassprompt = 0;
+ }
+
+ return(storepassprompt);
+}
+
+
+void
+macos_set_store_pass_prompt(int val)
+{
+ storepassprompt = val ? 1 : 0;
+
+ SecKeychainAddGenericPassword(NULL, 0, NULL, strlen(TNAMEPROMPT),
+ TNAMEPROMPT, 1, val ? "1" : "0", NULL);
+}
+
+
+void
+macos_erase_keychain(void)
+{
+ SecKeychainAttributeList attrList;
+ SecKeychainSearchRef searchRef = NULL;
+ SecKeychainAttribute attrs1[] = {
+ { kSecAccountItemAttr, strlen(TNAME), TNAME }
+ };
+ SecKeychainAttribute attrs2[] = {
+ { kSecAccountItemAttr, strlen(TNAMEPROMPT), TNAMEPROMPT }
+ };
+
+ dprint((9, "macos_erase_keychain\n"));
+
+ /*
+ * Seems like we ought to be able to combine attrs1 and attrs2
+ * into a single array, but I couldn't get it to work.
+ */
+
+ /* search for only our items in the keychain */
+ attrList.count = 1;
+ attrList.attr = attrs1;
+
+ if(!SecKeychainSearchCreateFromAttributes(NULL,
+ kSecGenericPasswordItemClass,
+ &attrList,
+ &searchRef)){
+ if(searchRef){
+ SecKeychainItemRef itemRef = NULL;
+
+ /*
+ * Go through each item we found and put it
+ * into our list.
+ */
+ while(!SecKeychainSearchCopyNext(searchRef, &itemRef) && itemRef){
+ dprint((10, "read_passfile: SecKeychainSearchCopyNext got one\n"));
+ SecKeychainItemDelete(itemRef);
+ CFRelease(itemRef);
+ }
+
+ CFRelease(searchRef);
+ }
+ }
+
+ attrList.count = 1;
+ attrList.attr = attrs2;
+
+ if(!SecKeychainSearchCreateFromAttributes(NULL,
+ kSecGenericPasswordItemClass,
+ &attrList,
+ &searchRef)){
+ if(searchRef){
+ SecKeychainItemRef itemRef = NULL;
+
+ /*
+ * Go through each item we found and put it
+ * into our list.
+ */
+ while(!SecKeychainSearchCopyNext(searchRef, &itemRef) && itemRef){
+ SecKeychainItemDelete(itemRef);
+ CFRelease(itemRef);
+ }
+
+ CFRelease(searchRef);
+ }
+ }
+}
+
+#endif /* APPLEKEYCHAIN */
+
+#ifdef LOCAL_PASSWD_CACHE
+
+/*
+ * set_passfile_passwd - set the password file entry associated with
+ * cache. The file is assumed to be in the same directory
+ * as the pinerc with the name defined above.
+ * already_prompted: 0 not prompted
+ * 1 prompted, answered yes
+ * 2 prompted, answered no
+ */
+void
+set_passfile_passwd(pinerc, passwd, user, hostlist, altflag, already_prompted)
+ char *pinerc, *passwd, *user;
+ STRLIST_S *hostlist;
+ int altflag, already_prompted;
+{
+ dprint((10, "set_passfile_passwd\n"));
+ if((passfile_cache || read_passfile(pinerc, &passfile_cache))
+ && !ps_global->nowrite_password_cache
+ && ((already_prompted == 0 && preserve_prompt())
+ || already_prompted == 1)){
+ imap_set_passwd(&passfile_cache, passwd, user, hostlist, altflag, 0, 0);
+ write_passfile(pinerc, passfile_cache);
+ }
+}
+
+
+/*
+ * Passfile lines are
+ *
+ * passwd TAB user TAB hostname TAB flags [ TAB orig_hostname ] \n
+ *
+ * In pine4.40 and before there was no orig_hostname.
+ * This routine attempts to repair that.
+ */
+void
+update_passfile_hostlist(pinerc, user, hostlist, altflag)
+ char *pinerc;
+ char *user;
+ STRLIST_S *hostlist;
+ int altflag;
+{
+#ifdef WINCRED
+ return;
+#else /* !WINCRED */
+ MMLOGIN_S *l;
+ STRLIST_S *h1, *h2;
+
+ for(l = passfile_cache; l; l = l->next)
+ if(imap_same_host(l->hosts, hostlist)
+ && *user
+ && !strcmp(user, l->user)
+ && l->altflag == altflag){
+ break;
+ }
+
+ if(l && l->hosts && hostlist && !l->hosts->next && hostlist->next
+ && hostlist->next->name
+ && !ps_global->nowrite_password_cache){
+ l->hosts->next = new_strlist(hostlist->next->name);
+ write_passfile(pinerc, passfile_cache);
+ }
+#endif /* !WINCRED */
+}
+
+#endif /* LOCAL_PASSWD_CACHE */
+
+
+#if (WINCRED > 0)
+/*
+ * Load and init the WinCred structure.
+ * This gives us a way to skip the WinCred code
+ * if the dll doesn't exist.
+ */
+int
+init_wincred_funcs(void)
+{
+ if(!g_CredInited)
+ {
+ HMODULE hmod;
+
+ /* Assume the worst. */
+ g_CredInited = -1;
+
+ hmod = LoadLibrary(TEXT("advapi32.dll"));
+ if(hmod)
+ {
+ FARPROC fpCredWriteW;
+ FARPROC fpCredEnumerateW;
+ FARPROC fpCredDeleteW;
+ FARPROC fpCredFree;
+
+ fpCredWriteW = GetProcAddress(hmod, "CredWriteW");
+ fpCredEnumerateW = GetProcAddress(hmod, "CredEnumerateW");
+ fpCredDeleteW = GetProcAddress(hmod, "CredDeleteW");
+ fpCredFree = GetProcAddress(hmod, "CredFree");
+
+ if(fpCredWriteW && fpCredEnumerateW && fpCredDeleteW && fpCredFree)
+ {
+ g_CredWriteW = (CREDWRITEW *)fpCredWriteW;
+ g_CredEnumerateW = (CREDENUMERATEW *)fpCredEnumerateW;
+ g_CredDeleteW = (CREDDELETEW *)fpCredDeleteW;
+ g_CredFree = (CREDFREE *)fpCredFree;
+
+ g_CredInited = 1;
+ }
+ }
+
+ mswin_set_erasecreds_callback(ask_erase_credentials);
+ }
+
+ return g_CredInited;
+}
+
+#endif /* WINCRED */
diff --git a/alpine/imap.h b/alpine/imap.h
new file mode 100644
index 00000000..b254fb53
--- /dev/null
+++ b/alpine/imap.h
@@ -0,0 +1,51 @@
+/*
+ * $Id: imap.h 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_IMAP_INCLUDED
+#define PINE_IMAP_INCLUDED
+
+
+#include "../pith/imap.h"
+
+
+/* exported protoypes */
+void pine_parse_quota (MAILSTREAM *, unsigned char *, QUOTALIST *);
+QUOTALIST *pine_quotalist_copy (QUOTALIST *);
+void *pine_block_notify(int, void *);
+long pine_tcptimeout(long, long, char *);
+long pine_sslcertquery(char *, char *, char *);
+char *pine_newsrcquery(MAILSTREAM *, char *, char *);
+int url_local_certdetails(char *);
+void pine_sslfailure(char *, char *, unsigned long);
+void mm_expunged_current(long unsigned int);
+
+#ifdef LOCAL_PASSWD_CACHE
+int get_passfile_passwd(char *, char *, char *, STRLIST_S *, int);
+int is_using_passfile();
+void set_passfile_passwd(char *, char *, char *, STRLIST_S *, int, int);
+char *get_passfile_user(char *, STRLIST_S *);
+#endif /* LOCAL_PASSWD_CACHE */
+
+#if (WINCRED > 0)
+void erase_windows_credentials(void);
+#endif
+
+#ifdef APPLEKEYCHAIN
+void macos_erase_keychain(void);
+#endif
+
+
+#endif /* PINE_IMAP_INCLUDED */
diff --git a/alpine/init.c b/alpine/init.c
new file mode 100644
index 00000000..27c58cbf
--- /dev/null
+++ b/alpine/init.c
@@ -0,0 +1,396 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: init.c 101 2006-08-10 22:53:04Z mikes@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ init.c
+
+ Session initiation support routines
+
+ ====*/
+
+
+#include "headers.h"
+
+#include "../pith/init.h"
+#include "../pith/conf.h"
+#include "../pith/status.h"
+#include "../pith/folder.h"
+#include "../pith/context.h"
+
+#include "init.h"
+
+
+/* these are used to report folder directory creation problems */
+CONF_TXT_T init_md_exists[] = "The \"%s\" subdirectory already exists, but it is not writable by Alpine so Alpine cannot run. Please correct the permissions and restart Alpine.";
+
+CONF_TXT_T init_md_file[] = "Alpine requires a directory called \"%s\" and usualy creates it. You already have a regular file by that name which means Alpine cannot create the directory. Please move or remove it and start Alpine again.";
+
+CONF_TXT_T init_md_create[] = "Creating subdirectory \"%s\" where Alpine will store its mail folders.";
+
+
+/*
+ * Internal prototypes
+ */
+void display_init_err(char *, int);
+char *context_string(char *);
+int prune_folders(CONTEXT_S *, char *, int, char *, unsigned);
+int prune_move_folder(char *, char *, CONTEXT_S *);
+void delete_old_mail(struct sm_folder *, CONTEXT_S *, char *);
+
+
+
+/*----------------------------------------------------------------------
+ Make sure the alpine folders directory exists, with proper folders
+
+ Args: ps -- alpine structure to get mail directory and contexts from
+
+ Result: returns 0 if it exists or it is created and all is well
+ 1 if it is missing and can't be created.
+ ----*/
+int
+init_mail_dir(struct pine *ps)
+{
+
+/* MAIL_LIST: can we use imap4 CREATE? */
+ /*
+ * We don't really care if mail_dir exists if it isn't
+ * part of the first folder collection specified. If this
+ * is the case, it must have been created external to us, so
+ * just move one...
+ */
+ if(ps->VAR_FOLDER_SPEC && ps->VAR_FOLDER_SPEC[0]){
+ char *p = context_string(ps->VAR_FOLDER_SPEC[0]);
+ int rv = strncmp(p, ps->VAR_MAIL_DIRECTORY,
+ strlen(ps->VAR_MAIL_DIRECTORY));
+ fs_give((void **)&p);
+ if(rv)
+ return(0);
+ }
+
+ switch(is_writable_dir(ps->folders_dir)){
+ case 0:
+ /* --- all is well --- */
+ return(0);
+
+ case 1:
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, init_md_exists, ps->folders_dir);
+ display_init_err(tmp_20k_buf, 1);
+ return(-1);
+
+ case 2:
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, init_md_file, ps->folders_dir);
+ display_init_err(tmp_20k_buf, 1);
+ return(-1);
+
+ case 3:
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, init_md_create, ps->folders_dir);
+ display_init_err(tmp_20k_buf, 0);
+#ifndef _WINDOWS
+ sleep(4);
+#endif
+ if(create_mail_dir(ps->folders_dir) < 0){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Error creating subdirectory \"%s\" : %s",
+ ps->folders_dir, error_description(errno));
+ display_init_err(tmp_20k_buf, 1);
+ return(-1);
+ }
+ }
+
+ return(0);
+}
+
+
+/*----------------------------------------------------------------------
+ Make sure the default save folders exist in the default
+ save context.
+ ----*/
+void
+display_init_err(char *s, int err)
+{
+#ifdef _WINDOWS
+ mswin_messagebox(s, err);
+#else
+ int n = 0;
+
+ if(err)
+ fputc(BELL, stdout);
+
+ for(; *s; s++)
+ if(++n > 60 && isspace((unsigned char)*s)){
+ n = 0;
+ fputc('\n', stdout);
+ while(*(s+1) && isspace((unsigned char)*(s+1)))
+ s++;
+ }
+ else
+ fputc(*s, stdout);
+
+ fputc('\n', stdout);
+#endif
+}
+
+
+/*
+ * Return malloc'd string containing only the context specifier given
+ * a string containing the raw pinerc entry
+ */
+char *
+context_string(char *s)
+{
+ CONTEXT_S *t = new_context(s, NULL);
+ char *rv = NULL;
+
+ if(t){
+ /*
+ * Take what we want from context, then free the rest...
+ */
+ rv = t->context;
+ t->context = NULL; /* don't free it! */
+ free_context(&t);
+ }
+
+ return(rv ? rv : cpystr(""));
+}
+
+
+
+/*----------------------------------------------------------------------
+ Rename the current sent-mail folder to sent-mail for last month
+
+ open up sent-mail and get date of very first message
+ if date is last month rename and...
+ if files from 3 months ago exist ask if they should be deleted and...
+ if files from previous months and yes ask about them, too.
+ ----------------------------------------------------------------------*/
+int
+expire_sent_mail(void)
+{
+ int cur_month, ok = 1;
+ time_t now;
+ char tmp[50], **p;
+ struct tm *tm_now;
+ CONTEXT_S *prune_cntxt;
+
+ dprint((5, "==== expire_mail called ====\n"));
+
+ if(!check_prune_time(&now, &tm_now))
+ return 0;
+
+ cur_month = (1900 + tm_now->tm_year) * 12 + tm_now->tm_mon;
+ dprint((5, "Current month %d\n", cur_month));
+
+ /*
+ * locate the default save context...
+ */
+ if(!(prune_cntxt = default_save_context(ps_global->context_list)))
+ prune_cntxt = ps_global->context_list;
+
+ /*
+ * Since fcc's and read-mail can be an IMAP mailbox, be sure to only
+ * try expiring a list if it's an ambiguous name associated with some
+ * collection...
+ *
+ * If sentmail set outside a context, then pruning is up to the
+ * user...
+ */
+ if(prune_cntxt){
+ if(ps_global->VAR_DEFAULT_FCC && *ps_global->VAR_DEFAULT_FCC
+ && context_isambig(ps_global->VAR_DEFAULT_FCC))
+ ok = prune_folders(prune_cntxt, ps_global->VAR_DEFAULT_FCC,
+ cur_month, " SENT",
+ ps_global->pruning_rule);
+
+ if(ok && ps_global->VAR_READ_MESSAGE_FOLDER
+ && *ps_global->VAR_READ_MESSAGE_FOLDER
+ && context_isambig(ps_global->VAR_READ_MESSAGE_FOLDER))
+ ok = prune_folders(prune_cntxt, ps_global->VAR_READ_MESSAGE_FOLDER,
+ cur_month, " READ",
+ ps_global->pruning_rule);
+ }
+
+ /*
+ * Within the default prune context,
+ * prune back the folders with the given name
+ */
+ if(ok && prune_cntxt && (p = ps_global->VAR_PRUNED_FOLDERS))
+ for(; ok && *p; p++)
+ if(**p && context_isambig(*p))
+ ok = prune_folders(prune_cntxt, *p, cur_month, "",
+ ps_global->pruning_rule);
+
+ /*
+ * Mark that we're done for this month...
+ */
+ if(ok){
+ ps_global->last_expire_year = tm_now->tm_year;
+ ps_global->last_expire_month = tm_now->tm_mon;
+ snprintf(tmp, sizeof(tmp), "%d.%d", ps_global->last_expire_year,
+ ps_global->last_expire_month + 1);
+ set_variable(V_LAST_TIME_PRUNE_QUESTION, tmp, 1, 1, Main);
+ }
+
+ return(1);
+}
+
+
+/*----------------------------------------------------------------------
+ Offer to delete old sent-mail folders
+
+ Args: sml -- The list of sent-mail folders
+
+ ----*/
+int
+prune_folders(CONTEXT_S *prune_cntxt, char *folder_base, int cur_month,
+ char *type, unsigned int pr)
+{
+ char path2[MAXPATH+1], prompt[128], tmp[21];
+ int month_to_use, exists;
+ struct sm_folder *mail_list, *sm;
+
+ mail_list = get_mail_list(prune_cntxt, folder_base);
+ free_folder_list(prune_cntxt);
+
+#ifdef DEBUG
+ for(sm = mail_list; sm != NULL && sm->name != NULL; sm++)
+ dprint((5,"Old sent-mail: %5d %s\n", sm->month_num,
+ sm->name[0] ? sm->name : "?"));
+#endif
+
+ for(sm = mail_list; sm != NULL && sm->name != NULL; sm++)
+ if(sm->month_num == cur_month - 1)
+ break; /* matched a month */
+
+ month_to_use = (sm == NULL || sm->name == NULL) ? cur_month - 1 : 0;
+
+ dprint((5, "Month_to_use : %d\n", month_to_use));
+
+ if(month_to_use == 0 || pr == PRUNE_NO_AND_ASK || pr == PRUNE_NO_AND_NO)
+ goto delete_old;
+
+ strncpy(path2, folder_base, sizeof(path2)-1);
+ path2[sizeof(path2)-1] = '\0';
+
+ if(F_ON(F_PRUNE_USES_ISO,ps_global)){ /* sent-mail-yyyy-mm */
+ snprintf(path2 + strlen(path2), sizeof(path2)-strlen(path2), "-%4.4d-%2.2d", month_to_use/12,
+ month_to_use % 12 + 1);
+ }
+ else{
+ strncpy(tmp, month_abbrev((month_to_use % 12)+1), 20);
+ tmp[sizeof(tmp)-1] = '\0';
+ lcase((unsigned char *) tmp);
+#ifdef DOS
+ if(*prune_cntxt->context != '{'){
+ int i;
+
+ i = strlen(path2);
+ snprintf(path2 + (size_t)((i > 4) ? 4 : i),
+ sizeof(path2)- ((i > 4) ? 4 : i),
+ "%2.2d%2.2d", (month_to_use % 12) + 1,
+ ((month_to_use / 12) - 1900) % 100);
+ }
+ else
+#endif
+ snprintf(path2 + strlen(path2), sizeof(path2)-strlen(path2), "-%.20s-%d", tmp, month_to_use/12);
+ }
+
+ Writechar(BELL, 0);
+ snprintf(prompt, sizeof(prompt), "Move current \"%.50s\" to \"%.50s\"", folder_base, path2);
+ if((exists = folder_exists(prune_cntxt, folder_base)) == FEX_ERROR){
+ dprint((5, "prune_folders: Error testing existence\n"));
+ return(0);
+ }
+ else if(exists == FEX_NOENT){ /* doesn't exist */
+ dprint((5, "prune_folders: nothing to prune <%s %s>\n",
+ prune_cntxt->context ? prune_cntxt->context : "?",
+ folder_base ? folder_base : "?"));
+ goto delete_old;
+ }
+ else if(!(exists & FEX_ISFILE)
+ || pr == PRUNE_NO_AND_ASK || pr == PRUNE_NO_AND_NO
+ || ((pr == PRUNE_ASK_AND_ASK || pr == PRUNE_ASK_AND_NO) &&
+ want_to(prompt, 'n', 0, h_wt_expire, WT_FLUSH_IN) == 'n')){
+ dprint((5, "User declines renaming %s\n",
+ ps_global->VAR_DEFAULT_FCC ? ps_global->VAR_DEFAULT_FCC : "?"));
+ goto delete_old;
+ }
+
+ prune_move_folder(folder_base, path2, prune_cntxt);
+
+ delete_old:
+ if(pr == PRUNE_ASK_AND_ASK || pr == PRUNE_YES_AND_ASK
+ || pr == PRUNE_NO_AND_ASK)
+ delete_old_mail(mail_list, prune_cntxt, type);
+
+ if((sm = mail_list) != NULL){
+ while(sm->name){
+ fs_give((void **)&(sm->name));
+ sm++;
+ }
+
+ fs_give((void **)&mail_list);
+ }
+
+ return(1);
+}
+
+
+/*----------------------------------------------------------------------
+ Offer to delete old sent-mail folders
+
+ Args: sml -- The list of sent-mail folders
+ fcc_cntxt -- context to delete list of folders in
+ type -- label indicating type of folders being deleted
+
+ ----*/
+void
+delete_old_mail(struct sm_folder *sml, CONTEXT_S *fcc_cntxt, char *type)
+{
+ char prompt[150];
+ struct sm_folder *sm;
+
+ for(sm = sml; sm != NULL && sm->name != NULL; sm++){
+ if(sm->name[0] == '\0') /* can't happen */
+ continue;
+
+ snprintf(prompt, sizeof(prompt),
+ "To save disk space, delete old%.10s mail folder \"%.30s\" ",
+ type, sm->name);
+ if(want_to(prompt, 'n', 0, h_wt_delete_old, WT_FLUSH_IN) == 'y'){
+
+ if(!context_delete(fcc_cntxt, NULL, sm->name)){
+ q_status_message1(SM_ORDER,
+ 3, 3, "Error deleting \"%s\".", sm->name);
+ dprint((1, "Error context_deleting %s in %s\n", sm->name,
+ (fcc_cntxt && fcc_cntxt->context)
+ ? fcc_cntxt->context : "<null>"));
+ }
+ else{
+ int index;
+
+ if((index = folder_index(sm->name, fcc_cntxt, FI_FOLDER)) >= 0)
+ folder_delete(index, FOLDERS(fcc_cntxt));
+ }
+ }else{
+ /* break;*/ /* skip out of the whole thing when he says no */
+ /* Decided to keep asking anyway */
+ }
+ }
+}
+
+
+
diff --git a/alpine/init.h b/alpine/init.h
new file mode 100644
index 00000000..e9c18053
--- /dev/null
+++ b/alpine/init.h
@@ -0,0 +1,27 @@
+/*
+ * $Id: init.h 82 2006-07-12 23:36:59Z mikes@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_INIT_INCLUDED
+#define PINE_INIT_INCLUDED
+
+
+/* exported protoypes */
+int init_mail_dir(struct pine *);
+int expire_sent_mail(void);
+
+
+#endif /* PINE_INIT_INCLUDED */
+
+
diff --git a/alpine/kblock.c b/alpine/kblock.c
new file mode 100644
index 00000000..774f31ad
--- /dev/null
+++ b/alpine/kblock.c
@@ -0,0 +1,196 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: kblock.c 1025 2008-04-08 22:59:38Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "headers.h"
+#include "kblock.h"
+#include "status.h"
+#include "titlebar.h"
+#include "radio.h"
+#include "../pith/conf.h"
+#include "../pith/state.h"
+
+#ifdef KEYBOARD_LOCK
+
+
+/*
+ * Internal prototypes
+ */
+void redraw_kl_body(void);
+void redraw_klocked_body(void);
+void draw_klocked_body(char *, char *);
+
+
+static char *klockin, *klockame;
+
+
+void
+redraw_kl_body(void)
+{
+ ClearScreen();
+
+ set_titlebar(_("KEYBOARD LOCK"), ps_global->mail_stream,
+ ps_global->context_current, ps_global->cur_folder, NULL,
+ 1, FolderName, 0, 0, NULL);
+
+ PutLine0(6,3 ,
+ _("You may lock this keyboard so that no one else can access your mail"));
+ PutLine0(8, 3 ,
+ _("while you are away. The screen will be locked after entering the "));
+ PutLine0(10, 3 ,
+ _("password to be used for unlocking the keyboard when you return."));
+ fflush(stdout);
+}
+
+
+void
+redraw_klocked_body(void)
+{
+ ClearScreen();
+
+ set_titlebar(_("KEYBOARD LOCK"), ps_global->mail_stream,
+ ps_global->context_current, ps_global->cur_folder, NULL,
+ 1, FolderName, 0, 0, NULL);
+
+ PutLine2(6, 3, _("This keyboard is locked by %s <%s>."),klockame, klockin);
+ PutLine0(8, 3, _("To unlock, enter password used to lock the keyboard."));
+ fflush(stdout);
+}
+
+
+void
+draw_klocked_body(char *login, char *username)
+{
+ klockin = login;
+ klockame = username;
+ redraw_klocked_body();
+}
+
+
+/*----------------------------------------------------------------------
+ Execute the lock keyboard command
+
+ Args: None
+
+ Result: keyboard is locked until user gives password
+ ---*/
+
+int
+lock_keyboard(void)
+{
+ struct pine *ps = ps_global;
+ char inpasswd[80], passwd[80], pw[80];
+ HelpType help = NO_HELP;
+ int i, times, old_suspend, flags;
+
+ passwd[0] = '\0';
+ redraw_kl_body();
+ ps->redrawer = redraw_kl_body;
+
+ times = atoi(ps->VAR_KBLOCK_PASSWD_COUNT);
+ if(times < 1 || times > 5){
+ dprint((2,
+ "Kblock-passwd-count var out of range (1 to 5) [%d]\n", times));
+ times = 1;
+ }
+
+ inpasswd[0] = '\0';
+
+ for(i = 0; i < times; i++){
+ pw[0] = '\0';
+ while(1){ /* input pasword to use for locking */
+ int rc;
+ char prompt[50];
+
+ if(i > 1)
+ snprintf(prompt, sizeof(prompt), _("Retype password to LOCK keyboard (Yes, again) : "));
+ else if(i)
+ snprintf(prompt, sizeof(prompt), _("Retype password to LOCK keyboard : "));
+ else
+ snprintf(prompt, sizeof(prompt), _("Enter password to LOCK keyboard : "));
+
+ flags = F_ON(F_QUELL_ASTERISKS, ps_global) ? OE_PASSWD_NOAST : OE_PASSWD;
+ rc = optionally_enter(pw, -FOOTER_ROWS(ps), 0, sizeof(pw),
+ prompt, NULL, help, &flags);
+
+ if(rc == 3)
+ help = help == NO_HELP ? h_kb_lock : NO_HELP;
+ else if(rc == 1 || pw[0] == '\0'){
+ q_status_message(SM_ORDER, 0, 2, _("Keyboard lock cancelled"));
+ return(-1);
+ }
+ else if(rc != 4)
+ break;
+ }
+
+ if(!inpasswd[0]){
+ strncpy(inpasswd, pw, sizeof(inpasswd));
+ inpasswd[sizeof(inpasswd)-1] = '\0';
+ }
+ else if(strcmp(inpasswd, pw)){
+ q_status_message(SM_ORDER, 0, 2,
+ _("Mismatch with initial password: keyboard lock cancelled"));
+ return(-1);
+ }
+ }
+
+ if(want_to(_("Really lock keyboard with entered password"), 'y', 'n',
+ NO_HELP, WT_NORM) != 'y'){
+ q_status_message(SM_ORDER, 0, 2, _("Keyboard lock cancelled"));
+ return(-1);
+ }
+
+ draw_klocked_body(ps->VAR_USER_ID ? ps->VAR_USER_ID : "<no-user>",
+ ps->VAR_PERSONAL_NAME ? ps->VAR_PERSONAL_NAME : "<no-name>");
+
+ ps->redrawer = redraw_klocked_body;
+ if((old_suspend = F_ON(F_CAN_SUSPEND, ps_global)) != 0)
+ F_TURN_OFF(F_CAN_SUSPEND, ps_global);
+
+ while(strcmp(inpasswd, passwd)){
+ if(passwd[0])
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("Password to UNLOCK doesn't match password used to LOCK"));
+
+ help = NO_HELP;
+ while(1){
+ int rc;
+
+ flags = OE_DISALLOW_CANCEL
+ | (F_ON(F_QUELL_ASTERISKS, ps_global) ? OE_PASSWD_NOAST : OE_PASSWD);
+ rc = optionally_enter(passwd, -FOOTER_ROWS(ps), 0, sizeof(passwd),
+ _("Enter password to UNLOCK keyboard : "),NULL,
+ help, &flags);
+ if(rc == 3) {
+ help = help == NO_HELP ? h_oe_keylock : NO_HELP;
+ continue;
+ }
+
+ if(rc != 4)
+ break;
+ }
+ }
+
+ if(old_suspend)
+ F_TURN_ON(F_CAN_SUSPEND, ps_global);
+
+ q_status_message(SM_ORDER, 0, 3, _("Keyboard Unlocked"));
+ return(0);
+}
+
+
+#endif /* KEYBOARD_LOCK */
diff --git a/alpine/kblock.h b/alpine/kblock.h
new file mode 100644
index 00000000..0b6984c0
--- /dev/null
+++ b/alpine/kblock.h
@@ -0,0 +1,27 @@
+/*
+ * $Id: kblock.h 870 2007-12-14 06:45:43Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_KBLOCK_INCLUDED
+#define PINE_KBLOCK_INCLUDED
+
+
+#ifdef KEYBOARD_LOCK
+
+/* exported protoypes */
+int lock_keyboard(void);
+
+#endif /* KEYBOARD_LOCK */
+
+#endif /* PINE_KBLOCK_INCLUDED */
diff --git a/alpine/keymenu.c b/alpine/keymenu.c
new file mode 100644
index 00000000..929e6ada
--- /dev/null
+++ b/alpine/keymenu.c
@@ -0,0 +1,3961 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: keymenu.c 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "headers.h"
+#include "keymenu.h"
+#include "mailcmd.h"
+#include "signal.h"
+#include "status.h"
+#include "../pith/bitmap.h"
+#include "../pith/conf.h"
+#include "../pith/state.h"
+
+
+/*
+ * We put all of the keymenu definitions here so that it is easy to
+ * share them. The names are in keymenu.h, as well. Some of these
+ * aren't easily shareable because we modify them dynamically but
+ * here they are anyway. It's not altogether clear that this is a good idea.
+ * Perhaps it would be better to just define the keymenus multiple times
+ * in different source files even though they are the same, with static
+ * declarations.
+ *
+ * The key numbers are sometimes used symbolically, like OTHER_KEY. Those
+ * are in keymenu.h, too, and are hard to use. If you change a menu here
+ * be sure to check those key numbers that go with it in keymenu.h.
+ */
+
+#ifdef _WINDOWS
+void configure_menu_items (struct key_menu *, bitmap_t);
+#endif
+
+
+/*
+ * Macro to simplify instantiation of key_menu structs from key structs
+ */
+#define INST_KEY_MENU(X, Y) struct key_menu X = \
+ {sizeof(Y)/(sizeof(Y[0])*12), 0, 0, Y}
+
+struct key cancel_keys[] =
+ {HELP_MENU,
+ {"^C",N_("Cancel"),{MC_NONE},KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU};
+INST_KEY_MENU(cancel_keymenu, cancel_keys);
+
+
+/*
+ * A bunch of these are NULL_MENU because we want to change them dynamically.
+ */
+struct key ab_keys[] =
+ {HELP_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ /* TRANSLATORS: go to Previous Entry */
+ {"P", N_("PrevEntry"), {MC_PREVITEM,1,{'p'}}, KS_NONE},
+ /* TRANSLATORS: go to Next Entry */
+ {"N", N_("NextEntry"), {MC_NEXTITEM,1,{'n'}}, KS_NONE},
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ QUIT_MENU,
+ NULL_MENU,
+ LISTFLD_MENU,
+ GOTO_MENU,
+ INDEX_MENU,
+ RCOMPOSE_MENU,
+ PRYNTTXT_MENU,
+ NULL_MENU,
+ SAVE_MENU,
+ FORWARD_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ /* TRANSLATORS: Select this Entry */
+ {";",N_("Select"),{MC_SELECT,1,{';'}},KS_NONE},
+ /* TRANSLATORS: Apply a command to several objects at once */
+ {"A",N_("Apply"),{MC_APPLY,1,{'a'}},KS_APPLY},
+ /* TRANSLATORS: Select Current entry */
+ {":",N_("SelectCur"),{MC_SELCUR,1,{':'}},KS_SELECTCUR},
+ /* TRANSLATORS: Zoom refers to zooming in on a smaller set of
+ items, like with a camera zoom lense */
+ {"Z",N_("ZoomMode"),{MC_ZOOM,1,{'z'}},KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU};
+INST_KEY_MENU(ab_keymenu, ab_keys);
+
+
+struct key abook_select_keys[] =
+ {HELP_MENU,
+ NULL_MENU,
+ {"E", N_("Exit"), {MC_EXIT,1,{'e'}}, KS_EXITMODE},
+ {"S", "[" N_("Select") "]", {MC_CHOICE,3,{'s',ctrl('M'),ctrl('J')}}, KS_NONE},
+ /* TRANSLATORS: go to Previous entry */
+ {"P", N_("Prev"), {MC_PREVITEM, 1, {'p'}}, KS_NONE},
+ {"N", N_("Next"), {MC_NEXTITEM, 2, {'n', TAB}}, KS_NONE},
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU,
+ NULL_MENU,
+ WHEREIS_MENU};
+INST_KEY_MENU(abook_select_km, abook_select_keys);
+
+
+struct key abook_view_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ /* TRANSLATORS: Abook is an abbreviation for Address Book */
+ {"<",N_("Abook"),{MC_EXIT,2,{'<',','}},KS_NONE},
+ {"U",N_("Update"),{MC_EDIT,1,{'u'}},KS_NONE},
+ /* TRANSLATORS: ComposeTo means to start editing a new message to
+ this address book entry */
+ {"C",N_("ComposeTo"),{MC_COMPOSE,1,{'c'}},KS_COMPOSER},
+ RCOMPOSE_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+ /* TRANSLATORS: abbreviation for Forward as Email */
+ {"F", N_("Fwd Email"), {MC_FORWARD, 1, {'f'}}, KS_FORWARD},
+ SAVE_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ /* TRANSLATORS: View the highlighted link, for example, a URL */
+ {"V",N_("ViewLink"),{MC_VIEW_HANDLE,3,{'v',ctrl('m'),ctrl('j')}},KS_NONE},
+ NULL_MENU,
+ /* TRANSLATORS: go to the previous link, for example, the previous URL */
+ {"^B",N_("PrevLink"),{MC_PREV_HANDLE,1,{ctrl('B')}},KS_NONE},
+ {"^F",N_("NextLink"),{MC_NEXT_HANDLE,1,{ctrl('F')}},KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU};
+INST_KEY_MENU(abook_view_keymenu, abook_view_keys);
+
+
+struct key abook_text_keys[] =
+ {HELP_MENU,
+ NULL_MENU,
+ {"E",N_("Exit Viewer"),{MC_EXIT,1,{'e'}},KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+ FWDEMAIL_MENU,
+ {"S", N_("Save"), {MC_SAVETEXT,1,{'s'}}, KS_SAVE}};
+INST_KEY_MENU(abook_text_km, abook_text_keys);
+
+
+struct key ldap_view_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ /* TRANSLATORS: go back to the index of results instead of
+ viewing this particular entry */
+ {"<",N_("Results Index"),{MC_EXIT,2,{'<',','}},KS_NONE},
+ NULL_MENU,
+ {"C", N_("ComposeTo"), {MC_COMPOSE,1,{'c'}}, KS_COMPOSER},
+ RCOMPOSE_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+ FWDEMAIL_MENU,
+ SAVE_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ {"V",N_("ViewLink"),{MC_VIEW_HANDLE,3,{'v',ctrl('m'),ctrl('j')}},KS_NONE},
+ NULL_MENU,
+ {"^B",N_("PrevLink"),{MC_PREV_HANDLE,1,{ctrl('B')}},KS_NONE},
+ {"^F",N_("NextLink"),{MC_NEXT_HANDLE,1,{ctrl('F')}},KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(ldap_view_keymenu, ldap_view_keys);
+
+
+struct key context_mgr_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ {"<", N_("Main Menu"), {MC_MAIN,3,{'m','<',','}}, KS_EXITMODE},
+ /* TRANSLATORS: View this Collection of folders */
+ {">", "[" N_("View Cltn") "]",
+ {MC_CHOICE,5,{'v','>','.',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREVC_MENU,
+ NEXTC_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ QUIT_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ GOTO_MENU,
+ CIND_MENU,
+ COMPOSE_MENU,
+ PRYNTTXT_MENU,
+ RCOMPOSE_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(c_mgr_km, context_mgr_keys);
+
+
+struct key context_cfg_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ {"E", N_("Exit Setup"), {MC_EXIT,1,{'e'}}, KS_EXITMODE},
+ {"C", "[" N_("Change") "]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREVC_MENU,
+ NEXTC_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ /* TRANSLATORS; Add a Collection of folders */
+ {"A", N_("Add Cltn"), {MC_ADD,1,{'a'}}, KS_NONE},
+ DELC_MENU,
+ /* TRANSLATORS; Change the order of items in a list */
+ {"$", N_("Shuffle"), {MC_SHUFFLE,1,{'$'}},KS_NONE},
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ PRYNTTXT_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(c_cfg_km, context_cfg_keys);
+
+
+struct key context_select_keys[] =
+ {HELP_MENU,
+ {"E", N_("ExitSelect"), {MC_EXIT,1,{'e'}}, KS_EXITMODE},
+ NULL_MENU,
+ /* TRANSLATORS: View this collection */
+ {">", "[" N_("View Cltn") "]",
+ {MC_CHOICE, 5, {'v','>','.',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREVC_MENU,
+ NEXTC_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU,
+ NULL_MENU,
+ WHEREIS_MENU};
+INST_KEY_MENU(c_sel_km, context_select_keys);
+
+
+struct key context_fcc_keys[] =
+ {HELP_MENU,
+ {"E", N_("ExitSelect"), {MC_EXIT,1,{'e'}}, KS_EXITMODE},
+ NULL_MENU,
+ {">", "[" N_("View Cltn") "]",
+ {MC_CHOICE, 5, {'v','>','.',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREVC_MENU,
+ NEXTC_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU,
+ NULL_MENU,
+ WHEREIS_MENU};
+INST_KEY_MENU(c_fcc_km, context_fcc_keys);
+
+static struct key quota_keys[] =
+ {HELP_MENU,
+ NULL_MENU,
+ {"E","Exit",{MC_EXIT,3,{'e','i',ctrl('C')}},KS_EXITMODE},
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU};
+INST_KEY_MENU(pine_quota_keymenu, quota_keys);
+
+struct key folder_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ {"<", NULL, {MC_EXIT,2,{'<',','}}, KS_NONE},
+ {">", NULL, {MC_CHOICE,2,{'>','.'}}, KS_NONE},
+ PREVF_MENU,
+ NEXTF_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ /* TRANSLATORS: make an addition, for example add a new folder
+ or a new entry in an address book */
+ {"A",N_("Add"),{MC_ADDFLDR,1,{'a'}},KS_NONE},
+ DELETE_MENU,
+ /* TRANSLATORS: change the name of something */
+ {"R",N_("Rename"),{MC_RENAMEFLDR,1,{'r'}}, KS_NONE},
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ QUIT_MENU,
+ MAIN_MENU,
+ {"V", "[" N_("View Fldr") "]", {MC_OPENFLDR}, KS_NONE},
+ GOTO_MENU,
+ CIND_MENU,
+ COMPOSE_MENU,
+ {"%", N_("Print"), {MC_PRINTFLDR,1,{'%'}}, KS_PRINT},
+ {"Z", N_("ZoomMode"), {MC_ZOOM,1,{'z'}}, KS_NONE},
+ {";",N_("Select"),{MC_SELECT,1,{';'}},KS_SELECT},
+ /* TRANSLATORS: Select current item */
+ {":",N_("SelectCur"),{MC_SELCUR,1,{':'}},KS_SELECT},
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ {"$", N_("Shuffle"), {MC_SHUFFLE,1,{'$'}},KS_NONE},
+ RCOMPOSE_MENU,
+ EXPORT_MENU,
+ /* TRANSLATORS: Import refers to bringing something in from
+ outside of alpine's normal world */
+ {"U", N_("Import"), {MC_IMPORT,1,{'u'}},KS_NONE},
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(folder_km, folder_keys);
+
+
+struct key folder_sel_keys[] =
+ {HELP_MENU,
+ {"E", N_("ExitSelect"), {MC_EXIT,1,{'e'}}, KS_EXITMODE},
+ NULL_MENU,
+ {NULL, NULL, {MC_CHOICE,0}, KS_NONE},
+ PREVF_MENU,
+ NEXTF_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ {"S", N_("Select"), {MC_OPENFLDR,1,{'s'}}, KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(folder_sel_km, folder_sel_keys);
+
+
+struct key folder_sela_keys[] =
+ {HELP_MENU,
+ {"E", N_("ExitSelect"), {MC_EXIT,1,{'e'}}, KS_EXITMODE},
+ NULL_MENU,
+ {NULL, NULL, {MC_CHOICE,0}, KS_NONE},
+ PREVF_MENU,
+ NEXTF_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ {"S", N_("Select"), {MC_OPENFLDR,1,{'s'}}, KS_NONE},
+ NULL_MENU,
+ {"A", N_("AddNew"), {MC_ADD,1,{'a'}}, KS_NONE},
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(folder_sela_km, folder_sela_keys);
+
+
+struct key folder_sub_keys[] =
+ {HELP_MENU,
+ /* TRANSLATORS: Subscribe to a news group */
+ {"S", N_("Subscribe"), {MC_CHOICE,1,{'s'}}, KS_NONE},
+ /* TRANSLATORS: Exit Subscribe screen */
+ {"E", N_("ExitSubscb"), {MC_EXIT,1,{'e'}}, KS_EXITMODE},
+ {NULL, "[" N_("Select") "]", {MC_CHOICE,3,{'s',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREVF_MENU,
+ NEXTF_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ /* TRANSLATORS: List Mode in alpine is where you can select not just
+ one of something but you can select a whole list of something, for
+ example a whole list of addresses to send to. */
+ {"L", N_("List Mode"), {MC_LISTMODE, 1, {'l'}}, KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(folder_sub_km, folder_sub_keys);
+
+
+struct key folder_post_keys[] =
+ {HELP_MENU,
+ NULL_MENU,
+ {"E", N_("ExitSelect"), {MC_EXIT,1,{'e'}}, KS_EXITMODE},
+ {"S", "[" N_("Select") "]", {MC_CHOICE, 3, {'s',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREVF_MENU,
+ NEXTF_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU,
+ NULL_MENU,
+ WHEREIS_MENU};
+INST_KEY_MENU(folder_post_km, folder_post_keys);
+
+
+struct key help_keys[] =
+ {MAIN_MENU,
+ {NULL,NULL,{MC_EXIT,1,{'e'}}, KS_EXITMODE},
+ {NULL,NULL,{MC_EXIT,1,{'e'}}, KS_EXITMODE},
+ {NULL,NULL,{MC_VIEW_HANDLE,3,{'v',ctrl('m'),ctrl('j')}},KS_NONE},
+ {"^B",N_("PrevLink"),{MC_PREV_HANDLE,1,{ctrl('B')}},KS_NONE},
+ {"^F",N_("NextLink"),{MC_NEXT_HANDLE,1,{ctrl('F')}},KS_NONE},
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ PRYNTMSG_MENU,
+ {"Z",N_("Print All"),{MC_PRINTALL,1,{'z'}},KS_NONE},
+ {"N",N_("Name"),{MC_EXPORT,1,{'n'}},KS_NONE},
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(help_keymenu, help_keys);
+
+
+struct key rev_msg_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ {"E",N_("Exit Viewer"),{MC_EXIT,1,{'e'}},KS_EXITMODE},
+ NULL_MENU,
+ {"T",NULL,{MC_TOGGLE,1,{'t'}},KS_NONE},
+ {"D",NULL,{MC_JUMP,1,{'d'}},KS_NONE},
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+ FWDEMAIL_MENU,
+ {"S", N_("Save"), {MC_SAVETEXT,1,{'s'}}, KS_SAVE},
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(rev_msg_keymenu, rev_msg_keys);
+
+
+struct key ans_certfail_keys[] =
+ {NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ /* TRANSLATORS: Continue means to keep going. The user is paused to read
+ something and has to tell us to continue when they are finished. */
+ {"C","[" N_("Continue") "]",{MC_YES,3,{'c',ctrl('J'),ctrl('M')}},KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU};
+INST_KEY_MENU(ans_certfail_keymenu, ans_certfail_keys);
+
+
+struct key ans_certquery_keys[] =
+ {HELP_MENU,
+ NULL_MENU,
+ {"Y",N_("Yes, continue"),{MC_YES,1,{'y'}},KS_NONE},
+ {"D","[" N_("Details") "]",{MC_VIEW_HANDLE,3,{'d',ctrl('M'),ctrl('J')}},KS_NONE},
+ {"N",N_("No"),{MC_NO,1,{'n'}},KS_NONE},
+ NULL_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+ FWDEMAIL_MENU,
+ {"S", N_("Save"), {MC_SAVETEXT,1,{'s'}}, KS_SAVE}};
+INST_KEY_MENU(ans_certquery_keymenu, ans_certquery_keys);
+
+
+struct key forge_keys[] =
+ {HELP_MENU,
+ NULL_MENU,
+ {"Y",N_("Yes, continue"),{MC_YES,1,{'y'}},KS_NONE},
+ {"N",N_("No"),{MC_NO,1,{'n'}},KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+ FWDEMAIL_MENU,
+ {"S", N_("Save"), {MC_SAVETEXT,1,{'s'}}, KS_SAVE}};
+INST_KEY_MENU(forge_keymenu, forge_keys);
+
+
+struct key listmgr_keys[] =
+ {HELP_MENU,
+ NULL_MENU,
+ /* TRANSLATORS: Exit Command List */
+ {"E",N_("Exit CmdList"),{MC_EXIT,1,{'e'}},KS_EXITMODE},
+ {"Ret","[" N_("Try Command") "]",{MC_VIEW_HANDLE,3,
+ {ctrl('m'),ctrl('j'),KEY_RIGHT}},KS_NONE},
+ /* TRANSLATORS: go to Previous Command in list */
+ {"^B",N_("Prev Cmd"),{MC_PREV_HANDLE,1,{ctrl('B')}},KS_NONE},
+ /* TRANSLATORS: go to Next Command in list */
+ {"^F",N_("Next Cmd"),{MC_NEXT_HANDLE,1,{ctrl('F')}},KS_NONE},
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(listmgr_keymenu, listmgr_keys);
+
+
+struct key index_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ {"<", NULL, {MC_FOLDERS,2,{'<',','}}, KS_NONE},
+ VIEWMSG_MENU,
+ PREVMSG_MENU,
+ NEXTMSG_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ DELETE_MENU,
+ UNDELETE_MENU,
+ REPLY_MENU,
+ FORWARD_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ MAIN_MENU,
+ QUIT_MENU,
+ COMPOSE_MENU,
+ GOTO_MENU,
+ TAB_MENU,
+ WHEREIS_MENU,
+ PRYNTMSG_MENU,
+ TAKE_MENU,
+ SAVE_MENU,
+ EXPORT_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ {"X",NULL,{MC_EXPUNGE,1,{'x'}},KS_NONE},
+ /* TRANSLATORS: this stands for unexclude which is the opposite
+ of the exclude command. Exclude eliminates some messages from
+ the view and unexclude gets them back. */
+ {"&",N_("unXclude"),{MC_UNEXCLUDE,1,{'&'}},KS_NONE},
+ {";",N_("Select"),{MC_SELECT,1,{';'}},KS_SELECT},
+ {"A",N_("Apply"),{MC_APPLY,1,{'a'}},KS_APPLY},
+ FLDRSORT_MENU,
+ JUMP_MENU,
+ HDRMODE_MENU,
+ BOUNCE_MENU,
+ FLAG_MENU,
+ PIPE_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ {":",N_("SelectCur"),{MC_SELCUR,1,{':'}},KS_SELECTCUR},
+ {"Z",N_("ZoomMode"),{MC_ZOOM,1,{'z'}},KS_ZOOM},
+ LISTFLD_MENU,
+ RCOMPOSE_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU,
+ NULL_MENU,
+ /* TRANSLATORS: toggles a collapsed view or an expanded view
+ of a message thread on and off */
+ {"/",N_("Collapse/Expand"),{MC_COLLAPSE,1,{'/'}},KS_NONE},
+ {"@", N_("Quota"), {MC_QUOTA,1,{'@'}}, KS_NONE},
+ NULL_MENU};
+INST_KEY_MENU(index_keymenu, index_keys);
+
+
+struct key simple_index_keys[] =
+ {HELP_MENU,
+ {"E",N_("ExitSelect"),{MC_EXIT,1,{'e'}},KS_EXITMODE},
+ NULL_MENU,
+ {"S","[" N_("Select") "]",{MC_SELECT,3,{'s',ctrl('M'),ctrl('J')}},KS_SELECT},
+ PREVMSG_MENU,
+ NEXTMSG_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ DELETE_MENU,
+ UNDELETE_MENU,
+ WHEREIS_MENU,
+ NULL_MENU};
+INST_KEY_MENU(simple_index_keymenu, simple_index_keys);
+
+
+struct key thread_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ /* TRANSLATORS: go to the Folder List */
+ {"<", N_("FldrList"), {MC_FOLDERS,2,{'<',','}}, KS_NONE},
+ /* TRANSLATORS: View a Thread of messages */
+ {">", "[" N_("ViewThd") "]", {MC_VIEW_ENTRY,5,{'v','.','>',ctrl('M'),ctrl('J')}},
+ KS_VIEW},
+ /* TRANSLATORS: go to the Previous Thread */
+ {"P", N_("PrevThd"), {MC_PREVITEM, 1, {'p'}}, KS_PREVMSG},
+ /* TRANSLATORS: go to the Next Thread */
+ {"N", N_("NextThd"), {MC_NEXTITEM, 1, {'n'}}, KS_NEXTMSG},
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ DELETE_MENU,
+ UNDELETE_MENU,
+ REPLY_MENU,
+ FORWARD_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ MAIN_MENU,
+ QUIT_MENU,
+ COMPOSE_MENU,
+ GOTO_MENU,
+ TAB_MENU,
+ WHEREIS_MENU,
+ PRYNTMSG_MENU,
+ TAKE_MENU,
+ SAVE_MENU,
+ EXPORT_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ {"X",NULL,{MC_EXPUNGE,1,{'x'}},KS_NONE},
+ {"&",N_("unXclude"),{MC_UNEXCLUDE,1,{'&'}},KS_NONE},
+ {";",N_("Select"),{MC_SELECT,1,{';'}},KS_SELECT},
+ {"A",N_("Apply"),{MC_APPLY,1,{'a'}},KS_APPLY},
+ FLDRSORT_MENU,
+ JUMP_MENU,
+ HDRMODE_MENU,
+ BOUNCE_MENU,
+ FLAG_MENU,
+ PIPE_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ {":",N_("SelectCur"),{MC_SELCUR,1,{':'}},KS_SELECTCUR},
+ {"Z",N_("ZoomMode"),{MC_ZOOM,1,{'z'}},KS_ZOOM},
+ LISTFLD_MENU,
+ RCOMPOSE_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU,
+ NULL_MENU,
+ {"/",N_("Collapse/Expand"),{MC_COLLAPSE,1,{'/'}},KS_NONE},
+ {"@", N_("Quota"), {MC_QUOTA,1,{'@'}}, KS_NONE},
+ NULL_MENU};
+INST_KEY_MENU(thread_keymenu, thread_keys);
+
+
+struct key att_index_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ {"<",NULL,{MC_EXIT,2,{'<',','}},KS_EXITMODE},
+ {">","[" N_("View") "]",{MC_VIEW_ATCH,5,{'v','>','.',ctrl('M'),ctrl('J')}},
+ KS_VIEW},
+ /* TRANSLATORS: go to Previous Attachment */
+ {"P", N_("PrevAttch"),{MC_PREVITEM,4,{'p',ctrl('B'),ctrl('P'),KEY_UP}},
+ KS_PREVMSG},
+ /* TRANSLATORS: go to Next Attachment */
+ {"N", N_("NextAtch"),
+ {MC_NEXTITEM, 5, {'n','\t',ctrl('F'),ctrl('N'), KEY_DOWN}},
+ KS_NEXTMSG},
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ DELETE_MENU,
+ UNDELETE_MENU,
+ {"S", N_("Save"), {MC_SAVETEXT,1,{'s'}}, KS_SAVE},
+ {NULL, NULL, {MC_EXPORT, 1, {'e'}}, KS_EXPORT},
+
+ HELP_MENU,
+ OTHER_MENU,
+ MAIN_MENU,
+ QUIT_MENU,
+ PIPE_MENU,
+ BOUNCE_MENU,
+ /* TRANSLATORS: About Attachment, a short description of the attachment */
+ {"A",N_("AboutAttch"),{MC_ABOUTATCH,1,{'a'}},KS_NONE},
+ WHEREIS_MENU,
+ {"%", N_("Print"), {MC_PRINTMSG,1,{'%'}}, KS_PRINT},
+ INDEX_MENU,
+ REPLY_MENU,
+ FORWARD_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ HDRMODE_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU};
+INST_KEY_MENU(att_index_keymenu, att_index_keys);
+
+
+struct key att_view_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ {"<",NULL,{MC_EXIT,2,{'<',','}},KS_EXITMODE},
+ /* TRANSLATORS: View highlighted URL */
+ {"Ret","[" N_("View Hilite") "]",{MC_VIEW_HANDLE,3,
+ {ctrl('m'),ctrl('j'),KEY_RIGHT}},KS_NONE},
+ /* TRANSLATORS: go to Previous URL */
+ {"^B",N_("Prev URL"),{MC_PREV_HANDLE,1,{ctrl('B')}},KS_NONE},
+ /* TRANSLATORS: go to Next URL */
+ {"^F",N_("Next URL"),{MC_NEXT_HANDLE,1,{ctrl('F')}},KS_NONE},
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ DELETE_MENU,
+ UNDELETE_MENU,
+ SAVE_MENU,
+ EXPORT_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ MAIN_MENU,
+ QUIT_MENU,
+ PIPE_MENU,
+ BOUNCE_MENU,
+ HDRMODE_MENU,
+ WHEREIS_MENU,
+ {"%", N_("Print"), {MC_PRINTMSG,1,{'%'}}, KS_PRINT},
+ NULL_MENU,
+ REPLY_MENU,
+ FORWARD_MENU};
+INST_KEY_MENU(att_view_keymenu, att_view_keys);
+
+
+struct key view_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ /* TRANSLATORS: go to Message Index */
+ {"<",N_("MsgIndex"),{MC_INDEX,3,{'i','<',','}},KS_FLDRINDEX},
+ /* TRANSLATORS: View the Attachment */
+ {">",N_("ViewAttch"),{MC_VIEW_ATCH,3,{'v','>','.'}},KS_NONE},
+ PREVMSG_MENU,
+ NEXTMSG_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ DELETE_MENU,
+ UNDELETE_MENU,
+ REPLY_MENU,
+ FORWARD_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ MAIN_MENU,
+ QUIT_MENU,
+ LISTFLD_MENU,
+ GOTO_MENU,
+ COMPOSE_MENU,
+ WHEREIS_MENU,
+ PRYNTMSG_MENU,
+ TAKE_MENU,
+ SAVE_MENU,
+ EXPORT_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ /* TRANSLATORS: View the highlighted URL */
+ {"Ret","[" N_("View Hilite") "]",{MC_VIEW_HANDLE,3,
+ {ctrl('m'),ctrl('j'),KEY_RIGHT}},KS_NONE},
+ /* TRANSLATORS: Select the current item */
+ {":",N_("SelectCur"),{MC_SELCUR,1,{':'}},KS_SELECTCUR},
+ /* TRANSLATORS: go to previous URL */
+ {"^B",N_("Prev URL"),{MC_PREV_HANDLE,1,{ctrl('B')}},KS_NONE},
+ /* TRANSLATORS: go to next URL */
+ {"^F",N_("Next URL"),{MC_NEXT_HANDLE,1,{ctrl('F')}},KS_NONE},
+ JUMP_MENU,
+ TAB_MENU,
+ HDRMODE_MENU,
+ BOUNCE_MENU,
+ FLAG_MENU,
+ PIPE_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU,
+ RCOMPOSE_MENU,
+ {"A",N_("TogglePreferPlain"),{MC_TOGGLE,1,{'a'}},KS_NONE},
+#ifdef SMIME
+ {"^D","Decrypt", {MC_DECRYPT,1,{ctrl('d')},KS_NONE}},
+ {"^E","Security", {MC_SECURITY,1,{ctrl('e')},KS_NONE}},
+#else
+ NULL_MENU,
+ NULL_MENU,
+#endif
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU};
+INST_KEY_MENU(view_keymenu, view_keys);
+
+
+struct key simple_text_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ {"E",N_("Exit Viewer"),{MC_EXIT,1,{'e'}},KS_EXITMODE},
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+ FWDEMAIL_MENU,
+ {"S", N_("Save"), {MC_SAVETEXT,1,{'s'}}, KS_SAVE},
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(simple_text_keymenu, simple_text_keys);
+
+
+struct key oe_keys[] =
+ {{"^G",N_("Help"),{MC_NONE},KS_SCREENHELP},
+ {"^C",N_("Cancel"),{MC_NONE},KS_NONE},
+ {"^T","xxx",{MC_NONE},KS_NONE},
+ /* TRANSLATORS: The user is entering characters, for example, the
+ name of a folder. Accept means the user is done and wants to
+ accept what is currently displayed. */
+ {"Ret",N_("Accept"),{MC_NONE},KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU};
+INST_KEY_MENU(oe_keymenu, oe_keys);
+
+
+struct key choose_setup_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ {"E",N_("Exit Setup"),{MC_EXIT,2,{'e',ctrl('C')}},KS_EXITMODE},
+ {"P",N_("Printer"),{MC_PRINTER,1,{'p'}},KS_NONE},
+ /* TRANSLATORS: Change password */
+ {"N",N_("Newpassword"),{MC_PASSWD,1,{'n'}},KS_NONE},
+ /* TRANSLATORS: Configure Alpine */
+ {"C",N_("Config"),{MC_CONFIG,1,{'c'}},KS_NONE},
+ /* TRANSLATORS: Edit signature block */
+ {"S",N_("Signature"),{MC_SIG,1,{'s'}},KS_NONE},
+ /* TRANSLATORS: configure address books */
+ {"A",N_("AddressBooks"),{MC_ABOOKS,1,{'a'}},KS_NONE},
+ /* TRANSLATORS: configure collection lists */
+ {"L",N_("collectionList"),{MC_CLISTS,1,{'l'}},KS_NONE},
+ /* TRANSLATORS: configure rules, an alpine concept */
+ {"R",N_("Rules"),{MC_RULES,1,{'r'}},KS_NONE},
+ /* TRANSLATORS: configure directory servers */
+ {"D",N_("Directory"),{MC_DIRECTORY,1,{'d'}},KS_NONE},
+ /* TRANSLATORS: configure color */
+ {"K",N_("Kolor"),{MC_KOLOR,1,{'k'}},KS_NONE},
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ /* TRANSLATORS: remote configuration setup */
+ {"Z",N_("RemoteConfigSetup"),{MC_REMOTE,1,{'z'}},KS_NONE},
+ /* TRANSLATORS: configure S/MIME */
+ {"M",N_("S/Mime"),{MC_SECURITY,1,{'m'}},KS_NONE},
+ NULL_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(choose_setup_keymenu, choose_setup_keys);
+
+
+struct key main_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ /* TRANSLATORS: go to Previous Command in list */
+ {"P",N_("PrevCmd"),{MC_PREVITEM,3,{'p',ctrl('P'),KEY_UP}},KS_NONE},
+ {"N",N_("NextCmd"),{MC_NEXTITEM,3,{'n',ctrl('N'),KEY_DOWN}},KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ /* TRANSLATORS: show release notes */
+ {"R",N_("RelNotes"),{MC_RELNOTES,1,{'r'}},KS_NONE},
+ /* TRANSLATORS: lock keyboard */
+ {"K",N_("KBLock"),{MC_KBLOCK,1,{'k'}},KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ QUIT_MENU,
+ COMPOSE_MENU,
+ LISTFLD_MENU,
+ GOTO_MENU,
+ {"I",N_("Index"),{MC_INDEX,1,{'i'}},KS_FLDRINDEX},
+ /* TRANSLATORS: go to the Journal. The Journal shows past
+ messages that alpine has shown the user. */
+ {"J",N_("Journal"),{MC_JOURNAL,1,{'j'}},KS_REVIEW},
+ /* TRANSLATORS: go to the Setup screen */
+ {"S",N_("Setup"),{MC_SETUP,1,{'s'}},KS_NONE},
+ /* TRANSLATORS: go to the address book screen */
+ {"A",N_("AddrBook"),{MC_ADDRBOOK,1,{'a'}},KS_ADDRBOOK},
+ RCOMPOSE_MENU,
+ NULL_MENU};
+INST_KEY_MENU(main_keymenu, main_keys);
+
+
+struct key simple_file_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ {"Q",N_("Quit Viewer"),{MC_EXIT,1,{'q'}},KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+ FWDEMAIL_MENU,
+ {"S", N_("Save"), {MC_SAVETEXT,1,{'s'}}, KS_SAVE},
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(simple_file_keymenu, simple_file_keys);
+
+
+struct key nuov_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ {"E",NULL,{MC_EXIT,1,{'e',ctrl('M'),ctrl('J')}},KS_NONE},
+ /* TRANSLATORS: Alpine asks the user to be counted when they
+ first start using alpine. */
+ {"Ret","[" N_("Be Counted!") "]",{MC_VIEW_HANDLE,2,{ctrl('M'),ctrl('J')}},KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ PRYNTMSG_MENU,
+ NULL_MENU,
+ /* TRANSLATORS: show release notes */
+ {"R",N_("RelNotes"),{MC_RELNOTES,1,{'r'}},KS_NONE},
+ NULL_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(nuov_keymenu, nuov_keys);
+
+
+struct key modal_message_keys[] =
+ {NULL_MENU,
+ NULL_MENU,
+ {"Ret",N_("Finished"),{MC_EXIT,2,{ctrl('m'),ctrl('j')}},KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(modal_message_keymenu, modal_message_keys);
+
+
+struct key ta_keys_lm[] =
+ {HELP_MENU,
+ WHEREIS_MENU,
+ TA_EXIT_MENU,
+ /* TRANSLATORS: Take this address into the address book */
+ {"T", N_("Take"), {MC_TAKE,1,{'t'}}, KS_NONE},
+ TA_PREV_MENU,
+ TA_NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ {"X","[" N_("Set/Unset") "]", {MC_CHOICE,3,{'x',ctrl('M'),ctrl('J')}}, KS_NONE},
+ {"A", N_("SetAll"),{MC_SELALL,1,{'a'}},KS_NONE},
+ {"U",N_("UnSetAll"),{MC_UNSELALL,1,{'u'}},KS_NONE},
+ /* TRANSLATORS: The Take Address screen has a Single mode and a
+ List mode. This command causes us to go into Single mode. */
+ {"S",N_("SinglMode"),{MC_LISTMODE,1,{'s'}},KS_NONE}};
+INST_KEY_MENU(ta_keymenu_lm, ta_keys_lm);
+
+
+struct key ta_keys_sm[] =
+ {HELP_MENU,
+ WHEREIS_MENU,
+ TA_EXIT_MENU,
+ {"T","[" N_("Take") "]",{MC_TAKE,3,{'t',ctrl('M'),ctrl('J')}}, KS_NONE},
+ TA_PREV_MENU,
+ TA_NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ /* TRANSLATORS: The Take Address screen has a Single mode and a
+ List mode. This command causes us to go into List mode. */
+ {"L",N_("ListMode"),{MC_LISTMODE,1,{'l'}},KS_NONE}};
+INST_KEY_MENU(ta_keymenu_sm, ta_keys_sm);
+
+
+struct key pipe_cancel_keys[] =
+ {NULL_MENU,
+ {"^C",N_("Stop Waiting"),{MC_NONE},KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU};
+INST_KEY_MENU(pipe_cancel_keymenu, pipe_cancel_keys);
+
+
+struct key color_pattern_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ EXIT_SETUP_MENU,
+ /* TRANSLATORS: Change Value */
+ {"C", "[" N_("Change Val") "]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ NULL_MENU,
+ /* TRANSLATORS: Delete Value */
+ {"D", N_("Delete Val"), {MC_DELETE,1,{'d'}}, KS_NONE},
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(color_pattern_keymenu, color_pattern_keys);
+
+
+struct key hdr_color_checkbox_keys[] =
+ {HELP_MENU,
+ NULL_MENU,
+ EXIT_SETUP_MENU,
+ TOGGLEB_MENU,
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU,
+ PRYNTTXT_MENU,
+ WHEREIS_MENU};
+INST_KEY_MENU(hdr_color_checkbox_keymenu, hdr_color_checkbox_keys);
+
+
+struct key kw_color_checkbox_keys[] =
+ {HELP_MENU,
+ NULL_MENU,
+ EXIT_SETUP_MENU,
+ TOGGLEC_MENU,
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU,
+ PRYNTTXT_MENU,
+ WHEREIS_MENU};
+INST_KEY_MENU(kw_color_checkbox_keymenu, kw_color_checkbox_keys);
+
+
+struct key selectable_bold_checkbox_keys[] =
+ {HELP_MENU,
+ NULL_MENU,
+ EXIT_SETUP_MENU,
+ TOGGLED_MENU,
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU,
+ PRYNTTXT_MENU,
+ WHEREIS_MENU};
+INST_KEY_MENU(selectable_bold_checkbox_keymenu, selectable_bold_checkbox_keys);
+
+
+struct key flag_keys[] =
+ {HELP_MENU,
+ {"A", N_("Add KW"), {MC_ADD,1,{'a'}}, KS_NONE},
+ /* TRANSLATORS: Exit from the Flags screen */
+ {"E", N_("Exit Flags"), {MC_EXIT,1,{'e'}}, KS_EXITMODE},
+ TOGGLE_MENU,
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU,
+ PRYNTTXT_MENU,
+ WHEREIS_MENU};
+INST_KEY_MENU(flag_keymenu, flag_keys);
+
+
+struct key addr_select_keys[] =
+ {HELP_MENU,
+ {"E", N_("ExitSelect"), {MC_EXIT,1,{'e'}}, KS_EXITMODE},
+ NULL_MENU,
+ {"S", "[" N_("Select") "]", {MC_CHOICE,3,{'s',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU,
+ NULL_MENU,
+ WHEREIS_MENU};
+INST_KEY_MENU(addr_s_km, addr_select_keys);
+
+
+struct key addr_select_with_goback_keys[] =
+ {HELP_MENU,
+ NULL_MENU,
+ /* TRANSLATORS: go to address book list */
+ {"<", N_("AddressBkList"), {MC_ADDRBOOK,2,{'<',','}}, KS_NONE},
+ {"S", "[" N_("Select") "]", {MC_CHOICE,3,{'s',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU,
+ {"E", N_("ExitSelect"), {MC_EXIT,1,{'e'}}, KS_EXITMODE},
+ WHEREIS_MENU};
+INST_KEY_MENU(addr_s_km_with_goback, addr_select_with_goback_keys);
+
+static struct key addr_select_with_view_keys[] =
+ {HELP_MENU,
+ RCOMPOSE_MENU,
+ {"<", N_("AddressBkList"), {MC_ADDRBOOK,2,{'<',','}}, KS_NONE},
+ {">", "[" N_("View") "]",
+ {MC_VIEW_TEXT,5,{'v','>','.',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ /* TRANSLATORS: compose a message to the current address */
+ {"C", N_("ComposeTo"), {MC_COMPOSE,1,{'c'}}, KS_COMPOSER},
+ FWDEMAIL_MENU,
+ SAVE_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(addr_s_km_with_view, addr_select_with_view_keys);
+
+
+struct key addr_select_for_url_keys[] =
+ {HELP_MENU,
+ RCOMPOSE_MENU,
+ {"<", N_("Exit Viewer"), {MC_ADDRBOOK,3,{'<',',','e'}}, KS_NONE},
+ {">", "[" N_("View") "]",
+ {MC_VIEW_TEXT,5,{'v','>','.',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ {"C", N_("ComposeTo"), {MC_COMPOSE,1,{'c'}}, KS_COMPOSER},
+ FWDEMAIL_MENU,
+ SAVE_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(addr_s_km_for_url, addr_select_for_url_keys);
+
+
+struct key addr_select_exit_keys[] =
+ {NULL_MENU,
+ NULL_MENU,
+ {"E", "[" N_("Exit") "]", {MC_EXIT,3,{'e',ctrl('M'),ctrl('J')}},
+ KS_EXITMODE},
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU};
+INST_KEY_MENU(addr_s_km_exit, addr_select_exit_keys);
+
+
+struct key addr_select_goback_keys[] =
+ {NULL_MENU,
+ NULL_MENU,
+ {"E", "[" N_("Exit") "]", {MC_ADDRBOOK,3,{'e',ctrl('M'),ctrl('J')}},
+ KS_EXITMODE},
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU};
+INST_KEY_MENU(addr_s_km_goback, addr_select_goback_keys);
+
+
+struct key config_text_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ EXIT_SETUP_MENU,
+ {"C", "[" N_("Change Val") "]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ {"A", N_("Add Value"), {MC_ADD,1,{'a'}}, KS_NONE},
+ {"D", N_("Delete Val"), {MC_DELETE,1,{'d'}}, KS_NONE},
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(config_text_keymenu, config_text_keys);
+
+
+struct key config_text_to_charsets_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ EXIT_SETUP_MENU,
+ {"C", "[" N_("Change Val") "]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ {"A", N_("Add Value"), {MC_ADD,1,{'a'}}, KS_NONE},
+ {"D", N_("Delete Val"), {MC_DELETE,1,{'d'}}, KS_NONE},
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ /* TRANSLATORS: go to list of keywords */
+ {"T", N_("ToCharsets"), {MC_CHOICE, 2, {'t', ctrl('T')}}, KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(config_text_to_charsets_keymenu, config_text_to_charsets_keys);
+
+
+struct key direct_config_keys[] =
+ {HELP_MENU,
+ NULL_MENU,
+ {"E", N_("Exit Setup"), {MC_EXIT,1,{'e'}}, KS_EXITMODE},
+ {"C", "[" N_("Change") "]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
+ /* TRANSLATORS: go to previous LDAP directory server in the list */
+ {"P", N_("PrevDir"), {MC_PREVITEM, 1, {'p'}}, KS_NONE},
+ {"N", N_("NextDir"), {MC_NEXTITEM, 2, {'n', TAB}}, KS_NONE},
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ /* TRANSLATORS: add a directory server to configuration */
+ {"A", N_("Add Dir"), {MC_ADD,1,{'a'}}, KS_NONE},
+ /* TRANSLATORS: delete a directory */
+ {"D", N_("Del Dir"), {MC_DELETE,1,{'d'}}, KS_NONE},
+ {"$", N_("Shuffle"), {MC_SHUFFLE,1,{'$'}}, KS_NONE},
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(dir_conf_km, direct_config_keys);
+
+
+struct key sel_from_list_keys[] =
+ {HELP_MENU,
+ NULL_MENU,
+ {"E", N_("Exit"), {MC_EXIT,1,{'e'}}, KS_EXITMODE},
+ {"S", "[" N_("Select") "]", {MC_SELECT,3,{'s',ctrl('J'),ctrl('M')}}, KS_NONE},
+ {"P", N_("Prev"), {MC_PREVITEM, 1, {'p'}}, KS_NONE},
+ {"N", N_("Next"), {MC_NEXTITEM, 2, {'n', TAB}}, KS_NONE},
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU,
+ PRYNTTXT_MENU,
+ WHEREIS_MENU};
+INST_KEY_MENU(sel_from_list, sel_from_list_keys);
+
+
+struct key sel_from_list_keys_ctrlc[] =
+ {HELP_MENU,
+ NULL_MENU,
+ {"^C", N_("exit"), {MC_EXIT,1,{ctrl('C')}}, KS_EXITMODE},
+ {"S", "[" N_("Select") "]", {MC_SELECT,3,{'s',ctrl('J'),ctrl('M')}}, KS_NONE},
+ {"P", N_("Prev"), {MC_PREVITEM, 1, {'p'}}, KS_NONE},
+ {"N", N_("Next"), {MC_NEXTITEM, 2, {'n', TAB}}, KS_NONE},
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU,
+ PRYNTTXT_MENU,
+ WHEREIS_MENU};
+INST_KEY_MENU(sel_from_list_ctrlc, sel_from_list_keys_ctrlc);
+
+
+struct key sel_from_list_keys_sm[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ {"E", N_("Exit"), {MC_EXIT,1,{'e'}}, KS_EXITMODE},
+ {"S", "[" N_("Select") "]", {MC_SELECT,3,{'s',ctrl('J'),ctrl('M')}}, KS_NONE},
+ {"P", N_("Prev"), {MC_PREVITEM, 1, {'p'}}, KS_NONE},
+ {"N", N_("Next"), {MC_NEXTITEM, 2, {'n', TAB}}, KS_NONE},
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ NULL_MENU,
+ {"L",N_("ListMode"),{MC_LISTMODE,1,{'l'}},KS_NONE},
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(sel_from_list_sm, sel_from_list_keys_sm);
+
+
+struct key sel_from_list_keys_sm_ctrlc[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ {"^C", N_("exit"), {MC_EXIT,1,{ctrl('C')}}, KS_EXITMODE},
+ {"S", "[" N_("Select") "]", {MC_SELECT,3,{'s',ctrl('J'),ctrl('M')}}, KS_NONE},
+ {"P", N_("Prev"), {MC_PREVITEM, 1, {'p'}}, KS_NONE},
+ {"N", N_("Next"), {MC_NEXTITEM, 2, {'n', TAB}}, KS_NONE},
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ NULL_MENU,
+ {"L",N_("ListMode"),{MC_LISTMODE,1,{'l'}},KS_NONE},
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(sel_from_list_sm_ctrlc, sel_from_list_keys_sm_ctrlc);
+
+
+struct key sel_from_list_keys_lm[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ {"E", N_("Exit"), {MC_EXIT,1,{'e'}}, KS_EXITMODE},
+ {"S", "[" N_("Select") "]", {MC_SELECT,3,{'s',ctrl('J'),ctrl('M')}}, KS_NONE},
+ {"P", N_("Prev"), {MC_PREVITEM, 1, {'p'}}, KS_NONE},
+ {"N", N_("Next"), {MC_NEXTITEM, 2, {'n', TAB}}, KS_NONE},
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ {"X", N_("Set/Unset"), {MC_TOGGLE,1,{'x'}}, KS_NONE},
+ {"1",N_("SinglMode"),{MC_LISTMODE,1,{'1'}},KS_NONE},
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(sel_from_list_lm, sel_from_list_keys_lm);
+
+
+struct key sel_from_list_keys_lm_ctrlc[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ {"^C", N_("exit"), {MC_EXIT,1,{ctrl('C')}}, KS_EXITMODE},
+ {"S", "[" N_("Select") "]", {MC_SELECT,3,{'s',ctrl('J'),ctrl('M')}}, KS_NONE},
+ {"P", N_("Prev"), {MC_PREVITEM, 1, {'p'}}, KS_NONE},
+ {"N", N_("Next"), {MC_NEXTITEM, 2, {'n', TAB}}, KS_NONE},
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ {"X", N_("Set/Unset"), {MC_TOGGLE,1,{'x'}}, KS_NONE},
+ {"1",N_("SinglMode"),{MC_LISTMODE,1,{'1'}},KS_NONE},
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(sel_from_list_lm_ctrlc, sel_from_list_keys_lm_ctrlc);
+
+
+struct key sel_from_list_keys_olm[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ {"E", N_("Exit"), {MC_EXIT,1,{'e'}}, KS_EXITMODE},
+ {"S", "[" N_("Select") "]", {MC_SELECT,3,{'s',ctrl('J'),ctrl('M')}}, KS_NONE},
+ {"P", N_("Prev"), {MC_PREVITEM, 1, {'p'}}, KS_NONE},
+ {"N", N_("Next"), {MC_NEXTITEM, 2, {'n', TAB}}, KS_NONE},
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ {"X", N_("Set/Unset"), {MC_TOGGLE,1,{'x'}}, KS_NONE},
+ NULL_MENU,
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(sel_from_list_olm, sel_from_list_keys_olm);
+
+
+struct key sel_from_list_keys_olm_ctrlc[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ {"^C", N_("exit"), {MC_EXIT,1,{ctrl('C')}}, KS_EXITMODE},
+ {"S", "[" N_("Select") "]", {MC_SELECT,3,{'s',ctrl('J'),ctrl('M')}}, KS_NONE},
+ {"P", N_("Prev"), {MC_PREVITEM, 1, {'p'}}, KS_NONE},
+ {"N", N_("Next"), {MC_NEXTITEM, 2, {'n', TAB}}, KS_NONE},
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ {"X", N_("Set/Unset"), {MC_TOGGLE,1,{'x'}}, KS_NONE},
+ NULL_MENU,
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(sel_from_list_olm_ctrlc, sel_from_list_keys_olm_ctrlc);
+
+
+#ifndef DOS
+
+struct key printer_edit_keys[] =
+ {HELP_MENU,
+ PRYNTTXT_MENU,
+ EXIT_SETUP_MENU,
+ {"S", "[" N_("Select") "]", {MC_CHOICE,3,{'s',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ /* TRANSLATORS: add a printer to configuration */
+ {"A", N_("Add Printer"), {MC_ADD,1,{'a'}}, KS_NONE},
+ /* TRANSLATORS: delete a printer from configuration */
+ {"D", N_("DeletePrint"), {MC_DELETE,1,{'d'}}, KS_NONE},
+ {"C", N_("Change"), {MC_EDIT,1,{'c'}}, KS_NONE},
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(printer_edit_keymenu, printer_edit_keys);
+
+
+struct key printer_select_keys[] =
+ {HELP_MENU,
+ PRYNTTXT_MENU,
+ EXIT_SETUP_MENU,
+ {"S", "[" N_("Select") "]", {MC_CHOICE,3,{'s',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU,
+ NULL_MENU,
+ WHEREIS_MENU};
+INST_KEY_MENU(printer_select_keymenu, printer_select_keys);
+
+#endif /* !DOS */
+
+
+struct key role_select_keys[] =
+ {HELP_MENU,
+ NULL_MENU,
+ {"E", N_("Exit"), {MC_EXIT,1,{'e'}}, KS_EXITMODE},
+ NULL_MENU,
+ /* TRANSLATORS: go to previous Role in list */
+ {"P", N_("PrevRole"), {MC_PREVITEM, 1, {'p'}}, KS_NONE},
+ {"N", N_("NextRole"), {MC_NEXTITEM, 2, {'n', TAB}}, KS_NONE},
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU,
+ {"D", "", {MC_TOGGLE, 1, {'d'}}, KS_NONE},
+ WHEREIS_MENU};
+INST_KEY_MENU(role_select_km, role_select_keys);
+
+
+struct key role_config_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ {"E", N_("Exit Setup"), {MC_EXIT,1,{'e'}}, KS_EXITMODE},
+ {"C", "[" N_("Change") "]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
+ /* TRANSLATORS: go to previous Rule in list */
+ {"P", N_("PrevRule"), {MC_PREVITEM, 1, {'p'}}, KS_NONE},
+ {"N", N_("NextRule"), {MC_NEXTITEM, 2, {'n', TAB}}, KS_NONE},
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ {"A", N_("Add"), {MC_ADD,1,{'a'}}, KS_NONE},
+ {"D", N_("Delete"), {MC_DELETE,1,{'d'}}, KS_NONE},
+ {"$", N_("Shuffle"), {MC_SHUFFLE,1,{'$'}}, KS_NONE},
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ /* TRANSLATORS: Include a File from filesystem */
+ {"I", N_("IncludeFile"), {MC_ADDFILE,1,{'i'}}, KS_NONE},
+ {"X", N_("eXcludeFile"), {MC_DELFILE,1,{'x'}}, KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ {"R", N_("Replicate"), {MC_COPY,1,{'r'}}, KS_NONE},
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(role_conf_km, role_config_keys);
+
+
+struct key config_text_wshuf_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ EXIT_SETUP_MENU,
+ {"C", "[" N_("Change Val") "]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ {"A", N_("Add Value"), {MC_ADD,1,{'a'}}, KS_NONE},
+ {"D", N_("Delete Val"), {MC_DELETE,1,{'d'}}, KS_NONE},
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ {"$", N_("Shuffle"), {MC_SHUFFLE,1,{'$'}}, KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(config_text_wshuf_keymenu, config_text_wshuf_keys);
+
+
+struct key config_text_wshufandfldr_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ EXIT_SETUP_MENU,
+ {"C", "[" N_("Change Val") "]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ {"A", N_("Add Value"), {MC_ADD,1,{'a'}}, KS_NONE},
+ {"D", N_("Delete Val"), {MC_DELETE,1,{'d'}}, KS_NONE},
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ {"$", N_("Shuffle"), {MC_SHUFFLE,1,{'$'}}, KS_NONE},
+ {"T", N_("ToFldrs"), {MC_CHOICE,2,{'t', ctrl('T')}}, KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(config_text_wshufandfldr_keymenu, config_text_wshufandfldr_keys);
+
+
+struct key config_role_file_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ EXIT_SETUP_MENU,
+ {"C", "[" N_("Change Val") "]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ {"A", N_("Add Value"), {MC_ADD,1,{'a'}}, KS_NONE},
+ {"D", N_("Delete Val"), {MC_DELETE,1,{'d'}}, KS_NONE},
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ /* TRANSLATORS: go to list of Files */
+ {"T", N_("ToFiles"), {MC_CHOICE, 2, {'t', ctrl('T')}}, KS_NONE},
+ /* TRANSLATORS: edit a file */
+ {"F", N_("editFile"), {MC_EDITFILE, 1, {'f'}}, KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(config_role_file_keymenu, config_role_file_keys);
+
+
+struct key config_role_file_res_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ EXIT_SETUP_MENU,
+ {"C", "[" N_("Change Val") "]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ {"A", N_("Add Value"), {MC_ADD,1,{'a'}}, KS_NONE},
+ {"D", N_("Delete Val"), {MC_DELETE,1,{'d'}}, KS_NONE},
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ {"T", N_("ToFiles"), {MC_CHOICE, 2, {'t', ctrl('T')}}, KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(config_role_file_res_keymenu, config_role_file_res_keys);
+
+
+struct key config_role_keyword_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ EXIT_SETUP_MENU,
+ {"C", "[" N_("Change Val") "]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ {"A", N_("Add Value"), {MC_ADD,1,{'a'}}, KS_NONE},
+ {"D", N_("Delete Val"), {MC_DELETE,1,{'d'}}, KS_NONE},
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ /* TRANSLATORS: go to list of keywords */
+ {"T", N_("ToKeywords"), {MC_CHOICE, 2, {'t', ctrl('T')}}, KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(config_role_keyword_keymenu, config_role_keyword_keys);
+
+
+struct key config_role_keyword_keys_not[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ EXIT_SETUP_MENU,
+ {"C", "[" N_("Change Val") "]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ {"A", N_("Add Value"), {MC_ADD,1,{'a'}}, KS_NONE},
+ {"D", N_("Delete Val"), {MC_DELETE,1,{'d'}}, KS_NONE},
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ {"T", N_("ToKeywords"), {MC_CHOICE, 2, {'t', ctrl('T')}}, KS_NONE},
+ NULL_MENU,
+ /* TRANSLATORS: toggle between NOT and not NOT, turn NOT on or off */
+ {"!", N_("toggle NOT"), {MC_NOT,1,{'!'}}, KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(config_role_keyword_keymenu_not, config_role_keyword_keys_not);
+
+
+struct key config_role_charset_keys_not[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ EXIT_SETUP_MENU,
+ {"C", "[" N_("Change Val") "]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ {"A", N_("Add Value"), {MC_ADD,1,{'a'}}, KS_NONE},
+ {"D", N_("Delete Val"), {MC_DELETE,1,{'d'}}, KS_NONE},
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ /* TRANSLATORS: go to list of character sets */
+ {"T", N_("ToCharSets"), {MC_CHOICE, 2, {'t', ctrl('T')}}, KS_NONE},
+ NULL_MENU,
+ {"!", N_("toggle NOT"), {MC_NOT,1,{'!'}}, KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(config_role_charset_keymenu_not, config_role_charset_keys_not);
+
+
+struct key config_role_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ EXIT_SETUP_MENU,
+ {"C", "[" N_("Change Val") "]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ {"A", N_("Add Value"), {MC_ADD,1,{'a'}}, KS_NONE},
+ {"D", N_("Delete Val"), {MC_DELETE,1,{'d'}}, KS_NONE},
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(config_role_keymenu, config_role_keys);
+
+
+struct key config_role_keys_not[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ EXIT_SETUP_MENU,
+ {"C", "[" N_("Change Val") "]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ {"A", N_("Add Value"), {MC_ADD,1,{'a'}}, KS_NONE},
+ {"D", N_("Delete Val"), {MC_DELETE,1,{'d'}}, KS_NONE},
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ /* TRANSLATORS: add extra headers to list */
+ {"X", N_("eXtraHdr"), {MC_ADDHDR, 1, {'x'}}, KS_NONE},
+ {"!", N_("toggle NOT"), {MC_NOT,1,{'!'}}, KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(config_role_keymenu_not, config_role_keys_not);
+
+
+struct key config_role_keys_extra[] =
+ {HELP_MENU,
+ NULL_MENU,
+ EXIT_SETUP_MENU,
+ {"X", "[" N_("eXtraHdr") "]", {MC_ADDHDR, 3,{'x',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU,
+ PRYNTTXT_MENU,
+ WHEREIS_MENU};
+INST_KEY_MENU(config_role_keymenu_extra, config_role_keys_extra);
+
+
+struct key config_role_addr_pat_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ EXIT_SETUP_MENU,
+ {"C", "[" N_("Change Val") "]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ {"A", N_("Add Value"), {MC_ADD,1,{'a'}}, KS_NONE},
+ {"D", N_("Delete Val"), {MC_DELETE,1,{'d'}}, KS_NONE},
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ /* TRANSLATORS: go to address book to get address */
+ {"T", N_("ToAddrBk"), {MC_CHOICEB, 2, {'t', ctrl('T')}}, KS_NONE},
+ {"X", N_("eXtraHdr"), {MC_ADDHDR, 1, {'x'}}, KS_NONE},
+ {"!", N_("toggle NOT"), {MC_NOT,1,{'!'}}, KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(config_role_addr_pat_keymenu, config_role_addr_pat_keys);
+
+
+struct key config_role_xtrahdr_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ EXIT_SETUP_MENU,
+ {"C", "[" N_("Change Val") "]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ {"A", N_("Add Value"), {MC_ADD,1,{'a'}}, KS_NONE},
+ {"D", N_("Delete Val"), {MC_DELETE,1,{'d'}}, KS_NONE},
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ {"X", N_("eXtraHdr"), {MC_ADDHDR, 1, {'x'}}, KS_NONE},
+ {"!", N_("toggle NOT"), {MC_NOT,1,{'!'}}, KS_NONE},
+ NULL_MENU,
+ /* TRANSLATORS: remove a header we previously added */
+ {"R", N_("RemoveHdr"), {MC_DELHDR, 1, {'r'}}, KS_NONE},
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(config_role_xtrahdr_keymenu, config_role_xtrahdr_keys);
+
+
+struct key config_role_addr_act_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ EXIT_SETUP_MENU,
+ {"C", "[" N_("Change Val") "]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ {"A", N_("Add Value"), {MC_ADD,1,{'a'}}, KS_NONE},
+ {"D", N_("Delete Val"), {MC_DELETE,1,{'d'}}, KS_NONE},
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ {"T", N_("ToAddrBk"), {MC_CHOICEC, 2, {'t', ctrl('T')}}, KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(config_role_addr_act_keymenu, config_role_addr_act_keys);
+
+
+struct key config_role_patfolder_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ EXIT_SETUP_MENU,
+ {"C", "[" N_("Change Val") "]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ {"A", N_("Add Value"), {MC_ADD,1,{'a'}}, KS_NONE},
+ {"D", N_("Delete Val"), {MC_DELETE,1,{'d'}}, KS_NONE},
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ /* TRANSLATORS: go to list of folders */
+ {"T", N_("ToFldrs"), {MC_CHOICED, 2, {'t', ctrl('T')}}, KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(config_role_patfolder_keymenu, config_role_patfolder_keys);
+
+
+struct key config_role_actionfolder_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ EXIT_SETUP_MENU,
+ {"C", "[" N_("Change Val") "]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ {"A", N_("Add Value"), {MC_ADD,1,{'a'}}, KS_NONE},
+ {"D", N_("Delete Val"), {MC_DELETE,1,{'d'}}, KS_NONE},
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ {"T", N_("ToFldrs"), {MC_CHOICEE, 2, {'t', ctrl('T')}}, KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(config_role_actionfolder_keymenu, config_role_actionfolder_keys);
+
+
+struct key config_role_inick_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ EXIT_SETUP_MENU,
+ {"C", "[" N_("Change Val") "]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ {"A", N_("Add Value"), {MC_ADD,1,{'a'}}, KS_NONE},
+ {"D", N_("Delete Val"), {MC_DELETE,1,{'d'}}, KS_NONE},
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ /* TRANSLATORS: go to list of nicknames */
+ {"T", N_("ToNicks"), {MC_CHOICE, 2, {'t', ctrl('T')}}, KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(config_role_inick_keymenu, config_role_inick_keys);
+
+
+struct key config_role_afrom_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ EXIT_SETUP_MENU,
+ {"C", "[" N_("Change Val") "]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ {"A", N_("Add Value"), {MC_ADD,1,{'a'}}, KS_NONE},
+ {"D", N_("Delete Val"), {MC_DELETE,1,{'d'}}, KS_NONE},
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ /* TRANSLATORS: go to list of address books */
+ {"T", N_("ToAbookList"), {MC_CHOICE, 2, {'t', ctrl('T')}}, KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(config_role_afrom_keymenu, config_role_afrom_keys);
+
+
+struct key config_checkbox_keys[] =
+ {HELP_MENU,
+ NULL_MENU,
+ EXIT_SETUP_MENU,
+ TOGGLE_MENU,
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU,
+ PRYNTTXT_MENU,
+ WHEREIS_MENU};
+INST_KEY_MENU(config_checkbox_keymenu, config_checkbox_keys);
+
+
+struct key config_radiobutton_keys[] =
+ {HELP_MENU,
+ NULL_MENU,
+ EXIT_SETUP_MENU,
+ {"*", "[" N_("Select") "]", {MC_CHOICE,3,{'*',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU,
+ PRYNTTXT_MENU,
+ WHEREIS_MENU};
+INST_KEY_MENU(config_radiobutton_keymenu, config_radiobutton_keys);
+
+
+struct key config_yesno_keys[] =
+ {HELP_MENU,
+ NULL_MENU,
+ EXIT_SETUP_MENU,
+ {"C", "[" N_("Change") "]", {MC_TOGGLE,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU,
+ PRYNTTXT_MENU,
+ WHEREIS_MENU};
+INST_KEY_MENU(config_yesno_keymenu, config_yesno_keys);
+
+
+struct key color_changing_keys[] =
+ {HELP_MENU,
+ NULL_MENU,
+ {"E", N_("To Colors"), {MC_EXIT,1,{'e'}}, KS_EXITMODE},
+ {"*", "[" N_("Select") "]", {MC_CHOICE,3,{'*',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU,
+ PRYNTTXT_MENU,
+ WHEREIS_MENU};
+INST_KEY_MENU(color_changing_keymenu, color_changing_keys);
+
+
+struct key custom_color_changing_keys[] =
+ {HELP_MENU,
+ NULL_MENU,
+ /* TRANSLATORS: go to color configuration screen */
+ {"E", N_("To Colors"), {MC_EXIT,1,{'e'}}, KS_EXITMODE},
+ {"*", "[" N_("Select") "]", {MC_CHOICEB,3,{'*',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU,
+ PRYNTTXT_MENU,
+ WHEREIS_MENU};
+INST_KEY_MENU(custom_color_changing_keymenu, custom_color_changing_keys);
+
+
+struct key kw_color_changing_keys[] =
+ {HELP_MENU,
+ NULL_MENU,
+ {"E", N_("To Colors"), {MC_EXIT,1,{'e'}}, KS_EXITMODE},
+ {"*", "[" N_("Select") "]", {MC_CHOICEC,3,{'*',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU,
+ PRYNTTXT_MENU,
+ WHEREIS_MENU};
+INST_KEY_MENU(kw_color_changing_keymenu, kw_color_changing_keys);
+
+
+#ifdef _WINDOWS
+
+struct key color_rgb_changing_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ {"E", N_("To Colors"), {MC_EXIT,1,{'e'}}, KS_EXITMODE},
+ {"*", "[" N_("Select") "]", {MC_CHOICE,3,{'*',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ {"C", N_("Customize"), {MC_RGB1,1,{'c'}},KS_NONE},
+ NULL_MENU,
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(color_rgb_keymenu, color_rgb_changing_keys);
+
+
+struct key custom_rgb_changing_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ {"E", N_("To Colors"), {MC_EXIT,1,{'e'}}, KS_EXITMODE},
+ {"*", "[" N_("Select") "]", {MC_CHOICEB,3,{'*',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ {"C", N_("Customize"), {MC_RGB2,1,{'c'}},KS_NONE},
+ NULL_MENU,
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(custom_rgb_keymenu, custom_rgb_changing_keys);
+
+
+struct key kw_rgb_changing_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ {"E", N_("To Colors"), {MC_EXIT,1,{'e'}}, KS_EXITMODE},
+ {"*", "[" N_("Select") "]", {MC_CHOICEC,3,{'*',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ {"C", N_("Customize"), {MC_RGB3,1,{'c'}},KS_NONE},
+ NULL_MENU,
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(kw_rgb_keymenu, kw_rgb_changing_keys);
+
+#endif
+
+
+struct key color_setting_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ EXIT_SETUP_MENU,
+ {"C", "[" N_("Change") "]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ {"A", N_("AddHeader"), {MC_ADD,1,{'a'}}, KS_NONE},
+ /* TRANSLATORS: restore defaults */
+ {"R", N_("RestoreDefs"), {MC_DEFAULT,1,{'r'}}, KS_NONE},
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(color_setting_keymenu, color_setting_keys);
+
+
+struct key custom_color_setting_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ EXIT_SETUP_MENU,
+ {"C", "[" N_("Change") "]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ {"A", N_("AddHeader"), {MC_ADD,1,{'a'}}, KS_NONE},
+ {"R", N_("RestoreDefs"), {MC_DEFAULT,1,{'r'}}, KS_NONE},
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ {"D", N_("DeleteHdr"), {MC_DELETE,1,{'d'}}, KS_NONE},
+ /* TRANSLATORS: shuffle headers (change the order of headers) */
+ {"$", N_("ShuffleHdr"), {MC_SHUFFLE,1,{'$'}}, KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(custom_color_setting_keymenu, custom_color_setting_keys);
+
+
+struct key role_color_setting_keys[] =
+ {HELP_MENU,
+ NULL_MENU,
+ EXIT_SETUP_MENU,
+ {"*", "[" N_("Select") "]", {MC_CHOICE,3,{'*',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU,
+ PRYNTTXT_MENU,
+ WHEREIS_MENU};
+INST_KEY_MENU(role_color_setting_keymenu, role_color_setting_keys);
+
+
+struct key kw_color_setting_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ EXIT_SETUP_MENU,
+ {"C", "[" N_("Change") "]", {MC_EDIT,3,{'c',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ {"A", N_("AddHeader"), {MC_ADD,1,{'a'}}, KS_NONE},
+ {"R", N_("RestoreDefs"), {MC_DEFAULT,1,{'r'}}, KS_NONE},
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(kw_color_setting_keymenu, kw_color_setting_keys);
+
+
+struct key take_export_keys_sm[] =
+ {HELP_MENU,
+ WHEREIS_MENU,
+ /* TRANSLATORS: exit the Take Address screen */
+ {"<",N_("ExitTake"), {MC_EXIT,4,{'e',ctrl('C'),'<',','}}, KS_EXITMODE},
+ {"T","[" N_("Take") "]",{MC_TAKE,3,{'t',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ {"L",N_("ListMode"),{MC_LISTMODE,1,{'l'}},KS_NONE}};
+INST_KEY_MENU(take_export_keymenu_sm, take_export_keys_sm);
+
+
+struct key take_export_keys_lm[] =
+ {HELP_MENU,
+ WHEREIS_MENU,
+ {"<",N_("ExitTake"), {MC_EXIT,4,{'e',ctrl('C'),'<',','}}, KS_EXITMODE},
+ {"T",N_("Take"), {MC_TAKE,1,{'t'}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ {"X","[" N_("Set/Unset") "]", {MC_CHOICE,3,{'x',ctrl('M'),ctrl('J')}}, KS_NONE},
+ {"A", N_("SetAll"),{MC_SELALL,1,{'a'}},KS_NONE},
+ {"U",N_("UnSetAll"),{MC_UNSELALL,1,{'u'}},KS_NONE},
+ {"S",N_("SinglMode"),{MC_LISTMODE,1,{'s'}},KS_NONE}};
+INST_KEY_MENU(take_export_keymenu_lm, take_export_keys_lm);
+
+
+struct key smime_info_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ {"<","Back",{MC_VIEW_TEXT,2,{'<',','}},KS_EXITMODE},
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+
+ HELP_MENU,
+ OTHER_MENU,
+ MAIN_MENU,
+ QUIT_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ INDEX_MENU,
+ NULL_MENU,
+ NULL_MENU};
+INST_KEY_MENU(smime_info_keymenu, smime_info_keys);
+
+
+struct key config_smime_helper_keys[] =
+ {HELP_MENU,
+ WHEREIS_MENU,
+ EXIT_SETUP_MENU,
+ {"T","[" N_("Transfer") "]", {MC_CHOICE,3,{'t',ctrl('M'),ctrl('J')}}, KS_NONE},
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(config_smime_helper_keymenu, config_smime_helper_keys);
+
+
+/*
+ * Internal prototypes
+ */
+void output_keymenu(struct key_menu *, bitmap_t, int, int);
+void format_keymenu(struct key_menu *, bitmap_t, int);
+void menu_clear_cmd_binding(struct key_menu *, int);
+#ifdef MOUSE
+void print_inverted_label(int, MENUITEM *);
+#endif
+
+
+/* Saved key menu drawing state */
+static struct {
+ struct key_menu *km;
+ int row,
+ column,
+ blanked;
+ bitmap_t bitmap;
+} km_state;
+
+
+/*
+ * Longest label that can be displayed in keymenu
+ */
+#define MAX_LABEL 40
+#define MAX_KEYNAME 3
+static struct key last_time_buf[12];
+static int keymenu_is_dirty = 1;
+
+void
+mark_keymenu_dirty(void)
+{
+ keymenu_is_dirty = 1;
+}
+
+
+/*
+ * Write an already formatted key_menu to the screen
+ *
+ * Args: km -- key_menu structure
+ * bm -- bitmap, 0's mean don't draw this key
+ * row -- the row on the screen to begin on, negative values
+ * are counted from the bottom of the screen up
+ * column -- column on the screen to begin on
+ *
+ * The bits in the bitmap are used from least significant to most significant,
+ * not left to right. So, if you write out the bitmap in the normal way, for
+ * example,
+ * bm[0] = 0x5, bm[1] = 0x8, bm[2] = 0x21, bm[3] = bm[4] = bm[5] = 0
+ * 0000 0101 0000 1000 0010 0001 ...
+ * means that menu item 0 (first row, first column) is set, item 1 (2nd row,
+ * first column) is not set, item 2 is set, items 3-10 are not set, item 11
+ * (2nd row, 6th and last column) is set. In the second menu (the second set
+ * of 12 bits) items 0-3 are unset, 4 is set, 5-8 unset, 9 set, 10-11 unset.
+ * That uses up bm[0] - bm[2].
+ * Just to make sure, here it is drawn out for the first set of 12 items in
+ * the first keymenu (0-11)
+ * bm[0] x x x x x x x x bm[1] x x x x x x x x
+ * 7 6 5 4 3 2 1 0 1110 9 8
+ */
+void
+output_keymenu(struct key_menu *km, unsigned char *bm, int row, int column)
+{
+#ifdef __CYGWIN__
+ extern char term_name[];
+#endif
+ register struct key *k;
+ struct key *last_time;
+ int i, j,
+ ufk, /* using function keys */
+ real_row,
+ max_column, /* number of columns on screen */
+ off; /* offset into keymap */
+ struct variable *vars = ps_global->vars;
+ COLOR_PAIR *lastc=NULL, *label_color=NULL, *name_color=NULL;
+#ifdef MOUSE
+ /* 6's are for UTF-8 */
+ char keystr[6*MAX_KEYNAME + 6*MAX_LABEL + 2];
+#endif
+
+ off = km->which * 12;
+ max_column = ps_global->ttyo->screen_cols;
+
+ if((ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)) < 0
+ || max_column <= 0){
+ keymenu_is_dirty = 1;
+ return;
+ }
+
+
+ real_row = row > 0 ? row : ps_global->ttyo->screen_rows + row;
+
+ if(pico_usingcolor()){
+ lastc = pico_get_cur_color();
+ if(lastc && VAR_KEYLABEL_FORE_COLOR && VAR_KEYLABEL_BACK_COLOR &&
+ pico_is_good_color(VAR_KEYLABEL_FORE_COLOR) &&
+ pico_is_good_color(VAR_KEYLABEL_BACK_COLOR)){
+ label_color = new_color_pair(VAR_KEYLABEL_FORE_COLOR,
+ VAR_KEYLABEL_BACK_COLOR);
+ if(label_color)
+ (void)pico_set_colorp(label_color, PSC_NONE);
+ }
+
+ if(label_color && VAR_KEYNAME_FORE_COLOR && VAR_KEYNAME_BACK_COLOR &&
+ pico_is_good_color(VAR_KEYNAME_FORE_COLOR) &&
+ pico_is_good_color(VAR_KEYNAME_BACK_COLOR)){
+ name_color = new_color_pair(VAR_KEYNAME_FORE_COLOR,
+ VAR_KEYNAME_BACK_COLOR);
+ }
+ }
+
+ if(keymenu_is_dirty){
+ ClearLines(real_row, real_row+1);
+ keymenu_is_dirty = 0;
+ /* first time through, set up storage */
+ if(!last_time_buf[0].name){
+ for(i = 0; i < 12; i++){
+ last_time = &last_time_buf[i];
+ last_time->name = (char *) fs_get(6*MAX_KEYNAME + 1);
+ last_time->label = (char *) fs_get(6*MAX_LABEL + 1);
+ }
+ }
+
+ for(i = 0; i < 12; i++)
+ last_time_buf[i].column = -1;
+ }
+
+ for(i = 0; i < 12; i++){
+ int e;
+
+ e = off + i;
+ dprint((9, "%2d %-7.7s %-10.10s %d\n", i,
+ km == NULL ? "(no km)"
+ : km->keys[e].name == NULL ? "(null)"
+ : km->keys[e].name,
+ km == NULL ? "(no km)"
+ : km->keys[e].label == NULL ? "(null)"
+ : km->keys[e].label, km ? km->keys[e].column : 0));
+#ifdef MOUSE
+ register_key(i, NO_OP_COMMAND, "", NULL, 0, 0, 0, NULL, NULL);
+#endif
+ }
+
+ ufk = F_ON(F_USE_FK, ps_global);
+ dprint((9, "row: %d, real_row: %d, column: %d\n", row,
+ real_row, column));
+
+ for(i = 0; i < 2; i++){
+ int c, el, empty, fkey, last_in_row, fix_start;
+ short next_col;
+ char temp[6*MAX_SCREEN_COLS+1];
+ char temp2[6*MAX_SCREEN_COLS+1];
+ char this_label[6*MAX_LABEL+1];
+
+ j = 6*i - 1;
+#ifndef __CYGWIN__
+ if(i == 1 && !label_color)
+#else
+ if(i == 1 && (!label_color || !struncmp(term_name,"cygwin", 6)))
+#endif
+ max_column--; /* Some terminals scroll if you write in the
+ lower right hand corner. If user has a
+ label_color set we'll take our chances.
+ Otherwise, we'd get one cell of Normal. */
+
+ /*
+ * k is the key struct we're working on
+ * c is the column number
+ * el is an index into the whole keys array
+ * Last_time_buf is ordered strangely. It goes row by row instead
+ * of down each column like km does. J is an index into it.
+ */
+ for(c = 0, el = off+i, k = &km->keys[el];
+ k < &km->keys[off+12] && c < max_column;
+ k += 2, el += 2){
+
+ if(k->column > max_column)
+ break;
+
+ j++;
+ if(ufk)
+ fkey = 1 + k - &km->keys[off];
+
+ empty = (!bitnset(el,bm) || !(k->name && *k->name));
+ last_time = &last_time_buf[j];
+ if(k+2 < &km->keys[off+12]){
+ last_in_row = 0;
+ next_col = last_time_buf[j+1].column;
+ fix_start = (k == &km->keys[off] ||
+ k == &km->keys[off+1]) ? k->column : 0;
+ }
+ else{
+ last_in_row = 1;
+ fix_start = 0;
+ }
+
+ /*
+ * Make sure there is a space between this label and
+ * the next name. That is, we prefer a space to the
+ * extra character of the label because the space
+ * separates the commands and looks nicer.
+ */
+ if(k->label){
+ size_t l;
+ char tmp_label[6*MAX_LABEL+1];
+
+ if(k->label[0] == '[' && k->label[(l=strlen(k->label))-1] == ']' && l > 2){
+ /*
+ * Can't write in k->label, which might be a constant array.
+ */
+ strncpy(tmp_label, &k->label[1], MIN(sizeof(tmp_label),l-2));
+ tmp_label[MIN(sizeof(tmp_label)-1,l-2)] = '\0';
+
+ snprintf(this_label, sizeof(this_label), "[%s]", _(tmp_label));
+ }
+ else
+ strncpy(this_label, _(k->label), sizeof(this_label));
+
+ this_label[sizeof(this_label)-1] = '\0';
+ if(!last_in_row){
+ int trunc;
+
+ trunc = (k+2)->column - k->column
+ - ((k->name ? utf8_width(k->name) : 0) + 1);
+ /*
+ * trunc columns available for label but we don't want the label
+ * to go all the way to the edge
+ */
+ if(utf8_width(this_label) >= trunc){
+ if(trunc > 1){
+ strncpy(tmp_label, this_label, sizeof(tmp_label));
+ tmp_label[sizeof(tmp_label)-1] = '\0';
+ l = utf8_pad_to_width(this_label, tmp_label, sizeof(this_label)-2, trunc-1, 1);
+ this_label[l++] = SPACE;
+ this_label[l] = '\0';;
+ }
+ else if(trunc == 1)
+ this_label[0] = SPACE;
+ else
+ this_label[0] = '\0';
+
+ this_label[sizeof(this_label)-1] = '\0';
+ }
+ }
+ }
+ else
+ this_label[0] = '\0';
+
+ if(!(k->column == last_time->column
+ && (last_in_row || (k+2)->column <= next_col)
+ && ((empty && !*last_time->label && !*last_time->name)
+ || (!empty
+ && this_label && !strcmp(this_label,last_time->label)
+ && ((k->name && !strcmp(k->name,last_time->name))
+ || ufk))))){
+ if(empty){
+ /* blank out key with spaces */
+ strncpy(temp, repeat_char(
+ ((last_in_row || (k+2)->column > max_column)
+ ? max_column
+ : (k+2)->column) -
+ (fix_start
+ ? 0
+ : k->column),
+ SPACE), sizeof(temp));
+ temp[sizeof(temp)-1] = '\0';
+ last_time->column = k->column;
+ *last_time->name = '\0';
+ *last_time->label = '\0';
+ MoveCursor(real_row + i, column + (fix_start ? 0 : k->column));
+ Write_to_screen(temp);
+ c = (fix_start ? 0 : k->column) + strlen(temp);
+ }
+ else{
+ /* make sure extra space before key name is there */
+ if(fix_start){
+ strncpy(temp, repeat_char(k->column, SPACE), sizeof(temp));
+ temp[sizeof(temp)-1] = '\0';
+ MoveCursor(real_row + i, column + 0);
+ Write_to_screen(temp);
+ }
+
+ /* short name of the key */
+ if(ufk)
+ snprintf(temp, sizeof(temp), "F%d", fkey);
+ else
+ strncpy(temp, k->name, sizeof(temp));
+
+ temp[sizeof(temp)-1] = '\0';
+ last_time->column = k->column;
+ strncpy(last_time->name, temp, 6*MAX_KEYNAME);
+ last_time->name[6*MAX_KEYNAME] = '\0';
+ /* make sure name not too long */
+#ifdef MOUSE
+ strncpy(keystr, temp, sizeof(keystr));
+ keystr[sizeof(keystr)-1] = '\0';
+#endif
+ MoveCursor(real_row + i, column + k->column);
+ if(!empty){
+ if(name_color)
+ (void)pico_set_colorp(name_color, PSC_NONE);
+ else
+ StartInverse();
+ }
+
+ Write_to_screen(temp);
+ c = k->column + utf8_width(temp);
+ if(!empty){
+ if(!name_color)
+ EndInverse();
+ }
+
+ /* now the space after the name and the label */
+ temp[0] = '\0';
+ if(c < max_column){
+ temp[0] = SPACE;
+ temp[1] = '\0';
+ strncat(temp, this_label, sizeof(temp)-strlen(temp)-1);
+
+ /* Don't run over the right hand edge */
+ if(utf8_width(temp) > max_column - c){
+ size_t l;
+
+ l = utf8_pad_to_width(temp2, temp, sizeof(temp2)-1, max_column-c, 1);
+ temp2[l] = '\0';
+ strncpy(temp, temp2, sizeof(temp));
+ temp[sizeof(temp)-1] = '\0';
+ }
+
+ c += utf8_width(temp);
+ }
+
+#ifdef MOUSE
+ strncat(keystr, temp, sizeof(keystr)-strlen(keystr)-1);
+ keystr[sizeof(keystr)-1] = '\0';
+#endif
+ /* fill out rest of this key with spaces */
+ if(c < max_column){
+ if(last_in_row){
+ strncat(temp, repeat_char(max_column - c, SPACE), sizeof(temp)-strlen(temp)-1);
+ c = max_column;
+ }
+ else{
+ if(c < (k+2)->column){
+ strncat(temp,
+ repeat_char((k+2)->column - c, SPACE), sizeof(temp)-strlen(temp)-1);
+ c = (k+2)->column;
+ }
+ }
+
+ temp[sizeof(temp)-1] = '\0';
+ }
+
+ strncpy(last_time->label, this_label, 6*MAX_LABEL);
+ last_time->label[6*MAX_LABEL] = '\0';
+ if(label_color)
+ (void)pico_set_colorp(label_color, PSC_NONE);
+
+ Write_to_screen(temp);
+ }
+ }
+#ifdef MOUSE
+ else if(!empty)
+ /* fill in what register_key needs from cached data */
+ snprintf(keystr, sizeof(keystr), "%s %s", last_time->name, last_time->label);
+
+ if(!empty){
+ int len;
+
+ /*
+ * If label ends in space,
+ * don't register the space part of label.
+ */
+ len = strlen(keystr);
+ while(keystr[len-1] == SPACE)
+ len--;
+ len--;
+
+ register_key(j, ufk ? PF1 + fkey - 1
+ : (k->name[0] == '^')
+ ? ctrl(k->name[1])
+ : (!strucmp(k->name, "ret"))
+ ? ctrl('M')
+ : (!strucmp(k->name, "tab"))
+ ? '\t'
+ : (!strucmp(k->name, "spc"))
+ ? SPACE
+ : (!strucmp(k->name, HISTORY_UP_KEYNAME))
+ ? KEY_UP
+ : (!strucmp(k->name, HISTORY_DOWN_KEYNAME))
+ ? KEY_DOWN
+ : (k->bind.nch)
+ ? ((isascii((int) k->bind.ch[0]) && islower((int) k->bind.ch[0]))
+ ? toupper((unsigned char) k->bind.ch[0])
+ : k->bind.ch[0])
+ : k->name[0],
+ keystr, print_inverted_label,
+ real_row+i, k->column, len,
+ name_color, label_color);
+ }
+#endif
+
+ }
+
+ while(++j < 6*(i+1))
+ last_time_buf[j].column = -1;
+ }
+
+ fflush(stdout);
+ if(lastc){
+ (void)pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ if(label_color)
+ free_color_pair(&label_color);
+ if(name_color)
+ free_color_pair(&name_color);
+ }
+}
+
+
+/*
+ * Clear the key menu lines.
+ */
+void
+blank_keymenu(int row, int column)
+{
+ struct variable *vars = ps_global->vars;
+ COLOR_PAIR *lastc;
+
+ if(FOOTER_ROWS(ps_global) > 1){
+ km_state.blanked = 1;
+ km_state.row = row;
+ km_state.column = column;
+ MoveCursor(row, column);
+ lastc = pico_set_colors(VAR_KEYLABEL_FORE_COLOR,
+ VAR_KEYLABEL_BACK_COLOR, PSC_NORM|PSC_RET);
+
+ CleartoEOLN();
+ MoveCursor(row+1, column);
+ CleartoEOLN();
+ fflush(stdout);
+ if(lastc){
+ (void)pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+ }
+}
+
+
+void
+draw_cancel_keymenu(void)
+{
+ bitmap_t bitmap;
+
+ setbitmap(bitmap);
+ draw_keymenu(&cancel_keymenu, bitmap, ps_global->ttyo->screen_cols,
+ 1-FOOTER_ROWS(ps_global), 0, FirstMenu);
+}
+
+
+void
+clearfooter(struct pine *ps)
+{
+ ClearLines(ps->ttyo->screen_rows - 3, ps->ttyo->screen_rows - 1);
+ mark_keymenu_dirty();
+ mark_status_unknown();
+}
+
+
+/*
+ * Calculate formatting for key menu at bottom of screen
+ *
+ * Args: km -- The key_menu structure to format
+ * bm -- Bitmap indicating which menu items should be displayed. If
+ * an item is NULL, that also means it shouldn't be displayed.
+ * Sometimes the bitmap will be turned on in that case and just
+ * rely on the NULL entry.
+ * width -- the screen width to format it at
+ *
+ * If already formatted for this particular screen width and the requested
+ * bitmap and formatted bitmap agree, return.
+ *
+ * The formatting results in the column field in the key_menu being
+ * filled in. The column field is the column to start the label at, the
+ * name of the key; after that is the label for the key. The basic idea
+ * is to line up the end of the names and beginning of the labels. If
+ * the name is too long and shifting it left would run into previous
+ * label, then shift the whole menu right, or at least that entry if
+ * things following are short enough to fit back into the regular
+ * spacing. This has to be calculated and not fixed so it can cope with
+ * screen resize.
+ */
+void
+format_keymenu(struct key_menu *km, unsigned char *bm, int width)
+{
+ int spacing[7], w[6], min_w[6], tw[6], extra[6], ufk, i, set;
+
+ /* already formatted? */
+ if(!km || (width == km->width &&
+ km->how_many <= km->formatted_hm &&
+ !memcmp(km->bitmap, bm, BM_SIZE)))
+ return;
+
+ /*
+ * If we're in the initial command sequence we may be using function
+ * keys instead of alphas, or vice versa, so we want to recalculate
+ * the formatting next time through.
+ */
+ if((F_ON(F_USE_FK,ps_global) && ps_global->orig_use_fkeys) ||
+ (F_OFF(F_USE_FK,ps_global) && !ps_global->orig_use_fkeys)){
+ km->width = width;
+ km->formatted_hm = km->how_many;
+ memcpy(km->bitmap, bm, BM_SIZE);
+ }
+
+ ufk = F_ON(F_USE_FK,ps_global); /* ufk = "Using Function Keys" */
+
+ /* set up "ideal" columns to start in, plus fake 7th column start */
+ for(i = 0; i < 7; i++)
+ spacing[i] = (i * width) / 6;
+
+ /* Loop thru each set of 12 menus */
+ for(set = 0; set < km->how_many; set++){
+ int k_top, k_bot, top_name_width, bot_name_width,
+ top_label_width, bot_label_width, done, offset, next_one;
+ struct key *keytop, *keybot;
+
+ offset = set * 12; /* offset into keymenu */
+
+ /*
+ * Find the required widths for each box.
+ */
+ for(i = 0; i < 6; i++){
+ k_top = offset + i*2;
+ k_bot = k_top + 1;
+ keytop = &km->keys[k_top];
+ keybot = &km->keys[k_bot];
+
+ /*
+ * The width of a box is the max width of top or bottom name,
+ * plus 1 space, plus the max width of top or bottom label.
+ *
+ * ? HelpInfo
+ * ^C Cancel
+ * ||||||||||| = 2 + 1 + 8 = 11
+ *
+ * Then we adjust that by adding one space after the box to
+ * separate it from the next box. The last box doesn't need that
+ * but we may need an extra space for last box to avoid putting
+ * a character in the lower right hand cell of display.
+ * We also have a minimum label width (if screen is really narrow)
+ * of 3, so at least "Hel" and "Can" shows and the rest gets
+ * truncated off right hand side.
+ */
+
+ top_name_width = (keytop->name && bitnset(k_top,bm))
+ ? (ufk ? (i >= 5 ? 3 : 2)
+ : utf8_width(keytop->name)) : 0;
+ bot_name_width = (keybot->name && bitnset(k_bot,bm))
+ ? (ufk ? (i >= 4 ? 3 : 2)
+ : utf8_width(keybot->name)) : 0;
+ /*
+ * Labels are complicated by the fact that we want to look
+ * up their translation, but also by the fact that we surround
+ * the word with brackets like [ViewMsg] when the command is
+ * the default. We want to look up the translation of the
+ * part inside the brackets, not the whole thing.
+ */
+ if(keytop->label && bitnset(k_top,bm)){
+ char tmp_label[6*MAX_LABEL+1];
+ size_t l;
+
+ if(keytop->label[0] == '[' && keytop->label[(l=strlen(keytop->label))-1] == ']' && l > 2){
+ /*
+ * Can't write in k->label, which might be a constant array.
+ */
+ strncpy(tmp_label, &keytop->label[1], MIN(sizeof(tmp_label),l-2));
+ tmp_label[MIN(sizeof(tmp_label)-1,l-2)] = '\0';
+
+ top_label_width = 2 + utf8_width(_(tmp_label));
+ }
+ else
+ top_label_width = utf8_width(_(keytop->label));
+ }
+ else
+ top_label_width = 0;
+
+ if(keybot->label && bitnset(k_bot,bm)){
+ char tmp_label[6*MAX_LABEL+1];
+ size_t l;
+
+ if(keybot->label[0] == '[' && keybot->label[(l=strlen(keybot->label))-1] == ']' && l > 2){
+ strncpy(tmp_label, &keybot->label[1], MIN(sizeof(tmp_label),l-2));
+ tmp_label[MIN(sizeof(tmp_label)-1,l-2)] = '\0';
+
+ bot_label_width = 2 + utf8_width(_(tmp_label));
+ }
+ else
+ bot_label_width = utf8_width(_(keybot->label));
+ }
+ else
+ bot_label_width = 0;
+
+ /*
+ * The 1 for i < 5 is the space between adjacent boxes.
+ * The last 1 or 0 when i == 5 is so that we won't try to put
+ * a character in the lower right cell of the display, since that
+ * causes a linefeed on some terminals.
+ */
+ w[i] = MAX(top_name_width, bot_name_width) + 1 +
+ MAX(top_label_width, bot_label_width) +
+ ((i < 5) ? 1
+ : ((bot_label_width >= top_label_width) ? 1 : 0));
+
+ /*
+ * The smallest we'll squeeze a column.
+ *
+ * X ABCDEF we'll squeeze to X ABC
+ * YZ GHIJ YZ GHI
+ */
+ min_w[i] = MAX(top_name_width, bot_name_width) + 1 +
+ MIN(MAX(top_label_width, bot_label_width), 3) +
+ ((i < 5) ? 1
+ : ((bot_label_width >= top_label_width) ? 1 : 0));
+
+ /* init trial width */
+ tw[i] = spacing[i+1] - spacing[i];
+ extra[i] = tw[i] - w[i]; /* negative if it doesn't fit */
+ }
+
+ /*
+ * See if we can fit everything on the screen.
+ */
+ done = 0;
+ while(!done){
+ int smallest_extra, how_small;
+
+ /* Find smallest extra */
+ smallest_extra = -1;
+ how_small = 100;
+ for(i = 0; i < 6; i++){
+ if(extra[i] < how_small){
+ smallest_extra = i;
+ how_small = extra[i];
+ }
+ }
+
+ if(how_small >= 0) /* everything fits */
+ done++;
+ else{
+ int take_from, how_close;
+
+ /*
+ * Find the one that is closest to the ideal width
+ * that has some extra to spare.
+ */
+ take_from = -1;
+ how_close = 100;
+ for(i = 0; i < 6; i++){
+ if(extra[i] > 0 &&
+ ((spacing[i+1]-spacing[i]) - tw[i]) < how_close){
+ take_from = i;
+ how_close = (spacing[i+1]-spacing[i]) - tw[i];
+ }
+ }
+
+ if(take_from >= 0){
+ /*
+ * Found one. Take one from take_from and add it
+ * to the smallest_extra.
+ */
+ tw[smallest_extra]++;
+ extra[smallest_extra]++;
+ tw[take_from]--;
+ extra[take_from]--;
+ }
+ else{
+ int used_width;
+
+ /*
+ * Oops. Not enough space to fit everything in.
+ * Some of the labels are truncated. Some may even be
+ * truncated past the minimum. We make sure that each
+ * field is at least its minimum size, and then we cut
+ * back those over the minimum until we can fit all the
+ * minimal names on the screen (if possible).
+ */
+ for(i = 0; i < 6; i++)
+ tw[i] = MAX(tw[i], min_w[i]);
+
+ used_width = 0;
+ for(i = 0; i < 6; i++)
+ used_width += tw[i];
+
+ while(used_width > width && !done){
+ int candidate, excess;
+
+ /*
+ * Find the one with the most width over it's
+ * minimum width.
+ */
+ candidate = -1;
+ excess = -100;
+ for(i = 0; i < 6; i++){
+ if(tw[i] - min_w[i] > excess){
+ candidate = i;
+ excess = tw[i] - min_w[i];
+ }
+ }
+
+ if(excess > 0){
+ tw[candidate]--;
+ used_width--;
+ }
+ else
+ done++;
+ }
+
+ done++;
+ }
+ }
+ }
+
+ /*
+ * Assign the format we came up with to the keymenu.
+ */
+ next_one = 0;
+ for(i = 0; i < 6; i++){
+ k_top = offset + i*2;
+ k_bot = k_top + 1;
+ keytop = &km->keys[k_top];
+ keybot = &km->keys[k_bot];
+ top_name_width = (keytop->name && bitnset(k_top,bm))
+ ? (ufk ? (i >= 5 ? 3 : 2)
+ : utf8_width(keytop->name)) : 0;
+ bot_name_width = (keybot->name && bitnset(k_bot,bm))
+ ? (ufk ? (i >= 4 ? 3 : 2)
+ : utf8_width(keybot->name)) : 0;
+
+ if(top_name_width >= bot_name_width){
+ keytop->column = next_one;
+ keybot->column = next_one + (top_name_width - bot_name_width);
+ }
+ else{
+ keytop->column = next_one + (bot_name_width - top_name_width);
+ keybot->column = next_one;
+ }
+
+ next_one += tw[i];
+ }
+ }
+}
+
+
+/*
+ * Draw the key menu at bottom of screen
+ *
+ * Args: km -- key_menu structure
+ * bitmap -- which fields are active
+ * width -- the screen width to format it at
+ * row -- where to put it
+ * column -- where to put it
+ * what -- this is an enum telling us whether to display the
+ * first menu (first set of 12 keys), or to display the same
+ * one we displayed last time, or to display a particular
+ * one (which), or to display the next one.
+ *
+ * Fields are inactive if *either* the corresponding bitmap entry is 0 *or*
+ * the actual entry in the key_menu is NULL. Therefore, it is sometimes
+ * useful to just turn on all the bits in a bitmap and let the NULLs take
+ * care of it. On the other hand, the bitmap gives a convenient method
+ * for turning some keys on or off dynamically or due to options.
+ * Both methods are used about equally.
+ *
+ * Also saves the state for a possible redraw later.
+ *
+ * Row should usually be a negative number. If row is 0, the menu is not
+ * drawn.
+ */
+void
+draw_keymenu(struct key_menu *km, unsigned char *bitmap, int width, int row,
+ int column, OtherMenu what)
+{
+#ifdef _WINDOWS
+ configure_menu_items (km, bitmap);
+#endif
+ format_keymenu(km, bitmap, width);
+
+ /*--- save state for a possible redraw ---*/
+ km_state.km = km;
+ km_state.row = row;
+ km_state.column = column;
+ memcpy(km_state.bitmap, bitmap, BM_SIZE);
+
+ if(row == 0)
+ return;
+
+ if(km_state.blanked)
+ keymenu_is_dirty = 1;
+
+ if(what == FirstMenu || what == SecondMenu || what == ThirdMenu ||
+ what == FourthMenu || what == MenuNotSet){
+ if(what == FirstMenu || what == MenuNotSet)
+ km->which = 0;
+ else if(what == SecondMenu)
+ km->which = 1;
+ else if(what == ThirdMenu)
+ km->which = 2;
+ else if(what == FourthMenu)
+ km->which = 3;
+
+ if(km->which >= km->how_many)
+ km->which = 0;
+ }
+ else if(what == NextMenu)
+ km->which = (km->which + 1) % km->how_many;
+ /* else what must be SameMenu */
+
+ output_keymenu(km, bitmap, row, column);
+
+ km_state.blanked = 0;
+}
+
+
+void
+redraw_keymenu(void)
+{
+ if(km_state.blanked)
+ blank_keymenu(km_state.row, km_state.column);
+ else
+ draw_keymenu(km_state.km, km_state.bitmap, ps_global->ttyo->screen_cols,
+ km_state.row, km_state.column, SameMenu);
+}
+
+
+/*
+ * end_keymenu - free resources associated with keymenu display cache
+ */
+void
+end_keymenu(void)
+{
+ int i;
+
+ for(i = 0; i < 12; i++){
+ if(last_time_buf[i].name)
+ fs_give((void **) &last_time_buf[i].name);
+
+ if(last_time_buf[i].label)
+ fs_give((void **) &last_time_buf[i].label);
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Reveal Keymenu to Pine Command mappings
+
+ Args:
+
+ ----*/
+int
+menu_command(UCS keystroke, struct key_menu *menu)
+{
+ int i, n;
+
+ if(keystroke == KEY_UTF8 || keystroke == KEY_UNKNOWN)
+ return(MC_UTF8);
+
+ if(!menu)
+ return(MC_UNKNOWN);
+
+ if(F_ON(F_USE_FK,ps_global)){
+ /* No alpha commands permitted in function key mode */
+ if(keystroke < 0x80 && isalpha((unsigned char) keystroke))
+ return(MC_UNKNOWN);
+
+ /* Tres simple: compute offset, and test */
+ if(keystroke >= F1 && keystroke <= F12){
+ n = (menu->which * 12) + (keystroke - F1);
+ if(bitnset(n, menu->bitmap))
+ return(menu->keys[n].bind.cmd);
+ }
+ }
+ else if(keystroke >= F1 && keystroke <= F12)
+ return(MC_UNKNOWN);
+
+ /* if ascii, coerce lower case */
+ if(keystroke < 0x80 && isupper((unsigned char) keystroke))
+ keystroke = tolower((unsigned char) keystroke);
+
+ /* keep this here for Windows port */
+ if((keystroke = validatekeys(keystroke)) == KEY_JUNK)
+ return(MC_UNKNOWN);
+
+ /* Scan the list for any keystroke/command binding */
+ if(keystroke != NO_OP_COMMAND)
+ for(i = (menu->how_many * 12) - 1; i >= 0; i--)
+ if(bitnset(i, menu->bitmap))
+ for(n = menu->keys[i].bind.nch - 1; n >= 0; n--)
+ if(keystroke == menu->keys[i].bind.ch[n])
+ return(menu->keys[i].bind.cmd);
+
+ /*
+ * If explicit mapping failed, check feature mappings and
+ * hardwired defaults...
+ */
+ if(F_ON(F_ENABLE_PRYNT,ps_global)
+ && (keystroke == 'y' || keystroke == 'Y')){
+ /* SPECIAL CASE: Scan the list for print bindings */
+ for(i = (menu->how_many * 12) - 1; i >= 0; i--)
+ if(bitnset(i, menu->bitmap))
+ if(menu->keys[i].bind.cmd == MC_PRINTMSG
+ || menu->keys[i].bind.cmd == MC_PRINTTXT)
+ return(menu->keys[i].bind.cmd);
+ }
+
+ if(F_ON(F_ENABLE_LESSTHAN_EXIT,ps_global)
+ && (keystroke == '<' || keystroke == ','
+ || (F_ON(F_ARROW_NAV,ps_global) && keystroke == KEY_LEFT))){
+ /* SPECIAL CASE: Scan the list for MC_EXIT bindings */
+ for(i = (menu->how_many * 12) - 1; i >= 0; i--)
+ if(bitnset(i, menu->bitmap))
+ if(menu->keys[i].bind.cmd == MC_EXIT)
+ return(MC_EXIT);
+ }
+
+ /*
+ * If no match after scanning bindings, try universally
+ * bound keystrokes...
+ */
+ switch(keystroke){
+ case KEY_MOUSE :
+ return(MC_MOUSE);
+
+ case ctrl('P') :
+ case KEY_UP :
+ return(MC_CHARUP);
+
+ case ctrl('N') :
+ case KEY_DOWN :
+ return(MC_CHARDOWN);
+
+ case ctrl('F') :
+ case KEY_RIGHT :
+ return(MC_CHARRIGHT);
+
+ case ctrl('B') :
+ case KEY_LEFT :
+ return(MC_CHARLEFT);
+
+ case ctrl('A') :
+ return(MC_GOTOBOL);
+
+ case ctrl('E') :
+ return(MC_GOTOEOL);
+
+ case ctrl('L') :
+ return(MC_REPAINT);
+
+ case KEY_RESIZE :
+ return(MC_RESIZE);
+
+ case NO_OP_IDLE:
+ case NO_OP_COMMAND:
+ if(USER_INPUT_TIMEOUT(ps_global))
+ user_input_timeout_exit(ps_global->hours_to_timeout); /* no return */
+
+ return(MC_NONE);
+
+ default :
+ break;
+ }
+
+ return(MC_UNKNOWN); /* utter failure */
+}
+
+
+
+/*----------------------------------------------------------------------
+ Set up a binding for cmd, with one key bound to it.
+ Use menu_add_binding to add more keys to this binding.
+
+ Args: menu -- the keymenu
+ key -- the initial key to bind to
+ cmd -- the command to initialize to
+ name -- a pointer to the string to point name to
+ label -- a pointer to the string to point label to
+ keynum -- which key in the keys array to initialize
+
+ For translation purposes, the label in the calling routine
+ should be wrapped in an N_() macro.
+
+ ----*/
+void
+menu_init_binding(struct key_menu *menu, UCS key, int cmd, char *name, char *label, int keynum)
+{
+ /* if ascii, coerce to lower case */
+ if(key < 0x80 && isupper((unsigned char)key))
+ key = tolower((unsigned char)key);
+
+ /* remove binding from any other key */
+ menu_clear_cmd_binding(menu, cmd);
+
+ menu->keys[keynum].name = name;
+ menu->keys[keynum].label = label;
+ menu->keys[keynum].bind.cmd = cmd;
+ menu->keys[keynum].bind.nch = 0;
+ menu->keys[keynum].bind.ch[menu->keys[keynum].bind.nch++] = key;
+}
+
+
+/*----------------------------------------------------------------------
+ Add a key/command binding to the given keymenu structure
+
+ Args:
+
+ ----*/
+void
+menu_add_binding(struct key_menu *menu, UCS key, int cmd)
+{
+ int i, n;
+
+ /* NOTE: cmd *MUST* already have had a binding */
+ for(i = (menu->how_many * 12) - 1; i >= 0; i--)
+ if(menu->keys[i].bind.cmd == cmd){
+ for(n = menu->keys[i].bind.nch - 1;
+ n >= 0 && key != menu->keys[i].bind.ch[n];
+ n--)
+ ;
+
+ /* if ascii, coerce to lower case */
+ if(key < 0x80 && isupper((unsigned char)key))
+ key = tolower((unsigned char)key);
+
+ if(n < 0) /* not already bound, bind it */
+ menu->keys[i].bind.ch[menu->keys[i].bind.nch++] = key;
+
+ break;
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ REMOVE a key/command binding from the given keymenu structure
+
+ Args:
+
+ ----*/
+int
+menu_clear_binding(struct key_menu *menu, UCS key)
+{
+ int i, n;
+
+ /* if ascii, coerce to lower case */
+ if(key < 0x80 && isupper((unsigned char)key))
+ key = tolower((unsigned char)key);
+
+ for(i = (menu->how_many * 12) - 1; i >= 0; i--)
+ for(n = menu->keys[i].bind.nch - 1; n >= 0; n--)
+ if(key == menu->keys[i].bind.ch[n]){
+ int cmd = menu->keys[i].bind.cmd;
+
+ for(--menu->keys[i].bind.nch; n < menu->keys[i].bind.nch; n++)
+ menu->keys[i].bind.ch[n] = menu->keys[i].bind.ch[n+1];
+
+ return(cmd);
+ }
+
+ return(MC_UNKNOWN);
+}
+
+
+void
+menu_clear_cmd_binding(struct key_menu *menu, int cmd)
+{
+ int i;
+
+ for(i = (menu->how_many * 12) - 1; i >= 0; i--){
+ if(cmd == menu->keys[i].bind.cmd){
+ menu->keys[i].name = NULL;
+ menu->keys[i].label = NULL;
+ menu->keys[i].bind.cmd = 0;
+ menu->keys[i].bind.nch = 0;
+ menu->keys[i].bind.ch[0] = 0;
+ }
+ }
+}
+
+
+int
+menu_binding_index(struct key_menu *menu, int cmd)
+{
+ int i;
+
+ for(i = 0; i < menu->how_many * 12; i++)
+ if(cmd == menu->keys[i].bind.cmd)
+ return(i);
+
+ return(-1);
+}
+
+
+#ifdef MOUSE
+/*
+ * print_inverted_label - highlight the label of the given menu item.
+ * (callback from pico mouse routines)
+ *
+ * So far, this is only
+ * ever called with the top left row equal to the bottom right row.
+ * If you change that you probably need to fix it.
+ */
+void
+print_inverted_label(int state, MENUITEM *m)
+{
+ unsigned i, j, k;
+ int col_offsetwid, col_offsetchars, do_color = 0, skipwid = 0, skipchars = 0, len, c;
+ char prename[100];
+ char namepart[100];
+ char labelpart[100];
+ char *lp, *label;
+ COLOR_PAIR *name_color = NULL, *label_color = NULL, *lastc = NULL;
+ struct variable *vars = ps_global->vars;
+
+ if(m->label && (lp=strchr(m->label, ' '))){
+ char save;
+
+ save = *lp;
+ *lp = '\0';
+ col_offsetwid = utf8_width(m->label);
+ col_offsetchars = lp - m->label;
+ *lp = save;
+ }
+ else
+ col_offsetwid = col_offsetchars = 0;
+
+ if(pico_usingcolor() && ((VAR_KEYLABEL_FORE_COLOR &&
+ VAR_KEYLABEL_BACK_COLOR) ||
+ (VAR_KEYNAME_FORE_COLOR &&
+ VAR_KEYNAME_BACK_COLOR))){
+ lastc = pico_get_cur_color();
+
+ if(VAR_KEYNAME_FORE_COLOR && VAR_KEYNAME_BACK_COLOR){
+ name_color = state ? new_color_pair(VAR_KEYNAME_BACK_COLOR,
+ VAR_KEYNAME_FORE_COLOR)
+ : new_color_pair(VAR_KEYNAME_FORE_COLOR,
+ VAR_KEYNAME_BACK_COLOR);
+ }
+ else if(VAR_REV_FORE_COLOR && VAR_REV_BACK_COLOR)
+ name_color = new_color_pair(VAR_REV_FORE_COLOR, VAR_REV_BACK_COLOR);
+
+ if(VAR_KEYLABEL_FORE_COLOR && VAR_KEYLABEL_BACK_COLOR){
+ label_color = state ? new_color_pair(VAR_KEYLABEL_BACK_COLOR,
+ VAR_KEYLABEL_FORE_COLOR)
+ : new_color_pair(VAR_KEYLABEL_FORE_COLOR,
+ VAR_KEYLABEL_BACK_COLOR);
+ }
+ else if(VAR_REV_FORE_COLOR && VAR_REV_BACK_COLOR){
+ label_color = state ? new_color_pair(VAR_REV_FORE_COLOR,
+ VAR_REV_BACK_COLOR)
+ : new_color_pair(VAR_NORM_FORE_COLOR,
+ VAR_NORM_BACK_COLOR);
+ }
+
+ /*
+ * See if we can grok all these colors. If not, we're going to
+ * punt and pretend there are no colors at all.
+ */
+ if(!pico_is_good_colorpair(name_color) ||
+ !pico_is_good_colorpair(label_color)){
+ if(name_color)
+ free_color_pair(&name_color);
+ if(label_color)
+ free_color_pair(&label_color);
+ if(lastc)
+ free_color_pair(&lastc);
+ }
+ else{
+ do_color++;
+ (void)pico_set_colorp(label_color, PSC_NONE);
+ if(!(VAR_KEYLABEL_FORE_COLOR && VAR_KEYLABEL_BACK_COLOR)){
+ if(state)
+ StartInverse();
+ else
+ EndInverse();
+ }
+ }
+ }
+
+ if(!do_color){
+ /*
+ * Command name's already inverted, leave it.
+ */
+ skipwid = state ? 0 : col_offsetwid;
+ skipchars = state ? 0 : col_offsetchars;
+ if(state)
+ StartInverse();
+ else
+ EndInverse();
+ }
+
+ MoveCursor((int)(m->tl.r), (int)(m->tl.c) + skipwid);
+
+ label = m->label ? m->label : "";
+ len = strlen(label);
+
+ /*
+ * this is a bit complicated by the fact that we have to keep track of
+ * the screenwidth as we print the label, because the screenwidth might
+ * not be the same as the number of characters.
+* UNTESTED SINCE switching to UTF-8 *
+ */
+ for(i = m->tl.r; i <= m->br.r; i++){
+ /* collect part before name */
+ for(k=0, j = m->tl.c + skipchars; j < MIN(m->lbl.c,m->br.c); j++){
+ if(k < sizeof(prename))
+ prename[k++] = ' ';
+ }
+
+ if(k < sizeof(prename))
+ prename[k] = '\0';
+
+ /* collect name part */
+ for(k=0; j < MIN(m->lbl.c+col_offsetchars,m->br.c); j++){
+ c = (i == m->lbl.r &&
+ j - m->lbl.c < len) ? label[j - m->lbl.c] : ' ';
+ if(k < sizeof(namepart))
+ namepart[k++] = c;
+ }
+
+ if(k < sizeof(namepart))
+ namepart[k] = '\0';
+
+ /* collect label part */
+ for(k=0; j <= m->br.c; j++){
+ c = (i == m->lbl.r &&
+ j - m->lbl.c < len) ? label[j - m->lbl.c] : ' ';
+ if(k < sizeof(labelpart))
+ labelpart[k++] = c;
+ }
+
+ if(k < sizeof(labelpart))
+ labelpart[k] = '\0';
+ }
+
+ if(prename)
+ Write_to_screen(prename);
+
+ if(namepart){
+ if(name_color && col_offsetchars)
+ (void) pico_set_colorp(name_color, PSC_NONE);
+
+ Write_to_screen(namepart);
+ }
+
+ if(labelpart){
+ if(name_color && col_offsetchars){
+ if(label_color)
+ (void) pico_set_colorp(label_color, PSC_NONE);
+ else{
+ if(state)
+ StartInverse();
+ else
+ EndInverse();
+ }
+ }
+
+ Write_to_screen(labelpart);
+ }
+
+ if(do_color){
+ if(lastc){
+ (void)pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+ else if(state)
+ EndInverse();
+ else
+ pico_set_normal_color();
+
+ if(name_color)
+ free_color_pair(&name_color);
+ if(label_color)
+ free_color_pair(&label_color);
+ }
+ else{
+ if(state)
+ EndInverse();
+ }
+}
+#endif /* MOUSE */
+
+
+#ifdef _WINDOWS
+/*
+ * This function scans the key menu and calls mswin.c functions
+ * to build a corresponding windows menu.
+ */
+void
+configure_menu_items (struct key_menu *km, bitmap_t bitmap)
+{
+ int i;
+ struct key *k;
+ UCS key;
+
+ mswin_menuitemclear ();
+
+ if(!km)
+ return;
+
+ for (i = 0, k = km->keys ; i < km->how_many * 12; i++, k++) {
+ if (k->name != NULL && k->label != NULL && bitnset (i, bitmap) &&
+ k->menuitem != KS_NONE) {
+
+ if (k->name[0] == '^')
+ key = ctrl(k->name[1]);
+ else if (strcmp(k->name, "Ret") == 0)
+ key = '\r';
+ else if (strcmp(k->name, "Spc") == 0)
+ key = ' ';
+ else
+ key = k->name[0];
+
+ mswin_menuitemadd (key, k->label, k->menuitem, 0);
+ }
+ }
+}
+#endif
diff --git a/alpine/keymenu.h b/alpine/keymenu.h
new file mode 100644
index 00000000..0802c20d
--- /dev/null
+++ b/alpine/keymenu.h
@@ -0,0 +1,670 @@
+/*
+ * $Id: keymenu.h 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_KEYMENU_INCLUDED
+#define PINE_KEYMENU_INCLUDED
+
+
+#include <general.h>
+#include "../pith/state.h"
+#include "../pith/bitmap.h"
+
+
+/*------
+ A key menu has two ways to turn on and off individual items in the menu.
+ If there is a null entry in the key_menu structure for that key, then
+ it is off. Also, if the passed bitmap has a zero in the position for
+ that key, then it is off. This means you can usually set all of the
+ bitmaps and only turn them off if you want to kill a key that is normally
+ there otherwise.
+ Each key_menu is an array of keys with a multiple of 12 number of keys.
+ ------*/
+/*------
+ Argument to draw_keymenu(). These are to identify which of the possibly
+ multiple sets of twelve keys should be shown in the keymenu. That is,
+ a keymenu may have 24 or 36 keys, so that there are 2 or 3 different
+ screens of key menus for that keymenu. FirstMenu means to use the
+ first twelve, NextTwelve uses the one after the previous one, SameTwelve
+ uses the same one.
+ ------*/
+typedef enum {MenuNotSet = 0, FirstMenu, NextMenu, SameMenu,
+ SecondMenu, ThirdMenu, FourthMenu} OtherMenu;
+
+
+struct key {
+ char *name; /* the short name */
+ char *label; /* the descriptive label */
+ struct { /* */
+ short cmd; /* the resulting command */
+ short nch; /* how many of ch are active */
+ UCS ch[11]; /* which keystrokes trigger cmd */
+ } bind; /* */
+ KS_OSDATAVAR /* slot for port-specific data */
+ short column; /* menu col after formatting */
+};
+
+
+struct key_menu {
+ unsigned int how_many:4; /* how many separate sets of 12 */
+ unsigned int which:4; /* which of the sets are we using */
+ unsigned int width:8; /* screen width when formatting done */
+ struct key *keys; /* array of how_many*12 size structs */
+ unsigned int formatted_hm:4; /* how_many when formatting done */
+ bitmap_t bitmap;
+};
+
+
+/*
+ * Definitions for the various Menu Commands...
+ */
+#define MC_NONE 0 /* NO command defined */
+#define MC_UNKNOWN 200
+#define MC_UTF8 201
+
+/* Cursor/page Motion */
+#define MC_CHARUP 100
+#define MC_CHARDOWN 101
+#define MC_CHARRIGHT 102
+#define MC_CHARLEFT 103
+#define MC_GOTOBOL 104
+#define MC_GOTOEOL 105
+#define MC_GOTOSOP 106
+#define MC_GOTOEOP 107
+#define MC_PAGEUP 108
+#define MC_PAGEDN 109
+#define MC_MOUSE 110
+#define MC_HOMEKEY 111
+#define MC_ENDKEY 112
+
+/* New Screen Commands */
+#define MC_HELP 500
+#define MC_QUIT 501
+#define MC_OTHER 502
+#define MC_MAIN 503
+#define MC_INDEX 504
+#define MC_VIEW_TEXT 505
+#define MC_VIEW_ATCH 506
+#define MC_FOLDERS 507
+#define MC_ADDRBOOK 508
+#define MC_RELNOTES 509
+#define MC_KBLOCK 510
+#define MC_JOURNAL 511
+#define MC_SETUP 512
+#define MC_COLLECTIONS 513
+#define MC_PARENT 514
+#define MC_ROLE 515
+#define MC_LISTMGR 516
+#define MC_THRDINDX 517
+#define MC_SECURITY 518
+
+/* Commands within Screens */
+#define MC_NEXTITEM 700
+#define MC_PREVITEM 701
+#define MC_DELETE 702
+#define MC_UNDELETE 703
+#define MC_COMPOSE 704
+#define MC_REPLY 705
+#define MC_FORWARD 706
+#define MC_GOTO 707
+#define MC_TAB 708
+#define MC_WHEREIS 709
+#define MC_ZOOM 710
+#define MC_PRINTMSG 711
+#define MC_PRINTTXT 712
+#define MC_TAKE 713
+#define MC_SAVE 714
+#define MC_EXPORT 715
+#define MC_IMPORT 716
+#define MC_EXPUNGE 717
+#define MC_UNEXCLUDE 718
+#define MC_CHOICE 719
+#define MC_SELECT 720
+#define MC_SELCUR 721
+#define MC_SELALL 722
+#define MC_UNSELALL 723
+#define MC_APPLY 724
+#define MC_SORT 725
+#define MC_FULLHDR 726
+#define MC_BOUNCE 727
+#define MC_FLAG 728
+#define MC_PIPE 729
+#define MC_EXIT 730
+#define MC_PRINTALL 731
+#define MC_REPAINT 732
+#define MC_JUMP 733
+#define MC_RESIZE 734
+#define MC_FWDTEXT 735
+#define MC_SAVETEXT 736
+#define MC_ABOUTATCH 737
+#define MC_LISTMODE 738
+#define MC_RENAMEFLDR 739
+#define MC_ADDFLDR 740
+#define MC_SUBSCRIBE 741
+#define MC_UNSUBSCRIBE 742
+#define MC_ADD 743
+#define MC_TOGGLE 744
+#define MC_EDIT 745
+#define MC_ADDABOOK 746
+#define MC_DELABOOK 747
+#define MC_VIEW_ENTRY 748
+#define MC_EDITABOOK 750
+#define MC_OPENABOOK 751
+#define MC_POPUP 752
+#define MC_EXPAND 753
+#define MC_UNEXPAND 754
+#define MC_COPY 755
+#define MC_SHUFFLE 756
+#define MC_VIEW_HANDLE 757
+#define MC_NEXT_HANDLE 758
+#define MC_PREV_HANDLE 759
+#define MC_QUERY_SERV 760
+#define MC_GRIPE_LOCAL 761
+#define MC_GRIPE_PIC 762
+#define MC_GRIPE_READ 763
+#define MC_GRIPE_POST 764
+#define MC_FINISH 765
+#define MC_PRINTFLDR 766
+#define MC_OPENFLDR 767
+#define MC_EDITFILE 768
+#define MC_ADDFILE 769
+#define MC_DELFILE 770
+#define MC_CHOICEB 771
+#define MC_CHOICEC 772
+#define MC_CHOICED 773
+#define MC_CHOICEE 774
+#define MC_DEFAULT 775
+#define MC_TOGGLEB 776
+#define MC_TOGGLEC 777
+#define MC_TOGGLED 778
+#define MC_RGB1 779
+#define MC_RGB2 780
+#define MC_RGB3 781
+#define MC_EXITQUERY 782
+#define MC_ADDHDR 783
+#define MC_DELHDR 784
+#define MC_PRINTER 785
+#define MC_PASSWD 786
+#define MC_CONFIG 787
+#define MC_SIG 788
+#define MC_ABOOKS 789
+#define MC_CLISTS 790
+#define MC_RULES 791
+#define MC_DIRECTORY 792
+#define MC_KOLOR 793
+#define MC_EXCEPT 794
+#define MC_REMOTE 795
+#define MC_NO_EXCEPT 796
+#define MC_YES 797
+#define MC_NO 798
+#define MC_NOT 799
+#define MC_COLLAPSE 800
+#define MC_CHK_RECENT 801
+#define MC_DECRYPT 802
+#define MC_QUOTA 803
+
+/*
+ * Some standard Key/Command Bindings
+ */
+#define NULL_MENU {NULL, NULL, {MC_NONE}, KS_NONE}
+/*
+ * TRANSLATORS: Alpine has a key menu at the bottom of each screen which
+ * lists all of the available commands. Each command is a single character
+ * which is followed with a short descriptive word which describes what
+ * the command does. Commands are almost always to be thought of as verbs
+ * so the descriptive words describe what the command does.
+ * These descriptive words have to fit across the
+ * screen so they would normally only be 8-10 characters long, though
+ * they will be truncated if there isn't enough space. The command
+ * descriptions are usually chosen so that they will be a mnemonic
+ * for the command. For example, the command S is Save,
+ * E is Export, Q is Quit, and ? is Help. You get to translate the
+ * label (the Save part) but the actual command (the S) will stay
+ * the same, so it will be very difficult to come up with mnemonic
+ * labels. The mnemonic isn't necessary, just nice. You can see that
+ * we have stretched to the edge of the usefullness of mnemonics with
+ * cases like K Kolor (instead of Color) and X eXceptions (because E
+ * already meant something else).
+ */
+#define HELP_MENU {"?", N_("Help"), \
+ {MC_HELP, 2, {'?',ctrl('G')}}, \
+ KS_SCREENHELP}
+/* TRANSLATORS: Show Other commands */
+#define OTHER_MENU {"O", N_("OTHER CMDS"), \
+ {MC_OTHER, 1, {'o'}}, \
+ KS_NONE}
+/* TRANSLATORS: WhereIs command searches for text */
+#define WHEREIS_MENU {"W", N_("WhereIs"), \
+ {MC_WHEREIS, 2, {'w',ctrl('W')}}, \
+ KS_WHEREIS}
+/* TRANSLATORS: the Main Menu is the menu of commands you get when
+ you first start alpine. */
+#define MAIN_MENU {"M", N_("Main Menu"), \
+ {MC_MAIN, 1, {'m'}}, \
+ KS_MAINMENU}
+#define QUIT_MENU {"Q", N_("Quit Alpine"), \
+ {MC_QUIT, 1, {'q'}}, \
+ KS_EXIT}
+/* TRANSLATORS: go to Previous Message */
+#define PREVMSG_MENU {"P", N_("PrevMsg"), \
+ {MC_PREVITEM, 1, {'p'}}, \
+ KS_PREVMSG}
+/* TRANSLATORS: go to Next Message */
+#define NEXTMSG_MENU {"N", N_("NextMsg"), \
+ {MC_NEXTITEM, 1, {'n'}}, \
+ KS_NEXTMSG}
+#define HOMEKEY_MENU {"Hme", N_("FirstPage"), \
+ {MC_HOMEKEY, 1, {KEY_HOME}}, \
+ KS_NONE}
+#define ENDKEY_MENU {"End", N_("LastPage"), \
+ {MC_ENDKEY, 1, {KEY_END}}, \
+ KS_NONE}
+/* TRANSLATORS: go to Previous Page */
+#define PREVPAGE_MENU {"-", N_("PrevPage"), \
+ {MC_PAGEUP, 3, {'-',ctrl('Y'),KEY_PGUP}}, \
+ KS_PREVPAGE}
+#define NEXTPAGE_MENU {"Spc", N_("NextPage"), \
+ {MC_PAGEDN, 4, {'+',' ',ctrl('V'),KEY_PGDN}}, \
+ KS_NEXTPAGE}
+/* TRANSLATORS: Jump to a different message in the index */
+#define JUMP_MENU {"J", N_("Jump"), \
+ {MC_JUMP, 1, {'j'}}, \
+ KS_JUMPTOMSG}
+/* TRANSLATORS: Forward Email */
+#define FWDEMAIL_MENU {"F", N_("Fwd Email"), \
+ {MC_FWDTEXT,1,{'f'}}, \
+ KS_FORWARD}
+#define PRYNTMSG_MENU {"%", N_("Print"), \
+ {MC_PRINTMSG,1,{'%'}}, \
+ KS_PRINT}
+#define PRYNTTXT_MENU {"%", N_("Print"), \
+ {MC_PRINTTXT,1,{'%'}}, \
+ KS_PRINT}
+/* TRANSLATORS: In alpine, Save means to save something internally
+ within alpine or within the email system. */
+#define SAVE_MENU {"S", N_("Save"), \
+ {MC_SAVE,1,{'s'}}, \
+ KS_SAVE}
+/* TRANSLATORS: In alpine, Export means to save something externally
+ to alpine, to a file in the user's home directory, for example. */
+#define EXPORT_MENU {"E", N_("Export"), \
+ {MC_EXPORT, 1, {'e'}}, \
+ KS_EXPORT}
+/* TRANSLATORS: Edit a new message to be sent as email */
+#define COMPOSE_MENU {"C", N_("Compose"), \
+ {MC_COMPOSE,1,{'c'}}, \
+ KS_COMPOSER}
+/* TRANSLATORS: Edit a new message while acting in one of
+ your roles */
+#define RCOMPOSE_MENU {"#", N_("Role"), \
+ {MC_ROLE,1,{'#'}}, \
+ KS_NONE}
+#define DELETE_MENU {"D", N_("Delete"), \
+ {MC_DELETE,2,{'d',KEY_DEL}}, \
+ KS_DELETE}
+#define UNDELETE_MENU {"U", N_("Undelete"), \
+ {MC_UNDELETE,1,{'u'}}, \
+ KS_UNDELETE}
+/* TRANSLATORS: Reply to an email message */
+#define REPLY_MENU {"R", N_("Reply"), \
+ {MC_REPLY,1,{'r'}}, \
+ KS_REPLY}
+/* TRANSLATORS: Forward an email message to someone else */
+#define FORWARD_MENU {"F", N_("Forward"), \
+ {MC_FORWARD,1,{'f'}}, \
+ KS_FORWARD}
+/* TRANSLATORS: go to List of Folders */
+#define LISTFLD_MENU {"L", N_("ListFldrs"), \
+ {MC_COLLECTIONS,1,{'l'}}, \
+ KS_FLDRLIST}
+/* TRANSLATORS: the index is a list of email messages, go there */
+#define INDEX_MENU {"I", N_("Index"), \
+ {MC_INDEX,1,{'i'}}, \
+ KS_FLDRINDEX}
+/* TRANSLATORS: Go To Folder, a command to go view another mail folder */
+#define GOTO_MENU {"G", N_("GotoFldr"), \
+ {MC_GOTO,1,{'g'}}, \
+ KS_GOTOFLDR}
+/* TRANSLATORS: Take Address is a command to copy addresses from a
+ message to the user's address book */
+#define TAKE_MENU {"T", N_("TakeAddr"), \
+ {MC_TAKE,1,{'t'}}, \
+ KS_TAKEADDR}
+/* TRANSLATORS: To Flag a message means to mark it in some way,
+ for example, flag it as important */
+#define FLAG_MENU {"*", N_("Flag"), \
+ {MC_FLAG,1,{'*'}}, \
+ KS_FLAG}
+/* TRANSLATORS: Pipe refers to the Unix pipe command (the vertical bar). */
+#define PIPE_MENU {"|", N_("Pipe"), \
+ {MC_PIPE,1,{'|'}}, \
+ KS_NONE}
+/* TRANSLATORS: Bounce is sometimes called re-sending a message.
+ A user would use this command if a message had been incorrectly
+ sent to him or her. */
+#define BOUNCE_MENU {"B", N_("Bounce"), \
+ {MC_BOUNCE,1,{'b'}}, \
+ KS_BOUNCE}
+/* TRANSLATORS: Header Mode, refers to showing more or fewer message
+ headers when viewing a message. This command toggles between more
+ and fewer each time it is typed. */
+#define HDRMODE_MENU {"H", N_("HdrMode"), \
+ {MC_FULLHDR,1,{'h'}}, \
+ KS_HDRMODE}
+/* TRANSLATORS: The Tab key goes to the Next New message */
+#define TAB_MENU {"Tab", N_("NextNew"), \
+ {MC_TAB,1,{TAB}}, \
+ KS_NONE}
+/* TRANSLATORS: go to the Previous item */
+#define PREV_MENU {"P", N_("Prev"), \
+ {MC_PREVITEM,1,{'p'}}, \
+ KS_NONE}
+#define NEXT_MENU {"N", N_("Next"), \
+ {MC_NEXTITEM,2,{'n','\t'}}, \
+ KS_NONE}
+#define EXIT_SETUP_MENU {"E", N_("Exit Setup"), \
+ {MC_EXIT,1,{'e'}}, \
+ KS_EXITMODE}
+#define TOGGLE_MENU {"X", "[" N_("Set/Unset") "]", \
+ {MC_TOGGLE,3,{'x',ctrl('M'),ctrl('J')}}, \
+ KS_NONE}
+#define TOGGLEB_MENU {"X", "[" N_("Set/Unset") "]", \
+ {MC_TOGGLEB,3,{'x',ctrl('M'),ctrl('J')}}, \
+ KS_NONE}
+#define TOGGLEC_MENU {"X", "[" N_("Set/Unset") "]", \
+ {MC_TOGGLEC,3,{'x',ctrl('M'),ctrl('J')}}, \
+ KS_NONE}
+#define TOGGLED_MENU {"X", "[" N_("Set/Unset") "]", \
+ {MC_TOGGLED,3,{'x',ctrl('M'),ctrl('J')}}, \
+ KS_NONE}
+/* TRANSLATORS: go to the Previous Collection. A Collection in Alpine refers
+ to a collection of mail folders. */
+#define PREVC_MENU {"P", N_("PrevCltn"), \
+ {MC_PREVITEM,1,{'p'}}, \
+ KS_NONE}
+/* TRANSLATORS: Next Collection. */
+#define NEXTC_MENU {"N", N_("NextCltn"), \
+ {MC_NEXTITEM,2,{'n','\t'}}, \
+ KS_NONE}
+/* TRANSLATORS: Delete Collection. */
+#define DELC_MENU {"D", N_("Del Cltn"), \
+ {MC_DELETE,2,{'d',KEY_DEL}}, \
+ KS_NONE}
+/* TRANSLATORS: go to the Previous Folder (in a list of folders). */
+#define PREVF_MENU {"P", N_("PrevFldr"), \
+ {MC_PREV_HANDLE,3,{'p',ctrl('B'),KEY_LEFT}}, \
+ KS_NONE}
+/* TRANSLATORS: Next Folder (in a list of folders). */
+#define NEXTF_MENU {"N", N_("NextFldr"), \
+ {MC_NEXT_HANDLE,4,{'n',ctrl('F'),TAB,KEY_RIGHT}}, \
+ KS_NONE}
+/* TRANSLATORS: Current Index of messages (go to the current index) */
+#define CIND_MENU {"I", N_("CurIndex"), \
+ {MC_INDEX,1,{'i'}}, \
+ KS_FLDRINDEX}
+/* TRANSLATORS: View this Message */
+#define VIEWMSG_MENU {">", "[" N_("ViewMsg") "]", \
+ {MC_VIEW_TEXT, 5,{'v','.','>',ctrl('M'),ctrl('J')}}, \
+ KS_VIEW}
+/* TRANSLATORS: Sort the index of messages */
+#define FLDRSORT_MENU {"$", N_("SortIndex"), \
+ {MC_SORT,1,{'$'}}, \
+ KS_SORT}
+/* TRANSLATORS: Exit the Take Address screen */
+#define TA_EXIT_MENU {"<",N_("ExitTake"), \
+ {MC_EXIT,4,{'e',ctrl('C'),'<',','}}, \
+ KS_EXITMODE}
+#define TA_NEXT_MENU {"N",N_("Next"), \
+ {MC_CHARDOWN,4,{'n','\t',ctrl('N'),KEY_DOWN}}, \
+ KS_NONE}
+/* TRANSLATORS: abbreviation for Previous */
+#define TA_PREV_MENU {"P",N_("Prev"), \
+ {MC_CHARUP, 3, {'p',ctrl('P'),KEY_UP}}, \
+ KS_NONE}
+
+
+/*
+ * It's bogus that these are defined here. They go with the structures
+ * defined in keymenu.c and have to stay in sync with them.
+ */
+#define OTHER_KEY 1
+#define TWO_KEY 2
+#define THREE_KEY 3
+#define ADD_KEY 8
+#define DELETE_KEY 9
+#define SENDTO_KEY 10
+#define SECONDARY_MAIN_KEY 15
+#define RCOMPOSE_KEY 19
+#define TAKE_KEY 21
+#define SAVE_KEY 22
+#define FORW_KEY 23
+#define KM_COL_KEY 2
+#define KM_SEL_KEY 3
+#define KM_MAIN_KEY 15
+#define KM_ALTVIEW_KEY 16
+#define KM_ZOOM_KEY 21
+#define KM_SELECT_KEY 22
+#define KM_SELCUR_KEY 23
+#define KM_RECENT_KEY 28
+#define KM_SHUFFLE_KEY 30
+#define KM_EXPORT_KEY 32
+#define KM_IMPORT_KEY 33
+#define FC_EXIT_KEY 1
+#define FC_COL_KEY 2
+#define FC_SEL_KEY 3
+#define FC_ALTSEL_KEY 8
+#define SB_SUB_KEY 1
+#define SB_EXIT_KEY 2
+#define SB_SEL_KEY 3
+#define SB_LIST_KEY 8
+#define HLP_MAIN_KEY 0
+#define HLP_SUBEXIT_KEY 1
+#define HLP_EXIT_KEY 2
+#define HLP_VIEW_HANDLE 3
+#define HLP_PREV_HANDLE 4
+#define HLP_NEXT_HANDLE 5
+#define HLP_ALL_KEY 9
+#define TIMESTAMP_KEY 4
+#define DEBUG_KEY 5
+#define LM_TRY_KEY 3
+#define LM_PREV_KEY 4
+#define LM_NEXT_KEY 5
+#define BACK_KEY 2
+#define PREVM_KEY 4
+#define NEXTM_KEY 5
+#define EXCLUDE_KEY 26
+#define UNEXCLUDE_KEY 27
+#define SELECT_KEY 28
+#define APPLY_KEY 29
+#define VIEW_FULL_HEADERS_KEY 32
+#define BOUNCE_KEY 33
+#define FLAG_KEY 34
+#define VIEW_PIPE_KEY 35
+#define SELCUR_KEY 38
+#define ZOOM_KEY 39
+#define COLLAPSE_KEY 45
+#define ATT_PARENT_KEY 2
+#define ATT_EXPORT_KEY 11
+#define ATT_PIPE_KEY 16
+#define ATT_BOUNCE_KEY 17
+#define ATT_PRINT_KEY 20
+#define ATT_REPLY_KEY 22
+#define ATT_FORWARD_KEY 23
+#define ATV_BACK_KEY 2
+#define ATV_VIEW_HILITE 3
+#define ATV_PREV_URL 4
+#define ATV_NEXT_URL 5
+#define ATV_EXPORT_KEY 11
+#define ATV_PIPE_KEY 16
+#define ATV_BOUNCE_KEY 17
+#define ATV_PRINT_KEY 20
+#define ATV_REPLY_KEY 22
+#define ATV_FORWARD_KEY 23
+#define VIEW_ATT_KEY 3
+#define VIEW_FULL_HEADERS_KEY 32
+#define VIEW_VIEW_HANDLE 26
+#define VIEW_SELECT_KEY 27
+#define VIEW_PREV_HANDLE 28
+#define VIEW_NEXT_HANDLE 29
+#define OE_HELP_KEY 0
+#define OE_CANCEL_KEY 1
+#define OE_CTRL_T_KEY 2
+#define OE_ENTER_KEY 3
+#define SETUP_PRINTER 3
+#define SETUP_PASSWD 4
+#define SETUP_CONFIG 5
+#define SETUP_SIG 6
+#define SETUP_DIRECTORY 10
+#define SETUP_EXCEPT 14
+#define SETUP_SMIME 16
+#define MAIN_HELP_KEY 0
+#define MAIN_DEFAULT_KEY 3
+#define MAIN_KBLOCK_KEY 9
+#define MAIN_QUIT_KEY 14
+#define MAIN_COMPOSE_KEY 15
+#define MAIN_FOLDER_KEY 16
+#define MAIN_INDEX_KEY 18
+#define MAIN_SETUP_KEY 20
+#define MAIN_ADDRESS_KEY 21
+#define NUOV_EXIT 2
+#define NUOV_VIEW 3
+#define NUOV_NEXT_PG 6
+#define NUOV_PREV_PG 7
+#define NUOV_RELNOTES 10
+#define DEFAULT_KEY 3
+#define CHANGEDEF_KEY 10
+#define SMIME_PARENT_KEY 2
+#define DECRYPT_KEY (VIEW_PIPE_KEY + 7)
+#define SECURITY_KEY (DECRYPT_KEY + 1)
+
+
+extern struct key_menu cancel_keymenu,
+ ab_keymenu,
+ abook_select_km,
+ abook_view_keymenu,
+ abook_text_km,
+ ldap_view_keymenu,
+ c_mgr_km,
+ c_cfg_km,
+ c_sel_km,
+ c_fcc_km,
+ pine_quota_keymenu,
+ folder_km,
+ folder_sel_km,
+ folder_sela_km,
+ folder_sub_km,
+ folder_post_km,
+ help_keymenu,
+ rev_msg_keymenu,
+ ans_certfail_keymenu,
+ ans_certquery_keymenu,
+ forge_keymenu,
+ listmgr_keymenu,
+ index_keymenu,
+ simple_index_keymenu,
+ thread_keymenu,
+ att_index_keymenu,
+ att_view_keymenu,
+ view_keymenu,
+ simple_text_keymenu,
+ oe_keymenu,
+ choose_setup_keymenu,
+ main_keymenu,
+ simple_file_keymenu,
+ nuov_keymenu,
+ modal_message_keymenu,
+ ta_keymenu_lm,
+ ta_keymenu_sm,
+ pipe_cancel_keymenu,
+ color_pattern_keymenu,
+ hdr_color_checkbox_keymenu,
+ kw_color_checkbox_keymenu,
+ selectable_bold_checkbox_keymenu,
+ flag_keymenu,
+ addr_s_km,
+ addr_s_km_with_goback,
+ addr_s_km_with_view,
+ addr_s_km_for_url,
+ addr_s_km_exit,
+ addr_s_km_goback,
+ dir_conf_km,
+ sel_from_list,
+ sel_from_list_ctrlc,
+ sel_from_list_sm,
+ sel_from_list_sm_ctrlc,
+ sel_from_list_lm,
+ sel_from_list_lm_ctrlc,
+ sel_from_list_olm,
+ sel_from_list_olm_ctrlc,
+ printer_edit_keymenu,
+ printer_select_keymenu,
+ role_select_km,
+ role_conf_km,
+ config_text_wshuf_keymenu,
+ config_text_wshufandfldr_keymenu,
+ config_role_file_keymenu,
+ config_role_file_res_keymenu,
+ config_role_keyword_keymenu,
+ config_role_keyword_keymenu_not,
+ config_role_charset_keymenu_not,
+ config_role_keymenu,
+ config_role_keymenu_not,
+ config_role_keymenu_extra,
+ config_role_addr_pat_keymenu,
+ config_role_xtrahdr_keymenu,
+ config_role_addr_act_keymenu,
+ config_role_patfolder_keymenu,
+ config_role_actionfolder_keymenu,
+ config_role_inick_keymenu,
+ config_role_afrom_keymenu,
+ config_checkbox_keymenu,
+ config_text_keymenu,
+ config_text_to_charsets_keymenu,
+ config_radiobutton_keymenu,
+ config_yesno_keymenu,
+ color_changing_keymenu,
+ custom_color_changing_keymenu,
+ kw_color_changing_keymenu,
+ color_rgb_keymenu,
+ custom_rgb_keymenu,
+ kw_rgb_keymenu,
+ color_setting_keymenu,
+ custom_color_setting_keymenu,
+ role_color_setting_keymenu,
+ kw_color_setting_keymenu,
+ take_export_keymenu_sm,
+ take_export_keymenu_lm,
+ config_smime_helper_keymenu,
+ smime_info_keymenu;
+
+extern struct key rev_msg_keys[];
+
+
+/* exported protoypes */
+void draw_cancel_keymenu(void);
+void end_keymenu(void);
+int menu_command(UCS, struct key_menu *);
+void menu_init_binding(struct key_menu *, UCS, int, char *, char *, int);
+void menu_add_binding(struct key_menu *, UCS, int);
+int menu_clear_binding(struct key_menu *, UCS);
+int menu_binding_index(struct key_menu *, int);
+void mark_keymenu_dirty(void);
+void blank_keymenu(int, int);
+void draw_keymenu(struct key_menu *, bitmap_t, int, int, int, OtherMenu);
+void redraw_keymenu(void);
+void clearfooter(struct pine *);
+
+
+#endif /* PINE_KEYMENU_INCLUDED */
diff --git a/alpine/ldap32.dll b/alpine/ldap32.dll
new file mode 100755
index 00000000..2284f9f8
--- /dev/null
+++ b/alpine/ldap32.dll
Binary files differ
diff --git a/alpine/ldapconf.c b/alpine/ldapconf.c
new file mode 100644
index 00000000..714bf0cd
--- /dev/null
+++ b/alpine/ldapconf.c
@@ -0,0 +1,2432 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: ldapconf.c 1012 2008-03-26 00:44:22Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "headers.h"
+#include "ldapconf.h"
+#include "keymenu.h"
+#include "radio.h"
+#include "status.h"
+#include "confscroll.h"
+#include "adrbkcmd.h"
+#include "titlebar.h"
+#include "takeaddr.h"
+#include "../pith/ldap.h"
+#include "../pith/state.h"
+#include "../pith/bitmap.h"
+#include "../pith/mailcmd.h"
+#include "../pith/list.h"
+
+
+/*
+ * Internal prototypes
+ */
+#ifdef ENABLE_LDAP
+int addr_select_tool(struct pine *, int, CONF_S **, unsigned);
+void dir_init_display(struct pine *, CONF_S **, char **, struct variable *, CONF_S **);
+int dir_config_tool(struct pine *, int, CONF_S **, unsigned);
+void dir_config_add(struct pine *, CONF_S **);
+void dir_config_shuffle(struct pine *, CONF_S **);
+void dir_config_edit(struct pine *, CONF_S **);
+int dir_edit_screen(struct pine *, LDAP_SERV_S *, char *, char **);
+int dir_edit_tool(struct pine *, int, CONF_S **, unsigned);
+void dir_config_del(struct pine *, CONF_S **);
+void add_ldap_fake_first_server(struct pine *, CONF_S **, struct variable *,
+ struct key_menu *, HelpType,
+ int (*)(struct pine *, int, CONF_S **, unsigned));
+void add_ldap_server_to_display(struct pine *, CONF_S **, char *, char *,
+ struct variable *, int, struct key_menu *, HelpType,
+ int (*)(struct pine *, int, CONF_S **, unsigned),
+ int, CONF_S **);
+int ldap_checkbox_tool(struct pine *, int, CONF_S **, unsigned);
+void toggle_ldap_option_bit(struct pine *, int, struct variable *, char *);
+NAMEVAL_S *ldap_feature_list(int);
+
+
+static char *srch_res_help_title = N_("HELP FOR SEARCH RESULTS INDEX");
+static char *set_choose = "--- ----------------------";
+#define ADD_FIRST_LDAP_SERVER _("Use Add to add a directory server")
+#define ADDR_SELECT_EXIT_VAL 5
+#define ADDR_SELECT_GOBACK_VAL 6
+#define ADDR_SELECT_FORCED_EXIT_VAL 7
+
+
+static int some_selectable;
+static char *dserv = N_("Directory Server on ");
+
+/*
+ * Let user choose an ldap entry (or return an entry if user doesn't need
+ * to be consulted).
+ *
+ * Returns 0 if ok,
+ * -1 if Exit was chosen
+ * -2 if none were selectable
+ * -3 if no entries existed at all
+ * -4 go back to Abook List was chosen
+ * -5 caller shouldn't free ac->res_head
+ *
+ * When 0 is returned the winner is pointed to by result.
+ * Result is an allocated LDAP_SEARCH_WINNER_S which has pointers
+ * to the ld and entry that were chosen. Those are pointers into
+ * the initial data, not copies. The two-pointer structure is
+ * allocated here and freed by the caller.
+ */
+int
+ldap_addr_select(struct pine *ps, ADDR_CHOOSE_S *ac, LDAP_CHOOSE_S **result,
+ LDAPLookupStyle style, WP_ERR_S *wp_err, char *srchstr)
+{
+ LDAPMessage *e;
+ LDAP_SERV_RES_S *res_list;
+ CONF_S *ctmpa = NULL, *first_line = NULL, *alt_first_line = NULL;
+ int i, retval = 0, got_n_mail = 0, got_n_entries = 0;
+ int need_mail;
+ OPT_SCREEN_S screen;
+ struct key_menu *km;
+ char ee[200];
+ HelpType help;
+ void (*prev_redrawer) (void);
+
+ dprint((4, "ldap_addr_select()\n"));
+
+ need_mail = (style == AlwaysDisplay || style == DisplayForURL) ? 0 : 1;
+ if(style == AlwaysDisplay){
+ km = &addr_s_km_with_view;
+ help = h_address_display;
+ }
+ else if(style == AlwaysDisplayAndMailRequired){
+ km = &addr_s_km_with_goback;
+ help = h_address_select;
+ }
+ else if(style == DisplayForURL){
+ km = &addr_s_km_for_url;
+ help = h_address_display;
+ }
+ else{
+ km = &addr_s_km;
+ help = h_address_select;
+ }
+
+ if(result)
+ *result = NULL;
+
+ some_selectable = 0;
+
+ for(res_list = ac->res_head; res_list; res_list = res_list->next){
+ for(e = ldap_first_entry(res_list->ld, res_list->res);
+ e != NULL;
+ e = ldap_next_entry(res_list->ld, e)){
+ char *dn, *a;
+ char **cn, **org, **unit, **title, **mail, **sn;
+ BerElement *ber;
+ int indent, have_mail;
+
+ dn = NULL;
+ cn = org = title = unit = mail = sn = NULL;
+ for(a = ldap_first_attribute(res_list->ld, e, &ber);
+ a != NULL;
+ a = ldap_next_attribute(res_list->ld, e, ber)){
+
+ dprint((9, " %s", a ? a : "?"));
+ if(strcmp(a, res_list->info_used->cnattr) == 0){
+ if(!cn)
+ cn = ldap_get_values(res_list->ld, e, a);
+
+ if(cn && !(cn[0] && cn[0][0])){
+ ldap_value_free(cn);
+ cn = NULL;
+ }
+ }
+ else if(strcmp(a, res_list->info_used->mailattr) == 0){
+ if(!mail)
+ mail = ldap_get_values(res_list->ld, e, a);
+ }
+ else if(strcmp(a, "o") == 0){
+ if(!org)
+ org = ldap_get_values(res_list->ld, e, a);
+ }
+ else if(strcmp(a, "ou") == 0){
+ if(!unit)
+ unit = ldap_get_values(res_list->ld, e, a);
+ }
+ else if(strcmp(a, "title") == 0){
+ if(!title)
+ title = ldap_get_values(res_list->ld, e, a);
+ }
+
+ our_ldap_memfree(a);
+ }
+
+ dprint((9, "\n"));
+
+ if(!cn){
+ for(a = ldap_first_attribute(res_list->ld, e, &ber);
+ a != NULL;
+ a = ldap_next_attribute(res_list->ld, e, ber)){
+
+ if(strcmp(a, res_list->info_used->snattr) == 0){
+ if(!sn)
+ sn = ldap_get_values(res_list->ld, e, a);
+
+ if(sn && !(sn[0] && sn[0][0])){
+ ldap_value_free(sn);
+ sn = NULL;
+ }
+ }
+
+ our_ldap_memfree(a);
+ }
+ }
+
+ if(mail && mail[0] && mail[0][0])
+ have_mail = 1;
+ else
+ have_mail = 0;
+
+ got_n_mail += have_mail;
+ got_n_entries++;
+ indent = 2;
+
+ /*
+ * First line is either cn, sn, or dn.
+ */
+ if(cn){
+ new_confline(&ctmpa);
+ if(!alt_first_line)
+ alt_first_line = ctmpa;
+
+ ctmpa->flags |= CF_STARTITEM;
+ if(need_mail && !have_mail)
+ ctmpa->flags |= CF_PRIVATE;
+
+ ctmpa->value = cpystr(cn[0]);
+ ldap_value_free(cn);
+ ctmpa->valoffset = indent;
+ ctmpa->keymenu = km;
+ ctmpa->help = help;
+ ctmpa->help_title = _(srch_res_help_title);
+ ctmpa->tool = addr_select_tool;
+ ctmpa->d.a.ld = res_list->ld;
+ ctmpa->d.a.entry = e;
+ ctmpa->d.a.info_used = res_list->info_used;
+ ctmpa->d.a.serv = res_list->serv;
+ ctmpa->d.a.ac = ac;
+ if(!first_line && (have_mail || !need_mail))
+ first_line = ctmpa;
+ }
+
+ /* only happens if no cn */
+ if(sn){
+ new_confline(&ctmpa);
+ if(!alt_first_line)
+ alt_first_line = ctmpa;
+
+ ctmpa->flags |= CF_STARTITEM;
+ if(need_mail && !have_mail)
+ ctmpa->flags |= CF_PRIVATE;
+
+ ctmpa->value = cpystr(sn[0]);
+ ldap_value_free(sn);
+ ctmpa->valoffset = indent;
+ ctmpa->keymenu = km;
+ ctmpa->help = help;
+ ctmpa->help_title = _(srch_res_help_title);
+ ctmpa->tool = addr_select_tool;
+ ctmpa->d.a.ld = res_list->ld;
+ ctmpa->d.a.entry = e;
+ ctmpa->d.a.info_used = res_list->info_used;
+ ctmpa->d.a.serv = res_list->serv;
+ ctmpa->d.a.ac = ac;
+ if(!first_line && (have_mail || !need_mail))
+ first_line = ctmpa;
+ }
+
+ if(!sn && !cn){
+ new_confline(&ctmpa);
+ if(!alt_first_line)
+ alt_first_line = ctmpa;
+
+ ctmpa->flags |= CF_STARTITEM;
+ if(need_mail && !have_mail)
+ ctmpa->flags |= CF_PRIVATE;
+
+ dn = ldap_get_dn(res_list->ld, e);
+
+ if(dn && !dn[0]){
+ our_ldap_dn_memfree(dn);
+ dn = NULL;
+ }
+
+ ctmpa->value = cpystr(dn ? dn : "?");
+ if(dn)
+ our_ldap_dn_memfree(dn);
+
+ ctmpa->valoffset = indent;
+ ctmpa->keymenu = km;
+ ctmpa->help = help;
+ ctmpa->help_title = _(srch_res_help_title);
+ ctmpa->tool = addr_select_tool;
+ ctmpa->d.a.ld = res_list->ld;
+ ctmpa->d.a.entry = e;
+ ctmpa->d.a.info_used = res_list->info_used;
+ ctmpa->d.a.serv = res_list->serv;
+ ctmpa->d.a.ac = ac;
+ if(!first_line && (have_mail || !need_mail))
+ first_line = ctmpa;
+ }
+
+ if(title){
+ for(i = 0; title[i] && title[i][0]; i++){
+ new_confline(&ctmpa);
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->valoffset = indent + 2;
+ ctmpa->value = cpystr(title[i]);
+ ctmpa->keymenu = km;
+ ctmpa->help = help;
+ ctmpa->help_title = _(srch_res_help_title);
+ ctmpa->tool = addr_select_tool;
+ ctmpa->d.a.ld = res_list->ld;
+ ctmpa->d.a.entry = e;
+ ctmpa->d.a.info_used = res_list->info_used;
+ ctmpa->d.a.serv = res_list->serv;
+ ctmpa->d.a.ac = ac;
+ }
+
+ ldap_value_free(title);
+ }
+
+ if(unit){
+ for(i = 0; unit[i] && unit[i][0]; i++){
+ new_confline(&ctmpa);
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->valoffset = indent + 2;
+ ctmpa->value = cpystr(unit[i]);
+ ctmpa->keymenu = km;
+ ctmpa->help = help;
+ ctmpa->help_title = _(srch_res_help_title);
+ ctmpa->tool = addr_select_tool;
+ ctmpa->d.a.ld = res_list->ld;
+ ctmpa->d.a.entry = e;
+ ctmpa->d.a.info_used = res_list->info_used;
+ ctmpa->d.a.serv = res_list->serv;
+ ctmpa->d.a.ac = ac;
+ }
+
+ ldap_value_free(unit);
+ }
+
+ if(org){
+ for(i = 0; org[i] && org[i][0]; i++){
+ new_confline(&ctmpa);
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->valoffset = indent + 2;
+ ctmpa->value = cpystr(org[i]);
+ ctmpa->keymenu = km;
+ ctmpa->help = help;
+ ctmpa->help_title = _(srch_res_help_title);
+ ctmpa->tool = addr_select_tool;
+ ctmpa->d.a.ld = res_list->ld;
+ ctmpa->d.a.entry = e;
+ ctmpa->d.a.info_used = res_list->info_used;
+ ctmpa->d.a.serv = res_list->serv;
+ ctmpa->d.a.ac = ac;
+ }
+
+ ldap_value_free(org);
+ }
+
+ if(have_mail){
+ /* Don't show long list of email addresses. */
+ if(!(mail[0] && mail[0][0]) ||
+ !(mail[1] && mail[1][0]) ||
+ !(mail[2] && mail[2][0]) ||
+ !(mail[3] && mail[3][0])){
+ for(i = 0; mail[i] && mail[i][0]; i++){
+ new_confline(&ctmpa);
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->valoffset = indent + 2;
+ ctmpa->value = cpystr(mail[i]);
+ ctmpa->keymenu = km;
+ ctmpa->help = help;
+ ctmpa->help_title = _(srch_res_help_title);
+ ctmpa->tool = addr_select_tool;
+ ctmpa->d.a.ld = res_list->ld;
+ ctmpa->d.a.entry = e;
+ ctmpa->d.a.info_used = res_list->info_used;
+ ctmpa->d.a.serv = res_list->serv;
+ ctmpa->d.a.ac = ac;
+ }
+ }
+ else{
+ char tmp[200];
+
+ for(i = 4; mail[i] && mail[i][0]; i++)
+ ;
+
+ new_confline(&ctmpa);
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->valoffset = indent + 2;
+ snprintf(tmp, sizeof(tmp), _("(%d email addresses)"), i);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmpa->value = cpystr(tmp);
+ ctmpa->keymenu = km;
+ ctmpa->help = help;
+ ctmpa->help_title = _(srch_res_help_title);
+ ctmpa->tool = addr_select_tool;
+ ctmpa->d.a.ld = res_list->ld;
+ ctmpa->d.a.entry = e;
+ ctmpa->d.a.info_used = res_list->info_used;
+ ctmpa->d.a.serv = res_list->serv;
+ ctmpa->d.a.ac = ac;
+ }
+ }
+ else{
+ new_confline(&ctmpa);
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->valoffset = indent + 2;
+ ctmpa->value = cpystr(_("<No Email Address Available>"));
+ ctmpa->keymenu = km;
+ ctmpa->help = help;
+ ctmpa->help_title = _(srch_res_help_title);
+ ctmpa->tool = addr_select_tool;
+ ctmpa->d.a.ld = res_list->ld;
+ ctmpa->d.a.entry = e;
+ ctmpa->d.a.info_used = res_list->info_used;
+ ctmpa->d.a.serv = res_list->serv;
+ ctmpa->d.a.ac = ac;
+ }
+
+ if(mail)
+ ldap_value_free(mail);
+
+ new_confline(&ctmpa); /* blank line */
+ ctmpa->keymenu = km;
+ ctmpa->help = help;
+ ctmpa->help_title = _(srch_res_help_title);
+ ctmpa->tool = addr_select_tool;
+ ctmpa->flags |= CF_NOSELECT | CF_B_LINE;
+ }
+ }
+
+ if(first_line)
+ some_selectable++;
+ else if(alt_first_line)
+ first_line = alt_first_line;
+ else{
+ new_confline(&ctmpa); /* blank line */
+ ctmpa->keymenu = need_mail ? &addr_s_km_exit : &addr_s_km_goback;
+ ctmpa->help = help;
+ ctmpa->help_title = _(srch_res_help_title);
+ ctmpa->tool = addr_select_tool;
+ ctmpa->flags |= CF_NOSELECT | CF_B_LINE;
+
+ new_confline(&ctmpa);
+ first_line = ctmpa;
+ strncpy(ee, "[ ", sizeof(ee));
+ ee[sizeof(ee)-1] = '\0';
+ if(wp_err && wp_err->ldap_errno)
+ /* TRANSLATORS: No matches returned for an LDAP search */
+ snprintf(ee+2, sizeof(ee)-2, _("%s, No Matches Returned"),
+ ldap_err2string(wp_err->ldap_errno));
+ else
+ strncpy(ee+2, _("No Matches"), sizeof(ee)-2);
+
+ ee[sizeof(ee)-1] = '\0';
+
+ /* TRANSLATORS: a request for user to choose Exit after they read text */
+ strncat(ee, _(" -- Choose Exit ]"), sizeof(ee)-strlen(ee)-1);
+ ee[sizeof(ee)-1] = '\0';
+ ctmpa->value = cpystr(ee);
+ ctmpa->valoffset = 10;
+ ctmpa->keymenu = need_mail ? &addr_s_km_exit : &addr_s_km_goback;
+ ctmpa->help = help;
+ ctmpa->help_title = _(srch_res_help_title);
+ ctmpa->tool = addr_select_tool;
+ ctmpa->flags |= CF_NOSELECT;
+ }
+
+ if(style == AlwaysDisplay || style == DisplayForURL ||
+ style == AlwaysDisplayAndMailRequired ||
+ (style == DisplayIfOne && got_n_mail >= 1) ||
+ (style == DisplayIfTwo && got_n_mail >= 1 && got_n_entries >= 2)){
+ if(wp_err && wp_err->mangled)
+ *wp_err->mangled = 1;
+
+ prev_redrawer = ps_global->redrawer;
+ push_titlebar_state();
+
+ memset(&screen, 0, sizeof(screen));
+ /* TRANSLATORS: Print something1 using something2.
+ "this" is something1 */
+ switch(conf_scroll_screen(ps,&screen,first_line,ac->title,_("this"),0)){
+ case ADDR_SELECT_EXIT_VAL:
+ retval = -1;
+ break;
+
+ case ADDR_SELECT_GOBACK_VAL:
+ retval = -4;
+ break;
+
+ case ADDR_SELECT_FORCED_EXIT_VAL:
+ if(alt_first_line) /* some entries, but none suitable */
+ retval = -2;
+ else
+ retval = -3;
+
+ break;
+
+ default:
+ retval = 0;
+ break;
+ }
+
+ ClearScreen();
+ pop_titlebar_state();
+ redraw_titlebar();
+ if((ps_global->redrawer = prev_redrawer) != NULL)
+ (*ps_global->redrawer)();
+
+ if(result && retval == 0 && ac->selected_ld && ac->selected_entry){
+ (*result) = (LDAP_CHOOSE_S *)fs_get(sizeof(LDAP_CHOOSE_S));
+ (*result)->ld = ac->selected_ld;
+ (*result)->selected_entry = ac->selected_entry;
+ (*result)->info_used = ac->info_used;
+ (*result)->serv = ac->selected_serv;
+ }
+ }
+ else if(style == DisplayIfOne && got_n_mail < 1){
+ if(alt_first_line) /* some entries, but none suitable */
+ retval = -2;
+ else
+ retval = -3;
+
+ first_line = first_confline(ctmpa);
+ free_conflines(&first_line);
+ }
+ else if(style == DisplayIfTwo && (got_n_mail < 1 || got_n_entries < 2)){
+ if(got_n_mail < 1){
+ if(alt_first_line) /* some entries, but none suitable */
+ retval = -2;
+ else
+ retval = -3;
+ }
+ else{
+ retval = 0;
+ if(result){
+ (*result) = (LDAP_CHOOSE_S *)fs_get(sizeof(LDAP_CHOOSE_S));
+ (*result)->ld = first_line->d.a.ld;
+ (*result)->selected_entry = first_line->d.a.entry;
+ (*result)->info_used = first_line->d.a.info_used;
+ (*result)->serv = first_line->d.a.serv;
+ }
+ }
+
+ first_line = first_confline(ctmpa);
+ free_conflines(&first_line);
+ }
+
+ return(retval);
+}
+
+
+int
+addr_select_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ int retval = 0;
+
+ switch(cmd){
+ case MC_CHOICE :
+ if(flags & CF_PRIVATE){
+ q_status_message(SM_ORDER | SM_DING, 0, 3,
+ _("No email address available for this entry; choose another or ExitSelect"));
+ }
+ else if(some_selectable){
+ (*cl)->d.a.ac->selected_ld = (*cl)->d.a.ld;
+ (*cl)->d.a.ac->selected_entry = (*cl)->d.a.entry;
+ (*cl)->d.a.ac->info_used = (*cl)->d.a.info_used;
+ (*cl)->d.a.ac->selected_serv = (*cl)->d.a.serv;
+ retval = simple_exit_cmd(flags);
+ }
+ else
+ retval = ADDR_SELECT_FORCED_EXIT_VAL;
+
+ break;
+
+ case MC_VIEW_TEXT :
+ case MC_SAVE :
+ case MC_FWDTEXT :
+ case MC_COMPOSE :
+ case MC_ROLE :
+ {LDAP_CHOOSE_S *e;
+
+ if((*cl)->d.a.ld && (*cl)->d.a.entry){
+ e = (LDAP_CHOOSE_S *)fs_get(sizeof(LDAP_CHOOSE_S));
+ e->ld = (*cl)->d.a.ld;
+ e->selected_entry = (*cl)->d.a.entry;
+ e->info_used = (*cl)->d.a.info_used;
+ e->serv = (*cl)->d.a.serv;
+ if(cmd == MC_VIEW_TEXT)
+ view_ldap_entry(ps, e);
+ else if(cmd == MC_SAVE)
+ save_ldap_entry(ps, e, 0);
+ else if(cmd == MC_COMPOSE)
+ compose_to_ldap_entry(ps, e, 0);
+ else if(cmd == MC_ROLE)
+ compose_to_ldap_entry(ps, e, 1);
+ else
+ forward_ldap_entry(ps, e);
+
+ fs_give((void **)&e);
+ }
+ }
+
+ break;
+
+ case MC_ADDRBOOK :
+ retval = ADDR_SELECT_GOBACK_VAL;
+ break;
+
+ case MC_EXIT :
+ retval = ADDR_SELECT_EXIT_VAL;
+ break;
+
+ default:
+ retval = -1;
+ break;
+ }
+
+ if(retval > 0)
+ ps->mangled_body = 1;
+
+ return(retval);
+}
+
+
+void
+dir_init_display(struct pine *ps, CONF_S **ctmp, char **servers,
+ struct variable *var, CONF_S **first_line)
+{
+ int i;
+ char *serv;
+ char *subtitle;
+ LDAP_SERV_S *info;
+
+ if(first_line)
+ *first_line = NULL;
+
+ if(servers && servers[0] && servers[0][0]){
+ for(i = 0; servers[i]; i++){
+ info = break_up_ldap_server(servers[i]);
+ serv = (info && info->nick && *info->nick) ? cpystr(info->nick) :
+ (info && info->serv && *info->serv) ? cpystr(info->serv) :
+ cpystr(_("Bad Server Config, Delete this"));
+ subtitle = (char *)fs_get((((info && info->serv && *info->serv)
+ ? strlen(info->serv)
+ : 3) +
+ strlen(_(dserv)) + 15) *
+ sizeof(char));
+ if(info && info->port >= 0)
+ snprintf(subtitle, sizeof(subtitle), "%s%s:%d",
+ _(dserv),
+ (info && info->serv && *info->serv) ? info->serv : "<?>",
+ info->port);
+ else
+ snprintf(subtitle, sizeof(subtitle), "%s%s",
+ _(dserv),
+ (info && info->serv && *info->serv) ? info->serv : "<?>");
+
+ subtitle[sizeof(subtitle)-1] = '\0';
+
+ add_ldap_server_to_display(ps, ctmp, serv, subtitle, var,
+ i, &dir_conf_km, h_direct_config,
+ dir_config_tool, 0,
+ (first_line && *first_line == NULL)
+ ? first_line
+ : NULL);
+
+ free_ldap_server_info(&info);
+ }
+ }
+ else{
+ add_ldap_fake_first_server(ps, ctmp, var,
+ &dir_conf_km, h_direct_config,
+ dir_config_tool);
+ if(first_line)
+ *first_line = *ctmp;
+ }
+}
+
+
+void
+directory_config(struct pine *ps, int edit_exceptions)
+{
+ CONF_S *ctmp = NULL, *first_line = NULL;
+ OPT_SCREEN_S screen;
+ int no_ex, readonly_warning = 0;
+
+ if(edit_exceptions){
+ q_status_message(SM_ORDER, 3, 7,
+ _("Exception Setup not implemented for directory"));
+ return;
+ }
+
+ ew = edit_exceptions ? ps_global->ew_for_except_vars : Main;
+
+ no_ex = (ps_global->ew_for_except_vars == Main);
+
+ if(ps->restricted)
+ readonly_warning = 1;
+ else{
+ PINERC_S *prc = NULL;
+
+ switch(ew){
+ case Main:
+ prc = ps->prc;
+ break;
+ case Post:
+ prc = ps->post_prc;
+ break;
+ default:
+ break;
+ }
+
+ readonly_warning = prc ? prc->readonly : 1;
+ if(prc && prc->quit_to_edit){
+ quit_to_edit_msg(prc);
+ return;
+ }
+ }
+
+ if(ps->fix_fixed_warning)
+ offer_to_fix_pinerc(ps);
+
+ dir_init_display(ps, &ctmp, no_ex ? ps->VAR_LDAP_SERVERS
+ : LVAL(&ps->vars[V_LDAP_SERVERS], ew),
+ &ps->vars[V_LDAP_SERVERS], &first_line);
+
+ memset(&screen, 0, sizeof(screen));
+ screen.deferred_ro_warning = readonly_warning;
+ /* TRANSLATORS: Print something1 using something2.
+ "servers" is something1 */
+ (void)conf_scroll_screen(ps, &screen, first_line,
+ _("SETUP DIRECTORY SERVERS"), _("servers"), 0);
+ ps->mangled_screen = 1;
+}
+
+
+int
+dir_config_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ int first_one, rv = 0;
+
+ first_one = (*cl)->value &&
+ (strcmp((*cl)->value, ADD_FIRST_LDAP_SERVER) == 0);
+ switch(cmd){
+ case MC_DELETE :
+ if(first_one)
+ q_status_message(SM_ORDER|SM_DING, 0, 3,
+ _("Nothing to Delete, use Add"));
+ else
+ dir_config_del(ps, cl);
+
+ break;
+
+ case MC_ADD :
+ if(!fixed_var((*cl)->var, NULL, "directory list"))
+ dir_config_add(ps, cl);
+
+ break;
+
+ case MC_EDIT :
+ if(!fixed_var((*cl)->var, NULL, "directory list")){
+ if(first_one)
+ dir_config_add(ps, cl);
+ else
+ dir_config_edit(ps, cl);
+ }
+
+ break;
+
+ case MC_SHUFFLE :
+ if(!fixed_var((*cl)->var, NULL, "directory list")){
+ if(first_one)
+ q_status_message(SM_ORDER|SM_DING, 0, 3,
+ _("Nothing to Shuffle, use Add"));
+ else
+ dir_config_shuffle(ps, cl);
+ }
+
+ break;
+
+ case MC_EXIT :
+ rv = 2;
+ break;
+
+ default:
+ rv = -1;
+ break;
+ }
+
+ return(rv);
+}
+
+
+/*
+ * Add LDAP directory entry
+ */
+void
+dir_config_add(struct pine *ps, CONF_S **cl)
+{
+ char *raw_server = NULL;
+ LDAP_SERV_S *info = NULL;
+ char **lval;
+ int no_ex;
+
+ no_ex = (ps_global->ew_for_except_vars == Main);
+
+ if(dir_edit_screen(ps, NULL, "ADD A", &raw_server) == 1){
+
+ info = break_up_ldap_server(raw_server);
+
+ if(info && info->serv && *info->serv){
+ char *subtitle;
+ int i, cnt = 0;
+ char **new_list;
+ CONF_S *cp;
+
+ lval = no_ex ? (*cl)->var->current_val.l : LVAL((*cl)->var, ew);
+ if(lval)
+ while(lval[cnt])
+ cnt++;
+
+ /* catch the special "" case */
+ if(cnt == 0 ||
+ (cnt == 1 && lval[0][0] == '\0')){
+ new_list = (char **)fs_get((1 + 1) * sizeof(char *));
+ new_list[0] = raw_server;
+ new_list[1] = NULL;
+ }
+ else{
+ /* add one for new value */
+ cnt++;
+ new_list = (char **)fs_get((cnt + 1) * sizeof(char *));
+
+ for(i = 0; i < (*cl)->varmem; i++)
+ new_list[i] = cpystr(lval[i]);
+
+ new_list[(*cl)->varmem] = raw_server;
+
+ for(i = (*cl)->varmem; i < cnt; i++)
+ new_list[i+1] = cpystr(lval[i]);
+ }
+
+ raw_server = NULL;
+ set_variable_list(V_LDAP_SERVERS, new_list, FALSE, ew);
+ free_list_array(&new_list);
+ set_current_val((*cl)->var, TRUE, FALSE);
+ subtitle = (char *)fs_get((((info && info->serv && *info->serv)
+ ? strlen(info->serv)
+ : 3) +
+ strlen(_(dserv)) + 15) *
+ sizeof(char));
+ if(info && info->port >= 0)
+ snprintf(subtitle, sizeof(subtitle), "%s%s:%d",
+ _(dserv),
+ (info && info->serv && *info->serv) ? info->serv : "<?>",
+ info->port);
+ else
+ snprintf(subtitle, sizeof(subtitle), "%s%s",
+ _(dserv),
+ (info && info->serv && *info->serv) ? info->serv : "<?>");
+
+ subtitle[sizeof(subtitle)-1] = '\0';
+
+ if(cnt < 2){ /* first one */
+ struct variable *var;
+ struct key_menu *keymenu;
+ HelpType help;
+ int (*tool)(struct pine *, int, CONF_S **, unsigned);
+
+ var = (*cl)->var;
+ keymenu = (*cl)->keymenu;
+ help = (*cl)->help;
+ tool = (*cl)->tool;
+ *cl = first_confline(*cl);
+ free_conflines(cl);
+ add_ldap_server_to_display(ps, cl,
+ (info && info->nick && *info->nick)
+ ? cpystr(info->nick)
+ : cpystr(info->serv),
+ subtitle, var, 0, keymenu, help,
+ tool, 0, NULL);
+
+ opt_screen->top_line = NULL;
+ }
+ else{
+ /*
+ * Insert new server.
+ */
+ add_ldap_server_to_display(ps, cl,
+ (info && info->nick && *info->nick)
+ ? cpystr(info->nick)
+ : cpystr(info->serv),
+ subtitle,
+ (*cl)->var,
+ (*cl)->varmem,
+ (*cl)->keymenu,
+ (*cl)->help,
+ (*cl)->tool,
+ 1,
+ NULL);
+ /* adjust the rest of the varmems */
+ for(cp = (*cl)->next; cp; cp = cp->next)
+ cp->varmem++;
+ }
+
+ /* because add_ldap advanced cl to its third line */
+ (*cl) = (*cl)->prev->prev;
+
+ fix_side_effects(ps, (*cl)->var, 0);
+ write_pinerc(ps, ew, WRP_NONE);
+ }
+ else
+ q_status_message(SM_ORDER, 0, 3, _("Add cancelled, no server name"));
+ }
+
+ free_ldap_server_info(&info);
+ if(raw_server)
+ fs_give((void **)&raw_server);
+}
+
+
+/*
+ * Shuffle order of LDAP directory entries
+ */
+void
+dir_config_shuffle(struct pine *ps, CONF_S **cl)
+{
+ int cnt, rv, current_num, new_num, i, j, deefault;
+ char **new_list, **lval;
+ char tmp[200];
+ HelpType help;
+ ESCKEY_S opts[3];
+ CONF_S *a, *b;
+ int no_ex;
+
+ no_ex = (ps_global->ew_for_except_vars == Main);
+
+ /* how many are in our current list? */
+ lval = no_ex ? (*cl)->var->current_val.l : LVAL((*cl)->var, ew);
+ for(cnt = 0; lval && lval[cnt]; cnt++)
+ ;
+
+ if(cnt < 2){
+ q_status_message(SM_ORDER, 0, 3,
+ _("Shuffle only makes sense when there is more than one server in list"));
+ return;
+ }
+
+ current_num = (*cl)->varmem; /* variable number of highlighted directory */
+
+ /* Move it up or down? */
+ i = 0;
+ opts[i].ch = 'u';
+ opts[i].rval = 'u';
+ opts[i].name = "U";
+ opts[i++].label = _("Up");
+
+ opts[i].ch = 'd';
+ opts[i].rval = 'd';
+ opts[i].name = "D";
+ opts[i++].label = _("Down");
+
+ opts[i].ch = -1;
+ deefault = 'u';
+
+ if(current_num == 0){ /* no up */
+ opts[0].ch = -2;
+ deefault = 'd';
+ }
+ else if(current_num == cnt - 1) /* no down */
+ opts[1].ch = -2;
+
+ snprintf(tmp, sizeof(tmp), "Shuffle \"%s\" %s%s%s ? ",
+ (*cl)->value,
+ (opts[0].ch != -2) ? _("UP") : "",
+ (opts[0].ch != -2 && opts[1].ch != -2) ? " or " : "",
+ (opts[1].ch != -2) ? _("DOWN") : "");
+ tmp[sizeof(tmp)-1] = '\0';
+ help = (opts[0].ch == -2) ? h_dir_shuf_down
+ : (opts[1].ch == -2) ? h_dir_shuf_up
+ : h_dir_shuf;
+
+ rv = radio_buttons(tmp, -FOOTER_ROWS(ps), opts, deefault, 'x',
+ help, RB_NORM);
+
+ switch(rv){
+ case 'x':
+ cmd_cancelled("Shuffle");
+ return;
+
+ case 'u':
+ new_num = current_num - 1;
+ a = (*cl)->prev->prev->prev;
+ b = *cl;
+ break;
+
+ case 'd':
+ new_num = current_num + 1;
+ a = *cl;
+ b = (*cl)->next->next->next;
+ break;
+ }
+
+ /* allocate space for new list */
+ new_list = (char **)fs_get((cnt + 1) * sizeof(char *));
+
+ /* fill in new_list */
+ for(i = 0; i < cnt; i++){
+ if(i == current_num)
+ j = new_num;
+ else if (i == new_num)
+ j = current_num;
+ else
+ j = i;
+
+ /* notice this works even if we were using default */
+ new_list[i] = cpystr(lval[j]);
+ }
+
+ new_list[i] = NULL;
+
+ j = set_variable_list((*cl)->var - ps->vars, new_list, TRUE, ew);
+ free_list_array(&new_list);
+ if(j){
+ q_status_message(SM_ORDER, 0, 3,
+ _("Shuffle cancelled: couldn't save configuration file"));
+ set_current_val((*cl)->var, TRUE, FALSE);
+ return;
+ }
+
+ set_current_val((*cl)->var, TRUE, FALSE);
+
+ if(a == opt_screen->top_line)
+ opt_screen->top_line = b;
+
+ j = a->varmem;
+ a->varmem = b->varmem;
+ b->varmem = j;
+
+ /*
+ * Swap display lines. To start with, a is lower in list, b is higher.
+ * The fact that there are 3 lines per entry is totally entangled in
+ * the code.
+ */
+ a->next->next->next = b->next->next->next;
+ if(b->next->next->next)
+ b->next->next->next->prev = a->next->next;
+ b->prev = a->prev;
+ if(a->prev)
+ a->prev->next = b;
+ b->next->next->next = a;
+ a->prev = b->next->next;
+
+ ps->mangled_body = 1;
+ write_pinerc(ps, ew, WRP_NONE);
+}
+
+
+/*
+ * Edit LDAP directory entry
+ */
+void
+dir_config_edit(struct pine *ps, CONF_S **cl)
+{
+ char *raw_server = NULL, **lval;
+ LDAP_SERV_S *info;
+ int no_ex;
+
+ no_ex = (ps_global->ew_for_except_vars == Main);
+
+ lval = no_ex ? (*cl)->var->current_val.l : LVAL((*cl)->var, ew);
+ info = break_up_ldap_server((lval && lval[(*cl)->varmem])
+ ? lval[(*cl)->varmem] : NULL);
+
+ if(dir_edit_screen(ps, info, "CHANGE THIS", &raw_server) == 1){
+
+ free_ldap_server_info(&info);
+ info = break_up_ldap_server(raw_server);
+
+ if(lval && lval[(*cl)->varmem] &&
+ strcmp(lval[(*cl)->varmem], raw_server) == 0)
+ q_status_message(SM_ORDER, 0, 3, _("No change, cancelled"));
+ else if(!(info && info->serv && *info->serv))
+ q_status_message(SM_ORDER, 0, 3,
+ _("Change cancelled, use Delete if you want to remove this server"));
+ else{
+ char *subtitle;
+ int i, cnt;
+ char **new_list;
+
+ for(cnt = 0; lval && lval[cnt]; cnt++)
+ ;
+
+ new_list = (char **)fs_get((cnt + 1) * sizeof(char *));
+
+ for(i = 0; i < (*cl)->varmem; i++)
+ new_list[i] = cpystr(lval[i]);
+
+ new_list[(*cl)->varmem] = raw_server;
+ raw_server = NULL;
+
+ for(i = (*cl)->varmem + 1; i < cnt; i++)
+ new_list[i] = cpystr(lval[i]);
+
+ new_list[cnt] = NULL;
+ set_variable_list(V_LDAP_SERVERS, new_list, FALSE, ew);
+ free_list_array(&new_list);
+ set_current_val((*cl)->var, TRUE, FALSE);
+
+ if((*cl)->value)
+ fs_give((void **)&(*cl)->value);
+
+ (*cl)->value = cpystr((info->nick && *info->nick) ? info->nick
+ : info->serv);
+
+ if((*cl)->next->value)
+ fs_give((void **)&(*cl)->next->value);
+
+ subtitle = (char *)fs_get((((info && info->serv && *info->serv)
+ ? strlen(info->serv)
+ : 3) +
+ strlen(_(dserv)) + 15) *
+ sizeof(char));
+ if(info && info->port >= 0)
+ snprintf(subtitle, sizeof(subtitle), "%s%s:%d",
+ _(dserv),
+ (info && info->serv && *info->serv) ? info->serv : "<?>",
+ info->port);
+ else
+ snprintf(subtitle, sizeof(subtitle), "%s%s",
+ _(dserv),
+ (info && info->serv && *info->serv) ? info->serv : "<?>");
+
+ subtitle[sizeof(subtitle)-1] = '\0';
+
+ (*cl)->next->value = subtitle;
+
+ fix_side_effects(ps, (*cl)->var, 0);
+ write_pinerc(ps, ew, WRP_NONE);
+ }
+ }
+
+ free_ldap_server_info(&info);
+ if(raw_server)
+ fs_give((void **)&raw_server);
+}
+
+
+#define LDAP_F_IMPL 0
+#define LDAP_F_RHS 1
+#define LDAP_F_REF 2
+#define LDAP_F_NOSUB 3
+#define LDAP_F_TLS 4
+#define LDAP_F_TLSMUST 5
+bitmap_t ldap_option_list;
+struct variable *ldap_srch_rule_ptr;
+
+/*
+ * Gives user screen to edit config values for ldap server.
+ *
+ * Args ps -- pine struct
+ * def -- default values to start with
+ * title -- part of title at top of screen
+ * raw_server -- This is the returned item, allocated here and freed by caller.
+ *
+ * Returns: 0 if no change
+ * 1 if user requested a change
+ * (change is stored in raw_server and hasn't been acted upon yet)
+ * 10 user says abort
+ */
+int
+dir_edit_screen(struct pine *ps, LDAP_SERV_S *def, char *title, char **raw_server)
+{
+ OPT_SCREEN_S screen, *saved_screen;
+ CONF_S *ctmp = NULL, *ctmpb, *first_line = NULL;
+ char tmp[MAXPATH+1], custom_scope[MAXPATH], **apval;
+ int rv, i, j, lv, indent, rindent;
+ NAMEVAL_S *f;
+ struct variable server_var, base_var, binddn_var, port_var, nick_var,
+ srch_type_var, srch_rule_var, time_var,
+ size_var, mailattr_var, cnattr_var,
+ snattr_var, gnattr_var, cust_var,
+ opt_var, *v, *varlist[21];
+ char *server = NULL, *base = NULL, *port = NULL, *nick = NULL,
+ *srch_type = NULL, *srch_rule = NULL, *ttime = NULL,
+ *c_s_f = "custom-search-filter", *binddn = NULL,
+ *ssize = NULL, *mailattr = NULL, *cnattr = NULL,
+ *snattr = NULL, *gnattr = NULL, *cust = NULL;
+
+ /*
+ * We edit by making a nested call to conf_scroll_screen.
+ * We use some fake struct variables to get back the results in, and
+ * so we can use the existing tools from the config screen.
+ */
+
+ custom_scope[0] = '\0';
+
+ varlist[j = 0] = &server_var;
+ varlist[++j] = &base_var;
+ varlist[++j] = &port_var;
+ varlist[++j] = &binddn_var;
+ varlist[++j] = &nick_var;
+ varlist[++j] = &srch_type_var;
+ varlist[++j] = &srch_rule_var;
+ varlist[++j] = &time_var;
+ varlist[++j] = &size_var;
+ varlist[++j] = &mailattr_var;
+ varlist[++j] = &cnattr_var;
+ varlist[++j] = &snattr_var;
+ varlist[++j] = &gnattr_var;
+ varlist[++j] = &cust_var;
+ varlist[++j] = &opt_var;
+ varlist[++j] = NULL;
+ for(j = 0; varlist[j]; j++)
+ memset(varlist[j], 0, sizeof(struct variable));
+
+ server_var.name = cpystr("ldap-server");
+ server_var.is_used = 1;
+ server_var.is_user = 1;
+ apval = APVAL(&server_var, ew);
+ *apval = (def && def->serv && def->serv[0]) ? cpystr(def->serv) : NULL;
+ set_current_val(&server_var, FALSE, FALSE);
+
+ base_var.name = cpystr("search-base");
+ base_var.is_used = 1;
+ base_var.is_user = 1;
+ apval = APVAL(&base_var, ew);
+ *apval = (def && def->base && def->base[0]) ? cpystr(def->base) : NULL;
+ set_current_val(&base_var, FALSE, FALSE);
+
+ port_var.name = cpystr("port");
+ port_var.is_used = 1;
+ port_var.is_user = 1;
+ if(def && def->port >= 0){
+ apval = APVAL(&port_var, ew);
+ *apval = cpystr(int2string(def->port));
+ }
+
+ port_var.global_val.p = cpystr(int2string(LDAP_PORT));
+ set_current_val(&port_var, FALSE, FALSE);
+
+ binddn_var.name = cpystr("bind-dn");
+ binddn_var.is_used = 1;
+ binddn_var.is_user = 1;
+ apval = APVAL(&binddn_var, ew);
+ *apval = (def && def->binddn && def->binddn[0]) ? cpystr(def->binddn) : NULL;
+ set_current_val(&binddn_var, FALSE, FALSE);
+
+ nick_var.name = cpystr("nickname");
+ nick_var.is_used = 1;
+ nick_var.is_user = 1;
+ apval = APVAL(&nick_var, ew);
+ *apval = (def && def->nick && def->nick[0]) ? cpystr(def->nick) : NULL;
+ set_current_val(&nick_var, FALSE, FALSE);
+
+ srch_type_var.name = cpystr("search-type");
+ srch_type_var.is_used = 1;
+ srch_type_var.is_user = 1;
+ apval = APVAL(&srch_type_var, ew);
+ *apval = (f=ldap_search_types(def ? def->type : -1))
+ ? cpystr(f->name) : NULL;
+ srch_type_var.global_val.p =
+ (f=ldap_search_types(DEF_LDAP_TYPE)) ? cpystr(f->name) : NULL;
+ set_current_val(&srch_type_var, FALSE, FALSE);
+
+ ldap_srch_rule_ptr = &srch_rule_var; /* so radiobuttons can tell */
+ srch_rule_var.name = cpystr("search-rule");
+ srch_rule_var.is_used = 1;
+ srch_rule_var.is_user = 1;
+ apval = APVAL(&srch_rule_var, ew);
+ *apval = (f=ldap_search_rules(def ? def->srch : -1))
+ ? cpystr(f->name) : NULL;
+ srch_rule_var.global_val.p =
+ (f=ldap_search_rules(DEF_LDAP_SRCH)) ? cpystr(f->name) : NULL;
+ set_current_val(&srch_rule_var, FALSE, FALSE);
+
+ time_var.name = cpystr("timelimit");
+ time_var.is_used = 1;
+ time_var.is_user = 1;
+ if(def && def->time >= 0){
+ apval = APVAL(&time_var, ew);
+ *apval = cpystr(int2string(def->time));
+ }
+
+ time_var.global_val.p = cpystr(int2string(DEF_LDAP_TIME));
+ set_current_val(&time_var, FALSE, FALSE);
+
+ size_var.name = cpystr("sizelimit");
+ size_var.is_used = 1;
+ size_var.is_user = 1;
+ if(def && def->size >= 0){
+ apval = APVAL(&size_var, ew);
+ *apval = cpystr(int2string(def->size));
+ }
+
+ size_var.global_val.p = cpystr(int2string(DEF_LDAP_SIZE));
+ set_current_val(&size_var, FALSE, FALSE);
+
+ mailattr_var.name = cpystr("email-attribute");
+ mailattr_var.is_used = 1;
+ mailattr_var.is_user = 1;
+ apval = APVAL(&mailattr_var, ew);
+ *apval = (def && def->mailattr && def->mailattr[0])
+ ? cpystr(def->mailattr) : NULL;
+ mailattr_var.global_val.p = cpystr(DEF_LDAP_MAILATTR);
+ set_current_val(&mailattr_var, FALSE, FALSE);
+
+ cnattr_var.name = cpystr("name-attribute");
+ cnattr_var.is_used = 1;
+ cnattr_var.is_user = 1;
+ apval = APVAL(&cnattr_var, ew);
+ *apval = (def && def->cnattr && def->cnattr[0])
+ ? cpystr(def->cnattr) : NULL;
+ cnattr_var.global_val.p = cpystr(DEF_LDAP_CNATTR);
+ set_current_val(&cnattr_var, FALSE, FALSE);
+
+ snattr_var.name = cpystr("surname-attribute");
+ snattr_var.is_used = 1;
+ snattr_var.is_user = 1;
+ apval = APVAL(&snattr_var, ew);
+ *apval = (def && def->snattr && def->snattr[0])
+ ? cpystr(def->snattr) : NULL;
+ snattr_var.global_val.p = cpystr(DEF_LDAP_SNATTR);
+ set_current_val(&snattr_var, FALSE, FALSE);
+
+ gnattr_var.name = cpystr("givenname-attribute");
+ gnattr_var.is_used = 1;
+ gnattr_var.is_user = 1;
+ apval = APVAL(&gnattr_var, ew);
+ *apval = (def && def->gnattr && def->gnattr[0])
+ ? cpystr(def->gnattr) : NULL;
+ gnattr_var.global_val.p = cpystr(DEF_LDAP_GNATTR);
+ set_current_val(&gnattr_var, FALSE, FALSE);
+
+ cust_var.name = cpystr(c_s_f);
+ cust_var.is_used = 1;
+ cust_var.is_user = 1;
+ apval = APVAL(&cust_var, ew);
+ *apval = (def && def->cust && def->cust[0]) ? cpystr(def->cust) : NULL;
+ set_current_val(&cust_var, FALSE, FALSE);
+
+ /* TRANSLATORS: Features is a section title in the LDAP configuration screen. Following
+ this are a list of features or options that can be turned on or off. */
+ opt_var.name = cpystr(_("Features"));
+ opt_var.is_used = 1;
+ opt_var.is_user = 1;
+ opt_var.is_list = 1;
+ clrbitmap(ldap_option_list);
+ if(def && def->impl)
+ setbitn(LDAP_F_IMPL, ldap_option_list);
+ if(def && def->rhs)
+ setbitn(LDAP_F_RHS, ldap_option_list);
+ if(def && def->ref)
+ setbitn(LDAP_F_REF, ldap_option_list);
+ if(def && def->nosub)
+ setbitn(LDAP_F_NOSUB, ldap_option_list);
+ if(def && def->tls)
+ setbitn(LDAP_F_TLS, ldap_option_list);
+ if(def && def->tlsmust)
+ setbitn(LDAP_F_TLSMUST, ldap_option_list);
+
+ /* save the old opt_screen before calling scroll screen again */
+ saved_screen = opt_screen;
+
+ indent = utf8_width(c_s_f) + 3;
+ rindent = 12;
+
+ /* Server */
+ new_confline(&ctmp);
+ ctmp->help_title= _("HELP FOR LDAP SERVER");
+ ctmp->var = &server_var;
+ ctmp->valoffset = indent;
+ ctmp->keymenu = &config_text_keymenu;
+ ctmp->help = h_config_ldap_server;
+ ctmp->tool = dir_edit_tool;
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.*w =", indent-3,indent-3,server_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmp;
+ ctmp->value = pretty_value(ps, ctmp);
+
+ first_line = ctmp;
+
+ /* Search Base */
+ new_confline(&ctmp);
+ ctmp->help_title= _("HELP FOR SERVER SEARCH BASE");
+ ctmp->var = &base_var;
+ ctmp->valoffset = indent;
+ ctmp->keymenu = &config_text_keymenu;
+ ctmp->help = h_config_ldap_base;
+ ctmp->tool = dir_edit_tool;
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.*w =", indent-3,indent-3,base_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmp;
+ ctmp->value = pretty_value(ps, ctmp);
+
+ /* Port */
+ new_confline(&ctmp);
+ ctmp->help_title= _("HELP FOR PORT NUMBER");
+ ctmp->var = &port_var;
+ ctmp->valoffset = indent;
+ ctmp->keymenu = &config_text_keymenu;
+ ctmp->help = h_config_ldap_port;
+ ctmp->tool = dir_edit_tool;
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.*w =", indent-3,indent-3,port_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmp;
+ ctmp->value = pretty_value(ps, ctmp);
+ ctmp->flags |= CF_NUMBER;
+
+ /* Bind DN (DN to bind to if needed) */
+ new_confline(&ctmp);
+ ctmp->help_title= _("HELP FOR SERVER BIND DN");
+ ctmp->var = &binddn_var;
+ ctmp->valoffset = indent;
+ ctmp->keymenu = &config_text_keymenu;
+ ctmp->help = h_config_ldap_binddn;
+ ctmp->tool = dir_edit_tool;
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.*w =", indent-3,indent-3,binddn_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmp;
+ ctmp->value = pretty_value(ps, ctmp);
+
+ /* Nickname */
+ new_confline(&ctmp);
+ ctmp->help_title= _("HELP FOR SERVER NICKNAME");
+ ctmp->var = &nick_var;
+ ctmp->valoffset = indent;
+ ctmp->keymenu = &config_text_keymenu;
+ ctmp->help = h_config_ldap_nick;
+ ctmp->tool = dir_edit_tool;
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.*w =", indent-3,indent-3,nick_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmp;
+ ctmp->value = pretty_value(ps, ctmp);
+
+ /* Blank line */
+ new_confline(&ctmp);
+ ctmp->flags |= CF_NOSELECT | CF_B_LINE;
+
+ /* Options */
+ new_confline(&ctmp);
+ ctmp->var = &opt_var;
+ ctmp->keymenu = &config_checkbox_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = NULL;
+ snprintf(tmp, sizeof(tmp), "%s =", opt_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmpb = ctmp;
+ ctmp->flags |= (CF_NOSELECT | CF_STARTITEM);
+
+ new_confline(&ctmp);
+ ctmp->var = NULL;
+ ctmp->valoffset = rindent;
+ ctmp->keymenu = &config_checkbox_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = ldap_checkbox_tool;
+ ctmp->varnamep = ctmpb;
+ ctmp->flags |= CF_NOSELECT;
+ ctmp->value = cpystr("Set Feature Name");
+
+ new_confline(&ctmp);
+ ctmp->var = NULL;
+ ctmp->valoffset = rindent;
+ ctmp->keymenu = &config_checkbox_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = ldap_checkbox_tool;
+ ctmp->varnamep = ctmpb;
+ ctmp->flags |= CF_NOSELECT;
+ ctmp->value = cpystr(set_choose);
+
+ /* find longest value's name */
+ for(lv = 0, i = 0; (f = ldap_feature_list(i)); i++)
+ if(lv < (j = utf8_width(f->name)))
+ lv = j;
+
+ lv = MIN(lv, 100);
+
+ for(i = 0; (f = ldap_feature_list(i)); i++){
+ new_confline(&ctmp);
+ ctmp->var = &opt_var;
+ ctmp->help_title= _("HELP FOR LDAP FEATURES");
+ ctmp->varnamep = ctmpb;
+ ctmp->keymenu = &config_checkbox_keymenu;
+ switch(i){
+ case LDAP_F_IMPL:
+ ctmp->help = h_config_ldap_opts_impl;
+ break;
+ case LDAP_F_RHS:
+ ctmp->help = h_config_ldap_opts_rhs;
+ break;
+ case LDAP_F_REF:
+ ctmp->help = h_config_ldap_opts_ref;
+ break;
+ case LDAP_F_NOSUB:
+ ctmp->help = h_config_ldap_opts_nosub;
+ break;
+ case LDAP_F_TLS:
+ ctmp->help = h_config_ldap_opts_tls;
+ break;
+ case LDAP_F_TLSMUST:
+ ctmp->help = h_config_ldap_opts_tlsmust;
+ break;
+ }
+
+ ctmp->tool = ldap_checkbox_tool;
+ ctmp->valoffset = rindent;
+ ctmp->varmem = i;
+ utf8_snprintf(tmp, sizeof(tmp), "[%c] %-*.*w",
+ bitnset(f->value, ldap_option_list) ? 'X' : ' ',
+ lv, lv, f->name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->value = cpystr(tmp);
+ }
+
+ /* Blank line */
+ new_confline(&ctmp);
+ ctmp->flags |= CF_NOSELECT | CF_B_LINE;
+
+ /* Search Type */
+ new_confline(&ctmp);
+ ctmp->var = &srch_type_var;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = NULL;
+ snprintf(tmp, sizeof(tmp), "%s =", srch_type_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmpb = ctmp;
+ ctmp->flags |= (CF_NOSELECT | CF_STARTITEM);
+
+ new_confline(&ctmp);
+ ctmp->var = NULL;
+ ctmp->valoffset = rindent;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = NULL;
+ ctmp->varnamep = ctmpb;
+ ctmp->flags |= CF_NOSELECT;
+ ctmp->value = cpystr("Set Rule Values");
+
+ new_confline(&ctmp);
+ ctmp->var = NULL;
+ ctmp->valoffset = rindent;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = ldap_radiobutton_tool;
+ ctmp->varnamep = ctmpb;
+ ctmp->flags |= CF_NOSELECT;
+ ctmp->value = cpystr(set_choose);
+
+ /* find longest value's name */
+ for(lv = 0, i = 0; (f = ldap_search_types(i)); i++)
+ if(lv < (j = utf8_width(f->name)))
+ lv = j;
+
+ lv = MIN(lv, 100);
+
+ for(i = 0; (f = ldap_search_types(i)); i++){
+ new_confline(&ctmp);
+ ctmp->help_title= _("HELP FOR SEARCH TYPE");
+ ctmp->var = &srch_type_var;
+ ctmp->valoffset = rindent;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = h_config_ldap_searchtypes;
+ ctmp->varmem = i;
+ ctmp->tool = ldap_radiobutton_tool;
+ ctmp->varnamep = ctmpb;
+ utf8_snprintf(tmp, sizeof(tmp), "(%c) %-*.*w", (((!def || def->type == -1) &&
+ f->value == DEF_LDAP_TYPE) ||
+ (def && f->value == def->type))
+ ? R_SELD : ' ',
+ lv, lv, f->name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->value = cpystr(tmp);
+ }
+
+ /* Blank line */
+ new_confline(&ctmp);
+ ctmp->flags |= CF_NOSELECT | CF_B_LINE;
+ ctmp->varname = cpystr("");
+
+ /* Search Rule */
+ new_confline(&ctmp);
+ ctmp->var = &srch_rule_var;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = NULL;
+ snprintf(tmp, sizeof(tmp), "%s =", srch_rule_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmpb = ctmp;
+ ctmp->flags |= (CF_NOSELECT | CF_STARTITEM);
+
+ /* Search Rule */
+ new_confline(&ctmp);
+ ctmp->var = NULL;
+ ctmp->valoffset = rindent;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = NULL;
+ ctmp->varnamep = ctmpb;
+ ctmp->flags |= CF_NOSELECT;
+ ctmp->value = cpystr("Set Rule Values");
+
+ new_confline(&ctmp);
+ ctmp->var = NULL;
+ ctmp->valoffset = rindent;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = ldap_radiobutton_tool;
+ ctmp->varnamep = ctmpb;
+ ctmp->flags |= CF_NOSELECT;
+ ctmp->value = cpystr(set_choose);
+
+ /* find longest value's name */
+ for(lv = 0, i = 0; (f = ldap_search_rules(i)); i++)
+ if(lv < (j = utf8_width(f->name)))
+ lv = j;
+
+ lv = MIN(lv, 100);
+
+ for(i = 0; (f = ldap_search_rules(i)); i++){
+ new_confline(&ctmp);
+ ctmp->help_title= _("HELP FOR SEARCH RULE");
+ ctmp->var = &srch_rule_var;
+ ctmp->valoffset = rindent;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = h_config_ldap_searchrules;
+ ctmp->varmem = i;
+ ctmp->tool = ldap_radiobutton_tool;
+ ctmp->varnamep = ctmpb;
+ utf8_snprintf(tmp, sizeof(tmp), "(%c) %-*.*w", (((!def || def->srch == -1) &&
+ f->value == DEF_LDAP_SRCH) ||
+ (def && f->value == def->srch))
+ ? R_SELD : ' ',
+ lv, lv, f->name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->value = cpystr(tmp);
+ }
+
+ /* Blank line */
+ new_confline(&ctmp);
+ ctmp->flags |= CF_NOSELECT | CF_B_LINE;
+ ctmp->varname = cpystr("");
+
+ /* Email attribute name */
+ new_confline(&ctmp);
+ ctmp->help_title= _("HELP FOR EMAIL ATTRIBUTE NAME");
+ ctmp->var = &mailattr_var;
+ ctmp->valoffset = indent;
+ ctmp->keymenu = &config_text_keymenu;
+ ctmp->help = h_config_ldap_email_attr;
+ ctmp->tool = dir_edit_tool;
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.*w =", indent-3,indent-3,mailattr_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmp;
+ ctmp->value = pretty_value(ps, ctmp);
+
+ /* Name attribute name */
+ new_confline(&ctmp);
+ ctmp->help_title= _("HELP FOR NAME ATTRIBUTE NAME");
+ ctmp->var = &cnattr_var;
+ ctmp->valoffset = indent;
+ ctmp->keymenu = &config_text_keymenu;
+ ctmp->help = h_config_ldap_cn_attr;
+ ctmp->tool = dir_edit_tool;
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.*w =", indent-3,indent-3,cnattr_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmp;
+ ctmp->value = pretty_value(ps, ctmp);
+
+ /* Surname attribute name */
+ new_confline(&ctmp);
+ ctmp->help_title= _("HELP FOR SURNAME ATTRIBUTE NAME");
+ ctmp->var = &snattr_var;
+ ctmp->valoffset = indent;
+ ctmp->keymenu = &config_text_keymenu;
+ ctmp->help = h_config_ldap_sn_attr;
+ ctmp->tool = dir_edit_tool;
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.*w =", indent-3,indent-3,snattr_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmp;
+ ctmp->value = pretty_value(ps, ctmp);
+
+ /* Givenname attribute name */
+ new_confline(&ctmp);
+ ctmp->help_title= _("HELP FOR GIVEN NAME ATTRIBUTE NAME");
+ ctmp->var = &gnattr_var;
+ ctmp->valoffset = indent;
+ ctmp->keymenu = &config_text_keymenu;
+ ctmp->help = h_config_ldap_gn_attr;
+ ctmp->tool = dir_edit_tool;
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.*w =", indent-3,indent-3,gnattr_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmp;
+ ctmp->value = pretty_value(ps, ctmp);
+
+ /* Blank line */
+ new_confline(&ctmp);
+ ctmp->flags |= CF_NOSELECT | CF_B_LINE;
+ ctmp->varname = cpystr("");
+
+ /* Time limit */
+ new_confline(&ctmp);
+ ctmp->help_title= _("HELP FOR SERVER TIMELIMIT");
+ ctmp->var = &time_var;
+ ctmp->valoffset = indent;
+ ctmp->keymenu = &config_text_keymenu;
+ ctmp->help = h_config_ldap_time;
+ ctmp->tool = dir_edit_tool;
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.*w =", indent-3,indent-3,time_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmp;
+ ctmp->value = pretty_value(ps, ctmp);
+ ctmp->flags |= CF_NUMBER;
+
+ /* Size limit */
+ new_confline(&ctmp);
+ ctmp->var = &size_var;
+ ctmp->help_title= _("HELP FOR SERVER SIZELIMIT");
+ ctmp->valoffset = indent;
+ ctmp->keymenu = &config_text_keymenu;
+ ctmp->help = h_config_ldap_size;
+ ctmp->tool = dir_edit_tool;
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.*w =", indent-3,indent-3,size_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmp;
+ ctmp->value = pretty_value(ps, ctmp);
+ ctmp->flags |= CF_NUMBER;
+
+ /* Blank line */
+ new_confline(&ctmp);
+ ctmp->flags |= CF_NOSELECT | CF_B_LINE;
+
+ /* Custom Search Filter */
+ new_confline(&ctmp);
+ ctmp->help_title= _("HELP FOR CUSTOM SEARCH FILTER");
+ ctmp->var = &cust_var;
+ ctmp->valoffset = indent;
+ ctmp->keymenu = &config_text_keymenu;
+ ctmp->help = h_config_ldap_cust;
+ ctmp->tool = dir_edit_tool;
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.*w =", indent-3,indent-3,cust_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmp;
+ ctmp->value = pretty_value(ps, ctmp);
+
+
+ snprintf(tmp, sizeof(tmp), "%s DIRECTORY SERVER", title);
+ tmp[sizeof(tmp)-1] = '\0';
+ memset(&screen, 0, sizeof(screen));
+ screen.ro_warning = saved_screen ? saved_screen->deferred_ro_warning : 0;
+ /* TRANSLATORS: Print something1 using something2.
+ servers is something1 */
+ rv = conf_scroll_screen(ps, &screen, first_line, tmp, _("servers"), 0);
+
+ /*
+ * Now look at the fake variables and extract the information we
+ * want from them.
+ */
+
+ if(rv == 1 && raw_server){
+ char dir_tmp[2200], *p;
+ int portval = -1, timeval = -1, sizeval = -1;
+
+ apval = APVAL(&server_var, ew);
+ server = *apval;
+ *apval = NULL;
+
+ apval = APVAL(&base_var, ew);
+ base = *apval;
+ *apval = NULL;
+
+ apval = APVAL(&port_var, ew);
+ port = *apval;
+ *apval = NULL;
+
+ apval = APVAL(&binddn_var, ew);
+ binddn = *apval;
+ *apval = NULL;
+
+ apval = APVAL(&nick_var, ew);
+ nick = *apval;
+ *apval = NULL;
+
+ apval = APVAL(&srch_type_var, ew);
+ srch_type = *apval;
+ *apval = NULL;
+
+ apval = APVAL(&srch_rule_var, ew);
+ srch_rule = *apval;
+ *apval = NULL;
+
+ apval = APVAL(&time_var, ew);
+ ttime = *apval;
+ *apval = NULL;
+
+ apval = APVAL(&size_var, ew);
+ ssize = *apval;
+ *apval = NULL;
+
+ apval = APVAL(&cust_var, ew);
+ cust = *apval;
+ *apval = NULL;
+
+ apval = APVAL(&mailattr_var, ew);
+ mailattr = *apval;
+ *apval = NULL;
+
+ apval = APVAL(&snattr_var, ew);
+ snattr = *apval;
+ *apval = NULL;
+
+ apval = APVAL(&gnattr_var, ew);
+ gnattr = *apval;
+ *apval = NULL;
+
+ apval = APVAL(&cnattr_var, ew);
+ cnattr = *apval;
+ *apval = NULL;
+
+ if(server)
+ removing_leading_and_trailing_white_space(server);
+
+ if(base){
+ removing_leading_and_trailing_white_space(base);
+ (void)removing_double_quotes(base);
+ p = add_backslash_escapes(base);
+ fs_give((void **)&base);
+ base = p;
+ }
+
+ if(port){
+ removing_leading_and_trailing_white_space(port);
+ if(*port)
+ portval = atoi(port);
+ }
+
+ if(binddn){
+ removing_leading_and_trailing_white_space(binddn);
+ (void)removing_double_quotes(binddn);
+ p = add_backslash_escapes(binddn);
+ fs_give((void **)&binddn);
+ binddn = p;
+ }
+
+ if(nick){
+ removing_leading_and_trailing_white_space(nick);
+ (void)removing_double_quotes(nick);
+ p = add_backslash_escapes(nick);
+ fs_give((void **)&nick);
+ nick = p;
+ }
+
+ if(ttime){
+ removing_leading_and_trailing_white_space(ttime);
+ if(*ttime)
+ timeval = atoi(ttime);
+ }
+
+ if(ssize){
+ removing_leading_and_trailing_white_space(ssize);
+ if(*ssize)
+ sizeval = atoi(ssize);
+ }
+
+ if(cust){
+ removing_leading_and_trailing_white_space(cust);
+ p = add_backslash_escapes(cust);
+ fs_give((void **)&cust);
+ cust = p;
+ }
+
+ if(mailattr){
+ removing_leading_and_trailing_white_space(mailattr);
+ p = add_backslash_escapes(mailattr);
+ fs_give((void **)&mailattr);
+ mailattr = p;
+ }
+
+ if(snattr){
+ removing_leading_and_trailing_white_space(snattr);
+ p = add_backslash_escapes(snattr);
+ fs_give((void **)&snattr);
+ snattr = p;
+ }
+
+ if(gnattr){
+ removing_leading_and_trailing_white_space(gnattr);
+ p = add_backslash_escapes(gnattr);
+ fs_give((void **)&gnattr);
+ gnattr = p;
+ }
+
+ if(cnattr){
+ removing_leading_and_trailing_white_space(cnattr);
+ p = add_backslash_escapes(cnattr);
+ fs_give((void **)&cnattr);
+ cnattr = p;
+ }
+
+ /*
+ * Don't allow user to edit scope but if one is present then we
+ * leave it (so they could edit it by hand).
+ */
+ if(def && def->scope != -1 && def->scope != DEF_LDAP_SCOPE){
+ NAMEVAL_S *v;
+
+ v = ldap_search_scope(def->scope);
+ if(v){
+ snprintf(custom_scope, sizeof(custom_scope), "/scope=%s", v->name);
+ custom_scope[sizeof(custom_scope)-1] = '\0';
+ }
+ }
+
+ snprintf(dir_tmp, sizeof(dir_tmp), "%s%s%s \"/base=%s/binddn=%s/impl=%d/rhs=%d/ref=%d/nosub=%d/tls=%d/tlsm=%d/type=%s/srch=%s%s/time=%s/size=%s/cust=%s/nick=%s/matr=%s/catr=%s/satr=%s/gatr=%s\"",
+ server ? server : "",
+ (portval >= 0 && port && *port) ? ":" : "",
+ (portval >= 0 && port && *port) ? port : "",
+ base ? base : "",
+ binddn ? binddn : "",
+ bitnset(LDAP_F_IMPL, ldap_option_list) ? 1 : 0,
+ bitnset(LDAP_F_RHS, ldap_option_list) ? 1 : 0,
+ bitnset(LDAP_F_REF, ldap_option_list) ? 1 : 0,
+ bitnset(LDAP_F_NOSUB, ldap_option_list) ? 1 : 0,
+ bitnset(LDAP_F_TLS, ldap_option_list) ? 1 : 0,
+ bitnset(LDAP_F_TLSMUST, ldap_option_list) ? 1 : 0,
+ srch_type ? srch_type : "",
+ srch_rule ? srch_rule : "",
+ custom_scope,
+ (timeval >= 0 && ttime && *ttime) ? ttime : "",
+ (sizeval >= 0 && ssize && *ssize) ? ssize : "",
+ cust ? cust : "",
+ nick ? nick : "",
+ mailattr ? mailattr : "",
+ cnattr ? cnattr : "",
+ snattr ? snattr : "",
+ gnattr ? gnattr : "");
+ dir_tmp[sizeof(dir_tmp)-1] = '\0';
+
+ *raw_server = cpystr(dir_tmp);
+ }
+
+ for(j = 0; varlist[j]; j++){
+ v = varlist[j];
+ if(v->current_val.p)
+ fs_give((void **)&v->current_val.p);
+ if(v->global_val.p)
+ fs_give((void **)&v->global_val.p);
+ if(v->main_user_val.p)
+ fs_give((void **)&v->main_user_val.p);
+ if(v->post_user_val.p)
+ fs_give((void **)&v->post_user_val.p);
+ if(v->name)
+ fs_give((void **)&v->name);
+ }
+
+ if(server)
+ fs_give((void **)&server);
+ if(base)
+ fs_give((void **)&base);
+ if(port)
+ fs_give((void **)&port);
+ if(binddn)
+ fs_give((void **)&binddn);
+ if(nick)
+ fs_give((void **)&nick);
+ if(srch_type)
+ fs_give((void **)&srch_type);
+ if(srch_rule)
+ fs_give((void **)&srch_rule);
+ if(ttime)
+ fs_give((void **)&ttime);
+ if(ssize)
+ fs_give((void **)&ssize);
+ if(mailattr)
+ fs_give((void **)&mailattr);
+ if(cnattr)
+ fs_give((void **)&cnattr);
+ if(snattr)
+ fs_give((void **)&snattr);
+ if(gnattr)
+ fs_give((void **)&gnattr);
+ if(cust)
+ fs_give((void **)&cust);
+
+ opt_screen = saved_screen;
+ ps->mangled_screen = 1;
+ return(rv);
+}
+
+
+/*
+ * Just calls text_tool except for intercepting MC_EXIT.
+ */
+int
+dir_edit_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ if(cmd == MC_EXIT)
+ return(config_exit_cmd(flags));
+ else
+ return(text_tool(ps, cmd, cl, flags));
+}
+
+
+/*
+ * Delete LDAP directory entry
+ */
+void
+dir_config_del(struct pine *ps, CONF_S **cl)
+{
+ char prompt[81];
+ int rv = 0, i;
+
+ if(fixed_var((*cl)->var, NULL, NULL)){
+ if((*cl)->var->post_user_val.l || (*cl)->var->main_user_val.l){
+ if(want_to(_("Delete (unused) directory servers "),
+ 'n', 'n', NO_HELP, WT_FLUSH_IN) == 'y'){
+ rv = 1;
+ delete_user_vals((*cl)->var);
+ }
+ }
+ else
+ q_status_message(SM_ORDER, 3, 3,
+ _("Can't delete sys-admin defined value"));
+ }
+ else{
+ int cnt, ans = 0, no_ex;
+ char **new_list, **lval, **nelval;
+
+ no_ex = (ps_global->ew_for_except_vars == Main);
+
+ /* This can't happen, intercepted at caller by first_one case */
+ nelval = no_ex ? (*cl)->var->current_val.l : LVAL((*cl)->var, ew);
+ lval = LVAL((*cl)->var, ew);
+ if(lval && lval[0] && lval[0][0] == '\0')
+ ans = 'r';
+
+ /* how many servers defined? */
+ for(cnt = 0; nelval[cnt]; cnt++)
+ ;
+
+ /*
+ * If using default and there is more than one in list, ask if user
+ * wants to ignore them all or delete just this one. If just this
+ * one, copy rest to user_val. If ignore all, copy "" to user_val
+ * to override.
+ */
+ if(!lval && cnt > 1){
+ static ESCKEY_S opts[] = {
+ {'i', 'i', "I", N_("Ignore All")},
+ {'r', 'r', "R", N_("Remove One")},
+ {-1, 0, NULL, NULL}};
+ ans = radio_buttons(
+ _("Ignore all default directory servers or just remove this one ? "),
+ -FOOTER_ROWS(ps), opts, 'i', 'x',
+ h_ab_del_dir_ignore, RB_NORM);
+ }
+
+ if(ans == 0){
+ snprintf(prompt, sizeof(prompt), _("Really delete %s \"%s\" from directory servers "),
+ ((*cl)->value && *(*cl)->value)
+ ? "server"
+ : "item",
+ ((*cl)->value && *(*cl)->value)
+ ? (*cl)->value
+ : int2string((*cl)->varmem + 1));
+ prompt[sizeof(prompt)-1] = '\0';
+ }
+
+
+ ps->mangled_footer = 1;
+ if(ans == 'i'){
+ rv = ps->mangled_body = 1;
+
+ /*
+ * Ignore all of default by adding an empty string. Make it
+ * look just like there are no servers defined.
+ */
+
+ new_list = (char **)fs_get((1 + 1) * sizeof(char *));
+ new_list[0] = cpystr("");
+ new_list[1] = NULL;
+ set_variable_list(V_LDAP_SERVERS, new_list, FALSE, ew);
+ free_list_array(&new_list);
+ *cl = first_confline(*cl);
+ free_conflines(cl);
+ opt_screen->top_line = NULL;
+
+ add_ldap_fake_first_server(ps, cl, &ps->vars[V_LDAP_SERVERS],
+ &dir_conf_km, h_direct_config,
+ dir_config_tool);
+ }
+ else if(ans == 'r' ||
+ (ans != 'x' &&
+ want_to(prompt, 'n', 'n', NO_HELP, WT_FLUSH_IN) == 'y')){
+ CONF_S *cp;
+ char **servers;
+ int move_top = 0, this_one, revert_to_default,
+ default_there_to_revert_to;
+
+ /*
+ * Remove one from current list.
+ */
+
+ rv = ps->mangled_body = 1;
+
+ this_one = (*cl)->varmem;
+
+ /* might have to re-adjust screen to see new current */
+ move_top = (this_one > 0) &&
+ (this_one == cnt - 1) &&
+ (((*cl) == opt_screen->top_line) ||
+ ((*cl)->prev == opt_screen->top_line) ||
+ ((*cl)->prev->prev == opt_screen->top_line));
+
+ /*
+ * If this is last one and there is a default available, revert
+ * to it.
+ */
+ revert_to_default = ((cnt == 1) && lval);
+ if(cnt > 1){
+ new_list = (char **)fs_get((cnt + 1) * sizeof(char *));
+ for(i = 0; i < this_one; i++)
+ new_list[i] = cpystr(nelval[i]);
+
+ for(i = this_one; i < cnt; i++)
+ new_list[i] = cpystr(nelval[i+1]);
+
+ set_variable_list(V_LDAP_SERVERS, new_list, FALSE, ew);
+ free_list_array(&new_list);
+ }
+ else if(revert_to_default){
+ char ***alval;
+
+ alval = ALVAL((*cl)->var, ew);
+ if(alval && *alval)
+ free_list_array(alval);
+ }
+ else{
+ /* cnt is one and we want to hide default */
+ new_list = (char **)fs_get((1 + 1) * sizeof(char *));
+ new_list[0] = cpystr("");
+ new_list[1] = NULL;
+ set_variable_list(V_LDAP_SERVERS, new_list, FALSE, ew);
+ free_list_array(&new_list);
+ }
+
+ if(cnt == 1){ /* delete display line for this_one */
+ if(revert_to_default){
+ servers = (*cl)->var->global_val.l;
+ default_there_to_revert_to = (servers != NULL);
+ }
+
+ *cl = first_confline(*cl);
+ free_conflines(cl);
+ opt_screen->top_line = NULL;
+ if(revert_to_default && default_there_to_revert_to){
+ CONF_S *first_line = NULL;
+
+ q_status_message(SM_ORDER, 0, 3,
+ _("Reverting to default directory server"));
+ dir_init_display(ps, cl, servers,
+ &ps->vars[V_LDAP_SERVERS], &first_line);
+ *cl = first_line;
+ }
+ else{
+ add_ldap_fake_first_server(ps, cl,
+ &ps->vars[V_LDAP_SERVERS],
+ &dir_conf_km, h_direct_config,
+ dir_config_tool);
+ }
+ }
+ else if(this_one == cnt - 1){ /* deleted last one */
+ /* back up and delete it */
+ *cl = (*cl)->prev;
+ free_conflines(&(*cl)->next);
+ /* now back up to first line of this server */
+ *cl = (*cl)->prev->prev;
+ if(move_top)
+ opt_screen->top_line = *cl;
+ }
+ else{ /* deleted one out of the middle */
+ if(*cl == opt_screen->top_line)
+ opt_screen->top_line = (*cl)->next->next->next;
+
+ cp = *cl;
+ *cl = (*cl)->next; /* move to next line, then */
+ snip_confline(&cp); /* snip 1st deleted line */
+ cp = *cl;
+ *cl = (*cl)->next; /* move to next line, then */
+ snip_confline(&cp); /* snip 2nd deleted line */
+ cp = *cl;
+ *cl = (*cl)->next; /* move to next line, then */
+ snip_confline(&cp); /* snip 3rd deleted line */
+ /* adjust varmems */
+ for(cp = *cl; cp; cp = cp->next)
+ cp->varmem--;
+ }
+ }
+ else
+ q_status_message(SM_ORDER, 0, 3, _("Server not deleted"));
+ }
+
+ if(rv == 1){
+ set_current_val((*cl)->var, TRUE, FALSE);
+ fix_side_effects(ps, (*cl)->var, 0);
+ write_pinerc(ps, ew, WRP_NONE);
+ }
+}
+
+
+/*
+ * Utility routine to help set up display
+ */
+void
+add_ldap_fake_first_server(struct pine *ps, CONF_S **ctmp, struct variable *var,
+ struct key_menu *km, HelpType help,
+ int (*tool)(struct pine *, int, CONF_S **, unsigned))
+{
+ new_confline(ctmp);
+ (*ctmp)->help_title= _("HELP FOR DIRECTORY SERVER CONFIGURATION");
+ (*ctmp)->value = cpystr(ADD_FIRST_LDAP_SERVER);
+ (*ctmp)->var = var;
+ (*ctmp)->varmem = 0;
+ (*ctmp)->keymenu = km;
+ (*ctmp)->help = help;
+ (*ctmp)->tool = tool;
+ (*ctmp)->valoffset = 2;
+}
+
+
+/*
+ * Add an ldap server to the display list.
+ *
+ * Args before -- Insert it before current, else append it after.
+ */
+void
+add_ldap_server_to_display(struct pine *ps, CONF_S **ctmp, char *serv, char *subtitle,
+ struct variable *var, int member, struct key_menu *km,
+ HelpType help,
+ int (*tool)(struct pine *, int, CONF_S **, unsigned),
+ int before, CONF_S **first_line)
+{
+ new_confline(ctmp);
+ if(first_line)
+ *first_line = *ctmp;
+
+ if(before){
+ /*
+ * New_confline appends ctmp after old current instead of inserting
+ * it, so we have to adjust. We have
+ * <- a <-> b <-> p <-> c -> and want <- a <-> p <-> b <-> c ->
+ */
+
+ CONF_S *a, *b, *c, *p;
+
+ p = *ctmp;
+ b = (*ctmp)->prev;
+ c = (*ctmp)->next;
+ a = b ? b->prev : NULL;
+ if(a)
+ a->next = p;
+
+ if(b){
+ b->prev = p;
+ b->next = c;
+ }
+
+ if(c)
+ c->prev = b;
+
+ p->prev = a;
+ p->next = b;
+ }
+
+ (*ctmp)->help_title= _("HELP FOR DIRECTORY SERVER CONFIGURATION");
+ (*ctmp)->value = serv;
+ (*ctmp)->var = var;
+ (*ctmp)->varmem = member;
+ (*ctmp)->keymenu = km;
+ (*ctmp)->help = help;
+ (*ctmp)->tool = tool;
+ (*ctmp)->flags |= CF_STARTITEM;
+ (*ctmp)->valoffset = 4;
+
+ new_confline(ctmp);
+ (*ctmp)->value = subtitle;
+ (*ctmp)->keymenu = km;
+ (*ctmp)->help = help;
+ (*ctmp)->tool = tool;
+ (*ctmp)->flags |= CF_NOSELECT;
+ (*ctmp)->valoffset = 8;
+
+ new_confline(ctmp);
+ (*ctmp)->keymenu = km;
+ (*ctmp)->help = help;
+ (*ctmp)->tool = tool;
+ (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
+ (*ctmp)->valoffset = 0;
+}
+
+
+/*
+ * ldap option list manipulation tool
+ *
+ *
+ * returns: -1 on unrecognized cmd, 0 if no change, 1 if change
+ */
+int
+ldap_checkbox_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ int rv = 0;
+
+ switch(cmd){
+ case MC_TOGGLE: /* mark/unmark option */
+ rv = 1;
+ toggle_ldap_option_bit(ps, (*cl)->varmem, (*cl)->var, (*cl)->value);
+ break;
+
+ case MC_EXIT: /* exit */
+ rv = config_exit_cmd(flags);
+ break;
+
+ default :
+ rv = -1;
+ break;
+ }
+
+ return(rv);
+}
+
+
+void
+toggle_ldap_option_bit(struct pine *ps, int index, struct variable *var, char *value)
+{
+ NAMEVAL_S *f;
+
+ f = ldap_feature_list(index);
+
+ /* flip the bit */
+ if(bitnset(f->value, ldap_option_list))
+ clrbitn(f->value, ldap_option_list);
+ else
+ setbitn(f->value, ldap_option_list);
+
+ if(value)
+ value[1] = bitnset(f->value, ldap_option_list) ? 'X' : ' ';
+}
+
+
+NAMEVAL_S *
+ldap_feature_list(int index)
+{
+ static NAMEVAL_S ldap_feat_list[] = {
+ {"use-implicitly-from-composer", NULL, LDAP_F_IMPL},
+ {"lookup-addrbook-contents", NULL, LDAP_F_RHS},
+ {"save-search-criteria-not-result", NULL, LDAP_F_REF},
+ {"disable-ad-hoc-space-substitution", NULL, LDAP_F_NOSUB},
+ {"attempt-tls-on-connection", NULL, LDAP_F_TLS},
+ {"require-tls-on-connection", NULL, LDAP_F_TLSMUST}
+ };
+
+ return((index >= 0 &&
+ index < (sizeof(ldap_feat_list)/sizeof(ldap_feat_list[0])))
+ ? &ldap_feat_list[index] : NULL);
+}
+
+
+/*
+ * simple radio-button style variable handler
+ */
+int
+ldap_radiobutton_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ int rv = 0;
+ CONF_S *ctmp;
+ NAMEVAL_S *rule;
+ char **apval;
+
+ switch(cmd){
+ case MC_CHOICE : /* set/unset feature */
+
+ /* hunt backwards, turning off old values */
+ for(ctmp = *cl; ctmp && !(ctmp->flags & CF_NOSELECT) && !ctmp->varname;
+ ctmp = prev_confline(ctmp))
+ ctmp->value[1] = ' ';
+
+ /* hunt forwards, turning off old values */
+ for(ctmp = *cl; ctmp && !(ctmp->flags & CF_NOSELECT) && !ctmp->varname;
+ ctmp = next_confline(ctmp))
+ ctmp->value[1] = ' ';
+
+ /* turn on current value */
+ (*cl)->value[1] = R_SELD;
+
+ if((*cl)->var == ldap_srch_rule_ptr)
+ rule = ldap_search_rules((*cl)->varmem);
+ else
+ rule = ldap_search_types((*cl)->varmem);
+
+ apval = APVAL((*cl)->var, ew);
+ if(apval && *apval)
+ fs_give((void **)apval);
+
+ if(apval)
+ *apval = cpystr(rule->name);
+
+ ps->mangled_body = 1; /* BUG: redraw it all for now? */
+ rv = 1;
+
+ break;
+
+ case MC_EXIT: /* exit */
+ rv = config_exit_cmd(flags);
+ break;
+
+ default :
+ rv = -1;
+ break;
+ }
+
+ return(rv);
+}
+
+#endif /* ENABLE_LDAP */
diff --git a/alpine/ldapconf.h b/alpine/ldapconf.h
new file mode 100644
index 00000000..9dbe8e4d
--- /dev/null
+++ b/alpine/ldapconf.h
@@ -0,0 +1,34 @@
+/*
+ * $Id: ldapconf.h 1012 2008-03-26 00:44:22Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_LDAPCONF_INCLUDED
+#define PINE_LDAPCONF_INCLUDED
+
+
+#include "conftype.h"
+#include "../pith/ldap.h"
+#include "../pith/state.h"
+#include "../pith/bldaddr.h"
+
+
+/* exported protoypes */
+#ifdef ENABLE_LDAP
+void directory_config(struct pine *, int);
+int ldap_radiobutton_tool(struct pine *, int, CONF_S **, unsigned);
+#endif
+
+
+#endif /* PINE_LDAPCONF_INCLUDED */
diff --git a/alpine/listsel.c b/alpine/listsel.c
new file mode 100644
index 00000000..cb9e8812
--- /dev/null
+++ b/alpine/listsel.c
@@ -0,0 +1,332 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: listsel.c 918 2008-01-23 19:39:38Z hubert@u.washington.edu $";
+#endif
+/* ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#include "headers.h"
+#include "listsel.h"
+#include "status.h"
+#include "confscroll.h"
+#include "../pith/state.h"
+
+
+/*
+ * Internal prototypes
+ */
+int select_from_list_tool(struct pine *, int, CONF_S **, unsigned);
+int select_from_list_tool_allow_noselections(struct pine *, int, CONF_S **, unsigned);
+
+
+/*
+ * This is intended to be a generic tool to select strings from a list
+ * of strings.
+ *
+ * Args lsel -- the items as well as the answer are contained in this list
+ * flags -- There is some inconsistent flags usage. Notice that the
+ * flag SFL_ALLOW_LISTMODE is a flag passed in the flags
+ * argument whereas the flag SFL_NOSELECT is a per item
+ * (that is, per LIST_SEL_S) flag.
+ * title -- passed to conf_scroll_screen
+ * pdesc -- passed to conf_scroll_screen
+ * help -- passed to conf_scroll_screen
+ * helptitle -- passed to conf_scroll_screen
+ *
+ * You have screen width - 4 columns to work with. If you want to overflow to
+ * a second (or third or fourth) line for an item just send another item
+ * in the list but with the SFL_NOSELECT flag set. Only the selectable lines
+ * will be highlighted, which is kind of a crock, but it looked like a lot
+ * of work to fix that.
+ *
+ * Returns 0 on successful choice
+ * -1 if cancelled
+ */
+int
+select_from_list_screen(LIST_SEL_S *lsel, long unsigned int flags, char *title,
+ char *pdesc, HelpType help, char *htitle,
+ LIST_SEL_S *starting_val)
+{
+ CONF_S *ctmp = NULL, *first_line = NULL;
+ OPT_SCREEN_S screen;
+ int j, lv, ret = -1;
+ LIST_SEL_S *p;
+ char *display;
+ size_t l;
+ ScreenMode listmode = SingleMode;
+ int (*tool)(struct pine *, int, CONF_S **, unsigned);
+
+ if(!lsel)
+ return(ret);
+
+ /* find longest value's length */
+ for(lv = 0, p = lsel; p; p = p->next){
+ if(!(p->flags & SFL_NOSELECT)){
+ display = p->display_item ? p->display_item :
+ p->item ? p->item : "";
+ if(lv < (j = utf8_width(display)))
+ lv = j;
+ }
+ }
+
+ lv = MIN(lv, ps_global->ttyo->screen_cols - 4);
+
+ tool = (flags & SFL_CTRLC) ? select_from_list_tool_allow_noselections
+ : select_from_list_tool;
+
+ /*
+ * Convert the passed in list to conf_scroll lines.
+ */
+
+ if(flags & SFL_ALLOW_LISTMODE){
+
+ if(flags & SFL_ONLY_LISTMODE) {assert(flags & SFL_STARTIN_LISTMODE);}
+
+ for(p = lsel; p; p = p->next){
+
+ display = p->display_item ? p->display_item :
+ p->item ? p->item : "";
+ new_confline(&ctmp);
+ if(!first_line && !(p->flags & SFL_NOSELECT))
+ first_line = ctmp;
+ if(!first_line && !(p->flags & SFL_NOSELECT))
+ if(!starting_val || (starting_val == p))
+ first_line = ctmp;
+
+ /* generous allocation */
+ l = lv + 4 + strlen(display);
+ ctmp->value = (char *) fs_get((l + 1) * sizeof(char));
+ utf8_snprintf(ctmp->value, l+1, " %-*.*w", lv, lv, display);
+ ctmp->value[l] = '\0';
+
+ ctmp->d.l.lsel = p;
+ ctmp->d.l.listmode = &listmode;
+ if(flags & SFL_ONLY_LISTMODE){
+ if(flags & SFL_CTRLC)
+ ctmp->keymenu = &sel_from_list_olm_ctrlc;
+ else
+ ctmp->keymenu = &sel_from_list_olm;
+ }
+ else{
+ if(flags & SFL_CTRLC)
+ ctmp->keymenu = &sel_from_list_sm_ctrlc;
+ else
+ ctmp->keymenu = &sel_from_list_sm;
+ }
+
+ ctmp->help = help;
+ ctmp->help_title = htitle;
+ ctmp->tool = tool;
+ ctmp->flags = CF_STARTITEM |
+ ((p->flags & SFL_NOSELECT) ? CF_NOSELECT : 0);
+ }
+ }
+ else{
+
+ assert(!(flags & SFL_ONLY_LISTMODE));
+ assert(!(flags & SFL_STARTIN_LISTMODE));
+
+ for(p = lsel; p; p = p->next){
+
+ display = p->display_item ? p->display_item :
+ p->item ? p->item : "";
+ new_confline(&ctmp);
+ if(!first_line && !(p->flags & SFL_NOSELECT))
+ if(!starting_val || (starting_val == p))
+ first_line = ctmp;
+
+ l = lv + strlen(display);
+ ctmp->value = (char *) fs_get((l + 1) * sizeof(char));
+ utf8_snprintf(ctmp->value, l+1, "%-*.*w", lv, lv, display);
+ ctmp->value[l] = '\0';
+
+ ctmp->d.l.lsel = p;
+ ctmp->d.l.listmode = &listmode;
+ if(flags & SFL_CTRLC)
+ ctmp->keymenu = &sel_from_list_ctrlc;
+ else
+ ctmp->keymenu = &sel_from_list;
+
+ ctmp->help = help;
+ ctmp->help_title = htitle;
+ ctmp->tool = tool;
+ ctmp->flags = CF_STARTITEM |
+ ((p->flags & SFL_NOSELECT) ? CF_NOSELECT : 0);
+ ctmp->valoffset = 4;
+ }
+ }
+
+ /* just convert to start in listmode after the fact, easier that way */
+ if(flags & SFL_STARTIN_LISTMODE){
+ listmode = ListMode;
+
+ for(ctmp = first_line; ctmp; ctmp = next_confline(ctmp))
+ if(!(ctmp->flags & CF_NOSELECT) && ctmp->value){
+ ctmp->value[0] = '[';
+ ctmp->value[1] = ctmp->d.l.lsel->selected ? 'X' : SPACE;
+ ctmp->value[2] = ']';
+ if(flags & SFL_ONLY_LISTMODE){
+ if(flags & SFL_CTRLC)
+ ctmp->keymenu = &sel_from_list_olm_ctrlc;
+ else
+ ctmp->keymenu = &sel_from_list_olm;
+ }
+ else{
+ if(flags & SFL_CTRLC)
+ ctmp->keymenu = &sel_from_list_lm_ctrlc;
+ else
+ ctmp->keymenu = &sel_from_list_lm;
+ }
+ }
+ }
+
+ memset(&screen, 0, sizeof(screen));
+ switch(conf_scroll_screen(ps_global, &screen, first_line, title, pdesc, 0)){
+ case 1:
+ ret = 0;
+ break;
+
+ default:
+ break;
+ }
+
+ ps_global->mangled_screen = 1;
+ return(ret);
+}
+
+
+int
+select_from_list_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ CONF_S *ctmp;
+ int retval = 0;
+
+ switch(cmd){
+ case MC_SELECT :
+ if(*(*cl)->d.l.listmode == SingleMode){
+ (*cl)->d.l.lsel->selected = 1;
+ retval = 3;
+ }
+ else{
+ /* check if anything is selected */
+ /* go to first line */
+ for(ctmp = *cl; prev_confline(ctmp); ctmp = prev_confline(ctmp))
+ ;
+
+ for(; ctmp; ctmp = next_confline(ctmp))
+ if(!(ctmp->flags & CF_NOSELECT) && ctmp->d.l.lsel->selected){
+ retval = 3;
+ break;
+ }
+
+ if(retval == 0){
+ q_status_message(SM_ORDER, 0, 3,
+ _("Nothing selected, use Exit to exit without a selection."));
+ }
+ }
+
+ break;
+
+ case MC_LISTMODE :
+ if(*(*cl)->d.l.listmode == SingleMode){
+ /*
+ * UnHide the checkboxes
+ */
+
+ *(*cl)->d.l.listmode = ListMode;
+
+ /* go to first line */
+ for(ctmp = *cl; prev_confline(ctmp); ctmp = prev_confline(ctmp))
+ ;
+
+ for(; ctmp; ctmp = next_confline(ctmp))
+ if(!(ctmp->flags & CF_NOSELECT) && ctmp->value){
+ ctmp->value[0] = '[';
+ ctmp->value[1] = ctmp->d.l.lsel->selected ? 'X' : SPACE;
+ ctmp->value[2] = ']';
+ ctmp->keymenu = &sel_from_list_lm;
+ }
+ }
+ else{
+ /*
+ * Hide the checkboxes
+ */
+
+ *(*cl)->d.l.listmode = SingleMode;
+
+ /* go to first line */
+ for(ctmp = *cl; prev_confline(ctmp); ctmp = prev_confline(ctmp))
+ ;
+
+ for(; ctmp; ctmp = next_confline(ctmp))
+ if(!(ctmp->flags & CF_NOSELECT) && ctmp->value){
+ ctmp->value[0] = ctmp->value[1] = ctmp->value[2] = SPACE;
+ ctmp->keymenu = &sel_from_list_sm;
+ }
+ }
+
+ ps->mangled_body = ps->mangled_footer = 1;
+ break;
+
+ case MC_TOGGLE :
+ if((*cl)->value[1] == 'X'){
+ (*cl)->d.l.lsel->selected = 0;
+ (*cl)->value[1] = SPACE;
+ }
+ else{
+ (*cl)->d.l.lsel->selected = 1;
+ (*cl)->value[1] = 'X';
+ }
+
+ ps->mangled_body = 1;
+ break;
+
+ case MC_EXIT :
+ retval = simple_exit_cmd(flags);
+ break;
+
+ default:
+ retval = -1;
+ break;
+ }
+
+ if(retval > 0)
+ ps->mangled_body = 1;
+
+ return(retval);
+}
+
+
+int
+select_from_list_tool_allow_noselections(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ int retval = 0;
+
+ switch(cmd){
+ case MC_SELECT :
+ retval = 3;
+ if(*(*cl)->d.l.listmode == SingleMode)
+ (*cl)->d.l.lsel->selected = 1;
+
+ break;
+
+ default:
+ retval = select_from_list_tool(ps, cmd, cl, flags);
+ break;
+ }
+
+ if(retval > 0)
+ ps->mangled_body = 1;
+
+ return(retval);
+}
diff --git a/alpine/listsel.h b/alpine/listsel.h
new file mode 100644
index 00000000..cab8da58
--- /dev/null
+++ b/alpine/listsel.h
@@ -0,0 +1,47 @@
+/*
+ * $Id: listsel.h 918 2008-01-23 19:39:38Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_LISTSEL_INCLUDED
+#define PINE_LISTSEL_INCLUDED
+
+
+#include "help.h"
+
+
+/* for select_from_list_screen */
+#define SFL_NONE 0x000
+#define SFL_ALLOW_LISTMODE 0x001
+#define SFL_STARTIN_LISTMODE 0x002 /* should also light ALLOW_LISTMODE */
+#define SFL_ONLY_LISTMODE 0x004 /* don't allow switching back out of ListMode */
+#define SFL_NOSELECT 0x008 /* per item flag, line not selectable */
+#define SFL_CTRLC 0x010 /* use ^C instead of Exit for Exit and
+ allow zero selections */
+
+
+typedef struct list_selection {
+ char *display_item; /* use item if this is NULL */
+ char *item; /* selected value for item */
+ int selected; /* is item selected or not */
+ int flags;
+ struct list_selection *next;
+} LIST_SEL_S;
+
+
+/* exported protoypes */
+int select_from_list_screen(LIST_SEL_S *, unsigned long, char *, char *, HelpType, char *, LIST_SEL_S *);
+
+
+#endif /* PINE_LISTSEL_INCLUDED */
diff --git a/alpine/mailcmd.c b/alpine/mailcmd.c
new file mode 100644
index 00000000..fa781556
--- /dev/null
+++ b/alpine/mailcmd.c
@@ -0,0 +1,9472 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: mailcmd.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2009 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ mailcmd.c
+ The meat and pototoes of mail processing here:
+ - initial command processing and dispatch
+ - save message
+ - capture address off incoming mail
+ - jump to specific numbered message
+ - open (broach) a new folder
+ - search message headers (where is) command
+ ====*/
+
+
+#include "headers.h"
+#include "mailcmd.h"
+#include "status.h"
+#include "mailview.h"
+#include "flagmaint.h"
+#include "listsel.h"
+#include "keymenu.h"
+#include "alpine.h"
+#include "mailpart.h"
+#include "mailindx.h"
+#include "folder.h"
+#include "reply.h"
+#include "help.h"
+#include "titlebar.h"
+#include "signal.h"
+#include "radio.h"
+#include "pipe.h"
+#include "send.h"
+#include "takeaddr.h"
+#include "roleconf.h"
+#include "smime.h"
+#include "../pith/state.h"
+#include "../pith/msgno.h"
+#include "../pith/store.h"
+#include "../pith/thread.h"
+#include "../pith/flag.h"
+#include "../pith/sort.h"
+#include "../pith/maillist.h"
+#include "../pith/save.h"
+#include "../pith/pipe.h"
+#include "../pith/news.h"
+#include "../pith/util.h"
+#include "../pith/sequence.h"
+#include "../pith/keyword.h"
+#include "../pith/stream.h"
+#include "../pith/mailcmd.h"
+#include "../pith/hist.h"
+#include "../pith/list.h"
+#include "../pith/icache.h"
+#include "../pith/busy.h"
+#include "../pith/mimedesc.h"
+#include "../pith/pattern.h"
+#include "../pith/tempfile.h"
+#include "../pith/search.h"
+#include "../pith/margin.h"
+#ifdef _WINDOWS
+#include "../pico/osdep/mswin.h"
+#endif
+
+/*
+ * Internal Prototypes
+ */
+int cmd_flag(struct pine *, MSGNO_S *, int);
+int cmd_flag_prompt(struct pine *, struct flag_screen *, int);
+void free_flag_table(struct flag_table **);
+int cmd_reply(struct pine *, MSGNO_S *, int);
+int cmd_forward(struct pine *, MSGNO_S *, int);
+int cmd_bounce(struct pine *, MSGNO_S *, int);
+int cmd_save(struct pine *, MAILSTREAM *, MSGNO_S *, int, CmdWhere);
+void role_compose(struct pine *);
+void cmd_expunge(struct pine *, MAILSTREAM *, MSGNO_S *);
+int cmd_export(struct pine *, MSGNO_S *, int, int);
+char *cmd_delete_action(struct pine *, MSGNO_S *, CmdWhere);
+char *cmd_delete_view(struct pine *, MSGNO_S *);
+char *cmd_delete_index(struct pine *, MSGNO_S *);
+long get_level(int, UCS, SCROLL_S *);
+long closest_jump_target(long, MAILSTREAM *, MSGNO_S *, int, CmdWhere, char *, size_t);
+int update_folder_spec(char *, size_t, char *);
+int cmd_print(struct pine *, MSGNO_S *, int, CmdWhere);
+int cmd_pipe(struct pine *, MSGNO_S *, int);
+STORE_S *list_mgmt_text(RFC2369_S *, long);
+void list_mgmt_screen(STORE_S *);
+int aggregate_select(struct pine *, MSGNO_S *, int, CmdWhere);
+int select_by_number(MAILSTREAM *, MSGNO_S *, SEARCHSET **);
+int select_by_thrd_number(MAILSTREAM *, MSGNO_S *, SEARCHSET **);
+int select_by_date(MAILSTREAM *, MSGNO_S *, long, SEARCHSET **);
+int select_by_text(MAILSTREAM *, MSGNO_S *, long, SEARCHSET **);
+int select_by_size(MAILSTREAM *, SEARCHSET **);
+SEARCHSET *visible_searchset(MAILSTREAM *, MSGNO_S *);
+int select_by_status(MAILSTREAM *, SEARCHSET **);
+int select_by_rule(MAILSTREAM *, SEARCHSET **);
+int select_by_thread(MAILSTREAM *, MSGNO_S *, SEARCHSET **);
+char *choose_a_rule(int);
+int select_by_keyword(MAILSTREAM *, SEARCHSET **);
+char *choose_a_keyword(void);
+int select_sort(struct pine *, int, SortOrder *, int *);
+int print_index(struct pine *, MSGNO_S *, int);
+
+
+
+/*
+ * List of Select options used by apply_* functions...
+ */
+static char *sel_pmt1 = N_("ALTER message selection : ");
+ESCKEY_S sel_opts1[] = {
+ /* TRANSLATORS: these are keymenu names for selecting. Broaden selection means
+ we will add more messages to the selection, Narrow selection means we will
+ remove some selections (like a logical AND instead of logical OR), and Flip
+ Selected means that all the messages that are currently selected become unselected,
+ and all the unselected messages become selected. */
+ {'a', 'a', "A", N_("unselect All")},
+ {'c', 'c', "C", NULL},
+ {'b', 'b', "B", N_("Broaden selctn")},
+ {'n', 'n', "N", N_("Narrow selctn")},
+ {'f', 'f', "F", N_("Flip selected")},
+ {-1, 0, NULL, NULL}
+};
+
+
+#define SEL_OPTS_THREAD 9 /* index number of "tHread" */
+#define SEL_OPTS_THREAD_CH 'h'
+
+char *sel_pmt2 = "SELECT criteria : ";
+static ESCKEY_S sel_opts2[] = {
+ /* TRANSLATORS: very short descriptions of message selection criteria. Select Cur
+ means select the currently highlighted message; select by Number is by message
+ number; Status is by status of the message, for example the message might be
+ New or it might be Unseen or marked Important; Size has the Z upper case because
+ it is a Z command; Keyword is an alpine keyword that has been set by the user;
+ and Rule is an alpine rule */
+ {'a', 'a', "A", N_("select All")},
+ {'c', 'c', "C", N_("select Cur")},
+ {'n', 'n', "N", N_("Number")},
+ {'d', 'd', "D", N_("Date")},
+ {'t', 't', "T", N_("Text")},
+ {'s', 's', "S", N_("Status")},
+ {'z', 'z', "Z", N_("siZe")},
+ {'k', 'k', "K", N_("Keyword")},
+ {'r', 'r', "R", N_("Rule")},
+ {SEL_OPTS_THREAD_CH, 'h', "H", N_("tHread")},
+ {-1, 0, NULL, NULL}
+};
+
+
+static ESCKEY_S sel_opts3[] = {
+ /* TRANSLATORS: these are operations we can do on a set of selected messages.
+ Del is Delete; Undel is Undelete; TakeAddr means to Take some Addresses into
+ the address book; Save means to save the messages into another alpine folder;
+ Export means to copy the messages to a file outside of alpine, external to
+ alpine's world. */
+ {'d', 'd', "D", N_("Del")},
+ {'u', 'u', "U", N_("Undel")},
+ {'r', 'r', "R", N_("Reply")},
+ {'f', 'f', "F", N_("Forward")},
+ {'%', '%', "%", N_("Print")},
+ {'t', 't', "T", N_("TakeAddr")},
+ {'s', 's', "S", N_("Save")},
+ {'e', 'e', "E", N_("Export")},
+ { -1, 0, NULL, NULL},
+ { -1, 0, NULL, NULL},
+ { -1, 0, NULL, NULL},
+ { -1, 0, NULL, NULL},
+ { -1, 0, NULL, NULL},
+ { -1, 0, NULL, NULL},
+ { -1, 0, NULL, NULL},
+ {-1, 0, NULL, NULL}
+};
+
+static ESCKEY_S sel_opts4[] = {
+ {'a', 'a', "A", N_("select All")},
+ /* TRANSLATORS: select currrently highlighted message Thread */
+ {'c', 'c', "C", N_("select Curthrd")},
+ {'n', 'n', "N", N_("Number")},
+ {'d', 'd', "D", N_("Date")},
+ {'t', 't', "T", N_("Text")},
+ {'s', 's', "S", N_("Status")},
+ {'z', 'z', "Z", N_("siZe")},
+ {'k', 'k', "K", N_("Keyword")},
+ {'r', 'r', "R", N_("Rule")},
+ {SEL_OPTS_THREAD_CH, 'h', "H", N_("tHread")},
+ {-1, 0, NULL, NULL}
+};
+
+
+static char *sel_flag =
+ N_("Select New, Deleted, Answered, Forwarded, or Important messages ? ");
+static char *sel_flag_not =
+ N_("Select NOT New, NOT Deleted, NOT Answered, NOT Forwarded or NOT Important msgs ? ");
+static ESCKEY_S sel_flag_opt[] = {
+ /* TRANSLATORS: When selecting messages by message Status these are the
+ different types of Status you can select on. Is the message New, Recent,
+ and so on. Not means flip the meaning of the selection to the opposite
+ thing, so message is not New or not Important. */
+ {'n', 'n', "N", N_("New")},
+ {'*', '*', "*", N_("Important")},
+ {'d', 'd', "D", N_("Deleted")},
+ {'a', 'a', "A", N_("Answered")},
+ {'f', 'f', "F", N_("Forwarded")},
+ {-2, 0, NULL, NULL},
+ {'!', '!', "!", N_("Not")},
+ {-2, 0, NULL, NULL},
+ {'r', 'r', "R", N_("Recent")},
+ {'u', 'u', "U", N_("Unseen")},
+ {-1, 0, NULL, NULL}
+};
+
+
+static ESCKEY_S sel_date_opt[] = {
+ {0, 0, NULL, NULL},
+ /* TRANSLATORS: options when selecting messages by Date */
+ {ctrl('P'), 12, "^P", N_("Prev Day")},
+ {ctrl('N'), 13, "^N", N_("Next Day")},
+ {ctrl('X'), 11, "^X", N_("Cur Msg")},
+ {ctrl('W'), 14, "^W", N_("Toggle When")},
+ {KEY_UP, 12, "", ""},
+ {KEY_DOWN, 13, "", ""},
+ {-1, 0, NULL, NULL}
+};
+
+
+static char *sel_text =
+ N_("Select based on To, From, Cc, Recip, Partic, Subject fields or All msg text ? ");
+static char *sel_text_not =
+ N_("Select based on NOT To, From, Cc, Recip, Partic, Subject or All msg text ? ");
+static ESCKEY_S sel_text_opt[] = {
+ /* TRANSLATORS: Select messages based on the text contained in the From line, or
+ the Subject line, and so on. */
+ {'f', 'f', "F", N_("From")},
+ {'s', 's', "S", N_("Subject")},
+ {'t', 't', "T", N_("To")},
+ {'a', 'a', "A", N_("All Text")},
+ {'c', 'c', "C", N_("Cc")},
+ {'!', '!', "!", N_("Not")},
+ {'r', 'r', "R", N_("Recipient")},
+ {'p', 'p', "P", N_("Participant")},
+ {'b', 'b', "B", N_("Body")},
+ {'h', 'h', "H", N_("Header")},
+ {-1, 0, NULL, NULL}
+};
+
+static ESCKEY_S choose_action[] = {
+ {'c', 'c', "C", N_("Compose")},
+ {'r', 'r', "R", N_("Reply")},
+ {'f', 'f', "F", N_("Forward")},
+ {'b', 'b', "B", N_("Bounce")},
+ {-1, 0, NULL, NULL}
+};
+
+static char *select_num =
+ N_("Enter comma-delimited list of numbers (dash between ranges): ");
+
+static char *select_size_larger_msg =
+ N_("Select messages with size larger than: ");
+
+static char *select_size_smaller_msg =
+ N_("Select messages with size smaller than: ");
+
+static char *sel_size_larger = N_("Larger");
+static char *sel_size_smaller = N_("Smaller");
+static ESCKEY_S sel_size_opt[] = {
+ {0, 0, NULL, NULL},
+ {ctrl('W'), 14, "^W", NULL},
+ {-1, 0, NULL, NULL}
+};
+
+static ESCKEY_S sel_key_opt[] = {
+ {0, 0, NULL, NULL},
+ {ctrl('T'), 14, "^T", N_("To List")},
+ {0, 0, NULL, NULL},
+ {'!', '!', "!", N_("Not")},
+ {-1, 0, NULL, NULL}
+};
+
+static ESCKEY_S flag_text_opt[] = {
+ /* TRANSLATORS: these are types of flags (markers) that the user can
+ set. For example, they can flag the message as an important message. */
+ {'n', 'n', "N", N_("New")},
+ {'*', '*', "*", N_("Important")},
+ {'d', 'd', "D", N_("Deleted")},
+ {'a', 'a', "A", N_("Answered")},
+ {'f', 'f', "F", N_("Forwarded")},
+ {'!', '!', "!", N_("Not")},
+ {ctrl('T'), 10, "^T", N_("To Flag Details")},
+ {-1, 0, NULL, NULL}
+};
+
+
+/*----------------------------------------------------------------------
+ The giant switch on the commands for index and viewing
+
+ Input: command -- The command char/code
+ in_index -- flag indicating command is from index
+ orig_command -- The original command typed before pre-processing
+ Output: force_mailchk -- Set to tell caller to force call to new_mail().
+
+ Result: Manifold
+
+ Returns 1 if the message number or attachment to show changed
+ ---*/
+int
+process_cmd(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap,
+ int command, CmdWhere in_index, int *force_mailchk)
+{
+ int question_line, a_changed, flags = 0, ret, j;
+ int notrealinbox;
+ long new_msgno, del_count, old_msgno, i;
+ long start;
+ char *newfolder, prompt[MAX_SCREEN_COLS+1];
+ CONTEXT_S *tc;
+ MESSAGECACHE *mc;
+#if defined(DOS) && !defined(_WINDOWS)
+ extern long coreleft();
+#endif
+
+ dprint((4, "\n - process_cmd(cmd=%d) -\n", command));
+
+ question_line = -FOOTER_ROWS(state);
+ state->mangled_screen = 0;
+ state->mangled_footer = 0;
+ state->mangled_header = 0;
+ state->next_screen = SCREEN_FUN_NULL;
+ old_msgno = mn_get_cur(msgmap);
+ a_changed = FALSE;
+ *force_mailchk = 0;
+
+ switch (command) {
+ /*------------- Help --------*/
+ case MC_HELP :
+ /*
+ * We're not using the h_mail_view portion of this right now because
+ * that call is being handled in scrolltool() before it gets
+ * here. Leave it in case we change how it works.
+ */
+ helper((in_index == MsgIndx)
+ ? h_mail_index
+ : (in_index == View)
+ ? h_mail_view
+ : h_mail_thread_index,
+ (in_index == MsgIndx)
+ ? _("HELP FOR MESSAGE INDEX")
+ : (in_index == View)
+ ? _("HELP FOR MESSAGE TEXT")
+ : _("HELP FOR THREAD INDEX"),
+ HLPD_NONE);
+ dprint((4,"MAIL_CMD: did help command\n"));
+ state->mangled_screen = 1;
+ break;
+
+
+ /*--------- Return to main menu ------------*/
+ case MC_MAIN :
+ state->next_screen = main_menu_screen;
+ dprint((2,"MAIL_CMD: going back to main menu\n"));
+ break;
+
+
+ /*------- View message text --------*/
+ case MC_VIEW_TEXT :
+view_text:
+ if(any_messages(msgmap, NULL, "to View")){
+ state->next_screen = mail_view_screen;
+ }
+
+ break;
+
+
+ /*------- View attachment --------*/
+ case MC_VIEW_ATCH :
+ state->next_screen = attachment_screen;
+ dprint((2,"MAIL_CMD: going to attachment screen\n"));
+ break;
+
+
+ /*---------- Previous message ----------*/
+ case MC_PREVITEM :
+ if(any_messages(msgmap, NULL, NULL)){
+ if((i = mn_get_cur(msgmap)) > 1L){
+ mn_dec_cur(stream, msgmap,
+ (in_index == View && THREADING()
+ && sp_viewing_a_thread(stream))
+ ? MH_THISTHD
+ : (in_index == View)
+ ? MH_ANYTHD : MH_NONE);
+ if(i == mn_get_cur(msgmap)){
+ PINETHRD_S *thrd, *topthrd;
+
+ if(THRD_INDX_ENABLED()){
+ mn_dec_cur(stream, msgmap, MH_ANYTHD);
+ if(i == mn_get_cur(msgmap))
+ q_status_message1(SM_ORDER, 0, 2,
+ _("Already on first %s in Zoomed Index"),
+ THRD_INDX() ? _("thread") : _("message"));
+ else{
+ if(in_index == View
+ || F_ON(F_NEXT_THRD_WO_CONFIRM, state))
+ ret = 'y';
+ else
+ ret = want_to(_("View previous thread"), 'y', 'x',
+ NO_HELP, WT_NORM);
+
+ if(ret == 'y'){
+ q_status_message(SM_ORDER, 0, 2,
+ _("Viewing previous thread"));
+ new_msgno = mn_get_cur(msgmap);
+ mn_set_cur(msgmap, i);
+ if(unview_thread(state, stream, msgmap)){
+ state->next_screen = mail_index_screen;
+ state->view_skipped_index = 0;
+ state->mangled_screen = 1;
+ }
+
+ mn_set_cur(msgmap, new_msgno);
+ if(THRD_AUTO_VIEW() && in_index == View){
+
+ thrd = fetch_thread(stream,
+ mn_m2raw(msgmap,
+ new_msgno));
+ if(count_lflags_in_thread(stream, thrd,
+ msgmap,
+ MN_NONE) == 1){
+ if(view_thread(state, stream, msgmap, 1)){
+ if(current_index_state)
+ msgmap->top_after_thrd = current_index_state->msg_at_top;
+
+ state->view_skipped_index = 1;
+ command = MC_VIEW_TEXT;
+ goto view_text;
+ }
+ }
+ }
+
+ j = 0;
+ if(THRD_AUTO_VIEW() && in_index != View){
+ thrd = fetch_thread(stream, mn_m2raw(msgmap, new_msgno));
+ if(thrd && thrd->top)
+ topthrd = fetch_thread(stream, thrd->top);
+
+ if(topthrd)
+ j = count_lflags_in_thread(stream, topthrd, msgmap, MN_NONE);
+ }
+
+ if(!THRD_AUTO_VIEW() || in_index == View || j != 1){
+ if(view_thread(state, stream, msgmap, 1)
+ && current_index_state)
+ msgmap->top_after_thrd = current_index_state->msg_at_top;
+
+ }
+
+ state->next_screen = SCREEN_FUN_NULL;
+ }
+ else
+ mn_set_cur(msgmap, i); /* put it back */
+ }
+ }
+ else
+ q_status_message1(SM_ORDER, 0, 2,
+ _("Already on first %s in Zoomed Index"),
+ THRD_INDX() ? _("thread") : _("message"));
+ }
+ }
+ else{
+ time_t now;
+
+ if(!IS_NEWS(stream)
+ && ((now = time(0)) > state->last_nextitem_forcechk)){
+ *force_mailchk = 1;
+ /* check at most once a second */
+ state->last_nextitem_forcechk = now;
+ }
+
+ q_status_message1(SM_ORDER, 0, 1, _("Already on first %s"),
+ THRD_INDX() ? _("thread") : _("message"));
+ }
+ }
+
+ break;
+
+
+ /*---------- Next Message ----------*/
+ case MC_NEXTITEM :
+ if(mn_get_total(msgmap) > 0L
+ && ((i = mn_get_cur(msgmap)) < mn_get_total(msgmap))){
+ mn_inc_cur(stream, msgmap,
+ (in_index == View && THREADING()
+ && sp_viewing_a_thread(stream))
+ ? MH_THISTHD
+ : (in_index == View)
+ ? MH_ANYTHD : MH_NONE);
+ if(i == mn_get_cur(msgmap)){
+ PINETHRD_S *thrd, *topthrd;
+
+ if(THRD_INDX_ENABLED()){
+ if(!THRD_INDX())
+ mn_inc_cur(stream, msgmap, MH_ANYTHD);
+
+ if(i == mn_get_cur(msgmap)){
+ if(any_lflagged(msgmap, MN_HIDE))
+ any_messages(NULL, "more", "in Zoomed Index");
+ else
+ goto nfolder;
+ }
+ else{
+ if(in_index == View
+ || F_ON(F_NEXT_THRD_WO_CONFIRM, state))
+ ret = 'y';
+ else
+ ret = want_to(_("View next thread"), 'y', 'x',
+ NO_HELP, WT_NORM);
+
+ if(ret == 'y'){
+ q_status_message(SM_ORDER, 0, 2,
+ _("Viewing next thread"));
+ new_msgno = mn_get_cur(msgmap);
+ mn_set_cur(msgmap, i);
+ if(unview_thread(state, stream, msgmap)){
+ state->next_screen = mail_index_screen;
+ state->view_skipped_index = 0;
+ state->mangled_screen = 1;
+ }
+
+ mn_set_cur(msgmap, new_msgno);
+ if(THRD_AUTO_VIEW() && in_index == View){
+
+ thrd = fetch_thread(stream,
+ mn_m2raw(msgmap,
+ new_msgno));
+ if(count_lflags_in_thread(stream, thrd,
+ msgmap,
+ MN_NONE) == 1){
+ if(view_thread(state, stream, msgmap, 1)){
+ if(current_index_state)
+ msgmap->top_after_thrd = current_index_state->msg_at_top;
+
+ state->view_skipped_index = 1;
+ command = MC_VIEW_TEXT;
+ goto view_text;
+ }
+ }
+ }
+
+ j = 0;
+ if(THRD_AUTO_VIEW() && in_index != View){
+ thrd = fetch_thread(stream, mn_m2raw(msgmap, new_msgno));
+ if(thrd && thrd->top)
+ topthrd = fetch_thread(stream, thrd->top);
+
+ if(topthrd)
+ j = count_lflags_in_thread(stream, topthrd, msgmap, MN_NONE);
+ }
+
+ if(!THRD_AUTO_VIEW() || in_index == View || j != 1){
+ if(view_thread(state, stream, msgmap, 1)
+ && current_index_state)
+ msgmap->top_after_thrd = current_index_state->msg_at_top;
+
+ }
+
+ state->next_screen = SCREEN_FUN_NULL;
+ }
+ else
+ mn_set_cur(msgmap, i); /* put it back */
+ }
+ }
+ else if(THREADING()
+ && (thrd = fetch_thread(stream, mn_m2raw(msgmap, i)))
+ && thrd->next
+ && get_lflag(stream, NULL, thrd->rawno, MN_COLL)){
+ q_status_message(SM_ORDER, 0, 2,
+ _("Expand collapsed thread to see more messages"));
+ }
+ else
+ any_messages(NULL, "more", "in Zoomed Index");
+ }
+ }
+ else{
+ time_t now;
+nfolder:
+ prompt[0] = '\0';
+ if(IS_NEWS(stream)
+ || (state->context_current->use & CNTXT_INCMNG)){
+ char nextfolder[MAXPATH];
+
+ strncpy(nextfolder, state->cur_folder, sizeof(nextfolder));
+ nextfolder[sizeof(nextfolder)-1] = '\0';
+ if(next_folder(NULL, nextfolder, sizeof(nextfolder), nextfolder,
+ state->context_current, NULL, NULL))
+ strncpy(prompt, _(". Press TAB for next folder."),
+ sizeof(prompt));
+ else
+ strncpy(prompt, _(". No more folders to TAB to."),
+ sizeof(prompt));
+
+ prompt[sizeof(prompt)-1] = '\0';
+ }
+
+ any_messages(NULL, (mn_get_total(msgmap) > 0L) ? "more" : NULL,
+ prompt[0] ? prompt : NULL);
+
+ if(!IS_NEWS(stream)
+ && ((now = time(0)) > state->last_nextitem_forcechk)){
+ *force_mailchk = 1;
+ /* check at most once a second */
+ state->last_nextitem_forcechk = now;
+ }
+ }
+
+ break;
+
+
+ /*---------- Delete message ----------*/
+ case MC_DELETE :
+ (void) cmd_delete(state, msgmap, MCMD_NONE,
+ (in_index == View) ? cmd_delete_view : cmd_delete_index);
+ break;
+
+
+ /*---------- Undelete message ----------*/
+ case MC_UNDELETE :
+ (void) cmd_undelete(state, msgmap, MCMD_NONE);
+ update_titlebar_status();
+ break;
+
+
+ /*---------- Reply to message ----------*/
+ case MC_REPLY :
+ (void) cmd_reply(state, msgmap, MCMD_NONE);
+ break;
+
+
+ /*---------- Forward message ----------*/
+ case MC_FORWARD :
+ (void) cmd_forward(state, msgmap, MCMD_NONE);
+ break;
+
+
+ /*---------- Quit pine ------------*/
+ case MC_QUIT :
+ state->next_screen = quit_screen;
+ dprint((1,"MAIL_CMD: quit\n"));
+ break;
+
+
+ /*---------- Compose message ----------*/
+ case MC_COMPOSE :
+ state->prev_screen = (in_index == View) ? mail_view_screen
+ : mail_index_screen;
+ compose_screen(state);
+ state->mangled_screen = 1;
+ if (state->next_screen)
+ a_changed = TRUE;
+ break;
+
+
+ /*---------- Alt Compose message ----------*/
+ case MC_ROLE :
+ state->prev_screen = (in_index == View) ? mail_view_screen
+ : mail_index_screen;
+ role_compose(state);
+ if(state->next_screen)
+ a_changed = TRUE;
+
+ break;
+
+
+ /*--------- Folders menu ------------*/
+ case MC_FOLDERS :
+ state->start_in_context = 1;
+
+ /*--------- Top of Folders list menu ------------*/
+ case MC_COLLECTIONS :
+ state->next_screen = folder_screen;
+ dprint((2,"MAIL_CMD: going to folder/collection menu\n"));
+ break;
+
+
+ /*---------- Open specific new folder ----------*/
+ case MC_GOTO :
+ tc = (state->context_last && !NEWS_TEST(state->context_current))
+ ? state->context_last : state->context_current;
+
+ newfolder = broach_folder(question_line, 1, &notrealinbox, &tc);
+ if(newfolder){
+ visit_folder(state, newfolder, tc, NULL, notrealinbox ? 0L : DB_INBOXWOCNTXT);
+ a_changed = TRUE;
+ }
+
+ break;
+
+
+ /*------- Go to Index Screen ----------*/
+ case MC_INDEX :
+ state->next_screen = mail_index_screen;
+ break;
+
+ /*------- Skip to next interesting message -----------*/
+ case MC_TAB :
+ if(THRD_INDX()){
+ PINETHRD_S *thrd;
+
+ /*
+ * If we're in the thread index, start looking after this
+ * thread. We don't want to match something in the current
+ * thread.
+ */
+ start = mn_get_cur(msgmap);
+ thrd = fetch_thread(stream, mn_m2raw(msgmap, mn_get_cur(msgmap)));
+ if(mn_get_revsort(msgmap)){
+ /* if reversed, top of thread is last one before next thread */
+ if(thrd && thrd->top)
+ start = mn_raw2m(msgmap, thrd->top);
+ }
+ else{
+ /* last msg of thread is at the ends of the branches/nexts */
+ while(thrd){
+ start = mn_raw2m(msgmap, thrd->rawno);
+ if(thrd->branch)
+ thrd = fetch_thread(stream, thrd->branch);
+ else if(thrd->next)
+ thrd = fetch_thread(stream, thrd->next);
+ else
+ thrd = NULL;
+ }
+ }
+
+ /*
+ * Flags is 0 in this case because we want to not skip
+ * messages inside of threads so that we can find threads
+ * which have some unseen messages even though the top-level
+ * of the thread is already seen.
+ * If new_msgno ends up being a message which is not visible
+ * because it isn't at the top-level, the current message #
+ * will be adjusted below in adjust_cur.
+ */
+ flags = 0;
+ new_msgno = next_sorted_flagged((F_UNDEL
+ | F_UNSEEN
+ | ((F_ON(F_TAB_TO_NEW,state))
+ ? 0 : F_OR_FLAG)),
+ stream, start, &flags);
+ }
+ else if(THREADING() && sp_viewing_a_thread(stream)){
+ PINETHRD_S *thrd, *topthrd = NULL;
+
+ start = mn_get_cur(msgmap);
+
+ /*
+ * Things are especially complicated when we're viewing_a_thread
+ * from the thread index. First we have to check within the
+ * current thread for a new message. If none is found, then
+ * we search in the next threads and offer to continue in
+ * them. Then we offer to go to the next folder.
+ */
+ flags = NSF_SKIP_CHID;
+ new_msgno = next_sorted_flagged((F_UNDEL
+ | F_UNSEEN
+ | ((F_ON(F_TAB_TO_NEW,state))
+ ? 0 : F_OR_FLAG)),
+ stream, start, &flags);
+ /*
+ * If we found a match then we are done, that is another message
+ * in the current thread index. Otherwise, we have to look
+ * further.
+ */
+ if(!(flags & NSF_FLAG_MATCH)){
+ ret = 'n';
+ while(1){
+
+ flags = 0;
+ new_msgno = next_sorted_flagged((F_UNDEL
+ | F_UNSEEN
+ | ((F_ON(F_TAB_TO_NEW,
+ state))
+ ? 0 : F_OR_FLAG)),
+ stream, start, &flags);
+ /*
+ * If we got a match, new_msgno is a message in
+ * a different thread from the one we are viewing.
+ */
+ if(flags & NSF_FLAG_MATCH){
+ thrd = fetch_thread(stream, mn_m2raw(msgmap,new_msgno));
+ if(thrd && thrd->top)
+ topthrd = fetch_thread(stream, thrd->top);
+
+ if(F_OFF(F_AUTO_OPEN_NEXT_UNREAD, state)){
+ static ESCKEY_S next_opt[] = {
+ {'y', 'y', "Y", N_("Yes")},
+ {'n', 'n', "N", N_("No")},
+ {TAB, 'n', "Tab", N_("NextNew")},
+ {-1, 0, NULL, NULL}
+ };
+
+ if(in_index)
+ snprintf(prompt, sizeof(prompt), _("View thread number %s? "),
+ topthrd ? comatose(topthrd->thrdno) : "?");
+ else
+ snprintf(prompt, sizeof(prompt),
+ _("View message in thread number %s? "),
+ topthrd ? comatose(topthrd->thrdno) : "?");
+
+ prompt[sizeof(prompt)-1] = '\0';
+
+ ret = radio_buttons(prompt, -FOOTER_ROWS(state),
+ next_opt, 'y', 'x', NO_HELP,
+ RB_NORM);
+ if(ret == 'x'){
+ cmd_cancelled(NULL);
+ goto get_out;
+ }
+ }
+ else
+ ret = 'y';
+
+ if(ret == 'y'){
+ if(unview_thread(state, stream, msgmap)){
+ state->next_screen = mail_index_screen;
+ state->view_skipped_index = 0;
+ state->mangled_screen = 1;
+ }
+
+ mn_set_cur(msgmap, new_msgno);
+ if(THRD_AUTO_VIEW()){
+
+ if(count_lflags_in_thread(stream, topthrd,
+ msgmap, MN_NONE) == 1){
+ if(view_thread(state, stream, msgmap, 1)){
+ if(current_index_state)
+ msgmap->top_after_thrd = current_index_state->msg_at_top;
+
+ state->view_skipped_index = 1;
+ command = MC_VIEW_TEXT;
+ goto view_text;
+ }
+ }
+ }
+
+ if(view_thread(state, stream, msgmap, 1) && current_index_state)
+ msgmap->top_after_thrd = current_index_state->msg_at_top;
+
+ state->next_screen = SCREEN_FUN_NULL;
+ break;
+ }
+ else if(ret == 'n' && topthrd){
+ /*
+ * skip to end of this thread and look starting
+ * in the next thread.
+ */
+ if(mn_get_revsort(msgmap)){
+ /*
+ * if reversed, top of thread is last one
+ * before next thread
+ */
+ start = mn_raw2m(msgmap, topthrd->rawno);
+ }
+ else{
+ /*
+ * last msg of thread is at the ends of
+ * the branches/nexts
+ */
+ thrd = topthrd;
+ while(thrd){
+ start = mn_raw2m(msgmap, thrd->rawno);
+ if(thrd->branch)
+ thrd = fetch_thread(stream, thrd->branch);
+ else if(thrd->next)
+ thrd = fetch_thread(stream, thrd->next);
+ else
+ thrd = NULL;
+ }
+ }
+ }
+ else if(ret == 'n')
+ break;
+ }
+ else
+ break;
+ }
+ }
+ }
+ else{
+
+ start = mn_get_cur(msgmap);
+
+ /*
+ * If we are on a collapsed thread, start looking after the
+ * collapsed part, unless we are viewing the message.
+ */
+ if(THREADING() && in_index != View){
+ PINETHRD_S *thrd;
+ long rawno;
+ int collapsed;
+
+ rawno = mn_m2raw(msgmap, start);
+ thrd = fetch_thread(stream, rawno);
+ collapsed = thrd && thrd->next
+ && get_lflag(stream, NULL, rawno, MN_COLL);
+
+ if(collapsed){
+ if(mn_get_revsort(msgmap)){
+ if(thrd && thrd->top)
+ start = mn_raw2m(msgmap, thrd->top);
+ }
+ else{
+ while(thrd){
+ start = mn_raw2m(msgmap, thrd->rawno);
+ if(thrd->branch)
+ thrd = fetch_thread(stream, thrd->branch);
+ else if(thrd->next)
+ thrd = fetch_thread(stream, thrd->next);
+ else
+ thrd = NULL;
+ }
+ }
+
+ }
+ }
+
+ new_msgno = next_sorted_flagged((F_UNDEL
+ | F_UNSEEN
+ | ((F_ON(F_TAB_TO_NEW,state))
+ ? 0 : F_OR_FLAG)),
+ stream, start, &flags);
+ }
+
+ /*
+ * If there weren't any unread messages left, OR there
+ * aren't any messages at all, we may want to offer to
+ * go on to the next folder...
+ */
+ if(flags & NSF_FLAG_MATCH){
+ mn_set_cur(msgmap, new_msgno);
+ if(in_index != View)
+ adjust_cur_to_visible(stream, msgmap);
+ }
+ else{
+ int in_inbox = sp_flagged(stream, SP_INBOX);
+
+ if(state->context_current
+ && ((NEWS_TEST(state->context_current)
+ && context_isambig(state->cur_folder))
+ || ((state->context_current->use & CNTXT_INCMNG)
+ && (in_inbox
+ || folder_index(state->cur_folder,
+ state->context_current,
+ FI_FOLDER) >= 0)))){
+ char nextfolder[MAXPATH];
+ MAILSTREAM *nextstream = NULL;
+ long recent_cnt;
+ int did_cancel = 0;
+
+ strncpy(nextfolder, state->cur_folder, sizeof(nextfolder));
+ nextfolder[sizeof(nextfolder)-1] = '\0';
+ while(1){
+ if(!(next_folder(&nextstream, nextfolder, sizeof(nextfolder), nextfolder,
+ state->context_current, &recent_cnt,
+ F_ON(F_TAB_NO_CONFIRM,state)
+ ? NULL : &did_cancel))){
+ if(!in_inbox){
+ static ESCKEY_S inbox_opt[] = {
+ {'y', 'y', "Y", N_("Yes")},
+ {'n', 'n', "N", N_("No")},
+ {TAB, 'z', "Tab", N_("To Inbox")},
+ {-1, 0, NULL, NULL}
+ };
+
+ if(F_ON(F_RET_INBOX_NO_CONFIRM,state))
+ ret = 'y';
+ else{
+ /* TRANSLATORS: this is a question, with some information followed
+ by Return to INBOX? */
+ if(state->context_current->use&CNTXT_INCMNG)
+ snprintf(prompt, sizeof(prompt), _("No more incoming folders. Return to \"%s\"? "), state->inbox_name);
+ else
+ snprintf(prompt, sizeof(prompt), _("No more news groups. Return to \"%s\"? "), state->inbox_name);
+
+ ret = radio_buttons(prompt, -FOOTER_ROWS(state),
+ inbox_opt, 'y', 'x',
+ NO_HELP, RB_NORM);
+ }
+
+ /*
+ * 'z' is a synonym for 'y'. It is not 'y'
+ * so that it isn't displayed as a default
+ * action with square-brackets around it
+ * in the keymenu...
+ */
+ if(ret == 'y' || ret == 'z'){
+ visit_folder(state, state->inbox_name,
+ state->context_current,
+ NULL, DB_INBOXWOCNTXT);
+ a_changed = TRUE;
+ }
+ }
+ else if (did_cancel)
+ cmd_cancelled(NULL);
+ else{
+ if(state->context_current->use&CNTXT_INCMNG)
+ q_status_message(SM_ORDER, 0, 2, _("No more incoming folders"));
+ else
+ q_status_message(SM_ORDER, 0, 2, _("No more news groups"));
+ }
+
+ break;
+ }
+
+ {char *front, type[80], cnt[80], fbuf[MAX_SCREEN_COLS/2+1];
+ int rbspace, avail, need, take_back;
+
+ /*
+ * View_next_
+ * Incoming_folder_ or news_group_ or folder_ or group_
+ * "foldername"
+ * _(13 recent) or _(some recent) or nothing
+ * ?_
+ */
+ front = "View next";
+ strncpy(type,
+ (state->context_current->use & CNTXT_INCMNG)
+ ? "Incoming folder" : "news group",
+ sizeof(type));
+ type[sizeof(type)-1] = '\0';
+ snprintf(cnt, sizeof(cnt), " (%.*s %s)", sizeof(cnt)-20,
+ recent_cnt ? long2string(recent_cnt) : "some",
+ F_ON(F_TAB_USES_UNSEEN, ps_global)
+ ? "unseen" : "recent");
+ cnt[sizeof(cnt)-1] = '\0';
+
+ /*
+ * Space reserved for radio_buttons call.
+ * If we make this 3 then radio_buttons won't mess
+ * with the prompt. If we make it 2, then we get
+ * one more character to use but radio_buttons will
+ * cut off the last character of our prompt, which is
+ * ok because it is a space.
+ */
+ rbspace = 2;
+ avail = ps_global->ttyo ? ps_global->ttyo->screen_cols
+ : 80;
+ need = strlen(front)+1 + strlen(type)+1 +
+ + strlen(nextfolder)+2 + strlen(cnt) +
+ 2 + rbspace;
+ if(avail < need){
+ take_back = strlen(type);
+ strncpy(type,
+ (state->context_current->use & CNTXT_INCMNG)
+ ? "folder" : "group", sizeof(type));
+ take_back -= strlen(type);
+ need -= take_back;
+ if(avail < need){
+ need -= strlen(cnt);
+ cnt[0] = '\0';
+ }
+ }
+
+ snprintf(prompt, sizeof(prompt), "%.*s %.*s \"%.*s\"%.*s? ",
+ sizeof(prompt)/8, front,
+ sizeof(prompt)/8, type,
+ sizeof(prompt)/2,
+ short_str(nextfolder, fbuf, sizeof(fbuf),
+ strlen(nextfolder) -
+ ((need>avail) ? (need-avail) : 0),
+ MidDots),
+ sizeof(prompt)/8, cnt);
+ prompt[sizeof(prompt)-1] = '\0';
+ }
+
+ /*
+ * When help gets added, this'll have to become
+ * a loop like the rest...
+ */
+ if(F_OFF(F_AUTO_OPEN_NEXT_UNREAD, state)){
+ static ESCKEY_S next_opt[] = {
+ {'y', 'y', "Y", N_("Yes")},
+ {'n', 'n', "N", N_("No")},
+ {TAB, 'n', "Tab", N_("NextNew")},
+ {-1, 0, NULL, NULL}
+ };
+
+ ret = radio_buttons(prompt, -FOOTER_ROWS(state),
+ next_opt, 'y', 'x', NO_HELP,
+ RB_NORM);
+ if(ret == 'x'){
+ cmd_cancelled(NULL);
+ break;
+ }
+ }
+ else
+ ret = 'y';
+
+ if(ret == 'y'){
+ if(nextstream && sp_dead_stream(nextstream))
+ nextstream = NULL;
+
+ visit_folder(state, nextfolder,
+ state->context_current, nextstream,
+ DB_FROMTAB);
+ /* visit_folder takes care of nextstream */
+ nextstream = NULL;
+ a_changed = TRUE;
+ break;
+ }
+ }
+
+ if(nextstream)
+ pine_mail_close(nextstream);
+ }
+ else
+ any_messages(NULL,
+ (mn_get_total(msgmap) > 0L)
+ ? IS_NEWS(stream) ? "more undeleted" : "more new"
+ : NULL,
+ NULL);
+ }
+
+get_out:
+
+ break;
+
+
+ /*------- Zoom -----------*/
+ case MC_ZOOM :
+ /*
+ * Right now the way zoom is implemented is sort of silly.
+ * There are two per-message flags where just one and a
+ * global "zoom mode" flag to suppress messags from the index
+ * should suffice.
+ */
+ if(any_messages(msgmap, NULL, "to Zoom on")){
+ if(unzoom_index(state, stream, msgmap)){
+ dprint((4, "\n\n ---- Exiting ZOOM mode ----\n"));
+ q_status_message(SM_ORDER,0,2, _("Index Zoom Mode is now off"));
+ }
+ else if((i = zoom_index(state, stream, msgmap, MN_SLCT)) != 0){
+ if(any_lflagged(msgmap, MN_HIDE)){
+ dprint((4,"\n\n ---- Entering ZOOM mode ----\n"));
+ q_status_message4(SM_ORDER, 0, 2,
+ _("In Zoomed Index of %s%s%s%s. Use \"Z\" to restore regular Index"),
+ THRD_INDX() ? "" : comatose(i),
+ THRD_INDX() ? "" : " ",
+ THRD_INDX() ? _("threads") : _("message"),
+ THRD_INDX() ? "" : plural(i));
+ }
+ else
+ q_status_message(SM_ORDER, 0, 2,
+ _("All messages selected, so not entering Index Zoom Mode"));
+ }
+ else
+ any_messages(NULL, "selected", "to Zoom on");
+ }
+
+ break;
+
+
+ /*---------- print message on paper ----------*/
+ case MC_PRINTMSG :
+ if(any_messages(msgmap, NULL, "to print"))
+ (void) cmd_print(state, msgmap, MCMD_NONE, in_index);
+
+ break;
+
+
+ /*---------- Take Address ----------*/
+ case MC_TAKE :
+ if(F_ON(F_ENABLE_ROLE_TAKE, state) ||
+ any_messages(msgmap, NULL, "to Take address from"))
+ (void) cmd_take_addr(state, msgmap, MCMD_NONE);
+
+ break;
+
+
+ /*---------- Save Message ----------*/
+ case MC_SAVE :
+ if(any_messages(msgmap, NULL, "to Save"))
+ (void) cmd_save(state, stream, msgmap, MCMD_NONE, in_index);
+
+ break;
+
+
+ /*---------- Export message ----------*/
+ case MC_EXPORT :
+ if(any_messages(msgmap, NULL, "to Export")){
+ (void) cmd_export(state, msgmap, question_line, MCMD_NONE);
+ state->mangled_footer = 1;
+ }
+
+ break;
+
+
+ /*---------- Expunge ----------*/
+ case MC_EXPUNGE :
+ cmd_expunge(state, stream, msgmap);
+ break;
+
+
+ /*------- Unexclude -----------*/
+ case MC_UNEXCLUDE :
+ if(!(IS_NEWS(stream) && stream->rdonly)){
+ q_status_message(SM_ORDER, 0, 3,
+ _("Unexclude not available for mail folders"));
+ }
+ else if(any_lflagged(msgmap, MN_EXLD)){
+ SEARCHPGM *pgm;
+ long i;
+ int exbits;
+
+ /*
+ * Since excluded means "hidden deleted" and "killed",
+ * the count should reflect the former.
+ */
+ pgm = mail_newsearchpgm();
+ pgm->deleted = 1;
+ pine_mail_search_full(stream, NULL, pgm, SE_NOPREFETCH | SE_FREE);
+ for(i = 1L, del_count = 0L; i <= stream->nmsgs; i++)
+ if((mc = mail_elt(stream, i)) && mc->searched
+ && get_lflag(stream, NULL, i, MN_EXLD)
+ && !(msgno_exceptions(stream, i, "0", &exbits, FALSE)
+ && (exbits & MSG_EX_FILTERED)))
+ del_count++;
+
+ if(del_count > 0L){
+ state->mangled_footer = 1;
+ snprintf(prompt, sizeof(prompt), "UNexclude %ld message%s in %.*s", del_count,
+ plural(del_count), sizeof(prompt)-40,
+ pretty_fn(state->cur_folder));
+ prompt[sizeof(prompt)-1] = '\0';
+ if(F_ON(F_FULL_AUTO_EXPUNGE, state)
+ || (F_ON(F_AUTO_EXPUNGE, state)
+ && (state->context_current
+ && (state->context_current->use & CNTXT_INCMNG))
+ && context_isambig(state->cur_folder))
+ || want_to(prompt, 'y', 0, NO_HELP, WT_NORM) == 'y'){
+ long save_cur_rawno;
+ int were_viewing_a_thread;
+
+ save_cur_rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
+ were_viewing_a_thread = (THREADING()
+ && sp_viewing_a_thread(stream));
+
+ if(msgno_include(stream, msgmap, MI_NONE)){
+ clear_index_cache(stream, 0);
+
+ if(stream && stream->spare)
+ erase_threading_info(stream, msgmap);
+
+ refresh_sort(stream, msgmap, SRT_NON);
+ }
+
+ if(were_viewing_a_thread){
+ if(save_cur_rawno > 0L)
+ mn_set_cur(msgmap, mn_raw2m(msgmap,save_cur_rawno));
+
+ if(view_thread(state, stream, msgmap, 1) && current_index_state)
+ msgmap->top_after_thrd = current_index_state->msg_at_top;
+ }
+
+ if(save_cur_rawno > 0L)
+ mn_set_cur(msgmap, mn_raw2m(msgmap,save_cur_rawno));
+
+ state->mangled_screen = 1;
+ q_status_message2(SM_ORDER, 0, 4,
+ "%s message%s UNexcluded",
+ long2string(del_count),
+ plural(del_count));
+
+ if(in_index != View)
+ adjust_cur_to_visible(stream, msgmap);
+ }
+ else
+ any_messages(NULL, NULL, "UNexcluded");
+ }
+ else
+ any_messages(NULL, "excluded", "to UNexclude");
+ }
+ else
+ any_messages(NULL, "excluded", "to UNexclude");
+
+ break;
+
+
+ /*------- Make Selection -----------*/
+ case MC_SELECT :
+ if(any_messages(msgmap, NULL, "to Select")){
+ if(aggregate_select(state, msgmap, question_line, in_index) == 0
+ && (in_index == MsgIndx || in_index == ThrdIndx)
+ && F_ON(F_AUTO_ZOOM, state)
+ && any_lflagged(msgmap, MN_SLCT) > 0L
+ && !any_lflagged(msgmap, MN_HIDE))
+ (void) zoom_index(state, stream, msgmap, MN_SLCT);
+ }
+
+ break;
+
+
+ /*------- Toggle Current Message Selection State -----------*/
+ case MC_SELCUR :
+ if(any_messages(msgmap, NULL, NULL)){
+ if((select_by_current(state, msgmap, in_index)
+ || (F_OFF(F_UNSELECT_WONT_ADVANCE, state)
+ && !any_lflagged(msgmap, MN_HIDE)))
+ && (i = mn_get_cur(msgmap)) < mn_get_total(msgmap)){
+ /* advance current */
+ mn_inc_cur(stream, msgmap,
+ (in_index == View && THREADING()
+ && sp_viewing_a_thread(stream))
+ ? MH_THISTHD
+ : (in_index == View)
+ ? MH_ANYTHD : MH_NONE);
+ }
+ }
+
+ break;
+
+
+ /*------- Apply command -----------*/
+ case MC_APPLY :
+ if(any_messages(msgmap, NULL, NULL)){
+ if(any_lflagged(msgmap, MN_SLCT) > 0L){
+ if(apply_command(state, stream, msgmap, 0,
+ AC_NONE, question_line)){
+ if(F_ON(F_AUTO_UNSELECT, state)){
+ agg_select_all(stream, msgmap, NULL, 0);
+ unzoom_index(state, stream, msgmap);
+ }
+ else if(F_ON(F_AUTO_UNZOOM, state))
+ unzoom_index(state, stream, msgmap);
+ }
+ }
+ else
+ any_messages(NULL, NULL, "to Apply command to. Try \"Select\"");
+ }
+
+ break;
+
+
+ /*-------- Sort command -------*/
+ case MC_SORT :
+ {
+ int were_threading = THREADING();
+ SortOrder sort = mn_get_sort(msgmap);
+ int rev = mn_get_revsort(msgmap);
+
+ dprint((1,"MAIL_CMD: sort\n"));
+ if(select_sort(state, question_line, &sort, &rev)){
+ /* $ command reinitializes threading collapsed/expanded info */
+ if(SORT_IS_THREADED(msgmap) && !SEP_THRDINDX())
+ erase_threading_info(stream, msgmap);
+
+ if(ps_global && ps_global->ttyo){
+ blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
+ ps_global->mangled_footer = 1;
+ }
+
+ sort_folder(stream, msgmap, sort, rev, SRT_VRB|SRT_MAN);
+ }
+
+ state->mangled_footer = 1;
+
+ /*
+ * We've changed whether we are threading or not so we need to
+ * exit the index and come back in so that we switch between the
+ * thread index and the regular index. Sort_folder will have
+ * reset viewing_a_thread if necessary.
+ */
+ if(SEP_THRDINDX()
+ && ((!were_threading && THREADING())
+ || (were_threading && !THREADING()))){
+ state->next_screen = mail_index_screen;
+ state->mangled_screen = 1;
+ }
+ }
+
+ break;
+
+
+ /*------- Toggle Full Headers -----------*/
+ case MC_FULLHDR :
+ state->full_header++;
+ if(state->full_header == 1){
+ if(!(state->quote_suppression_threshold
+ && (state->some_quoting_was_suppressed || in_index != View)))
+ state->full_header++;
+ }
+ else if(state->full_header > 2)
+ state->full_header = 0;
+
+ switch(state->full_header){
+ case 0:
+ q_status_message(SM_ORDER, 0, 3,
+ _("Display of full headers is now off."));
+ break;
+
+ case 1:
+ q_status_message1(SM_ORDER, 0, 3,
+ _("Quotes displayed, use %s to see full headers"),
+ F_ON(F_USE_FK, state) ? "F9" : "H");
+ break;
+
+ case 2:
+ q_status_message(SM_ORDER, 0, 3,
+ _("Display of full headers is now on."));
+ break;
+
+ }
+
+ a_changed = TRUE;
+ break;
+
+
+ case MC_TOGGLE :
+ a_changed = TRUE;
+ break;
+
+
+#ifdef SMIME
+ /*------- Try to decrypt message -----------*/
+ case MC_DECRYPT:
+ if(state->smime && state->smime->need_passphrase)
+ smime_get_passphrase();
+
+ a_changed = TRUE;
+ break;
+
+ case MC_SECURITY:
+ state->next_screen = smime_info_screen;
+ break;
+#endif
+
+
+ /*------- Bounce -----------*/
+ case MC_BOUNCE :
+ (void) cmd_bounce(state, msgmap, MCMD_NONE);
+ break;
+
+
+ /*------- Flag -----------*/
+ case MC_FLAG :
+ dprint((4, "\n - flag message -\n"));
+ (void) cmd_flag(state, msgmap, MCMD_NONE);
+ break;
+
+
+ /*------- Pipe message -----------*/
+ case MC_PIPE :
+ (void) cmd_pipe(state, msgmap, MCMD_NONE);
+ break;
+
+
+ /*--------- Default, unknown command ----------*/
+ default:
+ panic("Unexpected command case");
+ break;
+ }
+
+ return((a_changed || mn_get_cur(msgmap) != old_msgno) ? 1 : 0);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Map some of the special characters into sensible strings for human
+ consumption.
+ c is a UCS-4 character!
+ ----*/
+char *
+pretty_command(UCS c)
+{
+ static char buf[10];
+ char *s;
+
+ buf[0] = '\0';
+ s = buf;
+
+ switch(c){
+ case ' ' : s = "SPACE"; break;
+ case '\033' : s = "ESC"; break;
+ case '\177' : s = "DEL"; break;
+ case ctrl('I') : s = "TAB"; break;
+ case ctrl('J') : s = "LINEFEED"; break;
+ case ctrl('M') : s = "RETURN"; break;
+ case ctrl('Q') : s = "XON"; break;
+ case ctrl('S') : s = "XOFF"; break;
+ case KEY_UP : s = "Up Arrow"; break;
+ case KEY_DOWN : s = "Down Arrow"; break;
+ case KEY_RIGHT : s = "Right Arrow"; break;
+ case KEY_LEFT : s = "Left Arrow"; break;
+ case KEY_PGUP : s = "Prev Page"; break;
+ case KEY_PGDN : s = "Next Page"; break;
+ case KEY_HOME : s = "Home"; break;
+ case KEY_END : s = "End"; break;
+ case KEY_DEL : s = "Delete"; break; /* Not necessary DEL! */
+ case KEY_JUNK : s = "Junk!"; break;
+ case BADESC : s = "Bad Esc"; break;
+ case NO_OP_IDLE : s = "NO_OP_IDLE"; break;
+ case NO_OP_COMMAND : s = "NO_OP_COMMAND"; break;
+ case KEY_RESIZE : s = "KEY_RESIZE"; break;
+ case KEY_UTF8 : s = "KEY_UTF8"; break;
+ case KEY_MOUSE : s = "KEY_MOUSE"; break;
+ case KEY_SCRLUPL : s = "KEY_SCRLUPL"; break;
+ case KEY_SCRLDNL : s = "KEY_SCRLDNL"; break;
+ case KEY_SCRLTO : s = "KEY_SCRLTO"; break;
+ case KEY_XTERM_MOUSE : s = "KEY_XTERM_MOUSE"; break;
+ case KEY_DOUBLE_ESC : s = "KEY_DOUBLE_ESC"; break;
+ case CTRL_KEY_UP : s = "Ctrl Up Arrow"; break;
+ case CTRL_KEY_DOWN : s = "Ctrl Down Arrow"; break;
+ case CTRL_KEY_RIGHT : s = "Ctrl Right Arrow"; break;
+ case CTRL_KEY_LEFT : s = "Ctrl Left Arrow"; break;
+ case PF1 :
+ case PF2 :
+ case PF3 :
+ case PF4 :
+ case PF5 :
+ case PF6 :
+ case PF7 :
+ case PF8 :
+ case PF9 :
+ case PF10 :
+ case PF11 :
+ case PF12 :
+ snprintf(s = buf, sizeof(buf), "F%ld", (long) (c - PF1 + 1));
+ break;
+
+ default:
+ if(c < ' ' || (c >= 0x80 && c < 0xA0)){
+ char d;
+ int c1;
+
+ c1 = (c >= 0x80);
+ d = (c & 0x1f) + 'A' - 1;
+ snprintf(s = buf, sizeof(buf), "%c%c", c1 ? '~' : '^', d);
+ }
+ else{
+ memset(buf, 0, sizeof(buf));
+ utf8_put((unsigned char *) buf, (unsigned long) c);
+ }
+
+ break;
+ }
+
+ return(s);
+}
+
+
+/*----------------------------------------------------------------------
+ Complain about bogus input
+
+ Args: ch -- input command to complain about
+ help -- string indicating where to get help
+
+ ----*/
+void
+bogus_command(UCS cmd, char *help)
+{
+ if(cmd == ctrl('Q') || cmd == ctrl('S'))
+ q_status_message1(SM_ASYNC, 0, 2,
+ "%s char received. Set \"preserve-start-stop\" feature in Setup/Config.",
+ pretty_command(cmd));
+ else if(cmd == KEY_JUNK)
+ q_status_message3(SM_ORDER, 0, 2,
+ "Invalid key pressed.%s%s%s",
+ (help) ? " Use " : "",
+ (help) ? help : "",
+ (help) ? " for help" : "");
+ else
+ q_status_message4(SM_ORDER, 0, 2,
+ "Command \"%s\" not defined for this screen.%s%s%s",
+ pretty_command(cmd),
+ (help) ? " Use " : "",
+ (help) ? help : "",
+ (help) ? " for help" : "");
+}
+
+
+void
+bogus_utf8_command(char *cmd, char *help)
+{
+ q_status_message4(SM_ORDER, 0, 2,
+ "Command \"%s\" not defined for this screen.%s%s%s",
+ cmd ? cmd : "?",
+ (help) ? " Use " : "",
+ (help) ? help : "",
+ (help) ? " for help" : "");
+}
+
+
+/*----------------------------------------------------------------------
+ Execute FLAG message command
+
+ Args: state -- Various satate info
+ msgmap -- map of c-client to local message numbers
+
+ Result: with side effect of "current" message FLAG flag set or UNset
+
+ ----*/
+int
+cmd_flag(struct pine *state, MSGNO_S *msgmap, int aopt)
+{
+ char *flagit, *seq, *screen_text[20], **exp, **p, *answer = NULL;
+ char *keyword_array[2];
+ int user_defined_flags = 0, mailbox_flags = 0;
+ int directly_to_maint_screen = 0;
+ long unflagged, flagged, flags, rawno;
+ MESSAGECACHE *mc = NULL;
+ KEYWORD_S *kw;
+ int i, cnt, is_set, trouble = 0, rv = 0;
+ size_t len;
+ struct flag_table *fp, *ftbl = NULL;
+ struct flag_screen flag_screen;
+ static char *flag_screen_text1[] = {
+ N_(" Set desired flags for current message below. An 'X' means set"),
+ N_(" it, and a ' ' means to unset it. Choose \"Exit\" when finished."),
+ NULL
+ };
+
+ static char *flag_screen_text2[] = {
+ N_(" Set desired flags below for selected messages. A '?' means to"),
+ N_(" leave the flag unchanged, 'X' means to set it, and a ' ' means"),
+ N_(" to unset it. Use the \"Return\" key to toggle, and choose"),
+ N_(" \"Exit\" when finished."),
+ NULL
+ };
+
+ static struct flag_table default_ftbl[] = {
+ {N_("Important"), h_flag_important, F_FLAG, 0, 0, NULL, NULL},
+ {N_("New"), h_flag_new, F_SEEN, 0, 0, NULL, NULL},
+ {N_("Answered"), h_flag_answered, F_ANS, 0, 0, NULL, NULL},
+ {N_("Forwarded"), h_flag_forwarded, F_FWD, 0, 0, NULL, NULL},
+ {N_("Deleted"), h_flag_deleted, F_DEL, 0, 0, NULL, NULL},
+ {NULL, NO_HELP, 0, 0, 0, NULL, NULL}
+ };
+
+ /* Only check for dead stream for now. Should check permanent flags
+ * eventually
+ */
+ if(!(any_messages(msgmap, NULL, "to Flag") && can_set_flag(state, "flag", 1)))
+ return rv;
+
+ if(sp_io_error_on_stream(state->mail_stream)){
+ sp_set_io_error_on_stream(state->mail_stream, 0);
+ pine_mail_check(state->mail_stream); /* forces write */
+ return rv;
+ }
+
+go_again:
+ answer = NULL;
+ user_defined_flags = 0;
+ mailbox_flags = 0;
+ mc = NULL;
+ trouble = 0;
+ ftbl = NULL;
+
+ /* count how large ftbl will be */
+ for(cnt = 0; default_ftbl[cnt].name; cnt++)
+ ;
+
+ /* add user flags */
+ for(kw = ps_global->keywords; kw; kw = kw->next){
+ if(!((kw->nick && !strucmp(FORWARDED_FLAG, kw->nick)) || (kw->kw && !strucmp(FORWARDED_FLAG, kw->kw)))){
+ user_defined_flags++;
+ cnt++;
+ }
+ }
+
+ /*
+ * Add mailbox flags that aren't user-defined flags.
+ * Don't consider it if it matches either one of our defined
+ * keywords or one of our defined nicknames for a keyword.
+ */
+ for(i = 0; stream_to_user_flag_name(state->mail_stream, i); i++){
+ char *q;
+
+ q = stream_to_user_flag_name(state->mail_stream, i);
+ if(q && q[0]){
+ for(kw = ps_global->keywords; kw; kw = kw->next){
+ if((kw->nick && !strucmp(kw->nick, q)) || (kw->kw && !strucmp(kw->kw, q)))
+ break;
+ }
+ }
+
+ if(!kw && !(q && !strucmp(FORWARDED_FLAG, q))){
+ mailbox_flags++;
+ cnt++;
+ }
+ }
+
+ cnt += (user_defined_flags ? 2 : 0) + (mailbox_flags ? 2 : 0);
+
+ /* set up ftbl, first the system flags */
+ ftbl = (struct flag_table *) fs_get((cnt+1) * sizeof(*ftbl));
+ memset(ftbl, 0, (cnt+1) * sizeof(*ftbl));
+ for(i = 0, fp = ftbl; default_ftbl[i].name; i++, fp++){
+ fp->name = cpystr(default_ftbl[i].name);
+ fp->help = default_ftbl[i].help;
+ fp->flag = default_ftbl[i].flag;
+ fp->set = default_ftbl[i].set;
+ fp->ukn = default_ftbl[i].ukn;
+ }
+
+ if(user_defined_flags){
+ fp->flag = F_COMMENT;
+ fp->name = cpystr("");
+ fp++;
+ fp->flag = F_COMMENT;
+ len = strlen(_("User-defined Keywords from Setup/Config"));
+ fp->name = (char *) fs_get((len+6+6+1) * sizeof(char));
+ snprintf(fp->name, len+6+6+1, "----- %s -----", _("User-defined Keywords from Setup/Config"));
+ fp++;
+ }
+
+ /* then the user-defined keywords */
+ if(user_defined_flags)
+ for(kw = ps_global->keywords; kw; kw = kw->next){
+ if(!((kw->nick && !strucmp(FORWARDED_FLAG, kw->nick))
+ || (kw->kw && !strucmp(FORWARDED_FLAG, kw->kw)))){
+ fp->name = cpystr(kw->nick ? kw->nick : kw->kw ? kw->kw : "");
+ fp->keyword = cpystr(kw->kw ? kw->kw : "");
+ if(kw->nick && kw->kw){
+ size_t l;
+
+ l = strlen(kw->kw)+2;
+ fp->comment = (char *) fs_get((l+1) * sizeof(char));
+ snprintf(fp->comment, l+1, "(%.*s)", strlen(kw->kw), kw->kw);
+ fp->comment[l] = '\0';
+ }
+
+ fp->help = h_flag_user_flag;
+ fp->flag = F_KEYWORD;
+ fp->set = 0;
+ fp->ukn = 0;
+ fp++;
+ }
+ }
+
+ if(mailbox_flags){
+ fp->flag = F_COMMENT;
+ fp->name = cpystr("");
+ fp++;
+ fp->flag = F_COMMENT;
+ len = strlen(_("Other keywords in the mailbox that are not user-defined"));
+ fp->name = (char *) fs_get((len+6+6+1) * sizeof(char));
+ snprintf(fp->name, len+6+6+1, "----- %s -----", _("Other keywords in the mailbox that are not user-defined"));
+ fp++;
+ }
+
+ /* then the extra mailbox-defined keywords */
+ if(mailbox_flags)
+ for(i = 0; stream_to_user_flag_name(state->mail_stream, i); i++){
+ char *q;
+
+ q = stream_to_user_flag_name(state->mail_stream, i);
+ if(q && q[0]){
+ for(kw = ps_global->keywords; kw; kw = kw->next){
+ if((kw->nick && !strucmp(kw->nick, q)) || (kw->kw && !strucmp(kw->kw, q)))
+ break;
+ }
+ }
+
+ if(!kw && !(q && !strucmp(FORWARDED_FLAG, q))){
+ fp->name = cpystr(q);
+ fp->keyword = cpystr(q);
+ fp->help = h_flag_user_flag;
+ fp->flag = F_KEYWORD;
+ fp->set = 0;
+ fp->ukn = 0;
+ fp++;
+ }
+ }
+
+ flag_screen.flag_table = &ftbl;
+ flag_screen.explanation = screen_text;
+
+ if(MCMD_ISAGG(aopt)){
+ if(!pseudo_selected(ps_global->mail_stream, msgmap)){
+ free_flag_table(&ftbl);
+ return rv;
+ }
+
+ exp = flag_screen_text2;
+ for(fp = ftbl; fp->name; fp++){
+ fp->set = CMD_FLAG_UNKN; /* set to unknown */
+ fp->ukn = TRUE;
+ }
+ }
+ else if(state->mail_stream
+ && (rawno = mn_m2raw(msgmap, mn_get_cur(msgmap))) > 0L
+ && rawno <= state->mail_stream->nmsgs
+ && (mc = mail_elt(state->mail_stream, rawno))){
+ exp = flag_screen_text1;
+ for(fp = &ftbl[0]; fp->name; fp++){
+ fp->ukn = 0;
+ if(fp->flag == F_KEYWORD){
+ /* see if this keyword is defined for this message */
+ fp->set = CMD_FLAG_CLEAR;
+ if(user_flag_is_set(state->mail_stream,
+ rawno, fp->keyword))
+ fp->set = CMD_FLAG_SET;
+ }
+ else if(fp->flag == F_FWD){
+ /* see if forwarded keyword is defined for this message */
+ fp->set = CMD_FLAG_CLEAR;
+ if(user_flag_is_set(state->mail_stream,
+ rawno, FORWARDED_FLAG))
+ fp->set = CMD_FLAG_SET;
+ }
+ else if(fp->flag != F_COMMENT)
+ fp->set = ((fp->flag == F_SEEN && !mc->seen)
+ || (fp->flag == F_DEL && mc->deleted)
+ || (fp->flag == F_FLAG && mc->flagged)
+ || (fp->flag == F_ANS && mc->answered))
+ ? CMD_FLAG_SET : CMD_FLAG_CLEAR;
+ }
+ }
+ else{
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Error accessing message data"));
+ free_flag_table(&ftbl);
+ return rv;
+ }
+
+ if(directly_to_maint_screen)
+ goto the_maint_screen;
+
+#ifdef _WINDOWS
+ if (mswin_usedialog ()) {
+ if (!os_flagmsgdialog (&ftbl[0])){
+ free_flag_table(&ftbl);
+ return rv;
+ }
+ }
+ else
+#endif
+ {
+ int use_maint_screen;
+ int keyword_shortcut = 0;
+
+ use_maint_screen = F_ON(F_FLAG_SCREEN_DFLT, ps_global);
+
+ if(!use_maint_screen){
+ /*
+ * We're going to call cmd_flag_prompt(). We need
+ * to decide whether or not to offer the keyword setting
+ * shortcut. We'll offer it if the user has the feature
+ * enabled AND there are some possible keywords that could
+ * be set.
+ */
+ if(F_ON(F_FLAG_SCREEN_KW_SHORTCUT, ps_global)){
+ for(fp = &ftbl[0]; fp->name && !keyword_shortcut; fp++){
+ if(fp->flag == F_KEYWORD){
+ int first_char;
+ ESCKEY_S *tp;
+
+ first_char = (fp->name && fp->name[0])
+ ? fp->name[0] : -2;
+ if(isascii(first_char) && isupper(first_char))
+ first_char = tolower((unsigned char) first_char);
+
+ for(tp=flag_text_opt; tp->ch != -1; tp++)
+ if(tp->ch == first_char)
+ break;
+
+ if(tp->ch == -1)
+ keyword_shortcut++;
+ }
+ }
+ }
+
+ use_maint_screen = !cmd_flag_prompt(state, &flag_screen,
+ keyword_shortcut);
+ }
+
+the_maint_screen:
+ if(use_maint_screen){
+ for(p = &screen_text[0]; *exp; p++, exp++)
+ *p = *exp;
+
+ *p = NULL;
+
+ directly_to_maint_screen = flag_maintenance_screen(state, &flag_screen);
+ }
+ }
+
+ /* reaquire the elt pointer */
+ mc = (state->mail_stream
+ && (rawno = mn_m2raw(msgmap, mn_get_cur(msgmap))) > 0L
+ && rawno <= state->mail_stream->nmsgs)
+ ? mail_elt(state->mail_stream, rawno) : NULL;
+
+ for(fp = ftbl; mc && fp->name; fp++){
+ flags = -1;
+ switch(fp->flag){
+ case F_SEEN:
+ if((!MCMD_ISAGG(aopt) && fp->set != !mc->seen)
+ || (MCMD_ISAGG(aopt) && fp->set != CMD_FLAG_UNKN)){
+ flagit = "\\SEEN";
+ if(fp->set){
+ flags = 0L;
+ unflagged = F_SEEN;
+ }
+ else{
+ flags = ST_SET;
+ unflagged = F_UNSEEN;
+ }
+ }
+
+ break;
+
+ case F_ANS:
+ if((!MCMD_ISAGG(aopt) && fp->set != mc->answered)
+ || (MCMD_ISAGG(aopt) && fp->set != CMD_FLAG_UNKN)){
+ flagit = "\\ANSWERED";
+ if(fp->set){
+ flags = ST_SET;
+ unflagged = F_UNANS;
+ }
+ else{
+ flags = 0L;
+ unflagged = F_ANS;
+ }
+ }
+
+ break;
+
+ case F_DEL:
+ if((!MCMD_ISAGG(aopt) && fp->set != mc->deleted)
+ || (MCMD_ISAGG(aopt) && fp->set != CMD_FLAG_UNKN)){
+ flagit = "\\DELETED";
+ if(fp->set){
+ flags = ST_SET;
+ unflagged = F_UNDEL;
+ }
+ else{
+ flags = 0L;
+ unflagged = F_DEL;
+ }
+ }
+
+ break;
+
+ case F_FLAG:
+ if((!MCMD_ISAGG(aopt) && fp->set != mc->flagged)
+ || (MCMD_ISAGG(aopt) && fp->set != CMD_FLAG_UNKN)){
+ flagit = "\\FLAGGED";
+ if(fp->set){
+ flags = ST_SET;
+ unflagged = F_UNFLAG;
+ }
+ else{
+ flags = 0L;
+ unflagged = F_FLAG;
+ }
+ }
+
+ break;
+
+ case F_FWD :
+ if(!MCMD_ISAGG(aopt)){
+ /* see if forwarded is defined for this message */
+ is_set = CMD_FLAG_CLEAR;
+ if(user_flag_is_set(state->mail_stream,
+ mn_m2raw(msgmap, mn_get_cur(msgmap)),
+ FORWARDED_FLAG))
+ is_set = CMD_FLAG_SET;
+ }
+
+ if((!MCMD_ISAGG(aopt) && fp->set != is_set)
+ || (MCMD_ISAGG(aopt) && fp->set != CMD_FLAG_UNKN)){
+ flagit = FORWARDED_FLAG;
+ if(fp->set){
+ flags = ST_SET;
+ unflagged = F_UNFWD;
+ }
+ else{
+ flags = 0L;
+ unflagged = F_FWD;
+ }
+ }
+
+ break;
+
+ case F_KEYWORD:
+ if(!MCMD_ISAGG(aopt)){
+ /* see if this keyword is defined for this message */
+ is_set = CMD_FLAG_CLEAR;
+ if(user_flag_is_set(state->mail_stream,
+ mn_m2raw(msgmap, mn_get_cur(msgmap)),
+ fp->keyword))
+ is_set = CMD_FLAG_SET;
+ }
+
+ if((!MCMD_ISAGG(aopt) && fp->set != is_set)
+ || (MCMD_ISAGG(aopt) && fp->set != CMD_FLAG_UNKN)){
+ flagit = fp->keyword;
+ keyword_array[0] = fp->keyword;
+ keyword_array[1] = NULL;
+ if(fp->set){
+ flags = ST_SET;
+ unflagged = F_UNKEYWORD;
+ }
+ else{
+ flags = 0L;
+ unflagged = F_KEYWORD;
+ }
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ flagged = 0L;
+ if(flags >= 0L
+ && (seq = currentf_sequence(state->mail_stream, msgmap,
+ unflagged, &flagged, unflagged & F_DEL,
+ (fp->flag == F_KEYWORD
+ && unflagged == F_KEYWORD)
+ ? keyword_array : NULL,
+ (fp->flag == F_KEYWORD
+ && unflagged == F_UNKEYWORD)
+ ? keyword_array : NULL))){
+ /*
+ * For user keywords, we may have to create the flag in
+ * the folder if it doesn't already exist and we are setting
+ * it (as opposed to clearing it). Mail_flag will
+ * do that for us, but it's failure isn't very friendly
+ * error-wise. So we try to make it a little smoother.
+ */
+ if(!(fp->flag == F_KEYWORD || fp->flag == F_FWD) || !fp->set
+ || ((i=user_flag_index(state->mail_stream, flagit)) >= 0
+ && i < NUSERFLAGS))
+ mail_flag(state->mail_stream, seq, flagit, flags);
+ else{
+ /* trouble, see if we can add the user flag */
+ if(state->mail_stream->kwd_create)
+ mail_flag(state->mail_stream, seq, flagit, flags);
+ else{
+ trouble++;
+
+ if(some_user_flags_defined(state->mail_stream))
+ q_status_message(SM_ORDER, 3, 4,
+ _("No more keywords allowed in this folder!"));
+ else if(fp->flag == F_FWD)
+ q_status_message(SM_ORDER, 3, 4,
+ _("Cannot add keywords for this folder so cannot set Forwarded flag"));
+ else
+ q_status_message(SM_ORDER, 3, 4,
+ _("Cannot add keywords for this folder"));
+ }
+ }
+
+ fs_give((void **) &seq);
+ if(flagged && !trouble){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%slagged%s%s%s%s%s message%s%s \"%s\"",
+ (fp->set) ? "F" : "Unf",
+ MCMD_ISAGG(aopt) ? " " : "",
+ MCMD_ISAGG(aopt) ? long2string(flagged) : "",
+ (MCMD_ISAGG(aopt) && flagged != mn_total_cur(msgmap))
+ ? " (of " : "",
+ (MCMD_ISAGG(aopt) && flagged != mn_total_cur(msgmap))
+ ? comatose(mn_total_cur(msgmap)) : "",
+ (MCMD_ISAGG(aopt) && flagged != mn_total_cur(msgmap))
+ ? ")" : "",
+ MCMD_ISAGG(aopt) ? plural(flagged) : " ",
+ MCMD_ISAGG(aopt) ? "" : long2string(mn_get_cur(msgmap)),
+ fp->name);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ q_status_message(SM_ORDER, 0, 2, answer = tmp_20k_buf);
+ rv++;
+ }
+ }
+ }
+
+ free_flag_table(&ftbl);
+
+ if(directly_to_maint_screen)
+ goto go_again;
+
+ if(MCMD_ISAGG(aopt))
+ restore_selected(msgmap);
+
+ if(!answer)
+ q_status_message(SM_ORDER, 0, 2, _("No flags changed."));
+
+ return rv;
+}
+
+
+/*----------------------------------------------------------------------
+ Offer concise status line flag prompt
+
+ Args: state -- Various satate info
+ flags -- flags to offer setting
+
+ Result: TRUE if flag to set specified in flags struct or FALSE otw
+
+ ----*/
+int
+cmd_flag_prompt(struct pine *state, struct flag_screen *flags, int allow_keyword_shortcuts)
+{
+ int r, setflag = 1, first_char;
+ struct flag_table *fp;
+ ESCKEY_S *ek;
+ char *ftext, *ftext_not;
+ static char *flag_text =
+ N_("Flag New, Deleted, Answered, Forwarded or Important ? ");
+ static char *flag_text_ak =
+ N_("Flag New, Deleted, Answered, Forwarded, Important or Keyword initial ? ");
+ static char *flag_text_not =
+ N_("Flag !New, !Deleted, !Answered, !Forwarded, or !Important ? ");
+ static char *flag_text_ak_not =
+ N_("Flag !New, !Deleted, !Answered, !Forwarded, !Important or !Keyword initial ? ");
+
+ if(allow_keyword_shortcuts){
+ int cnt = 0;
+ ESCKEY_S *dp, *sp, *tp;
+
+ for(sp=flag_text_opt; sp->ch != -1; sp++)
+ cnt++;
+
+ for(fp=(flags->flag_table ? *flags->flag_table : NULL); fp->name; fp++)
+ if(fp->flag == F_KEYWORD)
+ cnt++;
+
+ /* set up an ESCKEY_S list which includes invisible keys for keywords */
+ ek = (ESCKEY_S *) fs_get((cnt + 1) * sizeof(*ek));
+ memset(ek, 0, (cnt+1) * sizeof(*ek));
+ for(dp=ek, sp=flag_text_opt; sp->ch != -1; sp++, dp++)
+ *dp = *sp;
+
+ for(fp=(flags->flag_table ? *flags->flag_table : NULL); fp->name; fp++){
+ if(fp->flag == F_KEYWORD){
+ first_char = (fp->name && fp->name[0]) ? fp->name[0] : -2;
+ if(isascii(first_char) && isupper(first_char))
+ first_char = tolower((unsigned char) first_char);
+
+ /*
+ * Check to see if an earlier keyword in the list, or one of
+ * the builtin system letters already uses this character.
+ * If so, the first one wins.
+ */
+ for(tp=ek; tp->ch != 0; tp++)
+ if(tp->ch == first_char)
+ break;
+
+ if(tp->ch != 0)
+ continue; /* skip it, already used that char */
+
+ dp->ch = first_char;
+ dp->rval = first_char;
+ dp->name = "";
+ dp->label = "";
+ dp++;
+ }
+ }
+
+ dp->ch = -1;
+ ftext = _(flag_text_ak);
+ ftext_not = _(flag_text_ak_not);
+ }
+ else{
+ ek = flag_text_opt;
+ ftext = _(flag_text);
+ ftext_not = _(flag_text_not);
+ }
+
+ while(1){
+ r = radio_buttons(setflag ? ftext : ftext_not,
+ -FOOTER_ROWS(state), ek, '*', SEQ_EXCEPTION-1,
+ NO_HELP, RB_NORM | RB_SEQ_SENSITIVE);
+ /*
+ * It is SEQ_EXCEPTION-1 just so that it is some value that isn't
+ * being used otherwise. The keywords use up all the possible
+ * letters, so a negative number is good, but it has to be different
+ * from other negative return values.
+ */
+ if(r == SEQ_EXCEPTION-1) /* ol'cancelrooney */
+ return(TRUE);
+ else if(r == 10) /* return and goto flag screen */
+ return(FALSE);
+ else if(r == '!') /* flip intention */
+ setflag = !setflag;
+ else
+ break;
+ }
+
+ for(fp = (flags->flag_table ? *flags->flag_table : NULL); fp->name; fp++){
+ if(r == 'n' || r == '*' || r == 'd' || r == 'a' || r == 'f'){
+ if((r == 'n' && fp->flag == F_SEEN)
+ || (r == '*' && fp->flag == F_FLAG)
+ || (r == 'd' && fp->flag == F_DEL)
+ || (r == 'f' && fp->flag == F_FWD)
+ || (r == 'a' && fp->flag == F_ANS)){
+ fp->set = setflag ? CMD_FLAG_SET : CMD_FLAG_CLEAR;
+ break;
+ }
+ }
+ else if(allow_keyword_shortcuts && fp->flag == F_KEYWORD){
+ first_char = (fp->name && fp->name[0]) ? fp->name[0] : -2;
+ if(isascii(first_char) && isupper(first_char))
+ first_char = tolower((unsigned char) first_char);
+
+ if(r == first_char){
+ fp->set = setflag ? CMD_FLAG_SET : CMD_FLAG_CLEAR;
+ break;
+ }
+ }
+ }
+
+ if(ek != flag_text_opt)
+ fs_give((void **) &ek);
+
+ return(TRUE);
+}
+
+
+/*
+ * (*ft) is an array of flag_table entries.
+ */
+void
+free_flag_table(struct flag_table **ft)
+{
+ struct flag_table *fp;
+
+ if(ft && *ft){
+ for(fp = (*ft); fp->name; fp++){
+ if(fp->name)
+ fs_give((void **) &fp->name);
+
+ if(fp->keyword)
+ fs_give((void **) &fp->keyword);
+
+ if(fp->comment)
+ fs_give((void **) &fp->comment);
+ }
+
+ fs_give((void **) ft);
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Execute REPLY message command
+
+ Args: state -- Various satate info
+ msgmap -- map of c-client to local message numbers
+
+ Result: reply sent or not
+
+ ----*/
+int
+cmd_reply(struct pine *state, MSGNO_S *msgmap, int aopt)
+{
+ int rv = 0;
+
+ if(any_messages(msgmap, NULL, "to Reply to")){
+ if(MCMD_ISAGG(aopt) && !pseudo_selected(state->mail_stream, msgmap))
+ return rv;
+
+ rv = reply(state, NULL);
+
+ if(MCMD_ISAGG(aopt))
+ restore_selected(msgmap);
+
+ state->mangled_screen = 1;
+ }
+
+ return rv;
+}
+
+
+/*----------------------------------------------------------------------
+ Execute FORWARD message command
+
+ Args: state -- Various satate info
+ msgmap -- map of c-client to local message numbers
+
+ Result: selected message[s] forwarded or not
+
+ ----*/
+int
+cmd_forward(struct pine *state, MSGNO_S *msgmap, int aopt)
+{
+ int rv = 0;
+
+ if(any_messages(msgmap, NULL, "to Forward")){
+ if(MCMD_ISAGG(aopt) && !pseudo_selected(state->mail_stream, msgmap))
+ return rv;
+
+ rv = forward(state, NULL);
+
+ if(MCMD_ISAGG(aopt))
+ restore_selected(msgmap);
+
+ state->mangled_screen = 1;
+ }
+
+ return rv;
+}
+
+
+/*----------------------------------------------------------------------
+ Execute BOUNCE message command
+
+ Args: state -- Various satate info
+ msgmap -- map of c-client to local message numbers
+ aopt -- aggregate options
+
+ Result: selected message[s] bounced or not
+
+ ----*/
+int
+cmd_bounce(struct pine *state, MSGNO_S *msgmap, int aopt)
+{
+ int rv = 0;
+
+ if(any_messages(msgmap, NULL, "to Bounce")){
+ if(MCMD_ISAGG(aopt) && !pseudo_selected(state->mail_stream, msgmap))
+ return rv;
+
+ rv = bounce(state, NULL);
+ if(MCMD_ISAGG(aopt))
+ restore_selected(msgmap);
+
+ state->mangled_footer = 1;
+ }
+
+ return rv;
+}
+
+
+/*----------------------------------------------------------------------
+ Execute save message command: prompt for folder and call function to save
+
+ Args: screen_line -- Line on the screen to prompt on
+ message -- The MESSAGECACHE entry of message to save
+
+ Result: The folder lister can be called to make selection; mangled screen set
+
+ This does the prompting for the folder name to save to, possibly calling
+ up the folder display for selection of folder by user.
+ ----*/
+int
+cmd_save(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, int aopt, CmdWhere in_index)
+{
+ char newfolder[MAILTMPLEN], nmsgs[32], *nick;
+ int we_cancel = 0, rv = 0, save_flags;
+ long i, raw;
+ CONTEXT_S *cntxt = NULL;
+ ENVELOPE *e = NULL;
+ SaveDel del = DontAsk;
+ SavePreserveOrder pre = DontAskPreserve;
+
+ dprint((4, "\n - saving message -\n"));
+
+ state->ugly_consider_advancing_bit = 0;
+ if(F_OFF(F_SAVE_PARTIAL_WO_CONFIRM, state)
+ && msgno_any_deletedparts(stream, msgmap)
+ && want_to(_("Saved copy will NOT include entire message! Continue"),
+ 'y', 'n', NO_HELP, WT_FLUSH_IN | WT_SEQ_SENSITIVE) != 'y'){
+ cmd_cancelled("Save message");
+ return rv;
+ }
+
+ if(MCMD_ISAGG(aopt) && !pseudo_selected(stream, msgmap))
+ return rv;
+
+ raw = mn_m2raw(msgmap, mn_get_cur(msgmap));
+
+ if(mn_total_cur(msgmap) <= 1L){
+ snprintf(nmsgs, sizeof(nmsgs), "Msg #%ld ", mn_get_cur(msgmap));
+ nmsgs[sizeof(nmsgs)-1] = '\0';
+ e = pine_mail_fetchstructure(stream, raw, NULL);
+ if(!e) {
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Can't save message. Error accessing folder"));
+ restore_selected(msgmap);
+ return rv;
+ }
+ }
+ else{
+ snprintf(nmsgs, sizeof(nmsgs), "%s msgs ", comatose(mn_total_cur(msgmap)));
+ nmsgs[sizeof(nmsgs)-1] = '\0';
+
+ /* e is just used to get a default save folder from the first msg */
+ e = pine_mail_fetchstructure(stream,
+ mn_m2raw(msgmap, mn_first_cur(msgmap)),
+ NULL);
+ }
+
+ del = (!READONLY_FOLDER(stream) && F_OFF(F_SAVE_WONT_DELETE, ps_global))
+ ? Del : NoDel;
+ if(mn_total_cur(msgmap) > 1L)
+ pre = F_OFF(F_AGG_SEQ_COPY, ps_global) ? Preserve : NoPreserve;
+ else
+ pre = DontAskPreserve;
+
+ if(save_prompt(state, &cntxt, newfolder, sizeof(newfolder), nmsgs, e,
+ raw, NULL, &del, &pre)){
+
+ if(ps_global && ps_global->ttyo){
+ blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
+ ps_global->mangled_footer = 1;
+ }
+
+ save_flags = SV_FIX_DELS;
+ if(pre == RetPreserve)
+ save_flags |= SV_PRESERVE;
+ if(del == RetDel)
+ save_flags |= SV_DELETE;
+ if(ps_global->context_list == cntxt && !strucmp(newfolder, ps_global->inbox_name))
+ save_flags |= SV_INBOXWOCNTXT;
+
+ we_cancel = busy_cue(_("Saving"), NULL, 1);
+ i = save(state, stream, cntxt, newfolder, msgmap, save_flags);
+ if(we_cancel)
+ cancel_busy_cue(0);
+
+ if(i == mn_total_cur(msgmap)){
+ rv++;
+ if(mn_total_cur(msgmap) <= 1L){
+ int need, avail = ps_global->ttyo->screen_cols - 2;
+ int lennick, lenfldr;
+
+ if(cntxt
+ && ps_global->context_list->next
+ && context_isambig(newfolder)){
+ lennick = MIN(strlen(cntxt->nickname), 500);
+ lenfldr = MIN(strlen(newfolder), 500);
+ need = 27 + strlen(long2string(mn_get_cur(msgmap))) +
+ lenfldr + lennick;
+ if(need > avail){
+ if(lennick > 10){
+ need -= MIN(lennick-10, need-avail);
+ lennick -= MIN(lennick-10, need-avail);
+ }
+
+ if(need > avail && lenfldr > 10)
+ lenfldr -= MIN(lenfldr-10, need-avail);
+ }
+
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,
+ "Message %s copied to \"%s\" in <%s>",
+ long2string(mn_get_cur(msgmap)),
+ short_str(newfolder, (char *)(tmp_20k_buf+1000), 1000,
+ lenfldr, MidDots),
+ short_str(cntxt->nickname,
+ (char *)(tmp_20k_buf+2000), 1000,
+ lennick, EndDots));
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ }
+ else if((nick=folder_is_target_of_nick(newfolder, cntxt)) != NULL){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,
+ "Message %s copied to \"%s\"",
+ long2string(mn_get_cur(msgmap)),
+ nick);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ }
+ else{
+ char *f = " folder";
+
+ lenfldr = MIN(strlen(newfolder), 500);
+ need = 28 + strlen(long2string(mn_get_cur(msgmap))) +
+ lenfldr;
+ if(need > avail){
+ need -= strlen(f);
+ f = "";
+ if(need > avail && lenfldr > 10)
+ lenfldr -= MIN(lenfldr-10, need-avail);
+ }
+
+ snprintf(tmp_20k_buf,SIZEOF_20KBUF,
+ "Message %s copied to%s \"%s\"",
+ long2string(mn_get_cur(msgmap)), f,
+ short_str(newfolder, (char *)(tmp_20k_buf+1000), 1000,
+ lenfldr, MidDots));
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ }
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s messages saved",
+ comatose(mn_total_cur(msgmap)));
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ }
+
+ if(del == RetDel){
+ strncat(tmp_20k_buf, " and deleted", SIZEOF_20KBUF-strlen(tmp_20k_buf)-1);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ }
+
+ q_status_message(SM_ORDER, 0, 3, tmp_20k_buf);
+
+ if(!MCMD_ISAGG(aopt) && F_ON(F_SAVE_ADVANCES, state)){
+ if(sp_new_mail_count(stream))
+ process_filter_patterns(stream, msgmap,
+ sp_new_mail_count(stream));
+
+ mn_inc_cur(stream, msgmap,
+ (in_index == View && THREADING()
+ && sp_viewing_a_thread(stream))
+ ? MH_THISTHD
+ : (in_index == View)
+ ? MH_ANYTHD : MH_NONE);
+ }
+
+ state->ugly_consider_advancing_bit = 1;
+ }
+ }
+
+ if(MCMD_ISAGG(aopt)) /* straighten out fakes */
+ restore_selected(msgmap);
+
+ if(del == RetDel)
+ update_titlebar_status(); /* make sure they see change */
+
+ return rv;
+}
+
+
+void
+role_compose(struct pine *state)
+{
+ int action;
+
+ if(F_ON(F_ALT_ROLE_MENU, state) && mn_get_total(state->msgmap) > 0L){
+ PAT_STATE pstate;
+
+ if(nonempty_patterns(ROLE_DO_ROLES, &pstate) && first_pattern(&pstate)){
+ action = radio_buttons(_("Compose, Forward, Reply, or Bounce? "),
+ -FOOTER_ROWS(state), choose_action,
+ 'c', 'x', h_role_compose, RB_NORM);
+ }
+ else{
+ q_status_message(SM_ORDER, 0, 3,
+ _("No roles available. Use Setup/Rules to add roles."));
+ return;
+ }
+ }
+ else
+ action = 'c';
+
+ if(action == 'c' || action == 'r' || action == 'f' || action == 'b'){
+ ACTION_S *role = NULL;
+ void (*prev_screen)(struct pine *) = NULL, (*redraw)(void) = NULL;
+
+ redraw = state->redrawer;
+ state->redrawer = NULL;
+ prev_screen = state->prev_screen;
+ role = NULL;
+ state->next_screen = SCREEN_FUN_NULL;
+
+ /* Setup role */
+ if(role_select_screen(state, &role,
+ action == 'f' ? MC_FORWARD :
+ action == 'r' ? MC_REPLY :
+ action == 'b' ? MC_BOUNCE :
+ action == 'c' ? MC_COMPOSE : 0) < 0){
+ cmd_cancelled(action == 'f' ? _("Forward") :
+ action == 'r' ? _("Reply") :
+ action == 'c' ? _("Composition") : _("Bounce"));
+ state->next_screen = prev_screen;
+ state->redrawer = redraw;
+ state->mangled_screen = 1;
+ }
+ else{
+ /*
+ * If default role was selected (NULL) we need to make
+ * up a role which won't do anything, but will cause
+ * compose_mail to think there's already a role so that
+ * it won't try to confirm the default.
+ */
+ if(role)
+ role = combine_inherited_role(role);
+ else{
+ role = (ACTION_S *) fs_get(sizeof(*role));
+ memset((void *) role, 0, sizeof(*role));
+ role->nick = cpystr("Default Role");
+ }
+
+ state->redrawer = NULL;
+ switch(action){
+ case 'c':
+ compose_mail(NULL, NULL, role, NULL, NULL);
+ break;
+
+ case 'r':
+ (void) reply(state, role);
+ break;
+
+ case 'f':
+ (void) forward(state, role);
+ break;
+
+ case 'b':
+ (void) bounce(state, role);
+ break;
+ }
+
+ if(role)
+ free_action(&role);
+
+ state->next_screen = prev_screen;
+ state->redrawer = redraw;
+ state->mangled_screen = 1;
+ }
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Do the dirty work of prompting the user for a folder name
+
+ Args:
+ nfldr should be a buffer at least MAILTMPLEN long
+ dela -- a pointer to a SaveDel. If it is
+ DontAsk on input, don't offer Delete prompt
+ Del on input, offer Delete command with default of Delete
+ NoDel NoDelete
+ RetDel and RetNoDel are return values
+
+
+ Result:
+
+ ----*/
+int
+save_prompt(struct pine *state, CONTEXT_S **cntxt, char *nfldr, size_t len_nfldr,
+ char *nmsgs, ENVELOPE *env, long int rawmsgno, char *section,
+ SaveDel *dela, SavePreserveOrder *prea)
+{
+ int rc, ku = -1, n, flags, last_rc = 0, saveable_count = 0, done = 0;
+ int delindex, preindex, r;
+ char prompt[6*MAX_SCREEN_COLS+1], *p, expanded[MAILTMPLEN];
+ char *buf = tmp_20k_buf;
+ char shortbuf[200];
+ char *folder;
+ HelpType help;
+ SaveDel del = DontAsk;
+ SavePreserveOrder pre = DontAskPreserve;
+ char *deltext = NULL;
+ static HISTORY_S *history = NULL;
+ CONTEXT_S *tc;
+ ESCKEY_S ekey[10];
+
+ if(!cntxt)
+ panic("no context ptr in save_prompt");
+
+ init_hist(&history, HISTSIZE);
+
+ if(!(folder = save_get_default(state, env, rawmsgno, section, cntxt)))
+ return(0); /* message expunged! */
+
+ /* how many context's can be saved to... */
+ for(tc = state->context_list; tc; tc = tc->next)
+ if(!NEWS_TEST(tc))
+ saveable_count++;
+
+ /* set up extra command option keys */
+ rc = 0;
+ ekey[rc].ch = ctrl('T');
+ ekey[rc].rval = 2;
+ ekey[rc].name = "^T";
+ /* TRANSLATORS: command means go to Folders list */
+ ekey[rc++].label = N_("To Fldrs");
+
+ if(saveable_count > 1){
+ ekey[rc].ch = ctrl('P');
+ ekey[rc].rval = 10;
+ ekey[rc].name = "^P";
+ ekey[rc++].label = N_("Prev Collection");
+
+ ekey[rc].ch = ctrl('N');
+ ekey[rc].rval = 11;
+ ekey[rc].name = "^N";
+ ekey[rc++].label = N_("Next Collection");
+ }
+
+ if(F_ON(F_ENABLE_TAB_COMPLETE, ps_global)){
+ ekey[rc].ch = TAB;
+ ekey[rc].rval = 12;
+ ekey[rc].name = "TAB";
+ /* TRANSLATORS: command asks alpine to complete the name when tab is typed */
+ ekey[rc++].label = N_("Complete");
+ }
+
+ if(F_ON(F_ENABLE_SUB_LISTS, ps_global)){
+ ekey[rc].ch = ctrl('X');
+ ekey[rc].rval = 14;
+ ekey[rc].name = "^X";
+ /* TRANSLATORS: list all the matches */
+ ekey[rc++].label = N_("ListMatches");
+ }
+
+ if(dela && (*dela == NoDel || *dela == Del)){
+ ekey[rc].ch = ctrl('R');
+ ekey[rc].rval = 15;
+ ekey[rc].name = "^R";
+ delindex = rc++;
+ del = *dela;
+ }
+
+ if(prea && (*prea == NoPreserve || *prea == Preserve)){
+ ekey[rc].ch = ctrl('W');
+ ekey[rc].rval = 16;
+ ekey[rc].name = "^W";
+ preindex = rc++;
+ pre = *prea;
+ }
+
+ if(saveable_count > 1 && F_ON(F_DISABLE_SAVE_INPUT_HISTORY, ps_global)){
+ ekey[rc].ch = KEY_UP;
+ ekey[rc].rval = 10;
+ ekey[rc].name = "";
+ ekey[rc++].label = "";
+
+ ekey[rc].ch = KEY_DOWN;
+ ekey[rc].rval = 11;
+ ekey[rc].name = "";
+ ekey[rc++].label = "";
+ }
+ else if(F_OFF(F_DISABLE_SAVE_INPUT_HISTORY, ps_global)){
+ ekey[rc].ch = KEY_UP;
+ ekey[rc].rval = 30;
+ ekey[rc].name = "";
+ ku = rc;
+ ekey[rc++].label = "";
+
+ ekey[rc].ch = KEY_DOWN;
+ ekey[rc].rval = 31;
+ ekey[rc].name = "";
+ ekey[rc++].label = "";
+ }
+
+ ekey[rc].ch = -1;
+
+ *nfldr = '\0';
+ help = NO_HELP;
+ while(!done){
+ /* only show collection number if more than one available */
+ if(ps_global->context_list->next)
+ snprintf(prompt, sizeof(prompt), "SAVE%s %sto folder in <%s> [%s] : ",
+ deltext ? deltext : "",
+ nmsgs,
+ short_str((*cntxt)->nickname, shortbuf, sizeof(shortbuf), 16, EndDots),
+ strsquish(buf, SIZEOF_20KBUF, folder, 25));
+ else
+ snprintf(prompt, sizeof(prompt), "SAVE%s %sto folder [%s] : ",
+ deltext ? deltext : "",
+ nmsgs, strsquish(buf, SIZEOF_20KBUF, folder, 40));
+
+ prompt[sizeof(prompt)-1] = '\0';
+
+ /*
+ * If the prompt won't fit, try removing deltext.
+ */
+ if(state->ttyo->screen_cols < strlen(prompt) + MIN_OPT_ENT_WIDTH && deltext){
+ if(ps_global->context_list->next)
+ snprintf(prompt, sizeof(prompt), "SAVE %sto folder in <%s> [%s] : ",
+ nmsgs,
+ short_str((*cntxt)->nickname, shortbuf, sizeof(shortbuf), 16, EndDots),
+ strsquish(buf, SIZEOF_20KBUF, folder, 25));
+ else
+ snprintf(prompt, sizeof(prompt), "SAVE %sto folder [%s] : ",
+ nmsgs, strsquish(buf, SIZEOF_20KBUF, folder, 40));
+
+ prompt[sizeof(prompt)-1] = '\0';
+ }
+
+ /*
+ * If the prompt still won't fit, remove the extra info contained
+ * in nmsgs.
+ */
+ if(state->ttyo->screen_cols < strlen(prompt) + MIN_OPT_ENT_WIDTH && *nmsgs){
+ if(ps_global->context_list->next)
+ snprintf(prompt, sizeof(prompt), "SAVE to folder in <%s> [%s] : ",
+ short_str((*cntxt)->nickname, shortbuf, sizeof(shortbuf), 16, EndDots),
+ strsquish(buf, SIZEOF_20KBUF, folder, 25));
+ else
+ snprintf(prompt, sizeof(prompt), "SAVE to folder [%s] : ",
+ strsquish(buf, SIZEOF_20KBUF, folder, 25));
+
+ prompt[sizeof(prompt)-1] = '\0';
+ }
+
+ if(del != DontAsk)
+ ekey[delindex].label = (del == NoDel) ? "Delete" : "No Delete";
+
+ if(pre != DontAskPreserve)
+ ekey[preindex].label = (pre == NoPreserve) ? "Preserve Order" : "Any Order";
+
+ if(ku >= 0){
+ if(items_in_hist(history) > 1){
+ ekey[ku].name = HISTORY_UP_KEYNAME;
+ ekey[ku].label = HISTORY_KEYLABEL;
+ ekey[ku+1].name = HISTORY_DOWN_KEYNAME;
+ ekey[ku+1].label = HISTORY_KEYLABEL;
+ }
+ else{
+ ekey[ku].name = "";
+ ekey[ku].label = "";
+ ekey[ku+1].name = "";
+ ekey[ku+1].label = "";
+ }
+ }
+
+ flags = OE_APPEND_CURRENT | OE_SEQ_SENSITIVE;
+ rc = optionally_enter(nfldr, -FOOTER_ROWS(state), 0, len_nfldr,
+ prompt, ekey, help, &flags);
+
+ switch(rc){
+ case -1 :
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("Error reading folder name"));
+ done--;
+ break;
+
+ case 0 :
+ removing_trailing_white_space(nfldr);
+ removing_leading_white_space(nfldr);
+
+ if(*nfldr || *folder){
+ char *p, *name, *fullname = NULL;
+ int exists, breakout = FALSE;
+
+ if(!*nfldr){
+ strncpy(nfldr, folder, len_nfldr-1);
+ nfldr[len_nfldr-1] = '\0';
+ }
+
+ save_hist(history, nfldr, 0, (void *) *cntxt);
+
+ if(!(name = folder_is_nick(nfldr, FOLDERS(*cntxt), 0)))
+ name = nfldr;
+
+ if(update_folder_spec(expanded, sizeof(expanded), name)){
+ strncpy(name = nfldr, expanded, len_nfldr-1);
+ nfldr[len_nfldr-1] = '\0';
+ }
+
+ exists = folder_name_exists(*cntxt, name, &fullname);
+
+ if(exists == FEX_ERROR){
+ q_status_message1(SM_ORDER, 0, 3,
+ _("Problem accessing folder \"%s\""),
+ nfldr);
+ done--;
+ }
+ else{
+ if(fullname){
+ strncpy(name = nfldr, fullname, len_nfldr-1);
+ nfldr[len_nfldr-1] = '\0';
+ fs_give((void **) &fullname);
+ breakout = TRUE;
+ }
+
+ if(exists & FEX_ISFILE){
+ done++;
+ }
+ else if((exists & FEX_ISDIR)){
+ char tmp[MAILTMPLEN];
+
+ tc = *cntxt;
+ if(breakout){
+ CONTEXT_S *fake_context;
+ size_t l;
+
+ strncpy(tmp, name, sizeof(tmp));
+ tmp[sizeof(tmp)-2-1] = '\0';
+ if(tmp[(l = strlen(tmp)) - 1] != tc->dir->delim){
+ if(l < sizeof(tmp)){
+ tmp[l] = tc->dir->delim;
+ strncpy(&tmp[l+1], "[]", sizeof(tmp)-(l+1));
+ }
+ }
+ else
+ strncat(tmp, "[]", sizeof(tmp)-strlen(tmp)-1);
+
+ tmp[sizeof(tmp)-1] = '\0';
+
+ fake_context = new_context(tmp, 0);
+ nfldr[0] = '\0';
+ done = display_folder_list(&fake_context, nfldr,
+ 1, folders_for_save);
+ free_context(&fake_context);
+ }
+ else if(tc->dir->delim
+ && (p = strrindex(name, tc->dir->delim))
+ && *(p+1) == '\0')
+ done = display_folder_list(cntxt, nfldr,
+ 1, folders_for_save);
+ else{
+ q_status_message1(SM_ORDER, 3, 3,
+ _("\"%s\" is a directory"), name);
+ if(tc->dir->delim
+ && !((p=strrindex(name, tc->dir->delim)) && *(p+1) == '\0')){
+ strncpy(tmp, name, sizeof(tmp));
+ tmp[sizeof(tmp)-1] = '\0';
+ snprintf(nfldr, len_nfldr, "%s%c", tmp, tc->dir->delim);
+ }
+ }
+ }
+ else{ /* Doesn't exist, create! */
+ if((fullname = folder_as_breakout(*cntxt, name)) != NULL){
+ strncpy(name = nfldr, fullname, len_nfldr-1);
+ nfldr[len_nfldr-1] = '\0';
+ fs_give((void **) &fullname);
+ }
+
+ switch(create_for_save(*cntxt, name)){
+ case 1 : /* success */
+ done++;
+ break;
+ case 0 : /* error */
+ case -1 : /* declined */
+ done--;
+ break;
+ }
+ }
+ }
+
+ break;
+ }
+ /* else fall thru like they cancelled */
+
+ case 1 :
+ cmd_cancelled("Save message");
+ done--;
+ break;
+
+ case 2 :
+ r = display_folder_list(cntxt, nfldr, 0, folders_for_save);
+
+ if(r)
+ done++;
+
+ break;
+
+ case 3 :
+ helper(h_save, _("HELP FOR SAVE"), HLPD_SIMPLE);
+ ps_global->mangled_screen = 1;
+ break;
+
+ case 4 : /* redraw */
+ break;
+
+ case 10 : /* previous collection */
+ for(tc = (*cntxt)->prev; tc; tc = tc->prev)
+ if(!NEWS_TEST(tc))
+ break;
+
+ if(!tc){
+ CONTEXT_S *tc2;
+
+ for(tc2 = (tc = (*cntxt))->next; tc2; tc2 = tc2->next)
+ if(!NEWS_TEST(tc2))
+ tc = tc2;
+ }
+
+ *cntxt = tc;
+ break;
+
+ case 11 : /* next collection */
+ tc = (*cntxt);
+
+ do
+ if(((*cntxt) = (*cntxt)->next) == NULL)
+ (*cntxt) = ps_global->context_list;
+ while(NEWS_TEST(*cntxt) && (*cntxt) != tc);
+ break;
+
+ case 12 : /* file name completion */
+ if(!folder_complete(*cntxt, nfldr, len_nfldr, &n)){
+ if(n && last_rc == 12 && !(flags & OE_USER_MODIFIED)){
+ r = display_folder_list(cntxt, nfldr, 1, folders_for_save);
+ if(r)
+ done++; /* bingo! */
+ else
+ rc = 0; /* burn last_rc */
+ }
+ else
+ Writechar(BELL, 0);
+ }
+
+ break;
+
+ case 14 : /* file name completion */
+ r = display_folder_list(cntxt, nfldr, 2, folders_for_save);
+ if(r)
+ done++; /* bingo! */
+ else
+ rc = 0; /* burn last_rc */
+
+ break;
+
+ case 15 : /* Delete / No Delete */
+ del = (del == NoDel) ? Del : NoDel;
+ deltext = (del == NoDel) ? " (no delete)" : " (and delete)";
+ break;
+
+ case 16 : /* Preserve Order or not */
+ pre = (pre == NoPreserve) ? Preserve : NoPreserve;
+ break;
+
+ case 30 :
+ if((p = get_prev_hist(history, nfldr, 0, (void *) *cntxt)) != NULL){
+ strncpy(nfldr, p, len_nfldr);
+ nfldr[len_nfldr-1] = '\0';
+ if(history->hist[history->curindex])
+ *cntxt = (CONTEXT_S *) history->hist[history->curindex]->cntxt;
+ }
+ else
+ Writechar(BELL, 0);
+
+ break;
+
+ case 31 :
+ if((p = get_next_hist(history, nfldr, 0, (void *) *cntxt)) != NULL){
+ strncpy(nfldr, p, len_nfldr);
+ nfldr[len_nfldr-1] = '\0';
+ if(history->hist[history->curindex])
+ *cntxt = (CONTEXT_S *) history->hist[history->curindex]->cntxt;
+ }
+ else
+ Writechar(BELL, 0);
+
+ break;
+
+ default :
+ panic("Unhandled case");
+ break;
+ }
+
+ last_rc = rc;
+ }
+
+ ps_global->mangled_footer = 1;
+
+ if(done < 0)
+ return(0);
+
+ if(*nfldr){
+ strncpy(ps_global->last_save_folder, nfldr, sizeof(ps_global->last_save_folder)-1);
+ ps_global->last_save_folder[sizeof(ps_global->last_save_folder)-1] = '\0';
+ if(*cntxt)
+ ps_global->last_save_context = *cntxt;
+ }
+ else{
+ strncpy(nfldr, folder, len_nfldr-1);
+ nfldr[len_nfldr-1] = '\0';
+ }
+
+ /* nickname? Copy real name to nfldr */
+ if(*cntxt
+ && context_isambig(nfldr)
+ && (p = folder_is_nick(nfldr, FOLDERS(*cntxt), 0))){
+ strncpy(nfldr, p, len_nfldr-1);
+ nfldr[len_nfldr-1] = '\0';
+ }
+
+ if(dela && (*dela == NoDel || *dela == Del))
+ *dela = (del == NoDel) ? RetNoDel : RetDel;
+
+ if(prea && (*prea == NoPreserve || *prea == Preserve))
+ *prea = (pre == NoPreserve) ? RetNoPreserve : RetPreserve;
+
+ return(1);
+}
+
+
+/*----------------------------------------------------------------------
+ Prompt user before implicitly creating a folder for saving
+
+ Args: context - context to create folder in
+ folder - folder name to create
+
+ Result: 1 on proceed, -1 on decline, 0 on error
+
+ ----*/
+int
+create_for_save_prompt(CONTEXT_S *context, char *folder, int sequence_sensitive)
+{
+ if(context && ps_global->context_list->next && context_isambig(folder)){
+ if(context->use & CNTXT_INCMNG){
+ snprintf(tmp_20k_buf,SIZEOF_20KBUF,
+ _("\"%.15s%s\" doesn't exist - Add it in FOLDER LIST screen"),
+ folder, (strlen(folder) > 15) ? "..." : "");
+ q_status_message(SM_ORDER, 3, 3, tmp_20k_buf);
+ return(0); /* error */
+ }
+
+ snprintf(tmp_20k_buf,SIZEOF_20KBUF,
+ _("Folder \"%.15s%s\" in <%.15s%s> doesn't exist. Create"),
+ folder, (strlen(folder) > 15) ? "..." : "",
+ context->nickname,
+ (strlen(context->nickname) > 15) ? "..." : "");
+ }
+ else
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,
+ _("Folder \"%.40s%s\" doesn't exist. Create"),
+ folder, strlen(folder) > 40 ? "..." : "");
+
+ if(want_to(tmp_20k_buf, 'y', 'n',
+ NO_HELP, (sequence_sensitive) ? WT_SEQ_SENSITIVE : WT_NORM) != 'y'){
+ cmd_cancelled("Save message");
+ return(-1);
+ }
+
+ return(1);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Expunge messages from current folder
+
+ Args: state -- pointer to struct holding a bunch of pine state
+ msgmap -- table mapping msg nums to c-client sequence nums
+ qline -- screen line to ask questions on
+ agg -- boolean indicating we're to operate on aggregate set
+
+ Result:
+ ----*/
+void
+cmd_expunge(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap)
+{
+ long del_count, prefilter_del_count;
+ int we_cancel = 0;
+ char prompt[MAX_SCREEN_COLS+1];
+ COLOR_PAIR *lastc = NULL;
+
+ dprint((2, "\n - expunge -\n"));
+
+ if(IS_NEWS(stream) && stream->rdonly){
+ if((del_count = count_flagged(stream, F_DEL)) > 0L){
+ state->mangled_footer = 1;
+ snprintf(prompt, sizeof(prompt), "Exclude %ld message%s from %.*s", del_count,
+ plural(del_count), sizeof(prompt)-40,
+ pretty_fn(state->cur_folder));
+ prompt[sizeof(prompt)-1] = '\0';
+ if(F_ON(F_FULL_AUTO_EXPUNGE, state)
+ || (F_ON(F_AUTO_EXPUNGE, state)
+ && (state->context_current
+ && (state->context_current->use & CNTXT_INCMNG))
+ && context_isambig(state->cur_folder))
+ || want_to(prompt, 'y', 0, NO_HELP, WT_NORM) == 'y'){
+
+ if(F_ON(F_NEWS_CROSS_DELETE, state))
+ cross_delete_crossposts(stream);
+
+ msgno_exclude_deleted(stream, msgmap);
+ clear_index_cache(stream, 0);
+
+ /*
+ * This is kind of surprising at first. For most sort
+ * orders, if the whole set is sorted, then any subset
+ * is also sorted. Not so for threaded sorts.
+ */
+ if(SORT_IS_THREADED(msgmap))
+ refresh_sort(stream, msgmap, SRT_NON);
+
+ state->mangled_body = 1;
+ state->mangled_header = 1;
+ q_status_message2(SM_ORDER, 0, 4,
+ "%s message%s excluded",
+ long2string(del_count),
+ plural(del_count));
+ }
+ else
+ any_messages(NULL, NULL, "Excluded");
+ }
+ else
+ any_messages(NULL, "deleted", "to Exclude");
+
+ return;
+ }
+ else if(READONLY_FOLDER(stream)){
+ q_status_message(SM_ORDER, 0, 4,
+ _("Can't expunge. Folder is read-only"));
+ return;
+ }
+
+ prefilter_del_count = count_flagged(stream, F_DEL|F_NOFILT);
+
+ mail_expunge_prefilter(stream, MI_NONE);
+
+ if((del_count = count_flagged(stream, F_DEL|F_NOFILT)) != 0){
+ int ret;
+
+ snprintf(prompt, sizeof(prompt), "Expunge %ld message%s from %.*s", del_count,
+ plural(del_count), sizeof(prompt)-40,
+ pretty_fn(state->cur_folder));
+ prompt[sizeof(prompt)-1] = '\0';
+ state->mangled_footer = 1;
+
+ if(F_ON(F_FULL_AUTO_EXPUNGE, state)
+ || (F_ON(F_AUTO_EXPUNGE, state)
+ && ((!strucmp(state->cur_folder,state->inbox_name))
+ || (state->context_current->use & CNTXT_INCMNG))
+ && context_isambig(state->cur_folder))
+ || (ret=want_to(prompt, 'y', 0, NO_HELP, WT_NORM)) == 'y')
+ ret = 'y';
+
+ if(ret == 'x')
+ cmd_cancelled("Expunge");
+
+ if(ret != 'y')
+ return;
+ }
+
+ dprint((8, "Expunge max:%ld cur:%ld kill:%d\n",
+ mn_get_total(msgmap), mn_get_cur(msgmap), del_count));
+
+ lastc = pico_set_colors(state->VAR_TITLE_FORE_COLOR,
+ state->VAR_TITLE_BACK_COLOR,
+ PSC_REV|PSC_RET);
+
+ PutLine0(0, 0, "**"); /* indicate delay */
+
+ if(lastc){
+ (void)pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+
+ MoveCursor(state->ttyo->screen_rows -FOOTER_ROWS(state), 0);
+ fflush(stdout);
+
+ we_cancel = busy_cue(_("Expunging"), NULL, 1);
+
+ if(cmd_expunge_work(stream, msgmap))
+ state->mangled_body = 1;
+
+ if(we_cancel)
+ cancel_busy_cue((sp_expunge_count(stream) > 0) ? 0 : -1);
+
+ lastc = pico_set_colors(state->VAR_TITLE_FORE_COLOR,
+ state->VAR_TITLE_BACK_COLOR,
+ PSC_REV|PSC_RET);
+ PutLine0(0, 0, " "); /* indicate delay's over */
+
+ if(lastc){
+ (void)pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+
+ fflush(stdout);
+
+ if(sp_expunge_count(stream) > 0){
+ /*
+ * This is kind of surprising at first. For most sort
+ * orders, if the whole set is sorted, then any subset
+ * is also sorted. Not so for threaded sorts.
+ */
+ if(SORT_IS_THREADED(msgmap))
+ refresh_sort(stream, msgmap, SRT_NON);
+ }
+ else{
+ if(del_count)
+ q_status_message1(SM_ORDER, 0, 3,
+ _("No messages expunged from folder \"%s\""),
+ pretty_fn(state->cur_folder));
+ else if(!prefilter_del_count)
+ q_status_message(SM_ORDER, 0, 3,
+ _("No messages marked deleted. No messages expunged."));
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Expunge_and_close callback to prompt user for confirmation
+
+ Args: stream -- folder's stream
+ folder -- name of folder containing folders
+ deleted -- number of del'd msgs
+
+ Result: 'y' to continue with expunge
+ ----*/
+int
+expunge_prompt(MAILSTREAM *stream, char *folder, long int deleted)
+{
+ long max_folder;
+ int charcnt = 0;
+ char prompt_b[MAX_SCREEN_COLS+1], temp[MAILTMPLEN+1], buff[MAX_SCREEN_COLS+1];
+ char *short_folder_name;
+
+ if(deleted == 1)
+ charcnt = 1;
+ else{
+ snprintf(temp, sizeof(temp), "%ld", deleted);
+ charcnt = strlen(temp)+1;
+ }
+
+ max_folder = MAX(1,MAXPROMPT - (36+charcnt));
+ strncpy(temp, folder, sizeof(temp));
+ temp[sizeof(temp)-1] = '\0';
+ short_folder_name = short_str(temp,buff,sizeof(buff),max_folder,FrontDots);
+
+ if(IS_NEWS(stream))
+ snprintf(prompt_b, sizeof(prompt_b),
+ "Delete %s%ld message%s from \"%s\"",
+ (deleted > 1L) ? "all " : "", deleted,
+ plural(deleted), short_folder_name);
+ else
+ snprintf(prompt_b, sizeof(prompt_b),
+ "Expunge the %ld deleted message%s from \"%s\"",
+ deleted, deleted == 1 ? "" : "s",
+ short_folder_name);
+
+ return(want_to(prompt_b, 'y', 0, NO_HELP, WT_NORM));
+}
+
+
+/*
+ * This is used with multiple append saves. Call it once before
+ * the series of appends with SSCP_INIT and once after all are
+ * done with SSCP_END. In between, it is called automatically
+ * from save_fetch_append or save_fetch_append_cb when we need
+ * to ask the user if he or she wants to continue even though
+ * announced message size doesn't match the actual message size.
+ * As of 2008-02-29 the gmail IMAP server has these size mismatches
+ * on a regular basis even though the data is ok.
+ */
+int
+save_size_changed_prompt(long msgno, int flags)
+{
+ int ret;
+ char prompt[100];
+ static int remember_the_yes = 0;
+ static int possible_corruption = 0;
+ static ESCKEY_S save_size_opts[] = {
+ {'y', 'y', "Y", "Yes"},
+ {'n', 'n', "N", "No"},
+ {'a', 'a', "A", "yes to All"},
+ {-1, 0, NULL, NULL}
+ };
+
+ if(flags & SSCP_INIT || flags & SSCP_END){
+ if(flags & SSCP_END && possible_corruption)
+ q_status_message(SM_ORDER, 3, 3, "There is possible data corruption, check the results");
+
+ remember_the_yes = 0;
+ possible_corruption = 0;
+ ps_global->noshow_error = 0;
+ ps_global->noshow_warn = 0;
+ return(0);
+ }
+
+ if(remember_the_yes){
+ snprintf(prompt, sizeof(prompt),
+ "Message to save shrank! (msg # %ld): Continuing", msgno);
+ q_status_message(SM_ORDER, 0, 3, prompt);
+ display_message('x');
+ return(remember_the_yes);
+ }
+
+ snprintf(prompt, sizeof(prompt),
+ "Message to save shrank! (msg # %ld): Continue anyway ? ", msgno);
+ ret = radio_buttons(prompt, -FOOTER_ROWS(ps_global), save_size_opts,
+ 'n', 0, h_save_size_changed, RB_NORM);
+
+ switch(ret){
+ case 'a':
+ remember_the_yes = 'y';
+ possible_corruption++;
+ return(remember_the_yes);
+
+ case 'y':
+ possible_corruption++;
+ return('y');
+
+ default:
+ possible_corruption = 0;
+ ps_global->noshow_error = 1;
+ ps_global->noshow_warn = 1;
+ break;
+ }
+
+ return('n');
+}
+
+
+/*----------------------------------------------------------------------
+ Expunge_and_close callback that happens once the decision to expunge
+ and close has been made and before expunging and closing begins
+
+
+ Args: stream -- folder's stream
+ folder -- name of folder containing folders
+ deleted -- number of del'd msgs
+
+ Result: 'y' to continue with expunge
+ ----*/
+void
+expunge_and_close_begins(int flags, char *folder)
+{
+ if(!(flags & EC_NO_CLOSE)){
+ q_status_message1(SM_INFO, 0, 1, "Closing \"%.200s\"...", folder);
+ flush_status_messages(1);
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Export a message to a plain file in users home directory
+
+ Args: state -- pointer to struct holding a bunch of pine state
+ msgmap -- table mapping msg nums to c-client sequence nums
+ qline -- screen line to ask questions on
+ agg -- boolean indicating we're to operate on aggregate set
+
+ Result:
+ ----*/
+int
+cmd_export(struct pine *state, MSGNO_S *msgmap, int qline, int aopt)
+{
+ char filename[MAXPATH+1], full_filename[MAXPATH+1], *err;
+ char nmsgs[80];
+ int r, leading_nl, failure = 0, orig_errno, rflags = GER_NONE;
+ int flags = GE_IS_EXPORT | GE_SEQ_SENSITIVE, rv = 0;
+ ENVELOPE *env;
+ MESSAGECACHE *mc;
+ BODY *b;
+ long i, count = 0L, start_of_append, rawno;
+ gf_io_t pc;
+ STORE_S *store;
+ struct variable *vars = ps_global->vars;
+ ESCKEY_S export_opts[5];
+ static HISTORY_S *history = NULL;
+
+ if(ps_global->restricted){
+ q_status_message(SM_ORDER, 0, 3,
+ "Alpine demo can't export messages to files");
+ return rv;
+ }
+
+ if(MCMD_ISAGG(aopt) && !pseudo_selected(state->mail_stream, msgmap))
+ return rv;
+
+ export_opts[i = 0].ch = ctrl('T');
+ export_opts[i].rval = 10;
+ export_opts[i].name = "^T";
+ export_opts[i++].label = N_("To Files");
+
+#if !defined(DOS) && !defined(MAC) && !defined(OS2)
+ if(ps_global->VAR_DOWNLOAD_CMD && ps_global->VAR_DOWNLOAD_CMD[0]){
+ export_opts[i].ch = ctrl('V');
+ export_opts[i].rval = 12;
+ export_opts[i].name = "^V";
+ /* TRANSLATORS: this is an abbreviation for Download Messages */
+ export_opts[i++].label = N_("Downld Msg");
+ }
+#endif /* !(DOS || MAC) */
+
+ if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
+ export_opts[i].ch = ctrl('I');
+ export_opts[i].rval = 11;
+ export_opts[i].name = "TAB";
+ export_opts[i++].label = N_("Complete");
+ }
+
+#if 0
+ /* Commented out since it's not yet support! */
+ if(F_ON(F_ENABLE_SUB_LISTS,ps_global)){
+ export_opts[i].ch = ctrl('X');
+ export_opts[i].rval = 14;
+ export_opts[i].name = "^X";
+ export_opts[i++].label = N_("ListMatches");
+ }
+#endif
+
+ /*
+ * If message has attachments, add a toggle that will allow the user
+ * to save all of the attachments to a single directory, using the
+ * names provided with the attachments or part names. What we'll do is
+ * export the message as usual, and then export the attachments into
+ * a subdirectory that did not exist before. The subdir will be named
+ * something based on the name of the file being saved to, but a
+ * unique, new name.
+ */
+ if(!MCMD_ISAGG(aopt)
+ && state->mail_stream
+ && (rawno = mn_m2raw(msgmap, mn_get_cur(msgmap))) > 0L
+ && rawno <= state->mail_stream->nmsgs
+ && (env = pine_mail_fetchstructure(state->mail_stream, rawno, &b))
+ && b
+ && b->type == TYPEMULTIPART
+ && b->subtype
+ && strucmp(b->subtype, "ALTERNATIVE") != 0){
+ PART *part;
+
+ part = b->nested.part; /* 1st part */
+ if(part && part->next)
+ flags |= GE_ALLPARTS;
+ }
+
+ export_opts[i].ch = -1;
+ filename[0] = '\0';
+
+ if(mn_total_cur(msgmap) <= 1L){
+ snprintf(nmsgs, sizeof(nmsgs), "Msg #%ld", mn_get_cur(msgmap));
+ nmsgs[sizeof(nmsgs)-1] = '\0';
+ }
+ else{
+ snprintf(nmsgs, sizeof(nmsgs), "%s messages", comatose(mn_total_cur(msgmap)));
+ nmsgs[sizeof(nmsgs)-1] = '\0';
+ }
+
+ r = get_export_filename(state, filename, NULL, full_filename,
+ sizeof(filename), nmsgs, "EXPORT",
+ export_opts, &rflags, qline, flags, &history);
+
+ if(r < 0){
+ switch(r){
+ case -1:
+ cmd_cancelled("Export message");
+ break;
+
+ case -2:
+ q_status_message1(SM_ORDER, 0, 2,
+ _("Can't export to file outside of %s"),
+ VAR_OPER_DIR);
+ break;
+ }
+
+ goto fini;
+ }
+#if !defined(DOS) && !defined(MAC) && !defined(OS2)
+ else if(r == 12){ /* Download */
+ char cmd[MAXPATH], *tfp = NULL;
+ int next = 0;
+ PIPE_S *syspipe;
+ STORE_S *so;
+ gf_io_t pc;
+
+ if(ps_global->restricted){
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ "Download disallowed in restricted mode");
+ goto fini;
+ }
+
+ err = NULL;
+ tfp = temp_nam(NULL, "pd");
+ build_updown_cmd(cmd, sizeof(cmd), ps_global->VAR_DOWNLOAD_CMD_PREFIX,
+ ps_global->VAR_DOWNLOAD_CMD, tfp);
+ dprint((1, "Download cmd called: \"%s\"\n", cmd));
+ if((so = so_get(FileStar, tfp, WRITE_ACCESS|OWNER_ONLY|WRITE_TO_LOCALE)) != NULL){
+ gf_set_so_writec(&pc, so);
+
+ for(i = mn_first_cur(msgmap); i > 0L; i = mn_next_cur(msgmap)){
+ if(!(state->mail_stream
+ && (rawno = mn_m2raw(msgmap, i)) > 0L
+ && rawno <= state->mail_stream->nmsgs
+ && (mc = mail_elt(state->mail_stream, rawno))
+ && mc->valid))
+ mc = NULL;
+
+ if(!(env = pine_mail_fetchstructure(state->mail_stream,
+ mn_m2raw(msgmap, i), &b))
+ || !bezerk_delimiter(env, mc, pc, next++)
+ || !format_message(mn_m2raw(msgmap, mn_get_cur(msgmap)),
+ env, b, NULL, FM_NEW_MESS | FM_NOWRAP, pc)){
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ err = "Error writing tempfile for download");
+ break;
+ }
+ }
+
+ gf_clear_so_writec(so);
+ if(so_give(&so)){ /* close file */
+ if(!err)
+ err = "Error writing tempfile for download";
+ }
+
+ if(!err){
+ if((syspipe = open_system_pipe(cmd, NULL, NULL,
+ PIPE_USER | PIPE_RESET,
+ 0, pipe_callback, pipe_report_error)) != NULL)
+ (void) close_system_pipe(&syspipe, NULL, pipe_callback);
+ else
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ err = _("Error running download command"));
+ }
+ }
+ else
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ err = "Error building temp file for download");
+
+ if(tfp){
+ our_unlink(tfp);
+ fs_give((void **)&tfp);
+ }
+
+ if(!err)
+ q_status_message(SM_ORDER, 0, 3, _("Download Command Completed"));
+
+ goto fini;
+ }
+#endif /* !(DOS || MAC) */
+
+
+ if(rflags & GER_APPEND)
+ leading_nl = 1;
+ else
+ leading_nl = 0;
+
+ dprint((5, "Opening file \"%s\" for export\n",
+ full_filename ? full_filename : "?"));
+
+ if(!(store = so_get(FileStar, full_filename, WRITE_ACCESS|WRITE_TO_LOCALE))){
+ q_status_message2(SM_ORDER | SM_DING, 3, 4,
+ /* TRANSLATORS: error opening file "<filename>" to export message: <error text> */
+ _("Error opening file \"%s\" to export message: %s"),
+ full_filename, error_description(errno));
+ goto fini;
+ }
+ else
+ gf_set_so_writec(&pc, store);
+
+ err = NULL;
+ for(i = mn_first_cur(msgmap); i > 0L; i = mn_next_cur(msgmap), count++){
+ env = pine_mail_fetchstructure(state->mail_stream, mn_m2raw(msgmap, i),
+ &b);
+ if(!env) {
+ err = _("Can't export message. Error accessing mail folder");
+ failure = 1;
+ break;
+ }
+
+ if(!(state->mail_stream
+ && (rawno = mn_m2raw(msgmap, i)) > 0L
+ && rawno <= state->mail_stream->nmsgs
+ && (mc = mail_elt(state->mail_stream, rawno))
+ && mc->valid))
+ mc = NULL;
+
+ start_of_append = so_tell(store);
+ if(!bezerk_delimiter(env, mc, pc, leading_nl)
+ || !format_message(mn_m2raw(msgmap, i), env, b, NULL,
+ FM_NEW_MESS | FM_NOWRAP, pc)){
+ orig_errno = errno; /* save incase things are really bad */
+ failure = 1; /* pop out of here */
+ break;
+ }
+
+ leading_nl = 1;
+ }
+
+ gf_clear_so_writec(store);
+ if(so_give(&store)) /* release storage */
+ failure++;
+
+ if(failure){
+ our_truncate(full_filename, (off_t)start_of_append);
+ if(err){
+ dprint((1, "FAILED Export: fetch(%ld): %s\n",
+ i, err ? err : "?"));
+ q_status_message(SM_ORDER | SM_DING, 3, 4, err);
+ }
+ else{
+ dprint((1, "FAILED Export: file \"%s\" : %s\n",
+ full_filename ? full_filename : "?",
+ error_description(orig_errno)));
+ q_status_message2(SM_ORDER | SM_DING, 3, 4,
+ /* TRANSLATORS: Error exporting to <filename>: <error text> */
+ _("Error exporting to \"%s\" : %s"),
+ filename, error_description(orig_errno));
+ }
+ }
+ else{
+ if(rflags & GER_ALLPARTS && full_filename[0]){
+ char dir[MAXPATH+1];
+ char lfile[MAXPATH+1];
+ int ok = 0, tries = 0, saved = 0, errs = 0;
+ ATTACH_S *a;
+
+ /*
+ * Now we want to save all of the attachments to a subdirectory.
+ * To make it easier for us and probably easier for the user, and
+ * to prevent the user from shooting himself in the foot, we
+ * make a new subdirectory so that we can't possibly step on
+ * any existing files, and we don't need any interaction with the
+ * user while saving.
+ *
+ * We'll just use the directory name full_filename.d or if that
+ * already exists and isn't empty, we'll try adding a suffix to
+ * that until we get something to use.
+ */
+
+ if(strlen(full_filename) + strlen(".d") + 1 > sizeof(dir)){
+ q_status_message1(SM_ORDER | SM_DING, 3, 4,
+ _("Can't save attachments, filename too long: %s"),
+ full_filename);
+ goto fini;
+ }
+
+ ok = 0;
+ snprintf(dir, sizeof(dir), "%s.d", full_filename);
+ dir[sizeof(dir)-1] = '\0';
+
+ do {
+ tries++;
+ switch(r = is_writable_dir(dir)){
+ case 0: /* exists and is a writable dir */
+ /*
+ * We could figure out if it is empty and use it in
+ * that case, but that sounds like a lot of work, so
+ * just fall through to default.
+ */
+
+ default:
+ if(strlen(full_filename) + strlen(".d") + 1 +
+ 1 + strlen(long2string((long) tries)) > sizeof(dir)){
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ "Problem saving attachments");
+ goto fini;
+ }
+
+ snprintf(dir, sizeof(dir), "%s.d_%s", full_filename,
+ long2string((long) tries));
+ dir[sizeof(dir)-1] = '\0';
+ break;
+
+ case 3: /* doesn't exist, that's good! */
+ /* make new directory */
+ ok++;
+ break;
+ }
+ } while(!ok && tries < 1000);
+
+ if(tries >= 1000){
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Problem saving attachments"));
+ goto fini;
+ }
+
+ /* create the new directory */
+ if(our_mkdir(dir, 0700)){
+ q_status_message2(SM_ORDER | SM_DING, 3, 4,
+ _("Problem saving attachments: %s: %s"), dir,
+ error_description(errno));
+ goto fini;
+ }
+
+ if(!(state->mail_stream
+ && (rawno = mn_m2raw(msgmap, mn_get_cur(msgmap))) > 0L
+ && rawno <= state->mail_stream->nmsgs
+ && (env=pine_mail_fetchstructure(state->mail_stream,rawno,&b))
+ && b)){
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Problem reading message"));
+ goto fini;
+ }
+
+ zero_atmts(state->atmts);
+ describe_mime(b, "", 1, 1, 0, 0);
+
+ a = state->atmts;
+ if(a && a->description) /* skip main body part */
+ a++;
+
+ for(; a->description != NULL; a++){
+ /* skip over these parts of the message */
+ if(MIME_MSG_A(a) || MIME_DGST_A(a) || MIME_VCARD_A(a))
+ continue;
+
+ lfile[0] = '\0';
+ (void) get_filename_parameter(lfile, sizeof(lfile), a->body, NULL);
+
+ if(lfile[0] == '\0'){
+ snprintf(lfile, sizeof(lfile), "part_%.*s", sizeof(lfile)-6,
+ a->number ? a->number : "?");
+ lfile[sizeof(lfile)-1] = '\0';
+ }
+
+ if(strlen(dir) + strlen(S_FILESEP) + strlen(lfile) + 1
+ > sizeof(filename)){
+ dprint((2,
+ "FAILED Att Export: name too long: %s\n",
+ dir, S_FILESEP, lfile));
+ errs++;
+ continue;
+ }
+
+ snprintf(filename, sizeof(filename), "%s%s%s", dir, S_FILESEP, lfile);
+ filename[sizeof(filename)-1] = '\0';
+
+ if(write_attachment_to_file(state->mail_stream, rawno,
+ a, GER_NONE, filename) == 1)
+ saved++;
+ else
+ errs++;
+ }
+
+ if(errs){
+ if(saved)
+ q_status_message1(SM_ORDER, 3, 3,
+ "Errors saving some attachments, %s attachments saved",
+ long2string((long) saved));
+ else
+ q_status_message(SM_ORDER, 3, 3,
+ _("Problems saving attachments"));
+ }
+ else{
+ if(saved)
+ q_status_message2(SM_ORDER, 0, 3,
+ /* TRANSLATORS: Saved <how many> attachements to <directory name> */
+ _("Saved %s attachments to %s"),
+ long2string((long) saved), dir);
+ else
+ q_status_message(SM_ORDER, 3, 3, _("No attachments to save"));
+ }
+ }
+ else if(mn_total_cur(msgmap) > 1L)
+ q_status_message4(SM_ORDER,0,3,
+ "%s message%s %s to file \"%s\"",
+ long2string(count), plural(count),
+ rflags & GER_OVER
+ ? "overwritten"
+ : rflags & GER_APPEND ? "appended" : "exported",
+ filename);
+ else
+ q_status_message3(SM_ORDER,0,3,
+ "Message %s %s to file \"%s\"",
+ long2string(mn_get_cur(msgmap)),
+ rflags & GER_OVER
+ ? "overwritten"
+ : rflags & GER_APPEND ? "appended" : "exported",
+ filename);
+ rv++;
+ }
+
+ fini:
+ if(MCMD_ISAGG(aopt))
+ restore_selected(msgmap);
+
+ return rv;
+}
+
+
+/*
+ * Ask user what file to export to. Export from srcstore to that file.
+ *
+ * Args ps -- pine struct
+ * srctext -- pointer to source text
+ * srctype -- type of that source text
+ * prompt_msg -- see get_export_filename
+ * lister_msg -- "
+ *
+ * Returns: != 0 : error
+ * 0 : ok
+ */
+int
+simple_export(struct pine *ps, void *srctext, SourceType srctype, char *prompt_msg, char *lister_msg)
+{
+ int r = 1, rflags = GER_NONE;
+ char filename[MAXPATH+1], full_filename[MAXPATH+1];
+ STORE_S *store = NULL;
+ struct variable *vars = ps->vars;
+ static HISTORY_S *history = NULL;
+ static ESCKEY_S simple_export_opts[] = {
+ {ctrl('T'), 10, "^T", N_("To Files")},
+ {-1, 0, NULL, NULL},
+ {-1, 0, NULL, NULL}};
+
+ if(F_ON(F_ENABLE_TAB_COMPLETE,ps)){
+ simple_export_opts[r].ch = ctrl('I');
+ simple_export_opts[r].rval = 11;
+ simple_export_opts[r].name = "TAB";
+ simple_export_opts[r].label = N_("Complete");
+ }
+
+ if(!srctext){
+ q_status_message(SM_ORDER, 0, 2, _("Error allocating space"));
+ r = -3;
+ goto fini;
+ }
+
+ simple_export_opts[++r].ch = -1;
+ filename[0] = '\0';
+ full_filename[0] = '\0';
+
+ r = get_export_filename(ps, filename, NULL, full_filename, sizeof(filename),
+ prompt_msg, lister_msg, simple_export_opts, &rflags,
+ -FOOTER_ROWS(ps), GE_IS_EXPORT, &history);
+
+ if(r < 0)
+ goto fini;
+ else if(!full_filename[0]){
+ r = -1;
+ goto fini;
+ }
+
+ dprint((5, "Opening file \"%s\" for export\n",
+ full_filename ? full_filename : "?"));
+
+ if((store = so_get(FileStar, full_filename, WRITE_ACCESS|WRITE_TO_LOCALE)) != NULL){
+ char *pipe_err;
+ gf_io_t pc, gc;
+
+ gf_set_so_writec(&pc, store);
+ gf_set_readc(&gc, srctext, (srctype == CharStar)
+ ? strlen((char *)srctext)
+ : 0L,
+ srctype, 0);
+ gf_filter_init();
+ if((pipe_err = gf_pipe(gc, pc)) != NULL){
+ q_status_message2(SM_ORDER | SM_DING, 3, 3,
+ /* TRANSLATORS: Problem saving to <filename>: <error text> */
+ _("Problem saving to \"%s\": %s"),
+ filename, pipe_err);
+ r = -3;
+ }
+ else
+ r = 0;
+
+ gf_clear_so_writec(store);
+ if(so_give(&store)){
+ q_status_message2(SM_ORDER | SM_DING, 3, 3,
+ _("Problem saving to \"%s\": %s"),
+ filename, error_description(errno));
+ r = -3;
+ }
+ }
+ else{
+ q_status_message2(SM_ORDER | SM_DING, 3, 4,
+ _("Error opening file \"%s\" for export: %s"),
+ full_filename, error_description(errno));
+ r = -3;
+ }
+
+fini:
+ switch(r){
+ case 0:
+ /* overloading full_filename */
+ snprintf(full_filename, sizeof(full_filename), "%c%s",
+ (prompt_msg && prompt_msg[0])
+ ? (islower((unsigned char)prompt_msg[0])
+ ? toupper((unsigned char)prompt_msg[0]) : prompt_msg[0])
+ : 'T',
+ (prompt_msg && prompt_msg[0]) ? prompt_msg+1 : "ext");
+ full_filename[sizeof(full_filename)-1] = '\0';
+ q_status_message3(SM_ORDER,0,2,"%s %s to \"%s\"",
+ full_filename,
+ rflags & GER_OVER
+ ? "overwritten"
+ : rflags & GER_APPEND ? "appended" : "exported",
+ filename);
+ break;
+
+ case -1:
+ cmd_cancelled("Export");
+ break;
+
+ case -2:
+ q_status_message1(SM_ORDER, 0, 2,
+ _("Can't export to file outside of %s"), VAR_OPER_DIR);
+ break;
+ }
+
+ ps->mangled_footer = 1;
+ return(r);
+}
+
+
+/*
+ * Ask user what file to export to.
+ *
+ * filename -- On input, this is the filename to start with. On exit,
+ * this is the filename chosen. (but this isn't used)
+ * deefault -- This is the default value if user hits return. The
+ * prompt will have [deefault] added to it automatically.
+ * full_filename -- This is the full filename on exit.
+ * len -- Minimum length of _both_ filename and full_filename.
+ * prompt_msg -- Message to insert in prompt.
+ * lister_msg -- Message to insert in file_lister.
+ * opts -- Key options.
+ * There is a tangled relationship between the callers
+ * and this routine as far as opts are concerned. Some
+ * of the opts are handled here. In particular, r == 3,
+ * r == 10, r == 11, and r == 13 are all handled here.
+ * Don't use those values unless you want what happens
+ * here. r == 12 and others are handled by the caller.
+ * rflags -- Return flags
+ * GER_OVER - overwrite of existing file
+ * GER_APPEND - append of existing file
+ * else file did not exist before
+ *
+ * GER_ALLPARTS - AllParts toggle was turned on
+ *
+ * qline -- Command line to prompt on.
+ * flags -- Logically OR'd flags
+ * GE_IS_EXPORT - The command was an Export command
+ * so the prompt should include
+ * EXPORT:.
+ * GE_SEQ_SENSITIVE - The command that got us here is
+ * sensitive to sequence number changes
+ * caused by unsolicited expunges.
+ * GE_NO_APPEND - We will not allow append to an
+ * existing file, only removal of the
+ * file if it exists.
+ * GE_IS_IMPORT - We are selecting for reading.
+ * No overwriting or checking for
+ * existence at all. Don't use this
+ * together with GE_NO_APPEND.
+ * GE_ALLPARTS - Turn on AllParts toggle.
+ *
+ * Returns: -1 cancelled
+ * -2 prohibited by VAR_OPER_DIR
+ * -3 other error, already reported here
+ * 0 ok
+ * 12 user chose 12 command from opts
+ */
+int
+get_export_filename(struct pine *ps, char *filename, char *deefault,
+ char *full_filename, size_t len, char *prompt_msg,
+ char *lister_msg, ESCKEY_S *optsarg, int *rflags,
+ int qline, int flags, HISTORY_S **history)
+{
+ char dir[MAXPATH+1], dir2[MAXPATH+1];
+ char precolon[MAXPATH+1], postcolon[MAXPATH+1];
+ char filename2[MAXPATH+1], tmp[MAXPATH+1], *fn, *ill;
+ int l, i, ku = -1, r, fatal, homedir = 0, was_abs_path=0, avail, ret = 0;
+ int allparts = 0;
+ char prompt_buf[400];
+ char def[500];
+ ESCKEY_S *opts = NULL;
+ struct variable *vars = ps->vars;
+
+ if(flags & GE_ALLPARTS || history){
+ /*
+ * Copy the opts and add one to the end of the list.
+ */
+ for(i = 0; optsarg[i].ch != -1; i++)
+ ;
+
+ if(history)
+ i += 2;
+
+ if(flags & GE_ALLPARTS)
+ i++;
+
+ opts = (ESCKEY_S *) fs_get((i+1) * sizeof(*opts));
+ memset(opts, 0, (i+1) * sizeof(*opts));
+
+ for(i = 0; optsarg[i].ch != -1; i++){
+ opts[i].ch = optsarg[i].ch;
+ opts[i].rval = optsarg[i].rval;
+ opts[i].name = optsarg[i].name; /* no need to make a copy */
+ opts[i].label = optsarg[i].label; /* " */
+ }
+
+ if(flags & GE_ALLPARTS){
+ allparts = i;
+ opts[i].ch = ctrl('P');
+ opts[i].rval = 13;
+ opts[i].name = "^P";
+ /* TRANSLATORS: Export all attachment parts */
+ opts[i++].label = N_("AllParts");
+ }
+
+ if(history){
+ opts[i].ch = KEY_UP;
+ opts[i].rval = 30;
+ opts[i].name = "";
+ ku = i;
+ opts[i++].label = "";
+
+ opts[i].ch = KEY_DOWN;
+ opts[i].rval = 31;
+ opts[i].name = "";
+ opts[i++].label = "";
+ }
+
+ opts[i].ch = -1;
+
+ if(history)
+ init_hist(history, HISTSIZE);
+ }
+ else
+ opts = optsarg;
+
+ if(rflags)
+ *rflags = GER_NONE;
+
+ if(F_ON(F_USE_CURRENT_DIR, ps))
+ dir[0] = '\0';
+ else if(VAR_OPER_DIR){
+ strncpy(dir, VAR_OPER_DIR, sizeof(dir));
+ dir[sizeof(dir)-1] = '\0';
+ }
+#if defined(DOS) || defined(OS2)
+ else if(VAR_FILE_DIR){
+ strncpy(dir, VAR_FILE_DIR, sizeof(dir));
+ dir[sizeof(dir)-1] = '\0';
+ }
+#endif
+ else{
+ dir[0] = '~';
+ dir[1] = '\0';
+ homedir=1;
+ }
+
+ postcolon[0] = '\0';
+ strncpy(precolon, dir, sizeof(precolon));
+ precolon[sizeof(precolon)-1] = '\0';
+ if(deefault){
+ strncpy(def, deefault, sizeof(def)-1);
+ def[sizeof(def)-1] = '\0';
+ removing_leading_and_trailing_white_space(def);
+ }
+ else
+ def[0] = '\0';
+
+ avail = MAX(20, ps_global->ttyo ? ps_global->ttyo->screen_cols : 80) - MIN_OPT_ENT_WIDTH;
+
+ /*---------- Prompt the user for the file name -------------*/
+ while(1){
+ int oeflags;
+ char dirb[50], fileb[50];
+ int l1, l2, l3, l4, l5, needed;
+ char *p, p1[100], p2[100], *p3, p4[100], p5[100];
+
+ snprintf(p1, sizeof(p1), "%sCopy ",
+ (flags & GE_IS_EXPORT) ? "EXPORT: " :
+ (flags & GE_IS_IMPORT) ? "IMPORT: " : "SAVE: ");
+ p1[sizeof(p1)-1] = '\0';
+ l1 = strlen(p1);
+
+ strncpy(p2, prompt_msg ? prompt_msg : "", sizeof(p2)-1);
+ p2[sizeof(p2)-1] = '\0';
+ l2 = strlen(p2);
+
+ if(rflags && *rflags & GER_ALLPARTS)
+ p3 = " (and atts)";
+ else
+ p3 = "";
+
+ l3 = strlen(p3);
+
+ snprintf(p4, sizeof(p4), " %s file%s%s",
+ (flags & GE_IS_IMPORT) ? "from" : "to",
+ is_absolute_path(filename) ? "" : " in ",
+ is_absolute_path(filename) ? "" :
+ (!dir[0] ? "current directory"
+ : (dir[0] == '~' && !dir[1]) ? "home directory"
+ : short_str(dir,dirb,sizeof(dirb),30,FrontDots)));
+ p4[sizeof(p4)-1] = '\0';
+ l4 = strlen(p4);
+
+ snprintf(p5, sizeof(p5), "%s%s%s: ",
+ *def ? " [" : "",
+ *def ? short_str(def,fileb,sizeof(fileb),40,EndDots) : "",
+ *def ? "]" : "");
+ p5[sizeof(p5)-1] = '\0';
+ l5 = strlen(p5);
+
+ if((needed = l1+l2+l3+l4+l5-avail) > 0){
+ snprintf(p4, sizeof(p4), " %s file%s%s",
+ (flags & GE_IS_IMPORT) ? "from" : "to",
+ is_absolute_path(filename) ? "" : " in ",
+ is_absolute_path(filename) ? "" :
+ (!dir[0] ? "current dir"
+ : (dir[0] == '~' && !dir[1]) ? "home dir"
+ : short_str(dir,dirb,sizeof(dirb),10,FrontDots)));
+ p4[sizeof(p4)-1] = '\0';
+ l4 = strlen(p4);
+ }
+
+ if((needed = l1+l2+l3+l4+l5-avail) > 0 && l5 > 0){
+ snprintf(p5, sizeof(p5), "%s%s%s: ",
+ *def ? " [" : "",
+ *def ? short_str(def,fileb,sizeof(fileb),
+ MAX(15,l5-5-needed),EndDots) : "",
+ *def ? "]" : "");
+ p5[sizeof(p5)-1] = '\0';
+ l5 = strlen(p5);
+ }
+
+ if((needed = l1+l2+l3+l4+l5-avail) > 0 && l2 > 0){
+
+ /*
+ * 14 is about the shortest we can make this, because there are
+ * fixed length strings of length 14 coming in here.
+ */
+ p = short_str(prompt_msg, p2, sizeof(p2), MAX(14,l2-needed), FrontDots);
+ if(p != p2){
+ strncpy(p2, p, sizeof(p2)-1);
+ p2[sizeof(p2)-1] = '\0';
+ }
+
+ l2 = strlen(p2);
+ }
+
+ if((needed = l1+l2+l3+l4+l5-avail) > 0){
+ strncpy(p1, "Copy ", sizeof(p1)-1);
+ p1[sizeof(p1)-1] = '\0';
+ l1 = strlen(p1);
+ }
+
+ if((needed = l1+l2+l3+l4+l5-avail) > 0 && l5 > 0){
+ snprintf(p5, sizeof(p5), "%s%s%s: ",
+ *def ? " [" : "",
+ *def ? short_str(def,fileb, sizeof(fileb),
+ MAX(10,l5-5-needed),EndDots) : "",
+ *def ? "]" : "");
+ p5[sizeof(p5)-1] = '\0';
+ l5 = strlen(p5);
+ }
+
+ if((needed = l1+l2+l3+l4+l5-avail) > 0 && l3 > 0){
+ if(needed <= l3 - strlen(" (+ atts)"))
+ p3 = " (+ atts)";
+ else if(needed <= l3 - strlen(" (atts)"))
+ p3 = " (atts)";
+ else if(needed <= l3 - strlen(" (+)"))
+ p3 = " (+)";
+ else if(needed <= l3 - strlen("+"))
+ p3 = "+";
+ else
+ p3 = "";
+
+ l3 = strlen(p3);
+ }
+
+ snprintf(prompt_buf, sizeof(prompt_buf), "%s%s%s%s%s", p1, p2, p3, p4, p5);
+ prompt_buf[sizeof(prompt_buf)-1] = '\0';
+
+ if(ku >= 0){
+ if(items_in_hist(*history) > 0){
+ opts[ku].name = HISTORY_UP_KEYNAME;
+ opts[ku].label = HISTORY_KEYLABEL;
+ opts[ku+1].name = HISTORY_DOWN_KEYNAME;
+ opts[ku+1].label = HISTORY_KEYLABEL;
+ }
+ else{
+ opts[ku].name = "";
+ opts[ku].label = "";
+ opts[ku+1].name = "";
+ opts[ku+1].label = "";
+ }
+ }
+
+ oeflags = OE_APPEND_CURRENT |
+ ((flags & GE_SEQ_SENSITIVE) ? OE_SEQ_SENSITIVE : 0);
+ r = optionally_enter(filename, qline, 0, len, prompt_buf,
+ opts, NO_HELP, &oeflags);
+
+ /*--- Help ----*/
+ if(r == 3){
+ /*
+ * Helps may not be right if you add another caller or change
+ * things. Check it out.
+ */
+ if(flags & GE_IS_IMPORT)
+ helper(h_ge_import, _("HELP FOR IMPORT FILE SELECT"), HLPD_SIMPLE);
+ else if(flags & GE_ALLPARTS)
+ helper(h_ge_allparts, _("HELP FOR EXPORT FILE SELECT"), HLPD_SIMPLE);
+ else
+ helper(h_ge_export, _("HELP FOR EXPORT FILE SELECT"), HLPD_SIMPLE);
+
+ ps->mangled_screen = 1;
+
+ continue;
+ }
+ else if(r == 10 || r == 11){ /* Browser or File Completion */
+ if(filename[0]=='~'){
+ if(filename[1] == C_FILESEP && filename[2]!='\0'){
+ precolon[0] = '~';
+ precolon[1] = '\0';
+ for(i=0; filename[i+2] != '\0' && i+2 < len-1; i++)
+ filename[i] = filename[i+2];
+ filename[i] = '\0';
+ strncpy(dir, precolon, sizeof(dir)-1);
+ dir[sizeof(dir)-1] = '\0';
+ }
+ else if(filename[1]=='\0' ||
+ (filename[1] == C_FILESEP && filename[2] == '\0')){
+ precolon[0] = '~';
+ precolon[1] = '\0';
+ filename[0] = '\0';
+ strncpy(dir, precolon, sizeof(dir)-1);
+ dir[sizeof(dir)-1] = '\0';
+ }
+ }
+ else if(!dir[0] && !is_absolute_path(filename) && was_abs_path){
+ if(homedir){
+ precolon[0] = '~';
+ precolon[1] = '\0';
+ strncpy(dir, precolon, sizeof(dir)-1);
+ dir[sizeof(dir)-1] = '\0';
+ }
+ else{
+ precolon[0] = '\0';
+ dir[0] = '\0';
+ }
+ }
+ l = MAXPATH;
+ dir2[0] = '\0';
+ strncpy(tmp, filename, sizeof(tmp)-1);
+ tmp[sizeof(tmp)-1] = '\0';
+ if(*tmp && is_absolute_path(tmp))
+ fnexpand(tmp, sizeof(tmp));
+ if(strncmp(tmp,postcolon, strlen(postcolon)))
+ postcolon[0] = '\0';
+
+ if(*tmp && (fn = last_cmpnt(tmp))){
+ l -= fn - tmp;
+ strncpy(filename2, fn, sizeof(filename2)-1);
+ filename2[sizeof(filename2)-1] = '\0';
+ if(is_absolute_path(tmp)){
+ strncpy(dir2, tmp, MIN(fn - tmp, sizeof(dir2)-1));
+ dir2[MIN(fn - tmp, sizeof(dir2)-1)] = '\0';
+#ifdef _WINDOWS
+ if(tmp[1]==':' && tmp[2]=='\\' && dir2[2]=='\0'){
+ dir2[2] = '\\';
+ dir2[3] = '\0';
+ }
+#endif
+ strncpy(postcolon, dir2, sizeof(postcolon)-1);
+ postcolon[sizeof(postcolon)-1] = '\0';
+ precolon[0] = '\0';
+ }
+ else{
+ char *p = NULL;
+ /*
+ * Just building the directory name in dir2,
+ * full_filename is overloaded.
+ */
+ snprintf(full_filename, len, "%.*s", MIN(fn-tmp,len-1), tmp);
+ full_filename[len-1] = '\0';
+ strncpy(postcolon, full_filename, sizeof(postcolon)-1);
+ postcolon[sizeof(postcolon)-1] = '\0';
+ build_path(dir2, !dir[0] ? p = (char *)getcwd(NULL,MAXPATH)
+ : (dir[0] == '~' && !dir[1])
+ ? ps->home_dir
+ : dir,
+ full_filename, sizeof(dir2));
+ if(p)
+ free(p);
+ }
+ }
+ else{
+ if(is_absolute_path(tmp)){
+ strncpy(dir2, tmp, sizeof(dir2)-1);
+ dir2[sizeof(dir2)-1] = '\0';
+#ifdef _WINDOWS
+ if(dir2[2]=='\0' && dir2[1]==':'){
+ dir2[2]='\\';
+ dir2[3]='\0';
+ strncpy(postcolon,dir2,sizeof(postcolon)-1);
+ postcolon[sizeof(postcolon)-1] = '\0';
+ }
+#endif
+ filename2[0] = '\0';
+ precolon[0] = '\0';
+ }
+ else{
+ strncpy(filename2, tmp, sizeof(filename2)-1);
+ filename2[sizeof(filename2)-1] = '\0';
+ if(!dir[0])
+ (void)getcwd(dir2, sizeof(dir2));
+ else if(dir[0] == '~' && !dir[1]){
+ strncpy(dir2, ps->home_dir, sizeof(dir2)-1);
+ dir2[sizeof(dir2)-1] = '\0';
+ }
+ else{
+ strncpy(dir2, dir, sizeof(dir2)-1);
+ dir2[sizeof(dir2)-1] = '\0';
+ }
+
+ postcolon[0] = '\0';
+ }
+ }
+
+ build_path(full_filename, dir2, filename2, len);
+ if(!strcmp(full_filename, dir2))
+ filename2[0] = '\0';
+ if(full_filename[strlen(full_filename)-1] == C_FILESEP
+ && isdir(full_filename,NULL,NULL)){
+ if(strlen(full_filename) == 1)
+ strncpy(postcolon, full_filename, sizeof(postcolon)-1);
+ else if(filename2[0])
+ strncpy(postcolon, filename2, sizeof(postcolon)-1);
+ postcolon[sizeof(postcolon)-1] = '\0';
+ strncpy(dir2, full_filename, sizeof(dir2)-1);
+ dir2[sizeof(dir2)-1] = '\0';
+ filename2[0] = '\0';
+ }
+#ifdef _WINDOWS /* use full_filename even if not a valid directory */
+ else if(full_filename[strlen(full_filename)-1] == C_FILESEP){
+ strncpy(postcolon, filename2, sizeof(postcolon)-1);
+ postcolon[sizeof(postcolon)-1] = '\0';
+ strncpy(dir2, full_filename, sizeof(dir2)-1);
+ dir2[sizeof(dir2)-1] = '\0';
+ filename2[0] = '\0';
+ }
+#endif
+ if(dir2[strlen(dir2)-1] == C_FILESEP && strlen(dir2)!=1
+ && strcmp(dir2+1, ":\\"))
+ /* last condition to prevent stripping of '\\'
+ in windows partition */
+ dir2[strlen(dir2)-1] = '\0';
+
+ if(r == 10){ /* File Browser */
+ r = file_lister(lister_msg ? lister_msg : "EXPORT",
+ dir2, sizeof(dir2), filename2, sizeof(filename2),
+ TRUE,
+ (flags & GE_IS_IMPORT) ? FB_READ : FB_SAVE);
+#ifdef _WINDOWS
+/* Windows has a special "feature" in which entering the file browser will
+ change the working directory if the directory is changed at all (even
+ clicking "Cancel" will change the working directory).
+*/
+ if(F_ON(F_USE_CURRENT_DIR, ps))
+ (void)getcwd(dir2,sizeof(dir2));
+#endif
+ if(isdir(dir2,NULL,NULL)){
+ strncpy(precolon, dir2, sizeof(precolon)-1);
+ precolon[sizeof(precolon)-1] = '\0';
+ }
+ strncpy(postcolon, filename2, sizeof(postcolon)-1);
+ postcolon[sizeof(postcolon)-1] = '\0';
+ if(r == 1){
+ build_path(full_filename, dir2, filename2, len);
+ if(isdir(full_filename, NULL, NULL)){
+ strncpy(dir, full_filename, sizeof(dir)-1);
+ dir[sizeof(dir)-1] = '\0';
+ filename[0] = '\0';
+ }
+ else{
+ fn = last_cmpnt(full_filename);
+ strncpy(dir, full_filename,
+ MIN(fn - full_filename, sizeof(dir)-1));
+ dir[MIN(fn - full_filename, sizeof(dir)-1)] = '\0';
+ if(fn - full_filename > 1)
+ dir[fn - full_filename - 1] = '\0';
+ }
+
+ if(!strcmp(dir, ps->home_dir)){
+ dir[0] = '~';
+ dir[1] = '\0';
+ }
+
+ strncpy(filename, fn, len-1);
+ filename[len-1] = '\0';
+ }
+ }
+ else{ /* File Completion */
+ if(!pico_fncomplete(dir2, filename2, sizeof(filename2)))
+ Writechar(BELL, 0);
+ strncat(postcolon, filename2,
+ sizeof(postcolon)-1-strlen(postcolon));
+ postcolon[sizeof(postcolon)-1] = '\0';
+
+ was_abs_path = is_absolute_path(filename);
+
+ if(!strcmp(dir, ps->home_dir)){
+ dir[0] = '~';
+ dir[1] = '\0';
+ }
+ }
+ strncpy(filename, postcolon, len-1);
+ filename[len-1] = '\0';
+ strncpy(dir, precolon, sizeof(dir)-1);
+ dir[sizeof(dir)-1] = '\0';
+
+ if(filename[0] == '~' && !filename[1]){
+ dir[0] = '~';
+ dir[1] = '\0';
+ filename[0] = '\0';
+ }
+
+ continue;
+ }
+ else if(r == 12){ /* Download, caller handles it */
+ ret = r;
+ goto done;
+ }
+ else if(r == 13){ /* toggle AllParts bit */
+ if(rflags){
+ if(*rflags & GER_ALLPARTS){
+ *rflags &= ~GER_ALLPARTS;
+ opts[allparts].label = N_("AllParts");
+ }
+ else{
+ *rflags |= GER_ALLPARTS;
+ /* opposite of All Parts, No All Parts */
+ opts[allparts].label = N_("NoAllParts");
+ }
+ }
+
+ continue;
+ }
+#if 0
+ else if(r == 14){ /* List file names matching partial? */
+ continue;
+ }
+#endif
+ else if(r == 1){ /* Cancel */
+ ret = -1;
+ goto done;
+ }
+ else if(r == 4){
+ continue;
+ }
+ else if(r == 30 || r == 31){
+ char *p = NULL;
+
+ if(history){
+ if(r == 30)
+ p = get_prev_hist(*history, filename, 0, NULL);
+ else
+ p = get_next_hist(*history, filename, 0, NULL);
+ }
+
+ if(p != NULL){
+ fn = last_cmpnt(p);
+ strncpy(dir, p, MIN(fn - p, sizeof(dir)-1));
+ dir[MIN(fn - p, sizeof(dir)-1)] = '\0';
+ if(fn - p > 1)
+ dir[fn - p - 1] = '\0';
+
+ if(!strcmp(dir, ps->home_dir)){
+ dir[0] = '~';
+ dir[1] = '\0';
+ }
+
+ strncpy(filename, fn, len-1);
+ filename[len-1] = '\0';
+ }
+ else
+ Writechar(BELL, 0);
+
+ continue;
+ }
+ else if(r != 0){
+ Writechar(BELL, 0);
+ continue;
+ }
+
+ removing_leading_and_trailing_white_space(filename);
+
+ if(!*filename){
+ if(!*def){ /* Cancel */
+ ret = -1;
+ goto done;
+ }
+
+ strncpy(filename, def, len-1);
+ filename[len-1] = '\0';
+ }
+
+#if defined(DOS) || defined(OS2)
+ if(is_absolute_path(filename)){
+ fixpath(filename, len);
+ }
+#else
+ if(filename[0] == '~'){
+ if(fnexpand(filename, len) == NULL){
+ char *p = strindex(filename, '/');
+ if(p != NULL)
+ *p = '\0';
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("Error expanding file name: \"%s\" unknown user"),
+ filename);
+ continue;
+ }
+ }
+#endif
+
+ if(is_absolute_path(filename)){
+ strncpy(full_filename, filename, len-1);
+ full_filename[len-1] = '\0';
+ }
+ else{
+ if(!dir[0])
+ build_path(full_filename, (char *)getcwd(dir,sizeof(dir)),
+ filename, len);
+ else if(dir[0] == '~' && !dir[1])
+ build_path(full_filename, ps->home_dir, filename, len);
+ else
+ build_path(full_filename, dir, filename, len);
+ }
+
+ if((ill = filter_filename(full_filename, &fatal,
+ ps_global->restricted || ps_global->VAR_OPER_DIR)) != NULL){
+ if(fatal){
+ q_status_message1(SM_ORDER | SM_DING, 3, 3, "%s", ill);
+ continue;
+ }
+ else{
+/* BUG: we should beep when the key's pressed rather than bitch later */
+ /* Warn and ask for confirmation. */
+ snprintf(prompt_buf, sizeof(prompt_buf), "File name contains a '%s'. %s anyway",
+ ill, (flags & GE_IS_EXPORT) ? "Export" : "Save");
+ prompt_buf[sizeof(prompt_buf)-1] = '\0';
+ if(want_to(prompt_buf, 'n', 0, NO_HELP,
+ ((flags & GE_SEQ_SENSITIVE) ? RB_SEQ_SENSITIVE : 0)) != 'y')
+ continue;
+ }
+ }
+
+ break; /* Must have got an OK file name */
+ }
+
+ if(VAR_OPER_DIR && !in_dir(VAR_OPER_DIR, full_filename)){
+ ret = -2;
+ goto done;
+ }
+
+ if(!can_access(full_filename, ACCESS_EXISTS)){
+ int rbflags;
+ static ESCKEY_S access_opts[] = {
+ /* TRANSLATORS: asking user if they want to overwrite (replace contents of)
+ a file or append to the end of the file */
+ {'o', 'o', "O", N_("Overwrite")},
+ {'a', 'a', "A", N_("Append")},
+ {-1, 0, NULL, NULL}};
+
+ rbflags = RB_NORM | ((flags & GE_SEQ_SENSITIVE) ? RB_SEQ_SENSITIVE : 0);
+
+ if(flags & GE_NO_APPEND){
+ r = strlen(filename);
+ snprintf(prompt_buf, sizeof(prompt_buf),
+ /* TRANSLATORS: asking user whether to overwrite a file or not,
+ File <filename> already exists. Overwrite it ? */
+ _("File \"%s%s\" already exists. Overwrite it "),
+ (r > 20) ? "..." : "",
+ filename + ((r > 20) ? r - 20 : 0));
+ prompt_buf[sizeof(prompt_buf)-1] = '\0';
+ if(want_to(prompt_buf, 'n', 'x', NO_HELP, rbflags) == 'y'){
+ if(rflags)
+ *rflags |= GER_OVER;
+
+ if(our_unlink(full_filename) < 0){
+ q_status_message2(SM_ORDER | SM_DING, 3, 5,
+ /* TRANSLATORS: Cannot remove old <filename>: <error text> */
+ _("Cannot remove old %s: %s"),
+ full_filename, error_description(errno));
+ }
+ }
+ else{
+ ret = -1;
+ goto done;
+ }
+ }
+ else if(!(flags & GE_IS_IMPORT)){
+ r = strlen(filename);
+ snprintf(prompt_buf, sizeof(prompt_buf),
+ /* TRANSLATORS: File <filename> already exists. Overwrite or append to it ? */
+ _("File \"%s%s\" already exists. Overwrite or append to it ? "),
+ (r > 20) ? "..." : "",
+ filename + ((r > 20) ? r - 20 : 0));
+ prompt_buf[sizeof(prompt_buf)-1] = '\0';
+ switch(radio_buttons(prompt_buf, -FOOTER_ROWS(ps_global),
+ access_opts, 'a', 'x', NO_HELP, rbflags)){
+ case 'o' :
+ if(rflags)
+ *rflags |= GER_OVER;
+
+ if(our_truncate(full_filename, (off_t)0) < 0)
+ /* trouble truncating, but we'll give it a try anyway */
+ q_status_message2(SM_ORDER | SM_DING, 3, 5,
+ /* TRANSLATORS: Warning: Cannot truncate old <filename>: <error text> */
+ _("Warning: Cannot truncate old %s: %s"),
+ full_filename, error_description(errno));
+ break;
+
+ case 'a' :
+ if(rflags)
+ *rflags |= GER_APPEND;
+
+ break;
+
+ case 'x' :
+ default :
+ ret = -1;
+ goto done;
+ }
+ }
+ }
+
+done:
+ if(history && ret == 0)
+ save_hist(*history, full_filename, 0, NULL);
+
+ if(opts && opts != optsarg)
+ fs_give((void **) &opts);
+
+ return(ret);
+}
+
+
+/*----------------------------------------------------------------------
+ parse the config'd upload/download command
+
+ Args: cmd -- buffer to return command fit for shellin'
+ prefix --
+ cfg_str --
+ fname -- file name to build into the command
+
+ Returns: pointer to cmd_str buffer or NULL on real bad error
+
+ NOTE: One SIDE EFFECT is that any defined "prefix" string in the
+ cfg_str is written to standard out right before a successful
+ return of this function. The call immediately following this
+ function darn well better be the shell exec...
+ ----*/
+char *
+build_updown_cmd(char *cmd, size_t cmdlen, char *prefix, char *cfg_str, char *fname)
+{
+ char *p;
+ int fname_found = 0;
+
+ if(prefix && *prefix){
+ /* loop thru replacing all occurances of _FILE_ */
+ p = strncpy(cmd, prefix, cmdlen);
+ cmd[cmdlen-1] = '\0';
+ while((p = strstr(p, "_FILE_")))
+ rplstr(p, cmdlen-(p-cmd), 6, fname);
+
+ fputs(cmd, stdout);
+ }
+
+ /* loop thru replacing all occurances of _FILE_ */
+ p = strncpy(cmd, cfg_str, cmdlen);
+ cmd[cmdlen-1] = '\0';
+ while((p = strstr(p, "_FILE_"))){
+ rplstr(p, cmdlen-(p-cmd), 6, fname);
+ fname_found = 1;
+ }
+
+ if(!fname_found)
+ snprintf(cmd+strlen(cmd), cmdlen-strlen(cmd), " %s", fname);
+
+ cmd[cmdlen-1] = '\0';
+
+ dprint((4, "\n - build_updown_cmd = \"%s\" -\n",
+ cmd ? cmd : "?"));
+ return(cmd);
+}
+
+
+/*----------------------------------------------------------------------
+ Write a berzerk format message delimiter using the given putc function
+
+ Args: e -- envelope of message to write
+ pc -- function to use
+
+ Returns: TRUE if we could write it, FALSE if there was a problem
+
+ NOTE: follows delimiter with OS-dependent newline
+ ----*/
+int
+bezerk_delimiter(ENVELOPE *env, MESSAGECACHE *mc, gf_io_t pc, int leading_newline)
+{
+ MESSAGECACHE telt;
+ time_t when;
+ char *p;
+
+ /* write "[\n]From mailbox[@host] " */
+ if(!((leading_newline ? gf_puts(NEWLINE, pc) : 1)
+ && gf_puts("From ", pc)
+ && gf_puts((env && env->from) ? env->from->mailbox
+ : "the-concourse-on-high", pc)
+ && gf_puts((env && env->from && env->from->host) ? "@" : "", pc)
+ && gf_puts((env && env->from && env->from->host) ? env->from->host
+ : "", pc)
+ && (*pc)(' ')))
+ return(0);
+
+ if(mc && mc->valid)
+ when = mail_longdate(mc);
+ else if(env && env->date && env->date[0]
+ && mail_parse_date(&telt,env->date))
+ when = mail_longdate(&telt);
+ else
+ when = time(0);
+
+ p = ctime(&when);
+
+ while(p && *p && *p != '\n') /* write date */
+ if(!(*pc)(*p++))
+ return(0);
+
+ if(!gf_puts(NEWLINE, pc)) /* write terminating newline */
+ return(0);
+
+ return(1);
+}
+
+
+/*----------------------------------------------------------------------
+ Execute command to jump to a given message number
+
+ Args: qline -- Line to ask question on
+
+ Result: returns true if the use selected a new message, false otherwise
+
+ ----*/
+long
+jump_to(MSGNO_S *msgmap, int qline, UCS first_num, SCROLL_S *sparms, CmdWhere in_index)
+{
+ char jump_num_string[80], *j, prompt[70];
+ HelpType help;
+ int rc;
+ static ESCKEY_S jump_to_key[] = { {0, 0, NULL, NULL},
+ /* TRANSLATORS: go to First Message */
+ {ctrl('Y'), 10, "^Y", N_("First Msg")},
+ {ctrl('V'), 11, "^V", N_("Last Msg")},
+ {-1, 0, NULL, NULL} };
+
+ dprint((4, "\n - jump_to -\n"));
+
+#ifdef DEBUG
+ if(sparms && sparms->jump_is_debug)
+ return(get_level(qline, first_num, sparms));
+#endif
+
+ if(!any_messages(msgmap, NULL, "to Jump to"))
+ return(0L);
+
+ if(first_num && first_num < 0x80 && isdigit((unsigned char) first_num)){
+ jump_num_string[0] = first_num;
+ jump_num_string[1] = '\0';
+ }
+ else
+ jump_num_string[0] = '\0';
+
+ if(mn_total_cur(msgmap) > 1L){
+ snprintf(prompt, sizeof(prompt), "Unselect %s msgs in favor of number to be entered",
+ comatose(mn_total_cur(msgmap)));
+ prompt[sizeof(prompt)-1] = '\0';
+ if((rc = want_to(prompt, 'n', 0, NO_HELP, WT_NORM)) == 'n')
+ return(0L);
+ }
+
+ snprintf(prompt, sizeof(prompt), "%s number to jump to : ", in_index == ThrdIndx
+ ? "Thread"
+ : "Message");
+ prompt[sizeof(prompt)-1] = '\0';
+
+ help = NO_HELP;
+ while(1){
+ int flags = OE_APPEND_CURRENT;
+
+ rc = optionally_enter(jump_num_string, qline, 0,
+ sizeof(jump_num_string), prompt,
+ jump_to_key, help, &flags);
+ if(rc == 3){
+ help = help == NO_HELP
+ ? (in_index == ThrdIndx ? h_oe_jump_thd : h_oe_jump)
+ : NO_HELP;
+ continue;
+ }
+ else if(rc == 10 || rc == 11){
+ char warning[100];
+ long closest;
+
+ closest = closest_jump_target(rc == 10 ? 1L
+ : ((in_index == ThrdIndx)
+ ? msgmap->max_thrdno
+ : mn_get_total(msgmap)),
+ ps_global->mail_stream,
+ msgmap, 0,
+ in_index, warning, sizeof(warning));
+ /* ignore warning */
+ return(closest);
+ }
+
+ /*
+ * If we take out the *jump_num_string nonempty test in this if
+ * then the closest_jump_target routine will offer a jump to the
+ * last message. However, it is slow because you have to wait for
+ * the status message and it is annoying for people who hit J command
+ * by mistake and just want to hit return to do nothing, like has
+ * always worked. So the test is there for now. Hubert 2002-08-19
+ *
+ * Jumping to first/last message is now possible through ^Y/^V
+ * commands above. jpf 2002-08-21
+ * (and through "end" hubert 2006-07-07)
+ */
+ if(rc == 0 && *jump_num_string != '\0'){
+ removing_leading_and_trailing_white_space(jump_num_string);
+ for(j=jump_num_string; isdigit((unsigned char)*j) || *j=='-'; j++)
+ ;
+
+ if(*j != '\0'){
+ if(!strucmp("end", j))
+ return((in_index == ThrdIndx) ? msgmap->max_thrdno : mn_get_total(msgmap));
+
+ q_status_message(SM_ORDER | SM_DING, 2, 2,
+ _("Invalid number entered. Use only digits 0-9"));
+ jump_num_string[0] = '\0';
+ }
+ else{
+ char warning[100];
+ long closest, jump_num;
+
+ if(*jump_num_string)
+ jump_num = atol(jump_num_string);
+ else
+ jump_num = -1L;
+
+ warning[0] = '\0';
+ closest = closest_jump_target(jump_num, ps_global->mail_stream,
+ msgmap,
+ *jump_num_string ? 0 : 1,
+ in_index, warning, sizeof(warning));
+ if(warning[0])
+ q_status_message(SM_ORDER | SM_DING, 2, 2, warning);
+
+ if(closest == jump_num)
+ return(jump_num);
+
+ if(closest == 0L)
+ jump_num_string[0] = '\0';
+ else
+ strncpy(jump_num_string, long2string(closest),
+ sizeof(jump_num_string));
+ }
+
+ continue;
+ }
+
+ if(rc != 4)
+ break;
+ }
+
+ return(0L);
+}
+
+
+/*
+ * cmd_delete_action - handle msgno advance and such after single message deletion
+ */
+char *
+cmd_delete_action(struct pine *state, MSGNO_S *msgmap, CmdWhere in_index)
+{
+ int opts;
+ long msgno;
+ char *rv = NULL;
+
+ msgno = mn_get_cur(msgmap);
+ advance_cur_after_delete(state, state->mail_stream, msgmap, in_index);
+
+ if(IS_NEWS(state->mail_stream)
+ || ((state->context_current->use & CNTXT_INCMNG)
+ && context_isambig(state->cur_folder))){
+
+ opts = (NSF_TRUST_FLAGS | NSF_SKIP_CHID);
+ if(in_index == View)
+ opts &= ~NSF_SKIP_CHID;
+
+ (void)next_sorted_flagged(F_UNDEL|F_UNSEEN, state->mail_stream, msgno, &opts);
+ if(!(opts & NSF_FLAG_MATCH)){
+ char nextfolder[MAXPATH];
+
+ strncpy(nextfolder, state->cur_folder, sizeof(nextfolder));
+ nextfolder[sizeof(nextfolder)-1] = '\0';
+ rv = next_folder(NULL, nextfolder, sizeof(nextfolder), nextfolder,
+ state->context_current, NULL, NULL)
+ ? ". Press TAB for next folder."
+ : ". No more folders to TAB to.";
+ }
+ }
+
+ return(rv);
+}
+
+
+/*
+ * cmd_delete_index - fixup msgmap or whatever after cmd_delete has done it's thing
+ */
+char *
+cmd_delete_index(struct pine *state, MSGNO_S *msgmap)
+{
+ return(cmd_delete_action(state, msgmap,MsgIndx));
+}
+
+/*
+ * cmd_delete_view - fixup msgmap or whatever after cmd_delete has done it's thing
+ */
+char *
+cmd_delete_view(struct pine *state, MSGNO_S *msgmap)
+{
+ return(cmd_delete_action(state, msgmap, View));
+}
+
+
+void
+advance_cur_after_delete(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, CmdWhere in_index)
+{
+ long new_msgno, msgno;
+ int opts;
+
+ new_msgno = msgno = mn_get_cur(msgmap);
+ opts = NSF_TRUST_FLAGS;
+
+ if(F_ON(F_DEL_SKIPS_DEL, state)){
+
+ if(THREADING() && sp_viewing_a_thread(stream))
+ opts |= NSF_SKIP_CHID;
+
+ new_msgno = next_sorted_flagged(F_UNDEL, stream, msgno, &opts);
+ }
+ else{
+ mn_inc_cur(stream, msgmap,
+ (in_index == View && THREADING()
+ && sp_viewing_a_thread(stream))
+ ? MH_THISTHD
+ : (in_index == View)
+ ? MH_ANYTHD : MH_NONE);
+ new_msgno = mn_get_cur(msgmap);
+ if(new_msgno != msgno)
+ opts |= NSF_FLAG_MATCH;
+ }
+
+ /*
+ * Viewing_a_thread is the complicated case because we want to ignore
+ * other threads at first and then look in other threads if we have to.
+ * By ignoring other threads we also ignore collapsed partial threads
+ * in our own thread.
+ */
+ if(THREADING() && sp_viewing_a_thread(stream) && !(opts & NSF_FLAG_MATCH)){
+ long rawno, orig_thrdno;
+ PINETHRD_S *thrd, *topthrd = NULL;
+
+ rawno = mn_m2raw(msgmap, msgno);
+ thrd = fetch_thread(stream, rawno);
+ if(thrd && thrd->top)
+ topthrd = fetch_thread(stream, thrd->top);
+
+ orig_thrdno = topthrd ? topthrd->thrdno : -1L;
+
+ opts = NSF_TRUST_FLAGS;
+ new_msgno = next_sorted_flagged(F_UNDEL, stream, msgno, &opts);
+
+ /*
+ * If we got a match, new_msgno may be a message in
+ * a different thread from the one we are viewing, or it could be
+ * in a collapsed part of this thread.
+ */
+ if(opts & NSF_FLAG_MATCH){
+ int ret;
+ char pmt[128];
+
+ topthrd = NULL;
+ thrd = fetch_thread(stream, mn_m2raw(msgmap,new_msgno));
+ if(thrd && thrd->top)
+ topthrd = fetch_thread(stream, thrd->top);
+
+ /*
+ * If this match is in the same thread we're already in
+ * then we're done, else we have to ask the user and maybe
+ * switch threads.
+ */
+ if(!(orig_thrdno > 0L && topthrd
+ && topthrd->thrdno == orig_thrdno)){
+
+ if(F_OFF(F_AUTO_OPEN_NEXT_UNREAD, state)){
+ if(in_index == View)
+ snprintf(pmt, sizeof(pmt),
+ "View message in thread number %.10s",
+ topthrd ? comatose(topthrd->thrdno) : "?");
+ else
+ snprintf(pmt, sizeof(pmt), "View thread number %.10s",
+ topthrd ? comatose(topthrd->thrdno) : "?");
+
+ ret = want_to(pmt, 'y', 'x', NO_HELP, WT_NORM);
+ }
+ else
+ ret = 'y';
+
+ if(ret == 'y'){
+ unview_thread(state, stream, msgmap);
+ mn_set_cur(msgmap, new_msgno);
+ if(THRD_AUTO_VIEW()
+ && (count_lflags_in_thread(stream, topthrd, msgmap,
+ MN_NONE) == 1)
+ && view_thread(state, stream, msgmap, 1)){
+ if(current_index_state)
+ msgmap->top_after_thrd = current_index_state->msg_at_top;
+
+ state->view_skipped_index = 1;
+ state->next_screen = mail_view_screen;
+ }
+ else{
+ view_thread(state, stream, msgmap, 1);
+ if(current_index_state)
+ msgmap->top_after_thrd = current_index_state->msg_at_top;
+
+ state->next_screen = SCREEN_FUN_NULL;
+ }
+ }
+ else
+ new_msgno = msgno; /* stick with original */
+ }
+ }
+ }
+
+ mn_set_cur(msgmap, new_msgno);
+ if(in_index != View)
+ adjust_cur_to_visible(stream, msgmap);
+}
+
+
+#ifdef DEBUG
+long
+get_level(int qline, UCS first_num, SCROLL_S *sparms)
+{
+ char debug_num_string[80], *j, prompt[70];
+ HelpType help;
+ int rc;
+ long debug_num;
+
+ if(first_num && first_num < 0x80 && isdigit((unsigned char)first_num)){
+ debug_num_string[0] = first_num;
+ debug_num_string[1] = '\0';
+ debug_num = atol(debug_num_string);
+ *(int *)(sparms->proc.data.p) = debug_num;
+ q_status_message1(SM_ORDER, 0, 3, "Show debug <= level %s",
+ comatose(debug_num));
+ return(1L);
+ }
+ else
+ debug_num_string[0] = '\0';
+
+ snprintf(prompt, sizeof(prompt), "Show debug <= this level (0-%d) : ", MAX(debug, 9));
+ prompt[sizeof(prompt)-1] = '\0';
+
+ help = NO_HELP;
+ while(1){
+ int flags = OE_APPEND_CURRENT;
+
+ rc = optionally_enter(debug_num_string, qline, 0,
+ sizeof(debug_num_string), prompt,
+ NULL, help, &flags);
+ if(rc == 3){
+ help = help == NO_HELP ? h_oe_debuglevel : NO_HELP;
+ continue;
+ }
+
+ if(rc == 0){
+ removing_leading_and_trailing_white_space(debug_num_string);
+ for(j=debug_num_string; isdigit((unsigned char)*j); j++)
+ ;
+
+ if(*j != '\0'){
+ q_status_message(SM_ORDER | SM_DING, 2, 2,
+ _("Invalid number entered. Use only digits 0-9"));
+ debug_num_string[0] = '\0';
+ }
+ else{
+ debug_num = atol(debug_num_string);
+ if(debug_num < 0)
+ q_status_message(SM_ORDER | SM_DING, 2, 2,
+ _("Number should be >= 0"));
+ else if(debug_num > MAX(debug,9))
+ q_status_message1(SM_ORDER | SM_DING, 2, 2,
+ _("Maximum is %s"), comatose(MAX(debug,9)));
+ else{
+ *(int *)(sparms->proc.data.p) = debug_num;
+ q_status_message1(SM_ORDER, 0, 3,
+ "Show debug <= level %s",
+ comatose(debug_num));
+ return(1L);
+ }
+ }
+
+ continue;
+ }
+
+ if(rc != 4)
+ break;
+ }
+
+ return(0L);
+}
+#endif /* DEBUG */
+
+
+/*
+ * Returns the message number closest to target that isn't hidden.
+ * Make warning at least 100 chars.
+ * A return of 0 means there is no message to jump to.
+ */
+long
+closest_jump_target(long int target, MAILSTREAM *stream, MSGNO_S *msgmap, int no_target, CmdWhere in_index, char *warning, size_t warninglen)
+{
+ long i, start, closest = 0L;
+ char buf[80];
+ long maxnum;
+
+ warning[0] = '\0';
+ maxnum = (in_index == ThrdIndx) ? msgmap->max_thrdno : mn_get_total(msgmap);
+
+ if(no_target){
+ target = maxnum;
+ start = 1L;
+ snprintf(warning, warninglen, "No %s number entered, jump to end? ",
+ (in_index == ThrdIndx) ? "thread" : "message");
+ warning[warninglen-1] = '\0';
+ }
+ else if(target < 1L)
+ start = 1L - target;
+ else if(target > maxnum)
+ start = target - maxnum;
+ else
+ start = 1L;
+
+ if(target > 0L && target <= maxnum)
+ if(in_index == ThrdIndx
+ || !msgline_hidden(stream, msgmap, target, 0))
+ return(target);
+
+ for(i = start; target+i <= maxnum || target-i > 0L; i++){
+
+ if(target+i > 0L && target+i <= maxnum &&
+ (in_index == ThrdIndx
+ || !msgline_hidden(stream, msgmap, target+i, 0))){
+ closest = target+i;
+ break;
+ }
+
+ if(target-i > 0L && target-i <= maxnum &&
+ (in_index == ThrdIndx
+ || !msgline_hidden(stream, msgmap, target-i, 0))){
+ closest = target-i;
+ break;
+ }
+ }
+
+ strncpy(buf, long2string(closest), sizeof(buf));
+ buf[sizeof(buf)-1] = '\0';
+
+ if(closest == 0L)
+ strncpy(warning, "Nothing to jump to", warninglen);
+ else if(target < 1L)
+ snprintf(warning, warninglen, "%s number (%s) must be at least %s",
+ (in_index == ThrdIndx) ? "Thread" : "Message",
+ long2string(target), buf);
+ else if(target > maxnum)
+ snprintf(warning, warninglen, "%s number (%s) may be no more than %s",
+ (in_index == ThrdIndx) ? "Thread" : "Message",
+ long2string(target), buf);
+ else if(!no_target)
+ snprintf(warning, warninglen,
+ "Message number (%s) is not in \"Zoomed Index\" - Closest is(%s)",
+ long2string(target), buf);
+
+ warning[warninglen-1] = '\0';
+
+ return(closest);
+}
+
+
+/*----------------------------------------------------------------------
+ Prompt for folder name to open, expand the name and return it
+
+ Args: qline -- Screen line to prompt on
+ allow_list -- if 1, allow ^T to bring up collection lister
+
+ Result: returns the folder name or NULL
+ pine structure mangled_footer flag is set
+ may call the collection lister in which case mangled screen will be set
+
+ This prompts the user for the folder to open, possibly calling up
+the collection lister if the user types ^T.
+----------------------------------------------------------------------*/
+char *
+broach_folder(int qline, int allow_list, int *notrealinbox, CONTEXT_S **context)
+{
+ HelpType help;
+ static char newfolder[MAILTMPLEN];
+ char expanded[MAXPATH+1],
+ prompt[MAX_SCREEN_COLS+1],
+ *last_folder, *p;
+ static HISTORY_S *history = NULL;
+ CONTEXT_S *tc, *tc2;
+ ESCKEY_S ekey[9];
+ int rc, r, ku = -1, n, flags, last_rc = 0, inbox, done = 0;
+
+ /*
+ * the idea is to provide a clue for the context the file name
+ * will be saved in (if a non-imap names is typed), and to
+ * only show the previous if it was also in the same context
+ */
+ help = NO_HELP;
+ *expanded = '\0';
+ *newfolder = '\0';
+ last_folder = NULL;
+ if(notrealinbox)
+ (*notrealinbox) = 1;
+
+ init_hist(&history, HISTSIZE);
+
+ tc = broach_get_folder(context ? *context : NULL, &inbox, NULL);
+
+ /* set up extra command option keys */
+ rc = 0;
+ ekey[rc].ch = (allow_list) ? ctrl('T') : 0 ;
+ ekey[rc].rval = (allow_list) ? 2 : 0;
+ ekey[rc].name = (allow_list) ? "^T" : "";
+ ekey[rc++].label = (allow_list) ? N_("ToFldrs") : "";
+
+ if(ps_global->context_list->next){
+ ekey[rc].ch = ctrl('P');
+ ekey[rc].rval = 10;
+ ekey[rc].name = "^P";
+ ekey[rc++].label = N_("Prev Collection");
+
+ ekey[rc].ch = ctrl('N');
+ ekey[rc].rval = 11;
+ ekey[rc].name = "^N";
+ ekey[rc++].label = N_("Next Collection");
+ }
+
+ ekey[rc].ch = ctrl('W');
+ ekey[rc].rval = 17;
+ ekey[rc].name = "^W";
+ ekey[rc++].label = N_("INBOX");
+
+ if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
+ ekey[rc].ch = TAB;
+ ekey[rc].rval = 12;
+ ekey[rc].name = "TAB";
+ ekey[rc++].label = N_("Complete");
+ }
+
+ if(F_ON(F_ENABLE_SUB_LISTS, ps_global)){
+ ekey[rc].ch = ctrl('X');
+ ekey[rc].rval = 14;
+ ekey[rc].name = "^X";
+ ekey[rc++].label = N_("ListMatches");
+ }
+
+ if(ps_global->context_list->next && F_ON(F_DISABLE_SAVE_INPUT_HISTORY, ps_global)){
+ ekey[rc].ch = KEY_UP;
+ ekey[rc].rval = 10;
+ ekey[rc].name = "";
+ ekey[rc++].label = "";
+
+ ekey[rc].ch = KEY_DOWN;
+ ekey[rc].rval = 11;
+ ekey[rc].name = "";
+ ekey[rc++].label = "";
+ }
+ else if(F_OFF(F_DISABLE_SAVE_INPUT_HISTORY, ps_global)){
+ ekey[rc].ch = KEY_UP;
+ ekey[rc].rval = 30;
+ ekey[rc].name = "";
+ ku = rc;
+ ekey[rc++].label = "";
+
+ ekey[rc].ch = KEY_DOWN;
+ ekey[rc].rval = 31;
+ ekey[rc].name = "";
+ ekey[rc++].label = "";
+ }
+
+ ekey[rc].ch = -1;
+
+ while(!done) {
+ /*
+ * Figure out next default value for this context. The idea
+ * is that in each context the last folder opened is cached.
+ * It's up to pick it out and display it. This is fine
+ * and dandy if we've currently got the inbox open, BUT
+ * if not, make the inbox the default the first time thru.
+ */
+ if(!inbox){
+ last_folder = ps_global->inbox_name;
+ inbox = 1; /* pretend we're in inbox from here on out */
+ }
+ else
+ last_folder = (ps_global->last_unambig_folder[0])
+ ? ps_global->last_unambig_folder
+ : ((tc->last_folder[0]) ? tc->last_folder : NULL);
+
+ if(last_folder){
+ unsigned char *fname = folder_name_decoded((unsigned char *)last_folder);
+ snprintf(expanded, sizeof(expanded), " [%.*s]", sizeof(expanded)-5,
+ fname ? (char *) fname : last_folder);
+ if(fname) fs_give((void **)&fname);
+ }
+ else
+ *expanded = '\0';
+
+ expanded[sizeof(expanded)-1] = '\0';
+
+ /* only show collection number if more than one available */
+ if(ps_global->context_list->next)
+ snprintf(prompt, sizeof(prompt), "GOTO %s in <%s> %.*s%s: ",
+ NEWS_TEST(tc) ? "news group" : "folder",
+ tc->nickname, sizeof(prompt)-50, expanded,
+ *expanded ? " " : "");
+ else
+ snprintf(prompt, sizeof(prompt), "GOTO folder %.*s%s: ", sizeof(prompt)-20, expanded,
+ *expanded ? " " : "");
+
+ prompt[sizeof(prompt)-1] = '\0';
+
+ if(utf8_width(prompt) > MAXPROMPT){
+ if(ps_global->context_list->next)
+ snprintf(prompt, sizeof(prompt), "GOTO <%s> %.*s%s: ",
+ tc->nickname, sizeof(prompt)-50, expanded,
+ *expanded ? " " : "");
+ else
+ snprintf(prompt, sizeof(prompt), "GOTO %.*s%s: ", sizeof(prompt)-20, expanded,
+ *expanded ? " " : "");
+
+ prompt[sizeof(prompt)-1] = '\0';
+
+ if(utf8_width(prompt) > MAXPROMPT){
+ if(ps_global->context_list->next)
+ snprintf(prompt, sizeof(prompt), "<%s> %.*s%s: ",
+ tc->nickname, sizeof(prompt)-50, expanded,
+ *expanded ? " " : "");
+ else
+ snprintf(prompt, sizeof(prompt), "%.*s%s: ", sizeof(prompt)-20, expanded,
+ *expanded ? " " : "");
+
+ prompt[sizeof(prompt)-1] = '\0';
+ }
+ }
+
+ if(ku >= 0){
+ if(items_in_hist(history) > 1){
+ ekey[ku].name = HISTORY_UP_KEYNAME;
+ ekey[ku].label = HISTORY_KEYLABEL;
+ ekey[ku+1].name = HISTORY_DOWN_KEYNAME;
+ ekey[ku+1].label = HISTORY_KEYLABEL;
+ }
+ else{
+ ekey[ku].name = "";
+ ekey[ku].label = "";
+ ekey[ku+1].name = "";
+ ekey[ku+1].label = "";
+ }
+ }
+
+ flags = OE_APPEND_CURRENT;
+ rc = optionally_enter(newfolder, qline, 0, sizeof(newfolder),
+ prompt, ekey, help, &flags);
+
+ ps_global->mangled_footer = 1;
+
+ switch(rc){
+ case -1 : /* o_e says error! */
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("Error reading folder name"));
+ return(NULL);
+
+ case 0 : /* o_e says normal entry */
+ removing_trailing_white_space(newfolder);
+ removing_leading_white_space(newfolder);
+
+ if(*newfolder){
+ char *name, *fullname = NULL;
+ int exists, breakout = 0;
+
+ save_hist(history, newfolder, 0, tc);
+
+ if(!(name = folder_is_nick(newfolder, FOLDERS(tc),
+ FN_WHOLE_NAME)))
+ name = newfolder;
+
+ if(update_folder_spec(expanded, sizeof(expanded), name)){
+ strncpy(name = newfolder, expanded, sizeof(newfolder));
+ newfolder[sizeof(newfolder)-1] = '\0';
+ }
+
+ exists = folder_name_exists(tc, name, &fullname);
+
+ if(fullname){
+ strncpy(name = newfolder, fullname, sizeof(newfolder));
+ newfolder[sizeof(newfolder)-1] = '\0';
+ fs_give((void **) &fullname);
+ breakout = TRUE;
+ }
+
+ /*
+ * if we know the things a folder, open it.
+ * else if we know its a directory, visit it.
+ * else we're not sure (it either doesn't really
+ * exist or its unLISTable) so try opening it anyway
+ */
+ if(exists & FEX_ISFILE){
+ done++;
+ break;
+ }
+ else if((exists & FEX_ISDIR)){
+ if(breakout){
+ CONTEXT_S *fake_context;
+ char tmp[MAILTMPLEN];
+ size_t l;
+
+ strncpy(tmp, name, sizeof(tmp));
+ tmp[sizeof(tmp)-2-1] = '\0';
+ if(tmp[(l = strlen(tmp)) - 1] != tc->dir->delim){
+ if(l < sizeof(tmp)){
+ tmp[l] = tc->dir->delim;
+ strncpy(&tmp[l+1], "[]", sizeof(tmp)-(l+1));
+ }
+ }
+ else
+ strncat(tmp, "[]", sizeof(tmp)-strlen(tmp)-1);
+
+ tmp[sizeof(tmp)-1] = '\0';
+
+ fake_context = new_context(tmp, 0);
+ newfolder[0] = '\0';
+ done = display_folder_list(&fake_context, newfolder,
+ 1, folders_for_goto);
+ free_context(&fake_context);
+ break;
+ }
+ else if(!(tc->use & CNTXT_INCMNG)){
+ done = display_folder_list(&tc, newfolder,
+ 1, folders_for_goto);
+ break;
+ }
+ }
+ else if((exists & FEX_ERROR)){
+ q_status_message1(SM_ORDER, 0, 3,
+ _("Problem accessing folder \"%s\""),
+ newfolder);
+ return(NULL);
+ }
+ else{
+ done++;
+ break;
+ }
+
+ if(exists == FEX_ERROR)
+ q_status_message1(SM_ORDER, 0, 3,
+ _("Problem accessing folder \"%s\""),
+ newfolder);
+ else if(tc->use & CNTXT_INCMNG)
+ q_status_message1(SM_ORDER, 0, 3,
+ _("Can't find Incoming Folder: %s"),
+ newfolder);
+ else if(context_isambig(newfolder))
+ q_status_message2(SM_ORDER, 0, 3,
+ _("Can't find folder \"%s\" in %s"),
+ newfolder, (void *) tc->nickname);
+ else
+ q_status_message1(SM_ORDER, 0, 3,
+ _("Can't find folder \"%s\""),
+ newfolder);
+
+ return(NULL);
+ }
+ else if(last_folder){
+ if(ps_global->goto_default_rule == GOTO_FIRST_CLCTN_DEF_INBOX
+ && !strucmp(last_folder, ps_global->inbox_name)
+ && tc == ((ps_global->context_list->use & CNTXT_INCMNG)
+ ? ps_global->context_list->next : ps_global->context_list)){
+ if(notrealinbox)
+ (*notrealinbox) = 0;
+
+ tc = ps_global->context_list;
+ }
+
+ strncpy(newfolder, last_folder, sizeof(newfolder));
+ newfolder[sizeof(newfolder)-1] = '\0';
+ save_hist(history, newfolder, 0, tc);
+ done++;
+ break;
+ }
+ /* fall thru like they cancelled */
+
+ case 1 : /* o_e says user cancel */
+ cmd_cancelled("Open folder");
+ return(NULL);
+
+ case 2 : /* o_e says user wants list */
+ r = display_folder_list(&tc, newfolder, 0, folders_for_goto);
+ if(r)
+ done++;
+
+ break;
+
+ case 3 : /* o_e says user wants help */
+ help = help == NO_HELP ? h_oe_broach : NO_HELP;
+ break;
+
+ case 4 : /* redraw */
+ break;
+
+ case 10 : /* Previous collection */
+ tc2 = ps_global->context_list;
+ while(tc2->next && tc2->next != tc)
+ tc2 = tc2->next;
+
+ tc = tc2;
+ break;
+
+ case 11 : /* Next collection */
+ tc = (tc->next) ? tc->next : ps_global->context_list;
+ break;
+
+ case 12 : /* file name completion */
+ if(!folder_complete(tc, newfolder, sizeof(newfolder), &n)){
+ if(n && last_rc == 12 && !(flags & OE_USER_MODIFIED)){
+ r = display_folder_list(&tc, newfolder, 1,folders_for_goto);
+ if(r)
+ done++; /* bingo! */
+ else
+ rc = 0; /* burn last_rc */
+ }
+ else
+ Writechar(BELL, 0);
+ }
+
+ break;
+
+ case 14 : /* file name completion */
+ r = display_folder_list(&tc, newfolder, 2, folders_for_goto);
+ if(r)
+ done++; /* bingo! */
+ else
+ rc = 0; /* burn last_rc */
+
+ break;
+
+ case 17 : /* GoTo INBOX */
+ done++;
+ strncpy(newfolder, ps_global->inbox_name, sizeof(newfolder)-1);
+ newfolder[sizeof(newfolder)-1] = '\0';
+ if(notrealinbox)
+ (*notrealinbox) = 0;
+
+ tc = ps_global->context_list;
+ save_hist(history, newfolder, 0, tc);
+
+ break;
+
+ case 30 :
+ if((p = get_prev_hist(history, newfolder, 0, tc)) != NULL){
+ strncpy(newfolder, p, sizeof(newfolder));
+ newfolder[sizeof(newfolder)-1] = '\0';
+ if(history->hist[history->curindex])
+ tc = history->hist[history->curindex]->cntxt;
+ }
+ else
+ Writechar(BELL, 0);
+
+ break;
+
+ case 31 :
+ if((p = get_next_hist(history, newfolder, 0, tc)) != NULL){
+ strncpy(newfolder, p, sizeof(newfolder));
+ newfolder[sizeof(newfolder)-1] = '\0';
+ if(history->hist[history->curindex])
+ tc = history->hist[history->curindex]->cntxt;
+ }
+ else
+ Writechar(BELL, 0);
+
+ break;
+
+ default :
+ panic("Unhandled case");
+ break;
+ }
+
+ last_rc = rc;
+ }
+
+ dprint((2, "broach folder, name entered \"%s\"\n",
+ newfolder ? newfolder : "?"));
+
+ /*-- Just check that we can expand this. It gets done for real later --*/
+ strncpy(expanded, newfolder, sizeof(expanded));
+ expanded[sizeof(expanded)-1] = '\0';
+
+ if(!expand_foldername(expanded, sizeof(expanded))) {
+ dprint((1, "Error: Failed on expansion of filename %s (do_broach)\n",
+ expanded ? expanded : "?"));
+ return(NULL);
+ }
+
+ *context = tc;
+ return(newfolder);
+}
+
+
+/*----------------------------------------------------------------------
+ Check to see if user wants to reopen dead stream.
+
+ Args: ps --
+ reopenp --
+
+ Result: 1 if the folder was successfully updatedn
+ 0 if not necessary
+
+ ----*/
+int
+ask_mailbox_reopen(struct pine *ps, int *reopenp)
+{
+ if(((ps->mail_stream->dtb
+ && ((ps->mail_stream->dtb->flags & DR_NONEWMAIL)
+ || (ps->mail_stream->rdonly
+ && ps->mail_stream->dtb->flags & DR_NONEWMAILRONLY)))
+ && (ps->reopen_rule == REOPEN_ASK_ASK_Y
+ || ps->reopen_rule == REOPEN_ASK_ASK_N
+ || ps->reopen_rule == REOPEN_ASK_NO_Y
+ || ps->reopen_rule == REOPEN_ASK_NO_N))
+ || ((ps->mail_stream->dtb
+ && ps->mail_stream->rdonly
+ && !(ps->mail_stream->dtb->flags & DR_LOCAL))
+ && (ps->reopen_rule == REOPEN_YES_ASK_Y
+ || ps->reopen_rule == REOPEN_YES_ASK_N
+ || ps->reopen_rule == REOPEN_ASK_ASK_Y
+ || ps->reopen_rule == REOPEN_ASK_ASK_N))){
+ int deefault;
+
+ switch(ps->reopen_rule){
+ case REOPEN_YES_ASK_Y:
+ case REOPEN_ASK_ASK_Y:
+ case REOPEN_ASK_NO_Y:
+ deefault = 'y';
+ break;
+
+ default:
+ deefault = 'n';
+ break;
+ }
+
+ switch(want_to("Re-open folder to check for new messages", deefault,
+ 'x', h_reopen_folder, WT_NORM)){
+ case 'y':
+ (*reopenp)++;
+ break;
+
+ case 'x':
+ return(-1);
+ }
+ }
+
+ return(0);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Check to see if user input is in form of old c-client mailbox speck
+
+ Args: old --
+ new --
+
+ Result: 1 if the folder was successfully updatedn
+ 0 if not necessary
+
+ ----*/
+int
+update_folder_spec(char *new, size_t newlen, char *old)
+{
+ char *p, *orignew;
+ int nntp = 0;
+
+ orignew = new;
+ if(*(p = old) == '*') /* old form? */
+ old++;
+
+ if(*old == '{') /* copy host spec */
+ do
+ switch(*new = *old++){
+ case '\0' :
+ return(FALSE);
+
+ case '/' :
+ if(!struncmp(old, "nntp", 4))
+ nntp++;
+
+ break;
+
+ default :
+ break;
+ }
+ while(*new++ != '}' && (new-orignew) < newlen-1);
+
+ if((*p == '*' && *old) || ((*old == '*') ? *++old : 0)){
+ /*
+ * OK, some heuristics here. If it looks like a newsgroup
+ * then we plunk it into the #news namespace else we
+ * assume that they're trying to get at a #public folder...
+ */
+ for(p = old;
+ *p && (isalnum((unsigned char) *p) || strindex(".-", *p));
+ p++)
+ ;
+
+ sstrncpy(&new, (*p && !nntp) ? "#public/" : "#news.", newlen-(new-orignew));
+ strncpy(new, old, newlen-(new-orignew));
+ return(TRUE);
+ }
+
+ orignew[newlen-1] = '\0';
+
+ return(FALSE);
+}
+
+
+/*----------------------------------------------------------------------
+ Open the requested folder in the requested context
+
+ Args: state -- usual pine state struct
+ newfolder -- folder to open
+ new_context -- folder context might live in
+ stream -- candidate for recycling
+
+ Result: New folder open or not (if error), and we're set to
+ enter the index screen.
+ ----*/
+void
+visit_folder(struct pine *state, char *newfolder, CONTEXT_S *new_context,
+ MAILSTREAM *stream, long unsigned int flags)
+{
+ dprint((9, "visit_folder(%s, %s)\n",
+ newfolder ? newfolder : "?",
+ (new_context && new_context->context)
+ ? new_context->context : "(NULL)"));
+
+ if(ps_global && ps_global->ttyo){
+ blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
+ ps_global->mangled_footer = 1;
+ }
+
+ if(do_broach_folder(newfolder, new_context, stream ? &stream : NULL,
+ flags) >= 0
+ || !sp_flagged(state->mail_stream, SP_LOCKED))
+ state->next_screen = mail_index_screen;
+ else
+ state->next_screen = folder_screen;
+}
+
+
+/*----------------------------------------------------------------------
+ Move read messages from folder if listed in archive
+
+ Args:
+
+ ----*/
+int
+read_msg_prompt(long int n, char *f)
+{
+ char buf[MAX_SCREEN_COLS+1];
+
+ snprintf(buf, sizeof(buf), "Save the %ld read message%s in \"%s\"", n, plural(n), f);
+ buf[sizeof(buf)-1] = '\0';
+ return(want_to(buf, 'y', 0, NO_HELP, WT_NORM) == 'y');
+}
+
+
+/*----------------------------------------------------------------------
+ Print current message[s] or folder index
+
+ Args: state -- pointer to struct holding a bunch of pine state
+ msgmap -- table mapping msg nums to c-client sequence nums
+ aopt -- aggregate options
+ in_index -- boolean indicating we're called from Index Screen
+
+ Filters the original header and sends stuff to printer
+ ---*/
+int
+cmd_print(struct pine *state, MSGNO_S *msgmap, int aopt, CmdWhere in_index)
+{
+ char prompt[250];
+ long i, msgs, rawno;
+ int next = 0, do_index = 0, rv = 0;
+ ENVELOPE *e;
+ BODY *b;
+ MESSAGECACHE *mc;
+
+ if(MCMD_ISAGG(aopt) && !pseudo_selected(state->mail_stream, msgmap))
+ return rv;
+
+ msgs = mn_total_cur(msgmap);
+
+ if((in_index != View) && F_ON(F_PRINT_INDEX, state)){
+ char m[10];
+ int ans;
+ static ESCKEY_S prt_opts[] = {
+ {'i', 'i', "I", N_("Index")},
+ {'m', 'm', "M", NULL},
+ {-1, 0, NULL, NULL}};
+
+ if(in_index == ThrdIndx){
+ /* TRANSLATORS: This is a question, Print Index ? */
+ if(want_to(_("Print Index"), 'y', 'x', NO_HELP, WT_NORM) == 'y')
+ ans = 'i';
+ else
+ ans = 'x';
+ }
+ else{
+ snprintf(m, sizeof(m), "Message%s", (msgs>1L) ? "s" : "");
+ m[sizeof(m)-1] = '\0';
+ prt_opts[1].label = m;
+ snprintf(prompt, sizeof(prompt), "Print %sFolder Index or %s %s? ",
+ (aopt & MCMD_AGG_2) ? "thread " : MCMD_ISAGG(aopt) ? "selected " : "",
+ (aopt & MCMD_AGG_2) ? "thread" : MCMD_ISAGG(aopt) ? "selected" : "current", m);
+ prompt[sizeof(prompt)-1] = '\0';
+
+ ans = radio_buttons(prompt, -FOOTER_ROWS(state), prt_opts, 'm', 'x',
+ NO_HELP, RB_NORM|RB_SEQ_SENSITIVE);
+ }
+
+ switch(ans){
+ case 'x' :
+ cmd_cancelled("Print");
+ if(MCMD_ISAGG(aopt))
+ restore_selected(msgmap);
+
+ return rv;
+
+ case 'i':
+ do_index = 1;
+ break;
+
+ default :
+ case 'm':
+ break;
+ }
+ }
+
+ if(do_index)
+ snprintf(prompt, sizeof(prompt), "%sFolder Index",
+ (aopt & MCMD_AGG_2) ? "Thread " : MCMD_ISAGG(aopt) ? "Selected " : "");
+ else if(msgs > 1L)
+ snprintf(prompt, sizeof(prompt), "%s messages", long2string(msgs));
+ else
+ snprintf(prompt, sizeof(prompt), "Message %s", long2string(mn_get_cur(msgmap)));
+
+ prompt[sizeof(prompt)-1] = '\0';
+
+ if(open_printer(prompt) < 0){
+ if(MCMD_ISAGG(aopt))
+ restore_selected(msgmap);
+
+ return rv;
+ }
+
+ if(do_index){
+ TITLE_S *tc;
+
+ tc = format_titlebar();
+
+ /* Print titlebar... */
+ print_text1("%s\n\n", tc ? tc->titlebar_line : "");
+ /* then all the index members... */
+ if(!print_index(state, msgmap, MCMD_ISAGG(aopt)))
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("Error printing folder index"));
+ else
+ rv++;
+ }
+ else{
+ rv++;
+ for(i = mn_first_cur(msgmap); i > 0L; i = mn_next_cur(msgmap), next++){
+ if(next && F_ON(F_AGG_PRINT_FF, state))
+ if(!print_char(FORMFEED)){
+ rv = 0;
+ break;
+ }
+
+ if(!(state->mail_stream
+ && (rawno = mn_m2raw(msgmap, i)) > 0L
+ && rawno <= state->mail_stream->nmsgs
+ && (mc = mail_elt(state->mail_stream, rawno))
+ && mc->valid))
+ mc = NULL;
+
+ if(!(e=pine_mail_fetchstructure(state->mail_stream,
+ mn_m2raw(msgmap,i),
+ &b))
+ || (F_ON(F_FROM_DELIM_IN_PRINT, ps_global)
+ && !bezerk_delimiter(e, mc, print_char, next))
+ || !format_message(mn_m2raw(msgmap, mn_get_cur(msgmap)),
+ e, b, NULL, FM_NEW_MESS | FM_NOINDENT,
+ print_char)){
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("Error printing message"));
+ rv = 0;
+ break;
+ }
+ }
+ }
+
+ close_printer();
+
+ if(MCMD_ISAGG(aopt))
+ restore_selected(msgmap);
+
+ return rv;
+}
+
+
+/*----------------------------------------------------------------------
+ Pipe message text
+
+ Args: state -- various pine state bits
+ msgmap -- Message number mapping table
+ aopt -- option flags
+
+ Filters the original header and sends stuff to specified command
+ ---*/
+int
+cmd_pipe(struct pine *state, MSGNO_S *msgmap, int aopt)
+{
+ ENVELOPE *e;
+ MESSAGECACHE *mc;
+ BODY *b;
+ PIPE_S *syspipe;
+ char *resultfilename = NULL, prompt[80], *p;
+ int done = 0, rv = 0;
+ gf_io_t pc;
+ int fourlabel = -1, j = 0, next = 0, ku;
+ int pipe_rv; /* rv of proc to separate from close_system_pipe rv */
+ long i, rawno;
+ unsigned flagsforhist = 1; /* raw=8/delimit=4/newpipe=2/capture=1 */
+ static HISTORY_S *history = NULL;
+ int capture = 1, raw = 0, delimit = 0, newpipe = 0;
+ char pipe_command[MAXPATH];
+ ESCKEY_S pipe_opt[8];
+
+ if(ps_global->restricted){
+ q_status_message(SM_ORDER | SM_DING, 0, 4,
+ "Alpine demo can't pipe messages");
+ return rv;
+ }
+ else if(!any_messages(msgmap, NULL, "to Pipe"))
+ return rv;
+
+ pipe_command[0] = '\0';
+ init_hist(&history, HISTSIZE);
+ flagsforhist = (raw ? 0x8 : 0) +
+ (delimit ? 0x4 : 0) +
+ (newpipe ? 0x2 : 0) +
+ (capture ? 0x1 : 0);
+ if((p = get_prev_hist(history, "", flagsforhist, NULL)) != NULL){
+ strncpy(pipe_command, p, sizeof(pipe_command));
+ pipe_command[sizeof(pipe_command)-1] = '\0';
+ if(history->hist[history->curindex]){
+ flagsforhist = history->hist[history->curindex]->flags;
+ raw = (flagsforhist & 0x8) ? 1 : 0;
+ delimit = (flagsforhist & 0x4) ? 1 : 0;
+ newpipe = (flagsforhist & 0x2) ? 1 : 0;
+ capture = (flagsforhist & 0x1) ? 1 : 0;
+ }
+ }
+
+ pipe_opt[j].ch = 0;
+ pipe_opt[j].rval = 0;
+ pipe_opt[j].name = "";
+ pipe_opt[j++].label = "";
+
+ pipe_opt[j].ch = ctrl('W');
+ pipe_opt[j].rval = 10;
+ pipe_opt[j].name = "^W";
+ pipe_opt[j++].label = NULL;
+
+ pipe_opt[j].ch = ctrl('Y');
+ pipe_opt[j].rval = 11;
+ pipe_opt[j].name = "^Y";
+ pipe_opt[j++].label = NULL;
+
+ pipe_opt[j].ch = ctrl('R');
+ pipe_opt[j].rval = 12;
+ pipe_opt[j].name = "^R";
+ pipe_opt[j++].label = NULL;
+
+ if(MCMD_ISAGG(aopt)){
+ if(!pseudo_selected(state->mail_stream, msgmap))
+ return rv;
+ else{
+ fourlabel = j;
+ pipe_opt[j].ch = ctrl('T');
+ pipe_opt[j].rval = 13;
+ pipe_opt[j].name = "^T";
+ pipe_opt[j++].label = NULL;
+ }
+ }
+
+ pipe_opt[j].ch = KEY_UP;
+ pipe_opt[j].rval = 30;
+ pipe_opt[j].name = "";
+ ku = j;
+ pipe_opt[j++].label = "";
+
+ pipe_opt[j].ch = KEY_DOWN;
+ pipe_opt[j].rval = 31;
+ pipe_opt[j].name = "";
+ pipe_opt[j++].label = "";
+
+ pipe_opt[j].ch = -1;
+
+ while (!done) {
+ int flags;
+
+ snprintf(prompt, sizeof(prompt), "Pipe %smessage%s%s to %s%s%s%s%s%s%s: ",
+ raw ? "RAW " : "",
+ MCMD_ISAGG(aopt) ? "s" : " ",
+ MCMD_ISAGG(aopt) ? "" : comatose(mn_get_cur(msgmap)),
+ (!capture || delimit || (newpipe && MCMD_ISAGG(aopt))) ? "(" : "",
+ capture ? "" : "uncaptured",
+ (!capture && delimit) ? "," : "",
+ delimit ? "delimited" : "",
+ ((!capture || delimit) && newpipe && MCMD_ISAGG(aopt)) ? "," : "",
+ (newpipe && MCMD_ISAGG(aopt)) ? "new pipe" : "",
+ (!capture || delimit || (newpipe && MCMD_ISAGG(aopt))) ? ") " : "");
+ prompt[sizeof(prompt)-1] = '\0';
+ pipe_opt[1].label = raw ? N_("Shown Text") : N_("Raw Text");
+ pipe_opt[2].label = capture ? N_("Free Output") : N_("Capture Output");
+ pipe_opt[3].label = delimit ? N_("No Delimiter") : N_("With Delimiter");
+ if(fourlabel > 0)
+ pipe_opt[fourlabel].label = newpipe ? N_("To Same Pipe") : N_("To Individual Pipes");
+
+
+ /*
+ * 2 is really 1 because there will be one real entry and
+ * one entry of "" because of the get_prev_hist above.
+ */
+ if(items_in_hist(history) > 2){
+ pipe_opt[ku].name = HISTORY_UP_KEYNAME;
+ pipe_opt[ku].label = HISTORY_KEYLABEL;
+ pipe_opt[ku+1].name = HISTORY_DOWN_KEYNAME;
+ pipe_opt[ku+1].label = HISTORY_KEYLABEL;
+ }
+ else{
+ pipe_opt[ku].name = "";
+ pipe_opt[ku].label = "";
+ pipe_opt[ku+1].name = "";
+ pipe_opt[ku+1].label = "";
+ }
+
+ flags = OE_APPEND_CURRENT | OE_SEQ_SENSITIVE;
+ switch(optionally_enter(pipe_command, -FOOTER_ROWS(state), 0,
+ sizeof(pipe_command), prompt,
+ pipe_opt, NO_HELP, &flags)){
+ case -1 :
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Internal problem encountered"));
+ done++;
+ break;
+
+ case 10 : /* flip raw bit */
+ raw = !raw;
+ break;
+
+ case 11 : /* flip capture bit */
+ capture = !capture;
+ break;
+
+ case 12 : /* flip delimit bit */
+ delimit = !delimit;
+ break;
+
+ case 13 : /* flip newpipe bit */
+ newpipe = !newpipe;
+ break;
+
+ case 30 :
+ flagsforhist = (raw ? 0x8 : 0) +
+ (delimit ? 0x4 : 0) +
+ (newpipe ? 0x2 : 0) +
+ (capture ? 0x1 : 0);
+ if((p = get_prev_hist(history, pipe_command, flagsforhist, NULL)) != NULL){
+ strncpy(pipe_command, p, sizeof(pipe_command));
+ pipe_command[sizeof(pipe_command)-1] = '\0';
+ if(history->hist[history->curindex]){
+ flagsforhist = history->hist[history->curindex]->flags;
+ raw = (flagsforhist & 0x8) ? 1 : 0;
+ delimit = (flagsforhist & 0x4) ? 1 : 0;
+ newpipe = (flagsforhist & 0x2) ? 1 : 0;
+ capture = (flagsforhist & 0x1) ? 1 : 0;
+ }
+ }
+ else
+ Writechar(BELL, 0);
+
+ break;
+
+ case 31 :
+ flagsforhist = (raw ? 0x8 : 0) +
+ (delimit ? 0x4 : 0) +
+ (newpipe ? 0x2 : 0) +
+ (capture ? 0x1 : 0);
+ if((p = get_next_hist(history, pipe_command, flagsforhist, NULL)) != NULL){
+ strncpy(pipe_command, p, sizeof(pipe_command));
+ pipe_command[sizeof(pipe_command)-1] = '\0';
+ if(history->hist[history->curindex]){
+ flagsforhist = history->hist[history->curindex]->flags;
+ raw = (flagsforhist & 0x8) ? 1 : 0;
+ delimit = (flagsforhist & 0x4) ? 1 : 0;
+ newpipe = (flagsforhist & 0x2) ? 1 : 0;
+ capture = (flagsforhist & 0x1) ? 1 : 0;
+ }
+ }
+ else
+ Writechar(BELL, 0);
+
+ break;
+
+ case 0 :
+ if(pipe_command[0]){
+
+ flagsforhist = (raw ? 0x8 : 0) +
+ (delimit ? 0x4 : 0) +
+ (newpipe ? 0x2 : 0) +
+ (capture ? 0x1 : 0);
+ save_hist(history, pipe_command, flagsforhist, NULL);
+
+ flags = PIPE_USER | PIPE_WRITE | PIPE_STDERR;
+ flags |= (raw ? PIPE_RAW : 0);
+ if(!capture){
+#ifndef _WINDOWS
+ ClearScreen();
+ fflush(stdout);
+ clear_cursor_pos();
+ ps_global->mangled_screen = 1;
+ ps_global->in_init_seq = 1;
+#endif
+ flags |= PIPE_RESET;
+ }
+
+ if(!newpipe && !(syspipe = cmd_pipe_open(pipe_command,
+ (flags & PIPE_RESET)
+ ? NULL
+ : &resultfilename,
+ flags, &pc)))
+ done++;
+
+ for(i = mn_first_cur(msgmap);
+ i > 0L && !done;
+ i = mn_next_cur(msgmap)){
+ e = pine_mail_fetchstructure(ps_global->mail_stream,
+ mn_m2raw(msgmap, i), &b);
+ if(!(state->mail_stream
+ && (rawno = mn_m2raw(msgmap, i)) > 0L
+ && rawno <= state->mail_stream->nmsgs
+ && (mc = mail_elt(state->mail_stream, rawno))
+ && mc->valid))
+ mc = NULL;
+
+ if((newpipe
+ && !(syspipe = cmd_pipe_open(pipe_command,
+ (flags & PIPE_RESET)
+ ? NULL
+ : &resultfilename,
+ flags, &pc)))
+ || (delimit && !bezerk_delimiter(e, mc, pc, next++)))
+ done++;
+
+ if(!done){
+ if(raw){
+ char *pipe_err;
+
+ prime_raw_pipe_getc(ps_global->mail_stream,
+ mn_m2raw(msgmap, i), -1L, 0L);
+ gf_filter_init();
+ gf_link_filter(gf_nvtnl_local, NULL);
+ if((pipe_err = gf_pipe(raw_pipe_getc, pc)) != NULL){
+ q_status_message1(SM_ORDER|SM_DING,
+ 3, 3,
+ _("Internal Error: %s"),
+ pipe_err);
+ done++;
+ }
+ }
+ else if(!format_message(mn_m2raw(msgmap, i), e, b,
+ NULL, FM_NEW_MESS | FM_NOWRAP, pc))
+ done++;
+ }
+
+ if(newpipe)
+ if(close_system_pipe(&syspipe, &pipe_rv, pipe_callback) == -1)
+ done++;
+ }
+
+ if(!capture)
+ ps_global->in_init_seq = 0;
+
+ if(!newpipe)
+ if(close_system_pipe(&syspipe, &pipe_rv, pipe_callback) == -1)
+ done++;
+ if(done) /* say we had a problem */
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("Error piping message"));
+ else if(resultfilename){
+ rv++;
+ /* only display if no error */
+ display_output_file(resultfilename, "PIPE MESSAGE",
+ NULL, DOF_EMPTY);
+ fs_give((void **)&resultfilename);
+ }
+ else{
+ rv++;
+ q_status_message(SM_ORDER, 0, 2, _("Pipe command completed"));
+ }
+
+ done++;
+ break;
+ }
+ /* else fall thru as if cancelled */
+
+ case 1 :
+ cmd_cancelled("Pipe command");
+ done++;
+ break;
+
+ case 3 :
+ helper(h_common_pipe, _("HELP FOR PIPE COMMAND"), HLPD_SIMPLE);
+ ps_global->mangled_screen = 1;
+ break;
+
+ case 2 : /* no place to escape to */
+ case 4 : /* can't suspend */
+ default :
+ break;
+ }
+ }
+
+ ps_global->mangled_footer = 1;
+ if(MCMD_ISAGG(aopt))
+ restore_selected(msgmap);
+
+ return rv;
+}
+
+
+/*----------------------------------------------------------------------
+ Screen to offer list management commands contained in message
+
+ Args: state -- pointer to struct holding a bunch of pine state
+ msgmap -- table mapping msg nums to c-client sequence nums
+ aopt -- aggregate options
+
+ Result:
+
+ NOTE: Inspired by contrib from Jeremy Blackman <loki@maison-otaku.net>
+ ----*/
+void
+rfc2369_display(MAILSTREAM *stream, MSGNO_S *msgmap, long int msgno)
+{
+ int winner = 0;
+ char *h, *hdrs[MLCMD_COUNT + 1];
+ long index_no = mn_raw2m(msgmap, msgno);
+ RFC2369_S data[MLCMD_COUNT];
+
+ /* for each header field */
+ if((h = pine_fetchheader_lines(stream, msgno, NULL, rfc2369_hdrs(hdrs))) != NULL){
+ memset(&data[0], 0, sizeof(RFC2369_S) * MLCMD_COUNT);
+ if(rfc2369_parse_fields(h, &data[0])){
+ STORE_S *explain;
+
+ if((explain = list_mgmt_text(data, index_no)) != NULL){
+ list_mgmt_screen(explain);
+ ps_global->mangled_screen = 1;
+ so_give(&explain);
+ winner++;
+ }
+ }
+
+ fs_give((void **) &h);
+ }
+
+ if(!winner)
+ q_status_message1(SM_ORDER, 0, 3,
+ "Message %s contains no list management information",
+ comatose(index_no));
+}
+
+
+STORE_S *
+list_mgmt_text(RFC2369_S *data, long int msgno)
+{
+ STORE_S *store;
+ int i, j, n, fields = 0;
+ static char *rfc2369_intro1 =
+ "<HTML><HEAD></HEAD><BODY><H1>Mail List Commands</H1>Message ";
+ static char *rfc2369_intro2[] = {
+ N_(" has information associated with it "),
+ N_("that explains how to participate in an email list. An "),
+ N_("email list is represented by a single email address that "),
+ N_("users sharing a common interest can send messages to (known "),
+ N_("as posting) which are then redistributed to all members "),
+ N_("of the list (sometimes after review by a moderator)."),
+ N_("<P>List participation commands in this message include:"),
+ NULL
+ };
+
+ if((store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
+
+ /* Insert introductory text */
+ so_puts(store, rfc2369_intro1);
+
+ so_puts(store, comatose(msgno));
+
+ for(i = 0; rfc2369_intro2[i]; i++)
+ so_puts(store, _(rfc2369_intro2[i]));
+
+ so_puts(store, "<P>");
+ for(i = 0; i < MLCMD_COUNT; i++)
+ if(data[i].data[0].value
+ || data[i].data[0].comment
+ || data[i].data[0].error){
+ if(!fields++)
+ so_puts(store, "<UL>");
+
+ so_puts(store, "<LI>");
+ so_puts(store,
+ (n = (data[i].data[1].value || data[i].data[1].comment))
+ ? "Methods to "
+ : "A method to ");
+
+ so_puts(store, data[i].field.description);
+ so_puts(store, ". ");
+
+ if(n)
+ so_puts(store, "<OL>");
+
+ for(j = 0;
+ j < MLCMD_MAXDATA
+ && (data[i].data[j].comment
+ || data[i].data[j].value
+ || data[i].data[j].error);
+ j++){
+
+ so_puts(store, n ? "<P><LI>" : "<P>");
+
+ if(data[i].data[j].comment){
+ so_puts(store,
+ _("With the provided comment:<P><BLOCKQUOTE>"));
+ so_puts(store, data[i].data[j].comment);
+ so_puts(store, "</BLOCKQUOTE><P>");
+ }
+
+ if(data[i].data[j].value){
+ if(i == MLCMD_POST
+ && !strucmp(data[i].data[j].value, "NO")){
+ so_puts(store,
+ _("Posting is <EM>not</EM> allowed on this list"));
+ }
+ else{
+ so_puts(store, "Select <A HREF=\"");
+ so_puts(store, data[i].data[j].value);
+ so_puts(store, "\">HERE</A> to ");
+ so_puts(store, (data[i].field.action)
+ ? data[i].field.action
+ : "try it");
+ }
+
+ so_puts(store, ".");
+ }
+
+ if(data[i].data[j].error){
+ so_puts(store, "<P>Unfortunately, Alpine can not offer");
+ so_puts(store, " to take direct action based upon it");
+ so_puts(store, " because it was improperly formatted.");
+ so_puts(store, " The unrecognized data associated with");
+ so_puts(store, " the \"");
+ so_puts(store, data[i].field.name);
+ so_puts(store, "\" header field was:<P><BLOCKQUOTE>");
+ so_puts(store, data[i].data[j].error);
+ so_puts(store, "</BLOCKQUOTE>");
+ }
+
+ so_puts(store, "<P>");
+ }
+
+ if(n)
+ so_puts(store, "</OL>");
+ }
+
+ if(fields)
+ so_puts(store, "</UL>");
+
+ so_puts(store, "</BODY></HTML>");
+ }
+
+ return(store);
+}
+
+
+void
+list_mgmt_screen(STORE_S *html)
+{
+ int cmd = MC_NONE;
+ long offset = 0L;
+ char *error = NULL;
+ STORE_S *store;
+ HANDLE_S *handles = NULL;
+ gf_io_t gc, pc;
+
+ do{
+ so_seek(html, 0L, 0);
+ gf_set_so_readc(&gc, html);
+
+ init_handles(&handles);
+
+ if((store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
+ gf_set_so_writec(&pc, store);
+ gf_filter_init();
+
+ gf_link_filter(gf_html2plain,
+ gf_html2plain_opt(NULL, ps_global->ttyo->screen_cols,
+ non_messageview_margin(), &handles, NULL, 0));
+
+ error = gf_pipe(gc, pc);
+
+ gf_clear_so_writec(store);
+
+ if(!error){
+ SCROLL_S sargs;
+
+ memset(&sargs, 0, sizeof(SCROLL_S));
+ sargs.text.text = so_text(store);
+ sargs.text.src = CharStar;
+ sargs.text.desc = "list commands";
+ sargs.text.handles = handles;
+ if(offset){
+ sargs.start.on = Offset;
+ sargs.start.loc.offset = offset;
+ }
+
+ sargs.bar.title = _("MAIL LIST COMMANDS");
+ sargs.bar.style = MessageNumber;
+ sargs.resize_exit = 1;
+ sargs.help.text = h_special_list_commands;
+ sargs.help.title = _("HELP FOR LIST COMMANDS");
+ sargs.keys.menu = &listmgr_keymenu;
+ setbitmap(sargs.keys.bitmap);
+ if(!handles){
+ clrbitn(LM_TRY_KEY, sargs.keys.bitmap);
+ clrbitn(LM_PREV_KEY, sargs.keys.bitmap);
+ clrbitn(LM_NEXT_KEY, sargs.keys.bitmap);
+ }
+
+ cmd = scrolltool(&sargs);
+ offset = sargs.start.loc.offset;
+ }
+
+ so_give(&store);
+ }
+
+ free_handles(&handles);
+ gf_clear_so_readc(html);
+ }
+ while(cmd == MC_RESIZE);
+}
+
+
+/*----------------------------------------------------------------------
+ Prompt the user for the type of select desired
+
+ NOTE: any and all functions that successfully exit the second
+ switch() statement below (currently "select_*() functions"),
+ *MUST* update the folder's MESSAGECACHE element's "searched"
+ bits to reflect the search result. Functions using
+ mail_search() get this for free, the others must update 'em
+ by hand.
+
+ Returns -1 if canceled without changing selection
+ 0 if selection may have changed
+ ----*/
+int
+aggregate_select(struct pine *state, MSGNO_S *msgmap, int q_line, CmdWhere in_index)
+{
+ long i, diff, old_tot, msgno, raw;
+ int q = 0, rv = 0, narrow = 0, hidden, ret = -1;
+ ESCKEY_S *sel_opts;
+ MESSAGECACHE *mc;
+ SEARCHSET *limitsrch = NULL;
+ PINETHRD_S *thrd;
+ extern MAILSTREAM *mm_search_stream;
+ extern long mm_search_count;
+
+ hidden = any_lflagged(msgmap, MN_HIDE) > 0L;
+ mm_search_stream = state->mail_stream;
+ mm_search_count = 0L;
+
+ sel_opts = THRD_INDX() ? sel_opts4 : sel_opts2;
+ if(THREADING()){
+ sel_opts[SEL_OPTS_THREAD].ch = SEL_OPTS_THREAD_CH;
+ }
+ else{
+ sel_opts[SEL_OPTS_THREAD].ch = -1;
+ }
+
+ if((old_tot = any_lflagged(msgmap, MN_SLCT)) != 0){
+ if(THRD_INDX()){
+ i = 0;
+ thrd = fetch_thread(state->mail_stream,
+ mn_m2raw(msgmap, mn_get_cur(msgmap)));
+ /* check if whole thread is selected or not */
+ if(thrd &&
+ count_lflags_in_thread(state->mail_stream,thrd,msgmap,MN_SLCT)
+ ==
+ count_lflags_in_thread(state->mail_stream,thrd,msgmap,MN_NONE))
+ i = 1;
+
+ sel_opts1[1].label = i ? N_("unselect Curthrd") : N_("select Curthrd");
+ }
+ else{
+ i = get_lflag(state->mail_stream, msgmap, mn_get_cur(msgmap),
+ MN_SLCT);
+ sel_opts1[1].label = i ? N_("unselect Cur") : N_("select Cur");
+ }
+
+ sel_opts += 2; /* disable extra options */
+ switch(q = radio_buttons(_(sel_pmt1), q_line, sel_opts1, 'c', 'x', NO_HELP,
+ RB_NORM)){
+ case 'f' : /* flip selection */
+ msgno = 0L;
+ for(i = 1L; i <= mn_get_total(msgmap); i++){
+ ret = 0;
+ q = !get_lflag(state->mail_stream, msgmap, i, MN_SLCT);
+ set_lflag(state->mail_stream, msgmap, i, MN_SLCT, q);
+ if(hidden){
+ set_lflag(state->mail_stream, msgmap, i, MN_HIDE, !q);
+ if(!msgno && q)
+ mn_reset_cur(msgmap, msgno = i);
+ }
+ }
+
+ return(ret);
+
+ case 'n' : /* narrow selection */
+ narrow++;
+ case 'b' : /* broaden selection */
+ q = 0; /* offer criteria prompt */
+ break;
+
+ case 'c' : /* Un/Select Current */
+ case 'a' : /* Unselect All */
+ case 'x' : /* cancel */
+ break;
+
+ default :
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ "Unsupported Select option");
+ return(ret);
+ }
+ }
+
+ if(!q){
+ while(1){
+ q = radio_buttons(sel_pmt2, q_line, sel_opts, 'c', 'x',
+ NO_HELP, RB_NORM|RB_RET_HELP);
+
+ if(q == 3){
+ helper(h_index_cmd_select, _("HELP FOR SELECT"), HLPD_SIMPLE);
+ ps_global->mangled_screen = 1;
+ }
+ else
+ break;
+ }
+ }
+
+ /*
+ * The purpose of this is to add the appropriate searchset to the
+ * search so that the search can be limited to only looking at what
+ * it needs to look at. That is, if we are narrowing then we only need
+ * to look at messages which are already selected, and if we are
+ * broadening, then we only need to look at messages which are not
+ * yet selected. This routine will work whether or not
+ * limiting_searchset properly limits the search set. In particular,
+ * the searchset returned by limiting_searchset may include messages
+ * which really shouldn't be included. We do that because a too-large
+ * searchset will break some IMAP servers. It is even possible that it
+ * becomes inefficient to send the whole set. If the select function
+ * frees limitsrch, it should be sure to set it to NULL so we won't
+ * try freeing it again here.
+ */
+ limitsrch = limiting_searchset(state->mail_stream, narrow);
+
+ /*
+ * NOTE: See note about MESSAGECACHE "searched" bits above!
+ */
+ switch(q){
+ case 'x': /* cancel */
+ cmd_cancelled("Select command");
+ return(ret);
+
+ case 'c' : /* select/unselect current */
+ (void) select_by_current(state, msgmap, in_index);
+ ret = 0;
+ return(ret);
+
+ case 'a' : /* select/unselect all */
+ msgno = any_lflagged(msgmap, MN_SLCT);
+ diff = (!msgno) ? mn_get_total(msgmap) : 0L;
+ ret = 0;
+ agg_select_all(state->mail_stream, msgmap, &diff,
+ any_lflagged(msgmap, MN_SLCT) <= 0L);
+ q_status_message4(SM_ORDER,0,2,
+ "%s%s message%s %sselected",
+ msgno ? "" : "All ", comatose(diff),
+ plural(diff), msgno ? "UN" : "");
+ return(ret);
+
+ case 'n' : /* Select by Number */
+ ret = 0;
+ if(THRD_INDX())
+ rv = select_by_thrd_number(state->mail_stream, msgmap, &limitsrch);
+ else
+ rv = select_by_number(state->mail_stream, msgmap, &limitsrch);
+
+ break;
+
+ case 'd' : /* Select by Date */
+ ret = 0;
+ rv = select_by_date(state->mail_stream, msgmap, mn_get_cur(msgmap),
+ &limitsrch);
+ break;
+
+ case 't' : /* Text */
+ ret = 0;
+ rv = select_by_text(state->mail_stream, msgmap, mn_get_cur(msgmap),
+ &limitsrch);
+ break;
+
+ case 'z' : /* Size */
+ ret = 0;
+ rv = select_by_size(state->mail_stream, &limitsrch);
+ break;
+
+ case 's' : /* Status */
+ ret = 0;
+ rv = select_by_status(state->mail_stream, &limitsrch);
+ break;
+
+ case 'k' : /* Keyword */
+ ret = 0;
+ rv = select_by_keyword(state->mail_stream, &limitsrch);
+ break;
+
+ case 'r' : /* Rule */
+ ret = 0;
+ rv = select_by_rule(state->mail_stream, &limitsrch);
+ break;
+
+ case 'h' : /* Thread */
+ ret = 0;
+ rv = select_by_thread(state->mail_stream, msgmap, &limitsrch);
+ break;
+
+ default :
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ "Unsupported Select option");
+ return(ret);
+ }
+
+ if(limitsrch)
+ mail_free_searchset(&limitsrch);
+
+ if(rv) /* bad return value.. */
+ return(ret); /* error already displayed */
+
+ if(narrow) /* make sure something was selected */
+ for(i = 1L; i <= mn_get_total(msgmap); i++)
+ if((raw = mn_m2raw(msgmap, i)) > 0L && state->mail_stream
+ && raw <= state->mail_stream->nmsgs
+ && (mc = mail_elt(state->mail_stream, raw)) && mc->searched){
+ if(get_lflag(state->mail_stream, msgmap, i, MN_SLCT))
+ break;
+ else
+ mm_search_count--;
+ }
+
+ diff = 0L;
+ if(mm_search_count){
+ /*
+ * loop thru all the messages, adjusting local flag bits
+ * based on their "searched" bit...
+ */
+ for(i = 1L, msgno = 0L; i <= mn_get_total(msgmap); i++)
+ if(narrow){
+ /* turning OFF selectedness if the "searched" bit isn't lit. */
+ if(get_lflag(state->mail_stream, msgmap, i, MN_SLCT)){
+ if((raw = mn_m2raw(msgmap, i)) > 0L && state->mail_stream
+ && raw <= state->mail_stream->nmsgs
+ && (mc = mail_elt(state->mail_stream, raw))
+ && !mc->searched){
+ diff--;
+ set_lflag(state->mail_stream, msgmap, i, MN_SLCT, 0);
+ if(hidden)
+ set_lflag(state->mail_stream, msgmap, i, MN_HIDE, 1);
+ }
+ /* adjust current message in case we unselect and hide it */
+ else if(msgno < mn_get_cur(msgmap)
+ && (!THRD_INDX()
+ || !get_lflag(state->mail_stream, msgmap,
+ i, MN_CHID)))
+ msgno = i;
+ }
+ }
+ else if((raw = mn_m2raw(msgmap, i)) > 0L && state->mail_stream
+ && raw <= state->mail_stream->nmsgs
+ && (mc = mail_elt(state->mail_stream, raw)) && mc->searched){
+ /* turn ON selectedness if "searched" bit is lit. */
+ if(!get_lflag(state->mail_stream, msgmap, i, MN_SLCT)){
+ diff++;
+ set_lflag(state->mail_stream, msgmap, i, MN_SLCT, 1);
+ if(hidden)
+ set_lflag(state->mail_stream, msgmap, i, MN_HIDE, 0);
+ }
+ }
+
+ /* if we're zoomed and the current message was unselected */
+ if(narrow && msgno
+ && get_lflag(state->mail_stream,msgmap,mn_get_cur(msgmap),MN_HIDE))
+ mn_reset_cur(msgmap, msgno);
+ }
+
+ if(!diff){
+ if(narrow)
+ q_status_message4(SM_ORDER, 3, 3,
+ "%s. %s message%s remain%s selected.",
+ mm_search_count
+ ? "No change resulted"
+ : "No messages in intersection",
+ comatose(old_tot), plural(old_tot),
+ (old_tot == 1L) ? "s" : "");
+ else if(old_tot)
+ q_status_message(SM_ORDER, 3, 3,
+ _("No change resulted. Matching messages already selected."));
+ else
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("Select failed. No %smessages selected."),
+ old_tot ? _("additional ") : "");
+ }
+ else if(old_tot){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,
+ "Select matched %ld message%s. %s %smessage%s %sselected.",
+ (diff > 0) ? diff : old_tot + diff,
+ plural((diff > 0) ? diff : old_tot + diff),
+ comatose((diff > 0) ? any_lflagged(msgmap, MN_SLCT) : -diff),
+ (diff > 0) ? "total " : "",
+ plural((diff > 0) ? any_lflagged(msgmap, MN_SLCT) : -diff),
+ (diff > 0) ? "" : "UN");
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ q_status_message(SM_ORDER, 3, 3, tmp_20k_buf);
+ }
+ else
+ q_status_message2(SM_ORDER, 3, 3, _("Select matched %s message%s!"),
+ comatose(diff), plural(diff));
+
+ return(ret);
+}
+
+
+/*----------------------------------------------------------------------
+ Toggle the state of the current message
+
+ Args: state -- pointer pine's state variables
+ msgmap -- message collection to operate on
+ in_index -- in the message index view
+ Returns: TRUE if current marked selected, FALSE otw
+ ----*/
+int
+select_by_current(struct pine *state, MSGNO_S *msgmap, CmdWhere in_index)
+{
+ long cur;
+ int all_selected = 0;
+ unsigned long was, tot, rawno;
+ PINETHRD_S *thrd;
+
+ cur = mn_get_cur(msgmap);
+
+ if(THRD_INDX()){
+ thrd = fetch_thread(state->mail_stream, mn_m2raw(msgmap, cur));
+ if(!thrd)
+ return 0;
+
+ was = count_lflags_in_thread(state->mail_stream, thrd, msgmap, MN_SLCT);
+ tot = count_lflags_in_thread(state->mail_stream, thrd, msgmap, MN_NONE);
+ if(was == tot)
+ all_selected++;
+
+ if(all_selected){
+ set_thread_lflags(state->mail_stream, thrd, msgmap, MN_SLCT, 0);
+ if(any_lflagged(msgmap, MN_HIDE) > 0L){
+ set_thread_lflags(state->mail_stream, thrd, msgmap, MN_HIDE, 1);
+ /*
+ * See if there's anything left to zoom on. If so,
+ * pick an adjacent one for highlighting, else make
+ * sure nothing is left hidden...
+ */
+ if(any_lflagged(msgmap, MN_SLCT)){
+ mn_inc_cur(state->mail_stream, msgmap,
+ (in_index == View && THREADING()
+ && sp_viewing_a_thread(state->mail_stream))
+ ? MH_THISTHD
+ : (in_index == View)
+ ? MH_ANYTHD : MH_NONE);
+ if(mn_get_cur(msgmap) == cur)
+ mn_dec_cur(state->mail_stream, msgmap,
+ (in_index == View && THREADING()
+ && sp_viewing_a_thread(state->mail_stream))
+ ? MH_THISTHD
+ : (in_index == View)
+ ? MH_ANYTHD : MH_NONE);
+ }
+ else /* clear all hidden flags */
+ (void) unzoom_index(state, state->mail_stream, msgmap);
+ }
+ }
+ else
+ set_thread_lflags(state->mail_stream, thrd, msgmap, MN_SLCT, 1);
+
+ q_status_message3(SM_ORDER, 0, 2, "%s message%s %sselected",
+ comatose(all_selected ? was : tot-was),
+ plural(all_selected ? was : tot-was),
+ all_selected ? "UN" : "");
+ }
+ /* collapsed thread */
+ else if(THREADING()
+ && ((rawno = mn_m2raw(msgmap, cur)) != 0L)
+ && ((thrd = fetch_thread(state->mail_stream, rawno)) != NULL)
+ && (thrd && thrd->next && get_lflag(state->mail_stream, NULL, rawno, MN_COLL))){
+ /*
+ * This doesn't work quite the same as the colon command works, but
+ * it is arguably doing the correct thing. The difference is
+ * that aggregate_select will zoom after selecting back where it
+ * was called from, but selecting a thread with colon won't zoom.
+ * Maybe it makes sense to zoom after a select but not after a colon
+ * command even though they are very similar.
+ */
+ thread_command(state, state->mail_stream, msgmap, ':', -FOOTER_ROWS(state));
+ }
+ else{
+ if((all_selected =
+ get_lflag(state->mail_stream, msgmap, cur, MN_SLCT)) != 0){ /* set? */
+ set_lflag(state->mail_stream, msgmap, cur, MN_SLCT, 0);
+ if(any_lflagged(msgmap, MN_HIDE) > 0L){
+ set_lflag(state->mail_stream, msgmap, cur, MN_HIDE, 1);
+ /*
+ * See if there's anything left to zoom on. If so,
+ * pick an adjacent one for highlighting, else make
+ * sure nothing is left hidden...
+ */
+ if(any_lflagged(msgmap, MN_SLCT)){
+ mn_inc_cur(state->mail_stream, msgmap,
+ (in_index == View && THREADING()
+ && sp_viewing_a_thread(state->mail_stream))
+ ? MH_THISTHD
+ : (in_index == View)
+ ? MH_ANYTHD : MH_NONE);
+ if(mn_get_cur(msgmap) == cur)
+ mn_dec_cur(state->mail_stream, msgmap,
+ (in_index == View && THREADING()
+ && sp_viewing_a_thread(state->mail_stream))
+ ? MH_THISTHD
+ : (in_index == View)
+ ? MH_ANYTHD : MH_NONE);
+ }
+ else /* clear all hidden flags */
+ (void) unzoom_index(state, state->mail_stream, msgmap);
+ }
+ }
+ else
+ set_lflag(state->mail_stream, msgmap, cur, MN_SLCT, 1);
+
+ q_status_message2(SM_ORDER, 0, 2, "Message %s %sselected",
+ long2string(cur), all_selected ? "UN" : "");
+ }
+
+
+ return(!all_selected);
+}
+
+
+/*----------------------------------------------------------------------
+ Prompt the user for the command to perform on selected messages
+
+ Args: state -- pointer pine's state variables
+ msgmap -- message collection to operate on
+ q_line -- line on display to write prompts
+ Returns: 1 if the selected messages are suitably commanded,
+ 0 if the choice to pick the command was declined
+
+ ----*/
+int
+apply_command(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap,
+ UCS preloadkeystroke, int flags, int q_line)
+{
+ int i = 8, /* number of static entries in sel_opts3 */
+ rv = 0,
+ cmd,
+ we_cancel = 0,
+ agg = (flags & AC_FROM_THREAD) ? MCMD_AGG_2 : MCMD_AGG;
+ char prompt[80];
+
+ /*
+ * To do this "right", we really ought to have access to the keymenu
+ * here and change the typed command into a real command by running
+ * it through menu_command. Then the switch below would be against
+ * results from menu_command. If we did that we'd also pass the
+ * results of menu_command in as preloadkeystroke instead of passing
+ * the keystroke itself. But we don't have the keymenu handy,
+ * so we have to fake it. The only complication that we run into
+ * is that KEY_DEL is an escape sequence so we change a typed
+ * KEY_DEL esc seq into the letter D.
+ */
+
+ if(!preloadkeystroke){
+ if(F_ON(F_ENABLE_FLAG,state)){ /* flag? */
+ sel_opts3[i].ch = '*';
+ sel_opts3[i].rval = '*';
+ sel_opts3[i].name = "*";
+ sel_opts3[i++].label = N_("Flag");
+ }
+
+ if(F_ON(F_ENABLE_PIPE,state)){ /* pipe? */
+ sel_opts3[i].ch = '|';
+ sel_opts3[i].rval = '|';
+ sel_opts3[i].name = "|";
+ sel_opts3[i++].label = N_("Pipe");
+ }
+
+ if(F_ON(F_ENABLE_BOUNCE,state)){ /* bounce? */
+ sel_opts3[i].ch = 'b';
+ sel_opts3[i].rval = 'b';
+ sel_opts3[i].name = "B";
+ sel_opts3[i++].label = N_("Bounce");
+ }
+
+ if(flags & AC_FROM_THREAD){
+ if(flags & (AC_COLL | AC_EXPN)){
+ sel_opts3[i].ch = '/';
+ sel_opts3[i].rval = '/';
+ sel_opts3[i].name = "/";
+ sel_opts3[i++].label = (flags & AC_COLL) ? N_("Collapse")
+ : N_("Expand");
+ }
+
+ sel_opts3[i].ch = ';';
+ sel_opts3[i].rval = ';';
+ sel_opts3[i].name = ";";
+ if(flags & AC_UNSEL)
+ sel_opts3[i++].label = N_("UnSelect");
+ else
+ sel_opts3[i++].label = N_("Select");
+ }
+
+ if(F_ON(F_ENABLE_PRYNT, state)){ /* this one is invisible */
+ sel_opts3[i].ch = 'y';
+ sel_opts3[i].rval = '%';
+ sel_opts3[i].name = "";
+ sel_opts3[i++].label = "";
+ }
+
+ sel_opts3[i].ch = KEY_DEL; /* also invisible */
+ sel_opts3[i].rval = 'd';
+ sel_opts3[i].name = "";
+ sel_opts3[i++].label = "";
+
+ sel_opts3[i].ch = -1;
+
+ snprintf(prompt, sizeof(prompt), "%s command : ",
+ (flags & AC_FROM_THREAD) ? "THREAD" : "APPLY");
+ prompt[sizeof(prompt)-1] = '\0';
+ cmd = double_radio_buttons(prompt, q_line, sel_opts3, 'z', 'x', NO_HELP,
+ RB_SEQ_SENSITIVE);
+ if(isupper(cmd))
+ cmd = tolower(cmd);
+ }
+ else{
+ if(preloadkeystroke == KEY_DEL)
+ cmd = 'd';
+ else{
+ if(preloadkeystroke < 0x80 && isupper((int) preloadkeystroke))
+ cmd = tolower((int) preloadkeystroke);
+ else
+ cmd = (int) preloadkeystroke; /* shouldn't happen */
+ }
+ }
+
+ switch(cmd){
+ case 'd' : /* delete */
+ we_cancel = busy_cue(NULL, NULL, 1);
+ rv = cmd_delete(state, msgmap, agg, NULL); /* don't advance or offer "TAB" */
+ if(we_cancel)
+ cancel_busy_cue(0);
+ break;
+
+ case 'u' : /* undelete */
+ we_cancel = busy_cue(NULL, NULL, 1);
+ rv = cmd_undelete(state, msgmap, agg);
+ if(we_cancel)
+ cancel_busy_cue(0);
+ break;
+
+ case 'r' : /* reply */
+ rv = cmd_reply(state, msgmap, agg);
+ break;
+
+ case 'f' : /* Forward */
+ rv = cmd_forward(state, msgmap, agg);
+ break;
+
+ case '%' : /* print */
+ rv = cmd_print(state, msgmap, agg, MsgIndx);
+ break;
+
+ case 't' : /* take address */
+ rv = cmd_take_addr(state, msgmap, agg);
+ break;
+
+ case 's' : /* save */
+ rv = cmd_save(state, stream, msgmap, agg, MsgIndx);
+ break;
+
+ case 'e' : /* export */
+ rv = cmd_export(state, msgmap, q_line, agg);
+ break;
+
+ case '|' : /* pipe */
+ rv = cmd_pipe(state, msgmap, agg);
+ break;
+
+ case '*' : /* flag */
+ we_cancel = busy_cue(NULL, NULL, 1);
+ rv = cmd_flag(state, msgmap, agg);
+ if(we_cancel)
+ cancel_busy_cue(0);
+ break;
+
+ case 'b' : /* bounce */
+ rv = cmd_bounce(state, msgmap, agg);
+ break;
+
+ case '/' :
+ collapse_or_expand(state, stream, msgmap,
+ F_ON(F_SLASH_COLL_ENTIRE, ps_global)
+ ? 0L
+ : mn_get_cur(msgmap));
+ break;
+
+ case ':' :
+ select_thread_stmp(state, stream, msgmap);
+ break;
+
+ case 'x' : /* cancel */
+ cmd_cancelled((flags & AC_FROM_THREAD) ? "Thread command"
+ : "Apply command");
+ break;
+
+ case 'z' : /* default */
+ q_status_message(SM_INFO, 0, 2,
+ "Cancelled, there is no default command");
+ break;
+
+ default:
+ break;
+ }
+
+ return(rv);
+}
+
+
+/*
+ * Select by message number ranges.
+ * Sets searched bits in mail_elts
+ *
+ * Args limitsrch -- limit search to this searchset
+ *
+ * Returns 0 on success.
+ */
+int
+select_by_number(MAILSTREAM *stream, MSGNO_S *msgmap, SEARCHSET **limitsrch)
+{
+ int r, end;
+ long n1, n2, raw;
+ char number1[16], number2[16], numbers[80], *p, *t;
+ HelpType help;
+ MESSAGECACHE *mc;
+
+ numbers[0] = '\0';
+ ps_global->mangled_footer = 1;
+ help = NO_HELP;
+ while(1){
+ int flags = OE_APPEND_CURRENT;
+
+ r = optionally_enter(numbers, -FOOTER_ROWS(ps_global), 0,
+ sizeof(numbers), _(select_num), NULL, help, &flags);
+ if(r == 4)
+ continue;
+
+ if(r == 3){
+ help = (help == NO_HELP) ? h_select_by_num : NO_HELP;
+ continue;
+ }
+
+ for(t = p = numbers; *p ; p++) /* strip whitespace */
+ if(!isspace((unsigned char)*p))
+ *t++ = *p;
+
+ *t = '\0';
+
+ if(r == 1 || numbers[0] == '\0'){
+ cmd_cancelled("Selection by number");
+ return(1);
+ }
+ else
+ break;
+ }
+
+ for(n1 = 1; n1 <= stream->nmsgs; n1++)
+ if((mc = mail_elt(stream, n1)) != NULL)
+ mc->searched = 0; /* clear searched bits */
+
+ for(p = numbers; *p ; p++){
+ t = number1;
+ while(*p && isdigit((unsigned char)*p))
+ *t++ = *p++;
+
+ *t = '\0';
+
+ end = 0;
+ if(number1[0] == '\0'){
+ if(*p == '-'){
+ q_status_message1(SM_ORDER | SM_DING, 0, 2,
+ _("Invalid number range, missing number before \"-\": %s"),
+ numbers);
+ return(1);
+ }
+ else if(!strucmp("end", p)){
+ end = 1;
+ p += strlen("end");
+ }
+ else{
+ q_status_message1(SM_ORDER | SM_DING, 0, 2,
+ _("Invalid message number: %s"), numbers);
+ return(1);
+ }
+ }
+
+ if(end)
+ n1 = mn_get_total(msgmap);
+ else if((n1 = atol(number1)) < 1L || n1 > mn_get_total(msgmap)){
+ q_status_message1(SM_ORDER | SM_DING, 0, 2,
+ _("\"%s\" out of message number range"),
+ long2string(n1));
+ return(1);
+ }
+
+ t = number2;
+ if(*p == '-'){
+ while(*++p && isdigit((unsigned char)*p))
+ *t++ = *p;
+
+ *t = '\0';
+
+ end = 0;
+ if(number2[0] == '\0'){
+ if(!strucmp("end", p)){
+ end = 1;
+ p += strlen("end");
+ }
+ else{
+ q_status_message1(SM_ORDER | SM_DING, 0, 2,
+ _("Invalid number range, missing number after \"-\": %s"),
+ numbers);
+ return(1);
+ }
+ }
+
+ if(end)
+ n2 = mn_get_total(msgmap);
+ else if((n2 = atol(number2)) < 1L || n2 > mn_get_total(msgmap)){
+ q_status_message1(SM_ORDER | SM_DING, 0, 2,
+ _("\"%s\" out of message number range"),
+ long2string(n2));
+ return(1);
+ }
+
+ if(n2 <= n1){
+ char t[20];
+
+ strncpy(t, long2string(n1), sizeof(t));
+ t[sizeof(t)-1] = '\0';
+ q_status_message2(SM_ORDER | SM_DING, 0, 2,
+ _("Invalid reverse message number range: %s-%s"),
+ t, long2string(n2));
+ return(1);
+ }
+
+ for(;n1 <= n2; n1++){
+ raw = mn_m2raw(msgmap, n1);
+ if(raw > 0L
+ && (!(limitsrch && *limitsrch)
+ || in_searchset(*limitsrch, (unsigned long) raw)))
+ mm_searched(stream, raw);
+ }
+ }
+ else{
+ raw = mn_m2raw(msgmap, n1);
+ if(raw > 0L
+ && (!(limitsrch && *limitsrch)
+ || in_searchset(*limitsrch, (unsigned long) raw)))
+ mm_searched(stream, raw);
+ }
+
+ if(*p == '\0')
+ break;
+ }
+
+ return(0);
+}
+
+
+/*
+ * Select by thread number ranges.
+ * Sets searched bits in mail_elts
+ *
+ * Args limitsrch -- limit search to this searchset
+ *
+ * Returns 0 on success.
+ */
+int
+select_by_thrd_number(MAILSTREAM *stream, MSGNO_S *msgmap, SEARCHSET **msgset)
+{
+ int r, end;
+ long n1, n2;
+ char number1[16], number2[16], numbers[80], *p, *t;
+ HelpType help;
+ PINETHRD_S *thrd = NULL;
+ MESSAGECACHE *mc;
+
+ numbers[0] = '\0';
+ ps_global->mangled_footer = 1;
+ help = NO_HELP;
+ while(1){
+ int flags = OE_APPEND_CURRENT;
+
+ r = optionally_enter(numbers, -FOOTER_ROWS(ps_global), 0,
+ sizeof(numbers), _(select_num), NULL, help, &flags);
+ if(r == 4)
+ continue;
+
+ if(r == 3){
+ help = (help == NO_HELP) ? h_select_by_thrdnum : NO_HELP;
+ continue;
+ }
+
+ for(t = p = numbers; *p ; p++) /* strip whitespace */
+ if(!isspace((unsigned char)*p))
+ *t++ = *p;
+
+ *t = '\0';
+
+ if(r == 1 || numbers[0] == '\0'){
+ cmd_cancelled("Selection by number");
+ return(1);
+ }
+ else
+ break;
+ }
+
+ for(n1 = 1; n1 <= stream->nmsgs; n1++)
+ if((mc = mail_elt(stream, n1)) != NULL)
+ mc->searched = 0; /* clear searched bits */
+
+ for(p = numbers; *p ; p++){
+ t = number1;
+ while(*p && isdigit((unsigned char)*p))
+ *t++ = *p++;
+
+ *t = '\0';
+
+ end = 0;
+ if(number1[0] == '\0'){
+ if(*p == '-'){
+ q_status_message1(SM_ORDER | SM_DING, 0, 2,
+ _("Invalid number range, missing number before \"-\": %s"),
+ numbers);
+ return(1);
+ }
+ else if(!strucmp("end", p)){
+ end = 1;
+ p += strlen("end");
+ }
+ else{
+ q_status_message1(SM_ORDER | SM_DING, 0, 2,
+ _("Invalid thread number: %s"), numbers);
+ return(1);
+ }
+ }
+
+ if(end)
+ n1 = msgmap->max_thrdno;
+ else if((n1 = atol(number1)) < 1L || n1 > msgmap->max_thrdno){
+ q_status_message1(SM_ORDER | SM_DING, 0, 2,
+ _("\"%s\" out of thread number range"),
+ long2string(n1));
+ return(1);
+ }
+
+ t = number2;
+ if(*p == '-'){
+
+ while(*++p && isdigit((unsigned char)*p))
+ *t++ = *p;
+
+ *t = '\0';
+
+ end = 0;
+ if(number2[0] == '\0'){
+ if(!strucmp("end", p)){
+ end = 1;
+ p += strlen("end");
+ }
+ else{
+ q_status_message1(SM_ORDER | SM_DING, 0, 2,
+ _("Invalid number range, missing number after \"-\": %s"),
+ numbers);
+ return(1);
+ }
+ }
+
+ if(end)
+ n2 = msgmap->max_thrdno;
+ else if((n2 = atol(number2)) < 1L || n2 > msgmap->max_thrdno){
+ q_status_message1(SM_ORDER | SM_DING, 0, 2,
+ _("\"%s\" out of thread number range"),
+ long2string(n2));
+ return(1);
+ }
+
+ if(n2 <= n1){
+ char t[20];
+
+ strncpy(t, long2string(n1), sizeof(t));
+ t[sizeof(t)-1] = '\0';
+ q_status_message2(SM_ORDER | SM_DING, 0, 2,
+ _("Invalid reverse message number range: %s-%s"),
+ t, long2string(n2));
+ return(1);
+ }
+
+ for(;n1 <= n2; n1++){
+ thrd = find_thread_by_number(stream, msgmap, n1, thrd);
+
+ if(thrd)
+ set_search_bit_for_thread(stream, thrd, msgset);
+ }
+ }
+ else{
+ thrd = find_thread_by_number(stream, msgmap, n1, NULL);
+
+ if(thrd)
+ set_search_bit_for_thread(stream, thrd, msgset);
+ }
+
+ if(*p == '\0')
+ break;
+ }
+
+ return(0);
+}
+
+
+/*
+ * Select by message dates.
+ * Sets searched bits in mail_elts
+ *
+ * Args limitsrch -- limit search to this searchset
+ *
+ * Returns 0 on success.
+ */
+int
+select_by_date(MAILSTREAM *stream, MSGNO_S *msgmap, long int msgno, SEARCHSET **limitsrch)
+{
+ int r, we_cancel = 0, when = 0;
+ char date[100], defdate[100], prompt[128];
+ time_t seldate = time(0);
+ struct tm *seldate_tm;
+ SEARCHPGM *pgm;
+ HelpType help;
+ static struct _tense {
+ char *preamble,
+ *range,
+ *scope;
+ } tense[] = {
+ {"were ", "SENT SINCE", " (inclusive)"},
+ {"were ", "SENT BEFORE", " (exclusive)"},
+ {"were ", "SENT ON", "" },
+ {"", "ARRIVED SINCE", " (inclusive)"},
+ {"", "ARRIVED BEFORE", " (exclusive)"},
+ {"", "ARRIVED ON", "" }
+ };
+
+ date[0] = '\0';
+ ps_global->mangled_footer = 1;
+ help = NO_HELP;
+
+ /*
+ * If talking to an old server, default to SINCE instead of
+ * SENTSINCE, which was added later.
+ */
+ if(is_imap_stream(stream) && !modern_imap_stream(stream))
+ when = 3;
+
+ while(1){
+ int flags = OE_APPEND_CURRENT;
+
+ seldate_tm = localtime(&seldate);
+ snprintf(defdate, sizeof(defdate), "%.2d-%.4s-%.4d", seldate_tm->tm_mday,
+ month_abbrev(seldate_tm->tm_mon + 1),
+ seldate_tm->tm_year + 1900);
+ defdate[sizeof(defdate)-1] = '\0';
+ snprintf(prompt,sizeof(prompt),"Select messages which %s%s%s [%s]: ",
+ tense[when].preamble, tense[when].range,
+ tense[when].scope, defdate);
+ prompt[sizeof(prompt)-1] = '\0';
+ r = optionally_enter(date,-FOOTER_ROWS(ps_global), 0, sizeof(date),
+ prompt, sel_date_opt, help, &flags);
+ switch (r){
+ case 1 :
+ cmd_cancelled("Selection by date");
+ return(1);
+
+ case 3 :
+ help = (help == NO_HELP) ? h_select_date : NO_HELP;
+ continue;
+
+ case 4 :
+ continue;
+
+ case 11 :
+ {
+ MESSAGECACHE *mc;
+ long rawno;
+
+ if(stream && (rawno = mn_m2raw(msgmap, msgno)) > 0L
+ && rawno <= stream->nmsgs
+ && (mc = mail_elt(stream, rawno))){
+
+ /* cache not filled in yet? */
+ if(mc->day == 0){
+ char seq[20];
+
+ if(stream->dtb && stream->dtb->flags & DR_NEWS){
+ strncpy(seq,
+ ulong2string(mail_uid(stream, rawno)),
+ sizeof(seq));
+ seq[sizeof(seq)-1] = '\0';
+ mail_fetch_overview(stream, seq, NULL);
+ }
+ else{
+ strncpy(seq, long2string(rawno),
+ sizeof(seq));
+ seq[sizeof(seq)-1] = '\0';
+ mail_fetch_fast(stream, seq, 0L);
+ }
+ }
+
+ /* mail_date returns fixed field width date */
+ mail_date(date, mc);
+ date[11] = '\0';
+ }
+ }
+
+ continue;
+
+ case 12 : /* set default to PREVIOUS day */
+ seldate -= 86400;
+ continue;
+
+ case 13 : /* set default to NEXT day */
+ seldate += 86400;
+ continue;
+
+ case 14 :
+ when = (when+1) % (sizeof(tense) / sizeof(struct _tense));
+ continue;
+
+ default:
+ break;
+ }
+
+ removing_leading_white_space(date);
+ removing_trailing_white_space(date);
+ if(!*date){
+ strncpy(date, defdate, sizeof(date));
+ date[sizeof(date)-1] = '\0';
+ }
+
+ break;
+ }
+
+ if((pgm = mail_newsearchpgm()) != NULL){
+ MESSAGECACHE elt;
+ short converted_date;
+
+ if(mail_parse_date(&elt, (unsigned char *) date)){
+ converted_date = mail_shortdate(elt.year, elt.month, elt.day);
+
+ switch(when){
+ case 0:
+ pgm->sentsince = converted_date;
+ break;
+ case 1:
+ pgm->sentbefore = converted_date;
+ break;
+ case 2:
+ pgm->senton = converted_date;
+ break;
+ case 3:
+ pgm->since = converted_date;
+ break;
+ case 4:
+ pgm->before = converted_date;
+ break;
+ case 5:
+ pgm->on = converted_date;
+ break;
+ }
+
+ pgm->msgno = (limitsrch ? *limitsrch : NULL);
+
+ if(ps_global && ps_global->ttyo){
+ blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
+ ps_global->mangled_footer = 1;
+ }
+
+ we_cancel = busy_cue(_("Selecting"), NULL, 1);
+
+ pine_mail_search_full(stream, NULL, pgm, SE_NOPREFETCH | SE_FREE);
+
+ if(we_cancel)
+ cancel_busy_cue(0);
+
+ /* we know this was freed in mail_search, let caller know */
+ if(limitsrch)
+ *limitsrch = NULL;
+ }
+ else{
+ mail_free_searchpgm(&pgm);
+ q_status_message1(SM_ORDER, 3, 3,
+ _("Invalid date entered: %s"), date);
+ return(1);
+ }
+ }
+
+ return(0);
+}
+
+
+/*
+ * Select by searching in message headers or body.
+ * Sets searched bits in mail_elts
+ *
+ * Args limitsrch -- limit search to this searchset
+ *
+ * Returns 0 on success.
+ */
+int
+select_by_text(MAILSTREAM *stream, MSGNO_S *msgmap, long int msgno, SEARCHSET **limitsrch)
+{
+ int r, ku, type, we_cancel = 0, flags, rv, ekeyi = 0;
+ int not = 0, me = 0;
+ char sstring[80], savedsstring[80], tmp[128];
+ char *p, *sval = NULL;
+ char buftmp[MAILTMPLEN], namehdr[80];
+ ESCKEY_S ekey[8];
+ ENVELOPE *env = NULL;
+ HelpType help;
+ unsigned flagsforhist = 0;
+ static HISTORY_S *history = NULL;
+ static char *recip = "RECIPIENTS";
+ static char *partic = "PARTICIPANTS";
+ static char *match_me = N_("[Match_My_Addresses]");
+ static char *dont_match_me = N_("[Don't_Match_My_Addresses]");
+
+ ps_global->mangled_footer = 1;
+ savedsstring[0] = '\0';
+ ekey[0].ch = ekey[1].ch = ekey[2].ch = ekey[3].ch = -1;
+
+ while(1){
+ type = radio_buttons(not ? _(sel_text_not) : _(sel_text),
+ -FOOTER_ROWS(ps_global), sel_text_opt,
+ 's', 'x', NO_HELP, RB_NORM|RB_RET_HELP);
+
+ if(type == '!')
+ not = !not;
+ else if(type == 3){
+ helper(h_select_text, "HELP FOR SELECT BASED ON CONTENTS",
+ HLPD_SIMPLE);
+ ps_global->mangled_screen = 1;
+ }
+ else
+ break;
+ }
+
+ /*
+ * prepare some friendly defaults...
+ */
+ switch(type){
+ case 't' : /* address fields, offer To or From */
+ case 'f' :
+ case 'c' :
+ case 'r' :
+ case 'p' :
+ sval = (type == 't') ? "TO" :
+ (type == 'f') ? "FROM" :
+ (type == 'c') ? "CC" :
+ (type == 'r') ? recip : partic;
+ ekey[ekeyi].ch = ctrl('T');
+ ekey[ekeyi].name = "^T";
+ ekey[ekeyi].rval = 10;
+ /* TRANSLATORS: use Current To Address */
+ ekey[ekeyi++].label = N_("Cur To");
+ ekey[ekeyi].ch = ctrl('R');
+ ekey[ekeyi].name = "^R";
+ ekey[ekeyi].rval = 11;
+ /* TRANSLATORS: use Current From Address */
+ ekey[ekeyi++].label = N_("Cur From");
+ ekey[ekeyi].ch = ctrl('W');
+ ekey[ekeyi].name = "^W";
+ ekey[ekeyi].rval = 12;
+ /* TRANSLATORS: use Current Cc Address */
+ ekey[ekeyi++].label = N_("Cur Cc");
+ ekey[ekeyi].ch = ctrl('Y');
+ ekey[ekeyi].name = "^Y";
+ ekey[ekeyi].rval = 13;
+ /* TRANSLATORS: Match Me means match my address */
+ ekey[ekeyi++].label = N_("Match Me");
+ ekey[ekeyi].ch = 0;
+ ekey[ekeyi].name = "";
+ ekey[ekeyi].rval = 0;
+ ekey[ekeyi++].label = "";
+ break;
+
+ case 's' :
+ sval = "SUBJECT";
+ ekey[ekeyi].ch = ctrl('X');
+ ekey[ekeyi].name = "^X";
+ ekey[ekeyi].rval = 14;
+ /* TRANSLATORS: use Current Subject */
+ ekey[ekeyi++].label = N_("Cur Subject");
+ break;
+
+ case 'a' :
+ sval = "TEXT";
+ break;
+
+ case 'b' :
+ sval = "BODYTEXT";
+ break;
+
+ case 'h' :
+ strcpy(tmp, "Name of HEADER to match : ");
+ flags = OE_APPEND_CURRENT;
+ namehdr[0] = '\0';
+ r = 'x';
+ while (r == 'x'){
+ int done = 0;
+
+ r = optionally_enter(namehdr, -FOOTER_ROWS(ps_global), 0,
+ sizeof(namehdr), tmp, ekey, NO_HELP, &flags);
+ if (r == 1){
+ cmd_cancelled("Selection by text");
+ return(1);
+ }
+ removing_leading_white_space(namehdr);
+ while(!done){
+ while ((namehdr[0] != '\0') && /* remove trailing ":" */
+ (namehdr[strlen(namehdr) - 1] == ':'))
+ namehdr[strlen(namehdr) - 1] = '\0';
+ if ((namehdr[0] != '\0')
+ && isspace((unsigned char) namehdr[strlen(namehdr) - 1]))
+ removing_trailing_white_space(namehdr);
+ else
+ done++;
+ }
+ if (strchr(namehdr,' ') || strchr(namehdr,'\t') ||
+ strchr(namehdr,':'))
+ namehdr[0] = '\0';
+ if (namehdr[0] == '\0')
+ r = 'x';
+ }
+ sval = namehdr;
+ break;
+
+ case 'x':
+ break;
+
+ default:
+ dprint((1,"\n - BOTCH: select_text unrecognized option\n"));
+ return(1);
+ }
+
+ ekey[ekeyi].ch = KEY_UP;
+ ekey[ekeyi].rval = 30;
+ ekey[ekeyi].name = "";
+ ku = ekeyi;
+ ekey[ekeyi++].label = "";
+
+ ekey[ekeyi].ch = KEY_DOWN;
+ ekey[ekeyi].rval = 31;
+ ekey[ekeyi].name = "";
+ ekey[ekeyi++].label = "";
+
+ ekey[ekeyi].ch = -1;
+
+ if(type != 'x'){
+
+ init_hist(&history, HISTSIZE);
+
+ if(ekey[0].ch > -1 && msgno > 0L
+ && !(env=pine_mail_fetchstructure(stream,mn_m2raw(msgmap,msgno),
+ NULL)))
+ ekey[0].ch = -1;
+
+ sstring[0] = '\0';
+ help = NO_HELP;
+ r = type;
+ while(r != 'x'){
+ if(not)
+ /* TRANSLATORS: character String in message <message number> to NOT match : " */
+ snprintf(tmp, sizeof(tmp), "String in message %s to NOT match : ", sval);
+ else
+ snprintf(tmp, sizeof(tmp), "String in message %s to match : ", sval);
+
+ if(items_in_hist(history) > 0){
+ ekey[ku].name = HISTORY_UP_KEYNAME;
+ ekey[ku].label = HISTORY_KEYLABEL;
+ ekey[ku+1].name = HISTORY_DOWN_KEYNAME;
+ ekey[ku+1].label = HISTORY_KEYLABEL;
+ }
+ else{
+ ekey[ku].name = "";
+ ekey[ku].label = "";
+ ekey[ku+1].name = "";
+ ekey[ku+1].label = "";
+ }
+
+ flags = OE_APPEND_CURRENT | OE_KEEP_TRAILING_SPACE;
+ r = optionally_enter(sstring, -FOOTER_ROWS(ps_global), 0,
+ 79, tmp, ekey, help, &flags);
+
+ if(me && r == 0 && ((!not && strcmp(sstring, _(match_me))) || (not && strcmp(sstring, _(dont_match_me)))))
+ me = 0;
+
+ switch(r){
+ case 3 :
+ help = (help == NO_HELP)
+ ? (not
+ ? ((type == 'f') ? h_select_txt_not_from
+ : (type == 't') ? h_select_txt_not_to
+ : (type == 'c') ? h_select_txt_not_cc
+ : (type == 's') ? h_select_txt_not_subj
+ : (type == 'a') ? h_select_txt_not_all
+ : (type == 'r') ? h_select_txt_not_recip
+ : (type == 'p') ? h_select_txt_not_partic
+ : (type == 'b') ? h_select_txt_not_body
+ : NO_HELP)
+ : ((type == 'f') ? h_select_txt_from
+ : (type == 't') ? h_select_txt_to
+ : (type == 'c') ? h_select_txt_cc
+ : (type == 's') ? h_select_txt_subj
+ : (type == 'a') ? h_select_txt_all
+ : (type == 'r') ? h_select_txt_recip
+ : (type == 'p') ? h_select_txt_partic
+ : (type == 'b') ? h_select_txt_body
+ : NO_HELP))
+ : NO_HELP;
+
+ case 4 :
+ continue;
+
+ case 10 : /* To: default */
+ if(env && env->to && env->to->mailbox){
+ snprintf(sstring, sizeof(sstring), "%s%s%s", env->to->mailbox,
+ env->to->host ? "@" : "",
+ env->to->host ? env->to->host : "");
+ sstring[sizeof(sstring)-1] = '\0';
+ }
+ continue;
+
+ case 11 : /* From: default */
+ if(env && env->from && env->from->mailbox){
+ snprintf(sstring, sizeof(sstring), "%s%s%s", env->from->mailbox,
+ env->from->host ? "@" : "",
+ env->from->host ? env->from->host : "");
+ sstring[sizeof(sstring)-1] = '\0';
+ }
+ continue;
+
+ case 12 : /* Cc: default */
+ if(env && env->cc && env->cc->mailbox){
+ snprintf(sstring, sizeof(sstring), "%s%s%s", env->cc->mailbox,
+ env->cc->host ? "@" : "",
+ env->cc->host ? env->cc->host : "");
+ sstring[sizeof(sstring)-1] = '\0';
+ }
+ continue;
+
+ case 13 : /* Match my addresses */
+ me++;
+ snprintf(sstring, sizeof(sstring), not ? _(dont_match_me) : _(match_me));
+ continue;
+
+ case 14 : /* Subject: default */
+ if(env && env->subject && env->subject[0]){
+ char *q = NULL;
+
+ snprintf(buftmp, sizeof(buftmp), "%.75s", env->subject);
+ buftmp[sizeof(buftmp)-1] = '\0';
+ q = (char *) rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, buftmp);
+ if(q != env->subject){
+ snprintf(savedsstring, sizeof(savedsstring), "%.70s", q);
+ savedsstring[sizeof(savedsstring)-1] = '\0';
+ }
+
+ snprintf(sstring, sizeof(sstring), "%s", q);
+ sstring[sizeof(sstring)-1] = '\0';
+ }
+
+ continue;
+
+ case 30 :
+ flagsforhist = (not ? 0x1 : 0) + (me ? 0x2 : 0);
+ if((p = get_prev_hist(history, sstring, flagsforhist, NULL)) != NULL){
+ strncpy(sstring, p, sizeof(sstring));
+ sstring[sizeof(sstring)-1] = '\0';
+ if(history->hist[history->curindex]){
+ flagsforhist = history->hist[history->curindex]->flags;
+ not = (flagsforhist & 0x1) ? 1 : 0;
+ me = (flagsforhist & 0x2) ? 1 : 0;
+ }
+ }
+ else
+ Writechar(BELL, 0);
+
+ continue;
+
+ case 31 :
+ flagsforhist = (not ? 0x1 : 0) + (me ? 0x2 : 0);
+ if((p = get_next_hist(history, sstring, flagsforhist, NULL)) != NULL){
+ strncpy(sstring, p, sizeof(sstring));
+ sstring[sizeof(sstring)-1] = '\0';
+ if(history->hist[history->curindex]){
+ flagsforhist = history->hist[history->curindex]->flags;
+ not = (flagsforhist & 0x1) ? 1 : 0;
+ me = (flagsforhist & 0x2) ? 1 : 0;
+ }
+ }
+ else
+ Writechar(BELL, 0);
+
+ continue;
+
+ default :
+ break;
+ }
+
+ if(r == 1 || sstring[0] == '\0')
+ r = 'x';
+
+ break;
+ }
+ }
+
+ if(type == 'x' || r == 'x'){
+ cmd_cancelled("Selection by text");
+ return(1);
+ }
+
+ if(ps_global && ps_global->ttyo){
+ blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
+ ps_global->mangled_footer = 1;
+ }
+
+ we_cancel = busy_cue(_("Selecting"), NULL, 1);
+
+ flagsforhist = (not ? 0x1 : 0) + (me ? 0x2 : 0);
+ save_hist(history, sstring, flagsforhist, NULL);
+
+ rv = agg_text_select(stream, msgmap, type, namehdr, not, me, sstring, "utf-8", limitsrch);
+ if(we_cancel)
+ cancel_busy_cue(0);
+
+ return(rv);
+}
+
+
+/*
+ * Select by message size.
+ * Sets searched bits in mail_elts
+ *
+ * Args limitsrch -- limit search to this searchset
+ *
+ * Returns 0 on success.
+ */
+int
+select_by_size(MAILSTREAM *stream, SEARCHSET **limitsrch)
+{
+ int r, large = 1, we_cancel = 0;
+ unsigned long n, mult = 1L, numerator = 0L, divisor = 1L;
+ char size[16], numbers[80], *p, *t;
+ HelpType help;
+ SEARCHPGM *pgm;
+ long flags = (SE_NOPREFETCH | SE_FREE);
+
+ numbers[0] = '\0';
+ ps_global->mangled_footer = 1;
+
+ help = NO_HELP;
+ while(1){
+ int flgs = OE_APPEND_CURRENT;
+
+ sel_size_opt[1].label = large ? sel_size_smaller : sel_size_larger;
+
+ r = optionally_enter(numbers, -FOOTER_ROWS(ps_global), 0,
+ sizeof(numbers), large ? _(select_size_larger_msg)
+ : _(select_size_smaller_msg),
+ sel_size_opt, help, &flgs);
+ if(r == 4)
+ continue;
+
+ if(r == 14){
+ large = 1 - large;
+ continue;
+ }
+
+ if(r == 3){
+ help = (help == NO_HELP) ? (large ? h_select_by_larger_size
+ : h_select_by_smaller_size)
+ : NO_HELP;
+ continue;
+ }
+
+ for(t = p = numbers; *p ; p++) /* strip whitespace */
+ if(!isspace((unsigned char)*p))
+ *t++ = *p;
+
+ *t = '\0';
+
+ if(r == 1 || numbers[0] == '\0'){
+ cmd_cancelled("Selection by size");
+ return(1);
+ }
+ else
+ break;
+ }
+
+ if(numbers[0] == '-'){
+ q_status_message1(SM_ORDER | SM_DING, 0, 2,
+ _("Invalid size entered: %s"), numbers);
+ return(1);
+ }
+
+ t = size;
+ p = numbers;
+
+ while(*p && isdigit((unsigned char)*p))
+ *t++ = *p++;
+
+ *t = '\0';
+
+ if(size[0] == '\0' && *p == '.' && isdigit(*(p+1))){
+ size[0] = '0';
+ size[1] = '\0';
+ }
+
+ if(size[0] == '\0'){
+ q_status_message1(SM_ORDER | SM_DING, 0, 2,
+ _("Invalid size entered: %s"), numbers);
+ return(1);
+ }
+
+ n = strtoul(size, (char **)NULL, 10);
+
+ size[0] = '\0';
+ if(*p == '.'){
+ /*
+ * We probably ought to just use atof() to convert 1.1 into a
+ * double, but since we haven't used atof() anywhere else I'm
+ * reluctant to use it because of portability concerns.
+ */
+ p++;
+ t = size;
+ while(*p && isdigit((unsigned char)*p)){
+ *t++ = *p++;
+ divisor *= 10;
+ }
+
+ *t = '\0';
+
+ if(size[0])
+ numerator = strtoul(size, (char **)NULL, 10);
+ }
+
+ switch(*p){
+ case 'g':
+ case 'G':
+ mult *= 1000;
+ /* fall through */
+
+ case 'm':
+ case 'M':
+ mult *= 1000;
+ /* fall through */
+
+ case 'k':
+ case 'K':
+ mult *= 1000;
+ break;
+ }
+
+ n = n * mult + (numerator * mult) / divisor;
+
+ pgm = mail_newsearchpgm();
+ if(large)
+ pgm->larger = n;
+ else
+ pgm->smaller = n;
+
+ if(is_imap_stream(stream) && !modern_imap_stream(stream))
+ flags |= SE_NOSERVER;
+
+ if(ps_global && ps_global->ttyo){
+ blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
+ ps_global->mangled_footer = 1;
+ }
+
+ we_cancel = busy_cue(_("Selecting"), NULL, 1);
+
+ pgm->msgno = (limitsrch ? *limitsrch : NULL);
+ pine_mail_search_full(stream, NULL, pgm, SE_NOPREFETCH | SE_FREE);
+ /* we know this was freed in mail_search, let caller know */
+ if(limitsrch)
+ *limitsrch = NULL;
+
+ if(we_cancel)
+ cancel_busy_cue(0);
+
+ return(0);
+}
+
+
+/*
+ * visible_searchset -- return c-client search set unEXLDed
+ * sequence numbers
+ */
+SEARCHSET *
+visible_searchset(MAILSTREAM *stream, MSGNO_S *msgmap)
+{
+ long n, run;
+ SEARCHSET *full_set = NULL, **set;
+
+ /*
+ * If we're talking to anything other than a server older than
+ * imap 4rev1, build a searchset otherwise it'll choke.
+ */
+ if(!(is_imap_stream(stream) && !modern_imap_stream(stream))){
+ if(any_lflagged(msgmap, MN_EXLD)){
+ for(n = 1L, set = &full_set, run = 0L; n <= stream->nmsgs; n++)
+ if(get_lflag(stream, NULL, n, MN_EXLD)){
+ if(run){ /* previous NOT excluded? */
+ if(run > 1L)
+ (*set)->last = n - 1L;
+
+ set = &(*set)->next;
+ run = 0L;
+ }
+ }
+ else if(run++){ /* next in run */
+ (*set)->last = n;
+ }
+ else{ /* start of run */
+ *set = mail_newsearchset();
+ (*set)->first = n;
+ }
+ }
+ else{
+ full_set = mail_newsearchset();
+ full_set->first = 1L;
+ full_set->last = stream->nmsgs;
+ }
+ }
+
+ return(full_set);
+}
+
+
+/*
+ * Select by message status bits.
+ * Sets searched bits in mail_elts
+ *
+ * Args limitsrch -- limit search to this searchset
+ *
+ * Returns 0 on success.
+ */
+int
+select_by_status(MAILSTREAM *stream, SEARCHSET **limitsrch)
+{
+ int s, not = 0, we_cancel = 0, rv;
+
+ while(1){
+ s = radio_buttons((not) ? _(sel_flag_not) : _(sel_flag),
+ -FOOTER_ROWS(ps_global), sel_flag_opt, '*', 'x',
+ NO_HELP, RB_NORM|RB_RET_HELP);
+
+ if(s == 'x'){
+ cmd_cancelled("Selection by status");
+ return(1);
+ }
+ else if(s == 3){
+ helper(h_select_status, _("HELP FOR SELECT BASED ON STATUS"),
+ HLPD_SIMPLE);
+ ps_global->mangled_screen = 1;
+ }
+ else if(s == '!')
+ not = !not;
+ else
+ break;
+ }
+
+ if(ps_global && ps_global->ttyo){
+ blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
+ ps_global->mangled_footer = 1;
+ }
+
+ we_cancel = busy_cue(_("Selecting"), NULL, 1);
+ rv = agg_flag_select(stream, not, s, limitsrch);
+ if(we_cancel)
+ cancel_busy_cue(0);
+
+ return(rv);
+}
+
+
+/*
+ * Select by rule. Usually srch, indexcolor, and roles would be most useful.
+ * Sets searched bits in mail_elts
+ *
+ * Args limitsrch -- limit search to this searchset
+ *
+ * Returns 0 on success.
+ */
+int
+select_by_rule(MAILSTREAM *stream, SEARCHSET **limitsrch)
+{
+ char rulenick[1000], *nick;
+ PATGRP_S *patgrp;
+ int r, not = 0, we_cancel = 0, rflags = ROLE_DO_SRCH
+ | ROLE_DO_INCOLS
+ | ROLE_DO_ROLES
+ | ROLE_DO_SCORES
+ | ROLE_DO_OTHER
+ | ROLE_DO_FILTER;
+
+ rulenick[0] = '\0';
+ ps_global->mangled_footer = 1;
+
+ do{
+ int oe_flags;
+
+ oe_flags = OE_APPEND_CURRENT;
+ r = optionally_enter(rulenick, -FOOTER_ROWS(ps_global), 0,
+ sizeof(rulenick),
+ not ? _("Rule to NOT match: ")
+ : _("Rule to match: "),
+ sel_key_opt, NO_HELP, &oe_flags);
+
+ if(r == 14){
+ /* select rulenick from a list */
+ if((nick=choose_a_rule(rflags)) != NULL){
+ strncpy(rulenick, nick, sizeof(rulenick)-1);
+ rulenick[sizeof(rulenick)-1] = '\0';
+ fs_give((void **) &nick);
+ }
+ else
+ r = 4;
+ }
+ else if(r == '!')
+ not = !not;
+
+ if(r == 3){
+ helper(h_select_rule, _("HELP FOR SELECT BY RULE"), HLPD_SIMPLE);
+ ps_global->mangled_screen = 1;
+ }
+ else if(r == 1){
+ cmd_cancelled("Selection by Rule");
+ return(1);
+ }
+
+ removing_leading_and_trailing_white_space(rulenick);
+
+ }while(r == 3 || r == 4 || r == '!');
+
+
+ /*
+ * The approach of requiring a nickname instead of just allowing the
+ * user to select from the list of rules has the drawback that a rule
+ * may not have a nickname, or there may be more than one rule with
+ * the same nickname. However, it has the benefit of allowing the user
+ * to type in the nickname and, most importantly, allows us to set
+ * up the ! (not). We could incorporate the ! into the selection
+ * screen, but this is easier and also allows the typing of nicks.
+ * User can just set up nicknames if they want to use this feature.
+ */
+ patgrp = nick_to_patgrp(rulenick, rflags);
+
+ if(patgrp){
+ if(ps_global && ps_global->ttyo){
+ blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
+ ps_global->mangled_footer = 1;
+ }
+
+ we_cancel = busy_cue(_("Selecting"), NULL, 1);
+ match_pattern(patgrp, stream, limitsrch ? *limitsrch : 0, NULL,
+ get_msg_score,
+ (not ? MP_NOT : 0) | SE_NOPREFETCH);
+ free_patgrp(&patgrp);
+ if(we_cancel)
+ cancel_busy_cue(0);
+ }
+
+ if(limitsrch && *limitsrch){
+ mail_free_searchset(limitsrch);
+ *limitsrch = NULL;
+ }
+
+ return(0);
+}
+
+
+/*
+ * Allow user to choose a rule from their list of rules.
+ *
+ * Returns an allocated rule nickname on success, NULL otherwise.
+ */
+char *
+choose_a_rule(int rflags)
+{
+ char *choice = NULL;
+ char **rule_list, **lp;
+ int cnt = 0;
+ PAT_S *pat;
+ PAT_STATE pstate;
+
+ if(!(nonempty_patterns(rflags, &pstate) && first_pattern(&pstate))){
+ q_status_message(SM_ORDER, 3, 3,
+ _("No rules available. Use Setup/Rules to add some."));
+ return(choice);
+ }
+
+ /*
+ * Build a list of rules to choose from.
+ */
+
+ for(pat = first_pattern(&pstate); pat; pat = next_pattern(&pstate))
+ cnt++;
+
+ if(cnt <= 0){
+ q_status_message(SM_ORDER, 3, 4, _("No rules defined, use Setup/Rules"));
+ return(choice);
+ }
+
+ lp = rule_list = (char **) fs_get((cnt + 1) * sizeof(*rule_list));
+ memset(rule_list, 0, (cnt+1) * sizeof(*rule_list));
+
+ for(pat = first_pattern(&pstate); pat; pat = next_pattern(&pstate))
+ *lp++ = cpystr((pat->patgrp && pat->patgrp->nick)
+ ? pat->patgrp->nick : "?");
+
+ /* TRANSLATORS: SELECT A RULE is a screen title
+ TRANSLATORS: Print something1 using something2.
+ "rules" is something1 */
+ choice = choose_item_from_list(rule_list, NULL, _("SELECT A RULE"),
+ _("rules"), h_select_rule_screen,
+ _("HELP FOR SELECTING A RULE NICKNAME"), NULL);
+
+ if(!choice)
+ q_status_message(SM_ORDER, 1, 4, "No choice");
+
+ free_list_array(&rule_list);
+
+ return(choice);
+}
+
+
+/*
+ * Select by current thread.
+ * Sets searched bits in mail_elts for this entire thread
+ *
+ * Args limitsrch -- limit search to this searchset
+ *
+ * Returns 0 on success.
+ */
+int
+select_by_thread(MAILSTREAM *stream, MSGNO_S *msgmap, SEARCHSET **limitsrch)
+{
+ long n;
+ PINETHRD_S *thrd = NULL;
+ int ret = 1;
+ MESSAGECACHE *mc;
+
+ if(!stream)
+ return(ret);
+
+ for(n = 1L; n <= stream->nmsgs; n++)
+ if((mc = mail_elt(stream, n)) != NULL)
+ mc->searched = 0; /* clear searched bits */
+
+ thrd = fetch_thread(stream, mn_m2raw(msgmap, mn_get_cur(msgmap)));
+ if(thrd && thrd->top && thrd->top != thrd->rawno)
+ thrd = fetch_thread(stream, thrd->top);
+
+ /*
+ * This doesn't unselect if the thread is already selected
+ * (like select current does), it always selects.
+ * There is no way to select ! this thread.
+ */
+ if(thrd){
+ set_search_bit_for_thread(stream, thrd, limitsrch);
+ ret = 0;
+ }
+
+ return(ret);
+}
+
+
+/*
+ * Select by message keywords.
+ * Sets searched bits in mail_elts
+ *
+ * Args limitsrch -- limit search to this searchset
+ *
+ * Returns 0 on success.
+ */
+int
+select_by_keyword(MAILSTREAM *stream, SEARCHSET **limitsrch)
+{
+ int r, not = 0, we_cancel = 0;
+ char keyword[MAXUSERFLAG+1], *kword;
+ char *error = NULL, *p, *prompt;
+ HelpType help;
+ SEARCHPGM *pgm;
+
+ keyword[0] = '\0';
+ ps_global->mangled_footer = 1;
+
+ help = NO_HELP;
+ do{
+ int oe_flags;
+
+ if(error){
+ q_status_message(SM_ORDER, 3, 4, error);
+ fs_give((void **) &error);
+ }
+
+ if(F_ON(F_FLAG_SCREEN_KW_SHORTCUT, ps_global) && ps_global->keywords){
+ if(not)
+ prompt = _("Keyword (or keyword initial) to NOT match: ");
+ else
+ prompt = _("Keyword (or keyword initial) to match: ");
+ }
+ else{
+ if(not)
+ prompt = _("Keyword to NOT match: ");
+ else
+ prompt = _("Keyword to match: ");
+ }
+
+ oe_flags = OE_APPEND_CURRENT;
+ r = optionally_enter(keyword, -FOOTER_ROWS(ps_global), 0,
+ sizeof(keyword),
+ prompt, sel_key_opt, help, &oe_flags);
+
+ if(r == 14){
+ /* select keyword from a list */
+ if((kword=choose_a_keyword()) != NULL){
+ strncpy(keyword, kword, sizeof(keyword)-1);
+ keyword[sizeof(keyword)-1] = '\0';
+ fs_give((void **) &kword);
+ }
+ else
+ r = 4;
+ }
+ else if(r == '!')
+ not = !not;
+
+ if(r == 3)
+ help = help == NO_HELP ? h_select_keyword : NO_HELP;
+ else if(r == 1){
+ cmd_cancelled("Selection by keyword");
+ return(1);
+ }
+
+ removing_leading_and_trailing_white_space(keyword);
+
+ }while(r == 3 || r == 4 || r == '!' || keyword_check(keyword, &error));
+
+
+ if(F_ON(F_FLAG_SCREEN_KW_SHORTCUT, ps_global) && ps_global->keywords){
+ p = initial_to_keyword(keyword);
+ if(p != keyword){
+ strncpy(keyword, p, sizeof(keyword)-1);
+ keyword[sizeof(keyword)-1] = '\0';
+ }
+ }
+
+ /*
+ * We want to check the keyword, not the nickname of the keyword,
+ * so convert it to the keyword if necessary.
+ */
+ p = nick_to_keyword(keyword);
+ if(p != keyword){
+ strncpy(keyword, p, sizeof(keyword)-1);
+ keyword[sizeof(keyword)-1] = '\0';
+ }
+
+ pgm = mail_newsearchpgm();
+ if(not){
+ pgm->unkeyword = mail_newstringlist();
+ pgm->unkeyword->text.data = (unsigned char *) cpystr(keyword);
+ pgm->unkeyword->text.size = strlen(keyword);
+ }
+ else{
+ pgm->keyword = mail_newstringlist();
+ pgm->keyword->text.data = (unsigned char *) cpystr(keyword);
+ pgm->keyword->text.size = strlen(keyword);
+ }
+
+ if(ps_global && ps_global->ttyo){
+ blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
+ ps_global->mangled_footer = 1;
+ }
+
+ we_cancel = busy_cue(_("Selecting"), NULL, 1);
+
+ pgm->msgno = (limitsrch ? *limitsrch : NULL);
+ pine_mail_search_full(stream, "UTF-8", pgm, SE_NOPREFETCH | SE_FREE);
+ /* we know this was freed in mail_search, let caller know */
+ if(limitsrch)
+ *limitsrch = NULL;
+
+ if(we_cancel)
+ cancel_busy_cue(0);
+
+ return(0);
+}
+
+
+/*
+ * Allow user to choose a keyword from their list of keywords.
+ *
+ * Returns an allocated keyword on success, NULL otherwise.
+ */
+char *
+choose_a_keyword(void)
+{
+ char *choice = NULL;
+ char **keyword_list, **lp;
+ int cnt;
+ KEYWORD_S *kw;
+
+ /*
+ * Build a list of keywords to choose from.
+ */
+
+ for(cnt = 0, kw = ps_global->keywords; kw; kw = kw->next)
+ cnt++;
+
+ if(cnt <= 0){
+ q_status_message(SM_ORDER, 3, 4,
+ _("No keywords defined, use \"keywords\" option in Setup/Config"));
+ return(choice);
+ }
+
+ lp = keyword_list = (char **) fs_get((cnt + 1) * sizeof(*keyword_list));
+ memset(keyword_list, 0, (cnt+1) * sizeof(*keyword_list));
+
+ for(kw = ps_global->keywords; kw; kw = kw->next)
+ *lp++ = cpystr(kw->nick ? kw->nick : kw->kw ? kw->kw : "");
+
+ /* TRANSLATORS: SELECT A KEYWORD is a screen title
+ TRANSLATORS: Print something1 using something2.
+ "keywords" is something1 */
+ choice = choose_item_from_list(keyword_list, NULL, _("SELECT A KEYWORD"),
+ _("keywords"), h_select_keyword_screen,
+ _("HELP FOR SELECTING A KEYWORD"), NULL);
+
+ if(!choice)
+ q_status_message(SM_ORDER, 1, 4, "No choice");
+
+ free_list_array(&keyword_list);
+
+ return(choice);
+}
+
+
+/*
+ * Allow user to choose a list of keywords from their list of keywords.
+ *
+ * Returns allocated list.
+ */
+char **
+choose_list_of_keywords(void)
+{
+ LIST_SEL_S *listhead, *ls, *p;
+ char **ret = NULL;
+ int cnt, i;
+ KEYWORD_S *kw;
+
+ /*
+ * Build a list of keywords to choose from.
+ */
+
+ p = listhead = NULL;
+ for(kw = ps_global->keywords; kw; kw = kw->next){
+
+ ls = (LIST_SEL_S *) fs_get(sizeof(*ls));
+ memset(ls, 0, sizeof(*ls));
+ ls->item = cpystr(kw->nick ? kw->nick : kw->kw ? kw->kw : "");
+
+ if(p){
+ p->next = ls;
+ p = p->next;
+ }
+ else
+ listhead = p = ls;
+ }
+
+ if(!listhead)
+ return(ret);
+
+ /* TRANSLATORS: SELECT KEYWORDS is a screen title
+ Print something1 using something2.
+ "keywords" is something1 */
+ if(!select_from_list_screen(listhead, SFL_ALLOW_LISTMODE,
+ _("SELECT KEYWORDS"), _("keywords"),
+ h_select_multkeyword_screen,
+ _("HELP FOR SELECTING KEYWORDS"), NULL)){
+ for(cnt = 0, p = listhead; p; p = p->next)
+ if(p->selected)
+ cnt++;
+
+ ret = (char **) fs_get((cnt+1) * sizeof(*ret));
+ memset(ret, 0, (cnt+1) * sizeof(*ret));
+ for(i = 0, p = listhead; p; p = p->next)
+ if(p->selected)
+ ret[i++] = cpystr(p->item ? p->item : "");
+ }
+
+ free_list_sel(&listhead);
+
+ return(ret);
+}
+
+
+/*
+ * Allow user to choose a charset
+ *
+ * Returns an allocated charset on success, NULL otherwise.
+ */
+char *
+choose_a_charset(int which_charsets)
+{
+ char *choice = NULL;
+ char **charset_list, **lp;
+ const CHARSET *cs;
+ int cnt;
+
+ /*
+ * Build a list of charsets to choose from.
+ */
+
+ for(cnt = 0, cs = utf8_charset(NIL); cs && cs->name; cs++){
+ if(!(cs->flags & (CF_UNSUPRT|CF_NOEMAIL))
+ && ((which_charsets & CAC_ALL)
+ || (which_charsets & CAC_POSTING
+ && cs->flags & CF_POSTING)
+ || (which_charsets & CAC_DISPLAY
+ && cs->type != CT_2022
+ && (cs->flags & (CF_PRIMARY|CF_DISPLAY)) == (CF_PRIMARY|CF_DISPLAY))))
+ cnt++;
+ }
+
+ if(cnt <= 0){
+ q_status_message(SM_ORDER, 3, 4,
+ _("No charsets found? Enter charset manually."));
+ return(choice);
+ }
+
+ lp = charset_list = (char **) fs_get((cnt + 1) * sizeof(*charset_list));
+ memset(charset_list, 0, (cnt+1) * sizeof(*charset_list));
+
+ for(cs = utf8_charset(NIL); cs && cs->name; cs++){
+ if(!(cs->flags & (CF_UNSUPRT|CF_NOEMAIL))
+ && ((which_charsets & CAC_ALL)
+ || (which_charsets & CAC_POSTING
+ && cs->flags & CF_POSTING)
+ || (which_charsets & CAC_DISPLAY
+ && cs->type != CT_2022
+ && (cs->flags & (CF_PRIMARY|CF_DISPLAY)) == (CF_PRIMARY|CF_DISPLAY))))
+ *lp++ = cpystr(cs->name);
+ }
+
+ /* TRANSLATORS: SELECT A CHARACTER SET is a screen title
+ TRANSLATORS: Print something1 using something2.
+ "character sets" is something1 */
+ choice = choose_item_from_list(charset_list, NULL, _("SELECT A CHARACTER SET"),
+ _("character sets"), h_select_charset_screen,
+ _("HELP FOR SELECTING A CHARACTER SET"), NULL);
+
+ if(!choice)
+ q_status_message(SM_ORDER, 1, 4, "No choice");
+
+ free_list_array(&charset_list);
+
+ return(choice);
+}
+
+
+/*
+ * Allow user to choose a list of character sets and/or scripts
+ *
+ * Returns allocated list.
+ */
+char **
+choose_list_of_charsets(void)
+{
+ LIST_SEL_S *listhead, *ls, *p;
+ char **ret = NULL;
+ int cnt, i, got_one;
+ const CHARSET *cs;
+ SCRIPT *s;
+ char *q, *t;
+ long width, limit;
+ char buf[1024], *folded;
+
+ /*
+ * Build a list of charsets to choose from.
+ */
+
+ p = listhead = NULL;
+
+ /* this width is determined by select_from_list_screen() */
+ width = ps_global->ttyo->screen_cols - 4;
+
+ /* first comes a list of scripts (sets of character sets) */
+ for(s = utf8_script(NIL); s && s->name; s++){
+
+ limit = sizeof(buf)-1;
+ q = buf;
+ memset(q, 0, limit+1);
+
+ if(s->name)
+ sstrncpy(&q, s->name, limit);
+
+ if(s->description){
+ sstrncpy(&q, " (", limit-(q-buf));
+ sstrncpy(&q, s->description, limit-(q-buf));
+ sstrncpy(&q, ")", limit-(q-buf));
+ }
+
+ /* add the list of charsets that are in this script */
+ got_one = 0;
+ for(cs = utf8_charset(NIL);
+ cs && cs->name && (q-buf) < limit; cs++){
+ if(cs->script & s->script){
+ /*
+ * Filter out some un-useful members of the list.
+ * UTF-7 and UTF-8 weren't actually in the list at the
+ * time this was written. Just making sure.
+ */
+ if(!strucmp(cs->name, "ISO-2022-JP-2")
+ || !strucmp(cs->name, "UTF-7")
+ || !strucmp(cs->name, "UTF-8"))
+ continue;
+
+ if(got_one)
+ sstrncpy(&q, " ", limit-(q-buf));
+ else{
+ got_one = 1;
+ sstrncpy(&q, " {", limit-(q-buf));
+ }
+
+ sstrncpy(&q, cs->name, limit-(q-buf));
+ }
+ }
+
+ if(got_one)
+ sstrncpy(&q, "}", limit-(q-buf));
+
+ /* fold this line so that it can all be seen on the screen */
+ folded = fold(buf, width, width, "", " ", FLD_NONE);
+ if(folded){
+ t = folded;
+ while(t && *t && (q = strindex(t, '\n')) != NULL){
+ *q = '\0';
+
+ ls = (LIST_SEL_S *) fs_get(sizeof(*ls));
+ memset(ls, 0, sizeof(*ls));
+ if(t == folded)
+ ls->item = cpystr(s->name);
+ else
+ ls->flags = SFL_NOSELECT;
+
+ ls->display_item = cpystr(t);
+
+ t = q+1;
+
+ if(p){
+ p->next = ls;
+ p = p->next;
+ }
+ else{
+ /* add a heading */
+ listhead = (LIST_SEL_S *) fs_get(sizeof(*ls));
+ memset(listhead, 0, sizeof(*listhead));
+ listhead->flags = SFL_NOSELECT;
+ listhead->display_item =
+ cpystr(_("Scripts representing groups of related character sets"));
+ listhead->next = (LIST_SEL_S *) fs_get(sizeof(*ls));
+ memset(listhead->next, 0, sizeof(*listhead));
+ listhead->next->flags = SFL_NOSELECT;
+ listhead->next->display_item =
+ cpystr(repeat_char(width, '-'));
+
+ listhead->next->next = ls;
+ p = ls;
+ }
+ }
+
+ fs_give((void **) &folded);
+ }
+ }
+
+ ls = (LIST_SEL_S *) fs_get(sizeof(*ls));
+ memset(ls, 0, sizeof(*ls));
+ ls->flags = SFL_NOSELECT;
+ if(p){
+ p->next = ls;
+ p = p->next;
+ }
+ else
+ listhead = p = ls;
+
+ ls = (LIST_SEL_S *) fs_get(sizeof(*ls));
+ memset(ls, 0, sizeof(*ls));
+ ls->flags = SFL_NOSELECT;
+ ls->display_item =
+ cpystr(_("Individual character sets, may be mixed with scripts"));
+ p->next = ls;
+ p = p->next;
+
+ ls = (LIST_SEL_S *) fs_get(sizeof(*ls));
+ memset(ls, 0, sizeof(*ls));
+ ls->flags = SFL_NOSELECT;
+ ls->display_item =
+ cpystr(repeat_char(width, '-'));
+ p->next = ls;
+ p = p->next;
+
+ /* then comes a list of individual character sets */
+ for(cs = utf8_charset(NIL); cs && cs->name; cs++){
+ ls = (LIST_SEL_S *) fs_get(sizeof(*ls));
+ memset(ls, 0, sizeof(*ls));
+ ls->item = cpystr(cs->name);
+
+ if(p){
+ p->next = ls;
+ p = p->next;
+ }
+ else
+ listhead = p = ls;
+ }
+
+ if(!listhead)
+ return(ret);
+
+ /* TRANSLATORS: SELECT CHARACTER SETS is a screen title
+ Print something1 using something2.
+ "character sets" is something1 */
+ if(!select_from_list_screen(listhead, SFL_ALLOW_LISTMODE,
+ _("SELECT CHARACTER SETS"), _("character sets"),
+ h_select_multcharsets_screen,
+ _("HELP FOR SELECTING CHARACTER SETS"), NULL)){
+ for(cnt = 0, p = listhead; p; p = p->next)
+ if(p->selected)
+ cnt++;
+
+ ret = (char **) fs_get((cnt+1) * sizeof(*ret));
+ memset(ret, 0, (cnt+1) * sizeof(*ret));
+ for(i = 0, p = listhead; p; p = p->next)
+ if(p->selected)
+ ret[i++] = cpystr(p->item ? p->item : "");
+ }
+
+ free_list_sel(&listhead);
+
+ return(ret);
+}
+
+/* Report quota summary resources in an IMAP server */
+
+void cmd_quota (struct pine *state)
+{
+ QUOTALIST *imapquota;
+ NETMBX mb;
+ STORE_S *store;
+ SCROLL_S sargs;
+
+ if(!state->mail_stream || !is_imap_stream(state->mail_stream)){
+ q_status_message(SM_ORDER, 1, 5, "Quota only available for IMAP folders");
+ return;
+ }
+
+ if (state->mail_stream
+ && !sp_dead_stream(state->mail_stream)
+ && state->mail_stream->mailbox
+ && *state->mail_stream->mailbox
+ && mail_valid_net_parse(state->mail_stream->mailbox, &mb))
+ imap_getquotaroot(state->mail_stream, mb.mailbox);
+
+ if(!state->quota) /* failed ? */
+ return; /* go back... */
+
+ if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
+ q_status_message(SM_ORDER | SM_DING, 3, 3, "Error allocating space.");
+ return;
+ }
+
+ so_puts(store, "Quota Report for ");
+ so_puts(store, state->mail_stream->original_mailbox);
+ so_puts(store, "\n\n");
+
+ for (imapquota = state->quota; imapquota; imapquota = imapquota->next){
+
+ so_puts(store, _("Resource : "));
+ so_puts(store, imapquota->name);
+ so_writec('\n', store);
+
+ so_puts(store, _("Usage : "));
+ so_puts(store, long2string(imapquota->usage));
+ if(!strucmp(imapquota->name,"STORAGE"))
+ so_puts(store, " KiB ");
+ if(!strucmp(imapquota->name,"MESSAGE")){
+ so_puts(store, _(" message"));
+ if(imapquota->usage != 1)
+ so_puts(store, _("s ")); /* plural */
+ else
+ so_puts(store, _(" "));
+ }
+ so_writec('(', store);
+ so_puts(store, long2string(100*imapquota->usage/imapquota->limit));
+ so_puts(store, "%)\n");
+
+ so_puts(store, _("Limit : "));
+ so_puts(store, long2string(imapquota->limit));
+ if(!strucmp(imapquota->name,"STORAGE"))
+ so_puts(store, " KiB\n\n");
+ if(!strucmp(imapquota->name,"MESSAGE")){
+ so_puts(store, _(" message"));
+ if(imapquota->usage != 1)
+ so_puts(store, _("s\n\n")); /* plural */
+ else
+ so_puts(store, _("\n\n"));
+ }
+ }
+
+ memset(&sargs, 0, sizeof(SCROLL_S));
+ sargs.text.text = so_text(store);
+ sargs.text.src = CharStar;
+ sargs.text.desc = _("Quota Resources Summary");
+ sargs.bar.title = _("QUOTA SUMMARY");
+ sargs.proc.tool = NULL;
+ sargs.help.text = h_quota_command;
+ sargs.help.title = NULL;
+ sargs.keys.menu = &pine_quota_keymenu;
+ setbitmap(sargs.keys.bitmap);
+
+ scrolltool(&sargs);
+ so_give(&store);
+
+ if (state->quota)
+ mail_free_quotalist(&(state->quota));
+}
+
+/*----------------------------------------------------------------------
+ Prompt the user for the type of sort he desires
+
+Args: state -- pine state pointer
+ q1 -- Line to prompt on
+
+ Returns 0 if it was cancelled, 1 otherwise.
+ ----*/
+int
+select_sort(struct pine *state, int ql, SortOrder *sort, int *rev)
+{
+ char prompt[200], tmp[3], *p;
+ int s, i;
+ int deefault = 'a', retval = 1;
+ HelpType help;
+ ESCKEY_S sorts[14];
+
+#ifdef _WINDOWS
+ DLG_SORTPARAM sortsel;
+
+ if (mswin_usedialog ()) {
+
+ sortsel.reverse = mn_get_revsort (state->msgmap);
+ sortsel.cursort = mn_get_sort (state->msgmap);
+ /* assumption here that HelpType is char ** */
+ sortsel.helptext = h_select_sort;
+ sortsel.rval = 0;
+
+ if ((retval = os_sortdialog (&sortsel))) {
+ *sort = sortsel.cursort;
+ *rev = sortsel.reverse;
+ }
+
+ return (retval);
+ }
+#endif
+
+ /*----- String together the prompt ------*/
+ tmp[1] = '\0';
+ if(F_ON(F_USE_FK,ps_global))
+ strncpy(prompt, _("Choose type of sort : "), sizeof(prompt));
+ else
+ strncpy(prompt, _("Choose type of sort, or 'R' to reverse current sort : "),
+ sizeof(prompt));
+
+ for(i = 0; state->sort_types[i] != EndofList; i++) {
+ sorts[i].rval = i;
+ p = sorts[i].label = sort_name(state->sort_types[i]);
+ while(*(p+1) && islower((unsigned char)*p))
+ p++;
+
+ sorts[i].ch = tolower((unsigned char)(tmp[0] = *p));
+ sorts[i].name = cpystr(tmp);
+
+ if(mn_get_sort(state->msgmap) == state->sort_types[i])
+ deefault = sorts[i].rval;
+ }
+
+ sorts[i].ch = 'r';
+ sorts[i].rval = 'r';
+ sorts[i].name = cpystr("R");
+ if(F_ON(F_USE_FK,ps_global))
+ sorts[i].label = N_("Reverse");
+ else
+ sorts[i].label = "";
+
+ sorts[++i].ch = -1;
+ help = h_select_sort;
+
+ if((F_ON(F_USE_FK,ps_global)
+ && ((s = double_radio_buttons(prompt,ql,sorts,deefault,'x',
+ help,RB_NORM)) != 'x'))
+ ||
+ (F_OFF(F_USE_FK,ps_global)
+ && ((s = radio_buttons(prompt,ql,sorts,deefault,'x',
+ help,RB_NORM)) != 'x'))){
+ state->mangled_body = 1; /* signal screen's changed */
+ if(s == 'r')
+ *rev = !mn_get_revsort(state->msgmap);
+ else
+ *sort = state->sort_types[s];
+
+ if(F_ON(F_SHOW_SORT, ps_global))
+ ps_global->mangled_header = 1;
+ }
+ else{
+ retval = 0;
+ cmd_cancelled("Sort");
+ }
+
+ while(--i >= 0)
+ fs_give((void **)&sorts[i].name);
+
+ blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
+ return(retval);
+}
+
+
+/*---------------------------------------------------------------------
+ Build list of folders in the given context for user selection
+
+ Args: c -- pointer to pointer to folder's context context
+ f -- folder prefix to display
+ sublist -- whether or not to use 'f's contents as prefix
+ lister -- function used to do the actual display
+
+ Returns: malloc'd string containing sequence, else NULL if
+ no messages in msgmap with local "selected" flag.
+ ----*/
+int
+display_folder_list(CONTEXT_S **c, char *f, int sublist, int (*lister) (struct pine *, CONTEXT_S **, char *, int))
+{
+ int rc;
+ CONTEXT_S *tc;
+ void (*redraw)(void) = ps_global->redrawer;
+
+ push_titlebar_state();
+ tc = *c;
+ if((rc = (*lister)(ps_global, &tc, f, sublist)) != 0)
+ *c = tc;
+
+ ClearScreen();
+ pop_titlebar_state();
+ redraw_titlebar();
+ if((ps_global->redrawer = redraw) != NULL) /* reset old value, and test */
+ (*ps_global->redrawer)();
+
+ if(rc == 1 && F_ON(F_SELECT_WO_CONFIRM, ps_global))
+ return(1);
+
+ return(0);
+}
+
+
+/*
+ * Allow user to choose a single item from a list of strings.
+ *
+ * Args list -- Array of strings to choose from, NULL terminated.
+ * displist -- Array of strings to display instead of displaying list.
+ * Indices correspond to the list array. Display the displist
+ * but return the item from list if displist non-NULL.
+ * title -- For conf_scroll_screen
+ * pdesc -- For conf_scroll_screen
+ * help -- For conf_scroll_screen
+ * htitle -- For conf_scroll_screen
+ *
+ * Returns an allocated copy of the chosen item or NULL.
+ */
+char *
+choose_item_from_list(char **list, char **displist, char *title, char *pdesc, HelpType help,
+ char *htitle, char *cursor_location)
+{
+ LIST_SEL_S *listhead, *ls, *p, *starting_val = NULL;
+ char **t, **dl;
+ char *ret = NULL, *choice = NULL;
+
+ /* build the LIST_SEL_S list */
+ p = listhead = NULL;
+ for(t = list, dl = displist; *t; t++, dl++){
+ ls = (LIST_SEL_S *) fs_get(sizeof(*ls));
+ memset(ls, 0, sizeof(*ls));
+ ls->item = cpystr(*t);
+ if(displist)
+ ls->display_item = cpystr(*dl);
+
+ if(cursor_location && (cursor_location == (*t)))
+ starting_val = ls;
+
+ if(p){
+ p->next = ls;
+ p = p->next;
+ }
+ else
+ listhead = p = ls;
+ }
+
+ if(!listhead)
+ return(ret);
+
+ if(!select_from_list_screen(listhead, SFL_NONE, title, pdesc,
+ help, htitle, starting_val))
+ for(p = listhead; !choice && p; p = p->next)
+ if(p->selected)
+ choice = p->item;
+
+ if(choice)
+ ret = cpystr(choice);
+
+ free_list_sel(&listhead);
+
+ return(ret);
+}
+
+
+void
+free_list_sel(LIST_SEL_S **lsel)
+{
+ if(lsel && *lsel){
+ free_list_sel(&(*lsel)->next);
+ if((*lsel)->item)
+ fs_give((void **) &(*lsel)->item);
+
+ if((*lsel)->display_item)
+ fs_give((void **) &(*lsel)->display_item);
+
+ fs_give((void **) lsel);
+ }
+}
+
+
+/*
+ * file_lister - call pico library's file lister
+ */
+int
+file_lister(char *title, char *path, size_t pathlen, char *file, size_t filelen, int newmail, int flags)
+{
+ PICO pbf;
+ int rv;
+ void (*redraw)(void) = ps_global->redrawer;
+
+ standard_picobuf_setup(&pbf);
+ push_titlebar_state();
+ if(!newmail)
+ pbf.newmail = NULL;
+
+/* BUG: what about help command and text? */
+ pbf.pine_anchor = title;
+
+ rv = pico_file_browse(&pbf, path, pathlen, file, filelen, NULL, 0, flags);
+ standard_picobuf_teardown(&pbf);
+ fix_windsize(ps_global);
+ init_signals(); /* has it's own signal stuff */
+
+ /* Restore display's titlebar and body */
+ pop_titlebar_state();
+ redraw_titlebar();
+ if((ps_global->redrawer = redraw) != NULL)
+ (*ps_global->redrawer)();
+
+ return(rv);
+}
+
+
+/*----------------------------------------------------------------------
+ Print current folder index
+
+ ---*/
+int
+print_index(struct pine *state, MSGNO_S *msgmap, int agg)
+{
+ long i;
+ ICE_S *ice;
+ char buf[MAX_SCREEN_COLS+1];
+
+ for(i = 1L; i <= mn_get_total(msgmap); i++){
+ if(agg && !get_lflag(state->mail_stream, msgmap, i, MN_SLCT))
+ continue;
+
+ if(!agg && msgline_hidden(state->mail_stream, msgmap, i, 0))
+ continue;
+
+ ice = build_header_line(state, state->mail_stream, msgmap, i, NULL);
+
+ if(ice){
+ /*
+ * I don't understand why we'd want to mark the current message
+ * instead of printing out the first character of the status
+ * so I'm taking it out and including the first character of the
+ * line instead. Hubert 2006-02-09
+ *
+ if(!print_char((mn_is_cur(msgmap, i)) ? '>' : ' '))
+ return(0);
+ */
+
+ if(!gf_puts(simple_index_line(buf,sizeof(buf),ice,i),
+ print_char)
+ || !gf_puts(NEWLINE, print_char))
+ return(0);
+ }
+ }
+
+ return(1);
+}
+
+
+#ifdef _WINDOWS
+
+/*
+ * windows callback to get/set header mode state
+ */
+int
+header_mode_callback(set, args)
+ int set;
+ long args;
+{
+ return(ps_global->full_header);
+}
+
+
+/*
+ * windows callback to get/set zoom mode state
+ */
+int
+zoom_mode_callback(set, args)
+ int set;
+ long args;
+{
+ return(any_lflagged(ps_global->msgmap, MN_HIDE) != 0);
+}
+
+
+/*
+ * windows callback to get/set zoom mode state
+ */
+int
+any_selected_callback(set, args)
+ int set;
+ long args;
+{
+ return(any_lflagged(ps_global->msgmap, MN_SLCT) != 0);
+}
+
+
+/*
+ *
+ */
+int
+flag_callback(set, flags)
+ int set;
+ long flags;
+{
+ MESSAGECACHE *mc;
+ int newflags = 0;
+ long msgno;
+ int permflag = 0;
+
+ switch (set) {
+ case 1: /* Important */
+ permflag = ps_global->mail_stream->perm_flagged;
+ break;
+
+ case 2: /* New */
+ permflag = ps_global->mail_stream->perm_seen;
+ break;
+
+ case 3: /* Answered */
+ permflag = ps_global->mail_stream->perm_answered;
+ break;
+
+ case 4: /* Deleted */
+ permflag = ps_global->mail_stream->perm_deleted;
+ break;
+
+ }
+
+ if(!(any_messages(ps_global->msgmap, NULL, "to Flag")
+ && can_set_flag(ps_global, "flag", permflag)))
+ return(0);
+
+ if(sp_io_error_on_stream(ps_global->mail_stream)){
+ sp_set_io_error_on_stream(ps_global->mail_stream, 0);
+ pine_mail_check(ps_global->mail_stream); /* forces write */
+ return(0);
+ }
+
+ msgno = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
+ if(msgno > 0L && ps_global->mail_stream
+ && msgno <= ps_global->mail_stream->nmsgs
+ && (mc = mail_elt(ps_global->mail_stream, msgno))
+ && mc->valid){
+ /*
+ * NOTE: code below is *VERY* sensitive to the order of
+ * the messages defined in resource.h for flag handling.
+ * Don't change it unless you know what you're doing.
+ */
+ if(set){
+ char *flagstr;
+ long mflag;
+
+ switch(set){
+ case 1 : /* Important */
+ flagstr = "\\FLAGGED";
+ mflag = (mc->flagged) ? 0L : ST_SET;
+ break;
+
+ case 2 : /* New */
+ flagstr = "\\SEEN";
+ mflag = (mc->seen) ? 0L : ST_SET;
+ break;
+
+ case 3 : /* Answered */
+ flagstr = "\\ANSWERED";
+ mflag = (mc->answered) ? 0L : ST_SET;
+ break;
+
+ case 4 : /* Deleted */
+ flagstr = "\\DELETED";
+ mflag = (mc->deleted) ? 0L : ST_SET;
+ break;
+
+ default : /* bogus */
+ return(0);
+ }
+
+ mail_flag(ps_global->mail_stream, long2string(msgno),
+ flagstr, mflag);
+
+ if(ps_global->redrawer)
+ (*ps_global->redrawer)();
+ }
+ else{
+ /* Important */
+ if(mc->flagged)
+ newflags |= 0x0001;
+
+ /* New */
+ if(!mc->seen)
+ newflags |= 0x0002;
+
+ /* Answered */
+ if(mc->answered)
+ newflags |= 0x0004;
+
+ /* Deleted */
+ if(mc->deleted)
+ newflags |= 0x0008;
+ }
+ }
+
+ return(newflags);
+}
+
+
+
+/*
+ * BUG: Should teach this about keywords
+ */
+MPopup *
+flag_submenu(mc)
+ MESSAGECACHE *mc;
+{
+ static MPopup flag_submenu[] = {
+ {tMessage, {N_("Important"), lNormal}, {IDM_MI_FLAGIMPORTANT}},
+ {tMessage, {N_("New"), lNormal}, {IDM_MI_FLAGNEW}},
+ {tMessage, {N_("Answered"), lNormal}, {IDM_MI_FLAGANSWERED}},
+ {tMessage , {N_("Deleted"), lNormal}, {IDM_MI_FLAGDELETED}},
+ {tTail}
+ };
+
+ /* Important */
+ flag_submenu[0].label.style = (mc && mc->flagged) ? lChecked : lNormal;
+
+ /* New */
+ flag_submenu[1].label.style = (mc && mc->seen) ? lNormal : lChecked;
+
+ /* Answered */
+ flag_submenu[2].label.style = (mc && mc->answered) ? lChecked : lNormal;
+
+ /* Deleted */
+ flag_submenu[3].label.style = (mc && mc->deleted) ? lChecked : lNormal;
+
+ return(flag_submenu);
+}
+
+#endif /* _WINDOWS */
diff --git a/alpine/mailcmd.h b/alpine/mailcmd.h
new file mode 100644
index 00000000..c86ef7cd
--- /dev/null
+++ b/alpine/mailcmd.h
@@ -0,0 +1,107 @@
+/*
+ * $Id: mailcmd.h 1012 2008-03-26 00:44:22Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_MAILCMD_INCLUDED
+#define PINE_MAILCMD_INCLUDED
+
+
+#include <general.h>
+#include "context.h"
+#include "mailview.h"
+#include "radio.h"
+#include "listsel.h"
+#include "../pith/mailcmd.h"
+#include "../pith/mailindx.h"
+#include "../pith/state.h"
+#include "../pith/msgno.h"
+#include "../pith/store.h"
+#include "../pith/filter.h"
+#include "../pith/string.h"
+#include "../pith/hist.h"
+
+
+#define USER_INPUT_TIMEOUT(ps) ((ps->hours_to_timeout > 0) && \
+ ((time(0) - time_of_last_input()) > 60*60*(ps->hours_to_timeout)))
+
+
+#define GE_NONE 0x00 /* get_export_filename flags */
+#define GE_IS_EXPORT 0x01 /* include EXPORT: in prompt */
+#define GE_SEQ_SENSITIVE 0x02 /* Sensitive to seq # changes */
+#define GE_NO_APPEND 0x04 /* No appending to file allowed */
+#define GE_IS_IMPORT 0x08 /* No writing of file */
+#define GE_ALLPARTS 0x10 /* Add AllParts toggle to options */
+
+#define GER_NONE 0x00 /* get_export_filename return flags */
+#define GER_OVER 0x01 /* overwrite of existing file */
+#define GER_APPEND 0x02 /* append of existing file */
+#define GER_ALLPARTS 0x04 /* AllParts toggle is on */
+
+
+#define CAC_NONE 0x00 /* flags for choose_a_charset */
+#define CAC_ALL 0x01 /* choose from entire list */
+#define CAC_POSTING 0x02 /* choose from charsets useful for posting */
+#define CAC_DISPLAY 0x04 /* choose from charsets useful for display */
+
+
+typedef enum {DontAsk, NoDel, Del, RetNoDel, RetDel} SaveDel;
+typedef enum {DontAskPreserve, NoPreserve, Preserve, RetNoPreserve, RetPreserve} SavePreserveOrder;
+
+typedef enum {View, MsgIndx, ThrdIndx} CmdWhere;
+
+
+/* exported protoypes */
+int process_cmd(struct pine *, MAILSTREAM *, MSGNO_S *, int, CmdWhere, int *);
+char *pretty_command(UCS);
+void bogus_command(UCS, char *);
+void bogus_utf8_command(char *, char *);
+int save_prompt(struct pine *, CONTEXT_S **, char *, size_t,
+ char *, ENVELOPE *, long, char *, SaveDel *,
+ SavePreserveOrder *);
+int create_for_save_prompt(CONTEXT_S *, char *, int);
+int expunge_prompt(MAILSTREAM *, char *, long);
+int save_size_changed_prompt(long, int);
+void expunge_and_close_begins(int, char *);
+int simple_export(struct pine *, void *, SourceType, char *, char *);
+int get_export_filename(struct pine *, char *, char *, char *, size_t, char *,
+ char *, ESCKEY_S *, int *, int, int, HISTORY_S **);
+char *build_updown_cmd(char *, size_t, char *, char *, char*);
+int bezerk_delimiter(ENVELOPE *, MESSAGECACHE *, gf_io_t, int);
+long jump_to(MSGNO_S *, int, UCS, SCROLL_S *, CmdWhere);
+char *broach_folder(int, int, int *, CONTEXT_S **);
+int ask_mailbox_reopen(struct pine *, int *);
+void visit_folder(struct pine *, char *, CONTEXT_S *, MAILSTREAM *, unsigned long);
+int select_by_current(struct pine *, MSGNO_S *, CmdWhere);
+int apply_command(struct pine *, MAILSTREAM *, MSGNO_S *, UCS, int, int);
+char **choose_list_of_keywords(void);
+char *choose_a_charset(int);
+char **choose_list_of_charsets(void);
+char *choose_item_from_list(char **, char **, char *, char *, HelpType, char *, char *);
+int display_folder_list(CONTEXT_S **, char *, int,
+ int (*)(struct pine *, CONTEXT_S **, char *, int));
+int file_lister(char *, char *, size_t, char *, size_t, int, int);
+int read_msg_prompt(long, char *);
+void advance_cur_after_delete(struct pine *, MAILSTREAM *, MSGNO_S *, CmdWhere);
+void free_list_sel(LIST_SEL_S **);
+#ifdef _WINDOWS
+int header_mode_callback(int, long);
+int zoom_mode_callback(int, long);
+int any_selected_callback(int, long);
+int flag_callback(int, long);
+MPopup *flag_submenu(MESSAGECACHE *);
+#endif
+
+
+#endif /* PINE_MAILCMD_INCLUDED */
diff --git a/alpine/mailindx.c b/alpine/mailindx.c
new file mode 100644
index 00000000..01e01558
--- /dev/null
+++ b/alpine/mailindx.c
@@ -0,0 +1,3644 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: mailindx.c 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "headers.h"
+#include "mailindx.h"
+#include "mailcmd.h"
+#include "status.h"
+#include "context.h"
+#include "keymenu.h"
+#include "alpine.h"
+#include "help.h"
+#include "radio.h"
+#include "titlebar.h"
+#include "../pith/flag.h"
+#include "../pith/newmail.h"
+#include "../pith/thread.h"
+#include "../pith/conf.h"
+#include "../pith/msgno.h"
+#include "../pith/icache.h"
+#include "../pith/state.h"
+#include "../pith/bitmap.h"
+#include "../pith/news.h"
+#include "../pith/strlst.h"
+#include "../pith/sequence.h"
+#include "../pith/sort.h"
+#include "../pith/hist.h"
+#include "../pith/busy.h"
+#include "../pith/signal.h"
+
+
+struct save_thrdinfo {
+ ICE_S *(*format_index_line)(INDEXDATA_S *);
+ void (*setup_header_widths)(MAILSTREAM *);
+ unsigned viewing_a_thread:1;
+};
+
+
+static OtherMenu what_keymenu = FirstMenu;
+
+struct index_state *current_index_state = NULL;
+
+
+/*
+ * Internal prototypes
+ */
+void index_index_screen(struct pine *);
+void thread_index_screen(struct pine *);
+int update_index(struct pine *, struct index_state *);
+int index_scroll_up(long);
+int index_scroll_down(long);
+int index_scroll_to_pos(long);
+long top_ent_calc(MAILSTREAM *, MSGNO_S *, long, long);
+void reset_index_border(void);
+void redraw_index_body(void);
+int paint_index_line(ICE_S *, int, long, IndexColType, IndexColType, IndexColType,
+ struct entry_state *, int, int);
+void pine_imap_envelope(MAILSTREAM *, unsigned long, ENVELOPE *);
+void index_search(struct pine *, MAILSTREAM *, int, MSGNO_S *);
+#ifdef _WINDOWS
+int index_scroll_callback(int,long);
+int index_gettext_callback(char *, size_t, void **, long *, int *);
+void index_popup(IndexType style, MAILSTREAM *, MSGNO_S *, int);
+char *pcpine_help_index(char *);
+char *pcpine_help_index_simple(char *);
+#endif
+
+
+
+/*----------------------------------------------------------------------
+
+
+ ----*/
+struct key_menu *
+do_index_border(CONTEXT_S *cntxt, char *folder, MAILSTREAM *stream, MSGNO_S *msgmap,
+ IndexType style, int *which_keys, int flags)
+{
+ struct key_menu *km = (style == ThreadIndex)
+ ? &thread_keymenu
+ : (ps_global->mail_stream != stream)
+ ? &simple_index_keymenu
+ : &index_keymenu;
+
+ if(flags & INDX_CLEAR)
+ ClearScreen();
+
+ if(flags & INDX_HEADER)
+ set_titlebar((style == ThreadIndex)
+ /* TRANSLATORS: these are some screen titles */
+ ? _("THREAD INDEX")
+ : (stream == ps_global->mail_stream)
+ ? (style == MsgIndex || style == MultiMsgIndex)
+ ? _("MESSAGE INDEX")
+ : _("ZOOMED MESSAGE INDEX")
+ : (!strcmp(folder, INTERRUPTED_MAIL))
+ ? _("COMPOSE: SELECT INTERRUPTED")
+ : (ps_global->VAR_FORM_FOLDER
+ && !strcmp(ps_global->VAR_FORM_FOLDER, folder))
+ ? _("COMPOSE: SELECT FORM LETTER")
+ : _("COMPOSE: SELECT POSTPONED"),
+ stream, cntxt, folder, msgmap, 1,
+ (style == ThreadIndex) ? ThrdIndex
+ : (THREADING()
+ && sp_viewing_a_thread(stream))
+ ? ThrdMsgNum
+ : MessageNumber,
+ 0, 0, NULL);
+
+ if(flags & INDX_FOOTER) {
+ bitmap_t bitmap;
+ int cmd;
+
+ setbitmap(bitmap);
+
+ if(km == &index_keymenu){
+ if(THREADING() && sp_viewing_a_thread(stream)){
+ menu_init_binding(km, '<', MC_THRDINDX, "<",
+ N_("ThrdIndex"), BACK_KEY);
+ menu_add_binding(km, ',', MC_THRDINDX);
+ }
+ else{
+ menu_init_binding(km, '<', MC_FOLDERS, "<",
+ N_("FldrList"), BACK_KEY);
+ menu_add_binding(km, ',', MC_FOLDERS);
+ }
+ if(F_OFF(F_ENABLE_PIPE,ps_global))
+ clrbitn(VIEW_PIPE_KEY, bitmap); /* always clear for DOS */
+ if(F_OFF(F_ENABLE_FULL_HDR,ps_global))
+ clrbitn(VIEW_FULL_HEADERS_KEY, bitmap);
+ if(F_OFF(F_ENABLE_BOUNCE,ps_global))
+ clrbitn(BOUNCE_KEY, bitmap);
+ if(F_OFF(F_ENABLE_FLAG,ps_global))
+ clrbitn(FLAG_KEY, bitmap);
+ if(F_OFF(F_ENABLE_AGG_OPS,ps_global)){
+ clrbitn(SELECT_KEY, bitmap);
+ clrbitn(APPLY_KEY, bitmap);
+ clrbitn(SELCUR_KEY, bitmap);
+ if(style != ZoomIndex)
+ clrbitn(ZOOM_KEY, bitmap);
+
+ }
+
+ if(style == MultiMsgIndex){
+ clrbitn(PREVM_KEY, bitmap);
+ clrbitn(NEXTM_KEY, bitmap);
+ }
+ }
+
+ if(km == &index_keymenu || km == &thread_keymenu){
+ if(IS_NEWS(stream)){
+ km->keys[EXCLUDE_KEY].label = N_("eXclude");
+ KS_OSDATASET(&km->keys[EXCLUDE_KEY], KS_NONE);
+ }
+ else {
+ clrbitn(UNEXCLUDE_KEY, bitmap);
+ km->keys[EXCLUDE_KEY].label = N_("eXpunge");
+ KS_OSDATASET(&km->keys[EXCLUDE_KEY], KS_EXPUNGE);
+ }
+ }
+
+ if(km != &simple_index_keymenu && !THRD_COLLAPSE_ENABLE())
+ clrbitn(COLLAPSE_KEY, bitmap);
+
+ menu_clear_binding(km, KEY_LEFT);
+ menu_clear_binding(km, KEY_RIGHT);
+ if(F_ON(F_ARROW_NAV, ps_global)){
+ if((cmd = menu_clear_binding(km, '<')) != MC_UNKNOWN){
+ menu_add_binding(km, '<', cmd);
+ menu_add_binding(km, KEY_LEFT, cmd);
+ }
+
+ if((cmd = menu_clear_binding(km, '>')) != MC_UNKNOWN){
+ menu_add_binding(km, '>', cmd);
+ menu_add_binding(km, KEY_RIGHT, cmd);
+ }
+ }
+
+ if(menu_binding_index(km, MC_JUMP) >= 0){
+ for(cmd = 0; cmd < 10; cmd++)
+ if(F_ON(F_ENABLE_JUMP, ps_global))
+ (void) menu_add_binding(km, '0' + cmd, MC_JUMP);
+ else
+ (void) menu_clear_binding(km, '0' + cmd);
+ }
+
+ draw_keymenu(km, bitmap, ps_global->ttyo->screen_cols,
+ 1-FOOTER_ROWS(ps_global), 0, what_keymenu);
+ what_keymenu = SameMenu;
+ if(which_keys)
+ *which_keys = km->which; /* pass back to caller */
+ }
+
+ return(km);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Main loop executing commands for the mail index screen
+
+ Args: state -- the pine_state structure for next/prev screen pointers
+ and to pass to the index manager...
+ ----*/
+
+void
+mail_index_screen(struct pine *state)
+{
+ if(!state->mail_stream) {
+ q_status_message(SM_ORDER, 0, 3, _("No folder is currently open"));
+ state->prev_screen = mail_index_screen;
+ state->next_screen = main_menu_screen;
+ return;
+ }
+
+ state->prev_screen = mail_index_screen;
+ state->next_screen = SCREEN_FUN_NULL;
+
+ if(THRD_AUTO_VIEW()
+ && sp_viewing_a_thread(state->mail_stream)
+ && state->view_skipped_index
+ && unview_thread(state, state->mail_stream, state->msgmap)){
+ state->next_screen = mail_index_screen;
+ state->view_skipped_index = 0;
+ state->mangled_screen = 1;
+ }
+
+ adjust_cur_to_visible(state->mail_stream, state->msgmap);
+
+ if(THRD_INDX())
+ thread_index_screen(state);
+ else
+ index_index_screen(state);
+}
+
+
+void
+index_index_screen(struct pine *state)
+{
+ dprint((1, "\n\n ---- MAIL INDEX ----\n"));
+
+ setup_for_index_index_screen();
+
+ index_lister(state, state->context_current, state->cur_folder,
+ state->mail_stream, state->msgmap);
+}
+
+
+void
+thread_index_screen(struct pine *state)
+{
+ dprint((1, "\n\n ---- THREAD INDEX ----\n"));
+
+ setup_for_thread_index_screen();
+
+ index_lister(state, state->context_current, state->cur_folder,
+ state->mail_stream, state->msgmap);
+}
+
+
+void *
+stop_threading_temporarily(void)
+{
+ struct save_thrdinfo *ti;
+
+ ps_global->turn_off_threading_temporarily = 1;
+
+ ti = (struct save_thrdinfo *) fs_get(sizeof(*ti));
+ ti->format_index_line = format_index_line;
+ ti->setup_header_widths = setup_header_widths;
+ ti->viewing_a_thread = sp_viewing_a_thread(ps_global->mail_stream);
+
+ setup_for_index_index_screen();
+
+ return((void *) ti);
+}
+
+
+void
+restore_threading(void **p)
+{
+ struct save_thrdinfo *ti;
+
+ ps_global->turn_off_threading_temporarily = 0;
+
+ if(p && *p){
+ ti = (struct save_thrdinfo *) (*p);
+ format_index_line = ti->format_index_line;
+ setup_header_widths = ti->setup_header_widths;
+ sp_set_viewing_a_thread(ps_global->mail_stream, ti->viewing_a_thread);
+
+ fs_give(p);
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Main loop executing commands for the mail index screen
+
+ Args: state -- pine_state structure for display flags and such
+ msgmap -- c-client/pine message number mapping struct
+ ----*/
+
+int
+index_lister(struct pine *state, CONTEXT_S *cntxt, char *folder, MAILSTREAM *stream, MSGNO_S *msgmap)
+{
+ UCS ch;
+ int cmd, which_keys, force,
+ cur_row, cur_col, km_popped, paint_status;
+ static int old_day = -1;
+ long i, j, k, old_max_msgno;
+ char *utf8str;
+ IndexType style, old_style = MsgIndex;
+ struct index_state id;
+ struct key_menu *km = NULL;
+
+
+ dprint((1, "\n\n ---- INDEX MANAGER ----\n"));
+
+ ch = 'x'; /* For displaying msg 1st time thru */
+ force = 0;
+ km_popped = 0;
+ state->mangled_screen = 1;
+ what_keymenu = FirstMenu;
+ old_max_msgno = mn_get_total(msgmap);
+ memset((void *)&id, 0, sizeof(struct index_state));
+ current_index_state = &id;
+ id.msgmap = msgmap;
+ if(msgmap->top != 0L)
+ id.msg_at_top = msgmap->top;
+
+ id.stream = stream;
+ set_need_format_setup(stream);
+
+ while (1) {
+ ps_global->user_says_cancel = 0;
+
+ if(km_popped){
+ km_popped--;
+ if(km_popped == 0){
+ clearfooter(state);
+ if(!state->mangled_body
+ && id.entry_state
+ && id.lines_per_page > 1){
+ id.entry_state[id.lines_per_page-2].id = 0;
+ id.entry_state[id.lines_per_page-1].id = 0;
+ }
+ else
+ state->mangled_body = 1;
+ }
+ }
+
+ /*------- Check for new mail -------*/
+ new_mail(force, NM_TIMING(ch), NM_STATUS_MSG);
+ force = 0; /* may not need to next time around */
+
+ /*
+ * If the width of the message number field in the display changes
+ * we need to flush the cache and redraw. When the cache is cleared
+ * the widths are recalculated, taking into account the max msgno.
+ */
+
+ if(format_includes_msgno(stream) &&
+ ((old_max_msgno < 1000L && mn_get_total(msgmap) >= 1000L)
+ || (old_max_msgno < 10000L && mn_get_total(msgmap) >= 10000L)
+ || (old_max_msgno < 100000L && mn_get_total(msgmap) >= 100000L))){
+ clear_index_cache(stream, IC_CLEAR_WIDTHS_DONE);
+ state->mangled_body = 1;
+ }
+
+ old_max_msgno = mn_get_total(msgmap);
+
+ /*
+ * If the display includes the SMARTDATE ("Today", "Yesterday", ...)
+ * then when the day changes the date column will change. All of the
+ * Today's will become Yesterday's at midnight. So we have to
+ * clear the cache at midnight.
+ */
+ if(format_includes_smartdate(stream)){
+ char db[200];
+ struct date nnow;
+
+ rfc822_date(db);
+ parse_date(db, &nnow);
+ if(old_day != -1 && nnow.day != old_day){
+ clear_index_cache(stream, 0);
+ state->mangled_body = 1;
+ }
+
+ old_day = nnow.day;
+ }
+
+ if(streams_died())
+ state->mangled_header = 1;
+
+ if(state->mangled_screen){
+ state->mangled_header = 1;
+ state->mangled_body = 1;
+ state->mangled_footer = 1;
+ state->mangled_screen = 0;
+ }
+
+ /*
+ * events may have occured that require us to shift from
+ * mode to another...
+ */
+ style = THRD_INDX()
+ ? ThreadIndex
+ : (any_lflagged(msgmap, MN_HIDE))
+ ? ZoomIndex
+ : (mn_total_cur(msgmap) > 1L) ? MultiMsgIndex : MsgIndex;
+ if(style != old_style){
+ state->mangled_header = 1;
+ state->mangled_footer = 1;
+ old_style = style;
+ if(!(style == ThreadIndex || old_style == ThreadIndex))
+ id.msg_at_top = 0L;
+ }
+
+ /*------------ Update the title bar -----------*/
+ if(state->mangled_header) {
+ km = do_index_border(cntxt, folder, stream, msgmap,
+ style, NULL, INDX_HEADER);
+ state->mangled_header = 0;
+ paint_status = 0;
+ }
+ else if(mn_get_total(msgmap) > 0) {
+ update_titlebar_message();
+ /*
+ * If flags aren't available to update the status,
+ * defer it until after all the fetches associated
+ * with building index lines are done (no extra rtts!)...
+ */
+ paint_status = !update_titlebar_status();
+ }
+
+ current_index_state = &id;
+
+ /*------------ draw the index body ---------------*/
+ cur_row = update_index(state, &id);
+ if(F_OFF(F_SHOW_CURSOR, state)){
+ cur_row = state->ttyo->screen_rows - FOOTER_ROWS(state);
+ cur_col = 0;
+ }
+ else if(id.status_col >= 0)
+ cur_col = MIN(id.status_col, state->ttyo->screen_cols-1);
+
+ ps_global->redrawer = redraw_index_body;
+
+ if(paint_status)
+ (void) update_titlebar_status();
+
+ /*------------ draw the footer/key menus ---------------*/
+ if(state->mangled_footer) {
+ if(!state->painted_footer_on_startup){
+ if(km_popped){
+ FOOTER_ROWS(state) = 3;
+ clearfooter(state);
+ }
+
+ km = do_index_border(cntxt, folder, stream, msgmap, style,
+ &which_keys, INDX_FOOTER);
+ if(km_popped){
+ FOOTER_ROWS(state) = 1;
+ mark_keymenu_dirty();
+ }
+ }
+
+ state->mangled_footer = 0;
+ }
+
+ state->painted_body_on_startup = 0;
+ state->painted_footer_on_startup = 0;
+
+ /*-- Display any queued message (eg, new mail, command result --*/
+ if(km_popped){
+ FOOTER_ROWS(state) = 3;
+ mark_status_unknown();
+ }
+
+ display_message(ch);
+ if(km_popped){
+ FOOTER_ROWS(state) = 1;
+ mark_status_unknown();
+ }
+
+ if(F_ON(F_SHOW_CURSOR, state) && cur_row < 0){
+ cur_row = state->ttyo->screen_rows - FOOTER_ROWS(state);
+ }
+
+ cur_row = MIN(MAX(cur_row, 0), state->ttyo->screen_rows-1);
+ MoveCursor(cur_row, cur_col);
+
+ /* Let read_command do the fflush(stdout) */
+
+ /*---------- Read command and validate it ----------------*/
+#ifdef MOUSE
+ mouse_in_content(KEY_MOUSE, -1, -1, 0x5, 0);
+ register_mfunc(mouse_in_content, HEADER_ROWS(ps_global), 0,
+ state->ttyo->screen_rows-(FOOTER_ROWS(ps_global)+1),
+ state->ttyo->screen_cols);
+#endif
+#ifdef _WINDOWS
+ mswin_setscrollcallback (index_scroll_callback);
+ mswin_sethelptextcallback((stream == state->mail_stream)
+ ? pcpine_help_index
+ : pcpine_help_index_simple);
+ mswin_setviewinwindcallback(view_in_new_window);
+#endif
+ ch = READ_COMMAND(&utf8str);
+#ifdef MOUSE
+ clear_mfunc(mouse_in_content);
+#endif
+#ifdef _WINDOWS
+ mswin_setscrollcallback(NULL);
+ mswin_sethelptextcallback(NULL);
+ mswin_setviewinwindcallback(NULL);
+#endif
+
+ cmd = menu_command(ch, km);
+
+ if(km_popped)
+ switch(cmd){
+ case MC_NONE :
+ case MC_OTHER :
+ case MC_RESIZE :
+ case MC_REPAINT :
+ km_popped++;
+ break;
+
+ default:
+ clearfooter(state);
+ break;
+ }
+
+ /*----------- Execute the command ------------------*/
+ switch(cmd){
+
+ /*---------- Roll keymenu ----------*/
+ case MC_OTHER :
+ if(F_OFF(F_USE_FK, ps_global))
+ warn_other_cmds();
+
+ what_keymenu = NextMenu;
+ state->mangled_footer = 1;
+ break;
+
+
+ /*---------- Scroll line up ----------*/
+ case MC_CHARUP :
+ (void) process_cmd(state, stream, msgmap, MC_PREVITEM,
+ (style == MsgIndex
+ || style == MultiMsgIndex
+ || style == ZoomIndex)
+ ? MsgIndx
+ : (style == ThreadIndex)
+ ? ThrdIndx
+ : View,
+ &force);
+ if(mn_get_cur(msgmap) < (id.msg_at_top + HS_MARGIN(state)))
+ index_scroll_up(1L);
+
+ break;
+
+
+ /*---------- Scroll line down ----------*/
+ case MC_CHARDOWN :
+ /*
+ * Special Page framing handling here. If we
+ * did something that should scroll-by-a-line, frame
+ * the page by hand here rather than leave it to the
+ * page-by-page framing in update_index()...
+ */
+ (void) process_cmd(state, stream, msgmap, MC_NEXTITEM,
+ (style == MsgIndex
+ || style == MultiMsgIndex
+ || style == ZoomIndex)
+ ? MsgIndx
+ : (style == ThreadIndex)
+ ? ThrdIndx
+ : View,
+ &force);
+ for(j = 0L, k = i = id.msg_at_top; ; i++){
+ if(!msgline_hidden(stream, msgmap, i, 0)){
+ k = i;
+ if(j++ >= id.lines_per_page)
+ break;
+ }
+
+ if(i >= mn_get_total(msgmap)){
+ k = 0L; /* don't scroll */
+ break;
+ }
+ }
+
+ if(k && (mn_get_cur(msgmap) + HS_MARGIN(state)) >= k)
+ index_scroll_down(1L);
+
+ break;
+
+
+ /*---------- Scroll page up ----------*/
+ case MC_PAGEUP :
+ j = -1L;
+ for(k = i = id.msg_at_top; ; i--){
+ if(!msgline_hidden(stream, msgmap, i, 0)){
+ k = i;
+ if(++j >= id.lines_per_page){
+ if((id.msg_at_top = i) == 1L)
+ q_status_message(SM_ORDER, 0, 1, _("First Index page"));
+
+ break;
+ }
+ }
+
+ if(i <= 1L){
+ if((!THREADING() && mn_get_cur(msgmap) == 1L)
+ || (THREADING()
+ && mn_get_cur(msgmap) == first_sorted_flagged(F_NONE,
+ stream,
+ 0L,
+ FSF_SKIP_CHID)))
+ q_status_message(SM_ORDER, 0, 1,
+ _("Already at start of Index"));
+
+ break;
+ }
+ }
+
+ if(mn_get_total(msgmap) > 0L && mn_total_cur(msgmap) == 1L)
+ mn_set_cur(msgmap, k);
+
+ break;
+
+
+ /*---------- Scroll page forward ----------*/
+ case MC_PAGEDN :
+ j = -1L;
+ for(k = i = id.msg_at_top; ; i++){
+ if(!msgline_hidden(stream, msgmap, i, 0)){
+ k = i;
+ if(++j >= id.lines_per_page){
+ if(i+id.lines_per_page > mn_get_total(msgmap))
+ q_status_message(SM_ORDER, 0, 1, _("Last Index page"));
+
+ id.msg_at_top = i;
+ break;
+ }
+ }
+
+ if(i >= mn_get_total(msgmap)){
+ if(mn_get_cur(msgmap) == k)
+ q_status_message(SM_ORDER,0,1,_("Already at end of Index"));
+
+ break;
+ }
+ }
+
+ if(mn_get_total(msgmap) > 0L && mn_total_cur(msgmap) == 1L)
+ mn_set_cur(msgmap, k);
+
+ break;
+
+
+ /*---------- Scroll to first page ----------*/
+ case MC_HOMEKEY :
+ if((mn_get_total(msgmap) > 0L)
+ && (mn_total_cur(msgmap) <= 1L)){
+ long cur_msg = mn_get_cur(msgmap), selected;
+
+ if(any_lflagged(msgmap, MN_HIDE | MN_CHID)){
+ do {
+ selected = cur_msg;
+ mn_dec_cur(stream, msgmap, MH_NONE);
+ cur_msg = mn_get_cur(msgmap);
+ }
+ while(selected != cur_msg);
+ }
+ else
+ cur_msg = (mn_get_total(msgmap) > 0L) ? 1L : 0L;
+ mn_set_cur(msgmap, cur_msg);
+ q_status_message(SM_ORDER, 0, 3, _("First Index Page"));
+ }
+ break;
+
+ /*---------- Scroll to last page ----------*/
+ case MC_ENDKEY :
+ if((mn_get_total(msgmap) > 0L)
+ && (mn_total_cur(msgmap) <= 1L)){
+ long cur_msg = mn_get_cur(msgmap), selected;
+
+ if(any_lflagged(msgmap, MN_HIDE | MN_CHID)){
+ do {
+ selected = cur_msg;
+ mn_inc_cur(stream, msgmap, MH_NONE);
+ cur_msg = mn_get_cur(msgmap);
+ }
+ while(selected != cur_msg);
+ }
+ else
+ cur_msg = mn_get_total(msgmap);
+ mn_set_cur(msgmap, cur_msg);
+ q_status_message(SM_ORDER, 0, 3, _("Last Index Page"));
+ }
+ break;
+
+ /*---------- Search (where is command) ----------*/
+ case MC_WHEREIS :
+ index_search(state, stream, -FOOTER_ROWS(ps_global), msgmap);
+ state->mangled_footer = 1;
+ break;
+
+
+ /*-------------- jump command -------------*/
+ /* NOTE: preempt the process_cmd() version because
+ * we need to get at the number..
+ */
+ case MC_JUMP :
+ j = jump_to(msgmap, -FOOTER_ROWS(ps_global), ch, NULL,
+ (style == ThreadIndex) ? ThrdIndx : MsgIndx);
+ if(j > 0L){
+ if(style == ThreadIndex){
+ PINETHRD_S *thrd;
+
+ thrd = find_thread_by_number(stream, msgmap, j, NULL);
+
+ if(thrd && thrd->rawno)
+ mn_set_cur(msgmap, mn_raw2m(msgmap, thrd->rawno));
+ }
+ else{
+ /* jump to message */
+ if(mn_total_cur(msgmap) > 1L){
+ mn_reset_cur(msgmap, j);
+ }
+ else{
+ mn_set_cur(msgmap, j);
+ }
+ }
+
+ id.msg_at_top = 0L;
+ }
+
+ state->mangled_footer = 1;
+ break;
+
+
+ case MC_VIEW_ENTRY : /* only happens in thread index */
+
+ /*
+ * If the feature F_THRD_AUTO_VIEW is turned on and there
+ * is only one message in the thread, then we skip the index
+ * view of the thread and go straight to the message view.
+ */
+view_a_thread:
+ if(THRD_AUTO_VIEW() && style == ThreadIndex){
+ PINETHRD_S *thrd;
+
+ thrd = fetch_thread(stream,
+ mn_m2raw(msgmap, mn_get_cur(msgmap)));
+ if(thrd
+ && (count_lflags_in_thread(stream, thrd,
+ msgmap, MN_NONE) == 1)){
+ if(view_thread(state, stream, msgmap, 1)){
+ state->view_skipped_index = 1;
+ cmd = MC_VIEW_TEXT;
+ goto do_the_default;
+ }
+ }
+ }
+
+ if(view_thread(state, stream, msgmap, 1)){
+ ps_global->next_screen = mail_index_screen;
+ ps_global->redrawer = NULL;
+ current_index_state = NULL;
+ if(id.entry_state)
+ fs_give((void **)&(id.entry_state));
+
+ return(0);
+ }
+
+ break;
+
+
+ case MC_THRDINDX :
+ msgmap->top = msgmap->top_after_thrd;
+ if(unview_thread(state, stream, msgmap)){
+ state->next_screen = mail_index_screen;
+ state->view_skipped_index = 0;
+ state->mangled_screen = 1;
+ ps_global->redrawer = NULL;
+ current_index_state = NULL;
+ if(id.entry_state)
+ fs_give((void **)&(id.entry_state));
+
+ return(0);
+ }
+
+ break;
+
+
+#ifdef MOUSE
+ case MC_MOUSE:
+ {
+ MOUSEPRESS mp;
+ int new_cur;
+
+ mouse_get_last (NULL, &mp);
+ mp.row -= 2;
+
+ for(i = id.msg_at_top;
+ mp.row >= 0 && i <= mn_get_total(msgmap);
+ i++)
+ if(!msgline_hidden(stream, msgmap, i, 0)){
+ mp.row--;
+ new_cur = i;
+ }
+
+ if(mn_get_total(msgmap) && mp.row < 0){
+ switch(mp.button){
+ case M_BUTTON_LEFT :
+ if(mn_total_cur(msgmap) == 1L)
+ mn_set_cur(msgmap, new_cur);
+
+ if(mp.flags & M_KEY_CONTROL){
+ if(F_ON(F_ENABLE_AGG_OPS, ps_global)){
+ (void) select_by_current(state, msgmap, MsgIndx);
+ }
+ }
+ else if(!(mp.flags & M_KEY_SHIFT)){
+ if (THREADING()
+ && mp.col >= 0
+ && mp.col == id.plus_col
+ && style != ThreadIndex){
+ collapse_or_expand(state, stream, msgmap,
+ mn_get_cur(msgmap));
+ }
+ else if (mp.doubleclick){
+ if(mp.button == M_BUTTON_LEFT){
+ if(stream == state->mail_stream){
+ if(THRD_INDX()){
+ cmd = MC_VIEW_ENTRY;
+ goto view_a_thread;
+ }
+ else{
+ cmd = MC_VIEW_TEXT;
+ goto do_the_default;
+ }
+ }
+
+ ps_global->redrawer = NULL;
+ current_index_state = NULL;
+ if(id.entry_state)
+ fs_give((void **)&(id.entry_state));
+
+ return(0);
+ }
+ }
+ }
+
+ break;
+
+ case M_BUTTON_MIDDLE:
+ break;
+
+ case M_BUTTON_RIGHT :
+#ifdef _WINDOWS
+ if (!mp.doubleclick){
+ if(mn_total_cur(msgmap) == 1L)
+ mn_set_cur(msgmap, new_cur);
+
+ cur_row = update_index(state, &id);
+
+ index_popup(style, stream, msgmap, TRUE);
+ }
+#endif
+ break;
+ }
+ }
+ else{
+ switch(mp.button){
+ case M_BUTTON_LEFT :
+ break;
+
+ case M_BUTTON_MIDDLE :
+ break;
+
+ case M_BUTTON_RIGHT :
+#ifdef _WINDOWS
+ index_popup(style, stream, msgmap, FALSE);
+#endif
+ break;
+ }
+ }
+ }
+
+ break;
+#endif /* MOUSE */
+
+ /*---------- Resize ----------*/
+ case MC_RESIZE:
+ /*
+ * If we were smarter we could do the
+ * IC_CLEAR_WIDTHS_DONE trick here. The problem is
+ * that entire columns of the format can go away or
+ * appear because the width gets smaller or larger,
+ * so in that case we need to re-do. If we could tell
+ * when that happened or not we could set the flag
+ * selectively.
+ */
+ clear_index_cache(stream, 0);
+ reset_index_border();
+ break;
+
+ case MC_QUOTA:
+ cmd_quota(state);
+
+ /*---------- Redraw ----------*/
+ case MC_REPAINT :
+ force = 1; /* check for new mail! */
+ reset_index_border();
+ break;
+
+
+ /*---------- No op command ----------*/
+ case MC_NONE :
+ break; /* no op check for new mail */
+
+
+ /*--------- keystroke not bound to command --------*/
+ case MC_CHARRIGHT :
+ case MC_CHARLEFT :
+ case MC_GOTOBOL :
+ case MC_GOTOEOL :
+ case MC_UNKNOWN :
+ if(cmd == MC_UNKNOWN && (ch == 'i' || ch == 'I'))
+ q_status_message(SM_ORDER, 0, 1, "Already in Index");
+ else
+ bogus_command(ch, F_ON(F_USE_FK,state) ? "F1" : "?");
+
+ break;
+
+
+ case MC_COLLAPSE :
+ thread_command(state, stream, msgmap, ch, -FOOTER_ROWS(state));
+ break;
+
+ case MC_DELETE :
+ case MC_UNDELETE :
+ case MC_REPLY :
+ case MC_FORWARD :
+ case MC_TAKE :
+ case MC_SAVE :
+ case MC_EXPORT :
+ case MC_BOUNCE :
+ case MC_PIPE :
+ case MC_FLAG :
+ case MC_SELCUR :
+ { int collapsed = 0;
+ unsigned long rawno;
+ PINETHRD_S *thrd = NULL;
+
+ if(THREADING()){
+ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
+ if(rawno)
+ thrd = fetch_thread(stream, rawno);
+
+ collapsed = thrd && thrd->next
+ && get_lflag(stream, NULL, rawno, MN_COLL);
+ }
+
+ if(collapsed){
+ thread_command(state, stream, msgmap,
+ ch, -FOOTER_ROWS(state));
+ /* increment current */
+ if(cmd == MC_DELETE){
+ advance_cur_after_delete(state, stream, msgmap,
+ (style == MsgIndex
+ || style == MultiMsgIndex
+ || style == ZoomIndex)
+ ? MsgIndx
+ : (style == ThreadIndex)
+ ? ThrdIndx
+ : View);
+ }
+ else if((cmd == MC_SELCUR
+ && (state->ugly_consider_advancing_bit
+ || F_OFF(F_UNSELECT_WONT_ADVANCE, state)))
+ || (state->ugly_consider_advancing_bit
+ && cmd == MC_SAVE
+ && F_ON(F_SAVE_ADVANCES, state))){
+ mn_inc_cur(stream, msgmap, MH_NONE);
+ }
+ }
+ else
+ goto do_the_default;
+ }
+
+ break;
+
+
+ case MC_UTF8:
+ bogus_utf8_command(utf8str, NULL);
+ break;
+
+
+ /*---------- First HELP command with menu hidden ----------*/
+ case MC_HELP :
+ if(FOOTER_ROWS(state) == 1 && km_popped == 0){
+ km_popped = 2;
+ mark_status_unknown();
+ mark_keymenu_dirty();
+ state->mangled_footer = 1;
+ break;
+ }
+ /* else fall thru to normal default */
+
+
+ /*---------- Default -- all other command ----------*/
+ default:
+ do_the_default:
+ if(stream == state->mail_stream){
+ msgmap->top = id.msg_at_top;
+ process_cmd(state, stream, msgmap, cmd,
+ (style == MsgIndex
+ || style == MultiMsgIndex
+ || style == ZoomIndex)
+ ? MsgIndx
+ : (style == ThreadIndex)
+ ? ThrdIndx
+ : View,
+ &force);
+ if(state->next_screen != SCREEN_FUN_NULL){
+ ps_global->redrawer = NULL;
+ current_index_state = NULL;
+ if(id.entry_state)
+ fs_give((void **)&(id.entry_state));
+
+ return(0);
+ }
+ else{
+ if(stream != state->mail_stream){
+ /*
+ * Must have had an failed open. repair our
+ * pointers...
+ */
+ id.stream = stream = state->mail_stream;
+ id.msgmap = msgmap = state->msgmap;
+ }
+
+ current_index_state = &id;
+
+ if(cmd == MC_ZOOM && THRD_INDX())
+ id.msg_at_top = 0L;
+ }
+ }
+ else{ /* special processing */
+ switch(cmd){
+ case MC_HELP :
+ helper(h_simple_index,
+ (!strcmp(folder, INTERRUPTED_MAIL))
+ ? _("HELP FOR SELECTING INTERRUPTED MSG")
+ : _("HELP FOR SELECTING POSTPONED MSG"),
+ HLPD_SIMPLE);
+ state->mangled_screen = 1;
+ break;
+
+ case MC_DELETE : /* delete */
+ dprint((3, "Special delete: msg %s\n",
+ long2string(mn_get_cur(msgmap))));
+ {
+ long raw, t;
+ int del = 0;
+ MESSAGECACHE *mc;
+
+ raw = mn_m2raw(msgmap, mn_get_cur(msgmap));
+ if(raw > 0L && stream
+ && raw <= stream->nmsgs
+ && (mc = mail_elt(stream, raw))
+ && !mc->deleted){
+ if((t = mn_get_cur(msgmap)) > 0L)
+ clear_index_cache_ent(stream, t, 0);
+
+ mail_setflag(stream,long2string(raw),"\\DELETED");
+ update_titlebar_status();
+ del++;
+ }
+
+ q_status_message1(SM_ORDER, 0, 1,
+ del ? _("Message %s deleted") : _("Message %s already deleted"),
+ long2string(mn_get_cur(msgmap)));
+ }
+
+ break;
+
+ case MC_UNDELETE : /* UNdelete */
+ dprint((3, "Special UNdelete: msg %s\n",
+ long2string(mn_get_cur(msgmap))));
+ {
+ long raw, t;
+ int del = 0;
+ MESSAGECACHE *mc;
+
+ raw = mn_m2raw(msgmap, mn_get_cur(msgmap));
+ if(raw > 0L && stream
+ && raw <= stream->nmsgs
+ && (mc = mail_elt(stream, raw))
+ && mc->deleted){
+ if((t = mn_get_cur(msgmap)) > 0L)
+ clear_index_cache_ent(stream, t, 0);
+
+ mail_clearflag(stream, long2string(raw),
+ "\\DELETED");
+ update_titlebar_status();
+ del++;
+ }
+
+ q_status_message1(SM_ORDER, 0, 1,
+ del ? _("Message %s UNdeleted") : _("Message %s NOT deleted"),
+ long2string(mn_get_cur(msgmap)));
+ }
+
+ break;
+
+ case MC_EXIT : /* exit */
+ ps_global->redrawer = NULL;
+ current_index_state = NULL;
+ if(id.entry_state)
+ fs_give((void **)&(id.entry_state));
+
+ return(1);
+
+ case MC_SELECT : /* select */
+ ps_global->redrawer = NULL;
+ current_index_state = NULL;
+ if(id.entry_state)
+ fs_give((void **)&(id.entry_state));
+
+ return(0);
+
+ case MC_PREVITEM : /* previous */
+ mn_dec_cur(stream, msgmap, MH_NONE);
+ break;
+
+ case MC_NEXTITEM : /* next */
+ mn_inc_cur(stream, msgmap, MH_NONE);
+ break;
+
+ default :
+ bogus_command(ch, NULL);
+ break;
+ }
+ }
+ } /* The big switch */
+ } /* the BIG while loop! */
+}
+
+
+
+/*----------------------------------------------------------------------
+ Manage index body painting
+
+ Args: state - pine struct containing selected message data
+ index_state - struct describing what's currently displayed
+
+ Returns: screen row number of first highlighted message
+
+ The idea is pretty simple. Maintain an array of index line id's that
+ are displayed and their hilited state. Decide what's to be displayed
+ and update the screen appropriately. All index screen painting
+ is done here. Pretty simple, huh?
+ ----*/
+int
+update_index(struct pine *state, struct index_state *screen)
+{
+ int i, retval = -1, row, already_fetched = 0;
+ long n, visible;
+ PINETHRD_S *thrd = NULL;
+ int we_cancel = 0;
+
+ dprint((7, "--update_index--\n"));
+
+ if(!screen)
+ return(-1);
+
+#ifdef _WINDOWS
+ mswin_beginupdate();
+#endif
+
+ /*---- reset the works if necessary ----*/
+ if(state->mangled_body){
+ ClearBody();
+ if(screen->entry_state){
+ fs_give((void **)&(screen->entry_state));
+ screen->lines_per_page = 0;
+ }
+ }
+
+ state->mangled_body = 0;
+
+ /*---- make sure we have a place to write state ----*/
+ if(screen->lines_per_page
+ != MAX(0, state->ttyo->screen_rows - FOOTER_ROWS(state)
+ - HEADER_ROWS(state))){
+ i = screen->lines_per_page;
+ screen->lines_per_page
+ = MAX(0, state->ttyo->screen_rows - FOOTER_ROWS(state)
+ - HEADER_ROWS(state));
+ if(!i){
+ size_t len = screen->lines_per_page * sizeof(struct entry_state);
+ screen->entry_state = (struct entry_state *) fs_get(len);
+ }
+ else
+ fs_resize((void **)&(screen->entry_state),
+ (size_t)screen->lines_per_page);
+
+ for(; i < screen->lines_per_page; i++) /* init new entries */
+ memset(&screen->entry_state[i], 0, sizeof(struct entry_state));
+ }
+
+ /*---- figure out the first message on the display ----*/
+ if(screen->msg_at_top < 1L
+ || msgline_hidden(screen->stream, screen->msgmap, screen->msg_at_top,0)){
+ screen->msg_at_top = top_ent_calc(screen->stream, screen->msgmap,
+ screen->msg_at_top,
+ screen->lines_per_page);
+ }
+ else if(mn_get_cur(screen->msgmap) < screen->msg_at_top){
+ long i, j, k;
+
+ /* scroll back a page at a time until current is displayed */
+ while(mn_get_cur(screen->msgmap) < screen->msg_at_top){
+ for(i = screen->lines_per_page, j = screen->msg_at_top-1L, k = 0L;
+ i > 0L && j > 0L;
+ j--)
+ if(!msgline_hidden(screen->stream, screen->msgmap, j, 0)){
+ k = j;
+ i--;
+ }
+
+ if(i == screen->lines_per_page)
+ break; /* can't scroll back ? */
+ else
+ screen->msg_at_top = k;
+ }
+ }
+ else if(mn_get_cur(screen->msgmap) >= screen->msg_at_top
+ + screen->lines_per_page){
+ long i, j, k;
+
+ while(1){
+ for(i = screen->lines_per_page, j = k = screen->msg_at_top;
+ j <= mn_get_total(screen->msgmap) && i > 0L;
+ j++)
+ if(!msgline_hidden(screen->stream, screen->msgmap, j, 0)){
+ k = j;
+ i--;
+ if(mn_get_cur(screen->msgmap) <= k)
+ break;
+ }
+
+ if(mn_get_cur(screen->msgmap) <= k)
+ break;
+ else{
+ /* set msg_at_top to next displayed message */
+ for(i = k + 1L; i <= mn_get_total(screen->msgmap); i++)
+ if(!msgline_hidden(screen->stream, screen->msgmap, i, 0)){
+ k = i;
+ break;
+ }
+
+ screen->msg_at_top = k;
+ }
+ }
+ }
+
+#ifdef _WINDOWS
+ /*
+ * Set scroll range and position. Note that message numbers start at 1
+ * while scroll position starts at 0.
+ */
+
+ if(THREADING() && sp_viewing_a_thread(screen->stream)
+ && mn_get_total(screen->msgmap) > 1L){
+ long x = 0L, range = 0L, lowest_numbered_msg;
+
+ /*
+ * We know that all visible messages in the thread are marked
+ * with MN_CHID2.
+ */
+ thrd = fetch_thread(screen->stream,
+ mn_m2raw(screen->msgmap,
+ mn_get_cur(screen->msgmap)));
+ if(thrd && thrd->top && thrd->top != thrd->rawno)
+ thrd = fetch_thread(screen->stream, thrd->top);
+
+ if(thrd){
+ if(mn_get_revsort(screen->msgmap)){
+ n = mn_raw2m(screen->msgmap, thrd->rawno);
+ while(n > 1L && get_lflag(screen->stream, screen->msgmap,
+ n-1L, MN_CHID2))
+ n--;
+
+ lowest_numbered_msg = n;
+ }
+ else
+ lowest_numbered_msg = mn_raw2m(screen->msgmap, thrd->rawno);
+ }
+
+ if(thrd){
+ n = lowest_numbered_msg;
+ for(; n <= mn_get_total(screen->msgmap); n++){
+
+ if(!get_lflag(screen->stream, screen->msgmap, n, MN_CHID2))
+ break;
+
+ if(!msgline_hidden(screen->stream, screen->msgmap, n, 0)){
+ range++;
+ if(n < screen->msg_at_top)
+ x++;
+ }
+ }
+ }
+
+ scroll_setrange(screen->lines_per_page, range-1L);
+ scroll_setpos(x);
+ }
+ else if(THRD_INDX()){
+ if(any_lflagged(screen->msgmap, MN_HIDE)){
+ long x = 0L, range;
+
+ range = screen->msgmap->visible_threads - 1L;
+ scroll_setrange(screen->lines_per_page, range);
+ if(range >= screen->lines_per_page){ /* else not needed */
+ PINETHRD_S *topthrd;
+ int thrddir;
+ long xdir;
+
+ /* find top of currently displayed top line */
+ topthrd = fetch_thread(screen->stream,
+ mn_m2raw(screen->msgmap,
+ screen->msg_at_top));
+ if(topthrd && topthrd->top != topthrd->rawno)
+ topthrd = fetch_thread(screen->stream, topthrd->top);
+
+ if(topthrd){
+ /*
+ * Split into two halves to speed up finding scroll pos.
+ * It's tricky because the thread list always goes from
+ * past to future but the thrdno's will be reversed if
+ * the sort is reversed and of course the order on the
+ * screen will be reversed.
+ */
+ if((!mn_get_revsort(screen->msgmap)
+ && topthrd->thrdno <= screen->msgmap->max_thrdno/2)
+ ||
+ (mn_get_revsort(screen->msgmap)
+ && topthrd->thrdno > screen->msgmap->max_thrdno/2)){
+
+ /* start with head of thread list */
+ if(topthrd && topthrd->head)
+ thrd = fetch_thread(screen->stream, topthrd->head);
+ else
+ thrd = NULL;
+
+ thrddir = 1;
+ }
+ else{
+ long tailrawno;
+
+ /*
+ * Start with tail thread and work back.
+ */
+ if(mn_get_revsort(screen->msgmap))
+ tailrawno = mn_m2raw(screen->msgmap, 1L);
+ else
+ tailrawno = mn_m2raw(screen->msgmap,
+ mn_get_total(screen->msgmap));
+
+ thrd = fetch_thread(screen->stream, tailrawno);
+ if(thrd && thrd->top && thrd->top != thrd->rawno)
+ thrd = fetch_thread(screen->stream, thrd->top);
+
+ thrddir = -1;
+ }
+
+ /*
+ * x is the scroll position. We try to use the fewest
+ * number of steps to find it, so we start with either
+ * the beginning or the end.
+ */
+ if(topthrd->thrdno <= screen->msgmap->max_thrdno/2){
+ x = 0L;
+ xdir = 1L;
+ }
+ else{
+ x = range;
+ xdir = -1L;
+ }
+
+ while(thrd && thrd != topthrd){
+ if(!msgline_hidden(screen->stream, screen->msgmap,
+ mn_raw2m(screen->msgmap,thrd->rawno),
+ 0))
+ x += xdir;
+
+ if(thrddir > 0 && thrd->nextthd)
+ thrd = fetch_thread(screen->stream, thrd->nextthd);
+ else if(thrddir < 0 && thrd->prevthd)
+ thrd = fetch_thread(screen->stream, thrd->prevthd);
+ else
+ thrd = NULL;
+ }
+ }
+
+ scroll_setpos(x);
+ }
+ }
+ else{
+ /*
+ * This works for forward or reverse sort because the thrdno's
+ * will have been reversed.
+ */
+ thrd = fetch_thread(screen->stream,
+ mn_m2raw(screen->msgmap, screen->msg_at_top));
+ if(thrd){
+ scroll_setrange(screen->lines_per_page,
+ screen->msgmap->max_thrdno - 1L);
+ scroll_setpos(thrd->thrdno - 1L);
+ }
+ }
+ }
+ else if(n = any_lflagged(screen->msgmap, MN_HIDE | MN_CHID)){
+ long x, range;
+
+ range = mn_get_total(screen->msgmap) - n - 1L;
+ scroll_setrange(screen->lines_per_page, range);
+
+ if(range >= screen->lines_per_page){ /* else not needed */
+ if(screen->msg_at_top < mn_get_total(screen->msgmap) / 2){
+ for(n = 1, x = 0; n != screen->msg_at_top; n++)
+ if(!msgline_hidden(screen->stream, screen->msgmap, n, 0))
+ x++;
+ }
+ else{
+ for(n = mn_get_total(screen->msgmap), x = range;
+ n != screen->msg_at_top; n--)
+ if(!msgline_hidden(screen->stream, screen->msgmap, n, 0))
+ x--;
+ }
+
+ scroll_setpos(x);
+ }
+ }
+ else{
+ scroll_setrange(screen->lines_per_page,
+ mn_get_total(screen->msgmap) - 1L);
+ scroll_setpos(screen->msg_at_top - 1L);
+ }
+#endif
+
+ /*
+ * Set up c-client call back to tell us about IMAP envelope arrivals
+ * Can't do it (easily) if single lines on the screen need information
+ * about more than a single message before they can be drawn.
+ */
+ if(F_OFF(F_QUELL_IMAP_ENV_CB, ps_global) && !THRD_INDX()
+ && !(THREADING() && (sp_viewing_a_thread(screen->stream)
+ || ps_global->thread_disp_style == THREAD_MUTTLIKE
+ || any_lflagged(screen->msgmap, MN_COLL))))
+ mail_parameters(NULL, SET_IMAPENVELOPE, (void *) pine_imap_envelope);
+
+ if(THRD_INDX())
+ visible = screen->msgmap->visible_threads;
+ else if(THREADING() && sp_viewing_a_thread(screen->stream)){
+ /*
+ * We know that all visible messages in the thread are marked
+ * with MN_CHID2.
+ */
+ for(visible = 0L, n = screen->msg_at_top;
+ visible < (int) screen->lines_per_page
+ && n <= mn_get_total(screen->msgmap); n++){
+
+ if(!get_lflag(screen->stream, screen->msgmap, n, MN_CHID2))
+ break;
+
+ if(!msgline_hidden(screen->stream, screen->msgmap, n, 0))
+ visible++;
+ }
+ }
+ else
+ visible = mn_get_total(screen->msgmap)
+ - any_lflagged(screen->msgmap, MN_HIDE|MN_CHID);
+
+ /*---- march thru display lines, painting whatever is needed ----*/
+ for(i = 0, n = screen->msg_at_top; i < (int) screen->lines_per_page; i++){
+ if(visible == 0L || n < 1 || n > mn_get_total(screen->msgmap)){
+ if(screen->entry_state[i].id != LINE_HASH_N){
+ screen->entry_state[i].hilite = 0;
+ screen->entry_state[i].bolded = 0;
+ screen->entry_state[i].msgno = 0L;
+ screen->entry_state[i].id = LINE_HASH_N;
+ ClearLine(HEADER_ROWS(state) + i);
+ }
+ }
+ else{
+ ICE_S *ice;
+
+ /*
+ * This changes status_col as a side effect so it has to be
+ * executed before next line.
+ */
+ ice = build_header_line(state, screen->stream, screen->msgmap,
+ n, &already_fetched);
+ if(visible > 0L)
+ visible--;
+
+ if(THRD_INDX()){
+ unsigned long rawno;
+
+ rawno = mn_m2raw(screen->msgmap, n);
+ if(rawno)
+ thrd = fetch_thread(screen->stream, rawno);
+ }
+
+ row = paint_index_line(ice, i,
+ (THRD_INDX() && thrd) ? thrd->thrdno : n,
+ screen->status_fld, screen->plus_fld,
+ screen->arrow_fld, &screen->entry_state[i],
+ mn_is_cur(screen->msgmap, n),
+ THRD_INDX()
+ ? (count_lflags_in_thread(screen->stream,
+ thrd,
+ screen->msgmap,
+ MN_SLCT) > 0)
+ : get_lflag(screen->stream, screen->msgmap,
+ n, MN_SLCT));
+ fflush(stdout);
+ if(row && retval < 0)
+ retval = row;
+ }
+
+ /*--- increment n ---*/
+ while((visible == -1L || visible > 0L)
+ && ++n <= mn_get_total(screen->msgmap)
+ && msgline_hidden(screen->stream, screen->msgmap, n, 0))
+ ;
+ }
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ mail_parameters(NULL, SET_IMAPENVELOPE, (void *) NULL);
+
+#ifdef _WINDOWS
+ mswin_endupdate();
+#endif
+ fflush(stdout);
+ dprint((7, "--update_index done\n"));
+ return(retval);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Create a string summarizing the message header for index on screen
+
+ Args: stream -- mail stream to fetch envelope info from
+ msgmap -- message number to pine sort mapping
+ msgno -- Message number to create line for
+
+ Result: returns a malloced string
+ saves string in a cache for next call for same header
+ ----*/
+ICE_S *
+build_header_line(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, long int msgno, int *already_fetched)
+{
+ return(build_header_work(state, stream, msgmap, msgno,
+ current_index_state->msg_at_top,
+ current_index_state->lines_per_page,
+ already_fetched));
+}
+
+
+/*----------------------------------------------------------------------
+ Paint the given index line
+
+
+ Args: ice -- index cache entry
+ line -- index line number on screen, starting at 0 for first
+ visible line, 1, 2, ...
+ msgno -- for painting the message number field
+ sfld -- field type of the status field, which is
+ where we'll put the X for selected if necessary
+ pfld -- field type where the thread indicator symbol goes
+ afld -- field type of column which corresponds to the
+ index-format ARROW token
+ entry -- cache used to help us decide whether or not we need to
+ redraw the index line or if we can just leave it alone because
+ we know it is already correct
+ cur -- is this the current message?
+ sel -- is this message in the selected set?
+
+ Returns: screen row number if this is current message, else 0
+ ----*/
+int
+paint_index_line(ICE_S *argice, int line, long int msgno, IndexColType sfld,
+ IndexColType pfld, IndexColType afld, struct entry_state *entry,
+ int cur, int sel)
+{
+ COLOR_PAIR *lastc = NULL, *base_color = NULL;
+ ICE_S *ice;
+ IFIELD_S *ifield, *previfield = NULL;
+ IELEM_S *ielem;
+ int save_schar1 = -1, save_schar2 = -1, save_pchar = -1, i;
+ int draw_whole_line = 0, draw_partial_line = 0;
+ int n = MAX_SCREEN_COLS*6;
+ char draw[MAX_SCREEN_COLS*6+1], *p;
+
+ ice = (THRD_INDX() && argice) ? argice->tice : argice;
+
+ /* This better not happen! */
+ if(!ice){
+ q_status_message3(SM_ORDER | SM_DING, 5, 5,
+ "NULL ice in paint_index_line: %s, msgno=%s line=%s",
+ THRD_INDX() ? "THRD_INDX" : "reg index",
+ comatose(msgno), comatose(line));
+ dprint((1, "NULL ice in paint_index_line: %s, msgno=%ld line=%d\n",
+ THRD_INDX() ? "THRD_INDX" : "reg index",
+ msgno, line));
+ return 0;
+ }
+
+ if(entry->msgno != msgno || ice->id == 0 || entry->id != ice->id){
+ entry->id = 0L;
+ entry->msgno = 0L;
+ draw_whole_line = 1;
+ }
+ else if((cur != entry->hilite) || (sel != entry->bolded)
+ || (ice->plus != entry->plus)){
+ draw_partial_line = 1;
+ }
+
+ if(draw_whole_line || draw_partial_line){
+
+ if(F_ON(F_FORCE_LOW_SPEED,ps_global) || ps_global->low_speed){
+
+ memset(draw, 0, sizeof(draw));
+ p = draw;
+
+ for(ifield = ice->ifield; ifield && p-draw < n; ifield = ifield->next){
+
+ /* space between fields */
+ if(ifield != ice->ifield && !(previfield && previfield->ctype == iText))
+ *p++ = ' ';
+
+ /* message number string is generated on the fly */
+ if(ifield->ctype == iMessNo){
+ ielem = ifield->ielem;
+ if(ielem && ielem->datalen >= ifield->width){
+ snprintf(ielem->data, ielem->datalen+1, "%*.ld", ifield->width, msgno);
+ ielem->data[ifield->width] = '\0';
+ ielem->data[ielem->datalen] = '\0';
+ }
+ }
+
+ if(ifield->ctype == sfld){
+ ielem = ifield->ielem;
+ if(ielem && ielem->datalen > 0){
+ if(draw_partial_line)
+ MoveCursor(HEADER_ROWS(ps_global) + line, utf8_width(draw));
+
+ if(ielem->datalen == 1){
+ save_schar1 = ielem->data[0];
+ ielem->data[0] = (sel) ? 'X' : (cur && save_schar1 == ' ') ? '-' : save_schar1;
+ if(draw_partial_line)
+ Writechar(ielem->data[0], 0);
+
+ if(ielem->next && ielem->next->datalen){
+ save_schar2 = ielem->next->data[0];
+ if(cur)
+ ielem->next->data[0] = '>';
+
+ if(draw_partial_line)
+ Writechar(ielem->next->data[0], 0);
+ }
+ }
+ else if(ielem->datalen > 1){
+ save_schar1 = ielem->data[0];
+ ielem->data[0] = (sel) ? 'X' : (cur && save_schar1 == ' ') ? '-' : save_schar1;
+ if(draw_partial_line)
+ Writechar(ielem->data[0], 0);
+
+ save_schar2 = ielem->data[1];
+ if(cur){
+ ielem->data[1] = '>';
+ if(draw_partial_line)
+ Writechar(ielem->data[1], 0);
+ }
+ }
+ }
+ }
+ else if(ifield->ctype == afld){
+
+ if(draw_partial_line){
+ MoveCursor(HEADER_ROWS(ps_global) + line, utf8_width(draw));
+ for(i = 0; i < ifield->width-1; i++)
+ Writechar(cur ? '-' : ' ', 0);
+
+ Writechar(cur ? '>' : ' ', 0);
+ }
+
+ ielem = ifield->ielem;
+ if(ielem && ielem->datalen >= ifield->width){
+ for(i = 0; i < ifield->width-1; i++)
+ ielem->data[i] = cur ? '-' : ' ';
+
+ ielem->data[i] = cur ? '>' : ' ';
+ }
+ }
+ else if(ifield->ctype == pfld){
+ ielem = ifield->ielem;
+ if(ielem && ielem->datalen > 0){
+ save_pchar = ielem->data[0];
+ ielem->data[0] = ice->plus;
+
+ if(draw_partial_line){
+ MoveCursor(HEADER_ROWS(ps_global) + line, utf8_width(draw));
+ Writechar(ielem->data[0], 0);
+ Writechar(' ', 0);
+ }
+ }
+ }
+
+ for(ielem = ifield->ielem;
+ ielem && ielem->print_format && p-draw < n;
+ ielem = ielem->next){
+ char *src;
+ size_t bytes_added;
+
+ src = ielem->data;
+ bytes_added = utf8_pad_to_width(p, src,
+ ((n+1)-(p-draw)) * sizeof(char),
+ ielem->wid, ifield->leftadj);
+ p += bytes_added;
+ }
+
+ draw[n] = '\0';
+
+ if(ifield->ctype == sfld){
+ ielem = ifield->ielem;
+ if(ielem && ielem->datalen > 0){
+ if(ielem->datalen == 1){
+ ielem->data[0] = save_schar1;
+ if(ielem->next && ielem->next->datalen)
+ ielem->next->data[0] = save_schar2;
+ }
+ else if(ielem->datalen > 1){
+ ielem->data[0] = save_schar1;
+ ielem->data[1] = save_schar2;
+ }
+ }
+ }
+ else if(ifield->ctype == afld){
+ ielem = ifield->ielem;
+ if(ielem && ielem->datalen >= ifield->width)
+ for(i = 0; i < ifield->width; i++)
+ ielem->data[i] = ' ';
+ }
+ else if(ifield->ctype == pfld){
+ ielem = ifield->ielem;
+ if(ielem && ielem->datalen > 0)
+ ielem->data[0] = save_pchar;
+ }
+
+ previfield = ifield;
+ }
+
+ *p = '\0';
+
+ if(draw_whole_line)
+ PutLine0(HEADER_ROWS(ps_global) + line, 0, draw);
+ }
+ else{
+ int uc, ac, do_arrow;
+ int i, drew_X = 0;
+ int inverse_hack = 0, need_inverse_hack = 0;
+ int doing_bold = 0;
+
+ /* so we can restore current color at the end */
+ if((uc=pico_usingcolor()) != 0)
+ lastc = pico_get_cur_color();
+
+ /*
+ * There are two possible "arrow" cursors. One is the one that
+ * you get when you are at a slow speed or you turn that slow
+ * speed one on. It is drawn as part of the status column.
+ * That one is the one associated with the variable "ac".
+ * It is always the base_color or the inverse of the base_color.
+ *
+ * The other "arrow" cursor is the one you get by including the
+ * ARROW token in the index-format. It may be configured to
+ * be colored.
+ *
+ * The arrow cursors have two special properties that make
+ * them different from other sections or fields.
+ * First, the arrow cursors only show up on the current line.
+ * Second, the arrow cursors are drawn with generated data, not
+ * data that is present in the passed in data.
+ */
+
+ /* ac is for the old integrated arrow cursor */
+ ac = F_ON(F_FORCE_ARROW,ps_global);
+
+ /* do_arrow is for the ARROW token in index-format */
+ do_arrow = (afld != iNothing);
+
+ MoveCursor(HEADER_ROWS(ps_global) + line, 0);
+
+ /* find the base color for the whole line */
+ if(cur && !ac && !do_arrow){
+ /*
+ * This stanza handles the current line marking in the
+ * regular, non-arrow-cursor case.
+ */
+
+ /*
+ * If the current line has a linecolor, apply the
+ * appropriate reverse transformation to show it is current.
+ */
+ if(uc && ice->linecolor && ice->linecolor->fg[0]
+ && ice->linecolor->bg[0] && pico_is_good_colorpair(ice->linecolor)){
+ base_color = apply_rev_color(ice->linecolor,
+ ps_global->index_color_style);
+
+ (void)pico_set_colorp(base_color, PSC_NONE);
+ }
+ else{
+ inverse_hack++;
+ if(uc){
+ COLOR_PAIR *rev;
+
+ if((rev = pico_get_rev_color()) != NULL){
+ base_color = new_color_pair(rev->fg, rev->bg);
+ (void)pico_set_colorp(base_color, PSC_NONE);
+ }
+ else
+ base_color = lastc;
+ }
+ }
+ }
+ else if(uc && ice->linecolor && ice->linecolor->fg[0]
+ && ice->linecolor->bg[0]
+ && pico_is_good_colorpair(ice->linecolor)){
+ (void)pico_set_colorp(ice->linecolor, PSC_NONE);
+ base_color = ice->linecolor;
+ }
+ else
+ base_color = lastc;
+
+ memset(draw, 0, sizeof(draw));
+ p = draw;
+
+ doing_bold = (sel && F_ON(F_SELECTED_SHOWN_BOLD, ps_global) && StartBold());
+
+ /* draw each field */
+ for(ifield = ice->ifield; ifield && p-draw < n; ifield = ifield->next){
+
+ drew_X = 0;
+
+ /*
+ * Fix up the data for some special cases.
+ */
+
+ /* message number string is generated on the fly */
+ if(ifield->ctype == iMessNo){
+ ielem = ifield->ielem;
+ if(ielem && ielem->datalen >= ifield->width){
+ snprintf(ielem->data, ielem->datalen+1, "%*.ld", ifield->width, msgno);
+ ielem->data[ifield->width] = '\0';
+ ielem->data[ielem->datalen] = '\0';
+ }
+ }
+
+ if(ifield->ctype == sfld){
+ ielem = ifield->ielem;
+ if(ielem && ielem->datalen > 0){
+ if(ielem->datalen == 1){
+ save_schar1 = ielem->data[0];
+ if(sel && !doing_bold){
+ ielem->data[0] = 'X';
+ drew_X++;
+ }
+ else if(ac && cur && ielem->data[0] == ' ')
+ ielem->data[0] = '-';
+
+ if(ielem->next && ielem->next->datalen){
+ save_schar2 = ielem->next->data[0];
+ if(ac && cur && ielem->next->data[0] != '\0')
+ ielem->next->data[0] = '>';
+ }
+ }
+ else if(ielem->datalen > 1){
+ if(sel && !doing_bold){
+ ielem->data[0] = 'X';
+ drew_X++;
+ }
+ else if(ac && cur && ielem->data[0] == ' ')
+ ielem->data[0] = '-';
+
+ save_schar2 = ielem->data[1];
+ if(ac && cur && ielem->data[1] != '\0')
+ ielem->data[1] = '>';
+ }
+ }
+ }
+ else if(ifield->ctype == afld && do_arrow && cur){
+
+ ielem = ifield->ielem;
+ if(ielem && ielem->datalen >= ifield->width){
+ for(i = 0; i < ifield->width-1; i++)
+ ielem->data[i] = cur ? '-' : ' ';
+
+ ielem->data[i] = '>';
+ }
+ }
+ else if(ifield->ctype == pfld){
+ ielem = ifield->ielem;
+ if(ielem && ielem->datalen > 0){
+ save_pchar = ielem->data[0];
+ ielem->data[0] = ice->plus;
+ }
+ }
+
+ /* space between fields */
+ if(ifield != ice->ifield && !(previfield && previfield->ctype == iText)){
+ if(inverse_hack)
+ StartInverse();
+
+ Write_to_screen(" ");
+ if(inverse_hack)
+ EndInverse();
+ }
+
+ for(ielem = ifield->ielem; ielem; ielem = ielem->next){
+ char *src;
+ size_t bytes_added;
+
+ src = ielem->data;
+ bytes_added = utf8_pad_to_width(draw, src,
+ (n+1) * sizeof(char),
+ ielem->wid, ifield->leftadj);
+ draw[n] = '\0';
+
+ /*
+ * Switch to color for ielem.
+ * But don't switch if we drew an X in this column,
+ * because that overwrites the colored thing, and don't
+ * switch if this is the ARROW field and this is not
+ * the current message. ARROW field is only colored for
+ * the current message.
+ * And don't switch if current line and type eTypeCol.
+ */
+ if(ielem->color && pico_is_good_colorpair(ielem->color)
+ && !(do_arrow && ifield->ctype == afld && !cur)
+ && (!drew_X || ielem != ifield->ielem)
+ && !(cur && ielem->type == eTypeCol)){
+ need_inverse_hack = 0;
+ (void) pico_set_colorp(ielem->color, PSC_NORM);
+ }
+ else
+ need_inverse_hack = 1;
+
+ if(need_inverse_hack && inverse_hack)
+ StartInverse();
+
+ Write_to_screen(draw);
+ if(need_inverse_hack && inverse_hack)
+ EndInverse();
+
+ (void) pico_set_colorp(base_color, PSC_NORM);
+ }
+
+ /*
+ * Restore the data for the special cases.
+ */
+
+ if(ifield->ctype == sfld){
+ ielem = ifield->ielem;
+ if(ielem && ielem->datalen > 0){
+ if(ielem->datalen == 1){
+ ielem->data[0] = save_schar1;
+ if(ielem->next && ielem->next->datalen)
+ ielem->next->data[0] = save_schar2;
+ }
+ else if(ielem->datalen > 1){
+ ielem->data[0] = save_schar1;
+ ielem->data[1] = save_schar2;
+ }
+ }
+ }
+ else if(ifield->ctype == afld){
+ ielem = ifield->ielem;
+ if(ielem && ielem->datalen >= ifield->width)
+ for(i = 0; i < ifield->width; i++)
+ ielem->data[i] = ' ';
+ }
+ else if(ifield->ctype == pfld){
+ ielem = ifield->ielem;
+ if(ielem && ielem->datalen > 0)
+ ielem->data[0] = save_pchar;
+ }
+
+ previfield = ifield;
+ }
+
+ if(doing_bold)
+ EndBold();
+
+ if(base_color && base_color != lastc && base_color != ice->linecolor)
+ free_color_pair(&base_color);
+
+ if(lastc){
+ (void)pico_set_colorp(lastc, PSC_NORM);
+ free_color_pair(&lastc);
+ }
+ }
+ }
+
+ entry->hilite = cur;
+ entry->bolded = sel;
+ entry->msgno = msgno;
+ entry->plus = ice->plus;
+ entry->id = ice->id;
+
+ if(!ice->color_lookup_done && pico_usingcolor())
+ entry->id = 0;
+
+ return(cur ? (line + HEADER_ROWS(ps_global)) : 0);
+}
+
+/*
+ * setup_index_state - hooked onto pith_opt_save_index_state to setup
+ * current_index_state after setup_{index,thread}_header_widths
+ */
+void
+setup_index_state(int threaded)
+{
+ if(current_index_state){
+ if(threaded){
+ current_index_state->status_col = 0;
+ current_index_state->status_fld = iStatus;
+ current_index_state->plus_fld = iNothing;
+ current_index_state->arrow_fld = iNothing;
+ } else {
+ INDEX_COL_S *cdesc, *prevcdesc = NULL;
+ IndexColType sfld, altfld, plusfld, arrowfld;
+ int width, fld, col, pluscol, scol, altcol;
+
+ col = 0;
+ scol = -1;
+ sfld = iNothing;
+ altcol = -1;
+ altfld = iNothing;
+ /* figure out which field is status field */
+ for(cdesc = ps_global->index_disp_format, fld = 0;
+ cdesc->ctype != iNothing;
+ cdesc++){
+ width = cdesc->width;
+ if(width == 0)
+ continue;
+
+ /* space between columns */
+ if(col > 0 && !(prevcdesc && prevcdesc->ctype == iText))
+ col++;
+
+ if(cdesc->ctype == iStatus){
+ scol = col;
+ sfld = cdesc->ctype;
+ break;
+ }
+
+ if(cdesc->ctype == iFStatus || cdesc->ctype == iIStatus){
+ scol = col;
+ sfld = cdesc->ctype;
+ break;
+ }
+
+ if(cdesc->ctype == iMessNo){
+ altcol = col;
+ altfld = cdesc->ctype;
+ }
+
+ col += width;
+ fld++;
+ prevcdesc = cdesc;
+ }
+
+ if(sfld == iNothing){
+ if(altcol == -1){
+ scol = 0;
+ }
+ else{
+ scol = altcol;
+ sfld = altfld;
+ }
+ }
+
+
+ current_index_state->status_col = scol;
+ current_index_state->status_fld = sfld;
+
+ col = 0;
+ plusfld = iNothing;
+ pluscol = -1;
+ prevcdesc = NULL;
+ /* figure out which column to use for threading '+' */
+ if(THREADING()
+ && ps_global->thread_disp_style != THREAD_NONE
+ && ps_global->VAR_THREAD_MORE_CHAR[0]
+ && ps_global->VAR_THREAD_EXP_CHAR[0])
+ for(cdesc = ps_global->index_disp_format, fld = 0;
+ cdesc->ctype != iNothing;
+ cdesc++){
+ width = cdesc->width;
+ if(width == 0)
+ continue;
+
+ /* space between columns */
+ if(col > 0 && !(prevcdesc && prevcdesc->ctype == iText))
+ col++;
+
+ if((cdesc->ctype == iSubject
+ || cdesc->ctype == iSubjectText
+ || cdesc->ctype == iSubjKey
+ || cdesc->ctype == iSubjKeyText
+ || cdesc->ctype == iSubjKeyInit
+ || cdesc->ctype == iSubjKeyInitText)
+ && (ps_global->thread_disp_style == THREAD_STRUCT
+ || ps_global->thread_disp_style == THREAD_MUTTLIKE
+ || ps_global->thread_disp_style == THREAD_INDENT_SUBJ1
+ || ps_global->thread_disp_style == THREAD_INDENT_SUBJ2)){
+ plusfld = cdesc->ctype;
+ pluscol = col;
+ break;
+ }
+
+ if((cdesc->ctype == iFrom
+ || cdesc->ctype == iFromToNotNews
+ || cdesc->ctype == iFromTo
+ || cdesc->ctype == iAddress
+ || cdesc->ctype == iMailbox)
+ && (ps_global->thread_disp_style == THREAD_INDENT_FROM1
+ || ps_global->thread_disp_style == THREAD_INDENT_FROM2
+ || ps_global->thread_disp_style == THREAD_STRUCT_FROM)){
+ plusfld = cdesc->ctype;
+ pluscol = col;
+ break;
+ }
+
+ col += width;
+ fld++;
+ prevcdesc = cdesc;
+ }
+
+ current_index_state->plus_fld = plusfld;
+ current_index_state->plus_col = pluscol;
+
+ arrowfld = iNothing;
+ /* figure out which field is arrow field, if any */
+ for(cdesc = ps_global->index_disp_format, fld = 0;
+ cdesc->ctype != iNothing;
+ cdesc++){
+ width = cdesc->width;
+ if(width == 0)
+ continue;
+
+ if(cdesc->ctype == iArrow){
+ arrowfld = cdesc->ctype;
+ break;
+ }
+
+ fld++;
+ }
+
+ current_index_state->arrow_fld = arrowfld;
+ }
+ }
+}
+
+
+/*
+ * insert_condensed_thread_cue - used on pith hook to add decoration to
+ * subject or from text to show condensed thread info
+ */
+int
+condensed_thread_cue(PINETHRD_S *thd, ICE_S *ice,
+ char **fieldstr, size_t *strsize, int width, int collapsed)
+{
+ if(current_index_state->plus_fld != iNothing && !THRD_INDX() && fieldstr && *fieldstr){
+ /*
+ * WARNING!
+ * There is an unwarranted assumption here that VAR_THREAD_MORE_CHAR[0]
+ * and VAR_THREAD_EXP_CHAR[0] are ascii.
+ * Could do something similar to the conversions done with keyword
+ * initials in key_str.
+ */
+ if(ice)
+ ice->plus = collapsed ? ps_global->VAR_THREAD_MORE_CHAR[0]
+ : (thd && thd->next)
+ ? ps_global->VAR_THREAD_EXP_CHAR[0] : ' ';
+
+ if(strsize && *strsize > 0 && width != 0){
+ *(*fieldstr)++ = ' ';
+ (*strsize)--;
+ if(width > 0)
+ width--;
+ }
+
+ if(strsize && *strsize > 0 && width != 0){
+ *(*fieldstr)++ = ' ';
+ (*strsize)--;
+ if(width > 0)
+ width--;
+ }
+ }
+
+ return(width);
+}
+
+
+int
+truncate_subj_and_from_strings(void)
+{
+ return 1;
+}
+
+
+/*
+ * paint_index_hline - paint index line given what we got
+ */
+void
+paint_index_hline(MAILSTREAM *stream, long int msgno, ICE_S *ice)
+{
+ PINETHRD_S *thrd;
+
+ /*
+ * Trust only what we get back that isn't bogus since
+ * we were prevented from doing any fetches and such...
+ */
+ if((ps_global->redrawer == redraw_index_body
+ || ps_global->prev_screen == mail_index_screen)
+ && current_index_state
+ && current_index_state->stream == stream
+ && !ps_global->msgmap->hilited){
+ int line;
+
+ /*
+ * This test isn't right if there are hidden lines. The line will
+ * fail the test because it seems like it is past the end of the
+ * screen but since the hidden lines don't take up space the line
+ * might actually be on the screen. Don't know that it is worth
+ * it to fix this, though, since you may have to file through
+ * many hidden lines before finding the visible ones. I'm not sure
+ * if the logic inside the if is correct when we do pass the
+ * top-level test. Leave it for now. Hubert - 2002-06-28
+ */
+ if((line = (int)(msgno - current_index_state->msg_at_top)) >= 0
+ && line < current_index_state->lines_per_page){
+ if(any_lflagged(ps_global->msgmap, MN_HIDE | MN_CHID)){
+ long n;
+ long zoomhide, collapsehide;
+
+ zoomhide = any_lflagged(ps_global->msgmap, MN_HIDE);
+ collapsehide = any_lflagged(ps_global->msgmap, MN_CHID);
+
+ /*
+ * Line is visible if it is selected and not hidden due to
+ * thread collapse, or if there is no zooming happening and
+ * it is not hidden due to thread collapse.
+ */
+ for(line = 0, n = current_index_state->msg_at_top;
+ n != msgno;
+ n++)
+ if((zoomhide
+ && get_lflag(stream, current_index_state->msgmap,
+ n, MN_SLCT)
+ && (!collapsehide
+ || !get_lflag(stream, current_index_state->msgmap, n,
+ MN_CHID)))
+ ||
+ (!zoomhide
+ && !get_lflag(stream, current_index_state->msgmap,
+ n, MN_CHID)))
+ line++;
+ }
+
+ thrd = NULL;
+ if(THRD_INDX()){
+ unsigned long rawno;
+
+ rawno = mn_m2raw(current_index_state->msgmap, msgno);
+ if(rawno)
+ thrd = fetch_thread(stream, rawno);
+ }
+
+ paint_index_line(ice, line,
+ (THRD_INDX() && thrd) ? thrd->thrdno : msgno,
+ current_index_state->status_fld,
+ current_index_state->plus_fld,
+ current_index_state->arrow_fld,
+ &current_index_state->entry_state[line],
+ mn_is_cur(current_index_state->msgmap, msgno),
+ THRD_INDX()
+ ? (count_lflags_in_thread(stream, thrd,
+ current_index_state->msgmap,
+ MN_SLCT) > 0)
+ : get_lflag(stream, current_index_state->msgmap,
+ msgno, MN_SLCT));
+ fflush(stdout);
+ }
+ }
+}
+
+
+
+
+/*
+ * pine_imap_env -- C-client's telling us an envelope just arrived
+ * from the server. Use it if we can...
+ */
+void
+pine_imap_envelope(MAILSTREAM *stream, long unsigned int rawno, ENVELOPE *env)
+{
+ MESSAGECACHE *mc;
+
+ dprint((7, "imap_env(%ld)\n", rawno));
+ if(stream && !sp_mail_box_changed(stream)
+ && stream == ps_global->mail_stream
+ && rawno > 0L && rawno <= stream->nmsgs
+ && (mc = mail_elt(stream,rawno))
+ && mc->valid
+ && mc->rfc822_size
+ && !get_lflag(stream, NULL, rawno, MN_HIDE | MN_CHID | MN_EXLD)){
+ INDEXDATA_S idata;
+ ICE_S *ice;
+
+ memset(&idata, 0, sizeof(INDEXDATA_S));
+ idata.no_fetch = 1;
+ idata.size = mc->rfc822_size;
+ idata.rawno = rawno;
+ idata.msgno = mn_raw2m(sp_msgmap(stream), rawno);
+ idata.stream = stream;
+
+ index_data_env(&idata, env);
+
+ /*
+ * Look for resent-to already in MAILCACHE data
+ */
+ if(mc->private.msg.header.text.data){
+ STRINGLIST *lines;
+ SIZEDTEXT szt;
+ static char *linelist[] = {"resent-to" , NULL};
+
+ if(mail_match_lines(lines = new_strlst(linelist),
+ mc->private.msg.lines, 0L)){
+ idata.valid_resent_to = 1;
+ memset(&szt, 0, sizeof(SIZEDTEXT));
+ textcpy(&szt, &mc->private.msg.header.text);
+ mail_filter((char *) szt.data, szt.size, lines, 0L);
+ idata.resent_to_us = parsed_resent_to_us((char *) szt.data);
+ if(szt.data)
+ fs_give((void **) &szt.data);
+ }
+
+ free_strlst(&lines);
+ }
+
+ ice = (*format_index_line)(&idata);
+ if(idata.bogus)
+ clear_ice(&ice);
+ else
+ paint_index_hline(stream, idata.msgno, ice);
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Scroll to specified postion.
+
+
+ Args: pos - position to scroll to.
+
+ Returns: TRUE - did the scroll operation.
+ FALSE - was not able to do the scroll operation.
+ ----*/
+int
+index_scroll_to_pos (long int pos)
+{
+ static short bad_timing = 0;
+ long i, j, k;
+
+ if(bad_timing)
+ return (FALSE);
+
+ /*
+ * Put the requested line at the top of the screen...
+ */
+
+ /*
+ * Starting at msg 'pos' find next visible message.
+ */
+ for(i=pos; i <= mn_get_total(current_index_state->msgmap); i++) {
+ if(!msgline_hidden(current_index_state->stream,
+ current_index_state->msgmap, i, 0)){
+ current_index_state->msg_at_top = i;
+ break;
+ }
+ }
+
+ /*
+ * If single selection, move selected message to be on the screen.
+ */
+ if (mn_total_cur(current_index_state->msgmap) == 1L) {
+ if (current_index_state->msg_at_top >
+ mn_get_cur (current_index_state->msgmap)) {
+ /* Selection was above screen, move to top of screen. */
+ mn_set_cur(current_index_state->msgmap,current_index_state->msg_at_top);
+ }
+ else {
+ /* Scan through the screen. If selection found, leave where is.
+ * Otherwise, move to end of screen */
+ for( i = current_index_state->msg_at_top,
+ j = current_index_state->lines_per_page;
+ i != mn_get_cur(current_index_state->msgmap) &&
+ i <= mn_get_total(current_index_state->msgmap) &&
+ j > 0L;
+ i++) {
+ if(!msgline_hidden(current_index_state->stream,
+ current_index_state->msgmap, i, 0)){
+ j--;
+ k = i;
+ }
+ }
+ if(j <= 0L)
+ /* Move to end of screen. */
+ mn_set_cur(current_index_state->msgmap, k);
+ }
+ }
+
+ bad_timing = 0;
+ return (TRUE);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Adjust the index display state down a line
+
+ Args: scroll_count -- number of lines to scroll
+
+ Returns: TRUE - did the scroll operation.
+ FALSE - was not able to do the scroll operation.
+ ----*/
+int
+index_scroll_down(long int scroll_count)
+{
+ static short bad_timing = 0;
+ long i, j, k;
+ long cur, total;
+
+ if(bad_timing)
+ return (FALSE);
+
+ bad_timing = 1;
+
+
+ j = -1L;
+ total = mn_get_total (current_index_state->msgmap);
+ for(k = i = current_index_state->msg_at_top; ; i++){
+
+ /* Only examine non-hidden messages. */
+ if(!msgline_hidden(current_index_state->stream,
+ current_index_state->msgmap, i, 0)){
+ /* Remember this message */
+ k = i;
+ /* Increment count of lines. */
+ if (++j >= scroll_count) {
+ /* Counted enough lines, stop. */
+ current_index_state->msg_at_top = k;
+ break;
+ }
+ }
+
+ /* If at last message, stop. */
+ if (i >= total){
+ current_index_state->msg_at_top = k;
+ break;
+ }
+ }
+
+ /*
+ * If not multiple selection, see if selected message visable. if not
+ * set it to last visable message.
+ */
+ if(mn_total_cur(current_index_state->msgmap) == 1L) {
+ j = 0L;
+ cur = mn_get_cur (current_index_state->msgmap);
+ for (i = current_index_state->msg_at_top; i <= total; ++i) {
+ if(!msgline_hidden(current_index_state->stream,
+ current_index_state->msgmap, i, 0)){
+ if (++j >= current_index_state->lines_per_page) {
+ break;
+ }
+ if (i == cur)
+ break;
+ }
+ }
+ if (i != cur)
+ mn_set_cur(current_index_state->msgmap,
+ current_index_state->msg_at_top);
+ }
+
+ bad_timing = 0;
+ return (TRUE);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Adjust the index display state up a line
+
+ Args: scroll_count -- number of lines to scroll
+
+ Returns: TRUE - did the scroll operation.
+ FALSE - was not able to do the scroll operation.
+
+ ----*/
+int
+index_scroll_up(long int scroll_count)
+{
+ static short bad_timing = 0;
+ long i, j, k;
+ long cur;
+
+ if(bad_timing)
+ return(FALSE);
+
+ bad_timing = 1;
+
+ j = -1L;
+ for(k = i = current_index_state->msg_at_top; ; i--){
+
+ /* Only examine non-hidden messages. */
+ if(!msgline_hidden(current_index_state->stream,
+ current_index_state->msgmap, i, 0)){
+ /* Remember this message */
+ k = i;
+ /* Increment count of lines. */
+ if (++j >= scroll_count) {
+ /* Counted enough lines, stop. */
+ current_index_state->msg_at_top = k;
+ break;
+ }
+ }
+
+ /* If at first message, stop */
+ if (i <= 1L){
+ current_index_state->msg_at_top = k;
+ break;
+ }
+ }
+
+
+ /*
+ * If not multiple selection, see if selected message visable. if not
+ * set it to last visable message.
+ */
+ if(mn_total_cur(current_index_state->msgmap) == 1L) {
+ j = 0L;
+ cur = mn_get_cur (current_index_state->msgmap);
+ for ( i = current_index_state->msg_at_top;
+ i <= mn_get_total(current_index_state->msgmap);
+ ++i) {
+ if(!msgline_hidden(current_index_state->stream,
+ current_index_state->msgmap, i, 0)){
+ if (++j >= current_index_state->lines_per_page) {
+ k = i;
+ break;
+ }
+ if (i == cur)
+ break;
+ }
+ }
+ if (i != cur)
+ mn_set_cur(current_index_state->msgmap, k);
+ }
+
+
+ bad_timing = 0;
+ return (TRUE);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Calculate the message number that should be at the top of the display
+
+ Args: current - the current message number
+ lines_per_page - the number of lines for the body of the index only
+
+ Returns: -1 if the current message is -1
+ the message entry for the first message at the top of the screen.
+
+When paging in the index it is always on even page boundies, and the
+current message is always on the page thus the top of the page is
+completely determined by the current message and the number of lines
+on the page.
+ ----*/
+long
+top_ent_calc(MAILSTREAM *stream, MSGNO_S *msgs, long int at_top, long int lines_per_page)
+{
+ long current, hidden, lastn;
+ long n, m = 0L, t = 1L;
+
+ current = (mn_total_cur(msgs) <= 1L) ? mn_get_cur(msgs) : at_top;
+
+ if(current < 0L)
+ return(-1);
+
+ if(lines_per_page == 0L)
+ return(current);
+
+ if(THRD_INDX_ENABLED()){
+ long rawno;
+ PINETHRD_S *thrd = NULL;
+
+ rawno = mn_m2raw(msgs, mn_get_cur(msgs));
+ if(rawno)
+ thrd = fetch_thread(stream, rawno);
+
+ if(THRD_INDX()){
+
+ if(any_lflagged(msgs, MN_HIDE)){
+ PINETHRD_S *is_current_thrd;
+
+ is_current_thrd = thrd;
+ if(is_current_thrd){
+ if(mn_get_revsort(msgs)){
+ /* start with top of tail of thread list */
+ thrd = fetch_thread(stream, mn_m2raw(msgs, 1L));
+ if(thrd && thrd->top && thrd->top != thrd->rawno)
+ thrd = fetch_thread(stream, thrd->top);
+ }
+ else{
+ /* start with head of thread list */
+ thrd = fetch_head_thread(stream);
+ }
+
+ t = 1L;
+ m = 0L;
+ if(thrd)
+ n = mn_raw2m(msgs, thrd->rawno);
+
+ while(thrd){
+ if(!msgline_hidden(stream, msgs, n, 0)
+ && (++m % lines_per_page) == 1L)
+ t = n;
+
+ if(thrd == is_current_thrd)
+ break;
+
+ if(mn_get_revsort(msgs) && thrd->prevthd)
+ thrd = fetch_thread(stream, thrd->prevthd);
+ else if(!mn_get_revsort(msgs) && thrd->nextthd)
+ thrd = fetch_thread(stream, thrd->nextthd);
+ else
+ thrd = NULL;
+
+ if(thrd)
+ n = mn_raw2m(msgs, thrd->rawno);
+ }
+ }
+ }
+ else{
+ if(thrd){
+ n = thrd->thrdno;
+ m = lines_per_page * ((n - 1L)/ lines_per_page) + 1L;
+ n = thrd->rawno;
+ /*
+ * We want to find the m'th thread and the
+ * message number that goes with that. We just have
+ * to back up from where we are to get there.
+ * If we have a reverse sort backing up is going
+ * forward through the thread.
+ */
+ while(thrd && m < thrd->thrdno){
+ n = thrd->rawno;
+ if(mn_get_revsort(msgs) && thrd->nextthd)
+ thrd = fetch_thread(stream, thrd->nextthd);
+ else if(!mn_get_revsort(msgs) && thrd->prevthd)
+ thrd = fetch_thread(stream, thrd->prevthd);
+ else
+ thrd = NULL;
+ }
+
+ if(thrd)
+ n = thrd->rawno;
+
+ t = mn_raw2m(msgs, n);
+ }
+ }
+ }
+ else{ /* viewing a thread */
+
+ lastn = mn_get_total(msgs);
+ t = 1L;
+
+ /* get top of thread */
+ if(thrd && thrd->top && thrd->top != thrd->rawno)
+ thrd = fetch_thread(stream, thrd->top);
+
+ if(thrd){
+ if(mn_get_revsort(msgs))
+ lastn = mn_raw2m(msgs, thrd->rawno);
+ else
+ t = mn_raw2m(msgs, thrd->rawno);
+ }
+
+ n = 0L;
+
+ /* n is the end of this thread */
+ while(thrd){
+ n = mn_raw2m(msgs, thrd->rawno);
+ if(thrd->branch)
+ thrd = fetch_thread(stream, thrd->branch);
+ else if(thrd->next)
+ thrd = fetch_thread(stream, thrd->next);
+ else
+ thrd = NULL;
+ }
+
+ if(n){
+ if(mn_get_revsort(msgs))
+ t = n;
+ else
+ lastn = n;
+ }
+
+ for(m = 0L, n = t; n <= MIN(current, lastn); n++)
+ if(!msgline_hidden(stream, msgs, n, 0)
+ && (++m % lines_per_page) == 1L)
+ t = n;
+ }
+
+ return(t);
+ }
+ else if((hidden = any_lflagged(msgs, MN_HIDE | MN_CHID)) != 0){
+
+ if(current < mn_get_total(msgs) / 2){
+ t = 1L;
+ m = 0L;
+ for(n = 1L; n <= MIN(current, mn_get_total(msgs)); n++)
+ if(!msgline_hidden(stream, msgs, n, 0)
+ && (++m % lines_per_page) == 1L)
+ t = n;
+ }
+ else{
+ t = current+1L;
+ m = mn_get_total(msgs)-hidden+1L;
+ for(n = mn_get_total(msgs); n >= 1L && t > current; n--)
+ if(!msgline_hidden(stream, msgs, n, 0)
+ && (--m % lines_per_page) == 1L)
+ t = n;
+
+ if(t > current)
+ t = 1L;
+ }
+
+ return(t);
+ }
+ else
+ return(lines_per_page * ((current - 1L)/ lines_per_page) + 1L);
+}
+
+
+/*----------------------------------------------------------------------
+ Clear various bits that make up a healthy display
+
+ ----*/
+void
+reset_index_border(void)
+{
+ mark_status_dirty();
+ mark_keymenu_dirty();
+ mark_titlebar_dirty();
+ ps_global->mangled_screen = 1; /* signal FULL repaint */
+}
+
+
+/*----------------------------------------------------------------------
+ This redraws the body of the index screen, taking into
+account any change in the size of the screen. All the state needed to
+repaint is in the static variables so this can be called from
+anywhere.
+ ----*/
+void
+redraw_index_body(void)
+{
+ int agg;
+
+ if((agg = (mn_total_cur(current_index_state->msgmap) > 1L)) != 0)
+ restore_selected(current_index_state->msgmap);
+
+ ps_global->mangled_body = 1;
+
+ (void) update_index(ps_global, current_index_state);
+ if(agg)
+ pseudo_selected(current_index_state->stream, current_index_state->msgmap);
+}
+
+
+/*----------------------------------------------------------------------
+ Give hint about Other command being optional. Some people get the idea
+ that it is required to use the commands on the 2nd and 3rd keymenus.
+
+ Args: none
+
+ Result: message may be printed to status line
+ ----*/
+void
+warn_other_cmds(void)
+{
+ static int other_cmds = 0;
+
+ other_cmds++;
+ if(((ps_global->first_time_user || ps_global->show_new_version) &&
+ other_cmds % 3 == 0 && other_cmds < 10) || other_cmds % 20 == 0)
+ q_status_message(SM_ASYNC, 0, 9,
+ _("Remember the \"O\" command is always optional"));
+}
+
+
+void
+thread_command(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap,
+ UCS preloadkeystroke, int q_line)
+{
+ PINETHRD_S *thrd = NULL;
+ unsigned long rawno, save_branch;
+ int we_cancel = 0;
+ int flags = AC_FROM_THREAD;
+
+ if(!(stream && msgmap))
+ return;
+
+ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
+ if(rawno)
+ thrd = fetch_thread(stream, rawno);
+
+ if(!thrd)
+ return;
+
+ save_branch = thrd->branch;
+ thrd->branch = 0L; /* branch is a sibling, not part of thread */
+
+ if(!preloadkeystroke){
+ if(!THRD_INDX()){
+ if(get_lflag(stream, NULL, rawno, MN_COLL) && thrd->next)
+ flags |= AC_EXPN;
+ else
+ flags |= AC_COLL;
+ }
+
+ if(count_lflags_in_thread(stream, thrd, msgmap, MN_SLCT)
+ == count_lflags_in_thread(stream, thrd, msgmap, MN_NONE))
+ flags |= AC_UNSEL;
+ }
+
+ we_cancel = busy_cue(NULL, NULL, 1);
+
+ /* save the SLCT flags in STMP for restoring at the bottom */
+ copy_lflags(stream, msgmap, MN_SLCT, MN_STMP);
+
+ /* clear the values from the SLCT flags */
+ set_lflags(stream, msgmap, MN_SLCT, 0);
+
+ /* set SLCT for thrd on down */
+ set_flags_for_thread(stream, msgmap, MN_SLCT, thrd, 1);
+ thrd->branch = save_branch;
+
+ if(we_cancel)
+ cancel_busy_cue(0);
+
+ (void ) apply_command(state, stream, msgmap, preloadkeystroke, flags,
+ q_line);
+
+ /* restore the original flags */
+ copy_lflags(stream, msgmap, MN_STMP, MN_SLCT);
+
+ if(any_lflagged(msgmap, MN_HIDE) > 0L){
+ /* if nothing left selected, unhide all */
+ if(any_lflagged(msgmap, MN_SLCT) == 0L){
+ (void) unzoom_index(ps_global, stream, msgmap);
+ dprint((4, "\n\n ---- Exiting ZOOM mode ----\n"));
+ q_status_message(SM_ORDER,0,2, _("Index Zoom Mode is now off"));
+ }
+
+ /* if current is hidden, adjust */
+ adjust_cur_to_visible(stream, msgmap);
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Search the message headers as displayed in index
+
+ Args: command_line -- The screen line to prompt on
+ msg -- The current message number to start searching at
+ max_msg -- The largest message number in the current folder
+
+ The headers are searched exactly as they are displayed on the screen. The
+search will wrap around to the beginning if not string is not found right
+away.
+ ----*/
+void
+index_search(struct pine *state, MAILSTREAM *stream, int command_line, MSGNO_S *msgmap)
+{
+ int rc, select_all = 0, flags, prefetch, we_turned_on = 0;
+ long i, sorted_msg, selected = 0L;
+ char prompt[MAX_SEARCH+50], new_string[MAX_SEARCH+1];
+ char buf[MAX_SCREEN_COLS+1], *p;
+ HelpType help;
+ char search_string[MAX_SEARCH+1];
+ ICE_S *ice, *ic;
+ static HISTORY_S *history = NULL;
+ static ESCKEY_S header_search_key[] = { {0, 0, NULL, NULL },
+ {ctrl('Y'), 10, "^Y", N_("First Msg")},
+ {ctrl('V'), 11, "^V", N_("Last Msg")},
+ {KEY_UP, 30, "", ""},
+ {KEY_DOWN, 31, "", ""},
+ {-1, 0, NULL, NULL} };
+#define KU_IS (3) /* index of KEY_UP */
+#define PREFETCH_THIS_MANY_LINES (50)
+
+ init_hist(&history, HISTSIZE);
+ search_string[0] = '\0';
+ if((p = get_prev_hist(history, "", 0, NULL)) != NULL){
+ strncpy(search_string, p, sizeof(search_string));
+ search_string[sizeof(search_string)-1] = '\0';
+ }
+
+ dprint((4, "\n - search headers - \n"));
+
+ if(!any_messages(msgmap, NULL, "to search")){
+ return;
+ }
+ else if(mn_total_cur(msgmap) > 1L){
+ q_status_message1(SM_ORDER, 0, 2, "%s msgs selected; Can't search",
+ comatose(mn_total_cur(msgmap)));
+ return;
+ }
+ else
+ sorted_msg = mn_get_cur(msgmap);
+
+ help = NO_HELP;
+ new_string[0] = '\0';
+
+ while(1) {
+ snprintf(prompt, sizeof(prompt), _("Word to search for [%s] : "), search_string);
+
+ if(F_ON(F_ENABLE_AGG_OPS, ps_global)){
+ header_search_key[0].ch = ctrl('X');
+ header_search_key[0].rval = 12;
+ header_search_key[0].name = "^X";
+ header_search_key[0].label = N_("Select Matches");
+ }
+ else{
+ header_search_key[0].ch = header_search_key[0].rval = 0;
+ header_search_key[0].name = header_search_key[0].label = NULL;
+ }
+
+ /*
+ * 2 is really 1 because there will be one real entry and
+ * one entry of "" because of the get_prev_hist above.
+ */
+ if(items_in_hist(history) > 2){
+ header_search_key[KU_IS].name = HISTORY_UP_KEYNAME;
+ header_search_key[KU_IS].label = HISTORY_KEYLABEL;
+ header_search_key[KU_IS+1].name = HISTORY_DOWN_KEYNAME;
+ header_search_key[KU_IS+1].label = HISTORY_KEYLABEL;
+ }
+ else{
+ header_search_key[KU_IS].name = "";
+ header_search_key[KU_IS].label = "";
+ header_search_key[KU_IS+1].name = "";
+ header_search_key[KU_IS+1].label = "";
+ }
+
+ flags = OE_APPEND_CURRENT | OE_KEEP_TRAILING_SPACE;
+
+ rc = optionally_enter(new_string, command_line, 0, sizeof(new_string),
+ prompt, header_search_key, help, &flags);
+
+ if(rc == 3) {
+ help = (help != NO_HELP) ? NO_HELP :
+ F_ON(F_ENABLE_AGG_OPS, ps_global) ? h_os_index_whereis_agg
+ : h_os_index_whereis;
+ continue;
+ }
+ else if(rc == 10){
+ q_status_message(SM_ORDER, 0, 3, _("Searched to First Message."));
+ if(any_lflagged(msgmap, MN_HIDE | MN_CHID)){
+ do{
+ selected = sorted_msg;
+ mn_dec_cur(stream, msgmap, MH_NONE);
+ sorted_msg = mn_get_cur(msgmap);
+ }
+ while(selected != sorted_msg);
+ }
+ else
+ sorted_msg = (mn_get_total(msgmap) > 0L) ? 1L : 0L;
+
+ mn_set_cur(msgmap, sorted_msg);
+ return;
+ }
+ else if(rc == 11){
+ q_status_message(SM_ORDER, 0, 3, _("Searched to Last Message."));
+ if(any_lflagged(msgmap, MN_HIDE | MN_CHID)){
+ do{
+ selected = sorted_msg;
+ mn_inc_cur(stream, msgmap, MH_NONE);
+ sorted_msg = mn_get_cur(msgmap);
+ }
+ while(selected != sorted_msg);
+ }
+ else
+ sorted_msg = mn_get_total(msgmap);
+
+ mn_set_cur(msgmap, sorted_msg);
+ return;
+ }
+ else if(rc == 12){
+ select_all = 1;
+ break;
+ }
+ else if(rc == 30){
+ if((p = get_prev_hist(history, new_string, 0, NULL)) != NULL){
+ strncpy(new_string, p, sizeof(new_string));
+ new_string[sizeof(new_string)-1] = '\0';
+ }
+ else
+ Writechar(BELL, 0);
+
+ continue;
+ }
+ else if(rc == 31){
+ if((p = get_next_hist(history, new_string, 0, NULL)) != NULL){
+ strncpy(new_string, p, sizeof(new_string));
+ new_string[sizeof(new_string)-1] = '\0';
+ }
+ else
+ Writechar(BELL, 0);
+
+ continue;
+ }
+
+ if(rc != 4){ /* 4 is redraw */
+ save_hist(history, new_string, 0, NULL);
+ break;
+ }
+ }
+
+ if(rc == 1 || (new_string[0] == '\0' && search_string[0] == '\0')) {
+ cmd_cancelled(_("Search"));
+ return;
+ }
+
+ if(new_string[0] == '\0'){
+ strncpy(new_string, search_string, sizeof(new_string));
+ new_string[sizeof(new_string)-1] = '\0';
+ }
+
+ strncpy(search_string, new_string, sizeof(search_string));
+ search_string[sizeof(search_string)-1] = '\0';
+
+ we_turned_on = intr_handling_on();
+
+ prefetch = 0;
+ for(i = sorted_msg + ((select_all)?0:1);
+ i <= mn_get_total(msgmap) && !ps_global->intr_pending;
+ i++){
+ if(msgline_hidden(stream, msgmap, i, 0))
+ continue;
+
+ if(prefetch <= 0)
+ prefetch = PREFETCH_THIS_MANY_LINES;
+
+ ic = build_header_work(state, stream, msgmap, i, i, prefetch--, NULL);
+
+ ice = (ic && THRD_INDX() && ic->tice) ? ic->tice : ic;
+
+ if(srchstr(simple_index_line(buf, sizeof(buf), ice, i),
+ search_string)){
+ selected++;
+ if(select_all)
+ set_lflag(stream, msgmap, i, MN_SLCT, 1);
+ else
+ break;
+ }
+ }
+
+ prefetch = 0;
+ if(i > mn_get_total(msgmap)){
+ for(i = 1; i < sorted_msg && !ps_global->intr_pending; i++){
+ if(msgline_hidden(stream, msgmap, i, 0))
+ continue;
+
+ if(prefetch <= 0)
+ prefetch = PREFETCH_THIS_MANY_LINES;
+
+ ic = build_header_work(state, stream, msgmap, i, i, prefetch--, NULL);
+
+ ice = (ic && THRD_INDX() && ic->tice) ? ic->tice : ic;
+
+ if(srchstr(simple_index_line(buf, sizeof(buf), ice, i),
+ search_string)){
+ selected++;
+ if(select_all)
+ set_lflag(stream, msgmap, i, MN_SLCT, 1);
+ else
+ break;
+ }
+ }
+ }
+
+ /* search current line */
+ if(!select_all && !selected){
+ i = sorted_msg;
+ if(!msgline_hidden(stream, msgmap, i, 0)){
+
+ ic = build_header_work(state, stream, msgmap, i, i, 1, NULL);
+
+ ice = (ic && THRD_INDX() && ic->tice) ? ic->tice : ic;
+
+ if(srchstr(simple_index_line(buf, sizeof(buf), ice, i),
+ search_string)){
+ selected++;
+ }
+ }
+ }
+
+ if(ps_global->intr_pending){
+ q_status_message1(SM_ORDER, 0, 3, _("Search cancelled.%s"),
+ select_all ? _(" Selected set may be incomplete."):"");
+ }
+ else if(select_all){
+ if(selected
+ && any_lflagged(msgmap, MN_SLCT) > 0L
+ && !any_lflagged(msgmap, MN_HIDE)
+ && F_ON(F_AUTO_ZOOM, state))
+ (void) zoom_index(state, stream, msgmap, MN_SLCT);
+
+ q_status_message1(SM_ORDER, 0, 3, _("%s messages found matching word"),
+ long2string(selected));
+ }
+ else if(selected){
+ q_status_message1(SM_ORDER, 0, 3, _("Word found%s"),
+ (i < sorted_msg) ? _(". Search wrapped to beginning") :
+ (i == sorted_msg) ? _(". Current line contains only match") : "");
+ mn_set_cur(msgmap, i);
+ }
+ else
+ q_status_message(SM_ORDER, 0, 3, _("Word not found"));
+
+ if(we_turned_on)
+ intr_handling_off();
+}
+
+
+/*
+ * Original idea from Stephen Casner <casner@acm.org>.
+ *
+ * Apply the appropriate reverse color transformation to the given
+ * color pair and return a new color pair. The caller should free the
+ * color pair.
+ *
+ */
+COLOR_PAIR *
+apply_rev_color(COLOR_PAIR *cp, int style)
+{
+ COLOR_PAIR *rc = pico_get_rev_color();
+
+ if(rc){
+ if(style == IND_COL_REV){
+ /* just use Reverse color regardless */
+ return(new_color_pair(rc->fg, rc->bg));
+ }
+ else if(style == IND_COL_FG){
+ /*
+ * If changing to Rev fg is readable and different
+ * from what it already is, do it.
+ */
+ if(strcmp(rc->fg, cp->bg) && strcmp(rc->fg, cp->fg))
+ return(new_color_pair(rc->fg, cp->bg));
+ }
+ else if(style == IND_COL_BG){
+ /*
+ * If changing to Rev bg is readable and different
+ * from what it already is, do it.
+ */
+ if(strcmp(rc->bg, cp->fg) && strcmp(rc->bg, cp->bg))
+ return(new_color_pair(cp->fg, rc->bg));
+ }
+ else if(style == IND_COL_FG_NOAMBIG){
+ /*
+ * If changing to Rev fg is readable, different
+ * from what it already is, and not the same as
+ * the Rev color, do it.
+ */
+ if(strcmp(rc->fg, cp->bg) && strcmp(rc->fg, cp->fg) &&
+ strcmp(rc->bg, cp->bg))
+ return(new_color_pair(rc->fg, cp->bg));
+ }
+ else if(style == IND_COL_BG_NOAMBIG){
+ /*
+ * If changing to Rev bg is readable, different
+ * from what it already is, and not the same as
+ * the Rev color, do it.
+ */
+ if(strcmp(rc->bg, cp->fg) && strcmp(rc->bg, cp->bg) &&
+ strcmp(rc->fg, cp->fg))
+ return(new_color_pair(cp->fg, rc->bg));
+ }
+ }
+
+ /* come here for IND_COL_FLIP and for the cases which fail the tests */
+ return(new_color_pair(cp->bg, cp->fg)); /* flip the colors */
+}
+
+
+
+#ifdef _WINDOWS
+
+/*----------------------------------------------------------------------
+ Callback to get the text of the current message. Used to display
+ a message in an alternate window.
+
+ Args: cmd - what type of scroll operation.
+ text - filled with pointer to text.
+ l - length of text.
+ style - Returns style of text. Can be:
+ GETTEXT_TEXT - Is a pointer to text with CRLF deliminated
+ lines
+ GETTEXT_LINES - Is a pointer to NULL terminated array of
+ char *. Each entry points to a line of
+ text.
+
+ this implementation always returns GETTEXT_TEXT.
+
+ Returns: TRUE - did the scroll operation.
+ FALSE - was not able to do the scroll operation.
+ ----*/
+int
+index_scroll_callback (cmd, scroll_pos)
+int cmd;
+long scroll_pos;
+{
+ int paint = TRUE;
+
+ switch (cmd) {
+ case MSWIN_KEY_SCROLLUPLINE:
+ paint = index_scroll_up (scroll_pos);
+ break;
+
+ case MSWIN_KEY_SCROLLDOWNLINE:
+ paint = index_scroll_down (scroll_pos);
+ break;
+
+ case MSWIN_KEY_SCROLLUPPAGE:
+ paint = index_scroll_up (current_index_state->lines_per_page);
+ break;
+
+ case MSWIN_KEY_SCROLLDOWNPAGE:
+ paint = index_scroll_down (current_index_state->lines_per_page);
+ break;
+
+ case MSWIN_KEY_SCROLLTO:
+ /* Normalize msgno in zoomed case */
+ if(any_lflagged(ps_global->msgmap, MN_HIDE | MN_CHID)){
+ long n, x;
+
+ for(n = 1L, x = 0;
+ x < scroll_pos && n < mn_get_total(ps_global->msgmap);
+ n++)
+ if(!msgline_hidden(ps_global->mail_stream, ps_global->msgmap,
+ n, 0))
+ x++;
+
+ scroll_pos = n - 1; /* list-position --> message number */
+ }
+
+ paint = index_scroll_to_pos (scroll_pos + 1);
+ break;
+ }
+
+ if(paint){
+ mswin_beginupdate();
+ update_titlebar_message();
+ update_titlebar_status();
+ redraw_index_body();
+ mswin_endupdate();
+ }
+
+ return(paint);
+}
+
+
+/*----------------------------------------------------------------------
+ MSWin scroll callback to get the text of the current message
+
+ Args: title - title for new window
+ text -
+ l -
+ style -
+
+ Returns: TRUE - got the requested text
+ FALSE - was not able to get the requested text
+ ----*/
+int
+index_gettext_callback(title, titlelen, text, l, style)
+ char *title;
+ size_t titlelen;
+ void **text;
+ long *l;
+ int *style;
+{
+ int rv = 0;
+ ENVELOPE *env;
+ BODY *body;
+ STORE_S *so;
+ gf_io_t pc;
+
+ if(mn_get_total(ps_global->msgmap) > 0L
+ && (so = so_get(CharStar, NULL, WRITE_ACCESS))){
+ gf_set_so_writec(&pc, so);
+
+ if((env = pine_mail_fetchstructure(ps_global->mail_stream,
+ mn_m2raw(ps_global->msgmap,
+ mn_get_cur(ps_global->msgmap)),
+ &body))
+ && format_message(mn_m2raw(ps_global->msgmap,
+ mn_get_cur(ps_global->msgmap)),
+ env, body, NULL, FM_NEW_MESS, pc)){
+ snprintf(title, titlelen, "Folder %s -- Message %ld of %ld",
+ strsquish(tmp_20k_buf + 500, SIZEOF_20KBUF-500, ps_global->cur_folder, 50),
+ mn_get_cur(ps_global->msgmap),
+ mn_get_total(ps_global->msgmap));
+ title[titlelen-1] = '\0';
+ *text = so_text(so);
+ *l = strlen((char *)so_text(so));
+ *style = GETTEXT_TEXT;
+
+ /* free alloc'd so, but preserve the text passed back to caller */
+ so->txt = (void *) NULL;
+ rv = 1;
+ }
+
+ gf_clear_so_writec(so);
+ so_give(&so);
+ }
+
+ return(rv);
+}
+
+
+/*
+ *
+ */
+int
+index_sort_callback(set, order)
+ int set;
+ long order;
+{
+ int i = 0;
+
+ if(set){
+ sort_folder(ps_global->mail_stream, ps_global->msgmap,
+ order & 0x000000ff,
+ (order & 0x00000100) != 0, SRT_VRB);
+ mswin_beginupdate();
+ update_titlebar_message();
+ update_titlebar_status();
+ redraw_index_body();
+ mswin_endupdate();
+ flush_status_messages(1);
+ }
+ else{
+ i = (int) mn_get_sort(ps_global->msgmap);
+ if(mn_get_revsort(ps_global->msgmap))
+ i |= 0x0100;
+ }
+
+ return(i);
+}
+
+
+/*
+ *
+ */
+void
+index_popup(IndexType style, MAILSTREAM *stream, MSGNO_S *msgmap, int full)
+{
+ int n = 0;
+ int view_in_new_wind_index = -1;
+ long rawno;
+ MESSAGECACHE *mc;
+ MPopup view_index_popup[32];
+ struct key_menu *km = (style == ThreadIndex)
+ ? &thread_keymenu
+ : (ps_global->mail_stream != stream)
+ ? &simple_index_keymenu
+ : &index_keymenu;
+
+ /*
+ * Loosely follow the logic in do_index_border to figure
+ * out which commands to show.
+ */
+
+ if(full){
+ if(km != &simple_index_keymenu){
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.string = (km == &thread_keymenu)
+ ? "&View Thread" : "&View";
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n++].data.val = 'V';
+ }
+
+ if(km == &index_keymenu){
+ view_in_new_wind_index = n;
+ view_index_popup[n].type = tIndex;
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n++].label.string = "View in New Window";
+ }
+
+ if(km != &simple_index_keymenu)
+ view_index_popup[n++].type = tSeparator;
+
+ if(km == &thread_keymenu){
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.string = "&Delete Thread";
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n++].data.val = 'D';
+
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.string = "&UnDelete Thread";
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n++].data.val = 'U';
+ }
+ else{
+ /* Make "delete/undelete" item sensitive */
+ mc = ((rawno = mn_m2raw(msgmap, mn_get_cur(msgmap))) > 0L
+ && stream && rawno <= stream->nmsgs)
+ ? mail_elt(stream, rawno) : NULL;
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.style = lNormal;
+ if(mc && mc->deleted){
+ view_index_popup[n].label.string = "&Undelete";
+ view_index_popup[n++].data.val = 'U';
+ }
+ else{
+ view_index_popup[n].label.string = "&Delete";
+ view_index_popup[n++].data.val = 'D';
+ }
+ }
+
+ if(km == &index_keymenu && F_ON(F_ENABLE_FLAG, ps_global)){
+ view_index_popup[n].type = tSubMenu;
+ view_index_popup[n].label.string = "Flag";
+ view_index_popup[n++].data.submenu = flag_submenu(mc);
+ }
+
+ if(km == &simple_index_keymenu){
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n].label.string = "&Select";
+ view_index_popup[n++].data.val = 'S';
+ }
+ else{
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n].label.string = (km == &thread_keymenu)
+ ? "&Save Thread" : "&Save";
+ view_index_popup[n++].data.val = 'S';
+
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n].label.string = (km == &thread_keymenu)
+ ? "&Export Thread" : "&Export";
+ view_index_popup[n++].data.val = 'E';
+
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n].label.string = "Print";
+ view_index_popup[n++].data.val = '%';
+
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n].label.string = (km == &thread_keymenu)
+ ? "&Reply To Thread" : "&Reply";
+ view_index_popup[n++].data.val = 'R';
+
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n].label.string = (km == &thread_keymenu)
+ ? "&Forward Thread" : "&Forward";
+ view_index_popup[n++].data.val = 'F';
+
+ if(F_ON(F_ENABLE_BOUNCE, ps_global)){
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n].label.string = (km == &thread_keymenu)
+ ? "&Bounce Thread" : "&Bounce";
+ view_index_popup[n++].data.val = 'B';
+ }
+
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n].label.string = "&Take Addresses";
+ view_index_popup[n++].data.val = 'T';
+
+ if(F_ON(F_ENABLE_AGG_OPS, ps_global)){
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n].label.string = "[Un]Select Current";
+ view_index_popup[n++].data.val = ':';
+ }
+ }
+
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n].label.string = "&WhereIs";
+ view_index_popup[n++].data.val = 'W';
+
+ view_index_popup[n++].type = tSeparator;
+ }
+
+ if(km == &simple_index_keymenu){
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n].label.string = "&Exit Select";
+ view_index_popup[n++].data.val = 'E';
+ }
+ else if(km == &index_keymenu && THREADING() && sp_viewing_a_thread(stream)){
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n].label.string = "Thread Index";
+ view_index_popup[n++].data.val = '<';
+ }
+ else{
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n].label.string = "Folder &List";
+ view_index_popup[n++].data.val = '<';
+ }
+
+ view_index_popup[n].type = tQueue;
+ view_index_popup[n].label.style = lNormal;
+ view_index_popup[n].label.string = "&Main Menu";
+ view_index_popup[n++].data.val = 'M';
+
+ view_index_popup[n].type = tTail;
+
+ if(mswin_popup(view_index_popup) == view_in_new_wind_index
+ && view_in_new_wind_index >= 0)
+ view_in_new_window();
+}
+
+
+char *
+pcpine_help_index(title)
+ char *title;
+{
+ /*
+ * Title is size 256 in pico. Put in args.
+ */
+ if(title)
+ strncpy(title, "Alpine MESSAGE INDEX Help", 256);
+
+ return(pcpine_help(h_mail_index));
+}
+
+char *
+pcpine_help_index_simple(title)
+ char *title;
+{
+ /*
+ * Title is size 256 in pico. Put in args.
+ */
+ if(title)
+ strncpy(title, "Alpine SELECT MESSAGE Help", 256);
+
+ return(pcpine_help(h_simple_index));
+}
+
+
+#include "../pico/osdep/mswin_tw.h"
+
+
+void
+view_in_new_window(void)
+{
+ char title[GETTEXT_TITLELEN+1];
+ void *text;
+ long len;
+ int format;
+ MSWIN_TEXTWINDOW *mswin_tw;
+
+ /* Launch text in alt window. */
+ if(index_gettext_callback(title, sizeof (title), &text, &len, &format)){
+ if(format == GETTEXT_TEXT)
+ mswin_tw = mswin_displaytext(title, text, (size_t) len, NULL,
+ NULL, MSWIN_DT_USEALTWINDOW);
+ else if(format == GETTEXT_LINES)
+ mswin_tw = mswin_displaytext(title, NULL, 0, text,
+ NULL, MSWIN_DT_USEALTWINDOW);
+
+ if(mswin_tw)
+ mswin_set_readonly(mswin_tw, FALSE);
+ }
+}
+
+#endif /* _WINDOWS */
diff --git a/alpine/mailindx.h b/alpine/mailindx.h
new file mode 100644
index 00000000..3b4291c2
--- /dev/null
+++ b/alpine/mailindx.h
@@ -0,0 +1,114 @@
+/*
+ * $Id: mailindx.h 770 2007-10-24 00:23:09Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_MAILINDX_INCLUDED
+#define PINE_MAILINDX_INCLUDED
+
+
+#include "../pith/mailindx.h"
+#include "context.h"
+#include "keymenu.h"
+#include "../pith/state.h"
+#include "../pith/msgno.h"
+#include "../pith/indxtype.h"
+#include "../pith/thread.h"
+#include "../pith/color.h"
+
+
+/*-----------
+ Saved state to redraw message index body
+ ----*/
+struct entry_state {
+ unsigned hilite:1;
+ unsigned bolded:1;
+ unsigned already_erased:1;
+ int plus;
+ long msgno; /* last msgno we drew */
+ unsigned long id;
+};
+
+
+struct index_state {
+ long msg_at_top,
+ lines_per_page;
+ struct entry_state *entry_state;
+ MSGNO_S *msgmap;
+ MAILSTREAM *stream;
+ IndexColType status_fld; /* field for select X's */
+ int status_col; /* column for select X's */
+ IndexColType plus_fld; /* field for threading '+' or '>' */
+ int plus_col; /* column for threading '+' or '>' */
+ IndexColType arrow_fld; /* field for cursor arrow */
+};
+
+
+#define AC_NONE 0x00 /* flags modifying apply_command */
+#define AC_FROM_THREAD 0x01 /* called from thread_command */
+#define AC_COLL 0x02 /* offer collapse command */
+#define AC_EXPN 0x04 /* offer expand command */
+#define AC_UNSEL 0x08 /* all selected, offer UnSelect */
+
+
+/*
+ * Macro to reveal horizontal scroll margin. It can be no greater than
+ * half the number of lines on the display...
+ */
+#define HS_MAX_MARGIN(p) (((p)->ttyo->screen_rows-FOOTER_ROWS(p)-3)/2)
+#define HS_MARGIN(p) MIN((p)->scroll_margin, HS_MAX_MARGIN(p))
+
+
+/*
+ * Flags to indicate how much index border to paint
+ */
+#define INDX_CLEAR 0x01
+#define INDX_HEADER 0x02
+#define INDX_FOOTER 0x04
+
+
+typedef enum {MsgIndex, MultiMsgIndex, ZoomIndex, ThreadIndex} IndexType;
+
+
+/*
+ * Macro to simplify clearing body portion of pine's display
+ */
+#define ClearBody() ClearLines(1, ps_global->ttyo->screen_rows \
+ - FOOTER_ROWS(ps_global) - 1)
+
+
+extern struct index_state *current_index_state;
+
+
+/* exported protoypes */
+void mail_index_screen(struct pine *);
+struct key_menu *do_index_border(CONTEXT_S *, char *, MAILSTREAM *, MSGNO_S *, IndexType, int *, int);
+void *stop_threading_temporarily(void);
+void restore_threading(void **);
+int index_lister(struct pine *, CONTEXT_S *, char *, MAILSTREAM *, MSGNO_S *);
+ICE_S *build_header_line(struct pine *, MAILSTREAM *, MSGNO_S *, long, int *);
+int condensed_thread_cue(PINETHRD_S *, ICE_S *, char **, size_t *, int, int);
+int truncate_subj_and_from_strings(void);
+void paint_index_hline(MAILSTREAM *, long, ICE_S *);
+void setup_index_state(int);
+void warn_other_cmds(void);
+void thread_command(struct pine *, MAILSTREAM *, MSGNO_S *, UCS, int);
+COLOR_PAIR *apply_rev_color(COLOR_PAIR *, int);
+#ifdef _WINDOWS
+int index_sort_callback(int, long);
+void view_in_new_window(void);
+#endif
+
+
+#endif /* PINE_MAILINDX_INCLUDED */
diff --git a/alpine/mailpart.c b/alpine/mailpart.c
new file mode 100644
index 00000000..8286d79d
--- /dev/null
+++ b/alpine/mailpart.c
@@ -0,0 +1,3981 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: mailpart.c 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ mailpart.c
+ The meat and pototoes of attachment processing here.
+
+ ====*/
+
+#include "headers.h"
+#include "mailpart.h"
+#include "status.h"
+#include "context.h"
+#include "keymenu.h"
+#include "alpine.h"
+#include "reply.h"
+#include "radio.h"
+#include "takeaddr.h"
+#include "mailview.h"
+#include "mailindx.h"
+#include "mailcmd.h"
+#include "help.h"
+#include "titlebar.h"
+#include "signal.h"
+#include "send.h"
+#include "busy.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/store.h"
+#include "../pith/msgno.h"
+#include "../pith/detach.h"
+#include "../pith/handle.h"
+#include "../pith/filter.h"
+#include "../pith/bitmap.h"
+#include "../pith/charset.h"
+#include "../pith/mimedesc.h"
+#include "../pith/mailcap.h"
+#include "../pith/newmail.h"
+#include "../pith/rfc2231.h"
+#include "../pith/flag.h"
+#include "../pith/text.h"
+#include "../pith/editorial.h"
+#include "../pith/save.h"
+#include "../pith/pipe.h"
+#include "../pith/util.h"
+#include "../pith/detoken.h"
+#include "../pith/busy.h"
+#include "../pith/mimetype.h"
+#include "../pith/icache.h"
+#include "../pith/list.h"
+#include "../pith/ablookup.h"
+#include "../pith/options.h"
+#include "../pith/smime.h"
+
+
+/*
+ * Information used to paint and maintain a line on the attachment
+ * screen.
+ */
+typedef struct atdisp_line {
+ char *dstring; /* alloc'd var value string */
+ ATTACH_S *attp; /* actual attachment pointer */
+ struct atdisp_line *next, *prev;
+} ATDISP_S;
+
+
+/*
+ * struct defining attachment screen's current state
+ */
+typedef struct att_screen {
+ ATDISP_S *current,
+ *top_line;
+ COLOR_PAIR *titlecolor;
+} ATT_SCREEN_S;
+
+static ATT_SCREEN_S *att_screen;
+
+
+#define next_attline(p) ((p) ? (p)->next : NULL)
+#define prev_attline(p) ((p) ? (p)->prev : NULL)
+
+
+#ifdef _WINDOWS
+/*
+ * local global pointer to scroll attachment popup menu
+ */
+static ATTACH_S *scrat_attachp;
+#endif
+
+
+#define FETCH_READC g_fr_desc->readc
+
+
+/* used to keep track of detached MIME segments total length */
+static long save_att_length;
+
+/*
+ * Internal Prototypes
+ */
+ATDISP_S *new_attline(ATDISP_S **);
+void free_attline(ATDISP_S **);
+int attachment_screen_updater(struct pine *, ATDISP_S *, ATT_SCREEN_S *);
+void attachment_screen_redrawer(void);
+void update_att_screen_titlebar(void);
+ATDISP_S *first_attline(ATDISP_S *);
+int init_att_progress(char *, MAILSTREAM *, BODY *);
+long save_att_piped(int);
+int save_att_percent(void);
+void save_attachment(int, long, ATTACH_S *);
+void export_attachment(int, long, ATTACH_S *);
+char *write_attached_msg(long, ATTACH_S **, STORE_S *, int);
+void save_msg_att(long, ATTACH_S *);
+void save_digest_att(long, ATTACH_S *);
+int save_msg_att_work(long int, ATTACH_S *, MAILSTREAM *, char *,
+ CONTEXT_S *, char *);
+void export_msg_att(long, ATTACH_S *);
+void export_digest_att(long, ATTACH_S *);
+void print_attachment(int, long, ATTACH_S *);
+int print_msg_att(long, ATTACH_S *, int);
+void print_digest_att(long, ATTACH_S *);
+void run_viewer(char *, BODY *, int);
+STORE_S *format_text_att(long, ATTACH_S *, HANDLE_S **);
+int display_text_att(long, ATTACH_S *, int);
+int display_msg_att(long, ATTACH_S *, int);
+void display_digest_att(long, ATTACH_S *, int);
+int scroll_attachment(char *, STORE_S *, SourceType, HANDLE_S *, ATTACH_S *, int);
+int process_attachment_cmd(int, MSGNO_S *, SCROLL_S *);
+int format_msg_att(long, ATTACH_S **, HANDLE_S **, gf_io_t, int);
+void display_vcard_att(long, ATTACH_S *, int);
+void display_attach_info(long, ATTACH_S *);
+void forward_attachment(MAILSTREAM *, long, ATTACH_S *);
+void forward_msg_att(MAILSTREAM *, long, ATTACH_S *);
+void reply_msg_att(MAILSTREAM *, long, ATTACH_S *);
+void bounce_msg_att(MAILSTREAM *, long, char *, char *);
+void pipe_attachment(long, ATTACH_S *);
+int delete_attachment(long, ATTACH_S *);
+int undelete_attachment(long, ATTACH_S *, int *);
+#ifdef _WINDOWS
+int scroll_att_popup(SCROLL_S *, int);
+void display_text_att_window(ATTACH_S *);
+void display_msg_att_window(ATTACH_S *);
+#endif
+
+
+/*----------------------------------------------------------------------
+ Provide attachments in browser for selected action
+
+ Args: ps -- pointer to pine structure
+ msgmap -- struct containing current message to display
+
+ Result:
+
+ Handle painting and navigation of attachment index. It would be nice
+ to someday handle message/rfc822 segments in a neat way (i.e., enable
+ forwarding, take address, etc.).
+ ----*/
+void
+attachment_screen(struct pine *ps)
+{
+ UCS ch = 'x';
+ int n, cmd, dline,
+ maxnumwid = 0, maxsizewid = 0, old_cols = -1, km_popped = 0, expbits,
+ last_type = TYPEOTHER;
+ long msgno;
+ char *q, *last_subtype = NULL, backtag[64], *utf8str;
+ OtherMenu what;
+ ATTACH_S *a;
+ ATDISP_S *current = NULL, *ctmp = NULL;
+ ATT_SCREEN_S screen;
+
+ ps->prev_screen = attachment_screen;
+ ps->next_screen = SCREEN_FUN_NULL;
+
+ ps->some_quoting_was_suppressed = 0;
+
+ if(ps->ttyo->screen_rows - HEADER_ROWS(ps) - FOOTER_ROWS(ps) < 1){
+ q_status_message(SM_ORDER | SM_DING, 0, 3,
+ _("Screen too small to view attachment"));
+ ps->next_screen = mail_view_screen;
+ return;
+ }
+
+ if(mn_total_cur(ps->msgmap) > 1L){
+ q_status_message(SM_ORDER | SM_DING, 0, 3,
+ _("Can only view one message's attachments at a time."));
+ return;
+ }
+ else if(ps->atmts && ps->atmts->description && !(ps->atmts + 1)->description)
+ q_status_message1(SM_ASYNC, 0, 3,
+ _("Message %s has only one part (the message body), and no attachments."),
+ long2string(mn_get_cur(ps->msgmap)));
+
+ /*----- Figure max display widths -----*/
+ for(a = ps->atmts; a && a->description != NULL; a++){
+ if((n = utf8_width(a->number)) > maxnumwid)
+ maxnumwid = n;
+
+ if((n = utf8_width(a->size)) > maxsizewid)
+ maxsizewid = n;
+ }
+
+ /*
+ * Then, allocate and initialize attachment line list...
+ */
+ for(a = ps->atmts; a && a->description; a++)
+ new_attline(&current)->attp = a;
+
+ memset(&screen, 0, sizeof(screen));
+ msgno = mn_m2raw(ps->msgmap, mn_get_cur(ps->msgmap));
+ ps->mangled_screen = 1; /* build display */
+ ps->redrawer = attachment_screen_redrawer;
+ att_screen = &screen;
+ current = first_attline(current);
+ what = FirstMenu;
+
+ /*
+ * Advance to next attachment if it's likely that we've already
+ * shown it to the user...
+ */
+
+ if (current == NULL){
+ q_status_message1(SM_ORDER | SM_DING, 0, 3,
+ _("Malformed message: %s"),
+ ps->c_client_error ? ps->c_client_error : "?");
+ ps->next_screen = mail_view_screen;
+ }
+ else if(current->attp->shown && (ctmp = next_attline(current)))
+ current = ctmp;
+
+ while(ps->next_screen == SCREEN_FUN_NULL){
+ ps->user_says_cancel = 0;
+ if(km_popped){
+ km_popped--;
+ if(km_popped == 0){
+ clearfooter(ps);
+ ps->mangled_body = 1;
+ }
+ }
+
+ if(ps->mangled_screen){
+ /*
+ * build/rebuild display lines
+ */
+ if(old_cols != ps->ttyo->screen_cols){
+ int avail, s1, s2, s4, s5, dwid, sizewid, descwid;
+
+ avail = ps_global->ttyo->screen_cols;
+
+ s1 = MAX(MIN(1, avail), 0);
+ avail -= s1;
+
+ dwid = MAX(MIN(1, avail), 0);
+ avail -= dwid;
+
+ s2 = MAX(MIN(1, avail), 0);
+ avail -= s2;
+
+ /* Only give up a third of the display to part numbers */
+ maxnumwid = MIN(maxnumwid, (ps_global->ttyo->screen_cols/3));
+ maxnumwid = MAX(MIN(maxnumwid, avail), 0);
+ avail -= maxnumwid;
+
+ s4 = MAX(MIN(3, avail), 0);
+ avail -= s4;
+
+ sizewid = MAX(MIN(maxsizewid, avail), 0);
+ avail -= sizewid;
+
+ s5 = MAX(MIN(3, avail), 0);
+ avail -= s5;
+
+ descwid = MAX(0, avail);
+
+ old_cols = ps->ttyo->screen_cols;
+
+ for(ctmp = first_attline(current);
+ ctmp && (a = ctmp->attp) && a->description;
+ ctmp = next_attline(ctmp)){
+ size_t len;
+ char numbuf[50];
+ char description[1000];
+
+ if(ctmp->dstring)
+ fs_give((void **)&ctmp->dstring);
+
+ len = 6 * MAX(80, ps->ttyo->screen_cols) * sizeof(char);
+ ctmp->dstring = (char *)fs_get(len + 1);
+
+ /*
+ * description
+ */
+ q = a->body->description;
+ if(!(q && q[0]) && (MIME_MSG_A(a) && a->body->nested.msg->env))
+ q = a->body->nested.msg->env->subject;
+
+ if(q && q[0]){
+ char buftmp[1000];
+
+ strncpy(buftmp, q, sizeof(buftmp));
+ buftmp[sizeof(buftmp)-1] = '\0';
+
+ q = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
+ SIZEOF_20KBUF, buftmp);
+ }
+
+ if(q && !q[0])
+ q = NULL;
+
+ snprintf(description, sizeof(description), "%s%s%s%s", type_desc(a->body->type, a->body->subtype, a->body->parameter, a->body->disposition.type ? a->body->disposition.parameter : NULL, 1), q ? ", \"" : "", q ? q : "", q ? "\"" : "");
+ description[sizeof(description)-1] = '\0';
+
+ utf8_snprintf(ctmp->dstring, len+1,
+ "%*.*s%*.*w%*.*s%-*.*w%*.*s%*.*w%*.*s%-*.*w",
+ s1, s1, "",
+ dwid, dwid,
+ msgno_part_deleted(ps->mail_stream, msgno, a->number) ? "D" : "",
+ s2, s2, "",
+ maxnumwid, maxnumwid,
+ a->number
+ ? short_str(a->number, numbuf, sizeof(numbuf), maxnumwid, FrontDots)
+ : "",
+ s4, s4, "",
+ sizewid, sizewid,
+ a->size ? a->size : "",
+ s5, s5, "",
+ descwid, descwid, description);
+
+ ctmp->dstring[len] = '\0';
+ }
+ }
+
+ ps->mangled_header = 1;
+ ps->mangled_footer = 1;
+ ps->mangled_body = 1;
+ }
+
+ /*----------- Check for new mail -----------*/
+ if(new_mail(0, NM_TIMING(ch), NM_STATUS_MSG | NM_DEFER_SORT) >= 0)
+ ps->mangled_header = 1;
+
+ /*
+ * If an expunge of the current message happened during the
+ * new mail check we want to bail out of here. See mm_expunged.
+ */
+ if(ps->next_screen != SCREEN_FUN_NULL)
+ break;
+
+ if(ps->mangled_header){
+ update_att_screen_titlebar();
+ ps->mangled_header = 0;
+ }
+
+ if(ps->mangled_screen){
+ ClearLine(1);
+ ps->mangled_screen = 0;
+ }
+
+ dline = attachment_screen_updater(ps, current, &screen);
+
+ /*---- This displays new mail notification, or errors ---*/
+ if(km_popped){
+ FOOTER_ROWS(ps) = 3;
+ mark_status_unknown();
+ }
+
+ display_message(ch);
+ if(km_popped){
+ FOOTER_ROWS(ps) = 1;
+ mark_status_unknown();
+ }
+
+ if(ps->mangled_footer
+ || current->attp->body->type != last_type
+ || !(last_subtype && current->attp->body->subtype)
+ || strucmp(last_subtype, current->attp->body->subtype)){
+
+ struct key_menu *km = &att_index_keymenu;
+ bitmap_t bitmap;
+
+ setbitmap(bitmap);
+ ps->mangled_footer = 0;
+ last_type = current->attp->body->type;
+ last_subtype = current->attp->body->subtype;
+
+ snprintf(backtag, sizeof(backtag), "Msg #%ld", mn_get_cur(ps->msgmap));
+ backtag[sizeof(backtag)-1] = '\0';
+ km->keys[ATT_PARENT_KEY].label = backtag;
+
+ if(F_OFF(F_ENABLE_PIPE, ps))
+ clrbitn(ATT_PIPE_KEY, bitmap);
+
+ /*
+ * If message or digest, leave Reply and Save and,
+ * conditionally, Bounce on...
+ */
+ if(MIME_MSG(last_type, last_subtype)){
+ if(F_OFF(F_ENABLE_BOUNCE, ps))
+ clrbitn(ATT_BOUNCE_KEY, bitmap);
+
+ km->keys[ATT_EXPORT_KEY].name = "";
+ km->keys[ATT_EXPORT_KEY].label = "";
+ }
+ else if(MIME_DGST(last_type, last_subtype)){
+ clrbitn(ATT_BOUNCE_KEY, bitmap);
+ clrbitn(ATT_REPLY_KEY, bitmap);
+
+ km->keys[ATT_EXPORT_KEY].name = "";
+ km->keys[ATT_EXPORT_KEY].label = "";
+ }
+ else{
+ clrbitn(ATT_BOUNCE_KEY, bitmap);
+ clrbitn(ATT_REPLY_KEY, bitmap);
+
+ if(last_type != TYPETEXT)
+ clrbitn(ATT_PRINT_KEY, bitmap);
+
+ km->keys[ATT_EXPORT_KEY].name = "E";
+ km->keys[ATT_EXPORT_KEY].label = N_("Export");
+ }
+
+ if(km_popped){
+ FOOTER_ROWS(ps) = 3;
+ clearfooter(ps);
+ }
+
+ if(F_ON(F_ARROW_NAV, ps)){
+ menu_add_binding(km, KEY_LEFT, MC_EXIT);
+ menu_add_binding(km, KEY_RIGHT, MC_VIEW_ATCH);
+ }
+ else{
+ menu_clear_binding(km, KEY_LEFT);
+ menu_clear_binding(km, KEY_RIGHT);
+ }
+
+ draw_keymenu(km, bitmap, ps->ttyo->screen_cols, 1-FOOTER_ROWS(ps),
+ 0, what);
+ what = SameMenu;
+ if(km_popped){
+ FOOTER_ROWS(ps) = 1;
+ mark_keymenu_dirty();
+ }
+ }
+
+ if(F_ON(F_SHOW_CURSOR, ps))
+ MoveCursor(dline, 0);
+ else
+ MoveCursor(MAX(0, ps->ttyo->screen_rows - FOOTER_ROWS(ps)), 0);
+
+ /*------ Prepare to read the command from the keyboard ----*/
+#ifdef MOUSE
+ mouse_in_content(KEY_MOUSE, -1, -1, 0, 0); /* prime the handler */
+ register_mfunc(mouse_in_content, HEADER_ROWS(ps_global), 0,
+ ps_global->ttyo->screen_rows -(FOOTER_ROWS(ps_global)+1),
+ ps_global->ttyo->screen_cols);
+#endif
+ ch = READ_COMMAND(&utf8str);
+#ifdef MOUSE
+ clear_mfunc(mouse_in_content);
+#endif
+
+ cmd = menu_command(ch, &att_index_keymenu);
+
+ if(km_popped)
+ switch(cmd){
+ case MC_NONE :
+ case MC_OTHER :
+ case MC_RESIZE :
+ case MC_REPAINT :
+ km_popped++;
+ break;
+
+ default:
+ clearfooter(ps);
+ break;
+ }
+
+ switch(cmd){
+ case MC_HELP :
+ if(FOOTER_ROWS(ps) == 1 && km_popped == 0){
+ km_popped = 2;
+ ps->mangled_footer = 1;
+ break;
+ }
+
+ helper(h_attachment_screen, _("HELP FOR ATTACHMENT INDEX"), 0);
+ ps->mangled_screen = 1;
+ break;
+
+ case MC_OTHER :
+ what = NextMenu;
+ ps->mangled_footer = 1;
+ break;
+
+ case MC_FULLHDR :
+ ps->full_header++;
+ if(ps->full_header == 1){
+ if(!(ps->quote_suppression_threshold
+ && (ps->some_quoting_was_suppressed /* || in_index != View */)))
+ ps->full_header++;
+ }
+ else if(ps->full_header > 2)
+ ps->full_header = 0;
+
+ switch(ps->full_header){
+ case 0:
+ q_status_message(SM_ORDER, 0, 3,
+ _("Display of full headers is now off."));
+ break;
+
+ case 1:
+ q_status_message1(SM_ORDER, 0, 3,
+ _("Quotes displayed, use %s to see full headers"),
+ F_ON(F_USE_FK, ps) ? "F9" : "H");
+ break;
+
+ case 2:
+ q_status_message(SM_ORDER, 0, 3,
+ _("Display of full headers is now on."));
+ break;
+
+ }
+
+ break;
+
+ case MC_EXIT : /* exit attachment screen */
+ ps->next_screen = mail_view_screen;
+ break;
+
+ case MC_QUIT :
+ ps->next_screen = quit_screen;
+ break;
+
+ case MC_MAIN :
+ ps->next_screen = main_menu_screen;
+ break;
+
+ case MC_INDEX :
+ ps->next_screen = mail_index_screen;
+ break;
+
+ case MC_NEXTITEM :
+ if((ctmp = next_attline(current)) != NULL)
+ current = ctmp;
+ else
+ q_status_message(SM_ORDER, 0, 1, _("Already on last attachment"));
+
+ break;
+
+ case MC_PREVITEM :
+ if((ctmp = prev_attline(current)) != NULL)
+ current = ctmp;
+ else
+ q_status_message(SM_ORDER, 0, 1, _("Already on first attachment"));
+
+ break;
+
+ case MC_PAGEDN : /* page forward */
+ if(next_attline(current)){
+ while(dline++ < ps->ttyo->screen_rows - FOOTER_ROWS(ps))
+ if((ctmp = next_attline(current)) != NULL)
+ current = ctmp;
+ else
+ break;
+ }
+ else
+ q_status_message(SM_ORDER, 0, 1,
+ _("Already on last page of attachments"));
+
+ break;
+
+ case MC_PAGEUP : /* page backward */
+ if(prev_attline(current)){
+ while(dline-- > HEADER_ROWS(ps))
+ if((ctmp = prev_attline(current)) != NULL)
+ current = ctmp;
+ else
+ break;
+
+ while(++dline < ps->ttyo->screen_rows - FOOTER_ROWS(ps))
+ if((ctmp = prev_attline(current)) != NULL)
+ current = ctmp;
+ else
+ break;
+ }
+ else
+ q_status_message(SM_ORDER, 0, 1,
+ _("Already on first page of attachments"));
+
+ break;
+
+#ifdef MOUSE
+ case MC_MOUSE:
+ {
+ MOUSEPRESS mp;
+
+ mouse_get_last (NULL, &mp);
+ mp.row -= HEADER_ROWS(ps);
+ ctmp = screen.top_line;
+ while (mp.row && ctmp != NULL) {
+ --mp.row;
+ ctmp = ctmp->next;
+ }
+
+ if (ctmp != NULL){
+ current = ctmp;
+
+ if (mp.doubleclick){
+ if(mp.button == M_BUTTON_LEFT)
+ display_attachment(msgno, current->attp, DA_SAVE);
+ }
+#ifdef _WINDOWS
+ else if(mp.button == M_BUTTON_RIGHT){
+ MPopup atmt_popup[20];
+ int i = -1, exoffer = 0;
+
+ dline = attachment_screen_updater(ps,current,&screen);
+
+ if(dispatch_attachment(current->attp) != MCD_NONE){
+ atmt_popup[++i].type = tQueue;
+ atmt_popup[i].data.val = 'V';
+ atmt_popup[i].label.style = lNormal;
+ atmt_popup[i].label.string = "&View";
+
+ if(!(current->attp->can_display & MCD_EXTERNAL)
+ && (current->attp->body->type == TYPETEXT
+ || MIME_MSG_A(current->attp)
+ || MIME_DGST_A(current->attp))){
+ exoffer++;
+ atmt_popup[++i].type = tIndex;
+ atmt_popup[i].label.style = lNormal;
+ atmt_popup[i].label.string =
+ "View in New Window";
+ }
+ }
+
+ atmt_popup[++i].type = tQueue;
+ atmt_popup[i].data.val = 'S';
+ atmt_popup[i].label.style = lNormal;
+ atmt_popup[i].label.string = "&Save";
+
+ atmt_popup[++i].type = tQueue;
+ atmt_popup[i].label.style = lNormal;
+ if(current->dstring[1] == 'D'){
+ atmt_popup[i].data.val = 'U';
+ atmt_popup[i].label.string = "&Undelete";
+ }
+ else{
+ atmt_popup[i].data.val = 'D';
+ atmt_popup[i].label.string = "&Delete";
+ }
+
+ if(MIME_MSG_A(current->attp)){
+ atmt_popup[++i].type = tQueue;
+ atmt_popup[i].label.style = lNormal;
+ atmt_popup[i].label.string = "&Reply...";
+ atmt_popup[i].data.val = 'R';
+
+ atmt_popup[++i].type = tQueue;
+ atmt_popup[i].label.style = lNormal;
+ atmt_popup[i].label.string = "&Forward...";
+ atmt_popup[i].data.val = 'F';
+ }
+
+ atmt_popup[++i].type = tQueue;
+ atmt_popup[i].label.style = lNormal;
+ atmt_popup[i].label.string = "&About...";
+ atmt_popup[i].data.val = 'A';
+
+ atmt_popup[++i].type = tSeparator;
+
+ atmt_popup[++i].type = tQueue;
+ snprintf(backtag, sizeof(backtag), "View Message #%ld",
+ mn_get_cur(ps->msgmap));
+ backtag[sizeof(backtag)-1] = '\0';
+ atmt_popup[i].label.string = backtag;
+ atmt_popup[i].label.style = lNormal;
+ atmt_popup[i].data.val = '<';
+
+ atmt_popup[++i].type = tTail;
+
+ if(mswin_popup(atmt_popup) == 1 && exoffer)
+ display_att_window(current->attp);
+ }
+ }
+ else if(mp.button == M_BUTTON_RIGHT){
+ MPopup atmt_popup[2];
+
+ atmt_popup[0].type = tQueue;
+ snprintf(backtag, sizeof(backtag), "View Message #%ld",
+ mn_get_cur(ps->msgmap));
+ backtag[sizeof(backtag)-1] = '\0';
+ atmt_popup[0].label.string = backtag;
+ atmt_popup[0].label.style = lNormal;
+ atmt_popup[0].data.val = '<';
+
+ atmt_popup[1].type = tTail;
+
+ mswin_popup(atmt_popup);
+#endif
+ }
+ }
+ break;
+#endif
+
+ case MC_WHEREIS : /* whereis */
+ /*--- get string ---*/
+ {int rc, found = 0;
+ char *result = NULL, buf[64];
+ static char last[64], tmp[64];
+ HelpType help;
+
+ ps->mangled_footer = 1;
+ buf[0] = '\0';
+ snprintf(tmp, sizeof(tmp), "Word to find %s%s%s: ",
+ (last[0]) ? "[" : "",
+ (last[0]) ? last : "",
+ (last[0]) ? "]" : "");
+ tmp[sizeof(tmp)-1] = '\0';
+ help = NO_HELP;
+ while(1){
+ int flags = OE_APPEND_CURRENT | OE_SEQ_SENSITIVE;
+
+ rc = optionally_enter(buf,-FOOTER_ROWS(ps),0,sizeof(buf),
+ tmp,NULL,help,&flags);
+ if(rc == 3)
+ help = help == NO_HELP ? h_attach_index_whereis : NO_HELP;
+ else if(rc == 0 || rc == 1 || !buf[0]){
+ if(rc == 0 && !buf[0] && last[0]){
+ strncpy(buf, last, sizeof(buf));
+ buf[sizeof(buf)-1] = '\0';
+ }
+
+ break;
+ }
+ }
+
+ if(rc == 0 && buf[0]){
+ ctmp = current;
+ while((ctmp = next_attline(ctmp)) != NULL)
+ if(srchstr(ctmp->dstring, buf)){
+ found++;
+ break;
+ }
+
+ if(!found){
+ ctmp = first_attline(current);
+
+ while(ctmp != current)
+ if(srchstr(ctmp->dstring, buf)){
+ found++;
+ break;
+ }
+ else
+ ctmp = next_attline(ctmp);
+ }
+ }
+ else
+ result = _("WhereIs cancelled");
+
+ if(found && ctmp){
+ strncpy(last, buf, sizeof(last));
+ last[sizeof(last)-1] = '\0';
+ result = "Word found";
+ current = ctmp;
+ }
+
+ q_status_message(SM_ORDER, 0, 3,
+ result ? result : _("Word not found"));
+ }
+
+ break;
+
+ case MC_DELETE :
+ if(delete_attachment(msgno, current->attp)){
+ int l = current ? strlen(current->attp->number) : 0;
+
+ current->dstring[1] = 'D';
+
+ /* Also indicate any children that will be deleted */
+
+ for(ctmp = current; ctmp; ctmp = next_attline(ctmp))
+ if(!strncmp(ctmp->attp->number, current->attp->number, l)
+ && ctmp->attp->number[l] == '.'){
+ ctmp->dstring[1] = 'D';
+ ps->mangled_screen = 1;
+ }
+ }
+
+ break;
+
+ case MC_UNDELETE :
+ if(undelete_attachment(msgno, current->attp, &expbits)){
+ int l = current ? strlen(current->attp->number) : 0;
+
+ current->dstring[1] = ' ';
+
+ /* And unflag anything implicitly undeleted */
+ for(ctmp = current; ctmp; ctmp = next_attline(ctmp))
+ if(!strncmp(ctmp->attp->number, current->attp->number, l)
+ && ctmp->attp->number[l] == '.'
+ && (!msgno_exceptions(ps->mail_stream, msgno,
+ ctmp->attp->number, &expbits, FALSE)
+ || !(expbits & MSG_EX_DELETE))){
+ ctmp->dstring[1] = ' ';
+ ps->mangled_screen = 1;
+ }
+ }
+
+ break;
+
+ case MC_REPLY :
+ reply_msg_att(ps->mail_stream, msgno, current->attp);
+ break;
+
+ case MC_FORWARD :
+ forward_attachment(ps->mail_stream, msgno, current->attp);
+ break;
+
+ case MC_BOUNCE :
+ bounce_msg_att(ps->mail_stream, msgno, current->attp->number,
+ current->attp->body->nested.msg->env->subject);
+ ps->mangled_footer = 1;
+ break;
+
+ case MC_ABOUTATCH :
+ display_attach_info(msgno, current->attp);
+ break;
+
+ case MC_VIEW_ATCH : /* View command */
+ display_attachment(msgno, current->attp, DA_SAVE);
+ old_cols = -1;
+ /* fall thru to repaint */
+
+ case MC_REPAINT : /* redraw */
+ case MC_RESIZE :
+ ps->mangled_screen = 1;
+ break;
+
+ case MC_EXPORT :
+ export_attachment(-FOOTER_ROWS(ps), msgno, current->attp);
+ ps->mangled_footer = 1;
+ break;
+
+ case MC_SAVETEXT : /* Save command */
+ save_attachment(-FOOTER_ROWS(ps), msgno, current->attp);
+ ps->mangled_footer = 1;
+ break;
+
+ case MC_PRINTMSG : /* Save command */
+ print_attachment(-FOOTER_ROWS(ps), msgno, current->attp);
+ ps->mangled_footer = 1;
+ break;
+
+ case MC_PIPE : /* Pipe command */
+ if(F_ON(F_ENABLE_PIPE, ps)){
+ pipe_attachment(msgno, current->attp);
+ ps->mangled_footer = 1;
+ break;
+ } /* fall thru to complain */
+
+ case MC_NONE: /* simple timeout */
+ break;
+
+ case MC_UTF8:
+ bogus_utf8_command(utf8str, F_ON(F_USE_FK, ps) ? "F1" : "?");
+ break;
+
+ case MC_CHARUP :
+ case MC_CHARDOWN :
+ case MC_CHARRIGHT :
+ case MC_CHARLEFT :
+ case MC_GOTOBOL :
+ case MC_GOTOEOL :
+ case MC_UNKNOWN :
+ default:
+ bogus_command(ch, F_ON(F_USE_FK, ps) ? "F1" : "?");
+
+ }
+ }
+
+ for(current = first_attline(current); current;){ /* clean up */
+ ctmp = current->next;
+ free_attline(&current);
+ current = ctmp;
+ }
+
+ if(screen.titlecolor)
+ free_color_pair(&screen.titlecolor);
+}
+
+
+/*----------------------------------------------------------------------
+ allocate and attach a fresh attachment line struct
+
+ Args: current -- display line to attache new struct to
+
+ Returns: newly alloc'd attachment display line
+ ----*/
+ATDISP_S *
+new_attline(ATDISP_S **current)
+{
+ ATDISP_S *p;
+
+ p = (ATDISP_S *)fs_get(sizeof(ATDISP_S));
+ memset((void *)p, 0, sizeof(ATDISP_S));
+ if(current){
+ if(*current){
+ p->next = (*current)->next;
+ (*current)->next = p;
+ p->prev = *current;
+ if(p->next)
+ p->next->prev = p;
+ }
+
+ *current = p;
+ }
+
+ return(p);
+}
+
+
+/*----------------------------------------------------------------------
+ Release system resources associated with attachment display line
+
+ Args: p -- line to free
+
+ Result:
+ ----*/
+void
+free_attline(ATDISP_S **p)
+{
+ if(p){
+ if((*p)->dstring)
+ fs_give((void **)&(*p)->dstring);
+
+ fs_give((void **)p);
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Manage display of the attachment screen menu body.
+
+ Args: ps -- pine struct pointer
+ current -- currently selected display line
+ screen -- reference points for display painting
+
+ Result:
+ */
+int
+attachment_screen_updater(struct pine *ps, ATDISP_S *current, ATT_SCREEN_S *screen)
+{
+ int dline, return_line = HEADER_ROWS(ps);
+ ATDISP_S *top_line, *ctmp;
+
+ /* calculate top line of display */
+ dline = 0;
+ ctmp = top_line = first_attline(current);
+ do
+ if(((dline++)%(ps->ttyo->screen_rows-HEADER_ROWS(ps)-FOOTER_ROWS(ps)))==0)
+ top_line = ctmp;
+ while(ctmp != current && (ctmp = next_attline(ctmp)));
+
+#ifdef _WINDOWS
+ /* Don't know how to manage scroll bar for attachment screen yet. */
+ scroll_setrange (0L, 0L);
+#endif
+
+ /* mangled body or new page, force redraw */
+ if(ps->mangled_body || screen->top_line != top_line)
+ screen->current = NULL;
+
+ /* loop thru painting what's needed */
+ for(dline = 0, ctmp = top_line;
+ dline < ps->ttyo->screen_rows - FOOTER_ROWS(ps) - HEADER_ROWS(ps);
+ dline++, ctmp = next_attline(ctmp)){
+
+ /*
+ * only fall thru painting if something needs painting...
+ */
+ if(!(!screen->current || ctmp == screen->current || ctmp == current))
+ continue;
+
+ if(ctmp && ctmp->dstring){
+ char *p = tmp_20k_buf;
+ int i, col, x = 0, totlen;
+
+ if(F_ON(F_FORCE_LOW_SPEED,ps) || ps->low_speed){
+ x = 2;
+ if(ctmp == current){
+ return_line = dline + HEADER_ROWS(ps);
+ PutLine0(dline + HEADER_ROWS(ps), 0, "->");
+ }
+ else
+ PutLine0(dline + HEADER_ROWS(ps), 0, " ");
+
+ /*
+ * Only paint lines if we have to...
+ */
+ if(screen->current)
+ continue;
+ }
+ else if(ctmp == current){
+ return_line = dline + HEADER_ROWS(ps);
+ StartInverse();
+ }
+
+ totlen = strlen(ctmp->dstring);
+
+ /*
+ * Copy the value to a temp buffer expanding tabs.
+ * Assume the caller set the widths so as not to overflow the
+ * right margin.
+ */
+ for(i=0,col=x; ctmp->dstring[i]; i++){
+ if(ctmp->dstring[i] == TAB){
+ if((p-tmp_20k_buf) < SIZEOF_20KBUF-8)
+ do
+ *p++ = ' ';
+ while((++col)&0x07);
+ }
+ else{
+ col += width_at_this_position((unsigned char *) &ctmp->dstring[i], totlen-i);
+ if((p-tmp_20k_buf) < SIZEOF_20KBUF)
+ *p++ = ctmp->dstring[i];
+
+ }
+ }
+
+ if((p-tmp_20k_buf) < SIZEOF_20KBUF)
+ *p = '\0';
+
+ PutLine0(dline + HEADER_ROWS(ps), x, tmp_20k_buf + x);
+
+ if(ctmp == current
+ && !(F_ON(F_FORCE_LOW_SPEED,ps) || ps->low_speed))
+ EndInverse();
+ }
+ else
+ ClearLine(dline + HEADER_ROWS(ps));
+ }
+
+ ps->mangled_body = 0;
+ screen->top_line = top_line;
+ screen->current = current;
+ return(return_line);
+}
+
+
+/*----------------------------------------------------------------------
+ Redraw the attachment screen based on the global "att_screen" struct
+
+ Args: none
+
+ Result:
+ ----*/
+void
+attachment_screen_redrawer(void)
+{
+ bitmap_t bitmap;
+
+ update_att_screen_titlebar();
+ ps_global->mangled_header = 0;
+ ClearLine(1);
+
+ ps_global->mangled_body = 1;
+ (void)attachment_screen_updater(ps_global,att_screen->current,att_screen);
+
+ setbitmap(bitmap);
+ draw_keymenu(&att_index_keymenu, bitmap, ps_global->ttyo->screen_cols,
+ 1-FOOTER_ROWS(ps_global), 0, SameMenu);
+}
+
+
+void
+update_att_screen_titlebar(void)
+{
+ long raw_msgno;
+ COLOR_PAIR *returned_color = NULL;
+ COLOR_PAIR *titlecolor = NULL;
+ int colormatch;
+ SEARCHSET *ss = NULL;
+ PAT_STATE *pstate = NULL;
+
+ if(ps_global->titlebar_color_style != TBAR_COLOR_DEFAULT){
+ raw_msgno = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
+ ss = mail_newsearchset();
+ ss->first = ss->last = (unsigned long) raw_msgno;
+
+ if(ss){
+ colormatch = get_index_line_color(ps_global->mail_stream,
+ ss, &pstate, &returned_color);
+ mail_free_searchset(&ss);
+
+ /*
+ * This is a bit tricky. If there is a colormatch but
+ * returned_color
+ * is NULL, that means that the user explicitly wanted the
+ * Normal color used in this index line, so that is what we
+ * use. If no colormatch then we will use the TITLE color
+ * instead of Normal.
+ */
+ if(colormatch){
+ if(returned_color)
+ titlecolor = returned_color;
+ else
+ titlecolor = new_color_pair(ps_global->VAR_NORM_FORE_COLOR,
+ ps_global->VAR_NORM_BACK_COLOR);
+ }
+
+ if(titlecolor
+ && ps_global->titlebar_color_style == TBAR_COLOR_REV_INDEXLINE){
+ char cbuf[MAXCOLORLEN+1];
+
+ strncpy(cbuf, titlecolor->fg, sizeof(cbuf));
+ cbuf[sizeof(cbuf)-1] = '\0';
+ strncpy(titlecolor->fg, titlecolor->bg, MAXCOLORLEN);
+ titlecolor->fg[MAXCOLORLEN] = '\0';
+ strncpy(titlecolor->bg, cbuf, MAXCOLORLEN);
+ titlecolor->bg[MAXCOLORLEN] = '\0';
+ }
+ }
+
+ /* Did the color change? */
+ if((!titlecolor && att_screen->titlecolor)
+ ||
+ (titlecolor && !att_screen->titlecolor)
+ ||
+ (titlecolor && att_screen->titlecolor
+ && (strcmp(titlecolor->fg, att_screen->titlecolor->fg)
+ || strcmp(titlecolor->bg, att_screen->titlecolor->bg)))){
+
+ if(att_screen->titlecolor)
+ free_color_pair(&att_screen->titlecolor);
+
+ att_screen->titlecolor = titlecolor;
+ titlecolor = NULL;
+ }
+
+ if(titlecolor)
+ free_color_pair(&titlecolor);
+ }
+
+ set_titlebar(_("ATTACHMENT INDEX"), ps_global->mail_stream,
+ ps_global->context_current, ps_global->cur_folder,
+ ps_global->msgmap, 1, MessageNumber, 0, 0,
+ att_screen->titlecolor);
+}
+
+
+/*----------------------------------------------------------------------
+ Seek back from the given display line to the beginning of the list
+
+ Args: p -- display linked list
+
+ Result:
+ ----*/
+ATDISP_S *
+first_attline(ATDISP_S *p)
+{
+ while(p && p->prev)
+ p = p->prev;
+
+ return(p);
+}
+
+
+int
+init_att_progress(char *msg, MAILSTREAM *stream, struct mail_bodystruct *body)
+{
+ if((save_att_length = body->size.bytes) != 0){
+ /* if there are display filters, factor in extra copy */
+ if(body->type == TYPETEXT && ps_global->VAR_DISPLAY_FILTERS)
+ save_att_length += body->size.bytes;
+
+ /* if remote folder and segment not cached, factor in IMAP fetch */
+ if(stream && stream->mailbox && IS_REMOTE(stream->mailbox)
+ && !((body->type == TYPETEXT && body->contents.text.data)
+ || ((body->type == TYPEMESSAGE)
+ && body->nested.msg && body->nested.msg->text.text.data)
+ || body->contents.text.data))
+ save_att_length += body->size.bytes;
+
+ gf_filter_init(); /* reset counters */
+ pine_gets_bytes(1);
+ save_att_piped(1);
+ return(busy_cue(msg, save_att_percent, 0));
+ }
+
+ return(0);
+}
+
+
+long
+save_att_piped(int reset)
+{
+ static long x;
+ long y;
+
+ if(reset){
+ x = y = 0L;
+ }
+ else if((y = gf_bytes_piped()) >= x){
+ x = y;
+ y = 0;
+ }
+
+ return(x + y);
+}
+
+
+int
+save_att_percent(void)
+{
+ int i = (int) (((pine_gets_bytes(0) + save_att_piped(0)) * 100)
+ / save_att_length);
+ return(MIN(i, 100));
+}
+
+
+/*----------------------------------------------------------------------
+ Save the given attachment associated with the given message no
+
+ Args: ps
+
+ Result:
+ ----*/
+void
+save_attachment(int qline, long int msgno, ATTACH_S *a)
+{
+ if(ps_global->restricted){
+ q_status_message(SM_ORDER | SM_DING, 0, 4,
+ "Alpine demo can't save attachments");
+ return;
+ }
+
+ if(MIME_MSG_A(a))
+ save_msg_att(msgno, a);
+ else if(MIME_DGST_A(a))
+ save_digest_att(msgno, a);
+ else if(MIME_VCARD_A(a))
+ save_vcard_att(ps_global, qline, msgno, a);
+ else
+ write_attachment(qline, msgno, a, "SAVE");
+}
+
+
+/*----------------------------------------------------------------------
+ Save the given attachment associated with the given message no
+
+ Args: ps
+
+ Result:
+ ----*/
+void
+export_attachment(int qline, long int msgno, ATTACH_S *a)
+{
+ if(ps_global->restricted){
+ q_status_message(SM_ORDER | SM_DING, 0, 4,
+ "Alpine demo can't export attachments");
+ return;
+ }
+
+ if(MIME_MSG_A(a))
+ export_msg_att(msgno, a);
+ else if(MIME_DGST_A(a))
+ export_digest_att(msgno, a);
+ else
+ q_status_message1(SM_ORDER, 0, 3,
+ _("Can't Export %s. Use \"Save\" to write file, \"<\" to leave index."),
+ body_type_names(a->body->type));
+}
+
+
+void
+write_attachment(int qline, long int msgno, ATTACH_S *a, char *method)
+{
+ char filename[MAXPATH+1], full_filename[MAXPATH+1],
+ title_buf[64], *err;
+ int r, rflags = GER_NONE, we_cancel = 0;
+ static HISTORY_S *history = NULL;
+ static ESCKEY_S att_save_opts[] = {
+ {ctrl('T'), 10, "^T", N_("To Files")},
+ {-1, 0, NULL, NULL},
+ {-1, 0, NULL, NULL},
+ {-1, 0, NULL, NULL}};
+
+ /*------- Figure out suggested file name ----*/
+ filename[0] = full_filename[0] = '\0';
+ (void) get_filename_parameter(filename, sizeof(filename), a->body, NULL);
+
+ dprint((9, "export_attachment(name: %s)\n",
+ filename ? filename : "?"));
+
+ r = 0;
+#if !defined(DOS) && !defined(MAC) && !defined(OS2)
+ if(ps_global->VAR_DOWNLOAD_CMD && ps_global->VAR_DOWNLOAD_CMD[0]){
+ att_save_opts[++r].ch = ctrl('V');
+ att_save_opts[r].rval = 12;
+ att_save_opts[r].name = "^V";
+ att_save_opts[r].label = N_("Downld Msg");
+ }
+#endif /* !(DOS || MAC) */
+
+ if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
+ att_save_opts[++r].ch = ctrl('I');
+ att_save_opts[r].rval = 11;
+ att_save_opts[r].name = "TAB";
+ att_save_opts[r].label = N_("Complete");
+ }
+
+ att_save_opts[++r].ch = -1;
+
+ snprintf(title_buf, sizeof(title_buf), "%s ATTACHMENT", method);
+ title_buf[sizeof(title_buf)-1] = '\0';
+
+ r = get_export_filename(ps_global, filename, NULL, full_filename,
+ sizeof(filename), "attachment", title_buf,
+ att_save_opts, &rflags, qline, GE_SEQ_SENSITIVE, &history);
+
+ if(r < 0){
+ switch(r){
+ case -1:
+ cmd_cancelled((char *) lcase((unsigned char *) title_buf + 1) - 1);
+ break;
+
+ case -2:
+ q_status_message1(SM_ORDER, 0, 2,
+ _("Can't save to file outside of %s"),
+ ps_global->VAR_OPER_DIR);
+ break;
+ }
+
+ return;
+ }
+#if !defined(DOS) && !defined(MAC) && !defined(OS2)
+ else if(r == 12){ /* Download */
+ char cmd[MAXPATH], *tfp = NULL;
+ PIPE_S *syspipe;
+ gf_io_t pc;
+ long len;
+ STORE_S *store;
+ char prompt_buf[256];
+
+ if(ps_global->restricted){
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ "Download disallowed in restricted mode");
+ return;
+ }
+
+ err = NULL;
+ tfp = temp_nam(NULL, "pd");
+ dprint((1, "Download attachment called!\n"));
+ if((store = so_get(FileStar, tfp, WRITE_ACCESS|OWNER_ONLY|WRITE_TO_LOCALE)) != NULL){
+
+ snprintf(prompt_buf, sizeof(prompt_buf), "Saving to \"%s\"", tfp);
+ prompt_buf[sizeof(prompt_buf)-1] = '\0';
+ we_cancel = init_att_progress(prompt_buf,
+ ps_global->mail_stream,
+ a->body);
+
+ gf_set_so_writec(&pc, store);
+ if((err = detach(ps_global->mail_stream, msgno,
+ a->number, 0L, &len, pc, NULL, 0)) != NULL)
+ q_status_message2(SM_ORDER | SM_DING, 3, 5,
+ "%s: Error writing attachment to \"%s\"",
+ err, tfp);
+
+ /* cancel regardless, so it doesn't get in way of xfer */
+ cancel_busy_cue(0);
+
+ gf_clear_so_writec(store);
+ if(so_give(&store)) /* close file */
+ err = "Error writing tempfile for download";
+
+ if(!err){
+ build_updown_cmd(cmd, sizeof(cmd), ps_global->VAR_DOWNLOAD_CMD_PREFIX,
+ ps_global->VAR_DOWNLOAD_CMD, tfp);
+ if((syspipe = open_system_pipe(cmd, NULL, NULL,
+ PIPE_USER | PIPE_RESET,
+ 0, pipe_callback, pipe_report_error)) != NULL)
+ (void)close_system_pipe(&syspipe, NULL, pipe_callback);
+ else
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ err = "Error running download command");
+ }
+ }
+ else
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ err = "Error building temp file for download");
+
+ if(tfp){
+ our_unlink(tfp);
+ fs_give((void **)&tfp);
+ }
+
+ if(!err)
+ q_status_message1(SM_ORDER, 0, 4, "Part %s downloaded",
+ a->number);
+
+ return;
+ }
+#endif /* !(DOS || MAC) */
+
+ (void) write_attachment_to_file(ps_global->mail_stream, msgno, a,
+ rflags, full_filename);
+}
+
+
+/*
+ * Args stream --
+ * msgno -- raw message number
+ * a -- attachment struct
+ * flags -- comes from get_export_filename
+ * GER_OVER -- the file was truncated before we wrote
+ * GER_APPEND -- the file was not truncated prior to our writing,
+ * so we were appending
+ * else -- the file didn't previously exist
+ * file -- the full pathname of the file
+ *
+ * Returns 1 for successful write, 0 for nothing to write, -1 for error
+ */
+int
+write_attachment_to_file(MAILSTREAM *stream, long int msgno, ATTACH_S *a, int flags, char *file)
+{
+ char *l_string, sbuf[256], *err;
+ int is_text, we_cancel = 0;
+ long len, orig_size;
+ gf_io_t pc;
+ STORE_S *store;
+
+ if(!(a && a->body && a->number && a->number[0] && file && file[0]
+ && stream))
+ return 0;
+
+ is_text = (a && a->body && a->body->type == TYPETEXT);
+
+ if(flags & GER_APPEND)
+ orig_size = name_file_size(file);
+
+ store = so_get(FileStar, file, WRITE_ACCESS | (is_text ? WRITE_TO_LOCALE : 0));
+ if(store == NULL){
+ q_status_message2(SM_ORDER | SM_DING, 3, 5,
+ /* TRANSLATORS: Error opening destination <filename>: <error text> */
+ _("Error opening destination %s: %s"),
+ file, error_description(errno));
+ return -1;
+ }
+
+ snprintf(sbuf, sizeof(sbuf), "Saving to \"%s\"", file);
+ sbuf[sizeof(sbuf)-1] = '\0';
+ we_cancel = init_att_progress(sbuf, stream, a->body);
+
+ gf_set_so_writec(&pc, store);
+ err = detach(stream, msgno, a->number, 0L, &len, pc, NULL, 0);
+ gf_clear_so_writec(store);
+
+ if(we_cancel)
+ cancel_busy_cue(0);
+
+ if(so_give(&store)) /* close file */
+ err = error_description(errno);
+
+ if(err){
+ if(!(flags & (GER_APPEND | GER_OVER)))
+ our_unlink(file);
+ else
+ our_truncate(file, (flags & GER_APPEND) ? (off_t) orig_size : (off_t) 0);
+
+ q_status_message2(SM_ORDER | SM_DING, 3, 5,
+ /* TRANSLATORS: <error text>: Error writing attachment to <filename> */
+ _("%s: Error writing attachment to \"%s\""),
+ err, file);
+ return -1;
+ }
+ else{
+ l_string = cpystr(byte_string(len));
+ q_status_message8(SM_ORDER, 0, 4,
+ "Part %s, %s%s %s to \"%s\"%s%s%s",
+ a->number,
+ is_text
+ ? comatose(a->body->size.lines) : l_string,
+ is_text ? " lines" : "",
+ flags & GER_OVER
+ ? "overwritten"
+ : flags & GER_APPEND ? "appended" : "written",
+ file,
+ (is_text || len == a->body->size.bytes)
+ ? "" : "(decoded from ",
+ (is_text || len == a->body->size.bytes)
+ ? "" : byte_string(a->body->size.bytes),
+ is_text || len == a->body->size.bytes
+ ? "" : ")");
+ fs_give((void **)&l_string);
+ return 1;
+ }
+}
+
+
+char *
+write_attached_msg(long int msgno, ATTACH_S **ap, STORE_S *store, int newfile)
+{
+ char *err = NULL;
+ long start_of_append;
+ gf_io_t pc;
+ MESSAGECACHE *mc;
+
+ if(ap && *ap && (*ap)->body && (*ap)->body->nested.msg
+ && (*ap)->body->nested.msg->env){
+ start_of_append = so_tell(store);
+
+ gf_set_so_writec(&pc, store);
+ if(!(ps_global->mail_stream && msgno > 0L
+ && msgno <= ps_global->mail_stream->nmsgs
+ && (mc = mail_elt(ps_global->mail_stream, msgno)) && mc->valid))
+ mc = NULL;
+
+ if(!bezerk_delimiter((*ap)->body->nested.msg->env, mc, pc, newfile)
+ || !format_msg_att(msgno, ap, NULL, pc, FM_NOINDENT))
+ err = error_description(errno);
+
+ gf_clear_so_writec(store);
+
+ if(err)
+ ftruncate(fileno((FILE *)store->txt), (off_t) start_of_append);
+ }
+ else
+ err = "Can't export message. Missing Envelope data";
+
+ return(err);
+}
+
+
+/*----------------------------------------------------------------------
+ Save the attachment message/rfc822 to specified folder
+
+ Args:
+
+ Result:
+ ----*/
+void
+save_msg_att(long int msgno, ATTACH_S *a)
+{
+ char newfolder[MAILTMPLEN], *save_folder, *flags = NULL;
+ char date[64], nmsgs[80];
+ CONTEXT_S *cntxt = NULL;
+ int our_stream = 0, rv;
+ MAILSTREAM *save_stream;
+ MESSAGECACHE *mc;
+
+ snprintf(nmsgs, sizeof(nmsgs), _("Attached Msg (part %s) "), a->number);
+ nmsgs[sizeof(nmsgs)-1] = '\0';
+ if(save_prompt(ps_global, &cntxt, newfolder, sizeof(newfolder), nmsgs,
+ a->body->nested.msg->env, msgno, a->number, NULL, NULL)){
+ if(strucmp(newfolder, ps_global->inbox_name) == 0){
+ save_folder = ps_global->VAR_INBOX_PATH;
+ cntxt = NULL;
+ }
+ else
+ save_folder = newfolder;
+
+ save_stream = save_msg_stream(cntxt, save_folder, &our_stream);
+
+ mc = (msgno > 0L && ps_global->mail_stream
+ && msgno <= ps_global->mail_stream->nmsgs)
+ ? mail_elt(ps_global->mail_stream, msgno) : NULL;
+ flags = flag_string(ps_global->mail_stream, msgno, F_ANS|F_FLAG|F_SEEN|F_KEYWORD);
+ if(mc && mc->day)
+ mail_date(date, mc);
+ else
+ *date = '\0';
+
+ if(pith_opt_save_size_changed_prompt)
+ (*pith_opt_save_size_changed_prompt)(0L, SSCP_INIT);
+
+ rv = save_msg_att_work(msgno, a, save_stream, save_folder, cntxt, date);
+
+ if(pith_opt_save_size_changed_prompt)
+ (*pith_opt_save_size_changed_prompt)(0L, SSCP_END);
+
+ if(flags)
+ fs_give((void **) &flags);
+
+ if(rv == 1)
+ q_status_message2(SM_ORDER, 0, 4,
+ _("Attached message (part %s) saved to \"%s\""),
+ a->number,
+ save_folder);
+ else if(rv == -1)
+ cmd_cancelled("Attached message Save");
+ /* else whatever broke in save_fetch_append shoulda bitched */
+
+ if(our_stream)
+ mail_close(save_stream);
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Save the message/rfc822 in the given digest to the specified folder
+
+ Args:
+
+ Result:
+ ----*/
+void
+save_digest_att(long int msgno, ATTACH_S *a)
+{
+ char newfolder[MAILTMPLEN], *save_folder,
+ date[64], nmsgs[80];
+ CONTEXT_S *cntxt = NULL;
+ int our_stream = 0, rv, cnt = 0;
+ MAILSTREAM *save_stream;
+ ATTACH_S *ap;
+
+ for(ap = a + 1;
+ ap->description
+ && !strncmp(a->number, ap->number, strlen(a->number));
+ ap++)
+ if(MIME_MSG(ap->body->type, ap->body->subtype))
+ cnt++;
+
+ snprintf(nmsgs, sizeof(nmsgs), "%d Msg Digest (part %s) ", cnt, a->number);
+ nmsgs[sizeof(nmsgs)-1] = '\0';
+
+ if(save_prompt(ps_global, &cntxt, newfolder, sizeof(newfolder),
+ nmsgs, NULL, 0, NULL, NULL, NULL)){
+ save_folder = (strucmp(newfolder, ps_global->inbox_name) == 0)
+ ? ps_global->VAR_INBOX_PATH : newfolder;
+
+ save_stream = save_msg_stream(cntxt, save_folder, &our_stream);
+
+ if(pith_opt_save_size_changed_prompt)
+ (*pith_opt_save_size_changed_prompt)(0L, SSCP_INIT);
+
+ for(ap = a + 1;
+ ap->description
+ && !strncmp(a->number, ap->number, strlen(a->number));
+ ap++)
+ if(MIME_MSG(ap->body->type, ap->body->subtype)){
+ *date = '\0';
+ rv = save_msg_att_work(msgno, ap, save_stream, save_folder, cntxt, date);
+ if(rv != 1)
+ break;
+ }
+
+ if(pith_opt_save_size_changed_prompt)
+ (*pith_opt_save_size_changed_prompt)(0L, SSCP_END);
+
+ if(rv == 1)
+ q_status_message2(SM_ORDER, 0, 4,
+ _("Attached digest (part %s) saved to \"%s\""),
+ a->number,
+ save_folder);
+ else if(rv == -1)
+ cmd_cancelled("Attached digest Save");
+ /* else whatever broke in save_fetch_append shoulda bitched */
+
+ if(our_stream)
+ mail_close(save_stream);
+ }
+}
+
+
+int
+save_msg_att_work(long int msgno, ATTACH_S *a, MAILSTREAM *save_stream,
+ char *save_folder, CONTEXT_S *cntxt, char *date)
+{
+ STORE_S *so;
+ int rv = 0;
+
+ if(a && a->body && MIME_MSG(a->body->type, a->body->subtype)){
+ if((so = so_get(CharStar, NULL, WRITE_ACCESS)) != NULL){
+ *date = '\0';
+ rv = save_fetch_append(ps_global->mail_stream, msgno,
+ a->number,
+ save_stream, save_folder, cntxt,
+ a->body->size.bytes,
+ NULL, date, so);
+ }
+ else{
+ dprint((1, "Can't allocate store for save: %s\n",
+ error_description(errno)));
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Problem creating space for message text."));
+ rv = 0;
+ }
+ }
+
+ return(rv);
+}
+
+
+/*----------------------------------------------------------------------
+ Export the attachment message/rfc822 to specified file
+
+ Args:
+
+ Result:
+ ----*/
+void
+export_msg_att(long int msgno, ATTACH_S *a)
+{
+ char filename[MAXPATH+1], full_filename[MAXPATH+1], *err;
+ int rv, rflags = GER_NONE, i = 1;
+ ATTACH_S *ap = a;
+ STORE_S *store;
+ static HISTORY_S *history = NULL;
+ static ESCKEY_S opts[] = {
+ {ctrl('T'), 10, "^T", N_("To Files")},
+ {-1, 0, NULL, NULL},
+ {-1, 0, NULL, NULL}};
+
+ if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
+ opts[i].ch = ctrl('I');
+ opts[i].rval = 11;
+ opts[i].name = "TAB";
+ opts[i].label = N_("Complete");
+ }
+
+ filename[0] = full_filename[0] = '\0';
+
+ rv = get_export_filename(ps_global, filename, NULL, full_filename,
+ sizeof(filename), "msg attachment",
+ /* TRANSLATORS: Message Attachment (a screen title) */
+ _("MSG ATTACHMENT"), opts,
+ &rflags, -FOOTER_ROWS(ps_global),
+ GE_IS_EXPORT | GE_SEQ_SENSITIVE, &history);
+
+ if(rv < 0){
+ switch(rv){
+ case -1:
+ cmd_cancelled("Export");
+ break;
+
+ case -2:
+ q_status_message1(SM_ORDER, 0, 2,
+ _("Can't export to file outside of %s"),
+ ps_global->VAR_OPER_DIR);
+ break;
+ }
+
+ return;
+ }
+
+ /* With name in hand, allocate storage object and save away... */
+ if((store = so_get(FileStar, full_filename, WRITE_ACCESS)) != NULL){
+ if((err = write_attached_msg(msgno, &ap, store, !(rflags & GER_APPEND))) != NULL)
+ q_status_message(SM_ORDER | SM_DING, 3, 4, err);
+ else
+ q_status_message3(SM_ORDER, 0, 4,
+ _("Attached message (part %s) %s to \"%s\""),
+ a->number,
+ rflags & GER_OVER
+ ? _("overwritten")
+ : rflags & GER_APPEND ? _("appended") : _("written"),
+ full_filename);
+
+ if(so_give(&store))
+ q_status_message2(SM_ORDER | SM_DING, 3, 4,
+ _("Error writing %s: %s"),
+ full_filename, error_description(errno));
+ }
+ else
+ q_status_message2(SM_ORDER | SM_DING, 3, 4,
+ /* TRANSLATORS: Error opening file <filename> to export message: <error text> */
+ _("Error opening file \"%s\" to export message: %s"),
+ full_filename, error_description(errno));
+}
+
+
+/*----------------------------------------------------------------------
+ Export the message/rfc822 in the given digest to the specified file
+
+ Args:
+
+ Result:
+ ----*/
+void
+export_digest_att(long int msgno, ATTACH_S *a)
+{
+ char filename[MAXPATH+1], full_filename[MAXPATH+1], *err = NULL;
+ int rv, rflags = GER_NONE, i = 1;
+ long count = 0L;
+ ATTACH_S *ap;
+ static HISTORY_S *history = NULL;
+ STORE_S *store;
+ static ESCKEY_S opts[] = {
+ {ctrl('T'), 10, "^T", N_("To Files")},
+ {-1, 0, NULL, NULL},
+ {-1, 0, NULL, NULL}};
+
+ if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
+ opts[i].ch = ctrl('I');
+ opts[i].rval = 11;
+ opts[i].name = "TAB";
+ opts[i].label = N_("Complete");
+ }
+
+ filename[0] = full_filename[0] = '\0';
+
+ rv = get_export_filename(ps_global, filename, NULL, full_filename,
+ sizeof(filename), "digest", _("DIGEST ATTACHMENT"),
+ opts, &rflags, -FOOTER_ROWS(ps_global),
+ GE_IS_EXPORT | GE_SEQ_SENSITIVE, &history);
+
+ if(rv < 0){
+ switch(rv){
+ case -1:
+ cmd_cancelled("Export");
+ break;
+
+ case -2:
+ q_status_message1(SM_ORDER, 0, 2,
+ _("Can't export to file outside of %s"),
+ ps_global->VAR_OPER_DIR);
+ break;
+ }
+
+ return;
+ }
+
+ /* With name in hand, allocate storage object and save away... */
+ if((store = so_get(FileStar, full_filename, WRITE_ACCESS)) != NULL){
+ count = 0;
+
+ for(ap = a + 1;
+ ap->description
+ && !strncmp(a->number, ap->number, strlen(a->number))
+ && !err;
+ ap++){
+ if(MIME_MSG(ap->body->type, ap->body->subtype)){
+ count++;
+ err = write_attached_msg(msgno, &ap, store,
+ !count && !(rflags & GER_APPEND));
+ }
+ }
+
+ if(so_give(&store))
+ err = error_description(errno);
+
+ if(err){
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("Error exporting: %s"), err);
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("%s messages exported before error occurred"), err);
+ }
+ else
+ q_status_message4(SM_ORDER, 0, 4,
+ "%s messages in digest (part %s) %s to \"%s\"",
+ long2string(count),
+ a->number,
+ rflags & GER_OVER
+ ? "overwritten"
+ : rflags & GER_APPEND ? "appended" : "written",
+ full_filename);
+ }
+ else
+ q_status_message2(SM_ORDER | SM_DING, 3, 4,
+ _("Error opening file \"%s\" to export digest: %s"),
+ full_filename, error_description(errno));
+}
+
+
+/*----------------------------------------------------------------------
+ Print the given attachment associated with the given message no
+
+ Args: ps
+
+ Result:
+ ----*/
+void
+print_attachment(int qline, long int msgno, ATTACH_S *a)
+{
+ char prompt[250];
+
+ if(ps_global->restricted){
+ q_status_message(SM_ORDER | SM_DING, 0, 4,
+ "Alpine demo can't Print attachments");
+ return;
+ }
+
+ snprintf(prompt, sizeof(prompt), "attach%s %s",
+ (a->body->type == TYPETEXT) ? "ment" : "ed message",
+ MIME_DGST_A(a) ? "digest" : a->number);
+ prompt[sizeof(prompt)-1] = '\0';
+ if(open_printer(prompt) >= 0){
+ if(MIME_MSG_A(a))
+ (void) print_msg_att(msgno, a, 1);
+ else if(MIME_DGST_A(a))
+ print_digest_att(msgno, a);
+ else
+ (void) decode_text(a, msgno, print_char, NULL, QStatus, FM_NOINDENT);
+
+ close_printer();
+ }
+}
+
+
+/*
+ * Print the attachment message/rfc822 to specified file
+ *
+ * Returns 1 on success, 0 on failure.
+ */
+int
+print_msg_att(long int msgno, ATTACH_S *a, int first)
+{
+ ATTACH_S *ap = a;
+ MESSAGECACHE *mc;
+
+ if(!(ps_global->mail_stream && msgno > 0L
+ && msgno <= ps_global->mail_stream->nmsgs
+ && (mc = mail_elt(ps_global->mail_stream, msgno)) && mc->valid))
+ mc = NULL;
+
+ if(((!first && F_ON(F_AGG_PRINT_FF, ps_global)) ? print_char(FORMFEED) : 1)
+ && pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL)
+ && (F_ON(F_FROM_DELIM_IN_PRINT, ps_global)
+ ? bezerk_delimiter(a->body->nested.msg->env, mc, print_char, !first)
+ : 1)
+ && format_msg_att(msgno, &ap, NULL, print_char, FM_NOINDENT))
+ return(1);
+
+
+ q_status_message2(SM_ORDER | SM_DING, 3, 3,
+ _("Error printing message %s, part %s"),
+ long2string(msgno), a->number);
+ return(0);
+}
+
+
+/*----------------------------------------------------------------------
+ Print the attachment message/rfc822 to specified file
+
+ Args:
+
+ Result:
+ ----*/
+void
+print_digest_att(long int msgno, ATTACH_S *a)
+{
+ ATTACH_S *ap;
+ int next = 0;
+
+ for(ap = a + 1;
+ ap->description
+ && !strncmp(a->number, ap->number, strlen(a->number));
+ ap++){
+ if(MIME_MSG(ap->body->type, ap->body->subtype)){
+ char *p = part_desc(ap->number, ap->body->nested.msg->body,
+ 0, 80, FM_NOINDENT, print_char);
+ if(p){
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("Can't print digest: %s"), p);
+ break;
+ }
+ else if(!print_msg_att(msgno, ap, !next))
+ break;
+
+ next++;
+ }
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Unpack and display the given attachment associated with given message no.
+
+ Args: msgno -- message no attachment is part of
+ a -- attachment to display
+
+ Returns: 0 on success, non-zero (and error message queued) otherwise
+ ----*/
+int
+display_attachment(long int msgno, ATTACH_S *a, int flags)
+{
+ char *filename = NULL;
+ char sender_filename[1000];
+ char *extp = NULL;
+ STORE_S *store;
+ gf_io_t pc;
+ char *err;
+ int we_cancel = 0, rv;
+ char prefix[70];
+ char ext[32];
+ char mtype[128];
+
+ /*------- Display the attachment -------*/
+ if(dispatch_attachment(a) == MCD_NONE){
+ /*----- Can't display this type ------*/
+ if(a->body->encoding < ENCOTHER)
+ q_status_message4(SM_ORDER | SM_DING, 3, 5,
+ /* TRANSLATORS: Don't know how to display <certain type> attachments. <might say Try Save.> */
+ _("Don't know how to display %s%s%s attachments.%s"),
+ body_type_names(a->body->type),
+ a->body->subtype ? "/" : "",
+ a->body->subtype ? a->body->subtype :"",
+ (flags & DA_SAVE) ? _(" Try Save.") : "");
+ else
+ q_status_message1(SM_ORDER | SM_DING, 3, 5,
+ _("Don't know how to unpack \"%s\" encoding"),
+ body_encodings[(a->body->encoding <= ENCMAX)
+ ? a->body->encoding : ENCOTHER]);
+
+ return(1);
+ }
+ else if(!(a->can_display & MCD_EXTERNAL)){
+ if(a->body->type == TYPEMULTIPART){
+ if(a->body->subtype){
+ if(!strucmp(a->body->subtype, "digest"))
+ display_digest_att(msgno, a, flags);
+ else
+ q_status_message1(SM_ORDER, 3, 5,
+ _("Can't display Multipart/%s"),
+ a->body->subtype);
+ }
+ else
+ q_status_message(SM_ORDER, 3, 5,
+ _("Can't display unknown Multipart Subtype"));
+ }
+ else if(MIME_VCARD_A(a))
+ display_vcard_att(msgno, a, flags);
+ else if(a->body->type == TYPETEXT){
+ do{
+ rv = display_text_att(msgno, a, flags);
+ } while(rv == MC_FULLHDR);
+ }
+ else if(a->body->type == TYPEMESSAGE){
+ do{
+ rv = display_msg_att(msgno, a, flags);
+ } while(rv == MC_FULLHDR);
+ }
+
+ ps_global->mangled_screen = 1;
+ return(0);
+ }
+
+ /* arrive here if MCD_EXTERNAL */
+
+ if(F_OFF(F_QUELL_ATTACH_EXTRA_PROMPT, ps_global)
+ && (!(flags & DA_DIDPROMPT)))
+ if(want_to(_("View selected Attachment"), 'y',
+ 0, NO_HELP, WT_NORM) == 'n'){
+ cmd_cancelled(NULL);
+ return(1);
+ }
+
+ sender_filename[0] = '\0';
+ ext[0] = '\0';
+ prefix[0] = '\0';
+
+ if(F_OFF(F_QUELL_ATTACH_EXT_WARN, ps_global)
+ && (a->can_display & MCD_EXT_PROMPT)){
+ char prompt[256];
+
+ (void) get_filename_parameter(sender_filename, sizeof(sender_filename),
+ a->body, &extp);
+ snprintf(prompt, sizeof(prompt),
+ "Attachment %s%s unrecognized. %s%s%s",
+ a->body->subtype,
+ strlen(a->body->subtype) > 12 ? "..." : "",
+ (extp && extp[0]) ? "Try open by file extension (." : "Try opening anyway",
+ (extp && extp[0]) ? extp : "",
+ (extp && extp[0]) ? ")" : "");
+
+ if(want_to(prompt, 'n', 0, NO_HELP, WT_NORM) == 'n'){
+ cmd_cancelled(NULL);
+ return(1);
+ }
+ }
+
+ /*------ Write the image to a temporary file ------*/
+
+ /* create type/subtype in mtype */
+ strncpy(mtype, body_type_names(a->body->type), sizeof(mtype));
+ mtype[sizeof(mtype)-1] = '\0';
+ if(a->body->subtype){
+ strncat(mtype, "/", sizeof(mtype)-strlen(mtype)-1);
+ mtype[sizeof(mtype)-1] = '\0';
+ strncat(mtype, a->body->subtype, sizeof(mtype)-strlen(mtype)-1);
+ mtype[sizeof(mtype)-1] = '\0';
+ }
+
+ /*
+ * If we haven't already gotten the filename parameter, get it
+ * now. It may be used in the temporary filename and possibly
+ * for its extension.
+ */
+ if(!sender_filename[0])
+ (void) get_filename_parameter(sender_filename, sizeof(sender_filename),
+ a->body, &extp);
+
+ if(!set_mime_extension_by_type(ext, mtype)){ /* extension from type */
+ if(extp && extp[0]){ /* extension from filename */
+ strncpy(ext, extp, sizeof(ext));
+ ext[sizeof(ext)-1] = '\0';
+ }
+ }
+
+ /* create a temp file */
+ if(sender_filename){
+ char *p, *q = NULL;
+
+ /* get rid of any extension */
+ if(mt_get_file_ext(sender_filename, &q) && q && q > sender_filename)
+ *(q-1) = '\0';
+
+ /* be careful about what is allowed in the filename */
+ for(p = sender_filename; *p; p++)
+ if(!(isascii((unsigned char) *p)
+ && (isalnum((unsigned char) *p)
+ || *p == '-' || *p == '_' || *p == '+' || *p == '.' || *p == '=')))
+ break;
+
+ if(!*p) /* filename was ok to use */
+ snprintf(prefix, sizeof(prefix), "img-%s-", sender_filename);
+ }
+
+ /* didn't get it yet */
+ if(!prefix[0]){
+ snprintf(prefix, sizeof(prefix), "img-%s-", (a->body->subtype)
+ ? a->body->subtype : "unk");
+ }
+
+ filename = temp_nam_ext(NULL, prefix, ext);
+
+ if(!filename){
+ q_status_message1(SM_ORDER | SM_DING, 3, 5,
+ _("Error \"%s\", Can't create temporary file"),
+ error_description(errno));
+ return(1);
+ }
+
+ if((store = so_get(FileStar, filename, WRITE_ACCESS|OWNER_ONLY)) == NULL){
+ q_status_message2(SM_ORDER | SM_DING, 3, 5,
+ _("Error \"%s\", Can't write file %s"),
+ error_description(errno), filename);
+ if(filename){
+ our_unlink(filename);
+ fs_give((void **)&filename);
+ }
+
+ return(1);
+ }
+
+
+ if(a->body->size.bytes){
+ char msg_buf[128];
+
+ snprintf(msg_buf, sizeof(msg_buf), "Decoding %s%s%s%s",
+ a->description ? "\"" : "",
+ a->description ? a->description : "attachment number ",
+ a->description ? "" : a->number,
+ a->description ? "\"" : "");
+ msg_buf[sizeof(msg_buf)-1] = '\0';
+ we_cancel = init_att_progress(msg_buf, ps_global->mail_stream,
+ a->body);
+ }
+
+ if(a->body->type == TYPEMULTIPART){
+ char *h = mail_fetch_mime(ps_global->mail_stream, msgno, a->number,
+ NULL, 0L);
+
+ /* Write to store while converting newlines */
+ while(h && *h)
+ if(*h == '\015' && *(h+1) == '\012'){
+ so_puts(store, NEWLINE);
+ h += 2;
+ }
+ else
+ so_writec(*h++, store);
+ }
+
+ gf_set_so_writec(&pc, store);
+
+ err = detach(ps_global->mail_stream, msgno, a->number, 0L, NULL, pc, NULL, 0);
+
+ gf_clear_so_writec(store);
+
+ if(we_cancel)
+ cancel_busy_cue(0);
+
+ so_give(&store);
+
+ if(err){
+ q_status_message2(SM_ORDER | SM_DING, 3, 5,
+ "%s: Error saving image to temp file %s",
+ err, filename);
+ if(filename){
+ our_unlink(filename);
+ fs_give((void **)&filename);
+ }
+
+ return(1);
+ }
+
+ /*----- Run the viewer process ----*/
+ run_viewer(filename, a->body, a->can_display & MCD_EXT_PROMPT);
+ if(filename)
+ fs_give((void **)&filename);
+
+ return(0);
+}
+
+
+/*----------------------------------------------------------------------
+ Fish the required command from mailcap and run it
+
+ Args: image_file -- The name of the file to pass viewer
+ body -- body struct containing type/subtype of part
+
+A side effect may be that scrolltool is called as well if
+exec_mailcap_cmd has any substantial output...
+ ----*/
+void
+run_viewer(char *image_file, struct mail_bodystruct *body, int chk_extension)
+{
+ MCAP_CMD_S *mc_cmd = NULL;
+ int needs_terminal = 0, we_cancel = 0;
+
+ we_cancel = busy_cue("Displaying attachment", NULL, 0);
+
+ if((mc_cmd = mailcap_build_command(body->type, body->subtype,
+ body, image_file,
+ &needs_terminal, chk_extension)) != NULL){
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ exec_mailcap_cmd(mc_cmd, image_file, needs_terminal);
+ if(mc_cmd->command)
+ fs_give((void **)&mc_cmd->command);
+ fs_give((void **)&mc_cmd);
+ }
+ else{
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ q_status_message1(SM_ORDER, 3, 4, _("Cannot display %s attachment"),
+ type_desc(body->type, body->subtype,
+ body->parameter, NULL, 1));
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Detach and provide for browsing a text body part
+
+ Args: msgno -- raw message number to get part from
+ a -- attachment struct for the desired part
+
+ Result:
+ ----*/
+STORE_S *
+format_text_att(long int msgno, ATTACH_S *a, HANDLE_S **handlesp)
+{
+ STORE_S *store;
+ gf_io_t pc;
+
+ if((store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
+ if(handlesp)
+ init_handles(handlesp);
+
+ gf_set_so_writec(&pc, store);
+ (void) decode_text(a, msgno, pc, handlesp, QStatus, FM_DISPLAY);
+ gf_clear_so_writec(store);
+ }
+
+ return(store);
+}
+
+
+/*----------------------------------------------------------------------
+ Detach and provide for browsing a text body part
+
+ Args: msgno -- raw message number to get part from
+ a -- attachment struct for the desired part
+
+ Result:
+ ----*/
+int
+display_text_att(long int msgno, ATTACH_S *a, int flags)
+{
+ STORE_S *store;
+ HANDLE_S *handles = NULL;
+ int rv = 0;
+
+ if(msgno > 0L)
+ clear_index_cache_ent(ps_global->mail_stream, msgno, 0);
+
+ if((store = format_text_att(msgno, a, &handles)) != NULL){
+ rv = scroll_attachment("ATTACHED TEXT", store, CharStar, handles, a, flags);
+ free_handles(&handles);
+ so_give(&store); /* free resources associated with store */
+ }
+ else
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("Error allocating space for attachment."));
+
+ return(rv);
+}
+
+
+/*----------------------------------------------------------------------
+ Detach and provide for browsing a body part of type "MESSAGE"
+
+ Args: msgno -- message number to get partrom
+ a -- attachment struct for the desired part
+
+ Result:
+ ----*/
+int
+display_msg_att(long int msgno, ATTACH_S *a, int flags)
+{
+ STORE_S *store;
+ gf_io_t pc;
+ ATTACH_S *ap = a;
+ HANDLE_S *handles = NULL;
+ int rv = 0;
+
+ if(msgno > 0L)
+ clear_index_cache_ent(ps_global->mail_stream, msgno, 0);
+
+ /* BUG, should check this return code */
+ (void) pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
+
+ /* initialize a storage object */
+ if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("Error allocating space for message."));
+ return(rv);
+ }
+
+ gf_set_so_writec(&pc, store);
+
+ if(format_msg_att(msgno, &ap, &handles, pc, FM_DISPLAY)){
+ if(ps_global->full_header == 2)
+ q_status_message(SM_INFO, 0, 3,
+ _("Full header mode ON. All header text being included"));
+
+ rv = scroll_attachment((a->body->subtype
+ && !strucmp(a->body->subtype, "delivery-status"))
+ ? "DELIVERY STATUS REPORT" : "ATTACHED MESSAGE",
+ store, CharStar, handles, a, flags);
+ free_handles(&handles);
+ }
+
+ gf_clear_so_writec(store);
+
+ so_give(&store); /* free resources associated with store */
+ return(rv);
+}
+
+
+/*----------------------------------------------------------------------
+ Detach and provide for browsing a multipart body part of type "DIGEST"
+
+ Args: msgno -- message number to get partrom
+ a -- attachment struct for the desired part
+
+ Result:
+ ----*/
+void
+display_digest_att(long int msgno, ATTACH_S *a, int flags)
+{
+ STORE_S *store;
+ ATTACH_S *ap;
+ HANDLE_S *handles = NULL;
+ gf_io_t pc;
+ SourceType src = CharStar;
+ int bad_news = 0;
+
+ if(msgno > 0L)
+ clear_index_cache_ent(ps_global->mail_stream, msgno, 0);
+
+ /* BUG, should check this return code */
+ (void) pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
+
+ if(!(store = so_get(src, NULL, EDIT_ACCESS))){
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("Error allocating space for message."));
+ return;
+ }
+
+ gf_set_so_writec(&pc, store);
+
+ /*
+ * While in a subtype of this message
+ */
+ for(ap = a + 1;
+ ap->description
+ && !strncmp(a->number, ap->number, strlen(a->number))
+ && !bad_news;
+ ap++){
+ if(ap->body->type == TYPEMESSAGE){
+ char *errstr;
+
+ if(ap->body->subtype && strucmp(ap->body->subtype, "rfc822")){
+ char *tsub;
+
+ tsub = cpystr(ap->body->subtype);
+ convert_possibly_encoded_str_to_utf8((char **) &tsub);
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Unknown Message subtype: %s", tsub);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+
+ if((errstr = format_editorial(tmp_20k_buf,
+ ps_global->ttyo->screen_cols, 0,
+ NULL, pc)) != NULL){
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("Can't format digest: %s"), errstr);
+ bad_news++;
+ }
+ else if(!gf_puts(NEWLINE, pc))
+ bad_news++;
+
+ fs_give((void **) &tsub);
+ }
+ else{
+ if((errstr = part_desc(ap->number, ap->body->nested.msg->body,
+ 0, ps_global->ttyo->screen_cols, FM_DISPLAY, pc)) != NULL){
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("Can't format digest: %s"), errstr);
+ bad_news++;
+ }
+ else if(!format_msg_att(msgno, &ap, &handles, pc, FM_DISPLAY))
+ bad_news++;
+ }
+ }
+ else if(ap->body->type == TYPETEXT
+ && decode_text(ap, msgno, pc, NULL, QStatus, FM_DISPLAY))
+ bad_news++;
+ else if(!gf_puts("Unknown type in Digest", pc))
+ bad_news++;
+ }
+
+ if(!bad_news){
+ if(ps_global->full_header == 2)
+ q_status_message(SM_INFO, 0, 3,
+ _("Full header mode ON. All header text being included"));
+
+ scroll_attachment(_("ATTACHED MESSAGES"), store, src, handles, a, flags);
+ }
+
+ free_handles(&handles);
+
+ gf_clear_so_writec(store);
+ so_give(&store); /* free resources associated with store */
+}
+
+
+int
+scroll_attachment(char *title, STORE_S *store, SourceType src, HANDLE_S *handles, ATTACH_S *a, int flags)
+{
+ SCROLL_S sargs;
+
+ memset(&sargs, 0, sizeof(SCROLL_S));
+ sargs.text.text = so_text(store);
+ sargs.text.src = src;
+ sargs.text.desc = "attachment";
+ sargs.text.handles = handles;
+ sargs.bar.title = title;
+ sargs.proc.tool = process_attachment_cmd;
+ sargs.proc.data.p = (void *) a;
+ sargs.help.text = h_mail_text_att_view;
+ sargs.help.title = _("HELP FOR ATTACHED TEXT VIEW");
+ sargs.keys.menu = &att_view_keymenu;
+ setbitmap(sargs.keys.bitmap);
+
+ /* First, fix up "back" key */
+ if(flags & DA_FROM_VIEW){
+ att_view_keymenu.keys[ATV_BACK_KEY].label = N_("MsgText");
+ }
+ else{
+ att_view_keymenu.keys[ATV_BACK_KEY].label = N_("AttchIndex");
+ }
+
+ if(!handles){
+ clrbitn(ATV_VIEW_HILITE, sargs.keys.bitmap);
+ clrbitn(ATV_PREV_URL, sargs.keys.bitmap);
+ clrbitn(ATV_NEXT_URL, sargs.keys.bitmap);
+ }
+
+ if(F_OFF(F_ENABLE_PIPE, ps_global))
+ clrbitn(ATV_PIPE_KEY, sargs.keys.bitmap);
+
+ if(!(a->body->type == TYPETEXT || MIME_MSG_A(a) || MIME_DGST_A(a)))
+ clrbitn(ATV_PRINT_KEY, sargs.keys.bitmap);
+
+ /*
+ * If message or digest, leave Reply and Save and,
+ * conditionally, Bounce on...
+ */
+ if(MIME_MSG_A(a)){
+ if(F_OFF(F_ENABLE_BOUNCE, ps_global))
+ clrbitn(ATV_BOUNCE_KEY, sargs.keys.bitmap);
+ }
+ else{
+ clrbitn(ATV_BOUNCE_KEY, sargs.keys.bitmap);
+ clrbitn(ATV_REPLY_KEY, sargs.keys.bitmap);
+ clrbitn(ATV_EXPORT_KEY, sargs.keys.bitmap);
+ }
+
+ sargs.use_indexline_color = 1;
+
+ if(DA_RESIZE & flags)
+ sargs.resize_exit = 1;
+
+#ifdef _WINDOWS
+ scrat_attachp = a;
+ sargs.mouse.popup = scroll_att_popup;
+#endif
+
+ return(scrolltool(&sargs));
+}
+
+
+int
+process_attachment_cmd(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
+{
+ int rv = 0, n;
+ long rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
+#define AD(X) ((ATTACH_S *) (X)->proc.data.p)
+
+ switch(cmd){
+ case MC_EXIT :
+ rv = 1;
+ break;
+
+ case MC_QUIT :
+ ps_global->next_screen = quit_screen;
+ break;
+
+ case MC_MAIN :
+ ps_global->next_screen = main_menu_screen;
+ break;
+
+ case MC_REPLY :
+ reply_msg_att(ps_global->mail_stream, rawno, sparms->proc.data.p);
+ break;
+
+ case MC_FORWARD :
+ forward_attachment(ps_global->mail_stream, rawno, sparms->proc.data.p);
+ break;
+
+ case MC_BOUNCE :
+ bounce_msg_att(ps_global->mail_stream, rawno, AD(sparms)->number,
+ AD(sparms)->body->nested.msg->env->subject);
+ ps_global->mangled_footer = 1;
+ break;
+
+ case MC_DELETE :
+ delete_attachment(rawno, sparms->proc.data.p);
+ break;
+
+ case MC_UNDELETE :
+ if(undelete_attachment(rawno, sparms->proc.data.p, &n))
+ q_status_message1(SM_ORDER, 0, 3,
+ "Part %s UNdeleted", AD(sparms)->number);
+
+ break;
+
+ case MC_SAVE :
+ save_attachment(-FOOTER_ROWS(ps_global), rawno, sparms->proc.data.p);
+ ps_global->mangled_footer = 1;
+ break;
+
+ case MC_EXPORT :
+ export_attachment(-FOOTER_ROWS(ps_global), rawno, sparms->proc.data.p);
+ ps_global->mangled_footer = 1;
+ break;
+
+ case MC_PRINTMSG :
+ print_attachment(-FOOTER_ROWS(ps_global), rawno, sparms->proc.data.p);
+ ps_global->mangled_footer = 1;
+ break;
+
+ case MC_PIPE :
+ pipe_attachment(rawno, sparms->proc.data.p);
+ ps_global->mangled_footer = 1;
+ break;
+
+ case MC_FULLHDR :
+ ps_global->full_header++;
+ if(ps_global->full_header == 1){
+ if(!(ps_global->quote_suppression_threshold
+ && (ps_global->some_quoting_was_suppressed /* || in_index != View*/)))
+ ps_global->full_header++;
+ }
+ else if(ps_global->full_header > 2)
+ ps_global->full_header = 0;
+
+ switch(ps_global->full_header){
+ case 0:
+ q_status_message(SM_ORDER, 0, 3,
+ _("Display of full headers is now off."));
+ break;
+
+ case 1:
+ q_status_message1(SM_ORDER, 0, 3,
+ _("Quotes displayed, use %s to see full headers"),
+ F_ON(F_USE_FK, ps_global) ? "F9" : "H");
+ break;
+
+ case 2:
+ q_status_message(SM_ORDER, 0, 3,
+ _("Display of full headers is now on."));
+ break;
+
+ }
+
+ rv = 1;
+ break;
+
+ default:
+ panic("Unexpected command case");
+ break;
+ }
+
+ return(rv);
+}
+
+
+/*
+ * Returns 1 on success, 0 on error.
+ */
+int
+format_msg_att(long int msgno, ATTACH_S **a, HANDLE_S **handlesp, gf_io_t pc, int flags)
+{
+ int rv = 1;
+
+ if((*a)->body->type != TYPEMESSAGE)
+ return(gf_puts("[ Undisplayed Attachment of Type ", pc)
+ && gf_puts(body_type_names((*a)->body->type), pc)
+ && gf_puts(" ]", pc) && gf_puts(NEWLINE, pc));
+
+ if((*a)->body->subtype && strucmp((*a)->body->subtype, "rfc822") == 0){
+ HEADER_S h;
+
+ HD_INIT(&h, ps_global->VAR_VIEW_HEADERS, ps_global->view_all_except,
+ FE_DEFAULT);
+ switch(format_header(ps_global->mail_stream, msgno, (*a)->number,
+ (*a)->body->nested.msg->env, &h, NULL, NULL,
+ flags, NULL, pc)){
+ case -1 : /* write error */
+ return(0);
+
+ case 1 : /* fetch error */
+ if(!(gf_puts("[ Error fetching header ]", pc)
+ && !gf_puts(NEWLINE, pc)))
+ return(0);
+
+ break;
+ }
+
+ gf_puts(NEWLINE, pc);
+
+ ++(*a);
+
+#ifdef SMIME
+ if((*a)->body && (*a)->body->subtype && (strucmp((*a)->body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0)){
+ if((*a)->description){
+ if(!(!format_editorial((*a)->description,
+ ps_global->ttyo->screen_cols,
+ flags, NULL, pc)
+ && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
+ rv = 0;
+ }
+
+ ++(*a);
+ }
+#endif /* SMIME */
+
+ if(((*a))->description
+ && (*a)->body && (*a)->body->type == TYPETEXT){
+ if(decode_text(*a, msgno, pc, NULL, QStatus, flags))
+ rv = 0;
+ }
+ else if(!(gf_puts("[Can't display ", pc)
+ && gf_puts(((*a)->description && (*a)->body)
+ ? "first non-" : "missing ", pc)
+ && gf_puts("text segment]", pc)
+ && gf_puts(NEWLINE, pc)))
+ rv = 0;
+ }
+ else if((*a)->body->subtype
+ && strucmp((*a)->body->subtype, "external-body") == 0) {
+ if(format_editorial("This part is not included and can be fetched as follows:",
+ ps_global->ttyo->screen_cols, flags, NULL, pc)
+ || !gf_puts(NEWLINE, pc)
+ || format_editorial(display_parameters((*a)->body->parameter),
+ ps_global->ttyo->screen_cols, flags, handlesp, pc))
+ rv = 0;
+ }
+ else if(decode_text(*a, msgno, pc, NULL, QStatus, flags))
+ rv = 0;
+
+ return(rv);
+}
+
+
+void
+display_vcard_att(long int msgno, ATTACH_S *a, int flags)
+{
+ STORE_S *in_store, *out_store = NULL;
+ HANDLE_S *handles = NULL;
+ URL_HILITE_S uh;
+ gf_io_t gc, pc;
+ char **lines, **ll, *errstr = NULL, tmp[MAILTMPLEN], *p;
+ int cmd, indent, begins = 0;
+
+ lines = detach_vcard_att(ps_global->mail_stream,
+ msgno, a->body, a->number);
+ if(!lines){
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("Error accessing attachment."));
+ return;
+ }
+
+ if(!(in_store = so_get(CharStar, NULL, EDIT_ACCESS))){
+ free_list_array(&lines);
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("Error allocating space for attachment."));
+ return;
+ }
+
+ for(ll = lines, indent = 0; ll && *ll; ll++)
+ if((p = strindex(*ll, ':')) && p - *ll > indent)
+ indent = p - *ll;
+
+ indent += 5;
+ for(ll = lines; ll && *ll; ll++){
+ if((p = strindex(*ll, ':')) != NULL){
+ if(begins < 2 && struncmp(*ll, "begin:", 6) == 0)
+ begins++;
+
+ snprintf(tmp, sizeof(tmp), " %-*.*s : ", indent - 5,
+ MIN(p - *ll, sizeof(tmp)-5), *ll);
+ tmp[sizeof(tmp)-1] = '\0';
+ so_puts(in_store, tmp);
+ p++;
+ }
+ else{
+ p = *ll;
+ so_puts(in_store, repeat_char(indent, SPACE));
+ }
+
+ snprintf(tmp, sizeof(tmp), "%.200s", p);
+ tmp[sizeof(tmp)-1] = '\0';
+ so_puts(in_store,
+ (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, tmp));
+ so_puts(in_store, "\015\012");
+ }
+
+ free_list_array(&lines);
+
+ so_puts(in_store, "\015\012\015\012");
+
+ do{
+ if((out_store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
+ so_seek(in_store, 0L, 0);
+
+ init_handles(&handles);
+ gf_filter_init();
+
+ if(F_ON(F_VIEW_SEL_URL, ps_global)
+ || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
+ || F_ON(F_SCAN_ADDR, ps_global))
+ gf_link_filter(gf_line_test,
+ gf_line_test_opt(url_hilite,
+ gf_url_hilite_opt(&uh,&handles,0)));
+
+ gf_link_filter(gf_wrap,
+ gf_wrap_filter_opt(ps_global->ttyo->screen_cols - 4,
+ ps_global->ttyo->screen_cols,
+ NULL, indent, GFW_HANDLES));
+ gf_link_filter(gf_nvtnl_local, NULL);
+
+ gf_set_so_readc(&gc, in_store);
+ gf_set_so_writec(&pc, out_store);
+
+ errstr = gf_pipe(gc, pc);
+
+ gf_clear_so_readc(in_store);
+
+ if(!errstr){
+#define VCARD_TEXT_ONE _("This is a vCard which has been forwarded to you. You may add parts of it to your address book with the Save command. You will have a chance to edit it before committing it to your address book.")
+#define VCARD_TEXT_MORE _("This is a vCard which has been forwarded to you. You may add the entries to your address book with the Save command.")
+ errstr = format_editorial((begins > 1)
+ ? VCARD_TEXT_MORE : VCARD_TEXT_ONE,
+ ps_global->ttyo->screen_cols, 0, NULL, pc);
+ }
+
+ gf_clear_so_writec(out_store);
+
+ if(!errstr)
+ cmd = scroll_attachment(_("ADDRESS BOOK ATTACHMENT"), out_store,
+ CharStar, handles, a, flags | DA_RESIZE);
+
+ free_handles(&handles);
+ so_give(&out_store);
+ }
+ else
+ errstr = _("Error allocating space");
+ }
+ while(!errstr && (cmd == MC_RESIZE || cmd == MC_FULLHDR));
+
+ if(errstr)
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("Can't format entry : %s"), errstr);
+
+ so_give(&in_store);
+}
+
+
+/*----------------------------------------------------------------------
+ Display attachment information
+
+ Args: msgno -- message number to get partrom
+ a -- attachment struct for the desired part
+
+ Result: a screen containing information about attachment:
+ ----*/
+void
+display_attach_info(long int msgno, ATTACH_S *a)
+{
+ int i, indent, cols;
+ char buf1[100], *folded;
+ STORE_S *store;
+ PARMLIST_S *plist;
+ SCROLL_S sargs;
+
+ (void) dispatch_attachment(a);
+
+ if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("Error allocating space for message."));
+ return;
+ }
+
+ cols = ps_global->ttyo->screen_cols;
+
+ /*
+ * 2 spaces on left
+ * 16 for text (longest is Display Method == 14)
+ * 2 for ": "
+ */
+ indent = 20;
+
+ /* don't try stupid folding */
+ cols = MAX(indent+10, cols);
+
+ so_puts(store, "Details about Attachment #");
+ so_puts(store, a->number);
+ so_puts(store, " :\n\n");
+ utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Type");
+ so_puts(store, buf1);
+ so_puts(store, body_type_names(a->body->type));
+ so_puts(store, "\n");
+ utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Subtype");
+ so_puts(store, buf1);
+ so_puts(store, a->body->subtype ? a->body->subtype : "Unknown");
+ so_puts(store, "\n");
+ utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Encoding");
+ so_puts(store, buf1);
+ so_puts(store, a->body->encoding < ENCMAX
+ ? body_encodings[a->body->encoding]
+ : "Unknown");
+ so_puts(store, "\n");
+ if((plist = rfc2231_newparmlist(a->body->parameter)) != NULL){
+ utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Parameters");
+ so_puts(store, buf1);
+ i = 0;
+ while(rfc2231_list_params(plist)){
+ if(i++)
+ so_puts(store, repeat_char(indent, ' '));
+
+ so_puts(store, plist->attrib);
+ so_puts(store, " = ");
+ so_puts(store, plist->value ? plist->value : "");
+ so_puts(store, "\n");
+ }
+
+ rfc2231_free_parmlist(&plist);
+ }
+
+ if(a->body->description && a->body->description[0]){
+ char buftmp[MAILTMPLEN];
+ unsigned char *q;
+
+ utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Description");
+
+ snprintf(buftmp, sizeof(buftmp), "%s", a->body->description);
+ buftmp[sizeof(buftmp)-1] = '\0';
+ q = rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, buftmp);
+ folded = fold((char *) q, cols, cols, buf1, repeat_char(indent+1, ' '), FLD_NONE);
+
+ if(folded){
+ so_puts(store, folded);
+ fs_give((void **) &folded);
+ }
+ }
+
+ /* BUG: no a->body->language support */
+
+ if(a->body->disposition.type){
+ utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Disposition");
+ so_puts(store, buf1);
+ so_puts(store, a->body->disposition.type);
+ so_puts(store, "\n");
+ if((plist = rfc2231_newparmlist(a->body->disposition.parameter)) != NULL){
+ while(rfc2231_list_params(plist)){
+ so_puts(store, repeat_char(indent, ' '));
+ so_puts(store, plist->attrib);
+ so_puts(store, " = ");
+ so_puts(store, plist->value ? plist->value : "");
+ so_puts(store, "\n");
+ }
+
+ rfc2231_free_parmlist(&plist);
+ }
+ }
+
+ utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Approx. Size");
+ so_puts(store, buf1);
+ so_puts(store, comatose((a->body->encoding == ENCBASE64)
+ ? ((a->body->size.bytes * 3)/4)
+ : a->body->size.bytes));
+ so_puts(store, " bytes\n");
+ utf8_snprintf(buf1, sizeof(buf1), " %-*.*w: ", indent-4, indent-4, "Display Method");
+ so_puts(store, buf1);
+ if(a->can_display == MCD_NONE) {
+ so_puts(store, "Can't, ");
+ so_puts(store, (a->body->encoding < ENCOTHER)
+ ? "Unknown Attachment Format"
+ : "Unknown Encoding");
+ }
+ else if(!(a->can_display & MCD_EXTERNAL)){
+ so_puts(store, "Alpine's Internal Pager");
+ }
+ else{
+ int nt, free_pretty_cmd;
+ MCAP_CMD_S *mc_cmd;
+ char *pretty_cmd;
+
+ if((mc_cmd = mailcap_build_command(a->body->type, a->body->subtype,
+ a->body, "<datafile>", &nt,
+ a->can_display & MCD_EXT_PROMPT)) != NULL){
+ so_puts(store, "\"");
+ if((pretty_cmd = execview_pretty_command(mc_cmd, &free_pretty_cmd)) != NULL)
+ so_puts(store, pretty_cmd);
+ so_puts(store, "\"");
+ if(free_pretty_cmd && pretty_cmd)
+ fs_give((void **)&pretty_cmd);
+ if(mc_cmd->command)
+ fs_give((void **)&mc_cmd->command);
+ fs_give((void **)&mc_cmd);
+ }
+ }
+
+ so_puts(store, "\n");
+
+ memset(&sargs, 0, sizeof(SCROLL_S));
+ sargs.text.text = so_text(store);
+ sargs.text.src = CharStar;
+ sargs.text.desc = "attachment info";
+ sargs.bar.title = _("ABOUT ATTACHMENT");
+ sargs.help.text = h_simple_text_view;
+ sargs.help.title = _("HELP FOR \"ABOUT ATTACHMENT\"");
+
+ sargs.use_indexline_color = 1;
+
+ scrolltool(&sargs);
+
+ so_give(&store); /* free resources associated with store */
+ ps_global->mangled_screen = 1;
+}
+
+
+/*----------------------------------------------------------------------
+
+ ----*/
+void
+forward_attachment(MAILSTREAM *stream, long int msgno, ATTACH_S *a)
+{
+ char *sig;
+ void *msgtext;
+ ENVELOPE *outgoing;
+ BODY *body;
+
+ if(MIME_MSG_A(a)){
+ forward_msg_att(stream, msgno, a);
+ }
+ else{
+ ACTION_S *role = NULL;
+ REDRAFT_POS_S *redraft_pos = NULL;
+ long rflags = ROLE_FORWARD;
+ PAT_STATE dummy;
+
+ outgoing = mail_newenvelope();
+ outgoing->message_id = generate_message_id();
+ outgoing->subject = cpystr("Forwarded attachment...");
+
+ if(nonempty_patterns(rflags, &dummy)){
+ /*
+ * There is no message, but a Current Folder Type might match.
+ *
+ * This has been changed to check against the message
+ * containing the attachment.
+ */
+ role = set_role_from_msg(ps_global, ROLE_FORWARD, msgno, NULL);
+ if(confirm_role(rflags, &role))
+ role = combine_inherited_role(role);
+ else{
+ role = NULL;
+ cmd_cancelled("Forward");
+ mail_free_envelope(&outgoing);
+ return;
+ }
+ }
+
+ if(role)
+ q_status_message1(SM_ORDER, 3, 4,
+ _("Forwarding using role \"%s\""), role->nick);
+
+ /*
+ * as with all text bound for the composer, build it in
+ * a storage object of the type it understands...
+ */
+ if((msgtext = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
+ int impl, template_len = 0;
+
+ if(role && role->template){
+ char *filtered;
+
+ impl = 1;
+ filtered = detoken(role, NULL, 0, 0, 0, &redraft_pos, &impl);
+ if(filtered){
+ if(*filtered){
+ so_puts((STORE_S *)msgtext, filtered);
+ if(impl == 1)
+ template_len = strlen(filtered);
+ }
+
+ fs_give((void **)&filtered);
+ }
+ }
+ else
+ impl = 1;
+
+ if((sig = detoken(role, NULL, 2, 0, 1, &redraft_pos, &impl)) != NULL){
+ if(impl == 2)
+ redraft_pos->offset += template_len;
+
+ so_puts((STORE_S *)msgtext, *sig ? sig : NEWLINE);
+
+ fs_give((void **)&sig);
+ }
+ else
+ so_puts((STORE_S *)msgtext, NEWLINE);
+
+ /*---- New Body to start with ----*/
+ body = mail_newbody();
+ body->type = TYPEMULTIPART;
+
+ /*---- The TEXT part/body ----*/
+ body->nested.part = mail_newbody_part();
+ body->nested.part->body.type = TYPETEXT;
+ body->nested.part->body.contents.text.data = msgtext;
+
+ /*---- The corresponding things we're attaching ----*/
+ body->nested.part->next = mail_newbody_part();
+ body->nested.part->next->body.id = generate_message_id();
+ copy_body(&body->nested.part->next->body, a->body);
+
+ if(fetch_contents(stream, msgno, a->number,
+ &body->nested.part->next->body)){
+ pine_send(outgoing, &body, "FORWARD MESSAGE",
+ role, NULL, NULL, redraft_pos, NULL, NULL, 0);
+
+ ps_global->mangled_screen = 1;
+ pine_free_body(&body);
+ free_redraft_pos(&redraft_pos);
+ }
+ else{
+ mail_free_body(&body);
+ so_give((STORE_S **) &msgtext);
+ free_redraft_pos(&redraft_pos);
+ q_status_message(SM_ORDER | SM_DING, 4, 5,
+ _("Error fetching message contents. Can't forward message."));
+ }
+ }
+ else
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Error allocating message text"));
+
+ mail_free_envelope(&outgoing);
+ free_action(&role);
+ }
+}
+
+
+void
+forward_msg_att(MAILSTREAM *stream, long int msgno, ATTACH_S *a)
+{
+ char *p, *sig = NULL;
+ int ret;
+ void *msgtext;
+ ENVELOPE *outgoing;
+ BODY *body;
+ ACTION_S *role = NULL;
+ REPLY_S reply;
+ REDRAFT_POS_S *redraft_pos = NULL;
+
+ outgoing = mail_newenvelope();
+ outgoing->message_id = generate_message_id();
+
+ memset((void *)&reply, 0, sizeof(reply));
+
+ if((outgoing->subject = forward_subject(a->body->nested.msg->env, 0)) != NULL){
+ /*
+ * as with all text bound for the composer, build it in
+ * a storage object of the type it understands...
+ */
+ if((msgtext = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
+ int impl, template_len = 0;
+ long rflags = ROLE_FORWARD;
+ PAT_STATE dummy;
+
+ ret = 'n';
+ if(ps_global->full_header == 2)
+ ret = want_to(_("Forward message as an attachment"), 'n', 0,
+ NO_HELP, WT_SEQ_SENSITIVE);
+ /* Setup possible role */
+ if(nonempty_patterns(rflags, &dummy)){
+ role = set_role_from_msg(ps_global, rflags, msgno, a->number);
+ if(confirm_role(rflags, &role))
+ role = combine_inherited_role(role);
+ else{ /* cancel reply */
+ role = NULL;
+ cmd_cancelled("Forward");
+ mail_free_envelope(&outgoing);
+ so_give((STORE_S **) &msgtext);
+ return;
+ }
+ }
+
+ if(role)
+ q_status_message1(SM_ORDER, 3, 4,
+ _("Forwarding using role \"%s\""), role->nick);
+
+ if(role && role->template){
+ char *filtered;
+
+ impl = 1;
+ filtered = detoken(role, a->body->nested.msg->env,
+ 0, 0, 0, &redraft_pos, &impl);
+ if(filtered){
+ if(*filtered){
+ so_puts((STORE_S *)msgtext, filtered);
+ if(impl == 1)
+ template_len = strlen(filtered);
+ }
+
+ fs_give((void **)&filtered);
+ }
+ }
+ else
+ impl = 1;
+
+ if((sig = detoken(role, a->body->nested.msg->env,
+ 2, 0, 1, &redraft_pos, &impl)) != NULL){
+ if(impl == 2)
+ redraft_pos->offset += template_len;
+
+ so_puts((STORE_S *)msgtext, *sig ? sig : NEWLINE);
+
+ fs_give((void **)&sig);
+ }
+ else
+ so_puts((STORE_S *)msgtext, NEWLINE);
+
+ if(ret == 'y'){
+ /*---- New Body to start with ----*/
+ body = mail_newbody();
+ body->type = TYPEMULTIPART;
+
+ /*---- The TEXT part/body ----*/
+ body->nested.part = mail_newbody_part();
+ body->nested.part->body.type = TYPETEXT;
+ body->nested.part->body.contents.text.data = msgtext;
+
+ if(!forward_mime_msg(stream, msgno,
+ p = body_partno(stream, msgno, a->body),
+ a->body->nested.msg->env,
+ &body->nested.part->next, msgtext))
+ mail_free_body(&body);
+ }
+ else{
+ reply.forw = 1;
+ if(a->body->nested.msg->body){
+ char *charset;
+
+ charset
+ = parameter_val(a->body->nested.msg->body->parameter,
+ "charset");
+
+ if(charset && strucmp(charset, "us-ascii") != 0){
+ CONV_TABLE *ct;
+
+ /*
+ * There is a non-ascii charset,
+ * is there conversion happening?
+ */
+ if(!(ct=conversion_table(charset, ps_global->posting_charmap))
+ || !ct->table){
+ reply.orig_charset = charset;
+ charset = NULL;
+ }
+ }
+
+ if(charset)
+ fs_give((void **) &charset);
+ }
+
+ body = forward_body(stream, a->body->nested.msg->env,
+ a->body->nested.msg->body, msgno,
+ p = body_partno(stream, msgno, a->body),
+ msgtext, FWD_NONE);
+ }
+
+ fs_give((void **) &p);
+
+ if(body){
+ pine_send(outgoing, &body,
+ "FORWARD MESSAGE",
+ role, NULL,
+ reply.forw ? &reply : NULL,
+ redraft_pos, NULL, NULL, 0);
+
+ ps_global->mangled_screen = 1;
+ pine_free_body(&body);
+ free_redraft_pos(&redraft_pos);
+ free_action(&role);
+ }
+ else{
+ so_give((STORE_S **) &msgtext);
+ q_status_message(SM_ORDER | SM_DING, 4, 5,
+ _("Error fetching message contents. Can't forward message."));
+ }
+ }
+ else
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Error allocating message text"));
+ }
+ else
+ q_status_message1(SM_ORDER,3,4,
+ _("Error fetching message %s. Can't forward it."),
+ long2string(msgno));
+
+ if(reply.orig_charset)
+ fs_give((void **)&reply.orig_charset);
+
+ mail_free_envelope(&outgoing);
+}
+
+
+void
+reply_msg_att(MAILSTREAM *stream, long int msgno, ATTACH_S *a)
+{
+ ADDRESS *saved_from, *saved_to, *saved_cc, *saved_resent;
+ ENVELOPE *outgoing;
+ BODY *body;
+ void *msgtext;
+ char *tp, *prefix = NULL, *fcc = NULL, *errmsg = NULL;
+ int include_text = 0, flags = RSF_QUERY_REPLY_ALL;
+ int rolemsg = 0, copytomsg = 0;
+ long rflags;
+ PAT_STATE dummy;
+ REDRAFT_POS_S *redraft_pos = NULL;
+ ACTION_S *role = NULL;
+
+ outgoing = mail_newenvelope();
+
+ dprint((4,"\n - attachment reply \n"));
+
+ saved_from = (ADDRESS *) NULL;
+ saved_to = (ADDRESS *) NULL;
+ saved_cc = (ADDRESS *) NULL;
+ saved_resent = (ADDRESS *) NULL;
+ outgoing->subject = NULL;
+
+ prefix = reply_quote_str(a->body->nested.msg->env);
+ /*
+ * For consistency, the first question is always "include text?"
+ */
+ if((include_text = reply_text_query(ps_global, 1, &prefix)) >= 0
+ && reply_news_test(a->body->nested.msg->env, outgoing) > 0
+ && reply_harvest(ps_global, msgno, a->number,
+ a->body->nested.msg->env, &saved_from,
+ &saved_to, &saved_cc, &saved_resent, &flags)){
+ outgoing->subject = reply_subject(a->body->nested.msg->env->subject,
+ NULL, 0);
+ clear_cursor_pos();
+ reply_seed(ps_global, outgoing, a->body->nested.msg->env,
+ saved_from, saved_to, saved_cc, saved_resent,
+ &fcc, flags & RSF_FORCE_REPLY_ALL, &errmsg);
+ if(errmsg){
+ if(*errmsg){
+ q_status_message1(SM_ORDER, 3, 3, "%.200s", errmsg);
+ display_message(NO_OP_COMMAND);
+ }
+
+ fs_give((void **)&errmsg);
+ }
+
+ if(sp_expunge_count(stream)) /* current msg was expunged */
+ goto seeyalater;
+
+ /* Setup possible role */
+ rflags = ROLE_REPLY;
+ if(nonempty_patterns(rflags, &dummy)){
+ role = set_role_from_msg(ps_global, rflags, msgno, a->number);
+ if(confirm_role(rflags, &role))
+ role = combine_inherited_role(role);
+ else{ /* cancel reply */
+ role = NULL;
+ cmd_cancelled("Reply");
+ goto seeyalater;
+ }
+ }
+
+ if(role){
+ rolemsg++;
+
+ /* override fcc gotten in reply_seed */
+ if(role->fcc && fcc)
+ fs_give((void **) &fcc);
+ }
+
+ if(F_ON(F_COPY_TO_TO_FROM, ps_global) && !(role && role->from)){
+ ADDRESS *us_in_to_and_cc, *ap;
+
+ us_in_to_and_cc = (ADDRESS *) NULL;
+ if(a->body->nested.msg->env && a->body->nested.msg->env->to)
+ if((ap=reply_cp_addr(ps_global, 0L, NULL,
+ NULL, us_in_to_and_cc, NULL,
+ a->body->nested.msg->env->to, RCA_ONLY_US)) != NULL)
+ reply_append_addr(&us_in_to_and_cc, ap);
+
+ if(a->body->nested.msg->env && a->body->nested.msg->env->cc)
+ if((ap=reply_cp_addr(ps_global, 0L, NULL,
+ NULL, us_in_to_and_cc, NULL,
+ a->body->nested.msg->env->cc, RCA_ONLY_US)) != NULL)
+ reply_append_addr(&us_in_to_and_cc, ap);
+
+ /*
+ * A list of all of our addresses that appear in the To
+ * and cc fields is in us_in_to_and_cc.
+ * If there is exactly one address in that list then
+ * use it for the outgoing From.
+ */
+ if(us_in_to_and_cc && !us_in_to_and_cc->next){
+ PINEFIELD *custom, *pf;
+ ADDRESS *a = NULL;
+ char *addr = NULL;
+
+ /*
+ * Check to see if this address is different from what
+ * we would have used anyway. If it is, notify the user
+ * with a status message. This is pretty hokey because we're
+ * mimicking how pine_send would set the From address and
+ * there is no coordination between the two.
+ */
+
+ /* in case user has a custom From value */
+ custom = parse_custom_hdrs(ps_global->VAR_CUSTOM_HDRS, UseAsDef);
+
+ pf = (PINEFIELD *) fs_get(sizeof(*pf));
+ memset((void *) pf, 0, sizeof(*pf));
+ pf->name = cpystr("From");
+ pf->addr = &a;
+ if(set_default_hdrval(pf, custom) >= UseAsDef
+ && pf->textbuf && pf->textbuf[0]){
+ removing_trailing_white_space(pf->textbuf);
+ (void)removing_double_quotes(pf->textbuf);
+ build_address(pf->textbuf, &addr, NULL, NULL, NULL);
+ rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
+ if(addr)
+ fs_give((void **) &addr);
+ }
+
+ if(!*pf->addr)
+ *pf->addr = generate_from();
+
+ if(*pf->addr && !address_is_same(*pf->addr, us_in_to_and_cc)){
+ copytomsg++;
+ if(!role){
+ role = (ACTION_S *) fs_get(sizeof(*role));
+ memset((void *) role, 0, sizeof(*role));
+ role->is_a_role = 1;
+ }
+
+ role->from = us_in_to_and_cc;
+ us_in_to_and_cc = NULL;
+ }
+
+ free_customs(custom);
+ free_customs(pf);
+ }
+
+ if(us_in_to_and_cc)
+ mail_free_address(&us_in_to_and_cc);
+
+ }
+
+ if(role){
+ if(rolemsg && copytomsg)
+ q_status_message1(SM_ORDER, 3, 4,
+ _("Replying using role \"%s\" and To as From"), role->nick);
+ else if(rolemsg)
+ q_status_message1(SM_ORDER, 3, 4,
+ _("Replying using role \"%s\""), role->nick);
+ else if(copytomsg)
+ q_status_message(SM_ORDER, 3, 4,
+ _("Replying using incoming To as outgoing From"));
+ }
+
+ outgoing->in_reply_to = reply_in_reply_to(a->body->nested.msg->env);
+ outgoing->references = reply_build_refs(a->body->nested.msg->env);
+ outgoing->message_id = generate_message_id();
+
+ if(!outgoing->to && !outgoing->cc
+ && !outgoing->bcc && !outgoing->newsgroups)
+ q_status_message(SM_ORDER | SM_DING, 3, 6,
+ _("Warning: no valid addresses to reply to!"));
+
+ /*
+ * Now fix up the body...
+ */
+ if((msgtext = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
+ REPLY_S reply;
+
+ memset((void *)&reply, 0, sizeof(reply));
+ reply.forw = 1;
+ if(a->body->nested.msg->body){
+ char *charset;
+
+ charset
+ = parameter_val(a->body->nested.msg->body->parameter,
+ "charset");
+
+ if(charset && strucmp(charset, "us-ascii") != 0){
+ CONV_TABLE *ct;
+
+ /*
+ * There is a non-ascii charset,
+ * is there conversion happening?
+ */
+ if(!(ct=conversion_table(charset, ps_global->posting_charmap))
+ || !ct->table){
+ reply.orig_charset = charset;
+ charset = NULL;
+ }
+ }
+
+ if(charset)
+ fs_give((void **) &charset);
+ }
+
+ if((body = reply_body(stream, a->body->nested.msg->env,
+ a->body->nested.msg->body, msgno,
+ tp = body_partno(stream, msgno, a->body),
+ msgtext, prefix, include_text, role,
+ 1, &redraft_pos)) != NULL){
+ /* partially formatted outgoing message */
+ pine_send(outgoing, &body, "COMPOSE MESSAGE REPLY",
+ role, fcc, &reply, redraft_pos, NULL, NULL, 0);
+
+ pine_free_body(&body);
+ ps_global->mangled_screen = 1;
+ }
+ else
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Error building message body"));
+
+ fs_give((void **) &tp);
+ if(reply.orig_charset)
+ fs_give((void **)&reply.orig_charset);
+ }
+ else
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Error allocating message text"));
+ }
+
+seeyalater:
+ mail_free_envelope(&outgoing);
+ mail_free_address(&saved_from);
+ mail_free_address(&saved_to);
+ mail_free_address(&saved_cc);
+ mail_free_address(&saved_resent);
+
+ if(prefix)
+ fs_give((void **) &prefix);
+
+ if(fcc)
+ fs_give((void **) &fcc);
+
+ free_redraft_pos(&redraft_pos);
+ free_action(&role);
+}
+
+
+void
+bounce_msg_att(MAILSTREAM *stream, long int msgno, char *part, char *subject)
+{
+ char *errstr;
+
+ if((errstr = bounce_msg(stream, msgno, part, NULL, NULL, subject, NULL, NULL)) != NULL)
+ q_status_message(SM_ORDER | SM_DING, 3, 3, errstr);
+}
+
+
+void
+pipe_attachment(long int msgno, ATTACH_S *a)
+{
+ char *err, *resultfilename = NULL, prompt[80], *p;
+ int rc, capture = 1, raw = 0, we_cancel = 0, j = 0;
+ long ku;
+ PIPE_S *syspipe;
+ HelpType help;
+ char pipe_command[MAXPATH+1];
+ unsigned flagsforhist = 1; /* raw=2 /capture=1 */
+ static HISTORY_S *history = NULL;
+ ESCKEY_S pipe_opt[6];
+
+ if(ps_global->restricted){
+ q_status_message(SM_ORDER | SM_DING, 0, 4,
+ "Alpine demo can't pipe attachments");
+ return;
+ }
+
+ help = NO_HELP;
+ pipe_command[0] = '\0';
+
+ init_hist(&history, HISTSIZE);
+ flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
+ if((p = get_prev_hist(history, "", flagsforhist, NULL)) != NULL){
+ strncpy(pipe_command, p, sizeof(pipe_command));
+ pipe_command[sizeof(pipe_command)-1] = '\0';
+ if(history->hist[history->curindex]){
+ flagsforhist = history->hist[history->curindex]->flags;
+ raw = (flagsforhist & 0x2) ? 1 : 0;
+ capture = (flagsforhist & 0x1) ? 1 : 0;
+ }
+ }
+
+ pipe_opt[j].ch = 0;
+ pipe_opt[j].rval = 0;
+ pipe_opt[j].name = "";
+ pipe_opt[j++].label = "";
+
+ pipe_opt[j].ch = ctrl('W');
+ pipe_opt[j].rval = 10;
+ pipe_opt[j].name = "^W";
+ pipe_opt[j++].label = NULL;
+
+ pipe_opt[j].ch = ctrl('Y');
+ pipe_opt[j].rval = 11;
+ pipe_opt[j].name = "^Y";
+ pipe_opt[j++].label = NULL;
+
+ pipe_opt[j].ch = KEY_UP;
+ pipe_opt[j].rval = 30;
+ pipe_opt[j].name = "";
+ ku = j;
+ pipe_opt[j++].label = "";
+
+ pipe_opt[j].ch = KEY_DOWN;
+ pipe_opt[j].rval = 31;
+ pipe_opt[j].name = "";
+ pipe_opt[j++].label = "";
+
+ pipe_opt[j].ch = -1;
+
+ while(1){
+ int flags;
+
+ snprintf(prompt, sizeof(prompt), "Pipe %sattachment %s to %s: ", raw ? "RAW " : "",
+ a->number, capture ? "" : "(Free Output) ");
+ prompt[sizeof(prompt)-1] = '\0';
+ pipe_opt[1].label = raw ? "DecodedData" : "Raw Data";
+ pipe_opt[2].label = capture ? "Free Output" : "Capture Output";
+
+ /*
+ * 2 is really 1 because there will be one real entry and
+ * one entry of "" because of the get_prev_hist above.
+ */
+ if(items_in_hist(history) > 2){
+ pipe_opt[ku].name = HISTORY_UP_KEYNAME;
+ pipe_opt[ku].label = HISTORY_KEYLABEL;
+ pipe_opt[ku+1].name = HISTORY_DOWN_KEYNAME;
+ pipe_opt[ku+1].label = HISTORY_KEYLABEL;
+ }
+ else{
+ pipe_opt[ku].name = "";
+ pipe_opt[ku].label = "";
+ pipe_opt[ku+1].name = "";
+ pipe_opt[ku+1].label = "";
+ }
+
+ flags = OE_APPEND_CURRENT | OE_SEQ_SENSITIVE;
+ rc = optionally_enter(pipe_command, -FOOTER_ROWS(ps_global), 0,
+ sizeof(pipe_command), prompt,
+ pipe_opt, help, &flags);
+ if(rc == -1){
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ "Internal problem encountered");
+ break;
+ }
+ else if(rc == 10){
+ raw = !raw; /* flip raw text */
+ }
+ else if(rc == 11){
+ capture = !capture; /* flip capture output */
+ }
+ else if(rc == 30){
+ flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
+ if((p = get_prev_hist(history, pipe_command, flagsforhist, NULL)) != NULL){
+ strncpy(pipe_command, p, sizeof(pipe_command));
+ pipe_command[sizeof(pipe_command)-1] = '\0';
+ if(history->hist[history->curindex]){
+ flagsforhist = history->hist[history->curindex]->flags;
+ raw = (flagsforhist & 0x2) ? 1 : 0;
+ capture = (flagsforhist & 0x1) ? 1 : 0;
+ }
+ }
+ else
+ Writechar(BELL, 0);
+ }
+ else if(rc == 31){
+ flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
+ if((p = get_next_hist(history, pipe_command, flagsforhist, NULL)) != NULL){
+ strncpy(pipe_command, p, sizeof(pipe_command));
+ pipe_command[sizeof(pipe_command)-1] = '\0';
+ if(history->hist[history->curindex]){
+ flagsforhist = history->hist[history->curindex]->flags;
+ raw = (flagsforhist & 0x2) ? 1 : 0;
+ capture = (flagsforhist & 0x1) ? 1 : 0;
+ }
+ }
+ else
+ Writechar(BELL, 0);
+ }
+ else if(rc == 0){
+ if(pipe_command[0] == '\0'){
+ cmd_cancelled("Pipe command");
+ break;
+ }
+
+ flagsforhist = (raw ? 0x2 : 0) + (capture ? 0x1 : 0);
+ save_hist(history, pipe_command, flagsforhist, NULL);
+
+ flags = PIPE_USER | PIPE_WRITE | PIPE_STDERR;
+ flags |= (raw ? PIPE_RAW : 0);
+ if(!capture){
+#ifndef _WINDOWS
+ ClearScreen();
+ fflush(stdout);
+ clear_cursor_pos();
+ ps_global->mangled_screen = 1;
+#endif
+ flags |= PIPE_RESET;
+ }
+
+ if((syspipe = open_system_pipe(pipe_command,
+ (flags&PIPE_RESET) ? NULL : &resultfilename,
+ NULL, flags, 0, pipe_callback, pipe_report_error)) != NULL){
+ int is_text = 0;
+ gf_io_t pc; /* wire up a generic putchar */
+
+ is_text = (a && a->body && a->body->type == TYPETEXT);
+
+ gf_set_writec(&pc, syspipe, 0L, PipeStar,
+ (is_text && !raw) ? WRITE_TO_LOCALE : 0);
+
+ /*------ Write the image to a temporary file ------*/
+ if(raw){ /* pipe raw text */
+ FETCH_READC_S fetch_part;
+
+ err = NULL;
+
+ if(capture)
+ we_cancel = busy_cue(NULL, NULL, 1);
+ else
+ suspend_busy_cue();
+
+ gf_filter_init();
+ fetch_readc_init(&fetch_part, ps_global->mail_stream,
+ msgno, a->number, a->body->size.bytes, 0, 0);
+ gf_link_filter(gf_nvtnl_local, NULL);
+ err = gf_pipe(FETCH_READC, pc);
+
+ if(capture){
+ if(we_cancel)
+ cancel_busy_cue(0);
+ }
+ else
+ resume_busy_cue(0);
+ }
+ else{
+ /* BUG: there's got to be a better way */
+ if(!capture)
+ ps_global->print = (PRINT_S *) 1;
+
+ suspend_busy_cue();
+ err = detach(ps_global->mail_stream, msgno,
+ a->number, 0L, (long *)NULL, pc, NULL, 0);
+ ps_global->print = (PRINT_S *) NULL;
+ resume_busy_cue(0);
+ }
+
+ (void) close_system_pipe(&syspipe, NULL, pipe_callback);
+
+ if(err)
+ q_status_message1(SM_ORDER | SM_DING, 3, 4,
+ _("Error detaching for pipe: %s"), err);
+
+ display_output_file(resultfilename,
+ (err)
+ ? _("PIPE ATTACHMENT (ERROR)")
+ : _("PIPE ATTACHMENT"),
+ NULL, DOF_EMPTY);
+
+ fs_give((void **) &resultfilename);
+ }
+ else
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Error opening pipe"));
+
+ break;
+ }
+ else if(rc == 1){
+ cmd_cancelled("Pipe");
+ break;
+ }
+ else if(rc == 3)
+ help = (help == NO_HELP) ? h_pipe_attach : NO_HELP;
+ }
+}
+
+
+int
+delete_attachment(long int msgno, ATTACH_S *a)
+{
+ int expbits, rv = 0;
+
+ if(!msgno_exceptions(ps_global->mail_stream, msgno,
+ a->number, &expbits, FALSE)
+ || !(expbits & MSG_EX_DELETE)){
+ expbits |= MSG_EX_DELETE;
+ msgno_exceptions(ps_global->mail_stream, msgno,
+ a->number, &expbits, TRUE);
+
+ q_status_message1(SM_ORDER, 0, 3,
+ _("Part %s will be omitted only if message is Saved"),
+ a->number);
+ rv = 1;
+ }
+ else
+ q_status_message1(SM_ORDER, 0, 3, _("Part %s already deleted"),
+ a->number);
+
+ return(rv);
+}
+
+
+int
+undelete_attachment(long int msgno, ATTACH_S *a, int *expbitsp)
+{
+ int rv = 0;
+
+ if(msgno_exceptions(ps_global->mail_stream, msgno,
+ a->number, expbitsp, FALSE)
+ && ((*expbitsp) & MSG_EX_DELETE)){
+ (*expbitsp) ^= MSG_EX_DELETE;
+ msgno_exceptions(ps_global->mail_stream, msgno,
+ a->number, expbitsp, TRUE);
+ rv = 1;
+ }
+ else
+ q_status_message1(SM_ORDER, 0, 3, _("Part %s already UNdeleted"),
+ a->number);
+
+ return(rv);
+}
+
+
+/*----------------------------------------------------------------------
+ Resolve any deferred tests for attachment displayability
+
+ Args: attachment structure
+
+ Returns: undefer's attachment's displayability test
+ ----*/
+int
+dispatch_attachment(ATTACH_S *a)
+{
+ if(a->test_deferred){
+ a->test_deferred = 0;
+ a->can_display = mime_can_display(a->body->type, a->body->subtype, a->body);
+ }
+
+ return(a->can_display);
+}
+
+
+#ifdef _WINDOWS
+int
+scroll_att_popup(sparms, in_handle)
+ SCROLL_S *sparms;
+ int in_handle;
+{
+ MPopup scrat_popup[20];
+ int i = -1, n;
+
+ if(in_handle){
+ scrat_popup[++i].type = tIndex;
+ scrat_popup[i].label.style = lNormal;
+ scrat_popup[i].label.string = "View Selectable Item";
+ scrat_popup[i].data.val = ctrl('L');
+ }
+
+ scrat_popup[++i].type = tQueue;
+ scrat_popup[i].label.style = lNormal;
+ scrat_popup[i].label.string = "&Save";
+ scrat_popup[i].data.val = 'S';
+
+ scrat_popup[++i].type = tQueue;
+ scrat_popup[i].label.style = lNormal;
+ if(msgno_exceptions(ps_global->mail_stream,
+ mn_m2raw(ps_global->msgmap,
+ mn_get_cur(ps_global->msgmap)),
+ scrat_attachp->number, &n, FALSE)
+ && (n & MSG_EX_DELETE)){
+ scrat_popup[i].label.string = "&Undelete";
+ scrat_popup[i].data.val = 'U';
+ }
+ else{
+ scrat_popup[i].label.string = "&Delete";
+ scrat_popup[i].data.val = 'D';
+ }
+
+ if(MIME_MSG_A(scrat_attachp) || MIME_DGST_A(scrat_attachp)){
+ scrat_popup[++i].type = tQueue;
+ scrat_popup[i].label.style = lNormal;
+ scrat_popup[i].label.string = "&Reply";
+ scrat_popup[i].data.val = 'R';
+
+ scrat_popup[++i].type = tQueue;
+ scrat_popup[i].label.style = lNormal;
+ scrat_popup[i].label.string = "&Forward";
+ scrat_popup[i].data.val = 'f';
+ }
+
+ scrat_popup[++i].type = tSeparator;
+
+ scrat_popup[++i].type = tQueue;
+ scrat_popup[i].label.style = lNormal;
+ scrat_popup[i].label.string = "Attachment Index";
+ scrat_popup[i].data.val = '<';
+
+ scrat_popup[++i].type = tTail;
+
+ return(mswin_popup(scrat_popup) == 0 && in_handle);
+}
+
+
+void
+display_att_window(a)
+ ATTACH_S *a;
+{
+#if !defined(DOS) && !defined(OS2)
+ char prefix[8];
+#endif
+
+ if(a->body->type == TYPEMULTIPART){
+ if(a->body->subtype){
+/* if(!strucmp(a->body->subtype, "digest"))
+ display_digest_att(msgno, a, flags);
+ else */
+ q_status_message1(SM_ORDER, 3, 5,
+ "Can't display Multipart/%s",
+ a->body->subtype);
+ }
+ else
+ q_status_message(SM_ORDER, 3, 5,
+ "Can't display unknown Multipart Subtype");
+ }
+/* else if(MIME_VCARD_A(a))
+ display_vcard_att_window(msgno, a, flags);*/
+ else if(a->body->type == TYPETEXT)
+ display_text_att_window(a);
+ else if(a->body->type == TYPEMESSAGE)
+ display_msg_att_window(a);
+}
+
+
+void
+display_text_att_window(a)
+ ATTACH_S *a;
+{
+ STORE_S *store;
+ long msgno;
+
+ msgno = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
+
+ if(store = format_text_att(msgno, a, NULL)){
+ if (mswin_displaytext("ATTACHED TEXT",
+ so_text(store),
+ strlen((char *) so_text(store)),
+ NULL, NULL, 0) >= 0)
+ store->txt = (void *) NULL; /* free'd in mswin_displaytext */
+
+ so_give(&store); /* free resources associated with store */
+ }
+ else
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ "Error allocating space for attachment.");
+}
+
+
+void
+display_msg_att_window(a)
+ ATTACH_S *a;
+{
+ STORE_S *store;
+ gf_io_t pc;
+ ATTACH_S *ap = a;
+ long msgno;
+
+ msgno = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
+
+ /* BUG, should check this return code */
+ (void) pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
+
+ /* initialize a storage object */
+ if(store = so_get(CharStar, NULL, EDIT_ACCESS)){
+
+ gf_set_so_writec(&pc, store);
+
+ if(format_msg_att(msgno, &ap, NULL, pc, FM_DISPLAY)
+ && mswin_displaytext("ATTACHED MESSAGE", so_text(store),
+ strlen((char *) so_text(store)),
+ NULL, NULL, 0) >= 0)
+ /* free'd in mswin_displaytext */
+ store->txt = (void *) NULL;
+
+ gf_clear_so_writec(store);
+
+ so_give(&store);
+ }
+ else
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ "Error allocating space for message.");
+}
+#endif
diff --git a/alpine/mailpart.h b/alpine/mailpart.h
new file mode 100644
index 00000000..c67cf083
--- /dev/null
+++ b/alpine/mailpart.h
@@ -0,0 +1,43 @@
+/*
+ * $Id: mailpart.h 769 2007-10-24 00:15:40Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_MAILPART_INCLUDED
+#define PINE_MAILPART_INCLUDED
+
+
+#include "../pith/mailpart.h"
+#include "context.h"
+#include "../pith/state.h"
+
+
+#define DA_SAVE 0x01 /* flags used by display_attachment */
+#define DA_FROM_VIEW 0x02 /* see mailpart.c */
+#define DA_RESIZE 0x04
+#define DA_DIDPROMPT 0x08 /* Already prompted to view att */
+
+
+/* exported protoypes */
+void attachment_screen(struct pine *);
+void write_attachment(int, long, ATTACH_S *, char *);
+int write_attachment_to_file(MAILSTREAM *, long, ATTACH_S *, int, char *);
+int display_attachment(long, ATTACH_S *, int);
+int dispatch_attachment(ATTACH_S *);
+#ifdef _WINDOWS
+void display_att_window(ATTACH_S *);
+#endif
+
+
+#endif /* PINE_MAILPART_INCLUDED */
diff --git a/alpine/mailtrfc.sh b/alpine/mailtrfc.sh
new file mode 100755
index 00000000..456ee817
--- /dev/null
+++ b/alpine/mailtrfc.sh
@@ -0,0 +1,124 @@
+#!/bin/sh
+#
+# $Id: mailtrfc.sh 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+#
+# T H E P I N E M A I L S Y S T E M
+#
+# Laurence Lundblade and Mike Seibel
+# Networks and Distributed Computing
+# Computing and Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, Washington, 98195, USA
+# Internet: lgl@CAC.Washington.EDU
+# mikes@CAC.Washington.EDU
+#
+# Please address all bugs and comments to "pine-bugs@cac.washington.edu"
+#
+#
+# Pine and Pico are registered trademarks of the University of Washington.
+# No commercial use of these trademarks may be made without prior written
+# permission of the University of Washington.
+#
+# Pine, Pico, and Pilot software and its included text are Copyright
+# 1989-1996 by the University of Washington.
+#
+# The full text of our legal notices is contained in the file called
+# CPYRIGHT, included with this distribution.
+#
+#
+# Pine is in part based on The Elm Mail System:
+# ***********************************************************************
+# * The Elm Mail System - Revision: 2.13 *
+# * *
+# * Copyright (c) 1986, 1987 Dave Taylor *
+# * Copyright (c) 1988, 1989 USENET Community Trust *
+# ***********************************************************************
+#
+#
+
+
+#
+# mailtrfc.sh -- A shell script to analyze the mail traffic as logged in
+# /usr/spool/mqueue/syslog*. This currently as the University of Washington
+# domains wired in and needs to be made more general. Also, lots more
+# formats of message ID's could be added.
+#
+
+
+
+org=`awk '/^domain/ {print $2}' < /etc/resolv.conf`
+domain=`echo $org | sed -e 's/^[^.]*\.//'`
+host=`hostname`".$org"
+
+echo "Domain: $domain"
+echo "Organization: $org"
+echo "Hostname: $host"
+
+sed -n -e '/message-id/s/^.*</</p' |
+awk 'BEGIN {mailers[0] = "Other";
+ mailers[1] = "Pine";
+ mailers[2] = "MailManager";
+ mailers[3] = "sendmail";
+ mailers[4] = "BITNET";
+ mailers[5] = "? news ?";
+ mailers[6] = "Sprint";
+ mailers[7] = "X.400";
+ mailers[8] = "Mac MS";
+ mailers[9] = "MMDF";
+ mailers[10] = "Andrew";
+ mailers[11] = "Columbia MM";
+ mailers[12] = "Unknown #1";
+ mailers[13] = "EasyMail";
+ mailers[14] = "CompuServe";
+ mailers[15] = "smail";
+ mailers[16] = "MCI Mail";
+ mailers[17] = "VAX MAIL(?)";
+ mailers[18] = "Gator Mail (?)";
+ mailers[19] = "TOTAL";
+ max = 19;}
+ {mailer = 0;}
+ /^<Pine/ {mailer = 1;}
+ /^<MailManager/ {mailer = 2;}
+ /^<[12]?[90]?9[0-9]1?[0-9][1-3]?[0-9]+\.[AaBb][AaBb][0-9]+@/ {mailer = 3;}
+ /^<[0-9A-Z]+@/ {mailer = 4;}
+ /^<199[0-9][A-Za-z]..[0-9]*\./ {mailer = 5;}
+ /@sprint.com/ {mailer = 6;}
+ /\/[A-Z]*=.*\/[A-Z]*=.*/ {mailer = 7;}
+ /^<MacMS\.[0-9]+\.[0-9]+\.[a-z]+@/ {mailer = 8;}
+ /^<MAILQUEUE-[0-9]+\.[0-9]+/ {mailer = 9;}
+ /^<.[d-l][A-Z0-9a-z=_]+00[A-Za-z0-9_=]+@/ {mailer = 10;}
+ /^<CMM\.[0-9]+\.[0-9]+\.[0-9]+/ {mailer = 11 ;}
+ /^<9[0-9][JFMASOND][aepuco][nbrylgptvc][0-9][0-9]?\.[0-9]+[a-z]+\./ {mailer = 12;}
+ /^<EasyMail\.[0-9]+/ {mailer = 13;}
+ /@CompuServe.COM/ {mailer = 14;}
+ /^<m[A-Za-z0-9].....-[0-9A-Za-z].....C@/ {mailer = 15;}
+ /@mcimail.com/ {mailer = 16;}
+ /^<9[0-9][01][0-9][0-3][0-9][0-2][0-9][0-5][0-9][0-5][0-9].[0-9a-z]*@/ {mailer = 17;}
+ /^<0[0-9][0-9]+\.[0-9][0-9][0-9][0-9]+\.[0-9][0-9]+@/ {mailer=18;}
+
+
+
+ '"/$domain>/"' {campus[mailer]++; campus[max]++}
+ '"/$org>/"' {u[mailer]++; u[max]++}
+ '"/$host>/"' {milton[mailer]++; milton[max]++}
+ {total[mailer]++; total[max]++}
+ {if(mailer == 0) printf("-->%s\n",$0)}
+ END {
+ for(m = 0; m <= max; m++) {
+ printf("%-10.10s", mailers[m]);
+ printf(" %11d %11d %11d %11d %11d (%3d%%)\n", milton[m], u[m] - milton[m], campus[m] -u[m], total[m] - campus[m], total[m], (total[m]*100)/total[max]);
+ }
+ printf(" ---- (%3d%%) (%3d%%) (%3d%%) (%3d%%)\n", (milton[max]*100)/total[max], ((u[max] - milton[max])*100)/total[max], ((campus[max] - u[max])*100)/total[max], ((total[max] - campus[max])*100)/total[max], (u[max]*100)/total[max]);
+
+ }' > /tmp/syslogx.$$
+
+
+echo $host $org $domain | \
+ awk '{printf(" %.17s %.11s %.11s Off Campus Total\n", $1, $2, $3)}'
+egrep -v 'TOTAL|----|^-->' /tmp/syslogx.$$ | sort +0.60rn
+egrep 'TOTAL|----' /tmp/syslogx.$$
+grep '^-->' /tmp/syslogx.$$ | sed -e 's/-->//' > other-traffic
+rm -f /tmp/syslogx.$$
+
+
diff --git a/alpine/mailview.c b/alpine/mailview.c
new file mode 100644
index 00000000..35f58339
--- /dev/null
+++ b/alpine/mailview.c
@@ -0,0 +1,5662 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: mailview.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+
+ mailview.c
+
+ Implements the mailview screen
+ Also includes scrolltool used to display help text
+
+ ====*/
+
+#include "headers.h"
+#include "mailcmd.h"
+#include "mailview.h"
+#include "mailindx.h"
+#include "mailpart.h"
+#include "adrbkcmd.h"
+#include "keymenu.h"
+#include "status.h"
+#include "radio.h"
+#include "help.h"
+#include "imap.h"
+#include "reply.h"
+#include "folder.h"
+#include "alpine.h"
+#include "titlebar.h"
+#include "signal.h"
+#include "send.h"
+#include "dispfilt.h"
+#include "busy.h"
+#include "smime.h"
+#include "../pith/conf.h"
+#include "../pith/filter.h"
+#include "../pith/msgno.h"
+#include "../pith/escapes.h"
+#include "../pith/flag.h"
+#include "../pith/mimedesc.h"
+#include "../pith/url.h"
+#include "../pith/bldaddr.h"
+#include "../pith/mailcmd.h"
+#include "../pith/newmail.h"
+#include "../pith/pipe.h"
+#include "../pith/thread.h"
+#include "../pith/util.h"
+#include "../pith/detoken.h"
+#include "../pith/editorial.h"
+#include "../pith/maillist.h"
+#include "../pith/hist.h"
+#include "../pith/busy.h"
+#include "../pith/list.h"
+#include "../pith/detach.h"
+
+
+/*----------------------------------------------------------------------
+ Saved state for scrolling text
+ ----*/
+typedef struct scroll_text {
+ SCROLL_S *parms; /* Original text (file, char *, char **) */
+ char **text_lines, /* Lines to display */
+ *fname; /* filename of line offsets in "text" */
+ FILE *findex; /* file pointer to line offsets in "text" */
+ short *line_lengths; /* Length of each line in "text_lines" */
+ long top_text_line, /* index in "text_lines" top displayed line */
+ num_lines; /* number of valid pointers in "text_lines" */
+ int lines_allocated; /* size of "text_lines" array */
+ struct {
+ int length, /* count of displayable lines (== PGSIZE) */
+ width, /* width of displayable lines */
+ start_line, /* line number to start painting on */
+ other_lines; /* # of lines not for scroll text */
+ } screen; /* screen parameters */
+} SCRLCTRL_S;
+
+
+typedef struct scroll_file {
+ long offset;
+ int len;
+} SCRLFILE_S;
+
+
+/*
+ * Struct to help write lines do display as they're decoded
+ */
+struct view_write_s {
+ char *line;
+ int index,
+ screen_line,
+ last_screen_line;
+#ifdef _WINDOWS
+ long lines;
+#endif
+ HANDLE_S **handles;
+ STORE_S *store;
+} *g_view_write;
+
+#define LINEBUFSIZ (4096)
+
+#define MAX_FUDGE (1024*1024)
+
+/*
+ * Definitions to help scrolltool
+ */
+#define SCROLL_LINES_ABOVE(X) HEADER_ROWS(X)
+#define SCROLL_LINES_BELOW(X) FOOTER_ROWS(X)
+#define SCROLL_LINES(X) MAX(((X)->ttyo->screen_rows \
+ - SCROLL_LINES_ABOVE(X) - SCROLL_LINES_BELOW(X)), 0)
+#define scroll_text_lines() (scroll_state(SS_CUR)->num_lines)
+
+
+/*
+ * Definitions for various scroll state manager's functions
+ */
+#define SS_NEW 1
+#define SS_CUR 2
+#define SS_FREE 3
+
+
+/*
+ * Handle hints.
+ */
+#define HANDLE_INIT_MSG \
+ _("Selectable items in text -- Use Up/Down Arrows to choose, Return to view")
+#define HANDLE_ABOVE_ERR \
+ _("No selected item displayed -- Use PrevPage to bring choice into view")
+#define HANDLE_BELOW_ERR \
+ _("No selected item displayed -- Use NextPage to bring choice into view")
+
+
+#define PGSIZE(X) (ps_global->ttyo->screen_rows - (X)->screen.other_lines)
+
+#define TYPICAL_BIG_MESSAGE_LINES 200
+
+
+/*
+ * Internal prototypes
+ */
+void view_writec_killbuf(void);
+int view_end_scroll(SCROLL_S *);
+long format_size_guess(BODY *);
+int scroll_handle_prompt(HANDLE_S *, int);
+int scroll_handle_launch(HANDLE_S *, int);
+int scroll_handle_obscured(HANDLE_S *);
+HANDLE_S *scroll_handle_in_frame(long);
+long scroll_handle_reframe(int, int);
+int handle_on_line(long, int);
+int handle_on_page(HANDLE_S *, long, long);
+int scroll_handle_selectable(HANDLE_S *);
+HANDLE_S *scroll_handle_next_sel(HANDLE_S *);
+HANDLE_S *scroll_handle_prev_sel(HANDLE_S *);
+HANDLE_S *scroll_handle_next(HANDLE_S *);
+HANDLE_S *scroll_handle_prev(HANDLE_S *);
+HANDLE_S *scroll_handle_boundary(HANDLE_S *, HANDLE_S *(*)(HANDLE_S *));
+int scroll_handle_column(int, int);
+int scroll_handle_index(int, int);
+void scroll_handle_set_loc(POSLIST_S **, int, int);
+int dot_on_handle(long, int);
+int url_launch(HANDLE_S *);
+int url_launch_too_long(int);
+char *url_external_handler(HANDLE_S *, int);
+void url_mailto_addr(ADDRESS **, char *);
+int url_local_imap(char *);
+int url_local_nntp(char *);
+int url_local_news(char *);
+int url_local_file(char *);
+int url_local_phone_home(char *);
+static int print_to_printer(SCROLL_S *);
+int search_text(int, long, int, char **, Pos *, int *);
+void update_scroll_titlebar(long, int);
+SCRLCTRL_S *scroll_state(int);
+void set_scroll_text(SCROLL_S *, long, SCRLCTRL_S *);
+void redraw_scroll_text(void);
+void zero_scroll_text(void);
+void format_scroll_text(void);
+void ScrollFile(long);
+long make_file_index(void);
+char *scroll_file_line(FILE *, char *, SCRLFILE_S *, int *);
+long scroll_scroll_text(long, HANDLE_S *, int);
+int search_scroll_text(long, int, char *, Pos *, int *);
+char *search_scroll_line(char *, char *, int, int);
+int visible_linelen(int);
+long doubleclick_handle(SCROLL_S *, HANDLE_S *, int *, int *);
+#ifdef _WINDOWS
+int format_message_popup(SCROLL_S *, int);
+int simple_text_popup(SCROLL_S *, int);
+int mswin_readscrollbuf(int);
+int pcpine_do_scroll(int, long);
+char *pcpine_help_scroll(char *);
+int pcpine_view_cursor(int, long);
+#endif
+
+
+
+/*----------------------------------------------------------------------
+ Format a buffer with the text of the current message for browser
+
+ Args: ps - pine state structure
+
+ Result: The scrolltool is called to display the message
+
+ Loop here viewing mail until the folder changed or a command takes
+us to another screen. Inside the loop the message text is fetched and
+formatted into a buffer allocated for it. These are passed to the
+scrolltool(), that displays the message and executes commands. It
+returns when it's time to display a different message, when we
+change folders, when it's time for a different screen, or when
+there are no more messages available.
+ ---*/
+
+void
+mail_view_screen(struct pine *ps)
+{
+ char last_was_full_header = 0;
+ long last_message_viewed = -1L, raw_msgno, offset = 0L;
+ OtherMenu save_what = FirstMenu;
+ int we_cancel = 0, flags, cmd = 0;
+ int force_prefer = 0;
+ MESSAGECACHE *mc;
+ ENVELOPE *env;
+ BODY *body;
+ STORE_S *store;
+ HANDLE_S *handles = NULL;
+ SCROLL_S scrollargs;
+ SourceType src = CharStar;
+
+ dprint((1, "\n\n ----- MAIL VIEW -----\n"));
+
+ ps->prev_screen = mail_view_screen;
+ ps->force_prefer_plain = ps->force_no_prefer_plain = 0;
+
+ if(ps->ttyo->screen_rows - HEADER_ROWS(ps) - FOOTER_ROWS(ps) < 1){
+ q_status_message(SM_ORDER | SM_DING, 0, 3,
+ _("Screen too small to view message"));
+ ps->next_screen = mail_index_screen;
+ return;
+ }
+
+ /*----------------- Loop viewing messages ------------------*/
+ do {
+
+ ps->user_says_cancel = 0;
+ ps->some_quoting_was_suppressed = 0;
+
+ /*
+ * Check total to make sure there's something to view. Check it
+ * inside the loop to make sure everything wasn't expunged while
+ * we were viewing. If so, make sure we don't just come back.
+ */
+ if(mn_get_total(ps->msgmap) <= 0L || !ps->mail_stream){
+ q_status_message(SM_ORDER, 0, 3, _("No messages to read!"));
+ ps->next_screen = mail_index_screen;
+ break;
+ }
+
+ we_cancel = busy_cue(NULL, NULL, 1);
+
+ if(mn_get_cur(ps->msgmap) <= 0L)
+ mn_set_cur(ps->msgmap,
+ THREADING() ? first_sorted_flagged(F_NONE,
+ ps->mail_stream,
+ 0L, FSF_SKIP_CHID)
+ : 1L);
+
+ raw_msgno = mn_m2raw(ps->msgmap, mn_get_cur(ps->msgmap));
+ body = NULL;
+ if(raw_msgno == 0L
+ || !(env = pine_mail_fetchstructure(ps->mail_stream,raw_msgno,&body))
+ || !(raw_msgno > 0L && ps->mail_stream
+ && raw_msgno <= ps->mail_stream->nmsgs
+ && (mc = mail_elt(ps->mail_stream, raw_msgno)))){
+ q_status_message1(SM_ORDER, 3, 3,
+ "Error getting message %s data",
+ comatose(mn_get_cur(ps->msgmap)));
+ dprint((1, "!!!! ERROR fetching %s of msg %ld\n",
+ env ? "elt" : "env", mn_get_cur(ps->msgmap)));
+
+ ps->next_screen = mail_index_screen;
+ break;
+ }
+ else
+ ps->unseen_in_view = !mc->seen;
+
+ init_handles(&handles);
+
+ store = so_get(src, NULL, EDIT_ACCESS);
+ so_truncate(store, format_size_guess(body) + 2048);
+
+ view_writec_init(store, &handles, SCROLL_LINES_ABOVE(ps),
+ SCROLL_LINES_ABOVE(ps) +
+ ps->ttyo->screen_rows - (SCROLL_LINES_ABOVE(ps)
+ + SCROLL_LINES_BELOW(ps)));
+
+ flags = FM_DISPLAY;
+ if(last_message_viewed != mn_get_cur(ps->msgmap)
+ || last_was_full_header == 2 || cmd == MC_TOGGLE)
+ flags |= FM_NEW_MESS;
+
+ if(F_OFF(F_QUELL_FULL_HDR_RESET, ps_global)
+ && last_message_viewed != -1L
+ && last_message_viewed != mn_get_cur(ps->msgmap))
+ ps->full_header = 0;
+
+ if(offset) /* no pre-paint during resize */
+ view_writec_killbuf();
+
+#ifdef SMIME
+ /* Attempt to handle S/MIME bodies */
+ if(ps->smime)
+ ps->smime->need_passphrase = 0;
+
+ if(F_OFF(F_DONT_DO_SMIME, ps_global) && fiddle_smime_message(body, raw_msgno))
+ flags |= FM_NEW_MESS; /* body was changed, force a reload */
+#endif
+
+#ifdef _WINDOWS
+ mswin_noscrollupdate(1);
+#endif
+ ps->cur_uid_stream = ps->mail_stream;
+ ps->cur_uid = mail_uid(ps->mail_stream, raw_msgno);
+ (void) format_message(raw_msgno, env, body, &handles, flags | force_prefer,
+ view_writec);
+#ifdef _WINDOWS
+ mswin_noscrollupdate(0);
+#endif
+
+ view_writec_destroy();
+
+ last_message_viewed = mn_get_cur(ps->msgmap);
+ last_was_full_header = ps->full_header;
+
+ ps->next_screen = SCREEN_FUN_NULL;
+
+ memset(&scrollargs, 0, sizeof(SCROLL_S));
+ scrollargs.text.text = so_text(store);
+ scrollargs.text.src = src;
+ scrollargs.text.desc = "message";
+
+ /*
+ * make first selectable handle the default
+ */
+ if(handles){
+ HANDLE_S *hp;
+
+ hp = handles;
+ while(!scroll_handle_selectable(hp) && hp != NULL)
+ hp = hp->next;
+
+ if((scrollargs.text.handles = hp) != NULL)
+ scrollargs.body_valid = (hp == handles);
+ else
+ free_handles(&handles);
+ }
+ else
+ scrollargs.body_valid = 1;
+
+ if(offset){ /* resize? preserve paging! */
+ scrollargs.start.on = Offset;
+ scrollargs.start.loc.offset = offset;
+ scrollargs.body_valid = 0;
+ offset = 0L;
+ }
+
+ scrollargs.use_indexline_color = 1;
+
+ /* TRANSLATORS: a screen title */
+ scrollargs.bar.title = _("MESSAGE TEXT");
+ scrollargs.end_scroll = view_end_scroll;
+ scrollargs.resize_exit = 1;
+ scrollargs.help.text = h_mail_view;
+ scrollargs.help.title = _("HELP FOR MESSAGE TEXT VIEW");
+ scrollargs.keys.menu = &view_keymenu;
+ scrollargs.keys.what = save_what;
+ setbitmap(scrollargs.keys.bitmap);
+ if(F_OFF(F_ENABLE_PIPE, ps_global))
+ clrbitn(VIEW_PIPE_KEY, scrollargs.keys.bitmap);
+
+ /*
+ * turn off attachment viewing for raw msg txt, atts
+ * haven't been set up at this point
+ */
+ if(ps_global->full_header == 2
+ && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global))
+ clrbitn(VIEW_ATT_KEY, scrollargs.keys.bitmap);
+
+ if(F_OFF(F_ENABLE_BOUNCE, ps_global))
+ clrbitn(BOUNCE_KEY, scrollargs.keys.bitmap);
+
+ if(F_OFF(F_ENABLE_FLAG, ps_global))
+ clrbitn(FLAG_KEY, scrollargs.keys.bitmap);
+
+ if(F_OFF(F_ENABLE_AGG_OPS, ps_global))
+ clrbitn(VIEW_SELECT_KEY, scrollargs.keys.bitmap);
+
+ if(F_OFF(F_ENABLE_FULL_HDR, ps_global))
+ clrbitn(VIEW_FULL_HEADERS_KEY, scrollargs.keys.bitmap);
+
+#ifdef SMIME
+ if(!(ps->smime && ps->smime->need_passphrase))
+ clrbitn(DECRYPT_KEY, scrollargs.keys.bitmap);
+
+ if(F_ON(F_DONT_DO_SMIME, ps_global)){
+ clrbitn(DECRYPT_KEY, scrollargs.keys.bitmap);
+ clrbitn(SECURITY_KEY, scrollargs.keys.bitmap);
+ }
+#endif
+
+ if(!handles){
+ /*
+ * NOTE: the comment below only really makes sense if we
+ * decide to replace the "Attachment Index" with
+ * a model that lets you highlight the attachments
+ * in the header. In a way its consistent, but
+ * would mean more keymenu monkeybusiness since not
+ * all things would be available all the time. No wait.
+ * Then what would "Save" mean; the attachment, url or
+ * message you're currently viewing? Would Save
+ * of a message then only be possible from the message
+ * index? Clumsy. what about arrow-navigation. isn't
+ * that now inconsistent? Maybe next/prev url shouldn't
+ * be bound to the arrow/^N/^P navigation?
+ */
+ clrbitn(VIEW_VIEW_HANDLE, scrollargs.keys.bitmap);
+ clrbitn(VIEW_PREV_HANDLE, scrollargs.keys.bitmap);
+ clrbitn(VIEW_NEXT_HANDLE, scrollargs.keys.bitmap);
+ }
+
+#ifdef _WINDOWS
+ scrollargs.mouse.popup = format_message_popup;
+#endif
+
+ if(((cmd = scrolltool(&scrollargs)) == MC_RESIZE
+ || (cmd == MC_FULLHDR && ps_global->full_header == 1))
+ && scrollargs.start.on == Offset)
+ offset = scrollargs.start.loc.offset;
+
+ if(cmd == MC_TOGGLE && ps->force_prefer_plain == 0 && ps->force_no_prefer_plain == 0){
+ if(F_ON(F_PREFER_PLAIN_TEXT, ps_global))
+ ps->force_no_prefer_plain = 1;
+ else
+ ps->force_prefer_plain = 1;
+ }
+ else{
+ ps->force_prefer_plain = ps->force_no_prefer_plain = 0;
+ }
+
+ /*
+ * We could use the values directly but this is the way it
+ * already worked with the flags, so leave it alone.
+ */
+ if(ps->force_prefer_plain == 0 && ps->force_no_prefer_plain == 0)
+ force_prefer = 0;
+ else if(ps->force_prefer_plain)
+ force_prefer = FM_FORCEPREFPLN;
+ else if(ps->force_no_prefer_plain)
+ force_prefer = FM_FORCENOPREFPLN;
+
+ save_what = scrollargs.keys.what;
+ ps_global->unseen_in_view = 0;
+ so_give(&store); /* free resources associated with store */
+ free_handles(&handles);
+#ifdef _WINDOWS
+ mswin_destroyicons();
+#endif
+ }
+ while(ps->next_screen == SCREEN_FUN_NULL);
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ ps->force_prefer_plain = ps->force_no_prefer_plain = 0;
+
+ ps->cur_uid_stream = NULL;
+ ps->cur_uid = 0;
+
+ /*
+ * Unless we're going into attachment screen,
+ * start over with full_header.
+ */
+ if(F_OFF(F_QUELL_FULL_HDR_RESET, ps_global)
+ && ps->next_screen != attachment_screen)
+ ps->full_header = 0;
+}
+
+
+
+/*
+ * view_writec_init - function to create and init struct that manages
+ * writing to the display what we can as soon
+ * as we can.
+ */
+void
+view_writec_init(STORE_S *store, HANDLE_S **handlesp, int first_line, int last_line)
+{
+ char tmp[MAILTMPLEN];
+
+ g_view_write = (struct view_write_s *)fs_get(sizeof(struct view_write_s));
+ memset(g_view_write, 0, sizeof(struct view_write_s));
+ g_view_write->store = store;
+ g_view_write->handles = handlesp;
+ g_view_write->screen_line = first_line;
+ g_view_write->last_screen_line = last_line;
+
+ if(!dfilter_trigger(NULL, tmp, sizeof(tmp))){
+ g_view_write->line = (char *) fs_get(LINEBUFSIZ*sizeof(char));
+#ifdef _WINDOWS
+ mswin_beginupdate();
+ scroll_setrange(0L, 0L);
+#endif
+
+ if(ps_global->VAR_DISPLAY_FILTERS)
+ ClearLines(first_line, last_line - 1);
+ }
+}
+
+
+
+void
+view_writec_destroy(void)
+{
+ if(g_view_write){
+ if(g_view_write->line && g_view_write->index)
+ view_writec('\n'); /* flush pending output! */
+
+ while(g_view_write->screen_line < g_view_write->last_screen_line)
+ ClearLine(g_view_write->screen_line++);
+
+ view_writec_killbuf();
+
+ fs_give((void **) &g_view_write);
+ }
+
+#ifdef _WINDOWS
+ mswin_endupdate();
+#endif
+}
+
+
+
+void
+view_writec_killbuf(void)
+{
+ if(g_view_write->line)
+ fs_give((void **) &g_view_write->line);
+}
+
+
+
+/*
+ * view_screen_pc - write chars into the final buffer
+ */
+int
+view_writec(int c)
+{
+ static int in_color = 0;
+
+ if(g_view_write->line){
+ /*
+ * This only works if the 2nd and 3rd parts of the || don't happen.
+ * The only way it breaks is if we get a really, really long line
+ * because there are oodles of tags in it. In that case we will
+ * wrap incorrectly or split a tag across lines (losing color perhaps)
+ * but we won't crash.
+ */
+ if(c == '\n' ||
+ (!in_color &&
+ (char)c != TAG_EMBED &&
+ g_view_write->index >= (LINEBUFSIZ - 20 * (RGBLEN+2))) ||
+ (g_view_write->index >= LINEBUFSIZ - 1)){
+ int rv;
+
+ in_color = 0;
+ suspend_busy_cue();
+ ClearLine(g_view_write->screen_line);
+ if(c != '\n')
+ g_view_write->line[g_view_write->index++] = (char) c;
+
+ PutLine0n8b(g_view_write->screen_line++, 0,
+ g_view_write->line, g_view_write->index,
+ g_view_write->handles ? *g_view_write->handles : NULL);
+
+ resume_busy_cue(0);
+ rv = so_nputs(g_view_write->store,
+ g_view_write->line,
+ g_view_write->index);
+ g_view_write->index = 0;
+
+ if(g_view_write->screen_line >= g_view_write->last_screen_line){
+ fs_give((void **) &g_view_write->line);
+ fflush(stdout);
+ }
+
+ if(!rv)
+ return(0);
+ else if(c != '\n')
+ return(1);
+ }
+ else{
+ /*
+ * Don't split embedded things over multiple lines. Colors are
+ * the longest tags so use their length.
+ */
+ if((char)c == TAG_EMBED)
+ in_color = RGBLEN+1;
+ else if(in_color)
+ in_color--;
+
+ g_view_write->line[g_view_write->index++] = (char) c;
+ return(1);
+ }
+ }
+#ifdef _WINDOWS
+ else if(c == '\n' && g_view_write->lines++ > SCROLL_LINES(ps_global))
+ scroll_setrange(SCROLL_LINES(ps_global),
+ g_view_write->lines + SCROLL_LINES(ps_global));
+#endif
+
+ return(so_writec(c, g_view_write->store));
+}
+
+int
+view_end_scroll(SCROLL_S *sparms)
+{
+ int done = 0, result, force;
+
+ if(F_ON(F_ENABLE_SPACE_AS_TAB, ps_global)){
+
+ if(F_ON(F_ENABLE_TAB_DELETES, ps_global)){
+ long save_msgno;
+
+ /* Let the TAB advance cur msgno for us */
+ save_msgno = mn_get_cur(ps_global->msgmap);
+ (void) cmd_delete(ps_global, ps_global->msgmap, MCMD_NONE, NULL);
+ mn_set_cur(ps_global->msgmap, save_msgno);
+ }
+
+ /* act like the user hit a TAB */
+ result = process_cmd(ps_global, ps_global->mail_stream,
+ ps_global->msgmap, MC_TAB, View, &force);
+
+ if(result == 1)
+ done = 1;
+ }
+
+ return(done);
+}
+
+
+/*
+ * format_size_guess -- Run down the given body summing the text/plain
+ * pieces we're likely to display. It need only
+ * be a guess since this is intended for preallocating
+ * our display buffer...
+ */
+long
+format_size_guess(struct mail_bodystruct *body)
+{
+ long size = 0L;
+ long extra = 0L;
+ char *free_me = NULL;
+
+ if(body){
+ if(body->type == TYPEMULTIPART){
+ PART *part;
+
+ for(part = body->nested.part; part; part = part->next)
+ size += format_size_guess(&part->body);
+ }
+ else if(body->type == TYPEMESSAGE
+ && body->subtype && !strucmp(body->subtype, "rfc822"))
+ size = format_size_guess(body->nested.msg->body);
+ else if((!body->type || body->type == TYPETEXT)
+ && (!body->subtype || !strucmp(body->subtype, "plain"))
+ && ((body->disposition.type
+ && !strucmp(body->disposition.type, "inline"))
+ || !(free_me = parameter_val(body->parameter, "name")))){
+ /*
+ * Handles and colored quotes cause memory overhead. Figure about
+ * 100 bytes per level of quote per line and about 100 bytes per
+ * handle. Make a guess of 1 handle or colored quote per
+ * 10 lines of message. If we guess too low, we'll do a resize in
+ * so_cs_writec. Most of the overhead comes from the colors, so
+ * if we can see we don't do colors or don't have these features
+ * turned on, skip it.
+ */
+ if(pico_usingcolor() &&
+ ((ps_global->VAR_QUOTE1_FORE_COLOR &&
+ ps_global->VAR_QUOTE1_BACK_COLOR) ||
+ F_ON(F_VIEW_SEL_URL, ps_global) ||
+ F_ON(F_VIEW_SEL_URL_HOST, ps_global) ||
+ F_ON(F_SCAN_ADDR, ps_global)))
+ extra = MIN(100/10 * body->size.lines, MAX_FUDGE);
+
+ size = body->size.bytes + extra;
+ }
+
+ if(free_me)
+ fs_give((void **) &free_me);
+ }
+
+ return(size);
+}
+
+
+int
+scroll_handle_prompt(HANDLE_S *handle, int force)
+{
+ char prompt[256], tmp[MAILTMPLEN];
+ int rc, flags, local_h;
+ static ESCKEY_S launch_opts[] = {
+ /* TRANSLATORS: command names, editURL means user gets to edit a URL if they
+ want, editApp is edit application where they edit the application used to
+ view a URL */
+ {'y', 'y', "Y", N_("Yes")},
+ {'n', 'n', "N", N_("No")},
+ {-2, 0, NULL, NULL},
+ {-2, 0, NULL, NULL},
+ {0, 'u', "U", N_("editURL")},
+ {0, 'a', "A", N_("editApp")},
+ {-1, 0, NULL, NULL}};
+
+ if(handle->type == URL){
+ launch_opts[4].ch = 'u';
+
+ if((!(local_h = !struncmp(handle->h.url.path, "x-alpine-", 9))
+ || !(local_h = !struncmp(handle->h.url.path, "x-pine-help", 11)))
+ && (handle->h.url.tool
+ || ((local_h = url_local_handler(handle->h.url.path) != NULL)
+ && (handle->h.url.tool = url_external_handler(handle,1)))
+ || (!local_h
+ && (handle->h.url.tool = url_external_handler(handle,0))))){
+#ifdef _WINDOWS
+ /* if NOT special DDE hack */
+ if(handle->h.url.tool[0] != '*')
+#endif
+ if(ps_global->vars[V_BROWSER].is_fixed)
+ launch_opts[5].ch = -1;
+ else
+ launch_opts[5].ch = 'a';
+ }
+ else{
+ launch_opts[5].ch = -1;
+ if(!local_h){
+ if(ps_global->vars[V_BROWSER].is_fixed){
+ q_status_message(SM_ORDER, 3, 4,
+ _("URL-Viewer is disabled by sys-admin"));
+ return(0);
+ }
+ else{
+ /* TRANSLATORS: a question */
+ if(want_to(_("No URL-Viewer application defined. Define now"),
+ 'y', 0, NO_HELP, WT_SEQ_SENSITIVE) == 'y'){
+ /* Prompt for the displayer? */
+ tmp[0] = '\0';
+ while(1){
+ flags = OE_APPEND_CURRENT |
+ OE_SEQ_SENSITIVE |
+ OE_KEEP_TRAILING_SPACE;
+
+ rc = optionally_enter(tmp, -FOOTER_ROWS(ps_global), 0,
+ sizeof(tmp),
+ _("Web Browser: "),
+ NULL, NO_HELP, &flags);
+ if(rc == 0){
+ if((flags & OE_USER_MODIFIED) && *tmp){
+ if(can_access(tmp, EXECUTE_ACCESS) == 0){
+ int n;
+ char **l;
+
+ /*
+ * Save it for next time...
+ */
+ for(l = ps_global->VAR_BROWSER, n = 0;
+ l && *l;
+ l++)
+ n++; /* count */
+
+ l = (char **) fs_get((n+2)*sizeof(char *));
+ for(n = 0;
+ ps_global->VAR_BROWSER
+ && ps_global->VAR_BROWSER[n];
+ n++)
+ l[n] = cpystr(ps_global->VAR_BROWSER[n]);
+
+ l[n++] = cpystr(tmp);
+ l[n] = NULL;
+
+ set_variable_list(V_BROWSER, l, TRUE, Main);
+ free_list_array(&l);
+
+ handle->h.url.tool = cpystr(tmp);
+ break;
+ }
+ else{
+ q_status_message1(SM_ORDER | SM_DING, 2, 2,
+ _("Browser not found: %s"),
+ error_description(errno));
+ continue;
+ }
+ }
+ else
+ return(0);
+ }
+ else if(rc == 1 || rc == -1){
+ return(0);
+ }
+ else if(rc == 4){
+ if(ps_global->redrawer)
+ (*ps_global->redrawer)();
+ }
+ }
+ }
+ else
+ return(0);
+ }
+ }
+ }
+ }
+ else
+ launch_opts[4].ch = -1;
+
+ if(force
+ || (handle->type == URL
+ && !struncmp(handle->h.url.path, "x-alpine-", 9)
+ || !struncmp(handle->h.url.path, "x-pine-help", 11)))
+ return(1);
+
+ while(1){
+ int sc = ps_global->ttyo->screen_cols;
+
+ /*
+ * Customize the prompt for mailto, all the other schemes make
+ * sense if you just say View selected URL ...
+ */
+ if(handle->type == URL &&
+ !struncmp(handle->h.url.path, "mailto:", 7))
+ snprintf(prompt, sizeof(prompt), "Compose mail to \"%.*s%s\" ? ",
+ MIN(MAX(0,sc - 25), sizeof(prompt)-50), handle->h.url.path+7,
+ (strlen(handle->h.url.path+7) > MAX(0,sc-25)) ? "..." : "");
+ else
+ snprintf(prompt, sizeof(prompt), "View selected %s %s%.*s%s ? ",
+ (handle->type == URL) ? "URL" : "Attachment",
+ (handle->type == URL) ? "\"" : "",
+ MIN(MAX(0,sc-27), sizeof(prompt)-50),
+ (handle->type == URL) ? handle->h.url.path : "",
+ (handle->type == URL)
+ ? ((strlen(handle->h.url.path) > MAX(0,sc-27))
+ ? "...\"" : "\"") : "");
+
+ prompt[sizeof(prompt)-1] = '\0';
+
+ switch(radio_buttons(prompt, -FOOTER_ROWS(ps_global),
+ launch_opts, 'y', 'n', NO_HELP, RB_SEQ_SENSITIVE)){
+ case 'y' :
+ return(1);
+
+ case 'u' :
+ strncpy(tmp, handle->h.url.path, sizeof(tmp)-1);
+ tmp[sizeof(tmp)-1] = '\0';
+ while(1){
+ flags = OE_APPEND_CURRENT |
+ OE_SEQ_SENSITIVE |
+ OE_KEEP_TRAILING_SPACE;
+
+ rc = optionally_enter(tmp, -FOOTER_ROWS(ps_global), 0,
+ sizeof(tmp), _("Edit URL: "),
+ NULL, NO_HELP, &flags);
+ if(rc == 0){
+ if(flags & OE_USER_MODIFIED){
+ if(handle->h.url.path)
+ fs_give((void **) &handle->h.url.path);
+
+ handle->h.url.path = cpystr(tmp);
+ }
+
+ break;
+ }
+ else if(rc == 1 || rc == -1){
+ return(0);
+ }
+ else if(rc == 4){
+ if(ps_global->redrawer)
+ (*ps_global->redrawer)();
+ }
+ }
+
+ continue;
+
+ case 'a' :
+ if(handle->h.url.tool){
+ strncpy(tmp, handle->h.url.tool, sizeof(tmp)-1);
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+ else
+ tmp[0] = '\0';
+
+ while(1){
+ flags = OE_APPEND_CURRENT |
+ OE_SEQ_SENSITIVE |
+ OE_KEEP_TRAILING_SPACE |
+ OE_DISALLOW_HELP;
+
+ rc = optionally_enter(tmp, -FOOTER_ROWS(ps_global), 0,
+ sizeof(tmp), _("Viewer Command: "),
+ NULL, NO_HELP, &flags);
+ if(rc == 0){
+ if(flags & OE_USER_MODIFIED){
+ if(handle->h.url.tool)
+ fs_give((void **) &handle->h.url.tool);
+
+ handle->h.url.tool = cpystr(tmp);
+ }
+
+ break;
+ }
+ else if(rc == 1 || rc == -1){
+ return(0);
+ }
+ else if(rc == 4){
+ if(ps_global->redrawer)
+ (*ps_global->redrawer)();
+ }
+ }
+
+ continue;
+
+ case 'n' :
+ default :
+ return(0);
+ }
+ }
+}
+
+
+
+int
+scroll_handle_launch(HANDLE_S *handle, int force)
+{
+ switch(handle->type){
+ case URL :
+ if(handle->h.url.path){
+ if(scroll_handle_prompt(handle, force)){
+ if(url_launch(handle)
+ || ps_global->next_screen != SCREEN_FUN_NULL)
+ return(1); /* done with this screen */
+ }
+ else
+ return(-1);
+ }
+
+ break;
+
+ case Attach :
+ if(scroll_handle_prompt(handle, force))
+ display_attachment(mn_m2raw(ps_global->msgmap,
+ mn_get_cur(ps_global->msgmap)),
+ handle->h.attach, DA_FROM_VIEW | DA_DIDPROMPT);
+ else
+ return(-1);
+
+ break;
+
+ case Folder :
+ break;
+
+ case Function :
+ (*handle->h.func.f)(handle->h.func.args.stream,
+ handle->h.func.args.msgmap,
+ handle->h.func.args.msgno);
+ break;
+
+
+ default :
+ panic("Unexpected HANDLE type");
+ }
+
+ return(0);
+}
+
+
+int
+scroll_handle_obscured(HANDLE_S *handle)
+{
+ SCRLCTRL_S *st = scroll_state(SS_CUR);
+
+ return(handle_on_page(handle, st->top_text_line,
+ st->top_text_line + st->screen.length));
+}
+
+
+
+/*
+ * scroll_handle_in_frame -- return handle pointer to visible handle.
+ */
+HANDLE_S *
+scroll_handle_in_frame(long int top_line)
+{
+ SCRLCTRL_S *st = scroll_state(SS_CUR);
+ HANDLE_S *hp;
+
+ switch(handle_on_page(hp = st->parms->text.handles, top_line,
+ top_line + st->screen.length)){
+ case -1 : /* handle above page */
+ /* Find first handle from top of page */
+ for(hp = st->parms->text.handles->next; hp; hp = hp->next)
+ if(scroll_handle_selectable(hp))
+ switch(handle_on_page(hp, top_line, top_line + st->screen.length)){
+ case 0 : return(hp);
+ case 1 : return(NULL);
+ case -1 : default : break;
+ }
+
+ break;
+
+ case 1 : /* handle below page */
+ /* Find first handle from top of page */
+ for(hp = st->parms->text.handles->prev; hp; hp = hp->prev)
+ if(scroll_handle_selectable(hp))
+ switch(handle_on_page(hp, top_line, top_line + st->screen.length)){
+ case 0 : return(hp);
+ case -1 : return(NULL);
+ case 1 : default : break;
+ }
+
+ break;
+
+ case 0 :
+ default :
+ break;
+ }
+
+ return(hp);
+}
+
+/*
+ * scroll_handle_reframe -- adjust display params to display given handle
+ */
+long
+scroll_handle_reframe(int key, int center)
+{
+ long l, offset, dlines, start_line;
+ SCRLCTRL_S *st = scroll_state(SS_CUR);
+
+ dlines = PGSIZE(st);
+ offset = (st->parms->text.src == FileStar) ? st->top_text_line : 0;
+ start_line = st->top_text_line;
+
+ if(key < 0)
+ key = st->parms->text.handles->key;
+
+ /* Searc down from the top line */
+ for(l = start_line; l < st->num_lines; l++) {
+ if(st->parms->text.src == FileStar && l > offset + dlines)
+ ScrollFile(offset += dlines);
+
+ if(handle_on_line(l - offset, key))
+ break;
+ }
+
+ if(l < st->num_lines){
+ if(l >= dlines + start_line) /* bingo! */
+ start_line = l - ((center ? (dlines / 2) : dlines) - 1);
+ }
+ else{
+ if(st->parms->text.src == FileStar) /* wrap offset */
+ ScrollFile(offset = 0);
+
+ for(l = 0; l < start_line; l++) {
+ if(st->parms->text.src == FileStar && l > offset + dlines)
+ ScrollFile(offset += dlines);
+
+ if(handle_on_line(l - offset, key))
+ break;
+ }
+
+ if(l == start_line)
+ panic("Internal Error: no handle found");
+ else
+ start_line = l;
+ }
+
+ return(start_line);
+}
+
+
+int
+handle_on_line(long int line, int goal)
+{
+ int i, n, key;
+ SCRLCTRL_S *st = scroll_state(SS_CUR);
+
+ for(i = 0; i < st->line_lengths[line]; i++)
+ if(st->text_lines[line][i] == TAG_EMBED
+ && st->text_lines[line][++i] == TAG_HANDLE){
+ for(key = 0, n = st->text_lines[line][++i]; n; n--)
+ key = (key * 10) + (st->text_lines[line][++i] - '0');
+
+ if(key == goal)
+ return(1);
+ }
+
+ return(0);
+}
+
+
+int
+handle_on_page(HANDLE_S *handle, long int first_line, long int last_line)
+{
+ POSLIST_S *l;
+ int rv = 0;
+
+ if(handle && (l = handle->loc)){
+ for( ; l; l = l->next)
+ if(l->where.row < first_line){
+ if(!rv)
+ rv = -1;
+ }
+ else if(l->where.row >= last_line){
+ if(!rv)
+ rv = 1;
+ }
+ else
+ return(0); /* found! */
+ }
+
+ return(rv);
+}
+
+
+int
+scroll_handle_selectable(HANDLE_S *handle)
+{
+ return(handle && (handle->type != URL
+ || (handle->h.url.path && *handle->h.url.path)));
+}
+
+
+HANDLE_S *
+scroll_handle_next_sel(HANDLE_S *handles)
+{
+ while(handles && !scroll_handle_selectable(handles = handles->next))
+ ;
+
+ return(handles);
+}
+
+
+HANDLE_S *
+scroll_handle_prev_sel(HANDLE_S *handles)
+{
+ while(handles && !scroll_handle_selectable(handles = handles->prev))
+ ;
+
+ return(handles);
+}
+
+
+HANDLE_S *
+scroll_handle_next(HANDLE_S *handles)
+{
+ HANDLE_S *next = NULL;
+
+ if(scroll_handle_obscured(handles) <= 0){
+ next = handles;
+ while((next = scroll_handle_next_sel(next))
+ && scroll_handle_obscured(next))
+ ;
+ }
+
+ return(next);
+}
+
+
+
+HANDLE_S *
+scroll_handle_prev(HANDLE_S *handles)
+{
+ HANDLE_S *prev = NULL;
+
+ if(scroll_handle_obscured(handles) >= 0){
+ prev = handles;
+ while((prev = scroll_handle_prev_sel(prev))
+ && scroll_handle_obscured(prev))
+ ;
+ }
+
+ return(prev);
+}
+
+
+HANDLE_S *
+scroll_handle_boundary(HANDLE_S *handle, HANDLE_S *(*f) (HANDLE_S *))
+{
+ HANDLE_S *hp, *whp = NULL;
+
+ /* Multi-line handle? Punt! */
+ if(handle && (!(hp = handle)->loc->next))
+ while((hp = (*f)(hp))
+ && hp->loc->where.row == handle->loc->where.row)
+ whp = hp;
+
+ return(whp);
+}
+
+
+int
+scroll_handle_column(int line, int offset)
+{
+ SCRLCTRL_S *st = scroll_state(SS_CUR);
+ int i, n, col;
+ int key, limit;
+
+ limit = (offset > -1) ? offset : st->line_lengths[line];
+
+ for(i = 0, col = 0; i < limit;){
+ if(st && st->text_lines && st->text_lines[line])
+ switch(st->text_lines[line][i]){
+ case TAG_EMBED:
+ i++;
+ switch((i < limit) ? st->text_lines[line][i] : 0){
+ case TAG_HANDLE:
+ for(key = 0, n = st->text_lines[line][++i]; n > 0 && i < limit-1; n--)
+ key = (key * 10) + (st->text_lines[line][++i] - '0');
+
+ i++;
+ break;
+
+ case TAG_FGCOLOR :
+ case TAG_BGCOLOR :
+ i += (RGBLEN + 1); /* 1 for TAG, RGBLEN for color */
+ break;
+
+ case TAG_INVON:
+ case TAG_INVOFF:
+ case TAG_BOLDON:
+ case TAG_BOLDOFF:
+ case TAG_ULINEON:
+ case TAG_ULINEOFF:
+ i++;
+ break;
+
+ default: /* literal embed char */
+ break;
+ }
+
+ break;
+
+ case TAB:
+ i++;
+ while(((++col) & 0x07) != 0) /* add tab's spaces */
+ ;
+
+ break;
+
+ default:
+ col += width_at_this_position((unsigned char*) &st->text_lines[line][i],
+ st->line_lengths[line] - i);
+ i++;
+ break;
+ }
+ }
+
+ return(col);
+}
+
+
+int
+scroll_handle_index(int row, int column)
+{
+ SCRLCTRL_S *st = scroll_state(SS_CUR);
+ int index = 0;
+
+ for(index = 0; column > 0;)
+ switch(st->text_lines[row][index++]){
+ case TAG_EMBED :
+ switch(st->text_lines[row][index++]){
+ case TAG_HANDLE:
+ index += st->text_lines[row][index] + 1;
+ break;
+
+ case TAG_FGCOLOR :
+ case TAG_BGCOLOR :
+ index += RGBLEN;
+ break;
+
+ default :
+ break;
+ }
+
+ break;
+
+ case TAB : /* add tab's spaces */
+ while(((--column) & 0x07) != 0)
+ ;
+
+ break;
+
+ default :
+ column--;
+ break;
+ }
+
+ return(index);
+}
+
+
+void
+scroll_handle_set_loc(POSLIST_S **lpp, int line, int column)
+{
+ POSLIST_S *lp;
+
+ lp = (POSLIST_S *) fs_get(sizeof(POSLIST_S));
+ lp->where.row = line;
+ lp->where.col = column;
+ lp->next = NULL;
+ for(; *lpp; lpp = &(*lpp)->next)
+ ;
+
+ *lpp = lp;
+}
+
+
+int
+dot_on_handle(long int line, int goal)
+{
+ int i = 0, n, key = 0, column = -1;
+ SCRLCTRL_S *st = scroll_state(SS_CUR);
+
+ do{
+ if(i >= st->line_lengths[line])
+ return(0);
+
+ switch(st->text_lines[line][i++]){
+ case TAG_EMBED :
+ switch(st->text_lines[line][i++]){
+ case TAG_HANDLE :
+ for(key = 0, n = st->text_lines[line][i++]; n; n--)
+ key = (key * 10) + (st->text_lines[line][i++] - '0');
+
+ break;
+
+ case TAG_FGCOLOR :
+ case TAG_BGCOLOR :
+ i += RGBLEN; /* advance past color setting */
+ break;
+
+ case TAG_BOLDOFF :
+ key = 0;
+ break;
+ }
+
+ break;
+
+ case TAB :
+ while(((++column) & 0x07) != 0) /* add tab's spaces */
+ ;
+
+ break;
+
+ default :
+ column += width_at_this_position((unsigned char*) &st->text_lines[line][i-1],
+ st->line_lengths[line] - (i-1));
+ break;
+ }
+ }
+ while(column < goal);
+
+ return(key);
+}
+
+
+/*
+ * url_launch - Sniff the given url, see if we can do anything with
+ * it. If not, hand off to user-defined app.
+ *
+ */
+int
+url_launch(HANDLE_S *handle)
+{
+ int rv = 0;
+ url_tool_t f;
+#define URL_MAX_LAUNCH (2 * MAILTMPLEN)
+
+ if(handle->h.url.tool){
+ char *toolp, *cmdp, *p, *q, cmd[URL_MAX_LAUNCH + 4];
+ char *left_double_quote, *right_double_quote;
+ int mode, quotable = 0, copied = 0, double_quoted = 0;
+ int escape_single_quotes = 0;
+ PIPE_S *syspipe;
+
+ toolp = handle->h.url.tool;
+
+ /*
+ * Figure out if we need to quote the URL. If there are shell
+ * metacharacters in it we want to quote it, because we don't want
+ * the shell to interpret them. However, if the user has already
+ * quoted the URL in the command definition we don't want to quote
+ * again. So, we try to see if there are a pair of unescaped
+ * quotes surrounding _URL_ in the cmd.
+ * If we quote when we shouldn't have, it'll cause it not to work.
+ * If we don't quote when we should have, it's a possible security
+ * problem (and it still won't work).
+ *
+ * In bash and ksh $( executes a command, so we use single quotes
+ * instead of double quotes to do our quoting. If configured command
+ * is double-quoted we change that to single quotes.
+ */
+#ifdef _WINDOWS
+ /*
+ * It should be safe to not quote any of the characters from the
+ * string below. It was quoting with '?' and '&' in a URL, which is
+ * unnecessary. Also the quoting code below only quotes with
+ * ' (single quote), so we'd want it to at least do double quotes
+ * instead, for Windows.
+ */
+ if(toolp)
+ quotable = 0; /* always never quote */
+ else
+#endif
+ /* quote shell specials */
+ if(strpbrk(handle->h.url.path, "&*;<>?[]|~$(){}'\"") != NULL){
+ escape_single_quotes++;
+ if((p = strstr(toolp, "_URL_")) != NULL){ /* explicit arg? */
+ int in_quote = 0;
+
+ /* see whether or not it is already quoted */
+
+ quotable = 1;
+
+ for(q = toolp; q < p; q++)
+ if(*q == '\'' && (q == toolp || q[-1] != '\\'))
+ in_quote = 1 - in_quote;
+
+ if(in_quote){
+ for(q = p+5; *q; q++)
+ if(*q == '\'' && q[-1] != '\\'){
+ /* already single quoted, leave it alone */
+ quotable = 0;
+ break;
+ }
+ }
+
+ if(quotable){
+ in_quote = 0;
+ for(q = toolp; q < p; q++)
+ if(*q == '\"' && (q == toolp || q[-1] != '\\')){
+ in_quote = 1 - in_quote;
+ if(in_quote)
+ left_double_quote = q;
+ }
+
+ if(in_quote){
+ for(q = p+5; *q; q++)
+ if(*q == '\"' && q[-1] != '\\'){
+ /* we'll replace double quotes with singles */
+ double_quoted = 1;
+ right_double_quote = q;
+ break;
+ }
+ }
+ }
+ }
+ else
+ quotable = 1;
+ }
+ else
+ quotable = 0;
+
+ /* Build the command */
+ cmdp = cmd;
+ while(cmdp-cmd < URL_MAX_LAUNCH)
+ if((!*toolp && !copied)
+ || (*toolp == '_' && !strncmp(toolp + 1, "URL_", 4))){
+
+ /* implicit _URL_ at end */
+ if(!*toolp)
+ *cmdp++ = ' ';
+
+ /* add single quotes */
+ if(quotable && !double_quoted && cmdp-cmd < URL_MAX_LAUNCH)
+ *cmdp++ = '\'';
+
+ copied = 1;
+ /*
+ * If the url.path contains a single quote we should escape
+ * that single quote to remove its special meaning.
+ * Since backslash-quote is ignored inside single quotes we
+ * close the quotes, backslash escape the quote, then open
+ * the quotes again. So
+ * 'fred's car'
+ * becomes 'fred'\''s car'
+ */
+ for(p = handle->h.url.path;
+ p && *p && cmdp-cmd < URL_MAX_LAUNCH; p++){
+ if(escape_single_quotes && *p == '\''){
+ *cmdp++ = '\''; /* closing quote */
+ *cmdp++ = '\\';
+ *cmdp++ = '\''; /* opening quote comes from p below */
+ }
+
+ *cmdp++ = *p;
+ }
+
+ if(quotable && !double_quoted && cmdp-cmd < URL_MAX_LAUNCH){
+ *cmdp++ = '\'';
+ *cmdp = '\0';
+ }
+
+ if(*toolp)
+ toolp += 5; /* length of "_URL_" */
+ }
+ else{
+ /* replace double quotes with single quotes */
+ if(double_quoted &&
+ (toolp == left_double_quote || toolp == right_double_quote)){
+ *cmdp++ = '\'';
+ toolp++;
+ }
+ else if(!(*cmdp++ = *toolp++))
+ break;
+ }
+
+
+ if(cmdp-cmd >= URL_MAX_LAUNCH)
+ return(url_launch_too_long(rv));
+
+ mode = PIPE_RESET | PIPE_USER | PIPE_RUNNOW ;
+ if((syspipe = open_system_pipe(cmd, NULL, NULL, mode, 0, pipe_callback, pipe_report_error)) != NULL){
+ close_system_pipe(&syspipe, NULL, pipe_callback);
+ q_status_message(SM_ORDER, 0, 4, _("VIEWER command completed"));
+ }
+ else
+ q_status_message1(SM_ORDER, 3, 4,
+ /* TRANSLATORS: Cannot start command : <command name> */
+ _("Cannot start command : %s"), cmd);
+ }
+ else if((f = url_local_handler(handle->h.url.path)) != NULL){
+ if((*f)(handle->h.url.path) > 1)
+ rv = 1; /* done! */
+ }
+ else
+ q_status_message1(SM_ORDER, 2, 2,
+ _("\"URL-Viewer\" not defined: Can't open %s"),
+ handle->h.url.path);
+
+ return(rv);
+}
+
+
+int
+url_launch_too_long(int return_value)
+{
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ "Can't spawn. Command too long.");
+ return(return_value);
+}
+
+
+char *
+url_external_handler(HANDLE_S *handle, int specific)
+{
+ char **l, *test, *cmd, *p, *q, *ep;
+ int i, specific_match;
+
+ for(l = ps_global->VAR_BROWSER ; l && *l; l++){
+ get_pair(*l, &test, &cmd, 0, 1);
+ dprint((5, "TEST: \"%s\" CMD: \"%s\"\n",
+ test ? test : "<NULL>", cmd ? cmd : "<NULL>"));
+ removing_quotes(cmd);
+ if(valid_filter_command(&cmd)){
+ specific_match = 0;
+
+ if((p = test) != NULL){
+ while(*p && cmd)
+ if(*p == '_'){
+ if(!strncmp(p+1, "TEST(", 5)
+ && (ep = strstr(p+6, ")_"))){
+ *ep = '\0';
+
+ if(exec_mailcap_test_cmd(p+6) == 0){
+ p = ep + 2;
+ }
+ else{
+ dprint((5,"failed handler TEST\n"));
+ fs_give((void **) &cmd);
+ }
+ }
+ else if(!strncmp(p+1, "SCHEME(", 7)
+ && (ep = strstr(p+8, ")_"))){
+ *ep = '\0';
+
+ p += 8;
+ do
+ if((q = strchr(p, ',')) != NULL)
+ *q++ = '\0';
+ else
+ q = ep;
+ while(!((i = strlen(p))
+ && ((p[i-1] == ':'
+ && handle->h.url.path[i - 1] == ':')
+ || (p[i-1] != ':'
+ && handle->h.url.path[i] == ':'))
+ && !struncmp(handle->h.url.path, p, i))
+ && *(p = q));
+
+ if(*p){
+ specific_match = 1;
+ p = ep + 2;
+ }
+ else{
+ dprint((5,"failed handler SCHEME\n"));
+ fs_give((void **) &cmd);
+ }
+ }
+ else{
+ dprint((5, "UNKNOWN underscore test\n"));
+ fs_give((void **) &cmd);
+ }
+ }
+ else if(isspace((unsigned char) *p)){
+ p++;
+ }
+ else{
+ dprint((1,"bogus handler test: \"%s\"",
+ test ? test : "?"));
+ fs_give((void **) &cmd);
+ }
+
+ fs_give((void **) &test);
+ }
+
+ if(cmd && (!specific || specific_match))
+ return(cmd);
+ }
+
+ if(test)
+ fs_give((void **) &test);
+
+ if(cmd)
+ fs_give((void **) &cmd);
+ }
+
+ cmd = NULL;
+
+ if(!specific){
+ cmd = url_os_specified_browser(handle->h.url.path);
+ /*
+ * Last chance, anything handling "text/html" in mailcap...
+ */
+ if(!cmd && mailcap_can_display(TYPETEXT, "html", NULL, 0)){
+ MCAP_CMD_S *mc_cmd;
+
+ mc_cmd = mailcap_build_command(TYPETEXT, "html",
+ NULL, "_URL_", NULL, 0);
+ /*
+ * right now URL viewing won't return anything requiring
+ * special handling
+ */
+ if(mc_cmd){
+ cmd = mc_cmd->command;
+ fs_give((void **)&mc_cmd);
+ }
+ }
+ }
+
+ return(cmd);
+}
+
+
+url_tool_t
+url_local_handler(char *s)
+{
+ int i;
+ static struct url_t {
+ char *url;
+ short len;
+ url_tool_t f;
+ } handlers[] = {
+ {"mailto:", 7, url_local_mailto}, /* see url_tool_t def's */
+ {"imap://", 7, url_local_imap}, /* for explanations */
+ {"nntp://", 7, url_local_nntp},
+ {"file://", 7, url_local_file},
+#ifdef ENABLE_LDAP
+ {"ldap://", 7, url_local_ldap},
+#endif
+ {"news:", 5, url_local_news},
+ {"x-alpine-gripe:", 15, gripe_gripe_to},
+ {"x-alpine-help:", 14, url_local_helper},
+ {"x-pine-help:", 12, url_local_helper},
+ {"x-alpine-phone-home:", 20, url_local_phone_home},
+ {"x-alpine-config:", 16, url_local_config},
+ {"x-alpine-cert:", 14, url_local_certdetails},
+ {"#", 1, url_local_fragment},
+ {NULL, 0, NULL}
+ };
+
+ for(i = 0; handlers[i].url ; i++)
+ if(!struncmp(s, handlers[i].url, handlers[i].len))
+ return(handlers[i].f);
+
+ return(NULL);
+}
+
+
+
+/*
+ * mailto URL digester ala draft-hoffman-mailto-url-02.txt
+ */
+int
+url_local_mailto(char *url)
+{
+ return(url_local_mailto_and_atts(url, NULL));
+}
+
+int
+url_local_mailto_and_atts(char *url, PATMT *attachlist)
+{
+ ENVELOPE *outgoing;
+ BODY *body = NULL;
+ REPLY_S fake_reply;
+ char *sig, *urlp, *p, *hname, *hvalue;
+ int rv = 0;
+ long rflags;
+ int was_a_body = 0, impl, template_len = 0;
+ char *fcc = NULL;
+ PAT_STATE dummy;
+ REDRAFT_POS_S *redraft_pos = NULL;
+ ACTION_S *role = NULL;
+
+ outgoing = mail_newenvelope();
+ outgoing->message_id = generate_message_id();
+ body = mail_newbody();
+ body->type = TYPETEXT;
+ if((body->contents.text.data = (void *) so_get(PicoText,NULL,EDIT_ACCESS)) != NULL){
+ /*
+ * URL format is:
+ *
+ * mailtoURL = "mailto:" [ to ] [ headers ]
+ * to = #mailbox
+ * headers = "?" header *( "&" header )
+ * header = hname "=" hvalue
+ * hname = *urlc
+ * hvalue = *urlc
+ *
+ * NOTE2: "from" and "bcc" are intentionally excluded from
+ * the list of understood "header" fields
+ */
+ if((p = strchr(urlp = cpystr(url+7), '?')) != NULL)
+ *p++ = '\0'; /* headers? Tie off mailbox */
+
+ /* grok mailbox as first "to", then roll thru specific headers */
+ if(*urlp)
+ rfc822_parse_adrlist(&outgoing->to,
+ rfc1738_str(urlp),
+ ps_global->maildomain);
+
+ while(p){
+ /* Find next "header" */
+ if((p = strchr(hname = p, '&')) != NULL)
+ *p++ = '\0'; /* tie off "header" */
+
+ if((hvalue = strchr(hname, '=')) != NULL)
+ *hvalue++ = '\0'; /* tie off hname */
+
+ if(!hvalue || !strucmp(hname, "subject")){
+ char *sub = rfc1738_str(hvalue ? hvalue : hname);
+
+ if(outgoing->subject){
+ int len = strlen(outgoing->subject);
+
+ fs_resize((void **) &outgoing->subject,
+ (len + strlen(sub) + 2) * sizeof(char));
+ snprintf(outgoing->subject + len, strlen(sub)+2, " %s", sub);
+ outgoing->subject[len + strlen(sub) + 2 - 1] = '\0';
+ }
+ else
+ outgoing->subject = cpystr(sub);
+ }
+ else if(!strucmp(hname, "to")){
+ url_mailto_addr(&outgoing->to, hvalue);
+ }
+ else if(!strucmp(hname, "cc")){
+ url_mailto_addr(&outgoing->cc, hvalue);
+ }
+ else if(!strucmp(hname, "bcc")){
+ q_status_message(SM_ORDER, 3, 4,
+ "\"Bcc\" header in mailto url ignored");
+ }
+ else if(!strucmp(hname, "from")){
+ q_status_message(SM_ORDER, 3, 4,
+ "\"From\" header in mailto url ignored");
+ }
+ else if(!strucmp(hname, "body")){
+ char *sub = rfc1738_str(hvalue ? hvalue : "");
+
+ so_puts((STORE_S *)body->contents.text.data, sub);
+ so_puts((STORE_S *)body->contents.text.data, NEWLINE);
+ was_a_body++;
+ }
+ }
+
+ fs_give((void **) &urlp);
+
+ rflags = ROLE_COMPOSE;
+ if(nonempty_patterns(rflags, &dummy)){
+ role = set_role_from_msg(ps_global, rflags, -1L, NULL);
+ if(confirm_role(rflags, &role))
+ role = combine_inherited_role(role);
+ else{ /* cancel */
+ role = NULL;
+ cmd_cancelled("Composition");
+ goto outta_here;
+ }
+ }
+
+ if(role)
+ q_status_message1(SM_ORDER, 3, 4, "Composing using role \"%s\"",
+ role->nick);
+
+ if(!was_a_body && role && role->template){
+ char *filtered;
+
+ impl = 1; /* leave cursor in header if not explicit */
+ filtered = detoken(role, NULL, 0, 0, 0, &redraft_pos, &impl);
+ if(filtered){
+ if(*filtered){
+ so_puts((STORE_S *)body->contents.text.data, filtered);
+ if(impl == 1)
+ template_len = strlen(filtered);
+ }
+
+ fs_give((void **)&filtered);
+ }
+ }
+ else
+ impl = 1;
+
+ if(!was_a_body && (sig = detoken(role, NULL, 2, 0, 1, &redraft_pos,
+ &impl))){
+ if(impl == 2)
+ redraft_pos->offset += template_len;
+
+ if(*sig)
+ so_puts((STORE_S *)body->contents.text.data, sig);
+
+ fs_give((void **)&sig);
+ }
+
+ memset((void *)&fake_reply, 0, sizeof(fake_reply));
+ fake_reply.pseudo = 1;
+ fake_reply.data.pico_flags = (outgoing->subject) ? P_BODY : P_HEADEND;
+
+
+ if(!(role && role->fcc))
+ fcc = get_fcc_based_on_to(outgoing->to);
+
+ if(attachlist)
+ create_message_body(&body, attachlist, 0);
+
+ pine_send(outgoing, &body, "\"MAILTO\" COMPOSE",
+ role, fcc, &fake_reply, redraft_pos, NULL, NULL, PS_STICKY_TO);
+ rv++;
+ ps_global->mangled_screen = 1;
+ }
+ else
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Can't create space for composer"));
+
+outta_here:
+ if(outgoing)
+ mail_free_envelope(&outgoing);
+
+ if(body)
+ pine_free_body(&body);
+
+ if(fcc)
+ fs_give((void **)&fcc);
+
+ free_redraft_pos(&redraft_pos);
+ free_action(&role);
+
+ return(rv);
+}
+
+
+void
+url_mailto_addr(struct mail_address **a, char *a_raw)
+{
+ char *p = cpystr(rfc1738_str(a_raw));
+
+ while(*a) /* append to address list */
+ a = &(*a)->next;
+
+ rfc822_parse_adrlist(a, p, ps_global->maildomain);
+ fs_give((void **) &p);
+}
+
+
+/*
+ * imap URL digester ala RFC 2192
+ */
+int
+url_local_imap(char *url)
+{
+ char *folder, *mailbox = NULL, *errstr = NULL, *search = NULL,
+ newfolder[MAILTMPLEN];
+ int rv;
+ long i;
+ imapuid_t uid = 0L, uid_val = 0L;
+ CONTEXT_S *fake_context;
+ MESSAGECACHE *mc;
+
+ rv = url_imap_folder(url, &folder, &uid, &uid_val, &search, 0);
+ switch(rv & URL_IMAP_MASK){
+ case URL_IMAP_IMAILBOXLIST :
+/* BUG: deal with lsub tag */
+ if((fake_context = new_context(folder, NULL)) != NULL){
+ newfolder[0] = '\0';
+ if(display_folder_list(&fake_context, newfolder,
+ 0, folders_for_goto))
+ if(strlen(newfolder) + 1 < MAILTMPLEN)
+ mailbox = newfolder;
+ }
+ else
+ errstr = "Problem building URL's folder list";
+
+ fs_give((void **) &folder);
+ free_context(&fake_context);
+
+ if(mailbox){
+ return(1);
+ }
+ else if(errstr)
+ q_status_message(SM_ORDER|SM_DING, 3, 3, errstr);
+ else
+ cmd_cancelled("URL Launch");
+
+ break;
+
+ case URL_IMAP_IMESSAGEPART :
+ case URL_IMAP_IMESSAGELIST :
+ if(ps_global && ps_global->ttyo){
+ blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
+ ps_global->mangled_footer = 1;
+ }
+
+ rv = do_broach_folder(folder, NULL, NULL, 0L);
+ fs_give((void **) &folder);
+ switch(rv){
+ case -1 : /* utter failure */
+ ps_global->next_screen = main_menu_screen;
+ break;
+
+ case 0 : /* same folder reopened */
+ ps_global->next_screen = mail_index_screen;
+ break;
+
+ case 1 : /* requested folder open */
+ ps_global->next_screen = mail_index_screen;
+
+ if(uid_val && uid_val != ps_global->mail_stream->uid_validity){
+ /* Complain! */
+ q_status_message(SM_ORDER|SM_DING, 3, 3,
+ "Warning! Referenced folder changed since URL recorded");
+ }
+
+ if(uid){
+ /*
+ * Make specified message the currently selected..
+ */
+ for(i = 1L; i <= mn_get_total(ps_global->msgmap); i++)
+ if(mail_uid(ps_global->mail_stream, i) == uid){
+ ps_global->next_screen = mail_view_screen;
+ mn_set_cur(ps_global->msgmap, i);
+ break;
+ }
+
+ if(i > mn_get_total(ps_global->msgmap))
+ q_status_message(SM_ORDER, 2, 3,
+ "Couldn't find specified article number");
+ }
+ else if(search){
+ /*
+ * Select the specified messages
+ * and present a zoom'd index...
+ */
+/* BUG: not dealing with CHARSET yet */
+
+/* ANOTHER BUG: mail_criteria is a compatibility routine for IMAP2BIS
+ * so it doesn't know about IMAP4 search criteria, like SENTSINCE.
+ * It also doesn't handle literals. */
+
+ pine_mail_search_full(ps_global->mail_stream, NULL,
+ mail_criteria(search),
+ SE_NOPREFETCH | SE_FREE);
+
+ for(i = 1L; i <= mn_get_total(ps_global->msgmap); i++)
+ if(ps_global->mail_stream
+ && i <= ps_global->mail_stream->nmsgs
+ && (mc = mail_elt(ps_global->mail_stream, i))
+ && mc->searched)
+ set_lflag(ps_global->mail_stream,
+ ps_global->msgmap, i, MN_SLCT, 1);
+
+ if((i = any_lflagged(ps_global->msgmap, MN_SLCT)) != 0){
+
+ q_status_message2(SM_ORDER, 0, 3,
+ "%s message%s selected",
+ long2string(i), plural(i));
+ /* Zoom the index! */
+ zoom_index(ps_global, ps_global->mail_stream,
+ ps_global->msgmap, MN_SLCT);
+ }
+ }
+ }
+
+ return(1);
+
+ default:
+ case URL_IMAP_ERROR :
+ break;
+ }
+
+ return(0);
+}
+
+
+int
+url_local_nntp(char *url)
+{
+ char folder[2*MAILTMPLEN], *group;
+ int group_len;
+ long i, article_num;
+
+ /* no hostport, no url, end of story */
+ if((group = strchr(url + 7, '/')) != 0){
+ group++;
+ for(group_len = 0; group[group_len] && group[group_len] != '/';
+ group_len++)
+ if(!rfc1738_group(&group[group_len]))
+ /* TRANSLATORS: these are errors in news group URLs */
+ return(url_bogus(url, _("Invalid newsgroup specified")));
+
+ if(group_len){
+ snprintf(folder, sizeof(folder), "{%.*s/nntp}#news.%.*s",
+ MIN((group - 1) - (url + 7), MAILTMPLEN-20), url + 7,
+ MIN(group_len, MAILTMPLEN-20), group);
+ folder[sizeof(folder)-1] = '\0';
+ }
+ else
+ return(url_bogus(url, _("No newsgroup specified")));
+ }
+ else
+ return(url_bogus(url, _("No server specified")));
+
+ if(ps_global && ps_global->ttyo){
+ blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
+ ps_global->mangled_footer = 1;
+ }
+
+ switch(do_broach_folder(rfc1738_str(folder), NULL, NULL, 0L)){
+ case -1 : /* utter failure */
+ ps_global->next_screen = main_menu_screen;
+ break;
+
+ case 0 : /* same folder reopened */
+ ps_global->next_screen = mail_index_screen;
+ break;
+
+ case 1 : /* requested folder open */
+ ps_global->next_screen = mail_index_screen;
+
+ /* grok article number --> c-client UID */
+ if(group[group_len++] == '/'
+ && (article_num = atol(&group[group_len]))){
+ /*
+ * Make the requested article our current message
+ */
+ for(i = 1; i <= mn_get_nmsgs(ps_global->msgmap); i++)
+ if(mail_uid(ps_global->mail_stream, i) == article_num){
+ ps_global->next_screen = mail_view_screen;
+ if((i = mn_raw2m(ps_global->msgmap, i)) != 0)
+ mn_set_cur(ps_global->msgmap, i);
+ break;
+ }
+
+ if(i == 0 || i > mn_get_total(ps_global->msgmap))
+ q_status_message(SM_ORDER, 2, 3,
+ _("Couldn't find specified article number"));
+ }
+
+ break;
+ }
+
+ ps_global->redrawer = (void(*)(void))NULL;
+ return(1);
+}
+
+
+int
+url_local_news(char *url)
+{
+ char folder[MAILTMPLEN], *p;
+ CONTEXT_S *cntxt = NULL;
+
+ /*
+ * NOTE: NO SUPPORT for '*' or message-id
+ */
+ if(*(url+5)){
+ if(*(url+5) == '/' && *(url+6) == '/')
+ return(url_local_nntp(url)); /* really meant "nntp://"? */
+
+ if(ps_global->VAR_NNTP_SERVER && ps_global->VAR_NNTP_SERVER[0])
+ /*
+ * BUG: Only the first NNTP server is tried.
+ */
+ snprintf(folder, sizeof(folder), "{%s/nntp}#news.", ps_global->VAR_NNTP_SERVER[0]);
+ else
+ strncpy(folder, "#news.", sizeof(folder));
+
+ folder[sizeof(folder)-1] = '\0';
+
+ for(p = strncpy(folder + strlen(folder), url + 5, sizeof(folder)-strlen(folder)-1);
+ *p && rfc1738_group(p);
+ p++)
+ ;
+
+ if(*p)
+ return(url_bogus(url, "Invalid newsgroup specified"));
+ }
+ else{ /* fish first group from newsrc */
+ FOLDER_S *f;
+ int alphaorder;
+
+ folder[0] = '\0';
+
+ /* Find first news context */
+ for(cntxt = ps_global->context_list;
+ cntxt && !(cntxt->use & CNTXT_NEWS);
+ cntxt = cntxt->next)
+ ;
+
+ if(cntxt){
+ if((alphaorder = F_OFF(F_READ_IN_NEWSRC_ORDER, ps_global)) != 0)
+ (void) F_SET(F_READ_IN_NEWSRC_ORDER, ps_global, 1);
+
+ build_folder_list(NULL, cntxt, NULL, NULL, BFL_LSUB);
+ if((f = folder_entry(0, FOLDERS(cntxt))) != NULL){
+ strncpy(folder, f->name, sizeof(folder));
+ folder[sizeof(folder)-1] = '\0';
+ }
+
+ free_folder_list(cntxt);
+
+ if(alphaorder)
+ (void) F_SET(F_READ_IN_NEWSRC_ORDER, ps_global, 0);
+ }
+
+ if(folder[0] == '\0'){
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ "No default newsgroup");
+ return(0);
+ }
+ }
+
+ if(ps_global && ps_global->ttyo){
+ blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
+ ps_global->mangled_footer = 1;
+ }
+
+ if(do_broach_folder(rfc1738_str(folder), cntxt, NULL, 0L) < 0)
+ ps_global->next_screen = main_menu_screen;
+ else
+ ps_global->next_screen = mail_index_screen;
+
+ ps_global->redrawer = (void(*)(void))NULL;
+
+ return(1);
+}
+
+
+int
+url_local_file(char *file_url)
+{
+ if(want_to(
+ /* TRANSLATORS: this is a warning that the file URL can cause programs to run which may
+ be a security problem. We are asking the user to confirm that they want to do this. */
+ _("\"file\" URL may cause programs to be run on your system. Run anyway"),
+ 'n', 0, NO_HELP, WT_NORM) == 'y'){
+ HANDLE_S handle;
+
+ /* fake a handle */
+ handle.h.url.path = file_url;
+ if((handle.h.url.tool = url_external_handler(&handle, 1))
+ || (handle.h.url.tool = url_external_handler(&handle, 0))){
+ url_launch(&handle);
+ return 1;
+ }
+ else{
+ q_status_message(SM_ORDER, 0, 4,
+ _("No viewer for \"file\" URL. VIEWER command cancelled"));
+ return 0;
+ }
+ }
+ q_status_message(SM_ORDER, 0, 4, _("VIEWER command cancelled"));
+ return 0;
+}
+
+
+int
+url_local_fragment(char *fragment)
+{
+ SCRLCTRL_S *st = scroll_state(SS_CUR);
+ HANDLE_S *hp;
+
+ /*
+ * find a handle with the fragment's name
+ */
+ for(hp = st->parms->text.handles; hp; hp = hp->next)
+ if(hp->type == URL && hp->h.url.name
+ && !strcmp(hp->h.url.name, fragment + 1))
+ break;
+
+ if(!hp)
+ for(hp = st->parms->text.handles->prev; hp; hp = hp->prev)
+ if(hp->type == URL && hp->h.url.name
+ && !strcmp(hp->h.url.name, fragment + 1))
+ break;
+
+ /*
+ * set the top line of the display to contain this line
+ */
+ if(hp && hp->loc){
+ st->top_text_line = hp->loc->where.row;
+ ps_global->mangled_body = 1;
+ }
+ else
+ q_status_message1(SM_ORDER | SM_DING, 0, 3,
+ "Can't find fragment: %s", fragment);
+
+ return(1);
+}
+
+
+int
+url_local_phone_home(char *url)
+{
+ phone_home(url + strlen("x-alpine-phone-home:"));
+ return(2);
+}
+
+
+/*
+ * Format editorial comment referencing screen offering
+ * List-* header supplied commands
+ */
+int
+rfc2369_editorial(long int msgno, HANDLE_S **handlesp, int flags, int width, gf_io_t pc)
+{
+ char *p, *hdrp, *hdrs[MLCMD_COUNT + 1],
+ color[64], buf[2048];
+ int i, n, rv = TRUE;
+ HANDLE_S *h = NULL;
+
+ if((flags & FM_DISPLAY)
+ && (hdrp = pine_fetchheader_lines(ps_global->mail_stream, msgno,
+ NULL, rfc2369_hdrs(hdrs)))){
+ if(*hdrp){
+ snprintf(buf, sizeof(buf), "Note: This message contains ");
+ buf[sizeof(buf)-1] = '\0';
+ p = buf + strlen(buf);
+
+ if(handlesp){
+ h = new_handle(handlesp);
+ h->type = Function;
+ h->h.func.f = rfc2369_display;
+ h->h.func.args.stream = ps_global->mail_stream;
+ h->h.func.args.msgmap = ps_global->msgmap;
+ h->h.func.args.msgno = msgno;
+
+ if(!(flags & FM_NOCOLOR)
+ && handle_start_color(color, sizeof(color), &n, 0)){
+ if((p-buf)+n < sizeof(buf))
+ for(i = 0; i < n; i++)
+ *p++ = color[i];
+ }
+ else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
+ *p++ = TAG_EMBED;
+ *p++ = TAG_BOLDON;
+ }
+
+ if((p-buf)+2 < sizeof(buf)){
+ *p++ = TAG_EMBED;
+ *p++ = TAG_HANDLE;
+ }
+
+ snprintf(p + 1, sizeof(buf)-(p+1-buf), "%d", h->key);
+ buf[sizeof(buf)-1] = '\0';
+ *p = strlen(p + 1);
+ p += (*p + 1);
+ }
+
+ sstrncpy(&p, "email list management information", sizeof(buf)-(p-buf));
+ buf[sizeof(buf)-1] = '\0';
+
+ if(h){
+ /* in case it was the current selection */
+ if((p-buf)+2 < sizeof(buf)){
+ *p++ = TAG_EMBED;
+ *p++ = TAG_INVOFF;
+ }
+
+ if(handle_end_color(color, sizeof(color), &n)){
+ if((p-buf)+n < sizeof(buf))
+ for(i = 0; i < n; i++)
+ *p++ = color[i];
+ }
+ else{
+ if((p-buf)+2 < sizeof(buf)){
+ *p++ = TAG_EMBED;
+ *p++ = TAG_BOLDOFF;
+ }
+ }
+
+ if(p-buf < sizeof(buf))
+ *p = '\0';
+ }
+
+ buf[sizeof(buf)-1] = '\0';
+
+ rv = (gf_puts(NEWLINE, pc)
+ && format_editorial(buf, width, flags, handlesp, pc) == NULL
+ && gf_puts(NEWLINE, pc));
+ }
+
+ fs_give((void **) &hdrp);
+ }
+
+ return(rv);
+}
+
+
+
+/*----------------------------------------------------------------------
+ routine for displaying text on the screen.
+
+ Args: sparms -- structure of args controlling what happens outside
+ just the business of managing text scrolling
+
+ This displays in three different kinds of text. One is an array of
+lines passed in in text_array. The other is a simple long string of
+characters passed in in text.
+
+ The style determines what some of the error messages will be, and
+what commands are available as different things are appropriate for
+help text than for message text etc.
+
+ ---*/
+
+int
+scrolltool(SCROLL_S *sparms)
+{
+ register long cur_top_line, num_display_lines;
+ UCS ch;
+ int result, done, cmd, found_on, found_on_index,
+ first_view, force, scroll_lines, km_size,
+ cursor_row, cursor_col, km_popped;
+ char *utf8str;
+ long jn;
+ struct key_menu *km;
+ HANDLE_S *next_handle;
+ bitmap_t bitmap;
+ OtherMenu what;
+ Pos whereis_pos;
+
+ num_display_lines = SCROLL_LINES(ps_global);
+ km_popped = 0;
+ ps_global->mangled_header = 1;
+ ps_global->mangled_footer = 1;
+ ps_global->mangled_body = !sparms->body_valid;
+
+
+ what = sparms->keys.what; /* which key menu to display */
+ cur_top_line = 0;
+ done = 0;
+ found_on = -1;
+ found_on_index = -1;
+ first_view = 1;
+ if(sparms->quell_first_view)
+ first_view = 0;
+
+ force = 0;
+ ch = 'x'; /* for first time through */
+ whereis_pos.row = 0;
+ whereis_pos.col = 0;
+ next_handle = sparms->text.handles;
+
+ set_scroll_text(sparms, cur_top_line, scroll_state(SS_NEW));
+ format_scroll_text();
+
+ if((km = sparms->keys.menu) != NULL){
+ memcpy(bitmap, sparms->keys.bitmap, sizeof(bitmap_t));
+ }
+ else{
+ setbitmap(bitmap);
+ km = &simple_text_keymenu;
+#ifdef _WINDOWS
+ sparms->mouse.popup = simple_text_popup;
+#endif
+ }
+
+ if(!sparms->bar.title)
+ sparms->bar.title = "Text";
+
+ if(sparms->bar.style == TitleBarNone){
+ if(THREADING() && sp_viewing_a_thread(ps_global->mail_stream))
+ sparms->bar.style = ThrdMsgPercent;
+ else
+ sparms->bar.style = MsgTextPercent;
+ }
+
+ switch(sparms->start.on){
+ case LastPage :
+ cur_top_line = MAX(0, scroll_text_lines() - (num_display_lines-2));
+ if(F_ON(F_SHOW_CURSOR, ps_global)){
+ whereis_pos.row = scroll_text_lines() - cur_top_line;
+ found_on = scroll_text_lines() - 1;
+ }
+
+ break;
+
+ case Fragment :
+ if(sparms->start.loc.frag){
+ (void) url_local_fragment(sparms->start.loc.frag);
+
+ cur_top_line = scroll_state(SS_CUR)->top_text_line;
+
+ if(F_ON(F_SHOW_CURSOR, ps_global)){
+ whereis_pos.row = scroll_text_lines() - cur_top_line;
+ found_on = scroll_text_lines() - 1;
+ }
+ }
+
+ break;
+
+ case Offset :
+ if(sparms->start.loc.offset){
+ for(cur_top_line = 0L;
+ cur_top_line + 1 < scroll_text_lines()
+ && (sparms->start.loc.offset
+ -= scroll_handle_column(cur_top_line,
+ -1)) >= 0;
+ cur_top_line++)
+ ;
+ }
+
+ break;
+
+ case Handle :
+ if(scroll_handle_obscured(sparms->text.handles))
+ cur_top_line = scroll_handle_reframe(-1, TRUE);
+
+ break;
+
+ default : /* no-op */
+ break;
+ }
+
+ /* prepare for calls below to tell us where to go */
+ ps_global->next_screen = SCREEN_FUN_NULL;
+
+ cancel_busy_cue(-1);
+
+ while(!done) {
+ ps_global->user_says_cancel = 0;
+ if(km_popped){
+ km_popped--;
+ if(km_popped == 0){
+ clearfooter(ps_global);
+ ps_global->mangled_body = 1;
+ }
+ }
+
+ if(ps_global->mangled_screen) {
+ ps_global->mangled_header = 1;
+ ps_global->mangled_footer = 1;
+ ps_global->mangled_body = 1;
+ }
+
+ if(!sparms->quell_newmail && streams_died())
+ ps_global->mangled_header = 1;
+
+ dprint((9, "@@@@ current:%ld\n",
+ mn_get_cur(ps_global->msgmap)));
+
+
+ /*==================== All Screen painting ====================*/
+ /*-------------- The title bar ---------------*/
+ update_scroll_titlebar(cur_top_line, ps_global->mangled_header);
+
+ if(ps_global->mangled_screen){
+ /* this is the only line not cleared by header, body or footer
+ * repaint calls....
+ */
+ ClearLine(1);
+ ps_global->mangled_screen = 0;
+ }
+
+ /*---- Scroll or update the body of the text on the screen -------*/
+ cur_top_line = scroll_scroll_text(cur_top_line, next_handle,
+ ps_global->mangled_body);
+ ps_global->redrawer = redraw_scroll_text;
+ ps_global->mangled_body = 0;
+
+ /*--- Check to see if keymenu might change based on next_handle --*/
+ if(sparms->text.handles != next_handle)
+ ps_global->mangled_footer = 1;
+
+ if(next_handle)
+ sparms->text.handles = next_handle;
+
+ /*------------- The key menu footer --------------------*/
+ if(ps_global->mangled_footer || sparms->keys.each_cmd){
+ if(km_popped){
+ FOOTER_ROWS(ps_global) = 3;
+ clearfooter(ps_global);
+ }
+
+ if(F_ON(F_ARROW_NAV, ps_global)){
+ menu_clear_binding(km, KEY_LEFT);
+ if((cmd = menu_clear_binding(km, '<')) != MC_UNKNOWN){
+ menu_add_binding(km, '<', cmd);
+ menu_add_binding(km, KEY_LEFT, cmd);
+ }
+ }
+
+ if(F_ON(F_ARROW_NAV, ps_global)){
+ menu_clear_binding(km, KEY_RIGHT);
+ if((cmd = menu_clear_binding(km, '>')) != MC_UNKNOWN){
+ menu_add_binding(km, '>', cmd);
+ menu_add_binding(km, KEY_RIGHT, cmd);
+ }
+ }
+
+ if(sparms->keys.each_cmd){
+ (*sparms->keys.each_cmd)(sparms,
+ scroll_handle_obscured(sparms->text.handles));
+ memcpy(bitmap, sparms->keys.bitmap, sizeof(bitmap_t));
+ }
+
+ if(menu_binding_index(km, MC_JUMP) >= 0){
+ for(cmd = 0; cmd < 10; cmd++)
+ if(F_ON(F_ENABLE_JUMP, ps_global))
+ (void) menu_add_binding(km, '0' + cmd, MC_JUMP);
+ else
+ (void) menu_clear_binding(km, '0' + cmd);
+ }
+
+ draw_keymenu(km, bitmap, ps_global->ttyo->screen_cols,
+ 1-FOOTER_ROWS(ps_global), 0, what);
+ what = SameMenu;
+ ps_global->mangled_footer = 0;
+ if(km_popped){
+ FOOTER_ROWS(ps_global) = 1;
+ mark_keymenu_dirty();
+ }
+ }
+
+ if((ps_global->first_time_user || ps_global->show_new_version)
+ && first_view && sparms->text.handles
+ && (sparms->text.handles->next || sparms->text.handles->prev)
+ && !sparms->quell_help)
+ q_status_message(SM_ORDER, 0, 3, HANDLE_INIT_MSG);
+
+ /*============ Check for New Mail and CheckPoint ============*/
+ if(!sparms->quell_newmail &&
+ new_mail(force, NM_TIMING(ch), NM_STATUS_MSG) >= 0){
+ update_scroll_titlebar(cur_top_line, 1);
+ if(ps_global->mangled_footer)
+ draw_keymenu(km, bitmap, ps_global->ttyo->screen_cols,
+ 1-FOOTER_ROWS(ps_global), 0, what);
+
+ ps_global->mangled_footer = 0;
+ }
+
+ /*
+ * If an expunge of the current message happened during the
+ * new mail check we want to bail out of here. See mm_expunged.
+ */
+ if(ps_global->next_screen != SCREEN_FUN_NULL){
+ done = 1;
+ continue;
+ }
+
+ if(ps_global->noticed_change_in_unseen){
+ ps_global->noticed_change_in_unseen = 0; /* redraw only once */
+ cmd = MC_RESIZE; /* causes cursor to be saved in folder_lister */
+ done = 1;
+ continue;
+ }
+
+ if(first_view && num_display_lines >= scroll_text_lines())
+ q_status_message1(SM_INFO, 0, 1, "ALL of %s", STYLE_NAME(sparms));
+
+
+ force = 0; /* may not need to next time around */
+ first_view = 0; /* check_point a priority any more? */
+
+ /*==================== Output the status message ==============*/
+ if(!sparms->no_stat_msg){
+ if(km_popped){
+ FOOTER_ROWS(ps_global) = 3;
+ mark_status_unknown();
+ }
+
+ display_message(ch);
+ if(km_popped){
+ FOOTER_ROWS(ps_global) = 1;
+ mark_status_unknown();
+ }
+ }
+
+ if(F_ON(F_SHOW_CURSOR, ps_global)){
+#ifdef WINDOWS
+ if(cur_top_line != scroll_state(SS_CUR)->top_text_line)
+ whereis_pos.row = 0;
+#endif
+
+ if(whereis_pos.row > 0){
+ cursor_row = SCROLL_LINES_ABOVE(ps_global)
+ + whereis_pos.row - 1;
+ cursor_col = whereis_pos.col;
+ }
+ else{
+ POSLIST_S *lp = NULL;
+
+ if(sparms->text.handles &&
+ !scroll_handle_obscured(sparms->text.handles)){
+ SCRLCTRL_S *st = scroll_state(SS_CUR);
+
+ for(lp = sparms->text.handles->loc; lp; lp = lp->next)
+ if(lp->where.row >= st->top_text_line
+ && lp->where.row < st->top_text_line
+ + st->screen.length){
+ cursor_row = lp->where.row - cur_top_line
+ + SCROLL_LINES_ABOVE(ps_global);
+ cursor_col = lp->where.col;
+ break;
+ }
+ }
+
+ if(!lp){
+ cursor_col = 0;
+ /* first new line of text */
+ cursor_row = SCROLL_LINES_ABOVE(ps_global) +
+ ((cur_top_line == 0) ? 0 : ps_global->viewer_overlap);
+ }
+ }
+ }
+ else{
+ cursor_col = 0;
+ cursor_row = ps_global->ttyo->screen_rows
+ - SCROLL_LINES_BELOW(ps_global);
+ }
+
+ MoveCursor(cursor_row, cursor_col);
+
+ /*================ Get command and validate =====================*/
+#ifdef MOUSE
+#ifndef WIN32
+ if(sparms->text.handles)
+#endif
+ {
+ mouse_in_content(KEY_MOUSE, -1, -1, 0x5, 0);
+ register_mfunc(mouse_in_content, HEADER_ROWS(ps_global), 0,
+ ps_global->ttyo->screen_rows
+ - (FOOTER_ROWS(ps_global) + 1),
+ ps_global->ttyo->screen_cols);
+ }
+#endif
+#ifdef _WINDOWS
+ mswin_allowcopy(mswin_readscrollbuf);
+ mswin_setscrollcallback(pcpine_do_scroll);
+
+ if(sparms->help.text != NO_HELP)
+ mswin_sethelptextcallback(pcpine_help_scroll);
+
+ if(sparms->text.handles
+ && sparms->text.handles->type != Folder)
+ mswin_mousetrackcallback(pcpine_view_cursor);
+
+ if(ps_global->prev_screen == mail_view_screen)
+ mswin_setviewinwindcallback(view_in_new_window);
+#endif
+ ch = (sparms->quell_newmail || read_command_prep()) ? read_command(&utf8str) : NO_OP_COMMAND;
+#ifdef MOUSE
+#ifndef WIN32
+ if(sparms->text.handles)
+#endif
+ clear_mfunc(mouse_in_content);
+#endif
+#ifdef _WINDOWS
+ mswin_allowcopy(NULL);
+ mswin_setscrollcallback(NULL);
+ mswin_sethelptextcallback(NULL);
+ mswin_mousetrackcallback(NULL);
+ mswin_setviewinwindcallback(NULL);
+ cur_top_line = scroll_state(SS_CUR)->top_text_line;
+#endif
+
+ cmd = menu_command(ch, km);
+
+ if(km_popped)
+ switch(cmd){
+ case MC_NONE :
+ case MC_OTHER :
+ case MC_RESIZE:
+ case MC_REPAINT :
+ km_popped++;
+ break;
+
+ default:
+ clearfooter(ps_global);
+ break;
+ }
+
+
+ /*============= Execute command =======================*/
+ switch(cmd){
+
+ /* ------ Help -------*/
+ case MC_HELP :
+ if(FOOTER_ROWS(ps_global) == 1 && km_popped == 0){
+ km_popped = 2;
+ ps_global->mangled_footer = 1;
+ break;
+ }
+
+ whereis_pos.row = 0;
+ if(sparms->help.text == NO_HELP){
+ q_status_message(SM_ORDER, 0, 5,
+ _("No help text currently available"));
+ break;
+ }
+
+ km_size = FOOTER_ROWS(ps_global);
+
+ helper(sparms->help.text, sparms->help.title, 0);
+
+ if(ps_global->next_screen != main_menu_screen
+ && km_size == FOOTER_ROWS(ps_global)) {
+ /* Have to reset because helper uses scroll_text */
+ num_display_lines = SCROLL_LINES(ps_global);
+ ps_global->mangled_screen = 1;
+ }
+ else
+ done = 1;
+
+ break;
+
+
+ /*---------- Roll keymenu ------*/
+ case MC_OTHER :
+ if(F_OFF(F_USE_FK, ps_global))
+ warn_other_cmds();
+
+ what = NextMenu;
+ ps_global->mangled_footer = 1;
+ break;
+
+
+ /* -------- Scroll back one page -----------*/
+ case MC_PAGEUP :
+ whereis_pos.row = 0;
+ if(cur_top_line) {
+ scroll_lines = MIN(MAX(num_display_lines -
+ ps_global->viewer_overlap, 1), num_display_lines);
+ cur_top_line -= scroll_lines;
+ if(cur_top_line <= 0){
+ cur_top_line = 0;
+ q_status_message1(SM_INFO, 0, 1, "START of %s",
+ STYLE_NAME(sparms));
+ }
+ }
+ else{
+ /* hilite last available handle */
+ next_handle = NULL;
+ if(sparms->text.handles){
+ HANDLE_S *h = sparms->text.handles;
+
+ while((h = scroll_handle_prev_sel(h))
+ && !scroll_handle_obscured(h))
+ next_handle = h;
+ }
+
+ if(!next_handle)
+ q_status_message1(SM_ORDER, 0, 1, _("Already at start of %s"),
+ STYLE_NAME(sparms));
+
+ }
+
+
+ break;
+
+
+ /*---- Scroll down one page -------*/
+ case MC_PAGEDN :
+ if(cur_top_line + num_display_lines < scroll_text_lines()){
+ whereis_pos.row = 0;
+ scroll_lines = MIN(MAX(num_display_lines -
+ ps_global->viewer_overlap, 1), num_display_lines);
+ cur_top_line += scroll_lines;
+
+ if(cur_top_line + num_display_lines >= scroll_text_lines())
+ q_status_message1(SM_INFO, 0, 1, "END of %s",
+ STYLE_NAME(sparms));
+ }
+ else if(!sparms->end_scroll
+ || !(done = (*sparms->end_scroll)(sparms))){
+ q_status_message1(SM_ORDER, 0, 1, _("Already at end of %s"),
+ STYLE_NAME(sparms));
+ /* hilite last available handle */
+ if(sparms->text.handles){
+ HANDLE_S *h = sparms->text.handles;
+
+ while((h = scroll_handle_next_sel(h)) != NULL)
+ next_handle = h;
+ }
+ }
+
+ break;
+
+ /* scroll to the top page */
+ case MC_HOMEKEY:
+ if(cur_top_line){
+ cur_top_line = 0;
+ q_status_message1(SM_INFO, 0, 1, "START of %s",
+ STYLE_NAME(sparms));
+ }
+
+ next_handle = NULL;
+ if(sparms->text.handles){
+ HANDLE_S *h = sparms->text.handles;
+
+ while((h = scroll_handle_prev_sel(h)) != NULL)
+ next_handle = h;
+ }
+ break;
+
+ /* scroll to the bottom page */
+ case MC_ENDKEY:
+ if(cur_top_line + num_display_lines < scroll_text_lines()){
+ cur_top_line = scroll_text_lines() - MIN(5, num_display_lines);
+ q_status_message1(SM_INFO, 0, 1, "END of %s",
+ STYLE_NAME(sparms));
+ }
+
+ if(sparms->text.handles){
+ HANDLE_S *h = sparms->text.handles;
+
+ while((h = scroll_handle_next_sel(h)) != NULL)
+ next_handle = h;
+ }
+ break;
+
+ /*------ Scroll down one line -----*/
+ case MC_CHARDOWN :
+ next_handle = NULL;
+ if(sparms->text.handles){
+ if(sparms->vert_handle){
+ HANDLE_S *h, *h2;
+ int i, j, k;
+
+ h2 = sparms->text.handles;
+ if(h2->type == Folder && h2->prev && h2->prev->is_dual_do_open)
+ h2 = h2->prev;
+
+ i = h2->loc->where.row + 1;
+ j = h2->loc->where.col;
+ for(h = NULL, k = h2->key;
+ h2 && (!h
+ || (h->loc->where.row == h2->loc->where.row));
+ h2 = h2->next)
+ /* must be different key */
+ /* ... below current line */
+ /* ... pref'bly to left */
+ if(h2->key != k
+ && h2->loc->where.row >= i){
+ if(h2->loc->where.col > j){
+ if(!h)
+ h = h2;
+
+ break;
+ }
+ else
+ h = h2;
+ }
+
+ if(h){
+ whereis_pos.row = 0;
+ next_handle = h;
+ if((result = scroll_handle_obscured(next_handle)) != 0){
+ long new_top;
+
+ if(scroll_handle_obscured(sparms->text.handles)
+ && result > 0)
+ next_handle = sparms->text.handles;
+
+ ps_global->mangled_body++;
+ new_top = scroll_handle_reframe(next_handle->key,0);
+ if(new_top >= 0)
+ cur_top_line = new_top;
+ }
+ }
+ }
+ else if(!(ch == ctrl('N') || F_ON(F_FORCE_ARROWS, ps_global)))
+ next_handle = scroll_handle_next(sparms->text.handles);
+ }
+
+ if(!next_handle){
+ if(cur_top_line + num_display_lines < scroll_text_lines()){
+ whereis_pos.row = 0;
+ cur_top_line++;
+ if(cur_top_line + num_display_lines >= scroll_text_lines())
+ q_status_message1(SM_INFO, 0, 1, "END of %s",
+ STYLE_NAME(sparms));
+ }
+ else
+ q_status_message1(SM_ORDER, 0, 1, _("Already at end of %s"),
+ STYLE_NAME(sparms));
+ }
+
+ break;
+
+
+ /* ------ Scroll back up one line -------*/
+ case MC_CHARUP :
+ next_handle = NULL;
+ if(sparms->text.handles){
+ if(sparms->vert_handle){
+ HANDLE_S *h, *h2;
+ int i, j, k;
+
+ h2 = sparms->text.handles;
+ if(h2->type == Folder && h2->prev && h2->prev->is_dual_do_open)
+ h2 = h2->prev;
+
+ i = h2->loc->where.row - 1;
+ j = h2->loc->where.col;
+
+ for(h = NULL, k = h2->key;
+ h2 && (!h
+ || (h->loc->where.row == h2->loc->where.row));
+ h2 = h2->prev)
+ /* must be new key, above current
+ * line and pref'bly to right
+ */
+ if(h2->key != k
+ && h2->loc->where.row <= i){
+ if(h2->loc->where.col < j){
+ if(!h)
+ h = h2;
+
+ break;
+ }
+ else
+ h = h2;
+ }
+
+ if(h){
+ whereis_pos.row = 0;
+ next_handle = h;
+ if((result = scroll_handle_obscured(next_handle)) != 0){
+ long new_top;
+
+ if(scroll_handle_obscured(sparms->text.handles)
+ && result < 0)
+ next_handle = sparms->text.handles;
+
+ ps_global->mangled_body++;
+ new_top = scroll_handle_reframe(next_handle->key,0);
+ if(new_top >= 0)
+ cur_top_line = new_top;
+ }
+ }
+ }
+ else if(!(ch == ctrl('P') || F_ON(F_FORCE_ARROWS, ps_global)))
+ next_handle = scroll_handle_prev(sparms->text.handles);
+ }
+
+ if(!next_handle){
+ whereis_pos.row = 0;
+ if(cur_top_line){
+ cur_top_line--;
+ if(cur_top_line == 0)
+ q_status_message1(SM_INFO, 0, 1, "START of %s",
+ STYLE_NAME(sparms));
+ }
+ else
+ q_status_message1(SM_ORDER, 0, 1,
+ _("Already at start of %s"),
+ STYLE_NAME(sparms));
+ }
+
+ break;
+
+
+ case MC_NEXT_HANDLE :
+ if((next_handle = scroll_handle_next_sel(sparms->text.handles)) != NULL){
+ whereis_pos.row = 0;
+ if((result = scroll_handle_obscured(next_handle)) != 0){
+ long new_top;
+
+ if(scroll_handle_obscured(sparms->text.handles)
+ && result > 0)
+ next_handle = sparms->text.handles;
+
+ ps_global->mangled_body++;
+ new_top = scroll_handle_reframe(next_handle->key, 0);
+ if(new_top >= 0)
+ cur_top_line = new_top;
+ }
+ }
+ else{
+ if(scroll_handle_obscured(sparms->text.handles)){
+ long new_top;
+
+ ps_global->mangled_body++;
+ if((new_top = scroll_handle_reframe(-1, 0)) >= 0){
+ whereis_pos.row = 0;
+ cur_top_line = new_top;
+ }
+ }
+
+ q_status_message1(SM_ORDER, 0, 1,
+ _("Already on last item in %s"),
+ STYLE_NAME(sparms));
+ }
+
+ break;
+
+
+ case MC_PREV_HANDLE :
+ if((next_handle = scroll_handle_prev_sel(sparms->text.handles)) != NULL){
+ whereis_pos.row = 0;
+ if((result = scroll_handle_obscured(next_handle)) != 0){
+ long new_top;
+
+ if(scroll_handle_obscured(sparms->text.handles)
+ && result < 0)
+ next_handle = sparms->text.handles;
+
+ ps_global->mangled_body++;
+ new_top = scroll_handle_reframe(next_handle->key, 0);
+ if(new_top >= 0)
+ cur_top_line = new_top;
+ }
+ }
+ else{
+ if(scroll_handle_obscured(sparms->text.handles)){
+ long new_top;
+
+ ps_global->mangled_body++;
+ if((new_top = scroll_handle_reframe(-1, 0)) >= 0){
+ whereis_pos.row = 0;
+ cur_top_line = new_top;
+ }
+ }
+
+ q_status_message1(SM_ORDER, 0, 1,
+ _("Already on first item in %s"),
+ STYLE_NAME(sparms));
+ }
+
+ break;
+
+
+ /*------ View the current handle ------*/
+ case MC_VIEW_HANDLE :
+ switch(scroll_handle_obscured(sparms->text.handles)){
+ default :
+ case 0 :
+ switch(scroll_handle_launch(sparms->text.handles,
+ sparms->text.handles->force_display)){
+ case 1 :
+ cmd = MC_EXIT; /* propagate */
+ done = 1;
+ break;
+
+ case -1 :
+ cmd_cancelled(NULL);
+ break;
+
+ default :
+ break;
+ }
+
+ cur_top_line = scroll_state(SS_CUR)->top_text_line;
+ break;
+
+ case 1 :
+ q_status_message(SM_ORDER, 0, 2, HANDLE_BELOW_ERR);
+ break;
+
+ case -1 :
+ q_status_message(SM_ORDER, 0, 2, HANDLE_ABOVE_ERR);
+ break;
+ }
+
+ break;
+
+ /*---------- Search text (where is) ----------*/
+ case MC_WHEREIS :
+ ps_global->mangled_footer = 1;
+ {long start_row;
+ int start_index, key = 0;
+ char *report = NULL;
+
+ start_row = cur_top_line;
+ start_index = 0;
+
+ if(F_ON(F_SHOW_CURSOR,ps_global)){
+ if(found_on < 0
+ || found_on >= scroll_text_lines()
+ || found_on < cur_top_line
+ || found_on >= cur_top_line + num_display_lines){
+ start_row = cur_top_line;
+ start_index = 0;
+ }
+ else{
+ if(found_on_index < 0){
+ start_row = found_on + 1;
+ start_index = 0;
+ }
+ else{
+ start_row = found_on;
+ start_index = found_on_index+1;
+ }
+ }
+ }
+ else if(sparms->srch_handle){
+ HANDLE_S *h;
+
+ if((h = scroll_handle_next_sel(sparms->text.handles)) != NULL){
+ /*
+ * Translate the screen's column into the
+ * line offset to start on...
+ *
+ * This makes it so search_text never returns -3
+ * so we don't know it is the same match. That's
+ * because we start well after the current handle
+ * (at the next handle) and that causes us to
+ * think the one we just matched on is a different
+ * one from before. Can't think of an easy way to
+ * fix it, though, and it isn't a big deal. We still
+ * match, we just don't say current line contains
+ * the only match.
+ */
+ start_row = h->loc->where.row;
+ start_index = scroll_handle_index(start_row, h->loc->where.col);
+ }
+ else{
+ /* last handle, start over at top */
+ start_row = cur_top_line;
+ start_index = 0;
+ }
+ }
+ else{
+ start_row = (found_on < 0
+ || found_on >= scroll_text_lines()
+ || found_on < cur_top_line
+ || found_on >= cur_top_line + num_display_lines)
+ ? cur_top_line : found_on + 1,
+ start_index = 0;
+ }
+
+ found_on = search_text(-FOOTER_ROWS(ps_global), start_row,
+ start_index, &report,
+ &whereis_pos, &found_on_index);
+
+ if(found_on == -4){ /* search to top of text */
+ whereis_pos.row = 0;
+ whereis_pos.col = 0;
+ found_on = 0;
+ if(sparms->text.handles && sparms->srch_handle)
+ key = 1;
+ }
+ else if(found_on == -5){ /* search to bottom of text */
+ HANDLE_S *h;
+
+ whereis_pos.row = MAX(scroll_text_lines() - 1, 0);
+ whereis_pos.col = 0;
+ found_on = whereis_pos.row;
+ if((h = sparms->text.handles) && sparms->srch_handle)
+ do
+ key = h->key;
+ while((h = h->next) != NULL);
+ }
+ else if(found_on == -3){
+ whereis_pos.row = found_on = start_row;
+ found_on_index = start_index - 1;
+ q_status_message(SM_ORDER, 1, 3,
+ _("Current line contains the only match"));
+ }
+
+ if(found_on >= 0){
+ result = found_on < cur_top_line;
+ if(!key)
+ key = (sparms->text.handles)
+ ? dot_on_handle(found_on, whereis_pos.col) : 0;
+
+ if(F_ON(F_FORCE_LOW_SPEED,ps_global)
+ || ps_global->low_speed
+ || F_ON(F_SHOW_CURSOR,ps_global)
+ || key){
+ if((found_on >= cur_top_line + num_display_lines ||
+ found_on < cur_top_line) &&
+ num_display_lines > ps_global->viewer_overlap){
+ cur_top_line = found_on - ((found_on > 0) ? 1 : 0);
+ if(scroll_text_lines()-cur_top_line < 5)
+ cur_top_line = MAX(0,
+ scroll_text_lines()-MIN(5,num_display_lines));
+ }
+ /* else leave cur_top_line alone */
+ }
+ else{
+ cur_top_line = found_on - ((found_on > 0) ? 1 : 0);
+ if(scroll_text_lines()-cur_top_line < 5)
+ cur_top_line = MAX(0,
+ scroll_text_lines()-MIN(5,num_display_lines));
+ }
+
+ whereis_pos.row = whereis_pos.row - cur_top_line + 1;
+ if(report)
+ q_status_message(SM_ORDER, 0, 3, report);
+ else
+ q_status_message2(SM_ORDER, 0, 3,
+ "%sFound on line %s on screen",
+ result ? "Search wrapped to start. " : "",
+ int2string(whereis_pos.row));
+
+ if(key){
+ if(sparms->text.handles->key < key)
+ for(next_handle = sparms->text.handles->next;
+ next_handle->key != key;
+ next_handle = next_handle->next)
+ ;
+ else
+ for(next_handle = sparms->text.handles;
+ next_handle->key != key;
+ next_handle = next_handle->prev)
+ ;
+ }
+ }
+ else if(found_on == -1)
+ cmd_cancelled("Search");
+ else
+ q_status_message(SM_ORDER, 0, 3, _("Word not found"));
+ }
+
+ break;
+
+
+ /*-------------- jump command -------------*/
+ /* NOTE: preempt the process_cmd() version because
+ * we need to get at the number..
+ */
+ case MC_JUMP :
+ jn = jump_to(ps_global->msgmap, -FOOTER_ROWS(ps_global), ch,
+ sparms, View);
+ if(sparms && sparms->jump_is_debug)
+ done = 1;
+ else if(jn > 0 && jn != mn_get_cur(ps_global->msgmap)){
+
+ if(mn_total_cur(ps_global->msgmap) > 1L)
+ mn_reset_cur(ps_global->msgmap, jn);
+ else
+ mn_set_cur(ps_global->msgmap, jn);
+
+ done = 1;
+ }
+ else
+ ps_global->mangled_footer = 1;
+
+ break;
+
+
+#ifdef MOUSE
+ /*-------------- Mouse Event -------------*/
+ case MC_MOUSE:
+ {
+ MOUSEPRESS mp;
+ long line;
+ int key;
+
+ mouse_get_last (NULL, &mp);
+ mp.row -= 2;
+
+ /* The clicked line have anything special on it? */
+ if((line = cur_top_line + mp.row) < scroll_text_lines()
+ && (key = dot_on_handle(line, mp.col))){
+ switch(mp.button){
+ case M_BUTTON_RIGHT :
+#ifdef _WINDOWS
+ if(sparms->mouse.popup){
+ if(sparms->text.handles->key < key)
+ for(next_handle = sparms->text.handles->next;
+ next_handle->key != key;
+ next_handle = next_handle->next)
+ ;
+ else
+ for(next_handle = sparms->text.handles;
+ next_handle->key != key;
+ next_handle = next_handle->prev)
+ ;
+
+ if(sparms->mouse.popup){
+ cur_top_line = scroll_scroll_text(cur_top_line,
+ next_handle,
+ ps_global->mangled_body);
+ fflush(stdout);
+ switch((*sparms->mouse.popup)(sparms, key)){
+ case 1 :
+ cur_top_line = doubleclick_handle(sparms, next_handle, &cmd, &done);
+ break;
+
+ case 2 :
+ done++;
+ break;
+ }
+ }
+ }
+
+#endif
+ break;
+
+ case M_BUTTON_LEFT :
+ if(sparms->text.handles->key < key)
+ for(next_handle = sparms->text.handles->next;
+ next_handle->key != key;
+ next_handle = next_handle->next)
+ ;
+ else
+ for(next_handle = sparms->text.handles;
+ next_handle->key != key;
+ next_handle = next_handle->prev)
+ ;
+
+ if(mp.doubleclick) /* launch url */
+ cur_top_line = doubleclick_handle(sparms, next_handle, &cmd, &done);
+ else if(sparms->mouse.click)
+ (*sparms->mouse.click)(sparms);
+
+ break;
+
+ case M_BUTTON_MIDDLE : /* NO-OP for now */
+ break;
+
+ default: /* just ignore */
+ break;
+ }
+ }
+#ifdef _WINDOWS
+ else if(mp.button == M_BUTTON_RIGHT){
+ /*
+ * Toss generic popup on to the screen
+ */
+ if(sparms->mouse.popup)
+ if((*sparms->mouse.popup)(sparms, 0) == 2){
+ done++;
+ }
+ }
+#endif
+ }
+
+ break;
+#endif /* MOUSE */
+
+
+ /*-------------- Display Resize -------------*/
+ case MC_RESIZE :
+ if(sparms->resize_exit){
+ long line;
+
+ /*
+ * Figure out char offset of the char in the top left
+ * corner of the display. Pass it back to the
+ * fetcher/formatter and have it pass the offset
+ * back to us...
+ */
+ sparms->start.on = Offset;
+ for(sparms->start.loc.offset = line = 0L;
+ line < cur_top_line;
+ line++)
+ sparms->start.loc.offset += scroll_handle_column(line, -1);
+
+ done = 1;
+ ClearLine(1);
+ break;
+ }
+ /* else no reformatting neccessary, fall thru to repaint */
+
+
+ /*-------------- refresh -------------*/
+ case MC_REPAINT :
+ num_display_lines = SCROLL_LINES(ps_global);
+ mark_status_dirty();
+ mark_keymenu_dirty();
+ mark_titlebar_dirty();
+ ps_global->mangled_screen = 1;
+ force = 1;
+ break;
+
+
+ /*------- no op timeout to check for new mail ------*/
+ case MC_NONE :
+ break;
+
+
+ /*------- Forward displayed text ------*/
+ case MC_FWDTEXT :
+ forward_text(ps_global, sparms->text.text, sparms->text.src);
+ break;
+
+
+ /*----------- Save the displayed text ------------*/
+ case MC_SAVETEXT :
+ (void)simple_export(ps_global, sparms->text.text,
+ sparms->text.src, "text", NULL);
+ break;
+
+
+ /*----------- Exit this screen ------------*/
+ case MC_EXIT :
+ done = 1;
+ break;
+
+
+ /*----------- Pop back to the Main Menu ------------*/
+ case MC_MAIN :
+ ps_global->next_screen = main_menu_screen;
+ done = 1;
+ break;
+
+
+ /*----------- Print ------------*/
+ case MC_PRINTTXT :
+ print_to_printer(sparms);
+ break;
+
+
+ /* ------- First handle on Line ------ */
+ case MC_GOTOBOL :
+ if(sparms->text.handles){
+ next_handle = scroll_handle_boundary(sparms->text.handles,
+ scroll_handle_prev_sel);
+
+ break;
+ }
+ /* fall thru as bogus */
+
+ /* ------- Last handle on Line ------ */
+ case MC_GOTOEOL :
+ if(sparms->text.handles){
+ next_handle = scroll_handle_boundary(sparms->text.handles,
+ scroll_handle_next_sel);
+
+ break;
+ }
+ /* fall thru as bogus */
+
+ /*------- BOGUS INPUT ------*/
+ case MC_CHARRIGHT :
+ case MC_CHARLEFT :
+ case MC_UNKNOWN :
+ if(sparms->bogus_input)
+ done = (*sparms->bogus_input)(ch);
+ else
+ bogus_command(ch, F_ON(F_USE_FK,ps_global) ? "F1" : "?");
+
+ break;
+
+
+ case MC_UTF8:
+ bogus_utf8_command(utf8str, F_ON(F_USE_FK, ps_global) ? "F1" : "?");
+ break;
+
+
+ /*------- Standard commands ------*/
+ default:
+ whereis_pos.row = 0;
+ if(sparms->proc.tool)
+ result = (*sparms->proc.tool)(cmd, ps_global->msgmap, sparms);
+ else
+ result = process_cmd(ps_global, ps_global->mail_stream,
+ ps_global->msgmap, cmd, View, &force);
+
+ dprint((7, "PROCESS_CMD return: %d\n", result));
+
+ if(ps_global->next_screen != SCREEN_FUN_NULL || result == 1){
+ done = 1;
+ if(cmd == MC_FULLHDR){
+ if(ps_global->full_header == 1){
+ long line;
+
+ /*
+ * Figure out char offset of the char in the top left
+ * corner of the display. Pass it back to the
+ * fetcher/formatter and have it pass the offset
+ * back to us...
+ */
+ sparms->start.on = Offset;
+ for(sparms->start.loc.offset = line = 0L;
+ line < cur_top_line;
+ line++)
+ sparms->start.loc.offset +=
+ scroll_handle_column(line, -1);
+ }
+ else
+ sparms->start.on = 0;
+
+ switch(km->which){
+ case 0:
+ sparms->keys.what = FirstMenu;
+ break;
+ case 1:
+ sparms->keys.what = SecondMenu;
+ break;
+ case 2:
+ sparms->keys.what = ThirdMenu;
+ break;
+ case 3:
+ sparms->keys.what = FourthMenu;
+ break;
+ }
+ }
+ }
+ else if(!scroll_state(SS_CUR)){
+ num_display_lines = SCROLL_LINES(ps_global);
+ ps_global->mangled_screen = 1;
+ }
+
+ break;
+
+ } /* End of switch() */
+
+ /* Need to frame some handles? */
+ if(sparms->text.handles
+ && ((!next_handle
+ && handle_on_page(sparms->text.handles, cur_top_line,
+ cur_top_line + num_display_lines))
+ || (next_handle
+ && handle_on_page(next_handle, cur_top_line,
+ cur_top_line + num_display_lines))))
+ next_handle = scroll_handle_in_frame(cur_top_line);
+
+ } /* End of while() -- loop executing commands */
+
+ ps_global->redrawer = NULL; /* next statement makes this invalid! */
+ zero_scroll_text(); /* very important to zero out on return!!! */
+ scroll_state(SS_FREE);
+ if(sparms->bar.color)
+ free_color_pair(&sparms->bar.color);
+
+#ifdef _WINDOWS
+ scroll_setrange(0L, 0L);
+#endif
+ return(cmd);
+}
+
+
+/*----------------------------------------------------------------------
+ Print text on paper
+
+ Args: text -- The text to print out
+ source -- What type of source text is
+ message -- Message for open_printer()
+ Handling of error conditions is very poor.
+
+ ----*/
+static int
+print_to_printer(SCROLL_S *sparms)
+{
+ char message[64];
+
+ snprintf(message, sizeof(message), "%s", STYLE_NAME(sparms));
+ message[sizeof(message)-1] = '\0';
+
+ if(open_printer(message) != 0)
+ return(-1);
+
+ switch(sparms->text.src){
+ case CharStar :
+ if(sparms->text.text != (char *)NULL)
+ print_text((char *)sparms->text.text);
+
+ break;
+
+ case CharStarStar :
+ if(sparms->text.text != (char **)NULL){
+ register char **t;
+
+ for(t = sparms->text.text; *t != NULL; t++){
+ print_text(*t);
+ print_text(NEWLINE);
+ }
+ }
+
+ break;
+
+ case FileStar :
+ if(sparms->text.text != (FILE *)NULL) {
+ size_t n;
+ int i;
+
+ fseek((FILE *)sparms->text.text, 0L, 0);
+ n = SIZEOF_20KBUF - 1;
+ while((i = fread((void *)tmp_20k_buf, sizeof(char),
+ n, (FILE *)sparms->text.text)) != 0) {
+ tmp_20k_buf[i] = '\0';
+ print_text(tmp_20k_buf);
+ }
+ }
+
+ default :
+ break;
+ }
+
+ close_printer();
+ return(0);
+}
+
+
+/*----------------------------------------------------------------------
+ Search text being viewed (help or message)
+
+ Args: q_line -- The screen line to prompt for search string on
+ start_line -- Line number in text to begin search on
+ start_index -- Where to begin search at in first line of text
+ cursor_pos -- position of cursor is returned to caller here
+ (Actually, this isn't really the position of the
+ cursor because we don't know where we are on the
+ screen. So row is set to the line number and col
+ is set to the right column.)
+ offset_in_line -- Offset where match was found.
+
+ Result: returns line number string was found on
+ -1 for cancel
+ -2 if not found
+ -3 if only match is at start_index - 1
+ -4 if search to first line
+ -5 if search to last line
+ ---*/
+int
+search_text(int q_line, long int start_line, int start_index, char **report,
+ Pos *cursor_pos, int *offset_in_line)
+{
+ char prompt[MAX_SEARCH+50], nsearch_string[MAX_SEARCH+1], *p;
+ HelpType help;
+ int rc, flags;
+ static HISTORY_S *history = NULL;
+ char search_string[MAX_SEARCH+1];
+ static ESCKEY_S word_search_key[] = { { 0, 0, "", "" },
+ {ctrl('Y'), 10, "^Y", N_("First Line")},
+ {ctrl('V'), 11, "^V", N_("Last Line")},
+ {KEY_UP, 30, "", ""},
+ {KEY_DOWN, 31, "", ""},
+ {-1, 0, NULL, NULL}
+ };
+#define KU_ST (3) /* index of KEY_UP */
+
+ init_hist(&history, HISTSIZE);
+
+ /*
+ * Put the last one used in the default search_string,
+ * not in nsearch_string.
+ */
+ search_string[0] = '\0';
+ if((p = get_prev_hist(history, "", 0, NULL)) != NULL){
+ strncpy(search_string, p, sizeof(search_string));
+ search_string[sizeof(search_string)-1] = '\0';
+ }
+
+ snprintf(prompt, sizeof(prompt), _("Word to search for [%s] : "), search_string);
+ help = NO_HELP;
+ nsearch_string[0] = '\0';
+
+ while(1) {
+ flags = OE_APPEND_CURRENT | OE_SEQ_SENSITIVE | OE_KEEP_TRAILING_SPACE;
+
+ /*
+ * 2 is really 1 because there will be one real entry and
+ * one entry of "" because of the get_prev_hist above.
+ */
+ if(items_in_hist(history) > 2){
+ word_search_key[KU_ST].name = HISTORY_UP_KEYNAME;
+ word_search_key[KU_ST].label = HISTORY_KEYLABEL;
+ word_search_key[KU_ST+1].name = HISTORY_DOWN_KEYNAME;
+ word_search_key[KU_ST+1].label = HISTORY_KEYLABEL;
+ }
+ else{
+ word_search_key[KU_ST].name = "";
+ word_search_key[KU_ST].label = "";
+ word_search_key[KU_ST+1].name = "";
+ word_search_key[KU_ST+1].label = "";
+ }
+
+ rc = optionally_enter(nsearch_string, q_line, 0, sizeof(nsearch_string),
+ prompt, word_search_key, help, &flags);
+
+ if(rc == 3) {
+ help = help == NO_HELP ? h_oe_searchview : NO_HELP;
+ continue;
+ }
+ else if(rc == 10){
+ if(report)
+ *report = _("Searched to First Line.");
+
+ return(-4);
+ }
+ else if(rc == 11){
+ if(report)
+ *report = _("Searched to Last Line.");
+
+ return(-5);
+ }
+ else if(rc == 30){
+ if((p = get_prev_hist(history, nsearch_string, 0, NULL)) != NULL){
+ strncpy(nsearch_string, p, sizeof(nsearch_string));
+ nsearch_string[sizeof(nsearch_string)-1] = '\0';
+ }
+ else
+ Writechar(BELL, 0);
+
+ continue;
+ }
+ else if(rc == 31){
+ if((p = get_next_hist(history, nsearch_string, 0, NULL)) != NULL){
+ strncpy(nsearch_string, p, sizeof(nsearch_string));
+ nsearch_string[sizeof(nsearch_string)-1] = '\0';
+ }
+ else
+ Writechar(BELL, 0);
+
+ continue;
+ }
+
+ if(rc != 4){ /* 4 is redraw */
+ save_hist(history, nsearch_string, 0, NULL);
+ break;
+ }
+ }
+
+ if(rc == 1 || (search_string[0] == '\0' && nsearch_string[0] == '\0'))
+ return(-1);
+
+ if(nsearch_string[0] != '\0'){
+ strncpy(search_string, nsearch_string, sizeof(search_string)-1);
+ search_string[sizeof(search_string)-1] = '\0';
+ }
+
+ rc = search_scroll_text(start_line, start_index, search_string, cursor_pos,
+ offset_in_line);
+ return(rc);
+}
+
+
+/*----------------------------------------------------------------------
+ Update the scroll tool's titlebar
+
+ Args: cur_top_line --
+ redraw -- flag to force updating
+
+ ----*/
+void
+update_scroll_titlebar(long int cur_top_line, int redraw)
+{
+ SCRLCTRL_S *st = scroll_state(SS_CUR);
+ int num_display_lines = SCROLL_LINES(ps_global);
+ long new_line = (cur_top_line + num_display_lines > st->num_lines)
+ ? st->num_lines
+ : cur_top_line + num_display_lines;
+ long raw_msgno;
+ COLOR_PAIR *returned_color = NULL;
+ COLOR_PAIR *titlecolor = NULL;
+ int colormatch;
+ SEARCHSET *ss = NULL;
+
+ if(st->parms->use_indexline_color
+ && ps_global->titlebar_color_style != TBAR_COLOR_DEFAULT){
+ raw_msgno = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
+ if(raw_msgno > 0L && ps_global->mail_stream
+ && raw_msgno <= ps_global->mail_stream->nmsgs){
+ ss = mail_newsearchset();
+ ss->first = ss->last = (unsigned long) raw_msgno;
+ }
+
+ if(ss){
+ PAT_STATE *pstate = NULL;
+
+ colormatch = get_index_line_color(ps_global->mail_stream,
+ ss, &pstate, &returned_color);
+ mail_free_searchset(&ss);
+
+ /*
+ * This is a bit tricky. If there is a colormatch but returned_color
+ * is NULL, that means that the user explicitly wanted the
+ * Normal color used in this index line, so that is what we
+ * use. If no colormatch then we will use the TITLE color
+ * instead of Normal.
+ */
+ if(colormatch){
+ if(returned_color)
+ titlecolor = returned_color;
+ else
+ titlecolor = new_color_pair(ps_global->VAR_NORM_FORE_COLOR,
+ ps_global->VAR_NORM_BACK_COLOR);
+ }
+
+ if(titlecolor
+ && ps_global->titlebar_color_style == TBAR_COLOR_REV_INDEXLINE){
+ char cbuf[MAXCOLORLEN+1];
+
+ strncpy(cbuf, titlecolor->fg, MAXCOLORLEN);
+ strncpy(titlecolor->fg, titlecolor->bg, MAXCOLORLEN);
+ strncpy(titlecolor->bg, cbuf, MAXCOLORLEN);
+ }
+ }
+
+ /* Did the color change? */
+ if((!titlecolor && st->parms->bar.color)
+ ||
+ (titlecolor && !st->parms->bar.color)
+ ||
+ (titlecolor && st->parms->bar.color
+ && (strcmp(titlecolor->fg, st->parms->bar.color->fg)
+ || strcmp(titlecolor->bg, st->parms->bar.color->bg)))){
+
+ redraw++;
+ if(st->parms->bar.color)
+ free_color_pair(&st->parms->bar.color);
+
+ st->parms->bar.color = titlecolor;
+ titlecolor = NULL;
+ }
+
+ if(titlecolor)
+ free_color_pair(&titlecolor);
+ }
+
+
+ if(redraw){
+ set_titlebar(st->parms->bar.title, ps_global->mail_stream,
+ ps_global->context_current, ps_global->cur_folder,
+ ps_global->msgmap, 1, st->parms->bar.style,
+ new_line, st->num_lines, st->parms->bar.color);
+ ps_global->mangled_header = 0;
+ }
+ else if(st->parms->bar.style == TextPercent)
+ update_titlebar_lpercent(new_line);
+ else
+ update_titlebar_percent(new_line);
+}
+
+
+/*----------------------------------------------------------------------
+ manager of global (to this module, anyway) scroll state structures
+
+
+ ----*/
+SCRLCTRL_S *
+scroll_state(int func)
+{
+ struct scrollstack {
+ SCRLCTRL_S s;
+ struct scrollstack *prev;
+ } *s;
+ static struct scrollstack *stack = NULL;
+
+ switch(func){
+ case SS_CUR: /* no op */
+ break;
+ case SS_NEW:
+ s = (struct scrollstack *)fs_get(sizeof(struct scrollstack));
+ memset((void *)s, 0, sizeof(struct scrollstack));
+ s->prev = stack;
+ stack = s;
+ break;
+ case SS_FREE:
+ if(stack){
+ s = stack->prev;
+ fs_give((void **)&stack);
+ stack = s;
+ }
+ break;
+ default: /* BUG: should complain */
+ break;
+ }
+
+ return(stack ? &stack->s : NULL);
+}
+
+
+/*----------------------------------------------------------------------
+ Save all the data for scrolling text and paint the screen
+
+
+ ----*/
+void
+set_scroll_text(SCROLL_S *sparms, long int current_line, SCRLCTRL_S *st)
+{
+ /* save all the stuff for possible asynchronous redraws */
+ st->parms = sparms;
+ st->top_text_line = current_line;
+ st->screen.start_line = SCROLL_LINES_ABOVE(ps_global);
+ st->screen.other_lines = SCROLL_LINES_ABOVE(ps_global)
+ + SCROLL_LINES_BELOW(ps_global);
+ st->screen.width = -1; /* Force text formatting calculation */
+}
+
+
+/*----------------------------------------------------------------------
+ Redraw the text on the screen, possibly reformatting if necessary
+
+ Args None
+
+ ----*/
+void
+redraw_scroll_text(void)
+{
+ int i, offset;
+ SCRLCTRL_S *st = scroll_state(SS_CUR);
+
+ format_scroll_text();
+
+ offset = (st->parms->text.src == FileStar) ? 0 : st->top_text_line;
+
+#ifdef _WINDOWS
+ mswin_beginupdate();
+#endif
+ /*---- Actually display the text on the screen ------*/
+ for(i = 0; i < st->screen.length; i++){
+ ClearLine(i + st->screen.start_line);
+ if((offset + i) < st->num_lines)
+ PutLine0n8b(i + st->screen.start_line, 0, st->text_lines[offset + i],
+ st->line_lengths[offset + i], st->parms->text.handles);
+ }
+
+
+ fflush(stdout);
+#ifdef _WINDOWS
+ mswin_endupdate();
+#endif
+}
+
+
+/*----------------------------------------------------------------------
+ Free memory used as scrolling buffers for text on disk. Also mark
+ text_lines as available
+ ----*/
+void
+zero_scroll_text(void)
+{
+ SCRLCTRL_S *st = scroll_state(SS_CUR);
+ register int i;
+
+ for(i = 0; i < st->lines_allocated; i++)
+ if(st->parms->text.src == FileStar && st->text_lines[i])
+ fs_give((void **)&st->text_lines[i]);
+ else
+ st->text_lines[i] = NULL;
+
+ if(st->parms->text.src == FileStar && st->findex != NULL){
+ fclose(st->findex);
+ st->findex = NULL;
+ if(st->fname){
+ our_unlink(st->fname);
+ fs_give((void **)&st->fname);
+ }
+ }
+
+ if(st->text_lines)
+ fs_give((void **)&st->text_lines);
+
+ if(st->line_lengths)
+ fs_give((void **) &st->line_lengths);
+}
+
+
+/*----------------------------------------------------------------------
+
+Always format at least 20 chars wide. Wrapping lines would be crazy for
+screen widths of 1-20 characters
+ ----*/
+void
+format_scroll_text(void)
+{
+ int i;
+ char *p, **pp;
+ SCRLCTRL_S *st = scroll_state(SS_CUR);
+ register short *ll;
+ register char **tl, **tl_end;
+
+ if(!st || (st->screen.width == (i = ps_global->ttyo->screen_cols)
+ && st->screen.length == PGSIZE(st)))
+ return;
+
+ st->screen.width = MAX(20, i);
+ st->screen.length = PGSIZE(st);
+
+ if(st->lines_allocated == 0) {
+ st->lines_allocated = TYPICAL_BIG_MESSAGE_LINES;
+ st->text_lines = (char **)fs_get(st->lines_allocated *sizeof(char *));
+ memset(st->text_lines, 0, st->lines_allocated * sizeof(char *));
+ st->line_lengths = (short *)fs_get(st->lines_allocated *sizeof(short));
+ }
+
+ tl = st->text_lines;
+ ll = st->line_lengths;
+ tl_end = &st->text_lines[st->lines_allocated];
+
+ if(st->parms->text.src == CharStarStar) {
+ /*---- original text is already list of lines -----*/
+ /* The text could be wrapped nicely for narrow screens; for now
+ it will get truncated as it is displayed */
+ for(pp = (char **)st->parms->text.text; *pp != NULL;) {
+ *tl++ = *pp++;
+ *ll++ = st->screen.width;
+ if(tl >= tl_end) {
+ i = tl - st->text_lines;
+ st->lines_allocated *= 2;
+ fs_resize((void **)&st->text_lines,
+ st->lines_allocated * sizeof(char *));
+ fs_resize((void **)&st->line_lengths,
+ st->lines_allocated*sizeof(short));
+ tl = &st->text_lines[i];
+ ll = &st->line_lengths[i];
+ tl_end = &st->text_lines[st->lines_allocated];
+ }
+ }
+
+ st->num_lines = tl - st->text_lines;
+ }
+ else if (st->parms->text.src == CharStar) {
+ /*------ Format the plain text ------*/
+ for(p = (char *)st->parms->text.text; *p; ) {
+ *tl = p;
+
+ for(; *p && !(*p == RETURN || *p == LINE_FEED); p++)
+ ;
+
+ *ll = p - *tl;
+ ll++; tl++;
+ if(tl >= tl_end) {
+ i = tl - st->text_lines;
+ st->lines_allocated *= 2;
+ fs_resize((void **)&st->text_lines,
+ st->lines_allocated * sizeof(char *));
+ fs_resize((void **)&st->line_lengths,
+ st->lines_allocated*sizeof(short));
+ tl = &st->text_lines[i];
+ ll = &st->line_lengths[i];
+ tl_end = &st->text_lines[st->lines_allocated];
+ }
+
+ if(*p == '\r' && *(p+1) == '\n')
+ p += 2;
+ else if(*p == '\n' || *p == '\r')
+ p++;
+ }
+
+ st->num_lines = tl - st->text_lines;
+ }
+ else {
+ /*------ Display text is in a file --------*/
+
+ /*
+ * This is pretty much only useful under DOS where we can't fit
+ * all of big messages in core at once. This scheme makes
+ * some simplifying assumptions:
+ * 1. Lines are on disk just the way we'll display them. That
+ * is, line breaks and such are left to the function that
+ * writes the disk file to catch and fix.
+ * 2. We get away with this mainly because the DOS display isn't
+ * going to be resized out from under us.
+ *
+ * The idea is to use the already alloc'd array of char * as a
+ * buffer for sections of what's on disk. We'll set up the first
+ * few lines here, and read new ones in as needed in
+ * scroll_scroll_text().
+ *
+ * but first, make sure there are enough buffer lines allocated
+ * to serve as a place to hold lines from the file.
+ *
+ * Actually, this is also used under windows so the display will
+ * be resized out from under us. So I changed the following
+ * to always
+ * 1. free old text_lines, which may have been allocated
+ * for a narrow screen.
+ * 2. insure we have enough text_lines
+ * 3. reallocate all text_lines that are needed.
+ * (tom unger 10/26/94)
+ */
+
+ /* free old text lines, which may be too short. */
+ for(i = 0; i < st->lines_allocated; i++)
+ if(st->text_lines[i]) /* clear alloc'd lines */
+ fs_give((void **)&st->text_lines[i]);
+
+ /* Insure we have enough text lines. */
+ if(st->lines_allocated < (2 * PGSIZE(st)) + 1){
+ st->lines_allocated = (2 * PGSIZE(st)) + 1; /* resize */
+
+ fs_resize((void **)&st->text_lines,
+ st->lines_allocated * sizeof(char *));
+ memset(st->text_lines, 0, st->lines_allocated * sizeof(char *));
+ fs_resize((void **)&st->line_lengths,
+ st->lines_allocated*sizeof(short));
+ }
+
+ /* reallocate all text lines that are needed. */
+ for(i = 0; i <= PGSIZE(st); i++)
+ if(st->text_lines[i] == NULL)
+ st->text_lines[i] = (char *)fs_get((st->screen.width + 1)
+ * sizeof(char));
+
+ tl = &st->text_lines[i];
+
+ st->num_lines = make_file_index();
+
+ ScrollFile(st->top_text_line); /* then load them up */
+ }
+
+ /*
+ * Efficiency hack. If there are handles, fill in their
+ * line number field for later...
+ */
+ if(st->parms->text.handles){
+ long line;
+ int i, col, n, key;
+ HANDLE_S *h;
+
+ for(line = 0; line < st->num_lines; line++)
+ for(i = 0, col = 0; i < st->line_lengths[line];)
+ switch(st->text_lines[line][i]){
+ case TAG_EMBED:
+ i++;
+ switch((i < st->line_lengths[line]) ? st->text_lines[line][i]
+ : 0){
+ case TAG_HANDLE:
+ for(key = 0, n = st->text_lines[line][++i]; n > 0; n--)
+ key = (key * 10) + (st->text_lines[line][++i] - '0');
+
+ i++;
+ for(h = st->parms->text.handles; h; h = h->next)
+ if(h->key == key){
+ scroll_handle_set_loc(&h->loc, line, col);
+ break;
+ }
+
+ if(!h) /* anything behind us? */
+ for(h = st->parms->text.handles->prev; h; h = h->prev)
+ if(h->key == key){
+ scroll_handle_set_loc(&h->loc, line, col);
+ break;
+ }
+
+ break;
+
+ case TAG_FGCOLOR :
+ case TAG_BGCOLOR :
+ i += (RGBLEN + 1); /* 1 for TAG, RGBLEN for color */
+ break;
+
+ case TAG_INVON:
+ case TAG_INVOFF:
+ case TAG_BOLDON:
+ case TAG_BOLDOFF:
+ case TAG_ULINEON:
+ case TAG_ULINEOFF:
+ i++;
+ break;
+
+ default: /* literal embed char */
+ break;
+ }
+
+ break;
+
+ case TAB:
+ i++;
+ while(((++col) & 0x07) != 0) /* add tab's spaces */
+ ;
+
+ break;
+
+ default:
+ col += width_at_this_position((unsigned char*) &st->text_lines[line][i],
+ st->line_lengths[line] - i);
+ i++; /* character count */
+ break;
+ }
+ }
+
+#ifdef _WINDOWS
+ scroll_setrange (st->screen.length, st->num_lines);
+#endif
+
+ *tl = NULL;
+}
+
+
+/*
+ * ScrollFile - scroll text into the st struct file making sure 'line'
+ * of the file is the one first in the text_lines buffer.
+ *
+ * NOTE: talk about massive potential for tuning...
+ * Goes without saying this is still under constuction
+ */
+void
+ScrollFile(long int line)
+{
+ SCRLCTRL_S *st = scroll_state(SS_CUR);
+ SCRLFILE_S sf;
+ register int i;
+
+ if(line <= 0){ /* reset and load first couple of pages */
+ fseek((FILE *) st->parms->text.text, 0L, 0);
+ line = 0L;
+ }
+
+ if(!st->text_lines)
+ return;
+
+ for(i = 0; i < PGSIZE(st); i++){
+ /*** do stuff to get the file pointer into the right place ***/
+ /*
+ * BOGUS: this is painfully crude right now, but I just want to get
+ * it going.
+ *
+ * possibly in the near furture, an array of indexes into the
+ * file that are the offset for the beginning of each line will
+ * speed things up. Of course, this
+ * will have limits, so maybe a disk file that is an array
+ * of indexes is the answer.
+ */
+ if(fseek(st->findex, (size_t)(line++) * sizeof(SCRLFILE_S), 0) < 0
+ || fread(&sf, sizeof(SCRLFILE_S), (size_t)1, st->findex) != 1
+ || fseek((FILE *) st->parms->text.text, sf.offset, 0) < 0
+ || !st->text_lines[i]
+ || (sf.len && !fgets(st->text_lines[i], sf.len + 1,
+ (FILE *) st->parms->text.text)))
+ break;
+
+ st->line_lengths[i] = sf.len;
+ }
+
+ for(; i < PGSIZE(st); i++)
+ if(st->text_lines[i]){ /* blank out any unused lines */
+ *st->text_lines[i] = '\0';
+ st->line_lengths[i] = 0;
+ }
+}
+
+
+/*
+ * make_file_index - do a single pass over the file containing the text
+ * to display, recording line lengths and offsets.
+ * NOTE: This is never really to be used on a real OS with virtual
+ * memory. This is the whole reason st->findex exists. Don't
+ * want to waste precious memory on a stupid array that could
+ * be very large.
+ */
+long
+make_file_index(void)
+{
+ SCRLCTRL_S *st = scroll_state(SS_CUR);
+ SCRLFILE_S sf;
+ long l = 0L;
+ int state = 0;
+
+ if(!st->findex){
+ if(!st->fname)
+ st->fname = temp_nam(NULL, "pi");
+
+ if(!st->fname || (st->findex = our_fopen(st->fname,"w+b")) == NULL){
+ if(st->fname){
+ our_unlink(st->fname);
+ fs_give((void **)&st->fname);
+ }
+
+ return(0);
+ }
+ }
+ else
+ fseek(st->findex, 0L, 0);
+
+ fseek((FILE *)st->parms->text.text, 0L, 0);
+
+ while(1){
+ sf.len = st->screen.width + 1;
+ if(scroll_file_line((FILE *) st->parms->text.text,
+ tmp_20k_buf, &sf, &state)){
+ fwrite((void *) &sf, sizeof(SCRLFILE_S), (size_t)1, st->findex);
+ l++;
+ }
+ else
+ break;
+ }
+
+ fseek((FILE *)st->parms->text.text, 0L, 0);
+
+ return(l);
+}
+
+
+/*----------------------------------------------------------------------
+ Get the next line to scroll from the given file
+
+ ----*/
+char *
+scroll_file_line(FILE *fp, char *buf, SCRLFILE_S *sfp, int *wrapt)
+{
+ register char *s = NULL;
+
+ while(1){
+ if(!s){
+ sfp->offset = ftell(fp);
+ if(!(s = fgets(buf, sfp->len, fp)))
+ return(NULL); /* can't grab a line? */
+ }
+
+ if(!*s){
+ *wrapt = 1; /* remember; that we wrapped */
+ break;
+ }
+ else if(*s == NEWLINE[0] && (!NEWLINE[1] || *(s+1) == NEWLINE[1])){
+ int empty = (*wrapt && s == buf);
+
+ *wrapt = 0; /* turn off wrapped state */
+ if(empty)
+ s = NULL; /* get a new line */
+ else
+ break; /* done! */
+ }
+ else
+ s++;
+ }
+
+ sfp->len = s - buf;
+ return(buf);
+}
+
+
+/*----------------------------------------------------------------------
+ Scroll the text on the screen
+
+ Args: new_top_line -- The line to be displayed on top of the screen
+ redraw -- Flag to force a redraw even if nothing changed
+
+ Returns: resulting top line
+ Note: the returned line number may be less than new_top_line if
+ reformatting caused the total line count to change.
+
+ ----*/
+long
+scroll_scroll_text(long int new_top_line, HANDLE_S *handle, int redraw)
+{
+ SCRLCTRL_S *st = scroll_state(SS_CUR);
+ int num_display_lines, l, top;
+ POSLIST_S *lp, *lp2;
+
+ /* When this is true, we're still on the same page of the display. */
+ if(st->top_text_line == new_top_line && !redraw){
+ /* handle changed, so hilite the new handle and unhilite the old */
+ if(handle && handle != st->parms->text.handles){
+ top = st->screen.start_line - new_top_line;
+ /* hilite the new one */
+ if(!scroll_handle_obscured(handle))
+ for(lp = handle->loc; lp; lp = lp->next)
+ if((l = lp->where.row) >= st->top_text_line
+ && l < st->top_text_line + st->screen.length){
+ if(st->parms->text.src == FileStar)
+ l -= new_top_line;
+
+ PutLine0n8b(top + lp->where.row, 0, st->text_lines[l],
+ st->line_lengths[l], handle);
+ }
+
+ /* unhilite the old one */
+ if(!scroll_handle_obscured(st->parms->text.handles))
+ for(lp = st->parms->text.handles->loc; lp; lp = lp->next)
+ if((l = lp->where.row) >= st->top_text_line
+ && l < st->top_text_line + st->screen.length){
+ for(lp2 = handle->loc; lp2; lp2 = lp2->next)
+ if(l == lp2->where.row)
+ break;
+
+ if(!lp2){
+ if(st->parms->text.src == FileStar)
+ l -= new_top_line;
+
+ PutLine0n8b(top + lp->where.row, 0, st->text_lines[l],
+ st->line_lengths[l], handle);
+ }
+ }
+
+ st->parms->text.handles = handle; /* update current */
+ }
+
+ return(new_top_line);
+ }
+
+ num_display_lines = PGSIZE(st);
+
+ format_scroll_text();
+
+ if(st->top_text_line >= st->num_lines) /* don't pop line count */
+ new_top_line = st->top_text_line = MAX(st->num_lines - 1, 0);
+
+ if(st->parms->text.src == FileStar)
+ ScrollFile(new_top_line); /* set up new st->text_lines */
+
+#ifdef _WINDOWS
+ scroll_setrange (st->screen.length, st->num_lines);
+ scroll_setpos (new_top_line);
+#endif
+
+ /* ---
+ Check out the scrolling situation. If we want to scroll, but BeginScroll
+ says we can't then repaint, + 10 is so we repaint most of the time.
+ ----*/
+ if(redraw ||
+ (st->top_text_line - new_top_line + 10 >= num_display_lines ||
+ new_top_line - st->top_text_line + 10 >= num_display_lines) ||
+ BeginScroll(st->screen.start_line,
+ st->screen.start_line + num_display_lines - 1) != 0) {
+ /* Too much text to scroll, or can't scroll -- just repaint */
+
+ if(handle)
+ st->parms->text.handles = handle;
+
+ st->top_text_line = new_top_line;
+ redraw_scroll_text();
+ }
+ else{
+ /*
+ * We're going to scroll the screen, but first we have to make sure
+ * the old hilited handles are unhilited if they are going to remain
+ * on the screen.
+ */
+ top = st->screen.start_line - st->top_text_line;
+ if(handle && handle != st->parms->text.handles
+ && st->parms->text.handles
+ && !scroll_handle_obscured(st->parms->text.handles))
+ for(lp = st->parms->text.handles->loc; lp; lp = lp->next)
+ if((l = lp->where.row) >= MAX(st->top_text_line,new_top_line)
+ && l < MIN(st->top_text_line,new_top_line) + st->screen.length){
+ if(st->parms->text.src == FileStar)
+ l -= new_top_line;
+
+ PutLine0n8b(top + lp->where.row, 0, st->text_lines[l],
+ st->line_lengths[l], handle);
+ }
+
+ if(new_top_line > st->top_text_line){
+ /*------ scroll down ------*/
+ while(new_top_line > st->top_text_line) {
+ ScrollRegion(1);
+
+ l = (st->parms->text.src == FileStar)
+ ? num_display_lines - (new_top_line - st->top_text_line)
+ : st->top_text_line + num_display_lines;
+
+ if(l < st->num_lines){
+ PutLine0n8b(st->screen.start_line + num_display_lines - 1,
+ 0, st->text_lines[l], st->line_lengths[l],
+ handle ? handle : st->parms->text.handles);
+ /*
+ * We clear to the end of line in the right background
+ * color. If the line was exactly the width of the screen
+ * then PutLine0n8b will have left _col and _row moved to
+ * the start of the next row. We don't need or want to clear
+ * that next row.
+ */
+ if(pico_usingcolor()
+ && (st->line_lengths[l] < ps_global->ttyo->screen_cols
+ || visible_linelen(l) < ps_global->ttyo->screen_cols))
+ CleartoEOLN();
+ }
+
+ st->top_text_line++;
+ }
+ }
+ else{
+ /*------ scroll up -----*/
+ while(new_top_line < st->top_text_line) {
+ ScrollRegion(-1);
+
+ st->top_text_line--;
+ l = (st->parms->text.src == FileStar)
+ ? st->top_text_line - new_top_line
+ : st->top_text_line;
+ PutLine0n8b(st->screen.start_line, 0, st->text_lines[l],
+ st->line_lengths[l],
+ handle ? handle : st->parms->text.handles);
+ /*
+ * We clear to the end of line in the right background
+ * color. If the line was exactly the width of the screen
+ * then PutLine0n8b will have left _col and _row moved to
+ * the start of the next row. We don't need or want to clear
+ * that next row.
+ */
+ if(pico_usingcolor()
+ && (st->line_lengths[l] < ps_global->ttyo->screen_cols
+ || visible_linelen(l) < ps_global->ttyo->screen_cols))
+ CleartoEOLN();
+ }
+ }
+
+ EndScroll();
+
+ if(handle && handle != st->parms->text.handles){
+ POSLIST_S *lp;
+
+ for(lp = handle->loc; lp; lp = lp->next)
+ if(lp->where.row >= st->top_text_line
+ && lp->where.row < st->top_text_line + st->screen.length){
+ PutLine0n8b(st->screen.start_line
+ + (lp->where.row - st->top_text_line),
+ 0, st->text_lines[lp->where.row],
+ st->line_lengths[lp->where.row],
+ handle);
+
+ }
+
+ st->parms->text.handles = handle;
+ }
+
+ fflush(stdout);
+ }
+
+ return(new_top_line);
+}
+
+
+/*---------------------------------------------------------------------
+ Edit individual char in text so that the entire text doesn't need
+ to be completely reformatted.
+
+ Returns 0 if there were no errors, 1 if we would like the entire
+ text to be reformatted.
+----*/
+int
+ng_scroll_edit(CONTEXT_S *context, int index)
+{
+ SCRLCTRL_S *st = scroll_state(SS_CUR);
+ char *ngp, tmp[MAILTMPLEN+10];
+ int len;
+ FOLDER_S *f;
+
+ if (!(f = folder_entry(index, FOLDERS(context))))
+ return 1;
+ if(f->subscribed)
+ return 0; /* nothing in scroll needs to be changed */
+ tmp[0] = TAG_HANDLE;
+ snprintf(tmp+2, sizeof(tmp)-2, "%d", st->parms->text.handles->key);
+ tmp[sizeof(tmp)-1] = '\0';
+ tmp[1] = len = strlen(tmp+2);
+ snprintf(tmp+len+2, sizeof(tmp)-(len+2), "%s ", f->selected ? "[ ]" : "[X]");
+ tmp[sizeof(tmp)-1] = '\0';
+ snprintf(tmp+len+6, sizeof(tmp)-(len+6), "%.*s", MAILTMPLEN, f->name);
+ tmp[sizeof(tmp)-1] = '\0';
+
+ ngp = *(st->text_lines);
+
+ ngp = strstr(ngp, tmp);
+
+ if(!ngp) return 1;
+ ngp += 3+len;
+
+ /* assumption that text is of form "[ ] xxx.xxx" */
+
+ if(ngp){
+ if(*ngp == 'X'){
+ *ngp = ' ';
+ return 0;
+ }
+ else if (*ngp == ' '){
+ *ngp = 'X';
+ return 0;
+ }
+ }
+ return 1;
+}
+
+
+/*---------------------------------------------------------------------
+ Similar to ng_scroll_edit, but this is the more general case of
+ selecting a folder, as opposed to selecting a newsgroup for
+ subscription while in listmode.
+
+ Returns 0 if there were no errors, 1 if we would like the entire
+ text to be reformatted.
+----*/
+int
+folder_select_update(CONTEXT_S *context, int index)
+{
+ SCRLCTRL_S *st = scroll_state(SS_CUR);
+ FOLDER_S *f;
+ char *ngp, tmp[MAILTMPLEN+10];
+ int len, total, fnum, num_sel = 0;
+
+ if (!(f = folder_entry(index, FOLDERS(context))))
+ return 1;
+ ngp = *(st->text_lines);
+
+ total = folder_total(FOLDERS(context));
+
+ for (fnum = 0; num_sel < 2 && fnum < total; fnum++)
+ if(folder_entry(fnum, FOLDERS(context))->selected)
+ num_sel++;
+ if(!num_sel || (f->selected && num_sel == 1))
+ return 1; /* need to reformat the whole thing */
+
+ tmp[0] = TAG_HANDLE;
+ snprintf(tmp+2, sizeof(tmp)-2, "%d", st->parms->text.handles->key);
+ tmp[sizeof(tmp)-1] = '\0';
+ tmp[1] = len = strlen(tmp+2);
+
+ ngp = strstr(ngp, tmp);
+ if(!ngp) return 1;
+
+ if(F_ON(F_SELECTED_SHOWN_BOLD, ps_global)){
+ ngp += 2 + len;
+ while(*ngp && ngp[0] != TAG_EMBED
+ && ngp[1] != (f->selected ? TAG_BOLDOFF : TAG_BOLDON)
+ && *ngp != *(f->name))
+ ngp++;
+
+ if (!(*ngp) || (*ngp == *(f->name)))
+ return 1;
+ else {
+ ngp++;
+ *ngp = (f->selected ? TAG_BOLDON : TAG_BOLDOFF);
+ return 0;
+ }
+ }
+ else{
+ while(*ngp != ' ' && *ngp != *(f->name) && *ngp)
+ ngp++;
+ if(!(*ngp) || (*ngp == *(f->name)))
+ return 1;
+ else {
+ ngp++;
+ *ngp = f->selected ? 'X' : ' ';
+ return 0;
+ }
+ }
+}
+
+
+/*---------------------------------------------------------------------
+ We gotta go through all of the formatted text and add "[ ] " in the right
+ place. If we don't do this, we must completely reformat the whole text,
+ which could take a very long time.
+
+ Return 1 if we encountered some sort of error and we want to reformat the
+ whole text, return 0 if everything went as planned.
+
+ ASSUMPTION: for this to work, we assume that there are only total
+ number of handles, numbered 1 through total.
+----*/
+int
+scroll_add_listmode(CONTEXT_S *context, int total)
+{
+ SCRLCTRL_S *st = scroll_state(SS_CUR);
+ long i;
+ char *ngp, *ngname, handle_str[MAILTMPLEN];
+ HANDLE_S *h;
+
+
+ ngp = *(st->text_lines);
+ h = st->parms->text.handles;
+
+ while(h && h->key != 1 && h->prev)
+ h = h->prev;
+ if (!h) return 1;
+ handle_str[0] = TAG_EMBED;
+ handle_str[1] = TAG_HANDLE;
+ for(i = 1; i <= total && h; i++, h = h->next){
+ snprintf(handle_str+3, sizeof(handle_str)-3, "%d", h->key);
+ handle_str[sizeof(handle_str)-1] = '\0';
+ handle_str[2] = strlen(handle_str+3);
+ ngp = strstr(ngp, handle_str);
+ if(!ngp){
+ ngp = *(st->text_lines);
+ if (!ngp)
+ return 1;
+ }
+ ngname = ngp + strlen(handle_str);
+ while (strncmp(ngp, " ", 4) && !(*ngp == '\n')
+ && !(ngp == *(st->text_lines)))
+ ngp--;
+ if (strncmp(ngp, " ", 4))
+ return 1;
+ while(ngp+4 != ngname && *ngp){
+ ngp[0] = ngp[4];
+ ngp++;
+ }
+
+ if(folder_entry(h->h.f.index, FOLDERS(context))->subscribed){
+ ngp[0] = 'S';
+ ngp[1] = 'U';
+ ngp[2] = 'B';
+ }
+ else{
+ ngp[0] = '[';
+ ngp[1] = ' ';
+ ngp[2] = ']';
+ }
+ ngp[3] = ' ';
+ }
+
+ return 0;
+}
+
+
+
+/*----------------------------------------------------------------------
+ Search the set scrolling text
+
+ Args: start_line -- line to start searching on
+ start_index -- column to start searching at in first line
+ word -- string to search for
+ cursor_pos -- position of cursor is returned to caller here
+ (Actually, this isn't really the position of the
+ cursor because we don't know where we are on the
+ screen. So row is set to the line number and col
+ is set to the right column.)
+ offset_in_line -- Offset where match was found.
+
+ Returns: the line the word was found on, or -2 if it wasn't found, or
+ -3 if the only match is at column start_index - 1.
+
+ ----*/
+int
+search_scroll_text(long int start_line, int start_index, char *word,
+ Pos *cursor_pos, int *offset_in_line)
+{
+ SCRLCTRL_S *st = scroll_state(SS_CUR);
+ char *wh;
+ long l, offset, dlines;
+#define SROW(N) ((N) - offset)
+#define SLINE(N) st->text_lines[SROW(N)]
+#define SLEN(N) st->line_lengths[SROW(N)]
+
+ dlines = PGSIZE(st);
+ offset = (st->parms->text.src == FileStar) ? st->top_text_line : 0;
+
+ if(start_line < st->num_lines){
+ /* search first line starting at position start_index in */
+ if((wh = search_scroll_line(SLINE(start_line) + start_index,
+ word,
+ SLEN(start_line) - start_index,
+ st->parms->text.handles != NULL)) != NULL){
+ cursor_pos->row = start_line;
+ cursor_pos->col = scroll_handle_column(SROW(start_line),
+ *offset_in_line = wh - SLINE(start_line));
+ return(start_line);
+ }
+
+ if(st->parms->text.src == FileStar)
+ offset++;
+
+ for(l = start_line + 1; l < st->num_lines; l++) {
+ if(st->parms->text.src == FileStar && l > offset + dlines)
+ ScrollFile(offset += dlines);
+
+ if((wh = search_scroll_line(SLINE(l), word, SLEN(l),
+ st->parms->text.handles != NULL)) != NULL){
+ cursor_pos->row = l;
+ cursor_pos->col = scroll_handle_column(SROW(l),
+ *offset_in_line = wh - SLINE(l));
+ return(l);
+ }
+ }
+ }
+ else
+ start_line = st->num_lines;
+
+ if(st->parms->text.src == FileStar) /* wrap offset */
+ ScrollFile(offset = 0);
+
+ for(l = 0; l < start_line; l++) {
+ if(st->parms->text.src == FileStar && l > offset + dlines)
+ ScrollFile(offset += dlines);
+
+ if((wh = search_scroll_line(SLINE(l), word, SLEN(l),
+ st->parms->text.handles != NULL)) != NULL){
+ cursor_pos->row = l;
+ cursor_pos->col = scroll_handle_column(SROW(l),
+ *offset_in_line = wh - SLINE(l));
+ return(l);
+ }
+ }
+
+ /* search in current line */
+ if(start_line < st->num_lines
+ && (wh = search_scroll_line(SLINE(start_line), word,
+ start_index + strlen(word) - 2,
+ st->parms->text.handles != NULL)) != NULL){
+ cursor_pos->row = start_line;
+ cursor_pos->col = scroll_handle_column(SROW(start_line),
+ *offset_in_line = wh - SLINE(start_line));
+
+ return(start_line);
+ }
+
+ /* see if the only match is a repeat */
+ if(start_index > 0 && start_line < st->num_lines
+ && (wh = search_scroll_line(
+ SLINE(start_line) + start_index - 1,
+ word, strlen(word),
+ st->parms->text.handles != NULL)) != NULL){
+ cursor_pos->row = start_line;
+ cursor_pos->col = scroll_handle_column(SROW(start_line),
+ *offset_in_line = wh - SLINE(start_line));
+ return(-3);
+ }
+
+ return(-2);
+}
+
+
+/*----------------------------------------------------------------------
+ Search one line of scroll text for given string
+
+ Args: haystack -- The string to search in, the larger string
+ needle -- The string to search for, the smaller string
+ n -- The max number of chars in haystack to search
+
+ Search for first occurrence of needle in the haystack, and return a pointer
+ into the string haystack when it is found. The search is case independent.
+ ----*/
+char *
+search_scroll_line(char *haystack, char *needle, int n, int handles)
+{
+ char *return_ptr = NULL, *found_it = NULL;
+ char *haystack_copy, *p, *free_this = NULL, *end;
+ char buf[1000];
+ int state = 0, i = 0;
+
+ if(n > 0 && haystack){
+ if(n < sizeof(buf))
+ haystack_copy = buf;
+ else
+ haystack_copy = free_this = (char *) fs_get((n+1) * sizeof(char));
+
+ strncpy(haystack_copy, haystack, n);
+ haystack_copy[n] = '\0';
+
+ /*
+ * We don't want to match text inside embedded tags.
+ * Replace embedded octets with nulls and convert
+ * uppercase ascii to lowercase. We should also do
+ * some sort of canonicalization of UTF-8 but that
+ * sounds daunting.
+ */
+ for(i = n, p = haystack_copy; i-- > 0 && *p; p++){
+ if(handles)
+ switch(state){
+ case 0 :
+ if(*p == TAG_EMBED){
+ *p = '\0';
+ state = -1;
+ continue;
+ }
+ else{
+ /* lower case just ascii chars */
+ if(!(*p & 0x80) && isupper(*p))
+ *p = tolower(*p);
+ }
+
+ break;
+
+ case -1 :
+ state = (*p == TAG_HANDLE)
+ ? -2
+ : (*p == TAG_FGCOLOR || *p == TAG_BGCOLOR) ? RGBLEN : 0;
+ *p = '\0';
+ continue;
+
+ case -2 :
+ state = *p; /* length of handle's key */
+ *p = '\0';
+ continue;
+
+ default :
+ state--;
+ *p = '\0';
+ continue;
+ }
+ }
+
+ /*
+ * The haystack_copy string now looks like
+ *
+ * "chars\0\0\0\0\0\0chars...\0\0\0chars... \0"
+ *
+ * with that final \0 at haystack_copy[n].
+ * Search each piece one at a time.
+ */
+
+ end = haystack_copy + n;
+ p = haystack_copy;
+
+ while(p < end && !return_ptr){
+
+ /* skip nulls */
+ while(*p == '\0' && p < end)
+ p++;
+
+ if(*p != '\0')
+ found_it = srchstr(p, needle);
+
+ if(found_it){
+ /* found it, make result relative to haystack */
+ return_ptr = haystack + (found_it - haystack_copy);
+ }
+
+ /* skip to next null */
+ while(*p != '\0' && p < end)
+ p++;
+ }
+
+ if(free_this)
+ fs_give((void **) &free_this);
+ }
+
+ return(return_ptr);
+}
+
+
+/*
+ * Returns the number of columns taken up by the visible part of the line.
+ * That is, account for handles and color changes and so forth.
+ */
+int
+visible_linelen(int line)
+{
+ SCRLCTRL_S *st = scroll_state(SS_CUR);
+ int i, n, len = 0;
+
+ if(line < 0 || line >= st->num_lines)
+ return(len);
+
+ for(i = 0, len = 0; i < st->line_lengths[line];)
+ switch(st->text_lines[line][i]){
+
+ case TAG_EMBED:
+ i++;
+ switch((i < st->line_lengths[line]) ? st->text_lines[line][i] : 0){
+ case TAG_HANDLE:
+ i++;
+ /* skip the length byte plus <length> more bytes */
+ if(i < st->line_lengths[line]){
+ n = st->text_lines[line][i];
+ i++;
+ }
+
+ if(i < st->line_lengths[line] && n > 0)
+ i += n;
+
+ break;
+
+ case TAG_FGCOLOR :
+ case TAG_BGCOLOR :
+ i += (RGBLEN + 1); /* 1 for TAG, RGBLEN for color */
+ break;
+
+ case TAG_INVON:
+ case TAG_INVOFF:
+ case TAG_BOLDON:
+ case TAG_BOLDOFF:
+ case TAG_ULINEON:
+ case TAG_ULINEOFF:
+ i++;
+ break;
+
+ case TAG_EMBED: /* escaped embed character */
+ i++;
+ len++;
+ break;
+
+ default: /* the embed char was literal */
+ i++;
+ len += 2;
+ break;
+ }
+
+ break;
+
+ case TAB:
+ i++;
+ while(((++len) & 0x07) != 0) /* add tab's spaces */
+ ;
+
+ break;
+
+ default:
+ i++;
+ len++;
+ break;
+ }
+
+ return(len);
+}
+
+
+/*----------------------------------------------------------------------
+ Display the contents of the given file (likely output from some command)
+
+ Args: filename -- name of file containing output
+ title -- title to be used for screen displaying output
+ alt_msg -- if no output, Q this message instead of the default
+ mode -- non-zero to display short files in status line
+ Returns: none
+ ----*/
+void
+display_output_file(char *filename, char *title, char *alt_msg, int mode)
+{
+ STORE_S *in_file = NULL, *out_store = NULL;
+
+ if((in_file = so_get(FileStar, filename, READ_ACCESS|READ_FROM_LOCALE))){
+ if(mode == DOF_BRIEF){
+ int msg_q = 0, i = 0;
+ char buf[512], *msg_p[4];
+#define MAX_SINGLE_MSG_LEN 60
+
+ buf[0] = '\0';
+ msg_p[0] = buf;
+
+ /*
+ * Might need to do something about CRLFs for Windows.
+ */
+ while(so_fgets(in_file, msg_p[msg_q], sizeof(buf) - (msg_p[msg_q] - buf))
+ && msg_q < 3
+ && (i = strlen(msg_p[msg_q])) < MAX_SINGLE_MSG_LEN){
+ msg_p[msg_q+1] = msg_p[msg_q]+strlen(msg_p[msg_q]);
+ if (*(msg_p[++msg_q] - 1) == '\n')
+ *(msg_p[msg_q] - 1) = '\0';
+ }
+
+ if(msg_q < 3 && i < MAX_SINGLE_MSG_LEN){
+ if(*msg_p[0])
+ for(i = 0; i < msg_q; i++)
+ q_status_message2(SM_ORDER, 3, 4,
+ "%s Result: %s", title, msg_p[i]);
+ else
+ q_status_message2(SM_ORDER, 0, 4, "%s%s", title,
+ alt_msg
+ ? alt_msg
+ : " command completed with no output");
+
+ so_give(&in_file);
+ in_file = NULL;
+ }
+ }
+ else if(mode == DOF_EMPTY){
+ unsigned char c;
+
+ if(so_readc(&c, in_file) < 1){
+ q_status_message2(SM_ORDER, 0, 4, "%s%s", title,
+ alt_msg
+ ? alt_msg
+ : " command completed with no output");
+ so_give(&in_file);
+ in_file = NULL;
+ }
+ }
+
+ /*
+ * We need to translate the file contents from the user's locale
+ * charset to UTF-8 for use in scrolltool. We get that translation
+ * from the READ_FROM_LOCALE in the in_file storage object.
+ * It would be nice to skip this step but scrolltool doesn't use
+ * the storage object routines to read from the file, so would
+ * skip the translation step.
+ */
+ if(in_file){
+ char *errstr;
+ gf_io_t gc, pc;
+
+ if(!(out_store = so_get(CharStar, NULL, EDIT_ACCESS))){
+ so_give(&in_file);
+ our_unlink(filename);
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("Error allocating space."));
+ return;
+ }
+
+ so_seek(in_file, 0L, 0);
+
+ gf_filter_init();
+
+ gf_link_filter(gf_wrap,
+ gf_wrap_filter_opt(ps_global->ttyo->screen_cols - 4,
+ ps_global->ttyo->screen_cols,
+ NULL, 0, GFW_NONE));
+
+ gf_set_so_readc(&gc, in_file);
+ gf_set_so_writec(&pc, out_store);
+
+ if((errstr = gf_pipe(gc, pc)) != NULL){
+ so_give(&in_file);
+ so_give(&out_store);
+ our_unlink(filename);
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("Error allocating space."));
+ return;
+ }
+
+ gf_clear_so_writec(out_store);
+ gf_clear_so_readc(in_file);
+ so_give(&in_file);
+ }
+
+ if(out_store){
+ SCROLL_S sargs;
+ char title_buf[64];
+
+ snprintf(title_buf, sizeof(title_buf), "HELP FOR %s VIEW", title);
+ title_buf[sizeof(title_buf)-1] = '\0';
+
+ memset(&sargs, 0, sizeof(SCROLL_S));
+ sargs.text.text = so_text(out_store);
+ sargs.text.src = CharStar;
+ sargs.text.desc = "output";
+ sargs.bar.title = title;
+ sargs.bar.style = TextPercent;
+ sargs.help.text = h_simple_text_view;
+ sargs.help.title = title_buf;
+ scrolltool(&sargs);
+ ps_global->mangled_screen = 1;
+ so_give(&out_store);
+ }
+
+ our_unlink(filename);
+ }
+ else
+ dprint((2, "Error reopening %s to get results: %s\n",
+ filename ? filename : "?", error_description(errno)));
+}
+
+
+/*--------------------------------------------------------------------
+ Call the function that will perform the double click operation
+
+ Returns: the current top line
+--------*/
+long
+doubleclick_handle(SCROLL_S *sparms, HANDLE_S *next_handle, int *cmd, int *done)
+{
+ if(sparms->mouse.clickclick){
+ if((*sparms->mouse.clickclick)(sparms))
+ *done = 1;
+ }
+ else
+ switch(scroll_handle_launch(next_handle, TRUE)){
+ case 1 :
+ *cmd = MC_EXIT; /* propagate */
+ *done = 1;
+ break;
+
+ case -1 :
+ cmd_cancelled("View");
+ break;
+
+ default :
+ break;
+ }
+
+ return(scroll_state(SS_CUR)->top_text_line);
+}
+
+
+
+#ifdef _WINDOWS
+/*
+ * Just a little something to simplify assignments
+ */
+#define VIEWPOPUP(p, c, s) { \
+ (p)->type = tQueue; \
+ (p)->data.val = c; \
+ (p)->label.style = lNormal; \
+ (p)->label.string = s; \
+ }
+
+
+/*
+ *
+ */
+int
+format_message_popup(sparms, in_handle)
+ SCROLL_S *sparms;
+ int in_handle;
+{
+ MPopup fmp_menu[32];
+ HANDLE_S *h = NULL;
+ int i = -1, n;
+ long rawno;
+ MESSAGECACHE *mc;
+
+ /* Reason to offer per message ops? */
+ if(mn_get_total(ps_global->msgmap) > 0L){
+ if(in_handle){
+ SCRLCTRL_S *st = scroll_state(SS_CUR);
+
+ switch((h = get_handle(st->parms->text.handles, in_handle))->type){
+ case Attach :
+ fmp_menu[++i].type = tIndex;
+ fmp_menu[i].label.string = "View Attachment";
+ fmp_menu[i].label.style = lNormal;
+ fmp_menu[i].data.val = 'X'; /* for local use */
+
+ if(h->h.attach
+ && dispatch_attachment(h->h.attach) != MCD_NONE
+ && !(h->h.attach->can_display & MCD_EXTERNAL)
+ && h->h.attach->body
+ && (h->h.attach->body->type == TYPETEXT
+ || (h->h.attach->body->type == TYPEMESSAGE
+ && h->h.attach->body->subtype
+ && !strucmp(h->h.attach->body->subtype,"rfc822")))){
+ fmp_menu[++i].type = tIndex;
+ fmp_menu[i].label.string = "View Attachment in New Window";
+ fmp_menu[i].label.style = lNormal;
+ fmp_menu[i].data.val = 'Y'; /* for local use */
+ }
+
+ fmp_menu[++i].type = tIndex;
+ fmp_menu[i].label.style = lNormal;
+ fmp_menu[i].data.val = 'Z'; /* for local use */
+ msgno_exceptions(ps_global->mail_stream,
+ mn_m2raw(ps_global->msgmap,
+ mn_get_cur(ps_global->msgmap)),
+ h->h.attach->number, &n, FALSE);
+ fmp_menu[i].label.string = (n & MSG_EX_DELETE)
+ ? "Undelete Attachment"
+ : "Delete Attachment";
+ break;
+
+ case URL :
+ default :
+ fmp_menu[++i].type = tIndex;
+ fmp_menu[i].label.string = "View Link";
+ fmp_menu[i].label.style = lNormal;
+ fmp_menu[i].data.val = 'X'; /* for local use */
+
+ fmp_menu[++i].type = tIndex;
+ fmp_menu[i].label.string = "Copy Link";
+ fmp_menu[i].label.style = lNormal;
+ fmp_menu[i].data.val = 'W'; /* for local use */
+ break;
+ }
+
+ fmp_menu[++i].type = tSeparator;
+ }
+
+ /* Delete or Undelete? That is the question. */
+ fmp_menu[++i].type = tQueue;
+ fmp_menu[i].label.style = lNormal;
+ mc = ((rawno = mn_m2raw(ps_global->msgmap,
+ mn_get_cur(ps_global->msgmap))) > 0L
+ && ps_global->mail_stream
+ && rawno <= ps_global->mail_stream->nmsgs)
+ ? mail_elt(ps_global->mail_stream, rawno) : NULL;
+ if(mc && mc->deleted){
+ fmp_menu[i].data.val = 'U';
+ fmp_menu[i].label.string = in_handle
+ ? "&Undelete Message" : "&Undelete";
+ }
+ else{
+ fmp_menu[i].data.val = 'D';
+ fmp_menu[i].label.string = in_handle
+ ? "&Delete Message" : "&Delete";
+ }
+
+ if(F_ON(F_ENABLE_FLAG, ps_global)){
+ fmp_menu[++i].type = tSubMenu;
+ fmp_menu[i].label.string = "Flag";
+ fmp_menu[i].data.submenu = flag_submenu(mc);
+ }
+
+ i++;
+ VIEWPOPUP(&fmp_menu[i], 'S', in_handle ? "&Save Message" : "&Save");
+
+ i++;
+ VIEWPOPUP(&fmp_menu[i], 'E', in_handle ? "&Export Message" : "&Export");
+
+ i++;
+ VIEWPOPUP(&fmp_menu[i], '%', in_handle ? "Print Message" : "Print");
+
+ i++;
+ VIEWPOPUP(&fmp_menu[i], 'R',
+ in_handle ? "&Reply to Message" : "&Reply");
+
+ i++;
+ VIEWPOPUP(&fmp_menu[i], 'F',
+ in_handle ? "&Forward Message" : "&Forward");
+
+ i++;
+ VIEWPOPUP(&fmp_menu[i], 'B',
+ in_handle ? "&Bounce Message" : "&Bounce");
+
+ i++;
+ VIEWPOPUP(&fmp_menu[i], 'T', "&Take Addresses");
+
+ fmp_menu[++i].type = tSeparator;
+
+ if(mn_get_cur(ps_global->msgmap) < mn_get_total(ps_global->msgmap)){
+ i++;
+ VIEWPOPUP(&fmp_menu[i], 'N', "View &Next Message");
+ }
+
+ if(mn_get_cur(ps_global->msgmap) > 0){
+ i++;
+ VIEWPOPUP(&fmp_menu[i], 'P', "View &Prev Message");
+ }
+
+ if(mn_get_cur(ps_global->msgmap) < mn_get_total(ps_global->msgmap)
+ || mn_get_cur(ps_global->msgmap) > 0)
+ fmp_menu[++i].type = tSeparator;
+
+ /* Offer the attachment screen? */
+ for(n = 0; ps_global->atmts && ps_global->atmts[n].description; n++)
+ ;
+
+ if(n > 1){
+ i++;
+ VIEWPOPUP(&fmp_menu[i], 'V', "&View Attachment Index");
+ }
+ }
+
+ i++;
+ VIEWPOPUP(&fmp_menu[i], 'I', "Message &Index");
+
+ i++;
+ VIEWPOPUP(&fmp_menu[i], 'M', "&Main Menu");
+
+ fmp_menu[++i].type = tTail;
+
+ if((i = mswin_popup(fmp_menu)) >= 0 && in_handle)
+ switch(fmp_menu[i].data.val){
+ case 'W' : /* Copy URL to clipboard */
+ mswin_addclipboard(h->h.url.path);
+ break;
+
+ case 'X' :
+ return(1); /* return like the user double-clicked */
+
+ break;
+
+ case 'Y' : /* popup the thing in another window */
+ display_att_window(h->h.attach);
+ break;
+
+ case 'Z' :
+ if(h && h->type == Attach){
+ msgno_exceptions(ps_global->mail_stream,
+ mn_m2raw(ps_global->msgmap,
+ mn_get_cur(ps_global->msgmap)),
+ h->h.attach->number, &n, FALSE);
+ n ^= MSG_EX_DELETE;
+ msgno_exceptions(ps_global->mail_stream,
+ mn_m2raw(ps_global->msgmap,
+ mn_get_cur(ps_global->msgmap)),
+ h->h.attach->number, &n, TRUE);
+ q_status_message2(SM_ORDER, 0, 3, "Attachment %s %s!",
+ h->h.attach->number,
+ (n & MSG_EX_DELETE) ? "deleted" : "undeleted");
+
+ return(2);
+ }
+
+ break;
+
+ default :
+ break;
+ }
+
+ return(0);
+}
+
+
+
+/*
+ *
+ */
+int
+simple_text_popup(sparms, in_handle)
+ SCROLL_S *sparms;
+ int in_handle;
+{
+ MPopup simple_menu[12];
+ int n = 0;
+
+ VIEWPOPUP(&simple_menu[n], '%', "Print");
+ n++;
+
+ VIEWPOPUP(&simple_menu[n], 'S', "Save");
+ n++;
+
+ VIEWPOPUP(&simple_menu[n], 'F', "Forward in Email");
+ n++;
+
+ simple_menu[n++].type = tSeparator;
+
+ VIEWPOPUP(&simple_menu[n], 'E', "Exit Viewer");
+ n++;
+
+ simple_menu[n].type = tTail;
+
+ (void) mswin_popup(simple_menu);
+ return(0);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Return characters in scroll tool buffer serially
+
+ Args: n -- index of char to return
+
+ Returns: returns the character at index 'n', or -1 on error or
+ end of buffer.
+
+ ----*/
+int
+mswin_readscrollbuf(n)
+ int n;
+{
+ SCRLCTRL_S *st = scroll_state(SS_CUR);
+ int c;
+ static char **orig = NULL, **l, *p;
+ static int lastn;
+
+ if(!st)
+ return(-1);
+
+ /*
+ * All of these are mind-numbingly slow at the moment...
+ */
+ switch(st->parms->text.src){
+ case CharStar :
+ return((n >= strlen((char *)st->parms->text.text))
+ ? -1 : ((char *)st->parms->text.text)[n]);
+
+ case CharStarStar :
+ /* BUG? is this test rigorous enough? */
+ if(orig != (char **)st->parms->text.text || n < lastn){
+ lastn = n;
+ if(orig = l = (char **)st->parms->text.text) /* reset l and p */
+ p = *l;
+ }
+ else{ /* use cached l and p */
+ c = n; /* and adjust n */
+ n -= lastn;
+ lastn = c;
+ }
+
+ while(l){ /* look for 'n' on each line */
+ for(; n && *p; n--, p++)
+ ;
+
+ if(n--) /* 'n' found ? */
+ p = *++l;
+ else
+ break;
+ }
+
+ return((l && *l) ? *p ? *p : '\n' : -1);
+
+ case FileStar :
+ return((fseek((FILE *)st->parms->text.text, (long) n, 0) < 0
+ || (c = fgetc((FILE *)st->parms->text.text)) == EOF) ? -1 : c);
+
+ default:
+ return(-1);
+ }
+}
+
+
+
+/*----------------------------------------------------------------------
+ MSWin scroll callback. Called during scroll message processing.
+
+
+
+ Args: cmd - what type of scroll operation.
+ scroll_pos - paramter for operation.
+ used as position for SCROLL_TO operation.
+
+ Returns: TRUE - did the scroll operation.
+ FALSE - was not able to do the scroll operation.
+ ----*/
+int
+pcpine_do_scroll (cmd, scroll_pos)
+int cmd;
+long scroll_pos;
+{
+ SCRLCTRL_S *st = scroll_state(SS_CUR);
+ HANDLE_S *next_handle;
+ int paint = FALSE;
+ int num_display_lines;
+ int scroll_lines;
+ char message[64];
+ long maxscroll;
+
+
+ message[0] = '\0';
+ maxscroll = st->num_lines;
+ switch (cmd) {
+ case MSWIN_KEY_SCROLLUPLINE:
+ if(st->top_text_line > 0) {
+ st->top_text_line -= (int) scroll_pos;
+ paint = TRUE;
+ if (st->top_text_line <= 0){
+ snprintf(message, sizeof(message), "START of %.*s",
+ 32, STYLE_NAME(st->parms));
+ message[sizeof(message)-1] = '\0';
+ st->top_text_line = 0;
+ }
+ }
+ break;
+
+ case MSWIN_KEY_SCROLLDOWNLINE:
+ if(st->top_text_line < maxscroll) {
+ st->top_text_line += (int) scroll_pos;
+ paint = TRUE;
+ if (st->top_text_line >= maxscroll){
+ snprintf(message, sizeof(message), "END of %.*s", 32, STYLE_NAME(st->parms));
+ message[sizeof(message)-1] = '\0';
+ st->top_text_line = maxscroll;
+ }
+ }
+ break;
+
+ case MSWIN_KEY_SCROLLUPPAGE:
+ if(st->top_text_line > 0) {
+ num_display_lines = SCROLL_LINES(ps_global);
+ scroll_lines = MIN(MAX(num_display_lines -
+ ps_global->viewer_overlap, 1), num_display_lines);
+ if (st->top_text_line > scroll_lines)
+ st->top_text_line -= scroll_lines;
+ else {
+ st->top_text_line = 0;
+ snprintf(message, sizeof(message), "START of %.*s", 32, STYLE_NAME(st->parms));
+ message[sizeof(message)-1] = '\0';
+ }
+ paint = TRUE;
+ }
+ break;
+
+ case MSWIN_KEY_SCROLLDOWNPAGE:
+ num_display_lines = SCROLL_LINES(ps_global);
+ if(st->top_text_line < maxscroll) {
+ scroll_lines = MIN(MAX(num_display_lines -
+ ps_global->viewer_overlap, 1), num_display_lines);
+ st->top_text_line += scroll_lines;
+ if (st->top_text_line >= maxscroll) {
+ st->top_text_line = maxscroll;
+ snprintf(message, sizeof(message), "END of %.*s", 32, STYLE_NAME(st->parms));
+ message[sizeof(message)-1] = '\0';
+ }
+ paint = TRUE;
+ }
+ break;
+
+ case MSWIN_KEY_SCROLLTO:
+ if (st->top_text_line != scroll_pos) {
+ st->top_text_line = scroll_pos;
+ if (st->top_text_line == 0)
+ snprintf(message, sizeof(message), "START of %.*s", 32, STYLE_NAME(st->parms));
+ else if(st->top_text_line >= maxscroll)
+ snprintf(message, sizeof(message), "END of %.*s", 32, STYLE_NAME(st->parms));
+
+ message[sizeof(message)-1] = '\0';
+ paint = TRUE;
+ }
+ break;
+ }
+
+ /* Need to frame some handles? */
+ if(st->parms->text.handles
+ && (next_handle = scroll_handle_in_frame(st->top_text_line)))
+ st->parms->text.handles = next_handle;
+
+ if (paint) {
+ mswin_beginupdate();
+ update_scroll_titlebar(st->top_text_line, 0);
+ (void) scroll_scroll_text(st->top_text_line,
+ st->parms->text.handles, 1);
+ if (message[0])
+ q_status_message(SM_INFO, 0, 1, message);
+
+ /* Display is always called so that the "START(END) of message"
+ * message gets removed when no longer at the start(end). */
+ display_message (KEY_PGDN);
+ mswin_endupdate();
+ }
+
+ return (TRUE);
+}
+
+
+char *
+pcpine_help_scroll(title)
+ char *title;
+{
+ SCRLCTRL_S *st = scroll_state(SS_CUR);
+
+ if(title)
+ strncpy(title, (st->parms->help.title)
+ ? st->parms->help.title : "Alpine Help", 256);
+
+ return(pcpine_help(st->parms->help.text));
+}
+
+
+int
+pcpine_view_cursor(col, row)
+ int col;
+ long row;
+{
+ SCRLCTRL_S *st = scroll_state(SS_CUR);
+ int key;
+ long line;
+
+ return((row >= HEADER_ROWS(ps_global)
+ && row < HEADER_ROWS(ps_global) + SCROLL_LINES(ps_global)
+ && (line = (row - 2) + st->top_text_line) < st->num_lines
+ && (key = dot_on_handle(line, col))
+ && scroll_handle_selectable(get_handle(st->parms->text.handles,key)))
+ ? MSWIN_CURSOR_HAND
+ : MSWIN_CURSOR_ARROW);
+}
+#endif /* _WINDOWS */
diff --git a/alpine/mailview.h b/alpine/mailview.h
new file mode 100644
index 00000000..1a404a38
--- /dev/null
+++ b/alpine/mailview.h
@@ -0,0 +1,129 @@
+/*
+ * $Id: mailview.h 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_MAILVIEW_INCLUDED
+#define PINE_MAILVIEW_INCLUDED
+
+
+#include <general.h>
+#include "../pith/mailview.h"
+#include "titlebar.h"
+#include "keymenu.h"
+#include "../pith/handle.h"
+#include "../pith/store.h"
+#include "../pith/bitmap.h"
+
+
+/*
+ * display_output_file mode flags
+ */
+#define DOF_NONE 0
+#define DOF_EMPTY 1
+#define DOF_BRIEF 2
+
+
+#define STYLE_NAME(s) ((s)->text.desc ? (s)->text.desc : "text")
+
+
+/*
+ * Struct defining scrolltool operating parameters.
+ */
+typedef struct scrolltool_s {
+ struct { /* Data and its attributes to scroll */
+ void *text; /* what to scroll */
+ SourceType src; /* it's form (char **,char *,FILE *) */
+ char *desc; /* Description of "type" of data shown */
+ HANDLE_S *handles; /* embedded data descriptions */
+ } text;
+ struct { /* titlebar state */
+ char *title; /* screen's title */
+ TitleBarType style; /* it's type */
+ COLOR_PAIR *color;
+ } bar;
+ struct { /* screen's keymenu/command bindings */
+ struct key_menu *menu;
+ bitmap_t bitmap;
+ OtherMenu what;
+ void (*each_cmd)(struct scrolltool_s *, int);
+ } keys;
+ struct { /* help for this attachment */
+ HelpType text; /* help text */
+ char *title; /* title for help screen */
+ } help;
+ struct {
+ int (*click)(struct scrolltool_s *);
+ int (*clickclick)(struct scrolltool_s *);
+#ifdef _WINDOWS
+ /*
+ * For systems that support it, allow caller to do popup menu
+ */
+ int (*popup)(struct scrolltool_s *, int);
+#endif
+ } mouse;
+ struct { /* where to start paging from */
+ enum {FirstPage = 0, LastPage, Fragment, Offset, Handle} on;
+ union {
+ char *frag; /* fragment in html text to start on */
+ long offset;
+ } loc;
+ } start;
+ struct { /* Non-default Command Processor */
+ int (*tool)(int, MSGNO_S *, struct scrolltool_s *);
+ /* The union below is opaque as far as scrolltool itself is
+ * concerned, but is provided as a container to pass data
+ * between the scrolltool caller and the given "handler"
+ * callback (or any other callback that takes a scrolltool_s *).
+ */
+ union {
+ void *p;
+ int i;
+ } data;
+ } proc;
+ /* End of page processing */
+ int (*end_scroll)(struct scrolltool_s *);
+ /* Handler for invalid command input */
+ int (*bogus_input)(UCS);
+ unsigned resize_exit:1; /* Return from scrolltool if resized */
+ unsigned body_valid:1; /* Screen's body already displayed */
+ unsigned no_stat_msg:1; /* Don't display status messages */
+ unsigned vert_handle:1; /* hunt up and down on arrows/ctrl-[np] */
+ unsigned srch_handle:1; /* search to next handle */
+ unsigned quell_help:1; /* Don't show handle nav help message */
+ unsigned quell_newmail:1; /* Don't check for new mail */
+ unsigned quell_first_view:1; /* Don't act special first time through */
+ unsigned jump_is_debug:1;
+ unsigned use_indexline_color:1;
+} SCROLL_S;
+
+
+/* exported protoypes */
+void mail_view_screen(struct pine *);
+url_tool_t url_local_handler(char *);
+int url_local_mailto(char *);
+int url_local_mailto_and_atts(char *, PATMT *);
+int url_local_fragment(char *);
+int scrolltool(SCROLL_S *);
+int ng_scroll_edit(CONTEXT_S *, int);
+int folder_select_update(CONTEXT_S *, int);
+int scroll_add_listmode(CONTEXT_S *, int);
+void display_output_file(char *, char *, char *, int);
+int rfc2369_editorial(long, HANDLE_S **, int, int, gf_io_t);
+void view_writec_init(STORE_S *, HANDLE_S **, int, int);
+void view_writec_destroy(void);
+int view_writec(int);
+
+
+#endif /* PINE_MAILVIEW_INCLUDED */
diff --git a/alpine/makefile.wnt b/alpine/makefile.wnt
new file mode 100644
index 00000000..db2e4707
--- /dev/null
+++ b/alpine/makefile.wnt
@@ -0,0 +1,89 @@
+# $Id: makefile.wnt 14098 2005-10-03 18:54:13Z jpf@u.washington.edu $
+#
+# ========================================================================
+# Copyright 2006-2007 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+#
+#
+# Makefile for WIN NT version of the libalpine.lib and pine.exe
+#
+#
+CC=cl
+RM=del
+CP=copy
+RC=rc
+
+#includes symbol info for debugging
+CDEBUG= #-Zi -Od
+LDEBUG= /DEBUG /DEBUGTYPE:CV
+
+STDCFLAGS= -I..\include -I..\regex -nologo -MT -DWIN32 -DDOS -D_WINDOWS -DMSC_MALLOC
+
+CFLAGS= $(CDEBUG) $(STDCFLAGS) $(NET) $(EXTRACFLAGS)
+
+LFLAGS= $(LDEBUG) $(EXTRALDFLAGS)
+
+RCFLAGS =
+
+STDLIBS=oldnames.lib kernel32.lib advapi32.lib ws2_32.lib user32.lib gdi32.lib \
+ shell32.lib comdlg32.lib winmm.lib crypt32.lib
+SPELLLIBS=
+
+LIBER=lib
+LIBARGS=/nologo /verbose
+
+HFILES= ../include/system.h ../include/general.h \
+ addrbook.h adrbkcmd.h after.h alpine.h arg.h busy.h colorconf.h confscroll.h \
+ conftype.h context.h dispfilt.h flagmaint.h folder.h headers.h help.h imap.h \
+ init.h kblock.h keymenu.h ldapconf.h listsel.h mailcmd.h mailindx.h mailpart.h \
+ mailview.h newmail.h newuser.h pattern.h pipe.h print.h radio.h remote.h \
+ reply.h roleconf.h \
+ send.h setup.h signal.h status.h takeaddr.h talk.h titlebar.h
+
+OFILES= addrbook.obj adrbkcmd.obj after.obj alpine.obj arg.obj busy.obj colorconf.obj \
+ confscroll.obj context.obj dispfilt.obj flagmaint.obj folder.obj help.obj \
+ imap.obj init.obj kblock.obj keymenu.obj ldapconf.obj listsel.obj mailcmd.obj \
+ mailindx.obj mailpart.obj mailview.obj newmail.obj \
+ newuser.obj pattern.obj pipe.obj \
+ print.obj radio.obj remote.obj reply.obj roleconf.obj \
+ send.obj setup.obj signal.obj status.obj takeaddr.obj titlebar.obj
+
+OURLIBS= ../c-client/cclient.lib osdep/libalpineosd.lib osdep/mswin.res \
+ ../pith/osdep/libpithosd.lib ../pith/charconv/libpithcc.lib ../regex/libregex.lib ../pith/libpith.lib \
+ ../pico/osdep/libpicoosd.lib ../pico/libpico.lib
+
+LIBS= $(STDLIBS) $(LDAPLIBS) $(SPELLLIBS) $(OURLIBS) $(EXTRALIBES)
+
+all: alpine.exe rpload.exe rpdump.exe
+
+.c.obj:
+ $(CC) -c $(CFLAGS) "$(MAKEDIR)"\$*.c
+
+$(OFILES): $(HFILES)
+
+libalpine.lib: $(OFILES)
+ $(RM) libalpine.lib || rem
+ $(LIBER) /out:libalpine.lib $(OFILES)
+
+rpload.exe: rpload.obj ..\c-client\cclient.lib
+ link /subsystem:console /out:rpload.exe $(LFLAGS) rpload.obj bdate.obj $(LIBS)
+
+rpdump.exe: rpdump.obj ..\c-client\cclient.lib
+ link /subsystem:console /out:rpdump.exe $(LFLAGS) rpdump.obj bdate.obj $(LIBS)
+
+alpine.exe: $(OFILES) $(OURLIBS)
+ ..\pico\blddate > bdate.c
+ $(CC) /c $(CFLAGS) bdate.c
+ link /subsystem:windows /entry:wWinMainCRTStartup /out:alpine.exe $(LFLAGS) bdate.obj $(OFILES) $(LIBS)
+
+clean:
+ $(RM) *.lib
+ $(RM) *.obj
diff --git a/alpine/newmail.c b/alpine/newmail.c
new file mode 100644
index 00000000..ba1dbda9
--- /dev/null
+++ b/alpine/newmail.c
@@ -0,0 +1,333 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: newmail.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2009 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/newmail.h"
+#include "../pith/conf.h"
+#include "../pith/flag.h"
+#include "../pith/mailindx.h"
+#include "../pith/msgno.h"
+#include "../pith/bldaddr.h"
+#include "../pith/stream.h"
+#include "../pith/sort.h"
+#include "../pith/status.h"
+#include "../pith/util.h"
+#include "../pith/thread.h"
+#include "../pith/options.h"
+#include "../pith/folder.h"
+#include "../pith/ablookup.h"
+
+#ifdef _WINDOWS
+#include "../pico/osdep/mswin.h"
+#endif
+
+
+/*
+ * Internal prototypes
+ */
+void new_mail_win_mess(MAILSTREAM *, long);
+void new_mail_mess(MAILSTREAM *, long, long, int);
+void newmailfifo(int, char *, char *, char *);
+
+
+/*----------------------------------------------------------------------
+ pith optional function to queue a newmail announcement
+
+
+ ----*/
+void
+newmail_status_message(MAILSTREAM *stream, long n, long t_nm_count)
+{
+#ifdef _WINDOWS
+ if(mswin_newmailwinon())
+ new_mail_win_mess(stream, t_nm_count);
+#elif !defined(DOS) && !defined(OS2) && !defined(LEAVEOUTFIFO)
+ if(ps_global->VAR_FIFOPATH)
+ new_mail_win_mess(stream, t_nm_count);
+#endif
+ if(n){
+ new_mail_mess(stream, sp_mail_since_cmd(stream), n, 0);
+ }
+}
+
+
+/*
+ * alert for each new message individually. new_mail_mess lumps
+ * messages together, we call new_mail_mess with 1 message at a time.
+ * This is currently for PC-Pine new mail window, but could probably
+ * be used more generally.
+ * stream - new mail stream
+ * number - number of new messages to alert for
+ */
+void
+new_mail_win_mess(MAILSTREAM *stream, long int number)
+{
+ int n, i;
+ MESSAGECACHE *mc;
+
+ if(!stream)
+ return;
+
+ /*
+ * spare6, or MN_STMP, should be safe to use for now, we
+ * just want to set which messages to alert about before
+ * going to c-client.
+ */
+ for(n = stream->nmsgs, i = 0; n > 1L && i < number; n--){
+ if(!get_lflag(stream, NULL, n, MN_EXLD)){
+ mc = mail_elt(stream, n);
+ if(mc)
+ mc->spare6 = 1;
+
+ if(++i == number)
+ break;
+ }
+ else{
+ mc = mail_elt(stream, n);
+ if(mc)
+ mc->spare6 = 0;
+ }
+ }
+ /*
+ * Here n is the first new message we want to notify about.
+ * spare6 will tell us which ones to use. We set spare6
+ * in case of new mail or expunge that could happen when
+ * we mail_fetchstructure in new_mail_mess.
+ */
+ for(; n <= stream->nmsgs; n++)
+ if(n > 0L && (mc = mail_elt(stream, n)) && mc->spare6){
+ mc->spare6 = 0;
+ new_mail_mess(stream, 1, n, 1);
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Format and queue a "new mail" message
+
+ Args: stream -- mailstream on which a mail has arrived
+ number -- number of new messages since last command
+ max_num -- The number of messages now on stream
+ for_new_mail_win -- for separate new mail window (curr. PC-Pine)
+
+ Not too much worry here about the length of the message because the
+status_message code will fit what it can on the screen and truncation on
+the right is about what we want which is what will happen.
+ ----*/
+void
+new_mail_mess(MAILSTREAM *stream, long int number, long int max_num, int for_new_mail_win)
+{
+ char subject[MAILTMPLEN+1], subjtext[MAILTMPLEN+1], from[MAILTMPLEN+1],
+ *folder = NULL, intro[MAILTMPLEN+1];
+ ENVELOPE *e = NULL;
+
+ if(stream)
+ e = pine_mail_fetchstructure(stream, max_num, NULL);
+
+ if(stream){
+ if(sp_flagged(stream, SP_INBOX))
+ folder = NULL;
+ else{
+ folder = STREAMNAME(stream);
+ if(folder[0] == '?' && folder[1] == '\0')
+ folder = NULL;
+ }
+ }
+
+ format_new_mail_msg(folder, number, e, intro, from, subject, subjtext, sizeof(subject));
+
+ if(!for_new_mail_win)
+ q_status_message5(SM_ASYNC | SM_DING, 0, 60,
+ "%s%s%s%.80s%.80s", intro,
+ from && from[0] ? ((number > 1L) ? " Most recent f" : " F") : "",
+ from && from[0] ? "rom " : "",
+ from && from[0] ? from : "",
+ subjtext);
+#if (!defined(DOS) && !defined(OS2) && !defined(LEAVEOUTFIFO)) || defined(_WINDOWS)
+ else {
+ int is_us = 0;
+ ADDRESS *tadr;
+
+ if(e)
+ for(tadr = e->to; tadr; tadr = tadr->next)
+ if(address_is_us(tadr, ps_global)){
+ is_us = 1;
+ break;
+ }
+#ifdef _WINDOWS
+ mswin_newmailwin(is_us, from, subject, folder);
+#else
+ newmailfifo(is_us, from, subject, folder);
+#endif
+ }
+#endif
+
+ if(pith_opt_icon_text){
+ if(F_ON(F_ENABLE_XTERM_NEWMAIL, ps_global)
+ && F_ON(F_ENABLE_NEWMAIL_SHORT_TEXT, ps_global)){
+ long inbox_nm;
+ if(!sp_flagged(stream, SP_INBOX)
+ && (inbox_nm = sp_mail_since_cmd(sp_inbox_stream()))){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "[%ld, %ld] %s",
+ inbox_nm > 1L ? inbox_nm : 1L,
+ number > 1L ? number: 1L,
+ ps_global->pine_name);
+ }
+ else
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "[%ld] %s", number > 1L ? number: 1L,
+ ps_global->pine_name);
+ }
+ else
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s%.80s", intro,
+ from && from[0] ? ((number > 1L) ? " Most recent f" : " F") : "",
+ from && from[0] ? "rom " : "",
+ from && from[0] ? from : "");
+
+ (*pith_opt_icon_text)(tmp_20k_buf, IT_NEWMAIL);
+ }
+}
+
+
+#if !defined(DOS) && !defined(OS2) && !defined(LEAVEOUTFIFO)
+static char *fifoname = NULL;
+static int fifoopenerrmsg = 0;
+static int fifofd = -1;
+static int fifoheader = 0;
+
+void
+init_newmailfifo(char *fname)
+{
+ if(fifoname)
+ close_newmailfifo();
+
+ if(!(fname && *fname))
+ return;
+
+ if(!fifoname){
+ if(mkfifo(fname, 0600) == -1){
+ q_status_message2(SM_ORDER,3,3,
+ "Can't create NewMail FIFO \"%s\": %s",
+ fname, error_description(errno));
+ return;
+ }
+ else
+ q_status_message1(SM_ORDER,0,3, "NewMail FIFO: \"%s\"", fname);
+
+ fifoname = cpystr(fname);
+ }
+}
+
+
+void
+close_newmailfifo(void)
+{
+ if(fifoname){
+ if(fifofd >= 0)
+ (void) close(fifofd);
+
+ if(*fifoname)
+ our_unlink(fifoname);
+
+ fs_give((void **) &fifoname);
+ }
+
+ fifoheader = 0;
+ fifoname = NULL;
+ fifofd = -1;
+ fifoopenerrmsg = 0;
+}
+
+
+void
+newmailfifo(int is_us, char *from, char *subject, char *folder)
+{
+ char buf[MAX_SCREEN_COLS+1], buf2[MAX_SCREEN_COLS+1];
+ char buf3[MAX_SCREEN_COLS+1], buf4[MAX_SCREEN_COLS+1];
+
+ if(!(fifoname && *fifoname)){
+ if(fifoname)
+ close_newmailfifo();
+
+ return;
+ }
+
+ if(fifofd < 0){
+ fifofd = our_open(fifoname, O_WRONLY | O_NONBLOCK | O_BINARY, 0600);
+ if(fifofd < 0){
+ if(!fifoopenerrmsg){
+ if(errno == ENXIO)
+ q_status_message2(SM_ORDER,0,3, "Nothing reading \"%s\": %s",
+ fifoname, error_description(errno));
+ else
+ q_status_message2(SM_ORDER,0,3, "Can't open \"%s\": %s",
+ fifoname, error_description(errno));
+
+ fifoopenerrmsg++;
+ }
+
+ return;
+ }
+ }
+
+ if(fifofd >= 0){
+ int width;
+ int fromlen, subjlen, foldlen;
+
+ width = MIN(MAX(20, ps_global->nmw_width), MAX_SCREEN_COLS);
+
+ foldlen = .18 * width;
+ foldlen = MAX(5, foldlen);
+ fromlen = .28 * width;
+ subjlen = width - 2 - foldlen - fromlen;
+
+ if(!fifoheader){
+ time_t now;
+ char *tmtxt;
+
+ now = time((time_t *) 0);
+ tmtxt = ctime(&now);
+ if(!tmtxt)
+ tmtxt = "";
+
+ snprintf(buf, sizeof(buf), "New Mail window started at %.*s\n",
+ MIN(100, strlen(tmtxt)-1), tmtxt);
+ (void) write(fifofd, buf, strlen(buf));
+
+ snprintf(buf, sizeof(buf), " %-*s%-*s%-*s\n",
+ fromlen, "From:",
+ subjlen, "Subject:",
+ foldlen, "Folder:");
+ (void) write(fifofd, buf, strlen(buf));
+
+ snprintf(buf, sizeof(buf), "%-*.*s\n", width, width, repeat_char(width, '-'));
+ (void) write(fifofd, buf, strlen(buf));
+
+ fifoheader++;
+ }
+
+ snprintf(buf, sizeof(buf), "%s %-*.*s %-*.*s %-*.*s\n", is_us ? "+" : " ",
+ fromlen - 1, fromlen - 1,
+ short_str(from ? from : "", buf2, sizeof(buf2), fromlen-1, EndDots),
+ subjlen - 1, subjlen - 1,
+ short_str(subject ? subject : "(no subject)",
+ buf3, sizeof(buf3), subjlen-1, EndDots),
+ foldlen, foldlen,
+ short_str(folder ? folder : "INBOX", buf4, sizeof(buf4), foldlen, FrontDots));
+ (void) write(fifofd, buf, strlen(buf));
+ }
+}
+#endif
diff --git a/alpine/newmail.h b/alpine/newmail.h
new file mode 100644
index 00000000..9526077f
--- /dev/null
+++ b/alpine/newmail.h
@@ -0,0 +1,22 @@
+/*
+ * $Id: newmail.h 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2009 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef ALPINE_NEWMAIL_INCLUDED
+#define ALPINE_NEWMAIL_INCLUDED
+
+/* exported protoypes */
+void newmail_status_message(MAILSTREAM *, long, long);
+
+#endif /* ALPINE_NEWMAIL_INCLUDED */
diff --git a/alpine/newuser.c b/alpine/newuser.c
new file mode 100644
index 00000000..381f21c6
--- /dev/null
+++ b/alpine/newuser.c
@@ -0,0 +1,204 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: newuser.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#include "headers.h"
+#include "newuser.h"
+#include "mailview.h"
+#include "help.h"
+#include "keymenu.h"
+#include "osdep/print.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/msgno.h"
+#include "../pith/filter.h"
+#include "../pith/init.h"
+#include "../pith/margin.h"
+
+
+/*
+ * Internal prototypes
+ */
+int nuov_processor(int, MSGNO_S *, SCROLL_S *);
+
+
+/*
+ * Display a new user or new version message.
+ */
+void
+new_user_or_version(struct pine *ps)
+{
+ char **shown_text;
+ int cmd = MC_NONE;
+ int first_time_alpine_user = 0;
+ char *error = NULL;
+ HelpType text;
+ SCROLL_S sargs;
+ STORE_S *store;
+ HANDLE_S *handles = NULL, *htmp;
+ gf_io_t pc;
+ char *vers = ps->vers_internal;
+
+ first_time_alpine_user = (ps->first_time_user
+ || (ps->pine_pre_vers
+ && isdigit((unsigned char) ps->pine_pre_vers[0])
+ && ps->pine_pre_vers[1] == '.'
+ && isdigit((unsigned char) vers[0])
+ && vers[1] == '.'
+ && ps->pine_pre_vers[0] < '5' /* it was Pine */
+ && vers[0] >= '5')); /* Alpine */
+
+ text = ps->first_time_user ? new_user_greeting :
+ first_time_alpine_user ? new_alpine_user_greeting : new_version_greeting;
+
+ shown_text = text;
+
+ /*
+ * Set it if the major revision number
+ * (the first after the dot) has changed.
+ */
+ ps->phone_home = (first_time_alpine_user
+ || (ps->pine_pre_vers
+ && isdigit((unsigned char) ps->pine_pre_vers[0])
+ && ps->pine_pre_vers[1] == '.'
+ && isdigit((unsigned char) ps->pine_pre_vers[2])
+ && isdigit((unsigned char) vers[0])
+ && vers[1] == '.'
+ && isdigit((unsigned char) vers[2])
+ && strncmp(ps->pine_pre_vers, vers, 3) < 0));
+
+ /*
+ * At this point, shown_text is a charstarstar with html
+ * Turn it into a charstar with digested html
+ */
+ do{
+ init_helper_getc(shown_text);
+ init_handles(&handles);
+
+ if((store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
+ gf_set_so_writec(&pc, store);
+ gf_filter_init();
+
+ gf_link_filter(gf_html2plain,
+ gf_html2plain_opt("x-alpine-help:",
+ ps->ttyo->screen_cols, non_messageview_margin(),
+ &handles, NULL, GFHP_LOCAL_HANDLES));
+
+ error = gf_pipe(helper_getc, pc);
+
+ gf_clear_so_writec(store);
+
+ if(!error){
+ struct key_menu km;
+ struct key keys[24];
+
+ for(htmp = handles; htmp; htmp = htmp->next)
+ if(htmp->type == URL
+ && htmp->h.url.path
+ && (htmp->h.url.path[0] == 'x'
+ || htmp->h.url.path[0] == '#'))
+ htmp->force_display = 1;
+
+ /* This is mostly here to get the curses variables
+ * for line and column in sync with where the
+ * cursor is on the screen. This gets warped when
+ * the composer is called because it does it's own
+ * stuff
+ */
+ ClearScreen();
+
+ memset(&sargs, 0, sizeof(SCROLL_S));
+ sargs.text.text = so_text(store);
+ sargs.text.src = CharStar;
+ sargs.text.desc = "greeting text";
+ sargs.text.handles = handles;
+ sargs.bar.title = "GREETING TEXT";
+ sargs.bar.style = TextPercent;
+ sargs.proc.tool = nuov_processor;
+ sargs.help.text = main_menu_tx;
+ sargs.help.title = "MAIN PINE HELP";
+ sargs.resize_exit = 1;
+ sargs.keys.menu = &km;
+ km = nuov_keymenu;
+ km.keys = keys;
+ memcpy(&keys[0], nuov_keymenu.keys,
+ (nuov_keymenu.how_many * 12) * sizeof(struct key));
+ setbitmap(sargs.keys.bitmap);
+
+ if(ps->phone_home){
+ km.keys[NUOV_EXIT].label = "Exit this greeting";
+ km.keys[NUOV_EXIT].bind.nch = 1;
+ }
+ else{
+ km.keys[NUOV_EXIT].label = "[Exit this greeting]";
+ km.keys[NUOV_EXIT].bind.nch = 3;
+ clrbitn(NUOV_VIEW, sargs.keys.bitmap);
+ }
+
+ if(ps->first_time_user)
+ clrbitn(NUOV_RELNOTES, sargs.keys.bitmap);
+
+ cmd = scrolltool(&sargs);
+
+ flush_input();
+
+ if(F_ON(F_BLANK_KEYMENU,ps_global))
+ FOOTER_ROWS(ps_global) = 1;
+
+ ClearScreen();
+ }
+
+ so_give(&store);
+ }
+
+ free_handles(&handles);
+ }
+ while(cmd == MC_RESIZE);
+}
+
+
+int
+nuov_processor(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
+{
+ int rv = 0;
+
+ switch(cmd){
+ /*----------- Print all the help ------------*/
+ case MC_PRINTMSG :
+ if(open_printer(sparms->text.desc) == 0){
+ print_help(sparms->proc.data.p);
+ close_printer();
+ }
+
+ break;
+
+ case MC_RELNOTES :
+ helper(h_news, "ALPINE RELEASE NOTES", 0);
+ ps_global->mangled_screen = 1;
+ break;
+
+ case MC_EXIT :
+ rv = 1;
+ break;
+
+ default :
+ panic("Unhandled case");
+ }
+
+ return(rv);
+}
diff --git a/alpine/newuser.h b/alpine/newuser.h
new file mode 100644
index 00000000..6cbb2c5d
--- /dev/null
+++ b/alpine/newuser.h
@@ -0,0 +1,28 @@
+/*
+ * $Id: newuser.h 768 2007-10-24 00:10:03Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_NEWUSER_INCLUDED
+#define PINE_NEWUSER_INCLUDED
+
+
+#include "../pith/state.h"
+
+
+/* exported protoypes */
+void new_user_or_version(struct pine *);
+
+
+#endif /* PINE_NEWUSER_INCLUDED */
diff --git a/alpine/osdep/Makefile.am b/alpine/osdep/Makefile.am
new file mode 100644
index 00000000..7c1547d4
--- /dev/null
+++ b/alpine/osdep/Makefile.am
@@ -0,0 +1,22 @@
+## Process this file with automake to produce Makefile.in
+## Use aclocal -I m4; automake
+
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+noinst_LIBRARIES = libpineosd.a
+
+libpineosd_a_SOURCES = chnge_pw.c debuging.c diskquot.non.c execview.c fltrname.c \
+ jobcntrl.c print.c termin.gen.c termin.unx.c termout.gen.c termout.unx.c \
+ debuging.h execview.h fltrname.h jobcntrl.h print.h resource.h termin.gen.h \
+ termin.unx.h termout.gen.h termout.unx.h windlg.h
+
+AM_CPPFLAGS = -I@top_builddir@/include -I@top_srcdir@/include
diff --git a/alpine/osdep/Makefile.in b/alpine/osdep/Makefile.in
new file mode 100644
index 00000000..4d452c9f
--- /dev/null
+++ b/alpine/osdep/Makefile.in
@@ -0,0 +1,544 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = alpine/osdep
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/acx_pthread.m4 \
+ $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
+ $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/VERSION \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/include/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+ARFLAGS = cru
+libpineosd_a_AR = $(AR) $(ARFLAGS)
+libpineosd_a_LIBADD =
+am_libpineosd_a_OBJECTS = chnge_pw.$(OBJEXT) debuging.$(OBJEXT) \
+ diskquot.non.$(OBJEXT) execview.$(OBJEXT) fltrname.$(OBJEXT) \
+ jobcntrl.$(OBJEXT) print.$(OBJEXT) termin.gen.$(OBJEXT) \
+ termin.unx.$(OBJEXT) termout.gen.$(OBJEXT) \
+ termout.unx.$(OBJEXT)
+libpineosd_a_OBJECTS = $(am_libpineosd_a_OBJECTS)
+DEFAULT_INCLUDES =
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+SOURCES = $(libpineosd_a_SOURCES)
+DIST_SOURCES = $(libpineosd_a_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_CFLAGS = @AM_CFLAGS@
+AM_LDFLAGS = @AM_LDFLAGS@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CP = @CP@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+C_CLIENT_CFLAGS = @C_CLIENT_CFLAGS@
+C_CLIENT_GCCOPTLEVEL = @C_CLIENT_GCCOPTLEVEL@
+C_CLIENT_LDFLAGS = @C_CLIENT_LDFLAGS@
+C_CLIENT_SPECIALS = @C_CLIENT_SPECIALS@
+C_CLIENT_TARGET = @C_CLIENT_TARGET@
+C_CLIENT_WITH_IPV6 = @C_CLIENT_WITH_IPV6@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISPELLPROG = @ISPELLPROG@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN = @LN@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKE = @MAKE@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NPA_PROG = @NPA_PROG@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+POSUB = @POSUB@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+PWPROG = @PWPROG@
+RANLIB = @RANLIB@
+REGEX_BUILD = @REGEX_BUILD@
+RM = @RM@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPELLPROG = @SPELLPROG@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEB_BINDIR = @WEB_BINDIR@
+WEB_BUILD = @WEB_BUILD@
+WEB_PUBCOOKIE_BUILD = @WEB_PUBCOOKIE_BUILD@
+WEB_PUBCOOKIE_LIB = @WEB_PUBCOOKIE_LIB@
+WEB_PUBCOOKIE_LINK = @WEB_PUBCOOKIE_LINK@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+acx_pthread_config = @acx_pthread_config@
+alpine_interactive_spellcheck = @alpine_interactive_spellcheck@
+alpine_simple_spellcheck = @alpine_simple_spellcheck@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LIBRARIES = libpineosd.a
+libpineosd_a_SOURCES = chnge_pw.c debuging.c diskquot.non.c execview.c fltrname.c \
+ jobcntrl.c print.c termin.gen.c termin.unx.c termout.gen.c termout.unx.c \
+ debuging.h execview.h fltrname.h jobcntrl.h print.h resource.h termin.gen.h \
+ termin.unx.h termout.gen.h termout.unx.h windlg.h
+
+AM_CPPFLAGS = -I@top_builddir@/include -I@top_srcdir@/include
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign alpine/osdep/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign alpine/osdep/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLIBRARIES:
+ -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+libpineosd.a: $(libpineosd_a_OBJECTS) $(libpineosd_a_DEPENDENCIES)
+ -rm -f libpineosd.a
+ $(libpineosd_a_AR) libpineosd.a $(libpineosd_a_OBJECTS) $(libpineosd_a_LIBADD)
+ $(RANLIB) libpineosd.a
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chnge_pw.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/debuging.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/diskquot.non.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/execview.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fltrname.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jobcntrl.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/print.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/termin.gen.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/termin.unx.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/termout.gen.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/termout.unx.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LIBRARIES)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libtool clean-noinstLIBRARIES ctags distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags uninstall uninstall-am
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/alpine/osdep/ReadMe b/alpine/osdep/ReadMe
new file mode 100644
index 00000000..787b9b71
--- /dev/null
+++ b/alpine/osdep/ReadMe
@@ -0,0 +1,14 @@
+The os-xxx.h files are created totally by hand. Start with os-gen.h,
+the generic version of the os.h file.
+
+The os-xxx.c files may be created by hand or you may use the simple include
+method that is used here. The os.c files are made by creating an
+os-xxx.ic file which lists all of the files it includes, and then
+running make os-xxx.c. There is an os-gen.ic generic version of the
+.ic file.
+
+You will also need to create a makefile.xxx in the parent directory. There
+are other makefiles to inspect there, including a generic makefile.gen.
+
+The names of all the included files here have to be eight characters or less
+for DOS, hence the stupid names.
diff --git a/alpine/osdep/alpine-splash.bmp b/alpine/osdep/alpine-splash.bmp
new file mode 100644
index 00000000..0bd797be
--- /dev/null
+++ b/alpine/osdep/alpine-splash.bmp
Binary files differ
diff --git a/alpine/osdep/alpine.ico b/alpine/osdep/alpine.ico
new file mode 100644
index 00000000..d6b7af76
--- /dev/null
+++ b/alpine/osdep/alpine.ico
Binary files differ
diff --git a/alpine/osdep/chnge_pw.c b/alpine/osdep/chnge_pw.c
new file mode 100644
index 00000000..e16e5983
--- /dev/null
+++ b/alpine/osdep/chnge_pw.c
@@ -0,0 +1,65 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: chnge_pw.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h> /* os-dep defs/includes */
+#include <general.h> /* generally useful definitions */
+
+#include "../../c-client/mail.h" /* for MAILSTREAM and friends */
+#include "../../c-client/osdep.h"
+#include "../../c-client/rfc822.h" /* for soutr_t and such */
+#include "../../c-client/misc.h" /* for cpystr proto */
+#include "../../c-client/utf8.h" /* for CHARSET and such*/
+#include "../../c-client/imap4r1.h"
+
+#include "../../pith/osdep/color.h"
+#include "../../pith/state.h"
+
+#include "termin.gen.h"
+#include "termout.gen.h"
+#include "chnge_pw.h"
+
+/*----------------------------------------------------------------------
+ Call the system to change the passwd
+
+It would be nice to talk to the passwd program via a pipe or ptty so the
+user interface could be consistent, but we can't count on the the prompts
+and responses from the passwd program to be regular so we just let the user
+type at the passwd program with some screen space, hope he doesn't scroll
+off the top and repaint when he's done.
+ ----*/
+void
+change_passwd(void)
+{
+#ifdef PASSWD_PROG
+ char cmd_buf[100];
+
+ ClearLines(1, ps_global->ttyo->screen_rows - 1);
+
+ MoveCursor(5, 0);
+ fflush(stdout);
+
+ PineRaw(0);
+ strncpy(cmd_buf, PASSWD_PROG, sizeof(cmd_buf));
+ cmd_buf[sizeof(cmd_buf)-1] = '\0';
+ system(cmd_buf);
+ sleep(3);
+ PineRaw(1);
+#endif /* PASSWD_PROG */
+}
+
+
diff --git a/alpine/osdep/chnge_pw.h b/alpine/osdep/chnge_pw.h
new file mode 100644
index 00000000..ce3463bc
--- /dev/null
+++ b/alpine/osdep/chnge_pw.h
@@ -0,0 +1,25 @@
+/*
+ * $Id: chnge_pw.h 769 2007-10-24 00:15:40Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_OSDEP_CHNGE_PW_INCLUDED
+#define PINE_OSDEP_CHNGE_PW_INCLUDED
+
+
+/* exported prototypes */
+void change_passwd(void);
+
+
+#endif /* PINE_OSDEP_CHNGE_PW_INCLUDED */
diff --git a/alpine/osdep/debuging.c b/alpine/osdep/debuging.c
new file mode 100644
index 00000000..0310f5b5
--- /dev/null
+++ b/alpine/osdep/debuging.c
@@ -0,0 +1,604 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: debuging.c 900 2008-01-05 01:13:26Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+#include <system.h> /* os-dep defs/includes */
+#include <general.h> /* generally useful definitions */
+
+#include "../../c-client/mail.h" /* for MAILSTREAM and friends */
+#include "../../c-client/osdep.h"
+#include "../../c-client/rfc822.h" /* for soutr_t and such */
+#include "../../c-client/misc.h" /* for cpystr proto */
+#include "../../c-client/utf8.h" /* for CHARSET and such*/
+#include "../../c-client/imap4r1.h"
+
+#include "../../pith/osdep/canaccess.h"
+
+#include "../../pith/debug.h"
+#include "../../pith/osdep/debugtime.h"
+#include "../../pith/osdep/color.h"
+#include "../../pith/osdep/bldpath.h"
+#include "../../pith/osdep/rename.h"
+#include "../../pith/osdep/filesize.h"
+#include "../../pith/init.h"
+#include "../../pith/status.h"
+#include "../../pith/sort.h"
+#include "../../pith/state.h"
+#include "../../pith/msgno.h"
+#include "../../pith/conf.h"
+#include "../../pith/flag.h"
+#include "../../pith/foldertype.h"
+#include "../../pith/folder.h"
+#include "../../pith/charconv/utf8.h"
+#include "../../pith/charconv/filesys.h"
+#include "../help.h"
+
+#include "debuging.h"
+
+
+#ifdef DEBUG
+
+/*
+ * globally visible
+ */
+FILE *debugfile = NULL;
+
+
+/*----------------------------------------------------------------------
+ Initialize debugging - open the debug log file
+
+ Args: none
+
+ Result: opens the debug logfile for dprints
+
+ Opens the file "~/.pine-debug1. Also maintains .pine-debug[2-4]
+ by renaming them each time so the last 4 sessions are saved.
+ ----*/
+void
+init_debug(void)
+{
+ char nbuf[5];
+ char newfname[MAXPATH+1], filename[MAXPATH+1], *dfile = NULL;
+ int i, fd;
+
+ if(!(debug || ps_global->debug_imap || ps_global->debug_tcp))
+ return;
+
+ for(i = ps_global->debug_nfiles - 1; i > 0; i--){
+ build_path(filename, ps_global->home_dir, DEBUGFILE, sizeof(filename));
+ strncpy(newfname, filename, sizeof(newfname)-1);
+ newfname[sizeof(newfname)-1] = '\0';
+ snprintf(nbuf, sizeof(nbuf), "%d", i);
+ strncat(filename, nbuf, sizeof(filename)-1-strlen(filename));
+ snprintf(nbuf, sizeof(nbuf), "%d", i+1);
+ strncat(newfname, nbuf, sizeof(newfname)-1-strlen(newfname));
+ (void)rename_file(filename, newfname);
+ }
+
+ build_path(filename, ps_global->home_dir, DEBUGFILE, sizeof(filename)-1);
+ strncat(filename, "1", sizeof(filename)-1-strlen(filename));
+ filename[sizeof(filename)-1] = '\0';
+
+ debugfile = NULL;
+ dfile = filename;
+ /*
+ * We were doing our_open so _WINDOWS would open it in widechar mode, but
+ * that means we have to print in widechar mode. For now just do open, since
+ * we it's a debug file and still readable. The alternative is copying all
+ * debug text to a widechar buffer, which would be a drag.
+ */
+ if((fd = open(filename, O_TRUNC|O_RDWR|O_CREAT, 0600)) >= 0)
+ debugfile = fdopen(fd, "w+");
+
+ if(debugfile != NULL){
+ char rev[128];
+ time_t now = time((time_t *)0);
+ if(ps_global->debug_flush)
+ setvbuf(debugfile, (char *)NULL, _IOLBF, BUFSIZ);
+
+ if(ps_global->debug_nfiles == 0){
+ /*
+ * If no debug files are asked for, make filename a tempfile
+ * to be used for a record should pine later crash...
+ */
+ if(debug < 9 && !ps_global->debug_flush && ps_global->debug_imap<4){
+ our_unlink(filename);
+ dfile = NULL;
+ }
+ }
+
+ dprint((0, "Debug output of the Alpine program (debug=%d debug_imap=%d). Version %s (%s %s)\n%s\n",
+ debug, ps_global->debug_imap,
+ ALPINE_VERSION,
+ SYSTYPE ? SYSTYPE : "?",
+ get_alpine_revision_string(rev, sizeof(rev)),
+ ctime(&now)));
+
+ dprint((0, "Starting after the reading_pinerc calls, the data in this file should\nbe encoded as UTF-8. Before that it will be in the user's native charset.\n"));
+ if(dfile && (debug > DEFAULT_DEBUG ||
+ ps_global->debug_imap > 0 ||
+ ps_global->debug_tcp > 0)){
+ snprintf(newfname, sizeof(newfname), "Debug file: %s (level=%d imap=%d)", dfile,
+ debug, ps_global->debug_imap);
+ init_error(ps_global, SM_ORDER, 3, 5, newfname);
+ }
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Try to save the debug file if we crash in a controlled way
+
+ Args: dfile: pointer to open debug file
+
+ Result: tries to move the appropriate .pine-debugx file to .pine-crash
+
+ Looks through the four .pine-debug files hunting for the one that is
+ associated with this pine, and then renames it.
+ ----*/
+void
+save_debug_on_crash(FILE *dfile, int (*keystrokes) (int *, char *, size_t))
+{
+ char nbuf[5], crashfile[MAXPATH+1], filename[MAXPATH+1];
+ int i;
+ struct stat dbuf, tbuf;
+ time_t now = time((time_t *)0);
+
+ if(!(dfile && fstat(fileno(dfile), &dbuf) == 0))
+ return;
+
+ fprintf(dfile, "save_debug_on_crash: Version %s: debug level %d",
+ ALPINE_VERSION, debug);
+ fprintf(dfile, "\n : %s\n", ctime(&now));
+
+ build_path(crashfile, ps_global->home_dir, ".pine-crash",sizeof(crashfile));
+
+ fprintf(dfile, "Attempting to save debug file to %s\n\n", crashfile);
+ fprintf(stderr,
+ "\n\n Attempting to save debug file to %s\n\n", crashfile);
+
+ /* Blat out last n keystrokes */
+ if(keystrokes){
+ int cval;
+ char cstr[256];
+
+ fputs("========== Latest Keystrokes =========================\n", dfile);
+
+ while((*keystrokes)(&cval, cstr, sizeof(cstr)) != -1){
+ fprintf(dfile, "\t%s\t(0x%4.4x)\n", cstr, cval);
+ }
+ }
+
+ fputs("========== Latest Keystrokes End =====================\n\n", dfile);
+
+#ifdef DEBUGJOURNAL
+ fputs("========== Append DebugJournal =======================\n", dfile);
+#else /* DEBUGJOURNAL */
+ fputs("========== Append Journal =======================\n", dfile);
+#endif /* DEBUGJOURNAL */
+ debugjournal_to_file(dfile);
+#ifdef DEBUGJOURNAL
+ fputs("========== Append DebugJournal End ===================\n", dfile);
+#else /* DEBUGJOURNAL */
+ fputs("========== Append Journal End ===================\n", dfile);
+#endif /* DEBUGJOURNAL */
+
+ /* look for existing debug file */
+ for(i = 1; i <= ps_global->debug_nfiles; i++){
+ build_path(filename, ps_global->home_dir, DEBUGFILE, sizeof(filename));
+ snprintf(nbuf, sizeof(nbuf), "%d", i);
+ strncat(filename, nbuf, sizeof(filename)-1-strlen(filename));
+ if(our_stat(filename, &tbuf) != 0)
+ continue;
+
+ /* This must be the current debug file */
+ if(tbuf.st_dev == dbuf.st_dev && tbuf.st_ino == dbuf.st_ino){
+ rename_file(filename, crashfile);
+ break;
+ }
+ }
+
+ /* if current debug file name not found, write it by hand */
+ if(i > ps_global->debug_nfiles){
+ FILE *cfp;
+ char buf[1025];
+
+ /*
+ * Copy the debug temp file into the
+ */
+ if((cfp = our_fopen(crashfile, "wb")) != NULL){
+ buf[sizeof(buf)-1] = '\0';
+ fseek(dfile, 0L, 0);
+ while(fgets(buf, sizeof(buf)-1, dfile) && fputs(buf, cfp) != EOF)
+ ;
+
+ fclose(cfp);
+ }
+ }
+
+ fclose(dfile);
+}
+
+
+/*
+ * functions required by pith library
+ */
+#define CHECK_EVERY_N_TIMES 100
+#define MAX_DEBUG_FILE_SIZE 200000L
+/*
+ * This is just to catch runaway Pines that are looping spewing out
+ * debugging (and filling up a file system). The stop doesn't have to be
+ * at all precise, just soon enough to hopefully prevent filling the
+ * file system. If the debugging level is high (9 for now), then we're
+ * presumably looking for some problem, so don't truncate.
+ */
+int
+do_debug(FILE *debug_fp)
+{
+ static int counter = CHECK_EVERY_N_TIMES;
+ static int ok = 1;
+ long filesize;
+
+ if(!debugfile)
+ return(0);
+
+ if(debug <= DEFAULT_DEBUG
+ && !ps_global->debug_flush
+ && !ps_global->debug_tcp
+ && !ps_global->debug_timestamp
+ && !ps_global->debug_imap
+ && ok
+ && --counter <= 0){
+ if((filesize = fp_file_size(debug_fp)) != -1L)
+ ok = (unsigned long)filesize < (unsigned long)MAX_DEBUG_FILE_SIZE;
+
+ counter = CHECK_EVERY_N_TIMES;
+ if(!ok){
+ fprintf(debug_fp, "\n\n --- No more debugging ---\n");
+ fprintf(debug_fp,
+ " (debug file growing too large - over %ld bytes)\n\n",
+ MAX_DEBUG_FILE_SIZE);
+ fflush(debug_fp);
+ }
+ }
+
+ if(ok && ps_global->debug_timestamp)
+ fprintf(debug_fp, "\n%s\n", debug_time(0, ps_global->debug_timestamp));
+
+ return(ok);
+}
+
+void
+output_debug_msg(int dlevel, char *fmt, ...)
+{
+ va_list args;
+
+ if(debugfile && debug >= dlevel && do_debug(debugfile)){
+ int l;
+
+ va_start(args, fmt);
+ vfprintf(debugfile, fmt, args);
+ va_end(args);
+
+ if((l = strlen(fmt)) > 2 && fmt[l-1] != '\n')
+ fputc('\n', debugfile);
+
+ if(ps_global->debug_flush)
+ fflush(debugfile);
+ }
+
+ if(panicking())
+ return;
+
+#ifdef DEBUGJOURNAL
+
+ if(dlevel <= 9 || dlevel <= debug){
+ /*
+ * Make this static in order to move it off of
+ * the stack. We were getting "random" crashes
+ * on some systems when this size interacted with
+ * pthread stack size somehow. Taking it off of
+ * the stack ought to fix that without us having to
+ * understand how it all works.
+ */
+ static char b[64000];
+
+ va_start(args, fmt);
+ vsnprintf(b, sizeof(b), fmt, args);
+ va_end(args);
+
+ b[sizeof(b)-1] = '\0';
+ add_review_message(b, dlevel);
+ }
+
+#endif /* DEBUGJOURNAL */
+
+}
+
+
+/*----------------------------------------------------------------------
+ Dump the whole config enchilada using the given function
+
+ Args: ps -- pine struct containing vars to dump
+ pc -- function to use to write the config data
+ brief -- brief dump, only dump user_vals
+ Result: command line, global, user var's written with given function
+
+ ----*/
+void
+dump_configuration(int brief)
+{
+ gf_io_t pc;
+
+ if(!do_debug(debugfile))
+ return;
+
+ gf_set_writec(&pc, debugfile, 0L, FileStar, 0);
+
+ dump_config(ps_global, pc, brief);
+}
+
+
+void
+dump_config(struct pine *ps, gf_io_t pc, int brief)
+{
+ int i;
+ char quotes[3], tmp[MAILTMPLEN];
+ register struct variable *vars;
+ FEATURE_S *feat;
+
+ quotes[0] = '"'; quotes[1] = '"'; quotes[2] = '\0';
+
+ for(i = (brief ? 2 : 0); i < (brief ? 4 : 6); i++){
+ snprintf(tmp, sizeof(tmp), "======= %.20s_val options set",
+ (i == 0) ? "Current" :
+ (i == 1) ? "Command_line" :
+ (i == 2) ? "User" :
+ (i == 3) ? "PostloadUser" :
+ (i == 4) ? "Global"
+ : "Fixed");
+ gf_puts(tmp, pc);
+ if(i > 1){
+ snprintf(tmp, sizeof(tmp), " (%.100s)",
+ (i == 2) ? ((ps->prc) ? ps->prc->name : ".pinerc") :
+ (i == 3) ? ((ps->post_prc) ? ps->post_prc->name : "postload") :
+ (i == 4) ? ((ps->pconf) ? ps->pconf->name
+ : SYSTEM_PINERC) :
+#if defined(DOS) || defined(OS2)
+ "NO FIXED"
+#else
+ ((can_access(SYSTEM_PINERC_FIXED, ACCESS_EXISTS) == 0)
+ ? SYSTEM_PINERC_FIXED : "NO pine.conf.fixed")
+#endif
+ );
+ gf_puts(tmp, pc);
+ }
+
+ gf_puts(" =======\n", pc);
+ for(vars = ps->vars; vars->name; vars++){
+ if(vars->is_list){
+ char **t;
+ t = (i == 0) ? vars->current_val.l :
+ (i == 1) ? vars->cmdline_val.l :
+ (i == 2) ? vars->main_user_val.l :
+ (i == 3) ? vars->post_user_val.l :
+ (i == 4) ? vars->global_val.l
+ : vars->fixed_val.l;
+ if(t && *t){
+ snprintf(tmp, sizeof(tmp), " %20.20s : %.*s\n", vars->name,
+ sizeof(tmp)-26, **t ? *t : quotes);
+ gf_puts(tmp, pc);
+ while(++t && *t){
+ snprintf(tmp, sizeof(tmp)," %20.20s : %.*s\n","",
+ sizeof(tmp)-26, **t ? *t : quotes);
+ gf_puts(tmp, pc);
+ }
+ }
+ }
+ else{
+ char *t;
+ t = (i == 0) ? vars->current_val.p :
+ (i == 1) ? vars->cmdline_val.p :
+ (i == 2) ? vars->main_user_val.p :
+ (i == 3) ? vars->post_user_val.p :
+ (i == 4) ? vars->global_val.p
+ : vars->fixed_val.p;
+ if(t){
+ snprintf(tmp, sizeof(tmp), " %20.20s : %.*s\n", vars->name,
+ sizeof(tmp)-26, *t ? t : quotes);
+ gf_puts(tmp, pc);
+ }
+ }
+ }
+ }
+
+ if(!brief){
+ gf_puts("========== Feature settings ==========\n", pc);
+ for(i = 0; (feat = feature_list(i)); i++)
+ if(feat->id != F_OLD_GROWTH){
+ snprintf(tmp, sizeof(tmp),
+ " %.50s%.100s\n", F_ON(feat->id, ps) ? " " : "no-",
+ feat->name);
+ gf_puts(tmp, pc);
+ }
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Dump interesting variables from the given pine struct
+
+ Args: ps -- pine struct to dump
+ pc -- function to use to write the config data
+
+ Result: various important pine struct data written
+
+ ----*/
+void
+dump_pine_struct(struct pine *ps, gf_io_t pc)
+{
+ char *p;
+ extern char term_name[];
+ int i;
+ MAILSTREAM *m;
+ MSGNO_S *msgmap;
+
+ gf_puts("========== struct pine * ==========\n", pc);
+ if(!ps){
+ gf_puts("!No struct!\n", pc);
+ return;
+ }
+
+ gf_puts("ui:\tlogin = ", pc);
+ gf_puts((ps->ui.login) ? ps->ui.login : "NULL", pc);
+ gf_puts(", full = ", pc);
+ gf_puts((ps->ui.fullname) ? ps->ui.fullname : "NULL", pc);
+ gf_puts("\n\thome = ", pc);
+ gf_puts((ps->ui.homedir) ? ps->ui.homedir : "NULL", pc);
+
+ gf_puts("\nhome_dir=\t", pc);
+ gf_puts(ps->home_dir ? ps->home_dir : "NULL", pc);
+ gf_puts("\nhostname=\t", pc);
+ gf_puts(ps->hostname ? ps->hostname : "NULL", pc);
+ gf_puts("\nlocaldom=\t", pc);
+ gf_puts(ps->localdomain ? ps->localdomain : "NULL", pc);
+ gf_puts("\nuserdom=\t", pc);
+ gf_puts(ps->userdomain ? ps->userdomain : "NULL", pc);
+ gf_puts("\nmaildom=\t", pc);
+ gf_puts(ps->maildomain ? ps->maildomain : "NULL", pc);
+
+ gf_puts("\ncur_cntxt=\t", pc);
+ gf_puts((ps->context_current && ps->context_current->context)
+ ? ps->context_current->context : "None", pc);
+ gf_puts("\ncur_fldr=\t", pc);
+ gf_puts(ps->cur_folder, pc);
+
+ gf_puts("\nnstream=\t", pc);
+ gf_puts(long2string((long) ps->s_pool.nstream), pc);
+
+ for(i = 0; i < ps->s_pool.nstream; i++){
+ m = ps->s_pool.streams[i];
+ gf_puts("\nstream slot ", pc);
+ gf_puts(long2string((long) i), pc);
+ if(!m){
+ gf_puts(" empty", pc);
+ continue;
+ }
+
+ if(ps->mail_stream == m)
+ gf_puts("\nThis is the current mailstream", pc);
+ if(sp_flagged(m, SP_INBOX))
+ gf_puts("\nThis is the inbox stream", pc);
+
+ gf_puts("\nactual mbox=\t", pc);
+ gf_puts(m->mailbox ? m->mailbox : "no mailbox!", pc);
+
+ gf_puts("\nflags=", pc);
+ gf_puts(long2string((long) sp_flags(m)), pc);
+ gf_puts("\nnew_mail_count=", pc);
+ gf_puts(long2string((long) sp_new_mail_count(m)), pc);
+ gf_puts("\nmail_since_cmd=", pc);
+ gf_puts(long2string((long) sp_mail_since_cmd(m)), pc);
+ gf_puts("\nmail_box_changed=", pc);
+ gf_puts(long2string((long) sp_mail_box_changed(m)), pc);
+ gf_puts("\nunsorted_newmail=", pc);
+ gf_puts(long2string((long) sp_unsorted_newmail(m)), pc);
+ gf_puts("\nneed_to_rethread=", pc);
+ gf_puts(long2string((long) sp_need_to_rethread(m)), pc);
+ gf_puts("\nviewing_a_thread=", pc);
+ gf_puts(long2string((long) sp_viewing_a_thread(m)), pc);
+ gf_puts("\ndead_stream=", pc);
+ gf_puts(long2string((long) sp_dead_stream(m)), pc);
+ gf_puts("\nnoticed_dead_stream=", pc);
+ gf_puts(long2string((long) sp_noticed_dead_stream(m)), pc);
+ gf_puts("\nio_error_on_stream=", pc);
+ gf_puts(long2string((long) sp_io_error_on_stream(m)), pc);
+
+ msgmap = sp_msgmap(m);
+ if(msgmap){
+ gf_puts("\nmsgmap: tot=", pc);
+ gf_puts(long2string(mn_get_total(msgmap)), pc);
+ gf_puts(", cur=", pc);
+ gf_puts(long2string(mn_get_cur(msgmap)), pc);
+ gf_puts(", del=", pc);
+ gf_puts(long2string(count_flagged(m, F_DEL)),pc);
+ gf_puts(", hid=", pc);
+ gf_puts(long2string(any_lflagged(msgmap, MN_HIDE)), pc);
+ gf_puts(", exld=", pc);
+ gf_puts(long2string(any_lflagged(msgmap, MN_EXLD)), pc);
+ gf_puts(", slct=", pc);
+ gf_puts(long2string(any_lflagged(msgmap, MN_SLCT)), pc);
+ gf_puts(", chid=", pc);
+ gf_puts(long2string(any_lflagged(msgmap, MN_CHID)), pc);
+ gf_puts(", coll=", pc);
+ gf_puts(long2string(any_lflagged(msgmap, MN_COLL)), pc);
+ gf_puts(", usor=", pc);
+ gf_puts(long2string(any_lflagged(msgmap, MN_USOR)), pc);
+ gf_puts(", stmp=", pc);
+ gf_puts(long2string(any_lflagged(msgmap, MN_STMP)), pc);
+ gf_puts(", sort=", pc);
+ if(mn_get_revsort(msgmap))
+ gf_puts("rev-", pc);
+
+ gf_puts(sort_name(mn_get_sort(msgmap)), pc);
+ }
+ else
+ gf_puts("\nNo msgmap", pc);
+ }
+
+
+ gf_puts("\nterm ", pc);
+#if !defined(DOS) && !defined(OS2)
+ gf_puts("type=", pc);
+ gf_puts(term_name, pc);
+ gf_puts(", ttyname=", pc);
+ gf_puts((p = (char *)ttyname(0)) ? p : "NONE", pc);
+#endif
+ gf_puts(", size=", pc);
+ gf_puts(int2string(ps->ttyo->screen_rows), pc);
+ gf_puts("x", pc);
+ gf_puts(int2string(ps->ttyo->screen_cols), pc);
+ gf_puts(", speed=", pc);
+ gf_puts((ps->low_speed) ? "slow" : "normal", pc);
+ gf_puts("\n", pc);
+}
+
+
+void
+dump_contexts(void)
+{
+ int i = 0;
+ CONTEXT_S *c = ps_global->context_list;
+
+ if(!(debugfile && debug > 7 && do_debug(debugfile)))
+ return;
+
+ while(debugfile && c != NULL){
+ fprintf(debugfile, "\n***** context %s\n", c->context);
+ if(c->label)
+ fprintf(debugfile,"LABEL: %s\n", c->label);
+
+ if(c->comment)
+ fprintf(debugfile,"COMMENT: %s\n", c->comment);
+
+ for(i = 0; i < folder_total(FOLDERS(c)); i++)
+ fprintf(debugfile, " %d) %s\n", i + 1, folder_entry(i,FOLDERS(c))->name);
+
+ c = c->next;
+ }
+}
+
+#endif /* DEBUG */
diff --git a/alpine/osdep/debuging.h b/alpine/osdep/debuging.h
new file mode 100644
index 00000000..aee9d721
--- /dev/null
+++ b/alpine/osdep/debuging.h
@@ -0,0 +1,30 @@
+/*
+ * $Id: debuging.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_OSDEP_DEBUGING_INCLUDED
+#define PINE_OSDEP_DEBUGING_INCLUDED
+
+/* stream to stuff debuging output */
+extern FILE *debugfile;
+
+/*
+ * Exported Prototypes
+ */
+#ifdef DEBUG
+void init_debug(void);
+void save_debug_on_crash(FILE *, int (*)(int *, char *, size_t));
+#endif
+
+#endif /* PINE_OSDEP_DEBUGING_INCLUDED */
diff --git a/alpine/osdep/diskquot b/alpine/osdep/diskquot
new file mode 100644
index 00000000..4ff6b261
--- /dev/null
+++ b/alpine/osdep/diskquot
@@ -0,0 +1,78 @@
+#if defined(USE_QUOTAS)
+
+#include <sys/param.h>
+#include <sys/quota.h>
+
+/*----------------------------------------------------------------------
+ Return space left in disk quota on file system which given path is in.
+
+ Args: path - Path name of file or directory on file system of concern
+ over - pointer to flag that is set if the user is over quota
+
+ Returns: If *over = 0, the number of bytes free in disk quota as per
+ the soft limit.
+ If *over = 1, the number of bytes *over* quota.
+ -1 is returned on an error looking up quota
+ 0 is returned if there is no quota
+
+BUG: If there's more than 2.1Gb free this function will break
+ ----*/
+long
+disk_quota(path, over)
+ char *path;
+ int *over;
+{
+ static int no_quota = 0;
+ struct stat statx;
+ struct dqblk quotax;
+ long q;
+
+ if(no_quota)
+ return(0L); /* If no quota the first time, then none the second,
+ Also Ultrix seems to give the wrong answer the second
+ time */
+
+ dprint(5, (debugfile, "quota_check path: %s\n", path ? path : "?"));
+ if(stat(path, &statx) < 0) {
+ return(-1L);
+ }
+
+ *over = 0;
+ errno = 0;
+
+ dprint(7, (debugfile, "Quota check: UID:%d stat: %d %x\n",
+ getuid(), statx.st_dev, statx.st_dev));
+ memset((void *)&quotax, 0, sizeof(struct dqblk));
+ if(quota(Q_GETDLIM, getuid(), statx.st_dev, &quotax) < 0) {
+ dprint(5, (debugfile, "Quota failed : %s\n",
+ error_description(errno)));
+ if(errno == ESRCH){
+ no_quota = 1;
+ return(0L); /* No limit */
+ } else {
+ return(-1L); /* Some thing went wrong */
+ }
+ }
+
+ dprint(5,(debugfile,"Quota: bsoftlimit:%d bhardlimit:%d curblock:%d\n",
+ quotax.dqb_bsoftlimit, quotax.dqb_bhardlimit, quotax.dqb_curblocks));
+
+ /* Some confusion on the type of bsoftlimit. The include file says
+ unsigned, but -1 seems to indicate no quota */
+ if(quotax.dqb_bsoftlimit == 0 || (long)quotax.dqb_bsoftlimit == -1) {
+ no_quota = 1;
+ return(0L);
+ }
+
+ q = (quotax.dqb_bsoftlimit - quotax.dqb_curblocks) * 1024;
+
+ if(q < 0) {
+ q = -q;
+ *over = 1;
+ }
+ dprint(5, (debugfile, "disk_quota returning :%d, over:%d\n", q, *over));
+ return(q);
+}
+#endif /* USE_QUOTAS */
+
+
diff --git a/alpine/osdep/diskquot.a32 b/alpine/osdep/diskquot.a32
new file mode 100644
index 00000000..c4695764
--- /dev/null
+++ b/alpine/osdep/diskquot.a32
@@ -0,0 +1,8 @@
+#if defined(USE_QUOTAS)
+
+#include <jfs/quota.h>
+
+include(sunquota)
+#endif /* USE_QUOTAS */
+
+
diff --git a/alpine/osdep/diskquot.a41 b/alpine/osdep/diskquot.a41
new file mode 100644
index 00000000..564c4201
--- /dev/null
+++ b/alpine/osdep/diskquot.a41
@@ -0,0 +1,6 @@
+#if defined(USE_QUOTAS)
+#include <jfs/quota.h>
+
+include(sunquota)
+#endif /* USE_QUOTAS */
+
diff --git a/alpine/osdep/diskquot.hpp b/alpine/osdep/diskquot.hpp
new file mode 100644
index 00000000..1b328cd6
--- /dev/null
+++ b/alpine/osdep/diskquot.hpp
@@ -0,0 +1,9 @@
+#if defined(USE_QUOTAS)
+
+#include <sys/param.h>
+#include <sys/quota.h>
+
+include(sunquota)
+#endif /* USE_QUOTAS */
+
+
diff --git a/alpine/osdep/diskquot.non.c b/alpine/osdep/diskquot.non.c
new file mode 100644
index 00000000..17465f63
--- /dev/null
+++ b/alpine/osdep/diskquot.non.c
@@ -0,0 +1,46 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: diskquot.non.c 761 2007-10-23 22:35:18Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+
+#ifdef USE_QUOTAS
+
+/*----------------------------------------------------------------------
+ This system doesn't have disk quotas.
+ Return space left in disk quota on file system which given path is in.
+
+ Args: path - Path name of file or directory on file system of concern
+ over - pointer to flag that is set if the user is over quota
+
+ Returns: If *over = 0, the number of bytes free in disk quota as per
+ the soft limit.
+ If *over = 1, the number of bytes *over* quota.
+ -1 is returned on an error looking up quota
+ 0 is returned if there is no quota
+
+BUG: If there's more than 2.1Gb free this function will break
+ ----*/
+long
+disk_quota(path, over)
+ char *path;
+ int *over;
+{
+ return(0L);
+}
+#endif /* USE_QUOTAS */
+
+
diff --git a/alpine/osdep/diskquot.ptx b/alpine/osdep/diskquot.ptx
new file mode 100644
index 00000000..b636db08
--- /dev/null
+++ b/alpine/osdep/diskquot.ptx
@@ -0,0 +1,9 @@
+#if defined(USE_QUOTAS)
+
+#include <sys/ufsquota.h>
+#define MTABNAME "/etc/mnttab"
+
+include(sunquota)
+#endif /* USE_QUOTAS */
+
+
diff --git a/alpine/osdep/diskquot.sgi b/alpine/osdep/diskquot.sgi
new file mode 100644
index 00000000..3100d06c
--- /dev/null
+++ b/alpine/osdep/diskquot.sgi
@@ -0,0 +1,8 @@
+#if defined(USE_QUOTAS)
+
+#include <sys/quota.h>
+
+include(sunquota)
+#endif /* USE_QUOTAS */
+
+
diff --git a/alpine/osdep/diskquot.so5 b/alpine/osdep/diskquot.so5
new file mode 100644
index 00000000..ffd4ca93
--- /dev/null
+++ b/alpine/osdep/diskquot.so5
@@ -0,0 +1,9 @@
+#if defined(USE_QUOTAS)
+
+#include <sys/fs/ufs_quota.h>
+#define MTABNAME "/etc/mnttab"
+
+include(solquota)
+#endif /* USE_QUOTAS */
+
+
diff --git a/alpine/osdep/diskquot.sun b/alpine/osdep/diskquot.sun
new file mode 100644
index 00000000..e8cb0506
--- /dev/null
+++ b/alpine/osdep/diskquot.sun
@@ -0,0 +1,8 @@
+#if defined(USE_QUOTAS)
+
+#include <ufs/quota.h>
+
+include(sunquota)
+#endif /* USE_QUOTAS */
+
+
diff --git a/alpine/osdep/diskquot.sv4 b/alpine/osdep/diskquot.sv4
new file mode 100644
index 00000000..dc330a30
--- /dev/null
+++ b/alpine/osdep/diskquot.sv4
@@ -0,0 +1,9 @@
+#if defined(USE_QUOTAS)
+
+#include <sys/fs/ufs_quota.h>
+#define MTABNAME "/etc/mnttab"
+
+include(sunquota)
+#endif /* USE_QUOTAS */
+
+
diff --git a/alpine/osdep/execview.c b/alpine/osdep/execview.c
new file mode 100644
index 00000000..da7310df
--- /dev/null
+++ b/alpine/osdep/execview.c
@@ -0,0 +1,555 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: execview.c 942 2008-03-04 18:21:33Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "../../c-client/mail.h" /* for MAILSTREAM and friends */
+#include "../../c-client/osdep.h"
+#include "../../c-client/rfc822.h" /* for soutr_t and such */
+#include "../../c-client/misc.h" /* for cpystr proto */
+#include "../../c-client/utf8.h" /* for CHARSET and such*/
+#include "../../c-client/imap4r1.h"
+
+#include "../../pith/debug.h"
+
+#include "../../pith/osdep/temp_nam.h"
+#include "../../pith/osdep/color.h"
+#include "../../pith/osdep/mimedisp.h"
+
+#include "../../pith/charconv/utf8.h"
+#include "../../pith/charconv/filesys.h"
+
+#include "../pith/mailcap.h"
+
+#include "../status.h"
+#include "../radio.h"
+#include "../signal.h"
+#include "../../pico/estruct.h"
+#include "../../pico/pico.h"
+#include "../mailview.h"
+#include "termin.gen.h"
+
+#ifdef _WINDOWS
+#include "../../pico/osdep/mswin.h"
+#endif
+
+/* Useful structures */
+#if OSX_TARGET
+typedef struct _execview_event_data_s {
+ int done;
+ ProcessSerialNumber pid;
+ int set_pid;
+} EXEC_EVENT_DATA_S;
+#endif
+
+
+/* internal prototypes */
+#if OSX_TARGET
+pascal OSStatus osx_launch_app_callback(EventHandlerCallRef,
+ EventRef, void *);
+int install_app_launch_cb(void *);
+void osx_launch_special_handling(MCAP_CMD_S *, char *);
+#endif
+
+
+
+/* ----------------------------------------------------------------------
+ Execute the given mailcap command
+
+ Args: cmd -- the command to execute
+ image_file -- the file the data is in
+ needsterminal -- does this command want to take over the terminal?
+ ----*/
+void
+exec_mailcap_cmd(MCAP_CMD_S *mc_cmd, char *image_file, int needsterminal)
+{
+#ifdef _WINDOWS
+ STARTUPINFO start_info;
+ PROCESS_INFORMATION proc_info;
+ WINHAND childProcess;
+ int success = 0;
+ char *cmd;
+ LPTSTR image_file_lpt = NULL;
+ LPTSTR cmd_lpt = NULL;
+
+ /* no special handling yet, but could be used to replace '*' hack */
+ if(mc_cmd)
+ cmd = mc_cmd->command;
+ else
+ return;
+
+ dprint((9, "run_viewer: command=%s\n", cmd ? cmd : "?")) ;
+
+ if(image_file)
+ image_file_lpt = utf8_to_lptstr(image_file);
+
+ /* Set to READONLY so the viewer can't try to edit it and keep it around */
+ if(image_file_lpt)
+ SetFileAttributes(image_file_lpt, FILE_ATTRIBUTE_READONLY);
+
+ if(*cmd == '*' || (*cmd == '\"' && *(cmd+1) == '*')){
+ /*
+ * It has been asked that there be the ability to do an
+ * "Open With..." on attachments like you can from the
+ * Windows file browser. After looking into this, it
+ * seems that the only way to do this would be through
+ * an undocumented hack. Here, we would pass "openas" as
+ * the verb to mswin_shell_exec (also some changes in
+ * mswin_shell_exec). Since this is the delicate world
+ * of attachment handling, it seems right not to rely on
+ * a hack. The interface wouldn't be too clean anyways,
+ * as we would have to download the attachment only to
+ * display the "Open With..." dialog. Go figure, some
+ * things Microsoft just wants to keep to themselves.
+ */
+
+ /*
+ * 2/1/2007. No idea when the above comment was written, but it is
+ * documented now at least. The below two urls describe the "openas" verb:
+ *
+ * http://blogs.msdn.com/oldnewthing/archive/2004/11/26/270710.aspx
+ * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/
+ * shellcc/platform/shell/programmersguide/shell_basics/
+ * shell_basics_extending/context.asp
+ */
+ success = mswin_shell_exec(cmd, &childProcess) == 0;
+ }
+ else{
+ memset(&proc_info, 0, sizeof(proc_info));
+ memset(&start_info, 0, sizeof(start_info));
+ start_info.dwFlags = STARTF_FORCEONFEEDBACK;
+ start_info.wShowWindow = SW_SHOWNORMAL;
+
+ if(cmd)
+ cmd_lpt = utf8_to_lptstr(cmd);
+
+ if(CreateProcess(NULL, cmd_lpt, NULL, NULL, FALSE,
+ CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP,
+ NULL, NULL, &start_info, &proc_info) == TRUE){
+ q_status_message(SM_ORDER, 0, 4, "VIEWER command completed");
+ dprint ((3, "CreatProcess(%s) Success.\n",
+ cmd ? cmd : "?"));
+ childProcess = proc_info.hProcess;
+ success = 1;
+ }
+
+ if(cmd_lpt)
+ fs_give((void **) &cmd_lpt);
+ }
+
+ if(!success){
+ int rc = (int) GetLastError();
+ if(image_file_lpt)
+ SetFileAttributes(image_file_lpt, FILE_ATTRIBUTE_NORMAL);
+
+ our_unlink(image_file);
+ q_status_message2(SM_ORDER, 3, 4, "\007Can't start viewer. %s%s.",
+ (rc == 2 || rc == 3) ? "Viewer not found: " :
+ (rc == 8) ? "Not enough memory" : "Windows error ",
+ (rc == 2 || rc == 3) ? cmd :
+ (rc == 8) ? "" : int2string(rc));
+ }
+
+ if(image_file_lpt)
+ fs_give((void **) &image_file_lpt);
+
+#elif OSX_TARGET
+
+ char *command = NULL,
+ *result_file = NULL,
+ *p;
+ char **r_file_h;
+ PIPE_S *syspipe;
+ int mode;
+
+ if(!mc_cmd)
+ return;
+ if(mc_cmd->special_handling){
+ char *rhost;
+
+ if(mime_os_specific_access())
+ osx_launch_special_handling(mc_cmd, image_file);
+ else{
+ q_status_message(SM_ORDER, 0, 4, "VIEWER command cancelled");
+ our_unlink(image_file);
+ }
+ }
+ else {
+ char *cmd = mc_cmd->command;
+ size_t l;
+
+ l = 32 + strlen(cmd) + (2*strlen(image_file));
+ p = command = (char *) fs_get((l+1) * sizeof(char));
+ if(!needsterminal) /* put in background if it doesn't need terminal */
+ *p++ = '(';
+
+ snprintf(p, l+1-(p-command), "%s", cmd);
+ p += strlen(p);
+ if(!needsterminal){
+ if(p-command+2 < l+1){
+ *p++ = ')';
+ *p++ = ' ';
+ *p++ = '&';
+ }
+ }
+
+ if(p-command < l+1)
+ *p++ = '\n';
+
+ if(p-command < l+1)
+ *p = '\0';
+
+ dprint((9, "exec_mailcap_cmd: command=%s\n",
+ command ? command : "?"));
+
+ mode = PIPE_RESET;
+ if(needsterminal == 1)
+ r_file_h = NULL;
+ else{
+ mode |= PIPE_WRITE | PIPE_STDERR;
+ result_file = temp_nam(NULL, "pine_cmd");
+ r_file_h = &result_file;
+ }
+
+ if(syspipe = open_system_pipe(command, r_file_h, NULL, mode, 0, pipe_callback, NULL)){
+ close_system_pipe(&syspipe, NULL, pipe_callback);
+ if(needsterminal == 1)
+ q_status_message(SM_ORDER, 0, 4, "VIEWER command completed");
+ else if(needsterminal == 2)
+ display_output_file(result_file, "VIEWER", " command result", 1);
+ else
+ display_output_file(result_file, "VIEWER", " command launched", 1);
+ }
+ else
+ q_status_message1(SM_ORDER, 3, 4, "Cannot spawn command : %s", cmd);
+
+ fs_give((void **)&command);
+ if(result_file)
+ fs_give((void **)&result_file);
+ }
+#else
+ char *command = NULL,
+ *result_file = NULL,
+ *p, *cmd;
+ char **r_file_h;
+ PIPE_S *syspipe;
+ int mode;
+ size_t l;
+
+ /* no os-specific command handling */
+ if(mc_cmd)
+ cmd = mc_cmd->command;
+ else
+ return;
+ l = 32 + strlen(cmd) + 2*strlen(image_file);
+ p = command = (char *)fs_get((l+1) * sizeof(char));
+ if(!needsterminal) /* put in background if it doesn't need terminal */
+ *p++ = '(';
+ snprintf(p, l+1-(p-command), "%s ; rm -f %s", cmd, image_file);
+ command[l] = '\0';
+ p += strlen(p);
+ if(!needsterminal && (p-command)+5 < l){
+ *p++ = ')';
+ *p++ = ' ';
+ *p++ = '&';
+ }
+
+ *p++ = '\n';
+ *p = '\0';
+
+ dprint((9, "exec_mailcap_cmd: command=%s\n",
+ command ? command : "?"));
+
+ mode = PIPE_RESET;
+ if(needsterminal == 1)
+ r_file_h = NULL;
+ else{
+ mode |= PIPE_WRITE | PIPE_STDERR;
+ result_file = temp_nam(NULL, "pine_cmd");
+ r_file_h = &result_file;
+ }
+
+ if((syspipe = open_system_pipe(command, r_file_h, NULL, mode, 0, pipe_callback, NULL)) != NULL){
+ close_system_pipe(&syspipe, NULL, pipe_callback);
+ if(needsterminal == 1)
+ q_status_message(SM_ORDER, 0, 4, "VIEWER command completed");
+ else if(needsterminal == 2)
+ display_output_file(result_file, "VIEWER", " command result", 1);
+ else
+ display_output_file(result_file, "VIEWER", " command launched", 1);
+ }
+ else
+ q_status_message1(SM_ORDER, 3, 4, "Cannot spawn command : %s", cmd);
+
+ fs_give((void **)&command);
+
+ if(result_file)
+ fs_give((void **)&result_file);
+#endif
+}
+
+
+/* ----------------------------------------------------------------------
+ Execute the given mailcap test= cmd
+
+ Args: cmd -- command to execute
+ Returns exit status
+
+ ----*/
+int
+exec_mailcap_test_cmd(char *cmd)
+{
+#ifdef _WINDOWS
+ return((WinExec(cmd, SW_SHOWMINNOACTIVE) < 32) ? 1 : 0);
+#else
+ PIPE_S *syspipe;
+
+ return((syspipe = open_system_pipe(cmd, NULL, NULL, PIPE_SILENT, 0,
+ pipe_callback, NULL))
+ ? close_system_pipe(&syspipe, NULL, pipe_callback) : -1);
+#endif
+}
+
+
+char *
+url_os_specified_browser(char *url)
+{
+#ifdef _WINDOWS
+ return(mswin_reg_default_browser(url));
+#elif OSX_TARGET
+ if(mime_os_specific_access()){
+ return(cpystr("open"));
+ }
+#else
+ /* do nothing here */
+ return(NULL);
+#endif
+}
+
+/*
+ * Return a pretty command, on some OS's we might do something
+ * different than just display the command.
+ *
+ * free_ret - whether or not to free the return value
+ */
+char *
+execview_pretty_command(MCAP_CMD_S *mc_cmd, int *free_ret)
+{
+ char *str;
+ int rv_to_free = 0;
+
+ if(free_ret)
+ *free_ret = rv_to_free;
+
+ if(!mc_cmd)
+ return NULL;
+
+ str = mc_cmd->command;
+
+#ifdef _WINDOWS
+ if(*str == '*' || (*str == '\"' && str[1] == '*')){
+ if(!strncmp(str + ((*str == '\"') ? 2 : 1), "DDE*", 4))
+ str = cpystr("via app already running");
+ else if(!strncmp(str + ((*str == '\"') ? 2 : 1),"ShellEx*",8))
+ str = cpystr("via Explorer defined app");
+ else
+ str = cpystr("via Windows-specific method");
+
+ rv_to_free = 1;
+ }
+#elif OSX_TARGET
+ if(mc_cmd->special_handling){
+ CFStringRef str_ref = NULL, kind_str_ref = NULL;
+ CFURLRef url_ref;
+ char buf[256];
+
+ if((str_ref = CFStringCreateWithCString(NULL, mc_cmd->command,
+ kCFStringEncodingASCII)) == NULL)
+ return "";
+
+ if((url_ref = CFURLCreateWithString(NULL, str_ref, NULL)) == NULL)
+ return "";
+
+ if(LSCopyDisplayNameForURL(url_ref, &kind_str_ref) != noErr)
+ return "";
+
+ if(CFStringGetCString(kind_str_ref, buf, (CFIndex)255,
+ kCFStringEncodingASCII) == false)
+ return "";
+
+ buf[255] = '\0';
+ str = cpystr(buf);
+ rv_to_free = 1;
+ if(kind_str_ref)
+ CFRelease(kind_str_ref);
+ }
+#else
+ /* always pretty */
+#endif
+
+ if(free_ret)
+ *free_ret = rv_to_free;
+
+ return(str);
+}
+
+
+#if OSX_TARGET
+void
+osx_launch_special_handling(MCAP_CMD_S *mc_cmd, char *image_file)
+{
+ CFStringRef str_ref = NULL;
+ CFURLRef url_ref = NULL;
+ LSLaunchFSRefSpec launch_spec;
+ FSRef app_ref, file_ref;
+ static EXEC_EVENT_DATA_S event_data;
+
+ install_app_launch_cb((void *)&event_data);
+ if((str_ref = CFStringCreateWithCString(NULL, mc_cmd->command,
+ kCFStringEncodingASCII)) == NULL)
+ return;
+ if((url_ref = CFURLCreateWithString(NULL, str_ref, NULL)) == NULL)
+ return;
+ if(CFURLGetFSRef(url_ref, &app_ref) == false)
+ return;
+ if(FSPathMakeRef((unsigned char *)image_file,
+ &file_ref, NULL) != noErr)
+ return;
+ launch_spec.appRef = &app_ref;
+ launch_spec.numDocs = 1;
+ launch_spec.itemRefs = &file_ref;
+ launch_spec.passThruParams = NULL;
+ launch_spec.launchFlags = kLSLaunchDontAddToRecents | kLSLaunchNoParams
+ | kLSLaunchAsync | kLSLaunchNewInstance;
+ /* would want to use this if we ever did true event handling */
+ launch_spec.asyncRefCon = 0;
+
+ if(LSOpenFromRefSpec( &launch_spec, NULL) == noErr){
+ /*
+ * Here's the strategy: we want to be able to just launch
+ * the app and then just delete the temp file, but that
+ * doesn't work because the called app needs the temp file
+ * at least until it's finished loading. Being that there's
+ * no way to tell when the app has finished loading, we wait
+ * until the program has exited, which is the safest thing to
+ * do and is what we do for windows. Since we haven't totally
+ * embraced event handling at this point, we must do the waiting
+ * synchronously. We allow for a keystroke to stop waiting, and
+ * just delete the temp file.
+ * Ideally, we would launch the app, and keep running, checking
+ * the events until the process terminates, and then delete the
+ * temp file. In this method, we would delete the temp file
+ * at close time if the app was still running.
+ */
+ int ch;
+ OSStatus rne_rv;
+ EventTargetRef target;
+ EventRef out_event;
+ EventTypeSpec event_types[2] = {
+ {kEventClassApplication, kEventAppTerminated},
+ {kEventClassApplication, kEventAppLaunchNotification}};
+
+ q_status_message(SM_ORDER, 0, 4,
+ "Waiting for program to finish, or press a key to stop waiting...");
+ flush_status_messages(1);
+ target = GetEventDispatcherTarget();
+ event_data.done = 0;
+ event_data.set_pid = 0;
+ while(!event_data.done){
+ if((rne_rv = ReceiveNextEvent(2, event_types, 1,
+ true, &out_event)) == noErr){
+ SendEventToEventTarget(out_event, target);
+ ReleaseEvent(out_event);
+ }
+ else if(rne_rv == eventLoopTimedOutErr){
+ ch = read_char(1);
+ if(ch)
+ event_data.done = 1;
+ }
+ else if(rne_rv == eventLoopQuitErr)
+ event_data.done = 1;
+ }
+ our_unlink(image_file);
+ }
+ q_status_message(SM_ORDER, 0, 4, "VIEWER command completed");
+}
+
+pascal OSStatus osx_launch_app_callback(EventHandlerCallRef next_h, EventRef event,void *user_data)
+{
+ EXEC_EVENT_DATA_S *ev_datap = (EXEC_EVENT_DATA_S *)user_data;
+ ProcessSerialNumber pid;
+ Boolean res = 0;
+
+ static int dont_do_anything_yet = 0;
+ switch(GetEventClass(event)){
+ case kEventClassKeyboard:
+ ev_datap->done = 1;
+ break;
+ case kEventClassApplication:
+ switch(GetEventKind(event)){
+ case kEventAppTerminated:
+ GetEventParameter(event,
+ kEventParamProcessID,
+ typeProcessSerialNumber, NULL,
+ sizeof(pid), NULL,
+ &pid);
+ SameProcess(&ev_datap->pid, &pid, &res);
+ if(res){
+ ev_datap->done = 1;
+ ev_datap->set_pid = 0;
+ }
+ break;
+ case kEventAppLaunchNotification:
+ /* could check asyncRef too */
+ if(!ev_datap->set_pid){ /* should always be true */
+ GetEventParameter(event,
+ kEventParamProcessID,
+ typeProcessSerialNumber, NULL,
+ sizeof(ev_datap->pid), NULL,
+ &(ev_datap->pid));
+ ev_datap->set_pid = 1;
+ }
+ break;
+ }
+ break;
+ }
+ return(noErr);
+}
+
+
+int
+install_app_launch_cb(void *user_data)
+{
+ static int already_installed = 0;
+
+ if(!already_installed){
+ EventHandlerUPP cb_upp;
+ EventTypeSpec event_types[2] = {
+ {kEventClassApplication, kEventAppTerminated},
+ {kEventClassApplication, kEventAppLaunchNotification}};
+
+ if((cb_upp = NewEventHandlerUPP(osx_launch_app_callback)) == NULL)
+ return 1;
+ InstallApplicationEventHandler(cb_upp, 2, event_types,
+ user_data, NULL);
+ already_installed = 1;
+ }
+ return 0;
+}
+#endif /* OSX_TARGET */
diff --git a/alpine/osdep/execview.h b/alpine/osdep/execview.h
new file mode 100644
index 00000000..b6406bfc
--- /dev/null
+++ b/alpine/osdep/execview.h
@@ -0,0 +1,27 @@
+/*
+ * $Id: execview.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_OSDEP_EXECVIEW_INCLUDED
+#define PINE_OSDEP_EXECVIEW_INCLUDED
+
+#include "../../pith/mailcap.h"
+
+/* exported prototypes */
+void exec_mailcap_cmd(MCAP_CMD_S *, char *, int);
+char *url_os_specified_browser(char *);
+char *execview_pretty_command(MCAP_CMD_S *, int *);
+
+
+#endif /* PINE_OSDEP_EXECVIEW_INCLUDED */
diff --git a/alpine/osdep/fltrname.c b/alpine/osdep/fltrname.c
new file mode 100644
index 00000000..5f2e2185
--- /dev/null
+++ b/alpine/osdep/fltrname.c
@@ -0,0 +1,119 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: fltrname.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../c-client/mail.h" /* for MAILSTREAM */
+#undef ERROR
+
+#include <system.h>
+#include <general.h>
+
+#include "../../pith/osdep/writ_dir.h"
+#include "../../pith/osdep/bldpath.h"
+
+#include "fltrname.h"
+
+
+/*----------------------------------------------------------------------
+ Filter file names for strange characters
+
+ Args: file -- the file name to check
+
+ Result: Returns NULL if file name is OK
+ Returns formatted error message if it is not
+ ----*/
+char *
+filter_filename(char *file, int *fatal, int strict)
+{
+#define ALLOW_WEIRD 1
+#ifdef ALLOW_WEIRD
+ static char illegal[] = {'\177', '\0'};
+#else
+#ifdef _WINDOWS
+ static char illegal[] = {'"', '#', '$', '%', '&', /*'/',*/ '(', ')','*',
+ ',', ';', '<', '=', '>', '?', '[', ']',
+ '^', '|', '\177', '\0'};
+#else /* UNIX */
+ static char illegal[] = {'"', '#', '$', '%', '&', '\'','(', ')','*',
+ ',', ':', ';', '<', '=', '>', '?', '[', ']',
+ '\\', '^', '|', '\177', '\0'};
+#endif /* UNIX */
+#endif
+ static char error[100];
+ char ill_file[MAXPATH+1], *ill_char, *ptr, e2[20];
+ int i;
+
+ for(ptr = file; *ptr == ' '; ptr++) ; /* leading spaces gone */
+
+ while(*ptr && (unsigned char)(*ptr) >= ' ' && strchr(illegal, *ptr) == 0)
+ ptr++;
+
+ *fatal = TRUE;
+ if(*ptr != '\0') {
+ if(*ptr == '\n') {
+ ill_char = "<newline>";
+ } else if(*ptr == '\r') {
+ ill_char = "<carriage return>";
+ } else if(*ptr == '\t') {
+ ill_char = "<tab>";
+ *fatal = FALSE; /* just whitespace */
+ } else if(*ptr < ' ') {
+ snprintf(e2, sizeof(e2), "control-%c", *ptr + '@');
+ ill_char = e2;
+ } else if (*ptr == '\177') {
+ ill_char = "<del>";
+ } else {
+ e2[0] = *ptr;
+ e2[1] = '\0';
+ ill_char = e2;
+ *fatal = FALSE; /* less offensive? */
+ }
+ if(!*fatal){
+ strncpy(error, ill_char, sizeof(error)-1);
+ error[sizeof(error)-1] = '\0';
+ }
+ else if(ptr != file) {
+ strncpy(ill_file, file, MIN(ptr-file,sizeof(ill_file)-1));
+ ill_file[MIN(ptr-file,sizeof(ill_file)-1)] = '\0';
+ snprintf(error, sizeof(error),
+ "Character \"%s\" after \"%.*s\" not allowed in file name",
+ ill_char, sizeof(error)-50, ill_file);
+ } else {
+ snprintf(error, sizeof(error),
+ "First character, \"%s\", not allowed in file name",
+ ill_char);
+ }
+
+ return(error);
+ }
+
+ if((i=is_writable_dir(file)) == 0 || i == 1){
+ snprintf(error, sizeof(error), "\"%.*s\" is a directory", sizeof(error)-50, file);
+ return(error);
+ }
+
+ if(strict){
+ for(ptr = file; *ptr == ' '; ptr++) ; /* leading spaces gone */
+
+ if((ptr[0] == '.' && ptr[1] == '.') || filename_parent_ref(ptr)){
+ snprintf(error, sizeof(error), "\"..\" not allowed in filename");
+ return(error);
+ }
+ }
+
+ return((char *)NULL);
+}
diff --git a/alpine/osdep/fltrname.h b/alpine/osdep/fltrname.h
new file mode 100644
index 00000000..523c641d
--- /dev/null
+++ b/alpine/osdep/fltrname.h
@@ -0,0 +1,26 @@
+/*
+ * $Id: fltrname.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_OSDEP_FLTRNAME_INCLUDED
+#define PINE_OSDEP_FLTRNAME_INCLUDED
+
+
+/*
+ * Exported Prototypes
+ */
+char *filter_filename(char *, int *, int);
+
+
+#endif /* PINE_OSDEP_FLTRNAME_INCLUDED */
diff --git a/alpine/osdep/jobcntrl.c b/alpine/osdep/jobcntrl.c
new file mode 100644
index 00000000..38169822
--- /dev/null
+++ b/alpine/osdep/jobcntrl.c
@@ -0,0 +1,55 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: jobcntrl.c 765 2007-10-23 23:51:37Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+
+#include "jobcntrl.h"
+
+
+/*----------------------------------------------------------------------
+ This routine returns 1 if job control is available. Note, thiis
+ could be some type of fake job control. It doesn't have to be
+ real BSD-style job control.
+ ----*/
+int
+have_job_control(void)
+{
+ return 1;
+}
+
+
+/*----------------------------------------------------------------------
+ If we don't have job control, this routine is never called.
+ ----*/
+void
+stop_process(void)
+{
+#ifndef _WINDOWS
+ RETSIGTYPE (*save_usr2)(int);
+
+ /*
+ * Since we can't respond to KOD while stopped, the process that sent
+ * the KOD is going to go read-only. Therefore, we can safely ignore
+ * any KODs that come in before we are ready to respond...
+ */
+ save_usr2 = signal(SIGUSR2, SIG_IGN);
+ kill(0, SIGSTOP);
+ (void)signal(SIGUSR2, save_usr2);
+#endif /* !_WINDOWS */
+}
+
+
diff --git a/alpine/osdep/jobcntrl.h b/alpine/osdep/jobcntrl.h
new file mode 100644
index 00000000..3908cce7
--- /dev/null
+++ b/alpine/osdep/jobcntrl.h
@@ -0,0 +1,25 @@
+/*
+ * $Id: jobcntrl.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_OSDEP_JOBCNTRL_INCLUDED
+#define PINE_OSDEP_JOBCNTRL_INCLUDED
+
+
+/* exported protoypes */
+int have_job_control(void);
+void stop_process(void);
+
+
+#endif /* PINE_OSDEP_JOBCNTRL_INCLUDED */
diff --git a/alpine/osdep/makefile.wnt b/alpine/osdep/makefile.wnt
new file mode 100644
index 00000000..07afeb63
--- /dev/null
+++ b/alpine/osdep/makefile.wnt
@@ -0,0 +1,66 @@
+# $Id: makefile.wnt 14098 2005-10-03 18:54:13Z jpf@u.washington.edu $
+#
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+#
+#
+# Makefile for WIN NT version of the libalpineosd.lib
+#
+#
+CC=cl
+RM=del
+CP=copy
+RC=rc
+
+#includes symbol info for debugging
+CDEBUG= #-Zi -Od
+LDEBUG= /DEBUG /DEBUGTYPE:CV
+
+STDCFLAGS= -I..\..\include -I..\..\regex -nologo -MT -DWIN32 -DDOS -D_WINDOWS -DJOB_CONTROL -DMSC_MALLOC
+
+CFLAGS= $(CDEBUG) $(STDCFLAGS) $(NET) $(EXTRACFLAGS)
+
+LFLAGS= $(LDEBUG) $(EXTRALDFLAGS)
+
+RCFLAGS =
+
+LIBER=lib
+LIBARGS=/nologo /verbose
+
+HFILES= ../../include/system.h ../../include/general.h \
+ debuging.h execview.h fltrname.h jobcntrl.h print.h resource.h termin.gen.h \
+ termin.wnt.h termout.gen.h termout.wnt.h windlg.h
+
+OFILES= chnge_pw.obj debuging.obj diskquot.non.obj execview.obj fltrname.obj \
+ jobcntrl.obj print.obj termin.gen.obj termin.wnt.obj termout.gen.obj \
+ termout.wnt.obj mswinver.obj
+
+all: libalpineosd.lib mswin.res
+
+.c.obj:
+ $(CC) -c $(CFLAGS) "$(MAKEDIR)"\$*.c
+
+$(OFILES): $(HFILES)
+
+libalpineosd.lib: $(OFILES)
+ $(RM) libalpineosd.lib || rem
+ $(LIBER) /out:libalpineosd.lib $(OFILES)
+
+mswin.res: ../../pico/osdep/mswinhnd.cur alpine-splash.bmp resource.h \
+ mswin.rc alpine.ico newmail.ico mclosed.ico
+ $(RC) $(RCFLAGS) /fo mswin.res mswin.rc
+
+
+clean:
+ $(RM) *.lib
+ $(RM) *.obj
+ $(RM) mswin.res
diff --git a/alpine/osdep/mclosed.ico b/alpine/osdep/mclosed.ico
new file mode 100644
index 00000000..0c6306b3
--- /dev/null
+++ b/alpine/osdep/mclosed.ico
Binary files differ
diff --git a/alpine/osdep/mswin.def b/alpine/osdep/mswin.def
new file mode 100644
index 00000000..0b262412
--- /dev/null
+++ b/alpine/osdep/mswin.def
@@ -0,0 +1,17 @@
+NAME PINE
+EXETYPE WINDOWS
+DESCRIPTION 'PC-Pine for Windows (Character)'
+STUB 'winstub.exe'
+
+CODE PRELOAD DISCARDABLE SHARED
+DATA PRELOAD MULTIPLE
+
+HEAPSIZE 100
+
+EXPORTS
+ PWNDPROC @1
+ TWWNDPROC @2
+ ABOUTDLGPROC @3
+
+
+
diff --git a/alpine/osdep/mswin.rc b/alpine/osdep/mswin.rc
new file mode 100644
index 00000000..b4a8533a
--- /dev/null
+++ b/alpine/osdep/mswin.rc
@@ -0,0 +1,605 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#define APSTUDIO_HIDDEN_SYMBOLS
+#include "windows.h"
+#undef APSTUDIO_HIDDEN_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+ALPINEICON ICON DISCARDABLE "ALPINE.ICO"
+NEWMAILICON ICON DISCARDABLE "NEWMAIL.ICO"
+MCLOSEDICON ICON DISCARDABLE "MCLOSED.ICO"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Bitmap
+//
+
+ALPINESPLASH BITMAP MOVEABLE PURE "ALPINE-SPLASH.BMP"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Cursor
+//
+
+PICOHAND CURSOR "..\..\PICO\OSDEP\MSWINHND.CUR"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+ALPINEMENU MENU DISCARDABLE
+BEGIN
+ POPUP "&File"
+ BEGIN
+ MENUITEM "&Print", IDM_MI_PRINT
+ MENUITEM SEPARATOR
+ MENUITEM "&Read File", IDM_MI_READFILE
+ MENUITEM SEPARATOR
+ MENUITEM "E&xit", IDM_MI_EXIT
+ END
+ POPUP "&Edit"
+ BEGIN
+ MENUITEM "Cu&t\tCtrl-Shift-X", IDM_EDIT_CUT
+ MENUITEM "&Copy\tCtrl-Shift-C", IDM_EDIT_COPY
+ MENUITEM "Copy Append\tCtrl-Alt-C", IDM_EDIT_COPY_APPEND
+ MENUITEM "&Paste\tCtrl-Shift-V", IDM_EDIT_PASTE
+ MENUITEM "Cance&l Paste!\tCtrl-Alt-V", IDM_EDIT_CANCEL_PASTE
+ MENUITEM SEPARATOR
+ MENUITEM "&Find\tCtrl-Shift-F", IDM_MI_WHEREIS
+ END
+ POPUP "&Go"
+ BEGIN
+ MENUITEM "&Main Menu", IDM_MI_MAINMENU
+ MENUITEM "&List Folders", IDM_MI_FLDRLIST
+ MENUITEM "&Index", IDM_MI_FLDRINDEX
+ MENUITEM "&Goto Folder", IDM_MI_GOTOFLDR
+ MENUITEM "E&xit Current Mode", IDM_MI_EXITMODE
+ MENUITEM SEPARATOR
+ MENUITEM "&Previous Message", IDM_MI_PREVMSG
+ MENUITEM "&Next Message", IDM_MI_NEXTMSG
+ MENUITEM "&Jump To Message", IDM_MI_JUMPTOMSG
+ MENUITEM "&Where Is", IDM_MI_WHEREIS
+ END
+ POPUP "&Message"
+ BEGIN
+ MENUITEM "&View", IDM_MI_VIEW
+ MENUITEM "ViewInNew&Window", IDM_MI_VIEWINWIND
+ MENUITEM "Pr&int", IDM_MI_PRINT
+ MENUITEM "&Save", IDM_MI_SAVE
+ MENUITEM "&Export", IDM_MI_EXPORT
+ MENUITEM SEPARATOR
+ MENUITEM "&Delete", IDM_MI_DELETE
+ POPUP "&Flag"
+ BEGIN
+ MENUITEM "&Important", IDM_MI_FLAGIMPORTANT
+ MENUITEM "&New", IDM_MI_FLAGNEW
+ MENUITEM "&Answered", IDM_MI_FLAGANSWERED
+ MENUITEM "&Deleted", IDM_MI_FLAGDELETED
+ END
+ MENUITEM "E&xpunge", IDM_MI_EXPUNGE
+ MENUITEM SEPARATOR
+ MENUITEM "&Take Address", IDM_MI_TAKEADDR
+ MENUITEM "&Header Mode", IDM_MI_HDRMODE
+ POPUP "S&ort"
+ BEGIN
+ MENUITEM "Arrival", IDM_MI_SORTARRIVAL
+ MENUITEM "Date", IDM_MI_SORTDATE
+ MENUITEM "From", IDM_MI_SORTFROM
+ MENUITEM "To", IDM_MI_SORTTO
+ MENUITEM "Cc", IDM_MI_SORTCC
+ MENUITEM "Subject", IDM_MI_SORTSUBJECT
+ MENUITEM "OrderedSubject", IDM_MI_SORTORDERSUB
+ MENUITEM "Size", IDM_MI_SORTSIZE
+ MENUITEM "Score", IDM_MI_SORTSCORE
+ MENUITEM "Thread", IDM_MI_SORTTHREAD
+ MENUITEM SEPARATOR
+ MENUITEM "Reverse", IDM_MI_SORTREVERSE
+ END
+ MENUITEM "Se&lect", IDM_MI_SELECT
+ MENUITEM "&Apply", IDM_MI_APPLY
+ MENUITEM "&Zoom", IDM_MI_ZOOM
+ END
+ POPUP "&Send"
+ BEGIN
+ MENUITEM "&Compose Message", IDM_MI_COMPOSER
+ MENUITEM "&Reply", IDM_MI_REPLY
+ MENUITEM "&Forward", IDM_MI_FORWARD
+ MENUITEM "&Bounce", IDM_MI_BOUNCE
+ END
+ POPUP "&Config"
+ BEGIN
+ MENUITEM "Set &Window Font", IDM_OPT_SETFONT
+ MENUITEM "Print Font &Same As Window", IDM_OPT_FONTSAMEAS
+ MENUITEM "Set &Printer Font", IDM_OPT_SETPRINTFONT
+ MENUITEM SEPARATOR
+ MENUITEM "&Toolbar", IDM_OPT_TOOLBAR
+ MENUITEM "Tool&bar on Top", IDM_OPT_TOOLBARPOS
+ POPUP "&Cursor"
+ BEGIN
+ MENUITEM "Block", IDM_OPT_CARETBLOCK
+ MENUITEM "Small Block", IDM_OPT_CARETSMALLBLOCK
+ MENUITEM "Underline", IDM_OPT_CARETHBAR
+ MENUITEM "Vertical Bar", IDM_OPT_CARETVBAR
+ END
+ MENUITEM SEPARATOR
+ MENUITEM "Use &Dialog Boxes", IDM_OPT_USEDIALOGS
+ MENUITEM "Erase Preserved &Passwords", IDM_OPT_ERASE_CREDENTIALS
+ MENUITEM SEPARATOR
+ MENUITEM "&IMAP Telemetry", IDM_OPT_IMAPTELEM
+ MENUITEM "&New Mail Window", IDM_OPT_NEWMAILWIN
+ END
+ POPUP "&Help"
+ BEGIN
+ MENUITEM "&Screen Help", IDM_MI_SCREENHELP
+ MENUITEM "&About", IDM_ABOUT
+ END
+END
+
+TEXTWINMENU MENU DISCARDABLE
+BEGIN
+ POPUP "&File"
+ BEGIN
+ MENUITEM "&Print", IDM_FILE_PRINT
+ MENUITEM SEPARATOR
+ MENUITEM "&Close", IDM_FILE_CLOSE
+ END
+ POPUP "&Edit"
+ BEGIN
+ MENUITEM "&Copy\tCtrl-Shift-C", IDM_EDIT_COPY
+ MENUITEM "Select All", IDM_EDIT_SEL_ALL
+ END
+END
+
+COMPOSERMENU MENU DISCARDABLE
+BEGIN
+ POPUP "&File"
+ BEGIN
+ MENUITEM "&Print", IDM_MI_PRINT
+ MENUITEM SEPARATOR
+ MENUITEM "&Read File", 181
+ MENUITEM SEPARATOR
+ MENUITEM "E&xit", IDM_MI_EXIT
+ END
+ POPUP "&Edit"
+ BEGIN
+ MENUITEM "Cu&t\tCtrl-Shift-X", IDM_EDIT_CUT
+ MENUITEM "&Copy\tCtrl-Shift-C", IDM_EDIT_COPY
+ MENUITEM "Copy Append\tCtrl-Alt-C", IDM_EDIT_COPY_APPEND
+ MENUITEM "&Paste\tCtrl-Shift-V", IDM_EDIT_PASTE
+ MENUITEM "Cance&l Paste!\tCtrl-Alt-V", IDM_EDIT_CANCEL_PASTE
+ MENUITEM SEPARATOR
+ MENUITEM "Select &All", IDM_EDIT_SEL_ALL
+ MENUITEM SEPARATOR
+ MENUITEM "&Find\tCtrl-Shift-F", IDM_MI_WHEREIS
+ MENUITEM SEPARATOR
+ MENUITEM "&Spell", IDM_MI_SPELLCHK
+ END
+ POPUP "&Compose"
+ BEGIN
+ MENUITEM "P&ostpone", IDM_MI_POSTPONE
+ MENUITEM "&Send", IDM_MI_SEND
+ MENUITEM "&Cancel", IDM_MI_CANCEL
+ MENUITEM SEPARATOR
+ MENUITEM "&Attach", IDM_MI_ATTACH
+ MENUITEM "Rich &Headers", IDM_MI_RICHHDR
+ MENUITEM "&To Address Book", IDM_MI_TOADDRBOOK
+ MENUITEM SEPARATOR
+ MENUITEM "&Justify Paragraph", IDM_MI_JUSTIFY
+ MENUITEM "Run Alternate &Editor", IDM_MI_ALTEDITOR
+ END
+ POPUP "C&onfig"
+ BEGIN
+ MENUITEM "Set &Window Font", IDM_OPT_SETFONT
+ MENUITEM "Print Font &Same As Window", IDM_OPT_FONTSAMEAS
+ MENUITEM "Set &Printer Font", IDM_OPT_SETPRINTFONT
+ MENUITEM SEPARATOR
+ MENUITEM "&Toolbar", IDM_OPT_TOOLBAR
+ MENUITEM "Tool&bar on Top", IDM_OPT_TOOLBARPOS
+ POPUP "&Cursor"
+ BEGIN
+ MENUITEM "Block", IDM_OPT_CARETBLOCK
+ MENUITEM "Small Block", IDM_OPT_CARETSMALLBLOCK
+ MENUITEM "Underline", IDM_OPT_CARETHBAR
+ MENUITEM "Vertical Bar", IDM_OPT_CARETVBAR
+ END
+ MENUITEM SEPARATOR
+ MENUITEM "Use &Dialog Boxes", IDM_OPT_USEDIALOGS
+ END
+ POPUP "&Help"
+ BEGIN
+ MENUITEM "&Screen Help", IDM_MI_SCREENHELP
+ MENUITEM "&About", IDM_ABOUT
+ END
+END
+
+#ifndef _MAC
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 2,01,0,0
+ PRODUCTVERSION 2,01,0,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "Comments", "see http://www.washington.edu/alpine\0"
+ VALUE "CompanyName", "University of Washington\0"
+#ifdef _PCP_W2K
+ VALUE "FileDescription", "Alpine with krb5 for Windows\0"
+#else
+ VALUE "FileDescription", "Alpine\0"
+#endif
+ VALUE "FileVersion", "2.10\0"
+ VALUE "InternalName", "alpine\0"
+ VALUE "LegalCopyright", "Copyright 2006-2009\0"
+ VALUE "OriginalFilename", "alpine.exe\0"
+ VALUE "ProductName", "alpine\0"
+ VALUE "ProductVersion", "2.10\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+#endif // !_MAC
+
+
+#include "..\..\pico\osdep\mswin_spell.dlg"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+ABOUTDLGBOX DIALOGEX 10, 25, 217, 80
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Alpine"
+FONT 8, "Helv", 0, 0, 0x1
+BEGIN
+ CTEXT "Alpine for Windows%S\nVersion %d.%d%S\nBuild (%S)",
+ IDD_VERSION,47,12,110,26,SS_NOPREFIX | NOT WS_GROUP,
+ WS_EX_TRANSPARENT
+ LTEXT "",IDD_BYLINE,21,46,172,56,SS_NOPREFIX | NOT WS_GROUP
+ DEFPUSHBUTTON "OK",IDD_OK,159,15,40,14
+ ICON ALPINEICON,IDC_STATIC,25,15,20,20
+END
+
+SPLASHDLGBOX DIALOGEX 0, 0, 520, 316
+STYLE WS_POPUP | DS_SETFOREGROUND | DS_LOCALEDIT
+BEGIN
+END
+
+IDD_TOOLBAR DIALOG DISCARDABLE 0, 0, 311, 12
+STYLE WS_CHILD
+FONT 8, "MS Sans Serif"
+BEGIN
+ PUSHBUTTON "Quit",IDM_MI_EXIT,1,1,25,11,NOT WS_TABSTOP
+ PUSHBUTTON "Main Menu",IDM_MI_MAINMENU,26,1,40,11,NOT WS_TABSTOP
+ PUSHBUTTON "Folders",IDM_MI_FLDRLIST,66,1,29,11,NOT WS_TABSTOP
+ PUSHBUTTON "Index",IDM_MI_FLDRINDEX,95,1,25,11,NOT WS_TABSTOP
+ PUSHBUTTON "View",IDM_MI_VIEW,120,1,24,11,NOT WS_TABSTOP
+ PUSHBUTTON "Compose",IDM_MI_COMPOSER,144,1,37,11,NOT WS_TABSTOP
+ PUSHBUTTON "Reply",IDM_MI_REPLY,181,1,27,11,NOT WS_TABSTOP
+ PUSHBUTTON "Forward",IDM_MI_FORWARD,208,1,34,11,NOT WS_TABSTOP
+END
+
+IDD_COMPOSER_TB DIALOG DISCARDABLE 0, 0, 311, 12
+STYLE WS_CHILD
+FONT 8, "MS Sans Serif"
+BEGIN
+ PUSHBUTTON "Send",IDM_MI_SEND,1,1,25,11
+ PUSHBUTTON "Cancel",IDM_MI_CANCEL,26,1,29,11
+ PUSHBUTTON "Postpone",IDM_MI_POSTPONE,55,1,37,11
+ PUSHBUTTON "Attach",IDM_MI_ATTACH,92,1,29,11
+ PUSHBUTTON "Find",IDM_MI_WHEREIS,121,1,22,11
+ PUSHBUTTON "Spell",IDM_MI_SPELLCHK,143,1,23,11
+END
+
+IDD_OPTIONALYENTER DIALOG DISCARDABLE 30, 30, 255, 86
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Alpine"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,7,52,79,14
+ PUSHBUTTON "Cancel",IDCANCEL,7,68,78,14
+ LTEXT "",IDC_PROMPT,6,9,231,18
+ EDITTEXT IDC_RESPONCE,3,33,237,12,ES_AUTOHSCROLL | ES_NOHIDESEL
+ DEFPUSHBUTTON "Help '^G'",IDC_GETHELP,87,52,79,14
+END
+
+IDD_LOGINDLG DIALOG DISCARDABLE 0, 0, 213, 111
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_MINIMIZEBOX |
+ WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Alpine Login"
+FONT 8, "MS Sans Serif"
+BEGIN
+ ICON ALPINEICON,IDC_STATIC,7,10,21,20
+ LTEXT "Authentication required.",IDC_STATIC,44,12,152,8
+ LTEXT "Host :",IDC_PROMPT,44,23,165,8
+ LTEXT "Login: ",IDC_STATIC,20,44,20,8
+ EDITTEXT IDC_RLOGINE,45,42,133,12,ES_AUTOHSCROLL
+ LTEXT "Password: ",IDC_RPWTEXT,7,57,36,8
+ EDITTEXT IDC_RPASSWORD,45,55,133,12,ES_PASSWORD | ES_AUTOHSCROLL
+ CONTROL "Preserve this password for future logins",
+ IDC_PRESPASS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,45,70,
+ 165,8
+ DEFPUSHBUTTON "OK",IDOK,47,90,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,106,90,50,14
+END
+
+IDD_LOGINDLG2 DIALOG DISCARDABLE 0, 0, 281, 222
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_MINIMIZEBOX |
+ WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Alpine Login"
+FONT 8, "MS Sans Serif"
+BEGIN
+ ICON ALPINEICON,IDC_STATIC,7,10,21,20
+ LTEXT "Authentication required.\n\nIn order to set up Alpine, please authenticate to the IMAP server where your configuration will be stored.",IDC_STATIC,44,24,180,50
+ LTEXT "Host:",IDC_PROMPT,59,80,165,8
+ LTEXT "Login: ",IDC_STATIC,57,98,20,8
+ EDITTEXT IDC_RLOGINE,82,96,133,12,ES_AUTOHSCROLL
+ LTEXT "Password: ",IDC_RPWTEXT,44,120,36,8
+ EDITTEXT IDC_RPASSWORD,82,118,133,12,ES_PASSWORD | ES_AUTOHSCROLL
+ CONTROL "Preserve this password for future logins",
+ IDC_PRESPASS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,82,138,
+ 165,8
+ DEFPUSHBUTTON "OK",IDOK,65,196,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,131,196,50,14
+END
+
+
+IDD_CONFIGDLG DIALOG DISCARDABLE 0, 0, 281, 222
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_MINIMIZEBOX |
+ WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Alpine Configuration Setup"
+FONT 8, "MS Sans Serif"
+BEGIN
+ ICON ALPINEICON,IDC_STATIC,7,10,21,20
+ LTEXT "Alpine was not able to locate your personal configuration file. Please specify where Alpine should look for (or create) it.",
+ IDC_CONFTEXT,45,14,211,17
+ GROUPBOX "",IDC_STATIC,10,34,259,88
+ CONTROL "Use configuration file stored on IMAP server",
+ IDC_CONFRRADIO,"Button",BS_AUTORADIOBUTTON,17,42,154,10
+ LTEXT "IMAP Server :",IDC_CONFSRVRTXT,31,54,45,8
+ EDITTEXT IDC_CONFESERVER,77,52,138,14,ES_AUTOHSCROLL
+ LTEXT "Username :",IDC_CONFUNTXT,115,70,37,8
+ EDITTEXT IDC_CONFEUSERNAME,154,68,62,14,ES_AUTOHSCROLL
+ CONTROL "Use default configuration folder name",IDC_CONFDFLTFLDR,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,31,89,133,10
+ LTEXT "Configuration folder name :",IDC_CONFFLDRTXT,31,104,85,8
+ EDITTEXT IDC_CONFEFLDRNAME,117,102,86,14,ES_AUTOHSCROLL
+ GROUPBOX "",IDC_STATIC,11,126,259,43
+ CONTROL "Use local configuration file",IDC_CONFLRADIO,"Button",
+ BS_AUTORADIOBUTTON,17,135,99,10
+ LTEXT "Configuration file name :",IDC_CONFFNTXT,34,148,76,8
+ EDITTEXT IDC_CONFEFN,113,146,89,14,ES_AUTOHSCROLL
+ PUSHBUTTON "Browse",IDC_CONFBROWSE,207,145,50,14
+ CONTROL "Use this as the default Alpine Configuration",
+ IDC_CONFDFLTSET,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,178,
+ 157,10
+ DEFPUSHBUTTON "OK",IDOK,65,196,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,131,196,50,14
+END
+
+IDD_CFVARSDLG DIALOG DISCARDABLE 0, 0, 281, 222
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_MINIMIZEBOX |
+ WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Alpine Configuration Setup"
+FONT 8, "MS Sans Serif"
+BEGIN
+ ICON ALPINEICON,IDC_STATIC,7,10,21,20
+ LTEXT "In order to properly set up Alpine, please fill out the following information, which can also be provided later.",
+ IDC_STATIC,45,14,211,17
+ LTEXT "Personal Name :",IDC_STATIC,33,46,53,8
+ EDITTEXT IDC_CFV_PNAME,90,43,157,14,ES_AUTOHSCROLL
+ LTEXT "Email Address :",IDC_STATIC,37,66,49,8
+ EDITTEXT IDC_CFV_EMAILADR,90,63,157,14,ES_AUTOHSCROLL
+ LTEXT "Mail Server :",IDC_STATIC,45,86,41,8
+ EDITTEXT IDC_CFV_MSERVER,90,83,157,14,ES_AUTOHSCROLL
+ CONTROL "This server is an IMAP server",IDC_CFV_IMAP,"Button",
+ BS_AUTORADIOBUTTON,141,100,108,10
+ CONTROL "This server is a POP3 server",IDC_CFV_POP3,"Button",
+ BS_AUTORADIOBUTTON,141,113,105,10
+ LTEXT "Login name (optional) :",IDC_STATIC,120,129,72,8
+ EDITTEXT IDC_CFV_LOGIN,195,126,51,14,ES_AUTOHSCROLL
+ LTEXT "SMTP Server :",IDC_STATIC,39,149,48,8
+ EDITTEXT IDC_CFV_SMTPSERVER,90,146,157,14,ES_AUTOHSCROLL
+ CONTROL "Make Alpine the default E-mail reader",
+ IDC_CFV_DEFMAILER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 90,165,157,10
+ CONTROL "Make Alpine the default Newsgroup reader",
+ IDC_CFV_DEFNEWSRDR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+ 90,177,157,10
+ DEFPUSHBUTTON "OK",IDOK,65,196,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,131,196,50,14
+END
+
+IDD_SELECT DIALOG DISCARDABLE 30, 30, 255, 66
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Alpine"
+FONT 8, "MS Sans Serif"
+BEGIN
+ PUSHBUTTON "Help",IDC_GETHELP,7,50,78,14
+ PUSHBUTTON "Cancel",IDCANCEL,7,33,78,14
+ LTEXT "",IDC_PROMPT,7,7,231,22
+ CONTROL "User1",IDC_RESPONCE,"KeyboardCapture",NOT WS_VISIBLE,
+ 208,49,35,12
+END
+
+IDD_SELECTSORT DIALOG DISCARDABLE 30, 30, 170, 102
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Select Sort"
+FONT 8, "MS Sans Serif"
+BEGIN
+ CONTROL "Arrival",IDC_SORTARRIVAL,"Button",BS_AUTORADIOBUTTON |
+ WS_GROUP | WS_TABSTOP,11,6,74,9
+ CONTROL "From",IDC_SORTFROM,"Button",BS_AUTORADIOBUTTON |
+ WS_TABSTOP,96,6,74,9
+ CONTROL "Subject",IDC_SORTSUBJECT,"Button",BS_AUTORADIOBUTTON |
+ WS_TABSTOP,11,18,74,9
+ CONTROL "To",IDC_SORTTO,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,
+ 96,18,74,9
+ CONTROL "Ordered Subject",IDC_SORTORDERSUB,"Button",
+ BS_AUTORADIOBUTTON | WS_TABSTOP,11,30,74,9
+ CONTROL "Cc",IDC_SORTCC,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,
+ 96,30,74,9
+ CONTROL "Size",IDC_SORTSIZE,"Button",BS_AUTORADIOBUTTON |
+ WS_TABSTOP,11,42,74,9
+ CONTROL "Date",IDC_SORTDATE,"Button",BS_AUTORADIOBUTTON |
+ WS_TABSTOP,96,42,74,9
+ CONTROL "Thread",IDC_SORTTHREAD,"Button",BS_AUTORADIOBUTTON |
+ WS_TABSTOP,11,54,74,9
+ CONTROL "Score",IDC_SORTSCORE,"Button",BS_AUTORADIOBUTTON |
+ WS_TABSTOP,96,54,74,9
+ CONTROL "Reverse Sort",IDC_SORTREVERSE,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,55,67,58,12
+ DEFPUSHBUTTON "OK",IDOK,5,82,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,60,82,50,14
+ PUSHBUTTON "Help",IDC_GETHELP,115,82,50,14
+END
+
+IDD_SELECTFLAG DIALOG DISCARDABLE 30, 30, 134, 42
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Select Flags"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,11,22,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,72,22,50,14
+ CONTROL "Flag 1",IDC_FLAGCOL1,"Button",BS_AUTOCHECKBOX | NOT
+ WS_VISIBLE | WS_TABSTOP,4,4,59,10
+ CONTROL "Flag 2",IDC_FLAGCOL2,"Button",BS_AUTOCHECKBOX | NOT
+ WS_VISIBLE | WS_TABSTOP,70,4,59,10
+END
+
+IDD_ARGLIST DIALOG DISCARDABLE 0, 0, 302, 223
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Alpine - Command Line Arguments"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,124,196,50,14,BS_CENTER
+ EDITTEXT IDC_ARGTEXT,7,7,287,182,ES_MULTILINE | ES_AUTOHSCROLL |
+ ES_READONLY | WS_VSCROLL | WS_HSCROLL
+END
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#define APSTUDIO_HIDDEN_SYMBOLS\r\n"
+ "#include ""windows.h""\r\n"
+ "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n"
+ "\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ IDD_ARGLIST, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 295
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 414
+ HORZGUIDE, 160
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_BYLINE "Copyright 2006-2009 University of Washington"
+ IDS_APPNAME "Alpine"
+ IDS_APPIDENT "alpine"
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/alpine/osdep/mswinver.c b/alpine/osdep/mswinver.c
new file mode 100644
index 00000000..0c683520
--- /dev/null
+++ b/alpine/osdep/mswinver.c
@@ -0,0 +1,74 @@
+/*
+ * ========================================================================
+ * Copyright 2006-2009 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#define VER_MAJOR 2
+#define VER_MINOR 1
+extern char datestamp[];
+
+
+/*
+ * Return major version number...
+ */
+int
+mswin_majorver()
+{
+ return(VER_MAJOR);
+}
+
+
+/*
+ * Return minor version number...
+ */
+int
+mswin_minorver()
+{
+ return(VER_MINOR);
+}
+
+
+/*
+ * Return compilation number...
+ */
+char *
+mswin_compilation_date()
+{
+ return(datestamp);
+}
+
+
+/*
+ * Return special remarks...
+ */
+char *
+mswin_compilation_remarks()
+{
+#ifdef SPCL_REMARKS
+ return(SPCL_REMARKS);
+#else
+ return("");
+#endif
+}
+
+/*
+ * Return specific windows version...
+ */
+char *
+mswin_specific_winver()
+{
+#ifdef SPCFC_WINVER
+ return(SPCFC_WINVER);
+#else
+ return("");
+#endif
+}
diff --git a/alpine/osdep/newmail.ico b/alpine/osdep/newmail.ico
new file mode 100644
index 00000000..47783cb8
--- /dev/null
+++ b/alpine/osdep/newmail.ico
Binary files differ
diff --git a/alpine/osdep/print.c b/alpine/osdep/print.c
new file mode 100644
index 00000000..01720ece
--- /dev/null
+++ b/alpine/osdep/print.c
@@ -0,0 +1,529 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: print.c 942 2008-03-04 18:21:33Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+#include <system.h>
+#include <general.h>
+
+#include "../c-client/mail.h" /* for MAILSTREAM and friends */
+#include "../c-client/osdep.h"
+#include "../c-client/rfc822.h" /* for soutr_t and such */
+#include "../c-client/misc.h" /* for cpystr proto */
+#include "../c-client/utf8.h" /* for CHARSET and such*/
+#include "../c-client/imap4r1.h"
+
+#include "../../pith/charconv/utf8.h"
+#include "../../pith/charconv/filesys.h"
+
+#include "../../pith/osdep/color.h"
+#include "../../pith/osdep/temp_nam.h"
+#include "../../pith/osdep/err_desc.h"
+#include "../../pith/osdep/collate.h"
+
+#include "../../pith/debug.h"
+#include "../../pith/conf.h"
+#include "../../pith/store.h"
+#include "../../pith/filttype.h"
+
+#include "../../pico/estruct.h" /* for ctrl() */
+#include "../../pico/keydefs.h" /* for KEY_* */
+
+#include "../status.h"
+#include "../signal.h"
+#include "../radio.h"
+#include "../../pico/estruct.h"
+#include "../../pico/pico.h"
+#include "../mailview.h"
+
+#ifdef _WINDOWS
+#include "../../pico/osdep/mswin.h"
+#endif
+
+#include "../../pico/osdep/raw.h"
+
+#include "termin.gen.h"
+
+#include "print.h"
+
+
+/*======================================================================
+ print routines
+
+ Functions having to do with printing on paper and forking of spoolers
+
+ In general one calls open_printer() to start printing. One of
+ the little print functions to send a line or string, and then
+ call print_end() when complete. This takes care of forking off a spooler
+ and piping the stuff down it. No handles or anything here because there's
+ only one printer open at a time.
+
+ ====*/
+
+
+
+#ifndef _WINDOWS
+static char *trailer; /* so both open and close_printer can see it */
+static int ansi_off;
+static CBUF_S cb;
+#endif /* !_WINDOWS */
+
+
+/*----------------------------------------------------------------------
+ Open the printer
+
+ Args: desc -- Description of item to print. Should have one trailing blank.
+
+ Return value: < 0 is a failure.
+ 0 a success.
+
+This does most of the work of popen so we can save the standard output of the
+command we execute and send it back to the user.
+ ----*/
+int
+open_printer(char *desc)
+{
+#ifndef _WINDOWS
+ char command[201], prompt[200];
+ int cmd, rc, just_one;
+ char *p, *init, *nick;
+ char aname[100], wname[100];
+ char *printer;
+ int done = 0, i, lastprinter, cur_printer = 0;
+ HelpType help;
+ char **list;
+ static ESCKEY_S ekey[] = {
+ /* TRANSLATORS: these are command labels for printing screen */
+ {'y', 'y', "Y", N_("Yes")},
+ {'n', 'n', "N", N_("No")},
+ /* TRANSLATORS: go to Previous Printer in list */
+ {ctrl('P'), 10, "^P", N_("Prev Printer")},
+ {ctrl('N'), 11, "^N", N_("Next Printer")},
+ {-2, 0, NULL, NULL},
+ /* TRANSLATORS: use Custom Print command */
+ {'c', 'c', "C", N_("CustomPrint")},
+ {KEY_UP, 10, "", ""},
+ {KEY_DOWN, 11, "", ""},
+ {-1, 0, NULL, NULL}};
+#define PREV_KEY 2
+#define NEXT_KEY 3
+#define CUSTOM_KEY 5
+#define UP_KEY 6
+#define DOWN_KEY 7
+
+ trailer = NULL;
+ init = NULL;
+ nick = NULL;
+ command[sizeof(command)-1] = '\0';
+
+ if(ps_global->VAR_PRINTER == NULL){
+ q_status_message(SM_ORDER | SM_DING, 3, 5,
+ "No printer has been chosen. Use SETUP on main menu to make choice.");
+ return(-1);
+ }
+
+ /* Is there just one print command available? */
+ just_one = (ps_global->printer_category!=3&&ps_global->printer_category!=2)
+ || (ps_global->printer_category == 2
+ && !(ps_global->VAR_STANDARD_PRINTER
+ && ps_global->VAR_STANDARD_PRINTER[0]
+ && ps_global->VAR_STANDARD_PRINTER[1]))
+ || (ps_global->printer_category == 3
+ && !(ps_global->VAR_PERSONAL_PRINT_COMMAND
+ && ps_global->VAR_PERSONAL_PRINT_COMMAND[0]
+ && ps_global->VAR_PERSONAL_PRINT_COMMAND[1]));
+
+ if(F_ON(F_CUSTOM_PRINT, ps_global))
+ ekey[CUSTOM_KEY].ch = 'c'; /* turn this key on */
+ else
+ ekey[CUSTOM_KEY].ch = -2; /* turn this key off */
+
+ if(just_one){
+ ekey[PREV_KEY].ch = -2; /* turn these keys off */
+ ekey[NEXT_KEY].ch = -2;
+ ekey[UP_KEY].ch = -2;
+ ekey[DOWN_KEY].ch = -2;
+ }
+ else{
+ ekey[PREV_KEY].ch = ctrl('P'); /* turn these keys on */
+ ekey[NEXT_KEY].ch = ctrl('N');
+ ekey[UP_KEY].ch = KEY_UP;
+ ekey[DOWN_KEY].ch = KEY_DOWN;
+ /*
+ * count how many printers in list and find the default in the list
+ */
+ if(ps_global->printer_category == 2)
+ list = ps_global->VAR_STANDARD_PRINTER;
+ else
+ list = ps_global->VAR_PERSONAL_PRINT_COMMAND;
+
+ for(i = 0; list[i]; i++)
+ if(strcmp(ps_global->VAR_PRINTER, list[i]) == 0)
+ cur_printer = i;
+
+ lastprinter = i - 1;
+ }
+
+ help = NO_HELP;
+ ps_global->mangled_footer = 1;
+
+ while(!done){
+ if(init)
+ fs_give((void **)&init);
+
+ if(trailer)
+ fs_give((void **)&trailer);
+
+ if(just_one)
+ printer = ps_global->VAR_PRINTER;
+ else
+ printer = list[cur_printer];
+
+ parse_printer(printer, &nick, &p, &init, &trailer, NULL, NULL);
+ strncpy(command, p, sizeof(command)-1);
+ command[sizeof(command)-1] = '\0';
+ fs_give((void **)&p);
+ /* TRANSLATORS: Print something1 using something2.
+ For example, Print configuration using printer three. */
+ snprintf(prompt, sizeof(prompt), _("Print %s using \"%s\" ? "),
+ desc ? desc : "",
+ *nick ? nick : command);
+ prompt[sizeof(prompt)-1] = '\0';
+
+ fs_give((void **)&nick);
+
+ cmd = radio_buttons(prompt, -FOOTER_ROWS(ps_global),
+ ekey, 'y', 'x', help, RB_NORM);
+
+ switch(cmd){
+ case 'y':
+ q_status_message1(SM_ORDER, 0, 9,
+ "Printing with command \"%s\"", command);
+ done++;
+ break;
+
+ case 10:
+ cur_printer = (cur_printer>0)
+ ? (cur_printer-1)
+ : lastprinter;
+ break;
+
+ case 11:
+ cur_printer = (cur_printer<lastprinter)
+ ? (cur_printer+1)
+ : 0;
+ break;
+
+ case 'n':
+ case 'x':
+ done++;
+ break;
+
+ case 'c':
+ done++;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if(cmd == 'c'){
+ if(init)
+ fs_give((void **)&init);
+
+ if(trailer)
+ fs_give((void **)&trailer);
+
+ snprintf(prompt, sizeof(prompt), "Enter custom command : ");
+ prompt[sizeof(prompt)-1] = '\0';
+ command[0] = '\0';
+ rc = 1;
+ help = NO_HELP;
+ while(rc){
+ int flags = OE_APPEND_CURRENT;
+
+ rc = optionally_enter(command, -FOOTER_ROWS(ps_global), 0,
+ sizeof(command), prompt, NULL, help, &flags);
+
+ if(rc == 1){
+ cmd = 'x';
+ rc = 0;
+ }
+ else if(rc == 3)
+ help = (help == NO_HELP) ? h_custom_print : NO_HELP;
+ else if(rc == 0){
+ removing_trailing_white_space(command);
+ removing_leading_white_space(command);
+ q_status_message1(SM_ORDER, 0, 9,
+ "Printing with command \"%s\"", command);
+ }
+ }
+ }
+
+ if(cmd == 'x' || cmd == 'n'){
+ q_status_message(SM_ORDER, 0, 2, "Print cancelled");
+ if(init)
+ fs_give((void **)&init);
+
+ if(trailer)
+ fs_give((void **)&trailer);
+
+ return(-1);
+ }
+
+ display_message('x');
+
+ ps_global->print = (PRINT_S *)fs_get(sizeof(PRINT_S));
+ memset(ps_global->print, 0, sizeof(PRINT_S));
+
+ strncpy(aname, ANSI_PRINTER, sizeof(aname)-1);
+ aname[sizeof(aname)-1] = '\0';
+ strncat(aname, "-no-formfeed", sizeof(aname)-strlen(aname)-1);
+ strncpy(wname, WYSE_PRINTER, sizeof(wname)-1);
+ wname[sizeof(wname)-1] = '\0';
+ strncat(wname, "-no-formfeed", sizeof(wname)-strlen(wname)-1);
+ if(strucmp(command, ANSI_PRINTER) == 0
+ || strucmp(command, aname) == 0
+ || strucmp(command, WYSE_PRINTER) == 0
+ || strucmp(command, wname) == 0){
+ /*----------- Attached printer ---------*/
+ q_status_message(SM_ORDER, 0, 9,
+ "Printing to attached desktop printer...");
+ display_message('x');
+ xonxoff_proc(1); /* make sure XON/XOFF used */
+ crlf_proc(1); /* AND LF->CR xlation */
+ if(strucmp(command, ANSI_PRINTER) == 0
+ || strucmp(command, aname) == 0){
+ fputs("\033[5i", stdout);
+ ansi_off = 1;
+ }
+ else{
+ ansi_off = 0;
+ printf("%c", 18); /* aux on for wyse60,
+ Chuck Everett <ceverett@odessa.edu> */
+ }
+
+ ps_global->print->fp = stdout;
+ if(strucmp(command, ANSI_PRINTER) == 0
+ || strucmp(command, WYSE_PRINTER) == 0){
+ /* put formfeed at the end of the trailer string */
+ if(trailer){
+ int len = strlen(trailer);
+
+ fs_resize((void **)&trailer, len+2);
+ trailer[len] = '\f';
+ trailer[len+1] = '\0';
+ }
+ else
+ trailer = cpystr("\f");
+ }
+ }
+ else{
+ /*----------- Print by forking off a UNIX command ------------*/
+ dprint((4, "Printing using command \"%s\"\n",
+ command ? command : "?"));
+ ps_global->print->result = temp_nam(NULL, "pine_prt");
+ if(ps_global->print->result &&
+ (ps_global->print->pipe = open_system_pipe(command,
+ &ps_global->print->result, NULL,
+ PIPE_WRITE | PIPE_STDERR, 0,
+ pipe_callback, NULL))){
+ ps_global->print->fp = ps_global->print->pipe->out.f;
+ }
+ else{
+ if(ps_global->print->result){
+ our_unlink(ps_global->print->result);
+ fs_give((void **)&ps_global->print->result);
+ }
+
+ q_status_message1(SM_ORDER | SM_DING, 3, 4,
+ "Error opening printer: %s",
+ error_description(errno));
+ dprint((2, "Error popening printer \"%s\"\n",
+ error_description(errno)));
+ if(init)
+ fs_give((void **)&init);
+
+ if(trailer)
+ fs_give((void **)&trailer);
+
+ return(-1);
+ }
+ }
+
+ ps_global->print->err = 0;
+ if(init){
+ if(*init)
+ fputs(init, ps_global->print->fp);
+
+ fs_give((void **)&init);
+ }
+
+ cb.cbuf[0] = '\0';
+ cb.cbufp = cb.cbuf;
+ cb.cbufend = cb.cbuf;
+#else /* _WINDOWS */
+ int status;
+ LPTSTR desclpt = NULL;
+
+ if(desc)
+ desclpt = utf8_to_lptstr(desc);
+
+ if (status = mswin_print_ready (0, desclpt)) {
+ q_status_message1(SM_ORDER | SM_DING, 3, 4,
+ "Error starting print job: %s",
+ mswin_print_error(status));
+ if(desclpt)
+ fs_give((void **) &desclpt);
+
+ return(-1);
+ }
+
+ if(desclpt)
+ fs_give((void **) &desclpt);
+
+ q_status_message(SM_ORDER, 0, 9, "Printing to windows printer...");
+ display_message('x');
+
+ /* init print control structure */
+ ps_global->print = (PRINT_S *)fs_get(sizeof(PRINT_S));
+ memset(ps_global->print, 0, sizeof(PRINT_S));
+
+ ps_global->print->err = 0;
+#endif /* _WINDOWS */
+
+ return(0);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Close printer
+
+ If we're piping to a spooler close down the pipe and wait for the process
+to finish. If we're sending to an attached printer send the escape sequence.
+Also let the user know the result of the print
+ ----*/
+void
+close_printer(void)
+{
+#ifndef _WINDOWS
+ if(trailer){
+ if(*trailer)
+ fputs(trailer, ps_global->print->fp);
+
+ fs_give((void **)&trailer);
+ }
+
+ if(ps_global->print->fp == stdout) {
+ if(ansi_off)
+ fputs("\033[4i", stdout);
+ else
+ printf("%c", 20); /* aux off for wyse60 */
+
+ fflush(stdout);
+ if(F_OFF(F_PRESERVE_START_STOP, ps_global))
+ xonxoff_proc(0); /* turn off XON/XOFF */
+
+ crlf_proc(0); /* turn off CF->LF xlantion */
+ } else {
+ (void) close_system_pipe(&ps_global->print->pipe, NULL, pipe_callback);
+ display_output_file(ps_global->print->result, "PRINT", NULL, 1);
+ if(ps_global->print && ps_global->print->result)
+ fs_give((void **) &ps_global->print->result);
+ }
+#else /* _WINDOWS */
+ mswin_print_done();
+#endif /* _WINDOWS */
+
+ if(ps_global->print)
+ fs_give((void **) &ps_global->print);
+
+ q_status_message(SM_ASYNC, 0, 3, "Print command completed");
+ display_message('x');
+}
+
+
+/*----------------------------------------------------------------------
+ Print a single character, translate from UTF-8 to user's locale charset.
+
+ Args: c -- char to print
+ Returns: 1 on success, 0 on ps_global->print->err
+ ----*/
+int
+print_char(int c)
+{
+#ifndef _WINDOWS
+ int i, outchars;
+ unsigned char obuf[MAX(MB_LEN_MAX,32)];
+
+ if(!ps_global->print->err
+ && (outchars = utf8_to_locale(c, &cb, obuf, sizeof(obuf)))){
+ for(i = 0; i < outchars && !ps_global->print->err; i++)
+ if(putc(obuf[i], ps_global->print->fp) == EOF)
+ ps_global->print->err = 1;
+ }
+#else /* _WINDOWS */
+ if(!ps_global->print->err
+ && (ps_global->print->err = mswin_print_char_utf8(c)))
+ q_status_message1(SM_ORDER, 0, 9, "Print cancelled: %s",
+ mswin_print_error((unsigned short)ps_global->print->err));
+#endif /* _WINDOWS */
+
+ return(!ps_global->print->err);
+}
+
+
+/*----------------------------------------------------------------------
+ Send a line of text to the printer
+
+ Args: line -- Text to print
+
+ ----*/
+void
+print_text(char *line)
+{
+#ifndef _WINDOWS
+ int slen = strlen(line);
+
+ while(!ps_global->print->err && slen--)
+ if(print_char(*line++) == 0)
+ ps_global->print->err = 1;
+#else /* _WINDOWS */
+ if(!ps_global->print->err
+ && (ps_global->print->err = mswin_print_text_utf8(line)))
+ q_status_message1(SM_ORDER, 0, 9, "Print cancelled: %s",
+ mswin_print_error((unsigned short)ps_global->print->err));
+#endif /* _WINDOWS */
+}
+
+
+/*----------------------------------------------------------------------
+ printf style formatting with one arg for printer
+
+ Args: line -- The printf control string
+ a1 -- The 1st argument for printf
+ ----*/
+void
+print_text1(char *line, char *a1)
+{
+ char buf[64000];
+
+ if(!ps_global->print->err && snprintf(buf, sizeof(buf), line, a1) < 0)
+ ps_global->print->err = 1;
+ else
+ print_text(buf);
+}
diff --git a/alpine/osdep/print.h b/alpine/osdep/print.h
new file mode 100644
index 00000000..47e28eec
--- /dev/null
+++ b/alpine/osdep/print.h
@@ -0,0 +1,28 @@
+/*
+ * $Id: print.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_OSDEP_PRINT_INCLUDED
+#define PINE_OSDEP_PRINT_INCLUDED
+
+
+/* exported prototypes */
+int open_printer(char *);
+void close_printer(void);
+int print_char(int);
+void print_text(char *);
+void print_text1(char *, char *);
+
+
+#endif /* PINE_OSDEP_PRINT_INCLUDED */
diff --git a/alpine/osdep/resource.h b/alpine/osdep/resource.h
new file mode 100644
index 00000000..c52b54f9
--- /dev/null
+++ b/alpine/osdep/resource.h
@@ -0,0 +1 @@
+#include "../../pico/osdep/resource.h"
diff --git a/alpine/osdep/solquota b/alpine/osdep/solquota
new file mode 100644
index 00000000..348bad2a
--- /dev/null
+++ b/alpine/osdep/solquota
@@ -0,0 +1,174 @@
+static char *device_name();
+
+#include <fcntl.h>
+#include <sys/fs/ufs_quota.h>
+
+/*
+ * define the "quotactl" function as in Solaris 1, based on ioctl().
+ * By Marc Mazuhelli <mazu@dmi.usherb.ca>
+ * The "special" parameter is any file on the file system,
+ * not the block special device name as in Solaris 1.
+ * Thanks to veronica@solution.maths.unsw.edu.au who provided
+ * the idea and the basis for this function.
+ *
+ * [ Apparently quotactl used to exist in SunOS but no longer exists in ]
+ * [ Solaris. This is an equivalent. If you are running on a system ]
+ * [ which has quotactl, comment this routine out or use sunquota. ]
+ */
+
+int
+quotactl(int cmd, char *special, uid_t uid, struct dqblk * addr)
+{
+ struct quotctl op;
+ int fd = open(special, O_RDONLY);
+
+ if (fd < 0)
+ return -1;
+
+ op.op = cmd;
+ op.uid = uid;
+ op.addr = (caddr_t) addr;
+
+ if (ioctl(fd, Q_QUOTACTL, &op) < 0) {
+ close(fd);
+ return -1;
+ }
+ close(fd);
+ return (0);
+}
+
+
+/*----------------------------------------------------------------------
+ Return space left in disk quota on file system which given path is in.
+
+ Args: path - Path name of file or directory on file system of concern
+ over - pointer to flag that is set if the user is over quota
+
+ Returns: If *over = 0, the number of bytes free in disk quota as per
+ the soft limit.
+ If *over = 1, the number of bytes *over* quota.
+ -1 is returned on an error looking up quota
+ 0 is returned if there is no quota
+
+BUG: If there's more than 2.1Gb free this function will break
+ ----*/
+long
+disk_quota(path, over)
+ char *path;
+ int *over;
+{
+ static int no_quota = 0;
+ struct stat statx;
+ struct dqblk quotax;
+ long q;
+ char *dname;
+
+ if(no_quota)
+ return(0L); /* If no quota the first time, then none the second. */
+
+ dprint(5, (debugfile, "quota_check path: %s\n", path ? path : "?"));
+ if(stat(path, &statx) < 0) {
+ return(-1L);
+ }
+
+ *over = 0;
+ errno = 0;
+
+ dname = device_name(statx.st_dev);
+ if(dname == NULL)
+ return(-1L);
+
+ dprint(7, (debugfile, "Quota check: UID:%d device: %s\n",
+ getuid(), dname ? dname : "?"));
+ if(quotactl(Q_GETQUOTA, dname, getuid(), (char *)&quotax) < 0) {
+ dprint(5, (debugfile, "Quota failed : %s\n",
+ error_description(errno)));
+ return(-1L); /* Something went wrong */
+ }
+
+ dprint(5,(debugfile,"Quota: bsoftlimit:%d bhardlimit:%d curblock:%d\n",
+ quotax.dqb_bsoftlimit, quotax.dqb_bhardlimit, quotax.dqb_curblocks));
+
+ if(quotax.dqb_bsoftlimit == -1)
+ return(-1L);
+
+ q = (quotax.dqb_bsoftlimit - quotax.dqb_curblocks) * 512;
+
+ if(q < 0) {
+ q = -q;
+ *over = 1;
+ }
+ dprint(5, (debugfile, "disk_quota returning :%d, over:%d\n", q, *over));
+ return(q);
+}
+
+
+/*----------------------------------------------------------------------
+ * devNumToName
+ *
+ * This routine is here so that ex can get a device name to check
+ * disk quotas. One might wonder, why not use getmntent(), rather
+ * than read /etc/mtab in this crude way? The problem with getmntent
+ * is that it uses stdio, and ex/vi pointedly doesn't.
+ ----*/
+static char
+*device_name(st_devArg)
+ dev_t st_devArg;
+{
+#ifndef MTABNAME
+#define MTABNAME "/etc/mtab"
+#endif
+ char *mtab;
+ static char devName[48];
+ static char *answer = (char *) 0;
+ struct stat devStat;
+ static dev_t st_dev;
+ int nb, cur, bol;
+ char c;
+ int dname;
+
+ if (st_devArg == st_dev)
+ return answer;
+
+ mtab = read_file(MTABNAME);
+ if(mtab == NULL)
+ return((char *)NULL);
+
+ /* Initialize save data. */
+ st_dev = st_devArg;
+ answer = (char *) 0;
+ nb = strlen(mtab);
+
+ for (cur=bol=0, dname=1; cur < nb; ++cur) {
+
+ if (dname && (mtab[cur] <= ' ')) {
+ /* Space, tab or other such character has been found,
+ presumably marking the end of the device name string. */
+
+ dname = 0;
+ c = mtab[cur]; /* Save current character. */
+ mtab[cur] = 0; /* C zero-terminated string. */
+
+ /* Get device number, via stat(). If it's the right
+ number, copy the string and return its address. */
+ if (stat (&mtab[bol], &devStat) == 0) {
+ if (devStat.st_rdev == st_dev) {
+ if ((cur - bol + 1) < sizeof (devName)) {
+ strncpy (devName, &mtab[bol], sizeof(devName));
+ devName[sizeof(devName)-1] = '\0';
+ answer = &devName[0];
+ return(answer);
+ }
+ }
+ }
+ mtab[cur] = c;
+ }
+ if (mtab[cur] == '\n') {
+ dname = 1;
+ bol = cur + 1;
+ }
+ }
+ answer = NULL;
+
+ return(answer);
+}
diff --git a/alpine/osdep/sunquota b/alpine/osdep/sunquota
new file mode 100644
index 00000000..3528a1bb
--- /dev/null
+++ b/alpine/osdep/sunquota
@@ -0,0 +1,136 @@
+static char *device_name();
+
+/*----------------------------------------------------------------------
+ Return space left in disk quota on file system which given path is in.
+
+ Args: path - Path name of file or directory on file system of concern
+ over - pointer to flag that is set if the user is over quota
+
+ Returns: If *over = 0, the number of bytes free in disk quota as per
+ the soft limit.
+ If *over = 1, the number of bytes *over* quota.
+ -1 is returned on an error looking up quota
+ 0 is returned if there is no quota
+
+BUG: If there's more than 2.1Gb free this function will break
+ ----*/
+long
+disk_quota(path, over)
+ char *path;
+ int *over;
+{
+ static int no_quota = 0;
+ struct stat statx;
+ struct dqblk quotax;
+ long q;
+ char *dname;
+
+ if(no_quota)
+ return(0L); /* If no quota the first time, then none the second. */
+
+ dprint(5, (debugfile, "quota_check path: %s\n", path ? path : "?"));
+ if(stat(path, &statx) < 0) {
+ return(-1L);
+ }
+
+ *over = 0;
+ errno = 0;
+
+ dname = device_name(statx.st_dev);
+ if(dname == NULL)
+ return(-1L);
+
+ dprint(7, (debugfile, "Quota check: UID:%d device: %s\n",
+ getuid(), dname ? dname : "?"));
+ if(quotactl(Q_GETQUOTA, dname, getuid(), (char *)&quotax) < 0) {
+ dprint(5, (debugfile, "Quota failed : %s\n",
+ error_description(errno)));
+ return(-1L); /* Something went wrong */
+ }
+
+ dprint(5,(debugfile,"Quota: bsoftlimit:%d bhardlimit:%d curblock:%d\n",
+ quotax.dqb_bsoftlimit, quotax.dqb_bhardlimit, quotax.dqb_curblocks));
+
+ if(quotax.dqb_bsoftlimit == -1)
+ return(-1L);
+
+ q = (quotax.dqb_bsoftlimit - quotax.dqb_curblocks) * 512;
+
+ if(q < 0) {
+ q = -q;
+ *over = 1;
+ }
+ dprint(5, (debugfile, "disk_quota returning :%d, over:%d\n", q, *over));
+ return(q);
+}
+
+
+/*----------------------------------------------------------------------
+ * devNumToName
+ *
+ * This routine is here so that ex can get a device name to check
+ * disk quotas. One might wonder, why not use getmntent(), rather
+ * than read /etc/mtab in this crude way? The problem with getmntent
+ * is that it uses stdio, and ex/vi pointedly doesn't.
+ ----*/
+static char
+*device_name(st_devArg)
+ dev_t st_devArg;
+{
+#ifndef MTABNAME
+#define MTABNAME "/etc/mtab"
+#endif
+ char *mtab;
+ static char devName[48];
+ static char *answer = (char *) 0;
+ struct stat devStat;
+ static dev_t st_dev;
+ int nb, cur, bol;
+ char c;
+ int dname;
+
+ if (st_devArg == st_dev)
+ return answer;
+
+ mtab = read_file(MTABNAME);
+ if(mtab == NULL)
+ return((char *)NULL);
+
+ /* Initialize save data. */
+ st_dev = st_devArg;
+ answer = (char *) 0;
+ nb = strlen(mtab);
+
+ for (cur=bol=0, dname=1; cur < nb; ++cur) {
+
+ if (dname && (mtab[cur] <= ' ')) {
+ /* Space, tab or other such character has been found,
+ presumably marking the end of the device name string. */
+
+ dname = 0;
+ c = mtab[cur]; /* Save current character. */
+ mtab[cur] = 0; /* C zero-terminated string. */
+
+ /* Get device number, via stat(). If it's the right
+ number, copy the string and return its address. */
+ if (stat (&mtab[bol], &devStat) == 0) {
+ if (devStat.st_rdev == st_dev) {
+ if ((cur - bol + 1) < sizeof (devName)) {
+ strncpy (devName, &mtab[bol], sizeof(devName));
+ devName[sizeof(devName)-1] = '\0';
+ answer = &devName[0];
+ return(answer);
+ }
+ }
+ }
+ mtab[cur] = c;
+ }
+ if (mtab[cur] == '\n') {
+ dname = 1;
+ bol = cur + 1;
+ }
+ }
+ answer = NULL;
+
+ return(answer);
+}
diff --git a/alpine/osdep/termin.gen.c b/alpine/osdep/termin.gen.c
new file mode 100644
index 00000000..fb106be1
--- /dev/null
+++ b/alpine/osdep/termin.gen.c
@@ -0,0 +1,1252 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: termin.gen.c 1025 2008-04-08 22:59:38Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+#include <system.h>
+#include <general.h>
+
+#include "../../c-client/mail.h" /* for MAILSTREAM and friends */
+#include "../../c-client/osdep.h"
+#include "../../c-client/rfc822.h" /* for soutr_t and such */
+#include "../../c-client/misc.h" /* for cpystr proto */
+#include "../../c-client/utf8.h" /* for CHARSET and such*/
+#include "../../c-client/imap4r1.h"
+
+#include "../../pith/osdep/color.h"
+
+#include "../../pith/charconv/utf8.h"
+
+#include "../../pith/debug.h"
+#include "../../pith/newmail.h"
+#include "../../pith/conf.h"
+#include "../../pith/busy.h"
+
+#include "../../pico/estruct.h"
+#include "../../pico/pico.h"
+#include "../../pico/keydefs.h"
+
+#include "../../pico/osdep/color.h"
+
+#include "../status.h"
+#include "../folder.h"
+#include "../keymenu.h"
+#include "../send.h"
+#include "../radio.h"
+#include "../busy.h"
+
+#ifdef _WINDOWS
+#include "../../pico/osdep/mswin.h"
+#include "termin.wnt.h"
+#include "termout.wnt.h"
+#else
+#include "termout.unx.h"
+#endif
+
+#include "termin.gen.h"
+#include "termout.gen.h"
+
+#include "../mailcmd.h"
+
+
+#ifdef _WINDOWS
+static int g_mc_row, g_mc_col;
+
+int pcpine_oe_cursor(int, long);
+#endif
+
+
+/*
+ * Generic tty input routines
+ */
+
+
+/*----------------------------------------------------------------------
+ Read a character from keyboard with timeout
+ Input: none
+
+ Result: Returns command read via read_char
+ Times out and returns a null command every so often
+
+ Calculates the timeout for the read, and does a few other house keeping
+things. The duration of the timeout is set in pine.c.
+ ----------------------------------------------------------------------*/
+UCS
+read_command(char **utf8str)
+{
+ int tm = 0, more_freq_timeo;
+ UCS ucs;
+ long dtime;
+ static unsigned char utf8buf[7];
+ unsigned char *newdestp;
+
+ /*
+ * timeo is the mail-check-interval. What we want to do (ignoring the
+ * messages_queued part) is timeout more often than timeo but only
+ * check for new mail every timeo or so seconds. The reason we want to
+ * timeout more often is so that we will have a chance to catch the user
+ * in an idle period where we can do a check_point(). Otherwise, with
+ * a default mail-check-interval, we are almost always calling newmail
+ * right after the user presses a key, making it the worst possible
+ * time to do a checkpoint.
+ */
+
+ more_freq_timeo = MIN(get_input_timeout(), IDLE_TIMEOUT);
+ if(more_freq_timeo == 0)
+ more_freq_timeo = IDLE_TIMEOUT;
+
+ cancel_busy_cue(-1);
+ tm = (messages_queued(&dtime) > 1) ? (int)dtime : more_freq_timeo;
+
+ if(utf8str)
+ *utf8str = NULL;
+
+ ucs = read_char(tm);
+ if(ucs != NO_OP_COMMAND && ucs != NO_OP_IDLE && ucs != KEY_RESIZE)
+ zero_new_mail_count();
+
+#ifdef BACKGROUND_POST
+ /*
+ * Any expired children to report on?
+ */
+ if(ps_global->post && ps_global->post->pid == 0){
+ int winner = 0;
+
+ if(ps_global->post->status < 0){
+ q_status_message(SM_ORDER | SM_DING, 3, 3, "Abysmal failure!");
+ }
+ else{
+ (void) pine_send_status(ps_global->post->status,
+ ps_global->post->fcc, tmp_20k_buf, SIZEOF_20KBUF,
+ &winner);
+ q_status_message(SM_ORDER | (winner ? 0 : SM_DING), 3, 3,
+ tmp_20k_buf);
+
+ }
+
+ if(!winner)
+ q_status_message(SM_ORDER, 0, 3,
+ "Re-send via \"Compose\" then \"Yes\" to \"Continue INTERRUPTED?\"");
+
+ if(ps_global->post->fcc)
+ fs_give((void **) &ps_global->post->fcc);
+
+ fs_give((void **) &ps_global->post);
+ }
+#endif
+
+ /*
+ * The character we get from read_char() is a UCS-4 char. Or it could be a special
+ * value like KEY_UP or NO_OP_IDLE or something similar. From here on out
+ * we're going to operate with UTF-8 internally. This is the point where we
+ * convert the UCS-4 input (actually whatever sort of input the user is typing
+ * was converted to UCS-4 first) to UTF-8. It's easy in this read_command
+ * case because if user types a non-ascii character as a command it's going to be
+ * an error. All commands are ascii. In order to present a reasonable error
+ * message we pass back the UTF-8 string to the caller.
+ */
+ if(ucs >= 0x80 && ucs < KEY_BASE){
+ /*
+ * User typed a character that is non-ascii. Convert it to
+ * UTF-8.
+ */
+ memset(utf8buf, 0, sizeof(utf8buf));
+ newdestp = utf8_put(utf8buf, (unsigned long) ucs);
+ if(newdestp - utf8buf > 1){ /* this should happen */
+ if(utf8str)
+ *utf8str = (char *) utf8buf;
+
+ dprint((9, "Read command: looks like user typed non-ascii command 0x%x %s: returning KEY_UTF8\n", ucs, pretty_command(ucs)));
+ ucs = KEY_UTF8;
+ }
+ else{
+ dprint((9, "Read command: looks like user typed unknown, non-ascii command 0x%x %s: returning KEY_UNKNOWN\n", ucs, pretty_command(ucs)));
+ ucs = KEY_UNKNOWN; /* best we can do, shouldn't happen */
+ }
+ }
+ else{
+ dprint((9, "Read command returning: 0x%x %s\n", ucs, pretty_command(ucs)));
+ }
+
+ return(ucs);
+}
+
+
+int
+read_command_prep()
+{
+ int i;
+ char *fname;
+ MAILSTREAM *m;
+
+ /*
+ * Before we sniff at the input queue, make sure no external event's
+ * changed our picture of the message sequence mapping. If so,
+ * recalculate the dang thing and run thru whatever processing loop
+ * we're in again...
+ */
+ for(i = 0; i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR)
+ && sp_expunge_count(m)){
+ fname = STREAMNAME(m);
+ q_status_message3(SM_ORDER, 3, 3,
+ "%s message%s expunged from folder \"%s\"",
+ long2string(sp_expunge_count(m)),
+ plural(sp_expunge_count(m)),
+ pretty_fn(fname));
+ sp_set_expunge_count(m, 0L);
+ display_message('x');
+ }
+ }
+
+ if(sp_mail_box_changed(ps_global->mail_stream)
+ && sp_new_mail_count(ps_global->mail_stream)){
+ dprint((2, "Noticed %ld new msgs! \n",
+ sp_new_mail_count(ps_global->mail_stream)));
+ return(FALSE); /* cycle thru so caller can update */
+ }
+
+ return(TRUE);
+}
+
+
+/*----------------------------------------------------------------------
+ Prompt user for a string in status line with various options
+
+ Args: utf8string -- the buffer result is returned in, and original string (if
+ any) is passed in.
+ y_base -- y position on screen to start on. 0,0 is upper left
+ negative numbers start from bottom
+ x_base -- column position on screen to start on. 0,0 is upper left
+ utf8string_size -- Length of utf8string buffer
+ utf8prompt -- The string to prompt with
+ escape_list -- pointer to array of ESCKEY_S's. input chars matching
+ those in list return value from list.
+ help -- Array of strings for help text in bottom screen lines
+ flags -- pointer (because some are return values) to flags
+ OE_USER_MODIFIED - Set on return if user modified buffer
+ OE_DISALLOW_CANCEL - No cancel in menu.
+ OE_DISALLOW_HELP - No help in menu.
+ OE_KEEP_TRAILING_SPACE - Allow trailing space.
+ OE_SEQ_SENSITIVE - Caller is sensitive to sequence
+ number changes.
+ OE_APPEND_CURRENT - String should not be truncated
+ before accepting user input.
+ OE_PASSWD - Don't echo on screen.
+
+ Result: editing input string
+ returns -1 unexpected errors
+ returns 0 normal entry typed (editing and return or PF2)
+ returns 1 typed ^C or PF2 (cancel)
+ returns 3 typed ^G or PF1 (help)
+ returns 4 typed ^L for a screen redraw
+
+ WARNING: Care is required with regard to the escape_list processing.
+ The passed array is terminated with an entry that has ch = -1.
+ Function key labels and key strokes need to be setup externally!
+ Traditionally, a return value of 2 is used for ^T escapes.
+
+ Unless in escape_list, tabs are trapped by isprint().
+This allows near full weemacs style editing in the line
+ ^A beginning of line
+ ^E End of line
+ ^R Redraw line
+ ^G Help
+ ^F forward
+ ^B backward
+ ^D delete
+----------------------------------------------------------------------*/
+
+int
+optionally_enter(char *utf8string, int y_base, int x_base, int utf8string_size,
+ char *utf8prompt, ESCKEY_S *escape_list, HelpType help, int *flags)
+{
+ UCS *string = NULL, ucs;
+ size_t string_size;
+ UCS *s2;
+ UCS *saved_original = NULL;
+ char *candidate;
+ UCS *kill_buffer = NULL;
+ UCS *k, *kb;
+ int field_pos; /* offset into array dline.vl */
+ int i, j, return_v, cols, prompt_width, too_thin,
+ real_y_base, km_popped, passwd;
+ char **help_text;
+ long fkey_table[12];
+ struct key_menu *km;
+ bitmap_t bitmap;
+ COLOR_PAIR *lastc = NULL, *promptc = NULL;
+ struct variable *vars = ps_global->vars;
+ struct display_line dline;
+#ifdef _WINDOWS
+ int cursor_shown;
+#endif
+
+ dprint((5, "=== optionally_enter called ===\n"));
+ dprint((9, "utf8string:\"%s\" y:%d x:%d length: %d append: %d\n",
+ utf8string ? utf8string : "",
+ x_base, y_base, utf8string_size,
+ (flags && *flags & OE_APPEND_CURRENT)));
+ dprint((9, "passwd:%d utf8prompt:\"%s\" label:\"%s\"\n",
+ (flags && *flags & OE_PASSWD_NOAST) ? 10 :
+ (flags && *flags & OE_PASSWD) ? 1 : 0,
+ utf8prompt ? utf8prompt : "",
+ (escape_list && escape_list[0].ch != -1 && escape_list[0].label)
+ ? escape_list[0].label: ""));
+
+ if(!ps_global->ttyo)
+ return(pre_screen_config_opt_enter(utf8string, utf8string_size, utf8prompt,
+ escape_list, help, flags));
+
+#ifdef _WINDOWS
+ if (mswin_usedialog ())
+ return(win_dialog_opt_enter(utf8string, utf8string_size, utf8prompt,
+ escape_list, help, flags));
+#endif
+
+
+ /*
+ * Utf8string comes in as UTF-8. We'll convert it to a UCS-4 array and operate on
+ * that array, then convert it back before returning. Utf8string_size is the size
+ * of the utf8string array but that doesn't help us much for the array we need to
+ * operate on here. We'll just allocate a big array and then cut it off when
+ * sending it back.
+ *
+ * This should come before the specialized calls above but those aren't
+ * converted to use UCS-4 yet.
+ */
+ string = utf8_to_ucs4_cpystr(utf8string);
+ dline.vused = ucs4_strlen(string);
+
+ string_size = (2 * MAX(utf8string_size,dline.vused) + 100);
+ fs_resize((void **) &string, string_size * sizeof(UCS));
+
+ suspend_busy_cue();
+ cols = ps_global->ttyo->screen_cols;
+ prompt_width = utf8_width(utf8prompt);
+ too_thin = 0;
+ km_popped = 0;
+ if(y_base > 0)
+ real_y_base = y_base;
+ else{
+ real_y_base = y_base + ps_global->ttyo->screen_rows;
+ real_y_base = MAX(real_y_base, 0);
+ }
+
+ flush_ordered_messages();
+ mark_status_dirty();
+
+ if(flags && *flags & OE_APPEND_CURRENT) /* save a copy in case of cancel */
+ saved_original = ucs4_cpystr(string);
+
+ /*
+ * build the function key mapping table, skipping predefined keys...
+ */
+ memset(fkey_table, NO_OP_COMMAND, 12 * sizeof(long));
+ for(i = 0, j = 0; escape_list && escape_list[i].ch != -1 && i+j < 12; i++){
+ if(i+j == OE_HELP_KEY)
+ j++;
+
+ if(i+j == OE_CANCEL_KEY)
+ j++;
+
+ if(i+j == OE_ENTER_KEY)
+ j++;
+
+ fkey_table[i+j] = escape_list[i].ch;
+ }
+
+ /* assumption that HelpType is char ** */
+ help_text = help;
+ if(help_text){ /*---- Show help text -----*/
+ int width = ps_global->ttyo->screen_cols - x_base;
+
+ if(FOOTER_ROWS(ps_global) == 1){
+ km_popped++;
+ FOOTER_ROWS(ps_global) = 3;
+ clearfooter(ps_global);
+
+ y_base = -3;
+ real_y_base = y_base + ps_global->ttyo->screen_rows;
+ }
+
+ for(j = 0; j < 2 && help_text[j]; j++){
+ MoveCursor(real_y_base + 1 + j, x_base);
+ CleartoEOLN();
+
+ if(width < utf8_width(help_text[j])){
+ char *tmp = cpystr(help_text[j]);
+ (void) utf8_truncate(tmp, width);
+ PutLine0(real_y_base + 1 + j, x_base, tmp);
+ fs_give((void **) &tmp);
+ }
+ else
+ PutLine0(real_y_base + 1 + j, x_base, help_text[j]);
+ }
+ }
+ else{
+ clrbitmap(bitmap);
+ clrbitmap((km = &oe_keymenu)->bitmap); /* force formatting */
+ if(!(flags && (*flags) & OE_DISALLOW_HELP))
+ setbitn(OE_HELP_KEY, bitmap);
+
+ setbitn(OE_ENTER_KEY, bitmap);
+ if(!(flags && (*flags) & OE_DISALLOW_CANCEL))
+ setbitn(OE_CANCEL_KEY, bitmap);
+
+ setbitn(OE_CTRL_T_KEY, bitmap);
+
+ /*---- Show the usual possible keys ----*/
+ for(i=0,j=0; escape_list && escape_list[i].ch != -1 && i+j < 12; i++){
+ if(i+j == OE_HELP_KEY)
+ j++;
+
+ if(i+j == OE_CANCEL_KEY)
+ j++;
+
+ if(i+j == OE_ENTER_KEY)
+ j++;
+
+ oe_keymenu.keys[i+j].label = escape_list[i].label;
+ oe_keymenu.keys[i+j].name = escape_list[i].name;
+ setbitn(i+j, bitmap);
+ }
+
+ for(i = i+j; i < 12; i++)
+ if(!(i == OE_HELP_KEY || i == OE_ENTER_KEY || i == OE_CANCEL_KEY))
+ oe_keymenu.keys[i].name = NULL;
+
+ draw_keymenu(km, bitmap, cols, 1-FOOTER_ROWS(ps_global), 0, FirstMenu);
+ }
+
+ if(pico_usingcolor() && VAR_PROMPT_FORE_COLOR &&
+ VAR_PROMPT_BACK_COLOR &&
+ pico_is_good_color(VAR_PROMPT_FORE_COLOR) &&
+ pico_is_good_color(VAR_PROMPT_BACK_COLOR)){
+ lastc = pico_get_cur_color();
+ if(lastc){
+ promptc = new_color_pair(VAR_PROMPT_FORE_COLOR,
+ VAR_PROMPT_BACK_COLOR);
+ (void)pico_set_colorp(promptc, PSC_NONE);
+ }
+ }
+ else
+ StartInverse();
+
+ /*
+ * if display length isn't wide enough to support input,
+ * shorten up the prompt...
+ */
+ if((dline.dwid = cols - (x_base + prompt_width)) < MIN_OPT_ENT_WIDTH){
+ char *p;
+ unsigned got_width;
+
+ /*
+ * Scoot prompt pointer forward at least (MIN_OPT_ENT_WIDTH - dline.dwid) screencells.
+ */
+ p = utf8_count_forw_width(utf8prompt, MIN_OPT_ENT_WIDTH-dline.dwid, &got_width);
+ if(got_width < MIN_OPT_ENT_WIDTH-dline.dwid)
+ p = utf8_count_forw_width(utf8prompt, MIN_OPT_ENT_WIDTH+1-dline.dwid, &got_width);
+
+ if(p){
+ prompt_width = utf8_width(p);
+ dline.dwid = cols - (x_base + prompt_width);
+ utf8prompt = p;
+ }
+ }
+
+ /*
+ * How many UCS-4 characters will we need to make up the width dwid? It could be
+ * unlimited because of zero-width characters, I suppose, but realistically it
+ * isn't going to be much more than dwid.
+ */
+ dline.dlen = 2 * dline.dwid + 100;
+
+ dline.dl = (UCS *) fs_get(dline.dlen * sizeof(UCS));
+ dline.olddl = (UCS *) fs_get(dline.dlen * sizeof(UCS));
+ memset(dline.dl, 0, dline.dlen * sizeof(UCS));
+ memset(dline.olddl, 0, dline.dlen * sizeof(UCS));
+
+ dline.movecursor = MoveCursor;
+ dline.writechar = Writewchar;
+
+ dline.row = real_y_base;
+ dline.col = x_base + prompt_width;
+
+ dline.vl = string;
+ dline.vlen = --string_size; /* -1 for terminating zero */
+ dline.vbase = field_pos = 0;
+
+#ifdef _WINDOWS
+ cursor_shown = mswin_showcaret(1);
+#endif
+
+ PutLine0(real_y_base, x_base, utf8prompt);
+
+ /*
+ * If appending, position field_pos at end of input.
+ */
+ if(flags && *flags & OE_APPEND_CURRENT)
+ while(string[field_pos])
+ field_pos++;
+
+ passwd = (flags && *flags & OE_PASSWD_NOAST) ? 10 :
+ (flags && *flags & OE_PASSWD) ? 1 : 0;
+ line_paint(field_pos, &dline, &passwd);
+
+ /*----------------------------------------------------------------------
+ The main loop
+ loops until someone sets the return_v.
+ ----------------------------------------------------------------------*/
+ return_v = -10;
+
+ while(return_v == -10) {
+
+#ifdef MOUSE
+ mouse_in_content(KEY_MOUSE, -1, -1, 0x5, 0);
+ register_mfunc(mouse_in_content,
+ real_y_base, x_base + prompt_width,
+ real_y_base, ps_global->ttyo->screen_cols);
+#endif
+#ifdef _WINDOWS
+ mswin_allowpaste(MSWIN_PASTE_LINE);
+ g_mc_row = real_y_base;
+ g_mc_col = x_base + prompt_width;
+ mswin_mousetrackcallback(pcpine_oe_cursor);
+#endif
+
+ /* Timeout 10 min to keep imap mail stream alive */
+ ps_global->conceal_sensitive_debugging = passwd ? 1 : 0;
+ ucs = read_char(600);
+ ps_global->conceal_sensitive_debugging = 0;
+
+#ifdef MOUSE
+ clear_mfunc(mouse_in_content);
+#endif
+#ifdef _WINDOWS
+ mswin_allowpaste(MSWIN_PASTE_DISABLE);
+ mswin_mousetrackcallback(NULL);
+#endif
+
+ /*
+ * Don't want to intercept all characters if typing in passwd.
+ * We select an ad hoc set that we will catch and let the rest
+ * through. We would have caught the set below in the big switch
+ * but we skip the switch instead. Still catch things like ^K,
+ * DELETE, ^C, RETURN.
+ */
+ if(passwd)
+ switch(ucs){
+ case ctrl('F'):
+ case KEY_RIGHT:
+ case ctrl('B'):
+ case KEY_LEFT:
+ case ctrl('U'):
+ case ctrl('A'):
+ case KEY_HOME:
+ case ctrl('E'):
+ case KEY_END:
+ case TAB:
+ goto ok_for_passwd;
+ }
+
+ if(too_thin && ucs != KEY_RESIZE && ucs != ctrl('Z') && ucs != ctrl('C'))
+ goto bleep;
+
+ switch(ucs){
+
+ /*--------------- KEY RIGHT ---------------*/
+ case ctrl('F'):
+ case KEY_RIGHT:
+ if(field_pos >= string_size || string[field_pos] == '\0')
+ goto bleep;
+
+ line_paint(++field_pos, &dline, &passwd);
+ break;
+
+ /*--------------- KEY LEFT ---------------*/
+ case ctrl('B'):
+ case KEY_LEFT:
+ if(field_pos <= 0)
+ goto bleep;
+
+ line_paint(--field_pos, &dline, &passwd);
+ break;
+
+ /*-------------------- WORD SKIP --------------------*/
+ case ctrl('@'):
+ /*
+ * Note: read_char *can* return NO_OP_COMMAND which is
+ * the def'd with the same value as ^@ (NULL), BUT since
+ * read_char has a big timeout (>25 secs) it won't.
+ */
+
+ /* skip thru current word */
+ while(string[field_pos]
+ && isalnum((unsigned char) string[field_pos]))
+ field_pos++;
+
+ /* skip thru current white space to next word */
+ while(string[field_pos]
+ && !isalnum((unsigned char) string[field_pos]))
+ field_pos++;
+
+ line_paint(field_pos, &dline, &passwd);
+ break;
+
+ /*-------------------- RETURN --------------------*/
+ case PF4:
+ if(F_OFF(F_USE_FK,ps_global)) goto bleep;
+ case ctrl('J'):
+ case ctrl('M'):
+ return_v = 0;
+ break;
+
+ /*-------------------- Destructive backspace --------------------*/
+ case '\177': /* DEL */
+ case ctrl('H'):
+ /* Try and do this with by telling the terminal to delete a
+ a character. If that fails, then repaint the rest of the
+ line, acheiving the same much less efficiently
+ */
+ if(field_pos <= 0)
+ goto bleep;
+
+ field_pos--;
+ /* drop thru to pull line back ... */
+
+ /*-------------------- Delete char --------------------*/
+ case ctrl('D'):
+ case KEY_DEL:
+ if(field_pos >= string_size || !string[field_pos])
+ goto bleep;
+
+ dline.vused--;
+ for(s2 = &string[field_pos]; *s2 != 0; s2++)
+ *s2 = s2[1];
+
+ *s2 = 0; /* Copy last NULL */
+ line_paint(field_pos, &dline, &passwd);
+ if(flags) /* record change if requested */
+ *flags |= OE_USER_MODIFIED;
+
+ break;
+
+ /*--------------- Kill line -----------------*/
+ case ctrl('K'):
+ if(kill_buffer != NULL)
+ fs_give((void **) &kill_buffer);
+
+ if(field_pos != 0 || string[0]){
+ if(!passwd && F_ON(F_DEL_FROM_DOT, ps_global))
+ dline.vused -= ucs4_strlen(&string[i = field_pos]);
+ else
+ dline.vused = i = 0;
+
+ kill_buffer = ucs4_cpystr(&string[field_pos = i]);
+ string[field_pos] = '\0';
+ line_paint(field_pos, &dline, &passwd);
+ if(flags) /* record change if requested */
+ *flags |= OE_USER_MODIFIED;
+ }
+
+ break;
+
+ /*------------------- Undelete line --------------------*/
+ case ctrl('U'):
+ if(kill_buffer == NULL)
+ goto bleep;
+
+ /* Make string so it will fit */
+ kb = ucs4_cpystr(kill_buffer);
+ if(ucs4_strlen(kb) + ucs4_strlen(string) > string_size)
+ kb[string_size - ucs4_strlen(string)] = '\0';
+
+ if(string[field_pos] == '\0') {
+ /*--- adding to the end of the string ----*/
+ for(k = kb; *k; k++)
+ string[field_pos++] = *k;
+
+ string[field_pos] = '\0';
+ }
+ else{
+ int shift;
+
+ shift = ucs4_strlen(kb);
+
+ /* shift field_pos ... end to right */
+ for(k = &string[field_pos] + ucs4_strlen(&string[field_pos]);
+ k >= &string[field_pos]; k--)
+ *(k+shift) = *k;
+
+ for(k = kb; *k; k++)
+ string[field_pos++] = *k;
+ }
+
+ if(*kb && flags) /* record change if requested */
+ *flags |= OE_USER_MODIFIED;
+
+ dline.vused = ucs4_strlen(string);
+ fs_give((void **) &kb);
+ line_paint(field_pos, &dline, &passwd);
+ break;
+
+ /*-------------------- Interrupt --------------------*/
+ case ctrl('C'): /* ^C */
+ if(F_ON(F_USE_FK,ps_global) || (flags && ((*flags) & OE_DISALLOW_CANCEL)))
+ goto bleep;
+
+ goto cancel;
+
+ case PF2:
+ if(F_OFF(F_USE_FK,ps_global) || (flags && ((*flags) & OE_DISALLOW_CANCEL)))
+ goto bleep;
+
+ cancel:
+ return_v = 1;
+ if(saved_original){
+ for(i = 0; saved_original[i]; i++)
+ string[i] = saved_original[i];
+
+ string[i] = 0;
+ }
+
+ break;
+
+ case ctrl('A'):
+ case KEY_HOME:
+ /*-------------------- Start of line -------------*/
+ line_paint(field_pos = 0, &dline, &passwd);
+ break;
+
+ case ctrl('E'):
+ case KEY_END:
+ /*-------------------- End of line ---------------*/
+ line_paint(field_pos = dline.vused, &dline, &passwd);
+ break;
+
+ /*-------------------- Help --------------------*/
+ case ctrl('G') :
+ case PF1:
+ if(flags && ((*flags) & OE_DISALLOW_HELP))
+ goto bleep;
+ else if(FOOTER_ROWS(ps_global) == 1 && km_popped == 0){
+ km_popped++;
+ FOOTER_ROWS(ps_global) = 3;
+ clearfooter(ps_global);
+ if(lastc)
+ (void)pico_set_colorp(lastc, PSC_NONE);
+ else
+ EndInverse();
+
+ draw_keymenu(km, bitmap, cols, 1-FOOTER_ROWS(ps_global),
+ 0, FirstMenu);
+
+ if(promptc)
+ (void)pico_set_colorp(promptc, PSC_NONE);
+ else
+ StartInverse();
+
+ mark_keymenu_dirty();
+ y_base = -3;
+ dline.row = real_y_base = y_base + ps_global->ttyo->screen_rows;
+ PutLine0(real_y_base, x_base, utf8prompt);
+ memset(dline.dl, 0, dline.dlen * sizeof(UCS));
+ memset(dline.olddl, 0, dline.dlen * sizeof(UCS));
+ line_paint(field_pos, &dline, &passwd);
+ break;
+ }
+
+ if(FOOTER_ROWS(ps_global) > 1){
+ mark_keymenu_dirty();
+ return_v = 3;
+ }
+ else
+ goto bleep;
+
+ break;
+
+
+#ifdef MOUSE
+ /* Mouse support untested in pine 5.00 */
+ case KEY_MOUSE :
+ {
+ MOUSEPRESS mp;
+ int w;
+
+ mouse_get_last (NULL, &mp);
+
+ switch(mp.button){
+ case M_BUTTON_LEFT : /* position cursor */
+ mp.col -= dline.col;
+
+ /*
+ * We have to figure out which character is under the cursor.
+ * This is complicated by the fact that characters may
+ * be other than one cell wide.
+ */
+
+ /* the -1 is for the '<' when text is offscreen left */
+ w = (dline.vbase > 0) ? mp.col-1 : mp.col;
+
+ if(mp.col <= 0)
+ field_pos = dline.vbase - 1;
+ else{
+ if(dline.vused <= dline.vbase
+ || ucs4_str_width_a_to_b(dline.vl,dline.vbase,dline.vused-1) <= w)
+ field_pos = dline.vused;
+ else{
+ /*
+ * Find index of 1st character that causes the
+ * width to be > w.
+ */
+ for(i = 0;
+ ucs4_str_width_a_to_b(dline.vl,dline.vbase,dline.vbase+i) <= w;
+ i++)
+ ;
+
+ field_pos = dline.vbase + i;
+ }
+ }
+
+ field_pos = MIN(MAX(field_pos, 0), dline.vused);
+
+ /* just allow line_paint to choose vbase */
+ line_paint(field_pos, &dline, &passwd);
+ break;
+
+ case M_BUTTON_RIGHT :
+#ifdef _WINDOWS
+
+ /*
+ * Same as M_BUTTON_LEFT except we paste in text after
+ * moving the cursor.
+ */
+
+ mp.col -= dline.col;
+
+ /* the -1 is for the '<' when text is offscreen left */
+ w = (dline.vbase > 0) ? mp.col-1 : mp.col;
+
+ if(mp.col <= 0)
+ field_pos = dline.vbase - 1;
+ else{
+ if(dline.vused <= dline.vbase
+ || ucs4_str_width_a_to_b(dline.vl,dline.vbase,dline.vused-1) <= w)
+ field_pos = dline.vused;
+ else{
+ /*
+ * Find index of 1st character that causes the
+ * width to be > w.
+ */
+ for(i = 0;
+ ucs4_str_width_a_to_b(dline.vl,dline.vbase,dline.vbase+i) <= w;
+ i++)
+ ;
+
+ field_pos = dline.vbase + i;
+ }
+ }
+
+ field_pos = MIN(MAX(field_pos, 0), dline.vused);
+
+ line_paint(field_pos, &dline, &passwd);
+
+ mswin_allowpaste(MSWIN_PASTE_LINE);
+ mswin_paste_popup();
+ mswin_allowpaste(MSWIN_PASTE_DISABLE);
+ break;
+#endif
+
+ case M_BUTTON_MIDDLE : /* NO-OP for now */
+ default: /* just ignore */
+ break;
+ }
+ }
+
+ break;
+#endif
+
+
+ case NO_OP_IDLE:
+ /*
+ * Keep mail stream alive by checking for new mail.
+ * If we're asking for a password in a login prompt
+ * we don't want to check for new_mail because the
+ * new mail check might be what got us here in the first
+ * place (because of a filter trying to save a message).
+ * If we need to wait for the user to come back then
+ * the caller will just have to deal with the failure
+ * to login.
+ */
+ i = -1;
+ if(!ps_global->no_newmail_check_from_optionally_enter)
+ i = new_mail(0, 2, NM_DEFER_SORT);
+
+ if(sp_expunge_count(ps_global->mail_stream) &&
+ flags && ((*flags) & OE_SEQ_SENSITIVE))
+ goto cancel;
+
+ if(i < 0){
+ line_paint(field_pos, &dline, &passwd);
+ break; /* no changes, get on with life */
+ }
+ /* Else fall into redraw */
+
+ /*-------------------- Redraw --------------------*/
+ case ctrl('L'):
+ /*---------------- re size ----------------*/
+ case KEY_RESIZE:
+
+ dline.row = real_y_base = y_base > 0 ? y_base :
+ y_base + ps_global->ttyo->screen_rows;
+ if(lastc)
+ (void)pico_set_colorp(lastc, PSC_NONE);
+ else
+ EndInverse();
+
+ ClearScreen();
+ redraw_titlebar();
+ if(ps_global->redrawer != (void (*)(void))NULL)
+ (*ps_global->redrawer)();
+
+ redraw_keymenu();
+ if(promptc)
+ (void)pico_set_colorp(promptc, PSC_NONE);
+ else
+ StartInverse();
+
+ PutLine0(real_y_base, x_base, utf8prompt);
+ cols = ps_global->ttyo->screen_cols;
+ too_thin = 0;
+ if(cols < x_base + prompt_width + 4){
+ Writechar(BELL, 0);
+ PutLine0(real_y_base, 0, "Screen's too thin. Ouch!");
+ too_thin = 1;
+ }
+ else{
+ dline.col = x_base + prompt_width;
+ dline.dwid = cols - (x_base + prompt_width);
+ dline.dlen = 2 * dline.dwid + 100;
+ fs_resize((void **) &dline.dl, (size_t) dline.dlen * sizeof(UCS));
+ fs_resize((void **) &dline.olddl, (size_t) dline.dlen * sizeof(UCS));
+ memset(dline.dl, 0, dline.dlen * sizeof(UCS));
+ memset(dline.olddl, 0, dline.dlen * sizeof(UCS));
+ line_paint(field_pos, &dline, &passwd);
+ }
+
+ fflush(stdout);
+
+ dprint((9,
+ "optionally_enter RESIZE new_cols:%d too_thin: %d\n",
+ cols, too_thin));
+ break;
+
+ case PF3 : /* input to potentially remap */
+ case PF5 :
+ case PF6 :
+ case PF7 :
+ case PF8 :
+ case PF9 :
+ case PF10 :
+ case PF11 :
+ case PF12 :
+ if(F_ON(F_USE_FK,ps_global)
+ && fkey_table[ucs - PF1] != NO_OP_COMMAND)
+ ucs = fkey_table[ucs - PF1]; /* remap function key input */
+
+ default:
+ if(escape_list){ /* in the escape key list? */
+ for(j=0; escape_list[j].ch != -1; j++){
+ if(escape_list[j].ch == ucs){
+ return_v = escape_list[j].rval;
+ break;
+ }
+ }
+
+ if(return_v != -10)
+ break;
+ }
+
+ if(ucs < 0x80 && FILTER_THIS((unsigned char) ucs)){
+ bleep:
+ putc(BELL, stdout);
+ continue;
+ }
+
+ ok_for_passwd:
+ /*--- Insert a character -----*/
+ if(dline.vused >= string_size)
+ goto bleep;
+
+ /*---- extending the length of the string ---*/
+ for(s2 = &string[++dline.vused]; s2 - string > field_pos; s2--)
+ *s2 = *(s2-1);
+
+ string[field_pos++] = ucs;
+ line_paint(field_pos, &dline, &passwd);
+ if(flags) /* record change if requested */
+ *flags |= OE_USER_MODIFIED;
+
+ } /*---- End of switch on char ----*/
+ }
+
+#ifdef _WINDOWS
+ if(!cursor_shown)
+ mswin_showcaret(0);
+#endif
+
+ if(dline.dl)
+ fs_give((void **) &dline.dl);
+
+ if(dline.olddl)
+ fs_give((void **) &dline.olddl);
+
+ if(saved_original)
+ fs_give((void **) &saved_original);
+
+ if(kill_buffer)
+ fs_give((void **) &kill_buffer);
+
+ /*
+ * Change string back into UTF-8.
+ */
+ candidate = ucs4_to_utf8_cpystr(string);
+
+ if(string)
+ fs_give((void **) &string);
+
+ if(candidate){
+ strncpy(utf8string, candidate, utf8string_size);
+ utf8string[utf8string_size-1] = '\0';
+ fs_give((void **) &candidate);
+ }
+
+ if (!(flags && (*flags) & OE_KEEP_TRAILING_SPACE))
+ removing_trailing_white_space(utf8string);
+
+ if(lastc){
+ (void)pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ if(promptc)
+ free_color_pair(&promptc);
+ }
+ else
+ EndInverse();
+
+ MoveCursor(real_y_base, x_base); /* Move the cursor to show we're done */
+ fflush(stdout);
+ resume_busy_cue(0);
+ if(km_popped){
+ FOOTER_ROWS(ps_global) = 1;
+ clearfooter(ps_global);
+ ps_global->mangled_body = 1;
+ }
+
+ return(return_v);
+}
+
+
+/*----------------------------------------------------------------------
+ Check to see if the given command is reasonably valid
+
+ Args: ch -- the character to check
+
+ Result: A valid command is returned, or a well know bad command is returned.
+
+ ---*/
+UCS
+validatekeys(UCS ch)
+{
+ if(F_ON(F_USE_FK,ps_global)){
+ if(ch >= 'a' && ch <= 'z')
+ return(KEY_JUNK);
+ }
+ else{
+ if(ch >= PF1 && ch <= PF12)
+ return(KEY_JUNK);
+ }
+
+ return(ch);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Prepend config'd commands to keyboard input
+
+ Args: ch -- pointer to storage for returned command
+
+ Returns: TRUE if we're passing back a useful command, FALSE otherwise
+
+ ---*/
+int
+process_config_input(UCS *ch)
+{
+ static char firsttime = (char) 1;
+ int c;
+ unsigned long octets_so_far, remaining_octets, ret = 0;
+ unsigned char *inputp;
+ UCS ucs;
+ unsigned char inputbuf[20];
+
+ /* commands in config file */
+ if(ps_global->initial_cmds && *ps_global->initial_cmds) {
+ /*
+ * There are a few commands that may require keyboard input before
+ * we enter the main command loop. That input should be interactive,
+ * not from our list of initial keystrokes.
+ */
+ if(ps_global->dont_use_init_cmds)
+ return(ret);
+
+ c = *ps_global->initial_cmds++;
+
+ /*
+ * Use enough bytes to make up a character and convert it to UCS-4.
+ */
+ if(c < 0x80 || c > KEY_BASE){
+ *ch = (UCS) c;
+ ret = 1;
+ }
+ else{
+ memset(inputbuf, 0, sizeof(inputbuf));
+ inputbuf[0] = (0xff & c);
+ octets_so_far = 1;
+
+ while(!ret){
+ remaining_octets = octets_so_far;
+ inputp = inputbuf;
+ ucs = (UCS) utf8_get(&inputp, &remaining_octets);
+ switch(ucs){
+ case U8G_ENDSTRG: /* incomplete character, wait */
+ case U8G_ENDSTRI: /* incomplete character, wait */
+ if(!*ps_global->initial_cmds || octets_so_far >= sizeof(inputbuf)){
+ *ch = BADESC;
+ ret = 1;
+ }
+ else
+ inputbuf[octets_so_far++] = (0xff & *ps_global->initial_cmds++);
+
+ break;
+
+ default:
+ if(ucs & U8G_ERROR || ucs == UBOGON)
+ *ch = BADESC;
+ else
+ *ch = ucs;
+
+ ret = 1;
+ break;
+ }
+ }
+ }
+
+ if(!*ps_global->initial_cmds && ps_global->free_initial_cmds){
+ fs_give((void **) &ps_global->free_initial_cmds);
+ ps_global->initial_cmds = NULL;
+ }
+
+ return(ret);
+ }
+
+ if(firsttime) {
+ firsttime = 0;
+ if(ps_global->in_init_seq) {
+ ps_global->in_init_seq = 0;
+ ps_global->save_in_init_seq = 0;
+ clear_cursor_pos();
+ F_SET(F_USE_FK,ps_global,ps_global->orig_use_fkeys);
+ /* draw screen */
+ *ch = (UCS) ctrl('L');
+ return(1);
+ }
+ }
+
+ return(0);
+}
+
+
+#define TAPELEN 256
+static int tape[TAPELEN];
+static long recorded = 0L;
+static short length = 0;
+
+
+/*
+ * record user keystrokes
+ *
+ * Args: ch -- the character to record
+ *
+ * Returns: character recorded
+ */
+int
+key_recorder(int ch)
+{
+ tape[recorded++ % TAPELEN] = ch;
+ if(length < TAPELEN)
+ length++;
+
+ return(ch);
+}
+
+
+/*
+ * playback user keystrokes
+ *
+ * Args: ch -- ignored
+ *
+ * Returns: character played back or -1 to indicate end of tape
+ */
+int
+key_playback(int ch)
+{
+ ch = length ? tape[(recorded + TAPELEN - length--) % TAPELEN] : -1;
+ return(ch);
+}
+
+
+/*
+ * recent_keystroke - verbose version of key_playback
+ */
+int
+recent_keystroke(int *cv, char *cs, size_t cslen)
+{
+ int c;
+
+ if((c = key_playback(0)) != -1){
+ *cv = c;
+ snprintf(cs, cslen, "%.32s", pretty_command(c));
+ return(0);
+ }
+
+ return(-1);
+}
+
+
+#ifdef _WINDOWS
+int
+pcpine_oe_cursor(col, row)
+ int col;
+ long row;
+{
+ return((row == g_mc_row
+ && col >= g_mc_col
+ && col < ps_global->ttyo->screen_cols)
+ ? MSWIN_CURSOR_IBEAM
+ : MSWIN_CURSOR_ARROW);
+}
+#endif
diff --git a/alpine/osdep/termin.gen.h b/alpine/osdep/termin.gen.h
new file mode 100644
index 00000000..18c7c0de
--- /dev/null
+++ b/alpine/osdep/termin.gen.h
@@ -0,0 +1,48 @@
+/*
+ * $Id: termin.gen.h 890 2007-12-21 05:34:43Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_OSDEP_TERMIN_GEN_INCLUDED
+#define PINE_OSDEP_TERMIN_GEN_INCLUDED
+
+#include <general.h>
+#include "../radio.h"
+
+/* Useful Macros */
+#define READ_COMMAND(U) (read_command_prep() ? read_command(U) : NO_OP_COMMAND)
+
+/* exported prototypes */
+UCS read_command(char **);
+int read_command_prep(void);
+int key_recorder(int);
+int optionally_enter(char *, int, int, int, char *, ESCKEY_S *, HelpType, int *);
+UCS validatekeys(UCS);
+int process_config_input(UCS *);
+int key_recorder(int);
+int key_playback(int);
+int recent_keystroke(int *, char *, size_t);
+int init_tty_driver(struct pine *);
+void end_tty_driver(struct pine *);
+int PineRaw(int);
+UCS read_char(int);
+void init_keyboard(int);
+void end_keyboard(int);
+int pre_screen_config_opt_enter(char *, int, char *,
+ ESCKEY_S *, HelpType, int *);
+#ifdef _WINDOWS
+int pcpine_oe_cursor(int, long);
+#endif
+
+#endif /* PINE_OSDEP_TERMIN_GEN_INCLUDED */
diff --git a/alpine/osdep/termin.unx.c b/alpine/osdep/termin.unx.c
new file mode 100644
index 00000000..451d18c2
--- /dev/null
+++ b/alpine/osdep/termin.unx.c
@@ -0,0 +1,752 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: termin.unx.c 1025 2008-04-08 22:59:38Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+#include <system.h>
+#include <general.h>
+
+#include "../../c-client/mail.h" /* for MAILSTREAM and friends */
+#include "../../c-client/osdep.h"
+#include "../../c-client/rfc822.h" /* for soutr_t and such */
+#include "../../c-client/misc.h" /* for cpystr proto */
+#include "../../c-client/utf8.h" /* for CHARSET and such*/
+#include "../../c-client/imap4r1.h"
+
+#include "../../pith/charconv/utf8.h"
+#include "../../pith/charconv/filesys.h"
+
+#include "../../pith/osdep/color.h"
+#include "../../pith/osdep/collate.h"
+#include "../../pith/osdep/err_desc.h"
+
+#include "../../pith/debug.h"
+#include "../../pith/state.h"
+#include "../../pith/conf.h"
+#include "../../pith/detach.h"
+#include "../../pith/adrbklib.h"
+#include "../../pith/remote.h"
+#include "../../pith/imap.h"
+#include "../../pith/status.h"
+
+#include "../pico/estruct.h"
+
+#include "../../pico/estruct.h"
+#include "../../pico/pico.h"
+#include "../../pico/osdep/raw.h"
+#include "../../pico/osdep/signals.h"
+#include "../../pico/osdep/mouse.h"
+#include "../../pico/osdep/read.h"
+#include "../../pico/osdep/getkey.h"
+#include "../../pico/osdep/tty.h"
+#include "../../pico/keydefs.h"
+
+#include "../talk.h"
+#include "../radio.h"
+#include "../dispfilt.h"
+#include "../signal.h"
+#include "../mailcmd.h"
+#include "../setup.h"
+
+#include "termin.gen.h"
+#include "termout.gen.h"
+#include "termin.unx.h"
+
+
+
+/*======================================================================
+ Things having to do with reading from the tty driver and keyboard
+ - initialize tty driver and reset tty driver
+ - read a character from terminal with keyboard escape seqence mapping
+ - initialize keyboard (keypad or such) and reset keyboard
+ - prompt user for a line of input
+ - read a command from keyboard with timeouts.
+
+ ====*/
+
+
+/*
+ * Helpful definitions
+ */
+/*
+ * Should really be using pico's TERM's t_getchar to read a character but
+ * we're just calling ttgetc directly for now. Ttgetc is the same as
+ * t_getchar whenever we use it so we're avoiding the trouble of initializing
+ * the TERM struct and calling ttgetc directly.
+ */
+#define READ_A_CHAR() ttgetc(NO_OP_COMMAND, key_rec, read_bail)
+
+
+/*
+ * Internal prototypes
+ */
+int pine_simple_ttgetc(int (*recorder)(int), void (*bail_handler)(void));
+UCS check_for_timeout(int);
+void read_bail(void);
+
+
+/*----------------------------------------------------------------------
+ Initialize the tty driver to do single char I/O and whatever else (UNIX)
+
+ Args: struct pine
+
+ Result: tty driver is put in raw mode so characters can be read one
+ at a time. Returns -1 if unsuccessful, 0 if successful.
+
+Some file descriptor voodoo to allow for pipes across vforks. See
+open_mailer for details.
+ ----------------------------------------------------------------------*/
+int
+init_tty_driver(struct pine *ps)
+{
+#ifdef MOUSE
+ if(F_ON(F_ENABLE_MOUSE, ps_global))
+ init_mouse();
+#endif /* MOUSE */
+
+ /* turn off talk permission by default */
+
+ if(F_ON(F_ALLOW_TALK, ps))
+ allow_talk(ps);
+ else
+ disallow_talk(ps);
+
+ return(PineRaw(1));
+}
+
+
+
+/*----------------------------------------------------------------------
+ Set or clear the specified tty mode
+
+ Args: ps -- struct pine
+ mode -- mode bits to modify
+ clear -- whether or not to clear or set
+
+ Result: tty driver mode change.
+ ----------------------------------------------------------------------*/
+void
+tty_chmod(struct pine *ps, int mode, int func)
+{
+ char *tty_name;
+ int new_mode;
+ struct stat sbuf;
+ static int saved_mode = -1;
+
+ /* if no problem figuring out tty's name & mode? */
+ if((((tty_name = (char *) ttyname(STDIN_FD)) != NULL
+ && fstat(STDIN_FD, &sbuf) == 0)
+ || ((tty_name = (char *) ttyname(STDOUT_FD)) != NULL
+ && fstat(STDOUT_FD, &sbuf) == 0))
+ && !(func == TMD_RESET && saved_mode < 0)){
+ new_mode = (func == TMD_RESET)
+ ? saved_mode
+ : (func == TMD_CLEAR)
+ ? (sbuf.st_mode & ~mode)
+ : (sbuf.st_mode | mode);
+ /* assign tty new mode */
+ if(our_chmod(tty_name, new_mode) == 0){
+ if(func == TMD_RESET) /* forget we knew */
+ saved_mode = -1;
+ else if(saved_mode < 0)
+ saved_mode = sbuf.st_mode; /* remember original */
+ }
+ }
+}
+
+
+
+/*----------------------------------------------------------------------
+ End use of the tty, put it back into it's normal mode (UNIX)
+
+ Args: ps -- struct pine
+
+ Result: tty driver mode change.
+ ----------------------------------------------------------------------*/
+void
+end_tty_driver(struct pine *ps)
+{
+ ps = ps; /* get rid of unused parameter warning */
+
+#ifdef MOUSE
+ end_mouse();
+#endif /* MOUSE */
+ fflush(stdout);
+ dprint((2, "about to end_tty_driver\n"));
+
+ tty_chmod(ps, 0, TMD_RESET);
+ PineRaw(0);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Actually set up the tty driver (UNIX)
+
+ Args: state -- which state to put it in. 1 means go into raw, 0 out of
+
+ Result: returns 0 if successful and < 0 if not.
+ ----*/
+int
+PineRaw(int state)
+{
+ int result;
+
+ result = Raw(state);
+
+ if(result == 0 && state == 1){
+ /*
+ * Only go into 8 bit mode if we are doing something other
+ * than plain ASCII. This will save the folks that have
+ * their parity on their serial lines wrong the trouble of
+ * getting it right
+ */
+ if((ps_global->keyboard_charmap && ps_global->keyboard_charmap[0] &&
+ strucmp(ps_global->keyboard_charmap, "us-ascii"))
+ || (ps_global->display_charmap && ps_global->display_charmap[0] &&
+ strucmp(ps_global->display_charmap, "us-ascii")))
+ bit_strip_off();
+
+#ifdef DEBUG
+ if(debug < 9) /* only on if full debugging set */
+#endif
+ quit_char_off();
+ ps_global->low_speed = ttisslow();
+ crlf_proc(0);
+ xonxoff_proc(F_ON(F_PRESERVE_START_STOP, ps_global));
+ }
+
+ return(result);
+}
+
+
+#if defined(SIGWINCH) && defined(TIOCGWINSZ)
+jmp_buf winch_state;
+int winch_occured = 0;
+int ready_for_winch = 0;
+#endif
+
+/*----------------------------------------------------------------------
+ This checks whether or not a character (UNIX)
+ is ready to be read, or it times out.
+
+ Args: time_out -- number of seconds before it will timeout
+
+ Result: Returns a NO_OP_IDLE or a NO_OP_COMMAND if the timeout expires
+ before input is available, or a KEY_RESIZE if a resize event
+ occurs, or READY_TO_READ if input is available before the timeout.
+ ----*/
+UCS
+check_for_timeout(int time_out)
+{
+ UCS res = NO_OP_COMMAND;
+
+ fflush(stdout);
+
+#if defined(SIGWINCH) && defined(TIOCGWINSZ)
+ if(!winch_occured){
+ if(setjmp(winch_state) != 0){
+ winch_occured = 1;
+ ready_for_winch = 0;
+
+ /*
+ * Need to unblock signal after longjmp from handler, because
+ * signal is normally unblocked upon routine exit from the handler.
+ */
+ our_sigunblock(SIGWINCH);
+ }
+ else
+ ready_for_winch = 1;
+ }
+
+ if(winch_occured){
+ winch_occured = ready_for_winch = 0;
+ fix_windsize(ps_global);
+ return(KEY_RESIZE);
+ }
+#endif /* SIGWINCH */
+
+ switch(res = input_ready(time_out)){
+ case BAIL_OUT:
+ read_bail(); /* non-tragic exit */
+ /* NO RETURN */
+
+ case PANIC_NOW:
+ panic1("Select error: %s\n", error_description(errno));
+ /* NO RETURN */
+
+ case READ_INTR:
+ res = NO_OP_COMMAND;
+ /* fall through */
+
+ case NO_OP_IDLE:
+ case NO_OP_COMMAND:
+ case READY_TO_READ:
+#if defined(SIGWINCH) && defined(TIOCGWINSZ)
+ ready_for_winch = 0;
+#endif
+ return(res);
+ }
+
+ /* not reachable */
+ return(res);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Read input characters with lots of processing for arrow keys and such (UNIX)
+
+ Args: time_out -- The timeout to for the reads
+
+ Result: returns the character read. Possible special chars.
+
+ This deals with function and arrow keys as well.
+
+ The idea is that this routine handles all escape codes so it done in
+ only one place. Especially so the back arrow key can work when entering
+ things on a line. Also so all function keys can be disabled and not
+ cause weird things to happen.
+ ---*/
+UCS
+read_char(int time_out)
+{
+ UCS status, cc, ch;
+ int (*key_rec)(int);
+
+ key_rec = key_recorder;
+ if(ps_global->conceal_sensitive_debugging)
+ key_rec = NULL;
+
+ /* Get input from initial-keystrokes */
+ if(process_config_input(&cc)){
+ ch = cc;
+ return(ch);
+ }
+
+ if((ch = check_for_timeout(time_out)) != READY_TO_READ)
+ goto done;
+
+ switch(status = kbseq(pine_simple_ttgetc, key_rec, read_bail,
+ ps_global->input_cs, &ch)){
+ case KEY_DOUBLE_ESC:
+ /*
+ * Special hack to get around comm devices eating control characters.
+ */
+ if(check_for_timeout(5) != READY_TO_READ){
+ dprint((9, "Read char: incomplete double escape timed out...\n"));
+ ch = KEY_JUNK; /* user typed ESC ESC, then stopped */
+ goto done;
+ }
+ else
+ ch = READ_A_CHAR();
+
+ ch &= 0x7f;
+
+ /* We allow a 3-digit number between 001 and 255 */
+ if(isdigit((unsigned char) ch)){
+ int n = 0, i = ch - '0';
+
+ if(i < 0 || i > 2){
+ dprint((9, "Read char: double escape followed by 1st digit not 0, 1, or 2... (%d)\n", i));
+ ch = KEY_JUNK;
+ goto done; /* bogus literal char value */
+ }
+
+ while(n++ < 2){
+ if(check_for_timeout(5) != READY_TO_READ
+ || (!isdigit((unsigned char) (ch = READ_A_CHAR()))
+ || (n == 1 && i == 2 && ch > '5')
+ || (n == 2 && i == 25 && ch > '5'))){
+ dprint((9, "Read char: bad double escape, either timed out or too large 3-digit num...\n"));
+ ch = KEY_JUNK; /* user typed ESC ESC #, stopped */
+ goto done;
+ }
+
+ i = (i * 10) + (ch - '0');
+ }
+
+ ch = i;
+ }
+ else{ /* or, normal case, ESC ESC c means ^c */
+ if(islower((unsigned char) ch)) /* canonicalize if alpha */
+ ch = toupper((unsigned char) ch);
+
+ ch = (isalpha((unsigned char)ch) || ch == '@'
+ || (ch >= '[' && ch <= '_'))
+ ? ctrl(ch) : ((ch == SPACE) ? ctrl('@'): ch);
+ dprint((9, "Read char: this is a successful double escape...\n"));
+ }
+
+ goto done;
+
+#ifdef MOUSE
+ case KEY_XTERM_MOUSE:
+ if(mouseexist()){
+ /*
+ * Special hack to get mouse events from an xterm.
+ * Get the details, then pass it past the keymenu event
+ * handler, and then to the installed handler if there
+ * is one...
+ */
+ static int down = 0;
+ int x, y, button;
+ unsigned long cmd;
+
+ clear_cursor_pos();
+ button = READ_A_CHAR() & 0x03;
+
+ x = READ_A_CHAR() - '!';
+ y = READ_A_CHAR() - '!';
+
+ ch = NO_OP_COMMAND;
+ if(button == 0){ /* xterm button 1 down */
+ down = 1;
+ if(checkmouse(&cmd, 1, x, y))
+ ch = cmd;
+ }
+ else if (down && button == 3){
+ down = 0;
+ if(checkmouse(&cmd, 0, x, y))
+ ch = cmd;
+ }
+
+ goto done;
+ }
+
+ break;
+#endif /* MOUSE */
+
+ case KEY_UP :
+ case KEY_DOWN :
+ case KEY_RIGHT :
+ case KEY_LEFT :
+ case KEY_PGUP :
+ case KEY_PGDN :
+ case KEY_HOME :
+ case KEY_END :
+ case KEY_DEL :
+ case PF1 :
+ case PF2 :
+ case PF3 :
+ case PF4 :
+ case PF5 :
+ case PF6 :
+ case PF7 :
+ case PF8 :
+ case PF9 :
+ case PF10 :
+ case PF11 :
+ case PF12 :
+ dprint((9, "Read char returning: 0x%x %s\n", status, pretty_command(status)));
+ return(status);
+
+ case CTRL_KEY_UP :
+ status = KEY_UP;
+ dprint((9, "Read char returning: 0x%x %s (for CTRL_KEY_UP)\n", status, pretty_command(status)));
+ return(status);
+
+ case CTRL_KEY_DOWN :
+ status = KEY_DOWN;
+ dprint((9, "Read char returning: 0x%x %s (for CTRL_KEY_DOWN)\n", status, pretty_command(status)));
+ return(status);
+
+ case CTRL_KEY_RIGHT :
+ status = KEY_RIGHT;
+ dprint((9, "Read char returning: 0x%x %s (for CTRL_KEY_RIGHT)\n", status, pretty_command(status)));
+ return(status);
+
+ case CTRL_KEY_LEFT :
+ status = KEY_LEFT;
+ dprint((9, "Read char returning: 0x%x %s (for CTRL_KEY_LEFT)\n", status, pretty_command(status)));
+ return(status);
+
+ case KEY_SWALLOW_Z:
+ status = KEY_JUNK;
+ case KEY_SWAL_UP:
+ case KEY_SWAL_DOWN:
+ case KEY_SWAL_LEFT:
+ case KEY_SWAL_RIGHT:
+ do
+ if(check_for_timeout(2) != READY_TO_READ){
+ status = KEY_JUNK;
+ break;
+ }
+ while(!strchr("~qz", READ_A_CHAR()));
+ ch = (status == KEY_JUNK) ? status : status - (KEY_SWAL_UP - KEY_UP);
+ goto done;
+
+ case KEY_KERMIT:
+ do{
+ cc = ch;
+ if(check_for_timeout(2) != READY_TO_READ){
+ status = KEY_JUNK;
+ break;
+ }
+ else
+ ch = READ_A_CHAR();
+ }while(cc != '\033' && ch != '\\');
+
+ ch = KEY_JUNK;
+ goto done;
+
+ case BADESC:
+ ch = KEY_JUNK;
+ goto done;
+
+ case 0: /* regular character */
+ default:
+ /*
+ * we used to strip (ch &= 0x7f;), but this seems much cleaner
+ * in the face of line noise and has the benefit of making it
+ * tougher to emit mistakenly labeled MIME...
+ */
+ if((ch & ~0x7f)
+ && ((!ps_global->keyboard_charmap || !strucmp(ps_global->keyboard_charmap, "US-ASCII"))
+ && (!ps_global->display_charmap || !strucmp(ps_global->display_charmap, "US-ASCII")))){
+ dprint((9, "Read char sees ch = 0x%x status=0x%x, returns KEY_JUNK\n", ch, status));
+ return(KEY_JUNK);
+ }
+ else if(ch == ctrl('Z')){
+ dprint((9, "Read char got ^Z, calling do_suspend\n"));
+ ch = do_suspend();
+ dprint((9, "After do_suspend Read char returns 0x%x %s\n", ch, pretty_command(ch)));
+ return(ch);
+ }
+#ifdef MOUSE
+ else if(ch == ctrl('\\')){
+ int e;
+
+ dprint((9, "Read char got ^\\, toggle xterm mouse\n"));
+ if(F_ON(F_ENABLE_MOUSE, ps_global)){
+ (e=mouseexist()) ? end_mouse() : (void) init_mouse();
+ if(e != mouseexist())
+ q_status_message1(SM_ASYNC, 0, 2, "Xterm mouse tracking %s!",
+ mouseexist() ? "on" : "off");
+ else if(!e)
+ q_status_message1(SM_ASYNC, 0, 2, "See help for feature \"%s\" ($DISPLAY variable set?)", pretty_feature_name(feature_list_name(F_ENABLE_MOUSE), -1));
+ }
+ else
+ q_status_message1(SM_ASYNC, 0, 2, "Feature \"%s\" not enabled",
+ pretty_feature_name(feature_list_name(F_ENABLE_MOUSE), -1));
+
+ return(NO_OP_COMMAND);
+ }
+#endif /* MOUSE */
+
+
+ done:
+#ifdef DEBUG
+ if(ps_global->conceal_sensitive_debugging && debug < 10){
+ dprint((9, "Read char returning: <hidden char>\n"));
+ }
+ else{
+ dprint((9, "Read char returning: 0x%x %s\n", ch, pretty_command(ch)));
+ }
+#endif
+
+ return(ch);
+ }
+
+ /* not reachable */
+ return(KEY_JUNK);
+}
+
+
+/*----------------------------------------------------------------------
+ Reading input somehow failed and we need to shutdown now
+
+ Args: none
+
+ Result: pine exits
+
+ ---*/
+void
+read_bail(void)
+{
+ dprint((1, "read_bail: cleaning up\n"));
+ end_signals(1);
+
+ /*
+ * This gets rid of temporary cache files for remote addrbooks.
+ */
+ completely_done_with_adrbks();
+
+ /*
+ * This flushes out deferred changes and gets rid of temporary cache
+ * files for remote config files.
+ */
+ if(ps_global->prc){
+ if(ps_global->prc->outstanding_pinerc_changes)
+ write_pinerc(ps_global, Main, WRP_NOUSER);
+
+ if(ps_global->prc->rd)
+ rd_close_remdata(&ps_global->prc->rd);
+
+ free_pinerc_s(&ps_global->prc);
+ }
+
+ /* as does this */
+ if(ps_global->post_prc){
+ if(ps_global->post_prc->outstanding_pinerc_changes)
+ write_pinerc(ps_global, Post, WRP_NOUSER);
+
+ if(ps_global->post_prc->rd)
+ rd_close_remdata(&ps_global->post_prc->rd);
+
+ free_pinerc_s(&ps_global->post_prc);
+ }
+
+ sp_end();
+
+ dprint((1, "done with read_bail clean up\n"));
+
+ imap_flush_passwd_cache(TRUE);
+ end_keyboard(F_ON(F_USE_FK,ps_global));
+ end_tty_driver(ps_global);
+ if(filter_data_file(0))
+ our_unlink(filter_data_file(0));
+
+ exit(0);
+}
+
+
+int
+pine_simple_ttgetc(int (*fi)(int), void (*fv)(void))
+{
+ int ch;
+
+#if defined(SIGWINCH) && defined(TIOCGWINSZ)
+ if(!winch_occured){
+ if(setjmp(winch_state) != 0){
+ winch_occured = 1;
+ ready_for_winch = 0;
+
+ /*
+ * Need to unblock signal after longjmp from handler, because
+ * signal is normally unblocked upon routine exit from the handler.
+ */
+ our_sigunblock(SIGWINCH);
+ }
+ else
+ ready_for_winch = 1;
+ }
+
+ if(winch_occured){
+ winch_occured = ready_for_winch = 0;
+ fix_windsize(ps_global);
+ return(KEY_RESIZE);
+ }
+#endif /* SIGWINCH */
+
+ ch = simple_ttgetc(fi, fv);
+
+#if defined(SIGWINCH) && defined(TIOCGWINSZ)
+ ready_for_winch = 0;
+#endif
+
+ return(ch);
+}
+
+
+
+extern char term_name[];
+/* -------------------------------------------------------------------
+ Set up the keyboard -- usually enable some function keys (UNIX)
+
+ Args: struct pine
+
+So far all we do here is turn on keypad mode for certain terminals
+
+Hack for NCSA telnet on an IBM PC to put the keypad in the right mode.
+This is the same for a vtXXX terminal or [zh][12]9's which we have
+a lot of at UW
+ ----*/
+void
+init_keyboard(int use_fkeys)
+{
+ if(use_fkeys && (!strucmp(term_name,"vt102")
+ || !strucmp(term_name,"vt100")))
+ printf("\033\133\071\071\150");
+}
+
+
+
+/*----------------------------------------------------------------------
+ Clear keyboard, usually disable some function keys (UNIX)
+
+ Args: pine state (terminal type)
+
+ Result: keyboard state reset
+ ----*/
+void
+end_keyboard(int use_fkeys)
+{
+ if(use_fkeys && (!strcmp(term_name, "vt102")
+ || !strcmp(term_name, "vt100"))){
+ printf("\033\133\071\071\154");
+ fflush(stdout);
+ }
+}
+
+
+/*
+ * This is a bare-bones implementation which is missing most of the
+ * features of the real optionally_enter. The initial value of string is
+ * isgnored. The escape_list is ignored. The help is not implemented. The
+ * only flag implemented is OE_PASSWD. We don't go into raw mode so the
+ * only input possible is a line (the EOL is stripped before returning).
+ */
+int
+pre_screen_config_opt_enter(char *string, int string_size, char *prompt,
+ ESCKEY_S *escape_list, HelpType help, int *flags)
+{
+ char *pw;
+ int return_v = -10;
+
+ while(return_v == -10) {
+
+ if(flags && (*flags & (OE_PASSWD | OE_PASSWD_NOAST))){
+ if((pw = getpass(prompt)) != NULL){
+ if(strlen(pw) < string_size){
+ strncpy(string, pw, string_size);
+ string[string_size-1] = '\0';
+ return_v = 0;
+ }
+ else{
+ fputs("Password too long\n", stderr);
+ return_v = -1;
+ }
+ }
+ else
+ return_v = 1; /* cancel? */
+ }
+ else{
+ char *p;
+
+ fputs(prompt, stdout);
+ fgets(string, string_size, stdin);
+ string[string_size-1] = '\0';
+ if((p = strpbrk(string, "\r\n")) != NULL)
+ *p = '\0';
+
+ return_v = 0;
+ }
+ }
+
+ return(return_v);
+}
+
+
diff --git a/alpine/osdep/termin.unx.h b/alpine/osdep/termin.unx.h
new file mode 100644
index 00000000..7ead09f0
--- /dev/null
+++ b/alpine/osdep/termin.unx.h
@@ -0,0 +1,29 @@
+/*
+ * $Id: termin.unx.h 769 2007-10-24 00:15:40Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_OSDEP_TERMIN_UNX_INCLUDED
+#define PINE_OSDEP_TERMIN_UNX_INCLUDED
+
+
+#include <general.h>
+
+
+/* exported prototypes */
+void tty_chmod(struct pine *, int, int);
+
+
+
+#endif /* PINE_OSDEP_TERMIN_UNX_INCLUDED */
diff --git a/alpine/osdep/termin.wnt.c b/alpine/osdep/termin.wnt.c
new file mode 100644
index 00000000..83dc8284
--- /dev/null
+++ b/alpine/osdep/termin.wnt.c
@@ -0,0 +1,352 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: termin.unx.c 193 2006-10-20 17:09:26Z mikes@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "../../c-client/mail.h" /* for MAILSTREAM and friends */
+#include "../../c-client/osdep.h"
+#include "../../c-client/rfc822.h" /* for soutr_t and such */
+#include "../../c-client/misc.h" /* for cpystr proto */
+#include "../../c-client/utf8.h" /* for CHARSET and such*/
+#include "../../c-client/imap4r1.h"
+
+#include "../../pith/charconv/utf8.h"
+#include "../../pith/charconv/filesys.h"
+
+#include "../../pith/osdep/color.h"
+
+#include "../../pith/debug.h"
+#include "../../pith/state.h"
+#include "../../pith/conf.h"
+#include "../../pith/detach.h"
+
+#include "../pico/estruct.h"
+#include "../pico/pico.h"
+
+#include "../../pico/osdep/raw.h"
+#include "../../pico/osdep/signals.h"
+#include "../../pico/osdep/mouse.h"
+#include "../../pico/keydefs.h"
+
+#include "../talk.h"
+#include "../radio.h"
+#include "../dispfilt.h"
+#include "../signal.h"
+#include "../status.h"
+#include "../titlebar.h"
+
+#include "../../pico/osdep/mswin.h"
+
+#include "termin.gen.h"
+#include "termin.wnt.h"
+#include "termout.gen.h"
+
+#define RETURN_CH(X) return(key_rec ? ((*key_rec)(X)) : (int)(X))
+
+/* global to tell us if the window was resized. */
+static int DidResize = FALSE;
+
+int pine_window_resize_callback(void);
+
+/*----------------------------------------------------------------------
+ Initialize the tty driver to do single char I/O and whatever else
+
+ Input: struct pine
+
+ Result: tty driver is put in raw mode
+ ----------------------------------------------------------------------*/
+int
+init_tty_driver(struct pine *pine)
+{
+ mswin_showwindow();
+ mswin_setresizecallback (pine_window_resize_callback);
+ init_mouse (); /* always a mouse under windows? */
+ return(PineRaw(1));
+}
+
+
+/*----------------------------------------------------------------------
+ End use of the tty, put it back into it's normal mode
+
+ Input: struct pine
+
+ Result: tty driver mode change
+ ----------------------------------------------------------------------*/
+void
+end_tty_driver(struct pine *pine)
+{
+ dprint((2, "about to end_tty_driver\n"));
+ mswin_clearresizecallback (pine_window_resize_callback);
+}
+
+/*----------------------------------------------------------------------
+ Actually set up the tty driver
+
+ Args: state -- which state to put it in. 1 means go into raw, 0 out of
+
+ Result: returns 0 if successful and -1 if not.
+ ----*/
+
+int
+PineRaw(int state)
+{
+ ps_global->low_speed = 0;
+ return(0);
+}
+
+/*----------------------------------------------------------------------
+ Read input characters with lots of processing for arrow keys and such
+
+ Input: none
+
+ Result: returns the character read. Possible special chars defined h file
+
+
+ This deals with function and arrow keys as well.
+ It returns ^T for up , ^U for down, ^V for forward and ^W for back.
+ These are just sort of arbitrarily picked and might be changed.
+ They are defined in defs.h. Didn't want to use 8 bit chars because
+ the values are signed chars, though it ought to work with negative
+ values.
+
+ The idea is that this routine handles all escape codes so it done in
+ only one place. Especially so the back arrow key can work when entering
+ things on a line. Also so all function keys can be broken and not
+ cause weird things to happen.
+----------------------------------------------------------------------*/
+
+UCS
+read_char(int tm)
+{
+ unsigned ch = 0;
+ time_t timein;
+ int (*key_rec)(int);
+
+ key_rec = key_recorder;
+ if(ps_global->conceal_sensitive_debugging && debug < 10)
+ key_rec = NULL;
+
+ if(process_config_input((int *) &ch))
+ RETURN_CH(ch);
+
+ if (DidResize) {
+ DidResize = FALSE;
+ fix_windsize(ps_global);
+ return(KEY_RESIZE);
+ }
+
+ mswin_setcursor(MSWIN_CURSOR_ARROW);
+
+ if(tm){
+ timein = time(0L);
+ /* mswin_charavail() Yields control to other window apps. */
+ while (!mswin_charavail()) {
+ if(time(0L) >= timein + (time_t) tm){
+ ch = (tm < IDLE_TIMEOUT) ? NO_OP_COMMAND : NO_OP_IDLE;
+ goto gotone;
+ }
+ if (DidResize) {
+ DidResize = FALSE;
+ fix_windsize(ps_global);
+ return(KEY_RESIZE);
+ }
+ if(checkmouse(&ch,0,0,0))
+ goto gotone;
+ }
+ }
+ else
+ while(!mswin_charavail())
+ if(checkmouse(&ch,0,0,0))
+ goto gotone;
+
+ ch = mswin_getc_fast();
+
+ /*
+ * mswin_getc_fast() returns UCS type codes (see keydefs.h). Map
+ * those values to what is expected from read_char(): Ctrl keys being
+ * in the 0..0x1f range, NO_OP_COMMAND, etc.
+ */
+ if(ch == NODATA)
+ ch = NO_OP_COMMAND;
+ else if(ch & CTRL)
+ {
+ ch &= ~CTRL;
+ if(ch >= '@' && ch < '@' + 32) {
+ ch -= '@';
+ }
+ else {
+ /*
+ * This could be a ctrl key this part of Pine doesn't understand.
+ * For example, CTRL|KEY_LEFT. Map these to nops.
+ */
+ ch = NO_OP_COMMAND;
+ }
+ }
+
+gotone:
+ /* More obtuse key mapping. If it is a mouse event, the return
+ * may be KEY_MOUSE, which indicates to the upper layer that it
+ * is a mouse event. Return it here to avoid the code that
+ * follows which would do a (ch & 0xff).
+ */
+ if (ch == KEY_MOUSE)
+ RETURN_CH(ch);
+
+ /*
+ * WARNING: Hack notice.
+ * the mouse interaction complicates this expression a bit as
+ * if function key mode is set, PFn values are setup for return
+ * by the mouse event catcher. For now, just special case them
+ * since they don't conflict with any of the DOS special keys.
+ */
+ if((ch & 0xff) == ctrl('Z'))
+ RETURN_CH(do_suspend());
+
+ RETURN_CH (ch);
+}
+
+void
+flush_input(void)
+{
+ mswin_flush_input();
+}
+
+void
+init_keyboard(int use_fkeys)
+{
+}
+
+void
+end_keyboard(int use_fkeys)
+{
+}
+
+int
+pre_screen_config_opt_enter(char *utf8string, int utf8string_size,
+ char *utf8prompt, ESCKEY_S *escape_list,
+ HelpType help, int *flags)
+{
+ mswin_setwindow(NULL, NULL, NULL, NULL, NULL, NULL);
+ return(win_dialog_opt_enter(utf8string, utf8string_size, utf8prompt, escape_list,
+ help, flags));
+}
+
+int
+win_dialog_opt_enter(char *utf8string, int utf8string_size, char *utf8prompt,
+ ESCKEY_S *escape_list, HelpType help, int *flags)
+{
+ MDlgButton button_list[12];
+ LPTSTR free_names[12];
+ LPTSTR free_labels[12];
+ int i, b, return_v;
+ char **help_text;
+ char *utf8, *saved_string = NULL;
+ UCS *string, *s, *prompt;
+ int n;
+
+ memset(&free_names, 0, sizeof(LPTSTR) * 12);
+ memset(&free_labels, 0, sizeof(LPTSTR) * 12);
+ memset (&button_list, 0, sizeof (MDlgButton) * 12);
+ b = 0;
+ for (i = 0; escape_list && escape_list[i].ch != -1 && i < 11; ++i) {
+ if (escape_list[i].name != NULL
+ && escape_list[i].ch > 0 && escape_list[i].ch < 256) {
+ button_list[b].ch = escape_list[i].ch;
+ button_list[b].rval = escape_list[i].rval;
+ free_names[b] = utf8_to_lptstr(escape_list[i].name);
+ button_list[b].name = free_names[b];
+ free_labels[b] = utf8_to_lptstr(escape_list[i].label);
+ button_list[b].label = free_labels[b];
+ b++;
+ }
+ }
+
+ button_list[b].ch = -1;
+
+ if(utf8string)
+ saved_string = cpystr(utf8string);
+
+ /* assumption here is that HelpType is char ** */
+ help_text = help;
+
+ n = utf8string_size;
+ string = (UCS *) fs_get(n * sizeof(UCS));
+ s = utf8_to_ucs4_cpystr(utf8string);
+ if(s){
+ ucs4_strncpy(string, s, n);
+ string[n-1] = '\0';
+ fs_give((void **) &s);
+ }
+
+ prompt = utf8_to_ucs4_cpystr(utf8prompt ? utf8prompt : "");
+
+ return_v = mswin_dialog(prompt, string, n,
+ (flags && *flags & OE_APPEND_CURRENT),
+ (flags && *flags & OE_PASSWD_NOAST) ? 10 :
+ (flags && *flags & OE_PASSWD) ? 1 : 0,
+ button_list,
+ help_text, flags ? *flags : OE_NONE);
+
+ for(i = 0; i < 12; i++){
+ if(free_names[i])
+ fs_give((void **) &free_names[i]);
+ if(free_labels[i])
+ fs_give((void **) &free_labels[i]);
+ }
+
+ utf8 = ucs4_to_utf8_cpystr(string);
+ if(utf8){
+ strncpy(utf8string, utf8, utf8string_size);
+ utf8string[utf8string_size-1] = '\0';
+ fs_give((void **) &utf8);
+ }
+
+ if(string)
+ fs_give((void **) &string);
+
+ if(prompt)
+ fs_give((void **) &prompt);
+
+ if(flags && (saved_string && !utf8string || !saved_string && utf8string ||
+ (saved_string && string && strcmp(saved_string, utf8string))))
+ *flags |= OE_USER_MODIFIED;
+
+ if(saved_string)
+ fs_give((void **) &saved_string);
+
+ return(return_v);
+}
+
+
+/*----------------------------------------------------------------------
+ Flag the fact the window has resized.
+*/
+int
+pine_window_resize_callback (void)
+{
+ DidResize = TRUE;
+ return(0);
+}
+
+void
+intr_proc(int state)
+{
+ return; /* no op */
+}
+
diff --git a/alpine/osdep/termin.wnt.h b/alpine/osdep/termin.wnt.h
new file mode 100644
index 00000000..68480852
--- /dev/null
+++ b/alpine/osdep/termin.wnt.h
@@ -0,0 +1,32 @@
+/*
+ * $Id: termin.unx.h 136 2006-09-22 20:06:05Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_OSDEP_TERMIN_WNT_INCLUDED
+#define PINE_OSDEP_TERMIN_WNT_INCLUDED
+
+
+#include <general.h>
+#include "../radio.h"
+#include "../../pith/helptext.h"
+
+
+/* exported prototypes */
+void flush_input(void);
+int win_dialog_opt_enter(char *, int, char *, ESCKEY_S *, HelpType, int *);
+void intr_proc(int);
+
+
+#endif /* PINE_OSDEP_TERMIN_WNT_INCLUDED */
diff --git a/alpine/osdep/termout.gen.c b/alpine/osdep/termout.gen.c
new file mode 100644
index 00000000..a006c694
--- /dev/null
+++ b/alpine/osdep/termout.gen.c
@@ -0,0 +1,584 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: termout.gen.c 1012 2008-03-26 00:44:22Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+#include <system.h>
+#include <general.h>
+
+#include "../../c-client/mail.h" /* for MAILSTREAM and friends */
+#include "../../c-client/osdep.h"
+#include "../../c-client/rfc822.h" /* for soutr_t and such */
+#include "../../c-client/misc.h" /* for cpystr proto */
+#include "../../c-client/utf8.h" /* for CHARSET and such*/
+#include "../../c-client/imap4r1.h"
+
+#include "../../pith/osdep/color.h"
+#include "../../pith/charconv/filesys.h"
+
+#include "../../pith/debug.h"
+#include "../../pith/newmail.h"
+#include "../../pith/filter.h"
+#include "../../pith/handle.h"
+#include "../../pith/conf.h"
+#include "../../pith/mimedesc.h"
+
+#include "../../pico/estruct.h"
+#include "../../pico/pico.h"
+#include "../../pico/keydefs.h"
+#include "../../pico/osdep/color.h"
+
+#include "../status.h"
+#include "../radio.h"
+#include "../folder.h"
+#include "../keymenu.h"
+#include "../send.h"
+#include "../mailindx.h"
+
+int _line = FARAWAY;
+int _col = FARAWAY;
+
+#include "termout.gen.h"
+
+
+#define PUTLINE_BUFLEN 256
+
+
+
+/*
+ * Generic tty output routines...
+ */
+
+/*----------------------------------------------------------------------
+ Printf style output line to the screen at given position, 0 args
+
+ Args: x -- column position on the screen
+ y -- row position on the screen
+ line -- line of text to output
+
+ Result: text is output
+ cursor position is update
+ ----*/
+void
+PutLine0(int x, int y, register char *line)
+{
+ MoveCursor(x,y);
+ Write_to_screen(line);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Output line of length len to the display observing embedded attributes
+
+ Args: x -- column position on the screen
+ y -- column position on the screen
+ line -- text to be output
+ length -- length of text to be output
+
+ Result: text is output
+ cursor position is updated
+ ----------------------------------------------------------------------*/
+void
+PutLine0n8b(int x, int y, register char *line, int length, HANDLE_S *handles)
+{
+ unsigned char c;
+ int is_inv = 0, is_bold = 0, is_uline = 0, is_fg = 0, is_bg = 0;
+#ifdef _WINDOWS
+ int hkey = 0;
+#endif
+
+ MoveCursor(x,y);
+
+ while(length--){
+
+ c = (unsigned char) *line++;
+
+ if(c == (unsigned char) TAG_EMBED && length){
+
+ length--;
+
+ switch(*line++){
+ case TAG_INVON :
+ StartInverse();
+ is_inv = 1;
+ break;
+
+ case TAG_INVOFF :
+ EndInverse();
+ is_inv = 0;
+ break;
+
+ case TAG_BOLDON :
+ StartBold();
+ is_bold = 1;
+ break;
+
+ case TAG_BOLDOFF :
+ EndBold();
+ is_bold = 0;
+ break;
+
+ case TAG_ITALICON : /* express italic as uline in terminal */
+ case TAG_ULINEON :
+ StartUnderline();
+ is_uline = 1;
+ break;
+
+ case TAG_ITALICOFF : /* express italic as uline in terminal */
+ case TAG_ULINEOFF :
+ EndUnderline();
+ is_uline = 0;
+ break;
+
+ case TAG_HANDLE :
+ length -= *line + 1; /* key length plus length tag */
+ if(handles){
+ int key, n, current_key = 0;
+
+ for(key = 0, n = *line++; n; n--) /* forget Horner? */
+ key = (key * 10) + (*line++ - '0');
+
+#if _WINDOWS
+ hkey = key;
+#endif
+
+ if(handles->using_is_used){
+ HANDLE_S *h;
+
+ for(h = handles; h; h = h->next)
+ if(h->is_used)
+ break;
+
+ if(h)
+ current_key = h->key;
+ }
+ else
+ current_key = handles->key;
+
+ if(key == current_key){
+ COLOR_PAIR *curcolor = NULL;
+ COLOR_PAIR *revcolor = NULL;
+
+ if(handles->color_unseen
+ && (curcolor = pico_get_cur_color())
+ && (colorcmp(curcolor->fg, ps_global->VAR_NORM_FORE_COLOR)
+ || colorcmp(curcolor->bg, ps_global->VAR_NORM_BACK_COLOR))
+ && (revcolor = apply_rev_color(curcolor,
+ ps_global->index_color_style)))
+ (void) pico_set_colorp(revcolor, PSC_NONE);
+ else{
+
+ if(pico_usingcolor() &&
+ ps_global->VAR_SLCTBL_FORE_COLOR &&
+ ps_global->VAR_SLCTBL_BACK_COLOR){
+ pico_set_normal_color();
+ }
+ else
+ EndBold();
+
+ StartInverse();
+ is_inv = 1;
+ }
+
+ if(curcolor)
+ free_color_pair(&curcolor);
+
+ if(revcolor)
+ free_color_pair(&revcolor);
+ }
+ }
+ else{
+ /* BUG: complain? */
+ line += *line + 1;
+ }
+
+ break;
+
+ case TAG_FGCOLOR :
+ if(length < RGBLEN){
+ dprint((9,
+ "FGCOLOR not proper length, ignoring\n"));
+ length = 0;
+ break;
+ }
+
+ (void)pico_set_fg_color(line);
+ is_fg = 1;
+ length -= RGBLEN;
+ line += RGBLEN;
+ break;
+
+ case TAG_BGCOLOR :
+ if(length < RGBLEN){
+ dprint((9,
+ "BGCOLOR not proper length, ignoring\n"));
+ length = 0;
+ break;
+ }
+
+ (void)pico_set_bg_color(line);
+ is_bg = 1;
+ length -= RGBLEN;
+ line += RGBLEN;
+ break;
+
+ case TAG_EMBED: /* literal "embed" char */
+ Writechar(TAG_EMBED, 0);
+ break;
+
+ case TAG_STRIKEON : /* unsupported text markup */
+ case TAG_STRIKEOFF :
+ case TAG_BIGON :
+ case TAG_BIGOFF :
+ case TAG_SMALLON :
+ case TAG_SMALLOFF :
+ default : /* Eat unrecognized tag - TAG_BIGON, etc */
+ break;
+ } /* tag with handle, skip it */
+ }
+ else
+ Writechar(c, 0);
+ }
+
+
+#if _WINDOWS_X
+ if(hkey) {
+ char *tmp_file = NULL, ext[32], mtype[128];
+ HANDLE_S *h;
+ extern HANDLE_S *get_handle (HANDLE_S *, int);
+
+ if((h = get_handle(handles, hkey)) && h->type == Attach){
+ ext[0] = '\0';
+ strncpy(mtype, body_type_names(h->h.attach->body->type), sizeof(mtype));
+ mtype[sizeof(mtype)-1] = '\0';
+ if (h->h.attach->body->subtype) {
+ strncat (mtype, "/", sizeof(mtype)-strlen(mtype)-1);
+ mtype[sizeof(mtype)-1] = '\0';
+ strncat (mtype, h->h.attach->body->subtype, sizeof(mtype)-strlen(mtype)-1);
+ mtype[sizeof(mtype)-1] = '\0';
+ }
+
+ if(!set_mime_extension_by_type(ext, mtype)){
+ char *p, *extp = NULL;
+
+ if((p = get_filename_parameter(NULL, 0, h->h.attach->body, &extp)) != NULL){
+ if(extp){
+ strncpy(ext, extp, sizeof(ext));
+ ext[sizeof(ext)-1] = '\0';
+ }
+
+ fs_give((void **) &p);
+ }
+ }
+
+ if(ext[0] && (tmp_file = temp_nam_ext(NULL, "im", ext))){
+ FILE *f = our_fopen(tmp_file, "w");
+
+ mswin_registericon(x, h->key, tmp_file);
+
+ fclose(f);
+ our_unlink(tmp_file);
+ fs_give((void **)&tmp_file);
+ }
+ }
+ }
+#endif
+ if(is_inv){
+ dprint((9,
+ "INVERSE left on at end of line, turning off now\n"));
+ EndInverse();
+ }
+ if(is_bold){
+ dprint((9,
+ "BOLD left on at end of line, turning off now\n"));
+ EndBold();
+ }
+ if(is_uline){
+ dprint((9,
+ "UNDERLINE left on at end of line, turning off now\n"));
+ EndUnderline();
+ }
+ if(is_fg || is_bg)
+ pico_set_normal_color();
+
+}
+
+
+/*----------------------------------------------------------------------
+ Printf style output line to the screen at given position, 1 arg
+
+ Input: position on the screen
+ line of text to output
+
+ Result: text is output
+ cursor position is update
+ ----------------------------------------------------------------------*/
+void
+/*VARARGS2*/
+PutLine1(int x, int y, char *line, void *arg1)
+{
+ char buffer[PUTLINE_BUFLEN];
+
+ snprintf(buffer, sizeof(buffer), line, arg1);
+ buffer[sizeof(buffer)-1] = '\0';
+ PutLine0(x, y, buffer);
+}
+
+
+/*----------------------------------------------------------------------
+ Printf style output line to the screen at given position, 2 args
+
+ Input: position on the screen
+ line of text to output
+
+ Result: text is output
+ cursor position is update
+ ----------------------------------------------------------------------*/
+void
+/*VARARGS3*/
+PutLine2(int x, int y, char *line, void *arg1, void *arg2)
+{
+ char buffer[PUTLINE_BUFLEN];
+
+ snprintf(buffer, sizeof(buffer), line, arg1, arg2);
+ buffer[sizeof(buffer)-1] = '\0';
+ PutLine0(x, y, buffer);
+}
+
+
+/*----------------------------------------------------------------------
+ Printf style output line to the screen at given position, 3 args
+
+ Input: position on the screen
+ line of text to output
+
+ Result: text is output
+ cursor position is update
+ ----------------------------------------------------------------------*/
+void
+/*VARARGS4*/
+PutLine3(int x, int y, char *line, void *arg1, void *arg2, void *arg3)
+{
+ char buffer[PUTLINE_BUFLEN];
+
+ snprintf(buffer, sizeof(buffer), line, arg1, arg2, arg3);
+ buffer[sizeof(buffer)-1] = '\0';
+ PutLine0(x, y, buffer);
+}
+
+
+/*----------------------------------------------------------------------
+ Printf style output line to the screen at given position, 4 args
+
+ Args: x -- column position on the screen
+ y -- column position on the screen
+ line -- printf style line of text to output
+
+ Result: text is output
+ cursor position is update
+ ----------------------------------------------------------------------*/
+void
+/*VARARGS5*/
+PutLine4(int x, int y, char *line, void *arg1, void *arg2, void *arg3, void *arg4)
+{
+ char buffer[PUTLINE_BUFLEN];
+
+ snprintf(buffer, sizeof(buffer), line, arg1, arg2, arg3, arg4);
+ buffer[sizeof(buffer)-1] = '\0';
+ PutLine0(x, y, buffer);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Printf style output line to the screen at given position, 5 args
+
+ Args: x -- column position on the screen
+ y -- column position on the screen
+ line -- printf style line of text to output
+
+ Result: text is output
+ cursor position is update
+ ----------------------------------------------------------------------*/
+void
+/*VARARGS6*/
+PutLine5(int x, int y, char *line, void *arg1, void *arg2, void *arg3, void *arg4, void *arg5)
+{
+ char buffer[PUTLINE_BUFLEN];
+
+ snprintf(buffer, sizeof(buffer), line, arg1, arg2, arg3, arg4, arg5);
+ buffer[sizeof(buffer)-1] = '\0';
+ PutLine0(x, y, buffer);
+}
+
+
+/*----------------------------------------------------------------------
+ Output a UTF-8 line to the screen, centered
+
+ Input: Line number to print on, string to output
+
+ Result: String is output to screen
+ Returns column number line is output on
+ ----------------------------------------------------------------------*/
+int
+Centerline(int line, char *string)
+{
+ int width, col;
+
+ width = (int) utf8_width(string);
+
+ if (width > ps_global->ttyo->screen_cols)
+ col = 0;
+ else
+ col = (ps_global->ttyo->screen_cols - width) / 2;
+
+ PutLine0(line, col, string);
+
+ return(col);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Clear specified line on the screen
+
+ Result: The line is blanked and the cursor is left at column 0.
+
+ ----*/
+void
+ClearLine(int n)
+{
+ if(ps_global->in_init_seq)
+ return;
+
+ MoveCursor(n, 0);
+ CleartoEOLN();
+}
+
+
+
+/*----------------------------------------------------------------------
+ Clear specified lines on the screen
+
+ Result: The lines starting at 'x' and ending at 'y' are blanked
+ and the cursor is left at row 'x', column 0
+
+ ----*/
+void
+ClearLines(int x, int y)
+{
+ int i;
+
+ for(i = x; i <= y; i++)
+ ClearLine(i);
+
+ MoveCursor(x, 0);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Indicate to the screen painting here that the position of the cursor
+ has been disturbed and isn't where these functions might think.
+ ----*/
+void
+clear_cursor_pos(void)
+{
+ _line = FARAWAY;
+ _col = FARAWAY;
+}
+
+
+/*----------------------------------------------------------------------
+ Write a character to the screen, keeping track of cursor position
+
+ Args: ch -- character to output. The stream of characters coming to
+ this function is expected to be UTF-8. State is kept between
+ calls in order to collect up the octets needed for a single
+ Unicode character.
+
+ Result: character output
+ cursor position variables updated
+ ----*/
+void
+Writechar(unsigned int ch, int new_esc_len)
+{
+ static unsigned char cbuf[6];
+ static unsigned char *cbufp = cbuf;
+
+ if(cbufp < cbuf+sizeof(cbuf)){
+ unsigned char *inputp;
+ unsigned long remaining_octets;
+ UCS ucs;
+
+ *cbufp++ = (unsigned char) ch;
+ inputp = cbuf;
+ remaining_octets = (cbufp - cbuf) * sizeof(unsigned char);
+ ucs = (UCS) utf8_get(&inputp, &remaining_octets);
+
+ switch(ucs){
+ case U8G_ENDSTRG: /* incomplete character, wait */
+ case U8G_ENDSTRI: /* incomplete character, wait */
+ break;
+
+ default:
+ if(ucs & U8G_ERROR || ucs == UBOGON){
+ /*
+ * None of these cases is supposed to happen. If it
+ * does happen then the input stream isn't UTF-8
+ * so something is wrong. Treat each character in the
+ * input buffer as a separate error character and
+ * print a '?' for each.
+ */
+ for(inputp = cbuf; inputp < cbufp; inputp++){
+ int width = 0;
+
+ if(_col + width <= ps_global->ttyo->screen_cols){
+ Writewchar('?');
+ width++;
+ }
+ }
+
+ cbufp = cbuf; /* start over */
+ }
+ else{
+
+ /* got a good character */
+ Writewchar(ucs);
+
+ /* update the input buffer */
+ if(inputp >= cbufp) /* this should be the case */
+ cbufp = cbuf;
+ else{ /* extra chars for some reason? */
+ unsigned char *q, *newcbufp;
+
+ newcbufp = (cbufp - inputp) + cbuf;
+ q = cbuf;
+ while(inputp < cbufp)
+ *q++ = *inputp++;
+
+ cbufp = newcbufp;
+ }
+ }
+
+ break;
+ }
+ }
+ else{ /* error */
+ Writewchar('?');
+ cbufp = cbuf; /* start over */
+ }
+}
diff --git a/alpine/osdep/termout.gen.h b/alpine/osdep/termout.gen.h
new file mode 100644
index 00000000..2aff4b37
--- /dev/null
+++ b/alpine/osdep/termout.gen.h
@@ -0,0 +1,54 @@
+/*
+ * $Id: termout.gen.h 769 2007-10-24 00:15:40Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_OSDEP_TERMOUT_GEN_INCLUDED
+#define PINE_OSDEP_TERMOUT_GEN_INCLUDED
+
+
+extern int _line;
+extern int _col;
+
+
+/* exported prototypes */
+void PutLine0(int, int, register char *);
+void PutLine0n8b(int, int, register char *, int, HANDLE_S *);
+void PutLine1(int, int, char *, void *);
+void PutLine2(int, int, char *, void *, void *);
+void PutLine3(int, int, char *, void *, void *, void *);
+void PutLine4(int, int, char *, void *, void *, void *, void *);
+void PutLine5(int, int, char *, void *, void *, void *, void *, void *);
+int Centerline(int, char *);
+void ClearLine(int);
+void ClearLines(int, int);
+void CleartoEOLN(void);
+void ClearScreen(void);
+void clear_cursor_pos(void);
+void Writechar(unsigned int, int);
+void Write_to_screen(char *);
+void Write_to_screen_n(char *, int);
+void init_screen(void);
+void end_screen(char *, int);
+int config_screen(struct ttyo **);
+void MoveCursor(int, int);
+void Writewchar(UCS);
+void icon_text(char *, int);
+int get_windsize(struct ttyo *);
+int BeginScroll(int, int);
+void EndScroll(void);
+int ScrollRegion(int);
+
+
+#endif /* PINE_OSDEP_TERMOUT_GEN_INCLUDED */
diff --git a/alpine/osdep/termout.unx.c b/alpine/osdep/termout.unx.c
new file mode 100644
index 00000000..faf8c7f9
--- /dev/null
+++ b/alpine/osdep/termout.unx.c
@@ -0,0 +1,1002 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: termout.unx.c 955 2008-03-06 23:52:36Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+#include <system.h>
+#include <general.h>
+
+#include "../../c-client/mail.h" /* for MAILSTREAM and friends */
+#include "../../c-client/osdep.h"
+#include "../../c-client/rfc822.h" /* for soutr_t and such */
+#include "../../c-client/misc.h" /* for cpystr proto */
+#include "../../c-client/utf8.h" /* for CHARSET and such*/
+#include "../../c-client/imap4r1.h"
+
+#include "../../pith/osdep/color.h"
+
+#include "../../pith/debug.h"
+#include "../../pith/conf.h"
+#include "../../pith/newmail.h"
+#include "../../pith/charconv/utf8.h"
+
+#include "../../pico/estruct.h"
+#include "../../pico/pico.h"
+#include "../../pico/edef.h"
+#include "../../pico/efunc.h"
+#include "../../pico/osdep/color.h"
+
+#include "../status.h"
+#include "../keymenu.h"
+#include "../titlebar.h"
+
+#include "termout.gen.h"
+#include "termout.unx.h"
+
+
+/*======================================================================
+ Routines for painting the screen
+ - figure out what the terminal type is
+ - deal with screen size changes
+ - save special output sequences
+ - the usual screen clearing, cursor addressing and scrolling
+
+
+ This library gives programs the ability to easily access the
+ termcap information and write screen oriented and raw input
+ programs. The routines can be called as needed, except that
+ to use the cursor / screen routines there must be a call to
+ InitScreen() first. The 'Raw' input routine can be used
+ independently, however. (Elm comment)
+
+ Not sure what the original source of this code was. It got to be
+ here as part of ELM. It has been changed significantly from the
+ ELM version to be more robust in the face of inconsistent terminal
+ autowrap behaviour. Also, the unused functions were removed, it was
+ made to pay attention to the window size, and some code was made nicer
+ (in my opinion anyways). It also outputs the terminal initialization
+ strings and provides for minimal scrolling and detects terminals
+ with out enough capabilities. (Pine comment, 1990)
+
+
+This code used to pay attention to the "am" auto margin and "xn"
+new line glitch fields, but they were so often incorrect because many
+terminals can be configured to do either that we've taken it out. It
+now assumes it dosn't know where the cursor is after outputing in the
+80th column.
+*/
+
+
+static int _lines, _columns;
+
+
+/*
+ * Internal prototypes
+ */
+static int outchar(int);
+static void moveabsolute(int, int);
+static void CursorUp(int);
+static void CursorDown(int);
+static void CursorLeft(int);
+static void CursorRight(int);
+
+
+extern char *_clearscreen, *_moveto, *_up, *_down, *_right, *_left,
+ *_setinverse, *_clearinverse,
+ *_cleartoeoln, *_cleartoeos,
+ *_startinsert, *_endinsert, *_insertchar, *_deletechar,
+ *_deleteline, *_insertline,
+ *_scrollregion, *_scrollup, *_scrolldown,
+ *_termcap_init, *_termcap_end;
+extern char term_name[];
+extern int _tlines, _tcolumns, _bce;
+
+static enum {NoScroll,UseScrollRegion,InsertDelete} _scrollmode;
+
+extern int tputs(char *, int, int (*)(int));
+extern char *tgoto(char *, int, int);
+
+
+
+/*----------------------------------------------------------------------
+ Initialize the screen for output, set terminal type, etc
+
+ Args: tt -- Pointer to variable to store the tty output structure.
+
+ Result: terminal size is discovered and set in pine state
+ termcap entry is fetched and stored
+ make sure terminal has adequate capabilites
+ evaluate scrolling situation
+ returns status of indicating the state of the screen/termcap entry
+
+ Returns:
+ -1 indicating no terminal name associated with this shell,
+ -2..-n No termcap for this terminal type known
+ -3 Can't open termcap file
+ -4 Terminal not powerful enough - missing clear to eoln or screen
+ or cursor motion
+ ----*/
+int
+config_screen(struct ttyo **tt)
+{
+ struct ttyo *ttyo;
+ int err;
+
+ ttyo = (struct ttyo *)fs_get(sizeof (struct ttyo));
+
+ _line = 0; /* where are we right now?? */
+ _col = 0; /* assume zero, zero... */
+
+ /*
+ * This is an ugly hack to let vtterminalinfo know it's being called
+ * from pine.
+ */
+ Pmaster = (PICO *)1;
+ if((err = vtterminalinfo(F_ON(F_TCAP_WINS, ps_global))) != 0)
+ return(err);
+
+ Pmaster = NULL;
+
+ if(_tlines <= 0)
+ _lines = DEFAULT_LINES_ON_TERMINAL;
+ else
+ _lines = _tlines;
+
+ if(_tcolumns <= 0)
+ _columns = DEFAULT_COLUMNS_ON_TERMINAL;
+ else
+ _columns = _tcolumns;
+
+ get_windsize(ttyo);
+
+ ttyo->header_rows = 2;
+ ttyo->footer_rows = 3;
+
+ /*---- Make sure this terminal has the capability.
+ All we need is cursor address, clear line, and
+ reverse video.
+ ---*/
+ if(_moveto == NULL || _cleartoeoln == NULL ||
+ _setinverse == NULL || _clearinverse == NULL) {
+ return(-4);
+ }
+
+ dprint((1, "Terminal type: %s\n", term_name ? term_name : "?"));
+
+ /*------ Figure out scrolling mode -----*/
+ if(_scrollregion != NULL && _scrollregion[0] != '\0' &&
+ _scrollup != NULL && _scrollup[0] != '\0'){
+ _scrollmode = UseScrollRegion;
+ } else if(_insertline != NULL && _insertline[0] != '\0' &&
+ _deleteline != NULL && _deleteline[0] != '\0') {
+ _scrollmode = InsertDelete;
+ } else {
+ _scrollmode = NoScroll;
+ }
+ dprint((7, "Scroll mode: %s\n",
+ _scrollmode==NoScroll ? "No Scroll" :
+ _scrollmode==InsertDelete ? "InsertDelete" : "Scroll Regions"));
+
+ if (!_left) {
+ _left = "\b";
+ }
+
+ *tt = ttyo;
+
+ return(0);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Initialize the screen with the termcap string
+ ----*/
+void
+init_screen(void)
+{
+ if(_termcap_init) /* init using termcap's rule */
+ tputs(_termcap_init, 1, outchar);
+
+ /* and make sure there are no scrolling surprises! */
+ BeginScroll(0, ps_global->ttyo->screen_rows - 1);
+
+ pico_toggle_color(0);
+ switch(ps_global->color_style){
+ case COL_NONE:
+ case COL_TERMDEF:
+ pico_set_color_options(pico_trans_color() ? COLOR_TRANS_OPT : 0);
+ break;
+ case COL_ANSI8:
+ pico_set_color_options(COLOR_ANSI8_OPT|COLOR_TRANS_OPT);
+ break;
+ case COL_ANSI16:
+ pico_set_color_options(COLOR_ANSI16_OPT|COLOR_TRANS_OPT);
+ break;
+ case COL_ANSI256:
+ pico_set_color_options(COLOR_ANSI256_OPT|COLOR_TRANS_OPT);
+ break;
+ }
+
+ if(ps_global->color_style != COL_NONE)
+ pico_toggle_color(1);
+
+ /* set colors */
+ if(pico_usingcolor()){
+ if(ps_global->VAR_NORM_FORE_COLOR)
+ pico_nfcolor(ps_global->VAR_NORM_FORE_COLOR);
+
+ if(ps_global->VAR_NORM_BACK_COLOR)
+ pico_nbcolor(ps_global->VAR_NORM_BACK_COLOR);
+
+ if(ps_global->VAR_REV_FORE_COLOR)
+ pico_rfcolor(ps_global->VAR_REV_FORE_COLOR);
+
+ if(ps_global->VAR_REV_BACK_COLOR)
+ pico_rbcolor(ps_global->VAR_REV_BACK_COLOR);
+
+ pico_set_normal_color();
+ }
+
+ /* and make sure icon text starts out consistent */
+ icon_text(NULL, IT_NEWMAIL);
+ fflush(stdout);
+}
+
+
+
+
+/*----------------------------------------------------------------------
+ Get the current window size
+
+ Args: ttyo -- pointer to structure to store window size in
+
+ NOTE: we don't override the given values unless we know better
+ ----*/
+int
+get_windsize(struct ttyo *ttyo)
+{
+#if defined(SIGWINCH) && defined(TIOCGWINSZ)
+ struct winsize win;
+
+ /*
+ * Get the window size from the tty driver. If we can't fish it from
+ * stdout (pine's output is directed someplace else), try stdin (which
+ * *must* be associated with the terminal; see init_tty_driver)...
+ */
+ if(ioctl(1, TIOCGWINSZ, &win) >= 0 /* 1 is stdout */
+ || ioctl(0, TIOCGWINSZ, &win) >= 0){ /* 0 is stdin */
+ if(win.ws_row)
+ _lines = MIN(win.ws_row, MAX_SCREEN_ROWS);
+
+ if(win.ws_col)
+ _columns = MIN(win.ws_col, MAX_SCREEN_COLS);
+
+ dprint((2, "new win size -----<%d %d>------\n",
+ _lines, _columns));
+ }
+ else{
+ /* Depending on the OS, the ioctl() may have failed because
+ of a 0 rows, 0 columns setting. That happens on DYNIX/ptx 1.3
+ (with a kernel patch that happens to involve the negotiation
+ of window size in the telnet streams module.) In this case
+ the error is EINVARG. Leave the default settings. */
+ dprint((1, "ioctl(TIOCWINSZ) failed :%s\n",
+ error_description(errno)));
+ }
+#endif
+
+ ttyo->screen_cols = MIN(_columns, MAX_SCREEN_COLS);
+ ttyo->screen_rows = MIN(_lines, MAX_SCREEN_ROWS);
+ return(0);
+}
+
+
+/*----------------------------------------------------------------------
+ End use of the screen.
+ Print status message, if any.
+ Flush status messages.
+ ----*/
+void
+end_screen(char *message, int exit_val)
+{
+ int footer_rows_was_one = 0;
+
+ if(!panicking()){
+
+ dprint((9, "end_screen called\n"));
+
+ if(FOOTER_ROWS(ps_global) == 1){
+ footer_rows_was_one++;
+ FOOTER_ROWS(ps_global) = 3;
+ mark_status_unknown();
+ }
+
+ flush_status_messages(exit_val ? 0 : 1);
+ blank_keymenu(_lines - 2, 0);
+ MoveCursor(_lines - 2, 0);
+ }
+
+ /* unset colors */
+ if(pico_usingcolor())
+ pico_endcolor();
+
+ if(_termcap_end != NULL)
+ tputs(_termcap_end, 1, outchar);
+
+ if(!panicking()){
+
+ if(message){
+ printf("%s\r\n", message);
+ }
+
+ if(F_ON(F_ENABLE_XTERM_NEWMAIL, ps_global) && getenv("DISPLAY"))
+ icon_text("xterm", IT_NEWMAIL);
+
+ fflush(stdout);
+
+ if(footer_rows_was_one){
+ FOOTER_ROWS(ps_global) = 1;
+ mark_status_unknown();
+ }
+ }
+}
+
+
+
+/*----------------------------------------------------------------------
+ Clear the terminal screen
+
+ Result: The screen is cleared
+ internal cursor position set to 0,0
+ ----*/
+void
+ClearScreen(void)
+{
+ _line = 0; /* clear leaves us at top... */
+ _col = 0;
+
+ if(ps_global->in_init_seq)
+ return;
+
+ mark_status_unknown();
+ mark_keymenu_dirty();
+ mark_titlebar_dirty();
+
+ /*
+ * If the terminal doesn't have back color erase, then we have to
+ * erase manually to preserve the background color.
+ */
+ if(pico_usingcolor() && (!_bce || !_clearscreen)){
+ ClearLines(0, _lines-1);
+ MoveCursor(0, 0);
+ }
+ else if(_clearscreen){
+ tputs(_clearscreen, 1, outchar);
+ moveabsolute(0, 0); /* some clearscreens don't move correctly */
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Internal move cursor to absolute position
+
+ Args: col -- column to move cursor to
+ row -- row to move cursor to
+
+ Result: cursor is moved (variables, not updates)
+ ----*/
+
+static void
+moveabsolute(int col, int row)
+{
+
+ char *stuff, *tgoto();
+
+ stuff = tgoto(_moveto, col, row);
+ tputs(stuff, 1, outchar);
+}
+
+
+/*----------------------------------------------------------------------
+ Move the cursor to the row and column number
+ Args: row number
+ column number
+
+ Result: Cursor moves
+ internal position updated
+ ----*/
+void
+MoveCursor(int row, int col)
+{
+ /** move cursor to the specified row column on the screen.
+ 0,0 is the top left! **/
+
+ int scrollafter = 0;
+
+ /* we don't want to change "rows" or we'll mangle scrolling... */
+
+ if(ps_global->in_init_seq)
+ return;
+
+ if (col < 0)
+ col = 0;
+ if (col >= ps_global->ttyo->screen_cols)
+ col = ps_global->ttyo->screen_cols - 1;
+ if (row < 0)
+ row = 0;
+ if (row > ps_global->ttyo->screen_rows) {
+ if (col == 0)
+ scrollafter = row - ps_global->ttyo->screen_rows;
+ row = ps_global->ttyo->screen_rows;
+ }
+
+ if (!_moveto)
+ return;
+
+ if (_col >= ps_global->ttyo->screen_cols)
+ _col = ps_global->ttyo->screen_cols - 1;
+
+ if (row == _line) {
+ if (col == _col)
+ return; /* already there! */
+
+ else if (abs(col - _col) < 5) { /* within 5 spaces... */
+ if (col > _col && _right)
+ CursorRight(col - _col);
+ else if (col < _col && _left)
+ CursorLeft(_col - col);
+ else
+ moveabsolute(col, row);
+ }
+ else /* move along to the new x,y loc */
+ moveabsolute(col, row);
+ }
+ else if (col == _col && abs(row - _line) < 5) {
+ if (row < _line && _up)
+ CursorUp(_line - row);
+ else if (_line > row && _down)
+ CursorDown(row - _line);
+ else
+ moveabsolute(col, row);
+ }
+ else if (_line == row-1 && col == 0) {
+ putchar('\n'); /* that's */
+ putchar('\r'); /* easy! */
+ }
+ else
+ moveabsolute(col, row);
+
+ _line = row; /* to ensure we're really there... */
+ _col = col;
+
+ if (scrollafter) {
+ while (scrollafter--) {
+ putchar('\n');
+ putchar('\r');
+
+ }
+ }
+
+ return;
+}
+
+
+
+/*----------------------------------------------------------------------
+ Newline, move the cursor to the start of next line
+
+ Result: Cursor moves
+ ----*/
+void
+NewLine(void)
+{
+ /** move the cursor to the beginning of the next line **/
+
+ Writechar('\n', 0);
+ Writechar('\r', 0);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Move cursor up n lines with terminal escape sequence
+
+ Args: n -- number of lines to go up
+
+ Result: cursor moves,
+ internal position updated
+
+ Only for ttyout use; not outside callers
+ ----*/
+static void
+CursorUp(int n)
+{
+ /** move the cursor up 'n' lines **/
+ /** Calling function must check that _up is not null before calling **/
+
+ _line = (_line-n > 0? _line - n: 0); /* up 'n' lines... */
+
+ while (n-- > 0)
+ tputs(_up, 1, outchar);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Move cursor down n lines with terminal escape sequence
+
+ Arg: n -- number of lines to go down
+
+ Result: cursor moves,
+ internal position updated
+
+ Only for ttyout use; not outside callers
+ ----*/
+static void
+CursorDown(int n)
+{
+ /** move the cursor down 'n' lines **/
+ /** Caller must check that _down is not null before calling **/
+
+ _line = (_line+n < ps_global->ttyo->screen_rows ? _line + n
+ : ps_global->ttyo->screen_rows);
+ /* down 'n' lines... */
+
+ while (n-- > 0)
+ tputs(_down, 1, outchar);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Move cursor left n lines with terminal escape sequence
+
+ Args: n -- number of lines to go left
+
+ Result: cursor moves,
+ internal position updated
+
+ Only for ttyout use; not outside callers
+ ----*/
+static void
+CursorLeft(int n)
+{
+ /** move the cursor 'n' characters to the left **/
+ /** Caller must check that _left is not null before calling **/
+
+ _col = (_col - n> 0? _col - n: 0); /* left 'n' chars... */
+
+ while (n-- > 0)
+ tputs(_left, 1, outchar);
+}
+
+
+/*----------------------------------------------------------------------
+ Move cursor right n lines with terminal escape sequence
+
+ Args: number of lines to go right
+
+ Result: cursor moves,
+ internal position updated
+
+ Only for ttyout use; not outside callers
+ ----*/
+static void
+CursorRight(int n)
+{
+ /** move the cursor 'n' characters to the right (nondestructive) **/
+ /** Caller must check that _right is not null before calling **/
+
+ _col = (_col+n < ps_global->ttyo->screen_cols? _col + n :
+ ps_global->ttyo->screen_cols); /* right 'n' chars... */
+
+ while (n-- > 0)
+ tputs(_right, 1, outchar);
+
+}
+
+
+/*----------------------------------------------------------------------
+ Go into scrolling mode, that is set scrolling region if applicable
+
+ Args: top -- top line of region to scroll
+ bottom -- bottom line of region to scroll
+ (These are zero-origin numbers)
+
+ Result: either set scrolling region or
+ save values for later scrolling
+ returns -1 if we can't scroll
+
+ Unfortunately this seems to leave the cursor in an unpredictable place
+ at least the manuals don't say where, so we force it here.
+-----*/
+static int __t, __b;
+
+int
+BeginScroll(int top, int bottom)
+{
+ char *stuff;
+
+ if(_scrollmode == NoScroll)
+ return(-1);
+
+ __t = top;
+ __b = bottom;
+ if(_scrollmode == UseScrollRegion){
+ stuff = tgoto(_scrollregion, bottom, top);
+ tputs(stuff, 1, outchar);
+ /*-- a location very far away to force a cursor address --*/
+ _line = FARAWAY;
+ _col = FARAWAY;
+ }
+ return(0);
+}
+
+
+
+/*----------------------------------------------------------------------
+ End scrolling -- clear scrolling regions if necessary
+
+ Result: Clear scrolling region on terminal
+ -----*/
+void
+EndScroll(void)
+{
+ if(_scrollmode == UseScrollRegion && _scrollregion != NULL){
+ /* Use tgoto even though we're not cursor addressing because
+ the format of the capability is the same.
+ */
+ char *stuff = tgoto(_scrollregion, ps_global->ttyo->screen_rows -1, 0);
+ tputs(stuff, 1, outchar);
+ /*-- a location very far away to force a cursor address --*/
+ _line = FARAWAY;
+ _col = FARAWAY;
+ }
+}
+
+
+/* ----------------------------------------------------------------------
+ Scroll the screen using insert/delete or scrolling regions
+
+ Args: lines -- number of lines to scroll, positive forward
+
+ Result: Screen scrolls
+ returns 0 if scroll succesful, -1 if not
+
+ positive lines goes foward (new lines come in at bottom
+ Leaves cursor at the place to insert put new text
+
+ 0,0 is the upper left
+ -----*/
+int
+ScrollRegion(int lines)
+{
+ int l;
+
+ if(lines == 0)
+ return(0);
+
+ if(_scrollmode == UseScrollRegion) {
+ if(lines > 0) {
+ MoveCursor(__b, 0);
+ for(l = lines ; l > 0 ; l--)
+ tputs((_scrolldown == NULL || _scrolldown[0] =='\0') ? "\n" :
+ _scrolldown, 1, outchar);
+ } else {
+ MoveCursor(__t, 0);
+ for(l = -lines; l > 0; l--)
+ tputs(_scrollup, 1, outchar);
+ }
+ } else if(_scrollmode == InsertDelete) {
+ if(lines > 0) {
+ MoveCursor(__t, 0);
+ for(l = lines; l > 0; l--)
+ tputs(_deleteline, 1, outchar);
+ MoveCursor(__b, 0);
+ for(l = lines; l > 0; l--)
+ tputs(_insertline, 1, outchar);
+ } else {
+ for(l = -lines; l > 0; l--) {
+ MoveCursor(__b, 0);
+ tputs(_deleteline, 1, outchar);
+ MoveCursor(__t, 0);
+ tputs(_insertline, 1, outchar);
+ }
+ }
+ } else {
+ return(-1);
+ }
+ fflush(stdout);
+ return(0);
+}
+
+
+void
+Writewchar(UCS ucs)
+{
+ if(ps_global->in_init_seq /* silent */
+ || (F_ON(F_BLANK_KEYMENU, ps_global) /* or bottom, */
+ /* right cell */
+ && _line + 1 == ps_global->ttyo->screen_rows
+ && _col + 1 == ps_global->ttyo->screen_cols))
+ return;
+
+
+ if(ucs == LINE_FEED || ucs == RETURN || ucs == BACKSPACE || ucs == BELL || ucs == TAB){
+ switch(ucs){
+ case LINE_FEED:
+ /*-- Don't have to watch out for auto wrap or newline glitch
+ because we never let it happen. See below
+ ---*/
+ putchar('\n');
+ _line = MIN(_line+1,ps_global->ttyo->screen_rows);
+ break;
+
+ case RETURN : /* move to column 0 */
+ putchar('\r');
+ _col = 0;
+ break;
+
+ case BACKSPACE : /* move back a space if not in column 0 */
+ if(_col != 0) {
+ putchar('\b');
+ _col--;
+ } /* else BACKSPACE does nothing */
+
+ break;
+
+ case BELL : /* ring the bell but don't advance _col */
+ putchar((int) ucs);
+ break;
+
+ case TAB : /* if a tab, output it */
+ do /* BUG? ignores tty driver's spacing */
+ putchar(' ');
+ /* fix from Eduardo Chappa, have to increment _col once more */
+ while(_col < ps_global->ttyo->screen_cols
+ && ((++_col)&0x07) != 0);
+ break;
+ }
+ }
+ else{
+ unsigned char obuf[MAX(MB_LEN_MAX,32)];
+ int i, width = 0, outchars = 0, printable_ascii = 0;
+
+ if(ucs < 0x80 && isprint((unsigned char) ucs)){
+ printable_ascii++; /* efficiency shortcut */
+ width = 1;
+ }
+ else
+ width = wcellwidth(ucs);
+
+ if(width < 0){
+ /*
+ * This happens when we have a Unicode character that
+ * we aren't able to print in our locale. For example,
+ * if the locale is setup with the terminal
+ * expecting ISO-8859-1 characters then there are
+ * lots of Unicode characters that can't be printed.
+ * Print a '?' instead.
+ */
+ width = 1;
+ obuf[outchars++] = '?';
+ }
+ else if(_col + width > ps_global->ttyo->screen_cols){
+ /*
+ * Hopefully this won't happen. The
+ * character overflows the right edge because it
+ * is a double wide and we're in last column.
+ * Fill with spaces instead and toss the
+ * character.
+ *
+ * Put a > in the right column to show we tried to write
+ * past the end.
+ */
+ while(outchars < ps_global->ttyo->screen_cols - _col - 1)
+ obuf[outchars++] = ' ';
+
+ obuf[outchars++] = '>';
+
+ /*
+ * In case last time through wrote in the last column and
+ * caused an autowrap, reposition cursor.
+ */
+ if(_col >= ps_global->ttyo->screen_cols)
+ moveabsolute(ps_global->ttyo->screen_cols-1, _line);
+ }
+ else{
+ /*
+ * Convert the ucs into the multibyte
+ * character that corresponds to the
+ * ucs in the users locale.
+ */
+ if(printable_ascii)
+ obuf[outchars++] = (unsigned char) ucs;
+ else{
+ outchars = wtomb((char *) obuf, ucs);
+ if(outchars < 0){
+ width = 1;
+ obuf[0] = '?';
+ outchars = 1;
+ }
+ }
+ }
+
+ _col += width;
+ for(i = 0; i < outchars; i++)
+ putchar(obuf[i]);
+ }
+
+ /*
+ * We used to wrap by moving to the next line and making _col = 0
+ * when we went past the end. We don't believe that this is useful
+ * anymore. If we wrap it is unintentional. Instead, stay at the
+ * end of the line. We need to be a little careful because we're
+ * moving to a different place than _col is set to, but since _col
+ * is off the right edge, it should be ok.
+ */
+ if(_col >= ps_global->ttyo->screen_cols) {
+ _col = ps_global->ttyo->screen_cols;
+ moveabsolute(ps_global->ttyo->screen_cols-1, _line);
+ }
+}
+
+
+void
+Write_to_screen(char *string) /* UNIX */
+
+{
+ while(*string)
+ Writechar((unsigned char) *string++, 0);
+}
+
+
+/*----------------------------------------------------------------------
+ Write no more than n chars of string to screen at current cursor position
+
+ Args: string -- string to be output
+ n -- number of chars to output
+
+ Result: String written to the screen
+ ----*/
+void
+Write_to_screen_n(char *string, int n) /* UNIX */
+
+
+{
+ while(n-- && *string)
+ Writechar((unsigned char) *string++, 0);
+}
+
+
+/*----------------------------------------------------------------------
+ Clear screen to end of line on current line
+
+ Result: Line is cleared
+ ----*/
+void
+CleartoEOLN(void)
+{
+ int c, starting_col, starting_line;
+ char *last_bg_color;
+
+ /*
+ * If the terminal doesn't have back color erase, then we have to
+ * erase manually to preserve the background color.
+ */
+ if(pico_usingcolor() && (!_bce || !_cleartoeoln)){
+ starting_col = _col;
+ starting_line = _line;
+ last_bg_color = pico_get_last_bg_color();
+ pico_set_nbg_color();
+ for(c = _col; c < _columns; c++)
+ Writechar(' ', 0);
+
+ MoveCursor(starting_line, starting_col);
+ if(last_bg_color){
+ (void)pico_set_bg_color(last_bg_color);
+ fs_give((void **)&last_bg_color);
+ }
+ }
+ else if(_cleartoeoln)
+ tputs(_cleartoeoln, 1, outchar);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Clear screen to end of screen from current point
+
+ Result: screen is cleared
+ ----*/
+void
+CleartoEOS(void)
+{
+ int starting_col, starting_line;
+
+ /*
+ * If the terminal doesn't have back color erase, then we have to
+ * erase manually to preserve the background color.
+ */
+ if(pico_usingcolor() && (!_bce || !_cleartoeos)){
+ starting_col = _col;
+ starting_line = _line;
+ CleartoEOLN();
+ ClearLines(_line+1, _lines-1);
+ MoveCursor(starting_line, starting_col);
+ }
+ else if(_cleartoeos)
+ tputs(_cleartoeos, 1, outchar);
+}
+
+
+
+/*----------------------------------------------------------------------
+ function to output character used by termcap
+
+ Args: c -- character to output
+
+ Result: character output to screen via stdio
+ ----*/
+int
+outchar(int c)
+{
+ /** output the given character. From tputs... **/
+ /** Note: this CANNOT be a macro! **/
+
+ return(putc((unsigned char)c, stdout));
+}
+
+
+
+/*----------------------------------------------------------------------
+ function to output string such that it becomes icon text
+
+ Args: s -- string to write
+
+ Result: string indicated become our "icon" text
+ ----*/
+void
+icon_text(char *s, int type)
+{
+ static enum {ukn, yes, no} xterm;
+ char *str, *converted, *use_this;
+
+ if(xterm == ukn)
+ xterm = (getenv("DISPLAY") != NULL) ? yes : no;
+
+ if(F_ON(F_ENABLE_XTERM_NEWMAIL,ps_global) && xterm == yes){
+ fputs("\033]0;", stdout);
+
+ str = s ? s : ps_global->pine_name;
+ converted = convert_to_locale(str);
+ use_this = converted ? converted : str;
+
+ fputs(use_this, stdout);
+
+ fputs("\007", stdout);
+ fputs("\033]1;", stdout);
+
+ fputs(use_this, stdout);
+
+ fputs("\007", stdout);
+ fflush(stdout);
+
+ if(converted)
+ fs_give((void **) &converted);
+ }
+}
+
+
diff --git a/alpine/osdep/termout.unx.h b/alpine/osdep/termout.unx.h
new file mode 100644
index 00000000..4d9d1b5c
--- /dev/null
+++ b/alpine/osdep/termout.unx.h
@@ -0,0 +1,24 @@
+/*
+ * $Id: termout.unx.h 769 2007-10-24 00:15:40Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_OSDEP_TERMOUT_UNX_INCLUDED
+#define PINE_OSDEP_TERMOUT_UNX_INCLUDED
+
+
+/* exported prototypes */
+
+
+#endif /* PINE_OSDEP_TERMOUT_UNX_INCLUDED */
diff --git a/alpine/osdep/termout.win b/alpine/osdep/termout.win
new file mode 100644
index 00000000..f3f027ee
--- /dev/null
+++ b/alpine/osdep/termout.win
@@ -0,0 +1,127 @@
+#line 2 "osdep/termout.win"
+
+
+
+
+
+/*----------------------------------------------------------------------
+ End use of the screen.
+ ----------------------------------------------------------------------*/
+
+
+
+
+/*----------------------------------------------------------------------
+ Newline, move the cursor to the start of next line
+
+ Input: none
+
+ Result: Cursor moves
+ ----------------------------------------------------------------------*/
+void
+NewLine()
+{
+ /** move the cursor to the beginning of the next line **/
+
+ MoveCursor(_line+1, 0);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Insert character on screen pushing others right
+
+ Input: character to output
+ termcap escape sequences
+
+ Result: charcter is inserted if possible
+ return -1 if it can't be done
+ ----------------------------------------------------------------------*/
+InsertChar(c)
+ int c;
+{
+ mswin_inschar (c);
+ return (0);
+}
+
+
+/*----------------------------------------------------------------------
+ Delete n characters from line, sliding rest of line left
+
+ Input: number of characters to delete
+ termcap escape sequences
+
+ Result: characters deleted on screen
+ returns -1 if it wasn't done
+ ----------------------------------------------------------------------*/
+DeleteChar(n)
+ int n;
+{
+ char c;
+ int oc; /* original column */
+
+ mswin_delchar ();
+ return(0);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Go into scrolling mode, that is set scrolling region if applicable
+
+ Input: top line of region to scroll
+ bottom line of region to scroll
+
+ Result: either set scrolling region or
+ save values for later scrolling
+ returns -1 if we can't scroll
+
+ Unfortunately this seems to leave the cursor in an unpredictable place
+ at least the manuals don't say were, so we force it here.
+----------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------
+ End scrolling -- clear scrolling regions if necessary
+
+ Input: none
+
+ Result: Clear scrolling region on terminal
+ ----------------------------------------------------------------------*/
+
+
+/* ----------------------------------------------------------------------
+ Scroll the screen using insert/delete or scrolling regions
+
+ Input: number of lines to scroll, positive forward
+
+ Result: Screen scrolls
+ returns 0 if scroll succesful, -1 if not
+
+ positive lines goes foward (new lines come in at bottom
+ Leaves cursor at the place to insert put new text
+
+ 0,0 is the upper left
+ ----------------------------------------------------------------------*/
+
+
+
+
+
+
+
+
+
+/*----------------------------------------------------------------------
+ Clear screen to end of screen from current point
+
+ Input: none
+
+ Result: screen is cleared
+ ----------------------------------------------------------------------*/
+void
+CleartoEOS() /* DOS */
+{
+ mswin_eeop();
+}
+
+
diff --git a/alpine/osdep/termout.wnt.c b/alpine/osdep/termout.wnt.c
new file mode 100644
index 00000000..fae661aa
--- /dev/null
+++ b/alpine/osdep/termout.wnt.c
@@ -0,0 +1,1862 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: termout.unx.c 159 2006-10-02 22:00:13Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+#include <system.h>
+#include <general.h>
+
+#include "../../c-client/mail.h" /* for MAILSTREAM and friends */
+#include "../../c-client/osdep.h"
+#include "../../c-client/rfc822.h" /* for soutr_t and such */
+#include "../../c-client/misc.h" /* for cpystr proto */
+#include "../../c-client/utf8.h" /* for CHARSET and such*/
+#include "../../c-client/imap4r1.h"
+
+#include "../../pith/osdep/color.h"
+
+#include "../../pith/charconv/filesys.h"
+
+#include "../../pith/debug.h"
+#include "../../pith/conf.h"
+#include "../../pith/newmail.h"
+
+#include "../../pico/estruct.h"
+#include "../../pico/pico.h"
+#include "../../pico/keydefs.h"
+
+#include "../../pico/osdep/mswin.h"
+#include "../../pico/osdep/color.h"
+
+#include "../flagmaint.h"
+#include "../status.h"
+#include "../keymenu.h"
+#include "../titlebar.h"
+
+#include "termout.gen.h"
+#include "termout.wnt.h"
+
+/*======================================================================
+ Routines for painting the screen
+ - figure out what the terminal type is
+ - deal with screen size changes
+ - save special output sequences
+ - the usual screen clearing, cursor addressing and scrolling
+
+
+ This library gives programs the ability to easily access the
+ termcap information and write screen oriented and raw input
+ programs. The routines can be called as needed, except that
+ to use the cursor / screen routines there must be a call to
+ InitScreen() first. The 'Raw' input routine can be used
+ independently, however. (Elm comment)
+
+ Not sure what the original source of this code was. It got to be
+ here as part of ELM. It has been changed significantly from the
+ ELM version to be more robust in the face of inconsistent terminal
+ autowrap behaviour. Also, the unused functions were removed, it was
+ made to pay attention to the window size, and some code was made nicer
+ (in my opinion anyways). It also outputs the terminal initialization
+ strings and provides for minimal scrolling and detects terminals
+ with out enough capabilities. (Pine comment, 1990)
+
+*/
+
+#define PUTLINE_BUFLEN 256
+
+static int _lines, _columns;
+
+BOOL CALLBACK __export
+args_dialog_proc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+BOOL CALLBACK __export
+login_dialog_proc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+BOOL CALLBACK __export
+flag_dialog_proc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+BOOL CALLBACK __export
+sort_dialog_proc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+BOOL CALLBACK __export
+config_vars_dialog_proc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+BOOL CALLBACK __export
+config_dialog_proc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+void
+init_screen(void)
+{
+ /*
+ * If we want to show it, turn it on. If we don't care, put it back
+ * to the way it was originally.
+ */
+ mswin_showcaret(F_ON(F_SHOW_CURSOR, ps_global));
+ mswin_trayicon(F_ON(F_ENABLE_TRAYICON, ps_global));
+ return; /* NO OP */
+}
+
+void
+end_screen(char *message, int exit_val)
+{
+ int footer_rows_was_one = 0;
+
+ if(panicking())
+ return;
+
+ if(FOOTER_ROWS(ps_global) == 1){
+ footer_rows_was_one++;
+ FOOTER_ROWS(ps_global) = 3;
+ mark_status_unknown();
+ }
+
+ flush_status_messages(exit_val ? 0 : 1);
+
+ blank_keymenu(_lines - 2, 0);
+
+ if(message){
+ StartInverse();
+ PutLine0(_lines - 2, 0, message);
+ }
+
+ EndInverse();
+
+ MoveCursor(_lines - 1, 0);
+
+ mswin_showcaret(F_ON(F_SHOW_CURSOR, ps_global));
+
+ if(footer_rows_was_one){
+ FOOTER_ROWS(ps_global) = 1;
+ mark_status_unknown();
+ }
+}
+
+/*----------------------------------------------------------------------
+ Initialize the screen for output, set terminal type, etc
+
+ Args: tt -- Pointer to variable to store the tty output structure.
+
+ Result: terminal size is discovered and set pine state
+ termcap entry is fetched and stored in local variables
+ make sure terminal has adequate capabilites
+ evaluate scrolling situation
+ returns status of indicating the state of the screen/termcap entry
+
+ Returns:
+ -1 indicating no terminal name associated with this shell,
+ -2..-n No termcap for this terminal type known
+ -3 Can't open termcap file
+ -4 Terminal not powerful enough - missing clear to eoln or screen
+ or cursor motion
+
+ ----*/
+int
+config_screen(struct ttyo **tt)
+{
+ struct ttyo *ttyo;
+
+ _line = 0; /* where are we right now?? */
+ _col = 0; /* assume zero, zero... */
+
+ mswin_getscreensize(&_lines, &_columns);
+ if (_lines > MAX_SCREEN_ROWS)
+ _lines = MAX_SCREEN_ROWS;
+ if (_columns > MAX_SCREEN_COLS)
+ _columns = MAX_SCREEN_COLS;
+
+ ttyo = (struct ttyo *)fs_get(sizeof(struct ttyo));
+ ttyo->screen_cols = _columns;
+ ttyo->screen_rows = _lines ;
+ ttyo->header_rows = 2;
+ ttyo->footer_rows = 3;
+
+ *tt = ttyo;
+
+ return(0);
+}
+
+/*----------------------------------------------------------------------
+ Get the current window size
+
+ Args: ttyo -- pointer to structure to store window size in
+
+ NOTE: we don't override the given values unless we know better
+ ----*/
+int
+get_windsize(struct ttyo *ttyo)
+{
+ char fontName[LF_FACESIZE+1];
+ char fontSize[12];
+ char fontStyle[64];
+ char fontCharSet[256];
+ char windowPosition[32], windowPositionReg[32];
+ char foreColor[64], backColor[64];
+ int newRows, newCols;
+ char cursorStyle[32];
+
+ cursorStyle[0] = '\0';
+
+ /* Get the new window parameters and update the 'pinerc' variables. */
+ mswin_getwindow(fontName, sizeof(fontName), fontSize, sizeof(fontSize),
+ fontStyle, sizeof(fontStyle),
+ windowPosition, sizeof(windowPosition),
+ foreColor, sizeof(foreColor),
+ backColor, sizeof(backColor),
+ cursorStyle, sizeof(cursorStyle),
+ fontCharSet, sizeof(fontCharSet));
+
+ if(!ps_global->VAR_FONT_NAME
+ || strucmp(ps_global->VAR_FONT_NAME, fontName))
+ set_variable(V_FONT_NAME, fontName, 1, 0,
+ ps_global->ew_for_except_vars);
+
+ if(!ps_global->VAR_FONT_SIZE
+ || strucmp(ps_global->VAR_FONT_SIZE, fontSize))
+ set_variable(V_FONT_SIZE, fontSize, 1, 0,
+ ps_global->ew_for_except_vars);
+
+ if(!ps_global->VAR_FONT_STYLE
+ || strucmp(ps_global->VAR_FONT_STYLE, fontStyle))
+ set_variable(V_FONT_STYLE, fontStyle, 1, 0,
+ ps_global->ew_for_except_vars);
+
+ if(!ps_global->VAR_FONT_CHAR_SET
+ || strucmp(ps_global->VAR_FONT_CHAR_SET, fontCharSet))
+ set_variable(V_FONT_CHAR_SET, fontCharSet, 1, 0,
+ ps_global->ew_for_except_vars);
+
+ if(strnicmp(windowPosition, "MIN0", 4) && !strchr(windowPosition, '!')){
+ if(F_ON(F_STORE_WINPOS_IN_CONFIG, ps_global)){
+ if(!ps_global->VAR_WINDOW_POSITION
+ || strucmp(ps_global->VAR_WINDOW_POSITION, windowPosition))
+ set_variable(V_WINDOW_POSITION, windowPosition, 1, 0,
+ ps_global->ew_for_except_vars);
+ }
+ else{
+ /*
+ * Get the window position stored in the registry.
+ *
+ * If current window position is not in the registry or is
+ * different from what is there, save it.
+ */
+ if((!mswin_reg(MSWR_OP_GET, MSWR_PINE_POS, windowPositionReg,
+ sizeof(windowPositionReg)) ||
+ strucmp(windowPositionReg, windowPosition))
+ && (ps_global->update_registry != UREG_NEVER_SET))
+ mswin_reg(MSWR_OP_SET | MSWR_OP_FORCE, MSWR_PINE_POS, windowPosition,
+ (size_t)NULL);
+ }
+ }
+
+ mswin_getprintfont(fontName, sizeof(fontName),
+ fontSize, sizeof(fontSize),
+ fontStyle, sizeof(fontStyle),
+ fontCharSet, sizeof(fontCharSet));
+ if(!ps_global->VAR_PRINT_FONT_NAME
+ || strucmp(ps_global->VAR_PRINT_FONT_NAME, fontName))
+ set_variable(V_PRINT_FONT_NAME, fontName, 1, 0,
+ ps_global->ew_for_except_vars);
+
+ if(!ps_global->VAR_PRINT_FONT_SIZE
+ || strucmp(ps_global->VAR_PRINT_FONT_SIZE, fontSize))
+ set_variable(V_PRINT_FONT_SIZE, fontSize, 1, 0,
+ ps_global->ew_for_except_vars);
+
+ if(!ps_global->VAR_PRINT_FONT_STYLE
+ || strucmp(ps_global->VAR_PRINT_FONT_STYLE, fontStyle))
+ set_variable(V_PRINT_FONT_STYLE, fontStyle, 1, 0,
+ ps_global->ew_for_except_vars);
+
+ if(!ps_global->VAR_PRINT_FONT_CHAR_SET
+ || strucmp(ps_global->VAR_PRINT_FONT_CHAR_SET, fontCharSet))
+ set_variable(V_PRINT_FONT_CHAR_SET, fontCharSet, 1, 0,
+ ps_global->ew_for_except_vars);
+
+ if(!ps_global->VAR_NORM_FORE_COLOR
+ || strucmp(ps_global->VAR_NORM_FORE_COLOR, foreColor))
+ set_variable(V_NORM_FORE_COLOR, foreColor, 1, 0,
+ ps_global->ew_for_except_vars);
+
+ if(!ps_global->VAR_NORM_BACK_COLOR
+ || strucmp(ps_global->VAR_NORM_BACK_COLOR, backColor))
+ set_variable(V_NORM_BACK_COLOR, backColor, 1, 0,
+ ps_global->ew_for_except_vars);
+
+ if(cursorStyle[0] && !ps_global->VAR_CURSOR_STYLE
+ || strucmp(ps_global->VAR_CURSOR_STYLE, cursorStyle))
+ set_variable(V_CURSOR_STYLE, cursorStyle, 1, 0,
+ ps_global->ew_for_except_vars);
+
+ /* Get new window size. Compare to old. The window may have just
+ * moved, in which case we don't bother updating the size. */
+ mswin_getscreensize(&newRows, &newCols);
+ if (newRows == ttyo->screen_rows && newCols == ttyo->screen_cols)
+ return (NO_OP_COMMAND);
+
+ /* True resize. */
+ ttyo->screen_rows = newRows;
+ ttyo->screen_cols = newCols;
+
+ if (ttyo->screen_rows > MAX_SCREEN_ROWS)
+ ttyo->screen_rows = MAX_SCREEN_ROWS;
+ if (ttyo->screen_cols > MAX_SCREEN_COLS)
+ ttyo->screen_cols = MAX_SCREEN_COLS;
+
+ return(KEY_RESIZE);
+}
+
+/*----------------------------------------------------------------------
+ Move the cursor to the row and column number
+ Input: row number
+ column number
+
+ Result: Cursor moves
+ internal position updated
+ ----------------------------------------------------------------------*/
+void
+MoveCursor(int row, int col)
+{
+ /** move cursor to the specified row column on the screen.
+ 0,0 is the top left! **/
+
+ if(ps_global->in_init_seq)
+ return;
+
+ /*
+ * This little hack is here for screen readers, for example, a screen
+ * reader associated with a braille display. If we flash the cursor off
+ * and on in the same position, the reader will be drawn to it. Since we
+ * are calling MoveCursor every time through the message viewing loops we
+ * need to suppress the calls if they aren't really doing anything.
+ * However, mswin_move actually does more than just move the cursor.
+ * It also call FlushAccum(), which is necessary in some cases. For
+ * example, in optionally_enter the user is typing characters in and
+ * the FlushAccum() allows them to show up on the screen. The putblock
+ * call is a quick hack to get the Flush_Accum() call, which is all
+ * the putblock does when the arg is NULL.
+ */
+ if(row == _line && col == _col){
+ mswin_putblock(NULL, 0);
+ return;
+ }
+
+ mswin_move(row, col);
+ _line = row;
+ _col = col;
+}
+
+
+/*----------------------------------------------------------------------
+ Write a character to the screen, keeping track of cursor position
+
+ Input: charater to write
+
+ Result: character output
+ cursor position variables updated
+ ----------------------------------------------------------------------*/
+void
+Writewchar(UCS ucs)
+{
+ int width;
+
+ if(ps_global->in_init_seq)
+ return;
+
+ switch(ucs){
+ case LINE_FEED :
+ _line = min(_line+1,ps_global->ttyo->screen_rows);
+ _col =0;
+ mswin_move(_line, _col);
+ break;
+
+ case RETURN : /* move to column 0 */
+ _col = 0;
+ mswin_move(_line, _col);
+
+ case BACKSPACE : /* move back a space if not in column 0 */
+ if(_col > 0)
+ mswin_move(_line, --_col);
+
+ break;
+
+ case BELL : /* ring the bell but don't advance _col */
+ mswin_beep(); /* libpico call */
+ break;
+
+ case TAB: /* if a tab, output it */
+ do
+ mswin_putc(' ');
+ while(((++_col)&0x07) != 0);
+ break;
+
+ default:
+ /* pass_ctrl_chars is always 1 for Windows */
+ width = wcellwidth(ucs);
+ if(width < 0){
+ mswin_putc('?');
+ _col++;
+ }
+ else if(_col + width > ps_global->ttyo->screen_cols){
+ int i;
+
+ i = ps_global->ttyo->screen_cols - _col - 1;
+ while(i-- > 0)
+ mswin_putc(' ');
+
+ _col = ps_global->ttyo->screen_cols - 1;
+ mswin_move(_line, _col);
+ mswin_putc('>');
+ _col++;
+ }
+ else{
+ mswin_putc(ucs);
+ _col += width;
+ }
+ }
+
+ if(_col >= ps_global->ttyo->screen_cols){
+ _col = ps_global->ttyo->screen_cols;
+ mswin_move(_line, _col);
+ }
+
+ return;
+}
+
+
+/*----------------------------------------------------------------------
+ Printf style write directly to the terminal at current position
+
+ Input: printf style control string
+ number of printf style arguments
+ up to three printf style arguments
+
+ Result: Line written to the screen
+ ----------------------------------------------------------------------*/
+void
+Write_to_screen(char *string)
+{
+ if(ps_global->in_init_seq)
+ return;
+
+ mswin_puts (string);
+
+ /*
+ * mswin_puts does not keep track of where we are. Simple fix is to
+ * reset _line and _col to unknown so that the next MoveCursor will
+ * do a real move. We could check for a simple string and actually
+ * keep track. Is that useful? I don't think so.
+ *
+ * There are other places where we lose track, but by comparing with
+ * the termout.unx code where we also lose track it would seem we don't
+ * really need to keep track in those other cases.
+ */
+ _line = FARAWAY;
+ _col = FARAWAY;
+}
+
+void
+Write_to_screen_n(char *string, int n)
+{
+ if(ps_global->in_init_seq)
+ return;
+
+ mswin_puts_n (string, n);
+ _line = FARAWAY;
+ _col = FARAWAY;
+}
+
+
+/*----------------------------------------------------------------------
+ Clear the terminal screen
+ ----------------------------------------------------------------------*/
+void
+ClearScreen(void)
+{
+ _line = 0; /* clear leaves us at top... */
+ _col = 0;
+
+ if(ps_global->in_init_seq)
+ return;
+
+ mark_status_unknown();
+ mark_keymenu_dirty();
+ mark_titlebar_dirty();
+
+ mswin_move(0, 0);
+ mswin_eeop();
+}
+
+/*----------------------------------------------------------------------
+ Clear screen to end of line on current line
+ ----------------------------------------------------------------------*/
+void
+CleartoEOLN(void)
+{
+ mswin_eeol();
+}
+
+
+/*----------------------------------------------------------------------
+ function to output string such that it becomes icon text
+
+ Args: s -- string to write
+
+ Result: string indicated become our "icon" text
+ ----*/
+void
+icon_text(char *s, int type)
+{
+ if(type == IT_MCLOSED)
+ mswin_mclosedtext (s);
+ else /* IT_NEWMAIL */
+ mswin_newmailtext (s);
+}
+
+/* Scroll stuff */
+
+/*
+ * Set the current position of the scroll bar.
+ */
+void
+scroll_setpos(long newpos)
+{
+ mswin_setscrollpos (newpos);
+}
+
+/*
+ * Set the range of the scroll bar.
+ * zero disables the scroll bar.
+ */
+void
+scroll_setrange (long pagelen, long rangeend)
+{
+ if(rangeend < pagelen)
+ mswin_setscrollrange (0L, 0L);
+ else
+ mswin_setscrollrange (pagelen, rangeend + pagelen - 1L);
+}
+
+void
+EndScroll(void)
+{
+}
+
+int
+ScrollRegion(int lines)
+{
+ return(-1);
+}
+
+int
+BeginScroll(int top, int bottom)
+{
+ static int __t, __b;
+
+ __t = top;
+ __b = bottom;
+ return(-1);
+}
+
+
+/* Dialog stuff */
+#define WINDOW_USER_DATA GWL_USERDATA
+
+/*===========================================================================
+ *
+ * Dialog Data
+ *
+ * The following structures hold the state data for dialogs.
+ */
+typedef struct DLG_TYPEMAP {
+ int pineid;
+ int rsrcid;
+} DLG_TYPEMAP;
+
+typedef struct DLG_SORTDATA {
+ DLG_SORTPARAM *sortsel; /* parameter structure. */
+ int sortcount; /* Number of different sorts. */
+ DLG_TYPEMAP types[20]; /* Map pine sort types to ctrl ids. */
+} DLG_SORTDATA;
+
+typedef struct DLG_LOGINDATA {
+ NETMBX *mb;
+ LPTSTR user;
+ int userlen;
+ LPTSTR pwd;
+ int pwdlen;
+ int pwc; /* set if we're using passfiles and we don't know user yet */
+ int fixuser;
+ int prespass; /* if user wants to preserve the password */
+ int rv;
+} DLG_LOGINDATA;
+
+typedef struct DLG_CONFIGDATA {
+ char *confpath; /* UTF-8 path */
+ int confpathlen;
+ int setreg;
+ int nopinerc;
+ int rv;
+} DLG_CONFIGDATA;
+
+typedef struct installvars {
+ char *pname;
+ char *userid;
+ char *domain;
+ char *inboxpath;
+ char *smtpserver;
+ unsigned defmailclient:1;
+ unsigned defnewsclient:1;
+} INSTALLVARS_S;
+
+typedef struct DLG_CONGIGVARSDATA {
+ INSTALLVARS_S *ivars;
+ int rv;
+} DLG_CONFIGVARSDATA;
+
+typedef struct DLG_FLAGDATA {
+ struct flag_table *ftbl;
+ int flagcount;
+ HINSTANCE hInstance;
+ HWND hTextWnd;
+} DLG_FLAGDATA;
+
+int os_config_vars_dialog(INSTALLVARS_S *);
+static void GetBtnPos (HWND, HWND, RECT *);
+
+int
+init_install_get_vars(void)
+{
+ INSTALLVARS_S ivars;
+ struct pine *ps = ps_global;
+ char *p;
+
+ memset((void *)&ivars, 0, sizeof(INSTALLVARS_S));
+ if(ps->vars[V_PERSONAL_NAME].main_user_val.p)
+ ivars.pname = cpystr(ps->vars[V_PERSONAL_NAME].main_user_val.p);
+ if(ps->vars[V_USER_ID].main_user_val.p)
+ ivars.userid = cpystr(ps->vars[V_USER_ID].main_user_val.p);
+ if(ps->vars[V_USER_DOMAIN].main_user_val.p)
+ ivars.domain = cpystr(ps->vars[V_USER_DOMAIN].main_user_val.p);
+ if(ps->vars[V_INBOX_PATH].main_user_val.p)
+ ivars.inboxpath = cpystr(ps->vars[V_INBOX_PATH].main_user_val.p);
+ else if(ps->prc && ps->prc->name && (*ps->prc->name == '{')
+ && (p = strindex(ps->prc->name, '}'))){
+ ivars.inboxpath = cpystr(ps->prc->name);
+ if(p = strindex(ivars.inboxpath, '}'))
+ *(p+1) = '\0';
+ }
+ if(ps->vars[V_SMTP_SERVER].main_user_val.l
+ && *ps->vars[V_SMTP_SERVER].main_user_val.l)
+ ivars.smtpserver = cpystr(ps->vars[V_SMTP_SERVER].main_user_val.l[0]);
+ ivars.defmailclient = mswin_is_def_client(MSWR_SDC_MAIL);
+ ivars.defnewsclient = mswin_is_def_client(MSWR_SDC_NEWS);
+
+ if(os_config_vars_dialog(&ivars) == 0){
+ set_variable(V_PERSONAL_NAME, ivars.pname, 0, 0, Main);
+ set_variable(V_USER_ID, ivars.userid, 0, 0, Main);
+ set_variable(V_USER_DOMAIN, ivars.domain, 0, 0, Main);
+ set_variable(V_INBOX_PATH, ivars.inboxpath, 0, 0, Main);
+ if(ivars.smtpserver){
+ if(ps->vars[V_SMTP_SERVER].main_user_val.l){
+ fs_give((void **)&ps->vars[V_SMTP_SERVER].main_user_val.l[0]);
+ ps->vars[V_SMTP_SERVER].main_user_val.l[0]
+ = cpystr(ivars.smtpserver);
+ set_current_val(&ps->vars[V_SMTP_SERVER], TRUE, FALSE);
+ }
+ else {
+ char *tstrlist[2];
+
+ tstrlist[0] = ivars.smtpserver;
+ tstrlist[1] = NULL;
+ set_variable_list(V_SMTP_SERVER, tstrlist, 0, Main);
+ }
+ }
+ write_pinerc(ps_global, Main, WRP_NONE);
+ if(ivars.defmailclient)
+ mswin_set_def_client(MSWR_SDC_MAIL);
+ if(ivars.defnewsclient)
+ mswin_set_def_client(MSWR_SDC_NEWS);
+
+ /* Tell Windows that stuff has changed */
+ if(ivars.defmailclient || ivars.defnewsclient)
+ SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE,
+ (WPARAM)NULL, (LPARAM)NULL,
+ SMTO_NORMAL, 1000, NULL);
+ }
+
+ if(ivars.pname)
+ fs_give((void **)&ivars.pname);
+ if(ivars.userid)
+ fs_give((void **)&ivars.userid);
+ if(ivars.domain)
+ fs_give((void **)&ivars.domain);
+ if(ivars.inboxpath)
+ fs_give((void **)&ivars.inboxpath);
+ if(ivars.smtpserver)
+ fs_give((void **)&ivars.smtpserver);
+ return 0;
+}
+
+/*
+ * Show args dialog
+ */
+int
+os_argsdialog (char **arg_text)
+{
+ DLGPROC dlgprc;
+ HINSTANCE hInst;
+ HWND hWnd;
+
+ hInst = (HINSTANCE) mswin_gethinstance ();
+ hWnd = (HWND) mswin_gethwnd ();
+
+ mswin_killsplash();
+
+ dlgprc = args_dialog_proc;
+
+ DialogBoxParam (hInst, MAKEINTRESOURCE (IDD_ARGLIST), hWnd,
+ dlgprc, (LPARAM) arg_text);
+
+ return(1);
+}
+
+/*
+ * Prompt for username and password
+ */
+int
+os_login_dialog (NETMBX *mb, char *user_utf8, int userlen,
+ char *pwd_utf8, int pwdlen, int pwc, int fixuser, int *prespass)
+{
+ DLGPROC dlgprc;
+ HINSTANCE hInst;
+ HWND hWnd;
+ DLG_LOGINDATA dlgpw;
+ LPTSTR user_lptstr, pwd_lptstr;
+ char *tuser_utf8, *tpwd_utf8;
+
+ mswin_killsplash();
+ hInst = (HINSTANCE) mswin_gethinstance ();
+ hWnd = (HWND) mswin_gethwnd ();
+
+ dlgpw.mb = mb;
+
+ dlgpw.user = (LPTSTR)fs_get(userlen*sizeof(TCHAR));
+ user_lptstr = utf8_to_lptstr(user_utf8);
+ _tcsncpy(dlgpw.user, user_lptstr, userlen - 1);
+ dlgpw.user[userlen - 1] = '\0';
+ fs_give((void **) &user_lptstr);
+ dlgpw.userlen = userlen;
+
+ dlgpw.pwd = (LPTSTR)fs_get(pwdlen*sizeof(TCHAR));
+ pwd_lptstr = utf8_to_lptstr(pwd_utf8);
+ _tcsncpy(dlgpw.pwd, pwd_lptstr, pwdlen - 1);
+ dlgpw.pwd[pwdlen - 1] = '\0';
+ fs_give((void **) &pwd_lptstr);
+ dlgpw.pwdlen = pwdlen;
+
+ dlgpw.fixuser = fixuser;
+ dlgpw.pwc = pwc;
+ dlgpw.rv = 0;
+
+ dlgprc = login_dialog_proc;
+
+ DialogBoxParam (hInst, MAKEINTRESOURCE (ps_global->install_flag
+ ? IDD_LOGINDLG2 : IDD_LOGINDLG),
+ NULL, dlgprc, (LPARAM)&dlgpw);
+
+ if(dlgpw.rv == 0){
+ tuser_utf8 = lptstr_to_utf8(dlgpw.user);
+ if(tuser_utf8){
+ strncpy(user_utf8, tuser_utf8, userlen - 1);
+ user_utf8[userlen - 1] = '\0';
+ fs_give((void **) &tuser_utf8);
+ }
+
+ tpwd_utf8 = lptstr_to_utf8(dlgpw.pwd);
+ if(tpwd_utf8){
+ strncpy(pwd_utf8, tpwd_utf8, pwdlen - 1);
+ pwd_utf8[pwdlen - 1] = '\0';
+ fs_give((void **) &tpwd_utf8);
+ }
+ if(prespass)
+ (*prespass) = dlgpw.prespass;
+ }
+
+ return(dlgpw.rv);
+}
+
+/*
+ * Select message flags.
+ */
+int
+os_flagmsgdialog (struct flag_table *ftbl)
+{
+ DLGPROC dlgprc;
+ HINSTANCE hInst;
+ HWND hWnd;
+ DLG_FLAGDATA dlgflag;
+ int rval;
+
+ hInst = (HINSTANCE) mswin_gethinstance ();
+ hWnd = (HWND) mswin_gethwnd ();
+
+ dlgflag.ftbl = ftbl;
+ dlgflag.hInstance = hInst;
+ dlgflag.hTextWnd = hWnd;
+
+ dlgprc = flag_dialog_proc;
+
+ rval = DialogBoxParam (hInst, MAKEINTRESOURCE (IDD_SELECTFLAG), hWnd,
+ dlgprc, (LPARAM)&dlgflag);
+
+ return (rval);
+}
+
+/*
+ * Select a sort type.
+ */
+
+int
+os_sortdialog (DLG_SORTPARAM *sortsel)
+{
+ DLGPROC dlgprc;
+ HINSTANCE hInst;
+ HWND hWnd;
+ int i;
+ DLG_SORTDATA dlgsort;
+
+ hInst = (HINSTANCE) mswin_gethinstance ();
+ hWnd = (HWND) mswin_gethwnd ();
+
+
+ /* Build a map of pine sort types to the resource types. */
+ i = 0;
+ dlgsort.types[i].pineid = SortArrival;
+ dlgsort.types[i++].rsrcid = IDC_SORTARRIVAL;
+ dlgsort.types[i].pineid = SortDate;
+ dlgsort.types[i++].rsrcid = IDC_SORTDATE;
+ dlgsort.types[i].pineid = SortFrom;
+ dlgsort.types[i++].rsrcid = IDC_SORTFROM;
+ dlgsort.types[i].pineid = SortSubject;
+ dlgsort.types[i++].rsrcid = IDC_SORTSUBJECT;
+ dlgsort.types[i].pineid = SortSubject2;
+ dlgsort.types[i++].rsrcid = IDC_SORTORDERSUB;
+ dlgsort.types[i].pineid = SortTo;
+ dlgsort.types[i++].rsrcid = IDC_SORTTO;
+ dlgsort.types[i].pineid = SortCc;
+ dlgsort.types[i++].rsrcid = IDC_SORTCC;
+ dlgsort.types[i].pineid = SortSize;
+ dlgsort.types[i++].rsrcid = IDC_SORTSIZE;
+ dlgsort.types[i].pineid = SortThread;
+ dlgsort.types[i++].rsrcid = IDC_SORTTHREAD;
+ dlgsort.types[i].pineid = SortScore;
+ dlgsort.types[i++].rsrcid = IDC_SORTSCORE;
+ dlgsort.sortcount = i;
+ dlgsort.sortsel = sortsel;
+
+ dlgprc = sort_dialog_proc;
+
+ DialogBoxParam (hInst, MAKEINTRESOURCE (IDD_SELECTSORT), hWnd,
+ dlgprc, (LPARAM)&dlgsort);
+
+ return(1);
+}
+
+
+/*
+ * Dialog proc to handle index sort selection.
+ *
+ * Configures the dialog box on init and retrieves the settings on exit.
+ */
+BOOL CALLBACK __export
+args_dialog_proc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ BOOL ret = FALSE;
+ long i, j, block_size;
+ char **args_text;
+ LPTSTR args_text_lptstr, args_block_lptstr;
+
+
+ switch (uMsg) {
+ case WM_INITDIALOG:
+
+ args_text = (char **)lParam;
+
+ /*
+ * First convert char *'s over to one big block of
+ * Unicode
+ */
+ i = 0;
+ while(args_text && *args_text){
+ i += strlen(*args_text++);
+ i += 2;
+ }
+ block_size = i;
+
+ args_block_lptstr = (LPTSTR)fs_get((block_size+1)*sizeof(TCHAR));
+
+ args_text = (char **)lParam;
+ i = 0;
+ j = 0;
+ while(args_text && *args_text){
+ args_text_lptstr = utf8_to_lptstr(*args_text++);
+ while(args_text_lptstr[i] && j < block_size){
+ args_block_lptstr[j++] = args_text_lptstr[i++];
+ }
+ args_block_lptstr[j++] = '\r';
+ args_block_lptstr[j++] = '\n';
+ fs_give((void **) &args_text_lptstr);
+ i = 0;
+ }
+ args_block_lptstr[j] = '\0';
+
+ /* and replace everything selected with args_text */
+ SendDlgItemMessage(hDlg, IDC_ARGTEXT, WM_SETTEXT, (WPARAM) 0,
+ (LPARAM) args_block_lptstr);
+ fs_give((void **)&args_block_lptstr);
+
+ return (1);
+
+ case WM_CLOSE :
+ ret = TRUE;
+ EndDialog (hDlg, TRUE);
+ break;
+
+
+ case WM_COMMAND:
+ switch (wParam) {
+ case IDOK:
+ ret = TRUE;
+ EndDialog (hDlg, TRUE);
+ break;
+ }
+ break;
+
+
+ }
+ return (ret);
+}
+
+/*
+ * Dialog proc to handle login
+ *
+ * Configures the dialog box on init and retrieves the settings on exit.
+ */
+BOOL CALLBACK __export
+login_dialog_proc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ DLG_LOGINDATA *dlglogin;
+ BOOL ret = FALSE;
+ TCHAR tcbuf[1024];
+ NETMBX *mb;
+ LPTSTR user, pwd;
+ LPTSTR host_lptstr;
+
+ switch (uMsg) {
+ case WM_INITDIALOG:
+
+ dlglogin = (DLG_LOGINDATA *)lParam;
+ mb = dlglogin->mb;
+ user = dlglogin->user;
+ pwd = dlglogin->pwd;
+ SetWindowLong (hDlg, WINDOW_USER_DATA, (LONG) dlglogin);
+
+ host_lptstr = utf8_to_lptstr(mb->host);
+ _sntprintf(tcbuf, sizeof(tcbuf), TEXT("Host: %.100s%s"), host_lptstr,
+ !mb->sslflag && !mb->tlsflag ? TEXT(" (INSECURE)") : TEXT(""));
+ fs_give((void **) &host_lptstr);
+
+ if(mb->sslflag || mb->tlsflag)
+ SetWindowText(hDlg, TEXT("Alpine Login +"));
+ else
+ SetWindowText(hDlg, TEXT("Alpine Insecure Login"));
+
+ if(dlglogin->pwc){
+ EnableWindow(GetDlgItem(hDlg, IDC_RPASSWORD),0);
+ EnableWindow(GetDlgItem(hDlg, IDC_RPWTEXT),0);
+ EnableWindow(GetDlgItem(hDlg, IDC_PRESPASS),0);
+ }
+ if(mb->user && *mb->user){
+ LPTSTR user_lptstr;
+
+ user_lptstr = utf8_to_lptstr(mb->user);
+ SetDlgItemText(hDlg, IDC_RLOGINE, user_lptstr);
+ if((mb->user && *mb->user) || dlglogin->fixuser)
+ EnableWindow(GetDlgItem(hDlg, IDC_RLOGINE), 0);
+ fs_give((void **) &user_lptstr);
+ }
+ else if(user){
+ SetDlgItemText(hDlg, IDC_RLOGINE, user);
+ if(dlglogin->fixuser)
+ EnableWindow(GetDlgItem(hDlg, IDC_RLOGINE), 0);
+ }
+ SetDlgItemText(hDlg, IDC_PROMPT, tcbuf);
+ return (1);
+
+
+ case WM_COMMAND:
+ dlglogin = (DLG_LOGINDATA *) GetWindowLong (hDlg, WINDOW_USER_DATA);
+ switch (wParam) {
+ case IDOK:
+ /* Retrieve the new username/passwd. */
+ user = dlglogin->user;
+ pwd = dlglogin->pwd;
+ GetDlgItemText(hDlg, IDC_RLOGINE, user, dlglogin->userlen - 1);
+ GetDlgItemText(hDlg, IDC_RPASSWORD, pwd, dlglogin->pwdlen - 1);
+ user[dlglogin->userlen - 1] = '\0';
+ pwd[dlglogin->pwdlen - 1] = '\0';
+ dlglogin->prespass = (IsDlgButtonChecked(hDlg, IDC_PRESPASS) == BST_CHECKED);
+
+ EndDialog (hDlg, LOWORD(wParam));
+ dlglogin->rv = 0;
+ ret = TRUE;
+ break;
+
+ case IDCANCEL:
+ dlglogin->rv = 1;
+ ret = TRUE;
+ EndDialog (hDlg, LOWORD(wParam));
+ break;
+ }
+ break;
+ }
+ return (ret);
+}
+
+/*
+ * Dialog proc to handle flag selection.
+ *
+ * Configures the dialog box on init, adding buttons as needed for
+ * an unknown number of flags.
+ * Retrieves the settings on exit.
+ */
+BOOL CALLBACK __export
+flag_dialog_proc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ DLG_FLAGDATA *dlgflag;
+ BOOL ret = FALSE;
+ int i;
+ struct flag_table *fp;
+ HWND hRB[2], hBtn;
+ RECT rb[2];
+ UINT bheight, bwidth, bvertSpace;
+ UINT btnOKHeight;
+ int base, line;
+ int bstate;
+ HFONT btnFont;
+
+
+
+
+ switch (uMsg) {
+ case WM_INITDIALOG:
+ dlgflag = (DLG_FLAGDATA *)lParam;
+ SetWindowLong (hDlg, WINDOW_USER_DATA, (LONG) dlgflag);
+
+ /* Count buttons */
+ dlgflag->flagcount = 0;
+ for (fp = dlgflag->ftbl; fp && fp->name; ++fp)
+ ++dlgflag->flagcount;
+
+ /* Get the positions of the current buttons. */
+ for (i = 0; i < 2; ++i) {
+ hRB[i] = GetDlgItem (hDlg, IDC_FLAGCOL1 + i);
+ GetBtnPos (hDlg, hRB[i], &rb[i]);
+ }
+ bheight = rb[0].bottom - rb[0].top;
+ bwidth = rb[0].right - rb[0].left;
+ bvertSpace = bheight + 5;
+ btnFont = (HFONT) SendMessage (hRB[0], WM_GETFONT, 0, 0);
+
+ for (i = 0; i < dlgflag->flagcount; ++i) {
+ LPTSTR fp_name_lptstr;
+
+ fp = &dlgflag->ftbl[i];
+
+ fp_name_lptstr = utf8_to_lptstr(fp->name);
+ if (i < 2) {
+ hBtn = hRB[i];
+ SetWindowText (hBtn, fp_name_lptstr);
+ }
+ else {
+ base = i % 2;
+ line = i / 2;
+ hBtn = CreateWindow (TEXT("BUTTON"), fp_name_lptstr,
+ WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX,
+ rb[base].left, rb[base].top + bvertSpace * line,
+ bwidth, bheight,
+ hDlg, (HMENU)NULL, dlgflag->hInstance, NULL);
+ SetWindowLong (hBtn, GWL_ID, IDC_FLAGCOL1 + i);
+ SendMessage (hBtn, WM_SETFONT, (WPARAM)btnFont,
+ MAKELPARAM (0, 0));
+ }
+
+ fs_give((void **) &fp_name_lptstr);
+ if (fp->ukn)
+ SendMessage (hBtn, BM_SETSTYLE,
+ (WPARAM)(BS_CHECKBOX | BS_AUTO3STATE), 0);
+ SendMessage (hBtn, BM_SETCHECK,
+ (WPARAM) fp->set == CMD_FLAG_UNKN ? 2 : fp->set ? 1 : 0,
+ 0);
+ ShowWindow (hBtn, SW_SHOW);
+ EnableWindow (hBtn, TRUE);
+ }
+
+ /* Position the OK and Cancel buttons. */
+ line = (dlgflag->flagcount + 1) / 2;
+ for (i = 0; i < 2; ++i) {
+ hRB[1] = GetDlgItem (hDlg, i == 0 ? IDOK : IDCANCEL);
+ GetBtnPos (hDlg, hRB[1], &rb[1]);
+ MoveWindow (hRB[1], rb[1].left, rb[0].top + bvertSpace * line,
+ rb[1].right - rb[1].left, rb[1].bottom - rb[1].top,
+ FALSE);
+ btnOKHeight = rb[1].bottom - rb[1].top;
+ }
+
+
+ /* Resize whole dialog window. */
+ GetWindowRect (hDlg, &rb[1]);
+ rb[1].right -= rb[1].left;
+ rb[1].bottom = rb[0].top + bvertSpace * line + btnOKHeight + 10 +
+ GetSystemMetrics (SM_CYCAPTION);
+ MoveWindow (hDlg, rb[1].left, rb[1].top, rb[1].right,
+ rb[1].bottom, TRUE);
+ return (1);
+
+
+ case WM_COMMAND:
+ dlgflag = (DLG_FLAGDATA *) GetWindowLong (hDlg, WINDOW_USER_DATA);
+ switch (wParam) {
+
+ case IDOK:
+ /* Retrieve the button states. */
+ for (i = 0; i < dlgflag->flagcount; ++i) {
+ fp = &dlgflag->ftbl[i];
+ bstate = SendMessage (GetDlgItem (hDlg, IDC_FLAGCOL1 + i),
+ BM_GETCHECK, 0, 0);
+ switch (bstate) {
+ case 2: fp->set = CMD_FLAG_UNKN; break;
+ case 1: fp->set = CMD_FLAG_SET; break;
+ case 0: fp->set = CMD_FLAG_CLEAR; break;
+ }
+ }
+ EndDialog (hDlg, TRUE);
+ ret = TRUE;
+ break;
+
+ case IDCANCEL:
+ EndDialog (hDlg, FALSE);
+ ret = TRUE;
+ break;
+ }
+ break;
+
+
+ }
+ return (ret);
+}
+
+/*
+ * Dialog proc to handle index sort selection.
+ *
+ * Configures the dialog box on init and retrieves the settings on exit.
+ */
+BOOL CALLBACK __export
+sort_dialog_proc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ DLG_SORTDATA *dlgsort;
+ BOOL ret = FALSE;
+ int cursort;
+ int i;
+
+
+ switch (uMsg) {
+ case WM_INITDIALOG:
+ dlgsort = (DLG_SORTDATA *)lParam;
+ SetWindowLong (hDlg, WINDOW_USER_DATA, (LONG) dlgsort);
+
+ /* Set the reversed button state. */
+ CheckDlgButton (hDlg, IDC_SORTREVERSE, dlgsort->sortsel->reverse);
+
+ /* Set the current sort type radio button.*/
+ cursort = IDC_SORTARRIVAL;
+ for (i = 0; i < dlgsort->sortcount; ++i) {
+ if (dlgsort->types[i].pineid == dlgsort->sortsel->cursort) {
+ cursort = dlgsort->types[i].rsrcid;
+ break;
+ }
+ }
+ CheckRadioButton (hDlg, IDC_SORTFIRSTBUTTON, IDC_SORTLASTBUTTON,
+ cursort);
+
+ EnableWindow (GetDlgItem (hDlg, IDC_GETHELP),
+ (dlgsort->sortsel->helptext != NULL));
+ return (1);
+
+
+ case WM_COMMAND:
+ dlgsort = (DLG_SORTDATA *) GetWindowLong (hDlg, WINDOW_USER_DATA);
+ switch (wParam) {
+ case IDC_GETHELP:
+ if (dlgsort->sortsel->helptext)
+ mswin_showhelpmsg ((WINHAND)hDlg, dlgsort->sortsel->helptext);
+ ret = TRUE;
+ break;
+
+ case IDOK:
+ dlgsort->sortsel->rval = 1;
+
+ /* Retrieve the reverse sort state. */
+ dlgsort->sortsel->reverse = (IsDlgButtonChecked (hDlg, IDC_SORTREVERSE) == 1);
+
+ /* Retrieve the new sort type. */
+ for (i = 0; i < dlgsort->sortcount; ++i) {
+ if (IsDlgButtonChecked (hDlg, dlgsort->types[i].rsrcid)) {
+ dlgsort->sortsel->cursort = dlgsort->types[i].pineid;
+ break;
+ }
+ }
+ EndDialog (hDlg, dlgsort->sortsel->rval);
+ ret = TRUE;
+ break;
+
+ case IDCANCEL:
+ dlgsort->sortsel->rval = 0;
+ ret = TRUE;
+ EndDialog (hDlg, dlgsort->sortsel->rval);
+ break;
+ }
+ break;
+
+
+ }
+ return (ret);
+}
+
+int
+os_config_vars_dialog (INSTALLVARS_S *ivars)
+{
+ DLGPROC dlgprc;
+ HINSTANCE hInst;
+ HWND hWnd;
+ DLG_CONFIGVARSDATA dlgcv;
+
+ mswin_killsplash();
+ hInst = (HINSTANCE) mswin_gethinstance ();
+ hWnd = (HWND) mswin_gethwnd ();
+
+ dlgcv.ivars = ivars;
+ dlgcv.rv = 0;
+
+ dlgprc = config_vars_dialog_proc;
+
+ DialogBoxParam (hInst, MAKEINTRESOURCE (IDD_CFVARSDLG), NULL,
+ dlgprc, (LPARAM)&dlgcv);
+
+ return(dlgcv.rv);
+}
+
+/*
+ * Dialog proc to handle login
+ *
+ * Configures the dialog box on init and retrieves the settings on exit.
+ */
+BOOL CALLBACK __export
+config_vars_dialog_proc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ DLG_CONFIGVARSDATA *dlgcv;
+ BOOL ret = FALSE;
+ int usepop = 0;
+ char buf[2*MAXPATH+1], *p;
+ int buflen = 2*MAXPATH;
+#define TCBUFLEN (2*MAXPATH)
+ TCHAR tcbuf[TCBUFLEN+1];
+ LPTSTR tmp_lptstr;
+ char *u;
+
+ switch (uMsg) {
+ case WM_INITDIALOG:
+ dlgcv = (DLG_CONFIGVARSDATA *)lParam;
+ SetWindowLong (hDlg, WINDOW_USER_DATA, (LONG) dlgcv);
+ if(dlgcv->ivars->pname){
+ tmp_lptstr = utf8_to_lptstr(dlgcv->ivars->pname);
+ if(tmp_lptstr){
+ SetDlgItemText(hDlg, IDC_CFV_PNAME, tmp_lptstr);
+ fs_give((void **) &tmp_lptstr);
+ }
+ }
+
+ if(dlgcv->ivars->userid && dlgcv->ivars->domain){
+ snprintf(buf, sizeof(buf), "%.*s@%.*s", MAXPATH, dlgcv->ivars->userid,
+ MAXPATH, dlgcv->ivars->domain);
+ buf[buflen-1] = '\0';
+ tmp_lptstr = utf8_to_lptstr(buf);
+ if(tmp_lptstr){
+ SetDlgItemText(hDlg, IDC_CFV_EMAILADR, tmp_lptstr);
+ fs_give((void **) &tmp_lptstr);
+ }
+ }
+
+ if(dlgcv->ivars->inboxpath){
+ char *mbx;
+
+ mbx = dlgcv->ivars->inboxpath;
+ if(*mbx == '{' && (p = strindex(mbx, '}'))
+ && ((*(p+1) == '\0') || !strucmp(p+1, "inbox"))){
+ char srvbuf[MAXPATH+1], tuser[MAXPATH+1];
+ int i, j, k;
+
+ srvbuf[0] = '\0';
+ tuser[0] = '\0';
+ strncpy(buf, mbx+1, min(buflen, (int)(p - (mbx+1))));
+ buf[min(buflen-1, (int)(p - (mbx+1)))] = '\0';
+ for(i = 0, j = 0; buf[i] && j < MAXPATH; i++){
+ if(buf[i] == '/'){
+ if(!struncmp("/user=", buf+i, 6)){
+ i += 6;
+ for(k = 0; k < MAXPATH && buf[i]
+ && buf[i] != '/'; i++)
+ tuser[k++] = buf[i];
+ tuser[k] = '\0';
+ i--;
+ }
+ else if(!struncmp("/pop3", buf+i, 5)
+ && (!*(buf+5) || (*(buf+5) == '/'))){
+ usepop = 1;
+ i += 4;
+ }
+ else
+ srvbuf[j++] = buf[i];
+ }
+ else
+ srvbuf[j++] = buf[i];
+ }
+ srvbuf[j] = '\0';
+ if(*srvbuf){
+ tmp_lptstr = utf8_to_lptstr(srvbuf);
+ if(tmp_lptstr){
+ SetDlgItemText(hDlg, IDC_CFV_MSERVER, tmp_lptstr);
+ fs_give((void **) &tmp_lptstr);
+ }
+ }
+
+ if(*tuser){
+ tmp_lptstr = utf8_to_lptstr(tuser);
+ if(tmp_lptstr){
+ SetDlgItemText(hDlg, IDC_CFV_LOGIN, tmp_lptstr);
+ fs_give((void **) &tmp_lptstr);
+ }
+ }
+ }
+ else {
+ tmp_lptstr = utf8_to_lptstr(mbx);
+ if(tmp_lptstr){
+ SetDlgItemText(hDlg, IDC_CFV_MSERVER, tmp_lptstr);
+ fs_give((void **) &tmp_lptstr);
+ }
+
+ if(*mbx != '{' && *mbx != '#')
+ EnableWindow(GetDlgItem(hDlg, IDC_CFV_MSERVER), 0);
+ }
+ }
+ CheckRadioButton (hDlg, IDC_CFV_IMAP, IDC_CFV_POP3,
+ usepop ? IDC_CFV_POP3 : IDC_CFV_IMAP);
+ if(dlgcv->ivars->smtpserver){
+ tmp_lptstr = utf8_to_lptstr(dlgcv->ivars->smtpserver);
+ if(tmp_lptstr){
+ SetDlgItemText(hDlg, IDC_CFV_SMTPSERVER, tmp_lptstr);
+ fs_give((void **) &tmp_lptstr);
+ }
+ }
+
+ if(dlgcv->ivars->defmailclient)
+ CheckDlgButton(hDlg, IDC_CFV_DEFMAILER, BST_CHECKED);
+ if(dlgcv->ivars->defnewsclient)
+ CheckDlgButton(hDlg, IDC_CFV_DEFNEWSRDR, BST_CHECKED);
+
+ return (1);
+ break;
+
+ case WM_COMMAND:
+ dlgcv = (DLG_CONFIGVARSDATA *) GetWindowLong (hDlg, WINDOW_USER_DATA);
+ switch (wParam) {
+ case IDOK:
+ /* personal name */
+ GetDlgItemText(hDlg, IDC_CFV_PNAME, tcbuf, TCBUFLEN);
+ tcbuf[TCBUFLEN] = '\0';
+ u = lptstr_to_utf8(tcbuf);
+ if(u){
+ removing_leading_and_trailing_white_space(u);
+ if(dlgcv->ivars->pname)
+ fs_give((void **)&dlgcv->ivars->pname);
+
+ if(*u){
+ dlgcv->ivars->pname = u;
+ u = NULL;
+ }
+
+ if(u)
+ fs_give((void **) &u);
+ }
+
+ /* user-id and domain */
+ GetDlgItemText(hDlg, IDC_CFV_EMAILADR, tcbuf, TCBUFLEN);
+ tcbuf[TCBUFLEN] = '\0';
+ u = lptstr_to_utf8(tcbuf);
+ if(u){
+ removing_leading_and_trailing_white_space(u);
+ if(p = strindex(u, '@')){
+ *(p++) = '\0';
+ if(dlgcv->ivars->userid)
+ fs_give((void **)&dlgcv->ivars->userid);
+
+ if(dlgcv->ivars->domain)
+ fs_give((void **)&dlgcv->ivars->domain);
+
+ if(*u){
+ dlgcv->ivars->userid = u;
+ u = NULL;
+ }
+
+ if(*p)
+ dlgcv->ivars->domain = cpystr(p);
+ }
+ else{
+ MessageBox(hDlg, TEXT("Invalid email address. Should be username@domain"),
+ TEXT("Alpine"), MB_ICONWARNING | MB_OK);
+ return(TRUE);
+ }
+
+ if(u)
+ fs_give((void **) &u);
+ }
+
+ /* inbox-path */
+ GetDlgItemText(hDlg, IDC_CFV_MSERVER, tcbuf, TCBUFLEN);
+ tcbuf[TCBUFLEN] = '\0';
+ u = lptstr_to_utf8(tcbuf);
+ if(u){
+ removing_leading_and_trailing_white_space(u);
+ if(*u == '{' || *u == '#'
+ || !IsWindowEnabled(GetDlgItem(hDlg, IDC_CFV_MSERVER))){
+ if(dlgcv->ivars->inboxpath)
+ fs_give((void **)&dlgcv->ivars->inboxpath);
+
+ dlgcv->ivars->inboxpath = u;
+ u = NULL;
+ }
+ else if(*u){
+ char tsrvr[4*MAXPATH+1];
+ int tsrvrlen = 4*MAXPATH;
+
+ if(dlgcv->ivars->inboxpath)
+ fs_give((void **)&dlgcv->ivars->inboxpath);
+
+ snprintf(tsrvr, sizeof(tsrvr), "{%s%s", u,
+ IsDlgButtonChecked(hDlg, IDC_CFV_POP3)
+ == BST_CHECKED ? "/pop3" : "");
+
+ if(u)
+ fs_give((void **) &u);
+
+ GetDlgItemText(hDlg, IDC_CFV_LOGIN, tcbuf, TCBUFLEN);
+ tcbuf[TCBUFLEN] = '\0';
+ u = lptstr_to_utf8(tcbuf);
+ if(u){
+ removing_leading_and_trailing_white_space(u);
+ if(*u){
+ strncat(tsrvr, "/user=", sizeof(tsrvr)-strlen(tsrvr)-1);
+ strncat(tsrvr, u, sizeof(tsrvr)-strlen(tsrvr)-1);
+ }
+
+ strncat(tsrvr, "}inbox", sizeof(tsrvr)-strlen(tsrvr)-1);
+ tsrvr[sizeof(tsrvr)-1] = '\0';
+ dlgcv->ivars->inboxpath = cpystr(tsrvr);
+ }
+ }
+
+ if(u)
+ fs_give((void **) &u);
+ }
+
+ /* smtp-server */
+ GetDlgItemText(hDlg, IDC_CFV_SMTPSERVER, tcbuf, TCBUFLEN);
+ tcbuf[TCBUFLEN] = '\0';
+ u = lptstr_to_utf8(tcbuf);
+ if(u){
+ removing_leading_and_trailing_white_space(u);
+ if(dlgcv->ivars->smtpserver)
+ fs_give((void **)&dlgcv->ivars->smtpserver);
+
+ if(*u){
+ dlgcv->ivars->smtpserver = u;
+ u = NULL;
+ }
+
+ if(u)
+ fs_give((void **) &u);
+ }
+
+ dlgcv->ivars->defmailclient =
+ (IsDlgButtonChecked(hDlg, IDC_CFV_DEFMAILER)
+ == BST_CHECKED);
+ dlgcv->ivars->defnewsclient =
+ (IsDlgButtonChecked(hDlg, IDC_CFV_DEFNEWSRDR)
+ == BST_CHECKED);
+
+ EndDialog (hDlg, LOWORD(wParam));
+ dlgcv->rv = 0;
+ ret = TRUE;
+ break;
+
+ case IDCANCEL:
+ dlgcv->rv = 1;
+ ret = TRUE;
+ EndDialog (hDlg, LOWORD(wParam));
+ break;
+ }
+ break;
+
+
+ }
+ return (ret);
+}
+
+/*
+ * Prompt for config location
+ */
+
+int
+os_config_dialog (char *utf8_buf, int utf8_buflen,
+ int *regset, int nopinerc)
+{
+ DLGPROC dlgprc;
+ HINSTANCE hInst;
+ HWND hWnd;
+ DLG_CONFIGDATA dlgcfg;
+
+ mswin_killsplash();
+ hInst = (HINSTANCE) mswin_gethinstance ();
+ hWnd = (HWND) mswin_gethwnd ();
+
+ dlgcfg.confpath = utf8_buf;
+ dlgcfg.confpathlen = utf8_buflen;
+ dlgcfg.setreg = 0;
+ dlgcfg.nopinerc = nopinerc;
+ dlgcfg.rv = 0;
+
+ dlgprc = config_dialog_proc;
+
+ DialogBoxParam (hInst, MAKEINTRESOURCE (IDD_CONFIGDLG), NULL,
+ dlgprc, (LPARAM)&dlgcfg);
+
+ *regset = dlgcfg.setreg;
+ return(dlgcfg.rv);
+}
+
+
+/*
+ * Dialog proc to handle login
+ *
+ * Configures the dialog box on init and retrieves the settings on exit.
+ */
+BOOL CALLBACK __export
+config_dialog_proc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ DLG_CONFIGDATA *dlgcfg;
+ BOOL ret = FALSE;
+ int checked, def_rem_fldr = 1, def_diff_fldr = 0;
+
+ switch (uMsg) {
+ case WM_INITDIALOG:
+
+ dlgcfg = (DLG_CONFIGDATA *)lParam;
+ SetWindowLong (hDlg, WINDOW_USER_DATA, (LONG) dlgcfg);
+ if(ps_global->install_flag)
+ SetDlgItemText(hDlg, IDC_CONFTEXT, TEXT("Please specify where Alpine should create (or look for) your personal configuration file."));
+ SetDlgItemText(hDlg, IDC_CONFEFLDRNAME, TEXT("remote_pinerc"));
+ if(*dlgcfg->confpath){
+ if(dlgcfg->confpath[0] == '{'){
+ LPTSTR tfldr, p, tfldr2, p2, user;
+
+ tfldr = utf8_to_lptstr((LPSTR)(dlgcfg->confpath+1));
+ if(p = _tcschr(tfldr, '}')){
+ *p++ = '\0';
+ if(_tcscmp(TEXT("remote_pinerc"), p)){
+ SetDlgItemText(hDlg, IDC_CONFEFLDRNAME, p);
+ def_diff_fldr = 1;
+ }
+
+ tfldr2 = _tcsdup(tfldr);
+ for(p = tfldr, p2 = tfldr2; *p; p++)
+ if(*p == '/' && !_tcsnicmp(p, TEXT("/user="), 6)){
+ p += 6;
+ for(user = p; *p && *p != '/'; p++);
+ if(*p){
+ *p2++ = *p;
+ *p = '\0';
+ }
+ else
+ p--;
+ SetDlgItemText(hDlg, IDC_CONFEUSERNAME, user);
+ }
+ else
+ *p2++ = *p;
+ *p2 = '\0';
+ SetDlgItemText(hDlg, IDC_CONFESERVER, tfldr2);
+ if(tfldr2)
+ MemFree((void *)tfldr2);
+ }
+
+ if(tfldr)
+ fs_give((void **) &tfldr);
+ }
+ else{
+ LPTSTR local_file;
+
+ local_file = utf8_to_lptstr((LPSTR)dlgcfg->confpath);
+ SetDlgItemText(hDlg, IDC_CONFEFN, local_file);
+ if(!dlgcfg->nopinerc)
+ def_rem_fldr = 0;
+
+ fs_give((void **) &local_file);
+ }
+ }
+ CheckRadioButton (hDlg, IDC_CONFRRADIO, IDC_CONFLRADIO,
+ def_rem_fldr ? IDC_CONFRRADIO : IDC_CONFLRADIO);
+ if(def_rem_fldr){
+ EnableWindow(GetDlgItem(hDlg, IDC_CONFFNTXT), 0);
+ EnableWindow(GetDlgItem(hDlg, IDC_CONFEFN), 0);
+ EnableWindow(GetDlgItem(hDlg, IDC_CONFBROWSE), 0);
+ if(!def_diff_fldr){
+ CheckDlgButton(hDlg, IDC_CONFDFLTFLDR, BST_CHECKED);
+ EnableWindow(GetDlgItem(hDlg, IDC_CONFEFLDRNAME), 0);
+ }
+ EnableWindow(GetDlgItem(hDlg, IDC_CONFFLDRTXT), 0);
+ }
+ else {
+ EnableWindow(GetDlgItem(hDlg, IDC_CONFSRVRTXT), 0);
+ EnableWindow(GetDlgItem(hDlg, IDC_CONFESERVER), 0);
+ EnableWindow(GetDlgItem(hDlg, IDC_CONFUNTXT), 0);
+ EnableWindow(GetDlgItem(hDlg, IDC_CONFEUSERNAME), 0);
+ CheckDlgButton(hDlg, IDC_CONFDFLTFLDR, BST_CHECKED);
+ EnableWindow(GetDlgItem(hDlg, IDC_CONFDFLTFLDR), 0);
+ EnableWindow(GetDlgItem(hDlg, IDC_CONFFLDRTXT), 0);
+ EnableWindow(GetDlgItem(hDlg, IDC_CONFEFLDRNAME), 0);
+ }
+ if(ps_global->install_flag)
+ CheckDlgButton(hDlg, IDC_CONFDFLTSET, BST_CHECKED);
+
+ return (1);
+
+
+ case WM_COMMAND:
+ dlgcfg = (DLG_CONFIGDATA *) GetWindowLong (hDlg, WINDOW_USER_DATA);
+ switch (wParam) {
+ case IDC_CONFDFLTFLDR:
+ checked = (IsDlgButtonChecked(hDlg, IDC_CONFDFLTFLDR) == BST_CHECKED);
+ EnableWindow(GetDlgItem(hDlg, IDC_CONFFLDRTXT), checked ? 0 : 1);
+ EnableWindow(GetDlgItem(hDlg, IDC_CONFEFLDRNAME), checked ? 0 : 1);
+ if(checked)
+ SetDlgItemText(hDlg, IDC_CONFEFLDRNAME, TEXT("remote_pinerc"));
+ break;
+ case IDC_CONFRRADIO:
+ case IDC_CONFLRADIO:
+ CheckRadioButton (hDlg, IDC_CONFRRADIO, IDC_CONFLRADIO, wParam);
+ EnableWindow(GetDlgItem(hDlg, IDC_CONFFNTXT), wParam == IDC_CONFRRADIO ? 0 : 1);
+ EnableWindow(GetDlgItem(hDlg, IDC_CONFEFN), wParam == IDC_CONFRRADIO ? 0 : 1);
+ EnableWindow(GetDlgItem(hDlg, IDC_CONFBROWSE), wParam == IDC_CONFRRADIO ? 0 : 1);
+ EnableWindow(GetDlgItem(hDlg, IDC_CONFSRVRTXT), wParam == IDC_CONFRRADIO ? 1 : 0);
+ EnableWindow(GetDlgItem(hDlg, IDC_CONFESERVER), wParam == IDC_CONFRRADIO ? 1 : 0);
+ EnableWindow(GetDlgItem(hDlg, IDC_CONFUNTXT), wParam == IDC_CONFRRADIO ? 1 : 0);
+ EnableWindow(GetDlgItem(hDlg, IDC_CONFEUSERNAME), wParam == IDC_CONFRRADIO ? 1 : 0);
+ EnableWindow(GetDlgItem(hDlg, IDC_CONFDFLTFLDR), wParam == IDC_CONFRRADIO ? 1 : 0);
+ if(wParam == IDC_CONFRRADIO && IsDlgButtonChecked(hDlg, IDC_CONFDFLTFLDR) == BST_UNCHECKED){
+ EnableWindow(GetDlgItem(hDlg, IDC_CONFFLDRTXT), 1);
+ EnableWindow(GetDlgItem(hDlg, IDC_CONFEFLDRNAME), 1);
+ }
+ else if(wParam == IDC_CONFLRADIO){
+ EnableWindow(GetDlgItem(hDlg, IDC_CONFFLDRTXT), 0);
+ EnableWindow(GetDlgItem(hDlg, IDC_CONFEFLDRNAME), 0);
+ }
+ break;
+ case IDC_CONFBROWSE:
+ if(1){
+ TCHAR fn[MAXPATH+1];
+ OPENFILENAME ofn;
+
+ fn[0] = '\0';
+
+ /* Set up the BIG STRUCTURE. see mswin.c */
+ memset (&ofn, 0, sizeof(ofn));
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ ofn.hwndOwner = hDlg;
+ ofn.lpstrFilter = NULL;
+ ofn.lpstrCustomFilter = NULL;
+ ofn.nFilterIndex = 0;
+ ofn.lpstrFile = fn;
+ ofn.nMaxFile = MAXPATH;
+ ofn.lpstrFileTitle = NULL;
+ ofn.nMaxFileTitle = 0;
+ ofn.lpstrInitialDir = NULL;
+ ofn.lpstrTitle = TEXT("Select File");
+ ofn.Flags = OFN_HIDEREADONLY | OFN_PATHMUSTEXIST;
+ ofn.lpstrDefExt = NULL;
+
+ if (GetOpenFileName (&ofn)) {
+ SetDlgItemText(hDlg, IDC_CONFEFN, fn);
+ }
+ }
+ break;
+ case IDOK:
+ /* Retrieve the new username/passwd. */
+ if(IsDlgButtonChecked(hDlg, IDC_CONFDFLTSET) == BST_CHECKED)
+ dlgcfg->setreg = 1;
+ else
+ dlgcfg->setreg = 0;
+ if(IsDlgButtonChecked(hDlg, IDC_CONFRRADIO) == BST_CHECKED){
+ TCHAR lptstr_buf[MAXPATH+1];
+ char *utf8_srvr, *utf8_username, *utf8_fldrname;
+
+ GetDlgItemText(hDlg, IDC_CONFESERVER, lptstr_buf, MAXPATH);
+ lptstr_buf[MAXPATH] = '\0';
+ utf8_srvr = lptstr_to_utf8(lptstr_buf);
+ removing_leading_and_trailing_white_space(utf8_srvr);
+ if(!*utf8_srvr){
+ MessageBox(hDlg, TEXT("IMAP Server field empty"),
+ TEXT("Alpine"), MB_ICONWARNING | MB_OK);
+ if(utf8_srvr)
+ fs_give((void **) &utf8_srvr);
+
+ return(TRUE);
+ }
+
+ GetDlgItemText(hDlg, IDC_CONFEUSERNAME, lptstr_buf, MAXPATH);
+ lptstr_buf[MAXPATH] = '\0';
+ utf8_username = lptstr_to_utf8(lptstr_buf);
+ removing_leading_and_trailing_white_space(utf8_username);
+ if(IsDlgButtonChecked(hDlg, IDC_CONFDFLTFLDR) == BST_CHECKED){
+ utf8_fldrname = cpystr("remote_pinerc");
+ }
+ else{
+ GetDlgItemText(hDlg, IDC_CONFEFLDRNAME, lptstr_buf, MAXPATH);
+ lptstr_buf[MAXPATH] = '\0';
+ utf8_fldrname = lptstr_to_utf8(lptstr_buf);
+ removing_leading_and_trailing_white_space(utf8_fldrname);
+ if(!*utf8_fldrname){
+ MessageBox(hDlg, TEXT("Configuration Folder Name field empty"),
+ TEXT("Alpine"), MB_ICONWARNING | MB_OK);
+ if(utf8_srvr)
+ fs_give((void **) &utf8_srvr);
+
+ if(utf8_username)
+ fs_give((void **) &utf8_username);
+
+ if(utf8_fldrname)
+ fs_give((void **) &utf8_fldrname);
+
+ return(TRUE);
+ }
+ }
+ if((strlen(utf8_srvr)
+ + strlen(utf8_username)
+ + strlen(utf8_fldrname)
+ + 11) > dlgcfg->confpathlen){
+ MessageBox(hDlg, TEXT("Config path too long"),
+ TEXT("Alpine"), MB_ICONWARNING | MB_OK);
+ if(utf8_srvr)
+ fs_give((void **) &utf8_srvr);
+
+ if(utf8_username)
+ fs_give((void **) &utf8_username);
+
+ if(utf8_fldrname)
+ fs_give((void **) &utf8_fldrname);
+
+ return(TRUE);
+ }
+
+ snprintf(dlgcfg->confpath, dlgcfg->confpathlen, "{%s%s%s}%s", utf8_srvr,
+ *utf8_username ? "/user=" : "",
+ utf8_username, utf8_fldrname);
+ if(utf8_srvr)
+ fs_give((void **) &utf8_srvr);
+
+ if(utf8_username)
+ fs_give((void **) &utf8_username);
+
+ if(utf8_fldrname)
+ fs_give((void **) &utf8_fldrname);
+ }
+ else{
+ TCHAR lptstr_fn[MAXPATH+1];
+ char *utf8_fn;
+
+ GetDlgItemText(hDlg, IDC_CONFEFN, lptstr_fn, MAXPATH);
+ lptstr_fn[MAXPATH] = '\0';
+ utf8_fn = lptstr_to_utf8(lptstr_fn);
+ removing_leading_and_trailing_white_space(utf8_fn);
+ if(!*utf8_fn){
+ MessageBox(hDlg, TEXT("Configuration File Name field empty"),
+ TEXT("Alpine"), MB_ICONWARNING | MB_OK);
+ if(utf8_fn)
+ fs_give((void **) &utf8_fn);
+
+ return(TRUE);
+ }
+
+ if(strlen(utf8_fn) >= dlgcfg->confpathlen){
+ MessageBox(hDlg, TEXT("Config path too long"),
+ TEXT("Alpine"), MB_ICONWARNING | MB_OK);
+ if(utf8_fn)
+ fs_give((void **) &utf8_fn);
+
+ return(TRUE);
+ }
+
+ strncpy(dlgcfg->confpath, utf8_fn, dlgcfg->confpathlen);
+ dlgcfg->confpath[dlgcfg->confpathlen-1] = '\0';
+ if(utf8_fn)
+ fs_give((void **) &utf8_fn);
+ }
+
+ EndDialog (hDlg, LOWORD(wParam));
+ dlgcfg->rv = 0;
+ ret = TRUE;
+ break;
+
+ case IDCANCEL:
+ dlgcfg->rv = 1;
+ ret = TRUE;
+ EndDialog (hDlg, LOWORD(wParam));
+ break;
+ }
+ break;
+
+
+ }
+ return (ret);
+}
+
+/*
+ * Get a button position in the parent's coordinate space.
+ */
+static void
+GetBtnPos (HWND hPrnt, HWND hWnd, RECT *r)
+{
+ GetWindowRect (hWnd, r);
+ ScreenToClient (hPrnt, (POINT *) r);
+ ScreenToClient (hPrnt, (POINT *) &r->right);
+}
diff --git a/alpine/osdep/termout.wnt.h b/alpine/osdep/termout.wnt.h
new file mode 100644
index 00000000..35ccbb77
--- /dev/null
+++ b/alpine/osdep/termout.wnt.h
@@ -0,0 +1,47 @@
+/*
+ * $Id: termout.unx.h 159 2006-10-02 22:00:13Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_OSDEP_TERMOUT_WNT_INCLUDED
+#define PINE_OSDEP_TERMOUT_WNT_INCLUDED
+
+#include "resource.h"
+
+typedef struct DLG_SORTPARAM {
+ int rval; /* Return value. */
+ int reverse; /* Indicates a reversed sort. */
+ int cursort; /* Current sort (pineid). */
+ char **helptext; /* Pointer to help text. */
+ int sortcount; /* Number of different sorts. */
+ struct sorttypemap *types; /* Pointer to array of conversion between
+ * the pine sort types and the radio button
+ * ids. */
+} DLG_SORTPARAM;
+
+
+/* exported prototypes */
+
+void scroll_setpos(long);
+void scroll_setrange(long, long);
+
+/* dialog stuff */
+int init_install_get_vars(void);
+int os_argsdialog(char **);
+int os_login_dialog(NETMBX *, char *, int, char *, int, int, int, int *);
+int os_flagmsgdialog(struct flag_table *);
+int os_sortdialog(DLG_SORTPARAM *);
+int os_config_dialog(char *, int, int *, int);
+
+#endif /* PINE_OSDEP_TERMOUT_WNT_INCLUDED */
diff --git a/alpine/osdep/windlg.h b/alpine/osdep/windlg.h
new file mode 100644
index 00000000..7730ae88
--- /dev/null
+++ b/alpine/osdep/windlg.h
@@ -0,0 +1,10 @@
+
+
+/*----------------------------------------------------------------------------
+
+ Common includes for custom dialogs.
+
+ ( This file included by os-win.h and os-wnt.h)
+
+*/
+
diff --git a/alpine/pattern.c b/alpine/pattern.c
new file mode 100644
index 00000000..1e497746
--- /dev/null
+++ b/alpine/pattern.c
@@ -0,0 +1,223 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: pattern.c 169 2006-10-04 23:26:48Z hubert@u.washington.edu $";
+#endif
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "../c-client/c-client.h"
+
+#include "../pith/osdep/canaccess.h"
+#include "../pith/osdep/color.h"
+#include "../pith/osdep/pipe.h"
+
+#include "../pith/charconv/utf8.h"
+#include "../pith/charconv/filesys.h"
+
+#include "../pith/status.h"
+#include "../pith/pipe.h"
+#include "../pith/debug.h"
+#include "../pith/detach.h"
+
+#include "pipe.h"
+#include "pattern.h"
+#include "signal.h"
+
+
+/* Internal prototypes */
+int test_message_with_cmd(MAILSTREAM *, long, char *, long, int *);
+
+
+/*
+ * pattern_filter_command - filter given message
+ */
+void
+pattern_filter_command(char **cat_cmds, SEARCHSET *srchset, MAILSTREAM *stream, long cat_lim, INTVL_S *cat)
+{
+ char **l;
+ int exitval, i;
+ SEARCHSET *s;
+ MESSAGECACHE *mc;
+ char *cmd = NULL;
+ char *just_arg0 = NULL;
+ char *cmd_start, *cmd_end;
+ int just_one;
+
+ if(!(cat_cmds && cat_cmds[0]))
+ return;
+
+ just_one = !(cat_cmds[1]);
+
+ /* find the first command that exists on this host */
+ for(l = cat_cmds; l && *l; l++){
+ cmd = cpystr(*l);
+ removing_quotes(cmd);
+ if(cmd){
+ for(cmd_start = cmd;
+ *cmd_start && isspace(*cmd_start); cmd_start++)
+ ;
+
+ for(cmd_end = cmd_start+1;
+ *cmd_end && !isspace(*cmd_end); cmd_end++)
+ ;
+
+ just_arg0 = (char *) fs_get((cmd_end-cmd_start+1)
+ * sizeof(char));
+ strncpy(just_arg0, cmd_start, cmd_end - cmd_start);
+ just_arg0[cmd_end - cmd_start] = '\0';
+ }
+
+ if(valid_filter_command(&just_arg0))
+ break;
+ else{
+ if(just_one){
+ if(can_access(just_arg0, ACCESS_EXISTS) != 0)
+ q_status_message1(SM_ORDER, 0, 3,
+ "\"%s\" does not exist",
+ just_arg0);
+ else
+ q_status_message1(SM_ORDER, 0, 3,
+ "\"%s\" is not executable",
+ just_arg0);
+ }
+
+ if(just_arg0)
+ fs_give((void **) &just_arg0);
+ if(cmd)
+ fs_give((void **) &cmd);
+ }
+ }
+
+ if(!just_arg0 && !just_one)
+ q_status_message(SM_ORDER, 0, 3,
+ "None of the category cmds exists and is executable");
+
+ /*
+ * If category_cmd isn't executable, it isn't a match.
+ */
+ if(!just_arg0 || !cmd){
+ /*
+ * If we couldn't run the pipe command,
+ * we declare no match
+ */
+ for(s = srchset; s; s = s->next)
+ for(i = s->first; i <= s->last; i++)
+ if(i > 0L && stream && i <= stream->nmsgs
+ && (mc=mail_elt(stream, i)) && mc->searched)
+ mc->searched = NIL;
+ }
+ else
+ for(s = srchset; s; s = s->next)
+ for(i = s->first; i <= s->last; i++)
+ if(i > 0L && stream && i <= stream->nmsgs
+ && (mc=mail_elt(stream, i)) && mc->searched){
+
+ /*
+ * If there was an error, or the exitval is out of
+ * range, then it is not a match.
+ * Default range is (0,0),
+ * which is right for matching
+ * bogofilter spam.
+ */
+ if(test_message_with_cmd(stream, i, cmd,
+ cat_lim, &exitval) != 0)
+ mc->searched = NIL;
+
+ /* test exitval */
+ if(mc->searched){
+ INTVL_S *iv;
+
+ if(cat){
+ for(iv = cat; iv; iv = iv->next)
+ if((long) exitval >= iv->imin
+ && (long) exitval <= iv->imax)
+ break;
+
+ if(!iv)
+ mc->searched = NIL; /* not in any interval */
+ }
+ else{
+ /* default to interval containing only zero */
+ if(exitval != 0)
+ mc->searched = NIL;
+ }
+ }
+ }
+
+ if(just_arg0)
+ fs_give((void **) &just_arg0);
+
+ if(cmd)
+ fs_give((void **) &cmd);
+}
+
+
+
+/*
+ * Returns 0 if ok, -1 if not ok.
+ * If ok then exitval contains the exit value of the cmd.
+ */
+int
+test_message_with_cmd(MAILSTREAM *stream, long int msgno, char *cmd,
+ long char_limit, /* limit testing to this many chars from body */
+ int *exitval)
+{
+ PIPE_S *tpipe;
+ gf_io_t pc;
+ int status = 0, flags, err = 0;
+ char *resultfile = NULL, *pipe_err;
+
+ if(cmd && cmd[0]){
+
+ flags = PIPE_WRITE | PIPE_NOSHELL | PIPE_STDERR | PIPE_NONEWMAIL;
+
+ dprint((7, "test_message_with_cmd(msgno=%ld cmd=%s)\n",
+ msgno, cmd));
+
+ if((tpipe = cmd_pipe_open(cmd, &resultfile, flags, &pc))){
+
+ prime_raw_pipe_getc(stream, msgno, char_limit, FT_PEEK);
+ gf_filter_init();
+ gf_link_filter(gf_nvtnl_local, NULL);
+ if((pipe_err = gf_pipe(raw_pipe_getc, pc)) != NULL){
+ q_status_message1(SM_ORDER|SM_DING, 3, 3,
+ "Internal Error: %.200s", pipe_err);
+ err++;
+ }
+
+ /*
+ * Don't call new_mail in close_system_pipe because we're probably
+ * already here from new_mail and we don't want to get loopy.
+ */
+ status = close_system_pipe(&tpipe, exitval, pipe_callback);
+
+ /*
+ * This is a place where the command can put its output, which we
+ * are not interested in.
+ */
+ if(resultfile){
+ our_unlink(resultfile);
+ fs_give((void **) &resultfile);
+ }
+
+ return((err || status) ? -1 : 0);
+ }
+ }
+
+ return(-1);
+}
+
+
diff --git a/alpine/pattern.h b/alpine/pattern.h
new file mode 100644
index 00000000..5c71747f
--- /dev/null
+++ b/alpine/pattern.h
@@ -0,0 +1,25 @@
+/*
+ * $Id: pattern.h 137 2006-09-22 21:34:06Z mikes@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef ALPINE_PATTERN_INCLUDED
+#define ALPINE_PATTERN_INCLUDED
+
+#include "../pith/pattern.h"
+
+/* exported protoypes */
+void pattern_filter_command(char **, SEARCHSET *, MAILSTREAM *, long, INTVL_S *);
+
+
+#endif /* ALPINE_PATTERN_INCLUDED */
diff --git a/alpine/pine-use.c b/alpine/pine-use.c
new file mode 100644
index 00000000..dd7fc8de
--- /dev/null
+++ b/alpine/pine-use.c
@@ -0,0 +1,180 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: pine-use.c 761 2007-10-23 22:35:18Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <stdio.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifndef MAILSPOOLPCTS
+#define MAILSPOOLPCTS "/usr/spool/mail/%s"
+/* #define MAILSPOOLPCTS "/usr/mail/%s" */
+#endif
+
+#define DAYSEC (60*60*24)
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ struct passwd *pw;
+ char filename[100], buf[100], *p;
+ struct stat statb;
+ long now, inbox_mess, inboxes, inbox_mess_max;
+ int core_files, c, core_id, count, sig_files;
+ int user_count[6], so_far;
+ FILE *f, *core;
+
+ user_count[0] = 0; /* Last week */
+ user_count[1] = 0; /* Last 2 weeks */
+ user_count[2] = 0; /* Last month */
+ user_count[3] = 0; /* Last year */
+ user_count[4] = 0; /* Ever */
+ sig_files = 0;
+ core_files = 0;
+ inboxes = 0;
+ inbox_mess = 0;
+ inbox_mess_max = 0;
+
+ now = time(0);
+ core = NULL;
+
+ if(argc > 1) {
+ core_id = atoi(argv[1]);
+ if(core_id == 0){
+ fprintf(stderr, "Bogus core starting number\n");
+ exit(-1);
+ } else {
+ printf("Core collect starting at %d\n", core_id);
+ core = fopen("pine-core-collect.sh", "w");
+ }
+ }
+
+ so_far = 0;
+ while((pw = getpwent()) != NULL) {
+ so_far++;
+ if((so_far % 200) == 0) {
+ printf("%5d users processed so far\n", so_far);
+ }
+
+ if(strcmp(pw->pw_dir, "/") == 0)
+ continue;
+
+ sprintf(filename, "%s/.pinerc", pw->pw_dir);
+ if(stat(filename, &statb) < 0)
+ continue;
+ if(statb.st_mtime + 7 * DAYSEC > now)
+ user_count[0]++;
+ else if(statb.st_mtime + 14 * DAYSEC > now)
+ user_count[1]++;
+ else if(statb.st_mtime + 30 * DAYSEC > now)
+ user_count[2]++;
+ else if(statb.st_mtime + 365 * DAYSEC > now)
+ user_count[3]++;
+ else
+ user_count[4]++;
+
+
+ if(statb.st_mtime + 30 * DAYSEC >= now) {
+ count = mail_file_size(pw->pw_name);
+ if(count >= 0){
+ inboxes++;
+ inbox_mess += count;
+ inbox_mess_max = inbox_mess_max > count ? inbox_mess_max:count;
+ }
+ }
+
+ sprintf(filename, "%s/.signature", pw->pw_dir);
+ if(access(filename, 0) == 0)
+ sig_files++;
+
+ sprintf(filename, "%s/core", pw->pw_dir);
+ if((f = fopen(filename, "r")) != NULL) {
+ fflush(stdout);
+ while((c = getc(f)) != EOF) {
+ if(c == 'P'){
+ p = buf;
+ *p++ = c;
+ while((c = getc(f)) != EOF) {
+ *p++ = c;
+ if(p > &buf[50]) {
+ break;
+ }
+ if(c == ')') {
+ break;
+ }
+ }
+ *p = '\0';
+ if(c == EOF)
+ break;
+ if(strcmp(&buf[strlen(buf) - 13], "(olivebranch)") == 0) {
+ printf("%s\t%s\n", filename, buf + 14);
+ core_files++;
+ if(core != NULL) {
+ fprintf(core, "mv %s core%d.%s\n", filename,
+ core_id++,pw->pw_name);
+ }
+ break;
+ }
+ }
+ }
+ fclose(f);
+ } else {
+/* printf("%s\n", pw->pw_name); */
+ }
+ }
+
+
+ printf("%5d: last week\n", user_count[0]);
+ printf("%5d: last two weeks (+%d)\n", user_count[1] + user_count[0],
+ user_count[1]);
+ printf("%5d: last month (+%d)\n", user_count[2] + user_count[1] + user_count[0], user_count[2]);
+ printf("%5d: last year\n", user_count[3]);
+ printf("%5d: more than a year\n", user_count[4]);
+ printf("%5d: core files\n", core_files);
+ printf("%5d: Average messages in inbox (%ld/%d)\n",
+ inbox_mess/inboxes, inbox_mess, inboxes);
+ printf("%5d: Largest inbox in messages\n", inbox_mess_max);
+ printf("%5d: Total users checked\n", so_far);
+ printf("%5d: signature files\n", sig_files);
+}
+
+
+mail_file_size(user)
+ char *user;
+{
+ int count = 0;
+ FILE *f;
+ char buf[20480];
+
+ sprintf(buf, MAILSPOOLPCTS, user);
+
+ f = fopen(buf, "r");
+ if(f == NULL)
+ return(-1);
+
+ while(fgets(buf, sizeof(buf), f) != NULL) {
+ if(strncmp(buf, "From ", 5) == 0)
+ count++;
+ }
+ fclose(f);
+/* printf("%s %d\n", user, count); */
+ return(count);
+}
+
+
+
diff --git a/alpine/pine-win.lnk b/alpine/pine-win.lnk
new file mode 100644
index 00000000..387aef1f
--- /dev/null
+++ b/alpine/pine-win.lnk
@@ -0,0 +1,11 @@
+mswinver+addrbook+adrbklib+args+filter+folder+
+context+help+imap+init+mailcmd+mailindx+mailpart+
+mailview+newmail+os+other+pine+reply+screen+
+send+signals+status+strings+ttyin+ttyout+mailcap+
+bdate
+pine
+pine
+..\c-client\cclient+..\pico\libpico+
+winsock+libw+shell+toolhelp+llibcew+oldnames+commdlg+
+..\spell\spell
+osdep\mswin.def
diff --git a/alpine/pipe.c b/alpine/pipe.c
new file mode 100644
index 00000000..033d67b9
--- /dev/null
+++ b/alpine/pipe.c
@@ -0,0 +1,150 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: pipe.c 155 2006-09-29 23:28:46Z hubert@u.washington.edu $";
+#endif
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#include "../pith/headers.h"
+#include "../pith/pipe.h"
+#include "../pith/stream.h"
+#include "../pith/status.h"
+
+#include "signal.h"
+#include "pipe.h"
+
+
+/*
+ * Support structure and functions to support piping raw message texts...
+ */
+static struct raw_pipe_data {
+ MAILSTREAM *stream;
+ unsigned long msgno, len;
+ long char_limit, flags;
+ char *cur, *body;
+} raw_pipe;
+
+
+int
+raw_pipe_getc(unsigned char *c)
+{
+ static char *free_this = NULL;
+
+ /*
+ * What is this if doing?
+ *
+ * If((just_starting && unsuccessful_fetch_header)
+ * || (no_chars_available && haven't_fetched_body
+ * && (not_supposed_to_fetch
+ * || (supposed_to_fetch_all && unsuccessful_fetch_text)
+ * || (supposed_to_partial_fetch && unsuccessful_partial_fetch))
+ * || (no_chars_available))
+ * return(0);
+ *
+ * otherwise, fall through and return next character
+ */
+ if((!raw_pipe.cur
+ && !(raw_pipe.cur = mail_fetch_header(raw_pipe.stream, raw_pipe.msgno,
+ NULL, NULL,
+ &raw_pipe.len,
+ raw_pipe.flags)))
+ || ((raw_pipe.len <= 0L) && !raw_pipe.body
+ && (raw_pipe.char_limit == 0L
+ || (raw_pipe.char_limit < 0L
+ && !(raw_pipe.cur = raw_pipe.body =
+ pine_mail_fetch_text(raw_pipe.stream,
+ raw_pipe.msgno,
+ NULL,
+ &raw_pipe.len,
+ raw_pipe.flags)))
+ || (raw_pipe.char_limit > 0L
+ && !(raw_pipe.cur = raw_pipe.body =
+ pine_mail_partial_fetch_wrapper(raw_pipe.stream,
+ raw_pipe.msgno,
+ NULL,
+ &raw_pipe.len,
+ raw_pipe.flags,
+ (unsigned long) raw_pipe.char_limit,
+ &free_this, 1)))))
+ || (raw_pipe.len <= 0L)){
+
+ if(free_this)
+ fs_give((void **) &free_this);
+
+ return(0);
+ }
+
+ if(raw_pipe.char_limit > 0L
+ && raw_pipe.body
+ && raw_pipe.len > raw_pipe.char_limit)
+ raw_pipe.len = raw_pipe.char_limit;
+
+ if(raw_pipe.len > 0L){
+ *c = (unsigned char) *raw_pipe.cur++;
+ raw_pipe.len--;
+ return(1);
+ }
+ else
+ return(0);
+
+}
+
+
+/*
+ * Set up for using raw_pipe_getc
+ *
+ * Args: stream
+ * msgno
+ * char_limit Set to -1 means whole thing
+ * 0 means headers only
+ * > 0 means headers plus char_limit body chars
+ * flags -- passed to fetch functions
+ */
+void
+prime_raw_pipe_getc(MAILSTREAM *stream, long int msgno, long int char_limit, long int flags)
+{
+ raw_pipe.stream = stream;
+ raw_pipe.msgno = (unsigned long) msgno;
+ raw_pipe.char_limit = char_limit;
+ raw_pipe.len = 0L;
+ raw_pipe.flags = flags;
+ raw_pipe.cur = NULL;
+ raw_pipe.body = NULL;
+}
+
+
+/*----------------------------------------------------------------------
+ Actually open the pipe used to write piped data down
+
+ Args:
+ Returns: TRUE if success, otherwise FALSE
+
+ ----*/
+PIPE_S *
+cmd_pipe_open(char *cmd, char **result, int flags, gf_io_t *pc)
+{
+ char err[200];
+ PIPE_S *pipe;
+
+ if((pipe = open_system_pipe(cmd, result, NULL, flags, 0,
+ pipe_callback, pipe_report_error)) != NULL)
+ gf_set_writec(pc, pipe, 0L, PipeStar, (flags & PIPE_RAW) ? 0 : WRITE_TO_LOCALE);
+ else{
+ /* TRANSLATORS: The argument is the command name being piped to. */
+ snprintf(err, sizeof(err), _("Error opening pipe: %s"), cmd ? cmd : "?");
+ q_status_message(SM_ORDER | SM_DING, 3, 3, err) ;
+ }
+
+ return(pipe);
+}
diff --git a/alpine/pipe.h b/alpine/pipe.h
new file mode 100644
index 00000000..8c242109
--- /dev/null
+++ b/alpine/pipe.h
@@ -0,0 +1,29 @@
+/*
+ * $Id: pipe.h 136 2006-09-22 20:06:05Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef ALPINE_PIPE_INCLUDED
+#define ALPINE_PIPE_INCLUDED
+
+
+#include "../pith/filter.h"
+#include "../pith/osdep/pipe.h"
+
+
+/* exported protoypes */
+int raw_pipe_getc(unsigned char *);
+void prime_raw_pipe_getc(MAILSTREAM *, long, long, long);
+PIPE_S *cmd_pipe_open(char *, char **, int, gf_io_t *);
+
+#endif /* ALPINE_PIPE_INCLUDED */
diff --git a/alpine/print.c b/alpine/print.c
new file mode 100644
index 00000000..4fa7edc3
--- /dev/null
+++ b/alpine/print.c
@@ -0,0 +1,1288 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: print.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "headers.h"
+#include "print.h"
+#include "confscroll.h"
+#include "keymenu.h"
+#include "radio.h"
+#include "status.h"
+#include "../pith/state.h"
+#include "../pith/mailcmd.h"
+
+
+/*
+ * Internal prototypes
+ */
+void set_def_printer_value(char *);
+int print_select_tool(struct pine *, int, CONF_S **, unsigned);
+int print_edit_tool(struct pine *, int, CONF_S **, unsigned);
+
+
+static char **def_printer_line;
+static char no_ff[] = "-no-formfeed";
+
+
+#ifndef DOS
+
+/*
+ * Information used to paint and maintain a line on the configuration screen
+ */
+/*----------------------------------------------------------------------
+ The printer selection screen
+
+ Draws the screen and prompts for the printer number and the custom
+ command if so selected.
+
+ ----*/
+void
+select_printer(struct pine *ps, int edit_exceptions)
+{
+ struct variable *vtmp;
+ CONF_S *ctmpa = NULL, *ctmpb = NULL, *heading = NULL,
+ *start_line = NULL;
+ PINERC_S *prc = NULL;
+ int i, saved_printer_cat, readonly_warning = 0, no_ex;
+ SAVED_CONFIG_S *vsave;
+ char *saved_printer, **lval;
+ OPT_SCREEN_S screen;
+ size_t l;
+
+ if(edit_exceptions){
+ q_status_message(SM_ORDER, 3, 7,
+ _("Exception Setup not implemented for printer"));
+ return;
+ }
+
+ if(fixed_var(&ps_global->vars[V_PRINTER], "change", "printer"))
+ return;
+
+ ew = edit_exceptions ? ps_global->ew_for_except_vars : Main;
+
+ no_ex = (ps_global->ew_for_except_vars == Main);
+
+ if(ps->restricted)
+ readonly_warning = 1;
+ else{
+ switch(ew){
+ case Main:
+ prc = ps->prc;
+ break;
+ case Post:
+ prc = ps->post_prc;
+ break;
+ default:
+ break;
+ }
+
+ readonly_warning = prc ? prc->readonly : 1;
+ if(prc && prc->quit_to_edit){
+ quit_to_edit_msg(prc);
+ return;
+ }
+ }
+
+ saved_printer = cpystr(ps->VAR_PRINTER);
+ saved_printer_cat = ps->printer_category;
+
+ new_confline(&ctmpa);
+ ctmpa->valoffset = 2;
+ ctmpa->keymenu = &printer_select_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = print_select_tool;
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->value
+#ifdef OS2
+ = cpystr("\"Select\" a port or |pipe-command as your default printer.");
+#else
+ = cpystr(_("You may \"Select\" a print command as your default printer."));
+#endif
+
+ new_confline(&ctmpa);
+ ctmpa->valoffset = 2;
+ ctmpa->keymenu = &printer_select_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = print_select_tool;
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->value
+#ifdef OS2
+ = cpystr("You may also add alternative ports or !pipes to the list in the");
+#else
+ = cpystr(_("You may also add custom print commands to the list in the"));
+#endif
+
+ new_confline(&ctmpa);
+ ctmpa->valoffset = 2;
+ ctmpa->keymenu = &printer_select_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = print_select_tool;
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->value
+#ifdef OS2
+ = cpystr("\"Personally selected port or |pipe\" section below.");
+#else
+ = cpystr(_("\"Personally selected print command\" section below."));
+#endif
+
+ new_confline(&ctmpa);
+ ctmpa->valoffset = 2;
+ ctmpa->keymenu = &printer_select_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = print_select_tool;
+ ctmpa->flags |= CF_NOSELECT | CF_B_LINE;
+
+ new_confline(&ctmpa);
+ ctmpa->valoffset = 4;
+ ctmpa->keymenu = &printer_select_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = print_select_tool;
+ ctmpa->flags |= CF_NOSELECT;
+ def_printer_line = &ctmpa->value;
+ set_def_printer_value(ps->VAR_PRINTER);
+
+ new_confline(&ctmpa);
+ ctmpa->valoffset = 2;
+ ctmpa->keymenu = &printer_select_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = print_select_tool;
+ ctmpa->flags |= CF_NOSELECT | CF_B_LINE;
+
+#ifndef OS2
+ new_confline(&ctmpa);
+ heading = ctmpa;
+ ctmpa->keymenu = &printer_select_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = print_select_tool;
+ ctmpa->varname
+ = cpystr(_(" Printer attached to IBM PC or compatible, Macintosh"));
+ ctmpa->flags |= (CF_NOSELECT | CF_STARTITEM);
+ ctmpa->value = cpystr("");
+ ctmpa->headingp = heading;
+
+ new_confline(&ctmpa);
+ ctmpa->valoffset = 6;
+ ctmpa->keymenu = &printer_select_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = print_select_tool;
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->value
+ = cpystr(_("This may not work with all attached printers, and will depend on the"));
+ ctmpa->headingp = heading;
+
+ new_confline(&ctmpa);
+ ctmpa->valoffset = 6;
+ ctmpa->keymenu = &printer_select_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = print_select_tool;
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->value
+ = cpystr(_("terminal emulation/communications software in use. It is known to work"));
+ ctmpa->headingp = heading;
+
+ new_confline(&ctmpa);
+ ctmpa->valoffset = 6;
+ ctmpa->keymenu = &printer_select_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = print_select_tool;
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->value
+ = cpystr("with Kermit and the latest UW version of NCSA telnet on Macs and PCs,");
+ ctmpa->headingp = heading;
+
+ new_confline(&ctmpa);
+ ctmpa->valoffset = 6;
+ ctmpa->keymenu = &printer_select_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = print_select_tool;
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->value
+ = cpystr("Versaterm Pro on Macs, and WRQ Reflections on PCs.");
+ ctmpa->headingp = heading;
+
+ new_confline(&ctmpa);
+ start_line = ctmpb = ctmpa; /* default start line */
+ ctmpa->valoffset = 17;
+ ctmpa->keymenu = &printer_select_keymenu;
+ ctmpa->help = h_config_set_att_ansi;
+ ctmpa->tool = print_select_tool;
+ ctmpa->flags |= CF_NOHILITE;
+ ctmpa->varoffset = 8;
+ ctmpa->varname = cpystr("Printer:");
+ ctmpa->value = cpystr(ANSI_PRINTER);
+ ctmpa->varnamep = ctmpb;
+ ctmpa->headingp = heading;
+
+ new_confline(&ctmpa);
+ ctmpa->valoffset = 17;
+ ctmpa->keymenu = &printer_select_keymenu;
+ ctmpa->help = h_config_set_att_ansi2;
+ ctmpa->tool = print_select_tool;
+ ctmpa->flags |= CF_NOHILITE;
+ ctmpa->varoffset = 8;
+ l = strlen(ANSI_PRINTER)+strlen(no_ff);
+ ctmpa->value = (char *)fs_get((l+1) * sizeof(char));
+ ctmpa->varnamep = ctmpb;
+ ctmpa->headingp = heading;
+ snprintf(ctmpa->value, l+1, "%s%s", ANSI_PRINTER, no_ff);
+ ctmpa->value[l] = '\0';
+
+ new_confline(&ctmpa);
+ ctmpa->valoffset = 17;
+ ctmpa->keymenu = &printer_select_keymenu;
+ ctmpa->help = h_config_set_att_wyse;
+ ctmpa->tool = print_select_tool;
+ ctmpa->flags |= CF_NOHILITE;
+ ctmpa->varoffset = 8;
+ ctmpa->value = cpystr(WYSE_PRINTER);
+ ctmpa->varnamep = ctmpb;
+ ctmpa->headingp = heading;
+
+ new_confline(&ctmpa);
+ ctmpa->valoffset = 17;
+ ctmpa->keymenu = &printer_select_keymenu;
+ ctmpa->help = h_config_set_att_wyse2;
+ ctmpa->tool = print_select_tool;
+ ctmpa->flags |= CF_NOHILITE;
+ ctmpa->varoffset = 8;
+ l = strlen(WYSE_PRINTER)+strlen(no_ff);
+ ctmpa->value = (char *)fs_get((l+1) * sizeof(char));
+ ctmpa->varnamep = ctmpb;
+ ctmpa->headingp = heading;
+ snprintf(ctmpa->value, l+1, "%s%s", WYSE_PRINTER, no_ff);
+ ctmpa->value[l] = '\0';
+#endif
+
+ new_confline(&ctmpa);
+ ctmpa->valoffset = 0;
+ ctmpa->keymenu = &printer_select_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = print_select_tool;
+ ctmpa->flags |= CF_NOSELECT | CF_B_LINE;
+ ctmpa->var = &ps->vars[V_STANDARD_PRINTER];
+
+
+ new_confline(&ctmpa);
+ heading = ctmpa;
+ ctmpa->valoffset = 6;
+ ctmpa->keymenu = &printer_select_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = print_select_tool;
+ ctmpa->varname
+#ifdef OS2
+ = cpystr(" Standard OS/2 printer port");
+#else
+ = cpystr(_(" Standard UNIX print command"));
+#endif
+ ctmpa->value = cpystr("");
+ ctmpa->flags |= (CF_NOSELECT | CF_STARTITEM);
+ ctmpa->headingp = heading;
+ ctmpa->var = &ps->vars[V_STANDARD_PRINTER];
+
+ new_confline(&ctmpa);
+ ctmpa->valoffset = 6;
+ ctmpa->keymenu = &printer_select_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = print_select_tool;
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->value
+#ifdef OS2
+ = cpystr("Using this option may require you to use the OS/2 \"MODE\" command to");
+#else
+ = cpystr(_("Using this option may require setting your \"PRINTER\" or \"LPDEST\""));
+#endif
+ ctmpa->headingp = heading;
+ ctmpa->var = &ps->vars[V_STANDARD_PRINTER];
+
+ new_confline(&ctmpa);
+ ctmpa->valoffset = 6;
+ ctmpa->keymenu = &printer_select_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = print_select_tool;
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->value
+#ifdef OS2
+ = cpystr("direct printer output to the correct port.");
+#else
+ = cpystr(_("environment variable using the standard UNIX utilities."));
+#endif
+ ctmpa->headingp = heading;
+ ctmpa->var = &ps->vars[V_STANDARD_PRINTER];
+
+ vtmp = &ps->vars[V_STANDARD_PRINTER];
+ for(i = 0; vtmp->current_val.l[i]; i++){
+ new_confline(&ctmpa);
+ ctmpa->valoffset = 22;
+ ctmpa->keymenu = &printer_select_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->help = h_config_set_stand_print;
+ ctmpa->tool = print_select_tool;
+ if(i == 0){
+ ctmpa->varoffset = 8;
+ ctmpa->varname = cpystr(_("Printer List:"));
+ ctmpa->flags |= CF_NOHILITE|CF_PRINTER;
+#ifdef OS2
+ start_line = ctmpb = ctmpa; /* default start line */
+#else
+ ctmpb = ctmpa;
+#endif
+ }
+
+ ctmpa->varnamep = ctmpb;
+ ctmpa->headingp = heading;
+ ctmpa->varmem = i;
+ ctmpa->var = vtmp;
+ ctmpa->value = printer_name(vtmp->current_val.l[i]);
+ }
+
+ new_confline(&ctmpa);
+ ctmpa->valoffset = 0;
+ ctmpa->keymenu = &printer_select_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = print_select_tool;
+ ctmpa->flags |= CF_NOSELECT | CF_B_LINE;
+
+ new_confline(&ctmpa);
+ heading = ctmpa;
+ ctmpa->valoffset = 0;
+ ctmpa->keymenu = &printer_edit_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = print_edit_tool;
+ ctmpa->varname
+#ifdef OS2
+ = cpystr(" Personally selected port or |command");
+#else
+ = cpystr(_(" Personally selected print command"));
+#endif
+ ctmpa->flags |= (CF_NOSELECT | CF_STARTITEM);
+ ctmpa->value = cpystr("");
+ ctmpa->headingp = heading;
+ ctmpa->var = &ps->vars[V_PERSONAL_PRINT_COMMAND];
+
+
+ new_confline(&ctmpa);
+ ctmpa->valoffset = 6;
+ ctmpa->keymenu = &printer_edit_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = print_edit_tool;
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->value
+#ifdef OS2
+ = cpystr("The text to be printed will be sent to the printer or command given here.");
+#else
+ = cpystr(_("The text to be printed will be piped into the command given here. The"));
+#endif
+ ctmpa->headingp = heading;
+ ctmpa->var = &ps->vars[V_PERSONAL_PRINT_COMMAND];
+
+ new_confline(&ctmpa);
+ ctmpa->valoffset = 6;
+ ctmpa->keymenu = &printer_edit_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = print_edit_tool;
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->value
+#ifdef OS2
+ = cpystr("The printer port or |pipe is in the 2nd column, the printer name is in");
+#else
+ = cpystr(_("command is in the 2nd column, the printer name is in the first column. Some"));
+#endif
+ ctmpa->headingp = heading;
+ ctmpa->var = &ps->vars[V_PERSONAL_PRINT_COMMAND];
+
+ new_confline(&ctmpa);
+ ctmpa->valoffset = 6;
+ ctmpa->keymenu = &printer_edit_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = print_edit_tool;
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->value
+#ifdef OS2
+ = cpystr("the first column. Examples: \"LPT1\", \"COM2\", \"|enscript\". A command may");
+#else
+ = cpystr(_("examples are: \"prt\", \"lpr\", \"lp\", or \"enscript\". The command may be given"));
+#endif
+ ctmpa->headingp = heading;
+ ctmpa->var = &ps->vars[V_PERSONAL_PRINT_COMMAND];
+
+ new_confline(&ctmpa);
+ ctmpa->valoffset = 6;
+ ctmpa->keymenu = &printer_edit_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = print_edit_tool;
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->value
+#ifdef OS2
+ = cpystr("be given options, for example \"|ascii2ps -p LPT1\" or \"|txt2hplc -2\". Use");
+#else
+ = cpystr(_("with options, for example \"enscript -2 -r\" or \"lpr -Plpacc170\". The"));
+#endif
+ ctmpa->headingp = heading;
+ ctmpa->var = &ps->vars[V_PERSONAL_PRINT_COMMAND];
+
+ new_confline(&ctmpa);
+ ctmpa->valoffset = 6;
+ ctmpa->keymenu = &printer_edit_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = print_edit_tool;
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->value
+#ifdef OS2
+ = cpystr("the |command method for printers that require conversion from ASCII.");
+#else
+ = cpystr(_("commands and options on your system may be different from these examples."));
+#endif
+ ctmpa->headingp = heading;
+ ctmpa->var = &ps->vars[V_PERSONAL_PRINT_COMMAND];
+
+ vtmp = &ps->vars[V_PERSONAL_PRINT_COMMAND];
+ lval = no_ex ? vtmp->current_val.l : LVAL(vtmp, ew);
+ if(lval){
+ for(i = 0; lval[i]; i++){
+ new_confline(&ctmpa);
+ ctmpa->valoffset = 22;
+ ctmpa->keymenu = &printer_edit_keymenu;
+ ctmpa->help = h_config_set_custom_print;
+ ctmpa->tool = print_edit_tool;
+ if(i == 0){
+ ctmpa->varoffset = 8;
+ ctmpa->varname = cpystr(_("Printer List:"));
+ ctmpa->flags |= CF_NOHILITE|CF_PRINTER;
+ ctmpb = ctmpa;
+ }
+
+ ctmpa->varnamep = ctmpb;
+ ctmpa->headingp = heading;
+ ctmpa->varmem = i;
+ ctmpa->var = vtmp;
+ ctmpa->value = printer_name(lval[i]);
+ }
+ }
+ else{
+ new_confline(&ctmpa);
+ ctmpa->valoffset = 22;
+ ctmpa->keymenu = &printer_edit_keymenu;
+ ctmpa->help = h_config_set_custom_print;
+ ctmpa->tool = print_edit_tool;
+ ctmpa->flags |= CF_NOHILITE;
+ ctmpa->varoffset = 8;
+ ctmpa->varname = cpystr(_("Printer List:"));
+ ctmpa->varnamep = ctmpa;
+ ctmpa->headingp = heading;
+ ctmpa->varmem = 0;
+ ctmpa->var = vtmp;
+ ctmpa->value = cpystr("");
+ }
+
+ memset(&screen, 0, sizeof(screen));
+ screen.ro_warning = readonly_warning;
+ vsave = save_config_vars(ps, 0);
+ /* TRANSLATORS: SETUP... is a screen title
+ Print something1 using something2.
+ "printer config" is something1 */
+ switch(conf_scroll_screen(ps, &screen, start_line,
+ edit_exceptions ? _("SETUP PRINTER EXCEPTIONS")
+ : _("SETUP PRINTER"),
+ _("printer config"), 0)){
+ case 0:
+ break;
+
+ case 1:
+ write_pinerc(ps, ew, WRP_NONE);
+ break;
+
+ case 10:
+ revert_to_saved_config(ps, vsave, 0);
+ ps->printer_category = saved_printer_cat;
+ set_variable(V_PRINTER, saved_printer, 1, 0, ew);
+ set_variable(V_PERSONAL_PRINT_CATEGORY, comatose(ps->printer_category),
+ 1, 0, ew);
+ if(prc)
+ prc->outstanding_pinerc_changes = 0;
+
+ break;
+ }
+
+ def_printer_line = NULL;
+ free_saved_config(ps, &vsave, 0);
+ fs_give((void **)&saved_printer);
+}
+
+#endif /* !DOS */
+
+
+void
+set_def_printer_value(char *printer)
+{
+ char *p, *nick, *cmd;
+ int set, editing_norm_except_exists;
+ size_t l;
+
+ if(!def_printer_line)
+ return;
+
+ editing_norm_except_exists = ((ps_global->ew_for_except_vars != Main) &&
+ (ew == Main));
+
+ parse_printer(printer, &nick, &cmd, NULL, NULL, NULL, NULL);
+ p = *nick ? nick : cmd;
+ set = *p;
+ if(*def_printer_line)
+ fs_give((void **)def_printer_line);
+
+ l = strlen(p) + 60;
+ *def_printer_line = fs_get((l+1) * sizeof(char));
+ snprintf(*def_printer_line, l+1, "Default printer %s%s%s%s%s",
+ set ? "set to \"" : "unset", set ? p : "", set ? "\"" : "",
+ (set && editing_norm_except_exists) ? " (in exception config)" : "",
+ set ? "." : "");
+ (*def_printer_line)[l] = '\0';
+
+ fs_give((void **)&nick);
+ fs_give((void **)&cmd);
+}
+
+
+int
+print_select_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ int rc, retval, no_ex, printer_msg = 0;
+ char *p, **lval, *printer_was;
+ struct variable *vtmp;
+
+ no_ex = (ps_global->ew_for_except_vars == Main);
+
+ printer_was = ps->VAR_PRINTER ? cpystr(ps->VAR_PRINTER) : NULL;
+
+ switch(cmd){
+ case MC_EXIT:
+ retval = config_exit_cmd(flags);
+ break;
+
+ case MC_CHOICE :
+ if(cl && *cl){
+ char aname[100], wname[100];
+
+ strncpy(aname, ANSI_PRINTER, sizeof(aname)-1);
+ aname[sizeof(aname)-1] = '\0';
+ strncat(aname, no_ff, sizeof(aname)-strlen(aname)-1);
+ strncpy(wname, WYSE_PRINTER, sizeof(wname)-1);
+ wname[sizeof(wname)-1] = '\0';
+ strncat(wname, no_ff, sizeof(wname)-strlen(wname)-1);
+ if((*cl)->var){
+ vtmp = (*cl)->var;
+ lval = (no_ex || !vtmp->is_user) ? vtmp->current_val.l
+ : LVAL(vtmp, ew);
+ rc = set_variable(V_PRINTER, lval ? lval[(*cl)->varmem] : NULL,
+ 1, 0, ew);
+ if(rc == 0){
+ if(vtmp == &ps->vars[V_STANDARD_PRINTER])
+ ps->printer_category = 2;
+ else if(vtmp == &ps->vars[V_PERSONAL_PRINT_COMMAND])
+ ps->printer_category = 3;
+
+ set_variable(V_PERSONAL_PRINT_CATEGORY,
+ comatose(ps->printer_category), 1, 0, ew);
+
+ printer_msg++;
+ }
+ else
+ q_status_message(SM_ORDER,3,5, _("Trouble setting default printer"));
+
+ retval = 1;
+ }
+ else if(!strcmp((*cl)->value,ANSI_PRINTER)){
+ rc = set_variable(V_PRINTER, ANSI_PRINTER, 1, 0, ew);
+ if(rc == 0){
+ ps->printer_category = 1;
+ set_variable(V_PERSONAL_PRINT_CATEGORY,
+ comatose(ps->printer_category), 1, 0, ew);
+ printer_msg++;
+ }
+ else
+ q_status_message(SM_ORDER,3,5, _("Trouble setting default printer"));
+
+ retval = 1;
+ }
+ else if(!strcmp((*cl)->value,aname)){
+ rc = set_variable(V_PRINTER, aname, 1, 0, ew);
+ if(rc == 0){
+ ps->printer_category = 1;
+ set_variable(V_PERSONAL_PRINT_CATEGORY,
+ comatose(ps->printer_category), 1, 0, ew);
+ printer_msg++;
+ }
+ else
+ q_status_message(SM_ORDER,3,5, _("Trouble setting default printer"));
+
+ retval = 1;
+ }
+ else if(!strcmp((*cl)->value,WYSE_PRINTER)){
+ rc = set_variable(V_PRINTER, WYSE_PRINTER, 1, 0, ew);
+ if(rc == 0){
+ ps->printer_category = 1;
+ set_variable(V_PERSONAL_PRINT_CATEGORY,
+ comatose(ps->printer_category), 1, 0, ew);
+ printer_msg++;
+ }
+ else
+ q_status_message(SM_ORDER,3,5, _("Trouble setting default printer"));
+
+ retval = 1;
+ }
+ else if(!strcmp((*cl)->value,wname)){
+ rc = set_variable(V_PRINTER, wname, 1, 0, ew);
+ if(rc == 0){
+ ps->printer_category = 1;
+ set_variable(V_PERSONAL_PRINT_CATEGORY,
+ comatose(ps->printer_category), 1, 0, ew);
+ printer_msg++;
+ }
+ else
+ q_status_message(SM_ORDER,3,5, _("Trouble setting default printer"));
+
+ retval = 1;
+ }
+ else
+ retval = 0;
+ }
+ else
+ retval = 0;
+
+ if(retval){
+ ps->mangled_body = 1; /* BUG: redraw it all for now? */
+ set_def_printer_value(ps->VAR_PRINTER);
+ }
+
+ break;
+
+ default:
+ retval = -1;
+ break;
+ }
+
+ if(printer_msg){
+ p = NULL;
+ if(ps->VAR_PRINTER){
+ char *nick, *q;
+
+ parse_printer(ps->VAR_PRINTER, &nick, &q,
+ NULL, NULL, NULL, NULL);
+ p = cpystr(*nick ? nick : q);
+ fs_give((void **)&nick);
+ fs_give((void **)&q);
+ }
+
+ q_status_message4(SM_ORDER, 0, 3,
+ "Default printer%s %s%s%s",
+ ((!printer_was && !ps->VAR_PRINTER) ||
+ (printer_was && ps->VAR_PRINTER &&
+ !strcmp(printer_was,ps->VAR_PRINTER)))
+ ? " still" : "",
+ p ? "set to \"" : "unset",
+ p ? p : "", p ? "\"" : "");
+
+ if(p)
+ fs_give((void **)&p);
+ }
+
+ if(printer_was)
+ fs_give((void **)&printer_was);
+
+ return(retval);
+}
+
+
+int
+print_edit_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ char prompt[81], sval[MAXPATH+1], name[MAXPATH+1];
+ char *nick, *p, *tmp, **newval = NULL, **ltmp = NULL;
+ char **lval, **nelval;
+ int rv = 0, skip_to_next = 0, after = 0, i = 4, j, k = 0;
+ int oeflags, changing_selected = 0, no_ex;
+ HelpType help;
+ ESCKEY_S ekey[6];
+
+ /* need this to preserve old behavior when no exception config file */
+ no_ex = (ps_global->ew_for_except_vars == Main);
+
+ if(cmd == MC_CHOICE)
+ return(print_select_tool(ps, cmd, cl, flags));
+
+ if(!(cl && *cl && (*cl)->var))
+ return(0);
+
+ nelval = no_ex ? (*cl)->var->current_val.l : LVAL((*cl)->var, ew);
+ lval = LVAL((*cl)->var, ew);
+
+ switch(cmd){
+ case MC_ADD: /* add to list */
+ sval[0] = '\0';
+ if(!fixed_var((*cl)->var, "add to", NULL)){
+
+ if(lval && (*cl)->value){
+ strncpy(prompt, _("Enter printer name : "), sizeof(prompt));
+ prompt[sizeof(prompt)-1] = '\0';
+ }
+ else if(!lval && nelval){
+ /* Add to list which doesn't exist, but default does exist */
+ ekey[0].ch = 'r';
+ ekey[0].rval = 'r';
+ ekey[0].name = "R";
+ ekey[0].label = N_("Replace");
+ ekey[1].ch = 'a';
+ ekey[1].rval = 'a';
+ ekey[1].name = "A";
+ ekey[1].label = N_("Add To");
+ ekey[2].ch = -1;
+ strncpy(prompt, _("Replace or Add To default value ? "), sizeof(prompt));
+ prompt[sizeof(prompt)-1] = '\0';
+ switch(i = radio_buttons(prompt, -FOOTER_ROWS(ps), ekey, 'a',
+ 'x', h_config_replace_add, RB_NORM)){
+ case 'a':
+ /* Make a list of the default commands, leaving room for
+ the command we are about to add below. */
+ for(k = 0; nelval[k]; k++)
+ ;
+
+ ltmp = (char **)fs_get((k+2) * sizeof(char *));
+
+ for(j = 0; j < k; j++)
+ ltmp[j] = cpystr(nelval[j]);
+
+ ltmp[k + 1] = ltmp[k] = NULL;
+
+add_text:
+ strncpy(prompt, _("Enter name of printer to be added : "), sizeof(prompt));
+ prompt[sizeof(prompt)-1] = '\0';
+ break;
+
+ case 'r':
+replace_text:
+ strncpy(prompt, _("Enter the name for replacement printer : "), sizeof(prompt));
+ prompt[sizeof(prompt)-1] = '\0';
+ break;
+
+ case 'x':
+ cmd_cancelled("Add");
+ break;
+ }
+
+ if(i == 'x')
+ break;
+ }
+ else{
+ strncpy(prompt, _("Enter name of printer to be added : "), sizeof(prompt));
+ prompt[sizeof(prompt)-1] = '\0';
+ }
+
+ ps->mangled_footer = 1;
+ help = NO_HELP;
+
+ name[0] = '\0';
+ i = 2;
+ while(i != 0 && i != 1){
+ if(lval && (*cl)->value){
+ ekey[0].ch = ctrl('W');
+ ekey[0].rval = 5;
+ ekey[0].name = "^W";
+ ekey[0].label = after ? N_("InsertBefore") : N_("InsertAfter");
+ ekey[1].ch = -1;
+ }
+ else
+ ekey[0].ch = -1;
+
+ oeflags = OE_APPEND_CURRENT;
+ i = optionally_enter(name, -FOOTER_ROWS(ps), 0, sizeof(name),
+ prompt,
+ (ekey[0].ch != -1) ? ekey : NULL,
+ help, &oeflags);
+ if(i == 0){
+ rv = ps->mangled_body = 1;
+ removing_leading_and_trailing_white_space(name);
+ }
+ else if(i == 1){
+ cmd_cancelled("Add");
+ }
+ else if(i == 3){
+ help = (help == NO_HELP) ? h_config_insert_after : NO_HELP;
+ }
+ else if(i == 4){ /* no redraw, yet */
+ }
+ else if(i == 5){ /* change from/to prepend to/from append */
+ after = after ? 0 : 1;
+ }
+ }
+
+ if(i == 0)
+ i = 2;
+
+#ifdef OS2
+ strncpy(prompt, "Enter port or |command : ", sizeof(prompt));
+#else
+ strncpy(prompt, _("Enter command for printer : "), sizeof(prompt));
+#endif
+ prompt[sizeof(prompt)-1] = '\0';
+ while(i != 0 && i != 1){
+ oeflags = OE_APPEND_CURRENT;
+ i = optionally_enter(sval, -FOOTER_ROWS(ps), 0, sizeof(sval),
+ prompt,
+ (ekey[0].ch != -1) ? ekey : NULL,
+ help, &oeflags);
+ if(i == 0){
+ rv = ps->mangled_body = 1;
+ removing_leading_and_trailing_white_space(sval);
+ if(*sval || !lval){
+
+ for(tmp = sval; *tmp; tmp++)
+ if(*tmp == ',')
+ i++; /* conservative count of ,'s */
+
+ if(!i){ /* only one item */
+ if (!ltmp){
+ ltmp = (char **)fs_get(2 * sizeof(char *));
+ ltmp[1] = NULL;
+ k = 0;
+ }
+ if(*name){
+ size_t l;
+
+ l = strlen(name) + 4 + strlen(sval);
+ ltmp[k] = (char *) fs_get((l+1) * sizeof(char));
+ snprintf(ltmp[k], l+1, "%s [] %s", name, sval);
+ ltmp[k][l] = '\0';
+ }
+ else
+ ltmp[k] = cpystr(sval);
+ }
+ else{
+ /*
+ * Don't allow input of multiple entries at once.
+ */
+ q_status_message(SM_ORDER,3,5,
+ _("No commas allowed in command"));
+ i = 2;
+ continue;
+ }
+
+ config_add_list(ps, cl, ltmp, &newval, after);
+
+ if(after)
+ skip_to_next = 1;
+
+ fs_give((void **)&ltmp);
+ k = 0;
+ }
+ else
+ q_status_message1(SM_ORDER, 0, 3,
+ _("Can't add %s to list"), empty_val);
+ }
+ else if(i == 1){
+ cmd_cancelled("Add");
+ }
+ else if(i == 3){
+ help = help == NO_HELP ? h_config_print_cmd : NO_HELP;
+ }
+ else if(i == 4){ /* no redraw, yet */
+ }
+ else if(i == 5){ /* change from/to prepend to/from append */
+ after = after ? 0 : 1;
+ }
+ }
+ }
+
+ break;
+
+ case MC_DELETE: /* delete */
+ if((*cl)->var->current_val.l
+ && (*cl)->var->current_val.l[(*cl)->varmem]
+ && !strucmp(ps->VAR_PRINTER,(*cl)->var->current_val.l[(*cl)->varmem]))
+ changing_selected = 1;
+
+ if(!lval && nelval){
+ char pmt[80];
+
+ snprintf(pmt, sizeof(pmt), _("Override default with %s"), empty_val2);
+ pmt[sizeof(pmt)-1] = '\0';
+ if(want_to(pmt, 'n', 'n', NO_HELP, WT_FLUSH_IN) == 'y'){
+ char **ltmp;
+
+ sval[0] = '\0';
+ ltmp = (char **)fs_get(2 * sizeof(char *));
+ ltmp[0] = cpystr(sval);
+ ltmp[1] = NULL;
+ config_add_list(ps, cl, ltmp, &newval, 0);
+ fs_give((void **)&ltmp);
+ rv = ps->mangled_body = 1;
+ }
+ }
+ else if(!lval){
+ q_status_message(SM_ORDER, 0, 3, _("No set value to delete"));
+ }
+ else{
+ if((*cl)->var->is_fixed){
+ parse_printer(lval[(*cl)->varmem],
+ &nick, &p, NULL, NULL, NULL, NULL);
+ snprintf(prompt, sizeof(prompt), _("Delete (unused) printer %s "),
+ *nick ? nick : (!*p) ? empty_val2 : p);
+ fs_give((void **)&nick);
+ fs_give((void **)&p);
+ }
+ else
+ snprintf(prompt, sizeof(prompt), _("Really delete item %s from printer list "),
+ int2string((*cl)->varmem + 1));
+
+ prompt[sizeof(prompt)-1] = '\0';
+
+ ps->mangled_footer = 1;
+ if(want_to(prompt,'n','n',h_config_print_del, WT_FLUSH_IN) == 'y'){
+ rv = ps->mangled_body = 1;
+ fs_give((void **)&lval[(*cl)->varmem]);
+ config_del_list_item(cl, &newval);
+ }
+ else
+ q_status_message(SM_ORDER, 0, 3, _("Printer not deleted"));
+ }
+
+ break;
+
+ case MC_EDIT: /* edit/change list option */
+ if((*cl)->var->current_val.l
+ && (*cl)->var->current_val.l[(*cl)->varmem]
+ && !strucmp(ps->VAR_PRINTER,(*cl)->var->current_val.l[(*cl)->varmem]))
+ changing_selected = 1;
+
+ if(fixed_var((*cl)->var, NULL, "printer"))
+ break;
+ else if(!lval && nelval)
+ goto replace_text;
+ else if(!lval && !nelval)
+ goto add_text;
+ else{
+ HelpType help;
+
+ ekey[0].ch = 'n';
+ ekey[0].rval = 'n';
+ ekey[0].name = "N";
+ ekey[0].label = N_("Name");
+ ekey[1].ch = 'c';
+ ekey[1].rval = 'c';
+ ekey[1].name = "C";
+ ekey[1].label = N_("Command");
+ ekey[2].ch = 'o';
+ ekey[2].rval = 'o';
+ ekey[2].name = "O";
+ ekey[2].label = N_("Options");
+ ekey[3].ch = -1;
+ /* TRANSLATORS: this is a question with three choices */
+ strncpy(prompt, _("Change Name or Command or Options ? "), sizeof(prompt));
+ prompt[sizeof(prompt)-1] = '\0';
+ i = radio_buttons(prompt, -FOOTER_ROWS(ps), ekey, 'c', 'x',
+ h_config_print_name_cmd, RB_NORM);
+
+ if(i == 'x'){
+ cmd_cancelled("Change");
+ break;
+ }
+ else if(i == 'c'){
+ char *all_but_cmd;
+
+ parse_printer(lval[(*cl)->varmem],
+ NULL, &p, NULL, NULL, NULL, &all_but_cmd);
+
+ strncpy(prompt, _("Change command : "), sizeof(prompt));
+ prompt[sizeof(prompt)-1] = '\0';
+ strncpy(sval, p ? p : "", sizeof(sval)-1);
+ sval[sizeof(sval)-1] = '\0';
+ fs_give((void **)&p);
+
+ ps->mangled_footer = 1;
+ help = NO_HELP;
+ while(1){
+ oeflags = OE_APPEND_CURRENT;
+ i = optionally_enter(sval, -FOOTER_ROWS(ps), 0,
+ sizeof(sval), prompt, NULL,
+ help, &oeflags);
+ if(i == 0){
+ removing_leading_and_trailing_white_space(sval);
+ rv = ps->mangled_body = 1;
+ if(lval[(*cl)->varmem])
+ fs_give((void **)&lval[(*cl)->varmem]);
+
+ i = 0;
+ for(tmp = sval; *tmp; tmp++)
+ if(*tmp == ',')
+ i++; /* count of ,'s */
+
+ if(!i){ /* only one item */
+ size_t l;
+
+ l = strlen(all_but_cmd) + strlen(sval);
+ lval[(*cl)->varmem] = (char *)fs_get((l+1) * sizeof(char));
+ snprintf(lval[(*cl)->varmem], l+1, "%s%s", all_but_cmd, sval);
+ lval[(*cl)->varmem][l] = '\0';
+
+ newval = &(*cl)->value;
+ }
+ else{
+ /*
+ * Don't allow input of multiple entries at once.
+ */
+ q_status_message(SM_ORDER,3,5,
+ _("No commas allowed in command"));
+ continue;
+ }
+ }
+ else if(i == 1){
+ cmd_cancelled("Change");
+ }
+ else if(i == 3){
+ help = help == NO_HELP ? h_config_change : NO_HELP;
+ continue;
+ }
+ else if(i == 4){ /* no redraw, yet */
+ continue;
+ }
+
+ break;
+ }
+ }
+ else if(i == 'n'){
+ char *all_but_nick;
+
+ parse_printer(lval[(*cl)->varmem],
+ &p, NULL, NULL, NULL, &all_but_nick, NULL);
+
+ strncpy(prompt, _("Change name : "), sizeof(prompt));
+ prompt[sizeof(prompt)-1] = '\0';
+ strncpy(name, p ? p : "", sizeof(name));
+ name[sizeof(name)-1] = '\0';
+
+ fs_give((void **)&p);
+
+ ps->mangled_footer = 1;
+ help = NO_HELP;
+ while(1){
+ oeflags = OE_APPEND_CURRENT;
+ i = optionally_enter(name, -FOOTER_ROWS(ps), 0,
+ sizeof(name), prompt, NULL,
+ help, &oeflags);
+ if(i == 0){
+ size_t l;
+
+ rv = ps->mangled_body = 1;
+ removing_leading_and_trailing_white_space(name);
+ if(lval[(*cl)->varmem])
+ fs_give((void **)&lval[(*cl)->varmem]);
+
+ l = strlen(name) + 1 + ((*all_but_nick == '[') ? 0 : 3) + strlen(all_but_nick);
+ lval[(*cl)->varmem] = (char *)fs_get((l+1) * sizeof(char));
+ snprintf(lval[(*cl)->varmem], l+1,
+ "%s %s%s", name,
+ (*all_but_nick == '[') ? "" : "[] ",
+ all_but_nick);
+ lval[(*cl)->varmem][l] = '\0';
+
+ newval = &(*cl)->value;
+ }
+ else if(i == 1){
+ cmd_cancelled("Change");
+ }
+ else if(i == 3){
+ help = help == NO_HELP ? h_config_change : NO_HELP;
+ continue;
+ }
+ else if(i == 4){ /* no redraw, yet */
+ continue;
+ }
+
+ break;
+ }
+
+ fs_give((void **)&all_but_nick);
+ }
+ else if(i == 'o'){
+ HelpType help;
+
+ ekey[0].ch = 'i';
+ ekey[0].rval = 'i';
+ ekey[0].name = "I";
+ ekey[0].label = N_("Init");
+ ekey[1].ch = 't';
+ ekey[1].rval = 't';
+ ekey[1].name = "T";
+ ekey[1].label = N_("Trailer");
+ ekey[2].ch = -1;
+ strncpy(prompt, _("Change Init string or Trailer string ? "), sizeof(prompt));
+ prompt[sizeof(prompt)-1] = '\0';
+ j = radio_buttons(prompt, -FOOTER_ROWS(ps), ekey, 'i', 'x',
+ h_config_print_opt_choice, RB_NORM);
+
+ if(j == 'x'){
+ cmd_cancelled("Change");
+ break;
+ }
+ else{
+ char *init, *trailer;
+
+ parse_printer(lval[(*cl)->varmem],
+ &nick, &p, &init, &trailer, NULL, NULL);
+
+ if(j == i)
+ snprintf(prompt, sizeof(prompt), _("Change INIT string : "));
+ else
+ snprintf(prompt, sizeof(prompt), _("Change TRAILER string : "));
+
+ strncpy(sval, (j == 'i') ? init : trailer, sizeof(sval)-1);
+ sval[sizeof(sval)-1] = '\0';
+
+ tmp = string_to_cstring(sval);
+ strncpy(sval, tmp, sizeof(sval)-1);
+ sval[sizeof(sval)-1] = '\0';
+ fs_give((void **)&tmp);
+
+ ps->mangled_footer = 1;
+ help = NO_HELP;
+ while(1){
+ oeflags = OE_APPEND_CURRENT;
+ i = optionally_enter(sval, -FOOTER_ROWS(ps), 0,
+ sizeof(sval), prompt, NULL, help, &oeflags);
+ if(i == 0){
+ size_t l;
+
+ removing_leading_and_trailing_white_space(sval);
+ rv = 1;
+ if(lval[(*cl)->varmem])
+ fs_give((void **)&lval[(*cl)->varmem]);
+ if(j == 'i'){
+ init = cstring_to_hexstring(sval);
+ tmp = cstring_to_hexstring(trailer);
+ fs_give((void **)&trailer);
+ trailer = tmp;
+ }
+ else{
+ trailer = cstring_to_hexstring(sval);
+ tmp = cstring_to_hexstring(init);
+ fs_give((void **)&init);
+ init = tmp;
+ }
+
+ l = strlen(nick) + 1 + 2 + strlen("INIT=") + strlen(init) + 1 + strlen("TRAILER=") + strlen(trailer)+ 1 + strlen(p);
+ lval[(*cl)->varmem] = (char *)fs_get((l+1) * sizeof(char));
+ snprintf(lval[(*cl)->varmem], l+1,
+ "%s%s%s%s%s%s%s%s%s%s%s",
+ /* nick */ nick,
+ /* space */ *nick ? " " : "",
+ /* [ */ (*nick || *init || *trailer) ? "[" : "",
+ /* INIT= */ *init ? "INIT=" : "",
+ /* init */ init,
+ /* space */ (*init && *trailer) ? " " : "",
+ /* TRAILER= */ *trailer ? "TRAILER=" : "",
+ /* trailer */ trailer,
+ /* ] */ (*nick || *init || *trailer) ? "]" : "",
+ /* space */ (*nick || *init || *trailer) ? " " : "",
+ /* command */ p);
+ lval[(*cl)->varmem][l] = '\0';
+
+ newval = &(*cl)->value;
+ }
+ else if(i == 1){
+ cmd_cancelled("Change");
+ }
+ else if(i == 3){
+ help=(help == NO_HELP)?h_config_print_init:NO_HELP;
+ continue;
+ }
+ else if(i == 4){ /* no redraw, yet */
+ continue;
+ }
+
+ break;
+ }
+
+ fs_give((void **)&nick);
+ fs_give((void **)&p);
+ fs_give((void **)&init);
+ fs_give((void **)&trailer);
+ }
+ }
+ }
+
+ break;
+
+ case MC_EXIT: /* exit */
+ rv = config_exit_cmd(flags);
+ break;
+
+ default:
+ rv = -1;
+ break;
+ }
+
+ if(skip_to_next)
+ *cl = next_confline(*cl);
+
+ /*
+ * At this point, if changes occurred, var->user_val.X is set.
+ * So, fix the current_val, and handle special cases...
+ */
+ if(rv == 1){
+ set_current_val((*cl)->var, TRUE, FALSE);
+ fix_side_effects(ps, (*cl)->var, 0);
+
+ if(newval){
+ if(*newval)
+ fs_give((void **)newval);
+
+ if((*cl)->var->current_val.l)
+ *newval = printer_name((*cl)->var->current_val.l[(*cl)->varmem]);
+ else
+ *newval = cpystr("");
+ }
+
+ if(changing_selected)
+ print_select_tool(ps, MC_CHOICE, cl, flags);
+ }
+
+ return(rv);
+}
+
+
+/*
+ * Given a single printer string from the config file, returns an allocated
+ * copy of the friendly printer name, which is
+ * "Nickname" command
+ */
+char *
+printer_name(char *input)
+{
+ char *nick, *cmd;
+ char *ret;
+
+ parse_printer(input, &nick, &cmd, NULL, NULL, NULL, NULL);
+ ret = (char *)fs_get((2+6*22+1+strlen(cmd)) * sizeof(char));
+ utf8_snprintf(ret, 2+6*22+1+strlen(cmd), "\"%.21w\"%*s%s",
+ *nick ? nick : "",
+ 22 - MIN(utf8_width(nick), 21),
+ "",
+ cmd);
+ fs_give((void **) &nick);
+ fs_give((void **) &cmd);
+
+ return(ret);
+}
diff --git a/alpine/print.h b/alpine/print.h
new file mode 100644
index 00000000..73e228b4
--- /dev/null
+++ b/alpine/print.h
@@ -0,0 +1,30 @@
+/*
+ * $Id: print.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_PRINT_INCLUDED
+#define PINE_PRINT_INCLUDED
+
+
+#include "../pith/state.h"
+
+
+/* exported protoypes */
+char *printer_name(char *);
+#ifndef DOS
+void select_printer(struct pine *, int);
+#endif
+
+
+#endif /* PINE_PRINT_INCLUDED */
diff --git a/alpine/radio.c b/alpine/radio.c
new file mode 100644
index 00000000..fc5c9ab9
--- /dev/null
+++ b/alpine/radio.c
@@ -0,0 +1,918 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: radio.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "headers.h"
+#include "radio.h"
+#include "keymenu.h"
+#include "busy.h"
+#include "status.h"
+#include "mailcmd.h"
+#include "titlebar.h"
+#include "roleconf.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/newmail.h"
+#include "../pith/util.h"
+
+
+/*
+ * Internal prototypes
+ */
+int pre_screen_config_want_to(char *, int, int);
+ESCKEY_S *construct_combined_esclist(ESCKEY_S *, ESCKEY_S *);
+void radio_help(int, int, HelpType);
+void draw_radio_prompt(int, unsigned, unsigned, char *);
+
+
+#define RAD_BUT_COL 0
+#define WANT_TO_BUF 2500
+
+
+/*
+ * want_to's array passed to radio_buttions...
+ */
+static ESCKEY_S yorn[] = {
+ {'y', 'y', "Y", N_("Yes")},
+ {'n', 'n', "N", N_("No")},
+ {-1, 0, NULL, NULL}
+};
+
+int
+pre_screen_config_want_to(char *question, int dflt, int on_ctrl_C)
+{
+ int ret = 0;
+ char rep[WANT_TO_BUF], *p;
+#ifdef _WINDOWS
+ rep[0] = '\0';
+ mswin_flush();
+ if(strlen(question) + 3 < WANT_TO_BUF){
+ snprintf(rep, sizeof(rep), "%s ?", question);
+ rep[sizeof(rep)-1] = '\0';
+ }
+
+ switch (mswin_yesno_utf8 (*rep ? rep : question)) {
+ default:
+ case 0: return (on_ctrl_C);
+ case 1: return ('y');
+ case 2: return ('n');
+ }
+#endif
+ while(!ret){
+ fprintf(stdout, "%s? [%c]:", question, dflt);
+ fgets(rep, WANT_TO_BUF, stdin);
+ if((p = strpbrk(rep, "\r\n")) != NULL)
+ *p = '\0';
+ switch(*rep){
+ case 'Y':
+ case 'y':
+ ret = (int)'y';
+ break;
+ case 'N':
+ case 'n':
+ ret = (int)'n';
+ break;
+ case '\0':
+ ret = dflt;
+ break;
+ default:
+ break;
+ }
+ }
+ return ret;
+}
+
+
+/*----------------------------------------------------------------------
+ Ask a yes/no question in the status line
+
+ Args: question -- string to prompt user with
+ dflt -- The default answer to the question (should probably
+ be y or n)
+ on_ctrl_C -- Answer returned on ^C
+ help -- Two line help text
+ flags -- Flags to modify behavior
+ WT_FLUSH_IN - Discard pending input.
+ WT_SEQ_SENSITIVE - Caller is sensitive to sequence
+ number changes caused by
+ unsolicited expunges while we're
+ viewing a message.
+
+ Result: Messes up the status line,
+ returns y, n, dflt, on_ctrl_C, or SEQ_EXCEPTION
+ ---*/
+int
+want_to(char *question, int dflt, int on_ctrl_C, HelpType help, int flags)
+{
+ char *free_this = NULL, *free_this2 = NULL, *prompt;
+ int rv, width;
+ size_t len;
+
+ if(!ps_global->ttyo)
+ return(pre_screen_config_want_to(question, dflt, on_ctrl_C));
+#ifdef _WINDOWS
+ if (mswin_usedialog ()) {
+ mswin_flush ();
+ switch (mswin_yesno_utf8 (question)) {
+ default:
+ case 0: return (on_ctrl_C);
+ case 1: return ('y');
+ case 2: return ('n');
+ }
+ }
+#endif
+
+ /*----
+ One problem with adding the (y/n) here is that shrinking the
+ screen while in radio_buttons() will cause it to get chopped
+ off. It would be better to truncate the question passed in
+ here and leave the full "(y/n) [x] : " on.
+ ----*/
+
+ len = strlen(question) + 4;
+ free_this = (char *) fs_get(len);
+ width = utf8_width(question);
+
+ if(width + 2 < ps_global->ttyo->screen_cols){
+ snprintf(free_this, len, "%s? ", question);
+ free_this[len-1] = '\0';
+ prompt = free_this;
+ }
+ else if(width + 1 < ps_global->ttyo->screen_cols){
+ snprintf(free_this, len, "%s?", question);
+ free_this[len-1] = '\0';
+ prompt = free_this;
+ }
+ else if(width < ps_global->ttyo->screen_cols){
+ snprintf(free_this, len, "%s", question);
+ free_this[len-1] = '\0';
+ prompt = free_this;
+ }
+ else{
+ free_this2 = (char *) fs_get(len);
+ snprintf(free_this2, len, "%s? ", question);
+ prompt = short_str(free_this2, free_this, len, ps_global->ttyo->screen_cols-1, MidDots);
+ }
+
+ if(on_ctrl_C == 'n') /* don't ever let cancel == 'n' */
+ on_ctrl_C = 0;
+
+ rv = radio_buttons(prompt,
+ (ps_global->ttyo->screen_rows > 4) ? - FOOTER_ROWS(ps_global) : -1,
+ yorn, dflt, on_ctrl_C, help, flags);
+
+ if(free_this)
+ fs_give((void **) &free_this);
+
+ if(free_this2)
+ fs_give((void **) &free_this2);
+
+ return(rv);
+}
+
+
+int
+one_try_want_to(char *question, int dflt, int on_ctrl_C, HelpType help, int flags)
+{
+ char *q2;
+ int rv;
+ size_t l;
+
+ l = strlen(question) + 5;
+ q2 = fs_get((l+1) * sizeof(char));
+ strncpy(q2, question, l);
+ q2[l] = '\0';
+ (void) utf8_truncate(q2, ps_global->ttyo->screen_cols - 6);
+ strncat(q2, "? ", l+1 - strlen(q2) - 1);
+ q2[l] = '\0';
+ rv = radio_buttons(q2,
+ (ps_global->ttyo->screen_rows > 4) ? - FOOTER_ROWS(ps_global) : -1,
+ yorn, dflt, on_ctrl_C, help, flags | RB_ONE_TRY);
+ fs_give((void **) &q2);
+
+ return(rv);
+}
+
+
+/*----------------------------------------------------------------------
+ Prompt user for a choice among alternatives
+
+Args -- utf8prompt: The prompt for the question/selection
+ line: The line to prompt on, if negative then relative to bottom
+ esc_list: ESC_KEY_S list of keys
+ dflt: The selection when the <CR> is pressed (should probably
+ be one of the chars in esc_list)
+ on_ctrl_C: The selection when ^C is pressed
+ help_text: Text to be displayed on bottom two lines
+ flags: Logically OR'd flags modifying our behavior to:
+ RB_FLUSH_IN - Discard any pending input chars.
+ RB_ONE_TRY - Only give one chance to answer. Returns
+ on_ctrl_C value if not answered acceptably
+ on first try.
+ RB_NO_NEWMAIL - Quell the usual newmail check.
+ RB_SEQ_SENSITIVE - The caller is sensitive to sequence number
+ changes so return on_ctrl_C if an
+ unsolicited expunge happens while we're
+ viewing a message.
+ RB_RET_HELP - Instead of the regular internal handling
+ way of handling help_text, this just causes
+ radio_buttons to return 3 when help is
+ asked for, so that the caller handles it
+ instead.
+
+ Note: If there are enough keys in the esc_list to need a second
+ screen, and there is no help, then the 13th key will be
+ put in the help position.
+
+Result -- Returns the letter pressed. Will be one of the characters in the
+ esc_list argument, or dflt, or on_ctrl_C, or SEQ_EXCEPTION.
+
+This will pause for any new status message to be seen and then prompt the user.
+The prompt will be truncated to fit on the screen. Redraw and resize are
+handled along with ^Z suspension. Typing ^G will toggle the help text on and
+off. Character types that are not buttons will result in a beep (unless one_try
+is set).
+ ----*/
+int
+radio_buttons(char *utf8prompt, int line, ESCKEY_S *esc_list, int dflt,
+ int on_ctrl_C, HelpType help_text, int flags)
+{
+ UCS ucs;
+ register int ch, real_line;
+ char *q, *ds = NULL;
+ unsigned maxcol;
+ int max_label, i, start, fkey_table[12];
+ int km_popped = 0;
+ struct key rb_keys[12];
+ struct key_menu rb_keymenu;
+ bitmap_t bitmap;
+ struct variable *vars = ps_global->vars;
+ COLOR_PAIR *lastc = NULL, *promptc = NULL;
+
+#ifdef _WINDOWS
+ int cursor_shown;
+
+ if (mswin_usedialog()){
+ MDlgButton button_list[25];
+ LPTSTR free_names[25];
+ LPTSTR free_labels[25];
+ int b, i, ret;
+ char **help;
+
+ memset(&free_names, 0, sizeof(LPTSTR) * 25);
+ memset(&free_labels, 0, sizeof(LPTSTR) * 25);
+ memset(&button_list, 0, sizeof (MDlgButton) * 25);
+ b = 0;
+
+ if(flags & RB_RET_HELP){
+ if(help_text != NO_HELP)
+ panic("RET_HELP and help in radio_buttons!");
+
+ button_list[b].ch = '?';
+ button_list[b].rval = 3;
+ button_list[b].name = TEXT("?");
+ free_labels[b] = utf8_to_lptstr(N_("Help"));
+ button_list[b].label = free_labels[b];
+ ++b;
+ }
+
+ for(i = 0; esc_list && esc_list[i].ch != -1 && i < 23; ++i){
+ if(esc_list[i].ch != -2){
+ button_list[b].ch = esc_list[i].ch;
+ button_list[b].rval = esc_list[i].rval;
+ free_names[b] = utf8_to_lptstr(esc_list[i].name);
+ button_list[b].name = free_names[b];
+ free_labels[b] = utf8_to_lptstr(esc_list[i].label);
+ button_list[b].label = free_labels[b];
+ ++b;
+ }
+ }
+
+ button_list[b].ch = -1;
+
+ /* assumption here is that HelpType is char ** */
+ help = help_text;
+
+ ret = mswin_select(utf8prompt, button_list, dflt, on_ctrl_C, help, flags);
+ for(i = 0; i < 25; i++){
+ if(free_names[i])
+ fs_give((void **) &free_names[i]);
+ if(free_labels[i])
+ fs_give((void **) &free_labels[i]);
+ }
+
+ return (ret);
+ }
+
+#endif /* _WINDOWS */
+
+ suspend_busy_cue();
+ flush_ordered_messages(); /* show user previous status msgs */
+ mark_status_dirty(); /* clear message next display call */
+ real_line = line > 0 ? line : ps_global->ttyo->screen_rows + line;
+ MoveCursor(real_line, RAD_BUT_COL);
+ CleartoEOLN();
+
+ /*---- Find widest label ----*/
+ max_label = 0;
+ for(i = 0; esc_list && esc_list[i].ch != -1 && i < 11; i++){
+ if(esc_list[i].ch == -2) /* -2 means to skip this key and leave blank */
+ continue;
+ if(esc_list[i].name)
+ max_label = MAX(max_label, utf8_width(esc_list[i].name));
+ }
+
+ if(ps_global->ttyo->screen_cols - max_label - 1 > 0)
+ maxcol = ps_global->ttyo->screen_cols - max_label - 1;
+ else
+ maxcol = 0;
+
+ /*
+ * We need to be able to truncate q, so copy it in case it is
+ * a readonly string.
+ */
+ q = cpystr(utf8prompt);
+
+ /*---- Init structs for keymenu ----*/
+ for(i = 0; i < 12; i++)
+ memset((void *)&rb_keys[i], 0, sizeof(struct key));
+
+ memset((void *)&rb_keymenu, 0, sizeof(struct key_menu));
+ rb_keymenu.how_many = 1;
+ rb_keymenu.keys = rb_keys;
+
+ /*---- Setup key menu ----*/
+ start = 0;
+ clrbitmap(bitmap);
+ memset(fkey_table, NO_OP_COMMAND, 12 * sizeof(int));
+ if(flags & RB_RET_HELP && help_text != NO_HELP)
+ panic("RET_HELP and help in radio_buttons!");
+
+ /* if shown, always at position 0 */
+ if(help_text != NO_HELP || flags & RB_RET_HELP){
+ rb_keymenu.keys[0].name = "?";
+ rb_keymenu.keys[0].label = N_("Help");
+ setbitn(0, bitmap);
+ fkey_table[0] = ctrl('G');
+ start++;
+ }
+
+ if(on_ctrl_C){
+ rb_keymenu.keys[1].name = "^C";
+ rb_keymenu.keys[1].label = N_("Cancel");
+ setbitn(1, bitmap);
+ fkey_table[1] = ctrl('C');
+ start++;
+ }
+
+ start = start ? 2 : 0;
+ /*---- Show the usual possible keys ----*/
+ for(i=start; esc_list && esc_list[i-start].ch != -1; i++){
+ /*
+ * If we have an esc_list item we'd like to put in the non-existent
+ * 13th slot, and there is no help, we put it in the help slot
+ * instead. We're hacking now...!
+ *
+ * We may also have invisible esc_list items that don't show up
+ * on the screen. We use this when we have two different keys
+ * which are synonyms, like ^P and KEY_UP. If all the slots are
+ * already full we can still fit invisible keys off the screen to
+ * the right. A key is invisible if it's label is "".
+ */
+ if(i >= 12){
+ if(esc_list[i-start].label
+ && esc_list[i-start].label[0] != '\0'){ /* visible */
+ if(i == 12){ /* special case where we put it in help slot */
+ if(help_text != NO_HELP)
+ panic("Programming botch in radio_buttons(): too many keys");
+
+ if(esc_list[i-start].ch != -2)
+ setbitn(0, bitmap); /* the help slot */
+
+ fkey_table[0] = esc_list[i-start].ch;
+ rb_keymenu.keys[0].name = esc_list[i-start].name;
+ if(esc_list[i-start].ch != -2
+ && esc_list[i-start].rval == dflt
+ && esc_list[i-start].label){
+ size_t l;
+
+ l = strlen(esc_list[i-start].label) + 2;
+ ds = (char *)fs_get((l+1) * sizeof(char));
+ snprintf(ds, l+1, "[%s]", esc_list[i-start].label);
+ ds[l] = '\0';
+ rb_keymenu.keys[0].label = ds;
+ }
+ else
+ rb_keymenu.keys[0].label = esc_list[i-start].label;
+ }
+ else
+ panic("Botch in radio_buttons(): too many keys");
+ }
+ }
+ else{
+ if(esc_list[i-start].ch != -2)
+ setbitn(i, bitmap);
+
+ fkey_table[i] = esc_list[i-start].ch;
+ rb_keymenu.keys[i].name = esc_list[i-start].name;
+ if(esc_list[i-start].ch != -2
+ && esc_list[i-start].rval == dflt
+ && esc_list[i-start].label){
+ size_t l;
+
+ l = strlen(esc_list[i-start].label) + 2;
+ ds = (char *)fs_get((l+1) * sizeof(char));
+ snprintf(ds, l+1, "[%s]", esc_list[i-start].label);
+ ds[l] = '\0';
+ rb_keymenu.keys[i].label = ds;
+ }
+ else
+ rb_keymenu.keys[i].label = esc_list[i-start].label;
+ }
+ }
+
+ for(; i < 12; i++)
+ rb_keymenu.keys[i].name = NULL;
+
+ ps_global->mangled_footer = 1;
+
+#ifdef _WINDOWS
+ cursor_shown = mswin_showcaret(1);
+#endif
+
+ if(pico_usingcolor() && VAR_PROMPT_FORE_COLOR &&
+ VAR_PROMPT_BACK_COLOR &&
+ pico_is_good_color(VAR_PROMPT_FORE_COLOR) &&
+ pico_is_good_color(VAR_PROMPT_BACK_COLOR)){
+ lastc = pico_get_cur_color();
+ if(lastc){
+ promptc = new_color_pair(VAR_PROMPT_FORE_COLOR,
+ VAR_PROMPT_BACK_COLOR);
+ (void)pico_set_colorp(promptc, PSC_NONE);
+ }
+ }
+ else
+ StartInverse();
+
+ draw_radio_prompt(real_line, RAD_BUT_COL, maxcol, q);
+
+ while(1){
+ fflush(stdout);
+
+ /*---- Paint the keymenu ----*/
+ if(lastc)
+ (void)pico_set_colorp(lastc, PSC_NONE);
+ else
+ EndInverse();
+
+ draw_keymenu(&rb_keymenu, bitmap, ps_global->ttyo->screen_cols,
+ 1 - FOOTER_ROWS(ps_global), 0, FirstMenu);
+ if(promptc)
+ (void)pico_set_colorp(promptc, PSC_NONE);
+ else
+ StartInverse();
+
+ MoveCursor(real_line, MIN(RAD_BUT_COL+utf8_width(q), maxcol+1));
+
+ if(flags & RB_FLUSH_IN)
+ flush_input();
+
+ newcmd:
+ /* Timeout 5 min to keep imap mail stream alive */
+ ucs = read_char(600);
+ dprint((2,
+ "Want_to read: %s (0x%x)\n", pretty_command(ucs), ucs));
+ if((ucs < 0x80) && isupper((unsigned char) ucs))
+ ucs = tolower((unsigned char) ucs);
+
+ if(F_ON(F_USE_FK,ps_global)
+ && (((ucs < 0x80) && isalpha((unsigned char) ucs) && !strchr("YyNn",(int) ucs))
+ || ((ucs >= PF1 && ucs <= PF12)
+ && (ucs = fkey_table[ucs - PF1]) == NO_OP_COMMAND))){
+ /*
+ * The funky test above does two things. It maps
+ * esc_list character commands to function keys, *and* prevents
+ * character commands from input while in function key mode.
+ * NOTE: this breaks if we ever need more than the first
+ * twelve function keys...
+ */
+ if(flags & RB_ONE_TRY){
+ ch = ucs = on_ctrl_C;
+ goto out_of_loop;
+ }
+
+ Writechar(BELL, 0);
+ continue;
+ }
+
+ switch(ucs){
+
+ default:
+ for(i = 0; esc_list && esc_list[i].ch != -1; i++)
+ if(ucs == esc_list[i].ch){
+ int len, n;
+
+ MoveCursor(real_line,len=MIN(RAD_BUT_COL+utf8_width(q),maxcol+1));
+ for(n = 0, len = ps_global->ttyo->screen_cols - len;
+ esc_list[i].label && esc_list[i].label[n] && len > 0;
+ n++, len--)
+ Writechar(esc_list[i].label[n], 0);
+
+ ch = esc_list[i].rval;
+ goto out_of_loop;
+ }
+
+ if(flags & RB_ONE_TRY){
+ ch = on_ctrl_C;
+ goto out_of_loop;
+ }
+
+ Writechar(BELL, 0);
+ break;
+
+ case ctrl('M'):
+ case ctrl('J'):
+ ch = dflt;
+ for(i = 0; esc_list && esc_list[i].ch != -1; i++)
+ if(ch == esc_list[i].rval){
+ int len, n;
+
+ MoveCursor(real_line,len=MIN(RAD_BUT_COL+utf8_width(q),maxcol+1));
+ for(n = 0, len = ps_global->ttyo->screen_cols - len;
+ esc_list[i].label && esc_list[i].label[n] && len > 0;
+ n++, len--)
+ Writechar(esc_list[i].label[n], 0);
+ break;
+ }
+
+ goto out_of_loop;
+
+ case ctrl('C'):
+ if(on_ctrl_C || (flags & RB_ONE_TRY)){
+ ch = on_ctrl_C;
+ goto out_of_loop;
+ }
+
+ Writechar(BELL, 0);
+ break;
+
+
+ case '?':
+ case ctrl('G'):
+ if(FOOTER_ROWS(ps_global) == 1 && km_popped == 0){
+ km_popped++;
+ FOOTER_ROWS(ps_global) = 3;
+ line = -3;
+ real_line = ps_global->ttyo->screen_rows + line;
+ if(lastc)
+ (void)pico_set_colorp(lastc, PSC_NONE);
+ else
+ EndInverse();
+
+ clearfooter(ps_global);
+ if(promptc)
+ (void)pico_set_colorp(promptc, PSC_NONE);
+ else
+ StartInverse();
+
+ draw_radio_prompt(real_line, RAD_BUT_COL, maxcol, q);
+ break;
+ }
+
+ if(flags & RB_RET_HELP){
+ ch = 3;
+ goto out_of_loop;
+ }
+ else if(help_text != NO_HELP && FOOTER_ROWS(ps_global) > 1){
+ mark_keymenu_dirty();
+ if(lastc)
+ (void)pico_set_colorp(lastc, PSC_NONE);
+ else
+ EndInverse();
+
+ MoveCursor(real_line + 1, RAD_BUT_COL);
+ CleartoEOLN();
+ MoveCursor(real_line + 2, RAD_BUT_COL);
+ CleartoEOLN();
+ radio_help(real_line, RAD_BUT_COL, help_text);
+ sleep(5);
+ MoveCursor(real_line, MIN(RAD_BUT_COL+utf8_width(q), maxcol+1));
+ if(promptc)
+ (void)pico_set_colorp(promptc, PSC_NONE);
+ else
+ StartInverse();
+ }
+ else
+ Writechar(BELL, 0);
+
+ break;
+
+
+ case NO_OP_COMMAND:
+ goto newcmd; /* misunderstood escape? */
+
+ case NO_OP_IDLE: /* UNODIR, keep the stream alive */
+ if(flags & RB_NO_NEWMAIL)
+ goto newcmd;
+
+ i = new_mail(0, VeryBadTime, NM_DEFER_SORT);
+ if(sp_expunge_count(ps_global->mail_stream)
+ && flags & RB_SEQ_SENSITIVE){
+ if(on_ctrl_C)
+ ch = on_ctrl_C;
+ else
+ ch = SEQ_EXCEPTION;
+
+ goto out_of_loop;
+ }
+
+ if(i < 0)
+ break; /* no changes, get on with life */
+ /* Else fall into redraw to adjust displayed numbers and such */
+
+
+ case KEY_RESIZE:
+ case ctrl('L'):
+ real_line = line > 0 ? line : ps_global->ttyo->screen_rows + line;
+ if(lastc)
+ (void)pico_set_colorp(lastc, PSC_NONE);
+ else
+ EndInverse();
+
+ ClearScreen();
+ redraw_titlebar();
+ if(ps_global->redrawer != NULL)
+ (*ps_global->redrawer)();
+ if(FOOTER_ROWS(ps_global) == 3 || km_popped)
+ redraw_keymenu();
+
+ if(ps_global->ttyo->screen_cols - max_label - 1 > 0)
+ maxcol = ps_global->ttyo->screen_cols - max_label - 1;
+ else
+ maxcol = 0;
+
+ if(promptc)
+ (void)pico_set_colorp(promptc, PSC_NONE);
+ else
+ StartInverse();
+
+ draw_radio_prompt(real_line, RAD_BUT_COL, maxcol, q);
+ break;
+
+ } /* switch */
+ }
+
+ out_of_loop:
+
+#ifdef _WINDOWS
+ if(!cursor_shown)
+ mswin_showcaret(0);
+#endif
+
+ fs_give((void **) &q);
+ if(ds)
+ fs_give((void **) &ds);
+
+ if(lastc){
+ (void) pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ if(promptc)
+ free_color_pair(&promptc);
+ }
+ else
+ EndInverse();
+
+ fflush(stdout);
+ resume_busy_cue(0);
+ if(km_popped){
+ FOOTER_ROWS(ps_global) = 1;
+ clearfooter(ps_global);
+ ps_global->mangled_body = 1;
+ }
+
+ return(ch);
+}
+
+
+#define OTHER_RETURN_VAL 1300
+#define KEYS_PER_LIST 8
+
+/*
+ * This should really be part of radio_buttons itself, I suppose. It was
+ * easier to do it this way. This is for when there are more than 12
+ * possible commands. We could have all the radio_buttons calls call this
+ * instead of radio_buttons, or rename this to radio_buttons.
+ *
+ * Radio_buttons is limited to 10 visible commands unless there is no Help,
+ * in which case it is 11 visible commands.
+ * Double_radio_buttons is limited to 16 visible commands because it uses
+ * slots 3 and 4 for readability and the OTHER CMD.
+ */
+int
+double_radio_buttons(char *prompt, int line, ESCKEY_S *esc_list, int dflt, int on_ctrl_C, HelpType help_text, int flags)
+{
+ ESCKEY_S *list = NULL, *list1 = NULL, *list2 = NULL;
+ int i = 0, j;
+ int v = OTHER_RETURN_VAL, listnum = 0;
+
+#ifdef _WINDOWS
+ if(mswin_usedialog())
+ return(radio_buttons(prompt, line, esc_list, dflt, on_ctrl_C,
+ help_text, flags));
+#endif
+
+ /* check to see if it will all fit in one */
+ while(esc_list && esc_list[i].ch != -1)
+ i++;
+
+ i++; /* for ^C */
+ if(help_text != NO_HELP)
+ i++;
+
+ if(i <= 12)
+ return(radio_buttons(prompt, line, esc_list, dflt, on_ctrl_C,
+ help_text, flags));
+
+ /*
+ * Won't fit, split it into two lists.
+ *
+ * We can fit at most 8 items in the visible list. The rest of
+ * the commands have to be invisible. Each of list1 and list2 should
+ * have no more than 8 visible (name != "" || label != "") items.
+ */
+ list1 = (ESCKEY_S *)fs_get((KEYS_PER_LIST+1) * sizeof(*list1));
+ memset(list1, 0, (KEYS_PER_LIST+1) * sizeof(*list1));
+ list2 = (ESCKEY_S *)fs_get((KEYS_PER_LIST+1) * sizeof(*list2));
+ memset(list2, 0, (KEYS_PER_LIST+1) * sizeof(*list2));
+
+ for(j=0,i=0; esc_list[i].ch != -1 && j < KEYS_PER_LIST; j++,i++)
+ list1[j] = esc_list[i];
+
+ list1[j].ch = -1;
+
+ for(j=0; esc_list[i].ch != -1 && j < KEYS_PER_LIST; j++,i++)
+ list2[j] = esc_list[i];
+
+ list2[j].ch = -1;
+
+ list = construct_combined_esclist(list1, list2);
+
+ while(v == OTHER_RETURN_VAL){
+ v = radio_buttons(prompt,line,list,dflt,on_ctrl_C,help_text,flags);
+ if(v == OTHER_RETURN_VAL){
+ fs_give((void **) &list);
+ listnum = 1 - listnum;
+ list = construct_combined_esclist(listnum ? list2 : list1,
+ listnum ? list1 : list2);
+ }
+ }
+
+ if(list)
+ fs_give((void **) &list);
+ if(list1)
+ fs_give((void **) &list1);
+ if(list2)
+ fs_give((void **) &list2);
+
+ return(v);
+}
+
+
+ESCKEY_S *
+construct_combined_esclist(ESCKEY_S *list1, ESCKEY_S *list2)
+{
+ ESCKEY_S *list;
+ int i, j=0, count;
+
+ count = 2; /* for blank key and for OTHER key */
+ for(i=0; list1 && list1[i].ch != -1; i++)
+ count++;
+ for(i=0; list2 && list2[i].ch != -1; i++)
+ count++;
+
+ list = (ESCKEY_S *) fs_get((count + 1) * sizeof(*list));
+ memset(list, 0, (count + 1) * sizeof(*list));
+
+ list[j].ch = -2; /* leave blank */
+ list[j].rval = 0;
+ list[j].name = NULL;
+ list[j++].label = NULL;
+
+ list[j].ch = 'o';
+ list[j].rval = OTHER_RETURN_VAL;
+ list[j].name = "O";
+ list[j].label = N_("OTHER CMDS");
+
+ /*
+ * Make sure that O for OTHER CMD or the return val for OTHER CMD
+ * isn't used for something else.
+ */
+ for(i=0; list1 && list1[i].ch != -1; i++){
+ if(list1[i].rval == list[j].rval)
+ panic("1bad rval in d_r");
+ if(F_OFF(F_USE_FK,ps_global) && list1[i].ch == list[j].ch)
+ panic("1bad ch in ccl");
+ }
+
+ for(i=0; list2 && list2[i].ch != -1; i++){
+ if(list2[i].rval == list[j].rval)
+ panic("2bad rval in d_r");
+ if(F_OFF(F_USE_FK,ps_global) && list2[i].ch == list[j].ch)
+ panic("2bad ch in ccl");
+ }
+
+ j++;
+
+ /* the visible set */
+ for(i=0; list1 && list1[i].ch != -1; i++){
+ if(i >= KEYS_PER_LIST && list1[i].label[0] != '\0')
+ panic("too many visible keys in ccl");
+
+ list[j++] = list1[i];
+ }
+
+ /* the rest are invisible */
+ for(i=0; list2 && list2[i].ch != -1; i++){
+ list[j] = list2[i];
+ list[j].label = "";
+ list[j++].name = "";
+ }
+
+ list[j].ch = -1;
+
+ return(list);
+}
+
+
+/*----------------------------------------------------------------------
+
+ ----*/
+void
+radio_help(int line, int column, HelpType help)
+{
+ char **text;
+
+ /* assumption here is that HelpType is char ** */
+ text = help;
+ if(text == NULL)
+ return;
+
+ MoveCursor(line + 1, column);
+ CleartoEOLN();
+ if(text[0])
+ PutLine0(line + 1, column, text[0]);
+
+ MoveCursor(line + 2, column);
+ CleartoEOLN();
+ if(text[1])
+ PutLine0(line + 2, column, text[1]);
+
+ fflush(stdout);
+}
+
+
+/*----------------------------------------------------------------------
+ Paint the screen with the radio buttons prompt
+ ----*/
+void
+draw_radio_prompt(int line, unsigned start_c, unsigned max_c, char *q)
+{
+ size_t len;
+ unsigned x, width, got_width;
+ char *tmpq = NULL, *useq;
+
+ width = utf8_width(q);
+ if(width > max_c - start_c + 1){
+ tmpq = (char *) fs_get((len=(strlen(q)+1)) * sizeof(char));
+ (void) utf8_to_width(tmpq, q, len, max_c - start_c + 1, &got_width);
+ useq = tmpq;
+ width = got_width;
+ }
+ else
+ useq = q;
+
+ PutLine0(line, start_c, useq);
+ x = start_c + width;
+ MoveCursor(line, x);
+ while(x++ < ps_global->ttyo->screen_cols)
+ Writechar(' ', 0);
+
+ MoveCursor(line, start_c + width);
+ fflush(stdout);
+ if(tmpq)
+ fs_give((void **) &tmpq);
+}
diff --git a/alpine/radio.h b/alpine/radio.h
new file mode 100644
index 00000000..3b29bd4d
--- /dev/null
+++ b/alpine/radio.h
@@ -0,0 +1,81 @@
+/*
+ * $Id: radio.h 1025 2008-04-08 22:59:38Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_RADIO_INCLUDED
+#define PINE_RADIO_INCLUDED
+
+#include "../pith/helptext.h"
+
+#include "help.h"
+
+
+/*
+ * For optionally_enter and radio_buttons, special meanings for keys.
+ */
+typedef struct esckey {
+ long ch; /* holds all UCS values plus -1 and -2 */
+ int rval; /* return value */
+ char *name; /* short name */
+ char *label; /* long descriptive key name */
+} ESCKEY_S;
+
+
+/*
+ * Minimum space needed after the prompt for optionally_enter() to work well.
+ * If the available width after the prompt is smaller than this then the prompt will
+ * have the leading edge cut off by optionally_enter.
+ * (note: used to be 5)
+ */
+#define MIN_OPT_ENT_WIDTH (7)
+
+/* max length prompt for optionally_enter and want_to */
+#define MAXPROMPT (ps_global->ttyo->screen_cols - MIN_OPT_ENT_WIDTH)
+
+
+#define SEQ_EXCEPTION (-3) /* returned when seq # change and no
+ on_ctrl_C available. */
+#define RB_NORM 0x00 /* flags modifying radio_buttons */
+#define RB_ONE_TRY 0x01 /* one shot answer, else default */
+#define RB_FLUSH_IN 0x02 /* discard pending input */
+#define RB_NO_NEWMAIL 0x04 /* Quell new mail check */
+#define RB_SEQ_SENSITIVE 0x08 /* Sensitive to seq # changes */
+#define RB_RET_HELP 0x10 /* Return when help key pressed */
+
+
+#define OE_NONE 0x00 /* optionally_enter flags */
+#define OE_DISALLOW_CANCEL 0x01 /* Turn off Cancel menu item */
+#define OE_DISALLOW_HELP 0x02 /* Turn off Help menu item */
+#define OE_USER_MODIFIED 0x08 /* set on return if user input */
+#define OE_KEEP_TRAILING_SPACE 0x10 /* Allow trailing white-space */
+#define OE_SEQ_SENSITIVE 0x20 /* Sensitive to seq # changes */
+#define OE_APPEND_CURRENT 0x40 /* append, don't truncate */
+#define OE_PASSWD 0x80 /* Use asterisks to echo input */
+#define OE_PASSWD_NOAST 0x100 /* Don't echo user input at all */
+
+#define WT_NORM 0x00 /* flags modifying want_to */
+#define WT_FLUSH_IN 0x01 /* discard pending input */
+#define WT_SEQ_SENSITIVE 0x02 /* Sensitive to seq # changes */
+
+
+/* exported protoypes */
+int want_to(char *, int, int, HelpType, int);
+int one_try_want_to(char *, int, int, HelpType, int);
+int radio_buttons(char *, int, ESCKEY_S *, int, int, HelpType, int);
+int double_radio_buttons(char *, int, ESCKEY_S *, int, int, HelpType, int);
+
+
+
+#endif /* PINE_RADIO_INCLUDED */
diff --git a/alpine/remote.c b/alpine/remote.c
new file mode 100644
index 00000000..b4891833
--- /dev/null
+++ b/alpine/remote.c
@@ -0,0 +1,347 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: remote.c 1032 2008-04-11 00:30:04Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "headers.h"
+#include "remote.h"
+#include "keymenu.h"
+#include "mailview.h"
+#include "status.h"
+#include "radio.h"
+#include "../pith/msgno.h"
+#include "../pith/filter.h"
+#include "../pith/util.h"
+#include "../pith/conf.h"
+#include "../pith/tempfile.h"
+#include "../pith/margin.h"
+
+
+/*
+ * Internal prototypes
+ */
+int rd_answer_forge_warning(int, MSGNO_S *, SCROLL_S *);
+
+
+int
+rd_prompt_about_forged_remote_data(int reason, REMDATA_S *rd, char *extra)
+{
+ char tmp[2000];
+ char *unknown = "<unknown>";
+ int rv = -1;
+ char *foldertype, *foldername, *special;
+
+ foldertype = (rd && rd->t.i.special_hdr && !strucmp(rd->t.i.special_hdr, REMOTE_ABOOK_SUBTYPE)) ? "address book" : (rd && rd->t.i.special_hdr && !strucmp(rd->t.i.special_hdr, REMOTE_PINERC_SUBTYPE)) ? "configuration" : "data";
+ foldername = (rd && rd->rn) ? rd->rn : unknown;
+ special = (rd && rd->t.i.special_hdr) ? rd->t.i.special_hdr : unknown;
+
+ dprint((1, "rd_check_out_forged_remote_data:\n"));
+ dprint((1, " reason=%d\n", reason));
+ dprint((1, " folder_type=%s\n", foldertype ? foldertype : "?"));
+ dprint((1, " remotename=%s\n\n", foldername ? foldername : "?"));
+
+ if(rd && rd->flags & USER_SAID_NO)
+ return rv;
+
+ if(reason == -2){
+ dprint((1, "The special header \"%s\" is missing from the last message in the folder.\nThis indicates that something is wrong.\nYou should probably answer \"No\"\nso that you don't use the corrupt data.\nThen you should investigate further.\n", special ? special : "?"));
+ }
+ else if(reason == -1){
+ dprint((1, "The last message in the folder contains \"Received\" headers.\nThis usually indicates that the message was put there by the mail\ndelivery system. Alpine does not add those Received headers.\nYou should probably answer \"No\" so that you don't use the corrupt data.\nThen you should investigate further.\n"));
+ }
+ else if(reason == 0){
+ dprint((1, "The special header \"%s\" in the last message\nin the folder has an unexpected value (%s)\nafter it. This could indicate that something is wrong.\nThis value should not normally be put there by Alpine.\nHowever, since there are no Received lines in the message we choose to\nbelieve that everything is ok and we will proceed.\n",
+ special ? special : "?", (extra && *extra) ? extra : "?"));
+ }
+ else if(reason == 1){
+ dprint((1, "The special header \"%s\" in the last message\nin the folder has an unexpected value (1)\nafter it. It appears that it may have been put there by an Pine\nwith a version number less than 4.50.\nSince there are no Received lines in the message we choose to believe that\nthe header was added by an old Pine and we will proceed.\n",
+ special ? special : "?"));
+ }
+ else if(reason > 1){
+ dprint((1, "The special header \"%s\" in the last message\nin the folder has an unexpected value (%s)\nafter it. This is the right sort of value that Alpine would normally put there,\nbut it doesn't match the value from the first message in the folder.\nThis may indicate that something is wrong.\nHowever, since there are no Received lines in the message we choose to\nbelieve that everything is ok and we will proceed.\n",
+ special ? special : "?", (extra && *extra) ? extra : "?"));
+ }
+
+ if(reason >= 0){
+ /*
+ * This check should not really be here. We have a cookie that
+ * has the wrong value so something is possibly wrong.
+ * But we are worried that old pines will put the bad value in
+ * there (which they will) and then the questions will bother
+ * users and mystify them. So we're just going to pretend the user
+ * said Yes in this case, and we'll try to fix the cookie.
+ * We still catch Received lines and use that or the complete absence
+ * of a special header as indicators of trouble.
+ */
+ rd->flags |= USER_SAID_YES;
+ return(1);
+ }
+
+ if(ps_global->ttyo){
+ SCROLL_S sargs;
+ STORE_S *in_store, *out_store;
+ gf_io_t pc, gc;
+ HANDLE_S *handles = NULL;
+ int the_answer = 'n';
+
+ if(!(in_store = so_get(CharStar, NULL, EDIT_ACCESS)) ||
+ !(out_store = so_get(CharStar, NULL, EDIT_ACCESS)))
+ goto try_wantto;
+
+ /* TRANSLATORS: the first %s is the folder type, it may be address book or
+ configuration. The second %s is the folder name. Of course, the HTML
+ tags should be left as is. */
+ snprintf(tmp, sizeof(tmp), _("<HTML><P>The data in the remote %s folder<P><CENTER>%s</CENTER><P>looks suspicious. The reason for the suspicion is<P><CENTER>"),
+ foldertype, foldername);
+ tmp[sizeof(tmp)-1] = '\0';
+ so_puts(in_store, tmp);
+
+ if(reason == -2){
+ snprintf(tmp, sizeof(tmp), _("header \"%s\" is missing</CENTER><P>The special header \"%s\" is missing from the last message in the folder. This indicates that something is wrong. You should probably answer \"No\" so that you don't use the corrupt data. Then you should investigate further."),
+ special, special);
+ tmp[sizeof(tmp)-1] = '\0';
+ so_puts(in_store, tmp);
+ }
+ else if(reason == -1){
+ so_puts(in_store, _("\"Received\" headers detected</CENTER><P>The last message in the folder contains \"Received\" headers. This usually indicates that the message was put there by the mail delivery system. Alpine does not add those Received headers. You should probably answer \"No\" so that you don't use the corrupt data. Then you should investigate further."));
+ }
+ else if(reason == 0){
+ snprintf(tmp, sizeof(tmp), _("Unexpected value for header \"%s\"</CENTER><P>The special header \"%s\" in the last message in the folder has an unexpected value (%s) after it. This probably indicates that something is wrong. This value would not normally be put there by Alpine. You should probably answer \"No\" so that you don't use the corrupt data. Then you should investigate further."),
+ special, special, (extra && *extra) ? extra : "?");
+ tmp[sizeof(tmp)-1] = '\0';
+ so_puts(in_store, tmp);
+ }
+ else if(reason == 1){
+ snprintf(tmp, sizeof(tmp), _("Unexpected value for header \"%s\"</CENTER><P>The special header \"%s\" in the last message in the folder has an unexpected value (1) after it. It appears that it may have been put there by a Pine with a version number less than 4.50. If you believe that you have changed this data with an older Pine more recently than you've changed it with this version of Alpine, then you can probably safely answer \"Yes\". If you do not understand why this has happened, you should probably answer \"No\" so that you don't use the corrupt data. Then you should investigate further."),
+ special, special);
+ tmp[sizeof(tmp)-1] = '\0';
+ so_puts(in_store, tmp);
+ }
+ else if(reason > 1){
+ snprintf(tmp, sizeof(tmp), _("Unexpected value for header \"%s\"</CENTER><P>The special header \"%s\" in the last message in the folder has an unexpected value (%s) after it. This is the right sort of value that Alpine would normally put there, but it doesn't match the value from the first message in the folder. This may indicate that something is wrong. Unless you understand why this has happened, you should probably answer \"No\" so that you don't use the corrupt data. Then you should investigate further."),
+ special, special, (extra && *extra) ? extra : "?");
+ tmp[sizeof(tmp)-1] = '\0';
+ so_puts(in_store, tmp);
+ }
+
+ so_seek(in_store, 0L, 0);
+ init_handles(&handles);
+ gf_filter_init();
+ gf_link_filter(gf_html2plain,
+ gf_html2plain_opt(NULL,
+ ps_global->ttyo->screen_cols, non_messageview_margin(),
+ &handles, NULL, GFHP_LOCAL_HANDLES));
+ gf_set_so_readc(&gc, in_store);
+ gf_set_so_writec(&pc, out_store);
+ gf_pipe(gc, pc);
+ gf_clear_so_writec(out_store);
+ gf_clear_so_readc(in_store);
+
+ memset(&sargs, 0, sizeof(SCROLL_S));
+ sargs.text.handles = handles;
+ sargs.text.text = so_text(out_store);
+ sargs.text.src = CharStar;
+ sargs.bar.title = _("REMOTE DATA FORGERY WARNING");
+ sargs.proc.tool = rd_answer_forge_warning;
+ sargs.proc.data.p = (void *)&the_answer;
+ sargs.keys.menu = &forge_keymenu;
+ setbitmap(sargs.keys.bitmap);
+
+ scrolltool(&sargs);
+
+ if(the_answer == 'y'){
+ rv = 1;
+ rd->flags |= USER_SAID_YES;
+ }
+ else if(rd)
+ rd->flags |= USER_SAID_NO;
+
+ ps_global->mangled_screen = 1;
+ ps_global->painted_body_on_startup = 0;
+ ps_global->painted_footer_on_startup = 0;
+ so_give(&in_store);
+ so_give(&out_store);
+ free_handles(&handles);
+ }
+ else{
+ char *p = tmp;
+
+ snprintf(p, sizeof(tmp), _("\nThe data in the remote %s folder\n\n %s\n\nlooks suspicious. The reason for the suspicion is\n\n "),
+ foldertype, foldername);
+ tmp[sizeof(tmp)-1] = '\0';
+ p += strlen(p);
+
+ if(reason == -2){
+ snprintf(p, sizeof(tmp)-(p-tmp), _("header \"%s\" is missing\n\nThe special header \"%s\" is missing from the last message\nin the folder. This indicates that something is wrong.\nYou should probably answer \"No\" so that you don't use the corrupt data.\nThen you should investigate further.\n\n"),
+ special, special);
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+ else if(reason == -1){
+ snprintf(p, sizeof(tmp)-(p-tmp), _("\"Received\" headers detected\n\nThe last message in the folder contains \"Received\" headers.\nThis usually indicates that the message was put there by the\nmail delivery system. Alpine does not add those Received headers.\nYou should probably answer \"No\" so that you don't use the corrupt data.\nThen you should investigate further.\n\n"));
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+ else if(reason == 0){
+ snprintf(p, sizeof(tmp)-(p-tmp), _("Unexpected value for header \"%s\"\n\nThe special header \"%s\" in the last message in the folder\nhas an unexpected value (%s) after it. This probably\nindicates that something is wrong. This value would not normally be put\nthere by Alpine. You should probably answer \"No\" so that you don't use\nthe corrupt data. Then you should investigate further.\n\n"),
+ special, special, (extra && *extra) ? extra : "?");
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+ else if(reason == 1){
+ snprintf(p, sizeof(tmp)-(p-tmp), _("Unexpected value for header \"%s\"\n\nThe special header \"%s\" in the last message in the folder\nhas an unexpected value (1) after it. It appears that it may have been\nput there by a Pine with a version number less than 4.50.\nIf you believe that you have changed this data with an older Pine more\nrecently than you've changed it with this version of Alpine, then you can\nprobably safely answer \"Yes\". If you do not understand why this has\nhappened, you should probably answer \"No\" so that you don't use the\ncorrupt data. Then you should investigate further.\n\n"),
+ special, special);
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+ else if(reason > 1){
+ snprintf(p, sizeof(tmp)-(p-tmp), _("Unexpected value for header \"%s\"\n\nThe special header \"%s\" in the last message in the folder\nhas an unexpected\nvalue (%s) after it. This is\nthe right sort of value that Alpine would normally put there, but it\ndoesn't match the value from the first message in the folder. This may\nindicate that something is wrong. Unless you understand why this has happened,\nyou should probably answer \"No\" so that you don't use the\ncorrupt data. Then you should investigate further.\n\n"),
+ special, special, (extra && *extra) ? extra : "?");
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+
+try_wantto:
+ p += strlen(p);
+ snprintf(p, sizeof(tmp)-(p-tmp), _("Suspicious data in \"%s\": Continue anyway "),
+ (rd && rd->t.i.special_hdr) ? rd->t.i.special_hdr
+ : unknown);
+ tmp[sizeof(tmp)-1] = '\0';
+ if(want_to(tmp, 'n', 'x', NO_HELP, WT_NORM) == 'y'){
+ rv = 1;
+ rd->flags |= USER_SAID_YES;
+ }
+ else if(rd)
+ rd->flags |= USER_SAID_NO;
+ }
+
+ if(rv < 0)
+ q_status_message1(SM_ORDER, 1, 3, _("Can't open remote %s"),
+ (rd && rd->rn) ? rd->rn : "<noname>");
+
+ return(rv);
+}
+
+
+int
+rd_answer_forge_warning(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
+{
+ int rv = 1;
+
+ ps_global->next_screen = SCREEN_FUN_NULL;
+
+ switch(cmd){
+ case MC_YES :
+ *(int *)(sparms->proc.data.p) = 'y';
+ break;
+
+ case MC_NO :
+ *(int *)(sparms->proc.data.p) = 'n';
+ break;
+
+ default:
+ panic("Unexpected command in rd_answer_forge_warning");
+ break;
+ }
+
+ return(rv);
+}
+
+
+
+char *
+rd_metadata_name(void)
+{
+ char *p, *q, *metafile;
+ char path[MAXPATH], pinerc_dir[MAXPATH];
+ struct variable *vars = ps_global->vars;
+
+ dprint((9, "rd_metadata_name\n"));
+
+ pinerc_dir[0] = '\0';
+ if(ps_global->pinerc){
+ char *prcn = ps_global->pinerc;
+ char *lc;
+
+ if((lc = last_cmpnt(prcn)) != NULL){
+ int to_copy;
+
+ to_copy = (lc - prcn > 1) ? (lc - prcn - 1) : 1;
+ strncpy(pinerc_dir, prcn, MIN(to_copy, sizeof(pinerc_dir)-1));
+ pinerc_dir[MIN(to_copy, sizeof(pinerc_dir)-1)] = '\0';
+ }
+ else{
+ pinerc_dir[0] = '.';
+ pinerc_dir[1] = '\0';
+ }
+ }
+
+ /*
+ * If there is no metadata file specified in the pinerc, create a filename.
+ */
+ if(!(VAR_REMOTE_ABOOK_METADATA && VAR_REMOTE_ABOOK_METADATA[0])){
+ if(pinerc_dir[0] && (p = tempfile_in_same_dir(ps_global->pinerc,
+ meta_prefix, NULL))){
+ /* fill in the pinerc variable */
+ q = p + strlen(pinerc_dir) + 1;
+ set_variable(V_REMOTE_ABOOK_METADATA, q, 1, 0, Main);
+ dprint((2, "creating name for metadata file: %s\n",
+ q ? q : "?"));
+
+ /* something's broken, return NULL rab */
+ if(!VAR_REMOTE_ABOOK_METADATA || !VAR_REMOTE_ABOOK_METADATA[0]){
+ our_unlink(p);
+ fs_give((void **)&p);
+ return(NULL);
+ }
+
+ fs_give((void **)&p);
+ }
+ else{
+ q_status_message(SM_ORDER, 3, 5,
+ "can't create metadata file in pinerc directory, continuing");
+ return(NULL);
+ }
+ }
+
+ build_path(path, pinerc_dir ? pinerc_dir : NULL,
+ VAR_REMOTE_ABOOK_METADATA, sizeof(path));
+ metafile = path;
+
+ /*
+ * If the metadata file doesn't exist, create it.
+ */
+ if(can_access(metafile, ACCESS_EXISTS) != 0){
+ int fd;
+
+ if((fd = our_open(metafile, O_CREAT|O_EXCL|O_WRONLY|O_BINARY, 0600)) < 0){
+
+ set_variable(V_REMOTE_ABOOK_METADATA, NULL, 1, 0, Main);
+
+ q_status_message2(SM_ORDER, 3, 5,
+ "can't create cache file %.200s, continuing (%.200s)",
+ metafile, error_description(errno));
+
+ dprint((2, "can't create metafile %s: %s\n",
+ metafile ? metafile : "?", error_description(errno)));
+
+ return(NULL);
+ }
+
+ dprint((2, "created metadata file: %s\n",
+ metafile ? metafile : "?"));
+
+ (void)close(fd);
+ }
+
+ return(cpystr(metafile));;
+}
+
diff --git a/alpine/remote.h b/alpine/remote.h
new file mode 100644
index 00000000..2509c79f
--- /dev/null
+++ b/alpine/remote.h
@@ -0,0 +1,27 @@
+/*
+ * $Id: remote.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_REMOTE_INCLUDED
+#define PINE_REMOTE_INCLUDED
+
+
+#include "../pith/remote.h"
+
+
+/* exported protoypes */
+char *rd_metadata_name(void);
+
+
+#endif /* PINE_REMOTE_INCLUDED */
diff --git a/alpine/reply.c b/alpine/reply.c
new file mode 100644
index 00000000..ee90ebd0
--- /dev/null
+++ b/alpine/reply.c
@@ -0,0 +1,2549 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: reply.c 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ Code here for forward and reply to mail
+ A few support routines as well
+
+ This code will forward and reply to MIME messages. The Alpine composer
+at this time will only support non-text segments at the end of a
+message so, things don't always come out as one would like. If you
+always forward a message in MIME format, all will be correct. Forwarding
+of nested MULTIPART messages will work. There's still a problem with
+MULTIPART/ALTERNATIVE as the "first text part" rule doesn't allow modifying
+the equivalent parts. Ideally, we should probably such segments as a
+single attachment when forwarding/replying. It would also be real nice to
+flatten out the nesting in the composer so pieces inside can get snipped.
+
+The evolution continues...
+ =====*/
+
+
+#include "headers.h"
+#include "reply.h"
+#include "status.h"
+#include "radio.h"
+#include "send.h"
+#include "titlebar.h"
+#include "mailindx.h"
+#include "help.h"
+#include "signal.h"
+#include "mailcmd.h"
+#include "alpine.h"
+#include "roleconf.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/init.h"
+#include "../pith/filter.h"
+#include "../pith/pattern.h"
+#include "../pith/charset.h"
+#include "../pith/mimedesc.h"
+#include "../pith/remote.h"
+#include "../pith/news.h"
+#include "../pith/util.h"
+#include "../pith/detoken.h"
+#include "../pith/newmail.h"
+#include "../pith/readfile.h"
+#include "../pith/tempfile.h"
+#include "../pith/busy.h"
+#include "../pith/ablookup.h"
+
+
+/*
+ * Internal Prototypes
+ */
+int reply_poster_followup(ENVELOPE *);
+int sigedit_exit_for_pico(struct headerentry *, void (*)(void), int, char **);
+long new_mail_for_pico(int, int);
+void cmd_input_for_pico(void);
+int display_message_for_pico(int);
+char *checkpoint_dir_for_pico(char *, size_t);
+void resize_for_pico(void);
+PCOLORS *colors_for_pico(void);
+void free_pcolors(PCOLORS **);
+
+
+/*----------------------------------------------------------------------
+ Fill in an outgoing message for reply and pass off to send
+
+ Args: pine_state -- The usual pine structure
+
+ Result: Reply is formatted and passed off to composer/mailer
+
+Reply
+
+ - put senders address in To field
+ - search to and cc fields to see if we aren't the only recipients
+ - if other than us, ask if we should reply to all.
+ - if answer yes, fill out the To and Cc fields
+ - fill in the fcc argument
+ - fill in the subject field
+ - fill out the body and the attachments
+ - pass off to pine_send()
+ ---*/
+int
+reply(struct pine *pine_state, ACTION_S *role_arg)
+{
+ ADDRESS *saved_from, *saved_to, *saved_cc, *saved_resent;
+ ADDRESS *us_in_to_and_cc, *ap;
+ ENVELOPE *env, *outgoing;
+ BODY *body, *orig_body = NULL;
+ REPLY_S reply;
+ void *msgtext = NULL;
+ char *tmpfix = NULL, *prefix = NULL, *fcc = NULL, *errmsg = NULL;
+ long msgno, j, totalm, rflags, *seq = NULL;
+ int i, include_text = 0, times = -1, warned = 0, rv = 0,
+ flags = RSF_QUERY_REPLY_ALL, reply_raw_body = 0;
+ int rolemsg = 0, copytomsg = 0;
+ gf_io_t pc;
+ PAT_STATE dummy;
+ REDRAFT_POS_S *redraft_pos = NULL;
+ ACTION_S *role = NULL, *nrole;
+#if defined(DOS) && !defined(_WINDOWS)
+ char *reserve;
+#endif
+
+ outgoing = mail_newenvelope();
+ totalm = mn_total_cur(pine_state->msgmap);
+ seq = (long *)fs_get(((size_t)totalm + 1) * sizeof(long));
+
+ dprint((4,"\n - reply (%s msgs) -\n", comatose(totalm)));
+
+ saved_from = (ADDRESS *) NULL;
+ saved_to = (ADDRESS *) NULL;
+ saved_cc = (ADDRESS *) NULL;
+ saved_resent = (ADDRESS *) NULL;
+
+ us_in_to_and_cc = (ADDRESS *) NULL;
+
+ outgoing->subject = NULL;
+
+ memset((void *)&reply, 0, sizeof(reply));
+
+ if(ps_global->full_header == 2
+ && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global))
+ reply_raw_body = 1;
+
+ /*
+ * We may have to loop through first to figure out what default
+ * reply-indent-string to offer...
+ */
+ if(mn_total_cur(pine_state->msgmap) > 1 &&
+ F_ON(F_ENABLE_EDIT_REPLY_INDENT, pine_state) &&
+ reply_quote_str_contains_tokens()){
+ for(msgno = mn_first_cur(pine_state->msgmap);
+ msgno > 0L && !tmpfix;
+ msgno = mn_next_cur(pine_state->msgmap)){
+
+ env = pine_mail_fetchstructure(pine_state->mail_stream,
+ mn_m2raw(pine_state->msgmap, msgno),
+ NULL);
+ if(!env) {
+ q_status_message1(SM_ORDER,3,4,
+ _("Error fetching message %s. Can't reply to it."),
+ long2string(msgno));
+ goto done_early;
+ }
+
+ if(!tmpfix){ /* look for prefix? */
+ tmpfix = reply_quote_str(env);
+ if(prefix){
+ i = strcmp(tmpfix, prefix);
+ fs_give((void **) &tmpfix);
+ if(i){ /* don't check back if dissimilar */
+ fs_give((void **) &prefix);
+ /*
+ * We free prefix, not tmpfix. We set tmpfix to prefix
+ * so that we won't come check again.
+ */
+ tmpfix = prefix = cpystr("> ");
+ }
+ }
+ else{
+ prefix = tmpfix;
+ tmpfix = NULL; /* check back later? */
+ }
+ }
+ }
+
+ tmpfix = prefix;
+ }
+
+ /*
+ * Loop thru the selected messages building the
+ * outgoing envelope's destinations...
+ */
+ for(msgno = mn_first_cur(pine_state->msgmap);
+ msgno > 0L;
+ msgno = mn_next_cur(pine_state->msgmap)){
+
+ /*--- Grab current envelope ---*/
+ env = pine_mail_fetchstructure(pine_state->mail_stream,
+ seq[++times] = mn_m2raw(pine_state->msgmap, msgno),
+ NULL);
+ if(!env) {
+ q_status_message1(SM_ORDER,3,4,
+ _("Error fetching message %s. Can't reply to it."),
+ long2string(msgno));
+ goto done_early;
+ }
+
+ /*
+ * We check for the prefix here if we didn't do it in the first
+ * loop above. This is just to save having to go through the loop
+ * twice in the cases where we don't need to.
+ */
+ if(!tmpfix){
+ tmpfix = reply_quote_str(env);
+ if(prefix){
+ i = strcmp(tmpfix, prefix);
+ fs_give((void **) &tmpfix);
+ if(i){ /* don't check back if dissimilar */
+ fs_give((void **) &prefix);
+ tmpfix = prefix = cpystr("> ");
+ }
+ }
+ else{
+ prefix = tmpfix;
+ tmpfix = NULL; /* check back later? */
+ }
+ }
+
+ /*
+ * For consistency, the first question is always "include text?"
+ */
+ if(!times){ /* only first time */
+ char *p = cpystr(prefix);
+
+ if((include_text=reply_text_query(pine_state,totalm,&prefix)) < 0)
+ goto done_early;
+
+ /* edited prefix? */
+ if(strcmp(p, prefix))
+ tmpfix = prefix; /* stop looking */
+
+ fs_give((void **)&p);
+ }
+
+ /*
+ * If we're agg-replying or there's a newsgroup and the user want's
+ * to post to news *and* via email, add relevant addresses to the
+ * outgoing envelope...
+ *
+ * The single message case gets us around the aggregate reply
+ * to messages in a mixed mail-news archive where some might
+ * have newsgroups and others not or whatever.
+ */
+ if(totalm > 1L || ((i = reply_news_test(env, outgoing)) & 1)){
+ if(totalm > 1)
+ flags |= RSF_FORCE_REPLY_TO;
+
+ if(!reply_harvest(pine_state, seq[times], NULL, env,
+ &saved_from, &saved_to, &saved_cc,
+ &saved_resent, &flags))
+ goto done_early;
+ }
+ else if(i == 0)
+ goto done_early;
+
+ /* collect a list of addresses that are us in to and cc fields */
+ if(env->to)
+ if((ap=reply_cp_addr(pine_state, 0L, NULL,
+ NULL, us_in_to_and_cc, NULL,
+ env->to, RCA_ONLY_US)) != NULL)
+ reply_append_addr(&us_in_to_and_cc, ap);
+
+ if(env->cc)
+ if((ap=reply_cp_addr(pine_state, 0L, NULL,
+ NULL, us_in_to_and_cc, NULL,
+ env->cc, RCA_ONLY_US)) != NULL)
+ reply_append_addr(&us_in_to_and_cc, ap);
+
+ /*------------ Format the subject line ---------------*/
+ if(outgoing->subject){
+ /*
+ * if reply to more than one message, and all subjects
+ * match, so be it. otherwise set it to something generic...
+ */
+ if(strucmp(outgoing->subject,
+ reply_subject(env->subject,tmp_20k_buf,SIZEOF_20KBUF))){
+ fs_give((void **)&outgoing->subject);
+ outgoing->subject = cpystr("Re: several messages");
+ }
+ }
+ else
+ outgoing->subject = reply_subject(env->subject, NULL, 0);
+ }
+
+ /* fill reply header */
+ reply_seed(pine_state, outgoing, env, saved_from,
+ saved_to, saved_cc, saved_resent,
+ &fcc, flags & RSF_FORCE_REPLY_ALL, &errmsg);
+ if(errmsg){
+ if(*errmsg){
+ q_status_message1(SM_ORDER, 3, 3, "%.200s", errmsg);
+ display_message(NO_OP_COMMAND);
+ }
+
+ fs_give((void **)&errmsg);
+ }
+
+ if(sp_expunge_count(pine_state->mail_stream)) /* cur msg expunged */
+ goto done_early;
+
+ /* Setup possible role */
+ if(role_arg)
+ role = copy_action(role_arg);
+
+ if(!role){
+ rflags = ROLE_REPLY;
+ if(nonempty_patterns(rflags, &dummy)){
+ /* setup default role */
+ nrole = NULL;
+ j = mn_first_cur(pine_state->msgmap);
+ do {
+ role = nrole;
+ nrole = set_role_from_msg(pine_state, rflags,
+ mn_m2raw(pine_state->msgmap, j),
+ NULL);
+ } while(nrole && (!role || nrole == role)
+ && (j=mn_next_cur(pine_state->msgmap)) > 0L);
+
+ if(!role || nrole == role)
+ role = nrole;
+ else
+ role = NULL;
+
+ if(confirm_role(rflags, &role))
+ role = combine_inherited_role(role);
+ else{ /* cancel reply */
+ role = NULL;
+ cmd_cancelled("Reply");
+ goto done_early;
+ }
+ }
+ }
+
+ /*
+ * Reply_seed may call c-client in get_fcc_based_on_to, so env may
+ * no longer be valid. Get it again.
+ * Similarly for set_role_from_message.
+ */
+ env = pine_mail_fetchstructure(pine_state->mail_stream, seq[times], NULL);
+
+ if(role){
+ rolemsg++;
+ /* override fcc gotten in reply_seed */
+ if(role->fcc && fcc)
+ fs_give((void **) &fcc);
+ }
+
+ if(F_ON(F_COPY_TO_TO_FROM, pine_state) && !(role && role->from)){
+ /*
+ * A list of all of our addresses that appear in the To
+ * and cc fields is in us_in_to_and_cc.
+ * If there is exactly one address in that list then
+ * use it for the outgoing From.
+ */
+ if(us_in_to_and_cc && !us_in_to_and_cc->next){
+ PINEFIELD *custom, *pf;
+ ADDRESS *a = NULL;
+ char *addr = NULL;
+
+ /*
+ * Check to see if this address is different from what
+ * we would have used anyway. If it is, notify the user
+ * with a status message. This is pretty hokey because we're
+ * mimicking how pine_send would set the From address and
+ * there is no coordination between the two.
+ */
+
+ /* in case user has a custom From value */
+ custom = parse_custom_hdrs(ps_global->VAR_CUSTOM_HDRS, UseAsDef);
+
+ pf = (PINEFIELD *) fs_get(sizeof(*pf));
+ memset((void *) pf, 0, sizeof(*pf));
+ pf->name = cpystr("From");
+ pf->addr = &a;
+ if(set_default_hdrval(pf, custom) >= UseAsDef
+ && pf->textbuf && pf->textbuf[0]){
+ removing_trailing_white_space(pf->textbuf);
+ (void)removing_double_quotes(pf->textbuf);
+ build_address(pf->textbuf, &addr, NULL, NULL, NULL);
+ rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
+ if(addr)
+ fs_give((void **) &addr);
+ }
+
+ if(!*pf->addr)
+ *pf->addr = generate_from();
+
+ if(*pf->addr && !address_is_same(*pf->addr, us_in_to_and_cc)){
+ copytomsg++;
+ if(!role){
+ role = (ACTION_S *) fs_get(sizeof(*role));
+ memset((void *) role, 0, sizeof(*role));
+ role->is_a_role = 1;
+ }
+
+ role->from = us_in_to_and_cc;
+ us_in_to_and_cc = NULL;
+ }
+
+ free_customs(custom);
+ free_customs(pf);
+ }
+ }
+
+ if(role){
+ if(rolemsg && copytomsg)
+ q_status_message1(SM_ORDER, 3, 4,
+ _("Replying using role \"%s\" and To as From"), role->nick);
+ else if(rolemsg)
+ q_status_message1(SM_ORDER, 3, 4,
+ _("Replying using role \"%s\""), role->nick);
+ else if(copytomsg)
+ q_status_message(SM_ORDER, 3, 4,
+ _("Replying using incoming To as outgoing From"));
+ }
+
+ if(us_in_to_and_cc)
+ mail_free_address(&us_in_to_and_cc);
+
+
+ seq[++times] = -1L; /* mark end of sequence list */
+
+ /*========== Other miscelaneous fields ===================*/
+ outgoing->in_reply_to = reply_in_reply_to(env);
+ outgoing->references = reply_build_refs(env);
+ outgoing->message_id = generate_message_id();
+
+ if(!outgoing->to &&
+ !outgoing->cc &&
+ !outgoing->bcc &&
+ !outgoing->newsgroups)
+ q_status_message(SM_ORDER | SM_DING, 3, 6,
+ _("Warning: no valid addresses to reply to!"));
+
+
+ /*==================== Now fix up the message body ====================*/
+
+ /*
+ * create storage object to be used for message text
+ */
+ if((msgtext = (void *)so_get(PicoText, NULL, EDIT_ACCESS)) == NULL){
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Error allocating message text"));
+ goto done_early;
+ }
+
+ gf_set_so_writec(&pc, (STORE_S *) msgtext);
+
+ /*---- Include the original text if requested ----*/
+ if(include_text && totalm > 1L){
+ char *sig;
+ int impl, template_len = 0, leave_cursor_at_top = 0;
+
+
+ env = NULL;
+ if(role && role->template){
+ char *filtered;
+
+ impl = 0;
+ filtered = detoken(role, env, 0,
+ F_ON(F_SIG_AT_BOTTOM, ps_global) ? 1 : 0,
+ 0, &redraft_pos, &impl);
+ if(filtered){
+ if(*filtered){
+ so_puts((STORE_S *)msgtext, filtered);
+ if(impl == 1)
+ template_len = strlen(filtered);
+ else if(impl == 2)
+ leave_cursor_at_top++;
+ }
+
+ fs_give((void **)&filtered);
+ }
+ else
+ impl = 1;
+ }
+ else
+ impl = 1;
+
+ if((sig = reply_signature(role, env, &redraft_pos, &impl)) &&
+ F_OFF(F_SIG_AT_BOTTOM, ps_global)){
+
+ /*
+ * If CURSORPOS was set explicitly in sig_file, and there was a
+ * template file before that, we need to adjust the offset by the
+ * length of the template file. However, if the template had
+ * a set CURSORPOS in it then impl was 2 before getting to the
+ * signature, so offset wouldn't have been reset by the signature
+ * CURSORPOS and offset would already be correct. That case will
+ * be ok here because template_len will be 0 and adding it does
+ * nothing. If template
+ * didn't have CURSORPOS in it, then impl was 1 and got set to 2
+ * by the CURSORPOS in the sig. In that case we have to adjust the
+ * offset. That's what the next line does. It adjusts it if
+ * template_len is nonzero and if CURSORPOS was set in sig_file.
+ */
+ if(impl == 2)
+ redraft_pos->offset += template_len;
+
+ if(*sig)
+ so_puts((STORE_S *)msgtext, sig);
+
+ /*
+ * Set sig to NULL because we've already used it. If SIG_AT_BOTTOM
+ * is set, we won't have used it yet and want it to be non-NULL.
+ */
+ fs_give((void **)&sig);
+ }
+
+ /*
+ * Only put cursor in sig if there is a cursorpos there but not
+ * one in the template, and sig-at-bottom.
+ */
+ if(!(sig && impl == 2 && !leave_cursor_at_top))
+ leave_cursor_at_top++;
+
+ body = mail_newbody();
+ body->type = TYPETEXT;
+ body->contents.text.data = msgtext;
+
+ for(msgno = mn_first_cur(pine_state->msgmap);
+ msgno > 0L;
+ msgno = mn_next_cur(pine_state->msgmap)){
+
+ if(env){ /* put 2 between messages */
+ gf_puts(NEWLINE, pc);
+ gf_puts(NEWLINE, pc);
+ }
+
+ /*--- Grab current envelope ---*/
+ env = pine_mail_fetchstructure(pine_state->mail_stream,
+ mn_m2raw(pine_state->msgmap, msgno),
+ &orig_body);
+ if(!env){
+ q_status_message1(SM_ORDER,3,4,
+ _("Error fetching message %s. Can't reply to it."),
+ long2string(mn_get_cur(pine_state->msgmap)));
+ goto done_early;
+ }
+
+ if(orig_body == NULL || orig_body->type == TYPETEXT || reply_raw_body) {
+ reply_delimiter(env, role, pc);
+ if(F_ON(F_INCLUDE_HEADER, pine_state))
+ reply_forward_header(pine_state->mail_stream,
+ mn_m2raw(pine_state->msgmap,msgno),
+ NULL, env, pc, prefix);
+
+ get_body_part_text(pine_state->mail_stream, reply_raw_body ? NULL : orig_body,
+ mn_m2raw(pine_state->msgmap, msgno),
+ reply_raw_body ? NULL : "1", 0L, pc, prefix,
+ NULL, GBPT_NONE);
+ }
+ else if(orig_body->type == TYPEMULTIPART) {
+ if(!warned++)
+ q_status_message(SM_ORDER,3,7,
+ _("WARNING! Attachments not included in multiple reply."));
+
+ if(orig_body->nested.part
+ && orig_body->nested.part->body.type == TYPETEXT) {
+ /*---- First part of the message is text -----*/
+ reply_delimiter(env, role, pc);
+ if(F_ON(F_INCLUDE_HEADER, pine_state))
+ reply_forward_header(pine_state->mail_stream,
+ mn_m2raw(pine_state->msgmap,
+ msgno),
+ NULL, env, pc, prefix);
+
+ get_body_part_text(pine_state->mail_stream,
+ &orig_body->nested.part->body,
+ mn_m2raw(pine_state->msgmap, msgno),
+ "1", 0L, pc, prefix, NULL, GBPT_NONE);
+ }
+ else{
+ q_status_message(SM_ORDER,0,3,
+ _("Multipart with no leading text part."));
+ }
+ }
+ else{
+ /*---- Single non-text message of some sort ----*/
+ q_status_message(SM_ORDER,3,3,
+ _("Non-text message not included."));
+ }
+ }
+
+ if(!leave_cursor_at_top){
+ long cnt = 0L;
+ unsigned char c;
+
+ /* rewind and count chars to start of sig file */
+ so_seek((STORE_S *)msgtext, 0L, 0);
+ while(so_readc(&c, (STORE_S *)msgtext))
+ cnt++;
+
+ if(!redraft_pos){
+ redraft_pos = (REDRAFT_POS_S *)fs_get(sizeof(*redraft_pos));
+ memset((void *)redraft_pos, 0,sizeof(*redraft_pos));
+ redraft_pos->hdrname = cpystr(":");
+ }
+
+ /*
+ * If explicit cursor positioning in sig file,
+ * add offset to start of sig file plus offset into sig file.
+ * Else, just offset to start of sig file.
+ */
+ redraft_pos->offset += cnt;
+ }
+
+ if(sig){
+ if(*sig)
+ so_puts((STORE_S *)msgtext, sig);
+
+ fs_give((void **)&sig);
+ }
+ }
+ else{
+ msgno = mn_m2raw(pine_state->msgmap,
+ mn_get_cur(pine_state->msgmap));
+
+ /*--- Grab current envelope ---*/
+ env = pine_mail_fetchstructure(pine_state->mail_stream, msgno,
+ &orig_body);
+
+ /*
+ * If the charset of the body part is different from ascii and
+ * charset conversion is _not_ happening, then preserve the original
+ * charset from the message so that if we don't enter any new
+ * chars with the hibit set we can use the original charset.
+ * If not all those things, then don't try to preserve it.
+ */
+ if(orig_body){
+ char *charset;
+
+ charset = parameter_val(orig_body->parameter, "charset");
+ if(charset && strucmp(charset, "us-ascii") != 0){
+ CONV_TABLE *ct;
+
+ /*
+ * There is a non-ascii charset, is there conversion happening?
+ */
+ if(!(ct=conversion_table(charset, ps_global->posting_charmap)) || !ct->table){
+ reply.orig_charset = charset;
+ charset = NULL;
+ }
+ }
+
+ if(charset)
+ fs_give((void **) &charset);
+ }
+
+ if(env) {
+ if(!(body = reply_body(pine_state->mail_stream, env, orig_body,
+ msgno, NULL, msgtext, prefix,
+ include_text, role, 1, &redraft_pos)))
+ goto done_early;
+ }
+ else{
+ q_status_message1(SM_ORDER,3,4,
+ _("Error fetching message %s. Can't reply to it."),
+ long2string(mn_get_cur(pine_state->msgmap)));
+ goto done_early;
+ }
+ }
+
+ /* fill in reply structure */
+ reply.prefix = prefix;
+ reply.mailbox = cpystr(pine_state->mail_stream->mailbox);
+ reply.origmbox = cpystr(pine_state->mail_stream->original_mailbox
+ ? pine_state->mail_stream->original_mailbox
+ : pine_state->mail_stream->mailbox);
+ reply.data.uid.msgs = (imapuid_t *) fs_get((times + 1) * sizeof(imapuid_t));
+ if((reply.data.uid.validity = pine_state->mail_stream->uid_validity) != 0){
+ reply.uid = 1;
+ for(i = 0; i < times ; i++)
+ reply.data.uid.msgs[i] = mail_uid(pine_state->mail_stream, seq[i]);
+ }
+ else{
+ reply.msgno = 1;
+ for(i = 0; i < times ; i++)
+ reply.data.uid.msgs[i] = seq[i];
+ }
+
+ reply.data.uid.msgs[i] = 0; /* tie off list */
+
+#if defined(DOS) && !defined(_WINDOWS)
+ free((void *)reserve);
+#endif
+
+ /* partially formatted outgoing message */
+ pine_send(outgoing, &body, _("COMPOSE MESSAGE REPLY"),
+ role, fcc, &reply, redraft_pos, NULL, NULL, 0);
+
+ rv++;
+ pine_free_body(&body);
+ if(reply.mailbox)
+ fs_give((void **) &reply.mailbox);
+ if(reply.origmbox)
+ fs_give((void **) &reply.origmbox);
+ if(reply.orig_charset)
+ fs_give((void **) &reply.orig_charset);
+ fs_give((void **) &reply.data.uid.msgs);
+ done_early:
+ if((STORE_S *) msgtext)
+ gf_clear_so_writec((STORE_S *) msgtext);
+
+ mail_free_envelope(&outgoing);
+ mail_free_address(&saved_from);
+ mail_free_address(&saved_to);
+ mail_free_address(&saved_cc);
+ mail_free_address(&saved_resent);
+
+ fs_give((void **)&seq);
+
+ if(prefix)
+ fs_give((void **)&prefix);
+
+ if(fcc)
+ fs_give((void **) &fcc);
+
+ free_redraft_pos(&redraft_pos);
+ free_action(&role);
+ return rv;
+}
+
+
+/*
+ * Ask user to confirm role choice, or choose another role.
+ *
+ * Args role -- A pointer into the pattern_h space at the default
+ * role to use. This can't be a copy, the comparison
+ * relies on it pointing at the actual role.
+ * This arg is also used to return a pointer to the
+ * chosen role.
+ *
+ * Returns 1 -- Yes, use role which is now in *role. This may not be
+ * the same as the role passed in and it may even be NULL.
+ * 0 -- Cancel reply.
+ */
+int
+confirm_role(long int rflags, ACTION_S **role)
+{
+ ACTION_S *role_p = NULL;
+ ACTION_S *default_role = NULL;
+ char prompt[80], *prompt_fodder;
+ int cmd, done, ret = 1;
+ void (*prev_screen)(struct pine *) = ps_global->prev_screen,
+ (*redraw)(void) = ps_global->redrawer;
+ PAT_S *curpat, *pat;
+ PAT_STATE pstate;
+ HelpType help;
+ ESCKEY_S ekey[4];
+
+ if(!nonempty_patterns(ROLE_DO_ROLES, &pstate) || !role)
+ return(ret);
+
+ /*
+ * If this is a reply or forward and the role doesn't require confirmation,
+ * then we just return with what was passed in.
+ */
+ if(((rflags & ROLE_REPLY) &&
+ *role && (*role)->repl_type == ROLE_REPL_NOCONF) ||
+ ((rflags & ROLE_FORWARD) &&
+ *role && (*role)->forw_type == ROLE_FORW_NOCONF) ||
+ ((rflags & ROLE_COMPOSE) &&
+ *role && (*role)->comp_type == ROLE_COMP_NOCONF) ||
+ (!*role && F_OFF(F_ROLE_CONFIRM_DEFAULT, ps_global)
+ && !ps_global->default_role))
+ return(ret);
+
+ /*
+ * Check that there is at least one role available. This is among all
+ * roles, not just the reply roles or just the forward roles. That's
+ * because we have ^T take us to all the roles, not the category-specific
+ * roles.
+ */
+ if(!(pat = last_pattern(&pstate)))
+ return(ret);
+
+ ekey[0].ch = 'y';
+ ekey[0].rval = 'y';
+
+ ekey[1].ch = 'n';
+ ekey[1].rval = 'n';
+
+ ekey[2].ch = ctrl('T');
+ ekey[2].rval = 2;
+ ekey[2].name = "^T";
+
+ ekey[3].ch = -1;
+
+ /* check for more than one role available (or no role set) */
+ if(pat == first_pattern(&pstate) && *role) /* no ^T */
+ ekey[2].ch = -1;
+
+ /*
+ * Setup default role
+ * Go through the loop just in case default_role doesn't point
+ * to a real current role.
+ */
+ if(ps_global->default_role){
+ for(pat = first_pattern(&pstate);
+ pat;
+ pat = next_pattern(&pstate)){
+ if(pat->action == ps_global->default_role){
+ default_role = ps_global->default_role;
+ break;
+ }
+ }
+ }
+
+ curpat = NULL;
+
+ /* override default */
+ if(*role){
+ for(pat = first_pattern(&pstate);
+ pat;
+ pat = next_pattern(&pstate)){
+ if(pat->action == *role){
+ curpat = pat;
+ break;
+ }
+ }
+ }
+
+ if(rflags & ROLE_REPLY)
+ prompt_fodder = _("Reply");
+ else if(rflags & ROLE_FORWARD)
+ prompt_fodder = _("Forward");
+ else
+ prompt_fodder = _("Compose");
+
+ done = 0;
+ while(!done){
+ if(curpat){
+ char buf[100];
+
+ help = h_role_confirm;
+ ekey[0].name = "Y";
+ ekey[0].label = N_("Yes");
+ ekey[1].name = "N";
+ if(default_role)
+ ekey[1].label = N_("No, use default role");
+ else
+ ekey[1].label = N_("No, use default settings");
+
+ ekey[2].label = N_("To Select Alternate Role");
+
+ if(curpat->patgrp && curpat->patgrp->nick)
+ /* TRANSLATORS: This is something like Use role <nickname of role> for Reply? */
+ snprintf(prompt, sizeof(prompt), _("Use role \"%s\" for %s? "),
+ short_str(curpat->patgrp->nick, buf, sizeof(buf), 50, MidDots),
+ prompt_fodder);
+ else
+ snprintf(prompt, sizeof(prompt),
+ _("Use role \"<a role without a nickname>\" for %s? "),
+ prompt_fodder);
+ }
+ else{
+ help = h_norole_confirm;
+ ekey[0].name = "Ret";
+ ekey[0].label = prompt_fodder;
+ ekey[1].name = "";
+ ekey[1].label = "";
+ ekey[2].label = N_("To Select Role");
+ snprintf(prompt, sizeof(prompt),
+ _("Press Return to %s using %s role, or ^T to select a role "),
+ prompt_fodder, default_role ? _("default") : _("no"));
+ }
+
+ prompt[sizeof(prompt)-1] = '\0';
+
+ cmd = radio_buttons(prompt, -FOOTER_ROWS(ps_global), ekey,
+ 'y', 'x', help, RB_NORM);
+
+ switch(cmd){
+ case 'y': /* Accept */
+ done++;
+ *role = curpat ? curpat->action : default_role;
+ break;
+
+ case 'x': /* Cancel */
+ ret = 0;
+ /* fall through */
+
+ case 'n': /* NoRole */
+ done++;
+ *role = default_role;
+ break;
+
+ case 2: /* ^T */
+ if(role_select_screen(ps_global, &role_p, 0) >= 0){
+ if(role_p){
+ for(pat = first_pattern(&pstate);
+ pat;
+ pat = next_pattern(&pstate)){
+ if(pat->action == role_p){
+ curpat = pat;
+ break;
+ }
+ }
+ }
+ else
+ curpat = NULL;
+ }
+
+ ClearBody();
+ ps_global->mangled_body = 1;
+ ps_global->prev_screen = prev_screen;
+ ps_global->redrawer = redraw;
+ break;
+ }
+ }
+
+ return(ret);
+}
+
+
+/*
+ * reply_to_all_query - Ask user about replying to all recipients
+ *
+ * Returns: -1 if cancel, 0 otherwise
+ * by reference: flagp
+ */
+int
+reply_to_all_query(int *flagp)
+{
+ switch(want_to("Reply to all recipients",
+ 'n', 'x', NO_HELP, WT_SEQ_SENSITIVE)){
+ case 'x' :
+ return(-1);
+
+ case 'y' : /* set reply-all bit */
+ (*flagp) |= RSF_FORCE_REPLY_ALL;
+ break;
+
+ case 'n' : /* clear reply-all bit */
+ (*flagp) &= ~RSF_FORCE_REPLY_ALL;
+ break;
+ }
+
+ return(0);
+}
+
+
+/*
+ * reply_using_replyto_query - Ask user about replying with reply-to value
+ *
+ * Returns: 'y' if yes
+ * 'x' if cancel
+ */
+int
+reply_using_replyto_query(void)
+{
+ return(want_to("Use \"Reply-To:\" address instead of \"From:\" address",
+ 'y', 'x', NO_HELP,WT_SEQ_SENSITIVE));
+}
+
+
+/*
+ * reply_text_query - Ask user about replying with text...
+ *
+ * Returns: 1 if include the text
+ * 0 if we're NOT to include the text
+ * -1 on cancel or error
+ */
+int
+reply_text_query(struct pine *ps, long int many, char **prefix)
+{
+ int ret, edited = 0;
+ static ESCKEY_S rtq_opts[] = {
+ {'y', 'y', "Y", N_("Yes")},
+ {'n', 'n', "N", N_("No")},
+ {-1, 0, NULL, NULL}, /* may be overridden below */
+ {-1, 0, NULL, NULL}
+ };
+
+ if(F_ON(F_AUTO_INCLUDE_IN_REPLY, ps)
+ && F_OFF(F_ENABLE_EDIT_REPLY_INDENT, ps))
+ return(1);
+
+ while(1){
+ if(many > 1L)
+ /* TRANSLATORS: The final three %s's can probably be safely ignored */
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Include %s original messages in Reply%s%s%s? "),
+ comatose(many),
+ F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? " (using \"" : "",
+ F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? *prefix : "",
+ F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? "\")" : "");
+ else
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Include original message in Reply%s%s%s? "),
+ F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? " (using \"" : "",
+ F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? *prefix : "",
+ F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? "\")" : "");
+
+ if(F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps)){
+ rtq_opts[2].ch = ctrl('R');
+ rtq_opts[2].rval = 'r';
+ rtq_opts[2].name = "^R";
+ rtq_opts[2].label = N_("Edit Indent String");
+ }
+ else
+ rtq_opts[2].ch = -1;
+
+ switch(ret = radio_buttons(tmp_20k_buf,
+ ps->ttyo->screen_rows > 4
+ ? -FOOTER_ROWS(ps_global) : -1,
+ rtq_opts,
+ (edited || F_ON(F_AUTO_INCLUDE_IN_REPLY, ps))
+ ? 'y' : 'n',
+ 'x', NO_HELP, RB_SEQ_SENSITIVE)){
+ case 'x':
+ cmd_cancelled("Reply");
+ return(-1);
+
+ case 'r':
+ if(prefix && *prefix){
+ int done = 0;
+ char buf[64];
+ int flags;
+
+ while(!done){
+ strncpy(buf, *prefix, sizeof(buf)-1);
+ buf[sizeof(buf)-1] = '\0';
+
+ flags = OE_APPEND_CURRENT |
+ OE_KEEP_TRAILING_SPACE |
+ OE_DISALLOW_HELP |
+ OE_SEQ_SENSITIVE;
+
+ switch(optionally_enter(buf, ps->ttyo->screen_rows > 4
+ ? -FOOTER_ROWS(ps_global) : -1,
+ 0, sizeof(buf), "Reply prefix : ",
+ NULL, NO_HELP, &flags)){
+ case 0: /* entry successful, continue */
+ if(flags & OE_USER_MODIFIED){
+ fs_give((void **)prefix);
+ *prefix = removing_quotes(cpystr(buf));
+ edited = 1;
+ }
+
+ done++;
+ break;
+
+ case 1:
+ cmd_cancelled("Reply");
+
+ case -1:
+ return(-1);
+
+ case 4:
+ EndInverse();
+ ClearScreen();
+ redraw_titlebar();
+ if(ps_global->redrawer != NULL)
+ (*ps_global->redrawer)();
+
+ redraw_keymenu();
+ break;
+
+ case 3:
+ break;
+
+ case 2:
+ default:
+ q_status_message(SM_ORDER, 3, 4,
+ "Programmer botch in reply_text_query()");
+ return(-1);
+ }
+ }
+ }
+
+ break;
+
+ case 'y':
+ return(1);
+
+ case 'n':
+ return(0);
+
+ default:
+ q_status_message1(SM_ORDER, 3, 4,
+ "Invalid rval \'%s\'", pretty_command(ret));
+ return(-1);
+ }
+ }
+}
+
+
+/*
+ * reply_poster_followup - return TRUE if "followup-to" set to "poster"
+ *
+ * NOTE: queues status message indicating such
+ */
+int
+reply_poster_followup(ENVELOPE *e)
+{
+ if(e && e->followup_to && !strucmp(e->followup_to, "poster")){
+ q_status_message(SM_ORDER, 2, 3,
+ _("Replying to Poster as specified in \"Followup-To\""));
+ return(1);
+ }
+
+ return(0);
+}
+
+
+/*
+ * reply_news_test - Test given envelope for newsgroup data and copy
+ * it at the users request
+ * RETURNS:
+ * 0 if error or cancel
+ * 1 reply via email
+ * 2 follow-up via news
+ * 3 do both
+ */
+int
+reply_news_test(ENVELOPE *env, ENVELOPE *outgoing)
+{
+ int ret = 1;
+ static ESCKEY_S news_opt[] = { {'f', 'f', "F", N_("Follow-up")},
+ {'r', 'r', "R", N_("Reply")},
+ {'b', 'b', "B", N_("Both")},
+ {-1, 0, NULL, NULL} };
+
+ if(env->newsgroups && *env->newsgroups && !reply_poster_followup(env))
+ /*
+ * Now that we know a newsgroups field is present,
+ * ask if the user is posting a follow-up article...
+ */
+ switch(radio_buttons(
+ _("Follow-up to news group(s), Reply via email to author or Both? "),
+ -FOOTER_ROWS(ps_global), news_opt, 'r', 'x',
+ NO_HELP, RB_NORM)){
+ case 'r' : /* Reply */
+ ret = 1;
+ break;
+
+ case 'f' : /* Follow-Up via news ONLY! */
+ ret = 2;
+ break;
+
+ case 'b' : /* BOTH */
+ ret = 3;
+ break;
+
+ case 'x' : /* cancel or unknown response */
+ default :
+ cmd_cancelled("Reply");
+ ret = 0;
+ break;
+ }
+
+ if(ret > 1){
+ if(env->followup_to){
+ q_status_message(SM_ORDER, 2, 3,
+ _("Posting to specified Followup-To groups"));
+ outgoing->newsgroups = cpystr(env->followup_to);
+ }
+ else if(!outgoing->newsgroups)
+ outgoing->newsgroups = cpystr(env->newsgroups);
+ }
+
+ return(ret);
+}
+
+
+/*----------------------------------------------------------------------
+ Acquire the pinerc defined signature file
+ It is allocated here and freed by the caller.
+
+ file -- use this file
+ prenewlines -- prefix the file contents with this many newlines
+ postnewlines -- postfix the file contents with this many newlines
+ is_sig -- this is a signature (not a template)
+ ----*/
+char *
+get_signature_file(char *file, int prenewlines, int postnewlines, int is_sig)
+{
+ char *sig, *tmp_sig = NULL, sig_path[MAXPATH+1];
+ int len, do_the_pipe_thang = 0;
+ long sigsize = 0L, cntdown;
+
+ sig_path[0] = '\0';
+ if(!signature_path(file, sig_path, MAXPATH))
+ return(NULL);
+
+ dprint((5, "get_signature(%s)\n", sig_path));
+
+ if(sig_path[(len=strlen(sig_path))-1] == '|'){
+ if(is_sig && F_ON(F_DISABLE_PIPES_IN_SIGS, ps_global)){
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Pipes for signatures are administratively disabled"));
+ return(NULL);
+ }
+ else if(!is_sig && F_ON(F_DISABLE_PIPES_IN_TEMPLATES, ps_global)){
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Pipes for templates are administratively disabled"));
+ return(NULL);
+ }
+
+ sig_path[len-1] = '\0';
+ removing_trailing_white_space(sig_path);
+ do_the_pipe_thang++;
+ }
+
+ if(!IS_REMOTE(sig_path) && ps_global->VAR_OPER_DIR &&
+ !in_dir(ps_global->VAR_OPER_DIR, sig_path)){
+ q_status_message2(SM_ORDER | SM_DING, 3, 4,
+ /* TRANSLATORS: First arg is the directory name, second is
+ the file user wants to read but can't. */
+ _("Can't read file outside %s: %s"),
+ ps_global->VAR_OPER_DIR, file);
+
+ return(NULL);
+ }
+
+ if(IS_REMOTE(sig_path) || can_access(sig_path, ACCESS_EXISTS) == 0){
+ if(do_the_pipe_thang){
+ if(can_access(sig_path, EXECUTE_ACCESS) == 0){
+ STORE_S *store;
+ int flags;
+ PIPE_S *syspipe;
+ gf_io_t pc, gc;
+ long start;
+
+ if((store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
+
+ flags = PIPE_READ | PIPE_STDERR | PIPE_NOSHELL;
+
+ start = time(0);
+
+ if((syspipe = open_system_pipe(sig_path, NULL, NULL, flags, 5,
+ pipe_callback, pipe_report_error)) != NULL){
+ unsigned char c;
+ char *error, *q;
+
+ gf_set_so_writec(&pc, store);
+ gf_set_readc(&gc, (void *)syspipe, 0, PipeStar, READ_FROM_LOCALE);
+ gf_filter_init();
+
+ if((error = gf_pipe(gc, pc)) != NULL){
+ (void)close_system_pipe(&syspipe, NULL, pipe_callback);
+ gf_clear_so_writec(store);
+ so_give(&store);
+ q_status_message1(SM_ORDER | SM_DING, 3, 4,
+ _("Can't get file: %s"), error);
+ return(NULL);
+ }
+
+ if(close_system_pipe(&syspipe, NULL, pipe_callback)){
+ long now;
+
+ now = time(0);
+ q_status_message2(SM_ORDER, 3, 4,
+ _("Error running program \"%s\"%s"),
+ file,
+ (now - start > 4) ? ": timed out" : "");
+ }
+
+ gf_clear_so_writec(store);
+
+ /* rewind and count chars */
+ so_seek(store, 0L, 0);
+ while(so_readc(&c, store) && sigsize < 100000L)
+ sigsize++;
+
+ /* allocate space */
+ tmp_sig = fs_get((sigsize + 1) * sizeof(char));
+ tmp_sig[0] = '\0';
+ q = tmp_sig;
+
+ /* rewind and copy chars, no prenewlines... */
+ so_seek(store, 0L, 0);
+ cntdown = sigsize;
+ while(so_readc(&c, store) && cntdown-- > 0L)
+ *q++ = c;
+
+ *q = '\0';
+ so_give(&store);
+ }
+ else{
+ so_give(&store);
+ q_status_message1(SM_ORDER | SM_DING, 3, 4,
+ _("Error running program \"%s\""),
+ file);
+ }
+ }
+ else
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ "Error allocating space for sig or template program");
+ }
+ else
+ q_status_message1(SM_ORDER | SM_DING, 3, 4,
+ /* TRANSLATORS: Arg is a program name */
+ _("Can't execute \"%s\": Permission denied"),
+ sig_path);
+ }
+ else if((IS_REMOTE(sig_path) &&
+ (tmp_sig = simple_read_remote_file(sig_path, REMOTE_SIG_SUBTYPE))) ||
+ (tmp_sig = read_file(sig_path, READ_FROM_LOCALE)))
+ sigsize = strlen(tmp_sig);
+ else
+ q_status_message2(SM_ORDER | SM_DING, 3, 4,
+ /* TRANSLATORS: First arg is error description, 2nd is
+ filename */
+ _("Error \"%s\" reading file \"%s\""),
+ error_description(errno), sig_path);
+ }
+
+ sig = get_signature_lit(tmp_sig, prenewlines, postnewlines, is_sig, 0);
+ if(tmp_sig)
+ fs_give((void **)&tmp_sig);
+
+ return(sig);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Partially set up message to forward and pass off to composer/mailer
+
+ Args: pine_state -- The usual pine structure
+
+ Result: outgoing envelope and body created and passed off to composer/mailer
+
+ Create the outgoing envelope for the mail being forwarded, which is
+not much more than filling in the subject, and create the message body
+of the outgoing message which requires formatting the header from the
+envelope of the original messasge.
+ ----------------------------------------------------------------------*/
+int
+forward(struct pine *ps, ACTION_S *role_arg)
+{
+ char *sig;
+ int ret, forward_raw_body = 0, rv = 0, i;
+ long msgno, j, totalmsgs, rflags;
+ ENVELOPE *env, *outgoing;
+ BODY *orig_body, *body = NULL;
+ REPLY_S reply;
+ void *msgtext = NULL;
+ gf_io_t pc;
+ int impl, template_len = 0;
+ PAT_STATE dummy;
+ REDRAFT_POS_S *redraft_pos = NULL;
+ ACTION_S *role = NULL, *nrole;
+#if defined(DOS) && !defined(_WINDOWS)
+ char *reserve;
+#endif
+
+ dprint((4, "\n - forward -\n"));
+
+ memset((void *)&reply, 0, sizeof(reply));
+ outgoing = mail_newenvelope();
+ outgoing->message_id = generate_message_id();
+
+ if(ps_global->full_header == 2
+ && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global))
+ forward_raw_body = 1;
+
+ if((totalmsgs = mn_total_cur(ps->msgmap)) > 1L){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s forwarded messages...", comatose(totalmsgs));
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ outgoing->subject = cpystr(tmp_20k_buf);
+ }
+ else{
+ /*---------- Get the envelope of message we're forwarding ------*/
+ msgno = mn_m2raw(ps->msgmap, mn_get_cur(ps->msgmap));
+ if(!((env = pine_mail_fetchstructure(ps->mail_stream, msgno, NULL))
+ && (outgoing->subject = forward_subject(env, 0)))){
+ q_status_message1(SM_ORDER,3,4,
+ _("Error fetching message %s. Can't forward it."),
+ long2string(msgno));
+ goto clean;
+ }
+ }
+
+ /*
+ * as with all text bound for the composer, build it in
+ * a storage object of the type it understands...
+ */
+ if((msgtext = (void *)so_get(PicoText, NULL, EDIT_ACCESS)) == NULL){
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Error allocating message text"));
+ goto clean;
+ }
+
+ ret = (F_ON(F_FORWARD_AS_ATTACHMENT, ps_global))
+ ? 'y'
+ : (totalmsgs > 1L)
+ ? want_to(_("Forward messages as a MIME digest"), 'y', 'x', NO_HELP, WT_SEQ_SENSITIVE)
+ : (ps->full_header == 2)
+ ? want_to(_("Forward message as an attachment"), 'n', 'x', NO_HELP, WT_SEQ_SENSITIVE)
+ : 0;
+
+ if(ret == 'x'){
+ cmd_cancelled("Forward");
+ so_give((STORE_S **)&msgtext);
+ goto clean;
+ }
+
+ /* Setup possible role */
+ if(role_arg)
+ role = copy_action(role_arg);
+
+ if(!role){
+ rflags = ROLE_FORWARD;
+ if(nonempty_patterns(rflags, &dummy)){
+ /* setup default role */
+ nrole = NULL;
+ j = mn_first_cur(ps->msgmap);
+ do {
+ role = nrole;
+ nrole = set_role_from_msg(ps, rflags,
+ mn_m2raw(ps->msgmap, j), NULL);
+ } while(nrole && (!role || nrole == role)
+ && (j=mn_next_cur(ps->msgmap)) > 0L);
+
+ if(!role || nrole == role)
+ role = nrole;
+ else
+ role = NULL;
+
+ if(confirm_role(rflags, &role))
+ role = combine_inherited_role(role);
+ else{ /* cancel reply */
+ role = NULL;
+ cmd_cancelled("Forward");
+ so_give((STORE_S **)&msgtext);
+ goto clean;
+ }
+ }
+ }
+
+ if(role)
+ q_status_message1(SM_ORDER, 3, 4,
+ _("Forwarding using role \"%s\""), role->nick);
+
+ if(role && role->template){
+ char *filtered;
+
+ impl = 1;
+ filtered = detoken(role, (totalmsgs == 1L) ? env : NULL,
+ 0, 0, 0, &redraft_pos, &impl);
+ if(filtered){
+ if(*filtered){
+ so_puts((STORE_S *)msgtext, filtered);
+ if(impl == 1)
+ template_len = strlen(filtered);
+ }
+
+ fs_give((void **)&filtered);
+ }
+ }
+ else
+ impl = 1;
+
+ if((sig = detoken(role, NULL, 2, 0, 1, &redraft_pos, &impl)) != NULL){
+ if(impl == 2)
+ redraft_pos->offset += template_len;
+
+ so_puts((STORE_S *)msgtext, *sig ? sig : NEWLINE);
+
+ fs_give((void **)&sig);
+ }
+ else
+ so_puts((STORE_S *)msgtext, NEWLINE);
+
+ gf_set_so_writec(&pc, (STORE_S *)msgtext);
+
+#if defined(DOS) && !defined(_WINDOWS)
+#if defined(LWP) || defined(PCTCP) || defined(PCNFS)
+#define IN_RESERVE 8192
+#else
+#define IN_RESERVE 16384
+#endif
+ if((reserve=(char *)malloc(IN_RESERVE)) == NULL){
+ gf_clear_so_writec((STORE_S *) msgtext);
+ so_give((STORE_S **)&msgtext);
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Insufficient memory for message text"));
+ goto clean;
+ }
+#endif
+
+ /*
+ * If we're forwarding multiple messages *or* the forward-as-mime
+ * is turned on and the users wants it done that way, package things
+ * up...
+ */
+ if(ret == 'y'){ /* attach message[s]!!! */
+ PART **pp;
+ long totalsize = 0L;
+
+ /*---- New Body to start with ----*/
+ body = mail_newbody();
+ body->type = TYPEMULTIPART;
+
+ /*---- The TEXT part/body ----*/
+ body->nested.part = mail_newbody_part();
+ body->nested.part->body.type = TYPETEXT;
+ body->nested.part->body.contents.text.data = msgtext;
+
+ if(totalmsgs > 1L){
+ /*---- The MULTIPART/DIGEST part ----*/
+ body->nested.part->next = mail_newbody_part();
+ body->nested.part->next->body.type = TYPEMULTIPART;
+ body->nested.part->next->body.subtype = cpystr("Digest");
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Digest of %s messages", comatose(totalmsgs));
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ body->nested.part->next->body.description = cpystr(tmp_20k_buf);
+ pp = &(body->nested.part->next->body.nested.part);
+ }
+ else
+ pp = &(body->nested.part->next);
+
+ /*---- The Message body subparts ----*/
+ for(msgno = mn_first_cur(ps->msgmap);
+ msgno > 0L;
+ msgno = mn_next_cur(ps->msgmap)){
+
+ msgno = mn_m2raw(ps->msgmap, msgno);
+ env = pine_mail_fetchstructure(ps->mail_stream, msgno, NULL);
+
+ if(forward_mime_msg(ps->mail_stream,msgno,NULL,env,pp,msgtext)){
+ totalsize += (*pp)->body.size.bytes;
+ pp = &((*pp)->next);
+ }
+ else
+ goto bomb;
+ }
+
+ if(totalmsgs > 1L)
+ body->nested.part->next->body.size.bytes = totalsize;
+ }
+ else if(totalmsgs > 1L){
+ int warned = 0;
+ body = mail_newbody();
+ body->type = TYPETEXT;
+ body->contents.text.data = msgtext;
+ env = NULL;
+
+ for(msgno = mn_first_cur(ps->msgmap);
+ msgno > 0L;
+ msgno = mn_next_cur(ps->msgmap)){
+
+ if(env){ /* put 2 between messages */
+ gf_puts(NEWLINE, pc);
+ gf_puts(NEWLINE, pc);
+ }
+
+ /*--- Grab current envelope ---*/
+ env = pine_mail_fetchstructure(ps->mail_stream,
+ mn_m2raw(ps->msgmap, msgno),
+ &orig_body);
+ if(!env || !orig_body){
+ q_status_message1(SM_ORDER,3,4,
+ _("Error fetching message %s. Can't forward it."),
+ long2string(msgno));
+ goto bomb;
+ }
+
+ if(orig_body == NULL || orig_body->type == TYPETEXT || forward_raw_body) {
+ forward_delimiter(pc);
+ reply_forward_header(ps->mail_stream,
+ mn_m2raw(ps->msgmap, msgno),
+ NULL, env, pc, "");
+
+ if(!get_body_part_text(ps->mail_stream, forward_raw_body ? NULL : orig_body,
+ mn_m2raw(ps->msgmap, msgno),
+ forward_raw_body ? NULL : "1", 0L, pc,
+ NULL, NULL, GBPT_NONE))
+ goto bomb;
+ } else if(orig_body->type == TYPEMULTIPART) {
+ if(!warned++)
+ q_status_message(SM_ORDER,3,7,
+ _("WARNING! Attachments not included in multiple forward."));
+
+ if(orig_body->nested.part &&
+ orig_body->nested.part->body.type == TYPETEXT) {
+ /*---- First part of the message is text -----*/
+ forward_delimiter(pc);
+ reply_forward_header(ps->mail_stream,
+ mn_m2raw(ps->msgmap,msgno),
+ NULL, env, pc, "");
+
+ if(!get_body_part_text(ps->mail_stream,
+ &orig_body->nested.part->body,
+ mn_m2raw(ps->msgmap, msgno),
+ "1", 0L, pc,
+ NULL, NULL, GBPT_NONE))
+ goto bomb;
+ } else {
+ q_status_message(SM_ORDER,0,3,
+ _("Multipart with no leading text part!"));
+ }
+ } else {
+ /*---- Single non-text message of some sort ----*/
+ q_status_message(SM_ORDER,0,3,
+ _("Non-text message not included!"));
+ }
+ }
+ }
+ else if(!((env = pine_mail_fetchstructure(ps->mail_stream, msgno,
+ &orig_body))
+ && (body = forward_body(ps->mail_stream, env, orig_body, msgno,
+ NULL, msgtext,
+ FWD_NONE)))){
+ q_status_message1(SM_ORDER,3,4,
+ _("Error fetching message %s. Can't forward it."),
+ long2string(msgno));
+ goto clean;
+ }
+
+ if(ret != 'y' && totalmsgs == 1L && orig_body){
+ char *charset;
+
+ charset = parameter_val(orig_body->parameter, "charset");
+ if(charset && strucmp(charset, "us-ascii") != 0){
+ CONV_TABLE *ct;
+
+ /*
+ * There is a non-ascii charset, is there conversion happening?
+ */
+ if(!(ct=conversion_table(charset, ps_global->posting_charmap)) || !ct->table){
+ reply.orig_charset = charset;
+ charset = NULL;
+ }
+ }
+
+ if(charset)
+ fs_give((void **) &charset);
+
+ /*
+ * I don't think orig_charset is ever used except possibly
+ * right here. Hubert 2008-01-15.
+ */
+ if(reply.orig_charset)
+ reply.forw = 1;
+ }
+
+ /* fill in reply structure */
+ reply.forwarded = 1;
+ reply.mailbox = cpystr(ps->mail_stream->mailbox);
+ reply.origmbox = cpystr(ps->mail_stream->original_mailbox
+ ? ps->mail_stream->original_mailbox
+ : ps->mail_stream->mailbox);
+ reply.data.uid.msgs = (imapuid_t *) fs_get((totalmsgs + 1) * sizeof(imapuid_t));
+ if((reply.data.uid.validity = ps->mail_stream->uid_validity) != 0){
+ reply.uid = 1;
+ for(msgno = mn_first_cur(ps->msgmap), i = 0;
+ msgno > 0L;
+ msgno = mn_next_cur(ps->msgmap), i++)
+ reply.data.uid.msgs[i] = mail_uid(ps->mail_stream, mn_m2raw(ps->msgmap, msgno));
+ }
+ else{
+ reply.msgno = 1;
+ for(msgno = mn_first_cur(ps->msgmap), i = 0;
+ msgno > 0L;
+ msgno = mn_next_cur(ps->msgmap), i++)
+ reply.data.uid.msgs[i] = mn_m2raw(ps->msgmap, msgno);
+ }
+
+ reply.data.uid.msgs[i] = 0; /* tie off list */
+
+#if defined(DOS) && !defined(_WINDOWS)
+ free((void *)reserve);
+#endif
+ pine_send(outgoing, &body, "FORWARD MESSAGE",
+ role, NULL, &reply, redraft_pos,
+ NULL, NULL, 0);
+ rv++;
+
+ clean:
+ if(body)
+ pine_free_body(&body);
+
+ if((STORE_S *) msgtext)
+ gf_clear_so_writec((STORE_S *) msgtext);
+
+ mail_free_envelope(&outgoing);
+ free_redraft_pos(&redraft_pos);
+ free_action(&role);
+
+ if(reply.orig_charset)
+ fs_give((void **)&reply.orig_charset);
+
+ return rv;
+
+ bomb:
+ q_status_message(SM_ORDER | SM_DING, 4, 5,
+ _("Error fetching message contents. Can't forward message."));
+ goto clean;
+}
+
+
+/*----------------------------------------------------------------------
+ Partially set up message to forward and pass off to composer/mailer
+
+ Args: pine_state -- The usual pine structure
+ message -- The MESSAGECACHE of entry to reply to
+
+ Result: outgoing envelope and body created and passed off to composer/mailer
+
+ Create the outgoing envelope for the mail being forwarded, which is
+not much more than filling in the subject, and create the message body
+of the outgoing message which requires formatting the header from the
+envelope of the original messasge.
+ ----------------------------------------------------------------------*/
+void
+forward_text(struct pine *pine_state, void *text, SourceType source)
+{
+ ENVELOPE *env;
+ BODY *body;
+ gf_io_t pc, gc;
+ STORE_S *msgtext;
+ char *enc_error, *sig;
+ ACTION_S *role = NULL;
+ PAT_STATE dummy;
+ long rflags = ROLE_COMPOSE;
+
+ if((msgtext = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
+ env = mail_newenvelope();
+ env->message_id = generate_message_id();
+ body = mail_newbody();
+ body->type = TYPETEXT;
+ body->contents.text.data = (void *) msgtext;
+
+ if(nonempty_patterns(rflags, &dummy)){
+ /*
+ * This is really more like Compose, even though it
+ * is called Forward.
+ */
+ if(confirm_role(rflags, &role))
+ role = combine_inherited_role(role);
+ else{ /* cancel */
+ cmd_cancelled("Composition");
+ display_message('x');
+ mail_free_envelope(&env);
+ pine_free_body(&body);
+ return;
+ }
+ }
+
+ if(role)
+ q_status_message1(SM_ORDER, 3, 4, _("Composing using role \"%s\""),
+ role->nick);
+
+ sig = detoken(role, NULL, 2, 0, 1, NULL, NULL);
+ so_puts(msgtext, (sig && *sig) ? sig : NEWLINE);
+ so_puts(msgtext, NEWLINE);
+ so_puts(msgtext, "----- Included text -----");
+ so_puts(msgtext, NEWLINE);
+ if(sig)
+ fs_give((void **)&sig);
+
+ gf_filter_init();
+ gf_set_so_writec(&pc, msgtext);
+ gf_set_readc(&gc,text,(source == CharStar) ? strlen((char *)text) : 0L,
+ source, 0);
+
+ if((enc_error = gf_pipe(gc, pc)) == NULL){
+ pine_send(env, &body, "SEND MESSAGE", role, NULL, NULL, NULL,
+ NULL, NULL, 0);
+ pine_state->mangled_screen = 1;
+ }
+ else{
+ q_status_message1(SM_ORDER | SM_DING, 3, 5,
+ _("Error reading text \"%s\""),enc_error);
+ display_message('x');
+ }
+
+ gf_clear_so_writec(msgtext);
+ mail_free_envelope(&env);
+ pine_free_body(&body);
+ }
+ else {
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Error allocating message text"));
+ display_message('x');
+ }
+
+ free_action(&role);
+}
+
+
+/*----------------------------------------------------------------------
+ Partially set up message to resend and pass off to mailer
+
+ Args: pine_state -- The usual pine structure
+
+ Result: outgoing envelope and body created and passed off to mailer
+
+ Create the outgoing envelope for the mail being resent, which is
+not much more than filling in the subject, and create the message body
+of the outgoing message which requires formatting the header from the
+envelope of the original messasge.
+ ----------------------------------------------------------------------*/
+int
+bounce(struct pine *pine_state, ACTION_S *role)
+{
+ ENVELOPE *env;
+ long msgno, rawno;
+ char *save_to = NULL, **save_toptr = NULL, *errstr = NULL,
+ *prmpt_who = NULL, *prmpt_cnf = NULL;
+
+ dprint((4, "\n - bounce -\n"));
+
+ if(mn_total_cur(pine_state->msgmap) > 1L){
+ save_toptr = &save_to;
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("BOUNCE (redirect) %ld messages to : "),
+ mn_total_cur(pine_state->msgmap));
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ prmpt_who = cpystr(tmp_20k_buf);
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Send %ld messages "),
+ mn_total_cur(pine_state->msgmap));
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ prmpt_cnf = cpystr(tmp_20k_buf);
+ }
+
+ for(msgno = mn_first_cur(pine_state->msgmap);
+ msgno > 0L;
+ msgno = mn_next_cur(pine_state->msgmap)){
+
+ rawno = mn_m2raw(pine_state->msgmap, msgno);
+ if((env = pine_mail_fetchstructure(pine_state->mail_stream, rawno, NULL)) != NULL)
+ errstr = bounce_msg(pine_state->mail_stream, rawno, NULL, role,
+ save_toptr, env->subject, prmpt_who, prmpt_cnf);
+ else
+ errstr = _("Can't fetch Subject for Bounce");
+
+
+ if(errstr){
+ if(*errstr)
+ q_status_message(SM_ORDER | SM_DING, 4, 7, errstr);
+
+ break;
+ }
+ }
+
+ if(save_to)
+ fs_give((void **)&save_to);
+
+ if(prmpt_who)
+ fs_give((void **) &prmpt_who);
+
+ if(prmpt_cnf)
+ fs_give((void **) &prmpt_cnf);
+
+ return(errstr ? 0 : 1);
+}
+
+
+
+char *
+bounce_msg(MAILSTREAM *stream,
+ long int rawno,
+ char *part,
+ ACTION_S *role,
+ char **to,
+ char *subject,
+ char *pmt_who,
+ char *pmt_cnf)
+{
+ char *errstr = NULL;
+ int was_seen = -1;
+ ENVELOPE *outgoing;
+ BODY *body = NULL;
+ MESSAGECACHE *mc;
+
+ if((errstr = bounce_msg_body(stream, rawno, part, to, subject, &outgoing, &body, &was_seen)) == NULL){
+ if(pine_simple_send(outgoing, &body, role, pmt_who, pmt_cnf, to,
+ !(to && *to) ? SS_PROMPTFORTO : 0) < 0){
+ errstr = ""; /* p_s_s() better have explained! */
+ /* clear seen flag */
+ if(was_seen == 0 && rawno > 0L
+ && stream && rawno <= stream->nmsgs
+ && (mc = mail_elt(stream, rawno)) && mc->seen)
+ mail_flag(stream, long2string(rawno), "\\SEEN", 0);
+ }
+ }
+
+ /* Just for good measure... */
+ mail_free_envelope(&outgoing);
+ pine_free_body(&body);
+
+ return(errstr); /* no problem-o */
+}
+
+
+/*----------------------------------------------------------------------
+ Serve up the current signature within pico for editing
+
+ Args: file to edit
+
+ Result: signature changed or not.
+ ---*/
+char *
+signature_edit(char *sigfile, char *title)
+{
+ int editor_result;
+ char sig_path[MAXPATH+1], errbuf[2000], *errstr = NULL;
+ char *ret = NULL;
+ STORE_S *msgso, *tmpso = NULL;
+ gf_io_t gc, pc;
+ PICO pbf;
+ struct variable *vars = ps_global->vars;
+ REMDATA_S *rd = NULL;
+
+ if(!signature_path(sigfile, sig_path, MAXPATH))
+ return(cpystr(_("No signature file defined.")));
+
+ if(IS_REMOTE(sigfile)){
+ rd = rd_create_remote(RemImap, sig_path, REMOTE_SIG_SUBTYPE,
+ NULL, "Error: ",
+ _("Can't access remote configuration."));
+ if(!rd)
+ return(cpystr(_("Error attempting to edit remote configuration")));
+
+ (void)rd_read_metadata(rd);
+
+ if(rd->access == MaybeRorW){
+ if(rd->read_status == 'R')
+ rd->access = ReadOnly;
+ else
+ rd->access = ReadWrite;
+ }
+
+ if(rd->access != NoExists){
+
+ rd_check_remvalid(rd, 1L);
+
+ /*
+ * If the cached info says it is readonly but
+ * it looks like it's been fixed now, change it to readwrite.
+ */
+ if(rd->read_status == 'R'){
+ rd_check_readonly_access(rd);
+ if(rd->read_status == 'W'){
+ rd->access = ReadWrite;
+ rd->flags |= REM_OUTOFDATE;
+ }
+ else
+ rd->access = ReadOnly;
+ }
+ }
+
+ if(rd->flags & REM_OUTOFDATE){
+ if(rd_update_local(rd) != 0){
+
+ dprint((1,
+ "signature_edit: rd_update_local failed\n"));
+ rd_close_remdata(&rd);
+ return(cpystr(_("Can't access remote sig")));
+ }
+ }
+ else
+ rd_open_remote(rd);
+
+ if(rd->access != ReadWrite || rd_remote_is_readonly(rd)){
+ rd_close_remdata(&rd);
+ return(cpystr(_("Can't get write permission for remote sig")));
+ }
+
+ rd->flags |= DO_REMTRIM;
+
+ strncpy(sig_path, rd->lf, sizeof(sig_path)-1);
+ sig_path[sizeof(sig_path)-1] = '\0';
+ }
+
+ standard_picobuf_setup(&pbf);
+ pbf.tty_fix = PineRaw;
+ pbf.composer_help = h_composer_sigedit;
+ pbf.exittest = sigedit_exit_for_pico;
+ pbf.upload = (VAR_UPLOAD_CMD && VAR_UPLOAD_CMD[0])
+ ? upload_msg_to_pico : NULL;
+ pbf.alt_ed = (VAR_EDITOR && VAR_EDITOR[0] && VAR_EDITOR[0][0])
+ ? VAR_EDITOR : NULL;
+ pbf.alt_spell = (VAR_SPELLER && VAR_SPELLER[0]) ? VAR_SPELLER : NULL;
+ pbf.always_spell_check = F_ON(F_ALWAYS_SPELL_CHECK, ps_global);
+ pbf.strip_ws_before_send = F_ON(F_STRIP_WS_BEFORE_SEND, ps_global);
+ pbf.allow_flowed_text = 0;
+
+ pbf.pine_anchor = set_titlebar(title,
+ ps_global->mail_stream,
+ ps_global->context_current,
+ ps_global->cur_folder,
+ ps_global->msgmap,
+ 0, FolderName, 0, 0, NULL);
+
+ /* NOTE: at this point, alot of pico struct fields are null'd out
+ * thanks to the leading memset; in particular "headents" which tells
+ * pico to behave like a normal editor (though modified slightly to
+ * let the caller dictate the file to edit and such)...
+ */
+
+ if(VAR_OPER_DIR && !in_dir(VAR_OPER_DIR, sig_path)){
+ size_t l;
+
+ l = strlen(VAR_OPER_DIR) + 100;
+ ret = (char *) fs_get((l+1) * sizeof(char));
+ snprintf(ret, l+1, _("Can't edit file outside of %s"), VAR_OPER_DIR);
+ ret[l] = '\0';
+ return(ret);
+ }
+
+ /*
+ * Now alloc and init the text to pass pico
+ */
+ if(!(msgso = so_get(PicoText, NULL, EDIT_ACCESS))){
+ ret = cpystr(_("Error allocating space for file"));
+ dprint((1, "Can't alloc space for signature_edit"));
+ return(ret);
+ }
+ else
+ pbf.msgtext = so_text(msgso);
+
+ if(can_access(sig_path, READ_ACCESS) == 0
+ && !(tmpso = so_get(FileStar, sig_path, READ_ACCESS|READ_FROM_LOCALE))){
+ char *problem = error_description(errno);
+
+ snprintf(errbuf, sizeof(errbuf), _("Error editing \"%s\": %s"),
+ sig_path, problem ? problem : "<NULL>");
+ errbuf[sizeof(errbuf)-1] = '\0';
+ ret = cpystr(errbuf);
+
+ dprint((1, "signature_edit: can't open %s: %s", sig_path,
+ problem ? problem : "<NULL>"));
+ return(ret);
+ }
+ else if(tmpso){ /* else, fill pico's edit buffer */
+ gf_set_so_readc(&gc, tmpso); /* read from file, write pico buf */
+ gf_set_so_writec(&pc, msgso);
+ gf_filter_init(); /* no filters needed */
+ if((errstr = gf_pipe(gc, pc)) != NULL){
+ snprintf(errbuf, sizeof(errbuf), _("Error reading file: \"%s\""), errstr);
+ errbuf[sizeof(errbuf)-1] = '\0';
+ ret = cpystr(errbuf);
+ }
+
+ gf_clear_so_readc(tmpso);
+ gf_clear_so_writec(msgso);
+ so_give(&tmpso);
+ }
+
+ if(!errstr){
+#ifdef _WINDOWS
+ mswin_setwindowmenu (MENU_COMPOSER);
+#endif
+
+ /*------ OK, Go edit the signature ------*/
+ editor_result = pico(&pbf);
+
+#ifdef _WINDOWS
+ mswin_setwindowmenu (MENU_DEFAULT);
+#endif
+ if(editor_result & COMP_GOTHUP){
+ hup_signal(); /* do what's normal for a hup */
+ }
+ else{
+ fix_windsize(ps_global);
+ init_signals();
+ }
+
+ if(editor_result & (COMP_SUSPEND | COMP_GOTHUP | COMP_CANCEL)){
+ }
+ else{
+ /*------ Must have an edited buffer, write it to .sig -----*/
+ our_unlink(sig_path); /* blast old copy */
+ if((tmpso = so_get(FileStar, sig_path, WRITE_ACCESS|WRITE_TO_LOCALE)) != NULL){
+ so_seek(msgso, 0L, 0);
+ gf_set_so_readc(&gc, msgso); /* read from pico buf */
+ gf_set_so_writec(&pc, tmpso); /* write sig file */
+ gf_filter_init(); /* no filters needed */
+ if((errstr = gf_pipe(gc, pc)) != NULL){
+ snprintf(errbuf, sizeof(errbuf), _("Error writing file: \"%s\""),
+ errstr);
+ errbuf[sizeof(errbuf)-1] = '\0';
+ ret = cpystr(errbuf);
+ }
+
+ gf_clear_so_readc(msgso);
+ gf_clear_so_writec(tmpso);
+ if(so_give(&tmpso)){
+ errstr = error_description(errno);
+ snprintf(errbuf, sizeof(errbuf), _("Error writing file: \"%s\""),
+ errstr);
+ errbuf[sizeof(errbuf)-1] = '\0';
+ ret = cpystr(errbuf);
+ }
+
+ if(IS_REMOTE(sigfile)){
+ int e, we_cancel;
+ char datebuf[200];
+
+ datebuf[0] = '\0';
+
+ we_cancel = busy_cue("Copying to remote sig", NULL, 1);
+ if((e = rd_update_remote(rd, datebuf)) != 0){
+ if(e == -1){
+ q_status_message2(SM_ORDER | SM_DING, 3, 5,
+ _("Error opening temporary sig file %s: %s"),
+ rd->lf, error_description(errno));
+ dprint((1,
+ "write_remote_sig: error opening temp file %s\n",
+ rd->lf ? rd->lf : "?"));
+ }
+ else{
+ q_status_message2(SM_ORDER | SM_DING, 3, 5,
+ _("Error copying to %s: %s"),
+ rd->rn, error_description(errno));
+ dprint((1,
+ "write_remote_sig: error copying from %s to %s\n",
+ rd->lf ? rd->lf : "?", rd->rn ? rd->rn : "?"));
+ }
+
+ q_status_message(SM_ORDER | SM_DING, 5, 5,
+ _("Copy of sig to remote folder failed, changes NOT saved remotely"));
+ }
+ else{
+ rd_update_metadata(rd, datebuf);
+ rd->read_status = 'W';
+ }
+
+ rd_close_remdata(&rd);
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+ }
+ }
+ else{
+ snprintf(errbuf, sizeof(errbuf), _("Error writing \"%s\""), sig_path);
+ errbuf[sizeof(errbuf)-1] = '\0';
+ ret = cpystr(errbuf);
+ dprint((1, "signature_edit: can't write %s",
+ sig_path));
+ }
+ }
+ }
+
+ standard_picobuf_teardown(&pbf);
+ so_give(&msgso);
+ return(ret);
+}
+
+
+/*----------------------------------------------------------------------
+ Serve up the current signature within pico for editing
+
+ Args: literal signature to edit
+
+ Result: raw edited signature is returned in result arg
+ ---*/
+char *
+signature_edit_lit(char *litsig, char **result, char *title, HelpType composer_help)
+{
+ int editor_result;
+ char *errstr = NULL;
+ char *ret = NULL;
+ STORE_S *msgso;
+ PICO pbf;
+ struct variable *vars = ps_global->vars;
+
+ standard_picobuf_setup(&pbf);
+ pbf.tty_fix = PineRaw;
+ pbf.search_help = h_sigedit_search;
+ pbf.composer_help = composer_help;
+ pbf.exittest = sigedit_exit_for_pico;
+ pbf.upload = (VAR_UPLOAD_CMD && VAR_UPLOAD_CMD[0])
+ ? upload_msg_to_pico : NULL;
+ pbf.alt_ed = (VAR_EDITOR && VAR_EDITOR[0] && VAR_EDITOR[0][0])
+ ? VAR_EDITOR : NULL;
+ pbf.alt_spell = (VAR_SPELLER && VAR_SPELLER[0]) ? VAR_SPELLER : NULL;
+ pbf.always_spell_check = F_ON(F_ALWAYS_SPELL_CHECK, ps_global);
+ pbf.strip_ws_before_send = F_ON(F_STRIP_WS_BEFORE_SEND, ps_global);
+ pbf.allow_flowed_text = 0;
+
+ pbf.pine_anchor = set_titlebar(title,
+ ps_global->mail_stream,
+ ps_global->context_current,
+ ps_global->cur_folder,
+ ps_global->msgmap,
+ 0, FolderName, 0, 0, NULL);
+
+ /* NOTE: at this point, alot of pico struct fields are null'd out
+ * thanks to the leading memset; in particular "headents" which tells
+ * pico to behave like a normal editor (though modified slightly to
+ * let the caller dictate the file to edit and such)...
+ */
+
+ /*
+ * Now alloc and init the text to pass pico
+ */
+ if(!(msgso = so_get(PicoText, NULL, EDIT_ACCESS))){
+ ret = cpystr(_("Error allocating space"));
+ dprint((1, "Can't alloc space for signature_edit_lit"));
+ return(ret);
+ }
+ else
+ pbf.msgtext = so_text(msgso);
+
+ so_puts(msgso, litsig ? litsig : "");
+
+
+ if(!errstr){
+#ifdef _WINDOWS
+ mswin_setwindowmenu (MENU_COMPOSER);
+#endif
+
+ /*------ OK, Go edit the signature ------*/
+ editor_result = pico(&pbf);
+
+#ifdef _WINDOWS
+ mswin_setwindowmenu (MENU_DEFAULT);
+#endif
+ if(editor_result & COMP_GOTHUP){
+ hup_signal(); /* do what's normal for a hup */
+ }
+ else{
+ fix_windsize(ps_global);
+ init_signals();
+ }
+
+ if(editor_result & (COMP_SUSPEND | COMP_GOTHUP | COMP_CANCEL)){
+ ret = cpystr(_("Edit Cancelled"));
+ }
+ else{
+ /*------ Must have an edited buffer, write it to .sig -----*/
+ unsigned char c;
+ int cnt = 0;
+ char *p;
+
+ so_seek(msgso, 0L, 0);
+ while(so_readc(&c, msgso))
+ cnt++;
+
+ *result = (char *)fs_get((cnt+1) * sizeof(char));
+ p = *result;
+ so_seek(msgso, 0L, 0);
+ while(so_readc(&c, msgso))
+ *p++ = c;
+
+ *p = '\0';
+ }
+ }
+
+ standard_picobuf_teardown(&pbf);
+ so_give(&msgso);
+ return(ret);
+}
+
+
+/*
+ * Returns 0 for Save Changes and exit
+ * 1 for Cancel Exit
+ * -1 exit but Dont Save Changes
+ */
+int
+sigedit_exit_for_pico(struct headerentry *he, void (*redraw_pico)(void), int allow_flowed,
+ char **result)
+{
+ int rv;
+ char *rstr = NULL;
+ void (*redraw)(void) = ps_global->redrawer;
+ static ESCKEY_S opts[] = {
+ {'s', 's', "S", N_("Save changes")},
+ {'d', 'd', "D", N_("Don't save changes")},
+ {-1, 0, NULL, NULL}
+ };
+
+ ps_global->redrawer = redraw_pico;
+ fix_windsize(ps_global);
+
+ while(1){
+ rv = radio_buttons(_("Exit editor? "),
+ -FOOTER_ROWS(ps_global), opts,
+ 's', 'x', h_exit_editor, RB_NORM);
+ if(rv == 's'){ /* user ACCEPTS! */
+ break;
+ }
+ else if(rv == 'd'){ /* Declined! */
+ rstr = _("No Changes Saved");
+ break;
+ }
+ else if(rv == 'x'){ /* Cancelled! */
+ rstr = _("Exit Cancelled");
+ break;
+ }
+ }
+
+ if(result)
+ *result = rstr;
+
+ ps_global->redrawer = redraw;
+ return((rv == 's') ? 0 : (rv == 'd') ? -1 : 1);
+}
+
+
+/*
+ * Common stuff we almost always want to set when calling pico.
+ */
+void
+standard_picobuf_setup(PICO *pbf)
+{
+ memset(pbf, 0, sizeof(*pbf));
+
+ pbf->pine_version = ALPINE_VERSION;
+ pbf->fillcolumn = ps_global->composer_fillcol;
+ pbf->menu_rows = FOOTER_ROWS(ps_global) - 1;
+ pbf->colors = colors_for_pico();
+ pbf->wordseps = user_wordseps(ps_global->VAR_WORDSEPS);
+ pbf->helper = helper;
+ pbf->showmsg = display_message_for_pico;
+ pbf->suspend = do_suspend;
+ pbf->keybinput = cmd_input_for_pico;
+ pbf->tty_fix = ttyfix; /* watch out for this one */
+ pbf->newmail = new_mail_for_pico;
+ pbf->ckptdir = checkpoint_dir_for_pico;
+ pbf->resize = resize_for_pico;
+ pbf->input_cs = ps_global->input_cs;
+ pbf->winch_cleanup = winch_cleanup;
+ pbf->search_help = h_composer_search;
+ pbf->ins_help = h_composer_ins;
+ pbf->ins_m_help = h_composer_ins_m;
+ pbf->composer_help = h_composer;
+ pbf->browse_help = h_composer_browse;
+ pbf->attach_help = h_composer_ctrl_j;
+
+ pbf->pine_flags =
+ ( (F_ON(F_CAN_SUSPEND,ps_global) ? P_SUSPEND : 0L)
+ | (F_ON(F_USE_FK,ps_global) ? P_FKEYS : 0L)
+ | (ps_global->restricted ? P_SECURE : 0L)
+ | (F_ON(F_ALT_ED_NOW,ps_global) ? P_ALTNOW : 0L)
+ | (F_ON(F_USE_CURRENT_DIR,ps_global) ? P_CURDIR : 0L)
+ | (F_ON(F_SUSPEND_SPAWNS,ps_global) ? P_SUBSHELL : 0L)
+ | (F_ON(F_COMPOSE_MAPS_DEL,ps_global) ? P_DELRUBS : 0L)
+ | (F_ON(F_ENABLE_TAB_COMPLETE,ps_global) ? P_COMPLETE : 0L)
+ | (F_ON(F_SHOW_CURSOR,ps_global) ? P_SHOCUR : 0L)
+ | (F_ON(F_DEL_FROM_DOT,ps_global) ? P_DOTKILL : 0L)
+ | (F_ON(F_ENABLE_DOT_FILES,ps_global) ? P_DOTFILES : 0L)
+ | (F_ON(F_ALLOW_GOTO,ps_global) ? P_ALLOW_GOTO : 0L)
+ | (F_ON(F_ENABLE_SEARCH_AND_REPL,ps_global) ? P_REPLACE : 0L)
+ | (!ps_global->pass_ctrl_chars
+ && !ps_global->pass_c1_ctrl_chars ? P_HICTRL : 0L)
+ | ((F_ON(F_ENABLE_ALT_ED,ps_global)
+ || F_ON(F_ALT_ED_NOW,ps_global)
+ || (ps_global->VAR_EDITOR
+ && ps_global->VAR_EDITOR[0]
+ && ps_global->VAR_EDITOR[0][0]))
+ ? P_ADVANCED : 0L)
+ | ((!ps_global->keyboard_charmap
+ || !strucmp(ps_global->keyboard_charmap, "US-ASCII"))
+ ? P_HIBITIGN : 0L));
+
+ if(ps_global->VAR_OPER_DIR){
+ pbf->oper_dir = ps_global->VAR_OPER_DIR;
+ pbf->pine_flags |= P_TREE;
+ }
+
+ pbf->home_dir = ps_global->home_dir;
+}
+
+
+void
+standard_picobuf_teardown(PICO *pbf)
+{
+ if(pbf){
+ if(pbf->colors)
+ free_pcolors(&pbf->colors);
+
+ if(pbf->wordseps)
+ fs_give((void **) &pbf->wordseps);
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Call back for pico to use to check for new mail.
+
+Args: cursor -- pointer to in to tell caller if cursor location changed
+ if NULL, turn off cursor positioning.
+ timing -- whether or not it's a good time to check
+
+
+Returns: returns 1 on success, zero on error.
+----*/
+long
+new_mail_for_pico(int timing, int status)
+{
+ /*
+ * If we're not interested in the status, don't display the busy
+ * cue either...
+ */
+ /* don't know where the cursor's been, reset it */
+ clear_cursor_pos();
+ return(new_mail(0, timing,
+ (status ? NM_STATUS_MSG : NM_NONE) | NM_DEFER_SORT
+ | NM_FROM_COMPOSER));
+}
+
+
+void
+cmd_input_for_pico(void)
+{
+ zero_new_mail_count();
+}
+
+
+/*----------------------------------------------------------------------
+ Call back for pico to get newmail status messages displayed
+
+Args: x -- char processed
+
+Returns:
+----*/
+int
+display_message_for_pico(int x)
+{
+ int rv;
+
+ clear_cursor_pos(); /* can't know where cursor is */
+ mark_status_dirty(); /* don't count on cached text */
+ fix_windsize(ps_global);
+ init_sigwinch();
+ display_message(x);
+ rv = ps_global->mangled_screen;
+ ps_global->mangled_screen = 0;
+ return(rv);
+}
+
+
+/*----------------------------------------------------------------------
+ Call back for pico to get desired directory for its check point file
+
+ Args: s -- buffer to write directory name
+ n -- length of that buffer
+
+ Returns: pointer to static buffer
+----*/
+char *
+checkpoint_dir_for_pico(char *s, size_t n)
+{
+#if defined(DOS) || defined(OS2)
+ /*
+ * we can't assume anything about root or home dirs, so
+ * just plunk it down in the same place as the pinerc
+ */
+ if(!getenv("HOME")){
+ char *lc = last_cmpnt(ps_global->pinerc);
+
+ if(lc != NULL){
+ strncpy(s, ps_global->pinerc, MIN(n-1,lc-ps_global->pinerc));
+ s[MIN(n-1,lc-ps_global->pinerc)] = '\0';
+ }
+ else{
+ strncpy(s, ".\\", n-1);
+ s[n-1] = '\0';
+ }
+ }
+ else
+#endif
+ strncpy(s, ps_global->home_dir, n-1);
+ s[n-1] = '\0';
+
+ return(s);
+}
+
+
+/*----------------------------------------------------------------------
+ Call back for pico to tell us the window size's changed
+
+ Args: none
+
+ Returns: none (but pine's ttyo structure may have been updated)
+----*/
+void
+resize_for_pico(void)
+{
+ fix_windsize(ps_global);
+}
+
+
+PCOLORS *
+colors_for_pico(void)
+{
+ PCOLORS *colors = NULL;
+ struct variable *vars = ps_global->vars;
+
+ if (pico_usingcolor()){
+ colors = (PCOLORS *)fs_get(sizeof(PCOLORS));
+
+ colors->tbcp = current_titlebar_color();
+
+ if (VAR_KEYLABEL_FORE_COLOR && VAR_KEYLABEL_BACK_COLOR){
+ colors->klcp = new_color_pair(VAR_KEYLABEL_FORE_COLOR,
+ VAR_KEYLABEL_BACK_COLOR);
+ if (!pico_is_good_colorpair(colors->klcp))
+ free_color_pair(&colors->klcp);
+ }
+ else colors->klcp = NULL;
+
+ if (colors->klcp && VAR_KEYNAME_FORE_COLOR && VAR_KEYNAME_BACK_COLOR){
+ colors->kncp = new_color_pair(VAR_KEYNAME_FORE_COLOR,
+ VAR_KEYNAME_BACK_COLOR);
+ }
+ else colors->kncp = NULL;
+
+ if (VAR_STATUS_FORE_COLOR && VAR_STATUS_BACK_COLOR){
+ colors->stcp = new_color_pair(VAR_STATUS_FORE_COLOR,
+ VAR_STATUS_BACK_COLOR);
+ }
+ else colors->stcp = NULL;
+
+ if (VAR_PROMPT_FORE_COLOR && VAR_PROMPT_BACK_COLOR){
+ colors->prcp = new_color_pair(VAR_PROMPT_FORE_COLOR,
+ VAR_PROMPT_BACK_COLOR);
+ }
+ else colors->prcp = NULL;
+ }
+
+ return colors;
+}
+
+
+void
+free_pcolors(PCOLORS **colors)
+{
+ if (*colors){
+ if ((*colors)->tbcp)
+ free_color_pair(&(*colors)->tbcp);
+ if ((*colors)->kncp)
+ free_color_pair(&(*colors)->kncp);
+ if ((*colors)->klcp)
+ free_color_pair(&(*colors)->klcp);
+ if ((*colors)->stcp)
+ free_color_pair(&(*colors)->stcp);
+ if ((*colors)->prcp)
+ free_color_pair(&(*colors)->prcp);
+ fs_give((void **)colors);
+ fs_give((void **)colors);
+ *colors = NULL;
+ }
+}
diff --git a/alpine/reply.h b/alpine/reply.h
new file mode 100644
index 00000000..2c239071
--- /dev/null
+++ b/alpine/reply.h
@@ -0,0 +1,44 @@
+/*
+ * $Id: reply.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_REPLY_INCLUDED
+#define PINE_REPLY_INCLUDED
+
+
+#include "../pith/reply.h"
+#include "../pith/state.h"
+#include "../pith/pattern.h"
+#include "../pith/store.h"
+
+
+/* exported protoypes */
+int reply(struct pine *, ACTION_S *);
+int confirm_role(long, ACTION_S **);
+int reply_to_all_query(int *);
+int reply_using_replyto_query(void);
+int reply_text_query(struct pine *, long, char **);
+int reply_news_test(ENVELOPE *, ENVELOPE *);
+char *get_signature_file(char *, int, int, int);
+int forward(struct pine *, ACTION_S *);
+void forward_text(struct pine *, void *, SourceType);
+int bounce(struct pine *, ACTION_S *);
+char *bounce_msg(MAILSTREAM *, long, char *, ACTION_S *, char **, char *, char *, char *);
+char *signature_edit(char *, char *);
+char *signature_edit_lit(char *, char **, char *, HelpType);
+void standard_picobuf_setup(PICO *);
+void standard_picobuf_teardown(PICO *);
+
+
+#endif /* PINE_REPLY_INCLUDED */
diff --git a/alpine/roleconf.c b/alpine/roleconf.c
new file mode 100644
index 00000000..fad1e661
--- /dev/null
+++ b/alpine/roleconf.c
@@ -0,0 +1,8187 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: roleconf.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "headers.h"
+#include "roleconf.h"
+#include "colorconf.h"
+#include "conftype.h"
+#include "confscroll.h"
+#include "keymenu.h"
+#include "status.h"
+#include "radio.h"
+#include "reply.h"
+#include "folder.h"
+#include "addrbook.h"
+#include "mailcmd.h"
+#include "setup.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/msgno.h"
+#include "../pith/bitmap.h"
+#include "../pith/sort.h"
+#include "../pith/addrstring.h"
+#include "../pith/list.h"
+#include "../pith/flag.h"
+#include "../pith/bldaddr.h"
+#include "../pith/news.h"
+#include "../pith/util.h"
+#include "../pith/detoken.h"
+#include "../pith/icache.h"
+#include "../pith/ablookup.h"
+#include "../pith/pattern.h"
+#include "../pith/tempfile.h"
+
+
+#define NOT "! "
+#define NOTLEN 2
+
+
+#define ARB_HELP _("HELP FOR ARBITRARY HEADER PATTERNS")
+#define ADDXHDRS _("Add Extra Headers")
+
+
+/*
+ * Internal prototypes
+ */
+int role_select_tool(struct pine *, int, CONF_S **, unsigned);
+PATTERN_S *addrlst_to_pattern(ADDRESS *);
+void role_config_init_disp(struct pine *, CONF_S **, long, PAT_STATE *);
+void add_patline_to_display(struct pine *, CONF_S **, int, CONF_S **, CONF_S **, PAT_LINE_S *, long);
+void add_role_to_display(CONF_S **, PAT_LINE_S *, PAT_S *, int, CONF_S **, int, long);
+void add_fake_first_role(CONF_S **, int, long);
+int role_config_tool(struct pine *, int, CONF_S **, unsigned);
+int role_config_add(struct pine *, CONF_S **, long);
+int role_config_replicate(struct pine *, CONF_S **, long);
+int role_config_edit(struct pine *, CONF_S **, long);
+int role_config_del(struct pine *, CONF_S **, long);
+void delete_a_role(CONF_S **, long);
+int role_config_shuffle(struct pine *, CONF_S **);
+int role_config_addfile(struct pine *, CONF_S **, long);
+int role_config_delfile(struct pine *, CONF_S **, long);
+void swap_literal_roles(CONF_S *, CONF_S *);
+void swap_file_roles(CONF_S *, CONF_S *);
+void move_role_into_file(CONF_S **, int);
+void move_role_outof_file(CONF_S **, int);
+void move_role_around_file(CONF_S **, int);
+int role_config_edit_screen(struct pine *, PAT_S *, char *, long, PAT_S **);
+void setup_dummy_pattern_var(struct variable *, char *, PATTERN_S *);
+void setup_role_pat(struct pine *, CONF_S **, struct variable *, HelpType, char *,
+ struct key_menu *,
+ int (*tool)(struct pine *, int, CONF_S **, unsigned),
+ EARB_S **, int);
+void setup_role_pat_alt(struct pine *, CONF_S **, struct variable *, HelpType, char *,
+ struct key_menu *,
+ int (*tool)(struct pine *, int, CONF_S **, unsigned),
+ int, int);
+void free_earb(EARB_S **);
+void calculate_inick_stuff(struct pine *);
+int check_role_folders(char **, unsigned);
+void maybe_add_to_incoming(CONTEXT_S *, char *);
+int role_filt_exitcheck(CONF_S **, unsigned);
+int role_filt_text_tool(struct pine *, int, CONF_S **, unsigned);
+int role_filt_addhdr_tool(struct pine *, int, CONF_S **, unsigned);
+int role_addhdr_tool(struct pine *, int, CONF_S **, unsigned);
+int role_filt_radiobutton_tool(struct pine *, int, CONF_S **, unsigned);
+int role_sort_tool(struct pine *, int, CONF_S **, unsigned);
+char **get_role_specific_folder(CONF_S **);
+int role_litsig_text_tool(struct pine *, int, CONF_S **, unsigned);
+int role_cstm_text_tool(struct pine *, int, CONF_S **, unsigned);
+int role_text_tool(struct pine *, int, CONF_S **, unsigned);
+int role_text_tool_inick(struct pine *, int, CONF_S **, unsigned);
+int role_text_tool_kword(struct pine *, int, CONF_S **, unsigned);
+int role_text_tool_charset(struct pine *, int, CONF_S **, unsigned);
+int role_text_tool_afrom(struct pine *, int, CONF_S **, unsigned);
+char *role_type_print(char *, size_t, char *, long);
+int feat_checkbox_tool(struct pine *, int, CONF_S **, unsigned);
+void toggle_feat_option_bit(struct pine *, int, struct variable *, char *);
+NAMEVAL_S *feat_feature_list(int);
+int inabook_checkbox_tool(struct pine *, int, CONF_S **, unsigned);
+void toggle_inabook_type_bit(struct pine *, int, struct variable *, char *);
+NAMEVAL_S *inabook_feature_list(int);
+
+
+static char *set_choose = "--- --------------------";
+static long role_global_flags;
+static PAT_STATE *role_global_pstate;
+
+
+int
+role_select_screen(struct pine *ps, ACTION_S **role, int alt_compose)
+{
+ CONF_S *ctmp = NULL, *first_line = NULL;
+ OPT_SCREEN_S screen;
+ PAT_S *pat, *sel_pat = NULL;
+ int ret = -1;
+ int change_default = 0;
+ long rflags = ROLE_DO_ROLES;
+ char *helptitle;
+ HelpType help;
+ PAT_STATE pstate;
+
+ if(!role)
+ return(ret);
+
+ *role = NULL;
+
+ if(!(nonempty_patterns(rflags, &pstate) &&
+ first_pattern(&pstate))){
+ q_status_message(SM_ORDER, 3, 3,
+ _("No roles available. Use Setup/Rules to add roles."));
+ return(ret);
+ }
+
+
+ if(alt_compose){
+ menu_init_binding(&role_select_km,
+ alt_compose == MC_FORWARD ? 'F' :
+ alt_compose == MC_REPLY ? 'R' :
+ alt_compose == MC_COMPOSE ? 'C' : 'B',
+ MC_CHOICE,
+ alt_compose == MC_FORWARD ? "F" :
+ alt_compose == MC_REPLY ? "R" :
+ alt_compose == MC_COMPOSE ? "C" : "B",
+ alt_compose == MC_FORWARD ? "[" N_("ForwardAs") "]" :
+ alt_compose == MC_REPLY ? "[" N_("ReplyAs") "]" :
+ alt_compose == MC_COMPOSE ? "[" N_("ComposeAs") "]" : "[" N_("BounceAs") "]",
+ DEFAULT_KEY);
+ menu_add_binding(&role_select_km, ctrl('J'), MC_CHOICE);
+ menu_add_binding(&role_select_km, ctrl('M'), MC_CHOICE);
+ }
+ else{
+ menu_init_binding(&role_select_km, 'S', MC_CHOICE, "S", "[" N_("Select") "]",
+ DEFAULT_KEY);
+ menu_add_binding(&role_select_km, ctrl('J'), MC_CHOICE);
+ menu_add_binding(&role_select_km, ctrl('M'), MC_CHOICE);
+ }
+
+ help = h_role_select;
+ if(alt_compose == MC_BOUNCE)
+ helptitle = _("HELP FOR SELECTING A ROLE TO BOUNCE AS");
+ else if(alt_compose)
+ helptitle = _("HELP FOR SELECTING A ROLE TO COMPOSE AS");
+ else
+ helptitle = _("HELP FOR SELECTING A ROLE");
+
+ menu_init_binding(&role_select_km, 'D', MC_TOGGLE, "D", "changeDef", CHANGEDEF_KEY);
+
+ for(pat = first_pattern(&pstate);
+ pat;
+ pat = next_pattern(&pstate)){
+ new_confline(&ctmp);
+ if(!first_line)
+ first_line = ctmp;
+
+ ctmp->value = cpystr((pat->patgrp && pat->patgrp->nick)
+ ? pat->patgrp->nick : "?");
+ ctmp->d.r.selected = &sel_pat;
+ ctmp->d.r.pat = pat;
+ ctmp->d.r.change_def = &change_default;
+ ctmp->keymenu = &role_select_km;
+ ctmp->help = help;
+ ctmp->help_title = helptitle;
+ ctmp->tool = role_select_tool;
+ ctmp->flags = CF_STARTITEM;
+ ctmp->valoffset = 4;
+ }
+
+ memset(&screen, 0, sizeof(screen));
+ /* TRANSLATORS: Print something1 using something2.
+ "roles" is something1 */
+ (void)conf_scroll_screen(ps, &screen, first_line, _("SELECT ROLE"),
+ _("roles"), 0);
+
+ if(sel_pat){
+ *role = sel_pat->action;
+ if(change_default == 1)
+ ps_global->default_role = *role;
+ else if(change_default == 2)
+ ps_global->default_role = NULL;
+
+ ret = 0;
+ }
+
+ ps->mangled_screen = 1;
+ return(ret);
+}
+
+
+int
+role_select_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ int retval = 0, newval;
+
+ switch(cmd){
+ case MC_CHOICE :
+ *((*cl)->d.r.selected) = (*cl)->d.r.pat;
+ retval = simple_exit_cmd(flags);
+ break;
+
+ case MC_TOGGLE :
+ newval = (*((*cl)->d.r.change_def) + 1) % 3;
+ *((*cl)->d.r.change_def) = newval;
+ menu_init_binding((*cl)->keymenu, 'D', MC_TOGGLE, "D",
+ (newval == 0) ? "changeDef" : (newval == 1) ? "removeDef" : "leaveDef",
+ CHANGEDEF_KEY);
+ if(newval == 1){
+ if(ps_global->default_role)
+ q_status_message(SM_ORDER, 0, 3,
+ _("Default role will be changed to the role you Select"));
+ else
+ q_status_message(SM_ORDER, 0, 3,
+ _("Default role will be set to the role you Select"));
+ }
+ else if(newval == 2){
+ q_status_message(SM_ORDER, 0, 3, _("Default role will be unset"));
+ }
+ else{ /* newval == 0 */
+ if(ps_global->default_role)
+ q_status_message(SM_ORDER, 0, 3, _("Default role will remain unchanged"));
+ else
+ q_status_message(SM_ORDER, 0, 3, _("Default role will remain unset"));
+ }
+
+ ps->mangled_footer = 1;
+ retval = 0;
+ break;
+
+ case MC_EXIT :
+ retval = simple_exit_cmd(flags);
+ break;
+
+ default:
+ retval = -1;
+ break;
+ }
+
+ if(retval > 0)
+ ps->mangled_body = 1;
+
+ return(retval);
+}
+
+
+void
+role_config_screen(struct pine *ps, long int rflags, int edit_exceptions)
+{
+ CONF_S *first_line;
+ OPT_SCREEN_S screen;
+ char title[100];
+ int readonly_warning = 0;
+ PAT_STATE pstate;
+ struct variable *v = NULL;
+
+ dprint((4, "role_config_screen()\n"));
+
+ if(ps->fix_fixed_warning)
+ offer_to_fix_pinerc(ps);
+
+ ew = edit_exceptions ? ps_global->ew_for_except_vars : Main;
+
+ if(ps->restricted)
+ readonly_warning = 1;
+ else{
+ PINERC_S *prc = NULL;
+
+ switch(ew){
+ case Main:
+ prc = ps->prc;
+ rflags |= PAT_USE_MAIN;
+ break;
+ case Post:
+ prc = ps->post_prc;
+ rflags |= PAT_USE_POST;
+ break;
+ default:
+ break;
+ }
+
+ readonly_warning = prc ? prc->readonly : 1;
+ if(prc && prc->quit_to_edit){
+ quit_to_edit_msg(prc);
+ return;
+ }
+ }
+
+ if(!any_patterns(rflags, &pstate))
+ return;
+
+ if(rflags & ROLE_DO_ROLES)
+ v = &ps_global->vars[V_PAT_ROLES];
+ else if(rflags & ROLE_DO_INCOLS)
+ v = &ps_global->vars[V_PAT_INCOLS];
+ else if(rflags & ROLE_DO_OTHER)
+ v = &ps_global->vars[V_PAT_OTHER];
+ else if(rflags & ROLE_DO_SCORES)
+ v = &ps_global->vars[V_PAT_SCORES];
+ else if(rflags & ROLE_DO_FILTER)
+ v = &ps_global->vars[V_PAT_FILTS];
+ else if(rflags & ROLE_DO_SRCH)
+ v = &ps_global->vars[V_PAT_SRCH];
+
+ if((ps_global->ew_for_except_vars != Main) && (ew == Main)){
+ char **lval;
+
+ if((lval=LVAL(v, ps_global->ew_for_except_vars)) &&
+ lval[0] && strcmp(INHERIT, lval[0]) != 0){
+ role_type_print(title, sizeof(title), _("Warning: \"%sRules\" are overridden in your exceptions configuration"), rflags);
+ q_status_message(SM_ORDER, 7, 7, title);
+ }
+ }
+
+ role_type_print(title, sizeof(title), "%sRules", rflags);
+ if(fixed_var(v, "change", title))
+ return;
+
+uh_oh:
+ first_line = NULL;
+
+ snprintf(title, sizeof(title), "SETUP%s ", edit_exceptions ? " EXCEPTIONAL" : "");
+ title[sizeof(title)-1] = '\0';
+ role_type_print(title+strlen(title), sizeof(title)-strlen(title), "%sRULES", rflags);
+ role_global_flags = rflags;
+ role_global_pstate = &pstate;
+ role_config_init_disp(ps, &first_line, rflags, &pstate);
+
+ if(!first_line){
+ role_global_flags = 0;
+ ps->mangled_screen = 1;
+ q_status_message(SM_ORDER,5,5,
+ _("Unexpected problem: config file modified externally?"));
+ q_status_message1(SM_ORDER,5,5,
+ _("Perhaps a newer version of pine was used to set variable \"%s\"?"),
+ v ? v->name : "?");
+ dprint((1, "Unexpected problem: config file modified externally?\nPerhaps by a newer pine? Variable \"%s\" has unexpected contents.\n",
+ (v && v->name) ? v->name : "?"));
+ return;
+ }
+
+ memset(&screen, 0, sizeof(screen));
+ screen.deferred_ro_warning = readonly_warning;
+ /* TRANSLATORS: Print something1 using something2.
+ "rules" is something1 */
+ switch(conf_scroll_screen(ps, &screen, first_line, title, _("rules"), 0)){
+ case 0:
+ break;
+
+ case 10:
+ /* flush changes and re-read orig */
+ close_patterns(rflags);
+ break;
+
+ case 1:
+ if(write_patterns(rflags))
+ goto uh_oh;
+
+ /*
+ * Flush out current_vals of anything we've possibly changed.
+ */
+
+ if(ps_global->default_role){
+ q_status_message(SM_ORDER,0,3, "Default role is unset");
+ ps_global->default_role = NULL;
+ }
+
+ close_patterns((rflags & ROLE_MASK) | PAT_USE_CURRENT);
+
+ /* scores may have changed */
+ if(rflags & ROLE_DO_SCORES){
+ int i;
+ MAILSTREAM *m;
+
+ for(i = 0; i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ if(m){
+ clear_folder_scores(m);
+ clear_index_cache(m, 0);
+ }
+ }
+
+ if(mn_get_sort(sp_msgmap(ps_global->mail_stream)) == SortScore)
+ refresh_sort(ps_global->mail_stream,
+ sp_msgmap(ps_global->mail_stream), SRT_VRB);
+ }
+
+ /* recalculate need for scores */
+ scores_are_used(SCOREUSE_INVALID);
+
+ /* we may want to fetch more or fewer headers each fetch */
+ calc_extra_hdrs();
+ if(get_extra_hdrs())
+ (void) mail_parameters(NULL, SET_IMAPEXTRAHEADERS,
+ (void *) get_extra_hdrs());
+
+ if(rflags & ROLE_DO_INCOLS && pico_usingcolor())
+ clear_index_cache(ps_global->mail_stream, 0);
+
+ if(rflags & ROLE_DO_FILTER)
+ role_process_filters();
+
+ /*
+ * ROLE_DO_OTHER is made up of a bunch of different variables
+ * that may have changed. Assume they all changed and fix them.
+ */
+ if(rflags & ROLE_DO_OTHER){
+ reset_index_format();
+ clear_index_cache(ps_global->mail_stream, 0);
+ if(!mn_get_mansort(ps_global->msgmap))
+ reset_sort_order(SRT_VRB);
+ }
+
+ break;
+
+ default:
+ q_status_message(SM_ORDER,7,10, "conf_scroll_screen unexpected ret");
+ break;
+ }
+
+ role_global_flags = 0;
+ ps->mangled_screen = 1;
+}
+
+
+/*
+ * This is called from process_cmd to add a new pattern to the end of the
+ * list of patterns. The pattern is seeded with values from the current
+ * message.
+ */
+void
+role_take(struct pine *ps, MSGNO_S *msgmap, int rtype)
+{
+ PAT_S *defpat, *newpat = NULL;
+ PAT_LINE_S *new_patline, *patline;
+ ENVELOPE *env = NULL;
+ long rflags;
+ char *s, title[100], specific_fldr[MAXPATH+1];
+ PAT_STATE pstate;
+ EditWhich ew;
+
+ dprint((4, "role_take()\n"));
+
+ if(mn_get_cur(msgmap) > 0){
+ env = pine_mail_fetchstructure(ps->mail_stream,
+ mn_m2raw(msgmap, mn_get_cur(msgmap)),
+ NULL);
+
+ if(!env){
+ q_status_message(SM_ORDER, 3, 7,
+ _("problem getting addresses from message"));
+ return;
+ }
+ }
+
+ switch(rtype){
+ case 'r':
+ rflags = ROLE_DO_ROLES;
+ ew = ps_global->ew_for_role_take;
+ break;
+ case 's':
+ rflags = ROLE_DO_SCORES;
+ ew = ps_global->ew_for_score_take;
+ break;
+ case 'i':
+ rflags = ROLE_DO_INCOLS;
+ ew = ps_global->ew_for_incol_take;
+ break;
+ case 'f':
+ rflags = ROLE_DO_FILTER;
+ ew = ps_global->ew_for_filter_take;
+ break;
+ case 'o':
+ rflags = ROLE_DO_OTHER;
+ ew = ps_global->ew_for_other_take;
+ break;
+ case 'c':
+ rflags = ROLE_DO_SRCH;
+ ew = ps_global->ew_for_srch_take;
+ break;
+
+ default:
+ cmd_cancelled(NULL);
+ return;
+ }
+
+ switch(ew){
+ case Main:
+ rflags |= PAT_USE_MAIN;
+ break;
+ case Post:
+ rflags |= PAT_USE_POST;
+ break;
+ default:
+ break;
+ }
+
+ if(!any_patterns(rflags, &pstate)){
+ q_status_message(SM_ORDER, 3, 7, _("problem accessing rules"));
+ return;
+ }
+
+ /* set this so that even if we don't edit at all, we'll be asked */
+ rflags |= ROLE_CHANGES;
+
+ /*
+ * Make a pattern out of the information in the envelope and
+ * use that as the default pattern we give to the role editor.
+ * It will have a pattern but no actions set.
+ */
+ defpat = (PAT_S *)fs_get(sizeof(*defpat));
+ memset((void *)defpat, 0, sizeof(*defpat));
+
+ defpat->patgrp = (PATGRP_S *)fs_get(sizeof(*defpat->patgrp));
+ memset((void *)defpat->patgrp, 0, sizeof(*defpat->patgrp));
+
+ if(env){
+ if(env->to)
+ defpat->patgrp->to = addrlst_to_pattern(env->to);
+
+ if(env->from)
+ defpat->patgrp->from = addrlst_to_pattern(env->from);
+
+ if(env->cc)
+ defpat->patgrp->cc = addrlst_to_pattern(env->cc);
+
+ if(env->sender &&
+ (!env->from || !address_is_same(env->sender, env->from)))
+ defpat->patgrp->sender = addrlst_to_pattern(env->sender);
+
+ /*
+ * Env->newsgroups is already comma-separated and there shouldn't be
+ * any commas or backslashes in newsgroup names, so we don't add the
+ * roletake escapes.
+ */
+ if(env->newsgroups)
+ defpat->patgrp->news = string_to_pattern(env->newsgroups);
+
+ /*
+ * Subject may have commas or backslashes, so we add escapes.
+ */
+ if(env->subject){
+ char *q, *t = NULL;
+
+ /*
+ * Mail_strip_subject not only strips the Re's and Fwd's but
+ * it also canonicalizes to UTF-8.
+ */
+ mail_strip_subject(env->subject, &q);
+ if(q != NULL){
+ t = add_roletake_escapes(q);
+ fs_give((void **)&q);
+ }
+
+ if(t){
+ defpat->patgrp->subj = string_to_pattern(t);
+ fs_give((void **)&t);
+ }
+ }
+ }
+
+ if(IS_NEWS(ps->mail_stream))
+ defpat->patgrp->fldr_type = FLDR_NEWS;
+ else
+ defpat->patgrp->fldr_type = FLDR_EMAIL;
+
+ specific_fldr[0] = specific_fldr[sizeof(specific_fldr)-1] = '\0';
+ if(sp_flagged(ps->mail_stream, SP_INBOX))
+ strncpy(specific_fldr, ps_global->inbox_name, sizeof(specific_fldr)-1);
+ else if(ps->context_current
+ && ps->context_current->use & CNTXT_INCMNG &&
+ folder_is_nick(ps->cur_folder, FOLDERS(ps->context_current), 0))
+ strncpy(specific_fldr, ps->cur_folder, sizeof(specific_fldr)-1);
+ else
+ context_apply(specific_fldr, ps->context_current, ps->cur_folder,
+ sizeof(specific_fldr));
+
+ if(specific_fldr[0]){
+ s = add_comma_escapes(specific_fldr);
+ if(s){
+ if(rtype == 'f')
+ defpat->patgrp->fldr_type = FLDR_SPECIFIC;
+
+ defpat->patgrp->folder = string_to_pattern(s);
+ fs_give((void **)&s);
+ }
+ }
+
+ role_type_print(title, sizeof(title), "ADD NEW %sRULE", rflags);
+
+ /*
+ * Role_config_edit_screen is sometimes called as a tool or a sub
+ * routine called from a tool within conf_scroll_screen, but here it
+ * is going to be at the top-level (we're not inside conf_scroll_screen
+ * right now). It uses opt_screen to set the ro_warning bit. We need
+ * to let it know that we're at the top, which we do by setting
+ * opt_screen to NULL. Otherwise, the thing that opt_screen is pointing
+ * to is just random stack stuff from some previous conf_scroll_screen
+ * call which has already exited.
+ */
+ opt_screen = NULL;
+
+ if(role_config_edit_screen(ps, defpat, title, rflags,
+ &newpat) == 1 && newpat){
+
+ if(ps->never_allow_changing_from && newpat->action &&
+ newpat->action->from)
+ q_status_message(SM_ORDER|SM_DING, 3, 7,
+ _("Site policy doesn't allow changing From address so From is ignored"));
+
+ if(rflags & ROLE_DO_ROLES && newpat->patgrp && newpat->patgrp->nick){
+ PAT_S *pat;
+
+ for(pat = first_pattern(&pstate);
+ pat;
+ pat = next_pattern(&pstate)){
+ if(pat->patgrp && pat->patgrp->nick &&
+ !strucmp(pat->patgrp->nick, newpat->patgrp->nick)){
+ q_status_message(SM_ORDER|SM_DING, 3, 7, _("Warning: The nickname of the new role is already in use."));
+ break;
+ }
+ }
+ }
+
+
+ set_pathandle(rflags);
+
+ /* need a new patline */
+ new_patline = (PAT_LINE_S *)fs_get(sizeof(*new_patline));
+ memset((void *)new_patline, 0, sizeof(*new_patline));
+ new_patline->type = Literal;
+ (*cur_pat_h)->dirtypinerc = 1;
+
+ /* tie together with new pattern */
+ new_patline->first = new_patline->last = newpat;
+ newpat->patline = new_patline;
+
+ /* find last current patline */
+ for(patline = (*cur_pat_h)->patlinehead;
+ patline && patline->next;
+ patline = patline->next)
+ ;
+
+ /* add new patline to end of list */
+ if(patline){
+ patline->next = new_patline;
+ new_patline->prev = patline;
+ }
+ else
+ (*cur_pat_h)->patlinehead = new_patline;
+
+ if(write_patterns(rflags) == 0){
+ char msg[60];
+
+ /*
+ * Flush out current_vals of anything we've possibly changed.
+ */
+
+ if(rflags & ROLE_DO_ROLES && ps_global->default_role){
+ q_status_message(SM_ORDER,0,3, "Default role is unset");
+ ps_global->default_role = NULL;
+ }
+
+ close_patterns(rflags | PAT_USE_CURRENT);
+
+ role_type_print(msg, sizeof(msg), "New %srule saved", rflags);
+ q_status_message(SM_ORDER, 0, 3, msg);
+
+ /* scores may have changed */
+ if(rflags & ROLE_DO_SCORES){
+ int i;
+ MAILSTREAM *m;
+
+ for(i = 0; i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ if(m){
+ clear_folder_scores(m);
+ clear_index_cache(m, 0);
+ }
+ }
+
+ /* We've already bound msgmap to global mail_stream
+ * at the start of this function, but if we wanted to
+ * we could clean this up.
+ */
+ if(mn_get_sort(msgmap) == SortScore)
+ refresh_sort(ps_global->mail_stream, msgmap, SRT_VRB);
+ }
+
+ if(rflags & ROLE_DO_FILTER)
+ role_process_filters();
+
+ /* recalculate need for scores */
+ scores_are_used(SCOREUSE_INVALID);
+
+ /* we may want to fetch more or fewer headers each fetch */
+ calc_extra_hdrs();
+ if(get_extra_hdrs())
+ (void) mail_parameters(NULL, SET_IMAPEXTRAHEADERS,
+ (void *) get_extra_hdrs());
+
+ if(rflags & ROLE_DO_INCOLS && pico_usingcolor())
+ clear_index_cache(ps_global->mail_stream, 0);
+
+ /*
+ * ROLE_DO_OTHER is made up of a bunch of different variables
+ * that may have changed. Assume they all changed and fix them.
+ */
+ if(rflags & ROLE_DO_OTHER){
+ reset_index_format();
+ clear_index_cache(ps_global->mail_stream, 0);
+ if(!mn_get_mansort(msgmap))
+ reset_sort_order(SRT_VRB);
+ }
+ }
+ }
+ else
+ cmd_cancelled(NULL);
+
+ free_pat(&defpat);
+ ps->mangled_screen = 1;
+}
+
+
+PATTERN_S *
+addrlst_to_pattern(struct mail_address *addr)
+{
+ char *s, *t, *u, *v;
+ PATTERN_S *p = NULL;
+ size_t l;
+
+ if(addr){
+ l = est_size(addr);
+ t = s = (char *) fs_get((l+1) * sizeof(char));
+ s[0] = '\0';
+ while(addr){
+ u = simple_addr_string(addr, tmp_20k_buf, SIZEOF_20KBUF);
+ v = add_roletake_escapes(u);
+ if(v){
+ if(*v && t != s)
+ sstrncpy(&t, ",", l-(t-s));
+
+ sstrncpy(&t, v, l-(t-s));
+ fs_give((void **)&v);
+ }
+
+ addr = addr->next;
+ }
+
+ s[l] = '\0';
+
+ if(*s)
+ p = string_to_pattern(s);
+
+ fs_give((void **) &s);
+ }
+
+ return(p);
+}
+
+
+void
+role_config_init_disp(struct pine *ps, CONF_S **first_line, long int rflags, PAT_STATE *pstate)
+{
+ PAT_LINE_S *patline;
+ CONF_S *ctmp = NULL;
+ int inherit = 0, added_fake = 0;
+
+ if(first_line)
+ *first_line = NULL;
+
+ /*
+ * Set cur_pat_h and manipulate directly.
+ */
+ set_pathandle(rflags);
+ patline = *cur_pat_h ? (*cur_pat_h)->patlinehead : NULL;
+ if(patline && patline->type == Inherit){
+ add_patline_to_display(ps, &ctmp, 0, first_line, NULL, patline, rflags);
+ patline = patline->next;
+ }
+
+ if(!patline){
+ add_fake_first_role(&ctmp, 0, rflags);
+ added_fake++;
+ if(first_line && !*first_line)
+ (*first_line) = ctmp;
+ }
+
+ for(; patline; patline = patline->next)
+ add_patline_to_display(ps, &ctmp, 0, first_line, NULL, patline, rflags);
+
+ /*
+ * If there are no actual patterns so far, we need to have an Add line
+ * for the cursor to be on. This would happen if all of the patlines
+ * were File includes and none of the files contained patterns.
+ */
+ if(!first_pattern(role_global_pstate) ||
+ ((inherit=first_pattern(role_global_pstate)->inherit) &&
+ !next_pattern(role_global_pstate))){
+
+ /*
+ * Find the start and prepend the fake first role.
+ */
+ while(ctmp && ctmp->prev)
+ ctmp = ctmp->prev;
+
+ if(!added_fake){
+ add_fake_first_role(&ctmp, inherit ? 0 : 1, rflags);
+ if(first_line && !*first_line)
+ (*first_line) = ctmp;
+ }
+ }
+}
+
+
+void
+add_patline_to_display(struct pine *ps, CONF_S **ctmp, int before, CONF_S **first_line, CONF_S **top_line, PAT_LINE_S *patline, long int rflags)
+{
+ PAT_S *pat;
+ int len, firstitem, wid;
+ char *q;
+ char buf[6*MAX_SCREEN_COLS+1];
+
+ /* put dashed line around file contents */
+ if(patline->type == File){
+
+ new_confline(ctmp);
+ if(before){
+ /*
+ * New_confline appends ctmp after old current instead of inserting
+ * it, so we have to adjust. We have
+ * <- a <-> b <-> p <-> c -> and want <- a <-> p <-> b <-> c ->
+ */
+
+ CONF_S *a, *b, *c, *p;
+
+ p = *ctmp;
+ b = (*ctmp)->prev;
+ c = (*ctmp)->next;
+ a = b ? b->prev : NULL;
+ if(a)
+ a->next = p;
+
+ if(b){
+ b->prev = p;
+ b->next = c;
+ }
+
+ if(c)
+ c->prev = b;
+
+ p->prev = a;
+ p->next = b;
+ }
+
+ if(top_line && *top_line == NULL)
+ *top_line = (*ctmp);
+
+ len = strlen(patline->filename) + 100;
+
+ q = (char *) fs_get((len + 1) * sizeof(char));
+ snprintf(q, len+1, "From file %s%s", patline->filename,
+ patline->readonly ? " (ReadOnly)" : "");
+ q[len-1] = '\0';
+
+ if((wid=utf8_width(q)) > ps->ttyo->screen_cols -2)
+ utf8_snprintf(buf, sizeof(buf), "--%.*w", ps->ttyo->screen_cols -2, q);
+ else
+ snprintf(buf, sizeof(buf), "--%s%s", q, repeat_char(ps->ttyo->screen_cols -2-wid, '-'));
+
+ (*ctmp)->value = cpystr(buf);
+
+ fs_give((void **)&q);
+ (*ctmp)->flags |= (CF_NOSELECT | CF_STARTITEM);
+ (*ctmp)->d.r.patline = patline;
+ firstitem = 0;
+ }
+ else
+ firstitem = 1;
+
+ for(pat = patline->first; pat; pat = pat->next){
+
+ /* Check that pattern has a role and is of right type */
+ if(pat->inherit ||
+ (pat->action &&
+ (((rflags & ROLE_DO_ROLES) && pat->action->is_a_role) ||
+ ((rflags & ROLE_DO_INCOLS) && pat->action->is_a_incol) ||
+ ((rflags & ROLE_DO_SRCH) && pat->action->is_a_srch) ||
+ ((rflags & ROLE_DO_OTHER) && pat->action->is_a_other) ||
+ ((rflags & ROLE_DO_SCORES) && pat->action->is_a_score) ||
+ ((rflags & ROLE_DO_FILTER) && pat->action->is_a_filter)))){
+ add_role_to_display(ctmp, patline, pat, 0,
+ (first_line && *first_line == NULL)
+ ? first_line :
+ (top_line && *top_line == NULL)
+ ? top_line : NULL,
+ firstitem, rflags);
+ firstitem = 1;
+ if(top_line && *top_line == NULL && first_line)
+ *top_line = *first_line;
+ }
+
+ }
+
+ if(patline->type == File){
+ new_confline(ctmp);
+ len = strlen(patline->filename) + 100;
+
+ q = (char *) fs_get((len + 1) * sizeof(char));
+ snprintf(q, len+1, "End of Rules from %s", patline->filename);
+ q[len-1] = '\0';
+
+ if((wid=utf8_width(q)) > ps->ttyo->screen_cols -2)
+ utf8_snprintf(buf, sizeof(buf), "--%.*w", ps->ttyo->screen_cols -2, q);
+ else
+ snprintf(buf, sizeof(buf), "--%s%s", q, repeat_char(ps->ttyo->screen_cols -2-wid, '-'));
+
+ (*ctmp)->value = cpystr(buf);
+
+ fs_give((void **)&q);
+ (*ctmp)->flags |= CF_NOSELECT;
+ (*ctmp)->d.r.patline = patline;
+ }
+}
+
+
+void
+add_role_to_display(CONF_S **ctmp, PAT_LINE_S *patline, PAT_S *pat, int before, CONF_S **first_line, int firstitem, long int rflags)
+{
+ char title[80];
+
+ if(!(pat && (pat->action || pat->inherit)))
+ return;
+
+ new_confline(ctmp);
+ if(first_line && !pat->inherit)
+ *first_line = *ctmp;
+
+ if(before){
+ /*
+ * New_confline appends ctmp after old current instead of inserting
+ * it, so we have to adjust. We have
+ * <- a <-> b <-> p <-> c -> and want <- a <-> p <-> b <-> c ->
+ */
+
+ CONF_S *a, *b, *c, *p;
+
+ p = *ctmp;
+ b = (*ctmp)->prev;
+ c = (*ctmp)->next;
+ a = b ? b->prev : NULL;
+ if(a)
+ a->next = p;
+
+ if(b){
+ b->prev = p;
+ b->next = c;
+ }
+
+ if(c)
+ c->prev = b;
+
+ p->prev = a;
+ p->next = b;
+ }
+
+ role_type_print(title, sizeof(title), _("HELP FOR %sRULE CONFIGURATION"), rflags);
+
+ if(pat->inherit){
+ (*ctmp)->flags |= ((firstitem ? CF_STARTITEM : 0) |
+ CF_NOSELECT | CF_INHERIT);
+ }
+ else{
+ (*ctmp)->flags |= (firstitem ? CF_STARTITEM : 0);
+ (*ctmp)->value = cpystr((pat && pat->patgrp && pat->patgrp->nick)
+ ? pat->patgrp->nick : "?");
+ }
+
+ (*ctmp)->d.r.patline = patline;
+ (*ctmp)->d.r.pat = pat;
+ (*ctmp)->keymenu = &role_conf_km;
+ (*ctmp)->help = (rflags & ROLE_DO_INCOLS) ? h_rules_incols :
+ (rflags & ROLE_DO_OTHER) ? h_rules_other :
+ (rflags & ROLE_DO_FILTER) ? h_rules_filter :
+ (rflags & ROLE_DO_SCORES) ? h_rules_score :
+ (rflags & ROLE_DO_ROLES) ? h_rules_roles :
+ (rflags & ROLE_DO_SRCH) ? h_rules_srch :
+ NO_HELP;
+ (*ctmp)->help_title = title;
+ (*ctmp)->tool = role_config_tool;
+ (*ctmp)->valoffset = 4;
+}
+
+
+void
+add_fake_first_role(CONF_S **ctmp, int before, long int rflags)
+{
+ char title[80];
+ char add[80];
+
+ new_confline(ctmp);
+
+ if(before){
+ /*
+ * New_confline appends ctmp after old current instead of inserting
+ * it, so we have to adjust. We have
+ * <- a <-> b <-> p <-> c -> and want <- a <-> p <-> b <-> c ->
+ */
+
+ CONF_S *a, *b, *c, *p;
+
+ p = *ctmp;
+ b = (*ctmp)->prev;
+ c = (*ctmp)->next;
+ a = b ? b->prev : NULL;
+ if(a)
+ a->next = p;
+
+ if(b){
+ b->prev = p;
+ b->next = c;
+ }
+
+ if(c)
+ c->prev = b;
+
+ p->prev = a;
+ p->next = b;
+ }
+
+ role_type_print(title, sizeof(title), _("HELP FOR %sRULE CONFIGURATION"), rflags);
+ role_type_print(add, sizeof(add), _("Use Add to add a %sRule"), rflags);
+
+ (*ctmp)->value = cpystr(add);
+ (*ctmp)->keymenu = &role_conf_km;
+ (*ctmp)->help = (rflags & ROLE_DO_INCOLS) ? h_rules_incols :
+ (rflags & ROLE_DO_OTHER) ? h_rules_other :
+ (rflags & ROLE_DO_FILTER) ? h_rules_filter :
+ (rflags & ROLE_DO_SCORES) ? h_rules_score :
+ (rflags & ROLE_DO_ROLES) ? h_rules_roles :
+ (rflags & ROLE_DO_SRCH) ? h_rules_srch :
+ NO_HELP;
+ (*ctmp)->help_title = title;
+ (*ctmp)->tool = role_config_tool;
+ (*ctmp)->flags |= CF_STARTITEM;
+ (*ctmp)->valoffset = 4;
+}
+
+
+int
+role_config_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ int first_one = 0, rv = 0;
+ char exitpmt[80];
+ PAT_S *pat;
+
+ if(!(pat = first_pattern(role_global_pstate)) ||
+ (pat->inherit && !next_pattern(role_global_pstate)))
+ first_one++;
+
+ switch(cmd){
+ case MC_DELETE :
+ if(first_one)
+ q_status_message(SM_ORDER|SM_DING, 0, 3,
+ _("Nothing to Delete, use Add"));
+ else
+ rv = role_config_del(ps, cl, role_global_flags);
+
+ break;
+
+ case MC_ADD :
+ rv = role_config_add(ps, cl, role_global_flags);
+ break;
+
+ case MC_EDIT :
+ if(first_one)
+ rv = role_config_add(ps, cl, role_global_flags);
+ else
+ rv = role_config_edit(ps, cl, role_global_flags);
+
+ break;
+
+ case MC_SHUFFLE :
+ if(first_one)
+ q_status_message(SM_ORDER|SM_DING, 0, 3,
+ _("Nothing to Shuffle, use Add"));
+ else
+ rv = role_config_shuffle(ps, cl);
+
+ break;
+
+ case MC_EXIT :
+ role_type_print(exitpmt, sizeof(exitpmt), "%sRule Setup", role_global_flags);
+ rv = screen_exit_cmd(flags, exitpmt);
+ break;
+
+ case MC_ADDFILE :
+ rv = role_config_addfile(ps, cl, role_global_flags);
+ break;
+
+ case MC_DELFILE :
+ rv = role_config_delfile(ps, cl, role_global_flags);
+ break;
+
+ case MC_COPY :
+ if(first_one)
+ q_status_message(SM_ORDER|SM_DING, 0, 3,
+ _("Nothing to Replicate, use Add"));
+ else
+ rv = role_config_replicate(ps, cl, role_global_flags);
+
+ break;
+
+ default:
+ rv = -1;
+ break;
+ }
+
+ return(rv);
+}
+
+
+/*
+ * Add a new role.
+ *
+ * Returns 1 -- There were changes
+ * 0 -- No changes
+ */
+int
+role_config_add(struct pine *ps, CONF_S **cl, long int rflags)
+{
+ int rv = 0, first_pat = 0;
+ PAT_S *new_pat = NULL, *cur_pat;
+ PAT_LINE_S *new_patline, *cur_patline;
+ PAT_STATE pstate;
+ char title[80];
+
+ if((*cl)->d.r.patline &&
+ (*cl)->d.r.patline->readonly
+ && (*cl)->d.r.patline->type == File){
+ q_status_message(SM_ORDER, 0, 3, _("Can't add rule to ReadOnly file"));
+ return(rv);
+ }
+
+ role_type_print(title, sizeof(title), "ADD A %sRULE", rflags);
+
+ if(role_config_edit_screen(ps, NULL, title, rflags,
+ &new_pat) == 1 && new_pat){
+ if(ps->never_allow_changing_from &&
+ new_pat->action &&
+ new_pat->action->from)
+ q_status_message(SM_ORDER|SM_DING, 0, 3,
+ _("Site policy doesn't allow changing From address so From is ignored"));
+
+ if(rflags & ROLE_DO_ROLES &&
+ new_pat->patgrp &&
+ new_pat->patgrp->nick &&
+ nonempty_patterns(ROLE_DO_ROLES, &pstate)){
+ PAT_S *pat;
+
+ for(pat = first_pattern(&pstate);
+ pat;
+ pat = next_pattern(&pstate)){
+ if(pat->patgrp && pat->patgrp->nick &&
+ !strucmp(pat->patgrp->nick, new_pat->patgrp->nick)){
+ q_status_message(SM_ORDER|SM_DING, 3, 7, _("Warning: The nickname of the new role is already in use."));
+ break;
+ }
+ }
+ }
+
+ rv = 1;
+ cur_pat = (*cl)->d.r.pat;
+ if(!cur_pat)
+ first_pat++;
+
+ set_pathandle(rflags);
+ cur_patline = first_pat ? (*cur_pat_h)->patlinehead : cur_pat->patline;
+
+ /* need a new pat_line */
+ if(first_pat || (cur_patline && cur_patline->type == Literal)){
+ new_patline = (PAT_LINE_S *)fs_get(sizeof(*new_patline));
+ memset((void *)new_patline, 0, sizeof(*new_patline));
+ new_patline->type = Literal;
+ (*cur_pat_h)->dirtypinerc = 1;
+ }
+
+ if(cur_patline){
+ if(first_pat || cur_patline->type == Literal){
+ new_patline->prev = cur_patline;
+ new_patline->next = cur_patline->next;
+ if(cur_patline->next)
+ cur_patline->next->prev = new_patline;
+
+ cur_patline->next = new_patline;
+
+ /* tie new patline and new pat together */
+ new_pat->patline = new_patline;
+ new_patline->first = new_patline->last = new_pat;
+ }
+ else if(cur_patline->type == File){ /* don't need a new pat_line */
+ /* tie together */
+ new_pat->patline = cur_patline;
+ cur_patline->dirty = 1;
+
+ /* Splice new_pat after cur_pat */
+ new_pat->prev = cur_pat;
+ new_pat->next = cur_pat->next;
+ if(cur_pat->next)
+ cur_pat->next->prev = new_pat;
+ else
+ cur_patline->last = new_pat;
+
+ cur_pat->next = new_pat;
+ }
+ }
+ else{
+ /* tie new first patline and pat together */
+ new_pat->patline = new_patline;
+ new_patline->first = new_patline->last = new_pat;
+
+ /* set head of list */
+ (*cur_pat_h)->patlinehead = new_patline;
+ }
+
+ /*
+ * If this is the first role, we replace the "Use Add" fake role
+ * with this real one.
+ */
+ if(first_pat){
+ /* Adjust conf_scroll_screen variables */
+ (*cl)->d.r.pat = new_pat;
+ (*cl)->d.r.patline = new_pat->patline;
+ if((*cl)->value)
+ fs_give((void **)&(*cl)->value);
+
+ (*cl)->value = cpystr((new_pat && new_pat->patgrp &&
+ new_pat->patgrp->nick)
+ ? new_pat->patgrp->nick : "?");
+ }
+ /* Else we are inserting a new role after the cur role */
+ else
+ add_role_to_display(cl, new_pat->patline, new_pat, 0, NULL,
+ 1, rflags);
+ }
+
+ return(rv);
+}
+
+
+/*
+ * Replicate a role.
+ *
+ * Returns 1 -- There were changes
+ * 0 -- No changes
+ */
+int
+role_config_replicate(struct pine *ps, CONF_S **cl, long int rflags)
+{
+ int rv = 0, first_pat = 0;
+ PAT_S *new_pat = NULL, *cur_pat, *defpat = NULL;
+ PAT_LINE_S *new_patline, *cur_patline;
+ PAT_STATE pstate;
+ char title[80];
+
+ if((*cl)->d.r.patline &&
+ (*cl)->d.r.patline->readonly
+ && (*cl)->d.r.patline->type == File){
+ q_status_message(SM_ORDER, 0, 3, _("Can't add rule to ReadOnly file"));
+ return(rv);
+ }
+
+ if((*cl)->d.r.pat && (defpat = copy_pat((*cl)->d.r.pat))){
+ /* change nickname */
+ if(defpat->patgrp && defpat->patgrp->nick){
+#define CLONEWORD " Copy"
+ char *oldnick = defpat->patgrp->nick;
+ size_t len;
+
+ len = strlen(oldnick)+strlen(CLONEWORD);
+ defpat->patgrp->nick = (char *)fs_get((len+1) * sizeof(char));
+ strncpy(defpat->patgrp->nick, oldnick, len);
+ defpat->patgrp->nick[len] = '\0';
+ strncat(defpat->patgrp->nick, CLONEWORD,
+ len+1-1-strlen(defpat->patgrp->nick));
+ fs_give((void **)&oldnick);
+ if(defpat->action){
+ if(defpat->action->nick)
+ fs_give((void **)&defpat->action->nick);
+
+ defpat->action->nick = cpystr(defpat->patgrp->nick);
+ }
+ }
+
+ /* set this so that even if we don't edit at all, we'll be asked */
+ rflags |= ROLE_CHANGES;
+
+ role_type_print(title, sizeof(title), "CHANGE THIS %sRULE", rflags);
+
+ if(role_config_edit_screen(ps, defpat, title, rflags,
+ &new_pat) == 1 && new_pat){
+
+ if(ps->never_allow_changing_from &&
+ new_pat->action &&
+ new_pat->action->from)
+ q_status_message(SM_ORDER|SM_DING, 0, 3,
+ _("Site policy doesn't allow changing From address so From is ignored"));
+
+ if(rflags & ROLE_DO_ROLES &&
+ new_pat->patgrp &&
+ new_pat->patgrp->nick &&
+ nonempty_patterns(ROLE_DO_ROLES, &pstate)){
+ PAT_S *pat;
+
+ for(pat = first_pattern(&pstate);
+ pat;
+ pat = next_pattern(&pstate)){
+ if(pat->patgrp && pat->patgrp->nick &&
+ !strucmp(pat->patgrp->nick, new_pat->patgrp->nick)){
+ q_status_message(SM_ORDER|SM_DING, 3, 7, _("Warning: The nickname of the new role is already in use."));
+ break;
+ }
+ }
+ }
+
+ rv = 1;
+ cur_pat = (*cl)->d.r.pat;
+ if(!cur_pat)
+ first_pat++;
+
+ set_pathandle(rflags);
+ cur_patline = first_pat ? (*cur_pat_h)->patlinehead : cur_pat->patline;
+
+ /* need a new pat_line */
+ if(first_pat || (cur_patline && cur_patline->type == Literal)){
+ new_patline = (PAT_LINE_S *)fs_get(sizeof(*new_patline));
+ memset((void *)new_patline, 0, sizeof(*new_patline));
+ new_patline->type = Literal;
+ (*cur_pat_h)->dirtypinerc = 1;
+ }
+
+ if(cur_patline){
+ if(first_pat || cur_patline->type == Literal){
+ new_patline->prev = cur_patline;
+ new_patline->next = cur_patline->next;
+ if(cur_patline->next)
+ cur_patline->next->prev = new_patline;
+
+ cur_patline->next = new_patline;
+
+ /* tie new patline and new pat together */
+ new_pat->patline = new_patline;
+ new_patline->first = new_patline->last = new_pat;
+ }
+ else if(cur_patline->type == File){ /* don't need a new pat_line */
+ /* tie together */
+ new_pat->patline = cur_patline;
+ cur_patline->dirty = 1;
+
+ /* Splice new_pat after cur_pat */
+ new_pat->prev = cur_pat;
+ new_pat->next = cur_pat->next;
+ if(cur_pat->next)
+ cur_pat->next->prev = new_pat;
+ else
+ cur_patline->last = new_pat;
+
+ cur_pat->next = new_pat;
+ }
+ }
+ else{
+ /* tie new first patline and pat together */
+ new_pat->patline = new_patline;
+ new_patline->first = new_patline->last = new_pat;
+
+ /* set head of list */
+ (*cur_pat_h)->patlinehead = new_patline;
+ }
+
+ /*
+ * If this is the first role, we replace the "Use Add" fake role
+ * with this real one.
+ */
+ if(first_pat){
+ /* Adjust conf_scroll_screen variables */
+ (*cl)->d.r.pat = new_pat;
+ (*cl)->d.r.patline = new_pat->patline;
+ if((*cl)->value)
+ fs_give((void **)&(*cl)->value);
+
+ (*cl)->value = cpystr((new_pat && new_pat->patgrp &&
+ new_pat->patgrp->nick)
+ ? new_pat->patgrp->nick : "?");
+ }
+ /* Else we are inserting a new role after the cur role */
+ else
+ add_role_to_display(cl, new_pat->patline, new_pat, 0, NULL,
+ 1, rflags);
+ }
+ }
+
+ if(defpat)
+ free_pat(&defpat);
+
+ return(rv);
+}
+
+
+/*
+ * Change the current role.
+ *
+ * Returns 1 -- There were changes
+ * 0 -- No changes
+ */
+int
+role_config_edit(struct pine *ps, CONF_S **cl, long int rflags)
+{
+ int rv = 0;
+ PAT_S *new_pat = NULL, *cur_pat;
+ char title[80];
+
+ if((*cl)->d.r.patline->readonly){
+ q_status_message(SM_ORDER, 0, 3, _("Can't change ReadOnly rule"));
+ return(rv);
+ }
+
+ cur_pat = (*cl)->d.r.pat;
+
+ role_type_print(title, sizeof(title), "CHANGE THIS %sRULE", rflags);
+
+ if(role_config_edit_screen(ps, cur_pat, title,
+ rflags, &new_pat) == 1 && new_pat){
+
+ if(ps->never_allow_changing_from &&
+ new_pat->action &&
+ new_pat->action->from)
+ q_status_message(SM_ORDER|SM_DING, 0, 3,
+ _("Site policy doesn't allow changing From address so From is ignored"));
+
+ if(rflags & ROLE_DO_ROLES && new_pat->patgrp && new_pat->patgrp->nick){
+ PAT_S *pat;
+
+ for(pat = first_pattern(role_global_pstate);
+ pat;
+ pat = next_pattern(role_global_pstate)){
+ if(pat->patgrp && pat->patgrp->nick && pat != cur_pat &&
+ !strucmp(pat->patgrp->nick, new_pat->patgrp->nick)){
+ q_status_message(SM_ORDER|SM_DING, 3, 7, _("Warning: The nickname of this role is also used for another role."));
+ break;
+ }
+ }
+ }
+
+ rv = 1;
+
+ /*
+ * Splice in new_pat in place of cur_pat
+ */
+
+ if(cur_pat->prev)
+ cur_pat->prev->next = new_pat;
+
+ if(cur_pat->next)
+ cur_pat->next->prev = new_pat;
+
+ new_pat->prev = cur_pat->prev;
+ new_pat->next = cur_pat->next;
+
+ /* tie together patline and pat (new_pat gets patline in editor) */
+ if(new_pat->patline->first == cur_pat)
+ new_pat->patline->first = new_pat;
+
+ if(new_pat->patline->last == cur_pat)
+ new_pat->patline->last = new_pat;
+
+ if(new_pat->patline->type == Literal){
+ set_pathandle(rflags);
+ if(*cur_pat_h)
+ (*cur_pat_h)->dirtypinerc = 1;
+ }
+ else
+ new_pat->patline->dirty = 1;
+
+ cur_pat->next = NULL;
+ free_pat(&cur_pat);
+
+ /* Adjust conf_scroll_screen variables */
+ (*cl)->d.r.pat = new_pat;
+ (*cl)->d.r.patline = new_pat->patline;
+ if((*cl)->value)
+ fs_give((void **)&(*cl)->value);
+
+ (*cl)->value = cpystr((new_pat->patgrp && new_pat->patgrp->nick)
+ ? new_pat->patgrp->nick : "?");
+ }
+
+ return(rv);
+}
+
+
+/*
+ * Delete a role.
+ *
+ * Returns 1 -- There were changes
+ * 0 -- No changes
+ */
+int
+role_config_del(struct pine *ps, CONF_S **cl, long int rflags)
+{
+ int rv = 0;
+ char msg[80];
+ char prompt[100];
+
+ if((*cl)->d.r.patline->readonly){
+ q_status_message(SM_ORDER, 0, 3, _("Can't delete ReadOnly rule"));
+ return(rv);
+ }
+
+ role_type_print(msg, sizeof(msg), _("Really delete %srule"), rflags);
+ snprintf(prompt, sizeof(prompt), "%s \"%s\" ", msg, (*cl)->value);
+ prompt[sizeof(prompt)-1] = '\0';
+
+ ps->mangled_footer = 1;
+ if(want_to(prompt,'n','n',h_config_role_del, WT_FLUSH_IN) == 'y'){
+ rv = ps->mangled_body = 1;
+ delete_a_role(cl, rflags);
+ }
+ else
+ q_status_message(SM_ORDER, 0, 3, _("Rule not deleted"));
+
+ return(rv);
+}
+
+
+void
+delete_a_role(CONF_S **cl, long int rflags)
+{
+ PAT_S *cur_pat;
+ CONF_S *cp, *cq;
+ PAT_LINE_S *cur_patline;
+ int inherit = 0;
+
+ cur_pat = (*cl)->d.r.pat;
+ cur_patline = (*cl)->d.r.patline;
+
+ if(cur_patline->type == Literal){ /* delete patline */
+ set_pathandle(rflags);
+ if(cur_patline->prev)
+ cur_patline->prev->next = cur_patline->next;
+ else{
+ if(*cur_pat_h) /* this has to be true */
+ (*cur_pat_h)->patlinehead = cur_patline->next;
+ }
+
+ if(cur_patline->next)
+ cur_patline->next->prev = cur_patline->prev;
+
+ if(*cur_pat_h) /* this has to be true */
+ (*cur_pat_h)->dirtypinerc = 1;
+
+ cur_patline->next = NULL;
+ free_patline(&cur_patline);
+ }
+ else if(cur_patline->type == File){ /* or delete pat */
+ if(cur_pat->prev)
+ cur_pat->prev->next = cur_pat->next;
+ else
+ cur_patline->first = cur_pat->next;
+
+ if(cur_pat->next)
+ cur_pat->next->prev = cur_pat->prev;
+ else
+ cur_patline->last = cur_pat->prev;
+
+ cur_patline->dirty = 1;
+
+ cur_pat->next = NULL;
+ free_pat(&cur_pat);
+ }
+
+ /* delete the conf line */
+
+ /* deleting last real rule */
+ if(!first_pattern(role_global_pstate) ||
+ ((inherit=first_pattern(role_global_pstate)->inherit) &&
+ !next_pattern(role_global_pstate))){
+
+ cq = *cl;
+
+ /*
+ * Find the start and prepend the fake first role.
+ */
+ while(*cl && (*cl)->prev)
+ *cl = (*cl)->prev;
+
+ add_fake_first_role(cl, inherit ? 0 : 1, rflags);
+ snip_confline(&cq);
+ opt_screen->top_line = (*cl);
+ opt_screen->current = (*cl);
+ }
+ else{
+ /* find next selectable line */
+ for(cp = (*cl)->next;
+ cp && (cp->flags & CF_NOSELECT);
+ cp = cp->next)
+ ;
+
+ if(!cp){ /* no next selectable, find previous selectable */
+ if(*cl == opt_screen->top_line)
+ opt_screen->top_line = (*cl)->prev;
+
+ for(cp = (*cl)->prev;
+ cp && (cp->flags & CF_NOSELECT);
+ cp = cp->prev)
+ ;
+ }
+ else if(*cl == opt_screen->top_line)
+ opt_screen->top_line = (*cl)->next;
+
+ cq = *cl;
+ *cl = cp;
+ snip_confline(&cq);
+ }
+}
+
+
+/*
+ * Shuffle the current role up or down.
+ *
+ * Returns 1 -- There were changes
+ * 0 -- No changes
+ */
+int
+role_config_shuffle(struct pine *ps, CONF_S **cl)
+{
+ int rv = 0, deefault, i;
+ int readonlyabove = 0, readonlybelow = 0;
+ ESCKEY_S opts[5];
+ HelpType help;
+ char tmp[200];
+ CONF_S *a, *b;
+ PAT_TYPE curtype, prevtype, nexttype;
+
+ if(!((*cl)->prev || (*cl)->next)){
+ q_status_message(SM_ORDER, 0, 3,
+ _("Shuffle only makes sense when there is more than one rule defined"));
+ return(rv);
+ }
+
+ /* Move it up or down? */
+ i = 0;
+ opts[i].ch = 'u';
+ opts[i].rval = 'u';
+ opts[i].name = "U";
+ opts[i++].label = N_("Up");
+
+ opts[i].ch = 'd';
+ opts[i].rval = 'd';
+ opts[i].name = "D";
+ opts[i++].label = N_("Down");
+
+ opts[i].ch = 'b';
+ opts[i].rval = 'b';
+ opts[i].name = "B";
+ opts[i++].label = N_("Before File");
+
+ opts[i].ch = 'a';
+ opts[i].rval = 'a';
+ opts[i].name = "A";
+ opts[i++].label = N_("After File");
+
+ opts[i].ch = -1;
+ deefault = 'u';
+
+ curtype = ((*cl)->d.r.patline) ? (*cl)->d.r.patline->type : TypeNotSet;
+
+ prevtype = ((*cl)->prev && (*cl)->prev->d.r.patline)
+ ? (*cl)->prev->d.r.patline->type : TypeNotSet;
+ if(curtype == File && prevtype == File && (*cl)->prev->d.r.pat == NULL)
+ prevtype = TypeNotSet;
+
+ nexttype = ((*cl)->next && (*cl)->next->d.r.patline)
+ ? (*cl)->next->d.r.patline->type : TypeNotSet;
+ if(curtype == File && nexttype == File && (*cl)->next->d.r.pat == NULL)
+ nexttype = TypeNotSet;
+
+
+ if(curtype == Literal){
+ if(prevtype == TypeNotSet ||
+ prevtype == Inherit){ /* no up, at top */
+ opts[0].ch = -2;
+ opts[2].ch = -2;
+ deefault = 'd';
+ }
+ else if(prevtype == Literal){ /* regular up */
+ opts[2].ch = -2;
+ }
+ else if(prevtype == File){ /* file above us */
+ if((*cl)->prev->d.r.patline->readonly)
+ readonlyabove++;
+ }
+
+ if(nexttype == TypeNotSet){ /* no down, at bottom */
+ opts[1].ch = -2;
+ opts[3].ch = -2;
+ }
+ else if(nexttype == Literal){ /* regular down */
+ opts[3].ch = -2;
+ }
+ else if(nexttype == File){ /* file below us */
+ if((*cl)->next->d.r.patline->readonly)
+ readonlybelow++;
+ }
+ }
+ else if(curtype == File){
+ if((*cl)->d.r.patline && (*cl)->d.r.patline->readonly){
+ q_status_message(SM_ORDER, 0, 3, _("Can't change ReadOnly file"));
+ return(0);
+ }
+
+ opts[2].ch = -2;
+ opts[3].ch = -2;
+ }
+ else{
+ q_status_message(SM_ORDER, 0, 3,
+ "Programming Error: unknown line type in role_shuffle");
+ return(rv);
+ }
+
+ snprintf(tmp, sizeof(tmp), "Shuffle \"%s\" %s%s%s%s%s%s%s ? ",
+ (*cl)->value,
+ (opts[0].ch != -2) ? N_("UP") : "",
+ (opts[0].ch != -2 && opts[1].ch != -2) ? " or " : "",
+ (opts[1].ch != -2) ? N_("DOWN") : "",
+ ((opts[0].ch != -2 ||
+ opts[1].ch != -2) && opts[2].ch != -2) ? " or " : "",
+ (opts[2].ch != -2) ? N_("BEFORE") : "",
+ ((opts[0].ch != -2 ||
+ opts[1].ch != -2 ||
+ opts[2].ch != -2) && opts[3].ch != -2) ? " or " : "",
+ (opts[3].ch != -2) ? N_("AFTER") : "");
+ tmp[sizeof(tmp)-1] = '\0';
+
+ help = (opts[0].ch == -2) ? h_role_shuf_down
+ : (opts[1].ch == -2) ? h_role_shuf_up
+ : h_role_shuf;
+
+ rv = radio_buttons(tmp, -FOOTER_ROWS(ps), opts, deefault, 'x',
+ help, RB_NORM);
+
+ if(rv == 'x'){
+ cmd_cancelled("Shuffle");
+ return(0);
+ }
+
+ if((readonlyabove && rv == 'u' && curtype != prevtype) ||
+ (readonlybelow && rv == 'd' && curtype != nexttype)){
+ q_status_message(SM_ORDER, 0, 3, _("Can't shuffle into ReadOnly file"));
+ return(0);
+ }
+
+ if(rv == 'u' && curtype == Literal && prevtype == Literal){
+ rv = 1;
+ a = (*cl)->prev;
+ b = (*cl);
+ if(a == opt_screen->top_line)
+ opt_screen->top_line = b;
+
+ swap_literal_roles(a, b);
+ ps->mangled_body = 1;
+ }
+ else if(rv == 'd' && curtype == Literal && nexttype == Literal){
+ rv = 1;
+ a = (*cl);
+ b = (*cl)->next;
+ if(a == opt_screen->top_line)
+ opt_screen->top_line = b;
+
+ swap_literal_roles(a, b);
+ ps->mangled_body = 1;
+ }
+ else if(rv == 'u' && curtype == File && prevtype == File){
+ rv = 1;
+ a = (*cl)->prev;
+ b = (*cl);
+ if(a == opt_screen->top_line)
+ opt_screen->top_line = b;
+
+ swap_file_roles(a, b);
+ ps->mangled_body = 1;
+ }
+ else if(rv == 'u' && curtype == File){
+ rv = 1;
+ move_role_outof_file(cl, 1);
+ ps->mangled_body = 1;
+ }
+ else if(rv == 'd' && curtype == File && nexttype == File){
+ rv = 1;
+ a = (*cl);
+ b = (*cl)->next;
+ if(a == opt_screen->top_line)
+ opt_screen->top_line = b;
+
+ swap_file_roles(a, b);
+ ps->mangled_body = 1;
+ }
+ else if(rv == 'd' && curtype == File){
+ rv = 1;
+ if(*cl == opt_screen->top_line)
+ opt_screen->top_line = (*cl)->next;
+
+ move_role_outof_file(cl, 0);
+ ps->mangled_body = 1;
+ }
+ else if(rv == 'u' && curtype == Literal && prevtype == File){
+ rv = 1;
+ move_role_into_file(cl, 1);
+ ps->mangled_body = 1;
+ }
+ else if(rv == 'd' && curtype == Literal && nexttype == File){
+ rv = 1;
+ if(*cl == opt_screen->top_line)
+ opt_screen->top_line = (*cl)->next;
+
+ move_role_into_file(cl, 0);
+ ps->mangled_body = 1;
+ }
+ else if(rv == 'b'){
+ rv = 1;
+ move_role_around_file(cl, 1);
+ ps->mangled_body = 1;
+ }
+ else if(rv == 'a'){
+ rv = 1;
+ if(*cl == opt_screen->top_line)
+ opt_screen->top_line = (*cl)->next;
+
+ move_role_around_file(cl, 0);
+ ps->mangled_body = 1;
+ }
+
+ return(rv);
+}
+
+
+int
+role_config_addfile(struct pine *ps, CONF_S **cl, long int rflags)
+{
+ char filename[MAXPATH+1], full_filename[MAXPATH+1];
+ char dir2[MAXPATH+1], pdir[MAXPATH+1];
+ char *lc, *newfile = NULL;
+ PAT_LINE_S *file_patline;
+ int rv = 0, len;
+ int r = 1, flags;
+ HelpType help = NO_HELP;
+ PAT_TYPE curtype;
+ CONF_S *first_line = NULL, *add_line, *save_current;
+ struct variable *vars = ps->vars;
+
+ if(ps->restricted){
+ q_status_message(SM_ORDER, 0, 3, "Alpine demo can't read files");
+ return(rv);
+ }
+
+ curtype = ((*cl)->d.r.patline && (*cl)->d.r.patline)
+ ? (*cl)->d.r.patline->type : TypeNotSet;
+
+ if(curtype == File){
+ q_status_message(SM_ORDER, 0, 3, _("Current rule is already part of a file. Move outside any files first."));
+ return(rv);
+ }
+
+ /*
+ * Parse_pattern_file uses signature_path to figure out where to look
+ * for the file. In signature_path we read signature files relative
+ * to the pinerc dir, so if user selects one that is in there we'll
+ * use relative instead of absolute, so it looks nicer.
+ */
+ pdir[0] = '\0';
+ if(VAR_OPER_DIR){
+ strncpy(pdir, VAR_OPER_DIR, sizeof(pdir)-1);
+ pdir[sizeof(pdir)-1] = '\0';
+ len = strlen(pdir) + 1;
+ }
+ else if((lc = last_cmpnt(ps->pinerc)) != NULL){
+ strncpy(pdir, ps->pinerc, MIN(sizeof(pdir)-1,lc-ps->pinerc));
+ pdir[MIN(sizeof(pdir)-1, lc-ps->pinerc)] = '\0';
+ len = strlen(pdir);
+ }
+
+ strncpy(dir2, pdir, sizeof(dir2)-1);
+ dir2[sizeof(dir2)-1] = '\0';
+ filename[0] = '\0';
+ full_filename[0] = '\0';
+ ps->mangled_footer = 1;
+
+ while(1){
+ flags = OE_APPEND_CURRENT;
+ r = optionally_enter(filename, -FOOTER_ROWS(ps), 0, sizeof(filename),
+ "Name of file to be added to rules: ",
+ NULL, help, &flags);
+
+ if(r == 3){
+ help = (help == NO_HELP) ? h_config_role_addfile : NO_HELP;
+ continue;
+ }
+ else if(r == 10 || r == 11){ /* Browser or File Completion */
+ continue;
+ }
+ else if(r == 1 || (r == 0 && filename[0] == '\0')){
+ cmd_cancelled("IncludeFile");
+ return(rv);
+ }
+ else if(r == 4){
+ continue;
+ }
+ else if(r != 0){
+ Writechar(BELL, 0);
+ continue;
+ }
+
+ removing_leading_and_trailing_white_space(filename);
+ if(is_absolute_path(filename))
+ newfile = cpystr(filename);
+ else{
+ build_path(full_filename, dir2, filename, sizeof(full_filename));
+ removing_leading_and_trailing_white_space(full_filename);
+ if(!strncmp(full_filename, pdir, strlen(pdir)))
+ newfile = cpystr(full_filename + len);
+ else
+ newfile = cpystr(full_filename);
+ }
+
+ if(newfile && *newfile)
+ break;
+ }
+
+ if(!newfile)
+ return(rv);
+
+ set_pathandle(rflags);
+
+ if((file_patline = parse_pat_file(newfile)) != NULL){
+ /*
+ * Insert the file after the current line.
+ */
+ PAT_LINE_S *cur_patline;
+ int first_pat;
+
+ rv = ps->mangled_screen = 1;
+ first_pat = !(*cl)->d.r.pat;
+ cur_patline = (*cl)->d.r.patline ? (*cl)->d.r.patline :
+ (*cur_pat_h) ? (*cur_pat_h)->patlinehead : NULL;
+
+ if(*cur_pat_h)
+ (*cur_pat_h)->dirtypinerc = 1;
+
+ file_patline->dirty = 1;
+
+ if(cur_patline){
+ file_patline->prev = cur_patline;
+ file_patline->next = cur_patline->next;
+ if(cur_patline->next)
+ cur_patline->next->prev = file_patline;
+
+ cur_patline->next = file_patline;
+ }
+ else{
+ /* set head of list */
+ if(*cur_pat_h)
+ (*cur_pat_h)->patlinehead = file_patline;
+ }
+
+ if(first_pat){
+ if(file_patline->first){
+ /* get rid of Fake Add line */
+ add_line = *cl;
+ opt_screen->top_line = NULL;
+ add_patline_to_display(ps, cl, 0, &first_line,
+ &opt_screen->top_line, file_patline,
+ rflags);
+ opt_screen->current = first_line;
+ snip_confline(&add_line);
+ }
+ else{
+ /* we're _appending_ to the Fake Add line */
+ save_current = opt_screen->current;
+ add_patline_to_display(ps, cl, 0, NULL, NULL, file_patline,
+ rflags);
+ opt_screen->current = save_current;
+ }
+ }
+ else{
+ opt_screen->top_line = NULL;
+ save_current = opt_screen->current;
+ add_patline_to_display(ps, cl, 0, &first_line,
+ &opt_screen->top_line, file_patline,
+ rflags);
+ if(first_line)
+ opt_screen->current = first_line;
+ else
+ opt_screen->current = save_current;
+ }
+ }
+
+ if(newfile)
+ fs_give((void **)&newfile);
+
+ return(rv);
+}
+
+
+int
+role_config_delfile(struct pine *ps, CONF_S **cl, long int rflags)
+{
+ int rv = 0;
+ PAT_LINE_S *cur_patline;
+ char prompt[100];
+
+ if(!(cur_patline = (*cl)->d.r.patline)){
+ q_status_message(SM_ORDER, 0, 3,
+ "Unknown problem in role_config_delfile");
+ return(rv);
+ }
+
+ if(cur_patline->type != File){
+ q_status_message(SM_ORDER, 0, 3, _("Current rule is not part of a file. Use Delete to remove current rule"));
+ return(rv);
+ }
+
+ snprintf(prompt, sizeof(prompt), _("Really remove rule file \"%s\" from rules config "),
+ cur_patline->filename);
+ prompt[sizeof(prompt)-1] = '\0';
+
+ ps->mangled_footer = 1;
+ if(want_to(prompt,'n','n',h_config_role_delfile, WT_FLUSH_IN) == 'y'){
+ CONF_S *ctmp, *cp;
+
+ set_pathandle(rflags);
+ rv = ps->mangled_screen = 1;
+ if(*cur_pat_h)
+ (*cur_pat_h)->dirtypinerc = 1;
+
+ if(cur_patline->prev)
+ cur_patline->prev->next = cur_patline->next;
+ else{
+ if(*cur_pat_h)
+ (*cur_pat_h)->patlinehead = cur_patline->next;
+ }
+
+ if(cur_patline->next)
+ cur_patline->next->prev = cur_patline->prev;
+
+ /* delete the conf lines */
+
+ /* find the first one associated with this file */
+ for(ctmp = *cl;
+ ctmp && ctmp->prev && ctmp->prev->d.r.patline == cur_patline;
+ ctmp = ctmp->prev)
+ ;
+
+ if(ctmp->prev) /* this file wasn't the first thing in config */
+ *cl = ctmp->prev;
+ else{ /* this file was first in config */
+ for(cp = ctmp; cp && cp->next; cp = cp->next)
+ ;
+
+ if(cp->d.r.patline == cur_patline)
+ *cl = NULL;
+ else
+ *cl = cp;
+ }
+
+ /* delete lines from the file */
+ while(ctmp && ctmp->d.r.patline == cur_patline){
+ cp = ctmp;
+ ctmp = ctmp->next;
+ snip_confline(&cp);
+ }
+
+ /* deleting last real rule */
+ if(!first_pattern(role_global_pstate)){
+ /*
+ * Find the start and prepend the fake first role
+ * in there.
+ */
+ while(*cl && (*cl)->prev)
+ *cl = (*cl)->prev;
+
+ add_fake_first_role(cl, 1, rflags);
+ }
+ else if(first_pattern(role_global_pstate)->inherit &&
+ !next_pattern(role_global_pstate)){
+ while(*cl && (*cl)->prev)
+ *cl = (*cl)->prev;
+
+ /* append fake first after inherit */
+ add_fake_first_role(cl, 0, rflags);
+ }
+
+ opt_screen->top_line = first_confline(*cl);
+ opt_screen->current = first_sel_confline(opt_screen->top_line);
+
+ cur_patline->next = NULL;
+ free_patline(&cur_patline);
+ }
+ else
+ q_status_message(SM_ORDER, 0, 3, _("Rule file not removed"));
+
+ return(rv);
+}
+
+
+/*
+ * Swap from a, b to b, a.
+ */
+void
+swap_literal_roles(CONF_S *a, CONF_S *b)
+{
+ PAT_LINE_S *patline_a, *patline_b;
+
+ patline_a = a->d.r.patline;
+ patline_b = b->d.r.patline;
+
+ set_pathandle(role_global_flags);
+ if(*cur_pat_h)
+ (*cur_pat_h)->dirtypinerc = 1;
+
+ /* first swap the patlines */
+ if(patline_a->next == patline_b){
+ patline_b->prev = patline_a->prev;
+ if(patline_a->prev)
+ patline_a->prev->next = patline_b;
+
+ patline_a->next = patline_b->next;
+ if(patline_b->next)
+ patline_b->next->prev = patline_a;
+
+ patline_b->next = patline_a;
+ patline_a->prev = patline_b;
+ }
+ else{
+ PAT_LINE_S *new_a_prev, *new_a_next;
+
+ new_a_prev = patline_b->prev;
+ new_a_next = patline_b->next;
+
+ patline_b->prev = patline_a->prev;
+ patline_b->next = patline_a->next;
+ if(patline_b->prev)
+ patline_b->prev->next = patline_b;
+ if(patline_b->next)
+ patline_b->next->prev = patline_b;
+
+ patline_a->prev = new_a_prev;
+ patline_a->next = new_a_next;
+ if(patline_a->prev)
+ patline_a->prev->next = patline_a;
+ if(patline_a->next)
+ patline_a->next->prev = patline_a;
+ }
+
+ /*
+ * If patline_b is now the first one in the list, we need to fix the
+ * head of the list to point to this new role.
+ */
+ if(patline_b->prev == NULL && *cur_pat_h)
+ (*cur_pat_h)->patlinehead = patline_b;
+
+
+ /* and then swap the conf lines */
+
+ b->prev = a->prev;
+ if(a->prev)
+ a->prev->next = b;
+
+ a->next = b->next;
+ if(b->next)
+ b->next->prev = a;
+
+ b->next = a;
+ a->prev = b;
+}
+
+
+/*
+ * Swap from a, b to b, a.
+ */
+void
+swap_file_roles(CONF_S *a, CONF_S *b)
+{
+ PAT_S *pat_a, *pat_b;
+ PAT_LINE_S *patline;
+
+ pat_a = a->d.r.pat;
+ pat_b = b->d.r.pat;
+ patline = pat_a->patline;
+
+ patline->dirty = 1;
+
+ /* first swap the pats */
+ if(pat_a->next == pat_b){
+ pat_b->prev = pat_a->prev;
+ if(pat_a->prev)
+ pat_a->prev->next = pat_b;
+
+ pat_a->next = pat_b->next;
+ if(pat_b->next)
+ pat_b->next->prev = pat_a;
+
+ pat_b->next = pat_a;
+ pat_a->prev = pat_b;
+ }
+ else{
+ PAT_S *new_a_prev, *new_a_next;
+
+ new_a_prev = pat_b->prev;
+ new_a_next = pat_b->next;
+
+ pat_b->prev = pat_a->prev;
+ pat_b->next = pat_a->next;
+ if(pat_b->prev)
+ pat_b->prev->next = pat_b;
+ if(pat_b->next)
+ pat_b->next->prev = pat_b;
+
+ pat_a->prev = new_a_prev;
+ pat_a->next = new_a_next;
+ if(pat_a->prev)
+ pat_a->prev->next = pat_a;
+ if(pat_a->next)
+ pat_a->next->prev = pat_a;
+ }
+
+ /*
+ * Fix the first and last pointers.
+ */
+ if(patline->first == pat_a)
+ patline->first = pat_b;
+ if(patline->last == pat_b)
+ patline->last = pat_a;
+
+ /* and then swap the conf lines */
+
+ b->prev = a->prev;
+ if(a->prev)
+ a->prev->next = b;
+
+ a->next = b->next;
+ if(b->next)
+ b->next->prev = a;
+
+ b->next = a;
+ a->prev = b;
+}
+
+
+/*
+ */
+void
+move_role_into_file(CONF_S **cl, int up)
+{
+ PAT_LINE_S *cur_patline, *file_patline;
+ PAT_S *pat;
+ CONF_S *a, *b;
+
+ cur_patline = (*cl)->d.r.patline;
+
+ if(up){
+ file_patline = (*cl)->prev->d.r.patline;
+ a = (*cl)->prev;
+ b = (*cl);
+ b->d.r.patline = file_patline;
+ }
+ else{
+ file_patline = (*cl)->next->d.r.patline;
+ a = (*cl);
+ b = (*cl)->next;
+ a->d.r.patline = file_patline;
+ }
+
+ set_pathandle(role_global_flags);
+ if(*cur_pat_h)
+ (*cur_pat_h)->dirtypinerc = 1;
+
+ file_patline->dirty = 1;
+
+ pat = cur_patline->first;
+
+ if(!up && *cur_pat_h && cur_patline == (*cur_pat_h)->patlinehead)
+ (*cur_pat_h)->patlinehead = (*cur_pat_h)->patlinehead->next;
+
+ if(file_patline->first){
+ if(up){
+ file_patline->last->next = pat;
+ pat->prev = file_patline->last;
+ file_patline->last = pat;
+ }
+ else{
+ file_patline->first->prev = pat;
+ pat->next = file_patline->first;
+ file_patline->first = pat;
+ }
+ }
+ else /* will be only role in file */
+ file_patline->first = file_patline->last = pat;
+
+ pat->patline = file_patline;
+
+ /* delete the now unused cur_patline */
+ cur_patline->first = cur_patline->last = NULL;
+ if(cur_patline->prev)
+ cur_patline->prev->next = cur_patline->next;
+ if(cur_patline->next)
+ cur_patline->next->prev = cur_patline->prev;
+
+ cur_patline->next = NULL;
+ free_patline(&cur_patline);
+
+ /* and then swap the conf lines */
+
+ b->prev = a->prev;
+ if(a->prev)
+ a->prev->next = b;
+
+ a->next = b->next;
+ if(b->next)
+ b->next->prev = a;
+
+ b->next = a;
+ a->prev = b;
+}
+
+
+/*
+ */
+void
+move_role_outof_file(CONF_S **cl, int up)
+{
+ PAT_LINE_S *file_patline, *new_patline;
+ PAT_S *pat;
+ CONF_S *a, *b;
+
+ new_patline = (PAT_LINE_S *)fs_get(sizeof(*new_patline));
+ memset((void *)new_patline, 0, sizeof(*new_patline));
+ new_patline->type = Literal;
+
+ file_patline = (*cl)->d.r.patline;
+ pat = (*cl)->d.r.pat;
+
+ if(up){
+ a = (*cl)->prev;
+ b = (*cl);
+
+ if(pat->prev)
+ pat->prev->next = pat->next;
+ else
+ file_patline->first = pat->next;
+
+ if(pat->next)
+ pat->next->prev = pat->prev;
+ else
+ file_patline->last = pat->prev;
+
+ if(file_patline->first)
+ file_patline->first->prev = NULL;
+
+ if(file_patline->last)
+ file_patline->last->next = NULL;
+
+ if(file_patline->prev)
+ file_patline->prev->next = new_patline;
+
+ new_patline->prev = file_patline->prev;
+ new_patline->next = file_patline;
+ file_patline->prev = new_patline;
+ b->d.r.patline = new_patline;
+ }
+ else{
+ a = (*cl);
+ b = (*cl)->next;
+
+ if(pat->prev)
+ pat->prev->next = pat->next;
+ else
+ file_patline->first = pat->next;
+
+ if(pat->next)
+ pat->next->prev = pat->prev;
+ else
+ file_patline->last = pat->prev;
+
+ if(file_patline->first)
+ file_patline->first->prev = NULL;
+
+ if(file_patline->last)
+ file_patline->last->next = NULL;
+
+ if(file_patline->next)
+ file_patline->next->prev = new_patline;
+
+ new_patline->next = file_patline->next;
+ new_patline->prev = file_patline;
+ file_patline->next = new_patline;
+ a->d.r.patline = new_patline;
+ }
+
+ set_pathandle(role_global_flags);
+ if(*cur_pat_h)
+ (*cur_pat_h)->dirtypinerc = 1;
+
+ file_patline->dirty = 1;
+
+ new_patline->first = new_patline->last = pat;
+ pat->patline = new_patline;
+ pat->prev = pat->next = NULL;
+
+ if(up && *cur_pat_h && file_patline == (*cur_pat_h)->patlinehead)
+ (*cur_pat_h)->patlinehead = new_patline;
+
+ /* and then swap the conf lines */
+
+ b->prev = a->prev;
+ if(a->prev)
+ a->prev->next = b;
+
+ a->next = b->next;
+ if(b->next)
+ b->next->prev = a;
+
+ b->next = a;
+ a->prev = b;
+}
+
+
+/*
+ * This is a move of a literal role from before a file to after a file,
+ * or vice versa.
+ */
+void
+move_role_around_file(CONF_S **cl, int up)
+{
+ PAT_LINE_S *file_patline, *lit_patline;
+ CONF_S *cp;
+
+ set_pathandle(role_global_flags);
+ lit_patline = (*cl)->d.r.patline;
+ if(up)
+ file_patline = (*cl)->prev->d.r.patline;
+ else{
+ if(*cur_pat_h && lit_patline == (*cur_pat_h)->patlinehead)
+ (*cur_pat_h)->patlinehead = (*cur_pat_h)->patlinehead->next;
+
+ file_patline = (*cl)->next->d.r.patline;
+ }
+
+ if(*cur_pat_h)
+ (*cur_pat_h)->dirtypinerc = 1;
+
+ /* remove the lit_patline from the list */
+ if(lit_patline->prev)
+ lit_patline->prev->next = lit_patline->next;
+ if(lit_patline->next)
+ lit_patline->next->prev = lit_patline->prev;
+
+ /* and reinsert it on the other side of the file */
+ if(up){
+ if(*cur_pat_h && file_patline == (*cur_pat_h)->patlinehead)
+ (*cur_pat_h)->patlinehead = lit_patline;
+
+ lit_patline->prev = file_patline->prev;
+ lit_patline->next = file_patline;
+
+ if(file_patline->prev)
+ file_patline->prev->next = lit_patline;
+
+ file_patline->prev = lit_patline;
+ }
+ else{
+ lit_patline->next = file_patline->next;
+ lit_patline->prev = file_patline;
+
+ if(file_patline->next)
+ file_patline->next->prev = lit_patline;
+
+ file_patline->next = lit_patline;
+ }
+
+ /*
+ * And then move the conf line around the file conf lines.
+ */
+
+ /* find it's new home */
+ if(up)
+ for(cp = (*cl);
+ cp && cp->prev && cp->prev->d.r.patline == file_patline;
+ cp = prev_confline(cp))
+ ;
+ else
+ for(cp = (*cl);
+ cp && cp->next && cp->next->d.r.patline == file_patline;
+ cp = next_confline(cp))
+ ;
+
+ /* remove it from where it is */
+ if((*cl)->prev)
+ (*cl)->prev->next = (*cl)->next;
+ if((*cl)->next)
+ (*cl)->next->prev = (*cl)->prev;
+
+ /* cp points to top or bottom of the file lines */
+ if(up){
+ (*cl)->prev = cp->prev;
+ if(cp->prev)
+ cp->prev->next = (*cl);
+
+ cp->prev = (*cl);
+ (*cl)->next = cp;
+ }
+ else{
+ (*cl)->next = cp->next;
+ if(cp->next)
+ cp->next->prev = (*cl);
+
+ cp->next = (*cl);
+ (*cl)->prev = cp;
+ }
+}
+
+
+#define SETUP_PAT_STATUS(ctmp,svar,val,htitle,hval) \
+ {char tmp[MAXPATH+1]; \
+ int i, j, lv; \
+ NAMEVAL_S *f; \
+ \
+ /* Blank line */ \
+ new_confline(&ctmp); \
+ ctmp->flags |= CF_NOSELECT | CF_B_LINE; \
+ \
+ new_confline(&ctmp); \
+ ctmp->var = &svar; \
+ ctmp->keymenu = &config_radiobutton_keymenu; \
+ ctmp->help = NO_HELP; \
+ ctmp->tool = NULL; \
+ snprintf(tmp, sizeof(tmp), "%-s =", svar.name); \
+ tmp[sizeof(tmp)-1] = '\0'; \
+ ctmp->varname = cpystr(tmp); \
+ ctmp->varnamep = ctmpb = ctmp; \
+ ctmp->flags |= (CF_NOSELECT | CF_STARTITEM); \
+ \
+ new_confline(&ctmp); \
+ ctmp->var = NULL; \
+ ctmp->valoffset = rindent; \
+ ctmp->keymenu = &config_radiobutton_keymenu; \
+ ctmp->help = NO_HELP; \
+ ctmp->tool = NULL; \
+ ctmp->varnamep = ctmpb; \
+ ctmp->flags |= CF_NOSELECT; \
+ ctmp->value = cpystr("Set Choose One"); \
+ \
+ new_confline(&ctmp); \
+ ctmp->var = NULL; \
+ ctmp->valoffset = rindent; \
+ ctmp->keymenu = &config_radiobutton_keymenu; \
+ ctmp->help = NO_HELP; \
+ ctmp->tool = radio_tool; \
+ ctmp->varnamep = ctmpb; \
+ ctmp->flags |= CF_NOSELECT; \
+ ctmp->value = cpystr(set_choose); \
+ \
+ /* find longest value's name */ \
+ for(lv = 0, i = 0; (f = role_status_types(i)); i++) \
+ if(lv < (j = utf8_width(f->name))) \
+ lv = j; \
+ \
+ lv = MIN(lv, 100); \
+ \
+ for(i = 0; (f = role_status_types(i)); i++){ \
+ new_confline(&ctmp); \
+ ctmp->help_title= htitle; \
+ ctmp->var = &svar; \
+ ctmp->valoffset = rindent; \
+ ctmp->keymenu = &config_radiobutton_keymenu; \
+ ctmp->help = hval; \
+ ctmp->varmem = i; \
+ ctmp->tool = radio_tool; \
+ ctmp->varnamep = ctmpb; \
+ utf8_snprintf(tmp, sizeof(tmp), "(%c) %-*.*w", (((!(def && def->patgrp) || \
+ val == -1) && \
+ f->value == PAT_STAT_EITHER) || \
+ (def && def->patgrp && \
+ f->value == val)) \
+ ? R_SELD : ' ', \
+ lv, lv, f->name); \
+ tmp[sizeof(tmp)-1] = '\0'; \
+ ctmp->value = cpystr(tmp); \
+ } \
+ }
+
+#define SETUP_MSG_STATE(ctmp,svar,val,htitle,hval) \
+ {char tmp[MAXPATH+1]; \
+ int i, j, lv; \
+ NAMEVAL_S *f; \
+ \
+ /* Blank line */ \
+ new_confline(&ctmp); \
+ ctmp->flags |= CF_NOSELECT | CF_B_LINE; \
+ \
+ new_confline(&ctmp); \
+ ctmp->var = &svar; \
+ ctmp->keymenu = &config_radiobutton_keymenu; \
+ ctmp->help = NO_HELP; \
+ ctmp->tool = NULL; \
+ snprintf(tmp, sizeof(tmp), "%-s =", svar.name); \
+ tmp[sizeof(tmp)-1] = '\0'; \
+ ctmp->varname = cpystr(tmp); \
+ ctmp->varnamep = ctmpb = ctmp; \
+ ctmp->flags |= (CF_NOSELECT | CF_STARTITEM); \
+ \
+ new_confline(&ctmp); \
+ ctmp->var = NULL; \
+ ctmp->valoffset = rindent; \
+ ctmp->keymenu = &config_radiobutton_keymenu; \
+ ctmp->help = NO_HELP; \
+ ctmp->tool = NULL; \
+ ctmp->varnamep = ctmpb; \
+ ctmp->flags |= CF_NOSELECT; \
+ ctmp->value = cpystr("Set Choose One"); \
+ \
+ new_confline(&ctmp); \
+ ctmp->var = NULL; \
+ ctmp->valoffset = rindent; \
+ ctmp->keymenu = &config_radiobutton_keymenu; \
+ ctmp->help = NO_HELP; \
+ ctmp->tool = radio_tool; \
+ ctmp->varnamep = ctmpb; \
+ ctmp->flags |= CF_NOSELECT; \
+ ctmp->value = cpystr(set_choose); \
+ \
+ /* find longest value's name */ \
+ for(lv = 0, i = 0; (f = msg_state_types(i)); i++) \
+ if(lv < (j = utf8_width(f->name))) \
+ lv = j; \
+ \
+ lv = MIN(lv, 100); \
+ \
+ for(i = 0; (f = msg_state_types(i)); i++){ \
+ new_confline(&ctmp); \
+ ctmp->help_title= htitle; \
+ ctmp->var = &svar; \
+ ctmp->valoffset = rindent; \
+ ctmp->keymenu = &config_radiobutton_keymenu; \
+ ctmp->help = hval; \
+ ctmp->varmem = i; \
+ ctmp->tool = radio_tool; \
+ ctmp->varnamep = ctmpb; \
+ utf8_snprintf(tmp, sizeof(tmp), "(%c) %-*.*w", (f->value == val) \
+ ? R_SELD : ' ', \
+ lv, lv, f->name); \
+ tmp[sizeof(tmp)-1] = '\0'; \
+ ctmp->value = cpystr(tmp); \
+ } \
+ }
+
+
+#define FEAT_SENTDATE 0
+#define FEAT_IFNOTDEL 1
+#define FEAT_NONTERM 2
+bitmap_t feat_option_list;
+
+#define INABOOK_FROM 0
+#define INABOOK_REPLYTO 1
+#define INABOOK_SENDER 2
+#define INABOOK_TO 3
+#define INABOOK_CC 4
+bitmap_t inabook_type_list;
+
+
+#define INICK_INICK_CONF 0
+#define INICK_FROM_CONF 1
+#define INICK_REPLYTO_CONF 2
+#define INICK_FCC_CONF 3
+#define INICK_LITSIG_CONF 4 /* this needs to come before SIG_CONF */
+#define INICK_SIG_CONF 5
+#define INICK_TEMPL_CONF 6
+#define INICK_CSTM_CONF 7
+#define INICK_SMTP_CONF 8
+#define INICK_NNTP_CONF 9
+CONF_S *inick_confs[INICK_NNTP_CONF+1];
+
+
+/*
+ * Screen for editing configuration of a role.
+ *
+ * Args ps -- pine struct
+ * def -- default role values to start with
+ * title -- part of title at top of screen
+ * rflags -- which parts of role to edit
+ * result -- This is the returned PAT_S, freed by caller.
+ *
+ * Returns: 0 if no change
+ * 1 if user requested a change
+ * (change is stored in raw_server and hasn't been acted upon yet)
+ * 10 user says abort
+ */
+int
+role_config_edit_screen(struct pine *ps, PAT_S *def, char *title, long int rflags, PAT_S **result)
+{
+ OPT_SCREEN_S screen, *saved_screen;
+ CONF_S *ctmp = NULL, *ctmpb, *first_line = NULL;
+ struct variable nick_var, to_pat_var, from_pat_var,
+ comment_var,
+ sender_pat_var, cc_pat_var, recip_pat_var, news_pat_var,
+ subj_pat_var, inick_var, fldr_type_var, folder_pat_var,
+ abook_type_var, abook_pat_var,
+ alltext_pat_var, scorei_pat_var, partic_pat_var,
+ bodytext_pat_var, age_pat_var, size_pat_var,
+ keyword_pat_var, charset_pat_var,
+ stat_new_var, stat_del_var, stat_imp_var, stat_ans_var,
+ stat_rec_var, stat_8bit_var,
+ stat_bom_var, stat_boy_var,
+ cat_cmd_var, cati_var, cat_lim_var,
+ from_act_var, replyto_act_var, fcc_act_var,
+ sig_act_var, litsig_act_var, templ_act_var,
+ cstm_act_var, smtp_act_var, nntp_act_var,
+ sort_act_var, iform_act_var, startup_var,
+ repl_type_var, forw_type_var, comp_type_var, score_act_var,
+ hdrtok_act_var,
+ rolecolor_vars[2], filter_type_var, folder_act_var,
+ keyword_set_var, keyword_clr_var,
+ filt_new_var, filt_del_var, filt_imp_var, filt_ans_var;
+ struct variable *v, *varlist[65], opt_var, inabook_type_var;
+ char *nick = NULL, *inick = NULL, *fldr_type_pat = NULL,
+ *comment = NULL,
+ *scorei_pat = NULL, *age_pat = NULL, *size_pat = NULL,
+ *abook_type_pat = NULL,
+ *stat_new = NULL, *stat_del = NULL, *stat_imp = NULL,
+ *stat_rec = NULL, *stat_ans = NULL, *stat_8bit = NULL,
+ *stat_bom = NULL, *stat_boy = NULL,
+ *filt_new = NULL, *filt_del = NULL, *filt_imp = NULL,
+ *filt_ans = NULL, *cati = NULL, *cat_lim = NULL,
+ *from_act = NULL, *replyto_act = NULL, *fcc_act = NULL,
+ *sig_act = NULL, *litsig_act = NULL, *sort_act = NULL,
+ *templ_act = NULL, *repl_type = NULL, *forw_type = NULL,
+ *comp_type = NULL, *rc_fg = NULL, *rc_bg = NULL,
+ *score_act = NULL, *filter_type = NULL,
+ *hdrtok_act = NULL,
+ *iform_act = NULL, *startup_act = NULL,
+ *old_fg = NULL, *old_bg = NULL;
+ char **to_pat = NULL, **from_pat = NULL, **sender_pat = NULL,
+ **cc_pat = NULL, **news_pat = NULL, **recip_pat = NULL,
+ **partic_pat = NULL, **subj_pat = NULL,
+ **alltext_pat = NULL, **bodytext_pat = NULL,
+ **keyword_pat = NULL, **folder_pat = NULL,
+ **charset_pat = NULL,
+ **abook_pat = NULL, **folder_act = NULL,
+ **keyword_set = NULL, **keyword_clr = NULL,
+ **cat_cmd = NULL, **cstm_act = NULL, **smtp_act = NULL,
+ **nntp_act = NULL, **spat;
+ char tmp[MAXPATH+1], **apval, **lval, ***alval, *p;
+ /* TRANSLATORS: These next 4 are subheading for sections of a configuration screen. */
+ char *fstr = _(" CURRENT FOLDER CONDITIONS BEGIN HERE ");
+ char mstr[50];
+ char *astr = _(" ACTIONS BEGIN HERE ");
+ char *ustr = _(" USES BEGIN HERE ");
+ char *ostr = _(" OPTIONS BEGIN HERE ");
+ char *s_p_v_n = _("Subject pattern"); /* longest one of these */
+ char *u_s_s = _("Use SMTP Server"); /* ditto */
+ char *c_v_n = _("Exit Status Interval"); /* ditto */
+ SortOrder def_sort;
+ int def_sort_rev;
+ ARBHDR_S *aa, *a;
+ EARB_S *earb = NULL, *ea;
+ int rv, i, j, lv, pindent, maxpindent, rindent,
+ scoreval = 0, edit_role, wid,
+ edit_incol, edit_score, edit_filter, edit_other, edit_srch,
+ dval, ival, nval, aval, fval,
+ per_folder_only, need_uses, need_options;
+ int (*radio_tool)(struct pine *, int, CONF_S **, unsigned);
+ int (*addhdr_tool)(struct pine *, int, CONF_S **, unsigned);
+ int (*t_tool)(struct pine *, int, CONF_S **, unsigned);
+ NAMEVAL_S *f;
+
+ dprint((4, "role_config_edit_screen()\n"));
+ edit_role = rflags & ROLE_DO_ROLES;
+ edit_incol = rflags & ROLE_DO_INCOLS;
+ edit_score = rflags & ROLE_DO_SCORES;
+ edit_filter = rflags & ROLE_DO_FILTER;
+ edit_other = rflags & ROLE_DO_OTHER;
+ edit_srch = rflags & ROLE_DO_SRCH;
+
+ per_folder_only = (edit_other &&
+ !(edit_role || edit_incol || edit_score || edit_filter || edit_srch));
+ need_uses = edit_role;
+ need_options = !per_folder_only;
+
+ radio_tool = edit_filter ? role_filt_radiobutton_tool
+ : role_radiobutton_tool;
+ t_tool = edit_filter ? role_filt_text_tool : role_text_tool;
+ addhdr_tool = edit_filter ? role_filt_addhdr_tool : role_addhdr_tool;
+
+ rindent = 12; /* radio indent */
+
+ /*
+ * We edit by making a nested call to conf_scroll_screen.
+ * We use some fake struct variables to get back the results in, and
+ * so we can use the existing tools from the config screen.
+ */
+ varlist[j = 0] = &nick_var;
+ varlist[++j] = &comment_var;
+ varlist[++j] = &to_pat_var;
+ varlist[++j] = &from_pat_var;
+ varlist[++j] = &sender_pat_var;
+ varlist[++j] = &cc_pat_var;
+ varlist[++j] = &recip_pat_var;
+ varlist[++j] = &partic_pat_var;
+ varlist[++j] = &news_pat_var;
+ varlist[++j] = &subj_pat_var;
+ varlist[++j] = &alltext_pat_var;
+ varlist[++j] = &bodytext_pat_var;
+ varlist[++j] = &keyword_pat_var;
+ varlist[++j] = &charset_pat_var;
+ varlist[++j] = &age_pat_var;
+ varlist[++j] = &size_pat_var;
+ varlist[++j] = &scorei_pat_var;
+ varlist[++j] = &stat_new_var;
+ varlist[++j] = &stat_rec_var;
+ varlist[++j] = &stat_del_var;
+ varlist[++j] = &stat_imp_var;
+ varlist[++j] = &stat_ans_var;
+ varlist[++j] = &stat_8bit_var;
+ varlist[++j] = &stat_bom_var;
+ varlist[++j] = &stat_boy_var;
+ varlist[++j] = &cat_cmd_var;
+ varlist[++j] = &cati_var;
+ varlist[++j] = &cat_lim_var;
+ varlist[++j] = &inick_var;
+ varlist[++j] = &fldr_type_var;
+ varlist[++j] = &folder_pat_var;
+ varlist[++j] = &abook_type_var;
+ varlist[++j] = &abook_pat_var;
+ varlist[++j] = &from_act_var;
+ varlist[++j] = &replyto_act_var;
+ varlist[++j] = &fcc_act_var;
+ varlist[++j] = &sig_act_var;
+ varlist[++j] = &litsig_act_var;
+ varlist[++j] = &sort_act_var;
+ varlist[++j] = &iform_act_var;
+ varlist[++j] = &startup_var;
+ varlist[++j] = &templ_act_var;
+ varlist[++j] = &cstm_act_var;
+ varlist[++j] = &smtp_act_var;
+ varlist[++j] = &nntp_act_var;
+ varlist[++j] = &score_act_var;
+ varlist[++j] = &hdrtok_act_var;
+ varlist[++j] = &repl_type_var;
+ varlist[++j] = &forw_type_var;
+ varlist[++j] = &comp_type_var;
+ varlist[++j] = &rolecolor_vars[0];
+ varlist[++j] = &rolecolor_vars[1];
+ varlist[++j] = &filter_type_var;
+ varlist[++j] = &folder_act_var;
+ varlist[++j] = &keyword_set_var;
+ varlist[++j] = &keyword_clr_var;
+ varlist[++j] = &filt_new_var;
+ varlist[++j] = &filt_del_var;
+ varlist[++j] = &filt_imp_var;
+ varlist[++j] = &filt_ans_var;
+ varlist[++j] = &opt_var;
+ varlist[++j] = &inabook_type_var;
+ varlist[++j] = NULL;
+ for(j = 0; varlist[j]; j++)
+ memset(varlist[j], 0, sizeof(struct variable));
+
+ if(def && ((def->patgrp && def->patgrp->bogus) || (def->action && def->action->bogus))){
+ char msg[MAX_SCREEN_COLS+1];
+
+ snprintf(msg, sizeof(msg),
+ _("Rule contains unknown %s element, possibly from newer Alpine"),
+ (def->patgrp && def->patgrp->bogus) ? "pattern" : "action");
+ msg[sizeof(msg)-1] = '\0';
+ q_status_message(SM_ORDER | SM_DING, 7, 7, msg);
+ q_status_message(SM_ORDER | SM_DING, 7, 7,
+ _("Editing with this version of Alpine will destroy information"));
+ flush_status_messages(0);
+ }
+
+ role_forw_ptr = role_repl_ptr = role_fldr_ptr = role_filt_ptr = NULL;
+ role_status1_ptr = role_status2_ptr = role_status3_ptr = NULL;
+ role_status4_ptr = role_status5_ptr = role_status6_ptr = NULL;
+ role_status7_ptr = NULL; role_status8_ptr = NULL;
+ msg_state1_ptr = msg_state2_ptr = NULL;
+ msg_state3_ptr = msg_state4_ptr = NULL;
+ role_afrom_ptr = startup_ptr = NULL;
+ role_comment_ptr = NULL;
+
+ nick_var.name = cpystr(_("Nickname"));
+ nick_var.is_used = 1;
+ nick_var.is_user = 1;
+ apval = APVAL(&nick_var, ew);
+ *apval = (def && def->patgrp && def->patgrp->nick)
+ ? cpystr(def->patgrp->nick) : NULL;
+
+ nick_var.global_val.p = cpystr(edit_role
+ ? "Alternate Role"
+ : (edit_other
+ ? "Other Rule"
+ : (edit_incol
+ ? "Index Color Rule"
+ : (edit_score
+ ? "Score Rule"
+ : "Filter Rule"))));
+ set_current_val(&nick_var, FALSE, FALSE);
+
+ role_comment_ptr = &comment_var; /* so radiobuttons can tell */
+ comment_var.name = cpystr(_("Comment"));
+ comment_var.is_used = 1;
+ comment_var.is_user = 1;
+ apval = APVAL(&comment_var, ew);
+ *apval = (def && def->patgrp && def->patgrp->comment)
+ ? cpystr(def->patgrp->comment) : NULL;
+ set_current_val(&comment_var, FALSE, FALSE);
+
+ /* TRANSLATORS: Quite a few of the translations to follow are from the
+ rules editing screens. These are mostly headings of individual categories
+ of criteria which can be set in a rule. */
+ setup_dummy_pattern_var(&to_pat_var, _("To pattern"),
+ (def && def->patgrp) ? def->patgrp->to : NULL);
+ setup_dummy_pattern_var(&from_pat_var, _("From pattern"),
+ (def && def->patgrp) ? def->patgrp->from : NULL);
+ setup_dummy_pattern_var(&sender_pat_var, _("Sender pattern"),
+ (def && def->patgrp) ? def->patgrp->sender : NULL);
+ setup_dummy_pattern_var(&cc_pat_var, _("Cc pattern"),
+ (def && def->patgrp) ? def->patgrp->cc : NULL);
+ setup_dummy_pattern_var(&news_pat_var, _("News pattern"),
+ (def && def->patgrp) ? def->patgrp->news : NULL);
+ setup_dummy_pattern_var(&subj_pat_var, s_p_v_n,
+ (def && def->patgrp) ? def->patgrp->subj : NULL);
+ /* TRANSLATORS: Recip is an abbreviation for Recipients which stands for
+ all of the recipients of a message. */
+ setup_dummy_pattern_var(&recip_pat_var, _("Recip pattern"),
+ (def && def->patgrp) ? def->patgrp->recip : NULL);
+ /* TRANSLATORS: Partic is an abbreviation for Participants which stands for
+ all of the recipients plus the sender of a message. */
+ setup_dummy_pattern_var(&partic_pat_var, _("Partic pattern"),
+ (def && def->patgrp) ? def->patgrp->partic : NULL);
+ /* TRANSLATORS: AllText means all of the text of a message */
+ setup_dummy_pattern_var(&alltext_pat_var, _("AllText pattern"),
+ (def && def->patgrp) ? def->patgrp->alltext : NULL);
+ /* TRANSLATORS: BdyText means the text of a message but not the text in the headers */
+ setup_dummy_pattern_var(&bodytext_pat_var, _("BdyText pattern"),
+ (def && def->patgrp) ? def->patgrp->bodytext : NULL);
+
+ setup_dummy_pattern_var(&keyword_pat_var, _("Keyword pattern"),
+ (def && def->patgrp) ? def->patgrp->keyword : NULL);
+ setup_dummy_pattern_var(&charset_pat_var, _("Charset pattern"),
+ (def && def->patgrp) ? def->patgrp->charsets : NULL);
+
+ age_pat_global_ptr = &age_pat_var;
+ /* TRANSLATORS: Age interval is a setting for how old the message is. */
+ age_pat_var.name = cpystr(_("Age interval"));
+ age_pat_var.is_used = 1;
+ age_pat_var.is_user = 1;
+ if(def && def->patgrp && def->patgrp->do_age){
+ apval = APVAL(&age_pat_var, ew);
+ *apval = stringform_of_intvl(def->patgrp->age);
+ }
+
+ set_current_val(&age_pat_var, FALSE, FALSE);
+
+ size_pat_global_ptr = &size_pat_var;
+ size_pat_var.name = cpystr(_("Size interval"));
+ size_pat_var.is_used = 1;
+ size_pat_var.is_user = 1;
+ if(def && def->patgrp && def->patgrp->do_size){
+ apval = APVAL(&size_pat_var, ew);
+ *apval = stringform_of_intvl(def->patgrp->size);
+ }
+
+ set_current_val(&size_pat_var, FALSE, FALSE);
+
+ scorei_pat_global_ptr = &scorei_pat_var;
+ /* TRANSLATORS: Score is an alpine concept where the score can be kept for a
+ message to see if it is a message you want to look at. */
+ scorei_pat_var.name = cpystr(_("Score interval"));
+ scorei_pat_var.is_used = 1;
+ scorei_pat_var.is_user = 1;
+ if(def && def->patgrp && def->patgrp->do_score){
+ apval = APVAL(&scorei_pat_var, ew);
+ *apval = stringform_of_intvl(def->patgrp->score);
+ }
+
+ set_current_val(&scorei_pat_var, FALSE, FALSE);
+
+ role_status1_ptr = &stat_del_var; /* so radiobuttons can tell */
+ stat_del_var.name = cpystr(_("Message is Deleted?"));
+ stat_del_var.is_used = 1;
+ stat_del_var.is_user = 1;
+ apval = APVAL(&stat_del_var, ew);
+ *apval = (f=role_status_types((def && def->patgrp) ? def->patgrp->stat_del : -1)) ? cpystr(f->name) : NULL;
+ set_current_val(&stat_del_var, FALSE, FALSE);
+
+ role_status2_ptr = &stat_new_var; /* so radiobuttons can tell */
+ stat_new_var.name = cpystr(_("Message is New (Unseen)?"));
+ stat_new_var.is_used = 1;
+ stat_new_var.is_user = 1;
+ apval = APVAL(&stat_new_var, ew);
+ *apval = (f=role_status_types((def && def->patgrp) ? def->patgrp->stat_new : -1)) ? cpystr(f->name) : NULL;
+ set_current_val(&stat_new_var, FALSE, FALSE);
+
+ role_status3_ptr = &stat_imp_var; /* so radiobuttons can tell */
+ stat_imp_var.name = cpystr(_("Message is Important?"));
+ stat_imp_var.is_used = 1;
+ stat_imp_var.is_user = 1;
+ apval = APVAL(&stat_imp_var, ew);
+ *apval = (f=role_status_types((def && def->patgrp) ? def->patgrp->stat_imp : -1)) ? cpystr(f->name) : NULL;
+ set_current_val(&stat_imp_var, FALSE, FALSE);
+
+ role_status4_ptr = &stat_ans_var; /* so radiobuttons can tell */
+ stat_ans_var.name = cpystr(_("Message is Answered?"));
+ stat_ans_var.is_used = 1;
+ stat_ans_var.is_user = 1;
+ apval = APVAL(&stat_ans_var, ew);
+ *apval = (f=role_status_types((def && def->patgrp) ? def->patgrp->stat_ans : -1)) ? cpystr(f->name) : NULL;
+ set_current_val(&stat_ans_var, FALSE, FALSE);
+
+ role_status5_ptr = &stat_8bit_var; /* so radiobuttons can tell */
+ stat_8bit_var.name = cpystr(_("Subject contains raw 8-bit?"));
+ stat_8bit_var.is_used = 1;
+ stat_8bit_var.is_user = 1;
+ apval = APVAL(&stat_8bit_var, ew);
+ *apval = (f=role_status_types((def && def->patgrp) ? def->patgrp->stat_8bitsubj : -1)) ? cpystr(f->name) : NULL;
+ set_current_val(&stat_8bit_var, FALSE, FALSE);
+
+ role_status6_ptr = &stat_rec_var; /* so radiobuttons can tell */
+ stat_rec_var.name = cpystr(_("Message is Recent?"));
+ stat_rec_var.is_used = 1;
+ stat_rec_var.is_user = 1;
+ apval = APVAL(&stat_rec_var, ew);
+ *apval = (f=role_status_types((def && def->patgrp) ? def->patgrp->stat_rec : -1)) ? cpystr(f->name) : NULL;
+ set_current_val(&stat_rec_var, FALSE, FALSE);
+
+ role_status7_ptr = &stat_bom_var; /* so radiobuttons can tell */
+ stat_bom_var.name = cpystr(_("Beginning of Month?"));
+ stat_bom_var.is_used = 1;
+ stat_bom_var.is_user = 1;
+ apval = APVAL(&stat_bom_var, ew);
+ *apval = (f=role_status_types((def && def->patgrp) ? def->patgrp->stat_bom : -1)) ? cpystr(f->name) : NULL;
+ set_current_val(&stat_bom_var, FALSE, FALSE);
+
+ role_status8_ptr = &stat_boy_var; /* so radiobuttons can tell */
+ stat_boy_var.name = cpystr(_("Beginning of Year?"));
+ stat_boy_var.is_used = 1;
+ stat_boy_var.is_user = 1;
+ apval = APVAL(&stat_boy_var, ew);
+ *apval = (f=role_status_types((def && def->patgrp) ? def->patgrp->stat_boy : -1)) ? cpystr(f->name) : NULL;
+ set_current_val(&stat_boy_var, FALSE, FALSE);
+
+
+
+ convert_statebits_to_vals((def && def->action) ? def->action->state_setting_bits : 0L, &dval, &aval, &ival, &nval);
+ msg_state1_ptr = &filt_del_var; /* so radiobuttons can tell */
+ /* TRANSLATORS: these are actions that might be taken by the rule */
+ filt_del_var.name = cpystr(_("Set Deleted Status"));
+ filt_del_var.is_used = 1;
+ filt_del_var.is_user = 1;
+ apval = APVAL(&filt_del_var, ew);
+ *apval = (f=msg_state_types(dval)) ? cpystr(f->name) : NULL;
+ set_current_val(&filt_del_var, FALSE, FALSE);
+
+ msg_state2_ptr = &filt_new_var; /* so radiobuttons can tell */
+ filt_new_var.name = cpystr(_("Set New Status"));
+ filt_new_var.is_used = 1;
+ filt_new_var.is_user = 1;
+ apval = APVAL(&filt_new_var, ew);
+ *apval = (f=msg_state_types(nval)) ? cpystr(f->name) : NULL;
+ set_current_val(&filt_new_var, FALSE, FALSE);
+
+ msg_state3_ptr = &filt_imp_var; /* so radiobuttons can tell */
+ filt_imp_var.name = cpystr(_("Set Important Status"));
+ filt_imp_var.is_used = 1;
+ filt_imp_var.is_user = 1;
+ apval = APVAL(&filt_imp_var, ew);
+ *apval = (f=msg_state_types(ival)) ? cpystr(f->name) : NULL;
+ set_current_val(&filt_imp_var, FALSE, FALSE);
+
+ msg_state4_ptr = &filt_ans_var; /* so radiobuttons can tell */
+ filt_ans_var.name = cpystr(_("Set Answered Status"));
+ filt_ans_var.is_used = 1;
+ filt_ans_var.is_user = 1;
+ apval = APVAL(&filt_ans_var, ew);
+ *apval = (f=msg_state_types(aval)) ? cpystr(f->name) : NULL;
+ set_current_val(&filt_ans_var, FALSE, FALSE);
+
+ inick_var.name = cpystr(_("Initialize settings using role"));
+ inick_var.is_used = 1;
+ inick_var.is_user = 1;
+ apval = APVAL(&inick_var, ew);
+ *apval = (def && def->action && def->action->inherit_nick &&
+ def->action->inherit_nick[0])
+ ? cpystr(def->action->inherit_nick) : NULL;
+
+ role_fldr_ptr = &fldr_type_var; /* so radiobuttons can tell */
+ fldr_type_var.name = cpystr(_("Current Folder Type"));
+ fldr_type_var.is_used = 1;
+ fldr_type_var.is_user = 1;
+ apval = APVAL(&fldr_type_var, ew);
+ *apval = (f=pat_fldr_types((def && def->patgrp) ? def->patgrp->fldr_type : (!def && edit_filter) ? FLDR_SPECIFIC : FLDR_DEFL)) ? cpystr(f->name) : NULL;
+ set_current_val(&fldr_type_var, FALSE, FALSE);
+
+ setup_dummy_pattern_var(&folder_pat_var, _("Folder List"),
+ (def && def->patgrp) ? def->patgrp->folder : NULL);
+ /* special default for folder_pat */
+ alval = ALVAL(&folder_pat_var, ew);
+ if(alval && !*alval && !def && edit_filter){
+ char **ltmp;
+
+ ltmp = (char **) fs_get(2 * sizeof(*ltmp));
+ ltmp[0] = cpystr(ps_global->inbox_name);
+ ltmp[1] = NULL;
+ *alval = ltmp;
+ set_current_val(&folder_pat_var, FALSE, FALSE);
+ }
+
+ role_afrom_ptr = &abook_type_var; /* so radiobuttons can tell */
+ abook_type_var.name = cpystr(_("Address in address book?"));
+ abook_type_var.is_used = 1;
+ abook_type_var.is_user = 1;
+ apval = APVAL(&abook_type_var, ew);
+ *apval = (f=inabook_fldr_types((def && def->patgrp) ? def->patgrp->inabook : IAB_EITHER)) ? cpystr(f->name) : NULL;
+ set_current_val(&abook_type_var, FALSE, FALSE);
+
+ /* TRANSLATORS: Abook is an abbreviation for Address Book */
+ setup_dummy_pattern_var(&abook_pat_var, _("Abook List"),
+ (def && def->patgrp) ? def->patgrp->abooks : NULL);
+
+ /*
+ * This is a little different from some of the other patterns. Tt is
+ * actually a char ** in the struct instead of a PATTERN_S.
+ */
+ cat_cmd_global_ptr = &cat_cmd_var;
+ cat_cmd_var.name = cpystr(_("External Categorizer Commands"));
+ cat_cmd_var.is_used = 1;
+ cat_cmd_var.is_user = 1;
+ cat_cmd_var.is_list = 1;
+ alval = ALVAL(&cat_cmd_var, ew);
+ *alval = (def && def->patgrp && def->patgrp->category_cmd &&
+ def->patgrp->category_cmd[0])
+ ? copy_list_array(def->patgrp->category_cmd) : NULL;
+ set_current_val(&cat_cmd_var, FALSE, FALSE);
+
+ cati_global_ptr = &cati_var;
+ cati_var.name = cpystr(c_v_n);
+ cati_var.is_used = 1;
+ cati_var.is_user = 1;
+ if(def && def->patgrp && def->patgrp->do_cat && def->patgrp->category_cmd &&
+ def->patgrp->category_cmd[0]){
+ apval = APVAL(&cati_var, ew);
+ *apval = stringform_of_intvl(def->patgrp->cat);
+ }
+
+ set_current_val(&cati_var, FALSE, FALSE);
+
+ cat_lim_global_ptr = &cat_lim_var;
+ cat_lim_var.name = cpystr(_("Character Limit"));
+ cat_lim_var.is_used = 1;
+ cat_lim_var.is_user = 1;
+ cat_lim_var.global_val.p = cpystr("-1");
+ apval = APVAL(&cat_lim_var, ew);
+ if(def && def->patgrp && def->patgrp->category_cmd &&
+ def->patgrp->category_cmd[0] && def->patgrp->cat_lim != -1){
+ *apval = (char *) fs_get(20 * sizeof(char));
+ snprintf(*apval, 20, "%ld", def->patgrp->cat_lim);
+ (*apval)[20-1] = '\0';
+ }
+
+ set_current_val(&cat_lim_var, FALSE, FALSE);
+
+ from_act_var.name = cpystr(_("Set From"));
+ from_act_var.is_used = 1;
+ from_act_var.is_user = 1;
+ if(def && def->action && def->action->from){
+ char *bufp;
+ size_t len;
+
+ len = est_size(def->action->from);
+ bufp = (char *) fs_get(len * sizeof(char));
+ apval = APVAL(&from_act_var, ew);
+ *apval = addr_string_mult(def->action->from, bufp, len);
+ }
+ else{
+ apval = APVAL(&from_act_var, ew);
+ *apval = NULL;
+ }
+
+ replyto_act_var.name = cpystr(_("Set Reply-To"));
+ replyto_act_var.is_used = 1;
+ replyto_act_var.is_user = 1;
+ if(def && def->action && def->action->replyto){
+ char *bufp;
+ size_t len;
+
+ len = est_size(def->action->replyto);
+ bufp = (char *) fs_get(len * sizeof(char));
+ apval = APVAL(&replyto_act_var, ew);
+ *apval = addr_string_mult(def->action->replyto, bufp, len);
+ }
+ else{
+ apval = APVAL(&replyto_act_var, ew);
+ *apval = NULL;
+ }
+
+ fcc_act_var.name = cpystr(_("Set Fcc"));
+ fcc_act_var.is_used = 1;
+ fcc_act_var.is_user = 1;
+ apval = APVAL(&fcc_act_var, ew);
+ *apval = (def && def->action && def->action->fcc)
+ ? cpystr(def->action->fcc) : NULL;
+
+ sort_act_var.name = cpystr(_("Set Sort Order"));
+ sort_act_var.is_used = 1;
+ sort_act_var.is_user = 1;
+ apval = APVAL(&sort_act_var, ew);
+ if(def && def->action && def->action->is_a_other &&
+ def->action->sort_is_set){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s", sort_name(def->action->sortorder),
+ (def->action->revsort) ? "/Reverse" : "");
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ *apval = cpystr(tmp_20k_buf);
+ }
+ else
+ *apval = NULL;
+
+ iform_act_var.name = cpystr(_("Set Index Format"));
+ iform_act_var.is_used = 1;
+ iform_act_var.is_user = 1;
+ apval = APVAL(&iform_act_var, ew);
+ *apval = (def && def->action && def->action->is_a_other &&
+ def->action->index_format)
+ ? cpystr(def->action->index_format) : NULL;
+ if(ps_global->VAR_INDEX_FORMAT){
+ iform_act_var.global_val.p = cpystr(ps_global->VAR_INDEX_FORMAT);
+ set_current_val(&iform_act_var, FALSE, FALSE);
+ }
+
+ startup_ptr = &startup_var;
+ startup_var.name = cpystr(_("Set Startup Rule"));
+ startup_var.is_used = 1;
+ startup_var.is_user = 1;
+ apval = APVAL(&startup_var, ew);
+ *apval = NULL;
+ if(def && def->action && def->action->is_a_other){
+ *apval = (f=startup_rules(def->action->startup_rule))
+ ? cpystr(f->name) : NULL;
+ set_current_val(&startup_var, FALSE, FALSE);
+ }
+ if(!*apval){
+ *apval = (f=startup_rules(IS_NOTSET)) ? cpystr(f->name) : NULL;
+ set_current_val(&startup_var, FALSE, FALSE);
+ }
+
+ /* TRANSLATORS: LiteralSig is a way to keep the signature in the configuration
+ file instead of in a separate Signature file. */
+ litsig_act_var.name = cpystr(_("Set LiteralSig"));
+ litsig_act_var.is_used = 1;
+ litsig_act_var.is_user = 1;
+ apval = APVAL(&litsig_act_var, ew);
+ *apval = (def && def->action && def->action->litsig)
+ ? cpystr(def->action->litsig) : NULL;
+
+ sig_act_var.name = cpystr(_("Set Signature"));
+ sig_act_var.is_used = 1;
+ sig_act_var.is_user = 1;
+ apval = APVAL(&sig_act_var, ew);
+ *apval = (def && def->action && def->action->sig)
+ ? cpystr(def->action->sig) : NULL;
+
+ /* TRANSLATORS: A template is a skeleton of a message to be used
+ for composing a new message */
+ templ_act_var.name = cpystr(_("Set Template"));
+ templ_act_var.is_used = 1;
+ templ_act_var.is_user = 1;
+ apval = APVAL(&templ_act_var, ew);
+ *apval = (def && def->action && def->action->template)
+ ? cpystr(def->action->template) : NULL;
+
+ /* TRANSLATORS: Hdrs is an abbreviation for Headers */
+ cstm_act_var.name = cpystr(_("Set Other Hdrs"));
+ cstm_act_var.is_used = 1;
+ cstm_act_var.is_user = 1;
+ cstm_act_var.is_list = 1;
+ alval = ALVAL(&cstm_act_var, ew);
+ *alval = (def && def->action && def->action->cstm)
+ ? copy_list_array(def->action->cstm) : NULL;
+
+ smtp_act_var.name = cpystr(u_s_s);
+ smtp_act_var.is_used = 1;
+ smtp_act_var.is_user = 1;
+ smtp_act_var.is_list = 1;
+ alval = ALVAL(&smtp_act_var, ew);
+ *alval = (def && def->action && def->action->smtp)
+ ? copy_list_array(def->action->smtp) : NULL;
+
+ nntp_act_var.name = cpystr(_("Use NNTP Server"));
+ nntp_act_var.is_used = 1;
+ nntp_act_var.is_user = 1;
+ nntp_act_var.is_list = 1;
+ alval = ALVAL(&nntp_act_var, ew);
+ *alval = (def && def->action && def->action->nntp)
+ ? copy_list_array(def->action->nntp) : NULL;
+
+ score_act_global_ptr = &score_act_var;
+ score_act_var.name = cpystr(_("Score Value"));
+ score_act_var.is_used = 1;
+ score_act_var.is_user = 1;
+ if(def && def->action && def->action->scoreval >= SCORE_MIN &&
+ def->action->scoreval <= SCORE_MAX)
+ scoreval = def->action->scoreval;
+
+ score_act_var.global_val.p = cpystr("0");
+ if(scoreval != 0){
+ apval = APVAL(&score_act_var, ew);
+ *apval = (char *)fs_get(20 * sizeof(char));
+ snprintf(*apval, 20, "%d", scoreval);
+ (*apval)[20-1] = '\0';
+ }
+
+ set_current_val(&score_act_var, FALSE, FALSE);
+
+ hdrtok_act_var.name = cpystr(_("Score From Header"));
+ hdrtok_act_var.is_used = 1;
+ hdrtok_act_var.is_user = 1;
+ if(def && def->action && def->action->scorevalhdrtok){
+ apval = APVAL(&hdrtok_act_var, ew);
+ *apval = hdrtok_to_stringform(def->action->scorevalhdrtok);
+ }
+
+ set_current_val(&hdrtok_act_var, FALSE, FALSE);
+
+ role_repl_ptr = &repl_type_var; /* so radiobuttons can tell */
+ /* TRANSLATORS: For these, Use is a now. This part of the rule describes how
+ it will be used when Replying so it is the Reply Use */
+ repl_type_var.name = cpystr(_("Reply Use"));
+ repl_type_var.is_used = 1;
+ repl_type_var.is_user = 1;
+ apval = APVAL(&repl_type_var, ew);
+ *apval = (f=role_repl_types((def && def->action) ? def->action->repl_type : -1)) ? cpystr(f->name) : NULL;
+ set_current_val(&repl_type_var, FALSE, FALSE);
+
+ role_forw_ptr = &forw_type_var; /* so radiobuttons can tell */
+ forw_type_var.name = cpystr(_("Forward Use"));
+ forw_type_var.is_used = 1;
+ forw_type_var.is_user = 1;
+ apval = APVAL(&forw_type_var, ew);
+ *apval = (f=role_forw_types((def && def->action) ? def->action->forw_type : -1)) ? cpystr(f->name) : NULL;
+ set_current_val(&forw_type_var, FALSE, FALSE);
+
+ comp_type_var.name = cpystr(_("Compose Use"));
+ comp_type_var.is_used = 1;
+ comp_type_var.is_user = 1;
+ apval = APVAL(&comp_type_var, ew);
+ *apval = (f=role_comp_types((def && def->action) ? def->action->comp_type : -1)) ? cpystr(f->name) : NULL;
+ set_current_val(&comp_type_var, FALSE, FALSE);
+
+ rolecolor_vars[0].is_used = 1;
+ rolecolor_vars[0].is_user = 1;
+ apval = APVAL(&rolecolor_vars[0], ew);
+ *apval = (def && def->action && def->action->incol &&
+ def->action->incol->fg[0])
+ ? cpystr(def->action->incol->fg) : NULL;
+ rolecolor_vars[1].is_used = 1;
+ rolecolor_vars[1].is_user = 1;
+ rolecolor_vars[0].name = cpystr("ic-foreground-color");
+ rolecolor_vars[1].name = cpystr(rolecolor_vars[0].name);
+ strncpy(rolecolor_vars[1].name + 3, "back", 4);
+ apval = APVAL(&rolecolor_vars[1], ew);
+ *apval = (def && def->action && def->action->incol &&
+ def->action->incol->bg[0])
+ ? cpystr(def->action->incol->bg) : NULL;
+ set_current_val(&rolecolor_vars[0], FALSE, FALSE);
+ set_current_val(&rolecolor_vars[1], FALSE, FALSE);
+ old_fg = PVAL(&rolecolor_vars[0], ew) ? cpystr(PVAL(&rolecolor_vars[0], ew))
+ : NULL;
+ old_bg = PVAL(&rolecolor_vars[1], ew) ? cpystr(PVAL(&rolecolor_vars[1], ew))
+ : NULL;
+
+
+ /* save the old opt_screen before calling scroll screen again */
+ saved_screen = opt_screen;
+
+ pindent = utf8_width(s_p_v_n); /* the longest one */
+ maxpindent = pindent + 6;
+ for(a = (def && def->patgrp) ? def->patgrp->arbhdr : NULL; a; a = a->next)
+ if((lv=utf8_width(a->field ? a->field : "")+utf8_width(" pattern")) > pindent)
+ pindent = lv;
+
+ pindent = MIN(pindent, maxpindent);
+
+ pindent += NOTLEN; /* width of `! ' */
+
+ pindent += 3; /* width of ` = ' */
+
+ /* Nickname */
+ new_confline(&ctmp);
+ ctmp->help_title= _("HELP FOR NICKNAME");
+ ctmp->var = &nick_var;
+ ctmp->valoffset = pindent;
+ ctmp->keymenu = &config_role_keymenu;
+ ctmp->help = edit_role ? h_config_role_nick :
+ edit_incol ? h_config_incol_nick :
+ edit_score ? h_config_score_nick :
+ edit_other ? h_config_other_nick
+ : h_config_filt_nick;
+ ctmp->tool = t_tool;
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.*w =", pindent-3, pindent-3, nick_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmp;
+ ctmp->value = pretty_value(ps, ctmp);
+ ctmp->varmem = -1;
+
+ first_line = ctmp;
+ if(rflags & ROLE_CHANGES)
+ first_line->flags |= CF_CHANGES;
+
+ /* Comment */
+ new_confline(&ctmp);
+ ctmp->help_title= _("HELP FOR COMMENT");
+ ctmp->var = &comment_var;
+ ctmp->valoffset = pindent;
+ ctmp->keymenu = &config_role_keymenu;
+ ctmp->help = h_config_role_comment;
+ ctmp->tool = role_litsig_text_tool;
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.*w =", pindent-3, pindent-3, comment_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmp;
+ ctmp->value = pretty_value(ps, ctmp);
+ ctmp->varmem = -1;
+
+ /* Blank line */
+ new_confline(&ctmp);
+ ctmp->flags |= CF_NOSELECT | CF_B_LINE;
+
+ new_confline(&ctmp);
+ ctmp->flags |= CF_NOSELECT;
+ if(ps->ttyo->screen_cols >= (wid=utf8_width(fstr)) + 4){
+ int dashes;
+
+ dashes = (ps->ttyo->screen_cols - wid)/2;
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s", repeat_char(dashes, '='),
+ fstr, repeat_char(ps->ttyo->screen_cols-wid-dashes, '='));
+ ctmp->value = cpystr(tmp_20k_buf);
+ }
+ else
+ ctmp->value = cpystr(repeat_char(ps->ttyo->screen_cols, '='));
+
+ /* Blank line */
+ new_confline(&ctmp);
+ ctmp->flags |= CF_NOSELECT | CF_B_LINE;
+
+ /* Folder Type */
+ new_confline(&ctmp);
+ ctmp->var = &fldr_type_var;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = NULL;
+ snprintf(tmp, sizeof(tmp), "%s =", fldr_type_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmpb = ctmp;
+ ctmp->flags |= (CF_NOSELECT | CF_STARTITEM);
+
+ new_confline(&ctmp);
+ ctmp->var = NULL;
+ ctmp->valoffset = rindent;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = NULL;
+ ctmp->varnamep = ctmpb;
+ ctmp->flags |= CF_NOSELECT;
+ ctmp->value = cpystr("Set Choose One");
+
+ new_confline(&ctmp);
+ ctmp->var = NULL;
+ ctmp->valoffset = rindent;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = radio_tool;
+ ctmp->varnamep = ctmpb;
+ ctmp->flags |= CF_NOSELECT;
+ ctmp->value = cpystr(set_choose); \
+
+ /* find longest value's name */
+ for(lv = 0, i = 0; (f = pat_fldr_types(i)); i++)
+ if(lv < (j = utf8_width(f->name)))
+ lv = j;
+
+ lv = MIN(lv, 100);
+
+ fval = -1;
+ for(i = 0; (f = pat_fldr_types(i)); i++){
+ new_confline(&ctmp);
+ ctmp->help_title= _("HELP FOR CURRENT FOLDER TYPE");
+ ctmp->var = &fldr_type_var;
+ ctmp->valoffset = rindent;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = edit_role ? h_config_role_fldr_type :
+ edit_incol ? h_config_incol_fldr_type :
+ edit_score ? h_config_score_fldr_type :
+ edit_other ? h_config_other_fldr_type
+ : h_config_filt_fldr_type;
+ ctmp->varmem = i;
+ ctmp->tool = radio_tool;
+ ctmp->varnamep = ctmpb;
+
+ if((PVAL(&fldr_type_var, ew) &&
+ !strucmp(PVAL(&fldr_type_var, ew), f->name))
+ || (!PVAL(&fldr_type_var, ew) && f->value == FLDR_DEFL))
+ fval = f->value;
+
+ utf8_snprintf(tmp, sizeof(tmp), "(%c) %-*.*w",
+ (fval == f->value) ? R_SELD : ' ',
+ lv, lv, f->name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->value = cpystr(tmp);
+ }
+
+ /* Folder */
+ /* 5 is the width of `(*) ' */
+ setup_role_pat_alt(ps, &ctmp, &folder_pat_var,
+ edit_role ? h_config_role_fldr_type :
+ edit_incol ? h_config_incol_fldr_type :
+ edit_score ? h_config_score_fldr_type :
+ edit_other ? h_config_other_fldr_type
+ : h_config_filt_fldr_type,
+ _("HELP FOR FOLDER LIST"),
+ &config_role_patfolder_keymenu, t_tool, rindent+5,
+ !(fval == FLDR_SPECIFIC));
+
+ if(!per_folder_only){ /* sorry about that indent */
+
+ /* Blank line */
+ new_confline(&ctmp);
+ ctmp->flags |= CF_NOSELECT | CF_B_LINE;
+
+ new_confline(&ctmp);
+ ctmp->flags |= CF_NOSELECT;
+
+ /* TRANSLATORS: The %s is replaced with one of the 4 or 5 words below, CURRENT,
+ SCORED, and so on. */
+ snprintf(mstr, sizeof(mstr), _(" %s MESSAGE CONDITIONS BEGIN HERE "),
+ edit_role ? _("CURRENT") :
+ edit_score ? _("SCORED") :
+ edit_incol ? _("COLORED") :
+ edit_filter ? _("FILTERED") : _("CURRENT"));
+ mstr[sizeof(mstr)-1] = '\0';
+
+ if(ps->ttyo->screen_cols >= (wid=utf8_width(mstr)) + 4){
+ int dashes;
+
+ dashes = (ps->ttyo->screen_cols - wid)/2;
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s", repeat_char(dashes, '='),
+ mstr, repeat_char(ps->ttyo->screen_cols-wid-dashes, '='));
+ ctmp->value = cpystr(tmp_20k_buf);
+ }
+ else
+ ctmp->value = cpystr(repeat_char(ps->ttyo->screen_cols, '='));
+
+ /* Blank line */
+ new_confline(&ctmp);
+ ctmp->flags |= CF_NOSELECT | CF_B_LINE;
+
+ setup_role_pat(ps, &ctmp, &to_pat_var,
+ edit_role ? h_config_role_topat :
+ edit_incol ? h_config_incol_topat :
+ edit_score ? h_config_score_topat :
+ edit_other ? h_config_other_topat
+ : h_config_filt_topat,
+ _("HELP FOR TO PATTERN"),
+ &config_role_addr_pat_keymenu, t_tool, &earb, pindent);
+
+ setup_role_pat(ps, &ctmp, &from_pat_var, h_config_role_frompat,
+ _("HELP FOR FROM PATTERN"),
+ &config_role_addr_pat_keymenu, t_tool, &earb, pindent);
+
+ setup_role_pat(ps, &ctmp, &sender_pat_var, h_config_role_senderpat,
+ _("HELP FOR SENDER PATTERN"),
+ &config_role_addr_pat_keymenu, t_tool, &earb, pindent);
+
+ setup_role_pat(ps, &ctmp, &cc_pat_var, h_config_role_ccpat,
+ _("HELP FOR CC PATTERN"),
+ &config_role_addr_pat_keymenu, t_tool, &earb, pindent);
+
+ setup_role_pat(ps, &ctmp, &news_pat_var, h_config_role_newspat,
+ _("HELP FOR NEWS PATTERN"),
+ &config_role_keymenu_not, t_tool, &earb, pindent);
+
+ setup_role_pat(ps, &ctmp, &subj_pat_var, h_config_role_subjpat,
+ _("HELP FOR SUBJECT PATTERN"),
+ &config_role_keymenu_not, t_tool, &earb, pindent);
+
+ setup_role_pat(ps, &ctmp, &recip_pat_var, h_config_role_recippat,
+ _("HELP FOR RECIPIENT PATTERN"),
+ &config_role_addr_pat_keymenu, t_tool, &earb, pindent);
+
+ setup_role_pat(ps, &ctmp, &partic_pat_var, h_config_role_particpat,
+ _("HELP FOR PARTICIPANT PATTERN"),
+ &config_role_addr_pat_keymenu, t_tool, &earb, pindent);
+
+ /* Arbitrary Patterns */
+ ea = NULL;
+ for(j = 0, a = (def && def->patgrp) ? def->patgrp->arbhdr : NULL;
+ a;
+ j++, a = a->next){
+ char *fn = (a->field) ? a->field : "";
+
+ if(ea){
+ ea->next = (EARB_S *)fs_get(sizeof(*ea));
+ ea = ea->next;
+ }
+ else{
+ earb = (EARB_S *)fs_get(sizeof(*ea));
+ ea = earb;
+ }
+
+ memset((void *)ea, 0, sizeof(*ea));
+ ea->v = (struct variable *)fs_get(sizeof(struct variable));
+ memset((void *)ea->v, 0, sizeof(struct variable));
+ ea->a = (ARBHDR_S *)fs_get(sizeof(ARBHDR_S));
+ memset((void *)ea->a, 0, sizeof(ARBHDR_S));
+
+ ea->a->field = cpystr(fn);
+
+ p = (char *) fs_get(strlen(fn) + strlen(" pattern") + 1);
+ snprintf(p, strlen(fn) + strlen(" pattern") + 1, "%s pattern", fn);
+ p[strlen(fn) + strlen(" pattern") + 1 - 1] = '\0';
+ setup_dummy_pattern_var(ea->v, p, a->p);
+ fs_give((void **) &p);
+ setup_role_pat(ps, &ctmp, ea->v, h_config_role_arbpat,
+ ARB_HELP, &config_role_xtrahdr_keymenu,
+ t_tool, &earb, pindent);
+ }
+
+ new_confline(&ctmp);
+ ctmp->help_title = _("HELP FOR EXTRA HEADERS");
+ ctmp->value = cpystr(ADDXHDRS);
+ ctmp->keymenu = &config_role_keymenu_extra;
+ ctmp->help = h_config_role_arbpat;
+ ctmp->tool = addhdr_tool;
+ ctmp->d.earb = &earb;
+ ctmp->varmem = -1;
+
+ setup_role_pat(ps, &ctmp, &alltext_pat_var, h_config_role_alltextpat,
+ _("HELP FOR ALL TEXT PATTERN"),
+ &config_role_keymenu_not, t_tool, &earb, pindent);
+
+ setup_role_pat(ps, &ctmp, &bodytext_pat_var, h_config_role_bodytextpat,
+ _("HELP FOR BODY TEXT PATTERN"),
+ &config_role_keymenu_not, t_tool, &earb, pindent);
+
+ /* Age Interval */
+ new_confline(&ctmp);
+ ctmp->help_title= _("HELP FOR AGE INTERVAL");
+ ctmp->var = &age_pat_var;
+ ctmp->valoffset = pindent;
+ ctmp->keymenu = &config_text_keymenu;
+ ctmp->help = h_config_role_age;
+ ctmp->tool = t_tool;
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.*w =", pindent-3, pindent-3, age_pat_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmp;
+ ctmp->value = pretty_value(ps, ctmp);
+
+ /* Size Interval */
+ new_confline(&ctmp);
+ ctmp->help_title= _("HELP FOR SIZE INTERVAL");
+ ctmp->var = &size_pat_var;
+ ctmp->valoffset = pindent;
+ ctmp->keymenu = &config_text_keymenu;
+ ctmp->help = h_config_role_size;
+ ctmp->tool = t_tool;
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.*w =", pindent-3, pindent-3, size_pat_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmp;
+ ctmp->value = pretty_value(ps, ctmp);
+
+ if(!edit_score){
+ /* Score Interval */
+ new_confline(&ctmp);
+ ctmp->help_title= _("HELP FOR SCORE INTERVAL");
+ ctmp->var = &scorei_pat_var;
+ ctmp->valoffset = pindent;
+ ctmp->keymenu = &config_text_keymenu;
+ ctmp->help = h_config_role_scorei;
+ ctmp->tool = t_tool;
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.*w =", pindent-3, pindent-3, scorei_pat_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmp;
+ ctmp->value = pretty_value(ps, ctmp);
+ }
+
+ /* Keyword Pattern */
+ setup_role_pat(ps, &ctmp, &keyword_pat_var, h_config_role_keywordpat,
+ _("HELP FOR KEYWORD PATTERN"),
+ &config_role_keyword_keymenu_not, role_text_tool_kword,
+ NULL, pindent);
+
+ /* Charset Pattern */
+ setup_role_pat(ps, &ctmp, &charset_pat_var, h_config_role_charsetpat,
+ _("HELP FOR CHARACTER SET PATTERN"),
+ &config_role_charset_keymenu_not, role_text_tool_charset,
+ NULL, pindent);
+
+ /* Important Status */
+ SETUP_PAT_STATUS(ctmp, stat_imp_var, def->patgrp->stat_imp,
+ _("HELP FOR IMPORTANT STATUS"), h_config_role_stat_imp);
+ /* New Status */
+ SETUP_PAT_STATUS(ctmp, stat_new_var, def->patgrp->stat_new,
+ _("HELP FOR NEW STATUS"), h_config_role_stat_new);
+ /* Recent Status */
+ SETUP_PAT_STATUS(ctmp, stat_rec_var, def->patgrp->stat_rec,
+ _("HELP FOR RECENT STATUS"), h_config_role_stat_recent);
+ /* Deleted Status */
+ SETUP_PAT_STATUS(ctmp, stat_del_var, def->patgrp->stat_del,
+ _("HELP FOR DELETED STATUS"), h_config_role_stat_del);
+ /* Answered Status */
+ SETUP_PAT_STATUS(ctmp, stat_ans_var, def->patgrp->stat_ans,
+ _("HELP FOR ANSWERED STATUS"), h_config_role_stat_ans);
+ /* 8-bit Subject */
+ SETUP_PAT_STATUS(ctmp, stat_8bit_var, def->patgrp->stat_8bitsubj,
+ _("HELP FOR 8-BIT SUBJECT"), h_config_role_stat_8bitsubj);
+ /* Beginning of month */
+ SETUP_PAT_STATUS(ctmp, stat_bom_var, def->patgrp->stat_bom,
+ _("HELP FOR BEGINNING OF MONTH"), h_config_role_bom);
+ /* Beginning of year */
+ SETUP_PAT_STATUS(ctmp, stat_boy_var, def->patgrp->stat_boy,
+ _("HELP FOR BEGINNING OF YEAR"), h_config_role_boy);
+
+ /* Blank line */
+ new_confline(&ctmp);
+ ctmp->flags |= CF_NOSELECT | CF_B_LINE;
+
+ /* From in Addrbook */
+ new_confline(&ctmp);
+ ctmp->var = &abook_type_var;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = NULL;
+ snprintf(tmp, sizeof(tmp), "%s =", abook_type_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmpb = ctmp;
+ ctmp->flags |= (CF_NOSELECT | CF_STARTITEM);
+
+ new_confline(&ctmp);
+ ctmp->var = NULL;
+ ctmp->valoffset = rindent;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = NULL;
+ ctmp->varnamep = ctmpb;
+ ctmp->flags |= CF_NOSELECT;
+ ctmp->value = cpystr("Set Choose One");
+
+ new_confline(&ctmp);
+ ctmp->var = NULL;
+ ctmp->valoffset = rindent;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = radio_tool;
+ ctmp->varnamep = ctmpb;
+ ctmp->flags |= CF_NOSELECT;
+ ctmp->value = cpystr(set_choose); \
+
+ /* find longest value's name */
+ for(lv = 0, i = 0; (f = inabook_fldr_types(i)); i++)
+ if(lv < (j = utf8_width(f->name)))
+ lv = j;
+
+ lv = MIN(lv, 100);
+
+ fval = -1;
+ for(i = 0; (f = inabook_fldr_types(i)); i++){
+ new_confline(&ctmp);
+ ctmp->help_title= _("HELP FOR ADDRESS IN ADDRESS BOOK");
+ ctmp->var = &abook_type_var;
+ ctmp->valoffset = rindent;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = h_config_role_abookfrom;
+ ctmp->varmem = i;
+ ctmp->tool = radio_tool;
+ ctmp->varnamep = ctmpb;
+
+ if((PVAL(&abook_type_var, ew) &&
+ !strucmp(PVAL(&abook_type_var, ew), f->name))
+ || (!PVAL(&abook_type_var, ew) && f->value == IAB_DEFL))
+ fval = f->value;
+
+ utf8_snprintf(tmp, sizeof(tmp), "(%c) %-*.*w",
+ (fval == f->value) ? R_SELD : ' ',
+ lv, lv, f->name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->value = cpystr(tmp);
+ }
+
+ /* Specific list of abooks */
+ /* 5 is the width of `(*) ' */
+ setup_role_pat_alt(ps, &ctmp, &abook_pat_var, h_config_role_abookfrom,
+ _("HELP FOR ABOOK LIST"),
+ &config_role_afrom_keymenu, role_text_tool_afrom, rindent+5,
+ !(fval == IAB_SPEC_YES || fval == IAB_SPEC_NO));
+
+ /* Which addresses to check for */
+ inabook_type_var.name = cpystr(_("Types of addresses to check for in address book"));
+ inabook_type_var.is_used = 1;
+ inabook_type_var.is_user = 1;
+ inabook_type_var.is_list = 1;
+ clrbitmap(inabook_type_list);
+ if(def && def->patgrp && def->patgrp->inabook & IAB_FROM)
+ setbitn(INABOOK_FROM, inabook_type_list);
+ if(def && def->patgrp && def->patgrp->inabook & IAB_REPLYTO)
+ setbitn(INABOOK_REPLYTO, inabook_type_list);
+ if(def && def->patgrp && def->patgrp->inabook & IAB_SENDER)
+ setbitn(INABOOK_SENDER, inabook_type_list);
+ if(def && def->patgrp && def->patgrp->inabook & IAB_TO)
+ setbitn(INABOOK_TO, inabook_type_list);
+ if(def && def->patgrp && def->patgrp->inabook & IAB_CC)
+ setbitn(INABOOK_CC, inabook_type_list);
+
+ /* default setting */
+ if(!(bitnset(INABOOK_FROM, inabook_type_list)
+ || bitnset(INABOOK_REPLYTO, inabook_type_list)
+ || bitnset(INABOOK_SENDER, inabook_type_list)
+ || bitnset(INABOOK_TO, inabook_type_list)
+ || bitnset(INABOOK_CC, inabook_type_list))){
+ setbitn(INABOOK_FROM, inabook_type_list);
+ setbitn(INABOOK_REPLYTO, inabook_type_list);
+ }
+
+ new_confline(&ctmp);
+ ctmp->var = &inabook_type_var;
+ ctmp->varoffset = 4;
+ ctmp->valoffset = 23;
+ ctmp->keymenu = &config_checkbox_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = NULL;
+ snprintf(tmp, sizeof(tmp), "%-20s =", inabook_type_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmpb = ctmp;
+ ctmp->flags |= (CF_NOSELECT | CF_STARTITEM);
+
+ new_confline(&ctmp);
+ ctmp->var = NULL;
+ ctmp->valoffset = rindent;
+ ctmp->keymenu = &config_checkbox_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = inabook_checkbox_tool;
+ ctmp->varnamep = ctmpb;
+ ctmp->flags |= CF_NOSELECT;
+ ctmp->value = cpystr("Set Address types");
+
+ new_confline(&ctmp);
+ ctmp->var = NULL;
+ ctmp->valoffset = rindent;
+ ctmp->keymenu = &config_checkbox_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = inabook_checkbox_tool;
+ ctmp->varnamep = ctmpb;
+ ctmp->flags |= CF_NOSELECT;
+ ctmp->value = cpystr(set_choose);
+
+ /* find longest value's name */
+ for(lv = 0, i = 0; (f = inabook_feature_list(i)); i++){
+ if(lv < (j = utf8_width(f->name)))
+ lv = j;
+ }
+
+ lv = MIN(lv, 100);
+
+ for(i = 0; (f = inabook_feature_list(i)); i++){
+ new_confline(&ctmp);
+ ctmp->var = &opt_var;
+ ctmp->help_title= _("HELP FOR ADDRESS TYPES");
+ ctmp->varnamep = ctmpb;
+ ctmp->keymenu = &config_checkbox_keymenu;
+ switch(i){
+ case INABOOK_FROM:
+ ctmp->help = h_config_inabook_from;
+ break;
+ case INABOOK_REPLYTO:
+ ctmp->help = h_config_inabook_replyto;
+ break;
+ case INABOOK_SENDER:
+ ctmp->help = h_config_inabook_sender;
+ break;
+ case INABOOK_TO:
+ ctmp->help = h_config_inabook_to;
+ break;
+ case INABOOK_CC:
+ ctmp->help = h_config_inabook_cc;
+ break;
+ }
+
+ ctmp->tool = inabook_checkbox_tool;
+ ctmp->valoffset = rindent;
+ ctmp->varmem = i;
+ utf8_snprintf(tmp, sizeof(tmp), "[%c] %-*.*w",
+ bitnset(f->value, inabook_type_list) ? 'X' : ' ',
+ lv, lv, f->name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->value = cpystr(tmp);
+ }
+
+ /* Blank line */
+ new_confline(&ctmp);
+ ctmp->flags |= CF_NOSELECT | CF_B_LINE;
+
+ /* 4 is indent of "Command", c_v_n is longest of these three, 3 is ` = ' */
+ i = 4 + utf8_width(c_v_n) + 3;
+
+ /* External Command Categorizer */
+ new_confline(&ctmp);
+ ctmp->var = &cat_cmd_var;
+ ctmp->keymenu = &config_text_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = NULL;
+ snprintf(tmp, sizeof(tmp), "%s =", cat_cmd_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmpb = ctmp;
+ ctmp->flags |= (CF_NOSELECT | CF_STARTITEM);
+
+ /* Commands */
+ new_confline(&ctmp);
+ ctmp->help_title= _("HELP FOR CATEGORIZER COMMAND");
+ ctmp->var = &cat_cmd_var;
+ ctmp->varoffset = 4;
+ ctmp->valoffset = i;
+ ctmp->keymenu = &config_text_wshuf_keymenu;
+ ctmp->help = h_config_role_cat_cmd;
+ ctmp->tool = t_tool;
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.*w =", i-4-3, i-4-3, "Command");
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmpb = ctmp;
+ ctmp->flags = CF_STARTITEM;
+
+ if((lval = LVAL(&cat_cmd_var, ew)) != NULL && lval[0]){
+ for(j = 0; lval[j]; j++){
+ if(j)
+ (void) new_confline(&ctmp);
+
+ ctmp->var = &cat_cmd_var;
+ ctmp->varmem = j;
+ ctmp->valoffset = i;
+ ctmp->keymenu = &config_text_wshuf_keymenu;
+ ctmp->help = h_config_role_cat_cmd;
+ ctmp->tool = t_tool;
+ ctmp->varnamep = ctmp;
+ ctmp->value = pretty_value(ps, ctmp);
+ }
+ }
+ else
+ ctmp->value = pretty_value(ps, ctmp);
+
+ /* Exit status interval */
+ new_confline(&ctmp);
+ ctmp->help_title= _("HELP FOR CATEGORIZER EXIT STATUS");
+ ctmp->var = &cati_var;
+ ctmp->varoffset = 4;
+ ctmp->valoffset = i;
+ ctmp->keymenu = &config_text_keymenu;
+ ctmp->help = h_config_role_cat_status;
+ ctmp->tool = t_tool;
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.*w =", i-4-3, i-4-3, cati_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmp;
+ ctmp->value = pretty_value(ps, ctmp);
+
+ /* Character Limit */
+ new_confline(&ctmp);
+ ctmp->help_title= _("HELP FOR CHARACTER LIMIT");
+ ctmp->var = &cat_lim_var;
+ ctmp->varoffset = 4;
+ ctmp->valoffset = i;
+ ctmp->keymenu = &config_text_keymenu;
+ ctmp->help = h_config_role_cat_limit;
+ ctmp->tool = t_tool;
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.*w =", i-4-3, i-4-3, cat_lim_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmp;
+ ctmp->value = pretty_value(ps, ctmp);
+ ctmp->flags |= CF_NUMBER;
+ }
+
+ if(!edit_srch){ /* sorry about that indent */
+ /* Actions */
+
+ /* Blank line */
+ new_confline(&ctmp);
+ ctmp->flags |= CF_NOSELECT | CF_B_LINE;
+
+ new_confline(&ctmp);
+ ctmp->flags |= CF_NOSELECT;
+ if(ps->ttyo->screen_cols >= (wid=utf8_width(astr)) + 4){
+ int dashes;
+
+ dashes = (ps->ttyo->screen_cols - wid)/2;
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s", repeat_char(dashes, '='),
+ astr, repeat_char(ps->ttyo->screen_cols-wid-dashes, '='));
+ ctmp->value = cpystr(tmp_20k_buf);
+ }
+ else
+ ctmp->value = cpystr(repeat_char(ps->ttyo->screen_cols, '='));
+
+ if(edit_role){
+ int roindent;
+
+ roindent = utf8_width(u_s_s); /* the longest one */
+ roindent += 3; /* width of ` = ' */
+
+ /* Blank line */
+ new_confline(&ctmp);
+ ctmp->flags |= CF_NOSELECT | CF_B_LINE;
+
+ /* Inherit Nickname */
+ new_confline(&ctmp);
+ inick_confs[INICK_INICK_CONF] = ctmp;
+ ctmp->help_title= _("HELP FOR INITIAL SET NICKNAME");
+ ctmp->var = &inick_var;
+ ctmp->keymenu = &config_role_inick_keymenu;
+ ctmp->help = h_config_role_inick;
+ ctmp->tool = role_text_tool_inick;
+ snprintf(tmp, sizeof(tmp), "%s :", inick_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->valoffset = utf8_width(tmp)+1;
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmp;
+
+ /* From Action */
+ new_confline(&ctmp);
+ inick_confs[INICK_FROM_CONF] = ctmp;
+ ctmp->help_title= _("HELP FOR SET FROM ACTION");
+ ctmp->var = &from_act_var;
+ ctmp->valoffset = roindent;
+ ctmp->keymenu = &config_role_addr_act_keymenu;
+ ctmp->help = h_config_role_setfrom;
+ ctmp->tool = role_text_tool;
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.*w =", roindent-3, roindent-3, from_act_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmp;
+
+ /* Reply-To Action */
+ new_confline(&ctmp);
+ inick_confs[INICK_REPLYTO_CONF] = ctmp;
+ ctmp->help_title= _("HELP FOR SET REPLY-TO ACTION");
+ ctmp->var = &replyto_act_var;
+ ctmp->valoffset = roindent;
+ ctmp->keymenu = &config_role_addr_act_keymenu;
+ ctmp->help = h_config_role_setreplyto;
+ ctmp->tool = role_text_tool;
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.*w =", roindent-3, roindent-3, replyto_act_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmp;
+
+ /* Fcc Action */
+ new_confline(&ctmp);
+ inick_confs[INICK_FCC_CONF] = ctmp;
+ ctmp->help_title= _("HELP FOR SET FCC ACTION");
+ ctmp->var = &fcc_act_var;
+ ctmp->valoffset = roindent;
+ ctmp->keymenu = &config_role_actionfolder_keymenu;
+ ctmp->help = h_config_role_setfcc;
+ ctmp->tool = role_text_tool;
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.*w =", roindent-3, roindent-3, fcc_act_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmp;
+
+ /* LitSig Action */
+ new_confline(&ctmp);
+ inick_confs[INICK_LITSIG_CONF] = ctmp;
+ ctmp->help_title= _("HELP FOR SET LITERAL SIGNATURE ACTION");
+ ctmp->var = &litsig_act_var;
+ ctmp->valoffset = roindent;
+ ctmp->keymenu = &config_text_keymenu;
+ ctmp->help = h_config_role_setlitsig;
+ ctmp->tool = role_litsig_text_tool;
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.*w =", roindent-3, roindent-3, litsig_act_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmp;
+
+ /* Sig Action */
+ new_confline(&ctmp);
+ inick_confs[INICK_SIG_CONF] = ctmp;
+ ctmp->help_title= _("HELP FOR SET SIGNATURE ACTION");
+ ctmp->var = &sig_act_var;
+ ctmp->valoffset = roindent;
+ if(F_ON(F_DISABLE_ROLES_SIGEDIT, ps_global))
+ ctmp->keymenu = &config_role_file_res_keymenu;
+ else
+ ctmp->keymenu = &config_role_file_keymenu;
+
+ ctmp->help = h_config_role_setsig;
+ ctmp->tool = role_text_tool;
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.*w =", roindent-3, roindent-3, sig_act_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmp;
+
+ /* Template Action */
+ new_confline(&ctmp);
+ inick_confs[INICK_TEMPL_CONF] = ctmp;
+ ctmp->help_title= _("HELP FOR SET TEMPLATE ACTION");
+ ctmp->var = &templ_act_var;
+ ctmp->valoffset = roindent;
+ if(F_ON(F_DISABLE_ROLES_TEMPLEDIT, ps_global))
+ ctmp->keymenu = &config_role_file_res_keymenu;
+ else
+ ctmp->keymenu = &config_role_file_keymenu;
+
+ ctmp->help = h_config_role_settempl;
+ ctmp->tool = role_text_tool;
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.*w =", roindent-3, roindent-3, templ_act_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmp;
+
+ /* Other Headers Action */
+ new_confline(&ctmp);
+ inick_confs[INICK_CSTM_CONF] = ctmp;
+ ctmp->help_title= _("HELP FOR SET OTHER HEADERS ACTION");
+ ctmp->var = &cstm_act_var;
+ ctmp->valoffset = roindent;
+ ctmp->keymenu = &config_text_wshuf_keymenu;
+ ctmp->help = h_config_role_setotherhdr;
+ ctmp->tool = role_cstm_text_tool;
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.*w =", roindent-3, roindent-3, cstm_act_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmpb = ctmp;
+ ctmp->flags = CF_STARTITEM;
+
+ if((lval = LVAL(&cstm_act_var, ew)) != NULL){
+ for(i = 0; lval[i]; i++){
+ if(i)
+ (void)new_confline(&ctmp);
+
+ ctmp->var = &cstm_act_var;
+ ctmp->varmem = i;
+ ctmp->valoffset = roindent;
+ ctmp->keymenu = &config_text_wshuf_keymenu;
+ ctmp->help = h_config_role_setotherhdr;
+ ctmp->tool = role_cstm_text_tool;
+ ctmp->varnamep = ctmpb;
+ }
+ }
+ else
+ ctmp->varmem = 0;
+
+ /* SMTP Server Action */
+ new_confline(&ctmp);
+ inick_confs[INICK_SMTP_CONF] = ctmp;
+ ctmp->help_title= _("HELP FOR SMTP SERVER ACTION");
+ ctmp->var = &smtp_act_var;
+ ctmp->valoffset = roindent;
+ ctmp->keymenu = &config_text_wshuf_keymenu;
+ ctmp->help = h_config_role_usesmtp;
+ ctmp->tool = t_tool;
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.*w =", roindent-3, roindent-3, smtp_act_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmpb = ctmp;
+ ctmp->flags = CF_STARTITEM;
+
+ if((lval = LVAL(&smtp_act_var, ew)) != NULL){
+ for(i = 0; lval[i]; i++){
+ if(i)
+ (void)new_confline(&ctmp);
+
+ ctmp->var = &smtp_act_var;
+ ctmp->varmem = i;
+ ctmp->valoffset = roindent;
+ ctmp->keymenu = &config_text_wshuf_keymenu;
+ ctmp->help = h_config_role_usesmtp;
+ ctmp->tool = t_tool;
+ ctmp->varnamep = ctmpb;
+ }
+ }
+ else
+ ctmp->varmem = 0;
+
+ /* NNTP Server Action */
+ new_confline(&ctmp);
+ inick_confs[INICK_NNTP_CONF] = ctmp;
+ ctmp->help_title= _("HELP FOR NNTP SERVER ACTION");
+ ctmp->var = &nntp_act_var;
+ ctmp->valoffset = roindent;
+ ctmp->keymenu = &config_text_wshuf_keymenu;
+ ctmp->help = h_config_role_usenntp;
+ ctmp->tool = t_tool;
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.*w =", roindent-3, roindent-3, nntp_act_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmpb = ctmp;
+ ctmp->flags = CF_STARTITEM;
+
+ if((lval = LVAL(&nntp_act_var, ew)) != NULL){
+ for(i = 0; lval[i]; i++){
+ if(i)
+ (void)new_confline(&ctmp);
+
+ ctmp->var = &nntp_act_var;
+ ctmp->varmem = i;
+ ctmp->valoffset = roindent;
+ ctmp->keymenu = &config_text_wshuf_keymenu;
+ ctmp->help = h_config_role_usenntp;
+ ctmp->tool = t_tool;
+ ctmp->varnamep = ctmpb;
+ }
+ }
+ else
+ ctmp->varmem = 0;
+
+ calculate_inick_stuff(ps);
+ }
+ else
+ inick_confs[INICK_INICK_CONF] = NULL;
+
+ if(edit_score){
+ int sindent;
+
+ sindent = MAX(utf8_width(score_act_var.name),utf8_width(hdrtok_act_var.name)) + 3;
+
+ /* Blank line */
+ new_confline(&ctmp);
+ ctmp->flags |= CF_NOSELECT | CF_B_LINE;
+
+ /* Score Value -- This doesn't inherit from inick */
+ new_confline(&ctmp);
+ ctmp->help_title= _("HELP FOR SCORE VALUE ACTION");
+ ctmp->var = &score_act_var;
+ ctmp->valoffset = sindent;
+ ctmp->keymenu = &config_text_keymenu;
+ ctmp->help = h_config_role_scoreval;
+ ctmp->tool = text_tool;
+ ctmp->flags |= CF_NUMBER;
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.*w =", sindent-3, sindent-3, score_act_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmp;
+ ctmp->value = pretty_value(ps, ctmp);
+
+ new_confline(&ctmp);
+ ctmp->flags |= CF_NOSELECT;
+ ctmp->value = cpystr(" OR");
+
+ /* Score From Header */
+ new_confline(&ctmp);
+ ctmp->help_title= _("HELP FOR SCORE VALUE FROM HEADER ACTION");
+ ctmp->var = &hdrtok_act_var;
+ ctmp->valoffset = sindent;
+ ctmp->keymenu = &config_text_keymenu;
+ ctmp->help = h_config_role_scorehdrtok;
+ ctmp->tool = text_tool;
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.*w =", sindent-3, sindent-3, hdrtok_act_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmp;
+ ctmp->value = pretty_value(ps, ctmp);
+ }
+
+ if(edit_filter){
+ /*
+ * Filtering got added in stages, so instead of simply having a
+ * variable in action which is set to one of the three possible
+ * values (FILTER_KILL, FILTER_STATE, FILTER_FOLDER) we infer
+ * the value from other variables. (Perhaps it would still make
+ * sense to change this.)
+ * Action->kill is set iff the user checks Delete.
+ * If the user checks the box that says Just Set State, then kill
+ * is not set and action->folder is not set (and vice versa).
+ * And finally, FILTER_FOLDER is set if !kill and action->folder is set.
+ * (And it is set here as the default if there is no default
+ * action and the user is required to fill in the Folder.)
+ */
+ if(def && def->action && def->action->kill)
+ fval = FILTER_KILL;
+ else if(def && def->action && !def->action->kill &&
+ !def->action->folder)
+ fval = FILTER_STATE;
+ else
+ fval = FILTER_FOLDER;
+
+ role_filt_ptr = &filter_type_var; /* so radiobuttons can tell */
+ filter_type_var.name = cpystr(_("Filter Action"));
+ filter_type_var.is_used = 1;
+ filter_type_var.is_user = 1;
+ apval = APVAL(&filter_type_var, ew);
+ *apval = (f=filter_types(fval)) ? cpystr(f->name) : NULL;
+ set_current_val(&filter_type_var, FALSE, FALSE);
+
+ /* Blank line */
+ new_confline(&ctmp);
+ ctmp->flags |= CF_NOSELECT | CF_B_LINE;
+
+ /* Filter Type */
+ new_confline(&ctmp);
+ ctmp->var = &filter_type_var;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = NULL;
+ snprintf(tmp, sizeof(tmp), "%s =", filter_type_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmpb = ctmp;
+ ctmp->flags |= (CF_NOSELECT | CF_STARTITEM);
+
+ new_confline(&ctmp);
+ ctmp->var = NULL;
+ ctmp->valoffset = rindent;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = NULL;
+ ctmp->varnamep = ctmpb;
+ ctmp->flags |= CF_NOSELECT;
+ ctmp->value = cpystr("Set Choose One");
+
+ new_confline(&ctmp);
+ ctmp->var = NULL;
+ ctmp->valoffset = rindent;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = radio_tool;
+ ctmp->varnamep = ctmpb;
+ ctmp->flags |= CF_NOSELECT;
+ ctmp->value = cpystr(set_choose); \
+
+ /* find longest value's name */
+ for(lv = 0, i = 0; (f = filter_types(i)); i++)
+ if(lv < (j = utf8_width(f->name)))
+ lv = j;
+
+ lv = MIN(lv, 100);
+
+ for(i = 0; (f = filter_types(i)); i++){
+ new_confline(&ctmp);
+ ctmp->help_title= _("HELP FOR FILTER ACTION");
+ ctmp->var = &filter_type_var;
+ ctmp->valoffset = rindent;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = h_config_filt_rule_type;
+ ctmp->varmem = i;
+ ctmp->tool = radio_tool;
+ ctmp->varnamep = ctmpb;
+ utf8_snprintf(tmp, sizeof(tmp), "(%c) %-*.*w", (f->value == fval) ? R_SELD : ' ',
+ lv, lv, f->name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->value = cpystr(tmp);
+ }
+
+ /* Specific list of folders to copy to */
+ setup_dummy_pattern_var(&folder_act_var, _("Folder List"),
+ (def && def->action)
+ ? def->action->folder : NULL);
+
+ /* 5 is the width of `(*) ' */
+ setup_role_pat_alt(ps, &ctmp, &folder_act_var, h_config_filter_folder,
+ _("HELP FOR FILTER FOLDER NAME"),
+ &config_role_actionfolder_keymenu, t_tool, rindent+5,
+ !(fval == FILTER_FOLDER));
+
+ SETUP_MSG_STATE(ctmp, filt_imp_var, ival,
+ _("HELP FOR SET IMPORTANT STATUS"), h_config_filt_stat_imp);
+ SETUP_MSG_STATE(ctmp, filt_new_var, nval,
+ _("HELP FOR SET NEW STATUS"), h_config_filt_stat_new);
+ SETUP_MSG_STATE(ctmp, filt_del_var, dval,
+ _("HELP FOR SET DELETED STATUS"), h_config_filt_stat_del);
+ SETUP_MSG_STATE(ctmp, filt_ans_var, aval,
+ _("HELP FOR SET ANSWERED STATUS"), h_config_filt_stat_ans);
+
+ /* Blank line */
+ new_confline(&ctmp);
+ ctmp->flags |= CF_NOSELECT | CF_B_LINE;
+
+ /* Keywords to be Set */
+ setup_dummy_pattern_var(&keyword_set_var, _("Set These Keywords"),
+ (def && def->action)
+ ? def->action->keyword_set : NULL);
+ setup_role_pat(ps, &ctmp, &keyword_set_var, h_config_filter_kw_set,
+ _("HELP FOR KEYWORDS TO BE SET"),
+ &config_role_keyword_keymenu, role_text_tool_kword,
+ NULL, 23);
+
+ /* Keywords to be Cleared */
+ setup_dummy_pattern_var(&keyword_clr_var, _("Clear These Keywords"),
+ (def && def->action)
+ ? def->action->keyword_clr : NULL);
+ setup_role_pat(ps, &ctmp, &keyword_clr_var, h_config_filter_kw_clr,
+ _("HELP FOR KEYWORDS TO BE CLEARED"),
+ &config_role_keyword_keymenu, role_text_tool_kword,
+ NULL, 23);
+ }
+
+ if(edit_other){
+ char *pval;
+ int oindent;
+
+ /* Blank line */
+ new_confline(&ctmp);
+ ctmp->flags |= CF_NOSELECT | CF_B_LINE;
+
+ new_confline(&ctmp)->var = NULL;
+ snprintf(tmp, sizeof(tmp), "%s =", sort_act_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmpb = ctmp;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = role_sort_tool;
+ ctmp->valoffset = rindent;
+ ctmp->flags |= CF_NOSELECT;
+
+ new_confline(&ctmp)->var = NULL;
+ ctmp->varnamep = ctmpb;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = role_sort_tool;
+ ctmp->valoffset = rindent;
+ ctmp->flags |= CF_NOSELECT;
+ ctmp->value = cpystr("Set Sort Options");
+
+ new_confline(&ctmp)->var = NULL;
+ ctmp->varnamep = ctmpb;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = role_sort_tool;
+ ctmp->valoffset = rindent;
+ ctmp->flags |= CF_NOSELECT;
+ ctmp->value = cpystr(set_choose); \
+
+ pval = PVAL(&sort_act_var, ew);
+ if(pval)
+ decode_sort(pval, &def_sort, &def_sort_rev);
+
+ /* allow user to set their default sort order */
+ new_confline(&ctmp)->var = &sort_act_var;
+ ctmp->varnamep = ctmpb;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = h_config_perfolder_sort;
+ ctmp->tool = role_sort_tool;
+ ctmp->valoffset = rindent;
+ ctmp->varmem = -1;
+ ctmp->value = generalized_sort_pretty_value(ps, ctmp, 0);
+
+ for(j = 0; j < 2; j++){
+ for(i = 0; ps->sort_types[i] != EndofList; i++){
+ new_confline(&ctmp)->var = &sort_act_var;
+ ctmp->varnamep = ctmpb;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = h_config_perfolder_sort;
+ ctmp->tool = role_sort_tool;
+ ctmp->valoffset = rindent;
+ ctmp->varmem = i + (j * EndofList);
+ ctmp->value = generalized_sort_pretty_value(ps, ctmp,
+ 0);
+ }
+ }
+
+
+ /* Blank line */
+ new_confline(&ctmp);
+ ctmp->flags |= CF_NOSELECT | CF_B_LINE;
+
+ oindent = utf8_width(iform_act_var.name) + 3;
+
+ /* Index Format Action */
+ new_confline(&ctmp);
+ ctmp->help_title= _("HELP FOR SET INDEX FORMAT ACTION");
+ ctmp->var = &iform_act_var;
+ ctmp->valoffset = oindent;
+ ctmp->keymenu = &config_text_keymenu;
+ ctmp->help = h_config_set_index_format;
+ ctmp->tool = text_tool;
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.*w =", oindent-3, oindent-3, iform_act_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmp;
+ ctmp->value = pretty_value(ps, ctmp);
+
+ /* Blank line */
+ new_confline(&ctmp);
+ ctmp->flags |= CF_NOSELECT | CF_B_LINE;
+
+ new_confline(&ctmp);
+ ctmp->flags |= CF_STARTITEM;
+ utf8_snprintf(tmp, sizeof(tmp), "%s =", startup_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ standard_radio_setup(ps, &ctmp, &startup_var, NULL);
+ }
+
+ if(edit_incol && pico_usingcolor()){
+ char *pval0, *pval1;
+ int def;
+
+ /* Blank line */
+ new_confline(&ctmp);
+ ctmp->flags |= CF_NOSELECT | CF_B_LINE;
+
+ new_confline(&ctmp);
+ ctmp->var = &rolecolor_vars[0]; /* foreground */
+ ctmp->varname = cpystr(_("Index Line Color ="));
+ ctmp->varnamep = ctmpb = ctmp;
+ ctmp->flags |= (CF_STARTITEM | CF_NOSELECT);
+ ctmp->keymenu = &role_color_setting_keymenu;
+
+ pval0 = PVAL(&rolecolor_vars[0], ew);
+ pval1 = PVAL(&rolecolor_vars[1], ew);
+ if(pval0 && pval1)
+ def = !(pval0[0] && pval1[1]);
+ else
+ def = 1;
+
+ add_color_setting_disp(ps, &ctmp, &rolecolor_vars[0], ctmpb,
+ &role_color_setting_keymenu,
+ &config_checkbox_keymenu,
+ h_config_incol,
+ rindent, 0,
+ def ? ps->VAR_NORM_FORE_COLOR
+ : PVAL(&rolecolor_vars[0], ew),
+ def ? ps->VAR_NORM_BACK_COLOR
+ : PVAL(&rolecolor_vars[1], ew),
+ def);
+ }
+ }
+
+ if(need_options){
+ /* Options */
+
+ /* Blank line */
+ new_confline(&ctmp);
+ ctmp->flags |= CF_NOSELECT | CF_B_LINE;
+
+ new_confline(&ctmp);
+ ctmp->flags |= CF_NOSELECT;
+ if(ps->ttyo->screen_cols >= (wid=utf8_width(ostr)) + 4){
+ int dashes;
+
+ dashes = (ps->ttyo->screen_cols - wid)/2;
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s", repeat_char(dashes, '='),
+ ostr, repeat_char(ps->ttyo->screen_cols-wid-dashes, '='));
+ ctmp->value = cpystr(tmp_20k_buf);
+ }
+ else
+ ctmp->value = cpystr(repeat_char(ps->ttyo->screen_cols, '='));
+
+ /* Blank line */
+ new_confline(&ctmp);
+ ctmp->flags |= CF_NOSELECT | CF_B_LINE;
+
+ opt_var.name = cpystr(_("Features"));
+ opt_var.is_used = 1;
+ opt_var.is_user = 1;
+ opt_var.is_list = 1;
+ clrbitmap(feat_option_list);
+ if(def && def->patgrp && def->patgrp->age_uses_sentdate)
+ setbitn(FEAT_SENTDATE, feat_option_list);
+
+ if(edit_filter){
+ if(def && def->action && def->action->move_only_if_not_deleted)
+ setbitn(FEAT_IFNOTDEL, feat_option_list);
+ if(def && def->action && def->action->non_terminating)
+ setbitn(FEAT_NONTERM, feat_option_list);
+ }
+
+ /* Options */
+ new_confline(&ctmp);
+ ctmp->var = &opt_var;
+ ctmp->valoffset = 23;
+ ctmp->keymenu = &config_checkbox_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = NULL;
+ snprintf(tmp, sizeof(tmp), "%-20s =", opt_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmpb = ctmp;
+ ctmp->flags |= (CF_NOSELECT | CF_STARTITEM);
+
+ new_confline(&ctmp);
+ ctmp->var = NULL;
+ ctmp->valoffset = rindent;
+ ctmp->keymenu = &config_checkbox_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = feat_checkbox_tool;
+ ctmp->varnamep = ctmpb;
+ ctmp->flags |= CF_NOSELECT;
+ ctmp->value = cpystr("Set Feature Name");
+
+ new_confline(&ctmp);
+ ctmp->var = NULL;
+ ctmp->valoffset = rindent;
+ ctmp->keymenu = &config_checkbox_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = feat_checkbox_tool;
+ ctmp->varnamep = ctmpb;
+ ctmp->flags |= CF_NOSELECT;
+ ctmp->value = cpystr(set_choose); \
+
+ /* find longest value's name */
+ for(lv = 0, i = 0; (f = feat_feature_list(i)); i++){
+ if(!edit_filter && (i == FEAT_IFNOTDEL || i == FEAT_NONTERM))
+ continue;
+
+ if(lv < (j = utf8_width(f->name)))
+ lv = j;
+ }
+
+ lv = MIN(lv, 100);
+
+ for(i = 0; (f = feat_feature_list(i)); i++){
+ if(!edit_filter && (i == FEAT_IFNOTDEL || i == FEAT_NONTERM))
+ continue;
+
+ new_confline(&ctmp);
+ ctmp->var = &opt_var;
+ ctmp->help_title= _("HELP FOR FILTER FEATURES");
+ ctmp->varnamep = ctmpb;
+ ctmp->keymenu = &config_checkbox_keymenu;
+ switch(i){
+ case FEAT_SENTDATE:
+ ctmp->help = h_config_filt_opts_sentdate;
+ break;
+ case FEAT_IFNOTDEL:
+ ctmp->help = h_config_filt_opts_notdel;
+ break;
+ case FEAT_NONTERM:
+ ctmp->help = h_config_filt_opts_nonterm;
+ break;
+ }
+
+ ctmp->tool = feat_checkbox_tool;
+ ctmp->valoffset = rindent;
+ ctmp->varmem = i;
+ utf8_snprintf(tmp, sizeof(tmp), "[%c] %-*.*w",
+ bitnset(f->value, feat_option_list) ? 'X' : ' ',
+ lv, lv, f->name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->value = cpystr(tmp);
+ }
+ }
+
+ if(need_uses){
+ /* Uses */
+
+ /* Blank line */
+ new_confline(&ctmp);
+ ctmp->flags |= CF_NOSELECT | CF_B_LINE;
+
+ new_confline(&ctmp);
+ ctmp->flags |= CF_NOSELECT;
+ if(ps->ttyo->screen_cols >= (wid=utf8_width(ustr)) + 4){
+ int dashes;
+
+ dashes = (ps->ttyo->screen_cols - wid)/2;
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s", repeat_char(dashes, '='),
+ ustr, repeat_char(ps->ttyo->screen_cols-wid-dashes, '='));
+ ctmp->value = cpystr(tmp_20k_buf);
+ }
+ else
+ ctmp->value = cpystr(repeat_char(ps->ttyo->screen_cols, '='));
+
+ /* Blank line */
+ new_confline(&ctmp);
+ ctmp->flags |= CF_NOSELECT | CF_B_LINE;
+
+ /* Reply Type */
+ new_confline(&ctmp);
+ ctmp->var = &repl_type_var;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = NULL;
+ snprintf(tmp, sizeof(tmp), "%s =", repl_type_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmpb = ctmp;
+ ctmp->flags |= (CF_NOSELECT | CF_STARTITEM);
+
+ new_confline(&ctmp);
+ ctmp->var = NULL;
+ ctmp->valoffset = rindent;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = NULL;
+ ctmp->varnamep = ctmpb;
+ ctmp->flags |= CF_NOSELECT;
+ ctmp->value = cpystr("Set Choose One");
+
+ new_confline(&ctmp);
+ ctmp->var = NULL;
+ ctmp->valoffset = rindent;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = radio_tool;
+ ctmp->varnamep = ctmpb;
+ ctmp->flags |= CF_NOSELECT;
+ ctmp->value = cpystr(set_choose); \
+
+ /* find longest value's name */
+ for(lv = 0, i = 0; (f = role_repl_types(i)); i++)
+ if(lv < (j = utf8_width(f->name)))
+ lv = j;
+
+ lv = MIN(lv, 100);
+
+ for(i = 0; (f = role_repl_types(i)); i++){
+ new_confline(&ctmp);
+ ctmp->help_title= _("HELP FOR ROLE REPLY USE");
+ ctmp->var = &repl_type_var;
+ ctmp->valoffset = rindent;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = h_config_role_replyuse;
+ ctmp->varmem = i;
+ ctmp->tool = radio_tool;
+ ctmp->varnamep = ctmpb;
+ utf8_snprintf(tmp, sizeof(tmp), "(%c) %-*.*w", (((!(def && def->action) ||
+ def->action->repl_type == -1) &&
+ f->value == ROLE_REPL_DEFL) ||
+ (def && def->action &&
+ f->value == def->action->repl_type))
+ ? R_SELD : ' ',
+ lv, lv, f->name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->value = cpystr(tmp);
+ }
+
+ /* Blank line */
+ new_confline(&ctmp);
+ ctmp->flags |= CF_NOSELECT | CF_B_LINE;
+
+ /* Forward Type */
+ new_confline(&ctmp);
+ ctmp->var = &forw_type_var;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = NULL;
+ snprintf(tmp, sizeof(tmp), "%s =", forw_type_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmpb = ctmp;
+ ctmp->flags |= (CF_NOSELECT | CF_STARTITEM);
+
+ new_confline(&ctmp);
+ ctmp->var = NULL;
+ ctmp->valoffset = rindent;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = NULL;
+ ctmp->varnamep = ctmpb;
+ ctmp->flags |= CF_NOSELECT;
+ ctmp->value = cpystr("Set Choose One");
+
+ new_confline(&ctmp);
+ ctmp->var = NULL;
+ ctmp->valoffset = rindent;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = radio_tool;
+ ctmp->varnamep = ctmpb;
+ ctmp->flags |= CF_NOSELECT;
+ ctmp->value = cpystr(set_choose); \
+
+ /* find longest value's name */
+ for(lv = 0, i = 0; (f = role_forw_types(i)); i++)
+ if(lv < (j = utf8_width(f->name)))
+ lv = j;
+
+ lv = MIN(lv, 100);
+
+ for(i = 0; (f = role_forw_types(i)); i++){
+ new_confline(&ctmp);
+ ctmp->help_title= _("HELP FOR ROLE FORWARD USE");
+ ctmp->var = &forw_type_var;
+ ctmp->valoffset = rindent;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = h_config_role_forwarduse;
+ ctmp->varmem = i;
+ ctmp->tool = radio_tool;
+ ctmp->varnamep = ctmpb;
+ utf8_snprintf(tmp, sizeof(tmp), "(%c) %-*.*w", (((!(def && def->action) ||
+ def->action->forw_type == -1) &&
+ f->value == ROLE_FORW_DEFL) ||
+ (def && def->action &&
+ f->value == def->action->forw_type))
+ ? R_SELD : ' ',
+ lv, lv, f->name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->value = cpystr(tmp);
+ }
+
+ /* Blank line */
+ new_confline(&ctmp);
+ ctmp->flags |= CF_NOSELECT | CF_B_LINE;
+
+ /* Compose Type */
+ new_confline(&ctmp);
+ ctmp->var = &comp_type_var;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = NULL;
+ snprintf(tmp, sizeof(tmp), "%s =", comp_type_var.name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->varname = cpystr(tmp);
+ ctmp->varnamep = ctmpb = ctmp;
+ ctmp->flags |= (CF_NOSELECT | CF_STARTITEM);
+
+ new_confline(&ctmp);
+ ctmp->var = NULL;
+ ctmp->valoffset = rindent;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = NULL;
+ ctmp->varnamep = ctmpb;
+ ctmp->flags |= CF_NOSELECT;
+ ctmp->value = cpystr("Set Choose One");
+
+ new_confline(&ctmp);
+ ctmp->var = NULL;
+ ctmp->valoffset = rindent;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = NO_HELP;
+ ctmp->tool = radio_tool;
+ ctmp->varnamep = ctmpb;
+ ctmp->flags |= CF_NOSELECT;
+ ctmp->value = cpystr(set_choose); \
+
+ /* find longest value's name */
+ for(lv = 0, i = 0; (f = role_comp_types(i)); i++)
+ if(lv < (j = utf8_width(f->name)))
+ lv = j;
+
+ lv = MIN(lv, 100);
+
+ for(i = 0; (f = role_comp_types(i)); i++){
+ new_confline(&ctmp);
+ ctmp->help_title= _("HELP FOR ROLE COMPOSE USE");
+ ctmp->var = &comp_type_var;
+ ctmp->valoffset = rindent;
+ ctmp->keymenu = &config_radiobutton_keymenu;
+ ctmp->help = h_config_role_composeuse;
+ ctmp->varmem = i;
+ ctmp->tool = radio_tool;
+ ctmp->varnamep = ctmpb;
+ utf8_snprintf(tmp, sizeof(tmp), "(%c) %-*.*w", (((!(def && def->action) ||
+ def->action->comp_type == -1) &&
+ f->value == ROLE_COMP_DEFL) ||
+ (def && def->action &&
+ f->value == def->action->comp_type))
+ ? R_SELD : ' ',
+ lv, lv, f->name);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmp->value = cpystr(tmp);
+ }
+ }
+
+ memset(&screen, 0, sizeof(screen));
+ screen.ro_warning = saved_screen ? saved_screen->deferred_ro_warning : 0;
+ /* TRANSLATORS: Print something1 using something2.
+ "roles" is something1 */
+ rv = conf_scroll_screen(ps, &screen, first_line, title, _("roles"),
+ (edit_incol && pico_usingcolor()) ? 1 : 0);
+
+ /*
+ * Now look at the fake variables and extract the information we
+ * want from them.
+ */
+
+ if(rv == 1 && result){
+ /*
+ * We know these variables exist, so we don't have to check that
+ * apval is nonnull before evaluating *apval.
+ */
+ apval = APVAL(&nick_var, ew);
+ nick = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(nick);
+
+ apval = APVAL(&comment_var, ew);
+ comment = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(comment);
+
+ alval = ALVAL(&to_pat_var, ew);
+ to_pat = *alval;
+ *alval = NULL;
+
+ alval = ALVAL(&from_pat_var, ew);
+ from_pat = *alval;
+ *alval = NULL;
+
+ alval = ALVAL(&sender_pat_var, ew);
+ sender_pat = *alval;
+ *alval = NULL;
+
+ alval = ALVAL(&cc_pat_var, ew);
+ cc_pat = *alval;
+ *alval = NULL;
+
+ alval = ALVAL(&recip_pat_var, ew);
+ recip_pat = *alval;
+ *alval = NULL;
+
+ alval = ALVAL(&partic_pat_var, ew);
+ partic_pat = *alval;
+ *alval = NULL;
+
+ alval = ALVAL(&news_pat_var, ew);
+ news_pat = *alval;
+ *alval = NULL;
+
+ alval = ALVAL(&subj_pat_var, ew);
+ subj_pat = *alval;
+ *alval = NULL;
+
+ alval = ALVAL(&alltext_pat_var, ew);
+ alltext_pat = *alval;
+ *alval = NULL;
+
+ alval = ALVAL(&bodytext_pat_var, ew);
+ bodytext_pat = *alval;
+ *alval = NULL;
+
+ alval = ALVAL(&keyword_pat_var, ew);
+ keyword_pat = *alval;
+ *alval = NULL;
+
+ alval = ALVAL(&charset_pat_var, ew);
+ charset_pat = *alval;
+ *alval = NULL;
+
+ apval = APVAL(&age_pat_var, ew);
+ age_pat = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(age_pat);
+
+ apval = APVAL(&size_pat_var, ew);
+ size_pat = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(size_pat);
+
+ apval = APVAL(&scorei_pat_var, ew);
+ scorei_pat = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(scorei_pat);
+
+ apval = APVAL(&stat_del_var, ew);
+ stat_del = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(stat_del);
+
+ apval = APVAL(&stat_new_var, ew);
+ stat_new = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(stat_new);
+
+ apval = APVAL(&stat_rec_var, ew);
+ stat_rec = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(stat_rec);
+
+ apval = APVAL(&stat_imp_var, ew);
+ stat_imp = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(stat_imp);
+
+ apval = APVAL(&stat_ans_var, ew);
+ stat_ans = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(stat_ans);
+
+ apval = APVAL(&stat_8bit_var, ew);
+ stat_8bit = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(stat_8bit);
+
+ apval = APVAL(&stat_bom_var, ew);
+ stat_bom = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(stat_bom);
+
+ apval = APVAL(&stat_boy_var, ew);
+ stat_boy = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(stat_boy);
+
+ apval = APVAL(&fldr_type_var, ew);
+ fldr_type_pat = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(fldr_type_pat);
+
+ alval = ALVAL(&folder_pat_var, ew);
+ folder_pat = *alval;
+ *alval = NULL;
+
+ apval = APVAL(&abook_type_var, ew);
+ abook_type_pat = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(abook_type_pat);
+
+ alval = ALVAL(&abook_pat_var, ew);
+ abook_pat = *alval;
+ *alval = NULL;
+
+ apval = APVAL(&cati_var, ew);
+ cati = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(cati);
+
+ apval = APVAL(&cat_lim_var, ew);
+ cat_lim = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(cat_lim);
+
+ apval = APVAL(&inick_var, ew);
+ inick = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(inick);
+
+ apval = APVAL(&from_act_var, ew);
+ from_act = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(from_act);
+
+ apval = APVAL(&replyto_act_var, ew);
+ replyto_act = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(replyto_act);
+
+ apval = APVAL(&fcc_act_var, ew);
+ fcc_act = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(fcc_act);
+
+ apval = APVAL(&litsig_act_var, ew);
+ litsig_act = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(litsig_act);
+
+ apval = APVAL(&sort_act_var, ew);
+ sort_act = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(sort_act);
+
+ apval = APVAL(&iform_act_var, ew);
+ iform_act = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(iform_act);
+
+ apval = APVAL(&startup_var, ew);
+ startup_act = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(startup_act);
+
+ apval = APVAL(&sig_act_var, ew);
+ sig_act = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(sig_act);
+
+ apval = APVAL(&templ_act_var, ew);
+ templ_act = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(templ_act);
+
+ apval = APVAL(&score_act_var, ew);
+ score_act = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(score_act);
+
+ apval = APVAL(&hdrtok_act_var, ew);
+ hdrtok_act = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(hdrtok_act);
+
+ apval = APVAL(&repl_type_var, ew);
+ repl_type = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(repl_type);
+
+ apval = APVAL(&forw_type_var, ew);
+ forw_type = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(forw_type);
+
+ apval = APVAL(&comp_type_var, ew);
+ comp_type = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(comp_type);
+
+ apval = APVAL(&rolecolor_vars[0], ew);
+ rc_fg = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(rc_fg);
+
+ apval = APVAL(&rolecolor_vars[1], ew);
+ rc_bg = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(rc_bg);
+
+ apval = APVAL(&filter_type_var, ew);
+ filter_type = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(filter_type);
+
+ alval = ALVAL(&folder_act_var, ew);
+ folder_act = *alval;
+ *alval = NULL;
+
+ alval = ALVAL(&keyword_set_var, ew);
+ keyword_set = *alval;
+ *alval = NULL;
+
+ alval = ALVAL(&keyword_clr_var, ew);
+ keyword_clr = *alval;
+ *alval = NULL;
+
+ apval = APVAL(&filt_imp_var, ew);
+ filt_imp = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(filt_imp);
+
+ apval = APVAL(&filt_del_var, ew);
+ filt_del = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(filt_del);
+
+ apval = APVAL(&filt_new_var, ew);
+ filt_new = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(filt_new);
+
+ apval = APVAL(&filt_ans_var, ew);
+ filt_ans = *apval;
+ *apval = NULL;
+ removing_leading_and_trailing_white_space(filt_ans);
+
+
+ alval = ALVAL(&cat_cmd_var, ew);
+ cat_cmd = *alval;
+ *alval = NULL;
+
+ alval = ALVAL(&cstm_act_var, ew);
+ cstm_act = *alval;
+ *alval = NULL;
+
+ alval = ALVAL(&smtp_act_var, ew);
+ smtp_act = *alval;
+ *alval = NULL;
+
+ alval = ALVAL(&nntp_act_var, ew);
+ nntp_act = *alval;
+ *alval = NULL;
+
+ if(ps->VAR_OPER_DIR && sig_act &&
+ is_absolute_path(sig_act) &&
+ !in_dir(ps->VAR_OPER_DIR, sig_act)){
+ q_status_message1(SM_ORDER | SM_DING, 3, 4,
+ _("Warning: Sig file can't be outside of %s"),
+ ps->VAR_OPER_DIR);
+ }
+
+ if(ps->VAR_OPER_DIR && templ_act &&
+ is_absolute_path(templ_act) &&
+ !in_dir(ps->VAR_OPER_DIR, templ_act)){
+ q_status_message1(SM_ORDER | SM_DING, 3, 4,
+ _("Warning: Template file can't be outside of %s"),
+ ps->VAR_OPER_DIR);
+ }
+
+ if(ps->VAR_OPER_DIR && folder_act)
+ for(i = 0; folder_act[i]; i++){
+ if(folder_act[i][0] && is_absolute_path(folder_act[i]) &&
+ !in_dir(ps->VAR_OPER_DIR, folder_act[i])){
+ q_status_message1(SM_ORDER | SM_DING, 3, 4,
+ _("Warning: Folder can't be outside of %s"),
+ ps->VAR_OPER_DIR);
+ }
+ }
+
+ *result = (PAT_S *)fs_get(sizeof(**result));
+ memset((void *)(*result), 0, sizeof(**result));
+
+ (*result)->patgrp = (PATGRP_S *)fs_get(sizeof(*(*result)->patgrp));
+ memset((void *)(*result)->patgrp, 0, sizeof(*(*result)->patgrp));
+
+ (*result)->action = (ACTION_S *)fs_get(sizeof(*(*result)->action));
+ memset((void *)(*result)->action, 0, sizeof(*(*result)->action));
+
+ (*result)->patline = def ? def->patline : NULL;
+
+ if(nick && *nick){
+ (*result)->patgrp->nick = nick;
+ nick = NULL;
+ }
+ else
+ (*result)->patgrp->nick = cpystr(nick_var.global_val.p);
+
+ if(comment && *comment){
+ (*result)->patgrp->comment = comment;
+ comment = NULL;
+ }
+
+ (*result)->action->nick = cpystr((*result)->patgrp->nick);
+
+ (*result)->action->is_a_role = edit_role ? 1 : 0;
+ (*result)->action->is_a_incol = edit_incol ? 1 : 0;
+ (*result)->action->is_a_score = edit_score ? 1 : 0;
+ (*result)->action->is_a_filter = edit_filter ? 1 : 0;
+ (*result)->action->is_a_other = edit_other ? 1 : 0;
+ (*result)->action->is_a_srch = edit_srch ? 1 : 0;
+
+ (*result)->patgrp->to = editlist_to_pattern(to_pat);
+ if((*result)->patgrp->to && !strncmp(to_pat_var.name, NOT, NOTLEN))
+ (*result)->patgrp->to->not = 1;
+
+ (*result)->patgrp->from = editlist_to_pattern(from_pat);
+ if((*result)->patgrp->from && !strncmp(from_pat_var.name, NOT, NOTLEN))
+ (*result)->patgrp->from->not = 1;
+
+ (*result)->patgrp->sender = editlist_to_pattern(sender_pat);
+ if((*result)->patgrp->sender &&
+ !strncmp(sender_pat_var.name, NOT, NOTLEN))
+ (*result)->patgrp->sender->not = 1;
+
+ (*result)->patgrp->cc = editlist_to_pattern(cc_pat);
+ if((*result)->patgrp->cc && !strncmp(cc_pat_var.name, NOT, NOTLEN))
+ (*result)->patgrp->cc->not = 1;
+
+ (*result)->patgrp->recip = editlist_to_pattern(recip_pat);
+ if((*result)->patgrp->recip &&
+ !strncmp(recip_pat_var.name, NOT, NOTLEN))
+ (*result)->patgrp->recip->not = 1;
+
+ (*result)->patgrp->partic = editlist_to_pattern(partic_pat);
+ if((*result)->patgrp->partic &&
+ !strncmp(partic_pat_var.name, NOT, NOTLEN))
+ (*result)->patgrp->partic->not = 1;
+
+ (*result)->patgrp->news = editlist_to_pattern(news_pat);
+ if((*result)->patgrp->news && !strncmp(news_pat_var.name, NOT, NOTLEN))
+ (*result)->patgrp->news->not = 1;
+
+ (*result)->patgrp->subj = editlist_to_pattern(subj_pat);
+ if((*result)->patgrp->subj && !strncmp(subj_pat_var.name, NOT, NOTLEN))
+ (*result)->patgrp->subj->not = 1;
+
+ (*result)->patgrp->alltext = editlist_to_pattern(alltext_pat);
+ if((*result)->patgrp->alltext &&
+ !strncmp(alltext_pat_var.name, NOT, NOTLEN))
+ (*result)->patgrp->alltext->not = 1;
+
+ (*result)->patgrp->bodytext = editlist_to_pattern(bodytext_pat);
+ if((*result)->patgrp->bodytext &&
+ !strncmp(bodytext_pat_var.name, NOT, NOTLEN))
+ (*result)->patgrp->bodytext->not = 1;
+
+ (*result)->patgrp->keyword = editlist_to_pattern(keyword_pat);
+ if((*result)->patgrp->keyword &&
+ !strncmp(keyword_pat_var.name, NOT, NOTLEN))
+ (*result)->patgrp->keyword->not = 1;
+
+ (*result)->patgrp->charsets = editlist_to_pattern(charset_pat);
+ if((*result)->patgrp->charsets &&
+ !strncmp(charset_pat_var.name, NOT, NOTLEN))
+ (*result)->patgrp->charsets->not = 1;
+
+ (*result)->patgrp->age_uses_sentdate =
+ bitnset(FEAT_SENTDATE, feat_option_list) ? 1 : 0;
+
+ if(age_pat){
+ if(((*result)->patgrp->age = parse_intvl(age_pat)) != NULL)
+ (*result)->patgrp->do_age = 1;
+ }
+
+ if(size_pat){
+ if(((*result)->patgrp->size = parse_intvl(size_pat)) != NULL)
+ (*result)->patgrp->do_size = 1;
+ }
+
+ if(scorei_pat){
+ if(((*result)->patgrp->score = parse_intvl(scorei_pat)) != NULL)
+ (*result)->patgrp->do_score = 1;
+ }
+
+ (*result)->patgrp->cat_lim = -1L; /* default */
+ if(cat_cmd){
+ if(!cat_cmd[0])
+ fs_give((void **) &cat_cmd);
+
+ /* quick check for absolute paths */
+ if(cat_cmd)
+ for(j = 0; cat_cmd[j]; j++)
+ if(!is_absolute_path(cat_cmd[j]))
+ q_status_message1(SM_ORDER | SM_DING, 3, 4,
+ _("Warning: command must be absolute path: \"%s\""),
+ cat_cmd[j]);
+
+ (*result)->patgrp->category_cmd = cat_cmd;
+ cat_cmd = NULL;
+
+ if(cati){
+ if(((*result)->patgrp->cat = parse_intvl(cati)) != NULL)
+ (*result)->patgrp->do_cat = 1;
+ }
+ if(cat_lim && *cat_lim)
+ (*result)->patgrp->cat_lim = atol(cat_lim);
+ }
+
+ if(stat_del && *stat_del){
+ for(j = 0; (f = role_status_types(j)); j++)
+ if(!strucmp(stat_del, f->name)){
+ (*result)->patgrp->stat_del = f->value;
+ break;
+ }
+ }
+ else
+ (*result)->patgrp->stat_del = PAT_STAT_EITHER;
+
+ if(stat_new && *stat_new){
+ for(j = 0; (f = role_status_types(j)); j++)
+ if(!strucmp(stat_new, f->name)){
+ (*result)->patgrp->stat_new = f->value;
+ break;
+ }
+ }
+ else
+ (*result)->patgrp->stat_new = PAT_STAT_EITHER;
+
+ if(stat_rec && *stat_rec){
+ for(j = 0; (f = role_status_types(j)); j++)
+ if(!strucmp(stat_rec, f->name)){
+ (*result)->patgrp->stat_rec = f->value;
+ break;
+ }
+ }
+ else
+ (*result)->patgrp->stat_rec = PAT_STAT_EITHER;
+
+ if(stat_imp && *stat_imp){
+ for(j = 0; (f = role_status_types(j)); j++)
+ if(!strucmp(stat_imp, f->name)){
+ (*result)->patgrp->stat_imp = f->value;
+ break;
+ }
+ }
+ else
+ (*result)->patgrp->stat_imp = PAT_STAT_EITHER;
+
+ if(stat_ans && *stat_ans){
+ for(j = 0; (f = role_status_types(j)); j++)
+ if(!strucmp(stat_ans, f->name)){
+ (*result)->patgrp->stat_ans = f->value;
+ break;
+ }
+ }
+ else
+ (*result)->patgrp->stat_ans = PAT_STAT_EITHER;
+
+ if(stat_8bit && *stat_8bit){
+ for(j = 0; (f = role_status_types(j)); j++)
+ if(!strucmp(stat_8bit, f->name)){
+ (*result)->patgrp->stat_8bitsubj = f->value;
+ break;
+ }
+ }
+ else
+ (*result)->patgrp->stat_8bitsubj = PAT_STAT_EITHER;
+
+ if(stat_bom && *stat_bom){
+ for(j = 0; (f = role_status_types(j)); j++)
+ if(!strucmp(stat_bom, f->name)){
+ (*result)->patgrp->stat_bom = f->value;
+ break;
+ }
+ }
+ else
+ (*result)->patgrp->stat_bom = PAT_STAT_EITHER;
+
+ if(stat_boy && *stat_boy){
+ for(j = 0; (f = role_status_types(j)); j++)
+ if(!strucmp(stat_boy, f->name)){
+ (*result)->patgrp->stat_boy = f->value;
+ break;
+ }
+ }
+ else
+ (*result)->patgrp->stat_boy = PAT_STAT_EITHER;
+
+ if(sort_act){
+ decode_sort(sort_act, &def_sort, &def_sort_rev);
+ (*result)->action->sort_is_set = 1;
+ (*result)->action->sortorder = def_sort;
+ (*result)->action->revsort = (def_sort_rev ? 1 : 0);
+ /*
+ * Don't try to re-sort until next open of folder. If user
+ * $-sorted then it probably shouldn't change anyway. Why
+ * bother keeping track of that?
+ */
+ }
+
+ (*result)->action->index_format = iform_act;
+ iform_act = NULL;
+
+ if(startup_act && *startup_act){
+ for(j = 0; (f = startup_rules(j)); j++)
+ if(!strucmp(startup_act, f->name)){
+ (*result)->action->startup_rule = f->value;
+ break;
+ }
+ }
+ else
+ (*result)->action->startup_rule = IS_NOTSET;
+
+ aa = NULL;
+ for(ea = earb; ea; ea = ea->next){
+ char *xyz;
+
+ if(aa){
+ aa->next = (ARBHDR_S *)fs_get(sizeof(*aa));
+ aa = aa->next;
+ }
+ else{
+ (*result)->patgrp->arbhdr =
+ (ARBHDR_S *)fs_get(sizeof(ARBHDR_S));
+ aa = (*result)->patgrp->arbhdr;
+ }
+
+ memset(aa, 0, sizeof(*aa));
+
+ aa->field = cpystr((ea->a && ea->a->field) ? ea->a->field : "");
+
+ alval = ALVAL(ea->v, ew);
+ spat = *alval;
+ *alval = NULL;
+ aa->p = editlist_to_pattern(spat);
+ if(aa->p && ea->v && ea->v->name &&
+ !strncmp(ea->v->name, NOT, NOTLEN))
+ aa->p->not = 1;
+
+ if((xyz = pattern_to_string(aa->p)) != NULL){
+ if(!*xyz)
+ aa->isemptyval = 1;
+
+ fs_give((void **)&xyz);
+ }
+ }
+
+ if(fldr_type_pat && *fldr_type_pat){
+ for(j = 0; (f = pat_fldr_types(j)); j++)
+ if(!strucmp(fldr_type_pat, f->name)){
+ (*result)->patgrp->fldr_type = f->value;
+ break;
+ }
+ }
+ else{
+ f = pat_fldr_types(FLDR_DEFL);
+ if(f)
+ (*result)->patgrp->fldr_type = f->value;
+ }
+
+ (*result)->patgrp->folder = editlist_to_pattern(folder_pat);
+
+ if(abook_type_pat && *abook_type_pat){
+ for(j = 0; (f = inabook_fldr_types(j)); j++)
+ if(!strucmp(abook_type_pat, f->name)){
+ (*result)->patgrp->inabook = f->value;
+ break;
+ }
+
+ if(bitnset(INABOOK_FROM, inabook_type_list))
+ (*result)->patgrp->inabook |= IAB_FROM;
+ if(bitnset(INABOOK_REPLYTO, inabook_type_list))
+ (*result)->patgrp->inabook |= IAB_REPLYTO;
+ if(bitnset(INABOOK_SENDER, inabook_type_list))
+ (*result)->patgrp->inabook |= IAB_SENDER;
+ if(bitnset(INABOOK_TO, inabook_type_list))
+ (*result)->patgrp->inabook |= IAB_TO;
+ if(bitnset(INABOOK_CC, inabook_type_list))
+ (*result)->patgrp->inabook |= IAB_CC;
+
+ if(!((*result)->patgrp->inabook & IAB_TYPE_MASK))
+ (*result)->patgrp->inabook |= (IAB_FROM | IAB_REPLYTO);
+ }
+ else{
+ f = inabook_fldr_types(IAB_DEFL);
+ if(f)
+ (*result)->patgrp->inabook = f->value;
+ }
+
+ (*result)->patgrp->abooks = editlist_to_pattern(abook_pat);
+
+
+ (*result)->action->inherit_nick = inick;
+ inick = NULL;
+ (*result)->action->fcc = fcc_act;
+ fcc_act = NULL;
+ (*result)->action->litsig = litsig_act;
+ litsig_act = NULL;
+ (*result)->action->sig = sig_act;
+ sig_act = NULL;
+ (*result)->action->template = templ_act;
+ templ_act = NULL;
+
+ if(cstm_act){
+ /*
+ * Check for From or Reply-To and eliminate them.
+ */
+ for(i = 0; cstm_act[i]; i++){
+ char *free_this;
+
+ if((!struncmp(cstm_act[i],"from",4) &&
+ (cstm_act[i][4] == ':' ||
+ cstm_act[i][4] == '\0')) ||
+ (!struncmp(cstm_act[i],"reply-to",8) &&
+ (cstm_act[i][8] == ':' ||
+ cstm_act[i][8] == '\0'))){
+ free_this = cstm_act[i];
+ /* slide the rest up */
+ for(j = i; cstm_act[j]; j++)
+ cstm_act[j] = cstm_act[j+1];
+
+ fs_give((void **)&free_this);
+ i--; /* recheck now that we've slid them up */
+ }
+ }
+
+ /* nothing left */
+ if(!cstm_act[0])
+ fs_give((void **)&cstm_act);
+
+ (*result)->action->cstm = cstm_act;
+ cstm_act = NULL;
+ }
+
+ if(smtp_act){
+ if(!smtp_act[0])
+ fs_give((void **)&smtp_act);
+
+ (*result)->action->smtp = smtp_act;
+ smtp_act = NULL;
+ }
+
+ if(nntp_act){
+ if(!nntp_act[0])
+ fs_give((void **)&nntp_act);
+
+ (*result)->action->nntp = nntp_act;
+ nntp_act = NULL;
+ }
+
+ if(filter_type && *filter_type){
+ (*result)->action->non_terminating =
+ bitnset(FEAT_NONTERM, feat_option_list) ? 1 : 0;
+ for(i = 0; (f = filter_types(i)); i++){
+ if(!strucmp(filter_type, f->name)){
+ if(f->value == FILTER_FOLDER){
+ (*result)->action->folder = editlist_to_pattern(folder_act);
+ (*result)->action->move_only_if_not_deleted =
+ bitnset(FEAT_IFNOTDEL, feat_option_list) ? 1 : 0;
+ }
+ else if(f->value == FILTER_STATE){
+ (*result)->action->kill = 0;
+ }
+ else if(f->value == FILTER_KILL){
+ (*result)->action->kill = 1;
+ }
+
+ /*
+ * This is indented an extra indent because we used to condition
+ * this on !kill. We changed it so that you can set state bits
+ * even if you're killing. This is marginally helpful if you
+ * have another client running that doesn't know about this
+ * filter, but you want to, for example, have the messages show
+ * up now as deleted instead of having that deferred until we
+ * exit. It is controlled by the user by setting the status
+ * action bits along with the Delete.
+ */
+ if(filt_imp && *filt_imp){
+ for(j = 0; (f = msg_state_types(j)); j++){
+ if(!strucmp(filt_imp, f->name)){
+ switch(f->value){
+ case ACT_STAT_LEAVE:
+ break;
+ case ACT_STAT_SET:
+ (*result)->action->state_setting_bits |= F_FLAG;
+ break;
+ case ACT_STAT_CLEAR:
+ (*result)->action->state_setting_bits |= F_UNFLAG;
+ break;
+ }
+ break;
+ }
+ }
+ }
+
+ if(filt_del && *filt_del){
+ for(j = 0; (f = msg_state_types(j)); j++){
+ if(!strucmp(filt_del, f->name)){
+ switch(f->value){
+ case ACT_STAT_LEAVE:
+ break;
+ case ACT_STAT_SET:
+ (*result)->action->state_setting_bits |= F_DEL;
+ break;
+ case ACT_STAT_CLEAR:
+ (*result)->action->state_setting_bits |= F_UNDEL;
+ break;
+ }
+ break;
+ }
+ }
+ }
+
+ if(filt_ans && *filt_ans){
+ for(j = 0; (f = msg_state_types(j)); j++){
+ if(!strucmp(filt_ans, f->name)){
+ switch(f->value){
+ case ACT_STAT_LEAVE:
+ break;
+ case ACT_STAT_SET:
+ (*result)->action->state_setting_bits |= F_ANS;
+ break;
+ case ACT_STAT_CLEAR:
+ (*result)->action->state_setting_bits |= F_UNANS;
+ break;
+ }
+ break;
+ }
+ }
+ }
+
+ if(filt_new && *filt_new){
+ for(j = 0; (f = msg_state_types(j)); j++){
+ if(!strucmp(filt_new, f->name)){
+ switch(f->value){
+ case ACT_STAT_LEAVE:
+ break;
+ case ACT_STAT_SET:
+ (*result)->action->state_setting_bits |= F_UNSEEN;
+ break;
+ case ACT_STAT_CLEAR:
+ (*result)->action->state_setting_bits |= F_SEEN;
+ break;
+ }
+ break;
+ }
+ }
+ }
+
+ (*result)->action->keyword_set =
+ editlist_to_pattern(keyword_set);
+ (*result)->action->keyword_clr =
+ editlist_to_pattern(keyword_clr);
+
+ break;
+ }
+ }
+ }
+
+ if(from_act && *from_act)
+ rfc822_parse_adrlist(&(*result)->action->from, from_act,
+ ps->maildomain);
+
+ if(replyto_act && *replyto_act)
+ rfc822_parse_adrlist(&(*result)->action->replyto, replyto_act,
+ ps->maildomain);
+
+ if(score_act && (j = atoi(score_act)) >= SCORE_MIN && j <= SCORE_MAX)
+ (*result)->action->scoreval = (long) j;
+
+ if(hdrtok_act)
+ (*result)->action->scorevalhdrtok = stringform_to_hdrtok(hdrtok_act);
+
+ if(repl_type && *repl_type){
+ for(j = 0; (f = role_repl_types(j)); j++)
+ if(!strucmp(repl_type, f->name)){
+ (*result)->action->repl_type = f->value;
+ break;
+ }
+ }
+ else{
+ f = role_repl_types(ROLE_REPL_DEFL);
+ if(f)
+ (*result)->action->repl_type = f->value;
+ }
+
+ if(forw_type && *forw_type){
+ for(j = 0; (f = role_forw_types(j)); j++)
+ if(!strucmp(forw_type, f->name)){
+ (*result)->action->forw_type = f->value;
+ break;
+ }
+ }
+ else{
+ f = role_forw_types(ROLE_FORW_DEFL);
+ if(f)
+ (*result)->action->forw_type = f->value;
+ }
+
+ if(comp_type && *comp_type){
+ for(j = 0; (f = role_comp_types(j)); j++)
+ if(!strucmp(comp_type, f->name)){
+ (*result)->action->comp_type = f->value;
+ break;
+ }
+ }
+ else{
+ f = role_comp_types(ROLE_COMP_DEFL);
+ if(f)
+ (*result)->action->comp_type = f->value;
+ }
+
+ if(rc_fg && *rc_fg && rc_bg && *rc_bg){
+ if(!old_fg || !old_bg || strucmp(old_fg, rc_fg) ||
+ strucmp(old_bg, rc_bg))
+ clear_index_cache(ps_global->mail_stream, 0);
+
+ /*
+ * If same as normal color, don't set it. This may or may
+ * not surprise the user when they change the normal color.
+ * This color will track the normal color instead of staying
+ * the same as the old normal color, which is probably
+ * what they want.
+ */
+ if(!ps_global->VAR_NORM_FORE_COLOR ||
+ !ps_global->VAR_NORM_BACK_COLOR ||
+ strucmp(ps_global->VAR_NORM_FORE_COLOR, rc_fg) ||
+ strucmp(ps_global->VAR_NORM_BACK_COLOR, rc_bg))
+ (*result)->action->incol = new_color_pair(rc_fg, rc_bg);
+ }
+ }
+
+ for(j = 0; varlist[j]; j++){
+ v = varlist[j];
+ free_variable_values(v);
+ if(v->name)
+ fs_give((void **)&v->name);
+ }
+
+ if(earb)
+ free_earb(&earb);
+ if(nick)
+ fs_give((void **)&nick);
+ if(comment)
+ fs_give((void **)&comment);
+ if(to_pat)
+ free_list_array(&to_pat);
+ if(from_pat)
+ free_list_array(&from_pat);
+ if(sender_pat)
+ free_list_array(&sender_pat);
+ if(cc_pat)
+ free_list_array(&cc_pat);
+ if(recip_pat)
+ free_list_array(&recip_pat);
+ if(partic_pat)
+ free_list_array(&partic_pat);
+ if(news_pat)
+ free_list_array(&news_pat);
+ if(subj_pat)
+ free_list_array(&subj_pat);
+ if(alltext_pat)
+ free_list_array(&alltext_pat);
+ if(bodytext_pat)
+ free_list_array(&bodytext_pat);
+ if(keyword_pat)
+ free_list_array(&keyword_pat);
+ if(charset_pat)
+ free_list_array(&charset_pat);
+ if(age_pat)
+ fs_give((void **)&age_pat);
+ if(size_pat)
+ fs_give((void **)&size_pat);
+ if(scorei_pat)
+ fs_give((void **)&scorei_pat);
+ if(cati)
+ fs_give((void **)&cati);
+ if(cat_lim)
+ fs_give((void **)&cat_lim);
+ if(stat_del)
+ fs_give((void **)&stat_del);
+ if(stat_new)
+ fs_give((void **)&stat_new);
+ if(stat_rec)
+ fs_give((void **)&stat_rec);
+ if(stat_imp)
+ fs_give((void **)&stat_imp);
+ if(stat_ans)
+ fs_give((void **)&stat_ans);
+ if(stat_8bit)
+ fs_give((void **)&stat_8bit);
+ if(stat_bom)
+ fs_give((void **)&stat_bom);
+ if(stat_boy)
+ fs_give((void **)&stat_boy);
+ if(fldr_type_pat)
+ fs_give((void **)&fldr_type_pat);
+ if(folder_pat)
+ free_list_array(&folder_pat);
+ if(abook_type_pat)
+ fs_give((void **)&abook_type_pat);
+ if(abook_pat)
+ free_list_array(&abook_pat);
+ if(inick)
+ fs_give((void **)&inick);
+ if(from_act)
+ fs_give((void **)&from_act);
+ if(replyto_act)
+ fs_give((void **)&replyto_act);
+ if(fcc_act)
+ fs_give((void **)&fcc_act);
+ if(litsig_act)
+ fs_give((void **)&litsig_act);
+ if(sort_act)
+ fs_give((void **)&sort_act);
+ if(iform_act)
+ fs_give((void **)&iform_act);
+ if(keyword_set)
+ free_list_array(&keyword_set);
+ if(keyword_clr)
+ free_list_array(&keyword_clr);
+ if(startup_act)
+ fs_give((void **)&startup_act);
+ if(sig_act)
+ fs_give((void **)&sig_act);
+ if(templ_act)
+ fs_give((void **)&templ_act);
+ if(score_act)
+ fs_give((void **)&score_act);
+ if(hdrtok_act)
+ fs_give((void **)&hdrtok_act);
+ if(repl_type)
+ fs_give((void **)&repl_type);
+ if(forw_type)
+ fs_give((void **)&forw_type);
+ if(comp_type)
+ fs_give((void **)&comp_type);
+ if(rc_fg)
+ fs_give((void **)&rc_fg);
+ if(rc_bg)
+ fs_give((void **)&rc_bg);
+ if(old_fg)
+ fs_give((void **)&old_fg);
+ if(old_bg)
+ fs_give((void **)&old_bg);
+ if(filt_del)
+ fs_give((void **)&filt_del);
+ if(filt_new)
+ fs_give((void **)&filt_new);
+ if(filt_ans)
+ fs_give((void **)&filt_ans);
+ if(filt_imp)
+ fs_give((void **)&filt_imp);
+ if(folder_act)
+ free_list_array(&folder_act);
+ if(filter_type)
+ fs_give((void **)&filter_type);
+
+ if(cat_cmd)
+ free_list_array(&cat_cmd);
+
+ if(cstm_act)
+ free_list_array(&cstm_act);
+
+ if(smtp_act)
+ free_list_array(&smtp_act);
+
+ if(nntp_act)
+ free_list_array(&nntp_act);
+
+ opt_screen = saved_screen;
+ ps->mangled_screen = 1;
+ return(rv);
+}
+
+
+void
+setup_dummy_pattern_var(struct variable *v, char *name, PATTERN_S *defpat)
+{
+ char ***alval;
+
+ if(!(v && name))
+ panic("setup_dummy_pattern_var");
+
+ v->name = (char *) fs_get(strlen(name)+NOTLEN+1);
+ snprintf(v->name, strlen(name)+NOTLEN+1, "%s%s", (defpat && defpat->not) ? NOT : "", name);
+ v->name[ strlen(name)+NOTLEN+1-1] = '\0';
+ v->is_used = 1;
+ v->is_user = 1;
+ v->is_list = 1;
+ alval = ALVAL(v, ew);
+ *alval = pattern_to_editlist(defpat);
+ set_current_val(v, FALSE, FALSE);
+}
+
+
+void
+setup_role_pat(struct pine *ps, CONF_S **ctmp, struct variable *v, HelpType help,
+ char *help_title, struct key_menu *keymenu,
+ int (*tool)(struct pine *, int, CONF_S **, unsigned),
+ EARB_S **earb, int indent)
+{
+ char tmp[MAXPATH+1];
+ char **lval;
+ int i;
+ CONF_S *ctmpb;
+
+ new_confline(ctmp);
+ (*ctmp)->help_title = help_title;
+ (*ctmp)->var = v;
+ (*ctmp)->valoffset = indent;
+ (*ctmp)->keymenu = keymenu;
+ (*ctmp)->help = help;
+ (*ctmp)->tool = tool;
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.*w =", indent-3, indent-3, (v && v->name) ? v->name : "");
+ tmp[sizeof(tmp)-1] = '\0';
+ (*ctmp)->varname = cpystr(tmp);
+ (*ctmp)->varnamep = *ctmp;
+ (*ctmp)->value = pretty_value(ps, *ctmp);
+ (*ctmp)->d.earb = earb;
+ (*ctmp)->varmem = 0;
+ (*ctmp)->flags = CF_STARTITEM;
+
+ ctmpb = (*ctmp);
+
+ lval = LVAL(v, ew);
+ if(lval){
+ for(i = 0; lval[i]; i++){
+ if(i)
+ new_confline(ctmp);
+
+ (*ctmp)->var = v;
+ (*ctmp)->varmem = i;
+ (*ctmp)->valoffset = indent;
+ (*ctmp)->value = pretty_value(ps, *ctmp);
+ (*ctmp)->d.earb = earb;
+ (*ctmp)->keymenu = keymenu;
+ (*ctmp)->help = help;
+ (*ctmp)->tool = tool;
+ (*ctmp)->varnamep = ctmpb;
+ }
+ }
+}
+
+
+/*
+ * Watch out for polarity of nosel flag. Setting it means to turn on
+ * the NOSELECT flag, which means to make that line unselectable.
+ */
+void
+setup_role_pat_alt(struct pine *ps, CONF_S **ctmp, struct variable *v, HelpType help,
+ char *help_title, struct key_menu *keymenu,
+ int (*tool)(struct pine *, int, CONF_S **, unsigned),
+ int voff, int nosel)
+{
+ char tmp[MAXPATH+1];
+ char **lval;
+ int i, j, k;
+ CONF_S *ctmpb;
+
+ new_confline(ctmp);
+ (*ctmp)->help_title = help_title;
+ (*ctmp)->var = v;
+
+ (*ctmp)->varoffset = voff;
+ k = utf8_width(v->name);
+ j = voff+k+3;
+ (*ctmp)->valoffset = j;
+
+ (*ctmp)->keymenu = keymenu;
+ (*ctmp)->help = help;
+ (*ctmp)->tool = tool;
+
+ utf8_snprintf(tmp, sizeof(tmp), "%*.*w =", k, k, v->name);
+ tmp[sizeof(tmp)-1] = '\0';
+ (*ctmp)->varname = cpystr(tmp);
+
+ (*ctmp)->varnamep = *ctmp;
+ (*ctmp)->value = pretty_value(ps, *ctmp);
+ (*ctmp)->varmem = 0;
+
+ (*ctmp)->flags = (nosel ? CF_NOSELECT : 0);
+
+ ctmpb = (*ctmp);
+
+ lval = LVAL(v, ew);
+ if(lval){
+ for(i = 0; lval[i]; i++){
+ if(i)
+ new_confline(ctmp);
+
+ (*ctmp)->var = v;
+ (*ctmp)->varmem = i;
+ (*ctmp)->varoffset = voff;
+ (*ctmp)->valoffset = j;
+ (*ctmp)->value = pretty_value(ps, *ctmp);
+ (*ctmp)->keymenu = keymenu;
+ (*ctmp)->help = help;
+ (*ctmp)->tool = tool;
+ (*ctmp)->varnamep = ctmpb;
+ (*ctmp)->flags = (nosel ? CF_NOSELECT : 0);
+ }
+ }
+}
+
+
+void
+free_earb(EARB_S **ea)
+{
+ if(ea && *ea){
+ free_earb(&(*ea)->next);
+ if((*ea)->v){
+ free_variable_values((*ea)->v);
+ if((*ea)->v->name)
+ fs_give((void **) &(*ea)->v->name);
+
+ fs_give((void **) &(*ea)->v);;
+ }
+
+ free_arbhdr(&(*ea)->a);
+ fs_give((void **) ea);
+ }
+}
+
+
+void
+calculate_inick_stuff(struct pine *ps)
+{
+ ACTION_S *role, *irole;
+ CONF_S *ctmp, *ctmpa;
+ struct variable *v;
+ int i;
+ char *nick;
+
+ if(inick_confs[INICK_INICK_CONF] == NULL)
+ return;
+
+ for(i = INICK_FROM_CONF; i <= INICK_NNTP_CONF; i++){
+ v = inick_confs[i] ? inick_confs[i]->var : NULL;
+ if(v){
+ if(v->is_list){
+ if(v->global_val.l)
+ free_list_array(&v->global_val.l);
+ }
+ else{
+ if(v->global_val.p)
+ fs_give((void **)&v->global_val.p);
+ }
+ }
+ }
+
+ nick = PVAL(inick_confs[INICK_INICK_CONF]->var, ew);
+
+ if(nick){
+ /*
+ * Use an empty role with inherit_nick set to nick and then use the
+ * combine function to find the action values.
+ */
+ role = (ACTION_S *)fs_get(sizeof(*role));
+ memset((void *)role, 0, sizeof(*role));
+ role->is_a_role = 1;
+ role->inherit_nick = cpystr(nick);
+ irole = combine_inherited_role(role);
+
+ ctmp = inick_confs[INICK_FROM_CONF];
+ v = ctmp ? ctmp->var : NULL;
+
+ if(irole && irole->from){
+ char *bufp;
+ size_t len;
+
+ len = est_size(irole->from);
+ bufp = (char *) fs_get(len * sizeof(char));
+ v->global_val.p = addr_string_mult(irole->from, bufp, len);
+ }
+
+ ctmp = inick_confs[INICK_REPLYTO_CONF];
+ v = ctmp ? ctmp->var : NULL;
+
+ if(irole && irole->replyto){
+ char *bufp;
+ size_t len;
+
+ len = est_size(irole->replyto);
+ bufp = (char *) fs_get(len * sizeof(char));
+ v->global_val.p = addr_string_mult(irole->replyto, bufp, len);
+ }
+
+ ctmp = inick_confs[INICK_FCC_CONF];
+ v = ctmp ? ctmp->var : NULL;
+ v->global_val.p = (irole && irole->fcc) ? cpystr(irole->fcc) : NULL;
+
+ ctmp = inick_confs[INICK_LITSIG_CONF];
+ v = ctmp ? ctmp->var : NULL;
+ v->global_val.p = (irole && irole->litsig) ? cpystr(irole->litsig)
+ : NULL;
+
+ ctmp = inick_confs[INICK_SIG_CONF];
+ v = ctmp ? ctmp->var : NULL;
+ v->global_val.p = (irole && irole->sig) ? cpystr(irole->sig) : NULL;
+
+ ctmp = inick_confs[INICK_TEMPL_CONF];
+ v = ctmp ? ctmp->var : NULL;
+ v->global_val.p = (irole && irole->template)
+ ? cpystr(irole->template) : NULL;
+
+ ctmp = inick_confs[INICK_CSTM_CONF];
+ v = ctmp ? ctmp->var : NULL;
+ v->global_val.l = (irole && irole->cstm) ? copy_list_array(irole->cstm)
+ : NULL;
+
+ ctmp = inick_confs[INICK_SMTP_CONF];
+ v = ctmp ? ctmp->var : NULL;
+ v->global_val.l = (irole && irole->smtp) ? copy_list_array(irole->smtp)
+ : NULL;
+
+ ctmp = inick_confs[INICK_NNTP_CONF];
+ v = ctmp ? ctmp->var : NULL;
+ v->global_val.l = (irole && irole->nntp) ? copy_list_array(irole->nntp)
+ : NULL;
+
+ free_action(&role);
+ free_action(&irole);
+ }
+
+ for(i = INICK_INICK_CONF; i <= INICK_NNTP_CONF; i++){
+ ctmp = inick_confs[i];
+ v = ctmp ? ctmp->var : NULL;
+ /*
+ * If we didn't set a global_val using the nick above, then
+ * set one here for each variable that uses one.
+ */
+ if(v && !v->global_val.p){
+ char *str, *astr, *lc, pdir[MAXPATH+1];
+ ADDRESS *addr;
+ int len;
+
+ switch(i){
+ case INICK_FROM_CONF:
+ addr = generate_from();
+ astr = addr_list_string(addr, NULL, 1);
+ str = (astr && astr[0]) ? astr : "?";
+ v->global_val.p = (char *)fs_get((strlen(str) + 20) *
+ sizeof(char));
+ snprintf(v->global_val.p, strlen(str) + 20, "%s%s)", DSTRING, str);
+ v->global_val.p[strlen(str) + 20 - 1] = '\0';
+ if(astr)
+ fs_give((void **)&astr);
+
+ if(addr)
+ mail_free_address(&addr);
+
+ break;
+
+ case INICK_FCC_CONF:
+ v->global_val.p = cpystr(VSTRING);
+ break;
+
+ case INICK_LITSIG_CONF:
+ /*
+ * This default works this way because of the ordering
+ * of the choices in the detoken routine.
+ */
+ if(ps->VAR_LITERAL_SIG){
+ str = ps->VAR_LITERAL_SIG;
+ v->global_val.p = (char *)fs_get((strlen(str) + 20) *
+ sizeof(char));
+ snprintf(v->global_val.p, strlen(str) + 20,
+ "%s%s)", DSTRING, str);
+ v->global_val.p[strlen(str) + 20 - 1] = '\0';
+ }
+
+ break;
+
+ case INICK_SIG_CONF:
+ pdir[0] = '\0';
+ if(ps_global->VAR_OPER_DIR){
+ strncpy(pdir, ps_global->VAR_OPER_DIR, MAXPATH);
+ pdir[MAXPATH] = '\0';
+ len = strlen(pdir) + 1;
+ }
+ else if((lc = last_cmpnt(ps_global->pinerc)) != NULL){
+ strncpy(pdir, ps_global->pinerc,
+ MIN(MAXPATH,lc-ps_global->pinerc));
+ pdir[MIN(MAXPATH, lc-ps_global->pinerc)] = '\0';
+ len = strlen(pdir);
+ }
+
+ if(pdir[0] && ps->VAR_SIGNATURE_FILE &&
+ ps->VAR_SIGNATURE_FILE[0] &&
+ is_absolute_path(ps->VAR_SIGNATURE_FILE) &&
+ !strncmp(ps->VAR_SIGNATURE_FILE, pdir, len)){
+ str = ps->VAR_SIGNATURE_FILE + len;
+ }
+ else
+ str = (ps->VAR_SIGNATURE_FILE && ps->VAR_SIGNATURE_FILE[0])
+ ? ps->VAR_SIGNATURE_FILE : NULL;
+ if(str){
+ v->global_val.p = (char *)fs_get((strlen(str) + 20) *
+ sizeof(char));
+ snprintf(v->global_val.p, strlen(str) + 20, "%s%s)", DSTRING, str);
+ v->global_val.p[strlen(str) + 20 - 1] = '\0';
+ }
+
+ break;
+
+ case INICK_INICK_CONF:
+ case INICK_REPLYTO_CONF:
+ case INICK_TEMPL_CONF:
+ case INICK_CSTM_CONF:
+ case INICK_SMTP_CONF:
+ case INICK_NNTP_CONF:
+ break;
+ }
+ }
+
+ if(v)
+ set_current_val(v, FALSE, FALSE);
+
+ if(ctmp){
+ CONF_S *ctmpsig = NULL;
+ struct variable *vlsig;
+
+ for(ctmpa = ctmp;
+ ctmpa && ctmpa->varnamep == ctmp;
+ ctmpa = ctmpa->next){
+ if(ctmpa->value)
+ fs_give((void **)&ctmpa->value);
+
+ ctmpa->value = pretty_value(ps, ctmpa);
+ }
+
+ if(i == INICK_SIG_CONF){
+ /*
+ * Turn off NOSELECT, but possibly turn it on again
+ * in next line.
+ */
+ if((ctmpsig = inick_confs[INICK_SIG_CONF]) != NULL)
+ ctmpsig->flags &= ~CF_NOSELECT;
+
+ if(inick_confs[INICK_LITSIG_CONF] &&
+ (vlsig = inick_confs[INICK_LITSIG_CONF]->var) &&
+ vlsig->current_val.p &&
+ vlsig->current_val.p[0]){
+ if(ctmp->value)
+ fs_give((void **)&ctmp->value);
+
+ ctmp->value =
+ cpystr("<Ignored: using LiteralSig instead>");
+
+ ctmp->flags |= CF_NOSELECT;
+ }
+ }
+ }
+ }
+}
+
+
+/* Arguments:
+ * lst: a list of folders
+ * action: a 1 or 0 value which basically says that str is associated with
+ * the filter action if true or the Current Folder type if false.
+ * Return:
+ * Returns 2 on success (user wants to exit) and 0 on failure.
+ *
+ * This function cycles through a list of folders and checks whether or not each
+ * folder exists. If they exist, return 2, if they don't exist, notify the user
+ * or offer to create depending on whether or not it is a filter action or not.
+ * With each of these prompts, the user can abort their desire to exit.
+ */
+int
+check_role_folders(char **lst, unsigned int action)
+{
+ char *cur_fn, wt_res, prompt[MAX_SCREEN_COLS];
+ int i, rv = 2;
+ CONTEXT_S *cntxt = NULL;
+ char nbuf1[MAX_SCREEN_COLS], nbuf2[MAX_SCREEN_COLS];
+ int space, w1, w2, exists;
+
+ if(!(lst && *lst)){
+ if(action)
+ q_status_message(SM_ORDER, 3, 5,
+ _("Set a valid Filter Action before Exiting"));
+ else
+ q_status_message(SM_ORDER, 3, 5,
+ _("Set a valid Specific Folder before Exiting"));
+ rv = 0;
+ return rv;
+ }
+
+ for(i = 0; lst[i]; i++){
+ if(action)
+ cur_fn = detoken_src(lst[i], FOR_FILT, NULL, NULL, NULL, NULL);
+ else
+ cur_fn = lst[i];
+
+ removing_leading_and_trailing_white_space(cur_fn);
+ if(*cur_fn != '\0'){
+ space = MAXPROMPT;
+ if(is_absolute_path(cur_fn) || !context_isambig(cur_fn))
+ cntxt = NULL;
+ else
+ cntxt = default_save_context(ps_global->context_list);
+
+ if(!(exists=folder_exists(cntxt, cur_fn))
+ && (action
+ || (ps_global->context_list->use & CNTXT_INCMNG
+ && !folder_is_nick(cur_fn,FOLDERS(ps_global->context_list), 0)))){
+ if(cntxt && (action == 1)){
+ space -= 37; /* for fixed part of prompt below */
+ w1 = MAX(1,MIN(strlen(cur_fn),space/2));
+ w2 = MIN(MAX(1,space-w1),strlen(cntxt->nickname));
+ w1 += MAX(0,space-w1-w2);
+ snprintf(prompt, sizeof(prompt),
+ _("Folder \"%s\" in <%s> doesn't exist. Create"),
+ short_str(cur_fn,nbuf1,sizeof(nbuf1),w1,MidDots),
+ short_str(cntxt->nickname,nbuf2,sizeof(nbuf2),w2,MidDots));
+ prompt[sizeof(prompt)-1] = '\0';
+ }
+ else if(cntxt && (action == 0)){
+ space -= 51; /* for fixed part of prompt below */
+ w1 = MAX(1,MIN(strlen(cur_fn),space/2));
+ w2 = MIN(MAX(1,space-w1),strlen(cntxt->nickname));
+ w1 += MAX(0,space-w1-w2);
+ snprintf(prompt, sizeof(prompt),
+ _("Folder \"%s\" in <%s> doesn't exist. Exit and save anyway"),
+ short_str(cur_fn,nbuf1,sizeof(nbuf1),w1,MidDots),
+ short_str(cntxt->nickname,nbuf2,sizeof(nbuf2),w2,MidDots));
+ prompt[sizeof(prompt)-1] = '\0';
+ }
+ else if(!cntxt && (action == 1)){
+ space -= 31; /* for fixed part of prompt below */
+ w1 = MAX(1,space);
+ snprintf(prompt, sizeof(prompt),
+ _("Folder \"%s\" doesn't exist. Create"),
+ short_str(cur_fn,nbuf1,sizeof(nbuf1),w1,MidDots));
+ prompt[sizeof(prompt)-1] = '\0';
+ }
+ else{ /*!cntxt && (action == 0) */
+ space -= 45; /* for fixed part of prompt below */
+ w1 = MAX(1,space);
+ snprintf(prompt, sizeof(prompt),
+ _("Folder \"%s\" doesn't exist. Exit and save anyway"),
+ short_str(cur_fn,nbuf1,sizeof(nbuf1),w1,MidDots));
+ prompt[sizeof(prompt)-1] = '\0';
+ }
+
+ wt_res = want_to(prompt, 'n', 'x', NO_HELP, WT_NORM);
+ if(wt_res == 'y'){
+ if(action){
+ if(context_create(cntxt, NULL, cur_fn)){
+ q_status_message(SM_ORDER,3,5,_("Folder created"));
+ maybe_add_to_incoming(cntxt, cur_fn);
+ }
+ }
+ /* No message to notify of changes being saved, we can't */
+ /* assume that the role screen isn't exited yet */
+ rv = 2;
+ }
+ else if(wt_res == 'n' && action){
+ rv = 2;
+ q_status_message(SM_ORDER,3,5,_("Folder not created"));
+ }
+ else{
+ q_status_message(SM_ORDER,3,5,_("Exit cancelled"));
+ rv = 0;
+ break;
+ }
+ }
+ else{
+ if(exists & FEX_ERROR){
+ if(ps_global->mm_log_error && ps_global->c_client_error)
+ q_status_message(SM_ORDER,3,5,ps_global->c_client_error);
+ else
+ q_status_message1(SM_ORDER,3,5,"\"%s\": Trouble checking for folder existence", cur_fn);
+ }
+
+ rv = 2;
+ }
+ }
+ else{ /* blank item in list of folders */
+ if(action && lst[i+1] == '\0')
+ q_status_message(SM_ORDER,3,5,_("Set a valid Filter Action before Exiting"));
+ else /* !action && lst[i+1] == '\0' */
+ q_status_message(SM_ORDER,3,5,_("Set a valid Specific Folder before Exiting"));
+ rv = 0;
+ break;
+ }
+
+ if(cur_fn && cur_fn != lst[i])
+ fs_give((void **) &cur_fn);
+ }
+
+ return(rv);
+}
+
+
+void
+maybe_add_to_incoming(CONTEXT_S *cntxt, char *cur_fn)
+{
+ char name[MAILTMPLEN], nname[32];
+ char nbuf1[MAX_SCREEN_COLS], nbuf2[MAX_SCREEN_COLS];
+ char prompt[MAX_SCREEN_COLS];
+ char ***alval;
+ int i, found, space, w1, w2;
+ FOLDER_S *f;
+
+ if(ps_global->context_list->use & CNTXT_INCMNG &&
+ ((alval = ALVAL(&ps_global->vars[V_INCOMING_FOLDERS], Main)) != NULL)){
+ (void)context_apply(name, cntxt, cur_fn, sizeof(name));
+ /*
+ * Since the folder didn't exist it is very unlikely that it is
+ * in the incoming-folders list already, but we're just checking
+ * to be sure. We should really be canonicalizing both names
+ * before comparing, but...
+ */
+ for(found = 0, i = 0; *alval && (*alval)[i] && !found; i++){
+ char *nickname, *folder;
+
+ get_pair((*alval)[i], &nickname, &folder, 0, 0);
+ if(folder && !strucmp((*alval)[i], folder))
+ found++;
+
+ if(nickname)
+ fs_give((void **)&nickname);
+ if(folder)
+ fs_give((void **)&folder);
+ }
+
+ if(found)
+ return;
+
+ space = MAXPROMPT;
+ space -= 15; /* for fixed part of prompt below */
+ w2 = MAX(1,
+ MIN(space/2,MIN(strlen(ps_global->context_list->nickname),20)));
+ w1 = MAX(1,space - w2);
+ snprintf(prompt, sizeof(prompt),
+ "Add \"%s\" to %s list",
+ short_str(name,nbuf1,sizeof(nbuf1),w1,MidDots),
+ short_str(ps_global->context_list->nickname,nbuf2,sizeof(nbuf2),w2,MidDots));
+ prompt[sizeof(prompt)-1] = '\0';
+ if(want_to(prompt, 'n', 'x', NO_HELP, WT_NORM) == 'y'){
+ char *pp;
+
+ nname[0] = '\0';
+ space = MAXPROMPT;
+ space -= 25;
+ w1 = MAX(1, space);
+ snprintf(prompt, sizeof(prompt), "Nickname for folder \"%s\" : ",
+ short_str(name,nbuf1,sizeof(nbuf1),w1,MidDots));
+ prompt[sizeof(prompt)-1] = '\0';
+ while(1){
+ int rc;
+ int flags = OE_APPEND_CURRENT;
+
+ rc = optionally_enter(nname, -FOOTER_ROWS(ps_global), 0,
+ sizeof(nname), prompt, NULL,
+ NO_HELP, &flags);
+ removing_leading_and_trailing_white_space(nname);
+ if(rc == 0 && *nname){
+ /* see if nickname already exists */
+ found = 0;
+ if(!strucmp(ps_global->inbox_name, nname))
+ found++;
+
+ for(i = 0;
+ !found &&
+ i < folder_total(FOLDERS(ps_global->context_list));
+ i++){
+ FOLDER_S *f;
+
+ f = folder_entry(i, FOLDERS(ps_global->context_list));
+ if(!strucmp(FLDR_NAME(f), nname))
+ found++;
+ }
+
+ if(found){
+ q_status_message1(SM_ORDER | SM_DING, 3, 5,
+ _("Nickname \"%s\" is already in use"),
+ nname);
+ continue;
+ }
+
+ break;
+ }
+ else if(rc == 3)
+ q_status_message(SM_ORDER, 0, 3, _("No help yet."));
+ else if(rc == 1){
+ q_status_message1(SM_ORDER, 0, 3,
+ _("Not adding nickname to %s list"),
+ ps_global->context_list->nickname);
+ return;
+ }
+ }
+
+ pp = put_pair(nname, name);
+ f = new_folder(name, line_hash(pp));
+ f->nickname = cpystr(nname);
+ f->name_len = strlen(nname);
+ folder_insert(folder_total(FOLDERS(ps_global->context_list)), f,
+ FOLDERS(ps_global->context_list));
+
+ if(!*alval){
+ i = 0;
+ *alval = (char **)fs_get(2 * sizeof(char *));
+ }
+ else{
+ for(i = 0; (*alval)[i]; i++)
+ ;
+
+ fs_resize((void **)alval, (i + 2) * sizeof(char *));
+ }
+
+ (*alval)[i] = pp;
+ (*alval)[i+1] = NULL;
+ set_current_val(&ps_global->vars[V_INCOMING_FOLDERS], TRUE, TRUE);
+ write_pinerc(ps_global, ew, WRP_NONE);
+ }
+ }
+}
+
+
+int
+role_filt_exitcheck(CONF_S **cl, unsigned int flags)
+{
+ int rv, j, action;
+ char **to_folder = NULL, **spec_fldr = NULL;
+ CONF_S *ctmp;
+ NAMEVAL_S *f;
+#define ACT_UNKNOWN 0
+#define ACT_KILL 1
+#define ACT_MOVE 2
+#define ACT_MOVE_NOFOLDER 3
+#define ACT_STATE 4
+
+ /*
+ * We have to locate the lines which define the Filter Action and
+ * then check to see that it is set to something before allowing
+ * user to Exit.
+ */
+ action = ACT_UNKNOWN;
+ if(flags & CF_CHANGES && role_filt_ptr && PVAL(role_filt_ptr,ew)){
+ for(j = 0; (f = filter_types(j)); j++)
+ if(!strucmp(PVAL(role_filt_ptr,ew), f->name))
+ break;
+
+ switch(f ? f->value : -1){
+ case FILTER_KILL:
+ action = ACT_KILL;
+ break;
+
+ case FILTER_STATE:
+ action = ACT_STATE;
+ break;
+
+ case FILTER_FOLDER:
+ /*
+ * Check that the folder is set to something.
+ */
+
+ action = ACT_MOVE_NOFOLDER;
+ /* go to end of screen */
+ for(ctmp = (*cl);
+ ctmp && ctmp->next;
+ ctmp = next_confline(ctmp))
+ ;
+
+ /* back up to start of Filter Action */
+ for(;
+ ctmp &&
+ !(ctmp->flags & CF_STARTITEM && ctmp->var == role_filt_ptr);
+ ctmp = prev_confline(ctmp))
+ ;
+
+ /* skip back past NOSELECTs */
+ for(;
+ ctmp && (ctmp->flags & CF_NOSELECT);
+ ctmp = next_confline(ctmp))
+ ;
+
+ /* find line with new var (the Folder line) */
+ for(;
+ ctmp && (ctmp->var == role_filt_ptr);
+ ctmp = next_confline(ctmp))
+ ;
+
+ /* ok, we're finally at the Folder line */
+ if(ctmp && ctmp->var && LVAL(ctmp->var,ew)){
+ to_folder = copy_list_array(LVAL(ctmp->var,ew));
+ if(to_folder && to_folder[0])
+ action = ACT_MOVE;
+ }
+
+ break;
+
+ default:
+ dprint((1,
+ "Can't happen, role_filt_ptr set to %s\n",
+ PVAL(role_filt_ptr,ew) ? PVAL(role_filt_ptr,ew) : "?"));
+ break;
+ }
+ }
+
+ if(flags & CF_CHANGES){
+ switch(want_to((action == ACT_KILL)
+ ? _("Commit changes (\"Yes\" means matching messages will be deleted)")
+ : EXIT_PMT, 'y', 'x', h_config_undo, WT_FLUSH_IN)){
+ case 'y':
+ switch(action){
+ case ACT_KILL:
+ if((spec_fldr = get_role_specific_folder(cl)) != NULL){
+ rv = check_role_folders(spec_fldr, 0);
+ free_list_array(&spec_fldr);
+ if(rv == 2)
+ q_status_message(SM_ORDER,0,3,_("Ok, messages matching that Pattern will be deleted"));
+ }
+ else{
+ q_status_message(SM_ORDER, 0, 3,
+ _("Ok, messages matching that Pattern will be deleted"));
+ rv = 2;
+ }
+ break;
+
+ case ACT_MOVE:
+ if((spec_fldr = get_role_specific_folder(cl)) != NULL){
+ rv = check_role_folders(spec_fldr, 0);
+ free_list_array(&spec_fldr);
+ if(to_folder && rv == 2)
+ rv = check_role_folders(to_folder, 1);
+ }
+ else
+ rv = check_role_folders(to_folder, 1);
+
+ break;
+
+ case ACT_MOVE_NOFOLDER:
+ rv = 0;
+ q_status_message(SM_ORDER, 3, 5,
+ _("Set a valid Filter Action before Exiting"));
+ break;
+
+ case ACT_STATE:
+ if((spec_fldr = get_role_specific_folder(cl)) != NULL){
+ rv = check_role_folders(spec_fldr, 0);
+ free_list_array(&spec_fldr);
+ }
+ else
+ rv = 2;
+
+ break;
+
+ default:
+ rv = 2;
+ dprint((1,
+ "This can't happen, role_filt_ptr or to_folder not set\n"));
+ break;
+ }
+
+ break;
+
+ case 'n':
+ q_status_message(SM_ORDER,3,5,_("No filter changes saved"));
+ rv = 10;
+ break;
+
+ case 'x': /* ^C */
+ default :
+ q_status_message(SM_ORDER,3,5,_("Changes not yet saved"));
+ rv = 0;
+ break;
+ }
+ }
+ else
+ rv = 2;
+
+ if(to_folder)
+ free_list_array(&to_folder);
+
+ return(rv);
+}
+
+
+/*
+ * Don't allow exit unless user has set the action to something.
+ */
+int
+role_filt_text_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ int rv;
+
+ switch(cmd){
+ case MC_EXIT:
+ rv = role_filt_exitcheck(cl, flags);
+ break;
+
+ default:
+ rv = role_text_tool(ps, cmd, cl, flags);
+ break;
+ }
+
+ return(rv);
+}
+
+
+int
+role_filt_addhdr_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ int rv;
+
+ switch(cmd){
+ case MC_EXIT:
+ rv = role_filt_exitcheck(cl, flags);
+ break;
+
+ default:
+ rv = role_addhdr_tool(ps, cmd, cl, flags);
+ break;
+ }
+
+ return rv;
+}
+
+int
+role_addhdr_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ int rv;
+
+ switch(cmd){
+ case MC_ADDHDR:
+ case MC_EXIT:
+ rv = role_text_tool(ps, cmd, cl, flags);
+ break;
+
+ default:
+ rv = -1;
+ break;
+ }
+
+ return rv;
+}
+
+/*
+ * Don't allow exit unless user has set the action to something.
+ */
+int
+role_filt_radiobutton_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ int rv;
+
+ switch(cmd){
+ case MC_EXIT:
+ rv = role_filt_exitcheck(cl, flags);
+ break;
+
+ default:
+ rv = role_radiobutton_tool(ps, cmd, cl, flags);
+ break;
+ }
+
+ return(rv);
+}
+
+
+/*
+ * simple radio-button style variable handler
+ */
+int
+role_radiobutton_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ int rv = 0, i;
+ CONF_S *ctmp, *spec_ctmp = NULL;
+ NAMEVAL_S *rule;
+ char **apval;
+
+ switch(cmd){
+ case MC_CHOICE : /* set/unset feature */
+
+ /* back up to first line */
+ for(ctmp = (*cl);
+ ctmp && ctmp->prev && !(ctmp->prev->flags & CF_NOSELECT);
+ ctmp = prev_confline(ctmp))
+ ;
+
+ for(i = 0; ctmp && (!(ctmp->flags & CF_NOSELECT)
+ || (*cl)->var == role_fldr_ptr
+ || (*cl)->var == role_afrom_ptr
+ || (*cl)->var == role_filt_ptr);
+ ctmp = next_confline(ctmp), i++){
+ if(((*cl)->var == role_fldr_ptr) ||
+ ((*cl)->var == role_afrom_ptr) ||
+ ((*cl)->var == role_filt_ptr)){
+ if((((*cl)->var == role_fldr_ptr) && !pat_fldr_types(i))
+ || (((*cl)->var == role_afrom_ptr)
+ && !inabook_fldr_types(i))
+ || (((*cl)->var == role_filt_ptr) && !filter_types(i))){
+ spec_ctmp = ctmp;
+ break;
+ }
+ }
+
+ ctmp->value[1] = ' ';
+ }
+
+ /* turn on current value */
+ (*cl)->value[1] = R_SELD;
+
+ if((*cl)->var == role_fldr_ptr){
+ for(ctmp = spec_ctmp;
+ ctmp && ctmp->varnamep == spec_ctmp;
+ ctmp = next_confline(ctmp))
+ if((*cl)->varmem == FLDR_SPECIFIC)
+ ctmp->flags &= ~CF_NOSELECT;
+ else
+ ctmp->flags |= CF_NOSELECT;
+
+ rule = pat_fldr_types((*cl)->varmem);
+ }
+ else if((*cl)->var == role_afrom_ptr){
+ for(ctmp = spec_ctmp;
+ ctmp && ctmp->varnamep == spec_ctmp;
+ ctmp = next_confline(ctmp))
+ if(((*cl)->varmem == IAB_SPEC_YES)
+ || ((*cl)->varmem == IAB_SPEC_NO))
+ ctmp->flags &= ~CF_NOSELECT;
+ else
+ ctmp->flags |= CF_NOSELECT;
+
+ rule = inabook_fldr_types((*cl)->varmem);
+ }
+ else if((*cl)->var == role_filt_ptr){
+ for(ctmp = spec_ctmp;
+ ctmp && ctmp->varnamep == spec_ctmp;
+ ctmp = next_confline(ctmp))
+ if((*cl)->varmem == FILTER_FOLDER)
+ ctmp->flags &= ~CF_NOSELECT;
+ else
+ ctmp->flags |= CF_NOSELECT;
+
+ rule = filter_types((*cl)->varmem);
+ }
+ else if((*cl)->var == role_forw_ptr)
+ rule = role_forw_types((*cl)->varmem);
+ else if((*cl)->var == role_repl_ptr)
+ rule = role_repl_types((*cl)->varmem);
+ else if((*cl)->var == role_status1_ptr ||
+ (*cl)->var == role_status2_ptr ||
+ (*cl)->var == role_status3_ptr ||
+ (*cl)->var == role_status4_ptr ||
+ (*cl)->var == role_status5_ptr ||
+ (*cl)->var == role_status6_ptr ||
+ (*cl)->var == role_status7_ptr ||
+ (*cl)->var == role_status8_ptr)
+ rule = role_status_types((*cl)->varmem);
+ else if((*cl)->var == msg_state1_ptr ||
+ (*cl)->var == msg_state2_ptr ||
+ (*cl)->var == msg_state3_ptr ||
+ (*cl)->var == msg_state4_ptr)
+ rule = msg_state_types((*cl)->varmem);
+ else
+ rule = role_comp_types((*cl)->varmem);
+
+ apval = APVAL((*cl)->var, ew);
+ if(apval && *apval)
+ fs_give((void **)apval);
+
+ if(apval)
+ *apval = cpystr(rule->name);
+
+ ps->mangled_body = 1; /* BUG: redraw it all for now? */
+ rv = 1;
+
+ break;
+
+ case MC_EXIT: /* exit */
+ rv = role_text_tool(ps, cmd, cl, flags);
+ break;
+
+ default :
+ rv = -1;
+ break;
+ }
+
+ return(rv);
+}
+
+
+int
+role_sort_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ int rv = 0, i;
+ CONF_S *ctmp;
+ char **apval;
+ SortOrder def_sort;
+ int def_sort_rev;
+
+ apval = APVAL((*cl)->var, ew);
+
+ switch(cmd){
+ case MC_CHOICE : /* set/unset feature */
+
+ if((*cl)->varmem >= 0){
+ def_sort_rev = (*cl)->varmem >= (short) EndofList;
+ def_sort = (SortOrder)((*cl)->varmem - (def_sort_rev * EndofList));
+
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s", sort_name(def_sort),
+ (def_sort_rev) ? "/Reverse" : "");
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ }
+
+ if(apval){
+ if(*apval)
+ fs_give((void **)apval);
+
+ if((*cl)->varmem >= 0)
+ *apval = cpystr(tmp_20k_buf);
+ }
+
+ /* back up to first line */
+ for(ctmp = (*cl);
+ ctmp && ctmp->prev && !(ctmp->prev->flags & CF_NOSELECT);
+ ctmp = prev_confline(ctmp))
+ ;
+
+ /* turn off all values */
+ for(i = 0;
+ ctmp && !(ctmp->flags & CF_NOSELECT);
+ ctmp = next_confline(ctmp), i++)
+ ctmp->value[1] = ' ';
+
+ /* turn on current value */
+ (*cl)->value[1] = R_SELD;
+
+ ps->mangled_body = 1; /* BUG: redraw it all for now? */
+ rv = 1;
+
+ break;
+
+ case MC_EXIT: /* exit */
+ rv = role_text_tool(ps, cmd, cl, flags);
+ break;
+
+ default :
+ rv = -1;
+ break;
+ }
+
+ return(rv);
+}
+
+/*
+ * Return an allocated list of the Specific Folder list for
+ * roles, or NULL if Current Folder type is not set to
+ * to Specific Folder
+ *
+ * WARNING, the method used in obtaining the specific folder is
+ * VERY dependent on the order in which it is presented on the
+ * screen. If the Current Folder radio buttons were changed,
+ * this function would probably need to be fixed accordingly.
+ */
+char **
+get_role_specific_folder(CONF_S **cl)
+{
+ CONF_S *ctmp;
+
+ /* go to the first line */
+ for(ctmp = *cl;
+ ctmp && ctmp->prev;
+ ctmp = prev_confline(ctmp))
+ ;
+
+ /* go to the current folder radio button list */
+ while(ctmp && ctmp->var != role_fldr_ptr)
+ ctmp = next_confline(ctmp);
+
+ /* go to the specific folder button (caution) */
+ while(ctmp && ctmp->varmem != FLDR_SPECIFIC)
+ ctmp = next_confline(ctmp);
+
+ /* check if selected (assumption of format "(*)" */
+ if(ctmp && ctmp->value[1] == R_SELD){
+ /* go to next line, the start of the list */
+ ctmp = next_confline(ctmp);
+ if(LVAL(ctmp->var, ew))
+ return copy_list_array(LVAL(ctmp->var, ew));
+ else{
+ char **ltmp;
+
+ /*
+ * Need to allocate empty string so as not to confuse it
+ * with the possibility that Specific Folder is not selected.
+ */
+ ltmp = (char **) fs_get(2 * sizeof(char *));
+ ltmp[0] = cpystr("");
+ ltmp[1] = NULL;
+ return(ltmp);
+ }
+ }
+ else
+ return NULL;
+}
+
+
+/*
+ */
+int
+role_litsig_text_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ int rv;
+
+ switch(cmd){
+ case MC_ADD :
+ case MC_EDIT :
+ rv = litsig_text_tool(ps, cmd, cl, flags);
+ if(rv)
+ calculate_inick_stuff(ps);
+
+ break;
+
+ default :
+ rv = role_text_tool(ps, cmd, cl, flags);
+ break;
+ }
+
+ return(rv);
+}
+
+
+/*
+ */
+int
+role_cstm_text_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ int rv;
+
+ switch(cmd){
+ case MC_EXIT :
+ rv = role_text_tool(ps, cmd, cl, flags);
+ break;
+
+ default :
+ rv = text_tool(ps, cmd, cl, flags);
+ if(rv == 1 && (*cl)->var){
+ char **lval;
+
+ lval = LVAL((*cl)->var, ew);
+ if(lval && lval[(*cl)->varmem] &&
+ ((!struncmp(lval[(*cl)->varmem],"from",4) &&
+ (lval[(*cl)->varmem][4] == ':' ||
+ lval[(*cl)->varmem][4] == '\0')) ||
+ (!struncmp(lval[(*cl)->varmem],"reply-to",8) &&
+ (lval[(*cl)->varmem][8] == ':' ||
+ lval[(*cl)->varmem][8] == '\0'))))
+ q_status_message1(SM_ORDER|SM_DING, 5, 7,
+ "Use \"Set %s\" instead, Change ignored",
+ !struncmp(lval[(*cl)->varmem],"from",4)
+ ? "From" : "Reply-To");
+ }
+
+ break;
+ }
+
+ return(rv);
+}
+
+
+/*
+ */
+int
+role_text_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ OPT_SCREEN_S *saved_screen;
+ int rv = -1, oeflags, len, sig, r, i, cancel = 0;
+ char *file, *err, title[20], *newfile, *lc, *addr, *fldr = NULL, *tmpfldr;
+ char dir2[MAXPATH+1], pdir[MAXPATH+1], *p;
+ char full_filename[MAXPATH+1], filename[MAXPATH+1];
+ char tmp[MAXPATH+1], **spec_fldr, **apval;
+ EARB_S *earb, *ea, *eaprev;
+ CONF_S *ctmp, *ctmpb, *newcp, *ctend;
+ HelpType help;
+
+ switch(cmd){
+ case MC_EXIT :
+ if(flags & CF_CHANGES){
+ switch(want_to(EXIT_PMT, 'y', 'x', h_config_role_undo, WT_FLUSH_IN)){
+ case 'y':
+ if((spec_fldr = get_role_specific_folder(cl)) != NULL){
+ rv = check_role_folders(spec_fldr, 0);
+ free_list_array(&spec_fldr);
+ }
+ else
+ rv = 2;
+ break;
+
+ case 'n':
+ q_status_message(SM_ORDER,3,5,_("No changes saved"));
+ rv = 10;
+ break;
+
+ case 'x': /* ^C */
+ q_status_message(SM_ORDER,3,5,_("Changes not yet saved"));
+ rv = 0;
+ break;
+ }
+ }
+ else
+ rv = 2;
+
+ break;
+
+ case MC_NOT : /* toggle between !matching and matching */
+ ctmp = (*cl)->varnamep;
+ if(ctmp->varname && ctmp->var && ctmp->var->name){
+ if(!strncmp(ctmp->varname, NOT, NOTLEN) &&
+ !strncmp(ctmp->var->name, NOT, NOTLEN)){
+ rplstr(ctmp->var->name, strlen(ctmp->var->name)+1, NOTLEN, "");
+ rplstr(ctmp->varname, strlen(ctmp->varname)+1, NOTLEN, "");
+ strncpy(ctmp->varname+strlen(ctmp->varname)-1,
+ repeat_char(NOTLEN, ' '), NOTLEN+1);
+ strncat(ctmp->varname, "=", NOTLEN);
+ }
+ else{
+ rplstr(ctmp->var->name, strlen(ctmp->var->name)+NOTLEN+1, 0, NOT);
+ strncpy(ctmp->varname+strlen(ctmp->varname)-1-NOTLEN, "=", NOTLEN);
+ rplstr(ctmp->varname, strlen(ctmp->varname)+NOTLEN+1, 0, NOT);
+ }
+
+ rv = 1;
+ }
+
+ break;
+
+ case MC_CHOICE : /* Choose a file */
+ /*
+ * In signature_path we read signature files relative to the pinerc
+ * dir, so if user selects one that is in there we'll make it
+ * relative instead of absolute, so it looks nicer.
+ */
+ pdir[0] = '\0';
+ if(ps_global->VAR_OPER_DIR){
+ strncpy(pdir, ps_global->VAR_OPER_DIR, MAXPATH);
+ pdir[MAXPATH] = '\0';
+ len = strlen(pdir) + 1;
+ }
+ else if((lc = last_cmpnt(ps_global->pinerc)) != NULL){
+ strncpy(pdir, ps_global->pinerc, MIN(MAXPATH,lc-ps_global->pinerc));
+ pdir[MIN(MAXPATH, lc-ps_global->pinerc)] = '\0';
+ len = strlen(pdir);
+ }
+
+ strncpy(title, "CHOOSE A", 15);
+ strncpy(dir2, pdir, MAXPATH);
+
+ filename[0] = '\0';
+ build_path(full_filename, dir2, filename, sizeof(full_filename));
+
+ r = file_lister(title, dir2, sizeof(dir2), filename, sizeof(filename), TRUE, FB_READ);
+ ps->mangled_screen = 1;
+
+ if(r == 1){
+ build_path(full_filename, dir2, filename, sizeof(full_filename));
+ removing_leading_and_trailing_white_space(full_filename);
+ if(!strncmp(full_filename, pdir, strlen(pdir)))
+ newfile = cpystr(full_filename + len);
+ else
+ newfile = cpystr(full_filename);
+
+ apval = APVAL((*cl)->var, ew);
+ if(apval && *apval)
+ fs_give((void **)apval);
+
+ if(apval)
+ *apval = newfile;
+
+ if((*cl)->value)
+ fs_give((void **)&((*cl)->value));
+
+ (*cl)->value = pretty_value(ps, *cl);
+ rv = 1;
+ }
+ else
+ rv = 0;
+
+ break;
+
+ case MC_CHOICEB : /* Choose Addresses, no full names */
+ addr = addr_book_multaddr_nf();
+ ps->mangled_screen = 1;
+ if(addr && (*cl)->var && (*cl)->var->is_list){
+ char **ltmp, *tmp;
+ int i;
+
+ i = 0;
+ for(tmp = addr; *tmp; tmp++)
+ if(*tmp == ',')
+ i++; /* conservative count of ,'s */
+
+ ltmp = parse_list(addr, i + 1, PL_COMMAQUOTE, NULL);
+ fs_give((void **) &addr);
+
+ if(ltmp && ltmp[0])
+ config_add_list(ps, cl, ltmp, NULL, 0);
+
+ if(ltmp)
+ fs_give((void **) &ltmp);
+
+ if((*cl)->value)
+ fs_give((void **)&((*cl)->value));
+
+ (*cl)->value = pretty_value(ps, *cl);
+ rv = 1;
+ }
+ else
+ rv = 0;
+
+ break;
+
+ case MC_CHOICEC : /* Choose an Address, no full name */
+ addr = addr_book_oneaddr();
+ ps->mangled_screen = 1;
+ if(addr){
+ apval = APVAL((*cl)->var, ew);
+ if(apval && *apval) /* replace current value */
+ fs_give((void **)apval);
+
+ if(apval)
+ *apval = addr;
+
+ if((*cl)->value)
+ fs_give((void **)&((*cl)->value));
+
+ (*cl)->value = pretty_value(ps, *cl);
+ rv = 1;
+ }
+ else
+ rv = 0;
+
+ break;
+
+ case MC_CHOICED : /* Choose a Folder */
+ case MC_CHOICEE :
+ saved_screen = opt_screen;
+ if(cmd == MC_CHOICED)
+ tmpfldr = folder_for_config(FOR_PATTERN);
+ else
+ tmpfldr = folder_for_config(0);
+
+ if(tmpfldr){
+ fldr = add_comma_escapes(tmpfldr);
+ fs_give((void**) &tmpfldr);
+ }
+
+ opt_screen = saved_screen;
+
+ ps->mangled_screen = 1;
+ if(fldr && *fldr && (*cl)->var && (*cl)->var->is_list){
+ char **ltmp;
+
+ ltmp = (char **) fs_get(2 * sizeof(char *));
+ ltmp[0] = fldr;
+ ltmp[1] = NULL;
+ fldr = NULL;
+
+ if(ltmp && ltmp[0])
+ config_add_list(ps, cl, ltmp, NULL, 0);
+
+ if(ltmp)
+ fs_give((void **) &ltmp);
+
+ if((*cl)->value)
+ fs_give((void **) &((*cl)->value));
+
+ (*cl)->value = pretty_value(ps, *cl);
+ rv = 1;
+ }
+ else if(fldr && *fldr && (*cl)->var && !(*cl)->var->is_list){
+ apval = APVAL((*cl)->var, ew);
+ if(apval && *apval) /* replace current value */
+ fs_give((void **)apval);
+
+ if(apval){
+ *apval = fldr;
+ fldr = NULL;
+ }
+
+ if((*cl)->value)
+ fs_give((void **) &((*cl)->value));
+
+ (*cl)->value = pretty_value(ps, *cl);
+ rv = 1;
+ }
+ else
+ rv = 0;
+
+ if(fldr)
+ fs_give((void **) &fldr);
+
+ break;
+
+ case MC_EDITFILE :
+ file = ((*cl)->var && PVAL((*cl)->var, ew))
+ ? cpystr(PVAL((*cl)->var, ew)) : NULL;
+ if(file)
+ removing_leading_and_trailing_white_space(file);
+
+ sig = (srchstr((*cl)->varname, "signature") != NULL);
+ if(!file || !*file){
+ err = (char *)fs_get(100 * sizeof(char));
+ snprintf(err, 100, "No %s file defined. First define a file name.",
+ sig ? "signature" : "template");
+ err[100-1] = '\0';
+ }
+ else{
+ if(file[len=(strlen(file)-1)] == '|')
+ file[len] = '\0';
+
+ snprintf(title, sizeof(title), "%s EDITOR", sig ? "SIGNATURE" : "TEMPLATE");
+ title[sizeof(title)-1] = '\0';
+ err = signature_edit(file, title);
+ }
+
+ fs_give((void **)&file);
+ if(err){
+ q_status_message1(SM_ORDER, 3, 5, "%s", err);
+ fs_give((void **)&err);
+ }
+
+ rv = 0;
+ ps->mangled_screen = 1;
+ break;
+
+ /* Add an arbitrary header to this role */
+ case MC_ADDHDR :
+ rv = 0;
+ /* make earb point to last one */
+ for(earb = *(*cl)->d.earb; earb && earb->next; earb = earb->next)
+ ;
+
+ /* Add new one to end of list */
+ ea = (EARB_S *)fs_get(sizeof(*ea));
+ memset((void *)ea, 0, sizeof(*ea));
+ ea->v = (struct variable *)fs_get(sizeof(struct variable));
+ memset((void *)ea->v, 0, sizeof(struct variable));
+ ea->a = (ARBHDR_S *)fs_get(sizeof(ARBHDR_S));
+ memset((void *)ea->a, 0, sizeof(ARBHDR_S));
+
+ /* get new header field name */
+ help = NO_HELP;
+ tmp[0] = '\0';
+ while(1){
+ i = optionally_enter(tmp, -FOOTER_ROWS(ps), 0, sizeof(tmp),
+ _("Enter the name of the header field to be added: "),
+ NULL, help, NULL);
+ if(i == 0)
+ break;
+ else if(i == 1){
+ cmd_cancelled("eXtraHdr");
+ cancel = 1;
+ break;
+ }
+ else if(i == 3){
+ help = help == NO_HELP ? h_config_add_pat_hdr : NO_HELP;
+ continue;
+ }
+ else
+ break;
+ }
+
+ ps->mangled_footer = 1;
+
+ removing_leading_and_trailing_white_space(tmp);
+ if(tmp[strlen(tmp)-1] == ':') /* remove trailing colon */
+ tmp[strlen(tmp)-1] = '\0';
+
+ removing_trailing_white_space(tmp);
+
+ if(cancel || !tmp[0])
+ break;
+
+ tmp[0] = islower((unsigned char)tmp[0]) ? toupper((unsigned char)tmp[0])
+ : tmp[0];
+ ea->a->field = cpystr(tmp);
+
+ if(earb)
+ earb->next = ea;
+ else
+ *((*cl)->d.earb) = ea;
+
+ /* go to first line */
+ for(ctmp = *cl; prev_confline(ctmp); ctmp = prev_confline(ctmp))
+ ;
+
+ /*
+ * Go to the Add Extra Headers line. We will put the
+ * new header before this line.
+ */
+ for(; ctmp; ctmp = next_confline(ctmp))
+ if(ctmp->value && !strcmp(ctmp->value, ADDXHDRS))
+ break;
+
+ /* move back one */
+ if(ctmp)
+ ctmp = prev_confline(ctmp);
+
+ /*
+ * Add a new line after this point, which is after the last
+ * extra header (if any) or after the Participant pattern, and
+ * before the Add Extra Headers placeholder line.
+ */
+ p = (char *) fs_get(strlen(tmp) + strlen(" pattern") + 1);
+ snprintf(p, strlen(tmp) + strlen(" pattern") + 1, "%s pattern", tmp);
+ p[strlen(tmp) + strlen(" pattern") + 1 - 1] = '\0';
+ setup_dummy_pattern_var(ea->v, p, NULL);
+ fs_give((void **) &p);
+
+ /* find what indent should be */
+ if(ctmp && ctmp->varnamep && ctmp->varnamep->varname)
+ i = MIN(MAX(utf8_width(ctmp->varnamep->varname) + 1, 3), 200);
+ else
+ i = 20;
+
+ setup_role_pat(ps, &ctmp, ea->v, h_config_role_arbpat,
+ ARB_HELP, &config_role_xtrahdr_keymenu,
+ ctmp->prev->tool, ctmp->prev->d.earb, i);
+
+ /*
+ * move current line to new line
+ */
+
+ newcp = ctmp;
+
+ /* check if new line comes before the top of the screen */
+ ctmpb = (opt_screen && opt_screen->top_line)
+ ? opt_screen->top_line->prev : NULL;
+ for(; ctmpb; ctmpb = prev_confline(ctmpb))
+ if(ctmpb == newcp)
+ break;
+ /*
+ * Keep the right lines visible.
+ * The if triggers if the new line is off the top of the screen, and
+ * it makes the new line be the top line.
+ * The else counts how many lines down the screen the new line is.
+ */
+
+ i = 0;
+ if(ctmpb == newcp)
+ opt_screen->top_line = newcp;
+ else{
+ for(ctmp = opt_screen->top_line; ctmp && ctmp != newcp;
+ i++, ctmp = next_confline(ctmp))
+ ;
+ }
+
+ if(i >= BODY_LINES(ps)){ /* new line is off screen */
+ /* move top line down this far */
+ i = i + 1 - BODY_LINES(ps);
+ for(ctmp = opt_screen->top_line;
+ i > 0;
+ i--, ctmp = next_confline(ctmp))
+ ;
+
+ opt_screen->top_line = ctmp;
+ }
+
+ *cl = newcp;
+
+ ps->mangled_screen = 1;
+ rv = 1;
+ break;
+
+ /* Delete an arbitrary header from this role */
+ case MC_DELHDR :
+ /*
+ * Find this one in earb list. We don't have a good way to locate
+ * it so we match the ea->v->name with ctmp->varname.
+ */
+ rv = 0;
+ eaprev = NULL;
+ for(ea = *(*cl)->d.earb; ea; ea = ea->next){
+ if((*cl)->varnamep && (*cl)->varnamep->varname
+ && ea->v && ea->v->name
+ && !strncmp((*cl)->varnamep->varname,
+ ea->v->name, strlen(ea->v->name)))
+ break;
+
+ eaprev = ea;
+ }
+
+ snprintf(tmp, sizeof(tmp), _("Really remove \"%s\" pattern from this rule"),
+ (ea && ea->a && ea->a->field) ? ea->a->field : "this");
+ tmp[sizeof(tmp)-1] = '\0';
+ if(want_to(tmp, 'y', 'n', NO_HELP, WT_NORM) != 'y'){
+ cmd_cancelled("RemoveHdr");
+ return(rv);
+ }
+
+ /* delete the earb element from the list */
+ if(ea){
+ if(eaprev)
+ eaprev->next = ea->next;
+ else
+ *(*cl)->d.earb = ea->next;
+
+ ea->next = NULL;
+ free_earb(&ea);
+ }
+
+ /* remember start of deleted header */
+ ctmp = (*cl && (*cl)->varnamep) ? (*cl)->varnamep : NULL;
+
+ /* and end of deleted header */
+ for(ctend = *cl; ctend; ctend = next_confline(ctend))
+ if(!ctend->next || ctend->next->varnamep != ctmp)
+ break;
+
+ /* check if top line is one we're deleting */
+ for(ctmpb = ctmp; ctmpb; ctmpb = next_confline(ctmpb)){
+ if(ctmpb == opt_screen->top_line)
+ break;
+
+ if(ctmpb == (*cl))
+ break;
+ }
+
+ if(ctmpb == opt_screen->top_line)
+ opt_screen->top_line = ctend ? ctend->next : NULL;
+
+ /* move current line after this header */
+ *cl = ctend ? ctend->next : NULL;
+
+ /* remove deleted header lines */
+ if(ctmp && ctend){
+ /* remove from linked list */
+ if(ctmp->prev)
+ ctmp->prev->next = ctend->next;
+
+ if(ctend->next)
+ ctend->next->prev = ctmp->prev;
+
+ /* free memory */
+ ctmp->prev = ctend->next = NULL;
+ free_conflines(&ctmp);
+ }
+
+ ps->mangled_body = 1;
+ rv = 1;
+ break;
+
+ default :
+ if(((*cl)->var == scorei_pat_global_ptr
+ || (*cl)->var == age_pat_global_ptr
+ || (*cl)->var == size_pat_global_ptr
+ || (*cl)->var == cati_global_ptr)
+ &&
+ (cmd == MC_EDIT || (cmd == MC_ADD && !PVAL((*cl)->var, ew)))){
+ char prompt[60];
+
+ rv = 0;
+ snprintf(prompt, sizeof(prompt), "%s the interval : ",
+ PVAL((*cl)->var, ew) ? "Change" : "Enter");
+ prompt[sizeof(prompt)-1] = '\0';
+
+ ps->mangled_footer = 1;
+ help = NO_HELP;
+ tmp[0] = '\0';
+ snprintf(tmp, sizeof(tmp),
+ "%s", PVAL((*cl)->var, ew) ? PVAL((*cl)->var, ew) : "");
+ tmp[sizeof(tmp)-1] = '\0';
+ while(1){
+ oeflags = OE_APPEND_CURRENT;
+ i = optionally_enter(tmp, -FOOTER_ROWS(ps), 0, sizeof(tmp),
+ prompt, NULL, help, &oeflags);
+ if(i == 0){
+ rv = ps->mangled_body = 1;
+ apval = APVAL((*cl)->var, ew);
+ if(apval && *apval)
+ fs_give((void **)apval);
+
+ if(apval && tmp[0])
+ *apval = cpystr(tmp);
+
+ fix_side_effects(ps, (*cl)->var, 0);
+ if((*cl)->value)
+ fs_give((void **)&(*cl)->value);
+
+ (*cl)->value = pretty_value(ps, *cl);
+ }
+ else if(i == 1)
+ cmd_cancelled(cmd == MC_ADD ? "Add" : "Change");
+ else if(i == 3){
+ help = help == NO_HELP ? h_config_edit_scorei : NO_HELP;
+ continue;
+ }
+ else if(i == 4)
+ continue;
+
+ break;
+ }
+ }
+ else{
+ if(cmd == MC_ADD && (*cl)->var && !(*cl)->var->is_list)
+ cmd = MC_EDIT;
+
+ rv = text_toolit(ps, cmd, cl, flags, 1);
+
+ /* make sure the earb pointers are set */
+ for(ctmp = (*cl)->varnamep;
+ ctmp->next && ctmp->next->var == ctmp->var;
+ ctmp = next_confline(ctmp))
+ ctmp->next->d.earb = ctmp->d.earb;
+ }
+
+ break;
+ }
+
+ /*
+ * If the inherit nickname changed, we have to re-calculate the
+ * global_vals and values for the action variables.
+ * We may have to do the same if literal sig changed, too.
+ */
+ if(rv)
+ calculate_inick_stuff(ps);
+
+ return(rv);
+}
+
+
+/*
+ */
+int
+role_text_tool_inick(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ int rv = -1;
+ char **apval;
+
+ switch(cmd){
+ case MC_EXIT :
+ rv = role_text_tool(ps, cmd, cl, flags);
+ break;
+
+ case MC_CHOICE : /* Choose a role nickname */
+ {void (*prev_screen)(struct pine *) = ps->prev_screen,
+ (*redraw)(void) = ps->redrawer;
+ OPT_SCREEN_S *saved_screen;
+ ACTION_S *role;
+
+ ps->redrawer = NULL;
+ ps->next_screen = SCREEN_FUN_NULL;
+
+ saved_screen = opt_screen;
+ if(role_select_screen(ps, &role, 0) == 0){
+ apval = APVAL((*cl)->var, ew);
+ if(apval && *apval)
+ fs_give((void **)apval);
+
+ if(apval)
+ *apval = (role && role->nick) ? cpystr(role->nick) : NULL;
+
+ if((*cl)->value)
+ fs_give((void **)&((*cl)->value));
+
+ (*cl)->value = pretty_value(ps, *cl);
+ rv = 1;
+ }
+ else{
+ ps->next_screen = prev_screen;
+ ps->redrawer = redraw;
+ rv = 0;
+ }
+
+ opt_screen = saved_screen;
+ }
+
+ ps->mangled_screen = 1;
+ break;
+
+ case MC_EDIT :
+ case MC_ADD :
+ case MC_DELETE :
+ rv = text_tool(ps, cmd, cl, flags);
+ ps->mangled_screen = 1;
+ break;
+
+ default :
+ rv = text_tool(ps, cmd, cl, flags);
+ break;
+ }
+
+ /*
+ * If the inherit nickname changed, we have to re-calculate the
+ * global_vals and values for the action variables.
+ * We may have to do the same if literal sig changed, too.
+ */
+ if(rv)
+ calculate_inick_stuff(ps);
+
+ return(rv);
+}
+
+
+/*
+ */
+int
+role_text_tool_kword(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ int i, j, rv = -1;
+ char **lval;
+
+ switch(cmd){
+ case MC_CHOICE : /* Choose keywords from list and add them */
+ {void (*prev_screen)(struct pine *) = ps->prev_screen,
+ (*redraw)(void) = ps->redrawer;
+ OPT_SCREEN_S *saved_screen;
+ char *esc;
+ char **kw;
+
+ ps->redrawer = NULL;
+ ps->next_screen = SCREEN_FUN_NULL;
+
+ saved_screen = opt_screen;
+
+ if((kw=choose_list_of_keywords()) != NULL){
+ for(i = 0; kw[i]; i++){
+ esc = add_roletake_escapes(kw[i]);
+ fs_give((void **) &kw[i]);
+ kw[i] = esc;
+ }
+
+ /* eliminate duplicates before the add */
+ lval = LVAL((*cl)->var, ew);
+ if(lval && *lval){
+ for(i = 0; kw[i]; ){
+ /* if kw[i] is a dup, eliminate it */
+ for(j = 0; lval[j]; j++)
+ if(!strcmp(kw[i], lval[j]))
+ break;
+
+ if(lval[j]){ /* it is a dup */
+ for(j = i; kw[j]; j++)
+ kw[j] = kw[j+1];
+ }
+ else
+ i++;
+ }
+ }
+
+ if(kw[0])
+ config_add_list(ps, cl, kw, NULL, 0);
+
+ fs_give((void **) &kw);
+
+ if((*cl)->value)
+ fs_give((void **) &((*cl)->value));
+
+ (*cl)->value = pretty_value(ps, *cl);
+ rv = 1;
+ }
+ else{
+ ps->next_screen = prev_screen;
+ ps->redrawer = redraw;
+ rv = 0;
+ }
+
+ opt_screen = saved_screen;
+ }
+
+ ps->mangled_screen = 1;
+ break;
+
+ case MC_EDIT :
+ case MC_ADD :
+ case MC_DELETE :
+ case MC_NOT :
+ rv = role_text_tool(ps, cmd, cl, flags);
+ ps->mangled_screen = 1;
+ break;
+
+ case MC_EXIT :
+ default :
+ rv = role_text_tool(ps, cmd, cl, flags);
+ break;
+ }
+
+ return(rv);
+}
+
+
+/*
+ */
+int
+role_text_tool_charset(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ int i, j, rv = -1;
+ char **lval;
+
+ switch(cmd){
+ case MC_CHOICE : /* Choose charsets from list and add them */
+ {void (*prev_screen)(struct pine *) = ps->prev_screen,
+ (*redraw)(void) = ps->redrawer;
+ OPT_SCREEN_S *saved_screen;
+ char *esc;
+ char **kw;
+
+ ps->redrawer = NULL;
+ ps->next_screen = SCREEN_FUN_NULL;
+
+ saved_screen = opt_screen;
+
+ if((kw=choose_list_of_charsets()) != NULL){
+ for(i = 0; kw[i]; i++){
+ esc = add_roletake_escapes(kw[i]);
+ fs_give((void **) &kw[i]);
+ kw[i] = esc;
+ }
+
+ /* eliminate duplicates before the add */
+ lval = LVAL((*cl)->var, ew);
+ if(lval && *lval){
+ for(i = 0; kw[i]; ){
+ /* if kw[i] is a dup, eliminate it */
+ for(j = 0; lval[j]; j++)
+ if(!strcmp(kw[i], lval[j]))
+ break;
+
+ if(lval[j]){ /* it is a dup */
+ for(j = i; kw[j]; j++)
+ kw[j] = kw[j+1];
+ }
+ else
+ i++;
+ }
+ }
+
+ if(kw[0])
+ config_add_list(ps, cl, kw, NULL, 0);
+
+ fs_give((void **) &kw);
+
+ if((*cl)->value)
+ fs_give((void **) &((*cl)->value));
+
+ (*cl)->value = pretty_value(ps, *cl);
+ rv = 1;
+ }
+ else{
+ ps->next_screen = prev_screen;
+ ps->redrawer = redraw;
+ rv = 0;
+ }
+
+ opt_screen = saved_screen;
+ }
+
+ ps->mangled_screen = 1;
+ break;
+
+ case MC_EDIT :
+ case MC_ADD :
+ case MC_DELETE :
+ case MC_NOT :
+ rv = role_text_tool(ps, cmd, cl, flags);
+ ps->mangled_screen = 1;
+ break;
+
+ case MC_EXIT :
+ default :
+ rv = role_text_tool(ps, cmd, cl, flags);
+ break;
+ }
+
+ return(rv);
+}
+
+
+/*
+ * Choose an address book nickname
+ */
+int
+role_text_tool_afrom(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ int rv = -1;
+
+ switch(cmd){
+ case MC_EXIT :
+ rv = role_text_tool(ps, cmd, cl, flags);
+ break;
+
+ case MC_CHOICE : /* Choose an addressbook */
+ {OPT_SCREEN_S *saved_screen;
+ char *abook = NULL, *abookesc = NULL;
+
+ ps->redrawer = NULL;
+ ps->next_screen = SCREEN_FUN_NULL;
+ saved_screen = opt_screen;
+
+ abook = abook_select_screen(ps);
+ if(abook){
+ abookesc = add_comma_escapes(abook);
+ fs_give((void**) &abook);
+ }
+
+ ps->mangled_screen = 1;
+ if(abookesc && *abookesc && (*cl)->var && (*cl)->var->is_list){
+ char **ltmp;
+
+ ltmp = (char **) fs_get(2 * sizeof(char *));
+ ltmp[0] = abookesc;
+ ltmp[1] = NULL;
+ abookesc = NULL;
+
+ if(ltmp && ltmp[0])
+ config_add_list(ps, cl, ltmp, NULL, 0);
+
+ if(ltmp)
+ fs_give((void **) &ltmp);
+
+ if((*cl)->value)
+ fs_give((void **) &((*cl)->value));
+
+ (*cl)->value = pretty_value(ps, *cl);
+ rv = 1;
+ }
+ else
+ rv = 0;
+
+ if(abookesc)
+ fs_give((void **) &abookesc);
+
+ opt_screen = saved_screen;
+ }
+
+ ps->mangled_screen = 1;
+ break;
+
+ default :
+ rv = text_tool(ps, cmd, cl, flags);
+ ps->mangled_screen = 1;
+ break;
+ }
+
+ return(rv);
+}
+
+
+/*
+ * Args fmt -- a printf style fmt string with a single %s
+ * buf -- place to put result, assumed large enough (strlen(fmt)+11)
+ * rflags -- controls what goes in buf
+ *
+ * Returns -- pointer to buf
+ */
+char *
+role_type_print(char *buf, size_t buflen, char *fmt, long int rflags)
+{
+#define CASE_MIXED 1
+#define CASE_UPPER 2
+#define CASE_LOWER 3
+ int cas = CASE_UPPER;
+ int prev_word_is_a = 0;
+ char *q, *p;
+
+ /* find %sRule to see what case */
+ if((p = srchstr(fmt, "%srule")) != NULL){
+ if(p[2] == 'R'){
+ if(p[3] == 'U')
+ cas = CASE_UPPER;
+ else
+ cas = CASE_MIXED;
+ }
+ else
+ cas = CASE_LOWER;
+
+ if(p-3 >= fmt &&
+ p[-1] == SPACE &&
+ (p[-2] == 'a' || p[-2] == 'A')
+ && p[-3] == SPACE)
+ prev_word_is_a++;
+ }
+
+ if(cas == CASE_UPPER)
+ q = (rflags & ROLE_DO_INCOLS) ? "INDEX COLOR " :
+ (rflags & ROLE_DO_FILTER) ? "FILTERING " :
+ (rflags & ROLE_DO_SCORES) ? "SCORING " :
+ (rflags & ROLE_DO_OTHER) ? "OTHER " :
+ (rflags & ROLE_DO_SRCH) ? "SEARCH " :
+ (rflags & ROLE_DO_ROLES) ? "ROLE " : "";
+ else if(cas == CASE_LOWER)
+ q = (rflags & ROLE_DO_INCOLS) ? "index color " :
+ (rflags & ROLE_DO_FILTER) ? "filtering " :
+ (rflags & ROLE_DO_SCORES) ? "scoring " :
+ (rflags & ROLE_DO_OTHER) ? "other " :
+ (rflags & ROLE_DO_OTHER) ? "search " :
+ (rflags & ROLE_DO_ROLES) ? "role " : "";
+ else
+ q = (rflags & ROLE_DO_INCOLS) ? "Index Color " :
+ (rflags & ROLE_DO_FILTER) ? "Filtering " :
+ (rflags & ROLE_DO_SCORES) ? "Scoring " :
+ (rflags & ROLE_DO_OTHER) ? "Other " :
+ (rflags & ROLE_DO_OTHER) ? "Search " :
+ (rflags & ROLE_DO_ROLES) ? "Role " : "";
+
+ /* it ain't right to say "a index" */
+ if(prev_word_is_a && !struncmp(q, "index", 5))
+ q += 6;
+
+ snprintf(buf, buflen, fmt, q);
+ buf[buflen-1] = '\0';
+ return(buf);
+}
+
+
+/*
+ * filter option list manipulation tool
+ *
+ *
+ * returns: -1 on unrecognized cmd, 0 if no change, 1 if change
+ */
+int
+feat_checkbox_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ int rv = 0;
+
+ switch(cmd){
+ case MC_TOGGLE: /* mark/unmark option */
+ rv = 1;
+ toggle_feat_option_bit(ps, (*cl)->varmem, (*cl)->var, (*cl)->value);
+ break;
+
+ case MC_EXIT: /* exit */
+ rv = role_filt_exitcheck(cl, flags);
+ break;
+
+ default :
+ rv = -1;
+ break;
+ }
+
+ return(rv);
+}
+
+
+void
+toggle_feat_option_bit(struct pine *ps, int index, struct variable *var, char *value)
+{
+ NAMEVAL_S *f;
+
+ f = feat_feature_list(index);
+
+ /* flip the bit */
+ if(bitnset(f->value, feat_option_list))
+ clrbitn(f->value, feat_option_list);
+ else
+ setbitn(f->value, feat_option_list);
+
+ if(value)
+ value[1] = bitnset(f->value, feat_option_list) ? 'X' : ' ';
+}
+
+
+NAMEVAL_S *
+feat_feature_list(int index)
+{
+ static NAMEVAL_S opt_feat_list[] = {
+ {"use-date-header-for-age", NULL, FEAT_SENTDATE},
+ {"move-only-if-not-deleted", NULL, FEAT_IFNOTDEL},
+ {"dont-stop-even-if-rule-matches", NULL, FEAT_NONTERM}
+ };
+
+ return((index >= 0 &&
+ index < (sizeof(opt_feat_list)/sizeof(opt_feat_list[0])))
+ ? &opt_feat_list[index] : NULL);
+}
+
+
+/*
+ * address type list manipulation tool
+ *
+ *
+ * returns: -1 on unrecognized cmd, 0 if no change, 1 if change
+ */
+int
+inabook_checkbox_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ int rv = 0;
+
+ switch(cmd){
+ case MC_TOGGLE: /* mark/unmark option */
+ rv = 1;
+ toggle_inabook_type_bit(ps, (*cl)->varmem, (*cl)->var, (*cl)->value);
+ break;
+
+ case MC_EXIT: /* exit */
+ rv = role_filt_exitcheck(cl, flags);
+ break;
+
+ default :
+ rv = -1;
+ break;
+ }
+
+ return(rv);
+}
+
+
+void
+toggle_inabook_type_bit(struct pine *ps, int index, struct variable *var, char *value)
+{
+ NAMEVAL_S *f;
+
+ f = inabook_feature_list(index);
+
+ /* flip the bit */
+ if(bitnset(f->value, inabook_type_list))
+ clrbitn(f->value, inabook_type_list);
+ else
+ setbitn(f->value, inabook_type_list);
+
+ if(value)
+ value[1] = bitnset(f->value, inabook_type_list) ? 'X' : ' ';
+}
+
+
+NAMEVAL_S *
+inabook_feature_list(int index)
+{
+ static NAMEVAL_S inabook_feat_list[] = {
+ {"From", NULL, INABOOK_FROM},
+ {"Reply-To", NULL, INABOOK_REPLYTO},
+ {"Sender", NULL, INABOOK_SENDER},
+ {"To", NULL, INABOOK_TO},
+ {"Cc", NULL, INABOOK_CC}
+ };
+
+ return((index >= 0 &&
+ index < (sizeof(inabook_feat_list)/sizeof(inabook_feat_list[0])))
+ ? &inabook_feat_list[index] : NULL);
+}
diff --git a/alpine/roleconf.h b/alpine/roleconf.h
new file mode 100644
index 00000000..8e0bb7b1
--- /dev/null
+++ b/alpine/roleconf.h
@@ -0,0 +1,33 @@
+/*
+ * $Id: roleconf.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_ROLECONF_INCLUDED
+#define PINE_ROLECONF_INCLUDED
+
+
+#include "conftype.h"
+#include "../pith/state.h"
+#include "../pith/pattern.h"
+#include "../pith/msgno.h"
+
+
+/* exported protoypes */
+int role_select_screen(struct pine *, ACTION_S **, int);
+void role_config_screen(struct pine *, long, int);
+void role_take(struct pine *, MSGNO_S *, int);
+int role_radiobutton_tool(struct pine *, int, CONF_S **, unsigned);
+
+
+#endif /* PINE_ROLECONF_INCLUDED */
diff --git a/alpine/rpdump.c b/alpine/rpdump.c
new file mode 100644
index 00000000..d6b10f54
--- /dev/null
+++ b/alpine/rpdump.c
@@ -0,0 +1,818 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: rpdump.c 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "headers.h"
+#include "radio.h" /* OE_PASSWD */
+#include "../pith/util.h" /* IS_REMOTE() */
+#include "../pith/remote.h" /* REMOTE_ABOOK_SUBTYPE... */
+
+
+typedef enum {Pinerc, Abook, Sig, Smime, NotSet} RemoteType;
+
+
+int parse_args(int, char **, int *, char **, char **);
+RemoteType check_for_header_msg(MAILSTREAM *);
+char *ptype(RemoteType);
+char *spechdr(RemoteType);
+char *err_desc(int);
+int opt_enter(char *, int, char *, int *);
+char *last_cmpnt(char *);
+int wantto(char *, int, int);
+
+
+char *ustr = "usage: %s [-f] -l Local_file -r Remote_folder\n";
+int noshow_error = 0;
+
+/* look for my_timer_period in pico directory for an explanation */
+int my_timer_period = ((IDLE_TIMEOUT + 1)*1000);
+
+
+#ifdef _WINDOWS
+
+#undef main
+
+app_main (argc, argv)
+ int argc;
+ char argv[];
+{
+}
+
+#endif /* _WINDOWS */
+
+
+/*
+ * rpdump [-f] -l Local_file -r Remote_folder
+ *
+ * -f (skip check for special header)
+ *
+ * Note: We're not worrying about memory leaks.
+ */
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ MAILSTREAM *stream = NULL;
+ FILE *fp;
+ int usage = 0;
+ char buf[10000];
+ char *local = NULL, *remote = NULL;
+ int force = 0;
+ BODY *body = NULL;
+ char *data, *p;
+ RemoteType rtype;
+ unsigned long i;
+ struct stat sbuf;
+
+#include "../c-client/linkage.c"
+
+ if(parse_args(argc, argv, &force, &local, &remote)){
+ fprintf(stderr, ustr, argv[0]);
+ exit(-1);
+ }
+
+ if(!local || !*local){
+ fprintf(stderr, "No local file specified\n");
+ usage++;
+ }
+
+ if(!remote || !*remote){
+ fprintf(stderr, "No remote folder specified\n");
+ usage++;
+ }
+
+ if(usage){
+ fprintf(stderr, ustr, argv[0]);
+ exit(-1);
+ }
+
+ if(!IS_REMOTE(remote)){
+ fprintf(stderr,
+ "Remote folder name \"%s\" %s\n", remote,
+ (*remote != '{') ? "must begin with \"{\"" : "not valid");
+ exit(-1);
+ }
+
+ if(IS_REMOTE(local)){
+ fprintf(stderr, "Argument to -l (%s) must be a local filename", local);
+ exit(-1);
+ }
+
+#ifdef _WINDOWS
+ if(stat(local, &sbuf))
+#else
+ if(lstat(local, &sbuf))
+#endif
+ {
+ if(errno == ENOENT){ /* File did not exist */
+ int fd;
+
+ /* create it */
+ if(((fd = open(local, O_CREAT|O_EXCL|O_WRONLY,0600)) < 0)
+ || (close(fd) != 0)){
+ fprintf(stderr, "%s: %s\n", local, err_desc(errno));
+ exit(-1);
+ }
+
+ /* now it exists! */
+ }
+ else{ /* unknown error */
+ fprintf(stderr, "%s: %s\n", local, err_desc(errno));
+ exit(-1);
+ }
+ }
+ else{ /* file exists */
+
+ /* is it a regular file? */
+#ifdef S_ISREG
+ if(!S_ISREG(sbuf.st_mode))
+#else
+ if(!(S_IFREG & sbuf.st_mode))
+#endif
+ {
+ fprintf(stderr, "Only allowed to write to regular local files. Try another filename.\n");
+ exit(-1);
+ }
+
+ if(access(local, WRITE_ACCESS) == 0){
+
+ snprintf(buf, sizeof(buf), "Local file \"%s\" exists, overwrite it",
+ (p = last_cmpnt(local)) ? p : local);
+ if(wantto(buf, 'n', 'n') != 'y'){
+ fprintf(stderr, "Dump cancelled\n");
+ exit(-1);
+ }
+ }
+ else{
+ fprintf(stderr, "Local file \"%s\" is not writable\n", local);
+ exit(-1);
+ }
+ }
+
+ /*
+ * Try opening the local file.
+ */
+ if((fp = fopen(local, "w")) == NULL){
+ fprintf(stderr, "Can't open \"%s\": %s\n", local, err_desc(errno));
+ mail_close(stream);
+ exit(-1);
+ }
+
+ /*
+ * Try opening the remote folder.
+ */
+ stream = mail_open(NULL, remote, OP_READONLY);
+ if(!stream || stream->halfopen){
+ fprintf(stderr, "Remote folder \"%s\" is not readable\n", remote);
+ if(stream)
+ mail_close(stream);
+
+ exit(-1);
+ }
+
+ if(stream->nmsgs >= 2){
+ /*
+ * There is a first message already. Check to see if it is one of
+ * our special header messages.
+ */
+ rtype = check_for_header_msg(stream);
+ if(!force && rtype == NotSet){
+ fprintf(stderr, "Folder \"%s\"\ndoes not appear to be an Alpine remote data folder.\nUse -f to force.\n", remote);
+ mail_close(stream);
+ exit(-1);
+ }
+ }
+ else if(stream->nmsgs == 1){
+ fprintf(stderr, "No data in remote folder to copy (only 1 message)\n");
+ mail_close(stream);
+ exit(-1);
+ }
+ else{
+ fprintf(stderr, "No data in remote folder to copy\n");
+ mail_close(stream);
+ exit(-1);
+ }
+
+ if(!mail_fetchstructure(stream, stream->nmsgs, &body)){
+ fprintf(stderr, "Can't access remote IMAP data\n");
+ mail_close(stream);
+ exit(-1);
+ }
+
+ if(!body ||
+ body->type != REMOTE_DATA_TYPE ||
+ !body->subtype ||
+ (!force && strucmp(body->subtype, spechdr(rtype)))){
+ fprintf(stderr, "Remote IMAP folder has wrong contents\n");
+ mail_close(stream);
+ exit(-1);
+ }
+
+ if(!mail_fetchenvelope(stream, stream->nmsgs)){
+ fprintf(stderr, "Can't access envelope in remote data\n");
+ mail_close(stream);
+ exit(-1);
+ }
+
+ data = mail_fetch_body(stream, stream->nmsgs, "1", &i, FT_PEEK);
+
+ p = data;
+ for(p = data; p < data+i; p++){
+
+ /* convert to unix newlines */
+ if(*p == '\r' && *(p+1) == '\n')
+ p++;
+
+ if(putc(*p, fp) == EOF){
+ fprintf(stderr,
+ "Error writing \"%s\": %s\n", local, err_desc(errno));
+ fclose(fp);
+ mail_close(stream);
+ exit(-1);
+ }
+ }
+
+ mail_close(stream);
+ fclose(fp);
+
+ fprintf(stderr,
+ "Remote folder is of type \"%s\", contents saved to \"%s\"\n",
+ ptype(rtype) ? ptype(rtype) : "unknown", local);
+ exit(0);
+}
+
+
+RemoteType
+check_for_header_msg(stream)
+ MAILSTREAM *stream;
+{
+ STRINGLIST *sl;
+ int ret = NotSet;
+ char *h, *try;
+ size_t len;
+ char *pinerc, *abook, *sig, *smime;
+
+ pinerc = spechdr(Pinerc);
+ abook = spechdr(Abook);
+ sig = spechdr(Sig);
+ smime = spechdr(Smime);
+
+ len = MAX(MAX(strlen(pinerc), strlen(abook)), MAX(strlen(sig), strlen(smime)));
+
+ sl = mail_newstringlist();
+ sl->text.data = (unsigned char *)fs_get((len+1) * sizeof(unsigned char));
+ try = pinerc;
+ strncpy((char *) sl->text.data, try, len);
+ sl->text.data[len] = '\0';
+ sl->text.size = strlen((char *) sl->text.data);
+
+ if(stream && (h=mail_fetch_header(stream,1L,NULL,sl,NULL,FT_PEEK))){
+
+ if(strlen(h) >= sl->text.size && !struncmp(h, try, sl->text.size))
+ ret = Pinerc;
+ }
+
+ if(ret == NotSet){
+ try = abook;
+ strncpy((char *)sl->text.data, try, len);
+ sl->text.data[len] = '\0';
+ sl->text.size = strlen((char *) sl->text.data);
+ if(stream && (h=mail_fetch_header(stream,1L,NULL,sl,NULL,FT_PEEK))){
+
+ if(strlen(h) >= sl->text.size && !struncmp(h, try, sl->text.size))
+ ret = Abook;
+ }
+ }
+
+ if(ret == NotSet){
+ try = sig;
+ strncpy((char *) sl->text.data, try, len);
+ sl->text.data[len] = '\0';
+ sl->text.size = strlen((char *) sl->text.data);
+ if(stream && (h=mail_fetch_header(stream,1L,NULL,sl,NULL,FT_PEEK))){
+
+ if(strlen(h) >= sl->text.size && !struncmp(h, try, sl->text.size))
+ ret = Sig;
+ }
+ }
+
+ if(sl)
+ mail_free_stringlist(&sl);
+
+ if(pinerc)
+ fs_give((void **)&pinerc);
+ if(abook)
+ fs_give((void **)&abook);
+ if(sig)
+ fs_give((void **)&sig);
+ if(smime)
+ fs_give((void **)&smime);
+
+ return(ret);
+}
+
+
+char *
+ptype(rtype)
+ RemoteType rtype;
+{
+ char *ret = NULL;
+
+ switch(rtype){
+ case Pinerc:
+ ret = cpystr("pinerc");
+ break;
+ case Abook:
+ ret = cpystr("abook");
+ break;
+ case Sig:
+ ret = cpystr("sig");
+ break;
+ case Smime:
+ ret = cpystr("smime");
+ break;
+ default:
+ break;
+ }
+
+ return(ret);
+}
+
+
+char *
+spechdr(rtype)
+ RemoteType rtype;
+{
+ char *ret = NULL;
+
+ switch(rtype){
+ case Pinerc:
+ ret = cpystr(REMOTE_PINERC_SUBTYPE);
+ break;
+ case Abook:
+ ret = cpystr(REMOTE_ABOOK_SUBTYPE);
+ break;
+ case Sig:
+ ret = cpystr(REMOTE_SIG_SUBTYPE);
+ break;
+ case Smime:
+ ret = cpystr(REMOTE_SMIME_SUBTYPE);
+ break;
+ default:
+ break;
+ }
+
+ return(ret);
+}
+
+
+int
+parse_args(argc, argv, force, local, remote)
+ int argc;
+ char **argv;
+ int *force;
+ char **local, **remote;
+{
+ int ac;
+ char **av;
+ int c;
+ char *str;
+ int usage = 0;
+
+ ac = argc;
+ av = argv;
+
+ /* while more arguments with leading - */
+Loop: while(--ac > 0 && **++av == '-'){
+ /* while more chars in this argument */
+ while(*++*av){
+ switch(c = **av){
+ case 'h':
+ usage++;
+ break;
+ case 'f':
+ (*force)++;
+ break;
+
+ case 'r': case 'l': /* string args */
+ if(*++*av)
+ str = *av;
+ else if(--ac)
+ str = *++av;
+ else{
+ fprintf(stderr, "missing argument for flag \"%c\"\n", c);
+ ++usage;
+ goto Loop;
+ }
+
+ switch(c){
+ case 'l':
+ if(str)
+ *local = str;
+
+ break;
+ case 'r':
+ if(str)
+ *remote = str;
+
+ break;
+ }
+
+ goto Loop;
+
+ default:
+ fprintf(stderr, "unknown flag \"%c\"\n", c);
+ ++usage;
+ break;
+ }
+ }
+ }
+
+ if(ac != 0)
+ usage++;
+
+ return(usage);
+}
+
+
+char *
+err_desc(err)
+ int err;
+{
+ return((char *) strerror(err));
+}
+
+
+void mm_exists(stream, number)
+ MAILSTREAM *stream;
+ unsigned long number;
+{
+}
+
+
+void mm_expunged(stream, number)
+ MAILSTREAM *stream;
+ unsigned long number;
+{
+}
+
+
+void mm_flags(stream, number)
+ MAILSTREAM *stream;
+ unsigned long number;
+{
+}
+
+
+void mm_list(stream, delim, name, attrib)
+ MAILSTREAM *stream;
+ int delim;
+ char *name;
+ long attrib;
+{
+}
+
+
+void mm_lsub(stream, delimiter, name, attributes)
+ MAILSTREAM *stream;
+ int delimiter;
+ char *name;
+ long attributes;
+{
+}
+
+
+void mm_notify(stream, string, errflg)
+ MAILSTREAM *stream;
+ char *string;
+ long errflg;
+{
+ mm_log(string, errflg);
+}
+
+
+void mm_log(string, errflg)
+ char *string;
+ long errflg;
+{
+ if(noshow_error)
+ return;
+
+ switch(errflg){
+ case BYE:
+ case NIL:
+ break;
+
+ case PARSE:
+ fprintf(stderr, "PARSE: %s\n", string);
+ break;
+
+ case WARN:
+ fprintf(stderr, "WARN: %s\n", string);
+ break;
+
+ case ERROR:
+ fprintf(stderr, "ERROR: %s\n", string);
+ break;
+
+ default:
+ fprintf(stderr, "%s\n", string);
+ break;
+ }
+}
+
+
+void mm_login(mb, user, pwd, trial)
+ NETMBX *mb;
+ char *user;
+ char *pwd;
+ long trial;
+{
+ char prompt[100], *last;
+ int i, j, goal, ugoal, len, rc, flags = 0;
+#define NETMAXPASSWD 100
+
+ user[NETMAXUSER-1] = '\0';
+
+ if(trial == 0L){
+ if(mb->user && *mb->user){
+ strncpy(user, mb->user, NETMAXUSER);
+ user[NETMAXUSER-1] = '\0';
+ }
+ }
+
+ if(!*mb->user){
+ /* Dress up long hostnames */
+ snprintf(prompt, sizeof(prompt), "%sHOST: ",
+ (mb->sslflag||mb->tlsflag) ? "+ " : "");
+ len = strlen(prompt);
+ /* leave space for "HOST", "ENTER NAME", and 15 chars for input name */
+ goal = 80 - (len + 20 + MIN(15, 80/5));
+ last = " ENTER LOGIN NAME: ";
+ if(goal < 9){
+ last = " LOGIN: ";
+ if((goal += 13) < 9){
+ last += 1;
+ goal = 0;
+ }
+ }
+
+ if(goal){
+ for(i = len, j = 0;
+ i < sizeof(prompt) && (prompt[i] = mb->host[j]); i++, j++)
+ if(i == goal && mb->host[goal+1] && i < sizeof(prompt)){
+ strncpy(&prompt[i-3], "...", sizeof(prompt)-(i-3));
+ prompt[sizeof(prompt)-1] = '\0';
+ break;
+ }
+ }
+ else
+ i = 0;
+
+ strncpy(&prompt[i], last, sizeof(prompt)-i);
+ prompt[sizeof(prompt)-1] = '\0';
+
+ while(1) {
+ rc = opt_enter(user, NETMAXUSER, prompt, &flags);
+ if(rc != 4)
+ break;
+ }
+
+ if(rc == 1 || !user[0]) {
+ user[0] = '\0';
+ pwd[0] = '\0';
+ }
+ }
+ else
+ strncpy(user, mb->user, NETMAXUSER);
+
+ user[NETMAXUSER-1] = '\0';
+ pwd[NETMAXPASSWD-1] = '\0';
+
+ if(!user[0])
+ return;
+
+
+ /* Dress up long host/user names */
+ /* leave space for "HOST", "USER" "ENTER PWD", 12 for user 6 for pwd */
+ snprintf(prompt, sizeof(prompt), "%sHOST: ", (mb->sslflag||mb->tlsflag) ? "+ " : "");
+ len = strlen(prompt);
+ goal = strlen(mb->host);
+ ugoal = strlen(user);
+ if((i = 80 - (len + 8 + 18 + 6)) < 14){
+ goal = 0; /* no host! */
+ if((i = 80 - (6 + 18 + 6)) <= 6){
+ ugoal = 0; /* no user! */
+ if((i = 80 - (18 + 6)) <= 0)
+ i = 0;
+ }
+ else{
+ ugoal = i; /* whatever's left */
+ i = 0;
+ }
+ }
+ else
+ while(goal + ugoal > i)
+ if(goal > ugoal)
+ goal--;
+ else
+ ugoal--;
+
+ if(goal){
+ snprintf(prompt, sizeof(prompt), "%sHOST: ",
+ (mb->sslflag||mb->tlsflag) ? "+ " : "");
+ for(i = len, j = 0;
+ i < sizeof(prompt) && (prompt[i] = mb->host[j]); i++, j++)
+ if(i == goal && mb->host[goal+1] && i < sizeof(prompt)){
+ strncpy(&prompt[i-3], "...", sizeof(prompt)-(i-3));
+ prompt[sizeof(prompt)-1] = '\0';
+ break;
+ }
+ }
+ else
+ i = 0;
+
+ if(ugoal){
+ strncpy(&prompt[i], &" USER: "[i ? 0 : 2], sizeof(prompt)-i);
+ prompt[sizeof(prompt)-1] = '\0';
+ for(i += strlen(&prompt[i]), j = 0;
+ i < sizeof(prompt) && (prompt[i] = user[j]); i++, j++)
+ if(j == ugoal && user[ugoal+1] && i < sizeof(prompt)){
+ strncpy(&prompt[i-3], "...", sizeof(prompt)-(i-3));
+ prompt[sizeof(prompt)-1] = '\0';
+ break;
+ }
+ }
+
+ strncpy(&prompt[i], &" ENTER PASSWORD: "[i ? 0 : 8], sizeof(prompt)-i);
+ prompt[sizeof(prompt)-1] = '\0';
+
+ *pwd = '\0';
+ while(1) {
+ flags = OE_PASSWD;
+ rc = opt_enter(pwd, NETMAXPASSWD, prompt, &flags);
+ if(rc != 4)
+ break;
+ }
+
+ if(rc == 1 || !pwd[0]) {
+ user[0] = pwd[0] = '\0';
+ return;
+ }
+}
+
+
+void mm_critical(stream)
+ MAILSTREAM *stream;
+{
+}
+
+
+void mm_nocritical(stream)
+ MAILSTREAM *stream;
+{
+}
+
+
+long mm_diskerror(stream, errcode, serious)
+ MAILSTREAM *stream;
+ long errcode;
+ long serious;
+{
+ return T;
+}
+
+
+void mm_fatal(string)
+ char *string;
+{
+ fprintf(stderr, "%s\n", string);
+}
+
+
+void mm_searched(stream, msgno)
+ MAILSTREAM *stream;
+ unsigned long msgno;
+{
+}
+
+
+void mm_status(stream, mailbox, status)
+ MAILSTREAM *stream;
+ char *mailbox;
+ MAILSTATUS *status;
+{
+}
+
+void mm_dlog(string)
+ char *string;
+{
+ fprintf(stderr, "%s\n", string);
+}
+
+
+int
+opt_enter(string, field_len, prompt, flags)
+ char *string, *prompt;
+ int field_len;
+ int *flags;
+{
+ char *pw;
+ int return_v = -10;
+
+ while(return_v == -10){
+
+ if(flags && *flags & OE_PASSWD){
+ if((pw = getpass(prompt)) != NULL){
+ if(strlen(pw) < field_len){
+ strncpy(string, pw, field_len);
+ string[field_len-1] = '\0';
+ return_v = 0;
+ }
+ else{
+ fputs("Password too long\n", stderr);
+ return_v = -1;
+ }
+ }
+ else
+ return_v = 1; /* cancel? */
+ }
+ else{
+ char *p;
+
+ fputs(prompt, stdout);
+ fgets(string, field_len, stdin);
+ string[field_len-1] = '\0';
+ if((p = strpbrk(string, "\r\n")) != NULL)
+ *p = '\0';
+
+ return_v = 0;
+ }
+ }
+
+ return(return_v);
+}
+
+char *
+last_cmpnt(filename)
+ char *filename;
+{
+ register char *p = NULL, *q = filename;
+
+ if(!q)
+ return(q);
+
+ while((q = strchr(q, '/')) != NULL)
+ if(*++q)
+ p = q;
+
+ return(p);
+}
+
+int
+wantto(question, dflt, on_ctrl_C)
+ char *question;
+ int dflt, on_ctrl_C;
+{
+ int ret = 0;
+ char rep[1000], *p;
+
+ while(!ret){
+ fprintf(stdout, "%s? [%c]:", question, dflt);
+ fgets(rep, sizeof(rep), stdin);
+ if((p = strpbrk(rep, "\r\n")) != NULL)
+ *p = '\0';
+ switch(*rep){
+ case 'Y':
+ case 'y':
+ ret = (int)'y';
+ break;
+ case 'N':
+ case 'n':
+ ret = (int)'n';
+ break;
+ case '\0':
+ ret = dflt;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return ret;
+}
diff --git a/alpine/rpload.c b/alpine/rpload.c
new file mode 100644
index 00000000..f5606611
--- /dev/null
+++ b/alpine/rpload.c
@@ -0,0 +1,1008 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: rpload.c 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "headers.h"
+#include "radio.h" /* OE_PASSWD */
+#include "../pith/util.h" /* IS_REMOTE() */
+#include "../pith/remote.h" /* REMOTE_ABOOK_SUBTYPE... */
+
+
+typedef enum {Pinerc, Abook, Sig, Smime, NotSet} RemoteType;
+
+
+int parse_args(int, char **, int *, int *, char **, char **, RemoteType *);
+RemoteType check_for_header_msg(MAILSTREAM *);
+char *ptype(RemoteType);
+char *spechdr(RemoteType);
+int add_initial_msg(MAILSTREAM *, char *, char *);
+int append_data(MAILSTREAM *, char *, char *, FILE *);
+void trim_data(MAILSTREAM *, int);
+void write_fake_headers(RFC822BUFFER *, char *, char *, char *);
+char *err_desc(int);
+int opt_enter(char *, int, char *, int *);
+
+
+int noshow_error = 0;
+char *ustr = "usage: %s [-s trimSize] [-f] -t Type -l Local_file -r Remote_folder\n";
+
+
+/* look for my_timer_period in pico directory for an explanation */
+int my_timer_period = ((IDLE_TIMEOUT + 1)*1000);
+
+
+#ifdef _WINDOWS
+
+#undef main
+
+app_main (argc, argv)
+ int argc;
+ char argv[];
+{
+}
+
+#endif /* _WINDOWS */
+
+
+/*
+ * rpload [-s trimSize] [-f] -t Type -l Local_file -r Remote_folder
+ *
+ * Type is one of abook
+ * pinerc
+ * smime
+ * sig (this is mostly obsolete, literal sigs
+ * should be used instead)
+ * -f means force the folder to be written even if it
+ * doesn't look like it is of the right format
+ *
+ * Note: We're not worrying about memory leaks.
+ */
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ MAILSTREAM *stream = NULL;
+ FILE *fp;
+ int delete_existing = 0, usage = 0;
+ int force = 0, trimsize = 0;
+ char *local = NULL, *remote = NULL, *special_hdr = NULL;
+ RemoteType rt, type = NotSet;
+
+#include "../c-client/linkage.c"
+
+ if(parse_args(argc, argv, &force, &trimsize, &local, &remote, &type)){
+ fprintf(stderr, ustr, argv[0]);
+ exit(-1);
+ }
+
+ if(!local || !*local){
+ fprintf(stderr, "No local file specified\n");
+ usage++;
+ }
+
+ if(!remote || !*remote){
+ fprintf(stderr, "No remote folder specified\n");
+ usage++;
+ }
+
+ if(type == NotSet){
+ fprintf(stderr, "Type must be set to one of:\n");
+ for(rt = Pinerc; rt != NotSet; rt++)
+ fprintf(stderr, " %s\n", ptype(rt));
+
+ usage++;
+ }
+
+ if(usage){
+ fprintf(stderr, ustr, argv[0]);
+ exit(-1);
+ }
+
+ if(!IS_REMOTE(remote)){
+ fprintf(stderr,
+ "Remote folder name \"%s\" %s\n", remote,
+ (*remote != '{') ? "must begin with \"{\"" : "not valid");
+ exit(-1);
+ }
+
+ if(IS_REMOTE(local)){
+ fprintf(stderr, "Argument to -l (%s) must be a local filename", local);
+ exit(-1);
+ }
+
+ if(access(local, ACCESS_EXISTS) != 0){
+ fprintf(stderr, "Local file \"%s\" does not exist\n", local);
+ exit(-1);
+ }
+
+ if(access(local, READ_ACCESS) != 0){
+ fprintf(stderr,
+ "Can't read local file \"%s\": %s\n",
+ local, err_desc(errno));
+ exit(-1);
+ }
+
+ /*
+ * Try opening the local file.
+ */
+ if((fp = fopen(local, "r")) == NULL){
+ fprintf(stderr, "Can't open \"%s\": %s\n", local, err_desc(errno));
+ exit(-1);
+ }
+
+ /*
+ * Try opening the remote folder. If it doesn't exist, create it.
+ */
+
+ /* failure would be normal here, so don't show it */
+ noshow_error = 1;
+ stream = mail_open(NULL, remote, 0L);
+ if(!stream || stream->halfopen){
+ if(stream && stream->halfopen){
+ noshow_error = 0;
+ if(!mail_create(stream, remote))
+ exit(-1);
+
+ stream = mail_open(stream, remote, 0L);
+ if(!stream || stream->halfopen)
+ exit(-1);
+ }
+ else{
+ fprintf(stderr, "Trouble opening remote folder \"%s\"\n", remote);
+ exit(-1);
+ }
+ }
+
+ noshow_error = 0;
+
+ if(stream->rdonly){
+ fprintf(stderr, "Remote folder \"%s\" is not writable\n", remote);
+ exit(-1);
+ }
+
+ if(stream->nmsgs > 0){
+ /*
+ * There is a first message already. Check to see if it is one of
+ * our special header messages.
+ */
+ rt = check_for_header_msg(stream);
+ if(rt == NotSet){
+ if(force)
+ delete_existing++;
+ else{
+ fprintf(stderr, "Folder \"%s\"\ndoes not appear to be an Alpine remote \"%s\" folder.\nUse -f to force.\n", remote, ptype(type));
+ fprintf(stderr, "-f will cause %ld messages to be deleted\n",
+ stream->nmsgs);
+ exit(-1);
+ }
+ }
+ else if(rt != type){
+ if(force)
+ delete_existing++;
+ else{
+ fprintf(stderr, "Folder \"%s\" is type \"%s\"\nUse -f to force switch.\n", remote, ptype(rt));
+ fprintf(stderr, "-f will cause %ld messages to be deleted\n",
+ stream->nmsgs);
+ exit(-1);
+ }
+ }
+ }
+
+ if(delete_existing){
+ char sequence[20];
+
+ mail_ping(stream);
+ snprintf(sequence, sizeof(sequence), "1:%ld", stream->nmsgs);
+ mail_flag(stream, sequence, "\\DELETED", ST_SET);
+ mail_expunge(stream);
+ mail_ping(stream);
+ }
+
+ special_hdr = spechdr(type);
+
+ /*
+ * Add the explanatory header message if needed.
+ */
+ if(stream->nmsgs == 0){
+ if(add_initial_msg(stream, remote, special_hdr) != 0){
+ mail_close(stream);
+ exit(-1);
+ }
+ }
+
+ /*
+ * Add the actual data in a message.
+ */
+ if(append_data(stream, remote, special_hdr, fp) != 0){
+ mail_close(stream);
+ exit(-1);
+ }
+
+ /*
+ * Trim the size of the remote folder.
+ */
+ if(trimsize)
+ trim_data(stream, trimsize);
+
+ mail_close(stream);
+ exit(0);
+}
+
+
+RemoteType
+check_for_header_msg(stream)
+ MAILSTREAM *stream;
+{
+ STRINGLIST *sl;
+ int ret = NotSet;
+ char *h, *try;
+ size_t len;
+ char *pinerc, *abook, *sig, *smime;
+
+ pinerc = spechdr(Pinerc);
+ abook = spechdr(Abook);
+ sig = spechdr(Sig);
+ smime = spechdr(Smime);
+
+ len = MAX(MAX(strlen(pinerc), strlen(abook)), MAX(strlen(sig), strlen(smime)));
+
+ sl = mail_newstringlist();
+ sl->text.data = (unsigned char *)fs_get((len+1) * sizeof(unsigned char));
+ try = pinerc;
+ strncpy((char *)sl->text.data, try, len);
+ sl->text.data[len] = '\0';
+ sl->text.size = strlen((char *) sl->text.data);
+
+ if(stream && (h=mail_fetch_header(stream,1L,NULL,sl,NULL,FT_PEEK))){
+
+ if(strlen(h) >= sl->text.size && !struncmp(h, try, sl->text.size))
+ ret = Pinerc;
+ }
+
+ if(ret == NotSet){
+ try = abook;
+ strncpy((char *)sl->text.data, try, len);
+ sl->text.data[len] = '\0';
+ sl->text.size = strlen((char *) sl->text.data);
+ if(stream && (h=mail_fetch_header(stream,1L,NULL,sl,NULL,FT_PEEK))){
+
+ if(strlen(h) >= sl->text.size && !struncmp(h, try, sl->text.size))
+ ret = Abook;
+ }
+ }
+
+ if(ret == NotSet){
+ try = sig;
+ strncpy((char *)sl->text.data, try, len);
+ sl->text.data[len] = '\0';
+ sl->text.size = strlen((char *) sl->text.data);
+ if(stream && (h=mail_fetch_header(stream,1L,NULL,sl,NULL,FT_PEEK))){
+
+ if(strlen(h) >= sl->text.size && !struncmp(h, try, sl->text.size))
+ ret = Sig;
+ }
+ }
+
+ if(sl)
+ mail_free_stringlist(&sl);
+
+ if(pinerc)
+ fs_give((void **)&pinerc);
+ if(abook)
+ fs_give((void **)&abook);
+ if(sig)
+ fs_give((void **)&sig);
+ if(smime)
+ fs_give((void **)&smime);
+
+ return(ret);
+}
+
+
+char *
+ptype(rtype)
+ RemoteType rtype;
+{
+ char *ret = NULL;
+
+ switch(rtype){
+ case Pinerc:
+ ret = cpystr("pinerc");
+ break;
+ case Abook:
+ ret = cpystr("abook");
+ break;
+ case Sig:
+ ret = cpystr("sig");
+ break;
+ case Smime:
+ ret = cpystr("smime");
+ break;
+ default:
+ break;
+ }
+
+ return(ret);
+}
+
+
+char *
+spechdr(rtype)
+ RemoteType rtype;
+{
+ char *ret = NULL;
+
+ switch(rtype){
+ case Pinerc:
+ ret = cpystr(REMOTE_PINERC_SUBTYPE);
+ break;
+ case Abook:
+ ret = cpystr(REMOTE_ABOOK_SUBTYPE);
+ break;
+ case Sig:
+ ret = cpystr(REMOTE_SIG_SUBTYPE);
+ break;
+ case Smime:
+ ret = cpystr(REMOTE_SMIME_SUBTYPE);
+ break;
+ default:
+ break;
+ }
+
+ return(ret);
+}
+
+
+int
+parse_args(argc, argv, force, trimsize, local, remote, type)
+ int argc;
+ char **argv;
+ int *force, *trimsize;
+ char **local, **remote;
+ RemoteType *type;
+{
+ int ac;
+ char **av;
+ int c;
+ char *str;
+ int usage = 0;
+ RemoteType rt;
+
+ ac = argc;
+ av = argv;
+
+ /* while more arguments with leading - */
+Loop: while(--ac > 0 && **++av == '-'){
+ /* while more chars in this argument */
+ while(*++*av){
+ switch(c = **av){
+ case 'h':
+ usage++;
+ break;
+ case 'f':
+ (*force)++;
+ break;
+
+ case 't': case 'l': /* string args */
+ case 'r':
+ case 's': /* integer args */
+ if(*++*av)
+ str = *av;
+ else if(--ac)
+ str = *++av;
+ else{
+ fprintf(stderr, "missing argument for flag \"%c\"\n", c);
+ ++usage;
+ goto Loop;
+ }
+
+ switch(c){
+ case 'l':
+ if(str)
+ *local = str;
+
+ break;
+ case 'r':
+ if(str)
+ *remote = str;
+
+ break;
+ case 't':
+ for(rt = Pinerc; rt != NotSet; rt++){
+ if(!strucmp(str, ptype(rt)))
+ break;
+ }
+
+ *type = rt;
+ break;
+ case 's':
+ if(!isdigit((unsigned char)str[0])){
+ fprintf(stderr,
+ "non-numeric argument for flag \"%c\"\n", c);
+ ++usage;
+ break;
+ }
+
+ *trimsize = atoi(str);
+ if(*trimsize < 1){
+ fprintf(stderr, "trimsize of %d is too small, have to leave at least one copy\n", *trimsize);
+ ++usage;
+ }
+ else if(*trimsize > 100){
+ fprintf(stderr,
+ "trimsize of %d is too large, 5 or 10 is sufficient\n",
+ *trimsize);
+ ++usage;
+ }
+
+ break;
+ }
+
+ goto Loop;
+
+ default:
+ fprintf(stderr, "unknown flag \"%c\"\n", c);
+ ++usage;
+ break;
+ }
+ }
+ }
+
+ if(ac != 0)
+ usage++;
+
+ return(usage);
+}
+
+
+long
+dummy_soutr(stream, string)
+ void *stream;
+ char *string;
+{
+ return LONGT;
+}
+
+
+/*
+ * Add an explanatory first message advising user that this is a
+ * special sort of folder.
+ */
+int
+add_initial_msg(stream, mailbox, special_hdr)
+ MAILSTREAM *stream;
+ char *mailbox;
+ char *special_hdr;
+{
+ STRING msg;
+ char buf[20000];
+ RFC822BUFFER rbuf;
+
+ rbuf.f = dummy_soutr;
+ rbuf.s = NULL;
+ rbuf.beg = buf;
+ rbuf.cur = buf;
+ rbuf.end = buf+sizeof(buf)-1;
+ write_fake_headers(&rbuf, "Header Message for Remote Data", "plain", special_hdr);
+ *rbuf.cur = '\0';
+
+ buf[sizeof(buf)-1] = '\0';
+
+ if(!strucmp(special_hdr, REMOTE_ABOOK_SUBTYPE)){
+ strncat(buf, "This folder contains a single Alpine addressbook.\015\012", sizeof(buf)-strlen(buf)-1);
+ strncat(buf, "This message is just an explanatory message.\015\012", sizeof(buf)-strlen(buf)-1);
+ strncat(buf, "The last message in the folder is the live addressbook data.\015\012", sizeof(buf)-strlen(buf)-1);
+ strncat(buf, "The rest of the messages contain previous revisions of the addressbook data.\015\012", sizeof(buf)-strlen(buf)-1);
+ strncat(buf, "To restore a previous revision just delete and expunge all of the messages\015\012", sizeof(buf)-strlen(buf)-1);
+ strncat(buf, "which come after it.\015\012", sizeof(buf)-strlen(buf)-1);
+ }
+ else if(!strucmp(special_hdr, REMOTE_PINERC_SUBTYPE)){
+ strncat(buf, "This folder contains an Alpine config file.\015\012", sizeof(buf)-strlen(buf)-1);
+ strncat(buf, "This message is just an explanatory message.\015\012", sizeof(buf)-strlen(buf)-1);
+ strncat(buf, "The last message in the folder is the live config data.\015\012", sizeof(buf)-strlen(buf)-1);
+ strncat(buf, "The rest of the messages contain previous revisions of the data.\015\012", sizeof(buf)-strlen(buf)-1);
+ strncat(buf, "To restore a previous revision just delete and expunge all of the messages\015\012", sizeof(buf)-strlen(buf)-1);
+ strncat(buf, "which come after it.\015\012", sizeof(buf)-strlen(buf)-1);
+ }
+ else if(!strucmp(special_hdr, REMOTE_SMIME_SUBTYPE)){
+ strncat(buf, "This folder contains Alpine S/MIME config information.\015\012", sizeof(buf)-strlen(buf)-1);
+ strncat(buf, "This message is just an explanatory message.\015\012", sizeof(buf)-strlen(buf)-1);
+ strncat(buf, "The last message in the folder is the live data.\015\012", sizeof(buf)-strlen(buf)-1);
+ strncat(buf, "The rest of the messages contain previous revisions of the data.\015\012", sizeof(buf)-strlen(buf)-1);
+ strncat(buf, "To restore a previous revision just delete and expunge all of the messages\015\012", sizeof(buf)-strlen(buf)-1);
+ strncat(buf, "which come after it.\015\012", sizeof(buf)-strlen(buf)-1);
+ }
+ else{
+ strncat(buf, "This folder contains remote Alpine data.\015\012", sizeof(buf)-strlen(buf)-1);
+ strncat(buf, "This message is just an explanatory message.\015\012", sizeof(buf)-strlen(buf)-1);
+ strncat(buf, "The last message in the folder is the live data.\015\012", sizeof(buf)-strlen(buf)-1);
+ strncat(buf, "The rest of the messages contain previous revisions of the data.\015\012", sizeof(buf)-strlen(buf)-1);
+ strncat(buf, "To restore a previous revision just delete and expunge all of the messages\015\012", sizeof(buf)-strlen(buf)-1);
+ strncat(buf, "which come after it.\015\012", sizeof(buf)-strlen(buf)-1);
+ }
+
+ INIT(&msg, mail_string, (void *)buf, strlen(buf));
+ if(!mail_append(stream, mailbox, &msg))
+ return(-1);
+
+ return(0);
+}
+
+
+/*
+ * Add a message to the folder with the contents of the local data
+ * in it.
+ */
+int
+append_data(stream, mailbox, special_hdr, fp)
+ MAILSTREAM *stream;
+ char *mailbox;
+ char *special_hdr;
+ FILE *fp;
+{
+ STRING msg;
+ char buf[20000], *sto, *p;
+ struct stat sbuf;
+ long filelen, len;
+ int c, nextc;
+ RFC822BUFFER rbuf;
+
+ if(fstat(fileno(fp), &sbuf) != 0){
+ fprintf(stderr, "fstat of local file failed\n");
+ return(-1);
+ }
+
+ filelen = (long) sbuf.st_size;
+
+ rbuf.f = dummy_soutr;
+ rbuf.s = NULL;
+ rbuf.beg = buf;
+ rbuf.cur = buf;
+ rbuf.end = buf+sizeof(buf)-1;
+ write_fake_headers(&rbuf, "Pine Remote Data Container", special_hdr,
+ special_hdr);
+ *rbuf.cur = '\0';
+
+ buf[sizeof(buf)-1] = '\0';
+
+ /* very conservative estimate of space needed */
+ len = filelen + filelen + strlen(buf) + 10;
+ sto = fs_get((len+1) * sizeof(char));
+
+ strncpy(sto, buf, len);
+ sto[len] = '\0';
+ p = sto + strlen(sto);
+ /* Write the contents */
+ while((c = getc(fp)) != EOF){
+ /*
+ * c-client expects CRLF-terminated lines. These lines
+ * can be either CRLF- or LF-terminated. We have to convert them
+ * when we copy into c-client.
+ */
+ if(c == '\r' || c == '\n'){
+ if(c == '\r' && ((nextc = getc(fp)) != '\n') && nextc != EOF)
+ ungetc(nextc, fp);
+
+ /* write the CRFL */
+ if(p - sto < len)
+ *p++ = '\r';
+
+ if(p - sto < len)
+ *p++ = '\n';
+ }
+ else if(p - sto < len)
+ *p++ = c;
+ }
+
+ fclose(fp);
+ if(p - sto < len)
+ *p = '\0';
+
+ sto[len] = '\0';
+
+ INIT(&msg, mail_string, (void *)sto, strlen(sto));
+ if(!mail_append(stream, mailbox, &msg)){
+ fprintf(stderr, "Copy failed\n");
+ return(-1);
+ }
+
+ fs_give((void **)&sto);
+
+ return(0);
+}
+
+
+/*
+ * Trim the number of saved copies of the remote data history in case
+ * this is the only way this folder is ever updated. We leave
+ * the first message there because it is supposed to be an explanatory
+ * message, but we don't actually check to see whether or not it is
+ * such a message or not.
+ */
+void
+trim_data(stream, trimsize)
+ MAILSTREAM *stream;
+ int trimsize;
+{
+ if(stream->nmsgs > trimsize + 1){
+ char sequence[20];
+
+ mail_ping(stream);
+ snprintf(sequence, sizeof(sequence), "2:%ld", stream->nmsgs - trimsize);
+ mail_flag(stream, sequence, "\\DELETED", ST_SET);
+ mail_expunge(stream);
+ }
+}
+
+
+void
+write_fake_headers(where, subject, subtype, special_hdr)
+ RFC822BUFFER *where;
+ char *subject;
+ char *subtype;
+ char *special_hdr;
+{
+ ENVELOPE *fake_env;
+ BODY *fake_body;
+ ADDRESS *fake_from;
+ char date[200], vers[10];
+
+ fake_env = (ENVELOPE *)fs_get(sizeof(ENVELOPE));
+ memset(fake_env, 0, sizeof(ENVELOPE));
+ fake_body = (BODY *)fs_get(sizeof(BODY));
+ memset(fake_body, 0, sizeof(BODY));
+ fake_from = (ADDRESS *)fs_get(sizeof(ADDRESS));
+ memset(fake_from, 0, sizeof(ADDRESS));
+ rfc822_date(date);
+
+ fake_env->subject = cpystr(subject);
+ fake_env->date = (unsigned char *) cpystr(date);
+ fake_from->personal = cpystr("Pine Remote Data");
+ fake_from->mailbox = cpystr("nobody");
+ fake_from->host = cpystr("nowhere");
+ fake_env->from = fake_from;
+ fake_body->type = REMOTE_DATA_TYPE;
+ fake_body->subtype = cpystr(subtype);
+
+ snprintf(vers, sizeof(vers), "%d", REMOTE_DATA_VERS_NUM);
+
+ /* re-use subtype for special header name, too */
+ rfc822_output_header_line(where, special_hdr, 0L, vers);
+ rfc822_output_header(where, fake_env, fake_body, NULL, 0L);
+ mail_free_envelope(&fake_env);
+ mail_free_body(&fake_body);
+}
+
+
+char *
+err_desc(err)
+ int err;
+{
+ return((char *) strerror(err));
+}
+
+
+void mm_exists(stream, number)
+ MAILSTREAM *stream;
+ unsigned long number;
+{
+}
+
+
+void mm_expunged(stream, number)
+ MAILSTREAM *stream;
+ unsigned long number;
+{
+}
+
+
+void mm_flags(stream, number)
+ MAILSTREAM *stream;
+ unsigned long number;
+{
+}
+
+
+void mm_list(stream, delim, name, attrib)
+ MAILSTREAM *stream;
+ int delim;
+ char *name;
+ long attrib;
+{
+}
+
+
+void mm_lsub(stream, delimiter, name, attributes)
+ MAILSTREAM *stream;
+ int delimiter;
+ char *name;
+ long attributes;
+{
+}
+
+
+void mm_notify(stream, string, errflg)
+ MAILSTREAM *stream;
+ char *string;
+ long errflg;
+{
+ mm_log(string, errflg);
+}
+
+
+void mm_log(string, errflg)
+ char *string;
+ long errflg;
+{
+ if(noshow_error)
+ return;
+
+ switch(errflg){
+ case BYE:
+ case NIL:
+ break;
+
+ case PARSE:
+ fprintf(stderr, "PARSE: %s\n", string);
+ break;
+
+ case WARN:
+ fprintf(stderr, "WARN: %s\n", string);
+ break;
+
+ case ERROR:
+ fprintf(stderr, "ERROR: %s\n", string);
+ break;
+
+ default:
+ fprintf(stderr, "%s\n", string);
+ break;
+ }
+}
+
+
+void mm_login(mb, user, pwd, trial)
+ NETMBX *mb;
+ char *user;
+ char *pwd;
+ long trial;
+{
+ char prompt[100], *last;
+ int i, j, goal, ugoal, len, rc, flags = 0;
+#define NETMAXPASSWD 100
+
+ user[NETMAXUSER-1] = '\0';
+
+ if(trial == 0L){
+ if(mb->user && *mb->user){
+ strncpy(user, mb->user, NETMAXUSER);
+ user[NETMAXUSER-1] = '\0';
+ }
+ }
+
+ if(!*mb->user){
+ /* Dress up long hostnames */
+ snprintf(prompt, sizeof(prompt), "%sHOST: ",
+ (mb->sslflag||mb->tlsflag) ? "+ " : "");
+ len = strlen(prompt);
+ /* leave space for "HOST", "ENTER NAME", and 15 chars for input name */
+ goal = 80 - (len + 20 + MIN(15, 80/5));
+ last = " ENTER LOGIN NAME: ";
+ if(goal < 9){
+ last = " LOGIN: ";
+ if((goal += 13) < 9){
+ last += 1;
+ goal = 0;
+ }
+ }
+
+ if(goal){
+ for(i = len, j = 0;
+ i < sizeof(prompt) && (prompt[i] = mb->host[j]); i++, j++)
+ if(i == goal && mb->host[goal+1] && i < sizeof(prompt)){
+ strncpy(&prompt[i-3], "...", sizeof(prompt)-(i-3));
+ prompt[sizeof(prompt)-1] = '\0';
+ break;
+ }
+ }
+ else
+ i = 0;
+
+ strncpy(&prompt[i], last, sizeof(prompt)-i);
+ prompt[sizeof(prompt)-1] = '\0';
+
+ while(1) {
+ rc = opt_enter(user, NETMAXUSER, prompt, &flags);
+ if(rc != 4)
+ break;
+ }
+
+ if(rc == 1 || !user[0]) {
+ user[0] = '\0';
+ pwd[0] = '\0';
+ }
+ }
+ else
+ strncpy(user, mb->user, NETMAXUSER);
+
+ user[NETMAXUSER-1] = '\0';
+ pwd[NETMAXPASSWD-1] = '\0';
+
+ if(!user[0])
+ return;
+
+
+ /* Dress up long host/user names */
+ /* leave space for "HOST", "USER" "ENTER PWD", 12 for user 6 for pwd */
+ snprintf(prompt, sizeof(prompt), "%sHOST: ", (mb->sslflag||mb->tlsflag) ? "+ " : "");
+ len = strlen(prompt);
+ goal = strlen(mb->host);
+ ugoal = strlen(user);
+ if((i = 80 - (len + 8 + 18 + 6)) < 14){
+ goal = 0; /* no host! */
+ if((i = 80 - (6 + 18 + 6)) <= 6){
+ ugoal = 0; /* no user! */
+ if((i = 80 - (18 + 6)) <= 0)
+ i = 0;
+ }
+ else{
+ ugoal = i; /* whatever's left */
+ i = 0;
+ }
+ }
+ else
+ while(goal + ugoal > i)
+ if(goal > ugoal)
+ goal--;
+ else
+ ugoal--;
+
+ if(goal){
+ snprintf(prompt, sizeof(prompt), "%sHOST: ",
+ (mb->sslflag||mb->tlsflag) ? "+ " : "");
+ for(i = len, j = 0;
+ i < sizeof(prompt) && (prompt[i] = mb->host[j]); i++, j++)
+ if(i == goal && mb->host[goal+1] && i < sizeof(prompt)){
+ strncpy(&prompt[i-3], "...", sizeof(prompt)-(i-3));
+ prompt[sizeof(prompt)-1] = '\0';
+ break;
+ }
+ }
+ else
+ i = 0;
+
+ if(ugoal){
+ strncpy(&prompt[i], &" USER: "[i ? 0 : 2], sizeof(prompt)-i);
+ prompt[sizeof(prompt)-1] = '\0';
+ for(i += strlen(&prompt[i]), j = 0;
+ i < sizeof(prompt) && (prompt[i] = user[j]); i++, j++)
+ if(j == ugoal && user[ugoal+1] && i < sizeof(prompt)){
+ strncpy(&prompt[i-3], "...", sizeof(prompt)-(i-3));
+ prompt[sizeof(prompt)-1] = '\0';
+ break;
+ }
+ }
+
+ strncpy(&prompt[i], &" ENTER PASSWORD: "[i ? 0 : 8], sizeof(prompt)-i);
+ prompt[sizeof(prompt)-1] = '\0';
+
+ *pwd = '\0';
+ while(1) {
+ flags = OE_PASSWD;
+ rc = opt_enter(pwd, NETMAXPASSWD, prompt, &flags);
+ if(rc != 4)
+ break;
+ }
+
+ if(rc == 1 || !pwd[0]) {
+ user[0] = pwd[0] = '\0';
+ return;
+ }
+}
+
+
+void mm_critical(stream)
+ MAILSTREAM *stream;
+{
+}
+
+
+void mm_nocritical(stream)
+ MAILSTREAM *stream;
+{
+}
+
+
+long mm_diskerror(stream, errcode, serious)
+ MAILSTREAM *stream;
+ long errcode;
+ long serious;
+{
+ return T;
+}
+
+
+void mm_fatal(string)
+ char *string;
+{
+ fprintf(stderr, "%s\n", string);
+}
+
+
+void mm_searched(stream, msgno)
+ MAILSTREAM *stream;
+ unsigned long msgno;
+{
+}
+
+
+void mm_status(stream, mailbox, status)
+ MAILSTREAM *stream;
+ char *mailbox;
+ MAILSTATUS *status;
+{
+}
+
+void mm_dlog(string)
+ char *string;
+{
+ fprintf(stderr, "%s\n", string);
+}
+
+
+int
+opt_enter(string, field_len, prompt, flags)
+ char *string, *prompt;
+ int field_len;
+ int *flags;
+{
+ char *pw;
+ int return_v = -10;
+
+ while(return_v == -10){
+
+ if(flags && *flags & OE_PASSWD){
+ if((pw = getpass(prompt)) != NULL){
+ if(strlen(pw) < field_len){
+ strncpy(string, pw, field_len);
+ string[field_len-1] = '\0';
+ return_v = 0;
+ }
+ else{
+ fputs("Password too long\n", stderr);
+ return_v = -1;
+ }
+ }
+ else
+ return_v = 1; /* cancel? */
+ }
+ else{
+ char *p;
+
+ fputs(prompt, stdout);
+ fgets(string, field_len, stdin);
+ string[field_len-1] = '\0';
+ if((p = strpbrk(string, "\r\n")) != NULL)
+ *p = '\0';
+
+ return_v = 0;
+ }
+ }
+
+ return(return_v);
+}
diff --git a/alpine/send.c b/alpine/send.c
new file mode 100644
index 00000000..3d670f02
--- /dev/null
+++ b/alpine/send.c
@@ -0,0 +1,7155 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: send.c 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ Functions for composing and sending mail
+
+ ====*/
+
+
+#include "headers.h"
+#include "send.h"
+#include "status.h"
+#include "mailview.h"
+#include "mailindx.h"
+#include "dispfilt.h"
+#include "keymenu.h"
+#include "folder.h"
+#include "radio.h"
+#include "addrbook.h"
+#include "reply.h"
+#include "titlebar.h"
+#include "signal.h"
+#include "mailcmd.h"
+#include "roleconf.h"
+#include "adrbkcmd.h"
+#include "busy.h"
+#include "../pith/debug.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/flag.h"
+#include "../pith/bldaddr.h"
+#include "../pith/copyaddr.h"
+#include "../pith/detach.h"
+#include "../pith/mimedesc.h"
+#include "../pith/pipe.h"
+#include "../pith/addrstring.h"
+#include "../pith/news.h"
+#include "../pith/detoken.h"
+#include "../pith/util.h"
+#include "../pith/init.h"
+#include "../pith/mailcmd.h"
+#include "../pith/ablookup.h"
+#include "../pith/reply.h"
+#include "../pith/hist.h"
+#include "../pith/list.h"
+#include "../pith/icache.h"
+#include "../pith/busy.h"
+#include "../pith/mimetype.h"
+#include "../pith/send.h"
+#include "../pith/smime.h"
+
+
+typedef struct body_particulars {
+ unsigned short type, encoding, had_csp;
+ char *subtype, *charset;
+ PARAMETER *parameter;
+} BODY_PARTICULARS_S;
+
+#define PHONE_HOME_VERSION "-count"
+#define PHONE_HOME_HOST "docserver.cac.washington.edu"
+
+/*
+ * macro to bind pico's headerentry pointer to PINEFIELD "extdata" hook
+ */
+#define HE(PF) ((struct headerentry *)((PF)->extdata))
+
+
+/*
+ * Internal Prototypes
+ */
+int redraft(MAILSTREAM **, ENVELOPE **, BODY **, char **, char **, REPLY_S **,
+ REDRAFT_POS_S **, PINEFIELD **, ACTION_S **, int);
+int redraft_prompt(char *, char *, int);
+int check_for_subject(METAENV *);
+int check_for_fcc(char *);
+void free_prompts(PINEFIELD *);
+int postpone_prompt(void);
+METAENV *pine_simple_send_header(ENVELOPE *, char **, char ***);
+void call_mailer_file_result(char *, int);
+void mark_address_failure_for_pico(METAENV *);
+BODY_PARTICULARS_S
+ *save_body_particulars(BODY *);
+void reset_body_particulars(BODY_PARTICULARS_S *, BODY *);
+void free_body_particulars(BODY_PARTICULARS_S *);
+long message_format_for_pico(long, int (*)(int));
+int send_exit_for_pico(struct headerentry *, void (*)(void), int, char **);
+void new_thread_on_blank_subject(void);
+char *choose_a_priority(char *);
+int dont_flow_this_time(void);
+int mime_type_for_pico(char *);
+char *cancel_for_pico(void (*)(void));
+int filter_message_text(char *, ENVELOPE *, BODY *, STORE_S **, METAENV *);
+void pine_send_newsgroup_name(char *, char*, size_t);
+void outgoing2strings(METAENV *, BODY *, void **, PATMT **, int);
+void strings2outgoing(METAENV *, BODY **, PATMT *, int);
+void create_message_body_text(BODY *, int);
+void set_body_size(BODY *);
+int view_as_rich(char *, int);
+int background_posting(int);
+int valid_subject(char *, char **, char **,BUILDER_ARG *,int *);
+int build_addr_lcc(char *, char **, char **, BUILDER_ARG *, int *);
+int news_build(char *, char **, char **, BUILDER_ARG *, int *);
+void news_build_busy(void);
+#if defined(DOS) || defined(OS2)
+int dos_valid_from(void);
+#endif /* defined(DOS) || defined(OS2) */
+
+
+/*
+ * Pointer to buffer to hold pointers into pine data that's needed by pico.
+ */
+static PICO *pbf;
+
+
+static char *g_rolenick = NULL;
+
+
+static char *sending_filter_requested;
+static char background_requested, flowing_requested;
+static unsigned call_mailer_flags;
+static char *priority_requested;
+
+/* local global to save busy_cue state */
+static int news_busy_cue = 0;
+
+
+/*
+ * Various useful strings
+ */
+#define INTRPT_PMT \
+ _("Continue INTERRUPTED composition (answering \"n\" won't erase it)")
+#define PSTPND_PMT \
+ _("Continue postponed composition (answering \"No\" won't erase it)")
+#define FORM_PMT \
+ _("Start composition from Form Letter Folder")
+#define PSTPN_FORM_PMT \
+ _("Save to Postponed or Form letter folder? ")
+#define POST_PMT \
+ _("Posted message may go to thousands of readers. Really post")
+#define INTR_DEL_PMT \
+ _("Deleted messages will be removed from folder after use. Proceed")
+
+
+/*
+ * Macros to help sort out posting results
+ */
+#define P_MAIL_WIN 0x01
+#define P_MAIL_LOSE 0x02
+#define P_MAIL_BITS 0x03
+#define P_NEWS_WIN 0x04
+#define P_NEWS_LOSE 0x08
+#define P_NEWS_BITS 0x0C
+#define P_FCC_WIN 0x10
+#define P_FCC_LOSE 0x20
+#define P_FCC_BITS 0x30
+
+
+#define COMPOSE_MAIL_TITLE "COMPOSE MESSAGE"
+
+
+/*
+ * For check_for_subject and check_for_fcc
+ */
+#define CF_OK 0x1
+#define CF_MISSING 0x2
+
+
+/*----------------------------------------------------------------------
+ Compose screen (not forward or reply). Set up envelope, call composer
+
+ Args: pine_state -- The usual pine structure
+
+ Little front end for the compose screen
+ ---*/
+void
+compose_screen(struct pine *pine_state)
+{
+ void (*prev_screen)(struct pine *) = pine_state->prev_screen,
+ (*redraw)(void) = pine_state->redrawer;
+
+ pine_state->redrawer = NULL;
+ ps_global->next_screen = SCREEN_FUN_NULL;
+ mailcap_free(); /* free resources we won't be using for a while */
+ compose_mail(NULL, NULL, NULL, NULL, NULL);
+ pine_state->next_screen = prev_screen;
+ pine_state->redrawer = redraw;
+}
+
+
+/*----------------------------------------------------------------------
+ Alternate compose screen. Set up role and call regular compose.
+
+ Args: pine_state -- The usual pine structure
+ ---*/
+void
+alt_compose_screen(struct pine *pine_state)
+{
+ ACTION_S *role = NULL;
+ void (*prev_screen)(struct pine *) = pine_state->prev_screen,
+ (*redraw)(void) = pine_state->redrawer;
+
+ pine_state->redrawer = NULL;
+ ps_global->next_screen = SCREEN_FUN_NULL;
+ mailcap_free(); /* free resources we won't be using for a while */
+
+ /* Setup role */
+ if(role_select_screen(pine_state, &role, MC_COMPOSE) < 0){
+ cmd_cancelled("Composition");
+ pine_state->next_screen = prev_screen;
+ pine_state->redrawer = redraw;
+ return;
+ }
+
+ /*
+ * If default role was selected (NULL) we need to make up a role which
+ * won't do anything, but will cause compose_mail to think there's
+ * already a role so that it won't try to confirm the default.
+ */
+ if(role)
+ role = combine_inherited_role(role);
+ else{
+ role = (ACTION_S *)fs_get(sizeof(*role));
+ memset((void *)role, 0, sizeof(*role));
+ role->nick = cpystr("Default Role");
+ }
+
+ pine_state->redrawer = NULL;
+ compose_mail(NULL, NULL, role, NULL, NULL);
+ free_action(&role);
+ pine_state->next_screen = prev_screen;
+ pine_state->redrawer = redraw;
+}
+
+
+/*----------------------------------------------------------------------
+ Format envelope for outgoing message and call editor
+
+ Args: given_to -- An address to send mail to (usually from command line
+ invocation)
+ fcc_arg -- The fcc that goes with this address.
+
+ If a "To" line is given format that into the envelope and get ready to call
+ the composer
+ If there's a message postponed, offer to continue it, and set it up,
+ otherwise just fill in the outgoing envelope as blank.
+
+ NOTE: we ignore postponed and interrupted messages in nr mode
+ ----*/
+void
+compose_mail(char *given_to, char *fcc_arg, ACTION_S *role_arg,
+ PATMT *attach, gf_io_t inc_text_getc)
+{
+ BODY *body = NULL;
+ ENVELOPE *outgoing = NULL;
+ PINEFIELD *custom = NULL;
+ REPLY_S *reply = NULL;
+ REDRAFT_POS_S *redraft_pos = NULL;
+ ACTION_S *role = NULL;
+ MAILSTREAM *stream;
+ char *fcc_to_free,
+ *fcc = NULL,
+ *lcc = NULL,
+ *sig = NULL;
+ int fcc_is_sticky = 0,
+ to_is_sticky = 0,
+ intrptd = 0,
+ postponed = 0,
+ form = 0;
+
+ dprint((1,
+ "\n\n ---- COMPOSE SCREEN (not in pico yet) ----\n"));
+
+ /*-- Check for INTERRUPTED mail --*/
+ if(!role_arg && !(given_to || attach)){
+ char file_path[MAXPATH+1];
+
+ /* build filename and see if it exists. build_path creates
+ * an explicit local path name, so all c-client access is thru
+ * local drivers.
+ */
+ file_path[0] = '\0';
+ build_path(file_path,
+ ps_global->VAR_OPER_DIR ? ps_global->VAR_OPER_DIR
+ : ps_global->home_dir,
+ INTERRUPTED_MAIL, sizeof(file_path));
+
+ /* check to see if the folder exists, the user wants to continue
+ * and that we can actually read something in...
+ */
+ if(folder_exists(NULL, file_path) & FEX_ISFILE)
+ intrptd = 1;
+ }
+
+ /*-- Check for postponed mail --*/
+ if(!role_arg
+ && !outgoing /* not replying/forwarding */
+ && !(given_to || attach) /* not command line send */
+ && ps_global->VAR_POSTPONED_FOLDER /* folder to look in */
+ && ps_global->VAR_POSTPONED_FOLDER[0])
+ postponed = 1;
+
+ /*-- Check for form letter folder --*/
+ if(!role_arg
+ && !outgoing /* not replying/forwarding */
+ && !(given_to || attach) /* not command line send */
+ && ps_global->VAR_FORM_FOLDER /* folder to look in */
+ && ps_global->VAR_FORM_FOLDER[0])
+ form = 1;
+
+ if(!outgoing && !(given_to || attach)
+ && !role_arg && F_ON(F_ALT_COMPOSE_MENU, ps_global)){
+ char prompt[80];
+ char letters[30];
+ char chosen_task;
+ char *new = "New";
+ char *intrpt = "Interrupted";
+ char *postpnd = "Postponed";
+ char *formltr = "FormLetter";
+ char *roles = "setRole";
+ HelpType help = h_composer_browse;
+ ESCKEY_S compose_style[6];
+ unsigned which_help;
+ int ekey_num;
+
+ ekey_num = 0;
+ compose_style[ekey_num].ch = 'n';
+ compose_style[ekey_num].rval = 'n';
+ compose_style[ekey_num].name = "N";
+ compose_style[ekey_num++].label = new;
+
+ if(intrptd){
+ compose_style[ekey_num].ch = 'i';
+ compose_style[ekey_num].rval = 'i';
+ compose_style[ekey_num].name = "I";
+ compose_style[ekey_num++].label = intrpt;
+ }
+
+ if(postponed){
+ compose_style[ekey_num].ch = 'p';
+ compose_style[ekey_num].rval = 'p';
+ compose_style[ekey_num].name = "P";
+ compose_style[ekey_num++].label = postpnd;
+ }
+
+ if(form){
+ compose_style[ekey_num].ch = 'f';
+ compose_style[ekey_num].rval = 'f';
+ compose_style[ekey_num].name = "F";
+ compose_style[ekey_num++].label = formltr;
+ }
+
+ compose_style[ekey_num].ch = 'r';
+ compose_style[ekey_num].rval = 'r';
+ compose_style[ekey_num].name = "R";
+ compose_style[ekey_num++].label = roles;
+
+ compose_style[ekey_num].ch = -1;
+
+ if(F_ON(F_BLANK_KEYMENU,ps_global)){
+ char *p;
+
+ p = letters;
+ *p = '\0';
+ for(ekey_num = 0; compose_style[ekey_num].ch != -1; ekey_num++){
+ if(p - letters < sizeof(letters))
+ *p++ = (char) compose_style[ekey_num].ch;
+
+ if(compose_style[ekey_num + 1].ch != -1 && p - letters < sizeof(letters))
+ *p++ = ',';
+ }
+
+ if(p - letters < sizeof(letters))
+ *p = '\0';
+ }
+
+ which_help = intrptd + 2 * postponed + 4 * form;
+ switch(which_help){
+ case 1:
+ help = h_compose_intrptd;
+ break;
+ case 2:
+ help = h_compose_postponed;
+ break;
+ case 3:
+ help = h_compose_intrptd_postponed;
+ break;
+ case 4:
+ help = h_compose_form;
+ break;
+ case 5:
+ help = h_compose_intrptd_form;
+ break;
+ case 6:
+ help = h_compose_postponed_form;
+ break;
+ case 7:
+ help = h_compose_intrptd_postponed_form;
+ break;
+ default:
+ help = h_compose_default;
+ break;
+ }
+
+ snprintf(prompt, sizeof(prompt),
+ "Choose a compose method from %s : ",
+ F_ON(F_BLANK_KEYMENU,ps_global) ? letters : "the menu below");
+ prompt[sizeof(prompt)-1] = '\0';
+
+ chosen_task = radio_buttons(prompt, -FOOTER_ROWS(ps_global),
+ compose_style, 'n', 'x', help, RB_NORM);
+ intrptd = postponed = form = 0;
+
+ switch(chosen_task){
+ case 'i':
+ intrptd = 1;
+ break;
+ case 'p':
+ postponed = 1;
+ break;
+ case 'r':
+ {
+ void (*prev_screen)(struct pine *) = ps_global->prev_screen,
+ (*redraw)(void) = ps_global->redrawer;
+
+ ps_global->redrawer = NULL;
+ ps_global->next_screen = SCREEN_FUN_NULL;
+ if(role_select_screen(ps_global, &role, MC_COMPOSE) < 0){
+ cmd_cancelled("Composition");
+ ps_global->next_screen = prev_screen;
+ ps_global->redrawer = redraw;
+ return;
+ }
+
+ ps_global->next_screen = prev_screen;
+ ps_global->redrawer = redraw;
+ if(role)
+ role = combine_inherited_role(role);
+ }
+ break;
+
+ case 'f':
+ form = 1;
+ break;
+
+ case 'x':
+ q_status_message(SM_ORDER, 0, 3,
+ "Composition cancelled");
+ return;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if(intrptd && !outgoing){
+ char file_path[MAXPATH+1];
+ int ret = 'n';
+
+ file_path[0] = '\0';
+ build_path(file_path,
+ ps_global->VAR_OPER_DIR ? ps_global->VAR_OPER_DIR
+ : ps_global->home_dir,
+ INTERRUPTED_MAIL, sizeof(file_path));
+ if(folder_exists(NULL, file_path) & FEX_ISFILE){
+ if((stream = pine_mail_open(NULL, file_path,
+ SP_USEPOOL|SP_TEMPUSE, NULL))
+ && !stream->halfopen){
+
+ if(F_ON(F_ALT_COMPOSE_MENU, ps_global) ||
+ (ret = redraft_prompt("Interrupted",INTRPT_PMT,'n')) =='y'){
+ if(!redraft(&stream, &outgoing, &body, &fcc, &lcc, &reply,
+ &redraft_pos, &custom, &role, REDRAFT_DEL)){
+ if(stream)
+ pine_mail_close(stream);
+
+ return;
+ }
+
+ to_is_sticky++;
+
+ /* redraft() may or may not have closed stream */
+ if(stream)
+ pine_mail_close(stream);
+
+ postponed = form = 0;
+ }
+ else{
+ pine_mail_close(stream);
+ if(ret == 'x'){
+ q_status_message(SM_ORDER, 0, 3,
+ _("Composition cancelled"));
+ return;
+ }
+ }
+ }
+ else{
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("Can't open Interrupted mailbox: %s"),
+ file_path);
+ if(stream)
+ pine_mail_close(stream);
+ }
+ }
+ }
+
+ if(postponed && !outgoing){
+ int ret = 'n', done = 0;
+ int exists;
+
+ if((exists=postponed_stream(&stream,
+ ps_global->VAR_POSTPONED_FOLDER,
+ "Postponed", 0)) & FEX_ISFILE){
+ if(F_ON(F_ALT_COMPOSE_MENU, ps_global) ||
+ (ret = redraft_prompt("Postponed",PSTPND_PMT,'n')) == 'y'){
+ if(!redraft(&stream, &outgoing, &body, &fcc, &lcc, &reply,
+ &redraft_pos, &custom, &role,
+ REDRAFT_DEL | REDRAFT_PPND))
+ done++;
+
+ /* stream may or may not be closed in redraft() */
+ if(stream && (stream != ps_global->mail_stream))
+ pine_mail_close(stream);
+
+ to_is_sticky++;
+ intrptd = form = 0;
+ }
+ else{
+ if(stream != ps_global->mail_stream)
+ pine_mail_close(stream);
+
+ if(ret == 'x'){
+ q_status_message(SM_ORDER, 0, 3,
+ _("Composition cancelled"));
+ done++;
+ }
+ }
+ }
+ else if(F_ON(F_ALT_COMPOSE_MENU, ps_global))
+ done++;
+
+ if(done)
+ return;
+ }
+
+ if(form && !outgoing){
+ int ret = 'n', done = 0;
+ int exists;
+
+ if((exists=postponed_stream(&stream,
+ ps_global->VAR_FORM_FOLDER,
+ "Form letter", 1)) & FEX_ISFILE){
+ if(F_ON(F_ALT_COMPOSE_MENU, ps_global) ||
+ (ret = want_to(FORM_PMT,'y','x',NO_HELP,WT_NORM))=='y'){
+ if(!redraft(&stream, &outgoing, &body, &fcc, &lcc, &reply,
+ &redraft_pos, &custom, NULL, REDRAFT_NONE))
+ done++;
+
+ /* stream may or may not be closed in redraft() */
+ if(stream && (stream != ps_global->mail_stream))
+ pine_mail_close(stream);
+
+ to_is_sticky++;
+ intrptd = postponed = 0;
+ }
+ else{
+ if(stream != ps_global->mail_stream)
+ pine_mail_close(stream);
+
+ if(ret == 'x'){
+ q_status_message(SM_ORDER, 0, 3,
+ _("Composition cancelled"));
+ done++;
+ }
+ }
+ }
+ else{
+ if(F_ON(F_ALT_COMPOSE_MENU, ps_global)){
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("Form letter folder doesn't exist!"));
+ return;
+ }
+ }
+
+ if(done)
+ return;
+ }
+
+ /*-- normal composition --*/
+ if(!outgoing){
+ int impl, template_len = 0;
+ long rflags = ROLE_COMPOSE;
+ PAT_STATE dummy;
+
+ /*================= Compose new message ===============*/
+ body = mail_newbody();
+ outgoing = mail_newenvelope();
+
+ if(given_to)
+ rfc822_parse_adrlist(&outgoing->to, given_to, ps_global->maildomain);
+
+ outgoing->message_id = generate_message_id();
+
+ /*
+ * Setup possible role
+ */
+ if(role_arg)
+ role = copy_action(role_arg);
+
+ if(!role){
+ /* Setup possible compose role */
+ if(nonempty_patterns(rflags, &dummy)){
+ /*
+ * setup default role
+ * Msgno = -1 means there is no msg.
+ * This will match roles which have the Compose Use turned
+ * on, and have no patterns set, and match the Current
+ * Folder Type.
+ */
+ role = set_role_from_msg(ps_global, rflags, -1L, NULL);
+
+ if(confirm_role(rflags, &role))
+ role = combine_inherited_role(role);
+ else{ /* cancel reply */
+ role = NULL;
+ cmd_cancelled("Composition");
+ return;
+ }
+ }
+ }
+
+ if(role)
+ q_status_message1(SM_ORDER, 3, 4, _("Composing using role \"%s\""),
+ role->nick);
+
+ /*
+ * The type of storage object allocated below is vitally
+ * important. See SIMPLIFYING ASSUMPTION #37
+ */
+ if((body->contents.text.data = (void *) so_get(PicoText,
+ NULL, EDIT_ACCESS)) != NULL){
+ char ch;
+
+ if(inc_text_getc){
+ while((*inc_text_getc)(&ch))
+ if(!so_writec(ch, (STORE_S *)body->contents.text.data)){
+ break;
+ }
+ }
+ }
+ else{
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Problem creating space for message text."));
+ return;
+ }
+
+ if(role && role->template){
+ char *filtered;
+
+ impl = 1; /* leave cursor in header if not explicit */
+ filtered = detoken(role, NULL, 0, 0, 0, &redraft_pos, &impl);
+ if(filtered){
+ if(*filtered){
+ so_puts((STORE_S *)body->contents.text.data, filtered);
+ if(impl == 1)
+ template_len = strlen(filtered);
+ }
+
+ fs_give((void **)&filtered);
+ }
+ }
+ else
+ impl = 1;
+
+ if((sig = detoken(role, NULL, 2, 0, 1, &redraft_pos, &impl)) != NULL){
+ if(impl == 2)
+ redraft_pos->offset += template_len;
+
+ if(*sig)
+ so_puts((STORE_S *)body->contents.text.data, sig);
+
+ fs_give((void **)&sig);
+ }
+
+ body->type = TYPETEXT;
+
+ if(attach)
+ create_message_body(&body, attach, 0);
+ }
+
+ ps_global->prev_screen = compose_screen;
+ if(!(fcc_to_free = fcc) && !(role && role->fcc))
+ fcc = fcc_arg; /* Didn't pick up fcc, use given */
+
+ /*
+ * check whether a build_address-produced fcc is different from
+ * fcc. If same, do nothing, if different, set sticky bit in pine_send.
+ */
+ if(fcc){
+ char *tmp_fcc = NULL;
+
+ if(outgoing->to){
+ tmp_fcc = get_fcc_based_on_to(outgoing->to);
+ if(strcmp(fcc, tmp_fcc ? tmp_fcc : ""))
+ fcc_is_sticky++; /* cause sticky bit to get set */
+
+ }
+ else if((tmp_fcc = get_fcc(NULL)) != NULL &&
+ !strcmp(fcc, tmp_fcc)){
+ /* not sticky */
+ }
+ else
+ fcc_is_sticky++;
+
+ if(tmp_fcc)
+ fs_give((void **)&tmp_fcc);
+ }
+
+ pine_send(outgoing, &body, COMPOSE_MAIL_TITLE, role, fcc,
+ reply, redraft_pos, lcc, custom,
+ (fcc_is_sticky ? PS_STICKY_FCC : 0) | (to_is_sticky ? PS_STICKY_TO : 0));
+
+ if(reply){
+ if(reply->mailbox)
+ fs_give((void **) &reply->mailbox);
+ if(reply->origmbox)
+ fs_give((void **) &reply->origmbox);
+ if(reply->prefix)
+ fs_give((void **) &reply->prefix);
+ if(reply->data.uid.msgs)
+ fs_give((void **) &reply->data.uid.msgs);
+ fs_give((void **) &reply);
+ }
+
+ if(fcc_to_free)
+ fs_give((void **)&fcc_to_free);
+
+ if(lcc)
+ fs_give((void **)&lcc);
+
+ mail_free_envelope(&outgoing);
+ pine_free_body(&body);
+ free_redraft_pos(&redraft_pos);
+ free_action(&role);
+}
+
+
+/*----------------------------------------------------------------------
+ Args: stream -- This is where we get the postponed messages from
+ We'll expunge and close it here unless it is mail_stream.
+
+ These are all return values:
+ ================
+ outgoing --
+ body --
+ fcc --
+ lcc --
+ reply --
+ redraft_pos --
+ custom --
+ role --
+ ================
+
+ flags --
+
+ ----*/
+int
+redraft(MAILSTREAM **streamp, ENVELOPE **outgoing, struct mail_bodystruct **body,
+ char **fcc, char **lcc, REPLY_S **reply, REDRAFT_POS_S **redraft_pos,
+ PINEFIELD **custom, ACTION_S **role, int flags)
+{
+ MAILSTREAM *stream;
+ long cont_msg = 1L;
+ STORE_S *so;
+
+ if(!(streamp && *streamp))
+ return(0);
+
+ stream = *streamp;
+
+ /*
+ * If we're manipulating the current folder, don't bother
+ * with index
+ */
+ if(!stream->nmsgs){
+ if(REDRAFT_PPND&flags)
+ q_status_message(SM_ORDER | SM_DING, 3, 5, _("Empty folder! No messages really postponed!"));
+ else
+ q_status_message(SM_ORDER | SM_DING, 3, 5, _("Empty folder! No messages really interrupted!"));
+
+ return(redraft_cleanup(streamp, FALSE, flags));
+ }
+ else if(stream == ps_global->mail_stream
+ && ps_global->prev_screen == mail_index_screen){
+ /*
+ * Since the user's got this folder already opened and they're
+ * on a selected message, pick that one rather than rebuild
+ * another index screen...
+ */
+ cont_msg = mn_m2raw(ps_global->msgmap, mn_get_cur(ps_global->msgmap));
+ }
+ else if(stream->nmsgs > 1L){ /* offer browser ? */
+ int rv;
+
+ if(REDRAFT_PPND&flags){ /* set to last message postponed */
+ mn_set_cur(sp_msgmap(stream),
+ mn_get_revsort(sp_msgmap(stream))
+ ? 1L : mn_get_total(sp_msgmap(stream)));
+ }
+ else{ /* set to top form letter */
+ mn_set_cur(sp_msgmap(stream), 1L);
+ }
+
+ clear_index_cache(stream, 0);
+ while(1){
+ void *ti;
+
+ ti = stop_threading_temporarily();
+ rv = index_lister(ps_global, NULL, stream->mailbox,
+ stream, sp_msgmap(stream));
+ restore_threading(&ti);
+
+ cont_msg = mn_m2raw(sp_msgmap(stream), mn_get_cur(sp_msgmap(stream)));
+ if(count_flagged(stream, F_DEL)
+ && want_to(INTR_DEL_PMT, 'n', 0, NO_HELP, WT_NORM) == 'n'){
+ if(REDRAFT_PPND&flags)
+ q_status_message(SM_ORDER, 3, 3, _("Undelete messages to remain postponed, and then continue message"));
+ else
+ q_status_message(SM_ORDER, 3, 3, _("Undelete form letters you want to keep, and then continue message"));
+
+ continue;
+ }
+
+ break;
+ }
+
+ clear_index_cache(stream, 0);
+
+ if(rv){
+ q_status_message(SM_ORDER, 0, 3, _("Composition cancelled"));
+ (void) redraft_cleanup(streamp, FALSE, flags);
+
+ if(!*streamp && !ps_global->mail_stream){
+ q_status_message2(SM_ORDER, 3, 7,
+ "No more %.200s, returning to \"%.200s\"",
+ (REDRAFT_PPND&flags) ? "postponed messages"
+ : "form letters",
+ ps_global->inbox_name);
+ if(ps_global && ps_global->ttyo){
+ blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
+ ps_global->mangled_footer = 1;
+ }
+
+ do_broach_folder(ps_global->inbox_name,
+ ps_global->context_list, NULL, DB_INBOXWOCNTXT);
+
+ ps_global->next_screen = mail_index_screen;
+ }
+
+ return(0); /* special case */
+ }
+ }
+
+ if((so = (void *) so_get(PicoText, NULL, EDIT_ACCESS)) != NULL)
+ return(redraft_work(streamp, cont_msg, outgoing, body,
+ fcc, lcc, reply, redraft_pos, custom,
+ role, flags, so));
+ else
+ return(0);
+}
+
+
+int
+redraft_prompt(char *type, char *prompt, int failure)
+{
+ if(background_posting(FALSE)){
+ q_status_message1(SM_ORDER, 0, 3,
+ _("%s folder unavailable while background posting"),
+ type);
+ return(failure);
+ }
+
+ return(want_to(prompt, 'y', 'x', NO_HELP, WT_NORM));
+}
+
+
+/* this is for initializing the fixed header elements in pine_send() */
+/*
+prompt::name::help::prwid::maxlen::realaddr::
+builder::affected_entry::next_affected::selector::key_label::fileedit::
+display_it::break_on_comma::is_attach::rich_header::only_file_chars::
+single_space::sticky::dirty::start_here::blank::sticky_special::KS_ODATAVAR
+*/
+static struct headerentry he_template[]={
+ {"", "X-Auth-Received", NO_HELP, 10, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"From : ", "From", h_composer_from, 10, 0, NULL,
+ build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
+ 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK},
+ {"Reply-To: ", "Reply To", h_composer_reply_to, 10, 0, NULL,
+ build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
+ 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK},
+ {"To : ", "To", h_composer_to, 10, 0, NULL,
+ build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
+ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, KS_TOADDRBOOK},
+ {"Cc : ", "Cc", h_composer_cc, 10, 0, NULL,
+ build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
+ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK},
+ {"Bcc : ", "Bcc", h_composer_bcc, 10, 0, NULL,
+ build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
+ 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK},
+ {"Newsgrps: ", "Newsgroups", h_composer_news, 10, 0, NULL,
+ news_build, NULL, NULL, news_group_selector, "To NwsGrps", NULL, NULL,
+ 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"Fcc : ", "Fcc", h_composer_fcc, 10, 0, NULL,
+ NULL, NULL, NULL, folders_for_fcc, "To Fldrs", NULL, NULL,
+ 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"Lcc : ", "Lcc", h_composer_lcc, 10, 0, NULL,
+ build_addr_lcc, NULL, NULL, addr_book_compose_lcc,"To AddrBk", NULL, abook_nickname_complete,
+ 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"Attchmnt: ", "Attchmnt", h_composer_attachment, 10, 0, NULL,
+ NULL, NULL, NULL, NULL, "To Files", NULL, NULL,
+ 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, KS_NONE},
+ {"Subject : ", "Subject", h_composer_subject, 10, 0, NULL,
+ valid_subject, NULL, NULL, NULL, NULL, NULL, NULL,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"", "References", NO_HELP, 10, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"", "Date", NO_HELP, 10, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"", "In-Reply-To", NO_HELP, 10, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"", "Message-ID", NO_HELP, 10, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"", "X-Priority", NO_HELP, 10, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"", "User-Agent", NO_HELP, 10, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"", "To", NO_HELP, 10, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"", "X-Post-Error",NO_HELP, 10, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"", "X-Reply-UID", NO_HELP, 10, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"", "X-Reply-Mbox", NO_HELP, 10, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"", "X-SMTP-Server", NO_HELP, 10, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"", "X-Cursor-Pos", NO_HELP, 10, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"", "X-Our-ReplyTo", NO_HELP, 10, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"", OUR_HDRS_LIST, NO_HELP, 10, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+ {"", "X-Auth-Received", NO_HELP, 10, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE},
+#if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
+ {"", "Sender", NO_HELP, 10, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KS_NONE}
+#endif
+};
+
+
+static struct headerentry he_custom_addr_templ={
+ NULL, NULL, h_composer_custom_addr,10, 0, NULL,
+ build_address, NULL, NULL, addr_book_compose, "To AddrBk", NULL, abook_nickname_complete,
+ 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_TOADDRBOOK};
+
+static struct headerentry he_custom_free_templ={
+ NULL, NULL, h_composer_custom_free,10, 0, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, KS_NONE};
+
+
+/*----------------------------------------------------------------------
+ Get addressee for message, then post message
+
+ Args: outgoing -- Partially formatted outgoing ENVELOPE
+ body -- Body of outgoing message
+ prmpt_who -- Optional prompt for optionally_enter call
+ prmpt_cnf -- Optional prompt for confirmation call
+ used_tobufval -- The string that the to was eventually set equal to.
+ This gets passed back if non-NULL on entry.
+ flagsarg -- SS_PROMPTFORTO - Allow user to change recipient
+ SS_NULLRP - Use null return-path so we'll send an
+ SMTP MAIL FROM: <>
+
+ Result: message "To: " field is provided and message is sent or cancelled.
+
+ Fields:
+ remail -
+ return_path -
+ date added here
+ from added here
+ sender -
+ reply_to -
+ subject passed in, NOT edited but maybe canonized here
+ to possibly passed in, edited and canonized here
+ cc -
+ bcc -
+ in_reply_to -
+ message_id -
+
+Can only deal with message bodies that are either TYPETEXT or TYPEMULTIPART
+with the first part TYPETEXT! All newlines in the text here also end with
+CRLF.
+
+Returns 0 on success, -1 on failure.
+ ----*/
+int
+pine_simple_send(ENVELOPE *outgoing, /* envelope for outgoing message */
+ struct mail_bodystruct **body,
+ ACTION_S *role,
+ char *prmpt_who,
+ char *prmpt_cnf,
+ char **used_tobufval,
+ int flagsarg)
+{
+ char **tobufp, *p;
+ void *messagebuf;
+ int done = 0, retval = 0, x;
+ int lastrc, rc = 0, ku, i, resize_len, result, fcc_result;
+ int og2s_done = 0;
+ HelpType help;
+ static HISTORY_S *history = NULL;
+ ESCKEY_S ekey[5];
+ BUILDER_ARG ba_fcc;
+ METAENV *header;
+
+ dprint((1,"\n === simple send called === \n"));
+
+ memset(&ba_fcc, 0, sizeof(BUILDER_ARG));
+
+ init_hist(&history, HISTSIZE);
+
+ header = pine_simple_send_header(outgoing, &ba_fcc.tptr, &tobufp);
+
+ /*----- Fill in a few general parts of the envelope ----*/
+ if(!outgoing->date){
+ if(F_ON(F_QUELL_TIMEZONE, ps_global))
+ mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) TRUE);
+
+ rfc822_date(tmp_20k_buf); /* format and copy new date */
+ if(F_ON(F_QUELL_TIMEZONE, ps_global))
+ mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) FALSE);
+
+ outgoing->date = (unsigned char *) cpystr(tmp_20k_buf);
+ }
+
+ if(!outgoing->from){
+ if(role && role->from){
+ if(ps_global->never_allow_changing_from)
+ q_status_message(SM_ORDER, 3, 3, _("Site policy doesn't allow changing From address so role's From has no effect"));
+ else
+ outgoing->from = copyaddrlist(role->from);
+ }
+ else
+ outgoing->from = generate_from();
+ }
+
+ if(!(flagsarg & SS_NULLRP))
+ outgoing->return_path = rfc822_cpy_adr(outgoing->from);
+
+ ekey[i = 0].ch = ctrl('T');
+ ekey[i].rval = 2;
+ ekey[i].name = "^T";
+ ekey[i++].label = N_("To AddrBk");
+
+ if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
+ ekey[i].ch = ctrl('I');
+ ekey[i].rval = 11;
+ ekey[i].name = "TAB";
+ ekey[i++].label = N_("Complete");
+ }
+
+ ekey[i].ch = KEY_UP;
+ ekey[i].rval = 30;
+ ekey[i].name = "";
+ ku = i;
+ ekey[i++].label = "";
+
+ ekey[i].ch = KEY_DOWN;
+ ekey[i].rval = 31;
+ ekey[i].name = "";
+ ekey[i++].label = "";
+
+ ekey[i].ch = -1;
+
+ /*----------------------------------------------------------------------
+ Loop editing the "To: " field until everything goes well
+ ----*/
+ help = NO_HELP;
+
+ while(!done){
+ int flags;
+
+ if(!og2s_done){
+ og2s_done++;
+ outgoing2strings(header, *body, &messagebuf, NULL, 1);
+ }
+
+ lastrc = rc;
+ if(flagsarg & SS_PROMPTFORTO){
+ if(!*tobufp)
+ *tobufp = cpystr("");
+
+ resize_len = MAX(MAXPATH, strlen(*tobufp));
+ fs_resize((void **) tobufp, resize_len+1);
+
+ if(items_in_hist(history) > 0){
+ ekey[ku].name = HISTORY_UP_KEYNAME;
+ ekey[ku].label = HISTORY_KEYLABEL;
+ ekey[ku+1].name = HISTORY_DOWN_KEYNAME;
+ ekey[ku+1].label = HISTORY_KEYLABEL;
+ }
+ else{
+ ekey[ku].name = "";
+ ekey[ku].label = "";
+ ekey[ku+1].name = "";
+ ekey[ku+1].label = "";
+ }
+
+ flags = OE_APPEND_CURRENT;
+
+ rc = optionally_enter(*tobufp, -FOOTER_ROWS(ps_global),
+ 0, resize_len,
+ prmpt_who
+ ? prmpt_who
+ : outgoing->remail == NULL
+ ? _("FORWARD (as e-mail) to : ")
+ : _("BOUNCE (redirect) message to : "),
+ ekey, help, &flags);
+ }
+ else
+ rc = 0;
+
+ switch(rc){
+ case -1:
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ "Internal problem encountered");
+ retval = -1;
+ done++;
+ break;
+
+ case 30 :
+ if((p = get_prev_hist(history, *tobufp, 0, NULL)) != NULL){
+ strncpy(*tobufp, p, resize_len);
+ (*tobufp)[resize_len-1] = '\0';
+ }
+ else
+ Writechar(BELL, 0);
+
+ break;
+
+ case 31 :
+ if((p = get_next_hist(history, *tobufp, 0, NULL)) != NULL){
+ strncpy(*tobufp, p, resize_len);
+ (*tobufp)[resize_len-1] = '\0';
+ }
+ else
+ Writechar(BELL, 0);
+
+ break;
+
+ case 2: /* ^T */
+ case 0:
+ {void (*redraw) (void) = ps_global->redrawer;
+ char *returned_addr = NULL;
+ int len, l;
+
+ if(rc == 2){
+ int got_something = 0;
+
+ push_titlebar_state();
+ returned_addr = addr_book_bounce();
+
+ /*
+ * Just make it look like user typed this list in.
+ */
+ if(returned_addr){
+ got_something++;
+ if((l=resize_len) < (len = strlen(returned_addr)) + 1){
+ l = len;
+ fs_resize((void **) tobufp, (size_t) (l+1));
+ }
+
+ strncpy(*tobufp, returned_addr, l);
+ (*tobufp)[l] = '\0';
+ fs_give((void **)&returned_addr);
+ }
+
+ ClearScreen();
+ pop_titlebar_state();
+ redraw_titlebar();
+ if((ps_global->redrawer = redraw) != NULL) /* reset old value, and test */
+ (*ps_global->redrawer)();
+
+ if(!got_something)
+ continue;
+ }
+
+ if(*tobufp && **tobufp != '\0'){
+ char *errbuf, *addr;
+ int tolen;
+
+ save_hist(history, *tobufp, 0, NULL);
+
+ errbuf = NULL;
+
+ /*
+ * If role has an fcc, use it instead of what build_address
+ * tells us.
+ */
+ if(role && role->fcc){
+ if(ba_fcc.tptr)
+ fs_give((void **) &ba_fcc.tptr);
+
+ ba_fcc.tptr = cpystr(role->fcc);
+ }
+
+ if(build_address(*tobufp, &addr, &errbuf,
+ (role && role->fcc) ? NULL : &ba_fcc, NULL) >= 0){
+ int sendit = 0;
+
+ if(errbuf)
+ fs_give((void **)&errbuf);
+
+ if((l=strlen(*tobufp)) < (tolen = strlen(addr)) + 1){
+ l = tolen;
+ fs_resize((void **) tobufp, (size_t) (l+1));
+ }
+
+ strncpy(*tobufp, addr, l);
+ (*tobufp)[l] = '\0';
+ if(used_tobufval)
+ *used_tobufval = cpystr(addr);
+
+ /* confirm address */
+ if(flagsarg & SS_PROMPTFORTO){
+ char dsn_string[30];
+ int dsn_label = 0, dsn_show, i;
+ int verbose_label = 0;
+ ESCKEY_S opts[13];
+
+ strings2outgoing(header, body, NULL, 0);
+
+ if((flagsarg & SS_PROMPTFORTO)
+ && ((x = check_addresses(header)) == CA_BAD
+ || (x == CA_EMPTY && F_OFF(F_FCC_ON_BOUNCE,
+ ps_global))))
+ /*--- Addresses didn't check out---*/
+ continue;
+
+ i = 0;
+ opts[i].ch = 'y';
+ opts[i].rval = 'y';
+ opts[i].name = "Y";
+ opts[i++].label = N_("Yes");
+
+ opts[i].ch = 'n';
+ opts[i].rval = 'n';
+ opts[i].name = "N";
+ opts[i++].label = N_("No");
+
+ call_mailer_flags &= ~CM_VERBOSE; /* clear verbose */
+ if(F_ON(F_VERBOSE_POST, ps_global)){
+ /* setup keymenu slot to toggle verbose mode */
+ opts[i].ch = ctrl('W');
+ opts[i].rval = 12;
+ opts[i].name = "^W";
+ verbose_label = i++;
+ if(F_ON(F_DSN, ps_global)){
+ opts[i].ch = 0;
+ opts[i].rval = 0;
+ opts[i].name = "";
+ opts[i++].label = "";
+ }
+ }
+
+ /* clear DSN flags */
+ call_mailer_flags &= ~(CM_DSN_NEVER | CM_DSN_DELAY | CM_DSN_SUCCESS | CM_DSN_FULL);
+ if(F_ON(F_DSN, ps_global)){
+ /* setup keymenu slots to toggle dsn bits */
+ opts[i].ch = 'd';
+ opts[i].rval = 'd';
+ opts[i].name = "D";
+ opts[i].label = "DSNOpts";
+ dsn_label = i++;
+ opts[i].ch = -2;
+ opts[i].rval = 's';
+ opts[i].name = "S";
+ opts[i++].label = "";
+ opts[i].ch = -2;
+ opts[i].rval = 'x';
+ opts[i].name = "X";
+ opts[i++].label = "";
+ opts[i].ch = -2;
+ opts[i].rval = 'h';
+ opts[i].name = "H";
+ opts[i++].label = "";
+ }
+
+ opts[i].ch = -1;
+
+ while(1){
+ int rv;
+
+ dsn_show = (call_mailer_flags & CM_DSN_SHOW);
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,
+ "%s%s%s%s%s%sto \"%s\" ? ",
+ prmpt_cnf ? prmpt_cnf : "Send message ",
+ ((call_mailer_flags & CM_VERBOSE)
+ || (dsn_show))
+ ? "(" : "",
+ (call_mailer_flags & CM_VERBOSE)
+ ? "in verbose mode" : "",
+ (dsn_show && (call_mailer_flags & CM_VERBOSE))
+ ? ", " : "",
+ (dsn_show) ? dsn_string : "",
+ ((call_mailer_flags & CM_VERBOSE) || dsn_show)
+ ? ") " : "",
+ (addr && *addr)
+ ? addr
+ : (F_ON(F_FCC_ON_BOUNCE, ps_global)
+ && ba_fcc.tptr && ba_fcc.tptr[0])
+ ? ba_fcc.tptr
+ : "");
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+
+ if((strlen(tmp_20k_buf) >
+ ps_global->ttyo->screen_cols - 2) &&
+ ps_global->ttyo->screen_cols >= 7)
+ strncpy(tmp_20k_buf+ps_global->ttyo->screen_cols-7,
+ "...? ", SIZEOF_20KBUF-ps_global->ttyo->screen_cols-7);
+
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+
+ if(verbose_label)
+ opts[verbose_label].label =
+ /* TRANSLATORS: several possible key labels follow */
+ (call_mailer_flags & CM_VERBOSE) ? N_("Normal") : N_("Verbose");
+
+ if(F_ON(F_DSN, ps_global)){
+ if(call_mailer_flags & CM_DSN_SHOW){
+ opts[dsn_label].label =
+ (call_mailer_flags & CM_DSN_DELAY)
+ ? N_("NoDelay") : N_("Delay");
+ opts[dsn_label+1].ch = 's';
+ opts[dsn_label+1].label =
+ (call_mailer_flags & CM_DSN_SUCCESS)
+ ? N_("NoSuccess") : N_("Success");
+ opts[dsn_label+2].ch = 'x';
+ opts[dsn_label+2].label =
+ (call_mailer_flags & CM_DSN_NEVER)
+ ? N_("ErrRets") : N_("NoErrRets");
+ opts[dsn_label+3].ch = 'h';
+ opts[dsn_label+3].label =
+ (call_mailer_flags & CM_DSN_FULL)
+ ? N_("RetHdrs") : N_("RetFull");
+ }
+ }
+
+ rv = radio_buttons(tmp_20k_buf,
+ -FOOTER_ROWS(ps_global), opts,
+ 'y', 'z', NO_HELP, RB_NORM);
+ if(rv == 'y'){ /* user ACCEPTS! */
+ sendit = 1;
+ break;
+ }
+ else if(rv == 'n'){ /* Declined! */
+ break;
+ }
+ else if(rv == 'z'){ /* Cancelled! */
+ break;
+ }
+ else if(rv == 12){ /* flip verbose bit */
+ if(call_mailer_flags & CM_VERBOSE)
+ call_mailer_flags &= ~CM_VERBOSE;
+ else
+ call_mailer_flags |= CM_VERBOSE;
+ }
+ else if(call_mailer_flags & CM_DSN_SHOW){
+ if(rv == 's'){ /* flip success bit */
+ call_mailer_flags ^= CM_DSN_SUCCESS;
+ /* turn off related bits */
+ if(call_mailer_flags & CM_DSN_SUCCESS)
+ call_mailer_flags &= ~(CM_DSN_NEVER);
+ }
+ else if(rv == 'd'){ /* flip delay bit */
+ call_mailer_flags ^= CM_DSN_DELAY;
+ /* turn off related bits */
+ if(call_mailer_flags & CM_DSN_DELAY)
+ call_mailer_flags &= ~(CM_DSN_NEVER);
+ }
+ else if(rv == 'x'){ /* flip never bit */
+ call_mailer_flags ^= CM_DSN_NEVER;
+ /* turn off related bits */
+ if(call_mailer_flags & CM_DSN_NEVER)
+ call_mailer_flags &= ~(CM_DSN_SUCCESS | CM_DSN_DELAY);
+ }
+ else if(rv == 'h'){ /* flip full bit */
+ call_mailer_flags ^= CM_DSN_FULL;
+ }
+ }
+ else if(rv == 'd'){ /* show dsn options */
+ call_mailer_flags |= (CM_DSN_SHOW | CM_DSN_SUCCESS | CM_DSN_DELAY | CM_DSN_FULL);
+ }
+
+ snprintf(dsn_string, sizeof(dsn_string), _("DSN requested[%s%s%s%s]"),
+ (call_mailer_flags & CM_DSN_NEVER)
+ ? _("Never") : "F",
+ (call_mailer_flags & CM_DSN_DELAY)
+ ? "D" : "",
+ (call_mailer_flags & CM_DSN_SUCCESS)
+ ? "S" : "",
+ (call_mailer_flags & CM_DSN_NEVER)
+ ? ""
+ : (call_mailer_flags & CM_DSN_FULL) ? "-Full"
+ : "-Hdrs");
+ dsn_string[sizeof(dsn_string)-1] = '\0';
+ }
+ }
+
+ if(addr)
+ fs_give((void **)&addr);
+
+ if(!(flagsarg & SS_PROMPTFORTO) || sendit){
+ char *fcc = NULL;
+ CONTEXT_S *fcc_cntxt = NULL;
+
+ if(F_ON(F_FCC_ON_BOUNCE, ps_global)){
+ if(ba_fcc.tptr)
+ fcc = cpystr(ba_fcc.tptr);
+
+ set_last_fcc(fcc);
+
+ /*
+ * If special name "inbox" then replace it with the
+ * real inbox path.
+ */
+ if(ps_global->VAR_INBOX_PATH
+ && strucmp(fcc, ps_global->inbox_name) == 0){
+ char *replace_fcc;
+
+ replace_fcc = cpystr(ps_global->VAR_INBOX_PATH);
+ fs_give((void **) &fcc);
+ fcc = replace_fcc;
+ }
+ }
+
+ /*---- Check out fcc -----*/
+ if(fcc && *fcc){
+ (void) commence_fcc(fcc, &fcc_cntxt, FALSE);
+ if(!lmc.so){
+ dprint((4,"can't open fcc, cont\n"));
+ if(!(flagsarg & SS_PROMPTFORTO)){
+ retval = -1;
+ fs_give((void **)&fcc);
+ fcc = NULL;
+ goto finish;
+ }
+ else
+ continue;
+ }
+ else
+ so_truncate(lmc.so, fcc_size_guess(*body) + 2048);
+ }
+ else
+ lmc.so = NULL;
+
+ if(!(outgoing->to || outgoing->cc || outgoing->bcc
+ || lmc.so)){
+ q_status_message(SM_ORDER, 3, 5, _("No recipients specified!"));
+ continue;
+ }
+
+ if(outgoing->to || outgoing->cc || outgoing->bcc){
+ char **alt_smtp = NULL;
+
+ if(role && role->smtp){
+ if(ps_global->FIX_SMTP_SERVER
+ && ps_global->FIX_SMTP_SERVER[0])
+ q_status_message(SM_ORDER | SM_DING, 5, 5, _("Use of a role-defined smtp-server is administratively prohibited"));
+ else
+ alt_smtp = role->smtp;
+ }
+
+ result = call_mailer(header, *body, alt_smtp,
+ call_mailer_flags,
+ call_mailer_file_result,
+ pipe_callback);
+ mark_address_failure_for_pico(header);
+ }
+ else
+ result = 0;
+
+ if(result == 1 && !lmc.so)
+ q_status_message(SM_ORDER, 0, 3, _("Message sent"));
+
+ /*----- Was there an fcc involved? -----*/
+ if(lmc.so){
+ if(result == 1
+ || (result == 0
+ && pine_rfc822_output(header, *body, NULL, NULL))){
+ char label[50];
+
+ strncpy(label, "Fcc", sizeof(label));
+ label[sizeof(label)-1] = '\0';
+ if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC)){
+ snprintf(label + 3, sizeof(label)-3, " to %s", fcc);
+ label[sizeof(label)-1] = '\0';
+ }
+
+ /* Now actually copy to fcc folder and close */
+ fcc_result =
+ write_fcc(fcc, fcc_cntxt, lmc.so, NULL,
+ label,
+ F_ON(F_MARK_FCC_SEEN, ps_global)
+ ? "\\SEEN" : NULL);
+ }
+ else if(result == 0){
+ q_status_message(SM_ORDER,3,5,
+ _("Fcc Failed!. No message saved."));
+ retval = -1;
+ dprint((1, "explicit fcc write failed!\n"));
+ }
+
+ so_give(&lmc.so);
+ }
+
+ if(result < 0){
+ dprint((1, "Bounce failed\n"));
+ if(!(flagsarg & SS_PROMPTFORTO))
+ retval = -1;
+ else
+ continue;
+ }
+ else if(result == 1){
+ if(!fcc)
+ q_status_message(SM_ORDER, 0, 3,
+ _("Message sent"));
+ else{
+ int avail = ps_global->ttyo->screen_cols-2;
+ int need, fcclen;
+ char *part1 = "Message sent and ";
+ char *part2 = fcc_result ? "" : "NOT ";
+ char *part3 = "copied to ";
+ fcclen = strlen(fcc);
+
+ need = 2 + strlen(part1) + strlen(part2) +
+ strlen(part3) + fcclen;
+
+ if(need > avail && fcclen > 6)
+ fcclen -= MIN(fcclen-6, need-avail);
+
+ q_status_message4(SM_ORDER, 0, 3,
+ "%s%s%s\"%s\"",
+ part1, part2, part3,
+ short_str(fcc,
+ (char *)tmp_20k_buf,
+ SIZEOF_20KBUF,
+ fcclen, FrontDots));
+ }
+ }
+
+ if(fcc)
+ fs_give((void **)&fcc);
+ }
+ else{
+ q_status_message(SM_ORDER, 0, 3, _("Send cancelled"));
+ retval = -1;
+ }
+ }
+ else{
+ q_status_message1(SM_ORDER | SM_DING, 3, 5,
+ _("Error in address: %s"), errbuf);
+ if(errbuf)
+ fs_give((void **)&errbuf);
+
+ if(!(flagsarg & SS_PROMPTFORTO))
+ retval = -1;
+ else
+ continue;
+ }
+
+ }
+ else{
+ q_status_message(SM_ORDER | SM_DING, 3, 5,
+ _("No addressee! No e-mail sent."));
+ retval = -1;
+ }
+ }
+
+ done++;
+ break;
+
+ case 1:
+ q_status_message(SM_ORDER, 0, 3, _("Send cancelled"));
+ done++;
+ retval = -1;
+ break;
+
+ case 3:
+ help = (help == NO_HELP)
+ ? (outgoing->remail == NULL
+ ? h_anon_forward
+ : h_bounce)
+ : NO_HELP;
+ break;
+
+ case 11:
+ if(**tobufp){
+ char *new_nickname = NULL;
+ int l;
+ int ambiguity;
+
+ ambiguity = abook_nickname_complete(*tobufp, &new_nickname,
+ (lastrc==rc && !(flags & OE_USER_MODIFIED)), ANC_AFTERCOMMA);
+ if(new_nickname){
+ if(*new_nickname){
+ if((l=strlen(new_nickname)) > resize_len){
+ resize_len = l;
+ fs_resize((void **) tobufp, resize_len+1);
+ }
+
+ strncpy(*tobufp, new_nickname, l);
+ (*tobufp)[l] = '\0';
+ }
+
+ fs_give((void **) &new_nickname);
+ }
+
+ if(ambiguity != 2)
+ Writechar(BELL, 0);
+ }
+
+ break;
+
+ case 4: /* can't suspend */
+ default:
+ break;
+ }
+ }
+
+finish:
+ if(ba_fcc.tptr)
+ fs_give((void **)&ba_fcc.tptr);
+
+ pine_free_env(&header);
+
+ return(retval);
+}
+
+
+/*
+ * pine_simple_send_header - generate header suitable for simple_sending
+ */
+METAENV *
+pine_simple_send_header(ENVELOPE *outgoing, char **fccp, char ***tobufpp)
+{
+ METAENV *header;
+ PINEFIELD *pf;
+ static struct headerentry he_dummy;
+
+ header = pine_new_env(outgoing, fccp, tobufpp, NULL);
+
+ /* assign he_dummy to "To:" field "he" for strings2outgoing */
+ for(pf = header->local; pf && pf->name; pf = pf->next)
+ if(pf->type == Address && !strucmp(pf->name, "to")){
+ memset((void *) &he_dummy, 0, sizeof(he_dummy));
+ pf->extdata = (void *) &he_dummy;
+ HE(pf)->dirty = 1;
+ break;
+ }
+
+ return(header);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Prepare data structures for pico, call pico, then post message
+
+ Args: outgoing -- Partially formatted outgoing ENVELOPE
+ body -- Body of outgoing message
+ editor_title -- Title for anchor line in composer
+ fcc_arg -- The file carbon copy field
+ reply -- Struct describing set of msgs being replied-to
+ lcc_arg --
+ custom -- custom header list.
+ sticky_fcc --
+
+ Result: message is edited, then postponed, cancelled or sent.
+
+ Fields:
+ remail -
+ return_path -
+ date added here
+ from added here
+ sender -
+ reply_to -
+ subject passed in, edited and cannonized here
+ to possibly passed in, edited and cannonized here
+ cc possibly passed in, edited and cannonized here
+ bcc edited and cannonized here
+ in_reply_to generated in reply() and passed in
+ message_id -
+
+ Storage for these fields comes from anywhere outside. It is remalloced
+ here so the composer can realloc them if needed. The copies here are also
+ freed here.
+
+Can only deal with message bodies that are either TYPETEXT or TYPEMULTIPART
+with the first part TYPETEXT! All newlines in the text here also end with
+CRLF.
+
+There's a further assumption that the text in the TYPETEXT part is
+stored in a storage object (see filter.c).
+ ----*/
+void
+pine_send(ENVELOPE *outgoing, struct mail_bodystruct **body,
+ char *editor_title, ACTION_S *role, char *fcc_arg,
+ REPLY_S *reply, REDRAFT_POS_S *redraft_pos, char *lcc_arg,
+ PINEFIELD *custom, int flags)
+{
+ int i, fixed_cnt, total_cnt, index,
+ editor_result = 0, body_start = 0, use_news_order = 0;
+ char *p, *addr, *fcc, *fcc_to_free = NULL;
+ char *start_here_name = NULL;
+ char *suggested_nntp_server = NULL;
+ char *title = NULL;
+ struct headerentry *he, *headents, *he_to, *he_fcc, *he_news = NULL, *he_lcc = NULL,
+ *he_from = NULL;
+ PINEFIELD *pfields, *pf, *pf_nobody = NULL, *pf_to = NULL,
+ *pf_smtp_server, *pf_nntp_server,
+ *pf_fcc = NULL, *pf_err, *pf_uid, *pf_mbox, *pf_curpos,
+ *pf_ourrep, *pf_ourhdrs, **sending_order;
+ METAENV header;
+ ADDRESS *lcc_addr = NULL;
+ ADDRESS *nobody_addr = NULL;
+ BODY_PARTICULARS_S *bp;
+ STORE_S *orig_so = NULL;
+ PICO pbuf1, *save_previous_pbuf;
+ CustomType ct;
+ REDRAFT_POS_S *local_redraft_pos = NULL;
+
+ dprint((1,"\n=== send called ===\n"));
+
+ save_previous_pbuf = pbf;
+ pbf = &pbuf1;
+ standard_picobuf_setup(pbf);
+
+ /*
+ * Cancel any pending initial commands since pico uses a different
+ * input routine. If we didn't cancel them, they would happen after
+ * we returned from the editor, which would be confusing.
+ */
+ if(ps_global->in_init_seq){
+ ps_global->in_init_seq = 0;
+ ps_global->save_in_init_seq = 0;
+ clear_cursor_pos();
+ if(ps_global->initial_cmds){
+ if(ps_global->free_initial_cmds)
+ fs_give((void **)&(ps_global->free_initial_cmds));
+
+ ps_global->initial_cmds = 0;
+ }
+
+ F_SET(F_USE_FK,ps_global,ps_global->orig_use_fkeys);
+ }
+
+#if defined(DOS) || defined(OS2)
+ if(!dos_valid_from()){
+ pbf = save_previous_pbuf;
+ return;
+ }
+
+ pbf->upload = NULL;
+#else
+ pbf->upload = (ps_global->VAR_UPLOAD_CMD
+ && ps_global->VAR_UPLOAD_CMD[0])
+ ? upload_msg_to_pico : NULL;
+#endif
+
+ pbf->msgntext = message_format_for_pico;
+ pbf->mimetype = mime_type_for_pico;
+ pbf->exittest = send_exit_for_pico;
+ pbf->user_says_noflow = dont_flow_this_time;
+ pbf->newthread = new_thread_on_blank_subject;
+ ps_global->newthread = 0; /* reset this value */
+ if(F_OFF(F_CANCEL_CONFIRM, ps_global))
+ pbf->canceltest = cancel_for_pico;
+
+ pbf->alt_ed = (ps_global->VAR_EDITOR && ps_global->VAR_EDITOR[0] &&
+ ps_global->VAR_EDITOR[0][0])
+ ? ps_global->VAR_EDITOR : NULL;
+ pbf->alt_spell = (ps_global->VAR_SPELLER && ps_global->VAR_SPELLER[0])
+ ? ps_global->VAR_SPELLER : NULL;
+ pbf->always_spell_check = F_ON(F_ALWAYS_SPELL_CHECK, ps_global);
+ pbf->quote_str = reply && reply->prefix ? reply->prefix : "> ";
+ /* We actually want to set this only if message we're sending is flowed */
+ pbf->strip_ws_before_send = F_ON(F_STRIP_WS_BEFORE_SEND, ps_global);
+ pbf->allow_flowed_text = (F_OFF(F_QUELL_FLOWED_TEXT, ps_global)
+ && F_OFF(F_STRIP_WS_BEFORE_SEND, ps_global)
+ && (strcmp(pbf->quote_str, "> ") == 0
+ || strcmp(pbf->quote_str, ">") == 0));
+ pbf->edit_offset = 0;
+ title = cpystr(set_titlebar(editor_title,
+ ps_global->mail_stream,
+ ps_global->context_current,
+ ps_global->cur_folder,ps_global->msgmap,
+ 0, FolderName, 0, 0, NULL));
+ pbf->pine_anchor = title;
+
+#if defined(DOS) || defined(OS2)
+ if(!pbf->oper_dir && ps_global->VAR_FILE_DIR){
+ pbf->oper_dir = ps_global->VAR_FILE_DIR;
+ }
+#endif
+
+ if(redraft_pos && editor_title && !strcmp(editor_title, COMPOSE_MAIL_TITLE))
+ pbf->pine_flags |= P_CHKPTNOW;
+
+ /* NOTE: initial cursor position set below */
+
+ dprint((9, "flags: %x\n", pbf->pine_flags));
+
+ /*
+ * When user runs compose and the current folder is a newsgroup,
+ * offer to post to the current newsgroup.
+ */
+ if(!(outgoing->to || (outgoing->newsgroups && *outgoing->newsgroups))
+ && IS_NEWS(ps_global->mail_stream)){
+ char prompt[200], news_group[MAILTMPLEN];
+
+ pine_send_newsgroup_name(ps_global->mail_stream->mailbox, news_group,
+ sizeof(news_group));
+
+ /*
+ * Replies don't get this far because To or Newsgroups will already
+ * be filled in. So must be either ordinary compose or forward.
+ * Forward sets subject, so use that to tell the difference.
+ */
+ if(news_group[0] && !outgoing->subject){
+ int ch = 'y';
+ int ret_val;
+ char *errmsg = NULL;
+ BUILDER_ARG *fcc_build = NULL;
+
+ if(F_OFF(F_COMPOSE_TO_NEWSGRP,ps_global)){
+ snprintf(prompt, sizeof(prompt),
+ _("Post to current newsgroup (%s)"), news_group);
+ prompt[sizeof(prompt)-1] = '\0';
+ ch = want_to(prompt, 'y', 'x', NO_HELP, WT_NORM);
+ }
+
+ switch(ch){
+ case 'y':
+ if(outgoing->newsgroups)
+ fs_give((void **)&outgoing->newsgroups);
+
+ if(!fcc_arg && !(role && role->fcc)){
+ fcc_build = (BUILDER_ARG *)fs_get(sizeof(BUILDER_ARG));
+ memset((void *)fcc_build, 0, sizeof(BUILDER_ARG));
+ fcc_build->tptr = fcc_to_free;
+ }
+
+ ret_val = news_build(news_group, &outgoing->newsgroups,
+ &errmsg, fcc_build, NULL);
+
+ if(ret_val == -1){
+ if(outgoing->newsgroups)
+ fs_give((void **)&outgoing->newsgroups);
+
+ outgoing->newsgroups = cpystr(news_group);
+ }
+
+ if(!fcc_arg && !(role && role->fcc)){
+ fcc_arg = fcc_to_free = fcc_build->tptr;
+ fs_give((void **)&fcc_build);
+ }
+
+ if(errmsg){
+ if(*errmsg){
+ q_status_message(SM_ORDER, 3, 3, errmsg);
+ display_message(NO_OP_COMMAND);
+ }
+
+ fs_give((void **)&errmsg);
+ }
+
+ break;
+
+ case 'x': /* ^C */
+ q_status_message(SM_ORDER, 0, 3, _("Message cancelled"));
+ dprint((4, "=== send: cancelled\n"));
+ pbf = save_previous_pbuf;
+ return;
+
+ case 'n':
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ if(F_ON(F_PREDICT_NNTP_SERVER, ps_global)
+ && outgoing->newsgroups && *outgoing->newsgroups
+ && IS_NEWS(ps_global->mail_stream)){
+ NETMBX news_mb;
+
+ if(mail_valid_net_parse(ps_global->mail_stream->original_mailbox,
+ &news_mb))
+ if(!strucmp(news_mb.service, "nntp")){
+ if(*ps_global->mail_stream->original_mailbox == '{'){
+ char *svcp = NULL, *psvcp;
+
+ suggested_nntp_server =
+ cpystr(ps_global->mail_stream->original_mailbox + 1);
+ if((p = strindex(suggested_nntp_server, '}')) != NULL)
+ *p = '\0';
+ for(p = strindex(suggested_nntp_server, '/'); p && *p;
+ p = strindex(p, '/')){
+ /* take out /nntp, which gets added in nntp_open */
+ if(!struncmp(p, "/nntp", 5))
+ svcp = p + 5;
+ else if(!struncmp(p, "/service=nntp", 13))
+ svcp = p + 13;
+ else if(!struncmp(p, "/service=\"nntp\"", 15))
+ svcp = p + 15;
+ else
+ p++;
+ if(svcp){
+ if(*svcp == '\0')
+ *p = '\0';
+ else if(*svcp == '/' || *svcp == ':'){
+ for(psvcp = p; *svcp; svcp++, psvcp++)
+ *psvcp = *svcp;
+ *psvcp = '\0';
+ }
+ svcp = NULL;
+ }
+ }
+ }
+ else
+ suggested_nntp_server = cpystr(news_mb.orighost);
+ }
+ }
+
+ /*
+ * If we don't already have custom headers set and the role has custom
+ * headers, then incorporate those custom headers into "custom".
+ */
+ if(!custom){
+ PINEFIELD *dflthdrs = NULL, *rolehdrs = NULL;
+
+ dflthdrs = parse_custom_hdrs(ps_global->VAR_CUSTOM_HDRS, UseAsDef);
+/*
+ * If we allow the Combine argument here, we're saying that we want to
+ * combine the values from the envelope and the role for the fields To,
+ * Cc, Bcc, and Newsgroups. For example, if we are replying to a message
+ * we'll have a To in the envelope because we're replying. If our role also
+ * has a To action, then Combine would combine those two and offer both
+ * to the user. We've decided against doing this. Instead, we always use
+ * Replace, and the role's header value replaces the value from the
+ * envelope. It might also make sense in some cases to do the opposite,
+ * which would be treating the role headers as defaults, just like
+ * customized-hdrs.
+ */
+#ifdef WANT_TO_COMBINE_ADDRESSES
+ if(role && role->cstm)
+ rolehdrs = parse_custom_hdrs(role->cstm, Combine);
+#else
+ if(role && role->cstm)
+ rolehdrs = parse_custom_hdrs(role->cstm, Replace);
+#endif
+
+ if(rolehdrs){
+ custom = combine_custom_headers(dflthdrs, rolehdrs);
+ if(dflthdrs){
+ free_prompts(dflthdrs);
+ free_customs(dflthdrs);
+ }
+
+ if(rolehdrs){
+ free_prompts(rolehdrs);
+ free_customs(rolehdrs);
+ }
+ }
+ else
+ custom = dflthdrs;
+ }
+
+ g_rolenick = role ? role->nick : NULL;
+
+ /* how many fixed fields are there? */
+ for(fixed_cnt = 0; pf_template && pf_template[fixed_cnt].name; fixed_cnt++)
+ ;
+
+ total_cnt = fixed_cnt + count_custom_hdrs_pf(custom,1);
+
+ /* the fixed part of the PINEFIELDs */
+ i = fixed_cnt * sizeof(PINEFIELD);
+ pfields = (PINEFIELD *)fs_get((size_t) i);
+ memset(pfields, 0, (size_t) i);
+
+ /* temporary headerentry array for pico */
+ i = (total_cnt + 1) * sizeof(struct headerentry);
+ headents = (struct headerentry *)fs_get((size_t) i);
+ memset(headents, 0, (size_t) i);
+
+ i = total_cnt * sizeof(PINEFIELD *);
+ sending_order = (PINEFIELD **)fs_get((size_t) i);
+ memset(sending_order, 0, (size_t) i);
+
+ pbf->headents = headents;
+ header.env = outgoing;
+ header.local = pfields;
+ header.sending_order = sending_order;
+
+ /* custom part of PINEFIELDs */
+ header.custom = custom;
+
+ he = headents;
+ pf = pfields;
+
+ /*
+ * For Address types, pf->addr points to an ADDRESS *.
+ * If that address is in the "outgoing" envelope, it will
+ * be freed by the caller, otherwise, it should be freed here.
+ * Pf->textbuf for an Address is used a little to set up a default,
+ * but then is freed right away below. Pf->scratch is used for a
+ * pointer to some alloced space for pico to edit in. Addresses in
+ * the custom area are freed by free_customs().
+ *
+ * For FreeText types, pf->addr is not used. Pf->text points to a
+ * pointer that points to the text. Pf->textbuf points to a copy of
+ * the text that must be freed before we leave, otherwise, it is
+ * probably a pointer into the envelope and that gets freed by the
+ * caller.
+ *
+ * He->realaddr is the pointer to the text that pico actually edits.
+ */
+
+#if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
+# define NN 4
+#else
+# define NN 3
+#endif
+
+ if(outgoing->newsgroups && *outgoing->newsgroups)
+ use_news_order++;
+
+ /* initialize the fixed header elements of the two temp arrays */
+ for(i=0; i < fixed_cnt; i++, pf++){
+ static int news_order[] = {
+ N_AUTHRCVD,N_FROM, N_REPLYTO, N_NEWS, N_TO, N_CC, N_BCC,
+ N_FCC, N_LCC, N_ATTCH, N_SUBJ, N_REF, N_DATE, N_INREPLY,
+ N_MSGID, N_PRIORITY, N_USERAGENT, N_NOBODY, N_POSTERR, N_RPLUID, N_RPLMBOX,
+ N_SMTP, N_NNTP, N_CURPOS, N_OURREPLYTO, N_OURHDRS
+#if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
+ , N_SENDER
+#endif
+ };
+
+ index = i;
+ /* slightly different editing order if sending to news */
+ if(use_news_order &&
+ index >= 0 && index < sizeof(news_order)/sizeof(news_order[0]))
+ index = news_order[i];
+
+ /* copy the templates */
+ *he = he_template[index];
+
+ pf->name = cpystr(pf_template[index].name);
+ if(index == N_SENDER && F_ON(F_USE_SENDER_NOT_X, ps_global))
+ /* slide string over so it is Sender instead of X-X-Sender */
+ for(p=pf->name; *(p+1); p++)
+ *p = *(p+4);
+
+ pf->type = pf_template[index].type;
+ pf->canedit = pf_template[index].canedit;
+ pf->rcptto = pf_template[index].rcptto;
+ pf->writehdr = pf_template[index].writehdr;
+ pf->localcopy = pf_template[index].localcopy;
+ pf->extdata = he;
+ pf->next = pf + 1;
+
+ he->rich_header = view_as_rich(pf->name, he->rich_header);
+ if(F_OFF(F_ENABLE_TAB_COMPLETE,ps_global))
+ he->nickcmpl = NULL;
+
+ switch(pf->type){
+ case FreeText: /* realaddr points to c-client env */
+ if(index == N_NEWS){
+ sending_order[1] = pf;
+ he->realaddr = &outgoing->newsgroups;
+ he_news = he;
+
+ switch(set_default_hdrval(pf, custom)){
+ case Replace:
+ if(*he->realaddr)
+ fs_give((void **)he->realaddr);
+
+ *he->realaddr = pf->textbuf;
+ pf->textbuf = NULL;
+ he->sticky = 1;
+ break;
+
+ case Combine:
+ if(*he->realaddr){ /* combine values */
+ if(pf->textbuf && *pf->textbuf){
+ char *combined_hdr;
+ size_t l;
+
+ l = strlen(*he->realaddr) + strlen(pf->textbuf) + 1;
+ combined_hdr = (char *) fs_get((l+1) * sizeof(char));
+ strncpy(combined_hdr, *he->realaddr, l);
+ combined_hdr[l] = '\0';
+ strncat(combined_hdr, ",", l+1-1-strlen(combined_hdr));
+ combined_hdr[l] = '\0';
+ strncat(combined_hdr, pf->textbuf, l+1-1-strlen(combined_hdr));
+ combined_hdr[l] = '\0';
+
+ fs_give((void **)he->realaddr);
+ *he->realaddr = combined_hdr;
+ q_status_message(SM_ORDER, 3, 3,
+ "Adding newsgroup from role");
+ he->sticky = 1;
+ }
+ }
+ else{
+ *he->realaddr = pf->textbuf;
+ pf->textbuf = NULL;
+ }
+
+ break;
+
+ case UseAsDef:
+ /* if no value, use default */
+ if(!*he->realaddr){
+ *he->realaddr = pf->textbuf;
+ pf->textbuf = NULL;
+ }
+
+ break;
+
+ case NoMatch:
+ break;
+ }
+
+ /* If there is a newsgroup, we'd better show it */
+ if(outgoing->newsgroups && *outgoing->newsgroups)
+ he->rich_header = 0; /* force on by default */
+
+ if(pf->textbuf)
+ fs_give((void **)&pf->textbuf);
+
+ pf->text = he->realaddr;
+ }
+ else if(index == N_DATE){
+ sending_order[2] = pf;
+ pf->text = (char **) &outgoing->date;
+ pf->extdata = NULL;
+ }
+ else if(index == N_INREPLY){
+ sending_order[NN+9] = pf;
+ pf->text = &outgoing->in_reply_to;
+ pf->extdata = NULL;
+ }
+ else if(index == N_MSGID){
+ sending_order[NN+10] = pf;
+ pf->text = &outgoing->message_id;
+ pf->extdata = NULL;
+ }
+ else if(index == N_REF){
+ sending_order[NN+11] = pf;
+ pf->text = &outgoing->references;
+ pf->extdata = NULL;
+ }
+ else if(index == N_PRIORITY){
+ sending_order[NN+12] = pf;
+ pf->text = &pf->textbuf;
+ pf->extdata = NULL;
+ }
+ else if(index == N_USERAGENT){
+ sending_order[NN+13] = pf;
+ pf->text = &pf->textbuf;
+ pf->textbuf = generate_user_agent();
+ pf->extdata = NULL;
+ }
+ else if(index == N_POSTERR){
+ sending_order[NN+14] = pf;
+ pf_err = pf;
+ pf->text = &pf->textbuf;
+ pf->extdata = NULL;
+ }
+ else if(index == N_RPLUID){
+ sending_order[NN+15] = pf;
+ pf_uid = pf;
+ pf->text = &pf->textbuf;
+ pf->extdata = NULL;
+ }
+ else if(index == N_RPLMBOX){
+ sending_order[NN+16] = pf;
+ pf_mbox = pf;
+ pf->text = &pf->textbuf;
+ pf->extdata = NULL;
+ }
+ else if(index == N_SMTP){
+ sending_order[NN+17] = pf;
+ pf_smtp_server = pf;
+ pf->text = &pf->textbuf;
+ pf->extdata = NULL;
+ }
+ else if(index == N_NNTP){
+ sending_order[NN+18] = pf;
+ pf_nntp_server = pf;
+ pf->text = &pf->textbuf;
+ pf->extdata = NULL;
+ }
+ else if(index == N_CURPOS){
+ sending_order[NN+19] = pf;
+ pf_curpos = pf;
+ pf->text = &pf->textbuf;
+ pf->extdata = NULL;
+ }
+ else if(index == N_OURREPLYTO){
+ sending_order[NN+20] = pf;
+ pf_ourrep = pf;
+ pf->text = &pf->textbuf;
+ pf->extdata = NULL;
+ }
+ else if(index == N_OURHDRS){
+ sending_order[NN+21] = pf;
+ pf_ourhdrs = pf;
+ pf->text = &pf->textbuf;
+ pf->extdata = NULL;
+ }
+ else if(index == N_AUTHRCVD){
+ sending_order[0] = pf;
+ pf_ourhdrs = pf;
+ pf->text = &pf->textbuf;
+ pf->extdata = NULL;
+ }
+ else{
+ q_status_message(SM_ORDER | SM_DING, 3, 7,
+ "Botched: Unmatched FreeText header in pine_send");
+ }
+
+ break;
+
+ /* can't do a default for this one */
+ case Attachment:
+ /* If there is an attachment already, we'd better show them */
+ if(body && *body && (*body)->type != TYPETEXT)
+ he->rich_header = 0; /* force on by default */
+
+ break;
+
+ case Address:
+ switch(index){
+ case N_FROM:
+ sending_order[3] = pf;
+ pf->addr = &outgoing->from;
+ if(role && role->from){
+ if(ps_global->never_allow_changing_from)
+ q_status_message(SM_ORDER, 3, 3, _("Site policy doesn't allow changing From address so role's From has no effect"));
+ else{
+ outgoing->from = copyaddrlist(role->from);
+ he->display_it = 1; /* show it */
+ he->rich_header = 0;
+ }
+ }
+
+ he_from = he;
+ break;
+
+ case N_TO:
+ sending_order[NN+2] = pf;
+ pf->addr = &outgoing->to;
+ /* If already set, make it act like we typed it in */
+ if(outgoing->to
+ && outgoing->to->mailbox
+ && outgoing->to->mailbox[0]
+ && flags & PS_STICKY_TO)
+ he->sticky = 1;
+
+ he_to = he;
+ pf_to = pf;
+ break;
+
+ case N_NOBODY:
+ sending_order[NN+5] = pf;
+ pf_nobody = pf;
+ if(ps_global->VAR_EMPTY_HDR_MSG
+ && !ps_global->VAR_EMPTY_HDR_MSG[0]){
+ pf->addr = NULL;
+ }
+ else{
+ nobody_addr = mail_newaddr();
+ nobody_addr->next = mail_newaddr();
+ nobody_addr->mailbox = cpystr(rfc1522_encode(tmp_20k_buf,
+ SIZEOF_20KBUF,
+ (unsigned char *)(ps_global->VAR_EMPTY_HDR_MSG
+ ? ps_global->VAR_EMPTY_HDR_MSG
+ : "undisclosed-recipients"),
+ ps_global->posting_charmap));
+ pf->addr = &nobody_addr;
+ }
+
+ break;
+
+ case N_CC:
+ sending_order[NN+3] = pf;
+ pf->addr = &outgoing->cc;
+ break;
+
+ case N_BCC:
+ sending_order[NN+4] = pf;
+ pf->addr = &outgoing->bcc;
+ /* if bcc exists, make sure it's exposed so nothing's
+ * sent by mistake...
+ */
+ if(outgoing->bcc)
+ he->display_it = 1;
+
+ break;
+
+ case N_REPLYTO:
+ sending_order[NN+1] = pf;
+ pf->addr = &outgoing->reply_to;
+ if(role && role->replyto){
+ if(outgoing->reply_to)
+ mail_free_address(&outgoing->reply_to);
+
+ outgoing->reply_to = copyaddrlist(role->replyto);
+ he->display_it = 1; /* show it */
+ he->rich_header = 0;
+ }
+
+ break;
+
+ case N_LCC:
+ sending_order[NN+7] = pf;
+ pf->addr = &lcc_addr;
+ he_lcc = he;
+ if(lcc_arg){
+ build_address(lcc_arg, &addr, NULL, NULL, NULL);
+ rfc822_parse_adrlist(&lcc_addr, addr,
+ ps_global->maildomain);
+ fs_give((void **)&addr);
+ he->display_it = 1;
+ }
+
+ break;
+
+#if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
+ case N_SENDER:
+ sending_order[4] = pf;
+ pf->addr = &outgoing->sender;
+ break;
+#endif
+
+ default:
+ q_status_message1(SM_ORDER,3,7,
+ "Internal error: Address header %s", comatose(index));
+ break;
+ }
+
+ /*
+ * If this is a reply to news, don't show the regular email
+ * recipient headers (unless they are non-empty).
+ */
+ if((outgoing->newsgroups && *outgoing->newsgroups)
+ && (index == N_TO || index == N_CC
+ || index == N_BCC || index == N_LCC)
+ && (pf->addr && !*pf->addr)){
+ if((ct=set_default_hdrval(pf, custom)) >= UseAsDef &&
+ pf->textbuf && *pf->textbuf){
+ removing_trailing_white_space(pf->textbuf);
+ (void)removing_double_quotes(pf->textbuf);
+ build_address(pf->textbuf, &addr, NULL, NULL, NULL);
+ rfc822_parse_adrlist(pf->addr, addr,
+ ps_global->maildomain);
+ fs_give((void **)&addr);
+ if(ct > UseAsDef)
+ he->sticky = 1;
+ }
+ else
+ he->rich_header = 1; /* hide */
+ }
+
+ /*
+ * If this address doesn't already have a value, then we check
+ * for a default value assigned by the user.
+ */
+ else if(pf->addr && !*pf->addr){
+ if((ct=set_default_hdrval(pf, custom)) >= UseAsDef &&
+ (index != N_FROM ||
+ (!ps_global->never_allow_changing_from &&
+ F_ON(F_ALLOW_CHANGING_FROM, ps_global))) &&
+ pf->textbuf && *pf->textbuf){
+
+ removing_trailing_white_space(pf->textbuf);
+ (void)removing_double_quotes(pf->textbuf);
+
+ /*
+ * Try to set To based on Lcc. Don't attempt Fcc.
+ */
+ if(index == N_LCC && !he_to->sticky && pf_to && pf_to->addr){
+ BUILDER_ARG *barg = NULL;
+ char *ppp = NULL;
+
+ if(*pf_to->addr)
+ ppp = addr_list_string(*pf_to->addr, NULL, 1);
+
+ if(!ppp)
+ ppp = cpystr("");
+
+ barg = (BUILDER_ARG *) fs_get(sizeof(*barg));
+ memset(barg, 0, sizeof(*barg));
+ barg->me = &(he->bldr_private);
+ barg->aff = &(he_to->bldr_private);
+ barg->tptr = cpystr(ppp);
+
+ build_addr_lcc(pf->textbuf, &addr, NULL, barg, NULL);
+ he->display_it = 1;
+
+ rfc822_parse_adrlist(pf->addr, addr,
+ ps_global->maildomain);
+ if(addr)
+ fs_give((void **) &addr);
+
+ if(ct > UseAsDef)
+ he->sticky = 1;
+
+ if(barg && barg->tptr && strcmp(ppp, barg->tptr)){
+ ADDRESS *a = NULL;
+
+ rfc822_parse_adrlist(&a, barg->tptr,
+ ps_global->maildomain);
+ if(a){
+ if(pf_to->addr)
+ mail_free_address(pf_to->addr);
+
+ *pf_to->addr = a;
+ }
+ }
+
+ if(barg){
+ if(barg->tptr)
+ fs_give((void **) &barg->tptr);
+
+ fs_give((void **) &barg);
+ }
+
+ if(ppp)
+ fs_give((void **) &ppp);
+ }
+ else{
+ build_address(pf->textbuf, &addr, NULL, NULL, NULL);
+ rfc822_parse_adrlist(pf->addr, addr,
+ ps_global->maildomain);
+ if(addr)
+ fs_give((void **) &addr);
+
+ if(ct > UseAsDef)
+ he->sticky = 1;
+ }
+ }
+
+ /* if we still don't have a from */
+ if(index == N_FROM && !*pf->addr)
+ *pf->addr = generate_from();
+ }
+
+ /*
+ * Addr is already set in the rest of the cases.
+ */
+ else if((index == N_FROM || index == N_REPLYTO) && pf->addr){
+ ADDRESS *adr = NULL;
+
+ /*
+ * We get to this case of the ifelse if the from or reply-to
+ * addr was set by a role above.
+ */
+
+ /* figure out the default value */
+ (void)set_default_hdrval(pf, custom);
+ if(pf->textbuf && *pf->textbuf){
+ removing_trailing_white_space(pf->textbuf);
+ (void)removing_double_quotes(pf->textbuf);
+ build_address(pf->textbuf, &addr, NULL, NULL, NULL);
+ rfc822_parse_adrlist(&adr, addr,
+ ps_global->maildomain);
+ fs_give((void **)&addr);
+ }
+
+ /* if value set by role is different from default, show it */
+ if(adr && !address_is_same(*pf->addr, adr))
+ he->display_it = 1; /* start this off showing */
+
+ /* malformed */
+ if(!(*pf->addr)->mailbox){
+ fs_give((void **)pf->addr);
+ he->display_it = 1;
+ }
+
+ if(adr)
+ mail_free_address(&adr);
+ }
+ else if((index == N_TO || index == N_CC || index == N_BCC)
+ && pf->addr){
+ ADDRESS *a = NULL, **tail;
+
+ /*
+ * These three are different from the others because we
+ * might add the addresses to what is already there instead
+ * of replacing.
+ */
+
+ switch(set_default_hdrval(pf, custom)){
+ case Replace:
+ if(*pf->addr)
+ mail_free_address(pf->addr);
+
+ removing_trailing_white_space(pf->textbuf);
+ (void)removing_double_quotes(pf->textbuf);
+ build_address(pf->textbuf, &addr, NULL, NULL, NULL);
+ rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
+ fs_give((void **)&addr);
+ he->sticky = 1;
+ break;
+
+ case Combine:
+ removing_trailing_white_space(pf->textbuf);
+ (void)removing_double_quotes(pf->textbuf);
+ build_address(pf->textbuf, &addr, NULL, NULL, NULL);
+ rfc822_parse_adrlist(&a, addr, ps_global->maildomain);
+ fs_give((void **)&addr);
+ he->sticky = 1;
+ if(a){
+ for(tail = pf->addr; *tail; tail = &(*tail)->next)
+ ;
+ *tail = reply_cp_addr(ps_global, 0, NULL, NULL,
+ *pf->addr, NULL, a, RCA_ALL);
+ q_status_message(SM_ORDER, 3, 3,
+ "Adding addresses from role");
+ mail_free_address(&a);
+ }
+
+ break;
+
+ case UseAsDef:
+ case NoMatch:
+ break;
+ }
+
+ he->display_it = 1; /* start this off showing */
+ }
+ else if(pf->addr){
+ switch(set_default_hdrval(pf, custom)){
+ case Replace:
+ case Combine:
+ if(*pf->addr)
+ mail_free_address(pf->addr);
+
+ removing_trailing_white_space(pf->textbuf);
+ (void)removing_double_quotes(pf->textbuf);
+ build_address(pf->textbuf, &addr, NULL, NULL, NULL);
+ rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
+ fs_give((void **)&addr);
+ he->sticky = 1;
+ break;
+
+ case UseAsDef:
+ case NoMatch:
+ break;
+ }
+
+ he->display_it = 1;
+ }
+
+ if(pf->addr && *pf->addr && !(*pf->addr)->mailbox){
+ mail_free_address(pf->addr);
+ he->display_it = 1; /* start this off showing */
+ }
+
+ if(pf->textbuf) /* free default value in any case */
+ fs_give((void **)&pf->textbuf);
+
+ /* outgoing2strings will alloc the string pf->scratch below */
+ he->realaddr = &pf->scratch;
+ break;
+
+ case Fcc:
+ sending_order[NN+8] = pf;
+ pf_fcc = pf;
+ if(role && role->fcc)
+ fcc = role->fcc;
+ else
+ fcc = get_fcc(fcc_arg);
+
+ if(fcc_to_free){
+ fs_give((void **)&fcc_to_free);
+ fcc_arg = NULL;
+ }
+
+ if(((flags & PS_STICKY_FCC) && fcc[0]) || (role && role->fcc))
+ he->sticky = 1;
+
+ if(role)
+ role->fcc = NULL;
+
+ if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC) != 0)
+ he->display_it = 1; /* start this off showing */
+
+ he->realaddr = &fcc;
+ pf->text = &fcc;
+ he_fcc = he;
+ break;
+
+ case Subject :
+ sending_order[NN+6] = pf;
+
+ switch(set_default_hdrval(pf, custom)){
+ case Replace:
+ case Combine:
+ pf->scratch = pf->textbuf;
+ pf->textbuf = NULL;
+ he->sticky = 1;
+ if(outgoing->subject)
+ fs_give((void **)&outgoing->subject);
+
+ break;
+
+ case UseAsDef:
+ case NoMatch:
+ /* if no value, use default */
+ if(outgoing->subject){
+ pf->scratch = cpystr(outgoing->subject);
+ }
+ else{
+ pf->scratch = pf->textbuf;
+ pf->textbuf = NULL;
+ }
+
+ break;
+ }
+
+ he->realaddr = &pf->scratch;
+ pf->text = &outgoing->subject;
+ break;
+
+ default:
+ q_status_message1(SM_ORDER,3,7,
+ "Unknown header type %d in pine_send",
+ (void *)pf->type);
+ break;
+ }
+
+ /*
+ * We may or may not want to give the user the chance to edit
+ * the From and Reply-To lines. If they are listed in either
+ * Default-composer-hdrs or Customized-hdrs, then they can edit
+ * them, else no.
+ * If canedit is not set, that means that this header is not in
+ * the user's customized-hdrs. If rich_header is set, that
+ * means that this header is not in the user's
+ * default-composer-hdrs (since From and Reply-To are rich
+ * by default). So, don't give it an he to edit with in that case.
+ *
+ * For other types, just not setting canedit will cause it to be
+ * uneditable, regardless of what the user does.
+ */
+ switch(index){
+ case N_FROM:
+ /* to allow it, we let this fall through to the reply-to case below */
+ if(ps_global->never_allow_changing_from ||
+ (F_OFF(F_ALLOW_CHANGING_FROM, ps_global) &&
+ !(role && role->from))){
+ if(pf->canedit || !he->rich_header)
+ q_status_message(SM_ORDER, 3, 3,
+ _("Not allowed to change header \"From\""));
+
+ memset(he, 0, (size_t)sizeof(*he));
+ pf->extdata = NULL;
+ break;
+ }
+
+ case N_REPLYTO:
+ if(!pf->canedit && he->rich_header){
+ memset(he, 0, (size_t)sizeof(*he));
+ pf->extdata = NULL;
+ }
+ else{
+ pf->canedit = 1;
+ he++;
+ }
+
+ break;
+
+ default:
+ if(!pf->canedit){
+ memset(he, 0, (size_t)sizeof(*he));
+ pf->extdata = NULL;
+ }
+ else
+ he++;
+
+ break;
+ }
+ }
+
+ /*
+ * This is so the builder can tell the composer to fill the affected
+ * field based on the value in the field on the left.
+ *
+ * Note that this mechanism isn't completely general. Each entry has
+ * only a single next_affected, so if some other entry points an
+ * affected entry at an entry with a next_affected, they all inherit
+ * that next_affected. Since this isn't used much a careful ordering
+ * of the affected fields should make it a sufficient mechanism.
+ */
+ he_to->affected_entry = he_fcc;
+ he_news->affected_entry = he_fcc;
+ he_lcc->affected_entry = he_to;
+ he_to->next_affected = he_fcc;
+
+ (--pf)->next = (total_cnt != fixed_cnt) ? header.custom : NULL;
+
+ i--; /* subtract one because N_ATTCH doesn't get a sending_order slot */
+ /*
+ * Set up headerentries for custom fields.
+ * NOTE: "i" is assumed to now index first custom field in sending
+ * order.
+ */
+ for(pf = pf->next; pf && pf->name; pf = pf->next){
+ char *addr;
+
+ if(pf->standard)
+ continue;
+
+ pf->extdata = he;
+ pf->canedit = 1;
+ pf->rcptto = 0;
+ pf->writehdr = 1;
+ pf->localcopy = 1;
+
+ switch(pf->type){
+ case Address:
+ if(pf->addr){ /* better be set */
+ sending_order[i++] = pf;
+ *he = he_custom_addr_templ;
+ /* change default text into an ADDRESS */
+ /* strip quotes around whole default */
+ removing_trailing_white_space(pf->textbuf);
+ (void)removing_double_quotes(pf->textbuf);
+ build_address(pf->textbuf, &addr, NULL, NULL, NULL);
+ rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
+ fs_give((void **)&addr);
+ if(pf->textbuf)
+ fs_give((void **)&pf->textbuf);
+
+ he->realaddr = &pf->scratch;
+ if(F_OFF(F_ENABLE_TAB_COMPLETE,ps_global))
+ he->nickcmpl = NULL;
+ }
+
+ break;
+
+ case FreeText:
+ sending_order[i++] = pf;
+ *he = he_custom_free_templ;
+ he->realaddr = &pf->textbuf;
+ pf->text = &pf->textbuf;
+ if(((!pf->val || !pf->val[0]) && pf->textbuf && pf->textbuf[0]) ||
+ (pf->val && (!pf->textbuf || strcmp(pf->textbuf, pf->val))))
+ he->display_it = 1; /* show it */
+
+ break;
+
+ default:
+ q_status_message1(SM_ORDER,0,7,"Unknown custom header type %d",
+ (void *)pf->type);
+ break;
+ }
+
+ he->name = pf->name;
+
+ /* use first 8 characters for prompt */
+ he->prompt = cpystr(" : ");
+ strncpy(he->prompt, he->name, MIN(strlen(he->name), he->prwid - 2));
+
+ he->rich_header = view_as_rich(he->name, he->rich_header);
+ he++;
+ }
+
+ /*
+ * Make sure at least *one* field is displayable...
+ */
+ for(index = -1, i=0, pf=header.local; pf && pf->name; pf=pf->next, i++)
+ if(HE(pf) && !HE(pf)->rich_header){
+ index = i;
+ break;
+ }
+
+ /*
+ * None displayable!!! Warn and display defaults.
+ */
+ if(index == -1){
+ q_status_message(SM_ORDER,0,5,
+ "No default-composer-hdrs matched, displaying defaults");
+ for(i = 0, pf = header.local; pf; pf = pf->next, i++)
+ if((i == N_TO || i == N_CC || i == N_SUBJ || i == N_ATTCH)
+ && HE(pf))
+ HE(pf)->rich_header = 0;
+ }
+
+ /*
+ * Save information about body which set_mime_type_by_grope might change.
+ * Then, if we get an error sending, we reset these things so that
+ * grope can do it's thing again after we edit some more.
+ */
+ if ((*body)->type == TYPEMULTIPART)
+ bp = save_body_particulars(&(*body)->nested.part->body);
+ else
+ bp = save_body_particulars(*body);
+
+
+ local_redraft_pos = redraft_pos;
+
+ /*----------------------------------------------------------------------
+ Loop calling the editor until everything goes well
+ ----*/
+ while(1){
+ int saved_user_timeout;
+
+ /* Reset body to what it was when we started. */
+ if ((*body)->type == TYPEMULTIPART)
+ reset_body_particulars(bp, &(*body)->nested.part->body);
+ else
+ reset_body_particulars(bp,*body);
+ /*
+ * set initial cursor position based on how many times we've been
+ * thru the loop...
+ */
+ if(reply && reply->pseudo){
+ pbf->pine_flags |= reply->data.pico_flags;
+ }
+ else if(body_start){
+ pbf->pine_flags |= P_BODY;
+ body_start = 0; /* maybe not next time */
+ }
+ else if(local_redraft_pos){
+ pbf->edit_offset = local_redraft_pos->offset;
+ /* set the start_here bit in correct header */
+ for(pf = header.local; pf && pf->name; pf = pf->next)
+ if(strcmp(pf->name, local_redraft_pos->hdrname) == 0
+ && HE(pf)){
+ HE(pf)->start_here = 1;
+ break;
+ }
+
+ /* If didn't find it, we start in body. */
+ if(!pf || !pf->name)
+ pbf->pine_flags |= P_BODY;
+ }
+ else if(reply && (!reply->forw && !reply->forwarded)){
+ pbf->pine_flags |= P_BODY;
+ }
+
+ /* in case these were turned on in previous pass through loop */
+ if(pf_nobody){
+ pf_nobody->writehdr = 0;
+ pf_nobody->localcopy = 0;
+ }
+
+ if(pf_fcc)
+ pf_fcc->localcopy = 0;
+
+ /*
+ * If a sending attempt failed after we passed the message text
+ * thru a user-defined filter, "orig_so" points to the original
+ * text. Replace the body's encoded data with the original...
+ */
+ if(orig_so){
+ STORE_S **so = (STORE_S **)(((*body)->type == TYPEMULTIPART)
+ ? &(*body)->nested.part->body.contents.text.data
+ : &(*body)->contents.text.data);
+ so_give(so);
+ *so = orig_so;
+ orig_so = NULL;
+ }
+
+ /*
+ * Convert the envelope and body to the string format that
+ * pico can edit
+ */
+ outgoing2strings(&header, *body, &pbf->msgtext, &pbf->attachments, 0);
+
+ for(pf = header.local; pf && pf->name; pf = pf->next){
+ /*
+ * If this isn't the first time through this loop, we may have
+ * freed some of the FreeText headers below so that they wouldn't
+ * show up as empty headers in the finished message. Need to
+ * alloc them again here so they can be edited.
+ */
+ if(pf->type == FreeText && HE(pf) && !*HE(pf)->realaddr)
+ *HE(pf)->realaddr = cpystr("");
+
+ if(pf->type != Attachment && HE(pf) && *HE(pf)->realaddr)
+ HE(pf)->maxlen = strlen(*HE(pf)->realaddr);
+ }
+
+ /*
+ * If From is exposed, probably by a role, then start the cursor
+ * on the first line which isn't filled in. If it isn't, then we
+ * don't move the cursor, mostly for back-compat.
+ */
+ if((!reply || reply->forw || reply->forwarded) &&
+ !local_redraft_pos && !(pbf->pine_flags & P_BODY) && he_from &&
+ (he_from->display_it || !he_from->rich_header)){
+ for(pf = header.local; pf && pf->name; pf = pf->next)
+ if(HE(pf) &&
+ (HE(pf)->display_it || !HE(pf)->rich_header) &&
+ HE(pf)->realaddr &&
+ (!*HE(pf)->realaddr || !**HE(pf)->realaddr)){
+ HE(pf)->start_here = 1;
+ break;
+ }
+ }
+
+#ifdef _WINDOWS
+ mswin_setwindowmenu (MENU_COMPOSER);
+#endif
+
+ cancel_busy_cue(-1);
+ flush_status_messages(1);
+
+ /* turn off user input timeout when in composer */
+ saved_user_timeout = ps_global->hours_to_timeout;
+ ps_global->hours_to_timeout = 0;
+ dprint((1, "\n ---- COMPOSER ----\n"));
+ editor_result = pico(pbf);
+ dprint((4, "... composer returns (0x%x)\n", editor_result));
+ ps_global->hours_to_timeout = saved_user_timeout;
+
+#ifdef _WINDOWS
+ mswin_setwindowmenu (MENU_DEFAULT);
+#endif
+ fix_windsize(ps_global);
+
+ /*
+ * Only reinitialize signals if we didn't receive an interesting
+ * one while in pico, since pico's return is part of processing that
+ * signal and it should continue to be ignored.
+ */
+ if(!(editor_result & COMP_GOTHUP))
+ init_signals(); /* Pico has it's own signal stuff */
+
+ /*
+ * We're going to save in DEADLETTER. Dump attachments first.
+ */
+ if(editor_result & COMP_CANCEL)
+ free_attachment_list(&pbf->attachments);
+
+ /* Turn strings back into structures */
+ strings2outgoing(&header, body, pbf->attachments, flowing_requested);
+
+ /* Make newsgroups NULL if it is "" (so won't show up in headers) */
+ if(outgoing->newsgroups){
+ sqzspaces(outgoing->newsgroups);
+ if(!outgoing->newsgroups[0])
+ fs_give((void **)&(outgoing->newsgroups));
+ }
+
+ /* Make subject NULL if it is "" (so won't show up in headers) */
+ if(outgoing->subject && !outgoing->subject[0])
+ fs_give((void **)&(outgoing->subject));
+
+ /* remove custom fields that are empty */
+ for(pf = header.local; pf && pf->name; pf = pf->next){
+ if(pf->type == FreeText && pf->textbuf){
+ if(pf->textbuf[0] == '\0'){
+ fs_give((void **)&pf->textbuf);
+ pf->text = NULL;
+ }
+ }
+ }
+
+ removing_trailing_white_space(fcc);
+
+ /*-------- Stamp it with a current date -------*/
+ if(outgoing->date) /* update old date */
+ fs_give((void **)&(outgoing->date));
+
+ if(F_ON(F_QUELL_TIMEZONE, ps_global))
+ mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) TRUE);
+
+ rfc822_date(tmp_20k_buf); /* format and copy new date */
+ if(F_ON(F_QUELL_TIMEZONE, ps_global))
+ mail_parameters(NULL, SET_DISABLE822TZTEXT, (void *) FALSE);
+
+ outgoing->date = (unsigned char *) cpystr(tmp_20k_buf);
+
+ /* Set return_path based on From which is going to be used */
+ if(outgoing->return_path)
+ mail_free_address(&outgoing->return_path);
+
+ outgoing->return_path = rfc822_cpy_adr(outgoing->from);
+
+ /*
+ * Don't ever believe the sender that is there.
+ * If From doesn't look quite right, generate our own sender.
+ */
+ if(outgoing->sender)
+ mail_free_address(&outgoing->sender);
+
+ /*
+ * If the LHS of the address doesn't match, or the RHS
+ * doesn't match one of localdomain or hostname,
+ * then add a sender line (really X-X-Sender).
+ *
+ * Don't add a personal_name since the user can change that.
+ */
+ if(F_OFF(F_DISABLE_SENDER, ps_global)
+ &&
+ (!outgoing->from
+ || !outgoing->from->mailbox
+ || strucmp(outgoing->from->mailbox, ps_global->VAR_USER_ID) != 0
+ || !outgoing->from->host
+ || !(strucmp(outgoing->from->host, ps_global->localdomain) == 0
+ || strucmp(outgoing->from->host, ps_global->hostname) == 0))){
+
+ outgoing->sender = mail_newaddr();
+ outgoing->sender->mailbox = cpystr(ps_global->VAR_USER_ID);
+ outgoing->sender->host = cpystr(ps_global->hostname);
+ }
+
+ if(ps_global->newthread){
+ if(outgoing->in_reply_to) fs_give((void **)&outgoing->in_reply_to);
+ if(outgoing->references) fs_give((void **)&outgoing->references);
+ }
+
+ /*----- Message is edited, now decide what to do with it ----*/
+ if(editor_result & (COMP_SUSPEND | COMP_GOTHUP | COMP_CANCEL)){
+ /*=========== Postpone or Interrupted message ============*/
+ CONTEXT_S *fcc_cntxt = NULL;
+ char folder[MAXPATH+1];
+ int fcc_result = 0;
+ char label[50];
+
+ dprint((4, "pine_send:%s handling\n",
+ (editor_result & COMP_SUSPEND)
+ ? "SUSPEND"
+ : (editor_result & COMP_GOTHUP)
+ ? "HUP"
+ : (editor_result & COMP_CANCEL)
+ ? "CANCEL" : "HUH?"));
+ if((editor_result & COMP_CANCEL)
+ && (F_ON(F_QUELL_DEAD_LETTER, ps_global)
+ || ps_global->deadlets == 0)){
+ q_status_message(SM_ORDER, 0, 3, "Message cancelled");
+ break;
+ }
+
+ /*
+ * The idea here is to use the Fcc: writing facility
+ * to append to the special postponed message folder...
+ *
+ * NOTE: the strategy now is to write the message and
+ * all attachments as they exist at composition time.
+ * In other words, attachments are postponed by value
+ * and not reference. This may change later, but we'll
+ * need a local "message/external-body" type that
+ * outgoing2strings knows how to properly set up for
+ * the composer. Maybe later...
+ */
+
+ label[0] = '\0';
+
+ if(F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global)
+ && (editor_result & COMP_SUSPEND)
+ && (check_addresses(&header) == CA_BAD)){
+ /*--- Addresses didn't check out---*/
+ q_status_message(SM_ORDER, 7, 7,
+ _("Not allowed to postpone message until addresses are qualified"));
+ continue;
+ }
+
+ /*
+ * Build the local message copy so.
+ *
+ * In the HUP case, we'll write the bezerk delimiter by
+ * hand and output the message directly into the folder.
+ * It's not only faster, we don't have to worry about
+ * c-client reentrance and less hands paw over the data so
+ * there's less chance of a problem.
+ *
+ * In the Postpone case, just create it if the user wants to
+ * and create a temporary storage object to write into. */
+ fake_hup:
+ lmc.all_written = lmc.text_only = lmc.text_written = 0;
+ if(editor_result & (COMP_GOTHUP | COMP_CANCEL)){
+ int newfile = 1;
+ time_t now = time((time_t *)0);
+
+#if defined(DOS) || defined(OS2)
+ /*
+ * we can't assume anything about root or home dirs, so
+ * just plunk it down in the same place as the pinerc
+ */
+ if(!getenv("HOME")){
+ char *lc = last_cmpnt(ps_global->pinerc);
+ folder[0] = '\0';
+ if(lc != NULL){
+ strncpy(folder,ps_global->pinerc,
+ MIN(lc-ps_global->pinerc,sizeof(folder)-1));
+ folder[MIN(lc-ps_global->pinerc,sizeof(folder)-1)]='\0';
+ }
+
+ strncat(folder, (editor_result & COMP_GOTHUP)
+ ? INTERRUPTED_MAIL : DEADLETTER,
+ sizeof(folder)-strlen(folder)-1);
+ }
+ else
+#endif
+ build_path(folder,
+ ps_global->VAR_OPER_DIR
+ ? ps_global->VAR_OPER_DIR : ps_global->home_dir,
+ (editor_result & COMP_GOTHUP)
+ ? INTERRUPTED_MAIL : DEADLETTER,
+ sizeof(folder));
+
+ if(editor_result & COMP_CANCEL){
+ char filename[MAXPATH+1], newfname[MAXPATH+1], nbuf[5];
+
+ if(strlen(folder) + 1 < sizeof(filename))
+ for(i = ps_global->deadlets - 1; i > 0 && i < 9; i--){
+ strncpy(filename, folder, sizeof(filename));
+ filename[sizeof(filename)-1] = '\0';
+ strncpy(newfname, filename, sizeof(newfname));
+ newfname[sizeof(newfname)-1] = '\0';
+
+ if(i > 1){
+ snprintf(nbuf, sizeof(nbuf), "%d", i);
+ nbuf[sizeof(nbuf)-1] = '\0';
+ strncat(filename, nbuf,
+ sizeof(filename)-strlen(filename)-1);
+ filename[sizeof(filename)-1] = '\0';
+ }
+
+ snprintf(nbuf, sizeof(nbuf), "%d", i+1);
+ nbuf[sizeof(nbuf)-1] = '\0';
+ strncat(newfname, nbuf,
+ sizeof(newfname)-strlen(newfname)-1);
+ newfname[sizeof(newfname)-1] = '\0';
+ (void) rename_file(filename, newfname);
+ }
+
+ our_unlink(folder);
+ }
+ else
+ newfile = can_access(folder, ACCESS_EXISTS);
+
+ if((lmc.so = so_get(FCC_SOURCE, NULL, WRITE_ACCESS)) != NULL){
+ if (outgoing->from){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%sFrom %s@%s %.24s\015\012",
+ newfile ? "" : "\015\012",
+ outgoing->from->mailbox,
+ outgoing->from->host, ctime(&now));
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ if(!so_puts(lmc.so, tmp_20k_buf)){
+ if(editor_result & COMP_CANCEL)
+ q_status_message2(SM_ORDER | SM_DING, 3, 3,
+ "Can't write \"%s\": %s",
+ folder, error_description(errno));
+ else
+ dprint((1, "* * * CAN'T WRITE %s: %s\n",
+ folder ? folder : "?",
+ error_description(errno)));
+ }
+ }
+ }
+ }
+ else{ /* Must be COMP_SUSPEND */
+ if(!ps_global->VAR_POSTPONED_FOLDER
+ || !ps_global->VAR_POSTPONED_FOLDER[0]){
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("No postponed file defined"));
+ continue;
+ }
+
+ /*
+ * Store the cursor position
+ *
+ * First find the header entry with the start_here
+ * bit set, if any. This means the editor is telling
+ * us to start on this header field next time.
+ */
+ start_here_name = NULL;
+ for(pf = header.local; pf && pf->name; pf = pf->next)
+ if(HE(pf) && HE(pf)->start_here){
+ start_here_name = pf->name;
+ break;
+ }
+
+ /* If there wasn't one, ":" means we start in the body. */
+ if(!start_here_name || !*start_here_name)
+ start_here_name = ":";
+
+ if(ps_global->VAR_FORM_FOLDER
+ && ps_global->VAR_FORM_FOLDER[0]
+ && postpone_prompt() == 'f'){
+ strncpy(folder, ps_global->VAR_FORM_FOLDER,
+ sizeof(folder)-1);
+ folder[sizeof(folder)-1] = '\0';
+ strncpy(label, "form letter", sizeof(label));
+ label[sizeof(label)-1] = '\0';
+ }
+ else{
+ strncpy(folder, ps_global->VAR_POSTPONED_FOLDER,
+ sizeof(folder)-1);
+ folder[sizeof(folder)-1] = '\0';
+ strncpy(label, "postponed message", sizeof(label));
+ label[sizeof(label)-1] = '\0';
+ }
+
+ lmc.so = open_fcc(folder,&fcc_cntxt, 1, NULL, NULL);
+ }
+
+ if(lmc.so){
+ size_t sz;
+ char *lmq = NULL;
+
+ /* copy fcc line to postponed or interrupted folder */
+ if(pf_fcc)
+ pf_fcc->localcopy = 1;
+
+ /* plug error into header for later display to user */
+ if((editor_result & ~0xff) && (lmq = last_message_queued()) != NULL){
+ pf_err->writehdr = 1;
+ pf_err->localcopy = 1;
+ pf_err->textbuf = lmq;
+ }
+
+ /*
+ * if reply, write (UID)folder header field so we can
+ * later flag the replied-to message \\ANSWERED
+ * DON'T save MSGNO's.
+ */
+ if(reply && reply->uid){
+ char uidbuf[MAILTMPLEN], *p;
+ long i;
+
+ for(i = 0L, p = tmp_20k_buf; reply->data.uid.msgs[i]; i++){
+ if(i)
+ sstrncpy(&p, ",", SIZEOF_20KBUF-(p-tmp_20k_buf));
+
+ sstrncpy(&p,ulong2string(reply->data.uid.msgs[i]),SIZEOF_20KBUF-(p-tmp_20k_buf));
+ }
+
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+
+ pf_uid->writehdr = 1;
+ pf_uid->localcopy = 1;
+ snprintf(uidbuf, sizeof(uidbuf), "(%s%s%s)(%ld %lu %s)%s",
+ reply->prefix ? int2string(strlen(reply->prefix))
+ : (reply->forwarded) ? "": "0 ",
+ reply->prefix ? " " : "",
+ reply->prefix ? reply->prefix : "",
+ i, reply->data.uid.validity,
+ tmp_20k_buf, reply->mailbox);
+ uidbuf[sizeof(uidbuf)-1] = '\0';
+ pf_uid->textbuf = cpystr(uidbuf);
+
+ /*
+ * Logically, this ought to be part of pf_uid, but this
+ * was added later and so had to be in a separate header
+ * for backwards compatibility.
+ */
+ pf_mbox->writehdr = 1;
+ pf_mbox->localcopy = 1;
+ pf_mbox->textbuf = cpystr(reply->origmbox
+ ? reply->origmbox
+ : reply->mailbox);
+ }
+
+ /* Save cursor position */
+ if(start_here_name && *start_here_name){
+ char curposbuf[MAILTMPLEN];
+
+ pf_curpos->writehdr = 1;
+ pf_curpos->localcopy = 1;
+ snprintf(curposbuf, sizeof(curposbuf), "%s %ld", start_here_name,
+ pbf->edit_offset);
+ curposbuf[sizeof(curposbuf)-1] = '\0';
+ pf_curpos->textbuf = cpystr(curposbuf);
+ }
+
+ /*
+ * Work around c-client reply-to bug. C-client will
+ * return a reply_to in an envelope even if there is
+ * no reply-to header field. We want to note here whether
+ * the reply-to is real or not.
+ */
+ if(outgoing->reply_to || hdr_is_in_list("reply-to", custom)){
+ pf_ourrep->writehdr = 1;
+ pf_ourrep->localcopy = 1;
+ if(outgoing->reply_to)
+ pf_ourrep->textbuf = cpystr("Full");
+ else
+ pf_ourrep->textbuf = cpystr("Empty");
+ }
+
+ /* Save the role-specific smtp server */
+ if(role && role->smtp && role->smtp[0]){
+ char *q, *smtp = NULL;
+ char **lp;
+ size_t len = 0;
+
+ /*
+ * Turn the list of smtp servers into a space-
+ * delimited list in a single string.
+ */
+ for(lp = role->smtp; (q = *lp) != NULL; lp++)
+ len += (strlen(q) + 1);
+
+ if(len){
+ smtp = (char *) fs_get(len * sizeof(char));
+ smtp[0] = '\0';
+ for(lp = role->smtp; (q = *lp) != NULL; lp++){
+ if(lp != role->smtp)
+ strncat(smtp, " ", len-strlen(smtp)-1);
+
+ strncat(smtp, q, len-strlen(smtp)-1);
+ }
+
+ smtp[len-1] = '\0';
+ }
+
+ pf_smtp_server->writehdr = 1;
+ pf_smtp_server->localcopy = 1;
+ if(smtp)
+ pf_smtp_server->textbuf = smtp;
+ else
+ pf_smtp_server->textbuf = cpystr("");
+ }
+
+ /* Save the role-specific nntp server */
+ if(suggested_nntp_server ||
+ (role && role->nntp && role->nntp[0])){
+ char *q, *nntp = NULL;
+ char **lp;
+ size_t len = 0;
+
+ if(role && role->nntp && role->nntp[0]){
+ /*
+ * Turn the list of nntp servers into a space-
+ * delimited list in a single string.
+ */
+ for(lp = role->nntp; (q = *lp) != NULL; lp++)
+ len += (strlen(q) + 1);
+
+ if(len){
+ nntp = (char *) fs_get(len * sizeof(char));
+ nntp[0] = '\0';
+ for(lp = role->nntp; (q = *lp) != NULL; lp++){
+ if(lp != role->nntp)
+ strncat(nntp, " ", len-strlen(nntp)-1);
+
+ strncat(nntp, q, len-strlen(nntp)-1);
+ }
+
+ nntp[len-1] = '\0';
+ }
+ }
+ else
+ nntp = cpystr(suggested_nntp_server);
+
+ pf_nntp_server->writehdr = 1;
+ pf_nntp_server->localcopy = 1;
+ if(nntp)
+ pf_nntp_server->textbuf = nntp;
+ else
+ pf_nntp_server->textbuf = cpystr("");
+ }
+
+ /*
+ * Write the list of custom headers to the
+ * X-Our-Headers header so that we can recover the
+ * list in redraft.
+ */
+ sz = 0;
+ for(pf = header.custom; pf && pf->name; pf = pf->next)
+ sz += strlen(pf->name) + 1;
+
+ if(sz){
+ char *q;
+
+ pf_ourhdrs->writehdr = 1;
+ pf_ourhdrs->localcopy = 1;
+ pf_ourhdrs->textbuf = (char *)fs_get(sz);
+ memset(pf_ourhdrs->textbuf, 0, sz);
+ q = pf_ourhdrs->textbuf;
+ for(pf = header.custom; pf && pf->name; pf = pf->next){
+ if(pf != header.custom)
+ sstrncpy(&q, ",", sz-(q-pf_ourhdrs->textbuf));
+
+ sstrncpy(&q, pf->name, sz-(q-pf_ourhdrs->textbuf));
+ }
+
+ pf_ourhdrs->textbuf[sz-1] = '\0';;
+ }
+
+ /*
+ * We need to make sure any header values that got cleared
+ * get written to the postponed message (they won't if
+ * pf->text is NULL). Otherwise, we can't tell previously
+ * non-existent custom headers or default values from
+ * custom (or other) headers that got blanked in the
+ * composer...
+ */
+ for(pf = header.local; pf && pf->name; pf = pf->next)
+ if(pf->type == FreeText && HE(pf) && !*(HE(pf)->realaddr))
+ *(HE(pf)->realaddr) = cpystr("");
+
+ /*
+ * We're saving the message for use later. It may be that the
+ * characters in the message are not all convertible to the
+ * user's posting_charmap. We'll save it as UTF-8 instead
+ * and worry about that the next time they try to send it.
+ * Use a different save pointer just to be sure we don't
+ * mess up the other stuff. We should probably make the
+ * charset an argument.
+ *
+ * We also need to fix the charset of the body part
+ * the user is editing so that we can read it back
+ * successfully when we resume the composition.
+ */
+ ps_global->post_utf8 = 1;
+
+ {
+ PARAMETER *pm;
+ BODY *bp;
+ if((*body)->type == TYPEMULTIPART)
+ bp = &(*body)->nested.part->body;
+ else
+ bp = *body;
+
+ for(pm = bp->parameter;
+ pm && strucmp(pm->attribute, "charset") != 0;
+ pm = pm->next)
+ ;
+
+ if(pm){
+ if(pm->value)
+ fs_give((void **) &pm->value);
+
+ pm->value = cpystr("UTF-8");
+ }
+ }
+
+ if(pine_rfc822_output(&header,*body,NULL,NULL) >= 0L){
+ if(editor_result & (COMP_GOTHUP | COMP_CANCEL)){
+ char *err;
+ STORE_S *hup_so;
+ gf_io_t gc, pc;
+ int we_cancel = 0;
+
+ if(editor_result & COMP_CANCEL){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,
+ "Saving to \"%s\"", folder);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ we_cancel = busy_cue((char *)tmp_20k_buf, NULL, 1);
+ }
+
+ if((hup_so =
+ so_get(FileStar, folder, WRITE_ACCESS|OWNER_ONLY)) != NULL){
+ gf_set_so_readc(&gc, lmc.so);
+ gf_set_so_writec(&pc, hup_so);
+ so_seek(lmc.so, 0L, 0); /* read msg copy and */
+ so_seek(hup_so, 0L, 2); /* append to folder */
+ gf_filter_init();
+ gf_link_filter(gf_nvtnl_local, NULL);
+ if(!(fcc_result = !(err = gf_pipe(gc, pc))))
+ dprint((1, "*** PIPE FAILED: %s\n",
+ err ? err : "?"));
+
+ gf_clear_so_readc(lmc.so);
+ gf_clear_so_writec(hup_so);
+ so_give(&hup_so);
+ }
+ else
+ dprint((1, "*** CAN'T CREATE %s: %s\n",
+ folder ? folder : "?",
+ error_description(errno)));
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+ }
+ else
+ fcc_result = write_fcc(folder, fcc_cntxt,
+ lmc.so, NULL, label, NULL);
+ }
+
+ /* discontinue coerced UTF-8 posting */
+ ps_global->post_utf8 = 0;
+
+ so_give(&lmc.so);
+ }
+ else
+ dprint((1, "***CAN'T ALLOCATE temp store: %s ",
+ error_description(errno)));
+
+ if(editor_result & COMP_GOTHUP){
+ /*
+ * Special Hack #291: if any hi-byte bits are set in
+ * editor's result, we put them there.
+ */
+ if(editor_result & 0xff00)
+ exit(editor_result >> 8);
+
+ dprint((1, "Save composition on HUP %sED\n",
+ fcc_result ? "SUCCEED" : "FAIL"));
+ hup_signal(); /* Do what we normally do on SIGHUP */
+ }
+ else if((editor_result & COMP_SUSPEND) && fcc_result){
+ if(ps_global->VAR_FORM_FOLDER
+ && ps_global->VAR_FORM_FOLDER[0]
+ && !strcmp(folder, ps_global->VAR_FORM_FOLDER))
+ q_status_message(SM_ORDER, 0, 3,
+ _("Composition saved to Form Letter Folder. Select Compose to send."));
+ else
+ q_status_message(SM_ORDER, 0, 3,
+ _("Composition postponed. Select Compose to resume."));
+
+ break; /* postpone went OK, get out of here */
+ }
+ else if(editor_result & COMP_CANCEL){
+ char *lc = NULL;
+
+ if(fcc_result && folder)
+ lc = last_cmpnt(folder);
+
+ q_status_message3(SM_ORDER, 0, 3,
+ _("Message cancelled%s%s%s"),
+ (lc && *lc) ? " and copied to \"" : "",
+ (lc && *lc) ? lc : "",
+ (lc && *lc) ? "\" file" : "");
+ break;
+ }
+ else{
+ q_status_message(SM_ORDER, 0, 4,
+ _("Continuing composition. Message not postponed or sent"));
+ body_start = 1;
+ continue; /* postpone failed, jump back in to composer */
+ }
+ }
+ else{
+ /*------ Must be sending mail or posting ! -----*/
+ int result, valid_addr, continue_with_only_fcc = 0;
+ CONTEXT_S *fcc_cntxt = NULL;
+
+ result = 0;
+ dprint((4, "=== sending: "));
+
+ /* --- If posting, confirm with user ----*/
+ if(outgoing->newsgroups && *outgoing->newsgroups
+ && F_OFF(F_QUELL_EXTRA_POST_PROMPT, ps_global)
+ && want_to(POST_PMT, 'n', 'n', NO_HELP, WT_NORM) == 'n'){
+ q_status_message(SM_ORDER, 0, 3, _("Message not posted"));
+ dprint((4, "no post, continuing\n"));
+ continue;
+ }
+
+ if(!(outgoing->to || outgoing->cc || outgoing->bcc || lcc_addr
+ || outgoing->newsgroups)){
+ if(fcc && fcc[0]){
+ if(F_OFF(F_AUTO_FCC_ONLY, ps_global) &&
+ want_to(_("No recipients, really copy only to Fcc "),
+ 'n', 'n', h_send_fcc_only, WT_NORM) != 'y')
+ continue;
+
+ continue_with_only_fcc++;
+ }
+ else{
+ q_status_message(SM_ORDER, 3, 4,
+ _("No recipients specified!"));
+ dprint((4, "no recip, continuing\n"));
+ continue;
+ }
+ }
+
+ if((valid_addr = check_addresses(&header)) == CA_BAD){
+ /*--- Addresses didn't check out---*/
+ dprint((4, "addrs failed, continuing\n"));
+ continue;
+ }
+
+ if(F_ON(F_WARN_ABOUT_NO_TO_OR_CC, ps_global)
+ && !continue_with_only_fcc
+ && !(outgoing->to || outgoing->cc || lcc_addr
+ || outgoing->newsgroups)
+ && (want_to(_("No To, Cc, or Newsgroup specified, send anyway "),
+ 'n', 'n', h_send_check_to_cc, WT_NORM) != 'y')){
+ dprint((4, "No To or CC or Newsgroup, continuing\n"));
+ if(local_redraft_pos && local_redraft_pos != redraft_pos)
+ free_redraft_pos(&local_redraft_pos);
+
+ local_redraft_pos
+ = (REDRAFT_POS_S *) fs_get(sizeof(*local_redraft_pos));
+ memset((void *) local_redraft_pos,0,sizeof(*local_redraft_pos));
+ local_redraft_pos->hdrname = cpystr(TONAME);
+ continue;
+ }
+
+ if(F_ON(F_WARN_ABOUT_NO_SUBJECT, ps_global)
+ && check_for_subject(&header) == CF_MISSING){
+ dprint((4, "No subject, continuing\n"));
+ if(local_redraft_pos && local_redraft_pos != redraft_pos)
+ free_redraft_pos(&local_redraft_pos);
+
+ local_redraft_pos
+ = (REDRAFT_POS_S *) fs_get(sizeof(*local_redraft_pos));
+ memset((void *) local_redraft_pos,0,sizeof(*local_redraft_pos));
+ local_redraft_pos->hdrname = cpystr(SUBJNAME);
+ continue;
+ }
+
+ if(F_ON(F_WARN_ABOUT_NO_FCC, ps_global)
+ && check_for_fcc(fcc) == CF_MISSING){
+ dprint((4, "No fcc, continuing\n"));
+ if(local_redraft_pos && local_redraft_pos != redraft_pos)
+ free_redraft_pos(&local_redraft_pos);
+
+ local_redraft_pos
+ = (REDRAFT_POS_S *) fs_get(sizeof(*local_redraft_pos));
+ memset((void *) local_redraft_pos,0,sizeof(*local_redraft_pos));
+ local_redraft_pos->hdrname = cpystr("Fcc");
+ continue;
+ }
+
+ set_last_fcc(fcc);
+
+ /*---- Check out fcc -----*/
+ if(fcc && *fcc){
+ /*
+ * If special name "inbox" then replace it with the
+ * real inbox path.
+ */
+ if(ps_global->VAR_INBOX_PATH
+ && strucmp(fcc, ps_global->inbox_name) == 0){
+ char *replace_fcc;
+
+ replace_fcc = cpystr(ps_global->VAR_INBOX_PATH);
+ fs_give((void **)&fcc);
+ fcc = replace_fcc;
+ }
+
+ lmc.all_written = lmc.text_written = 0;
+ /* lmc.text_only set on command line */
+ if(!(lmc.so = open_fcc(fcc, &fcc_cntxt, 0, NULL, NULL))){
+ /* ---- Open or allocation of fcc failed ----- */
+ dprint((4,"can't open/allocate fcc, cont'g\n"));
+
+ /*
+ * Find field entry associated with fcc, and start
+ * composer on it...
+ */
+ for(pf = header.local; pf && pf->name; pf = pf->next)
+ if(pf->type == Fcc && HE(pf))
+ HE(pf)->start_here = 1;
+
+ continue;
+ }
+ else
+ so_truncate(lmc.so, fcc_size_guess(*body) + 2048);
+ }
+ else
+ lmc.so = NULL;
+
+ /*---- Take care of any requested prefiltering ----*/
+ if(sending_filter_requested
+ && !filter_message_text(sending_filter_requested, outgoing,
+ *body, &orig_so, &header)){
+ q_status_message1(SM_ORDER, 3, 3,
+ _("Problem filtering! Nothing sent%s."),
+ fcc ? " or saved to fcc" : "");
+ continue;
+ }
+
+ /*------ Actually post -------*/
+ if(outgoing->newsgroups){
+ char **alt_nntp = NULL, *alt_nntp_p[2];
+ if(((role && role->nntp)
+ || suggested_nntp_server)){
+ if(ps_global->FIX_NNTP_SERVER
+ && ps_global->FIX_NNTP_SERVER[0])
+ q_status_message(SM_ORDER | SM_DING, 5, 5,
+ "Using nntp-server that is administratively fixed");
+ else if(role && role->nntp)
+ alt_nntp = role->nntp;
+ else{
+ alt_nntp_p[0] = suggested_nntp_server;
+ alt_nntp_p[1] = NULL;
+ alt_nntp = alt_nntp_p;
+ }
+ }
+ if(news_poster(&header, *body, alt_nntp, pipe_callback) < 0){
+ dprint((1, "Post failed, continuing\n"));
+ if(outgoing->message_id)
+ fs_give((void **) &outgoing->message_id);
+
+ outgoing->message_id = generate_message_id();
+
+ continue;
+ }
+ else
+ result |= P_NEWS_WIN;
+ }
+
+ /*
+ * BUG: IF we've posted the message *and* an fcc was specified
+ * then we've already got a neatly formatted message in the
+ * lmc.so. It'd be nice not to have to re-encode everything
+ * to insert it into the smtp slot...
+ */
+
+ /*
+ * Turn on "undisclosed recipients" header if no To or cc.
+ */
+ if(!(outgoing->to || outgoing->cc)
+ && (outgoing->bcc || lcc_addr) && pf_nobody && pf_nobody->addr){
+ pf_nobody->writehdr = 1;
+ pf_nobody->localcopy = 1;
+ }
+
+ if(priority_requested){
+ (void) set_priority_header(&header, priority_requested);
+ fs_give((void **) &priority_requested);
+ }
+
+#if defined(BACKGROUND_POST) && defined(SIGCHLD)
+ /*
+ * If requested, launch backgroud posting...
+ */
+ if(background_requested && !(call_mailer_flags & CM_VERBOSE)){
+ ps_global->post = (POST_S *)fs_get(sizeof(POST_S));
+ memset(ps_global->post, 0, sizeof(POST_S));
+ if(fcc)
+ ps_global->post->fcc = cpystr(fcc);
+
+ if((ps_global->post->pid = fork()) == 0){
+ /*
+ * Put us in new process group...
+ */
+ setpgrp(0, ps_global->post->pid);
+
+ /* BUG: should fix argv[0] to indicate what we're up to */
+
+ /*
+ * If there are any live streams, pretend we never
+ * knew them. Problem is two processes writing
+ * same server process.
+ * This is not clean but we're just going to exit
+ * right away anyway. We just want to be sure to leave
+ * the stuff that the parent is going to use alone.
+ * The next three lines will disable the re-use of the
+ * existing streams and cause us to open a new one if
+ * needed.
+ */
+ ps_global->mail_stream = NULL;
+ ps_global->s_pool.streams = NULL;
+ ps_global->s_pool.nstream = 0;
+
+ /* quell any display output */
+ ps_global->in_init_seq = 1;
+
+ /*------- Actually mail the message ------*/
+ if(valid_addr == CA_OK
+ && (outgoing->to || outgoing->cc
+ || outgoing->bcc || lcc_addr)){
+ char **alt_smtp = NULL;
+
+ if(role && role->smtp){
+ if(ps_global->FIX_SMTP_SERVER
+ && ps_global->FIX_SMTP_SERVER[0])
+ q_status_message(SM_ORDER | SM_DING, 5, 5, "Use of a role-defined smtp-server is administratively prohibited");
+ else
+ alt_smtp = role->smtp;
+ }
+
+ result |= (call_mailer(&header, *body, alt_smtp,
+ call_mailer_flags,
+ call_mailer_file_result,
+ pipe_callback) > 0)
+ ? P_MAIL_WIN : P_MAIL_LOSE;
+
+ if(result & P_MAIL_LOSE)
+ mark_address_failure_for_pico(&header);
+ }
+
+ /*----- Was there an fcc involved? -----*/
+ if(lmc.so){
+ /*------ Write it if at least something worked ------*/
+ if((result & (P_MAIL_WIN | P_NEWS_WIN))
+ || (!(result & (P_MAIL_BITS | P_NEWS_BITS))
+ && pine_rfc822_output(&header, *body,
+ NULL, NULL))){
+ char label[50];
+
+ strncpy(label, "Fcc", sizeof(label));
+ label[sizeof(label)-1] = '\0';
+ if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC)){
+ snprintf(label + 3, sizeof(label)-3, " to %s", fcc);
+ label[sizeof(label)-1] = '\0';
+ }
+
+ /*-- Now actually copy to fcc folder and close --*/
+ result |= (write_fcc(fcc, fcc_cntxt, lmc.so,
+ NULL, label,
+ F_ON(F_MARK_FCC_SEEN, ps_global)
+ ? "\\SEEN" : NULL))
+ ? P_FCC_WIN : P_FCC_LOSE;
+ }
+ else if(!(result & (P_MAIL_BITS | P_NEWS_BITS))){
+ q_status_message(SM_ORDER, 3, 5,
+ _("Fcc Failed!. No message saved."));
+ dprint((1,
+ "explicit fcc write failed!\n"));
+ result |= P_FCC_LOSE;
+ }
+
+ so_give(&lmc.so);
+ }
+
+ if(result & (P_MAIL_LOSE | P_NEWS_LOSE | P_FCC_LOSE)){
+ /*
+ * Encode child's result in hi-byte of
+ * editor's result
+ */
+ editor_result = ((result << 8) | COMP_GOTHUP);
+ goto fake_hup;
+ }
+
+ exit(result);
+ }
+
+ if(ps_global->post->pid > 0){
+ q_status_message(SM_ORDER, 3, 3,
+ _("Message handed off for posting"));
+ break; /* up to our child now */
+ }
+ else{
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ "Can't fork for send: %s",
+ error_description(errno));
+ if(ps_global->post->fcc)
+ fs_give((void **) &ps_global->post->fcc);
+
+ fs_give((void **) &ps_global->post);
+ }
+
+ if(lmc.so) /* throw away unused store obj */
+ so_give(&lmc.so);
+
+ if(outgoing->message_id)
+ fs_give((void **) &outgoing->message_id);
+
+ outgoing->message_id = generate_message_id();
+
+ continue; /* if we got here, there was a prob */
+ }
+#endif /* BACKGROUND_POST */
+
+ /*------- Actually mail the message ------*/
+ if(valid_addr == CA_OK
+ && (outgoing->to || outgoing->cc || outgoing->bcc || lcc_addr)){
+ char **alt_smtp = NULL;
+
+ if(role && role->smtp){
+ if(ps_global->FIX_SMTP_SERVER
+ && ps_global->FIX_SMTP_SERVER[0])
+ q_status_message(SM_ORDER | SM_DING, 5, 5, "Use of a role-defined smtp-server is administratively prohibited");
+ else
+ alt_smtp = role->smtp;
+ }
+
+ result |= (call_mailer(&header, *body, alt_smtp,
+ call_mailer_flags,
+ call_mailer_file_result,
+ pipe_callback) > 0)
+ ? P_MAIL_WIN : P_MAIL_LOSE;
+
+ if(result & P_MAIL_LOSE)
+ mark_address_failure_for_pico(&header);
+ }
+
+ /*----- Was there an fcc involved? -----*/
+ if(lmc.so){
+ /*------ Write it if at least something worked ------*/
+ if((result & (P_MAIL_WIN | P_NEWS_WIN))
+ || (!(result & (P_MAIL_BITS | P_NEWS_BITS))
+ && pine_rfc822_output(&header, *body, NULL, NULL))){
+ char label[50];
+
+ strncpy(label, "Fcc", sizeof(label));
+ label[sizeof(label)-1] = '\0';
+ if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC)){
+ snprintf(label + 3, sizeof(label)-3, " to %s", fcc);
+ label[sizeof(label)-1] = '\0';
+ }
+
+ /*-- Now actually copy to fcc folder and close --*/
+ result |= (write_fcc(fcc, fcc_cntxt, lmc.so, NULL, label,
+ F_ON(F_MARK_FCC_SEEN, ps_global)
+ ? "\\SEEN" : NULL))
+ ? P_FCC_WIN : P_FCC_LOSE;
+ }
+ else if(!(result & (P_MAIL_BITS | P_NEWS_BITS))){
+ q_status_message(SM_ORDER,3,5,
+ _("Fcc Failed!. No message saved."));
+ dprint((1, "explicit fcc write failed!\n"));
+ result |= P_FCC_LOSE;
+ }
+
+ so_give(&lmc.so);
+ }
+
+ /*----- Mail Post FAILED, back to composer -----*/
+ if(result & (P_MAIL_LOSE | P_FCC_LOSE)){
+ dprint((1, "Send failed, continuing\n"));
+
+ if(result & P_FCC_LOSE){
+ /*
+ * Find field entry associated with fcc, and start
+ * composer on it...
+ */
+ for(pf = header.local; pf && pf->name; pf = pf->next)
+ if(pf->type == Fcc && HE(pf))
+ HE(pf)->start_here = 1;
+
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ pine_send_status(result, fcc,
+ tmp_20k_buf, SIZEOF_20KBUF, NULL));
+ }
+
+ if(outgoing->message_id)
+ fs_give((void **) &outgoing->message_id);
+
+ outgoing->message_id = generate_message_id();
+
+ continue;
+ }
+
+ /*
+ * If message sent *completely* successfully, there's a
+ * reply struct AND we're allowed to write back state, do it.
+ * But also protect against shifted message numbers due
+ * to new mail arrival. Since the number passed is based
+ * on the real imap msg no, AND we're sure no expunge has
+ * been done, just fix up the sorted number...
+ */
+ update_answered_flags(reply);
+
+ /*----- Signed, sealed, delivered! ------*/
+ q_status_message(SM_ORDER, 0, 3,
+ pine_send_status(result, fcc, tmp_20k_buf, SIZEOF_20KBUF, NULL));
+
+ break; /* All's well, pop out of here */
+ }
+ }
+
+ if(orig_so)
+ so_give(&orig_so);
+
+ if(fcc)
+ fs_give((void **)&fcc);
+
+ free_body_particulars(bp);
+
+ free_attachment_list(&pbf->attachments);
+
+ standard_picobuf_teardown(pbf);
+
+ for(i=0; i < fixed_cnt; i++){
+ if(pfields[i].textbuf)
+ fs_give((void **)&pfields[i].textbuf);
+
+ fs_give((void **)&pfields[i].name);
+ }
+
+ if(lcc_addr)
+ mail_free_address(&lcc_addr);
+
+ if(nobody_addr)
+ mail_free_address(&nobody_addr);
+
+ free_prompts(header.custom);
+ free_customs(header.custom);
+ fs_give((void **)&pfields);
+ free_headents(&headents);
+ fs_give((void **)&sending_order);
+ if(suggested_nntp_server)
+ fs_give((void **)&suggested_nntp_server);
+ if(title)
+ fs_give((void **)&title);
+
+ if(local_redraft_pos && local_redraft_pos != redraft_pos)
+ free_redraft_pos(&local_redraft_pos);
+
+ pbf = save_previous_pbuf;
+ g_rolenick = NULL;
+
+ dprint((4, "=== send returning ===\n"));
+}
+
+
+/*
+ * Check for subject in outgoing message.
+ *
+ * Asks user whether to proceed with no subject.
+ */
+int
+check_for_subject(METAENV *header)
+{
+ PINEFIELD *pf;
+ int rv = CF_OK;
+
+ for(pf = header->local; pf && pf->name; pf = pf->next)
+ if(pf->type == Subject){
+ if(pf->text && *pf->text && **pf->text)
+ rv = CF_OK;
+ else{
+ if(want_to("No Subject, send anyway ",
+ 'n', 'n', h_send_check_subj, WT_NORM) == 'y')
+ rv = CF_OK;
+ else
+ rv = CF_MISSING;
+ }
+
+ break;
+ }
+
+
+ return(rv);
+}
+
+
+/*
+ * Check for fcc in outgoing message.
+ *
+ * Asks user whether to proceed with no fcc.
+ */
+int
+check_for_fcc(char *fcc)
+{
+ int rv = CF_OK;
+
+ if(fcc && *fcc)
+ rv = CF_OK;
+ else{
+ if(want_to("No Fcc, send anyway ", 'n', 'n', h_send_check_fcc, WT_NORM) == 'y')
+ rv = CF_OK;
+ else
+ rv = CF_MISSING;
+ }
+
+ return(rv);
+}
+
+
+/*
+ * Confirm that the user wants to send to MAILER-DAEMON
+ */
+int
+confirm_daemon_send(void)
+{
+ return(want_to("Really send this message to the MAILER-DAEMON",
+ 'n', 'n', NO_HELP, WT_NORM) == 'y');
+}
+
+
+void
+free_prompts(PINEFIELD *head)
+{
+ PINEFIELD *pf;
+
+ for(pf = head; pf && pf->name; pf = pf->next){
+ if(HE(pf) && HE(pf)->prompt)
+ fs_give((void **)& HE(pf)->prompt);
+ }
+}
+
+
+int
+postpone_prompt(void)
+{
+ static ESCKEY_S pstpn_form_opt[] = { {'p', 'p', "P", N_("Postponed Folder")},
+ {'f', 'f', "F", N_("Form Letter Folder")},
+ {-1, 0, NULL, NULL} };
+
+ return(radio_buttons(PSTPN_FORM_PMT, -FOOTER_ROWS(ps_global),
+ pstpn_form_opt, 'p', 0, NO_HELP, RB_FLUSH_IN));
+}
+
+
+/*
+ * call__mailer_file_result - some results from call_mailer might be in a file.
+ * dislplay that file.
+ */
+void
+call_mailer_file_result(char *filename, int style)
+{
+ if(filename){
+ if(style & CM_BR_VERBOSE){
+ display_output_file(filename, "Verbose SMTP Interaction", NULL, DOF_BRIEF);
+ }
+ else{
+ display_output_file(filename, "POSTING ERRORS", "Posting Error", DOF_EMPTY);
+ }
+ }
+}
+
+void
+mark_address_failure_for_pico(METAENV *header)
+{
+ PINEFIELD *pf;
+ ADDRESS *a;
+ int error_count = 0;
+ struct headerentry *last_he = NULL;
+
+ for(pf = header->local; pf && pf->name; pf = pf->next)
+ if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr)
+ for(a = *pf->addr; a != NULL; a = a->next)
+ if(a->error != NULL
+ && error_count++ < MAX_ADDR_ERROR
+ && (HE(pf))){
+ if(last_he) /* start last reported err */
+ last_he->start_here = 0;
+
+ (last_he = HE(pf))->start_here = 1;
+ }
+}
+
+
+
+/*
+ * This is specialized routine. It assumes that the only things that we
+ * care about restoring are the body type, subtype, encoding and the
+ * state of the charset parameter. It also assumes that if the charset
+ * parameter exists when we save it, it won't be removed later.
+ */
+BODY_PARTICULARS_S *
+save_body_particulars(struct mail_bodystruct *body)
+{
+ BODY_PARTICULARS_S *bp;
+ PARAMETER *pm;
+
+ bp = (BODY_PARTICULARS_S *)fs_get(sizeof(BODY_PARTICULARS_S));
+
+ bp->type = body->type;
+ bp->encoding = body->encoding;
+ bp->subtype = body->subtype ? cpystr(body->subtype) : NULL;
+ bp->parameter = body->parameter;
+ for(pm = bp->parameter;
+ pm && strucmp(pm->attribute, "charset") != 0;
+ pm = pm->next)
+ ;/* searching for possible charset parameter */
+
+ if(pm){ /* found one */
+ bp->had_csp = 1; /* saved body had charset parameter */
+ bp->charset = pm->value ? cpystr(pm->value) : NULL;
+ }
+ else{
+ bp->had_csp = 0;
+ bp->charset = NULL;
+ }
+
+ return(bp);
+}
+
+
+void
+reset_body_particulars(BODY_PARTICULARS_S *bp, struct mail_bodystruct *body)
+{
+ body->type = bp->type;
+ body->encoding = bp->encoding;
+ if(body->subtype)
+ fs_give((void **)&body->subtype);
+
+ body->subtype = bp->subtype ? cpystr(bp->subtype) : NULL;
+
+ if(bp->parameter){
+ PARAMETER *pm, *pm_prev = NULL;
+
+ for(pm = body->parameter;
+ pm && strucmp(pm->attribute, "charset") != 0;
+ pm = pm->next)
+ pm_prev = pm;
+
+ if(pm){ /* body has charset parameter */
+ if(bp->had_csp){ /* reset to what it used to be */
+ if(pm->value)
+ fs_give((void **)&pm->value);
+
+ pm->value = bp->charset ? cpystr(bp->charset) : NULL;
+ }
+ else{ /* remove charset parameter */
+ if(pm_prev)
+ pm_prev->next = pm->next;
+ else
+ body->parameter = pm->next;
+
+ mail_free_body_parameter(&pm);
+ }
+ }
+ else{
+ if(bp->had_csp){
+ /*
+ * This can't happen because grope never removes
+ * the charset parameter.
+ */
+ q_status_message(SM_ORDER | SM_DING, 5, 5,
+"Programmer error: saved charset but no current charset param in pine_send");
+ }
+ /*
+ else{
+ ok, still no parameter
+ }
+ */
+ }
+ }
+ else{
+ if(body->parameter)
+ mail_free_body_parameter(&body->parameter);
+
+ body->parameter = NULL;
+ }
+}
+
+
+void
+free_body_particulars(BODY_PARTICULARS_S *bp)
+{
+ if(bp){
+ if(bp->subtype)
+ fs_give((void **)&bp->subtype);
+
+ if(bp->charset)
+ fs_give((void **)&bp->charset);
+
+ fs_give((void **)&bp);
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Build a status message suitable for framing
+
+Returns: pointer to resulting buffer
+ ---*/
+char *
+pine_send_status(int result, char *fcc_name, char *buf, size_t buflen, int *goodorbad)
+{
+ int avail = ps_global->ttyo->screen_cols - 2;
+ int fixedneed, need, lenfcc;
+ char *part1, *part2, *part3, *part4, *part5;
+ char fbuf[MAILTMPLEN+1];
+
+ part1 = (result & P_NEWS_WIN)
+ ? "posted"
+ : (result & P_NEWS_LOSE)
+ ? "NOT POSTED"
+ : "";
+ part2 = ((result & P_NEWS_BITS) && (result & P_MAIL_BITS)
+ && (result & P_FCC_BITS))
+ ? ", "
+ : ((result & P_NEWS_BITS) && (result & (P_MAIL_BITS | P_FCC_BITS)))
+ ? " and "
+ : "";
+ part3 = (result & P_MAIL_WIN)
+ ? "sent"
+ : (result & P_MAIL_LOSE)
+ ? "NOT SENT"
+ : "";
+ part4 = ((result & P_MAIL_BITS) && (result & P_FCC_BITS))
+ ? " and "
+ : "";
+ part5 = ((result & P_FCC_WIN) && !(result & (P_MAIL_WIN | P_NEWS_WIN)))
+ ? "ONLY copied to "
+ : (result & P_FCC_WIN)
+ ? "copied to "
+ : (result & P_FCC_LOSE)
+ ? "NOT copied to "
+ : "";
+ lenfcc = MIN(sizeof(fbuf)-1, (result & P_FCC_BITS) ? strlen(fcc_name) : 0);
+
+ fixedneed = 9 + strlen(part1) + strlen(part2) + strlen(part3) +
+ strlen(part4) + strlen(part5);
+ need = fixedneed + ((result & P_FCC_BITS) ? 2 : 0) + lenfcc;
+
+ if(need > avail && fixedneed + 3 >= avail){
+ /* dots on end of fixed, no fcc */
+ snprintf(fbuf, sizeof(fbuf), "Message %s%s%s%s%s ",
+ part1, part2, part3, part4, part5);
+ short_str(fbuf, buf, buflen, avail, EndDots);
+ }
+ else if(need > avail){
+ /* include whole fixed part, quotes and dots at end of fcc name */
+ if(lenfcc > 0)
+ lenfcc = MAX(1, lenfcc-(need-avail));
+
+ snprintf(buf, buflen, "Message %s%s%s%s%s%s%s%s.",
+ part1, part2, part3, part4, part5,
+ (result & P_FCC_BITS) ? "\"" : "",
+ short_str((result & P_FCC_BITS) ? fcc_name : "",
+ fbuf, sizeof(fbuf), lenfcc, FrontDots),
+ (result & P_FCC_BITS) ? "\"" : "");
+ }
+ else{
+ /* whole thing */
+ snprintf(buf, buflen, "Message %s%s%s%s%s%s%s%s.",
+ part1, part2, part3, part4, part5,
+ (result & P_FCC_BITS) ? "\"" : "",
+ (result & P_FCC_BITS) ? fcc_name : "",
+ (result & P_FCC_BITS) ? "\"" : "");
+ }
+
+ if(goodorbad)
+ *goodorbad = (result & (P_MAIL_LOSE | P_NEWS_LOSE | P_FCC_LOSE)) == 0;
+
+ return(buf);
+}
+
+/* Callback from Pico to set the conditions for Alpine to start a new thread
+ */
+
+void
+new_thread_on_blank_subject(void)
+{
+ ps_global->newthread = F_ON(F_NEW_THREAD_ON_BLANK_SUBJECT, ps_global);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Call back for pico to insert the specified message's text
+
+Args: n -- message number to format
+ f -- function to use to output the formatted message
+
+
+Returns: returns msg number formatted on success, zero on error.
+----*/
+long
+message_format_for_pico(long int n, int (*f) (int))
+{
+ ENVELOPE *e;
+ BODY *b;
+ char *old_quote = NULL;
+ long rv = n;
+
+ if(!(n > 0L && n <= mn_get_total(ps_global->msgmap)
+ && (e = pine_mail_fetchstructure(ps_global->mail_stream,
+ mn_m2raw(ps_global->msgmap, n), &b)))){
+ q_status_message(SM_ORDER|SM_DING,3,3,"Error inserting Message");
+ flush_status_messages(0);
+ return(0L);
+ }
+
+ /* temporarily assign a new quote string */
+ old_quote = pbf->quote_str;
+ pbf->quote_str = reply_quote_str(e);
+
+ /* build separator line */
+ reply_delimiter(e, NULL, f);
+
+ /* actually write message text */
+ if(!format_message(mn_m2raw(ps_global->msgmap, n), e, b, NULL,
+ FM_NEW_MESS | FM_DISPLAY | FM_NOCOLOR | FM_NOINDENT, f)){
+ q_status_message(SM_ORDER|SM_DING,3,3,"Error inserting Message");
+ flush_status_messages(0);
+ rv = 0L;
+ }
+
+ fs_give((void **)&pbf->quote_str);
+ pbf->quote_str = old_quote;
+ return(rv);
+}
+
+
+/*----------------------------------------------------------------------
+ Call back for pico to prompt the user for exit confirmation
+
+Args: dflt -- default answer for confirmation prompt
+
+Returns: either NULL if the user accepts exit, or string containing
+ reason why the user declined.
+----*/
+int
+send_exit_for_pico(struct headerentry *he, void (*redraw_pico)(void), int allow_flowed,
+ char **result)
+{
+ int i, rv, c, verbose_label = 0, bg_label = 0, old_suspend;
+ int dsn_label = 0, fcc_label = 0, lparen;
+ int flowing_label = 0, double_rad;
+ char *rstr = NULL, *p, *lc, *optp;
+ char dsn_string[30];
+ void (*redraw)(void) = ps_global->redrawer;
+ ESCKEY_S opts[18];
+ struct filters {
+ char *filter;
+ int index;
+ struct filters *prev, *next;
+ } *filters = NULL, *fp;
+
+ sending_filter_requested = NULL;
+ call_mailer_flags = 0;
+ background_requested = 0;
+ flowing_requested = allow_flowed ? 1 : 0;
+ lmc.text_only = F_ON(F_NO_FCC_ATTACH, ps_global) != 0;
+ if(priority_requested)
+ fs_give((void **) &priority_requested);
+
+ if(background_posting(FALSE)){
+ if(result)
+ *result = "Can't send while background posting. Use postpone.";
+
+ return(1);
+ }
+
+ if(F_ON(F_SEND_WO_CONFIRM, ps_global)){
+ if(result)
+ *result = NULL;
+
+ return(0);
+ }
+
+ ps_global->redrawer = redraw_pico;
+
+ if((old_suspend = F_ON(F_CAN_SUSPEND, ps_global)) != 0)
+ (void) F_SET(F_CAN_SUSPEND, ps_global, 0);
+
+ /*
+ * Build list of available filters...
+ */
+ for(i=0; ps_global->VAR_SEND_FILTER && ps_global->VAR_SEND_FILTER[i]; i++){
+ for(p = ps_global->VAR_SEND_FILTER[i];
+ *p && !isspace((unsigned char)*p); p++)
+ ;
+
+ c = *p;
+ *p = '\0';
+ if(!(is_absolute_path(ps_global->VAR_SEND_FILTER[i])
+ && can_access(ps_global->VAR_SEND_FILTER[i],EXECUTE_ACCESS) ==0)){
+ *p = c;
+ continue;
+ }
+
+ fp = (struct filters *)fs_get(sizeof(struct filters));
+ fp->index = i;
+ if((lc = last_cmpnt(ps_global->VAR_SEND_FILTER[i])) != NULL){
+ fp->filter = cpystr(lc);
+ }
+ else if((p - ps_global->VAR_SEND_FILTER[i]) > 20){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "...%s", p - 17);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ fp->filter = cpystr(tmp_20k_buf);
+ }
+ else
+ fp->filter = cpystr(ps_global->VAR_SEND_FILTER[i]);
+
+ *p = c;
+
+ if(filters){
+ fp->next = filters;
+ fp->prev = filters->prev;
+ fp->prev->next = filters->prev = fp;
+ }
+ else{
+ filters = (struct filters *)fs_get(sizeof(struct filters));
+ filters->index = -1;
+ filters->filter = NULL;
+ filters->next = filters->prev = fp;
+ fp->next = fp->prev = filters;
+ }
+ }
+
+ i = 0;
+ opts[i].ch = 'y';
+ opts[i].rval = 'y';
+ opts[i].name = "Y";
+ opts[i++].label = N_("Yes");
+
+ opts[i].ch = 'n';
+ opts[i].rval = 'n';
+ opts[i].name = "N";
+ opts[i++].label = N_("No");
+
+ if(filters){
+ /* set global_filter_pointer to desired filter or NULL if none */
+ /* prepare two keymenu slots for selecting filter */
+ opts[i].ch = ctrl('P');
+ opts[i].rval = 10;
+ opts[i].name = "^P";
+ opts[i++].label = N_("Prev Filter");
+
+ opts[i].ch = ctrl('N');
+ opts[i].rval = 11;
+ opts[i].name = "^N";
+ opts[i++].label = N_("Next Filter");
+
+ if(F_ON(F_FIRST_SEND_FILTER_DFLT, ps_global))
+ filters = filters->next;
+ }
+
+ if(F_ON(F_VERBOSE_POST, ps_global)){
+ /* setup keymenu slot to toggle verbose mode */
+ opts[i].ch = ctrl('W');
+ opts[i].rval = 12;
+ opts[i].name = "^W";
+ verbose_label = i++;
+ }
+
+ if(allow_flowed){
+ /* setup keymenu slot to toggle flowed mode */
+ opts[i].ch = ctrl('V');
+ opts[i].rval = 22;
+ opts[i].name = "^V";
+ flowing_label = i++;
+ flowing_requested = 1;
+ }
+
+ if(F_ON(F_NO_FCC_ATTACH, ps_global)){
+ /* setup keymenu slot to toggle attacment on fcc */
+ opts[i].ch = ctrl('F');
+ opts[i].rval = 21;
+ opts[i].name = "^F";
+ fcc_label = i++;
+ }
+
+#if defined(BACKGROUND_POST) && defined(SIGCHLD)
+ if(F_ON(F_BACKGROUND_POST, ps_global)){
+ opts[i].ch = ctrl('R');
+ opts[i].rval = 15;
+ opts[i].name = "^R";
+ bg_label = i++;
+ }
+#endif
+
+ opts[i].ch = 'p';
+ opts[i].rval = 'p';
+ opts[i].name = "P";
+ opts[i++].label = N_("Priority");
+
+#ifdef SMIME
+ if(F_OFF(F_DONT_DO_SMIME, ps_global)){
+ opts[i].ch = 'e';
+ opts[i].rval = 'e';
+ opts[i].name = "E";
+ opts[i++].label = "Encrypt";
+
+ opts[i].ch = 'g';
+ opts[i].rval = 'g';
+ opts[i].name = "G";
+ opts[i++].label = "Sign";
+
+ if(ps_global->smime){
+ ps_global->smime->do_encrypt = F_ON(F_ENCRYPT_DEFAULT_ON, ps_global);
+ ps_global->smime->do_sign = F_ON(F_SIGN_DEFAULT_ON, ps_global);
+ }
+ }
+#endif
+
+ double_rad = i;
+
+ if(F_ON(F_DSN, ps_global)){
+ /* setup keymenu slots to toggle dsn bits */
+ opts[i].ch = 'd';
+ opts[i].rval = 'd';
+ opts[i].name = "D";
+ opts[i].label = N_("DSNOpts");
+ dsn_label = i++;
+ opts[i].ch = -2;
+ opts[i].rval = 's';
+ opts[i].name = "S";
+ opts[i++].label = "";
+ opts[i].ch = -2;
+ opts[i].rval = 'x';
+ opts[i].name = "X";
+ opts[i++].label = "";
+ opts[i].ch = -2;
+ opts[i].rval = 'h';
+ opts[i].name = "H";
+ opts[i++].label = "";
+ }
+
+ if(filters){
+ opts[i].ch = KEY_UP;
+ opts[i].rval = 10;
+ opts[i].name = "";
+ opts[i++].label = "";
+
+ opts[i].ch = KEY_DOWN;
+ opts[i].rval = 11;
+ opts[i].name = "";
+ opts[i++].label = "";
+ }
+
+ opts[i].ch = -1;
+
+ fix_windsize(ps_global);
+
+ while(1){
+ if(filters && filters->filter && (p = strindex(filters->filter, ' ')))
+ *p = '\0';
+ else
+ p = NULL;
+
+ lparen = 0;
+ strncpy(tmp_20k_buf, "Send message", SIZEOF_20KBUF);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ optp = tmp_20k_buf + strlen(tmp_20k_buf);
+
+ if(F_ON(F_NO_FCC_ATTACH, ps_global) && !lmc.text_only)
+ sstrncpy(&optp, " and Fcc Atmts", SIZEOF_20KBUF-(optp-tmp_20k_buf));
+
+ if(allow_flowed && !flowing_requested){
+ if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
+ if(!lparen){
+ *optp++ = ' ';
+ *optp++ = '(';
+ lparen++;
+ }
+ else
+ *optp++ = ' ';
+ }
+
+ sstrncpy(&optp, "not flowed", SIZEOF_20KBUF-(optp-tmp_20k_buf));
+ }
+
+ if(filters){
+ if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
+ if(!lparen){
+ *optp++ = ' ';
+ *optp++ = '(';
+ lparen++;
+ }
+ else{
+ *optp++ = ',';
+ *optp++ = ' ';
+ }
+ }
+
+ if(filters->filter){
+ sstrncpy(&optp, "filtered thru \"", SIZEOF_20KBUF-(optp-tmp_20k_buf));
+ sstrncpy(&optp, filters->filter, SIZEOF_20KBUF-(optp-tmp_20k_buf));
+ if((optp-tmp_20k_buf) < SIZEOF_20KBUF)
+ *optp++ = '\"';
+ }
+ else
+ sstrncpy(&optp, "unfiltered", SIZEOF_20KBUF-(optp-tmp_20k_buf));
+ }
+
+ if((call_mailer_flags & CM_VERBOSE) || background_requested){
+ if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
+ if(!lparen){
+ *optp++ = ' ';
+ *optp++ = '(';
+ lparen++;
+ }
+ else
+ *optp++ = ' ';
+ }
+
+ sstrncpy(&optp, "in ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
+ if(call_mailer_flags & CM_VERBOSE)
+ sstrncpy(&optp, "verbose ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
+
+ if(background_requested)
+ sstrncpy(&optp, "background ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
+
+ sstrncpy(&optp, "mode", SIZEOF_20KBUF-(optp-tmp_20k_buf));
+ }
+
+ if(g_rolenick && !(he && he[N_FROM].dirty)){
+ if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
+ if(!lparen){
+ *optp++ = ' ';
+ *optp++ = '(';
+ lparen++;
+ }
+ else
+ *optp++ = ' ';
+ }
+
+ sstrncpy(&optp, "as \"", SIZEOF_20KBUF-(optp-tmp_20k_buf));
+ sstrncpy(&optp, g_rolenick, SIZEOF_20KBUF-(optp-tmp_20k_buf));
+ sstrncpy(&optp, "\"", SIZEOF_20KBUF-(optp-tmp_20k_buf));
+ }
+
+ if(call_mailer_flags & CM_DSN_SHOW){
+ if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
+ if(!lparen){
+ *optp++ = ' ';
+ *optp++ = '(';
+ lparen++;
+ }
+ else{
+ *optp++ = ',';
+ *optp++ = ' ';
+ }
+ }
+
+ sstrncpy(&optp, dsn_string, SIZEOF_20KBUF-(optp-tmp_20k_buf));
+ }
+
+#ifdef SMIME
+ if(ps_global->smime && ps_global->smime->do_encrypt){
+ if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
+ if(!lparen){
+ *optp++ = ' ';
+ *optp++ = '(';
+ lparen++;
+ }
+ else{
+ *optp++ = ',';
+ *optp++ = ' ';
+ }
+ }
+
+ sstrncpy(&optp, "Encrypted", SIZEOF_20KBUF-(optp-tmp_20k_buf));
+ }
+
+ if(ps_global->smime && ps_global->smime->do_sign){
+ if((optp-tmp_20k_buf)+2 < SIZEOF_20KBUF){
+ if(!lparen){
+ *optp++ = ' ';
+ *optp++ = '(';
+ lparen++;
+ }
+ else{
+ *optp++ = ',';
+ *optp++ = ' ';
+ }
+ }
+
+ sstrncpy(&optp, "Signed", SIZEOF_20KBUF-(optp-tmp_20k_buf));
+ }
+#endif
+
+ if(lparen && (optp-tmp_20k_buf) < SIZEOF_20KBUF)
+ *optp++ = ')';
+
+ sstrncpy(&optp, "? ", SIZEOF_20KBUF-(optp-tmp_20k_buf));
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+
+ if(p)
+ *p = ' ';
+
+ if(flowing_label)
+ opts[flowing_label].label = flowing_requested ? N_("NoFlow") : N_("Flow");
+
+ if(verbose_label)
+ opts[verbose_label].label = (call_mailer_flags & CM_VERBOSE) ? N_("Normal") : N_("Verbose");
+
+ if(bg_label)
+ opts[bg_label].label = background_requested
+ ? N_("Foreground") : N_("Background");
+
+ if(fcc_label)
+ opts[fcc_label].label = lmc.text_only ? N_("Fcc Attchmnts")
+ : N_("No Fcc Atmts ");
+
+ if(F_ON(F_DSN, ps_global)){
+ if(call_mailer_flags & CM_DSN_SHOW){
+ opts[dsn_label].label = (call_mailer_flags & CM_DSN_DELAY)
+ ? N_("NoDelay") : N_("Delay");
+ opts[dsn_label+1].ch = 's';
+ opts[dsn_label+1].label = (call_mailer_flags & CM_DSN_SUCCESS)
+ ? N_("NoSuccess") : N_("Success");
+ opts[dsn_label+2].ch = 'x';
+ opts[dsn_label+2].label = (call_mailer_flags & CM_DSN_NEVER)
+ ? N_("ErrRets") : N_("NoErrRets");
+ opts[dsn_label+3].ch = 'h';
+ opts[dsn_label+3].label = (call_mailer_flags & CM_DSN_FULL)
+ ? N_("RetHdrs") : N_("RetFull");
+ }
+ }
+
+ if(double_rad +
+ ((call_mailer_flags & CM_DSN_SHOW)
+ ? 4 : F_ON(F_DSN, ps_global) ? 1 : 0) > 11)
+ rv = double_radio_buttons(tmp_20k_buf, -FOOTER_ROWS(ps_global), opts,
+ 'y', 'z',
+ (F_ON(F_DSN, ps_global) && allow_flowed)
+ ? h_send_prompt_dsn_flowed :
+ F_ON(F_DSN, ps_global) ? h_send_prompt_dsn :
+ allow_flowed ? h_send_prompt_flowed :
+ h_send_prompt,
+ RB_NORM);
+ else
+ rv = radio_buttons(tmp_20k_buf, -FOOTER_ROWS(ps_global), opts,
+ 'y', 'z',
+ (double_rad +
+ ((call_mailer_flags & CM_DSN_SHOW)
+ ? 4 : F_ON(F_DSN, ps_global) ? 1 : 0) == 11)
+ ? NO_HELP :
+ (F_ON(F_DSN, ps_global) && allow_flowed)
+ ? h_send_prompt_dsn_flowed :
+ F_ON(F_DSN, ps_global) ? h_send_prompt_dsn :
+ allow_flowed ? h_send_prompt_flowed :
+ h_send_prompt,
+ RB_NORM);
+
+ if(rv == 'y'){ /* user ACCEPTS! */
+ break;
+ }
+ else if(rv == 'n'){ /* Declined! */
+ rstr = _("No Message Sent");
+ break;
+ }
+ else if(rv == 'z'){ /* Cancelled! */
+ rstr = _("Send Cancelled");
+ break;
+ }
+ else if(rv == 10){ /* PREVIOUS filter */
+ filters = filters->prev;
+ }
+ else if(rv == 11){ /* NEXT filter */
+ filters = filters->next;
+ }
+ else if(rv == 21){
+ lmc.text_only = !lmc.text_only;
+ }
+ else if(rv == 12){ /* flip verbose bit */
+ if(call_mailer_flags & CM_VERBOSE)
+ call_mailer_flags &= ~CM_VERBOSE;
+ else
+ call_mailer_flags |= CM_VERBOSE;
+
+ if((call_mailer_flags & CM_VERBOSE) && background_requested)
+ background_requested = 0;
+ }
+ else if(rv == 22){ /* flip flowing bit */
+ flowing_requested = !flowing_requested;
+ }
+ else if(rv == 15){
+ if((background_requested = !background_requested)
+ && (call_mailer_flags & CM_VERBOSE))
+ call_mailer_flags &= ~CM_VERBOSE; /* clear verbose */
+ }
+ else if(call_mailer_flags & CM_DSN_SHOW){
+ if(rv == 's'){ /* flip success bit */
+ call_mailer_flags ^= CM_DSN_SUCCESS;
+ /* turn off related bits */
+ if(call_mailer_flags & CM_DSN_SUCCESS)
+ call_mailer_flags &= ~(CM_DSN_NEVER);
+ }
+ else if(rv == 'd'){ /* flip delay bit */
+ call_mailer_flags ^= CM_DSN_DELAY;
+ /* turn off related bits */
+ if(call_mailer_flags & CM_DSN_DELAY)
+ call_mailer_flags &= ~(CM_DSN_NEVER);
+ }
+ else if(rv == 'x'){ /* flip never bit */
+ call_mailer_flags ^= CM_DSN_NEVER;
+ /* turn off related bits */
+ if(call_mailer_flags & CM_DSN_NEVER)
+ call_mailer_flags &= ~(CM_DSN_SUCCESS | CM_DSN_DELAY);
+ }
+ else if(rv == 'h'){ /* flip full bit */
+ call_mailer_flags ^= CM_DSN_FULL;
+ }
+ }
+ else if(rv == 'd'){ /* show dsn options */
+ /*
+ * When you turn on DSN, the default is to notify on
+ * failure, success, or delay; and to return the whole
+ * body on failure.
+ */
+ call_mailer_flags |= (CM_DSN_SHOW | CM_DSN_SUCCESS | CM_DSN_DELAY | CM_DSN_FULL);
+ }
+ else if(rv == 'p'){ /* choose X-Priority */
+ char *prio;
+
+ prio = choose_a_priority(priority_requested);
+ if((ps_global->redrawer = redraw_pico) != NULL){
+ (*ps_global->redrawer)();
+ fix_windsize(ps_global);
+ }
+
+ if(prio){
+ if(priority_requested)
+ fs_give((void **) &priority_requested);
+
+ if(prio[0])
+ priority_requested = prio;
+ else
+ fs_give((void **) &prio);
+ }
+ }
+#ifdef SMIME
+ else if(rv=='e'){
+ if(ps_global->smime)
+ ps_global->smime->do_encrypt = !ps_global->smime->do_encrypt;
+ }
+ else if(rv=='g'){
+ if(ps_global->smime)
+ ps_global->smime->do_sign = !ps_global->smime->do_sign;
+ }
+#endif
+
+ snprintf(dsn_string, sizeof(dsn_string), "DSN requested[%s%s%s%s]",
+ (call_mailer_flags & CM_DSN_NEVER)
+ ? "Never" : "F",
+ (call_mailer_flags & CM_DSN_DELAY)
+ ? "D" : "",
+ (call_mailer_flags & CM_DSN_SUCCESS)
+ ? "S" : "",
+ (call_mailer_flags & CM_DSN_NEVER)
+ ? ""
+ : (call_mailer_flags & CM_DSN_FULL) ? "-Full"
+ : "-Hdrs");
+ dsn_string[sizeof(dsn_string)-1] = '\0';
+ }
+
+ /* remember selection */
+ if(filters && filters->index > -1)
+ sending_filter_requested = ps_global->VAR_SEND_FILTER[filters->index];
+
+ if(filters){
+ filters->prev->next = NULL; /* tie off list */
+ while(filters){ /* then free it */
+ fp = filters->next;
+ if(filters->filter)
+ fs_give((void **)&filters->filter);
+
+ fs_give((void **)&filters);
+ filters = fp;
+ }
+ }
+
+ if(old_suspend)
+ (void) F_SET(F_CAN_SUSPEND, ps_global, 1);
+
+ if(result)
+ *result = rstr;
+
+ ps_global->redrawer = redraw;
+
+ return((rstr == NULL) ? 0 : 1);
+}
+
+
+/*
+ * Allow user to choose a priority for sending.
+ *
+ * Returns an allocated priority on success, NULL otherwise.
+ */
+char *
+choose_a_priority(char *default_val)
+{
+ char *choice = NULL;
+ char **priority_list, **lp;
+ char *starting_val = NULL;
+ char *none;
+ int i, cnt;
+ PRIORITY_S *p;
+
+ for(cnt = 0, p = priorities; p && p->desc; p++)
+ cnt++;
+
+ cnt++; /* for NONE entry */
+ lp = priority_list = (char **) fs_get((cnt + 1) * sizeof(*priority_list));
+ memset(priority_list, 0, (cnt+1) * sizeof(*priority_list));
+
+ for(i = 0, p = priorities; p && p->desc; p++){
+ *lp = cpystr(p->desc);
+ if(default_val && !strcmp(default_val, p->desc))
+ starting_val = (*lp);
+
+ lp++;
+ }
+
+ none = _("NONE - No X-Priority header included");
+ *lp = cpystr(none);
+ if(!starting_val)
+ starting_val = (*lp);
+
+ /* TRANSLATORS: SELECT A PRIORITY is a screen title
+ TRANSLATORS: Print something1 using something2.
+ "priorities" is something1 */
+ choice = choose_item_from_list(priority_list, NULL, _("SELECT A PRIORITY"),
+ _("priorities"), h_select_priority_screen,
+ _("HELP FOR SELECTING A PRIORITY"),
+ starting_val);
+
+ if(!choice)
+ q_status_message(SM_ORDER, 1, 4, _("No change"));
+ else if(!strcmp(choice, none))
+ choice[0] = '\0';
+
+ free_list_array(&priority_list);
+
+ return(choice);
+}
+
+
+int
+dont_flow_this_time(void)
+{
+ return(flowing_requested ? 0 : 1);
+}
+
+
+/*----------------------------------------------------------------------
+ Call back for pico to display mime type of attachment
+
+Args: file -- filename being attached
+
+Returns: returns 1 on success (message queued), zero otherwise (don't know
+ type so nothing queued).
+----*/
+int
+mime_type_for_pico(char *file)
+{
+ BODY *body;
+ int rv;
+ void *file_contents;
+
+ body = mail_newbody();
+ body->type = TYPEOTHER;
+ body->encoding = ENCOTHER;
+
+ /* don't know where the cursor's been, reset it */
+ clear_cursor_pos();
+ if(!set_mime_type_by_extension(body, file)){
+ if((file_contents=(void *)so_get(FileStar,file,READ_ACCESS|READ_FROM_LOCALE)) != NULL){
+ body->contents.text.data = file_contents;
+ set_mime_type_by_grope(body);
+ }
+ }
+
+ if(body->type != TYPEOTHER){
+ rv = 1;
+ q_status_message3(SM_ORDER, 0, 3,
+ _("File %s attached as type %s/%s"), file,
+ body_types[body->type],
+ body->subtype ? body->subtype : rfc822_default_subtype(body->type));
+ }
+ else
+ rv = 0;
+
+ pine_free_body(&body);
+ return(rv);
+}
+
+
+/*----------------------------------------------------------------------
+ Call back for pico to receive an uploaded message
+
+ Args: fname -- name for uploaded file (empty if they want us to assign it)
+ size -- pointer to long to hold the attachment's size
+
+ Notes: the attachment is uploaded to a temp file, and
+
+ Returns: TRUE on success, FALSE otherwise
+----*/
+int
+upload_msg_to_pico(char *fname, size_t fnlen, long int *size)
+{
+ char cmd[MAXPATH+1], *fnp = NULL;
+ char *locale_name = NULL;
+ long l;
+ PIPE_S *syspipe;
+
+ dprint((1, "Upload cmd called to xfer \"%s\"\n",
+ fname ? fname : "<NO FILE>"));
+
+ if(!fname) /* no place for file name */
+ return(0);
+
+ if(!*fname){ /* caller wants temp file */
+ if((fnp = temp_nam(NULL, "pu")) != NULL){
+ strncpy(fname, fnp, fnlen);
+ fname[fnlen-1] = '\0';
+ our_unlink(fnp);
+ fs_give((void **)&fnp);
+ }
+ }
+ else
+ locale_name = convert_to_locale(fname);
+
+ build_updown_cmd(cmd, sizeof(cmd), ps_global->VAR_UPLOAD_CMD_PREFIX,
+ ps_global->VAR_UPLOAD_CMD, locale_name ? locale_name : fname);
+ if((syspipe = open_system_pipe(cmd, NULL, NULL, PIPE_USER | PIPE_RESET,
+ 0, pipe_callback, pipe_report_error)) != NULL){
+ (void) close_system_pipe(&syspipe, NULL, pipe_callback);
+ if((l = name_file_size(locale_name ? locale_name : fname)) < 0L){
+ q_status_message2(SM_ORDER | SM_DING, 3, 4,
+ "Error determining size of %s: %s", fname,
+ fnp = error_description(errno));
+ dprint((1,
+ "!!! Upload cmd \"%s\" failed for \"%s\": %s\n",
+ cmd ? cmd : "?",
+ fname ? fname : "?",
+ fnp ? fnp : "?"));
+ }
+ else if(size)
+ *size = l;
+
+ if(locale_name)
+ fs_give((void **) &locale_name);
+
+ return(l >= 0);
+ }
+ else
+ q_status_message(SM_ORDER | SM_DING, 3, 4, _("Error opening pipe"));
+
+ if(locale_name)
+ fs_give((void **) &locale_name);
+
+ return(0);
+}
+
+
+char *
+cancel_for_pico(void (*redraw_pico)(void))
+{
+ int rv;
+ char *rstr = NULL;
+ char *prompt =
+ _("Cancel message (answering \"Confirm\" will abandon your mail message) ? ");
+ void (*redraw)(void) = ps_global->redrawer;
+ static ESCKEY_S opts[] = {
+ {'c', 'c', "C", N_("Confirm")},
+ {'n', 'n', "N", N_("No")},
+ {'y', 'y', "", ""},
+ {-1, 0, NULL, NULL}
+ };
+
+ ps_global->redrawer = redraw_pico;
+ fix_windsize(ps_global);
+
+ while(1){
+ rv = radio_buttons(prompt, -FOOTER_ROWS(ps_global), opts,
+ 'n', 'x', h_confirm_cancel, RB_NORM);
+ if(rv == 'c'){ /* user ACCEPTS! */
+ rstr = "";
+ break;
+ }
+ else if(rv == 'y'){
+ q_status_message(SM_INFO, 1, 3, _(" Type \"C\" to cancel message "));
+ display_message('x');
+ }
+ else
+ break;
+ }
+
+ ps_global->redrawer = redraw;
+ return(rstr);
+}
+
+
+/*----------------------------------------------------------------------
+ Pass the first text segment of the message thru the "send filter"
+
+Args: body pointer and address for storage object of old data
+
+Returns: returns 1 on success, zero on error.
+----*/
+int
+filter_message_text(char *fcmd, ENVELOPE *outgoing, struct mail_bodystruct *body,
+ STORE_S **old, METAENV *header)
+{
+ char *cmd, *tmpf = NULL, *resultf = NULL, *errstr = NULL, *mtf = NULL;
+ int key = 0, include_hdrs = 0;
+ gf_io_t gc, pc;
+ STORE_S **so = (STORE_S **)((body->type == TYPEMULTIPART)
+ ? &body->nested.part->body.contents.text.data
+ : &body->contents.text.data),
+ *tmp_so = NULL, *tmpf_so,
+ *save_local_so, *readthis_so, *our_tmpf_so = NULL;
+#define DO_HEADERS 1
+
+ if(fcmd
+ && (cmd=expand_filter_tokens(fcmd, outgoing, &tmpf, &resultf, &mtf,
+ &key, &include_hdrs))){
+ if(tmpf){
+ /*
+ * We need WRITE_TO_LOCALE here because the user is going to
+ * be operating on tmpf. We need to change it back after they
+ * are done.
+ */
+ if((tmpf_so = so_get(FileStar, tmpf, EDIT_ACCESS|OWNER_ONLY|WRITE_TO_LOCALE)) != NULL){
+ if(key){
+ so_puts(tmpf_so, filter_session_key());
+ so_puts(tmpf_so, NEWLINE);
+ }
+
+ /*
+ * If the headers are wanted for filtering, we can just
+ * stick them in the tmpf file that is already there before
+ * putting the body in.
+ */
+ if(include_hdrs){
+ save_local_so = lmc.so;
+ lmc.so = tmpf_so; /* write it to tmpf_so */
+ lmc.all_written = lmc.text_written = lmc.text_only = 0;
+ pine_rfc822_header(header, body, NULL, NULL);
+ lmc.so = save_local_so;
+ }
+
+ so_seek(*so, 0L, 0);
+ gf_set_so_readc(&gc, *so);
+ gf_set_so_writec(&pc, tmpf_so);
+ gf_filter_init();
+ errstr = gf_pipe(gc, pc);
+ gf_clear_so_readc(*so);
+ gf_clear_so_writec(tmpf_so);
+ so_give(&tmpf_so);
+ }
+ else
+ errstr = "Can't create space for filter temporary file.";
+ }
+ else if(include_hdrs){
+ /*
+ * Gf_filter wants a single storage object to read from.
+ * If headers are wanted for filtering we'll have to put them
+ * and the body into a temp file first and then use that
+ * as the storage object for gf_filter.
+ * We don't use WRITE_TO_LOCALE in this case because gf_filter
+ * takes care of that.
+ */
+ if((our_tmpf_so = so_get(TmpFileStar, NULL, EDIT_ACCESS|OWNER_ONLY)) != NULL){
+ /* put headers in our_tmpf_so */
+ save_local_so = lmc.so;
+ lmc.so = our_tmpf_so; /* write it to our_tmpf_so */
+ lmc.all_written = lmc.text_written = lmc.text_only = 0;
+ pine_rfc822_header(header, body, NULL, NULL);
+ lmc.so = save_local_so;
+
+ /* put body in our_tmpf_so */
+ so_seek(*so, 0L, 0);
+ gf_set_so_readc(&gc, *so);
+ gf_set_so_writec(&pc, our_tmpf_so);
+ gf_filter_init();
+ errstr = gf_pipe(gc, pc);
+ gf_clear_so_readc(*so);
+ gf_clear_so_writec(our_tmpf_so);
+
+ /* tell gf_filter to read from our_tmpf_so instead of *so */
+ readthis_so = our_tmpf_so;
+ }
+ else
+ errstr = "Can't create space for temporary file.";
+ }
+ else
+ readthis_so = *so;
+
+ if(!errstr){
+ if((tmp_so = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
+ gf_set_so_writec(&pc, tmp_so);
+ ps_global->mangled_screen = 1;
+ suspend_busy_cue();
+ ClearScreen();
+ fflush(stdout);
+ if(tmpf){
+ PIPE_S *fpipe;
+
+ if((fpipe = open_system_pipe(cmd, NULL, NULL,
+ PIPE_NOSHELL | PIPE_RESET,
+ 0, pipe_callback, pipe_report_error)) != NULL){
+ if(close_system_pipe(&fpipe, NULL, pipe_callback) == 0){
+
+ /* now we undo the WRITE_FROM_LOCALE change in tmpf */
+ if((tmpf_so = so_get(FileStar, tmpf, READ_ACCESS|READ_FROM_LOCALE)) != NULL){
+ gf_set_so_readc(&gc, tmpf_so);
+ gf_filter_init();
+ errstr = gf_pipe(gc, pc);
+ gf_clear_so_readc(tmpf_so);
+ so_give(&tmpf_so);
+ }
+ else
+ errstr = "Can't open temp file filter wrote.";
+ }
+ else
+ errstr = "Filter command returned error.";
+ }
+ else
+ errstr = "Can't exec filter text.";
+ }
+ else
+ errstr = gf_filter(cmd, key ? filter_session_key() : NULL,
+ readthis_so, pc, NULL, 0,
+ pipe_callback);
+
+ if(our_tmpf_so)
+ so_give(&our_tmpf_so);
+
+ gf_clear_so_writec(tmp_so);
+
+#ifdef _WINDOWS
+ if(!errstr){
+ /*
+ * Can't really be using stdout, so don't print message that
+ * gets printed in the else. Ideally the program being called
+ * will wait after showing the message, we might want to look
+ * into doing the waiting in console based apps... or not.
+ */
+#else
+ if(errstr){
+ unsigned long ch;
+
+ fprintf(stdout, "\r\n%s Hit return to continue.", errstr);
+ fflush(stdout);
+ while((ch = read_char(300)) != ctrl('M')
+ && ch != NO_OP_IDLE)
+ putchar(BELL);
+ }
+ else{
+#endif /* _WINDOWS */
+ BODY *b = (body->type == TYPEMULTIPART)
+ ? &body->nested.part->body : body;
+
+ *old = *so; /* save old so */
+ *so = tmp_so; /* return new one */
+ (*so)->attr = copy_parameters((*old)->attr);
+
+ /*
+ * If the command said it would return new MIME
+ * mime type data, check it out...
+ */
+ if(mtf){
+ char buf[MAILTMPLEN], *s;
+ FILE *fp;
+
+ if((fp = our_fopen(mtf, "rb")) != NULL){
+ if(fgets(buf, sizeof(buf), fp)
+ && !struncmp(buf, "content-", 8)
+ && (s = strchr(buf+8, ':'))){
+ BODY *nb = mail_newbody();
+
+ for(*s++ = '\0'; *s == ' '; s++)
+ ;
+
+ rfc822_parse_content_header(nb,
+ (char *) ucase((unsigned char *) buf+8),s);
+ if(nb->type == TYPETEXT
+ && nb->subtype
+ && (!b->subtype
+ || strucmp(b->subtype, nb->subtype))){
+ if(b->subtype)
+ fs_give((void **) &b->subtype);
+
+ b->subtype = nb->subtype;
+ nb->subtype = NULL;
+
+ mail_free_body_parameter(&b->parameter);
+ b->parameter = nb->parameter;
+ nb->parameter = NULL;
+ mail_free_body_parameter(&nb->parameter);
+ }
+
+ mail_free_body(&nb);
+ }
+
+ fclose(fp);
+ }
+ }
+
+ /*
+ * Reevaluate the encoding in case form's changed...
+ */
+ b->encoding = ENCOTHER;
+ set_mime_type_by_grope(b);
+ }
+
+ ClearScreen();
+ resume_busy_cue(0);
+ }
+ else
+ errstr = "Can't create space for filtered text.";
+ }
+
+ fs_give((void **)&cmd);
+ }
+ else
+ return(0);
+
+ if(tmpf){
+ our_unlink(tmpf);
+ fs_give((void **)&tmpf);
+ }
+
+ if(mtf){
+ our_unlink(mtf);
+ fs_give((void **) &mtf);
+ }
+
+ if(resultf){
+ if(name_file_size(resultf) > 0L)
+ display_output_file(resultf, "Filter", NULL, DOF_BRIEF);
+
+ fs_give((void **)&resultf);
+ }
+ else if(errstr){
+ if(tmp_so)
+ so_give(&tmp_so);
+
+ q_status_message1(SM_ORDER | SM_DING, 3, 6, _("Problem filtering: %s"),
+ errstr);
+ dprint((1, "Filter FAILED: %s\n",
+ errstr ? errstr : "?"));
+ }
+
+ return(errstr == NULL);
+}
+
+
+/*----------------------------------------------------------------------
+ Copy the newsgroup name of the given mailbox into the given buffer
+
+Args:
+
+Returns:
+----*/
+void
+pine_send_newsgroup_name(char *mailbox, char *group_name, size_t len)
+{
+ NETMBX mb;
+
+ if(*mailbox == '#'){ /* Strip the leading "#news." */
+ strncpy(group_name, mailbox + 6, len-1);
+ group_name[len-1] = '\0';
+ }
+ else if(mail_valid_net_parse(mailbox, &mb)){
+ pine_send_newsgroup_name(mb.mailbox, group_name, len);
+ }
+ else
+ *group_name = '\0';
+}
+
+
+/*----------------------------------------------------------------------
+ Generate and send a message back to the pine development team
+
+Args: none
+
+Returns: none
+----*/
+void
+phone_home(char *addr)
+{
+ char tmp[MAX_ADDRESS];
+ ENVELOPE *outgoing;
+ BODY *body;
+
+ outgoing = mail_newenvelope();
+ if(!addr || !strindex(addr, '@')){
+ snprintf(addr = tmp, sizeof(tmp), "alpine%s@%s", PHONE_HOME_VERSION, PHONE_HOME_HOST);
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+
+ rfc822_parse_adrlist(&outgoing->to, addr, ps_global->maildomain);
+
+ outgoing->message_id = generate_message_id();
+ outgoing->subject = cpystr("Document Request");
+ outgoing->from = phone_home_from();
+
+ body = mail_newbody();
+ body->type = TYPETEXT;
+
+ if((body->contents.text.data = (void *)so_get(PicoText,NULL,EDIT_ACCESS)) != NULL){
+ so_puts((STORE_S *)body->contents.text.data, "Document request: ");
+ so_puts((STORE_S *)body->contents.text.data, "Alpine-");
+ so_puts((STORE_S *)body->contents.text.data, ALPINE_VERSION);
+ if(ps_global->first_time_user)
+ so_puts((STORE_S *)body->contents.text.data, " for New Users");
+
+ if(ps_global->VAR_INBOX_PATH && ps_global->VAR_INBOX_PATH[0] == '{')
+ so_puts((STORE_S *)body->contents.text.data, " and IMAP");
+
+ if(ps_global->VAR_NNTP_SERVER && ps_global->VAR_NNTP_SERVER[0]
+ && ps_global->VAR_NNTP_SERVER[0][0])
+ so_puts((STORE_S *)body->contents.text.data, " and NNTP");
+
+ (void)pine_simple_send(outgoing, &body, NULL,NULL,NULL,NULL, SS_NULLRP);
+
+ q_status_message(SM_ORDER, 1, 3, "Thanks for being counted!");
+ }
+ else
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ "Problem creating space for message text.");
+
+ mail_free_envelope(&outgoing);
+ pine_free_body(&body);
+
+}
+
+
+/*----------------------------------------------------------------------
+ Set up fields for passing to pico. Assumes first text part is
+ intended to be passed along for editing, and is in the form of
+ of a storage object brought into existence sometime before pico_send().
+ -----*/
+void
+outgoing2strings(METAENV *header, struct mail_bodystruct *bod, void **text,
+ PATMT **pico_a, int from_bounce)
+{
+ PINEFIELD *pf;
+
+ /*
+ * SIMPLIFYING ASSUMPTION #37: the first TEXT part's storage object
+ * is guaranteed to be of type PicoText!
+ */
+ if(bod->type == TYPETEXT){
+ *text = so_text((STORE_S *) bod->contents.text.data);
+
+ /* mark storage object as user edited */
+ if(!from_bounce)
+ (void) so_attr((STORE_S *) bod->contents.text.data, "edited", "1");
+ }
+ else if(bod->type == TYPEMULTIPART){
+ PART *part;
+ PATMT **ppa;
+ char *type, *name, *p;
+ int name_l;
+
+ /*
+ * We used to jump out the window if the first part wasn't text,
+ * but that may not be the case when bouncing a message with
+ * a leading non-text segment. So, IT'S UNDERSTOOD that the
+ * contents of the first part to send is still ALWAYS in a
+ * PicoText storage object, *AND* if that object doesn't contain
+ * data of type text, then it must contain THE ENCODED NON-TEXT
+ * DATA of the piece being sent.
+ *
+ * It's up to the programmer to make sure that such a message is
+ * sent via pine_simple_send and never get to the composer via
+ * pine_send.
+ *
+ * Make sense?
+ */
+ *text = so_text((STORE_S *) bod->nested.part->body.contents.text.data);
+
+ /* mark storage object as user edited */
+ if(!from_bounce)
+ (void) so_attr((STORE_S *) bod->nested.part->body.contents.text.data, "edited", "1");
+
+ /*
+ * If we already had a list, blast it now, so we can build a new
+ * attachment list that reflects what's really there...
+ */
+ if(pico_a){
+ free_attachment_list(pico_a);
+
+
+ /* Simplifyihg assumption #28e. (see cross reference)
+ All parts in the body passed in here that are not already
+ in the attachments list are added to the end of the attachments
+ list. Attachment items not in the body list will be taken care
+ of in strings2outgoing, but they are unlikey to occur
+ */
+
+ for(part = bod->nested.part->next; part != NULL; part = part->next) {
+ /* Already in list? */
+ for(ppa = pico_a;
+ *ppa && strcmp((*ppa)->id, part->body.id);
+ ppa = &(*ppa)->next)
+ ;
+
+ if(!*ppa){ /* Not in the list! append it... */
+ *ppa = (PATMT *)fs_get(sizeof(PATMT));
+
+ if(part->body.description){
+ char *p;
+ size_t len;
+
+ len = 4*strlen(part->body.description)+1;
+ p = (char *)fs_get(len*sizeof(char));
+ if(rfc1522_decode_to_utf8((unsigned char *)p,
+ len, part->body.description) == (unsigned char *) p){
+ (*ppa)->description = p;
+ }
+ else{
+ fs_give((void **)&p);
+ (*ppa)->description = cpystr(part->body.description);
+ }
+ }
+ else
+ (*ppa)->description = cpystr("");
+
+ type = type_desc(part->body.type, part->body.subtype,
+ part->body.parameter, NULL, 0);
+
+ /*
+ * If we can find a "name" parm, display that too...
+ */
+ if((name = parameter_val(part->body.parameter, "name")) != NULL){
+ /* Convert any [ or ]'s the name contained */
+ for(p = name; *p ; p++)
+ if(*p == '[')
+ *p = '(';
+ else if(*p == ']')
+ *p = ')';
+
+ name_l = p - name;
+ }
+ else
+ name_l = 0;
+
+ (*ppa)->filename = fs_get(strlen(type) + name_l + 5);
+
+ snprintf((*ppa)->filename, strlen(type) + name_l + 5, "[%s%s%s]", type,
+ name ? ": " : "", name ? name : "");
+ (*ppa)->filename[strlen(type) + name_l + 5 - 1] = '\0';
+
+ if(name)
+ fs_give((void **) &name);
+
+ (*ppa)->flags = A_FLIT;
+ (*ppa)->size = cpystr(byte_string(
+ send_body_size(&part->body)));
+ if(!part->body.id)
+ part->body.id = generate_message_id();
+
+ (*ppa)->id = cpystr(part->body.id);
+ (*ppa)->next = NULL;
+ }
+ }
+ }
+ }
+
+
+ /*------------------------------------------------------------------
+ Malloc strings to pass to composer editor because it expects
+ such strings so it can realloc them
+ -----------------------------------------------------------------*/
+ /*
+ * turn any address fields into text strings
+ */
+ /*
+ * SIMPLIFYING ASSUMPTION #116: all header strings are understood
+ * NOT to be RFC1522 decoded. Said differently, they're understood
+ * to be RFC1522 ENCODED as necessary. The intent is to preserve
+ * original charset tagging as far into the compose/send pipe as
+ * we can.
+ */
+ for(pf = header->local; pf && pf->name; pf = pf->next)
+ if(pf->canedit)
+ switch(pf->type){
+ case Address :
+ if(pf->addr){
+ char *p, *t, *u;
+ long l;
+
+ pf->scratch = addr_list_string(*pf->addr, NULL, 1);
+
+ /*
+ * Scan for and fix-up patently bogus fields.
+ *
+ * NOTE: collaboration with this code and what's done in
+ * reply.c:reply_cp_addr to package up the bogus stuff
+ * is required.
+ */
+ for(p = pf->scratch; (p = strstr(p, "@" RAWFIELD)); )
+ for(t = p; ; t--)
+ if(*t == '&'){ /* find "leading" token */
+ int replacelen;
+
+ /*
+ * Rfc822_cat has been changed so that it now quotes
+ * this sometimes. So we have to look out for quotes
+ * which confuse the decoder. It was only quoting
+ * because we were putting \r \n in the input, I think.
+ */
+ if(t > pf->scratch && t[-1] == '\"' && p[-1] == '\"')
+ t[-1] = p[-1] = ' ';
+
+ *t++ = ' '; /* replace token */
+ *p = '\0'; /* tie off string */
+ u = rfc822_base64((unsigned char *) t,
+ (unsigned long) strlen(t),
+ (unsigned long *) &l);
+ if(!u)
+ u = "";
+
+ replacelen = strlen(t);
+ *p = '@'; /* restore 'p' */
+ rplstr(p, strlen(p), 12, ""); /* clear special token */
+ rplstr(t, strlen(u)-replacelen+1, replacelen, u);
+ if(u)
+ fs_give((void **) &u);
+
+ if(HE(pf))
+ HE(pf)->start_here = 1;
+
+ break;
+ }
+ else if(t == pf->scratch)
+ break;
+
+ removing_leading_white_space(pf->scratch);
+ if(pf->scratch){
+ size_t l;
+
+ /*
+ * Replace control characters with ^C notation, unless
+ * some conditions are met (see istrncpy).
+ * If user doesn't edit, then we switch back to the
+ * original version. If user does edit, then all bets
+ * are off.
+ */
+ iutf8ncpy((char *)tmp_20k_buf, pf->scratch, SIZEOF_20KBUF-1);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ if((l=strlen((char *)tmp_20k_buf)) > strlen(pf->scratch)){
+ fs_give((void **)&pf->scratch);
+ pf->scratch = (char *)fs_get((l+1) * sizeof(char));
+ }
+
+ strncpy(pf->scratch, (char *)tmp_20k_buf, l+1);
+ }
+ }
+
+ break;
+
+ case Subject :
+ if(pf->text){
+ char *p, *src;
+ size_t len;
+
+ src = pf->scratch ? pf->scratch
+ : (*pf->text) ? *pf->text : "";
+
+ len = 4*strlen(src)+1;
+ p = (char *)fs_get(len * sizeof(char));
+ if(rfc1522_decode_to_utf8((unsigned char *)p, len, src) == (unsigned char *) p){
+ if(pf->scratch)
+ fs_give((void **)&pf->scratch);
+
+ pf->scratch = p;
+ }
+ else{
+ fs_give((void **)&p);
+ if(!pf->scratch)
+ pf->scratch = cpystr(src);
+ }
+
+ if(pf->scratch){
+ size_t l;
+
+ /*
+ * Replace control characters with ^C notation, unless
+ * some conditions are met (see istrncpy).
+ * If user doesn't edit, then we switch back to the
+ * original version. If user does edit, then all bets
+ * are off.
+ */
+ iutf8ncpy((char *)tmp_20k_buf, pf->scratch, SIZEOF_20KBUF-1);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ if((l=strlen((char *)tmp_20k_buf)) > strlen(pf->scratch)){
+ fs_give((void **)&pf->scratch);
+ pf->scratch = (char *)fs_get((l+1) * sizeof(char));
+ }
+
+ strncpy(pf->scratch, (char *)tmp_20k_buf, l+1);
+ }
+ }
+
+ break;
+
+ default :
+ break;
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Restore fields returned from pico to form useful to sending
+ routines.
+ -----*/
+void
+strings2outgoing(METAENV *header, struct mail_bodystruct **bod, PATMT *attach, int flow_it)
+{
+ PINEFIELD *pf;
+ int we_cancel = 0;
+
+ we_cancel = busy_cue(NULL, NULL, 1);
+
+ /*
+ * turn any local address strings into address lists
+ */
+ for(pf = header->local; pf && pf->name; pf = pf->next)
+ if(pf->scratch){
+ char *the_address = NULL;
+
+ switch(pf->type){
+ case Address :
+ removing_trailing_white_space(pf->scratch);
+
+ if((the_address || *pf->scratch) && pf->addr){
+ ADDRESS *new_addr = NULL;
+ static char *fakedomain = "@";
+
+ if(!the_address)
+ the_address = pf->scratch;
+
+ rfc822_parse_adrlist(&new_addr, the_address,
+ (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global))
+ ? fakedomain : ps_global->maildomain);
+ mail_free_address(pf->addr); /* free old addrs */
+ *pf->addr = new_addr; /* assign new addr */
+ }
+ else if(pf->addr)
+ mail_free_address(pf->addr); /* free old addrs */
+
+ break;
+
+ case Subject :
+ if(*pf->text)
+ fs_give((void **)pf->text);
+
+ if(*pf->scratch){
+ *pf->text = cpystr(pf->scratch);
+ }
+
+ break;
+
+ default :
+ break;
+ }
+
+ fs_give((void **)&pf->scratch); /* free now useless text */
+ }
+
+ create_message_body(bod, attach, flow_it);
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+}
+
+
+/*----------------------------------------------------------------------
+
+ The head of the body list here is always either TEXT or MULTIPART. It may be
+changed from TEXT to MULTIPART if there are attachments to be added
+and it is not already multipart.
+ ----*/
+void
+create_message_body(struct mail_bodystruct **b, PATMT *attach, int flow_it)
+{
+ PART *p, **pp;
+ PATMT *pa;
+ BODY *tmp_body, *text_body = NULL;
+ void *file_contents;
+ PARAMETER **parmp;
+ char *lc;
+
+ TIME_STAMP("create_body start.", 1);
+ /*
+ * if conditions are met short circuit MIME wrapping
+ */
+ if((*b)->type != TYPEMULTIPART && !attach){
+
+ /* only override assigned encoding if it might need upgrading */
+ if((*b)->type == TYPETEXT && (*b)->encoding == ENC7BIT)
+ (*b)->encoding = ENCOTHER;
+
+ create_message_body_text(*b, flow_it);
+
+ if(F_ON(F_COMPOSE_ALWAYS_DOWNGRADE, ps_global)
+ || !((*b)->encoding == ENC8BIT
+ || (*b)->encoding == ENCBINARY)){
+ TIME_STAMP("create_body end.", 1);
+ return;
+ }
+ else /* protect 8bit in multipart */
+ text_body = *b;
+ }
+
+ if((*b)->type == TYPETEXT) {
+ /*-- Current type is text, but there are attachments to add --*/
+ /*-- Upgrade to a TYPEMULTIPART --*/
+ tmp_body = (BODY *)mail_newbody();
+ tmp_body->type = TYPEMULTIPART;
+ tmp_body->nested.part = mail_newbody_part();
+ if(text_body){
+ /*
+ * Why do we do this?
+ * The problem is that base64 or quoted-printable encoding is
+ * sensitive to having random data appended to it's end. If
+ * we use a single part TEXT message and something in between
+ * us and the end appends advertising without adjusting for
+ * the encoding, the message is screwed up. So we wrap the
+ * text part inside a multipart and then the appended data
+ * will come after the boundary.
+ *
+ * We wish we could do this on the way out the door in a
+ * child of post_rfc822_output because at that point we know
+ * the character set and the encoding being used. For example,
+ * iso-2022-jp is an encoding that is not sensitive to data
+ * appended to the end, so it wouldn't need to be wrapped.
+ * We could conceivably have post_rfc822_body inspect the
+ * body and change it before doing the output. It would work
+ * but would be very fragile. We'd be passed a body from
+ * c-client to output and instead of just doing the output
+ * we'd change the body and then output it. Not worth it
+ * since the multipart wrapping is completely correct for
+ * MIME-aware mailers.
+ */
+ (void) copy_body(&(tmp_body->nested.part->body), *b);
+ /* move contents which were NOT copied */
+ tmp_body->nested.part->body.contents.text.data = (*b)->contents.text.data;
+ (*b)->contents.text.data = NULL;
+ }
+ else{
+ tmp_body->nested.part->body = **b;
+
+ (*b)->subtype = (*b)->id = (*b)->description = NULL;
+ (*b)->parameter = NULL;
+ (*b)->contents.text.data = NULL;
+ }
+
+ pine_free_body(b);
+ *b = tmp_body;
+ }
+
+ if(!text_body){
+ /*-- Now type must be MULTIPART with first part text --*/
+ (*b)->nested.part->body.encoding = ENCOTHER;
+ create_message_body_text(&((*b)->nested.part->body), flow_it);
+ }
+
+ /*------ Go through the parts list remove those to be deleted -----*/
+ for(pp = &(*b)->nested.part->next; *pp;){
+ for(pa = attach; pa && (*pp)->body.id; pa = pa->next)
+ /* already existed? */
+ if(pa->id && strcmp(pa->id, (*pp)->body.id) == 0){
+ char *orig_descp = NULL, *cs = NULL;
+
+ /*
+ * decode original to see if it matches what was decoded
+ * when we sent it in.
+ */
+
+ if((*pp)->body.description)
+ orig_descp = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
+ SIZEOF_20KBUF, (*pp)->body.description);
+
+ if(!(*pp)->body.description /* update description? */
+ || (pa->description && strcmp(pa->description, orig_descp))){
+ if((*pp)->body.description)
+ fs_give((void **) &(*pp)->body.description);
+
+ /* encoding happens as msg text is written */
+ (*pp)->body.description = cpystr(pa->description);
+ }
+
+ if(cs)
+ fs_give((void **) &cs);
+
+ break;
+ }
+
+ if(pa == NULL){
+ p = *pp; /* prepare to zap *pp */
+ *pp = p->next; /* pull next one in list up */
+ p->next = NULL; /* tie off removed node */
+
+ pine_free_body_data(&p->body); /* clean up contained data */
+ mail_free_body_part(&p); /* free up the part */
+ }
+ else
+ pp = &(*pp)->next;
+ }
+
+ /*---------- Now add any new attachments ---------*/
+ for(p = (*b)->nested.part ; p->next != NULL; p = p->next);
+ for(pa = attach; pa != NULL; pa = pa->next) {
+ if(pa->id != NULL)
+ continue; /* Has an ID, it's old */
+
+ /*
+ * the idea is handle ALL attachments as open FILE *'s. Actual
+ * encoding and such is handled at the time the message
+ * is shoved into the mail slot or written to disk...
+ *
+ * Also, we never unlink a file, so it's up to whoever opens
+ * it to deal with tmpfile issues.
+ */
+ if((file_contents = (void *)so_get(FileStar, pa->filename,
+ READ_ACCESS)) == NULL){
+ q_status_message2(SM_ORDER | SM_DING, 3, 4,
+ _("Error \"%s\", couldn't attach file \"%s\""),
+ error_description(errno), pa->filename);
+ display_message('x');
+ continue;
+ }
+
+ p->next = mail_newbody_part();
+ p = p->next;
+ p->body.id = generate_message_id();
+ p->body.contents.text.data = file_contents;
+
+ /*
+ * Set type to unknown and let set_mime_type_by_* figure it out.
+ * Always encode attachments we add as BINARY.
+ */
+ p->body.type = TYPEOTHER;
+ p->body.encoding = ENCBINARY;
+ p->body.size.bytes = name_file_size(pa->filename);
+ if(!set_mime_type_by_extension(&p->body, pa->filename)){
+ set_mime_type_by_grope(&p->body);
+ set_charset_possibly_to_ascii(&p->body, ps_global->keyboard_charmap);
+ }
+
+ so_release((STORE_S *)p->body.contents.text.data);
+
+ if(pa->description) /* encoding happens when msg written */
+ p->body.description = cpystr(pa->description);
+
+ /* Add name attribute for backward compatibility */
+ for(parmp = &p->body.parameter; *parmp; )
+ if(!struncmp((*parmp)->attribute, "name", 4)
+ && (!*((*parmp)->attribute + 4)
+ || *((*parmp)->attribute + 4) == '*')){
+ PARAMETER *free_me = *parmp;
+ *parmp = (*parmp)->next;
+ free_me->next = NULL;
+ mail_free_body_parameter(&free_me);
+ }
+ else
+ parmp = &(*parmp)->next;
+
+ set_parameter(parmp, "name",
+ pa->filename
+ ? ((lc = last_cmpnt(pa->filename)) ? lc : pa->filename)
+ : NULL);
+
+ /* Then set the Content-Disposition ala RFC1806 */
+ if(!p->body.disposition.type){
+ p->body.disposition.type = cpystr("attachment");
+ for(parmp = &p->body.disposition.parameter; *parmp; )
+ if(!struncmp((*parmp)->attribute, "filename", 4)
+ && (!*((*parmp)->attribute + 4)
+ || *((*parmp)->attribute + 4) == '*')){
+ PARAMETER *free_me = *parmp;
+ *parmp = (*parmp)->next;
+ free_me->next = NULL;
+ mail_free_body_parameter(&free_me);
+ }
+ else
+ parmp = &(*parmp)->next;
+
+ set_parameter(parmp, "filename",
+ pa->filename
+ ? ((lc = last_cmpnt(pa->filename)) ? lc : pa->filename)
+ : NULL);
+ }
+
+ p->next = NULL;
+ pa->id = cpystr(p->body.id);
+ }
+
+ /*
+ * Now, if this multipart has but one text piece (that is, no
+ * attachments), then downgrade from a composite type to a discrete
+ * text/plain message if CTE is not 8bit.
+ */
+ if(!(*b)->nested.part->next
+ && (*b)->nested.part->body.type == TYPETEXT
+ && (F_ON(F_COMPOSE_ALWAYS_DOWNGRADE, ps_global)
+ || !((*b)->nested.part->body.encoding == ENC8BIT
+ || (*b)->nested.part->body.encoding == ENCBINARY))){
+ /* Clone the interesting body part */
+ tmp_body = mail_newbody();
+ *tmp_body = (*b)->nested.part->body;
+ /* and rub out what we don't want cleaned up when it's free'd */
+ mail_initbody(&(*b)->nested.part->body);
+ mail_free_body(b);
+ *b = tmp_body;
+ }
+
+
+ TIME_STAMP("create_body end.", 1);
+}
+
+
+/*
+ * Fill in text BODY part's structure
+ *
+ */
+void
+create_message_body_text(struct mail_bodystruct *b, int flow_it)
+{
+ set_mime_type_by_grope(b);
+ if(F_OFF(F_QUELL_FLOWED_TEXT, ps_global)
+ && F_OFF(F_STRIP_WS_BEFORE_SEND, ps_global)
+ && flow_it)
+ set_parameter(b ? &b->parameter : NULL, "format", "flowed");
+
+ set_body_size(b);
+}
+
+
+/*
+ * free_attachment_list - free attachments in given list
+ */
+void
+free_attachment_list(PATMT **alist)
+{
+ PATMT *leading;
+
+ while(alist && *alist){ /* pointer pointing to something */
+ leading = (*alist)->next;
+ if((*alist)->description)
+ fs_give((void **)&(*alist)->description);
+
+ if((*alist)->filename){
+ if((*alist)->flags & A_TMP)
+ if(our_unlink((*alist)->filename) < 0)
+ dprint((1, "-- Can't unlink(%s): %s\n",
+ (*alist)->filename ? (*alist)->filename : "?",
+ error_description(errno)));
+
+ fs_give((void **)&(*alist)->filename);
+ }
+
+ if((*alist)->size)
+ fs_give((void **)&(*alist)->size);
+
+ if((*alist)->id)
+ fs_give((void **)&(*alist)->id);
+
+ fs_give((void **)alist);
+
+ *alist = leading;
+ }
+}
+
+
+void
+set_body_size(struct mail_bodystruct *b)
+{
+ unsigned char c;
+ int we_cancel = 0;
+
+ we_cancel = busy_cue(NULL, NULL, 1);
+ so_seek((STORE_S *)b->contents.text.data, 0L, 0);
+ b->size.bytes = 0L;
+ while(so_readc(&c, (STORE_S *)b->contents.text.data))
+ b->size.bytes++;
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+}
+
+
+/*
+ * view_as_rich - set the rich_header flag
+ *
+ * name - name of the header field
+ * deflt - default value to return if user didn't set it
+ *
+ * Note: if the user tries to turn them all off with "", then
+ * we take that to mean default, since otherwise there is no
+ * way to get to the headers.
+ */
+int
+view_as_rich(char *name, int deflt)
+{
+ char **p;
+ char *q;
+
+ p = ps_global->VAR_COMP_HDRS;
+
+ if(p && *p && **p){
+ for(; (q = *p) != NULL; p++){
+ if(!struncmp(q, name, strlen(name)))
+ return 0; /* 0 means we *do* view it by default */
+ }
+
+ return 1; /* 1 means it starts out hidden */
+ }
+ return(deflt);
+}
+
+
+/*
+ * background_posting - return whether or not we're already in the process
+ * of posting
+ */
+int
+background_posting(int gripe)
+{
+ if(ps_global->post){
+ if(gripe)
+ q_status_message(SM_ORDER|SM_DING, 3, 3,
+ _("Can't post while posting!"));
+ return(1);
+ }
+
+ return(0);
+}
+
+
+/*----------------------------------------------------------------------
+ Validate the given subject relative to any news groups.
+
+Args: none
+
+Returns: always returns 1, but also returns error if
+----*/
+int
+valid_subject(char *given, char **expanded, char **error, BUILDER_ARG *fcc, int *mangled)
+{
+ struct headerentry *hp;
+
+ if(expanded)
+ *expanded = cpystr(given);
+
+ if(error){
+ /*
+ * Now look for any header entry we passed to pico that has to do
+ * with news. If there's no subject, gripe.
+ */
+ for(hp = pbf->headents; hp->prompt; hp++)
+ if(hp->help == h_composer_news){
+ if(hp->hd_text->text[0] && !*given)
+ *error = cpystr(
+ _("News postings MUST have a subject! Please add one!"));
+
+ break;
+ }
+ }
+
+ return(0);
+}
+
+
+/*
+ * This is the build_address used by the composer to check for an address
+ * in the addrbook.
+ *
+ * Args: to -- the passed in line to parse
+ * full_to -- Address of a pointer to return the full address in.
+ * This will be allocated here and freed by the caller.
+ * error -- Address of a pointer to return an error message in.
+ * This will be allocated here and freed by the caller.
+ * barg -- Address of a pointer to return the fcc in is in
+ * fcc->tptr. It will have already been allocated by the
+ * caller but we may free it and reallocate if we wish.
+ * Caller will free it.
+ *
+ * Result: 0 is returned if address was OK,
+ * -1 if address wasn't OK.
+ *
+ * Side effect: Can flush addrbook entry cache entries so they need to be
+ * re-fetched afterwords.
+ */
+int
+build_address(char *to, char **full_to, char **error, BUILDER_ARG *barg, int *mangled)
+{
+ char *p;
+ int ret_val, no_repo = 0, *save_nesting_level;
+ BuildTo bldto;
+ PrivateTop *pt = NULL;
+ PrivateAffector *af = NULL;
+ char *fcc_local = NULL;
+ jmp_buf save_jmp_buf;
+
+ dprint((5, "- build_address - (%s)\n", to ? to : "nul"));
+
+ /* check to see if to string is empty to avoid work */
+ for(p = to; p && *p && isspace((unsigned char)*p); p++)
+ ;/* do nothing */
+
+ if(!p || !*p){
+ if(full_to)
+ *full_to = cpystr(to ? to : ""); /* because pico does a strcmp() */
+
+ return 0;
+ }
+
+ if(full_to != NULL)
+ *full_to = (char *)NULL;
+
+ if(error != NULL)
+ *error = (char *)NULL;
+
+ /* No guarantee cursor or status line is how we saved it */
+ clear_cursor_pos();
+ mark_status_unknown();
+
+ if(ps_global->remote_abook_validity > 0 &&
+ adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0) && mangled)
+ *mangled |= BUILDER_SCREEN_MANGLED;
+
+ /*
+ * If we end up jumping back here because somebody else changed one of
+ * our addrbooks out from underneath us, we may well leak some memory.
+ * That's probably ok since this will be very rare.
+ *
+ * The reason for the memcpy of the jmp_buf is that we may actually
+ * be indirectly calling this function from within the address book.
+ * For example, we may be in the address book screen and then run
+ * the ComposeTo command which puts us in the composer, then we call
+ * build_address from there which resets addrbook_changed_unexpectedly.
+ * Once we leave build_address we need to reset addrbook_changed_un...
+ * because this position on the stack will no longer be valid.
+ * Same is true of the other setjmp's in this file which are wrapped
+ * in memcpy calls.
+ */
+ save_nesting_level = cpyint(ab_nesting_level);
+ memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
+ if(setjmp(addrbook_changed_unexpectedly)){
+ no_repo = 0;
+ pt = NULL;
+ af = NULL;
+ fcc_local = NULL;
+ if(error != NULL)
+ *error = (char *)NULL;
+
+ if(full_to && *full_to)
+ fs_give((void **)full_to);
+
+ q_status_message(SM_ORDER, 3, 5, "Resetting address book...");
+ dprint((1,
+ "RESETTING address book... build_address(%s)!\n", to ? to : "?"));
+ addrbook_reset();
+ ab_nesting_level = *save_nesting_level;
+ }
+
+ ab_nesting_level++;
+ bldto.type = Str;
+ bldto.arg.str = to;
+ ret_val = build_address_internal(bldto, full_to, error,
+ barg ? &fcc_local : NULL,
+ &no_repo, NULL, save_and_restore,
+ 0, mangled);
+ ab_nesting_level--;
+ if(save_nesting_level)
+ fs_give((void **)&save_nesting_level);
+
+ /*
+ * Have to rfc1522_decode the full_to string before sending it back.
+ */
+ if(full_to && *full_to ){
+ char *q;
+ size_t len;
+
+ len = 4*strlen(*full_to)+1;
+ q = (char *)fs_get(len * sizeof(char));
+ p = (char *)rfc1522_decode_to_utf8((unsigned char *)q, len, *full_to);
+
+ /* p == q means that decoding happened, p is decoded *full_to */
+ if(p == q){
+ fs_give((void **)full_to);
+ *full_to = p;
+ }
+ else
+ fs_give((void **)&q);
+ }
+
+ if(fcc_local){
+ unsigned long csum;
+
+ /* Pt will point to headents[Fcc].bldr_private */
+ pt = NULL;
+ if(barg && barg->aff)
+ pt = (PrivateTop *)(*barg->aff);
+
+ /*
+ * If *barg->aff is set, that means fcc was set from a list
+ * during some previous builder call.
+ * If the current To line contains the old expansion as a prefix, then
+ * we should leave things as they are. In order to decide that,
+ * we look at a hash value computed from the strings.
+ */
+ if(pt && (af=pt->affector) && af->who == BP_To){
+ int len;
+
+ len = strlen(to);
+ if(len >= af->cksumlen){
+ int save;
+
+ save = to[af->cksumlen];
+ to[af->cksumlen] = '\0';
+ csum = line_hash(to);
+ to[af->cksumlen] = save;
+ }
+ else
+ csum = af->cksumval + 1; /* something not equal to cksumval */
+ }
+
+ if(!pt ||
+ !pt->affector ||
+ (pt->affector->who == BP_To && csum != pt->affector->cksumval)){
+
+ /* replace fcc value */
+ if(barg->tptr)
+ fs_give((void **)&barg->tptr);
+
+ barg->tptr = fcc_local;
+
+ if(barg->aff){
+ if(!pt){
+ *barg->aff = (void *)fs_get(sizeof(PrivateTop));
+ pt = (PrivateTop *)(*barg->aff);
+ memset((void *)pt, 0, sizeof(PrivateTop));
+ }
+
+ if(no_repo){
+ if(!pt->affector)
+ pt->affector =
+ (PrivateAffector *)fs_get(sizeof(PrivateAffector));
+
+ af = pt->affector;
+ af->who = BP_To;
+ af->cksumlen = strlen(((full_to && *full_to)
+ ? *full_to : ""));
+ af->cksumval = line_hash(((full_to && *full_to)
+ ? *full_to : ""));
+ }
+ else{
+ /*
+ * If result is reproducible, we don't keep track here.
+ */
+ if(pt->affector)
+ fs_give((void **)&pt->affector);
+ }
+ }
+ }
+ else
+ fs_give((void **)&fcc_local); /* unused in this case */
+ }
+
+ /* This is so pico will erase the old message */
+ if(error != NULL && *error == NULL)
+ *error = cpystr("");
+
+ memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
+ flush_status_messages(1);
+ return(ret_val);
+}
+
+
+/*
+ * This is the builder used by the composer for the Lcc line.
+ *
+ * Args: lcc -- the passed in Lcc line to parse
+ * full_lcc -- Address of a pointer to return the full address in.
+ * This will be allocated here and freed by the caller.
+ * error -- Address of a pointer to return an error message in.
+ * This is not allocated so should not be freed by the caller.
+ * barg -- This is a pointer to text for affected entries which
+ * we may be changing. The first one in the list is the
+ * To entry. We may put the name of the list in empty
+ * group syntax form there (like List Name: ;).
+ * The second one in the list is the fcc field.
+ * The tptr members already point to text allocated in the
+ * caller. We may free and reallocate here, caller will
+ * free the result in any case.
+ *
+ * Result: 0 is returned if address was OK,
+ * -1 if address wasn't OK.
+ *
+ * Side effect: Can flush addrbook entry cache entries so they need to be
+ * re-fetched afterwords.
+ */
+int
+build_addr_lcc(char *lcc, char **full_lcc, char **error, BUILDER_ARG *barg, int *mangled)
+{
+ int ret_val,
+ no_repo = 0; /* fcc or lcc not reproducible */
+ int *save_nesting_level;
+ BuildTo bldlcc;
+ PrivateTop *pt = NULL;
+ PrivateAffector *af = NULL;
+ char *p,
+ *fcc_local = NULL,
+ *to = NULL,
+ *dummy;
+ jmp_buf save_jmp_buf;
+
+ dprint((5, "- build_addr_lcc - (%s)\n", lcc ? lcc : "nul"));
+
+ /* check to see if to string is empty to avoid work */
+ for(p = lcc; p && *p && isspace((unsigned char)*p); p++)
+ ;/* do nothing */
+
+ if(!p || !*p){
+ if(full_lcc)
+ *full_lcc = cpystr(lcc ? lcc : ""); /* because pico does a strcmp() */
+
+ return 0;
+ }
+
+ if(error != NULL)
+ *error = (char *)NULL;
+
+ if(ps_global->remote_abook_validity > 0 &&
+ adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0) && mangled)
+ *mangled |= BUILDER_SCREEN_MANGLED;
+
+ /*
+ * If we end up jumping back here because somebody else changed one of
+ * our addrbooks out from underneath us, we may well leak some memory.
+ * That's probably ok since this will be very rare.
+ */
+ save_nesting_level = cpyint(ab_nesting_level);
+ memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
+ if(setjmp(addrbook_changed_unexpectedly)){
+ no_repo = 0;
+ pt = NULL;
+ af = NULL;
+ fcc_local = NULL;
+ to = NULL;
+ if(error != NULL)
+ *error = (char *)NULL;
+
+ if(full_lcc && *full_lcc)
+ fs_give((void **)full_lcc);
+
+ q_status_message(SM_ORDER, 3, 5, "Resetting address book...");
+ dprint((1,
+ "RESETTING address book... build_address(%s)!\n", lcc ? lcc : "?"));
+ addrbook_reset();
+ ab_nesting_level = *save_nesting_level;
+ }
+
+ ab_nesting_level++;
+ bldlcc.type = Str;
+ bldlcc.arg.str = lcc;
+
+ /*
+ * To is first affected_entry and Fcc is second.
+ * The conditional stuff for the fcc argument says to only change the
+ * fcc if the fcc pointer is passed in non-null, and the To pointer
+ * is also non-null. If they are null, that means they've already been
+ * entered (are sticky). We don't affect fcc if either fcc or To has
+ * been typed in.
+ */
+ ret_val = build_address_internal(bldlcc,
+ full_lcc,
+ error,
+ (barg && barg->next && barg->next->tptr && barg->tptr)
+ ? &fcc_local : NULL,
+ &no_repo,
+ (barg && barg->tptr) ? &to : NULL,
+ save_and_restore, 0, mangled);
+
+ ab_nesting_level--;
+ if(save_nesting_level)
+ fs_give((void **)&save_nesting_level);
+
+ /* full_lcc is what ends up in the Lcc: line */
+ if(full_lcc && *full_lcc){
+ size_t len;
+
+ /*
+ * Have to rfc1522_decode the full_lcc string before sending it back.
+ */
+ len = 4*strlen(*full_lcc)+1;
+ p = (char *)fs_get(len * sizeof(char));
+ if(rfc1522_decode_to_utf8((unsigned char *)p, len, *full_lcc) == (unsigned char *)p){
+ fs_give((void **)full_lcc);
+ *full_lcc = p;
+ }
+ else
+ fs_give((void **)&p);
+ }
+
+ /* to is what ends up in the To: line */
+ if(to && *to){
+ unsigned long csum;
+ size_t len;
+
+ /*
+ * Have to rfc1522_decode the full_to string before sending it back.
+ */
+ len = 4*strlen(to)+1;
+ p = (char *)fs_get(len * sizeof(char));
+ dummy = NULL;
+ if(rfc1522_decode_to_utf8((unsigned char *)p, len, to) == (unsigned char *)p){
+ /*
+ * If the caller wants us to try to preserve the charset
+ * information (they set aff) we copy it into encoded->etext.
+ * We don't have to worry about pasting together pieces of
+ * etext like we do in build_address because whenever the
+ * Lcc line is setting the To line it will be setting the
+ * whole line, not modifying it.
+ * Pt will point to headents[To].bldr_private.
+ */
+ if(barg && barg->aff){
+ pt = (PrivateTop *)(*barg->aff);
+
+ if(!pt){
+ *barg->aff = (void *)fs_get(sizeof(PrivateTop));
+ pt = (PrivateTop *)(*barg->aff);
+ memset((void *)pt, 0, sizeof(PrivateTop));
+ }
+ }
+
+ fs_give((void **)&to);
+ to = p;
+ }
+ else
+ fs_give((void **)&p);
+
+ if(dummy)
+ fs_give((void **)&dummy);
+
+
+ /*
+ * This part is recording the fact that the To line was set to
+ * what it is by entering something on the Lcc line. In particular,
+ * if a list alias was entered here then the fullname of the list
+ * goes in the To line. We save this affector information so that
+ * we can tell it shouldn't be modified if we call build_addr_lcc
+ * again unless we actually modified what's in the Lcc line so that
+ * it doesn't start with the same thing. The problem we're solving
+ * is that the contents of the Lcc line no longer look like the
+ * list they were derived from.
+ * Pt will point to headents[To].bldr_private.
+ */
+ if(barg && barg->aff)
+ pt = (PrivateTop *)(*barg->aff);
+
+ if(pt && (af=pt->affector) && af->who == BP_Lcc){
+ int len;
+
+ len = strlen(lcc);
+ if(len >= af->cksumlen){
+ int save;
+
+ save = lcc[af->cksumlen];
+ lcc[af->cksumlen] = '\0';
+ csum = line_hash(lcc);
+ lcc[af->cksumlen] = save;
+ }
+ else
+ csum = af->cksumval + 1; /* so they aren't equal */
+ }
+
+ if(!pt ||
+ !pt->affector ||
+ pt->affector->who != BP_Lcc ||
+ (pt->affector->who == BP_Lcc && csum != pt->affector->cksumval)){
+
+ /* replace to value */
+ if(barg->tptr && barg->tptr[0]){
+ size_t l;
+ char *t;
+
+ l = strlen(barg->tptr) + strlen(to ? to : "") + 2;
+ t = (char *)fs_get((l+1) * sizeof(char));
+ snprintf(t, l+1, "%s%s%s",
+ barg->tptr,
+ (to && *to) ? ", " : "",
+ (to && *to) ? to : "");
+ fs_give((void **)&barg->tptr);
+ if(to)
+ fs_give((void **)&to);
+
+ barg->tptr = t;
+ }
+ else{
+ if(barg->tptr)
+ fs_give((void **)&barg->tptr);
+
+ barg->tptr = to;
+ }
+
+ if(barg->aff){
+ if(!pt){
+ *barg->aff = (void *)fs_get(sizeof(PrivateTop));
+ pt = (PrivateTop *)(*barg->aff);
+ memset((void *)pt, 0, sizeof(PrivateTop));
+ }
+
+ if(no_repo){
+ if(!pt->affector)
+ pt->affector =
+ (PrivateAffector *)fs_get(sizeof(PrivateAffector));
+
+ af = pt->affector;
+ af->who = BP_Lcc;
+ af->cksumlen = strlen(((full_lcc && *full_lcc)
+ ? *full_lcc : ""));
+ af->cksumval = line_hash(((full_lcc && *full_lcc)
+ ? *full_lcc : ""));
+ }
+ else{
+ /*
+ * If result is reproducible, we don't keep track here.
+ */
+ if(pt->affector)
+ fs_give((void **)&pt->affector);
+ }
+ }
+ }
+ else
+ fs_give((void **)&to); /* unused in this case */
+ }
+
+ if(fcc_local){
+ unsigned long csum;
+
+ /*
+ * If *barg->next->aff is set, that means fcc was set from a list
+ * during some previous builder call. If the current Lcc line
+ * contains the old expansion as a prefix, then we should leave
+ * things as they are. In order to decide that we look at a hash
+ * value computed from the strings.
+ * Pt will point to headents[Fcc].bldr_private
+ */
+ pt = NULL;
+ if(barg && barg->next && barg->next->aff)
+ pt = (PrivateTop *)(*barg->next->aff);
+
+ if(pt && (af=pt->affector) && af->who == BP_Lcc){
+ int len;
+
+ len = strlen(lcc);
+ if(len >= af->cksumlen){
+ int save;
+
+ save = lcc[af->cksumlen];
+ lcc[af->cksumlen] = '\0';
+ csum = line_hash(lcc);
+ lcc[af->cksumlen] = save;
+ }
+ else
+ csum = af->cksumval + 1; /* something not equal to cksumval */
+ }
+
+ if(!pt ||
+ !pt->affector ||
+ pt->affector->who != BP_Lcc ||
+ (pt->affector->who == BP_Lcc && csum != pt->affector->cksumval)){
+
+ /* replace fcc value */
+ if(barg->next->tptr)
+ fs_give((void **)&barg->next->tptr);
+
+ barg->next->tptr = fcc_local;
+
+ if(barg->next->aff){
+ if(!pt){
+ *barg->next->aff = (void *)fs_get(sizeof(PrivateTop));
+ pt = (PrivateTop *)(*barg->next->aff);
+ memset((void *)pt, 0, sizeof(PrivateTop));
+ }
+
+ if(no_repo){
+ if(!pt->affector)
+ pt->affector =
+ (PrivateAffector *)fs_get(sizeof(PrivateAffector));
+
+ af = pt->affector;
+ af->who = BP_Lcc;
+ af->cksumlen = strlen(((full_lcc && *full_lcc)
+ ? *full_lcc : ""));
+ af->cksumval = line_hash(((full_lcc && *full_lcc)
+ ? *full_lcc : ""));
+ }
+ else{
+ /*
+ * If result is reproducible, we don't keep track here.
+ */
+ if(pt->affector)
+ fs_give((void **)&pt->affector);
+ }
+ }
+ }
+ else
+ fs_give((void **)&fcc_local); /* unused in this case */
+ }
+
+
+ if(error != NULL && *error == NULL)
+ *error = cpystr("");
+
+ memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
+ flush_status_messages(0);
+ return(ret_val);
+}
+
+
+/*----------------------------------------------------------------------
+ Verify and canonicalize news groups names.
+ Called from the message composer
+
+Args: given_group -- List of groups typed by user
+ expanded_group -- pointer to point to expanded list, which will be
+ allocated here and freed in caller. If this is
+ NULL, don't attempt to validate.
+ error -- pointer to store error message
+ fcc -- pointer to point to fcc, which will be
+ allocated here and freed in caller
+
+Returns: 0 if all is OK
+ -1 if addresses weren't valid
+
+Test the given list of newstroups against those recognized by our nntp
+servers. Testing by actually trying to open the list is much cheaper, both
+in bandwidth and memory, than yanking the whole list across the wire.
+ ----*/
+int
+news_build(char *given_group, char **expanded_group, char **error, BUILDER_ARG *fcc, int *mangled)
+{
+ int rv;
+ char *fccptr = NULL;
+
+ if(fcc && fcc->tptr)
+ fccptr = cpystr(fcc->tptr);
+
+ clear_cursor_pos();
+
+ rv = news_grouper(given_group, expanded_group, error, &fccptr, news_build_busy);
+
+ /* assign any new fcc to the BUILDER_ARG */
+ if(fccptr){
+ if(fcc){
+ /* it changed */
+ if(fcc->tptr && strcmp(fcc->tptr, fccptr)){
+ fs_give((void **) &fcc->tptr);
+ fcc->tptr = fccptr;
+ fccptr = NULL;
+ }
+ }
+
+ if(fccptr)
+ fs_give((void **) &fccptr);
+ }
+
+ /* deal with any busy indicator */
+ if(news_busy_cue){
+ news_busy_cue = 0;
+ cancel_busy_cue(0);
+ mark_status_dirty();
+ display_message('x');
+ if(mangled)
+ *mangled |= BUILDER_MESSAGE_DISPLAYED;
+ }
+
+
+ return(rv);
+}
+
+
+void
+news_build_busy(void)
+{
+ news_busy_cue = busy_cue("Validating newsgroup(s)", NULL, 0);
+}
+
+
+#if defined(DOS) || defined(OS2)
+
+/*----------------------------------------------------------------------
+ Verify that the necessary pieces are around to allow for
+ message sending under DOS
+
+Args: strict -- tells us if a remote stream is required before
+ sending is permitted.
+
+The idea is to make sure pine knows enough to put together a valid
+from line. The things we MUST know are a user-id, user-domain and
+smtp server to dump the message off on. Typically these are
+provided in pine's configuration file, but if not, the user is
+queried here.
+ ----*/
+int
+dos_valid_from()
+{
+ char prompt[100], answer[80];
+ int rc, i, flags;
+ HelpType help;
+
+ /*
+ * query for user name portion of address, use IMAP login
+ * name as default
+ */
+ if(!ps_global->VAR_USER_ID || ps_global->VAR_USER_ID[0] == '\0'){
+ NETMBX mb;
+ int no_prompt_user_id = 0;
+
+ if(ps_global->mail_stream && ps_global->mail_stream->mailbox
+ && mail_valid_net_parse(ps_global->mail_stream->mailbox, &mb)
+ && *mb.user){
+ strncpy(answer, mb.user, sizeof(answer)-1);
+ answer[sizeof(answer)-1] = '\0';
+ }
+ else if(F_ON(F_QUELL_USER_ID_PROMPT, ps_global)){
+ /* no user-id prompting if set */
+ no_prompt_user_id = 1;
+ rc = 0;
+ if(!ps_global->mail_stream)
+ do_broach_folder(ps_global->inbox_name,
+ ps_global->context_list, NULL, DB_INBOXWOCNTXT);
+ if(ps_global->mail_stream && ps_global->mail_stream->mailbox
+ && mail_valid_net_parse(ps_global->mail_stream->mailbox, &mb)
+ && *mb.user){
+ strncpy(answer, mb.user, sizeof(answer)-1);
+ answer[sizeof(answer)-1] = '\0';
+ }
+ else
+ answer[0] = '\0';
+ }
+ else
+ answer[0] = '\0';
+
+ if(F_ON(F_QUELL_USER_ID_PROMPT, ps_global) && answer[0]){
+ /* No prompt, just assume mailbox login is user-id */
+ no_prompt_user_id = 1;
+ rc = 0;
+ }
+
+ snprintf(prompt,sizeof(prompt),_("User-id for From address : "));
+ prompt[sizeof(prompt)-1] = '\0';
+
+ help = NO_HELP;
+ while(!no_prompt_user_id) {
+ flags = OE_APPEND_CURRENT;
+ rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
+ sizeof(answer),prompt,NULL,help,&flags);
+ if(rc == 2)
+ continue;
+
+ if(rc == 3){
+ help = (help == NO_HELP) ? h_sticky_user_id : NO_HELP;
+ continue;
+ }
+
+ if(rc != 4)
+ break;
+ }
+
+ if(rc == 1 || (rc == 0 && !answer[0])) {
+ q_status_message(SM_ORDER, 3, 4,
+ _("Send cancelled (User-id must be provided before sending)"));
+ return(0);
+ }
+
+ /* save the name */
+ snprintf(prompt, sizeof(prompt), _("Preserve %.*s as \"user-id\" in PINERC"),
+ sizeof(prompt)-50, answer);
+ prompt[sizeof(prompt)-1] = '\0';
+ if(ps_global->blank_user_id
+ && !no_prompt_user_id
+ && want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){
+ set_variable(V_USER_ID, answer, 1, 1, Main);
+ }
+ else{
+ fs_give((void **)&(ps_global->VAR_USER_ID));
+ ps_global->VAR_USER_ID = cpystr(answer);
+ }
+ }
+
+ /* query for personal name */
+ if(!ps_global->VAR_PERSONAL_NAME || ps_global->VAR_PERSONAL_NAME[0]=='\0'
+ && F_OFF(F_QUELL_PERSONAL_NAME_PROMPT, ps_global)){
+ answer[0] = '\0';
+ snprintf(prompt, sizeof(prompt), _("Personal name for From address : "));
+ prompt[sizeof(prompt)-1] = '\0';
+
+ help = NO_HELP;
+ while(1) {
+ flags = OE_APPEND_CURRENT;
+ rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
+ sizeof(answer),prompt,NULL,help,&flags);
+ if(rc == 2)
+ continue;
+
+ if(rc == 3){
+ help = (help == NO_HELP) ? h_sticky_personal_name : NO_HELP;
+ continue;
+ }
+
+ if(rc != 4)
+ break;
+ }
+
+ if(rc == 0 && answer){ /* save the name */
+ snprintf(prompt, sizeof(prompt), _("Preserve %.*s as \"personal-name\" in PINERC"),
+ sizeof(prompt)-50, answer);
+ prompt[sizeof(prompt)-1] = '\0';
+ if(ps_global->blank_personal_name
+ && want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){
+ set_variable(V_PERSONAL_NAME, answer, 1, 1, Main);
+ }
+ else{
+ fs_give((void **)&(ps_global->VAR_PERSONAL_NAME));
+ ps_global->VAR_PERSONAL_NAME = cpystr(answer);
+ }
+ }
+ }
+
+ /*
+ * query for host/domain portion of address, using IMAP
+ * host as default
+ */
+ if(ps_global->blank_user_domain
+ || ps_global->maildomain == ps_global->localdomain
+ || ps_global->maildomain == ps_global->hostname){
+ if(ps_global->inbox_name[0] == '{'){
+ for(i=0;
+ i < sizeof(answer)-1 && ps_global->inbox_name[i+1] != '}'; i++)
+ answer[i] = ps_global->inbox_name[i+1];
+
+ answer[i] = '\0';
+ }
+ else
+ answer[0] = '\0';
+
+ snprintf(prompt,sizeof(prompt),_("Host/domain for From address : "));
+ prompt[sizeof(prompt)-1] = '\0';
+
+ help = NO_HELP;
+ while(1) {
+ flags = OE_APPEND_CURRENT;
+ rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
+ sizeof(answer),prompt,NULL,help,&flags);
+ if(rc == 2)
+ continue;
+
+ if(rc == 3){
+ help = (help == NO_HELP) ? h_sticky_domain : NO_HELP;
+ continue;
+ }
+
+ if(rc != 4)
+ break;
+ }
+
+ if(rc == 1 || (rc == 0 && !answer[0])) {
+ q_status_message(SM_ORDER, 3, 4,
+ _("Send cancelled (Host/domain name must be provided before sending)"));
+ return(0);
+ }
+
+ /* save the name */
+ snprintf(prompt, sizeof(prompt), _("Preserve %.*s as \"user-domain\" in PINERC"),
+ sizeof(prompt)-50, answer);
+ prompt[sizeof(prompt)-1] = '\0';
+ if(!ps_global->userdomain && !ps_global->blank_user_domain
+ && want_to(prompt, 'y', 'n', NO_HELP, WT_NORM) == 'y'){
+ set_variable(V_USER_DOMAIN, answer, 1, 1, Main);
+ fs_give((void **)&(ps_global->maildomain)); /* blast old val */
+ ps_global->userdomain = cpystr(answer);
+ ps_global->maildomain = ps_global->userdomain;
+ }
+ else{
+ fs_give((void **)&(ps_global->maildomain));
+ ps_global->userdomain = cpystr(answer);
+ ps_global->maildomain = ps_global->userdomain;
+ }
+ }
+
+ /* check for smtp server */
+ if(!ps_global->VAR_SMTP_SERVER ||
+ !ps_global->VAR_SMTP_SERVER[0] ||
+ !ps_global->VAR_SMTP_SERVER[0][0]){
+ char **list;
+
+ if(ps_global->inbox_name[0] == '{'){
+ for(i=0;
+ i < sizeof(answer)-1 && ps_global->inbox_name[i+1] != '}'; i++)
+ answer[i] = ps_global->inbox_name[i+1];
+
+ answer[i] = '\0';
+ }
+ else
+ answer[0] = '\0';
+
+ snprintf(prompt,sizeof(prompt),_("SMTP server to forward message : "));
+ prompt[sizeof(prompt)-1] = '\0';
+
+ help = NO_HELP;
+ while(1) {
+ flags = OE_APPEND_CURRENT;
+ rc = optionally_enter(answer,-FOOTER_ROWS(ps_global),0,
+ sizeof(answer),prompt,NULL,help,&flags);
+ if(rc == 2)
+ continue;
+
+ if(rc == 3){
+ help = (help == NO_HELP) ? h_sticky_smtp : NO_HELP;
+ continue;
+ }
+
+ if(rc != 4)
+ break;
+ }
+
+ if(rc == 1 || (rc == 0 && answer[0] == '\0')) {
+ q_status_message(SM_ORDER, 3, 4,
+ _("Send cancelled (SMTP server must be provided before sending)"));
+ return(0);
+ }
+
+ /* save the name */
+ list = (char **) fs_get(2 * sizeof(char *));
+ list[0] = cpystr(answer);
+ list[1] = NULL;
+ set_variable_list(V_SMTP_SERVER, list, TRUE, Main);
+ fs_give((void *)&list[0]);
+ fs_give((void *)list);
+ }
+
+ return(1);
+}
+
+#endif /* defined(DOS) || defined(OS2) */
diff --git a/alpine/send.h b/alpine/send.h
new file mode 100644
index 00000000..4d216dd5
--- /dev/null
+++ b/alpine/send.h
@@ -0,0 +1,53 @@
+/*
+ * $Id: send.h 769 2007-10-24 00:15:40Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_SEND_INCLUDED
+#define PINE_SEND_INCLUDED
+
+
+#include "../pith/send.h"
+#include "../pith/reply.h"
+#include "../pith/state.h"
+#include "../pith/pattern.h"
+#include "../pith/filter.h"
+
+
+#define SS_PROMPTFORTO 0x01 /* Simple_send: prompt for To */
+#define SS_NULLRP 0x02 /* Null Return-path */
+
+
+/* pine_send flags */
+#define PS_STICKY_FCC 0x01
+#define PS_STICKY_TO 0x02
+
+
+/* exported protoypes */
+void compose_screen(struct pine *);
+void alt_compose_screen(struct pine *);
+void compose_mail(char *, char *, ACTION_S *, PATMT *, gf_io_t);
+int pine_simple_send(ENVELOPE *, BODY **, ACTION_S *, char *, char *, char **, int);
+void pine_send(ENVELOPE *, BODY **, char *, ACTION_S *, char *, REPLY_S *,
+ REDRAFT_POS_S *, char *, PINEFIELD *, int);
+int upload_msg_to_pico(char *, size_t, long *);
+void phone_home(char *);
+void create_message_body(BODY **, PATMT *, int);
+char *pine_send_status(int, char *, char *, size_t, int *);
+int confirm_daemon_send(void);
+int build_address(char *, char **,char **, BUILDER_ARG *, int *);
+void free_attachment_list(PATMT **);
+
+
+#endif /* PINE_SEND_INCLUDED */
diff --git a/alpine/setup.c b/alpine/setup.c
new file mode 100644
index 00000000..3ac90f21
--- /dev/null
+++ b/alpine/setup.c
@@ -0,0 +1,1131 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: setup.c 918 2008-01-23 19:39:38Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "headers.h"
+#include "setup.h"
+#include "keymenu.h"
+#include "status.h"
+#include "confscroll.h"
+#include "colorconf.h"
+#include "reply.h"
+#include "radio.h"
+#include "listsel.h"
+#include "folder.h"
+#include "mailcmd.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/util.h"
+#include "../pith/sort.h"
+#include "../pith/folder.h"
+#include "../pith/list.h"
+#include "../pith/icache.h"
+
+
+/*
+ * Internal prototypes
+ */
+int inbox_path_text_tool(struct pine *, int, CONF_S **, unsigned);
+int incoming_monitoring_list_tool(struct pine *, int, CONF_S **, unsigned);
+int stayopen_list_tool(struct pine *, int, CONF_S **, unsigned);
+char **adjust_list_of_monitored_incoming(CONTEXT_S *, EditWhich, int);
+int to_charsets_text_tool(struct pine *, int, CONF_S **, unsigned);
+
+
+#define CONFIG_SCREEN_TITLE _("SETUP CONFIGURATION")
+#define CONFIG_SCREEN_TITLE_EXC _("SETUP CONFIGURATION EXCEPTIONS")
+
+
+
+/*----------------------------------------------------------------------
+ Present pinerc data for manipulation
+
+ Args: None
+
+ Result: help edit certain pinerc fields.
+ ---*/
+void
+option_screen(struct pine *ps, int edit_exceptions)
+{
+ char tmp[MAXPATH+1], *pval, **lval;
+ int i, j, ln = 0, readonly_warning = 0;
+ struct variable *vtmp;
+ CONF_S *ctmpa = NULL, *ctmpb, *first_line = NULL;
+ FEATURE_S *feature;
+ PINERC_S *prc = NULL;
+ SAVED_CONFIG_S *vsave;
+ OPT_SCREEN_S screen;
+ int expose_hidden_config, add_hidden_vars_title = 0;
+
+ dprint((3, "-- option_screen --\n"));
+
+ expose_hidden_config = F_ON(F_EXPOSE_HIDDEN_CONFIG, ps_global);
+ treat_color_vars_as_text = expose_hidden_config;
+
+ ew = edit_exceptions ? ps_global->ew_for_except_vars : Main;
+
+ if(ps->restricted)
+ readonly_warning = 1;
+ else{
+ switch(ew){
+ case Main:
+ prc = ps->prc;
+ break;
+ case Post:
+ prc = ps->post_prc;
+ break;
+ default:
+ break;
+ }
+
+ readonly_warning = prc ? prc->readonly : 1;
+ if(prc && prc->quit_to_edit){
+ quit_to_edit_msg(prc);
+ treat_color_vars_as_text = 0;
+ return;
+ }
+ }
+
+ ps->next_screen = SCREEN_FUN_NULL;
+
+ mailcap_free(); /* free resources we won't be using for a while */
+
+ if(ps->fix_fixed_warning)
+ offer_to_fix_pinerc(ps);
+
+ /*
+ * First, find longest variable name
+ */
+ for(vtmp = ps->vars; vtmp->name; vtmp++){
+ if(exclude_config_var(ps, vtmp, expose_hidden_config))
+ continue;
+
+ if((i = utf8_width(pretty_var_name(vtmp->name))) > ln)
+ ln = i;
+ }
+
+ dprint((9, "initialize config list\n"));
+
+ /*
+ * Next, allocate and initialize config line list...
+ */
+ for(vtmp = ps->vars; vtmp->name; vtmp++){
+ /*
+ * INCOMING_FOLDERS is currently the first of the normally
+ * hidden variables. Should probably invent a more robust way
+ * to keep this up to date.
+ */
+ if(expose_hidden_config && vtmp == &ps->vars[V_INCOMING_FOLDERS])
+ add_hidden_vars_title = 1;
+
+ if(exclude_config_var(ps, vtmp, expose_hidden_config))
+ continue;
+
+ if(add_hidden_vars_title){
+
+ add_hidden_vars_title = 0;
+
+ new_confline(&ctmpa); /* Blank line */
+ ctmpa->flags |= CF_NOSELECT | CF_B_LINE;
+
+ new_confline(&ctmpa)->var = NULL;
+ ctmpa->help = NO_HELP;
+ ctmpa->valoffset = 2;
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->value = cpystr("--- [ Normally hidden configuration options ] ---");
+
+ new_confline(&ctmpa); /* Blank line */
+ ctmpa->flags |= CF_NOSELECT | CF_B_LINE;
+ }
+
+ if(vtmp->is_list)
+ lval = LVAL(vtmp, ew);
+ else
+ pval = PVAL(vtmp, ew);
+
+ new_confline(&ctmpa)->var = vtmp;
+ if(!first_line)
+ first_line = ctmpa;
+
+ ctmpa->valoffset = ln + 3;
+ if(vtmp->is_list)
+ ctmpa->keymenu = &config_text_wshuf_keymenu;
+ else
+ ctmpa->keymenu = &config_text_keymenu;
+
+ ctmpa->help = config_help(vtmp - ps->vars, 0);
+ ctmpa->tool = text_tool;
+
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.100w =", ln, pretty_var_name(vtmp->name));
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmpa->varname = cpystr(tmp);
+ ctmpa->varnamep = ctmpb = ctmpa;
+ ctmpa->flags |= CF_STARTITEM;
+ if(vtmp == &ps->vars[V_FEATURE_LIST]){ /* special checkbox case */
+ char *this_sect, *new_sect;
+
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->keymenu = &config_checkbox_keymenu;
+ ctmpa->tool = NULL;
+
+ /* put a nice delimiter before list */
+ new_confline(&ctmpa)->var = NULL;
+ ctmpa->varnamep = ctmpb;
+ ctmpa->keymenu = &config_checkbox_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = checkbox_tool;
+ ctmpa->valoffset = feature_indent();
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->value = cpystr("Set Feature Name");
+
+ new_confline(&ctmpa)->var = NULL;
+ ctmpa->varnamep = ctmpb;
+ ctmpa->keymenu = &config_checkbox_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = checkbox_tool;
+ ctmpa->valoffset = feature_indent();
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->value = cpystr("--- ----------------------");
+
+ for(i = 0, this_sect = NULL; (feature = feature_list(i)); i++)
+ if((new_sect = feature_list_section(feature)) &&
+ (strcmp(new_sect, HIDDEN_PREF) != 0)){
+ if(this_sect != new_sect){
+ new_confline(&ctmpa)->var = NULL;
+ ctmpa->varnamep = ctmpb;
+ ctmpa->keymenu = &config_checkbox_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = checkbox_tool;
+ ctmpa->valoffset = 2;
+ ctmpa->flags |= (CF_NOSELECT | CF_STARTITEM);
+ snprintf(tmp, sizeof(tmp), "[ %s ]", this_sect = new_sect);
+ tmp[sizeof(tmp)-1] = '\0';
+ ctmpa->value = cpystr(tmp);
+ }
+
+ new_confline(&ctmpa)->var = vtmp;
+ ctmpa->varnamep = ctmpb;
+ ctmpa->keymenu = &config_checkbox_keymenu;
+ ctmpa->help = config_help(vtmp-ps->vars,
+ feature->id);
+ ctmpa->tool = checkbox_tool;
+ ctmpa->valoffset = feature_indent();
+ ctmpa->varmem = i;
+ ctmpa->value = pretty_value(ps, ctmpa);
+ }
+ }
+ else if(standard_radio_var(ps, vtmp)){
+ standard_radio_setup(ps, &ctmpa, vtmp, NULL);
+ }
+ else if(vtmp == &ps->vars[V_SORT_KEY]){ /* radio case */
+ SortOrder def_sort;
+ int def_sort_rev;
+
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->keymenu = &config_radiobutton_keymenu;
+ ctmpa->tool = NULL;
+
+ /* put a nice delimiter before list */
+ new_confline(&ctmpa)->var = NULL;
+ ctmpa->varnamep = ctmpb;
+ ctmpa->keymenu = &config_radiobutton_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = radiobutton_tool;
+ ctmpa->valoffset = 12;
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->value = cpystr("Set Sort Options");
+
+ new_confline(&ctmpa)->var = NULL;
+ ctmpa->varnamep = ctmpb;
+ ctmpa->keymenu = &config_radiobutton_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = radiobutton_tool;
+ ctmpa->valoffset = 12;
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->value = cpystr("--- ----------------------");
+
+ decode_sort(pval, &def_sort, &def_sort_rev);
+
+ for(j = 0; j < 2; j++){
+ for(i = 0; ps->sort_types[i] != EndofList; i++){
+ new_confline(&ctmpa)->var = vtmp;
+ ctmpa->varnamep = ctmpb;
+ ctmpa->keymenu = &config_radiobutton_keymenu;
+ ctmpa->help = config_help(vtmp - ps->vars, 0);
+ ctmpa->tool = radiobutton_tool;
+ ctmpa->valoffset = 12;
+ ctmpa->varmem = i + (j * EndofList);
+ ctmpa->value = pretty_value(ps, ctmpa);
+ }
+ }
+ }
+ else if(vtmp == &ps->vars[V_USE_ONLY_DOMAIN_NAME]){ /* yesno case */
+ ctmpa->keymenu = &config_yesno_keymenu;
+ ctmpa->tool = yesno_tool;
+ ctmpa->value = pretty_value(ps, ctmpa);
+ }
+ else if(vtmp == &ps->vars[V_LITERAL_SIG]){
+ ctmpa->tool = litsig_text_tool;
+ ctmpa->value = pretty_value(ps, ctmpa);
+ }
+ else if(vtmp == &ps->vars[V_INBOX_PATH]){
+ ctmpa->tool = inbox_path_text_tool;
+ ctmpa->value = pretty_value(ps, ctmpa);
+ }
+ else if(vtmp == &ps->vars[V_POST_CHAR_SET]
+#ifndef _WINDOWS
+ || vtmp == &ps->vars[V_CHAR_SET]
+ || vtmp == &ps->vars[V_KEY_CHAR_SET]
+#endif /* !_WINDOWS */
+ || vtmp == &ps->vars[V_UNK_CHAR_SET]){
+ ctmpa->keymenu = &config_text_to_charsets_keymenu;
+ ctmpa->tool = to_charsets_text_tool;
+ ctmpa->value = pretty_value(ps, ctmpa);
+ }
+ else if(vtmp->is_list){
+ int (*t_tool)(struct pine *, int, CONF_S **, unsigned);
+ struct key_menu *km;
+
+ t_tool = NULL;
+ km = NULL;
+ if(vtmp == &ps->vars[V_INCCHECKLIST]){
+ t_tool = incoming_monitoring_list_tool;
+ km = &config_text_keymenu;
+ }
+ else if(vtmp == &ps->vars[V_PERMLOCKED]){
+ t_tool = stayopen_list_tool;
+ km = &config_text_wshufandfldr_keymenu;
+ }
+
+ if(lval){
+ for(i = 0; lval[i]; i++){
+ if(i)
+ (void)new_confline(&ctmpa);
+
+ ctmpa->var = vtmp;
+ ctmpa->varmem = i;
+ ctmpa->valoffset = ln + 3;
+ ctmpa->value = pretty_value(ps, ctmpa);
+ ctmpa->keymenu = km ? km : &config_text_wshuf_keymenu;
+ ctmpa->help = config_help(vtmp - ps->vars, 0);
+ ctmpa->tool = t_tool ? t_tool : text_tool;
+ ctmpa->varnamep = ctmpb;
+ }
+ }
+ else{
+ ctmpa->varmem = 0;
+ ctmpa->value = pretty_value(ps, ctmpa);
+ ctmpa->tool = t_tool ? t_tool : text_tool;
+ ctmpa->keymenu = km ? km : &config_text_wshuf_keymenu;
+ }
+ }
+ else{
+ if(vtmp == &ps->vars[V_FILLCOL]
+ || vtmp == &ps->vars[V_QUOTE_SUPPRESSION]
+ || vtmp == &ps->vars[V_OVERLAP]
+ || vtmp == &ps->vars[V_MAXREMSTREAM]
+ || vtmp == &ps->vars[V_MARGIN]
+ || vtmp == &ps->vars[V_DEADLETS]
+ || vtmp == &ps->vars[V_NMW_WIDTH]
+ || vtmp == &ps->vars[V_STATUS_MSG_DELAY]
+ || vtmp == &ps->vars[V_ACTIVE_MSG_INTERVAL]
+ || vtmp == &ps->vars[V_MAILCHECK]
+ || vtmp == &ps->vars[V_MAILCHECKNONCURR]
+ || vtmp == &ps->vars[V_MAILDROPCHECK]
+ || vtmp == &ps->vars[V_NNTPRANGE]
+ || vtmp == &ps->vars[V_TCPOPENTIMEO]
+ || vtmp == &ps->vars[V_TCPREADWARNTIMEO]
+ || vtmp == &ps->vars[V_TCPWRITEWARNTIMEO]
+ || vtmp == &ps->vars[V_TCPQUERYTIMEO]
+ || vtmp == &ps->vars[V_RSHOPENTIMEO]
+ || vtmp == &ps->vars[V_SSHOPENTIMEO]
+ || vtmp == &ps->vars[V_INCCHECKTIMEO]
+ || vtmp == &ps->vars[V_INCCHECKINTERVAL]
+ || vtmp == &ps->vars[V_INC2NDCHECKINTERVAL]
+ || vtmp == &ps->vars[V_USERINPUTTIMEO]
+ || vtmp == &ps->vars[V_REMOTE_ABOOK_VALIDITY]
+ || vtmp == &ps->vars[V_REMOTE_ABOOK_HISTORY])
+ ctmpa->flags |= CF_NUMBER;
+
+ ctmpa->value = pretty_value(ps, ctmpa);
+ }
+ }
+
+ dprint((9, "add hidden features\n"));
+
+ /* add the hidden features */
+ if(expose_hidden_config){
+ char *new_sect;
+
+ new_confline(&ctmpa); /* Blank line */
+ ctmpa->flags |= CF_NOSELECT | CF_B_LINE;
+
+ new_confline(&ctmpa)->var = NULL;
+ ctmpa->help = NO_HELP;
+ ctmpa->valoffset = 2;
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->value = cpystr("--- [ Normally hidden configuration features ] ---");
+
+ new_confline(&ctmpa); /* Blank line */
+ ctmpa->flags |= CF_NOSELECT | CF_B_LINE;
+
+ vtmp = &ps->vars[V_FEATURE_LIST];
+
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->keymenu = &config_checkbox_keymenu;
+ ctmpa->tool = NULL;
+
+ /* put a nice delimiter before list */
+ new_confline(&ctmpa)->var = NULL;
+ ctmpa->varnamep = ctmpb;
+ ctmpa->keymenu = &config_checkbox_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = checkbox_tool;
+ ctmpa->valoffset = feature_indent();
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->value = cpystr("Set Feature Name");
+
+ new_confline(&ctmpa)->var = NULL;
+ ctmpa->varnamep = ctmpb;
+ ctmpa->keymenu = &config_checkbox_keymenu;
+ ctmpa->help = NO_HELP;
+ ctmpa->tool = checkbox_tool;
+ ctmpa->valoffset = feature_indent();
+ ctmpa->flags |= CF_NOSELECT;
+ ctmpa->value = cpystr("--- ----------------------");
+
+ for(i = 0; (feature = feature_list(i)); i++)
+ if((new_sect = feature_list_section(feature)) &&
+ (strcmp(new_sect, HIDDEN_PREF) == 0)){
+
+ new_confline(&ctmpa)->var = vtmp;
+ ctmpa->varnamep = ctmpb;
+ ctmpa->keymenu = &config_checkbox_keymenu;
+ ctmpa->help = config_help(vtmp-ps->vars,
+ feature->id);
+ ctmpa->tool = checkbox_tool;
+ ctmpa->valoffset = feature_indent();
+ ctmpa->varmem = i;
+ ctmpa->value = pretty_value(ps, ctmpa);
+ }
+ }
+
+ vsave = save_config_vars(ps, expose_hidden_config);
+ first_line = first_sel_confline(first_line);
+
+ memset(&screen, 0, sizeof(screen));
+ screen.ro_warning = readonly_warning;
+ /* TRANSLATORS: Print something1 using something2.
+ "configuration" is something1 */
+ switch(conf_scroll_screen(ps, &screen, first_line,
+ edit_exceptions ? CONFIG_SCREEN_TITLE_EXC
+ : CONFIG_SCREEN_TITLE,
+ _("configuration"), 0)){
+ case 0:
+ break;
+
+ case 1:
+ write_pinerc(ps, ew, WRP_NONE);
+ break;
+
+ case 10:
+ revert_to_saved_config(ps, vsave, expose_hidden_config);
+ if(prc)
+ prc->outstanding_pinerc_changes = 0;
+
+ break;
+
+ default:
+ q_status_message(SM_ORDER,7,10,
+ "conf_scroll_screen bad ret, not supposed to happen");
+ break;
+ }
+
+ pval = PVAL(&ps->vars[V_SORT_KEY], ew);
+ if(vsave[V_SORT_KEY].saved_user_val.p && pval
+ && strcmp(vsave[V_SORT_KEY].saved_user_val.p, pval)){
+ if(!mn_get_mansort(ps_global->msgmap)){
+ clear_index_cache(ps_global->mail_stream, 0);
+ reset_sort_order(SRT_VRB);
+ }
+ }
+
+ treat_color_vars_as_text = 0;
+ free_saved_config(ps, &vsave, expose_hidden_config);
+#ifdef _WINDOWS
+ mswin_set_quit_confirm (F_OFF(F_QUIT_WO_CONFIRM, ps_global));
+#endif
+}
+
+
+int
+litsig_text_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ char **apval;
+ int rv = 0;
+
+ if(cmd != MC_EXIT && fixed_var((*cl)->var, NULL, NULL))
+ return(rv);
+
+ apval = APVAL((*cl)->var, ew);
+
+ switch(cmd){
+ case MC_ADD:
+ case MC_EDIT :
+ if(apval){
+ char *input = NULL, *result = NULL, *err = NULL, *cstring_version;
+ char *olddefval = NULL, *start_with;
+ size_t len;
+
+ if(!*apval && (*cl)->var->current_val.p &&
+ (*cl)->var->current_val.p[0]){
+ if(!strncmp((*cl)->var->current_val.p,
+ DSTRING,
+ (len=strlen(DSTRING)))){
+ /* strip DSTRING and trailing paren */
+ olddefval = (char *)fs_get(strlen((*cl)->var->current_val.p)+1);
+ strncpy(olddefval, (*cl)->var->current_val.p+len,
+ strlen((*cl)->var->current_val.p)-len-1);
+ olddefval[strlen((*cl)->var->current_val.p)-len-1] = '\0';
+ start_with = olddefval;
+ }
+ else{
+ olddefval = cpystr((*cl)->var->current_val.p);
+ start_with = olddefval;
+ }
+ }
+ else
+ start_with = (*apval) ? *apval : "";
+
+ input = (char *)fs_get((strlen(start_with)+1) * sizeof(char));
+ input[0] = '\0';
+ cstring_to_string(start_with, input);
+ err = signature_edit_lit(input, &result,
+ ((*cl)->var == role_comment_ptr)
+ ? "COMMENT EDITOR"
+ : "SIGNATURE EDITOR",
+ ((*cl)->var == role_comment_ptr)
+ ? h_composer_commentedit
+ : h_composer_sigedit);
+
+ if(!err){
+ if(olddefval && !strcmp(input, result) &&
+ want_to(_("Leave unset and use default "), 'y',
+ 'y', NO_HELP, WT_FLUSH_IN) == 'y'){
+ rv = 0;
+ }
+ else{
+ cstring_version = string_to_cstring(result);
+
+ if(apval && *apval)
+ fs_give((void **)apval);
+
+ if(apval){
+ *apval = cstring_version;
+ cstring_version = NULL;
+ }
+
+ if(cstring_version)
+ fs_give((void **)&cstring_version);
+
+ rv = 1;
+ }
+ }
+ else
+ rv = 0;
+
+ if(err){
+ q_status_message1(SM_ORDER, 3, 5, "%s", err);
+ fs_give((void **)&err);
+ }
+
+ if(result)
+ fs_give((void **)&result);
+ if(olddefval)
+ fs_give((void **)&olddefval);
+ if(input)
+ fs_give((void **)&input);
+ }
+
+ ps->mangled_screen = 1;
+ break;
+
+ default:
+ rv = text_tool(ps, cmd, cl, flags);
+ break;
+ }
+
+ /*
+ * At this point, if changes occurred, var->user_val.X is set.
+ * So, fix the current_val, and handle special cases...
+ *
+ * NOTE: we don't worry about the "fixed variable" case here, because
+ * editing such vars should have been prevented above...
+ */
+ if(rv == 1){
+ /*
+ * Now go and set the current_val based on user_val changes
+ * above. Turn off command line settings...
+ */
+ set_current_val((*cl)->var, TRUE, FALSE);
+
+ if((*cl)->value)
+ fs_give((void **)&(*cl)->value);
+
+ (*cl)->value = pretty_value(ps, *cl);
+
+ exception_override_warning((*cl)->var);
+
+ /*
+ * The value of literal sig can affect whether signature file is
+ * used or not. So it affects what we display for sig file variable.
+ */
+ if((*cl)->next && (*cl)->next->var == &ps->vars[V_SIGNATURE_FILE]){
+ if((*cl)->next->value)
+ fs_give((void **)&(*cl)->next->value);
+
+ (*cl)->next->value = pretty_value(ps, (*cl)->next);
+ }
+ }
+
+ return(rv);
+}
+
+
+int
+inbox_path_text_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ char **apval;
+ int rv = 0;
+ char new_inbox_path[2*MAXFOLDER+1];
+ char *def = NULL;
+ CONTEXT_S *cntxt;
+
+ if(cmd != MC_EXIT && fixed_var((*cl)->var, NULL, NULL))
+ return(rv);
+
+ apval = APVAL((*cl)->var, ew);
+
+ switch(cmd){
+ case MC_ADD:
+ case MC_EDIT:
+ cntxt = ps->context_list;
+ if(cmd == MC_EDIT && (*cl)->var){
+ if(ew == Post && (*cl)->var->post_user_val.p)
+ def = (*cl)->var->post_user_val.p;
+ else if(ew == Main && (*cl)->var->main_user_val.p)
+ def = (*cl)->var->main_user_val.p;
+ else if((*cl)->var->current_val.p)
+ def = (*cl)->var->current_val.p;
+ }
+
+ rv = add_new_folder(cntxt, ew, V_INBOX_PATH, new_inbox_path,
+ sizeof(new_inbox_path), NULL, def);
+ rv = rv ? 1 : 0;
+
+ ps->mangled_screen = 1;
+ break;
+
+ default:
+ rv = text_tool(ps, cmd, cl, flags);
+ break;
+ }
+
+ /*
+ * This is just like the end of text_tool.
+ */
+ if(rv == 1){
+ /*
+ * Now go and set the current_val based on user_val changes
+ * above. Turn off command line settings...
+ */
+ set_current_val((*cl)->var, TRUE, FALSE);
+ fix_side_effects(ps, (*cl)->var, 0);
+
+ if((*cl)->value)
+ fs_give((void **) &(*cl)->value);
+
+ (*cl)->value = pretty_value(ps, *cl);
+
+ exception_override_warning((*cl)->var);
+ }
+
+ return(rv);
+}
+
+
+int
+incoming_monitoring_list_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ int rv = 0;
+ CONTEXT_S *cntxt;
+ char **the_list;
+ CONF_S *ctmp;
+ char ***alval;
+ OPT_SCREEN_S *saved_screen;
+
+ if(cmd != MC_EXIT && fixed_var((*cl)->var, NULL, NULL))
+ return(rv);
+
+ switch(cmd){
+ case MC_ADD:
+ case MC_EDIT:
+ cntxt = ps->context_list;
+ if(!(cntxt && cntxt->use & CNTXT_INCMNG)){
+ q_status_message1(SM_ORDER, 3, 3, _("Turn on incoming folders with Config feature \"%s\""), pretty_feature_name(feature_list_name(F_ENABLE_INCOMING), -1));
+ return(rv);
+ }
+
+ saved_screen = opt_screen;
+
+ the_list = adjust_list_of_monitored_incoming(cntxt, ew, V_INCCHECKLIST);
+
+ /* adjust top if it might be necessary */
+ for(ctmp = (*cl)->varnamep;
+ ctmp && ctmp->varnamep == (*cl)->varnamep;
+ ctmp = next_confline(ctmp))
+ if(ctmp == opt_screen->top_line)
+ opt_screen->top_line = (*cl)->varnamep;
+
+ if(the_list){
+ alval = ALVAL((*cl)->var, ew);
+ free_list_array(alval);
+ if(!*the_list){
+ q_status_message(SM_ORDER, 0, 3, _("Using default, monitor all incoming folders"));
+ if(alval){
+ /*
+ * Remove config lines except for first one.
+ */
+ *cl = (*cl)->varnamep;
+ while((*cl)->next && (*cl)->next->varnamep == (*cl)->varnamep)
+ snip_confline(&(*cl)->next);
+ }
+ }
+ else
+ config_add_list(ps, cl, the_list, NULL, 0);
+
+ /* only have to free the top-level pointer */
+ fs_give((void **) &the_list);
+ rv = 1;
+ }
+ else{
+ if(LVAL((*cl)->var, ew))
+ q_status_message(SM_ORDER, 0, 3, _("List is unchanged"));
+ else
+ q_status_message(SM_ORDER, 0, 3, _("Using default, monitor all incoming folders"));
+ }
+
+ opt_screen = saved_screen;
+ ps->mangled_screen = 1;
+ break;
+
+ default:
+ rv = text_tool(ps, cmd, cl, flags);
+ /* if we deleted last one, reverts to default */
+ if(cmd == MC_DELETE && rv == 1 && (*cl)->varmem == 0
+ && (!(*cl)->next || (*cl)->next->varnamep != (*cl)))
+ q_status_message(SM_ORDER, 0, 3, _("Using default, monitor all incoming folders"));
+
+ break;
+ }
+
+ /*
+ * This is just like the end of text_tool.
+ */
+ if(rv == 1){
+ /*
+ * Now go and set the current_val based on user_val changes
+ * above. Turn off command line settings...
+ */
+ set_current_val((*cl)->var, TRUE, FALSE);
+ fix_side_effects(ps, (*cl)->var, 0);
+
+ if((*cl)->value)
+ fs_give((void **) &(*cl)->value);
+
+ (*cl)->value = pretty_value(ps, *cl);
+
+ exception_override_warning((*cl)->var);
+ }
+
+ return(rv);
+}
+
+
+char **
+adjust_list_of_monitored_incoming(CONTEXT_S *cntxt, EditWhich which, int varnum)
+{
+ LIST_SEL_S *listhead, *p, *ls;
+ int i, cnt, ftotal;
+ long width;
+ FOLDER_S *f;
+ char **the_list = NULL, buf[1000];
+
+ if(!(cntxt && cntxt->use & CNTXT_INCMNG))
+ return(the_list);
+
+ p = listhead = NULL;
+
+ /* this width is determined by select_from_list_screen() */
+ width = ps_global->ttyo->screen_cols - 4;
+
+ /*
+ * Put together a list of folders to select from.
+ * We could choose to use the list associated with
+ * the config we're editing, and that may be correct.
+ * However, we think most users will expect the list
+ * to be the list that is in use. In any case, these
+ * are almost always the same.
+ */
+
+ ftotal = folder_total(FOLDERS(cntxt));
+
+ for(i = 0; i < ftotal; i++){
+
+ f = folder_entry(i, FOLDERS(cntxt));
+
+ ls = (LIST_SEL_S *) fs_get(sizeof(*ls));
+ memset(ls, 0, sizeof(*ls));
+
+ if(f && f->nickname){
+ ls->item = cpystr(f->nickname);
+ snprintf(buf, sizeof(buf), "%s (%s)", f->nickname, f->name ? f->name : "?");
+ ls->display_item = cpystr(buf);
+ }
+ else
+ ls->item = cpystr((f && f->name) ? f->name : "?");
+
+ if(f && f->last_unseen_update != LUU_NEVERCHK)
+ ls->selected = 1;
+
+ if(p){
+ p->next = ls;
+ p = p->next;
+ }
+ else{
+ /* add a heading */
+ listhead = (LIST_SEL_S *) fs_get(sizeof(*ls));
+ memset(listhead, 0, sizeof(*listhead));
+ listhead->flags = SFL_NOSELECT;
+ listhead->display_item = cpystr(_("Incoming folders to be monitored"));
+ listhead->next = (LIST_SEL_S *) fs_get(sizeof(*ls));
+ memset(listhead->next, 0, sizeof(*listhead));
+ listhead->next->flags = SFL_NOSELECT;
+ listhead->next->display_item = cpystr(repeat_char(width, '-'));
+
+ listhead->next->next = ls;
+ p = ls;
+ }
+ }
+
+ if(!listhead){
+ q_status_message1(SM_ORDER, 3, 3, _("Turn on incoming folders with Config feature \"%s\""), pretty_feature_name(feature_list_name(F_ENABLE_INCOMING), -1));
+ return(the_list);
+ }
+
+ if(!select_from_list_screen(listhead,
+ SFL_ALLOW_LISTMODE|SFL_STARTIN_LISTMODE|SFL_ONLY_LISTMODE|SFL_CTRLC,
+ _("SELECT FOLDERS TO MONITOR"), _("folders"),
+ h_select_incoming_to_monitor,
+ _("HELP FOR SELECTING FOLDERS"), NULL)){
+
+ for(cnt = 0, p = listhead; p; p = p->next)
+ if(p->selected)
+ cnt++;
+
+ if(cnt >= 0 && cnt <= ftotal){
+ the_list = (char **) fs_get((cnt+1) * sizeof(*the_list));
+ memset(the_list, 0, (cnt+1) * sizeof(*the_list));
+ for(i = 0, p = listhead; p; p = p->next)
+ if(p->selected)
+ the_list[i++] = cpystr(p->item ? p->item : "");
+ }
+ }
+
+ free_list_sel(&listhead);
+
+ return(the_list);
+}
+
+
+int
+stayopen_list_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ int rv = 0;
+ char **newval = NULL;
+ char **ltmp = NULL;
+ char *folder = NULL;
+ void (*prev_screen)(struct pine *) = ps->prev_screen,
+ (*redraw)(void) = ps->redrawer;
+ OPT_SCREEN_S *saved_screen = NULL;
+
+ switch(cmd){
+ case MC_CHOICE:
+ if(fixed_var((*cl)->var, NULL, NULL))
+ break;
+
+ ps->redrawer = NULL;
+ ps->next_screen = SCREEN_FUN_NULL;
+ saved_screen = opt_screen;
+ folder = folder_for_config(FOR_OPTIONSCREEN);
+ removing_leading_and_trailing_white_space(folder);
+ if(folder && *folder){
+ ltmp = (char **) fs_get(2 * sizeof(char *));
+ ltmp[0] = cpystr(folder);
+ ltmp[1] = NULL;
+
+ config_add_list(ps, cl, ltmp, &newval, 0);
+
+ if(ltmp)
+ fs_give((void **) &ltmp);
+
+ rv = 1;
+
+ /* this stuff is from bottom of text_toolit() */
+
+ /*
+ * At this point, if changes occurred, var->user_val.X is set.
+ * So, fix the current_val, and handle special cases...
+ *
+ * NOTE: we don't worry about the "fixed variable" case here, because
+ * editing such vars should have been prevented above...
+ */
+
+ /*
+ * Now go and set the current_val based on user_val changes
+ * above. Turn off command line settings...
+ */
+ set_current_val((*cl)->var, TRUE, FALSE);
+ fix_side_effects(ps, (*cl)->var, 0);
+
+ /*
+ * Delay setting the displayed value until "var.current_val" is set
+ * in case current val get's changed due to a special case above.
+ */
+ if(newval){
+ if(*newval)
+ fs_give((void **) newval);
+
+ *newval = pretty_value(ps, *cl);
+ }
+
+ exception_override_warning((*cl)->var);
+
+ if(folder)
+ fs_give((void **) &folder);
+ }
+ else{
+ ps->next_screen = prev_screen;
+ ps->redrawer = redraw;
+ rv = 0;
+ }
+
+ opt_screen = saved_screen;
+ ps->mangled_screen = 1;
+ break;
+
+ default:
+ rv = text_tool(ps, cmd, cl, flags);
+ break;
+ }
+
+ return(rv);
+}
+
+
+int
+to_charsets_text_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ int rv = 0;
+ char **newval = NULL;
+ void (*prev_screen)(struct pine *) = ps->prev_screen,
+ (*redraw)(void) = ps->redrawer;
+ OPT_SCREEN_S *saved_screen = NULL;
+ char **apval;
+ char *charset = NULL;
+
+ switch(cmd){
+ case MC_CHOICE:
+ if(fixed_var((*cl)->var, NULL, NULL))
+ break;
+
+ apval = APVAL((*cl)->var, ew);
+
+ if(apval){
+ int cac_flags = CAC_ALL;
+
+ ps->redrawer = NULL;
+ ps->next_screen = SCREEN_FUN_NULL;
+ saved_screen = opt_screen;
+
+ if((*cl)->var == &ps->vars[V_POST_CHAR_SET])
+ cac_flags = CAC_POSTING;
+#ifndef _WINDOWS
+ else if((*cl)->var == &ps->vars[V_CHAR_SET]
+ || (*cl)->var == &ps->vars[V_KEY_CHAR_SET])
+ cac_flags = CAC_DISPLAY;
+#endif /* !_WINDOWS */
+
+ charset = choose_a_charset(cac_flags);
+
+ removing_leading_and_trailing_white_space(charset);
+ if(charset && *charset){
+ rv = 1;
+ if(apval && *apval)
+ fs_give((void **) apval);
+
+ *apval = charset;
+ charset = NULL;
+ newval = &(*cl)->value;
+
+ /* this stuff is from bottom of text_toolit() */
+
+ /*
+ * At this point, if changes occurred, var->user_val.X is set.
+ * So, fix the current_val, and handle special cases...
+ *
+ * NOTE: we don't worry about the "fixed variable" case here, because
+ * editing such vars should have been prevented above...
+ */
+
+ /*
+ * Now go and set the current_val based on user_val changes
+ * above. Turn off command line settings...
+ */
+ set_current_val((*cl)->var, TRUE, FALSE);
+ fix_side_effects(ps, (*cl)->var, 0);
+ if(newval){
+ if(*newval)
+ fs_give((void **) newval);
+
+ *newval = pretty_value(ps, *cl);
+ }
+
+ exception_override_warning((*cl)->var);
+ }
+ else{
+ ps->next_screen = prev_screen;
+ ps->redrawer = redraw;
+ rv = 0;
+ }
+
+ if(charset)
+ fs_give((void **) &charset);
+
+ opt_screen = saved_screen;
+ ps->mangled_screen = 1;
+ }
+ break;
+
+ default:
+ rv = text_tool(ps, cmd, cl, flags);
+ break;
+ }
+
+ return(rv);
+}
+
+
+/*
+ * pretty_var_name - return a pleasantly displayable form
+ * of variable name variable
+ */
+char *
+pretty_var_name(char *varname)
+{
+ struct variable *v;
+ int i, upper = 1;
+ static char vbuf[100];
+
+ vbuf[0] = '\0';
+ v = var_from_name(varname);
+
+ if(!v)
+ return(vbuf);
+
+ if(v->dname && v->dname[0])
+ return(v->dname);
+
+ if(!(v->name && v->name[0]))
+ return(vbuf);
+
+ /* default: uppercase first letters, dashes to spaces */
+ for(i = 0; i < sizeof(vbuf)-1 && v->name[i]; i++)
+ if(upper){
+ vbuf[i] = (islower((unsigned char) v->name[i]))
+ ? toupper((unsigned char) v->name[i])
+ : v->name[i];
+ upper = 0;
+ }
+ else if(v->name[i] == '-'){
+ vbuf[i] = SPACE;
+ upper++;
+ }
+ else
+ vbuf[i] = v->name[i];
+
+ vbuf[i] = '\0';
+ return(vbuf);
+}
+
+
+/*
+ * pretty_feature_name - return a pleasantly displayable form
+ * of feature name variable
+ */
+char *
+pretty_feature_name(char *feat, int width)
+{
+ FEATURE_S *f;
+ int i, upper = 1;
+ static char fbuf[100];
+
+ f = feature_list(feature_list_index(feature_list_id(feat)));
+ if(f && f->dname && f->dname[0])
+ return(f->dname);
+
+ /* default: uppercase first letters, dashes become spaces */
+ for(i = 0; i < sizeof(fbuf)-1 && feat[i]; i++)
+ if(upper){
+ fbuf[i] = (islower((unsigned char) feat[i]))
+ ? toupper((unsigned char) feat[i])
+ : feat[i];
+ upper = 0;
+ }
+ else if(feat[i] == '-'){
+ fbuf[i] = SPACE;
+ upper++;
+ }
+ else
+ fbuf[i] = feat[i];
+
+ fbuf[i] = '\0';
+
+ /* cut off right hand edge if necessary */
+ if(width > 0){
+ char *p, gbuf[100];
+
+ p = short_str(fbuf, gbuf, sizeof(gbuf), width, EndDots);
+
+ if(p != fbuf){
+ strncpy(fbuf, p, sizeof(fbuf)-1);
+ fbuf[sizeof(fbuf)-1] = '\0';
+ }
+ }
+
+ return(fbuf);
+}
diff --git a/alpine/setup.h b/alpine/setup.h
new file mode 100644
index 00000000..b722f8ef
--- /dev/null
+++ b/alpine/setup.h
@@ -0,0 +1,32 @@
+/*
+ * $Id: setup.h 812 2007-11-10 01:00:15Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_SETUP_INCLUDED
+#define PINE_SETUP_INCLUDED
+
+
+#include "confscroll.h"
+#include "../pith/state.h"
+
+
+/* exported protoypes */
+void option_screen(struct pine *, int);
+int litsig_text_tool(struct pine *, int, CONF_S **, unsigned);
+char *pretty_var_name(char *);
+char *pretty_feature_name(char *, int);
+
+
+#endif /* PINE_SETUP_INCLUDED */
diff --git a/alpine/signal.c b/alpine/signal.c
new file mode 100644
index 00000000..1e027e51
--- /dev/null
+++ b/alpine/signal.c
@@ -0,0 +1,921 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: signal.c 1025 2008-04-08 22:59:38Z hubert@u.washington.edu $";
+#endif
+
+/* ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ Different signal handlers for different signals
+ - Catches all the abort signals, cleans up tty modes and then coredumps
+ - Not much to do for SIGHUP
+ - Not much to do for SIGTERM
+ - turn SIGWINCH into a KEY_RESIZE command
+ - No signals for ^Z/suspend, but do it here anyway
+ - Also set up the signal handlers, and hold signals for
+ critical imap sections of code.
+ ====*/
+
+
+#include "headers.h"
+#include "alpine.h"
+#include "signal.h"
+#include "status.h"
+#include "dispfilt.h"
+#include "keymenu.h"
+#include "titlebar.h"
+#include "mailcmd.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/detach.h"
+#include "../pith/pipe.h"
+#include "../pith/util.h"
+#include "../pith/icache.h"
+#include "../pith/newmail.h"
+#include "../pith/imap.h"
+#include "../pith/adrbklib.h"
+#include "../pith/remote.h"
+#include "../pith/osdep/pipe.h"
+
+
+/*
+ * Internal Prototypes
+ */
+static RETSIGTYPE auger_in_signal(int);
+void init_sighup(void);
+void end_sighup(void);
+RETSIGTYPE term_signal(void);
+void fast_clean_up(void);
+static RETSIGTYPE usr2_signal(int);
+static RETSIGTYPE winch_signal(int);
+static RETSIGTYPE intr_signal(int);
+void suspend_notice(char *);
+void suspend_warning(void);
+
+
+
+static int cleanup_called_from_sig_handler;
+
+
+
+/*----------------------------------------------------------------------
+ Install handlers for all the signals we care to catch
+ ----------------------------------------------------------------------*/
+void
+init_signals(void)
+{
+ dprint((9, "init_signals()\n"));
+ init_sighup();
+
+#ifdef _WINDOWS
+#else
+# ifdef DEBUG
+# define CUSHION_SIG (debug < 7)
+# else
+# define CUSHION_SIG (1)
+# endif
+
+ if(CUSHION_SIG){
+ signal(SIGILL, auger_in_signal);
+ signal(SIGTRAP, auger_in_signal);
+# ifdef SIGEMT
+ signal(SIGEMT, auger_in_signal);
+# endif
+ signal(SIGBUS, auger_in_signal);
+ signal(SIGSEGV, auger_in_signal);
+ signal(SIGSYS, auger_in_signal);
+ signal(SIGQUIT, auger_in_signal);
+ /* Don't catch SIGFPE cause it's rare and we use it in a hack below*/
+ }
+
+ init_sigwinch();
+
+ /*
+ * Set up SIGUSR2 to catch signal from other software using the
+ * c-client to tell us that other access to the folder is being
+ * attempted. THIS IS A TEST: if it turns out that simply
+ * going R/O when another pine is started or the same folder is opened,
+ * then we may want to install a smarter handler that uses idle time
+ * or even prompts the user to see if it's ok to give up R/O access...
+ */
+ signal(SIGUSR2, usr2_signal);
+
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGINT, SIG_IGN);
+
+# ifdef SIGTSTP
+ /* Some unexplained behaviour on Ultrix 4.2 (Hardy) seems to be
+ resulting in Pine getting sent a SIGTSTP. Ignore it here.
+ probably better to ignore it than let it happen in any case
+ */
+ signal(SIGTSTP, SIG_IGN);
+# endif /* SIGTSTP */
+
+# ifdef SIGCHLD
+ signal(SIGCHLD, child_signal);
+# endif
+#endif /* !_WINDOWS */
+}
+
+
+/*----------------------------------------------------------------------
+ Return all signal handling back to normal
+ ----------------------------------------------------------------------*/
+void
+end_signals(int blockem)
+{
+#ifdef _WINDOWS
+
+ signal(SIGHUP, blockem ? SIG_IGN : SIG_DFL);
+
+#else
+#ifndef SIG_ERR
+#define SIG_ERR (RETSIGTYPE (*)())-1
+#endif
+
+ dprint((5, "end_signals(%d)\n", blockem));
+ if(signal(SIGILL, blockem ? SIG_IGN : SIG_DFL) == SIG_ERR){
+ fprintf(stderr, "Error resetting signals: %s\n",
+ error_description(errno));
+ exit(-1);
+ }
+
+ signal(SIGTRAP, blockem ? SIG_IGN : SIG_DFL);
+#ifdef SIGEMT
+ signal(SIGEMT, blockem ? SIG_IGN : SIG_DFL);
+#endif
+ signal(SIGBUS, blockem ? SIG_IGN : SIG_DFL);
+ signal(SIGSEGV, blockem ? SIG_IGN : SIG_DFL);
+ signal(SIGSYS, blockem ? SIG_IGN : SIG_DFL);
+#if defined(SIGWINCH) && defined(TIOCGWINSZ)
+ signal(SIGWINCH, blockem ? SIG_IGN : SIG_DFL);
+#endif
+ signal(SIGQUIT, blockem ? SIG_IGN : SIG_DFL);
+#ifdef SIGTSTP
+ signal(SIGTSTP, blockem ? SIG_IGN : SIG_DFL);
+#endif /* SIGTSTP */
+ signal(SIGHUP, blockem ? SIG_IGN : SIG_DFL);
+ signal(SIGTERM, blockem ? SIG_IGN : SIG_DFL);
+ signal(SIGINT, blockem ? SIG_IGN : SIG_DFL);
+
+#endif /* !_WINDOWS */
+}
+
+
+/*----------------------------------------------------------------------
+ Handle signals caused by aborts -- SIGSEGV, SIGILL, etc
+
+Call panic which cleans up tty modes and then core dumps
+ ----------------------------------------------------------------------*/
+static RETSIGTYPE
+auger_in_signal(int sig)
+{
+ char buf[100];
+
+ end_signals(1); /* don't catch any more signals */
+ imap_flush_passwd_cache(FALSE);
+
+ snprintf(buf, sizeof(buf), "Received abort signal(sig=%d)", sig);
+ buf[sizeof(buf)-1] = '\0';
+
+ panic(buf); /* clean up and get out */
+
+ exit(-1); /* in case panic doesn't kill us */
+}
+
+
+/*----------------------------------------------------------------------
+ Install signal handler to deal with hang up signals -- SIGHUP, SIGTERM
+
+ ----------------------------------------------------------------------*/
+void
+init_sighup(void)
+{
+#if !(defined(DOS) && !defined(_WINDOWS))
+#if defined(_WINDOWS) || defined(OS2)
+ signal(SIGHUP, (void *) hup_signal);
+#else
+ signal(SIGHUP, hup_signal);
+#endif
+#endif
+#if !(defined(DOS) || defined(OS2))
+ signal(SIGTERM, term_signal);
+#endif
+}
+
+
+/*----------------------------------------------------------------------
+ De-Install signal handler to deal with hang up signals -- SIGHUP, SIGTERM
+
+ ----------------------------------------------------------------------*/
+void
+end_sighup(void)
+{
+#if !(defined(DOS) && !defined(_WINDOWS))
+ signal(SIGHUP, SIG_IGN);
+#endif
+#if !(defined(DOS) || defined(OS2))
+ signal(SIGTERM, SIG_IGN);
+#endif
+}
+
+
+/*----------------------------------------------------------------------
+ handle hang up signal -- SIGHUP
+
+Not much to do. Rely on periodic mail file check pointing.
+ ----------------------------------------------------------------------*/
+RETSIGTYPE
+hup_signal(void)
+{
+ end_signals(1); /* don't catch any more signals */
+ dprint((1, "\n\n** Received SIGHUP **\n\n\n\n"));
+ cleanup_called_from_sig_handler = 1;
+ fast_clean_up();
+#if defined(DEBUG)
+ if(debugfile)
+ fclose(debugfile);
+#endif
+
+ _exit(0); /* cleaning up can crash */
+}
+
+
+/*----------------------------------------------------------------------
+ Timeout when no user input for a long, long time.
+ Treat it pretty much the same as if we got a HUP.
+ Only difference is we sometimes turns the timeout off (when composing).
+ ----------------------------------------------------------------------*/
+void
+user_input_timeout_exit(int to_hours)
+{
+ char msg[80];
+
+ dprint((1,
+ "\n\n** Exiting: user input timeout (%d hours) **\n\n\n\n",
+ to_hours));
+ snprintf(msg, sizeof(msg), _("\n\nAlpine timed out (No user input for %d %s)\n"), to_hours,
+ to_hours > 1 ? "hours" : "hour");
+ msg[sizeof(msg)-1] = '\0';
+ fast_clean_up();
+ end_screen(msg, 0);
+ end_titlebar();
+ end_keymenu();
+ end_keyboard(F_ON(F_USE_FK,ps_global));
+ end_tty_driver(ps_global);
+ end_signals(0);
+#if defined(DEBUG) && (!defined(DOS) || defined(_WINDOWS))
+ if(debugfile)
+ fclose(debugfile);
+#endif
+ exit(0);
+}
+
+
+/*----------------------------------------------------------------------
+ handle terminate signal -- SIGTERM
+
+Not much to do. Rely on periodic mail file check pointing.
+ ----------------------------------------------------------------------*/
+RETSIGTYPE
+term_signal(void)
+{
+#if !defined(DOS) && !defined(OS2)
+ end_signals(1); /* don't catch any more signals */
+ dprint((1, "\n\n** Received SIGTERM **\n\n\n\n"));
+ cleanup_called_from_sig_handler = 1;
+ fast_clean_up();
+#if defined(DEBUG) && (!defined(DOS) || defined(_WINDOWS))
+ if(debugfile)
+ fclose(debugfile);
+#endif
+ printf(_("\n\nAlpine finished. Received terminate signal\n\n"));
+#endif /* !DOS */
+ exit(0);
+}
+
+
+/*----------------------------------------------------------------------
+ Handle cleaning up mail streams and tty modes...
+Not much to do. Rely on periodic mail file check pointing. Don't try
+cleaning up screen or flushing output since stdout is likely already
+gone. To be safe, though, we'll at least restore the original tty mode.
+Also delete any remnant _DATAFILE_ from sending-filters.
+ ----------------------------------------------------------------------*/
+void
+fast_clean_up(void)
+{
+#if !defined(DOS) && !defined(OS2)
+ int i;
+ MAILSTREAM *m;
+#endif
+
+ dprint((1, "fast_clean_up()\n"));
+
+ if(ps_global->expunge_in_progress){
+ PineRaw(0);
+ return;
+ }
+
+ /*
+ * This gets rid of temporary cache files for remote addrbooks.
+ */
+ completely_done_with_adrbks();
+
+ /*
+ * This flushes out deferred changes and gets rid of temporary cache
+ * files for remote config files.
+ */
+ if(ps_global->prc){
+ if(ps_global->prc->outstanding_pinerc_changes)
+ write_pinerc(ps_global, Main,
+ cleanup_called_from_sig_handler ? WRP_NOUSER : WRP_NONE);
+
+ if(ps_global->prc->rd)
+ rd_close_remdata(&ps_global->prc->rd);
+
+ free_pinerc_s(&ps_global->prc);
+ }
+
+ /* as does this */
+ if(ps_global->post_prc){
+ if(ps_global->post_prc->outstanding_pinerc_changes)
+ write_pinerc(ps_global, Post,
+ cleanup_called_from_sig_handler ? WRP_NOUSER : WRP_NONE);
+
+ if(ps_global->post_prc->rd)
+ rd_close_remdata(&ps_global->post_prc->rd);
+
+ free_pinerc_s(&ps_global->post_prc);
+ }
+
+ /*
+ * Can't figure out why this section is inside the ifdef, but no
+ * harm leaving it that way, I guess.
+ */
+#if !defined(DOS) && !defined(OS2)
+ for(i = 0; i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ if(m && !m->lock)
+ pine_mail_actually_close(m);
+ }
+
+ PineRaw(0);
+
+#endif /* !DOS */
+
+ imap_flush_passwd_cache(TRUE);
+
+ dprint((1, "done with fast_clean_up\n"));
+}
+
+
+#if !defined(DOS) && !defined(OS2)
+/*----------------------------------------------------------------------
+ handle hang up signal -- SIGUSR2
+
+Not much to do. Rely on periodic mail file check pointing.
+ ----------------------------------------------------------------------*/
+static RETSIGTYPE
+usr2_signal(int sig)
+{
+ char c, *mbox, mboxbuf[20];
+ int i;
+ MAILSTREAM *stream;
+ NETMBX mb;
+
+ for(i = 0; i < ps_global->s_pool.nstream; i++){
+ stream = ps_global->s_pool.streams[i];
+ if(stream
+ && sp_flagged(stream, SP_LOCKED)
+ && !sp_dead_stream(stream)
+ && !stream->lock
+ && !stream->rdonly
+ && stream->mailbox
+ && (c = *stream->mailbox) != '{' && c != '*'){
+ pine_mail_check(stream); /* write latest state */
+ stream->rdonly = 1; /* and become read-only */
+ (void) pine_mail_ping(stream);
+ mbox = stream->mailbox;
+ if(!strucmp(stream->mailbox, ps_global->inbox_name)
+ || !strcmp(stream->mailbox, ps_global->VAR_INBOX_PATH)
+ || !strucmp(stream->original_mailbox, ps_global->inbox_name)
+ || !strcmp(stream->original_mailbox, ps_global->VAR_INBOX_PATH))
+ mbox = "INBOX";
+ else if(mail_valid_net_parse(stream->mailbox, &mb) && mb.mailbox)
+ mbox = mb.mailbox;
+
+ q_status_message1(SM_ASYNC, 3, 7,
+ _("Another email program is accessing %s. Session now Read-Only."),
+ short_str((mbox && *mbox) ? mbox : "folder",
+ mboxbuf, sizeof(mboxbuf), 19, FrontDots));
+ dprint((1, "** folder %s went read-only **\n\n",
+ stream->mailbox));
+ }
+ }
+}
+#endif
+
+
+/*----------------------------------------------------------------------
+ Install signal handler to deal with window resize signal -- SIGWINCH
+
+ ----------------------------------------------------------------------*/
+void
+init_sigwinch (void)
+{
+#if defined(SIGWINCH) && defined(TIOCGWINSZ)
+ signal(SIGWINCH, winch_signal);
+#endif
+}
+
+
+#if defined(SIGWINCH) && defined(TIOCGWINSZ)
+/*----------------------------------------------------------------------
+ Handle window resize signal -- SIGWINCH
+
+ The planned strategy is just force a redraw command. This is similar
+ to new mail handling which forces a noop command. The signals are
+ help until pine reads input. Then a KEY_RESIZE is forced into the command
+ stream .
+ Note that ready_for_winch is only non-zero inside the read_char function,
+ so the longjmp only ever happens there, and it is really just a jump
+ from lower down in the function up to the top of that function. Its
+ purpose is to return a KEY_RESIZE from read_char when interrupted
+ out of the select lower down in read_char.
+ ----------------------------------------------------------------------*/
+extern jmp_buf winch_state;
+extern int ready_for_winch, winch_occured;
+
+static RETSIGTYPE
+winch_signal(int sig)
+{
+ clear_cursor_pos();
+ init_sigwinch();
+ winch_cleanup();
+}
+#endif
+
+
+/*
+ * winch_cleanup -
+ */
+void
+winch_cleanup(void)
+{
+#if defined(SIGWINCH) && defined(TIOCGWINSZ)
+ if(ready_for_winch)
+ longjmp(winch_state, 1);
+ else
+ winch_occured = 1;
+#endif
+}
+
+
+#ifdef SIGCHLD
+/*----------------------------------------------------------------------
+ Handle child status change -- SIGCHLD
+
+ The strategy here is to install the handler when we spawn a child, and
+ to let the system tell us when the child's state has changed. In the
+ mean time, we can do whatever. Typically, "whatever" is spinning in a
+ loop alternating between sleep and new_mail calls intended to keep the
+ IMAP stream alive.
+
+ ----------------------------------------------------------------------*/
+static short child_signalled, child_jump;
+static jmp_buf child_state;
+
+RETSIGTYPE
+child_signal(int sig)
+{
+#ifdef BACKGROUND_POST
+ /*
+ * reap background posting process
+ */
+ if(ps_global->post && ps_global->post->pid){
+ pid_t pid;
+ int es;
+
+ pid = process_reap(ps_global->post->pid, &es, PR_NOHANG);
+ if(pid == ps_global->post->pid){
+ ps_global->post->status = es;
+ ps_global->post->pid = 0;
+ return;
+ }
+ else if(pid < 0 && errno != EINTR){
+ fs_give((void **) &ps_global->post);
+ }
+ }
+#endif /* BACKGROUND_POST */
+
+ child_signalled = 1;
+ if(child_jump)
+ longjmp(child_state, 1);
+}
+#endif /* SIGCHLD */
+
+
+/*
+ * pipe_callback - handle pine-specific pipe setup like child
+ * signal handler and possibly any display stuff
+ * BUG: this function should probably be in a "alpine/pipe.c"
+ */
+void
+pipe_callback(PIPE_S *syspipe, int flags, void *data)
+{
+#ifdef _WINDOWS
+ bitmap_t bitmap;
+#endif
+ if(flags & OSB_PRE_OPEN){
+ dprint((5, "Opening pipe: (%s%s%s%s%s%s)\n",
+ (syspipe->mode & PIPE_WRITE) ? "W":"", (syspipe->mode & PIPE_READ) ? "R":"",
+ (syspipe->mode & PIPE_NOSHELL) ? "N":"", (syspipe->mode & PIPE_PROT) ? "P":"",
+ (syspipe->mode & PIPE_USER) ? "U":"", (syspipe->mode & PIPE_RESET) ? "T":""));
+
+#ifdef _WINDOWS
+ q_status_message(SM_ORDER, 0, 0,
+ "Waiting for called program to finish...");
+
+ flush_status_messages(1);
+ setbitmap(bitmap);
+ draw_keymenu(&pipe_cancel_keymenu, bitmap, ps_global->ttyo->screen_cols,
+ 1-FOOTER_ROWS(ps_global), 0, FirstMenu);
+#else /* UNIX */
+
+ if(!(syspipe->mode & (PIPE_WRITE | PIPE_READ)) && !(syspipe->mode & PIPE_SILENT)){
+ flush_status_messages(0); /* just clean up display */
+ ClearScreen();
+ fflush(stdout);
+ }
+
+ if(syspipe->mode & PIPE_RESET)
+ ttyfix(0);
+
+#ifdef SIGCHLD
+ /*
+ * Prepare for demise of child. Use SIGCHLD if it's available so
+ * we can do useful things, like keep the IMAP stream alive, while
+ * we're waiting on the child. The handler may have been changed by
+ * something in the composer, in particular, by an alt_editor call.
+ * So we need to re-set it to child_signal and then set it back
+ * when we're done.
+ */
+ child_signalled = child_jump = 0;
+ syspipe->chld = signal(SIGCHLD, child_signal);
+#endif
+#endif /* UNIX */
+ }
+ else if(flags & OSB_POST_OPEN){
+#ifdef _WINDOWS
+
+ clearfooter(ps_global);
+ ps_global->mangled_footer = 1;
+
+ if((int) data == -2)
+ q_status_message1(SM_ORDER, 2, 3, "Ignoring completion of %s", syspipe->command);
+
+#else /* UNIX */
+ if(!(syspipe->mode & (PIPE_WRITE | PIPE_READ)) && !(syspipe->mode & PIPE_SILENT)){
+ ClearScreen();
+ ps_global->mangled_screen = 1;
+ }
+
+ if(syspipe->mode & PIPE_RESET)
+ ttyfix(1);
+
+#ifdef SIGCHLD
+ (void) signal(SIGCHLD, SIG_DFL);
+#endif
+#endif /* UNIX */
+ }
+ else if(flags & OSB_PRE_CLOSE){
+#ifdef SIGCHLD
+ /*
+ * this is here so close_system_pipe so it has something
+ * to do while we're waiting on the other end of the
+ * pipe to complete. When we're in the background for
+ * a shell, the the side effect is pinging
+ */
+ RETSIGTYPE (*alarm_sig)();
+ int old_cue = F_ON(F_SHOW_DELAY_CUE, ps_global);
+
+ /*
+ * remember the current SIGALRM handler, and make sure it's
+ * installed when we're finished just in case the longjmp
+ * out of the SIGCHLD handler caused sleep() to lose it.
+ * Don't pay any attention to that man behind the curtain.
+ */
+ alarm_sig = signal(SIGALRM, SIG_IGN);
+ (void) F_SET(F_SHOW_DELAY_CUE, ps_global, 0);
+ ps_global->noshow_timeout = 1;
+ while(!child_signalled){
+ /* wake up and prod server */
+ if(!(syspipe->mode & PIPE_NONEWMAIL))
+ new_mail(0, 2,
+ (syspipe->mode & PIPE_RESET) ? NM_NONE : NM_DEFER_SORT);
+
+ if(!child_signalled){
+ if(setjmp(child_state) == 0){
+ child_jump = 1; /* prepare to wake up */
+ sleep(600); /* give it 5mins to happend */
+ }
+ else
+ our_sigunblock(SIGCHLD);
+ }
+
+ child_jump = 0;
+ }
+
+ ps_global->noshow_timeout = 0;
+ F_SET(F_SHOW_DELAY_CUE, ps_global, old_cue);
+ (void) signal(SIGALRM, alarm_sig);
+ (void) signal(SIGCHLD, syspipe->chld);
+#endif
+ }
+ else if(flags & OSB_POST_CLOSE){
+ if(syspipe->mode & PIPE_RESET) /* restore our tty modes */
+ ttyfix(1);
+
+ if(!(syspipe->mode & (PIPE_WRITE | PIPE_READ | PIPE_SILENT))){
+ ClearScreen(); /* No I/O to forked child */
+ ps_global->mangled_screen = 1;
+ }
+ }
+}
+
+
+/*
+ * Command interrupt support.
+ */
+
+static RETSIGTYPE
+intr_signal(int sig)
+{
+ ps_global->intr_pending = 1;
+}
+
+
+int
+intr_handling_on(void)
+{
+#ifdef _WINDOWS
+ return 0; /* No interrupts in Windows */
+#else /* UNIX */
+ if(signal(SIGINT, intr_signal) == intr_signal)
+ return 0; /* already installed */
+
+ intr_proc(1);
+ if(ps_global && ps_global->ttyo)
+ draw_cancel_keymenu();
+
+ return 1;
+#endif /* UNIX */
+}
+
+
+void
+intr_handling_off(void)
+{
+#ifdef _WINDOWS
+#else /* UNIX */
+ if(signal(SIGINT, SIG_IGN) == SIG_IGN) /* already off! */
+ return;
+
+ ps_global->intr_pending = 0;
+ intr_proc(0);
+ if(ps_global && ps_global->ttyo)
+ blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
+
+ ps_global->mangled_footer = 1;
+#endif /* UNIX */
+}
+
+
+/*----------------------------------------------------------------------
+ Set or reset what needs to be set when coming out of pico to run
+ an alternate editor.
+
+ Args: come_back -- If come_back is 0 we're going out of our environment
+ to set up for an external editor.
+ If come_back is 1 we're coming back into pine.
+ ----------------------------------------------------------------------*/
+int
+ttyfix(int come_back)
+{
+#if defined(DEBUG) && (!defined(DOS) || defined(_WINDOWS))
+ if(debugfile)
+ fflush(debugfile);
+#endif
+
+ if(come_back){
+#ifdef OS2
+ enter_text_mode(NULL);
+#endif
+ init_screen();
+ init_tty_driver(ps_global);
+ init_keyboard(F_ON(F_USE_FK,ps_global));
+#ifdef OS2
+ dont_interrupt();
+#endif
+ fix_windsize(ps_global);
+ }
+ else{
+ EndInverse();
+ end_keyboard(F_ON(F_USE_FK,ps_global));
+ end_tty_driver(ps_global);
+ end_screen(NULL, 0);
+#ifdef OS2
+ interrupt_ok();
+#endif
+ }
+
+ return(0);
+}
+
+
+/*----------------------------------------------------------------------
+ Suspend Pine. Reset tty and suspend. Suspend is finished when this returns
+
+ Args: The pine structure
+
+ Result: Execution suspended for a while. Screen will need redrawing
+ after this is done.
+
+ Instead of the usual handling of ^Z by catching a signal, we actually read
+the ^Z and then clean up the tty driver, then kill ourself to stop, and
+pick up where we left off when execution resumes.
+ ----------------------------------------------------------------------*/
+UCS
+do_suspend(void)
+{
+ struct pine *pine = ps_global;
+ time_t now;
+ UCS retval;
+#if defined(DOS) || defined(OS2)
+ int result, orig_cols, orig_rows;
+#else
+ int isremote;
+#endif
+#ifdef DOS
+ static char *shell = NULL;
+#define STD_SHELL "COMMAND.COM"
+#else
+#ifdef OS2
+ static char *shell = NULL;
+#define STD_SHELL "CMD.EXE"
+#endif
+#endif
+
+ if(!have_job_control()){
+ bogus_command(ctrl('Z'), F_ON(F_USE_FK, pine) ? "F1" : "?");
+ return(NO_OP_COMMAND);
+ }
+
+ if(F_OFF(F_CAN_SUSPEND, pine)){
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("Alpine suspension not enabled - see help text"));
+ return(NO_OP_COMMAND);
+ }
+
+#ifdef _WINDOWS
+ mswin_minimize();
+ return(NO_OP_COMMAND);
+#else
+
+ isremote = (ps_global->mail_stream && ps_global->mail_stream->mailbox
+ && (*ps_global->mail_stream->mailbox == '{'
+ || (*ps_global->mail_stream->mailbox == '*'
+ && *(ps_global->mail_stream->mailbox + 1) == '{')));
+
+ now = time((time_t *)0);
+ dprint((1, "\n\n --- %s - SUSPEND ---- %s",
+ isremote ? "REMOTE" : "LOCAL", ctime(&now)));
+ ttyfix(0);
+
+#if defined(DOS) || defined(OS2)
+ suspend_notice("exit");
+ if (!shell){
+ char *p;
+
+ if (!((shell = getenv("SHELL")) || (shell = getenv("COMSPEC"))))
+ shell = STD_SHELL;
+
+ shell = cpystr(shell); /* copy in free storage */
+ for(p = shell; (p = strchr(p, '/')); p++)
+ *p = '\\';
+ }
+
+ result = system(shell);
+#else
+ if(F_ON(F_SUSPEND_SPAWNS, ps_global)){
+ PIPE_S *syspipe;
+ int flag = some_stream_is_locked() ? PIPE_NONEWMAIL : 0;
+
+ flag |= PIPE_USER|PIPE_RESET;
+ if((syspipe = open_system_pipe(NULL, NULL, NULL, flag,
+ 0, pipe_callback, pipe_report_error)) != NULL){
+ suspend_notice("exit");
+#ifndef SIGCHLD
+ if(isremote)
+ suspend_warning();
+#endif
+ (void) close_system_pipe(&syspipe, NULL, pipe_callback);
+ }
+ }
+ else{
+ suspend_notice("fg");
+
+ if(isremote)
+ suspend_warning();
+
+ stop_process();
+ }
+#endif /* DOS */
+
+ now = time((time_t *)0);
+ dprint((1, "\n\n ---- RETURN FROM SUSPEND ---- %s",
+ ctime(&now)));
+
+ ttyfix(1);
+
+#if defined(DOS) || defined(OS2)
+ orig_cols = pine->ttyo->screen_cols;
+ orig_rows = pine->ttyo->screen_rows;
+#endif
+
+ fix_windsize(pine);
+
+#if defined(DOS) || defined(OS2)
+ if(orig_cols != pine->ttyo->screen_cols ||
+ orig_rows != pine->ttyo->screen_rows)
+ retval = KEY_RESIZE;
+ else
+#endif
+ retval = ctrl('L');;
+
+#if defined(DOS) || defined(OS2)
+ if(result == -1)
+ q_status_message1(SM_ORDER | SM_DING, 3, 4,
+ _("Error loading \"%s\""), shell);
+#endif
+
+ if(isremote && !ps_global->mail_stream->lock
+ && !pine_mail_ping(ps_global->mail_stream))
+ q_status_message(SM_ORDER | SM_DING, 4, 9,
+ _("Suspended for too long, IMAP connection broken"));
+
+ return(retval);
+#endif /* !_WINDOWS */
+}
+
+
+
+/*----------------------------------------------------------------------
+ ----*/
+void
+suspend_notice(char *s)
+{
+ printf(_("\nAlpine suspended. Give the \"%s\" command to come back.\n"), s);
+ fflush(stdout);
+}
+
+
+/*----------------------------------------------------------------------
+ ----*/
+void
+suspend_warning(void)
+{
+ puts(_("Warning: Your IMAP connection will be closed if Alpine"));
+ puts(_("is suspended for more than 30 minutes\n"));
+ fflush(stdout);
+}
+
+
+/*----------------------------------------------------------------------
+ ----*/
+void
+fix_windsize(struct pine *pine)
+{
+ int old_width = pine->ttyo->screen_cols;
+
+ dprint((9, "fix_windsize()\n"));
+ mark_keymenu_dirty();
+ mark_status_dirty();
+ mark_titlebar_dirty();
+ clear_cursor_pos();
+ get_windsize(pine->ttyo);
+
+ if(old_width != pine->ttyo->screen_cols)
+ clear_index_cache(pine->mail_stream, 0);
+}
diff --git a/alpine/signal.h b/alpine/signal.h
new file mode 100644
index 00000000..0abf6924
--- /dev/null
+++ b/alpine/signal.h
@@ -0,0 +1,44 @@
+/*
+ * $Id: signal.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_SIGNAL_INCLUDED
+#define PINE_SIGNAL_INCLUDED
+
+
+#include <general.h>
+
+#include "../pith/osdep/pipe.h" /* for PIPE_S */
+
+#include "../pith/state.h"
+#include "../pith/signal.h"
+
+#define MAX_BM 150 /* max length of busy message */
+
+
+/* exported protoypes */
+RETSIGTYPE hup_signal(void);
+RETSIGTYPE child_signal(int);
+void user_input_timeout_exit(int);
+void init_signals(void);
+void init_sigwinch(void);
+void end_signals(int);
+int ttyfix(int);
+UCS do_suspend(void);
+void winch_cleanup(void);
+void pipe_callback(PIPE_S *, int, void *);
+void fix_windsize(struct pine *);
+
+
+#endif /* PINE_SIGNAL_INCLUDED */
diff --git a/alpine/smime.c b/alpine/smime.c
new file mode 100644
index 00000000..bcf88acb
--- /dev/null
+++ b/alpine/smime.c
@@ -0,0 +1,1151 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: smime.c 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*
+ * This is based on a contribution from Jonathan Paisley
+ *
+ * File: smime.c
+ * Author: paisleyj@dcs.gla.ac.uk
+ * Date: 01/2001
+ */
+
+
+#include "headers.h"
+
+#ifdef SMIME
+
+#include "../pith/charconv/utf8.h"
+#include "../pith/status.h"
+#include "../pith/store.h"
+#include "../pith/conf.h"
+#include "../pith/list.h"
+#include "radio.h"
+#include "keymenu.h"
+#include "mailview.h"
+#include "conftype.h"
+#include "confscroll.h"
+#include "setup.h"
+#include "smime.h"
+
+
+/* internal prototypes */
+void format_smime_info(int pass, BODY *body, long msgno, gf_io_t pc);
+void print_separator_line(int percent, int ch, gf_io_t pc);
+void output_cert_info(X509 *cert, gf_io_t pc);
+void output_X509_NAME(X509_NAME *name, gf_io_t pc);
+void side_by_side(STORE_S *left, STORE_S *right, gf_io_t pc);
+void get_fingerprint(X509 *cert, const EVP_MD *type, char *buf, size_t maxLen);
+STORE_S *wrap_store(STORE_S *in, int width);
+void smime_config_init_display(struct pine *, CONF_S **, CONF_S **);
+void revert_to_saved_smime_config(struct pine *ps, SAVED_CONFIG_S *vsave);
+SAVED_CONFIG_S *save_smime_config_vars(struct pine *ps);
+void free_saved_smime_config(struct pine *ps, SAVED_CONFIG_S **vsavep);
+int smime_helper_tool(struct pine *, int, CONF_S **, unsigned);
+
+
+/*
+ * prompt the user for their passphrase
+ * (possibly prompting with the email address in s_passphrase_emailaddr)
+ */
+int
+smime_get_passphrase(void)
+{
+ int rc;
+ int flags;
+ char prompt[500];
+ HelpType help = NO_HELP;
+
+ assert(ps_global->smime != NULL);
+ snprintf(prompt, sizeof(prompt),
+ _("Enter passphrase for <%s>: "), (ps_global->smime && ps_global->smime->passphrase_emailaddr) ? ps_global->smime->passphrase_emailaddr : "unknown");
+
+ do {
+ flags = OE_PASSWD | OE_DISALLOW_HELP;
+ ((char *) ps_global->smime->passphrase)[0] = '\0';
+ rc = optionally_enter((char *) ps_global->smime->passphrase,
+ -FOOTER_ROWS(ps_global), 0,
+ sizeof(ps_global->smime->passphrase),
+ prompt, NULL, help, &flags);
+ } while (rc!=0 && rc!=1 && rc>0);
+
+ if(rc==0){
+ if(ps_global->smime)
+ ps_global->smime->entered_passphrase = 1;
+ }
+
+ return rc; /* better return rc and make the caller check its return value */
+}
+
+
+void
+smime_info_screen(struct pine *ps)
+{
+ long msgno;
+ OtherMenu what;
+ int offset = 0;
+ BODY *body;
+ ENVELOPE *env;
+ HANDLE_S *handles = NULL;
+ SCROLL_S scrollargs;
+ STORE_S *store = NULL;
+
+ ps->prev_screen = smime_info_screen;
+ ps->next_screen = SCREEN_FUN_NULL;
+
+ if(mn_total_cur(ps->msgmap) > 1L){
+ q_status_message(SM_ORDER | SM_DING, 0, 3,
+ _("Can only view one message's information at a time."));
+ return;
+ }
+ /* else check for existence of smime bits */
+
+ msgno = mn_m2raw(ps->msgmap, mn_get_cur(ps->msgmap));
+
+ env = mail_fetch_structure(ps->mail_stream, msgno, &body, 0);
+ if(!env || !body){
+ q_status_message(SM_ORDER, 0, 3,
+ _("Can't fetch body of message."));
+ return;
+ }
+
+ what = FirstMenu;
+
+ store = so_get(CharStar, NULL, EDIT_ACCESS);
+
+ while(ps->next_screen == SCREEN_FUN_NULL){
+
+ ClearLine(1);
+
+ so_truncate(store, 0);
+
+ view_writec_init(store, &handles, HEADER_ROWS(ps),
+ HEADER_ROWS(ps) +
+ ps->ttyo->screen_rows - (HEADER_ROWS(ps)
+ + HEADER_ROWS(ps)));
+
+ gf_puts_uline("Overview", view_writec);
+ gf_puts(NEWLINE, view_writec);
+
+ format_smime_info(1, body, msgno, view_writec);
+ gf_puts(NEWLINE, view_writec);
+ format_smime_info(2, body, msgno, view_writec);
+
+ view_writec_destroy();
+
+ ps->next_screen = SCREEN_FUN_NULL;
+
+ memset(&scrollargs, 0, sizeof(SCROLL_S));
+ scrollargs.text.text = so_text(store);
+ scrollargs.text.src = CharStar;
+ scrollargs.text.desc = "S/MIME information";
+ scrollargs.body_valid = 1;
+
+ if(offset){ /* resize? preserve paging! */
+ scrollargs.start.on = Offset;
+ scrollargs.start.loc.offset = offset;
+ offset = 0L;
+ }
+
+ scrollargs.bar.title = "S/MIME INFORMATION";
+/* scrollargs.end_scroll = view_end_scroll; */
+ scrollargs.resize_exit = 1;
+ scrollargs.help.text = NULL;
+ scrollargs.help.title = "HELP FOR S/MIME INFORMATION VIEW";
+ scrollargs.keys.menu = &smime_info_keymenu;
+ scrollargs.keys.what = what;
+ setbitmap(scrollargs.keys.bitmap);
+
+ if(scrolltool(&scrollargs) == MC_RESIZE)
+ offset = scrollargs.start.loc.offset;
+ }
+
+ so_give(&store);
+}
+
+
+void
+format_smime_info(int pass, BODY *body, long msgno, gf_io_t pc)
+{
+ PKCS7 *p7;
+ int i;
+
+ if(body->type == TYPEMULTIPART){
+ PART *p;
+
+ for(p=body->nested.part; p; p=p->next)
+ format_smime_info(pass, &p->body, msgno, pc);
+ }
+
+ p7 = body->sparep;
+ if(p7){
+
+ if(PKCS7_type_is_signed(p7)){
+ STACK_OF(X509) *signers;
+
+ switch(pass){
+ case 1:
+ gf_puts(_("This message was cryptographically signed."), pc);
+ gf_puts(NEWLINE, pc);
+ break;
+
+ case 2:
+ signers = PKCS7_get0_signers(p7, NULL, 0);
+
+ if(signers){
+
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Certificate%s used for signing"),
+ plural(sk_X509_num(signers)));
+ gf_puts_uline(tmp_20k_buf, pc);
+ gf_puts(NEWLINE, pc);
+ print_separator_line(100, '-', pc);
+
+ for(i=0; i<sk_X509_num(signers); i++){
+ X509 *x = sk_X509_value(signers, i);
+
+ if(x){
+ output_cert_info(x, pc);
+ gf_puts(NEWLINE, pc);
+ }
+ }
+ }
+
+ sk_X509_free(signers);
+ break;
+ }
+
+ }
+ else if(PKCS7_type_is_enveloped(p7)){
+
+ switch(pass){
+ case 1:
+ gf_puts(_("This message was encrypted."), pc);
+ gf_puts(NEWLINE, pc);
+ break;
+
+ case 2:
+ if(p7->d.enveloped && p7->d.enveloped->enc_data){
+ X509_ALGOR *alg = p7->d.enveloped->enc_data->algorithm;
+ STACK_OF(PKCS7_RECIP_INFO) *ris = p7->d.enveloped->recipientinfo;
+ int found = 0;
+
+ gf_puts(_("The algorithm used to encrypt was "), pc);
+
+ if(alg){
+ char *n = (char *) OBJ_nid2sn( OBJ_obj2nid(alg->algorithm));
+
+ gf_puts(n ? n : "<unknown>", pc);
+
+ }
+ else
+ gf_puts("<unknown>", pc);
+
+ gf_puts("." NEWLINE NEWLINE, pc);
+
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Certificate%s for decrypting"),
+ plural(sk_PKCS7_RECIP_INFO_num(ris)));
+ gf_puts_uline(tmp_20k_buf, pc);
+ gf_puts(NEWLINE, pc);
+ print_separator_line(100, '-', pc);
+
+ for(i=0; i<sk_PKCS7_RECIP_INFO_num(ris); i++){
+ PKCS7_RECIP_INFO *ri;
+ PERSONAL_CERT *pcert;
+
+ ri = sk_PKCS7_RECIP_INFO_value(ris, i);
+ if(!ri)
+ continue;
+
+ pcert = find_certificate_matching_recip_info(ri);
+
+ if(pcert){
+ if(found){
+ print_separator_line(25, '*', pc);
+ gf_puts(NEWLINE, pc);
+ }
+
+ found = 1;
+
+ output_cert_info(pcert->cert, pc);
+ gf_puts(NEWLINE, pc);
+
+ }
+ }
+
+ if(!found){
+ gf_puts(_("No certificate capable of decrypting could be found."), pc);
+ gf_puts(NEWLINE, pc);
+ gf_puts(NEWLINE, pc);
+ }
+ }
+
+ break;
+ }
+ }
+ }
+}
+
+
+void
+print_separator_line(int percent, int ch, gf_io_t pc)
+{
+ int i, start, len;
+
+ len = ps_global->ttyo->screen_cols * percent / 100;
+ start = (ps_global->ttyo->screen_cols - len)/2;
+
+ for(i=0; i<start; i++)
+ pc(' ');
+
+ for(i=start; i<start+len; i++)
+ pc(ch);
+
+ gf_puts(NEWLINE, pc);
+}
+
+
+void
+output_cert_info(X509 *cert, gf_io_t pc)
+{
+ char buf[256];
+ STORE_S *left,*right;
+ gf_io_t spc;
+ int len;
+
+ left = so_get(CharStar, NULL, EDIT_ACCESS);
+ right = so_get(CharStar, NULL, EDIT_ACCESS);
+ if(!(left && right))
+ return;
+
+ gf_set_so_writec(&spc, left);
+
+ if(!cert->cert_info){
+ gf_puts("Couldn't find certificate info.", spc);
+ gf_puts(NEWLINE, spc);
+ }
+ else{
+ gf_puts_uline("Subject (whose certificate it is)", spc);
+ gf_puts(NEWLINE, spc);
+
+ output_X509_NAME(cert->cert_info->subject, spc);
+ gf_puts(NEWLINE, spc);
+
+ gf_puts_uline("Serial Number", spc);
+ gf_puts(NEWLINE, spc);
+
+ snprintf(buf, sizeof(buf), "%ld", ASN1_INTEGER_get(cert->cert_info->serialNumber));
+ gf_puts(buf, spc);
+ gf_puts(NEWLINE, spc);
+ gf_puts(NEWLINE, spc);
+
+ gf_puts_uline("Validity", spc);
+ gf_puts(NEWLINE, spc);
+ {
+ BIO *mb = BIO_new(BIO_s_mem());
+ char iobuf[4096];
+
+ gf_puts("Not Before: ", spc);
+
+ (void) BIO_reset(mb);
+ ASN1_UTCTIME_print(mb, cert->cert_info->validity->notBefore);
+ (void) BIO_flush(mb);
+ while((len = BIO_read(mb, iobuf, sizeof(iobuf))) > 0)
+ gf_nputs(iobuf, len, spc);
+
+ gf_puts(NEWLINE, spc);
+
+ gf_puts("Not After: ", spc);
+
+ (void) BIO_reset(mb);
+ ASN1_UTCTIME_print(mb, cert->cert_info->validity->notAfter);
+ (void) BIO_flush(mb);
+ while((len = BIO_read(mb, iobuf, sizeof(iobuf))) > 0)
+ gf_nputs(iobuf, len, spc);
+
+ gf_puts(NEWLINE, spc);
+ gf_puts(NEWLINE, spc);
+
+ BIO_free(mb);
+ }
+ }
+
+ gf_clear_so_writec(left);
+
+ gf_set_so_writec(&spc, right);
+
+ if(!cert->cert_info){
+ gf_puts(_("Couldn't find certificate info."), spc);
+ gf_puts(NEWLINE, spc);
+ }
+ else{
+ gf_puts_uline("Issuer", spc);
+ gf_puts(NEWLINE, spc);
+
+ output_X509_NAME(cert->cert_info->issuer, spc);
+ gf_puts(NEWLINE, spc);
+ }
+
+ gf_clear_so_writec(right);
+
+ side_by_side(left, right, pc);
+
+ gf_puts_uline("SHA1 Fingerprint", pc);
+ gf_puts(NEWLINE, pc);
+ get_fingerprint(cert, EVP_sha1(), buf, sizeof(buf));
+ gf_puts(buf, pc);
+ gf_puts(NEWLINE, pc);
+
+ gf_puts_uline("MD5 Fingerprint", pc);
+ gf_puts(NEWLINE, pc);
+ get_fingerprint(cert, EVP_md5(), buf, sizeof(buf));
+ gf_puts(buf, pc);
+ gf_puts(NEWLINE, pc);
+
+ so_give(&left);
+ so_give(&right);
+}
+
+
+void
+output_X509_NAME(X509_NAME *name, gf_io_t pc)
+{
+ int i, c;
+ char buf[256];
+
+ c = X509_NAME_entry_count(name);
+
+ for(i=c-1; i>=0; i--){
+ X509_NAME_ENTRY *e;
+
+ e = X509_NAME_get_entry(name,i);
+ if(!e)
+ continue;
+
+ X509_NAME_get_text_by_OBJ(name, e->object, buf, sizeof(buf));
+
+ gf_puts(buf, pc);
+ gf_puts(NEWLINE, pc);
+ }
+}
+
+
+/*
+ * Output the contents of the given stores (left and right)
+ * to the given gf_io_t.
+ * The width of the terminal is inspected and two columns
+ * are created to fit the stores into. They are then wrapped
+ * and merged.
+ */
+void
+side_by_side(STORE_S *left, STORE_S *right, gf_io_t pc)
+{
+ STORE_S *left_wrapped;
+ STORE_S *right_wrapped;
+ char buf_l[256];
+ char buf_r[256];
+ char *b;
+ int i;
+ int w = ps_global->ttyo->screen_cols/2 - 1;
+
+ so_seek(left, 0, 0);
+ so_seek(right, 0, 0);
+
+ left_wrapped = wrap_store(left, w);
+ right_wrapped = wrap_store(right, w);
+
+ so_seek(left_wrapped, 0, 0);
+ so_seek(right_wrapped, 0, 0);
+
+ for(;;){
+
+ if(!so_fgets(left_wrapped, buf_l, sizeof(buf_l)))
+ break;
+
+ if(!so_fgets(right_wrapped, buf_r, sizeof(buf_r)))
+ break;
+
+ for(i=0, b=buf_l; i<w && *b && *b!='\r' && *b!='\n'; i++,b++){
+ pc(*b);
+ /* reduce accumulated width if an embed tag is discovered */
+ if(*b==TAG_EMBED)
+ i-=2;
+ }
+
+ if(buf_r[0]){
+ while(i<w){
+ pc(' ');
+ i++;
+ }
+
+ for(i=0, b=buf_r; i<w && *b && *b!='\r' && *b!='\n'; i++,b++)
+ pc(*b);
+ }
+
+ gf_puts(NEWLINE, pc);
+ }
+
+ so_give(&left_wrapped);
+ so_give(&right_wrapped);
+}
+
+
+void
+get_fingerprint(X509 *cert, const EVP_MD *type, char *buf, size_t maxLen)
+{
+ unsigned char md[128];
+ char *b;
+ unsigned int len, i;
+
+ len = sizeof(md);
+
+ X509_digest(cert, type, md, &len);
+
+ b = buf;
+ *b = 0;
+ for(i=0; i<len; i++){
+ if(b-buf+3>=maxLen)
+ break;
+
+ if(i != 0)
+ *b++ = ':';
+
+ snprintf(b, maxLen - (b-buf), "%02x", md[i]);
+ b+=2;
+ }
+}
+
+
+/*
+ * Wrap the text in the given store to the given width.
+ * A new store is created for the result.
+ */
+STORE_S *
+wrap_store(STORE_S *in, int width)
+{
+ STORE_S *result;
+ void *ws;
+ gf_io_t ipc,opc;
+
+ if(width<10)
+ width = 10;
+
+ result = so_get(CharStar, NULL, EDIT_ACCESS);
+ ws = gf_wrap_filter_opt(width, width, NULL, 0, 0);
+
+ gf_filter_init();
+ gf_link_filter(gf_wrap, ws);
+
+ gf_set_so_writec(&opc, result);
+ gf_set_so_readc(&ipc, in);
+
+ gf_pipe(ipc, opc);
+
+ gf_clear_so_readc(in);
+ gf_clear_so_writec(result);
+
+ return result;
+}
+
+
+void
+smime_config_screen(struct pine *ps, int edit_exceptions)
+{
+ CONF_S *ctmp = NULL, *first_line = NULL;
+ SAVED_CONFIG_S *vsave;
+ OPT_SCREEN_S screen;
+ int ew, readonly_warning = 0;
+
+ dprint((9, "smime_config_screen()"));
+ ps->next_screen = SCREEN_FUN_NULL;
+
+ if(ps->fix_fixed_warning)
+ offer_to_fix_pinerc(ps);
+
+ ew = edit_exceptions ? ps_global->ew_for_except_vars : Main;
+
+ if(ps->restricted)
+ readonly_warning = 1;
+ else{
+ PINERC_S *prc = NULL;
+
+ switch(ew){
+ case Main:
+ prc = ps->prc;
+ break;
+ case Post:
+ prc = ps->post_prc;
+ break;
+ default:
+ break;
+ }
+
+ readonly_warning = prc ? prc->readonly : 1;
+ if(prc && prc->quit_to_edit){
+ quit_to_edit_msg(prc);
+ return;
+ }
+ }
+
+ smime_config_init_display(ps, &ctmp, &first_line);
+
+ vsave = save_smime_config_vars(ps);
+
+ memset(&screen, 0, sizeof(screen));
+ screen.deferred_ro_warning = readonly_warning;
+ switch(conf_scroll_screen(ps, &screen, first_line,
+ edit_exceptions ? _("SETUP S/MIME EXCEPTIONS")
+ : _("SETUP S/MIME"),
+ /* TRANSLATORS: Print something1 using something2.
+ configuration is something1 */
+ _("configuration"), 0)){
+ case 0:
+ break;
+
+ case 1:
+ write_pinerc(ps, ew, WRP_NONE);
+ break;
+
+ case 10:
+ revert_to_saved_smime_config(ps, vsave);
+ break;
+
+ default:
+ q_status_message(SM_ORDER, 7, 10,
+ _("conf_scroll_screen bad ret in smime_config"));
+ break;
+ }
+
+ free_saved_smime_config(ps, &vsave);
+ smime_deinit();
+}
+
+
+int
+smime_related_var(struct pine *ps, struct variable *var)
+{
+ return(var == &ps->vars[V_PUBLICCERT_DIR] ||
+ var == &ps->vars[V_PUBLICCERT_CONTAINER] ||
+ var == &ps->vars[V_PRIVATEKEY_DIR] ||
+ var == &ps->vars[V_PRIVATEKEY_CONTAINER] ||
+ var == &ps->vars[V_CACERT_DIR] ||
+ var == &ps->vars[V_CACERT_CONTAINER]);
+}
+
+void
+smime_config_init_display(struct pine *ps, CONF_S **ctmp, CONF_S **first_line)
+{
+ char tmp[200];
+ int i, ind, ln = 0;
+ struct variable *vtmp;
+ CONF_S *ctmpb;
+ FEATURE_S *feature;
+
+ /* find longest variable name */
+ for(vtmp = ps->vars; vtmp->name; vtmp++){
+ if(!(smime_related_var(ps, vtmp)))
+ continue;
+
+ if((i = utf8_width(pretty_var_name(vtmp->name))) > ln)
+ ln = i;
+ }
+
+ for(vtmp = ps->vars; vtmp->name; vtmp++){
+ if(!(smime_related_var(ps, vtmp)))
+ continue;
+
+ new_confline(ctmp)->var = vtmp;
+ if(first_line && !*first_line)
+ *first_line = *ctmp;
+
+ (*ctmp)->valoffset = ln+3;
+ (*ctmp)->keymenu = &config_text_keymenu;
+ (*ctmp)->help = config_help(vtmp - ps->vars, 0);
+ (*ctmp)->tool = text_tool;
+
+ utf8_snprintf(tmp, sizeof(tmp), "%-*.100w =", ln, pretty_var_name(vtmp->name));
+ tmp[sizeof(tmp)-1] = '\0';
+
+ (*ctmp)->varname = cpystr(tmp);
+ (*ctmp)->varnamep = (*ctmp);
+ (*ctmp)->flags = CF_STARTITEM;
+ (*ctmp)->value = pretty_value(ps, *ctmp);
+ }
+
+
+ vtmp = &ps->vars[V_FEATURE_LIST];
+
+ new_confline(ctmp);
+ ctmpb = (*ctmp);
+ (*ctmp)->flags |= CF_NOSELECT | CF_STARTITEM;
+ (*ctmp)->keymenu = &config_checkbox_keymenu;
+ (*ctmp)->tool = NULL;
+
+ /* put a nice delimiter before list */
+ new_confline(ctmp)->var = NULL;
+ (*ctmp)->varnamep = ctmpb;
+ (*ctmp)->keymenu = &config_checkbox_keymenu;
+ (*ctmp)->help = NO_HELP;
+ (*ctmp)->tool = checkbox_tool;
+ (*ctmp)->valoffset = feature_indent();
+ (*ctmp)->flags |= CF_NOSELECT;
+ (*ctmp)->value = cpystr("Set Feature Name");
+
+ new_confline(ctmp)->var = NULL;
+ (*ctmp)->varnamep = ctmpb;
+ (*ctmp)->keymenu = &config_checkbox_keymenu;
+ (*ctmp)->help = NO_HELP;
+ (*ctmp)->tool = checkbox_tool;
+ (*ctmp)->valoffset = feature_indent();
+ (*ctmp)->flags |= CF_NOSELECT;
+ (*ctmp)->value = cpystr("--- ----------------------");
+
+ ind = feature_list_index(F_DONT_DO_SMIME);
+ feature = feature_list(ind);
+ new_confline(ctmp)->var = vtmp;
+ (*ctmp)->varnamep = ctmpb;
+ (*ctmp)->keymenu = &config_checkbox_keymenu;
+ (*ctmp)->help = config_help(vtmp-ps->vars, feature->id);
+ (*ctmp)->tool = checkbox_tool;
+ (*ctmp)->valoffset = feature_indent();
+ (*ctmp)->varmem = ind;
+ (*ctmp)->value = pretty_value(ps, (*ctmp));
+
+ ind = feature_list_index(F_ENCRYPT_DEFAULT_ON);
+ feature = feature_list(ind);
+ new_confline(ctmp)->var = vtmp;
+ (*ctmp)->varnamep = ctmpb;
+ (*ctmp)->keymenu = &config_checkbox_keymenu;
+ (*ctmp)->help = config_help(vtmp-ps->vars, feature->id);
+ (*ctmp)->tool = checkbox_tool;
+ (*ctmp)->valoffset = feature_indent();
+ (*ctmp)->varmem = ind;
+ (*ctmp)->value = pretty_value(ps, (*ctmp));
+
+ ind = feature_list_index(F_REMEMBER_SMIME_PASSPHRASE);
+ feature = feature_list(ind);
+ new_confline(ctmp)->var = vtmp;
+ (*ctmp)->varnamep = ctmpb;
+ (*ctmp)->keymenu = &config_checkbox_keymenu;
+ (*ctmp)->help = config_help(vtmp-ps->vars, feature->id);
+ (*ctmp)->tool = checkbox_tool;
+ (*ctmp)->valoffset = feature_indent();
+ (*ctmp)->varmem = ind;
+ (*ctmp)->value = pretty_value(ps, (*ctmp));
+
+ ind = feature_list_index(F_SIGN_DEFAULT_ON);
+ feature = feature_list(ind);
+ new_confline(ctmp)->var = vtmp;
+ (*ctmp)->varnamep = ctmpb;
+ (*ctmp)->keymenu = &config_checkbox_keymenu;
+ (*ctmp)->help = config_help(vtmp-ps->vars, feature->id);
+ (*ctmp)->tool = checkbox_tool;
+ (*ctmp)->valoffset = feature_indent();
+ (*ctmp)->varmem = ind;
+ (*ctmp)->value = pretty_value(ps, (*ctmp));
+
+#ifdef APPLEKEYCHAIN
+ new_confline(ctmp);
+ (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
+
+ new_confline(ctmp);
+ (*ctmp)->flags |= CF_NOSELECT;
+ (*ctmp)->value = cpystr(_("Mac OS X specific features"));
+
+ ind = feature_list_index(F_PUBLICCERTS_IN_KEYCHAIN);
+ feature = feature_list(ind);
+ new_confline(ctmp)->var = vtmp;
+ (*ctmp)->varnamep = ctmpb;
+ (*ctmp)->keymenu = &config_checkbox_keymenu;
+ (*ctmp)->help = config_help(vtmp-ps->vars, feature->id);
+ (*ctmp)->tool = checkbox_tool;
+ (*ctmp)->valoffset = feature_indent();
+ (*ctmp)->varmem = ind;
+ (*ctmp)->value = pretty_value(ps, (*ctmp));
+#endif /* APPLEKEYCHAIN */
+
+ new_confline(ctmp);
+ (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
+
+ new_confline(ctmp);
+ (*ctmp)->flags |= CF_NOSELECT;
+ (*ctmp)->value = cpystr("---------------------------------------------------------------------------");
+
+ new_confline(ctmp);
+ (*ctmp)->flags |= CF_NOSELECT;
+ (*ctmp)->value = cpystr(_("Be careful with the following commands, they REPLACE contents in the target"));
+
+ new_confline(ctmp);
+ (*ctmp)->flags |= CF_NOSELECT;
+ (*ctmp)->value = cpystr("---------------------------------------------------------------------------");
+
+ new_confline(ctmp);
+ (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
+
+ /* copy public directory to container */
+ new_confline(ctmp);
+ (*ctmp)->tool = smime_helper_tool;
+ (*ctmp)->keymenu = &config_smime_helper_keymenu;
+ (*ctmp)->help = h_config_smime_transfer_pub_to_con;
+ (*ctmp)->value = cpystr(_("Transfer public certs FROM directory TO container"));
+ (*ctmp)->varmem = 1;
+
+ /* copy private directory to container */
+ new_confline(ctmp);
+ (*ctmp)->tool = smime_helper_tool;
+ (*ctmp)->keymenu = &config_smime_helper_keymenu;
+ (*ctmp)->help = h_config_smime_transfer_priv_to_con;
+ (*ctmp)->value = cpystr(_("Transfer private keys FROM directory TO container"));
+ (*ctmp)->varmem = 3;
+
+ /* copy cacert directory to container */
+ new_confline(ctmp);
+ (*ctmp)->tool = smime_helper_tool;
+ (*ctmp)->keymenu = &config_smime_helper_keymenu;
+ (*ctmp)->help = h_config_smime_transfer_cacert_to_con;
+ (*ctmp)->value = cpystr(_("Transfer CA certs FROM directory TO container"));
+ (*ctmp)->varmem = 5;
+
+ new_confline(ctmp)->var = vtmp;
+ (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
+
+ /* copy public container to directory */
+ new_confline(ctmp);
+ (*ctmp)->tool = smime_helper_tool;
+ (*ctmp)->keymenu = &config_smime_helper_keymenu;
+ (*ctmp)->help = h_config_smime_transfer_pub_to_dir;
+ (*ctmp)->value = cpystr(_("Transfer public certs FROM container TO directory"));
+ (*ctmp)->varmem = 2;
+
+ /* copy private container to directory */
+ new_confline(ctmp);
+ (*ctmp)->tool = smime_helper_tool;
+ (*ctmp)->keymenu = &config_smime_helper_keymenu;
+ (*ctmp)->help = h_config_smime_transfer_priv_to_dir;
+ (*ctmp)->value = cpystr(_("Transfer private keys FROM container TO directory"));
+ (*ctmp)->varmem = 4;
+
+ /* copy cacert container to directory */
+ new_confline(ctmp);
+ (*ctmp)->tool = smime_helper_tool;
+ (*ctmp)->keymenu = &config_smime_helper_keymenu;
+ (*ctmp)->help = h_config_smime_transfer_cacert_to_dir;
+ (*ctmp)->value = cpystr(_("Transfer CA certs FROM container TO directory"));
+ (*ctmp)->varmem = 6;
+
+#ifdef APPLEKEYCHAIN
+
+ new_confline(ctmp)->var = vtmp;
+ (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
+
+ /* copy public container to keychain */
+ new_confline(ctmp);
+ (*ctmp)->tool = smime_helper_tool;
+ (*ctmp)->keymenu = &config_smime_helper_keymenu;
+ (*ctmp)->help = h_config_smime_transfer_pubcon_to_key;
+ (*ctmp)->value = cpystr(_("Transfer public certs FROM container TO keychain"));
+ (*ctmp)->varmem = 7;
+
+ /* copy public keychain to container */
+ new_confline(ctmp);
+ (*ctmp)->tool = smime_helper_tool;
+ (*ctmp)->keymenu = &config_smime_helper_keymenu;
+ (*ctmp)->help = h_config_smime_transfer_pubkey_to_con;
+ (*ctmp)->value = cpystr(_("Transfer public certs FROM keychain TO container"));
+ (*ctmp)->varmem = 8;
+
+#endif /* APPLEKEYCHAIN */
+}
+
+
+int
+smime_helper_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned flags)
+{
+ int rv = 0;
+
+ switch(cmd){
+ case MC_CHOICE:
+ switch((*cl)->varmem){
+ case 1:
+ rv = copy_publiccert_dir_to_container();
+ if(rv == 0)
+ q_status_message(SM_ORDER, 1, 3, _("Public certs transferred to container"));
+ else{
+ q_status_message(SM_ORDER, 3, 3, _("Problem transferring certs"));
+ rv = 0;
+ }
+
+ break;
+
+ case 2:
+ rv = copy_publiccert_container_to_dir();
+ if(rv == 0)
+ q_status_message(SM_ORDER, 1, 3, _("Public certs transferred to directory, delete Container config to use"));
+ else{
+ q_status_message(SM_ORDER, 3, 3, _("Problem transferring certs"));
+ rv = 0;
+ }
+
+ break;
+
+ case 3:
+ rv = copy_privatecert_dir_to_container();
+ if(rv == 0)
+ q_status_message(SM_ORDER, 1, 3, _("Private keys transferred to container"));
+ else{
+ q_status_message(SM_ORDER, 3, 3, _("Problem transferring certs"));
+ rv = 0;
+ }
+
+ break;
+
+ case 4:
+ rv = copy_privatecert_container_to_dir();
+ if(rv == 0)
+ q_status_message(SM_ORDER, 1, 3, _("Private keys transferred to directory, delete Container config to use"));
+ else{
+ q_status_message(SM_ORDER, 3, 3, _("Problem transferring certs"));
+ rv = 0;
+ }
+
+ break;
+
+ case 5:
+ rv = copy_cacert_dir_to_container();
+ if(rv == 0)
+ q_status_message(SM_ORDER, 1, 3, _("CA certs transferred to container"));
+ else{
+ q_status_message(SM_ORDER, 3, 3, _("Problem transferring certs"));
+ rv = 0;
+ }
+
+ break;
+
+ case 6:
+ rv = copy_cacert_container_to_dir();
+ if(rv == 0)
+ q_status_message(SM_ORDER, 1, 3, _("CA certs transferred to directory, delete Container config to use"));
+ else{
+ q_status_message(SM_ORDER, 3, 3, _("Problem transferring certs"));
+ rv = 0;
+ }
+
+ break;
+
+#ifdef APPLEKEYCHAIN
+ case 7:
+ rv = copy_publiccert_container_to_keychain();
+ if(rv == 0)
+ q_status_message(SM_ORDER, 1, 3, _("Public certs transferred to keychain"));
+ else{
+ q_status_message(SM_ORDER, 3, 3, _("Command not implemented yet"));
+ rv = 0;
+ }
+
+ break;
+
+ case 8:
+ rv = copy_publiccert_keychain_to_container();
+ if(rv == 0)
+ q_status_message(SM_ORDER, 1, 3, _("Public certs transferred to container"));
+ else{
+ q_status_message(SM_ORDER, 3, 3, _("Command not implemented yet"));
+ rv = 0;
+ }
+
+ break;
+#endif /* APPLEKEYCHAIN */
+
+ default:
+ rv = -1;
+ break;
+ }
+
+ break;
+
+ case MC_EXIT:
+ rv = config_exit_cmd(flags);
+ break;
+
+ default:
+ rv = -1;
+ break;
+ }
+
+ return rv;
+}
+
+
+/*
+ * Compare saved user_val with current user_val to see if it changed.
+ * If any have changed, change it back and take the appropriate action.
+ */
+void
+revert_to_saved_smime_config(struct pine *ps, SAVED_CONFIG_S *vsave)
+{
+ struct variable *vreal;
+ SAVED_CONFIG_S *v;
+ int i, n;
+ int changed = 0;
+ char *pval, **apval, **lval, ***alval;
+
+ v = vsave;
+ for(vreal = ps->vars; vreal->name; vreal++,v++){
+ if(!(smime_related_var(ps, vreal) || vreal==&ps->vars[V_FEATURE_LIST]))
+ continue;
+
+ if(vreal->is_list){
+ lval = LVAL(vreal, ew);
+ alval = ALVAL(vreal, ew);
+
+ if((v->saved_user_val.l && !lval)
+ || (!v->saved_user_val.l && lval))
+ changed++;
+ else if(!v->saved_user_val.l && !lval)
+ ;/* no change, nothing to do */
+ else
+ for(i = 0; v->saved_user_val.l[i] || lval[i]; i++)
+ if((v->saved_user_val.l[i]
+ && (!lval[i]
+ || strcmp(v->saved_user_val.l[i], lval[i])))
+ ||
+ (!v->saved_user_val.l[i] && lval[i])){
+ changed++;
+ break;
+ }
+
+ if(changed){
+ char **list;
+
+ if(alval){
+ if(*alval)
+ free_list_array(alval);
+
+ /* copy back the original one */
+ if(v->saved_user_val.l){
+ list = v->saved_user_val.l;
+ n = 0;
+ /* count how many */
+ while(list[n])
+ n++;
+
+ *alval = (char **)fs_get((n+1) * sizeof(char *));
+
+ for(i = 0; i < n; i++)
+ (*alval)[i] = cpystr(v->saved_user_val.l[i]);
+
+ (*alval)[n] = NULL;
+ }
+ }
+ }
+ }
+ else{
+ pval = PVAL(vreal, ew);
+ apval = APVAL(vreal, ew);
+
+ if((v->saved_user_val.p &&
+ (!pval || strcmp(v->saved_user_val.p, pval))) ||
+ (!v->saved_user_val.p && pval)){
+ /* It changed, fix it */
+ changed++;
+ if(apval){
+ /* free the changed value */
+ if(*apval)
+ fs_give((void **)apval);
+
+ if(v->saved_user_val.p)
+ *apval = cpystr(v->saved_user_val.p);
+ }
+ }
+ }
+
+ if(changed){
+ if(vreal == &ps->vars[V_FEATURE_LIST])
+ set_feature_list_current_val(vreal);
+ else
+ set_current_val(vreal, TRUE, FALSE);
+
+ fix_side_effects(ps, vreal, 1);
+ }
+ }
+}
+
+
+SAVED_CONFIG_S *
+save_smime_config_vars(struct pine *ps)
+{
+ struct variable *vreal;
+ SAVED_CONFIG_S *vsave, *v;
+
+ vsave = (SAVED_CONFIG_S *)fs_get((V_LAST_VAR+1)*sizeof(SAVED_CONFIG_S));
+ memset((void *)vsave, 0, (V_LAST_VAR+1)*sizeof(SAVED_CONFIG_S));
+ v = vsave;
+ for(vreal = ps->vars; vreal->name; vreal++,v++){
+ if(!(smime_related_var(ps, vreal) || vreal==&ps->vars[V_FEATURE_LIST]))
+ continue;
+
+ if(vreal->is_list){
+ int n, i;
+ char **list;
+
+ if(LVAL(vreal, ew)){
+ /* count how many */
+ n = 0;
+ list = LVAL(vreal, ew);
+ while(list[n])
+ n++;
+
+ v->saved_user_val.l = (char **)fs_get((n+1) * sizeof(char *));
+ memset((void *)v->saved_user_val.l, 0, (n+1)*sizeof(char *));
+ for(i = 0; i < n; i++)
+ v->saved_user_val.l[i] = cpystr(list[i]);
+
+ v->saved_user_val.l[n] = NULL;
+ }
+ }
+ else{
+ if(PVAL(vreal, ew))
+ v->saved_user_val.p = cpystr(PVAL(vreal, ew));
+ }
+ }
+
+ return(vsave);
+}
+
+
+void
+free_saved_smime_config(struct pine *ps, SAVED_CONFIG_S **vsavep)
+{
+ struct variable *vreal;
+ SAVED_CONFIG_S *v;
+
+ if(vsavep && *vsavep){
+ for(v = *vsavep, vreal = ps->vars; vreal->name; vreal++,v++){
+ if(!(smime_related_var(ps, vreal)))
+ continue;
+
+ if(vreal->is_list){ /* free saved_user_val.l */
+ if(v && v->saved_user_val.l)
+ free_list_array(&v->saved_user_val.l);
+ }
+ else if(v && v->saved_user_val.p)
+ fs_give((void **)&v->saved_user_val.p);
+ }
+
+ fs_give((void **)vsavep);
+ }
+}
+
+#endif /* SMIME */
diff --git a/alpine/smime.h b/alpine/smime.h
new file mode 100644
index 00000000..048a497c
--- /dev/null
+++ b/alpine/smime.h
@@ -0,0 +1,34 @@
+/*
+ * $Id: smime.h 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifdef SMIME
+#ifndef PINE_SMIME_INCLUDED
+#define PINE_SMIME_INCLUDED
+
+
+#include "../pith/state.h"
+#include "../pith/send.h"
+#include "../pith/smime.h"
+
+
+/* exported protoypes */
+int smime_get_passphrase(void);
+void smime_info_screen(struct pine *ps);
+void smime_config_screen(struct pine *, int edit_exceptions);
+int smime_related_var(struct pine *, struct variable *);
+
+
+#endif /* PINE_SMIME_INCLUDED */
+#endif /* SMIME */
diff --git a/alpine/status.c b/alpine/status.c
new file mode 100644
index 00000000..b51c640f
--- /dev/null
+++ b/alpine/status.c
@@ -0,0 +1,1282 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: status.c 840 2007-12-01 01:34:49Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ status.c
+ Functions that manage the status line (third from the bottom)
+ - put messages on the queue to be displayed
+ - display messages on the queue with timers
+ - check queue to figure out next timeout
+ - prompt for yes/no type of questions
+ ====*/
+
+#include "headers.h"
+#include "status.h"
+#include "keymenu.h"
+#include "mailview.h"
+#include "mailcmd.h"
+#include "busy.h"
+#include "after.h"
+
+#include "../pith/charconv/utf8.h"
+
+#include "../pith/conf.h"
+#include "../pith/bitmap.h"
+
+
+/*
+ * Internal queue of messages. The circular, double-linked list's
+ * allocated on demand, and cleared as each message is displayed.
+ */
+typedef struct message {
+ char *text;
+ unsigned flags:8;
+ unsigned shown:1;
+ unsigned saw_it:1;
+ unsigned pending_removal:1;
+ int min_display_time, max_display_time;
+ struct message *next, *prev;
+} SMQ_T;
+
+
+/*
+ * Internal prototypes
+ */
+void pause_for_and_dq_cur_msg(void);
+SMQ_T *top_of_queue(void);
+int new_info_msg_need_not_be_queued(void);
+int is_last_message(SMQ_T *);
+void d_q_status_message(void);
+int output_message(SMQ_T *, int);
+int output_message_modal(SMQ_T *, int);
+void delay_cmd_cue(int);
+int modal_bogus_input(UCS);
+int messages_in_queue(void);
+int status_message_remaining_nolock(void);
+void set_saw_it_to_zero();
+void mark_modals_done();
+SMQ_T *copy_status_queue(SMQ_T *);
+
+
+
+/*----------------------------------------------------------------------
+ Manage the second line from the bottom where status and error messages
+are displayed. A small queue is set up and messages are put on the queue
+by calling one of the q_status_message routines. Even though this is a queue
+most of the time message will go right on through. The messages are
+displayed just before the read for the next command, or when a read times
+out. Read timeouts occur every minute or so for new mail checking and every
+few seconds when there are still messages on the queue. Hopefully, this scheme
+will not let messages fly past that the user can't see.
+ ----------------------------------------------------------------------*/
+
+
+static SMQ_T *message_queue = NULL;
+static short needs_clearing = 0, /* Flag set by want_to()
+ and optionally_enter() */
+ prevstartcol, prevendcol;
+static char prevstatusbuf[6*MAX_SCREEN_COLS+1];
+static time_t displayed_time;
+
+
+/*----------------------------------------------------------------------
+ Put a message for the status line on the queue
+
+ Args: time -- the min time in seconds to display the message
+ message -- message string
+
+ Result: queues message on queue represented by static variables
+
+ This puts a single message on the queue to be shown.
+ ----------*/
+void
+q_status_message(int flags, int min_time, int max_time, char *message)
+{
+ SMQ_T *new, *q;
+ char *clean_msg;
+ size_t mlen;
+
+ status_message_lock();
+
+ /*
+ * If there is already a message queued and
+ * new message is just informational, discard it.
+ */
+ if(flags & SM_INFO && new_info_msg_need_not_be_queued()){
+ status_message_unlock();
+ return;
+ }
+
+ /*
+ * By convention, we have min_time equal to zero in messages which we
+ * think are not as important, so-called comfort messages. We have
+ * min_time >= 3 for messages which we think the user should see for
+ * sure. Some users don't like to wait so we've provided a way for them
+ * to live on the edge.
+ * status_msg_delay == -1 => min time == MIN(0, min_time)
+ * status_msg_delay == -2 => min time == MIN(1, min_time)
+ * status_msg_delay == -3 => min time == MIN(2, min_time)
+ * ...
+ */
+ if(ps_global->status_msg_delay < 0)
+ min_time = MIN(-1 - ps_global->status_msg_delay, min_time);
+
+ /* The 40 is room for 40 escaped control characters */
+ mlen = strlen(message) + 40;
+ clean_msg = (char *)fs_get(mlen + 1);
+ iutf8ncpy(clean_msg, message, mlen); /* does the cleaning */
+
+ clean_msg[mlen] = '\0';
+
+ if((q = message_queue) != NULL){ /* the queue exists */
+
+ /*
+ * Scan through all of the messages currently in the queue.
+ * If the new message is already queued, don't add it again.
+ */
+ do {
+ if(!q->pending_removal && q->text && !strcmp(q->text, clean_msg)){
+ q->shown = 0;
+ if(q->min_display_time < min_time)
+ q->min_display_time = min_time;
+
+ if(q->max_display_time < max_time)
+ q->max_display_time = max_time;
+
+ dprint((9, "q_status_message(%s): skipping duplicate msg\n",
+ clean_msg ? clean_msg : "?"));
+
+ if(clean_msg)
+ fs_give((void **)&clean_msg);
+
+ status_message_unlock();
+ return;
+ }
+
+ q = q->next;
+
+ } while(q != message_queue);
+ }
+
+ new = (SMQ_T *)fs_get(sizeof(SMQ_T));
+ memset(new, 0, sizeof(SMQ_T));
+ new->text = clean_msg;
+ new->min_display_time = min_time;
+ new->max_display_time = max_time;
+ new->flags = flags;
+ if(message_queue){
+ new->next = message_queue;
+ new->prev = message_queue->prev;
+ new->prev->next = message_queue->prev = new;
+ }
+ else
+ message_queue = new->next = new->prev = new;
+
+ status_message_unlock();
+
+ dprint((9, "q_status_message(%s)\n",
+ clean_msg ? clean_msg : "?"));
+}
+
+
+/*----------------------------------------------------------------------
+ Mark the status line as dirty so it gets cleared next chance
+ ----*/
+void
+mark_status_dirty(void)
+{
+ mark_status_unknown();
+ needs_clearing++;
+}
+
+
+/*----------------------------------------------------------------------
+ Cause status line drawing optimization to be turned off, because we
+ don't know what the status line looks like.
+ ----*/
+void
+mark_status_unknown(void)
+{
+ prevstartcol = -1;
+ prevendcol = -1;
+ prevstatusbuf[0] = '\0';
+}
+
+
+/*----------------------------------------------------------------------
+ Wait a suitable amount of time for the currently displayed message
+ ----*/
+void
+pause_for_and_dq_cur_msg(void)
+{
+ if(top_of_queue()){
+ int w;
+
+ if((w = status_message_remaining_nolock()) != 0){
+ delay_cmd_cue(1);
+ sleep(w);
+ delay_cmd_cue(0);
+ }
+
+ d_q_status_message();
+ }
+}
+
+
+/*
+ * This relies on the global displayed_time being properly set
+ * for this message.
+ */
+void
+pause_for_and_mark_specific_msg(SMQ_T *msg)
+{
+ if(msg){
+ int w;
+
+ w = (int) (displayed_time - time(0)) + msg->min_display_time;
+ w = (w > 0) ? w : 0;
+ if(w){
+ delay_cmd_cue(1);
+ sleep(w);
+ delay_cmd_cue(0);
+ }
+
+ msg->pending_removal = 1;
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Time remaining for current message's minimum display
+ ----*/
+int
+status_message_remaining(void)
+{
+ int ret;
+
+ status_message_lock();
+ ret = status_message_remaining_nolock();
+ status_message_unlock();
+
+ return(ret);
+}
+
+
+int
+status_message_remaining_nolock(void)
+{
+ SMQ_T *q;
+ int d = 0;
+
+ if((q = top_of_queue()) != NULL)
+ d = (int) (displayed_time - time(0)) + q->min_display_time;
+
+ return((d > 0) ? d : 0);
+}
+
+
+/*
+ * Return first message in queue that isn't pending_removal.
+ */
+SMQ_T *
+top_of_queue(void)
+{
+ SMQ_T *p;
+
+ if((p = message_queue) != NULL){
+ do
+ if(!p->pending_removal)
+ return(p);
+ while((p = p->next) != message_queue);
+ }
+
+ return(NULL);
+}
+
+
+int
+new_info_msg_need_not_be_queued(void)
+{
+ SMQ_T *q;
+
+ if(status_message_remaining_nolock() > 0)
+ return 1;
+
+ if((q = top_of_queue()) != NULL && (q = q->next) != message_queue){
+ do
+ if(!q->pending_removal && !(q->flags & SM_INFO))
+ return 1;
+ while((q = q->next) != message_queue);
+ }
+
+ return 0;
+}
+
+
+/*----------------------------------------------------------------------
+ Find out how many messages are queued for display
+
+ Args: dtime -- will get set to minimum display time for current message
+
+ Result: number of messages in the queue.
+
+ ---------*/
+int
+messages_queued(long int *dtime)
+{
+ SMQ_T *q;
+ int ret;
+
+ status_message_lock();
+ if(dtime && (q = top_of_queue()) != NULL)
+ *dtime = (long) MAX(q->min_display_time, 1L);
+
+ ret = ps_global->in_init_seq ? 0 : messages_in_queue();
+
+ status_message_unlock();
+
+ return(ret);
+}
+
+
+/*----------------------------------------------------------------------
+ Return number of messages in queue
+ ---------*/
+int
+messages_in_queue(void)
+{
+ int n = 0;
+ SMQ_T *p;
+
+ if((p = message_queue) != NULL){
+ do
+ if(!p->pending_removal)
+ n++;
+ while((p = p->next) != message_queue);
+ }
+
+ return(n);
+}
+
+
+/*----------------------------------------------------------------------
+ Return last message queued
+ ---------*/
+char *
+last_message_queued(void)
+{
+ SMQ_T *p, *r = NULL;
+ char *ret = NULL;
+
+ status_message_lock();
+ if((p = message_queue) != NULL){
+ do
+ if(p->flags & SM_ORDER && !p->pending_removal)
+ r = p;
+ while((p = p->next) != message_queue);
+ }
+
+ ret = (r && r->text) ? cpystr(r->text) : NULL;
+
+ status_message_unlock();
+
+ return(ret);
+}
+
+
+int
+is_last_message(SMQ_T *msg)
+{
+ SMQ_T *p, *r = NULL;
+
+ if(msg && !msg->pending_removal){
+ if((p = message_queue) != NULL){
+ do
+ if(!p->pending_removal)
+ r = p;
+ while((p = p->next) != message_queue);
+ }
+ }
+
+ return(r && msg && (r == msg));
+}
+
+
+/*----------------------------------------------------------------------
+ Update status line, clearing or displaying a message
+
+ Arg: command -- The command that is about to be executed
+
+ Result: status line cleared or
+ next message queued is displayed or
+ current message is redisplayed.
+ if next message displayed, it's min display time
+ is returned else if message already displayed, it's
+ time remaining on the display is returned, else 0.
+
+ This is called when ready to display the next message, usually just
+before reading the next command from the user. We pass in the nature
+of the command because it affects what we do here. If the command just
+executed by the user is a redraw screen, we don't want to reset or go to
+next message because it might not have been seen. Also if the command
+is just a noop, which are usually executed when checking for new mail
+and happen every few minutes, we don't clear the message.
+
+If it was really a command and there's nothing more to show, then we
+clear, because we know the user has seen the message. In this case the
+user might be typing commands very quickly and miss a message, so
+there is a time stamp and time check that each message has been on the
+screen for a few seconds. If it hasn't we just return and let it be
+taken care of next time.
+----------------------------------------------------------------------*/
+int
+display_message(UCS command)
+{
+ SMQ_T *q, *copy_of_q;
+ int need_to_unlock;
+ int ding;
+
+ if(ps_global == NULL || ps_global->ttyo == NULL
+ || ps_global->ttyo->screen_rows < 1 || ps_global->in_init_seq)
+ return(0);
+
+ status_message_lock();
+
+ /*---- Deal with any previously displayed message ----*/
+ if((q = top_of_queue()) != NULL && q->shown){
+ int rv = -1;
+
+ if(command == ctrl('L')){ /* just repaint it, and go on */
+ mark_status_unknown();
+ mark_keymenu_dirty();
+ mark_titlebar_dirty();
+ rv = 0;
+ }
+ else{ /* ensure sufficient time's passed */
+ time_t now;
+ int diff;
+
+ now = time(0);
+ diff = (int)(displayed_time - now)
+ + ((command == NO_OP_COMMAND || command == NO_OP_IDLE)
+ ? q->max_display_time
+ : q->min_display_time);
+ dprint((9,
+ "STATUS: diff:%d, displayed: %ld, now: %ld\n",
+ diff, (long) displayed_time, (long) now));
+ if(diff > 0)
+ rv = diff; /* check again next time */
+ else if(is_last_message(q)
+ && (command == NO_OP_COMMAND || command == NO_OP_IDLE)
+ && q->max_display_time)
+ rv = 0; /* last msg, no cmd, has max */
+ }
+
+ if(rv >= 0){ /* leave message displayed? */
+ if(prevstartcol < 0){ /* need to redisplay it? */
+ ding = q->flags & SM_DING;
+ q->flags &= ~SM_DING;
+ if(q->flags & SM_MODAL && !q->shown){
+ copy_of_q = copy_status_queue(q);
+ mark_modals_done();
+ status_message_unlock();
+ output_message_modal(copy_of_q, ding);
+ }
+ else{
+ output_message(q, ding);
+ status_message_unlock();
+ }
+ }
+ else
+ status_message_unlock();
+
+ return(rv);
+ }
+
+ d_q_status_message(); /* remove it from queue and */
+ needs_clearing++; /* clear the line if needed */
+ }
+
+ if(!top_of_queue() && (command == ctrl('L') || needs_clearing)){
+ int inverse;
+ struct variable *vars = ps_global->vars;
+ char *last_bg = NULL;
+
+ dprint((9, "Clearing status line\n"));
+ inverse = InverseState(); /* already in inverse? */
+ if(inverse && pico_usingcolor() && VAR_STATUS_FORE_COLOR &&
+ VAR_STATUS_BACK_COLOR){
+ last_bg = pico_get_last_bg_color();
+ pico_set_nbg_color(); /* so ClearLine will clear in bg color */
+ }
+
+ ClearLine(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global));
+ if(last_bg){
+ (void)pico_set_bg_color(last_bg);
+ if(last_bg)
+ fs_give((void **)&last_bg);
+ }
+
+ mark_status_unknown();
+ if(command == ctrl('L')){
+ mark_keymenu_dirty();
+ mark_titlebar_dirty();
+ }
+ }
+
+ need_to_unlock = 1;
+
+ /* display next message, weeding out 0 display times */
+ for(q = top_of_queue(); q && !q->shown; q = top_of_queue()){
+ if(q->min_display_time || is_last_message(q)){
+ displayed_time = time(0);
+ ding = q->flags & SM_DING;
+ q->flags &= ~SM_DING;
+ if(q->flags & SM_MODAL && !q->shown){
+ copy_of_q = copy_status_queue(q);
+ mark_modals_done();
+ status_message_unlock();
+ output_message_modal(copy_of_q, ding);
+ need_to_unlock = 0;
+ }
+ else
+ output_message(q, ding);
+
+ break;
+ }
+ /* zero display time message, log it, delete it */
+ else{
+ if(q->text){
+ char buf[1000];
+ char *append = " [not actually shown]";
+ char *ptr;
+ size_t len;
+
+ len = strlen(q->text) + strlen(append);
+ if(len < sizeof(buf))
+ ptr = buf;
+ else
+ ptr = (char *) fs_get((len+1) * sizeof(char));
+
+ strncpy(ptr, q->text, len);
+ ptr[len] = '\0';
+ strncat(ptr, append, len+1-1-strlen(ptr));
+ ptr[len] = '\0';
+ add_review_message(ptr, -1);
+ if(ptr != buf)
+ fs_give((void **) &ptr);
+ }
+
+ d_q_status_message();
+ }
+ }
+
+ needs_clearing = 0; /* always cleared or written */
+ fflush(stdout);
+
+ if(need_to_unlock)
+ status_message_unlock();
+
+ return(0);
+}
+
+
+/*----------------------------------------------------------------------
+ Display all the messages on the queue as quickly as possible
+ ----*/
+void
+flush_status_messages(int skip_last_pause)
+{
+ SMQ_T *q, *copy_of_q;
+ int ding;
+
+start_over:
+ status_message_lock();
+
+ for(q = top_of_queue(); q; q = top_of_queue()){
+ /* don't have to wait for this one */
+ if(is_last_message(q) && skip_last_pause && q->shown)
+ break;
+
+ if(q->shown)
+ pause_for_and_dq_cur_msg();
+
+ /* find next one we need to show and show it */
+ for(q = top_of_queue(); q && !q->shown; q = top_of_queue()){
+ if((q->min_display_time || is_last_message(q))){
+ displayed_time = time(0);
+ ding = q->flags & SM_DING;
+ q->flags &= ~SM_DING;
+ if(q->flags & SM_MODAL){
+ copy_of_q = copy_status_queue(q);
+ mark_modals_done();
+ status_message_unlock();
+ output_message_modal(copy_of_q, ding);
+
+ /*
+ * Because we unlock the message queue in order
+ * to display modal messages we have to worry
+ * about the queue being changed while we have
+ * it unlocked. So start the whole process over.
+ */
+ goto start_over;
+ }
+ else
+ output_message(q, ding);
+ }
+ else{
+ d_q_status_message();
+ }
+ }
+ }
+
+ status_message_unlock();
+}
+
+
+/*----------------------------------------------------------------------
+ Make sure any and all SM_ORDER messages get displayed.
+
+ Note: This flags the message line as having nothing displayed.
+ The idea is that it's a function called by routines that want
+ the message line for a prompt or something, and that they're
+ going to obliterate the message anyway.
+ ----*/
+void
+flush_ordered_messages(void)
+{
+ SMQ_T *q, *copy_of_q;
+ int firsttime = 1;
+ int ding;
+
+ status_message_lock();
+
+ set_saw_it_to_zero();
+
+start_over2:
+ if(!firsttime)
+ status_message_lock();
+ else
+ firsttime = 0;
+
+ if((q = message_queue) != NULL){
+ do{
+ if(q->pending_removal || q->saw_it || q->shown){
+ if(!q->pending_removal && q->shown)
+ pause_for_and_mark_specific_msg(q);
+
+ q->saw_it = 1;
+ q = (q->next != message_queue) ? q->next : NULL;
+ }
+ else{
+ /* find next one we need to show and show it */
+ do{
+ if(q->pending_removal || q->saw_it || q->shown){
+ q->saw_it = 1;
+ q = (q->next != message_queue) ? q->next : NULL;
+ }
+ else{
+ if((q->flags & (SM_ORDER | SM_MODAL))
+ && q->min_display_time){
+ displayed_time = time(0);
+ ding = q->flags & SM_DING;
+ q->flags &= ~SM_DING;
+ if(q->flags & SM_MODAL){
+ copy_of_q = copy_status_queue(q);
+ mark_modals_done();
+ q->saw_it = 1;
+ status_message_unlock();
+ output_message_modal(copy_of_q, ding);
+ goto start_over2;
+ }
+ else{
+ output_message(q, ding);
+ }
+ }
+ else{
+ q->saw_it = 1;
+ if(!(q->flags & SM_ASYNC))
+ q->pending_removal = 1;
+
+ q = (q->next != message_queue) ? q->next : NULL;
+ }
+ }
+ }while(q && !q->shown);
+ }
+ }while(q);
+ }
+
+ status_message_unlock();
+}
+
+
+void
+set_saw_it_to_zero()
+{
+ SMQ_T *q;
+
+ /* set saw_it to zero */
+ if((q = message_queue) != NULL){
+ do{
+ q->saw_it = 0;
+ q = (q->next != message_queue) ? q->next : NULL;
+ }while(q);
+ }
+}
+
+
+void
+mark_modals_done()
+{
+ SMQ_T *q;
+
+ /* set shown to one */
+ if((q = message_queue) != NULL){
+ do{
+ if(q->flags & SM_MODAL){
+ q->shown = 1;
+ q->pending_removal = 1;
+ }
+
+ q = (q->next != message_queue) ? q->next : NULL;
+ }while(q);
+ }
+}
+
+
+/*
+ * Caller needs to free the memory.
+ */
+SMQ_T *
+copy_status_queue(SMQ_T *start)
+{
+ SMQ_T *q, *new, *head = NULL;
+
+ if((q = start) != NULL){
+ do{
+ new = (SMQ_T *) fs_get(sizeof(SMQ_T));
+ *new = *q;
+
+ if(q->text)
+ new->text = cpystr(q->text);
+ else
+ new->text = NULL;
+
+ if(head){
+ new->next = head;
+ new->prev = head->prev;
+ new->prev->next = head->prev = new;
+ }
+ else
+ head = new->next = new->prev = new;
+
+ q = (q->next != start) ? q->next : NULL;
+ }while(q);
+ }
+
+ return(head);
+}
+
+
+/*----------------------------------------------------------------------
+ Removes the first message from the message queue.
+ Returns 0 if all was well, -1 if had to skip the removal and
+ message_queue is unchanged.
+ ----*/
+void
+d_q_status_message(void)
+{
+ int the_last_one = 0;
+ SMQ_T *q, *p, *last_one;
+
+ /* mark the top one for removal */
+ if((p = top_of_queue()) != NULL)
+ p->pending_removal = 1;
+
+ if(message_queue){
+
+ /* flush out pending removals */
+ the_last_one = 0; /* loop control */
+ q = message_queue;
+ last_one = message_queue->prev;
+ while(!the_last_one){
+ if(q == last_one)
+ the_last_one++;
+
+ if(q->pending_removal){
+ p = q;
+ if(q == q->next){ /* last item in queue */
+ q = message_queue = NULL;
+ }
+ else{
+ p->next->prev = p->prev;
+ q = p->prev->next = p->next; /* here's the increment */
+ if(message_queue == p)
+ message_queue = q;
+ }
+
+ if(p){
+ if(p->text)
+ fs_give((void **) &p->text);
+
+ fs_give((void **) &p);
+ }
+ }
+ else
+ q = q->next;
+ }
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Actually output the message to the screen
+
+ Args: message -- The message to output
+ from_alarm_handler -- Called from alarm signal handler.
+ We don't want to add this message to the review
+ message list since we may mess with the malloc
+ arena here, and the interrupt may be from
+ the middle of something malloc'ing.
+ ----*/
+int
+status_message_write(char *message, int from_alarm_handler)
+{
+ int col, row, max_width, invert;
+ int bytes;
+ char newstatusbuf[6*MAX_SCREEN_COLS + 1];
+ struct variable *vars = ps_global->vars;
+ COLOR_PAIR *lastc = NULL, *newc;
+
+ if(!from_alarm_handler)
+ add_review_message(message, -1);
+
+ invert = !InverseState(); /* already in inverse? */
+ row = MAX(0, ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global));
+
+ /* Put [] around message and truncate to screen width */
+ max_width = ps_global->ttyo != NULL ? ps_global->ttyo->screen_cols : 80;
+ max_width = MIN(max_width, MAX_SCREEN_COLS);
+ newstatusbuf[0] = '[';
+ newstatusbuf[1] = '\0';
+
+ bytes = utf8_to_width(newstatusbuf+1, message, sizeof(newstatusbuf)-1, max_width-2, NULL);
+ newstatusbuf[1+bytes] = ']';
+ newstatusbuf[1+bytes+1] = '\0';
+
+ if(prevstartcol == -1 || strcmp(newstatusbuf, prevstatusbuf)){
+ UCS *prevbuf = NULL, *newbuf = NULL;
+ size_t plen;
+
+ if(prevstartcol != -1){
+ prevbuf = utf8_to_ucs4_cpystr(prevstatusbuf);
+ newbuf = utf8_to_ucs4_cpystr(newstatusbuf);
+ }
+
+ /*
+ * Simple optimization. If the strings are the same length
+ * and width just skip leading and trailing strings of common
+ * characters and only write whatever's in between. Otherwise,
+ * write out the whole thing.
+ * We could do something more complicated but the odds of
+ * getting any optimization goes way down if they aren't the
+ * same length and width and the complexity goes way up.
+ */
+ if(prevbuf && newbuf
+ && (plen=ucs4_strlen(prevbuf)) == ucs4_strlen(newbuf)
+ && ucs4_str_width(prevbuf) == ucs4_str_width(newbuf)){
+ UCS *start_of_unequal, *end_of_unequal;
+ UCS *pprev, *endnewbuf;
+ char *to_screen = NULL;
+ int column;
+
+ pprev = prevbuf;
+ start_of_unequal = newbuf;
+ endnewbuf = newbuf + plen;
+ col = column = prevstartcol;
+
+ while(start_of_unequal < endnewbuf && (*start_of_unequal) == (*pprev)){
+ int w;
+
+ w = wcellwidth(*start_of_unequal);
+ if(w >= 0)
+ column += w;
+
+ pprev++;
+ start_of_unequal++;
+ }
+
+ end_of_unequal = endnewbuf-1;
+ pprev = prevbuf + plen - 1;
+
+ while(end_of_unequal > start_of_unequal && (*end_of_unequal) == (*pprev)){
+ *end_of_unequal = '\0';
+ pprev--;
+ end_of_unequal--;
+ }
+
+ if(pico_usingcolor() && VAR_STATUS_FORE_COLOR &&
+ VAR_STATUS_BACK_COLOR &&
+ pico_is_good_color(VAR_STATUS_FORE_COLOR) &&
+ pico_is_good_color(VAR_STATUS_BACK_COLOR)){
+ lastc = pico_get_cur_color();
+ if(lastc){
+ newc = new_color_pair(VAR_STATUS_FORE_COLOR,
+ VAR_STATUS_BACK_COLOR);
+ (void)pico_set_colorp(newc, PSC_NONE);
+ free_color_pair(&newc);
+ }
+ }
+ else if(invert)
+ StartInverse();
+
+ /* PutLine wants UTF-8 string */
+ if(start_of_unequal && (*start_of_unequal))
+ to_screen = ucs4_to_utf8_cpystr(start_of_unequal);
+
+ if(to_screen){
+ PutLine0(row, column, to_screen);
+ fs_give((void **) &to_screen);
+
+ strncpy(prevstatusbuf, newstatusbuf, sizeof(prevstatusbuf));
+ prevstatusbuf[sizeof(prevstatusbuf)-1] = '\0';
+ }
+
+ if(lastc){
+ (void)pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+ else if(invert)
+ EndInverse();
+ }
+ else{
+ if(pico_usingcolor())
+ lastc = pico_get_cur_color();
+
+ if(!invert && pico_usingcolor() && VAR_STATUS_FORE_COLOR &&
+ VAR_STATUS_BACK_COLOR &&
+ pico_is_good_color(VAR_STATUS_FORE_COLOR) &&
+ pico_is_good_color(VAR_STATUS_BACK_COLOR))
+ pico_set_nbg_color(); /* so ClearLine uses bg color */
+
+ ClearLine(row);
+
+ if(pico_usingcolor() && VAR_STATUS_FORE_COLOR &&
+ VAR_STATUS_BACK_COLOR &&
+ pico_is_good_color(VAR_STATUS_FORE_COLOR) &&
+ pico_is_good_color(VAR_STATUS_BACK_COLOR)){
+ if(lastc){
+ newc = new_color_pair(VAR_STATUS_FORE_COLOR,
+ VAR_STATUS_BACK_COLOR);
+ (void)pico_set_colorp(newc, PSC_NONE);
+ free_color_pair(&newc);
+ }
+ }
+ else if(invert){
+ if(lastc)
+ free_color_pair(&lastc);
+
+ StartInverse();
+ }
+
+ col = Centerline(row, newstatusbuf);
+
+ if(lastc){
+ (void)pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+ else if(invert)
+ EndInverse();
+
+ strncpy(prevstatusbuf, newstatusbuf, sizeof(prevstatusbuf));
+ prevstatusbuf[sizeof(prevstatusbuf)-1] = '\0';
+ prevstartcol = col;
+ prevendcol = col + utf8_width(prevstatusbuf) - 1;
+ }
+
+ /* move cursor to a consistent position */
+ if(F_ON(F_SHOW_CURSOR, ps_global))
+ MoveCursor(row, MIN(MAX(0,col+1),ps_global->ttyo->screen_cols-1));
+ else
+ MoveCursor(row, 0);
+
+ fflush(stdout);
+
+ if(prevbuf)
+ fs_give((void **) &prevbuf);
+
+ if(newbuf)
+ fs_give((void **) &newbuf);
+ }
+ else
+ col = prevstartcol;
+
+ return(col);
+}
+
+
+/*----------------------------------------------------------------------
+ Write the given status message to the display.
+
+ Args: mq_entry -- pointer to message queue entry to write.
+
+ ----*/
+int
+output_message(SMQ_T *mq_entry, int ding)
+{
+ int col = 0;
+
+ dprint((9, "output_message(%s)\n",
+ (mq_entry && mq_entry->text) ? mq_entry->text : "?"));
+
+ if(ding && F_OFF(F_QUELL_BEEPS, ps_global)){
+ Writechar(BELL, 0); /* ring bell */
+ fflush(stdout);
+ }
+
+ if(!(mq_entry->flags & SM_MODAL)){
+ col = status_message_write(mq_entry->text, 0);
+ if(ps_global->status_msg_delay > 0){
+ if(F_ON(F_SHOW_CURSOR, ps_global))
+ /* col+1 because col is "[" character */
+ MoveCursor(ps_global->ttyo->screen_rows-FOOTER_ROWS(ps_global),
+ MIN(MAX(0,col+1),ps_global->ttyo->screen_cols-1));
+ else
+ MoveCursor(ps_global->ttyo->screen_rows-FOOTER_ROWS(ps_global), 0);
+
+ fflush(stdout);
+ sleep(ps_global->status_msg_delay);
+ }
+
+ mq_entry->shown = 1;
+ }
+
+ return(col);
+}
+
+
+/*
+ * This is split off from output_message due to the locking
+ * on the status message queue data. This calls scrolltool
+ * which can call back into q_status and so on. So instead of
+ * keeping the queue locked for a long time we copy the
+ * data, unlock the queue, and display the data.
+ */
+int
+output_message_modal(SMQ_T *mq_entry, int ding)
+{
+ int rv = 0;
+ SMQ_T *m, *mnext;
+
+ if(!mq_entry)
+ return(rv);
+
+ dprint((9, "output_message_modal(%s)\n",
+ (mq_entry && mq_entry->text) ? mq_entry->text : "?"));
+
+ if(ding && F_OFF(F_QUELL_BEEPS, ps_global)){
+ Writechar(BELL, 0); /* ring bell */
+ fflush(stdout);
+ }
+
+ if(!mq_entry->shown){
+ int i = 0,
+ pad = MAX(0, (ps_global->ttyo->screen_cols - 59) / 2);
+ char *p, *q, *s, *t;
+ SCROLL_S sargs;
+
+ /* Count the number of modal messsages and add up their lengths. */
+ for(m = mq_entry->next; m != mq_entry; m = m->next)
+ if((m->flags & SM_MODAL) && !m->shown){
+ i++;
+ }
+
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*s%s\n%*s%s\n%*s%s\n%*s%s\n%*s%s\n%*s%s\n%*s%s\n\n",
+ /* 1 2 3 4 5 6*/
+ /*23456789012345678901234567890123456789012345678901234567890*/
+ pad, "",
+ "***********************************************************",
+ pad, "", i ?
+ "* What follows are advisory messages. After reading them *" :
+ "* What follows is an advisory message. After reading it *",
+
+ pad, "",
+ "* simply hit \"Return\" to continue your Alpine session. *",
+ pad, "",
+ "* *",
+ pad, "", i ?
+ "* To review these messages later, press 'J' from the *" :
+ "* To review this message later, press 'J' from the *",
+ pad, "",
+ "* MAIN MENU. *",
+ pad, "",
+ "***********************************************************");
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ t = tmp_20k_buf + strlen(tmp_20k_buf);
+
+ m = mq_entry;
+ do{
+ if((m->flags & SM_MODAL) && !m->shown){
+ int indent;
+
+ indent = ps_global->ttyo->screen_cols > 80
+ ? (ps_global->ttyo->screen_cols - 80) / 3 : 0;
+
+ if(t - tmp_20k_buf > 19000){
+ snprintf(t, SIZEOF_20KBUF-(t-tmp_20k_buf), "\n%*s* * * Running out of buffer space * * *", indent, "");
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ t += strlen(t);
+ snprintf(t, SIZEOF_20KBUF-(t-tmp_20k_buf), "\n%*s* * * Press RETURN for more messages * * *", indent, "");
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ break;
+ }
+
+ add_review_message(m->text, -1);
+
+ if((p = strstr(m->text, "[ALERT]")) != NULL){
+ snprintf(t, SIZEOF_20KBUF-(t-tmp_20k_buf), "%*.*s\n", indent + p - m->text, p - m->text, m->text);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ t += strlen(t);
+
+ for(p += 7; *p && isspace((unsigned char)*p); p++)
+ ;
+ indent += 8;
+ }
+ else{
+ p = m->text;
+ }
+
+ while(strlen(p) > ps_global->ttyo->screen_cols - 2 * indent){
+ for(q = p + ps_global->ttyo->screen_cols - 2 * indent;
+ q > p && !isspace((unsigned char)*q); q--)
+ ;
+
+ snprintf(t, SIZEOF_20KBUF-(t-tmp_20k_buf), "\n%*.*s", indent + q - p, q - p, p);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ t += strlen(t);
+ p = q + 1;
+ }
+
+ snprintf(t, SIZEOF_20KBUF-(t-tmp_20k_buf), "\n%*s%s", indent, "", p);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ t += strlen(t);
+
+ if(i--){
+ snprintf(t, SIZEOF_20KBUF-(t-tmp_20k_buf), "\n\n%*s\n", pad + 30, "- - -");
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ t += strlen(t);
+ }
+ }
+ m = m->next;
+ } while(m != mq_entry);
+
+ s = cpystr(tmp_20k_buf);
+ ClearLine(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global));
+
+ memset(&sargs, 0, sizeof(SCROLL_S));
+ sargs.text.text = s;
+ sargs.text.src = CharStar;
+ sargs.bar.title = "Status Message";
+ sargs.bogus_input = modal_bogus_input;
+ sargs.no_stat_msg = 1;
+ sargs.keys.menu = &modal_message_keymenu;
+ setbitmap(sargs.keys.bitmap);
+
+ scrolltool(&sargs);
+
+ fs_give((void **)&s);
+ ps_global->mangled_screen = 1;
+ }
+
+ /* free the passed in queue */
+ m = mq_entry;
+ do{
+ if(m->text)
+ fs_give((void **) &m->text);
+
+ mnext = m->next;
+ fs_give((void **) &m);
+ m = mnext;
+ } while(m != mq_entry);
+
+ return(rv);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Write or clear delay cue
+
+ Args: on -- whether to turn it on or not
+
+ ----*/
+void
+delay_cmd_cue(int on)
+{
+ COLOR_PAIR *lastc;
+ struct variable *vars = ps_global->vars;
+
+ if(prevstartcol >= 0 && prevendcol < ps_global->ttyo->screen_cols){
+ MoveCursor(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global),
+ MAX(prevstartcol - 1, 0));
+ lastc = pico_set_colors(VAR_STATUS_FORE_COLOR, VAR_STATUS_BACK_COLOR,
+ PSC_REV|PSC_RET);
+ Write_to_screen(on ? (prevstartcol ? "[>" : ">")
+ : (prevstartcol ? " [" : "["));
+
+ MoveCursor(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global),
+ prevendcol);
+
+ Write_to_screen(on ? (prevendcol < ps_global->ttyo->screen_cols-1 ? "<]" : "<")
+ : (prevendcol < ps_global->ttyo->screen_cols-1 ? "] " : "]"));
+
+ if(lastc){
+ (void)pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+
+ if(F_ON(F_SHOW_CURSOR, ps_global))
+ MoveCursor(ps_global->ttyo->screen_rows-FOOTER_ROWS(ps_global),
+ MIN(MAX(0,(prevstartcol + on ? 2 : 1)),ps_global->ttyo->screen_cols-1));
+ else
+ MoveCursor(ps_global->ttyo->screen_rows-FOOTER_ROWS(ps_global), 0);
+ }
+
+ fflush(stdout);
+#ifdef _WINDOWS
+ mswin_setcursor ((on) ? MSWIN_CURSOR_BUSY : MSWIN_CURSOR_ARROW);
+#endif
+}
+
+
+/*
+ * modal_bogus_input - used by scrolltool to complain about
+ * invalid user input.
+ */
+int
+modal_bogus_input(UCS ch)
+{
+ char s[MAX_SCREEN_COLS+1];
+
+ snprintf(s, sizeof(s), _("Command \"%s\" not allowed. Press RETURN to continue Alpine."),
+ pretty_command(ch));
+ s[sizeof(s)-1] = '\0';
+ status_message_write(s, 0);
+ Writechar(BELL, 0);
+ return(0);
+}
diff --git a/alpine/status.h b/alpine/status.h
new file mode 100644
index 00000000..4076c8e0
--- /dev/null
+++ b/alpine/status.h
@@ -0,0 +1,32 @@
+/*
+ * $Id: status.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_STATUS_INCLUDED
+#define PINE_STATUS_INCLUDED
+
+
+#include "../pith/status.h"
+
+
+/* exported protoypes */
+int messages_queued(long *);
+char *last_message_queued(void);
+void flush_ordered_messages(void);
+int status_message_write(char *, int);
+void mark_status_dirty(void);
+void mark_status_unknown(void);
+
+
+#endif /* PINE_STATUS_INCLUDED */
diff --git a/alpine/takeaddr.c b/alpine/takeaddr.c
new file mode 100644
index 00000000..ec89c017
--- /dev/null
+++ b/alpine/takeaddr.c
@@ -0,0 +1,3520 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: takeaddr.c 1012 2008-03-26 00:44:22Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ takeaddr.c
+ Mostly support for Take Address command.
+ ====*/
+
+
+#include "headers.h"
+#include "takeaddr.h"
+#include "addrbook.h"
+#include "adrbkcmd.h"
+#include "status.h"
+#include "confscroll.h"
+#include "keymenu.h"
+#include "radio.h"
+#include "titlebar.h"
+#include "alpine.h"
+#include "help.h"
+#include "mailcmd.h"
+#include "mailpart.h"
+#include "roleconf.h"
+#include "../pith/state.h"
+#include "../pith/msgno.h"
+#include "../pith/adrbklib.h"
+#include "../pith/bldaddr.h"
+#include "../pith/bitmap.h"
+#include "../pith/util.h"
+#include "../pith/addrstring.h"
+#include "../pith/remote.h"
+#include "../pith/newmail.h"
+#include "../pith/list.h"
+#include "../pith/abdlc.h"
+#include "../pith/ablookup.h"
+#include "../pith/stream.h"
+#include "../pith/mailcmd.h"
+#include "../pith/busy.h"
+
+
+typedef struct takeaddress_screen {
+ ScreenMode mode;
+ TA_S *current,
+ *top_line;
+} TA_SCREEN_S;
+
+static TA_SCREEN_S *ta_screen;
+static char *fakedomain = "@";
+
+static ESCKEY_S save_or_export[] = {
+ {'s', 's', "S", N_("Save")},
+ {'e', 'e', "E", N_("Export")},
+ {-1, 0, NULL, NULL}};
+
+
+/* internal prototypes */
+int edit_nickname(AdrBk *, AddrScrn_Disp *, int, char *, char *, HelpType, int, int);
+void add_abook_entry(TA_S *, char *, char *, char *, char *, int, TA_STATE_S **, char *);
+void take_to_addrbooks_frontend(char **, char *, char *, char *, char *,
+ char *, int, TA_STATE_S **, char *);
+void take_to_addrbooks(char **, char *, char *, char *, char *,
+ char *, int, TA_STATE_S **, char *);
+PerAddrBook *use_this_addrbook(int, char *);
+PerAddrBook *check_for_addrbook(char *);
+int takeaddr_screen(struct pine *, TA_S *, int, ScreenMode, TA_STATE_S **, char *);
+void takeaddr_bypass(struct pine *, TA_S *, TA_STATE_S **);
+int ta_do_take(TA_S *, int, int, TA_STATE_S **, char *);
+TA_S *whereis_taline(TA_S *);
+int ta_take_marked_addrs(int, TA_S *, int, TA_STATE_S **, char *);
+int ta_take_single_addr(TA_S *, int, TA_STATE_S **, char *);
+int update_takeaddr_screen(struct pine *, TA_S *, TA_SCREEN_S *, Pos *);
+void takeaddr_screen_redrawer_list(void);
+void takeaddr_screen_redrawer_single(void);
+int attached_addr_handler(TA_S *, int);
+int take_without_edit(TA_S *, int, int, TA_STATE_S **, char *);
+void export_vcard_att(struct pine *, int, long, ATTACH_S *);
+int take_export_tool(struct pine *, int, CONF_S **, unsigned);
+
+#ifdef _WINDOWS
+int ta_scroll_up(long);
+int ta_scroll_down(long);
+int ta_scroll_to_pos(long);
+int ta_scroll_callback(int, long);
+#endif
+
+
+/*
+ * Edit a nickname field.
+ *
+ * Args: abook -- the addressbook handle
+ * dl -- display list line (NULL if new entry)
+ * command_line -- line to prompt on
+ * orig -- nickname to edit
+ * prompt -- prompt
+ * this_help -- help
+ * return_existing -- changes the behavior when a user types in a nickname
+ * which already exists in this abook. If not set, it
+ * will just keep looping until the user changes; if set,
+ * it will return -8 to the caller and orig will be set
+ * to the matching nickname.
+ *
+ * Returns: -10 to cancel
+ * -9 no change
+ * -7 only case of nickname changed (only happens if dl set)
+ * -8 existing nickname chosen (only happens if return_existing set)
+ * 0 new value copied into orig
+ */
+int
+edit_nickname(AdrBk *abook, AddrScrn_Disp *dl, int command_line, char *orig,
+ char *prompt, HelpType this_help, int return_existing, int takeaddr)
+{
+ char edit_buf[MAX_NICKNAME + 1];
+ HelpType help;
+ int i, flags, lastrc, rc;
+ AdrBk_Entry *check, *passed_in_ae;
+ ESCKEY_S ekey[3];
+ SAVE_STATE_S state; /* For saving state of addrbooks temporarily */
+ char *error = NULL;
+
+ ekey[i = 0].ch = ctrl('T');
+ ekey[i].rval = 2;
+ ekey[i].name = "^T";
+ ekey[i++].label = N_("To AddrBk");
+
+ if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
+ ekey[i].ch = ctrl('I');
+ ekey[i].rval = 11;
+ ekey[i].name = "TAB";
+ ekey[i++].label = N_("Complete");
+ }
+
+ ekey[i].ch = -1;
+
+ strncpy(edit_buf, orig, sizeof(edit_buf)-1);
+ edit_buf[sizeof(edit_buf)-1] = '\0';
+ if(dl)
+ passed_in_ae = adrbk_get_ae(abook, (a_c_arg_t) dl->elnum);
+ else
+ passed_in_ae = (AdrBk_Entry *)NULL;
+
+ help = NO_HELP;
+ rc = 0;
+ check = NULL;
+ do{
+ if(error){
+ q_status_message(SM_ORDER, 3, 4, error);
+ fs_give((void **)&error);
+ }
+
+ /* display a message because adrbk_lookup_by_nick returned positive */
+ if(check){
+ if(return_existing){
+ strncpy(orig, edit_buf, sizeof(edit_buf)-1);
+ orig[sizeof(edit_buf)-1] = '\0';
+ if(passed_in_ae)
+ (void)adrbk_get_ae(abook, (a_c_arg_t) dl->elnum);
+ return -8;
+ }
+
+ q_status_message1(SM_ORDER, 0, 4,
+ _("Already an entry with nickname \"%s\""), edit_buf);
+ }
+
+ lastrc = rc;
+ if(rc == 3)
+ help = (help == NO_HELP ? this_help : NO_HELP);
+
+ flags = OE_APPEND_CURRENT;
+ rc = optionally_enter(edit_buf, command_line, 0, sizeof(edit_buf),
+ prompt, ekey, help, &flags);
+
+ if(rc == 1) /* ^C */
+ break;
+
+ if(rc == 2){ /* ^T */
+ void (*redraw) (void) = ps_global->redrawer;
+ char *returned_nickname;
+
+ push_titlebar_state();
+ save_state(&state);
+ if(takeaddr)
+ returned_nickname = addr_book_takeaddr();
+ else
+ returned_nickname = addr_book_selnick();
+
+ restore_state(&state);
+ if(returned_nickname){
+ strncpy(edit_buf, returned_nickname, sizeof(edit_buf)-1);
+ edit_buf[sizeof(edit_buf)-1] = '\0';
+ fs_give((void **)&returned_nickname);
+ }
+
+ ClearScreen();
+ pop_titlebar_state();
+ redraw_titlebar();
+ if((ps_global->redrawer = redraw) != NULL) /* reset old value, and test */
+ (*ps_global->redrawer)();
+ }
+ else if(rc == 11){ /* TAB */
+ if(edit_buf[0]){
+ char *new_nickname = NULL;
+ int ambiguity;
+
+ ambiguity = abook_nickname_complete(edit_buf, &new_nickname,
+ (lastrc==rc && !(flags & OE_USER_MODIFIED)), 0);
+ if(new_nickname){
+ if(*new_nickname){
+ strncpy(edit_buf, new_nickname, sizeof(edit_buf));
+ edit_buf[sizeof(edit_buf)-1] = '\0';
+ }
+
+ fs_give((void **) &new_nickname);
+ }
+
+ if(ambiguity != 2)
+ Writechar(BELL, 0);
+ }
+ }
+
+ }while(rc == 2 ||
+ rc == 3 ||
+ rc == 4 ||
+ rc == 11||
+ nickname_check(edit_buf, &error) ||
+ ((check =
+ adrbk_lookup_by_nick(abook, edit_buf, (adrbk_cntr_t *)NULL)) &&
+ check != passed_in_ae));
+
+ if(rc != 0){
+ if(passed_in_ae)
+ (void)adrbk_get_ae(abook, (a_c_arg_t) dl->elnum);
+
+ return -10;
+ }
+
+ /* only the case of nickname changed */
+ if(passed_in_ae && check == passed_in_ae && strcmp(edit_buf, orig)){
+ (void)adrbk_get_ae(abook, (a_c_arg_t) dl->elnum);
+ strncpy(orig, edit_buf, sizeof(edit_buf)-1);
+ orig[sizeof(edit_buf)-1] = '\0';
+ return -7;
+ }
+
+ if(passed_in_ae)
+ (void)adrbk_get_ae(abook, (a_c_arg_t) dl->elnum);
+
+ if(strcmp(edit_buf, orig) == 0) /* no change */
+ return -9;
+
+ strncpy(orig, edit_buf, sizeof(edit_buf)-1);
+ orig[sizeof(edit_buf)-1] = '\0';
+ return 0;
+}
+
+
+/*
+ * Add an entry to address book.
+ * It is for capturing addresses off incoming mail.
+ * This is a front end for take_to_addrbooks.
+ * It is also used for replacing an existing entry and for adding a single
+ * new address to an existing list.
+ *
+ * The reason this is here is so that when Taking a single address, we can
+ * rearrange the fullname to be Last, First instead of First Last.
+ *
+ * Args: ta_entry -- the entry from the take screen
+ * command_line -- line to prompt on
+ *
+ * Result: item is added to one of the address books,
+ * an error message is queued if appropriate.
+ */
+void
+add_abook_entry(TA_S *ta_entry, char *nick, char *fullname, char *fcc,
+ char *comment, int command_line, TA_STATE_S **tas, char *cmd)
+{
+ ADDRESS *addr;
+ char new_fullname[6*MAX_FULLNAME + 1], new_address[6*MAX_ADDRESS + 1];
+ char **new_list;
+
+ dprint((5, "-- add_abook_entry --\n"));
+
+ /*-- rearrange full name (Last, First) ---*/
+ new_fullname[0] = '\0';
+ addr = ta_entry->addr;
+ if(!fullname && addr->personal != NULL){
+ if(F_ON(F_DISABLE_TAKE_LASTFIRST, ps_global)){
+ strncpy(new_fullname, addr->personal, sizeof(new_fullname)-1);
+ new_fullname[sizeof(new_fullname)-1] = '\0';
+ }
+ else{
+ char old_fullname[6*MAX_FULLNAME + 1];
+
+ snprintf(old_fullname, sizeof(old_fullname), "%s", addr->personal);
+ old_fullname[sizeof(old_fullname)-1] = '\0';
+ switch_to_last_comma_first(old_fullname, new_fullname, sizeof(new_fullname));
+ }
+ }
+
+ /* initial value for new address */
+ new_address[0] = '\0';
+ if(addr->mailbox && addr->mailbox[0]){
+ char *scratch, *p, *t, *u;
+ size_t es;
+ unsigned long l;
+ RFC822BUFFER rbuf;
+
+ es = est_size(addr);
+ scratch = (char *) fs_get(es);
+ scratch[0] = '\0';
+ rbuf.f = dummy_soutr;
+ rbuf.s = NULL;
+ rbuf.beg = scratch;
+ rbuf.cur = scratch;
+ rbuf.end = scratch+es-1;
+ rfc822_output_address_list(&rbuf, addr, 0L, NULL);
+ *rbuf.cur = '\0';
+ if((p = srchstr(scratch, "@" RAWFIELD)) != NULL){
+ for(t = p; ; t--)
+ if(*t == '&'){ /* find "leading" token */
+ *t++ = ' '; /* replace token */
+ *p = '\0'; /* tie off string */
+ u = (char *)rfc822_base64((unsigned char *)t,
+ (unsigned long)strlen(t), &l);
+ *p = '@'; /* restore 'p' */
+ rplstr(p, es-(p-scratch), 12, ""); /* clear special token */
+ rplstr(t, es-(t-scratch), strlen(t), u); /* Null u is handled */
+ if(u)
+ fs_give((void **)&u);
+ }
+ else if(t == scratch)
+ break;
+ }
+
+ strncpy(new_address, scratch, sizeof(new_address)-1);
+ new_address[sizeof(new_address)-1] = '\0';
+
+ if(scratch)
+ fs_give((void **)&scratch);
+ }
+
+ if(ta_entry->frwrded){
+ ADDRESS *a;
+ int i, j;
+
+ for(i = 0, a = addr; a; i++, a = a->next)
+ ;/* just counting for alloc below */
+
+ /* catch special case where empty addr was set in vcard_to_ta */
+ if(i == 1 && !addr->host && !addr->mailbox && !addr->personal)
+ i = 0;
+
+ new_list = (char **) fs_get((i+1) * sizeof(char *));
+ for(j = 0, a = addr; i && a; j++, a = a->next){
+ ADDRESS *next_addr;
+ char *bufp;
+ size_t len;
+
+ next_addr = a->next;
+ a->next = NULL;
+ len = est_size(a);
+ bufp = (char *) fs_get(len * sizeof(char));
+ new_list[j] = cpystr(addr_string(a, bufp, len));
+ a->next = next_addr;
+ fs_give((void **) &bufp);
+ }
+
+ new_list[j] = NULL;
+ }
+ else{
+ int i = 0, j;
+
+ j = (ta_entry->strvalue && ta_entry->strvalue[0]) ? 2 : 1;
+
+ new_list = (char **) fs_get(j * sizeof(char *));
+
+ if(j == 2)
+ new_list[i++] = cpystr(ta_entry->strvalue);
+
+ new_list[i] = NULL;
+ }
+
+ take_to_addrbooks_frontend(new_list, nick,
+ fullname ? fullname : new_fullname,
+ new_address, fcc, comment, command_line,
+ tas, cmd);
+ free_list_array(&new_list);
+}
+
+
+void
+take_to_addrbooks_frontend(char **new_entries, char *nick, char *fullname,
+ char *addr, char *fcc, char *comment, int cmdline,
+ TA_STATE_S **tas, char *cmd)
+{
+ jmp_buf save_jmp_buf;
+ int *save_nesting_level;
+
+ dprint((5, "-- take_to_addrbooks_frontend --\n"));
+
+ if(ps_global->remote_abook_validity > 0 &&
+ adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0))
+ ps_global->mangled_footer = 1;
+
+ save_nesting_level = cpyint(ab_nesting_level);
+ memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
+ if(setjmp(addrbook_changed_unexpectedly)){
+ q_status_message(SM_ORDER, 5, 10, _("Resetting address book..."));
+ dprint((1,
+ "RESETTING address book... take_to_addrbooks_frontend!\n"));
+ addrbook_reset();
+ ab_nesting_level = *save_nesting_level;
+ }
+
+ ab_nesting_level++;
+ take_to_addrbooks(new_entries, nick, fullname, addr, fcc, comment, cmdline,
+ tas, cmd);
+ memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
+ ab_nesting_level--;
+ if(save_nesting_level)
+ fs_give((void **)&save_nesting_level);
+}
+
+
+/*
+ * Add to address book, called from take screen.
+ * It is also used for adding to an existing list or replacing an existing
+ * entry.
+ *
+ * Args: new_entries -- a list of addresses to add to a list or to form
+ * a new list with
+ * nick -- if adding new entry, suggest this for nickname
+ * fullname -- if adding new entry, use this for fullname
+ * addr -- if only one new_entry, this is its addr
+ * fcc -- if adding new entry, use this for fcc
+ * comment -- if adding new entry, use this for comment
+ * command_line -- line to prompt on
+ *
+ * Result: item is added to one of the address books,
+ * an error message is queued if appropriate.
+ */
+void
+take_to_addrbooks(char **new_entries, char *nick, char *fullname, char *addr,
+ char *fcc, char *comment, int command_line, TA_STATE_S **tas, char *cmd)
+{
+ char new_nickname[6*MAX_NICKNAME + 1], exist_nick[6*MAX_NICKNAME + 1];
+ char prompt[200], **p;
+ int rc, listadd = 0, ans, i;
+ AdrBk *abook;
+ SAVE_STATE_S state;
+ PerAddrBook *pab;
+ AdrBk_Entry *abe = (AdrBk_Entry *)NULL, *abe_copy;
+ adrbk_cntr_t entry_num = NO_NEXT;
+ size_t tot_size, new_size, old_size;
+ Tag old_tag;
+ char *tmp_a_string;
+ char *simple_a = NULL;
+ ADDRESS *a = NULL;
+
+
+ dprint((5, "-- take_to_addrbooks --\n"));
+
+ if(tas && *tas)
+ pab = (*tas)->pab;
+ else
+ pab = setup_for_addrbook_add(&state, command_line, cmd);
+
+ /* check we got it opened ok */
+ if(pab == NULL || pab->address_book == NULL)
+ goto take_to_addrbooks_cancel;
+
+ adrbk_check_validity(pab->address_book, 1L);
+ if(pab->address_book->flags & FILE_OUTOFDATE ||
+ (pab->address_book->rd &&
+ pab->address_book->rd->flags & REM_OUTOFDATE)){
+ q_status_message3(SM_ORDER, 0, 4,
+ "Address book%s%s has changed: %stry again",
+ (as.n_addrbk > 1 && pab->abnick) ? " " : "",
+ (as.n_addrbk > 1 && pab->abnick) ? pab->abnick : "",
+ (ps_global->remote_abook_validity == -1) ? "resynchronize and " : "");
+ if(tas && *tas){
+ restore_state(&((*tas)->state));
+ (*tas)->pab = NULL;
+ }
+ else
+ restore_state(&state);
+
+ return;
+ }
+
+ abook = pab->address_book;
+ new_nickname[0] = '\0';
+ exist_nick[0] = '\0';
+
+ if(addr){
+ simple_a = NULL;
+ a = NULL;
+ /* rfc822_parse_adrlist feels free to destroy input so send copy */
+ tmp_a_string = cpystr(addr);
+ rfc822_parse_adrlist(&a, tmp_a_string, fakedomain);
+ if(tmp_a_string)
+ fs_give((void **)&tmp_a_string);
+
+ if(a){
+ simple_a = simple_addr_string(a, tmp_20k_buf, SIZEOF_20KBUF);
+ mail_free_address(&a);
+ }
+
+ if(simple_a && *simple_a)
+ abe = adrbk_lookup_by_addr(abook, simple_a, NULL);
+
+ if(abe){
+ snprintf(prompt, sizeof(prompt), _("Warning: address exists with %s%s, continue "),
+ (abe->nickname && abe->nickname[0]) ? "nickname "
+ : (abe->fullname && abe->fullname[0]) ? "fullname "
+ : "no nickname",
+ (abe->nickname && abe->nickname[0]) ? abe->nickname
+ : (abe->fullname && abe->fullname[0]) ? abe->fullname
+ : "");
+ prompt[sizeof(prompt)-1] = '\0';
+ switch(want_to(prompt, 'y', 'x', NO_HELP, WT_NORM)){
+ case 'y':
+ if(abe->nickname && abe->nickname[0]){
+ strncpy(new_nickname, abe->nickname, sizeof(new_nickname));
+ new_nickname[sizeof(new_nickname)-1] = '\0';
+ strncpy(exist_nick, new_nickname, sizeof(exist_nick));
+ exist_nick[sizeof(exist_nick)-1] = '\0';
+ }
+
+ break;
+
+ default:
+ goto take_to_addrbooks_cancel;
+ }
+ }
+ }
+
+get_nick:
+ abe = NULL;
+ old_tag = NotSet;
+ entry_num = NO_NEXT;
+
+ /*----- nickname ------*/
+ snprintf(prompt, sizeof(prompt),
+ _("Enter new or existing nickname (one word and easy to remember): "));
+ prompt[sizeof(prompt)-1] = '\0';
+ if(!new_nickname[0] && nick){
+ strncpy(new_nickname, nick, sizeof(new_nickname));
+ new_nickname[sizeof(new_nickname)-1] = '\0';
+ }
+
+ rc = edit_nickname(abook, (AddrScrn_Disp *)NULL, command_line,
+ new_nickname, prompt, h_oe_takenick, 1, 1);
+ if(rc == -8){ /* this means an existing nickname was entered */
+ abe = adrbk_lookup_by_nick(abook, new_nickname, &entry_num);
+ if(!abe){ /* this shouldn't happen */
+ q_status_message1(SM_ORDER, 0, 4,
+ _("Already an entry %s in address book!"), new_nickname);
+ goto take_to_addrbooks_cancel;
+ }
+
+ old_tag = abe->tag;
+
+ if(abe->tag == Single && !strcmp(new_nickname, exist_nick)){
+ static ESCKEY_S choices[] = {
+ {'r', 'r', "R", N_("Replace")},
+ {'n', 'n', "N", N_("No")},
+ {-1, 0, NULL, NULL}};
+
+ snprintf(prompt, sizeof(prompt), _("Entry %s (%s) exists, replace ? "),
+ new_nickname,
+ (abe->fullname && abe->fullname[0])
+ ? (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, abe->fullname)
+ : "<no long name>");
+ prompt[sizeof(prompt)-1] = '\0';
+ ans = radio_buttons(prompt,
+ command_line,
+ choices,
+ 'r',
+ 'x',
+ h_oe_take_replace,
+ RB_NORM);
+ }
+ else{
+ static ESCKEY_S choices[] = {
+ {'r', 'r', "R", N_("Replace")},
+ {'a', 'a', "A", N_("Add")},
+ {'n', 'n', "N", N_("No")},
+ {-1, 0, NULL, NULL}};
+
+ snprintf(prompt, sizeof(prompt),
+ _("%s %s (%s) exists, replace or add addresses to it ? "),
+ abe->tag == List ? "List" : "Entry",
+ new_nickname,
+ (abe->fullname && abe->fullname[0])
+ ? (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, abe->fullname)
+ : "<no long name>");
+ prompt[sizeof(prompt)-1] = '\0';
+
+ ans = radio_buttons(prompt,
+ command_line,
+ choices,
+ 'a',
+ 'x',
+ h_oe_take_replace_or_add,
+ RB_NORM);
+ }
+
+ switch(ans){
+ case 'y':
+ case 'r':
+ break;
+
+ case 'n':
+ goto get_nick;
+ break;
+
+ case 'a':
+ listadd++;
+ break;
+
+ default:
+ goto take_to_addrbooks_cancel;
+ }
+ }
+ else if(rc != 0 && rc != -9) /* -9 means a null nickname */
+ goto take_to_addrbooks_cancel;
+
+ if((long)abook->count > MAX_ADRBK_SIZE ||
+ (old_tag == NotSet && (long)abook->count >= MAX_ADRBK_SIZE)){
+ q_status_message(SM_ORDER, 3, 5,
+ _("Address book is at maximum size, cancelled."));
+ dprint((2, "Addrbook at Max size, TakeAddr cancelled\n"));
+ goto take_to_addrbooks_cancel;
+ }
+
+ if(listadd){
+ /* count up size of existing list */
+ if(abe->tag == List){
+ for(p = abe->addr.list; p != NULL && *p != NULL; p++)
+ ;/* do nothing */
+
+ old_size = p - abe->addr.list;
+ }
+ /* or size of existing single address */
+ else if(abe->addr.addr && abe->addr.addr[0])
+ old_size = 1;
+ else
+ old_size = 0;
+ }
+ else /* don't care about old size, they will be tossed in edit_entry */
+ old_size = 0;
+
+ /* make up an abe to pass to edit_entry */
+ abe_copy = adrbk_newentry();
+ abe_copy->nickname = cpystr(new_nickname);
+ abe_copy->tag = List;
+ abe_copy->addr.list = NULL;
+
+ if(listadd){
+ abe_copy->fullname = cpystr((abe->fullname && abe->fullname[0])
+ ? abe->fullname : "");
+ abe_copy->fcc = cpystr((abe->fcc && abe->fcc[0]) ? abe->fcc : "");
+ abe_copy->extra = cpystr((abe->extra&&abe->extra[0]) ? abe->extra : "");
+ }
+ else{
+ /*
+ * use passed in info if available
+ */
+ abe_copy->fullname = cpystr((fullname && fullname[0])
+ ? fullname
+ : (abe && abe->fullname)
+ ? abe->fullname
+ : "");
+ abe_copy->fcc = cpystr((fcc && fcc[0])
+ ? fcc
+ : (abe && abe->fcc)
+ ? abe->fcc
+ : "");
+ abe_copy->extra = cpystr((comment && comment[0])
+ ? comment
+ : (abe && abe->extra)
+ ? abe->extra
+ : "");
+ }
+
+ /* get rid of duplicates */
+ if(listadd){
+ if(abe->tag == List){
+ int elim_dup;
+ char **q, **r;
+ ADDRESS *newadr, *oldadr;
+
+ for(q = new_entries; q != NULL && *q != NULL;){
+
+ tmp_a_string = cpystr(*q);
+ newadr = NULL;
+ rfc822_parse_adrlist(&newadr, tmp_a_string, fakedomain);
+ fs_give((void **) &tmp_a_string);
+
+ elim_dup = (newadr == NULL);
+ for(p = abe->addr.list;
+ !elim_dup && p != NULL && *p != NULL;
+ p++){
+ tmp_a_string = cpystr(*p);
+ oldadr = NULL;
+ rfc822_parse_adrlist(&oldadr, tmp_a_string, fakedomain);
+ fs_give((void **) &tmp_a_string);
+
+ if(address_is_same(newadr, oldadr))
+ elim_dup++;
+
+ if(oldadr)
+ mail_free_address(&oldadr);
+ }
+
+ /* slide the addresses down one to eliminate newadr */
+ if(elim_dup){
+ char *f;
+
+ f = *q;
+ for(r = q; r != NULL && *r != NULL; r++)
+ *r = *(r+1);
+
+ if(f)
+ fs_give((void **) &f);
+ }
+ else
+ q++;
+
+ if(newadr)
+ mail_free_address(&newadr);
+ }
+ }
+ else{
+ char **q, **r;
+ ADDRESS *newadr, *oldadr;
+
+ tmp_a_string = cpystr(abe->addr.addr ? abe->addr.addr : "");
+ oldadr = NULL;
+ rfc822_parse_adrlist(&oldadr, tmp_a_string, fakedomain);
+ fs_give((void **) &tmp_a_string);
+
+ for(q = new_entries; q != NULL && *q != NULL;){
+
+ tmp_a_string = cpystr(*q);
+ newadr = NULL;
+ rfc822_parse_adrlist(&newadr, tmp_a_string, fakedomain);
+ fs_give((void **) &tmp_a_string);
+
+ /* slide the addresses down one to eliminate newadr */
+ if(address_is_same(newadr, oldadr)){
+ char *f;
+
+ f = *q;
+ for(r = q; r != NULL && *r != NULL; r++)
+ *r = *(r+1);
+
+ if(f)
+ fs_give((void **) &f);
+ }
+ else
+ q++;
+
+ if(newadr)
+ mail_free_address(&newadr);
+ }
+
+ if(oldadr)
+ mail_free_address(&oldadr);
+ }
+
+ if(!new_entries || !*new_entries){
+ q_status_message1(SM_ORDER, 0, 4,
+ _("All of the addresses are already included in \"%s\""),
+ new_nickname);
+ free_ae(&abe_copy);
+ if(tas && *tas){
+ restore_state(&((*tas)->state));
+ (*tas)->pab = NULL;
+ }
+ else
+ restore_state(&state);
+
+ return;
+ }
+ }
+
+ /* count up size of new list */
+ for(p = new_entries; p != NULL && *p != NULL; p++)
+ ;/* do nothing */
+
+ new_size = p - new_entries;
+ tot_size = old_size + new_size;
+ abe_copy->addr.list = (char **) fs_get((tot_size+1) * sizeof(char *));
+ memset((void *) abe_copy->addr.list, 0, (tot_size+1) * sizeof(char *));
+ if(old_size > 0){
+ if(abe->tag == List){
+ for(i = 0; i < old_size; i++)
+ abe_copy->addr.list[i] = cpystr(abe->addr.list[i]);
+ }
+ else
+ abe_copy->addr.list[0] = cpystr(abe->addr.addr);
+ }
+
+ /* add new addresses to list */
+ if(tot_size == 1 && addr)
+ abe_copy->addr.list[0] = cpystr(addr);
+ else
+ for(i = 0; i < new_size; i++)
+ abe_copy->addr.list[old_size + i] = cpystr(new_entries[i]);
+
+ abe_copy->addr.list[tot_size] = NULL;
+
+ if(F_ON(F_DISABLE_TAKE_FULLNAMES, ps_global)){
+ for(i = 0; abe_copy->addr.list[i]; i++){
+ simple_a = NULL;
+ a = NULL;
+ tmp_a_string = cpystr(abe_copy->addr.list[i]);
+ rfc822_parse_adrlist(&a, tmp_a_string, fakedomain);
+ if(tmp_a_string)
+ fs_give((void **) &tmp_a_string);
+
+ if(a){
+ simple_a = simple_addr_string(a, tmp_20k_buf, SIZEOF_20KBUF);
+ mail_free_address(&a);
+ }
+
+ /* replace the old addr string with one with no full name */
+ if(simple_a && *simple_a){
+ if(abe_copy->addr.list[i])
+ fs_give((void **) &abe_copy->addr.list[i]);
+
+ abe_copy->addr.list[i] = cpystr(simple_a);
+ }
+ }
+ }
+
+ edit_entry(abook, abe_copy, (a_c_arg_t) entry_num, old_tag, 0, NULL, cmd);
+
+ /* free copy */
+ free_ae(&abe_copy);
+ if(tas && *tas){
+ restore_state(&((*tas)->state));
+ (*tas)->pab = NULL;
+ }
+ else
+ restore_state(&state);
+
+ return;
+
+take_to_addrbooks_cancel:
+ q_status_message(SM_INFO, 0, 2, _("Address book addition cancelled"));
+ if(tas && *tas){
+ restore_state(&((*tas)->state));
+ (*tas)->pab = NULL;
+ }
+ else
+ restore_state(&state);
+}
+
+
+/*
+ * Prep addrbook for TakeAddr add operation.
+ *
+ * Arg: savep -- Address of a pointer to save addrbook state in.
+ * stp -- Address of a pointer to save addrbook state in.
+ *
+ * Returns: a PerAddrBook pointer, or NULL.
+ */
+PerAddrBook *
+setup_for_addrbook_add(SAVE_STATE_S *state, int command_line, char *cmd)
+{
+ PerAddrBook *pab;
+ int save_rem_abook_valid = 0;
+
+ init_ab_if_needed();
+ save_state(state);
+
+ if(as.n_addrbk == 0){
+ q_status_message(SM_ORDER, 3, 4, _("No address book configured!"));
+ return NULL;
+ }
+ else
+ pab = use_this_addrbook(command_line, cmd);
+
+ if(!pab)
+ return NULL;
+
+ if((pab->type & REMOTE_VIA_IMAP) && ps_global->remote_abook_validity == -1){
+ save_rem_abook_valid = -1;
+ ps_global->remote_abook_validity = 0;
+ }
+
+ /* initialize addrbook so we can add to it */
+ init_abook(pab, Open);
+
+ if(save_rem_abook_valid)
+ ps_global->remote_abook_validity = save_rem_abook_valid;
+
+ if(pab->ostatus != Open){
+ q_status_message(SM_ORDER, 3, 4, _("Can't open address book!"));
+ return NULL;
+ }
+
+ if(pab->access != ReadWrite){
+ if(pab->access == ReadOnly)
+ q_status_message(SM_ORDER, 0, 4, _("AddressBook is Read Only"));
+ else if(pab->access == NoAccess)
+ q_status_message(SM_ORDER, 3, 4,
+ _("AddressBook not accessible, permission denied"));
+
+ return NULL;
+ }
+
+ return(pab);
+}
+
+
+/*
+ * Interact with user to figure out which address book they want to add a
+ * new entry (TakeAddr) to.
+ *
+ * Args: command_line -- just the line to prompt on
+ *
+ * Results: returns a pab pointing to the selected addrbook, or NULL.
+ */
+PerAddrBook *
+use_this_addrbook(int command_line, char *cmd)
+{
+ HelpType help;
+ int rc = 0;
+ PerAddrBook *pab, *the_only_pab;
+#define MAX_ABOOK 2000
+ int i, abook_num, count_read_write;
+ char addrbook[MAX_ABOOK + 1],
+ prompt[MAX_ABOOK + 81];
+ static ESCKEY_S ekey[] = {
+ {-2, 0, NULL, NULL},
+ {ctrl('P'), 10, "^P", N_("Prev AddrBook")},
+ {ctrl('N'), 11, "^N", N_("Next AddrBook")},
+ {KEY_UP, 10, "", ""},
+ {KEY_DOWN, 11, "", ""},
+ {-1, 0, NULL, NULL}};
+
+ dprint((9, "- use_this_addrbook -\n"));
+
+ /* check for only one ReadWrite addrbook */
+ count_read_write = 0;
+ for(i = 0; i < as.n_addrbk; i++){
+ pab = &as.adrbks[i];
+ /*
+ * NoExists is counted, too, so the user can add to an empty
+ * addrbook the first time.
+ */
+ if(pab->access == ReadWrite ||
+ pab->access == NoExists ||
+ pab->access == MaybeRorW){
+ count_read_write++;
+ the_only_pab = &as.adrbks[i];
+ }
+ }
+
+ /* only one usable addrbook, use it */
+ if(count_read_write == 1)
+ return(the_only_pab);
+
+ /* no addrbook to write to */
+ if(count_read_write == 0){
+ q_status_message2(SM_ORDER | SM_DING, 3, 4,
+ "No %sAddressbook to %s to!",
+ (as.n_addrbk > 0) ? "writable " : "", cmd);
+ return NULL;
+ }
+
+ /* start with the first addrbook */
+ abook_num = 0;
+ pab = &as.adrbks[abook_num];
+ strncpy(addrbook, pab->abnick, sizeof(addrbook)-1);
+ addrbook[sizeof(addrbook)-1] = '\0';
+ snprintf(prompt, sizeof(prompt), "%c%s to which addrbook : %s",
+ islower((unsigned char)(*cmd)) ? toupper((unsigned char)*cmd) : *cmd,
+ cmd+1,
+ (pab->access == ReadOnly || pab->access == NoAccess) ?
+ "[ReadOnly] " : "");
+ prompt[sizeof(prompt)-1] = '\0';
+ help = NO_HELP;
+ ps_global->mangled_footer = 1;
+ do{
+ int flags;
+
+ if(!pab)
+ q_status_message1(SM_ORDER, 3, 4, _("No addressbook \"%s\""),
+ addrbook);
+
+ if(rc == 3)
+ help = (help == NO_HELP ? h_oe_chooseabook : NO_HELP);
+
+ flags = OE_APPEND_CURRENT;
+ rc = optionally_enter(addrbook, command_line, 0, sizeof(addrbook),
+ prompt, ekey, help, &flags);
+
+ if(rc == 1){ /* ^C */
+ char capcmd[50];
+
+ snprintf(capcmd, sizeof(capcmd),
+ "%c%s",
+ islower((unsigned char)(*cmd)) ? toupper((unsigned char)*cmd)
+ : *cmd,
+ cmd+1);
+ capcmd[sizeof(capcmd)-1] = '\0';
+ cmd_cancelled(capcmd);
+ break;
+ }
+
+ if(rc == 10){ /* Previous addrbook */
+ if(--abook_num < 0)
+ abook_num = as.n_addrbk - 1;
+
+ pab = &as.adrbks[abook_num];
+ strncpy(addrbook, pab->abnick, sizeof(addrbook)-1);
+ addrbook[sizeof(addrbook)-1] = '\0';
+ snprintf(prompt, sizeof(prompt), "%s to which addrbook : %s", cmd,
+ (pab->access == ReadOnly || pab->access == NoAccess) ?
+ "[ReadOnly] " : "");
+ prompt[sizeof(prompt)-1] = '\0';
+ }
+ else if(rc == 11){ /* Next addrbook */
+ if(++abook_num > as.n_addrbk - 1)
+ abook_num = 0;
+
+ pab = &as.adrbks[abook_num];
+ strncpy(addrbook, pab->abnick, sizeof(addrbook)-1);
+ addrbook[sizeof(addrbook)-1] = '\0';
+ snprintf(prompt, sizeof(prompt), "%s to which addrbook : %s", cmd,
+ (pab->access == ReadOnly || pab->access == NoAccess) ?
+ "[ReadOnly] " : "");
+ prompt[sizeof(prompt)-1] = '\0';
+ }
+
+ }while(rc == 2 || rc == 3 || rc == 4 || rc == 10 || rc == 11 || rc == 12 ||
+ !(pab = check_for_addrbook(addrbook)));
+
+ ps_global->mangled_footer = 1;
+
+ if(rc != 0)
+ return NULL;
+
+ return(pab);
+}
+
+
+/*
+ * Return a pab pointer to the addrbook which corresponds to the argument.
+ *
+ * Args: addrbook -- the string representing the addrbook.
+ *
+ * Results: returns a PerAddrBook pointer for the referenced addrbook, NULL
+ * if none. First the nicknames are checked and then the filenames.
+ * This must be one of the existing addrbooks.
+ */
+PerAddrBook *
+check_for_addrbook(char *addrbook)
+{
+ register int i;
+ register PerAddrBook *pab;
+
+ for(i = 0; i < as.n_addrbk; i++){
+ pab = &as.adrbks[i];
+ if(strcmp(pab->abnick, addrbook) == 0)
+ break;
+ }
+
+ if(i < as.n_addrbk)
+ return(pab);
+
+ for(i = 0; i < as.n_addrbk; i++){
+ pab = &as.adrbks[i];
+ if(strcmp(pab->filename, addrbook) == 0)
+ break;
+ }
+
+ if(i < as.n_addrbk)
+ return(pab);
+
+ return NULL;
+}
+
+
+/*
+ * Screen for selecting which addresses to Take to address book.
+ *
+ * Args: ps -- Pine state
+ * ta_list -- Screen is formed from this list of addresses
+ * how_many_selected -- how many checked initially in ListMode
+ * mode -- which mode to start in
+ *
+ * Result: an address book may be updated
+ * Returns -- 0 normally
+ * 1 if it returns before redrawing screen
+ */
+int
+takeaddr_screen(struct pine *ps, TA_S *ta_list, int how_many_selected,
+ ScreenMode mode, TA_STATE_S **tas, char *command)
+{
+ UCS ch = 'x';
+ int cmd, dline, give_warn_message, command_line;
+ int km_popped = 0,
+ directly_to_take = 0,
+ ret = 0,
+ done = 0;
+ TA_S *current = NULL,
+ *ctmp = NULL;
+ TA_SCREEN_S screen;
+ Pos cursor_pos;
+ char *utf8str;
+ struct key_menu *km;
+
+ dprint((2, "- takeaddr_screen -\n"));
+
+ command_line = -FOOTER_ROWS(ps); /* third line from the bottom */
+
+ screen.current = screen.top_line = NULL;
+ screen.mode = mode;
+
+ if(ta_list == NULL){
+ /* TRANSLATORS: something like
+ No addresses to save, cancelled */
+ q_status_message1(SM_INFO, 0, 2, "No addresses to %s, cancelled",
+ command);
+ return 1;
+ }
+
+ current = first_sel_taline(ta_list);
+ ps->mangled_screen = 1;
+ ta_screen = &screen;
+
+ if(is_talist_of_one(current)){
+ directly_to_take++;
+ screen.mode = SingleMode;
+ }
+ else if(screen.mode == ListMode)
+ q_status_message(SM_INFO, 0, 1,
+ _("List mode: Use \"X\" to mark addresses to be included in list"));
+ else
+ q_status_message(SM_INFO, 0, 1,
+ _("Single mode: Use \"P\" or \"N\" to select desired address"));
+
+ while(!done){
+ if(km_popped){
+ km_popped--;
+ if(km_popped == 0){
+ clearfooter(ps);
+ ps->mangled_body = 1;
+ }
+ }
+
+ if(screen.mode == ListMode)
+ ps->redrawer = takeaddr_screen_redrawer_list;
+ else
+ ps->redrawer = takeaddr_screen_redrawer_single;
+
+ if(ps->mangled_screen){
+ ps->mangled_header = 1;
+ ps->mangled_footer = 1;
+ ps->mangled_body = 1;
+ ps->mangled_screen = 0;
+ }
+
+ /*----------- Check for new mail -----------*/
+ if(new_mail(0, NM_TIMING(ch), NM_STATUS_MSG | NM_DEFER_SORT) >= 0)
+ ps->mangled_header = 1;
+
+#ifdef _WINDOWS
+ mswin_beginupdate();
+#endif
+ if(ps->mangled_header){
+ char tbuf[40];
+
+ snprintf(tbuf, sizeof(tbuf), "TAKE ADDRESS SCREEN (%s Mode)",
+ (screen.mode == ListMode) ? "List"
+ : "Single");
+ tbuf[sizeof(tbuf)-1] = '\0';
+ set_titlebar(tbuf, ps->mail_stream, ps->context_current,
+ ps->cur_folder, ps->msgmap, 1, FolderName, 0, 0,
+ NULL);
+ ps->mangled_header = 0;
+ }
+
+ dline = update_takeaddr_screen(ps, current, &screen, &cursor_pos);
+ if(F_OFF(F_SHOW_CURSOR, ps)){
+ cursor_pos.row = ps->ttyo->screen_rows - FOOTER_ROWS(ps);
+ cursor_pos.col = 0;
+ }
+
+ /*---- This displays new mail notification, or errors ---*/
+ if(km_popped){
+ FOOTER_ROWS(ps_global) = 3;
+ mark_status_unknown();
+ }
+
+ display_message(ch);
+ if(km_popped){
+ FOOTER_ROWS(ps_global) = 1;
+ mark_status_unknown();
+ }
+
+ /*---- Redraw footer ----*/
+ if(ps->mangled_footer){
+ bitmap_t bitmap;
+
+ if(km_popped){
+ FOOTER_ROWS(ps) = 3;
+ clearfooter(ps);
+ }
+
+ setbitmap(bitmap);
+ ps->mangled_footer = 0;
+
+ km = (screen.mode == ListMode) ? &ta_keymenu_lm : &ta_keymenu_sm;
+
+ menu_clear_binding(km, KEY_LEFT);
+ menu_clear_binding(km, KEY_RIGHT);
+ if(F_ON(F_ARROW_NAV, ps_global)){
+ int cmd;
+
+ if((cmd = menu_clear_binding(km, '<')) != MC_UNKNOWN){
+ menu_add_binding(km, '<', cmd);
+ menu_add_binding(km, KEY_LEFT, cmd);
+ }
+
+ if((cmd = menu_clear_binding(km, '>')) != MC_UNKNOWN){
+ menu_add_binding(km, '>', cmd);
+ menu_add_binding(km, KEY_RIGHT, cmd);
+ }
+ }
+
+ draw_keymenu(km, bitmap, ps->ttyo->screen_cols,
+ 1 - FOOTER_ROWS(ps_global), 0, FirstMenu);
+
+ if(km_popped){
+ FOOTER_ROWS(ps) = 1;
+ mark_keymenu_dirty();
+ }
+ }
+
+#ifdef _WINDOWS
+ mswin_endupdate();
+#endif
+ /*------ Read the command from the keyboard ----*/
+ MoveCursor(cursor_pos.row, cursor_pos.col);
+
+ if(directly_to_take){ /* bypass this screen */
+ cmd = MC_TAKE;
+ blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
+ }
+ else {
+#ifdef MOUSE
+ mouse_in_content(KEY_MOUSE, -1, -1, 0, 0);
+ register_mfunc(mouse_in_content, HEADER_ROWS(ps_global), 0,
+ ps_global->ttyo->screen_rows - (FOOTER_ROWS(ps)+1),
+ ps_global->ttyo->screen_cols);
+#endif
+
+#ifdef _WINDOWS
+ mswin_setscrollcallback(ta_scroll_callback);
+#endif
+ ch = READ_COMMAND(&utf8str);
+#ifdef MOUSE
+ clear_mfunc(mouse_in_content);
+#endif
+
+#ifdef _WINDOWS
+ mswin_setscrollcallback(NULL);
+#endif
+ cmd = menu_command(ch, km);
+ if (ta_screen->current)
+ current = ta_screen->current;
+
+ if(km_popped)
+ switch(cmd){
+ case MC_NONE :
+ case MC_OTHER :
+ case MC_RESIZE :
+ case MC_REPAINT :
+ km_popped++;
+ break;
+
+ default:
+ clearfooter(ps);
+ break;
+ }
+ }
+
+ switch(cmd){
+ case MC_HELP : /* help! */
+ if(FOOTER_ROWS(ps_global) == 1 && km_popped == 0){
+ km_popped = 2;
+ ps_global->mangled_footer = 1;
+ break;
+ }
+
+ helper(h_takeaddr_screen, _("HELP FOR TAKE ADDRESS SCREEN"),
+ HLPD_SIMPLE);
+ ps->mangled_screen = 1;
+ break;
+
+ case MC_EXIT: /* exit takeaddr screen */
+ q_status_message(SM_INFO, 0, 2, _("Address book addition cancelled"));
+ ret = 1;
+ done++;
+ break;
+
+ case MC_TAKE:
+ if(ta_do_take(current, how_many_selected, command_line, tas,
+ command))
+ done++;
+ else
+ directly_to_take = 0;
+
+ break;
+
+ case MC_CHARDOWN : /* next list element */
+ if((ctmp = next_sel_taline(current)) != NULL)
+ current = ctmp;
+ else
+ q_status_message(SM_INFO, 0, 1, _("Already on last line."));
+
+ break;
+
+ case MC_CHARUP: /* previous list element */
+ if((ctmp = pre_sel_taline(current)) != NULL)
+ current = ctmp;
+ else
+ q_status_message(SM_INFO, 0, 1, _("Already on first line."));
+
+ break;
+
+ case MC_PAGEDN : /* page forward */
+ give_warn_message = 1;
+ while(dline++ < ps->ttyo->screen_rows - FOOTER_ROWS(ps)){
+ if((ctmp = next_sel_taline(current)) != NULL){
+ current = ctmp;
+ give_warn_message = 0;
+ }
+ else
+ break;
+ }
+
+ if(give_warn_message)
+ q_status_message(SM_INFO, 0, 1, _("Already on last page."));
+
+ break;
+
+ case MC_PAGEUP : /* page backward */
+ /* move to top of screen */
+ give_warn_message = 1;
+ while(dline-- > HEADER_ROWS(ps_global)){
+ if((ctmp = pre_sel_taline(current)) != NULL){
+ current = ctmp;
+ give_warn_message = 0;
+ }
+ else
+ break;
+ }
+
+ /* page back one screenful */
+ while(++dline < ps->ttyo->screen_rows - FOOTER_ROWS(ps)){
+ if((ctmp = pre_sel_taline(current)) != NULL){
+ current = ctmp;
+ give_warn_message = 0;
+ }
+ else
+ break;
+ }
+
+ if(give_warn_message)
+ q_status_message(SM_INFO, 0, 1, _("Already on first page."));
+
+ break;
+
+ case MC_WHEREIS : /* whereis */
+ if((ctmp = whereis_taline(current)) != NULL)
+ current = ctmp;
+
+ ps->mangled_footer = 1;
+ break;
+
+ case KEY_SCRLTO:
+ /* no op for now */
+ break;
+
+#ifdef MOUSE
+ case MC_MOUSE:
+ {
+ MOUSEPRESS mp;
+
+ mouse_get_last(NULL, &mp);
+ mp.row -= HEADER_ROWS(ps_global);
+ ctmp = screen.top_line;
+ if(mp.doubleclick){
+ if(screen.mode == SingleMode){
+ if(ta_do_take(current, how_many_selected, command_line,
+ tas, command))
+ done++;
+ else
+ directly_to_take = 0;
+ }
+ else{
+ current->checked = !current->checked; /* flip it */
+ how_many_selected += (current->checked ? 1 : -1);
+ }
+ }
+ else{
+ while(mp.row && ctmp != NULL){
+ --mp.row;
+ do ctmp = ctmp->next;
+ while(ctmp != NULL && ctmp->skip_it && !ctmp->print);
+ }
+
+ if(ctmp != NULL && !ctmp->skip_it)
+ current = ctmp;
+ }
+ }
+ break;
+#endif
+
+ case MC_REPAINT :
+ case MC_RESIZE :
+ ClearScreen();
+ ps->mangled_screen = 1;
+ break;
+
+ case MC_CHOICE : /* [UN]select this addr */
+ current->checked = !current->checked; /* flip it */
+ how_many_selected += (current->checked ? 1 : -1);
+ break;
+
+ case MC_SELALL : /* select all */
+ how_many_selected = ta_mark_all(first_sel_taline(current));
+ ps->mangled_body = 1;
+ break;
+
+ case MC_UNSELALL: /* unselect all */
+ how_many_selected = ta_unmark_all(first_sel_taline(current));
+ ps->mangled_body = 1;
+ break;
+
+ case MC_LISTMODE: /* switch to SingleMode */
+ if(screen.mode == ListMode){
+ screen.mode = SingleMode;
+ q_status_message(SM_INFO, 0, 1,
+ _("Single mode: Use \"P\" or \"N\" to select desired address"));
+ }
+ else{
+ screen.mode = ListMode;
+ q_status_message(SM_INFO, 0, 1,
+ _("List mode: Use \"X\" to mark addresses to be included in list"));
+
+ if(how_many_selected <= 1){
+ how_many_selected =
+ ta_unmark_all(first_sel_taline(current));
+ current->checked = 1;
+ how_many_selected++;
+ }
+ }
+
+ ps->mangled_screen = 1;
+ break;
+
+
+ case MC_NONE : /* simple timeout */
+ break;
+
+
+ /* Unbound (or not dealt with) keystroke */
+ case MC_CHARRIGHT :
+ case MC_CHARLEFT :
+ case MC_GOTOBOL :
+ case MC_GOTOEOL :
+ case MC_UNKNOWN :
+ default:
+ bogus_command(ch, F_ON(F_USE_FK, ps) ? "F1" : "?");
+ break;
+
+ case MC_UTF8:
+ bogus_utf8_command(utf8str, F_ON(F_USE_FK, ps) ? "F1" : "?");
+ break;
+ }
+ }
+
+ ps->mangled_screen = 1;
+
+ return(ret);
+}
+
+
+/*
+ * Do what takeaddr_screen does except bypass the takeaddr_screen and
+ * go directly to do_take.
+ */
+void
+takeaddr_bypass(struct pine *ps, TA_S *current, TA_STATE_S **tasp)
+{
+ TA_SCREEN_S screen; /* We have to fake out ta_do_take because */
+ /* we're bypassing takeaddr_screen. */
+ ta_screen = &screen;
+ ta_screen->mode = SingleMode;
+ current = first_sel_taline(current);
+ (void) ta_do_take(current, 1, -FOOTER_ROWS(ps_global), tasp, _("save"));
+ ps->mangled_screen = 1;
+}
+
+
+/*
+ *
+ */
+int
+ta_do_take(TA_S *current, int how_many_selected, int command_line,
+ TA_STATE_S **tas, char *cmd)
+{
+ return((ta_screen->mode == ListMode)
+ ? ta_take_marked_addrs(how_many_selected,
+ first_sel_taline(current),
+ command_line, tas, cmd)
+ : ta_take_single_addr(current, command_line, tas, cmd));
+}
+
+
+/*
+ * WhereIs for TakeAddr screen.
+ *
+ * Returns the line match is found in or NULL.
+ */
+TA_S *
+whereis_taline(TA_S *current)
+{
+ TA_S *p;
+ int rc, found = 0, wrapped = 0, flags;
+ char *result = NULL, buf[MAX_SEARCH+1], tmp[MAX_SEARCH+20];
+ static char last[MAX_SEARCH+1];
+ HelpType help;
+ static ESCKEY_S ekey[] = {
+ {0, 0, "", ""},
+ {ctrl('Y'), 10, "^Y", N_("Top")},
+ {ctrl('V'), 11, "^V", N_("Bottom")},
+ {-1, 0, NULL, NULL}};
+
+ if(!current)
+ return NULL;
+
+ /*--- get string ---*/
+ buf[0] = '\0';
+ snprintf(tmp, sizeof(tmp), _("Word to find %s%.*s%s: "),
+ (last[0]) ? "[" : "",
+ sizeof(tmp)-20, (last[0]) ? last : "",
+ (last[0]) ? "]" : "");
+ tmp[sizeof(tmp)-1] = '\0';
+ help = NO_HELP;
+ flags = OE_APPEND_CURRENT | OE_KEEP_TRAILING_SPACE;
+ while(1){
+ rc = optionally_enter(buf,-FOOTER_ROWS(ps_global),0,sizeof(buf),
+ tmp,ekey,help,&flags);
+ if(rc == 3)
+ help = help == NO_HELP ? h_config_whereis : NO_HELP;
+ else if(rc == 0 || rc == 1 || rc == 10 || rc == 11 || !buf[0]){
+ if(rc == 0 && !buf[0] && last[0]){
+ strncpy(buf, last, sizeof(buf)-1);
+ buf[sizeof(buf)-1] = '\0';
+ }
+
+ break;
+ }
+ }
+
+ if(rc == 0 && buf[0]){
+ p = current;
+ while((p = next_taline(p)) != NULL)
+ if(srchstr((char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, p->strvalue),
+ buf)){
+ found++;
+ break;
+ }
+
+ if(!found){
+ p = first_taline(current);
+
+ while(p != current)
+ if(srchstr((char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, p->strvalue),
+ buf)){
+ found++;
+ wrapped++;
+ break;
+ }
+ else
+ p = next_taline(p);
+ }
+ }
+ else if(rc == 10){
+ current = first_sel_taline(current);
+ result = _("Searched to top");
+ }
+ else if(rc == 11){
+ current = last_sel_taline(current);
+ result = _("Searched to bottom");
+ }
+ else{
+ current = NULL;
+ result = _("WhereIs cancelled");
+ }
+
+ if(found){
+ current = p;
+ result = wrapped ? _("Search wrapped to beginning") : _("Word found");
+ strncpy(last, buf, sizeof(last)-1);
+ last[sizeof(last)-1] = '\0';
+ }
+
+ q_status_message(SM_ORDER,0,3,result ? result : _("Word not found"));
+ return(current);
+}
+
+
+/*
+ * Call the addrbook functions which add the checked addresses.
+ *
+ * Args: how_many_selected -- how many addresses are checked
+ * f_line -- the first ta line
+ *
+ * Returns: 1 -- we're done, caller should return
+ * 0 -- we're not done
+ */
+int
+ta_take_marked_addrs(int how_many_selected, TA_S *f_line, int command_line,
+ TA_STATE_S **tas, char *cmd)
+{
+ char **new_list;
+ TA_S *p;
+
+ if(how_many_selected == 0){
+ q_status_message(SM_ORDER, 0, 4,
+ _("No addresses marked for taking. Use ExitTake to leave TakeAddr screen"));
+ return 0;
+ }
+
+ if(how_many_selected == 1){
+ for(p = f_line; p; p = next_sel_taline(p))
+ if(p->checked && !p->skip_it)
+ break;
+
+ if(p)
+ add_abook_entry(p,
+ (p->nickname && p->nickname[0]) ? p->nickname : NULL,
+ (p->fullname && p->fullname[0]) ? p->fullname : NULL,
+ (p->fcc && p->fcc[0]) ? p->fcc : NULL,
+ (p->comment && p->comment[0]) ? p->comment : NULL,
+ command_line, tas, cmd);
+ }
+ else{
+ new_list = list_of_checked(f_line);
+ for(p = f_line; p; p = next_sel_taline(p))
+ if(p->checked && !p->skip_it)
+ break;
+
+ take_to_addrbooks_frontend(new_list, p ? p->nickname : NULL,
+ p ? p->fullname : NULL, NULL, p ? p->fcc : NULL,
+ p ? p->comment : NULL, command_line, tas, cmd);
+ free_list_array(&new_list);
+ }
+
+ return 1;
+}
+
+
+int
+ta_take_single_addr(TA_S *cur, int command_line, TA_STATE_S **tas, char *cmd)
+{
+ add_abook_entry(cur,
+ (cur->nickname && cur->nickname[0]) ? cur->nickname : NULL,
+ (cur->fullname && cur->fullname[0]) ? cur->fullname : NULL,
+ (cur->fcc && cur->fcc[0]) ? cur->fcc : NULL,
+ (cur->comment && cur->comment[0]) ? cur->comment : NULL,
+ command_line, tas, cmd);
+
+ return 1;
+}
+
+
+/*
+ * Manage display of the Take Address screen.
+ *
+ * Args: ps -- pine state
+ * current -- the current TA line
+ * screen -- the TA screen
+ * cursor_pos -- return good cursor position here
+ */
+int
+update_takeaddr_screen(struct pine *ps, TA_S *current, TA_SCREEN_S *screen, Pos *cursor_pos)
+{
+ int dline;
+ TA_S *top_line,
+ *ctmp;
+ int longest, i, j;
+ char buf1[6*MAX_SCREEN_COLS + 30];
+ char buf2[6*MAX_SCREEN_COLS + 30];
+ char *p, *q;
+ int screen_width = ps->ttyo->screen_cols;
+ Pos cpos;
+
+ cpos.row = HEADER_ROWS(ps); /* default return value */
+
+ /* calculate top line of display */
+ dline = 0;
+ top_line = 0;
+
+ if (ta_screen->top_line){
+ for(dline = 0, ctmp = ta_screen->top_line;
+ ctmp && ctmp != current; ctmp = next_taline(ctmp))
+ dline++;
+
+ if (ctmp && (dline < ps->ttyo->screen_rows - HEADER_ROWS(ps)
+ - FOOTER_ROWS(ps)))
+ top_line = ta_screen->top_line;
+ }
+
+ if (!top_line){
+ dline = 0;
+ ctmp = top_line = first_taline(current);
+ do
+ if(((dline++) % (ps->ttyo->screen_rows - HEADER_ROWS(ps)
+ - FOOTER_ROWS(ps))) == 0)
+ top_line = ctmp;
+ while(ctmp != current && (ctmp = next_taline(ctmp)));
+ }
+
+#ifdef _WINDOWS
+ /*
+ * Figure out how far down the top line is from the top and how many
+ * total lines there are. Dumb to loop every time thru, but
+ * there aren't that many lines, and it's cheaper than rewriting things
+ * to maintain a line count in each structure...
+ */
+ for(dline = 0, ctmp = pre_taline(top_line); ctmp; ctmp = pre_taline(ctmp))
+ dline++;
+
+ scroll_setpos(dline);
+
+ for(ctmp = next_taline(top_line); ctmp ; ctmp = next_taline(ctmp))
+ dline++;
+
+ scroll_setrange(ps->ttyo->screen_rows - FOOTER_ROWS(ps) - HEADER_ROWS(ps),
+ dline);
+#endif
+
+
+ /* mangled body or new page, force redraw */
+ if(ps->mangled_body || screen->top_line != top_line)
+ screen->current = NULL;
+
+ /* find width of longest line for nicer formatting */
+ longest = 0;
+ for(ctmp = first_taline(top_line); ctmp; ctmp = next_taline(ctmp)){
+ int width;
+
+ if(ctmp
+ && !ctmp->print
+ && ctmp->strvalue
+ && longest < (width = utf8_width((char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, ctmp->strvalue))))
+ longest = width;
+ }
+
+#define LENGTH_OF_THAT_STRING 5 /* "[X] " */
+ longest = MIN(longest, ps->ttyo->screen_cols);
+
+ /* loop thru painting what's needed */
+ for(dline = 0, ctmp = top_line;
+ dline < ps->ttyo->screen_rows - FOOTER_ROWS(ps) - HEADER_ROWS(ps);
+ dline++, ctmp = next_taline(ctmp)){
+
+ /*
+ * only fall thru painting if something needs painting...
+ */
+ if(!ctmp || !screen->current || ctmp == screen->current ||
+ ctmp == top_line || ctmp == current){
+ ClearLine(dline + HEADER_ROWS(ps));
+ if(!ctmp || !ctmp->strvalue)
+ continue;
+ }
+
+ p = buf1;
+ if(ctmp == current){
+ cpos.row = dline + HEADER_ROWS(ps); /* col set below */
+ StartInverse();
+ }
+
+ if(ctmp->print)
+ j = 0;
+ else
+ j = LENGTH_OF_THAT_STRING;
+
+ /*
+ * Copy the value to a temp buffer expanding tabs, and
+ * making sure not to write beyond screen right...
+ */
+ q = (char *) rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, ctmp->strvalue);
+
+ for(i = 0; q[i] && j < ps->ttyo->screen_cols && p-buf1 < sizeof(buf1); i++){
+ if(q[i] == ctrl('I')){
+ do
+ *p++ = SPACE;
+ while(j < ps->ttyo->screen_cols && ((++j)&0x07) && p-buf1 < sizeof(buf1));
+
+ }
+ else{
+ *p++ = q[i];
+ j++;
+ }
+ }
+
+ if(p-buf1 < sizeof(buf1))
+ *p = '\0';
+
+ if(utf8_width(buf1) < longest){
+ (void) utf8_pad_to_width(buf2, buf1, sizeof(buf2), longest, 1);
+ /* it's expected to be in buf1 below */
+ strncpy(buf1, buf2, sizeof(buf1));
+ buf1[sizeof(buf1)-1] = '\0';
+ }
+
+ /* mark lines which have check marks */
+ if(ctmp == current){
+ if(screen->mode == ListMode){
+ snprintf(buf2, sizeof(buf2), "[%c] %s", ctmp->checked ? 'X' : SPACE, buf1);
+ buf2[sizeof(buf2)-1] = '\0';
+ cpos.col = 1; /* position on the X */
+ }
+ else{
+ snprintf(buf2, sizeof(buf2), " %s", buf1);
+ buf2[sizeof(buf2)-1] = '\0';
+ cpos.col = 5; /* 5 spaces before text */
+ }
+ }
+ else{
+ if(ctmp->print){
+ int width, actual_width;
+ size_t len;
+
+ /*
+ * In buf2 make ------string--------
+ * which reaches all the way across the screen. String will
+ * already have a leading and trailing space.
+ */
+ width = utf8_width(buf1);
+
+ if(width > screen_width){
+ actual_width = utf8_truncate(buf1, screen_width);
+ /* it might be 1 less */
+ if(actual_width < screen_width && (len=strlen(buf1))+1 < sizeof(buf1)){
+ buf1[len] = SPACE;
+ buf1[len+1] = '\0';
+ }
+ }
+ else{
+ snprintf(buf2, sizeof(buf2), "%s%s",
+ repeat_char((screen_width-width)/2, '-'), buf1);
+ buf2[sizeof(buf2)-1] = '\0';
+ len = strlen(buf2);
+ width = utf8_width(buf2);
+ snprintf(buf2+len, sizeof(buf2)-len, "%s",
+ repeat_char(screen_width-width, '-'));
+ buf2[sizeof(buf2)-1] = '\0';
+ }
+ }
+ else{
+ if(screen->mode == ListMode)
+ snprintf(buf2, sizeof(buf2), "[%c] %.*s", ctmp->checked ? 'X' : SPACE,
+ sizeof(buf2)-6, buf1);
+ else
+ snprintf(buf2, sizeof(buf2), " %.*s", sizeof(buf2)-6, buf1);
+
+ buf2[sizeof(buf2)-1] = '\0';
+ }
+ }
+
+ PutLine0(dline + HEADER_ROWS(ps), 0, buf2);
+
+ if(ctmp == current)
+ EndInverse();
+ }
+
+ ps->mangled_body = 0;
+ screen->top_line = top_line;
+ screen->current = current;
+ if(cursor_pos)
+ *cursor_pos = cpos;
+
+ return(cpos.row);
+}
+
+
+void
+takeaddr_screen_redrawer_list(void)
+{
+ ps_global->mangled_body = 1;
+ (void)update_takeaddr_screen(ps_global, ta_screen->current, ta_screen,
+ (Pos *)NULL);
+}
+
+
+void
+takeaddr_screen_redrawer_single(void)
+{
+ ps_global->mangled_body = 1;
+ (void)update_takeaddr_screen(ps_global, ta_screen->current, ta_screen,
+ (Pos *)NULL);
+}
+
+
+/* jpf work in progress
+ * Execute command to take addresses out of message and put in the address book
+ *
+ * Args: ps -- pine state
+ * msgmap -- the MessageMap
+ * agg -- this is aggregate operation if set
+ *
+ * Result: The entry is added to an address book.
+ */
+int
+cmd_take_addr(struct pine *ps, MSGNO_S *msgmap, int agg)
+{
+ TA_S *ta_list = NULL;
+ int how_many_selected = 0, rtype;
+
+ /* Ask user what kind of Take they want to do */
+ if(!agg && F_ON(F_ENABLE_ROLE_TAKE, ps)){
+ rtype = rule_setup_type(ps,
+ RS_RULES |
+ ((mn_get_total(msgmap) > 0)
+ ? (F_ON(F_ENABLE_TAKE_EXPORT, ps)
+ ? (RS_INCADDR | RS_INCEXP)
+ : RS_INCADDR)
+ : RS_NONE),
+ "Take to : ");
+ }
+ else if(F_ON(F_ENABLE_TAKE_EXPORT, ps) && mn_get_total(msgmap) > 0)
+ rtype = rule_setup_type(ps, RS_INCADDR | RS_INCEXP, "Take to : ");
+ else
+ rtype = 'a';
+
+ if(rtype == 'x' || rtype == 'Z'){
+ if(rtype == 'x')
+ cmd_cancelled(NULL);
+ else if(rtype == 'Z')
+ q_status_message(SM_ORDER | SM_DING, 3, 5,
+ "Try turning on color with the Setup/Kolor command.");
+ return -1;
+ }
+
+ ps->mangled_footer = 1;
+
+ if(rtype > 0)
+ switch(rtype){
+ case 'e':
+ {
+ LINES_TO_TAKE *lines_to_take = NULL;
+
+ rtype = set_up_takeaddr('e', ps, msgmap, &ta_list, &how_many_selected,
+ agg ? TA_AGG : 0, NULL);
+
+ if(rtype >= 0){
+ if(convert_ta_to_lines(ta_list, &lines_to_take)){
+ while(lines_to_take && lines_to_take->prev)
+ lines_to_take = lines_to_take->prev;
+
+ take_to_export(ps, lines_to_take);
+
+ free_ltlines(&lines_to_take);
+ }
+ else
+ q_status_message(SM_ORDER, 3, 4, _("Can't find anything to export"));
+ }
+ }
+
+ break;
+
+ case 'r':
+ case 's':
+ case 'i':
+ case 'f':
+ case 'o':
+ case 'c':
+ case 'x':
+ role_take(ps, msgmap, rtype);
+ break;
+
+ case 'a':
+ rtype = set_up_takeaddr('a', ps, msgmap, &ta_list, &how_many_selected,
+ agg ? TA_AGG : 0, attached_addr_handler);
+ if(rtype >= 0){
+ (void) takeaddr_screen(ps, ta_list, how_many_selected,
+ agg ? ListMode : SingleMode,
+ NULL, _("take"));
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ /* clean up */
+ free_talines(&ta_list);
+ env_for_pico_callback = NULL;
+ body_for_pico_callback = NULL;
+
+ return(rtype >= 0 ? 1 : 0);
+}
+
+
+int
+attached_addr_handler(TA_S *current, int added)
+{
+ char prompt[200];
+ int command_line = -FOOTER_ROWS(ps_global);
+
+ snprintf(prompt, sizeof(prompt),
+ "Take %d entries from attachment to addrbook all at once ",
+ added);
+ switch(want_to(prompt, 'n', 'x', NO_HELP, WT_NORM)){
+ case 'y':
+ if(take_without_edit(current, added, command_line, NULL, "take") >= 0)
+ return(0); /* all taken care of */
+ else
+ return(-1); /* problem */
+
+ case 'x':
+ cmd_cancelled("Take");
+ return(-1); /* problem */
+
+ default:
+ return(1); /* proceed */
+ }
+}
+
+
+int
+take_without_edit(TA_S *ta_list, int num_in_list, int command_line, TA_STATE_S **tas, char *cmd)
+{
+ PerAddrBook *pab_dst;
+ SAVE_STATE_S state; /* For saving state of addrbooks temporarily */
+ int rc, total_to_copy;
+ int how_many_dups = 0, how_many_to_copy = 0, skip_dups = 0;
+ int ret = 0;
+ int err = 0, need_write = 0, we_cancel = 0;
+ adrbk_cntr_t new_entry_num;
+ char warn[2][MAX_NICKNAME+1];
+ char tmp[200];
+ TA_S *current;
+ SWOOP_S *swoop_list = NULL, *sw;
+
+ dprint((2, "\n - take_without_edit(%d) - \n",
+ num_in_list));
+
+ /* move to beginning of the list */
+ if(ta_list)
+ while(ta_list->prev)
+ ta_list = ta_list->prev;
+
+ pab_dst = setup_for_addrbook_add(&state, command_line, cmd);
+ if(!pab_dst)
+ goto get_out;
+
+ swoop_list = (SWOOP_S *)fs_get((num_in_list+1) * sizeof(SWOOP_S));
+ memset((void *)swoop_list, 0, (num_in_list+1) * sizeof(SWOOP_S));
+ sw = swoop_list;
+
+ /*
+ * Look through all the vcards for those with nicknames already
+ * existing in the destination abook (dups) and build a list of
+ * entries to be acted on.
+ */
+ for(current = ta_list; current; current = current->next){
+ adrbk_cntr_t dst_enum;
+
+ if(current->skip_it)
+ continue;
+
+ /* check to see if this nickname already exists in the dest abook */
+ if(current->nickname && current->nickname[0]){
+ AdrBk_Entry *abe;
+
+ current->checked = 0;
+ abe = adrbk_lookup_by_nick(pab_dst->address_book,
+ current->nickname, &dst_enum);
+ /*
+ * This nickname already exists.
+ */
+ if(abe){
+ sw->dup = 1;
+ sw->dst_enum = dst_enum;
+ if(how_many_dups < 2){
+ strncpy(warn[how_many_dups], current->nickname, MAX_NICKNAME);
+ warn[how_many_dups][MAX_NICKNAME] = '\0';
+ }
+
+ how_many_dups++;
+ }
+ }
+
+ sw->ta = current;
+ sw++;
+ how_many_to_copy++;
+ }
+
+ /*
+ * If there are some nicknames which already exist in the selected
+ * abook, ask user what to do.
+ */
+ if(how_many_dups > 0){
+ if(how_many_dups == 1){
+ if(how_many_to_copy == 1 && num_in_list == 1){
+ ret = 'T'; /* use Take */
+ if(tas && *tas){
+ (*tas)->state = state;
+ (*tas)->pab = pab_dst;
+ }
+
+ goto get_out;
+ }
+ else{
+ snprintf(tmp, sizeof(tmp),
+ "Entry with nickname \"%.*s\" already exists, replace ",
+ sizeof(tmp)-50, warn[0]);
+ }
+ }
+ else if(how_many_dups == 2)
+ snprintf(tmp, sizeof(tmp),
+ "Nicknames \"%.*s\" and \"%.*s\" already exist, replace ",
+ (sizeof(tmp)-50)/2, warn[0], (sizeof(tmp)-50)/2, warn[1]);
+ else
+ snprintf(tmp, sizeof(tmp), "%d of the nicknames already exist, replace ",
+ how_many_dups);
+
+ switch(want_to(tmp, 'n', 'x', h_ab_copy_dups, WT_NORM)){
+ case 'n':
+ skip_dups++;
+ break;
+
+ case 'y':
+ break;
+
+ case 'x':
+ err++;
+ goto get_out;
+ }
+ }
+
+ /*
+ * Because the deletes happen immediately we have to delete from high
+ * entry number towards lower entry numbers so that we are deleting
+ * the correct entries. In order to do that we'll sort the swoop_list
+ * to give us a safe order.
+ */
+ if(!skip_dups && how_many_dups > 1)
+ qsort((qsort_t *)swoop_list, (size_t)num_in_list, sizeof(*swoop_list),
+ cmp_swoop_list);
+
+ we_cancel = busy_cue("Saving addrbook entries", NULL, 0);
+ total_to_copy = how_many_to_copy - (skip_dups ? how_many_dups : 0);
+
+ /*
+ * Add the list of entries to the destination abook.
+ */
+ for(sw = swoop_list; sw && sw->ta; sw++){
+ Tag tag;
+ char abuf[MAX_ADDRESS + 1];
+ int count_of_addrs;
+
+ if(skip_dups && sw->dup)
+ continue;
+
+ /*
+ * Delete existing dups and replace them.
+ */
+ if(sw->dup){
+
+ /* delete the existing entry */
+ rc = 0;
+ if(adrbk_delete(pab_dst->address_book,
+ (a_c_arg_t)sw->dst_enum, 1, 0, 0, 0) == 0){
+ need_write++;
+ }
+ else{
+ q_status_message2(SM_ORDER | SM_DING, 3, 5,
+ "Error replacing entry in %.200s: %.200s",
+ pab_dst->abnick,
+ error_description(errno));
+ err++;
+ goto get_out;
+ }
+ }
+
+ /*
+ * We need to count the number of addresses in this entry in order
+ * to tell the adrbk routines if it is a List or a Single, and in
+ * order to pass the right stuff to be added.
+ */
+ count_of_addrs = count_addrs(sw->ta->addr);
+ tag = (count_of_addrs > 1) ? List : Single;
+ if(tag == Single){
+ if(sw->ta->addr->mailbox && sw->ta->addr->mailbox[0]){
+ char *scratch, *p, *t, *u;
+ unsigned long l;
+ RFC822BUFFER rbuf;
+ size_t es;
+
+ es = est_size(sw->ta->addr);
+ scratch = (char *) fs_get(es * sizeof(char));
+ scratch[0] = '\0';
+ rbuf.f = dummy_soutr;
+ rbuf.s = NULL;
+ rbuf.beg = scratch;
+ rbuf.cur = scratch;
+ rbuf.end = scratch+es-1;
+ rfc822_output_address_list(&rbuf, sw->ta->addr, 0L, NULL);
+ *rbuf.cur = '\0';
+ if((p = srchstr(scratch, "@" RAWFIELD)) != NULL){
+ for(t = p; ; t--)
+ if(*t == '&'){ /* find "leading" token */
+ *t++ = ' '; /* replace token */
+ *p = '\0'; /* tie off string */
+ u = (char *)rfc822_base64((unsigned char *)t,
+ (unsigned long)strlen(t), &l);
+ *p = '@'; /* restore 'p' */
+ rplstr(p, es-(p-scratch), 12, ""); /* clear special token */
+ rplstr(t, es-(t-scratch), strlen(t), u); /* Null u is handled */
+ if(u)
+ fs_give((void **)&u);
+ }
+ else if(t == scratch)
+ break;
+ }
+
+ strncpy(abuf, scratch, sizeof(abuf)-1);
+ abuf[sizeof(abuf)-1] = '\0';
+
+ if(scratch)
+ fs_give((void **)&scratch);
+ }
+ else
+ abuf[0] = '\0';
+ }
+
+
+ /*
+ * Now we have a clean slate to work with.
+ */
+ if(total_to_copy <= 1)
+ rc = adrbk_add(pab_dst->address_book,
+ NO_NEXT,
+ sw->ta->nickname,
+ sw->ta->fullname,
+ tag == Single ? abuf : NULL,
+ sw->ta->fcc,
+ sw->ta->comment,
+ tag,
+ &new_entry_num,
+ (int *)NULL,
+ 0,
+ 0,
+ 0);
+ else
+ rc = adrbk_append(pab_dst->address_book,
+ sw->ta->nickname,
+ sw->ta->fullname,
+ tag == Single ? abuf : NULL,
+ sw->ta->fcc,
+ sw->ta->comment,
+ tag,
+ &new_entry_num);
+
+ if(rc == 0)
+ need_write++;
+
+ /*
+ * If the entry we copied is a list, we also have to add
+ * the list members to the copy.
+ */
+ if(rc == 0 && tag == List){
+ int i, save_sort_rule;
+ ADDRESS *a, *save_next;
+ char **list;
+
+ list = (char **)fs_get((count_of_addrs + 1) * sizeof(char *));
+ memset((void *)list, 0, (count_of_addrs+1) * sizeof(char *));
+ i = 0;
+ for(a = sw->ta->addr; a; a = a->next){
+ save_next = a->next;
+ a->next = NULL;
+
+ if(a->mailbox && a->mailbox[0]){
+ char *scratch, *p, *t, *u;
+ unsigned long l;
+ RFC822BUFFER rbuf;
+ size_t es;
+
+ es = est_size(a);
+ scratch = (char *) fs_get(es * sizeof(char));
+ scratch[0] = '\0';
+ rbuf.f = dummy_soutr;
+ rbuf.s = NULL;
+ rbuf.beg = scratch;
+ rbuf.cur = scratch;
+ rbuf.end = scratch+es-1;
+ rfc822_output_address_list(&rbuf, a, 0L, NULL);
+ *rbuf.cur = '\0';
+ if((p = srchstr(scratch, "@" RAWFIELD)) != NULL){
+ for(t = p; ; t--)
+ if(*t == '&'){ /* find "leading" token */
+ *t++ = ' '; /* replace token */
+ *p = '\0'; /* tie off string */
+ u = (char *)rfc822_base64((unsigned char *)t,
+ (unsigned long)strlen(t), &l);
+ *p = '@'; /* restore 'p' */
+ rplstr(p, es-(p-scratch), 12, ""); /* clear special token */
+ rplstr(t, es-(t-scratch), strlen(t), u); /* Null u is handled */
+ if(u)
+ fs_give((void **)&u);
+ }
+ else if(t == scratch)
+ break;
+ }
+
+ strncpy(abuf, scratch, sizeof(abuf)-1);
+ abuf[sizeof(abuf)-1] = '\0';
+
+ if(scratch)
+ fs_give((void **)&scratch);
+ }
+ else
+ abuf[0] = '\0';
+
+ list[i++] = cpystr(abuf);
+ a->next = save_next;
+ }
+
+ /*
+ * We want it to copy the list in the exact order
+ * without sorting it.
+ */
+ save_sort_rule = pab_dst->address_book->sort_rule;
+ pab_dst->address_book->sort_rule = AB_SORT_RULE_NONE;
+
+ rc = adrbk_nlistadd(pab_dst->address_book,
+ (a_c_arg_t)new_entry_num, NULL, NULL,
+ list, 0, 0, 0);
+
+ pab_dst->address_book->sort_rule = save_sort_rule;
+ free_list_array(&list);
+ }
+
+ if(rc != 0){
+ q_status_message1(SM_ORDER | SM_DING, 3, 5,
+ "Error saving: %.200s",
+ error_description(errno));
+ err++;
+ goto get_out;
+ }
+ }
+
+ if(need_write){
+ int sort_happened = 0;
+
+ if(adrbk_write(pab_dst->address_book, 0, NULL, &sort_happened, 0, 1)){
+ err++;
+ goto get_out;
+ }
+
+ if(sort_happened)
+ ps_global->mangled_screen = 1;
+ }
+
+get_out:
+ if(we_cancel)
+ cancel_busy_cue(1);
+
+ if(!ret)
+ restore_state(&state);
+
+ if(swoop_list)
+ fs_give((void **)&swoop_list);
+
+ ps_global->mangled_footer = 1;
+
+ if(err){
+ char capcmd[50];
+
+ ret = -1;
+ snprintf(capcmd, sizeof(capcmd),
+ "%c%.*s",
+ islower((unsigned char)(*cmd)) ? toupper((unsigned char)*cmd)
+ : *cmd,
+ sizeof(capcmd)-2, cmd+1);
+ if(need_write)
+ q_status_message1(SM_ORDER | SM_DING, 3, 4,
+ "%.200s only partially completed", capcmd);
+ else
+ cmd_cancelled(capcmd);
+ }
+ else if(ret != 'T' && total_to_copy > 0){
+
+ ret = 1;
+ snprintf(tmp, sizeof(tmp), "Saved %d %s to \"%.*s\"",
+ total_to_copy,
+ (total_to_copy > 1) ? "entries" : "entry",
+ sizeof(tmp)-30, pab_dst->abnick);
+ q_status_message(SM_ORDER, 4, 4, tmp);
+ }
+
+ return(ret);
+}
+
+
+/*
+ * Special case interface to allow a more interactive Save in the case where
+ * the user seems to be wanting to save an exact copy of an existing entry.
+ * For example, they might be trying to save a copy of a list with the intention
+ * of changing it a little bit. The regular save doesn't allow this, since no
+ * editing takes place, but this version plops them into the address book
+ * editor.
+ */
+void
+take_this_one_entry(struct pine *ps, TA_STATE_S **tasp, AdrBk *abook, long int cur_line)
+{
+ AdrBk_Entry *abe;
+ AddrScrn_Disp *dl;
+ char *fcc = NULL, *comment = NULL, *fullname = NULL,
+ *nickname = NULL;
+ ADDRESS *addr;
+ int how_many_selected;
+ TA_S *current = NULL;
+
+ dl = dlist(cur_line);
+ abe = ae(cur_line);
+ if(!abe){
+ q_status_message(SM_ORDER, 0, 4, _("Nothing to save, cancelled"));
+ return;
+ }
+
+ if(dl->type == ListHead || dl->type == Simple){
+ fcc = (abe->fcc && abe->fcc[0]) ? abe->fcc : NULL;
+ comment = (abe->extra && abe->extra[0]) ? abe->extra : NULL;
+ fullname = (abe->fullname && abe->fullname[0]) ? abe->fullname : NULL;
+ nickname = (abe->nickname && abe->nickname[0]) ? abe->nickname : NULL;
+ }
+
+ addr = abe_to_address(abe, dl, abook, &how_many_selected);
+ if(!addr){
+ addr = mail_newaddr();
+ addr->host = cpystr("");
+ addr->mailbox = cpystr("");
+ }
+
+ switch(abe->tag){
+ case Single:
+#ifdef ENABLE_LDAP
+ /*
+ * Special case. When user is saving an entry with a runtime
+ * ldap lookup address, they may be doing it because the lookup
+ * has become stale. Give them a way to get the old address out
+ * of the lookup entry so they can save that, instead.
+ */
+ if(!addr->personal && !strncmp(addr->mailbox, RUN_LDAP, LEN_RL)){
+ LDAP_SERV_S *info = NULL;
+ int i = 'l';
+ static ESCKEY_S backup_or_ldap[] = {
+ {'b', 'b', "B", N_("Backup")},
+ {'l', 'l', "L", N_("LDAP")},
+ {-1, 0, NULL, NULL}};
+
+ info = break_up_ldap_server(addr->mailbox + LEN_RL);
+ if(info && info->mail && *info->mail)
+ i = radio_buttons(_("Copy backup address or retain LDAP search criteria ? "),
+ -FOOTER_ROWS(ps_global), backup_or_ldap,
+ 'b', 'x',
+ h_ab_backup_or_ldap, RB_NORM);
+
+ if(i == 'b'){
+ ADDRESS *a = NULL;
+
+ rfc822_parse_adrlist(&a, info->mail, fakedomain);
+
+ if(a){
+ if(addr->mailbox)
+ fs_give((void **)&addr->mailbox);
+ if(addr->host)
+ fs_give((void **)&addr->host);
+
+ addr->mailbox = a->mailbox;
+ a->mailbox = NULL;
+ addr->host = a->host;
+ a->host = NULL;
+ mail_free_address(&a);
+ }
+ }
+
+ if(info)
+ free_ldap_server_info(&info);
+ }
+#endif /* ENABLE_LDAP */
+ current = fill_in_ta(&current, addr, 0, (char *)NULL);
+ break;
+
+ case List:
+ /*
+ * The empty string for the last argument is significant. Fill_in_ta
+ * copies the whole adrlist into a single TA if there is a print
+ * string.
+ */
+ if(dl->type == ListHead)
+ current = fill_in_ta(&current, addr, 1, "");
+ else
+ current = fill_in_ta(&current, addr, 0, (char *)NULL);
+
+ break;
+
+ default:
+ dprint((1,
+ "default case in take_this_one_entry, shouldn't happen\n"));
+ return;
+ }
+
+ if(current->strvalue && !strcmp(current->strvalue, "@")){
+ fs_give((void **)&current->strvalue);
+ if(fullname && fullname[0])
+ current->strvalue = cpystr(fullname);
+ else if(nickname && nickname[0])
+ current->strvalue = cpystr(nickname);
+ else
+ current->strvalue = cpystr("?");
+
+ convert_possibly_encoded_str_to_utf8(&current->strvalue);
+ }
+
+ if(addr)
+ mail_free_address(&addr);
+
+ if(current){
+ current = first_sel_taline(current);
+ if(fullname && *fullname){
+ current->fullname = cpystr(fullname);
+ convert_possibly_encoded_str_to_utf8(&current->fullname);
+ }
+
+ if(fcc && *fcc){
+ current->fcc = cpystr(fcc);
+ convert_possibly_encoded_str_to_utf8(&current->fcc);
+ }
+
+ if(comment && *comment){
+ current->comment = cpystr(comment);
+ convert_possibly_encoded_str_to_utf8(&current->comment);
+ }
+
+ if(nickname && *nickname){
+ current->nickname = cpystr(nickname);
+ convert_possibly_encoded_str_to_utf8(&current->nickname);
+ }
+
+ takeaddr_bypass(ps, current, tasp);
+ }
+ else
+ q_status_message(SM_ORDER, 0, 4, _("Nothing to save, cancelled"));
+
+ free_talines(&current);
+}
+
+
+/*
+ * Execute command to save addresses out of vcard attachment.
+ */
+void
+save_vcard_att(struct pine *ps, int qline, long int msgno, ATTACH_S *a)
+{
+ int how_many_selected, j;
+ TA_S *current = NULL;
+ TA_STATE_S tas, *tasp;
+
+
+ dprint((2, "\n - saving vcard attachment - \n"));
+
+ j = radio_buttons(_("Save to address book or Export to filesystem ? "),
+ qline, save_or_export, 's', 'x',
+ h_ab_save_exp, RB_NORM|RB_SEQ_SENSITIVE);
+
+ switch(j){
+ case 'x':
+ q_status_message(SM_INFO, 0, 2, _("Address book save cancelled"));
+ return;
+
+ case 'e':
+ export_vcard_att(ps, qline, msgno, a);
+ return;
+
+ case 's':
+ break;
+
+ default:
+ q_status_message(SM_ORDER, 3, 3, "can't happen in save_vcard_att");
+ return;
+ }
+
+ dprint((2, "\n - saving attachment into address book - \n"));
+ ps->mangled_footer = 1;
+ current = NULL;
+ how_many_selected = process_vcard_atts(ps->mail_stream, msgno, NULL,
+ a->body, a->number, &current);
+ if(how_many_selected > 0){
+ tas.pab = NULL;
+ tasp = &tas;
+ if(how_many_selected == 1){
+ takeaddr_bypass(ps, current, NULL);
+ }
+ else if(take_without_edit(current, how_many_selected, qline,
+ &tasp, "save") == 'T'){
+ /*
+ * Eliminate dups.
+ */
+ how_many_selected -=
+ eliminate_dups_but_not_us(first_sel_taline(current));
+
+ (void)takeaddr_screen(ps, current, how_many_selected, SingleMode,
+ &tasp, _("save"));
+
+ /*
+ * If takeaddr_screen or its children didn't do this for us,
+ * we do it here.
+ */
+ if(tas.pab)
+ restore_state(&(tas.state));
+ }
+ }
+ else if(how_many_selected == 0)
+ q_status_message(SM_ORDER, 0, 3,
+ _("Save cancelled: no entries in attachment"));
+
+ free_talines(&current);
+}
+
+
+/*
+ * Execute command to export vcard attachment.
+ */
+void
+export_vcard_att(struct pine *ps, int qline, long int msgno, ATTACH_S *a)
+{
+ int how_many_selected, i;
+ TA_S *current;
+ STORE_S *srcstore = NULL;
+ SourceType srctype;
+ static ESCKEY_S vcard_or_addresses[] = {
+ {'a', 'a', "A", N_("Address List")},
+ {'v', 'v', "V", N_("VCard")},
+ {-1, 0, NULL, NULL}};
+
+ if(ps->restricted){
+ q_status_message(SM_ORDER, 0, 3,
+ "Alpine demo can't export addresses to files");
+ return;
+ }
+
+ dprint((2, "\n - exporting vcard attachment - \n"));
+
+ i = radio_buttons(_("Export list of addresses or vCard text ? "),
+ qline, vcard_or_addresses, 'a', 'x',
+ h_ab_export_vcard, RB_NORM|RB_SEQ_SENSITIVE);
+
+ switch(i){
+ case 'x':
+ q_status_message(SM_INFO, 0, 2, _("Address book export cancelled"));
+ return;
+
+ case 'a':
+ break;
+
+ case 'v':
+ write_attachment(qline, msgno, a, "EXPORT");
+ return;
+
+ default:
+ q_status_message(SM_ORDER, 3, 3, _("can't happen in export_vcard_att"));
+ return;
+ }
+
+ ps->mangled_footer = 1;
+ current = NULL;
+ how_many_selected = process_vcard_atts(ps->mail_stream, msgno, NULL,
+ a->body, a->number, &current);
+ /*
+ * Run through all of the list and run through
+ * the addresses in each ta->addr, writing them into a storage object.
+ * Then export to filesystem.
+ */
+ srctype = CharStar;
+ if(how_many_selected > 0 &&
+ (srcstore = so_get(srctype, NULL, EDIT_ACCESS)) != NULL){
+ ADDRESS *aa, *bb;
+ int are_some = 0;
+
+ for(current = first_taline(current);
+ current;
+ current = next_taline(current)){
+
+ for(aa = current->addr; aa; aa = aa->next){
+ bb = aa->next;
+ aa->next = NULL;
+ so_puts(srcstore, addr_list_string(aa, NULL, 0));
+ are_some++;
+ so_puts(srcstore, "\n");
+ aa->next = bb;
+ }
+ }
+
+ if(are_some)
+ simple_export(ps, so_text(srcstore), srctype, "addresses", NULL);
+ else
+ q_status_message(SM_ORDER, 0, 3, _("No addresses to export"));
+
+ so_give(&srcstore);
+ }
+ else{
+ if(how_many_selected == 0)
+ q_status_message(SM_ORDER, 0, 3, _("Nothing to export"));
+ else
+ q_status_message(SM_ORDER,0,2, _("Error allocating space"));
+ }
+}
+
+
+void
+take_to_export(struct pine *ps, LINES_TO_TAKE *lines_to_take)
+{
+ CONF_S *ctmp = NULL, *first_line = NULL;
+ OPT_SCREEN_S screen;
+ LINES_TO_TAKE *li;
+ char *help_title = _("HELP FOR TAKE EXPORT SCREEN");
+ char *p;
+ ScreenMode listmode = SingleMode;
+
+ for(li = lines_to_take; li; li = li->next){
+
+ new_confline(&ctmp);
+ ctmp->flags |= CF_STARTITEM;
+ if(li->flags & LT_NOSELECT)
+ ctmp->flags |= CF_NOSELECT;
+ else if(!first_line)
+ first_line = ctmp;
+
+ p = li->printval ? li->printval : "";
+
+ if(ctmp->flags & CF_NOSELECT)
+ ctmp->value = cpystr(p);
+ else{
+ size_t l;
+
+ /* 5 is for "[X] " */
+ l = strlen(p)+5;
+ ctmp->value = (char *)fs_get((l+1) * sizeof(char));
+ snprintf(ctmp->value, l+1, " %s", p);
+ ctmp->value[l] = '\0';
+ }
+
+ /* this points to data, it doesn't have its own copy */
+ ctmp->d.t.exportval = li->exportval ? li->exportval : NULL;
+ ctmp->d.t.selected = 0;
+ ctmp->d.t.listmode = &listmode;
+
+ ctmp->tool = take_export_tool;
+ ctmp->help_title = help_title;
+ ctmp->help = h_takeexport_screen;
+ ctmp->keymenu = &take_export_keymenu_sm;
+ }
+
+ if(!first_line)
+ q_status_message(SM_ORDER, 3, 3, _("No lines to export"));
+ else{
+ memset(&screen, 0, sizeof(screen));
+ conf_scroll_screen(ps, &screen, first_line, _("Take Export"), NULL, 0);
+ }
+}
+
+
+int
+take_export_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned int flags)
+{
+ CONF_S *ctmp;
+ int retval = 0;
+ int some_selected = 0, something_to_export = 0;
+ SourceType srctype;
+ STORE_S *srcstore = NULL;
+ char *prompt_msg;
+
+ switch(cmd){
+ case MC_TAKE :
+ srctype = CharStar;
+ if((srcstore = so_get(srctype, NULL, EDIT_ACCESS)) != NULL){
+ if(*(*cl)->d.t.listmode == SingleMode){
+ some_selected++;
+ if((*cl)->d.t.exportval && (*cl)->d.t.exportval[0]){
+ so_puts(srcstore, (*cl)->d.t.exportval);
+ so_puts(srcstore, "\n");
+ something_to_export++;
+ prompt_msg = "selection";
+ }
+ }
+ else{
+ /* go to first line */
+ for(ctmp = *cl; prev_confline(ctmp); ctmp = prev_confline(ctmp))
+ ;
+
+ for(; ctmp; ctmp = next_confline(ctmp))
+ if(!(ctmp->flags & CF_NOSELECT) && ctmp->d.t.selected){
+ some_selected++;
+ if(ctmp->d.t.exportval && ctmp->d.t.exportval[0]){
+ so_puts(srcstore, ctmp->d.t.exportval);
+ so_puts(srcstore, "\n");
+ something_to_export++;
+ prompt_msg = "selections";
+ }
+ }
+ }
+ }
+
+ if(!srcstore)
+ q_status_message(SM_ORDER, 0, 3, _("Error allocating space"));
+ else if(something_to_export)
+ simple_export(ps, so_text(srcstore), srctype, prompt_msg, NULL);
+ else if(!some_selected && *(*cl)->d.t.listmode == ListMode)
+ q_status_message(SM_ORDER, 0, 3, _("Use \"X\" to mark selections"));
+ else
+ q_status_message(SM_ORDER, 0, 3, _("Nothing to export"));
+
+ if(srcstore)
+ so_give(&srcstore);
+
+ break;
+
+ case MC_LISTMODE :
+ if(*(*cl)->d.t.listmode == SingleMode){
+ /*
+ * UnHide the checkboxes
+ */
+
+ *(*cl)->d.t.listmode = ListMode;
+
+ /* go to first line */
+ for(ctmp = *cl; prev_confline(ctmp); ctmp = prev_confline(ctmp))
+ ;
+
+ for(; ctmp; ctmp = next_confline(ctmp))
+ if(!(ctmp->flags & CF_NOSELECT) && ctmp->value){
+ ctmp->value[0] = '[';
+ ctmp->value[1] = ctmp->d.t.selected ? 'X' : SPACE;
+ ctmp->value[2] = ']';
+ ctmp->keymenu = &take_export_keymenu_lm;
+ }
+ }
+ else{
+ /*
+ * Hide the checkboxes
+ */
+
+ *(*cl)->d.t.listmode = SingleMode;
+
+ /* go to first line */
+ for(ctmp = *cl; prev_confline(ctmp); ctmp = prev_confline(ctmp))
+ ;
+
+ for(; ctmp; ctmp = next_confline(ctmp))
+ if(!(ctmp->flags & CF_NOSELECT) && ctmp->value){
+ ctmp->value[0] = ctmp->value[1] = ctmp->value[2] = SPACE;
+ ctmp->keymenu = &take_export_keymenu_sm;
+ }
+ }
+
+ ps->mangled_body = ps->mangled_footer = 1;
+ break;
+
+ case MC_CHOICE :
+ if((*cl)->value[1] == 'X'){
+ (*cl)->d.t.selected = 0;
+ (*cl)->value[1] = SPACE;
+ }
+ else{
+ (*cl)->d.t.selected = 1;
+ (*cl)->value[1] = 'X';
+ }
+
+ ps->mangled_body = 1;
+ break;
+
+ case MC_SELALL :
+ /* go to first line */
+ for(ctmp = *cl; prev_confline(ctmp); ctmp = prev_confline(ctmp))
+ ;
+
+ for(; ctmp; ctmp = next_confline(ctmp)){
+ if(!(ctmp->flags & CF_NOSELECT) && ctmp->value){
+ ctmp->d.t.selected = 1;
+ if(ctmp->value)
+ ctmp->value[1] = 'X';
+ }
+ }
+
+ ps->mangled_body = 1;
+ break;
+
+ case MC_UNSELALL :
+ /* go to first line */
+ for(ctmp = *cl; prev_confline(ctmp); ctmp = prev_confline(ctmp))
+ ;
+
+ for(; ctmp; ctmp = next_confline(ctmp)){
+ if(!(ctmp->flags & CF_NOSELECT) && ctmp->value){
+ ctmp->d.t.selected = 0;
+ if(ctmp->value)
+ ctmp->value[1] = SPACE;
+ }
+ }
+
+ ps->mangled_body = 1;
+ break;
+
+ case MC_EXIT :
+ retval = simple_exit_cmd(flags);
+ break;
+
+ default:
+ retval = -1;
+ break;
+ }
+
+ return(retval);
+}
+
+#ifdef ENABLE_LDAP
+/*
+ * Save an LDAP directory entry somewhere
+ *
+ * Args: ps -- pine struct
+ * e -- the entry to save
+ * save -- If this is set, then bypass the question about whether
+ * to save or export and just do the save.
+ */
+void
+save_ldap_entry(struct pine *ps, LDAP_CHOOSE_S *e, int save)
+{
+ char *fullname = NULL,
+ *address = NULL,
+ *first = NULL,
+ *last = NULL,
+ *comment = NULL;
+ char **cn = NULL,
+ **mail = NULL,
+ **sn = NULL,
+ **givenname = NULL,
+ **title = NULL,
+ **telephone = NULL,
+ **elecmail = NULL,
+ **note = NULL,
+ **ll;
+ int j,
+ export = 0;
+
+
+ dprint((2, "\n - save_ldap_entry - \n"));
+
+ if(e){
+ char *a;
+ BerElement *ber;
+
+ for(a = ldap_first_attribute(e->ld, e->selected_entry, &ber);
+ a != NULL;
+ a = ldap_next_attribute(e->ld, e->selected_entry, ber)){
+
+ dprint((9, " %s", a ? a : "?"));
+ if(strcmp(a, e->info_used->cnattr) == 0){
+ if(!cn)
+ cn = ldap_get_values(e->ld, e->selected_entry, a);
+ }
+ else if(strcmp(a, e->info_used->mailattr) == 0){
+ if(!mail)
+ mail = ldap_get_values(e->ld, e->selected_entry, a);
+ }
+ else if(strcmp(a, "electronicmail") == 0){
+ if(!elecmail)
+ elecmail = ldap_get_values(e->ld, e->selected_entry, a);
+ }
+ else if(strcmp(a, "comment") == 0){
+ if(!note)
+ note = ldap_get_values(e->ld, e->selected_entry, a);
+ }
+ else if(strcmp(a, e->info_used->snattr) == 0){
+ if(!sn)
+ sn = ldap_get_values(e->ld, e->selected_entry, a);
+ }
+ else if(strcmp(a, e->info_used->gnattr) == 0){
+ if(!givenname)
+ givenname = ldap_get_values(e->ld, e->selected_entry, a);
+ }
+ else if(strcmp(a, "telephonenumber") == 0){
+ if(!telephone)
+ telephone = ldap_get_values(e->ld, e->selected_entry, a);
+ }
+ else if(strcmp(a, "title") == 0){
+ if(!title)
+ title = ldap_get_values(e->ld, e->selected_entry, a);
+ }
+ }
+
+ if(!save){
+ j = radio_buttons(_("Save to address book or Export to filesystem ? "),
+ -FOOTER_ROWS(ps), save_or_export, 's', 'x',
+ h_ab_save_exp, RB_NORM);
+
+ switch(j){
+ case 'x':
+ q_status_message(SM_INFO, 0, 2, _("Address book save cancelled"));
+ break;
+
+ case 'e':
+ export++;
+ break;
+
+ case 's':
+ save++;
+ break;
+
+ default:
+ q_status_message(SM_ORDER, 3, 3,
+ "can't happen in save_ldap_ent");
+ break;
+ }
+ }
+ }
+
+ if(elecmail){
+ if(elecmail[0] && elecmail[0][0] && !mail)
+ mail = elecmail;
+ else
+ ldap_value_free(elecmail);
+
+ elecmail = NULL;
+ }
+
+ if(save){ /* save into the address book */
+ ADDRESS *addr;
+ char *d,
+ *fakedomain = "@";
+ size_t len;
+ char **cm;
+ int how_many_selected = 0;
+
+
+ if(cn && cn[0] && cn[0][0])
+ fullname = cpystr(cn[0]);
+ if(sn && sn[0] && sn[0][0])
+ last = cpystr(sn[0]);
+ if(givenname && givenname[0] && givenname[0][0])
+ first = cpystr(givenname[0]);
+
+ if(note && note[0] && note[0][0])
+ cm = note;
+ else
+ cm = title;
+
+ for(ll = cm, len = 0; ll && *ll; ll++)
+ len += strlen(*ll) + 2;
+
+ if(len){
+ comment = (char *)fs_get(len * sizeof(char));
+ d = comment;
+ ll = cm;
+ while(*ll){
+ sstrncpy(&d, *ll, len-(d-comment));
+ ll++;
+ if(*ll)
+ sstrncpy(&d, "; ", len-(d-comment));
+ }
+
+ comment[len-1] = '\0';
+ }
+
+ for(ll = mail, len = 0; ll && *ll; ll++)
+ len += strlen(*ll) + 1;
+
+ /* paste the email addresses together */
+ if(len){
+ address = (char *)fs_get(len * sizeof(char));
+ d = address;
+ ll = mail;
+ while(*ll){
+ sstrncpy(&d, *ll, len-(d-address));
+ ll++;
+ if(*ll)
+ sstrncpy(&d, ", ", len-(d-address));
+ }
+
+ address[len-1] = '\0';
+ }
+
+ addr = NULL;
+ if(address)
+ rfc822_parse_adrlist(&addr, address, fakedomain);
+
+ if(addr && fullname && !(first && last)){
+ if(addr->personal)
+ fs_give((void **)&addr->personal);
+
+ addr->personal = cpystr(fullname);
+ }
+
+ if(addr && e->serv && *e->serv){ /* save by reference */
+ char *dn, *edn = NULL;
+
+ dn = ldap_get_dn(e->ld, e->selected_entry);
+ if(dn){
+ edn = add_backslash_escapes(dn);
+ our_ldap_dn_memfree(dn);
+ }
+
+ if(e->serv && *e->serv && edn && *edn){
+ char buf[MAILTMPLEN+1];
+ char *backup_mail = NULL;
+
+ how_many_selected++;
+
+ if(addr && addr->mailbox && addr->host){
+ strncpy(buf, addr->mailbox, sizeof(buf)-2),
+ buf[sizeof(buf)-2] = '\0';
+ strncat(buf, "@", sizeof(buf)-1-strlen(buf));
+ strncat(buf, addr->host, sizeof(buf)-1-strlen(buf));
+ buf[sizeof(buf)-1] = '\0';
+ backup_mail = cpystr(buf);
+ }
+
+ /*
+ * We only need one addr which we will use to hold the
+ * pointer to the query.
+ */
+ if(addr->next)
+ mail_free_address(&addr->next);
+
+ if(addr->mailbox)
+ fs_give((void **)&addr->mailbox);
+ if(addr->host)
+ fs_give((void **)&addr->host);
+ if(addr->personal)
+ fs_give((void **)&addr->personal);
+
+ snprintf(buf, sizeof(buf),
+ "%s%s /base=%s/scope=base/cust=(objectclass=*)%s%s",
+ RUN_LDAP,
+ e->serv,
+ edn,
+ backup_mail ? "/mail=" : "",
+ backup_mail ? backup_mail : "");
+ buf[sizeof(buf)-1] = '\0';
+
+ if(backup_mail)
+ fs_give((void **)&backup_mail);
+
+ /*
+ * Put the search parameters in mailbox and put @ in
+ * host so that expand_address accepts it as an unqualified
+ * address and doesn't try to add localdomain.
+ */
+ addr->mailbox = cpystr(buf);
+ addr->host = cpystr("@");
+ }
+
+ if(edn)
+ fs_give((void **)&edn);
+ }
+ else{ /* save by value */
+ how_many_selected++;
+ if(!addr)
+ addr = mail_newaddr();
+ }
+
+ if(how_many_selected > 0){
+ TA_S *current = NULL;
+
+ /*
+ * The empty string for the last argument is significant.
+ * Fill_in_ta copies the whole adrlist into a single TA if
+ * there is a print string.
+ */
+ current = fill_in_ta(&current, addr, 1, "");
+ current = first_sel_taline(current);
+
+ if(first && last && current){
+ char *p;
+ size_t l;
+
+ l = strlen(last) + 2 + strlen(first);
+ p = (char *)fs_get((l+1) * sizeof(char));
+ snprintf(p, l, "%s, %s", last, first);
+ p[l] = '\0';
+ current->fullname = p;
+ convert_possibly_encoded_str_to_utf8(&current->fullname);
+ }
+
+ if(comment && current){
+ current->comment = cpystr(comment);
+ convert_possibly_encoded_str_to_utf8(&current->comment);
+ }
+
+ /*
+ * We don't want the personal name to make it into the address
+ * field in an LDAP: query sort of address, so move it
+ * out of the addr.
+ */
+ if(e->serv && *e->serv && current && fullname){
+ if(current->fullname)
+ fs_give((void **)&current->fullname);
+
+ current->fullname = fullname;
+ fullname = NULL;
+ convert_possibly_encoded_str_to_utf8(&current->fullname);
+ }
+
+ mail_free_address(&addr);
+
+ if(current)
+ takeaddr_bypass(ps, current, NULL);
+ else
+ q_status_message(SM_ORDER, 0, 4, "Nothing to save");
+
+ free_talines(&current);
+ }
+ else
+ q_status_message(SM_ORDER, 0, 4, "Nothing to save");
+
+ }
+ else if(export){ /* export to filesystem */
+ STORE_S *srcstore = NULL;
+ SourceType srctype;
+ static ESCKEY_S text_or_vcard[] = {
+ {'t', 't', "T", N_("Text")},
+ {'a', 'a', "A", N_("Addresses")},
+ {'v', 'v', "V", N_("VCard")},
+ {-1, 0, NULL, NULL}};
+
+ j = radio_buttons(
+ _("Export text of entry, address, or VCard format ? "),
+ -FOOTER_ROWS(ps), text_or_vcard, 't', 'x',
+ h_ldap_text_or_vcard, RB_NORM);
+
+ switch(j){
+ case 'x':
+ q_status_message(SM_INFO, 0, 2, _("Address book export cancelled"));
+ break;
+
+ case 't':
+ srctype = CharStar;
+ if(!(srcstore = prep_ldap_for_viewing(ps, e, srctype, NULL)))
+ q_status_message(SM_ORDER, 0, 2, _("Error allocating space"));
+ else{
+ (void)simple_export(ps_global, so_text(srcstore), srctype,
+ "text", NULL);
+ so_give(&srcstore);
+ }
+
+ break;
+
+ case 'a':
+ if(mail && mail[0] && mail[0][0]){
+
+ srctype = CharStar;
+ if(!(srcstore = so_get(srctype, NULL, EDIT_ACCESS)))
+ q_status_message(SM_ORDER,0,2, _("Error allocating space"));
+ else{
+ for(ll = mail; ll && *ll; ll++){
+ so_puts(srcstore, *ll);
+ so_puts(srcstore, "\n");
+ }
+
+ (void)simple_export(ps_global, so_text(srcstore),
+ srctype, "addresses", NULL);
+ so_give(&srcstore);
+ }
+ }
+
+ break;
+
+ case 'v':
+ srctype = CharStar;
+ if(!(srcstore = so_get(srctype, NULL, EDIT_ACCESS)))
+ q_status_message(SM_ORDER,0,2, _("Error allocating space"));
+ else{
+ gf_io_t pc;
+ VCARD_INFO_S *vinfo;
+
+ vinfo = (VCARD_INFO_S *)fs_get(sizeof(VCARD_INFO_S));
+ memset((void *)vinfo, 0, sizeof(VCARD_INFO_S));
+
+ if(cn && cn[0] && cn[0][0])
+ vinfo->fullname = copy_list_array(cn);
+
+ if(note && note[0] && note[0][0])
+ vinfo->note = copy_list_array(note);
+
+ if(title && title[0] && title[0][0])
+ vinfo->title = copy_list_array(title);
+
+ if(telephone && telephone[0] && telephone[0][0])
+ vinfo->tel = copy_list_array(telephone);
+
+ if(mail && mail[0] && mail[0][0])
+ vinfo->email = copy_list_array(mail);
+
+ if(sn && sn[0] && sn[0][0])
+ vinfo->last = cpystr(sn[0]);
+
+ if(givenname && givenname[0] && givenname[0][0])
+ vinfo->first = cpystr(givenname[0]);
+
+ gf_set_so_writec(&pc, srcstore);
+
+ write_single_vcard_entry(ps_global, pc, vinfo);
+
+ free_vcard_info(&vinfo);
+
+ (void)simple_export(ps_global, so_text(srcstore),
+ srctype, "vcard text", NULL);
+ so_give(&srcstore);
+ }
+
+ break;
+
+ default:
+ q_status_message(SM_ORDER, 3, 3, "can't happen in text_or_vcard");
+ break;
+ }
+ }
+
+ if(cn)
+ ldap_value_free(cn);
+ if(mail)
+ ldap_value_free(mail);
+ if(elecmail)
+ ldap_value_free(elecmail);
+ if(note)
+ ldap_value_free(note);
+ if(sn)
+ ldap_value_free(sn);
+ if(givenname)
+ ldap_value_free(givenname);
+ if(telephone)
+ ldap_value_free(telephone);
+ if(title)
+ ldap_value_free(title);
+ if(fullname)
+ fs_give((void **)&fullname);
+ if(address)
+ fs_give((void **)&address);
+ if(first)
+ fs_give((void **)&first);
+ if(last)
+ fs_give((void **)&last);
+ if(comment)
+ fs_give((void **)&comment);
+}
+#endif /* ENABLE_LDAP */
+
+
+#ifdef _WINDOWS
+
+int
+ta_scroll_up(count)
+ long count;
+{
+ if(count<0)
+ return(ta_scroll_down(-count));
+ else if (count){
+ long i=count;
+ TA_S *next_sel;
+
+ while(i && ta_screen->top_line->next){
+ if(ta_screen->top_line == ta_screen->current){
+ if(next_sel = next_sel_taline(ta_screen->current)){
+ ta_screen->current = next_sel;
+ ta_screen->top_line = next_taline(ta_screen->top_line);
+ i--;
+ }
+ else i = 0;
+ }
+ else{
+ ta_screen->top_line = next_taline(ta_screen->top_line);
+ i--;
+ }
+ }
+ }
+ return(TRUE);
+}
+
+int
+ta_scroll_down(count)
+ long count;
+{
+ if(count < 0)
+ return(ta_scroll_up(-count));
+ else if (count){
+ long i,dline;
+ long page_size = ps_global->ttyo->screen_rows -
+ FOOTER_ROWS(ps_global) - HEADER_ROWS(ps_global);
+ TA_S *ctmp;
+ TA_S *first = first_taline(ta_screen->top_line);
+
+ i=count;
+ dline=0;
+ for(ctmp = ta_screen->top_line;
+ ctmp != ta_screen->current; ctmp = next_taline(ctmp))
+ dline++;
+ while(i && ta_screen->top_line != first){
+ ta_screen->top_line = pre_taline(ta_screen->top_line);
+ i--;
+ dline++;
+ if(dline >= page_size){
+ ctmp = pre_sel_taline(ta_screen->current);
+ if(ctmp == NULL){
+ i = 0;
+ ta_screen->top_line = next_taline(ta_screen->top_line);
+ }
+ else {
+ for(; ctmp != ta_screen->current;
+ ta_screen->current = pre_taline(ta_screen->current))
+ dline--;
+ }
+ }
+ }
+ }
+ return(TRUE);
+}
+
+int ta_scroll_to_pos(line)
+ long line;
+{
+ TA_S *ctmp;
+ int dline;
+
+ for(dline = 0, ctmp = first_taline(ta_screen->top_line);
+ ctmp != ta_screen->top_line; ctmp = next_taline(ctmp))
+ dline++;
+
+ if (!ctmp)
+ dline = 1;
+
+ return(ta_scroll_up(line - dline));
+}
+
+int
+ta_scroll_callback (cmd, scroll_pos)
+ int cmd;
+ long scroll_pos;
+{
+ int paint = TRUE;
+ long page_size = ps_global->ttyo->screen_rows - HEADER_ROWS(ps_global)
+ - FOOTER_ROWS(ps_global);
+
+ switch (cmd) {
+ case MSWIN_KEY_SCROLLUPLINE:
+ paint = ta_scroll_down (scroll_pos);
+ break;
+
+ case MSWIN_KEY_SCROLLDOWNLINE:
+ paint = ta_scroll_up (scroll_pos);
+ break;
+
+ case MSWIN_KEY_SCROLLUPPAGE:
+ paint = ta_scroll_down (page_size);
+ break;
+
+ case MSWIN_KEY_SCROLLDOWNPAGE:
+ paint = ta_scroll_up (page_size);
+ break;
+
+ case MSWIN_KEY_SCROLLTO:
+ paint = ta_scroll_to_pos (scroll_pos);
+ break;
+ }
+
+ if(paint)
+ update_takeaddr_screen(ps_global, ta_screen->current, ta_screen, (Pos *)NULL);
+
+ return(paint);
+}
+
+#endif /* _WINDOWS */
diff --git a/alpine/takeaddr.h b/alpine/takeaddr.h
new file mode 100644
index 00000000..37890023
--- /dev/null
+++ b/alpine/takeaddr.h
@@ -0,0 +1,40 @@
+/*
+ * $Id: takeaddr.h 1012 2008-03-26 00:44:22Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_TAKEADDR_INCLUDED
+#define PINE_TAKEADDR_INCLUDED
+
+
+#include "../pith/takeaddr.h"
+#include "../pith/state.h"
+#include "../pith/adrbklib.h"
+#include "../pith/msgno.h"
+#include "../pith/mailpart.h"
+#include "../pith/ldap.h"
+
+
+/* exported prototypes */
+int cmd_take_addr(struct pine *, MSGNO_S *, int);
+void take_this_one_entry(struct pine *, TA_STATE_S **, AdrBk *, long);
+void save_vcard_att(struct pine *, int, long, ATTACH_S *);
+void take_to_export(struct pine *, LINES_TO_TAKE *);
+PerAddrBook *setup_for_addrbook_add(SAVE_STATE_S *, int, char *);
+#ifdef ENABLE_LDAP
+void save_ldap_entry(struct pine *, LDAP_CHOOSE_S *, int);
+#endif
+
+
+#endif /* PINE_TAKEADDR_INCLUDED */
diff --git a/alpine/talk.h b/alpine/talk.h
new file mode 100644
index 00000000..86337bc0
--- /dev/null
+++ b/alpine/talk.h
@@ -0,0 +1,37 @@
+/*
+ * $Id: talk.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_TALK_INCLUDED
+#define PITH_TALK_INCLUDED
+
+
+/*
+ * Macros to aid hack to turn off talk permission.
+ * Bit 020 is usually used to turn off talk permission, we turn off
+ * 002 also for good measure, since some mesg commands seem to do that.
+ */
+#define TALK_BIT 020 /* mode bits */
+#define GM_BIT 002
+#define TMD_CLEAR 0 /* functions */
+#define TMD_SET 1
+#define TMD_RESET 2
+#define allow_talk(p) tty_chmod((p), TALK_BIT, TMD_SET)
+#define disallow_talk(p) tty_chmod((p), TALK_BIT|GM_BIT, TMD_CLEAR)
+
+
+/* exported protoypes */
+
+
+#endif /* PITH_TALK_INCLUDED */
diff --git a/alpine/titlebar.c b/alpine/titlebar.c
new file mode 100644
index 00000000..7f0d2cc3
--- /dev/null
+++ b/alpine/titlebar.c
@@ -0,0 +1,1236 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: titlebar.c 1075 2008-06-04 00:19:39Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "headers.h"
+#include "titlebar.h"
+#include "folder.h"
+#include "../pith/state.h"
+#include "../pith/bitmap.h"
+#include "../pith/msgno.h"
+#include "../pith/thread.h"
+#include "../pith/conf.h"
+#include "../pith/init.h"
+#include "../pith/sort.h"
+#include "../pith/news.h"
+#include "../pith/util.h"
+#include "../pith/folder.h"
+
+/*
+ * Internal prototypes
+ */
+int digit_count(long);
+void output_titlebar(TITLE_S *);
+char sort_letter(SortOrder);
+char *percentage(long, long, int);
+
+
+/*
+ * some useful macros...
+ */
+#define MS_DEL (0x01)
+#define MS_NEW (0x02)
+#define MS_ANS (0x04)
+#define MS_FWD (0x08)
+
+#define STATUS_BITS(X) (!(X && (X)->valid) ? 0 \
+ : (X)->deleted ? MS_DEL \
+ : (X)->answered ? MS_ANS \
+ : (as.stream && user_flag_is_set(as.stream, (X)->msgno, FORWARDED_FLAG)) ? MS_FWD \
+ : (as.stream \
+ && (ps_global->unseen_in_view \
+ || (!(X)->seen \
+ && (!IS_NEWS(as.stream) \
+ || F_ON(F_FAKE_NEW_IN_NEWS, \
+ ps_global))))) \
+ ? MS_NEW : 0)
+
+#define BAR_STATUS(X) (((X) & MS_DEL) ? "DEL" \
+ : ((X) & MS_ANS) ? "ANS" \
+ : ((X) & MS_FWD) ? "FWD" \
+ : (as.stream \
+ && (!IS_NEWS(as.stream) \
+ || F_ON(F_FAKE_NEW_IN_NEWS, ps_global)) \
+ && ((X) & MS_NEW)) ? "NEW" : " ")
+
+
+static struct titlebar_state {
+ MAILSTREAM *stream;
+ MSGNO_S *msgmap;
+ char *title,
+ *folder_name,
+ *context_name;
+ long current_msg,
+ current_line,
+ current_thrd,
+ total_lines;
+ int msg_state,
+ cur_mess_col,
+ del_column,
+ percent_column,
+ page_column,
+ screen_cols;
+ enum {Normal, OnlyRead, Closed} stream_status;
+ TitleBarType style;
+ TITLE_S titlecontainer;
+} as, titlebar_stack;
+
+
+static int titlebar_is_dirty = 1;
+
+
+void
+push_titlebar_state(void)
+{
+ titlebar_stack = as;
+ as.folder_name = NULL; /* erase knowledge of malloc'd data */
+ as.context_name = NULL;
+}
+
+
+void
+pop_titlebar_state(void)
+{
+ /* guard against case where push pushed no state */
+ if(titlebar_stack.style != TitleBarNone){
+ fs_give((void **)&(as.folder_name)); /* free malloc'd values */
+ fs_give((void **)&(as.context_name));
+ as = titlebar_stack;
+ }
+}
+
+
+int
+digit_count(long int n)
+{
+ int i;
+
+ return((n > 9)
+ ? (1 + (((i = digit_count(n / 10L)) == 3 || i == 7) ? i+1 : i))
+ : 1);
+}
+
+
+void
+mark_titlebar_dirty(void)
+{
+ titlebar_is_dirty = 1;
+}
+
+
+/*----------------------------------------------------------------------
+ Sets up style and contents of current titlebar line
+
+ All of the text is assumed to be UTF-8 text, which probably isn't
+ true yet.
+
+ Args: title -- The title that appears in the center of the line
+ This title is in UTF-8 characters.
+ display_on_screen -- flag whether to display on screen or generate
+ string
+ style -- The format/style of the titlebar line
+ msgmap -- MSGNO_S * to selected message map
+ current_pl -- The current page or line number
+ total_pl -- The total line or page count
+
+ Set the contents of the anchor line. It's called an anchor line
+because it's always present and titlebars the user. This accesses a
+number of global variables, but doesn't change any. There are several
+different styles of the titlebar line.
+
+It's OK to call this with a bogus current message - it is only used
+to look up status of current message
+
+Formats only change the right section (part3).
+ FolderName: "<folder>" xx Messages
+ MessageNumber: "<folder>" message x,xxx of x,xxx XXX
+ TextPercent: line xxx of xxx xx%
+ MsgTextPercent: "<folder>" message x,xxx of x,xxx xx% XXX
+ FileTextPercent: "<filename>" line xxx of xxx xx%
+
+Several strings and column numbers are saved so later updates to the status
+line for changes in message number or percentage can be done efficiently.
+ ----*/
+
+char *
+set_titlebar(char *title, MAILSTREAM *stream, CONTEXT_S *cntxt, char *folder,
+ MSGNO_S *msgmap, int display_on_screen, TitleBarType style,
+ long int current_pl, long int total_pl, COLOR_PAIR *color)
+{
+ struct variable *vars = ps_global->vars;
+ MESSAGECACHE *mc = NULL;
+ PINETHRD_S *thrd = NULL;
+ TITLE_S *tc;
+
+ dprint((9, "set_titlebar - style: %d current message cnt:%ld",
+ style, mn_total_cur(msgmap)));
+ dprint((9, " current_pl: %ld total_pl: %ld\n",
+ current_pl, total_pl));
+
+ as.current_msg = (mn_get_total(msgmap) > 0L)
+ ? MAX(0, mn_get_cur(msgmap)) : 0L;
+ as.msgmap = msgmap;
+ as.style = style;
+ as.title = title;
+ as.stream = stream;
+ as.stream_status = (!as.stream || (sp_dead_stream(as.stream)))
+ ? Closed : as.stream->rdonly ? OnlyRead : Normal;
+
+ if(ps_global->first_open_was_attempted
+ && as.stream_status == Closed
+ && VAR_TITLECLOSED_FORE_COLOR && VAR_TITLECLOSED_BACK_COLOR){
+ memset(&as.titlecontainer.color, 0, sizeof(as.titlecontainer.color));
+ strncpy(as.titlecontainer.color.fg,
+ VAR_TITLECLOSED_FORE_COLOR, MAXCOLORLEN);
+ as.titlecontainer.color.fg[MAXCOLORLEN] = '\0';
+ strncpy(as.titlecontainer.color.bg,
+ VAR_TITLECLOSED_BACK_COLOR, MAXCOLORLEN);
+ as.titlecontainer.color.bg[MAXCOLORLEN] = '\0';
+ }
+ else{
+ if(color){
+ memset(&as.titlecontainer.color, 0, sizeof(as.titlecontainer.color));
+ if(color->fg){
+ strncpy(as.titlecontainer.color.fg, color->fg, MAXCOLORLEN);
+ as.titlecontainer.color.fg[MAXCOLORLEN] = '\0';
+ }
+
+ if(color->bg){
+ strncpy(as.titlecontainer.color.bg, color->bg, MAXCOLORLEN);
+ as.titlecontainer.color.bg[MAXCOLORLEN] = '\0';
+ }
+ }
+ else{
+ memset(&as.titlecontainer.color, 0, sizeof(as.titlecontainer.color));
+ if(VAR_TITLE_FORE_COLOR){
+ strncpy(as.titlecontainer.color.fg,
+ VAR_TITLE_FORE_COLOR, MAXCOLORLEN);
+ as.titlecontainer.color.fg[MAXCOLORLEN] = '\0';
+ }
+
+ if(VAR_TITLE_BACK_COLOR){
+ strncpy(as.titlecontainer.color.bg,
+ VAR_TITLE_BACK_COLOR, MAXCOLORLEN);
+ as.titlecontainer.color.bg[MAXCOLORLEN] = '\0';
+ }
+ }
+ }
+
+ if(as.folder_name)
+ fs_give((void **)&as.folder_name);
+
+ if(folder){
+ unsigned char *fname;
+ fname = folder_name_decoded((unsigned char *) folder);
+ if(!strucmp(folder, ps_global->inbox_name) && cntxt == ps_global->context_list)
+ as.folder_name = cpystr(pretty_fn(fname ? (char *) fname : folder));
+ else
+ as.folder_name = cpystr(fname ? (char *) fname : folder);
+ if(fname) fs_give((void **)&fname);
+ }
+
+ if(!as.folder_name)
+ as.folder_name = cpystr("");
+
+ if(as.context_name)
+ fs_give((void **)&as.context_name);
+
+ /*
+ * Handle setting up the context if appropriate.
+ */
+ if(cntxt && context_isambig(folder) && ps_global->context_list->next
+ && (strucmp(as.folder_name, ps_global->inbox_name) || cntxt != ps_global->context_list)){
+ /*
+ * if there's more than one context and the current folder
+ * is in it (ambiguous name), set the context name...
+ */
+ as.context_name = cpystr(cntxt->nickname
+ ? cntxt->nickname
+ : cntxt->context);
+ }
+
+ if(!as.context_name)
+ as.context_name = cpystr("");
+
+ if(as.stream && style != FolderName
+ && style != ThrdIndex && as.current_msg > 0L) {
+ long rawno;
+
+ if((rawno = mn_m2raw(msgmap, as.current_msg)) > 0L
+ && rawno <= as.stream->nmsgs
+ && !((mc = mail_elt(as.stream, rawno)) && mc->valid)){
+ pine_mail_fetch_flags(as.stream, long2string(rawno), NIL);
+ mc = mail_elt(as.stream, rawno);
+ }
+ }
+
+ if(style == ThrdIndex || style == ThrdMsgNum || style == ThrdMsgPercent){
+ if(mn_get_total(msgmap) > 0L)
+ thrd = fetch_thread(stream, mn_m2raw(msgmap, mn_get_cur(msgmap)));
+
+ if(thrd && thrd->top && thrd->top != thrd->rawno)
+ thrd = fetch_thread(stream, thrd->top);
+
+ if(thrd)
+ as.current_thrd = thrd->thrdno;
+ }
+
+ switch(style) {
+ case ThrdIndex:
+ as.total_lines = msgmap->max_thrdno;
+ break;
+
+ case TextPercent:
+ case MsgTextPercent:
+ case FileTextPercent :
+ case ThrdMsgPercent:
+ as.total_lines = total_pl;
+ as.current_line = current_pl;
+ /* fall through to set state */
+ case ThrdMsgNum:
+ case MessageNumber:
+ as.msg_state = STATUS_BITS(mc);
+
+ case FolderName: /* nothing more to do for this one */
+ break;
+ default:
+ break;
+ }
+
+ tc = format_titlebar();
+ if(display_on_screen)
+ output_titlebar(tc);
+
+ return(tc->titlebar_line);
+}
+
+
+void
+redraw_titlebar(void)
+{
+ output_titlebar(format_titlebar());
+}
+
+
+void
+output_titlebar(TITLE_S *tc)
+{
+ COLOR_PAIR *lastc = NULL, *newcolor;
+
+ if(ps_global->ttyo
+ && (ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)) < 1){
+ titlebar_is_dirty = 1;
+ return;
+ }
+
+ newcolor = tc ? &tc->color : NULL;
+
+ if(newcolor)
+ lastc = pico_set_colorp(newcolor, PSC_REV | PSC_RET);
+
+ if(tc && tc->titlebar_line)
+ PutLine0(0, 0, tc->titlebar_line);
+
+ if(lastc){
+ (void)pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+
+ fflush(stdout);
+}
+
+
+void
+titlebar_stream_closing(MAILSTREAM *stream)
+{
+ if(stream == as.stream)
+ as.stream = NULL;
+}
+
+
+/* caller should free returned color pair */
+COLOR_PAIR *
+current_titlebar_color(void)
+{
+ TITLE_S *tc;
+ COLOR_PAIR *col;
+ COLOR_PAIR *the_color = NULL;
+
+ tc = format_titlebar();
+ col = tc ? &tc->color : NULL;
+
+ if(col && col->fg && col->fg[0] && col->bg && col->bg[0])
+ the_color = new_color_pair(col->fg, col->bg);
+
+ return(the_color);
+}
+
+
+/*----------------------------------------------------------------------
+ Redraw or draw the top line, the title bar
+
+ The titlebar has Four fields:
+ 1) "Version" of fixed length and is always positioned two spaces
+ in from left display edge.
+ 2) "Location" which is fixed for each style of titlebar and
+ is positioned two spaces from the right display edge
+ 3) "Title" which is of fixed length, and is centered if
+ there's space
+ 4) "Folder" whose existence depends on style and which can
+ have it's length adjusted (within limits) so it will
+ equally share the space between 1) and 2) with the
+ "Title". The rule for existence is that in the
+ space between 1) and 2) there must be one space between
+ 3) and 4) AND at least 50% of 4) must be displayed.
+
+ Returns - Formatted title bar
+ ----*/
+TITLE_S *
+format_titlebar(void)
+{
+ char version[50], fold_tmp[6*MAXPATH+1], *titlebar_line,
+ loc1[200], loc_label[10], *thd_label, *ss_string, *q,
+ *plus, *loc2 = "", title[200];
+ int title_len = 0, ver_len, loc1_len = 0, loc2_len = 0, fold_len = 0, num_len,
+ s1 = 0, s2 = 0, s3 = 0, s4 = 0, s5 = 0, s6 = 0, tryloc = 1,
+ cur_mess_col_offset = -1, percent_column_offset = -1, page_column_offset = -1,
+ ss_len, thd_len, is_context, avail, extra;
+
+ titlebar_is_dirty = 0;
+
+ if(!as.title)
+ return NULL;
+
+ if(!as.folder_name)
+ as.folder_name = cpystr("");
+
+ if(!as.context_name)
+ as.context_name = cpystr("");
+
+ /*
+ * Full blown title looks like:
+ *
+ * | LV vers VT title TF folder FL location LR |
+ */
+#define LV 2 /* space between Left edge and Version, must be >= 2 */
+#define VT 3 /* space between Version and Title */
+#define TF 1 /* space between Title and Folder */
+#define FL 2 /* space between Folder and Location */
+#define LR 2 /* space between Location and Right edge, >= 2 */
+/* half of n but round up */
+#define HRU(n) (((n) <= 0) ? 0 : (((n)%2) ? ((n)+1)/2 : (n)/2))
+/* half of n but round down */
+#define HRD(n) (((n) <= 0) ? 0 : ((n)/2))
+
+ titlebar_line = as.titlecontainer.titlebar_line;
+
+ avail = MIN(ps_global->ttyo->screen_cols, MAX_SCREEN_COLS);
+
+ /* initialize */
+ as.del_column = -1;
+ as.cur_mess_col = -1;
+ as.percent_column = -1;
+ as.page_column = -1;
+
+ is_context = as.context_name ? strlen(as.context_name) : 0;
+
+ snprintf(version, sizeof(version), "ALPINE %s", ALPINE_VERSION);
+ version[sizeof(version)-1] = '\0';
+ ver_len = (int) utf8_width(version); /* fixed version field width */
+
+ title[0] = '\0';
+ if(as.title){
+ strncpy(title, as.title, sizeof(title));
+ title[sizeof(title)-1] = '\0';
+ }
+
+ /* Add Sort indicator to title */
+ if(F_ON(F_SHOW_SORT, ps_global) &&
+ !(as.style == FileTextPercent || as.style == TextPercent)){
+ SortOrder current_sort;
+ int current_rev;
+ char let;
+
+ current_sort = mn_get_sort(ps_global->msgmap);
+ current_rev = mn_get_revsort(ps_global->msgmap);
+
+ /* turn current_sort into a letter */
+ let = sort_letter(current_sort);
+ if(let == 'A' && current_rev){
+ let = 'R';
+ current_rev = 0;
+ }
+
+ snprintf(title+strlen(title), sizeof(title)-strlen(title),
+ " [%s%c]", current_rev ? "R" : "", let);
+ title[sizeof(title)-1] = '\0';
+ }
+
+ title_len = (int) utf8_width(title); /* title field width */
+
+ s1 = MAX(MIN(LV, avail), 0); /* left two spaces */
+ avail -= s1;
+
+ s6 = MAX(MIN(LR, avail), 0); /* right two spaces */
+ avail -= s6;
+
+ title_len = MAX(MIN(title_len, avail), 0);
+ avail -= title_len;
+
+ if(ver_len + VT > avail){ /* can only fit title */
+ ver_len = 0;
+
+ s2 = MAX(MIN(HRD(avail), avail), 0);
+ avail -= s2;
+
+ s3 = MAX(0, avail);
+ }
+ else{
+ s2 = MAX(MIN(VT, avail), 0);
+ avail -= s2;
+
+ ver_len = MAX(MIN(ver_len, avail), 0);
+ avail -= ver_len;
+
+try_smaller_loc:
+
+ /*
+ * set location field's length and value based on requested style
+ */
+ if(as.style == ThrdIndex)
+ /* TRANSLATORS: In titlebar, Thd is an abbreviation for Thread, Msg for Message.
+ They are used when there isn't enough space so need to be short.
+ The formatting isn't very flexible. These come before the number
+ of the message or thread, as in
+ Message 17
+ when reading message number 17. */
+ snprintf(loc_label, sizeof(loc_label), "%s ", (is_context || tryloc==2) ? _("Thd") : _("Thread"));
+ else
+ snprintf(loc_label, sizeof(loc_label), "%s ", (is_context || tryloc==2) ? _("Msg") : _("Message"));
+
+ if(tryloc == 3 && as.style != FolderName && mn_get_total(as.msgmap))
+ loc_label[0] = '\0';
+
+ loc_label[sizeof(loc_label)-1] = '\0';
+
+ if(as.style == ThrdMsgNum || as.style == ThrdMsgPercent){
+ thd_label = is_context ? _("Thd") : _("Thread");
+ thd_len = (int) utf8_width(thd_label);
+ }
+
+ loc1_len = (int) utf8_width(loc_label); /* initial length */
+
+ if(!mn_get_total(as.msgmap)){
+ loc_label[strlen(loc_label)-1] = 's';
+ snprintf(loc1, sizeof(loc1), "%s %s", _("No"), loc_label);
+ loc1[sizeof(loc1)-1]= '\0';
+ }
+ else{
+ switch(as.style){
+ case FolderName : /* "x,xxx <loc_label>s" */
+ if(*loc_label){
+ if(mn_get_total(as.msgmap) != 1)
+ loc_label[strlen(loc_label)-1] = 's';
+ else
+ loc_label[strlen(loc_label)-1] = '\0';
+ }
+
+ snprintf(loc1, sizeof(loc1), "%s %s", comatose(mn_get_total(as.msgmap)), loc_label);
+ loc1[sizeof(loc1)-1]= '\0';
+ break;
+
+ case MessageNumber : /* "<loc_label> xxx of xxx DEL" */
+ num_len = strlen(comatose(mn_get_total(as.msgmap)));
+ cur_mess_col_offset = loc1_len;
+ snprintf(loc1, sizeof(loc1), "%s%*.*s of %s", loc_label,
+ num_len, num_len,
+ comatose(as.current_msg),
+ comatose(mn_get_total(as.msgmap)));
+ loc1[sizeof(loc1)-1]= '\0';
+ loc2 = BAR_STATUS(as.msg_state);
+ loc2_len = 3;
+ break;
+
+ case ThrdIndex : /* "<loc_label> xxx of xxx" */
+ num_len = strlen(comatose(as.total_lines));
+ cur_mess_col_offset = loc1_len;
+ snprintf(loc1, sizeof(loc1), "%s%*.*s of %s", loc_label,
+ num_len, num_len,
+ comatose(as.current_thrd),
+ comatose(as.total_lines));
+ loc1[sizeof(loc1)-1]= '\0';
+ break;
+
+ case ThrdMsgNum : /* "<loc_label> xxx in Thd xxx DEL" */
+ num_len = strlen(comatose(mn_get_total(as.msgmap)));
+ cur_mess_col_offset = loc1_len;
+ snprintf(loc1, sizeof(loc1), "%s%*.*s in %s %s", loc_label,
+ num_len, num_len,
+ comatose(as.current_msg),
+ thd_label,
+ comatose(as.current_thrd));
+ loc1[sizeof(loc1)-1]= '\0';
+ loc2 = BAR_STATUS(as.msg_state);
+ loc2_len = 3;
+ break;
+
+ case MsgTextPercent : /* "<loc_label> xxx of xxx xx% DEL" */
+ num_len = strlen(comatose(mn_get_total(as.msgmap)));
+ cur_mess_col_offset = loc1_len;
+ percent_column_offset = 3;
+ snprintf(loc1, sizeof(loc1), "%s%*.*s of %s %s", loc_label,
+ num_len, num_len,
+ comatose(as.current_msg),
+ comatose(mn_get_total(as.msgmap)),
+ percentage(as.current_line, as.total_lines, 1));
+ loc1[sizeof(loc1)-1]= '\0';
+ loc2 = BAR_STATUS(as.msg_state);
+ loc2_len = 3;
+ break;
+
+ case ThrdMsgPercent : /* "<loc_label> xxx in Thd xxx xx% DEL" */
+ num_len = strlen(comatose(mn_get_total(as.msgmap)));
+ cur_mess_col_offset = loc1_len;
+ percent_column_offset = 3;
+ snprintf(loc1, sizeof(loc1), "%s%*.*s in %s %s %s", loc_label,
+ num_len, num_len,
+ comatose(as.current_msg),
+ thd_label,
+ comatose(as.current_thrd),
+ percentage(as.current_line, as.total_lines, 1));
+ loc1[sizeof(loc1)-1]= '\0';
+ loc2 = BAR_STATUS(as.msg_state);
+ loc2_len = 3;
+ break;
+
+ case TextPercent :
+ /* NOTE: no fold_tmp setup below for TextPercent style */
+ case FileTextPercent : /* "Line xxx of xxx xx%" */
+ num_len = strlen(comatose(as.total_lines));
+ page_column_offset = 5;
+ percent_column_offset = 3;
+ snprintf(loc1, sizeof(loc1), "Line %*.*s of %s %s",
+ num_len, num_len,
+ comatose(as.current_line),
+ comatose(as.total_lines),
+ percentage(as.current_line, as.total_lines, 1));
+ loc1[sizeof(loc1)-1]= '\0';
+ break;
+ default:
+ break;
+ }
+ }
+
+ loc1_len = utf8_width(loc1);
+
+ if(loc1_len + loc2_len + ((loc2_len > 0) ? 1 : 0) >= avail){ /* can't fit location in */
+ if(tryloc < 3){
+ tryloc++;
+ goto try_smaller_loc;
+ }
+
+ loc1_len = loc2_len = 0;
+
+ avail += s2; /* re-allocate s2 to center title */
+
+ s2 = MAX(MIN(HRD(avail), avail), 0);
+ avail -= s2;
+
+ s3 = MAX(0, avail);
+ }
+ else{
+ loc1_len = MAX(MIN(loc1_len, avail), 0);
+ avail -= loc1_len;
+
+ loc2_len = MAX(MIN(loc2_len, avail), 0);
+ avail -= loc2_len;
+
+ if(loc2_len > 0){
+ s5 = MAX(MIN(1, avail), 0);
+ avail -= s5;
+ }
+ else
+ s5 = 0;
+
+ s3 = MAX(MIN(TF, avail), 0);
+ avail -= s3;
+
+ s4 = MAX(MIN(FL, avail), 0);
+ avail -= s4;
+
+ if(avail){
+ /* TRANSLATORS: it might say READONLY or CLOSED in the titlebar, referring to
+ the current folder. */
+ ss_string = as.stream_status == Closed ? _("(CLOSED)") :
+ (as.stream_status == OnlyRead
+ && !IS_NEWS(as.stream))
+ ? _("(READONLY)") : "";
+ ss_len = (int) utf8_width(ss_string);
+
+ /* figure folder_length and what's to be displayed */
+ fold_tmp[0] = '\0';
+ if(as.style == FileTextPercent || as.style == TextPercent){
+ if(as.style == FileTextPercent){
+ extra = (int) utf8_width("File: ");
+ fold_len = (int) utf8_width(as.folder_name);
+ if(fold_len + extra <= avail){ /* all of folder fit? */
+ strncpy(fold_tmp, "File: ", sizeof(fold_tmp));
+ q = fold_tmp + strlen(fold_tmp);
+ strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
+ }
+ else if(HRU(fold_len) + extra+3 <= avail){
+ /*
+ * fold_tmp = ...partial_folder_name
+ */
+ strncpy(fold_tmp, "File: ...", sizeof(fold_tmp));
+ q = fold_tmp + strlen(fold_tmp);
+ (void) utf8_to_width_rhs(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp), avail-(extra+3));
+ }
+ }
+ /* else leave folder/file name blank */
+ }
+ else{
+ int ct_len;
+
+ fold_len = (int) utf8_width(as.folder_name);
+
+ if(is_context
+ && as.stream_status != Closed
+ && (ct_len = (int) utf8_width(as.context_name))){
+
+ extra = 3; /* length from "<" ">" and SPACE */
+
+ if(ct_len + fold_len + ss_len + extra <= avail){
+ q = fold_tmp;
+ *q++ = '<';
+ strncpy(q, as.context_name, sizeof(fold_tmp)-(q-fold_tmp));
+ q += strlen(q);
+ *q++ = '>';
+ *q++ = ' ';
+ strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
+ q += strlen(q);
+ strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
+ }
+ else if(ct_len + fold_len + ss_len + extra <= avail){
+ q = fold_tmp;
+ *q++ = '<';
+ strncpy(q, as.context_name, sizeof(fold_tmp)-(q-fold_tmp));
+ q += strlen(q);
+ *q++ = '>';
+ *q++ = ' ';
+ strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
+ q += strlen(q);
+ strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
+ }
+ else if(HRU(ct_len) + fold_len + ss_len + extra <= avail){
+ q = fold_tmp;
+ *q++ = '<';
+ q += utf8_pad_to_width(q, as.context_name, sizeof(fold_tmp)-(q-fold_tmp), avail-(fold_len+ss_len+extra), 1);
+ *q++ = '>';
+ *q++ = ' ';
+ strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
+ q += strlen(q);
+ strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
+ }
+ else if(HRU(ct_len) + HRU(fold_len) + ss_len + extra <= avail){
+ q = fold_tmp;
+ *q++ = '<';
+ q += utf8_pad_to_width(q, as.context_name, sizeof(fold_tmp)-(q-fold_tmp), HRU(ct_len), 1);
+ *q++ = '>';
+ *q++ = ' ';
+ q += utf8_to_width_rhs(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp), avail-(HRU(ct_len)+ss_len+extra));
+ strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
+ }
+ else if(ss_len > 0 && ss_len <= avail){
+ q = fold_tmp;
+ strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
+ }
+ /* else leave it out */
+ }
+ else{
+ /* TRANSLATORS: the name of the open folder follows this in the titlebar */
+ extra = strlen(_("Folder: "));
+ if(fold_len + ss_len + extra <= avail){
+ q = fold_tmp;
+ strncpy(q, "Folder: ", sizeof(fold_tmp)-(q-fold_tmp));
+ q += strlen(q);
+ strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
+ q += strlen(q);
+ strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
+ }
+ else{
+ if(fold_len + ss_len <= avail){
+ q = fold_tmp;
+ strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
+ q += strlen(q);
+ strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
+ }
+ else if(((HRU(fold_len) + ss_len <= avail)
+ || (avail > ss_len+3 && avail > 10)) && fold_len > 5){
+ q = fold_tmp;
+ strncpy(q, "...", sizeof(fold_tmp));
+ q += strlen(q);
+ q += utf8_to_width_rhs(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp), avail-(3+ss_len));
+ strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
+ }
+ else if(ss_len > 0 && ss_len <= avail){
+ q = fold_tmp;
+ strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
+ }
+ /* else leave it out */
+ }
+ }
+ }
+
+ fold_tmp[sizeof(fold_tmp)-1] = '\0';
+
+ /* write title, location and, optionally, the folder name */
+ fold_len = (int) utf8_width(fold_tmp);
+ }
+
+ fold_len = MAX(MIN(fold_len, avail), 0);
+ avail -= fold_len;
+
+ /* center folder in its space */
+ if(avail){
+ int inc;
+
+ inc = HRU(avail);
+
+ s3 += inc;
+ avail -= inc;
+
+ s4 += avail;
+ }
+ }
+ }
+
+ plus = "";
+
+ if(as.style != FileTextPercent && as.style != TextPercent){
+ NETMBX mb;
+
+ if(as.stream
+ && as.stream->mailbox
+ && mail_valid_net_parse(as.stream->mailbox, &mb)
+ && (mb.sslflag || mb.tlsflag))
+ plus = "+";
+ }
+
+
+ if(loc1_len > 0 && cur_mess_col_offset >= 0)
+ as.cur_mess_col = s1+ver_len+s2+title_len+s3+fold_len+s4 + cur_mess_col_offset;
+
+ if(loc1_len > 0 && page_column_offset >= 0)
+ as.page_column = s1+ver_len+s2+title_len+s3+fold_len+s4 + page_column_offset;
+
+ if(loc2_len > 0)
+ as.del_column = s1+ver_len+s2+title_len+s3+fold_len+s4+loc1_len+s5;
+
+ if(loc1_len > 0 && percent_column_offset > 0)
+ as.percent_column = s1+ver_len+s2+title_len+s3+fold_len+s4+loc1_len - percent_column_offset;
+
+
+ utf8_snprintf(titlebar_line, 6*MAX_SCREEN_COLS, "%*.*s%-*.*w%*.*s%-*.*w%*.*s%-*.*w%*.*s%-*.*w%*.*s%-*.*w%*.*s",
+ s1, s1, "",
+ ver_len, ver_len, version,
+ s2, s2, "",
+ title_len, title_len, title,
+ s3, s3, "",
+ fold_len, fold_len, fold_tmp,
+ s4, s4, "",
+ loc1_len, loc1_len, loc1,
+ s5, s5, "",
+ loc2_len, loc2_len, loc2,
+ s6, s6, plus);
+
+ return(&as.titlecontainer);
+}
+
+
+char
+sort_letter(SortOrder sort)
+{
+ char let = 'A', *p;
+
+ if((p = sort_name(sort)) != NULL && *p){
+ while(*(p+1) && islower((unsigned char) *p))
+ p++;
+
+ if(*p)
+ let = *p;
+ }
+
+ return(let);
+}
+
+
+/*
+ * Update the titlebar line if the message number changed
+ *
+ * Args: None, uses state setup on previous call to set_titlebar.
+ */
+void
+update_titlebar_message(void)
+{
+ long curnum, maxnum, oldnum;
+ PINETHRD_S *thrd = NULL;
+ COLOR_PAIR *lastc = NULL, *titlecolor;
+ char buf[50];
+ int num_len;
+ unsigned long rawno;
+
+ if(ps_global->ttyo
+ && (ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)) < 1){
+ titlebar_is_dirty = 1;
+ return;
+ }
+
+ if(as.style == ThrdIndex){
+
+ oldnum = as.current_thrd;
+
+ if(as.stream && (rawno=mn_m2raw(as.msgmap, mn_get_cur(as.msgmap))))
+ thrd = fetch_thread(as.stream, rawno);
+
+ if(thrd && thrd->top && (thrd=fetch_thread(as.stream, thrd->top)))
+ curnum = thrd->thrdno;
+ }
+ else if(as.cur_mess_col >= 0){
+ curnum = mn_get_cur(as.msgmap);
+ oldnum = as.current_msg;
+ }
+
+ if(as.cur_mess_col >= 0 && oldnum != curnum){
+
+ if(as.style == ThrdIndex){
+ as.current_thrd = curnum;
+ maxnum = as.msgmap->max_thrdno;
+ }
+ else{
+ as.current_msg = curnum;
+ maxnum = mn_get_total(as.msgmap);
+ }
+
+ titlecolor = &as.titlecontainer.color;
+
+ if(titlecolor)
+ lastc = pico_set_colorp(titlecolor, PSC_REV|PSC_RET);
+
+ num_len = strlen(comatose(maxnum));
+
+ snprintf(buf, sizeof(buf), "%*.*s", num_len, num_len, comatose(curnum));
+
+ PutLine0(0, as.cur_mess_col, buf);
+
+ if(lastc){
+ (void)pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+
+ fflush(stdout);
+ }
+}
+
+
+
+/*
+ * Update titlebar line's message status field ("DEL", "NEW", etc)
+ *
+ * Args: None, operates on state set during most recent set_titlebar call
+ */
+int
+update_titlebar_status(void)
+{
+ unsigned long rawno;
+ MESSAGECACHE *mc;
+ COLOR_PAIR *lastc = NULL, *titlecolor;
+
+ if(ps_global->ttyo
+ && (ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)) < 1){
+ titlebar_is_dirty = 1;
+ return(0);
+ }
+
+ if(!as.stream || as.current_msg <= 0L || as.del_column < 0)
+ return(1);
+
+ if(as.current_msg != mn_get_cur(as.msgmap))
+ update_titlebar_message();
+
+ mc = ((rawno = mn_m2raw(as.msgmap, as.current_msg)) > 0L
+ && rawno <= as.stream->nmsgs)
+ ? mail_elt(as.stream, rawno) : NULL;
+
+ if(!(mc && mc->valid))
+ return(0); /* indeterminate */
+
+ if(mc->deleted){ /* deleted takes precedence */
+ if(as.msg_state & MS_DEL)
+ return(1);
+ }
+ else if(mc->answered){ /* then answered */
+ if(as.msg_state & MS_ANS)
+ return(1);
+ } /* then forwarded */
+ else if(user_flag_is_set(as.stream, mc->msgno, FORWARDED_FLAG)){
+ if(as.msg_state & MS_FWD)
+ return(1);
+ }
+ else if(!mc->seen && as.stream
+ && (!IS_NEWS(as.stream)
+ || F_ON(F_FAKE_NEW_IN_NEWS, ps_global))){
+ if(as.msg_state & MS_NEW) /* then seen */
+ return(1);
+ }
+ else{
+ if(as.msg_state == 0) /* nothing to change */
+ return(1);
+ }
+
+ as.msg_state = STATUS_BITS(mc);
+
+ titlecolor = &as.titlecontainer.color;
+
+ if(titlecolor)
+ lastc = pico_set_colorp(titlecolor, PSC_REV|PSC_RET);
+
+ PutLine0(0, as.del_column, BAR_STATUS(as.msg_state));
+
+ if(lastc){
+ (void)pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+
+ fflush(stdout);
+ return(1);
+}
+
+
+/*
+ * Update the percentage shown in the titlebar line
+ *
+ * Args: new_line_number -- line number to calculate new percentage
+ */
+void
+update_titlebar_percent(long int new_line_number)
+{
+ COLOR_PAIR *lastc = NULL, *titlecolor;
+
+ if(as.percent_column < 0 || new_line_number == as.current_line)
+ return;
+
+ if(ps_global->ttyo
+ && (ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)) < 1){
+ titlebar_is_dirty = 1;
+ return;
+ }
+
+ as.current_line = new_line_number;
+
+ titlecolor = &as.titlecontainer.color;
+
+ if(titlecolor)
+ lastc = pico_set_colorp(titlecolor, PSC_REV|PSC_RET);
+
+ PutLine0(0, as.percent_column,
+ percentage(as.current_line, as.total_lines, 0));
+
+ if(lastc){
+ (void)pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+
+ fflush(stdout);
+}
+
+
+/*
+ * Update the percentage AND line number shown in the titlebar line
+ *
+ * Args: new_line_number -- line number to calculate new percentage
+ */
+void
+update_titlebar_lpercent(long int new_line_number)
+{
+ COLOR_PAIR *lastc = NULL, *titlecolor;
+ int num_len;
+ char buf[50];
+
+ if(as.page_column < 0 || new_line_number == as.current_line)
+ return;
+
+ if(ps_global->ttyo
+ && (ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)) < 1){
+ titlebar_is_dirty = 1;
+ return;
+ }
+
+ as.current_line = new_line_number;
+
+ titlecolor = &as.titlecontainer.color;
+
+ if(titlecolor)
+ lastc = pico_set_colorp(titlecolor, PSC_REV|PSC_RET);
+
+ num_len = strlen(comatose(as.total_lines));
+ snprintf(buf, sizeof(buf), "%*.*s", num_len, num_len, comatose(as.current_line));
+
+ PutLine0(0, as.page_column, buf);
+
+ PutLine0(0, as.percent_column,
+ percentage(as.current_line, as.total_lines, 0));
+
+ if(lastc){
+ (void)pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+
+ fflush(stdout);
+}
+
+
+/*----------------------------------------------------------------------
+ Return static buf containing portion of lines displayed
+
+ Args: part -- how much so far
+ total -- how many total
+
+ ---*/
+char *
+percentage(long int part, long int total, int suppress_top)
+{
+ static char percent[4];
+
+ if(total == 0L || (total <= ps_global->ttyo->screen_rows
+ - HEADER_ROWS(ps_global)
+ - FOOTER_ROWS(ps_global)))
+ strncpy(percent, "ALL", sizeof(percent));
+ else if(!suppress_top && part <= ps_global->ttyo->screen_rows
+ - HEADER_ROWS(ps_global)
+ - FOOTER_ROWS(ps_global))
+ strncpy(percent, "TOP", sizeof(percent));
+ else if(part >= total)
+ strncpy(percent, "END", sizeof(percent));
+ else
+ snprintf(percent, sizeof(percent), "%2ld%%", (100L * part)/total);
+
+ percent[sizeof(percent)-1] = '\0';
+
+ return(percent);
+}
+
+
+/*
+ * end_titlebar - free resources associated with titlebar state struct
+ */
+void
+end_titlebar(void)
+{
+ if(as.folder_name)
+ fs_give((void **)&as.folder_name);
+
+ if(as.context_name)
+ fs_give((void **)&as.context_name);
+}
+
+
+/*----------------------------------------------------------------------
+ Exported method to display status of mail check
+
+ Args: putstr -- should be NO LONGER THAN 2 bytes
+
+ Result: putstr displayed at upper-left-hand corner of screen
+ ----*/
+void
+check_cue_display(char *putstr)
+{
+ COLOR_PAIR *lastc = NULL, *titlecolor;
+ static char check_cue_char;
+
+ if(ps_global->read_predicted &&
+ (check_cue_char == putstr[0]
+ || (putstr[0] == ' ' && putstr[1] == '\0')))
+ return;
+ else{
+ if(putstr[0] == ' ')
+ check_cue_char = '\0';
+ else
+ check_cue_char = putstr[0];
+ }
+
+ titlecolor = &as.titlecontainer.color;
+
+ if(titlecolor)
+ lastc = pico_set_colorp(titlecolor, PSC_REV|PSC_RET);
+
+ PutLine0(0, 0, putstr); /* show delay cue */
+ if(lastc){
+ (void)pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+
+ fflush(stdout);
+}
+
+
+/*----------------------------------------------------------------------
+ Mandatory function of ../pith/newmail.c
+
+ Args: none
+
+ Result: newmail cue displayed at upper-left-hand corner of screen
+ ----*/
+void
+newmail_check_cue(int onoroff)
+{
+ if(F_ON(F_SHOW_DELAY_CUE, ps_global) && !ps_global->in_init_seq){
+ check_cue_display((onoroff == TRUE) ? " *" : " ");
+ MoveCursor(ps_global->ttyo->screen_rows-FOOTER_ROWS(ps_global), 0);
+ }
+
+#ifdef _WINDOWS
+ if(onoroff)
+ mswin_setcursor (MSWIN_CURSOR_BUSY);
+ else
+ mswin_setcursor (MSWIN_CURSOR_ARROW);
+#endif
+}
+
+
+/*----------------------------------------------------------------------
+ Mandatory function of ../pith/newmail.c
+
+ Args: none
+
+ Result: checkpoint delay cue displayed at upper-left-hand corner of screen
+ ----*/
+void
+newmail_check_point_cue(int onoroff)
+{
+ if(F_ON(F_SHOW_DELAY_CUE, ps_global)){
+ check_cue_display((onoroff == TRUE) ? "**" : " ");
+ MoveCursor(ps_global->ttyo->screen_rows-FOOTER_ROWS(ps_global), 0);
+ }
+
+#ifdef _WINDOWS
+ if(onoroff)
+ mswin_setcursor (MSWIN_CURSOR_BUSY);
+ else
+ mswin_setcursor (MSWIN_CURSOR_ARROW);
+#endif
+}
diff --git a/alpine/titlebar.h b/alpine/titlebar.h
new file mode 100644
index 00000000..65c094b6
--- /dev/null
+++ b/alpine/titlebar.h
@@ -0,0 +1,55 @@
+/*
+ * $Id: titlebar.h 769 2007-10-24 00:15:40Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PINE_TITLEBAR_INCLUDED
+#define PINE_TITLEBAR_INCLUDED
+
+
+#include "../pith/context.h"
+#include "../pith/msgno.h"
+#include "../pith/osdep/color.h"
+
+
+typedef struct titlebarcontainer {
+ char titlebar_line[6*MAX_SCREEN_COLS+1];
+ COLOR_PAIR color;
+} TITLE_S;
+
+
+typedef enum {TitleBarNone = 0, FolderName, MessageNumber, MsgTextPercent,
+ TextPercent, FileTextPercent, ThrdIndex,
+ ThrdMsgNum, ThrdMsgPercent} TitleBarType;
+
+
+/* exported protoypes */
+void end_titlebar(void);
+void push_titlebar_state(void);
+void pop_titlebar_state(void);
+void mark_titlebar_dirty(void);
+char *set_titlebar(char *, MAILSTREAM *, CONTEXT_S *, char *, MSGNO_S *, int,
+ TitleBarType, long, long, COLOR_PAIR *);
+void redraw_titlebar(void);
+TITLE_S *format_titlebar(void);
+COLOR_PAIR *current_titlebar_color(void);
+void update_titlebar_message(void);
+void update_titlebar_percent(long);
+void update_titlebar_lpercent(long);
+void titlebar_stream_closing(MAILSTREAM *);
+int update_titlebar_status(void);
+void check_cue_display(char *);
+
+
+#endif /* PINE_TITLEBAR_INCLUDED */
diff --git a/autom4te.cache/output.0 b/autom4te.cache/output.0
new file mode 100644
index 00000000..0088bc98
--- /dev/null
+++ b/autom4te.cache/output.0
@@ -0,0 +1,21685 @@
+@%:@! /bin/sh
+@%:@ From configure.ac Id: configure.ac 1266 2009-07-14 18:39:12Z hubert@u.washington.edu .
+@%:@ Guess values for system-dependent variables and create Makefiles.
+@%:@ Generated by GNU Autoconf 2.68 for alpine 2.10.
+@%:@
+@%:@ Report bugs to <alpine-contact@u.washington.edu>.
+@%:@
+@%:@
+@%:@ Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
+@%:@ 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software
+@%:@ Foundation, Inc.
+@%:@
+@%:@
+@%:@ This configure script is free software; the Free Software Foundation
+@%:@ gives unlimited permission to copy, distribute and modify it.
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in @%:@(
+ *posix*) :
+ set -o posix ;; @%:@(
+ *) :
+ ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='print -r --'
+ as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='printf %s\n'
+ as_echo_n='printf %s'
+else
+ if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+ as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+ as_echo_n='/usr/ucb/echo -n'
+ else
+ as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+ as_echo_n_body='eval
+ arg=$1;
+ case $arg in @%:@(
+ *"$as_nl"*)
+ expr "X$arg" : "X\\(.*\\)$as_nl";
+ arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+ esac;
+ expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+ '
+ export as_echo_n_body
+ as_echo_n='sh -c $as_echo_n_body as_echo'
+ fi
+ export as_echo_body
+ as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+as_myself=
+case $0 in @%:@((
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there. '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+if test "x$CONFIG_SHELL" = x; then
+ as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '\${1+\"\$@\"}'='\"\$@\"'
+ setopt NO_GLOB_SUBST
+else
+ case \`(set -o) 2>/dev/null\` in @%:@(
+ *posix*) :
+ set -o posix ;; @%:@(
+ *) :
+ ;;
+esac
+fi
+"
+ as_required="as_fn_return () { (exit \$1); }
+as_fn_success () { as_fn_return 0; }
+as_fn_failure () { as_fn_return 1; }
+as_fn_ret_success () { return 0; }
+as_fn_ret_failure () { return 1; }
+
+exitcode=0
+as_fn_success || { exitcode=1; echo as_fn_success failed.; }
+as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
+as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
+as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
+if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
+
+else
+ exitcode=1; echo positional parameters were not saved.
+fi
+test x\$exitcode = x0 || exit 1"
+ as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
+ as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
+ eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
+ test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1
+test \$(( 1 + 1 )) = 2 || exit 1"
+ if (eval "$as_required") 2>/dev/null; then :
+ as_have_required=yes
+else
+ as_have_required=no
+fi
+ if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then :
+
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ as_found=:
+ case $as_dir in @%:@(
+ /*)
+ for as_base in sh bash ksh sh5; do
+ # Try only shells that exist, to save several forks.
+ as_shell=$as_dir/$as_base
+ if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+ { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then :
+ CONFIG_SHELL=$as_shell as_have_required=yes
+ if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then :
+ break 2
+fi
+fi
+ done;;
+ esac
+ as_found=false
+done
+$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
+ { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
+ CONFIG_SHELL=$SHELL as_have_required=yes
+fi; }
+IFS=$as_save_IFS
+
+
+ if test "x$CONFIG_SHELL" != x; then :
+ # We cannot yet assume a decent shell, so we have to provide a
+ # neutralization value for shells without unset; and this also
+ # works around shells that cannot unset nonexistent variables.
+ # Preserve -v and -x to the replacement shell.
+ BASH_ENV=/dev/null
+ ENV=/dev/null
+ (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+ export CONFIG_SHELL
+ case $- in @%:@ ((((
+ *v*x* | *x*v* ) as_opts=-vx ;;
+ *v* ) as_opts=-v ;;
+ *x* ) as_opts=-x ;;
+ * ) as_opts= ;;
+ esac
+ exec "$CONFIG_SHELL" $as_opts "$as_myself" ${1+"$@"}
+fi
+
+ if test x$as_have_required = xno; then :
+ $as_echo "$0: This script requires a shell more modern than all"
+ $as_echo "$0: the shells that I found on your system."
+ if test x${ZSH_VERSION+set} = xset ; then
+ $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
+ $as_echo "$0: be upgraded to zsh 4.3.4 or later."
+ else
+ $as_echo "$0: Please tell bug-autoconf@gnu.org and
+$0: alpine-contact@u.washington.edu about your system,
+$0: including any error possibly output before this
+$0: message. Then install a modern shell, or manually run
+$0: the script under such a shell if you do have one."
+ fi
+ exit 1
+fi
+fi
+fi
+SHELL=${CONFIG_SHELL-/bin/sh}
+export SHELL
+# Unset more variables known to interfere with behavior of common tools.
+CLICOLOR_FORCE= GREP_OPTIONS=
+unset CLICOLOR_FORCE GREP_OPTIONS
+
+## --------------------- ##
+## M4sh Shell Functions. ##
+## --------------------- ##
+@%:@ as_fn_unset VAR
+@%:@ ---------------
+@%:@ Portably unset VAR.
+as_fn_unset ()
+{
+ { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+@%:@ as_fn_set_status STATUS
+@%:@ -----------------------
+@%:@ Set @S|@? to STATUS, without forking.
+as_fn_set_status ()
+{
+ return $1
+} @%:@ as_fn_set_status
+
+@%:@ as_fn_exit STATUS
+@%:@ -----------------
+@%:@ Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+ set +e
+ as_fn_set_status $1
+ exit $1
+} @%:@ as_fn_exit
+
+@%:@ as_fn_mkdir_p
+@%:@ -------------
+@%:@ Create "@S|@as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || eval $as_mkdir_p || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} @%:@ as_fn_mkdir_p
+@%:@ as_fn_append VAR VALUE
+@%:@ ----------------------
+@%:@ Append the text in VALUE to the end of the definition contained in VAR. Take
+@%:@ advantage of any shell optimizations that allow amortized linear growth over
+@%:@ repeated appends, instead of the typical quadratic growth present in naive
+@%:@ implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+ eval 'as_fn_append ()
+ {
+ eval $1+=\$2
+ }'
+else
+ as_fn_append ()
+ {
+ eval $1=\$$1\$2
+ }
+fi # as_fn_append
+
+@%:@ as_fn_arith ARG...
+@%:@ ------------------
+@%:@ Perform arithmetic evaluation on the ARGs, and store the result in the
+@%:@ global @S|@as_val. Take advantage of shells that can avoid forks. The arguments
+@%:@ must be portable across @S|@(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+ eval 'as_fn_arith ()
+ {
+ as_val=$(( $* ))
+ }'
+else
+ as_fn_arith ()
+ {
+ as_val=`expr "$@" || test $? -eq 1`
+ }
+fi # as_fn_arith
+
+
+@%:@ as_fn_error STATUS ERROR [LINENO LOG_FD]
+@%:@ ----------------------------------------
+@%:@ Output "`basename @S|@0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+@%:@ provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+@%:@ script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+ as_status=$1; test $as_status -eq 0 && as_status=1
+ if test "$4"; then
+ as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+ fi
+ $as_echo "$as_me: error: $2" >&2
+ as_fn_exit $as_status
+} @%:@ as_fn_error
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+ as_lineno_1=$LINENO as_lineno_1a=$LINENO
+ as_lineno_2=$LINENO as_lineno_2a=$LINENO
+ eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
+ test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
+ # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-)
+ sed -n '
+ p
+ /[$]LINENO/=
+ ' <$as_myself |
+ sed '
+ s/[$]LINENO.*/&-/
+ t lineno
+ b
+ :lineno
+ N
+ :loop
+ s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+ t loop
+ s/-\n.*//
+ ' >$as_me.lineno &&
+ chmod +x "$as_me.lineno" ||
+ { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensitive to this).
+ . "./$as_me.lineno"
+ # Exit status is that of the last command.
+ exit
+}
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in @%:@(((((
+-n*)
+ case `echo 'xy\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ xy) ECHO_C='\c';;
+ *) echo `echo ksh88 bug on AIX 6.1` > /dev/null
+ ECHO_T=' ';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+ if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -p'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -p'
+ elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+ else
+ as_ln_s='cp -p'
+ fi
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p='mkdir -p "$as_dir"'
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+if test -x / >/dev/null 2>&1; then
+ as_test_x='test -x'
+else
+ if ls -dL / >/dev/null 2>&1; then
+ as_ls_L_option=L
+ else
+ as_ls_L_option=
+ fi
+ as_test_x='
+ eval sh -c '\''
+ if test -d "$1"; then
+ test -d "$1/.";
+ else
+ case $1 in @%:@(
+ -*)set "./$1";;
+ esac;
+ case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in @%:@((
+ ???[sx]*):;;*)false;;esac;fi
+ '\'' sh
+ '
+fi
+as_executable_p=$as_test_x
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+
+# Check that we are running under the correct shell.
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+case X$lt_ECHO in
+X*--fallback-echo)
+ # Remove one level of quotation (which was required for Make).
+ ECHO=`echo "$lt_ECHO" | sed 's,\\\\\$\\$0,'$0','`
+ ;;
+esac
+
+ECHO=${lt_ECHO-echo}
+if test "X$1" = X--no-reexec; then
+ # Discard the --no-reexec flag, and continue.
+ shift
+elif test "X$1" = X--fallback-echo; then
+ # Avoid inline document here, it may be left over
+ :
+elif test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' ; then
+ # Yippee, $ECHO works!
+ :
+else
+ # Restart under the correct shell.
+ exec $SHELL "$0" --no-reexec ${1+"$@"}
+fi
+
+if test "X$1" = X--fallback-echo; then
+ # used as fallback echo
+ shift
+ cat <<_LT_EOF
+$*
+_LT_EOF
+ exit 0
+fi
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+if test -z "$lt_ECHO"; then
+ if test "X${echo_test_string+set}" != Xset; then
+ # find a string as large as possible, as long as the shell can cope with it
+ for cmd in 'sed 50q "$0"' 'sed 20q "$0"' 'sed 10q "$0"' 'sed 2q "$0"' 'echo test'; do
+ # expected sizes: less than 2Kb, 1Kb, 512 bytes, 16 bytes, ...
+ if { echo_test_string=`eval $cmd`; } 2>/dev/null &&
+ { test "X$echo_test_string" = "X$echo_test_string"; } 2>/dev/null
+ then
+ break
+ fi
+ done
+ fi
+
+ if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ :
+ else
+ # The Solaris, AIX, and Digital Unix default echo programs unquote
+ # backslashes. This makes it impossible to quote backslashes using
+ # echo "$something" | sed 's/\\/\\\\/g'
+ #
+ # So, first we look for a working echo in the user's PATH.
+
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ for dir in $PATH /usr/ucb; do
+ IFS="$lt_save_ifs"
+ if (test -f $dir/echo || test -f $dir/echo$ac_exeext) &&
+ test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`($dir/echo "$echo_test_string") 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ ECHO="$dir/echo"
+ break
+ fi
+ done
+ IFS="$lt_save_ifs"
+
+ if test "X$ECHO" = Xecho; then
+ # We didn't find a better echo, so look for alternatives.
+ if test "X`{ print -r '\t'; } 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`{ print -r "$echo_test_string"; } 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ # This shell has a builtin print -r that does the trick.
+ ECHO='print -r'
+ elif { test -f /bin/ksh || test -f /bin/ksh$ac_exeext; } &&
+ test "X$CONFIG_SHELL" != X/bin/ksh; then
+ # If we have ksh, try running configure again with it.
+ ORIGINAL_CONFIG_SHELL=${CONFIG_SHELL-/bin/sh}
+ export ORIGINAL_CONFIG_SHELL
+ CONFIG_SHELL=/bin/ksh
+ export CONFIG_SHELL
+ exec $CONFIG_SHELL "$0" --no-reexec ${1+"$@"}
+ else
+ # Try using printf.
+ ECHO='printf %s\n'
+ if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ # Cool, printf works
+ :
+ elif echo_testing_string=`($ORIGINAL_CONFIG_SHELL "$0" --fallback-echo '\t') 2>/dev/null` &&
+ test "X$echo_testing_string" = 'X\t' &&
+ echo_testing_string=`($ORIGINAL_CONFIG_SHELL "$0" --fallback-echo "$echo_test_string") 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ CONFIG_SHELL=$ORIGINAL_CONFIG_SHELL
+ export CONFIG_SHELL
+ SHELL="$CONFIG_SHELL"
+ export SHELL
+ ECHO="$CONFIG_SHELL $0 --fallback-echo"
+ elif echo_testing_string=`($CONFIG_SHELL "$0" --fallback-echo '\t') 2>/dev/null` &&
+ test "X$echo_testing_string" = 'X\t' &&
+ echo_testing_string=`($CONFIG_SHELL "$0" --fallback-echo "$echo_test_string") 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ ECHO="$CONFIG_SHELL $0 --fallback-echo"
+ else
+ # maybe with a smaller string...
+ prev=:
+
+ for cmd in 'echo test' 'sed 2q "$0"' 'sed 10q "$0"' 'sed 20q "$0"' 'sed 50q "$0"'; do
+ if { test "X$echo_test_string" = "X`eval $cmd`"; } 2>/dev/null
+ then
+ break
+ fi
+ prev="$cmd"
+ done
+
+ if test "$prev" != 'sed 50q "$0"'; then
+ echo_test_string=`eval $prev`
+ export echo_test_string
+ exec ${ORIGINAL_CONFIG_SHELL-${CONFIG_SHELL-/bin/sh}} "$0" ${1+"$@"}
+ else
+ # Oops. We lost completely, so just stick with echo.
+ ECHO=echo
+ fi
+ fi
+ fi
+ fi
+ fi
+fi
+
+# Copy echo and quote the copy suitably for passing to libtool from
+# the Makefile, instead of quoting the original, which is used later.
+lt_ECHO=$ECHO
+if test "X$lt_ECHO" = "X$CONFIG_SHELL $0 --fallback-echo"; then
+ lt_ECHO="$CONFIG_SHELL \\\$\$0 --fallback-echo"
+fi
+
+
+
+
+test -n "$DJDIR" || exec 7<&0 </dev/null
+exec 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIB@&t@OBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+
+# Identity of this package.
+PACKAGE_NAME='alpine'
+PACKAGE_TARNAME='alpine'
+PACKAGE_VERSION='2.10'
+PACKAGE_STRING='alpine 2.10'
+PACKAGE_BUGREPORT='alpine-contact@u.washington.edu'
+PACKAGE_URL=''
+
+ac_unique_file="include/system.h"
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_STRING_H
+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+# include <memory.h>
+# endif
+# include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+gt_needs=
+ac_subst_vars='am__EXEEXT_FALSE
+am__EXEEXT_TRUE
+LTLIBOBJS
+LIB@&t@OBJS
+AM_LDFLAGS
+AM_CFLAGS
+WEB_PUBCOOKIE_LINK
+WEB_PUBCOOKIE_LIB
+WEB_PUBCOOKIE_BUILD
+WEB_BINDIR
+WEB_BUILD
+REGEX_BUILD
+C_CLIENT_SPECIALS
+C_CLIENT_GCCOPTLEVEL
+C_CLIENT_LDFLAGS
+C_CLIENT_CFLAGS
+C_CLIENT_WITH_IPV6
+C_CLIENT_TARGET
+PTHREAD_CFLAGS
+PTHREAD_LIBS
+PTHREAD_CC
+acx_pthread_config
+alpine_interactive_spellcheck
+ISPELLPROG
+alpine_simple_spellcheck
+SPELLPROG
+PWPROG
+NPA_PROG
+SENDMAIL
+POSUB
+LTLIBINTL
+LIBINTL
+INTLLIBS
+LTLIBICONV
+LIBICONV
+INTL_MACOSX_LIBS
+MSGMERGE
+XGETTEXT_015
+XGETTEXT
+GMSGFMT_015
+MSGFMT_015
+GMSGFMT
+MSGFMT
+USE_NLS
+MAKE
+LN
+CP
+RM
+CPP
+OTOOL64
+OTOOL
+LIPO
+NMEDIT
+DSYMUTIL
+lt_ECHO
+AR
+OBJDUMP
+NM
+ac_ct_DUMPBIN
+DUMPBIN
+LD
+FGREP
+EGREP
+GREP
+SED
+LIBTOOL
+RANLIB
+LN_S
+am__fastdepCC_FALSE
+am__fastdepCC_TRUE
+CCDEPMODE
+AMDEPBACKSLASH
+AMDEP_FALSE
+AMDEP_TRUE
+am__quote
+am__include
+DEPDIR
+OBJEXT
+EXEEXT
+ac_ct_CC
+CPPFLAGS
+LDFLAGS
+CFLAGS
+CC
+host_os
+host_vendor
+host_cpu
+host
+build_os
+build_vendor
+build_cpu
+build
+MAINT
+MAINTAINER_MODE_FALSE
+MAINTAINER_MODE_TRUE
+am__untar
+am__tar
+AMTAR
+am__leading_dot
+SET_MAKE
+AWK
+mkdir_p
+MKDIR_P
+INSTALL_STRIP_PROGRAM
+STRIP
+install_sh
+MAKEINFO
+AUTOHEADER
+AUTOMAKE
+AUTOCONF
+ACLOCAL
+VERSION
+PACKAGE
+CYGPATH_W
+am__isrc
+INSTALL_DATA
+INSTALL_SCRIPT
+INSTALL_PROGRAM
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_URL
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+enable_maintainer_mode
+enable_dependency_tracking
+enable_shared
+enable_static
+with_pic
+enable_fast_install
+with_gnu_ld
+enable_libtool_lock
+enable_nls
+enable_rpath
+with_libiconv_prefix
+with_libintl_prefix
+enable_dmalloc
+with_dmalloc_dir
+with_localedir
+enable_osx_universal_binaries
+with_include_path
+with_lib_path
+with_pubcookie
+with_web_bin
+enable_debug
+enable_optimization
+enable_mouse
+enable_quotas
+enable_from_changing
+enable_background_post
+enable_keyboard_lock
+enable_from_encoding
+with_smtp_msa
+with_smtp_msa_flags
+with_npa
+with_npa_flags
+with_password_prog
+with_simple_spellcheck
+with_interactive_spellcheck
+with_system_pinerc
+with_system_fixed_pinerc
+with_mailcheck_interval
+with_checkpoint_interval
+with_checkpoint_frequency
+with_display_rows
+with_display_columns
+with_max_display_rows
+with_max_display_columns
+with_fill_column
+with_max_fill_column
+with_debug_level
+with_debug_files
+with_debug_file
+with_forwarded_keyword
+with_display_overlap
+with_display_margin
+with_default_fcc
+with_default_save_folder
+with_default_legacy_postponed_folder
+with_default_postponed_folder
+with_default_trash_folder
+with_default_interrupted_mail
+with_default_dead_letter_folder
+with_default_mail_directory
+with_default_inbox_name
+with_default_signature_file
+with_default_elm_style_save
+with_default_header_in_reply
+with_default_old_style_reply
+with_default_use_only_domain_name
+with_default_save_by_sender
+with_default_sort_key
+with_default_addressbook_sort_rule
+with_default_folder_sort_rule
+with_default_saved_message_name_rule
+with_default_fcc_rule
+with_default_standard_printer
+with_default_ansi_printer
+with_default_addressbook
+with_default_local_fullname
+with_default_local_address
+with_default_keyboard_lock_count
+with_default_remote_addressbook_history
+with_smime_public_cert_directory
+with_smime_private_key_directory
+with_smime_cacert_directory
+with_default_printer
+with_passfile
+with_local_password_cache
+with_local_password_cache_method
+with_default_sshpath
+with_default_sshcmd
+with_ssl
+with_ssl_dir
+with_ssl_certs_dir
+with_ssl_include_dir
+with_ssl_lib_dir
+with_krb5
+with_krb5_dir
+with_krb5_include_dir
+with_krb5_lib_dir
+with_ldap
+with_ldap_dir
+with_ldap_include_dir
+with_ldap_lib_dir
+with_smime
+with_tcl
+with_tcl_lib
+with_tcl_include
+with_supplied_regex
+with_pthread
+with_system_mail_directory
+with_c_client_target
+with_ipv6
+'
+ ac_precious_vars='build_alias
+host_alias
+target_alias
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+CPP'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval $ac_prev=\$ac_option
+ ac_prev=
+ continue
+ fi
+
+ case $ac_option in
+ *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+ *=) ac_optarg= ;;
+ *) ac_optarg=yes ;;
+ esac
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case $ac_dashdash$ac_option in
+ --)
+ ac_dashdash=yes ;;
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir=$ac_optarg ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build_alias ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build_alias=$ac_optarg ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file=$ac_optarg ;;
+
+ --config-cache | -C)
+ cache_file=config.cache ;;
+
+ -datadir | --datadir | --datadi | --datad)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=*)
+ datadir=$ac_optarg ;;
+
+ -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+ | --dataroo | --dataro | --datar)
+ ac_prev=datarootdir ;;
+ -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+ datarootdir=$ac_optarg ;;
+
+ -disable-* | --disable-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid feature name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"enable_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval enable_$ac_useropt=no ;;
+
+ -docdir | --docdir | --docdi | --doc | --do)
+ ac_prev=docdir ;;
+ -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+ docdir=$ac_optarg ;;
+
+ -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+ ac_prev=dvidir ;;
+ -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+ dvidir=$ac_optarg ;;
+
+ -enable-* | --enable-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid feature name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"enable_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval enable_$ac_useropt=\$ac_optarg ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix=$ac_optarg ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he | -h)
+ ac_init_help=long ;;
+ -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+ ac_init_help=recursive ;;
+ -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+ ac_init_help=short ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host_alias ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host_alias=$ac_optarg ;;
+
+ -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+ ac_prev=htmldir ;;
+ -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+ | --ht=*)
+ htmldir=$ac_optarg ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir=$ac_optarg ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir=$ac_optarg ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir=$ac_optarg ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir=$ac_optarg ;;
+
+ -localedir | --localedir | --localedi | --localed | --locale)
+ ac_prev=localedir ;;
+ -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+ localedir=$ac_optarg ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst | --locals)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+ localstatedir=$ac_optarg ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir=$ac_optarg ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c | -n)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir=$ac_optarg ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix=$ac_optarg ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix=$ac_optarg ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix=$ac_optarg ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name=$ac_optarg ;;
+
+ -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+ ac_prev=pdfdir ;;
+ -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+ pdfdir=$ac_optarg ;;
+
+ -psdir | --psdir | --psdi | --psd | --ps)
+ ac_prev=psdir ;;
+ -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+ psdir=$ac_optarg ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir=$ac_optarg ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir=$ac_optarg ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site=$ac_optarg ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir=$ac_optarg ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir=$ac_optarg ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target_alias ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target_alias=$ac_optarg ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers | -V)
+ ac_init_version=: ;;
+
+ -with-* | --with-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid package name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"with_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval with_$ac_useropt=\$ac_optarg ;;
+
+ -without-* | --without-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid package name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"with_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval with_$ac_useropt=no ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes=$ac_optarg ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries=$ac_optarg ;;
+
+ -*) as_fn_error $? "unrecognized option: \`$ac_option'
+Try \`$0 --help' for more information"
+ ;;
+
+ *=*)
+ ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+ # Reject names that are not valid shell variable names.
+ case $ac_envvar in #(
+ '' | [0-9]* | *[!_$as_cr_alnum]* )
+ as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
+ esac
+ eval $ac_envvar=\$ac_optarg
+ export $ac_envvar ;;
+
+ *)
+ # FIXME: should be removed in autoconf 3.0.
+ $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+ expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+ : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+ as_fn_error $? "missing argument to $ac_option"
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+ case $enable_option_checking in
+ no) ;;
+ fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
+ *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+ esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
+ datadir sysconfdir sharedstatedir localstatedir includedir \
+ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+ libdir localedir mandir
+do
+ eval ac_val=\$$ac_var
+ # Remove trailing slashes.
+ case $ac_val in
+ */ )
+ ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+ eval $ac_var=\$ac_val;;
+ esac
+ # Be sure to have absolute directory names.
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* ) continue;;
+ NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+ esac
+ as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+ if test "x$build_alias" = x; then
+ cross_compiling=maybe
+ $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host.
+ If a cross compiler is detected then cross compile mode will be used" >&2
+ elif test "x$build_alias" != "x$host_alias"; then
+ cross_compiling=yes
+ fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+ as_fn_error $? "working directory cannot be determined"
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+ as_fn_error $? "pwd does not report name of working directory"
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then the parent directory.
+ ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_myself" : 'X\(//\)[^/]' \| \
+ X"$as_myself" : 'X\(//\)$' \| \
+ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_myself" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ srcdir=$ac_confdir
+ if test ! -r "$srcdir/$ac_unique_file"; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+ test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+ as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+ cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
+ pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+ srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+ eval ac_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_env_${ac_var}_value=\$${ac_var}
+ eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat <<_ACEOF
+\`configure' configures alpine 2.10 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE. See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+ -h, --help display this help and exit
+ --help=short display options specific to this package
+ --help=recursive display the short help of all the included packages
+ -V, --version display version information and exit
+ -q, --quiet, --silent do not print \`checking ...' messages
+ --cache-file=FILE cache test results in FILE [disabled]
+ -C, --config-cache alias for \`--cache-file=config.cache'
+ -n, --no-create do not create output files
+ --srcdir=DIR find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ @<:@@S|@ac_default_prefix@:>@
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ @<:@PREFIX@:>@
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+ --bindir=DIR user executables [EPREFIX/bin]
+ --sbindir=DIR system admin executables [EPREFIX/sbin]
+ --libexecdir=DIR program executables [EPREFIX/libexec]
+ --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --libdir=DIR object code libraries [EPREFIX/lib]
+ --includedir=DIR C header files [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc [/usr/include]
+ --datarootdir=DIR read-only arch.-independent data root [PREFIX/share]
+ --datadir=DIR read-only architecture-independent data [DATAROOTDIR]
+ --infodir=DIR info documentation [DATAROOTDIR/info]
+ --localedir=DIR locale-dependent data [DATAROOTDIR/locale]
+ --mandir=DIR man documentation [DATAROOTDIR/man]
+ --docdir=DIR documentation root @<:@DATAROOTDIR/doc/alpine@:>@
+ --htmldir=DIR html documentation [DOCDIR]
+ --dvidir=DIR dvi documentation [DOCDIR]
+ --pdfdir=DIR pdf documentation [DOCDIR]
+ --psdir=DIR ps documentation [DOCDIR]
+_ACEOF
+
+ cat <<\_ACEOF
+
+Program names:
+ --program-prefix=PREFIX prepend PREFIX to installed program names
+ --program-suffix=SUFFIX append SUFFIX to installed program names
+ --program-transform-name=PROGRAM run sed PROGRAM on installed program names
+
+System types:
+ --build=BUILD configure for building on BUILD [guessed]
+ --host=HOST cross-compile to build programs to run on HOST [BUILD]
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+ case $ac_init_help in
+ short | recursive ) echo "Configuration of alpine 2.10:";;
+ esac
+ cat <<\_ACEOF
+
+Optional Features:
+ --disable-option-checking ignore unrecognized --enable/--with options
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --enable-maintainer-mode enable make rules and dependencies not useful
+ (and sometimes confusing) to the casual installer
+ --disable-dependency-tracking speeds up one-time build
+ --enable-dependency-tracking do not reject slow dependency extractors
+ --enable-shared@<:@=PKGS@:>@ build shared libraries @<:@default=yes@:>@
+ --enable-static@<:@=PKGS@:>@ build static libraries @<:@default=yes@:>@
+ --enable-fast-install@<:@=PKGS@:>@
+ optimize for fast installation @<:@default=yes@:>@
+ --disable-libtool-lock avoid locking (might break parallel builds)
+ --disable-nls do not use Native Language Support
+ --disable-rpath do not hardcode runtime library paths
+ --enable-dmalloc Enable dmalloc debugging
+ --enable-osx-universal-binaries
+ Produce universal binaries under OS X @<:@@<:@default=no@:>@@:>@
+ --disable-debug Exclude debug messages from source
+ --disable-optimization Exclude optimizing compiler flags
+ --disable-mouse Disable mouse support
+ --enable-quotas Enable disk quota checking on startup
+ --disable-from-changing Disallow users changing From addresss
+ --disable-background-post
+ Disable background posting
+ --disable-keyboard-lock Disable keyboard locking
+ --enable-from-encoding Enable From encoding in sent messages
+
+Optional Packages:
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --with-pic try to use only PIC/non-PIC objects @<:@default=use
+ both@:>@
+ --with-gnu-ld assume the C compiler uses GNU ld @<:@default=no@:>@
+ --with-gnu-ld assume the C compiler uses GNU ld default=no
+ --with-libiconv-prefix[=DIR] search for libiconv in DIR/include and DIR/lib
+ --without-libiconv-prefix don't search for libiconv in includedir and libdir
+ --with-libintl-prefix[=DIR] search for libintl in DIR/include and DIR/lib
+ --without-libintl-prefix don't search for libintl in includedir and libdir
+ --with-dmalloc-dir=DIR Root of dmalloc lib/include path
+ --with-localedir=DIR Name of gettext locale directory
+ --with-include-path=PATHS
+ Colon-separated list of directories used for include
+ file search
+ --with-lib-path=PATHS Colon-separated list of directories used for library
+ search
+ --with-pubcookie Include support for UW-Pubcookie Web Authentication
+ --with-web-bin=PATH Directory to hold Web Alpine component binary files
+ --with-smtp-msa=PATH Local Mail Submission Agent (sendmail)
+ --with-smtp-msa-flags=FLAGS
+ MSA flags for SMTP on stdin/stdout (-bs -odb -oem)
+ --with-npa=PATH Posting agent when no nntp-servers defined (inews)
+ --with-npa-flags=FLAGS Flags to allow posting via local agent (-h)
+ --with-password-prog=PATH
+ Password change program (/bin/passwd)
+ --with-simple-spellcheck=PROG
+ Spellcheck program reads stdin, emits misspellings
+ on stdout
+ --with-interactive-spellcheck=PROG
+ Interactive, filewise spell checker
+ --with-system-pinerc=VALUE
+ System pinerc (/usr/local/lib/pine.conf)
+ --with-system-fixed-pinerc=VALUE
+ System fixed pinerc (/usr/local/lib/pine.conf.fixed)
+ --with-mailcheck-interval=VALUE
+ Specify default mail-check-interval (150)
+ --with-checkpoint-interval=VALUE
+ Specify default checkpoint-interval (420)
+ --with-checkpoint-frequency=VALUE
+ State change count before checkpoint (12)
+ --with-display-rows=VALUE
+ Initial rows on display (24)
+ --with-display-columns=VALUE
+ Initial columns on display (80)
+ --with-max-display-rows=VALUE
+ Maximum display rows (200)
+ --with-max-display-columns=VALUE
+ Maximum display columns (500)
+ --with-fill-column=VALUE
+ Default fill column (74)
+ --with-max_fill-column=VALUE
+ Maximum fill column (80)
+ --with-debug-level=VALUE
+ Specify default debug verbosity level (2)
+ --with-debug-files=VALUE
+ Specify number of debug files (4)
+ --with-debug-file=VALUE Specify debug file name (.pine-debug)
+ --with-forwarded-keyword=VALUE
+ IMAP (c-client) keyword to store forwarded status
+ ("\@S|@Forwarded")
+ --with-display-overlap=VALUE
+ Lines preserved while paging (2)
+ --with-display-margin=VALUE
+ Lines visible while scrolling (0)
+ --with-default-fcc=VALUE
+ Default sent mail folder (sent-mail)
+ --with-default-save-folder=VALUE
+ Default save folder (saved-messages)
+ --with-default-legacy-postponed-folder=VALUE
+ Pre Pine 3.90 postponed folder (postponed-mail)
+ --with-default-postponed-folder=VALUE
+ Default postponed folder (postponed-msgs)
+ --with-default-trash-folder=VALUE
+ Default Trash folder for Web Alpine (Trash)
+ --with-default-interrupted-mail=VALUE
+ Default folder for interrupted mail
+ (.pine-interrupted-mail)
+ --with-default-dead-letter-folder=VALUE
+ Default dead letter folder (dead.letter)
+ --with-default-mail-directory=VALUE
+ Default mail directory (mail)
+ --with-default-inbox-name=VALUE
+ Default inbox name (INBOX)
+ --with-default-signature-file=VALUE
+ Default signature file (.signature)
+ --with-default-elm-style-save=VALUE
+ Default to Elm style save (no)
+ --with-default-header-in-reply=VALUE
+ Include header in reply (no)
+ --with-default-old-style-reply=VALUE
+ Default to old style reply (no)
+ --with-default-use-only-domain-name=VALUE
+ Default to using only the domain name (no)
+ --with-default-save-by-sender=VALUE
+ Default to save by sender (no)
+ --with-default-sort-key=VALUE
+ Default sort key (arrival)
+ --with-default-addressbook-sort-rule=VALUE
+ Default addressbook sort rule
+ (fullname-with-lists-last)
+ --with-default-folder-sort-rule=VALUE
+ Default folder sort rule (alphabetical)
+ --with-default-saved-message-name-rule=VALUE
+ Default saved message name rule (default-folder)
+ --with-default-fcc-rule=VALUE
+ Default fcc rule (default-fcc)
+ --with-default-standard-printer=VALUE
+ Default standard printern (lpr)
+ --with-default-ansi-printer=VALUE
+ ANSI printer definition (attached-to-ansi)
+ --with-default-addressbook=VALUE
+ Default addressbook name (.addressbook)
+ --with-default-local-fullname=VALUE
+ Default local support fullname ("Local Support")
+ --with-default-local-address=VALUE
+ Default local support address (postmaster)
+ --with-default-keyboard-lock-count=VALUE
+ Default keyboard lock count (1)
+ --with-default-remote-addressbook-history=VALUE
+ Default address book history count (3)
+ --with-smime-public-cert-directory=VALUE
+ Default Public Cert Directory (.alpine-smime/public)
+ --with-smime-private-key-directory=VALUE
+ Default Private Key Directory
+ (.alpine-smime/private)
+ --with-smime-cacert-directory=VALUE
+ Default Cert Authority Directory (.alpine-smime/ca)
+ --with-default-printer=VALUE
+ Default printer (ANSI_PRINTER)
+ --with-passfile=FILENAME
+ Password cache file (NOT secure, NOT recommended)
+ --without-local-password-cache
+ Disable OS-specific password cache, if supported
+ --with-local-password-cache-method
+ OS-specific credential cache (OSX=APPLEKEYCHAIN,
+ Windows=WINCRED)
+ --with-default-sshpath=FILENAME
+ set default value of ssh command path (defining
+ should cause ssh to be preferred to rsh)
+ --with-default-sshcmd=PERCENT_S_STRING
+ set default value of ssh command string (usually "%s
+ %s -l %s exec /etc/r%sd")
+ --without-ssl Disable SSL support (OpenSSL)
+ --with-ssl-dir=DIR Root of SSL lib/include path
+ --with-ssl-certs-dir=DIR
+ Path to SSL certificate directory
+ --with-ssl-include-dir=DIR
+ SSL include file path
+ --with-ssl-lib-dir=DIR SSL library path
+ --without-krb5 Disable Kerberos support
+ --with-krb5-dir=DIR Root of Kerberos lib/include path
+ --with-krb5-include-dir=DIR
+ Kerberos include file path
+ --with-krb5-lib-dir=DIR Kerberos library path
+ --without-ldap Disable LDAP query support
+ --with-ldap-dir=DIR Root of LDAP lib/include path
+ --with-ldap-include-dir=DIR
+ Directory containing LDAP include files
+ --with-ldap-lib-dir=DIR LDAP library path
+ --without-smime Disable S/MIME
+ --without-tcl Disable TCL, thus Web Alpine support
+ --with-tcl-lib=LIBRARY Specific TCL Library, like \"tcl8.4\"
+ --with-tcl-include=DIR Directory containing TCL include files
+ --with-supplied-regex Use regex library supplied with alpine
+ --without-pthread Do NOT test for nor build with POSIX thread support
+ --with-system-mail-directory=DIR
+ Directory where local mail is delivered
+ --with-c-client-target=TARGET
+ IMAP build target (see imap/Makefile)
+ --without-ipv6 Disable IPv6, primarily to work around resolver
+ problems
+
+Some influential environment variables:
+ CC C compiler command
+ CFLAGS C compiler flags
+ LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
+ nonstandard directory <lib dir>
+ LIBS libraries to pass to the linker, e.g. -l<library>
+ CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
+ you have headers in a nonstandard directory <include dir>
+ CPP C preprocessor
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to <alpine-contact@u.washington.edu>.
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+ # If there are subdirs, report their specific --help.
+ for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+ test -d "$ac_dir" ||
+ { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+ continue
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+ cd "$ac_dir" || { ac_status=$?; continue; }
+ # Check for guested configure.
+ if test -f "$ac_srcdir/configure.gnu"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+ elif test -f "$ac_srcdir/configure"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure" --help=recursive
+ else
+ $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+ fi || ac_status=$?
+ cd "$ac_pwd" || { ac_status=$?; break; }
+ done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+ cat <<\_ACEOF
+alpine configure 2.10
+generated by GNU Autoconf 2.68
+
+Copyright (C) 2010 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+ exit
+fi
+
+## ------------------------ ##
+## Autoconf initialization. ##
+## ------------------------ ##
+
+@%:@ ac_fn_c_try_compile LINENO
+@%:@ --------------------------
+@%:@ Try to compile conftest.@S|@ac_ext, and return whether this succeeded.
+ac_fn_c_try_compile ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ rm -f conftest.$ac_objext
+ if { { ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compile") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} @%:@ ac_fn_c_try_compile
+
+@%:@ ac_fn_c_try_link LINENO
+@%:@ -----------------------
+@%:@ Try to link conftest.@S|@ac_ext, and return whether this succeeded.
+ac_fn_c_try_link ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ rm -f conftest.$ac_objext conftest$ac_exeext
+ if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
+ # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
+ # interfere with the next link command; also delete a directory that is
+ # left behind by Apple's compiler. We do this before executing the actions.
+ rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} @%:@ ac_fn_c_try_link
+
+@%:@ ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
+@%:@ -------------------------------------------------------
+@%:@ Tests whether HEADER exists and can be compiled using the include files in
+@%:@ INCLUDES, setting the cache variable VAR accordingly.
+ac_fn_c_check_header_compile ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+@%:@include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ eval "$3=yes"
+else
+ eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} @%:@ ac_fn_c_check_header_compile
+
+@%:@ ac_fn_c_try_cpp LINENO
+@%:@ ----------------------
+@%:@ Try to preprocess conftest.@S|@ac_ext, and return whether this succeeded.
+ac_fn_c_try_cpp ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if { { ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } > conftest.i && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} @%:@ ac_fn_c_try_cpp
+
+@%:@ ac_fn_c_try_run LINENO
+@%:@ ----------------------
+@%:@ Try to link conftest.@S|@ac_ext, and return whether this succeeded. Assumes
+@%:@ that executables *can* be run.
+ac_fn_c_try_run ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'
+ { { case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; }; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: program exited with status $ac_status" >&5
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=$ac_status
+fi
+ rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} @%:@ ac_fn_c_try_run
+
+@%:@ ac_fn_c_check_func LINENO FUNC VAR
+@%:@ ----------------------------------
+@%:@ Tests whether FUNC exists, setting the cache variable VAR accordingly
+ac_fn_c_check_func ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $2 innocuous_$2
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $2 (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $2
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $2 ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_$2 || defined __stub___$2
+choke me
+#endif
+
+int
+main ()
+{
+return $2 ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ eval "$3=yes"
+else
+ eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} @%:@ ac_fn_c_check_func
+
+@%:@ ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES
+@%:@ -------------------------------------------------------
+@%:@ Tests whether HEADER exists, giving a warning if it cannot be compiled using
+@%:@ the include files in INCLUDES and setting the cache variable VAR
+@%:@ accordingly.
+ac_fn_c_check_header_mongrel ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if eval \${$3+:} false; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5
+$as_echo_n "checking $2 usability... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+@%:@include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_header_compiler=yes
+else
+ ac_header_compiler=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5
+$as_echo_n "checking $2 presence... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+@%:@include <$2>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ ac_header_preproc=yes
+else
+ ac_header_preproc=no
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #((
+ yes:no: )
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+( $as_echo "## ---------------------------------------------- ##
+## Report this to alpine-contact@u.washington.edu ##
+## ---------------------------------------------- ##"
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ eval "$3=\$ac_header_compiler"
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} @%:@ ac_fn_c_check_header_mongrel
+
+@%:@ ac_fn_c_check_type LINENO TYPE VAR INCLUDES
+@%:@ -------------------------------------------
+@%:@ Tests whether TYPE exists after having included INCLUDES, setting cache
+@%:@ variable VAR accordingly.
+ac_fn_c_check_type ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ eval "$3=no"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+if (sizeof ($2))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+if (sizeof (($2)))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+ eval "$3=yes"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} @%:@ ac_fn_c_check_type
+
+@%:@ ac_fn_c_compute_int LINENO EXPR VAR INCLUDES
+@%:@ --------------------------------------------
+@%:@ Tries to find the compile-time value of EXPR in a program that includes
+@%:@ INCLUDES, setting VAR accordingly. Returns whether the value could be
+@%:@ computed
+ac_fn_c_compute_int ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if test "$cross_compiling" = yes; then
+ # Depending upon the size, compute the lo and hi bounds.
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+static int test_array @<:@1 - 2 * !(($2) >= 0)@:>@;
+test_array @<:@0@:>@ = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_lo=0 ac_mid=0
+ while :; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+static int test_array @<:@1 - 2 * !(($2) <= $ac_mid)@:>@;
+test_array @<:@0@:>@ = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_hi=$ac_mid; break
+else
+ as_fn_arith $ac_mid + 1 && ac_lo=$as_val
+ if test $ac_lo -le $ac_mid; then
+ ac_lo= ac_hi=
+ break
+ fi
+ as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ done
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+static int test_array @<:@1 - 2 * !(($2) < 0)@:>@;
+test_array @<:@0@:>@ = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_hi=-1 ac_mid=-1
+ while :; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+static int test_array @<:@1 - 2 * !(($2) >= $ac_mid)@:>@;
+test_array @<:@0@:>@ = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_lo=$ac_mid; break
+else
+ as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val
+ if test $ac_mid -le $ac_hi; then
+ ac_lo= ac_hi=
+ break
+ fi
+ as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ done
+else
+ ac_lo= ac_hi=
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+# Binary search between lo and hi bounds.
+while test "x$ac_lo" != "x$ac_hi"; do
+ as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+static int test_array @<:@1 - 2 * !(($2) <= $ac_mid)@:>@;
+test_array @<:@0@:>@ = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_hi=$ac_mid
+else
+ as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+case $ac_lo in @%:@((
+?*) eval "$3=\$ac_lo"; ac_retval=0 ;;
+'') ac_retval=1 ;;
+esac
+ else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+static long int longval () { return $2; }
+static unsigned long int ulongval () { return $2; }
+@%:@include <stdio.h>
+@%:@include <stdlib.h>
+int
+main ()
+{
+
+ FILE *f = fopen ("conftest.val", "w");
+ if (! f)
+ return 1;
+ if (($2) < 0)
+ {
+ long int i = longval ();
+ if (i != ($2))
+ return 1;
+ fprintf (f, "%ld", i);
+ }
+ else
+ {
+ unsigned long int i = ulongval ();
+ if (i != ($2))
+ return 1;
+ fprintf (f, "%lu", i);
+ }
+ /* Do not output a trailing newline, as this causes \r\n confusion
+ on some platforms. */
+ return ferror (f) || fclose (f) != 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ echo >>conftest.val; read $3 <conftest.val; ac_retval=0
+else
+ ac_retval=1
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+rm -f conftest.val
+
+ fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} @%:@ ac_fn_c_compute_int
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by alpine $as_me 2.10, which was
+generated by GNU Autoconf 2.68. Invocation command line was
+
+ $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`
+
+/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown`
+/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`
+/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ $as_echo "PATH: $as_dir"
+ done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+ for ac_arg
+ do
+ case $ac_arg in
+ -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ continue ;;
+ *\'*)
+ ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ case $ac_pass in
+ 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
+ 2)
+ as_fn_append ac_configure_args1 " '$ac_arg'"
+ if test $ac_must_keep_next = true; then
+ ac_must_keep_next=false # Got value, back to normal.
+ else
+ case $ac_arg in
+ *=* | --config-cache | -C | -disable-* | --disable-* \
+ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+ | -with-* | --with-* | -without-* | --without-* | --x)
+ case "$ac_configure_args0 " in
+ "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+ esac
+ ;;
+ -* ) ac_must_keep_next=true ;;
+ esac
+ fi
+ as_fn_append ac_configure_args " '$ac_arg'"
+ ;;
+ esac
+ done
+done
+{ ac_configure_args0=; unset ac_configure_args0;}
+{ ac_configure_args1=; unset ac_configure_args1;}
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log. We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+ # Save into config.log some information that might help in debugging.
+ {
+ echo
+
+ $as_echo "## ---------------- ##
+## Cache variables. ##
+## ---------------- ##"
+ echo
+ # The following way of writing the cache mishandles newlines in values,
+(
+ for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+ *) { eval $ac_var=; unset $ac_var;} ;;
+ esac ;;
+ esac
+ done
+ (set) 2>&1 |
+ case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ sed -n \
+ "s/'\''/'\''\\\\'\'''\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+ ;; #(
+ *)
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+)
+ echo
+
+ $as_echo "## ----------------- ##
+## Output variables. ##
+## ----------------- ##"
+ echo
+ for ac_var in $ac_subst_vars
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ $as_echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+
+ if test -n "$ac_subst_files"; then
+ $as_echo "## ------------------- ##
+## File substitutions. ##
+## ------------------- ##"
+ echo
+ for ac_var in $ac_subst_files
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ $as_echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+ fi
+
+ if test -s confdefs.h; then
+ $as_echo "## ----------- ##
+## confdefs.h. ##
+## ----------- ##"
+ echo
+ cat confdefs.h
+ echo
+ fi
+ test "$ac_signal" != 0 &&
+ $as_echo "$as_me: caught signal $ac_signal"
+ $as_echo "$as_me: exit $exit_status"
+ } >&5
+ rm -f core *.core core.conftest.* &&
+ rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+ exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+ trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+$as_echo "/* confdefs.h */" > confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_URL "$PACKAGE_URL"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+ac_site_file1=NONE
+ac_site_file2=NONE
+if test -n "$CONFIG_SITE"; then
+ # We do not want a PATH search for config.site.
+ case $CONFIG_SITE in @%:@((
+ -*) ac_site_file1=./$CONFIG_SITE;;
+ */*) ac_site_file1=$CONFIG_SITE;;
+ *) ac_site_file1=./$CONFIG_SITE;;
+ esac
+elif test "x$prefix" != xNONE; then
+ ac_site_file1=$prefix/share/config.site
+ ac_site_file2=$prefix/etc/config.site
+else
+ ac_site_file1=$ac_default_prefix/share/config.site
+ ac_site_file2=$ac_default_prefix/etc/config.site
+fi
+for ac_site_file in "$ac_site_file1" "$ac_site_file2"
+do
+ test "x$ac_site_file" = xNONE && continue
+ if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+$as_echo "$as_me: loading site script $ac_site_file" >&6;}
+ sed 's/^/| /' "$ac_site_file" >&5
+ . "$ac_site_file" \
+ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed to load site script $ac_site_file
+See \`config.log' for more details" "$LINENO" 5; }
+ fi
+done
+
+if test -r "$cache_file"; then
+ # Some versions of bash will fail to source /dev/null (special files
+ # actually), so we avoid doing that. DJGPP emulates it as a regular file.
+ if test /dev/null != "$cache_file" && test -f "$cache_file"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
+$as_echo "$as_me: loading cache $cache_file" >&6;}
+ case $cache_file in
+ [\\/]* | ?:[\\/]* ) . "$cache_file";;
+ *) . "./$cache_file";;
+ esac
+ fi
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
+$as_echo "$as_me: creating cache $cache_file" >&6;}
+ >$cache_file
+fi
+
+gt_needs="$gt_needs "
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+ eval ac_old_set=\$ac_cv_env_${ac_var}_set
+ eval ac_new_set=\$ac_env_${ac_var}_set
+ eval ac_old_val=\$ac_cv_env_${ac_var}_value
+ eval ac_new_val=\$ac_env_${ac_var}_value
+ case $ac_old_set,$ac_new_set in
+ set,)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,set)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,);;
+ *)
+ if test "x$ac_old_val" != "x$ac_new_val"; then
+ # differences in whitespace do not lead to failure.
+ ac_old_val_w=`echo x $ac_old_val`
+ ac_new_val_w=`echo x $ac_new_val`
+ if test "$ac_old_val_w" != "$ac_new_val_w"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
+$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+ ac_cache_corrupted=:
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+ eval $ac_var=\$ac_old_val
+ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5
+$as_echo "$as_me: former value: \`$ac_old_val'" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5
+$as_echo "$as_me: current value: \`$ac_new_val'" >&2;}
+ fi;;
+ esac
+ # Pass precious variables to config.status.
+ if test "$ac_new_set" = set; then
+ case $ac_new_val in
+ *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+ *) ac_arg=$ac_var=$ac_new_val ;;
+ esac
+ case " $ac_configure_args " in
+ *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
+ *) as_fn_append ac_configure_args " '$ac_arg'" ;;
+ esac
+ fi
+done
+if $ac_cache_corrupted; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
+$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+ as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
+fi
+## -------------------- ##
+## Main body of script. ##
+## -------------------- ##
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+ac_config_headers="$ac_config_headers include/config.h"
+
+
+am__api_version='1.11'
+
+ac_aux_dir=
+for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
+ if test -f "$ac_dir/install-sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f "$ac_dir/install.sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ elif test -f "$ac_dir/shtool"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/shtool install -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5
+fi
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var.
+ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var.
+ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
+
+
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+# Reject install programs that cannot install multiple files.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5
+$as_echo_n "checking for a BSD-compatible install... " >&6; }
+if test -z "$INSTALL"; then
+if ${ac_cv_path_install+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in @%:@((
+ ./ | .// | /[cC]/* | \
+ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \
+ /usr/ucb/* ) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ # Don't use installbsd from OSF since it installs stuff as root
+ # by default.
+ for ac_prog in ginstall scoinst install; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then
+ if test $ac_prog = install &&
+ grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ :
+ elif test $ac_prog = install &&
+ grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # program-specific install script used by HP pwplus--don't use.
+ :
+ else
+ rm -rf conftest.one conftest.two conftest.dir
+ echo one > conftest.one
+ echo two > conftest.two
+ mkdir conftest.dir
+ if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" &&
+ test -s conftest.one && test -s conftest.two &&
+ test -s conftest.dir/conftest.one &&
+ test -s conftest.dir/conftest.two
+ then
+ ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+ break 3
+ fi
+ fi
+ fi
+ done
+ done
+ ;;
+esac
+
+ done
+IFS=$as_save_IFS
+
+rm -rf conftest.one conftest.two conftest.dir
+
+fi
+ if test "${ac_cv_path_install+set}" = set; then
+ INSTALL=$ac_cv_path_install
+ else
+ # As a last resort, use the slow shell script. Don't cache a
+ # value for INSTALL within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the value is a relative name.
+ INSTALL=$ac_install_sh
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5
+$as_echo "$INSTALL" >&6; }
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5
+$as_echo_n "checking whether build environment is sane... " >&6; }
+# Just in case
+sleep 1
+echo timestamp > conftest.file
+# Reject unsafe characters in $srcdir or the absolute working directory
+# name. Accept space and tab only in the latter.
+am_lf='
+'
+case `pwd` in
+ *[\\\"\#\$\&\'\`$am_lf]*)
+ as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;;
+esac
+case $srcdir in
+ *[\\\"\#\$\&\'\`$am_lf\ \ ]*)
+ as_fn_error $? "unsafe srcdir value: \`$srcdir'" "$LINENO" 5;;
+esac
+
+# Do `set' in a subshell so we don't clobber the current shell's
+# arguments. Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+ set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
+ if test "$*" = "X"; then
+ # -L didn't work.
+ set X `ls -t "$srcdir/configure" conftest.file`
+ fi
+ rm -f conftest.file
+ if test "$*" != "X $srcdir/configure conftest.file" \
+ && test "$*" != "X conftest.file $srcdir/configure"; then
+
+ # If neither matched, then we have a broken ls. This can happen
+ # if, for instance, CONFIG_SHELL is bash and it inherits a
+ # broken ls alias from the environment. This has actually
+ # happened. Such a system could not be considered "sane".
+ as_fn_error $? "ls -t appears to fail. Make sure there is not a broken
+alias in your environment" "$LINENO" 5
+ fi
+
+ test "$2" = conftest.file
+ )
+then
+ # Ok.
+ :
+else
+ as_fn_error $? "newly created file is older than distributed files!
+Check your system clock" "$LINENO" 5
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+test "$program_prefix" != NONE &&
+ program_transform_name="s&^&$program_prefix&;$program_transform_name"
+# Use a double $ so make ignores it.
+test "$program_suffix" != NONE &&
+ program_transform_name="s&\$&$program_suffix&;$program_transform_name"
+# Double any \ or $.
+# By default was `s,x,x', remove it if useless.
+ac_script='s/[\\$]/&&/g;s/;s,x,x,$//'
+program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"`
+
+# expand $ac_aux_dir to an absolute path
+am_aux_dir=`cd $ac_aux_dir && pwd`
+
+if test x"${MISSING+set}" != xset; then
+ case $am_aux_dir in
+ *\ * | *\ *)
+ MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
+ *)
+ MISSING="\${SHELL} $am_aux_dir/missing" ;;
+ esac
+fi
+# Use eval to expand $SHELL
+if eval "$MISSING --run true"; then
+ am_missing_run="$MISSING --run "
+else
+ am_missing_run=
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`missing' script is too old or missing" >&5
+$as_echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;}
+fi
+
+if test x"${install_sh}" != xset; then
+ case $am_aux_dir in
+ *\ * | *\ *)
+ install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
+ *)
+ install_sh="\${SHELL} $am_aux_dir/install-sh"
+ esac
+fi
+
+# Installed binaries are usually stripped using `strip' when the user
+# run `make install-strip'. However `strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the `STRIP' environment variable to overrule this program.
+if test "$cross_compiling" != no; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_STRIP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$STRIP"; then
+ ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5
+$as_echo "$STRIP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_STRIP"; then
+ ac_ct_STRIP=$STRIP
+ # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_STRIP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_STRIP"; then
+ ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_STRIP="strip"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5
+$as_echo "$ac_ct_STRIP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_STRIP" = x; then
+ STRIP=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ STRIP=$ac_ct_STRIP
+ fi
+else
+ STRIP="$ac_cv_prog_STRIP"
+fi
+
+fi
+INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5
+$as_echo_n "checking for a thread-safe mkdir -p... " >&6; }
+if test -z "$MKDIR_P"; then
+ if ${ac_cv_path_mkdir+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in mkdir gmkdir; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; } || continue
+ case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #(
+ 'mkdir (GNU coreutils) '* | \
+ 'mkdir (coreutils) '* | \
+ 'mkdir (fileutils) '4.1*)
+ ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext
+ break 3;;
+ esac
+ done
+ done
+ done
+IFS=$as_save_IFS
+
+fi
+
+ test -d ./--version && rmdir ./--version
+ if test "${ac_cv_path_mkdir+set}" = set; then
+ MKDIR_P="$ac_cv_path_mkdir -p"
+ else
+ # As a last resort, use the slow shell script. Don't cache a
+ # value for MKDIR_P within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the value is a relative name.
+ MKDIR_P="$ac_install_sh -d"
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5
+$as_echo "$MKDIR_P" >&6; }
+
+mkdir_p="$MKDIR_P"
+case $mkdir_p in
+ [\\/$]* | ?:[\\/]*) ;;
+ */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;;
+esac
+
+for ac_prog in gawk mawk nawk awk
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_AWK+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$AWK"; then
+ ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_AWK="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+AWK=$ac_cv_prog_AWK
+if test -n "$AWK"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5
+$as_echo "$AWK" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$AWK" && break
+done
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5
+$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; }
+set x ${MAKE-make}
+ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
+if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.make <<\_ACEOF
+SHELL = /bin/sh
+all:
+ @echo '@@@%%%=$(MAKE)=@@@%%%'
+_ACEOF
+# GNU make sometimes prints "make[1]: Entering ...", which would confuse us.
+case `${MAKE-make} -f conftest.make 2>/dev/null` in
+ *@@@%%%=?*=@@@%%%*)
+ eval ac_cv_prog_make_${ac_make}_set=yes;;
+ *)
+ eval ac_cv_prog_make_${ac_make}_set=no;;
+esac
+rm -f conftest.make
+fi
+if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ SET_MAKE=
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+ am__leading_dot=.
+else
+ am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+
+if test "`cd $srcdir && pwd`" != "`pwd`"; then
+ # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
+ # is not polluted with repeated "-I."
+ am__isrc=' -I$(srcdir)'
+ # test to see if srcdir already configured
+ if test -f $srcdir/config.status; then
+ as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5
+ fi
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+ if (cygpath --version) >/dev/null 2>/dev/null; then
+ CYGPATH_W='cygpath -w'
+ else
+ CYGPATH_W=echo
+ fi
+fi
+
+
+# Define the identity of the package.
+ PACKAGE='alpine'
+ VERSION='2.10'
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE "$PACKAGE"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define VERSION "$VERSION"
+_ACEOF
+
+# Some tools Automake needs.
+
+ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"}
+
+
+AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"}
+
+
+AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"}
+
+
+AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"}
+
+
+MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"}
+
+# We need awk for the "check" target. The system "awk" is bad on
+# some platforms.
+# Always define AMTAR for backward compatibility.
+
+AMTAR=${AMTAR-"${am_missing_run}tar"}
+
+am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable maintainer-specific portions of Makefiles" >&5
+$as_echo_n "checking whether to enable maintainer-specific portions of Makefiles... " >&6; }
+ @%:@ Check whether --enable-maintainer-mode was given.
+if test "${enable_maintainer_mode+set}" = set; then :
+ enableval=$enable_maintainer_mode; USE_MAINTAINER_MODE=$enableval
+else
+ USE_MAINTAINER_MODE=no
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $USE_MAINTAINER_MODE" >&5
+$as_echo "$USE_MAINTAINER_MODE" >&6; }
+ if test $USE_MAINTAINER_MODE = yes; then
+ MAINTAINER_MODE_TRUE=
+ MAINTAINER_MODE_FALSE='#'
+else
+ MAINTAINER_MODE_TRUE='#'
+ MAINTAINER_MODE_FALSE=
+fi
+
+ MAINT=$MAINTAINER_MODE_TRUE
+
+
+
+# Make sure we can run config.sub.
+$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
+ as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5
+$as_echo_n "checking build system type... " >&6; }
+if ${ac_cv_build+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_build_alias=$build_alias
+test "x$ac_build_alias" = x &&
+ ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"`
+test "x$ac_build_alias" = x &&
+ as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5
+ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` ||
+ as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5
+$as_echo "$ac_cv_build" >&6; }
+case $ac_cv_build in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;;
+esac
+build=$ac_cv_build
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_build
+shift
+build_cpu=$1
+build_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+build_os=$*
+IFS=$ac_save_IFS
+case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5
+$as_echo_n "checking host system type... " >&6; }
+if ${ac_cv_host+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "x$host_alias" = x; then
+ ac_cv_host=$ac_cv_build
+else
+ ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` ||
+ as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5
+$as_echo "$ac_cv_host" >&6; }
+case $ac_cv_host in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;;
+esac
+host=$ac_cv_host
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_host
+shift
+host_cpu=$1
+host_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+host_os=$*
+IFS=$ac_save_IFS
+case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: Configuring for $PACKAGE_STRING ($host))" >&5
+$as_echo "$as_me: Configuring for $PACKAGE_STRING ($host))" >&6;}
+
+# start out with intent to build Web Alpine
+WEB_BUILD=web/src/alpined.d
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="${ac_tool_prefix}gcc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_CC="gcc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="${ac_tool_prefix}cc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ fi
+fi
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $@%:@ != 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+ fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in cl.exe
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$CC" && break
+ done
+fi
+if test -z "$CC"; then
+ ac_ct_CC=$CC
+ for ac_prog in cl.exe
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_CC="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_CC" && break
+done
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+fi
+
+fi
+
+
+test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "no acceptable C compiler found in \$PATH
+See \`config.log' for more details" "$LINENO" 5; }
+
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+ { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ sed '10a\
+... rest of stderr output deleted ...
+ 10q' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ fi
+ rm -f conftest.er1 conftest.err
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+done
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
+$as_echo_n "checking whether the C compiler works... " >&6; }
+ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+ * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+ esac
+done
+rm -f $ac_rmfiles
+
+if { { ac_try="$ac_link_default"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link_default") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile. We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+ ;;
+ [ab].out )
+ # We found the default executable, but exeext='' is most
+ # certainly right.
+ break;;
+ *.* )
+ if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+ then :; else
+ ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ fi
+ # We set ac_cv_exeext here because the later test for it is not
+ # safe: cross compilers may not add the suffix if given an `-o'
+ # argument, so we may need to know it at that point already.
+ # Even if this section looks crufty: it has the advantage of
+ # actually working.
+ break;;
+ * )
+ break;;
+ esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+ ac_file=''
+fi
+if test -z "$ac_file"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "C compiler cannot create executables
+See \`config.log' for more details" "$LINENO" 5; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
+$as_echo_n "checking for C compiler default output file name... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+$as_echo "$ac_file" >&6; }
+ac_exeext=$ac_cv_exeext
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
+$as_echo_n "checking for suffix of executables... " >&6; }
+if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+ *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ break;;
+ * ) break;;
+ esac
+done
+else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest conftest$ac_cv_exeext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+$as_echo "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+@%:@include <stdio.h>
+int
+main ()
+{
+FILE *f = fopen ("conftest.out", "w");
+ return ferror (f) || fclose (f) != 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files="$ac_clean_files conftest.out"
+# Check that the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+$as_echo_n "checking whether we are cross compiling... " >&6; }
+if test "$cross_compiling" != yes; then
+ { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+ if { ac_try='./conftest$ac_cv_exeext'
+ { { case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; }; then
+ cross_compiling=no
+ else
+ if test "$cross_compiling" = maybe; then
+ cross_compiling=yes
+ else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details" "$LINENO" 5; }
+ fi
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
+$as_echo "$cross_compiling" >&6; }
+
+rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
+$as_echo_n "checking for suffix of object files... " >&6; }
+if ${ac_cv_objext+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { { ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compile") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ for ac_file in conftest.o conftest.obj conftest.*; do
+ test -f "$ac_file" || continue;
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+ *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+ break;;
+ esac
+done
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of object files: cannot compile
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
+$as_echo "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
+$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
+if ${ac_cv_c_compiler_gnu+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_compiler_gnu=yes
+else
+ ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+ GCC=yes
+else
+ GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+$as_echo_n "checking whether $CC accepts -g... " >&6; }
+if ${ac_cv_prog_cc_g+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_save_c_werror_flag=$ac_c_werror_flag
+ ac_c_werror_flag=yes
+ ac_cv_prog_cc_g=no
+ CFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_g=yes
+else
+ CFLAGS=""
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+ ac_c_werror_flag=$ac_save_c_werror_flag
+ CFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+$as_echo "$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if ${ac_cv_prog_cc_c89+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
+ function prototypes and stuff, but not '\xHH' hex character constants.
+ These don't provoke an error unfortunately, instead are silently treated
+ as 'x'. The following induces an error, until -std is added to get
+ proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
+ array size at least. It's necessary to write '\x00'==0 to get something
+ that's true only with -std. */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+ inside strings and character constants. */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
+ ;
+ return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+ test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+ x)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+ xno)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+ *)
+ CC="$CC $ac_cv_prog_cc_c89"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c89" != xno; then :
+
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+DEPDIR="${am__leading_dot}deps"
+
+ac_config_commands="$ac_config_commands depfiles"
+
+
+am_make=${MAKE-make}
+cat > confinc << 'END'
+am__doit:
+ @echo this is the am__doit target
+.PHONY: am__doit
+END
+# If we don't find an include directive, just comment out the code.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5
+$as_echo_n "checking for style of include used by $am_make... " >&6; }
+am__include="#"
+am__quote=
+_am_result=none
+# First try GNU make style include.
+echo "include confinc" > confmf
+# Ignore all kinds of additional output from `make'.
+case `$am_make -s -f confmf 2> /dev/null` in #(
+*the\ am__doit\ target*)
+ am__include=include
+ am__quote=
+ _am_result=GNU
+ ;;
+esac
+# Now try BSD make style include.
+if test "$am__include" = "#"; then
+ echo '.include "confinc"' > confmf
+ case `$am_make -s -f confmf 2> /dev/null` in #(
+ *the\ am__doit\ target*)
+ am__include=.include
+ am__quote="\""
+ _am_result=BSD
+ ;;
+ esac
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5
+$as_echo "$_am_result" >&6; }
+rm -f confinc confmf
+
+@%:@ Check whether --enable-dependency-tracking was given.
+if test "${enable_dependency_tracking+set}" = set; then :
+ enableval=$enable_dependency_tracking;
+fi
+
+if test "x$enable_dependency_tracking" != xno; then
+ am_depcomp="$ac_aux_dir/depcomp"
+ AMDEPBACKSLASH='\'
+fi
+ if test "x$enable_dependency_tracking" != xno; then
+ AMDEP_TRUE=
+ AMDEP_FALSE='#'
+else
+ AMDEP_TRUE='#'
+ AMDEP_FALSE=
+fi
+
+
+
+depcc="$CC" am_compiler_list=
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5
+$as_echo_n "checking dependency style of $depcc... " >&6; }
+if ${am_cv_CC_dependencies_compiler_type+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+ # We make a subdir and do the tests there. Otherwise we can end up
+ # making bogus files that we don't know about and never remove. For
+ # instance it was reported that on HP-UX the gcc test will end up
+ # making a dummy file named `D' -- because `-MD' means `put the output
+ # in D'.
+ mkdir conftest.dir
+ # Copy depcomp to subdir because otherwise we won't find it if we're
+ # using a relative directory.
+ cp "$am_depcomp" conftest.dir
+ cd conftest.dir
+ # We will build objects and dependencies in a subdirectory because
+ # it helps to detect inapplicable dependency modes. For instance
+ # both Tru64's cc and ICC support -MD to output dependencies as a
+ # side effect of compilation, but ICC will put the dependencies in
+ # the current directory while Tru64 will put them in the object
+ # directory.
+ mkdir sub
+
+ am_cv_CC_dependencies_compiler_type=none
+ if test "$am_compiler_list" = ""; then
+ am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
+ fi
+ am__universal=false
+ case " $depcc " in #(
+ *\ -arch\ *\ -arch\ *) am__universal=true ;;
+ esac
+
+ for depmode in $am_compiler_list; do
+ # Setup a source with many dependencies, because some compilers
+ # like to wrap large dependency lists on column 80 (with \), and
+ # we should not choose a depcomp mode which is confused by this.
+ #
+ # We need to recreate these files for each test, as the compiler may
+ # overwrite some of them when testing with obscure command lines.
+ # This happens at least with the AIX C compiler.
+ : > sub/conftest.c
+ for i in 1 2 3 4 5 6; do
+ echo '#include "conftst'$i'.h"' >> sub/conftest.c
+ # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
+ # Solaris 8's {/usr,}/bin/sh.
+ touch sub/conftst$i.h
+ done
+ echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+ # We check with `-c' and `-o' for the sake of the "dashmstdout"
+ # mode. It turns out that the SunPro C++ compiler does not properly
+ # handle `-M -o', and we need to detect this. Also, some Intel
+ # versions had trouble with output in subdirs
+ am__obj=sub/conftest.${OBJEXT-o}
+ am__minus_obj="-o $am__obj"
+ case $depmode in
+ gcc)
+ # This depmode causes a compiler race in universal mode.
+ test "$am__universal" = false || continue
+ ;;
+ nosideeffect)
+ # after this tag, mechanisms are not by side-effect, so they'll
+ # only be used when explicitly requested
+ if test "x$enable_dependency_tracking" = xyes; then
+ continue
+ else
+ break
+ fi
+ ;;
+ msvisualcpp | msvcmsys)
+ # This compiler won't grok `-c -o', but also, the minuso test has
+ # not run yet. These depmodes are late enough in the game, and
+ # so weak that their functioning should not be impacted.
+ am__obj=conftest.${OBJEXT-o}
+ am__minus_obj=
+ ;;
+ none) break ;;
+ esac
+ if depmode=$depmode \
+ source=sub/conftest.c object=$am__obj \
+ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
+ >/dev/null 2>conftest.err &&
+ grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
+ ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+ # icc doesn't choke on unknown options, it will just issue warnings
+ # or remarks (even with -Werror). So we grep stderr for any message
+ # that says an option was ignored or not supported.
+ # When given -MP, icc 7.0 and 7.1 complain thusly:
+ # icc: Command line warning: ignoring option '-M'; no argument required
+ # The diagnosis changed in icc 8.0:
+ # icc: Command line remark: option '-MP' not supported
+ if (grep 'ignoring option' conftest.err ||
+ grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+ am_cv_CC_dependencies_compiler_type=$depmode
+ break
+ fi
+ fi
+ done
+
+ cd ..
+ rm -rf conftest.dir
+else
+ am_cv_CC_dependencies_compiler_type=none
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5
+$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; }
+CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type
+
+ if
+ test "x$enable_dependency_tracking" != xno \
+ && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then
+ am__fastdepCC_TRUE=
+ am__fastdepCC_FALSE='#'
+else
+ am__fastdepCC_TRUE='#'
+ am__fastdepCC_FALSE=
+fi
+
+
+ case $ac_cv_prog_cc_stdc in @%:@(
+ no) :
+ ac_cv_prog_cc_c99=no; ac_cv_prog_cc_c89=no ;; @%:@(
+ *) :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C99" >&5
+$as_echo_n "checking for $CC option to accept ISO C99... " >&6; }
+if ${ac_cv_prog_cc_c99+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_prog_cc_c99=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <wchar.h>
+#include <stdio.h>
+
+// Check varargs macros. These examples are taken from C99 6.10.3.5.
+#define debug(...) fprintf (stderr, __VA_ARGS__)
+#define showlist(...) puts (#__VA_ARGS__)
+#define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__))
+static void
+test_varargs_macros (void)
+{
+ int x = 1234;
+ int y = 5678;
+ debug ("Flag");
+ debug ("X = %d\n", x);
+ showlist (The first, second, and third items.);
+ report (x>y, "x is %d but y is %d", x, y);
+}
+
+// Check long long types.
+#define BIG64 18446744073709551615ull
+#define BIG32 4294967295ul
+#define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0)
+#if !BIG_OK
+ your preprocessor is broken;
+#endif
+#if BIG_OK
+#else
+ your preprocessor is broken;
+#endif
+static long long int bignum = -9223372036854775807LL;
+static unsigned long long int ubignum = BIG64;
+
+struct incomplete_array
+{
+ int datasize;
+ double data[];
+};
+
+struct named_init {
+ int number;
+ const wchar_t *name;
+ double average;
+};
+
+typedef const char *ccp;
+
+static inline int
+test_restrict (ccp restrict text)
+{
+ // See if C++-style comments work.
+ // Iterate through items via the restricted pointer.
+ // Also check for declarations in for loops.
+ for (unsigned int i = 0; *(text+i) != '\0'; ++i)
+ continue;
+ return 0;
+}
+
+// Check varargs and va_copy.
+static void
+test_varargs (const char *format, ...)
+{
+ va_list args;
+ va_start (args, format);
+ va_list args_copy;
+ va_copy (args_copy, args);
+
+ const char *str;
+ int number;
+ float fnumber;
+
+ while (*format)
+ {
+ switch (*format++)
+ {
+ case 's': // string
+ str = va_arg (args_copy, const char *);
+ break;
+ case 'd': // int
+ number = va_arg (args_copy, int);
+ break;
+ case 'f': // float
+ fnumber = va_arg (args_copy, double);
+ break;
+ default:
+ break;
+ }
+ }
+ va_end (args_copy);
+ va_end (args);
+}
+
+int
+main ()
+{
+
+ // Check bool.
+ _Bool success = false;
+
+ // Check restrict.
+ if (test_restrict ("String literal") == 0)
+ success = true;
+ char *restrict newvar = "Another string";
+
+ // Check varargs.
+ test_varargs ("s, d' f .", "string", 65, 34.234);
+ test_varargs_macros ();
+
+ // Check flexible array members.
+ struct incomplete_array *ia =
+ malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10));
+ ia->datasize = 10;
+ for (int i = 0; i < ia->datasize; ++i)
+ ia->data[i] = i * 1.234;
+
+ // Check named initializers.
+ struct named_init ni = {
+ .number = 34,
+ .name = L"Test wide string",
+ .average = 543.34343,
+ };
+
+ ni.number = 58;
+
+ int dynamic_array[ni.number];
+ dynamic_array[ni.number - 1] = 543;
+
+ // work around unused variable warnings
+ return (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == 'x'
+ || dynamic_array[ni.number - 1] != 543);
+
+ ;
+ return 0;
+}
+_ACEOF
+for ac_arg in '' -std=gnu99 -std=c99 -c99 -AC99 -xc99=all -qlanglvl=extc99
+do
+ CC="$ac_save_CC $ac_arg"
+ if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_c99=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+ test "x$ac_cv_prog_cc_c99" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c99" in
+ x)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+ xno)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+ *)
+ CC="$CC $ac_cv_prog_cc_c99"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5
+$as_echo "$ac_cv_prog_cc_c99" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c99" != xno; then :
+ ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if ${ac_cv_prog_cc_c89+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
+ function prototypes and stuff, but not '\xHH' hex character constants.
+ These don't provoke an error unfortunately, instead are silently treated
+ as 'x'. The following induces an error, until -std is added to get
+ proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
+ array size at least. It's necessary to write '\x00'==0 to get something
+ that's true only with -std. */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+ inside strings and character constants. */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
+ ;
+ return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+ test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+ x)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+ xno)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+ *)
+ CC="$CC $ac_cv_prog_cc_c89"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c89" != xno; then :
+ ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89
+else
+ ac_cv_prog_cc_stdc=no
+fi
+
+fi
+ ;;
+esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO Standard C" >&5
+$as_echo_n "checking for $CC option to accept ISO Standard C... " >&6; }
+ if ${ac_cv_prog_cc_stdc+:} false; then :
+ $as_echo_n "(cached) " >&6
+fi
+
+ case $ac_cv_prog_cc_stdc in @%:@(
+ no) :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;; @%:@(
+ '') :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;; @%:@(
+ *) :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_stdc" >&5
+$as_echo "$ac_cv_prog_cc_stdc" >&6; } ;;
+esac
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5
+$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; }
+set x ${MAKE-make}
+ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
+if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.make <<\_ACEOF
+SHELL = /bin/sh
+all:
+ @echo '@@@%%%=$(MAKE)=@@@%%%'
+_ACEOF
+# GNU make sometimes prints "make[1]: Entering ...", which would confuse us.
+case `${MAKE-make} -f conftest.make 2>/dev/null` in
+ *@@@%%%=?*=@@@%%%*)
+ eval ac_cv_prog_make_${ac_make}_set=yes;;
+ *)
+ eval ac_cv_prog_make_${ac_make}_set=no;;
+esac
+rm -f conftest.make
+fi
+if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ SET_MAKE=
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5
+$as_echo_n "checking whether ln -s works... " >&6; }
+LN_S=$as_ln_s
+if test "$LN_S" = "ln -s"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5
+$as_echo "no, using $LN_S" >&6; }
+fi
+
+for ac_prog in gawk mawk nawk awk
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_AWK+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$AWK"; then
+ ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_AWK="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+AWK=$ac_cv_prog_AWK
+if test -n "$AWK"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5
+$as_echo "$AWK" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$AWK" && break
+done
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_RANLIB+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$RANLIB"; then
+ ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5
+$as_echo "$RANLIB" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+ ac_ct_RANLIB=$RANLIB
+ # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_RANLIB+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_RANLIB"; then
+ ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_RANLIB="ranlib"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5
+$as_echo "$ac_ct_RANLIB" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_RANLIB" = x; then
+ RANLIB=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ RANLIB=$ac_ct_RANLIB
+ fi
+else
+ RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+case `pwd` in
+ *\ * | *\ *)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5
+$as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;;
+esac
+
+
+
+macro_version='2.2.6b'
+macro_revision='1.3018'
+
+
+
+
+
+
+
+
+
+
+
+
+
+ltmain="$ac_aux_dir/ltmain.sh"
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5
+$as_echo_n "checking for a sed that does not truncate output... " >&6; }
+if ${ac_cv_path_SED+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
+ for ac_i in 1 2 3 4 5 6 7; do
+ ac_script="$ac_script$as_nl$ac_script"
+ done
+ echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed
+ { ac_script=; unset ac_script;}
+ if test -z "$SED"; then
+ ac_path_SED_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in sed gsed; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_SED="$as_dir/$ac_prog$ac_exec_ext"
+ { test -f "$ac_path_SED" && $as_test_x "$ac_path_SED"; } || continue
+# Check for GNU ac_path_SED and select it if it is found.
+ # Check for GNU $ac_path_SED
+case `"$ac_path_SED" --version 2>&1` in
+*GNU*)
+ ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo '' >> "conftest.nl"
+ "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_SED_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_SED="$ac_path_SED"
+ ac_path_SED_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_SED_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_SED"; then
+ as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5
+ fi
+else
+ ac_cv_path_SED=$SED
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5
+$as_echo "$ac_cv_path_SED" >&6; }
+ SED="$ac_cv_path_SED"
+ rm -f conftest.sed
+
+test -z "$SED" && SED=sed
+Xsed="$SED -e 1s/^X//"
+
+
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
+$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
+if ${ac_cv_path_GREP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$GREP"; then
+ ac_path_GREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in grep ggrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
+ { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue
+# Check for GNU ac_path_GREP and select it if it is found.
+ # Check for GNU $ac_path_GREP
+case `"$ac_path_GREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo 'GREP' >> "conftest.nl"
+ "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_GREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_GREP="$ac_path_GREP"
+ ac_path_GREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_GREP_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_GREP"; then
+ as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ fi
+else
+ ac_cv_path_GREP=$GREP
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5
+$as_echo "$ac_cv_path_GREP" >&6; }
+ GREP="$ac_cv_path_GREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5
+$as_echo_n "checking for egrep... " >&6; }
+if ${ac_cv_path_EGREP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
+ then ac_cv_path_EGREP="$GREP -E"
+ else
+ if test -z "$EGREP"; then
+ ac_path_EGREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in egrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
+ { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue
+# Check for GNU ac_path_EGREP and select it if it is found.
+ # Check for GNU $ac_path_EGREP
+case `"$ac_path_EGREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo 'EGREP' >> "conftest.nl"
+ "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_EGREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_EGREP="$ac_path_EGREP"
+ ac_path_EGREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_EGREP_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_EGREP"; then
+ as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ fi
+else
+ ac_cv_path_EGREP=$EGREP
+fi
+
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5
+$as_echo "$ac_cv_path_EGREP" >&6; }
+ EGREP="$ac_cv_path_EGREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5
+$as_echo_n "checking for fgrep... " >&6; }
+if ${ac_cv_path_FGREP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1
+ then ac_cv_path_FGREP="$GREP -F"
+ else
+ if test -z "$FGREP"; then
+ ac_path_FGREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in fgrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext"
+ { test -f "$ac_path_FGREP" && $as_test_x "$ac_path_FGREP"; } || continue
+# Check for GNU ac_path_FGREP and select it if it is found.
+ # Check for GNU $ac_path_FGREP
+case `"$ac_path_FGREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo 'FGREP' >> "conftest.nl"
+ "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_FGREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_FGREP="$ac_path_FGREP"
+ ac_path_FGREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_FGREP_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_FGREP"; then
+ as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ fi
+else
+ ac_cv_path_FGREP=$FGREP
+fi
+
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5
+$as_echo "$ac_cv_path_FGREP" >&6; }
+ FGREP="$ac_cv_path_FGREP"
+
+
+test -z "$GREP" && GREP=grep
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+@%:@ Check whether --with-gnu-ld was given.
+if test "${with_gnu_ld+set}" = set; then :
+ withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes
+else
+ with_gnu_ld=no
+fi
+
+ac_prog=ld
+if test "$GCC" = yes; then
+ # Check if gcc -print-prog-name=ld gives a path.
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5
+$as_echo_n "checking for ld used by $CC... " >&6; }
+ case $host in
+ *-*-mingw*)
+ # gcc leaves a trailing carriage return which upsets mingw
+ ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+ *)
+ ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+ esac
+ case $ac_prog in
+ # Accept absolute paths.
+ [\\/]* | ?:[\\/]*)
+ re_direlt='/[^/][^/]*/\.\./'
+ # Canonicalize the pathname of ld
+ ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'`
+ while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
+ ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
+ done
+ test -z "$LD" && LD="$ac_prog"
+ ;;
+ "")
+ # If it fails, then pretend we aren't using GCC.
+ ac_prog=ld
+ ;;
+ *)
+ # If it is relative, then search for the first ld in PATH.
+ with_gnu_ld=unknown
+ ;;
+ esac
+elif test "$with_gnu_ld" = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5
+$as_echo_n "checking for GNU ld... " >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5
+$as_echo_n "checking for non-GNU ld... " >&6; }
+fi
+if ${lt_cv_path_LD+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$LD"; then
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+ lt_cv_path_LD="$ac_dir/$ac_prog"
+ # Check to see if the program is GNU ld. I'd rather use --version,
+ # but apparently some variants of GNU ld only accept -v.
+ # Break only if it was the GNU/non-GNU ld that we prefer.
+ case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
+ *GNU* | *'with BFD'*)
+ test "$with_gnu_ld" != no && break
+ ;;
+ *)
+ test "$with_gnu_ld" != yes && break
+ ;;
+ esac
+ fi
+ done
+ IFS="$lt_save_ifs"
+else
+ lt_cv_path_LD="$LD" # Let the user override the test with a path.
+fi
+fi
+
+LD="$lt_cv_path_LD"
+if test -n "$LD"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LD" >&5
+$as_echo "$LD" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5
+$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; }
+if ${lt_cv_prog_gnu_ld+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ # I'd rather use --version here, but apparently some GNU lds only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+ lt_cv_prog_gnu_ld=yes
+ ;;
+*)
+ lt_cv_prog_gnu_ld=no
+ ;;
+esac
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_gnu_ld" >&5
+$as_echo "$lt_cv_prog_gnu_ld" >&6; }
+with_gnu_ld=$lt_cv_prog_gnu_ld
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5
+$as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; }
+if ${lt_cv_path_NM+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$NM"; then
+ # Let the user override the test.
+ lt_cv_path_NM="$NM"
+else
+ lt_nm_to_check="${ac_tool_prefix}nm"
+ if test -n "$ac_tool_prefix" && test "$build" = "$host"; then
+ lt_nm_to_check="$lt_nm_to_check nm"
+ fi
+ for lt_tmp_nm in $lt_nm_to_check; do
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ tmp_nm="$ac_dir/$lt_tmp_nm"
+ if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then
+ # Check to see if the nm accepts a BSD-compat flag.
+ # Adding the `sed 1q' prevents false positives on HP-UX, which says:
+ # nm: unknown option "B" ignored
+ # Tru64's nm complains that /dev/null is an invalid object file
+ case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in
+ */dev/null* | *'Invalid file or object type'*)
+ lt_cv_path_NM="$tmp_nm -B"
+ break
+ ;;
+ *)
+ case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in
+ */dev/null*)
+ lt_cv_path_NM="$tmp_nm -p"
+ break
+ ;;
+ *)
+ lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
+ continue # so that we can try to find one that supports BSD flags
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ done
+ IFS="$lt_save_ifs"
+ done
+ : ${lt_cv_path_NM=no}
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5
+$as_echo "$lt_cv_path_NM" >&6; }
+if test "$lt_cv_path_NM" != "no"; then
+ NM="$lt_cv_path_NM"
+else
+ # Didn't find any BSD compatible name lister, look for dumpbin.
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in "dumpbin -symbols" "link -dump -symbols"
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_DUMPBIN+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$DUMPBIN"; then
+ ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+DUMPBIN=$ac_cv_prog_DUMPBIN
+if test -n "$DUMPBIN"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5
+$as_echo "$DUMPBIN" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$DUMPBIN" && break
+ done
+fi
+if test -z "$DUMPBIN"; then
+ ac_ct_DUMPBIN=$DUMPBIN
+ for ac_prog in "dumpbin -symbols" "link -dump -symbols"
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_DUMPBIN+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_DUMPBIN"; then
+ ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_DUMPBIN="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN
+if test -n "$ac_ct_DUMPBIN"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5
+$as_echo "$ac_ct_DUMPBIN" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_DUMPBIN" && break
+done
+
+ if test "x$ac_ct_DUMPBIN" = x; then
+ DUMPBIN=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ DUMPBIN=$ac_ct_DUMPBIN
+ fi
+fi
+
+
+ if test "$DUMPBIN" != ":"; then
+ NM="$DUMPBIN"
+ fi
+fi
+test -z "$NM" && NM=nm
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5
+$as_echo_n "checking the name lister ($NM) interface... " >&6; }
+if ${lt_cv_nm_interface+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_nm_interface="BSD nm"
+ echo "int some_variable = 0;" > conftest.$ac_ext
+ (eval echo "\"\$as_me:__oline__: $ac_compile\"" >&5)
+ (eval "$ac_compile" 2>conftest.err)
+ cat conftest.err >&5
+ (eval echo "\"\$as_me:__oline__: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
+ (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
+ cat conftest.err >&5
+ (eval echo "\"\$as_me:__oline__: output\"" >&5)
+ cat conftest.out >&5
+ if $GREP 'External.*some_variable' conftest.out > /dev/null; then
+ lt_cv_nm_interface="MS dumpbin"
+ fi
+ rm -f conftest*
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5
+$as_echo "$lt_cv_nm_interface" >&6; }
+
+# find the maximum length of command line arguments
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5
+$as_echo_n "checking the maximum length of command line arguments... " >&6; }
+if ${lt_cv_sys_max_cmd_len+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ i=0
+ teststring="ABCD"
+
+ case $build_os in
+ msdosdjgpp*)
+ # On DJGPP, this test can blow up pretty badly due to problems in libc
+ # (any single argument exceeding 2000 bytes causes a buffer overrun
+ # during glob expansion). Even if it were fixed, the result of this
+ # check would be larger than it should be.
+ lt_cv_sys_max_cmd_len=12288; # 12K is about right
+ ;;
+
+ gnu*)
+ # Under GNU Hurd, this test is not required because there is
+ # no limit to the length of command line arguments.
+ # Libtool will interpret -1 as no limit whatsoever
+ lt_cv_sys_max_cmd_len=-1;
+ ;;
+
+ cygwin* | mingw* | cegcc*)
+ # On Win9x/ME, this test blows up -- it succeeds, but takes
+ # about 5 minutes as the teststring grows exponentially.
+ # Worse, since 9x/ME are not pre-emptively multitasking,
+ # you end up with a "frozen" computer, even though with patience
+ # the test eventually succeeds (with a max line length of 256k).
+ # Instead, let's just punt: use the minimum linelength reported by
+ # all of the supported platforms: 8192 (on NT/2K/XP).
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ amigaos*)
+ # On AmigaOS with pdksh, this test takes hours, literally.
+ # So we just punt and use a minimum line length of 8192.
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ netbsd* | freebsd* | openbsd* | darwin* | dragonfly*)
+ # This has been around since 386BSD, at least. Likely further.
+ if test -x /sbin/sysctl; then
+ lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax`
+ elif test -x /usr/sbin/sysctl; then
+ lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax`
+ else
+ lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs
+ fi
+ # And add a safety zone
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+ ;;
+
+ interix*)
+ # We know the value 262144 and hardcode it with a safety zone (like BSD)
+ lt_cv_sys_max_cmd_len=196608
+ ;;
+
+ osf*)
+ # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure
+ # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not
+ # nice to cause kernel panics so lets avoid the loop below.
+ # First set a reasonable default.
+ lt_cv_sys_max_cmd_len=16384
+ #
+ if test -x /sbin/sysconfig; then
+ case `/sbin/sysconfig -q proc exec_disable_arg_limit` in
+ *1*) lt_cv_sys_max_cmd_len=-1 ;;
+ esac
+ fi
+ ;;
+ sco3.2v5*)
+ lt_cv_sys_max_cmd_len=102400
+ ;;
+ sysv5* | sco5v6* | sysv4.2uw2*)
+ kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null`
+ if test -n "$kargmax"; then
+ lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'`
+ else
+ lt_cv_sys_max_cmd_len=32768
+ fi
+ ;;
+ *)
+ lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null`
+ if test -n "$lt_cv_sys_max_cmd_len"; then
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+ else
+ # Make teststring a little bigger before we do anything with it.
+ # a 1K string should be a reasonable start.
+ for i in 1 2 3 4 5 6 7 8 ; do
+ teststring=$teststring$teststring
+ done
+ SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}}
+ # If test is not a shell built-in, we'll probably end up computing a
+ # maximum length that is only half of the actual maximum length, but
+ # we can't tell.
+ while { test "X"`$SHELL $0 --fallback-echo "X$teststring$teststring" 2>/dev/null` \
+ = "XX$teststring$teststring"; } >/dev/null 2>&1 &&
+ test $i != 17 # 1/2 MB should be enough
+ do
+ i=`expr $i + 1`
+ teststring=$teststring$teststring
+ done
+ # Only check the string length outside the loop.
+ lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1`
+ teststring=
+ # Add a significant safety factor because C++ compilers can tack on
+ # massive amounts of additional arguments before passing them to the
+ # linker. It appears as though 1/2 is a usable value.
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2`
+ fi
+ ;;
+ esac
+
+fi
+
+if test -n $lt_cv_sys_max_cmd_len ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5
+$as_echo "$lt_cv_sys_max_cmd_len" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5
+$as_echo "none" >&6; }
+fi
+max_cmd_len=$lt_cv_sys_max_cmd_len
+
+
+
+
+
+
+: ${CP="cp -f"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands some XSI constructs" >&5
+$as_echo_n "checking whether the shell understands some XSI constructs... " >&6; }
+# Try some XSI features
+xsi_shell=no
+( _lt_dummy="a/b/c"
+ test "${_lt_dummy##*/},${_lt_dummy%/*},"${_lt_dummy%"$_lt_dummy"}, \
+ = c,a/b,, \
+ && eval 'test $(( 1 + 1 )) -eq 2 \
+ && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \
+ && xsi_shell=yes
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $xsi_shell" >&5
+$as_echo "$xsi_shell" >&6; }
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands \"+=\"" >&5
+$as_echo_n "checking whether the shell understands \"+=\"... " >&6; }
+lt_shell_append=no
+( foo=bar; set foo baz; eval "$1+=\$2" && test "$foo" = barbaz ) \
+ >/dev/null 2>&1 \
+ && lt_shell_append=yes
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_shell_append" >&5
+$as_echo "$lt_shell_append" >&6; }
+
+
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ lt_unset=unset
+else
+ lt_unset=false
+fi
+
+
+
+
+
+# test EBCDIC or ASCII
+case `echo X|tr X '\101'` in
+ A) # ASCII based system
+ # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr
+ lt_SP2NL='tr \040 \012'
+ lt_NL2SP='tr \015\012 \040\040'
+ ;;
+ *) # EBCDIC based system
+ lt_SP2NL='tr \100 \n'
+ lt_NL2SP='tr \r\n \100\100'
+ ;;
+esac
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5
+$as_echo_n "checking for $LD option to reload object files... " >&6; }
+if ${lt_cv_ld_reload_flag+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_ld_reload_flag='-r'
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5
+$as_echo "$lt_cv_ld_reload_flag" >&6; }
+reload_flag=$lt_cv_ld_reload_flag
+case $reload_flag in
+"" | " "*) ;;
+*) reload_flag=" $reload_flag" ;;
+esac
+reload_cmds='$LD$reload_flag -o $output$reload_objs'
+case $host_os in
+ darwin*)
+ if test "$GCC" = yes; then
+ reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs'
+ else
+ reload_cmds='$LD$reload_flag -o $output$reload_objs'
+ fi
+ ;;
+esac
+
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args.
+set dummy ${ac_tool_prefix}objdump; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_OBJDUMP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$OBJDUMP"; then
+ ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+OBJDUMP=$ac_cv_prog_OBJDUMP
+if test -n "$OBJDUMP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5
+$as_echo "$OBJDUMP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OBJDUMP"; then
+ ac_ct_OBJDUMP=$OBJDUMP
+ # Extract the first word of "objdump", so it can be a program name with args.
+set dummy objdump; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_OBJDUMP"; then
+ ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_OBJDUMP="objdump"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP
+if test -n "$ac_ct_OBJDUMP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5
+$as_echo "$ac_ct_OBJDUMP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_OBJDUMP" = x; then
+ OBJDUMP="false"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ OBJDUMP=$ac_ct_OBJDUMP
+ fi
+else
+ OBJDUMP="$ac_cv_prog_OBJDUMP"
+fi
+
+test -z "$OBJDUMP" && OBJDUMP=objdump
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5
+$as_echo_n "checking how to recognize dependent libraries... " >&6; }
+if ${lt_cv_deplibs_check_method+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_file_magic_cmd='$MAGIC_CMD'
+lt_cv_file_magic_test_file=
+lt_cv_deplibs_check_method='unknown'
+# Need to set the preceding variable on all platforms that support
+# interlibrary dependencies.
+# 'none' -- dependencies not supported.
+# `unknown' -- same as none, but documents that we really don't know.
+# 'pass_all' -- all dependencies passed with no checks.
+# 'test_compile' -- check by making test program.
+# 'file_magic [[regex]]' -- check by looking for files in library path
+# which responds to the $file_magic_cmd with a given extended regex.
+# If you have `file' or equivalent on your system and you're not sure
+# whether `pass_all' will *always* work, you probably want this one.
+
+case $host_os in
+aix[4-9]*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+beos*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+bsdi[45]*)
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)'
+ lt_cv_file_magic_cmd='/usr/bin/file -L'
+ lt_cv_file_magic_test_file=/shlib/libc.so
+ ;;
+
+cygwin*)
+ # func_win32_libid is a shell function defined in ltmain.sh
+ lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+ lt_cv_file_magic_cmd='func_win32_libid'
+ ;;
+
+mingw* | pw32*)
+ # Base MSYS/MinGW do not provide the 'file' command needed by
+ # func_win32_libid shell function, so use a weaker test based on 'objdump',
+ # unless we find 'file', for example because we are cross-compiling.
+ if ( file / ) >/dev/null 2>&1; then
+ lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+ lt_cv_file_magic_cmd='func_win32_libid'
+ else
+ lt_cv_deplibs_check_method='file_magic file format pei*-i386(.*architecture: i386)?'
+ lt_cv_file_magic_cmd='$OBJDUMP -f'
+ fi
+ ;;
+
+cegcc)
+ # use the weaker test based on 'objdump'. See mingw*.
+ lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?'
+ lt_cv_file_magic_cmd='$OBJDUMP -f'
+ ;;
+
+darwin* | rhapsody*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+freebsd* | dragonfly*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+ case $host_cpu in
+ i*86 )
+ # Not sure whether the presence of OpenBSD here was a mistake.
+ # Let's accept both of them until this is cleared up.
+ lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library'
+ lt_cv_file_magic_cmd=/usr/bin/file
+ lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
+ ;;
+ esac
+ else
+ lt_cv_deplibs_check_method=pass_all
+ fi
+ ;;
+
+gnu*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+hpux10.20* | hpux11*)
+ lt_cv_file_magic_cmd=/usr/bin/file
+ case $host_cpu in
+ ia64*)
+ lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64'
+ lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so
+ ;;
+ hppa*64*)
+ lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - PA-RISC [0-9].[0-9]'
+ lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl
+ ;;
+ *)
+ lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9].[0-9]) shared library'
+ lt_cv_file_magic_test_file=/usr/lib/libc.sl
+ ;;
+ esac
+ ;;
+
+interix[3-9]*)
+ # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$'
+ ;;
+
+irix5* | irix6* | nonstopux*)
+ case $LD in
+ *-32|*"-32 ") libmagic=32-bit;;
+ *-n32|*"-n32 ") libmagic=N32;;
+ *-64|*"-64 ") libmagic=64-bit;;
+ *) libmagic=never-match;;
+ esac
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+# This must be Linux ELF.
+linux* | k*bsd*-gnu)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
+ else
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$'
+ fi
+ ;;
+
+newos6*)
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)'
+ lt_cv_file_magic_cmd=/usr/bin/file
+ lt_cv_file_magic_test_file=/usr/lib/libnls.so
+ ;;
+
+*nto* | *qnx*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+openbsd*)
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$'
+ else
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
+ fi
+ ;;
+
+osf3* | osf4* | osf5*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+rdos*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+solaris*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+sysv4 | sysv4.3*)
+ case $host_vendor in
+ motorola)
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]'
+ lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*`
+ ;;
+ ncr)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ sequent)
+ lt_cv_file_magic_cmd='/bin/file'
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )'
+ ;;
+ sni)
+ lt_cv_file_magic_cmd='/bin/file'
+ lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib"
+ lt_cv_file_magic_test_file=/lib/libc.so
+ ;;
+ siemens)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ pc)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ esac
+ ;;
+
+tpf*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+esac
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5
+$as_echo "$lt_cv_deplibs_check_method" >&6; }
+file_magic_cmd=$lt_cv_file_magic_cmd
+deplibs_check_method=$lt_cv_deplibs_check_method
+test -z "$deplibs_check_method" && deplibs_check_method=unknown
+
+
+
+
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ar; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_AR+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$AR"; then
+ ac_cv_prog_AR="$AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_AR="${ac_tool_prefix}ar"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+AR=$ac_cv_prog_AR
+if test -n "$AR"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5
+$as_echo "$AR" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_AR"; then
+ ac_ct_AR=$AR
+ # Extract the first word of "ar", so it can be a program name with args.
+set dummy ar; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_AR+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_AR"; then
+ ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_AR="ar"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_AR=$ac_cv_prog_ac_ct_AR
+if test -n "$ac_ct_AR"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5
+$as_echo "$ac_ct_AR" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_AR" = x; then
+ AR="false"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ AR=$ac_ct_AR
+ fi
+else
+ AR="$ac_cv_prog_AR"
+fi
+
+test -z "$AR" && AR=ar
+test -z "$AR_FLAGS" && AR_FLAGS=cru
+
+
+
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_STRIP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$STRIP"; then
+ ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5
+$as_echo "$STRIP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_STRIP"; then
+ ac_ct_STRIP=$STRIP
+ # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_STRIP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_STRIP"; then
+ ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_STRIP="strip"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5
+$as_echo "$ac_ct_STRIP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_STRIP" = x; then
+ STRIP=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ STRIP=$ac_ct_STRIP
+ fi
+else
+ STRIP="$ac_cv_prog_STRIP"
+fi
+
+test -z "$STRIP" && STRIP=:
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_RANLIB+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$RANLIB"; then
+ ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5
+$as_echo "$RANLIB" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+ ac_ct_RANLIB=$RANLIB
+ # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_RANLIB+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_RANLIB"; then
+ ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_RANLIB="ranlib"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5
+$as_echo "$ac_ct_RANLIB" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_RANLIB" = x; then
+ RANLIB=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ RANLIB=$ac_ct_RANLIB
+ fi
+else
+ RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+test -z "$RANLIB" && RANLIB=:
+
+
+
+
+
+
+# Determine commands to create old-style static archives.
+old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs'
+old_postinstall_cmds='chmod 644 $oldlib'
+old_postuninstall_cmds=
+
+if test -n "$RANLIB"; then
+ case $host_os in
+ openbsd*)
+ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$oldlib"
+ ;;
+ *)
+ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$oldlib"
+ ;;
+ esac
+ old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib"
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+
+# Check for command to grab the raw symbol name followed by C symbol from nm.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5
+$as_echo_n "checking command to parse $NM output from $compiler object... " >&6; }
+if ${lt_cv_sys_global_symbol_pipe+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+# These are sane defaults that work on at least a few old systems.
+# [They come from Ultrix. What could be older than Ultrix?!! ;)]
+
+# Character class describing NM global symbol codes.
+symcode='[BCDEGRST]'
+
+# Regexp to match symbols that can be accessed directly from C.
+sympat='\([_A-Za-z][_A-Za-z0-9]*\)'
+
+# Define system-specific variables.
+case $host_os in
+aix*)
+ symcode='[BCDT]'
+ ;;
+cygwin* | mingw* | pw32* | cegcc*)
+ symcode='[ABCDGISTW]'
+ ;;
+hpux*)
+ if test "$host_cpu" = ia64; then
+ symcode='[ABCDEGRST]'
+ fi
+ ;;
+irix* | nonstopux*)
+ symcode='[BCDEGRST]'
+ ;;
+osf*)
+ symcode='[BCDEGQRST]'
+ ;;
+solaris*)
+ symcode='[BDRT]'
+ ;;
+sco3.2v5*)
+ symcode='[DT]'
+ ;;
+sysv4.2uw2*)
+ symcode='[DT]'
+ ;;
+sysv5* | sco5v6* | unixware* | OpenUNIX*)
+ symcode='[ABDT]'
+ ;;
+sysv4)
+ symcode='[DFNSTU]'
+ ;;
+esac
+
+# If we're using GNU nm, then use its standard symbol codes.
+case `$NM -V 2>&1` in
+*GNU* | *'with BFD'*)
+ symcode='[ABCDGIRSTW]' ;;
+esac
+
+# Transform an extracted symbol line into a proper C declaration.
+# Some systems (esp. on ia64) link data and code symbols differently,
+# so use this general approach.
+lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'"
+
+# Transform an extracted symbol line into symbol name and symbol address
+lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"\2\", (void *) \&\2},/p'"
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([^ ]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \(lib[^ ]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"lib\2\", (void *) \&\2},/p'"
+
+# Handle CRLF in mingw tool chain
+opt_cr=
+case $build_os in
+mingw*)
+ opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp
+ ;;
+esac
+
+# Try without a prefix underscore, then with it.
+for ac_symprfx in "" "_"; do
+
+ # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol.
+ symxfrm="\\1 $ac_symprfx\\2 \\2"
+
+ # Write the raw and C identifiers.
+ if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+ # Fake it for dumpbin and say T for any non-static function
+ # and D for any global variable.
+ # Also find C++ and __fastcall symbols from MSVC++,
+ # which start with @ or ?.
+ lt_cv_sys_global_symbol_pipe="$AWK '"\
+" {last_section=section; section=\$ 3};"\
+" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\
+" \$ 0!~/External *\|/{next};"\
+" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\
+" {if(hide[section]) next};"\
+" {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\
+" {split(\$ 0, a, /\||\r/); split(a[2], s)};"\
+" s[1]~/^[@?]/{print s[1], s[1]; next};"\
+" s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\
+" ' prfx=^$ac_symprfx"
+ else
+ lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'"
+ fi
+
+ # Check to see that the pipe works correctly.
+ pipe_works=no
+
+ rm -f conftest*
+ cat > conftest.$ac_ext <<_LT_EOF
+#ifdef __cplusplus
+extern "C" {
+#endif
+char nm_test_var;
+void nm_test_func(void);
+void nm_test_func(void){}
+#ifdef __cplusplus
+}
+#endif
+int main(){nm_test_var='a';nm_test_func();return(0);}
+_LT_EOF
+
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ # Now try to grab the symbols.
+ nlist=conftest.nm
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist\""; } >&5
+ (eval $NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && test -s "$nlist"; then
+ # Try sorting and uniquifying the output.
+ if sort "$nlist" | uniq > "$nlist"T; then
+ mv -f "$nlist"T "$nlist"
+ else
+ rm -f "$nlist"T
+ fi
+
+ # Make sure that we snagged all the symbols we need.
+ if $GREP ' nm_test_var$' "$nlist" >/dev/null; then
+ if $GREP ' nm_test_func$' "$nlist" >/dev/null; then
+ cat <<_LT_EOF > conftest.$ac_ext
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+_LT_EOF
+ # Now generate the symbol file.
+ eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext'
+
+ cat <<_LT_EOF >> conftest.$ac_ext
+
+/* The mapping between symbol names and symbols. */
+const struct {
+ const char *name;
+ void *address;
+}
+lt__PROGRAM__LTX_preloaded_symbols[] =
+{
+ { "@PROGRAM@", (void *) 0 },
+_LT_EOF
+ $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext
+ cat <<\_LT_EOF >> conftest.$ac_ext
+ {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+ return lt__PROGRAM__LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+_LT_EOF
+ # Now try linking the two files.
+ mv conftest.$ac_objext conftstm.$ac_objext
+ lt_save_LIBS="$LIBS"
+ lt_save_CFLAGS="$CFLAGS"
+ LIBS="conftstm.$ac_objext"
+ CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag"
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && test -s conftest${ac_exeext}; then
+ pipe_works=yes
+ fi
+ LIBS="$lt_save_LIBS"
+ CFLAGS="$lt_save_CFLAGS"
+ else
+ echo "cannot find nm_test_func in $nlist" >&5
+ fi
+ else
+ echo "cannot find nm_test_var in $nlist" >&5
+ fi
+ else
+ echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5
+ fi
+ else
+ echo "$progname: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ fi
+ rm -rf conftest* conftst*
+
+ # Do not use the global_symbol_pipe unless it works.
+ if test "$pipe_works" = yes; then
+ break
+ else
+ lt_cv_sys_global_symbol_pipe=
+ fi
+done
+
+fi
+
+if test -z "$lt_cv_sys_global_symbol_pipe"; then
+ lt_cv_sys_global_symbol_to_cdecl=
+fi
+if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5
+$as_echo "failed" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5
+$as_echo "ok" >&6; }
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+@%:@ Check whether --enable-libtool-lock was given.
+if test "${enable_libtool_lock+set}" = set; then :
+ enableval=$enable_libtool_lock;
+fi
+
+test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes
+
+# Some flags need to be propagated to the compiler or linker for good
+# libtool support.
+case $host in
+ia64-*-hpux*)
+ # Find out which ABI we are using.
+ echo 'int i;' > conftest.$ac_ext
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ case `/usr/bin/file conftest.$ac_objext` in
+ *ELF-32*)
+ HPUX_IA64_MODE="32"
+ ;;
+ *ELF-64*)
+ HPUX_IA64_MODE="64"
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+*-*-irix6*)
+ # Find out which ABI we are using.
+ echo '#line __oline__ "configure"' > conftest.$ac_ext
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ if test "$lt_cv_prog_gnu_ld" = yes; then
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ LD="${LD-ld} -melf32bsmip"
+ ;;
+ *N32*)
+ LD="${LD-ld} -melf32bmipn32"
+ ;;
+ *64-bit*)
+ LD="${LD-ld} -melf64bmip"
+ ;;
+ esac
+ else
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ LD="${LD-ld} -32"
+ ;;
+ *N32*)
+ LD="${LD-ld} -n32"
+ ;;
+ *64-bit*)
+ LD="${LD-ld} -64"
+ ;;
+ esac
+ fi
+ fi
+ rm -rf conftest*
+ ;;
+
+x86_64-*kfreebsd*-gnu|x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*| \
+s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
+ # Find out which ABI we are using.
+ echo 'int i;' > conftest.$ac_ext
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ case `/usr/bin/file conftest.o` in
+ *32-bit*)
+ case $host in
+ x86_64-*kfreebsd*-gnu)
+ LD="${LD-ld} -m elf_i386_fbsd"
+ ;;
+ x86_64-*linux*)
+ LD="${LD-ld} -m elf_i386"
+ ;;
+ ppc64-*linux*|powerpc64-*linux*)
+ LD="${LD-ld} -m elf32ppclinux"
+ ;;
+ s390x-*linux*)
+ LD="${LD-ld} -m elf_s390"
+ ;;
+ sparc64-*linux*)
+ LD="${LD-ld} -m elf32_sparc"
+ ;;
+ esac
+ ;;
+ *64-bit*)
+ case $host in
+ x86_64-*kfreebsd*-gnu)
+ LD="${LD-ld} -m elf_x86_64_fbsd"
+ ;;
+ x86_64-*linux*)
+ LD="${LD-ld} -m elf_x86_64"
+ ;;
+ ppc*-*linux*|powerpc*-*linux*)
+ LD="${LD-ld} -m elf64ppc"
+ ;;
+ s390*-*linux*|s390*-*tpf*)
+ LD="${LD-ld} -m elf64_s390"
+ ;;
+ sparc*-*linux*)
+ LD="${LD-ld} -m elf64_sparc"
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+
+*-*-sco3.2v5*)
+ # On SCO OpenServer 5, we need -belf to get full-featured binaries.
+ SAVE_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS -belf"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5
+$as_echo_n "checking whether the C compiler needs -belf... " >&6; }
+if ${lt_cv_cc_needs_belf+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ lt_cv_cc_needs_belf=yes
+else
+ lt_cv_cc_needs_belf=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5
+$as_echo "$lt_cv_cc_needs_belf" >&6; }
+ if test x"$lt_cv_cc_needs_belf" != x"yes"; then
+ # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
+ CFLAGS="$SAVE_CFLAGS"
+ fi
+ ;;
+sparc*-*solaris*)
+ # Find out which ABI we are using.
+ echo 'int i;' > conftest.$ac_ext
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ case `/usr/bin/file conftest.o` in
+ *64-bit*)
+ case $lt_cv_prog_gnu_ld in
+ yes*) LD="${LD-ld} -m elf64_sparc" ;;
+ *)
+ if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then
+ LD="${LD-ld} -64"
+ fi
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+esac
+
+need_locks="$enable_libtool_lock"
+
+
+ case $host_os in
+ rhapsody* | darwin*)
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args.
+set dummy ${ac_tool_prefix}dsymutil; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_DSYMUTIL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$DSYMUTIL"; then
+ ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+DSYMUTIL=$ac_cv_prog_DSYMUTIL
+if test -n "$DSYMUTIL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5
+$as_echo "$DSYMUTIL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_DSYMUTIL"; then
+ ac_ct_DSYMUTIL=$DSYMUTIL
+ # Extract the first word of "dsymutil", so it can be a program name with args.
+set dummy dsymutil; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_DSYMUTIL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_DSYMUTIL"; then
+ ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_DSYMUTIL="dsymutil"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL
+if test -n "$ac_ct_DSYMUTIL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5
+$as_echo "$ac_ct_DSYMUTIL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_DSYMUTIL" = x; then
+ DSYMUTIL=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ DSYMUTIL=$ac_ct_DSYMUTIL
+ fi
+else
+ DSYMUTIL="$ac_cv_prog_DSYMUTIL"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args.
+set dummy ${ac_tool_prefix}nmedit; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_NMEDIT+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$NMEDIT"; then
+ ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+NMEDIT=$ac_cv_prog_NMEDIT
+if test -n "$NMEDIT"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5
+$as_echo "$NMEDIT" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_NMEDIT"; then
+ ac_ct_NMEDIT=$NMEDIT
+ # Extract the first word of "nmedit", so it can be a program name with args.
+set dummy nmedit; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_NMEDIT+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_NMEDIT"; then
+ ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_NMEDIT="nmedit"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT
+if test -n "$ac_ct_NMEDIT"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5
+$as_echo "$ac_ct_NMEDIT" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_NMEDIT" = x; then
+ NMEDIT=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ NMEDIT=$ac_ct_NMEDIT
+ fi
+else
+ NMEDIT="$ac_cv_prog_NMEDIT"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args.
+set dummy ${ac_tool_prefix}lipo; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_LIPO+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$LIPO"; then
+ ac_cv_prog_LIPO="$LIPO" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_LIPO="${ac_tool_prefix}lipo"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+LIPO=$ac_cv_prog_LIPO
+if test -n "$LIPO"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5
+$as_echo "$LIPO" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_LIPO"; then
+ ac_ct_LIPO=$LIPO
+ # Extract the first word of "lipo", so it can be a program name with args.
+set dummy lipo; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_LIPO+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_LIPO"; then
+ ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_LIPO="lipo"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO
+if test -n "$ac_ct_LIPO"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5
+$as_echo "$ac_ct_LIPO" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_LIPO" = x; then
+ LIPO=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ LIPO=$ac_ct_LIPO
+ fi
+else
+ LIPO="$ac_cv_prog_LIPO"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args.
+set dummy ${ac_tool_prefix}otool; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_OTOOL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$OTOOL"; then
+ ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_OTOOL="${ac_tool_prefix}otool"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+OTOOL=$ac_cv_prog_OTOOL
+if test -n "$OTOOL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5
+$as_echo "$OTOOL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OTOOL"; then
+ ac_ct_OTOOL=$OTOOL
+ # Extract the first word of "otool", so it can be a program name with args.
+set dummy otool; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_OTOOL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_OTOOL"; then
+ ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_OTOOL="otool"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL
+if test -n "$ac_ct_OTOOL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5
+$as_echo "$ac_ct_OTOOL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_OTOOL" = x; then
+ OTOOL=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ OTOOL=$ac_ct_OTOOL
+ fi
+else
+ OTOOL="$ac_cv_prog_OTOOL"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args.
+set dummy ${ac_tool_prefix}otool64; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_OTOOL64+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$OTOOL64"; then
+ ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+OTOOL64=$ac_cv_prog_OTOOL64
+if test -n "$OTOOL64"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5
+$as_echo "$OTOOL64" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OTOOL64"; then
+ ac_ct_OTOOL64=$OTOOL64
+ # Extract the first word of "otool64", so it can be a program name with args.
+set dummy otool64; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_OTOOL64+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_OTOOL64"; then
+ ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_OTOOL64="otool64"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64
+if test -n "$ac_ct_OTOOL64"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5
+$as_echo "$ac_ct_OTOOL64" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_OTOOL64" = x; then
+ OTOOL64=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ OTOOL64=$ac_ct_OTOOL64
+ fi
+else
+ OTOOL64="$ac_cv_prog_OTOOL64"
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5
+$as_echo_n "checking for -single_module linker flag... " >&6; }
+if ${lt_cv_apple_cc_single_mod+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_apple_cc_single_mod=no
+ if test -z "${LT_MULTI_MODULE}"; then
+ # By default we will add the -single_module flag. You can override
+ # by either setting the environment variable LT_MULTI_MODULE
+ # non-empty at configure time, or by adding -multi_module to the
+ # link flags.
+ rm -rf libconftest.dylib*
+ echo "int foo(void){return 1;}" > conftest.c
+ echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+-dynamiclib -Wl,-single_module conftest.c" >&5
+ $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err
+ _lt_result=$?
+ if test -f libconftest.dylib && test ! -s conftest.err && test $_lt_result = 0; then
+ lt_cv_apple_cc_single_mod=yes
+ else
+ cat conftest.err >&5
+ fi
+ rm -rf libconftest.dylib*
+ rm -f conftest.*
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5
+$as_echo "$lt_cv_apple_cc_single_mod" >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5
+$as_echo_n "checking for -exported_symbols_list linker flag... " >&6; }
+if ${lt_cv_ld_exported_symbols_list+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_ld_exported_symbols_list=no
+ save_LDFLAGS=$LDFLAGS
+ echo "_main" > conftest.sym
+ LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ lt_cv_ld_exported_symbols_list=yes
+else
+ lt_cv_ld_exported_symbols_list=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LDFLAGS="$save_LDFLAGS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5
+$as_echo "$lt_cv_ld_exported_symbols_list" >&6; }
+ case $host_os in
+ rhapsody* | darwin1.[012])
+ _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;;
+ darwin1.*)
+ _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+ darwin*) # darwin 5.x on
+ # if running on 10.5 or later, the deployment target defaults
+ # to the OS version, if on x86, and 10.4, the deployment
+ # target defaults to 10.4. Don't you love it?
+ case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
+ 10.0,*86*-darwin8*|10.0,*-darwin[91]*)
+ _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+ 10.[012]*)
+ _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+ 10.*)
+ _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+ esac
+ ;;
+ esac
+ if test "$lt_cv_apple_cc_single_mod" = "yes"; then
+ _lt_dar_single_mod='$single_module'
+ fi
+ if test "$lt_cv_ld_exported_symbols_list" = "yes"; then
+ _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym'
+ else
+ _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}'
+ fi
+ if test "$DSYMUTIL" != ":"; then
+ _lt_dsymutil='~$DSYMUTIL $lib || :'
+ else
+ _lt_dsymutil=
+ fi
+ ;;
+ esac
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
+$as_echo_n "checking how to run the C preprocessor... " >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+ if ${ac_cv_prog_CPP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ # Double quotes because CPP needs to be expanded
+ for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+ do
+ ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+@%:@ifdef __STDC__
+@%:@ include <limits.h>
+@%:@else
+@%:@ include <assert.h>
+@%:@endif
+ Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+@%:@include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ # Broken: success on invalid input.
+continue
+else
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+ break
+fi
+
+ done
+ ac_cv_prog_CPP=$CPP
+
+fi
+ CPP=$ac_cv_prog_CPP
+else
+ ac_cv_prog_CPP=$CPP
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
+$as_echo "$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+@%:@ifdef __STDC__
+@%:@ include <limits.h>
+@%:@else
+@%:@ include <assert.h>
+@%:@endif
+ Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+@%:@include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ # Broken: success on invalid input.
+continue
+else
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+
+else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if ${ac_cv_header_stdc+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_header_stdc=yes
+else
+ ac_cv_header_stdc=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "memchr" >/dev/null 2>&1; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "free" >/dev/null 2>&1; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+ if test "$cross_compiling" = yes; then :
+ :
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+ (('a' <= (c) && (c) <= 'i') \
+ || ('j' <= (c) && (c) <= 'r') \
+ || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ if (XOR (islower (i), ISLOWER (i))
+ || toupper (i) != TOUPPER (i))
+ return 2;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+$as_echo "@%:@define STDC_HEADERS 1" >>confdefs.h
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+ inttypes.h stdint.h unistd.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
+"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in dlfcn.h
+do :
+ ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default
+"
+if test "x$ac_cv_header_dlfcn_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_DLFCN_H 1
+_ACEOF
+
+fi
+
+done
+
+
+
+# Set options
+
+
+
+ enable_dlopen=no
+
+
+ enable_win32_dll=no
+
+
+ @%:@ Check whether --enable-shared was given.
+if test "${enable_shared+set}" = set; then :
+ enableval=$enable_shared; p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_shared=yes ;;
+ no) enable_shared=no ;;
+ *)
+ enable_shared=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ for pkg in $enableval; do
+ IFS="$lt_save_ifs"
+ if test "X$pkg" = "X$p"; then
+ enable_shared=yes
+ fi
+ done
+ IFS="$lt_save_ifs"
+ ;;
+ esac
+else
+ enable_shared=yes
+fi
+
+
+
+
+
+
+
+
+
+ @%:@ Check whether --enable-static was given.
+if test "${enable_static+set}" = set; then :
+ enableval=$enable_static; p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_static=yes ;;
+ no) enable_static=no ;;
+ *)
+ enable_static=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ for pkg in $enableval; do
+ IFS="$lt_save_ifs"
+ if test "X$pkg" = "X$p"; then
+ enable_static=yes
+ fi
+ done
+ IFS="$lt_save_ifs"
+ ;;
+ esac
+else
+ enable_static=yes
+fi
+
+
+
+
+
+
+
+
+
+
+@%:@ Check whether --with-pic was given.
+if test "${with_pic+set}" = set; then :
+ withval=$with_pic; pic_mode="$withval"
+else
+ pic_mode=default
+fi
+
+
+test -z "$pic_mode" && pic_mode=default
+
+
+
+
+
+
+
+ @%:@ Check whether --enable-fast-install was given.
+if test "${enable_fast_install+set}" = set; then :
+ enableval=$enable_fast_install; p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_fast_install=yes ;;
+ no) enable_fast_install=no ;;
+ *)
+ enable_fast_install=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ for pkg in $enableval; do
+ IFS="$lt_save_ifs"
+ if test "X$pkg" = "X$p"; then
+ enable_fast_install=yes
+ fi
+ done
+ IFS="$lt_save_ifs"
+ ;;
+ esac
+else
+ enable_fast_install=yes
+fi
+
+
+
+
+
+
+
+
+
+
+
+# This can be used to rebuild libtool when needed
+LIBTOOL_DEPS="$ltmain"
+
+# Always use our own libtool.
+LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+test -z "$LN_S" && LN_S="ln -s"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+if test -n "${ZSH_VERSION+set}" ; then
+ setopt NO_GLOB_SUBST
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5
+$as_echo_n "checking for objdir... " >&6; }
+if ${lt_cv_objdir+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ rm -f .libs 2>/dev/null
+mkdir .libs 2>/dev/null
+if test -d .libs; then
+ lt_cv_objdir=.libs
+else
+ # MS-DOS does not allow filenames that begin with a dot.
+ lt_cv_objdir=_libs
+fi
+rmdir .libs 2>/dev/null
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5
+$as_echo "$lt_cv_objdir" >&6; }
+objdir=$lt_cv_objdir
+
+
+
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define LT_OBJDIR "$lt_cv_objdir/"
+_ACEOF
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+case $host_os in
+aix3*)
+ # AIX sometimes has problems with the GCC collect2 program. For some
+ # reason, if we set the COLLECT_NAMES environment variable, the problems
+ # vanish in a puff of smoke.
+ if test "X${COLLECT_NAMES+set}" != Xset; then
+ COLLECT_NAMES=
+ export COLLECT_NAMES
+ fi
+ ;;
+esac
+
+# Sed substitution that helps us do robust quoting. It backslashifies
+# metacharacters that are still active within double-quoted strings.
+sed_quote_subst='s/\(["`$\\]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\(["`\\]\)/\\\1/g'
+
+# Sed substitution to delay expansion of an escaped shell variable in a
+# double_quote_subst'ed string.
+delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
+
+# Sed substitution to delay expansion of an escaped single quote.
+delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g'
+
+# Sed substitution to avoid accidental globbing in evaled expressions
+no_glob_subst='s/\*/\\\*/g'
+
+# Global variables:
+ofile=libtool
+can_build_shared=yes
+
+# All known linkers require a `.a' archive for static linking (except MSVC,
+# which needs '.lib').
+libext=a
+
+with_gnu_ld="$lt_cv_prog_gnu_ld"
+
+old_CC="$CC"
+old_CFLAGS="$CFLAGS"
+
+# Set sane defaults for various variables
+test -z "$CC" && CC=cc
+test -z "$LTCC" && LTCC=$CC
+test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS
+test -z "$LD" && LD=ld
+test -z "$ac_objext" && ac_objext=o
+
+for cc_temp in $compiler""; do
+ case $cc_temp in
+ compile | *[\\/]compile | ccache | *[\\/]ccache ) ;;
+ distcc | *[\\/]distcc | purify | *[\\/]purify ) ;;
+ \-*) ;;
+ *) break;;
+ esac
+done
+cc_basename=`$ECHO "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"`
+
+
+# Only perform the check for file, if the check method requires it
+test -z "$MAGIC_CMD" && MAGIC_CMD=file
+case $deplibs_check_method in
+file_magic*)
+ if test "$file_magic_cmd" = '$MAGIC_CMD'; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5
+$as_echo_n "checking for ${ac_tool_prefix}file... " >&6; }
+if ${lt_cv_path_MAGIC_CMD+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $MAGIC_CMD in
+[\\/*] | ?:[\\/]*)
+ lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+ ;;
+*)
+ lt_save_MAGIC_CMD="$MAGIC_CMD"
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
+ for ac_dir in $ac_dummy; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/${ac_tool_prefix}file; then
+ lt_cv_path_MAGIC_CMD="$ac_dir/${ac_tool_prefix}file"
+ if test -n "$file_magic_test_file"; then
+ case $deplibs_check_method in
+ "file_magic "*)
+ file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+ MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+ if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+ $EGREP "$file_magic_regex" > /dev/null; then
+ :
+ else
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such. This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem. Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+ fi ;;
+ esac
+ fi
+ break
+ fi
+ done
+ IFS="$lt_save_ifs"
+ MAGIC_CMD="$lt_save_MAGIC_CMD"
+ ;;
+esac
+fi
+
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5
+$as_echo "$MAGIC_CMD" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+
+
+if test -z "$lt_cv_path_MAGIC_CMD"; then
+ if test -n "$ac_tool_prefix"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5
+$as_echo_n "checking for file... " >&6; }
+if ${lt_cv_path_MAGIC_CMD+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $MAGIC_CMD in
+[\\/*] | ?:[\\/]*)
+ lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+ ;;
+*)
+ lt_save_MAGIC_CMD="$MAGIC_CMD"
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
+ for ac_dir in $ac_dummy; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/file; then
+ lt_cv_path_MAGIC_CMD="$ac_dir/file"
+ if test -n "$file_magic_test_file"; then
+ case $deplibs_check_method in
+ "file_magic "*)
+ file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+ MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+ if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+ $EGREP "$file_magic_regex" > /dev/null; then
+ :
+ else
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such. This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem. Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+ fi ;;
+ esac
+ fi
+ break
+ fi
+ done
+ IFS="$lt_save_ifs"
+ MAGIC_CMD="$lt_save_MAGIC_CMD"
+ ;;
+esac
+fi
+
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5
+$as_echo "$MAGIC_CMD" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ else
+ MAGIC_CMD=:
+ fi
+fi
+
+ fi
+ ;;
+esac
+
+# Use C for the default configuration in the libtool script
+
+lt_save_CC="$CC"
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+# Source file extension for C test sources.
+ac_ext=c
+
+# Object file extension for compiled C test sources.
+objext=o
+objext=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="int some_variable = 0;"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='int main(){return(0);}'
+
+
+
+
+
+
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+# Save the default compiler, since it gets overwritten when the other
+# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP.
+compiler_DEFAULT=$CC
+
+# save warnings/boilerplate of simple test code
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_compile_test_code" >conftest.$ac_ext
+eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_compiler_boilerplate=`cat conftest.err`
+$RM conftest*
+
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_link_test_code" >conftest.$ac_ext
+eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_linker_boilerplate=`cat conftest.err`
+$RM -r conftest*
+
+
+## CAVEAT EMPTOR:
+## There is no encapsulation within the following macros, do not change
+## the running order or otherwise move them around unless you know exactly
+## what you are doing...
+if test -n "$compiler"; then
+
+lt_prog_compiler_no_builtin_flag=
+
+if test "$GCC" = yes; then
+ lt_prog_compiler_no_builtin_flag=' -fno-builtin'
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5
+$as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; }
+if ${lt_cv_prog_compiler_rtti_exceptions+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_rtti_exceptions=no
+ ac_outfile=conftest.$ac_objext
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+ lt_compiler_flag="-fno-rtti -fno-exceptions"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ # The option is referenced via a variable to avoid confusing sed.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>conftest.err)
+ ac_status=$?
+ cat conftest.err >&5
+ echo "$as_me:__oline__: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s "$ac_outfile"; then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings other than the usual output.
+ $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_rtti_exceptions=yes
+ fi
+ fi
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5
+$as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; }
+
+if test x"$lt_cv_prog_compiler_rtti_exceptions" = xyes; then
+ lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions"
+else
+ :
+fi
+
+fi
+
+
+
+
+
+
+ lt_prog_compiler_wl=
+lt_prog_compiler_pic=
+lt_prog_compiler_static=
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5
+$as_echo_n "checking for $compiler option to produce PIC... " >&6; }
+
+ if test "$GCC" = yes; then
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_static='-static'
+
+ case $host_os in
+ aix*)
+ # All AIX code is PIC.
+ if test "$host_cpu" = ia64; then
+ # AIX 5 now supports IA64 processor
+ lt_prog_compiler_static='-Bstatic'
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ lt_prog_compiler_pic='-fPIC'
+ ;;
+ m68k)
+ # FIXME: we need at least 68020 code to build shared libraries, but
+ # adding the `-m68020' flag to GCC prevents building anything better,
+ # like `-m68040'.
+ lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4'
+ ;;
+ esac
+ ;;
+
+ beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+ # PIC is the default for these OSes.
+ ;;
+
+ mingw* | cygwin* | pw32* | os2* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ # Although the cygwin gcc ignores -fPIC, still need this for old-style
+ # (--disable-auto-import) libraries
+ lt_prog_compiler_pic='-DDLL_EXPORT'
+ ;;
+
+ darwin* | rhapsody*)
+ # PIC is the default on this platform
+ # Common symbols not allowed in MH_DYLIB files
+ lt_prog_compiler_pic='-fno-common'
+ ;;
+
+ hpux*)
+ # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+ # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag
+ # sets the default TLS model and affects inlining.
+ case $host_cpu in
+ hppa*64*)
+ # +Z the default
+ ;;
+ *)
+ lt_prog_compiler_pic='-fPIC'
+ ;;
+ esac
+ ;;
+
+ interix[3-9]*)
+ # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+ # Instead, we relocate shared libraries at runtime.
+ ;;
+
+ msdosdjgpp*)
+ # Just because we use GCC doesn't mean we suddenly get shared libraries
+ # on systems that don't support them.
+ lt_prog_compiler_can_build_shared=no
+ enable_shared=no
+ ;;
+
+ *nto* | *qnx*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ lt_prog_compiler_pic='-fPIC -shared'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ lt_prog_compiler_pic=-Kconform_pic
+ fi
+ ;;
+
+ *)
+ lt_prog_compiler_pic='-fPIC'
+ ;;
+ esac
+ else
+ # PORTME Check for flag to pass linker flags through the system compiler.
+ case $host_os in
+ aix*)
+ lt_prog_compiler_wl='-Wl,'
+ if test "$host_cpu" = ia64; then
+ # AIX 5 now supports IA64 processor
+ lt_prog_compiler_static='-Bstatic'
+ else
+ lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp'
+ fi
+ ;;
+
+ mingw* | cygwin* | pw32* | os2* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ lt_prog_compiler_pic='-DDLL_EXPORT'
+ ;;
+
+ hpux9* | hpux10* | hpux11*)
+ lt_prog_compiler_wl='-Wl,'
+ # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+ # not for PA HP-UX.
+ case $host_cpu in
+ hppa*64*|ia64*)
+ # +Z the default
+ ;;
+ *)
+ lt_prog_compiler_pic='+Z'
+ ;;
+ esac
+ # Is there a better lt_prog_compiler_static that works with the bundled CC?
+ lt_prog_compiler_static='${wl}-a ${wl}archive'
+ ;;
+
+ irix5* | irix6* | nonstopux*)
+ lt_prog_compiler_wl='-Wl,'
+ # PIC (with -KPIC) is the default.
+ lt_prog_compiler_static='-non_shared'
+ ;;
+
+ linux* | k*bsd*-gnu)
+ case $cc_basename in
+ # old Intel for x86_64 which still supported -KPIC.
+ ecc*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-static'
+ ;;
+ # icc used to be incompatible with GCC.
+ # ICC 10 doesn't accept -KPIC any more.
+ icc* | ifort*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-fPIC'
+ lt_prog_compiler_static='-static'
+ ;;
+ # Lahey Fortran 8.1.
+ lf95*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='--shared'
+ lt_prog_compiler_static='--static'
+ ;;
+ pgcc* | pgf77* | pgf90* | pgf95*)
+ # Portland Group compilers (*not* the Pentium gcc compiler,
+ # which looks to be a dead project)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-fpic'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+ ccc*)
+ lt_prog_compiler_wl='-Wl,'
+ # All Alpha code is PIC.
+ lt_prog_compiler_static='-non_shared'
+ ;;
+ xl*)
+ # IBM XL C 8.0/Fortran 10.1 on PPC
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-qpic'
+ lt_prog_compiler_static='-qstaticlink'
+ ;;
+ *)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*)
+ # Sun C 5.9
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ lt_prog_compiler_wl='-Wl,'
+ ;;
+ *Sun\ F*)
+ # Sun Fortran 8.3 passes all unrecognized flags to the linker
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ lt_prog_compiler_wl=''
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+
+ newsos6)
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ *nto* | *qnx*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ lt_prog_compiler_pic='-fPIC -shared'
+ ;;
+
+ osf3* | osf4* | osf5*)
+ lt_prog_compiler_wl='-Wl,'
+ # All OSF/1 code is PIC.
+ lt_prog_compiler_static='-non_shared'
+ ;;
+
+ rdos*)
+ lt_prog_compiler_static='-non_shared'
+ ;;
+
+ solaris*)
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ case $cc_basename in
+ f77* | f90* | f95*)
+ lt_prog_compiler_wl='-Qoption ld ';;
+ *)
+ lt_prog_compiler_wl='-Wl,';;
+ esac
+ ;;
+
+ sunos4*)
+ lt_prog_compiler_wl='-Qoption ld '
+ lt_prog_compiler_pic='-PIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ sysv4 | sysv4.2uw2* | sysv4.3*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec ;then
+ lt_prog_compiler_pic='-Kconform_pic'
+ lt_prog_compiler_static='-Bstatic'
+ fi
+ ;;
+
+ sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ unicos*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_can_build_shared=no
+ ;;
+
+ uts4*)
+ lt_prog_compiler_pic='-pic'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ *)
+ lt_prog_compiler_can_build_shared=no
+ ;;
+ esac
+ fi
+
+case $host_os in
+ # For platforms which do not support PIC, -DPIC is meaningless:
+ *djgpp*)
+ lt_prog_compiler_pic=
+ ;;
+ *)
+ lt_prog_compiler_pic="$lt_prog_compiler_pic@&t@ -DPIC"
+ ;;
+esac
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_prog_compiler_pic" >&5
+$as_echo "$lt_prog_compiler_pic" >&6; }
+
+
+
+
+
+
+#
+# Check to make sure the PIC flag actually works.
+#
+if test -n "$lt_prog_compiler_pic"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5
+$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; }
+if ${lt_cv_prog_compiler_pic_works+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_pic_works=no
+ ac_outfile=conftest.$ac_objext
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+ lt_compiler_flag="$lt_prog_compiler_pic@&t@ -DPIC"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ # The option is referenced via a variable to avoid confusing sed.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>conftest.err)
+ ac_status=$?
+ cat conftest.err >&5
+ echo "$as_me:__oline__: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s "$ac_outfile"; then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings other than the usual output.
+ $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_pic_works=yes
+ fi
+ fi
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5
+$as_echo "$lt_cv_prog_compiler_pic_works" >&6; }
+
+if test x"$lt_cv_prog_compiler_pic_works" = xyes; then
+ case $lt_prog_compiler_pic in
+ "" | " "*) ;;
+ *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;;
+ esac
+else
+ lt_prog_compiler_pic=
+ lt_prog_compiler_can_build_shared=no
+fi
+
+fi
+
+
+
+
+
+
+#
+# Check to make sure the static flag actually works.
+#
+wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5
+$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; }
+if ${lt_cv_prog_compiler_static_works+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_static_works=no
+ save_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS $lt_tmp_static_flag"
+ echo "$lt_simple_link_test_code" > conftest.$ac_ext
+ if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+ # The linker can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ if test -s conftest.err; then
+ # Append any errors to the config.log.
+ cat conftest.err 1>&5
+ $ECHO "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if diff conftest.exp conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_static_works=yes
+ fi
+ else
+ lt_cv_prog_compiler_static_works=yes
+ fi
+ fi
+ $RM -r conftest*
+ LDFLAGS="$save_LDFLAGS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5
+$as_echo "$lt_cv_prog_compiler_static_works" >&6; }
+
+if test x"$lt_cv_prog_compiler_static_works" = xyes; then
+ :
+else
+ lt_prog_compiler_static=
+fi
+
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5
+$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
+if ${lt_cv_prog_compiler_c_o+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_c_o=no
+ $RM -r conftest 2>/dev/null
+ mkdir conftest
+ cd conftest
+ mkdir out
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ lt_compiler_flag="-o out/conftest2.$ac_objext"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>out/conftest.err)
+ ac_status=$?
+ cat out/conftest.err >&5
+ echo "$as_me:__oline__: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s out/conftest2.$ac_objext
+ then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp
+ $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+ if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_c_o=yes
+ fi
+ fi
+ chmod u+w . 2>&5
+ $RM conftest*
+ # SGI C++ compiler will create directory out/ii_files/ for
+ # template instantiation
+ test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+ $RM out/* && rmdir out
+ cd ..
+ $RM -r conftest
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5
+$as_echo "$lt_cv_prog_compiler_c_o" >&6; }
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5
+$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
+if ${lt_cv_prog_compiler_c_o+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_c_o=no
+ $RM -r conftest 2>/dev/null
+ mkdir conftest
+ cd conftest
+ mkdir out
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ lt_compiler_flag="-o out/conftest2.$ac_objext"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>out/conftest.err)
+ ac_status=$?
+ cat out/conftest.err >&5
+ echo "$as_me:__oline__: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s out/conftest2.$ac_objext
+ then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp
+ $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+ if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_c_o=yes
+ fi
+ fi
+ chmod u+w . 2>&5
+ $RM conftest*
+ # SGI C++ compiler will create directory out/ii_files/ for
+ # template instantiation
+ test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+ $RM out/* && rmdir out
+ cd ..
+ $RM -r conftest
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5
+$as_echo "$lt_cv_prog_compiler_c_o" >&6; }
+
+
+
+
+hard_links="nottested"
+if test "$lt_cv_prog_compiler_c_o" = no && test "$need_locks" != no; then
+ # do not overwrite the value of need_locks provided by the user
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5
+$as_echo_n "checking if we can lock with hard links... " >&6; }
+ hard_links=yes
+ $RM conftest*
+ ln conftest.a conftest.b 2>/dev/null && hard_links=no
+ touch conftest.a
+ ln conftest.a conftest.b 2>&5 || hard_links=no
+ ln conftest.a conftest.b 2>/dev/null && hard_links=no
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5
+$as_echo "$hard_links" >&6; }
+ if test "$hard_links" = no; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5
+$as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;}
+ need_locks=warn
+ fi
+else
+ need_locks=no
+fi
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5
+$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; }
+
+ runpath_var=
+ allow_undefined_flag=
+ always_export_symbols=no
+ archive_cmds=
+ archive_expsym_cmds=
+ compiler_needs_object=no
+ enable_shared_with_static_runtimes=no
+ export_dynamic_flag_spec=
+ export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+ hardcode_automatic=no
+ hardcode_direct=no
+ hardcode_direct_absolute=no
+ hardcode_libdir_flag_spec=
+ hardcode_libdir_flag_spec_ld=
+ hardcode_libdir_separator=
+ hardcode_minus_L=no
+ hardcode_shlibpath_var=unsupported
+ inherit_rpath=no
+ link_all_deplibs=unknown
+ module_cmds=
+ module_expsym_cmds=
+ old_archive_from_new_cmds=
+ old_archive_from_expsyms_cmds=
+ thread_safe_flag_spec=
+ whole_archive_flag_spec=
+ # include_expsyms should be a list of space-separated symbols to be *always*
+ # included in the symbol list
+ include_expsyms=
+ # exclude_expsyms can be an extended regexp of symbols to exclude
+ # it will be wrapped by ` (' and `)$', so one must not match beginning or
+ # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc',
+ # as well as any symbol that contains `d'.
+ exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'
+ # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
+ # platforms (ab)use it in PIC code, but their linkers get confused if
+ # the symbol is explicitly referenced. Since portable code cannot
+ # rely on this symbol name, it's probably fine to never include it in
+ # preloaded symbol tables.
+ # Exclude shared library initialization/finalization symbols.
+ extract_expsyms_cmds=
+
+ case $host_os in
+ cygwin* | mingw* | pw32* | cegcc*)
+ # FIXME: the MSVC++ port hasn't been tested in a loooong time
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ if test "$GCC" != yes; then
+ with_gnu_ld=no
+ fi
+ ;;
+ interix*)
+ # we just hope/assume this is gcc and not c89 (= MSVC++)
+ with_gnu_ld=yes
+ ;;
+ openbsd*)
+ with_gnu_ld=no
+ ;;
+ esac
+
+ ld_shlibs=yes
+ if test "$with_gnu_ld" = yes; then
+ # If archive_cmds runs LD, not CC, wlarc should be empty
+ wlarc='${wl}'
+
+ # Set some defaults for GNU ld with shared library support. These
+ # are reset later if shared libraries are not supported. Putting them
+ # here allows them to be overridden if necessary.
+ runpath_var=LD_RUN_PATH
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ export_dynamic_flag_spec='${wl}--export-dynamic'
+ # ancient GNU ld didn't support --whole-archive et. al.
+ if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then
+ whole_archive_flag_spec="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+ else
+ whole_archive_flag_spec=
+ fi
+ supports_anon_versioning=no
+ case `$LD -v 2>&1` in
+ *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11
+ *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
+ *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
+ *\ 2.11.*) ;; # other 2.11 versions
+ *) supports_anon_versioning=yes ;;
+ esac
+
+ # See if GNU ld supports shared libraries.
+ case $host_os in
+ aix[3-9]*)
+ # On AIX/PPC, the GNU linker is very broken
+ if test "$host_cpu" != ia64; then
+ ld_shlibs=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the GNU linker, at least up to release 2.9.1, is reported
+*** to be unable to reliably create shared libraries on AIX.
+*** Therefore, libtool is disabling shared libraries support. If you
+*** really care for shared libraries, you may want to modify your PATH
+*** so that a non-GNU linker is found, and then restart.
+
+_LT_EOF
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds=''
+ ;;
+ m68k)
+ archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ ;;
+ esac
+ ;;
+
+ beos*)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ allow_undefined_flag=unsupported
+ # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+ # support --undefined. This deserves some investigation. FIXME
+ archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ cygwin* | mingw* | pw32* | cegcc*)
+ # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless,
+ # as there is no search path for DLLs.
+ hardcode_libdir_flag_spec='-L$libdir'
+ allow_undefined_flag=unsupported
+ always_export_symbols=no
+ enable_shared_with_static_runtimes=yes
+ export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols'
+
+ if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ # If the export-symbols file already is a .def file (1st line
+ # is EXPORTS), use it as is; otherwise, prepend...
+ archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+ cp $export_symbols $output_objdir/$soname.def;
+ else
+ echo EXPORTS > $output_objdir/$soname.def;
+ cat $export_symbols >> $output_objdir/$soname.def;
+ fi~
+ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ interix[3-9]*)
+ hardcode_direct=no
+ hardcode_shlibpath_var=no
+ hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+ export_dynamic_flag_spec='${wl}-E'
+ # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+ # Instead, shared libraries are loaded at an image base (0x10000000 by
+ # default) and relocated if they conflict, which is a slow very memory
+ # consuming and fragmenting process. To avoid this, we pick a random,
+ # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+ # time. Moving up from 0x10000000 also allows more sbrk(2) space.
+ archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ archive_expsym_cmds='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ ;;
+
+ gnu* | linux* | tpf* | k*bsd*-gnu)
+ tmp_diet=no
+ if test "$host_os" = linux-dietlibc; then
+ case $cc_basename in
+ diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn)
+ esac
+ fi
+ if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \
+ && test "$tmp_diet" = no
+ then
+ tmp_addflag=
+ tmp_sharedflag='-shared'
+ case $cc_basename,$host_cpu in
+ pgcc*) # Portland Group C compiler
+ whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+ tmp_addflag=' $pic_flag'
+ ;;
+ pgf77* | pgf90* | pgf95*) # Portland Group f77 and f90 compilers
+ whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+ tmp_addflag=' $pic_flag -Mnomain' ;;
+ ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64
+ tmp_addflag=' -i_dynamic' ;;
+ efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64
+ tmp_addflag=' -i_dynamic -nofor_main' ;;
+ ifc* | ifort*) # Intel Fortran compiler
+ tmp_addflag=' -nofor_main' ;;
+ lf95*) # Lahey Fortran 8.1
+ whole_archive_flag_spec=
+ tmp_sharedflag='--shared' ;;
+ xl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below)
+ tmp_sharedflag='-qmkshrobj'
+ tmp_addflag= ;;
+ esac
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*) # Sun C 5.9
+ whole_archive_flag_spec='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+ compiler_needs_object=yes
+ tmp_sharedflag='-G' ;;
+ *Sun\ F*) # Sun Fortran 8.3
+ tmp_sharedflag='-G' ;;
+ esac
+ archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+
+ if test "x$supports_anon_versioning" = xyes; then
+ archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+ fi
+
+ case $cc_basename in
+ xlf*)
+ # IBM XL Fortran 10.1 on PPC cannot create shared libs itself
+ whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive'
+ hardcode_libdir_flag_spec=
+ hardcode_libdir_flag_spec_ld='-rpath $libdir'
+ archive_cmds='$LD -shared $libobjs $deplibs $compiler_flags -soname $soname -o $lib'
+ if test "x$supports_anon_versioning" = xyes; then
+ archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $LD -shared $libobjs $deplibs $compiler_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib'
+ fi
+ ;;
+ esac
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
+ wlarc=
+ else
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ fi
+ ;;
+
+ solaris*)
+ if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then
+ ld_shlibs=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: The releases 2.8.* of the GNU linker cannot reliably
+*** create shared libraries on Solaris systems. Therefore, libtool
+*** is disabling shared libraries support. We urge you to upgrade GNU
+*** binutils to release 2.9.1 or newer. Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+ elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*)
+ case `$LD -v 2>&1` in
+ *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*)
+ ld_shlibs=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not
+*** reliably create shared libraries on SCO systems. Therefore, libtool
+*** is disabling shared libraries support. We urge you to upgrade GNU
+*** binutils to release 2.16.91.0.3 or newer. Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+ ;;
+ *)
+ # For security reasons, it is highly recommended that you always
+ # use absolute paths for naming shared libraries, and exclude the
+ # DT_RUNPATH tag from executables and libraries. But doing so
+ # requires that you compile everything twice, which is a pain.
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+ esac
+ ;;
+
+ sunos4*)
+ archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ wlarc=
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ *)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+ esac
+
+ if test "$ld_shlibs" = no; then
+ runpath_var=
+ hardcode_libdir_flag_spec=
+ export_dynamic_flag_spec=
+ whole_archive_flag_spec=
+ fi
+ else
+ # PORTME fill in a description of your system's linker (not GNU ld)
+ case $host_os in
+ aix3*)
+ allow_undefined_flag=unsupported
+ always_export_symbols=yes
+ archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'
+ # Note: this linker hardcodes the directories in LIBPATH if there
+ # are no directories specified by -L.
+ hardcode_minus_L=yes
+ if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then
+ # Neither direct hardcoding nor static linking is supported with a
+ # broken collect2.
+ hardcode_direct=unsupported
+ fi
+ ;;
+
+ aix[4-9]*)
+ if test "$host_cpu" = ia64; then
+ # On IA64, the linker does run time linking by default, so we don't
+ # have to do anything special.
+ aix_use_runtimelinking=no
+ exp_sym_flag='-Bexport'
+ no_entry_flag=""
+ else
+ # If we're using GNU nm, then we don't want the "-C" option.
+ # -C means demangle to AIX nm, but means don't demangle with GNU nm
+ if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+ export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+ else
+ export_symbols_cmds='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+ fi
+ aix_use_runtimelinking=no
+
+ # Test if we are trying to use run time linking or normal
+ # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+ # need to do runtime linking.
+ case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*)
+ for ld_flag in $LDFLAGS; do
+ if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then
+ aix_use_runtimelinking=yes
+ break
+ fi
+ done
+ ;;
+ esac
+
+ exp_sym_flag='-bexport'
+ no_entry_flag='-bnoentry'
+ fi
+
+ # When large executables or shared objects are built, AIX ld can
+ # have problems creating the table of contents. If linking a library
+ # or program results in "error TOC overflow" add -mminimal-toc to
+ # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not
+ # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+ archive_cmds=''
+ hardcode_direct=yes
+ hardcode_direct_absolute=yes
+ hardcode_libdir_separator=':'
+ link_all_deplibs=yes
+ file_list_spec='${wl}-f,'
+
+ if test "$GCC" = yes; then
+ case $host_os in aix4.[012]|aix4.[012].*)
+ # We only want to do this on AIX 4.2 and lower, the check
+ # below for broken collect2 doesn't work under 4.3+
+ collect2name=`${CC} -print-prog-name=collect2`
+ if test -f "$collect2name" &&
+ strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+ then
+ # We have reworked collect2
+ :
+ else
+ # We have old collect2
+ hardcode_direct=unsupported
+ # It fails to find uninstalled libraries when the uninstalled
+ # path is not listed in the libpath. Setting hardcode_minus_L
+ # to unsupported forces relinking
+ hardcode_minus_L=yes
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_libdir_separator=
+ fi
+ ;;
+ esac
+ shared_flag='-shared'
+ if test "$aix_use_runtimelinking" = yes; then
+ shared_flag="$shared_flag "'${wl}-G'
+ fi
+ else
+ # not using gcc
+ if test "$host_cpu" = ia64; then
+ # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+ # chokes on -Wl,-G. The following line is correct:
+ shared_flag='-G'
+ else
+ if test "$aix_use_runtimelinking" = yes; then
+ shared_flag='${wl}-G'
+ else
+ shared_flag='${wl}-bM:SRE'
+ fi
+ fi
+ fi
+
+ export_dynamic_flag_spec='${wl}-bexpall'
+ # It seems that -bexpall does not export symbols beginning with
+ # underscore (_), so it is better to generate a list of symbols to export.
+ always_export_symbols=yes
+ if test "$aix_use_runtimelinking" = yes; then
+ # Warning - without using the other runtime loading flags (-brtl),
+ # -berok will link without error, but may produce a broken library.
+ allow_undefined_flag='-berok'
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+lt_aix_libpath_sed='
+ /Import File Strings/,/^$/ {
+ /^0/ {
+ s/^0 *\(.*\)$/\1/
+ p
+ }
+ }'
+aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+# Check for a 64-bit object if we didn't find anything.
+if test -z "$aix_libpath"; then
+ aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+fi
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+
+ hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath"
+ archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then $ECHO "X${wl}${allow_undefined_flag}" | $Xsed; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+ else
+ if test "$host_cpu" = ia64; then
+ hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib'
+ allow_undefined_flag="-z nodefs"
+ archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols"
+ else
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+lt_aix_libpath_sed='
+ /Import File Strings/,/^$/ {
+ /^0/ {
+ s/^0 *\(.*\)$/\1/
+ p
+ }
+ }'
+aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+# Check for a 64-bit object if we didn't find anything.
+if test -z "$aix_libpath"; then
+ aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+fi
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+
+ hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath"
+ # Warning - without using the other run time loading flags,
+ # -berok will link without error, but may produce a broken library.
+ no_undefined_flag=' ${wl}-bernotok'
+ allow_undefined_flag=' ${wl}-berok'
+ # Exported symbols can be pulled into shared objects from archives
+ whole_archive_flag_spec='$convenience'
+ archive_cmds_need_lc=yes
+ # This is similar to how AIX traditionally builds its shared libraries.
+ archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+ fi
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds=''
+ ;;
+ m68k)
+ archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ ;;
+ esac
+ ;;
+
+ bsdi[45]*)
+ export_dynamic_flag_spec=-rdynamic
+ ;;
+
+ cygwin* | mingw* | pw32* | cegcc*)
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ # hardcode_libdir_flag_spec is actually meaningless, as there is
+ # no search path for DLLs.
+ hardcode_libdir_flag_spec=' '
+ allow_undefined_flag=unsupported
+ # Tell ltmain to make .lib files, not .a files.
+ libext=lib
+ # Tell ltmain to make .dll files, not .so files.
+ shrext_cmds=".dll"
+ # FIXME: Setting linknames here is a bad hack.
+ archive_cmds='$CC -o $lib $libobjs $compiler_flags `$ECHO "X$deplibs" | $Xsed -e '\''s/ -lc$//'\''` -link -dll~linknames='
+ # The linker will automatically build a .lib file if we build a DLL.
+ old_archive_from_new_cmds='true'
+ # FIXME: Should let the user specify the lib program.
+ old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs'
+ fix_srcfile_path='`cygpath -w "$srcfile"`'
+ enable_shared_with_static_runtimes=yes
+ ;;
+
+ darwin* | rhapsody*)
+
+
+ archive_cmds_need_lc=no
+ hardcode_direct=no
+ hardcode_automatic=yes
+ hardcode_shlibpath_var=unsupported
+ whole_archive_flag_spec=''
+ link_all_deplibs=yes
+ allow_undefined_flag="$_lt_dar_allow_undefined"
+ case $cc_basename in
+ ifort*) _lt_dar_can_shared=yes ;;
+ *) _lt_dar_can_shared=$GCC ;;
+ esac
+ if test "$_lt_dar_can_shared" = "yes"; then
+ output_verbose_link_cmd=echo
+ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}"
+ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}"
+ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}"
+ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}"
+
+ else
+ ld_shlibs=no
+ fi
+
+ ;;
+
+ dgux*)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_shlibpath_var=no
+ ;;
+
+ freebsd1*)
+ ld_shlibs=no
+ ;;
+
+ # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
+ # support. Future versions do this automatically, but an explicit c++rt0.o
+ # does not break anything, and helps significantly (at the cost of a little
+ # extra space).
+ freebsd2.2*)
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ # Unfortunately, older versions of FreeBSD 2 do not have this feature.
+ freebsd2*)
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=yes
+ hardcode_minus_L=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
+ freebsd* | dragonfly*)
+ archive_cmds='$CC -shared -o $lib $libobjs $deplibs $compiler_flags'
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ hpux9*)
+ if test "$GCC" = yes; then
+ archive_cmds='$RM $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+ else
+ archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+ fi
+ hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+ hardcode_libdir_separator=:
+ hardcode_direct=yes
+
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ hardcode_minus_L=yes
+ export_dynamic_flag_spec='${wl}-E'
+ ;;
+
+ hpux10*)
+ if test "$GCC" = yes -a "$with_gnu_ld" = no; then
+ archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+ fi
+ if test "$with_gnu_ld" = no; then
+ hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+ hardcode_libdir_flag_spec_ld='+b $libdir'
+ hardcode_libdir_separator=:
+ hardcode_direct=yes
+ hardcode_direct_absolute=yes
+ export_dynamic_flag_spec='${wl}-E'
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ hardcode_minus_L=yes
+ fi
+ ;;
+
+ hpux11*)
+ if test "$GCC" = yes -a "$with_gnu_ld" = no; then
+ case $host_cpu in
+ hppa*64*)
+ archive_cmds='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ ia64*)
+ archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ else
+ case $host_cpu in
+ hppa*64*)
+ archive_cmds='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ ia64*)
+ archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ fi
+ if test "$with_gnu_ld" = no; then
+ hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+ hardcode_libdir_separator=:
+
+ case $host_cpu in
+ hppa*64*|ia64*)
+ hardcode_direct=no
+ hardcode_shlibpath_var=no
+ ;;
+ *)
+ hardcode_direct=yes
+ hardcode_direct_absolute=yes
+ export_dynamic_flag_spec='${wl}-E'
+
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ hardcode_minus_L=yes
+ ;;
+ esac
+ fi
+ ;;
+
+ irix5* | irix6* | nonstopux*)
+ if test "$GCC" = yes; then
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ # Try to use the -exported_symbol ld option, if it does not
+ # work, assume that -exports_file does not work either and
+ # implicitly export all symbols.
+ save_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+int foo (void) { return 0; }
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib'
+
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LDFLAGS="$save_LDFLAGS"
+ else
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib'
+ fi
+ archive_cmds_need_lc='no'
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ hardcode_libdir_separator=:
+ inherit_rpath=yes
+ link_all_deplibs=yes
+ ;;
+
+ netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out
+ else
+ archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF
+ fi
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ newsos6)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=yes
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ hardcode_libdir_separator=:
+ hardcode_shlibpath_var=no
+ ;;
+
+ *nto* | *qnx*)
+ ;;
+
+ openbsd*)
+ if test -f /usr/libexec/ld.so; then
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ hardcode_direct_absolute=yes
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+ archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols'
+ hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+ export_dynamic_flag_spec='${wl}-E'
+ else
+ case $host_os in
+ openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*)
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_libdir_flag_spec='-R$libdir'
+ ;;
+ *)
+ archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+ ;;
+ esac
+ fi
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ os2*)
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ allow_undefined_flag=unsupported
+ archive_cmds='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$ECHO DATA >> $output_objdir/$libname.def~$ECHO " SINGLE NONSHARED" >> $output_objdir/$libname.def~$ECHO EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def'
+ old_archive_from_new_cmds='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def'
+ ;;
+
+ osf3*)
+ if test "$GCC" = yes; then
+ allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*'
+ archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ else
+ allow_undefined_flag=' -expect_unresolved \*'
+ archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+ fi
+ archive_cmds_need_lc='no'
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ hardcode_libdir_separator=:
+ ;;
+
+ osf4* | osf5*) # as osf3* with the addition of -msym flag
+ if test "$GCC" = yes; then
+ allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*'
+ archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ else
+ allow_undefined_flag=' -expect_unresolved \*'
+ archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+ archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~
+ $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp'
+
+ # Both c and cxx compiler support -rpath directly
+ hardcode_libdir_flag_spec='-rpath $libdir'
+ fi
+ archive_cmds_need_lc='no'
+ hardcode_libdir_separator=:
+ ;;
+
+ solaris*)
+ no_undefined_flag=' -z defs'
+ if test "$GCC" = yes; then
+ wlarc='${wl}'
+ archive_cmds='$CC -shared ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -shared ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ else
+ case `$CC -V 2>&1` in
+ *"Compilers 5.0"*)
+ wlarc=''
+ archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp'
+ ;;
+ *)
+ wlarc='${wl}'
+ archive_cmds='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ ;;
+ esac
+ fi
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_shlibpath_var=no
+ case $host_os in
+ solaris2.[0-5] | solaris2.[0-5].*) ;;
+ *)
+ # The compiler driver will combine and reorder linker options,
+ # but understands `-z linker_flag'. GCC discards it without `$wl',
+ # but is careful enough not to reorder.
+ # Supported since Solaris 2.6 (maybe 2.5.1?)
+ if test "$GCC" = yes; then
+ whole_archive_flag_spec='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
+ else
+ whole_archive_flag_spec='-z allextract$convenience -z defaultextract'
+ fi
+ ;;
+ esac
+ link_all_deplibs=yes
+ ;;
+
+ sunos4*)
+ if test "x$host_vendor" = xsequent; then
+ # Use $CC to link under sequent, because it throws in some extra .o
+ # files that make .init and .fini sections work.
+ archive_cmds='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
+ fi
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_direct=yes
+ hardcode_minus_L=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ sysv4)
+ case $host_vendor in
+ sni)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=yes # is this really true???
+ ;;
+ siemens)
+ ## LD is ld it makes a PLAMLIB
+ ## CC just makes a GrossModule.
+ archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+ reload_cmds='$CC -r -o $output$reload_objs'
+ hardcode_direct=no
+ ;;
+ motorola)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=no #Motorola manual says yes, but my tests say they lie
+ ;;
+ esac
+ runpath_var='LD_RUN_PATH'
+ hardcode_shlibpath_var=no
+ ;;
+
+ sysv4.3*)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_shlibpath_var=no
+ export_dynamic_flag_spec='-Bexport'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_shlibpath_var=no
+ runpath_var=LD_RUN_PATH
+ hardcode_runpath_var=yes
+ ld_shlibs=yes
+ fi
+ ;;
+
+ sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*)
+ no_undefined_flag='${wl}-z,text'
+ archive_cmds_need_lc=no
+ hardcode_shlibpath_var=no
+ runpath_var='LD_RUN_PATH'
+
+ if test "$GCC" = yes; then
+ archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ fi
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6*)
+ # Note: We can NOT use -z defs as we might desire, because we do not
+ # link with -lc, and that would cause any symbols used from libc to
+ # always be unresolved, which means just about no library would
+ # ever link correctly. If we're not using GNU ld we use -z text
+ # though, which does catch some bad symbols but isn't as heavy-handed
+ # as -z defs.
+ no_undefined_flag='${wl}-z,text'
+ allow_undefined_flag='${wl}-z,nodefs'
+ archive_cmds_need_lc=no
+ hardcode_shlibpath_var=no
+ hardcode_libdir_flag_spec='${wl}-R,$libdir'
+ hardcode_libdir_separator=':'
+ link_all_deplibs=yes
+ export_dynamic_flag_spec='${wl}-Bexport'
+ runpath_var='LD_RUN_PATH'
+
+ if test "$GCC" = yes; then
+ archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ fi
+ ;;
+
+ uts4*)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_shlibpath_var=no
+ ;;
+
+ *)
+ ld_shlibs=no
+ ;;
+ esac
+
+ if test x$host_vendor = xsni; then
+ case $host in
+ sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+ export_dynamic_flag_spec='${wl}-Blargedynsym'
+ ;;
+ esac
+ fi
+ fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5
+$as_echo "$ld_shlibs" >&6; }
+test "$ld_shlibs" = no && can_build_shared=no
+
+with_gnu_ld=$with_gnu_ld
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#
+# Do we need to explicitly link libc?
+#
+case "x$archive_cmds_need_lc" in
+x|xyes)
+ # Assume -lc should be added
+ archive_cmds_need_lc=yes
+
+ if test "$enable_shared" = yes && test "$GCC" = yes; then
+ case $archive_cmds in
+ *'~'*)
+ # FIXME: we may have to deal with multi-command sequences.
+ ;;
+ '$CC '*)
+ # Test whether the compiler implicitly links with -lc since on some
+ # systems, -lgcc has to come before -lc. If gcc already passes -lc
+ # to ld, don't add -lc before -lgcc.
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5
+$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; }
+ $RM conftest*
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } 2>conftest.err; then
+ soname=conftest
+ lib=conftest
+ libobjs=conftest.$ac_objext
+ deplibs=
+ wl=$lt_prog_compiler_wl
+ pic_flag=$lt_prog_compiler_pic
+ compiler_flags=-v
+ linker_flags=-v
+ verstring=
+ output_objdir=.
+ libname=conftest
+ lt_save_allow_undefined_flag=$allow_undefined_flag
+ allow_undefined_flag=
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5
+ (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+ then
+ archive_cmds_need_lc=no
+ else
+ archive_cmds_need_lc=yes
+ fi
+ allow_undefined_flag=$lt_save_allow_undefined_flag
+ else
+ cat conftest.err 1>&5
+ fi
+ $RM conftest*
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $archive_cmds_need_lc" >&5
+$as_echo "$archive_cmds_need_lc" >&6; }
+ ;;
+ esac
+ fi
+ ;;
+esac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5
+$as_echo_n "checking dynamic linker characteristics... " >&6; }
+
+if test "$GCC" = yes; then
+ case $host_os in
+ darwin*) lt_awk_arg="/^libraries:/,/LR/" ;;
+ *) lt_awk_arg="/^libraries:/" ;;
+ esac
+ lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e "s,=/,/,g"`
+ if $ECHO "$lt_search_path_spec" | $GREP ';' >/dev/null ; then
+ # if the path contains ";" then we assume it to be the separator
+ # otherwise default to the standard path separator (i.e. ":") - it is
+ # assumed that no part of a normal pathname contains ";" but that should
+ # okay in the real world where ";" in dirpaths is itself problematic.
+ lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e 's/;/ /g'`
+ else
+ lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+ fi
+ # Ok, now we have the path, separated by spaces, we can step through it
+ # and add multilib dir if necessary.
+ lt_tmp_lt_search_path_spec=
+ lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null`
+ for lt_sys_path in $lt_search_path_spec; do
+ if test -d "$lt_sys_path/$lt_multi_os_dir"; then
+ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir"
+ else
+ test -d "$lt_sys_path" && \
+ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path"
+ fi
+ done
+ lt_search_path_spec=`$ECHO $lt_tmp_lt_search_path_spec | awk '
+BEGIN {RS=" "; FS="/|\n";} {
+ lt_foo="";
+ lt_count=0;
+ for (lt_i = NF; lt_i > 0; lt_i--) {
+ if ($lt_i != "" && $lt_i != ".") {
+ if ($lt_i == "..") {
+ lt_count++;
+ } else {
+ if (lt_count == 0) {
+ lt_foo="/" $lt_i lt_foo;
+ } else {
+ lt_count--;
+ }
+ }
+ }
+ }
+ if (lt_foo != "") { lt_freq[lt_foo]++; }
+ if (lt_freq[lt_foo] == 1) { print lt_foo; }
+}'`
+ sys_lib_search_path_spec=`$ECHO $lt_search_path_spec`
+else
+ sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+fi
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+shrext_cmds=".so"
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+need_lib_prefix=unknown
+hardcode_into_libs=no
+
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+need_version=unknown
+
+case $host_os in
+aix3*)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a'
+ shlibpath_var=LIBPATH
+
+ # AIX 3 has no versioning support, so we append a major version to the name.
+ soname_spec='${libname}${release}${shared_ext}$major'
+ ;;
+
+aix[4-9]*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ hardcode_into_libs=yes
+ if test "$host_cpu" = ia64; then
+ # AIX 5 supports IA64
+ library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ else
+ # With GCC up to 2.95.x, collect2 would create an import file
+ # for dependence libraries. The import file would start with
+ # the line `#! .'. This would cause the generated library to
+ # depend on `.', always an invalid library. This was fixed in
+ # development snapshots of GCC prior to 3.0.
+ case $host_os in
+ aix4 | aix4.[01] | aix4.[01].*)
+ if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
+ echo ' yes '
+ echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then
+ :
+ else
+ can_build_shared=no
+ fi
+ ;;
+ esac
+ # AIX (on Power*) has no versioning support, so currently we can not hardcode correct
+ # soname into executable. Probably we can add versioning support to
+ # collect2, so additional links can be useful in future.
+ if test "$aix_use_runtimelinking" = yes; then
+ # If using run time linking (on AIX 4.2 or later) use lib<name>.so
+ # instead of lib<name>.a to let people know that these are not
+ # typical AIX shared libraries.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ else
+ # We preserve .a as extension for shared libraries through AIX4.2
+ # and later when we are not doing run time linking.
+ library_names_spec='${libname}${release}.a $libname.a'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ fi
+ shlibpath_var=LIBPATH
+ fi
+ ;;
+
+amigaos*)
+ case $host_cpu in
+ powerpc)
+ # Since July 2007 AmigaOS4 officially supports .so libraries.
+ # When compiling the executable, add -use-dynld -Lsobjs: to the compileline.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ ;;
+ m68k)
+ library_names_spec='$libname.ixlibrary $libname.a'
+ # Create ${libname}_ixlibrary.a entries in /sys/libs.
+ finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$ECHO "X$lib" | $Xsed -e '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+ ;;
+ esac
+ ;;
+
+beos*)
+ library_names_spec='${libname}${shared_ext}'
+ dynamic_linker="$host_os ld.so"
+ shlibpath_var=LIBRARY_PATH
+ ;;
+
+bsdi[45]*)
+ version_type=linux
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+ sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+ # the default ld.so.conf also contains /usr/contrib/lib and
+ # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+ # libtool to hard-code these into programs
+ ;;
+
+cygwin* | mingw* | pw32* | cegcc*)
+ version_type=windows
+ shrext_cmds=".dll"
+ need_version=no
+ need_lib_prefix=no
+
+ case $GCC,$host_os in
+ yes,cygwin* | yes,mingw* | yes,pw32* | yes,cegcc*)
+ library_names_spec='$libname.dll.a'
+ # DLL is installed to $(libdir)/../bin by postinstall_cmds
+ postinstall_cmds='base_file=`basename \${file}`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~
+ dldir=$destdir/`dirname \$dlpath`~
+ test -d \$dldir || mkdir -p \$dldir~
+ $install_prog $dir/$dlname \$dldir/$dlname~
+ chmod a+x \$dldir/$dlname~
+ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+ eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+ fi'
+ postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+ dlpath=$dir/\$dldll~
+ $RM \$dlpath'
+ shlibpath_overrides_runpath=yes
+
+ case $host_os in
+ cygwin*)
+ # Cygwin DLLs use 'cyg' prefix rather than 'lib'
+ soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+ sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib"
+ ;;
+ mingw* | cegcc*)
+ # MinGW DLLs use traditional 'lib' prefix
+ soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+ sys_lib_search_path_spec=`$CC -print-search-dirs | $GREP "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"`
+ if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then
+ # It is most probably a Windows format PATH printed by
+ # mingw gcc, but we are running on Cygwin. Gcc prints its search
+ # path with ; separators, and with drive letters. We can handle the
+ # drive letters (cygwin fileutils understands them), so leave them,
+ # especially as we might pass files found there to a mingw objdump,
+ # which wouldn't understand a cygwinified path. Ahh.
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+ else
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+ fi
+ ;;
+ pw32*)
+ # pw32 DLLs use 'pw' prefix rather than 'lib'
+ library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+ ;;
+ esac
+ ;;
+
+ *)
+ library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib'
+ ;;
+ esac
+ dynamic_linker='Win32 ld.exe'
+ # FIXME: first we should search . and the directory the executable is in
+ shlibpath_var=PATH
+ ;;
+
+darwin* | rhapsody*)
+ dynamic_linker="$host_os dyld"
+ version_type=darwin
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext'
+ soname_spec='${libname}${release}${major}$shared_ext'
+ shlibpath_overrides_runpath=yes
+ shlibpath_var=DYLD_LIBRARY_PATH
+ shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'
+
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"
+ sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
+ ;;
+
+dgux*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+freebsd1*)
+ dynamic_linker=no
+ ;;
+
+freebsd* | dragonfly*)
+ # DragonFly does not have aout. When/if they implement a new
+ # versioning mechanism, adjust this.
+ if test -x /usr/bin/objformat; then
+ objformat=`/usr/bin/objformat`
+ else
+ case $host_os in
+ freebsd[123]*) objformat=aout ;;
+ *) objformat=elf ;;
+ esac
+ fi
+ version_type=freebsd-$objformat
+ case $version_type in
+ freebsd-elf*)
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+ need_version=no
+ need_lib_prefix=no
+ ;;
+ freebsd-*)
+ library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix'
+ need_version=yes
+ ;;
+ esac
+ shlibpath_var=LD_LIBRARY_PATH
+ case $host_os in
+ freebsd2*)
+ shlibpath_overrides_runpath=yes
+ ;;
+ freebsd3.[01]* | freebsdelf3.[01]*)
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+ freebsd3.[2-9]* | freebsdelf3.[2-9]* | \
+ freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1)
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+ *) # from 4.6 on, and DragonFly
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+ esac
+ ;;
+
+gnu*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ hardcode_into_libs=yes
+ ;;
+
+hpux9* | hpux10* | hpux11*)
+ # Give a soname corresponding to the major version so that dld.sl refuses to
+ # link against other versions.
+ version_type=sunos
+ need_lib_prefix=no
+ need_version=no
+ case $host_cpu in
+ ia64*)
+ shrext_cmds='.so'
+ hardcode_into_libs=yes
+ dynamic_linker="$host_os dld.so"
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ if test "X$HPUX_IA64_MODE" = X32; then
+ sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
+ else
+ sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
+ fi
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ ;;
+ hppa*64*)
+ shrext_cmds='.sl'
+ hardcode_into_libs=yes
+ dynamic_linker="$host_os dld.sl"
+ shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
+ shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ ;;
+ *)
+ shrext_cmds='.sl'
+ dynamic_linker="$host_os dld.sl"
+ shlibpath_var=SHLIB_PATH
+ shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ ;;
+ esac
+ # HP-UX runs *really* slowly unless shared libraries are mode 555.
+ postinstall_cmds='chmod 555 $lib'
+ ;;
+
+interix[3-9]*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+
+irix5* | irix6* | nonstopux*)
+ case $host_os in
+ nonstopux*) version_type=nonstopux ;;
+ *)
+ if test "$lt_cv_prog_gnu_ld" = yes; then
+ version_type=linux
+ else
+ version_type=irix
+ fi ;;
+ esac
+ need_lib_prefix=no
+ need_version=no
+ soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}'
+ case $host_os in
+ irix5* | nonstopux*)
+ libsuff= shlibsuff=
+ ;;
+ *)
+ case $LD in # libtool.m4 will add one of these switches to LD
+ *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
+ libsuff= shlibsuff= libmagic=32-bit;;
+ *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
+ libsuff=32 shlibsuff=N32 libmagic=N32;;
+ *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
+ libsuff=64 shlibsuff=64 libmagic=64-bit;;
+ *) libsuff= shlibsuff= libmagic=never-match;;
+ esac
+ ;;
+ esac
+ shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+ shlibpath_overrides_runpath=no
+ sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}"
+ sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}"
+ hardcode_into_libs=yes
+ ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux*oldld* | linux*aout* | linux*coff*)
+ dynamic_linker=no
+ ;;
+
+# This must be Linux ELF.
+linux* | k*bsd*-gnu)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ # Some binutils ld are patched to set DT_RUNPATH
+ save_LDFLAGS=$LDFLAGS
+ save_libdir=$libdir
+ eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \
+ LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\""
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then :
+ shlibpath_overrides_runpath=yes
+fi
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LDFLAGS=$save_LDFLAGS
+ libdir=$save_libdir
+
+ # This implies no fast_install, which is unacceptable.
+ # Some rework will be needed to allow for fast_install
+ # before this can be enabled.
+ hardcode_into_libs=yes
+
+ # Append ld.so.conf contents to the search path
+ if test -f /etc/ld.so.conf; then
+ lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '`
+ sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
+ fi
+
+ # We used to test for /lib/ld.so.1 and disable shared libraries on
+ # powerpc, because MkLinux only supported shared libraries with the
+ # GNU dynamic linker. Since this was broken with cross compilers,
+ # most powerpc-linux boxes support dynamic linking these days and
+ # people can always --disable-shared, the test was removed, and we
+ # assume the GNU/Linux dynamic linker is in use.
+ dynamic_linker='GNU/Linux ld.so'
+ ;;
+
+netbsd*)
+ version_type=sunos
+ need_lib_prefix=no
+ need_version=no
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ dynamic_linker='NetBSD (a.out) ld.so'
+ else
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ dynamic_linker='NetBSD ld.elf_so'
+ fi
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+
+newsos6)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ ;;
+
+*nto* | *qnx*)
+ version_type=qnx
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ dynamic_linker='ldqnx.so'
+ ;;
+
+openbsd*)
+ version_type=sunos
+ sys_lib_dlsearch_path_spec="/usr/lib"
+ need_lib_prefix=no
+ # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs.
+ case $host_os in
+ openbsd3.3 | openbsd3.3.*) need_version=yes ;;
+ *) need_version=no ;;
+ esac
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+ case $host_os in
+ openbsd2.[89] | openbsd2.[89].*)
+ shlibpath_overrides_runpath=no
+ ;;
+ *)
+ shlibpath_overrides_runpath=yes
+ ;;
+ esac
+ else
+ shlibpath_overrides_runpath=yes
+ fi
+ ;;
+
+os2*)
+ libname_spec='$name'
+ shrext_cmds=".dll"
+ need_lib_prefix=no
+ library_names_spec='$libname${shared_ext} $libname.a'
+ dynamic_linker='OS/2 ld.exe'
+ shlibpath_var=LIBPATH
+ ;;
+
+osf3* | osf4* | osf5*)
+ version_type=osf
+ need_lib_prefix=no
+ need_version=no
+ soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+ sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec"
+ ;;
+
+rdos*)
+ dynamic_linker=no
+ ;;
+
+solaris*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ # ldd complains unless libraries are executable
+ postinstall_cmds='chmod +x $lib'
+ ;;
+
+sunos4*)
+ version_type=sunos
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ if test "$with_gnu_ld" = yes; then
+ need_lib_prefix=no
+ fi
+ need_version=yes
+ ;;
+
+sysv4 | sysv4.3*)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ case $host_vendor in
+ sni)
+ shlibpath_overrides_runpath=no
+ need_lib_prefix=no
+ runpath_var=LD_RUN_PATH
+ ;;
+ siemens)
+ need_lib_prefix=no
+ ;;
+ motorola)
+ need_lib_prefix=no
+ need_version=no
+ shlibpath_overrides_runpath=no
+ sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+ ;;
+ esac
+ ;;
+
+sysv4*MP*)
+ if test -d /usr/nec ;then
+ version_type=linux
+ library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}'
+ soname_spec='$libname${shared_ext}.$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ fi
+ ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+ version_type=freebsd-elf
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ if test "$with_gnu_ld" = yes; then
+ sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib'
+ else
+ sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'
+ case $host_os in
+ sco3.2v5*)
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /lib"
+ ;;
+ esac
+ fi
+ sys_lib_dlsearch_path_spec='/usr/lib'
+ ;;
+
+tpf*)
+ # TPF is a cross-target only. Preferred cross-host = GNU/Linux.
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+
+uts4*)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+*)
+ dynamic_linker=no
+ ;;
+esac
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5
+$as_echo "$dynamic_linker" >&6; }
+test "$dynamic_linker" = no && can_build_shared=no
+
+variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
+if test "$GCC" = yes; then
+ variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
+fi
+
+if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then
+ sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec"
+fi
+if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then
+ sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec"
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5
+$as_echo_n "checking how to hardcode library paths into programs... " >&6; }
+hardcode_action=
+if test -n "$hardcode_libdir_flag_spec" ||
+ test -n "$runpath_var" ||
+ test "X$hardcode_automatic" = "Xyes" ; then
+
+ # We can hardcode non-existent directories.
+ if test "$hardcode_direct" != no &&
+ # If the only mechanism to avoid hardcoding is shlibpath_var, we
+ # have to relink, otherwise we might link with an installed library
+ # when we should be linking with a yet-to-be-installed one
+ ## test "$_LT_TAGVAR(hardcode_shlibpath_var, )" != no &&
+ test "$hardcode_minus_L" != no; then
+ # Linking always hardcodes the temporary library directory.
+ hardcode_action=relink
+ else
+ # We can link without hardcoding, and we can hardcode nonexisting dirs.
+ hardcode_action=immediate
+ fi
+else
+ # We cannot hardcode anything, or else we can only hardcode existing
+ # directories.
+ hardcode_action=unsupported
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5
+$as_echo "$hardcode_action" >&6; }
+
+if test "$hardcode_action" = relink ||
+ test "$inherit_rpath" = yes; then
+ # Fast installation is not supported
+ enable_fast_install=no
+elif test "$shlibpath_overrides_runpath" = yes ||
+ test "$enable_shared" = no; then
+ # Fast installation is not necessary
+ enable_fast_install=needless
+fi
+
+
+
+
+
+
+ if test "x$enable_dlopen" != xyes; then
+ enable_dlopen=unknown
+ enable_dlopen_self=unknown
+ enable_dlopen_self_static=unknown
+else
+ lt_cv_dlopen=no
+ lt_cv_dlopen_libs=
+
+ case $host_os in
+ beos*)
+ lt_cv_dlopen="load_add_on"
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+ ;;
+
+ mingw* | pw32* | cegcc*)
+ lt_cv_dlopen="LoadLibrary"
+ lt_cv_dlopen_libs=
+ ;;
+
+ cygwin*)
+ lt_cv_dlopen="dlopen"
+ lt_cv_dlopen_libs=
+ ;;
+
+ darwin*)
+ # if libdl is installed we need to link against it
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
+$as_echo_n "checking for dlopen in -ldl... " >&6; }
+if ${ac_cv_lib_dl_dlopen+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_dl_dlopen=yes
+else
+ ac_cv_lib_dl_dlopen=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
+$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
+if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
+ lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"
+else
+
+ lt_cv_dlopen="dyld"
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+
+fi
+
+ ;;
+
+ *)
+ ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load"
+if test "x$ac_cv_func_shl_load" = xyes; then :
+ lt_cv_dlopen="shl_load"
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5
+$as_echo_n "checking for shl_load in -ldld... " >&6; }
+if ${ac_cv_lib_dld_shl_load+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char shl_load ();
+int
+main ()
+{
+return shl_load ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_dld_shl_load=yes
+else
+ ac_cv_lib_dld_shl_load=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5
+$as_echo "$ac_cv_lib_dld_shl_load" >&6; }
+if test "x$ac_cv_lib_dld_shl_load" = xyes; then :
+ lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"
+else
+ ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen"
+if test "x$ac_cv_func_dlopen" = xyes; then :
+ lt_cv_dlopen="dlopen"
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
+$as_echo_n "checking for dlopen in -ldl... " >&6; }
+if ${ac_cv_lib_dl_dlopen+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_dl_dlopen=yes
+else
+ ac_cv_lib_dl_dlopen=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
+$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
+if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
+ lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5
+$as_echo_n "checking for dlopen in -lsvld... " >&6; }
+if ${ac_cv_lib_svld_dlopen+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsvld $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_svld_dlopen=yes
+else
+ ac_cv_lib_svld_dlopen=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5
+$as_echo "$ac_cv_lib_svld_dlopen" >&6; }
+if test "x$ac_cv_lib_svld_dlopen" = xyes; then :
+ lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5
+$as_echo_n "checking for dld_link in -ldld... " >&6; }
+if ${ac_cv_lib_dld_dld_link+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dld_link ();
+int
+main ()
+{
+return dld_link ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_dld_dld_link=yes
+else
+ ac_cv_lib_dld_dld_link=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5
+$as_echo "$ac_cv_lib_dld_dld_link" >&6; }
+if test "x$ac_cv_lib_dld_dld_link" = xyes; then :
+ lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+ ;;
+ esac
+
+ if test "x$lt_cv_dlopen" != xno; then
+ enable_dlopen=yes
+ else
+ enable_dlopen=no
+ fi
+
+ case $lt_cv_dlopen in
+ dlopen)
+ save_CPPFLAGS="$CPPFLAGS"
+ test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+
+ save_LDFLAGS="$LDFLAGS"
+ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
+
+ save_LIBS="$LIBS"
+ LIBS="$lt_cv_dlopen_libs $LIBS"
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5
+$as_echo_n "checking whether a program can dlopen itself... " >&6; }
+if ${lt_cv_dlopen_self+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then :
+ lt_cv_dlopen_self=cross
+else
+ lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+ lt_status=$lt_dlunknown
+ cat > conftest.$ac_ext <<_LT_EOF
+#line __oline__ "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+# define LT_DLGLOBAL RTLD_GLOBAL
+#else
+# ifdef DL_GLOBAL
+# define LT_DLGLOBAL DL_GLOBAL
+# else
+# define LT_DLGLOBAL 0
+# endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+ find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+# ifdef RTLD_LAZY
+# define LT_DLLAZY_OR_NOW RTLD_LAZY
+# else
+# ifdef DL_LAZY
+# define LT_DLLAZY_OR_NOW DL_LAZY
+# else
+# ifdef RTLD_NOW
+# define LT_DLLAZY_OR_NOW RTLD_NOW
+# else
+# ifdef DL_NOW
+# define LT_DLLAZY_OR_NOW DL_NOW
+# else
+# define LT_DLLAZY_OR_NOW 0
+# endif
+# endif
+# endif
+# endif
+#endif
+
+void fnord() { int i=42;}
+int main ()
+{
+ void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+ int status = $lt_dlunknown;
+
+ if (self)
+ {
+ if (dlsym (self,"fnord")) status = $lt_dlno_uscore;
+ else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+ /* dlclose (self); */
+ }
+ else
+ puts (dlerror ());
+
+ return status;
+}
+_LT_EOF
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then
+ (./conftest; exit; ) >&5 2>/dev/null
+ lt_status=$?
+ case x$lt_status in
+ x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;;
+ x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;;
+ x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;;
+ esac
+ else :
+ # compilation failed
+ lt_cv_dlopen_self=no
+ fi
+fi
+rm -fr conftest*
+
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5
+$as_echo "$lt_cv_dlopen_self" >&6; }
+
+ if test "x$lt_cv_dlopen_self" = xyes; then
+ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5
+$as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; }
+if ${lt_cv_dlopen_self_static+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then :
+ lt_cv_dlopen_self_static=cross
+else
+ lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+ lt_status=$lt_dlunknown
+ cat > conftest.$ac_ext <<_LT_EOF
+#line __oline__ "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+# define LT_DLGLOBAL RTLD_GLOBAL
+#else
+# ifdef DL_GLOBAL
+# define LT_DLGLOBAL DL_GLOBAL
+# else
+# define LT_DLGLOBAL 0
+# endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+ find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+# ifdef RTLD_LAZY
+# define LT_DLLAZY_OR_NOW RTLD_LAZY
+# else
+# ifdef DL_LAZY
+# define LT_DLLAZY_OR_NOW DL_LAZY
+# else
+# ifdef RTLD_NOW
+# define LT_DLLAZY_OR_NOW RTLD_NOW
+# else
+# ifdef DL_NOW
+# define LT_DLLAZY_OR_NOW DL_NOW
+# else
+# define LT_DLLAZY_OR_NOW 0
+# endif
+# endif
+# endif
+# endif
+#endif
+
+void fnord() { int i=42;}
+int main ()
+{
+ void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+ int status = $lt_dlunknown;
+
+ if (self)
+ {
+ if (dlsym (self,"fnord")) status = $lt_dlno_uscore;
+ else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+ /* dlclose (self); */
+ }
+ else
+ puts (dlerror ());
+
+ return status;
+}
+_LT_EOF
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then
+ (./conftest; exit; ) >&5 2>/dev/null
+ lt_status=$?
+ case x$lt_status in
+ x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;;
+ x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;;
+ x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;;
+ esac
+ else :
+ # compilation failed
+ lt_cv_dlopen_self_static=no
+ fi
+fi
+rm -fr conftest*
+
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5
+$as_echo "$lt_cv_dlopen_self_static" >&6; }
+ fi
+
+ CPPFLAGS="$save_CPPFLAGS"
+ LDFLAGS="$save_LDFLAGS"
+ LIBS="$save_LIBS"
+ ;;
+ esac
+
+ case $lt_cv_dlopen_self in
+ yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+ *) enable_dlopen_self=unknown ;;
+ esac
+
+ case $lt_cv_dlopen_self_static in
+ yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+ *) enable_dlopen_self_static=unknown ;;
+ esac
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+striplib=
+old_striplib=
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5
+$as_echo_n "checking whether stripping libraries is possible... " >&6; }
+if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then
+ test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
+ test -z "$striplib" && striplib="$STRIP --strip-unneeded"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+# FIXME - insert some real tests, host_os isn't really good enough
+ case $host_os in
+ darwin*)
+ if test -n "$STRIP" ; then
+ striplib="$STRIP -x"
+ old_striplib="$STRIP -S"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ fi
+ ;;
+ *)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ ;;
+ esac
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+ # Report which library types will actually be built
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5
+$as_echo_n "checking if libtool supports shared libraries... " >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5
+$as_echo "$can_build_shared" >&6; }
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5
+$as_echo_n "checking whether to build shared libraries... " >&6; }
+ test "$can_build_shared" = "no" && enable_shared=no
+
+ # On AIX, shared libraries and static libraries use the same namespace, and
+ # are all built from PIC.
+ case $host_os in
+ aix3*)
+ test "$enable_shared" = yes && enable_static=no
+ if test -n "$RANLIB"; then
+ archive_cmds="$archive_cmds~\$RANLIB \$lib"
+ postinstall_cmds='$RANLIB $lib'
+ fi
+ ;;
+
+ aix[4-9]*)
+ if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+ test "$enable_shared" = yes && enable_static=no
+ fi
+ ;;
+ esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5
+$as_echo "$enable_shared" >&6; }
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5
+$as_echo_n "checking whether to build static libraries... " >&6; }
+ # Make sure either enable_shared or enable_static is yes.
+ test "$enable_shared" = yes || enable_static=yes
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5
+$as_echo "$enable_static" >&6; }
+
+
+
+
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+CC="$lt_save_CC"
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ac_config_commands="$ac_config_commands libtool"
+
+
+
+
+# Only expand once:
+
+
+
+# Extract the first word of "ar", so it can be a program name with args.
+set dummy ar; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_AR+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $AR in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_AR="$AR" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_AR="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_AR" && ac_cv_path_AR="ar"
+ ;;
+esac
+fi
+AR=$ac_cv_path_AR
+if test -n "$AR"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5
+$as_echo "$AR" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+# Extract the first word of "rm", so it can be a program name with args.
+set dummy rm; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_RM+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $RM in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_RM="$RM" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_RM="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_RM" && ac_cv_path_RM="rm"
+ ;;
+esac
+fi
+RM=$ac_cv_path_RM
+if test -n "$RM"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RM" >&5
+$as_echo "$RM" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+# Extract the first word of "cp", so it can be a program name with args.
+set dummy cp; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_CP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $CP in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_CP="$CP" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_CP="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_CP" && ac_cv_path_CP="cp"
+ ;;
+esac
+fi
+CP=$ac_cv_path_CP
+if test -n "$CP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CP" >&5
+$as_echo "$CP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+# Extract the first word of "ln", so it can be a program name with args.
+set dummy ln; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_LN+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $LN in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_LN="$LN" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_LN="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_LN" && ac_cv_path_LN="ln"
+ ;;
+esac
+fi
+LN=$ac_cv_path_LN
+if test -n "$LN"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LN" >&5
+$as_echo "$LN" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+# Extract the first word of "sed", so it can be a program name with args.
+set dummy sed; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_SED+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $SED in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_SED="$SED" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_SED="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_SED" && ac_cv_path_SED="sed"
+ ;;
+esac
+fi
+SED=$ac_cv_path_SED
+if test -n "$SED"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SED" >&5
+$as_echo "$SED" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+# Extract the first word of "make", so it can be a program name with args.
+set dummy make; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_MAKE+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $MAKE in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_MAKE="$MAKE" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_MAKE="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+MAKE=$ac_cv_path_MAKE
+if test -n "$MAKE"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAKE" >&5
+$as_echo "$MAKE" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether NLS is requested" >&5
+$as_echo_n "checking whether NLS is requested... " >&6; }
+ @%:@ Check whether --enable-nls was given.
+if test "${enable_nls+set}" = set; then :
+ enableval=$enable_nls; USE_NLS=$enableval
+else
+ USE_NLS=yes
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $USE_NLS" >&5
+$as_echo "$USE_NLS" >&6; }
+
+
+
+
+
+
+# Prepare PATH_SEPARATOR.
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+# Find out how to test for executable files. Don't use a zero-byte file,
+# as systems may use methods other than mode bits to determine executability.
+cat >conf$$.file <<_ASEOF
+#! /bin/sh
+exit 0
+_ASEOF
+chmod +x conf$$.file
+if test -x conf$$.file >/dev/null 2>&1; then
+ ac_executable_p="test -x"
+else
+ ac_executable_p="test -f"
+fi
+rm -f conf$$.file
+
+# Extract the first word of "msgfmt", so it can be a program name with args.
+set dummy msgfmt; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_MSGFMT+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case "$MSGFMT" in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_MSGFMT="$MSGFMT" # Let the user override the test with a path.
+ ;;
+ *)
+ ac_save_IFS="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH; do
+ IFS="$ac_save_IFS"
+ test -z "$ac_dir" && ac_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $ac_executable_p "$ac_dir/$ac_word$ac_exec_ext"; then
+ echo "$as_me: trying $ac_dir/$ac_word..." >&5
+ if $ac_dir/$ac_word --statistics /dev/null >&5 2>&1 &&
+ (if $ac_dir/$ac_word --statistics /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi); then
+ ac_cv_path_MSGFMT="$ac_dir/$ac_word$ac_exec_ext"
+ break 2
+ fi
+ fi
+ done
+ done
+ IFS="$ac_save_IFS"
+ test -z "$ac_cv_path_MSGFMT" && ac_cv_path_MSGFMT=":"
+ ;;
+esac
+fi
+MSGFMT="$ac_cv_path_MSGFMT"
+if test "$MSGFMT" != ":"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MSGFMT" >&5
+$as_echo "$MSGFMT" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ # Extract the first word of "gmsgfmt", so it can be a program name with args.
+set dummy gmsgfmt; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_GMSGFMT+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $GMSGFMT in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_GMSGFMT="$GMSGFMT" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_GMSGFMT="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_GMSGFMT" && ac_cv_path_GMSGFMT="$MSGFMT"
+ ;;
+esac
+fi
+GMSGFMT=$ac_cv_path_GMSGFMT
+if test -n "$GMSGFMT"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $GMSGFMT" >&5
+$as_echo "$GMSGFMT" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+ case `$MSGFMT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in
+ '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) MSGFMT_015=: ;;
+ *) MSGFMT_015=$MSGFMT ;;
+ esac
+
+ case `$GMSGFMT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in
+ '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) GMSGFMT_015=: ;;
+ *) GMSGFMT_015=$GMSGFMT ;;
+ esac
+
+
+
+# Prepare PATH_SEPARATOR.
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+# Find out how to test for executable files. Don't use a zero-byte file,
+# as systems may use methods other than mode bits to determine executability.
+cat >conf$$.file <<_ASEOF
+#! /bin/sh
+exit 0
+_ASEOF
+chmod +x conf$$.file
+if test -x conf$$.file >/dev/null 2>&1; then
+ ac_executable_p="test -x"
+else
+ ac_executable_p="test -f"
+fi
+rm -f conf$$.file
+
+# Extract the first word of "xgettext", so it can be a program name with args.
+set dummy xgettext; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_XGETTEXT+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case "$XGETTEXT" in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_XGETTEXT="$XGETTEXT" # Let the user override the test with a path.
+ ;;
+ *)
+ ac_save_IFS="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH; do
+ IFS="$ac_save_IFS"
+ test -z "$ac_dir" && ac_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $ac_executable_p "$ac_dir/$ac_word$ac_exec_ext"; then
+ echo "$as_me: trying $ac_dir/$ac_word..." >&5
+ if $ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= /dev/null >&5 2>&1 &&
+ (if $ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi); then
+ ac_cv_path_XGETTEXT="$ac_dir/$ac_word$ac_exec_ext"
+ break 2
+ fi
+ fi
+ done
+ done
+ IFS="$ac_save_IFS"
+ test -z "$ac_cv_path_XGETTEXT" && ac_cv_path_XGETTEXT=":"
+ ;;
+esac
+fi
+XGETTEXT="$ac_cv_path_XGETTEXT"
+if test "$XGETTEXT" != ":"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $XGETTEXT" >&5
+$as_echo "$XGETTEXT" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ rm -f messages.po
+
+ case `$XGETTEXT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in
+ '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) XGETTEXT_015=: ;;
+ *) XGETTEXT_015=$XGETTEXT ;;
+ esac
+
+
+
+# Prepare PATH_SEPARATOR.
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+# Find out how to test for executable files. Don't use a zero-byte file,
+# as systems may use methods other than mode bits to determine executability.
+cat >conf$$.file <<_ASEOF
+#! /bin/sh
+exit 0
+_ASEOF
+chmod +x conf$$.file
+if test -x conf$$.file >/dev/null 2>&1; then
+ ac_executable_p="test -x"
+else
+ ac_executable_p="test -f"
+fi
+rm -f conf$$.file
+
+# Extract the first word of "msgmerge", so it can be a program name with args.
+set dummy msgmerge; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_MSGMERGE+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case "$MSGMERGE" in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_MSGMERGE="$MSGMERGE" # Let the user override the test with a path.
+ ;;
+ *)
+ ac_save_IFS="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH; do
+ IFS="$ac_save_IFS"
+ test -z "$ac_dir" && ac_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $ac_executable_p "$ac_dir/$ac_word$ac_exec_ext"; then
+ echo "$as_me: trying $ac_dir/$ac_word..." >&5
+ if $ac_dir/$ac_word --update -q /dev/null /dev/null >&5 2>&1; then
+ ac_cv_path_MSGMERGE="$ac_dir/$ac_word$ac_exec_ext"
+ break 2
+ fi
+ fi
+ done
+ done
+ IFS="$ac_save_IFS"
+ test -z "$ac_cv_path_MSGMERGE" && ac_cv_path_MSGMERGE=":"
+ ;;
+esac
+fi
+MSGMERGE="$ac_cv_path_MSGMERGE"
+if test "$MSGMERGE" != ":"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MSGMERGE" >&5
+$as_echo "$MSGMERGE" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$localedir" || localedir='${datadir}/locale'
+
+
+ ac_config_commands="$ac_config_commands po-directories"
+
+
+
+ if test "X$prefix" = "XNONE"; then
+ acl_final_prefix="$ac_default_prefix"
+ else
+ acl_final_prefix="$prefix"
+ fi
+ if test "X$exec_prefix" = "XNONE"; then
+ acl_final_exec_prefix='${prefix}'
+ else
+ acl_final_exec_prefix="$exec_prefix"
+ fi
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ eval acl_final_exec_prefix=\"$acl_final_exec_prefix\"
+ prefix="$acl_save_prefix"
+
+
+@%:@ Check whether --with-gnu-ld was given.
+if test "${with_gnu_ld+set}" = set; then :
+ withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes
+else
+ with_gnu_ld=no
+fi
+
+# Prepare PATH_SEPARATOR.
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+ac_prog=ld
+if test "$GCC" = yes; then
+ # Check if gcc -print-prog-name=ld gives a path.
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by GCC" >&5
+$as_echo_n "checking for ld used by GCC... " >&6; }
+ case $host in
+ *-*-mingw*)
+ # gcc leaves a trailing carriage return which upsets mingw
+ ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+ *)
+ ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+ esac
+ case $ac_prog in
+ # Accept absolute paths.
+ [\\/]* | [A-Za-z]:[\\/]*)
+ re_direlt='/[^/][^/]*/\.\./'
+ # Canonicalize the path of ld
+ ac_prog=`echo $ac_prog| sed 's%\\\\%/%g'`
+ while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do
+ ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"`
+ done
+ test -z "$LD" && LD="$ac_prog"
+ ;;
+ "")
+ # If it fails, then pretend we aren't using GCC.
+ ac_prog=ld
+ ;;
+ *)
+ # If it is relative, then search for the first ld in PATH.
+ with_gnu_ld=unknown
+ ;;
+ esac
+elif test "$with_gnu_ld" = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5
+$as_echo_n "checking for GNU ld... " >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5
+$as_echo_n "checking for non-GNU ld... " >&6; }
+fi
+if ${acl_cv_path_LD+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$LD"; then
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR-:}"
+ for ac_dir in $PATH; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+ acl_cv_path_LD="$ac_dir/$ac_prog"
+ # Check to see if the program is GNU ld. I'd rather use --version,
+ # but apparently some GNU ld's only accept -v.
+ # Break only if it was the GNU/non-GNU ld that we prefer.
+ case `"$acl_cv_path_LD" -v 2>&1 < /dev/null` in
+ *GNU* | *'with BFD'*)
+ test "$with_gnu_ld" != no && break ;;
+ *)
+ test "$with_gnu_ld" != yes && break ;;
+ esac
+ fi
+ done
+ IFS="$ac_save_ifs"
+else
+ acl_cv_path_LD="$LD" # Let the user override the test with a path.
+fi
+fi
+
+LD="$acl_cv_path_LD"
+if test -n "$LD"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LD" >&5
+$as_echo "$LD" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5
+$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; }
+if ${acl_cv_prog_gnu_ld+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ # I'd rather use --version here, but apparently some GNU ld's only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+ acl_cv_prog_gnu_ld=yes ;;
+*)
+ acl_cv_prog_gnu_ld=no ;;
+esac
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $acl_cv_prog_gnu_ld" >&5
+$as_echo "$acl_cv_prog_gnu_ld" >&6; }
+with_gnu_ld=$acl_cv_prog_gnu_ld
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shared library run path origin" >&5
+$as_echo_n "checking for shared library run path origin... " >&6; }
+if ${acl_cv_rpath+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ CC="$CC" GCC="$GCC" LDFLAGS="$LDFLAGS" LD="$LD" with_gnu_ld="$with_gnu_ld" \
+ ${CONFIG_SHELL-/bin/sh} "$ac_aux_dir/config.rpath" "$host" > conftest.sh
+ . ./conftest.sh
+ rm -f ./conftest.sh
+ acl_cv_rpath=done
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $acl_cv_rpath" >&5
+$as_echo "$acl_cv_rpath" >&6; }
+ wl="$acl_cv_wl"
+ libext="$acl_cv_libext"
+ shlibext="$acl_cv_shlibext"
+ hardcode_libdir_flag_spec="$acl_cv_hardcode_libdir_flag_spec"
+ hardcode_libdir_separator="$acl_cv_hardcode_libdir_separator"
+ hardcode_direct="$acl_cv_hardcode_direct"
+ hardcode_minus_L="$acl_cv_hardcode_minus_L"
+ @%:@ Check whether --enable-rpath was given.
+if test "${enable_rpath+set}" = set; then :
+ enableval=$enable_rpath; :
+else
+ enable_rpath=yes
+fi
+
+
+
+ acl_libdirstem=lib
+ searchpath=`(LC_ALL=C $CC -print-search-dirs) 2>/dev/null | sed -n -e 's,^libraries: ,,p' | sed -e 's,^=,,'`
+ if test -n "$searchpath"; then
+ acl_save_IFS="${IFS= }"; IFS=":"
+ for searchdir in $searchpath; do
+ if test -d "$searchdir"; then
+ case "$searchdir" in
+ */lib64/ | */lib64 ) acl_libdirstem=lib64 ;;
+ *) searchdir=`cd "$searchdir" && pwd`
+ case "$searchdir" in
+ */lib64 ) acl_libdirstem=lib64 ;;
+ esac ;;
+ esac
+ fi
+ done
+ IFS="$acl_save_IFS"
+ fi
+
+
+
+
+
+
+
+
+ use_additional=yes
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+
+@%:@ Check whether --with-libiconv-prefix was given.
+if test "${with_libiconv_prefix+set}" = set; then :
+ withval=$with_libiconv_prefix;
+ if test "X$withval" = "Xno"; then
+ use_additional=no
+ else
+ if test "X$withval" = "X"; then
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ else
+ additional_includedir="$withval/include"
+ additional_libdir="$withval/$acl_libdirstem"
+ fi
+ fi
+
+fi
+
+ LIBICONV=
+ LTLIBICONV=
+ INCICONV=
+ rpathdirs=
+ ltrpathdirs=
+ names_already_handled=
+ names_next_round='iconv '
+ while test -n "$names_next_round"; do
+ names_this_round="$names_next_round"
+ names_next_round=
+ for name in $names_this_round; do
+ already_handled=
+ for n in $names_already_handled; do
+ if test "$n" = "$name"; then
+ already_handled=yes
+ break
+ fi
+ done
+ if test -z "$already_handled"; then
+ names_already_handled="$names_already_handled $name"
+ uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'`
+ eval value=\"\$HAVE_LIB$uppername\"
+ if test -n "$value"; then
+ if test "$value" = yes; then
+ eval value=\"\$LIB$uppername\"
+ test -z "$value" || LIBICONV="${LIBICONV}${LIBICONV:+ }$value"
+ eval value=\"\$LTLIB$uppername\"
+ test -z "$value" || LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }$value"
+ else
+ :
+ fi
+ else
+ found_dir=
+ found_la=
+ found_so=
+ found_a=
+ if test $use_additional = yes; then
+ if test -n "$shlibext" \
+ && { test -f "$additional_libdir/lib$name.$shlibext" \
+ || { test "$shlibext" = dll \
+ && test -f "$additional_libdir/lib$name.dll.a"; }; }; then
+ found_dir="$additional_libdir"
+ if test -f "$additional_libdir/lib$name.$shlibext"; then
+ found_so="$additional_libdir/lib$name.$shlibext"
+ else
+ found_so="$additional_libdir/lib$name.dll.a"
+ fi
+ if test -f "$additional_libdir/lib$name.la"; then
+ found_la="$additional_libdir/lib$name.la"
+ fi
+ else
+ if test -f "$additional_libdir/lib$name.$libext"; then
+ found_dir="$additional_libdir"
+ found_a="$additional_libdir/lib$name.$libext"
+ if test -f "$additional_libdir/lib$name.la"; then
+ found_la="$additional_libdir/lib$name.la"
+ fi
+ fi
+ fi
+ fi
+ if test "X$found_dir" = "X"; then
+ for x in $LDFLAGS $LTLIBICONV; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ case "$x" in
+ -L*)
+ dir=`echo "X$x" | sed -e 's/^X-L//'`
+ if test -n "$shlibext" \
+ && { test -f "$dir/lib$name.$shlibext" \
+ || { test "$shlibext" = dll \
+ && test -f "$dir/lib$name.dll.a"; }; }; then
+ found_dir="$dir"
+ if test -f "$dir/lib$name.$shlibext"; then
+ found_so="$dir/lib$name.$shlibext"
+ else
+ found_so="$dir/lib$name.dll.a"
+ fi
+ if test -f "$dir/lib$name.la"; then
+ found_la="$dir/lib$name.la"
+ fi
+ else
+ if test -f "$dir/lib$name.$libext"; then
+ found_dir="$dir"
+ found_a="$dir/lib$name.$libext"
+ if test -f "$dir/lib$name.la"; then
+ found_la="$dir/lib$name.la"
+ fi
+ fi
+ fi
+ ;;
+ esac
+ if test "X$found_dir" != "X"; then
+ break
+ fi
+ done
+ fi
+ if test "X$found_dir" != "X"; then
+ LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-L$found_dir -l$name"
+ if test "X$found_so" != "X"; then
+ if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/$acl_libdirstem"; then
+ LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so"
+ else
+ haveit=
+ for x in $ltrpathdirs; do
+ if test "X$x" = "X$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ ltrpathdirs="$ltrpathdirs $found_dir"
+ fi
+ if test "$hardcode_direct" = yes; then
+ LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so"
+ else
+ if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then
+ LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so"
+ haveit=
+ for x in $rpathdirs; do
+ if test "X$x" = "X$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ rpathdirs="$rpathdirs $found_dir"
+ fi
+ else
+ haveit=
+ for x in $LDFLAGS $LIBICONV; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-L$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ LIBICONV="${LIBICONV}${LIBICONV:+ }-L$found_dir"
+ fi
+ if test "$hardcode_minus_L" != no; then
+ LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so"
+ else
+ LIBICONV="${LIBICONV}${LIBICONV:+ }-l$name"
+ fi
+ fi
+ fi
+ fi
+ else
+ if test "X$found_a" != "X"; then
+ LIBICONV="${LIBICONV}${LIBICONV:+ }$found_a"
+ else
+ LIBICONV="${LIBICONV}${LIBICONV:+ }-L$found_dir -l$name"
+ fi
+ fi
+ additional_includedir=
+ case "$found_dir" in
+ */$acl_libdirstem | */$acl_libdirstem/)
+ basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem/"'*$,,'`
+ additional_includedir="$basedir/include"
+ ;;
+ esac
+ if test "X$additional_includedir" != "X"; then
+ if test "X$additional_includedir" != "X/usr/include"; then
+ haveit=
+ if test "X$additional_includedir" = "X/usr/local/include"; then
+ if test -n "$GCC"; then
+ case $host_os in
+ linux* | gnu* | k*bsd*-gnu) haveit=yes;;
+ esac
+ fi
+ fi
+ if test -z "$haveit"; then
+ for x in $CPPFLAGS $INCICONV; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-I$additional_includedir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_includedir"; then
+ INCICONV="${INCICONV}${INCICONV:+ }-I$additional_includedir"
+ fi
+ fi
+ fi
+ fi
+ fi
+ if test -n "$found_la"; then
+ save_libdir="$libdir"
+ case "$found_la" in
+ */* | *\\*) . "$found_la" ;;
+ *) . "./$found_la" ;;
+ esac
+ libdir="$save_libdir"
+ for dep in $dependency_libs; do
+ case "$dep" in
+ -L*)
+ additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'`
+ if test "X$additional_libdir" != "X/usr/$acl_libdirstem"; then
+ haveit=
+ if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem"; then
+ if test -n "$GCC"; then
+ case $host_os in
+ linux* | gnu* | k*bsd*-gnu) haveit=yes;;
+ esac
+ fi
+ fi
+ if test -z "$haveit"; then
+ haveit=
+ for x in $LDFLAGS $LIBICONV; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-L$additional_libdir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_libdir"; then
+ LIBICONV="${LIBICONV}${LIBICONV:+ }-L$additional_libdir"
+ fi
+ fi
+ haveit=
+ for x in $LDFLAGS $LTLIBICONV; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-L$additional_libdir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_libdir"; then
+ LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-L$additional_libdir"
+ fi
+ fi
+ fi
+ fi
+ ;;
+ -R*)
+ dir=`echo "X$dep" | sed -e 's/^X-R//'`
+ if test "$enable_rpath" != no; then
+ haveit=
+ for x in $rpathdirs; do
+ if test "X$x" = "X$dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ rpathdirs="$rpathdirs $dir"
+ fi
+ haveit=
+ for x in $ltrpathdirs; do
+ if test "X$x" = "X$dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ ltrpathdirs="$ltrpathdirs $dir"
+ fi
+ fi
+ ;;
+ -l*)
+ names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'`
+ ;;
+ *.la)
+ names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'`
+ ;;
+ *)
+ LIBICONV="${LIBICONV}${LIBICONV:+ }$dep"
+ LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }$dep"
+ ;;
+ esac
+ done
+ fi
+ else
+ LIBICONV="${LIBICONV}${LIBICONV:+ }-l$name"
+ LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-l$name"
+ fi
+ fi
+ fi
+ done
+ done
+ if test "X$rpathdirs" != "X"; then
+ if test -n "$hardcode_libdir_separator"; then
+ alldirs=
+ for found_dir in $rpathdirs; do
+ alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$found_dir"
+ done
+ acl_save_libdir="$libdir"
+ libdir="$alldirs"
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ libdir="$acl_save_libdir"
+ LIBICONV="${LIBICONV}${LIBICONV:+ }$flag"
+ else
+ for found_dir in $rpathdirs; do
+ acl_save_libdir="$libdir"
+ libdir="$found_dir"
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ libdir="$acl_save_libdir"
+ LIBICONV="${LIBICONV}${LIBICONV:+ }$flag"
+ done
+ fi
+ fi
+ if test "X$ltrpathdirs" != "X"; then
+ for found_dir in $ltrpathdirs; do
+ LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-R$found_dir"
+ done
+ fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for CFPreferencesCopyAppValue" >&5
+$as_echo_n "checking for CFPreferencesCopyAppValue... " >&6; }
+if ${gt_cv_func_CFPreferencesCopyAppValue+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ gt_save_LIBS="$LIBS"
+ LIBS="$LIBS -Wl,-framework -Wl,CoreFoundation"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <CoreFoundation/CFPreferences.h>
+int
+main ()
+{
+CFPreferencesCopyAppValue(NULL, NULL)
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ gt_cv_func_CFPreferencesCopyAppValue=yes
+else
+ gt_cv_func_CFPreferencesCopyAppValue=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LIBS="$gt_save_LIBS"
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gt_cv_func_CFPreferencesCopyAppValue" >&5
+$as_echo "$gt_cv_func_CFPreferencesCopyAppValue" >&6; }
+ if test $gt_cv_func_CFPreferencesCopyAppValue = yes; then
+
+$as_echo "@%:@define HAVE_CFPREFERENCESCOPYAPPVALUE 1" >>confdefs.h
+
+ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for CFLocaleCopyCurrent" >&5
+$as_echo_n "checking for CFLocaleCopyCurrent... " >&6; }
+if ${gt_cv_func_CFLocaleCopyCurrent+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ gt_save_LIBS="$LIBS"
+ LIBS="$LIBS -Wl,-framework -Wl,CoreFoundation"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <CoreFoundation/CFLocale.h>
+int
+main ()
+{
+CFLocaleCopyCurrent();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ gt_cv_func_CFLocaleCopyCurrent=yes
+else
+ gt_cv_func_CFLocaleCopyCurrent=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LIBS="$gt_save_LIBS"
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gt_cv_func_CFLocaleCopyCurrent" >&5
+$as_echo "$gt_cv_func_CFLocaleCopyCurrent" >&6; }
+ if test $gt_cv_func_CFLocaleCopyCurrent = yes; then
+
+$as_echo "@%:@define HAVE_CFLOCALECOPYCURRENT 1" >>confdefs.h
+
+ fi
+ INTL_MACOSX_LIBS=
+ if test $gt_cv_func_CFPreferencesCopyAppValue = yes || test $gt_cv_func_CFLocaleCopyCurrent = yes; then
+ INTL_MACOSX_LIBS="-Wl,-framework -Wl,CoreFoundation"
+ fi
+
+
+
+
+
+
+ LIBINTL=
+ LTLIBINTL=
+ POSUB=
+
+ case " $gt_needs " in
+ *" need-formatstring-macros "*) gt_api_version=3 ;;
+ *" need-ngettext "*) gt_api_version=2 ;;
+ *) gt_api_version=1 ;;
+ esac
+ gt_func_gnugettext_libc="gt_cv_func_gnugettext${gt_api_version}_libc"
+ gt_func_gnugettext_libintl="gt_cv_func_gnugettext${gt_api_version}_libintl"
+
+ if test "$USE_NLS" = "yes"; then
+ gt_use_preinstalled_gnugettext=no
+
+
+ if test $gt_api_version -ge 3; then
+ gt_revision_test_code='
+#ifndef __GNU_GETTEXT_SUPPORTED_REVISION
+#define __GNU_GETTEXT_SUPPORTED_REVISION(major) ((major) == 0 ? 0 : -1)
+#endif
+typedef int array [2 * (__GNU_GETTEXT_SUPPORTED_REVISION(0) >= 1) - 1];
+'
+ else
+ gt_revision_test_code=
+ fi
+ if test $gt_api_version -ge 2; then
+ gt_expression_test_code=' + * ngettext ("", "", 0)'
+ else
+ gt_expression_test_code=
+ fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU gettext in libc" >&5
+$as_echo_n "checking for GNU gettext in libc... " >&6; }
+if eval \${$gt_func_gnugettext_libc+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <libintl.h>
+$gt_revision_test_code
+extern int _nl_msg_cat_cntr;
+extern int *_nl_domain_bindings;
+int
+main ()
+{
+bindtextdomain ("", "");
+return * gettext ("")$gt_expression_test_code + _nl_msg_cat_cntr + *_nl_domain_bindings
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ eval "$gt_func_gnugettext_libc=yes"
+else
+ eval "$gt_func_gnugettext_libc=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+eval ac_res=\$$gt_func_gnugettext_libc
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+ if { eval "gt_val=\$$gt_func_gnugettext_libc"; test "$gt_val" != "yes"; }; then
+
+
+
+
+
+ am_save_CPPFLAGS="$CPPFLAGS"
+
+ for element in $INCICONV; do
+ haveit=
+ for x in $CPPFLAGS; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X$element"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }$element"
+ fi
+ done
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for iconv" >&5
+$as_echo_n "checking for iconv... " >&6; }
+if ${am_cv_func_iconv+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ am_cv_func_iconv="no, consider installing GNU libiconv"
+ am_cv_lib_iconv=no
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <iconv.h>
+int
+main ()
+{
+iconv_t cd = iconv_open("","");
+ iconv(cd,NULL,NULL,NULL,NULL);
+ iconv_close(cd);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ am_cv_func_iconv=yes
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ if test "$am_cv_func_iconv" != yes; then
+ am_save_LIBS="$LIBS"
+ LIBS="$LIBS $LIBICONV"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <iconv.h>
+int
+main ()
+{
+iconv_t cd = iconv_open("","");
+ iconv(cd,NULL,NULL,NULL,NULL);
+ iconv_close(cd);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ am_cv_lib_iconv=yes
+ am_cv_func_iconv=yes
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LIBS="$am_save_LIBS"
+ fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_func_iconv" >&5
+$as_echo "$am_cv_func_iconv" >&6; }
+ if test "$am_cv_func_iconv" = yes; then
+
+$as_echo "@%:@define HAVE_ICONV 1" >>confdefs.h
+
+ fi
+ if test "$am_cv_lib_iconv" = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link with libiconv" >&5
+$as_echo_n "checking how to link with libiconv... " >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIBICONV" >&5
+$as_echo "$LIBICONV" >&6; }
+ else
+ CPPFLAGS="$am_save_CPPFLAGS"
+ LIBICONV=
+ LTLIBICONV=
+ fi
+
+
+
+
+
+
+
+ use_additional=yes
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+
+@%:@ Check whether --with-libintl-prefix was given.
+if test "${with_libintl_prefix+set}" = set; then :
+ withval=$with_libintl_prefix;
+ if test "X$withval" = "Xno"; then
+ use_additional=no
+ else
+ if test "X$withval" = "X"; then
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ else
+ additional_includedir="$withval/include"
+ additional_libdir="$withval/$acl_libdirstem"
+ fi
+ fi
+
+fi
+
+ LIBINTL=
+ LTLIBINTL=
+ INCINTL=
+ rpathdirs=
+ ltrpathdirs=
+ names_already_handled=
+ names_next_round='intl '
+ while test -n "$names_next_round"; do
+ names_this_round="$names_next_round"
+ names_next_round=
+ for name in $names_this_round; do
+ already_handled=
+ for n in $names_already_handled; do
+ if test "$n" = "$name"; then
+ already_handled=yes
+ break
+ fi
+ done
+ if test -z "$already_handled"; then
+ names_already_handled="$names_already_handled $name"
+ uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'`
+ eval value=\"\$HAVE_LIB$uppername\"
+ if test -n "$value"; then
+ if test "$value" = yes; then
+ eval value=\"\$LIB$uppername\"
+ test -z "$value" || LIBINTL="${LIBINTL}${LIBINTL:+ }$value"
+ eval value=\"\$LTLIB$uppername\"
+ test -z "$value" || LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }$value"
+ else
+ :
+ fi
+ else
+ found_dir=
+ found_la=
+ found_so=
+ found_a=
+ if test $use_additional = yes; then
+ if test -n "$shlibext" \
+ && { test -f "$additional_libdir/lib$name.$shlibext" \
+ || { test "$shlibext" = dll \
+ && test -f "$additional_libdir/lib$name.dll.a"; }; }; then
+ found_dir="$additional_libdir"
+ if test -f "$additional_libdir/lib$name.$shlibext"; then
+ found_so="$additional_libdir/lib$name.$shlibext"
+ else
+ found_so="$additional_libdir/lib$name.dll.a"
+ fi
+ if test -f "$additional_libdir/lib$name.la"; then
+ found_la="$additional_libdir/lib$name.la"
+ fi
+ else
+ if test -f "$additional_libdir/lib$name.$libext"; then
+ found_dir="$additional_libdir"
+ found_a="$additional_libdir/lib$name.$libext"
+ if test -f "$additional_libdir/lib$name.la"; then
+ found_la="$additional_libdir/lib$name.la"
+ fi
+ fi
+ fi
+ fi
+ if test "X$found_dir" = "X"; then
+ for x in $LDFLAGS $LTLIBINTL; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ case "$x" in
+ -L*)
+ dir=`echo "X$x" | sed -e 's/^X-L//'`
+ if test -n "$shlibext" \
+ && { test -f "$dir/lib$name.$shlibext" \
+ || { test "$shlibext" = dll \
+ && test -f "$dir/lib$name.dll.a"; }; }; then
+ found_dir="$dir"
+ if test -f "$dir/lib$name.$shlibext"; then
+ found_so="$dir/lib$name.$shlibext"
+ else
+ found_so="$dir/lib$name.dll.a"
+ fi
+ if test -f "$dir/lib$name.la"; then
+ found_la="$dir/lib$name.la"
+ fi
+ else
+ if test -f "$dir/lib$name.$libext"; then
+ found_dir="$dir"
+ found_a="$dir/lib$name.$libext"
+ if test -f "$dir/lib$name.la"; then
+ found_la="$dir/lib$name.la"
+ fi
+ fi
+ fi
+ ;;
+ esac
+ if test "X$found_dir" != "X"; then
+ break
+ fi
+ done
+ fi
+ if test "X$found_dir" != "X"; then
+ LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }-L$found_dir -l$name"
+ if test "X$found_so" != "X"; then
+ if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/$acl_libdirstem"; then
+ LIBINTL="${LIBINTL}${LIBINTL:+ }$found_so"
+ else
+ haveit=
+ for x in $ltrpathdirs; do
+ if test "X$x" = "X$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ ltrpathdirs="$ltrpathdirs $found_dir"
+ fi
+ if test "$hardcode_direct" = yes; then
+ LIBINTL="${LIBINTL}${LIBINTL:+ }$found_so"
+ else
+ if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then
+ LIBINTL="${LIBINTL}${LIBINTL:+ }$found_so"
+ haveit=
+ for x in $rpathdirs; do
+ if test "X$x" = "X$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ rpathdirs="$rpathdirs $found_dir"
+ fi
+ else
+ haveit=
+ for x in $LDFLAGS $LIBINTL; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-L$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ LIBINTL="${LIBINTL}${LIBINTL:+ }-L$found_dir"
+ fi
+ if test "$hardcode_minus_L" != no; then
+ LIBINTL="${LIBINTL}${LIBINTL:+ }$found_so"
+ else
+ LIBINTL="${LIBINTL}${LIBINTL:+ }-l$name"
+ fi
+ fi
+ fi
+ fi
+ else
+ if test "X$found_a" != "X"; then
+ LIBINTL="${LIBINTL}${LIBINTL:+ }$found_a"
+ else
+ LIBINTL="${LIBINTL}${LIBINTL:+ }-L$found_dir -l$name"
+ fi
+ fi
+ additional_includedir=
+ case "$found_dir" in
+ */$acl_libdirstem | */$acl_libdirstem/)
+ basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem/"'*$,,'`
+ additional_includedir="$basedir/include"
+ ;;
+ esac
+ if test "X$additional_includedir" != "X"; then
+ if test "X$additional_includedir" != "X/usr/include"; then
+ haveit=
+ if test "X$additional_includedir" = "X/usr/local/include"; then
+ if test -n "$GCC"; then
+ case $host_os in
+ linux* | gnu* | k*bsd*-gnu) haveit=yes;;
+ esac
+ fi
+ fi
+ if test -z "$haveit"; then
+ for x in $CPPFLAGS $INCINTL; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-I$additional_includedir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_includedir"; then
+ INCINTL="${INCINTL}${INCINTL:+ }-I$additional_includedir"
+ fi
+ fi
+ fi
+ fi
+ fi
+ if test -n "$found_la"; then
+ save_libdir="$libdir"
+ case "$found_la" in
+ */* | *\\*) . "$found_la" ;;
+ *) . "./$found_la" ;;
+ esac
+ libdir="$save_libdir"
+ for dep in $dependency_libs; do
+ case "$dep" in
+ -L*)
+ additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'`
+ if test "X$additional_libdir" != "X/usr/$acl_libdirstem"; then
+ haveit=
+ if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem"; then
+ if test -n "$GCC"; then
+ case $host_os in
+ linux* | gnu* | k*bsd*-gnu) haveit=yes;;
+ esac
+ fi
+ fi
+ if test -z "$haveit"; then
+ haveit=
+ for x in $LDFLAGS $LIBINTL; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-L$additional_libdir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_libdir"; then
+ LIBINTL="${LIBINTL}${LIBINTL:+ }-L$additional_libdir"
+ fi
+ fi
+ haveit=
+ for x in $LDFLAGS $LTLIBINTL; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-L$additional_libdir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_libdir"; then
+ LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }-L$additional_libdir"
+ fi
+ fi
+ fi
+ fi
+ ;;
+ -R*)
+ dir=`echo "X$dep" | sed -e 's/^X-R//'`
+ if test "$enable_rpath" != no; then
+ haveit=
+ for x in $rpathdirs; do
+ if test "X$x" = "X$dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ rpathdirs="$rpathdirs $dir"
+ fi
+ haveit=
+ for x in $ltrpathdirs; do
+ if test "X$x" = "X$dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ ltrpathdirs="$ltrpathdirs $dir"
+ fi
+ fi
+ ;;
+ -l*)
+ names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'`
+ ;;
+ *.la)
+ names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'`
+ ;;
+ *)
+ LIBINTL="${LIBINTL}${LIBINTL:+ }$dep"
+ LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }$dep"
+ ;;
+ esac
+ done
+ fi
+ else
+ LIBINTL="${LIBINTL}${LIBINTL:+ }-l$name"
+ LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }-l$name"
+ fi
+ fi
+ fi
+ done
+ done
+ if test "X$rpathdirs" != "X"; then
+ if test -n "$hardcode_libdir_separator"; then
+ alldirs=
+ for found_dir in $rpathdirs; do
+ alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$found_dir"
+ done
+ acl_save_libdir="$libdir"
+ libdir="$alldirs"
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ libdir="$acl_save_libdir"
+ LIBINTL="${LIBINTL}${LIBINTL:+ }$flag"
+ else
+ for found_dir in $rpathdirs; do
+ acl_save_libdir="$libdir"
+ libdir="$found_dir"
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ libdir="$acl_save_libdir"
+ LIBINTL="${LIBINTL}${LIBINTL:+ }$flag"
+ done
+ fi
+ fi
+ if test "X$ltrpathdirs" != "X"; then
+ for found_dir in $ltrpathdirs; do
+ LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }-R$found_dir"
+ done
+ fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU gettext in libintl" >&5
+$as_echo_n "checking for GNU gettext in libintl... " >&6; }
+if eval \${$gt_func_gnugettext_libintl+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ gt_save_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $INCINTL"
+ gt_save_LIBS="$LIBS"
+ LIBS="$LIBS $LIBINTL"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <libintl.h>
+$gt_revision_test_code
+extern int _nl_msg_cat_cntr;
+extern
+#ifdef __cplusplus
+"C"
+#endif
+const char *_nl_expand_alias (const char *);
+int
+main ()
+{
+bindtextdomain ("", "");
+return * gettext ("")$gt_expression_test_code + _nl_msg_cat_cntr + *_nl_expand_alias ("")
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ eval "$gt_func_gnugettext_libintl=yes"
+else
+ eval "$gt_func_gnugettext_libintl=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" != yes; } && test -n "$LIBICONV"; then
+ LIBS="$LIBS $LIBICONV"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <libintl.h>
+$gt_revision_test_code
+extern int _nl_msg_cat_cntr;
+extern
+#ifdef __cplusplus
+"C"
+#endif
+const char *_nl_expand_alias (const char *);
+int
+main ()
+{
+bindtextdomain ("", "");
+return * gettext ("")$gt_expression_test_code + _nl_msg_cat_cntr + *_nl_expand_alias ("")
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ LIBINTL="$LIBINTL $LIBICONV"
+ LTLIBINTL="$LTLIBINTL $LTLIBICONV"
+ eval "$gt_func_gnugettext_libintl=yes"
+
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ fi
+ CPPFLAGS="$gt_save_CPPFLAGS"
+ LIBS="$gt_save_LIBS"
+fi
+eval ac_res=\$$gt_func_gnugettext_libintl
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ fi
+
+ if { eval "gt_val=\$$gt_func_gnugettext_libc"; test "$gt_val" = "yes"; } \
+ || { { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = "yes"; } \
+ && test "$PACKAGE" != gettext-runtime \
+ && test "$PACKAGE" != gettext-tools; }; then
+ gt_use_preinstalled_gnugettext=yes
+ else
+ LIBINTL=
+ LTLIBINTL=
+ INCINTL=
+ fi
+
+
+
+ if test -n "$INTL_MACOSX_LIBS"; then
+ if test "$gt_use_preinstalled_gnugettext" = "yes" \
+ || test "$nls_cv_use_gnu_gettext" = "yes"; then
+ LIBINTL="$LIBINTL $INTL_MACOSX_LIBS"
+ LTLIBINTL="$LTLIBINTL $INTL_MACOSX_LIBS"
+ fi
+ fi
+
+ if test "$gt_use_preinstalled_gnugettext" = "yes" \
+ || test "$nls_cv_use_gnu_gettext" = "yes"; then
+
+$as_echo "@%:@define ENABLE_NLS 1" >>confdefs.h
+
+ else
+ USE_NLS=no
+ fi
+ fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use NLS" >&5
+$as_echo_n "checking whether to use NLS... " >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $USE_NLS" >&5
+$as_echo "$USE_NLS" >&6; }
+ if test "$USE_NLS" = "yes"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking where the gettext function comes from" >&5
+$as_echo_n "checking where the gettext function comes from... " >&6; }
+ if test "$gt_use_preinstalled_gnugettext" = "yes"; then
+ if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = "yes"; }; then
+ gt_source="external libintl"
+ else
+ gt_source="libc"
+ fi
+ else
+ gt_source="included intl directory"
+ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gt_source" >&5
+$as_echo "$gt_source" >&6; }
+ fi
+
+ if test "$USE_NLS" = "yes"; then
+
+ if test "$gt_use_preinstalled_gnugettext" = "yes"; then
+ if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = "yes"; }; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link with libintl" >&5
+$as_echo_n "checking how to link with libintl... " >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIBINTL" >&5
+$as_echo "$LIBINTL" >&6; }
+
+ for element in $INCINTL; do
+ haveit=
+ for x in $CPPFLAGS; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X$element"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }$element"
+ fi
+ done
+
+ fi
+
+
+$as_echo "@%:@define HAVE_GETTEXT 1" >>confdefs.h
+
+
+$as_echo "@%:@define HAVE_DCGETTEXT 1" >>confdefs.h
+
+ fi
+
+ POSUB=po
+ fi
+
+
+
+ INTLLIBS="$LIBINTL"
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: dmalloc enabled" >&5
+$as_echo_n "checking option: dmalloc enabled... " >&6; }
+@%:@ Check whether --enable-dmalloc was given.
+if test "${enable_dmalloc+set}" = set; then :
+ enableval=$enable_dmalloc;
+fi
+
+if test x$enable_dmalloc = "xyes" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+@%:@ Check whether --with-dmalloc-dir was given.
+if test "${with_dmalloc_dir+set}" = set; then :
+ withval=$with_dmalloc_dir;
+ if test "x$withval" != "xno" ; then
+ enable_dmalloc = "yes"
+ CPPFLAGS="$CPPCFLAGS -I${withval}"
+ LDFLAGS="$LDFLAGS -L${withval}"
+ fi
+
+fi
+
+
+if test x$enable_dmalloc = "xyes" ; then
+
+$as_echo "@%:@define ENABLE_DMALLOC 1" >>confdefs.h
+
+fi
+
+localedir="\${datadir}/locale"
+
+@%:@ Check whether --with-localedir was given.
+if test "${with_localedir+set}" = set; then :
+ withval=$with_localedir;
+ case $withval in
+ yes)
+ ;;
+ no)
+ ;;
+ *)
+ localedir=$withval
+ ;;
+ esac
+
+fi
+
+localedir="$localedir"
+
+
+# Setup OS-Specific features
+case "$host" in
+ *darwin*)
+ @%:@ Check whether --enable-osx-universal-binaries was given.
+if test "${enable_osx_universal_binaries+set}" = set; then :
+ enableval=$enable_osx_universal_binaries;
+fi
+
+ if test "x$enable_osx_universal_binaries" = "xyes" ; then
+ if test "x$enable_dependency_tracking" != xno ; then
+ as_fn_error $? "--enable-osx-universal-binary requires --disable-dependency-tracking.
+Please re-run configure with these options:
+ --disable-dependency-tracking --enable-osx-universal-binary" "$LINENO" 5
+ fi
+ if test -d /Developer/SDKs/MacOSX10.5.sdk ; then
+ alpine_sysroot=/Developer/SDKs/MacOSX10.5.sdk
+ elif test -d /Developer/SDKs/MacOSX10.4u.sdk ; then
+ alpine_sysroot=/Developer/SDKs/MacOSX10.4u.sdk
+ else
+ as_fn_error $? "No suitable MacOSX SDK found. Make sure Xcode tools are installed" "$LINENO" 5
+ fi
+ ub_cflags="-isysroot $alpine_sysroot -arch ppc -arch i386"
+ ub_ldflags="-Wl,-syslibroot,$alpine_sysroot -arch ppc -arch i386"
+ AM_CFLAGS="$AM_CFLAGS $ub_cflags"
+ AM_LDFLAGS="$AM_LDFLAGS $ub_ldflags"
+ alpine_c_client_cflags="$alpine_c_client_cflags $ub_cflags"
+ alpine_c_client_ldflags="$alpine_c_client_ldflags $ub_ldflags"
+ fi
+ ;;
+esac
+
+
+@%:@ Check whether --with-include-path was given.
+if test "${with_include_path+set}" = set; then :
+ withval=$with_include_path;
+ case $withval in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+ new_cppflags="-I`echo ${withval} | ${SED} 's/:/ -I/g'`"
+ CPPFLAGS="$CPPFLAGS ${new_cppflags}"
+ alpine_c_client_cflags="$alpine_c_client_cflags ${new_cppflags}"
+ ;;
+ esac
+
+fi
+
+
+
+@%:@ Check whether --with-lib-path was given.
+if test "${with_lib_path+set}" = set; then :
+ withval=$with_lib_path;
+ case $withval in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+ new_ldflags="-L`echo ${withval} | ${SED} 's/:/ -L/g'`"
+ LDFLAGS="$LDFLAGS $new_ldflags"
+ alpine_c_client_ldflags="$alpine_c_client_ldflags ${new_ldflags}"
+ ;;
+ esac
+
+fi
+
+
+
+@%:@ Check whether --with-pubcookie was given.
+if test "${with_pubcookie+set}" = set; then :
+ withval=$with_pubcookie;
+ if test "x$withval" != "xno" ; then
+ WEB_PUBCOOKIE_BUILD=web/src/pubcookie
+ fi
+
+fi
+
+
+
+
+@%:@ Check whether --with-web-bin was given.
+if test "${with_web_bin+set}" = set; then :
+ withval=$with_web_bin;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+ WEB_BINDIR=$withval
+ ;;
+ esac
+
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: debugging is enabled" >&5
+$as_echo_n "checking option: debugging is enabled... " >&6; }
+@%:@ Check whether --enable-debug was given.
+if test "${enable_debug+set}" = set; then :
+ enableval=$enable_debug;
+fi
+
+if test x$enable_debug != "xno" ; then
+ AM_CFLAGS="$AM_CFLAGS -g"
+
+$as_echo "@%:@define DEBUG 1" >>confdefs.h
+
+
+$as_echo "@%:@define DEBUGJOURNAL 1" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: optimization is enabled" >&5
+$as_echo_n "checking option: optimization is enabled... " >&6; }
+@%:@ Check whether --enable-optimization was given.
+if test "${enable_optimization+set}" = set; then :
+ enableval=$enable_optimization;
+fi
+
+if test x$enable_optimization != "xno" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ CFLAGS="`echo $AM_CFLAGS | ${SED} 's/-O2//'`"
+ alpine_c_client_gccoptlevel="-O0"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: mouse support enabled" >&5
+$as_echo_n "checking option: mouse support enabled... " >&6; }
+@%:@ Check whether --enable-mouse was given.
+if test "${enable_mouse+set}" = set; then :
+ enableval=$enable_mouse;
+fi
+
+if test x$enable_mouse != "xno" ; then
+
+$as_echo "@%:@define MOUSE /**/" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: quotas enabled" >&5
+$as_echo_n "checking option: quotas enabled... " >&6; }
+@%:@ Check whether --enable-quotas was given.
+if test "${enable_quotas+set}" = set; then :
+ enableval=$enable_quotas;
+fi
+
+if test x$enable_quotas = "xyes" ; then
+
+$as_echo "@%:@define USE_QUOTAS /**/" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: From changing enabled" >&5
+$as_echo_n "checking option: From changing enabled... " >&6; }
+@%:@ Check whether --enable-from_changing was given.
+if test "${enable_from_changing+set}" = set; then :
+ enableval=$enable_from_changing;
+fi
+
+if test x$enable_from_changing != "xno" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+
+$as_echo "@%:@define NEVER_ALLOW_CHANGING_FROM /**/" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: background post enabled" >&5
+$as_echo_n "checking option: background post enabled... " >&6; }
+@%:@ Check whether --enable-background-post was given.
+if test "${enable_background_post+set}" = set; then :
+ enableval=$enable_background_post;
+fi
+
+if test x$enable_background_post != "xno" ; then
+
+$as_echo "@%:@define BACKGROUND_POST /**/" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: keyboard lock enabled" >&5
+$as_echo_n "checking option: keyboard lock enabled... " >&6; }
+@%:@ Check whether --enable-keyboard-lock was given.
+if test "${enable_keyboard_lock+set}" = set; then :
+ enableval=$enable_keyboard_lock;
+fi
+
+if test x$enable_keyboard_lock != "xno" ; then
+
+$as_echo "@%:@define KEYBOARD_LOCK /**/" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: from encoding enabled" >&5
+$as_echo_n "checking option: from encoding enabled... " >&6; }
+@%:@ Check whether --enable-from-encoding was given.
+if test "${enable_from_encoding+set}" = set; then :
+ enableval=$enable_from_encoding;
+fi
+
+if test x$enable_from_encoding = "xyes" ; then
+
+$as_echo "@%:@define ENCODE_FROMS /**/" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+@%:@ Check whether --with-smtp-msa was given.
+if test "${with_smtp_msa+set}" = set; then :
+ withval=$with_smtp_msa;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ # Extract the first word of "sendmail", so it can be a program name with args.
+set dummy sendmail; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_SENDMAIL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $SENDMAIL in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_SENDMAIL="$SENDMAIL" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_dummy="$PATH:/usr/sbin:/usr/lib"
+for as_dir in $as_dummy
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_SENDMAIL="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_SENDMAIL" && ac_cv_path_SENDMAIL=""""
+ ;;
+esac
+fi
+SENDMAIL=$ac_cv_path_SENDMAIL
+if test -n "$SENDMAIL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SENDMAIL" >&5
+$as_echo "$SENDMAIL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ ;;
+ *)
+ SENDMAIL=$withval
+ ;;
+ esac
+
+else
+
+ # Extract the first word of "sendmail", so it can be a program name with args.
+set dummy sendmail; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_SENDMAIL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $SENDMAIL in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_SENDMAIL="$SENDMAIL" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_dummy="$PATH:/usr/sbin:/usr/lib"
+for as_dir in $as_dummy
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_SENDMAIL="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_SENDMAIL" && ac_cv_path_SENDMAIL=""""
+ ;;
+esac
+fi
+SENDMAIL=$ac_cv_path_SENDMAIL
+if test -n "$SENDMAIL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SENDMAIL" >&5
+$as_echo "$SENDMAIL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+fi
+
+if test -n "$SENDMAIL" ; then
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SENDMAIL "$SENDMAIL"
+_ACEOF
+
+fi
+
+smtp_msa_flags="-bs -odb -oem"
+
+@%:@ Check whether --with-smtp-msa-flags was given.
+if test "${with_smtp_msa_flags+set}" = set; then :
+ withval=$with_smtp_msa_flags;
+ if test "x$withval" != "xno" ; then
+ smtp_msa_flags=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SENDMAILFLAGS "$smtp_msa_flags"
+_ACEOF
+
+
+npa="inews"
+
+@%:@ Check whether --with-npa was given.
+if test "${with_npa+set}" = set; then :
+ withval=$with_npa;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ # Extract the first word of "inews", so it can be a program name with args.
+set dummy inews; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_NPA_PROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $NPA_PROG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_NPA_PROG="$NPA_PROG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_dummy="$PATH:/usr/sbin:/usr/lib"
+for as_dir in $as_dummy
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_NPA_PROG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_NPA_PROG" && ac_cv_path_NPA_PROG=""""
+ ;;
+esac
+fi
+NPA_PROG=$ac_cv_path_NPA_PROG
+if test -n "$NPA_PROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NPA_PROG" >&5
+$as_echo "$NPA_PROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ ;;
+ *)
+ NPA_PROG=$withval
+ ;;
+ esac
+
+else
+
+ # Extract the first word of "inews", so it can be a program name with args.
+set dummy inews; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_NPA_PROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $NPA_PROG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_NPA_PROG="$NPA_PROG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_dummy="$PATH:/usr/sbin:/usr/lib"
+for as_dir in $as_dummy
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_NPA_PROG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_NPA_PROG" && ac_cv_path_NPA_PROG=""""
+ ;;
+esac
+fi
+NPA_PROG=$ac_cv_path_NPA_PROG
+if test -n "$NPA_PROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NPA_PROG" >&5
+$as_echo "$NPA_PROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+fi
+
+
+npa_flags="-h"
+
+@%:@ Check whether --with-npa-flags was given.
+if test "${with_npa_flags+set}" = set; then :
+ withval=$with_npa_flags;
+ if test "x$withval" != "xno" ; then
+ npa_flags=$withval
+ fi
+
+fi
+
+if test -n "$NPA_PROG" ; then
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SENDNEWS "$NPA_PROG $npa_flags"
+_ACEOF
+
+fi
+
+
+@%:@ Check whether --with-password-prog was given.
+if test "${with_password_prog+set}" = set; then :
+ withval=$with_password_prog;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ # Extract the first word of "passwd", so it can be a program name with args.
+set dummy passwd; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_PWPROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $PWPROG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_PWPROG="$PWPROG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_dummy="$PATH:/usr/sbin:/usr/lib"
+for as_dir in $as_dummy
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_PWPROG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_PWPROG" && ac_cv_path_PWPROG=""""
+ ;;
+esac
+fi
+PWPROG=$ac_cv_path_PWPROG
+if test -n "$PWPROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PWPROG" >&5
+$as_echo "$PWPROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ ;;
+ *)
+ # Extract the first word of "$withval", so it can be a program name with args.
+set dummy $withval; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_PWPROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $PWPROG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_PWPROG="$PWPROG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_dummy="$PATH:/usr/sbin:/usr/lib"
+for as_dir in $as_dummy
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_PWPROG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_PWPROG" && ac_cv_path_PWPROG=""""
+ ;;
+esac
+fi
+PWPROG=$ac_cv_path_PWPROG
+if test -n "$PWPROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PWPROG" >&5
+$as_echo "$PWPROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ ;;
+ esac
+
+else
+
+ # Extract the first word of "passwd", so it can be a program name with args.
+set dummy passwd; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_PWPROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $PWPROG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_PWPROG="$PWPROG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_dummy="$PATH:/usr/sbin:/usr/lib"
+for as_dir in $as_dummy
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_PWPROG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_PWPROG" && ac_cv_path_PWPROG=""""
+ ;;
+esac
+fi
+PWPROG=$ac_cv_path_PWPROG
+if test -n "$PWPROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PWPROG" >&5
+$as_echo "$PWPROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+fi
+
+if test -n "$PWPROG" ; then
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PASSWD_PROG "$PWPROG"
+_ACEOF
+
+fi
+
+
+@%:@ Check whether --with-simple-spellcheck was given.
+if test "${with_simple_spellcheck+set}" = set; then :
+ withval=$with_simple_spellcheck;
+ if test "x$withval" != "xno" ; then
+ SPELLPROG=$withval
+ fi
+
+else
+
+ # Extract the first word of "hunspell", so it can be a program name with args.
+set dummy hunspell; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_SPELLPROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$SPELLPROG"; then
+ ac_cv_prog_SPELLPROG="$SPELLPROG" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_SPELLPROG="hunspell"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+SPELLPROG=$ac_cv_prog_SPELLPROG
+if test -n "$SPELLPROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SPELLPROG" >&5
+$as_echo "$SPELLPROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test -z "$SPELLPROG" ; then
+ # Extract the first word of "aspell", so it can be a program name with args.
+set dummy aspell; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_SPELLPROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$SPELLPROG"; then
+ ac_cv_prog_SPELLPROG="$SPELLPROG" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_SPELLPROG="aspell"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+SPELLPROG=$ac_cv_prog_SPELLPROG
+if test -n "$SPELLPROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SPELLPROG" >&5
+$as_echo "$SPELLPROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test -z "$SPELLPROG" ; then
+ # Extract the first word of "ispell", so it can be a program name with args.
+set dummy ispell; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_SPELLPROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$SPELLPROG"; then
+ ac_cv_prog_SPELLPROG="$SPELLPROG" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_SPELLPROG="ispell"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+SPELLPROG=$ac_cv_prog_SPELLPROG
+if test -n "$SPELLPROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SPELLPROG" >&5
+$as_echo "$SPELLPROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test -z "$SPELLPROG" ; then
+ SPELLPROG="spell"
+ fi
+ fi
+ fi
+
+fi
+
+
+if test "x$SPELLPROG" != "xno" ; then
+ # Extract the first word of "$SPELLPROG", so it can be a program name with args.
+set dummy $SPELLPROG; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_alpine_simple_spellcheck+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $alpine_simple_spellcheck in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_alpine_simple_spellcheck="$alpine_simple_spellcheck" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_alpine_simple_spellcheck="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+alpine_simple_spellcheck=$ac_cv_path_alpine_simple_spellcheck
+if test -n "$alpine_simple_spellcheck"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $alpine_simple_spellcheck" >&5
+$as_echo "$alpine_simple_spellcheck" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test -n "$alpine_simple_spellcheck" ; then
+ case "$SPELLPROG" in
+ hunspell)
+ alpine_simple_spellcheck="$alpine_simple_spellcheck -l"
+ ;;
+ aspell)
+ alpine_simple_spellcheck="$alpine_simple_spellcheck --dont-backup --mode=email list"
+ ;;
+ ispell)
+ alpine_simple_spellcheck="$alpine_simple_spellcheck -l"
+ ;;
+ *)
+ ;;
+ esac
+ fi
+fi
+
+
+@%:@ Check whether --with-interactive-spellcheck was given.
+if test "${with_interactive_spellcheck+set}" = set; then :
+ withval=$with_interactive_spellcheck;
+ if test "x$withval" != "xno" ; then
+ ISPELLPROG=$withval
+ fi
+
+else
+
+ # Extract the first word of "hunspell", so it can be a program name with args.
+set dummy hunspell; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ISPELLPROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ISPELLPROG"; then
+ ac_cv_prog_ISPELLPROG="$ISPELLPROG" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ISPELLPROG="hunspell"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ISPELLPROG=$ac_cv_prog_ISPELLPROG
+if test -n "$ISPELLPROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ISPELLPROG" >&5
+$as_echo "$ISPELLPROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test -z "$ISPELLPROG" ; then
+ # Extract the first word of "aspell", so it can be a program name with args.
+set dummy aspell; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ISPELLPROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ISPELLPROG"; then
+ ac_cv_prog_ISPELLPROG="$ISPELLPROG" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ISPELLPROG="aspell"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ISPELLPROG=$ac_cv_prog_ISPELLPROG
+if test -n "$ISPELLPROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ISPELLPROG" >&5
+$as_echo "$ISPELLPROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test -z "$SPELLPROG" ; then
+ ISPELLPROG="ispell"
+ fi
+ fi
+
+fi
+
+
+if test "x$ISPELLPROG" != "xno" ; then
+ # Extract the first word of "$ISPELLPROG", so it can be a program name with args.
+set dummy $ISPELLPROG; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_alpine_interactive_spellcheck+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $alpine_interactive_spellcheck in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_alpine_interactive_spellcheck="$alpine_interactive_spellcheck" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_alpine_interactive_spellcheck="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+alpine_interactive_spellcheck=$ac_cv_path_alpine_interactive_spellcheck
+if test -n "$alpine_interactive_spellcheck"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $alpine_interactive_spellcheck" >&5
+$as_echo "$alpine_interactive_spellcheck" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test -n "$alpine_interactive_spellcheck" ; then
+ case "$ISPELLPROG" in
+ aspell)
+ alpine_interactive_spellcheck="$alpine_interactive_spellcheck --dont-backup --mode=email check"
+ ;;
+ *)
+ ;;
+ esac
+ fi
+fi
+
+if test -n "$alpine_interactive_spellcheck" ; then
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_VAR_SPELLER "$alpine_interactive_spellcheck"
+_ACEOF
+
+fi
+
+if test -z "$alpine_simple_spellcheck" -a -n "$alpine_interactive_spellcheck" ; then
+ alpine_simple_spellcheck=test
+fi
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SPELLER "$alpine_simple_spellcheck"
+_ACEOF
+
+
+case "$prefix" in
+ NONE) dpv=/usr/local/lib/pine.conf ;;
+ *) dpv=${prefix}/lib/pine.conf ;;
+esac
+
+@%:@ Check whether --with-system-pinerc was given.
+if test "${with_system_pinerc+set}" = set; then :
+ withval=$with_system_pinerc;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+ dpv=$withval
+ ;;
+ esac
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SYSTEM_PINERC "$dpv"
+_ACEOF
+
+
+case "$prefix" in
+ NONE) dpv=/usr/local/lib/pine.conf.fixed ;;
+ *) dpv=${prefix}/lib/pine.conf.fixed ;;
+esac
+
+@%:@ Check whether --with-system-fixed-pinerc was given.
+if test "${with_system_fixed_pinerc+set}" = set; then :
+ withval=$with_system_fixed_pinerc;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+ dpv=$withval
+ ;;
+ esac
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SYSTEM_PINERC_FIXED "$dpv"
+_ACEOF
+
+
+
+
+
+
+
+ dpv=150
+
+@%:@ Check whether --with-mailcheck-interval was given.
+if test "${with_mailcheck_interval+set}" = set; then :
+ withval=$with_mailcheck_interval;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_MAILCHECK "$dpv"
+_ACEOF
+
+
+
+ dpv=420
+
+@%:@ Check whether --with-checkpoint-interval was given.
+if test "${with_checkpoint_interval+set}" = set; then :
+ withval=$with_checkpoint_interval;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define CHECK_POINT_TIME $dpv
+_ACEOF
+
+
+
+ dpv=12
+
+@%:@ Check whether --with-checkpoint-frequency was given.
+if test "${with_checkpoint_frequency+set}" = set; then :
+ withval=$with_checkpoint_frequency;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define CHECK_POINT_FREQ $dpv
+_ACEOF
+
+
+
+ dpv=24
+
+@%:@ Check whether --with-display-rows was given.
+if test "${with_display_rows+set}" = set; then :
+ withval=$with_display_rows;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DEFAULT_LINES_ON_TERMINAL $dpv
+_ACEOF
+
+
+
+ dpv=80
+
+@%:@ Check whether --with-display-columns was given.
+if test "${with_display_columns+set}" = set; then :
+ withval=$with_display_columns;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DEFAULT_COLUMNS_ON_TERMINAL $dpv
+_ACEOF
+
+
+
+ dpv=200
+
+@%:@ Check whether --with-max-display-rows was given.
+if test "${with_max_display_rows+set}" = set; then :
+ withval=$with_max_display_rows;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define MAX_SCREEN_ROWS $dpv
+_ACEOF
+
+
+
+ dpv=500
+
+@%:@ Check whether --with-max-display-columns was given.
+if test "${with_max_display_columns+set}" = set; then :
+ withval=$with_max_display_columns;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define MAX_SCREEN_COLS $dpv
+_ACEOF
+
+
+
+ dpv=74
+
+@%:@ Check whether --with-fill-column was given.
+if test "${with_fill_column+set}" = set; then :
+ withval=$with_fill_column;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_FILLCOL "$dpv"
+_ACEOF
+
+
+
+ dpv=80
+
+@%:@ Check whether --with-max_fill-column was given.
+if test "${with_max_fill_column+set}" = set; then :
+ withval=$with_max_fill_column;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define MAX_FILLCOL $dpv
+_ACEOF
+
+
+
+ dpv=2
+
+@%:@ Check whether --with-debug-level was given.
+if test "${with_debug_level+set}" = set; then :
+ withval=$with_debug_level;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DEFAULT_DEBUG $dpv
+_ACEOF
+
+
+
+ dpv=4
+
+@%:@ Check whether --with-debug-files was given.
+if test "${with_debug_files+set}" = set; then :
+ withval=$with_debug_files;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define NUMDEBUGFILES $dpv
+_ACEOF
+
+
+
+ dpv=.pine-debug
+
+@%:@ Check whether --with-debug-file was given.
+if test "${with_debug_file+set}" = set; then :
+ withval=$with_debug_file;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DEBUGFILE "$dpv"
+_ACEOF
+
+
+
+ dpv="\$Forwarded"
+
+@%:@ Check whether --with-forwarded-keyword was given.
+if test "${with_forwarded_keyword+set}" = set; then :
+ withval=$with_forwarded_keyword;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define FORWARDED_FLAG "$dpv"
+_ACEOF
+
+
+
+ dpv=2
+
+@%:@ Check whether --with-display-overlap was given.
+if test "${with_display_overlap+set}" = set; then :
+ withval=$with_display_overlap;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_OVERLAP "$dpv"
+_ACEOF
+
+
+
+ dpv=0
+
+@%:@ Check whether --with-display-margin was given.
+if test "${with_display_margin+set}" = set; then :
+ withval=$with_display_margin;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_MARGIN "$dpv"
+_ACEOF
+
+
+
+ dpv=sent-mail
+
+@%:@ Check whether --with-default-fcc was given.
+if test "${with_default_fcc+set}" = set; then :
+ withval=$with_default_fcc;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_DEFAULT_FCC "$dpv"
+_ACEOF
+
+
+
+ dpv=saved-messages
+
+@%:@ Check whether --with-default-save-folder was given.
+if test "${with_default_save_folder+set}" = set; then :
+ withval=$with_default_save_folder;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DEFAULT_SAVE "$dpv"
+_ACEOF
+
+
+
+ dpv=postponed-mail
+
+@%:@ Check whether --with-default-legacy-postponed-folder was given.
+if test "${with_default_legacy_postponed_folder+set}" = set; then :
+ withval=$with_default_legacy_postponed_folder;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define POSTPONED_MAIL "$dpv"
+_ACEOF
+
+
+
+ dpv=postponed-msgs
+
+@%:@ Check whether --with-default-postponed-folder was given.
+if test "${with_default_postponed_folder+set}" = set; then :
+ withval=$with_default_postponed_folder;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define POSTPONED_MSGS "$dpv"
+_ACEOF
+
+
+
+ dpv=Trash
+
+@%:@ Check whether --with-default-trash-folder was given.
+if test "${with_default_trash_folder+set}" = set; then :
+ withval=$with_default_trash_folder;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define TRASH_FOLDER "$dpv"
+_ACEOF
+
+
+
+ dpv=.pine-interrupted-mail
+
+@%:@ Check whether --with-default-interrupted-mail was given.
+if test "${with_default_interrupted_mail+set}" = set; then :
+ withval=$with_default_interrupted_mail;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define INTERRUPTED_MAIL "$dpv"
+_ACEOF
+
+
+
+ dpv=dead.letter
+
+@%:@ Check whether --with-default-dead-letter-folder was given.
+if test "${with_default_dead_letter_folder+set}" = set; then :
+ withval=$with_default_dead_letter_folder;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DEADLETTER "$dpv"
+_ACEOF
+
+
+
+ dpv=mail
+
+@%:@ Check whether --with-default-mail-directory was given.
+if test "${with_default_mail_directory+set}" = set; then :
+ withval=$with_default_mail_directory;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_MAIL_DIRECTORY "$dpv"
+_ACEOF
+
+
+
+ dpv=INBOX
+
+@%:@ Check whether --with-default-inbox-name was given.
+if test "${with_default_inbox_name+set}" = set; then :
+ withval=$with_default_inbox_name;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define INBOX_NAME "$dpv"
+_ACEOF
+
+
+
+ dpv=.signature
+
+@%:@ Check whether --with-default-signature-file was given.
+if test "${with_default_signature_file+set}" = set; then :
+ withval=$with_default_signature_file;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_SIGNATURE_FILE "$dpv"
+_ACEOF
+
+
+
+ dpv=no
+
+@%:@ Check whether --with-default-elm-style-save was given.
+if test "${with_default_elm_style_save+set}" = set; then :
+ withval=$with_default_elm_style_save;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_ELM_STYLE_SAVE "$dpv"
+_ACEOF
+
+
+
+ dpv=no
+
+@%:@ Check whether --with-default-header-in-reply was given.
+if test "${with_default_header_in_reply+set}" = set; then :
+ withval=$with_default_header_in_reply;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_HEADER_IN_REPLY "$dpv"
+_ACEOF
+
+
+
+ dpv=no
+
+@%:@ Check whether --with-default-old-style-reply was given.
+if test "${with_default_old_style_reply+set}" = set; then :
+ withval=$with_default_old_style_reply;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_OLD_STYLE_REPLY "$dpv"
+_ACEOF
+
+
+
+ dpv=no
+
+@%:@ Check whether --with-default-use-only-domain-name was given.
+if test "${with_default_use_only_domain_name+set}" = set; then :
+ withval=$with_default_use_only_domain_name;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_USE_ONLY_DOMAIN_NAME "$dpv"
+_ACEOF
+
+
+
+ dpv=no
+
+@%:@ Check whether --with-default-save-by-sender was given.
+if test "${with_default_save_by_sender+set}" = set; then :
+ withval=$with_default_save_by_sender;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_SAVE_BY_SENDER "$dpv"
+_ACEOF
+
+
+
+ dpv=arrival
+
+@%:@ Check whether --with-default-sort-key was given.
+if test "${with_default_sort_key+set}" = set; then :
+ withval=$with_default_sort_key;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_SORT_KEY "$dpv"
+_ACEOF
+
+
+
+ dpv=fullname-with-lists-last
+
+@%:@ Check whether --with-default-addressbook-sort-rule was given.
+if test "${with_default_addressbook_sort_rule+set}" = set; then :
+ withval=$with_default_addressbook_sort_rule;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_AB_SORT_RULE "$dpv"
+_ACEOF
+
+
+
+ dpv=alphabetical
+
+@%:@ Check whether --with-default-folder-sort-rule was given.
+if test "${with_default_folder_sort_rule+set}" = set; then :
+ withval=$with_default_folder_sort_rule;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_FLD_SORT_RULE "$dpv"
+_ACEOF
+
+
+
+ dpv=default-folder
+
+@%:@ Check whether --with-default-saved-message-name-rule was given.
+if test "${with_default_saved_message_name_rule+set}" = set; then :
+ withval=$with_default_saved_message_name_rule;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_SAVED_MSG_NAME_RULE "$dpv"
+_ACEOF
+
+
+
+ dpv=default-fcc
+
+@%:@ Check whether --with-default-fcc-rule was given.
+if test "${with_default_fcc_rule+set}" = set; then :
+ withval=$with_default_fcc_rule;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_FCC_RULE "$dpv"
+_ACEOF
+
+
+
+ dpv=lpr
+
+@%:@ Check whether --with-default-standard-printer was given.
+if test "${with_default_standard_printer+set}" = set; then :
+ withval=$with_default_standard_printer;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_STANDARD_PRINTER "$dpv"
+_ACEOF
+
+
+
+ dpv=attached-to-ansi
+
+@%:@ Check whether --with-default-ansi-printer was given.
+if test "${with_default_ansi_printer+set}" = set; then :
+ withval=$with_default_ansi_printer;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define ANSI_PRINTER "$dpv"
+_ACEOF
+
+
+
+ dpv=.addressbook
+
+@%:@ Check whether --with-default-addressbook was given.
+if test "${with_default_addressbook+set}" = set; then :
+ withval=$with_default_addressbook;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_ADDRESSBOOK "$dpv"
+_ACEOF
+
+
+
+ dpv="Local Support"
+
+@%:@ Check whether --with-default-local-fullname was given.
+if test "${with_default_local_fullname+set}" = set; then :
+ withval=$with_default_local_fullname;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_LOCAL_FULLNAME "$dpv"
+_ACEOF
+
+
+
+ dpv=postmaster
+
+@%:@ Check whether --with-default-local-address was given.
+if test "${with_default_local_address+set}" = set; then :
+ withval=$with_default_local_address;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_LOCAL_ADDRESS "$dpv"
+_ACEOF
+
+
+
+ dpv=1
+
+@%:@ Check whether --with-default-keyboard-lock-count was given.
+if test "${with_default_keyboard_lock_count+set}" = set; then :
+ withval=$with_default_keyboard_lock_count;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_KBLOCK_PASSWD_COUNT "$dpv"
+_ACEOF
+
+
+
+ dpv=3
+
+@%:@ Check whether --with-default-remote-addressbook-history was given.
+if test "${with_default_remote_addressbook_history+set}" = set; then :
+ withval=$with_default_remote_addressbook_history;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_REMOTE_ABOOK_HISTORY "$dpv"
+_ACEOF
+
+
+
+ dpv=.alpine-smime/public
+
+@%:@ Check whether --with-smime-public-cert-directory was given.
+if test "${with_smime_public_cert_directory+set}" = set; then :
+ withval=$with_smime_public_cert_directory;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_PUBLICCERT_DIR "$dpv"
+_ACEOF
+
+
+
+ dpv=.alpine-smime/private
+
+@%:@ Check whether --with-smime-private-key-directory was given.
+if test "${with_smime_private_key_directory+set}" = set; then :
+ withval=$with_smime_private_key_directory;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_PRIVATEKEY_DIR "$dpv"
+_ACEOF
+
+
+
+ dpv=.alpine-smime/ca
+
+@%:@ Check whether --with-smime-cacert-directory was given.
+if test "${with_smime_cacert_directory+set}" = set; then :
+ withval=$with_smime_cacert_directory;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_CACERT_DIR "$dpv"
+_ACEOF
+
+
+
+ dpv=ANSI_PRINTER
+
+@%:@ Check whether --with-default-printer was given.
+if test "${with_default_printer+set}" = set; then :
+ withval=$with_default_printer;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_DEFAULT_PRINTER $dpv
+_ACEOF
+
+
+
+
+@%:@ Check whether --with-passfile was given.
+if test "${with_passfile+set}" = set; then :
+ withval=$with_passfile;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+ alpine_PASSFILE=$withval
+ ;;
+ esac
+
+fi
+
+
+
+@%:@ Check whether --with-local-password-cache was given.
+if test "${with_local_password_cache+set}" = set; then :
+ withval=$with_local_password_cache;
+ alpine_os_credential_cache=$withval
+
+fi
+
+
+
+@%:@ Check whether --with-local-password-cache-method was given.
+if test "${with_local_password_cache_method+set}" = set; then :
+ withval=$with_local_password_cache_method;
+ alpine_os_credential_cache_method=$withval
+
+fi
+
+
+if test -n "$alpine_PASSFILE" ; then
+ case $alpine_os_credential_cache in
+ no)
+ ;;
+ *)
+ alpine_os_credential_cache="no"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: --with-passfile definition overrides OS-Specific password caching" >&5
+$as_echo "$as_me: --with-passfile definition overrides OS-Specific password caching" >&6;}
+ ;;
+ esac
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PASSFILE "$alpine_PASSFILE"
+_ACEOF
+
+fi
+
+
+@%:@ Check whether --with-default-sshpath was given.
+if test "${with_default_sshpath+set}" = set; then :
+ withval=$with_default_sshpath;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_SSHPATH "$withval"
+_ACEOF
+
+ ;;
+ esac
+
+fi
+
+
+
+@%:@ Check whether --with-default-sshcmd was given.
+if test "${with_default_sshcmd+set}" = set; then :
+ withval=$with_default_sshcmd;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_SSHCMD "$withval"
+_ACEOF
+
+ ;;
+ esac
+
+fi
+
+
+
+@%:@ Check whether --with-ssl was given.
+if test "${with_ssl+set}" = set; then :
+ withval=$with_ssl; with_ssl=$withval
+fi
+
+
+if test "x$with_ssl" = "xno" ; then
+ alpine_SSLTYPE="none"
+else
+ case $host in
+ *-linux-gnu)
+ if test -f /etc/fedora-release -o -f /etc/redhat-release -o -f /etc/redhat_version ; then
+ alpine_SSLTYPE="nopwd"
+ if test -d /etc/pki/tls ; then
+ alpine_SSLDIR="/etc/pki/tls"
+ else
+ alpine_SSLDIR="/usr/share/ssl"
+ fi
+ elif test -f /etc/SuSE-release ; then
+ alpine_SSLTYPE="nopwd"
+ alpine_SSLDIR="/usr/share/ssl"
+ elif test -d /etc/osso-af-init ; then
+ alpine_SSLTYPE="nopwd"
+ alpine_SSLDIR="/usr"
+ alpine_SSLCERTS="/usr/share/certs"
+ else
+ alpine_SSLTYPE="nopwd"
+ alpine_SSLDIR="/usr"
+ alpine_SSLCERTS="/etc/ssl/certs"
+ fi
+ ;;
+ *-apple-darwin*)
+ alpine_SSLTYPE="nopwd"
+ alpine_SSLCERTS="/System/Library/OpenSSL/certs"
+ ;;
+ *-openbsd*)
+ alpine_SSLTYPE="nopwd"
+ alpine_SSLDIR="/usr"
+ alpine_SSLCERTS="/etc/ssl/certs"
+ ;;
+ *-sco-sysv* | *-sysv*UnixWare | *-sysv*OpenUNIX)
+ alpine_SSLTYPE="sco.nopwd"
+ alpine_SSLDIR=/usr/local/ssl
+ ;;
+ *-*-solaris*)
+ if test -d /usr/sfw/include/openssl ; then
+ alpine_SSLDIR="/usr/sfw"
+ elif test -d /opt/csw/include/openssl ; then
+ alpine_SSLDIR="/opt/csw"
+ if test -d /opt/csw/ssl/certs ; then
+ alpine_SSLCERTS="/opt/csw/ssl/certs"
+ fi
+ fi
+ if test -z "$alpine_SSLCERTS" -a -d /etc/certs ; then
+ alpine_SSLCERTS="/etc/certs"
+ fi
+ ;;
+ *)
+ alpine_SSLTYPE="nopwd"
+ alpine_SSLDIR=/usr/local/ssl
+ ;;
+ esac
+
+
+@%:@ Check whether --with-ssl-dir was given.
+if test "${with_ssl_dir+set}" = set; then :
+ withval=$with_ssl_dir;
+ if test "x$withval" != "xno" ; then
+ alpine_SSLDIR=$withval
+ fi
+
+fi
+
+
+
+@%:@ Check whether --with-ssl-certs-dir was given.
+if test "${with_ssl_certs_dir+set}" = set; then :
+ withval=$with_ssl_certs_dir;
+ if test "x$withval" != "xno" ; then
+ alpine_SSLCERTS=$withval
+ fi
+
+fi
+
+
+
+@%:@ Check whether --with-ssl-include-dir was given.
+if test "${with_ssl_include_dir+set}" = set; then :
+ withval=$with_ssl_include_dir;
+ if test "x$withval" != "xno" ; then
+ alpine_SSLINCLUDE=$withval
+ fi
+
+fi
+
+
+
+@%:@ Check whether --with-ssl-lib-dir was given.
+if test "${with_ssl_lib_dir+set}" = set; then :
+ withval=$with_ssl_lib_dir;
+ if test "x$withval" != "xno" ; then
+ alpine_SSLLIB=$withval
+ fi
+
+fi
+
+ if test -n "$alpine_SSLINCLUDE" ; then
+ CPPCFLAGS="-I$alpine_SSLINCLUDE $CPPFLAGS"
+ elif test -n "$alpine_SSLDIR" ; then
+ CPPFLAGS="-I${alpine_SSLDIR}/include $CPPFLAGS"
+ fi
+ if test -n "$alpine_SSLLIB" ; then
+ LDFLAGS="-L$alpine_SSLLIB $LDFLAGS"
+ elif test -n "$alpine_SSLDIR" ; then
+ LDFLAGS="-L${alpine_SSLDIR}/lib $LDFLAGS"
+ fi
+fi
+
+
+@%:@ Check whether --with-krb5 was given.
+if test "${with_krb5+set}" = set; then :
+ withval=$with_krb5; with_krb5=$withval
+fi
+
+
+if test "x$with_krb5" = "xno" ; then
+ alpine_GSSTYPE="none"
+else
+ alpine_GSSTYPE=
+
+
+@%:@ Check whether --with-krb5-dir was given.
+if test "${with_krb5_dir+set}" = set; then :
+ withval=$with_krb5_dir;
+ if test "x$withval" != "xno" ; then
+ CPPFLAGS="$CPPFLAGS -I${withval}/include"
+ LDFLAGS="$LDFLAGS -L${withval}/lib"
+ fi
+
+fi
+
+
+
+@%:@ Check whether --with-krb5-include-dir was given.
+if test "${with_krb5_include_dir+set}" = set; then :
+ withval=$with_krb5_include_dir;
+ if test "x$withval" != "xno" ; then
+ CPPFLAGS="$CPPFLAGS -I$withval"
+ fi
+
+fi
+
+
+
+@%:@ Check whether --with-krb5-lib-dir was given.
+if test "${with_krb5_lib_dir+set}" = set; then :
+ withval=$with_krb5_lib_dir;
+ if test "x$withval" != "xno" ; then
+ LDFLAGS="$LDFLAGS -L$withval"
+ fi
+
+fi
+
+fi
+
+
+@%:@ Check whether --with-ldap was given.
+if test "${with_ldap+set}" = set; then :
+ withval=$with_ldap; with_ldap=$withval
+fi
+
+
+if test "x$with_ldap" = "xno" ; then
+ alpine_with_ldap=
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Excluding LDAP Support" >&5
+$as_echo "$as_me: Excluding LDAP Support" >&6;}
+else
+
+ alpine_with_ldap=yes
+
+@%:@ Check whether --with-ldap-dir was given.
+if test "${with_ldap_dir+set}" = set; then :
+ withval=$with_ldap_dir;
+ if test "x$withval" != "xno" ; then
+ CPPFLAGS="$CPPFLAGS -I${withval}/include"
+ LDFLAGS="$LDFLAGS -L${withval}/lib"
+ fi
+
+fi
+
+
+
+@%:@ Check whether --with-ldap-include-dir was given.
+if test "${with_ldap_include_dir+set}" = set; then :
+ withval=$with_ldap_include_dir;
+ if test "x$withval" != "xno" ; then
+ CPPFLAGS="$CPPFLAGS -I$withval"
+ fi
+
+fi
+
+
+
+@%:@ Check whether --with-ldap-lib-dir was given.
+if test "${with_ldap_lib_dir+set}" = set; then :
+ withval=$with_ldap_lib_dir;
+ if test "x$withval" != "xno" ; then
+ LDFLAGS="$LDFLAGS -L$withval"
+ fi
+
+fi
+
+fi
+
+
+@%:@ Check whether --with-smime was given.
+if test "${with_smime+set}" = set; then :
+ withval=$with_smime; with_smime=$withval
+fi
+
+
+
+@%:@ Check whether --with-tcl was given.
+if test "${with_tcl+set}" = set; then :
+ withval=$with_tcl; with_tcl=$withval
+fi
+
+
+if test "x$with_tcl" = "xno" ; then
+ WEB_BUILD=
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Excluding TCL Support, and thus Web Alpine Support" >&5
+$as_echo "$as_me: Excluding TCL Support, and thus Web Alpine Support" >&6;}
+else
+
+@%:@ Check whether --with-tcl-lib was given.
+if test "${with_tcl_lib+set}" = set; then :
+ withval=$with_tcl_lib;
+ if test "x$withval" != "xno" ; then
+ alpine_TCLLIB=$withval
+ fi
+
+fi
+
+
+@%:@ Check whether --with-tcl-include was given.
+if test "${with_tcl_include+set}" = set; then :
+ withval=$with_tcl_include;
+ if test "x$withval" != "xno" ; then
+ CPPFLAGS="$CPPFLAGS -I$withval"
+ alpine_TCLINC=$withval
+ fi
+
+fi
+
+fi
+
+
+@%:@ Check whether --with-supplied-regex was given.
+if test "${with_supplied_regex+set}" = set; then :
+ withval=$with_supplied_regex; alpine_REGEX=$withval
+fi
+
+
+
+@%:@ Check whether --with-pthread was given.
+if test "${with_pthread+set}" = set; then :
+ withval=$with_pthread; with_pthread=$withval
+fi
+
+
+
+@%:@ Check whether --with-system-mail-directory was given.
+if test "${with_system_mail_directory+set}" = set; then :
+ withval=$with_system_mail_directory;
+ if test "x$withval" != "xno" ; then
+ alpine_with_local_maildir="$withval"
+ fi
+
+fi
+
+
+
+@%:@ Check whether --with-c-client-target was given.
+if test "${with_c_client_target+set}" = set; then :
+ withval=$with_c_client_target;
+ if test "x$withval" != "xno" ;then
+ alpine_with_c_client_target="$withval"
+ fi
+
+fi
+
+
+
+
+@%:@ Check whether --with-ipv6 was given.
+if test "${with_ipv6+set}" = set; then :
+ withval=$with_ipv6; with_ipv6=$withval
+fi
+
+
+if test "x$with_ipv6" = "xno" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Excluding IPv6 Support" >&5
+$as_echo "$as_me: Excluding IPv6 Support" >&6;}
+ c_client_specials="${c_client_specials}IP6=4 "
+ c_client_ip6="true"
+else
+ c_client_ip6="touch imap/ip6"
+fi
+
+
+
+if test x$enable_dmalloc = "xyes" ; then
+ if test "x$with_pthread" = "xyes" ; then
+ dmalloc_lib=dmallocth
+ else
+ dmalloc_lib=dmalloc
+ fi
+
+ as_ac_Lib=`$as_echo "ac_cv_lib_$dmalloc_lib''_dmalloc_shutdown" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dmalloc_shutdown in -l$dmalloc_lib" >&5
+$as_echo_n "checking for dmalloc_shutdown in -l$dmalloc_lib... " >&6; }
+if eval \${$as_ac_Lib+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-l$dmalloc_lib $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dmalloc_shutdown ();
+int
+main ()
+{
+return dmalloc_shutdown ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ eval "$as_ac_Lib=yes"
+else
+ eval "$as_ac_Lib=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+eval ac_res=\$$as_ac_Lib
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define `$as_echo "HAVE_LIB$dmalloc_lib" | $as_tr_cpp` 1
+_ACEOF
+
+ LIBS="-l$dmalloc_lib $LIBS"
+
+else
+
+ as_fn_error $? "$dmalloc_lib requested, but -ldmalloc not found" "$LINENO" 5
+
+fi
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for setupterm in -ltinfo" >&5
+$as_echo_n "checking for setupterm in -ltinfo... " >&6; }
+if ${ac_cv_lib_tinfo_setupterm+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ltinfo $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char setupterm ();
+int
+main ()
+{
+return setupterm ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_tinfo_setupterm=yes
+else
+ ac_cv_lib_tinfo_setupterm=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_tinfo_setupterm" >&5
+$as_echo "$ac_cv_lib_tinfo_setupterm" >&6; }
+if test "x$ac_cv_lib_tinfo_setupterm" = xyes; then :
+
+ alpine_termdata=info
+ LIBS="$LIBS -ltinfo"
+
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for setupterm in -lncurses" >&5
+$as_echo_n "checking for setupterm in -lncurses... " >&6; }
+if ${ac_cv_lib_ncurses_setupterm+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lncurses $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char setupterm ();
+int
+main ()
+{
+return setupterm ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_ncurses_setupterm=yes
+else
+ ac_cv_lib_ncurses_setupterm=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ncurses_setupterm" >&5
+$as_echo "$ac_cv_lib_ncurses_setupterm" >&6; }
+if test "x$ac_cv_lib_ncurses_setupterm" = xyes; then :
+
+ alpine_termdata=info
+ LIBS="$LIBS -lncurses"
+
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for setupterm in -lcurses" >&5
+$as_echo_n "checking for setupterm in -lcurses... " >&6; }
+if ${ac_cv_lib_curses_setupterm+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lcurses $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char setupterm ();
+int
+main ()
+{
+return setupterm ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_curses_setupterm=yes
+else
+ ac_cv_lib_curses_setupterm=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_curses_setupterm" >&5
+$as_echo "$ac_cv_lib_curses_setupterm" >&6; }
+if test "x$ac_cv_lib_curses_setupterm" = xyes; then :
+
+ alpine_termdata=info
+ LIBS="$LIBS -lcurses"
+
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for tgetent in -ltermlib" >&5
+$as_echo_n "checking for tgetent in -ltermlib... " >&6; }
+if ${ac_cv_lib_termlib_tgetent+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ltermlib $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char tgetent ();
+int
+main ()
+{
+return tgetent ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_termlib_tgetent=yes
+else
+ ac_cv_lib_termlib_tgetent=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_termlib_tgetent" >&5
+$as_echo "$ac_cv_lib_termlib_tgetent" >&6; }
+if test "x$ac_cv_lib_termlib_tgetent" = xyes; then :
+
+ alpine_termdata=cap
+ LIBS="$LIBS -ltermlib"
+
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for tgetent in -ltermcap" >&5
+$as_echo_n "checking for tgetent in -ltermcap... " >&6; }
+if ${ac_cv_lib_termcap_tgetent+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ltermcap $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char tgetent ();
+int
+main ()
+{
+return tgetent ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_termcap_tgetent=yes
+else
+ ac_cv_lib_termcap_tgetent=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_termcap_tgetent" >&5
+$as_echo "$ac_cv_lib_termcap_tgetent" >&6; }
+if test "x$ac_cv_lib_termcap_tgetent" = xyes; then :
+
+ alpine_termdata=cap
+ LIBS="$LIBS -ltermcap"
+
+else
+
+ as_fn_error $? "Terminfo/termcap not found" "$LINENO" 5
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+case $alpine_termdata in
+ info)
+
+$as_echo "@%:@define HAS_TERMINFO 1" >>confdefs.h
+
+ ;;
+ cap)
+
+$as_echo "@%:@define HAS_TERMCAP 1" >>confdefs.h
+
+ ;;
+esac
+
+if test "$alpine_with_ldap" = "yes" ; then
+ alpine_has_ldap=
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ber_alloc in -llber" >&5
+$as_echo_n "checking for ber_alloc in -llber... " >&6; }
+if ${ac_cv_lib_lber_ber_alloc+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-llber $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ber_alloc ();
+int
+main ()
+{
+return ber_alloc ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_lber_ber_alloc=yes
+else
+ ac_cv_lib_lber_ber_alloc=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lber_ber_alloc" >&5
+$as_echo "$ac_cv_lib_lber_ber_alloc" >&6; }
+if test "x$ac_cv_lib_lber_ber_alloc" = xyes; then :
+
+ LIBS="$LIBS -llber"
+
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing ldap_init" >&5
+$as_echo_n "checking for library containing ldap_init... " >&6; }
+if ${ac_cv_search_ldap_init+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ldap_init ();
+int
+main ()
+{
+return ldap_init ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' ldap; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_ldap_init=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_ldap_init+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_ldap_init+:} false; then :
+
+else
+ ac_cv_search_ldap_init=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_ldap_init" >&5
+$as_echo "$ac_cv_search_ldap_init" >&6; }
+ac_res=$ac_cv_search_ldap_init
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+ alpine_has_ldap=yes
+
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing ldap_open" >&5
+$as_echo_n "checking for library containing ldap_open... " >&6; }
+if ${ac_cv_search_ldap_open+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ldap_open ();
+int
+main ()
+{
+return ldap_open ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' ldap; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_ldap_open=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_ldap_open+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_ldap_open+:} false; then :
+
+else
+ ac_cv_search_ldap_open=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_ldap_open" >&5
+$as_echo "$ac_cv_search_ldap_open" >&6; }
+ac_res=$ac_cv_search_ldap_open
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+ alpine_has_ldap=yes
+
+fi
+
+
+fi
+
+
+ if test "$alpine_has_ldap" = "yes" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Including LDAP Support" >&5
+$as_echo "$as_me: Including LDAP Support" >&6;}
+
+$as_echo "@%:@define ENABLE_LDAP /**/" >>confdefs.h
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we should define LDAP_DEPRECATED" >&5
+$as_echo_n "checking if we should define LDAP_DEPRECATED... " >&6; }
+ if test "$cross_compiling" = yes; then :
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cross compiling: not checking" >&5
+$as_echo "$as_me: WARNING: cross compiling: not checking" >&2;}
+
+
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#include <stdio.h>
+#include <string.h>
+#include <ldap.h>
+int main(void) {
+
+ if (LDAP_VENDOR_VERSION >= 20300)
+ exit(0);
+
+ exit(2);
+}
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+$as_echo "@%:@define LDAP_DEPRECATED 1" >>confdefs.h
+
+
+
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Cannot find LDAP functions! Excluding LDAP support." >&5
+$as_echo "$as_me: Cannot find LDAP functions! Excluding LDAP support." >&6;}
+ fi
+fi
+
+if test "x$alpine_SSLTYPE" != "xnone" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing SSL_library_init" >&5
+$as_echo_n "checking for library containing SSL_library_init... " >&6; }
+if ${ac_cv_search_SSL_library_init+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char SSL_library_init ();
+int
+main ()
+{
+return SSL_library_init ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' ssl; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_SSL_library_init=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_SSL_library_init+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_SSL_library_init+:} false; then :
+
+else
+ ac_cv_search_SSL_library_init=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_SSL_library_init" >&5
+$as_echo "$ac_cv_search_SSL_library_init" >&6; }
+ac_res=$ac_cv_search_SSL_library_init
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+ LIBS="$LIBS -lssl"
+
+fi
+
+ if test "x$alpine_SSLTYPE" = "xnone" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: OpenSSL libraries NOT found" >&5
+$as_echo "$as_me: OpenSSL libraries NOT found" >&6;}
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: OpenSSL libraries FOUND" >&5
+$as_echo "$as_me: OpenSSL libraries FOUND" >&6;}
+ fi
+fi
+
+if test "x$alpine_GSSTYPE" != "xnone" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing gss_init_sec_context" >&5
+$as_echo_n "checking for library containing gss_init_sec_context... " >&6; }
+if ${ac_cv_search_gss_init_sec_context+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char gss_init_sec_context ();
+int
+main ()
+{
+return gss_init_sec_context ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' gss gssapi gssapi_krb5; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_gss_init_sec_context=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_gss_init_sec_context+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_gss_init_sec_context+:} false; then :
+
+else
+ ac_cv_search_gss_init_sec_context=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_gss_init_sec_context" >&5
+$as_echo "$ac_cv_search_gss_init_sec_context" >&6; }
+ac_res=$ac_cv_search_gss_init_sec_context
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+ ac_fn_c_check_header_mongrel "$LINENO" "gssapi/gssapi_generic.h" "ac_cv_header_gssapi_gssapi_generic_h" "$ac_includes_default"
+if test "x$ac_cv_header_gssapi_gssapi_generic_h" = xyes; then :
+
+else
+
+ if test ! -d /usr/kerberos/include ; then
+ alpine_GSSTYPE="none"
+ alpine_gss_none_reason="header files not found"
+ fi
+
+fi
+
+
+
+else
+
+ alpine_GSSTYPE="none"
+ alpine_gss_none_reason="libraries not found"
+
+fi
+
+ if test -n "$alpine_gss_none_reason" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: NOT including Kerberos Support: $alpine_gss_none_reason" >&5
+$as_echo "$as_me: NOT including Kerberos Support: $alpine_gss_none_reason" >&6;}
+ fi
+fi
+
+if test -n "$WEB_BUILD" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing Tcl_Eval" >&5
+$as_echo_n "checking for library containing Tcl_Eval... " >&6; }
+if ${ac_cv_search_Tcl_Eval+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char Tcl_Eval ();
+int
+main ()
+{
+return Tcl_Eval ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' $alpine_TCLLIB tcl8.4 tcl8.3 tcl84 tcl83 tcl; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_Tcl_Eval=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_Tcl_Eval+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_Tcl_Eval+:} false; then :
+
+else
+ ac_cv_search_Tcl_Eval=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_Tcl_Eval" >&5
+$as_echo "$ac_cv_search_Tcl_Eval" >&6; }
+ac_res=$ac_cv_search_Tcl_Eval
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+else
+
+ WEB_BUILD=
+
+fi
+
+
+ if test -n "$alpine_TCLINC" ; then
+ as_ac_Header=`$as_echo "ac_cv_header_$alpine_TCLINC" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$alpine_TCLINC" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+
+else
+
+ WEB_BUILD=
+
+fi
+
+
+ if test -z "$WEB_BUILD" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Tcl Include file NOT found" >&5
+$as_echo "$as_me: Tcl Include file NOT found" >&6;}
+ fi
+ else
+ ac_fn_c_check_header_mongrel "$LINENO" "tcl.h" "ac_cv_header_tcl_h" "$ac_includes_default"
+if test "x$ac_cv_header_tcl_h" = xyes; then :
+
+else
+
+ for dir in tcl8.4 tcl8.3 tcl84 tcl83 ; do
+ as_ac_File=`$as_echo "ac_cv_file_/usr/include/$dir/tcl.h" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for /usr/include/$dir/tcl.h" >&5
+$as_echo_n "checking for /usr/include/$dir/tcl.h... " >&6; }
+if eval \${$as_ac_File+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ test "$cross_compiling" = yes &&
+ as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5
+if test -r "/usr/include/$dir/tcl.h"; then
+ eval "$as_ac_File=yes"
+else
+ eval "$as_ac_File=no"
+fi
+fi
+eval ac_res=\$$as_ac_File
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_File"\" = x"yes"; then :
+
+ found=yes
+
+fi
+
+ if test "$found" = "yes" ; then
+ CPPFLAGS="$CPPFLAGS -I/usr/include/$dir"
+ break
+ fi
+ done
+
+fi
+
+
+ fi
+fi
+
+if test x$alpine_REGEX != "xyes" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing regcomp" >&5
+$as_echo_n "checking for library containing regcomp... " >&6; }
+if ${ac_cv_search_regcomp+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char regcomp ();
+int
+main ()
+{
+return regcomp ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' posix regexp regex re; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_regcomp=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_regcomp+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_regcomp+:} false; then :
+
+else
+ ac_cv_search_regcomp=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_regcomp" >&5
+$as_echo "$ac_cv_search_regcomp" >&6; }
+ac_res=$ac_cv_search_regcomp
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+else
+
+ if test x$alpine_REGEX = "xno" ; then
+ as_fn_error $? "Unable to find system regex library" "$LINENO" 5
+ else
+ alpine_REGEX=yes
+ fi
+
+fi
+
+fi
+if test x$alpine_REGEX != "xyes" ; then
+ ac_fn_c_check_header_mongrel "$LINENO" "regex.h" "ac_cv_header_regex_h" "$ac_includes_default"
+if test "x$ac_cv_header_regex_h" = xyes; then :
+
+else
+
+ if test x$alpine_REGEX = "xno" ; then
+ as_fn_error $? "Unable to find system regex include file" "$LINENO" 5
+ else
+ alpine_REGEX=yes
+ fi
+
+fi
+
+
+fi
+
+$as_echo "@%:@define HAVE_REGEX_H 1" >>confdefs.h
+
+if test x$alpine_REGEX = "xyes" ; then
+ CPPFLAGS="$CPPFLAGS -I${top_builddir}/regex"
+ LDFLAGS="$LDFLAGS -L${top_builddir}/regex -lregex"
+ REGEX_BUILD=regex
+fi
+
+if test "x$with_pthread" != "xno" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread support" >&5
+$as_echo_n "checking for pthread support... " >&6; }
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+acx_pthread_ok=no
+
+# We used to check for pthread.h first, but this fails if pthread.h
+# requires special compiler flags (e.g. on True64 or Sequent).
+# It gets checked for in the link test anyway.
+
+# First of all, check if the user has set any of the PTHREAD_LIBS,
+# etcetera environment variables, and if threads linking works using
+# them:
+if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+ save_LIBS="$LIBS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS" >&5
+$as_echo_n "checking for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS... " >&6; }
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pthread_join ();
+int
+main ()
+{
+return pthread_join ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ acx_pthread_ok=yes
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $acx_pthread_ok" >&5
+$as_echo "$acx_pthread_ok" >&6; }
+ if test x"$acx_pthread_ok" = xno; then
+ PTHREAD_LIBS=""
+ PTHREAD_CFLAGS=""
+ fi
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+fi
+
+# We must check for the threads library under a number of different
+# names; the ordering is very important because some systems
+# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
+# libraries is broken (non-POSIX).
+
+# Create a list of thread flags to try. Items starting with a "-" are
+# C compiler flags, and other items are library names, except for "none"
+# which indicates that we try without any flags at all, and "pthread-config"
+# which is a program returning the flags for the Pth emulation library.
+
+acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
+
+# The ordering *is* (sometimes) important. Some notes on the
+# individual items follow:
+
+# pthreads: AIX (must check this before -lpthread)
+# none: in case threads are in libc; should be tried before -Kthread and
+# other compiler flags to prevent continual compiler warnings
+# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
+# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
+# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
+# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
+# -pthreads: Solaris/gcc
+# -mthreads: Mingw32/gcc, Lynx/gcc
+# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
+# doesn't hurt to check since this sometimes defines pthreads too;
+# also defines -D_REENTRANT)
+# ... -mt is also the pthreads flag for HP/aCC
+# pthread: Linux, etcetera
+# --thread-safe: KAI C++
+# pthread-config: use pthread-config program (for GNU Pth library)
+
+case "${host_cpu}-${host_os}" in
+ *solaris*)
+
+ # On Solaris (at least, for some versions), libc contains stubbed
+ # (non-functional) versions of the pthreads routines, so link-based
+ # tests will erroneously succeed. (We need to link with -pthreads/-mt/
+ # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
+ # a function called by this macro, so we could check for that, but
+ # who knows whether they'll stub that too in a future libc.) So,
+ # we'll just look for -pthreads and -lpthread first:
+
+ acx_pthread_flags="-pthreads pthread -mt -pthread $acx_pthread_flags"
+ ;;
+esac
+
+if test x"$acx_pthread_ok" = xno; then
+for flag in $acx_pthread_flags; do
+
+ case $flag in
+ none)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work without any flags" >&5
+$as_echo_n "checking whether pthreads work without any flags... " >&6; }
+ ;;
+
+ -*)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work with $flag" >&5
+$as_echo_n "checking whether pthreads work with $flag... " >&6; }
+ PTHREAD_CFLAGS="$flag"
+ ;;
+
+ pthread-config)
+ # Extract the first word of "pthread-config", so it can be a program name with args.
+set dummy pthread-config; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_acx_pthread_config+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$acx_pthread_config"; then
+ ac_cv_prog_acx_pthread_config="$acx_pthread_config" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_acx_pthread_config="yes"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_prog_acx_pthread_config" && ac_cv_prog_acx_pthread_config="no"
+fi
+fi
+acx_pthread_config=$ac_cv_prog_acx_pthread_config
+if test -n "$acx_pthread_config"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $acx_pthread_config" >&5
+$as_echo "$acx_pthread_config" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test x"$acx_pthread_config" = xno; then continue; fi
+ PTHREAD_CFLAGS="`pthread-config --cflags`"
+ PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
+ ;;
+
+ *)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for the pthreads library -l$flag" >&5
+$as_echo_n "checking for the pthreads library -l$flag... " >&6; }
+ PTHREAD_LIBS="-l$flag"
+ ;;
+ esac
+
+ save_LIBS="$LIBS"
+ save_CFLAGS="$CFLAGS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+ # Check for various functions. We must include pthread.h,
+ # since some functions may be macros. (On the Sequent, we
+ # need a special flag -Kthread to make this header compile.)
+ # We check for pthread_join because it is in -lpthread on IRIX
+ # while pthread_create is in libc. We check for pthread_attr_init
+ # due to DEC craziness with -lpthreads. We check for
+ # pthread_cleanup_push because it is one of the few pthread
+ # functions on Solaris that doesn't have a non-functional libc stub.
+ # We try pthread_create on general principles.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <pthread.h>
+int
+main ()
+{
+pthread_t th; pthread_join(th, 0);
+ pthread_attr_init(0); pthread_cleanup_push(0, 0);
+ pthread_create(0,0,0,0); pthread_cleanup_pop(0);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ acx_pthread_ok=yes
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $acx_pthread_ok" >&5
+$as_echo "$acx_pthread_ok" >&6; }
+ if test "x$acx_pthread_ok" = xyes; then
+ break;
+ fi
+
+ PTHREAD_LIBS=""
+ PTHREAD_CFLAGS=""
+done
+fi
+
+# Various other checks:
+if test "x$acx_pthread_ok" = xyes; then
+ save_LIBS="$LIBS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+ # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for joinable pthread attribute" >&5
+$as_echo_n "checking for joinable pthread attribute... " >&6; }
+ attr_name=unknown
+ for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <pthread.h>
+int
+main ()
+{
+int attr=$attr; return attr;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ attr_name=$attr; break
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ done
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $attr_name" >&5
+$as_echo "$attr_name" >&6; }
+ if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PTHREAD_CREATE_JOINABLE $attr_name
+_ACEOF
+
+ fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if more special flags are required for pthreads" >&5
+$as_echo_n "checking if more special flags are required for pthreads... " >&6; }
+ flag=no
+ case "${host_cpu}-${host_os}" in
+ *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";;
+ *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
+ esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${flag}" >&5
+$as_echo "${flag}" >&6; }
+ if test "x$flag" != xno; then
+ PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
+ fi
+
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+
+ # More AIX lossage: must compile with xlc_r or cc_r
+ if test x"$GCC" != xyes; then
+ for ac_prog in xlc_r cc_r
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_PTHREAD_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$PTHREAD_CC"; then
+ ac_cv_prog_PTHREAD_CC="$PTHREAD_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_PTHREAD_CC="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+PTHREAD_CC=$ac_cv_prog_PTHREAD_CC
+if test -n "$PTHREAD_CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PTHREAD_CC" >&5
+$as_echo "$PTHREAD_CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$PTHREAD_CC" && break
+done
+test -n "$PTHREAD_CC" || PTHREAD_CC="${CC}"
+
+ else
+ PTHREAD_CC=$CC
+ fi
+else
+ PTHREAD_CC="$CC"
+fi
+
+
+
+
+
+# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
+if test x"$acx_pthread_ok" = xyes; then
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ case "$target" in
+ *openbsd*)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: pthread support on OpenBSD is unstable!" >&5
+$as_echo "$as_me: WARNING: pthread support on OpenBSD is unstable!" >&6;}
+ AM_CFLAGS="$AM_CFLAGS -pthread"
+ ;;
+ esac
+ LIBS="$PTHREAD_LIBS $LIBS"
+ AM_CFLAGS="$AM_CFLAGS $PTHREAD_CFLAGS"
+ CC="$PTHREAD_CC"
+
+$as_echo "@%:@define HAVE_PTHREAD 1" >>confdefs.h
+
+
+ :
+else
+ acx_pthread_ok=no
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing nanosleep" >&5
+$as_echo_n "checking for library containing nanosleep... " >&6; }
+if ${ac_cv_search_nanosleep+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char nanosleep ();
+int
+main ()
+{
+return nanosleep ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' rt posix4; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_nanosleep=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_nanosleep+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_nanosleep+:} false; then :
+
+else
+ ac_cv_search_nanosleep=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_nanosleep" >&5
+$as_echo "$ac_cv_search_nanosleep" >&6; }
+ac_res=$ac_cv_search_nanosleep
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+
+$as_echo "@%:@define HAVE_NANOSLEEP 1" >>confdefs.h
+
+
+fi
+
+fi
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if ${ac_cv_header_stdc+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_header_stdc=yes
+else
+ ac_cv_header_stdc=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "memchr" >/dev/null 2>&1; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "free" >/dev/null 2>&1; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+ if test "$cross_compiling" = yes; then :
+ :
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+ (('a' <= (c) && (c) <= 'i') \
+ || ('j' <= (c) && (c) <= 'r') \
+ || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ if (XOR (islower (i), ISLOWER (i))
+ || toupper (i) != TOUPPER (i))
+ return 2;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+$as_echo "@%:@define STDC_HEADERS 1" >>confdefs.h
+
+fi
+
+ac_header_dirent=no
+for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do
+ as_ac_Header=`$as_echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_hdr that defines DIR" >&5
+$as_echo_n "checking for $ac_hdr that defines DIR... " >&6; }
+if eval \${$as_ac_Header+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <$ac_hdr>
+
+int
+main ()
+{
+if ((DIR *) 0)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ eval "$as_ac_Header=yes"
+else
+ eval "$as_ac_Header=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$as_ac_Header
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define `$as_echo "HAVE_$ac_hdr" | $as_tr_cpp` 1
+_ACEOF
+
+ac_header_dirent=$ac_hdr; break
+fi
+
+done
+# Two versions of opendir et al. are in -ldir and -lx on SCO Xenix.
+if test $ac_header_dirent = dirent.h; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5
+$as_echo_n "checking for library containing opendir... " >&6; }
+if ${ac_cv_search_opendir+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char opendir ();
+int
+main ()
+{
+return opendir ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' dir; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_opendir=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_opendir+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_opendir+:} false; then :
+
+else
+ ac_cv_search_opendir=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5
+$as_echo "$ac_cv_search_opendir" >&6; }
+ac_res=$ac_cv_search_opendir
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5
+$as_echo_n "checking for library containing opendir... " >&6; }
+if ${ac_cv_search_opendir+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char opendir ();
+int
+main ()
+{
+return opendir ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' x; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_opendir=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_opendir+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_opendir+:} false; then :
+
+else
+ ac_cv_search_opendir=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5
+$as_echo "$ac_cv_search_opendir" >&6; }
+ac_res=$ac_cv_search_opendir
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stat file-mode macros are broken" >&5
+$as_echo_n "checking whether stat file-mode macros are broken... " >&6; }
+if ${ac_cv_header_stat_broken+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#if defined S_ISBLK && defined S_IFDIR
+extern char c1[S_ISBLK (S_IFDIR) ? -1 : 1];
+#endif
+
+#if defined S_ISBLK && defined S_IFCHR
+extern char c2[S_ISBLK (S_IFCHR) ? -1 : 1];
+#endif
+
+#if defined S_ISLNK && defined S_IFREG
+extern char c3[S_ISLNK (S_IFREG) ? -1 : 1];
+#endif
+
+#if defined S_ISSOCK && defined S_IFREG
+extern char c4[S_ISSOCK (S_IFREG) ? -1 : 1];
+#endif
+
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_header_stat_broken=no
+else
+ ac_cv_header_stat_broken=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stat_broken" >&5
+$as_echo "$ac_cv_header_stat_broken" >&6; }
+if test $ac_cv_header_stat_broken = yes; then
+
+$as_echo "@%:@define STAT_MACROS_BROKEN 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sys/wait.h that is POSIX.1 compatible" >&5
+$as_echo_n "checking for sys/wait.h that is POSIX.1 compatible... " >&6; }
+if ${ac_cv_header_sys_wait_h+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/wait.h>
+#ifndef WEXITSTATUS
+# define WEXITSTATUS(stat_val) ((unsigned int) (stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+
+int
+main ()
+{
+ int s;
+ wait (&s);
+ s = WIFEXITED (s) ? WEXITSTATUS (s) : 1;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_header_sys_wait_h=yes
+else
+ ac_cv_header_sys_wait_h=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_sys_wait_h" >&5
+$as_echo "$ac_cv_header_sys_wait_h" >&6; }
+if test $ac_cv_header_sys_wait_h = yes; then
+
+$as_echo "@%:@define HAVE_SYS_WAIT_H 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether time.h and sys/time.h may both be included" >&5
+$as_echo_n "checking whether time.h and sys/time.h may both be included... " >&6; }
+if ${ac_cv_header_time+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
+
+int
+main ()
+{
+if ((struct tm *) 0)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_header_time=yes
+else
+ ac_cv_header_time=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_time" >&5
+$as_echo "$ac_cv_header_time" >&6; }
+if test $ac_cv_header_time = yes; then
+
+$as_echo "@%:@define TIME_WITH_SYS_TIME 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether termios.h defines TIOCGWINSZ" >&5
+$as_echo_n "checking whether termios.h defines TIOCGWINSZ... " >&6; }
+if ${ac_cv_sys_tiocgwinsz_in_termios_h+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <termios.h>
+#ifdef TIOCGWINSZ
+ yes
+#endif
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "yes" >/dev/null 2>&1; then :
+ ac_cv_sys_tiocgwinsz_in_termios_h=yes
+else
+ ac_cv_sys_tiocgwinsz_in_termios_h=no
+fi
+rm -f conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_tiocgwinsz_in_termios_h" >&5
+$as_echo "$ac_cv_sys_tiocgwinsz_in_termios_h" >&6; }
+
+if test $ac_cv_sys_tiocgwinsz_in_termios_h != yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether sys/ioctl.h defines TIOCGWINSZ" >&5
+$as_echo_n "checking whether sys/ioctl.h defines TIOCGWINSZ... " >&6; }
+if ${ac_cv_sys_tiocgwinsz_in_sys_ioctl_h+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#ifdef TIOCGWINSZ
+ yes
+#endif
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "yes" >/dev/null 2>&1; then :
+ ac_cv_sys_tiocgwinsz_in_sys_ioctl_h=yes
+else
+ ac_cv_sys_tiocgwinsz_in_sys_ioctl_h=no
+fi
+rm -f conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_tiocgwinsz_in_sys_ioctl_h" >&5
+$as_echo "$ac_cv_sys_tiocgwinsz_in_sys_ioctl_h" >&6; }
+
+ if test $ac_cv_sys_tiocgwinsz_in_sys_ioctl_h = yes; then
+
+$as_echo "@%:@define GWINSZ_IN_SYS_IOCTL 1" >>confdefs.h
+
+ fi
+fi
+
+
+for ac_header in unistd.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "unistd.h" "ac_cv_header_unistd_h" "$ac_includes_default"
+if test "x$ac_cv_header_unistd_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_UNISTD_H 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in errno.h \
+ ctype.h \
+ fcntl.h \
+ signal.h \
+ setjmp.h \
+ memory.h \
+ sys/param.h \
+ sys/socket.h \
+ sys/uio.h \
+ sys/un.h \
+ limits.h \
+ wchar.h \
+ sys/poll.h \
+ stropts.h \
+ netdb.h \
+ syslog.h \
+ sys/syslog.h \
+ locale.h \
+ langinfo.h \
+ utime.h \
+ sys/utime.h \
+ pthread.h \
+ pwd.h \
+ assert.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+ac_fn_c_check_header_mongrel "$LINENO" "termios.h" "ac_cv_header_termios_h" "$ac_includes_default"
+if test "x$ac_cv_header_termios_h" = xyes; then :
+
+$as_echo "@%:@define HAS_TERMIOS 1" >>confdefs.h
+
+else
+
+ ac_fn_c_check_header_mongrel "$LINENO" "termio.h" "ac_cv_header_termio_h" "$ac_includes_default"
+if test "x$ac_cv_header_termio_h" = xyes; then :
+
+$as_echo "@%:@define HAS_TERMIO 1" >>confdefs.h
+
+else
+
+ ac_fn_c_check_header_mongrel "$LINENO" "sgtty.h" "ac_cv_header_sgtty_h" "$ac_includes_default"
+if test "x$ac_cv_header_sgtty_h" = xyes; then :
+
+$as_echo "@%:@define HAS_SGTTY 1" >>confdefs.h
+
+else
+
+ as_fn_error $? "Unable to figure out terminal control method" "$LINENO" 5
+
+fi
+
+
+
+fi
+
+
+
+fi
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking return type of signal handlers" >&5
+$as_echo_n "checking return type of signal handlers... " >&6; }
+if ${ac_cv_type_signal+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <signal.h>
+
+int
+main ()
+{
+return *(signal (0, 0)) (0) == 1;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_type_signal=int
+else
+ ac_cv_type_signal=void
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_signal" >&5
+$as_echo "$ac_cv_type_signal" >&6; }
+
+cat >>confdefs.h <<_ACEOF
+@%:@define RETSIGTYPE $ac_cv_type_signal
+_ACEOF
+
+
+ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default"
+if test "x$ac_cv_type_size_t" = xyes; then :
+
+else
+
+cat >>confdefs.h <<_ACEOF
+@%:@define size_t unsigned int
+_ACEOF
+
+fi
+
+ac_fn_c_check_type "$LINENO" "mode_t" "ac_cv_type_mode_t" "$ac_includes_default"
+if test "x$ac_cv_type_mode_t" = xyes; then :
+
+else
+
+cat >>confdefs.h <<_ACEOF
+@%:@define mode_t int
+_ACEOF
+
+fi
+
+ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default"
+if test "x$ac_cv_type_pid_t" = xyes; then :
+
+else
+
+cat >>confdefs.h <<_ACEOF
+@%:@define pid_t int
+_ACEOF
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for uid_t in sys/types.h" >&5
+$as_echo_n "checking for uid_t in sys/types.h... " >&6; }
+if ${ac_cv_type_uid_t+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "uid_t" >/dev/null 2>&1; then :
+ ac_cv_type_uid_t=yes
+else
+ ac_cv_type_uid_t=no
+fi
+rm -f conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_uid_t" >&5
+$as_echo "$ac_cv_type_uid_t" >&6; }
+if test $ac_cv_type_uid_t = no; then
+
+$as_echo "@%:@define uid_t int" >>confdefs.h
+
+
+$as_echo "@%:@define gid_t int" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether struct tm is in sys/time.h or time.h" >&5
+$as_echo_n "checking whether struct tm is in sys/time.h or time.h... " >&6; }
+if ${ac_cv_struct_tm+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <time.h>
+
+int
+main ()
+{
+struct tm tm;
+ int *p = &tm.tm_sec;
+ return !p;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_struct_tm=time.h
+else
+ ac_cv_struct_tm=sys/time.h
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_struct_tm" >&5
+$as_echo "$ac_cv_struct_tm" >&6; }
+if test $ac_cv_struct_tm = sys/time.h; then
+
+$as_echo "@%:@define TM_IN_SYS_TIME 1" >>confdefs.h
+
+fi
+
+
+ac_fn_c_check_type "$LINENO" "union wait" "ac_cv_type_union_wait" "$ac_includes_default"
+if test "x$ac_cv_type_union_wait" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_UNION_WAIT 1
+_ACEOF
+
+
+fi
+
+
+for ac_header in stdint.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "stdint.h" "ac_cv_header_stdint_h" "$ac_includes_default"
+if test "x$ac_cv_header_stdint_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_STDINT_H 1
+_ACEOF
+ uint16=uint16_t
+else
+
+ for ac_header in inttypes.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "inttypes.h" "ac_cv_header_inttypes_h" "$ac_includes_default"
+if test "x$ac_cv_header_inttypes_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_INTTYPES_H 1
+_ACEOF
+ uint16=uint16_t
+else
+
+ for ac_header in sys/types.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "sys/types.h" "ac_cv_header_sys_types_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_types_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_SYS_TYPES_H 1
+_ACEOF
+ uint16=u_int16_t
+else
+
+ # The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned short" >&5
+$as_echo_n "checking size of unsigned short... " >&6; }
+if ${ac_cv_sizeof_unsigned_short+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned short))" "ac_cv_sizeof_unsigned_short" "$ac_includes_default"; then :
+
+else
+ if test "$ac_cv_type_unsigned_short" = yes; then
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (unsigned short)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_unsigned_short=0
+ fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_short" >&5
+$as_echo "$ac_cv_sizeof_unsigned_short" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SIZEOF_UNSIGNED_SHORT $ac_cv_sizeof_unsigned_short
+_ACEOF
+
+
+ if test $ac_cv_sizeof_unsigned_short -eq 2 ; then
+ uint16="unsigned short"
+ else
+ # The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned int" >&5
+$as_echo_n "checking size of unsigned int... " >&6; }
+if ${ac_cv_sizeof_unsigned_int+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned int))" "ac_cv_sizeof_unsigned_int" "$ac_includes_default"; then :
+
+else
+ if test "$ac_cv_type_unsigned_int" = yes; then
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (unsigned int)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_unsigned_int=0
+ fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_int" >&5
+$as_echo "$ac_cv_sizeof_unsigned_int" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SIZEOF_UNSIGNED_INT $ac_cv_sizeof_unsigned_int
+_ACEOF
+
+
+ if $ac_cv_sizeof_unsigned_int -eq 2 ; then
+ uint16="unsigned int"
+ else
+ as_fn_error $? "Unable to determine 16 bit integer type" "$LINENO" 5
+ fi
+ fi
+
+fi
+
+done
+
+
+fi
+
+done
+
+
+fi
+
+done
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define UINT16 $uint16
+_ACEOF
+
+
+for ac_header in stdint.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "stdint.h" "ac_cv_header_stdint_h" "$ac_includes_default"
+if test "x$ac_cv_header_stdint_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_STDINT_H 1
+_ACEOF
+ uint32=uint32_t
+else
+
+ for ac_header in inttypes.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "inttypes.h" "ac_cv_header_inttypes_h" "$ac_includes_default"
+if test "x$ac_cv_header_inttypes_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_INTTYPES_H 1
+_ACEOF
+ uint32=uint32_t
+else
+
+ for ac_header in sys/types.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "sys/types.h" "ac_cv_header_sys_types_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_types_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_SYS_TYPES_H 1
+_ACEOF
+ uint32=u_int32_t
+else
+
+ # The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned int" >&5
+$as_echo_n "checking size of unsigned int... " >&6; }
+if ${ac_cv_sizeof_unsigned_int+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned int))" "ac_cv_sizeof_unsigned_int" "$ac_includes_default"; then :
+
+else
+ if test "$ac_cv_type_unsigned_int" = yes; then
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (unsigned int)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_unsigned_int=0
+ fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_int" >&5
+$as_echo "$ac_cv_sizeof_unsigned_int" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SIZEOF_UNSIGNED_INT $ac_cv_sizeof_unsigned_int
+_ACEOF
+
+
+ if test $ac_cv_sizeof_unsigned_int -eq 4 ; then
+ uint32="unsigned int"
+ else
+ # The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned long" >&5
+$as_echo_n "checking size of unsigned long... " >&6; }
+if ${ac_cv_sizeof_unsigned_long+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned long))" "ac_cv_sizeof_unsigned_long" "$ac_includes_default"; then :
+
+else
+ if test "$ac_cv_type_unsigned_long" = yes; then
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (unsigned long)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_unsigned_long=0
+ fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_long" >&5
+$as_echo "$ac_cv_sizeof_unsigned_long" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SIZEOF_UNSIGNED_LONG $ac_cv_sizeof_unsigned_long
+_ACEOF
+
+
+ if $ac_cv_sizeof_unsigned_long -eq 4 ; then
+ uint32="unsigned long"
+ else
+ as_fn_error $? "Unable to determine 32 bit integer type" "$LINENO" 5
+ fi
+ fi
+
+fi
+
+done
+
+
+fi
+
+done
+
+
+fi
+
+done
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define UINT32 $uint32
+_ACEOF
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking argument pointer type of qsort compare function and base" >&5
+$as_echo_n "checking argument pointer type of qsort compare function and base... " >&6; }
+if ${ac_cv_func_qsort_argtype+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#if HAVE_STDLIB_H
+#include "stdlib.h"
+#endif
+
+extern void *base;
+extern sortf(const void *, const void *);
+int sortf(a, b)
+ const void *a;
+ const void *b; { return 0; }
+
+int
+main ()
+{
+
+qsort(base, 2, sizeof(char *), sortf);
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_func_qsort_argtype=void
+else
+ ac_cv_func_qsort_argtype=char
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_qsort_argtype" >&5
+$as_echo "$ac_cv_func_qsort_argtype" >&6; }
+
+cat >>confdefs.h <<_ACEOF
+@%:@define qsort_t $ac_cv_func_qsort_argtype
+_ACEOF
+
+
+
+for ac_header in sys/select.h sys/socket.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking types of arguments for select" >&5
+$as_echo_n "checking types of arguments for select... " >&6; }
+if ${ac_cv_func_select_args+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ for ac_arg234 in 'fd_set *' 'int *' 'void *'; do
+ for ac_arg1 in 'int' 'size_t' 'unsigned long int' 'unsigned int'; do
+ for ac_arg5 in 'struct timeval *' 'const struct timeval *'; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_includes_default
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+
+int
+main ()
+{
+extern int select ($ac_arg1,
+ $ac_arg234, $ac_arg234, $ac_arg234,
+ $ac_arg5);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_func_select_args="$ac_arg1,$ac_arg234,$ac_arg5"; break 3
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ done
+ done
+done
+# Provide a safe default value.
+: "${ac_cv_func_select_args=int,int *,struct timeval *}"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_select_args" >&5
+$as_echo "$ac_cv_func_select_args" >&6; }
+ac_save_IFS=$IFS; IFS=','
+set dummy `echo "$ac_cv_func_select_args" | sed 's/\*/\*/g'`
+IFS=$ac_save_IFS
+shift
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SELECT_TYPE_ARG1 $1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SELECT_TYPE_ARG234 ($2)
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SELECT_TYPE_ARG5 ($3)
+_ACEOF
+
+rm -f conftest*
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working strcoll" >&5
+$as_echo_n "checking for working strcoll... " >&6; }
+if ${ac_cv_func_strcoll_works+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then :
+ ac_cv_func_strcoll_works=no
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+return (strcoll ("abc", "def") >= 0 ||
+ strcoll ("ABC", "DEF") >= 0 ||
+ strcoll ("123", "456") >= 0)
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ac_cv_func_strcoll_works=yes
+else
+ ac_cv_func_strcoll_works=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_strcoll_works" >&5
+$as_echo "$ac_cv_func_strcoll_works" >&6; }
+if test $ac_cv_func_strcoll_works = yes; then
+
+$as_echo "@%:@define HAVE_STRCOLL 1" >>confdefs.h
+
+fi
+
+
+
+for ac_header in vfork.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "vfork.h" "ac_cv_header_vfork_h" "$ac_includes_default"
+if test "x$ac_cv_header_vfork_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_VFORK_H 1
+_ACEOF
+
+fi
+
+done
+
+for ac_func in fork vfork
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+if test "x$ac_cv_func_fork" = xyes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working fork" >&5
+$as_echo_n "checking for working fork... " >&6; }
+if ${ac_cv_func_fork_works+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then :
+ ac_cv_func_fork_works=cross
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+
+ /* By Ruediger Kuhlmann. */
+ return fork () < 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ac_cv_func_fork_works=yes
+else
+ ac_cv_func_fork_works=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_fork_works" >&5
+$as_echo "$ac_cv_func_fork_works" >&6; }
+
+else
+ ac_cv_func_fork_works=$ac_cv_func_fork
+fi
+if test "x$ac_cv_func_fork_works" = xcross; then
+ case $host in
+ *-*-amigaos* | *-*-msdosdjgpp*)
+ # Override, as these systems have only a dummy fork() stub
+ ac_cv_func_fork_works=no
+ ;;
+ *)
+ ac_cv_func_fork_works=yes
+ ;;
+ esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5
+$as_echo "$as_me: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&2;}
+fi
+ac_cv_func_vfork_works=$ac_cv_func_vfork
+if test "x$ac_cv_func_vfork" = xyes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working vfork" >&5
+$as_echo_n "checking for working vfork... " >&6; }
+if ${ac_cv_func_vfork_works+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then :
+ ac_cv_func_vfork_works=cross
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+/* Thanks to Paul Eggert for this test. */
+$ac_includes_default
+#include <sys/wait.h>
+#ifdef HAVE_VFORK_H
+# include <vfork.h>
+#endif
+/* On some sparc systems, changes by the child to local and incoming
+ argument registers are propagated back to the parent. The compiler
+ is told about this with #include <vfork.h>, but some compilers
+ (e.g. gcc -O) don't grok <vfork.h>. Test for this by using a
+ static variable whose address is put into a register that is
+ clobbered by the vfork. */
+static void
+#ifdef __cplusplus
+sparc_address_test (int arg)
+# else
+sparc_address_test (arg) int arg;
+#endif
+{
+ static pid_t child;
+ if (!child) {
+ child = vfork ();
+ if (child < 0) {
+ perror ("vfork");
+ _exit(2);
+ }
+ if (!child) {
+ arg = getpid();
+ write(-1, "", 0);
+ _exit (arg);
+ }
+ }
+}
+
+int
+main ()
+{
+ pid_t parent = getpid ();
+ pid_t child;
+
+ sparc_address_test (0);
+
+ child = vfork ();
+
+ if (child == 0) {
+ /* Here is another test for sparc vfork register problems. This
+ test uses lots of local variables, at least as many local
+ variables as main has allocated so far including compiler
+ temporaries. 4 locals are enough for gcc 1.40.3 on a Solaris
+ 4.1.3 sparc, but we use 8 to be safe. A buggy compiler should
+ reuse the register of parent for one of the local variables,
+ since it will think that parent can't possibly be used any more
+ in this routine. Assigning to the local variable will thus
+ munge parent in the parent process. */
+ pid_t
+ p = getpid(), p1 = getpid(), p2 = getpid(), p3 = getpid(),
+ p4 = getpid(), p5 = getpid(), p6 = getpid(), p7 = getpid();
+ /* Convince the compiler that p..p7 are live; otherwise, it might
+ use the same hardware register for all 8 local variables. */
+ if (p != p1 || p != p2 || p != p3 || p != p4
+ || p != p5 || p != p6 || p != p7)
+ _exit(1);
+
+ /* On some systems (e.g. IRIX 3.3), vfork doesn't separate parent
+ from child file descriptors. If the child closes a descriptor
+ before it execs or exits, this munges the parent's descriptor
+ as well. Test for this by closing stdout in the child. */
+ _exit(close(fileno(stdout)) != 0);
+ } else {
+ int status;
+ struct stat st;
+
+ while (wait(&status) != child)
+ ;
+ return (
+ /* Was there some problem with vforking? */
+ child < 0
+
+ /* Did the child fail? (This shouldn't happen.) */
+ || status
+
+ /* Did the vfork/compiler bug occur? */
+ || parent != getpid()
+
+ /* Did the file descriptor bug occur? */
+ || fstat(fileno(stdout), &st) != 0
+ );
+ }
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ac_cv_func_vfork_works=yes
+else
+ ac_cv_func_vfork_works=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_vfork_works" >&5
+$as_echo "$ac_cv_func_vfork_works" >&6; }
+
+fi;
+if test "x$ac_cv_func_fork_works" = xcross; then
+ ac_cv_func_vfork_works=$ac_cv_func_vfork
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5
+$as_echo "$as_me: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&2;}
+fi
+
+if test "x$ac_cv_func_vfork_works" = xyes; then
+
+$as_echo "@%:@define HAVE_WORKING_VFORK 1" >>confdefs.h
+
+else
+
+$as_echo "@%:@define vfork fork" >>confdefs.h
+
+fi
+if test "x$ac_cv_func_fork_works" = xyes; then
+
+$as_echo "@%:@define HAVE_WORKING_FORK 1" >>confdefs.h
+
+fi
+
+for ac_func in strchr \
+ memcpy \
+ strtol \
+ strtoul \
+ select \
+ poll \
+ qsort \
+ getuid \
+ getpwuid \
+ getpwnam \
+ gettimeofday \
+ tmpfile \
+ uname \
+ rename \
+ read \
+ signal \
+ setjmp \
+ chown \
+ wait4 \
+ waitpid \
+ wait \
+ srandom \
+ popen \
+ pclose \
+ fsync \
+ truncate \
+ listen \
+ wcwidth \
+ mbstowcs \
+ wcrtomb \
+ putenv \
+ setenv
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing gethostname" >&5
+$as_echo_n "checking for library containing gethostname... " >&6; }
+if ${ac_cv_search_gethostname+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char gethostname ();
+int
+main ()
+{
+return gethostname ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' nsl; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_gethostname=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_gethostname+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_gethostname+:} false; then :
+
+else
+ ac_cv_search_gethostname=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_gethostname" >&5
+$as_echo "$ac_cv_search_gethostname" >&6; }
+ac_res=$ac_cv_search_gethostname
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing socket" >&5
+$as_echo_n "checking for library containing socket... " >&6; }
+if ${ac_cv_search_socket+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char socket ();
+int
+main ()
+{
+return socket ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' socket; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_socket=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_socket+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_socket+:} false; then :
+
+else
+ ac_cv_search_socket=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_socket" >&5
+$as_echo "$ac_cv_search_socket" >&6; }
+ac_res=$ac_cv_search_socket
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+else
+
+ WEB_BUILD=
+
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing bind" >&5
+$as_echo_n "checking for library containing bind... " >&6; }
+if ${ac_cv_search_bind+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char bind ();
+int
+main ()
+{
+return bind ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' bind; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_bind=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_bind+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_bind+:} false; then :
+
+else
+ ac_cv_search_bind=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_bind" >&5
+$as_echo "$ac_cv_search_bind" >&6; }
+ac_res=$ac_cv_search_bind
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+else
+
+ WEB_BUILD=
+
+fi
+
+
+for ac_func in sigaction sigemptyset sigaddset sigprocmask
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+
+$as_echo "@%:@define POSIX_SIGNALS /**/" >>confdefs.h
+
+
+else
+
+ for ac_func in sigset sigrelse
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+
+$as_echo "@%:@define SYSV_SIGNALS /**/" >>confdefs.h
+
+
+fi
+done
+
+
+fi
+done
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing syslog" >&5
+$as_echo_n "checking for library containing syslog... " >&6; }
+if ${ac_cv_search_syslog+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char syslog ();
+int
+main ()
+{
+return syslog ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' bsd socket inet; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_syslog=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_syslog+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_syslog+:} false; then :
+
+else
+ ac_cv_search_syslog=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_syslog" >&5
+$as_echo "$ac_cv_search_syslog" >&6; }
+ac_res=$ac_cv_search_syslog
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+
+$as_echo "@%:@define HAVE_SYSLOG 1" >>confdefs.h
+
+
+fi
+
+
+
+case "$host" in
+ *-linux-gnu*|*-k*bsd*-gnu*)
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ if test -f /etc/fedora-release ; then
+ systype="LFD"
+ if test -d /etc/pki/tls ; then
+ alpine_c_client_target="lfd"
+ else
+ alpine_c_client_target="lrh"
+ fi
+ elif test -f /etc/mandrake-release ; then
+ systype="LMD"
+ alpine_c_client_target="lmd"
+ elif test -f /etc/redhat-release -o -f /etc/redhat_version ; then
+ systype="LRH"
+ if test -d /etc/pki/tls ; then
+ alpine_c_client_target="lr5"
+ else
+ alpine_c_client_target="lrh"
+ fi
+ elif test -f /etc/debian_release -o -f /etc/debian_version ; then
+ if test -d /etc/osso-af-init ; then
+ systype="LN8"
+ alpine_c_client_target="ln8"
+ else
+ systype="DEB"
+ alpine_c_client_target="ldb"
+ fi
+ elif test -f /etc/SuSE-release ; then
+ systype="LSU"
+ alpine_c_client_target="lsu"
+ else
+ systype="LNX"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pam_start in -lpam" >&5
+$as_echo_n "checking for pam_start in -lpam... " >&6; }
+if ${ac_cv_lib_pam_pam_start+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpam $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pam_start ();
+int
+main ()
+{
+return pam_start ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_pam_pam_start=yes
+else
+ ac_cv_lib_pam_pam_start=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pam_pam_start" >&5
+$as_echo "$ac_cv_lib_pam_pam_start" >&6; }
+if test "x$ac_cv_lib_pam_pam_start" = xyes; then :
+
+ alpine_c_client_target="lnp"
+
+else
+
+ if test -f /etc/shadow ; then
+ alpine_c_client_target="slx"
+ else
+ alpine_c_client_target="lnx"
+ fi
+
+fi
+
+ fi
+ ;;
+ *-apple-darwin*)
+ systype="OSX"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ LIBS="$LIBS -framework Carbon -framework ApplicationServices -framework Security"
+ AM_CFLAGS="$AM_CFLAGS -Dbsd"
+
+$as_echo "@%:@define OSX_TARGET 1" >>confdefs.h
+
+ case "$alpine_os_credential_cache" in
+ no)
+ ;;
+ *)
+
+$as_echo "@%:@define APPLEKEYCHAIN 1" >>confdefs.h
+
+ ;;
+ esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pam_start in -lpam" >&5
+$as_echo_n "checking for pam_start in -lpam... " >&6; }
+if ${ac_cv_lib_pam_pam_start+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpam $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pam_start ();
+int
+main ()
+{
+return pam_start ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_pam_pam_start=yes
+else
+ ac_cv_lib_pam_pam_start=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pam_pam_start" >&5
+$as_echo "$ac_cv_lib_pam_pam_start" >&6; }
+if test "x$ac_cv_lib_pam_pam_start" = xyes; then :
+
+ alpine_c_client_target="oxp"
+
+else
+
+ alpine_c_client_target="osx"
+
+fi
+
+ ;;
+ *-*-solaris*)
+ if test x$GCC = "xyes" ; then
+ systype="GSO"
+ alpine_c_client_target="gso"
+ else
+ systype="SOC"
+ alpine_c_client_target="soc"
+
+$as_echo "@%:@define __EXTENSIONS__ 1" >>confdefs.h
+
+ fi
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ ;;
+ *-*-sunos4*)
+ systype="SUN"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="s40"
+ ;;
+ *-*-sco3.2v5*)
+ if test x$GCC = "xyes" ; then
+ systype="GO5"
+ alpine_c_client_target="go5"
+ else
+ systype="SC5"
+ alpine_c_client_target="sc5"
+ fi
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ ;;
+ *-next-*)
+ systype="NXT"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="nx3"
+ ;;
+ *-*-netbsd*)
+ systype="NEB"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="neb"
+ ;;
+ *-*-dragonfly*)
+ systype="DFB"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="neb"
+ ;;
+ *-*-bsdi*)
+ systype="BSI"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="bsi"
+ ;;
+ *-*-freebsd*)
+ systype="BSF"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="bsf"
+ LIBS="$LIBS $LIBINTL"
+ ;;
+ *-*-openbsd*)
+ systype="BSO"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="bso"
+ LIBS="$LIBS $LIBINTL"
+ ;;
+ *-*-aix5*)
+ systype="A52"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="a52"
+ ;;
+ *-*-aix4*)
+ systype="A41"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="a41"
+ ;;
+ *-*-aix3*)
+ systype="A32"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="a32"
+ ;;
+ *-*UNIX_SV | *-*-sysv5UnixWare7* | *-*OpenUNIX*)
+ systype="UW2"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="uw2"
+ ;;
+ *-*-osf5*)
+ systype="OSF"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="osf"
+ ;;
+ *-*-cygwin)
+ systype="CYG"
+ alpine_path_delim="\\"
+ alpine_mode_readonly="(S_IREAD | S_IWRITE)"
+ alpine_c_client_target="cyg"
+ ;;
+ windows* | *-*-pw32*)
+ systype="WNT"
+ alpine_path_delim="\\"
+ alpine_mode_readonly="(S_IREAD | S_IWRITE)"
+ alpine_c_client_target="wnt"
+
+$as_echo "@%:@define _WINDOWS 1" >>confdefs.h
+
+ ;;
+ *)
+ as_fn_error $? "Unrecognized system: $host" "$LINENO" 5
+ ;;
+esac
+
+if test -n "$alpine_with_local_maildir" ; then
+ alpine_local_maildir=$alpine_with_local_maildir
+elif test -d /var/spool/mail ; then
+ alpine_local_maildir="/var/spool/mail"
+elif test -d /var/mail ; then
+ alpine_local_maildir="/var/mail"
+else
+ alpine_local_maildir="/usr/spool/mail"
+fi
+
+if test -n "$alpine_with_c_client_target" ; then
+ alpine_c_client_target=$alpine_with_c_client_target
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SYSTYPE "$systype"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define C_FILESEP '$alpine_path_delim'
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define S_FILESEP "$alpine_path_delim"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define MAILDIR "$alpine_local_maildir"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define MODE_READONLY $alpine_mode_readonly
+_ACEOF
+
+
+C_CLIENT_TARGET=$alpine_c_client_target
+
+C_CLIENT_WITH_IPV6=$c_client_ip6
+
+if test "x$alpine_SSLTYPE" = "xnone" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * NOT Including SSL Support" >&5
+$as_echo "$as_me: * * * NOT Including SSL Support" >&6;}
+ c_client_specials="${c_client_specials}SSLTYPE=none "
+else
+
+ if test -n "$alpine_SSLCERTS" -a -d "$alpine_SSLCERTS" ; then
+ certdir="$alpine_SSLCERTS"
+ elif test -n "$alpine_SSLDIR" -a -d "${alpine_SSLDIR}/certs" ; then
+ certdir="${alpine_SSLDIR}/certs"
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: SSL Problem: certificate directory not found" >&5
+$as_echo "$as_me: SSL Problem: certificate directory not found" >&6;}
+ fi
+
+ if test "x$with_smime" != "xno" ; then
+ if test -n "$certdir" ; then
+
+$as_echo "@%:@define SMIME /**/" >>confdefs.h
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SMIME_SSLCERTS "$certdir"
+_ACEOF
+
+ fi
+ fi
+
+ if test ! -f ${certdir}/factory.pem ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * SSL file \"${certdir}/factory.pem\" is missing." >&5
+$as_echo "$as_me: * * * SSL file \"${certdir}/factory.pem\" is missing." >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * This might indicate that CA certs did not get properly" >&5
+$as_echo "$as_me: * * * This might indicate that CA certs did not get properly" >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * installed. If you get certificate validation failures" >&5
+$as_echo "$as_me: * * * installed. If you get certificate validation failures" >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * in Alpine, this might be the reason for them." >&5
+$as_echo "$as_me: * * * in Alpine, this might be the reason for them." >&6;}
+ fi
+
+ if test -z "`ls ${certdir} | $EGREP '^@<:@0-9A-Fa-f@:>@{8}\.@<:@0-9@:>@'`" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * No 8-hexdigit symlinks in certificate directory \"${certdir}\"." >&5
+$as_echo "$as_me: * * * No 8-hexdigit symlinks in certificate directory \"${certdir}\"." >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * This might indicate that CA certs did not get properly" >&5
+$as_echo "$as_me: * * * This might indicate that CA certs did not get properly" >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * installed. If you get certificate validation failures" >&5
+$as_echo "$as_me: * * * installed. If you get certificate validation failures" >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * in Alpine, this might be the reason for them." >&5
+$as_echo "$as_me: * * * in Alpine, this might be the reason for them." >&6;}
+ fi
+
+ if test -n "$alpine_SSLDIR" ; then
+ c_client_specials="${c_client_specials}SSLDIR=$alpine_SSLDIR "
+ fi
+
+ if test -n "$alpine_SSLCERTS" ; then
+ c_client_specials="${c_client_specials}SSLCERTS=$alpine_SSLCERTS "
+ fi
+
+ if test -n "$alpine_SSLINCLUDE" ; then
+ c_client_specials="${c_client_specials}SSLINCLUDE=$alpine_SSLINCLUDE "
+ fi
+
+ if test -n "$alpine_SSLLIB" ; then
+ c_client_specials="${c_client_specials}SSLLIB=$alpine_SSLLIB "
+ fi
+
+fi
+
+if test "x$alpine_GSSTYPE" != "xnone" ; then
+ c_client_specials="${c_client_specials}EXTRAAUTHENTICATORS=gss "
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * Including Kerberos5 functionality" >&5
+$as_echo "$as_me: * * * Including Kerberos5 functionality" >&6;}
+fi
+
+if test -n "$CPPFLAGS" ; then
+ alpine_c_client_cflags="$alpine_c_client_cflags ${CPPFLAGS}"
+fi
+if test -n "$CFLAGS" ; then
+ alpine_c_client_cflags="$alpine_c_client_cflags ${CFLAGS}"
+fi
+
+if test -n "$alpine_c_client_cflags" ; then
+ C_CLIENT_CFLAGS=EXTRACFLAGS=\"$alpine_c_client_cflags\"
+
+fi
+
+if test -n "$LDFLAGS" ; then
+ alpine_c_client_ldflags="$alpine_c_client_ldflags ${LDFLAGS}"
+fi
+if test -n "$LIBS" ; then
+ alpine_c_client_ldflags="$alpine_c_client_ldflags ${LIBS}"
+fi
+
+if test -n "$alpine_c_client_ldflags" ; then
+ C_CLIENT_LDFLAGS=EXTRALDFLAGS=\"$alpine_c_client_ldflags\"
+
+fi
+
+if test -n "$alpine_c_client_gccoptlevel" ; then
+ C_CLIENT_GCCOPTLEVEL=GCCOPTLEVEL=\"$alpine_c_client_gccoptlevel\"
+
+fi
+
+C_CLIENT_SPECIALS=$c_client_specials
+
+
+if test -z "$WEB_BUILD" ; then
+ WEB_PUBCOOKIE_BUILD=
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * TCL libraries could not be found." >&5
+$as_echo "$as_me: * * * TCL libraries could not be found." >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * WEB ALPINE COMPONENT WILL NOT BE BUILT." >&5
+$as_echo "$as_me: * * * WEB ALPINE COMPONENT WILL NOT BE BUILT." >&6;}
+else
+ if test -n "$WEB_PUBCOOKIE_BUILD" ; then
+ if test "x$alpine_GSSTYPE" = "xnone" ; then
+ WEB_PUBCOOKIE_BUILD=
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * Kerberos5 support not found." >&5
+$as_echo "$as_me: * * * Kerberos5 support not found." >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * WEB ALPINE PUBCOOKIE COMPONENT WILL NOT BE BUILT." >&5
+$as_echo "$as_me: * * * WEB ALPINE PUBCOOKIE COMPONENT WILL NOT BE BUILT." >&6;}
+ elif test -z "$WEB_BINDIR" ; then
+ WEB_PUBCOOKIE_BUILD=
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * Web Alpine binary directory not provided." >&5
+$as_echo "$as_me: * * * Web Alpine binary directory not provided." >&6;}
+ as_fn_error $? "* * * --with-pubcookie requires --with-web-bin=PATH.
+ Please re-run configure with these options:
+ --with-pubcookie --with-web-bin=/usr/local/libexec/alpine/bin" "$LINENO" 5
+ else
+
+$as_echo "@%:@define PUBCOOKIE 1" >>confdefs.h
+
+ WEB_PUBCOOKIE_LIB=../pubcookie/libauthgssproxy.a
+ WEB_PUBCOOKIE_LINK=gssapi_proxy.l
+ fi
+ fi
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+ac_config_files="$ac_config_files m4/Makefile po/Makefile.in regex/Makefile pith/osdep/Makefile pith/charconv/Makefile pith/Makefile pico/osdep/Makefile pico/Makefile alpine/osdep/Makefile alpine/Makefile web/src/Makefile web/src/pubcookie/Makefile web/src/alpined.d/Makefile Makefile"
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems. If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+ for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+ *) { eval $ac_var=; unset $ac_var;} ;;
+ esac ;;
+ esac
+ done
+
+ (set) 2>&1 |
+ case $as_nl`(ac_space=' '; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ # `set' does not quote correctly, so add quotes: double-quote
+ # substitution turns \\\\ into \\, and sed turns \\ into \.
+ sed -n \
+ "s/'/'\\\\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+ ;; #(
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+) |
+ sed '
+ /^ac_cv_env_/b end
+ t clear
+ :clear
+ s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+ t end
+ s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+ :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+ if test -w "$cache_file"; then
+ if test "x$cache_file" != "x/dev/null"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+$as_echo "$as_me: updating cache $cache_file" >&6;}
+ if test ! -f "$cache_file" || test -h "$cache_file"; then
+ cat confcache >"$cache_file"
+ else
+ case $cache_file in #(
+ */* | ?:*)
+ mv -f confcache "$cache_file"$$ &&
+ mv -f "$cache_file"$$ "$cache_file" ;; #(
+ *)
+ mv -f confcache "$cache_file" ;;
+ esac
+ fi
+ fi
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+ fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+U=
+for ac_i in : $LIB@&t@OBJS; do test "x$ac_i" = x: && continue
+ # 1. Remove the extension, and $U if already installed.
+ ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+ ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
+ # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR
+ # will be set to the directory where LIBOBJS objects are built.
+ as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+ as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIB@&t@OBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+ if test -n "$EXEEXT"; then
+ am__EXEEXT_TRUE=
+ am__EXEEXT_FALSE='#'
+else
+ am__EXEEXT_TRUE='#'
+ am__EXEEXT_FALSE=
+fi
+
+if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then
+ as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then
+ as_fn_error $? "conditional \"AMDEP\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
+ as_fn_error $? "conditional \"am__fastdepCC\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+
+: "${CONFIG_STATUS=./config.status}"
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
+as_write_fail=0
+cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in @%:@(
+ *posix*) :
+ set -o posix ;; @%:@(
+ *) :
+ ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='print -r --'
+ as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='printf %s\n'
+ as_echo_n='printf %s'
+else
+ if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+ as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+ as_echo_n='/usr/ucb/echo -n'
+ else
+ as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+ as_echo_n_body='eval
+ arg=$1;
+ case $arg in @%:@(
+ *"$as_nl"*)
+ expr "X$arg" : "X\\(.*\\)$as_nl";
+ arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+ esac;
+ expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+ '
+ export as_echo_n_body
+ as_echo_n='sh -c $as_echo_n_body as_echo'
+ fi
+ export as_echo_body
+ as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+as_myself=
+case $0 in @%:@((
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there. '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+
+@%:@ as_fn_error STATUS ERROR [LINENO LOG_FD]
+@%:@ ----------------------------------------
+@%:@ Output "`basename @S|@0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+@%:@ provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+@%:@ script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+ as_status=$1; test $as_status -eq 0 && as_status=1
+ if test "$4"; then
+ as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+ fi
+ $as_echo "$as_me: error: $2" >&2
+ as_fn_exit $as_status
+} @%:@ as_fn_error
+
+
+@%:@ as_fn_set_status STATUS
+@%:@ -----------------------
+@%:@ Set @S|@? to STATUS, without forking.
+as_fn_set_status ()
+{
+ return $1
+} @%:@ as_fn_set_status
+
+@%:@ as_fn_exit STATUS
+@%:@ -----------------
+@%:@ Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+ set +e
+ as_fn_set_status $1
+ exit $1
+} @%:@ as_fn_exit
+
+@%:@ as_fn_unset VAR
+@%:@ ---------------
+@%:@ Portably unset VAR.
+as_fn_unset ()
+{
+ { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+@%:@ as_fn_append VAR VALUE
+@%:@ ----------------------
+@%:@ Append the text in VALUE to the end of the definition contained in VAR. Take
+@%:@ advantage of any shell optimizations that allow amortized linear growth over
+@%:@ repeated appends, instead of the typical quadratic growth present in naive
+@%:@ implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+ eval 'as_fn_append ()
+ {
+ eval $1+=\$2
+ }'
+else
+ as_fn_append ()
+ {
+ eval $1=\$$1\$2
+ }
+fi # as_fn_append
+
+@%:@ as_fn_arith ARG...
+@%:@ ------------------
+@%:@ Perform arithmetic evaluation on the ARGs, and store the result in the
+@%:@ global @S|@as_val. Take advantage of shells that can avoid forks. The arguments
+@%:@ must be portable across @S|@(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+ eval 'as_fn_arith ()
+ {
+ as_val=$(( $* ))
+ }'
+else
+ as_fn_arith ()
+ {
+ as_val=`expr "$@" || test $? -eq 1`
+ }
+fi # as_fn_arith
+
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in @%:@(((((
+-n*)
+ case `echo 'xy\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ xy) ECHO_C='\c';;
+ *) echo `echo ksh88 bug on AIX 6.1` > /dev/null
+ ECHO_T=' ';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+ if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -p'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -p'
+ elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+ else
+ as_ln_s='cp -p'
+ fi
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+
+@%:@ as_fn_mkdir_p
+@%:@ -------------
+@%:@ Create "@S|@as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || eval $as_mkdir_p || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} @%:@ as_fn_mkdir_p
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p='mkdir -p "$as_dir"'
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+if test -x / >/dev/null 2>&1; then
+ as_test_x='test -x'
+else
+ if ls -dL / >/dev/null 2>&1; then
+ as_ls_L_option=L
+ else
+ as_ls_L_option=
+ fi
+ as_test_x='
+ eval sh -c '\''
+ if test -d "$1"; then
+ test -d "$1/.";
+ else
+ case $1 in @%:@(
+ -*)set "./$1";;
+ esac;
+ case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in @%:@((
+ ???[sx]*):;;*)false;;esac;fi
+ '\'' sh
+ '
+fi
+as_executable_p=$as_test_x
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+## ----------------------------------- ##
+## Main body of $CONFIG_STATUS script. ##
+## ----------------------------------- ##
+_ASEOF
+test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# Save the log message, to keep $0 and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by alpine $as_me 2.10, which was
+generated by GNU Autoconf 2.68. Invocation command line was
+
+ CONFIG_FILES = $CONFIG_FILES
+ CONFIG_HEADERS = $CONFIG_HEADERS
+ CONFIG_LINKS = $CONFIG_LINKS
+ CONFIG_COMMANDS = $CONFIG_COMMANDS
+ $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+case $ac_config_headers in *"
+"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
+esac
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+config_headers="$ac_config_headers"
+config_commands="$ac_config_commands"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files and other configuration actions
+from templates according to the current configuration. Unless the files
+and actions are specified as TAGs, all are instantiated by default.
+
+Usage: $0 [OPTION]... [TAG]...
+
+ -h, --help print this help, then exit
+ -V, --version print version number and configuration settings, then exit
+ --config print configuration, then exit
+ -q, --quiet, --silent
+ do not print progress messages
+ -d, --debug don't remove temporary files
+ --recheck update $as_me by reconfiguring in the same conditions
+ --file=FILE[:TEMPLATE]
+ instantiate the configuration file FILE
+ --header=FILE[:TEMPLATE]
+ instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Configuration commands:
+$config_commands
+
+Report bugs to <alpine-contact@u.washington.edu>."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
+ac_cs_version="\\
+alpine config.status 2.10
+configured by $0, generated by GNU Autoconf 2.68,
+ with options \\"\$ac_cs_config\\"
+
+Copyright (C) 2010 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+INSTALL='$INSTALL'
+MKDIR_P='$MKDIR_P'
+AWK='$AWK'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+ case $1 in
+ --*=?*)
+ ac_option=`expr "X$1" : 'X\([^=]*\)='`
+ ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+ ac_shift=:
+ ;;
+ --*=)
+ ac_option=`expr "X$1" : 'X\([^=]*\)='`
+ ac_optarg=
+ ac_shift=:
+ ;;
+ *)
+ ac_option=$1
+ ac_optarg=$2
+ ac_shift=shift
+ ;;
+ esac
+
+ case $ac_option in
+ # Handling of the options.
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ ac_cs_recheck=: ;;
+ --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+ $as_echo "$ac_cs_version"; exit ;;
+ --config | --confi | --conf | --con | --co | --c )
+ $as_echo "$ac_cs_config"; exit ;;
+ --debug | --debu | --deb | --de | --d | -d )
+ debug=: ;;
+ --file | --fil | --fi | --f )
+ $ac_shift
+ case $ac_optarg in
+ *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ '') as_fn_error $? "missing file argument" ;;
+ esac
+ as_fn_append CONFIG_FILES " '$ac_optarg'"
+ ac_need_defaults=false;;
+ --header | --heade | --head | --hea )
+ $ac_shift
+ case $ac_optarg in
+ *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ as_fn_append CONFIG_HEADERS " '$ac_optarg'"
+ ac_need_defaults=false;;
+ --he | --h)
+ # Conflict between --help and --header
+ as_fn_error $? "ambiguous option: \`$1'
+Try \`$0 --help' for more information.";;
+ --help | --hel | -h )
+ $as_echo "$ac_cs_usage"; exit ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil | --si | --s)
+ ac_cs_silent=: ;;
+
+ # This is an error.
+ -*) as_fn_error $? "unrecognized option: \`$1'
+Try \`$0 --help' for more information." ;;
+
+ *) as_fn_append ac_config_targets " $1"
+ ac_need_defaults=false ;;
+
+ esac
+ shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+ exec 6>/dev/null
+ ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+ set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+ shift
+ \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
+ CONFIG_SHELL='$SHELL'
+ export CONFIG_SHELL
+ exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+ echo
+ sed 'h;s/./-/g;s/^.../@%:@@%:@ /;s/...$/ @%:@@%:@/;p;x;p;x' <<_ASBOX
+@%:@@%:@ Running $as_me. @%:@@%:@
+_ASBOX
+ $as_echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+#
+# INIT-COMMANDS
+#
+AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"
+
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+sed_quote_subst='$sed_quote_subst'
+double_quote_subst='$double_quote_subst'
+delay_variable_subst='$delay_variable_subst'
+macro_version='`$ECHO "X$macro_version" | $Xsed -e "$delay_single_quote_subst"`'
+macro_revision='`$ECHO "X$macro_revision" | $Xsed -e "$delay_single_quote_subst"`'
+enable_shared='`$ECHO "X$enable_shared" | $Xsed -e "$delay_single_quote_subst"`'
+enable_static='`$ECHO "X$enable_static" | $Xsed -e "$delay_single_quote_subst"`'
+pic_mode='`$ECHO "X$pic_mode" | $Xsed -e "$delay_single_quote_subst"`'
+enable_fast_install='`$ECHO "X$enable_fast_install" | $Xsed -e "$delay_single_quote_subst"`'
+host_alias='`$ECHO "X$host_alias" | $Xsed -e "$delay_single_quote_subst"`'
+host='`$ECHO "X$host" | $Xsed -e "$delay_single_quote_subst"`'
+host_os='`$ECHO "X$host_os" | $Xsed -e "$delay_single_quote_subst"`'
+build_alias='`$ECHO "X$build_alias" | $Xsed -e "$delay_single_quote_subst"`'
+build='`$ECHO "X$build" | $Xsed -e "$delay_single_quote_subst"`'
+build_os='`$ECHO "X$build_os" | $Xsed -e "$delay_single_quote_subst"`'
+SED='`$ECHO "X$SED" | $Xsed -e "$delay_single_quote_subst"`'
+Xsed='`$ECHO "X$Xsed" | $Xsed -e "$delay_single_quote_subst"`'
+GREP='`$ECHO "X$GREP" | $Xsed -e "$delay_single_quote_subst"`'
+EGREP='`$ECHO "X$EGREP" | $Xsed -e "$delay_single_quote_subst"`'
+FGREP='`$ECHO "X$FGREP" | $Xsed -e "$delay_single_quote_subst"`'
+LD='`$ECHO "X$LD" | $Xsed -e "$delay_single_quote_subst"`'
+NM='`$ECHO "X$NM" | $Xsed -e "$delay_single_quote_subst"`'
+LN_S='`$ECHO "X$LN_S" | $Xsed -e "$delay_single_quote_subst"`'
+max_cmd_len='`$ECHO "X$max_cmd_len" | $Xsed -e "$delay_single_quote_subst"`'
+ac_objext='`$ECHO "X$ac_objext" | $Xsed -e "$delay_single_quote_subst"`'
+exeext='`$ECHO "X$exeext" | $Xsed -e "$delay_single_quote_subst"`'
+lt_unset='`$ECHO "X$lt_unset" | $Xsed -e "$delay_single_quote_subst"`'
+lt_SP2NL='`$ECHO "X$lt_SP2NL" | $Xsed -e "$delay_single_quote_subst"`'
+lt_NL2SP='`$ECHO "X$lt_NL2SP" | $Xsed -e "$delay_single_quote_subst"`'
+reload_flag='`$ECHO "X$reload_flag" | $Xsed -e "$delay_single_quote_subst"`'
+reload_cmds='`$ECHO "X$reload_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+OBJDUMP='`$ECHO "X$OBJDUMP" | $Xsed -e "$delay_single_quote_subst"`'
+deplibs_check_method='`$ECHO "X$deplibs_check_method" | $Xsed -e "$delay_single_quote_subst"`'
+file_magic_cmd='`$ECHO "X$file_magic_cmd" | $Xsed -e "$delay_single_quote_subst"`'
+AR='`$ECHO "X$AR" | $Xsed -e "$delay_single_quote_subst"`'
+AR_FLAGS='`$ECHO "X$AR_FLAGS" | $Xsed -e "$delay_single_quote_subst"`'
+STRIP='`$ECHO "X$STRIP" | $Xsed -e "$delay_single_quote_subst"`'
+RANLIB='`$ECHO "X$RANLIB" | $Xsed -e "$delay_single_quote_subst"`'
+old_postinstall_cmds='`$ECHO "X$old_postinstall_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+old_postuninstall_cmds='`$ECHO "X$old_postuninstall_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+old_archive_cmds='`$ECHO "X$old_archive_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+CC='`$ECHO "X$CC" | $Xsed -e "$delay_single_quote_subst"`'
+CFLAGS='`$ECHO "X$CFLAGS" | $Xsed -e "$delay_single_quote_subst"`'
+compiler='`$ECHO "X$compiler" | $Xsed -e "$delay_single_quote_subst"`'
+GCC='`$ECHO "X$GCC" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_pipe='`$ECHO "X$lt_cv_sys_global_symbol_pipe" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_cdecl='`$ECHO "X$lt_cv_sys_global_symbol_to_cdecl" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "X$lt_cv_sys_global_symbol_to_c_name_address" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "X$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $Xsed -e "$delay_single_quote_subst"`'
+objdir='`$ECHO "X$objdir" | $Xsed -e "$delay_single_quote_subst"`'
+SHELL='`$ECHO "X$SHELL" | $Xsed -e "$delay_single_quote_subst"`'
+ECHO='`$ECHO "X$ECHO" | $Xsed -e "$delay_single_quote_subst"`'
+MAGIC_CMD='`$ECHO "X$MAGIC_CMD" | $Xsed -e "$delay_single_quote_subst"`'
+lt_prog_compiler_no_builtin_flag='`$ECHO "X$lt_prog_compiler_no_builtin_flag" | $Xsed -e "$delay_single_quote_subst"`'
+lt_prog_compiler_wl='`$ECHO "X$lt_prog_compiler_wl" | $Xsed -e "$delay_single_quote_subst"`'
+lt_prog_compiler_pic='`$ECHO "X$lt_prog_compiler_pic" | $Xsed -e "$delay_single_quote_subst"`'
+lt_prog_compiler_static='`$ECHO "X$lt_prog_compiler_static" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_prog_compiler_c_o='`$ECHO "X$lt_cv_prog_compiler_c_o" | $Xsed -e "$delay_single_quote_subst"`'
+need_locks='`$ECHO "X$need_locks" | $Xsed -e "$delay_single_quote_subst"`'
+DSYMUTIL='`$ECHO "X$DSYMUTIL" | $Xsed -e "$delay_single_quote_subst"`'
+NMEDIT='`$ECHO "X$NMEDIT" | $Xsed -e "$delay_single_quote_subst"`'
+LIPO='`$ECHO "X$LIPO" | $Xsed -e "$delay_single_quote_subst"`'
+OTOOL='`$ECHO "X$OTOOL" | $Xsed -e "$delay_single_quote_subst"`'
+OTOOL64='`$ECHO "X$OTOOL64" | $Xsed -e "$delay_single_quote_subst"`'
+libext='`$ECHO "X$libext" | $Xsed -e "$delay_single_quote_subst"`'
+shrext_cmds='`$ECHO "X$shrext_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+extract_expsyms_cmds='`$ECHO "X$extract_expsyms_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+archive_cmds_need_lc='`$ECHO "X$archive_cmds_need_lc" | $Xsed -e "$delay_single_quote_subst"`'
+enable_shared_with_static_runtimes='`$ECHO "X$enable_shared_with_static_runtimes" | $Xsed -e "$delay_single_quote_subst"`'
+export_dynamic_flag_spec='`$ECHO "X$export_dynamic_flag_spec" | $Xsed -e "$delay_single_quote_subst"`'
+whole_archive_flag_spec='`$ECHO "X$whole_archive_flag_spec" | $Xsed -e "$delay_single_quote_subst"`'
+compiler_needs_object='`$ECHO "X$compiler_needs_object" | $Xsed -e "$delay_single_quote_subst"`'
+old_archive_from_new_cmds='`$ECHO "X$old_archive_from_new_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+old_archive_from_expsyms_cmds='`$ECHO "X$old_archive_from_expsyms_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+archive_cmds='`$ECHO "X$archive_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+archive_expsym_cmds='`$ECHO "X$archive_expsym_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+module_cmds='`$ECHO "X$module_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+module_expsym_cmds='`$ECHO "X$module_expsym_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+with_gnu_ld='`$ECHO "X$with_gnu_ld" | $Xsed -e "$delay_single_quote_subst"`'
+allow_undefined_flag='`$ECHO "X$allow_undefined_flag" | $Xsed -e "$delay_single_quote_subst"`'
+no_undefined_flag='`$ECHO "X$no_undefined_flag" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_libdir_flag_spec='`$ECHO "X$hardcode_libdir_flag_spec" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_libdir_flag_spec_ld='`$ECHO "X$hardcode_libdir_flag_spec_ld" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_libdir_separator='`$ECHO "X$hardcode_libdir_separator" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_direct='`$ECHO "X$hardcode_direct" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_direct_absolute='`$ECHO "X$hardcode_direct_absolute" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_minus_L='`$ECHO "X$hardcode_minus_L" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_shlibpath_var='`$ECHO "X$hardcode_shlibpath_var" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_automatic='`$ECHO "X$hardcode_automatic" | $Xsed -e "$delay_single_quote_subst"`'
+inherit_rpath='`$ECHO "X$inherit_rpath" | $Xsed -e "$delay_single_quote_subst"`'
+link_all_deplibs='`$ECHO "X$link_all_deplibs" | $Xsed -e "$delay_single_quote_subst"`'
+fix_srcfile_path='`$ECHO "X$fix_srcfile_path" | $Xsed -e "$delay_single_quote_subst"`'
+always_export_symbols='`$ECHO "X$always_export_symbols" | $Xsed -e "$delay_single_quote_subst"`'
+export_symbols_cmds='`$ECHO "X$export_symbols_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+exclude_expsyms='`$ECHO "X$exclude_expsyms" | $Xsed -e "$delay_single_quote_subst"`'
+include_expsyms='`$ECHO "X$include_expsyms" | $Xsed -e "$delay_single_quote_subst"`'
+prelink_cmds='`$ECHO "X$prelink_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+file_list_spec='`$ECHO "X$file_list_spec" | $Xsed -e "$delay_single_quote_subst"`'
+variables_saved_for_relink='`$ECHO "X$variables_saved_for_relink" | $Xsed -e "$delay_single_quote_subst"`'
+need_lib_prefix='`$ECHO "X$need_lib_prefix" | $Xsed -e "$delay_single_quote_subst"`'
+need_version='`$ECHO "X$need_version" | $Xsed -e "$delay_single_quote_subst"`'
+version_type='`$ECHO "X$version_type" | $Xsed -e "$delay_single_quote_subst"`'
+runpath_var='`$ECHO "X$runpath_var" | $Xsed -e "$delay_single_quote_subst"`'
+shlibpath_var='`$ECHO "X$shlibpath_var" | $Xsed -e "$delay_single_quote_subst"`'
+shlibpath_overrides_runpath='`$ECHO "X$shlibpath_overrides_runpath" | $Xsed -e "$delay_single_quote_subst"`'
+libname_spec='`$ECHO "X$libname_spec" | $Xsed -e "$delay_single_quote_subst"`'
+library_names_spec='`$ECHO "X$library_names_spec" | $Xsed -e "$delay_single_quote_subst"`'
+soname_spec='`$ECHO "X$soname_spec" | $Xsed -e "$delay_single_quote_subst"`'
+postinstall_cmds='`$ECHO "X$postinstall_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+postuninstall_cmds='`$ECHO "X$postuninstall_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+finish_cmds='`$ECHO "X$finish_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+finish_eval='`$ECHO "X$finish_eval" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_into_libs='`$ECHO "X$hardcode_into_libs" | $Xsed -e "$delay_single_quote_subst"`'
+sys_lib_search_path_spec='`$ECHO "X$sys_lib_search_path_spec" | $Xsed -e "$delay_single_quote_subst"`'
+sys_lib_dlsearch_path_spec='`$ECHO "X$sys_lib_dlsearch_path_spec" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_action='`$ECHO "X$hardcode_action" | $Xsed -e "$delay_single_quote_subst"`'
+enable_dlopen='`$ECHO "X$enable_dlopen" | $Xsed -e "$delay_single_quote_subst"`'
+enable_dlopen_self='`$ECHO "X$enable_dlopen_self" | $Xsed -e "$delay_single_quote_subst"`'
+enable_dlopen_self_static='`$ECHO "X$enable_dlopen_self_static" | $Xsed -e "$delay_single_quote_subst"`'
+old_striplib='`$ECHO "X$old_striplib" | $Xsed -e "$delay_single_quote_subst"`'
+striplib='`$ECHO "X$striplib" | $Xsed -e "$delay_single_quote_subst"`'
+
+LTCC='$LTCC'
+LTCFLAGS='$LTCFLAGS'
+compiler='$compiler_DEFAULT'
+
+# Quote evaled strings.
+for var in SED \
+GREP \
+EGREP \
+FGREP \
+LD \
+NM \
+LN_S \
+lt_SP2NL \
+lt_NL2SP \
+reload_flag \
+OBJDUMP \
+deplibs_check_method \
+file_magic_cmd \
+AR \
+AR_FLAGS \
+STRIP \
+RANLIB \
+CC \
+CFLAGS \
+compiler \
+lt_cv_sys_global_symbol_pipe \
+lt_cv_sys_global_symbol_to_cdecl \
+lt_cv_sys_global_symbol_to_c_name_address \
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \
+SHELL \
+ECHO \
+lt_prog_compiler_no_builtin_flag \
+lt_prog_compiler_wl \
+lt_prog_compiler_pic \
+lt_prog_compiler_static \
+lt_cv_prog_compiler_c_o \
+need_locks \
+DSYMUTIL \
+NMEDIT \
+LIPO \
+OTOOL \
+OTOOL64 \
+shrext_cmds \
+export_dynamic_flag_spec \
+whole_archive_flag_spec \
+compiler_needs_object \
+with_gnu_ld \
+allow_undefined_flag \
+no_undefined_flag \
+hardcode_libdir_flag_spec \
+hardcode_libdir_flag_spec_ld \
+hardcode_libdir_separator \
+fix_srcfile_path \
+exclude_expsyms \
+include_expsyms \
+file_list_spec \
+variables_saved_for_relink \
+libname_spec \
+library_names_spec \
+soname_spec \
+finish_eval \
+old_striplib \
+striplib; do
+ case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in
+ *[\\\\\\\`\\"\\\$]*)
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$sed_quote_subst\\"\\\`\\\\\\""
+ ;;
+ *)
+ eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+ ;;
+ esac
+done
+
+# Double-quote double-evaled strings.
+for var in reload_cmds \
+old_postinstall_cmds \
+old_postuninstall_cmds \
+old_archive_cmds \
+extract_expsyms_cmds \
+old_archive_from_new_cmds \
+old_archive_from_expsyms_cmds \
+archive_cmds \
+archive_expsym_cmds \
+module_cmds \
+module_expsym_cmds \
+export_symbols_cmds \
+prelink_cmds \
+postinstall_cmds \
+postuninstall_cmds \
+finish_cmds \
+sys_lib_search_path_spec \
+sys_lib_dlsearch_path_spec; do
+ case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in
+ *[\\\\\\\`\\"\\\$]*)
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\""
+ ;;
+ *)
+ eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+ ;;
+ esac
+done
+
+# Fix-up fallback echo if it was mangled by the above quoting rules.
+case \$lt_ECHO in
+*'\\\$0 --fallback-echo"') lt_ECHO=\`\$ECHO "X\$lt_ECHO" | \$Xsed -e 's/\\\\\\\\\\\\\\\$0 --fallback-echo"\$/\$0 --fallback-echo"/'\`
+ ;;
+esac
+
+ac_aux_dir='$ac_aux_dir'
+xsi_shell='$xsi_shell'
+lt_shell_append='$lt_shell_append'
+
+# See if we are running on zsh, and set the options which allow our
+# commands through without removal of \ escapes INIT.
+if test -n "\${ZSH_VERSION+set}" ; then
+ setopt NO_GLOB_SUBST
+fi
+
+
+ PACKAGE='$PACKAGE'
+ VERSION='$VERSION'
+ TIMESTAMP='$TIMESTAMP'
+ RM='$RM'
+ ofile='$ofile'
+
+
+
+# Capture the value of obsolete ALL_LINGUAS because we need it to compute
+ # POFILES, UPDATEPOFILES, DUMMYPOFILES, GMOFILES, CATALOGS. But hide it
+ # from automake < 1.5.
+ eval 'OBSOLETE_ALL_LINGUAS''="$ALL_LINGUAS"'
+ # Capture the value of LINGUAS because we need it to compute CATALOGS.
+ LINGUAS="${LINGUAS-%UNSET%}"
+
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+ case $ac_config_target in
+ "include/config.h") CONFIG_HEADERS="$CONFIG_HEADERS include/config.h" ;;
+ "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;;
+ "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;;
+ "po-directories") CONFIG_COMMANDS="$CONFIG_COMMANDS po-directories" ;;
+ "m4/Makefile") CONFIG_FILES="$CONFIG_FILES m4/Makefile" ;;
+ "po/Makefile.in") CONFIG_FILES="$CONFIG_FILES po/Makefile.in" ;;
+ "regex/Makefile") CONFIG_FILES="$CONFIG_FILES regex/Makefile" ;;
+ "pith/osdep/Makefile") CONFIG_FILES="$CONFIG_FILES pith/osdep/Makefile" ;;
+ "pith/charconv/Makefile") CONFIG_FILES="$CONFIG_FILES pith/charconv/Makefile" ;;
+ "pith/Makefile") CONFIG_FILES="$CONFIG_FILES pith/Makefile" ;;
+ "pico/osdep/Makefile") CONFIG_FILES="$CONFIG_FILES pico/osdep/Makefile" ;;
+ "pico/Makefile") CONFIG_FILES="$CONFIG_FILES pico/Makefile" ;;
+ "alpine/osdep/Makefile") CONFIG_FILES="$CONFIG_FILES alpine/osdep/Makefile" ;;
+ "alpine/Makefile") CONFIG_FILES="$CONFIG_FILES alpine/Makefile" ;;
+ "web/src/Makefile") CONFIG_FILES="$CONFIG_FILES web/src/Makefile" ;;
+ "web/src/pubcookie/Makefile") CONFIG_FILES="$CONFIG_FILES web/src/pubcookie/Makefile" ;;
+ "web/src/alpined.d/Makefile") CONFIG_FILES="$CONFIG_FILES web/src/alpined.d/Makefile" ;;
+ "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+
+ *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+ esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used. Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+ test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+ test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+ test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands
+fi
+
+# Have a temporary directory for convenience. Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+ tmp= ac_tmp=
+ trap 'exit_status=$?
+ : "${ac_tmp:=$tmp}"
+ { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
+' 0
+ trap 'as_fn_exit 1' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+ tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+ test -d "$tmp"
+} ||
+{
+ tmp=./conf$$-$RANDOM
+ (umask 077 && mkdir "$tmp")
+} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
+ac_tmp=$tmp
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr=`echo X | tr X '\015'`
+# On cygwin, bash can eat \r inside `` if the user requested igncr.
+# But we know of no other shell where ac_cr would be empty at this
+# point, so we can use a bashism as a fallback.
+if test "x$ac_cr" = x; then
+ eval ac_cr=\$\'\\r\'
+fi
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+ ac_cs_awk_cr='\\r'
+else
+ ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+ echo "cat >conf$$subs.awk <<_ACEOF" &&
+ echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+ echo "_ACEOF"
+} >conf$$subs.sh ||
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+ . ./conf$$subs.sh ||
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+
+ ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+ if test $ac_delim_n = $ac_delim_num; then
+ break
+ elif $ac_last_try; then
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\)..*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\)..*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+ N
+ s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
+ for (key in S) S_is_set[key] = 1
+ FS = ""
+
+}
+{
+ line = $ 0
+ nfields = split(line, field, "@")
+ substed = 0
+ len = length(field[1])
+ for (i = 2; i < nfields; i++) {
+ key = field[i]
+ keylen = length(key)
+ if (S_is_set[key]) {
+ value = S[key]
+ line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+ len += length(value) + length(field[++i])
+ substed = 1
+ } else
+ len += 1 + keylen
+ }
+
+ print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+ sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+ cat
+fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
+ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
+# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{
+h
+s///
+s/^/:/
+s/[ ]*$/:/
+s/:\$(srcdir):/:/g
+s/:\${srcdir}:/:/g
+s/:@srcdir@:/:/g
+s/^:*//
+s/:*$//
+x
+s/\(=[ ]*\).*/\1/
+G
+s/\n//
+s/^[^=]*=[ ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+# Set up the scripts for CONFIG_HEADERS section.
+# No need to generate them if there are no CONFIG_HEADERS.
+# This happens for instance with `./config.status Makefile'.
+if test -n "$CONFIG_HEADERS"; then
+cat >"$ac_tmp/defines.awk" <<\_ACAWK ||
+BEGIN {
+_ACEOF
+
+# Transform confdefs.h into an awk script `defines.awk', embedded as
+# here-document in config.status, that substitutes the proper values into
+# config.h.in to produce config.h.
+
+# Create a delimiter string that does not exist in confdefs.h, to ease
+# handling of long lines.
+ac_delim='%!_!# '
+for ac_last_try in false false :; do
+ ac_tt=`sed -n "/$ac_delim/p" confdefs.h`
+ if test -z "$ac_tt"; then
+ break
+ elif $ac_last_try; then
+ as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+done
+
+# For the awk script, D is an array of macro values keyed by name,
+# likewise P contains macro parameters if any. Preserve backslash
+# newline sequences.
+
+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
+sed -n '
+s/.\{148\}/&'"$ac_delim"'/g
+t rset
+:rset
+s/^[ ]*#[ ]*define[ ][ ]*/ /
+t def
+d
+:def
+s/\\$//
+t bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3"/p
+s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p
+d
+:bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3\\\\\\n"\\/p
+t cont
+s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
+t cont
+d
+:cont
+n
+s/.\{148\}/&'"$ac_delim"'/g
+t clear
+:clear
+s/\\$//
+t bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/"/p
+d
+:bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
+b cont
+' <confdefs.h | sed '
+s/'"$ac_delim"'/"\\\
+"/g' >>$CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ for (key in D) D_is_set[key] = 1
+ FS = ""
+}
+/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
+ line = \$ 0
+ split(line, arg, " ")
+ if (arg[1] == "#") {
+ defundef = arg[2]
+ mac1 = arg[3]
+ } else {
+ defundef = substr(arg[1], 2)
+ mac1 = arg[2]
+ }
+ split(mac1, mac2, "(") #)
+ macro = mac2[1]
+ prefix = substr(line, 1, index(line, defundef) - 1)
+ if (D_is_set[macro]) {
+ # Preserve the white space surrounding the "#".
+ print prefix "define", macro P[macro] D[macro]
+ next
+ } else {
+ # Replace #undef with comments. This is necessary, for example,
+ # in the case of _POSIX_SOURCE, which is predefined and required
+ # on some systems where configure will not decide to define it.
+ if (defundef == "undef") {
+ print "/*", prefix defundef, macro, "*/"
+ next
+ }
+ }
+}
+{ print }
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
+fi # test -n "$CONFIG_HEADERS"
+
+
+eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS"
+shift
+for ac_tag
+do
+ case $ac_tag in
+ :[FHLC]) ac_mode=$ac_tag; continue;;
+ esac
+ case $ac_mode$ac_tag in
+ :[FHL]*:*);;
+ :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
+ :[FH]-) ac_tag=-:-;;
+ :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+ esac
+ ac_save_IFS=$IFS
+ IFS=:
+ set x $ac_tag
+ IFS=$ac_save_IFS
+ shift
+ ac_file=$1
+ shift
+
+ case $ac_mode in
+ :L) ac_source=$1;;
+ :[FH])
+ ac_file_inputs=
+ for ac_f
+ do
+ case $ac_f in
+ -) ac_f="$ac_tmp/stdin";;
+ *) # Look for the file first in the build tree, then in the source tree
+ # (if the path is not absolute). The absolute path cannot be DOS-style,
+ # because $ac_f cannot contain `:'.
+ test -f "$ac_f" ||
+ case $ac_f in
+ [\\/$]*) false;;
+ *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+ esac ||
+ as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+ esac
+ case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+ as_fn_append ac_file_inputs " '$ac_f'"
+ done
+
+ # Let's still pretend it is `configure' which instantiates (i.e., don't
+ # use $as_me), people would be surprised to read:
+ # /* config.h. Generated by config.status. */
+ configure_input='Generated from '`
+ $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+ `' by configure.'
+ if test x"$ac_file" != x-; then
+ configure_input="$ac_file. $configure_input"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+$as_echo "$as_me: creating $ac_file" >&6;}
+ fi
+ # Neutralize special characters interpreted by sed in replacement strings.
+ case $configure_input in #(
+ *\&* | *\|* | *\\* )
+ ac_sed_conf_input=`$as_echo "$configure_input" |
+ sed 's/[\\\\&|]/\\\\&/g'`;; #(
+ *) ac_sed_conf_input=$configure_input;;
+ esac
+
+ case $ac_tag in
+ *:-:* | *:-) cat >"$ac_tmp/stdin" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
+ esac
+ ;;
+ esac
+
+ ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_file" : 'X\(//\)[^/]' \| \
+ X"$ac_file" : 'X\(//\)$' \| \
+ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$ac_file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ as_dir="$ac_dir"; as_fn_mkdir_p
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+ case $ac_mode in
+ :F)
+ #
+ # CONFIG_FILE
+ #
+
+ case $INSTALL in
+ [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+ *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
+ esac
+ ac_MKDIR_P=$MKDIR_P
+ case $MKDIR_P in
+ [\\/$]* | ?:[\\/]* ) ;;
+ */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;;
+ esac
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+ac_sed_dataroot='
+/datarootdir/ {
+ p
+ q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ ac_datarootdir_hack='
+ s&@datadir@&$datadir&g
+ s&@docdir@&$docdir&g
+ s&@infodir@&$infodir&g
+ s&@localedir@&$localedir&g
+ s&@mandir@&$mandir&g
+ s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+s&@INSTALL@&$ac_INSTALL&;t t
+s&@MKDIR_P@&$ac_MKDIR_P&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
+ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+ { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
+ { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \
+ "$ac_tmp/out"`; test -z "$ac_out"; } &&
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined" >&5
+$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined" >&2;}
+
+ rm -f "$ac_tmp/stdin"
+ case $ac_file in
+ -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
+ *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
+ esac \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ ;;
+ :H)
+ #
+ # CONFIG_HEADER
+ #
+ if test x"$ac_file" != x-; then
+ {
+ $as_echo "/* $configure_input */" \
+ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
+ } >"$ac_tmp/config.h" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
+$as_echo "$as_me: $ac_file is unchanged" >&6;}
+ else
+ rm -f "$ac_file"
+ mv "$ac_tmp/config.h" "$ac_file" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ fi
+ else
+ $as_echo "/* $configure_input */" \
+ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
+ || as_fn_error $? "could not create -" "$LINENO" 5
+ fi
+# Compute "$ac_file"'s index in $config_headers.
+_am_arg="$ac_file"
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+ case $_am_header in
+ $_am_arg | $_am_arg:* )
+ break ;;
+ * )
+ _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+ esac
+done
+echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" ||
+$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$_am_arg" : 'X\(//\)[^/]' \| \
+ X"$_am_arg" : 'X\(//\)$' \| \
+ X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$_am_arg" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`/stamp-h$_am_stamp_count
+ ;;
+
+ :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5
+$as_echo "$as_me: executing $ac_file commands" >&6;}
+ ;;
+ esac
+
+
+ case $ac_file$ac_mode in
+ "depfiles":C) test x"$AMDEP_TRUE" != x"" || {
+ # Autoconf 2.62 quotes --file arguments for eval, but not when files
+ # are listed without --file. Let's play safe and only enable the eval
+ # if we detect the quoting.
+ case $CONFIG_FILES in
+ *\'*) eval set x "$CONFIG_FILES" ;;
+ *) set x $CONFIG_FILES ;;
+ esac
+ shift
+ for mf
+ do
+ # Strip MF so we end up with the name of the file.
+ mf=`echo "$mf" | sed -e 's/:.*$//'`
+ # Check whether this is an Automake generated Makefile or not.
+ # We used to match only the files named `Makefile.in', but
+ # some people rename them; so instead we look at the file content.
+ # Grep'ing the first line is not enough: some people post-process
+ # each Makefile.in and add a new line on top of each file to say so.
+ # Grep'ing the whole file is not good either: AIX grep has a line
+ # limit of 2048, but all sed's we know have understand at least 4000.
+ if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
+ dirpart=`$as_dirname -- "$mf" ||
+$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$mf" : 'X\(//\)[^/]' \| \
+ X"$mf" : 'X\(//\)$' \| \
+ X"$mf" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$mf" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ else
+ continue
+ fi
+ # Extract the definition of DEPDIR, am__include, and am__quote
+ # from the Makefile without running `make'.
+ DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
+ test -z "$DEPDIR" && continue
+ am__include=`sed -n 's/^am__include = //p' < "$mf"`
+ test -z "am__include" && continue
+ am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
+ # When using ansi2knr, U may be empty or an underscore; expand it
+ U=`sed -n 's/^U = //p' < "$mf"`
+ # Find all dependency output files, they are included files with
+ # $(DEPDIR) in their names. We invoke sed twice because it is the
+ # simplest approach to changing $(DEPDIR) to its actual value in the
+ # expansion.
+ for file in `sed -n "
+ s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
+ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
+ # Make sure the directory exists.
+ test -f "$dirpart/$file" && continue
+ fdir=`$as_dirname -- "$file" ||
+$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$file" : 'X\(//\)[^/]' \| \
+ X"$file" : 'X\(//\)$' \| \
+ X"$file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ as_dir=$dirpart/$fdir; as_fn_mkdir_p
+ # echo "creating $dirpart/$file"
+ echo '# dummy' > "$dirpart/$file"
+ done
+ done
+}
+ ;;
+ "libtool":C)
+
+ # See if we are running on zsh, and set the options which allow our
+ # commands through without removal of \ escapes.
+ if test -n "${ZSH_VERSION+set}" ; then
+ setopt NO_GLOB_SUBST
+ fi
+
+ cfgfile="${ofile}T"
+ trap "$RM \"$cfgfile\"; exit 1" 1 2 15
+ $RM "$cfgfile"
+
+ cat <<_LT_EOF >> "$cfgfile"
+#! $SHELL
+
+# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services.
+# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION
+# NOTE: Changes made to this file will be lost: look at ltmain.sh.
+#
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005,
+# 2006, 2007, 2008 Free Software Foundation, Inc.
+# Written by Gordon Matzigkeit, 1996
+#
+# This file is part of GNU Libtool.
+#
+# GNU Libtool 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.
+#
+# As a special exception to the GNU General Public License,
+# if you distribute this file as part of a program or library that
+# is built using GNU Libtool, you may include this file under the
+# same distribution terms that you use for the rest of that program.
+#
+# GNU Libtool 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 GNU Libtool; see the file COPYING. If not, a copy
+# can be downloaded from http://www.gnu.org/licenses/gpl.html, or
+# obtained by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+
+# The names of the tagged configurations supported by this script.
+available_tags=""
+
+# ### BEGIN LIBTOOL CONFIG
+
+# Which release of libtool.m4 was used?
+macro_version=$macro_version
+macro_revision=$macro_revision
+
+# Whether or not to build shared libraries.
+build_libtool_libs=$enable_shared
+
+# Whether or not to build static libraries.
+build_old_libs=$enable_static
+
+# What type of objects to build.
+pic_mode=$pic_mode
+
+# Whether or not to optimize for fast installation.
+fast_install=$enable_fast_install
+
+# The host system.
+host_alias=$host_alias
+host=$host
+host_os=$host_os
+
+# The build system.
+build_alias=$build_alias
+build=$build
+build_os=$build_os
+
+# A sed program that does not truncate output.
+SED=$lt_SED
+
+# Sed that helps us avoid accidentally triggering echo(1) options like -n.
+Xsed="\$SED -e 1s/^X//"
+
+# A grep program that handles long lines.
+GREP=$lt_GREP
+
+# An ERE matcher.
+EGREP=$lt_EGREP
+
+# A literal string matcher.
+FGREP=$lt_FGREP
+
+# A BSD- or MS-compatible name lister.
+NM=$lt_NM
+
+# Whether we need soft or hard links.
+LN_S=$lt_LN_S
+
+# What is the maximum length of a command?
+max_cmd_len=$max_cmd_len
+
+# Object file suffix (normally "o").
+objext=$ac_objext
+
+# Executable file suffix (normally "").
+exeext=$exeext
+
+# whether the shell understands "unset".
+lt_unset=$lt_unset
+
+# turn spaces into newlines.
+SP2NL=$lt_lt_SP2NL
+
+# turn newlines into spaces.
+NL2SP=$lt_lt_NL2SP
+
+# How to create reloadable object files.
+reload_flag=$lt_reload_flag
+reload_cmds=$lt_reload_cmds
+
+# An object symbol dumper.
+OBJDUMP=$lt_OBJDUMP
+
+# Method to check whether dependent libraries are shared objects.
+deplibs_check_method=$lt_deplibs_check_method
+
+# Command to use when deplibs_check_method == "file_magic".
+file_magic_cmd=$lt_file_magic_cmd
+
+# The archiver.
+AR=$lt_AR
+AR_FLAGS=$lt_AR_FLAGS
+
+# A symbol stripping program.
+STRIP=$lt_STRIP
+
+# Commands used to install an old-style archive.
+RANLIB=$lt_RANLIB
+old_postinstall_cmds=$lt_old_postinstall_cmds
+old_postuninstall_cmds=$lt_old_postuninstall_cmds
+
+# A C compiler.
+LTCC=$lt_CC
+
+# LTCC compiler flags.
+LTCFLAGS=$lt_CFLAGS
+
+# Take the output of nm and produce a listing of raw symbols and C names.
+global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe
+
+# Transform the output of nm in a proper C declaration.
+global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl
+
+# Transform the output of nm in a C name address pair.
+global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address
+
+# Transform the output of nm in a C name address pair when lib prefix is needed.
+global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix
+
+# The name of the directory that contains temporary libtool files.
+objdir=$objdir
+
+# Shell to use when invoking shell scripts.
+SHELL=$lt_SHELL
+
+# An echo program that does not interpret backslashes.
+ECHO=$lt_ECHO
+
+# Used to examine libraries when file_magic_cmd begins with "file".
+MAGIC_CMD=$MAGIC_CMD
+
+# Must we lock files when doing compilation?
+need_locks=$lt_need_locks
+
+# Tool to manipulate archived DWARF debug symbol files on Mac OS X.
+DSYMUTIL=$lt_DSYMUTIL
+
+# Tool to change global to local symbols on Mac OS X.
+NMEDIT=$lt_NMEDIT
+
+# Tool to manipulate fat objects and archives on Mac OS X.
+LIPO=$lt_LIPO
+
+# ldd/readelf like tool for Mach-O binaries on Mac OS X.
+OTOOL=$lt_OTOOL
+
+# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4.
+OTOOL64=$lt_OTOOL64
+
+# Old archive suffix (normally "a").
+libext=$libext
+
+# Shared library suffix (normally ".so").
+shrext_cmds=$lt_shrext_cmds
+
+# The commands to extract the exported symbol list from a shared archive.
+extract_expsyms_cmds=$lt_extract_expsyms_cmds
+
+# Variables whose values should be saved in libtool wrapper scripts and
+# restored at link time.
+variables_saved_for_relink=$lt_variables_saved_for_relink
+
+# Do we need the "lib" prefix for modules?
+need_lib_prefix=$need_lib_prefix
+
+# Do we need a version for libraries?
+need_version=$need_version
+
+# Library versioning type.
+version_type=$version_type
+
+# Shared library runtime path variable.
+runpath_var=$runpath_var
+
+# Shared library path variable.
+shlibpath_var=$shlibpath_var
+
+# Is shlibpath searched before the hard-coded library search path?
+shlibpath_overrides_runpath=$shlibpath_overrides_runpath
+
+# Format of library name prefix.
+libname_spec=$lt_libname_spec
+
+# List of archive names. First name is the real one, the rest are links.
+# The last name is the one that the linker finds with -lNAME
+library_names_spec=$lt_library_names_spec
+
+# The coded name of the library, if different from the real name.
+soname_spec=$lt_soname_spec
+
+# Command to use after installation of a shared archive.
+postinstall_cmds=$lt_postinstall_cmds
+
+# Command to use after uninstallation of a shared archive.
+postuninstall_cmds=$lt_postuninstall_cmds
+
+# Commands used to finish a libtool library installation in a directory.
+finish_cmds=$lt_finish_cmds
+
+# As "finish_cmds", except a single script fragment to be evaled but
+# not shown.
+finish_eval=$lt_finish_eval
+
+# Whether we should hardcode library paths into libraries.
+hardcode_into_libs=$hardcode_into_libs
+
+# Compile-time system search path for libraries.
+sys_lib_search_path_spec=$lt_sys_lib_search_path_spec
+
+# Run-time system search path for libraries.
+sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec
+
+# Whether dlopen is supported.
+dlopen_support=$enable_dlopen
+
+# Whether dlopen of programs is supported.
+dlopen_self=$enable_dlopen_self
+
+# Whether dlopen of statically linked programs is supported.
+dlopen_self_static=$enable_dlopen_self_static
+
+# Commands to strip libraries.
+old_striplib=$lt_old_striplib
+striplib=$lt_striplib
+
+
+# The linker used to build libraries.
+LD=$lt_LD
+
+# Commands used to build an old-style archive.
+old_archive_cmds=$lt_old_archive_cmds
+
+# A language specific compiler.
+CC=$lt_compiler
+
+# Is the compiler the GNU compiler?
+with_gcc=$GCC
+
+# Compiler flag to turn off builtin functions.
+no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag
+
+# How to pass a linker flag through the compiler.
+wl=$lt_lt_prog_compiler_wl
+
+# Additional compiler flags for building library objects.
+pic_flag=$lt_lt_prog_compiler_pic
+
+# Compiler flag to prevent dynamic linking.
+link_static_flag=$lt_lt_prog_compiler_static
+
+# Does compiler simultaneously support -c and -o options?
+compiler_c_o=$lt_lt_cv_prog_compiler_c_o
+
+# Whether or not to add -lc for building shared libraries.
+build_libtool_need_lc=$archive_cmds_need_lc
+
+# Whether or not to disallow shared libs when runtime libs are static.
+allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes
+
+# Compiler flag to allow reflexive dlopens.
+export_dynamic_flag_spec=$lt_export_dynamic_flag_spec
+
+# Compiler flag to generate shared objects directly from archives.
+whole_archive_flag_spec=$lt_whole_archive_flag_spec
+
+# Whether the compiler copes with passing no objects directly.
+compiler_needs_object=$lt_compiler_needs_object
+
+# Create an old-style archive from a shared archive.
+old_archive_from_new_cmds=$lt_old_archive_from_new_cmds
+
+# Create a temporary old-style archive to link instead of a shared archive.
+old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds
+
+# Commands used to build a shared archive.
+archive_cmds=$lt_archive_cmds
+archive_expsym_cmds=$lt_archive_expsym_cmds
+
+# Commands used to build a loadable module if different from building
+# a shared archive.
+module_cmds=$lt_module_cmds
+module_expsym_cmds=$lt_module_expsym_cmds
+
+# Whether we are building with GNU ld or not.
+with_gnu_ld=$lt_with_gnu_ld
+
+# Flag that allows shared libraries with undefined symbols to be built.
+allow_undefined_flag=$lt_allow_undefined_flag
+
+# Flag that enforces no undefined symbols.
+no_undefined_flag=$lt_no_undefined_flag
+
+# Flag to hardcode \$libdir into a binary during linking.
+# This must work even if \$libdir does not exist
+hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec
+
+# If ld is used when linking, flag to hardcode \$libdir into a binary
+# during linking. This must work even if \$libdir does not exist.
+hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld
+
+# Whether we need a single "-rpath" flag with a separated argument.
+hardcode_libdir_separator=$lt_hardcode_libdir_separator
+
+# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
+# DIR into the resulting binary.
+hardcode_direct=$hardcode_direct
+
+# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
+# DIR into the resulting binary and the resulting library dependency is
+# "absolute",i.e impossible to change by setting \${shlibpath_var} if the
+# library is relocated.
+hardcode_direct_absolute=$hardcode_direct_absolute
+
+# Set to "yes" if using the -LDIR flag during linking hardcodes DIR
+# into the resulting binary.
+hardcode_minus_L=$hardcode_minus_L
+
+# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
+# into the resulting binary.
+hardcode_shlibpath_var=$hardcode_shlibpath_var
+
+# Set to "yes" if building a shared library automatically hardcodes DIR
+# into the library and all subsequent libraries and executables linked
+# against it.
+hardcode_automatic=$hardcode_automatic
+
+# Set to yes if linker adds runtime paths of dependent libraries
+# to runtime path list.
+inherit_rpath=$inherit_rpath
+
+# Whether libtool must link a program against all its dependency libraries.
+link_all_deplibs=$link_all_deplibs
+
+# Fix the shell variable \$srcfile for the compiler.
+fix_srcfile_path=$lt_fix_srcfile_path
+
+# Set to "yes" if exported symbols are required.
+always_export_symbols=$always_export_symbols
+
+# The commands to list exported symbols.
+export_symbols_cmds=$lt_export_symbols_cmds
+
+# Symbols that should not be listed in the preloaded symbols.
+exclude_expsyms=$lt_exclude_expsyms
+
+# Symbols that must always be exported.
+include_expsyms=$lt_include_expsyms
+
+# Commands necessary for linking programs (against libraries) with templates.
+prelink_cmds=$lt_prelink_cmds
+
+# Specify filename containing input files.
+file_list_spec=$lt_file_list_spec
+
+# How to hardcode a shared library path into an executable.
+hardcode_action=$hardcode_action
+
+# ### END LIBTOOL CONFIG
+
+_LT_EOF
+
+ case $host_os in
+ aix3*)
+ cat <<\_LT_EOF >> "$cfgfile"
+# AIX sometimes has problems with the GCC collect2 program. For some
+# reason, if we set the COLLECT_NAMES environment variable, the problems
+# vanish in a puff of smoke.
+if test "X${COLLECT_NAMES+set}" != Xset; then
+ COLLECT_NAMES=
+ export COLLECT_NAMES
+fi
+_LT_EOF
+ ;;
+ esac
+
+
+ltmain="$ac_aux_dir/ltmain.sh"
+
+
+ # We use sed instead of cat because bash on DJGPP gets confused if
+ # if finds mixed CR/LF and LF-only lines. Since sed operates in
+ # text mode, it properly converts lines to CR/LF. This bash problem
+ # is reportedly fixed, but why not run on old versions too?
+ sed '/^# Generated shell functions inserted here/q' "$ltmain" >> "$cfgfile" \
+ || (rm -f "$cfgfile"; exit 1)
+
+ case $xsi_shell in
+ yes)
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_dirname file append nondir_replacement
+# Compute the dirname of FILE. If nonempty, add APPEND to the result,
+# otherwise set result to NONDIR_REPLACEMENT.
+func_dirname ()
+{
+ case ${1} in
+ */*) func_dirname_result="${1%/*}${2}" ;;
+ * ) func_dirname_result="${3}" ;;
+ esac
+}
+
+# func_basename file
+func_basename ()
+{
+ func_basename_result="${1##*/}"
+}
+
+# func_dirname_and_basename file append nondir_replacement
+# perform func_basename and func_dirname in a single function
+# call:
+# dirname: Compute the dirname of FILE. If nonempty,
+# add APPEND to the result, otherwise set result
+# to NONDIR_REPLACEMENT.
+# value returned in "$func_dirname_result"
+# basename: Compute filename of FILE.
+# value retuned in "$func_basename_result"
+# Implementation must be kept synchronized with func_dirname
+# and func_basename. For efficiency, we do not delegate to
+# those functions but instead duplicate the functionality here.
+func_dirname_and_basename ()
+{
+ case ${1} in
+ */*) func_dirname_result="${1%/*}${2}" ;;
+ * ) func_dirname_result="${3}" ;;
+ esac
+ func_basename_result="${1##*/}"
+}
+
+# func_stripname prefix suffix name
+# strip PREFIX and SUFFIX off of NAME.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+func_stripname ()
+{
+ # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are
+ # positional parameters, so assign one to ordinary parameter first.
+ func_stripname_result=${3}
+ func_stripname_result=${func_stripname_result#"${1}"}
+ func_stripname_result=${func_stripname_result%"${2}"}
+}
+
+# func_opt_split
+func_opt_split ()
+{
+ func_opt_split_opt=${1%%=*}
+ func_opt_split_arg=${1#*=}
+}
+
+# func_lo2o object
+func_lo2o ()
+{
+ case ${1} in
+ *.lo) func_lo2o_result=${1%.lo}.${objext} ;;
+ *) func_lo2o_result=${1} ;;
+ esac
+}
+
+# func_xform libobj-or-source
+func_xform ()
+{
+ func_xform_result=${1%.*}.lo
+}
+
+# func_arith arithmetic-term...
+func_arith ()
+{
+ func_arith_result=$(( $* ))
+}
+
+# func_len string
+# STRING may not start with a hyphen.
+func_len ()
+{
+ func_len_result=${#1}
+}
+
+_LT_EOF
+ ;;
+ *) # Bourne compatible functions.
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_dirname file append nondir_replacement
+# Compute the dirname of FILE. If nonempty, add APPEND to the result,
+# otherwise set result to NONDIR_REPLACEMENT.
+func_dirname ()
+{
+ # Extract subdirectory from the argument.
+ func_dirname_result=`$ECHO "X${1}" | $Xsed -e "$dirname"`
+ if test "X$func_dirname_result" = "X${1}"; then
+ func_dirname_result="${3}"
+ else
+ func_dirname_result="$func_dirname_result${2}"
+ fi
+}
+
+# func_basename file
+func_basename ()
+{
+ func_basename_result=`$ECHO "X${1}" | $Xsed -e "$basename"`
+}
+
+
+# func_stripname prefix suffix name
+# strip PREFIX and SUFFIX off of NAME.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+# func_strip_suffix prefix name
+func_stripname ()
+{
+ case ${2} in
+ .*) func_stripname_result=`$ECHO "X${3}" \
+ | $Xsed -e "s%^${1}%%" -e "s%\\\\${2}\$%%"`;;
+ *) func_stripname_result=`$ECHO "X${3}" \
+ | $Xsed -e "s%^${1}%%" -e "s%${2}\$%%"`;;
+ esac
+}
+
+# sed scripts:
+my_sed_long_opt='1s/^\(-[^=]*\)=.*/\1/;q'
+my_sed_long_arg='1s/^-[^=]*=//'
+
+# func_opt_split
+func_opt_split ()
+{
+ func_opt_split_opt=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_opt"`
+ func_opt_split_arg=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_arg"`
+}
+
+# func_lo2o object
+func_lo2o ()
+{
+ func_lo2o_result=`$ECHO "X${1}" | $Xsed -e "$lo2o"`
+}
+
+# func_xform libobj-or-source
+func_xform ()
+{
+ func_xform_result=`$ECHO "X${1}" | $Xsed -e 's/\.[^.]*$/.lo/'`
+}
+
+# func_arith arithmetic-term...
+func_arith ()
+{
+ func_arith_result=`expr "$@"`
+}
+
+# func_len string
+# STRING may not start with a hyphen.
+func_len ()
+{
+ func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len`
+}
+
+_LT_EOF
+esac
+
+case $lt_shell_append in
+ yes)
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_append var value
+# Append VALUE to the end of shell variable VAR.
+func_append ()
+{
+ eval "$1+=\$2"
+}
+_LT_EOF
+ ;;
+ *)
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_append var value
+# Append VALUE to the end of shell variable VAR.
+func_append ()
+{
+ eval "$1=\$$1\$2"
+}
+
+_LT_EOF
+ ;;
+ esac
+
+
+ sed -n '/^# Generated shell functions inserted here/,$p' "$ltmain" >> "$cfgfile" \
+ || (rm -f "$cfgfile"; exit 1)
+
+ mv -f "$cfgfile" "$ofile" ||
+ (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
+ chmod +x "$ofile"
+
+ ;;
+ "po-directories":C)
+ for ac_file in $CONFIG_FILES; do
+ # Support "outfile[:infile[:infile...]]"
+ case "$ac_file" in
+ *:*) ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ esac
+ # PO directories have a Makefile.in generated from Makefile.in.in.
+ case "$ac_file" in */Makefile.in)
+ # Adjust a relative srcdir.
+ ac_dir=`echo "$ac_file"|sed 's%/[^/][^/]*$%%'`
+ ac_dir_suffix="/`echo "$ac_dir"|sed 's%^\./%%'`"
+ ac_dots=`echo "$ac_dir_suffix"|sed 's%/[^/]*%../%g'`
+ # In autoconf-2.13 it is called $ac_given_srcdir.
+ # In autoconf-2.50 it is called $srcdir.
+ test -n "$ac_given_srcdir" || ac_given_srcdir="$srcdir"
+ case "$ac_given_srcdir" in
+ .) top_srcdir=`echo $ac_dots|sed 's%/$%%'` ;;
+ /*) top_srcdir="$ac_given_srcdir" ;;
+ *) top_srcdir="$ac_dots$ac_given_srcdir" ;;
+ esac
+ # Treat a directory as a PO directory if and only if it has a
+ # POTFILES.in file. This allows packages to have multiple PO
+ # directories under different names or in different locations.
+ if test -f "$ac_given_srcdir/$ac_dir/POTFILES.in"; then
+ rm -f "$ac_dir/POTFILES"
+ test -n "$as_me" && echo "$as_me: creating $ac_dir/POTFILES" || echo "creating $ac_dir/POTFILES"
+ cat "$ac_given_srcdir/$ac_dir/POTFILES.in" | sed -e "/^#/d" -e "/^[ ]*\$/d" -e "s,.*, $top_srcdir/& \\\\," | sed -e "\$s/\(.*\) \\\\/\1/" > "$ac_dir/POTFILES"
+ POMAKEFILEDEPS="POTFILES.in"
+ # ALL_LINGUAS, POFILES, UPDATEPOFILES, DUMMYPOFILES, GMOFILES depend
+ # on $ac_dir but don't depend on user-specified configuration
+ # parameters.
+ if test -f "$ac_given_srcdir/$ac_dir/LINGUAS"; then
+ # The LINGUAS file contains the set of available languages.
+ if test -n "$OBSOLETE_ALL_LINGUAS"; then
+ test -n "$as_me" && echo "$as_me: setting ALL_LINGUAS in configure.in is obsolete" || echo "setting ALL_LINGUAS in configure.in is obsolete"
+ fi
+ ALL_LINGUAS_=`sed -e "/^#/d" -e "s/#.*//" "$ac_given_srcdir/$ac_dir/LINGUAS"`
+ # Hide the ALL_LINGUAS assigment from automake < 1.5.
+ eval 'ALL_LINGUAS''=$ALL_LINGUAS_'
+ POMAKEFILEDEPS="$POMAKEFILEDEPS LINGUAS"
+ else
+ # The set of available languages was given in configure.in.
+ # Hide the ALL_LINGUAS assigment from automake < 1.5.
+ eval 'ALL_LINGUAS''=$OBSOLETE_ALL_LINGUAS'
+ fi
+ # Compute POFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).po)
+ # Compute UPDATEPOFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(lang).po-update)
+ # Compute DUMMYPOFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(lang).nop)
+ # Compute GMOFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).gmo)
+ case "$ac_given_srcdir" in
+ .) srcdirpre= ;;
+ *) srcdirpre='$(srcdir)/' ;;
+ esac
+ POFILES=
+ UPDATEPOFILES=
+ DUMMYPOFILES=
+ GMOFILES=
+ for lang in $ALL_LINGUAS; do
+ POFILES="$POFILES $srcdirpre$lang.po"
+ UPDATEPOFILES="$UPDATEPOFILES $lang.po-update"
+ DUMMYPOFILES="$DUMMYPOFILES $lang.nop"
+ GMOFILES="$GMOFILES $srcdirpre$lang.gmo"
+ done
+ # CATALOGS depends on both $ac_dir and the user's LINGUAS
+ # environment variable.
+ INST_LINGUAS=
+ if test -n "$ALL_LINGUAS"; then
+ for presentlang in $ALL_LINGUAS; do
+ useit=no
+ if test "%UNSET%" != "$LINGUAS"; then
+ desiredlanguages="$LINGUAS"
+ else
+ desiredlanguages="$ALL_LINGUAS"
+ fi
+ for desiredlang in $desiredlanguages; do
+ # Use the presentlang catalog if desiredlang is
+ # a. equal to presentlang, or
+ # b. a variant of presentlang (because in this case,
+ # presentlang can be used as a fallback for messages
+ # which are not translated in the desiredlang catalog).
+ case "$desiredlang" in
+ "$presentlang"*) useit=yes;;
+ esac
+ done
+ if test $useit = yes; then
+ INST_LINGUAS="$INST_LINGUAS $presentlang"
+ fi
+ done
+ fi
+ CATALOGS=
+ if test -n "$INST_LINGUAS"; then
+ for lang in $INST_LINGUAS; do
+ CATALOGS="$CATALOGS $lang.gmo"
+ done
+ fi
+ test -n "$as_me" && echo "$as_me: creating $ac_dir/Makefile" || echo "creating $ac_dir/Makefile"
+ sed -e "/^POTFILES =/r $ac_dir/POTFILES" -e "/^# Makevars/r $ac_given_srcdir/$ac_dir/Makevars" -e "s|@POFILES@|$POFILES|g" -e "s|@UPDATEPOFILES@|$UPDATEPOFILES|g" -e "s|@DUMMYPOFILES@|$DUMMYPOFILES|g" -e "s|@GMOFILES@|$GMOFILES|g" -e "s|@CATALOGS@|$CATALOGS|g" -e "s|@POMAKEFILEDEPS@|$POMAKEFILEDEPS|g" "$ac_dir/Makefile.in" > "$ac_dir/Makefile"
+ for f in "$ac_given_srcdir/$ac_dir"/Rules-*; do
+ if test -f "$f"; then
+ case "$f" in
+ *.orig | *.bak | *~) ;;
+ *) cat "$f" >> "$ac_dir/Makefile" ;;
+ esac
+ fi
+ done
+ fi
+ ;;
+ esac
+ done ;;
+
+ esac
+done # for ac_tag
+
+
+as_fn_exit 0
+_ACEOF
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+ as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded. So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status. When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+ ac_cs_success=:
+ ac_config_status_args=
+ test "$silent" = yes &&
+ ac_config_status_args="$ac_config_status_args --quiet"
+ exec 5>/dev/null
+ $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+ exec 5>>config.log
+ # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+ # would make configure fail if this is the last instruction.
+ $ac_cs_success || as_fn_exit 1
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
diff --git a/autom4te.cache/output.1 b/autom4te.cache/output.1
new file mode 100644
index 00000000..71338f68
--- /dev/null
+++ b/autom4te.cache/output.1
@@ -0,0 +1,21687 @@
+@%:@! /bin/sh
+@%:@ From configure.ac Id: configure.ac 1266 2009-07-14 18:39:12Z hubert@u.washington.edu .
+@%:@ Guess values for system-dependent variables and create Makefiles.
+@%:@ Generated by GNU Autoconf 2.68 for alpine 2.10.
+@%:@
+@%:@ Report bugs to <alpine-contact@u.washington.edu>.
+@%:@
+@%:@
+@%:@ Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
+@%:@ 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software
+@%:@ Foundation, Inc.
+@%:@
+@%:@
+@%:@ This configure script is free software; the Free Software Foundation
+@%:@ gives unlimited permission to copy, distribute and modify it.
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in @%:@(
+ *posix*) :
+ set -o posix ;; @%:@(
+ *) :
+ ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='print -r --'
+ as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='printf %s\n'
+ as_echo_n='printf %s'
+else
+ if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+ as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+ as_echo_n='/usr/ucb/echo -n'
+ else
+ as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+ as_echo_n_body='eval
+ arg=$1;
+ case $arg in @%:@(
+ *"$as_nl"*)
+ expr "X$arg" : "X\\(.*\\)$as_nl";
+ arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+ esac;
+ expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+ '
+ export as_echo_n_body
+ as_echo_n='sh -c $as_echo_n_body as_echo'
+ fi
+ export as_echo_body
+ as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+as_myself=
+case $0 in @%:@((
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there. '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+if test "x$CONFIG_SHELL" = x; then
+ as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '\${1+\"\$@\"}'='\"\$@\"'
+ setopt NO_GLOB_SUBST
+else
+ case \`(set -o) 2>/dev/null\` in @%:@(
+ *posix*) :
+ set -o posix ;; @%:@(
+ *) :
+ ;;
+esac
+fi
+"
+ as_required="as_fn_return () { (exit \$1); }
+as_fn_success () { as_fn_return 0; }
+as_fn_failure () { as_fn_return 1; }
+as_fn_ret_success () { return 0; }
+as_fn_ret_failure () { return 1; }
+
+exitcode=0
+as_fn_success || { exitcode=1; echo as_fn_success failed.; }
+as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
+as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
+as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
+if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
+
+else
+ exitcode=1; echo positional parameters were not saved.
+fi
+test x\$exitcode = x0 || exit 1"
+ as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
+ as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
+ eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
+ test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1
+test \$(( 1 + 1 )) = 2 || exit 1"
+ if (eval "$as_required") 2>/dev/null; then :
+ as_have_required=yes
+else
+ as_have_required=no
+fi
+ if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then :
+
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ as_found=:
+ case $as_dir in @%:@(
+ /*)
+ for as_base in sh bash ksh sh5; do
+ # Try only shells that exist, to save several forks.
+ as_shell=$as_dir/$as_base
+ if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+ { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then :
+ CONFIG_SHELL=$as_shell as_have_required=yes
+ if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then :
+ break 2
+fi
+fi
+ done;;
+ esac
+ as_found=false
+done
+$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
+ { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
+ CONFIG_SHELL=$SHELL as_have_required=yes
+fi; }
+IFS=$as_save_IFS
+
+
+ if test "x$CONFIG_SHELL" != x; then :
+ # We cannot yet assume a decent shell, so we have to provide a
+ # neutralization value for shells without unset; and this also
+ # works around shells that cannot unset nonexistent variables.
+ # Preserve -v and -x to the replacement shell.
+ BASH_ENV=/dev/null
+ ENV=/dev/null
+ (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+ export CONFIG_SHELL
+ case $- in @%:@ ((((
+ *v*x* | *x*v* ) as_opts=-vx ;;
+ *v* ) as_opts=-v ;;
+ *x* ) as_opts=-x ;;
+ * ) as_opts= ;;
+ esac
+ exec "$CONFIG_SHELL" $as_opts "$as_myself" ${1+"$@"}
+fi
+
+ if test x$as_have_required = xno; then :
+ $as_echo "$0: This script requires a shell more modern than all"
+ $as_echo "$0: the shells that I found on your system."
+ if test x${ZSH_VERSION+set} = xset ; then
+ $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
+ $as_echo "$0: be upgraded to zsh 4.3.4 or later."
+ else
+ $as_echo "$0: Please tell bug-autoconf@gnu.org and
+$0: alpine-contact@u.washington.edu about your system,
+$0: including any error possibly output before this
+$0: message. Then install a modern shell, or manually run
+$0: the script under such a shell if you do have one."
+ fi
+ exit 1
+fi
+fi
+fi
+SHELL=${CONFIG_SHELL-/bin/sh}
+export SHELL
+# Unset more variables known to interfere with behavior of common tools.
+CLICOLOR_FORCE= GREP_OPTIONS=
+unset CLICOLOR_FORCE GREP_OPTIONS
+
+## --------------------- ##
+## M4sh Shell Functions. ##
+## --------------------- ##
+@%:@ as_fn_unset VAR
+@%:@ ---------------
+@%:@ Portably unset VAR.
+as_fn_unset ()
+{
+ { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+@%:@ as_fn_set_status STATUS
+@%:@ -----------------------
+@%:@ Set @S|@? to STATUS, without forking.
+as_fn_set_status ()
+{
+ return $1
+} @%:@ as_fn_set_status
+
+@%:@ as_fn_exit STATUS
+@%:@ -----------------
+@%:@ Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+ set +e
+ as_fn_set_status $1
+ exit $1
+} @%:@ as_fn_exit
+
+@%:@ as_fn_mkdir_p
+@%:@ -------------
+@%:@ Create "@S|@as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || eval $as_mkdir_p || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} @%:@ as_fn_mkdir_p
+@%:@ as_fn_append VAR VALUE
+@%:@ ----------------------
+@%:@ Append the text in VALUE to the end of the definition contained in VAR. Take
+@%:@ advantage of any shell optimizations that allow amortized linear growth over
+@%:@ repeated appends, instead of the typical quadratic growth present in naive
+@%:@ implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+ eval 'as_fn_append ()
+ {
+ eval $1+=\$2
+ }'
+else
+ as_fn_append ()
+ {
+ eval $1=\$$1\$2
+ }
+fi # as_fn_append
+
+@%:@ as_fn_arith ARG...
+@%:@ ------------------
+@%:@ Perform arithmetic evaluation on the ARGs, and store the result in the
+@%:@ global @S|@as_val. Take advantage of shells that can avoid forks. The arguments
+@%:@ must be portable across @S|@(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+ eval 'as_fn_arith ()
+ {
+ as_val=$(( $* ))
+ }'
+else
+ as_fn_arith ()
+ {
+ as_val=`expr "$@" || test $? -eq 1`
+ }
+fi # as_fn_arith
+
+
+@%:@ as_fn_error STATUS ERROR [LINENO LOG_FD]
+@%:@ ----------------------------------------
+@%:@ Output "`basename @S|@0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+@%:@ provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+@%:@ script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+ as_status=$1; test $as_status -eq 0 && as_status=1
+ if test "$4"; then
+ as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+ fi
+ $as_echo "$as_me: error: $2" >&2
+ as_fn_exit $as_status
+} @%:@ as_fn_error
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+ as_lineno_1=$LINENO as_lineno_1a=$LINENO
+ as_lineno_2=$LINENO as_lineno_2a=$LINENO
+ eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
+ test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
+ # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-)
+ sed -n '
+ p
+ /[$]LINENO/=
+ ' <$as_myself |
+ sed '
+ s/[$]LINENO.*/&-/
+ t lineno
+ b
+ :lineno
+ N
+ :loop
+ s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+ t loop
+ s/-\n.*//
+ ' >$as_me.lineno &&
+ chmod +x "$as_me.lineno" ||
+ { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensitive to this).
+ . "./$as_me.lineno"
+ # Exit status is that of the last command.
+ exit
+}
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in @%:@(((((
+-n*)
+ case `echo 'xy\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ xy) ECHO_C='\c';;
+ *) echo `echo ksh88 bug on AIX 6.1` > /dev/null
+ ECHO_T=' ';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+ if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -p'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -p'
+ elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+ else
+ as_ln_s='cp -p'
+ fi
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p='mkdir -p "$as_dir"'
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+if test -x / >/dev/null 2>&1; then
+ as_test_x='test -x'
+else
+ if ls -dL / >/dev/null 2>&1; then
+ as_ls_L_option=L
+ else
+ as_ls_L_option=
+ fi
+ as_test_x='
+ eval sh -c '\''
+ if test -d "$1"; then
+ test -d "$1/.";
+ else
+ case $1 in @%:@(
+ -*)set "./$1";;
+ esac;
+ case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in @%:@((
+ ???[sx]*):;;*)false;;esac;fi
+ '\'' sh
+ '
+fi
+as_executable_p=$as_test_x
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+
+# Check that we are running under the correct shell.
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+case X$lt_ECHO in
+X*--fallback-echo)
+ # Remove one level of quotation (which was required for Make).
+ ECHO=`echo "$lt_ECHO" | sed 's,\\\\\$\\$0,'$0','`
+ ;;
+esac
+
+ECHO=${lt_ECHO-echo}
+if test "X$1" = X--no-reexec; then
+ # Discard the --no-reexec flag, and continue.
+ shift
+elif test "X$1" = X--fallback-echo; then
+ # Avoid inline document here, it may be left over
+ :
+elif test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' ; then
+ # Yippee, $ECHO works!
+ :
+else
+ # Restart under the correct shell.
+ exec $SHELL "$0" --no-reexec ${1+"$@"}
+fi
+
+if test "X$1" = X--fallback-echo; then
+ # used as fallback echo
+ shift
+ cat <<_LT_EOF
+$*
+_LT_EOF
+ exit 0
+fi
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+if test -z "$lt_ECHO"; then
+ if test "X${echo_test_string+set}" != Xset; then
+ # find a string as large as possible, as long as the shell can cope with it
+ for cmd in 'sed 50q "$0"' 'sed 20q "$0"' 'sed 10q "$0"' 'sed 2q "$0"' 'echo test'; do
+ # expected sizes: less than 2Kb, 1Kb, 512 bytes, 16 bytes, ...
+ if { echo_test_string=`eval $cmd`; } 2>/dev/null &&
+ { test "X$echo_test_string" = "X$echo_test_string"; } 2>/dev/null
+ then
+ break
+ fi
+ done
+ fi
+
+ if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ :
+ else
+ # The Solaris, AIX, and Digital Unix default echo programs unquote
+ # backslashes. This makes it impossible to quote backslashes using
+ # echo "$something" | sed 's/\\/\\\\/g'
+ #
+ # So, first we look for a working echo in the user's PATH.
+
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ for dir in $PATH /usr/ucb; do
+ IFS="$lt_save_ifs"
+ if (test -f $dir/echo || test -f $dir/echo$ac_exeext) &&
+ test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`($dir/echo "$echo_test_string") 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ ECHO="$dir/echo"
+ break
+ fi
+ done
+ IFS="$lt_save_ifs"
+
+ if test "X$ECHO" = Xecho; then
+ # We didn't find a better echo, so look for alternatives.
+ if test "X`{ print -r '\t'; } 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`{ print -r "$echo_test_string"; } 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ # This shell has a builtin print -r that does the trick.
+ ECHO='print -r'
+ elif { test -f /bin/ksh || test -f /bin/ksh$ac_exeext; } &&
+ test "X$CONFIG_SHELL" != X/bin/ksh; then
+ # If we have ksh, try running configure again with it.
+ ORIGINAL_CONFIG_SHELL=${CONFIG_SHELL-/bin/sh}
+ export ORIGINAL_CONFIG_SHELL
+ CONFIG_SHELL=/bin/ksh
+ export CONFIG_SHELL
+ exec $CONFIG_SHELL "$0" --no-reexec ${1+"$@"}
+ else
+ # Try using printf.
+ ECHO='printf %s\n'
+ if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ # Cool, printf works
+ :
+ elif echo_testing_string=`($ORIGINAL_CONFIG_SHELL "$0" --fallback-echo '\t') 2>/dev/null` &&
+ test "X$echo_testing_string" = 'X\t' &&
+ echo_testing_string=`($ORIGINAL_CONFIG_SHELL "$0" --fallback-echo "$echo_test_string") 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ CONFIG_SHELL=$ORIGINAL_CONFIG_SHELL
+ export CONFIG_SHELL
+ SHELL="$CONFIG_SHELL"
+ export SHELL
+ ECHO="$CONFIG_SHELL $0 --fallback-echo"
+ elif echo_testing_string=`($CONFIG_SHELL "$0" --fallback-echo '\t') 2>/dev/null` &&
+ test "X$echo_testing_string" = 'X\t' &&
+ echo_testing_string=`($CONFIG_SHELL "$0" --fallback-echo "$echo_test_string") 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ ECHO="$CONFIG_SHELL $0 --fallback-echo"
+ else
+ # maybe with a smaller string...
+ prev=:
+
+ for cmd in 'echo test' 'sed 2q "$0"' 'sed 10q "$0"' 'sed 20q "$0"' 'sed 50q "$0"'; do
+ if { test "X$echo_test_string" = "X`eval $cmd`"; } 2>/dev/null
+ then
+ break
+ fi
+ prev="$cmd"
+ done
+
+ if test "$prev" != 'sed 50q "$0"'; then
+ echo_test_string=`eval $prev`
+ export echo_test_string
+ exec ${ORIGINAL_CONFIG_SHELL-${CONFIG_SHELL-/bin/sh}} "$0" ${1+"$@"}
+ else
+ # Oops. We lost completely, so just stick with echo.
+ ECHO=echo
+ fi
+ fi
+ fi
+ fi
+ fi
+fi
+
+# Copy echo and quote the copy suitably for passing to libtool from
+# the Makefile, instead of quoting the original, which is used later.
+lt_ECHO=$ECHO
+if test "X$lt_ECHO" = "X$CONFIG_SHELL $0 --fallback-echo"; then
+ lt_ECHO="$CONFIG_SHELL \\\$\$0 --fallback-echo"
+fi
+
+
+
+
+test -n "$DJDIR" || exec 7<&0 </dev/null
+exec 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIB@&t@OBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+
+# Identity of this package.
+PACKAGE_NAME='alpine'
+PACKAGE_TARNAME='alpine'
+PACKAGE_VERSION='2.10'
+PACKAGE_STRING='alpine 2.10'
+PACKAGE_BUGREPORT='alpine-contact@u.washington.edu'
+PACKAGE_URL=''
+
+ac_unique_file="include/system.h"
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_STRING_H
+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+# include <memory.h>
+# endif
+# include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+gt_needs=
+ac_subst_vars='am__EXEEXT_FALSE
+am__EXEEXT_TRUE
+LTLIBOBJS
+LIB@&t@OBJS
+AM_LDFLAGS
+AM_CFLAGS
+WEB_PUBCOOKIE_LINK
+WEB_PUBCOOKIE_LIB
+WEB_PUBCOOKIE_BUILD
+WEB_BINDIR
+WEB_BUILD
+REGEX_BUILD
+C_CLIENT_SPECIALS
+C_CLIENT_GCCOPTLEVEL
+C_CLIENT_LDFLAGS
+C_CLIENT_CFLAGS
+C_CLIENT_WITH_IPV6
+C_CLIENT_TARGET
+PTHREAD_CFLAGS
+PTHREAD_LIBS
+PTHREAD_CC
+acx_pthread_config
+alpine_interactive_spellcheck
+ISPELLPROG
+alpine_simple_spellcheck
+SPELLPROG
+PWPROG
+NPA_PROG
+SENDMAIL
+POSUB
+LTLIBINTL
+LIBINTL
+INTLLIBS
+LTLIBICONV
+LIBICONV
+INTL_MACOSX_LIBS
+MSGMERGE
+XGETTEXT_015
+XGETTEXT
+GMSGFMT_015
+MSGFMT_015
+GMSGFMT
+MSGFMT
+USE_NLS
+MAKE
+LN
+CP
+RM
+CPP
+OTOOL64
+OTOOL
+LIPO
+NMEDIT
+DSYMUTIL
+lt_ECHO
+AR
+OBJDUMP
+NM
+ac_ct_DUMPBIN
+DUMPBIN
+LD
+FGREP
+EGREP
+GREP
+SED
+LIBTOOL
+RANLIB
+LN_S
+am__fastdepCC_FALSE
+am__fastdepCC_TRUE
+CCDEPMODE
+AMDEPBACKSLASH
+AMDEP_FALSE
+AMDEP_TRUE
+am__quote
+am__include
+DEPDIR
+OBJEXT
+EXEEXT
+ac_ct_CC
+CPPFLAGS
+LDFLAGS
+CFLAGS
+CC
+host_os
+host_vendor
+host_cpu
+host
+build_os
+build_vendor
+build_cpu
+build
+MAINT
+MAINTAINER_MODE_FALSE
+MAINTAINER_MODE_TRUE
+am__untar
+am__tar
+AMTAR
+am__leading_dot
+SET_MAKE
+AWK
+mkdir_p
+MKDIR_P
+INSTALL_STRIP_PROGRAM
+STRIP
+install_sh
+MAKEINFO
+AUTOHEADER
+AUTOMAKE
+AUTOCONF
+ACLOCAL
+VERSION
+PACKAGE
+CYGPATH_W
+am__isrc
+INSTALL_DATA
+INSTALL_SCRIPT
+INSTALL_PROGRAM
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_URL
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+enable_maintainer_mode
+enable_dependency_tracking
+enable_shared
+enable_static
+with_pic
+enable_fast_install
+with_gnu_ld
+enable_libtool_lock
+enable_nls
+enable_rpath
+with_libiconv_prefix
+with_libintl_prefix
+enable_dmalloc
+with_dmalloc_dir
+with_localedir
+enable_osx_universal_binaries
+with_include_path
+with_lib_path
+with_pubcookie
+with_web_bin
+enable_debug
+enable_optimization
+enable_mouse
+enable_quotas
+enable_from_changing
+enable_background_post
+enable_keyboard_lock
+enable_from_encoding
+with_smtp_msa
+with_smtp_msa_flags
+with_npa
+with_npa_flags
+with_password_prog
+with_simple_spellcheck
+with_interactive_spellcheck
+with_system_pinerc
+with_system_fixed_pinerc
+with_mailcheck_interval
+with_checkpoint_interval
+with_checkpoint_frequency
+with_display_rows
+with_display_columns
+with_max_display_rows
+with_max_display_columns
+with_fill_column
+with_max_fill_column
+with_debug_level
+with_debug_files
+with_debug_file
+with_forwarded_keyword
+with_display_overlap
+with_display_margin
+with_default_fcc
+with_default_save_folder
+with_default_legacy_postponed_folder
+with_default_postponed_folder
+with_default_trash_folder
+with_default_interrupted_mail
+with_default_dead_letter_folder
+with_default_mail_directory
+with_default_inbox_name
+with_default_signature_file
+with_default_elm_style_save
+with_default_header_in_reply
+with_default_old_style_reply
+with_default_use_only_domain_name
+with_default_save_by_sender
+with_default_sort_key
+with_default_addressbook_sort_rule
+with_default_folder_sort_rule
+with_default_saved_message_name_rule
+with_default_fcc_rule
+with_default_standard_printer
+with_default_ansi_printer
+with_default_addressbook
+with_default_local_fullname
+with_default_local_address
+with_default_keyboard_lock_count
+with_default_remote_addressbook_history
+with_smime_public_cert_directory
+with_smime_private_key_directory
+with_smime_cacert_directory
+with_default_printer
+with_passfile
+with_local_password_cache
+with_local_password_cache_method
+with_default_sshpath
+with_default_sshcmd
+with_ssl
+with_ssl_dir
+with_ssl_certs_dir
+with_ssl_include_dir
+with_ssl_lib_dir
+with_krb5
+with_krb5_dir
+with_krb5_include_dir
+with_krb5_lib_dir
+with_ldap
+with_ldap_dir
+with_ldap_include_dir
+with_ldap_lib_dir
+with_smime
+with_tcl
+with_tcl_lib
+with_tcl_include
+with_supplied_regex
+with_pthread
+with_system_mail_directory
+with_c_client_target
+with_ipv6
+'
+ ac_precious_vars='build_alias
+host_alias
+target_alias
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+CPP'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval $ac_prev=\$ac_option
+ ac_prev=
+ continue
+ fi
+
+ case $ac_option in
+ *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+ *=) ac_optarg= ;;
+ *) ac_optarg=yes ;;
+ esac
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case $ac_dashdash$ac_option in
+ --)
+ ac_dashdash=yes ;;
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir=$ac_optarg ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build_alias ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build_alias=$ac_optarg ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file=$ac_optarg ;;
+
+ --config-cache | -C)
+ cache_file=config.cache ;;
+
+ -datadir | --datadir | --datadi | --datad)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=*)
+ datadir=$ac_optarg ;;
+
+ -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+ | --dataroo | --dataro | --datar)
+ ac_prev=datarootdir ;;
+ -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+ datarootdir=$ac_optarg ;;
+
+ -disable-* | --disable-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid feature name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"enable_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval enable_$ac_useropt=no ;;
+
+ -docdir | --docdir | --docdi | --doc | --do)
+ ac_prev=docdir ;;
+ -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+ docdir=$ac_optarg ;;
+
+ -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+ ac_prev=dvidir ;;
+ -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+ dvidir=$ac_optarg ;;
+
+ -enable-* | --enable-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid feature name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"enable_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval enable_$ac_useropt=\$ac_optarg ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix=$ac_optarg ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he | -h)
+ ac_init_help=long ;;
+ -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+ ac_init_help=recursive ;;
+ -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+ ac_init_help=short ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host_alias ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host_alias=$ac_optarg ;;
+
+ -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+ ac_prev=htmldir ;;
+ -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+ | --ht=*)
+ htmldir=$ac_optarg ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir=$ac_optarg ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir=$ac_optarg ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir=$ac_optarg ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir=$ac_optarg ;;
+
+ -localedir | --localedir | --localedi | --localed | --locale)
+ ac_prev=localedir ;;
+ -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+ localedir=$ac_optarg ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst | --locals)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+ localstatedir=$ac_optarg ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir=$ac_optarg ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c | -n)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir=$ac_optarg ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix=$ac_optarg ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix=$ac_optarg ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix=$ac_optarg ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name=$ac_optarg ;;
+
+ -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+ ac_prev=pdfdir ;;
+ -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+ pdfdir=$ac_optarg ;;
+
+ -psdir | --psdir | --psdi | --psd | --ps)
+ ac_prev=psdir ;;
+ -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+ psdir=$ac_optarg ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir=$ac_optarg ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir=$ac_optarg ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site=$ac_optarg ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir=$ac_optarg ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir=$ac_optarg ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target_alias ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target_alias=$ac_optarg ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers | -V)
+ ac_init_version=: ;;
+
+ -with-* | --with-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid package name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"with_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval with_$ac_useropt=\$ac_optarg ;;
+
+ -without-* | --without-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid package name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"with_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval with_$ac_useropt=no ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes=$ac_optarg ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries=$ac_optarg ;;
+
+ -*) as_fn_error $? "unrecognized option: \`$ac_option'
+Try \`$0 --help' for more information"
+ ;;
+
+ *=*)
+ ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+ # Reject names that are not valid shell variable names.
+ case $ac_envvar in #(
+ '' | [0-9]* | *[!_$as_cr_alnum]* )
+ as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
+ esac
+ eval $ac_envvar=\$ac_optarg
+ export $ac_envvar ;;
+
+ *)
+ # FIXME: should be removed in autoconf 3.0.
+ $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+ expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+ : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+ as_fn_error $? "missing argument to $ac_option"
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+ case $enable_option_checking in
+ no) ;;
+ fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
+ *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+ esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
+ datadir sysconfdir sharedstatedir localstatedir includedir \
+ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+ libdir localedir mandir
+do
+ eval ac_val=\$$ac_var
+ # Remove trailing slashes.
+ case $ac_val in
+ */ )
+ ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+ eval $ac_var=\$ac_val;;
+ esac
+ # Be sure to have absolute directory names.
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* ) continue;;
+ NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+ esac
+ as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+ if test "x$build_alias" = x; then
+ cross_compiling=maybe
+ $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host.
+ If a cross compiler is detected then cross compile mode will be used" >&2
+ elif test "x$build_alias" != "x$host_alias"; then
+ cross_compiling=yes
+ fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+ as_fn_error $? "working directory cannot be determined"
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+ as_fn_error $? "pwd does not report name of working directory"
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then the parent directory.
+ ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_myself" : 'X\(//\)[^/]' \| \
+ X"$as_myself" : 'X\(//\)$' \| \
+ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_myself" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ srcdir=$ac_confdir
+ if test ! -r "$srcdir/$ac_unique_file"; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+ test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+ as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+ cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
+ pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+ srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+ eval ac_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_env_${ac_var}_value=\$${ac_var}
+ eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat <<_ACEOF
+\`configure' configures alpine 2.10 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE. See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+ -h, --help display this help and exit
+ --help=short display options specific to this package
+ --help=recursive display the short help of all the included packages
+ -V, --version display version information and exit
+ -q, --quiet, --silent do not print \`checking ...' messages
+ --cache-file=FILE cache test results in FILE [disabled]
+ -C, --config-cache alias for \`--cache-file=config.cache'
+ -n, --no-create do not create output files
+ --srcdir=DIR find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ @<:@@S|@ac_default_prefix@:>@
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ @<:@PREFIX@:>@
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+ --bindir=DIR user executables [EPREFIX/bin]
+ --sbindir=DIR system admin executables [EPREFIX/sbin]
+ --libexecdir=DIR program executables [EPREFIX/libexec]
+ --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --libdir=DIR object code libraries [EPREFIX/lib]
+ --includedir=DIR C header files [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc [/usr/include]
+ --datarootdir=DIR read-only arch.-independent data root [PREFIX/share]
+ --datadir=DIR read-only architecture-independent data [DATAROOTDIR]
+ --infodir=DIR info documentation [DATAROOTDIR/info]
+ --localedir=DIR locale-dependent data [DATAROOTDIR/locale]
+ --mandir=DIR man documentation [DATAROOTDIR/man]
+ --docdir=DIR documentation root @<:@DATAROOTDIR/doc/alpine@:>@
+ --htmldir=DIR html documentation [DOCDIR]
+ --dvidir=DIR dvi documentation [DOCDIR]
+ --pdfdir=DIR pdf documentation [DOCDIR]
+ --psdir=DIR ps documentation [DOCDIR]
+_ACEOF
+
+ cat <<\_ACEOF
+
+Program names:
+ --program-prefix=PREFIX prepend PREFIX to installed program names
+ --program-suffix=SUFFIX append SUFFIX to installed program names
+ --program-transform-name=PROGRAM run sed PROGRAM on installed program names
+
+System types:
+ --build=BUILD configure for building on BUILD [guessed]
+ --host=HOST cross-compile to build programs to run on HOST [BUILD]
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+ case $ac_init_help in
+ short | recursive ) echo "Configuration of alpine 2.10:";;
+ esac
+ cat <<\_ACEOF
+
+Optional Features:
+ --disable-option-checking ignore unrecognized --enable/--with options
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --enable-maintainer-mode enable make rules and dependencies not useful
+ (and sometimes confusing) to the casual installer
+ --disable-dependency-tracking speeds up one-time build
+ --enable-dependency-tracking do not reject slow dependency extractors
+ --enable-shared@<:@=PKGS@:>@ build shared libraries @<:@default=yes@:>@
+ --enable-static@<:@=PKGS@:>@ build static libraries @<:@default=yes@:>@
+ --enable-fast-install@<:@=PKGS@:>@
+ optimize for fast installation @<:@default=yes@:>@
+ --disable-libtool-lock avoid locking (might break parallel builds)
+ --disable-nls do not use Native Language Support
+ --disable-rpath do not hardcode runtime library paths
+ --enable-dmalloc Enable dmalloc debugging
+ --enable-osx-universal-binaries
+ Produce universal binaries under OS X @<:@@<:@default=no@:>@@:>@
+ --disable-debug Exclude debug messages from source
+ --disable-optimization Exclude optimizing compiler flags
+ --disable-mouse Disable mouse support
+ --enable-quotas Enable disk quota checking on startup
+ --disable-from-changing Disallow users changing From addresss
+ --disable-background-post
+ Disable background posting
+ --disable-keyboard-lock Disable keyboard locking
+ --enable-from-encoding Enable From encoding in sent messages
+
+Optional Packages:
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --with-pic try to use only PIC/non-PIC objects @<:@default=use
+ both@:>@
+ --with-gnu-ld assume the C compiler uses GNU ld @<:@default=no@:>@
+ --with-gnu-ld assume the C compiler uses GNU ld default=no
+ --with-libiconv-prefix[=DIR] search for libiconv in DIR/include and DIR/lib
+ --without-libiconv-prefix don't search for libiconv in includedir and libdir
+ --with-libintl-prefix[=DIR] search for libintl in DIR/include and DIR/lib
+ --without-libintl-prefix don't search for libintl in includedir and libdir
+ --with-dmalloc-dir=DIR Root of dmalloc lib/include path
+ --with-localedir=DIR Name of gettext locale directory
+ --with-include-path=PATHS
+ Colon-separated list of directories used for include
+ file search
+ --with-lib-path=PATHS Colon-separated list of directories used for library
+ search
+ --with-pubcookie Include support for UW-Pubcookie Web Authentication
+ --with-web-bin=PATH Directory to hold Web Alpine component binary files
+ --with-smtp-msa=PATH Local Mail Submission Agent (sendmail)
+ --with-smtp-msa-flags=FLAGS
+ MSA flags for SMTP on stdin/stdout (-bs -odb -oem)
+ --with-npa=PATH Posting agent when no nntp-servers defined (inews)
+ --with-npa-flags=FLAGS Flags to allow posting via local agent (-h)
+ --with-password-prog=PATH
+ Password change program (/bin/passwd)
+ --with-simple-spellcheck=PROG
+ Spellcheck program reads stdin, emits misspellings
+ on stdout
+ --with-interactive-spellcheck=PROG
+ Interactive, filewise spell checker
+ --with-system-pinerc=VALUE
+ System pinerc (/usr/local/lib/pine.conf)
+ --with-system-fixed-pinerc=VALUE
+ System fixed pinerc (/usr/local/lib/pine.conf.fixed)
+ --with-mailcheck-interval=VALUE
+ Specify default mail-check-interval (150)
+ --with-checkpoint-interval=VALUE
+ Specify default checkpoint-interval (420)
+ --with-checkpoint-frequency=VALUE
+ State change count before checkpoint (12)
+ --with-display-rows=VALUE
+ Initial rows on display (24)
+ --with-display-columns=VALUE
+ Initial columns on display (80)
+ --with-max-display-rows=VALUE
+ Maximum display rows (200)
+ --with-max-display-columns=VALUE
+ Maximum display columns (500)
+ --with-fill-column=VALUE
+ Default fill column (74)
+ --with-max_fill-column=VALUE
+ Maximum fill column (80)
+ --with-debug-level=VALUE
+ Specify default debug verbosity level (2)
+ --with-debug-files=VALUE
+ Specify number of debug files (4)
+ --with-debug-file=VALUE Specify debug file name (.pine-debug)
+ --with-forwarded-keyword=VALUE
+ IMAP (c-client) keyword to store forwarded status
+ ("\@S|@Forwarded")
+ --with-display-overlap=VALUE
+ Lines preserved while paging (2)
+ --with-display-margin=VALUE
+ Lines visible while scrolling (0)
+ --with-default-fcc=VALUE
+ Default sent mail folder (sent-mail)
+ --with-default-save-folder=VALUE
+ Default save folder (saved-messages)
+ --with-default-legacy-postponed-folder=VALUE
+ Pre Pine 3.90 postponed folder (postponed-mail)
+ --with-default-postponed-folder=VALUE
+ Default postponed folder (postponed-msgs)
+ --with-default-trash-folder=VALUE
+ Default Trash folder for Web Alpine (Trash)
+ --with-default-interrupted-mail=VALUE
+ Default folder for interrupted mail
+ (.pine-interrupted-mail)
+ --with-default-dead-letter-folder=VALUE
+ Default dead letter folder (dead.letter)
+ --with-default-mail-directory=VALUE
+ Default mail directory (mail)
+ --with-default-inbox-name=VALUE
+ Default inbox name (INBOX)
+ --with-default-signature-file=VALUE
+ Default signature file (.signature)
+ --with-default-elm-style-save=VALUE
+ Default to Elm style save (no)
+ --with-default-header-in-reply=VALUE
+ Include header in reply (no)
+ --with-default-old-style-reply=VALUE
+ Default to old style reply (no)
+ --with-default-use-only-domain-name=VALUE
+ Default to using only the domain name (no)
+ --with-default-save-by-sender=VALUE
+ Default to save by sender (no)
+ --with-default-sort-key=VALUE
+ Default sort key (arrival)
+ --with-default-addressbook-sort-rule=VALUE
+ Default addressbook sort rule
+ (fullname-with-lists-last)
+ --with-default-folder-sort-rule=VALUE
+ Default folder sort rule (alphabetical)
+ --with-default-saved-message-name-rule=VALUE
+ Default saved message name rule (default-folder)
+ --with-default-fcc-rule=VALUE
+ Default fcc rule (default-fcc)
+ --with-default-standard-printer=VALUE
+ Default standard printern (lpr)
+ --with-default-ansi-printer=VALUE
+ ANSI printer definition (attached-to-ansi)
+ --with-default-addressbook=VALUE
+ Default addressbook name (.addressbook)
+ --with-default-local-fullname=VALUE
+ Default local support fullname ("Local Support")
+ --with-default-local-address=VALUE
+ Default local support address (postmaster)
+ --with-default-keyboard-lock-count=VALUE
+ Default keyboard lock count (1)
+ --with-default-remote-addressbook-history=VALUE
+ Default address book history count (3)
+ --with-smime-public-cert-directory=VALUE
+ Default Public Cert Directory (.alpine-smime/public)
+ --with-smime-private-key-directory=VALUE
+ Default Private Key Directory
+ (.alpine-smime/private)
+ --with-smime-cacert-directory=VALUE
+ Default Cert Authority Directory (.alpine-smime/ca)
+ --with-default-printer=VALUE
+ Default printer (ANSI_PRINTER)
+ --with-passfile=FILENAME
+ Password cache file (NOT secure, NOT recommended)
+ --without-local-password-cache
+ Disable OS-specific password cache, if supported
+ --with-local-password-cache-method
+ OS-specific credential cache (OSX=APPLEKEYCHAIN,
+ Windows=WINCRED)
+ --with-default-sshpath=FILENAME
+ set default value of ssh command path (defining
+ should cause ssh to be preferred to rsh)
+ --with-default-sshcmd=PERCENT_S_STRING
+ set default value of ssh command string (usually "%s
+ %s -l %s exec /etc/r%sd")
+ --without-ssl Disable SSL support (OpenSSL)
+ --with-ssl-dir=DIR Root of SSL lib/include path
+ --with-ssl-certs-dir=DIR
+ Path to SSL certificate directory
+ --with-ssl-include-dir=DIR
+ SSL include file path
+ --with-ssl-lib-dir=DIR SSL library path
+ --without-krb5 Disable Kerberos support
+ --with-krb5-dir=DIR Root of Kerberos lib/include path
+ --with-krb5-include-dir=DIR
+ Kerberos include file path
+ --with-krb5-lib-dir=DIR Kerberos library path
+ --without-ldap Disable LDAP query support
+ --with-ldap-dir=DIR Root of LDAP lib/include path
+ --with-ldap-include-dir=DIR
+ Directory containing LDAP include files
+ --with-ldap-lib-dir=DIR LDAP library path
+ --without-smime Disable S/MIME
+ --without-tcl Disable TCL, thus Web Alpine support
+ --with-tcl-lib=LIBRARY Specific TCL Library, like \"tcl8.4\"
+ --with-tcl-include=DIR Directory containing TCL include files
+ --with-supplied-regex Use regex library supplied with alpine
+ --without-pthread Do NOT test for nor build with POSIX thread support
+ --with-system-mail-directory=DIR
+ Directory where local mail is delivered
+ --with-c-client-target=TARGET
+ IMAP build target (see imap/Makefile)
+ --without-ipv6 Disable IPv6, primarily to work around resolver
+ problems
+
+Some influential environment variables:
+ CC C compiler command
+ CFLAGS C compiler flags
+ LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
+ nonstandard directory <lib dir>
+ LIBS libraries to pass to the linker, e.g. -l<library>
+ CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
+ you have headers in a nonstandard directory <include dir>
+ CPP C preprocessor
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to <alpine-contact@u.washington.edu>.
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+ # If there are subdirs, report their specific --help.
+ for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+ test -d "$ac_dir" ||
+ { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+ continue
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+ cd "$ac_dir" || { ac_status=$?; continue; }
+ # Check for guested configure.
+ if test -f "$ac_srcdir/configure.gnu"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+ elif test -f "$ac_srcdir/configure"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure" --help=recursive
+ else
+ $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+ fi || ac_status=$?
+ cd "$ac_pwd" || { ac_status=$?; break; }
+ done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+ cat <<\_ACEOF
+alpine configure 2.10
+generated by GNU Autoconf 2.68
+
+Copyright (C) 2010 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+ exit
+fi
+
+## ------------------------ ##
+## Autoconf initialization. ##
+## ------------------------ ##
+
+@%:@ ac_fn_c_try_compile LINENO
+@%:@ --------------------------
+@%:@ Try to compile conftest.@S|@ac_ext, and return whether this succeeded.
+ac_fn_c_try_compile ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ rm -f conftest.$ac_objext
+ if { { ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compile") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} @%:@ ac_fn_c_try_compile
+
+@%:@ ac_fn_c_try_link LINENO
+@%:@ -----------------------
+@%:@ Try to link conftest.@S|@ac_ext, and return whether this succeeded.
+ac_fn_c_try_link ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ rm -f conftest.$ac_objext conftest$ac_exeext
+ if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
+ # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
+ # interfere with the next link command; also delete a directory that is
+ # left behind by Apple's compiler. We do this before executing the actions.
+ rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} @%:@ ac_fn_c_try_link
+
+@%:@ ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
+@%:@ -------------------------------------------------------
+@%:@ Tests whether HEADER exists and can be compiled using the include files in
+@%:@ INCLUDES, setting the cache variable VAR accordingly.
+ac_fn_c_check_header_compile ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+@%:@include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ eval "$3=yes"
+else
+ eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} @%:@ ac_fn_c_check_header_compile
+
+@%:@ ac_fn_c_try_cpp LINENO
+@%:@ ----------------------
+@%:@ Try to preprocess conftest.@S|@ac_ext, and return whether this succeeded.
+ac_fn_c_try_cpp ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if { { ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } > conftest.i && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} @%:@ ac_fn_c_try_cpp
+
+@%:@ ac_fn_c_try_run LINENO
+@%:@ ----------------------
+@%:@ Try to link conftest.@S|@ac_ext, and return whether this succeeded. Assumes
+@%:@ that executables *can* be run.
+ac_fn_c_try_run ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'
+ { { case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; }; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: program exited with status $ac_status" >&5
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=$ac_status
+fi
+ rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} @%:@ ac_fn_c_try_run
+
+@%:@ ac_fn_c_check_func LINENO FUNC VAR
+@%:@ ----------------------------------
+@%:@ Tests whether FUNC exists, setting the cache variable VAR accordingly
+ac_fn_c_check_func ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $2 innocuous_$2
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $2 (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $2
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $2 ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_$2 || defined __stub___$2
+choke me
+#endif
+
+int
+main ()
+{
+return $2 ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ eval "$3=yes"
+else
+ eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} @%:@ ac_fn_c_check_func
+
+@%:@ ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES
+@%:@ -------------------------------------------------------
+@%:@ Tests whether HEADER exists, giving a warning if it cannot be compiled using
+@%:@ the include files in INCLUDES and setting the cache variable VAR
+@%:@ accordingly.
+ac_fn_c_check_header_mongrel ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if eval \${$3+:} false; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5
+$as_echo_n "checking $2 usability... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+@%:@include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_header_compiler=yes
+else
+ ac_header_compiler=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5
+$as_echo_n "checking $2 presence... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+@%:@include <$2>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ ac_header_preproc=yes
+else
+ ac_header_preproc=no
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #((
+ yes:no: )
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+( $as_echo "## ---------------------------------------------- ##
+## Report this to alpine-contact@u.washington.edu ##
+## ---------------------------------------------- ##"
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ eval "$3=\$ac_header_compiler"
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} @%:@ ac_fn_c_check_header_mongrel
+
+@%:@ ac_fn_c_check_type LINENO TYPE VAR INCLUDES
+@%:@ -------------------------------------------
+@%:@ Tests whether TYPE exists after having included INCLUDES, setting cache
+@%:@ variable VAR accordingly.
+ac_fn_c_check_type ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ eval "$3=no"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+if (sizeof ($2))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+if (sizeof (($2)))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+ eval "$3=yes"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} @%:@ ac_fn_c_check_type
+
+@%:@ ac_fn_c_compute_int LINENO EXPR VAR INCLUDES
+@%:@ --------------------------------------------
+@%:@ Tries to find the compile-time value of EXPR in a program that includes
+@%:@ INCLUDES, setting VAR accordingly. Returns whether the value could be
+@%:@ computed
+ac_fn_c_compute_int ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if test "$cross_compiling" = yes; then
+ # Depending upon the size, compute the lo and hi bounds.
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+static int test_array @<:@1 - 2 * !(($2) >= 0)@:>@;
+test_array @<:@0@:>@ = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_lo=0 ac_mid=0
+ while :; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+static int test_array @<:@1 - 2 * !(($2) <= $ac_mid)@:>@;
+test_array @<:@0@:>@ = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_hi=$ac_mid; break
+else
+ as_fn_arith $ac_mid + 1 && ac_lo=$as_val
+ if test $ac_lo -le $ac_mid; then
+ ac_lo= ac_hi=
+ break
+ fi
+ as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ done
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+static int test_array @<:@1 - 2 * !(($2) < 0)@:>@;
+test_array @<:@0@:>@ = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_hi=-1 ac_mid=-1
+ while :; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+static int test_array @<:@1 - 2 * !(($2) >= $ac_mid)@:>@;
+test_array @<:@0@:>@ = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_lo=$ac_mid; break
+else
+ as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val
+ if test $ac_mid -le $ac_hi; then
+ ac_lo= ac_hi=
+ break
+ fi
+ as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ done
+else
+ ac_lo= ac_hi=
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+# Binary search between lo and hi bounds.
+while test "x$ac_lo" != "x$ac_hi"; do
+ as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+static int test_array @<:@1 - 2 * !(($2) <= $ac_mid)@:>@;
+test_array @<:@0@:>@ = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_hi=$ac_mid
+else
+ as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+case $ac_lo in @%:@((
+?*) eval "$3=\$ac_lo"; ac_retval=0 ;;
+'') ac_retval=1 ;;
+esac
+ else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+static long int longval () { return $2; }
+static unsigned long int ulongval () { return $2; }
+@%:@include <stdio.h>
+@%:@include <stdlib.h>
+int
+main ()
+{
+
+ FILE *f = fopen ("conftest.val", "w");
+ if (! f)
+ return 1;
+ if (($2) < 0)
+ {
+ long int i = longval ();
+ if (i != ($2))
+ return 1;
+ fprintf (f, "%ld", i);
+ }
+ else
+ {
+ unsigned long int i = ulongval ();
+ if (i != ($2))
+ return 1;
+ fprintf (f, "%lu", i);
+ }
+ /* Do not output a trailing newline, as this causes \r\n confusion
+ on some platforms. */
+ return ferror (f) || fclose (f) != 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ echo >>conftest.val; read $3 <conftest.val; ac_retval=0
+else
+ ac_retval=1
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+rm -f conftest.val
+
+ fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} @%:@ ac_fn_c_compute_int
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by alpine $as_me 2.10, which was
+generated by GNU Autoconf 2.68. Invocation command line was
+
+ $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`
+
+/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown`
+/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`
+/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ $as_echo "PATH: $as_dir"
+ done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+ for ac_arg
+ do
+ case $ac_arg in
+ -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ continue ;;
+ *\'*)
+ ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ case $ac_pass in
+ 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
+ 2)
+ as_fn_append ac_configure_args1 " '$ac_arg'"
+ if test $ac_must_keep_next = true; then
+ ac_must_keep_next=false # Got value, back to normal.
+ else
+ case $ac_arg in
+ *=* | --config-cache | -C | -disable-* | --disable-* \
+ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+ | -with-* | --with-* | -without-* | --without-* | --x)
+ case "$ac_configure_args0 " in
+ "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+ esac
+ ;;
+ -* ) ac_must_keep_next=true ;;
+ esac
+ fi
+ as_fn_append ac_configure_args " '$ac_arg'"
+ ;;
+ esac
+ done
+done
+{ ac_configure_args0=; unset ac_configure_args0;}
+{ ac_configure_args1=; unset ac_configure_args1;}
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log. We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+ # Save into config.log some information that might help in debugging.
+ {
+ echo
+
+ $as_echo "## ---------------- ##
+## Cache variables. ##
+## ---------------- ##"
+ echo
+ # The following way of writing the cache mishandles newlines in values,
+(
+ for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+ *) { eval $ac_var=; unset $ac_var;} ;;
+ esac ;;
+ esac
+ done
+ (set) 2>&1 |
+ case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ sed -n \
+ "s/'\''/'\''\\\\'\'''\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+ ;; #(
+ *)
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+)
+ echo
+
+ $as_echo "## ----------------- ##
+## Output variables. ##
+## ----------------- ##"
+ echo
+ for ac_var in $ac_subst_vars
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ $as_echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+
+ if test -n "$ac_subst_files"; then
+ $as_echo "## ------------------- ##
+## File substitutions. ##
+## ------------------- ##"
+ echo
+ for ac_var in $ac_subst_files
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ $as_echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+ fi
+
+ if test -s confdefs.h; then
+ $as_echo "## ----------- ##
+## confdefs.h. ##
+## ----------- ##"
+ echo
+ cat confdefs.h
+ echo
+ fi
+ test "$ac_signal" != 0 &&
+ $as_echo "$as_me: caught signal $ac_signal"
+ $as_echo "$as_me: exit $exit_status"
+ } >&5
+ rm -f core *.core core.conftest.* &&
+ rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+ exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+ trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+$as_echo "/* confdefs.h */" > confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_URL "$PACKAGE_URL"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+ac_site_file1=NONE
+ac_site_file2=NONE
+if test -n "$CONFIG_SITE"; then
+ # We do not want a PATH search for config.site.
+ case $CONFIG_SITE in @%:@((
+ -*) ac_site_file1=./$CONFIG_SITE;;
+ */*) ac_site_file1=$CONFIG_SITE;;
+ *) ac_site_file1=./$CONFIG_SITE;;
+ esac
+elif test "x$prefix" != xNONE; then
+ ac_site_file1=$prefix/share/config.site
+ ac_site_file2=$prefix/etc/config.site
+else
+ ac_site_file1=$ac_default_prefix/share/config.site
+ ac_site_file2=$ac_default_prefix/etc/config.site
+fi
+for ac_site_file in "$ac_site_file1" "$ac_site_file2"
+do
+ test "x$ac_site_file" = xNONE && continue
+ if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+$as_echo "$as_me: loading site script $ac_site_file" >&6;}
+ sed 's/^/| /' "$ac_site_file" >&5
+ . "$ac_site_file" \
+ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed to load site script $ac_site_file
+See \`config.log' for more details" "$LINENO" 5; }
+ fi
+done
+
+if test -r "$cache_file"; then
+ # Some versions of bash will fail to source /dev/null (special files
+ # actually), so we avoid doing that. DJGPP emulates it as a regular file.
+ if test /dev/null != "$cache_file" && test -f "$cache_file"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
+$as_echo "$as_me: loading cache $cache_file" >&6;}
+ case $cache_file in
+ [\\/]* | ?:[\\/]* ) . "$cache_file";;
+ *) . "./$cache_file";;
+ esac
+ fi
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
+$as_echo "$as_me: creating cache $cache_file" >&6;}
+ >$cache_file
+fi
+
+gt_needs="$gt_needs "
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+ eval ac_old_set=\$ac_cv_env_${ac_var}_set
+ eval ac_new_set=\$ac_env_${ac_var}_set
+ eval ac_old_val=\$ac_cv_env_${ac_var}_value
+ eval ac_new_val=\$ac_env_${ac_var}_value
+ case $ac_old_set,$ac_new_set in
+ set,)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,set)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,);;
+ *)
+ if test "x$ac_old_val" != "x$ac_new_val"; then
+ # differences in whitespace do not lead to failure.
+ ac_old_val_w=`echo x $ac_old_val`
+ ac_new_val_w=`echo x $ac_new_val`
+ if test "$ac_old_val_w" != "$ac_new_val_w"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
+$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+ ac_cache_corrupted=:
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+ eval $ac_var=\$ac_old_val
+ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5
+$as_echo "$as_me: former value: \`$ac_old_val'" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5
+$as_echo "$as_me: current value: \`$ac_new_val'" >&2;}
+ fi;;
+ esac
+ # Pass precious variables to config.status.
+ if test "$ac_new_set" = set; then
+ case $ac_new_val in
+ *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+ *) ac_arg=$ac_var=$ac_new_val ;;
+ esac
+ case " $ac_configure_args " in
+ *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
+ *) as_fn_append ac_configure_args " '$ac_arg'" ;;
+ esac
+ fi
+done
+if $ac_cache_corrupted; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
+$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+ as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
+fi
+## -------------------- ##
+## Main body of script. ##
+## -------------------- ##
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+ac_config_headers="$ac_config_headers include/config.h"
+
+
+am__api_version='1.11'
+
+ac_aux_dir=
+for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
+ if test -f "$ac_dir/install-sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f "$ac_dir/install.sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ elif test -f "$ac_dir/shtool"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/shtool install -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5
+fi
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var.
+ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var.
+ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
+
+
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+# Reject install programs that cannot install multiple files.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5
+$as_echo_n "checking for a BSD-compatible install... " >&6; }
+if test -z "$INSTALL"; then
+if ${ac_cv_path_install+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in @%:@((
+ ./ | .// | /[cC]/* | \
+ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \
+ /usr/ucb/* ) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ # Don't use installbsd from OSF since it installs stuff as root
+ # by default.
+ for ac_prog in ginstall scoinst install; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then
+ if test $ac_prog = install &&
+ grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ :
+ elif test $ac_prog = install &&
+ grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # program-specific install script used by HP pwplus--don't use.
+ :
+ else
+ rm -rf conftest.one conftest.two conftest.dir
+ echo one > conftest.one
+ echo two > conftest.two
+ mkdir conftest.dir
+ if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" &&
+ test -s conftest.one && test -s conftest.two &&
+ test -s conftest.dir/conftest.one &&
+ test -s conftest.dir/conftest.two
+ then
+ ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+ break 3
+ fi
+ fi
+ fi
+ done
+ done
+ ;;
+esac
+
+ done
+IFS=$as_save_IFS
+
+rm -rf conftest.one conftest.two conftest.dir
+
+fi
+ if test "${ac_cv_path_install+set}" = set; then
+ INSTALL=$ac_cv_path_install
+ else
+ # As a last resort, use the slow shell script. Don't cache a
+ # value for INSTALL within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the value is a relative name.
+ INSTALL=$ac_install_sh
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5
+$as_echo "$INSTALL" >&6; }
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5
+$as_echo_n "checking whether build environment is sane... " >&6; }
+# Just in case
+sleep 1
+echo timestamp > conftest.file
+# Reject unsafe characters in $srcdir or the absolute working directory
+# name. Accept space and tab only in the latter.
+am_lf='
+'
+case `pwd` in
+ *[\\\"\#\$\&\'\`$am_lf]*)
+ as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;;
+esac
+case $srcdir in
+ *[\\\"\#\$\&\'\`$am_lf\ \ ]*)
+ as_fn_error $? "unsafe srcdir value: \`$srcdir'" "$LINENO" 5;;
+esac
+
+# Do `set' in a subshell so we don't clobber the current shell's
+# arguments. Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+ set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
+ if test "$*" = "X"; then
+ # -L didn't work.
+ set X `ls -t "$srcdir/configure" conftest.file`
+ fi
+ rm -f conftest.file
+ if test "$*" != "X $srcdir/configure conftest.file" \
+ && test "$*" != "X conftest.file $srcdir/configure"; then
+
+ # If neither matched, then we have a broken ls. This can happen
+ # if, for instance, CONFIG_SHELL is bash and it inherits a
+ # broken ls alias from the environment. This has actually
+ # happened. Such a system could not be considered "sane".
+ as_fn_error $? "ls -t appears to fail. Make sure there is not a broken
+alias in your environment" "$LINENO" 5
+ fi
+
+ test "$2" = conftest.file
+ )
+then
+ # Ok.
+ :
+else
+ as_fn_error $? "newly created file is older than distributed files!
+Check your system clock" "$LINENO" 5
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+test "$program_prefix" != NONE &&
+ program_transform_name="s&^&$program_prefix&;$program_transform_name"
+# Use a double $ so make ignores it.
+test "$program_suffix" != NONE &&
+ program_transform_name="s&\$&$program_suffix&;$program_transform_name"
+# Double any \ or $.
+# By default was `s,x,x', remove it if useless.
+ac_script='s/[\\$]/&&/g;s/;s,x,x,$//'
+program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"`
+
+# expand $ac_aux_dir to an absolute path
+am_aux_dir=`cd $ac_aux_dir && pwd`
+
+if test x"${MISSING+set}" != xset; then
+ case $am_aux_dir in
+ *\ * | *\ *)
+ MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
+ *)
+ MISSING="\${SHELL} $am_aux_dir/missing" ;;
+ esac
+fi
+# Use eval to expand $SHELL
+if eval "$MISSING --run true"; then
+ am_missing_run="$MISSING --run "
+else
+ am_missing_run=
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`missing' script is too old or missing" >&5
+$as_echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;}
+fi
+
+if test x"${install_sh}" != xset; then
+ case $am_aux_dir in
+ *\ * | *\ *)
+ install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
+ *)
+ install_sh="\${SHELL} $am_aux_dir/install-sh"
+ esac
+fi
+
+# Installed binaries are usually stripped using `strip' when the user
+# run `make install-strip'. However `strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the `STRIP' environment variable to overrule this program.
+if test "$cross_compiling" != no; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_STRIP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$STRIP"; then
+ ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5
+$as_echo "$STRIP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_STRIP"; then
+ ac_ct_STRIP=$STRIP
+ # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_STRIP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_STRIP"; then
+ ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_STRIP="strip"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5
+$as_echo "$ac_ct_STRIP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_STRIP" = x; then
+ STRIP=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ STRIP=$ac_ct_STRIP
+ fi
+else
+ STRIP="$ac_cv_prog_STRIP"
+fi
+
+fi
+INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5
+$as_echo_n "checking for a thread-safe mkdir -p... " >&6; }
+if test -z "$MKDIR_P"; then
+ if ${ac_cv_path_mkdir+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in mkdir gmkdir; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; } || continue
+ case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #(
+ 'mkdir (GNU coreutils) '* | \
+ 'mkdir (coreutils) '* | \
+ 'mkdir (fileutils) '4.1*)
+ ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext
+ break 3;;
+ esac
+ done
+ done
+ done
+IFS=$as_save_IFS
+
+fi
+
+ test -d ./--version && rmdir ./--version
+ if test "${ac_cv_path_mkdir+set}" = set; then
+ MKDIR_P="$ac_cv_path_mkdir -p"
+ else
+ # As a last resort, use the slow shell script. Don't cache a
+ # value for MKDIR_P within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the value is a relative name.
+ MKDIR_P="$ac_install_sh -d"
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5
+$as_echo "$MKDIR_P" >&6; }
+
+mkdir_p="$MKDIR_P"
+case $mkdir_p in
+ [\\/$]* | ?:[\\/]*) ;;
+ */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;;
+esac
+
+for ac_prog in gawk mawk nawk awk
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_AWK+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$AWK"; then
+ ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_AWK="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+AWK=$ac_cv_prog_AWK
+if test -n "$AWK"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5
+$as_echo "$AWK" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$AWK" && break
+done
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5
+$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; }
+set x ${MAKE-make}
+ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
+if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.make <<\_ACEOF
+SHELL = /bin/sh
+all:
+ @echo '@@@%%%=$(MAKE)=@@@%%%'
+_ACEOF
+# GNU make sometimes prints "make[1]: Entering ...", which would confuse us.
+case `${MAKE-make} -f conftest.make 2>/dev/null` in
+ *@@@%%%=?*=@@@%%%*)
+ eval ac_cv_prog_make_${ac_make}_set=yes;;
+ *)
+ eval ac_cv_prog_make_${ac_make}_set=no;;
+esac
+rm -f conftest.make
+fi
+if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ SET_MAKE=
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+ am__leading_dot=.
+else
+ am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+
+if test "`cd $srcdir && pwd`" != "`pwd`"; then
+ # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
+ # is not polluted with repeated "-I."
+ am__isrc=' -I$(srcdir)'
+ # test to see if srcdir already configured
+ if test -f $srcdir/config.status; then
+ as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5
+ fi
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+ if (cygpath --version) >/dev/null 2>/dev/null; then
+ CYGPATH_W='cygpath -w'
+ else
+ CYGPATH_W=echo
+ fi
+fi
+
+
+# Define the identity of the package.
+ PACKAGE='alpine'
+ VERSION='2.10'
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE "$PACKAGE"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define VERSION "$VERSION"
+_ACEOF
+
+# Some tools Automake needs.
+
+ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"}
+
+
+AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"}
+
+
+AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"}
+
+
+AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"}
+
+
+MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"}
+
+# We need awk for the "check" target. The system "awk" is bad on
+# some platforms.
+# Always define AMTAR for backward compatibility.
+
+AMTAR=${AMTAR-"${am_missing_run}tar"}
+
+am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable maintainer-specific portions of Makefiles" >&5
+$as_echo_n "checking whether to enable maintainer-specific portions of Makefiles... " >&6; }
+ @%:@ Check whether --enable-maintainer-mode was given.
+if test "${enable_maintainer_mode+set}" = set; then :
+ enableval=$enable_maintainer_mode; USE_MAINTAINER_MODE=$enableval
+else
+ USE_MAINTAINER_MODE=no
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $USE_MAINTAINER_MODE" >&5
+$as_echo "$USE_MAINTAINER_MODE" >&6; }
+ if test $USE_MAINTAINER_MODE = yes; then
+ MAINTAINER_MODE_TRUE=
+ MAINTAINER_MODE_FALSE='#'
+else
+ MAINTAINER_MODE_TRUE='#'
+ MAINTAINER_MODE_FALSE=
+fi
+
+ MAINT=$MAINTAINER_MODE_TRUE
+
+
+
+# Make sure we can run config.sub.
+$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
+ as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5
+$as_echo_n "checking build system type... " >&6; }
+if ${ac_cv_build+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_build_alias=$build_alias
+test "x$ac_build_alias" = x &&
+ ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"`
+test "x$ac_build_alias" = x &&
+ as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5
+ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` ||
+ as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5
+$as_echo "$ac_cv_build" >&6; }
+case $ac_cv_build in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;;
+esac
+build=$ac_cv_build
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_build
+shift
+build_cpu=$1
+build_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+build_os=$*
+IFS=$ac_save_IFS
+case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5
+$as_echo_n "checking host system type... " >&6; }
+if ${ac_cv_host+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "x$host_alias" = x; then
+ ac_cv_host=$ac_cv_build
+else
+ ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` ||
+ as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5
+$as_echo "$ac_cv_host" >&6; }
+case $ac_cv_host in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;;
+esac
+host=$ac_cv_host
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_host
+shift
+host_cpu=$1
+host_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+host_os=$*
+IFS=$ac_save_IFS
+case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: Configuring for $PACKAGE_STRING ($host))" >&5
+$as_echo "$as_me: Configuring for $PACKAGE_STRING ($host))" >&6;}
+
+# start out with intent to build Web Alpine
+WEB_BUILD=web/src/alpined.d
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="${ac_tool_prefix}gcc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_CC="gcc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="${ac_tool_prefix}cc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ fi
+fi
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $@%:@ != 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+ fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in cl.exe
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$CC" && break
+ done
+fi
+if test -z "$CC"; then
+ ac_ct_CC=$CC
+ for ac_prog in cl.exe
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_CC="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_CC" && break
+done
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+fi
+
+fi
+
+
+test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "no acceptable C compiler found in \$PATH
+See \`config.log' for more details" "$LINENO" 5; }
+
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+ { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ sed '10a\
+... rest of stderr output deleted ...
+ 10q' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ fi
+ rm -f conftest.er1 conftest.err
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+done
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
+$as_echo_n "checking whether the C compiler works... " >&6; }
+ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+ * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+ esac
+done
+rm -f $ac_rmfiles
+
+if { { ac_try="$ac_link_default"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link_default") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile. We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+ ;;
+ [ab].out )
+ # We found the default executable, but exeext='' is most
+ # certainly right.
+ break;;
+ *.* )
+ if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+ then :; else
+ ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ fi
+ # We set ac_cv_exeext here because the later test for it is not
+ # safe: cross compilers may not add the suffix if given an `-o'
+ # argument, so we may need to know it at that point already.
+ # Even if this section looks crufty: it has the advantage of
+ # actually working.
+ break;;
+ * )
+ break;;
+ esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+ ac_file=''
+fi
+if test -z "$ac_file"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "C compiler cannot create executables
+See \`config.log' for more details" "$LINENO" 5; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
+$as_echo_n "checking for C compiler default output file name... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+$as_echo "$ac_file" >&6; }
+ac_exeext=$ac_cv_exeext
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
+$as_echo_n "checking for suffix of executables... " >&6; }
+if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+ *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ break;;
+ * ) break;;
+ esac
+done
+else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest conftest$ac_cv_exeext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+$as_echo "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+@%:@include <stdio.h>
+int
+main ()
+{
+FILE *f = fopen ("conftest.out", "w");
+ return ferror (f) || fclose (f) != 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files="$ac_clean_files conftest.out"
+# Check that the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+$as_echo_n "checking whether we are cross compiling... " >&6; }
+if test "$cross_compiling" != yes; then
+ { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+ if { ac_try='./conftest$ac_cv_exeext'
+ { { case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; }; then
+ cross_compiling=no
+ else
+ if test "$cross_compiling" = maybe; then
+ cross_compiling=yes
+ else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details" "$LINENO" 5; }
+ fi
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
+$as_echo "$cross_compiling" >&6; }
+
+rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
+$as_echo_n "checking for suffix of object files... " >&6; }
+if ${ac_cv_objext+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { { ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compile") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ for ac_file in conftest.o conftest.obj conftest.*; do
+ test -f "$ac_file" || continue;
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+ *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+ break;;
+ esac
+done
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of object files: cannot compile
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
+$as_echo "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
+$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
+if ${ac_cv_c_compiler_gnu+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_compiler_gnu=yes
+else
+ ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+ GCC=yes
+else
+ GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+$as_echo_n "checking whether $CC accepts -g... " >&6; }
+if ${ac_cv_prog_cc_g+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_save_c_werror_flag=$ac_c_werror_flag
+ ac_c_werror_flag=yes
+ ac_cv_prog_cc_g=no
+ CFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_g=yes
+else
+ CFLAGS=""
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+ ac_c_werror_flag=$ac_save_c_werror_flag
+ CFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+$as_echo "$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if ${ac_cv_prog_cc_c89+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
+ function prototypes and stuff, but not '\xHH' hex character constants.
+ These don't provoke an error unfortunately, instead are silently treated
+ as 'x'. The following induces an error, until -std is added to get
+ proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
+ array size at least. It's necessary to write '\x00'==0 to get something
+ that's true only with -std. */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+ inside strings and character constants. */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
+ ;
+ return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+ test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+ x)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+ xno)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+ *)
+ CC="$CC $ac_cv_prog_cc_c89"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c89" != xno; then :
+
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+DEPDIR="${am__leading_dot}deps"
+
+ac_config_commands="$ac_config_commands depfiles"
+
+
+am_make=${MAKE-make}
+cat > confinc << 'END'
+am__doit:
+ @echo this is the am__doit target
+.PHONY: am__doit
+END
+# If we don't find an include directive, just comment out the code.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5
+$as_echo_n "checking for style of include used by $am_make... " >&6; }
+am__include="#"
+am__quote=
+_am_result=none
+# First try GNU make style include.
+echo "include confinc" > confmf
+# Ignore all kinds of additional output from `make'.
+case `$am_make -s -f confmf 2> /dev/null` in #(
+*the\ am__doit\ target*)
+ am__include=include
+ am__quote=
+ _am_result=GNU
+ ;;
+esac
+# Now try BSD make style include.
+if test "$am__include" = "#"; then
+ echo '.include "confinc"' > confmf
+ case `$am_make -s -f confmf 2> /dev/null` in #(
+ *the\ am__doit\ target*)
+ am__include=.include
+ am__quote="\""
+ _am_result=BSD
+ ;;
+ esac
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5
+$as_echo "$_am_result" >&6; }
+rm -f confinc confmf
+
+@%:@ Check whether --enable-dependency-tracking was given.
+if test "${enable_dependency_tracking+set}" = set; then :
+ enableval=$enable_dependency_tracking;
+fi
+
+if test "x$enable_dependency_tracking" != xno; then
+ am_depcomp="$ac_aux_dir/depcomp"
+ AMDEPBACKSLASH='\'
+fi
+ if test "x$enable_dependency_tracking" != xno; then
+ AMDEP_TRUE=
+ AMDEP_FALSE='#'
+else
+ AMDEP_TRUE='#'
+ AMDEP_FALSE=
+fi
+
+
+
+depcc="$CC" am_compiler_list=
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5
+$as_echo_n "checking dependency style of $depcc... " >&6; }
+if ${am_cv_CC_dependencies_compiler_type+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+ # We make a subdir and do the tests there. Otherwise we can end up
+ # making bogus files that we don't know about and never remove. For
+ # instance it was reported that on HP-UX the gcc test will end up
+ # making a dummy file named `D' -- because `-MD' means `put the output
+ # in D'.
+ mkdir conftest.dir
+ # Copy depcomp to subdir because otherwise we won't find it if we're
+ # using a relative directory.
+ cp "$am_depcomp" conftest.dir
+ cd conftest.dir
+ # We will build objects and dependencies in a subdirectory because
+ # it helps to detect inapplicable dependency modes. For instance
+ # both Tru64's cc and ICC support -MD to output dependencies as a
+ # side effect of compilation, but ICC will put the dependencies in
+ # the current directory while Tru64 will put them in the object
+ # directory.
+ mkdir sub
+
+ am_cv_CC_dependencies_compiler_type=none
+ if test "$am_compiler_list" = ""; then
+ am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
+ fi
+ am__universal=false
+ case " $depcc " in #(
+ *\ -arch\ *\ -arch\ *) am__universal=true ;;
+ esac
+
+ for depmode in $am_compiler_list; do
+ # Setup a source with many dependencies, because some compilers
+ # like to wrap large dependency lists on column 80 (with \), and
+ # we should not choose a depcomp mode which is confused by this.
+ #
+ # We need to recreate these files for each test, as the compiler may
+ # overwrite some of them when testing with obscure command lines.
+ # This happens at least with the AIX C compiler.
+ : > sub/conftest.c
+ for i in 1 2 3 4 5 6; do
+ echo '#include "conftst'$i'.h"' >> sub/conftest.c
+ # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
+ # Solaris 8's {/usr,}/bin/sh.
+ touch sub/conftst$i.h
+ done
+ echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+ # We check with `-c' and `-o' for the sake of the "dashmstdout"
+ # mode. It turns out that the SunPro C++ compiler does not properly
+ # handle `-M -o', and we need to detect this. Also, some Intel
+ # versions had trouble with output in subdirs
+ am__obj=sub/conftest.${OBJEXT-o}
+ am__minus_obj="-o $am__obj"
+ case $depmode in
+ gcc)
+ # This depmode causes a compiler race in universal mode.
+ test "$am__universal" = false || continue
+ ;;
+ nosideeffect)
+ # after this tag, mechanisms are not by side-effect, so they'll
+ # only be used when explicitly requested
+ if test "x$enable_dependency_tracking" = xyes; then
+ continue
+ else
+ break
+ fi
+ ;;
+ msvisualcpp | msvcmsys)
+ # This compiler won't grok `-c -o', but also, the minuso test has
+ # not run yet. These depmodes are late enough in the game, and
+ # so weak that their functioning should not be impacted.
+ am__obj=conftest.${OBJEXT-o}
+ am__minus_obj=
+ ;;
+ none) break ;;
+ esac
+ if depmode=$depmode \
+ source=sub/conftest.c object=$am__obj \
+ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
+ >/dev/null 2>conftest.err &&
+ grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
+ ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+ # icc doesn't choke on unknown options, it will just issue warnings
+ # or remarks (even with -Werror). So we grep stderr for any message
+ # that says an option was ignored or not supported.
+ # When given -MP, icc 7.0 and 7.1 complain thusly:
+ # icc: Command line warning: ignoring option '-M'; no argument required
+ # The diagnosis changed in icc 8.0:
+ # icc: Command line remark: option '-MP' not supported
+ if (grep 'ignoring option' conftest.err ||
+ grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+ am_cv_CC_dependencies_compiler_type=$depmode
+ break
+ fi
+ fi
+ done
+
+ cd ..
+ rm -rf conftest.dir
+else
+ am_cv_CC_dependencies_compiler_type=none
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5
+$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; }
+CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type
+
+ if
+ test "x$enable_dependency_tracking" != xno \
+ && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then
+ am__fastdepCC_TRUE=
+ am__fastdepCC_FALSE='#'
+else
+ am__fastdepCC_TRUE='#'
+ am__fastdepCC_FALSE=
+fi
+
+
+ case $ac_cv_prog_cc_stdc in @%:@(
+ no) :
+ ac_cv_prog_cc_c99=no; ac_cv_prog_cc_c89=no ;; @%:@(
+ *) :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C99" >&5
+$as_echo_n "checking for $CC option to accept ISO C99... " >&6; }
+if ${ac_cv_prog_cc_c99+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_prog_cc_c99=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <wchar.h>
+#include <stdio.h>
+
+// Check varargs macros. These examples are taken from C99 6.10.3.5.
+#define debug(...) fprintf (stderr, __VA_ARGS__)
+#define showlist(...) puts (#__VA_ARGS__)
+#define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__))
+static void
+test_varargs_macros (void)
+{
+ int x = 1234;
+ int y = 5678;
+ debug ("Flag");
+ debug ("X = %d\n", x);
+ showlist (The first, second, and third items.);
+ report (x>y, "x is %d but y is %d", x, y);
+}
+
+// Check long long types.
+#define BIG64 18446744073709551615ull
+#define BIG32 4294967295ul
+#define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0)
+#if !BIG_OK
+ your preprocessor is broken;
+#endif
+#if BIG_OK
+#else
+ your preprocessor is broken;
+#endif
+static long long int bignum = -9223372036854775807LL;
+static unsigned long long int ubignum = BIG64;
+
+struct incomplete_array
+{
+ int datasize;
+ double data[];
+};
+
+struct named_init {
+ int number;
+ const wchar_t *name;
+ double average;
+};
+
+typedef const char *ccp;
+
+static inline int
+test_restrict (ccp restrict text)
+{
+ // See if C++-style comments work.
+ // Iterate through items via the restricted pointer.
+ // Also check for declarations in for loops.
+ for (unsigned int i = 0; *(text+i) != '\0'; ++i)
+ continue;
+ return 0;
+}
+
+// Check varargs and va_copy.
+static void
+test_varargs (const char *format, ...)
+{
+ va_list args;
+ va_start (args, format);
+ va_list args_copy;
+ va_copy (args_copy, args);
+
+ const char *str;
+ int number;
+ float fnumber;
+
+ while (*format)
+ {
+ switch (*format++)
+ {
+ case 's': // string
+ str = va_arg (args_copy, const char *);
+ break;
+ case 'd': // int
+ number = va_arg (args_copy, int);
+ break;
+ case 'f': // float
+ fnumber = va_arg (args_copy, double);
+ break;
+ default:
+ break;
+ }
+ }
+ va_end (args_copy);
+ va_end (args);
+}
+
+int
+main ()
+{
+
+ // Check bool.
+ _Bool success = false;
+
+ // Check restrict.
+ if (test_restrict ("String literal") == 0)
+ success = true;
+ char *restrict newvar = "Another string";
+
+ // Check varargs.
+ test_varargs ("s, d' f .", "string", 65, 34.234);
+ test_varargs_macros ();
+
+ // Check flexible array members.
+ struct incomplete_array *ia =
+ malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10));
+ ia->datasize = 10;
+ for (int i = 0; i < ia->datasize; ++i)
+ ia->data[i] = i * 1.234;
+
+ // Check named initializers.
+ struct named_init ni = {
+ .number = 34,
+ .name = L"Test wide string",
+ .average = 543.34343,
+ };
+
+ ni.number = 58;
+
+ int dynamic_array[ni.number];
+ dynamic_array[ni.number - 1] = 543;
+
+ // work around unused variable warnings
+ return (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == 'x'
+ || dynamic_array[ni.number - 1] != 543);
+
+ ;
+ return 0;
+}
+_ACEOF
+for ac_arg in '' -std=gnu99 -std=c99 -c99 -AC99 -xc99=all -qlanglvl=extc99
+do
+ CC="$ac_save_CC $ac_arg"
+ if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_c99=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+ test "x$ac_cv_prog_cc_c99" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c99" in
+ x)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+ xno)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+ *)
+ CC="$CC $ac_cv_prog_cc_c99"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5
+$as_echo "$ac_cv_prog_cc_c99" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c99" != xno; then :
+ ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if ${ac_cv_prog_cc_c89+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
+ function prototypes and stuff, but not '\xHH' hex character constants.
+ These don't provoke an error unfortunately, instead are silently treated
+ as 'x'. The following induces an error, until -std is added to get
+ proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
+ array size at least. It's necessary to write '\x00'==0 to get something
+ that's true only with -std. */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+ inside strings and character constants. */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
+ ;
+ return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+ test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+ x)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+ xno)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+ *)
+ CC="$CC $ac_cv_prog_cc_c89"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c89" != xno; then :
+ ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89
+else
+ ac_cv_prog_cc_stdc=no
+fi
+
+fi
+ ;;
+esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO Standard C" >&5
+$as_echo_n "checking for $CC option to accept ISO Standard C... " >&6; }
+ if ${ac_cv_prog_cc_stdc+:} false; then :
+ $as_echo_n "(cached) " >&6
+fi
+
+ case $ac_cv_prog_cc_stdc in @%:@(
+ no) :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;; @%:@(
+ '') :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;; @%:@(
+ *) :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_stdc" >&5
+$as_echo "$ac_cv_prog_cc_stdc" >&6; } ;;
+esac
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5
+$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; }
+set x ${MAKE-make}
+ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
+if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.make <<\_ACEOF
+SHELL = /bin/sh
+all:
+ @echo '@@@%%%=$(MAKE)=@@@%%%'
+_ACEOF
+# GNU make sometimes prints "make[1]: Entering ...", which would confuse us.
+case `${MAKE-make} -f conftest.make 2>/dev/null` in
+ *@@@%%%=?*=@@@%%%*)
+ eval ac_cv_prog_make_${ac_make}_set=yes;;
+ *)
+ eval ac_cv_prog_make_${ac_make}_set=no;;
+esac
+rm -f conftest.make
+fi
+if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ SET_MAKE=
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5
+$as_echo_n "checking whether ln -s works... " >&6; }
+LN_S=$as_ln_s
+if test "$LN_S" = "ln -s"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5
+$as_echo "no, using $LN_S" >&6; }
+fi
+
+for ac_prog in gawk mawk nawk awk
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_AWK+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$AWK"; then
+ ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_AWK="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+AWK=$ac_cv_prog_AWK
+if test -n "$AWK"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5
+$as_echo "$AWK" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$AWK" && break
+done
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_RANLIB+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$RANLIB"; then
+ ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5
+$as_echo "$RANLIB" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+ ac_ct_RANLIB=$RANLIB
+ # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_RANLIB+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_RANLIB"; then
+ ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_RANLIB="ranlib"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5
+$as_echo "$ac_ct_RANLIB" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_RANLIB" = x; then
+ RANLIB=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ RANLIB=$ac_ct_RANLIB
+ fi
+else
+ RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+case `pwd` in
+ *\ * | *\ *)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5
+$as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;;
+esac
+
+
+
+macro_version='2.2.6b'
+macro_revision='1.3018'
+
+
+
+
+
+
+
+
+
+
+
+
+
+ltmain="$ac_aux_dir/ltmain.sh"
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5
+$as_echo_n "checking for a sed that does not truncate output... " >&6; }
+if ${ac_cv_path_SED+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
+ for ac_i in 1 2 3 4 5 6 7; do
+ ac_script="$ac_script$as_nl$ac_script"
+ done
+ echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed
+ { ac_script=; unset ac_script;}
+ if test -z "$SED"; then
+ ac_path_SED_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in sed gsed; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_SED="$as_dir/$ac_prog$ac_exec_ext"
+ { test -f "$ac_path_SED" && $as_test_x "$ac_path_SED"; } || continue
+# Check for GNU ac_path_SED and select it if it is found.
+ # Check for GNU $ac_path_SED
+case `"$ac_path_SED" --version 2>&1` in
+*GNU*)
+ ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo '' >> "conftest.nl"
+ "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_SED_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_SED="$ac_path_SED"
+ ac_path_SED_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_SED_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_SED"; then
+ as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5
+ fi
+else
+ ac_cv_path_SED=$SED
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5
+$as_echo "$ac_cv_path_SED" >&6; }
+ SED="$ac_cv_path_SED"
+ rm -f conftest.sed
+
+test -z "$SED" && SED=sed
+Xsed="$SED -e 1s/^X//"
+
+
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
+$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
+if ${ac_cv_path_GREP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$GREP"; then
+ ac_path_GREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in grep ggrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
+ { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue
+# Check for GNU ac_path_GREP and select it if it is found.
+ # Check for GNU $ac_path_GREP
+case `"$ac_path_GREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo 'GREP' >> "conftest.nl"
+ "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_GREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_GREP="$ac_path_GREP"
+ ac_path_GREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_GREP_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_GREP"; then
+ as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ fi
+else
+ ac_cv_path_GREP=$GREP
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5
+$as_echo "$ac_cv_path_GREP" >&6; }
+ GREP="$ac_cv_path_GREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5
+$as_echo_n "checking for egrep... " >&6; }
+if ${ac_cv_path_EGREP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
+ then ac_cv_path_EGREP="$GREP -E"
+ else
+ if test -z "$EGREP"; then
+ ac_path_EGREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in egrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
+ { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue
+# Check for GNU ac_path_EGREP and select it if it is found.
+ # Check for GNU $ac_path_EGREP
+case `"$ac_path_EGREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo 'EGREP' >> "conftest.nl"
+ "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_EGREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_EGREP="$ac_path_EGREP"
+ ac_path_EGREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_EGREP_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_EGREP"; then
+ as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ fi
+else
+ ac_cv_path_EGREP=$EGREP
+fi
+
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5
+$as_echo "$ac_cv_path_EGREP" >&6; }
+ EGREP="$ac_cv_path_EGREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5
+$as_echo_n "checking for fgrep... " >&6; }
+if ${ac_cv_path_FGREP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1
+ then ac_cv_path_FGREP="$GREP -F"
+ else
+ if test -z "$FGREP"; then
+ ac_path_FGREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in fgrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext"
+ { test -f "$ac_path_FGREP" && $as_test_x "$ac_path_FGREP"; } || continue
+# Check for GNU ac_path_FGREP and select it if it is found.
+ # Check for GNU $ac_path_FGREP
+case `"$ac_path_FGREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo 'FGREP' >> "conftest.nl"
+ "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_FGREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_FGREP="$ac_path_FGREP"
+ ac_path_FGREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_FGREP_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_FGREP"; then
+ as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ fi
+else
+ ac_cv_path_FGREP=$FGREP
+fi
+
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5
+$as_echo "$ac_cv_path_FGREP" >&6; }
+ FGREP="$ac_cv_path_FGREP"
+
+
+test -z "$GREP" && GREP=grep
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+@%:@ Check whether --with-gnu-ld was given.
+if test "${with_gnu_ld+set}" = set; then :
+ withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes
+else
+ with_gnu_ld=no
+fi
+
+ac_prog=ld
+if test "$GCC" = yes; then
+ # Check if gcc -print-prog-name=ld gives a path.
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5
+$as_echo_n "checking for ld used by $CC... " >&6; }
+ case $host in
+ *-*-mingw*)
+ # gcc leaves a trailing carriage return which upsets mingw
+ ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+ *)
+ ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+ esac
+ case $ac_prog in
+ # Accept absolute paths.
+ [\\/]* | ?:[\\/]*)
+ re_direlt='/[^/][^/]*/\.\./'
+ # Canonicalize the pathname of ld
+ ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'`
+ while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
+ ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
+ done
+ test -z "$LD" && LD="$ac_prog"
+ ;;
+ "")
+ # If it fails, then pretend we aren't using GCC.
+ ac_prog=ld
+ ;;
+ *)
+ # If it is relative, then search for the first ld in PATH.
+ with_gnu_ld=unknown
+ ;;
+ esac
+elif test "$with_gnu_ld" = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5
+$as_echo_n "checking for GNU ld... " >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5
+$as_echo_n "checking for non-GNU ld... " >&6; }
+fi
+if ${lt_cv_path_LD+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$LD"; then
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+ lt_cv_path_LD="$ac_dir/$ac_prog"
+ # Check to see if the program is GNU ld. I'd rather use --version,
+ # but apparently some variants of GNU ld only accept -v.
+ # Break only if it was the GNU/non-GNU ld that we prefer.
+ case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
+ *GNU* | *'with BFD'*)
+ test "$with_gnu_ld" != no && break
+ ;;
+ *)
+ test "$with_gnu_ld" != yes && break
+ ;;
+ esac
+ fi
+ done
+ IFS="$lt_save_ifs"
+else
+ lt_cv_path_LD="$LD" # Let the user override the test with a path.
+fi
+fi
+
+LD="$lt_cv_path_LD"
+if test -n "$LD"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LD" >&5
+$as_echo "$LD" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5
+$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; }
+if ${lt_cv_prog_gnu_ld+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ # I'd rather use --version here, but apparently some GNU lds only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+ lt_cv_prog_gnu_ld=yes
+ ;;
+*)
+ lt_cv_prog_gnu_ld=no
+ ;;
+esac
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_gnu_ld" >&5
+$as_echo "$lt_cv_prog_gnu_ld" >&6; }
+with_gnu_ld=$lt_cv_prog_gnu_ld
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5
+$as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; }
+if ${lt_cv_path_NM+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$NM"; then
+ # Let the user override the test.
+ lt_cv_path_NM="$NM"
+else
+ lt_nm_to_check="${ac_tool_prefix}nm"
+ if test -n "$ac_tool_prefix" && test "$build" = "$host"; then
+ lt_nm_to_check="$lt_nm_to_check nm"
+ fi
+ for lt_tmp_nm in $lt_nm_to_check; do
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ tmp_nm="$ac_dir/$lt_tmp_nm"
+ if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then
+ # Check to see if the nm accepts a BSD-compat flag.
+ # Adding the `sed 1q' prevents false positives on HP-UX, which says:
+ # nm: unknown option "B" ignored
+ # Tru64's nm complains that /dev/null is an invalid object file
+ case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in
+ */dev/null* | *'Invalid file or object type'*)
+ lt_cv_path_NM="$tmp_nm -B"
+ break
+ ;;
+ *)
+ case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in
+ */dev/null*)
+ lt_cv_path_NM="$tmp_nm -p"
+ break
+ ;;
+ *)
+ lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
+ continue # so that we can try to find one that supports BSD flags
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ done
+ IFS="$lt_save_ifs"
+ done
+ : ${lt_cv_path_NM=no}
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5
+$as_echo "$lt_cv_path_NM" >&6; }
+if test "$lt_cv_path_NM" != "no"; then
+ NM="$lt_cv_path_NM"
+else
+ # Didn't find any BSD compatible name lister, look for dumpbin.
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in "dumpbin -symbols" "link -dump -symbols"
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_DUMPBIN+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$DUMPBIN"; then
+ ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+DUMPBIN=$ac_cv_prog_DUMPBIN
+if test -n "$DUMPBIN"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5
+$as_echo "$DUMPBIN" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$DUMPBIN" && break
+ done
+fi
+if test -z "$DUMPBIN"; then
+ ac_ct_DUMPBIN=$DUMPBIN
+ for ac_prog in "dumpbin -symbols" "link -dump -symbols"
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_DUMPBIN+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_DUMPBIN"; then
+ ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_DUMPBIN="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN
+if test -n "$ac_ct_DUMPBIN"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5
+$as_echo "$ac_ct_DUMPBIN" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_DUMPBIN" && break
+done
+
+ if test "x$ac_ct_DUMPBIN" = x; then
+ DUMPBIN=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ DUMPBIN=$ac_ct_DUMPBIN
+ fi
+fi
+
+
+ if test "$DUMPBIN" != ":"; then
+ NM="$DUMPBIN"
+ fi
+fi
+test -z "$NM" && NM=nm
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5
+$as_echo_n "checking the name lister ($NM) interface... " >&6; }
+if ${lt_cv_nm_interface+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_nm_interface="BSD nm"
+ echo "int some_variable = 0;" > conftest.$ac_ext
+ (eval echo "\"\$as_me:__oline__: $ac_compile\"" >&5)
+ (eval "$ac_compile" 2>conftest.err)
+ cat conftest.err >&5
+ (eval echo "\"\$as_me:__oline__: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
+ (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
+ cat conftest.err >&5
+ (eval echo "\"\$as_me:__oline__: output\"" >&5)
+ cat conftest.out >&5
+ if $GREP 'External.*some_variable' conftest.out > /dev/null; then
+ lt_cv_nm_interface="MS dumpbin"
+ fi
+ rm -f conftest*
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5
+$as_echo "$lt_cv_nm_interface" >&6; }
+
+# find the maximum length of command line arguments
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5
+$as_echo_n "checking the maximum length of command line arguments... " >&6; }
+if ${lt_cv_sys_max_cmd_len+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ i=0
+ teststring="ABCD"
+
+ case $build_os in
+ msdosdjgpp*)
+ # On DJGPP, this test can blow up pretty badly due to problems in libc
+ # (any single argument exceeding 2000 bytes causes a buffer overrun
+ # during glob expansion). Even if it were fixed, the result of this
+ # check would be larger than it should be.
+ lt_cv_sys_max_cmd_len=12288; # 12K is about right
+ ;;
+
+ gnu*)
+ # Under GNU Hurd, this test is not required because there is
+ # no limit to the length of command line arguments.
+ # Libtool will interpret -1 as no limit whatsoever
+ lt_cv_sys_max_cmd_len=-1;
+ ;;
+
+ cygwin* | mingw* | cegcc*)
+ # On Win9x/ME, this test blows up -- it succeeds, but takes
+ # about 5 minutes as the teststring grows exponentially.
+ # Worse, since 9x/ME are not pre-emptively multitasking,
+ # you end up with a "frozen" computer, even though with patience
+ # the test eventually succeeds (with a max line length of 256k).
+ # Instead, let's just punt: use the minimum linelength reported by
+ # all of the supported platforms: 8192 (on NT/2K/XP).
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ amigaos*)
+ # On AmigaOS with pdksh, this test takes hours, literally.
+ # So we just punt and use a minimum line length of 8192.
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ netbsd* | freebsd* | openbsd* | darwin* | dragonfly*)
+ # This has been around since 386BSD, at least. Likely further.
+ if test -x /sbin/sysctl; then
+ lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax`
+ elif test -x /usr/sbin/sysctl; then
+ lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax`
+ else
+ lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs
+ fi
+ # And add a safety zone
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+ ;;
+
+ interix*)
+ # We know the value 262144 and hardcode it with a safety zone (like BSD)
+ lt_cv_sys_max_cmd_len=196608
+ ;;
+
+ osf*)
+ # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure
+ # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not
+ # nice to cause kernel panics so lets avoid the loop below.
+ # First set a reasonable default.
+ lt_cv_sys_max_cmd_len=16384
+ #
+ if test -x /sbin/sysconfig; then
+ case `/sbin/sysconfig -q proc exec_disable_arg_limit` in
+ *1*) lt_cv_sys_max_cmd_len=-1 ;;
+ esac
+ fi
+ ;;
+ sco3.2v5*)
+ lt_cv_sys_max_cmd_len=102400
+ ;;
+ sysv5* | sco5v6* | sysv4.2uw2*)
+ kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null`
+ if test -n "$kargmax"; then
+ lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'`
+ else
+ lt_cv_sys_max_cmd_len=32768
+ fi
+ ;;
+ *)
+ lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null`
+ if test -n "$lt_cv_sys_max_cmd_len"; then
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+ else
+ # Make teststring a little bigger before we do anything with it.
+ # a 1K string should be a reasonable start.
+ for i in 1 2 3 4 5 6 7 8 ; do
+ teststring=$teststring$teststring
+ done
+ SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}}
+ # If test is not a shell built-in, we'll probably end up computing a
+ # maximum length that is only half of the actual maximum length, but
+ # we can't tell.
+ while { test "X"`$SHELL $0 --fallback-echo "X$teststring$teststring" 2>/dev/null` \
+ = "XX$teststring$teststring"; } >/dev/null 2>&1 &&
+ test $i != 17 # 1/2 MB should be enough
+ do
+ i=`expr $i + 1`
+ teststring=$teststring$teststring
+ done
+ # Only check the string length outside the loop.
+ lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1`
+ teststring=
+ # Add a significant safety factor because C++ compilers can tack on
+ # massive amounts of additional arguments before passing them to the
+ # linker. It appears as though 1/2 is a usable value.
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2`
+ fi
+ ;;
+ esac
+
+fi
+
+if test -n $lt_cv_sys_max_cmd_len ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5
+$as_echo "$lt_cv_sys_max_cmd_len" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5
+$as_echo "none" >&6; }
+fi
+max_cmd_len=$lt_cv_sys_max_cmd_len
+
+
+
+
+
+
+: ${CP="cp -f"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands some XSI constructs" >&5
+$as_echo_n "checking whether the shell understands some XSI constructs... " >&6; }
+# Try some XSI features
+xsi_shell=no
+( _lt_dummy="a/b/c"
+ test "${_lt_dummy##*/},${_lt_dummy%/*},"${_lt_dummy%"$_lt_dummy"}, \
+ = c,a/b,, \
+ && eval 'test $(( 1 + 1 )) -eq 2 \
+ && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \
+ && xsi_shell=yes
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $xsi_shell" >&5
+$as_echo "$xsi_shell" >&6; }
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands \"+=\"" >&5
+$as_echo_n "checking whether the shell understands \"+=\"... " >&6; }
+lt_shell_append=no
+( foo=bar; set foo baz; eval "$1+=\$2" && test "$foo" = barbaz ) \
+ >/dev/null 2>&1 \
+ && lt_shell_append=yes
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_shell_append" >&5
+$as_echo "$lt_shell_append" >&6; }
+
+
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ lt_unset=unset
+else
+ lt_unset=false
+fi
+
+
+
+
+
+# test EBCDIC or ASCII
+case `echo X|tr X '\101'` in
+ A) # ASCII based system
+ # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr
+ lt_SP2NL='tr \040 \012'
+ lt_NL2SP='tr \015\012 \040\040'
+ ;;
+ *) # EBCDIC based system
+ lt_SP2NL='tr \100 \n'
+ lt_NL2SP='tr \r\n \100\100'
+ ;;
+esac
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5
+$as_echo_n "checking for $LD option to reload object files... " >&6; }
+if ${lt_cv_ld_reload_flag+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_ld_reload_flag='-r'
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5
+$as_echo "$lt_cv_ld_reload_flag" >&6; }
+reload_flag=$lt_cv_ld_reload_flag
+case $reload_flag in
+"" | " "*) ;;
+*) reload_flag=" $reload_flag" ;;
+esac
+reload_cmds='$LD$reload_flag -o $output$reload_objs'
+case $host_os in
+ darwin*)
+ if test "$GCC" = yes; then
+ reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs'
+ else
+ reload_cmds='$LD$reload_flag -o $output$reload_objs'
+ fi
+ ;;
+esac
+
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args.
+set dummy ${ac_tool_prefix}objdump; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_OBJDUMP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$OBJDUMP"; then
+ ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+OBJDUMP=$ac_cv_prog_OBJDUMP
+if test -n "$OBJDUMP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5
+$as_echo "$OBJDUMP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OBJDUMP"; then
+ ac_ct_OBJDUMP=$OBJDUMP
+ # Extract the first word of "objdump", so it can be a program name with args.
+set dummy objdump; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_OBJDUMP"; then
+ ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_OBJDUMP="objdump"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP
+if test -n "$ac_ct_OBJDUMP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5
+$as_echo "$ac_ct_OBJDUMP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_OBJDUMP" = x; then
+ OBJDUMP="false"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ OBJDUMP=$ac_ct_OBJDUMP
+ fi
+else
+ OBJDUMP="$ac_cv_prog_OBJDUMP"
+fi
+
+test -z "$OBJDUMP" && OBJDUMP=objdump
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5
+$as_echo_n "checking how to recognize dependent libraries... " >&6; }
+if ${lt_cv_deplibs_check_method+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_file_magic_cmd='$MAGIC_CMD'
+lt_cv_file_magic_test_file=
+lt_cv_deplibs_check_method='unknown'
+# Need to set the preceding variable on all platforms that support
+# interlibrary dependencies.
+# 'none' -- dependencies not supported.
+# `unknown' -- same as none, but documents that we really don't know.
+# 'pass_all' -- all dependencies passed with no checks.
+# 'test_compile' -- check by making test program.
+# 'file_magic [[regex]]' -- check by looking for files in library path
+# which responds to the $file_magic_cmd with a given extended regex.
+# If you have `file' or equivalent on your system and you're not sure
+# whether `pass_all' will *always* work, you probably want this one.
+
+case $host_os in
+aix[4-9]*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+beos*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+bsdi[45]*)
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)'
+ lt_cv_file_magic_cmd='/usr/bin/file -L'
+ lt_cv_file_magic_test_file=/shlib/libc.so
+ ;;
+
+cygwin*)
+ # func_win32_libid is a shell function defined in ltmain.sh
+ lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+ lt_cv_file_magic_cmd='func_win32_libid'
+ ;;
+
+mingw* | pw32*)
+ # Base MSYS/MinGW do not provide the 'file' command needed by
+ # func_win32_libid shell function, so use a weaker test based on 'objdump',
+ # unless we find 'file', for example because we are cross-compiling.
+ if ( file / ) >/dev/null 2>&1; then
+ lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+ lt_cv_file_magic_cmd='func_win32_libid'
+ else
+ lt_cv_deplibs_check_method='file_magic file format pei*-i386(.*architecture: i386)?'
+ lt_cv_file_magic_cmd='$OBJDUMP -f'
+ fi
+ ;;
+
+cegcc)
+ # use the weaker test based on 'objdump'. See mingw*.
+ lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?'
+ lt_cv_file_magic_cmd='$OBJDUMP -f'
+ ;;
+
+darwin* | rhapsody*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+freebsd* | dragonfly*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+ case $host_cpu in
+ i*86 )
+ # Not sure whether the presence of OpenBSD here was a mistake.
+ # Let's accept both of them until this is cleared up.
+ lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library'
+ lt_cv_file_magic_cmd=/usr/bin/file
+ lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
+ ;;
+ esac
+ else
+ lt_cv_deplibs_check_method=pass_all
+ fi
+ ;;
+
+gnu*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+hpux10.20* | hpux11*)
+ lt_cv_file_magic_cmd=/usr/bin/file
+ case $host_cpu in
+ ia64*)
+ lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64'
+ lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so
+ ;;
+ hppa*64*)
+ lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - PA-RISC [0-9].[0-9]'
+ lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl
+ ;;
+ *)
+ lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9].[0-9]) shared library'
+ lt_cv_file_magic_test_file=/usr/lib/libc.sl
+ ;;
+ esac
+ ;;
+
+interix[3-9]*)
+ # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$'
+ ;;
+
+irix5* | irix6* | nonstopux*)
+ case $LD in
+ *-32|*"-32 ") libmagic=32-bit;;
+ *-n32|*"-n32 ") libmagic=N32;;
+ *-64|*"-64 ") libmagic=64-bit;;
+ *) libmagic=never-match;;
+ esac
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+# This must be Linux ELF.
+linux* | k*bsd*-gnu)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
+ else
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$'
+ fi
+ ;;
+
+newos6*)
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)'
+ lt_cv_file_magic_cmd=/usr/bin/file
+ lt_cv_file_magic_test_file=/usr/lib/libnls.so
+ ;;
+
+*nto* | *qnx*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+openbsd*)
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$'
+ else
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
+ fi
+ ;;
+
+osf3* | osf4* | osf5*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+rdos*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+solaris*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+sysv4 | sysv4.3*)
+ case $host_vendor in
+ motorola)
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]'
+ lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*`
+ ;;
+ ncr)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ sequent)
+ lt_cv_file_magic_cmd='/bin/file'
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )'
+ ;;
+ sni)
+ lt_cv_file_magic_cmd='/bin/file'
+ lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib"
+ lt_cv_file_magic_test_file=/lib/libc.so
+ ;;
+ siemens)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ pc)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ esac
+ ;;
+
+tpf*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+esac
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5
+$as_echo "$lt_cv_deplibs_check_method" >&6; }
+file_magic_cmd=$lt_cv_file_magic_cmd
+deplibs_check_method=$lt_cv_deplibs_check_method
+test -z "$deplibs_check_method" && deplibs_check_method=unknown
+
+
+
+
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ar; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_AR+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$AR"; then
+ ac_cv_prog_AR="$AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_AR="${ac_tool_prefix}ar"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+AR=$ac_cv_prog_AR
+if test -n "$AR"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5
+$as_echo "$AR" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_AR"; then
+ ac_ct_AR=$AR
+ # Extract the first word of "ar", so it can be a program name with args.
+set dummy ar; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_AR+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_AR"; then
+ ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_AR="ar"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_AR=$ac_cv_prog_ac_ct_AR
+if test -n "$ac_ct_AR"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5
+$as_echo "$ac_ct_AR" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_AR" = x; then
+ AR="false"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ AR=$ac_ct_AR
+ fi
+else
+ AR="$ac_cv_prog_AR"
+fi
+
+test -z "$AR" && AR=ar
+test -z "$AR_FLAGS" && AR_FLAGS=cru
+
+
+
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_STRIP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$STRIP"; then
+ ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5
+$as_echo "$STRIP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_STRIP"; then
+ ac_ct_STRIP=$STRIP
+ # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_STRIP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_STRIP"; then
+ ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_STRIP="strip"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5
+$as_echo "$ac_ct_STRIP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_STRIP" = x; then
+ STRIP=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ STRIP=$ac_ct_STRIP
+ fi
+else
+ STRIP="$ac_cv_prog_STRIP"
+fi
+
+test -z "$STRIP" && STRIP=:
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_RANLIB+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$RANLIB"; then
+ ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5
+$as_echo "$RANLIB" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+ ac_ct_RANLIB=$RANLIB
+ # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_RANLIB+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_RANLIB"; then
+ ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_RANLIB="ranlib"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5
+$as_echo "$ac_ct_RANLIB" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_RANLIB" = x; then
+ RANLIB=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ RANLIB=$ac_ct_RANLIB
+ fi
+else
+ RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+test -z "$RANLIB" && RANLIB=:
+
+
+
+
+
+
+# Determine commands to create old-style static archives.
+old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs'
+old_postinstall_cmds='chmod 644 $oldlib'
+old_postuninstall_cmds=
+
+if test -n "$RANLIB"; then
+ case $host_os in
+ openbsd*)
+ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$oldlib"
+ ;;
+ *)
+ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$oldlib"
+ ;;
+ esac
+ old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib"
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+
+# Check for command to grab the raw symbol name followed by C symbol from nm.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5
+$as_echo_n "checking command to parse $NM output from $compiler object... " >&6; }
+if ${lt_cv_sys_global_symbol_pipe+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+# These are sane defaults that work on at least a few old systems.
+# [They come from Ultrix. What could be older than Ultrix?!! ;)]
+
+# Character class describing NM global symbol codes.
+symcode='[BCDEGRST]'
+
+# Regexp to match symbols that can be accessed directly from C.
+sympat='\([_A-Za-z][_A-Za-z0-9]*\)'
+
+# Define system-specific variables.
+case $host_os in
+aix*)
+ symcode='[BCDT]'
+ ;;
+cygwin* | mingw* | pw32* | cegcc*)
+ symcode='[ABCDGISTW]'
+ ;;
+hpux*)
+ if test "$host_cpu" = ia64; then
+ symcode='[ABCDEGRST]'
+ fi
+ ;;
+irix* | nonstopux*)
+ symcode='[BCDEGRST]'
+ ;;
+osf*)
+ symcode='[BCDEGQRST]'
+ ;;
+solaris*)
+ symcode='[BDRT]'
+ ;;
+sco3.2v5*)
+ symcode='[DT]'
+ ;;
+sysv4.2uw2*)
+ symcode='[DT]'
+ ;;
+sysv5* | sco5v6* | unixware* | OpenUNIX*)
+ symcode='[ABDT]'
+ ;;
+sysv4)
+ symcode='[DFNSTU]'
+ ;;
+esac
+
+# If we're using GNU nm, then use its standard symbol codes.
+case `$NM -V 2>&1` in
+*GNU* | *'with BFD'*)
+ symcode='[ABCDGIRSTW]' ;;
+esac
+
+# Transform an extracted symbol line into a proper C declaration.
+# Some systems (esp. on ia64) link data and code symbols differently,
+# so use this general approach.
+lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'"
+
+# Transform an extracted symbol line into symbol name and symbol address
+lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"\2\", (void *) \&\2},/p'"
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([^ ]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \(lib[^ ]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"lib\2\", (void *) \&\2},/p'"
+
+# Handle CRLF in mingw tool chain
+opt_cr=
+case $build_os in
+mingw*)
+ opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp
+ ;;
+esac
+
+# Try without a prefix underscore, then with it.
+for ac_symprfx in "" "_"; do
+
+ # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol.
+ symxfrm="\\1 $ac_symprfx\\2 \\2"
+
+ # Write the raw and C identifiers.
+ if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+ # Fake it for dumpbin and say T for any non-static function
+ # and D for any global variable.
+ # Also find C++ and __fastcall symbols from MSVC++,
+ # which start with @ or ?.
+ lt_cv_sys_global_symbol_pipe="$AWK '"\
+" {last_section=section; section=\$ 3};"\
+" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\
+" \$ 0!~/External *\|/{next};"\
+" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\
+" {if(hide[section]) next};"\
+" {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\
+" {split(\$ 0, a, /\||\r/); split(a[2], s)};"\
+" s[1]~/^[@?]/{print s[1], s[1]; next};"\
+" s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\
+" ' prfx=^$ac_symprfx"
+ else
+ lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'"
+ fi
+
+ # Check to see that the pipe works correctly.
+ pipe_works=no
+
+ rm -f conftest*
+ cat > conftest.$ac_ext <<_LT_EOF
+#ifdef __cplusplus
+extern "C" {
+#endif
+char nm_test_var;
+void nm_test_func(void);
+void nm_test_func(void){}
+#ifdef __cplusplus
+}
+#endif
+int main(){nm_test_var='a';nm_test_func();return(0);}
+_LT_EOF
+
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ # Now try to grab the symbols.
+ nlist=conftest.nm
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist\""; } >&5
+ (eval $NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && test -s "$nlist"; then
+ # Try sorting and uniquifying the output.
+ if sort "$nlist" | uniq > "$nlist"T; then
+ mv -f "$nlist"T "$nlist"
+ else
+ rm -f "$nlist"T
+ fi
+
+ # Make sure that we snagged all the symbols we need.
+ if $GREP ' nm_test_var$' "$nlist" >/dev/null; then
+ if $GREP ' nm_test_func$' "$nlist" >/dev/null; then
+ cat <<_LT_EOF > conftest.$ac_ext
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+_LT_EOF
+ # Now generate the symbol file.
+ eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext'
+
+ cat <<_LT_EOF >> conftest.$ac_ext
+
+/* The mapping between symbol names and symbols. */
+const struct {
+ const char *name;
+ void *address;
+}
+lt__PROGRAM__LTX_preloaded_symbols[] =
+{
+ { "@PROGRAM@", (void *) 0 },
+_LT_EOF
+ $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext
+ cat <<\_LT_EOF >> conftest.$ac_ext
+ {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+ return lt__PROGRAM__LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+_LT_EOF
+ # Now try linking the two files.
+ mv conftest.$ac_objext conftstm.$ac_objext
+ lt_save_LIBS="$LIBS"
+ lt_save_CFLAGS="$CFLAGS"
+ LIBS="conftstm.$ac_objext"
+ CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag"
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && test -s conftest${ac_exeext}; then
+ pipe_works=yes
+ fi
+ LIBS="$lt_save_LIBS"
+ CFLAGS="$lt_save_CFLAGS"
+ else
+ echo "cannot find nm_test_func in $nlist" >&5
+ fi
+ else
+ echo "cannot find nm_test_var in $nlist" >&5
+ fi
+ else
+ echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5
+ fi
+ else
+ echo "$progname: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ fi
+ rm -rf conftest* conftst*
+
+ # Do not use the global_symbol_pipe unless it works.
+ if test "$pipe_works" = yes; then
+ break
+ else
+ lt_cv_sys_global_symbol_pipe=
+ fi
+done
+
+fi
+
+if test -z "$lt_cv_sys_global_symbol_pipe"; then
+ lt_cv_sys_global_symbol_to_cdecl=
+fi
+if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5
+$as_echo "failed" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5
+$as_echo "ok" >&6; }
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+@%:@ Check whether --enable-libtool-lock was given.
+if test "${enable_libtool_lock+set}" = set; then :
+ enableval=$enable_libtool_lock;
+fi
+
+test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes
+
+# Some flags need to be propagated to the compiler or linker for good
+# libtool support.
+case $host in
+ia64-*-hpux*)
+ # Find out which ABI we are using.
+ echo 'int i;' > conftest.$ac_ext
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ case `/usr/bin/file conftest.$ac_objext` in
+ *ELF-32*)
+ HPUX_IA64_MODE="32"
+ ;;
+ *ELF-64*)
+ HPUX_IA64_MODE="64"
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+*-*-irix6*)
+ # Find out which ABI we are using.
+ echo '#line __oline__ "configure"' > conftest.$ac_ext
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ if test "$lt_cv_prog_gnu_ld" = yes; then
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ LD="${LD-ld} -melf32bsmip"
+ ;;
+ *N32*)
+ LD="${LD-ld} -melf32bmipn32"
+ ;;
+ *64-bit*)
+ LD="${LD-ld} -melf64bmip"
+ ;;
+ esac
+ else
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ LD="${LD-ld} -32"
+ ;;
+ *N32*)
+ LD="${LD-ld} -n32"
+ ;;
+ *64-bit*)
+ LD="${LD-ld} -64"
+ ;;
+ esac
+ fi
+ fi
+ rm -rf conftest*
+ ;;
+
+x86_64-*kfreebsd*-gnu|x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*| \
+s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
+ # Find out which ABI we are using.
+ echo 'int i;' > conftest.$ac_ext
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ case `/usr/bin/file conftest.o` in
+ *32-bit*)
+ case $host in
+ x86_64-*kfreebsd*-gnu)
+ LD="${LD-ld} -m elf_i386_fbsd"
+ ;;
+ x86_64-*linux*)
+ LD="${LD-ld} -m elf_i386"
+ ;;
+ ppc64-*linux*|powerpc64-*linux*)
+ LD="${LD-ld} -m elf32ppclinux"
+ ;;
+ s390x-*linux*)
+ LD="${LD-ld} -m elf_s390"
+ ;;
+ sparc64-*linux*)
+ LD="${LD-ld} -m elf32_sparc"
+ ;;
+ esac
+ ;;
+ *64-bit*)
+ case $host in
+ x86_64-*kfreebsd*-gnu)
+ LD="${LD-ld} -m elf_x86_64_fbsd"
+ ;;
+ x86_64-*linux*)
+ LD="${LD-ld} -m elf_x86_64"
+ ;;
+ ppc*-*linux*|powerpc*-*linux*)
+ LD="${LD-ld} -m elf64ppc"
+ ;;
+ s390*-*linux*|s390*-*tpf*)
+ LD="${LD-ld} -m elf64_s390"
+ ;;
+ sparc*-*linux*)
+ LD="${LD-ld} -m elf64_sparc"
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+
+*-*-sco3.2v5*)
+ # On SCO OpenServer 5, we need -belf to get full-featured binaries.
+ SAVE_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS -belf"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5
+$as_echo_n "checking whether the C compiler needs -belf... " >&6; }
+if ${lt_cv_cc_needs_belf+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ lt_cv_cc_needs_belf=yes
+else
+ lt_cv_cc_needs_belf=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5
+$as_echo "$lt_cv_cc_needs_belf" >&6; }
+ if test x"$lt_cv_cc_needs_belf" != x"yes"; then
+ # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
+ CFLAGS="$SAVE_CFLAGS"
+ fi
+ ;;
+sparc*-*solaris*)
+ # Find out which ABI we are using.
+ echo 'int i;' > conftest.$ac_ext
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ case `/usr/bin/file conftest.o` in
+ *64-bit*)
+ case $lt_cv_prog_gnu_ld in
+ yes*) LD="${LD-ld} -m elf64_sparc" ;;
+ *)
+ if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then
+ LD="${LD-ld} -64"
+ fi
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+esac
+
+need_locks="$enable_libtool_lock"
+
+
+ case $host_os in
+ rhapsody* | darwin*)
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args.
+set dummy ${ac_tool_prefix}dsymutil; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_DSYMUTIL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$DSYMUTIL"; then
+ ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+DSYMUTIL=$ac_cv_prog_DSYMUTIL
+if test -n "$DSYMUTIL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5
+$as_echo "$DSYMUTIL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_DSYMUTIL"; then
+ ac_ct_DSYMUTIL=$DSYMUTIL
+ # Extract the first word of "dsymutil", so it can be a program name with args.
+set dummy dsymutil; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_DSYMUTIL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_DSYMUTIL"; then
+ ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_DSYMUTIL="dsymutil"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL
+if test -n "$ac_ct_DSYMUTIL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5
+$as_echo "$ac_ct_DSYMUTIL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_DSYMUTIL" = x; then
+ DSYMUTIL=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ DSYMUTIL=$ac_ct_DSYMUTIL
+ fi
+else
+ DSYMUTIL="$ac_cv_prog_DSYMUTIL"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args.
+set dummy ${ac_tool_prefix}nmedit; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_NMEDIT+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$NMEDIT"; then
+ ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+NMEDIT=$ac_cv_prog_NMEDIT
+if test -n "$NMEDIT"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5
+$as_echo "$NMEDIT" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_NMEDIT"; then
+ ac_ct_NMEDIT=$NMEDIT
+ # Extract the first word of "nmedit", so it can be a program name with args.
+set dummy nmedit; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_NMEDIT+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_NMEDIT"; then
+ ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_NMEDIT="nmedit"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT
+if test -n "$ac_ct_NMEDIT"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5
+$as_echo "$ac_ct_NMEDIT" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_NMEDIT" = x; then
+ NMEDIT=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ NMEDIT=$ac_ct_NMEDIT
+ fi
+else
+ NMEDIT="$ac_cv_prog_NMEDIT"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args.
+set dummy ${ac_tool_prefix}lipo; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_LIPO+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$LIPO"; then
+ ac_cv_prog_LIPO="$LIPO" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_LIPO="${ac_tool_prefix}lipo"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+LIPO=$ac_cv_prog_LIPO
+if test -n "$LIPO"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5
+$as_echo "$LIPO" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_LIPO"; then
+ ac_ct_LIPO=$LIPO
+ # Extract the first word of "lipo", so it can be a program name with args.
+set dummy lipo; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_LIPO+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_LIPO"; then
+ ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_LIPO="lipo"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO
+if test -n "$ac_ct_LIPO"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5
+$as_echo "$ac_ct_LIPO" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_LIPO" = x; then
+ LIPO=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ LIPO=$ac_ct_LIPO
+ fi
+else
+ LIPO="$ac_cv_prog_LIPO"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args.
+set dummy ${ac_tool_prefix}otool; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_OTOOL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$OTOOL"; then
+ ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_OTOOL="${ac_tool_prefix}otool"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+OTOOL=$ac_cv_prog_OTOOL
+if test -n "$OTOOL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5
+$as_echo "$OTOOL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OTOOL"; then
+ ac_ct_OTOOL=$OTOOL
+ # Extract the first word of "otool", so it can be a program name with args.
+set dummy otool; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_OTOOL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_OTOOL"; then
+ ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_OTOOL="otool"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL
+if test -n "$ac_ct_OTOOL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5
+$as_echo "$ac_ct_OTOOL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_OTOOL" = x; then
+ OTOOL=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ OTOOL=$ac_ct_OTOOL
+ fi
+else
+ OTOOL="$ac_cv_prog_OTOOL"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args.
+set dummy ${ac_tool_prefix}otool64; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_OTOOL64+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$OTOOL64"; then
+ ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+OTOOL64=$ac_cv_prog_OTOOL64
+if test -n "$OTOOL64"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5
+$as_echo "$OTOOL64" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OTOOL64"; then
+ ac_ct_OTOOL64=$OTOOL64
+ # Extract the first word of "otool64", so it can be a program name with args.
+set dummy otool64; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_OTOOL64+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_OTOOL64"; then
+ ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_OTOOL64="otool64"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64
+if test -n "$ac_ct_OTOOL64"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5
+$as_echo "$ac_ct_OTOOL64" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_OTOOL64" = x; then
+ OTOOL64=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ OTOOL64=$ac_ct_OTOOL64
+ fi
+else
+ OTOOL64="$ac_cv_prog_OTOOL64"
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5
+$as_echo_n "checking for -single_module linker flag... " >&6; }
+if ${lt_cv_apple_cc_single_mod+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_apple_cc_single_mod=no
+ if test -z "${LT_MULTI_MODULE}"; then
+ # By default we will add the -single_module flag. You can override
+ # by either setting the environment variable LT_MULTI_MODULE
+ # non-empty at configure time, or by adding -multi_module to the
+ # link flags.
+ rm -rf libconftest.dylib*
+ echo "int foo(void){return 1;}" > conftest.c
+ echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+-dynamiclib -Wl,-single_module conftest.c" >&5
+ $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err
+ _lt_result=$?
+ if test -f libconftest.dylib && test ! -s conftest.err && test $_lt_result = 0; then
+ lt_cv_apple_cc_single_mod=yes
+ else
+ cat conftest.err >&5
+ fi
+ rm -rf libconftest.dylib*
+ rm -f conftest.*
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5
+$as_echo "$lt_cv_apple_cc_single_mod" >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5
+$as_echo_n "checking for -exported_symbols_list linker flag... " >&6; }
+if ${lt_cv_ld_exported_symbols_list+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_ld_exported_symbols_list=no
+ save_LDFLAGS=$LDFLAGS
+ echo "_main" > conftest.sym
+ LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ lt_cv_ld_exported_symbols_list=yes
+else
+ lt_cv_ld_exported_symbols_list=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LDFLAGS="$save_LDFLAGS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5
+$as_echo "$lt_cv_ld_exported_symbols_list" >&6; }
+ case $host_os in
+ rhapsody* | darwin1.[012])
+ _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;;
+ darwin1.*)
+ _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+ darwin*) # darwin 5.x on
+ # if running on 10.5 or later, the deployment target defaults
+ # to the OS version, if on x86, and 10.4, the deployment
+ # target defaults to 10.4. Don't you love it?
+ case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
+ 10.0,*86*-darwin8*|10.0,*-darwin[91]*)
+ _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+ 10.[012]*)
+ _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+ 10.*)
+ _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+ esac
+ ;;
+ esac
+ if test "$lt_cv_apple_cc_single_mod" = "yes"; then
+ _lt_dar_single_mod='$single_module'
+ fi
+ if test "$lt_cv_ld_exported_symbols_list" = "yes"; then
+ _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym'
+ else
+ _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}'
+ fi
+ if test "$DSYMUTIL" != ":"; then
+ _lt_dsymutil='~$DSYMUTIL $lib || :'
+ else
+ _lt_dsymutil=
+ fi
+ ;;
+ esac
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
+$as_echo_n "checking how to run the C preprocessor... " >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+ if ${ac_cv_prog_CPP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ # Double quotes because CPP needs to be expanded
+ for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+ do
+ ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+@%:@ifdef __STDC__
+@%:@ include <limits.h>
+@%:@else
+@%:@ include <assert.h>
+@%:@endif
+ Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+@%:@include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ # Broken: success on invalid input.
+continue
+else
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+ break
+fi
+
+ done
+ ac_cv_prog_CPP=$CPP
+
+fi
+ CPP=$ac_cv_prog_CPP
+else
+ ac_cv_prog_CPP=$CPP
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
+$as_echo "$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+@%:@ifdef __STDC__
+@%:@ include <limits.h>
+@%:@else
+@%:@ include <assert.h>
+@%:@endif
+ Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+@%:@include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ # Broken: success on invalid input.
+continue
+else
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+
+else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if ${ac_cv_header_stdc+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_header_stdc=yes
+else
+ ac_cv_header_stdc=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "memchr" >/dev/null 2>&1; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "free" >/dev/null 2>&1; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+ if test "$cross_compiling" = yes; then :
+ :
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+ (('a' <= (c) && (c) <= 'i') \
+ || ('j' <= (c) && (c) <= 'r') \
+ || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ if (XOR (islower (i), ISLOWER (i))
+ || toupper (i) != TOUPPER (i))
+ return 2;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+$as_echo "@%:@define STDC_HEADERS 1" >>confdefs.h
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+ inttypes.h stdint.h unistd.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
+"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in dlfcn.h
+do :
+ ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default
+"
+if test "x$ac_cv_header_dlfcn_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_DLFCN_H 1
+_ACEOF
+
+fi
+
+done
+
+
+
+# Set options
+
+
+
+ enable_dlopen=no
+
+
+ enable_win32_dll=no
+
+
+ @%:@ Check whether --enable-shared was given.
+if test "${enable_shared+set}" = set; then :
+ enableval=$enable_shared; p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_shared=yes ;;
+ no) enable_shared=no ;;
+ *)
+ enable_shared=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ for pkg in $enableval; do
+ IFS="$lt_save_ifs"
+ if test "X$pkg" = "X$p"; then
+ enable_shared=yes
+ fi
+ done
+ IFS="$lt_save_ifs"
+ ;;
+ esac
+else
+ enable_shared=yes
+fi
+
+
+
+
+
+
+
+
+
+ @%:@ Check whether --enable-static was given.
+if test "${enable_static+set}" = set; then :
+ enableval=$enable_static; p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_static=yes ;;
+ no) enable_static=no ;;
+ *)
+ enable_static=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ for pkg in $enableval; do
+ IFS="$lt_save_ifs"
+ if test "X$pkg" = "X$p"; then
+ enable_static=yes
+ fi
+ done
+ IFS="$lt_save_ifs"
+ ;;
+ esac
+else
+ enable_static=yes
+fi
+
+
+
+
+
+
+
+
+
+
+@%:@ Check whether --with-pic was given.
+if test "${with_pic+set}" = set; then :
+ withval=$with_pic; pic_mode="$withval"
+else
+ pic_mode=default
+fi
+
+
+test -z "$pic_mode" && pic_mode=default
+
+
+
+
+
+
+
+ @%:@ Check whether --enable-fast-install was given.
+if test "${enable_fast_install+set}" = set; then :
+ enableval=$enable_fast_install; p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_fast_install=yes ;;
+ no) enable_fast_install=no ;;
+ *)
+ enable_fast_install=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ for pkg in $enableval; do
+ IFS="$lt_save_ifs"
+ if test "X$pkg" = "X$p"; then
+ enable_fast_install=yes
+ fi
+ done
+ IFS="$lt_save_ifs"
+ ;;
+ esac
+else
+ enable_fast_install=yes
+fi
+
+
+
+
+
+
+
+
+
+
+
+# This can be used to rebuild libtool when needed
+LIBTOOL_DEPS="$ltmain"
+
+# Always use our own libtool.
+LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+test -z "$LN_S" && LN_S="ln -s"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+if test -n "${ZSH_VERSION+set}" ; then
+ setopt NO_GLOB_SUBST
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5
+$as_echo_n "checking for objdir... " >&6; }
+if ${lt_cv_objdir+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ rm -f .libs 2>/dev/null
+mkdir .libs 2>/dev/null
+if test -d .libs; then
+ lt_cv_objdir=.libs
+else
+ # MS-DOS does not allow filenames that begin with a dot.
+ lt_cv_objdir=_libs
+fi
+rmdir .libs 2>/dev/null
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5
+$as_echo "$lt_cv_objdir" >&6; }
+objdir=$lt_cv_objdir
+
+
+
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define LT_OBJDIR "$lt_cv_objdir/"
+_ACEOF
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+case $host_os in
+aix3*)
+ # AIX sometimes has problems with the GCC collect2 program. For some
+ # reason, if we set the COLLECT_NAMES environment variable, the problems
+ # vanish in a puff of smoke.
+ if test "X${COLLECT_NAMES+set}" != Xset; then
+ COLLECT_NAMES=
+ export COLLECT_NAMES
+ fi
+ ;;
+esac
+
+# Sed substitution that helps us do robust quoting. It backslashifies
+# metacharacters that are still active within double-quoted strings.
+sed_quote_subst='s/\(["`$\\]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\(["`\\]\)/\\\1/g'
+
+# Sed substitution to delay expansion of an escaped shell variable in a
+# double_quote_subst'ed string.
+delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
+
+# Sed substitution to delay expansion of an escaped single quote.
+delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g'
+
+# Sed substitution to avoid accidental globbing in evaled expressions
+no_glob_subst='s/\*/\\\*/g'
+
+# Global variables:
+ofile=libtool
+can_build_shared=yes
+
+# All known linkers require a `.a' archive for static linking (except MSVC,
+# which needs '.lib').
+libext=a
+
+with_gnu_ld="$lt_cv_prog_gnu_ld"
+
+old_CC="$CC"
+old_CFLAGS="$CFLAGS"
+
+# Set sane defaults for various variables
+test -z "$CC" && CC=cc
+test -z "$LTCC" && LTCC=$CC
+test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS
+test -z "$LD" && LD=ld
+test -z "$ac_objext" && ac_objext=o
+
+for cc_temp in $compiler""; do
+ case $cc_temp in
+ compile | *[\\/]compile | ccache | *[\\/]ccache ) ;;
+ distcc | *[\\/]distcc | purify | *[\\/]purify ) ;;
+ \-*) ;;
+ *) break;;
+ esac
+done
+cc_basename=`$ECHO "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"`
+
+
+# Only perform the check for file, if the check method requires it
+test -z "$MAGIC_CMD" && MAGIC_CMD=file
+case $deplibs_check_method in
+file_magic*)
+ if test "$file_magic_cmd" = '$MAGIC_CMD'; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5
+$as_echo_n "checking for ${ac_tool_prefix}file... " >&6; }
+if ${lt_cv_path_MAGIC_CMD+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $MAGIC_CMD in
+[\\/*] | ?:[\\/]*)
+ lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+ ;;
+*)
+ lt_save_MAGIC_CMD="$MAGIC_CMD"
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
+ for ac_dir in $ac_dummy; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/${ac_tool_prefix}file; then
+ lt_cv_path_MAGIC_CMD="$ac_dir/${ac_tool_prefix}file"
+ if test -n "$file_magic_test_file"; then
+ case $deplibs_check_method in
+ "file_magic "*)
+ file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+ MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+ if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+ $EGREP "$file_magic_regex" > /dev/null; then
+ :
+ else
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such. This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem. Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+ fi ;;
+ esac
+ fi
+ break
+ fi
+ done
+ IFS="$lt_save_ifs"
+ MAGIC_CMD="$lt_save_MAGIC_CMD"
+ ;;
+esac
+fi
+
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5
+$as_echo "$MAGIC_CMD" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+
+
+if test -z "$lt_cv_path_MAGIC_CMD"; then
+ if test -n "$ac_tool_prefix"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5
+$as_echo_n "checking for file... " >&6; }
+if ${lt_cv_path_MAGIC_CMD+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $MAGIC_CMD in
+[\\/*] | ?:[\\/]*)
+ lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+ ;;
+*)
+ lt_save_MAGIC_CMD="$MAGIC_CMD"
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
+ for ac_dir in $ac_dummy; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/file; then
+ lt_cv_path_MAGIC_CMD="$ac_dir/file"
+ if test -n "$file_magic_test_file"; then
+ case $deplibs_check_method in
+ "file_magic "*)
+ file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+ MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+ if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+ $EGREP "$file_magic_regex" > /dev/null; then
+ :
+ else
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such. This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem. Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+ fi ;;
+ esac
+ fi
+ break
+ fi
+ done
+ IFS="$lt_save_ifs"
+ MAGIC_CMD="$lt_save_MAGIC_CMD"
+ ;;
+esac
+fi
+
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5
+$as_echo "$MAGIC_CMD" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ else
+ MAGIC_CMD=:
+ fi
+fi
+
+ fi
+ ;;
+esac
+
+# Use C for the default configuration in the libtool script
+
+lt_save_CC="$CC"
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+# Source file extension for C test sources.
+ac_ext=c
+
+# Object file extension for compiled C test sources.
+objext=o
+objext=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="int some_variable = 0;"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='int main(){return(0);}'
+
+
+
+
+
+
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+# Save the default compiler, since it gets overwritten when the other
+# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP.
+compiler_DEFAULT=$CC
+
+# save warnings/boilerplate of simple test code
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_compile_test_code" >conftest.$ac_ext
+eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_compiler_boilerplate=`cat conftest.err`
+$RM conftest*
+
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_link_test_code" >conftest.$ac_ext
+eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_linker_boilerplate=`cat conftest.err`
+$RM -r conftest*
+
+
+## CAVEAT EMPTOR:
+## There is no encapsulation within the following macros, do not change
+## the running order or otherwise move them around unless you know exactly
+## what you are doing...
+if test -n "$compiler"; then
+
+lt_prog_compiler_no_builtin_flag=
+
+if test "$GCC" = yes; then
+ lt_prog_compiler_no_builtin_flag=' -fno-builtin'
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5
+$as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; }
+if ${lt_cv_prog_compiler_rtti_exceptions+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_rtti_exceptions=no
+ ac_outfile=conftest.$ac_objext
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+ lt_compiler_flag="-fno-rtti -fno-exceptions"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ # The option is referenced via a variable to avoid confusing sed.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>conftest.err)
+ ac_status=$?
+ cat conftest.err >&5
+ echo "$as_me:__oline__: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s "$ac_outfile"; then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings other than the usual output.
+ $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_rtti_exceptions=yes
+ fi
+ fi
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5
+$as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; }
+
+if test x"$lt_cv_prog_compiler_rtti_exceptions" = xyes; then
+ lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions"
+else
+ :
+fi
+
+fi
+
+
+
+
+
+
+ lt_prog_compiler_wl=
+lt_prog_compiler_pic=
+lt_prog_compiler_static=
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5
+$as_echo_n "checking for $compiler option to produce PIC... " >&6; }
+
+ if test "$GCC" = yes; then
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_static='-static'
+
+ case $host_os in
+ aix*)
+ # All AIX code is PIC.
+ if test "$host_cpu" = ia64; then
+ # AIX 5 now supports IA64 processor
+ lt_prog_compiler_static='-Bstatic'
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ lt_prog_compiler_pic='-fPIC'
+ ;;
+ m68k)
+ # FIXME: we need at least 68020 code to build shared libraries, but
+ # adding the `-m68020' flag to GCC prevents building anything better,
+ # like `-m68040'.
+ lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4'
+ ;;
+ esac
+ ;;
+
+ beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+ # PIC is the default for these OSes.
+ ;;
+
+ mingw* | cygwin* | pw32* | os2* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ # Although the cygwin gcc ignores -fPIC, still need this for old-style
+ # (--disable-auto-import) libraries
+ lt_prog_compiler_pic='-DDLL_EXPORT'
+ ;;
+
+ darwin* | rhapsody*)
+ # PIC is the default on this platform
+ # Common symbols not allowed in MH_DYLIB files
+ lt_prog_compiler_pic='-fno-common'
+ ;;
+
+ hpux*)
+ # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+ # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag
+ # sets the default TLS model and affects inlining.
+ case $host_cpu in
+ hppa*64*)
+ # +Z the default
+ ;;
+ *)
+ lt_prog_compiler_pic='-fPIC'
+ ;;
+ esac
+ ;;
+
+ interix[3-9]*)
+ # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+ # Instead, we relocate shared libraries at runtime.
+ ;;
+
+ msdosdjgpp*)
+ # Just because we use GCC doesn't mean we suddenly get shared libraries
+ # on systems that don't support them.
+ lt_prog_compiler_can_build_shared=no
+ enable_shared=no
+ ;;
+
+ *nto* | *qnx*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ lt_prog_compiler_pic='-fPIC -shared'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ lt_prog_compiler_pic=-Kconform_pic
+ fi
+ ;;
+
+ *)
+ lt_prog_compiler_pic='-fPIC'
+ ;;
+ esac
+ else
+ # PORTME Check for flag to pass linker flags through the system compiler.
+ case $host_os in
+ aix*)
+ lt_prog_compiler_wl='-Wl,'
+ if test "$host_cpu" = ia64; then
+ # AIX 5 now supports IA64 processor
+ lt_prog_compiler_static='-Bstatic'
+ else
+ lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp'
+ fi
+ ;;
+
+ mingw* | cygwin* | pw32* | os2* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ lt_prog_compiler_pic='-DDLL_EXPORT'
+ ;;
+
+ hpux9* | hpux10* | hpux11*)
+ lt_prog_compiler_wl='-Wl,'
+ # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+ # not for PA HP-UX.
+ case $host_cpu in
+ hppa*64*|ia64*)
+ # +Z the default
+ ;;
+ *)
+ lt_prog_compiler_pic='+Z'
+ ;;
+ esac
+ # Is there a better lt_prog_compiler_static that works with the bundled CC?
+ lt_prog_compiler_static='${wl}-a ${wl}archive'
+ ;;
+
+ irix5* | irix6* | nonstopux*)
+ lt_prog_compiler_wl='-Wl,'
+ # PIC (with -KPIC) is the default.
+ lt_prog_compiler_static='-non_shared'
+ ;;
+
+ linux* | k*bsd*-gnu)
+ case $cc_basename in
+ # old Intel for x86_64 which still supported -KPIC.
+ ecc*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-static'
+ ;;
+ # icc used to be incompatible with GCC.
+ # ICC 10 doesn't accept -KPIC any more.
+ icc* | ifort*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-fPIC'
+ lt_prog_compiler_static='-static'
+ ;;
+ # Lahey Fortran 8.1.
+ lf95*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='--shared'
+ lt_prog_compiler_static='--static'
+ ;;
+ pgcc* | pgf77* | pgf90* | pgf95*)
+ # Portland Group compilers (*not* the Pentium gcc compiler,
+ # which looks to be a dead project)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-fpic'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+ ccc*)
+ lt_prog_compiler_wl='-Wl,'
+ # All Alpha code is PIC.
+ lt_prog_compiler_static='-non_shared'
+ ;;
+ xl*)
+ # IBM XL C 8.0/Fortran 10.1 on PPC
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-qpic'
+ lt_prog_compiler_static='-qstaticlink'
+ ;;
+ *)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*)
+ # Sun C 5.9
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ lt_prog_compiler_wl='-Wl,'
+ ;;
+ *Sun\ F*)
+ # Sun Fortran 8.3 passes all unrecognized flags to the linker
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ lt_prog_compiler_wl=''
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+
+ newsos6)
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ *nto* | *qnx*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ lt_prog_compiler_pic='-fPIC -shared'
+ ;;
+
+ osf3* | osf4* | osf5*)
+ lt_prog_compiler_wl='-Wl,'
+ # All OSF/1 code is PIC.
+ lt_prog_compiler_static='-non_shared'
+ ;;
+
+ rdos*)
+ lt_prog_compiler_static='-non_shared'
+ ;;
+
+ solaris*)
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ case $cc_basename in
+ f77* | f90* | f95*)
+ lt_prog_compiler_wl='-Qoption ld ';;
+ *)
+ lt_prog_compiler_wl='-Wl,';;
+ esac
+ ;;
+
+ sunos4*)
+ lt_prog_compiler_wl='-Qoption ld '
+ lt_prog_compiler_pic='-PIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ sysv4 | sysv4.2uw2* | sysv4.3*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec ;then
+ lt_prog_compiler_pic='-Kconform_pic'
+ lt_prog_compiler_static='-Bstatic'
+ fi
+ ;;
+
+ sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ unicos*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_can_build_shared=no
+ ;;
+
+ uts4*)
+ lt_prog_compiler_pic='-pic'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ *)
+ lt_prog_compiler_can_build_shared=no
+ ;;
+ esac
+ fi
+
+case $host_os in
+ # For platforms which do not support PIC, -DPIC is meaningless:
+ *djgpp*)
+ lt_prog_compiler_pic=
+ ;;
+ *)
+ lt_prog_compiler_pic="$lt_prog_compiler_pic@&t@ -DPIC"
+ ;;
+esac
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_prog_compiler_pic" >&5
+$as_echo "$lt_prog_compiler_pic" >&6; }
+
+
+
+
+
+
+#
+# Check to make sure the PIC flag actually works.
+#
+if test -n "$lt_prog_compiler_pic"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5
+$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; }
+if ${lt_cv_prog_compiler_pic_works+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_pic_works=no
+ ac_outfile=conftest.$ac_objext
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+ lt_compiler_flag="$lt_prog_compiler_pic@&t@ -DPIC"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ # The option is referenced via a variable to avoid confusing sed.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>conftest.err)
+ ac_status=$?
+ cat conftest.err >&5
+ echo "$as_me:__oline__: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s "$ac_outfile"; then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings other than the usual output.
+ $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_pic_works=yes
+ fi
+ fi
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5
+$as_echo "$lt_cv_prog_compiler_pic_works" >&6; }
+
+if test x"$lt_cv_prog_compiler_pic_works" = xyes; then
+ case $lt_prog_compiler_pic in
+ "" | " "*) ;;
+ *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;;
+ esac
+else
+ lt_prog_compiler_pic=
+ lt_prog_compiler_can_build_shared=no
+fi
+
+fi
+
+
+
+
+
+
+#
+# Check to make sure the static flag actually works.
+#
+wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5
+$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; }
+if ${lt_cv_prog_compiler_static_works+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_static_works=no
+ save_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS $lt_tmp_static_flag"
+ echo "$lt_simple_link_test_code" > conftest.$ac_ext
+ if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+ # The linker can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ if test -s conftest.err; then
+ # Append any errors to the config.log.
+ cat conftest.err 1>&5
+ $ECHO "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if diff conftest.exp conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_static_works=yes
+ fi
+ else
+ lt_cv_prog_compiler_static_works=yes
+ fi
+ fi
+ $RM -r conftest*
+ LDFLAGS="$save_LDFLAGS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5
+$as_echo "$lt_cv_prog_compiler_static_works" >&6; }
+
+if test x"$lt_cv_prog_compiler_static_works" = xyes; then
+ :
+else
+ lt_prog_compiler_static=
+fi
+
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5
+$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
+if ${lt_cv_prog_compiler_c_o+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_c_o=no
+ $RM -r conftest 2>/dev/null
+ mkdir conftest
+ cd conftest
+ mkdir out
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ lt_compiler_flag="-o out/conftest2.$ac_objext"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>out/conftest.err)
+ ac_status=$?
+ cat out/conftest.err >&5
+ echo "$as_me:__oline__: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s out/conftest2.$ac_objext
+ then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp
+ $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+ if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_c_o=yes
+ fi
+ fi
+ chmod u+w . 2>&5
+ $RM conftest*
+ # SGI C++ compiler will create directory out/ii_files/ for
+ # template instantiation
+ test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+ $RM out/* && rmdir out
+ cd ..
+ $RM -r conftest
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5
+$as_echo "$lt_cv_prog_compiler_c_o" >&6; }
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5
+$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
+if ${lt_cv_prog_compiler_c_o+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_c_o=no
+ $RM -r conftest 2>/dev/null
+ mkdir conftest
+ cd conftest
+ mkdir out
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ lt_compiler_flag="-o out/conftest2.$ac_objext"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>out/conftest.err)
+ ac_status=$?
+ cat out/conftest.err >&5
+ echo "$as_me:__oline__: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s out/conftest2.$ac_objext
+ then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp
+ $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+ if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_c_o=yes
+ fi
+ fi
+ chmod u+w . 2>&5
+ $RM conftest*
+ # SGI C++ compiler will create directory out/ii_files/ for
+ # template instantiation
+ test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+ $RM out/* && rmdir out
+ cd ..
+ $RM -r conftest
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5
+$as_echo "$lt_cv_prog_compiler_c_o" >&6; }
+
+
+
+
+hard_links="nottested"
+if test "$lt_cv_prog_compiler_c_o" = no && test "$need_locks" != no; then
+ # do not overwrite the value of need_locks provided by the user
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5
+$as_echo_n "checking if we can lock with hard links... " >&6; }
+ hard_links=yes
+ $RM conftest*
+ ln conftest.a conftest.b 2>/dev/null && hard_links=no
+ touch conftest.a
+ ln conftest.a conftest.b 2>&5 || hard_links=no
+ ln conftest.a conftest.b 2>/dev/null && hard_links=no
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5
+$as_echo "$hard_links" >&6; }
+ if test "$hard_links" = no; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5
+$as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;}
+ need_locks=warn
+ fi
+else
+ need_locks=no
+fi
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5
+$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; }
+
+ runpath_var=
+ allow_undefined_flag=
+ always_export_symbols=no
+ archive_cmds=
+ archive_expsym_cmds=
+ compiler_needs_object=no
+ enable_shared_with_static_runtimes=no
+ export_dynamic_flag_spec=
+ export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+ hardcode_automatic=no
+ hardcode_direct=no
+ hardcode_direct_absolute=no
+ hardcode_libdir_flag_spec=
+ hardcode_libdir_flag_spec_ld=
+ hardcode_libdir_separator=
+ hardcode_minus_L=no
+ hardcode_shlibpath_var=unsupported
+ inherit_rpath=no
+ link_all_deplibs=unknown
+ module_cmds=
+ module_expsym_cmds=
+ old_archive_from_new_cmds=
+ old_archive_from_expsyms_cmds=
+ thread_safe_flag_spec=
+ whole_archive_flag_spec=
+ # include_expsyms should be a list of space-separated symbols to be *always*
+ # included in the symbol list
+ include_expsyms=
+ # exclude_expsyms can be an extended regexp of symbols to exclude
+ # it will be wrapped by ` (' and `)$', so one must not match beginning or
+ # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc',
+ # as well as any symbol that contains `d'.
+ exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'
+ # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
+ # platforms (ab)use it in PIC code, but their linkers get confused if
+ # the symbol is explicitly referenced. Since portable code cannot
+ # rely on this symbol name, it's probably fine to never include it in
+ # preloaded symbol tables.
+ # Exclude shared library initialization/finalization symbols.
+ extract_expsyms_cmds=
+
+ case $host_os in
+ cygwin* | mingw* | pw32* | cegcc*)
+ # FIXME: the MSVC++ port hasn't been tested in a loooong time
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ if test "$GCC" != yes; then
+ with_gnu_ld=no
+ fi
+ ;;
+ interix*)
+ # we just hope/assume this is gcc and not c89 (= MSVC++)
+ with_gnu_ld=yes
+ ;;
+ openbsd*)
+ with_gnu_ld=no
+ ;;
+ esac
+
+ ld_shlibs=yes
+ if test "$with_gnu_ld" = yes; then
+ # If archive_cmds runs LD, not CC, wlarc should be empty
+ wlarc='${wl}'
+
+ # Set some defaults for GNU ld with shared library support. These
+ # are reset later if shared libraries are not supported. Putting them
+ # here allows them to be overridden if necessary.
+ runpath_var=LD_RUN_PATH
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ export_dynamic_flag_spec='${wl}--export-dynamic'
+ # ancient GNU ld didn't support --whole-archive et. al.
+ if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then
+ whole_archive_flag_spec="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+ else
+ whole_archive_flag_spec=
+ fi
+ supports_anon_versioning=no
+ case `$LD -v 2>&1` in
+ *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11
+ *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
+ *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
+ *\ 2.11.*) ;; # other 2.11 versions
+ *) supports_anon_versioning=yes ;;
+ esac
+
+ # See if GNU ld supports shared libraries.
+ case $host_os in
+ aix[3-9]*)
+ # On AIX/PPC, the GNU linker is very broken
+ if test "$host_cpu" != ia64; then
+ ld_shlibs=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the GNU linker, at least up to release 2.9.1, is reported
+*** to be unable to reliably create shared libraries on AIX.
+*** Therefore, libtool is disabling shared libraries support. If you
+*** really care for shared libraries, you may want to modify your PATH
+*** so that a non-GNU linker is found, and then restart.
+
+_LT_EOF
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds=''
+ ;;
+ m68k)
+ archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ ;;
+ esac
+ ;;
+
+ beos*)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ allow_undefined_flag=unsupported
+ # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+ # support --undefined. This deserves some investigation. FIXME
+ archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ cygwin* | mingw* | pw32* | cegcc*)
+ # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless,
+ # as there is no search path for DLLs.
+ hardcode_libdir_flag_spec='-L$libdir'
+ allow_undefined_flag=unsupported
+ always_export_symbols=no
+ enable_shared_with_static_runtimes=yes
+ export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols'
+
+ if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ # If the export-symbols file already is a .def file (1st line
+ # is EXPORTS), use it as is; otherwise, prepend...
+ archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+ cp $export_symbols $output_objdir/$soname.def;
+ else
+ echo EXPORTS > $output_objdir/$soname.def;
+ cat $export_symbols >> $output_objdir/$soname.def;
+ fi~
+ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ interix[3-9]*)
+ hardcode_direct=no
+ hardcode_shlibpath_var=no
+ hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+ export_dynamic_flag_spec='${wl}-E'
+ # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+ # Instead, shared libraries are loaded at an image base (0x10000000 by
+ # default) and relocated if they conflict, which is a slow very memory
+ # consuming and fragmenting process. To avoid this, we pick a random,
+ # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+ # time. Moving up from 0x10000000 also allows more sbrk(2) space.
+ archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ archive_expsym_cmds='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ ;;
+
+ gnu* | linux* | tpf* | k*bsd*-gnu)
+ tmp_diet=no
+ if test "$host_os" = linux-dietlibc; then
+ case $cc_basename in
+ diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn)
+ esac
+ fi
+ if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \
+ && test "$tmp_diet" = no
+ then
+ tmp_addflag=
+ tmp_sharedflag='-shared'
+ case $cc_basename,$host_cpu in
+ pgcc*) # Portland Group C compiler
+ whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+ tmp_addflag=' $pic_flag'
+ ;;
+ pgf77* | pgf90* | pgf95*) # Portland Group f77 and f90 compilers
+ whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+ tmp_addflag=' $pic_flag -Mnomain' ;;
+ ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64
+ tmp_addflag=' -i_dynamic' ;;
+ efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64
+ tmp_addflag=' -i_dynamic -nofor_main' ;;
+ ifc* | ifort*) # Intel Fortran compiler
+ tmp_addflag=' -nofor_main' ;;
+ lf95*) # Lahey Fortran 8.1
+ whole_archive_flag_spec=
+ tmp_sharedflag='--shared' ;;
+ xl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below)
+ tmp_sharedflag='-qmkshrobj'
+ tmp_addflag= ;;
+ esac
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*) # Sun C 5.9
+ whole_archive_flag_spec='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+ compiler_needs_object=yes
+ tmp_sharedflag='-G' ;;
+ *Sun\ F*) # Sun Fortran 8.3
+ tmp_sharedflag='-G' ;;
+ esac
+ archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+
+ if test "x$supports_anon_versioning" = xyes; then
+ archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+ fi
+
+ case $cc_basename in
+ xlf*)
+ # IBM XL Fortran 10.1 on PPC cannot create shared libs itself
+ whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive'
+ hardcode_libdir_flag_spec=
+ hardcode_libdir_flag_spec_ld='-rpath $libdir'
+ archive_cmds='$LD -shared $libobjs $deplibs $compiler_flags -soname $soname -o $lib'
+ if test "x$supports_anon_versioning" = xyes; then
+ archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $LD -shared $libobjs $deplibs $compiler_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib'
+ fi
+ ;;
+ esac
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
+ wlarc=
+ else
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ fi
+ ;;
+
+ solaris*)
+ if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then
+ ld_shlibs=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: The releases 2.8.* of the GNU linker cannot reliably
+*** create shared libraries on Solaris systems. Therefore, libtool
+*** is disabling shared libraries support. We urge you to upgrade GNU
+*** binutils to release 2.9.1 or newer. Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+ elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*)
+ case `$LD -v 2>&1` in
+ *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*)
+ ld_shlibs=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not
+*** reliably create shared libraries on SCO systems. Therefore, libtool
+*** is disabling shared libraries support. We urge you to upgrade GNU
+*** binutils to release 2.16.91.0.3 or newer. Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+ ;;
+ *)
+ # For security reasons, it is highly recommended that you always
+ # use absolute paths for naming shared libraries, and exclude the
+ # DT_RUNPATH tag from executables and libraries. But doing so
+ # requires that you compile everything twice, which is a pain.
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+ esac
+ ;;
+
+ sunos4*)
+ archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ wlarc=
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ *)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+ esac
+
+ if test "$ld_shlibs" = no; then
+ runpath_var=
+ hardcode_libdir_flag_spec=
+ export_dynamic_flag_spec=
+ whole_archive_flag_spec=
+ fi
+ else
+ # PORTME fill in a description of your system's linker (not GNU ld)
+ case $host_os in
+ aix3*)
+ allow_undefined_flag=unsupported
+ always_export_symbols=yes
+ archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'
+ # Note: this linker hardcodes the directories in LIBPATH if there
+ # are no directories specified by -L.
+ hardcode_minus_L=yes
+ if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then
+ # Neither direct hardcoding nor static linking is supported with a
+ # broken collect2.
+ hardcode_direct=unsupported
+ fi
+ ;;
+
+ aix[4-9]*)
+ if test "$host_cpu" = ia64; then
+ # On IA64, the linker does run time linking by default, so we don't
+ # have to do anything special.
+ aix_use_runtimelinking=no
+ exp_sym_flag='-Bexport'
+ no_entry_flag=""
+ else
+ # If we're using GNU nm, then we don't want the "-C" option.
+ # -C means demangle to AIX nm, but means don't demangle with GNU nm
+ if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+ export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+ else
+ export_symbols_cmds='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+ fi
+ aix_use_runtimelinking=no
+
+ # Test if we are trying to use run time linking or normal
+ # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+ # need to do runtime linking.
+ case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*)
+ for ld_flag in $LDFLAGS; do
+ if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then
+ aix_use_runtimelinking=yes
+ break
+ fi
+ done
+ ;;
+ esac
+
+ exp_sym_flag='-bexport'
+ no_entry_flag='-bnoentry'
+ fi
+
+ # When large executables or shared objects are built, AIX ld can
+ # have problems creating the table of contents. If linking a library
+ # or program results in "error TOC overflow" add -mminimal-toc to
+ # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not
+ # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+ archive_cmds=''
+ hardcode_direct=yes
+ hardcode_direct_absolute=yes
+ hardcode_libdir_separator=':'
+ link_all_deplibs=yes
+ file_list_spec='${wl}-f,'
+
+ if test "$GCC" = yes; then
+ case $host_os in aix4.[012]|aix4.[012].*)
+ # We only want to do this on AIX 4.2 and lower, the check
+ # below for broken collect2 doesn't work under 4.3+
+ collect2name=`${CC} -print-prog-name=collect2`
+ if test -f "$collect2name" &&
+ strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+ then
+ # We have reworked collect2
+ :
+ else
+ # We have old collect2
+ hardcode_direct=unsupported
+ # It fails to find uninstalled libraries when the uninstalled
+ # path is not listed in the libpath. Setting hardcode_minus_L
+ # to unsupported forces relinking
+ hardcode_minus_L=yes
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_libdir_separator=
+ fi
+ ;;
+ esac
+ shared_flag='-shared'
+ if test "$aix_use_runtimelinking" = yes; then
+ shared_flag="$shared_flag "'${wl}-G'
+ fi
+ else
+ # not using gcc
+ if test "$host_cpu" = ia64; then
+ # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+ # chokes on -Wl,-G. The following line is correct:
+ shared_flag='-G'
+ else
+ if test "$aix_use_runtimelinking" = yes; then
+ shared_flag='${wl}-G'
+ else
+ shared_flag='${wl}-bM:SRE'
+ fi
+ fi
+ fi
+
+ export_dynamic_flag_spec='${wl}-bexpall'
+ # It seems that -bexpall does not export symbols beginning with
+ # underscore (_), so it is better to generate a list of symbols to export.
+ always_export_symbols=yes
+ if test "$aix_use_runtimelinking" = yes; then
+ # Warning - without using the other runtime loading flags (-brtl),
+ # -berok will link without error, but may produce a broken library.
+ allow_undefined_flag='-berok'
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+lt_aix_libpath_sed='
+ /Import File Strings/,/^$/ {
+ /^0/ {
+ s/^0 *\(.*\)$/\1/
+ p
+ }
+ }'
+aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+# Check for a 64-bit object if we didn't find anything.
+if test -z "$aix_libpath"; then
+ aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+fi
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+
+ hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath"
+ archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then $ECHO "X${wl}${allow_undefined_flag}" | $Xsed; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+ else
+ if test "$host_cpu" = ia64; then
+ hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib'
+ allow_undefined_flag="-z nodefs"
+ archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols"
+ else
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+lt_aix_libpath_sed='
+ /Import File Strings/,/^$/ {
+ /^0/ {
+ s/^0 *\(.*\)$/\1/
+ p
+ }
+ }'
+aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+# Check for a 64-bit object if we didn't find anything.
+if test -z "$aix_libpath"; then
+ aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+fi
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+
+ hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath"
+ # Warning - without using the other run time loading flags,
+ # -berok will link without error, but may produce a broken library.
+ no_undefined_flag=' ${wl}-bernotok'
+ allow_undefined_flag=' ${wl}-berok'
+ # Exported symbols can be pulled into shared objects from archives
+ whole_archive_flag_spec='$convenience'
+ archive_cmds_need_lc=yes
+ # This is similar to how AIX traditionally builds its shared libraries.
+ archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+ fi
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds=''
+ ;;
+ m68k)
+ archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ ;;
+ esac
+ ;;
+
+ bsdi[45]*)
+ export_dynamic_flag_spec=-rdynamic
+ ;;
+
+ cygwin* | mingw* | pw32* | cegcc*)
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ # hardcode_libdir_flag_spec is actually meaningless, as there is
+ # no search path for DLLs.
+ hardcode_libdir_flag_spec=' '
+ allow_undefined_flag=unsupported
+ # Tell ltmain to make .lib files, not .a files.
+ libext=lib
+ # Tell ltmain to make .dll files, not .so files.
+ shrext_cmds=".dll"
+ # FIXME: Setting linknames here is a bad hack.
+ archive_cmds='$CC -o $lib $libobjs $compiler_flags `$ECHO "X$deplibs" | $Xsed -e '\''s/ -lc$//'\''` -link -dll~linknames='
+ # The linker will automatically build a .lib file if we build a DLL.
+ old_archive_from_new_cmds='true'
+ # FIXME: Should let the user specify the lib program.
+ old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs'
+ fix_srcfile_path='`cygpath -w "$srcfile"`'
+ enable_shared_with_static_runtimes=yes
+ ;;
+
+ darwin* | rhapsody*)
+
+
+ archive_cmds_need_lc=no
+ hardcode_direct=no
+ hardcode_automatic=yes
+ hardcode_shlibpath_var=unsupported
+ whole_archive_flag_spec=''
+ link_all_deplibs=yes
+ allow_undefined_flag="$_lt_dar_allow_undefined"
+ case $cc_basename in
+ ifort*) _lt_dar_can_shared=yes ;;
+ *) _lt_dar_can_shared=$GCC ;;
+ esac
+ if test "$_lt_dar_can_shared" = "yes"; then
+ output_verbose_link_cmd=echo
+ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}"
+ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}"
+ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}"
+ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}"
+
+ else
+ ld_shlibs=no
+ fi
+
+ ;;
+
+ dgux*)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_shlibpath_var=no
+ ;;
+
+ freebsd1*)
+ ld_shlibs=no
+ ;;
+
+ # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
+ # support. Future versions do this automatically, but an explicit c++rt0.o
+ # does not break anything, and helps significantly (at the cost of a little
+ # extra space).
+ freebsd2.2*)
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ # Unfortunately, older versions of FreeBSD 2 do not have this feature.
+ freebsd2*)
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=yes
+ hardcode_minus_L=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
+ freebsd* | dragonfly*)
+ archive_cmds='$CC -shared -o $lib $libobjs $deplibs $compiler_flags'
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ hpux9*)
+ if test "$GCC" = yes; then
+ archive_cmds='$RM $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+ else
+ archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+ fi
+ hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+ hardcode_libdir_separator=:
+ hardcode_direct=yes
+
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ hardcode_minus_L=yes
+ export_dynamic_flag_spec='${wl}-E'
+ ;;
+
+ hpux10*)
+ if test "$GCC" = yes -a "$with_gnu_ld" = no; then
+ archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+ fi
+ if test "$with_gnu_ld" = no; then
+ hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+ hardcode_libdir_flag_spec_ld='+b $libdir'
+ hardcode_libdir_separator=:
+ hardcode_direct=yes
+ hardcode_direct_absolute=yes
+ export_dynamic_flag_spec='${wl}-E'
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ hardcode_minus_L=yes
+ fi
+ ;;
+
+ hpux11*)
+ if test "$GCC" = yes -a "$with_gnu_ld" = no; then
+ case $host_cpu in
+ hppa*64*)
+ archive_cmds='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ ia64*)
+ archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ else
+ case $host_cpu in
+ hppa*64*)
+ archive_cmds='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ ia64*)
+ archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ fi
+ if test "$with_gnu_ld" = no; then
+ hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+ hardcode_libdir_separator=:
+
+ case $host_cpu in
+ hppa*64*|ia64*)
+ hardcode_direct=no
+ hardcode_shlibpath_var=no
+ ;;
+ *)
+ hardcode_direct=yes
+ hardcode_direct_absolute=yes
+ export_dynamic_flag_spec='${wl}-E'
+
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ hardcode_minus_L=yes
+ ;;
+ esac
+ fi
+ ;;
+
+ irix5* | irix6* | nonstopux*)
+ if test "$GCC" = yes; then
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ # Try to use the -exported_symbol ld option, if it does not
+ # work, assume that -exports_file does not work either and
+ # implicitly export all symbols.
+ save_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+int foo (void) { return 0; }
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib'
+
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LDFLAGS="$save_LDFLAGS"
+ else
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib'
+ fi
+ archive_cmds_need_lc='no'
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ hardcode_libdir_separator=:
+ inherit_rpath=yes
+ link_all_deplibs=yes
+ ;;
+
+ netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out
+ else
+ archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF
+ fi
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ newsos6)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=yes
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ hardcode_libdir_separator=:
+ hardcode_shlibpath_var=no
+ ;;
+
+ *nto* | *qnx*)
+ ;;
+
+ openbsd*)
+ if test -f /usr/libexec/ld.so; then
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ hardcode_direct_absolute=yes
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+ archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols'
+ hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+ export_dynamic_flag_spec='${wl}-E'
+ else
+ case $host_os in
+ openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*)
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_libdir_flag_spec='-R$libdir'
+ ;;
+ *)
+ archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+ ;;
+ esac
+ fi
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ os2*)
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ allow_undefined_flag=unsupported
+ archive_cmds='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$ECHO DATA >> $output_objdir/$libname.def~$ECHO " SINGLE NONSHARED" >> $output_objdir/$libname.def~$ECHO EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def'
+ old_archive_from_new_cmds='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def'
+ ;;
+
+ osf3*)
+ if test "$GCC" = yes; then
+ allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*'
+ archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ else
+ allow_undefined_flag=' -expect_unresolved \*'
+ archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+ fi
+ archive_cmds_need_lc='no'
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ hardcode_libdir_separator=:
+ ;;
+
+ osf4* | osf5*) # as osf3* with the addition of -msym flag
+ if test "$GCC" = yes; then
+ allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*'
+ archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ else
+ allow_undefined_flag=' -expect_unresolved \*'
+ archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+ archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~
+ $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp'
+
+ # Both c and cxx compiler support -rpath directly
+ hardcode_libdir_flag_spec='-rpath $libdir'
+ fi
+ archive_cmds_need_lc='no'
+ hardcode_libdir_separator=:
+ ;;
+
+ solaris*)
+ no_undefined_flag=' -z defs'
+ if test "$GCC" = yes; then
+ wlarc='${wl}'
+ archive_cmds='$CC -shared ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -shared ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ else
+ case `$CC -V 2>&1` in
+ *"Compilers 5.0"*)
+ wlarc=''
+ archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp'
+ ;;
+ *)
+ wlarc='${wl}'
+ archive_cmds='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ ;;
+ esac
+ fi
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_shlibpath_var=no
+ case $host_os in
+ solaris2.[0-5] | solaris2.[0-5].*) ;;
+ *)
+ # The compiler driver will combine and reorder linker options,
+ # but understands `-z linker_flag'. GCC discards it without `$wl',
+ # but is careful enough not to reorder.
+ # Supported since Solaris 2.6 (maybe 2.5.1?)
+ if test "$GCC" = yes; then
+ whole_archive_flag_spec='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
+ else
+ whole_archive_flag_spec='-z allextract$convenience -z defaultextract'
+ fi
+ ;;
+ esac
+ link_all_deplibs=yes
+ ;;
+
+ sunos4*)
+ if test "x$host_vendor" = xsequent; then
+ # Use $CC to link under sequent, because it throws in some extra .o
+ # files that make .init and .fini sections work.
+ archive_cmds='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
+ fi
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_direct=yes
+ hardcode_minus_L=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ sysv4)
+ case $host_vendor in
+ sni)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=yes # is this really true???
+ ;;
+ siemens)
+ ## LD is ld it makes a PLAMLIB
+ ## CC just makes a GrossModule.
+ archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+ reload_cmds='$CC -r -o $output$reload_objs'
+ hardcode_direct=no
+ ;;
+ motorola)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=no #Motorola manual says yes, but my tests say they lie
+ ;;
+ esac
+ runpath_var='LD_RUN_PATH'
+ hardcode_shlibpath_var=no
+ ;;
+
+ sysv4.3*)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_shlibpath_var=no
+ export_dynamic_flag_spec='-Bexport'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_shlibpath_var=no
+ runpath_var=LD_RUN_PATH
+ hardcode_runpath_var=yes
+ ld_shlibs=yes
+ fi
+ ;;
+
+ sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*)
+ no_undefined_flag='${wl}-z,text'
+ archive_cmds_need_lc=no
+ hardcode_shlibpath_var=no
+ runpath_var='LD_RUN_PATH'
+
+ if test "$GCC" = yes; then
+ archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ fi
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6*)
+ # Note: We can NOT use -z defs as we might desire, because we do not
+ # link with -lc, and that would cause any symbols used from libc to
+ # always be unresolved, which means just about no library would
+ # ever link correctly. If we're not using GNU ld we use -z text
+ # though, which does catch some bad symbols but isn't as heavy-handed
+ # as -z defs.
+ no_undefined_flag='${wl}-z,text'
+ allow_undefined_flag='${wl}-z,nodefs'
+ archive_cmds_need_lc=no
+ hardcode_shlibpath_var=no
+ hardcode_libdir_flag_spec='${wl}-R,$libdir'
+ hardcode_libdir_separator=':'
+ link_all_deplibs=yes
+ export_dynamic_flag_spec='${wl}-Bexport'
+ runpath_var='LD_RUN_PATH'
+
+ if test "$GCC" = yes; then
+ archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ fi
+ ;;
+
+ uts4*)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_shlibpath_var=no
+ ;;
+
+ *)
+ ld_shlibs=no
+ ;;
+ esac
+
+ if test x$host_vendor = xsni; then
+ case $host in
+ sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+ export_dynamic_flag_spec='${wl}-Blargedynsym'
+ ;;
+ esac
+ fi
+ fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5
+$as_echo "$ld_shlibs" >&6; }
+test "$ld_shlibs" = no && can_build_shared=no
+
+with_gnu_ld=$with_gnu_ld
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#
+# Do we need to explicitly link libc?
+#
+case "x$archive_cmds_need_lc" in
+x|xyes)
+ # Assume -lc should be added
+ archive_cmds_need_lc=yes
+
+ if test "$enable_shared" = yes && test "$GCC" = yes; then
+ case $archive_cmds in
+ *'~'*)
+ # FIXME: we may have to deal with multi-command sequences.
+ ;;
+ '$CC '*)
+ # Test whether the compiler implicitly links with -lc since on some
+ # systems, -lgcc has to come before -lc. If gcc already passes -lc
+ # to ld, don't add -lc before -lgcc.
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5
+$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; }
+ $RM conftest*
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } 2>conftest.err; then
+ soname=conftest
+ lib=conftest
+ libobjs=conftest.$ac_objext
+ deplibs=
+ wl=$lt_prog_compiler_wl
+ pic_flag=$lt_prog_compiler_pic
+ compiler_flags=-v
+ linker_flags=-v
+ verstring=
+ output_objdir=.
+ libname=conftest
+ lt_save_allow_undefined_flag=$allow_undefined_flag
+ allow_undefined_flag=
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5
+ (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+ then
+ archive_cmds_need_lc=no
+ else
+ archive_cmds_need_lc=yes
+ fi
+ allow_undefined_flag=$lt_save_allow_undefined_flag
+ else
+ cat conftest.err 1>&5
+ fi
+ $RM conftest*
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $archive_cmds_need_lc" >&5
+$as_echo "$archive_cmds_need_lc" >&6; }
+ ;;
+ esac
+ fi
+ ;;
+esac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5
+$as_echo_n "checking dynamic linker characteristics... " >&6; }
+
+if test "$GCC" = yes; then
+ case $host_os in
+ darwin*) lt_awk_arg="/^libraries:/,/LR/" ;;
+ *) lt_awk_arg="/^libraries:/" ;;
+ esac
+ lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e "s,=/,/,g"`
+ if $ECHO "$lt_search_path_spec" | $GREP ';' >/dev/null ; then
+ # if the path contains ";" then we assume it to be the separator
+ # otherwise default to the standard path separator (i.e. ":") - it is
+ # assumed that no part of a normal pathname contains ";" but that should
+ # okay in the real world where ";" in dirpaths is itself problematic.
+ lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e 's/;/ /g'`
+ else
+ lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+ fi
+ # Ok, now we have the path, separated by spaces, we can step through it
+ # and add multilib dir if necessary.
+ lt_tmp_lt_search_path_spec=
+ lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null`
+ for lt_sys_path in $lt_search_path_spec; do
+ if test -d "$lt_sys_path/$lt_multi_os_dir"; then
+ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir"
+ else
+ test -d "$lt_sys_path" && \
+ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path"
+ fi
+ done
+ lt_search_path_spec=`$ECHO $lt_tmp_lt_search_path_spec | awk '
+BEGIN {RS=" "; FS="/|\n";} {
+ lt_foo="";
+ lt_count=0;
+ for (lt_i = NF; lt_i > 0; lt_i--) {
+ if ($lt_i != "" && $lt_i != ".") {
+ if ($lt_i == "..") {
+ lt_count++;
+ } else {
+ if (lt_count == 0) {
+ lt_foo="/" $lt_i lt_foo;
+ } else {
+ lt_count--;
+ }
+ }
+ }
+ }
+ if (lt_foo != "") { lt_freq[lt_foo]++; }
+ if (lt_freq[lt_foo] == 1) { print lt_foo; }
+}'`
+ sys_lib_search_path_spec=`$ECHO $lt_search_path_spec`
+else
+ sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+fi
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+shrext_cmds=".so"
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+need_lib_prefix=unknown
+hardcode_into_libs=no
+
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+need_version=unknown
+
+case $host_os in
+aix3*)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a'
+ shlibpath_var=LIBPATH
+
+ # AIX 3 has no versioning support, so we append a major version to the name.
+ soname_spec='${libname}${release}${shared_ext}$major'
+ ;;
+
+aix[4-9]*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ hardcode_into_libs=yes
+ if test "$host_cpu" = ia64; then
+ # AIX 5 supports IA64
+ library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ else
+ # With GCC up to 2.95.x, collect2 would create an import file
+ # for dependence libraries. The import file would start with
+ # the line `#! .'. This would cause the generated library to
+ # depend on `.', always an invalid library. This was fixed in
+ # development snapshots of GCC prior to 3.0.
+ case $host_os in
+ aix4 | aix4.[01] | aix4.[01].*)
+ if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
+ echo ' yes '
+ echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then
+ :
+ else
+ can_build_shared=no
+ fi
+ ;;
+ esac
+ # AIX (on Power*) has no versioning support, so currently we can not hardcode correct
+ # soname into executable. Probably we can add versioning support to
+ # collect2, so additional links can be useful in future.
+ if test "$aix_use_runtimelinking" = yes; then
+ # If using run time linking (on AIX 4.2 or later) use lib<name>.so
+ # instead of lib<name>.a to let people know that these are not
+ # typical AIX shared libraries.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ else
+ # We preserve .a as extension for shared libraries through AIX4.2
+ # and later when we are not doing run time linking.
+ library_names_spec='${libname}${release}.a $libname.a'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ fi
+ shlibpath_var=LIBPATH
+ fi
+ ;;
+
+amigaos*)
+ case $host_cpu in
+ powerpc)
+ # Since July 2007 AmigaOS4 officially supports .so libraries.
+ # When compiling the executable, add -use-dynld -Lsobjs: to the compileline.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ ;;
+ m68k)
+ library_names_spec='$libname.ixlibrary $libname.a'
+ # Create ${libname}_ixlibrary.a entries in /sys/libs.
+ finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$ECHO "X$lib" | $Xsed -e '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+ ;;
+ esac
+ ;;
+
+beos*)
+ library_names_spec='${libname}${shared_ext}'
+ dynamic_linker="$host_os ld.so"
+ shlibpath_var=LIBRARY_PATH
+ ;;
+
+bsdi[45]*)
+ version_type=linux
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+ sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+ # the default ld.so.conf also contains /usr/contrib/lib and
+ # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+ # libtool to hard-code these into programs
+ ;;
+
+cygwin* | mingw* | pw32* | cegcc*)
+ version_type=windows
+ shrext_cmds=".dll"
+ need_version=no
+ need_lib_prefix=no
+
+ case $GCC,$host_os in
+ yes,cygwin* | yes,mingw* | yes,pw32* | yes,cegcc*)
+ library_names_spec='$libname.dll.a'
+ # DLL is installed to $(libdir)/../bin by postinstall_cmds
+ postinstall_cmds='base_file=`basename \${file}`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~
+ dldir=$destdir/`dirname \$dlpath`~
+ test -d \$dldir || mkdir -p \$dldir~
+ $install_prog $dir/$dlname \$dldir/$dlname~
+ chmod a+x \$dldir/$dlname~
+ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+ eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+ fi'
+ postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+ dlpath=$dir/\$dldll~
+ $RM \$dlpath'
+ shlibpath_overrides_runpath=yes
+
+ case $host_os in
+ cygwin*)
+ # Cygwin DLLs use 'cyg' prefix rather than 'lib'
+ soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+ sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib"
+ ;;
+ mingw* | cegcc*)
+ # MinGW DLLs use traditional 'lib' prefix
+ soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+ sys_lib_search_path_spec=`$CC -print-search-dirs | $GREP "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"`
+ if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then
+ # It is most probably a Windows format PATH printed by
+ # mingw gcc, but we are running on Cygwin. Gcc prints its search
+ # path with ; separators, and with drive letters. We can handle the
+ # drive letters (cygwin fileutils understands them), so leave them,
+ # especially as we might pass files found there to a mingw objdump,
+ # which wouldn't understand a cygwinified path. Ahh.
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+ else
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+ fi
+ ;;
+ pw32*)
+ # pw32 DLLs use 'pw' prefix rather than 'lib'
+ library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+ ;;
+ esac
+ ;;
+
+ *)
+ library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib'
+ ;;
+ esac
+ dynamic_linker='Win32 ld.exe'
+ # FIXME: first we should search . and the directory the executable is in
+ shlibpath_var=PATH
+ ;;
+
+darwin* | rhapsody*)
+ dynamic_linker="$host_os dyld"
+ version_type=darwin
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext'
+ soname_spec='${libname}${release}${major}$shared_ext'
+ shlibpath_overrides_runpath=yes
+ shlibpath_var=DYLD_LIBRARY_PATH
+ shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'
+
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"
+ sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
+ ;;
+
+dgux*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+freebsd1*)
+ dynamic_linker=no
+ ;;
+
+freebsd* | dragonfly*)
+ # DragonFly does not have aout. When/if they implement a new
+ # versioning mechanism, adjust this.
+ if test -x /usr/bin/objformat; then
+ objformat=`/usr/bin/objformat`
+ else
+ case $host_os in
+ freebsd[123]*) objformat=aout ;;
+ *) objformat=elf ;;
+ esac
+ fi
+ version_type=freebsd-$objformat
+ case $version_type in
+ freebsd-elf*)
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+ need_version=no
+ need_lib_prefix=no
+ ;;
+ freebsd-*)
+ library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix'
+ need_version=yes
+ ;;
+ esac
+ shlibpath_var=LD_LIBRARY_PATH
+ case $host_os in
+ freebsd2*)
+ shlibpath_overrides_runpath=yes
+ ;;
+ freebsd3.[01]* | freebsdelf3.[01]*)
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+ freebsd3.[2-9]* | freebsdelf3.[2-9]* | \
+ freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1)
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+ *) # from 4.6 on, and DragonFly
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+ esac
+ ;;
+
+gnu*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ hardcode_into_libs=yes
+ ;;
+
+hpux9* | hpux10* | hpux11*)
+ # Give a soname corresponding to the major version so that dld.sl refuses to
+ # link against other versions.
+ version_type=sunos
+ need_lib_prefix=no
+ need_version=no
+ case $host_cpu in
+ ia64*)
+ shrext_cmds='.so'
+ hardcode_into_libs=yes
+ dynamic_linker="$host_os dld.so"
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ if test "X$HPUX_IA64_MODE" = X32; then
+ sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
+ else
+ sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
+ fi
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ ;;
+ hppa*64*)
+ shrext_cmds='.sl'
+ hardcode_into_libs=yes
+ dynamic_linker="$host_os dld.sl"
+ shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
+ shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ ;;
+ *)
+ shrext_cmds='.sl'
+ dynamic_linker="$host_os dld.sl"
+ shlibpath_var=SHLIB_PATH
+ shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ ;;
+ esac
+ # HP-UX runs *really* slowly unless shared libraries are mode 555.
+ postinstall_cmds='chmod 555 $lib'
+ ;;
+
+interix[3-9]*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+
+irix5* | irix6* | nonstopux*)
+ case $host_os in
+ nonstopux*) version_type=nonstopux ;;
+ *)
+ if test "$lt_cv_prog_gnu_ld" = yes; then
+ version_type=linux
+ else
+ version_type=irix
+ fi ;;
+ esac
+ need_lib_prefix=no
+ need_version=no
+ soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}'
+ case $host_os in
+ irix5* | nonstopux*)
+ libsuff= shlibsuff=
+ ;;
+ *)
+ case $LD in # libtool.m4 will add one of these switches to LD
+ *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
+ libsuff= shlibsuff= libmagic=32-bit;;
+ *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
+ libsuff=32 shlibsuff=N32 libmagic=N32;;
+ *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
+ libsuff=64 shlibsuff=64 libmagic=64-bit;;
+ *) libsuff= shlibsuff= libmagic=never-match;;
+ esac
+ ;;
+ esac
+ shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+ shlibpath_overrides_runpath=no
+ sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}"
+ sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}"
+ hardcode_into_libs=yes
+ ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux*oldld* | linux*aout* | linux*coff*)
+ dynamic_linker=no
+ ;;
+
+# This must be Linux ELF.
+linux* | k*bsd*-gnu)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ # Some binutils ld are patched to set DT_RUNPATH
+ save_LDFLAGS=$LDFLAGS
+ save_libdir=$libdir
+ eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \
+ LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\""
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then :
+ shlibpath_overrides_runpath=yes
+fi
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LDFLAGS=$save_LDFLAGS
+ libdir=$save_libdir
+
+ # This implies no fast_install, which is unacceptable.
+ # Some rework will be needed to allow for fast_install
+ # before this can be enabled.
+ hardcode_into_libs=yes
+
+ # Append ld.so.conf contents to the search path
+ if test -f /etc/ld.so.conf; then
+ lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '`
+ sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
+ fi
+
+ # We used to test for /lib/ld.so.1 and disable shared libraries on
+ # powerpc, because MkLinux only supported shared libraries with the
+ # GNU dynamic linker. Since this was broken with cross compilers,
+ # most powerpc-linux boxes support dynamic linking these days and
+ # people can always --disable-shared, the test was removed, and we
+ # assume the GNU/Linux dynamic linker is in use.
+ dynamic_linker='GNU/Linux ld.so'
+ ;;
+
+netbsd*)
+ version_type=sunos
+ need_lib_prefix=no
+ need_version=no
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ dynamic_linker='NetBSD (a.out) ld.so'
+ else
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ dynamic_linker='NetBSD ld.elf_so'
+ fi
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+
+newsos6)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ ;;
+
+*nto* | *qnx*)
+ version_type=qnx
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ dynamic_linker='ldqnx.so'
+ ;;
+
+openbsd*)
+ version_type=sunos
+ sys_lib_dlsearch_path_spec="/usr/lib"
+ need_lib_prefix=no
+ # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs.
+ case $host_os in
+ openbsd3.3 | openbsd3.3.*) need_version=yes ;;
+ *) need_version=no ;;
+ esac
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+ case $host_os in
+ openbsd2.[89] | openbsd2.[89].*)
+ shlibpath_overrides_runpath=no
+ ;;
+ *)
+ shlibpath_overrides_runpath=yes
+ ;;
+ esac
+ else
+ shlibpath_overrides_runpath=yes
+ fi
+ ;;
+
+os2*)
+ libname_spec='$name'
+ shrext_cmds=".dll"
+ need_lib_prefix=no
+ library_names_spec='$libname${shared_ext} $libname.a'
+ dynamic_linker='OS/2 ld.exe'
+ shlibpath_var=LIBPATH
+ ;;
+
+osf3* | osf4* | osf5*)
+ version_type=osf
+ need_lib_prefix=no
+ need_version=no
+ soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+ sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec"
+ ;;
+
+rdos*)
+ dynamic_linker=no
+ ;;
+
+solaris*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ # ldd complains unless libraries are executable
+ postinstall_cmds='chmod +x $lib'
+ ;;
+
+sunos4*)
+ version_type=sunos
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ if test "$with_gnu_ld" = yes; then
+ need_lib_prefix=no
+ fi
+ need_version=yes
+ ;;
+
+sysv4 | sysv4.3*)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ case $host_vendor in
+ sni)
+ shlibpath_overrides_runpath=no
+ need_lib_prefix=no
+ runpath_var=LD_RUN_PATH
+ ;;
+ siemens)
+ need_lib_prefix=no
+ ;;
+ motorola)
+ need_lib_prefix=no
+ need_version=no
+ shlibpath_overrides_runpath=no
+ sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+ ;;
+ esac
+ ;;
+
+sysv4*MP*)
+ if test -d /usr/nec ;then
+ version_type=linux
+ library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}'
+ soname_spec='$libname${shared_ext}.$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ fi
+ ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+ version_type=freebsd-elf
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ if test "$with_gnu_ld" = yes; then
+ sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib'
+ else
+ sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'
+ case $host_os in
+ sco3.2v5*)
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /lib"
+ ;;
+ esac
+ fi
+ sys_lib_dlsearch_path_spec='/usr/lib'
+ ;;
+
+tpf*)
+ # TPF is a cross-target only. Preferred cross-host = GNU/Linux.
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+
+uts4*)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+*)
+ dynamic_linker=no
+ ;;
+esac
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5
+$as_echo "$dynamic_linker" >&6; }
+test "$dynamic_linker" = no && can_build_shared=no
+
+variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
+if test "$GCC" = yes; then
+ variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
+fi
+
+if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then
+ sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec"
+fi
+if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then
+ sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec"
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5
+$as_echo_n "checking how to hardcode library paths into programs... " >&6; }
+hardcode_action=
+if test -n "$hardcode_libdir_flag_spec" ||
+ test -n "$runpath_var" ||
+ test "X$hardcode_automatic" = "Xyes" ; then
+
+ # We can hardcode non-existent directories.
+ if test "$hardcode_direct" != no &&
+ # If the only mechanism to avoid hardcoding is shlibpath_var, we
+ # have to relink, otherwise we might link with an installed library
+ # when we should be linking with a yet-to-be-installed one
+ ## test "$_LT_TAGVAR(hardcode_shlibpath_var, )" != no &&
+ test "$hardcode_minus_L" != no; then
+ # Linking always hardcodes the temporary library directory.
+ hardcode_action=relink
+ else
+ # We can link without hardcoding, and we can hardcode nonexisting dirs.
+ hardcode_action=immediate
+ fi
+else
+ # We cannot hardcode anything, or else we can only hardcode existing
+ # directories.
+ hardcode_action=unsupported
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5
+$as_echo "$hardcode_action" >&6; }
+
+if test "$hardcode_action" = relink ||
+ test "$inherit_rpath" = yes; then
+ # Fast installation is not supported
+ enable_fast_install=no
+elif test "$shlibpath_overrides_runpath" = yes ||
+ test "$enable_shared" = no; then
+ # Fast installation is not necessary
+ enable_fast_install=needless
+fi
+
+
+
+
+
+
+ if test "x$enable_dlopen" != xyes; then
+ enable_dlopen=unknown
+ enable_dlopen_self=unknown
+ enable_dlopen_self_static=unknown
+else
+ lt_cv_dlopen=no
+ lt_cv_dlopen_libs=
+
+ case $host_os in
+ beos*)
+ lt_cv_dlopen="load_add_on"
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+ ;;
+
+ mingw* | pw32* | cegcc*)
+ lt_cv_dlopen="LoadLibrary"
+ lt_cv_dlopen_libs=
+ ;;
+
+ cygwin*)
+ lt_cv_dlopen="dlopen"
+ lt_cv_dlopen_libs=
+ ;;
+
+ darwin*)
+ # if libdl is installed we need to link against it
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
+$as_echo_n "checking for dlopen in -ldl... " >&6; }
+if ${ac_cv_lib_dl_dlopen+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_dl_dlopen=yes
+else
+ ac_cv_lib_dl_dlopen=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
+$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
+if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
+ lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"
+else
+
+ lt_cv_dlopen="dyld"
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+
+fi
+
+ ;;
+
+ *)
+ ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load"
+if test "x$ac_cv_func_shl_load" = xyes; then :
+ lt_cv_dlopen="shl_load"
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5
+$as_echo_n "checking for shl_load in -ldld... " >&6; }
+if ${ac_cv_lib_dld_shl_load+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char shl_load ();
+int
+main ()
+{
+return shl_load ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_dld_shl_load=yes
+else
+ ac_cv_lib_dld_shl_load=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5
+$as_echo "$ac_cv_lib_dld_shl_load" >&6; }
+if test "x$ac_cv_lib_dld_shl_load" = xyes; then :
+ lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"
+else
+ ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen"
+if test "x$ac_cv_func_dlopen" = xyes; then :
+ lt_cv_dlopen="dlopen"
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
+$as_echo_n "checking for dlopen in -ldl... " >&6; }
+if ${ac_cv_lib_dl_dlopen+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_dl_dlopen=yes
+else
+ ac_cv_lib_dl_dlopen=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
+$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
+if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
+ lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5
+$as_echo_n "checking for dlopen in -lsvld... " >&6; }
+if ${ac_cv_lib_svld_dlopen+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsvld $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_svld_dlopen=yes
+else
+ ac_cv_lib_svld_dlopen=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5
+$as_echo "$ac_cv_lib_svld_dlopen" >&6; }
+if test "x$ac_cv_lib_svld_dlopen" = xyes; then :
+ lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5
+$as_echo_n "checking for dld_link in -ldld... " >&6; }
+if ${ac_cv_lib_dld_dld_link+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dld_link ();
+int
+main ()
+{
+return dld_link ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_dld_dld_link=yes
+else
+ ac_cv_lib_dld_dld_link=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5
+$as_echo "$ac_cv_lib_dld_dld_link" >&6; }
+if test "x$ac_cv_lib_dld_dld_link" = xyes; then :
+ lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+ ;;
+ esac
+
+ if test "x$lt_cv_dlopen" != xno; then
+ enable_dlopen=yes
+ else
+ enable_dlopen=no
+ fi
+
+ case $lt_cv_dlopen in
+ dlopen)
+ save_CPPFLAGS="$CPPFLAGS"
+ test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+
+ save_LDFLAGS="$LDFLAGS"
+ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
+
+ save_LIBS="$LIBS"
+ LIBS="$lt_cv_dlopen_libs $LIBS"
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5
+$as_echo_n "checking whether a program can dlopen itself... " >&6; }
+if ${lt_cv_dlopen_self+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then :
+ lt_cv_dlopen_self=cross
+else
+ lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+ lt_status=$lt_dlunknown
+ cat > conftest.$ac_ext <<_LT_EOF
+#line __oline__ "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+# define LT_DLGLOBAL RTLD_GLOBAL
+#else
+# ifdef DL_GLOBAL
+# define LT_DLGLOBAL DL_GLOBAL
+# else
+# define LT_DLGLOBAL 0
+# endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+ find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+# ifdef RTLD_LAZY
+# define LT_DLLAZY_OR_NOW RTLD_LAZY
+# else
+# ifdef DL_LAZY
+# define LT_DLLAZY_OR_NOW DL_LAZY
+# else
+# ifdef RTLD_NOW
+# define LT_DLLAZY_OR_NOW RTLD_NOW
+# else
+# ifdef DL_NOW
+# define LT_DLLAZY_OR_NOW DL_NOW
+# else
+# define LT_DLLAZY_OR_NOW 0
+# endif
+# endif
+# endif
+# endif
+#endif
+
+void fnord() { int i=42;}
+int main ()
+{
+ void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+ int status = $lt_dlunknown;
+
+ if (self)
+ {
+ if (dlsym (self,"fnord")) status = $lt_dlno_uscore;
+ else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+ /* dlclose (self); */
+ }
+ else
+ puts (dlerror ());
+
+ return status;
+}
+_LT_EOF
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then
+ (./conftest; exit; ) >&5 2>/dev/null
+ lt_status=$?
+ case x$lt_status in
+ x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;;
+ x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;;
+ x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;;
+ esac
+ else :
+ # compilation failed
+ lt_cv_dlopen_self=no
+ fi
+fi
+rm -fr conftest*
+
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5
+$as_echo "$lt_cv_dlopen_self" >&6; }
+
+ if test "x$lt_cv_dlopen_self" = xyes; then
+ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5
+$as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; }
+if ${lt_cv_dlopen_self_static+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then :
+ lt_cv_dlopen_self_static=cross
+else
+ lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+ lt_status=$lt_dlunknown
+ cat > conftest.$ac_ext <<_LT_EOF
+#line __oline__ "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+# define LT_DLGLOBAL RTLD_GLOBAL
+#else
+# ifdef DL_GLOBAL
+# define LT_DLGLOBAL DL_GLOBAL
+# else
+# define LT_DLGLOBAL 0
+# endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+ find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+# ifdef RTLD_LAZY
+# define LT_DLLAZY_OR_NOW RTLD_LAZY
+# else
+# ifdef DL_LAZY
+# define LT_DLLAZY_OR_NOW DL_LAZY
+# else
+# ifdef RTLD_NOW
+# define LT_DLLAZY_OR_NOW RTLD_NOW
+# else
+# ifdef DL_NOW
+# define LT_DLLAZY_OR_NOW DL_NOW
+# else
+# define LT_DLLAZY_OR_NOW 0
+# endif
+# endif
+# endif
+# endif
+#endif
+
+void fnord() { int i=42;}
+int main ()
+{
+ void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+ int status = $lt_dlunknown;
+
+ if (self)
+ {
+ if (dlsym (self,"fnord")) status = $lt_dlno_uscore;
+ else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+ /* dlclose (self); */
+ }
+ else
+ puts (dlerror ());
+
+ return status;
+}
+_LT_EOF
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then
+ (./conftest; exit; ) >&5 2>/dev/null
+ lt_status=$?
+ case x$lt_status in
+ x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;;
+ x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;;
+ x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;;
+ esac
+ else :
+ # compilation failed
+ lt_cv_dlopen_self_static=no
+ fi
+fi
+rm -fr conftest*
+
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5
+$as_echo "$lt_cv_dlopen_self_static" >&6; }
+ fi
+
+ CPPFLAGS="$save_CPPFLAGS"
+ LDFLAGS="$save_LDFLAGS"
+ LIBS="$save_LIBS"
+ ;;
+ esac
+
+ case $lt_cv_dlopen_self in
+ yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+ *) enable_dlopen_self=unknown ;;
+ esac
+
+ case $lt_cv_dlopen_self_static in
+ yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+ *) enable_dlopen_self_static=unknown ;;
+ esac
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+striplib=
+old_striplib=
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5
+$as_echo_n "checking whether stripping libraries is possible... " >&6; }
+if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then
+ test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
+ test -z "$striplib" && striplib="$STRIP --strip-unneeded"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+# FIXME - insert some real tests, host_os isn't really good enough
+ case $host_os in
+ darwin*)
+ if test -n "$STRIP" ; then
+ striplib="$STRIP -x"
+ old_striplib="$STRIP -S"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ fi
+ ;;
+ *)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ ;;
+ esac
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+ # Report which library types will actually be built
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5
+$as_echo_n "checking if libtool supports shared libraries... " >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5
+$as_echo "$can_build_shared" >&6; }
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5
+$as_echo_n "checking whether to build shared libraries... " >&6; }
+ test "$can_build_shared" = "no" && enable_shared=no
+
+ # On AIX, shared libraries and static libraries use the same namespace, and
+ # are all built from PIC.
+ case $host_os in
+ aix3*)
+ test "$enable_shared" = yes && enable_static=no
+ if test -n "$RANLIB"; then
+ archive_cmds="$archive_cmds~\$RANLIB \$lib"
+ postinstall_cmds='$RANLIB $lib'
+ fi
+ ;;
+
+ aix[4-9]*)
+ if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+ test "$enable_shared" = yes && enable_static=no
+ fi
+ ;;
+ esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5
+$as_echo "$enable_shared" >&6; }
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5
+$as_echo_n "checking whether to build static libraries... " >&6; }
+ # Make sure either enable_shared or enable_static is yes.
+ test "$enable_shared" = yes || enable_static=yes
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5
+$as_echo "$enable_static" >&6; }
+
+
+
+
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+CC="$lt_save_CC"
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ac_config_commands="$ac_config_commands libtool"
+
+
+
+
+# Only expand once:
+
+
+
+# Extract the first word of "ar", so it can be a program name with args.
+set dummy ar; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_AR+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $AR in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_AR="$AR" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_AR="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_AR" && ac_cv_path_AR="ar"
+ ;;
+esac
+fi
+AR=$ac_cv_path_AR
+if test -n "$AR"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5
+$as_echo "$AR" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+# Extract the first word of "rm", so it can be a program name with args.
+set dummy rm; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_RM+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $RM in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_RM="$RM" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_RM="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_RM" && ac_cv_path_RM="rm"
+ ;;
+esac
+fi
+RM=$ac_cv_path_RM
+if test -n "$RM"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RM" >&5
+$as_echo "$RM" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+# Extract the first word of "cp", so it can be a program name with args.
+set dummy cp; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_CP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $CP in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_CP="$CP" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_CP="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_CP" && ac_cv_path_CP="cp"
+ ;;
+esac
+fi
+CP=$ac_cv_path_CP
+if test -n "$CP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CP" >&5
+$as_echo "$CP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+# Extract the first word of "ln", so it can be a program name with args.
+set dummy ln; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_LN+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $LN in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_LN="$LN" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_LN="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_LN" && ac_cv_path_LN="ln"
+ ;;
+esac
+fi
+LN=$ac_cv_path_LN
+if test -n "$LN"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LN" >&5
+$as_echo "$LN" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+# Extract the first word of "sed", so it can be a program name with args.
+set dummy sed; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_SED+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $SED in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_SED="$SED" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_SED="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_SED" && ac_cv_path_SED="sed"
+ ;;
+esac
+fi
+SED=$ac_cv_path_SED
+if test -n "$SED"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SED" >&5
+$as_echo "$SED" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+# Extract the first word of "make", so it can be a program name with args.
+set dummy make; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_MAKE+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $MAKE in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_MAKE="$MAKE" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_MAKE="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+MAKE=$ac_cv_path_MAKE
+if test -n "$MAKE"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAKE" >&5
+$as_echo "$MAKE" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether NLS is requested" >&5
+$as_echo_n "checking whether NLS is requested... " >&6; }
+ @%:@ Check whether --enable-nls was given.
+if test "${enable_nls+set}" = set; then :
+ enableval=$enable_nls; USE_NLS=$enableval
+else
+ USE_NLS=yes
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $USE_NLS" >&5
+$as_echo "$USE_NLS" >&6; }
+
+
+
+
+
+
+# Prepare PATH_SEPARATOR.
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+# Find out how to test for executable files. Don't use a zero-byte file,
+# as systems may use methods other than mode bits to determine executability.
+cat >conf$$.file <<_ASEOF
+#! /bin/sh
+exit 0
+_ASEOF
+chmod +x conf$$.file
+if test -x conf$$.file >/dev/null 2>&1; then
+ ac_executable_p="test -x"
+else
+ ac_executable_p="test -f"
+fi
+rm -f conf$$.file
+
+# Extract the first word of "msgfmt", so it can be a program name with args.
+set dummy msgfmt; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_MSGFMT+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case "$MSGFMT" in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_MSGFMT="$MSGFMT" # Let the user override the test with a path.
+ ;;
+ *)
+ ac_save_IFS="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH; do
+ IFS="$ac_save_IFS"
+ test -z "$ac_dir" && ac_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $ac_executable_p "$ac_dir/$ac_word$ac_exec_ext"; then
+ echo "$as_me: trying $ac_dir/$ac_word..." >&5
+ if $ac_dir/$ac_word --statistics /dev/null >&5 2>&1 &&
+ (if $ac_dir/$ac_word --statistics /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi); then
+ ac_cv_path_MSGFMT="$ac_dir/$ac_word$ac_exec_ext"
+ break 2
+ fi
+ fi
+ done
+ done
+ IFS="$ac_save_IFS"
+ test -z "$ac_cv_path_MSGFMT" && ac_cv_path_MSGFMT=":"
+ ;;
+esac
+fi
+MSGFMT="$ac_cv_path_MSGFMT"
+if test "$MSGFMT" != ":"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MSGFMT" >&5
+$as_echo "$MSGFMT" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ # Extract the first word of "gmsgfmt", so it can be a program name with args.
+set dummy gmsgfmt; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_GMSGFMT+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $GMSGFMT in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_GMSGFMT="$GMSGFMT" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_GMSGFMT="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_GMSGFMT" && ac_cv_path_GMSGFMT="$MSGFMT"
+ ;;
+esac
+fi
+GMSGFMT=$ac_cv_path_GMSGFMT
+if test -n "$GMSGFMT"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $GMSGFMT" >&5
+$as_echo "$GMSGFMT" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+ case `$MSGFMT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in
+ '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) MSGFMT_015=: ;;
+ *) MSGFMT_015=$MSGFMT ;;
+ esac
+
+ case `$GMSGFMT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in
+ '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) GMSGFMT_015=: ;;
+ *) GMSGFMT_015=$GMSGFMT ;;
+ esac
+
+
+
+# Prepare PATH_SEPARATOR.
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+# Find out how to test for executable files. Don't use a zero-byte file,
+# as systems may use methods other than mode bits to determine executability.
+cat >conf$$.file <<_ASEOF
+#! /bin/sh
+exit 0
+_ASEOF
+chmod +x conf$$.file
+if test -x conf$$.file >/dev/null 2>&1; then
+ ac_executable_p="test -x"
+else
+ ac_executable_p="test -f"
+fi
+rm -f conf$$.file
+
+# Extract the first word of "xgettext", so it can be a program name with args.
+set dummy xgettext; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_XGETTEXT+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case "$XGETTEXT" in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_XGETTEXT="$XGETTEXT" # Let the user override the test with a path.
+ ;;
+ *)
+ ac_save_IFS="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH; do
+ IFS="$ac_save_IFS"
+ test -z "$ac_dir" && ac_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $ac_executable_p "$ac_dir/$ac_word$ac_exec_ext"; then
+ echo "$as_me: trying $ac_dir/$ac_word..." >&5
+ if $ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= /dev/null >&5 2>&1 &&
+ (if $ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi); then
+ ac_cv_path_XGETTEXT="$ac_dir/$ac_word$ac_exec_ext"
+ break 2
+ fi
+ fi
+ done
+ done
+ IFS="$ac_save_IFS"
+ test -z "$ac_cv_path_XGETTEXT" && ac_cv_path_XGETTEXT=":"
+ ;;
+esac
+fi
+XGETTEXT="$ac_cv_path_XGETTEXT"
+if test "$XGETTEXT" != ":"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $XGETTEXT" >&5
+$as_echo "$XGETTEXT" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ rm -f messages.po
+
+ case `$XGETTEXT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in
+ '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) XGETTEXT_015=: ;;
+ *) XGETTEXT_015=$XGETTEXT ;;
+ esac
+
+
+
+# Prepare PATH_SEPARATOR.
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+# Find out how to test for executable files. Don't use a zero-byte file,
+# as systems may use methods other than mode bits to determine executability.
+cat >conf$$.file <<_ASEOF
+#! /bin/sh
+exit 0
+_ASEOF
+chmod +x conf$$.file
+if test -x conf$$.file >/dev/null 2>&1; then
+ ac_executable_p="test -x"
+else
+ ac_executable_p="test -f"
+fi
+rm -f conf$$.file
+
+# Extract the first word of "msgmerge", so it can be a program name with args.
+set dummy msgmerge; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_MSGMERGE+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case "$MSGMERGE" in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_MSGMERGE="$MSGMERGE" # Let the user override the test with a path.
+ ;;
+ *)
+ ac_save_IFS="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH; do
+ IFS="$ac_save_IFS"
+ test -z "$ac_dir" && ac_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $ac_executable_p "$ac_dir/$ac_word$ac_exec_ext"; then
+ echo "$as_me: trying $ac_dir/$ac_word..." >&5
+ if $ac_dir/$ac_word --update -q /dev/null /dev/null >&5 2>&1; then
+ ac_cv_path_MSGMERGE="$ac_dir/$ac_word$ac_exec_ext"
+ break 2
+ fi
+ fi
+ done
+ done
+ IFS="$ac_save_IFS"
+ test -z "$ac_cv_path_MSGMERGE" && ac_cv_path_MSGMERGE=":"
+ ;;
+esac
+fi
+MSGMERGE="$ac_cv_path_MSGMERGE"
+if test "$MSGMERGE" != ":"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MSGMERGE" >&5
+$as_echo "$MSGMERGE" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$localedir" || localedir='${datadir}/locale'
+
+
+ ac_config_commands="$ac_config_commands po-directories"
+
+
+
+ if test "X$prefix" = "XNONE"; then
+ acl_final_prefix="$ac_default_prefix"
+ else
+ acl_final_prefix="$prefix"
+ fi
+ if test "X$exec_prefix" = "XNONE"; then
+ acl_final_exec_prefix='${prefix}'
+ else
+ acl_final_exec_prefix="$exec_prefix"
+ fi
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ eval acl_final_exec_prefix=\"$acl_final_exec_prefix\"
+ prefix="$acl_save_prefix"
+
+
+@%:@ Check whether --with-gnu-ld was given.
+if test "${with_gnu_ld+set}" = set; then :
+ withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes
+else
+ with_gnu_ld=no
+fi
+
+# Prepare PATH_SEPARATOR.
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+ac_prog=ld
+if test "$GCC" = yes; then
+ # Check if gcc -print-prog-name=ld gives a path.
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by GCC" >&5
+$as_echo_n "checking for ld used by GCC... " >&6; }
+ case $host in
+ *-*-mingw*)
+ # gcc leaves a trailing carriage return which upsets mingw
+ ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+ *)
+ ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+ esac
+ case $ac_prog in
+ # Accept absolute paths.
+ [\\/]* | [A-Za-z]:[\\/]*)
+ re_direlt='/[^/][^/]*/\.\./'
+ # Canonicalize the path of ld
+ ac_prog=`echo $ac_prog| sed 's%\\\\%/%g'`
+ while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do
+ ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"`
+ done
+ test -z "$LD" && LD="$ac_prog"
+ ;;
+ "")
+ # If it fails, then pretend we aren't using GCC.
+ ac_prog=ld
+ ;;
+ *)
+ # If it is relative, then search for the first ld in PATH.
+ with_gnu_ld=unknown
+ ;;
+ esac
+elif test "$with_gnu_ld" = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5
+$as_echo_n "checking for GNU ld... " >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5
+$as_echo_n "checking for non-GNU ld... " >&6; }
+fi
+if ${acl_cv_path_LD+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$LD"; then
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR-:}"
+ for ac_dir in $PATH; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+ acl_cv_path_LD="$ac_dir/$ac_prog"
+ # Check to see if the program is GNU ld. I'd rather use --version,
+ # but apparently some GNU ld's only accept -v.
+ # Break only if it was the GNU/non-GNU ld that we prefer.
+ case `"$acl_cv_path_LD" -v 2>&1 < /dev/null` in
+ *GNU* | *'with BFD'*)
+ test "$with_gnu_ld" != no && break ;;
+ *)
+ test "$with_gnu_ld" != yes && break ;;
+ esac
+ fi
+ done
+ IFS="$ac_save_ifs"
+else
+ acl_cv_path_LD="$LD" # Let the user override the test with a path.
+fi
+fi
+
+LD="$acl_cv_path_LD"
+if test -n "$LD"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LD" >&5
+$as_echo "$LD" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5
+$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; }
+if ${acl_cv_prog_gnu_ld+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ # I'd rather use --version here, but apparently some GNU ld's only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+ acl_cv_prog_gnu_ld=yes ;;
+*)
+ acl_cv_prog_gnu_ld=no ;;
+esac
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $acl_cv_prog_gnu_ld" >&5
+$as_echo "$acl_cv_prog_gnu_ld" >&6; }
+with_gnu_ld=$acl_cv_prog_gnu_ld
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shared library run path origin" >&5
+$as_echo_n "checking for shared library run path origin... " >&6; }
+if ${acl_cv_rpath+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ CC="$CC" GCC="$GCC" LDFLAGS="$LDFLAGS" LD="$LD" with_gnu_ld="$with_gnu_ld" \
+ ${CONFIG_SHELL-/bin/sh} "$ac_aux_dir/config.rpath" "$host" > conftest.sh
+ . ./conftest.sh
+ rm -f ./conftest.sh
+ acl_cv_rpath=done
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $acl_cv_rpath" >&5
+$as_echo "$acl_cv_rpath" >&6; }
+ wl="$acl_cv_wl"
+ libext="$acl_cv_libext"
+ shlibext="$acl_cv_shlibext"
+ hardcode_libdir_flag_spec="$acl_cv_hardcode_libdir_flag_spec"
+ hardcode_libdir_separator="$acl_cv_hardcode_libdir_separator"
+ hardcode_direct="$acl_cv_hardcode_direct"
+ hardcode_minus_L="$acl_cv_hardcode_minus_L"
+ @%:@ Check whether --enable-rpath was given.
+if test "${enable_rpath+set}" = set; then :
+ enableval=$enable_rpath; :
+else
+ enable_rpath=yes
+fi
+
+
+
+ acl_libdirstem=lib
+ searchpath=`(LC_ALL=C $CC -print-search-dirs) 2>/dev/null | sed -n -e 's,^libraries: ,,p' | sed -e 's,^=,,'`
+ if test -n "$searchpath"; then
+ acl_save_IFS="${IFS= }"; IFS=":"
+ for searchdir in $searchpath; do
+ if test -d "$searchdir"; then
+ case "$searchdir" in
+ */lib64/ | */lib64 ) acl_libdirstem=lib64 ;;
+ *) searchdir=`cd "$searchdir" && pwd`
+ case "$searchdir" in
+ */lib64 ) acl_libdirstem=lib64 ;;
+ esac ;;
+ esac
+ fi
+ done
+ IFS="$acl_save_IFS"
+ fi
+
+
+
+
+
+
+
+
+ use_additional=yes
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+
+@%:@ Check whether --with-libiconv-prefix was given.
+if test "${with_libiconv_prefix+set}" = set; then :
+ withval=$with_libiconv_prefix;
+ if test "X$withval" = "Xno"; then
+ use_additional=no
+ else
+ if test "X$withval" = "X"; then
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ else
+ additional_includedir="$withval/include"
+ additional_libdir="$withval/$acl_libdirstem"
+ fi
+ fi
+
+fi
+
+ LIBICONV=
+ LTLIBICONV=
+ INCICONV=
+ rpathdirs=
+ ltrpathdirs=
+ names_already_handled=
+ names_next_round='iconv '
+ while test -n "$names_next_round"; do
+ names_this_round="$names_next_round"
+ names_next_round=
+ for name in $names_this_round; do
+ already_handled=
+ for n in $names_already_handled; do
+ if test "$n" = "$name"; then
+ already_handled=yes
+ break
+ fi
+ done
+ if test -z "$already_handled"; then
+ names_already_handled="$names_already_handled $name"
+ uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'`
+ eval value=\"\$HAVE_LIB$uppername\"
+ if test -n "$value"; then
+ if test "$value" = yes; then
+ eval value=\"\$LIB$uppername\"
+ test -z "$value" || LIBICONV="${LIBICONV}${LIBICONV:+ }$value"
+ eval value=\"\$LTLIB$uppername\"
+ test -z "$value" || LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }$value"
+ else
+ :
+ fi
+ else
+ found_dir=
+ found_la=
+ found_so=
+ found_a=
+ if test $use_additional = yes; then
+ if test -n "$shlibext" \
+ && { test -f "$additional_libdir/lib$name.$shlibext" \
+ || { test "$shlibext" = dll \
+ && test -f "$additional_libdir/lib$name.dll.a"; }; }; then
+ found_dir="$additional_libdir"
+ if test -f "$additional_libdir/lib$name.$shlibext"; then
+ found_so="$additional_libdir/lib$name.$shlibext"
+ else
+ found_so="$additional_libdir/lib$name.dll.a"
+ fi
+ if test -f "$additional_libdir/lib$name.la"; then
+ found_la="$additional_libdir/lib$name.la"
+ fi
+ else
+ if test -f "$additional_libdir/lib$name.$libext"; then
+ found_dir="$additional_libdir"
+ found_a="$additional_libdir/lib$name.$libext"
+ if test -f "$additional_libdir/lib$name.la"; then
+ found_la="$additional_libdir/lib$name.la"
+ fi
+ fi
+ fi
+ fi
+ if test "X$found_dir" = "X"; then
+ for x in $LDFLAGS $LTLIBICONV; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ case "$x" in
+ -L*)
+ dir=`echo "X$x" | sed -e 's/^X-L//'`
+ if test -n "$shlibext" \
+ && { test -f "$dir/lib$name.$shlibext" \
+ || { test "$shlibext" = dll \
+ && test -f "$dir/lib$name.dll.a"; }; }; then
+ found_dir="$dir"
+ if test -f "$dir/lib$name.$shlibext"; then
+ found_so="$dir/lib$name.$shlibext"
+ else
+ found_so="$dir/lib$name.dll.a"
+ fi
+ if test -f "$dir/lib$name.la"; then
+ found_la="$dir/lib$name.la"
+ fi
+ else
+ if test -f "$dir/lib$name.$libext"; then
+ found_dir="$dir"
+ found_a="$dir/lib$name.$libext"
+ if test -f "$dir/lib$name.la"; then
+ found_la="$dir/lib$name.la"
+ fi
+ fi
+ fi
+ ;;
+ esac
+ if test "X$found_dir" != "X"; then
+ break
+ fi
+ done
+ fi
+ if test "X$found_dir" != "X"; then
+ LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-L$found_dir -l$name"
+ if test "X$found_so" != "X"; then
+ if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/$acl_libdirstem"; then
+ LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so"
+ else
+ haveit=
+ for x in $ltrpathdirs; do
+ if test "X$x" = "X$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ ltrpathdirs="$ltrpathdirs $found_dir"
+ fi
+ if test "$hardcode_direct" = yes; then
+ LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so"
+ else
+ if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then
+ LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so"
+ haveit=
+ for x in $rpathdirs; do
+ if test "X$x" = "X$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ rpathdirs="$rpathdirs $found_dir"
+ fi
+ else
+ haveit=
+ for x in $LDFLAGS $LIBICONV; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-L$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ LIBICONV="${LIBICONV}${LIBICONV:+ }-L$found_dir"
+ fi
+ if test "$hardcode_minus_L" != no; then
+ LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so"
+ else
+ LIBICONV="${LIBICONV}${LIBICONV:+ }-l$name"
+ fi
+ fi
+ fi
+ fi
+ else
+ if test "X$found_a" != "X"; then
+ LIBICONV="${LIBICONV}${LIBICONV:+ }$found_a"
+ else
+ LIBICONV="${LIBICONV}${LIBICONV:+ }-L$found_dir -l$name"
+ fi
+ fi
+ additional_includedir=
+ case "$found_dir" in
+ */$acl_libdirstem | */$acl_libdirstem/)
+ basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem/"'*$,,'`
+ additional_includedir="$basedir/include"
+ ;;
+ esac
+ if test "X$additional_includedir" != "X"; then
+ if test "X$additional_includedir" != "X/usr/include"; then
+ haveit=
+ if test "X$additional_includedir" = "X/usr/local/include"; then
+ if test -n "$GCC"; then
+ case $host_os in
+ linux* | gnu* | k*bsd*-gnu) haveit=yes;;
+ esac
+ fi
+ fi
+ if test -z "$haveit"; then
+ for x in $CPPFLAGS $INCICONV; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-I$additional_includedir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_includedir"; then
+ INCICONV="${INCICONV}${INCICONV:+ }-I$additional_includedir"
+ fi
+ fi
+ fi
+ fi
+ fi
+ if test -n "$found_la"; then
+ save_libdir="$libdir"
+ case "$found_la" in
+ */* | *\\*) . "$found_la" ;;
+ *) . "./$found_la" ;;
+ esac
+ libdir="$save_libdir"
+ for dep in $dependency_libs; do
+ case "$dep" in
+ -L*)
+ additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'`
+ if test "X$additional_libdir" != "X/usr/$acl_libdirstem"; then
+ haveit=
+ if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem"; then
+ if test -n "$GCC"; then
+ case $host_os in
+ linux* | gnu* | k*bsd*-gnu) haveit=yes;;
+ esac
+ fi
+ fi
+ if test -z "$haveit"; then
+ haveit=
+ for x in $LDFLAGS $LIBICONV; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-L$additional_libdir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_libdir"; then
+ LIBICONV="${LIBICONV}${LIBICONV:+ }-L$additional_libdir"
+ fi
+ fi
+ haveit=
+ for x in $LDFLAGS $LTLIBICONV; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-L$additional_libdir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_libdir"; then
+ LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-L$additional_libdir"
+ fi
+ fi
+ fi
+ fi
+ ;;
+ -R*)
+ dir=`echo "X$dep" | sed -e 's/^X-R//'`
+ if test "$enable_rpath" != no; then
+ haveit=
+ for x in $rpathdirs; do
+ if test "X$x" = "X$dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ rpathdirs="$rpathdirs $dir"
+ fi
+ haveit=
+ for x in $ltrpathdirs; do
+ if test "X$x" = "X$dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ ltrpathdirs="$ltrpathdirs $dir"
+ fi
+ fi
+ ;;
+ -l*)
+ names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'`
+ ;;
+ *.la)
+ names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'`
+ ;;
+ *)
+ LIBICONV="${LIBICONV}${LIBICONV:+ }$dep"
+ LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }$dep"
+ ;;
+ esac
+ done
+ fi
+ else
+ LIBICONV="${LIBICONV}${LIBICONV:+ }-l$name"
+ LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-l$name"
+ fi
+ fi
+ fi
+ done
+ done
+ if test "X$rpathdirs" != "X"; then
+ if test -n "$hardcode_libdir_separator"; then
+ alldirs=
+ for found_dir in $rpathdirs; do
+ alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$found_dir"
+ done
+ acl_save_libdir="$libdir"
+ libdir="$alldirs"
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ libdir="$acl_save_libdir"
+ LIBICONV="${LIBICONV}${LIBICONV:+ }$flag"
+ else
+ for found_dir in $rpathdirs; do
+ acl_save_libdir="$libdir"
+ libdir="$found_dir"
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ libdir="$acl_save_libdir"
+ LIBICONV="${LIBICONV}${LIBICONV:+ }$flag"
+ done
+ fi
+ fi
+ if test "X$ltrpathdirs" != "X"; then
+ for found_dir in $ltrpathdirs; do
+ LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-R$found_dir"
+ done
+ fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for CFPreferencesCopyAppValue" >&5
+$as_echo_n "checking for CFPreferencesCopyAppValue... " >&6; }
+if ${gt_cv_func_CFPreferencesCopyAppValue+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ gt_save_LIBS="$LIBS"
+ LIBS="$LIBS -Wl,-framework -Wl,CoreFoundation"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <CoreFoundation/CFPreferences.h>
+int
+main ()
+{
+CFPreferencesCopyAppValue(NULL, NULL)
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ gt_cv_func_CFPreferencesCopyAppValue=yes
+else
+ gt_cv_func_CFPreferencesCopyAppValue=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LIBS="$gt_save_LIBS"
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gt_cv_func_CFPreferencesCopyAppValue" >&5
+$as_echo "$gt_cv_func_CFPreferencesCopyAppValue" >&6; }
+ if test $gt_cv_func_CFPreferencesCopyAppValue = yes; then
+
+$as_echo "@%:@define HAVE_CFPREFERENCESCOPYAPPVALUE 1" >>confdefs.h
+
+ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for CFLocaleCopyCurrent" >&5
+$as_echo_n "checking for CFLocaleCopyCurrent... " >&6; }
+if ${gt_cv_func_CFLocaleCopyCurrent+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ gt_save_LIBS="$LIBS"
+ LIBS="$LIBS -Wl,-framework -Wl,CoreFoundation"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <CoreFoundation/CFLocale.h>
+int
+main ()
+{
+CFLocaleCopyCurrent();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ gt_cv_func_CFLocaleCopyCurrent=yes
+else
+ gt_cv_func_CFLocaleCopyCurrent=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LIBS="$gt_save_LIBS"
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gt_cv_func_CFLocaleCopyCurrent" >&5
+$as_echo "$gt_cv_func_CFLocaleCopyCurrent" >&6; }
+ if test $gt_cv_func_CFLocaleCopyCurrent = yes; then
+
+$as_echo "@%:@define HAVE_CFLOCALECOPYCURRENT 1" >>confdefs.h
+
+ fi
+ INTL_MACOSX_LIBS=
+ if test $gt_cv_func_CFPreferencesCopyAppValue = yes || test $gt_cv_func_CFLocaleCopyCurrent = yes; then
+ INTL_MACOSX_LIBS="-Wl,-framework -Wl,CoreFoundation"
+ fi
+
+
+
+
+
+
+ LIBINTL=
+ LTLIBINTL=
+ POSUB=
+
+ case " $gt_needs " in
+ *" need-formatstring-macros "*) gt_api_version=3 ;;
+ *" need-ngettext "*) gt_api_version=2 ;;
+ *) gt_api_version=1 ;;
+ esac
+ gt_func_gnugettext_libc="gt_cv_func_gnugettext${gt_api_version}_libc"
+ gt_func_gnugettext_libintl="gt_cv_func_gnugettext${gt_api_version}_libintl"
+
+ if test "$USE_NLS" = "yes"; then
+ gt_use_preinstalled_gnugettext=no
+
+
+ if test $gt_api_version -ge 3; then
+ gt_revision_test_code='
+#ifndef __GNU_GETTEXT_SUPPORTED_REVISION
+#define __GNU_GETTEXT_SUPPORTED_REVISION(major) ((major) == 0 ? 0 : -1)
+#endif
+typedef int array [2 * (__GNU_GETTEXT_SUPPORTED_REVISION(0) >= 1) - 1];
+'
+ else
+ gt_revision_test_code=
+ fi
+ if test $gt_api_version -ge 2; then
+ gt_expression_test_code=' + * ngettext ("", "", 0)'
+ else
+ gt_expression_test_code=
+ fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU gettext in libc" >&5
+$as_echo_n "checking for GNU gettext in libc... " >&6; }
+if eval \${$gt_func_gnugettext_libc+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <libintl.h>
+$gt_revision_test_code
+extern int _nl_msg_cat_cntr;
+extern int *_nl_domain_bindings;
+int
+main ()
+{
+bindtextdomain ("", "");
+return * gettext ("")$gt_expression_test_code + _nl_msg_cat_cntr + *_nl_domain_bindings
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ eval "$gt_func_gnugettext_libc=yes"
+else
+ eval "$gt_func_gnugettext_libc=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+eval ac_res=\$$gt_func_gnugettext_libc
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+ if { eval "gt_val=\$$gt_func_gnugettext_libc"; test "$gt_val" != "yes"; }; then
+
+
+
+
+
+ am_save_CPPFLAGS="$CPPFLAGS"
+
+ for element in $INCICONV; do
+ haveit=
+ for x in $CPPFLAGS; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X$element"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }$element"
+ fi
+ done
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for iconv" >&5
+$as_echo_n "checking for iconv... " >&6; }
+if ${am_cv_func_iconv+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ am_cv_func_iconv="no, consider installing GNU libiconv"
+ am_cv_lib_iconv=no
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <iconv.h>
+int
+main ()
+{
+iconv_t cd = iconv_open("","");
+ iconv(cd,NULL,NULL,NULL,NULL);
+ iconv_close(cd);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ am_cv_func_iconv=yes
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ if test "$am_cv_func_iconv" != yes; then
+ am_save_LIBS="$LIBS"
+ LIBS="$LIBS $LIBICONV"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <iconv.h>
+int
+main ()
+{
+iconv_t cd = iconv_open("","");
+ iconv(cd,NULL,NULL,NULL,NULL);
+ iconv_close(cd);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ am_cv_lib_iconv=yes
+ am_cv_func_iconv=yes
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LIBS="$am_save_LIBS"
+ fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_func_iconv" >&5
+$as_echo "$am_cv_func_iconv" >&6; }
+ if test "$am_cv_func_iconv" = yes; then
+
+$as_echo "@%:@define HAVE_ICONV 1" >>confdefs.h
+
+ fi
+ if test "$am_cv_lib_iconv" = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link with libiconv" >&5
+$as_echo_n "checking how to link with libiconv... " >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIBICONV" >&5
+$as_echo "$LIBICONV" >&6; }
+ else
+ CPPFLAGS="$am_save_CPPFLAGS"
+ LIBICONV=
+ LTLIBICONV=
+ fi
+
+
+
+
+
+
+
+ use_additional=yes
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+
+@%:@ Check whether --with-libintl-prefix was given.
+if test "${with_libintl_prefix+set}" = set; then :
+ withval=$with_libintl_prefix;
+ if test "X$withval" = "Xno"; then
+ use_additional=no
+ else
+ if test "X$withval" = "X"; then
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ else
+ additional_includedir="$withval/include"
+ additional_libdir="$withval/$acl_libdirstem"
+ fi
+ fi
+
+fi
+
+ LIBINTL=
+ LTLIBINTL=
+ INCINTL=
+ rpathdirs=
+ ltrpathdirs=
+ names_already_handled=
+ names_next_round='intl '
+ while test -n "$names_next_round"; do
+ names_this_round="$names_next_round"
+ names_next_round=
+ for name in $names_this_round; do
+ already_handled=
+ for n in $names_already_handled; do
+ if test "$n" = "$name"; then
+ already_handled=yes
+ break
+ fi
+ done
+ if test -z "$already_handled"; then
+ names_already_handled="$names_already_handled $name"
+ uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'`
+ eval value=\"\$HAVE_LIB$uppername\"
+ if test -n "$value"; then
+ if test "$value" = yes; then
+ eval value=\"\$LIB$uppername\"
+ test -z "$value" || LIBINTL="${LIBINTL}${LIBINTL:+ }$value"
+ eval value=\"\$LTLIB$uppername\"
+ test -z "$value" || LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }$value"
+ else
+ :
+ fi
+ else
+ found_dir=
+ found_la=
+ found_so=
+ found_a=
+ if test $use_additional = yes; then
+ if test -n "$shlibext" \
+ && { test -f "$additional_libdir/lib$name.$shlibext" \
+ || { test "$shlibext" = dll \
+ && test -f "$additional_libdir/lib$name.dll.a"; }; }; then
+ found_dir="$additional_libdir"
+ if test -f "$additional_libdir/lib$name.$shlibext"; then
+ found_so="$additional_libdir/lib$name.$shlibext"
+ else
+ found_so="$additional_libdir/lib$name.dll.a"
+ fi
+ if test -f "$additional_libdir/lib$name.la"; then
+ found_la="$additional_libdir/lib$name.la"
+ fi
+ else
+ if test -f "$additional_libdir/lib$name.$libext"; then
+ found_dir="$additional_libdir"
+ found_a="$additional_libdir/lib$name.$libext"
+ if test -f "$additional_libdir/lib$name.la"; then
+ found_la="$additional_libdir/lib$name.la"
+ fi
+ fi
+ fi
+ fi
+ if test "X$found_dir" = "X"; then
+ for x in $LDFLAGS $LTLIBINTL; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ case "$x" in
+ -L*)
+ dir=`echo "X$x" | sed -e 's/^X-L//'`
+ if test -n "$shlibext" \
+ && { test -f "$dir/lib$name.$shlibext" \
+ || { test "$shlibext" = dll \
+ && test -f "$dir/lib$name.dll.a"; }; }; then
+ found_dir="$dir"
+ if test -f "$dir/lib$name.$shlibext"; then
+ found_so="$dir/lib$name.$shlibext"
+ else
+ found_so="$dir/lib$name.dll.a"
+ fi
+ if test -f "$dir/lib$name.la"; then
+ found_la="$dir/lib$name.la"
+ fi
+ else
+ if test -f "$dir/lib$name.$libext"; then
+ found_dir="$dir"
+ found_a="$dir/lib$name.$libext"
+ if test -f "$dir/lib$name.la"; then
+ found_la="$dir/lib$name.la"
+ fi
+ fi
+ fi
+ ;;
+ esac
+ if test "X$found_dir" != "X"; then
+ break
+ fi
+ done
+ fi
+ if test "X$found_dir" != "X"; then
+ LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }-L$found_dir -l$name"
+ if test "X$found_so" != "X"; then
+ if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/$acl_libdirstem"; then
+ LIBINTL="${LIBINTL}${LIBINTL:+ }$found_so"
+ else
+ haveit=
+ for x in $ltrpathdirs; do
+ if test "X$x" = "X$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ ltrpathdirs="$ltrpathdirs $found_dir"
+ fi
+ if test "$hardcode_direct" = yes; then
+ LIBINTL="${LIBINTL}${LIBINTL:+ }$found_so"
+ else
+ if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then
+ LIBINTL="${LIBINTL}${LIBINTL:+ }$found_so"
+ haveit=
+ for x in $rpathdirs; do
+ if test "X$x" = "X$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ rpathdirs="$rpathdirs $found_dir"
+ fi
+ else
+ haveit=
+ for x in $LDFLAGS $LIBINTL; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-L$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ LIBINTL="${LIBINTL}${LIBINTL:+ }-L$found_dir"
+ fi
+ if test "$hardcode_minus_L" != no; then
+ LIBINTL="${LIBINTL}${LIBINTL:+ }$found_so"
+ else
+ LIBINTL="${LIBINTL}${LIBINTL:+ }-l$name"
+ fi
+ fi
+ fi
+ fi
+ else
+ if test "X$found_a" != "X"; then
+ LIBINTL="${LIBINTL}${LIBINTL:+ }$found_a"
+ else
+ LIBINTL="${LIBINTL}${LIBINTL:+ }-L$found_dir -l$name"
+ fi
+ fi
+ additional_includedir=
+ case "$found_dir" in
+ */$acl_libdirstem | */$acl_libdirstem/)
+ basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem/"'*$,,'`
+ additional_includedir="$basedir/include"
+ ;;
+ esac
+ if test "X$additional_includedir" != "X"; then
+ if test "X$additional_includedir" != "X/usr/include"; then
+ haveit=
+ if test "X$additional_includedir" = "X/usr/local/include"; then
+ if test -n "$GCC"; then
+ case $host_os in
+ linux* | gnu* | k*bsd*-gnu) haveit=yes;;
+ esac
+ fi
+ fi
+ if test -z "$haveit"; then
+ for x in $CPPFLAGS $INCINTL; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-I$additional_includedir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_includedir"; then
+ INCINTL="${INCINTL}${INCINTL:+ }-I$additional_includedir"
+ fi
+ fi
+ fi
+ fi
+ fi
+ if test -n "$found_la"; then
+ save_libdir="$libdir"
+ case "$found_la" in
+ */* | *\\*) . "$found_la" ;;
+ *) . "./$found_la" ;;
+ esac
+ libdir="$save_libdir"
+ for dep in $dependency_libs; do
+ case "$dep" in
+ -L*)
+ additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'`
+ if test "X$additional_libdir" != "X/usr/$acl_libdirstem"; then
+ haveit=
+ if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem"; then
+ if test -n "$GCC"; then
+ case $host_os in
+ linux* | gnu* | k*bsd*-gnu) haveit=yes;;
+ esac
+ fi
+ fi
+ if test -z "$haveit"; then
+ haveit=
+ for x in $LDFLAGS $LIBINTL; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-L$additional_libdir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_libdir"; then
+ LIBINTL="${LIBINTL}${LIBINTL:+ }-L$additional_libdir"
+ fi
+ fi
+ haveit=
+ for x in $LDFLAGS $LTLIBINTL; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-L$additional_libdir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_libdir"; then
+ LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }-L$additional_libdir"
+ fi
+ fi
+ fi
+ fi
+ ;;
+ -R*)
+ dir=`echo "X$dep" | sed -e 's/^X-R//'`
+ if test "$enable_rpath" != no; then
+ haveit=
+ for x in $rpathdirs; do
+ if test "X$x" = "X$dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ rpathdirs="$rpathdirs $dir"
+ fi
+ haveit=
+ for x in $ltrpathdirs; do
+ if test "X$x" = "X$dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ ltrpathdirs="$ltrpathdirs $dir"
+ fi
+ fi
+ ;;
+ -l*)
+ names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'`
+ ;;
+ *.la)
+ names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'`
+ ;;
+ *)
+ LIBINTL="${LIBINTL}${LIBINTL:+ }$dep"
+ LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }$dep"
+ ;;
+ esac
+ done
+ fi
+ else
+ LIBINTL="${LIBINTL}${LIBINTL:+ }-l$name"
+ LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }-l$name"
+ fi
+ fi
+ fi
+ done
+ done
+ if test "X$rpathdirs" != "X"; then
+ if test -n "$hardcode_libdir_separator"; then
+ alldirs=
+ for found_dir in $rpathdirs; do
+ alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$found_dir"
+ done
+ acl_save_libdir="$libdir"
+ libdir="$alldirs"
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ libdir="$acl_save_libdir"
+ LIBINTL="${LIBINTL}${LIBINTL:+ }$flag"
+ else
+ for found_dir in $rpathdirs; do
+ acl_save_libdir="$libdir"
+ libdir="$found_dir"
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ libdir="$acl_save_libdir"
+ LIBINTL="${LIBINTL}${LIBINTL:+ }$flag"
+ done
+ fi
+ fi
+ if test "X$ltrpathdirs" != "X"; then
+ for found_dir in $ltrpathdirs; do
+ LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }-R$found_dir"
+ done
+ fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU gettext in libintl" >&5
+$as_echo_n "checking for GNU gettext in libintl... " >&6; }
+if eval \${$gt_func_gnugettext_libintl+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ gt_save_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $INCINTL"
+ gt_save_LIBS="$LIBS"
+ LIBS="$LIBS $LIBINTL"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <libintl.h>
+$gt_revision_test_code
+extern int _nl_msg_cat_cntr;
+extern
+#ifdef __cplusplus
+"C"
+#endif
+const char *_nl_expand_alias (const char *);
+int
+main ()
+{
+bindtextdomain ("", "");
+return * gettext ("")$gt_expression_test_code + _nl_msg_cat_cntr + *_nl_expand_alias ("")
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ eval "$gt_func_gnugettext_libintl=yes"
+else
+ eval "$gt_func_gnugettext_libintl=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" != yes; } && test -n "$LIBICONV"; then
+ LIBS="$LIBS $LIBICONV"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <libintl.h>
+$gt_revision_test_code
+extern int _nl_msg_cat_cntr;
+extern
+#ifdef __cplusplus
+"C"
+#endif
+const char *_nl_expand_alias (const char *);
+int
+main ()
+{
+bindtextdomain ("", "");
+return * gettext ("")$gt_expression_test_code + _nl_msg_cat_cntr + *_nl_expand_alias ("")
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ LIBINTL="$LIBINTL $LIBICONV"
+ LTLIBINTL="$LTLIBINTL $LTLIBICONV"
+ eval "$gt_func_gnugettext_libintl=yes"
+
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ fi
+ CPPFLAGS="$gt_save_CPPFLAGS"
+ LIBS="$gt_save_LIBS"
+fi
+eval ac_res=\$$gt_func_gnugettext_libintl
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ fi
+
+ if { eval "gt_val=\$$gt_func_gnugettext_libc"; test "$gt_val" = "yes"; } \
+ || { { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = "yes"; } \
+ && test "$PACKAGE" != gettext-runtime \
+ && test "$PACKAGE" != gettext-tools; }; then
+ gt_use_preinstalled_gnugettext=yes
+ else
+ LIBINTL=
+ LTLIBINTL=
+ INCINTL=
+ fi
+
+
+
+ if test -n "$INTL_MACOSX_LIBS"; then
+ if test "$gt_use_preinstalled_gnugettext" = "yes" \
+ || test "$nls_cv_use_gnu_gettext" = "yes"; then
+ LIBINTL="$LIBINTL $INTL_MACOSX_LIBS"
+ LTLIBINTL="$LTLIBINTL $INTL_MACOSX_LIBS"
+ fi
+ fi
+
+ if test "$gt_use_preinstalled_gnugettext" = "yes" \
+ || test "$nls_cv_use_gnu_gettext" = "yes"; then
+
+$as_echo "@%:@define ENABLE_NLS 1" >>confdefs.h
+
+ else
+ USE_NLS=no
+ fi
+ fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use NLS" >&5
+$as_echo_n "checking whether to use NLS... " >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $USE_NLS" >&5
+$as_echo "$USE_NLS" >&6; }
+ if test "$USE_NLS" = "yes"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking where the gettext function comes from" >&5
+$as_echo_n "checking where the gettext function comes from... " >&6; }
+ if test "$gt_use_preinstalled_gnugettext" = "yes"; then
+ if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = "yes"; }; then
+ gt_source="external libintl"
+ else
+ gt_source="libc"
+ fi
+ else
+ gt_source="included intl directory"
+ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gt_source" >&5
+$as_echo "$gt_source" >&6; }
+ fi
+
+ if test "$USE_NLS" = "yes"; then
+
+ if test "$gt_use_preinstalled_gnugettext" = "yes"; then
+ if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = "yes"; }; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link with libintl" >&5
+$as_echo_n "checking how to link with libintl... " >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIBINTL" >&5
+$as_echo "$LIBINTL" >&6; }
+
+ for element in $INCINTL; do
+ haveit=
+ for x in $CPPFLAGS; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X$element"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }$element"
+ fi
+ done
+
+ fi
+
+
+$as_echo "@%:@define HAVE_GETTEXT 1" >>confdefs.h
+
+
+$as_echo "@%:@define HAVE_DCGETTEXT 1" >>confdefs.h
+
+ fi
+
+ POSUB=po
+ fi
+
+
+
+ INTLLIBS="$LIBINTL"
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: dmalloc enabled" >&5
+$as_echo_n "checking option: dmalloc enabled... " >&6; }
+@%:@ Check whether --enable-dmalloc was given.
+if test "${enable_dmalloc+set}" = set; then :
+ enableval=$enable_dmalloc;
+fi
+
+if test x$enable_dmalloc = "xyes" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+@%:@ Check whether --with-dmalloc-dir was given.
+if test "${with_dmalloc_dir+set}" = set; then :
+ withval=$with_dmalloc_dir;
+ if test "x$withval" != "xno" ; then
+ enable_dmalloc = "yes"
+ CPPFLAGS="$CPPCFLAGS -I${withval}"
+ LDFLAGS="$LDFLAGS -L${withval}"
+ fi
+
+fi
+
+
+if test x$enable_dmalloc = "xyes" ; then
+
+$as_echo "@%:@define ENABLE_DMALLOC 1" >>confdefs.h
+
+fi
+
+localedir="\${datadir}/locale"
+
+@%:@ Check whether --with-localedir was given.
+if test "${with_localedir+set}" = set; then :
+ withval=$with_localedir;
+ case $withval in
+ yes)
+ ;;
+ no)
+ ;;
+ *)
+ localedir=$withval
+ ;;
+ esac
+
+fi
+
+localedir="$localedir"
+
+
+# Setup OS-Specific features
+case "$host" in
+ *darwin*)
+ @%:@ Check whether --enable-osx-universal-binaries was given.
+if test "${enable_osx_universal_binaries+set}" = set; then :
+ enableval=$enable_osx_universal_binaries;
+fi
+
+ if test "x$enable_osx_universal_binaries" = "xyes" ; then
+ if test "x$enable_dependency_tracking" != xno ; then
+ as_fn_error $? "--enable-osx-universal-binary requires --disable-dependency-tracking.
+Please re-run configure with these options:
+ --disable-dependency-tracking --enable-osx-universal-binary" "$LINENO" 5
+ fi
+ if test -d /Developer/SDKs/MacOSX10.5.sdk ; then
+ alpine_sysroot=/Developer/SDKs/MacOSX10.5.sdk
+ elif test -d /Developer/SDKs/MacOSX10.4u.sdk ; then
+ alpine_sysroot=/Developer/SDKs/MacOSX10.4u.sdk
+ else
+ as_fn_error $? "No suitable MacOSX SDK found. Make sure Xcode tools are installed" "$LINENO" 5
+ fi
+ ub_cflags="-isysroot $alpine_sysroot -arch ppc -arch i386"
+ ub_ldflags="-Wl,-syslibroot,$alpine_sysroot -arch ppc -arch i386"
+ AM_CFLAGS="$AM_CFLAGS $ub_cflags"
+ AM_LDFLAGS="$AM_LDFLAGS $ub_ldflags"
+ alpine_c_client_cflags="$alpine_c_client_cflags $ub_cflags"
+ alpine_c_client_ldflags="$alpine_c_client_ldflags $ub_ldflags"
+ fi
+ ;;
+esac
+
+
+@%:@ Check whether --with-include-path was given.
+if test "${with_include_path+set}" = set; then :
+ withval=$with_include_path;
+ case $withval in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+ new_cppflags="-I`echo ${withval} | ${SED} 's/:/ -I/g'`"
+ CPPFLAGS="$CPPFLAGS ${new_cppflags}"
+ alpine_c_client_cflags="$alpine_c_client_cflags ${new_cppflags}"
+ ;;
+ esac
+
+fi
+
+
+
+@%:@ Check whether --with-lib-path was given.
+if test "${with_lib_path+set}" = set; then :
+ withval=$with_lib_path;
+ case $withval in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+ new_ldflags="-L`echo ${withval} | ${SED} 's/:/ -L/g'`"
+ LDFLAGS="$LDFLAGS $new_ldflags"
+ alpine_c_client_ldflags="$alpine_c_client_ldflags ${new_ldflags}"
+ ;;
+ esac
+
+fi
+
+
+
+@%:@ Check whether --with-pubcookie was given.
+if test "${with_pubcookie+set}" = set; then :
+ withval=$with_pubcookie;
+ if test "x$withval" != "xno" ; then
+ WEB_PUBCOOKIE_BUILD=web/src/pubcookie
+ fi
+
+fi
+
+
+
+
+@%:@ Check whether --with-web-bin was given.
+if test "${with_web_bin+set}" = set; then :
+ withval=$with_web_bin;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+ WEB_BINDIR=$withval
+ ;;
+ esac
+
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: debugging is enabled" >&5
+$as_echo_n "checking option: debugging is enabled... " >&6; }
+@%:@ Check whether --enable-debug was given.
+if test "${enable_debug+set}" = set; then :
+ enableval=$enable_debug;
+fi
+
+if test x$enable_debug != "xno" ; then
+ AM_CFLAGS="$AM_CFLAGS -g"
+
+$as_echo "@%:@define DEBUG 1" >>confdefs.h
+
+
+$as_echo "@%:@define DEBUGJOURNAL 1" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: optimization is enabled" >&5
+$as_echo_n "checking option: optimization is enabled... " >&6; }
+@%:@ Check whether --enable-optimization was given.
+if test "${enable_optimization+set}" = set; then :
+ enableval=$enable_optimization;
+fi
+
+if test x$enable_optimization != "xno" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ CFLAGS="`echo $AM_CFLAGS | ${SED} 's/-O2//'`"
+ alpine_c_client_gccoptlevel="-O0"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: mouse support enabled" >&5
+$as_echo_n "checking option: mouse support enabled... " >&6; }
+@%:@ Check whether --enable-mouse was given.
+if test "${enable_mouse+set}" = set; then :
+ enableval=$enable_mouse;
+fi
+
+if test x$enable_mouse != "xno" ; then
+
+$as_echo "@%:@define MOUSE /**/" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: quotas enabled" >&5
+$as_echo_n "checking option: quotas enabled... " >&6; }
+@%:@ Check whether --enable-quotas was given.
+if test "${enable_quotas+set}" = set; then :
+ enableval=$enable_quotas;
+fi
+
+if test x$enable_quotas = "xyes" ; then
+
+$as_echo "@%:@define USE_QUOTAS /**/" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: From changing enabled" >&5
+$as_echo_n "checking option: From changing enabled... " >&6; }
+@%:@ Check whether --enable-from_changing was given.
+if test "${enable_from_changing+set}" = set; then :
+ enableval=$enable_from_changing;
+fi
+
+if test x$enable_from_changing != "xno" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+
+$as_echo "@%:@define NEVER_ALLOW_CHANGING_FROM /**/" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: background post enabled" >&5
+$as_echo_n "checking option: background post enabled... " >&6; }
+@%:@ Check whether --enable-background-post was given.
+if test "${enable_background_post+set}" = set; then :
+ enableval=$enable_background_post;
+fi
+
+if test x$enable_background_post != "xno" ; then
+
+$as_echo "@%:@define BACKGROUND_POST /**/" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: keyboard lock enabled" >&5
+$as_echo_n "checking option: keyboard lock enabled... " >&6; }
+@%:@ Check whether --enable-keyboard-lock was given.
+if test "${enable_keyboard_lock+set}" = set; then :
+ enableval=$enable_keyboard_lock;
+fi
+
+if test x$enable_keyboard_lock != "xno" ; then
+
+$as_echo "@%:@define KEYBOARD_LOCK /**/" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: from encoding enabled" >&5
+$as_echo_n "checking option: from encoding enabled... " >&6; }
+@%:@ Check whether --enable-from-encoding was given.
+if test "${enable_from_encoding+set}" = set; then :
+ enableval=$enable_from_encoding;
+fi
+
+if test x$enable_from_encoding = "xyes" ; then
+
+$as_echo "@%:@define ENCODE_FROMS /**/" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+@%:@ Check whether --with-smtp-msa was given.
+if test "${with_smtp_msa+set}" = set; then :
+ withval=$with_smtp_msa;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ # Extract the first word of "sendmail", so it can be a program name with args.
+set dummy sendmail; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_SENDMAIL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $SENDMAIL in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_SENDMAIL="$SENDMAIL" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_dummy="$PATH:/usr/sbin:/usr/lib"
+for as_dir in $as_dummy
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_SENDMAIL="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_SENDMAIL" && ac_cv_path_SENDMAIL=""""
+ ;;
+esac
+fi
+SENDMAIL=$ac_cv_path_SENDMAIL
+if test -n "$SENDMAIL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SENDMAIL" >&5
+$as_echo "$SENDMAIL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ ;;
+ *)
+ SENDMAIL=$withval
+ ;;
+ esac
+
+else
+
+ # Extract the first word of "sendmail", so it can be a program name with args.
+set dummy sendmail; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_SENDMAIL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $SENDMAIL in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_SENDMAIL="$SENDMAIL" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_dummy="$PATH:/usr/sbin:/usr/lib"
+for as_dir in $as_dummy
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_SENDMAIL="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_SENDMAIL" && ac_cv_path_SENDMAIL=""""
+ ;;
+esac
+fi
+SENDMAIL=$ac_cv_path_SENDMAIL
+if test -n "$SENDMAIL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SENDMAIL" >&5
+$as_echo "$SENDMAIL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+fi
+
+if test -n "$SENDMAIL" ; then
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SENDMAIL "$SENDMAIL"
+_ACEOF
+
+fi
+
+smtp_msa_flags="-bs -odb -oem"
+
+@%:@ Check whether --with-smtp-msa-flags was given.
+if test "${with_smtp_msa_flags+set}" = set; then :
+ withval=$with_smtp_msa_flags;
+ if test "x$withval" != "xno" ; then
+ smtp_msa_flags=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SENDMAILFLAGS "$smtp_msa_flags"
+_ACEOF
+
+
+npa="inews"
+
+@%:@ Check whether --with-npa was given.
+if test "${with_npa+set}" = set; then :
+ withval=$with_npa;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ # Extract the first word of "inews", so it can be a program name with args.
+set dummy inews; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_NPA_PROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $NPA_PROG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_NPA_PROG="$NPA_PROG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_dummy="$PATH:/usr/sbin:/usr/lib"
+for as_dir in $as_dummy
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_NPA_PROG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_NPA_PROG" && ac_cv_path_NPA_PROG=""""
+ ;;
+esac
+fi
+NPA_PROG=$ac_cv_path_NPA_PROG
+if test -n "$NPA_PROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NPA_PROG" >&5
+$as_echo "$NPA_PROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ ;;
+ *)
+ NPA_PROG=$withval
+ ;;
+ esac
+
+else
+
+ # Extract the first word of "inews", so it can be a program name with args.
+set dummy inews; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_NPA_PROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $NPA_PROG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_NPA_PROG="$NPA_PROG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_dummy="$PATH:/usr/sbin:/usr/lib"
+for as_dir in $as_dummy
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_NPA_PROG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_NPA_PROG" && ac_cv_path_NPA_PROG=""""
+ ;;
+esac
+fi
+NPA_PROG=$ac_cv_path_NPA_PROG
+if test -n "$NPA_PROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NPA_PROG" >&5
+$as_echo "$NPA_PROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+fi
+
+
+npa_flags="-h"
+
+@%:@ Check whether --with-npa-flags was given.
+if test "${with_npa_flags+set}" = set; then :
+ withval=$with_npa_flags;
+ if test "x$withval" != "xno" ; then
+ npa_flags=$withval
+ fi
+
+fi
+
+if test -n "$NPA_PROG" ; then
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SENDNEWS "$NPA_PROG $npa_flags"
+_ACEOF
+
+fi
+
+
+@%:@ Check whether --with-password-prog was given.
+if test "${with_password_prog+set}" = set; then :
+ withval=$with_password_prog;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ # Extract the first word of "passwd", so it can be a program name with args.
+set dummy passwd; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_PWPROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $PWPROG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_PWPROG="$PWPROG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_dummy="$PATH:/usr/sbin:/usr/lib"
+for as_dir in $as_dummy
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_PWPROG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_PWPROG" && ac_cv_path_PWPROG=""""
+ ;;
+esac
+fi
+PWPROG=$ac_cv_path_PWPROG
+if test -n "$PWPROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PWPROG" >&5
+$as_echo "$PWPROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ ;;
+ *)
+ # Extract the first word of "$withval", so it can be a program name with args.
+set dummy $withval; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_PWPROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $PWPROG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_PWPROG="$PWPROG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_dummy="$PATH:/usr/sbin:/usr/lib"
+for as_dir in $as_dummy
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_PWPROG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_PWPROG" && ac_cv_path_PWPROG=""""
+ ;;
+esac
+fi
+PWPROG=$ac_cv_path_PWPROG
+if test -n "$PWPROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PWPROG" >&5
+$as_echo "$PWPROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ ;;
+ esac
+
+else
+
+ # Extract the first word of "passwd", so it can be a program name with args.
+set dummy passwd; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_PWPROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $PWPROG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_PWPROG="$PWPROG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_dummy="$PATH:/usr/sbin:/usr/lib"
+for as_dir in $as_dummy
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_PWPROG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_PWPROG" && ac_cv_path_PWPROG=""""
+ ;;
+esac
+fi
+PWPROG=$ac_cv_path_PWPROG
+if test -n "$PWPROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PWPROG" >&5
+$as_echo "$PWPROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+fi
+
+if test -n "$PWPROG" ; then
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PASSWD_PROG "$PWPROG"
+_ACEOF
+
+fi
+
+
+@%:@ Check whether --with-simple-spellcheck was given.
+if test "${with_simple_spellcheck+set}" = set; then :
+ withval=$with_simple_spellcheck;
+ if test "x$withval" != "xno" ; then
+ SPELLPROG=$withval
+ fi
+
+else
+
+ # Extract the first word of "hunspell", so it can be a program name with args.
+set dummy hunspell; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_SPELLPROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$SPELLPROG"; then
+ ac_cv_prog_SPELLPROG="$SPELLPROG" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_SPELLPROG="hunspell"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+SPELLPROG=$ac_cv_prog_SPELLPROG
+if test -n "$SPELLPROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SPELLPROG" >&5
+$as_echo "$SPELLPROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test -z "$SPELLPROG" ; then
+ # Extract the first word of "aspell", so it can be a program name with args.
+set dummy aspell; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_SPELLPROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$SPELLPROG"; then
+ ac_cv_prog_SPELLPROG="$SPELLPROG" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_SPELLPROG="aspell"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+SPELLPROG=$ac_cv_prog_SPELLPROG
+if test -n "$SPELLPROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SPELLPROG" >&5
+$as_echo "$SPELLPROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test -z "$SPELLPROG" ; then
+ # Extract the first word of "ispell", so it can be a program name with args.
+set dummy ispell; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_SPELLPROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$SPELLPROG"; then
+ ac_cv_prog_SPELLPROG="$SPELLPROG" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_SPELLPROG="ispell"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+SPELLPROG=$ac_cv_prog_SPELLPROG
+if test -n "$SPELLPROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SPELLPROG" >&5
+$as_echo "$SPELLPROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test -z "$SPELLPROG" ; then
+ SPELLPROG="spell"
+ fi
+ fi
+ fi
+
+fi
+
+
+if test "x$SPELLPROG" != "xno" ; then
+ # Extract the first word of "$SPELLPROG", so it can be a program name with args.
+set dummy $SPELLPROG; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_alpine_simple_spellcheck+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $alpine_simple_spellcheck in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_alpine_simple_spellcheck="$alpine_simple_spellcheck" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_alpine_simple_spellcheck="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+alpine_simple_spellcheck=$ac_cv_path_alpine_simple_spellcheck
+if test -n "$alpine_simple_spellcheck"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $alpine_simple_spellcheck" >&5
+$as_echo "$alpine_simple_spellcheck" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test -n "$alpine_simple_spellcheck" ; then
+ case "$SPELLPROG" in
+ hunspell)
+ alpine_simple_spellcheck="$alpine_simple_spellcheck -l"
+ ;;
+ aspell)
+ alpine_simple_spellcheck="$alpine_simple_spellcheck --dont-backup --mode=email list"
+ ;;
+ ispell)
+ alpine_simple_spellcheck="$alpine_simple_spellcheck -l"
+ ;;
+ *)
+ ;;
+ esac
+ fi
+fi
+
+
+@%:@ Check whether --with-interactive-spellcheck was given.
+if test "${with_interactive_spellcheck+set}" = set; then :
+ withval=$with_interactive_spellcheck;
+ if test "x$withval" != "xno" ; then
+ ISPELLPROG=$withval
+ fi
+
+else
+
+ # Extract the first word of "hunspell", so it can be a program name with args.
+set dummy hunspell; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ISPELLPROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ISPELLPROG"; then
+ ac_cv_prog_ISPELLPROG="$ISPELLPROG" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ISPELLPROG="hunspell"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ISPELLPROG=$ac_cv_prog_ISPELLPROG
+if test -n "$ISPELLPROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ISPELLPROG" >&5
+$as_echo "$ISPELLPROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test -z "$ISPELLPROG" ; then
+ # Extract the first word of "aspell", so it can be a program name with args.
+set dummy aspell; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ISPELLPROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ISPELLPROG"; then
+ ac_cv_prog_ISPELLPROG="$ISPELLPROG" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ISPELLPROG="aspell"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ISPELLPROG=$ac_cv_prog_ISPELLPROG
+if test -n "$ISPELLPROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ISPELLPROG" >&5
+$as_echo "$ISPELLPROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test -z "$SPELLPROG" ; then
+ ISPELLPROG="ispell"
+ fi
+ fi
+
+fi
+
+
+if test "x$ISPELLPROG" != "xno" ; then
+ # Extract the first word of "$ISPELLPROG", so it can be a program name with args.
+set dummy $ISPELLPROG; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_alpine_interactive_spellcheck+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $alpine_interactive_spellcheck in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_alpine_interactive_spellcheck="$alpine_interactive_spellcheck" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_alpine_interactive_spellcheck="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+alpine_interactive_spellcheck=$ac_cv_path_alpine_interactive_spellcheck
+if test -n "$alpine_interactive_spellcheck"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $alpine_interactive_spellcheck" >&5
+$as_echo "$alpine_interactive_spellcheck" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test -n "$alpine_interactive_spellcheck" ; then
+ case "$ISPELLPROG" in
+ aspell)
+ alpine_interactive_spellcheck="$alpine_interactive_spellcheck --dont-backup --mode=email check"
+ ;;
+ *)
+ ;;
+ esac
+ fi
+fi
+
+if test -n "$alpine_interactive_spellcheck" ; then
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_VAR_SPELLER "$alpine_interactive_spellcheck"
+_ACEOF
+
+fi
+
+if test -z "$alpine_simple_spellcheck" -a -n "$alpine_interactive_spellcheck" ; then
+ alpine_simple_spellcheck=test
+fi
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SPELLER "$alpine_simple_spellcheck"
+_ACEOF
+
+
+case "$prefix" in
+ NONE) dpv=/usr/local/lib/pine.conf ;;
+ *) dpv=${prefix}/lib/pine.conf ;;
+esac
+
+@%:@ Check whether --with-system-pinerc was given.
+if test "${with_system_pinerc+set}" = set; then :
+ withval=$with_system_pinerc;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+ dpv=$withval
+ ;;
+ esac
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SYSTEM_PINERC "$dpv"
+_ACEOF
+
+
+case "$prefix" in
+ NONE) dpv=/usr/local/lib/pine.conf.fixed ;;
+ *) dpv=${prefix}/lib/pine.conf.fixed ;;
+esac
+
+@%:@ Check whether --with-system-fixed-pinerc was given.
+if test "${with_system_fixed_pinerc+set}" = set; then :
+ withval=$with_system_fixed_pinerc;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+ dpv=$withval
+ ;;
+ esac
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SYSTEM_PINERC_FIXED "$dpv"
+_ACEOF
+
+
+
+
+
+
+
+ dpv=150
+
+@%:@ Check whether --with-mailcheck-interval was given.
+if test "${with_mailcheck_interval+set}" = set; then :
+ withval=$with_mailcheck_interval;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_MAILCHECK "$dpv"
+_ACEOF
+
+
+
+ dpv=420
+
+@%:@ Check whether --with-checkpoint-interval was given.
+if test "${with_checkpoint_interval+set}" = set; then :
+ withval=$with_checkpoint_interval;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define CHECK_POINT_TIME $dpv
+_ACEOF
+
+
+
+ dpv=12
+
+@%:@ Check whether --with-checkpoint-frequency was given.
+if test "${with_checkpoint_frequency+set}" = set; then :
+ withval=$with_checkpoint_frequency;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define CHECK_POINT_FREQ $dpv
+_ACEOF
+
+
+
+ dpv=24
+
+@%:@ Check whether --with-display-rows was given.
+if test "${with_display_rows+set}" = set; then :
+ withval=$with_display_rows;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DEFAULT_LINES_ON_TERMINAL $dpv
+_ACEOF
+
+
+
+ dpv=80
+
+@%:@ Check whether --with-display-columns was given.
+if test "${with_display_columns+set}" = set; then :
+ withval=$with_display_columns;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DEFAULT_COLUMNS_ON_TERMINAL $dpv
+_ACEOF
+
+
+
+ dpv=200
+
+@%:@ Check whether --with-max-display-rows was given.
+if test "${with_max_display_rows+set}" = set; then :
+ withval=$with_max_display_rows;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define MAX_SCREEN_ROWS $dpv
+_ACEOF
+
+
+
+ dpv=500
+
+@%:@ Check whether --with-max-display-columns was given.
+if test "${with_max_display_columns+set}" = set; then :
+ withval=$with_max_display_columns;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define MAX_SCREEN_COLS $dpv
+_ACEOF
+
+
+
+ dpv=74
+
+@%:@ Check whether --with-fill-column was given.
+if test "${with_fill_column+set}" = set; then :
+ withval=$with_fill_column;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_FILLCOL "$dpv"
+_ACEOF
+
+
+
+ dpv=80
+
+@%:@ Check whether --with-max_fill-column was given.
+if test "${with_max_fill_column+set}" = set; then :
+ withval=$with_max_fill_column;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define MAX_FILLCOL $dpv
+_ACEOF
+
+
+
+ dpv=2
+
+@%:@ Check whether --with-debug-level was given.
+if test "${with_debug_level+set}" = set; then :
+ withval=$with_debug_level;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DEFAULT_DEBUG $dpv
+_ACEOF
+
+
+
+ dpv=4
+
+@%:@ Check whether --with-debug-files was given.
+if test "${with_debug_files+set}" = set; then :
+ withval=$with_debug_files;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define NUMDEBUGFILES $dpv
+_ACEOF
+
+
+
+ dpv=.pine-debug
+
+@%:@ Check whether --with-debug-file was given.
+if test "${with_debug_file+set}" = set; then :
+ withval=$with_debug_file;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DEBUGFILE "$dpv"
+_ACEOF
+
+
+
+ dpv="\$Forwarded"
+
+@%:@ Check whether --with-forwarded-keyword was given.
+if test "${with_forwarded_keyword+set}" = set; then :
+ withval=$with_forwarded_keyword;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define FORWARDED_FLAG "$dpv"
+_ACEOF
+
+
+
+ dpv=2
+
+@%:@ Check whether --with-display-overlap was given.
+if test "${with_display_overlap+set}" = set; then :
+ withval=$with_display_overlap;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_OVERLAP "$dpv"
+_ACEOF
+
+
+
+ dpv=0
+
+@%:@ Check whether --with-display-margin was given.
+if test "${with_display_margin+set}" = set; then :
+ withval=$with_display_margin;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_MARGIN "$dpv"
+_ACEOF
+
+
+
+ dpv=sent-mail
+
+@%:@ Check whether --with-default-fcc was given.
+if test "${with_default_fcc+set}" = set; then :
+ withval=$with_default_fcc;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_DEFAULT_FCC "$dpv"
+_ACEOF
+
+
+
+ dpv=saved-messages
+
+@%:@ Check whether --with-default-save-folder was given.
+if test "${with_default_save_folder+set}" = set; then :
+ withval=$with_default_save_folder;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DEFAULT_SAVE "$dpv"
+_ACEOF
+
+
+
+ dpv=postponed-mail
+
+@%:@ Check whether --with-default-legacy-postponed-folder was given.
+if test "${with_default_legacy_postponed_folder+set}" = set; then :
+ withval=$with_default_legacy_postponed_folder;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define POSTPONED_MAIL "$dpv"
+_ACEOF
+
+
+
+ dpv=postponed-msgs
+
+@%:@ Check whether --with-default-postponed-folder was given.
+if test "${with_default_postponed_folder+set}" = set; then :
+ withval=$with_default_postponed_folder;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define POSTPONED_MSGS "$dpv"
+_ACEOF
+
+
+
+ dpv=Trash
+
+@%:@ Check whether --with-default-trash-folder was given.
+if test "${with_default_trash_folder+set}" = set; then :
+ withval=$with_default_trash_folder;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define TRASH_FOLDER "$dpv"
+_ACEOF
+
+
+
+ dpv=.pine-interrupted-mail
+
+@%:@ Check whether --with-default-interrupted-mail was given.
+if test "${with_default_interrupted_mail+set}" = set; then :
+ withval=$with_default_interrupted_mail;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define INTERRUPTED_MAIL "$dpv"
+_ACEOF
+
+
+
+ dpv=dead.letter
+
+@%:@ Check whether --with-default-dead-letter-folder was given.
+if test "${with_default_dead_letter_folder+set}" = set; then :
+ withval=$with_default_dead_letter_folder;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DEADLETTER "$dpv"
+_ACEOF
+
+
+
+ dpv=mail
+
+@%:@ Check whether --with-default-mail-directory was given.
+if test "${with_default_mail_directory+set}" = set; then :
+ withval=$with_default_mail_directory;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_MAIL_DIRECTORY "$dpv"
+_ACEOF
+
+
+
+ dpv=INBOX
+
+@%:@ Check whether --with-default-inbox-name was given.
+if test "${with_default_inbox_name+set}" = set; then :
+ withval=$with_default_inbox_name;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define INBOX_NAME "$dpv"
+_ACEOF
+
+
+
+ dpv=.signature
+
+@%:@ Check whether --with-default-signature-file was given.
+if test "${with_default_signature_file+set}" = set; then :
+ withval=$with_default_signature_file;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_SIGNATURE_FILE "$dpv"
+_ACEOF
+
+
+
+ dpv=no
+
+@%:@ Check whether --with-default-elm-style-save was given.
+if test "${with_default_elm_style_save+set}" = set; then :
+ withval=$with_default_elm_style_save;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_ELM_STYLE_SAVE "$dpv"
+_ACEOF
+
+
+
+ dpv=no
+
+@%:@ Check whether --with-default-header-in-reply was given.
+if test "${with_default_header_in_reply+set}" = set; then :
+ withval=$with_default_header_in_reply;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_HEADER_IN_REPLY "$dpv"
+_ACEOF
+
+
+
+ dpv=no
+
+@%:@ Check whether --with-default-old-style-reply was given.
+if test "${with_default_old_style_reply+set}" = set; then :
+ withval=$with_default_old_style_reply;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_OLD_STYLE_REPLY "$dpv"
+_ACEOF
+
+
+
+ dpv=no
+
+@%:@ Check whether --with-default-use-only-domain-name was given.
+if test "${with_default_use_only_domain_name+set}" = set; then :
+ withval=$with_default_use_only_domain_name;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_USE_ONLY_DOMAIN_NAME "$dpv"
+_ACEOF
+
+
+
+ dpv=no
+
+@%:@ Check whether --with-default-save-by-sender was given.
+if test "${with_default_save_by_sender+set}" = set; then :
+ withval=$with_default_save_by_sender;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_SAVE_BY_SENDER "$dpv"
+_ACEOF
+
+
+
+ dpv=arrival
+
+@%:@ Check whether --with-default-sort-key was given.
+if test "${with_default_sort_key+set}" = set; then :
+ withval=$with_default_sort_key;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_SORT_KEY "$dpv"
+_ACEOF
+
+
+
+ dpv=fullname-with-lists-last
+
+@%:@ Check whether --with-default-addressbook-sort-rule was given.
+if test "${with_default_addressbook_sort_rule+set}" = set; then :
+ withval=$with_default_addressbook_sort_rule;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_AB_SORT_RULE "$dpv"
+_ACEOF
+
+
+
+ dpv=alphabetical
+
+@%:@ Check whether --with-default-folder-sort-rule was given.
+if test "${with_default_folder_sort_rule+set}" = set; then :
+ withval=$with_default_folder_sort_rule;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_FLD_SORT_RULE "$dpv"
+_ACEOF
+
+
+
+ dpv=default-folder
+
+@%:@ Check whether --with-default-saved-message-name-rule was given.
+if test "${with_default_saved_message_name_rule+set}" = set; then :
+ withval=$with_default_saved_message_name_rule;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_SAVED_MSG_NAME_RULE "$dpv"
+_ACEOF
+
+
+
+ dpv=default-fcc
+
+@%:@ Check whether --with-default-fcc-rule was given.
+if test "${with_default_fcc_rule+set}" = set; then :
+ withval=$with_default_fcc_rule;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_FCC_RULE "$dpv"
+_ACEOF
+
+
+
+ dpv=lpr
+
+@%:@ Check whether --with-default-standard-printer was given.
+if test "${with_default_standard_printer+set}" = set; then :
+ withval=$with_default_standard_printer;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_STANDARD_PRINTER "$dpv"
+_ACEOF
+
+
+
+ dpv=attached-to-ansi
+
+@%:@ Check whether --with-default-ansi-printer was given.
+if test "${with_default_ansi_printer+set}" = set; then :
+ withval=$with_default_ansi_printer;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define ANSI_PRINTER "$dpv"
+_ACEOF
+
+
+
+ dpv=.addressbook
+
+@%:@ Check whether --with-default-addressbook was given.
+if test "${with_default_addressbook+set}" = set; then :
+ withval=$with_default_addressbook;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_ADDRESSBOOK "$dpv"
+_ACEOF
+
+
+
+ dpv="Local Support"
+
+@%:@ Check whether --with-default-local-fullname was given.
+if test "${with_default_local_fullname+set}" = set; then :
+ withval=$with_default_local_fullname;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_LOCAL_FULLNAME "$dpv"
+_ACEOF
+
+
+
+ dpv=postmaster
+
+@%:@ Check whether --with-default-local-address was given.
+if test "${with_default_local_address+set}" = set; then :
+ withval=$with_default_local_address;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_LOCAL_ADDRESS "$dpv"
+_ACEOF
+
+
+
+ dpv=1
+
+@%:@ Check whether --with-default-keyboard-lock-count was given.
+if test "${with_default_keyboard_lock_count+set}" = set; then :
+ withval=$with_default_keyboard_lock_count;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_KBLOCK_PASSWD_COUNT "$dpv"
+_ACEOF
+
+
+
+ dpv=3
+
+@%:@ Check whether --with-default-remote-addressbook-history was given.
+if test "${with_default_remote_addressbook_history+set}" = set; then :
+ withval=$with_default_remote_addressbook_history;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_REMOTE_ABOOK_HISTORY "$dpv"
+_ACEOF
+
+
+
+ dpv=.alpine-smime/public
+
+@%:@ Check whether --with-smime-public-cert-directory was given.
+if test "${with_smime_public_cert_directory+set}" = set; then :
+ withval=$with_smime_public_cert_directory;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_PUBLICCERT_DIR "$dpv"
+_ACEOF
+
+
+
+ dpv=.alpine-smime/private
+
+@%:@ Check whether --with-smime-private-key-directory was given.
+if test "${with_smime_private_key_directory+set}" = set; then :
+ withval=$with_smime_private_key_directory;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_PRIVATEKEY_DIR "$dpv"
+_ACEOF
+
+
+
+ dpv=.alpine-smime/ca
+
+@%:@ Check whether --with-smime-cacert-directory was given.
+if test "${with_smime_cacert_directory+set}" = set; then :
+ withval=$with_smime_cacert_directory;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_CACERT_DIR "$dpv"
+_ACEOF
+
+
+
+ dpv=ANSI_PRINTER
+
+@%:@ Check whether --with-default-printer was given.
+if test "${with_default_printer+set}" = set; then :
+ withval=$with_default_printer;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_DEFAULT_PRINTER $dpv
+_ACEOF
+
+
+
+
+@%:@ Check whether --with-passfile was given.
+if test "${with_passfile+set}" = set; then :
+ withval=$with_passfile;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+ alpine_PASSFILE=$withval
+ ;;
+ esac
+
+fi
+
+
+
+@%:@ Check whether --with-local-password-cache was given.
+if test "${with_local_password_cache+set}" = set; then :
+ withval=$with_local_password_cache;
+ alpine_os_credential_cache=$withval
+
+fi
+
+
+
+@%:@ Check whether --with-local-password-cache-method was given.
+if test "${with_local_password_cache_method+set}" = set; then :
+ withval=$with_local_password_cache_method;
+ alpine_os_credential_cache_method=$withval
+
+fi
+
+
+if test -n "$alpine_PASSFILE" ; then
+ case $alpine_os_credential_cache in
+ no)
+ ;;
+ *)
+ alpine_os_credential_cache="no"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: --with-passfile definition overrides OS-Specific password caching" >&5
+$as_echo "$as_me: --with-passfile definition overrides OS-Specific password caching" >&6;}
+ ;;
+ esac
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PASSFILE "$alpine_PASSFILE"
+_ACEOF
+
+fi
+
+
+@%:@ Check whether --with-default-sshpath was given.
+if test "${with_default_sshpath+set}" = set; then :
+ withval=$with_default_sshpath;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_SSHPATH "$withval"
+_ACEOF
+
+ ;;
+ esac
+
+fi
+
+
+
+@%:@ Check whether --with-default-sshcmd was given.
+if test "${with_default_sshcmd+set}" = set; then :
+ withval=$with_default_sshcmd;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_SSHCMD "$withval"
+_ACEOF
+
+ ;;
+ esac
+
+fi
+
+
+
+@%:@ Check whether --with-ssl was given.
+if test "${with_ssl+set}" = set; then :
+ withval=$with_ssl; with_ssl=$withval
+fi
+
+
+if test "x$with_ssl" = "xno" ; then
+ alpine_SSLTYPE="none"
+else
+ case $host in
+ *-linux-gnu)
+ if test -f /etc/fedora-release -o -f /etc/redhat-release -o -f /etc/redhat_version ; then
+ alpine_SSLTYPE="nopwd"
+ if test -d /etc/pki/tls ; then
+ alpine_SSLDIR="/etc/pki/tls"
+ else
+ alpine_SSLDIR="/usr/share/ssl"
+ fi
+ elif test -f /etc/SuSE-release ; then
+ alpine_SSLTYPE="nopwd"
+ alpine_SSLDIR="/usr/share/ssl"
+ alpine_SSLCERTS="/etc/ssl/certs"
+ elif test -d /etc/osso-af-init ; then
+ alpine_SSLTYPE="nopwd"
+ alpine_SSLDIR="/usr"
+ alpine_SSLCERTS="/usr/share/certs"
+ else
+ alpine_SSLTYPE="nopwd"
+ alpine_SSLDIR="/usr"
+ alpine_SSLCERTS="/etc/ssl/certs"
+ fi
+ ;;
+ *-apple-darwin*)
+ alpine_SSLTYPE="nopwd"
+ alpine_SSLCERTS="/System/Library/OpenSSL/certs"
+ ;;
+ *-openbsd*)
+ alpine_SSLTYPE="nopwd"
+ alpine_SSLDIR="/usr"
+ alpine_SSLCERTS="/etc/ssl/certs"
+ ;;
+ *-sco-sysv* | *-sysv*UnixWare | *-sysv*OpenUNIX)
+ alpine_SSLTYPE="sco.nopwd"
+ alpine_SSLDIR=/usr/local/ssl
+ ;;
+ *-*-solaris*)
+ if test -d /usr/sfw/include/openssl ; then
+ alpine_SSLDIR="/usr/sfw"
+ elif test -d /opt/csw/include/openssl ; then
+ alpine_SSLDIR="/opt/csw"
+ if test -d /opt/csw/ssl/certs ; then
+ alpine_SSLCERTS="/opt/csw/ssl/certs"
+ fi
+ fi
+ if test -z "$alpine_SSLCERTS" -a -d /etc/certs ; then
+ alpine_SSLCERTS="/etc/certs"
+ fi
+ ;;
+ *)
+ alpine_SSLTYPE="nopwd"
+ alpine_SSLDIR=/usr/local/ssl
+ ;;
+ esac
+
+
+@%:@ Check whether --with-ssl-dir was given.
+if test "${with_ssl_dir+set}" = set; then :
+ withval=$with_ssl_dir;
+ if test "x$withval" != "xno" ; then
+ alpine_SSLDIR=$withval
+ fi
+
+fi
+
+
+
+@%:@ Check whether --with-ssl-certs-dir was given.
+if test "${with_ssl_certs_dir+set}" = set; then :
+ withval=$with_ssl_certs_dir;
+ if test "x$withval" != "xno" ; then
+ alpine_SSLCERTS=$withval
+ fi
+
+fi
+
+
+
+@%:@ Check whether --with-ssl-include-dir was given.
+if test "${with_ssl_include_dir+set}" = set; then :
+ withval=$with_ssl_include_dir;
+ if test "x$withval" != "xno" ; then
+ alpine_SSLINCLUDE=$withval
+ fi
+
+fi
+
+
+
+@%:@ Check whether --with-ssl-lib-dir was given.
+if test "${with_ssl_lib_dir+set}" = set; then :
+ withval=$with_ssl_lib_dir;
+ if test "x$withval" != "xno" ; then
+ alpine_SSLLIB=$withval
+ fi
+
+fi
+
+ if test -n "$alpine_SSLINCLUDE" ; then
+ CPPCFLAGS="-I$alpine_SSLINCLUDE $CPPFLAGS"
+ elif test -n "$alpine_SSLDIR" ; then
+ CPPFLAGS="-I${alpine_SSLDIR}/include $CPPFLAGS"
+ fi
+ if test -n "$alpine_SSLLIB" ; then
+ LDFLAGS="-L$alpine_SSLLIB $LDFLAGS"
+ elif test -n "$alpine_SSLDIR" ; then
+ LDFLAGS="-L${alpine_SSLDIR}/lib $LDFLAGS"
+ fi
+fi
+
+
+@%:@ Check whether --with-krb5 was given.
+if test "${with_krb5+set}" = set; then :
+ withval=$with_krb5; with_krb5=$withval
+fi
+
+
+if test "x$with_krb5" = "xno" ; then
+ alpine_GSSTYPE="none"
+else
+ alpine_GSSTYPE=
+
+
+@%:@ Check whether --with-krb5-dir was given.
+if test "${with_krb5_dir+set}" = set; then :
+ withval=$with_krb5_dir;
+ if test "x$withval" != "xno" ; then
+ CPPFLAGS="$CPPFLAGS -I${withval}/include"
+ LDFLAGS="$LDFLAGS -L${withval}/lib"
+ fi
+
+fi
+
+
+
+@%:@ Check whether --with-krb5-include-dir was given.
+if test "${with_krb5_include_dir+set}" = set; then :
+ withval=$with_krb5_include_dir;
+ if test "x$withval" != "xno" ; then
+ CPPFLAGS="$CPPFLAGS -I$withval"
+ fi
+
+fi
+
+
+
+@%:@ Check whether --with-krb5-lib-dir was given.
+if test "${with_krb5_lib_dir+set}" = set; then :
+ withval=$with_krb5_lib_dir;
+ if test "x$withval" != "xno" ; then
+ LDFLAGS="$LDFLAGS -L$withval"
+ fi
+
+fi
+
+fi
+
+
+@%:@ Check whether --with-ldap was given.
+if test "${with_ldap+set}" = set; then :
+ withval=$with_ldap; with_ldap=$withval
+fi
+
+
+if test "x$with_ldap" = "xno" ; then
+ alpine_with_ldap=
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Excluding LDAP Support" >&5
+$as_echo "$as_me: Excluding LDAP Support" >&6;}
+else
+
+ alpine_with_ldap=yes
+
+@%:@ Check whether --with-ldap-dir was given.
+if test "${with_ldap_dir+set}" = set; then :
+ withval=$with_ldap_dir;
+ if test "x$withval" != "xno" ; then
+ CPPFLAGS="$CPPFLAGS -I${withval}/include"
+ LDFLAGS="$LDFLAGS -L${withval}/lib"
+ fi
+
+fi
+
+
+
+@%:@ Check whether --with-ldap-include-dir was given.
+if test "${with_ldap_include_dir+set}" = set; then :
+ withval=$with_ldap_include_dir;
+ if test "x$withval" != "xno" ; then
+ CPPFLAGS="$CPPFLAGS -I$withval"
+ fi
+
+fi
+
+
+
+@%:@ Check whether --with-ldap-lib-dir was given.
+if test "${with_ldap_lib_dir+set}" = set; then :
+ withval=$with_ldap_lib_dir;
+ if test "x$withval" != "xno" ; then
+ LDFLAGS="$LDFLAGS -L$withval"
+ fi
+
+fi
+
+fi
+
+
+@%:@ Check whether --with-smime was given.
+if test "${with_smime+set}" = set; then :
+ withval=$with_smime; with_smime=$withval
+fi
+
+
+
+@%:@ Check whether --with-tcl was given.
+if test "${with_tcl+set}" = set; then :
+ withval=$with_tcl; with_tcl=$withval
+fi
+
+
+if test "x$with_tcl" = "xno" ; then
+ WEB_BUILD=
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Excluding TCL Support, and thus Web Alpine Support" >&5
+$as_echo "$as_me: Excluding TCL Support, and thus Web Alpine Support" >&6;}
+else
+
+@%:@ Check whether --with-tcl-lib was given.
+if test "${with_tcl_lib+set}" = set; then :
+ withval=$with_tcl_lib;
+ if test "x$withval" != "xno" ; then
+ alpine_TCLLIB=$withval
+ fi
+
+fi
+
+
+@%:@ Check whether --with-tcl-include was given.
+if test "${with_tcl_include+set}" = set; then :
+ withval=$with_tcl_include;
+ if test "x$withval" != "xno" ; then
+ CPPFLAGS="$CPPFLAGS -I$withval"
+ alpine_TCLINC=$withval
+ fi
+
+fi
+
+fi
+
+
+@%:@ Check whether --with-supplied-regex was given.
+if test "${with_supplied_regex+set}" = set; then :
+ withval=$with_supplied_regex; alpine_REGEX=$withval
+fi
+
+
+
+@%:@ Check whether --with-pthread was given.
+if test "${with_pthread+set}" = set; then :
+ withval=$with_pthread; with_pthread=$withval
+fi
+
+
+
+@%:@ Check whether --with-system-mail-directory was given.
+if test "${with_system_mail_directory+set}" = set; then :
+ withval=$with_system_mail_directory;
+ if test "x$withval" != "xno" ; then
+ alpine_with_local_maildir="$withval"
+ fi
+
+fi
+
+
+
+@%:@ Check whether --with-c-client-target was given.
+if test "${with_c_client_target+set}" = set; then :
+ withval=$with_c_client_target;
+ if test "x$withval" != "xno" ;then
+ alpine_with_c_client_target="$withval"
+ fi
+
+fi
+
+
+
+
+@%:@ Check whether --with-ipv6 was given.
+if test "${with_ipv6+set}" = set; then :
+ withval=$with_ipv6; with_ipv6=$withval
+fi
+
+
+if test "x$with_ipv6" = "xno" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Excluding IPv6 Support" >&5
+$as_echo "$as_me: Excluding IPv6 Support" >&6;}
+ c_client_specials="${c_client_specials}IP6=4 "
+ c_client_ip6="true"
+else
+ c_client_ip6="touch imap/ip6"
+fi
+
+
+
+if test x$enable_dmalloc = "xyes" ; then
+ if test "x$with_pthread" = "xyes" ; then
+ dmalloc_lib=dmallocth
+ else
+ dmalloc_lib=dmalloc
+ fi
+
+ as_ac_Lib=`$as_echo "ac_cv_lib_$dmalloc_lib''_dmalloc_shutdown" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dmalloc_shutdown in -l$dmalloc_lib" >&5
+$as_echo_n "checking for dmalloc_shutdown in -l$dmalloc_lib... " >&6; }
+if eval \${$as_ac_Lib+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-l$dmalloc_lib $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dmalloc_shutdown ();
+int
+main ()
+{
+return dmalloc_shutdown ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ eval "$as_ac_Lib=yes"
+else
+ eval "$as_ac_Lib=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+eval ac_res=\$$as_ac_Lib
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define `$as_echo "HAVE_LIB$dmalloc_lib" | $as_tr_cpp` 1
+_ACEOF
+
+ LIBS="-l$dmalloc_lib $LIBS"
+
+else
+
+ as_fn_error $? "$dmalloc_lib requested, but -ldmalloc not found" "$LINENO" 5
+
+fi
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for setupterm in -ltinfo" >&5
+$as_echo_n "checking for setupterm in -ltinfo... " >&6; }
+if ${ac_cv_lib_tinfo_setupterm+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ltinfo $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char setupterm ();
+int
+main ()
+{
+return setupterm ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_tinfo_setupterm=yes
+else
+ ac_cv_lib_tinfo_setupterm=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_tinfo_setupterm" >&5
+$as_echo "$ac_cv_lib_tinfo_setupterm" >&6; }
+if test "x$ac_cv_lib_tinfo_setupterm" = xyes; then :
+
+ alpine_termdata=info
+ LIBS="$LIBS -ltinfo"
+
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for setupterm in -lncurses" >&5
+$as_echo_n "checking for setupterm in -lncurses... " >&6; }
+if ${ac_cv_lib_ncurses_setupterm+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lncurses $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char setupterm ();
+int
+main ()
+{
+return setupterm ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_ncurses_setupterm=yes
+else
+ ac_cv_lib_ncurses_setupterm=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ncurses_setupterm" >&5
+$as_echo "$ac_cv_lib_ncurses_setupterm" >&6; }
+if test "x$ac_cv_lib_ncurses_setupterm" = xyes; then :
+
+ alpine_termdata=info
+ LIBS="$LIBS -lncurses"
+
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for setupterm in -lcurses" >&5
+$as_echo_n "checking for setupterm in -lcurses... " >&6; }
+if ${ac_cv_lib_curses_setupterm+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lcurses $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char setupterm ();
+int
+main ()
+{
+return setupterm ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_curses_setupterm=yes
+else
+ ac_cv_lib_curses_setupterm=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_curses_setupterm" >&5
+$as_echo "$ac_cv_lib_curses_setupterm" >&6; }
+if test "x$ac_cv_lib_curses_setupterm" = xyes; then :
+
+ alpine_termdata=info
+ LIBS="$LIBS -lcurses"
+
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for tgetent in -ltermlib" >&5
+$as_echo_n "checking for tgetent in -ltermlib... " >&6; }
+if ${ac_cv_lib_termlib_tgetent+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ltermlib $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char tgetent ();
+int
+main ()
+{
+return tgetent ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_termlib_tgetent=yes
+else
+ ac_cv_lib_termlib_tgetent=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_termlib_tgetent" >&5
+$as_echo "$ac_cv_lib_termlib_tgetent" >&6; }
+if test "x$ac_cv_lib_termlib_tgetent" = xyes; then :
+
+ alpine_termdata=cap
+ LIBS="$LIBS -ltermlib"
+
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for tgetent in -ltermcap" >&5
+$as_echo_n "checking for tgetent in -ltermcap... " >&6; }
+if ${ac_cv_lib_termcap_tgetent+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ltermcap $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char tgetent ();
+int
+main ()
+{
+return tgetent ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_termcap_tgetent=yes
+else
+ ac_cv_lib_termcap_tgetent=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_termcap_tgetent" >&5
+$as_echo "$ac_cv_lib_termcap_tgetent" >&6; }
+if test "x$ac_cv_lib_termcap_tgetent" = xyes; then :
+
+ alpine_termdata=cap
+ LIBS="$LIBS -ltermcap"
+
+else
+
+ as_fn_error $? "Terminfo/termcap not found" "$LINENO" 5
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+case $alpine_termdata in
+ info)
+
+$as_echo "@%:@define HAS_TERMINFO 1" >>confdefs.h
+
+ ;;
+ cap)
+
+$as_echo "@%:@define HAS_TERMCAP 1" >>confdefs.h
+
+ ;;
+esac
+
+if test "$alpine_with_ldap" = "yes" ; then
+ alpine_has_ldap=
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ber_alloc in -llber" >&5
+$as_echo_n "checking for ber_alloc in -llber... " >&6; }
+if ${ac_cv_lib_lber_ber_alloc+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-llber $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ber_alloc ();
+int
+main ()
+{
+return ber_alloc ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_lber_ber_alloc=yes
+else
+ ac_cv_lib_lber_ber_alloc=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lber_ber_alloc" >&5
+$as_echo "$ac_cv_lib_lber_ber_alloc" >&6; }
+if test "x$ac_cv_lib_lber_ber_alloc" = xyes; then :
+
+ LIBS="$LIBS -llber"
+
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing ldap_init" >&5
+$as_echo_n "checking for library containing ldap_init... " >&6; }
+if ${ac_cv_search_ldap_init+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ldap_init ();
+int
+main ()
+{
+return ldap_init ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' ldap; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_ldap_init=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_ldap_init+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_ldap_init+:} false; then :
+
+else
+ ac_cv_search_ldap_init=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_ldap_init" >&5
+$as_echo "$ac_cv_search_ldap_init" >&6; }
+ac_res=$ac_cv_search_ldap_init
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+ alpine_has_ldap=yes
+
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing ldap_open" >&5
+$as_echo_n "checking for library containing ldap_open... " >&6; }
+if ${ac_cv_search_ldap_open+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ldap_open ();
+int
+main ()
+{
+return ldap_open ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' ldap; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_ldap_open=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_ldap_open+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_ldap_open+:} false; then :
+
+else
+ ac_cv_search_ldap_open=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_ldap_open" >&5
+$as_echo "$ac_cv_search_ldap_open" >&6; }
+ac_res=$ac_cv_search_ldap_open
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+ alpine_has_ldap=yes
+
+fi
+
+
+fi
+
+
+ if test "$alpine_has_ldap" = "yes" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Including LDAP Support" >&5
+$as_echo "$as_me: Including LDAP Support" >&6;}
+
+$as_echo "@%:@define ENABLE_LDAP /**/" >>confdefs.h
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we should define LDAP_DEPRECATED" >&5
+$as_echo_n "checking if we should define LDAP_DEPRECATED... " >&6; }
+ if test "$cross_compiling" = yes; then :
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cross compiling: not checking" >&5
+$as_echo "$as_me: WARNING: cross compiling: not checking" >&2;}
+
+
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#include <stdio.h>
+#include <string.h>
+#include <ldap.h>
+int main(void) {
+
+ if (LDAP_VENDOR_VERSION >= 20300)
+ exit(0);
+
+ exit(2);
+}
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+$as_echo "@%:@define LDAP_DEPRECATED 1" >>confdefs.h
+
+
+
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Cannot find LDAP functions! Excluding LDAP support." >&5
+$as_echo "$as_me: Cannot find LDAP functions! Excluding LDAP support." >&6;}
+ fi
+fi
+
+if test "x$alpine_SSLTYPE" != "xnone" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing SSL_library_init" >&5
+$as_echo_n "checking for library containing SSL_library_init... " >&6; }
+if ${ac_cv_search_SSL_library_init+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char SSL_library_init ();
+int
+main ()
+{
+return SSL_library_init ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' ssl; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_SSL_library_init=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_SSL_library_init+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_SSL_library_init+:} false; then :
+
+else
+ ac_cv_search_SSL_library_init=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_SSL_library_init" >&5
+$as_echo "$ac_cv_search_SSL_library_init" >&6; }
+ac_res=$ac_cv_search_SSL_library_init
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+ LIBS="$LIBS -lssl"
+
+fi
+
+ if test "x$alpine_SSLTYPE" = "xnone" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: OpenSSL libraries NOT found" >&5
+$as_echo "$as_me: OpenSSL libraries NOT found" >&6;}
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: OpenSSL libraries FOUND" >&5
+$as_echo "$as_me: OpenSSL libraries FOUND" >&6;}
+ fi
+fi
+
+if test "x$alpine_GSSTYPE" != "xnone" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing gss_init_sec_context" >&5
+$as_echo_n "checking for library containing gss_init_sec_context... " >&6; }
+if ${ac_cv_search_gss_init_sec_context+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char gss_init_sec_context ();
+int
+main ()
+{
+return gss_init_sec_context ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' gss gssapi gssapi_krb5; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_gss_init_sec_context=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_gss_init_sec_context+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_gss_init_sec_context+:} false; then :
+
+else
+ ac_cv_search_gss_init_sec_context=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_gss_init_sec_context" >&5
+$as_echo "$ac_cv_search_gss_init_sec_context" >&6; }
+ac_res=$ac_cv_search_gss_init_sec_context
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+ ac_fn_c_check_header_mongrel "$LINENO" "gssapi/gssapi_generic.h" "ac_cv_header_gssapi_gssapi_generic_h" "$ac_includes_default"
+if test "x$ac_cv_header_gssapi_gssapi_generic_h" = xyes; then :
+
+else
+
+ if test ! -d /usr/kerberos/include ; then
+ alpine_GSSTYPE="none"
+ alpine_gss_none_reason="header files not found"
+ fi
+
+fi
+
+
+
+else
+
+ alpine_GSSTYPE="none"
+ alpine_gss_none_reason="libraries not found"
+
+fi
+
+ if test -n "$alpine_gss_none_reason" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: NOT including Kerberos Support: $alpine_gss_none_reason" >&5
+$as_echo "$as_me: NOT including Kerberos Support: $alpine_gss_none_reason" >&6;}
+ fi
+fi
+
+if test -n "$WEB_BUILD" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing Tcl_Eval" >&5
+$as_echo_n "checking for library containing Tcl_Eval... " >&6; }
+if ${ac_cv_search_Tcl_Eval+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char Tcl_Eval ();
+int
+main ()
+{
+return Tcl_Eval ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' $alpine_TCLLIB tcl8.4 tcl8.3 tcl84 tcl83 tcl; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_Tcl_Eval=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_Tcl_Eval+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_Tcl_Eval+:} false; then :
+
+else
+ ac_cv_search_Tcl_Eval=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_Tcl_Eval" >&5
+$as_echo "$ac_cv_search_Tcl_Eval" >&6; }
+ac_res=$ac_cv_search_Tcl_Eval
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+else
+
+ WEB_BUILD=
+
+fi
+
+
+ if test -n "$alpine_TCLINC" ; then
+ as_ac_Header=`$as_echo "ac_cv_header_$alpine_TCLINC" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$alpine_TCLINC" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+
+else
+
+ WEB_BUILD=
+
+fi
+
+
+ if test -z "$WEB_BUILD" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Tcl Include file NOT found" >&5
+$as_echo "$as_me: Tcl Include file NOT found" >&6;}
+ fi
+ else
+ ac_fn_c_check_header_mongrel "$LINENO" "tcl.h" "ac_cv_header_tcl_h" "$ac_includes_default"
+if test "x$ac_cv_header_tcl_h" = xyes; then :
+
+else
+
+ for dir in tcl8.4 tcl8.3 tcl84 tcl83 ; do
+ as_ac_File=`$as_echo "ac_cv_file_/usr/include/$dir/tcl.h" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for /usr/include/$dir/tcl.h" >&5
+$as_echo_n "checking for /usr/include/$dir/tcl.h... " >&6; }
+if eval \${$as_ac_File+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ test "$cross_compiling" = yes &&
+ as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5
+if test -r "/usr/include/$dir/tcl.h"; then
+ eval "$as_ac_File=yes"
+else
+ eval "$as_ac_File=no"
+fi
+fi
+eval ac_res=\$$as_ac_File
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_File"\" = x"yes"; then :
+
+ found=yes
+
+fi
+
+ if test "$found" = "yes" ; then
+ CPPFLAGS="$CPPFLAGS -I/usr/include/$dir"
+ break
+ fi
+ done
+
+fi
+
+
+ fi
+fi
+
+if test x$alpine_REGEX != "xyes" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing regcomp" >&5
+$as_echo_n "checking for library containing regcomp... " >&6; }
+if ${ac_cv_search_regcomp+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char regcomp ();
+int
+main ()
+{
+return regcomp ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' posix regexp regex re; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_regcomp=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_regcomp+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_regcomp+:} false; then :
+
+else
+ ac_cv_search_regcomp=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_regcomp" >&5
+$as_echo "$ac_cv_search_regcomp" >&6; }
+ac_res=$ac_cv_search_regcomp
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+else
+
+ if test x$alpine_REGEX = "xno" ; then
+ as_fn_error $? "Unable to find system regex library" "$LINENO" 5
+ else
+ alpine_REGEX=yes
+ fi
+
+fi
+
+fi
+if test x$alpine_REGEX != "xyes" ; then
+ ac_fn_c_check_header_mongrel "$LINENO" "regex.h" "ac_cv_header_regex_h" "$ac_includes_default"
+if test "x$ac_cv_header_regex_h" = xyes; then :
+
+else
+
+ if test x$alpine_REGEX = "xno" ; then
+ as_fn_error $? "Unable to find system regex include file" "$LINENO" 5
+ else
+ alpine_REGEX=yes
+ fi
+
+fi
+
+
+fi
+
+$as_echo "@%:@define HAVE_REGEX_H 1" >>confdefs.h
+
+if test x$alpine_REGEX = "xyes" ; then
+ CPPFLAGS="$CPPFLAGS -I${top_builddir}/regex"
+ LDFLAGS="$LDFLAGS -L${top_builddir}/regex -lregex"
+ REGEX_BUILD=regex
+fi
+
+if test "x$with_pthread" != "xno" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread support" >&5
+$as_echo_n "checking for pthread support... " >&6; }
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+acx_pthread_ok=no
+
+# We used to check for pthread.h first, but this fails if pthread.h
+# requires special compiler flags (e.g. on True64 or Sequent).
+# It gets checked for in the link test anyway.
+
+# First of all, check if the user has set any of the PTHREAD_LIBS,
+# etcetera environment variables, and if threads linking works using
+# them:
+if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+ save_LIBS="$LIBS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS" >&5
+$as_echo_n "checking for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS... " >&6; }
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pthread_join ();
+int
+main ()
+{
+return pthread_join ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ acx_pthread_ok=yes
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $acx_pthread_ok" >&5
+$as_echo "$acx_pthread_ok" >&6; }
+ if test x"$acx_pthread_ok" = xno; then
+ PTHREAD_LIBS=""
+ PTHREAD_CFLAGS=""
+ fi
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+fi
+
+# We must check for the threads library under a number of different
+# names; the ordering is very important because some systems
+# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
+# libraries is broken (non-POSIX).
+
+# Create a list of thread flags to try. Items starting with a "-" are
+# C compiler flags, and other items are library names, except for "none"
+# which indicates that we try without any flags at all, and "pthread-config"
+# which is a program returning the flags for the Pth emulation library.
+
+acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
+
+# The ordering *is* (sometimes) important. Some notes on the
+# individual items follow:
+
+# pthreads: AIX (must check this before -lpthread)
+# none: in case threads are in libc; should be tried before -Kthread and
+# other compiler flags to prevent continual compiler warnings
+# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
+# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
+# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
+# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
+# -pthreads: Solaris/gcc
+# -mthreads: Mingw32/gcc, Lynx/gcc
+# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
+# doesn't hurt to check since this sometimes defines pthreads too;
+# also defines -D_REENTRANT)
+# ... -mt is also the pthreads flag for HP/aCC
+# pthread: Linux, etcetera
+# --thread-safe: KAI C++
+# pthread-config: use pthread-config program (for GNU Pth library)
+
+case "${host_cpu}-${host_os}" in
+ *solaris*)
+
+ # On Solaris (at least, for some versions), libc contains stubbed
+ # (non-functional) versions of the pthreads routines, so link-based
+ # tests will erroneously succeed. (We need to link with -pthreads/-mt/
+ # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
+ # a function called by this macro, so we could check for that, but
+ # who knows whether they'll stub that too in a future libc.) So,
+ # we'll just look for -pthreads and -lpthread first:
+
+ acx_pthread_flags="-pthreads pthread -mt -pthread $acx_pthread_flags"
+ ;;
+esac
+
+if test x"$acx_pthread_ok" = xno; then
+for flag in $acx_pthread_flags; do
+
+ case $flag in
+ none)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work without any flags" >&5
+$as_echo_n "checking whether pthreads work without any flags... " >&6; }
+ ;;
+
+ -*)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work with $flag" >&5
+$as_echo_n "checking whether pthreads work with $flag... " >&6; }
+ PTHREAD_CFLAGS="$flag"
+ ;;
+
+ pthread-config)
+ # Extract the first word of "pthread-config", so it can be a program name with args.
+set dummy pthread-config; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_acx_pthread_config+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$acx_pthread_config"; then
+ ac_cv_prog_acx_pthread_config="$acx_pthread_config" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_acx_pthread_config="yes"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_prog_acx_pthread_config" && ac_cv_prog_acx_pthread_config="no"
+fi
+fi
+acx_pthread_config=$ac_cv_prog_acx_pthread_config
+if test -n "$acx_pthread_config"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $acx_pthread_config" >&5
+$as_echo "$acx_pthread_config" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test x"$acx_pthread_config" = xno; then continue; fi
+ PTHREAD_CFLAGS="`pthread-config --cflags`"
+ PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
+ ;;
+
+ *)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for the pthreads library -l$flag" >&5
+$as_echo_n "checking for the pthreads library -l$flag... " >&6; }
+ PTHREAD_LIBS="-l$flag"
+ ;;
+ esac
+
+ save_LIBS="$LIBS"
+ save_CFLAGS="$CFLAGS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+ # Check for various functions. We must include pthread.h,
+ # since some functions may be macros. (On the Sequent, we
+ # need a special flag -Kthread to make this header compile.)
+ # We check for pthread_join because it is in -lpthread on IRIX
+ # while pthread_create is in libc. We check for pthread_attr_init
+ # due to DEC craziness with -lpthreads. We check for
+ # pthread_cleanup_push because it is one of the few pthread
+ # functions on Solaris that doesn't have a non-functional libc stub.
+ # We try pthread_create on general principles.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <pthread.h>
+int
+main ()
+{
+pthread_t th; pthread_join(th, 0);
+ pthread_attr_init(0); pthread_cleanup_push(0, 0);
+ pthread_create(0,0,0,0); pthread_cleanup_pop(0);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ acx_pthread_ok=yes
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $acx_pthread_ok" >&5
+$as_echo "$acx_pthread_ok" >&6; }
+ if test "x$acx_pthread_ok" = xyes; then
+ break;
+ fi
+
+ PTHREAD_LIBS=""
+ PTHREAD_CFLAGS=""
+done
+fi
+
+# Various other checks:
+if test "x$acx_pthread_ok" = xyes; then
+ save_LIBS="$LIBS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+ # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for joinable pthread attribute" >&5
+$as_echo_n "checking for joinable pthread attribute... " >&6; }
+ attr_name=unknown
+ for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <pthread.h>
+int
+main ()
+{
+int attr=$attr; return attr;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ attr_name=$attr; break
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ done
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $attr_name" >&5
+$as_echo "$attr_name" >&6; }
+ if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PTHREAD_CREATE_JOINABLE $attr_name
+_ACEOF
+
+ fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if more special flags are required for pthreads" >&5
+$as_echo_n "checking if more special flags are required for pthreads... " >&6; }
+ flag=no
+ case "${host_cpu}-${host_os}" in
+ *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";;
+ *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
+ esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${flag}" >&5
+$as_echo "${flag}" >&6; }
+ if test "x$flag" != xno; then
+ PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
+ fi
+
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+
+ # More AIX lossage: must compile with xlc_r or cc_r
+ if test x"$GCC" != xyes; then
+ for ac_prog in xlc_r cc_r
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_PTHREAD_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$PTHREAD_CC"; then
+ ac_cv_prog_PTHREAD_CC="$PTHREAD_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_PTHREAD_CC="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+PTHREAD_CC=$ac_cv_prog_PTHREAD_CC
+if test -n "$PTHREAD_CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PTHREAD_CC" >&5
+$as_echo "$PTHREAD_CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$PTHREAD_CC" && break
+done
+test -n "$PTHREAD_CC" || PTHREAD_CC="${CC}"
+
+ else
+ PTHREAD_CC=$CC
+ fi
+else
+ PTHREAD_CC="$CC"
+fi
+
+
+
+
+
+# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
+if test x"$acx_pthread_ok" = xyes; then
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ case "$target" in
+ *openbsd*)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: pthread support on OpenBSD is unstable!" >&5
+$as_echo "$as_me: WARNING: pthread support on OpenBSD is unstable!" >&6;}
+ AM_CFLAGS="$AM_CFLAGS -pthread"
+ ;;
+ esac
+ LIBS="$PTHREAD_LIBS $LIBS"
+ AM_CFLAGS="$AM_CFLAGS $PTHREAD_CFLAGS"
+ CC="$PTHREAD_CC"
+
+$as_echo "@%:@define HAVE_PTHREAD 1" >>confdefs.h
+
+
+ :
+else
+ acx_pthread_ok=no
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing nanosleep" >&5
+$as_echo_n "checking for library containing nanosleep... " >&6; }
+if ${ac_cv_search_nanosleep+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char nanosleep ();
+int
+main ()
+{
+return nanosleep ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' rt posix4; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_nanosleep=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_nanosleep+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_nanosleep+:} false; then :
+
+else
+ ac_cv_search_nanosleep=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_nanosleep" >&5
+$as_echo "$ac_cv_search_nanosleep" >&6; }
+ac_res=$ac_cv_search_nanosleep
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+
+$as_echo "@%:@define HAVE_NANOSLEEP 1" >>confdefs.h
+
+
+fi
+
+fi
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if ${ac_cv_header_stdc+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_header_stdc=yes
+else
+ ac_cv_header_stdc=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "memchr" >/dev/null 2>&1; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "free" >/dev/null 2>&1; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+ if test "$cross_compiling" = yes; then :
+ :
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+ (('a' <= (c) && (c) <= 'i') \
+ || ('j' <= (c) && (c) <= 'r') \
+ || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ if (XOR (islower (i), ISLOWER (i))
+ || toupper (i) != TOUPPER (i))
+ return 2;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+$as_echo "@%:@define STDC_HEADERS 1" >>confdefs.h
+
+fi
+
+ac_header_dirent=no
+for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do
+ as_ac_Header=`$as_echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_hdr that defines DIR" >&5
+$as_echo_n "checking for $ac_hdr that defines DIR... " >&6; }
+if eval \${$as_ac_Header+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <$ac_hdr>
+
+int
+main ()
+{
+if ((DIR *) 0)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ eval "$as_ac_Header=yes"
+else
+ eval "$as_ac_Header=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$as_ac_Header
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define `$as_echo "HAVE_$ac_hdr" | $as_tr_cpp` 1
+_ACEOF
+
+ac_header_dirent=$ac_hdr; break
+fi
+
+done
+# Two versions of opendir et al. are in -ldir and -lx on SCO Xenix.
+if test $ac_header_dirent = dirent.h; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5
+$as_echo_n "checking for library containing opendir... " >&6; }
+if ${ac_cv_search_opendir+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char opendir ();
+int
+main ()
+{
+return opendir ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' dir; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_opendir=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_opendir+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_opendir+:} false; then :
+
+else
+ ac_cv_search_opendir=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5
+$as_echo "$ac_cv_search_opendir" >&6; }
+ac_res=$ac_cv_search_opendir
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5
+$as_echo_n "checking for library containing opendir... " >&6; }
+if ${ac_cv_search_opendir+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char opendir ();
+int
+main ()
+{
+return opendir ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' x; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_opendir=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_opendir+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_opendir+:} false; then :
+
+else
+ ac_cv_search_opendir=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5
+$as_echo "$ac_cv_search_opendir" >&6; }
+ac_res=$ac_cv_search_opendir
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stat file-mode macros are broken" >&5
+$as_echo_n "checking whether stat file-mode macros are broken... " >&6; }
+if ${ac_cv_header_stat_broken+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#if defined S_ISBLK && defined S_IFDIR
+extern char c1[S_ISBLK (S_IFDIR) ? -1 : 1];
+#endif
+
+#if defined S_ISBLK && defined S_IFCHR
+extern char c2[S_ISBLK (S_IFCHR) ? -1 : 1];
+#endif
+
+#if defined S_ISLNK && defined S_IFREG
+extern char c3[S_ISLNK (S_IFREG) ? -1 : 1];
+#endif
+
+#if defined S_ISSOCK && defined S_IFREG
+extern char c4[S_ISSOCK (S_IFREG) ? -1 : 1];
+#endif
+
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_header_stat_broken=no
+else
+ ac_cv_header_stat_broken=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stat_broken" >&5
+$as_echo "$ac_cv_header_stat_broken" >&6; }
+if test $ac_cv_header_stat_broken = yes; then
+
+$as_echo "@%:@define STAT_MACROS_BROKEN 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sys/wait.h that is POSIX.1 compatible" >&5
+$as_echo_n "checking for sys/wait.h that is POSIX.1 compatible... " >&6; }
+if ${ac_cv_header_sys_wait_h+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/wait.h>
+#ifndef WEXITSTATUS
+# define WEXITSTATUS(stat_val) ((unsigned int) (stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+
+int
+main ()
+{
+ int s;
+ wait (&s);
+ s = WIFEXITED (s) ? WEXITSTATUS (s) : 1;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_header_sys_wait_h=yes
+else
+ ac_cv_header_sys_wait_h=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_sys_wait_h" >&5
+$as_echo "$ac_cv_header_sys_wait_h" >&6; }
+if test $ac_cv_header_sys_wait_h = yes; then
+
+$as_echo "@%:@define HAVE_SYS_WAIT_H 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether time.h and sys/time.h may both be included" >&5
+$as_echo_n "checking whether time.h and sys/time.h may both be included... " >&6; }
+if ${ac_cv_header_time+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
+
+int
+main ()
+{
+if ((struct tm *) 0)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_header_time=yes
+else
+ ac_cv_header_time=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_time" >&5
+$as_echo "$ac_cv_header_time" >&6; }
+if test $ac_cv_header_time = yes; then
+
+$as_echo "@%:@define TIME_WITH_SYS_TIME 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether termios.h defines TIOCGWINSZ" >&5
+$as_echo_n "checking whether termios.h defines TIOCGWINSZ... " >&6; }
+if ${ac_cv_sys_tiocgwinsz_in_termios_h+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <termios.h>
+#ifdef TIOCGWINSZ
+ yes
+#endif
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "yes" >/dev/null 2>&1; then :
+ ac_cv_sys_tiocgwinsz_in_termios_h=yes
+else
+ ac_cv_sys_tiocgwinsz_in_termios_h=no
+fi
+rm -f conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_tiocgwinsz_in_termios_h" >&5
+$as_echo "$ac_cv_sys_tiocgwinsz_in_termios_h" >&6; }
+
+if test $ac_cv_sys_tiocgwinsz_in_termios_h != yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether sys/ioctl.h defines TIOCGWINSZ" >&5
+$as_echo_n "checking whether sys/ioctl.h defines TIOCGWINSZ... " >&6; }
+if ${ac_cv_sys_tiocgwinsz_in_sys_ioctl_h+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#ifdef TIOCGWINSZ
+ yes
+#endif
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "yes" >/dev/null 2>&1; then :
+ ac_cv_sys_tiocgwinsz_in_sys_ioctl_h=yes
+else
+ ac_cv_sys_tiocgwinsz_in_sys_ioctl_h=no
+fi
+rm -f conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_tiocgwinsz_in_sys_ioctl_h" >&5
+$as_echo "$ac_cv_sys_tiocgwinsz_in_sys_ioctl_h" >&6; }
+
+ if test $ac_cv_sys_tiocgwinsz_in_sys_ioctl_h = yes; then
+
+$as_echo "@%:@define GWINSZ_IN_SYS_IOCTL 1" >>confdefs.h
+
+ fi
+fi
+
+
+for ac_header in unistd.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "unistd.h" "ac_cv_header_unistd_h" "$ac_includes_default"
+if test "x$ac_cv_header_unistd_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_UNISTD_H 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in errno.h \
+ ctype.h \
+ fcntl.h \
+ signal.h \
+ setjmp.h \
+ memory.h \
+ sys/param.h \
+ sys/socket.h \
+ sys/uio.h \
+ sys/un.h \
+ limits.h \
+ wchar.h \
+ sys/poll.h \
+ stropts.h \
+ netdb.h \
+ syslog.h \
+ sys/syslog.h \
+ locale.h \
+ langinfo.h \
+ utime.h \
+ sys/utime.h \
+ pthread.h \
+ pwd.h \
+ assert.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+ac_fn_c_check_header_mongrel "$LINENO" "termios.h" "ac_cv_header_termios_h" "$ac_includes_default"
+if test "x$ac_cv_header_termios_h" = xyes; then :
+
+$as_echo "@%:@define HAS_TERMIOS 1" >>confdefs.h
+
+else
+
+ ac_fn_c_check_header_mongrel "$LINENO" "termio.h" "ac_cv_header_termio_h" "$ac_includes_default"
+if test "x$ac_cv_header_termio_h" = xyes; then :
+
+$as_echo "@%:@define HAS_TERMIO 1" >>confdefs.h
+
+else
+
+ ac_fn_c_check_header_mongrel "$LINENO" "sgtty.h" "ac_cv_header_sgtty_h" "$ac_includes_default"
+if test "x$ac_cv_header_sgtty_h" = xyes; then :
+
+$as_echo "@%:@define HAS_SGTTY 1" >>confdefs.h
+
+else
+
+ as_fn_error $? "Unable to figure out terminal control method" "$LINENO" 5
+
+fi
+
+
+
+fi
+
+
+
+fi
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking return type of signal handlers" >&5
+$as_echo_n "checking return type of signal handlers... " >&6; }
+if ${ac_cv_type_signal+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <signal.h>
+
+int
+main ()
+{
+return *(signal (0, 0)) (0) == 1;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_type_signal=int
+else
+ ac_cv_type_signal=void
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_signal" >&5
+$as_echo "$ac_cv_type_signal" >&6; }
+
+cat >>confdefs.h <<_ACEOF
+@%:@define RETSIGTYPE $ac_cv_type_signal
+_ACEOF
+
+
+ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default"
+if test "x$ac_cv_type_size_t" = xyes; then :
+
+else
+
+cat >>confdefs.h <<_ACEOF
+@%:@define size_t unsigned int
+_ACEOF
+
+fi
+
+ac_fn_c_check_type "$LINENO" "mode_t" "ac_cv_type_mode_t" "$ac_includes_default"
+if test "x$ac_cv_type_mode_t" = xyes; then :
+
+else
+
+cat >>confdefs.h <<_ACEOF
+@%:@define mode_t int
+_ACEOF
+
+fi
+
+ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default"
+if test "x$ac_cv_type_pid_t" = xyes; then :
+
+else
+
+cat >>confdefs.h <<_ACEOF
+@%:@define pid_t int
+_ACEOF
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for uid_t in sys/types.h" >&5
+$as_echo_n "checking for uid_t in sys/types.h... " >&6; }
+if ${ac_cv_type_uid_t+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "uid_t" >/dev/null 2>&1; then :
+ ac_cv_type_uid_t=yes
+else
+ ac_cv_type_uid_t=no
+fi
+rm -f conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_uid_t" >&5
+$as_echo "$ac_cv_type_uid_t" >&6; }
+if test $ac_cv_type_uid_t = no; then
+
+$as_echo "@%:@define uid_t int" >>confdefs.h
+
+
+$as_echo "@%:@define gid_t int" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether struct tm is in sys/time.h or time.h" >&5
+$as_echo_n "checking whether struct tm is in sys/time.h or time.h... " >&6; }
+if ${ac_cv_struct_tm+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <time.h>
+
+int
+main ()
+{
+struct tm tm;
+ int *p = &tm.tm_sec;
+ return !p;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_struct_tm=time.h
+else
+ ac_cv_struct_tm=sys/time.h
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_struct_tm" >&5
+$as_echo "$ac_cv_struct_tm" >&6; }
+if test $ac_cv_struct_tm = sys/time.h; then
+
+$as_echo "@%:@define TM_IN_SYS_TIME 1" >>confdefs.h
+
+fi
+
+
+ac_fn_c_check_type "$LINENO" "union wait" "ac_cv_type_union_wait" "$ac_includes_default"
+if test "x$ac_cv_type_union_wait" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_UNION_WAIT 1
+_ACEOF
+
+
+fi
+
+
+for ac_header in stdint.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "stdint.h" "ac_cv_header_stdint_h" "$ac_includes_default"
+if test "x$ac_cv_header_stdint_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_STDINT_H 1
+_ACEOF
+ uint16=uint16_t
+else
+
+ for ac_header in inttypes.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "inttypes.h" "ac_cv_header_inttypes_h" "$ac_includes_default"
+if test "x$ac_cv_header_inttypes_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_INTTYPES_H 1
+_ACEOF
+ uint16=uint16_t
+else
+
+ for ac_header in sys/types.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "sys/types.h" "ac_cv_header_sys_types_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_types_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_SYS_TYPES_H 1
+_ACEOF
+ uint16=u_int16_t
+else
+
+ # The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned short" >&5
+$as_echo_n "checking size of unsigned short... " >&6; }
+if ${ac_cv_sizeof_unsigned_short+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned short))" "ac_cv_sizeof_unsigned_short" "$ac_includes_default"; then :
+
+else
+ if test "$ac_cv_type_unsigned_short" = yes; then
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (unsigned short)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_unsigned_short=0
+ fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_short" >&5
+$as_echo "$ac_cv_sizeof_unsigned_short" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SIZEOF_UNSIGNED_SHORT $ac_cv_sizeof_unsigned_short
+_ACEOF
+
+
+ if test $ac_cv_sizeof_unsigned_short -eq 2 ; then
+ uint16="unsigned short"
+ else
+ # The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned int" >&5
+$as_echo_n "checking size of unsigned int... " >&6; }
+if ${ac_cv_sizeof_unsigned_int+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned int))" "ac_cv_sizeof_unsigned_int" "$ac_includes_default"; then :
+
+else
+ if test "$ac_cv_type_unsigned_int" = yes; then
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (unsigned int)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_unsigned_int=0
+ fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_int" >&5
+$as_echo "$ac_cv_sizeof_unsigned_int" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SIZEOF_UNSIGNED_INT $ac_cv_sizeof_unsigned_int
+_ACEOF
+
+
+ if $ac_cv_sizeof_unsigned_int -eq 2 ; then
+ uint16="unsigned int"
+ else
+ as_fn_error $? "Unable to determine 16 bit integer type" "$LINENO" 5
+ fi
+ fi
+
+fi
+
+done
+
+
+fi
+
+done
+
+
+fi
+
+done
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define UINT16 $uint16
+_ACEOF
+
+
+for ac_header in stdint.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "stdint.h" "ac_cv_header_stdint_h" "$ac_includes_default"
+if test "x$ac_cv_header_stdint_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_STDINT_H 1
+_ACEOF
+ uint32=uint32_t
+else
+
+ for ac_header in inttypes.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "inttypes.h" "ac_cv_header_inttypes_h" "$ac_includes_default"
+if test "x$ac_cv_header_inttypes_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_INTTYPES_H 1
+_ACEOF
+ uint32=uint32_t
+else
+
+ for ac_header in sys/types.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "sys/types.h" "ac_cv_header_sys_types_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_types_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_SYS_TYPES_H 1
+_ACEOF
+ uint32=u_int32_t
+else
+
+ # The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned int" >&5
+$as_echo_n "checking size of unsigned int... " >&6; }
+if ${ac_cv_sizeof_unsigned_int+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned int))" "ac_cv_sizeof_unsigned_int" "$ac_includes_default"; then :
+
+else
+ if test "$ac_cv_type_unsigned_int" = yes; then
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (unsigned int)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_unsigned_int=0
+ fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_int" >&5
+$as_echo "$ac_cv_sizeof_unsigned_int" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SIZEOF_UNSIGNED_INT $ac_cv_sizeof_unsigned_int
+_ACEOF
+
+
+ if test $ac_cv_sizeof_unsigned_int -eq 4 ; then
+ uint32="unsigned int"
+ else
+ # The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned long" >&5
+$as_echo_n "checking size of unsigned long... " >&6; }
+if ${ac_cv_sizeof_unsigned_long+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned long))" "ac_cv_sizeof_unsigned_long" "$ac_includes_default"; then :
+
+else
+ if test "$ac_cv_type_unsigned_long" = yes; then
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (unsigned long)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_unsigned_long=0
+ fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_long" >&5
+$as_echo "$ac_cv_sizeof_unsigned_long" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SIZEOF_UNSIGNED_LONG $ac_cv_sizeof_unsigned_long
+_ACEOF
+
+
+ if $ac_cv_sizeof_unsigned_long -eq 4 ; then
+ uint32="unsigned long"
+ else
+ as_fn_error $? "Unable to determine 32 bit integer type" "$LINENO" 5
+ fi
+ fi
+
+fi
+
+done
+
+
+fi
+
+done
+
+
+fi
+
+done
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define UINT32 $uint32
+_ACEOF
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking argument pointer type of qsort compare function and base" >&5
+$as_echo_n "checking argument pointer type of qsort compare function and base... " >&6; }
+if ${ac_cv_func_qsort_argtype+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#if HAVE_STDLIB_H
+#include "stdlib.h"
+#endif
+
+extern void *base;
+extern sortf(const void *, const void *);
+int sortf(a, b)
+ const void *a;
+ const void *b; { return 0; }
+
+int
+main ()
+{
+
+qsort(base, 2, sizeof(char *), sortf);
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_func_qsort_argtype=void
+else
+ ac_cv_func_qsort_argtype=char
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_qsort_argtype" >&5
+$as_echo "$ac_cv_func_qsort_argtype" >&6; }
+
+cat >>confdefs.h <<_ACEOF
+@%:@define qsort_t $ac_cv_func_qsort_argtype
+_ACEOF
+
+
+
+for ac_header in sys/select.h sys/socket.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking types of arguments for select" >&5
+$as_echo_n "checking types of arguments for select... " >&6; }
+if ${ac_cv_func_select_args+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ for ac_arg234 in 'fd_set *' 'int *' 'void *'; do
+ for ac_arg1 in 'int' 'size_t' 'unsigned long int' 'unsigned int'; do
+ for ac_arg5 in 'struct timeval *' 'const struct timeval *'; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_includes_default
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+
+int
+main ()
+{
+extern int select ($ac_arg1,
+ $ac_arg234, $ac_arg234, $ac_arg234,
+ $ac_arg5);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_func_select_args="$ac_arg1,$ac_arg234,$ac_arg5"; break 3
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ done
+ done
+done
+# Provide a safe default value.
+: "${ac_cv_func_select_args=int,int *,struct timeval *}"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_select_args" >&5
+$as_echo "$ac_cv_func_select_args" >&6; }
+ac_save_IFS=$IFS; IFS=','
+set dummy `echo "$ac_cv_func_select_args" | sed 's/\*/\*/g'`
+IFS=$ac_save_IFS
+shift
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SELECT_TYPE_ARG1 $1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SELECT_TYPE_ARG234 ($2)
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SELECT_TYPE_ARG5 ($3)
+_ACEOF
+
+rm -f conftest*
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working strcoll" >&5
+$as_echo_n "checking for working strcoll... " >&6; }
+if ${ac_cv_func_strcoll_works+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then :
+ ac_cv_func_strcoll_works=no
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+return (strcoll ("abc", "def") >= 0 ||
+ strcoll ("ABC", "DEF") >= 0 ||
+ strcoll ("123", "456") >= 0)
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ac_cv_func_strcoll_works=yes
+else
+ ac_cv_func_strcoll_works=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_strcoll_works" >&5
+$as_echo "$ac_cv_func_strcoll_works" >&6; }
+if test $ac_cv_func_strcoll_works = yes; then
+
+$as_echo "@%:@define HAVE_STRCOLL 1" >>confdefs.h
+
+fi
+
+
+
+for ac_header in vfork.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "vfork.h" "ac_cv_header_vfork_h" "$ac_includes_default"
+if test "x$ac_cv_header_vfork_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_VFORK_H 1
+_ACEOF
+
+fi
+
+done
+
+for ac_func in fork vfork
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+if test "x$ac_cv_func_fork" = xyes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working fork" >&5
+$as_echo_n "checking for working fork... " >&6; }
+if ${ac_cv_func_fork_works+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then :
+ ac_cv_func_fork_works=cross
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+
+ /* By Ruediger Kuhlmann. */
+ return fork () < 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ac_cv_func_fork_works=yes
+else
+ ac_cv_func_fork_works=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_fork_works" >&5
+$as_echo "$ac_cv_func_fork_works" >&6; }
+
+else
+ ac_cv_func_fork_works=$ac_cv_func_fork
+fi
+if test "x$ac_cv_func_fork_works" = xcross; then
+ case $host in
+ *-*-amigaos* | *-*-msdosdjgpp*)
+ # Override, as these systems have only a dummy fork() stub
+ ac_cv_func_fork_works=no
+ ;;
+ *)
+ ac_cv_func_fork_works=yes
+ ;;
+ esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5
+$as_echo "$as_me: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&2;}
+fi
+ac_cv_func_vfork_works=$ac_cv_func_vfork
+if test "x$ac_cv_func_vfork" = xyes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working vfork" >&5
+$as_echo_n "checking for working vfork... " >&6; }
+if ${ac_cv_func_vfork_works+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then :
+ ac_cv_func_vfork_works=cross
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+/* Thanks to Paul Eggert for this test. */
+$ac_includes_default
+#include <sys/wait.h>
+#ifdef HAVE_VFORK_H
+# include <vfork.h>
+#endif
+/* On some sparc systems, changes by the child to local and incoming
+ argument registers are propagated back to the parent. The compiler
+ is told about this with #include <vfork.h>, but some compilers
+ (e.g. gcc -O) don't grok <vfork.h>. Test for this by using a
+ static variable whose address is put into a register that is
+ clobbered by the vfork. */
+static void
+#ifdef __cplusplus
+sparc_address_test (int arg)
+# else
+sparc_address_test (arg) int arg;
+#endif
+{
+ static pid_t child;
+ if (!child) {
+ child = vfork ();
+ if (child < 0) {
+ perror ("vfork");
+ _exit(2);
+ }
+ if (!child) {
+ arg = getpid();
+ write(-1, "", 0);
+ _exit (arg);
+ }
+ }
+}
+
+int
+main ()
+{
+ pid_t parent = getpid ();
+ pid_t child;
+
+ sparc_address_test (0);
+
+ child = vfork ();
+
+ if (child == 0) {
+ /* Here is another test for sparc vfork register problems. This
+ test uses lots of local variables, at least as many local
+ variables as main has allocated so far including compiler
+ temporaries. 4 locals are enough for gcc 1.40.3 on a Solaris
+ 4.1.3 sparc, but we use 8 to be safe. A buggy compiler should
+ reuse the register of parent for one of the local variables,
+ since it will think that parent can't possibly be used any more
+ in this routine. Assigning to the local variable will thus
+ munge parent in the parent process. */
+ pid_t
+ p = getpid(), p1 = getpid(), p2 = getpid(), p3 = getpid(),
+ p4 = getpid(), p5 = getpid(), p6 = getpid(), p7 = getpid();
+ /* Convince the compiler that p..p7 are live; otherwise, it might
+ use the same hardware register for all 8 local variables. */
+ if (p != p1 || p != p2 || p != p3 || p != p4
+ || p != p5 || p != p6 || p != p7)
+ _exit(1);
+
+ /* On some systems (e.g. IRIX 3.3), vfork doesn't separate parent
+ from child file descriptors. If the child closes a descriptor
+ before it execs or exits, this munges the parent's descriptor
+ as well. Test for this by closing stdout in the child. */
+ _exit(close(fileno(stdout)) != 0);
+ } else {
+ int status;
+ struct stat st;
+
+ while (wait(&status) != child)
+ ;
+ return (
+ /* Was there some problem with vforking? */
+ child < 0
+
+ /* Did the child fail? (This shouldn't happen.) */
+ || status
+
+ /* Did the vfork/compiler bug occur? */
+ || parent != getpid()
+
+ /* Did the file descriptor bug occur? */
+ || fstat(fileno(stdout), &st) != 0
+ );
+ }
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ac_cv_func_vfork_works=yes
+else
+ ac_cv_func_vfork_works=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_vfork_works" >&5
+$as_echo "$ac_cv_func_vfork_works" >&6; }
+
+fi;
+if test "x$ac_cv_func_fork_works" = xcross; then
+ ac_cv_func_vfork_works=$ac_cv_func_vfork
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5
+$as_echo "$as_me: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&2;}
+fi
+
+if test "x$ac_cv_func_vfork_works" = xyes; then
+
+$as_echo "@%:@define HAVE_WORKING_VFORK 1" >>confdefs.h
+
+else
+
+$as_echo "@%:@define vfork fork" >>confdefs.h
+
+fi
+if test "x$ac_cv_func_fork_works" = xyes; then
+
+$as_echo "@%:@define HAVE_WORKING_FORK 1" >>confdefs.h
+
+fi
+
+for ac_func in strchr \
+ memcpy \
+ strtol \
+ strtoul \
+ select \
+ poll \
+ qsort \
+ getuid \
+ getpwuid \
+ getpwnam \
+ gettimeofday \
+ tmpfile \
+ uname \
+ rename \
+ read \
+ signal \
+ setjmp \
+ chown \
+ wait4 \
+ waitpid \
+ wait \
+ srandom \
+ popen \
+ pclose \
+ fsync \
+ truncate \
+ listen \
+ wcwidth \
+ mbstowcs \
+ wcrtomb \
+ putenv \
+ setenv
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing gethostname" >&5
+$as_echo_n "checking for library containing gethostname... " >&6; }
+if ${ac_cv_search_gethostname+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char gethostname ();
+int
+main ()
+{
+return gethostname ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' nsl; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_gethostname=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_gethostname+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_gethostname+:} false; then :
+
+else
+ ac_cv_search_gethostname=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_gethostname" >&5
+$as_echo "$ac_cv_search_gethostname" >&6; }
+ac_res=$ac_cv_search_gethostname
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing socket" >&5
+$as_echo_n "checking for library containing socket... " >&6; }
+if ${ac_cv_search_socket+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char socket ();
+int
+main ()
+{
+return socket ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' socket; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_socket=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_socket+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_socket+:} false; then :
+
+else
+ ac_cv_search_socket=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_socket" >&5
+$as_echo "$ac_cv_search_socket" >&6; }
+ac_res=$ac_cv_search_socket
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+else
+
+ WEB_BUILD=
+
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing bind" >&5
+$as_echo_n "checking for library containing bind... " >&6; }
+if ${ac_cv_search_bind+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char bind ();
+int
+main ()
+{
+return bind ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' bind; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_bind=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_bind+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_bind+:} false; then :
+
+else
+ ac_cv_search_bind=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_bind" >&5
+$as_echo "$ac_cv_search_bind" >&6; }
+ac_res=$ac_cv_search_bind
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+else
+
+ WEB_BUILD=
+
+fi
+
+
+for ac_func in sigaction sigemptyset sigaddset sigprocmask
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+
+$as_echo "@%:@define POSIX_SIGNALS /**/" >>confdefs.h
+
+
+else
+
+ for ac_func in sigset sigrelse
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+
+$as_echo "@%:@define SYSV_SIGNALS /**/" >>confdefs.h
+
+
+fi
+done
+
+
+fi
+done
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing syslog" >&5
+$as_echo_n "checking for library containing syslog... " >&6; }
+if ${ac_cv_search_syslog+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char syslog ();
+int
+main ()
+{
+return syslog ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' bsd socket inet; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_syslog=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_syslog+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_syslog+:} false; then :
+
+else
+ ac_cv_search_syslog=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_syslog" >&5
+$as_echo "$ac_cv_search_syslog" >&6; }
+ac_res=$ac_cv_search_syslog
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+
+$as_echo "@%:@define HAVE_SYSLOG 1" >>confdefs.h
+
+
+fi
+
+
+
+case "$host" in
+ *-linux-gnu*|*-k*bsd*-gnu*)
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ if test -f /etc/fedora-release ; then
+ systype="LFD"
+ if test -d /etc/pki/tls ; then
+ alpine_c_client_target="lfd"
+ else
+ alpine_c_client_target="lrh"
+ fi
+ elif test -f /etc/mandrake-release ; then
+ systype="LMD"
+ alpine_c_client_target="lmd"
+ elif test -f /etc/redhat-release -o -f /etc/redhat_version ; then
+ systype="LRH"
+ if test -d /etc/pki/tls ; then
+ alpine_c_client_target="lr5"
+ else
+ alpine_c_client_target="lrh"
+ fi
+ elif test -f /etc/debian_release -o -f /etc/debian_version ; then
+ if test -d /etc/osso-af-init ; then
+ systype="LN8"
+ alpine_c_client_target="ln8"
+ else
+ systype="DEB"
+ alpine_c_client_target="ldb"
+ fi
+ elif test -f /etc/SuSE-release ; then
+ systype="LSU"
+ alpine_c_client_target="lsu"
+ else
+ systype="LNX"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pam_start in -lpam" >&5
+$as_echo_n "checking for pam_start in -lpam... " >&6; }
+if ${ac_cv_lib_pam_pam_start+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpam $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pam_start ();
+int
+main ()
+{
+return pam_start ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_pam_pam_start=yes
+else
+ ac_cv_lib_pam_pam_start=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pam_pam_start" >&5
+$as_echo "$ac_cv_lib_pam_pam_start" >&6; }
+if test "x$ac_cv_lib_pam_pam_start" = xyes; then :
+
+ alpine_c_client_target="lnp"
+
+else
+
+ if test -f /etc/shadow ; then
+ alpine_c_client_target="slx"
+ else
+ alpine_c_client_target="lnx"
+ fi
+
+fi
+
+ fi
+ ;;
+ *-apple-darwin*)
+ systype="OSX"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ LIBS="$LIBS -framework Carbon -framework ApplicationServices -framework Security"
+ AM_CFLAGS="$AM_CFLAGS -Dbsd"
+
+$as_echo "@%:@define OSX_TARGET 1" >>confdefs.h
+
+ case "$alpine_os_credential_cache" in
+ no)
+ ;;
+ *)
+
+$as_echo "@%:@define APPLEKEYCHAIN 1" >>confdefs.h
+
+ ;;
+ esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pam_start in -lpam" >&5
+$as_echo_n "checking for pam_start in -lpam... " >&6; }
+if ${ac_cv_lib_pam_pam_start+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpam $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pam_start ();
+int
+main ()
+{
+return pam_start ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_pam_pam_start=yes
+else
+ ac_cv_lib_pam_pam_start=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pam_pam_start" >&5
+$as_echo "$ac_cv_lib_pam_pam_start" >&6; }
+if test "x$ac_cv_lib_pam_pam_start" = xyes; then :
+
+ alpine_c_client_target="oxp"
+
+else
+
+ alpine_c_client_target="osx"
+
+fi
+
+ ;;
+ *-*-solaris*)
+ if test x$GCC = "xyes" ; then
+ systype="GSO"
+ alpine_c_client_target="gso"
+ else
+ systype="SOC"
+ alpine_c_client_target="soc"
+
+$as_echo "@%:@define __EXTENSIONS__ 1" >>confdefs.h
+
+ fi
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ ;;
+ *-*-sunos4*)
+ systype="SUN"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="s40"
+ ;;
+ *-*-sco3.2v5*)
+ if test x$GCC = "xyes" ; then
+ systype="GO5"
+ alpine_c_client_target="go5"
+ else
+ systype="SC5"
+ alpine_c_client_target="sc5"
+ fi
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ ;;
+ *-next-*)
+ systype="NXT"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="nx3"
+ ;;
+ *-*-netbsd*)
+ systype="NEB"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="neb"
+ ;;
+ *-*-dragonfly*)
+ systype="DFB"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="neb"
+ ;;
+ *-*-bsdi*)
+ systype="BSI"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="bsi"
+ ;;
+ *-*-freebsd*)
+ systype="BSF"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="bsf"
+ LIBS="$LIBS $LIBINTL"
+ ;;
+ *-*-openbsd*)
+ systype="BSO"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="bso"
+ LIBS="$LIBS $LIBINTL"
+ ;;
+ *-*-aix5*)
+ systype="A52"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="a52"
+ ;;
+ *-*-aix4*)
+ systype="A41"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="a41"
+ ;;
+ *-*-aix3*)
+ systype="A32"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="a32"
+ ;;
+ *-*UNIX_SV | *-*-sysv5UnixWare7* | *-*OpenUNIX*)
+ systype="UW2"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="uw2"
+ ;;
+ *-*-osf5*)
+ systype="OSF"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="osf"
+ ;;
+ *-*-cygwin)
+ systype="CYG"
+ alpine_path_delim="\\\\"
+ alpine_mode_readonly="(S_IREAD | S_IWRITE)"
+ alpine_c_client_target="cyg"
+ LIBS="$LIBS $LIBINTL"
+ ;;
+ windows* | *-*-pw32*)
+ systype="WNT"
+ alpine_path_delim="\\"
+ alpine_mode_readonly="(S_IREAD | S_IWRITE)"
+ alpine_c_client_target="wnt"
+
+$as_echo "@%:@define _WINDOWS 1" >>confdefs.h
+
+ ;;
+ *)
+ as_fn_error $? "Unrecognized system: $host" "$LINENO" 5
+ ;;
+esac
+
+if test -n "$alpine_with_local_maildir" ; then
+ alpine_local_maildir=$alpine_with_local_maildir
+elif test -d /var/spool/mail ; then
+ alpine_local_maildir="/var/spool/mail"
+elif test -d /var/mail ; then
+ alpine_local_maildir="/var/mail"
+else
+ alpine_local_maildir="/usr/spool/mail"
+fi
+
+if test -n "$alpine_with_c_client_target" ; then
+ alpine_c_client_target=$alpine_with_c_client_target
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SYSTYPE "$systype"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define C_FILESEP '$alpine_path_delim'
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define S_FILESEP "$alpine_path_delim"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define MAILDIR "$alpine_local_maildir"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define MODE_READONLY $alpine_mode_readonly
+_ACEOF
+
+
+C_CLIENT_TARGET=$alpine_c_client_target
+
+C_CLIENT_WITH_IPV6=$c_client_ip6
+
+if test "x$alpine_SSLTYPE" = "xnone" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * NOT Including SSL Support" >&5
+$as_echo "$as_me: * * * NOT Including SSL Support" >&6;}
+ c_client_specials="${c_client_specials}SSLTYPE=none "
+else
+
+ if test -n "$alpine_SSLCERTS" -a -d "$alpine_SSLCERTS" ; then
+ certdir="$alpine_SSLCERTS"
+ elif test -n "$alpine_SSLDIR" -a -d "${alpine_SSLDIR}/certs" ; then
+ certdir="${alpine_SSLDIR}/certs"
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: SSL Problem: certificate directory not found" >&5
+$as_echo "$as_me: SSL Problem: certificate directory not found" >&6;}
+ fi
+
+ if test "x$with_smime" != "xno" ; then
+ if test -n "$certdir" ; then
+
+$as_echo "@%:@define SMIME /**/" >>confdefs.h
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SMIME_SSLCERTS "$certdir"
+_ACEOF
+
+ fi
+ fi
+
+ if test ! -f ${certdir}/factory.pem ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * SSL file \"${certdir}/factory.pem\" is missing." >&5
+$as_echo "$as_me: * * * SSL file \"${certdir}/factory.pem\" is missing." >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * This might indicate that CA certs did not get properly" >&5
+$as_echo "$as_me: * * * This might indicate that CA certs did not get properly" >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * installed. If you get certificate validation failures" >&5
+$as_echo "$as_me: * * * installed. If you get certificate validation failures" >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * in Alpine, this might be the reason for them." >&5
+$as_echo "$as_me: * * * in Alpine, this might be the reason for them." >&6;}
+ fi
+
+ if test -z "`ls ${certdir} | $EGREP '^@<:@0-9A-Fa-f@:>@{8}\.@<:@0-9@:>@'`" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * No 8-hexdigit symlinks in certificate directory \"${certdir}\"." >&5
+$as_echo "$as_me: * * * No 8-hexdigit symlinks in certificate directory \"${certdir}\"." >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * This might indicate that CA certs did not get properly" >&5
+$as_echo "$as_me: * * * This might indicate that CA certs did not get properly" >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * installed. If you get certificate validation failures" >&5
+$as_echo "$as_me: * * * installed. If you get certificate validation failures" >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * in Alpine, this might be the reason for them." >&5
+$as_echo "$as_me: * * * in Alpine, this might be the reason for them." >&6;}
+ fi
+
+ if test -n "$alpine_SSLDIR" ; then
+ c_client_specials="${c_client_specials}SSLDIR=$alpine_SSLDIR "
+ fi
+
+ if test -n "$alpine_SSLCERTS" ; then
+ c_client_specials="${c_client_specials}SSLCERTS=$alpine_SSLCERTS "
+ fi
+
+ if test -n "$alpine_SSLINCLUDE" ; then
+ c_client_specials="${c_client_specials}SSLINCLUDE=$alpine_SSLINCLUDE "
+ fi
+
+ if test -n "$alpine_SSLLIB" ; then
+ c_client_specials="${c_client_specials}SSLLIB=$alpine_SSLLIB "
+ fi
+
+fi
+
+if test "x$alpine_GSSTYPE" != "xnone" ; then
+ c_client_specials="${c_client_specials}EXTRAAUTHENTICATORS=gss "
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * Including Kerberos5 functionality" >&5
+$as_echo "$as_me: * * * Including Kerberos5 functionality" >&6;}
+fi
+
+if test -n "$CPPFLAGS" ; then
+ alpine_c_client_cflags="$alpine_c_client_cflags ${CPPFLAGS}"
+fi
+if test -n "$CFLAGS" ; then
+ alpine_c_client_cflags="$alpine_c_client_cflags ${CFLAGS}"
+fi
+
+if test -n "$alpine_c_client_cflags" ; then
+ C_CLIENT_CFLAGS=EXTRACFLAGS=\"$alpine_c_client_cflags\"
+
+fi
+
+if test -n "$LDFLAGS" ; then
+ alpine_c_client_ldflags="$alpine_c_client_ldflags ${LDFLAGS}"
+fi
+if test -n "$LIBS" ; then
+ alpine_c_client_ldflags="$alpine_c_client_ldflags ${LIBS}"
+fi
+
+if test -n "$alpine_c_client_ldflags" ; then
+ C_CLIENT_LDFLAGS=EXTRALDFLAGS=\"$alpine_c_client_ldflags\"
+
+fi
+
+if test -n "$alpine_c_client_gccoptlevel" ; then
+ C_CLIENT_GCCOPTLEVEL=GCCOPTLEVEL=\"$alpine_c_client_gccoptlevel\"
+
+fi
+
+C_CLIENT_SPECIALS=$c_client_specials
+
+
+if test -z "$WEB_BUILD" ; then
+ WEB_PUBCOOKIE_BUILD=
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * TCL libraries could not be found." >&5
+$as_echo "$as_me: * * * TCL libraries could not be found." >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * WEB ALPINE COMPONENT WILL NOT BE BUILT." >&5
+$as_echo "$as_me: * * * WEB ALPINE COMPONENT WILL NOT BE BUILT." >&6;}
+else
+ if test -n "$WEB_PUBCOOKIE_BUILD" ; then
+ if test "x$alpine_GSSTYPE" = "xnone" ; then
+ WEB_PUBCOOKIE_BUILD=
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * Kerberos5 support not found." >&5
+$as_echo "$as_me: * * * Kerberos5 support not found." >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * WEB ALPINE PUBCOOKIE COMPONENT WILL NOT BE BUILT." >&5
+$as_echo "$as_me: * * * WEB ALPINE PUBCOOKIE COMPONENT WILL NOT BE BUILT." >&6;}
+ elif test -z "$WEB_BINDIR" ; then
+ WEB_PUBCOOKIE_BUILD=
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * Web Alpine binary directory not provided." >&5
+$as_echo "$as_me: * * * Web Alpine binary directory not provided." >&6;}
+ as_fn_error $? "* * * --with-pubcookie requires --with-web-bin=PATH.
+ Please re-run configure with these options:
+ --with-pubcookie --with-web-bin=/usr/local/libexec/alpine/bin" "$LINENO" 5
+ else
+
+$as_echo "@%:@define PUBCOOKIE 1" >>confdefs.h
+
+ WEB_PUBCOOKIE_LIB=../pubcookie/libauthgssproxy.a
+ WEB_PUBCOOKIE_LINK=gssapi_proxy.l
+ fi
+ fi
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+ac_config_files="$ac_config_files m4/Makefile po/Makefile.in regex/Makefile pith/osdep/Makefile pith/charconv/Makefile pith/Makefile pico/osdep/Makefile pico/Makefile alpine/osdep/Makefile alpine/Makefile web/src/Makefile web/src/pubcookie/Makefile web/src/alpined.d/Makefile Makefile"
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems. If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+ for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+ *) { eval $ac_var=; unset $ac_var;} ;;
+ esac ;;
+ esac
+ done
+
+ (set) 2>&1 |
+ case $as_nl`(ac_space=' '; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ # `set' does not quote correctly, so add quotes: double-quote
+ # substitution turns \\\\ into \\, and sed turns \\ into \.
+ sed -n \
+ "s/'/'\\\\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+ ;; #(
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+) |
+ sed '
+ /^ac_cv_env_/b end
+ t clear
+ :clear
+ s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+ t end
+ s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+ :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+ if test -w "$cache_file"; then
+ if test "x$cache_file" != "x/dev/null"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+$as_echo "$as_me: updating cache $cache_file" >&6;}
+ if test ! -f "$cache_file" || test -h "$cache_file"; then
+ cat confcache >"$cache_file"
+ else
+ case $cache_file in #(
+ */* | ?:*)
+ mv -f confcache "$cache_file"$$ &&
+ mv -f "$cache_file"$$ "$cache_file" ;; #(
+ *)
+ mv -f confcache "$cache_file" ;;
+ esac
+ fi
+ fi
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+ fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+U=
+for ac_i in : $LIB@&t@OBJS; do test "x$ac_i" = x: && continue
+ # 1. Remove the extension, and $U if already installed.
+ ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+ ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
+ # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR
+ # will be set to the directory where LIBOBJS objects are built.
+ as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+ as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIB@&t@OBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+ if test -n "$EXEEXT"; then
+ am__EXEEXT_TRUE=
+ am__EXEEXT_FALSE='#'
+else
+ am__EXEEXT_TRUE='#'
+ am__EXEEXT_FALSE=
+fi
+
+if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then
+ as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then
+ as_fn_error $? "conditional \"AMDEP\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
+ as_fn_error $? "conditional \"am__fastdepCC\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+
+: "${CONFIG_STATUS=./config.status}"
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
+as_write_fail=0
+cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in @%:@(
+ *posix*) :
+ set -o posix ;; @%:@(
+ *) :
+ ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='print -r --'
+ as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='printf %s\n'
+ as_echo_n='printf %s'
+else
+ if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+ as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+ as_echo_n='/usr/ucb/echo -n'
+ else
+ as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+ as_echo_n_body='eval
+ arg=$1;
+ case $arg in @%:@(
+ *"$as_nl"*)
+ expr "X$arg" : "X\\(.*\\)$as_nl";
+ arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+ esac;
+ expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+ '
+ export as_echo_n_body
+ as_echo_n='sh -c $as_echo_n_body as_echo'
+ fi
+ export as_echo_body
+ as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+as_myself=
+case $0 in @%:@((
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there. '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+
+@%:@ as_fn_error STATUS ERROR [LINENO LOG_FD]
+@%:@ ----------------------------------------
+@%:@ Output "`basename @S|@0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+@%:@ provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+@%:@ script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+ as_status=$1; test $as_status -eq 0 && as_status=1
+ if test "$4"; then
+ as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+ fi
+ $as_echo "$as_me: error: $2" >&2
+ as_fn_exit $as_status
+} @%:@ as_fn_error
+
+
+@%:@ as_fn_set_status STATUS
+@%:@ -----------------------
+@%:@ Set @S|@? to STATUS, without forking.
+as_fn_set_status ()
+{
+ return $1
+} @%:@ as_fn_set_status
+
+@%:@ as_fn_exit STATUS
+@%:@ -----------------
+@%:@ Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+ set +e
+ as_fn_set_status $1
+ exit $1
+} @%:@ as_fn_exit
+
+@%:@ as_fn_unset VAR
+@%:@ ---------------
+@%:@ Portably unset VAR.
+as_fn_unset ()
+{
+ { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+@%:@ as_fn_append VAR VALUE
+@%:@ ----------------------
+@%:@ Append the text in VALUE to the end of the definition contained in VAR. Take
+@%:@ advantage of any shell optimizations that allow amortized linear growth over
+@%:@ repeated appends, instead of the typical quadratic growth present in naive
+@%:@ implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+ eval 'as_fn_append ()
+ {
+ eval $1+=\$2
+ }'
+else
+ as_fn_append ()
+ {
+ eval $1=\$$1\$2
+ }
+fi # as_fn_append
+
+@%:@ as_fn_arith ARG...
+@%:@ ------------------
+@%:@ Perform arithmetic evaluation on the ARGs, and store the result in the
+@%:@ global @S|@as_val. Take advantage of shells that can avoid forks. The arguments
+@%:@ must be portable across @S|@(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+ eval 'as_fn_arith ()
+ {
+ as_val=$(( $* ))
+ }'
+else
+ as_fn_arith ()
+ {
+ as_val=`expr "$@" || test $? -eq 1`
+ }
+fi # as_fn_arith
+
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in @%:@(((((
+-n*)
+ case `echo 'xy\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ xy) ECHO_C='\c';;
+ *) echo `echo ksh88 bug on AIX 6.1` > /dev/null
+ ECHO_T=' ';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+ if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -p'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -p'
+ elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+ else
+ as_ln_s='cp -p'
+ fi
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+
+@%:@ as_fn_mkdir_p
+@%:@ -------------
+@%:@ Create "@S|@as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || eval $as_mkdir_p || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} @%:@ as_fn_mkdir_p
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p='mkdir -p "$as_dir"'
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+if test -x / >/dev/null 2>&1; then
+ as_test_x='test -x'
+else
+ if ls -dL / >/dev/null 2>&1; then
+ as_ls_L_option=L
+ else
+ as_ls_L_option=
+ fi
+ as_test_x='
+ eval sh -c '\''
+ if test -d "$1"; then
+ test -d "$1/.";
+ else
+ case $1 in @%:@(
+ -*)set "./$1";;
+ esac;
+ case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in @%:@((
+ ???[sx]*):;;*)false;;esac;fi
+ '\'' sh
+ '
+fi
+as_executable_p=$as_test_x
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+## ----------------------------------- ##
+## Main body of $CONFIG_STATUS script. ##
+## ----------------------------------- ##
+_ASEOF
+test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# Save the log message, to keep $0 and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by alpine $as_me 2.10, which was
+generated by GNU Autoconf 2.68. Invocation command line was
+
+ CONFIG_FILES = $CONFIG_FILES
+ CONFIG_HEADERS = $CONFIG_HEADERS
+ CONFIG_LINKS = $CONFIG_LINKS
+ CONFIG_COMMANDS = $CONFIG_COMMANDS
+ $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+case $ac_config_headers in *"
+"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
+esac
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+config_headers="$ac_config_headers"
+config_commands="$ac_config_commands"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files and other configuration actions
+from templates according to the current configuration. Unless the files
+and actions are specified as TAGs, all are instantiated by default.
+
+Usage: $0 [OPTION]... [TAG]...
+
+ -h, --help print this help, then exit
+ -V, --version print version number and configuration settings, then exit
+ --config print configuration, then exit
+ -q, --quiet, --silent
+ do not print progress messages
+ -d, --debug don't remove temporary files
+ --recheck update $as_me by reconfiguring in the same conditions
+ --file=FILE[:TEMPLATE]
+ instantiate the configuration file FILE
+ --header=FILE[:TEMPLATE]
+ instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Configuration commands:
+$config_commands
+
+Report bugs to <alpine-contact@u.washington.edu>."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
+ac_cs_version="\\
+alpine config.status 2.10
+configured by $0, generated by GNU Autoconf 2.68,
+ with options \\"\$ac_cs_config\\"
+
+Copyright (C) 2010 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+INSTALL='$INSTALL'
+MKDIR_P='$MKDIR_P'
+AWK='$AWK'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+ case $1 in
+ --*=?*)
+ ac_option=`expr "X$1" : 'X\([^=]*\)='`
+ ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+ ac_shift=:
+ ;;
+ --*=)
+ ac_option=`expr "X$1" : 'X\([^=]*\)='`
+ ac_optarg=
+ ac_shift=:
+ ;;
+ *)
+ ac_option=$1
+ ac_optarg=$2
+ ac_shift=shift
+ ;;
+ esac
+
+ case $ac_option in
+ # Handling of the options.
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ ac_cs_recheck=: ;;
+ --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+ $as_echo "$ac_cs_version"; exit ;;
+ --config | --confi | --conf | --con | --co | --c )
+ $as_echo "$ac_cs_config"; exit ;;
+ --debug | --debu | --deb | --de | --d | -d )
+ debug=: ;;
+ --file | --fil | --fi | --f )
+ $ac_shift
+ case $ac_optarg in
+ *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ '') as_fn_error $? "missing file argument" ;;
+ esac
+ as_fn_append CONFIG_FILES " '$ac_optarg'"
+ ac_need_defaults=false;;
+ --header | --heade | --head | --hea )
+ $ac_shift
+ case $ac_optarg in
+ *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ as_fn_append CONFIG_HEADERS " '$ac_optarg'"
+ ac_need_defaults=false;;
+ --he | --h)
+ # Conflict between --help and --header
+ as_fn_error $? "ambiguous option: \`$1'
+Try \`$0 --help' for more information.";;
+ --help | --hel | -h )
+ $as_echo "$ac_cs_usage"; exit ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil | --si | --s)
+ ac_cs_silent=: ;;
+
+ # This is an error.
+ -*) as_fn_error $? "unrecognized option: \`$1'
+Try \`$0 --help' for more information." ;;
+
+ *) as_fn_append ac_config_targets " $1"
+ ac_need_defaults=false ;;
+
+ esac
+ shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+ exec 6>/dev/null
+ ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+ set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+ shift
+ \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
+ CONFIG_SHELL='$SHELL'
+ export CONFIG_SHELL
+ exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+ echo
+ sed 'h;s/./-/g;s/^.../@%:@@%:@ /;s/...$/ @%:@@%:@/;p;x;p;x' <<_ASBOX
+@%:@@%:@ Running $as_me. @%:@@%:@
+_ASBOX
+ $as_echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+#
+# INIT-COMMANDS
+#
+AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"
+
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+sed_quote_subst='$sed_quote_subst'
+double_quote_subst='$double_quote_subst'
+delay_variable_subst='$delay_variable_subst'
+macro_version='`$ECHO "X$macro_version" | $Xsed -e "$delay_single_quote_subst"`'
+macro_revision='`$ECHO "X$macro_revision" | $Xsed -e "$delay_single_quote_subst"`'
+enable_shared='`$ECHO "X$enable_shared" | $Xsed -e "$delay_single_quote_subst"`'
+enable_static='`$ECHO "X$enable_static" | $Xsed -e "$delay_single_quote_subst"`'
+pic_mode='`$ECHO "X$pic_mode" | $Xsed -e "$delay_single_quote_subst"`'
+enable_fast_install='`$ECHO "X$enable_fast_install" | $Xsed -e "$delay_single_quote_subst"`'
+host_alias='`$ECHO "X$host_alias" | $Xsed -e "$delay_single_quote_subst"`'
+host='`$ECHO "X$host" | $Xsed -e "$delay_single_quote_subst"`'
+host_os='`$ECHO "X$host_os" | $Xsed -e "$delay_single_quote_subst"`'
+build_alias='`$ECHO "X$build_alias" | $Xsed -e "$delay_single_quote_subst"`'
+build='`$ECHO "X$build" | $Xsed -e "$delay_single_quote_subst"`'
+build_os='`$ECHO "X$build_os" | $Xsed -e "$delay_single_quote_subst"`'
+SED='`$ECHO "X$SED" | $Xsed -e "$delay_single_quote_subst"`'
+Xsed='`$ECHO "X$Xsed" | $Xsed -e "$delay_single_quote_subst"`'
+GREP='`$ECHO "X$GREP" | $Xsed -e "$delay_single_quote_subst"`'
+EGREP='`$ECHO "X$EGREP" | $Xsed -e "$delay_single_quote_subst"`'
+FGREP='`$ECHO "X$FGREP" | $Xsed -e "$delay_single_quote_subst"`'
+LD='`$ECHO "X$LD" | $Xsed -e "$delay_single_quote_subst"`'
+NM='`$ECHO "X$NM" | $Xsed -e "$delay_single_quote_subst"`'
+LN_S='`$ECHO "X$LN_S" | $Xsed -e "$delay_single_quote_subst"`'
+max_cmd_len='`$ECHO "X$max_cmd_len" | $Xsed -e "$delay_single_quote_subst"`'
+ac_objext='`$ECHO "X$ac_objext" | $Xsed -e "$delay_single_quote_subst"`'
+exeext='`$ECHO "X$exeext" | $Xsed -e "$delay_single_quote_subst"`'
+lt_unset='`$ECHO "X$lt_unset" | $Xsed -e "$delay_single_quote_subst"`'
+lt_SP2NL='`$ECHO "X$lt_SP2NL" | $Xsed -e "$delay_single_quote_subst"`'
+lt_NL2SP='`$ECHO "X$lt_NL2SP" | $Xsed -e "$delay_single_quote_subst"`'
+reload_flag='`$ECHO "X$reload_flag" | $Xsed -e "$delay_single_quote_subst"`'
+reload_cmds='`$ECHO "X$reload_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+OBJDUMP='`$ECHO "X$OBJDUMP" | $Xsed -e "$delay_single_quote_subst"`'
+deplibs_check_method='`$ECHO "X$deplibs_check_method" | $Xsed -e "$delay_single_quote_subst"`'
+file_magic_cmd='`$ECHO "X$file_magic_cmd" | $Xsed -e "$delay_single_quote_subst"`'
+AR='`$ECHO "X$AR" | $Xsed -e "$delay_single_quote_subst"`'
+AR_FLAGS='`$ECHO "X$AR_FLAGS" | $Xsed -e "$delay_single_quote_subst"`'
+STRIP='`$ECHO "X$STRIP" | $Xsed -e "$delay_single_quote_subst"`'
+RANLIB='`$ECHO "X$RANLIB" | $Xsed -e "$delay_single_quote_subst"`'
+old_postinstall_cmds='`$ECHO "X$old_postinstall_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+old_postuninstall_cmds='`$ECHO "X$old_postuninstall_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+old_archive_cmds='`$ECHO "X$old_archive_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+CC='`$ECHO "X$CC" | $Xsed -e "$delay_single_quote_subst"`'
+CFLAGS='`$ECHO "X$CFLAGS" | $Xsed -e "$delay_single_quote_subst"`'
+compiler='`$ECHO "X$compiler" | $Xsed -e "$delay_single_quote_subst"`'
+GCC='`$ECHO "X$GCC" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_pipe='`$ECHO "X$lt_cv_sys_global_symbol_pipe" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_cdecl='`$ECHO "X$lt_cv_sys_global_symbol_to_cdecl" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "X$lt_cv_sys_global_symbol_to_c_name_address" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "X$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $Xsed -e "$delay_single_quote_subst"`'
+objdir='`$ECHO "X$objdir" | $Xsed -e "$delay_single_quote_subst"`'
+SHELL='`$ECHO "X$SHELL" | $Xsed -e "$delay_single_quote_subst"`'
+ECHO='`$ECHO "X$ECHO" | $Xsed -e "$delay_single_quote_subst"`'
+MAGIC_CMD='`$ECHO "X$MAGIC_CMD" | $Xsed -e "$delay_single_quote_subst"`'
+lt_prog_compiler_no_builtin_flag='`$ECHO "X$lt_prog_compiler_no_builtin_flag" | $Xsed -e "$delay_single_quote_subst"`'
+lt_prog_compiler_wl='`$ECHO "X$lt_prog_compiler_wl" | $Xsed -e "$delay_single_quote_subst"`'
+lt_prog_compiler_pic='`$ECHO "X$lt_prog_compiler_pic" | $Xsed -e "$delay_single_quote_subst"`'
+lt_prog_compiler_static='`$ECHO "X$lt_prog_compiler_static" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_prog_compiler_c_o='`$ECHO "X$lt_cv_prog_compiler_c_o" | $Xsed -e "$delay_single_quote_subst"`'
+need_locks='`$ECHO "X$need_locks" | $Xsed -e "$delay_single_quote_subst"`'
+DSYMUTIL='`$ECHO "X$DSYMUTIL" | $Xsed -e "$delay_single_quote_subst"`'
+NMEDIT='`$ECHO "X$NMEDIT" | $Xsed -e "$delay_single_quote_subst"`'
+LIPO='`$ECHO "X$LIPO" | $Xsed -e "$delay_single_quote_subst"`'
+OTOOL='`$ECHO "X$OTOOL" | $Xsed -e "$delay_single_quote_subst"`'
+OTOOL64='`$ECHO "X$OTOOL64" | $Xsed -e "$delay_single_quote_subst"`'
+libext='`$ECHO "X$libext" | $Xsed -e "$delay_single_quote_subst"`'
+shrext_cmds='`$ECHO "X$shrext_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+extract_expsyms_cmds='`$ECHO "X$extract_expsyms_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+archive_cmds_need_lc='`$ECHO "X$archive_cmds_need_lc" | $Xsed -e "$delay_single_quote_subst"`'
+enable_shared_with_static_runtimes='`$ECHO "X$enable_shared_with_static_runtimes" | $Xsed -e "$delay_single_quote_subst"`'
+export_dynamic_flag_spec='`$ECHO "X$export_dynamic_flag_spec" | $Xsed -e "$delay_single_quote_subst"`'
+whole_archive_flag_spec='`$ECHO "X$whole_archive_flag_spec" | $Xsed -e "$delay_single_quote_subst"`'
+compiler_needs_object='`$ECHO "X$compiler_needs_object" | $Xsed -e "$delay_single_quote_subst"`'
+old_archive_from_new_cmds='`$ECHO "X$old_archive_from_new_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+old_archive_from_expsyms_cmds='`$ECHO "X$old_archive_from_expsyms_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+archive_cmds='`$ECHO "X$archive_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+archive_expsym_cmds='`$ECHO "X$archive_expsym_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+module_cmds='`$ECHO "X$module_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+module_expsym_cmds='`$ECHO "X$module_expsym_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+with_gnu_ld='`$ECHO "X$with_gnu_ld" | $Xsed -e "$delay_single_quote_subst"`'
+allow_undefined_flag='`$ECHO "X$allow_undefined_flag" | $Xsed -e "$delay_single_quote_subst"`'
+no_undefined_flag='`$ECHO "X$no_undefined_flag" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_libdir_flag_spec='`$ECHO "X$hardcode_libdir_flag_spec" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_libdir_flag_spec_ld='`$ECHO "X$hardcode_libdir_flag_spec_ld" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_libdir_separator='`$ECHO "X$hardcode_libdir_separator" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_direct='`$ECHO "X$hardcode_direct" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_direct_absolute='`$ECHO "X$hardcode_direct_absolute" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_minus_L='`$ECHO "X$hardcode_minus_L" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_shlibpath_var='`$ECHO "X$hardcode_shlibpath_var" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_automatic='`$ECHO "X$hardcode_automatic" | $Xsed -e "$delay_single_quote_subst"`'
+inherit_rpath='`$ECHO "X$inherit_rpath" | $Xsed -e "$delay_single_quote_subst"`'
+link_all_deplibs='`$ECHO "X$link_all_deplibs" | $Xsed -e "$delay_single_quote_subst"`'
+fix_srcfile_path='`$ECHO "X$fix_srcfile_path" | $Xsed -e "$delay_single_quote_subst"`'
+always_export_symbols='`$ECHO "X$always_export_symbols" | $Xsed -e "$delay_single_quote_subst"`'
+export_symbols_cmds='`$ECHO "X$export_symbols_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+exclude_expsyms='`$ECHO "X$exclude_expsyms" | $Xsed -e "$delay_single_quote_subst"`'
+include_expsyms='`$ECHO "X$include_expsyms" | $Xsed -e "$delay_single_quote_subst"`'
+prelink_cmds='`$ECHO "X$prelink_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+file_list_spec='`$ECHO "X$file_list_spec" | $Xsed -e "$delay_single_quote_subst"`'
+variables_saved_for_relink='`$ECHO "X$variables_saved_for_relink" | $Xsed -e "$delay_single_quote_subst"`'
+need_lib_prefix='`$ECHO "X$need_lib_prefix" | $Xsed -e "$delay_single_quote_subst"`'
+need_version='`$ECHO "X$need_version" | $Xsed -e "$delay_single_quote_subst"`'
+version_type='`$ECHO "X$version_type" | $Xsed -e "$delay_single_quote_subst"`'
+runpath_var='`$ECHO "X$runpath_var" | $Xsed -e "$delay_single_quote_subst"`'
+shlibpath_var='`$ECHO "X$shlibpath_var" | $Xsed -e "$delay_single_quote_subst"`'
+shlibpath_overrides_runpath='`$ECHO "X$shlibpath_overrides_runpath" | $Xsed -e "$delay_single_quote_subst"`'
+libname_spec='`$ECHO "X$libname_spec" | $Xsed -e "$delay_single_quote_subst"`'
+library_names_spec='`$ECHO "X$library_names_spec" | $Xsed -e "$delay_single_quote_subst"`'
+soname_spec='`$ECHO "X$soname_spec" | $Xsed -e "$delay_single_quote_subst"`'
+postinstall_cmds='`$ECHO "X$postinstall_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+postuninstall_cmds='`$ECHO "X$postuninstall_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+finish_cmds='`$ECHO "X$finish_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+finish_eval='`$ECHO "X$finish_eval" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_into_libs='`$ECHO "X$hardcode_into_libs" | $Xsed -e "$delay_single_quote_subst"`'
+sys_lib_search_path_spec='`$ECHO "X$sys_lib_search_path_spec" | $Xsed -e "$delay_single_quote_subst"`'
+sys_lib_dlsearch_path_spec='`$ECHO "X$sys_lib_dlsearch_path_spec" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_action='`$ECHO "X$hardcode_action" | $Xsed -e "$delay_single_quote_subst"`'
+enable_dlopen='`$ECHO "X$enable_dlopen" | $Xsed -e "$delay_single_quote_subst"`'
+enable_dlopen_self='`$ECHO "X$enable_dlopen_self" | $Xsed -e "$delay_single_quote_subst"`'
+enable_dlopen_self_static='`$ECHO "X$enable_dlopen_self_static" | $Xsed -e "$delay_single_quote_subst"`'
+old_striplib='`$ECHO "X$old_striplib" | $Xsed -e "$delay_single_quote_subst"`'
+striplib='`$ECHO "X$striplib" | $Xsed -e "$delay_single_quote_subst"`'
+
+LTCC='$LTCC'
+LTCFLAGS='$LTCFLAGS'
+compiler='$compiler_DEFAULT'
+
+# Quote evaled strings.
+for var in SED \
+GREP \
+EGREP \
+FGREP \
+LD \
+NM \
+LN_S \
+lt_SP2NL \
+lt_NL2SP \
+reload_flag \
+OBJDUMP \
+deplibs_check_method \
+file_magic_cmd \
+AR \
+AR_FLAGS \
+STRIP \
+RANLIB \
+CC \
+CFLAGS \
+compiler \
+lt_cv_sys_global_symbol_pipe \
+lt_cv_sys_global_symbol_to_cdecl \
+lt_cv_sys_global_symbol_to_c_name_address \
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \
+SHELL \
+ECHO \
+lt_prog_compiler_no_builtin_flag \
+lt_prog_compiler_wl \
+lt_prog_compiler_pic \
+lt_prog_compiler_static \
+lt_cv_prog_compiler_c_o \
+need_locks \
+DSYMUTIL \
+NMEDIT \
+LIPO \
+OTOOL \
+OTOOL64 \
+shrext_cmds \
+export_dynamic_flag_spec \
+whole_archive_flag_spec \
+compiler_needs_object \
+with_gnu_ld \
+allow_undefined_flag \
+no_undefined_flag \
+hardcode_libdir_flag_spec \
+hardcode_libdir_flag_spec_ld \
+hardcode_libdir_separator \
+fix_srcfile_path \
+exclude_expsyms \
+include_expsyms \
+file_list_spec \
+variables_saved_for_relink \
+libname_spec \
+library_names_spec \
+soname_spec \
+finish_eval \
+old_striplib \
+striplib; do
+ case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in
+ *[\\\\\\\`\\"\\\$]*)
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$sed_quote_subst\\"\\\`\\\\\\""
+ ;;
+ *)
+ eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+ ;;
+ esac
+done
+
+# Double-quote double-evaled strings.
+for var in reload_cmds \
+old_postinstall_cmds \
+old_postuninstall_cmds \
+old_archive_cmds \
+extract_expsyms_cmds \
+old_archive_from_new_cmds \
+old_archive_from_expsyms_cmds \
+archive_cmds \
+archive_expsym_cmds \
+module_cmds \
+module_expsym_cmds \
+export_symbols_cmds \
+prelink_cmds \
+postinstall_cmds \
+postuninstall_cmds \
+finish_cmds \
+sys_lib_search_path_spec \
+sys_lib_dlsearch_path_spec; do
+ case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in
+ *[\\\\\\\`\\"\\\$]*)
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\""
+ ;;
+ *)
+ eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+ ;;
+ esac
+done
+
+# Fix-up fallback echo if it was mangled by the above quoting rules.
+case \$lt_ECHO in
+*'\\\$0 --fallback-echo"') lt_ECHO=\`\$ECHO "X\$lt_ECHO" | \$Xsed -e 's/\\\\\\\\\\\\\\\$0 --fallback-echo"\$/\$0 --fallback-echo"/'\`
+ ;;
+esac
+
+ac_aux_dir='$ac_aux_dir'
+xsi_shell='$xsi_shell'
+lt_shell_append='$lt_shell_append'
+
+# See if we are running on zsh, and set the options which allow our
+# commands through without removal of \ escapes INIT.
+if test -n "\${ZSH_VERSION+set}" ; then
+ setopt NO_GLOB_SUBST
+fi
+
+
+ PACKAGE='$PACKAGE'
+ VERSION='$VERSION'
+ TIMESTAMP='$TIMESTAMP'
+ RM='$RM'
+ ofile='$ofile'
+
+
+
+# Capture the value of obsolete ALL_LINGUAS because we need it to compute
+ # POFILES, UPDATEPOFILES, DUMMYPOFILES, GMOFILES, CATALOGS. But hide it
+ # from automake < 1.5.
+ eval 'OBSOLETE_ALL_LINGUAS''="$ALL_LINGUAS"'
+ # Capture the value of LINGUAS because we need it to compute CATALOGS.
+ LINGUAS="${LINGUAS-%UNSET%}"
+
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+ case $ac_config_target in
+ "include/config.h") CONFIG_HEADERS="$CONFIG_HEADERS include/config.h" ;;
+ "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;;
+ "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;;
+ "po-directories") CONFIG_COMMANDS="$CONFIG_COMMANDS po-directories" ;;
+ "m4/Makefile") CONFIG_FILES="$CONFIG_FILES m4/Makefile" ;;
+ "po/Makefile.in") CONFIG_FILES="$CONFIG_FILES po/Makefile.in" ;;
+ "regex/Makefile") CONFIG_FILES="$CONFIG_FILES regex/Makefile" ;;
+ "pith/osdep/Makefile") CONFIG_FILES="$CONFIG_FILES pith/osdep/Makefile" ;;
+ "pith/charconv/Makefile") CONFIG_FILES="$CONFIG_FILES pith/charconv/Makefile" ;;
+ "pith/Makefile") CONFIG_FILES="$CONFIG_FILES pith/Makefile" ;;
+ "pico/osdep/Makefile") CONFIG_FILES="$CONFIG_FILES pico/osdep/Makefile" ;;
+ "pico/Makefile") CONFIG_FILES="$CONFIG_FILES pico/Makefile" ;;
+ "alpine/osdep/Makefile") CONFIG_FILES="$CONFIG_FILES alpine/osdep/Makefile" ;;
+ "alpine/Makefile") CONFIG_FILES="$CONFIG_FILES alpine/Makefile" ;;
+ "web/src/Makefile") CONFIG_FILES="$CONFIG_FILES web/src/Makefile" ;;
+ "web/src/pubcookie/Makefile") CONFIG_FILES="$CONFIG_FILES web/src/pubcookie/Makefile" ;;
+ "web/src/alpined.d/Makefile") CONFIG_FILES="$CONFIG_FILES web/src/alpined.d/Makefile" ;;
+ "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+
+ *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+ esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used. Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+ test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+ test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+ test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands
+fi
+
+# Have a temporary directory for convenience. Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+ tmp= ac_tmp=
+ trap 'exit_status=$?
+ : "${ac_tmp:=$tmp}"
+ { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
+' 0
+ trap 'as_fn_exit 1' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+ tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+ test -d "$tmp"
+} ||
+{
+ tmp=./conf$$-$RANDOM
+ (umask 077 && mkdir "$tmp")
+} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
+ac_tmp=$tmp
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr=`echo X | tr X '\015'`
+# On cygwin, bash can eat \r inside `` if the user requested igncr.
+# But we know of no other shell where ac_cr would be empty at this
+# point, so we can use a bashism as a fallback.
+if test "x$ac_cr" = x; then
+ eval ac_cr=\$\'\\r\'
+fi
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+ ac_cs_awk_cr='\\r'
+else
+ ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+ echo "cat >conf$$subs.awk <<_ACEOF" &&
+ echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+ echo "_ACEOF"
+} >conf$$subs.sh ||
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+ . ./conf$$subs.sh ||
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+
+ ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+ if test $ac_delim_n = $ac_delim_num; then
+ break
+ elif $ac_last_try; then
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\)..*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\)..*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+ N
+ s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
+ for (key in S) S_is_set[key] = 1
+ FS = ""
+
+}
+{
+ line = $ 0
+ nfields = split(line, field, "@")
+ substed = 0
+ len = length(field[1])
+ for (i = 2; i < nfields; i++) {
+ key = field[i]
+ keylen = length(key)
+ if (S_is_set[key]) {
+ value = S[key]
+ line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+ len += length(value) + length(field[++i])
+ substed = 1
+ } else
+ len += 1 + keylen
+ }
+
+ print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+ sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+ cat
+fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
+ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
+# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{
+h
+s///
+s/^/:/
+s/[ ]*$/:/
+s/:\$(srcdir):/:/g
+s/:\${srcdir}:/:/g
+s/:@srcdir@:/:/g
+s/^:*//
+s/:*$//
+x
+s/\(=[ ]*\).*/\1/
+G
+s/\n//
+s/^[^=]*=[ ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+# Set up the scripts for CONFIG_HEADERS section.
+# No need to generate them if there are no CONFIG_HEADERS.
+# This happens for instance with `./config.status Makefile'.
+if test -n "$CONFIG_HEADERS"; then
+cat >"$ac_tmp/defines.awk" <<\_ACAWK ||
+BEGIN {
+_ACEOF
+
+# Transform confdefs.h into an awk script `defines.awk', embedded as
+# here-document in config.status, that substitutes the proper values into
+# config.h.in to produce config.h.
+
+# Create a delimiter string that does not exist in confdefs.h, to ease
+# handling of long lines.
+ac_delim='%!_!# '
+for ac_last_try in false false :; do
+ ac_tt=`sed -n "/$ac_delim/p" confdefs.h`
+ if test -z "$ac_tt"; then
+ break
+ elif $ac_last_try; then
+ as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+done
+
+# For the awk script, D is an array of macro values keyed by name,
+# likewise P contains macro parameters if any. Preserve backslash
+# newline sequences.
+
+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
+sed -n '
+s/.\{148\}/&'"$ac_delim"'/g
+t rset
+:rset
+s/^[ ]*#[ ]*define[ ][ ]*/ /
+t def
+d
+:def
+s/\\$//
+t bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3"/p
+s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p
+d
+:bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3\\\\\\n"\\/p
+t cont
+s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
+t cont
+d
+:cont
+n
+s/.\{148\}/&'"$ac_delim"'/g
+t clear
+:clear
+s/\\$//
+t bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/"/p
+d
+:bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
+b cont
+' <confdefs.h | sed '
+s/'"$ac_delim"'/"\\\
+"/g' >>$CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ for (key in D) D_is_set[key] = 1
+ FS = ""
+}
+/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
+ line = \$ 0
+ split(line, arg, " ")
+ if (arg[1] == "#") {
+ defundef = arg[2]
+ mac1 = arg[3]
+ } else {
+ defundef = substr(arg[1], 2)
+ mac1 = arg[2]
+ }
+ split(mac1, mac2, "(") #)
+ macro = mac2[1]
+ prefix = substr(line, 1, index(line, defundef) - 1)
+ if (D_is_set[macro]) {
+ # Preserve the white space surrounding the "#".
+ print prefix "define", macro P[macro] D[macro]
+ next
+ } else {
+ # Replace #undef with comments. This is necessary, for example,
+ # in the case of _POSIX_SOURCE, which is predefined and required
+ # on some systems where configure will not decide to define it.
+ if (defundef == "undef") {
+ print "/*", prefix defundef, macro, "*/"
+ next
+ }
+ }
+}
+{ print }
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
+fi # test -n "$CONFIG_HEADERS"
+
+
+eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS"
+shift
+for ac_tag
+do
+ case $ac_tag in
+ :[FHLC]) ac_mode=$ac_tag; continue;;
+ esac
+ case $ac_mode$ac_tag in
+ :[FHL]*:*);;
+ :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
+ :[FH]-) ac_tag=-:-;;
+ :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+ esac
+ ac_save_IFS=$IFS
+ IFS=:
+ set x $ac_tag
+ IFS=$ac_save_IFS
+ shift
+ ac_file=$1
+ shift
+
+ case $ac_mode in
+ :L) ac_source=$1;;
+ :[FH])
+ ac_file_inputs=
+ for ac_f
+ do
+ case $ac_f in
+ -) ac_f="$ac_tmp/stdin";;
+ *) # Look for the file first in the build tree, then in the source tree
+ # (if the path is not absolute). The absolute path cannot be DOS-style,
+ # because $ac_f cannot contain `:'.
+ test -f "$ac_f" ||
+ case $ac_f in
+ [\\/$]*) false;;
+ *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+ esac ||
+ as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+ esac
+ case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+ as_fn_append ac_file_inputs " '$ac_f'"
+ done
+
+ # Let's still pretend it is `configure' which instantiates (i.e., don't
+ # use $as_me), people would be surprised to read:
+ # /* config.h. Generated by config.status. */
+ configure_input='Generated from '`
+ $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+ `' by configure.'
+ if test x"$ac_file" != x-; then
+ configure_input="$ac_file. $configure_input"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+$as_echo "$as_me: creating $ac_file" >&6;}
+ fi
+ # Neutralize special characters interpreted by sed in replacement strings.
+ case $configure_input in #(
+ *\&* | *\|* | *\\* )
+ ac_sed_conf_input=`$as_echo "$configure_input" |
+ sed 's/[\\\\&|]/\\\\&/g'`;; #(
+ *) ac_sed_conf_input=$configure_input;;
+ esac
+
+ case $ac_tag in
+ *:-:* | *:-) cat >"$ac_tmp/stdin" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
+ esac
+ ;;
+ esac
+
+ ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_file" : 'X\(//\)[^/]' \| \
+ X"$ac_file" : 'X\(//\)$' \| \
+ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$ac_file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ as_dir="$ac_dir"; as_fn_mkdir_p
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+ case $ac_mode in
+ :F)
+ #
+ # CONFIG_FILE
+ #
+
+ case $INSTALL in
+ [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+ *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
+ esac
+ ac_MKDIR_P=$MKDIR_P
+ case $MKDIR_P in
+ [\\/$]* | ?:[\\/]* ) ;;
+ */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;;
+ esac
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+ac_sed_dataroot='
+/datarootdir/ {
+ p
+ q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ ac_datarootdir_hack='
+ s&@datadir@&$datadir&g
+ s&@docdir@&$docdir&g
+ s&@infodir@&$infodir&g
+ s&@localedir@&$localedir&g
+ s&@mandir@&$mandir&g
+ s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+s&@INSTALL@&$ac_INSTALL&;t t
+s&@MKDIR_P@&$ac_MKDIR_P&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
+ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+ { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
+ { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \
+ "$ac_tmp/out"`; test -z "$ac_out"; } &&
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined" >&5
+$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined" >&2;}
+
+ rm -f "$ac_tmp/stdin"
+ case $ac_file in
+ -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
+ *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
+ esac \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ ;;
+ :H)
+ #
+ # CONFIG_HEADER
+ #
+ if test x"$ac_file" != x-; then
+ {
+ $as_echo "/* $configure_input */" \
+ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
+ } >"$ac_tmp/config.h" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
+$as_echo "$as_me: $ac_file is unchanged" >&6;}
+ else
+ rm -f "$ac_file"
+ mv "$ac_tmp/config.h" "$ac_file" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ fi
+ else
+ $as_echo "/* $configure_input */" \
+ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
+ || as_fn_error $? "could not create -" "$LINENO" 5
+ fi
+# Compute "$ac_file"'s index in $config_headers.
+_am_arg="$ac_file"
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+ case $_am_header in
+ $_am_arg | $_am_arg:* )
+ break ;;
+ * )
+ _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+ esac
+done
+echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" ||
+$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$_am_arg" : 'X\(//\)[^/]' \| \
+ X"$_am_arg" : 'X\(//\)$' \| \
+ X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$_am_arg" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`/stamp-h$_am_stamp_count
+ ;;
+
+ :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5
+$as_echo "$as_me: executing $ac_file commands" >&6;}
+ ;;
+ esac
+
+
+ case $ac_file$ac_mode in
+ "depfiles":C) test x"$AMDEP_TRUE" != x"" || {
+ # Autoconf 2.62 quotes --file arguments for eval, but not when files
+ # are listed without --file. Let's play safe and only enable the eval
+ # if we detect the quoting.
+ case $CONFIG_FILES in
+ *\'*) eval set x "$CONFIG_FILES" ;;
+ *) set x $CONFIG_FILES ;;
+ esac
+ shift
+ for mf
+ do
+ # Strip MF so we end up with the name of the file.
+ mf=`echo "$mf" | sed -e 's/:.*$//'`
+ # Check whether this is an Automake generated Makefile or not.
+ # We used to match only the files named `Makefile.in', but
+ # some people rename them; so instead we look at the file content.
+ # Grep'ing the first line is not enough: some people post-process
+ # each Makefile.in and add a new line on top of each file to say so.
+ # Grep'ing the whole file is not good either: AIX grep has a line
+ # limit of 2048, but all sed's we know have understand at least 4000.
+ if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
+ dirpart=`$as_dirname -- "$mf" ||
+$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$mf" : 'X\(//\)[^/]' \| \
+ X"$mf" : 'X\(//\)$' \| \
+ X"$mf" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$mf" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ else
+ continue
+ fi
+ # Extract the definition of DEPDIR, am__include, and am__quote
+ # from the Makefile without running `make'.
+ DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
+ test -z "$DEPDIR" && continue
+ am__include=`sed -n 's/^am__include = //p' < "$mf"`
+ test -z "am__include" && continue
+ am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
+ # When using ansi2knr, U may be empty or an underscore; expand it
+ U=`sed -n 's/^U = //p' < "$mf"`
+ # Find all dependency output files, they are included files with
+ # $(DEPDIR) in their names. We invoke sed twice because it is the
+ # simplest approach to changing $(DEPDIR) to its actual value in the
+ # expansion.
+ for file in `sed -n "
+ s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
+ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
+ # Make sure the directory exists.
+ test -f "$dirpart/$file" && continue
+ fdir=`$as_dirname -- "$file" ||
+$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$file" : 'X\(//\)[^/]' \| \
+ X"$file" : 'X\(//\)$' \| \
+ X"$file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ as_dir=$dirpart/$fdir; as_fn_mkdir_p
+ # echo "creating $dirpart/$file"
+ echo '# dummy' > "$dirpart/$file"
+ done
+ done
+}
+ ;;
+ "libtool":C)
+
+ # See if we are running on zsh, and set the options which allow our
+ # commands through without removal of \ escapes.
+ if test -n "${ZSH_VERSION+set}" ; then
+ setopt NO_GLOB_SUBST
+ fi
+
+ cfgfile="${ofile}T"
+ trap "$RM \"$cfgfile\"; exit 1" 1 2 15
+ $RM "$cfgfile"
+
+ cat <<_LT_EOF >> "$cfgfile"
+#! $SHELL
+
+# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services.
+# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION
+# NOTE: Changes made to this file will be lost: look at ltmain.sh.
+#
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005,
+# 2006, 2007, 2008 Free Software Foundation, Inc.
+# Written by Gordon Matzigkeit, 1996
+#
+# This file is part of GNU Libtool.
+#
+# GNU Libtool 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.
+#
+# As a special exception to the GNU General Public License,
+# if you distribute this file as part of a program or library that
+# is built using GNU Libtool, you may include this file under the
+# same distribution terms that you use for the rest of that program.
+#
+# GNU Libtool 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 GNU Libtool; see the file COPYING. If not, a copy
+# can be downloaded from http://www.gnu.org/licenses/gpl.html, or
+# obtained by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+
+# The names of the tagged configurations supported by this script.
+available_tags=""
+
+# ### BEGIN LIBTOOL CONFIG
+
+# Which release of libtool.m4 was used?
+macro_version=$macro_version
+macro_revision=$macro_revision
+
+# Whether or not to build shared libraries.
+build_libtool_libs=$enable_shared
+
+# Whether or not to build static libraries.
+build_old_libs=$enable_static
+
+# What type of objects to build.
+pic_mode=$pic_mode
+
+# Whether or not to optimize for fast installation.
+fast_install=$enable_fast_install
+
+# The host system.
+host_alias=$host_alias
+host=$host
+host_os=$host_os
+
+# The build system.
+build_alias=$build_alias
+build=$build
+build_os=$build_os
+
+# A sed program that does not truncate output.
+SED=$lt_SED
+
+# Sed that helps us avoid accidentally triggering echo(1) options like -n.
+Xsed="\$SED -e 1s/^X//"
+
+# A grep program that handles long lines.
+GREP=$lt_GREP
+
+# An ERE matcher.
+EGREP=$lt_EGREP
+
+# A literal string matcher.
+FGREP=$lt_FGREP
+
+# A BSD- or MS-compatible name lister.
+NM=$lt_NM
+
+# Whether we need soft or hard links.
+LN_S=$lt_LN_S
+
+# What is the maximum length of a command?
+max_cmd_len=$max_cmd_len
+
+# Object file suffix (normally "o").
+objext=$ac_objext
+
+# Executable file suffix (normally "").
+exeext=$exeext
+
+# whether the shell understands "unset".
+lt_unset=$lt_unset
+
+# turn spaces into newlines.
+SP2NL=$lt_lt_SP2NL
+
+# turn newlines into spaces.
+NL2SP=$lt_lt_NL2SP
+
+# How to create reloadable object files.
+reload_flag=$lt_reload_flag
+reload_cmds=$lt_reload_cmds
+
+# An object symbol dumper.
+OBJDUMP=$lt_OBJDUMP
+
+# Method to check whether dependent libraries are shared objects.
+deplibs_check_method=$lt_deplibs_check_method
+
+# Command to use when deplibs_check_method == "file_magic".
+file_magic_cmd=$lt_file_magic_cmd
+
+# The archiver.
+AR=$lt_AR
+AR_FLAGS=$lt_AR_FLAGS
+
+# A symbol stripping program.
+STRIP=$lt_STRIP
+
+# Commands used to install an old-style archive.
+RANLIB=$lt_RANLIB
+old_postinstall_cmds=$lt_old_postinstall_cmds
+old_postuninstall_cmds=$lt_old_postuninstall_cmds
+
+# A C compiler.
+LTCC=$lt_CC
+
+# LTCC compiler flags.
+LTCFLAGS=$lt_CFLAGS
+
+# Take the output of nm and produce a listing of raw symbols and C names.
+global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe
+
+# Transform the output of nm in a proper C declaration.
+global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl
+
+# Transform the output of nm in a C name address pair.
+global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address
+
+# Transform the output of nm in a C name address pair when lib prefix is needed.
+global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix
+
+# The name of the directory that contains temporary libtool files.
+objdir=$objdir
+
+# Shell to use when invoking shell scripts.
+SHELL=$lt_SHELL
+
+# An echo program that does not interpret backslashes.
+ECHO=$lt_ECHO
+
+# Used to examine libraries when file_magic_cmd begins with "file".
+MAGIC_CMD=$MAGIC_CMD
+
+# Must we lock files when doing compilation?
+need_locks=$lt_need_locks
+
+# Tool to manipulate archived DWARF debug symbol files on Mac OS X.
+DSYMUTIL=$lt_DSYMUTIL
+
+# Tool to change global to local symbols on Mac OS X.
+NMEDIT=$lt_NMEDIT
+
+# Tool to manipulate fat objects and archives on Mac OS X.
+LIPO=$lt_LIPO
+
+# ldd/readelf like tool for Mach-O binaries on Mac OS X.
+OTOOL=$lt_OTOOL
+
+# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4.
+OTOOL64=$lt_OTOOL64
+
+# Old archive suffix (normally "a").
+libext=$libext
+
+# Shared library suffix (normally ".so").
+shrext_cmds=$lt_shrext_cmds
+
+# The commands to extract the exported symbol list from a shared archive.
+extract_expsyms_cmds=$lt_extract_expsyms_cmds
+
+# Variables whose values should be saved in libtool wrapper scripts and
+# restored at link time.
+variables_saved_for_relink=$lt_variables_saved_for_relink
+
+# Do we need the "lib" prefix for modules?
+need_lib_prefix=$need_lib_prefix
+
+# Do we need a version for libraries?
+need_version=$need_version
+
+# Library versioning type.
+version_type=$version_type
+
+# Shared library runtime path variable.
+runpath_var=$runpath_var
+
+# Shared library path variable.
+shlibpath_var=$shlibpath_var
+
+# Is shlibpath searched before the hard-coded library search path?
+shlibpath_overrides_runpath=$shlibpath_overrides_runpath
+
+# Format of library name prefix.
+libname_spec=$lt_libname_spec
+
+# List of archive names. First name is the real one, the rest are links.
+# The last name is the one that the linker finds with -lNAME
+library_names_spec=$lt_library_names_spec
+
+# The coded name of the library, if different from the real name.
+soname_spec=$lt_soname_spec
+
+# Command to use after installation of a shared archive.
+postinstall_cmds=$lt_postinstall_cmds
+
+# Command to use after uninstallation of a shared archive.
+postuninstall_cmds=$lt_postuninstall_cmds
+
+# Commands used to finish a libtool library installation in a directory.
+finish_cmds=$lt_finish_cmds
+
+# As "finish_cmds", except a single script fragment to be evaled but
+# not shown.
+finish_eval=$lt_finish_eval
+
+# Whether we should hardcode library paths into libraries.
+hardcode_into_libs=$hardcode_into_libs
+
+# Compile-time system search path for libraries.
+sys_lib_search_path_spec=$lt_sys_lib_search_path_spec
+
+# Run-time system search path for libraries.
+sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec
+
+# Whether dlopen is supported.
+dlopen_support=$enable_dlopen
+
+# Whether dlopen of programs is supported.
+dlopen_self=$enable_dlopen_self
+
+# Whether dlopen of statically linked programs is supported.
+dlopen_self_static=$enable_dlopen_self_static
+
+# Commands to strip libraries.
+old_striplib=$lt_old_striplib
+striplib=$lt_striplib
+
+
+# The linker used to build libraries.
+LD=$lt_LD
+
+# Commands used to build an old-style archive.
+old_archive_cmds=$lt_old_archive_cmds
+
+# A language specific compiler.
+CC=$lt_compiler
+
+# Is the compiler the GNU compiler?
+with_gcc=$GCC
+
+# Compiler flag to turn off builtin functions.
+no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag
+
+# How to pass a linker flag through the compiler.
+wl=$lt_lt_prog_compiler_wl
+
+# Additional compiler flags for building library objects.
+pic_flag=$lt_lt_prog_compiler_pic
+
+# Compiler flag to prevent dynamic linking.
+link_static_flag=$lt_lt_prog_compiler_static
+
+# Does compiler simultaneously support -c and -o options?
+compiler_c_o=$lt_lt_cv_prog_compiler_c_o
+
+# Whether or not to add -lc for building shared libraries.
+build_libtool_need_lc=$archive_cmds_need_lc
+
+# Whether or not to disallow shared libs when runtime libs are static.
+allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes
+
+# Compiler flag to allow reflexive dlopens.
+export_dynamic_flag_spec=$lt_export_dynamic_flag_spec
+
+# Compiler flag to generate shared objects directly from archives.
+whole_archive_flag_spec=$lt_whole_archive_flag_spec
+
+# Whether the compiler copes with passing no objects directly.
+compiler_needs_object=$lt_compiler_needs_object
+
+# Create an old-style archive from a shared archive.
+old_archive_from_new_cmds=$lt_old_archive_from_new_cmds
+
+# Create a temporary old-style archive to link instead of a shared archive.
+old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds
+
+# Commands used to build a shared archive.
+archive_cmds=$lt_archive_cmds
+archive_expsym_cmds=$lt_archive_expsym_cmds
+
+# Commands used to build a loadable module if different from building
+# a shared archive.
+module_cmds=$lt_module_cmds
+module_expsym_cmds=$lt_module_expsym_cmds
+
+# Whether we are building with GNU ld or not.
+with_gnu_ld=$lt_with_gnu_ld
+
+# Flag that allows shared libraries with undefined symbols to be built.
+allow_undefined_flag=$lt_allow_undefined_flag
+
+# Flag that enforces no undefined symbols.
+no_undefined_flag=$lt_no_undefined_flag
+
+# Flag to hardcode \$libdir into a binary during linking.
+# This must work even if \$libdir does not exist
+hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec
+
+# If ld is used when linking, flag to hardcode \$libdir into a binary
+# during linking. This must work even if \$libdir does not exist.
+hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld
+
+# Whether we need a single "-rpath" flag with a separated argument.
+hardcode_libdir_separator=$lt_hardcode_libdir_separator
+
+# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
+# DIR into the resulting binary.
+hardcode_direct=$hardcode_direct
+
+# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
+# DIR into the resulting binary and the resulting library dependency is
+# "absolute",i.e impossible to change by setting \${shlibpath_var} if the
+# library is relocated.
+hardcode_direct_absolute=$hardcode_direct_absolute
+
+# Set to "yes" if using the -LDIR flag during linking hardcodes DIR
+# into the resulting binary.
+hardcode_minus_L=$hardcode_minus_L
+
+# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
+# into the resulting binary.
+hardcode_shlibpath_var=$hardcode_shlibpath_var
+
+# Set to "yes" if building a shared library automatically hardcodes DIR
+# into the library and all subsequent libraries and executables linked
+# against it.
+hardcode_automatic=$hardcode_automatic
+
+# Set to yes if linker adds runtime paths of dependent libraries
+# to runtime path list.
+inherit_rpath=$inherit_rpath
+
+# Whether libtool must link a program against all its dependency libraries.
+link_all_deplibs=$link_all_deplibs
+
+# Fix the shell variable \$srcfile for the compiler.
+fix_srcfile_path=$lt_fix_srcfile_path
+
+# Set to "yes" if exported symbols are required.
+always_export_symbols=$always_export_symbols
+
+# The commands to list exported symbols.
+export_symbols_cmds=$lt_export_symbols_cmds
+
+# Symbols that should not be listed in the preloaded symbols.
+exclude_expsyms=$lt_exclude_expsyms
+
+# Symbols that must always be exported.
+include_expsyms=$lt_include_expsyms
+
+# Commands necessary for linking programs (against libraries) with templates.
+prelink_cmds=$lt_prelink_cmds
+
+# Specify filename containing input files.
+file_list_spec=$lt_file_list_spec
+
+# How to hardcode a shared library path into an executable.
+hardcode_action=$hardcode_action
+
+# ### END LIBTOOL CONFIG
+
+_LT_EOF
+
+ case $host_os in
+ aix3*)
+ cat <<\_LT_EOF >> "$cfgfile"
+# AIX sometimes has problems with the GCC collect2 program. For some
+# reason, if we set the COLLECT_NAMES environment variable, the problems
+# vanish in a puff of smoke.
+if test "X${COLLECT_NAMES+set}" != Xset; then
+ COLLECT_NAMES=
+ export COLLECT_NAMES
+fi
+_LT_EOF
+ ;;
+ esac
+
+
+ltmain="$ac_aux_dir/ltmain.sh"
+
+
+ # We use sed instead of cat because bash on DJGPP gets confused if
+ # if finds mixed CR/LF and LF-only lines. Since sed operates in
+ # text mode, it properly converts lines to CR/LF. This bash problem
+ # is reportedly fixed, but why not run on old versions too?
+ sed '/^# Generated shell functions inserted here/q' "$ltmain" >> "$cfgfile" \
+ || (rm -f "$cfgfile"; exit 1)
+
+ case $xsi_shell in
+ yes)
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_dirname file append nondir_replacement
+# Compute the dirname of FILE. If nonempty, add APPEND to the result,
+# otherwise set result to NONDIR_REPLACEMENT.
+func_dirname ()
+{
+ case ${1} in
+ */*) func_dirname_result="${1%/*}${2}" ;;
+ * ) func_dirname_result="${3}" ;;
+ esac
+}
+
+# func_basename file
+func_basename ()
+{
+ func_basename_result="${1##*/}"
+}
+
+# func_dirname_and_basename file append nondir_replacement
+# perform func_basename and func_dirname in a single function
+# call:
+# dirname: Compute the dirname of FILE. If nonempty,
+# add APPEND to the result, otherwise set result
+# to NONDIR_REPLACEMENT.
+# value returned in "$func_dirname_result"
+# basename: Compute filename of FILE.
+# value retuned in "$func_basename_result"
+# Implementation must be kept synchronized with func_dirname
+# and func_basename. For efficiency, we do not delegate to
+# those functions but instead duplicate the functionality here.
+func_dirname_and_basename ()
+{
+ case ${1} in
+ */*) func_dirname_result="${1%/*}${2}" ;;
+ * ) func_dirname_result="${3}" ;;
+ esac
+ func_basename_result="${1##*/}"
+}
+
+# func_stripname prefix suffix name
+# strip PREFIX and SUFFIX off of NAME.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+func_stripname ()
+{
+ # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are
+ # positional parameters, so assign one to ordinary parameter first.
+ func_stripname_result=${3}
+ func_stripname_result=${func_stripname_result#"${1}"}
+ func_stripname_result=${func_stripname_result%"${2}"}
+}
+
+# func_opt_split
+func_opt_split ()
+{
+ func_opt_split_opt=${1%%=*}
+ func_opt_split_arg=${1#*=}
+}
+
+# func_lo2o object
+func_lo2o ()
+{
+ case ${1} in
+ *.lo) func_lo2o_result=${1%.lo}.${objext} ;;
+ *) func_lo2o_result=${1} ;;
+ esac
+}
+
+# func_xform libobj-or-source
+func_xform ()
+{
+ func_xform_result=${1%.*}.lo
+}
+
+# func_arith arithmetic-term...
+func_arith ()
+{
+ func_arith_result=$(( $* ))
+}
+
+# func_len string
+# STRING may not start with a hyphen.
+func_len ()
+{
+ func_len_result=${#1}
+}
+
+_LT_EOF
+ ;;
+ *) # Bourne compatible functions.
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_dirname file append nondir_replacement
+# Compute the dirname of FILE. If nonempty, add APPEND to the result,
+# otherwise set result to NONDIR_REPLACEMENT.
+func_dirname ()
+{
+ # Extract subdirectory from the argument.
+ func_dirname_result=`$ECHO "X${1}" | $Xsed -e "$dirname"`
+ if test "X$func_dirname_result" = "X${1}"; then
+ func_dirname_result="${3}"
+ else
+ func_dirname_result="$func_dirname_result${2}"
+ fi
+}
+
+# func_basename file
+func_basename ()
+{
+ func_basename_result=`$ECHO "X${1}" | $Xsed -e "$basename"`
+}
+
+
+# func_stripname prefix suffix name
+# strip PREFIX and SUFFIX off of NAME.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+# func_strip_suffix prefix name
+func_stripname ()
+{
+ case ${2} in
+ .*) func_stripname_result=`$ECHO "X${3}" \
+ | $Xsed -e "s%^${1}%%" -e "s%\\\\${2}\$%%"`;;
+ *) func_stripname_result=`$ECHO "X${3}" \
+ | $Xsed -e "s%^${1}%%" -e "s%${2}\$%%"`;;
+ esac
+}
+
+# sed scripts:
+my_sed_long_opt='1s/^\(-[^=]*\)=.*/\1/;q'
+my_sed_long_arg='1s/^-[^=]*=//'
+
+# func_opt_split
+func_opt_split ()
+{
+ func_opt_split_opt=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_opt"`
+ func_opt_split_arg=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_arg"`
+}
+
+# func_lo2o object
+func_lo2o ()
+{
+ func_lo2o_result=`$ECHO "X${1}" | $Xsed -e "$lo2o"`
+}
+
+# func_xform libobj-or-source
+func_xform ()
+{
+ func_xform_result=`$ECHO "X${1}" | $Xsed -e 's/\.[^.]*$/.lo/'`
+}
+
+# func_arith arithmetic-term...
+func_arith ()
+{
+ func_arith_result=`expr "$@"`
+}
+
+# func_len string
+# STRING may not start with a hyphen.
+func_len ()
+{
+ func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len`
+}
+
+_LT_EOF
+esac
+
+case $lt_shell_append in
+ yes)
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_append var value
+# Append VALUE to the end of shell variable VAR.
+func_append ()
+{
+ eval "$1+=\$2"
+}
+_LT_EOF
+ ;;
+ *)
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_append var value
+# Append VALUE to the end of shell variable VAR.
+func_append ()
+{
+ eval "$1=\$$1\$2"
+}
+
+_LT_EOF
+ ;;
+ esac
+
+
+ sed -n '/^# Generated shell functions inserted here/,$p' "$ltmain" >> "$cfgfile" \
+ || (rm -f "$cfgfile"; exit 1)
+
+ mv -f "$cfgfile" "$ofile" ||
+ (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
+ chmod +x "$ofile"
+
+ ;;
+ "po-directories":C)
+ for ac_file in $CONFIG_FILES; do
+ # Support "outfile[:infile[:infile...]]"
+ case "$ac_file" in
+ *:*) ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ esac
+ # PO directories have a Makefile.in generated from Makefile.in.in.
+ case "$ac_file" in */Makefile.in)
+ # Adjust a relative srcdir.
+ ac_dir=`echo "$ac_file"|sed 's%/[^/][^/]*$%%'`
+ ac_dir_suffix="/`echo "$ac_dir"|sed 's%^\./%%'`"
+ ac_dots=`echo "$ac_dir_suffix"|sed 's%/[^/]*%../%g'`
+ # In autoconf-2.13 it is called $ac_given_srcdir.
+ # In autoconf-2.50 it is called $srcdir.
+ test -n "$ac_given_srcdir" || ac_given_srcdir="$srcdir"
+ case "$ac_given_srcdir" in
+ .) top_srcdir=`echo $ac_dots|sed 's%/$%%'` ;;
+ /*) top_srcdir="$ac_given_srcdir" ;;
+ *) top_srcdir="$ac_dots$ac_given_srcdir" ;;
+ esac
+ # Treat a directory as a PO directory if and only if it has a
+ # POTFILES.in file. This allows packages to have multiple PO
+ # directories under different names or in different locations.
+ if test -f "$ac_given_srcdir/$ac_dir/POTFILES.in"; then
+ rm -f "$ac_dir/POTFILES"
+ test -n "$as_me" && echo "$as_me: creating $ac_dir/POTFILES" || echo "creating $ac_dir/POTFILES"
+ cat "$ac_given_srcdir/$ac_dir/POTFILES.in" | sed -e "/^#/d" -e "/^[ ]*\$/d" -e "s,.*, $top_srcdir/& \\\\," | sed -e "\$s/\(.*\) \\\\/\1/" > "$ac_dir/POTFILES"
+ POMAKEFILEDEPS="POTFILES.in"
+ # ALL_LINGUAS, POFILES, UPDATEPOFILES, DUMMYPOFILES, GMOFILES depend
+ # on $ac_dir but don't depend on user-specified configuration
+ # parameters.
+ if test -f "$ac_given_srcdir/$ac_dir/LINGUAS"; then
+ # The LINGUAS file contains the set of available languages.
+ if test -n "$OBSOLETE_ALL_LINGUAS"; then
+ test -n "$as_me" && echo "$as_me: setting ALL_LINGUAS in configure.in is obsolete" || echo "setting ALL_LINGUAS in configure.in is obsolete"
+ fi
+ ALL_LINGUAS_=`sed -e "/^#/d" -e "s/#.*//" "$ac_given_srcdir/$ac_dir/LINGUAS"`
+ # Hide the ALL_LINGUAS assigment from automake < 1.5.
+ eval 'ALL_LINGUAS''=$ALL_LINGUAS_'
+ POMAKEFILEDEPS="$POMAKEFILEDEPS LINGUAS"
+ else
+ # The set of available languages was given in configure.in.
+ # Hide the ALL_LINGUAS assigment from automake < 1.5.
+ eval 'ALL_LINGUAS''=$OBSOLETE_ALL_LINGUAS'
+ fi
+ # Compute POFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).po)
+ # Compute UPDATEPOFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(lang).po-update)
+ # Compute DUMMYPOFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(lang).nop)
+ # Compute GMOFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).gmo)
+ case "$ac_given_srcdir" in
+ .) srcdirpre= ;;
+ *) srcdirpre='$(srcdir)/' ;;
+ esac
+ POFILES=
+ UPDATEPOFILES=
+ DUMMYPOFILES=
+ GMOFILES=
+ for lang in $ALL_LINGUAS; do
+ POFILES="$POFILES $srcdirpre$lang.po"
+ UPDATEPOFILES="$UPDATEPOFILES $lang.po-update"
+ DUMMYPOFILES="$DUMMYPOFILES $lang.nop"
+ GMOFILES="$GMOFILES $srcdirpre$lang.gmo"
+ done
+ # CATALOGS depends on both $ac_dir and the user's LINGUAS
+ # environment variable.
+ INST_LINGUAS=
+ if test -n "$ALL_LINGUAS"; then
+ for presentlang in $ALL_LINGUAS; do
+ useit=no
+ if test "%UNSET%" != "$LINGUAS"; then
+ desiredlanguages="$LINGUAS"
+ else
+ desiredlanguages="$ALL_LINGUAS"
+ fi
+ for desiredlang in $desiredlanguages; do
+ # Use the presentlang catalog if desiredlang is
+ # a. equal to presentlang, or
+ # b. a variant of presentlang (because in this case,
+ # presentlang can be used as a fallback for messages
+ # which are not translated in the desiredlang catalog).
+ case "$desiredlang" in
+ "$presentlang"*) useit=yes;;
+ esac
+ done
+ if test $useit = yes; then
+ INST_LINGUAS="$INST_LINGUAS $presentlang"
+ fi
+ done
+ fi
+ CATALOGS=
+ if test -n "$INST_LINGUAS"; then
+ for lang in $INST_LINGUAS; do
+ CATALOGS="$CATALOGS $lang.gmo"
+ done
+ fi
+ test -n "$as_me" && echo "$as_me: creating $ac_dir/Makefile" || echo "creating $ac_dir/Makefile"
+ sed -e "/^POTFILES =/r $ac_dir/POTFILES" -e "/^# Makevars/r $ac_given_srcdir/$ac_dir/Makevars" -e "s|@POFILES@|$POFILES|g" -e "s|@UPDATEPOFILES@|$UPDATEPOFILES|g" -e "s|@DUMMYPOFILES@|$DUMMYPOFILES|g" -e "s|@GMOFILES@|$GMOFILES|g" -e "s|@CATALOGS@|$CATALOGS|g" -e "s|@POMAKEFILEDEPS@|$POMAKEFILEDEPS|g" "$ac_dir/Makefile.in" > "$ac_dir/Makefile"
+ for f in "$ac_given_srcdir/$ac_dir"/Rules-*; do
+ if test -f "$f"; then
+ case "$f" in
+ *.orig | *.bak | *~) ;;
+ *) cat "$f" >> "$ac_dir/Makefile" ;;
+ esac
+ fi
+ done
+ fi
+ ;;
+ esac
+ done ;;
+
+ esac
+done # for ac_tag
+
+
+as_fn_exit 0
+_ACEOF
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+ as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded. So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status. When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+ ac_cs_success=:
+ ac_config_status_args=
+ test "$silent" = yes &&
+ ac_config_status_args="$ac_config_status_args --quiet"
+ exec 5>/dev/null
+ $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+ exec 5>>config.log
+ # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+ # would make configure fail if this is the last instruction.
+ $ac_cs_success || as_fn_exit 1
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
diff --git a/autom4te.cache/output.2 b/autom4te.cache/output.2
new file mode 100644
index 00000000..71338f68
--- /dev/null
+++ b/autom4te.cache/output.2
@@ -0,0 +1,21687 @@
+@%:@! /bin/sh
+@%:@ From configure.ac Id: configure.ac 1266 2009-07-14 18:39:12Z hubert@u.washington.edu .
+@%:@ Guess values for system-dependent variables and create Makefiles.
+@%:@ Generated by GNU Autoconf 2.68 for alpine 2.10.
+@%:@
+@%:@ Report bugs to <alpine-contact@u.washington.edu>.
+@%:@
+@%:@
+@%:@ Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
+@%:@ 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software
+@%:@ Foundation, Inc.
+@%:@
+@%:@
+@%:@ This configure script is free software; the Free Software Foundation
+@%:@ gives unlimited permission to copy, distribute and modify it.
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in @%:@(
+ *posix*) :
+ set -o posix ;; @%:@(
+ *) :
+ ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='print -r --'
+ as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='printf %s\n'
+ as_echo_n='printf %s'
+else
+ if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+ as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+ as_echo_n='/usr/ucb/echo -n'
+ else
+ as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+ as_echo_n_body='eval
+ arg=$1;
+ case $arg in @%:@(
+ *"$as_nl"*)
+ expr "X$arg" : "X\\(.*\\)$as_nl";
+ arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+ esac;
+ expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+ '
+ export as_echo_n_body
+ as_echo_n='sh -c $as_echo_n_body as_echo'
+ fi
+ export as_echo_body
+ as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+as_myself=
+case $0 in @%:@((
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there. '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+if test "x$CONFIG_SHELL" = x; then
+ as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '\${1+\"\$@\"}'='\"\$@\"'
+ setopt NO_GLOB_SUBST
+else
+ case \`(set -o) 2>/dev/null\` in @%:@(
+ *posix*) :
+ set -o posix ;; @%:@(
+ *) :
+ ;;
+esac
+fi
+"
+ as_required="as_fn_return () { (exit \$1); }
+as_fn_success () { as_fn_return 0; }
+as_fn_failure () { as_fn_return 1; }
+as_fn_ret_success () { return 0; }
+as_fn_ret_failure () { return 1; }
+
+exitcode=0
+as_fn_success || { exitcode=1; echo as_fn_success failed.; }
+as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
+as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
+as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
+if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
+
+else
+ exitcode=1; echo positional parameters were not saved.
+fi
+test x\$exitcode = x0 || exit 1"
+ as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
+ as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
+ eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
+ test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1
+test \$(( 1 + 1 )) = 2 || exit 1"
+ if (eval "$as_required") 2>/dev/null; then :
+ as_have_required=yes
+else
+ as_have_required=no
+fi
+ if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then :
+
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ as_found=:
+ case $as_dir in @%:@(
+ /*)
+ for as_base in sh bash ksh sh5; do
+ # Try only shells that exist, to save several forks.
+ as_shell=$as_dir/$as_base
+ if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+ { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then :
+ CONFIG_SHELL=$as_shell as_have_required=yes
+ if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then :
+ break 2
+fi
+fi
+ done;;
+ esac
+ as_found=false
+done
+$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
+ { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
+ CONFIG_SHELL=$SHELL as_have_required=yes
+fi; }
+IFS=$as_save_IFS
+
+
+ if test "x$CONFIG_SHELL" != x; then :
+ # We cannot yet assume a decent shell, so we have to provide a
+ # neutralization value for shells without unset; and this also
+ # works around shells that cannot unset nonexistent variables.
+ # Preserve -v and -x to the replacement shell.
+ BASH_ENV=/dev/null
+ ENV=/dev/null
+ (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+ export CONFIG_SHELL
+ case $- in @%:@ ((((
+ *v*x* | *x*v* ) as_opts=-vx ;;
+ *v* ) as_opts=-v ;;
+ *x* ) as_opts=-x ;;
+ * ) as_opts= ;;
+ esac
+ exec "$CONFIG_SHELL" $as_opts "$as_myself" ${1+"$@"}
+fi
+
+ if test x$as_have_required = xno; then :
+ $as_echo "$0: This script requires a shell more modern than all"
+ $as_echo "$0: the shells that I found on your system."
+ if test x${ZSH_VERSION+set} = xset ; then
+ $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
+ $as_echo "$0: be upgraded to zsh 4.3.4 or later."
+ else
+ $as_echo "$0: Please tell bug-autoconf@gnu.org and
+$0: alpine-contact@u.washington.edu about your system,
+$0: including any error possibly output before this
+$0: message. Then install a modern shell, or manually run
+$0: the script under such a shell if you do have one."
+ fi
+ exit 1
+fi
+fi
+fi
+SHELL=${CONFIG_SHELL-/bin/sh}
+export SHELL
+# Unset more variables known to interfere with behavior of common tools.
+CLICOLOR_FORCE= GREP_OPTIONS=
+unset CLICOLOR_FORCE GREP_OPTIONS
+
+## --------------------- ##
+## M4sh Shell Functions. ##
+## --------------------- ##
+@%:@ as_fn_unset VAR
+@%:@ ---------------
+@%:@ Portably unset VAR.
+as_fn_unset ()
+{
+ { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+@%:@ as_fn_set_status STATUS
+@%:@ -----------------------
+@%:@ Set @S|@? to STATUS, without forking.
+as_fn_set_status ()
+{
+ return $1
+} @%:@ as_fn_set_status
+
+@%:@ as_fn_exit STATUS
+@%:@ -----------------
+@%:@ Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+ set +e
+ as_fn_set_status $1
+ exit $1
+} @%:@ as_fn_exit
+
+@%:@ as_fn_mkdir_p
+@%:@ -------------
+@%:@ Create "@S|@as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || eval $as_mkdir_p || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} @%:@ as_fn_mkdir_p
+@%:@ as_fn_append VAR VALUE
+@%:@ ----------------------
+@%:@ Append the text in VALUE to the end of the definition contained in VAR. Take
+@%:@ advantage of any shell optimizations that allow amortized linear growth over
+@%:@ repeated appends, instead of the typical quadratic growth present in naive
+@%:@ implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+ eval 'as_fn_append ()
+ {
+ eval $1+=\$2
+ }'
+else
+ as_fn_append ()
+ {
+ eval $1=\$$1\$2
+ }
+fi # as_fn_append
+
+@%:@ as_fn_arith ARG...
+@%:@ ------------------
+@%:@ Perform arithmetic evaluation on the ARGs, and store the result in the
+@%:@ global @S|@as_val. Take advantage of shells that can avoid forks. The arguments
+@%:@ must be portable across @S|@(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+ eval 'as_fn_arith ()
+ {
+ as_val=$(( $* ))
+ }'
+else
+ as_fn_arith ()
+ {
+ as_val=`expr "$@" || test $? -eq 1`
+ }
+fi # as_fn_arith
+
+
+@%:@ as_fn_error STATUS ERROR [LINENO LOG_FD]
+@%:@ ----------------------------------------
+@%:@ Output "`basename @S|@0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+@%:@ provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+@%:@ script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+ as_status=$1; test $as_status -eq 0 && as_status=1
+ if test "$4"; then
+ as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+ fi
+ $as_echo "$as_me: error: $2" >&2
+ as_fn_exit $as_status
+} @%:@ as_fn_error
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+ as_lineno_1=$LINENO as_lineno_1a=$LINENO
+ as_lineno_2=$LINENO as_lineno_2a=$LINENO
+ eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
+ test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
+ # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-)
+ sed -n '
+ p
+ /[$]LINENO/=
+ ' <$as_myself |
+ sed '
+ s/[$]LINENO.*/&-/
+ t lineno
+ b
+ :lineno
+ N
+ :loop
+ s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+ t loop
+ s/-\n.*//
+ ' >$as_me.lineno &&
+ chmod +x "$as_me.lineno" ||
+ { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensitive to this).
+ . "./$as_me.lineno"
+ # Exit status is that of the last command.
+ exit
+}
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in @%:@(((((
+-n*)
+ case `echo 'xy\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ xy) ECHO_C='\c';;
+ *) echo `echo ksh88 bug on AIX 6.1` > /dev/null
+ ECHO_T=' ';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+ if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -p'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -p'
+ elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+ else
+ as_ln_s='cp -p'
+ fi
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p='mkdir -p "$as_dir"'
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+if test -x / >/dev/null 2>&1; then
+ as_test_x='test -x'
+else
+ if ls -dL / >/dev/null 2>&1; then
+ as_ls_L_option=L
+ else
+ as_ls_L_option=
+ fi
+ as_test_x='
+ eval sh -c '\''
+ if test -d "$1"; then
+ test -d "$1/.";
+ else
+ case $1 in @%:@(
+ -*)set "./$1";;
+ esac;
+ case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in @%:@((
+ ???[sx]*):;;*)false;;esac;fi
+ '\'' sh
+ '
+fi
+as_executable_p=$as_test_x
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+
+# Check that we are running under the correct shell.
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+case X$lt_ECHO in
+X*--fallback-echo)
+ # Remove one level of quotation (which was required for Make).
+ ECHO=`echo "$lt_ECHO" | sed 's,\\\\\$\\$0,'$0','`
+ ;;
+esac
+
+ECHO=${lt_ECHO-echo}
+if test "X$1" = X--no-reexec; then
+ # Discard the --no-reexec flag, and continue.
+ shift
+elif test "X$1" = X--fallback-echo; then
+ # Avoid inline document here, it may be left over
+ :
+elif test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' ; then
+ # Yippee, $ECHO works!
+ :
+else
+ # Restart under the correct shell.
+ exec $SHELL "$0" --no-reexec ${1+"$@"}
+fi
+
+if test "X$1" = X--fallback-echo; then
+ # used as fallback echo
+ shift
+ cat <<_LT_EOF
+$*
+_LT_EOF
+ exit 0
+fi
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+if test -z "$lt_ECHO"; then
+ if test "X${echo_test_string+set}" != Xset; then
+ # find a string as large as possible, as long as the shell can cope with it
+ for cmd in 'sed 50q "$0"' 'sed 20q "$0"' 'sed 10q "$0"' 'sed 2q "$0"' 'echo test'; do
+ # expected sizes: less than 2Kb, 1Kb, 512 bytes, 16 bytes, ...
+ if { echo_test_string=`eval $cmd`; } 2>/dev/null &&
+ { test "X$echo_test_string" = "X$echo_test_string"; } 2>/dev/null
+ then
+ break
+ fi
+ done
+ fi
+
+ if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ :
+ else
+ # The Solaris, AIX, and Digital Unix default echo programs unquote
+ # backslashes. This makes it impossible to quote backslashes using
+ # echo "$something" | sed 's/\\/\\\\/g'
+ #
+ # So, first we look for a working echo in the user's PATH.
+
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ for dir in $PATH /usr/ucb; do
+ IFS="$lt_save_ifs"
+ if (test -f $dir/echo || test -f $dir/echo$ac_exeext) &&
+ test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`($dir/echo "$echo_test_string") 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ ECHO="$dir/echo"
+ break
+ fi
+ done
+ IFS="$lt_save_ifs"
+
+ if test "X$ECHO" = Xecho; then
+ # We didn't find a better echo, so look for alternatives.
+ if test "X`{ print -r '\t'; } 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`{ print -r "$echo_test_string"; } 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ # This shell has a builtin print -r that does the trick.
+ ECHO='print -r'
+ elif { test -f /bin/ksh || test -f /bin/ksh$ac_exeext; } &&
+ test "X$CONFIG_SHELL" != X/bin/ksh; then
+ # If we have ksh, try running configure again with it.
+ ORIGINAL_CONFIG_SHELL=${CONFIG_SHELL-/bin/sh}
+ export ORIGINAL_CONFIG_SHELL
+ CONFIG_SHELL=/bin/ksh
+ export CONFIG_SHELL
+ exec $CONFIG_SHELL "$0" --no-reexec ${1+"$@"}
+ else
+ # Try using printf.
+ ECHO='printf %s\n'
+ if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ # Cool, printf works
+ :
+ elif echo_testing_string=`($ORIGINAL_CONFIG_SHELL "$0" --fallback-echo '\t') 2>/dev/null` &&
+ test "X$echo_testing_string" = 'X\t' &&
+ echo_testing_string=`($ORIGINAL_CONFIG_SHELL "$0" --fallback-echo "$echo_test_string") 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ CONFIG_SHELL=$ORIGINAL_CONFIG_SHELL
+ export CONFIG_SHELL
+ SHELL="$CONFIG_SHELL"
+ export SHELL
+ ECHO="$CONFIG_SHELL $0 --fallback-echo"
+ elif echo_testing_string=`($CONFIG_SHELL "$0" --fallback-echo '\t') 2>/dev/null` &&
+ test "X$echo_testing_string" = 'X\t' &&
+ echo_testing_string=`($CONFIG_SHELL "$0" --fallback-echo "$echo_test_string") 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ ECHO="$CONFIG_SHELL $0 --fallback-echo"
+ else
+ # maybe with a smaller string...
+ prev=:
+
+ for cmd in 'echo test' 'sed 2q "$0"' 'sed 10q "$0"' 'sed 20q "$0"' 'sed 50q "$0"'; do
+ if { test "X$echo_test_string" = "X`eval $cmd`"; } 2>/dev/null
+ then
+ break
+ fi
+ prev="$cmd"
+ done
+
+ if test "$prev" != 'sed 50q "$0"'; then
+ echo_test_string=`eval $prev`
+ export echo_test_string
+ exec ${ORIGINAL_CONFIG_SHELL-${CONFIG_SHELL-/bin/sh}} "$0" ${1+"$@"}
+ else
+ # Oops. We lost completely, so just stick with echo.
+ ECHO=echo
+ fi
+ fi
+ fi
+ fi
+ fi
+fi
+
+# Copy echo and quote the copy suitably for passing to libtool from
+# the Makefile, instead of quoting the original, which is used later.
+lt_ECHO=$ECHO
+if test "X$lt_ECHO" = "X$CONFIG_SHELL $0 --fallback-echo"; then
+ lt_ECHO="$CONFIG_SHELL \\\$\$0 --fallback-echo"
+fi
+
+
+
+
+test -n "$DJDIR" || exec 7<&0 </dev/null
+exec 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIB@&t@OBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+
+# Identity of this package.
+PACKAGE_NAME='alpine'
+PACKAGE_TARNAME='alpine'
+PACKAGE_VERSION='2.10'
+PACKAGE_STRING='alpine 2.10'
+PACKAGE_BUGREPORT='alpine-contact@u.washington.edu'
+PACKAGE_URL=''
+
+ac_unique_file="include/system.h"
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_STRING_H
+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+# include <memory.h>
+# endif
+# include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+gt_needs=
+ac_subst_vars='am__EXEEXT_FALSE
+am__EXEEXT_TRUE
+LTLIBOBJS
+LIB@&t@OBJS
+AM_LDFLAGS
+AM_CFLAGS
+WEB_PUBCOOKIE_LINK
+WEB_PUBCOOKIE_LIB
+WEB_PUBCOOKIE_BUILD
+WEB_BINDIR
+WEB_BUILD
+REGEX_BUILD
+C_CLIENT_SPECIALS
+C_CLIENT_GCCOPTLEVEL
+C_CLIENT_LDFLAGS
+C_CLIENT_CFLAGS
+C_CLIENT_WITH_IPV6
+C_CLIENT_TARGET
+PTHREAD_CFLAGS
+PTHREAD_LIBS
+PTHREAD_CC
+acx_pthread_config
+alpine_interactive_spellcheck
+ISPELLPROG
+alpine_simple_spellcheck
+SPELLPROG
+PWPROG
+NPA_PROG
+SENDMAIL
+POSUB
+LTLIBINTL
+LIBINTL
+INTLLIBS
+LTLIBICONV
+LIBICONV
+INTL_MACOSX_LIBS
+MSGMERGE
+XGETTEXT_015
+XGETTEXT
+GMSGFMT_015
+MSGFMT_015
+GMSGFMT
+MSGFMT
+USE_NLS
+MAKE
+LN
+CP
+RM
+CPP
+OTOOL64
+OTOOL
+LIPO
+NMEDIT
+DSYMUTIL
+lt_ECHO
+AR
+OBJDUMP
+NM
+ac_ct_DUMPBIN
+DUMPBIN
+LD
+FGREP
+EGREP
+GREP
+SED
+LIBTOOL
+RANLIB
+LN_S
+am__fastdepCC_FALSE
+am__fastdepCC_TRUE
+CCDEPMODE
+AMDEPBACKSLASH
+AMDEP_FALSE
+AMDEP_TRUE
+am__quote
+am__include
+DEPDIR
+OBJEXT
+EXEEXT
+ac_ct_CC
+CPPFLAGS
+LDFLAGS
+CFLAGS
+CC
+host_os
+host_vendor
+host_cpu
+host
+build_os
+build_vendor
+build_cpu
+build
+MAINT
+MAINTAINER_MODE_FALSE
+MAINTAINER_MODE_TRUE
+am__untar
+am__tar
+AMTAR
+am__leading_dot
+SET_MAKE
+AWK
+mkdir_p
+MKDIR_P
+INSTALL_STRIP_PROGRAM
+STRIP
+install_sh
+MAKEINFO
+AUTOHEADER
+AUTOMAKE
+AUTOCONF
+ACLOCAL
+VERSION
+PACKAGE
+CYGPATH_W
+am__isrc
+INSTALL_DATA
+INSTALL_SCRIPT
+INSTALL_PROGRAM
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_URL
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+enable_maintainer_mode
+enable_dependency_tracking
+enable_shared
+enable_static
+with_pic
+enable_fast_install
+with_gnu_ld
+enable_libtool_lock
+enable_nls
+enable_rpath
+with_libiconv_prefix
+with_libintl_prefix
+enable_dmalloc
+with_dmalloc_dir
+with_localedir
+enable_osx_universal_binaries
+with_include_path
+with_lib_path
+with_pubcookie
+with_web_bin
+enable_debug
+enable_optimization
+enable_mouse
+enable_quotas
+enable_from_changing
+enable_background_post
+enable_keyboard_lock
+enable_from_encoding
+with_smtp_msa
+with_smtp_msa_flags
+with_npa
+with_npa_flags
+with_password_prog
+with_simple_spellcheck
+with_interactive_spellcheck
+with_system_pinerc
+with_system_fixed_pinerc
+with_mailcheck_interval
+with_checkpoint_interval
+with_checkpoint_frequency
+with_display_rows
+with_display_columns
+with_max_display_rows
+with_max_display_columns
+with_fill_column
+with_max_fill_column
+with_debug_level
+with_debug_files
+with_debug_file
+with_forwarded_keyword
+with_display_overlap
+with_display_margin
+with_default_fcc
+with_default_save_folder
+with_default_legacy_postponed_folder
+with_default_postponed_folder
+with_default_trash_folder
+with_default_interrupted_mail
+with_default_dead_letter_folder
+with_default_mail_directory
+with_default_inbox_name
+with_default_signature_file
+with_default_elm_style_save
+with_default_header_in_reply
+with_default_old_style_reply
+with_default_use_only_domain_name
+with_default_save_by_sender
+with_default_sort_key
+with_default_addressbook_sort_rule
+with_default_folder_sort_rule
+with_default_saved_message_name_rule
+with_default_fcc_rule
+with_default_standard_printer
+with_default_ansi_printer
+with_default_addressbook
+with_default_local_fullname
+with_default_local_address
+with_default_keyboard_lock_count
+with_default_remote_addressbook_history
+with_smime_public_cert_directory
+with_smime_private_key_directory
+with_smime_cacert_directory
+with_default_printer
+with_passfile
+with_local_password_cache
+with_local_password_cache_method
+with_default_sshpath
+with_default_sshcmd
+with_ssl
+with_ssl_dir
+with_ssl_certs_dir
+with_ssl_include_dir
+with_ssl_lib_dir
+with_krb5
+with_krb5_dir
+with_krb5_include_dir
+with_krb5_lib_dir
+with_ldap
+with_ldap_dir
+with_ldap_include_dir
+with_ldap_lib_dir
+with_smime
+with_tcl
+with_tcl_lib
+with_tcl_include
+with_supplied_regex
+with_pthread
+with_system_mail_directory
+with_c_client_target
+with_ipv6
+'
+ ac_precious_vars='build_alias
+host_alias
+target_alias
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+CPP'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval $ac_prev=\$ac_option
+ ac_prev=
+ continue
+ fi
+
+ case $ac_option in
+ *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+ *=) ac_optarg= ;;
+ *) ac_optarg=yes ;;
+ esac
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case $ac_dashdash$ac_option in
+ --)
+ ac_dashdash=yes ;;
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir=$ac_optarg ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build_alias ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build_alias=$ac_optarg ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file=$ac_optarg ;;
+
+ --config-cache | -C)
+ cache_file=config.cache ;;
+
+ -datadir | --datadir | --datadi | --datad)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=*)
+ datadir=$ac_optarg ;;
+
+ -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+ | --dataroo | --dataro | --datar)
+ ac_prev=datarootdir ;;
+ -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+ datarootdir=$ac_optarg ;;
+
+ -disable-* | --disable-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid feature name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"enable_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval enable_$ac_useropt=no ;;
+
+ -docdir | --docdir | --docdi | --doc | --do)
+ ac_prev=docdir ;;
+ -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+ docdir=$ac_optarg ;;
+
+ -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+ ac_prev=dvidir ;;
+ -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+ dvidir=$ac_optarg ;;
+
+ -enable-* | --enable-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid feature name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"enable_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval enable_$ac_useropt=\$ac_optarg ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix=$ac_optarg ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he | -h)
+ ac_init_help=long ;;
+ -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+ ac_init_help=recursive ;;
+ -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+ ac_init_help=short ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host_alias ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host_alias=$ac_optarg ;;
+
+ -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+ ac_prev=htmldir ;;
+ -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+ | --ht=*)
+ htmldir=$ac_optarg ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir=$ac_optarg ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir=$ac_optarg ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir=$ac_optarg ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir=$ac_optarg ;;
+
+ -localedir | --localedir | --localedi | --localed | --locale)
+ ac_prev=localedir ;;
+ -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+ localedir=$ac_optarg ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst | --locals)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+ localstatedir=$ac_optarg ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir=$ac_optarg ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c | -n)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir=$ac_optarg ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix=$ac_optarg ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix=$ac_optarg ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix=$ac_optarg ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name=$ac_optarg ;;
+
+ -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+ ac_prev=pdfdir ;;
+ -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+ pdfdir=$ac_optarg ;;
+
+ -psdir | --psdir | --psdi | --psd | --ps)
+ ac_prev=psdir ;;
+ -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+ psdir=$ac_optarg ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir=$ac_optarg ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir=$ac_optarg ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site=$ac_optarg ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir=$ac_optarg ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir=$ac_optarg ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target_alias ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target_alias=$ac_optarg ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers | -V)
+ ac_init_version=: ;;
+
+ -with-* | --with-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid package name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"with_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval with_$ac_useropt=\$ac_optarg ;;
+
+ -without-* | --without-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid package name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"with_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval with_$ac_useropt=no ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes=$ac_optarg ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries=$ac_optarg ;;
+
+ -*) as_fn_error $? "unrecognized option: \`$ac_option'
+Try \`$0 --help' for more information"
+ ;;
+
+ *=*)
+ ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+ # Reject names that are not valid shell variable names.
+ case $ac_envvar in #(
+ '' | [0-9]* | *[!_$as_cr_alnum]* )
+ as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
+ esac
+ eval $ac_envvar=\$ac_optarg
+ export $ac_envvar ;;
+
+ *)
+ # FIXME: should be removed in autoconf 3.0.
+ $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+ expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+ : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+ as_fn_error $? "missing argument to $ac_option"
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+ case $enable_option_checking in
+ no) ;;
+ fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
+ *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+ esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
+ datadir sysconfdir sharedstatedir localstatedir includedir \
+ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+ libdir localedir mandir
+do
+ eval ac_val=\$$ac_var
+ # Remove trailing slashes.
+ case $ac_val in
+ */ )
+ ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+ eval $ac_var=\$ac_val;;
+ esac
+ # Be sure to have absolute directory names.
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* ) continue;;
+ NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+ esac
+ as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+ if test "x$build_alias" = x; then
+ cross_compiling=maybe
+ $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host.
+ If a cross compiler is detected then cross compile mode will be used" >&2
+ elif test "x$build_alias" != "x$host_alias"; then
+ cross_compiling=yes
+ fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+ as_fn_error $? "working directory cannot be determined"
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+ as_fn_error $? "pwd does not report name of working directory"
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then the parent directory.
+ ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_myself" : 'X\(//\)[^/]' \| \
+ X"$as_myself" : 'X\(//\)$' \| \
+ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_myself" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ srcdir=$ac_confdir
+ if test ! -r "$srcdir/$ac_unique_file"; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+ test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+ as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+ cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
+ pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+ srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+ eval ac_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_env_${ac_var}_value=\$${ac_var}
+ eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat <<_ACEOF
+\`configure' configures alpine 2.10 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE. See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+ -h, --help display this help and exit
+ --help=short display options specific to this package
+ --help=recursive display the short help of all the included packages
+ -V, --version display version information and exit
+ -q, --quiet, --silent do not print \`checking ...' messages
+ --cache-file=FILE cache test results in FILE [disabled]
+ -C, --config-cache alias for \`--cache-file=config.cache'
+ -n, --no-create do not create output files
+ --srcdir=DIR find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ @<:@@S|@ac_default_prefix@:>@
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ @<:@PREFIX@:>@
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+ --bindir=DIR user executables [EPREFIX/bin]
+ --sbindir=DIR system admin executables [EPREFIX/sbin]
+ --libexecdir=DIR program executables [EPREFIX/libexec]
+ --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --libdir=DIR object code libraries [EPREFIX/lib]
+ --includedir=DIR C header files [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc [/usr/include]
+ --datarootdir=DIR read-only arch.-independent data root [PREFIX/share]
+ --datadir=DIR read-only architecture-independent data [DATAROOTDIR]
+ --infodir=DIR info documentation [DATAROOTDIR/info]
+ --localedir=DIR locale-dependent data [DATAROOTDIR/locale]
+ --mandir=DIR man documentation [DATAROOTDIR/man]
+ --docdir=DIR documentation root @<:@DATAROOTDIR/doc/alpine@:>@
+ --htmldir=DIR html documentation [DOCDIR]
+ --dvidir=DIR dvi documentation [DOCDIR]
+ --pdfdir=DIR pdf documentation [DOCDIR]
+ --psdir=DIR ps documentation [DOCDIR]
+_ACEOF
+
+ cat <<\_ACEOF
+
+Program names:
+ --program-prefix=PREFIX prepend PREFIX to installed program names
+ --program-suffix=SUFFIX append SUFFIX to installed program names
+ --program-transform-name=PROGRAM run sed PROGRAM on installed program names
+
+System types:
+ --build=BUILD configure for building on BUILD [guessed]
+ --host=HOST cross-compile to build programs to run on HOST [BUILD]
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+ case $ac_init_help in
+ short | recursive ) echo "Configuration of alpine 2.10:";;
+ esac
+ cat <<\_ACEOF
+
+Optional Features:
+ --disable-option-checking ignore unrecognized --enable/--with options
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --enable-maintainer-mode enable make rules and dependencies not useful
+ (and sometimes confusing) to the casual installer
+ --disable-dependency-tracking speeds up one-time build
+ --enable-dependency-tracking do not reject slow dependency extractors
+ --enable-shared@<:@=PKGS@:>@ build shared libraries @<:@default=yes@:>@
+ --enable-static@<:@=PKGS@:>@ build static libraries @<:@default=yes@:>@
+ --enable-fast-install@<:@=PKGS@:>@
+ optimize for fast installation @<:@default=yes@:>@
+ --disable-libtool-lock avoid locking (might break parallel builds)
+ --disable-nls do not use Native Language Support
+ --disable-rpath do not hardcode runtime library paths
+ --enable-dmalloc Enable dmalloc debugging
+ --enable-osx-universal-binaries
+ Produce universal binaries under OS X @<:@@<:@default=no@:>@@:>@
+ --disable-debug Exclude debug messages from source
+ --disable-optimization Exclude optimizing compiler flags
+ --disable-mouse Disable mouse support
+ --enable-quotas Enable disk quota checking on startup
+ --disable-from-changing Disallow users changing From addresss
+ --disable-background-post
+ Disable background posting
+ --disable-keyboard-lock Disable keyboard locking
+ --enable-from-encoding Enable From encoding in sent messages
+
+Optional Packages:
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --with-pic try to use only PIC/non-PIC objects @<:@default=use
+ both@:>@
+ --with-gnu-ld assume the C compiler uses GNU ld @<:@default=no@:>@
+ --with-gnu-ld assume the C compiler uses GNU ld default=no
+ --with-libiconv-prefix[=DIR] search for libiconv in DIR/include and DIR/lib
+ --without-libiconv-prefix don't search for libiconv in includedir and libdir
+ --with-libintl-prefix[=DIR] search for libintl in DIR/include and DIR/lib
+ --without-libintl-prefix don't search for libintl in includedir and libdir
+ --with-dmalloc-dir=DIR Root of dmalloc lib/include path
+ --with-localedir=DIR Name of gettext locale directory
+ --with-include-path=PATHS
+ Colon-separated list of directories used for include
+ file search
+ --with-lib-path=PATHS Colon-separated list of directories used for library
+ search
+ --with-pubcookie Include support for UW-Pubcookie Web Authentication
+ --with-web-bin=PATH Directory to hold Web Alpine component binary files
+ --with-smtp-msa=PATH Local Mail Submission Agent (sendmail)
+ --with-smtp-msa-flags=FLAGS
+ MSA flags for SMTP on stdin/stdout (-bs -odb -oem)
+ --with-npa=PATH Posting agent when no nntp-servers defined (inews)
+ --with-npa-flags=FLAGS Flags to allow posting via local agent (-h)
+ --with-password-prog=PATH
+ Password change program (/bin/passwd)
+ --with-simple-spellcheck=PROG
+ Spellcheck program reads stdin, emits misspellings
+ on stdout
+ --with-interactive-spellcheck=PROG
+ Interactive, filewise spell checker
+ --with-system-pinerc=VALUE
+ System pinerc (/usr/local/lib/pine.conf)
+ --with-system-fixed-pinerc=VALUE
+ System fixed pinerc (/usr/local/lib/pine.conf.fixed)
+ --with-mailcheck-interval=VALUE
+ Specify default mail-check-interval (150)
+ --with-checkpoint-interval=VALUE
+ Specify default checkpoint-interval (420)
+ --with-checkpoint-frequency=VALUE
+ State change count before checkpoint (12)
+ --with-display-rows=VALUE
+ Initial rows on display (24)
+ --with-display-columns=VALUE
+ Initial columns on display (80)
+ --with-max-display-rows=VALUE
+ Maximum display rows (200)
+ --with-max-display-columns=VALUE
+ Maximum display columns (500)
+ --with-fill-column=VALUE
+ Default fill column (74)
+ --with-max_fill-column=VALUE
+ Maximum fill column (80)
+ --with-debug-level=VALUE
+ Specify default debug verbosity level (2)
+ --with-debug-files=VALUE
+ Specify number of debug files (4)
+ --with-debug-file=VALUE Specify debug file name (.pine-debug)
+ --with-forwarded-keyword=VALUE
+ IMAP (c-client) keyword to store forwarded status
+ ("\@S|@Forwarded")
+ --with-display-overlap=VALUE
+ Lines preserved while paging (2)
+ --with-display-margin=VALUE
+ Lines visible while scrolling (0)
+ --with-default-fcc=VALUE
+ Default sent mail folder (sent-mail)
+ --with-default-save-folder=VALUE
+ Default save folder (saved-messages)
+ --with-default-legacy-postponed-folder=VALUE
+ Pre Pine 3.90 postponed folder (postponed-mail)
+ --with-default-postponed-folder=VALUE
+ Default postponed folder (postponed-msgs)
+ --with-default-trash-folder=VALUE
+ Default Trash folder for Web Alpine (Trash)
+ --with-default-interrupted-mail=VALUE
+ Default folder for interrupted mail
+ (.pine-interrupted-mail)
+ --with-default-dead-letter-folder=VALUE
+ Default dead letter folder (dead.letter)
+ --with-default-mail-directory=VALUE
+ Default mail directory (mail)
+ --with-default-inbox-name=VALUE
+ Default inbox name (INBOX)
+ --with-default-signature-file=VALUE
+ Default signature file (.signature)
+ --with-default-elm-style-save=VALUE
+ Default to Elm style save (no)
+ --with-default-header-in-reply=VALUE
+ Include header in reply (no)
+ --with-default-old-style-reply=VALUE
+ Default to old style reply (no)
+ --with-default-use-only-domain-name=VALUE
+ Default to using only the domain name (no)
+ --with-default-save-by-sender=VALUE
+ Default to save by sender (no)
+ --with-default-sort-key=VALUE
+ Default sort key (arrival)
+ --with-default-addressbook-sort-rule=VALUE
+ Default addressbook sort rule
+ (fullname-with-lists-last)
+ --with-default-folder-sort-rule=VALUE
+ Default folder sort rule (alphabetical)
+ --with-default-saved-message-name-rule=VALUE
+ Default saved message name rule (default-folder)
+ --with-default-fcc-rule=VALUE
+ Default fcc rule (default-fcc)
+ --with-default-standard-printer=VALUE
+ Default standard printern (lpr)
+ --with-default-ansi-printer=VALUE
+ ANSI printer definition (attached-to-ansi)
+ --with-default-addressbook=VALUE
+ Default addressbook name (.addressbook)
+ --with-default-local-fullname=VALUE
+ Default local support fullname ("Local Support")
+ --with-default-local-address=VALUE
+ Default local support address (postmaster)
+ --with-default-keyboard-lock-count=VALUE
+ Default keyboard lock count (1)
+ --with-default-remote-addressbook-history=VALUE
+ Default address book history count (3)
+ --with-smime-public-cert-directory=VALUE
+ Default Public Cert Directory (.alpine-smime/public)
+ --with-smime-private-key-directory=VALUE
+ Default Private Key Directory
+ (.alpine-smime/private)
+ --with-smime-cacert-directory=VALUE
+ Default Cert Authority Directory (.alpine-smime/ca)
+ --with-default-printer=VALUE
+ Default printer (ANSI_PRINTER)
+ --with-passfile=FILENAME
+ Password cache file (NOT secure, NOT recommended)
+ --without-local-password-cache
+ Disable OS-specific password cache, if supported
+ --with-local-password-cache-method
+ OS-specific credential cache (OSX=APPLEKEYCHAIN,
+ Windows=WINCRED)
+ --with-default-sshpath=FILENAME
+ set default value of ssh command path (defining
+ should cause ssh to be preferred to rsh)
+ --with-default-sshcmd=PERCENT_S_STRING
+ set default value of ssh command string (usually "%s
+ %s -l %s exec /etc/r%sd")
+ --without-ssl Disable SSL support (OpenSSL)
+ --with-ssl-dir=DIR Root of SSL lib/include path
+ --with-ssl-certs-dir=DIR
+ Path to SSL certificate directory
+ --with-ssl-include-dir=DIR
+ SSL include file path
+ --with-ssl-lib-dir=DIR SSL library path
+ --without-krb5 Disable Kerberos support
+ --with-krb5-dir=DIR Root of Kerberos lib/include path
+ --with-krb5-include-dir=DIR
+ Kerberos include file path
+ --with-krb5-lib-dir=DIR Kerberos library path
+ --without-ldap Disable LDAP query support
+ --with-ldap-dir=DIR Root of LDAP lib/include path
+ --with-ldap-include-dir=DIR
+ Directory containing LDAP include files
+ --with-ldap-lib-dir=DIR LDAP library path
+ --without-smime Disable S/MIME
+ --without-tcl Disable TCL, thus Web Alpine support
+ --with-tcl-lib=LIBRARY Specific TCL Library, like \"tcl8.4\"
+ --with-tcl-include=DIR Directory containing TCL include files
+ --with-supplied-regex Use regex library supplied with alpine
+ --without-pthread Do NOT test for nor build with POSIX thread support
+ --with-system-mail-directory=DIR
+ Directory where local mail is delivered
+ --with-c-client-target=TARGET
+ IMAP build target (see imap/Makefile)
+ --without-ipv6 Disable IPv6, primarily to work around resolver
+ problems
+
+Some influential environment variables:
+ CC C compiler command
+ CFLAGS C compiler flags
+ LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
+ nonstandard directory <lib dir>
+ LIBS libraries to pass to the linker, e.g. -l<library>
+ CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
+ you have headers in a nonstandard directory <include dir>
+ CPP C preprocessor
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to <alpine-contact@u.washington.edu>.
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+ # If there are subdirs, report their specific --help.
+ for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+ test -d "$ac_dir" ||
+ { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+ continue
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+ cd "$ac_dir" || { ac_status=$?; continue; }
+ # Check for guested configure.
+ if test -f "$ac_srcdir/configure.gnu"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+ elif test -f "$ac_srcdir/configure"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure" --help=recursive
+ else
+ $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+ fi || ac_status=$?
+ cd "$ac_pwd" || { ac_status=$?; break; }
+ done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+ cat <<\_ACEOF
+alpine configure 2.10
+generated by GNU Autoconf 2.68
+
+Copyright (C) 2010 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+ exit
+fi
+
+## ------------------------ ##
+## Autoconf initialization. ##
+## ------------------------ ##
+
+@%:@ ac_fn_c_try_compile LINENO
+@%:@ --------------------------
+@%:@ Try to compile conftest.@S|@ac_ext, and return whether this succeeded.
+ac_fn_c_try_compile ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ rm -f conftest.$ac_objext
+ if { { ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compile") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} @%:@ ac_fn_c_try_compile
+
+@%:@ ac_fn_c_try_link LINENO
+@%:@ -----------------------
+@%:@ Try to link conftest.@S|@ac_ext, and return whether this succeeded.
+ac_fn_c_try_link ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ rm -f conftest.$ac_objext conftest$ac_exeext
+ if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
+ # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
+ # interfere with the next link command; also delete a directory that is
+ # left behind by Apple's compiler. We do this before executing the actions.
+ rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} @%:@ ac_fn_c_try_link
+
+@%:@ ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
+@%:@ -------------------------------------------------------
+@%:@ Tests whether HEADER exists and can be compiled using the include files in
+@%:@ INCLUDES, setting the cache variable VAR accordingly.
+ac_fn_c_check_header_compile ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+@%:@include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ eval "$3=yes"
+else
+ eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} @%:@ ac_fn_c_check_header_compile
+
+@%:@ ac_fn_c_try_cpp LINENO
+@%:@ ----------------------
+@%:@ Try to preprocess conftest.@S|@ac_ext, and return whether this succeeded.
+ac_fn_c_try_cpp ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if { { ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } > conftest.i && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} @%:@ ac_fn_c_try_cpp
+
+@%:@ ac_fn_c_try_run LINENO
+@%:@ ----------------------
+@%:@ Try to link conftest.@S|@ac_ext, and return whether this succeeded. Assumes
+@%:@ that executables *can* be run.
+ac_fn_c_try_run ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'
+ { { case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; }; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: program exited with status $ac_status" >&5
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=$ac_status
+fi
+ rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} @%:@ ac_fn_c_try_run
+
+@%:@ ac_fn_c_check_func LINENO FUNC VAR
+@%:@ ----------------------------------
+@%:@ Tests whether FUNC exists, setting the cache variable VAR accordingly
+ac_fn_c_check_func ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $2 innocuous_$2
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $2 (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $2
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $2 ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_$2 || defined __stub___$2
+choke me
+#endif
+
+int
+main ()
+{
+return $2 ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ eval "$3=yes"
+else
+ eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} @%:@ ac_fn_c_check_func
+
+@%:@ ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES
+@%:@ -------------------------------------------------------
+@%:@ Tests whether HEADER exists, giving a warning if it cannot be compiled using
+@%:@ the include files in INCLUDES and setting the cache variable VAR
+@%:@ accordingly.
+ac_fn_c_check_header_mongrel ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if eval \${$3+:} false; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5
+$as_echo_n "checking $2 usability... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+@%:@include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_header_compiler=yes
+else
+ ac_header_compiler=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5
+$as_echo_n "checking $2 presence... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+@%:@include <$2>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ ac_header_preproc=yes
+else
+ ac_header_preproc=no
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #((
+ yes:no: )
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+( $as_echo "## ---------------------------------------------- ##
+## Report this to alpine-contact@u.washington.edu ##
+## ---------------------------------------------- ##"
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ eval "$3=\$ac_header_compiler"
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} @%:@ ac_fn_c_check_header_mongrel
+
+@%:@ ac_fn_c_check_type LINENO TYPE VAR INCLUDES
+@%:@ -------------------------------------------
+@%:@ Tests whether TYPE exists after having included INCLUDES, setting cache
+@%:@ variable VAR accordingly.
+ac_fn_c_check_type ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ eval "$3=no"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+if (sizeof ($2))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+if (sizeof (($2)))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+ eval "$3=yes"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} @%:@ ac_fn_c_check_type
+
+@%:@ ac_fn_c_compute_int LINENO EXPR VAR INCLUDES
+@%:@ --------------------------------------------
+@%:@ Tries to find the compile-time value of EXPR in a program that includes
+@%:@ INCLUDES, setting VAR accordingly. Returns whether the value could be
+@%:@ computed
+ac_fn_c_compute_int ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if test "$cross_compiling" = yes; then
+ # Depending upon the size, compute the lo and hi bounds.
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+static int test_array @<:@1 - 2 * !(($2) >= 0)@:>@;
+test_array @<:@0@:>@ = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_lo=0 ac_mid=0
+ while :; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+static int test_array @<:@1 - 2 * !(($2) <= $ac_mid)@:>@;
+test_array @<:@0@:>@ = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_hi=$ac_mid; break
+else
+ as_fn_arith $ac_mid + 1 && ac_lo=$as_val
+ if test $ac_lo -le $ac_mid; then
+ ac_lo= ac_hi=
+ break
+ fi
+ as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ done
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+static int test_array @<:@1 - 2 * !(($2) < 0)@:>@;
+test_array @<:@0@:>@ = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_hi=-1 ac_mid=-1
+ while :; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+static int test_array @<:@1 - 2 * !(($2) >= $ac_mid)@:>@;
+test_array @<:@0@:>@ = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_lo=$ac_mid; break
+else
+ as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val
+ if test $ac_mid -le $ac_hi; then
+ ac_lo= ac_hi=
+ break
+ fi
+ as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ done
+else
+ ac_lo= ac_hi=
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+# Binary search between lo and hi bounds.
+while test "x$ac_lo" != "x$ac_hi"; do
+ as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+static int test_array @<:@1 - 2 * !(($2) <= $ac_mid)@:>@;
+test_array @<:@0@:>@ = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_hi=$ac_mid
+else
+ as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+case $ac_lo in @%:@((
+?*) eval "$3=\$ac_lo"; ac_retval=0 ;;
+'') ac_retval=1 ;;
+esac
+ else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+static long int longval () { return $2; }
+static unsigned long int ulongval () { return $2; }
+@%:@include <stdio.h>
+@%:@include <stdlib.h>
+int
+main ()
+{
+
+ FILE *f = fopen ("conftest.val", "w");
+ if (! f)
+ return 1;
+ if (($2) < 0)
+ {
+ long int i = longval ();
+ if (i != ($2))
+ return 1;
+ fprintf (f, "%ld", i);
+ }
+ else
+ {
+ unsigned long int i = ulongval ();
+ if (i != ($2))
+ return 1;
+ fprintf (f, "%lu", i);
+ }
+ /* Do not output a trailing newline, as this causes \r\n confusion
+ on some platforms. */
+ return ferror (f) || fclose (f) != 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ echo >>conftest.val; read $3 <conftest.val; ac_retval=0
+else
+ ac_retval=1
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+rm -f conftest.val
+
+ fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} @%:@ ac_fn_c_compute_int
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by alpine $as_me 2.10, which was
+generated by GNU Autoconf 2.68. Invocation command line was
+
+ $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`
+
+/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown`
+/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`
+/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ $as_echo "PATH: $as_dir"
+ done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+ for ac_arg
+ do
+ case $ac_arg in
+ -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ continue ;;
+ *\'*)
+ ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ case $ac_pass in
+ 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
+ 2)
+ as_fn_append ac_configure_args1 " '$ac_arg'"
+ if test $ac_must_keep_next = true; then
+ ac_must_keep_next=false # Got value, back to normal.
+ else
+ case $ac_arg in
+ *=* | --config-cache | -C | -disable-* | --disable-* \
+ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+ | -with-* | --with-* | -without-* | --without-* | --x)
+ case "$ac_configure_args0 " in
+ "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+ esac
+ ;;
+ -* ) ac_must_keep_next=true ;;
+ esac
+ fi
+ as_fn_append ac_configure_args " '$ac_arg'"
+ ;;
+ esac
+ done
+done
+{ ac_configure_args0=; unset ac_configure_args0;}
+{ ac_configure_args1=; unset ac_configure_args1;}
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log. We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+ # Save into config.log some information that might help in debugging.
+ {
+ echo
+
+ $as_echo "## ---------------- ##
+## Cache variables. ##
+## ---------------- ##"
+ echo
+ # The following way of writing the cache mishandles newlines in values,
+(
+ for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+ *) { eval $ac_var=; unset $ac_var;} ;;
+ esac ;;
+ esac
+ done
+ (set) 2>&1 |
+ case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ sed -n \
+ "s/'\''/'\''\\\\'\'''\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+ ;; #(
+ *)
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+)
+ echo
+
+ $as_echo "## ----------------- ##
+## Output variables. ##
+## ----------------- ##"
+ echo
+ for ac_var in $ac_subst_vars
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ $as_echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+
+ if test -n "$ac_subst_files"; then
+ $as_echo "## ------------------- ##
+## File substitutions. ##
+## ------------------- ##"
+ echo
+ for ac_var in $ac_subst_files
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ $as_echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+ fi
+
+ if test -s confdefs.h; then
+ $as_echo "## ----------- ##
+## confdefs.h. ##
+## ----------- ##"
+ echo
+ cat confdefs.h
+ echo
+ fi
+ test "$ac_signal" != 0 &&
+ $as_echo "$as_me: caught signal $ac_signal"
+ $as_echo "$as_me: exit $exit_status"
+ } >&5
+ rm -f core *.core core.conftest.* &&
+ rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+ exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+ trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+$as_echo "/* confdefs.h */" > confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE_URL "$PACKAGE_URL"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+ac_site_file1=NONE
+ac_site_file2=NONE
+if test -n "$CONFIG_SITE"; then
+ # We do not want a PATH search for config.site.
+ case $CONFIG_SITE in @%:@((
+ -*) ac_site_file1=./$CONFIG_SITE;;
+ */*) ac_site_file1=$CONFIG_SITE;;
+ *) ac_site_file1=./$CONFIG_SITE;;
+ esac
+elif test "x$prefix" != xNONE; then
+ ac_site_file1=$prefix/share/config.site
+ ac_site_file2=$prefix/etc/config.site
+else
+ ac_site_file1=$ac_default_prefix/share/config.site
+ ac_site_file2=$ac_default_prefix/etc/config.site
+fi
+for ac_site_file in "$ac_site_file1" "$ac_site_file2"
+do
+ test "x$ac_site_file" = xNONE && continue
+ if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+$as_echo "$as_me: loading site script $ac_site_file" >&6;}
+ sed 's/^/| /' "$ac_site_file" >&5
+ . "$ac_site_file" \
+ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed to load site script $ac_site_file
+See \`config.log' for more details" "$LINENO" 5; }
+ fi
+done
+
+if test -r "$cache_file"; then
+ # Some versions of bash will fail to source /dev/null (special files
+ # actually), so we avoid doing that. DJGPP emulates it as a regular file.
+ if test /dev/null != "$cache_file" && test -f "$cache_file"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
+$as_echo "$as_me: loading cache $cache_file" >&6;}
+ case $cache_file in
+ [\\/]* | ?:[\\/]* ) . "$cache_file";;
+ *) . "./$cache_file";;
+ esac
+ fi
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
+$as_echo "$as_me: creating cache $cache_file" >&6;}
+ >$cache_file
+fi
+
+gt_needs="$gt_needs "
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+ eval ac_old_set=\$ac_cv_env_${ac_var}_set
+ eval ac_new_set=\$ac_env_${ac_var}_set
+ eval ac_old_val=\$ac_cv_env_${ac_var}_value
+ eval ac_new_val=\$ac_env_${ac_var}_value
+ case $ac_old_set,$ac_new_set in
+ set,)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,set)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,);;
+ *)
+ if test "x$ac_old_val" != "x$ac_new_val"; then
+ # differences in whitespace do not lead to failure.
+ ac_old_val_w=`echo x $ac_old_val`
+ ac_new_val_w=`echo x $ac_new_val`
+ if test "$ac_old_val_w" != "$ac_new_val_w"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
+$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+ ac_cache_corrupted=:
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+ eval $ac_var=\$ac_old_val
+ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5
+$as_echo "$as_me: former value: \`$ac_old_val'" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5
+$as_echo "$as_me: current value: \`$ac_new_val'" >&2;}
+ fi;;
+ esac
+ # Pass precious variables to config.status.
+ if test "$ac_new_set" = set; then
+ case $ac_new_val in
+ *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+ *) ac_arg=$ac_var=$ac_new_val ;;
+ esac
+ case " $ac_configure_args " in
+ *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
+ *) as_fn_append ac_configure_args " '$ac_arg'" ;;
+ esac
+ fi
+done
+if $ac_cache_corrupted; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
+$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+ as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
+fi
+## -------------------- ##
+## Main body of script. ##
+## -------------------- ##
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+ac_config_headers="$ac_config_headers include/config.h"
+
+
+am__api_version='1.11'
+
+ac_aux_dir=
+for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
+ if test -f "$ac_dir/install-sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f "$ac_dir/install.sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ elif test -f "$ac_dir/shtool"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/shtool install -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5
+fi
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var.
+ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var.
+ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
+
+
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+# Reject install programs that cannot install multiple files.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5
+$as_echo_n "checking for a BSD-compatible install... " >&6; }
+if test -z "$INSTALL"; then
+if ${ac_cv_path_install+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in @%:@((
+ ./ | .// | /[cC]/* | \
+ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \
+ /usr/ucb/* ) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ # Don't use installbsd from OSF since it installs stuff as root
+ # by default.
+ for ac_prog in ginstall scoinst install; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then
+ if test $ac_prog = install &&
+ grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ :
+ elif test $ac_prog = install &&
+ grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # program-specific install script used by HP pwplus--don't use.
+ :
+ else
+ rm -rf conftest.one conftest.two conftest.dir
+ echo one > conftest.one
+ echo two > conftest.two
+ mkdir conftest.dir
+ if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" &&
+ test -s conftest.one && test -s conftest.two &&
+ test -s conftest.dir/conftest.one &&
+ test -s conftest.dir/conftest.two
+ then
+ ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+ break 3
+ fi
+ fi
+ fi
+ done
+ done
+ ;;
+esac
+
+ done
+IFS=$as_save_IFS
+
+rm -rf conftest.one conftest.two conftest.dir
+
+fi
+ if test "${ac_cv_path_install+set}" = set; then
+ INSTALL=$ac_cv_path_install
+ else
+ # As a last resort, use the slow shell script. Don't cache a
+ # value for INSTALL within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the value is a relative name.
+ INSTALL=$ac_install_sh
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5
+$as_echo "$INSTALL" >&6; }
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5
+$as_echo_n "checking whether build environment is sane... " >&6; }
+# Just in case
+sleep 1
+echo timestamp > conftest.file
+# Reject unsafe characters in $srcdir or the absolute working directory
+# name. Accept space and tab only in the latter.
+am_lf='
+'
+case `pwd` in
+ *[\\\"\#\$\&\'\`$am_lf]*)
+ as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;;
+esac
+case $srcdir in
+ *[\\\"\#\$\&\'\`$am_lf\ \ ]*)
+ as_fn_error $? "unsafe srcdir value: \`$srcdir'" "$LINENO" 5;;
+esac
+
+# Do `set' in a subshell so we don't clobber the current shell's
+# arguments. Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+ set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
+ if test "$*" = "X"; then
+ # -L didn't work.
+ set X `ls -t "$srcdir/configure" conftest.file`
+ fi
+ rm -f conftest.file
+ if test "$*" != "X $srcdir/configure conftest.file" \
+ && test "$*" != "X conftest.file $srcdir/configure"; then
+
+ # If neither matched, then we have a broken ls. This can happen
+ # if, for instance, CONFIG_SHELL is bash and it inherits a
+ # broken ls alias from the environment. This has actually
+ # happened. Such a system could not be considered "sane".
+ as_fn_error $? "ls -t appears to fail. Make sure there is not a broken
+alias in your environment" "$LINENO" 5
+ fi
+
+ test "$2" = conftest.file
+ )
+then
+ # Ok.
+ :
+else
+ as_fn_error $? "newly created file is older than distributed files!
+Check your system clock" "$LINENO" 5
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+test "$program_prefix" != NONE &&
+ program_transform_name="s&^&$program_prefix&;$program_transform_name"
+# Use a double $ so make ignores it.
+test "$program_suffix" != NONE &&
+ program_transform_name="s&\$&$program_suffix&;$program_transform_name"
+# Double any \ or $.
+# By default was `s,x,x', remove it if useless.
+ac_script='s/[\\$]/&&/g;s/;s,x,x,$//'
+program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"`
+
+# expand $ac_aux_dir to an absolute path
+am_aux_dir=`cd $ac_aux_dir && pwd`
+
+if test x"${MISSING+set}" != xset; then
+ case $am_aux_dir in
+ *\ * | *\ *)
+ MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
+ *)
+ MISSING="\${SHELL} $am_aux_dir/missing" ;;
+ esac
+fi
+# Use eval to expand $SHELL
+if eval "$MISSING --run true"; then
+ am_missing_run="$MISSING --run "
+else
+ am_missing_run=
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`missing' script is too old or missing" >&5
+$as_echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;}
+fi
+
+if test x"${install_sh}" != xset; then
+ case $am_aux_dir in
+ *\ * | *\ *)
+ install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
+ *)
+ install_sh="\${SHELL} $am_aux_dir/install-sh"
+ esac
+fi
+
+# Installed binaries are usually stripped using `strip' when the user
+# run `make install-strip'. However `strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the `STRIP' environment variable to overrule this program.
+if test "$cross_compiling" != no; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_STRIP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$STRIP"; then
+ ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5
+$as_echo "$STRIP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_STRIP"; then
+ ac_ct_STRIP=$STRIP
+ # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_STRIP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_STRIP"; then
+ ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_STRIP="strip"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5
+$as_echo "$ac_ct_STRIP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_STRIP" = x; then
+ STRIP=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ STRIP=$ac_ct_STRIP
+ fi
+else
+ STRIP="$ac_cv_prog_STRIP"
+fi
+
+fi
+INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5
+$as_echo_n "checking for a thread-safe mkdir -p... " >&6; }
+if test -z "$MKDIR_P"; then
+ if ${ac_cv_path_mkdir+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in mkdir gmkdir; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; } || continue
+ case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #(
+ 'mkdir (GNU coreutils) '* | \
+ 'mkdir (coreutils) '* | \
+ 'mkdir (fileutils) '4.1*)
+ ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext
+ break 3;;
+ esac
+ done
+ done
+ done
+IFS=$as_save_IFS
+
+fi
+
+ test -d ./--version && rmdir ./--version
+ if test "${ac_cv_path_mkdir+set}" = set; then
+ MKDIR_P="$ac_cv_path_mkdir -p"
+ else
+ # As a last resort, use the slow shell script. Don't cache a
+ # value for MKDIR_P within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the value is a relative name.
+ MKDIR_P="$ac_install_sh -d"
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5
+$as_echo "$MKDIR_P" >&6; }
+
+mkdir_p="$MKDIR_P"
+case $mkdir_p in
+ [\\/$]* | ?:[\\/]*) ;;
+ */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;;
+esac
+
+for ac_prog in gawk mawk nawk awk
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_AWK+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$AWK"; then
+ ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_AWK="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+AWK=$ac_cv_prog_AWK
+if test -n "$AWK"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5
+$as_echo "$AWK" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$AWK" && break
+done
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5
+$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; }
+set x ${MAKE-make}
+ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
+if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.make <<\_ACEOF
+SHELL = /bin/sh
+all:
+ @echo '@@@%%%=$(MAKE)=@@@%%%'
+_ACEOF
+# GNU make sometimes prints "make[1]: Entering ...", which would confuse us.
+case `${MAKE-make} -f conftest.make 2>/dev/null` in
+ *@@@%%%=?*=@@@%%%*)
+ eval ac_cv_prog_make_${ac_make}_set=yes;;
+ *)
+ eval ac_cv_prog_make_${ac_make}_set=no;;
+esac
+rm -f conftest.make
+fi
+if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ SET_MAKE=
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+ am__leading_dot=.
+else
+ am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+
+if test "`cd $srcdir && pwd`" != "`pwd`"; then
+ # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
+ # is not polluted with repeated "-I."
+ am__isrc=' -I$(srcdir)'
+ # test to see if srcdir already configured
+ if test -f $srcdir/config.status; then
+ as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5
+ fi
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+ if (cygpath --version) >/dev/null 2>/dev/null; then
+ CYGPATH_W='cygpath -w'
+ else
+ CYGPATH_W=echo
+ fi
+fi
+
+
+# Define the identity of the package.
+ PACKAGE='alpine'
+ VERSION='2.10'
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PACKAGE "$PACKAGE"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define VERSION "$VERSION"
+_ACEOF
+
+# Some tools Automake needs.
+
+ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"}
+
+
+AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"}
+
+
+AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"}
+
+
+AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"}
+
+
+MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"}
+
+# We need awk for the "check" target. The system "awk" is bad on
+# some platforms.
+# Always define AMTAR for backward compatibility.
+
+AMTAR=${AMTAR-"${am_missing_run}tar"}
+
+am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable maintainer-specific portions of Makefiles" >&5
+$as_echo_n "checking whether to enable maintainer-specific portions of Makefiles... " >&6; }
+ @%:@ Check whether --enable-maintainer-mode was given.
+if test "${enable_maintainer_mode+set}" = set; then :
+ enableval=$enable_maintainer_mode; USE_MAINTAINER_MODE=$enableval
+else
+ USE_MAINTAINER_MODE=no
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $USE_MAINTAINER_MODE" >&5
+$as_echo "$USE_MAINTAINER_MODE" >&6; }
+ if test $USE_MAINTAINER_MODE = yes; then
+ MAINTAINER_MODE_TRUE=
+ MAINTAINER_MODE_FALSE='#'
+else
+ MAINTAINER_MODE_TRUE='#'
+ MAINTAINER_MODE_FALSE=
+fi
+
+ MAINT=$MAINTAINER_MODE_TRUE
+
+
+
+# Make sure we can run config.sub.
+$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
+ as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5
+$as_echo_n "checking build system type... " >&6; }
+if ${ac_cv_build+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_build_alias=$build_alias
+test "x$ac_build_alias" = x &&
+ ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"`
+test "x$ac_build_alias" = x &&
+ as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5
+ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` ||
+ as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5
+$as_echo "$ac_cv_build" >&6; }
+case $ac_cv_build in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;;
+esac
+build=$ac_cv_build
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_build
+shift
+build_cpu=$1
+build_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+build_os=$*
+IFS=$ac_save_IFS
+case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5
+$as_echo_n "checking host system type... " >&6; }
+if ${ac_cv_host+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "x$host_alias" = x; then
+ ac_cv_host=$ac_cv_build
+else
+ ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` ||
+ as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5
+$as_echo "$ac_cv_host" >&6; }
+case $ac_cv_host in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;;
+esac
+host=$ac_cv_host
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_host
+shift
+host_cpu=$1
+host_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+host_os=$*
+IFS=$ac_save_IFS
+case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: Configuring for $PACKAGE_STRING ($host))" >&5
+$as_echo "$as_me: Configuring for $PACKAGE_STRING ($host))" >&6;}
+
+# start out with intent to build Web Alpine
+WEB_BUILD=web/src/alpined.d
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="${ac_tool_prefix}gcc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_CC="gcc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="${ac_tool_prefix}cc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ fi
+fi
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $@%:@ != 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+ fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in cl.exe
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$CC" && break
+ done
+fi
+if test -z "$CC"; then
+ ac_ct_CC=$CC
+ for ac_prog in cl.exe
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_CC="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_CC" && break
+done
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+fi
+
+fi
+
+
+test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "no acceptable C compiler found in \$PATH
+See \`config.log' for more details" "$LINENO" 5; }
+
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+ { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ sed '10a\
+... rest of stderr output deleted ...
+ 10q' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ fi
+ rm -f conftest.er1 conftest.err
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+done
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
+$as_echo_n "checking whether the C compiler works... " >&6; }
+ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+ * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+ esac
+done
+rm -f $ac_rmfiles
+
+if { { ac_try="$ac_link_default"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link_default") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile. We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+ ;;
+ [ab].out )
+ # We found the default executable, but exeext='' is most
+ # certainly right.
+ break;;
+ *.* )
+ if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+ then :; else
+ ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ fi
+ # We set ac_cv_exeext here because the later test for it is not
+ # safe: cross compilers may not add the suffix if given an `-o'
+ # argument, so we may need to know it at that point already.
+ # Even if this section looks crufty: it has the advantage of
+ # actually working.
+ break;;
+ * )
+ break;;
+ esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+ ac_file=''
+fi
+if test -z "$ac_file"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "C compiler cannot create executables
+See \`config.log' for more details" "$LINENO" 5; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
+$as_echo_n "checking for C compiler default output file name... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+$as_echo "$ac_file" >&6; }
+ac_exeext=$ac_cv_exeext
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
+$as_echo_n "checking for suffix of executables... " >&6; }
+if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+ *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ break;;
+ * ) break;;
+ esac
+done
+else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest conftest$ac_cv_exeext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+$as_echo "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+@%:@include <stdio.h>
+int
+main ()
+{
+FILE *f = fopen ("conftest.out", "w");
+ return ferror (f) || fclose (f) != 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files="$ac_clean_files conftest.out"
+# Check that the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+$as_echo_n "checking whether we are cross compiling... " >&6; }
+if test "$cross_compiling" != yes; then
+ { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+ if { ac_try='./conftest$ac_cv_exeext'
+ { { case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; }; then
+ cross_compiling=no
+ else
+ if test "$cross_compiling" = maybe; then
+ cross_compiling=yes
+ else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details" "$LINENO" 5; }
+ fi
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
+$as_echo "$cross_compiling" >&6; }
+
+rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
+$as_echo_n "checking for suffix of object files... " >&6; }
+if ${ac_cv_objext+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { { ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compile") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ for ac_file in conftest.o conftest.obj conftest.*; do
+ test -f "$ac_file" || continue;
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+ *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+ break;;
+ esac
+done
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of object files: cannot compile
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
+$as_echo "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
+$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
+if ${ac_cv_c_compiler_gnu+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_compiler_gnu=yes
+else
+ ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+ GCC=yes
+else
+ GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+$as_echo_n "checking whether $CC accepts -g... " >&6; }
+if ${ac_cv_prog_cc_g+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_save_c_werror_flag=$ac_c_werror_flag
+ ac_c_werror_flag=yes
+ ac_cv_prog_cc_g=no
+ CFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_g=yes
+else
+ CFLAGS=""
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+ ac_c_werror_flag=$ac_save_c_werror_flag
+ CFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+$as_echo "$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if ${ac_cv_prog_cc_c89+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
+ function prototypes and stuff, but not '\xHH' hex character constants.
+ These don't provoke an error unfortunately, instead are silently treated
+ as 'x'. The following induces an error, until -std is added to get
+ proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
+ array size at least. It's necessary to write '\x00'==0 to get something
+ that's true only with -std. */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+ inside strings and character constants. */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
+ ;
+ return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+ test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+ x)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+ xno)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+ *)
+ CC="$CC $ac_cv_prog_cc_c89"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c89" != xno; then :
+
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+DEPDIR="${am__leading_dot}deps"
+
+ac_config_commands="$ac_config_commands depfiles"
+
+
+am_make=${MAKE-make}
+cat > confinc << 'END'
+am__doit:
+ @echo this is the am__doit target
+.PHONY: am__doit
+END
+# If we don't find an include directive, just comment out the code.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5
+$as_echo_n "checking for style of include used by $am_make... " >&6; }
+am__include="#"
+am__quote=
+_am_result=none
+# First try GNU make style include.
+echo "include confinc" > confmf
+# Ignore all kinds of additional output from `make'.
+case `$am_make -s -f confmf 2> /dev/null` in #(
+*the\ am__doit\ target*)
+ am__include=include
+ am__quote=
+ _am_result=GNU
+ ;;
+esac
+# Now try BSD make style include.
+if test "$am__include" = "#"; then
+ echo '.include "confinc"' > confmf
+ case `$am_make -s -f confmf 2> /dev/null` in #(
+ *the\ am__doit\ target*)
+ am__include=.include
+ am__quote="\""
+ _am_result=BSD
+ ;;
+ esac
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5
+$as_echo "$_am_result" >&6; }
+rm -f confinc confmf
+
+@%:@ Check whether --enable-dependency-tracking was given.
+if test "${enable_dependency_tracking+set}" = set; then :
+ enableval=$enable_dependency_tracking;
+fi
+
+if test "x$enable_dependency_tracking" != xno; then
+ am_depcomp="$ac_aux_dir/depcomp"
+ AMDEPBACKSLASH='\'
+fi
+ if test "x$enable_dependency_tracking" != xno; then
+ AMDEP_TRUE=
+ AMDEP_FALSE='#'
+else
+ AMDEP_TRUE='#'
+ AMDEP_FALSE=
+fi
+
+
+
+depcc="$CC" am_compiler_list=
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5
+$as_echo_n "checking dependency style of $depcc... " >&6; }
+if ${am_cv_CC_dependencies_compiler_type+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+ # We make a subdir and do the tests there. Otherwise we can end up
+ # making bogus files that we don't know about and never remove. For
+ # instance it was reported that on HP-UX the gcc test will end up
+ # making a dummy file named `D' -- because `-MD' means `put the output
+ # in D'.
+ mkdir conftest.dir
+ # Copy depcomp to subdir because otherwise we won't find it if we're
+ # using a relative directory.
+ cp "$am_depcomp" conftest.dir
+ cd conftest.dir
+ # We will build objects and dependencies in a subdirectory because
+ # it helps to detect inapplicable dependency modes. For instance
+ # both Tru64's cc and ICC support -MD to output dependencies as a
+ # side effect of compilation, but ICC will put the dependencies in
+ # the current directory while Tru64 will put them in the object
+ # directory.
+ mkdir sub
+
+ am_cv_CC_dependencies_compiler_type=none
+ if test "$am_compiler_list" = ""; then
+ am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
+ fi
+ am__universal=false
+ case " $depcc " in #(
+ *\ -arch\ *\ -arch\ *) am__universal=true ;;
+ esac
+
+ for depmode in $am_compiler_list; do
+ # Setup a source with many dependencies, because some compilers
+ # like to wrap large dependency lists on column 80 (with \), and
+ # we should not choose a depcomp mode which is confused by this.
+ #
+ # We need to recreate these files for each test, as the compiler may
+ # overwrite some of them when testing with obscure command lines.
+ # This happens at least with the AIX C compiler.
+ : > sub/conftest.c
+ for i in 1 2 3 4 5 6; do
+ echo '#include "conftst'$i'.h"' >> sub/conftest.c
+ # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
+ # Solaris 8's {/usr,}/bin/sh.
+ touch sub/conftst$i.h
+ done
+ echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+ # We check with `-c' and `-o' for the sake of the "dashmstdout"
+ # mode. It turns out that the SunPro C++ compiler does not properly
+ # handle `-M -o', and we need to detect this. Also, some Intel
+ # versions had trouble with output in subdirs
+ am__obj=sub/conftest.${OBJEXT-o}
+ am__minus_obj="-o $am__obj"
+ case $depmode in
+ gcc)
+ # This depmode causes a compiler race in universal mode.
+ test "$am__universal" = false || continue
+ ;;
+ nosideeffect)
+ # after this tag, mechanisms are not by side-effect, so they'll
+ # only be used when explicitly requested
+ if test "x$enable_dependency_tracking" = xyes; then
+ continue
+ else
+ break
+ fi
+ ;;
+ msvisualcpp | msvcmsys)
+ # This compiler won't grok `-c -o', but also, the minuso test has
+ # not run yet. These depmodes are late enough in the game, and
+ # so weak that their functioning should not be impacted.
+ am__obj=conftest.${OBJEXT-o}
+ am__minus_obj=
+ ;;
+ none) break ;;
+ esac
+ if depmode=$depmode \
+ source=sub/conftest.c object=$am__obj \
+ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
+ >/dev/null 2>conftest.err &&
+ grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
+ ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+ # icc doesn't choke on unknown options, it will just issue warnings
+ # or remarks (even with -Werror). So we grep stderr for any message
+ # that says an option was ignored or not supported.
+ # When given -MP, icc 7.0 and 7.1 complain thusly:
+ # icc: Command line warning: ignoring option '-M'; no argument required
+ # The diagnosis changed in icc 8.0:
+ # icc: Command line remark: option '-MP' not supported
+ if (grep 'ignoring option' conftest.err ||
+ grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+ am_cv_CC_dependencies_compiler_type=$depmode
+ break
+ fi
+ fi
+ done
+
+ cd ..
+ rm -rf conftest.dir
+else
+ am_cv_CC_dependencies_compiler_type=none
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5
+$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; }
+CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type
+
+ if
+ test "x$enable_dependency_tracking" != xno \
+ && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then
+ am__fastdepCC_TRUE=
+ am__fastdepCC_FALSE='#'
+else
+ am__fastdepCC_TRUE='#'
+ am__fastdepCC_FALSE=
+fi
+
+
+ case $ac_cv_prog_cc_stdc in @%:@(
+ no) :
+ ac_cv_prog_cc_c99=no; ac_cv_prog_cc_c89=no ;; @%:@(
+ *) :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C99" >&5
+$as_echo_n "checking for $CC option to accept ISO C99... " >&6; }
+if ${ac_cv_prog_cc_c99+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_prog_cc_c99=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <wchar.h>
+#include <stdio.h>
+
+// Check varargs macros. These examples are taken from C99 6.10.3.5.
+#define debug(...) fprintf (stderr, __VA_ARGS__)
+#define showlist(...) puts (#__VA_ARGS__)
+#define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__))
+static void
+test_varargs_macros (void)
+{
+ int x = 1234;
+ int y = 5678;
+ debug ("Flag");
+ debug ("X = %d\n", x);
+ showlist (The first, second, and third items.);
+ report (x>y, "x is %d but y is %d", x, y);
+}
+
+// Check long long types.
+#define BIG64 18446744073709551615ull
+#define BIG32 4294967295ul
+#define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0)
+#if !BIG_OK
+ your preprocessor is broken;
+#endif
+#if BIG_OK
+#else
+ your preprocessor is broken;
+#endif
+static long long int bignum = -9223372036854775807LL;
+static unsigned long long int ubignum = BIG64;
+
+struct incomplete_array
+{
+ int datasize;
+ double data[];
+};
+
+struct named_init {
+ int number;
+ const wchar_t *name;
+ double average;
+};
+
+typedef const char *ccp;
+
+static inline int
+test_restrict (ccp restrict text)
+{
+ // See if C++-style comments work.
+ // Iterate through items via the restricted pointer.
+ // Also check for declarations in for loops.
+ for (unsigned int i = 0; *(text+i) != '\0'; ++i)
+ continue;
+ return 0;
+}
+
+// Check varargs and va_copy.
+static void
+test_varargs (const char *format, ...)
+{
+ va_list args;
+ va_start (args, format);
+ va_list args_copy;
+ va_copy (args_copy, args);
+
+ const char *str;
+ int number;
+ float fnumber;
+
+ while (*format)
+ {
+ switch (*format++)
+ {
+ case 's': // string
+ str = va_arg (args_copy, const char *);
+ break;
+ case 'd': // int
+ number = va_arg (args_copy, int);
+ break;
+ case 'f': // float
+ fnumber = va_arg (args_copy, double);
+ break;
+ default:
+ break;
+ }
+ }
+ va_end (args_copy);
+ va_end (args);
+}
+
+int
+main ()
+{
+
+ // Check bool.
+ _Bool success = false;
+
+ // Check restrict.
+ if (test_restrict ("String literal") == 0)
+ success = true;
+ char *restrict newvar = "Another string";
+
+ // Check varargs.
+ test_varargs ("s, d' f .", "string", 65, 34.234);
+ test_varargs_macros ();
+
+ // Check flexible array members.
+ struct incomplete_array *ia =
+ malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10));
+ ia->datasize = 10;
+ for (int i = 0; i < ia->datasize; ++i)
+ ia->data[i] = i * 1.234;
+
+ // Check named initializers.
+ struct named_init ni = {
+ .number = 34,
+ .name = L"Test wide string",
+ .average = 543.34343,
+ };
+
+ ni.number = 58;
+
+ int dynamic_array[ni.number];
+ dynamic_array[ni.number - 1] = 543;
+
+ // work around unused variable warnings
+ return (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == 'x'
+ || dynamic_array[ni.number - 1] != 543);
+
+ ;
+ return 0;
+}
+_ACEOF
+for ac_arg in '' -std=gnu99 -std=c99 -c99 -AC99 -xc99=all -qlanglvl=extc99
+do
+ CC="$ac_save_CC $ac_arg"
+ if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_c99=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+ test "x$ac_cv_prog_cc_c99" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c99" in
+ x)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+ xno)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+ *)
+ CC="$CC $ac_cv_prog_cc_c99"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5
+$as_echo "$ac_cv_prog_cc_c99" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c99" != xno; then :
+ ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if ${ac_cv_prog_cc_c89+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
+ function prototypes and stuff, but not '\xHH' hex character constants.
+ These don't provoke an error unfortunately, instead are silently treated
+ as 'x'. The following induces an error, until -std is added to get
+ proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
+ array size at least. It's necessary to write '\x00'==0 to get something
+ that's true only with -std. */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+ inside strings and character constants. */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
+ ;
+ return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+ test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+ x)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+ xno)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+ *)
+ CC="$CC $ac_cv_prog_cc_c89"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c89" != xno; then :
+ ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89
+else
+ ac_cv_prog_cc_stdc=no
+fi
+
+fi
+ ;;
+esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO Standard C" >&5
+$as_echo_n "checking for $CC option to accept ISO Standard C... " >&6; }
+ if ${ac_cv_prog_cc_stdc+:} false; then :
+ $as_echo_n "(cached) " >&6
+fi
+
+ case $ac_cv_prog_cc_stdc in @%:@(
+ no) :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;; @%:@(
+ '') :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;; @%:@(
+ *) :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_stdc" >&5
+$as_echo "$ac_cv_prog_cc_stdc" >&6; } ;;
+esac
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5
+$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; }
+set x ${MAKE-make}
+ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
+if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.make <<\_ACEOF
+SHELL = /bin/sh
+all:
+ @echo '@@@%%%=$(MAKE)=@@@%%%'
+_ACEOF
+# GNU make sometimes prints "make[1]: Entering ...", which would confuse us.
+case `${MAKE-make} -f conftest.make 2>/dev/null` in
+ *@@@%%%=?*=@@@%%%*)
+ eval ac_cv_prog_make_${ac_make}_set=yes;;
+ *)
+ eval ac_cv_prog_make_${ac_make}_set=no;;
+esac
+rm -f conftest.make
+fi
+if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ SET_MAKE=
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5
+$as_echo_n "checking whether ln -s works... " >&6; }
+LN_S=$as_ln_s
+if test "$LN_S" = "ln -s"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5
+$as_echo "no, using $LN_S" >&6; }
+fi
+
+for ac_prog in gawk mawk nawk awk
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_AWK+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$AWK"; then
+ ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_AWK="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+AWK=$ac_cv_prog_AWK
+if test -n "$AWK"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5
+$as_echo "$AWK" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$AWK" && break
+done
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_RANLIB+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$RANLIB"; then
+ ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5
+$as_echo "$RANLIB" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+ ac_ct_RANLIB=$RANLIB
+ # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_RANLIB+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_RANLIB"; then
+ ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_RANLIB="ranlib"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5
+$as_echo "$ac_ct_RANLIB" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_RANLIB" = x; then
+ RANLIB=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ RANLIB=$ac_ct_RANLIB
+ fi
+else
+ RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+case `pwd` in
+ *\ * | *\ *)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5
+$as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;;
+esac
+
+
+
+macro_version='2.2.6b'
+macro_revision='1.3018'
+
+
+
+
+
+
+
+
+
+
+
+
+
+ltmain="$ac_aux_dir/ltmain.sh"
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5
+$as_echo_n "checking for a sed that does not truncate output... " >&6; }
+if ${ac_cv_path_SED+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
+ for ac_i in 1 2 3 4 5 6 7; do
+ ac_script="$ac_script$as_nl$ac_script"
+ done
+ echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed
+ { ac_script=; unset ac_script;}
+ if test -z "$SED"; then
+ ac_path_SED_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in sed gsed; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_SED="$as_dir/$ac_prog$ac_exec_ext"
+ { test -f "$ac_path_SED" && $as_test_x "$ac_path_SED"; } || continue
+# Check for GNU ac_path_SED and select it if it is found.
+ # Check for GNU $ac_path_SED
+case `"$ac_path_SED" --version 2>&1` in
+*GNU*)
+ ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo '' >> "conftest.nl"
+ "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_SED_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_SED="$ac_path_SED"
+ ac_path_SED_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_SED_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_SED"; then
+ as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5
+ fi
+else
+ ac_cv_path_SED=$SED
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5
+$as_echo "$ac_cv_path_SED" >&6; }
+ SED="$ac_cv_path_SED"
+ rm -f conftest.sed
+
+test -z "$SED" && SED=sed
+Xsed="$SED -e 1s/^X//"
+
+
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
+$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
+if ${ac_cv_path_GREP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$GREP"; then
+ ac_path_GREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in grep ggrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
+ { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue
+# Check for GNU ac_path_GREP and select it if it is found.
+ # Check for GNU $ac_path_GREP
+case `"$ac_path_GREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo 'GREP' >> "conftest.nl"
+ "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_GREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_GREP="$ac_path_GREP"
+ ac_path_GREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_GREP_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_GREP"; then
+ as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ fi
+else
+ ac_cv_path_GREP=$GREP
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5
+$as_echo "$ac_cv_path_GREP" >&6; }
+ GREP="$ac_cv_path_GREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5
+$as_echo_n "checking for egrep... " >&6; }
+if ${ac_cv_path_EGREP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
+ then ac_cv_path_EGREP="$GREP -E"
+ else
+ if test -z "$EGREP"; then
+ ac_path_EGREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in egrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
+ { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue
+# Check for GNU ac_path_EGREP and select it if it is found.
+ # Check for GNU $ac_path_EGREP
+case `"$ac_path_EGREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo 'EGREP' >> "conftest.nl"
+ "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_EGREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_EGREP="$ac_path_EGREP"
+ ac_path_EGREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_EGREP_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_EGREP"; then
+ as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ fi
+else
+ ac_cv_path_EGREP=$EGREP
+fi
+
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5
+$as_echo "$ac_cv_path_EGREP" >&6; }
+ EGREP="$ac_cv_path_EGREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5
+$as_echo_n "checking for fgrep... " >&6; }
+if ${ac_cv_path_FGREP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1
+ then ac_cv_path_FGREP="$GREP -F"
+ else
+ if test -z "$FGREP"; then
+ ac_path_FGREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in fgrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext"
+ { test -f "$ac_path_FGREP" && $as_test_x "$ac_path_FGREP"; } || continue
+# Check for GNU ac_path_FGREP and select it if it is found.
+ # Check for GNU $ac_path_FGREP
+case `"$ac_path_FGREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo 'FGREP' >> "conftest.nl"
+ "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_FGREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_FGREP="$ac_path_FGREP"
+ ac_path_FGREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_FGREP_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_FGREP"; then
+ as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ fi
+else
+ ac_cv_path_FGREP=$FGREP
+fi
+
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5
+$as_echo "$ac_cv_path_FGREP" >&6; }
+ FGREP="$ac_cv_path_FGREP"
+
+
+test -z "$GREP" && GREP=grep
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+@%:@ Check whether --with-gnu-ld was given.
+if test "${with_gnu_ld+set}" = set; then :
+ withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes
+else
+ with_gnu_ld=no
+fi
+
+ac_prog=ld
+if test "$GCC" = yes; then
+ # Check if gcc -print-prog-name=ld gives a path.
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5
+$as_echo_n "checking for ld used by $CC... " >&6; }
+ case $host in
+ *-*-mingw*)
+ # gcc leaves a trailing carriage return which upsets mingw
+ ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+ *)
+ ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+ esac
+ case $ac_prog in
+ # Accept absolute paths.
+ [\\/]* | ?:[\\/]*)
+ re_direlt='/[^/][^/]*/\.\./'
+ # Canonicalize the pathname of ld
+ ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'`
+ while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
+ ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
+ done
+ test -z "$LD" && LD="$ac_prog"
+ ;;
+ "")
+ # If it fails, then pretend we aren't using GCC.
+ ac_prog=ld
+ ;;
+ *)
+ # If it is relative, then search for the first ld in PATH.
+ with_gnu_ld=unknown
+ ;;
+ esac
+elif test "$with_gnu_ld" = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5
+$as_echo_n "checking for GNU ld... " >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5
+$as_echo_n "checking for non-GNU ld... " >&6; }
+fi
+if ${lt_cv_path_LD+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$LD"; then
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+ lt_cv_path_LD="$ac_dir/$ac_prog"
+ # Check to see if the program is GNU ld. I'd rather use --version,
+ # but apparently some variants of GNU ld only accept -v.
+ # Break only if it was the GNU/non-GNU ld that we prefer.
+ case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
+ *GNU* | *'with BFD'*)
+ test "$with_gnu_ld" != no && break
+ ;;
+ *)
+ test "$with_gnu_ld" != yes && break
+ ;;
+ esac
+ fi
+ done
+ IFS="$lt_save_ifs"
+else
+ lt_cv_path_LD="$LD" # Let the user override the test with a path.
+fi
+fi
+
+LD="$lt_cv_path_LD"
+if test -n "$LD"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LD" >&5
+$as_echo "$LD" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5
+$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; }
+if ${lt_cv_prog_gnu_ld+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ # I'd rather use --version here, but apparently some GNU lds only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+ lt_cv_prog_gnu_ld=yes
+ ;;
+*)
+ lt_cv_prog_gnu_ld=no
+ ;;
+esac
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_gnu_ld" >&5
+$as_echo "$lt_cv_prog_gnu_ld" >&6; }
+with_gnu_ld=$lt_cv_prog_gnu_ld
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5
+$as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; }
+if ${lt_cv_path_NM+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$NM"; then
+ # Let the user override the test.
+ lt_cv_path_NM="$NM"
+else
+ lt_nm_to_check="${ac_tool_prefix}nm"
+ if test -n "$ac_tool_prefix" && test "$build" = "$host"; then
+ lt_nm_to_check="$lt_nm_to_check nm"
+ fi
+ for lt_tmp_nm in $lt_nm_to_check; do
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ tmp_nm="$ac_dir/$lt_tmp_nm"
+ if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then
+ # Check to see if the nm accepts a BSD-compat flag.
+ # Adding the `sed 1q' prevents false positives on HP-UX, which says:
+ # nm: unknown option "B" ignored
+ # Tru64's nm complains that /dev/null is an invalid object file
+ case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in
+ */dev/null* | *'Invalid file or object type'*)
+ lt_cv_path_NM="$tmp_nm -B"
+ break
+ ;;
+ *)
+ case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in
+ */dev/null*)
+ lt_cv_path_NM="$tmp_nm -p"
+ break
+ ;;
+ *)
+ lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
+ continue # so that we can try to find one that supports BSD flags
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ done
+ IFS="$lt_save_ifs"
+ done
+ : ${lt_cv_path_NM=no}
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5
+$as_echo "$lt_cv_path_NM" >&6; }
+if test "$lt_cv_path_NM" != "no"; then
+ NM="$lt_cv_path_NM"
+else
+ # Didn't find any BSD compatible name lister, look for dumpbin.
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in "dumpbin -symbols" "link -dump -symbols"
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_DUMPBIN+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$DUMPBIN"; then
+ ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+DUMPBIN=$ac_cv_prog_DUMPBIN
+if test -n "$DUMPBIN"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5
+$as_echo "$DUMPBIN" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$DUMPBIN" && break
+ done
+fi
+if test -z "$DUMPBIN"; then
+ ac_ct_DUMPBIN=$DUMPBIN
+ for ac_prog in "dumpbin -symbols" "link -dump -symbols"
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_DUMPBIN+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_DUMPBIN"; then
+ ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_DUMPBIN="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN
+if test -n "$ac_ct_DUMPBIN"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5
+$as_echo "$ac_ct_DUMPBIN" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_DUMPBIN" && break
+done
+
+ if test "x$ac_ct_DUMPBIN" = x; then
+ DUMPBIN=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ DUMPBIN=$ac_ct_DUMPBIN
+ fi
+fi
+
+
+ if test "$DUMPBIN" != ":"; then
+ NM="$DUMPBIN"
+ fi
+fi
+test -z "$NM" && NM=nm
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5
+$as_echo_n "checking the name lister ($NM) interface... " >&6; }
+if ${lt_cv_nm_interface+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_nm_interface="BSD nm"
+ echo "int some_variable = 0;" > conftest.$ac_ext
+ (eval echo "\"\$as_me:__oline__: $ac_compile\"" >&5)
+ (eval "$ac_compile" 2>conftest.err)
+ cat conftest.err >&5
+ (eval echo "\"\$as_me:__oline__: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
+ (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
+ cat conftest.err >&5
+ (eval echo "\"\$as_me:__oline__: output\"" >&5)
+ cat conftest.out >&5
+ if $GREP 'External.*some_variable' conftest.out > /dev/null; then
+ lt_cv_nm_interface="MS dumpbin"
+ fi
+ rm -f conftest*
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5
+$as_echo "$lt_cv_nm_interface" >&6; }
+
+# find the maximum length of command line arguments
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5
+$as_echo_n "checking the maximum length of command line arguments... " >&6; }
+if ${lt_cv_sys_max_cmd_len+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ i=0
+ teststring="ABCD"
+
+ case $build_os in
+ msdosdjgpp*)
+ # On DJGPP, this test can blow up pretty badly due to problems in libc
+ # (any single argument exceeding 2000 bytes causes a buffer overrun
+ # during glob expansion). Even if it were fixed, the result of this
+ # check would be larger than it should be.
+ lt_cv_sys_max_cmd_len=12288; # 12K is about right
+ ;;
+
+ gnu*)
+ # Under GNU Hurd, this test is not required because there is
+ # no limit to the length of command line arguments.
+ # Libtool will interpret -1 as no limit whatsoever
+ lt_cv_sys_max_cmd_len=-1;
+ ;;
+
+ cygwin* | mingw* | cegcc*)
+ # On Win9x/ME, this test blows up -- it succeeds, but takes
+ # about 5 minutes as the teststring grows exponentially.
+ # Worse, since 9x/ME are not pre-emptively multitasking,
+ # you end up with a "frozen" computer, even though with patience
+ # the test eventually succeeds (with a max line length of 256k).
+ # Instead, let's just punt: use the minimum linelength reported by
+ # all of the supported platforms: 8192 (on NT/2K/XP).
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ amigaos*)
+ # On AmigaOS with pdksh, this test takes hours, literally.
+ # So we just punt and use a minimum line length of 8192.
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ netbsd* | freebsd* | openbsd* | darwin* | dragonfly*)
+ # This has been around since 386BSD, at least. Likely further.
+ if test -x /sbin/sysctl; then
+ lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax`
+ elif test -x /usr/sbin/sysctl; then
+ lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax`
+ else
+ lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs
+ fi
+ # And add a safety zone
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+ ;;
+
+ interix*)
+ # We know the value 262144 and hardcode it with a safety zone (like BSD)
+ lt_cv_sys_max_cmd_len=196608
+ ;;
+
+ osf*)
+ # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure
+ # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not
+ # nice to cause kernel panics so lets avoid the loop below.
+ # First set a reasonable default.
+ lt_cv_sys_max_cmd_len=16384
+ #
+ if test -x /sbin/sysconfig; then
+ case `/sbin/sysconfig -q proc exec_disable_arg_limit` in
+ *1*) lt_cv_sys_max_cmd_len=-1 ;;
+ esac
+ fi
+ ;;
+ sco3.2v5*)
+ lt_cv_sys_max_cmd_len=102400
+ ;;
+ sysv5* | sco5v6* | sysv4.2uw2*)
+ kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null`
+ if test -n "$kargmax"; then
+ lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'`
+ else
+ lt_cv_sys_max_cmd_len=32768
+ fi
+ ;;
+ *)
+ lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null`
+ if test -n "$lt_cv_sys_max_cmd_len"; then
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+ else
+ # Make teststring a little bigger before we do anything with it.
+ # a 1K string should be a reasonable start.
+ for i in 1 2 3 4 5 6 7 8 ; do
+ teststring=$teststring$teststring
+ done
+ SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}}
+ # If test is not a shell built-in, we'll probably end up computing a
+ # maximum length that is only half of the actual maximum length, but
+ # we can't tell.
+ while { test "X"`$SHELL $0 --fallback-echo "X$teststring$teststring" 2>/dev/null` \
+ = "XX$teststring$teststring"; } >/dev/null 2>&1 &&
+ test $i != 17 # 1/2 MB should be enough
+ do
+ i=`expr $i + 1`
+ teststring=$teststring$teststring
+ done
+ # Only check the string length outside the loop.
+ lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1`
+ teststring=
+ # Add a significant safety factor because C++ compilers can tack on
+ # massive amounts of additional arguments before passing them to the
+ # linker. It appears as though 1/2 is a usable value.
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2`
+ fi
+ ;;
+ esac
+
+fi
+
+if test -n $lt_cv_sys_max_cmd_len ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5
+$as_echo "$lt_cv_sys_max_cmd_len" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5
+$as_echo "none" >&6; }
+fi
+max_cmd_len=$lt_cv_sys_max_cmd_len
+
+
+
+
+
+
+: ${CP="cp -f"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands some XSI constructs" >&5
+$as_echo_n "checking whether the shell understands some XSI constructs... " >&6; }
+# Try some XSI features
+xsi_shell=no
+( _lt_dummy="a/b/c"
+ test "${_lt_dummy##*/},${_lt_dummy%/*},"${_lt_dummy%"$_lt_dummy"}, \
+ = c,a/b,, \
+ && eval 'test $(( 1 + 1 )) -eq 2 \
+ && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \
+ && xsi_shell=yes
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $xsi_shell" >&5
+$as_echo "$xsi_shell" >&6; }
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands \"+=\"" >&5
+$as_echo_n "checking whether the shell understands \"+=\"... " >&6; }
+lt_shell_append=no
+( foo=bar; set foo baz; eval "$1+=\$2" && test "$foo" = barbaz ) \
+ >/dev/null 2>&1 \
+ && lt_shell_append=yes
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_shell_append" >&5
+$as_echo "$lt_shell_append" >&6; }
+
+
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ lt_unset=unset
+else
+ lt_unset=false
+fi
+
+
+
+
+
+# test EBCDIC or ASCII
+case `echo X|tr X '\101'` in
+ A) # ASCII based system
+ # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr
+ lt_SP2NL='tr \040 \012'
+ lt_NL2SP='tr \015\012 \040\040'
+ ;;
+ *) # EBCDIC based system
+ lt_SP2NL='tr \100 \n'
+ lt_NL2SP='tr \r\n \100\100'
+ ;;
+esac
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5
+$as_echo_n "checking for $LD option to reload object files... " >&6; }
+if ${lt_cv_ld_reload_flag+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_ld_reload_flag='-r'
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5
+$as_echo "$lt_cv_ld_reload_flag" >&6; }
+reload_flag=$lt_cv_ld_reload_flag
+case $reload_flag in
+"" | " "*) ;;
+*) reload_flag=" $reload_flag" ;;
+esac
+reload_cmds='$LD$reload_flag -o $output$reload_objs'
+case $host_os in
+ darwin*)
+ if test "$GCC" = yes; then
+ reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs'
+ else
+ reload_cmds='$LD$reload_flag -o $output$reload_objs'
+ fi
+ ;;
+esac
+
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args.
+set dummy ${ac_tool_prefix}objdump; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_OBJDUMP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$OBJDUMP"; then
+ ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+OBJDUMP=$ac_cv_prog_OBJDUMP
+if test -n "$OBJDUMP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5
+$as_echo "$OBJDUMP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OBJDUMP"; then
+ ac_ct_OBJDUMP=$OBJDUMP
+ # Extract the first word of "objdump", so it can be a program name with args.
+set dummy objdump; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_OBJDUMP"; then
+ ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_OBJDUMP="objdump"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP
+if test -n "$ac_ct_OBJDUMP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5
+$as_echo "$ac_ct_OBJDUMP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_OBJDUMP" = x; then
+ OBJDUMP="false"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ OBJDUMP=$ac_ct_OBJDUMP
+ fi
+else
+ OBJDUMP="$ac_cv_prog_OBJDUMP"
+fi
+
+test -z "$OBJDUMP" && OBJDUMP=objdump
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5
+$as_echo_n "checking how to recognize dependent libraries... " >&6; }
+if ${lt_cv_deplibs_check_method+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_file_magic_cmd='$MAGIC_CMD'
+lt_cv_file_magic_test_file=
+lt_cv_deplibs_check_method='unknown'
+# Need to set the preceding variable on all platforms that support
+# interlibrary dependencies.
+# 'none' -- dependencies not supported.
+# `unknown' -- same as none, but documents that we really don't know.
+# 'pass_all' -- all dependencies passed with no checks.
+# 'test_compile' -- check by making test program.
+# 'file_magic [[regex]]' -- check by looking for files in library path
+# which responds to the $file_magic_cmd with a given extended regex.
+# If you have `file' or equivalent on your system and you're not sure
+# whether `pass_all' will *always* work, you probably want this one.
+
+case $host_os in
+aix[4-9]*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+beos*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+bsdi[45]*)
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)'
+ lt_cv_file_magic_cmd='/usr/bin/file -L'
+ lt_cv_file_magic_test_file=/shlib/libc.so
+ ;;
+
+cygwin*)
+ # func_win32_libid is a shell function defined in ltmain.sh
+ lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+ lt_cv_file_magic_cmd='func_win32_libid'
+ ;;
+
+mingw* | pw32*)
+ # Base MSYS/MinGW do not provide the 'file' command needed by
+ # func_win32_libid shell function, so use a weaker test based on 'objdump',
+ # unless we find 'file', for example because we are cross-compiling.
+ if ( file / ) >/dev/null 2>&1; then
+ lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+ lt_cv_file_magic_cmd='func_win32_libid'
+ else
+ lt_cv_deplibs_check_method='file_magic file format pei*-i386(.*architecture: i386)?'
+ lt_cv_file_magic_cmd='$OBJDUMP -f'
+ fi
+ ;;
+
+cegcc)
+ # use the weaker test based on 'objdump'. See mingw*.
+ lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?'
+ lt_cv_file_magic_cmd='$OBJDUMP -f'
+ ;;
+
+darwin* | rhapsody*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+freebsd* | dragonfly*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+ case $host_cpu in
+ i*86 )
+ # Not sure whether the presence of OpenBSD here was a mistake.
+ # Let's accept both of them until this is cleared up.
+ lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library'
+ lt_cv_file_magic_cmd=/usr/bin/file
+ lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
+ ;;
+ esac
+ else
+ lt_cv_deplibs_check_method=pass_all
+ fi
+ ;;
+
+gnu*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+hpux10.20* | hpux11*)
+ lt_cv_file_magic_cmd=/usr/bin/file
+ case $host_cpu in
+ ia64*)
+ lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64'
+ lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so
+ ;;
+ hppa*64*)
+ lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - PA-RISC [0-9].[0-9]'
+ lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl
+ ;;
+ *)
+ lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9].[0-9]) shared library'
+ lt_cv_file_magic_test_file=/usr/lib/libc.sl
+ ;;
+ esac
+ ;;
+
+interix[3-9]*)
+ # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$'
+ ;;
+
+irix5* | irix6* | nonstopux*)
+ case $LD in
+ *-32|*"-32 ") libmagic=32-bit;;
+ *-n32|*"-n32 ") libmagic=N32;;
+ *-64|*"-64 ") libmagic=64-bit;;
+ *) libmagic=never-match;;
+ esac
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+# This must be Linux ELF.
+linux* | k*bsd*-gnu)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
+ else
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$'
+ fi
+ ;;
+
+newos6*)
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)'
+ lt_cv_file_magic_cmd=/usr/bin/file
+ lt_cv_file_magic_test_file=/usr/lib/libnls.so
+ ;;
+
+*nto* | *qnx*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+openbsd*)
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$'
+ else
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
+ fi
+ ;;
+
+osf3* | osf4* | osf5*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+rdos*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+solaris*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+sysv4 | sysv4.3*)
+ case $host_vendor in
+ motorola)
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]'
+ lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*`
+ ;;
+ ncr)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ sequent)
+ lt_cv_file_magic_cmd='/bin/file'
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )'
+ ;;
+ sni)
+ lt_cv_file_magic_cmd='/bin/file'
+ lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib"
+ lt_cv_file_magic_test_file=/lib/libc.so
+ ;;
+ siemens)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ pc)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ esac
+ ;;
+
+tpf*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+esac
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5
+$as_echo "$lt_cv_deplibs_check_method" >&6; }
+file_magic_cmd=$lt_cv_file_magic_cmd
+deplibs_check_method=$lt_cv_deplibs_check_method
+test -z "$deplibs_check_method" && deplibs_check_method=unknown
+
+
+
+
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ar; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_AR+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$AR"; then
+ ac_cv_prog_AR="$AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_AR="${ac_tool_prefix}ar"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+AR=$ac_cv_prog_AR
+if test -n "$AR"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5
+$as_echo "$AR" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_AR"; then
+ ac_ct_AR=$AR
+ # Extract the first word of "ar", so it can be a program name with args.
+set dummy ar; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_AR+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_AR"; then
+ ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_AR="ar"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_AR=$ac_cv_prog_ac_ct_AR
+if test -n "$ac_ct_AR"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5
+$as_echo "$ac_ct_AR" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_AR" = x; then
+ AR="false"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ AR=$ac_ct_AR
+ fi
+else
+ AR="$ac_cv_prog_AR"
+fi
+
+test -z "$AR" && AR=ar
+test -z "$AR_FLAGS" && AR_FLAGS=cru
+
+
+
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_STRIP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$STRIP"; then
+ ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5
+$as_echo "$STRIP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_STRIP"; then
+ ac_ct_STRIP=$STRIP
+ # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_STRIP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_STRIP"; then
+ ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_STRIP="strip"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5
+$as_echo "$ac_ct_STRIP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_STRIP" = x; then
+ STRIP=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ STRIP=$ac_ct_STRIP
+ fi
+else
+ STRIP="$ac_cv_prog_STRIP"
+fi
+
+test -z "$STRIP" && STRIP=:
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_RANLIB+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$RANLIB"; then
+ ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5
+$as_echo "$RANLIB" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+ ac_ct_RANLIB=$RANLIB
+ # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_RANLIB+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_RANLIB"; then
+ ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_RANLIB="ranlib"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5
+$as_echo "$ac_ct_RANLIB" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_RANLIB" = x; then
+ RANLIB=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ RANLIB=$ac_ct_RANLIB
+ fi
+else
+ RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+test -z "$RANLIB" && RANLIB=:
+
+
+
+
+
+
+# Determine commands to create old-style static archives.
+old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs'
+old_postinstall_cmds='chmod 644 $oldlib'
+old_postuninstall_cmds=
+
+if test -n "$RANLIB"; then
+ case $host_os in
+ openbsd*)
+ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$oldlib"
+ ;;
+ *)
+ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$oldlib"
+ ;;
+ esac
+ old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib"
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+
+# Check for command to grab the raw symbol name followed by C symbol from nm.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5
+$as_echo_n "checking command to parse $NM output from $compiler object... " >&6; }
+if ${lt_cv_sys_global_symbol_pipe+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+# These are sane defaults that work on at least a few old systems.
+# [They come from Ultrix. What could be older than Ultrix?!! ;)]
+
+# Character class describing NM global symbol codes.
+symcode='[BCDEGRST]'
+
+# Regexp to match symbols that can be accessed directly from C.
+sympat='\([_A-Za-z][_A-Za-z0-9]*\)'
+
+# Define system-specific variables.
+case $host_os in
+aix*)
+ symcode='[BCDT]'
+ ;;
+cygwin* | mingw* | pw32* | cegcc*)
+ symcode='[ABCDGISTW]'
+ ;;
+hpux*)
+ if test "$host_cpu" = ia64; then
+ symcode='[ABCDEGRST]'
+ fi
+ ;;
+irix* | nonstopux*)
+ symcode='[BCDEGRST]'
+ ;;
+osf*)
+ symcode='[BCDEGQRST]'
+ ;;
+solaris*)
+ symcode='[BDRT]'
+ ;;
+sco3.2v5*)
+ symcode='[DT]'
+ ;;
+sysv4.2uw2*)
+ symcode='[DT]'
+ ;;
+sysv5* | sco5v6* | unixware* | OpenUNIX*)
+ symcode='[ABDT]'
+ ;;
+sysv4)
+ symcode='[DFNSTU]'
+ ;;
+esac
+
+# If we're using GNU nm, then use its standard symbol codes.
+case `$NM -V 2>&1` in
+*GNU* | *'with BFD'*)
+ symcode='[ABCDGIRSTW]' ;;
+esac
+
+# Transform an extracted symbol line into a proper C declaration.
+# Some systems (esp. on ia64) link data and code symbols differently,
+# so use this general approach.
+lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'"
+
+# Transform an extracted symbol line into symbol name and symbol address
+lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"\2\", (void *) \&\2},/p'"
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([^ ]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \(lib[^ ]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"lib\2\", (void *) \&\2},/p'"
+
+# Handle CRLF in mingw tool chain
+opt_cr=
+case $build_os in
+mingw*)
+ opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp
+ ;;
+esac
+
+# Try without a prefix underscore, then with it.
+for ac_symprfx in "" "_"; do
+
+ # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol.
+ symxfrm="\\1 $ac_symprfx\\2 \\2"
+
+ # Write the raw and C identifiers.
+ if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+ # Fake it for dumpbin and say T for any non-static function
+ # and D for any global variable.
+ # Also find C++ and __fastcall symbols from MSVC++,
+ # which start with @ or ?.
+ lt_cv_sys_global_symbol_pipe="$AWK '"\
+" {last_section=section; section=\$ 3};"\
+" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\
+" \$ 0!~/External *\|/{next};"\
+" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\
+" {if(hide[section]) next};"\
+" {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\
+" {split(\$ 0, a, /\||\r/); split(a[2], s)};"\
+" s[1]~/^[@?]/{print s[1], s[1]; next};"\
+" s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\
+" ' prfx=^$ac_symprfx"
+ else
+ lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'"
+ fi
+
+ # Check to see that the pipe works correctly.
+ pipe_works=no
+
+ rm -f conftest*
+ cat > conftest.$ac_ext <<_LT_EOF
+#ifdef __cplusplus
+extern "C" {
+#endif
+char nm_test_var;
+void nm_test_func(void);
+void nm_test_func(void){}
+#ifdef __cplusplus
+}
+#endif
+int main(){nm_test_var='a';nm_test_func();return(0);}
+_LT_EOF
+
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ # Now try to grab the symbols.
+ nlist=conftest.nm
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist\""; } >&5
+ (eval $NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && test -s "$nlist"; then
+ # Try sorting and uniquifying the output.
+ if sort "$nlist" | uniq > "$nlist"T; then
+ mv -f "$nlist"T "$nlist"
+ else
+ rm -f "$nlist"T
+ fi
+
+ # Make sure that we snagged all the symbols we need.
+ if $GREP ' nm_test_var$' "$nlist" >/dev/null; then
+ if $GREP ' nm_test_func$' "$nlist" >/dev/null; then
+ cat <<_LT_EOF > conftest.$ac_ext
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+_LT_EOF
+ # Now generate the symbol file.
+ eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext'
+
+ cat <<_LT_EOF >> conftest.$ac_ext
+
+/* The mapping between symbol names and symbols. */
+const struct {
+ const char *name;
+ void *address;
+}
+lt__PROGRAM__LTX_preloaded_symbols[] =
+{
+ { "@PROGRAM@", (void *) 0 },
+_LT_EOF
+ $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext
+ cat <<\_LT_EOF >> conftest.$ac_ext
+ {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+ return lt__PROGRAM__LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+_LT_EOF
+ # Now try linking the two files.
+ mv conftest.$ac_objext conftstm.$ac_objext
+ lt_save_LIBS="$LIBS"
+ lt_save_CFLAGS="$CFLAGS"
+ LIBS="conftstm.$ac_objext"
+ CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag"
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && test -s conftest${ac_exeext}; then
+ pipe_works=yes
+ fi
+ LIBS="$lt_save_LIBS"
+ CFLAGS="$lt_save_CFLAGS"
+ else
+ echo "cannot find nm_test_func in $nlist" >&5
+ fi
+ else
+ echo "cannot find nm_test_var in $nlist" >&5
+ fi
+ else
+ echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5
+ fi
+ else
+ echo "$progname: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ fi
+ rm -rf conftest* conftst*
+
+ # Do not use the global_symbol_pipe unless it works.
+ if test "$pipe_works" = yes; then
+ break
+ else
+ lt_cv_sys_global_symbol_pipe=
+ fi
+done
+
+fi
+
+if test -z "$lt_cv_sys_global_symbol_pipe"; then
+ lt_cv_sys_global_symbol_to_cdecl=
+fi
+if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5
+$as_echo "failed" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5
+$as_echo "ok" >&6; }
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+@%:@ Check whether --enable-libtool-lock was given.
+if test "${enable_libtool_lock+set}" = set; then :
+ enableval=$enable_libtool_lock;
+fi
+
+test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes
+
+# Some flags need to be propagated to the compiler or linker for good
+# libtool support.
+case $host in
+ia64-*-hpux*)
+ # Find out which ABI we are using.
+ echo 'int i;' > conftest.$ac_ext
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ case `/usr/bin/file conftest.$ac_objext` in
+ *ELF-32*)
+ HPUX_IA64_MODE="32"
+ ;;
+ *ELF-64*)
+ HPUX_IA64_MODE="64"
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+*-*-irix6*)
+ # Find out which ABI we are using.
+ echo '#line __oline__ "configure"' > conftest.$ac_ext
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ if test "$lt_cv_prog_gnu_ld" = yes; then
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ LD="${LD-ld} -melf32bsmip"
+ ;;
+ *N32*)
+ LD="${LD-ld} -melf32bmipn32"
+ ;;
+ *64-bit*)
+ LD="${LD-ld} -melf64bmip"
+ ;;
+ esac
+ else
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ LD="${LD-ld} -32"
+ ;;
+ *N32*)
+ LD="${LD-ld} -n32"
+ ;;
+ *64-bit*)
+ LD="${LD-ld} -64"
+ ;;
+ esac
+ fi
+ fi
+ rm -rf conftest*
+ ;;
+
+x86_64-*kfreebsd*-gnu|x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*| \
+s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
+ # Find out which ABI we are using.
+ echo 'int i;' > conftest.$ac_ext
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ case `/usr/bin/file conftest.o` in
+ *32-bit*)
+ case $host in
+ x86_64-*kfreebsd*-gnu)
+ LD="${LD-ld} -m elf_i386_fbsd"
+ ;;
+ x86_64-*linux*)
+ LD="${LD-ld} -m elf_i386"
+ ;;
+ ppc64-*linux*|powerpc64-*linux*)
+ LD="${LD-ld} -m elf32ppclinux"
+ ;;
+ s390x-*linux*)
+ LD="${LD-ld} -m elf_s390"
+ ;;
+ sparc64-*linux*)
+ LD="${LD-ld} -m elf32_sparc"
+ ;;
+ esac
+ ;;
+ *64-bit*)
+ case $host in
+ x86_64-*kfreebsd*-gnu)
+ LD="${LD-ld} -m elf_x86_64_fbsd"
+ ;;
+ x86_64-*linux*)
+ LD="${LD-ld} -m elf_x86_64"
+ ;;
+ ppc*-*linux*|powerpc*-*linux*)
+ LD="${LD-ld} -m elf64ppc"
+ ;;
+ s390*-*linux*|s390*-*tpf*)
+ LD="${LD-ld} -m elf64_s390"
+ ;;
+ sparc*-*linux*)
+ LD="${LD-ld} -m elf64_sparc"
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+
+*-*-sco3.2v5*)
+ # On SCO OpenServer 5, we need -belf to get full-featured binaries.
+ SAVE_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS -belf"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5
+$as_echo_n "checking whether the C compiler needs -belf... " >&6; }
+if ${lt_cv_cc_needs_belf+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ lt_cv_cc_needs_belf=yes
+else
+ lt_cv_cc_needs_belf=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5
+$as_echo "$lt_cv_cc_needs_belf" >&6; }
+ if test x"$lt_cv_cc_needs_belf" != x"yes"; then
+ # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
+ CFLAGS="$SAVE_CFLAGS"
+ fi
+ ;;
+sparc*-*solaris*)
+ # Find out which ABI we are using.
+ echo 'int i;' > conftest.$ac_ext
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ case `/usr/bin/file conftest.o` in
+ *64-bit*)
+ case $lt_cv_prog_gnu_ld in
+ yes*) LD="${LD-ld} -m elf64_sparc" ;;
+ *)
+ if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then
+ LD="${LD-ld} -64"
+ fi
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+esac
+
+need_locks="$enable_libtool_lock"
+
+
+ case $host_os in
+ rhapsody* | darwin*)
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args.
+set dummy ${ac_tool_prefix}dsymutil; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_DSYMUTIL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$DSYMUTIL"; then
+ ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+DSYMUTIL=$ac_cv_prog_DSYMUTIL
+if test -n "$DSYMUTIL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5
+$as_echo "$DSYMUTIL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_DSYMUTIL"; then
+ ac_ct_DSYMUTIL=$DSYMUTIL
+ # Extract the first word of "dsymutil", so it can be a program name with args.
+set dummy dsymutil; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_DSYMUTIL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_DSYMUTIL"; then
+ ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_DSYMUTIL="dsymutil"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL
+if test -n "$ac_ct_DSYMUTIL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5
+$as_echo "$ac_ct_DSYMUTIL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_DSYMUTIL" = x; then
+ DSYMUTIL=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ DSYMUTIL=$ac_ct_DSYMUTIL
+ fi
+else
+ DSYMUTIL="$ac_cv_prog_DSYMUTIL"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args.
+set dummy ${ac_tool_prefix}nmedit; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_NMEDIT+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$NMEDIT"; then
+ ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+NMEDIT=$ac_cv_prog_NMEDIT
+if test -n "$NMEDIT"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5
+$as_echo "$NMEDIT" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_NMEDIT"; then
+ ac_ct_NMEDIT=$NMEDIT
+ # Extract the first word of "nmedit", so it can be a program name with args.
+set dummy nmedit; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_NMEDIT+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_NMEDIT"; then
+ ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_NMEDIT="nmedit"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT
+if test -n "$ac_ct_NMEDIT"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5
+$as_echo "$ac_ct_NMEDIT" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_NMEDIT" = x; then
+ NMEDIT=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ NMEDIT=$ac_ct_NMEDIT
+ fi
+else
+ NMEDIT="$ac_cv_prog_NMEDIT"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args.
+set dummy ${ac_tool_prefix}lipo; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_LIPO+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$LIPO"; then
+ ac_cv_prog_LIPO="$LIPO" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_LIPO="${ac_tool_prefix}lipo"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+LIPO=$ac_cv_prog_LIPO
+if test -n "$LIPO"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5
+$as_echo "$LIPO" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_LIPO"; then
+ ac_ct_LIPO=$LIPO
+ # Extract the first word of "lipo", so it can be a program name with args.
+set dummy lipo; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_LIPO+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_LIPO"; then
+ ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_LIPO="lipo"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO
+if test -n "$ac_ct_LIPO"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5
+$as_echo "$ac_ct_LIPO" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_LIPO" = x; then
+ LIPO=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ LIPO=$ac_ct_LIPO
+ fi
+else
+ LIPO="$ac_cv_prog_LIPO"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args.
+set dummy ${ac_tool_prefix}otool; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_OTOOL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$OTOOL"; then
+ ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_OTOOL="${ac_tool_prefix}otool"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+OTOOL=$ac_cv_prog_OTOOL
+if test -n "$OTOOL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5
+$as_echo "$OTOOL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OTOOL"; then
+ ac_ct_OTOOL=$OTOOL
+ # Extract the first word of "otool", so it can be a program name with args.
+set dummy otool; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_OTOOL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_OTOOL"; then
+ ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_OTOOL="otool"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL
+if test -n "$ac_ct_OTOOL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5
+$as_echo "$ac_ct_OTOOL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_OTOOL" = x; then
+ OTOOL=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ OTOOL=$ac_ct_OTOOL
+ fi
+else
+ OTOOL="$ac_cv_prog_OTOOL"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args.
+set dummy ${ac_tool_prefix}otool64; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_OTOOL64+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$OTOOL64"; then
+ ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+OTOOL64=$ac_cv_prog_OTOOL64
+if test -n "$OTOOL64"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5
+$as_echo "$OTOOL64" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OTOOL64"; then
+ ac_ct_OTOOL64=$OTOOL64
+ # Extract the first word of "otool64", so it can be a program name with args.
+set dummy otool64; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_OTOOL64+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_OTOOL64"; then
+ ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_OTOOL64="otool64"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64
+if test -n "$ac_ct_OTOOL64"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5
+$as_echo "$ac_ct_OTOOL64" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_OTOOL64" = x; then
+ OTOOL64=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ OTOOL64=$ac_ct_OTOOL64
+ fi
+else
+ OTOOL64="$ac_cv_prog_OTOOL64"
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5
+$as_echo_n "checking for -single_module linker flag... " >&6; }
+if ${lt_cv_apple_cc_single_mod+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_apple_cc_single_mod=no
+ if test -z "${LT_MULTI_MODULE}"; then
+ # By default we will add the -single_module flag. You can override
+ # by either setting the environment variable LT_MULTI_MODULE
+ # non-empty at configure time, or by adding -multi_module to the
+ # link flags.
+ rm -rf libconftest.dylib*
+ echo "int foo(void){return 1;}" > conftest.c
+ echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+-dynamiclib -Wl,-single_module conftest.c" >&5
+ $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err
+ _lt_result=$?
+ if test -f libconftest.dylib && test ! -s conftest.err && test $_lt_result = 0; then
+ lt_cv_apple_cc_single_mod=yes
+ else
+ cat conftest.err >&5
+ fi
+ rm -rf libconftest.dylib*
+ rm -f conftest.*
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5
+$as_echo "$lt_cv_apple_cc_single_mod" >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5
+$as_echo_n "checking for -exported_symbols_list linker flag... " >&6; }
+if ${lt_cv_ld_exported_symbols_list+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_ld_exported_symbols_list=no
+ save_LDFLAGS=$LDFLAGS
+ echo "_main" > conftest.sym
+ LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ lt_cv_ld_exported_symbols_list=yes
+else
+ lt_cv_ld_exported_symbols_list=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LDFLAGS="$save_LDFLAGS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5
+$as_echo "$lt_cv_ld_exported_symbols_list" >&6; }
+ case $host_os in
+ rhapsody* | darwin1.[012])
+ _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;;
+ darwin1.*)
+ _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+ darwin*) # darwin 5.x on
+ # if running on 10.5 or later, the deployment target defaults
+ # to the OS version, if on x86, and 10.4, the deployment
+ # target defaults to 10.4. Don't you love it?
+ case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
+ 10.0,*86*-darwin8*|10.0,*-darwin[91]*)
+ _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+ 10.[012]*)
+ _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+ 10.*)
+ _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+ esac
+ ;;
+ esac
+ if test "$lt_cv_apple_cc_single_mod" = "yes"; then
+ _lt_dar_single_mod='$single_module'
+ fi
+ if test "$lt_cv_ld_exported_symbols_list" = "yes"; then
+ _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym'
+ else
+ _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}'
+ fi
+ if test "$DSYMUTIL" != ":"; then
+ _lt_dsymutil='~$DSYMUTIL $lib || :'
+ else
+ _lt_dsymutil=
+ fi
+ ;;
+ esac
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
+$as_echo_n "checking how to run the C preprocessor... " >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+ if ${ac_cv_prog_CPP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ # Double quotes because CPP needs to be expanded
+ for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+ do
+ ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+@%:@ifdef __STDC__
+@%:@ include <limits.h>
+@%:@else
+@%:@ include <assert.h>
+@%:@endif
+ Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+@%:@include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ # Broken: success on invalid input.
+continue
+else
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+ break
+fi
+
+ done
+ ac_cv_prog_CPP=$CPP
+
+fi
+ CPP=$ac_cv_prog_CPP
+else
+ ac_cv_prog_CPP=$CPP
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
+$as_echo "$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+@%:@ifdef __STDC__
+@%:@ include <limits.h>
+@%:@else
+@%:@ include <assert.h>
+@%:@endif
+ Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+@%:@include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ # Broken: success on invalid input.
+continue
+else
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+
+else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if ${ac_cv_header_stdc+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_header_stdc=yes
+else
+ ac_cv_header_stdc=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "memchr" >/dev/null 2>&1; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "free" >/dev/null 2>&1; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+ if test "$cross_compiling" = yes; then :
+ :
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+ (('a' <= (c) && (c) <= 'i') \
+ || ('j' <= (c) && (c) <= 'r') \
+ || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ if (XOR (islower (i), ISLOWER (i))
+ || toupper (i) != TOUPPER (i))
+ return 2;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+$as_echo "@%:@define STDC_HEADERS 1" >>confdefs.h
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+ inttypes.h stdint.h unistd.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
+"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in dlfcn.h
+do :
+ ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default
+"
+if test "x$ac_cv_header_dlfcn_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_DLFCN_H 1
+_ACEOF
+
+fi
+
+done
+
+
+
+# Set options
+
+
+
+ enable_dlopen=no
+
+
+ enable_win32_dll=no
+
+
+ @%:@ Check whether --enable-shared was given.
+if test "${enable_shared+set}" = set; then :
+ enableval=$enable_shared; p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_shared=yes ;;
+ no) enable_shared=no ;;
+ *)
+ enable_shared=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ for pkg in $enableval; do
+ IFS="$lt_save_ifs"
+ if test "X$pkg" = "X$p"; then
+ enable_shared=yes
+ fi
+ done
+ IFS="$lt_save_ifs"
+ ;;
+ esac
+else
+ enable_shared=yes
+fi
+
+
+
+
+
+
+
+
+
+ @%:@ Check whether --enable-static was given.
+if test "${enable_static+set}" = set; then :
+ enableval=$enable_static; p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_static=yes ;;
+ no) enable_static=no ;;
+ *)
+ enable_static=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ for pkg in $enableval; do
+ IFS="$lt_save_ifs"
+ if test "X$pkg" = "X$p"; then
+ enable_static=yes
+ fi
+ done
+ IFS="$lt_save_ifs"
+ ;;
+ esac
+else
+ enable_static=yes
+fi
+
+
+
+
+
+
+
+
+
+
+@%:@ Check whether --with-pic was given.
+if test "${with_pic+set}" = set; then :
+ withval=$with_pic; pic_mode="$withval"
+else
+ pic_mode=default
+fi
+
+
+test -z "$pic_mode" && pic_mode=default
+
+
+
+
+
+
+
+ @%:@ Check whether --enable-fast-install was given.
+if test "${enable_fast_install+set}" = set; then :
+ enableval=$enable_fast_install; p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_fast_install=yes ;;
+ no) enable_fast_install=no ;;
+ *)
+ enable_fast_install=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ for pkg in $enableval; do
+ IFS="$lt_save_ifs"
+ if test "X$pkg" = "X$p"; then
+ enable_fast_install=yes
+ fi
+ done
+ IFS="$lt_save_ifs"
+ ;;
+ esac
+else
+ enable_fast_install=yes
+fi
+
+
+
+
+
+
+
+
+
+
+
+# This can be used to rebuild libtool when needed
+LIBTOOL_DEPS="$ltmain"
+
+# Always use our own libtool.
+LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+test -z "$LN_S" && LN_S="ln -s"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+if test -n "${ZSH_VERSION+set}" ; then
+ setopt NO_GLOB_SUBST
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5
+$as_echo_n "checking for objdir... " >&6; }
+if ${lt_cv_objdir+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ rm -f .libs 2>/dev/null
+mkdir .libs 2>/dev/null
+if test -d .libs; then
+ lt_cv_objdir=.libs
+else
+ # MS-DOS does not allow filenames that begin with a dot.
+ lt_cv_objdir=_libs
+fi
+rmdir .libs 2>/dev/null
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5
+$as_echo "$lt_cv_objdir" >&6; }
+objdir=$lt_cv_objdir
+
+
+
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define LT_OBJDIR "$lt_cv_objdir/"
+_ACEOF
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+case $host_os in
+aix3*)
+ # AIX sometimes has problems with the GCC collect2 program. For some
+ # reason, if we set the COLLECT_NAMES environment variable, the problems
+ # vanish in a puff of smoke.
+ if test "X${COLLECT_NAMES+set}" != Xset; then
+ COLLECT_NAMES=
+ export COLLECT_NAMES
+ fi
+ ;;
+esac
+
+# Sed substitution that helps us do robust quoting. It backslashifies
+# metacharacters that are still active within double-quoted strings.
+sed_quote_subst='s/\(["`$\\]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\(["`\\]\)/\\\1/g'
+
+# Sed substitution to delay expansion of an escaped shell variable in a
+# double_quote_subst'ed string.
+delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
+
+# Sed substitution to delay expansion of an escaped single quote.
+delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g'
+
+# Sed substitution to avoid accidental globbing in evaled expressions
+no_glob_subst='s/\*/\\\*/g'
+
+# Global variables:
+ofile=libtool
+can_build_shared=yes
+
+# All known linkers require a `.a' archive for static linking (except MSVC,
+# which needs '.lib').
+libext=a
+
+with_gnu_ld="$lt_cv_prog_gnu_ld"
+
+old_CC="$CC"
+old_CFLAGS="$CFLAGS"
+
+# Set sane defaults for various variables
+test -z "$CC" && CC=cc
+test -z "$LTCC" && LTCC=$CC
+test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS
+test -z "$LD" && LD=ld
+test -z "$ac_objext" && ac_objext=o
+
+for cc_temp in $compiler""; do
+ case $cc_temp in
+ compile | *[\\/]compile | ccache | *[\\/]ccache ) ;;
+ distcc | *[\\/]distcc | purify | *[\\/]purify ) ;;
+ \-*) ;;
+ *) break;;
+ esac
+done
+cc_basename=`$ECHO "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"`
+
+
+# Only perform the check for file, if the check method requires it
+test -z "$MAGIC_CMD" && MAGIC_CMD=file
+case $deplibs_check_method in
+file_magic*)
+ if test "$file_magic_cmd" = '$MAGIC_CMD'; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5
+$as_echo_n "checking for ${ac_tool_prefix}file... " >&6; }
+if ${lt_cv_path_MAGIC_CMD+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $MAGIC_CMD in
+[\\/*] | ?:[\\/]*)
+ lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+ ;;
+*)
+ lt_save_MAGIC_CMD="$MAGIC_CMD"
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
+ for ac_dir in $ac_dummy; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/${ac_tool_prefix}file; then
+ lt_cv_path_MAGIC_CMD="$ac_dir/${ac_tool_prefix}file"
+ if test -n "$file_magic_test_file"; then
+ case $deplibs_check_method in
+ "file_magic "*)
+ file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+ MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+ if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+ $EGREP "$file_magic_regex" > /dev/null; then
+ :
+ else
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such. This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem. Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+ fi ;;
+ esac
+ fi
+ break
+ fi
+ done
+ IFS="$lt_save_ifs"
+ MAGIC_CMD="$lt_save_MAGIC_CMD"
+ ;;
+esac
+fi
+
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5
+$as_echo "$MAGIC_CMD" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+
+
+if test -z "$lt_cv_path_MAGIC_CMD"; then
+ if test -n "$ac_tool_prefix"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5
+$as_echo_n "checking for file... " >&6; }
+if ${lt_cv_path_MAGIC_CMD+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $MAGIC_CMD in
+[\\/*] | ?:[\\/]*)
+ lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+ ;;
+*)
+ lt_save_MAGIC_CMD="$MAGIC_CMD"
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
+ for ac_dir in $ac_dummy; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/file; then
+ lt_cv_path_MAGIC_CMD="$ac_dir/file"
+ if test -n "$file_magic_test_file"; then
+ case $deplibs_check_method in
+ "file_magic "*)
+ file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+ MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+ if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+ $EGREP "$file_magic_regex" > /dev/null; then
+ :
+ else
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such. This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem. Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+ fi ;;
+ esac
+ fi
+ break
+ fi
+ done
+ IFS="$lt_save_ifs"
+ MAGIC_CMD="$lt_save_MAGIC_CMD"
+ ;;
+esac
+fi
+
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5
+$as_echo "$MAGIC_CMD" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ else
+ MAGIC_CMD=:
+ fi
+fi
+
+ fi
+ ;;
+esac
+
+# Use C for the default configuration in the libtool script
+
+lt_save_CC="$CC"
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+# Source file extension for C test sources.
+ac_ext=c
+
+# Object file extension for compiled C test sources.
+objext=o
+objext=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="int some_variable = 0;"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='int main(){return(0);}'
+
+
+
+
+
+
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+# Save the default compiler, since it gets overwritten when the other
+# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP.
+compiler_DEFAULT=$CC
+
+# save warnings/boilerplate of simple test code
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_compile_test_code" >conftest.$ac_ext
+eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_compiler_boilerplate=`cat conftest.err`
+$RM conftest*
+
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_link_test_code" >conftest.$ac_ext
+eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_linker_boilerplate=`cat conftest.err`
+$RM -r conftest*
+
+
+## CAVEAT EMPTOR:
+## There is no encapsulation within the following macros, do not change
+## the running order or otherwise move them around unless you know exactly
+## what you are doing...
+if test -n "$compiler"; then
+
+lt_prog_compiler_no_builtin_flag=
+
+if test "$GCC" = yes; then
+ lt_prog_compiler_no_builtin_flag=' -fno-builtin'
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5
+$as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; }
+if ${lt_cv_prog_compiler_rtti_exceptions+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_rtti_exceptions=no
+ ac_outfile=conftest.$ac_objext
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+ lt_compiler_flag="-fno-rtti -fno-exceptions"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ # The option is referenced via a variable to avoid confusing sed.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>conftest.err)
+ ac_status=$?
+ cat conftest.err >&5
+ echo "$as_me:__oline__: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s "$ac_outfile"; then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings other than the usual output.
+ $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_rtti_exceptions=yes
+ fi
+ fi
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5
+$as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; }
+
+if test x"$lt_cv_prog_compiler_rtti_exceptions" = xyes; then
+ lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions"
+else
+ :
+fi
+
+fi
+
+
+
+
+
+
+ lt_prog_compiler_wl=
+lt_prog_compiler_pic=
+lt_prog_compiler_static=
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5
+$as_echo_n "checking for $compiler option to produce PIC... " >&6; }
+
+ if test "$GCC" = yes; then
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_static='-static'
+
+ case $host_os in
+ aix*)
+ # All AIX code is PIC.
+ if test "$host_cpu" = ia64; then
+ # AIX 5 now supports IA64 processor
+ lt_prog_compiler_static='-Bstatic'
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ lt_prog_compiler_pic='-fPIC'
+ ;;
+ m68k)
+ # FIXME: we need at least 68020 code to build shared libraries, but
+ # adding the `-m68020' flag to GCC prevents building anything better,
+ # like `-m68040'.
+ lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4'
+ ;;
+ esac
+ ;;
+
+ beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+ # PIC is the default for these OSes.
+ ;;
+
+ mingw* | cygwin* | pw32* | os2* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ # Although the cygwin gcc ignores -fPIC, still need this for old-style
+ # (--disable-auto-import) libraries
+ lt_prog_compiler_pic='-DDLL_EXPORT'
+ ;;
+
+ darwin* | rhapsody*)
+ # PIC is the default on this platform
+ # Common symbols not allowed in MH_DYLIB files
+ lt_prog_compiler_pic='-fno-common'
+ ;;
+
+ hpux*)
+ # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+ # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag
+ # sets the default TLS model and affects inlining.
+ case $host_cpu in
+ hppa*64*)
+ # +Z the default
+ ;;
+ *)
+ lt_prog_compiler_pic='-fPIC'
+ ;;
+ esac
+ ;;
+
+ interix[3-9]*)
+ # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+ # Instead, we relocate shared libraries at runtime.
+ ;;
+
+ msdosdjgpp*)
+ # Just because we use GCC doesn't mean we suddenly get shared libraries
+ # on systems that don't support them.
+ lt_prog_compiler_can_build_shared=no
+ enable_shared=no
+ ;;
+
+ *nto* | *qnx*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ lt_prog_compiler_pic='-fPIC -shared'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ lt_prog_compiler_pic=-Kconform_pic
+ fi
+ ;;
+
+ *)
+ lt_prog_compiler_pic='-fPIC'
+ ;;
+ esac
+ else
+ # PORTME Check for flag to pass linker flags through the system compiler.
+ case $host_os in
+ aix*)
+ lt_prog_compiler_wl='-Wl,'
+ if test "$host_cpu" = ia64; then
+ # AIX 5 now supports IA64 processor
+ lt_prog_compiler_static='-Bstatic'
+ else
+ lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp'
+ fi
+ ;;
+
+ mingw* | cygwin* | pw32* | os2* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ lt_prog_compiler_pic='-DDLL_EXPORT'
+ ;;
+
+ hpux9* | hpux10* | hpux11*)
+ lt_prog_compiler_wl='-Wl,'
+ # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+ # not for PA HP-UX.
+ case $host_cpu in
+ hppa*64*|ia64*)
+ # +Z the default
+ ;;
+ *)
+ lt_prog_compiler_pic='+Z'
+ ;;
+ esac
+ # Is there a better lt_prog_compiler_static that works with the bundled CC?
+ lt_prog_compiler_static='${wl}-a ${wl}archive'
+ ;;
+
+ irix5* | irix6* | nonstopux*)
+ lt_prog_compiler_wl='-Wl,'
+ # PIC (with -KPIC) is the default.
+ lt_prog_compiler_static='-non_shared'
+ ;;
+
+ linux* | k*bsd*-gnu)
+ case $cc_basename in
+ # old Intel for x86_64 which still supported -KPIC.
+ ecc*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-static'
+ ;;
+ # icc used to be incompatible with GCC.
+ # ICC 10 doesn't accept -KPIC any more.
+ icc* | ifort*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-fPIC'
+ lt_prog_compiler_static='-static'
+ ;;
+ # Lahey Fortran 8.1.
+ lf95*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='--shared'
+ lt_prog_compiler_static='--static'
+ ;;
+ pgcc* | pgf77* | pgf90* | pgf95*)
+ # Portland Group compilers (*not* the Pentium gcc compiler,
+ # which looks to be a dead project)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-fpic'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+ ccc*)
+ lt_prog_compiler_wl='-Wl,'
+ # All Alpha code is PIC.
+ lt_prog_compiler_static='-non_shared'
+ ;;
+ xl*)
+ # IBM XL C 8.0/Fortran 10.1 on PPC
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-qpic'
+ lt_prog_compiler_static='-qstaticlink'
+ ;;
+ *)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*)
+ # Sun C 5.9
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ lt_prog_compiler_wl='-Wl,'
+ ;;
+ *Sun\ F*)
+ # Sun Fortran 8.3 passes all unrecognized flags to the linker
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ lt_prog_compiler_wl=''
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+
+ newsos6)
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ *nto* | *qnx*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ lt_prog_compiler_pic='-fPIC -shared'
+ ;;
+
+ osf3* | osf4* | osf5*)
+ lt_prog_compiler_wl='-Wl,'
+ # All OSF/1 code is PIC.
+ lt_prog_compiler_static='-non_shared'
+ ;;
+
+ rdos*)
+ lt_prog_compiler_static='-non_shared'
+ ;;
+
+ solaris*)
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ case $cc_basename in
+ f77* | f90* | f95*)
+ lt_prog_compiler_wl='-Qoption ld ';;
+ *)
+ lt_prog_compiler_wl='-Wl,';;
+ esac
+ ;;
+
+ sunos4*)
+ lt_prog_compiler_wl='-Qoption ld '
+ lt_prog_compiler_pic='-PIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ sysv4 | sysv4.2uw2* | sysv4.3*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec ;then
+ lt_prog_compiler_pic='-Kconform_pic'
+ lt_prog_compiler_static='-Bstatic'
+ fi
+ ;;
+
+ sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ unicos*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_can_build_shared=no
+ ;;
+
+ uts4*)
+ lt_prog_compiler_pic='-pic'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ *)
+ lt_prog_compiler_can_build_shared=no
+ ;;
+ esac
+ fi
+
+case $host_os in
+ # For platforms which do not support PIC, -DPIC is meaningless:
+ *djgpp*)
+ lt_prog_compiler_pic=
+ ;;
+ *)
+ lt_prog_compiler_pic="$lt_prog_compiler_pic@&t@ -DPIC"
+ ;;
+esac
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_prog_compiler_pic" >&5
+$as_echo "$lt_prog_compiler_pic" >&6; }
+
+
+
+
+
+
+#
+# Check to make sure the PIC flag actually works.
+#
+if test -n "$lt_prog_compiler_pic"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5
+$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; }
+if ${lt_cv_prog_compiler_pic_works+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_pic_works=no
+ ac_outfile=conftest.$ac_objext
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+ lt_compiler_flag="$lt_prog_compiler_pic@&t@ -DPIC"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ # The option is referenced via a variable to avoid confusing sed.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>conftest.err)
+ ac_status=$?
+ cat conftest.err >&5
+ echo "$as_me:__oline__: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s "$ac_outfile"; then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings other than the usual output.
+ $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_pic_works=yes
+ fi
+ fi
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5
+$as_echo "$lt_cv_prog_compiler_pic_works" >&6; }
+
+if test x"$lt_cv_prog_compiler_pic_works" = xyes; then
+ case $lt_prog_compiler_pic in
+ "" | " "*) ;;
+ *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;;
+ esac
+else
+ lt_prog_compiler_pic=
+ lt_prog_compiler_can_build_shared=no
+fi
+
+fi
+
+
+
+
+
+
+#
+# Check to make sure the static flag actually works.
+#
+wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5
+$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; }
+if ${lt_cv_prog_compiler_static_works+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_static_works=no
+ save_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS $lt_tmp_static_flag"
+ echo "$lt_simple_link_test_code" > conftest.$ac_ext
+ if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+ # The linker can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ if test -s conftest.err; then
+ # Append any errors to the config.log.
+ cat conftest.err 1>&5
+ $ECHO "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if diff conftest.exp conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_static_works=yes
+ fi
+ else
+ lt_cv_prog_compiler_static_works=yes
+ fi
+ fi
+ $RM -r conftest*
+ LDFLAGS="$save_LDFLAGS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5
+$as_echo "$lt_cv_prog_compiler_static_works" >&6; }
+
+if test x"$lt_cv_prog_compiler_static_works" = xyes; then
+ :
+else
+ lt_prog_compiler_static=
+fi
+
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5
+$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
+if ${lt_cv_prog_compiler_c_o+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_c_o=no
+ $RM -r conftest 2>/dev/null
+ mkdir conftest
+ cd conftest
+ mkdir out
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ lt_compiler_flag="-o out/conftest2.$ac_objext"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>out/conftest.err)
+ ac_status=$?
+ cat out/conftest.err >&5
+ echo "$as_me:__oline__: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s out/conftest2.$ac_objext
+ then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp
+ $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+ if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_c_o=yes
+ fi
+ fi
+ chmod u+w . 2>&5
+ $RM conftest*
+ # SGI C++ compiler will create directory out/ii_files/ for
+ # template instantiation
+ test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+ $RM out/* && rmdir out
+ cd ..
+ $RM -r conftest
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5
+$as_echo "$lt_cv_prog_compiler_c_o" >&6; }
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5
+$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
+if ${lt_cv_prog_compiler_c_o+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_c_o=no
+ $RM -r conftest 2>/dev/null
+ mkdir conftest
+ cd conftest
+ mkdir out
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ lt_compiler_flag="-o out/conftest2.$ac_objext"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>out/conftest.err)
+ ac_status=$?
+ cat out/conftest.err >&5
+ echo "$as_me:__oline__: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s out/conftest2.$ac_objext
+ then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp
+ $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+ if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_c_o=yes
+ fi
+ fi
+ chmod u+w . 2>&5
+ $RM conftest*
+ # SGI C++ compiler will create directory out/ii_files/ for
+ # template instantiation
+ test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+ $RM out/* && rmdir out
+ cd ..
+ $RM -r conftest
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5
+$as_echo "$lt_cv_prog_compiler_c_o" >&6; }
+
+
+
+
+hard_links="nottested"
+if test "$lt_cv_prog_compiler_c_o" = no && test "$need_locks" != no; then
+ # do not overwrite the value of need_locks provided by the user
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5
+$as_echo_n "checking if we can lock with hard links... " >&6; }
+ hard_links=yes
+ $RM conftest*
+ ln conftest.a conftest.b 2>/dev/null && hard_links=no
+ touch conftest.a
+ ln conftest.a conftest.b 2>&5 || hard_links=no
+ ln conftest.a conftest.b 2>/dev/null && hard_links=no
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5
+$as_echo "$hard_links" >&6; }
+ if test "$hard_links" = no; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5
+$as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;}
+ need_locks=warn
+ fi
+else
+ need_locks=no
+fi
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5
+$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; }
+
+ runpath_var=
+ allow_undefined_flag=
+ always_export_symbols=no
+ archive_cmds=
+ archive_expsym_cmds=
+ compiler_needs_object=no
+ enable_shared_with_static_runtimes=no
+ export_dynamic_flag_spec=
+ export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+ hardcode_automatic=no
+ hardcode_direct=no
+ hardcode_direct_absolute=no
+ hardcode_libdir_flag_spec=
+ hardcode_libdir_flag_spec_ld=
+ hardcode_libdir_separator=
+ hardcode_minus_L=no
+ hardcode_shlibpath_var=unsupported
+ inherit_rpath=no
+ link_all_deplibs=unknown
+ module_cmds=
+ module_expsym_cmds=
+ old_archive_from_new_cmds=
+ old_archive_from_expsyms_cmds=
+ thread_safe_flag_spec=
+ whole_archive_flag_spec=
+ # include_expsyms should be a list of space-separated symbols to be *always*
+ # included in the symbol list
+ include_expsyms=
+ # exclude_expsyms can be an extended regexp of symbols to exclude
+ # it will be wrapped by ` (' and `)$', so one must not match beginning or
+ # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc',
+ # as well as any symbol that contains `d'.
+ exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'
+ # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
+ # platforms (ab)use it in PIC code, but their linkers get confused if
+ # the symbol is explicitly referenced. Since portable code cannot
+ # rely on this symbol name, it's probably fine to never include it in
+ # preloaded symbol tables.
+ # Exclude shared library initialization/finalization symbols.
+ extract_expsyms_cmds=
+
+ case $host_os in
+ cygwin* | mingw* | pw32* | cegcc*)
+ # FIXME: the MSVC++ port hasn't been tested in a loooong time
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ if test "$GCC" != yes; then
+ with_gnu_ld=no
+ fi
+ ;;
+ interix*)
+ # we just hope/assume this is gcc and not c89 (= MSVC++)
+ with_gnu_ld=yes
+ ;;
+ openbsd*)
+ with_gnu_ld=no
+ ;;
+ esac
+
+ ld_shlibs=yes
+ if test "$with_gnu_ld" = yes; then
+ # If archive_cmds runs LD, not CC, wlarc should be empty
+ wlarc='${wl}'
+
+ # Set some defaults for GNU ld with shared library support. These
+ # are reset later if shared libraries are not supported. Putting them
+ # here allows them to be overridden if necessary.
+ runpath_var=LD_RUN_PATH
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ export_dynamic_flag_spec='${wl}--export-dynamic'
+ # ancient GNU ld didn't support --whole-archive et. al.
+ if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then
+ whole_archive_flag_spec="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+ else
+ whole_archive_flag_spec=
+ fi
+ supports_anon_versioning=no
+ case `$LD -v 2>&1` in
+ *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11
+ *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
+ *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
+ *\ 2.11.*) ;; # other 2.11 versions
+ *) supports_anon_versioning=yes ;;
+ esac
+
+ # See if GNU ld supports shared libraries.
+ case $host_os in
+ aix[3-9]*)
+ # On AIX/PPC, the GNU linker is very broken
+ if test "$host_cpu" != ia64; then
+ ld_shlibs=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the GNU linker, at least up to release 2.9.1, is reported
+*** to be unable to reliably create shared libraries on AIX.
+*** Therefore, libtool is disabling shared libraries support. If you
+*** really care for shared libraries, you may want to modify your PATH
+*** so that a non-GNU linker is found, and then restart.
+
+_LT_EOF
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds=''
+ ;;
+ m68k)
+ archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ ;;
+ esac
+ ;;
+
+ beos*)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ allow_undefined_flag=unsupported
+ # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+ # support --undefined. This deserves some investigation. FIXME
+ archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ cygwin* | mingw* | pw32* | cegcc*)
+ # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless,
+ # as there is no search path for DLLs.
+ hardcode_libdir_flag_spec='-L$libdir'
+ allow_undefined_flag=unsupported
+ always_export_symbols=no
+ enable_shared_with_static_runtimes=yes
+ export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols'
+
+ if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ # If the export-symbols file already is a .def file (1st line
+ # is EXPORTS), use it as is; otherwise, prepend...
+ archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+ cp $export_symbols $output_objdir/$soname.def;
+ else
+ echo EXPORTS > $output_objdir/$soname.def;
+ cat $export_symbols >> $output_objdir/$soname.def;
+ fi~
+ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ interix[3-9]*)
+ hardcode_direct=no
+ hardcode_shlibpath_var=no
+ hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+ export_dynamic_flag_spec='${wl}-E'
+ # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+ # Instead, shared libraries are loaded at an image base (0x10000000 by
+ # default) and relocated if they conflict, which is a slow very memory
+ # consuming and fragmenting process. To avoid this, we pick a random,
+ # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+ # time. Moving up from 0x10000000 also allows more sbrk(2) space.
+ archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ archive_expsym_cmds='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ ;;
+
+ gnu* | linux* | tpf* | k*bsd*-gnu)
+ tmp_diet=no
+ if test "$host_os" = linux-dietlibc; then
+ case $cc_basename in
+ diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn)
+ esac
+ fi
+ if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \
+ && test "$tmp_diet" = no
+ then
+ tmp_addflag=
+ tmp_sharedflag='-shared'
+ case $cc_basename,$host_cpu in
+ pgcc*) # Portland Group C compiler
+ whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+ tmp_addflag=' $pic_flag'
+ ;;
+ pgf77* | pgf90* | pgf95*) # Portland Group f77 and f90 compilers
+ whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+ tmp_addflag=' $pic_flag -Mnomain' ;;
+ ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64
+ tmp_addflag=' -i_dynamic' ;;
+ efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64
+ tmp_addflag=' -i_dynamic -nofor_main' ;;
+ ifc* | ifort*) # Intel Fortran compiler
+ tmp_addflag=' -nofor_main' ;;
+ lf95*) # Lahey Fortran 8.1
+ whole_archive_flag_spec=
+ tmp_sharedflag='--shared' ;;
+ xl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below)
+ tmp_sharedflag='-qmkshrobj'
+ tmp_addflag= ;;
+ esac
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*) # Sun C 5.9
+ whole_archive_flag_spec='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+ compiler_needs_object=yes
+ tmp_sharedflag='-G' ;;
+ *Sun\ F*) # Sun Fortran 8.3
+ tmp_sharedflag='-G' ;;
+ esac
+ archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+
+ if test "x$supports_anon_versioning" = xyes; then
+ archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+ fi
+
+ case $cc_basename in
+ xlf*)
+ # IBM XL Fortran 10.1 on PPC cannot create shared libs itself
+ whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive'
+ hardcode_libdir_flag_spec=
+ hardcode_libdir_flag_spec_ld='-rpath $libdir'
+ archive_cmds='$LD -shared $libobjs $deplibs $compiler_flags -soname $soname -o $lib'
+ if test "x$supports_anon_versioning" = xyes; then
+ archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $LD -shared $libobjs $deplibs $compiler_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib'
+ fi
+ ;;
+ esac
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
+ wlarc=
+ else
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ fi
+ ;;
+
+ solaris*)
+ if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then
+ ld_shlibs=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: The releases 2.8.* of the GNU linker cannot reliably
+*** create shared libraries on Solaris systems. Therefore, libtool
+*** is disabling shared libraries support. We urge you to upgrade GNU
+*** binutils to release 2.9.1 or newer. Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+ elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*)
+ case `$LD -v 2>&1` in
+ *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*)
+ ld_shlibs=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not
+*** reliably create shared libraries on SCO systems. Therefore, libtool
+*** is disabling shared libraries support. We urge you to upgrade GNU
+*** binutils to release 2.16.91.0.3 or newer. Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+ ;;
+ *)
+ # For security reasons, it is highly recommended that you always
+ # use absolute paths for naming shared libraries, and exclude the
+ # DT_RUNPATH tag from executables and libraries. But doing so
+ # requires that you compile everything twice, which is a pain.
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+ esac
+ ;;
+
+ sunos4*)
+ archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ wlarc=
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ *)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+ esac
+
+ if test "$ld_shlibs" = no; then
+ runpath_var=
+ hardcode_libdir_flag_spec=
+ export_dynamic_flag_spec=
+ whole_archive_flag_spec=
+ fi
+ else
+ # PORTME fill in a description of your system's linker (not GNU ld)
+ case $host_os in
+ aix3*)
+ allow_undefined_flag=unsupported
+ always_export_symbols=yes
+ archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'
+ # Note: this linker hardcodes the directories in LIBPATH if there
+ # are no directories specified by -L.
+ hardcode_minus_L=yes
+ if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then
+ # Neither direct hardcoding nor static linking is supported with a
+ # broken collect2.
+ hardcode_direct=unsupported
+ fi
+ ;;
+
+ aix[4-9]*)
+ if test "$host_cpu" = ia64; then
+ # On IA64, the linker does run time linking by default, so we don't
+ # have to do anything special.
+ aix_use_runtimelinking=no
+ exp_sym_flag='-Bexport'
+ no_entry_flag=""
+ else
+ # If we're using GNU nm, then we don't want the "-C" option.
+ # -C means demangle to AIX nm, but means don't demangle with GNU nm
+ if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+ export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+ else
+ export_symbols_cmds='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+ fi
+ aix_use_runtimelinking=no
+
+ # Test if we are trying to use run time linking or normal
+ # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+ # need to do runtime linking.
+ case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*)
+ for ld_flag in $LDFLAGS; do
+ if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then
+ aix_use_runtimelinking=yes
+ break
+ fi
+ done
+ ;;
+ esac
+
+ exp_sym_flag='-bexport'
+ no_entry_flag='-bnoentry'
+ fi
+
+ # When large executables or shared objects are built, AIX ld can
+ # have problems creating the table of contents. If linking a library
+ # or program results in "error TOC overflow" add -mminimal-toc to
+ # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not
+ # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+ archive_cmds=''
+ hardcode_direct=yes
+ hardcode_direct_absolute=yes
+ hardcode_libdir_separator=':'
+ link_all_deplibs=yes
+ file_list_spec='${wl}-f,'
+
+ if test "$GCC" = yes; then
+ case $host_os in aix4.[012]|aix4.[012].*)
+ # We only want to do this on AIX 4.2 and lower, the check
+ # below for broken collect2 doesn't work under 4.3+
+ collect2name=`${CC} -print-prog-name=collect2`
+ if test -f "$collect2name" &&
+ strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+ then
+ # We have reworked collect2
+ :
+ else
+ # We have old collect2
+ hardcode_direct=unsupported
+ # It fails to find uninstalled libraries when the uninstalled
+ # path is not listed in the libpath. Setting hardcode_minus_L
+ # to unsupported forces relinking
+ hardcode_minus_L=yes
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_libdir_separator=
+ fi
+ ;;
+ esac
+ shared_flag='-shared'
+ if test "$aix_use_runtimelinking" = yes; then
+ shared_flag="$shared_flag "'${wl}-G'
+ fi
+ else
+ # not using gcc
+ if test "$host_cpu" = ia64; then
+ # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+ # chokes on -Wl,-G. The following line is correct:
+ shared_flag='-G'
+ else
+ if test "$aix_use_runtimelinking" = yes; then
+ shared_flag='${wl}-G'
+ else
+ shared_flag='${wl}-bM:SRE'
+ fi
+ fi
+ fi
+
+ export_dynamic_flag_spec='${wl}-bexpall'
+ # It seems that -bexpall does not export symbols beginning with
+ # underscore (_), so it is better to generate a list of symbols to export.
+ always_export_symbols=yes
+ if test "$aix_use_runtimelinking" = yes; then
+ # Warning - without using the other runtime loading flags (-brtl),
+ # -berok will link without error, but may produce a broken library.
+ allow_undefined_flag='-berok'
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+lt_aix_libpath_sed='
+ /Import File Strings/,/^$/ {
+ /^0/ {
+ s/^0 *\(.*\)$/\1/
+ p
+ }
+ }'
+aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+# Check for a 64-bit object if we didn't find anything.
+if test -z "$aix_libpath"; then
+ aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+fi
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+
+ hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath"
+ archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then $ECHO "X${wl}${allow_undefined_flag}" | $Xsed; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+ else
+ if test "$host_cpu" = ia64; then
+ hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib'
+ allow_undefined_flag="-z nodefs"
+ archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols"
+ else
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+lt_aix_libpath_sed='
+ /Import File Strings/,/^$/ {
+ /^0/ {
+ s/^0 *\(.*\)$/\1/
+ p
+ }
+ }'
+aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+# Check for a 64-bit object if we didn't find anything.
+if test -z "$aix_libpath"; then
+ aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+fi
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+
+ hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath"
+ # Warning - without using the other run time loading flags,
+ # -berok will link without error, but may produce a broken library.
+ no_undefined_flag=' ${wl}-bernotok'
+ allow_undefined_flag=' ${wl}-berok'
+ # Exported symbols can be pulled into shared objects from archives
+ whole_archive_flag_spec='$convenience'
+ archive_cmds_need_lc=yes
+ # This is similar to how AIX traditionally builds its shared libraries.
+ archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+ fi
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds=''
+ ;;
+ m68k)
+ archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ ;;
+ esac
+ ;;
+
+ bsdi[45]*)
+ export_dynamic_flag_spec=-rdynamic
+ ;;
+
+ cygwin* | mingw* | pw32* | cegcc*)
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ # hardcode_libdir_flag_spec is actually meaningless, as there is
+ # no search path for DLLs.
+ hardcode_libdir_flag_spec=' '
+ allow_undefined_flag=unsupported
+ # Tell ltmain to make .lib files, not .a files.
+ libext=lib
+ # Tell ltmain to make .dll files, not .so files.
+ shrext_cmds=".dll"
+ # FIXME: Setting linknames here is a bad hack.
+ archive_cmds='$CC -o $lib $libobjs $compiler_flags `$ECHO "X$deplibs" | $Xsed -e '\''s/ -lc$//'\''` -link -dll~linknames='
+ # The linker will automatically build a .lib file if we build a DLL.
+ old_archive_from_new_cmds='true'
+ # FIXME: Should let the user specify the lib program.
+ old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs'
+ fix_srcfile_path='`cygpath -w "$srcfile"`'
+ enable_shared_with_static_runtimes=yes
+ ;;
+
+ darwin* | rhapsody*)
+
+
+ archive_cmds_need_lc=no
+ hardcode_direct=no
+ hardcode_automatic=yes
+ hardcode_shlibpath_var=unsupported
+ whole_archive_flag_spec=''
+ link_all_deplibs=yes
+ allow_undefined_flag="$_lt_dar_allow_undefined"
+ case $cc_basename in
+ ifort*) _lt_dar_can_shared=yes ;;
+ *) _lt_dar_can_shared=$GCC ;;
+ esac
+ if test "$_lt_dar_can_shared" = "yes"; then
+ output_verbose_link_cmd=echo
+ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}"
+ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}"
+ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}"
+ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}"
+
+ else
+ ld_shlibs=no
+ fi
+
+ ;;
+
+ dgux*)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_shlibpath_var=no
+ ;;
+
+ freebsd1*)
+ ld_shlibs=no
+ ;;
+
+ # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
+ # support. Future versions do this automatically, but an explicit c++rt0.o
+ # does not break anything, and helps significantly (at the cost of a little
+ # extra space).
+ freebsd2.2*)
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ # Unfortunately, older versions of FreeBSD 2 do not have this feature.
+ freebsd2*)
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=yes
+ hardcode_minus_L=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
+ freebsd* | dragonfly*)
+ archive_cmds='$CC -shared -o $lib $libobjs $deplibs $compiler_flags'
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ hpux9*)
+ if test "$GCC" = yes; then
+ archive_cmds='$RM $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+ else
+ archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+ fi
+ hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+ hardcode_libdir_separator=:
+ hardcode_direct=yes
+
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ hardcode_minus_L=yes
+ export_dynamic_flag_spec='${wl}-E'
+ ;;
+
+ hpux10*)
+ if test "$GCC" = yes -a "$with_gnu_ld" = no; then
+ archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+ fi
+ if test "$with_gnu_ld" = no; then
+ hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+ hardcode_libdir_flag_spec_ld='+b $libdir'
+ hardcode_libdir_separator=:
+ hardcode_direct=yes
+ hardcode_direct_absolute=yes
+ export_dynamic_flag_spec='${wl}-E'
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ hardcode_minus_L=yes
+ fi
+ ;;
+
+ hpux11*)
+ if test "$GCC" = yes -a "$with_gnu_ld" = no; then
+ case $host_cpu in
+ hppa*64*)
+ archive_cmds='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ ia64*)
+ archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ else
+ case $host_cpu in
+ hppa*64*)
+ archive_cmds='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ ia64*)
+ archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ fi
+ if test "$with_gnu_ld" = no; then
+ hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+ hardcode_libdir_separator=:
+
+ case $host_cpu in
+ hppa*64*|ia64*)
+ hardcode_direct=no
+ hardcode_shlibpath_var=no
+ ;;
+ *)
+ hardcode_direct=yes
+ hardcode_direct_absolute=yes
+ export_dynamic_flag_spec='${wl}-E'
+
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ hardcode_minus_L=yes
+ ;;
+ esac
+ fi
+ ;;
+
+ irix5* | irix6* | nonstopux*)
+ if test "$GCC" = yes; then
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ # Try to use the -exported_symbol ld option, if it does not
+ # work, assume that -exports_file does not work either and
+ # implicitly export all symbols.
+ save_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+int foo (void) { return 0; }
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib'
+
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LDFLAGS="$save_LDFLAGS"
+ else
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib'
+ fi
+ archive_cmds_need_lc='no'
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ hardcode_libdir_separator=:
+ inherit_rpath=yes
+ link_all_deplibs=yes
+ ;;
+
+ netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out
+ else
+ archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF
+ fi
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ newsos6)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=yes
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ hardcode_libdir_separator=:
+ hardcode_shlibpath_var=no
+ ;;
+
+ *nto* | *qnx*)
+ ;;
+
+ openbsd*)
+ if test -f /usr/libexec/ld.so; then
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ hardcode_direct_absolute=yes
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+ archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols'
+ hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+ export_dynamic_flag_spec='${wl}-E'
+ else
+ case $host_os in
+ openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*)
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_libdir_flag_spec='-R$libdir'
+ ;;
+ *)
+ archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+ ;;
+ esac
+ fi
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ os2*)
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ allow_undefined_flag=unsupported
+ archive_cmds='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$ECHO DATA >> $output_objdir/$libname.def~$ECHO " SINGLE NONSHARED" >> $output_objdir/$libname.def~$ECHO EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def'
+ old_archive_from_new_cmds='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def'
+ ;;
+
+ osf3*)
+ if test "$GCC" = yes; then
+ allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*'
+ archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ else
+ allow_undefined_flag=' -expect_unresolved \*'
+ archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+ fi
+ archive_cmds_need_lc='no'
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ hardcode_libdir_separator=:
+ ;;
+
+ osf4* | osf5*) # as osf3* with the addition of -msym flag
+ if test "$GCC" = yes; then
+ allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*'
+ archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ else
+ allow_undefined_flag=' -expect_unresolved \*'
+ archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+ archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~
+ $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp'
+
+ # Both c and cxx compiler support -rpath directly
+ hardcode_libdir_flag_spec='-rpath $libdir'
+ fi
+ archive_cmds_need_lc='no'
+ hardcode_libdir_separator=:
+ ;;
+
+ solaris*)
+ no_undefined_flag=' -z defs'
+ if test "$GCC" = yes; then
+ wlarc='${wl}'
+ archive_cmds='$CC -shared ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -shared ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ else
+ case `$CC -V 2>&1` in
+ *"Compilers 5.0"*)
+ wlarc=''
+ archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp'
+ ;;
+ *)
+ wlarc='${wl}'
+ archive_cmds='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ ;;
+ esac
+ fi
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_shlibpath_var=no
+ case $host_os in
+ solaris2.[0-5] | solaris2.[0-5].*) ;;
+ *)
+ # The compiler driver will combine and reorder linker options,
+ # but understands `-z linker_flag'. GCC discards it without `$wl',
+ # but is careful enough not to reorder.
+ # Supported since Solaris 2.6 (maybe 2.5.1?)
+ if test "$GCC" = yes; then
+ whole_archive_flag_spec='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
+ else
+ whole_archive_flag_spec='-z allextract$convenience -z defaultextract'
+ fi
+ ;;
+ esac
+ link_all_deplibs=yes
+ ;;
+
+ sunos4*)
+ if test "x$host_vendor" = xsequent; then
+ # Use $CC to link under sequent, because it throws in some extra .o
+ # files that make .init and .fini sections work.
+ archive_cmds='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
+ fi
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_direct=yes
+ hardcode_minus_L=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ sysv4)
+ case $host_vendor in
+ sni)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=yes # is this really true???
+ ;;
+ siemens)
+ ## LD is ld it makes a PLAMLIB
+ ## CC just makes a GrossModule.
+ archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+ reload_cmds='$CC -r -o $output$reload_objs'
+ hardcode_direct=no
+ ;;
+ motorola)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=no #Motorola manual says yes, but my tests say they lie
+ ;;
+ esac
+ runpath_var='LD_RUN_PATH'
+ hardcode_shlibpath_var=no
+ ;;
+
+ sysv4.3*)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_shlibpath_var=no
+ export_dynamic_flag_spec='-Bexport'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_shlibpath_var=no
+ runpath_var=LD_RUN_PATH
+ hardcode_runpath_var=yes
+ ld_shlibs=yes
+ fi
+ ;;
+
+ sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*)
+ no_undefined_flag='${wl}-z,text'
+ archive_cmds_need_lc=no
+ hardcode_shlibpath_var=no
+ runpath_var='LD_RUN_PATH'
+
+ if test "$GCC" = yes; then
+ archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ fi
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6*)
+ # Note: We can NOT use -z defs as we might desire, because we do not
+ # link with -lc, and that would cause any symbols used from libc to
+ # always be unresolved, which means just about no library would
+ # ever link correctly. If we're not using GNU ld we use -z text
+ # though, which does catch some bad symbols but isn't as heavy-handed
+ # as -z defs.
+ no_undefined_flag='${wl}-z,text'
+ allow_undefined_flag='${wl}-z,nodefs'
+ archive_cmds_need_lc=no
+ hardcode_shlibpath_var=no
+ hardcode_libdir_flag_spec='${wl}-R,$libdir'
+ hardcode_libdir_separator=':'
+ link_all_deplibs=yes
+ export_dynamic_flag_spec='${wl}-Bexport'
+ runpath_var='LD_RUN_PATH'
+
+ if test "$GCC" = yes; then
+ archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ fi
+ ;;
+
+ uts4*)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_shlibpath_var=no
+ ;;
+
+ *)
+ ld_shlibs=no
+ ;;
+ esac
+
+ if test x$host_vendor = xsni; then
+ case $host in
+ sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+ export_dynamic_flag_spec='${wl}-Blargedynsym'
+ ;;
+ esac
+ fi
+ fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5
+$as_echo "$ld_shlibs" >&6; }
+test "$ld_shlibs" = no && can_build_shared=no
+
+with_gnu_ld=$with_gnu_ld
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#
+# Do we need to explicitly link libc?
+#
+case "x$archive_cmds_need_lc" in
+x|xyes)
+ # Assume -lc should be added
+ archive_cmds_need_lc=yes
+
+ if test "$enable_shared" = yes && test "$GCC" = yes; then
+ case $archive_cmds in
+ *'~'*)
+ # FIXME: we may have to deal with multi-command sequences.
+ ;;
+ '$CC '*)
+ # Test whether the compiler implicitly links with -lc since on some
+ # systems, -lgcc has to come before -lc. If gcc already passes -lc
+ # to ld, don't add -lc before -lgcc.
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5
+$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; }
+ $RM conftest*
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } 2>conftest.err; then
+ soname=conftest
+ lib=conftest
+ libobjs=conftest.$ac_objext
+ deplibs=
+ wl=$lt_prog_compiler_wl
+ pic_flag=$lt_prog_compiler_pic
+ compiler_flags=-v
+ linker_flags=-v
+ verstring=
+ output_objdir=.
+ libname=conftest
+ lt_save_allow_undefined_flag=$allow_undefined_flag
+ allow_undefined_flag=
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5
+ (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+ then
+ archive_cmds_need_lc=no
+ else
+ archive_cmds_need_lc=yes
+ fi
+ allow_undefined_flag=$lt_save_allow_undefined_flag
+ else
+ cat conftest.err 1>&5
+ fi
+ $RM conftest*
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $archive_cmds_need_lc" >&5
+$as_echo "$archive_cmds_need_lc" >&6; }
+ ;;
+ esac
+ fi
+ ;;
+esac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5
+$as_echo_n "checking dynamic linker characteristics... " >&6; }
+
+if test "$GCC" = yes; then
+ case $host_os in
+ darwin*) lt_awk_arg="/^libraries:/,/LR/" ;;
+ *) lt_awk_arg="/^libraries:/" ;;
+ esac
+ lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e "s,=/,/,g"`
+ if $ECHO "$lt_search_path_spec" | $GREP ';' >/dev/null ; then
+ # if the path contains ";" then we assume it to be the separator
+ # otherwise default to the standard path separator (i.e. ":") - it is
+ # assumed that no part of a normal pathname contains ";" but that should
+ # okay in the real world where ";" in dirpaths is itself problematic.
+ lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e 's/;/ /g'`
+ else
+ lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+ fi
+ # Ok, now we have the path, separated by spaces, we can step through it
+ # and add multilib dir if necessary.
+ lt_tmp_lt_search_path_spec=
+ lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null`
+ for lt_sys_path in $lt_search_path_spec; do
+ if test -d "$lt_sys_path/$lt_multi_os_dir"; then
+ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir"
+ else
+ test -d "$lt_sys_path" && \
+ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path"
+ fi
+ done
+ lt_search_path_spec=`$ECHO $lt_tmp_lt_search_path_spec | awk '
+BEGIN {RS=" "; FS="/|\n";} {
+ lt_foo="";
+ lt_count=0;
+ for (lt_i = NF; lt_i > 0; lt_i--) {
+ if ($lt_i != "" && $lt_i != ".") {
+ if ($lt_i == "..") {
+ lt_count++;
+ } else {
+ if (lt_count == 0) {
+ lt_foo="/" $lt_i lt_foo;
+ } else {
+ lt_count--;
+ }
+ }
+ }
+ }
+ if (lt_foo != "") { lt_freq[lt_foo]++; }
+ if (lt_freq[lt_foo] == 1) { print lt_foo; }
+}'`
+ sys_lib_search_path_spec=`$ECHO $lt_search_path_spec`
+else
+ sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+fi
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+shrext_cmds=".so"
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+need_lib_prefix=unknown
+hardcode_into_libs=no
+
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+need_version=unknown
+
+case $host_os in
+aix3*)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a'
+ shlibpath_var=LIBPATH
+
+ # AIX 3 has no versioning support, so we append a major version to the name.
+ soname_spec='${libname}${release}${shared_ext}$major'
+ ;;
+
+aix[4-9]*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ hardcode_into_libs=yes
+ if test "$host_cpu" = ia64; then
+ # AIX 5 supports IA64
+ library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ else
+ # With GCC up to 2.95.x, collect2 would create an import file
+ # for dependence libraries. The import file would start with
+ # the line `#! .'. This would cause the generated library to
+ # depend on `.', always an invalid library. This was fixed in
+ # development snapshots of GCC prior to 3.0.
+ case $host_os in
+ aix4 | aix4.[01] | aix4.[01].*)
+ if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
+ echo ' yes '
+ echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then
+ :
+ else
+ can_build_shared=no
+ fi
+ ;;
+ esac
+ # AIX (on Power*) has no versioning support, so currently we can not hardcode correct
+ # soname into executable. Probably we can add versioning support to
+ # collect2, so additional links can be useful in future.
+ if test "$aix_use_runtimelinking" = yes; then
+ # If using run time linking (on AIX 4.2 or later) use lib<name>.so
+ # instead of lib<name>.a to let people know that these are not
+ # typical AIX shared libraries.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ else
+ # We preserve .a as extension for shared libraries through AIX4.2
+ # and later when we are not doing run time linking.
+ library_names_spec='${libname}${release}.a $libname.a'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ fi
+ shlibpath_var=LIBPATH
+ fi
+ ;;
+
+amigaos*)
+ case $host_cpu in
+ powerpc)
+ # Since July 2007 AmigaOS4 officially supports .so libraries.
+ # When compiling the executable, add -use-dynld -Lsobjs: to the compileline.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ ;;
+ m68k)
+ library_names_spec='$libname.ixlibrary $libname.a'
+ # Create ${libname}_ixlibrary.a entries in /sys/libs.
+ finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$ECHO "X$lib" | $Xsed -e '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+ ;;
+ esac
+ ;;
+
+beos*)
+ library_names_spec='${libname}${shared_ext}'
+ dynamic_linker="$host_os ld.so"
+ shlibpath_var=LIBRARY_PATH
+ ;;
+
+bsdi[45]*)
+ version_type=linux
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+ sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+ # the default ld.so.conf also contains /usr/contrib/lib and
+ # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+ # libtool to hard-code these into programs
+ ;;
+
+cygwin* | mingw* | pw32* | cegcc*)
+ version_type=windows
+ shrext_cmds=".dll"
+ need_version=no
+ need_lib_prefix=no
+
+ case $GCC,$host_os in
+ yes,cygwin* | yes,mingw* | yes,pw32* | yes,cegcc*)
+ library_names_spec='$libname.dll.a'
+ # DLL is installed to $(libdir)/../bin by postinstall_cmds
+ postinstall_cmds='base_file=`basename \${file}`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~
+ dldir=$destdir/`dirname \$dlpath`~
+ test -d \$dldir || mkdir -p \$dldir~
+ $install_prog $dir/$dlname \$dldir/$dlname~
+ chmod a+x \$dldir/$dlname~
+ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+ eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+ fi'
+ postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+ dlpath=$dir/\$dldll~
+ $RM \$dlpath'
+ shlibpath_overrides_runpath=yes
+
+ case $host_os in
+ cygwin*)
+ # Cygwin DLLs use 'cyg' prefix rather than 'lib'
+ soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+ sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib"
+ ;;
+ mingw* | cegcc*)
+ # MinGW DLLs use traditional 'lib' prefix
+ soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+ sys_lib_search_path_spec=`$CC -print-search-dirs | $GREP "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"`
+ if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then
+ # It is most probably a Windows format PATH printed by
+ # mingw gcc, but we are running on Cygwin. Gcc prints its search
+ # path with ; separators, and with drive letters. We can handle the
+ # drive letters (cygwin fileutils understands them), so leave them,
+ # especially as we might pass files found there to a mingw objdump,
+ # which wouldn't understand a cygwinified path. Ahh.
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+ else
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+ fi
+ ;;
+ pw32*)
+ # pw32 DLLs use 'pw' prefix rather than 'lib'
+ library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+ ;;
+ esac
+ ;;
+
+ *)
+ library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib'
+ ;;
+ esac
+ dynamic_linker='Win32 ld.exe'
+ # FIXME: first we should search . and the directory the executable is in
+ shlibpath_var=PATH
+ ;;
+
+darwin* | rhapsody*)
+ dynamic_linker="$host_os dyld"
+ version_type=darwin
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext'
+ soname_spec='${libname}${release}${major}$shared_ext'
+ shlibpath_overrides_runpath=yes
+ shlibpath_var=DYLD_LIBRARY_PATH
+ shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'
+
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"
+ sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
+ ;;
+
+dgux*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+freebsd1*)
+ dynamic_linker=no
+ ;;
+
+freebsd* | dragonfly*)
+ # DragonFly does not have aout. When/if they implement a new
+ # versioning mechanism, adjust this.
+ if test -x /usr/bin/objformat; then
+ objformat=`/usr/bin/objformat`
+ else
+ case $host_os in
+ freebsd[123]*) objformat=aout ;;
+ *) objformat=elf ;;
+ esac
+ fi
+ version_type=freebsd-$objformat
+ case $version_type in
+ freebsd-elf*)
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+ need_version=no
+ need_lib_prefix=no
+ ;;
+ freebsd-*)
+ library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix'
+ need_version=yes
+ ;;
+ esac
+ shlibpath_var=LD_LIBRARY_PATH
+ case $host_os in
+ freebsd2*)
+ shlibpath_overrides_runpath=yes
+ ;;
+ freebsd3.[01]* | freebsdelf3.[01]*)
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+ freebsd3.[2-9]* | freebsdelf3.[2-9]* | \
+ freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1)
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+ *) # from 4.6 on, and DragonFly
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+ esac
+ ;;
+
+gnu*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ hardcode_into_libs=yes
+ ;;
+
+hpux9* | hpux10* | hpux11*)
+ # Give a soname corresponding to the major version so that dld.sl refuses to
+ # link against other versions.
+ version_type=sunos
+ need_lib_prefix=no
+ need_version=no
+ case $host_cpu in
+ ia64*)
+ shrext_cmds='.so'
+ hardcode_into_libs=yes
+ dynamic_linker="$host_os dld.so"
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ if test "X$HPUX_IA64_MODE" = X32; then
+ sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
+ else
+ sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
+ fi
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ ;;
+ hppa*64*)
+ shrext_cmds='.sl'
+ hardcode_into_libs=yes
+ dynamic_linker="$host_os dld.sl"
+ shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
+ shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ ;;
+ *)
+ shrext_cmds='.sl'
+ dynamic_linker="$host_os dld.sl"
+ shlibpath_var=SHLIB_PATH
+ shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ ;;
+ esac
+ # HP-UX runs *really* slowly unless shared libraries are mode 555.
+ postinstall_cmds='chmod 555 $lib'
+ ;;
+
+interix[3-9]*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+
+irix5* | irix6* | nonstopux*)
+ case $host_os in
+ nonstopux*) version_type=nonstopux ;;
+ *)
+ if test "$lt_cv_prog_gnu_ld" = yes; then
+ version_type=linux
+ else
+ version_type=irix
+ fi ;;
+ esac
+ need_lib_prefix=no
+ need_version=no
+ soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}'
+ case $host_os in
+ irix5* | nonstopux*)
+ libsuff= shlibsuff=
+ ;;
+ *)
+ case $LD in # libtool.m4 will add one of these switches to LD
+ *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
+ libsuff= shlibsuff= libmagic=32-bit;;
+ *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
+ libsuff=32 shlibsuff=N32 libmagic=N32;;
+ *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
+ libsuff=64 shlibsuff=64 libmagic=64-bit;;
+ *) libsuff= shlibsuff= libmagic=never-match;;
+ esac
+ ;;
+ esac
+ shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+ shlibpath_overrides_runpath=no
+ sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}"
+ sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}"
+ hardcode_into_libs=yes
+ ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux*oldld* | linux*aout* | linux*coff*)
+ dynamic_linker=no
+ ;;
+
+# This must be Linux ELF.
+linux* | k*bsd*-gnu)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ # Some binutils ld are patched to set DT_RUNPATH
+ save_LDFLAGS=$LDFLAGS
+ save_libdir=$libdir
+ eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \
+ LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\""
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then :
+ shlibpath_overrides_runpath=yes
+fi
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LDFLAGS=$save_LDFLAGS
+ libdir=$save_libdir
+
+ # This implies no fast_install, which is unacceptable.
+ # Some rework will be needed to allow for fast_install
+ # before this can be enabled.
+ hardcode_into_libs=yes
+
+ # Append ld.so.conf contents to the search path
+ if test -f /etc/ld.so.conf; then
+ lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '`
+ sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
+ fi
+
+ # We used to test for /lib/ld.so.1 and disable shared libraries on
+ # powerpc, because MkLinux only supported shared libraries with the
+ # GNU dynamic linker. Since this was broken with cross compilers,
+ # most powerpc-linux boxes support dynamic linking these days and
+ # people can always --disable-shared, the test was removed, and we
+ # assume the GNU/Linux dynamic linker is in use.
+ dynamic_linker='GNU/Linux ld.so'
+ ;;
+
+netbsd*)
+ version_type=sunos
+ need_lib_prefix=no
+ need_version=no
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ dynamic_linker='NetBSD (a.out) ld.so'
+ else
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ dynamic_linker='NetBSD ld.elf_so'
+ fi
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+
+newsos6)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ ;;
+
+*nto* | *qnx*)
+ version_type=qnx
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ dynamic_linker='ldqnx.so'
+ ;;
+
+openbsd*)
+ version_type=sunos
+ sys_lib_dlsearch_path_spec="/usr/lib"
+ need_lib_prefix=no
+ # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs.
+ case $host_os in
+ openbsd3.3 | openbsd3.3.*) need_version=yes ;;
+ *) need_version=no ;;
+ esac
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+ case $host_os in
+ openbsd2.[89] | openbsd2.[89].*)
+ shlibpath_overrides_runpath=no
+ ;;
+ *)
+ shlibpath_overrides_runpath=yes
+ ;;
+ esac
+ else
+ shlibpath_overrides_runpath=yes
+ fi
+ ;;
+
+os2*)
+ libname_spec='$name'
+ shrext_cmds=".dll"
+ need_lib_prefix=no
+ library_names_spec='$libname${shared_ext} $libname.a'
+ dynamic_linker='OS/2 ld.exe'
+ shlibpath_var=LIBPATH
+ ;;
+
+osf3* | osf4* | osf5*)
+ version_type=osf
+ need_lib_prefix=no
+ need_version=no
+ soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+ sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec"
+ ;;
+
+rdos*)
+ dynamic_linker=no
+ ;;
+
+solaris*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ # ldd complains unless libraries are executable
+ postinstall_cmds='chmod +x $lib'
+ ;;
+
+sunos4*)
+ version_type=sunos
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ if test "$with_gnu_ld" = yes; then
+ need_lib_prefix=no
+ fi
+ need_version=yes
+ ;;
+
+sysv4 | sysv4.3*)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ case $host_vendor in
+ sni)
+ shlibpath_overrides_runpath=no
+ need_lib_prefix=no
+ runpath_var=LD_RUN_PATH
+ ;;
+ siemens)
+ need_lib_prefix=no
+ ;;
+ motorola)
+ need_lib_prefix=no
+ need_version=no
+ shlibpath_overrides_runpath=no
+ sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+ ;;
+ esac
+ ;;
+
+sysv4*MP*)
+ if test -d /usr/nec ;then
+ version_type=linux
+ library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}'
+ soname_spec='$libname${shared_ext}.$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ fi
+ ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+ version_type=freebsd-elf
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ if test "$with_gnu_ld" = yes; then
+ sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib'
+ else
+ sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'
+ case $host_os in
+ sco3.2v5*)
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /lib"
+ ;;
+ esac
+ fi
+ sys_lib_dlsearch_path_spec='/usr/lib'
+ ;;
+
+tpf*)
+ # TPF is a cross-target only. Preferred cross-host = GNU/Linux.
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+
+uts4*)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+*)
+ dynamic_linker=no
+ ;;
+esac
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5
+$as_echo "$dynamic_linker" >&6; }
+test "$dynamic_linker" = no && can_build_shared=no
+
+variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
+if test "$GCC" = yes; then
+ variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
+fi
+
+if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then
+ sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec"
+fi
+if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then
+ sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec"
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5
+$as_echo_n "checking how to hardcode library paths into programs... " >&6; }
+hardcode_action=
+if test -n "$hardcode_libdir_flag_spec" ||
+ test -n "$runpath_var" ||
+ test "X$hardcode_automatic" = "Xyes" ; then
+
+ # We can hardcode non-existent directories.
+ if test "$hardcode_direct" != no &&
+ # If the only mechanism to avoid hardcoding is shlibpath_var, we
+ # have to relink, otherwise we might link with an installed library
+ # when we should be linking with a yet-to-be-installed one
+ ## test "$_LT_TAGVAR(hardcode_shlibpath_var, )" != no &&
+ test "$hardcode_minus_L" != no; then
+ # Linking always hardcodes the temporary library directory.
+ hardcode_action=relink
+ else
+ # We can link without hardcoding, and we can hardcode nonexisting dirs.
+ hardcode_action=immediate
+ fi
+else
+ # We cannot hardcode anything, or else we can only hardcode existing
+ # directories.
+ hardcode_action=unsupported
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5
+$as_echo "$hardcode_action" >&6; }
+
+if test "$hardcode_action" = relink ||
+ test "$inherit_rpath" = yes; then
+ # Fast installation is not supported
+ enable_fast_install=no
+elif test "$shlibpath_overrides_runpath" = yes ||
+ test "$enable_shared" = no; then
+ # Fast installation is not necessary
+ enable_fast_install=needless
+fi
+
+
+
+
+
+
+ if test "x$enable_dlopen" != xyes; then
+ enable_dlopen=unknown
+ enable_dlopen_self=unknown
+ enable_dlopen_self_static=unknown
+else
+ lt_cv_dlopen=no
+ lt_cv_dlopen_libs=
+
+ case $host_os in
+ beos*)
+ lt_cv_dlopen="load_add_on"
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+ ;;
+
+ mingw* | pw32* | cegcc*)
+ lt_cv_dlopen="LoadLibrary"
+ lt_cv_dlopen_libs=
+ ;;
+
+ cygwin*)
+ lt_cv_dlopen="dlopen"
+ lt_cv_dlopen_libs=
+ ;;
+
+ darwin*)
+ # if libdl is installed we need to link against it
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
+$as_echo_n "checking for dlopen in -ldl... " >&6; }
+if ${ac_cv_lib_dl_dlopen+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_dl_dlopen=yes
+else
+ ac_cv_lib_dl_dlopen=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
+$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
+if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
+ lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"
+else
+
+ lt_cv_dlopen="dyld"
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+
+fi
+
+ ;;
+
+ *)
+ ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load"
+if test "x$ac_cv_func_shl_load" = xyes; then :
+ lt_cv_dlopen="shl_load"
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5
+$as_echo_n "checking for shl_load in -ldld... " >&6; }
+if ${ac_cv_lib_dld_shl_load+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char shl_load ();
+int
+main ()
+{
+return shl_load ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_dld_shl_load=yes
+else
+ ac_cv_lib_dld_shl_load=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5
+$as_echo "$ac_cv_lib_dld_shl_load" >&6; }
+if test "x$ac_cv_lib_dld_shl_load" = xyes; then :
+ lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"
+else
+ ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen"
+if test "x$ac_cv_func_dlopen" = xyes; then :
+ lt_cv_dlopen="dlopen"
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
+$as_echo_n "checking for dlopen in -ldl... " >&6; }
+if ${ac_cv_lib_dl_dlopen+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_dl_dlopen=yes
+else
+ ac_cv_lib_dl_dlopen=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
+$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
+if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
+ lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5
+$as_echo_n "checking for dlopen in -lsvld... " >&6; }
+if ${ac_cv_lib_svld_dlopen+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsvld $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_svld_dlopen=yes
+else
+ ac_cv_lib_svld_dlopen=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5
+$as_echo "$ac_cv_lib_svld_dlopen" >&6; }
+if test "x$ac_cv_lib_svld_dlopen" = xyes; then :
+ lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5
+$as_echo_n "checking for dld_link in -ldld... " >&6; }
+if ${ac_cv_lib_dld_dld_link+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dld_link ();
+int
+main ()
+{
+return dld_link ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_dld_dld_link=yes
+else
+ ac_cv_lib_dld_dld_link=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5
+$as_echo "$ac_cv_lib_dld_dld_link" >&6; }
+if test "x$ac_cv_lib_dld_dld_link" = xyes; then :
+ lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+ ;;
+ esac
+
+ if test "x$lt_cv_dlopen" != xno; then
+ enable_dlopen=yes
+ else
+ enable_dlopen=no
+ fi
+
+ case $lt_cv_dlopen in
+ dlopen)
+ save_CPPFLAGS="$CPPFLAGS"
+ test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+
+ save_LDFLAGS="$LDFLAGS"
+ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
+
+ save_LIBS="$LIBS"
+ LIBS="$lt_cv_dlopen_libs $LIBS"
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5
+$as_echo_n "checking whether a program can dlopen itself... " >&6; }
+if ${lt_cv_dlopen_self+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then :
+ lt_cv_dlopen_self=cross
+else
+ lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+ lt_status=$lt_dlunknown
+ cat > conftest.$ac_ext <<_LT_EOF
+#line __oline__ "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+# define LT_DLGLOBAL RTLD_GLOBAL
+#else
+# ifdef DL_GLOBAL
+# define LT_DLGLOBAL DL_GLOBAL
+# else
+# define LT_DLGLOBAL 0
+# endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+ find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+# ifdef RTLD_LAZY
+# define LT_DLLAZY_OR_NOW RTLD_LAZY
+# else
+# ifdef DL_LAZY
+# define LT_DLLAZY_OR_NOW DL_LAZY
+# else
+# ifdef RTLD_NOW
+# define LT_DLLAZY_OR_NOW RTLD_NOW
+# else
+# ifdef DL_NOW
+# define LT_DLLAZY_OR_NOW DL_NOW
+# else
+# define LT_DLLAZY_OR_NOW 0
+# endif
+# endif
+# endif
+# endif
+#endif
+
+void fnord() { int i=42;}
+int main ()
+{
+ void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+ int status = $lt_dlunknown;
+
+ if (self)
+ {
+ if (dlsym (self,"fnord")) status = $lt_dlno_uscore;
+ else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+ /* dlclose (self); */
+ }
+ else
+ puts (dlerror ());
+
+ return status;
+}
+_LT_EOF
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then
+ (./conftest; exit; ) >&5 2>/dev/null
+ lt_status=$?
+ case x$lt_status in
+ x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;;
+ x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;;
+ x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;;
+ esac
+ else :
+ # compilation failed
+ lt_cv_dlopen_self=no
+ fi
+fi
+rm -fr conftest*
+
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5
+$as_echo "$lt_cv_dlopen_self" >&6; }
+
+ if test "x$lt_cv_dlopen_self" = xyes; then
+ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5
+$as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; }
+if ${lt_cv_dlopen_self_static+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then :
+ lt_cv_dlopen_self_static=cross
+else
+ lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+ lt_status=$lt_dlunknown
+ cat > conftest.$ac_ext <<_LT_EOF
+#line __oline__ "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+# define LT_DLGLOBAL RTLD_GLOBAL
+#else
+# ifdef DL_GLOBAL
+# define LT_DLGLOBAL DL_GLOBAL
+# else
+# define LT_DLGLOBAL 0
+# endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+ find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+# ifdef RTLD_LAZY
+# define LT_DLLAZY_OR_NOW RTLD_LAZY
+# else
+# ifdef DL_LAZY
+# define LT_DLLAZY_OR_NOW DL_LAZY
+# else
+# ifdef RTLD_NOW
+# define LT_DLLAZY_OR_NOW RTLD_NOW
+# else
+# ifdef DL_NOW
+# define LT_DLLAZY_OR_NOW DL_NOW
+# else
+# define LT_DLLAZY_OR_NOW 0
+# endif
+# endif
+# endif
+# endif
+#endif
+
+void fnord() { int i=42;}
+int main ()
+{
+ void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+ int status = $lt_dlunknown;
+
+ if (self)
+ {
+ if (dlsym (self,"fnord")) status = $lt_dlno_uscore;
+ else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+ /* dlclose (self); */
+ }
+ else
+ puts (dlerror ());
+
+ return status;
+}
+_LT_EOF
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then
+ (./conftest; exit; ) >&5 2>/dev/null
+ lt_status=$?
+ case x$lt_status in
+ x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;;
+ x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;;
+ x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;;
+ esac
+ else :
+ # compilation failed
+ lt_cv_dlopen_self_static=no
+ fi
+fi
+rm -fr conftest*
+
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5
+$as_echo "$lt_cv_dlopen_self_static" >&6; }
+ fi
+
+ CPPFLAGS="$save_CPPFLAGS"
+ LDFLAGS="$save_LDFLAGS"
+ LIBS="$save_LIBS"
+ ;;
+ esac
+
+ case $lt_cv_dlopen_self in
+ yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+ *) enable_dlopen_self=unknown ;;
+ esac
+
+ case $lt_cv_dlopen_self_static in
+ yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+ *) enable_dlopen_self_static=unknown ;;
+ esac
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+striplib=
+old_striplib=
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5
+$as_echo_n "checking whether stripping libraries is possible... " >&6; }
+if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then
+ test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
+ test -z "$striplib" && striplib="$STRIP --strip-unneeded"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+# FIXME - insert some real tests, host_os isn't really good enough
+ case $host_os in
+ darwin*)
+ if test -n "$STRIP" ; then
+ striplib="$STRIP -x"
+ old_striplib="$STRIP -S"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ fi
+ ;;
+ *)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ ;;
+ esac
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+ # Report which library types will actually be built
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5
+$as_echo_n "checking if libtool supports shared libraries... " >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5
+$as_echo "$can_build_shared" >&6; }
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5
+$as_echo_n "checking whether to build shared libraries... " >&6; }
+ test "$can_build_shared" = "no" && enable_shared=no
+
+ # On AIX, shared libraries and static libraries use the same namespace, and
+ # are all built from PIC.
+ case $host_os in
+ aix3*)
+ test "$enable_shared" = yes && enable_static=no
+ if test -n "$RANLIB"; then
+ archive_cmds="$archive_cmds~\$RANLIB \$lib"
+ postinstall_cmds='$RANLIB $lib'
+ fi
+ ;;
+
+ aix[4-9]*)
+ if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+ test "$enable_shared" = yes && enable_static=no
+ fi
+ ;;
+ esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5
+$as_echo "$enable_shared" >&6; }
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5
+$as_echo_n "checking whether to build static libraries... " >&6; }
+ # Make sure either enable_shared or enable_static is yes.
+ test "$enable_shared" = yes || enable_static=yes
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5
+$as_echo "$enable_static" >&6; }
+
+
+
+
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+CC="$lt_save_CC"
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ac_config_commands="$ac_config_commands libtool"
+
+
+
+
+# Only expand once:
+
+
+
+# Extract the first word of "ar", so it can be a program name with args.
+set dummy ar; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_AR+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $AR in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_AR="$AR" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_AR="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_AR" && ac_cv_path_AR="ar"
+ ;;
+esac
+fi
+AR=$ac_cv_path_AR
+if test -n "$AR"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5
+$as_echo "$AR" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+# Extract the first word of "rm", so it can be a program name with args.
+set dummy rm; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_RM+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $RM in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_RM="$RM" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_RM="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_RM" && ac_cv_path_RM="rm"
+ ;;
+esac
+fi
+RM=$ac_cv_path_RM
+if test -n "$RM"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RM" >&5
+$as_echo "$RM" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+# Extract the first word of "cp", so it can be a program name with args.
+set dummy cp; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_CP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $CP in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_CP="$CP" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_CP="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_CP" && ac_cv_path_CP="cp"
+ ;;
+esac
+fi
+CP=$ac_cv_path_CP
+if test -n "$CP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CP" >&5
+$as_echo "$CP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+# Extract the first word of "ln", so it can be a program name with args.
+set dummy ln; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_LN+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $LN in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_LN="$LN" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_LN="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_LN" && ac_cv_path_LN="ln"
+ ;;
+esac
+fi
+LN=$ac_cv_path_LN
+if test -n "$LN"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LN" >&5
+$as_echo "$LN" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+# Extract the first word of "sed", so it can be a program name with args.
+set dummy sed; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_SED+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $SED in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_SED="$SED" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_SED="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_SED" && ac_cv_path_SED="sed"
+ ;;
+esac
+fi
+SED=$ac_cv_path_SED
+if test -n "$SED"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SED" >&5
+$as_echo "$SED" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+# Extract the first word of "make", so it can be a program name with args.
+set dummy make; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_MAKE+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $MAKE in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_MAKE="$MAKE" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_MAKE="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+MAKE=$ac_cv_path_MAKE
+if test -n "$MAKE"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAKE" >&5
+$as_echo "$MAKE" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether NLS is requested" >&5
+$as_echo_n "checking whether NLS is requested... " >&6; }
+ @%:@ Check whether --enable-nls was given.
+if test "${enable_nls+set}" = set; then :
+ enableval=$enable_nls; USE_NLS=$enableval
+else
+ USE_NLS=yes
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $USE_NLS" >&5
+$as_echo "$USE_NLS" >&6; }
+
+
+
+
+
+
+# Prepare PATH_SEPARATOR.
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+# Find out how to test for executable files. Don't use a zero-byte file,
+# as systems may use methods other than mode bits to determine executability.
+cat >conf$$.file <<_ASEOF
+#! /bin/sh
+exit 0
+_ASEOF
+chmod +x conf$$.file
+if test -x conf$$.file >/dev/null 2>&1; then
+ ac_executable_p="test -x"
+else
+ ac_executable_p="test -f"
+fi
+rm -f conf$$.file
+
+# Extract the first word of "msgfmt", so it can be a program name with args.
+set dummy msgfmt; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_MSGFMT+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case "$MSGFMT" in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_MSGFMT="$MSGFMT" # Let the user override the test with a path.
+ ;;
+ *)
+ ac_save_IFS="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH; do
+ IFS="$ac_save_IFS"
+ test -z "$ac_dir" && ac_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $ac_executable_p "$ac_dir/$ac_word$ac_exec_ext"; then
+ echo "$as_me: trying $ac_dir/$ac_word..." >&5
+ if $ac_dir/$ac_word --statistics /dev/null >&5 2>&1 &&
+ (if $ac_dir/$ac_word --statistics /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi); then
+ ac_cv_path_MSGFMT="$ac_dir/$ac_word$ac_exec_ext"
+ break 2
+ fi
+ fi
+ done
+ done
+ IFS="$ac_save_IFS"
+ test -z "$ac_cv_path_MSGFMT" && ac_cv_path_MSGFMT=":"
+ ;;
+esac
+fi
+MSGFMT="$ac_cv_path_MSGFMT"
+if test "$MSGFMT" != ":"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MSGFMT" >&5
+$as_echo "$MSGFMT" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ # Extract the first word of "gmsgfmt", so it can be a program name with args.
+set dummy gmsgfmt; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_GMSGFMT+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $GMSGFMT in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_GMSGFMT="$GMSGFMT" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_GMSGFMT="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_GMSGFMT" && ac_cv_path_GMSGFMT="$MSGFMT"
+ ;;
+esac
+fi
+GMSGFMT=$ac_cv_path_GMSGFMT
+if test -n "$GMSGFMT"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $GMSGFMT" >&5
+$as_echo "$GMSGFMT" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+ case `$MSGFMT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in
+ '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) MSGFMT_015=: ;;
+ *) MSGFMT_015=$MSGFMT ;;
+ esac
+
+ case `$GMSGFMT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in
+ '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) GMSGFMT_015=: ;;
+ *) GMSGFMT_015=$GMSGFMT ;;
+ esac
+
+
+
+# Prepare PATH_SEPARATOR.
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+# Find out how to test for executable files. Don't use a zero-byte file,
+# as systems may use methods other than mode bits to determine executability.
+cat >conf$$.file <<_ASEOF
+#! /bin/sh
+exit 0
+_ASEOF
+chmod +x conf$$.file
+if test -x conf$$.file >/dev/null 2>&1; then
+ ac_executable_p="test -x"
+else
+ ac_executable_p="test -f"
+fi
+rm -f conf$$.file
+
+# Extract the first word of "xgettext", so it can be a program name with args.
+set dummy xgettext; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_XGETTEXT+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case "$XGETTEXT" in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_XGETTEXT="$XGETTEXT" # Let the user override the test with a path.
+ ;;
+ *)
+ ac_save_IFS="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH; do
+ IFS="$ac_save_IFS"
+ test -z "$ac_dir" && ac_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $ac_executable_p "$ac_dir/$ac_word$ac_exec_ext"; then
+ echo "$as_me: trying $ac_dir/$ac_word..." >&5
+ if $ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= /dev/null >&5 2>&1 &&
+ (if $ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi); then
+ ac_cv_path_XGETTEXT="$ac_dir/$ac_word$ac_exec_ext"
+ break 2
+ fi
+ fi
+ done
+ done
+ IFS="$ac_save_IFS"
+ test -z "$ac_cv_path_XGETTEXT" && ac_cv_path_XGETTEXT=":"
+ ;;
+esac
+fi
+XGETTEXT="$ac_cv_path_XGETTEXT"
+if test "$XGETTEXT" != ":"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $XGETTEXT" >&5
+$as_echo "$XGETTEXT" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ rm -f messages.po
+
+ case `$XGETTEXT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in
+ '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) XGETTEXT_015=: ;;
+ *) XGETTEXT_015=$XGETTEXT ;;
+ esac
+
+
+
+# Prepare PATH_SEPARATOR.
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+# Find out how to test for executable files. Don't use a zero-byte file,
+# as systems may use methods other than mode bits to determine executability.
+cat >conf$$.file <<_ASEOF
+#! /bin/sh
+exit 0
+_ASEOF
+chmod +x conf$$.file
+if test -x conf$$.file >/dev/null 2>&1; then
+ ac_executable_p="test -x"
+else
+ ac_executable_p="test -f"
+fi
+rm -f conf$$.file
+
+# Extract the first word of "msgmerge", so it can be a program name with args.
+set dummy msgmerge; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_MSGMERGE+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case "$MSGMERGE" in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_MSGMERGE="$MSGMERGE" # Let the user override the test with a path.
+ ;;
+ *)
+ ac_save_IFS="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH; do
+ IFS="$ac_save_IFS"
+ test -z "$ac_dir" && ac_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $ac_executable_p "$ac_dir/$ac_word$ac_exec_ext"; then
+ echo "$as_me: trying $ac_dir/$ac_word..." >&5
+ if $ac_dir/$ac_word --update -q /dev/null /dev/null >&5 2>&1; then
+ ac_cv_path_MSGMERGE="$ac_dir/$ac_word$ac_exec_ext"
+ break 2
+ fi
+ fi
+ done
+ done
+ IFS="$ac_save_IFS"
+ test -z "$ac_cv_path_MSGMERGE" && ac_cv_path_MSGMERGE=":"
+ ;;
+esac
+fi
+MSGMERGE="$ac_cv_path_MSGMERGE"
+if test "$MSGMERGE" != ":"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MSGMERGE" >&5
+$as_echo "$MSGMERGE" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$localedir" || localedir='${datadir}/locale'
+
+
+ ac_config_commands="$ac_config_commands po-directories"
+
+
+
+ if test "X$prefix" = "XNONE"; then
+ acl_final_prefix="$ac_default_prefix"
+ else
+ acl_final_prefix="$prefix"
+ fi
+ if test "X$exec_prefix" = "XNONE"; then
+ acl_final_exec_prefix='${prefix}'
+ else
+ acl_final_exec_prefix="$exec_prefix"
+ fi
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ eval acl_final_exec_prefix=\"$acl_final_exec_prefix\"
+ prefix="$acl_save_prefix"
+
+
+@%:@ Check whether --with-gnu-ld was given.
+if test "${with_gnu_ld+set}" = set; then :
+ withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes
+else
+ with_gnu_ld=no
+fi
+
+# Prepare PATH_SEPARATOR.
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+ac_prog=ld
+if test "$GCC" = yes; then
+ # Check if gcc -print-prog-name=ld gives a path.
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by GCC" >&5
+$as_echo_n "checking for ld used by GCC... " >&6; }
+ case $host in
+ *-*-mingw*)
+ # gcc leaves a trailing carriage return which upsets mingw
+ ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+ *)
+ ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+ esac
+ case $ac_prog in
+ # Accept absolute paths.
+ [\\/]* | [A-Za-z]:[\\/]*)
+ re_direlt='/[^/][^/]*/\.\./'
+ # Canonicalize the path of ld
+ ac_prog=`echo $ac_prog| sed 's%\\\\%/%g'`
+ while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do
+ ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"`
+ done
+ test -z "$LD" && LD="$ac_prog"
+ ;;
+ "")
+ # If it fails, then pretend we aren't using GCC.
+ ac_prog=ld
+ ;;
+ *)
+ # If it is relative, then search for the first ld in PATH.
+ with_gnu_ld=unknown
+ ;;
+ esac
+elif test "$with_gnu_ld" = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5
+$as_echo_n "checking for GNU ld... " >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5
+$as_echo_n "checking for non-GNU ld... " >&6; }
+fi
+if ${acl_cv_path_LD+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$LD"; then
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR-:}"
+ for ac_dir in $PATH; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+ acl_cv_path_LD="$ac_dir/$ac_prog"
+ # Check to see if the program is GNU ld. I'd rather use --version,
+ # but apparently some GNU ld's only accept -v.
+ # Break only if it was the GNU/non-GNU ld that we prefer.
+ case `"$acl_cv_path_LD" -v 2>&1 < /dev/null` in
+ *GNU* | *'with BFD'*)
+ test "$with_gnu_ld" != no && break ;;
+ *)
+ test "$with_gnu_ld" != yes && break ;;
+ esac
+ fi
+ done
+ IFS="$ac_save_ifs"
+else
+ acl_cv_path_LD="$LD" # Let the user override the test with a path.
+fi
+fi
+
+LD="$acl_cv_path_LD"
+if test -n "$LD"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LD" >&5
+$as_echo "$LD" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5
+$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; }
+if ${acl_cv_prog_gnu_ld+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ # I'd rather use --version here, but apparently some GNU ld's only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+ acl_cv_prog_gnu_ld=yes ;;
+*)
+ acl_cv_prog_gnu_ld=no ;;
+esac
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $acl_cv_prog_gnu_ld" >&5
+$as_echo "$acl_cv_prog_gnu_ld" >&6; }
+with_gnu_ld=$acl_cv_prog_gnu_ld
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shared library run path origin" >&5
+$as_echo_n "checking for shared library run path origin... " >&6; }
+if ${acl_cv_rpath+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ CC="$CC" GCC="$GCC" LDFLAGS="$LDFLAGS" LD="$LD" with_gnu_ld="$with_gnu_ld" \
+ ${CONFIG_SHELL-/bin/sh} "$ac_aux_dir/config.rpath" "$host" > conftest.sh
+ . ./conftest.sh
+ rm -f ./conftest.sh
+ acl_cv_rpath=done
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $acl_cv_rpath" >&5
+$as_echo "$acl_cv_rpath" >&6; }
+ wl="$acl_cv_wl"
+ libext="$acl_cv_libext"
+ shlibext="$acl_cv_shlibext"
+ hardcode_libdir_flag_spec="$acl_cv_hardcode_libdir_flag_spec"
+ hardcode_libdir_separator="$acl_cv_hardcode_libdir_separator"
+ hardcode_direct="$acl_cv_hardcode_direct"
+ hardcode_minus_L="$acl_cv_hardcode_minus_L"
+ @%:@ Check whether --enable-rpath was given.
+if test "${enable_rpath+set}" = set; then :
+ enableval=$enable_rpath; :
+else
+ enable_rpath=yes
+fi
+
+
+
+ acl_libdirstem=lib
+ searchpath=`(LC_ALL=C $CC -print-search-dirs) 2>/dev/null | sed -n -e 's,^libraries: ,,p' | sed -e 's,^=,,'`
+ if test -n "$searchpath"; then
+ acl_save_IFS="${IFS= }"; IFS=":"
+ for searchdir in $searchpath; do
+ if test -d "$searchdir"; then
+ case "$searchdir" in
+ */lib64/ | */lib64 ) acl_libdirstem=lib64 ;;
+ *) searchdir=`cd "$searchdir" && pwd`
+ case "$searchdir" in
+ */lib64 ) acl_libdirstem=lib64 ;;
+ esac ;;
+ esac
+ fi
+ done
+ IFS="$acl_save_IFS"
+ fi
+
+
+
+
+
+
+
+
+ use_additional=yes
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+
+@%:@ Check whether --with-libiconv-prefix was given.
+if test "${with_libiconv_prefix+set}" = set; then :
+ withval=$with_libiconv_prefix;
+ if test "X$withval" = "Xno"; then
+ use_additional=no
+ else
+ if test "X$withval" = "X"; then
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ else
+ additional_includedir="$withval/include"
+ additional_libdir="$withval/$acl_libdirstem"
+ fi
+ fi
+
+fi
+
+ LIBICONV=
+ LTLIBICONV=
+ INCICONV=
+ rpathdirs=
+ ltrpathdirs=
+ names_already_handled=
+ names_next_round='iconv '
+ while test -n "$names_next_round"; do
+ names_this_round="$names_next_round"
+ names_next_round=
+ for name in $names_this_round; do
+ already_handled=
+ for n in $names_already_handled; do
+ if test "$n" = "$name"; then
+ already_handled=yes
+ break
+ fi
+ done
+ if test -z "$already_handled"; then
+ names_already_handled="$names_already_handled $name"
+ uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'`
+ eval value=\"\$HAVE_LIB$uppername\"
+ if test -n "$value"; then
+ if test "$value" = yes; then
+ eval value=\"\$LIB$uppername\"
+ test -z "$value" || LIBICONV="${LIBICONV}${LIBICONV:+ }$value"
+ eval value=\"\$LTLIB$uppername\"
+ test -z "$value" || LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }$value"
+ else
+ :
+ fi
+ else
+ found_dir=
+ found_la=
+ found_so=
+ found_a=
+ if test $use_additional = yes; then
+ if test -n "$shlibext" \
+ && { test -f "$additional_libdir/lib$name.$shlibext" \
+ || { test "$shlibext" = dll \
+ && test -f "$additional_libdir/lib$name.dll.a"; }; }; then
+ found_dir="$additional_libdir"
+ if test -f "$additional_libdir/lib$name.$shlibext"; then
+ found_so="$additional_libdir/lib$name.$shlibext"
+ else
+ found_so="$additional_libdir/lib$name.dll.a"
+ fi
+ if test -f "$additional_libdir/lib$name.la"; then
+ found_la="$additional_libdir/lib$name.la"
+ fi
+ else
+ if test -f "$additional_libdir/lib$name.$libext"; then
+ found_dir="$additional_libdir"
+ found_a="$additional_libdir/lib$name.$libext"
+ if test -f "$additional_libdir/lib$name.la"; then
+ found_la="$additional_libdir/lib$name.la"
+ fi
+ fi
+ fi
+ fi
+ if test "X$found_dir" = "X"; then
+ for x in $LDFLAGS $LTLIBICONV; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ case "$x" in
+ -L*)
+ dir=`echo "X$x" | sed -e 's/^X-L//'`
+ if test -n "$shlibext" \
+ && { test -f "$dir/lib$name.$shlibext" \
+ || { test "$shlibext" = dll \
+ && test -f "$dir/lib$name.dll.a"; }; }; then
+ found_dir="$dir"
+ if test -f "$dir/lib$name.$shlibext"; then
+ found_so="$dir/lib$name.$shlibext"
+ else
+ found_so="$dir/lib$name.dll.a"
+ fi
+ if test -f "$dir/lib$name.la"; then
+ found_la="$dir/lib$name.la"
+ fi
+ else
+ if test -f "$dir/lib$name.$libext"; then
+ found_dir="$dir"
+ found_a="$dir/lib$name.$libext"
+ if test -f "$dir/lib$name.la"; then
+ found_la="$dir/lib$name.la"
+ fi
+ fi
+ fi
+ ;;
+ esac
+ if test "X$found_dir" != "X"; then
+ break
+ fi
+ done
+ fi
+ if test "X$found_dir" != "X"; then
+ LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-L$found_dir -l$name"
+ if test "X$found_so" != "X"; then
+ if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/$acl_libdirstem"; then
+ LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so"
+ else
+ haveit=
+ for x in $ltrpathdirs; do
+ if test "X$x" = "X$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ ltrpathdirs="$ltrpathdirs $found_dir"
+ fi
+ if test "$hardcode_direct" = yes; then
+ LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so"
+ else
+ if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then
+ LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so"
+ haveit=
+ for x in $rpathdirs; do
+ if test "X$x" = "X$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ rpathdirs="$rpathdirs $found_dir"
+ fi
+ else
+ haveit=
+ for x in $LDFLAGS $LIBICONV; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-L$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ LIBICONV="${LIBICONV}${LIBICONV:+ }-L$found_dir"
+ fi
+ if test "$hardcode_minus_L" != no; then
+ LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so"
+ else
+ LIBICONV="${LIBICONV}${LIBICONV:+ }-l$name"
+ fi
+ fi
+ fi
+ fi
+ else
+ if test "X$found_a" != "X"; then
+ LIBICONV="${LIBICONV}${LIBICONV:+ }$found_a"
+ else
+ LIBICONV="${LIBICONV}${LIBICONV:+ }-L$found_dir -l$name"
+ fi
+ fi
+ additional_includedir=
+ case "$found_dir" in
+ */$acl_libdirstem | */$acl_libdirstem/)
+ basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem/"'*$,,'`
+ additional_includedir="$basedir/include"
+ ;;
+ esac
+ if test "X$additional_includedir" != "X"; then
+ if test "X$additional_includedir" != "X/usr/include"; then
+ haveit=
+ if test "X$additional_includedir" = "X/usr/local/include"; then
+ if test -n "$GCC"; then
+ case $host_os in
+ linux* | gnu* | k*bsd*-gnu) haveit=yes;;
+ esac
+ fi
+ fi
+ if test -z "$haveit"; then
+ for x in $CPPFLAGS $INCICONV; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-I$additional_includedir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_includedir"; then
+ INCICONV="${INCICONV}${INCICONV:+ }-I$additional_includedir"
+ fi
+ fi
+ fi
+ fi
+ fi
+ if test -n "$found_la"; then
+ save_libdir="$libdir"
+ case "$found_la" in
+ */* | *\\*) . "$found_la" ;;
+ *) . "./$found_la" ;;
+ esac
+ libdir="$save_libdir"
+ for dep in $dependency_libs; do
+ case "$dep" in
+ -L*)
+ additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'`
+ if test "X$additional_libdir" != "X/usr/$acl_libdirstem"; then
+ haveit=
+ if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem"; then
+ if test -n "$GCC"; then
+ case $host_os in
+ linux* | gnu* | k*bsd*-gnu) haveit=yes;;
+ esac
+ fi
+ fi
+ if test -z "$haveit"; then
+ haveit=
+ for x in $LDFLAGS $LIBICONV; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-L$additional_libdir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_libdir"; then
+ LIBICONV="${LIBICONV}${LIBICONV:+ }-L$additional_libdir"
+ fi
+ fi
+ haveit=
+ for x in $LDFLAGS $LTLIBICONV; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-L$additional_libdir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_libdir"; then
+ LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-L$additional_libdir"
+ fi
+ fi
+ fi
+ fi
+ ;;
+ -R*)
+ dir=`echo "X$dep" | sed -e 's/^X-R//'`
+ if test "$enable_rpath" != no; then
+ haveit=
+ for x in $rpathdirs; do
+ if test "X$x" = "X$dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ rpathdirs="$rpathdirs $dir"
+ fi
+ haveit=
+ for x in $ltrpathdirs; do
+ if test "X$x" = "X$dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ ltrpathdirs="$ltrpathdirs $dir"
+ fi
+ fi
+ ;;
+ -l*)
+ names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'`
+ ;;
+ *.la)
+ names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'`
+ ;;
+ *)
+ LIBICONV="${LIBICONV}${LIBICONV:+ }$dep"
+ LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }$dep"
+ ;;
+ esac
+ done
+ fi
+ else
+ LIBICONV="${LIBICONV}${LIBICONV:+ }-l$name"
+ LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-l$name"
+ fi
+ fi
+ fi
+ done
+ done
+ if test "X$rpathdirs" != "X"; then
+ if test -n "$hardcode_libdir_separator"; then
+ alldirs=
+ for found_dir in $rpathdirs; do
+ alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$found_dir"
+ done
+ acl_save_libdir="$libdir"
+ libdir="$alldirs"
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ libdir="$acl_save_libdir"
+ LIBICONV="${LIBICONV}${LIBICONV:+ }$flag"
+ else
+ for found_dir in $rpathdirs; do
+ acl_save_libdir="$libdir"
+ libdir="$found_dir"
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ libdir="$acl_save_libdir"
+ LIBICONV="${LIBICONV}${LIBICONV:+ }$flag"
+ done
+ fi
+ fi
+ if test "X$ltrpathdirs" != "X"; then
+ for found_dir in $ltrpathdirs; do
+ LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-R$found_dir"
+ done
+ fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for CFPreferencesCopyAppValue" >&5
+$as_echo_n "checking for CFPreferencesCopyAppValue... " >&6; }
+if ${gt_cv_func_CFPreferencesCopyAppValue+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ gt_save_LIBS="$LIBS"
+ LIBS="$LIBS -Wl,-framework -Wl,CoreFoundation"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <CoreFoundation/CFPreferences.h>
+int
+main ()
+{
+CFPreferencesCopyAppValue(NULL, NULL)
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ gt_cv_func_CFPreferencesCopyAppValue=yes
+else
+ gt_cv_func_CFPreferencesCopyAppValue=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LIBS="$gt_save_LIBS"
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gt_cv_func_CFPreferencesCopyAppValue" >&5
+$as_echo "$gt_cv_func_CFPreferencesCopyAppValue" >&6; }
+ if test $gt_cv_func_CFPreferencesCopyAppValue = yes; then
+
+$as_echo "@%:@define HAVE_CFPREFERENCESCOPYAPPVALUE 1" >>confdefs.h
+
+ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for CFLocaleCopyCurrent" >&5
+$as_echo_n "checking for CFLocaleCopyCurrent... " >&6; }
+if ${gt_cv_func_CFLocaleCopyCurrent+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ gt_save_LIBS="$LIBS"
+ LIBS="$LIBS -Wl,-framework -Wl,CoreFoundation"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <CoreFoundation/CFLocale.h>
+int
+main ()
+{
+CFLocaleCopyCurrent();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ gt_cv_func_CFLocaleCopyCurrent=yes
+else
+ gt_cv_func_CFLocaleCopyCurrent=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LIBS="$gt_save_LIBS"
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gt_cv_func_CFLocaleCopyCurrent" >&5
+$as_echo "$gt_cv_func_CFLocaleCopyCurrent" >&6; }
+ if test $gt_cv_func_CFLocaleCopyCurrent = yes; then
+
+$as_echo "@%:@define HAVE_CFLOCALECOPYCURRENT 1" >>confdefs.h
+
+ fi
+ INTL_MACOSX_LIBS=
+ if test $gt_cv_func_CFPreferencesCopyAppValue = yes || test $gt_cv_func_CFLocaleCopyCurrent = yes; then
+ INTL_MACOSX_LIBS="-Wl,-framework -Wl,CoreFoundation"
+ fi
+
+
+
+
+
+
+ LIBINTL=
+ LTLIBINTL=
+ POSUB=
+
+ case " $gt_needs " in
+ *" need-formatstring-macros "*) gt_api_version=3 ;;
+ *" need-ngettext "*) gt_api_version=2 ;;
+ *) gt_api_version=1 ;;
+ esac
+ gt_func_gnugettext_libc="gt_cv_func_gnugettext${gt_api_version}_libc"
+ gt_func_gnugettext_libintl="gt_cv_func_gnugettext${gt_api_version}_libintl"
+
+ if test "$USE_NLS" = "yes"; then
+ gt_use_preinstalled_gnugettext=no
+
+
+ if test $gt_api_version -ge 3; then
+ gt_revision_test_code='
+#ifndef __GNU_GETTEXT_SUPPORTED_REVISION
+#define __GNU_GETTEXT_SUPPORTED_REVISION(major) ((major) == 0 ? 0 : -1)
+#endif
+typedef int array [2 * (__GNU_GETTEXT_SUPPORTED_REVISION(0) >= 1) - 1];
+'
+ else
+ gt_revision_test_code=
+ fi
+ if test $gt_api_version -ge 2; then
+ gt_expression_test_code=' + * ngettext ("", "", 0)'
+ else
+ gt_expression_test_code=
+ fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU gettext in libc" >&5
+$as_echo_n "checking for GNU gettext in libc... " >&6; }
+if eval \${$gt_func_gnugettext_libc+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <libintl.h>
+$gt_revision_test_code
+extern int _nl_msg_cat_cntr;
+extern int *_nl_domain_bindings;
+int
+main ()
+{
+bindtextdomain ("", "");
+return * gettext ("")$gt_expression_test_code + _nl_msg_cat_cntr + *_nl_domain_bindings
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ eval "$gt_func_gnugettext_libc=yes"
+else
+ eval "$gt_func_gnugettext_libc=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+eval ac_res=\$$gt_func_gnugettext_libc
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+ if { eval "gt_val=\$$gt_func_gnugettext_libc"; test "$gt_val" != "yes"; }; then
+
+
+
+
+
+ am_save_CPPFLAGS="$CPPFLAGS"
+
+ for element in $INCICONV; do
+ haveit=
+ for x in $CPPFLAGS; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X$element"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }$element"
+ fi
+ done
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for iconv" >&5
+$as_echo_n "checking for iconv... " >&6; }
+if ${am_cv_func_iconv+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ am_cv_func_iconv="no, consider installing GNU libiconv"
+ am_cv_lib_iconv=no
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <iconv.h>
+int
+main ()
+{
+iconv_t cd = iconv_open("","");
+ iconv(cd,NULL,NULL,NULL,NULL);
+ iconv_close(cd);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ am_cv_func_iconv=yes
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ if test "$am_cv_func_iconv" != yes; then
+ am_save_LIBS="$LIBS"
+ LIBS="$LIBS $LIBICONV"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <iconv.h>
+int
+main ()
+{
+iconv_t cd = iconv_open("","");
+ iconv(cd,NULL,NULL,NULL,NULL);
+ iconv_close(cd);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ am_cv_lib_iconv=yes
+ am_cv_func_iconv=yes
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LIBS="$am_save_LIBS"
+ fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_func_iconv" >&5
+$as_echo "$am_cv_func_iconv" >&6; }
+ if test "$am_cv_func_iconv" = yes; then
+
+$as_echo "@%:@define HAVE_ICONV 1" >>confdefs.h
+
+ fi
+ if test "$am_cv_lib_iconv" = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link with libiconv" >&5
+$as_echo_n "checking how to link with libiconv... " >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIBICONV" >&5
+$as_echo "$LIBICONV" >&6; }
+ else
+ CPPFLAGS="$am_save_CPPFLAGS"
+ LIBICONV=
+ LTLIBICONV=
+ fi
+
+
+
+
+
+
+
+ use_additional=yes
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+
+@%:@ Check whether --with-libintl-prefix was given.
+if test "${with_libintl_prefix+set}" = set; then :
+ withval=$with_libintl_prefix;
+ if test "X$withval" = "Xno"; then
+ use_additional=no
+ else
+ if test "X$withval" = "X"; then
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ else
+ additional_includedir="$withval/include"
+ additional_libdir="$withval/$acl_libdirstem"
+ fi
+ fi
+
+fi
+
+ LIBINTL=
+ LTLIBINTL=
+ INCINTL=
+ rpathdirs=
+ ltrpathdirs=
+ names_already_handled=
+ names_next_round='intl '
+ while test -n "$names_next_round"; do
+ names_this_round="$names_next_round"
+ names_next_round=
+ for name in $names_this_round; do
+ already_handled=
+ for n in $names_already_handled; do
+ if test "$n" = "$name"; then
+ already_handled=yes
+ break
+ fi
+ done
+ if test -z "$already_handled"; then
+ names_already_handled="$names_already_handled $name"
+ uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'`
+ eval value=\"\$HAVE_LIB$uppername\"
+ if test -n "$value"; then
+ if test "$value" = yes; then
+ eval value=\"\$LIB$uppername\"
+ test -z "$value" || LIBINTL="${LIBINTL}${LIBINTL:+ }$value"
+ eval value=\"\$LTLIB$uppername\"
+ test -z "$value" || LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }$value"
+ else
+ :
+ fi
+ else
+ found_dir=
+ found_la=
+ found_so=
+ found_a=
+ if test $use_additional = yes; then
+ if test -n "$shlibext" \
+ && { test -f "$additional_libdir/lib$name.$shlibext" \
+ || { test "$shlibext" = dll \
+ && test -f "$additional_libdir/lib$name.dll.a"; }; }; then
+ found_dir="$additional_libdir"
+ if test -f "$additional_libdir/lib$name.$shlibext"; then
+ found_so="$additional_libdir/lib$name.$shlibext"
+ else
+ found_so="$additional_libdir/lib$name.dll.a"
+ fi
+ if test -f "$additional_libdir/lib$name.la"; then
+ found_la="$additional_libdir/lib$name.la"
+ fi
+ else
+ if test -f "$additional_libdir/lib$name.$libext"; then
+ found_dir="$additional_libdir"
+ found_a="$additional_libdir/lib$name.$libext"
+ if test -f "$additional_libdir/lib$name.la"; then
+ found_la="$additional_libdir/lib$name.la"
+ fi
+ fi
+ fi
+ fi
+ if test "X$found_dir" = "X"; then
+ for x in $LDFLAGS $LTLIBINTL; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ case "$x" in
+ -L*)
+ dir=`echo "X$x" | sed -e 's/^X-L//'`
+ if test -n "$shlibext" \
+ && { test -f "$dir/lib$name.$shlibext" \
+ || { test "$shlibext" = dll \
+ && test -f "$dir/lib$name.dll.a"; }; }; then
+ found_dir="$dir"
+ if test -f "$dir/lib$name.$shlibext"; then
+ found_so="$dir/lib$name.$shlibext"
+ else
+ found_so="$dir/lib$name.dll.a"
+ fi
+ if test -f "$dir/lib$name.la"; then
+ found_la="$dir/lib$name.la"
+ fi
+ else
+ if test -f "$dir/lib$name.$libext"; then
+ found_dir="$dir"
+ found_a="$dir/lib$name.$libext"
+ if test -f "$dir/lib$name.la"; then
+ found_la="$dir/lib$name.la"
+ fi
+ fi
+ fi
+ ;;
+ esac
+ if test "X$found_dir" != "X"; then
+ break
+ fi
+ done
+ fi
+ if test "X$found_dir" != "X"; then
+ LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }-L$found_dir -l$name"
+ if test "X$found_so" != "X"; then
+ if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/$acl_libdirstem"; then
+ LIBINTL="${LIBINTL}${LIBINTL:+ }$found_so"
+ else
+ haveit=
+ for x in $ltrpathdirs; do
+ if test "X$x" = "X$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ ltrpathdirs="$ltrpathdirs $found_dir"
+ fi
+ if test "$hardcode_direct" = yes; then
+ LIBINTL="${LIBINTL}${LIBINTL:+ }$found_so"
+ else
+ if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then
+ LIBINTL="${LIBINTL}${LIBINTL:+ }$found_so"
+ haveit=
+ for x in $rpathdirs; do
+ if test "X$x" = "X$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ rpathdirs="$rpathdirs $found_dir"
+ fi
+ else
+ haveit=
+ for x in $LDFLAGS $LIBINTL; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-L$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ LIBINTL="${LIBINTL}${LIBINTL:+ }-L$found_dir"
+ fi
+ if test "$hardcode_minus_L" != no; then
+ LIBINTL="${LIBINTL}${LIBINTL:+ }$found_so"
+ else
+ LIBINTL="${LIBINTL}${LIBINTL:+ }-l$name"
+ fi
+ fi
+ fi
+ fi
+ else
+ if test "X$found_a" != "X"; then
+ LIBINTL="${LIBINTL}${LIBINTL:+ }$found_a"
+ else
+ LIBINTL="${LIBINTL}${LIBINTL:+ }-L$found_dir -l$name"
+ fi
+ fi
+ additional_includedir=
+ case "$found_dir" in
+ */$acl_libdirstem | */$acl_libdirstem/)
+ basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem/"'*$,,'`
+ additional_includedir="$basedir/include"
+ ;;
+ esac
+ if test "X$additional_includedir" != "X"; then
+ if test "X$additional_includedir" != "X/usr/include"; then
+ haveit=
+ if test "X$additional_includedir" = "X/usr/local/include"; then
+ if test -n "$GCC"; then
+ case $host_os in
+ linux* | gnu* | k*bsd*-gnu) haveit=yes;;
+ esac
+ fi
+ fi
+ if test -z "$haveit"; then
+ for x in $CPPFLAGS $INCINTL; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-I$additional_includedir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_includedir"; then
+ INCINTL="${INCINTL}${INCINTL:+ }-I$additional_includedir"
+ fi
+ fi
+ fi
+ fi
+ fi
+ if test -n "$found_la"; then
+ save_libdir="$libdir"
+ case "$found_la" in
+ */* | *\\*) . "$found_la" ;;
+ *) . "./$found_la" ;;
+ esac
+ libdir="$save_libdir"
+ for dep in $dependency_libs; do
+ case "$dep" in
+ -L*)
+ additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'`
+ if test "X$additional_libdir" != "X/usr/$acl_libdirstem"; then
+ haveit=
+ if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem"; then
+ if test -n "$GCC"; then
+ case $host_os in
+ linux* | gnu* | k*bsd*-gnu) haveit=yes;;
+ esac
+ fi
+ fi
+ if test -z "$haveit"; then
+ haveit=
+ for x in $LDFLAGS $LIBINTL; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-L$additional_libdir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_libdir"; then
+ LIBINTL="${LIBINTL}${LIBINTL:+ }-L$additional_libdir"
+ fi
+ fi
+ haveit=
+ for x in $LDFLAGS $LTLIBINTL; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-L$additional_libdir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_libdir"; then
+ LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }-L$additional_libdir"
+ fi
+ fi
+ fi
+ fi
+ ;;
+ -R*)
+ dir=`echo "X$dep" | sed -e 's/^X-R//'`
+ if test "$enable_rpath" != no; then
+ haveit=
+ for x in $rpathdirs; do
+ if test "X$x" = "X$dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ rpathdirs="$rpathdirs $dir"
+ fi
+ haveit=
+ for x in $ltrpathdirs; do
+ if test "X$x" = "X$dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ ltrpathdirs="$ltrpathdirs $dir"
+ fi
+ fi
+ ;;
+ -l*)
+ names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'`
+ ;;
+ *.la)
+ names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'`
+ ;;
+ *)
+ LIBINTL="${LIBINTL}${LIBINTL:+ }$dep"
+ LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }$dep"
+ ;;
+ esac
+ done
+ fi
+ else
+ LIBINTL="${LIBINTL}${LIBINTL:+ }-l$name"
+ LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }-l$name"
+ fi
+ fi
+ fi
+ done
+ done
+ if test "X$rpathdirs" != "X"; then
+ if test -n "$hardcode_libdir_separator"; then
+ alldirs=
+ for found_dir in $rpathdirs; do
+ alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$found_dir"
+ done
+ acl_save_libdir="$libdir"
+ libdir="$alldirs"
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ libdir="$acl_save_libdir"
+ LIBINTL="${LIBINTL}${LIBINTL:+ }$flag"
+ else
+ for found_dir in $rpathdirs; do
+ acl_save_libdir="$libdir"
+ libdir="$found_dir"
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ libdir="$acl_save_libdir"
+ LIBINTL="${LIBINTL}${LIBINTL:+ }$flag"
+ done
+ fi
+ fi
+ if test "X$ltrpathdirs" != "X"; then
+ for found_dir in $ltrpathdirs; do
+ LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }-R$found_dir"
+ done
+ fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU gettext in libintl" >&5
+$as_echo_n "checking for GNU gettext in libintl... " >&6; }
+if eval \${$gt_func_gnugettext_libintl+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ gt_save_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $INCINTL"
+ gt_save_LIBS="$LIBS"
+ LIBS="$LIBS $LIBINTL"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <libintl.h>
+$gt_revision_test_code
+extern int _nl_msg_cat_cntr;
+extern
+#ifdef __cplusplus
+"C"
+#endif
+const char *_nl_expand_alias (const char *);
+int
+main ()
+{
+bindtextdomain ("", "");
+return * gettext ("")$gt_expression_test_code + _nl_msg_cat_cntr + *_nl_expand_alias ("")
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ eval "$gt_func_gnugettext_libintl=yes"
+else
+ eval "$gt_func_gnugettext_libintl=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" != yes; } && test -n "$LIBICONV"; then
+ LIBS="$LIBS $LIBICONV"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <libintl.h>
+$gt_revision_test_code
+extern int _nl_msg_cat_cntr;
+extern
+#ifdef __cplusplus
+"C"
+#endif
+const char *_nl_expand_alias (const char *);
+int
+main ()
+{
+bindtextdomain ("", "");
+return * gettext ("")$gt_expression_test_code + _nl_msg_cat_cntr + *_nl_expand_alias ("")
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ LIBINTL="$LIBINTL $LIBICONV"
+ LTLIBINTL="$LTLIBINTL $LTLIBICONV"
+ eval "$gt_func_gnugettext_libintl=yes"
+
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ fi
+ CPPFLAGS="$gt_save_CPPFLAGS"
+ LIBS="$gt_save_LIBS"
+fi
+eval ac_res=\$$gt_func_gnugettext_libintl
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ fi
+
+ if { eval "gt_val=\$$gt_func_gnugettext_libc"; test "$gt_val" = "yes"; } \
+ || { { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = "yes"; } \
+ && test "$PACKAGE" != gettext-runtime \
+ && test "$PACKAGE" != gettext-tools; }; then
+ gt_use_preinstalled_gnugettext=yes
+ else
+ LIBINTL=
+ LTLIBINTL=
+ INCINTL=
+ fi
+
+
+
+ if test -n "$INTL_MACOSX_LIBS"; then
+ if test "$gt_use_preinstalled_gnugettext" = "yes" \
+ || test "$nls_cv_use_gnu_gettext" = "yes"; then
+ LIBINTL="$LIBINTL $INTL_MACOSX_LIBS"
+ LTLIBINTL="$LTLIBINTL $INTL_MACOSX_LIBS"
+ fi
+ fi
+
+ if test "$gt_use_preinstalled_gnugettext" = "yes" \
+ || test "$nls_cv_use_gnu_gettext" = "yes"; then
+
+$as_echo "@%:@define ENABLE_NLS 1" >>confdefs.h
+
+ else
+ USE_NLS=no
+ fi
+ fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use NLS" >&5
+$as_echo_n "checking whether to use NLS... " >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $USE_NLS" >&5
+$as_echo "$USE_NLS" >&6; }
+ if test "$USE_NLS" = "yes"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking where the gettext function comes from" >&5
+$as_echo_n "checking where the gettext function comes from... " >&6; }
+ if test "$gt_use_preinstalled_gnugettext" = "yes"; then
+ if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = "yes"; }; then
+ gt_source="external libintl"
+ else
+ gt_source="libc"
+ fi
+ else
+ gt_source="included intl directory"
+ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gt_source" >&5
+$as_echo "$gt_source" >&6; }
+ fi
+
+ if test "$USE_NLS" = "yes"; then
+
+ if test "$gt_use_preinstalled_gnugettext" = "yes"; then
+ if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = "yes"; }; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link with libintl" >&5
+$as_echo_n "checking how to link with libintl... " >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIBINTL" >&5
+$as_echo "$LIBINTL" >&6; }
+
+ for element in $INCINTL; do
+ haveit=
+ for x in $CPPFLAGS; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X$element"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }$element"
+ fi
+ done
+
+ fi
+
+
+$as_echo "@%:@define HAVE_GETTEXT 1" >>confdefs.h
+
+
+$as_echo "@%:@define HAVE_DCGETTEXT 1" >>confdefs.h
+
+ fi
+
+ POSUB=po
+ fi
+
+
+
+ INTLLIBS="$LIBINTL"
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: dmalloc enabled" >&5
+$as_echo_n "checking option: dmalloc enabled... " >&6; }
+@%:@ Check whether --enable-dmalloc was given.
+if test "${enable_dmalloc+set}" = set; then :
+ enableval=$enable_dmalloc;
+fi
+
+if test x$enable_dmalloc = "xyes" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+@%:@ Check whether --with-dmalloc-dir was given.
+if test "${with_dmalloc_dir+set}" = set; then :
+ withval=$with_dmalloc_dir;
+ if test "x$withval" != "xno" ; then
+ enable_dmalloc = "yes"
+ CPPFLAGS="$CPPCFLAGS -I${withval}"
+ LDFLAGS="$LDFLAGS -L${withval}"
+ fi
+
+fi
+
+
+if test x$enable_dmalloc = "xyes" ; then
+
+$as_echo "@%:@define ENABLE_DMALLOC 1" >>confdefs.h
+
+fi
+
+localedir="\${datadir}/locale"
+
+@%:@ Check whether --with-localedir was given.
+if test "${with_localedir+set}" = set; then :
+ withval=$with_localedir;
+ case $withval in
+ yes)
+ ;;
+ no)
+ ;;
+ *)
+ localedir=$withval
+ ;;
+ esac
+
+fi
+
+localedir="$localedir"
+
+
+# Setup OS-Specific features
+case "$host" in
+ *darwin*)
+ @%:@ Check whether --enable-osx-universal-binaries was given.
+if test "${enable_osx_universal_binaries+set}" = set; then :
+ enableval=$enable_osx_universal_binaries;
+fi
+
+ if test "x$enable_osx_universal_binaries" = "xyes" ; then
+ if test "x$enable_dependency_tracking" != xno ; then
+ as_fn_error $? "--enable-osx-universal-binary requires --disable-dependency-tracking.
+Please re-run configure with these options:
+ --disable-dependency-tracking --enable-osx-universal-binary" "$LINENO" 5
+ fi
+ if test -d /Developer/SDKs/MacOSX10.5.sdk ; then
+ alpine_sysroot=/Developer/SDKs/MacOSX10.5.sdk
+ elif test -d /Developer/SDKs/MacOSX10.4u.sdk ; then
+ alpine_sysroot=/Developer/SDKs/MacOSX10.4u.sdk
+ else
+ as_fn_error $? "No suitable MacOSX SDK found. Make sure Xcode tools are installed" "$LINENO" 5
+ fi
+ ub_cflags="-isysroot $alpine_sysroot -arch ppc -arch i386"
+ ub_ldflags="-Wl,-syslibroot,$alpine_sysroot -arch ppc -arch i386"
+ AM_CFLAGS="$AM_CFLAGS $ub_cflags"
+ AM_LDFLAGS="$AM_LDFLAGS $ub_ldflags"
+ alpine_c_client_cflags="$alpine_c_client_cflags $ub_cflags"
+ alpine_c_client_ldflags="$alpine_c_client_ldflags $ub_ldflags"
+ fi
+ ;;
+esac
+
+
+@%:@ Check whether --with-include-path was given.
+if test "${with_include_path+set}" = set; then :
+ withval=$with_include_path;
+ case $withval in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+ new_cppflags="-I`echo ${withval} | ${SED} 's/:/ -I/g'`"
+ CPPFLAGS="$CPPFLAGS ${new_cppflags}"
+ alpine_c_client_cflags="$alpine_c_client_cflags ${new_cppflags}"
+ ;;
+ esac
+
+fi
+
+
+
+@%:@ Check whether --with-lib-path was given.
+if test "${with_lib_path+set}" = set; then :
+ withval=$with_lib_path;
+ case $withval in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+ new_ldflags="-L`echo ${withval} | ${SED} 's/:/ -L/g'`"
+ LDFLAGS="$LDFLAGS $new_ldflags"
+ alpine_c_client_ldflags="$alpine_c_client_ldflags ${new_ldflags}"
+ ;;
+ esac
+
+fi
+
+
+
+@%:@ Check whether --with-pubcookie was given.
+if test "${with_pubcookie+set}" = set; then :
+ withval=$with_pubcookie;
+ if test "x$withval" != "xno" ; then
+ WEB_PUBCOOKIE_BUILD=web/src/pubcookie
+ fi
+
+fi
+
+
+
+
+@%:@ Check whether --with-web-bin was given.
+if test "${with_web_bin+set}" = set; then :
+ withval=$with_web_bin;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+ WEB_BINDIR=$withval
+ ;;
+ esac
+
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: debugging is enabled" >&5
+$as_echo_n "checking option: debugging is enabled... " >&6; }
+@%:@ Check whether --enable-debug was given.
+if test "${enable_debug+set}" = set; then :
+ enableval=$enable_debug;
+fi
+
+if test x$enable_debug != "xno" ; then
+ AM_CFLAGS="$AM_CFLAGS -g"
+
+$as_echo "@%:@define DEBUG 1" >>confdefs.h
+
+
+$as_echo "@%:@define DEBUGJOURNAL 1" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: optimization is enabled" >&5
+$as_echo_n "checking option: optimization is enabled... " >&6; }
+@%:@ Check whether --enable-optimization was given.
+if test "${enable_optimization+set}" = set; then :
+ enableval=$enable_optimization;
+fi
+
+if test x$enable_optimization != "xno" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ CFLAGS="`echo $AM_CFLAGS | ${SED} 's/-O2//'`"
+ alpine_c_client_gccoptlevel="-O0"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: mouse support enabled" >&5
+$as_echo_n "checking option: mouse support enabled... " >&6; }
+@%:@ Check whether --enable-mouse was given.
+if test "${enable_mouse+set}" = set; then :
+ enableval=$enable_mouse;
+fi
+
+if test x$enable_mouse != "xno" ; then
+
+$as_echo "@%:@define MOUSE /**/" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: quotas enabled" >&5
+$as_echo_n "checking option: quotas enabled... " >&6; }
+@%:@ Check whether --enable-quotas was given.
+if test "${enable_quotas+set}" = set; then :
+ enableval=$enable_quotas;
+fi
+
+if test x$enable_quotas = "xyes" ; then
+
+$as_echo "@%:@define USE_QUOTAS /**/" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: From changing enabled" >&5
+$as_echo_n "checking option: From changing enabled... " >&6; }
+@%:@ Check whether --enable-from_changing was given.
+if test "${enable_from_changing+set}" = set; then :
+ enableval=$enable_from_changing;
+fi
+
+if test x$enable_from_changing != "xno" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+
+$as_echo "@%:@define NEVER_ALLOW_CHANGING_FROM /**/" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: background post enabled" >&5
+$as_echo_n "checking option: background post enabled... " >&6; }
+@%:@ Check whether --enable-background-post was given.
+if test "${enable_background_post+set}" = set; then :
+ enableval=$enable_background_post;
+fi
+
+if test x$enable_background_post != "xno" ; then
+
+$as_echo "@%:@define BACKGROUND_POST /**/" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: keyboard lock enabled" >&5
+$as_echo_n "checking option: keyboard lock enabled... " >&6; }
+@%:@ Check whether --enable-keyboard-lock was given.
+if test "${enable_keyboard_lock+set}" = set; then :
+ enableval=$enable_keyboard_lock;
+fi
+
+if test x$enable_keyboard_lock != "xno" ; then
+
+$as_echo "@%:@define KEYBOARD_LOCK /**/" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: from encoding enabled" >&5
+$as_echo_n "checking option: from encoding enabled... " >&6; }
+@%:@ Check whether --enable-from-encoding was given.
+if test "${enable_from_encoding+set}" = set; then :
+ enableval=$enable_from_encoding;
+fi
+
+if test x$enable_from_encoding = "xyes" ; then
+
+$as_echo "@%:@define ENCODE_FROMS /**/" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+@%:@ Check whether --with-smtp-msa was given.
+if test "${with_smtp_msa+set}" = set; then :
+ withval=$with_smtp_msa;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ # Extract the first word of "sendmail", so it can be a program name with args.
+set dummy sendmail; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_SENDMAIL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $SENDMAIL in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_SENDMAIL="$SENDMAIL" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_dummy="$PATH:/usr/sbin:/usr/lib"
+for as_dir in $as_dummy
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_SENDMAIL="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_SENDMAIL" && ac_cv_path_SENDMAIL=""""
+ ;;
+esac
+fi
+SENDMAIL=$ac_cv_path_SENDMAIL
+if test -n "$SENDMAIL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SENDMAIL" >&5
+$as_echo "$SENDMAIL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ ;;
+ *)
+ SENDMAIL=$withval
+ ;;
+ esac
+
+else
+
+ # Extract the first word of "sendmail", so it can be a program name with args.
+set dummy sendmail; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_SENDMAIL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $SENDMAIL in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_SENDMAIL="$SENDMAIL" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_dummy="$PATH:/usr/sbin:/usr/lib"
+for as_dir in $as_dummy
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_SENDMAIL="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_SENDMAIL" && ac_cv_path_SENDMAIL=""""
+ ;;
+esac
+fi
+SENDMAIL=$ac_cv_path_SENDMAIL
+if test -n "$SENDMAIL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SENDMAIL" >&5
+$as_echo "$SENDMAIL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+fi
+
+if test -n "$SENDMAIL" ; then
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SENDMAIL "$SENDMAIL"
+_ACEOF
+
+fi
+
+smtp_msa_flags="-bs -odb -oem"
+
+@%:@ Check whether --with-smtp-msa-flags was given.
+if test "${with_smtp_msa_flags+set}" = set; then :
+ withval=$with_smtp_msa_flags;
+ if test "x$withval" != "xno" ; then
+ smtp_msa_flags=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SENDMAILFLAGS "$smtp_msa_flags"
+_ACEOF
+
+
+npa="inews"
+
+@%:@ Check whether --with-npa was given.
+if test "${with_npa+set}" = set; then :
+ withval=$with_npa;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ # Extract the first word of "inews", so it can be a program name with args.
+set dummy inews; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_NPA_PROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $NPA_PROG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_NPA_PROG="$NPA_PROG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_dummy="$PATH:/usr/sbin:/usr/lib"
+for as_dir in $as_dummy
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_NPA_PROG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_NPA_PROG" && ac_cv_path_NPA_PROG=""""
+ ;;
+esac
+fi
+NPA_PROG=$ac_cv_path_NPA_PROG
+if test -n "$NPA_PROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NPA_PROG" >&5
+$as_echo "$NPA_PROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ ;;
+ *)
+ NPA_PROG=$withval
+ ;;
+ esac
+
+else
+
+ # Extract the first word of "inews", so it can be a program name with args.
+set dummy inews; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_NPA_PROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $NPA_PROG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_NPA_PROG="$NPA_PROG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_dummy="$PATH:/usr/sbin:/usr/lib"
+for as_dir in $as_dummy
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_NPA_PROG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_NPA_PROG" && ac_cv_path_NPA_PROG=""""
+ ;;
+esac
+fi
+NPA_PROG=$ac_cv_path_NPA_PROG
+if test -n "$NPA_PROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NPA_PROG" >&5
+$as_echo "$NPA_PROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+fi
+
+
+npa_flags="-h"
+
+@%:@ Check whether --with-npa-flags was given.
+if test "${with_npa_flags+set}" = set; then :
+ withval=$with_npa_flags;
+ if test "x$withval" != "xno" ; then
+ npa_flags=$withval
+ fi
+
+fi
+
+if test -n "$NPA_PROG" ; then
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SENDNEWS "$NPA_PROG $npa_flags"
+_ACEOF
+
+fi
+
+
+@%:@ Check whether --with-password-prog was given.
+if test "${with_password_prog+set}" = set; then :
+ withval=$with_password_prog;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ # Extract the first word of "passwd", so it can be a program name with args.
+set dummy passwd; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_PWPROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $PWPROG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_PWPROG="$PWPROG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_dummy="$PATH:/usr/sbin:/usr/lib"
+for as_dir in $as_dummy
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_PWPROG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_PWPROG" && ac_cv_path_PWPROG=""""
+ ;;
+esac
+fi
+PWPROG=$ac_cv_path_PWPROG
+if test -n "$PWPROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PWPROG" >&5
+$as_echo "$PWPROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ ;;
+ *)
+ # Extract the first word of "$withval", so it can be a program name with args.
+set dummy $withval; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_PWPROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $PWPROG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_PWPROG="$PWPROG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_dummy="$PATH:/usr/sbin:/usr/lib"
+for as_dir in $as_dummy
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_PWPROG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_PWPROG" && ac_cv_path_PWPROG=""""
+ ;;
+esac
+fi
+PWPROG=$ac_cv_path_PWPROG
+if test -n "$PWPROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PWPROG" >&5
+$as_echo "$PWPROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ ;;
+ esac
+
+else
+
+ # Extract the first word of "passwd", so it can be a program name with args.
+set dummy passwd; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_PWPROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $PWPROG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_PWPROG="$PWPROG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_dummy="$PATH:/usr/sbin:/usr/lib"
+for as_dir in $as_dummy
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_PWPROG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_PWPROG" && ac_cv_path_PWPROG=""""
+ ;;
+esac
+fi
+PWPROG=$ac_cv_path_PWPROG
+if test -n "$PWPROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PWPROG" >&5
+$as_echo "$PWPROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+fi
+
+if test -n "$PWPROG" ; then
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PASSWD_PROG "$PWPROG"
+_ACEOF
+
+fi
+
+
+@%:@ Check whether --with-simple-spellcheck was given.
+if test "${with_simple_spellcheck+set}" = set; then :
+ withval=$with_simple_spellcheck;
+ if test "x$withval" != "xno" ; then
+ SPELLPROG=$withval
+ fi
+
+else
+
+ # Extract the first word of "hunspell", so it can be a program name with args.
+set dummy hunspell; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_SPELLPROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$SPELLPROG"; then
+ ac_cv_prog_SPELLPROG="$SPELLPROG" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_SPELLPROG="hunspell"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+SPELLPROG=$ac_cv_prog_SPELLPROG
+if test -n "$SPELLPROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SPELLPROG" >&5
+$as_echo "$SPELLPROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test -z "$SPELLPROG" ; then
+ # Extract the first word of "aspell", so it can be a program name with args.
+set dummy aspell; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_SPELLPROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$SPELLPROG"; then
+ ac_cv_prog_SPELLPROG="$SPELLPROG" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_SPELLPROG="aspell"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+SPELLPROG=$ac_cv_prog_SPELLPROG
+if test -n "$SPELLPROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SPELLPROG" >&5
+$as_echo "$SPELLPROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test -z "$SPELLPROG" ; then
+ # Extract the first word of "ispell", so it can be a program name with args.
+set dummy ispell; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_SPELLPROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$SPELLPROG"; then
+ ac_cv_prog_SPELLPROG="$SPELLPROG" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_SPELLPROG="ispell"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+SPELLPROG=$ac_cv_prog_SPELLPROG
+if test -n "$SPELLPROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SPELLPROG" >&5
+$as_echo "$SPELLPROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test -z "$SPELLPROG" ; then
+ SPELLPROG="spell"
+ fi
+ fi
+ fi
+
+fi
+
+
+if test "x$SPELLPROG" != "xno" ; then
+ # Extract the first word of "$SPELLPROG", so it can be a program name with args.
+set dummy $SPELLPROG; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_alpine_simple_spellcheck+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $alpine_simple_spellcheck in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_alpine_simple_spellcheck="$alpine_simple_spellcheck" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_alpine_simple_spellcheck="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+alpine_simple_spellcheck=$ac_cv_path_alpine_simple_spellcheck
+if test -n "$alpine_simple_spellcheck"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $alpine_simple_spellcheck" >&5
+$as_echo "$alpine_simple_spellcheck" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test -n "$alpine_simple_spellcheck" ; then
+ case "$SPELLPROG" in
+ hunspell)
+ alpine_simple_spellcheck="$alpine_simple_spellcheck -l"
+ ;;
+ aspell)
+ alpine_simple_spellcheck="$alpine_simple_spellcheck --dont-backup --mode=email list"
+ ;;
+ ispell)
+ alpine_simple_spellcheck="$alpine_simple_spellcheck -l"
+ ;;
+ *)
+ ;;
+ esac
+ fi
+fi
+
+
+@%:@ Check whether --with-interactive-spellcheck was given.
+if test "${with_interactive_spellcheck+set}" = set; then :
+ withval=$with_interactive_spellcheck;
+ if test "x$withval" != "xno" ; then
+ ISPELLPROG=$withval
+ fi
+
+else
+
+ # Extract the first word of "hunspell", so it can be a program name with args.
+set dummy hunspell; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ISPELLPROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ISPELLPROG"; then
+ ac_cv_prog_ISPELLPROG="$ISPELLPROG" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ISPELLPROG="hunspell"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ISPELLPROG=$ac_cv_prog_ISPELLPROG
+if test -n "$ISPELLPROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ISPELLPROG" >&5
+$as_echo "$ISPELLPROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test -z "$ISPELLPROG" ; then
+ # Extract the first word of "aspell", so it can be a program name with args.
+set dummy aspell; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ISPELLPROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ISPELLPROG"; then
+ ac_cv_prog_ISPELLPROG="$ISPELLPROG" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ISPELLPROG="aspell"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ISPELLPROG=$ac_cv_prog_ISPELLPROG
+if test -n "$ISPELLPROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ISPELLPROG" >&5
+$as_echo "$ISPELLPROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test -z "$SPELLPROG" ; then
+ ISPELLPROG="ispell"
+ fi
+ fi
+
+fi
+
+
+if test "x$ISPELLPROG" != "xno" ; then
+ # Extract the first word of "$ISPELLPROG", so it can be a program name with args.
+set dummy $ISPELLPROG; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_alpine_interactive_spellcheck+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $alpine_interactive_spellcheck in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_alpine_interactive_spellcheck="$alpine_interactive_spellcheck" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_alpine_interactive_spellcheck="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+alpine_interactive_spellcheck=$ac_cv_path_alpine_interactive_spellcheck
+if test -n "$alpine_interactive_spellcheck"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $alpine_interactive_spellcheck" >&5
+$as_echo "$alpine_interactive_spellcheck" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test -n "$alpine_interactive_spellcheck" ; then
+ case "$ISPELLPROG" in
+ aspell)
+ alpine_interactive_spellcheck="$alpine_interactive_spellcheck --dont-backup --mode=email check"
+ ;;
+ *)
+ ;;
+ esac
+ fi
+fi
+
+if test -n "$alpine_interactive_spellcheck" ; then
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_VAR_SPELLER "$alpine_interactive_spellcheck"
+_ACEOF
+
+fi
+
+if test -z "$alpine_simple_spellcheck" -a -n "$alpine_interactive_spellcheck" ; then
+ alpine_simple_spellcheck=test
+fi
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SPELLER "$alpine_simple_spellcheck"
+_ACEOF
+
+
+case "$prefix" in
+ NONE) dpv=/usr/local/lib/pine.conf ;;
+ *) dpv=${prefix}/lib/pine.conf ;;
+esac
+
+@%:@ Check whether --with-system-pinerc was given.
+if test "${with_system_pinerc+set}" = set; then :
+ withval=$with_system_pinerc;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+ dpv=$withval
+ ;;
+ esac
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SYSTEM_PINERC "$dpv"
+_ACEOF
+
+
+case "$prefix" in
+ NONE) dpv=/usr/local/lib/pine.conf.fixed ;;
+ *) dpv=${prefix}/lib/pine.conf.fixed ;;
+esac
+
+@%:@ Check whether --with-system-fixed-pinerc was given.
+if test "${with_system_fixed_pinerc+set}" = set; then :
+ withval=$with_system_fixed_pinerc;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+ dpv=$withval
+ ;;
+ esac
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SYSTEM_PINERC_FIXED "$dpv"
+_ACEOF
+
+
+
+
+
+
+
+ dpv=150
+
+@%:@ Check whether --with-mailcheck-interval was given.
+if test "${with_mailcheck_interval+set}" = set; then :
+ withval=$with_mailcheck_interval;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_MAILCHECK "$dpv"
+_ACEOF
+
+
+
+ dpv=420
+
+@%:@ Check whether --with-checkpoint-interval was given.
+if test "${with_checkpoint_interval+set}" = set; then :
+ withval=$with_checkpoint_interval;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define CHECK_POINT_TIME $dpv
+_ACEOF
+
+
+
+ dpv=12
+
+@%:@ Check whether --with-checkpoint-frequency was given.
+if test "${with_checkpoint_frequency+set}" = set; then :
+ withval=$with_checkpoint_frequency;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define CHECK_POINT_FREQ $dpv
+_ACEOF
+
+
+
+ dpv=24
+
+@%:@ Check whether --with-display-rows was given.
+if test "${with_display_rows+set}" = set; then :
+ withval=$with_display_rows;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DEFAULT_LINES_ON_TERMINAL $dpv
+_ACEOF
+
+
+
+ dpv=80
+
+@%:@ Check whether --with-display-columns was given.
+if test "${with_display_columns+set}" = set; then :
+ withval=$with_display_columns;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DEFAULT_COLUMNS_ON_TERMINAL $dpv
+_ACEOF
+
+
+
+ dpv=200
+
+@%:@ Check whether --with-max-display-rows was given.
+if test "${with_max_display_rows+set}" = set; then :
+ withval=$with_max_display_rows;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define MAX_SCREEN_ROWS $dpv
+_ACEOF
+
+
+
+ dpv=500
+
+@%:@ Check whether --with-max-display-columns was given.
+if test "${with_max_display_columns+set}" = set; then :
+ withval=$with_max_display_columns;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define MAX_SCREEN_COLS $dpv
+_ACEOF
+
+
+
+ dpv=74
+
+@%:@ Check whether --with-fill-column was given.
+if test "${with_fill_column+set}" = set; then :
+ withval=$with_fill_column;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_FILLCOL "$dpv"
+_ACEOF
+
+
+
+ dpv=80
+
+@%:@ Check whether --with-max_fill-column was given.
+if test "${with_max_fill_column+set}" = set; then :
+ withval=$with_max_fill_column;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define MAX_FILLCOL $dpv
+_ACEOF
+
+
+
+ dpv=2
+
+@%:@ Check whether --with-debug-level was given.
+if test "${with_debug_level+set}" = set; then :
+ withval=$with_debug_level;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DEFAULT_DEBUG $dpv
+_ACEOF
+
+
+
+ dpv=4
+
+@%:@ Check whether --with-debug-files was given.
+if test "${with_debug_files+set}" = set; then :
+ withval=$with_debug_files;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define NUMDEBUGFILES $dpv
+_ACEOF
+
+
+
+ dpv=.pine-debug
+
+@%:@ Check whether --with-debug-file was given.
+if test "${with_debug_file+set}" = set; then :
+ withval=$with_debug_file;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DEBUGFILE "$dpv"
+_ACEOF
+
+
+
+ dpv="\$Forwarded"
+
+@%:@ Check whether --with-forwarded-keyword was given.
+if test "${with_forwarded_keyword+set}" = set; then :
+ withval=$with_forwarded_keyword;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define FORWARDED_FLAG "$dpv"
+_ACEOF
+
+
+
+ dpv=2
+
+@%:@ Check whether --with-display-overlap was given.
+if test "${with_display_overlap+set}" = set; then :
+ withval=$with_display_overlap;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_OVERLAP "$dpv"
+_ACEOF
+
+
+
+ dpv=0
+
+@%:@ Check whether --with-display-margin was given.
+if test "${with_display_margin+set}" = set; then :
+ withval=$with_display_margin;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_MARGIN "$dpv"
+_ACEOF
+
+
+
+ dpv=sent-mail
+
+@%:@ Check whether --with-default-fcc was given.
+if test "${with_default_fcc+set}" = set; then :
+ withval=$with_default_fcc;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_DEFAULT_FCC "$dpv"
+_ACEOF
+
+
+
+ dpv=saved-messages
+
+@%:@ Check whether --with-default-save-folder was given.
+if test "${with_default_save_folder+set}" = set; then :
+ withval=$with_default_save_folder;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DEFAULT_SAVE "$dpv"
+_ACEOF
+
+
+
+ dpv=postponed-mail
+
+@%:@ Check whether --with-default-legacy-postponed-folder was given.
+if test "${with_default_legacy_postponed_folder+set}" = set; then :
+ withval=$with_default_legacy_postponed_folder;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define POSTPONED_MAIL "$dpv"
+_ACEOF
+
+
+
+ dpv=postponed-msgs
+
+@%:@ Check whether --with-default-postponed-folder was given.
+if test "${with_default_postponed_folder+set}" = set; then :
+ withval=$with_default_postponed_folder;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define POSTPONED_MSGS "$dpv"
+_ACEOF
+
+
+
+ dpv=Trash
+
+@%:@ Check whether --with-default-trash-folder was given.
+if test "${with_default_trash_folder+set}" = set; then :
+ withval=$with_default_trash_folder;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define TRASH_FOLDER "$dpv"
+_ACEOF
+
+
+
+ dpv=.pine-interrupted-mail
+
+@%:@ Check whether --with-default-interrupted-mail was given.
+if test "${with_default_interrupted_mail+set}" = set; then :
+ withval=$with_default_interrupted_mail;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define INTERRUPTED_MAIL "$dpv"
+_ACEOF
+
+
+
+ dpv=dead.letter
+
+@%:@ Check whether --with-default-dead-letter-folder was given.
+if test "${with_default_dead_letter_folder+set}" = set; then :
+ withval=$with_default_dead_letter_folder;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DEADLETTER "$dpv"
+_ACEOF
+
+
+
+ dpv=mail
+
+@%:@ Check whether --with-default-mail-directory was given.
+if test "${with_default_mail_directory+set}" = set; then :
+ withval=$with_default_mail_directory;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_MAIL_DIRECTORY "$dpv"
+_ACEOF
+
+
+
+ dpv=INBOX
+
+@%:@ Check whether --with-default-inbox-name was given.
+if test "${with_default_inbox_name+set}" = set; then :
+ withval=$with_default_inbox_name;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define INBOX_NAME "$dpv"
+_ACEOF
+
+
+
+ dpv=.signature
+
+@%:@ Check whether --with-default-signature-file was given.
+if test "${with_default_signature_file+set}" = set; then :
+ withval=$with_default_signature_file;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_SIGNATURE_FILE "$dpv"
+_ACEOF
+
+
+
+ dpv=no
+
+@%:@ Check whether --with-default-elm-style-save was given.
+if test "${with_default_elm_style_save+set}" = set; then :
+ withval=$with_default_elm_style_save;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_ELM_STYLE_SAVE "$dpv"
+_ACEOF
+
+
+
+ dpv=no
+
+@%:@ Check whether --with-default-header-in-reply was given.
+if test "${with_default_header_in_reply+set}" = set; then :
+ withval=$with_default_header_in_reply;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_HEADER_IN_REPLY "$dpv"
+_ACEOF
+
+
+
+ dpv=no
+
+@%:@ Check whether --with-default-old-style-reply was given.
+if test "${with_default_old_style_reply+set}" = set; then :
+ withval=$with_default_old_style_reply;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_OLD_STYLE_REPLY "$dpv"
+_ACEOF
+
+
+
+ dpv=no
+
+@%:@ Check whether --with-default-use-only-domain-name was given.
+if test "${with_default_use_only_domain_name+set}" = set; then :
+ withval=$with_default_use_only_domain_name;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_USE_ONLY_DOMAIN_NAME "$dpv"
+_ACEOF
+
+
+
+ dpv=no
+
+@%:@ Check whether --with-default-save-by-sender was given.
+if test "${with_default_save_by_sender+set}" = set; then :
+ withval=$with_default_save_by_sender;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_SAVE_BY_SENDER "$dpv"
+_ACEOF
+
+
+
+ dpv=arrival
+
+@%:@ Check whether --with-default-sort-key was given.
+if test "${with_default_sort_key+set}" = set; then :
+ withval=$with_default_sort_key;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_SORT_KEY "$dpv"
+_ACEOF
+
+
+
+ dpv=fullname-with-lists-last
+
+@%:@ Check whether --with-default-addressbook-sort-rule was given.
+if test "${with_default_addressbook_sort_rule+set}" = set; then :
+ withval=$with_default_addressbook_sort_rule;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_AB_SORT_RULE "$dpv"
+_ACEOF
+
+
+
+ dpv=alphabetical
+
+@%:@ Check whether --with-default-folder-sort-rule was given.
+if test "${with_default_folder_sort_rule+set}" = set; then :
+ withval=$with_default_folder_sort_rule;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_FLD_SORT_RULE "$dpv"
+_ACEOF
+
+
+
+ dpv=default-folder
+
+@%:@ Check whether --with-default-saved-message-name-rule was given.
+if test "${with_default_saved_message_name_rule+set}" = set; then :
+ withval=$with_default_saved_message_name_rule;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_SAVED_MSG_NAME_RULE "$dpv"
+_ACEOF
+
+
+
+ dpv=default-fcc
+
+@%:@ Check whether --with-default-fcc-rule was given.
+if test "${with_default_fcc_rule+set}" = set; then :
+ withval=$with_default_fcc_rule;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_FCC_RULE "$dpv"
+_ACEOF
+
+
+
+ dpv=lpr
+
+@%:@ Check whether --with-default-standard-printer was given.
+if test "${with_default_standard_printer+set}" = set; then :
+ withval=$with_default_standard_printer;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_STANDARD_PRINTER "$dpv"
+_ACEOF
+
+
+
+ dpv=attached-to-ansi
+
+@%:@ Check whether --with-default-ansi-printer was given.
+if test "${with_default_ansi_printer+set}" = set; then :
+ withval=$with_default_ansi_printer;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define ANSI_PRINTER "$dpv"
+_ACEOF
+
+
+
+ dpv=.addressbook
+
+@%:@ Check whether --with-default-addressbook was given.
+if test "${with_default_addressbook+set}" = set; then :
+ withval=$with_default_addressbook;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_ADDRESSBOOK "$dpv"
+_ACEOF
+
+
+
+ dpv="Local Support"
+
+@%:@ Check whether --with-default-local-fullname was given.
+if test "${with_default_local_fullname+set}" = set; then :
+ withval=$with_default_local_fullname;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_LOCAL_FULLNAME "$dpv"
+_ACEOF
+
+
+
+ dpv=postmaster
+
+@%:@ Check whether --with-default-local-address was given.
+if test "${with_default_local_address+set}" = set; then :
+ withval=$with_default_local_address;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_LOCAL_ADDRESS "$dpv"
+_ACEOF
+
+
+
+ dpv=1
+
+@%:@ Check whether --with-default-keyboard-lock-count was given.
+if test "${with_default_keyboard_lock_count+set}" = set; then :
+ withval=$with_default_keyboard_lock_count;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_KBLOCK_PASSWD_COUNT "$dpv"
+_ACEOF
+
+
+
+ dpv=3
+
+@%:@ Check whether --with-default-remote-addressbook-history was given.
+if test "${with_default_remote_addressbook_history+set}" = set; then :
+ withval=$with_default_remote_addressbook_history;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_REMOTE_ABOOK_HISTORY "$dpv"
+_ACEOF
+
+
+
+ dpv=.alpine-smime/public
+
+@%:@ Check whether --with-smime-public-cert-directory was given.
+if test "${with_smime_public_cert_directory+set}" = set; then :
+ withval=$with_smime_public_cert_directory;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_PUBLICCERT_DIR "$dpv"
+_ACEOF
+
+
+
+ dpv=.alpine-smime/private
+
+@%:@ Check whether --with-smime-private-key-directory was given.
+if test "${with_smime_private_key_directory+set}" = set; then :
+ withval=$with_smime_private_key_directory;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_PRIVATEKEY_DIR "$dpv"
+_ACEOF
+
+
+
+ dpv=.alpine-smime/ca
+
+@%:@ Check whether --with-smime-cacert-directory was given.
+if test "${with_smime_cacert_directory+set}" = set; then :
+ withval=$with_smime_cacert_directory;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_CACERT_DIR "$dpv"
+_ACEOF
+
+
+
+ dpv=ANSI_PRINTER
+
+@%:@ Check whether --with-default-printer was given.
+if test "${with_default_printer+set}" = set; then :
+ withval=$with_default_printer;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_DEFAULT_PRINTER $dpv
+_ACEOF
+
+
+
+
+@%:@ Check whether --with-passfile was given.
+if test "${with_passfile+set}" = set; then :
+ withval=$with_passfile;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+ alpine_PASSFILE=$withval
+ ;;
+ esac
+
+fi
+
+
+
+@%:@ Check whether --with-local-password-cache was given.
+if test "${with_local_password_cache+set}" = set; then :
+ withval=$with_local_password_cache;
+ alpine_os_credential_cache=$withval
+
+fi
+
+
+
+@%:@ Check whether --with-local-password-cache-method was given.
+if test "${with_local_password_cache_method+set}" = set; then :
+ withval=$with_local_password_cache_method;
+ alpine_os_credential_cache_method=$withval
+
+fi
+
+
+if test -n "$alpine_PASSFILE" ; then
+ case $alpine_os_credential_cache in
+ no)
+ ;;
+ *)
+ alpine_os_credential_cache="no"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: --with-passfile definition overrides OS-Specific password caching" >&5
+$as_echo "$as_me: --with-passfile definition overrides OS-Specific password caching" >&6;}
+ ;;
+ esac
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PASSFILE "$alpine_PASSFILE"
+_ACEOF
+
+fi
+
+
+@%:@ Check whether --with-default-sshpath was given.
+if test "${with_default_sshpath+set}" = set; then :
+ withval=$with_default_sshpath;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_SSHPATH "$withval"
+_ACEOF
+
+ ;;
+ esac
+
+fi
+
+
+
+@%:@ Check whether --with-default-sshcmd was given.
+if test "${with_default_sshcmd+set}" = set; then :
+ withval=$with_default_sshcmd;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+
+cat >>confdefs.h <<_ACEOF
+@%:@define DF_SSHCMD "$withval"
+_ACEOF
+
+ ;;
+ esac
+
+fi
+
+
+
+@%:@ Check whether --with-ssl was given.
+if test "${with_ssl+set}" = set; then :
+ withval=$with_ssl; with_ssl=$withval
+fi
+
+
+if test "x$with_ssl" = "xno" ; then
+ alpine_SSLTYPE="none"
+else
+ case $host in
+ *-linux-gnu)
+ if test -f /etc/fedora-release -o -f /etc/redhat-release -o -f /etc/redhat_version ; then
+ alpine_SSLTYPE="nopwd"
+ if test -d /etc/pki/tls ; then
+ alpine_SSLDIR="/etc/pki/tls"
+ else
+ alpine_SSLDIR="/usr/share/ssl"
+ fi
+ elif test -f /etc/SuSE-release ; then
+ alpine_SSLTYPE="nopwd"
+ alpine_SSLDIR="/usr/share/ssl"
+ alpine_SSLCERTS="/etc/ssl/certs"
+ elif test -d /etc/osso-af-init ; then
+ alpine_SSLTYPE="nopwd"
+ alpine_SSLDIR="/usr"
+ alpine_SSLCERTS="/usr/share/certs"
+ else
+ alpine_SSLTYPE="nopwd"
+ alpine_SSLDIR="/usr"
+ alpine_SSLCERTS="/etc/ssl/certs"
+ fi
+ ;;
+ *-apple-darwin*)
+ alpine_SSLTYPE="nopwd"
+ alpine_SSLCERTS="/System/Library/OpenSSL/certs"
+ ;;
+ *-openbsd*)
+ alpine_SSLTYPE="nopwd"
+ alpine_SSLDIR="/usr"
+ alpine_SSLCERTS="/etc/ssl/certs"
+ ;;
+ *-sco-sysv* | *-sysv*UnixWare | *-sysv*OpenUNIX)
+ alpine_SSLTYPE="sco.nopwd"
+ alpine_SSLDIR=/usr/local/ssl
+ ;;
+ *-*-solaris*)
+ if test -d /usr/sfw/include/openssl ; then
+ alpine_SSLDIR="/usr/sfw"
+ elif test -d /opt/csw/include/openssl ; then
+ alpine_SSLDIR="/opt/csw"
+ if test -d /opt/csw/ssl/certs ; then
+ alpine_SSLCERTS="/opt/csw/ssl/certs"
+ fi
+ fi
+ if test -z "$alpine_SSLCERTS" -a -d /etc/certs ; then
+ alpine_SSLCERTS="/etc/certs"
+ fi
+ ;;
+ *)
+ alpine_SSLTYPE="nopwd"
+ alpine_SSLDIR=/usr/local/ssl
+ ;;
+ esac
+
+
+@%:@ Check whether --with-ssl-dir was given.
+if test "${with_ssl_dir+set}" = set; then :
+ withval=$with_ssl_dir;
+ if test "x$withval" != "xno" ; then
+ alpine_SSLDIR=$withval
+ fi
+
+fi
+
+
+
+@%:@ Check whether --with-ssl-certs-dir was given.
+if test "${with_ssl_certs_dir+set}" = set; then :
+ withval=$with_ssl_certs_dir;
+ if test "x$withval" != "xno" ; then
+ alpine_SSLCERTS=$withval
+ fi
+
+fi
+
+
+
+@%:@ Check whether --with-ssl-include-dir was given.
+if test "${with_ssl_include_dir+set}" = set; then :
+ withval=$with_ssl_include_dir;
+ if test "x$withval" != "xno" ; then
+ alpine_SSLINCLUDE=$withval
+ fi
+
+fi
+
+
+
+@%:@ Check whether --with-ssl-lib-dir was given.
+if test "${with_ssl_lib_dir+set}" = set; then :
+ withval=$with_ssl_lib_dir;
+ if test "x$withval" != "xno" ; then
+ alpine_SSLLIB=$withval
+ fi
+
+fi
+
+ if test -n "$alpine_SSLINCLUDE" ; then
+ CPPCFLAGS="-I$alpine_SSLINCLUDE $CPPFLAGS"
+ elif test -n "$alpine_SSLDIR" ; then
+ CPPFLAGS="-I${alpine_SSLDIR}/include $CPPFLAGS"
+ fi
+ if test -n "$alpine_SSLLIB" ; then
+ LDFLAGS="-L$alpine_SSLLIB $LDFLAGS"
+ elif test -n "$alpine_SSLDIR" ; then
+ LDFLAGS="-L${alpine_SSLDIR}/lib $LDFLAGS"
+ fi
+fi
+
+
+@%:@ Check whether --with-krb5 was given.
+if test "${with_krb5+set}" = set; then :
+ withval=$with_krb5; with_krb5=$withval
+fi
+
+
+if test "x$with_krb5" = "xno" ; then
+ alpine_GSSTYPE="none"
+else
+ alpine_GSSTYPE=
+
+
+@%:@ Check whether --with-krb5-dir was given.
+if test "${with_krb5_dir+set}" = set; then :
+ withval=$with_krb5_dir;
+ if test "x$withval" != "xno" ; then
+ CPPFLAGS="$CPPFLAGS -I${withval}/include"
+ LDFLAGS="$LDFLAGS -L${withval}/lib"
+ fi
+
+fi
+
+
+
+@%:@ Check whether --with-krb5-include-dir was given.
+if test "${with_krb5_include_dir+set}" = set; then :
+ withval=$with_krb5_include_dir;
+ if test "x$withval" != "xno" ; then
+ CPPFLAGS="$CPPFLAGS -I$withval"
+ fi
+
+fi
+
+
+
+@%:@ Check whether --with-krb5-lib-dir was given.
+if test "${with_krb5_lib_dir+set}" = set; then :
+ withval=$with_krb5_lib_dir;
+ if test "x$withval" != "xno" ; then
+ LDFLAGS="$LDFLAGS -L$withval"
+ fi
+
+fi
+
+fi
+
+
+@%:@ Check whether --with-ldap was given.
+if test "${with_ldap+set}" = set; then :
+ withval=$with_ldap; with_ldap=$withval
+fi
+
+
+if test "x$with_ldap" = "xno" ; then
+ alpine_with_ldap=
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Excluding LDAP Support" >&5
+$as_echo "$as_me: Excluding LDAP Support" >&6;}
+else
+
+ alpine_with_ldap=yes
+
+@%:@ Check whether --with-ldap-dir was given.
+if test "${with_ldap_dir+set}" = set; then :
+ withval=$with_ldap_dir;
+ if test "x$withval" != "xno" ; then
+ CPPFLAGS="$CPPFLAGS -I${withval}/include"
+ LDFLAGS="$LDFLAGS -L${withval}/lib"
+ fi
+
+fi
+
+
+
+@%:@ Check whether --with-ldap-include-dir was given.
+if test "${with_ldap_include_dir+set}" = set; then :
+ withval=$with_ldap_include_dir;
+ if test "x$withval" != "xno" ; then
+ CPPFLAGS="$CPPFLAGS -I$withval"
+ fi
+
+fi
+
+
+
+@%:@ Check whether --with-ldap-lib-dir was given.
+if test "${with_ldap_lib_dir+set}" = set; then :
+ withval=$with_ldap_lib_dir;
+ if test "x$withval" != "xno" ; then
+ LDFLAGS="$LDFLAGS -L$withval"
+ fi
+
+fi
+
+fi
+
+
+@%:@ Check whether --with-smime was given.
+if test "${with_smime+set}" = set; then :
+ withval=$with_smime; with_smime=$withval
+fi
+
+
+
+@%:@ Check whether --with-tcl was given.
+if test "${with_tcl+set}" = set; then :
+ withval=$with_tcl; with_tcl=$withval
+fi
+
+
+if test "x$with_tcl" = "xno" ; then
+ WEB_BUILD=
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Excluding TCL Support, and thus Web Alpine Support" >&5
+$as_echo "$as_me: Excluding TCL Support, and thus Web Alpine Support" >&6;}
+else
+
+@%:@ Check whether --with-tcl-lib was given.
+if test "${with_tcl_lib+set}" = set; then :
+ withval=$with_tcl_lib;
+ if test "x$withval" != "xno" ; then
+ alpine_TCLLIB=$withval
+ fi
+
+fi
+
+
+@%:@ Check whether --with-tcl-include was given.
+if test "${with_tcl_include+set}" = set; then :
+ withval=$with_tcl_include;
+ if test "x$withval" != "xno" ; then
+ CPPFLAGS="$CPPFLAGS -I$withval"
+ alpine_TCLINC=$withval
+ fi
+
+fi
+
+fi
+
+
+@%:@ Check whether --with-supplied-regex was given.
+if test "${with_supplied_regex+set}" = set; then :
+ withval=$with_supplied_regex; alpine_REGEX=$withval
+fi
+
+
+
+@%:@ Check whether --with-pthread was given.
+if test "${with_pthread+set}" = set; then :
+ withval=$with_pthread; with_pthread=$withval
+fi
+
+
+
+@%:@ Check whether --with-system-mail-directory was given.
+if test "${with_system_mail_directory+set}" = set; then :
+ withval=$with_system_mail_directory;
+ if test "x$withval" != "xno" ; then
+ alpine_with_local_maildir="$withval"
+ fi
+
+fi
+
+
+
+@%:@ Check whether --with-c-client-target was given.
+if test "${with_c_client_target+set}" = set; then :
+ withval=$with_c_client_target;
+ if test "x$withval" != "xno" ;then
+ alpine_with_c_client_target="$withval"
+ fi
+
+fi
+
+
+
+
+@%:@ Check whether --with-ipv6 was given.
+if test "${with_ipv6+set}" = set; then :
+ withval=$with_ipv6; with_ipv6=$withval
+fi
+
+
+if test "x$with_ipv6" = "xno" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Excluding IPv6 Support" >&5
+$as_echo "$as_me: Excluding IPv6 Support" >&6;}
+ c_client_specials="${c_client_specials}IP6=4 "
+ c_client_ip6="true"
+else
+ c_client_ip6="touch imap/ip6"
+fi
+
+
+
+if test x$enable_dmalloc = "xyes" ; then
+ if test "x$with_pthread" = "xyes" ; then
+ dmalloc_lib=dmallocth
+ else
+ dmalloc_lib=dmalloc
+ fi
+
+ as_ac_Lib=`$as_echo "ac_cv_lib_$dmalloc_lib''_dmalloc_shutdown" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dmalloc_shutdown in -l$dmalloc_lib" >&5
+$as_echo_n "checking for dmalloc_shutdown in -l$dmalloc_lib... " >&6; }
+if eval \${$as_ac_Lib+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-l$dmalloc_lib $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dmalloc_shutdown ();
+int
+main ()
+{
+return dmalloc_shutdown ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ eval "$as_ac_Lib=yes"
+else
+ eval "$as_ac_Lib=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+eval ac_res=\$$as_ac_Lib
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define `$as_echo "HAVE_LIB$dmalloc_lib" | $as_tr_cpp` 1
+_ACEOF
+
+ LIBS="-l$dmalloc_lib $LIBS"
+
+else
+
+ as_fn_error $? "$dmalloc_lib requested, but -ldmalloc not found" "$LINENO" 5
+
+fi
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for setupterm in -ltinfo" >&5
+$as_echo_n "checking for setupterm in -ltinfo... " >&6; }
+if ${ac_cv_lib_tinfo_setupterm+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ltinfo $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char setupterm ();
+int
+main ()
+{
+return setupterm ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_tinfo_setupterm=yes
+else
+ ac_cv_lib_tinfo_setupterm=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_tinfo_setupterm" >&5
+$as_echo "$ac_cv_lib_tinfo_setupterm" >&6; }
+if test "x$ac_cv_lib_tinfo_setupterm" = xyes; then :
+
+ alpine_termdata=info
+ LIBS="$LIBS -ltinfo"
+
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for setupterm in -lncurses" >&5
+$as_echo_n "checking for setupterm in -lncurses... " >&6; }
+if ${ac_cv_lib_ncurses_setupterm+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lncurses $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char setupterm ();
+int
+main ()
+{
+return setupterm ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_ncurses_setupterm=yes
+else
+ ac_cv_lib_ncurses_setupterm=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ncurses_setupterm" >&5
+$as_echo "$ac_cv_lib_ncurses_setupterm" >&6; }
+if test "x$ac_cv_lib_ncurses_setupterm" = xyes; then :
+
+ alpine_termdata=info
+ LIBS="$LIBS -lncurses"
+
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for setupterm in -lcurses" >&5
+$as_echo_n "checking for setupterm in -lcurses... " >&6; }
+if ${ac_cv_lib_curses_setupterm+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lcurses $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char setupterm ();
+int
+main ()
+{
+return setupterm ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_curses_setupterm=yes
+else
+ ac_cv_lib_curses_setupterm=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_curses_setupterm" >&5
+$as_echo "$ac_cv_lib_curses_setupterm" >&6; }
+if test "x$ac_cv_lib_curses_setupterm" = xyes; then :
+
+ alpine_termdata=info
+ LIBS="$LIBS -lcurses"
+
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for tgetent in -ltermlib" >&5
+$as_echo_n "checking for tgetent in -ltermlib... " >&6; }
+if ${ac_cv_lib_termlib_tgetent+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ltermlib $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char tgetent ();
+int
+main ()
+{
+return tgetent ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_termlib_tgetent=yes
+else
+ ac_cv_lib_termlib_tgetent=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_termlib_tgetent" >&5
+$as_echo "$ac_cv_lib_termlib_tgetent" >&6; }
+if test "x$ac_cv_lib_termlib_tgetent" = xyes; then :
+
+ alpine_termdata=cap
+ LIBS="$LIBS -ltermlib"
+
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for tgetent in -ltermcap" >&5
+$as_echo_n "checking for tgetent in -ltermcap... " >&6; }
+if ${ac_cv_lib_termcap_tgetent+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ltermcap $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char tgetent ();
+int
+main ()
+{
+return tgetent ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_termcap_tgetent=yes
+else
+ ac_cv_lib_termcap_tgetent=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_termcap_tgetent" >&5
+$as_echo "$ac_cv_lib_termcap_tgetent" >&6; }
+if test "x$ac_cv_lib_termcap_tgetent" = xyes; then :
+
+ alpine_termdata=cap
+ LIBS="$LIBS -ltermcap"
+
+else
+
+ as_fn_error $? "Terminfo/termcap not found" "$LINENO" 5
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+case $alpine_termdata in
+ info)
+
+$as_echo "@%:@define HAS_TERMINFO 1" >>confdefs.h
+
+ ;;
+ cap)
+
+$as_echo "@%:@define HAS_TERMCAP 1" >>confdefs.h
+
+ ;;
+esac
+
+if test "$alpine_with_ldap" = "yes" ; then
+ alpine_has_ldap=
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ber_alloc in -llber" >&5
+$as_echo_n "checking for ber_alloc in -llber... " >&6; }
+if ${ac_cv_lib_lber_ber_alloc+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-llber $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ber_alloc ();
+int
+main ()
+{
+return ber_alloc ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_lber_ber_alloc=yes
+else
+ ac_cv_lib_lber_ber_alloc=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lber_ber_alloc" >&5
+$as_echo "$ac_cv_lib_lber_ber_alloc" >&6; }
+if test "x$ac_cv_lib_lber_ber_alloc" = xyes; then :
+
+ LIBS="$LIBS -llber"
+
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing ldap_init" >&5
+$as_echo_n "checking for library containing ldap_init... " >&6; }
+if ${ac_cv_search_ldap_init+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ldap_init ();
+int
+main ()
+{
+return ldap_init ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' ldap; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_ldap_init=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_ldap_init+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_ldap_init+:} false; then :
+
+else
+ ac_cv_search_ldap_init=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_ldap_init" >&5
+$as_echo "$ac_cv_search_ldap_init" >&6; }
+ac_res=$ac_cv_search_ldap_init
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+ alpine_has_ldap=yes
+
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing ldap_open" >&5
+$as_echo_n "checking for library containing ldap_open... " >&6; }
+if ${ac_cv_search_ldap_open+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ldap_open ();
+int
+main ()
+{
+return ldap_open ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' ldap; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_ldap_open=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_ldap_open+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_ldap_open+:} false; then :
+
+else
+ ac_cv_search_ldap_open=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_ldap_open" >&5
+$as_echo "$ac_cv_search_ldap_open" >&6; }
+ac_res=$ac_cv_search_ldap_open
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+ alpine_has_ldap=yes
+
+fi
+
+
+fi
+
+
+ if test "$alpine_has_ldap" = "yes" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Including LDAP Support" >&5
+$as_echo "$as_me: Including LDAP Support" >&6;}
+
+$as_echo "@%:@define ENABLE_LDAP /**/" >>confdefs.h
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we should define LDAP_DEPRECATED" >&5
+$as_echo_n "checking if we should define LDAP_DEPRECATED... " >&6; }
+ if test "$cross_compiling" = yes; then :
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cross compiling: not checking" >&5
+$as_echo "$as_me: WARNING: cross compiling: not checking" >&2;}
+
+
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#include <stdio.h>
+#include <string.h>
+#include <ldap.h>
+int main(void) {
+
+ if (LDAP_VENDOR_VERSION >= 20300)
+ exit(0);
+
+ exit(2);
+}
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+$as_echo "@%:@define LDAP_DEPRECATED 1" >>confdefs.h
+
+
+
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Cannot find LDAP functions! Excluding LDAP support." >&5
+$as_echo "$as_me: Cannot find LDAP functions! Excluding LDAP support." >&6;}
+ fi
+fi
+
+if test "x$alpine_SSLTYPE" != "xnone" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing SSL_library_init" >&5
+$as_echo_n "checking for library containing SSL_library_init... " >&6; }
+if ${ac_cv_search_SSL_library_init+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char SSL_library_init ();
+int
+main ()
+{
+return SSL_library_init ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' ssl; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_SSL_library_init=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_SSL_library_init+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_SSL_library_init+:} false; then :
+
+else
+ ac_cv_search_SSL_library_init=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_SSL_library_init" >&5
+$as_echo "$ac_cv_search_SSL_library_init" >&6; }
+ac_res=$ac_cv_search_SSL_library_init
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+ LIBS="$LIBS -lssl"
+
+fi
+
+ if test "x$alpine_SSLTYPE" = "xnone" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: OpenSSL libraries NOT found" >&5
+$as_echo "$as_me: OpenSSL libraries NOT found" >&6;}
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: OpenSSL libraries FOUND" >&5
+$as_echo "$as_me: OpenSSL libraries FOUND" >&6;}
+ fi
+fi
+
+if test "x$alpine_GSSTYPE" != "xnone" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing gss_init_sec_context" >&5
+$as_echo_n "checking for library containing gss_init_sec_context... " >&6; }
+if ${ac_cv_search_gss_init_sec_context+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char gss_init_sec_context ();
+int
+main ()
+{
+return gss_init_sec_context ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' gss gssapi gssapi_krb5; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_gss_init_sec_context=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_gss_init_sec_context+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_gss_init_sec_context+:} false; then :
+
+else
+ ac_cv_search_gss_init_sec_context=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_gss_init_sec_context" >&5
+$as_echo "$ac_cv_search_gss_init_sec_context" >&6; }
+ac_res=$ac_cv_search_gss_init_sec_context
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+ ac_fn_c_check_header_mongrel "$LINENO" "gssapi/gssapi_generic.h" "ac_cv_header_gssapi_gssapi_generic_h" "$ac_includes_default"
+if test "x$ac_cv_header_gssapi_gssapi_generic_h" = xyes; then :
+
+else
+
+ if test ! -d /usr/kerberos/include ; then
+ alpine_GSSTYPE="none"
+ alpine_gss_none_reason="header files not found"
+ fi
+
+fi
+
+
+
+else
+
+ alpine_GSSTYPE="none"
+ alpine_gss_none_reason="libraries not found"
+
+fi
+
+ if test -n "$alpine_gss_none_reason" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: NOT including Kerberos Support: $alpine_gss_none_reason" >&5
+$as_echo "$as_me: NOT including Kerberos Support: $alpine_gss_none_reason" >&6;}
+ fi
+fi
+
+if test -n "$WEB_BUILD" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing Tcl_Eval" >&5
+$as_echo_n "checking for library containing Tcl_Eval... " >&6; }
+if ${ac_cv_search_Tcl_Eval+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char Tcl_Eval ();
+int
+main ()
+{
+return Tcl_Eval ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' $alpine_TCLLIB tcl8.4 tcl8.3 tcl84 tcl83 tcl; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_Tcl_Eval=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_Tcl_Eval+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_Tcl_Eval+:} false; then :
+
+else
+ ac_cv_search_Tcl_Eval=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_Tcl_Eval" >&5
+$as_echo "$ac_cv_search_Tcl_Eval" >&6; }
+ac_res=$ac_cv_search_Tcl_Eval
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+else
+
+ WEB_BUILD=
+
+fi
+
+
+ if test -n "$alpine_TCLINC" ; then
+ as_ac_Header=`$as_echo "ac_cv_header_$alpine_TCLINC" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$alpine_TCLINC" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+
+else
+
+ WEB_BUILD=
+
+fi
+
+
+ if test -z "$WEB_BUILD" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Tcl Include file NOT found" >&5
+$as_echo "$as_me: Tcl Include file NOT found" >&6;}
+ fi
+ else
+ ac_fn_c_check_header_mongrel "$LINENO" "tcl.h" "ac_cv_header_tcl_h" "$ac_includes_default"
+if test "x$ac_cv_header_tcl_h" = xyes; then :
+
+else
+
+ for dir in tcl8.4 tcl8.3 tcl84 tcl83 ; do
+ as_ac_File=`$as_echo "ac_cv_file_/usr/include/$dir/tcl.h" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for /usr/include/$dir/tcl.h" >&5
+$as_echo_n "checking for /usr/include/$dir/tcl.h... " >&6; }
+if eval \${$as_ac_File+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ test "$cross_compiling" = yes &&
+ as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5
+if test -r "/usr/include/$dir/tcl.h"; then
+ eval "$as_ac_File=yes"
+else
+ eval "$as_ac_File=no"
+fi
+fi
+eval ac_res=\$$as_ac_File
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_File"\" = x"yes"; then :
+
+ found=yes
+
+fi
+
+ if test "$found" = "yes" ; then
+ CPPFLAGS="$CPPFLAGS -I/usr/include/$dir"
+ break
+ fi
+ done
+
+fi
+
+
+ fi
+fi
+
+if test x$alpine_REGEX != "xyes" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing regcomp" >&5
+$as_echo_n "checking for library containing regcomp... " >&6; }
+if ${ac_cv_search_regcomp+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char regcomp ();
+int
+main ()
+{
+return regcomp ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' posix regexp regex re; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_regcomp=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_regcomp+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_regcomp+:} false; then :
+
+else
+ ac_cv_search_regcomp=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_regcomp" >&5
+$as_echo "$ac_cv_search_regcomp" >&6; }
+ac_res=$ac_cv_search_regcomp
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+else
+
+ if test x$alpine_REGEX = "xno" ; then
+ as_fn_error $? "Unable to find system regex library" "$LINENO" 5
+ else
+ alpine_REGEX=yes
+ fi
+
+fi
+
+fi
+if test x$alpine_REGEX != "xyes" ; then
+ ac_fn_c_check_header_mongrel "$LINENO" "regex.h" "ac_cv_header_regex_h" "$ac_includes_default"
+if test "x$ac_cv_header_regex_h" = xyes; then :
+
+else
+
+ if test x$alpine_REGEX = "xno" ; then
+ as_fn_error $? "Unable to find system regex include file" "$LINENO" 5
+ else
+ alpine_REGEX=yes
+ fi
+
+fi
+
+
+fi
+
+$as_echo "@%:@define HAVE_REGEX_H 1" >>confdefs.h
+
+if test x$alpine_REGEX = "xyes" ; then
+ CPPFLAGS="$CPPFLAGS -I${top_builddir}/regex"
+ LDFLAGS="$LDFLAGS -L${top_builddir}/regex -lregex"
+ REGEX_BUILD=regex
+fi
+
+if test "x$with_pthread" != "xno" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread support" >&5
+$as_echo_n "checking for pthread support... " >&6; }
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+acx_pthread_ok=no
+
+# We used to check for pthread.h first, but this fails if pthread.h
+# requires special compiler flags (e.g. on True64 or Sequent).
+# It gets checked for in the link test anyway.
+
+# First of all, check if the user has set any of the PTHREAD_LIBS,
+# etcetera environment variables, and if threads linking works using
+# them:
+if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+ save_LIBS="$LIBS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS" >&5
+$as_echo_n "checking for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS... " >&6; }
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pthread_join ();
+int
+main ()
+{
+return pthread_join ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ acx_pthread_ok=yes
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $acx_pthread_ok" >&5
+$as_echo "$acx_pthread_ok" >&6; }
+ if test x"$acx_pthread_ok" = xno; then
+ PTHREAD_LIBS=""
+ PTHREAD_CFLAGS=""
+ fi
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+fi
+
+# We must check for the threads library under a number of different
+# names; the ordering is very important because some systems
+# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
+# libraries is broken (non-POSIX).
+
+# Create a list of thread flags to try. Items starting with a "-" are
+# C compiler flags, and other items are library names, except for "none"
+# which indicates that we try without any flags at all, and "pthread-config"
+# which is a program returning the flags for the Pth emulation library.
+
+acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
+
+# The ordering *is* (sometimes) important. Some notes on the
+# individual items follow:
+
+# pthreads: AIX (must check this before -lpthread)
+# none: in case threads are in libc; should be tried before -Kthread and
+# other compiler flags to prevent continual compiler warnings
+# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
+# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
+# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
+# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
+# -pthreads: Solaris/gcc
+# -mthreads: Mingw32/gcc, Lynx/gcc
+# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
+# doesn't hurt to check since this sometimes defines pthreads too;
+# also defines -D_REENTRANT)
+# ... -mt is also the pthreads flag for HP/aCC
+# pthread: Linux, etcetera
+# --thread-safe: KAI C++
+# pthread-config: use pthread-config program (for GNU Pth library)
+
+case "${host_cpu}-${host_os}" in
+ *solaris*)
+
+ # On Solaris (at least, for some versions), libc contains stubbed
+ # (non-functional) versions of the pthreads routines, so link-based
+ # tests will erroneously succeed. (We need to link with -pthreads/-mt/
+ # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
+ # a function called by this macro, so we could check for that, but
+ # who knows whether they'll stub that too in a future libc.) So,
+ # we'll just look for -pthreads and -lpthread first:
+
+ acx_pthread_flags="-pthreads pthread -mt -pthread $acx_pthread_flags"
+ ;;
+esac
+
+if test x"$acx_pthread_ok" = xno; then
+for flag in $acx_pthread_flags; do
+
+ case $flag in
+ none)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work without any flags" >&5
+$as_echo_n "checking whether pthreads work without any flags... " >&6; }
+ ;;
+
+ -*)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work with $flag" >&5
+$as_echo_n "checking whether pthreads work with $flag... " >&6; }
+ PTHREAD_CFLAGS="$flag"
+ ;;
+
+ pthread-config)
+ # Extract the first word of "pthread-config", so it can be a program name with args.
+set dummy pthread-config; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_acx_pthread_config+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$acx_pthread_config"; then
+ ac_cv_prog_acx_pthread_config="$acx_pthread_config" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_acx_pthread_config="yes"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_prog_acx_pthread_config" && ac_cv_prog_acx_pthread_config="no"
+fi
+fi
+acx_pthread_config=$ac_cv_prog_acx_pthread_config
+if test -n "$acx_pthread_config"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $acx_pthread_config" >&5
+$as_echo "$acx_pthread_config" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test x"$acx_pthread_config" = xno; then continue; fi
+ PTHREAD_CFLAGS="`pthread-config --cflags`"
+ PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
+ ;;
+
+ *)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for the pthreads library -l$flag" >&5
+$as_echo_n "checking for the pthreads library -l$flag... " >&6; }
+ PTHREAD_LIBS="-l$flag"
+ ;;
+ esac
+
+ save_LIBS="$LIBS"
+ save_CFLAGS="$CFLAGS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+ # Check for various functions. We must include pthread.h,
+ # since some functions may be macros. (On the Sequent, we
+ # need a special flag -Kthread to make this header compile.)
+ # We check for pthread_join because it is in -lpthread on IRIX
+ # while pthread_create is in libc. We check for pthread_attr_init
+ # due to DEC craziness with -lpthreads. We check for
+ # pthread_cleanup_push because it is one of the few pthread
+ # functions on Solaris that doesn't have a non-functional libc stub.
+ # We try pthread_create on general principles.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <pthread.h>
+int
+main ()
+{
+pthread_t th; pthread_join(th, 0);
+ pthread_attr_init(0); pthread_cleanup_push(0, 0);
+ pthread_create(0,0,0,0); pthread_cleanup_pop(0);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ acx_pthread_ok=yes
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $acx_pthread_ok" >&5
+$as_echo "$acx_pthread_ok" >&6; }
+ if test "x$acx_pthread_ok" = xyes; then
+ break;
+ fi
+
+ PTHREAD_LIBS=""
+ PTHREAD_CFLAGS=""
+done
+fi
+
+# Various other checks:
+if test "x$acx_pthread_ok" = xyes; then
+ save_LIBS="$LIBS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+ # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for joinable pthread attribute" >&5
+$as_echo_n "checking for joinable pthread attribute... " >&6; }
+ attr_name=unknown
+ for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <pthread.h>
+int
+main ()
+{
+int attr=$attr; return attr;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ attr_name=$attr; break
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ done
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $attr_name" >&5
+$as_echo "$attr_name" >&6; }
+ if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
+
+cat >>confdefs.h <<_ACEOF
+@%:@define PTHREAD_CREATE_JOINABLE $attr_name
+_ACEOF
+
+ fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if more special flags are required for pthreads" >&5
+$as_echo_n "checking if more special flags are required for pthreads... " >&6; }
+ flag=no
+ case "${host_cpu}-${host_os}" in
+ *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";;
+ *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
+ esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${flag}" >&5
+$as_echo "${flag}" >&6; }
+ if test "x$flag" != xno; then
+ PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
+ fi
+
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+
+ # More AIX lossage: must compile with xlc_r or cc_r
+ if test x"$GCC" != xyes; then
+ for ac_prog in xlc_r cc_r
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_PTHREAD_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$PTHREAD_CC"; then
+ ac_cv_prog_PTHREAD_CC="$PTHREAD_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_PTHREAD_CC="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+PTHREAD_CC=$ac_cv_prog_PTHREAD_CC
+if test -n "$PTHREAD_CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PTHREAD_CC" >&5
+$as_echo "$PTHREAD_CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$PTHREAD_CC" && break
+done
+test -n "$PTHREAD_CC" || PTHREAD_CC="${CC}"
+
+ else
+ PTHREAD_CC=$CC
+ fi
+else
+ PTHREAD_CC="$CC"
+fi
+
+
+
+
+
+# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
+if test x"$acx_pthread_ok" = xyes; then
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ case "$target" in
+ *openbsd*)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: pthread support on OpenBSD is unstable!" >&5
+$as_echo "$as_me: WARNING: pthread support on OpenBSD is unstable!" >&6;}
+ AM_CFLAGS="$AM_CFLAGS -pthread"
+ ;;
+ esac
+ LIBS="$PTHREAD_LIBS $LIBS"
+ AM_CFLAGS="$AM_CFLAGS $PTHREAD_CFLAGS"
+ CC="$PTHREAD_CC"
+
+$as_echo "@%:@define HAVE_PTHREAD 1" >>confdefs.h
+
+
+ :
+else
+ acx_pthread_ok=no
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing nanosleep" >&5
+$as_echo_n "checking for library containing nanosleep... " >&6; }
+if ${ac_cv_search_nanosleep+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char nanosleep ();
+int
+main ()
+{
+return nanosleep ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' rt posix4; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_nanosleep=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_nanosleep+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_nanosleep+:} false; then :
+
+else
+ ac_cv_search_nanosleep=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_nanosleep" >&5
+$as_echo "$ac_cv_search_nanosleep" >&6; }
+ac_res=$ac_cv_search_nanosleep
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+
+$as_echo "@%:@define HAVE_NANOSLEEP 1" >>confdefs.h
+
+
+fi
+
+fi
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if ${ac_cv_header_stdc+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_header_stdc=yes
+else
+ ac_cv_header_stdc=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "memchr" >/dev/null 2>&1; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "free" >/dev/null 2>&1; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+ if test "$cross_compiling" = yes; then :
+ :
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+ (('a' <= (c) && (c) <= 'i') \
+ || ('j' <= (c) && (c) <= 'r') \
+ || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ if (XOR (islower (i), ISLOWER (i))
+ || toupper (i) != TOUPPER (i))
+ return 2;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+$as_echo "@%:@define STDC_HEADERS 1" >>confdefs.h
+
+fi
+
+ac_header_dirent=no
+for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do
+ as_ac_Header=`$as_echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_hdr that defines DIR" >&5
+$as_echo_n "checking for $ac_hdr that defines DIR... " >&6; }
+if eval \${$as_ac_Header+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <$ac_hdr>
+
+int
+main ()
+{
+if ((DIR *) 0)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ eval "$as_ac_Header=yes"
+else
+ eval "$as_ac_Header=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$as_ac_Header
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define `$as_echo "HAVE_$ac_hdr" | $as_tr_cpp` 1
+_ACEOF
+
+ac_header_dirent=$ac_hdr; break
+fi
+
+done
+# Two versions of opendir et al. are in -ldir and -lx on SCO Xenix.
+if test $ac_header_dirent = dirent.h; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5
+$as_echo_n "checking for library containing opendir... " >&6; }
+if ${ac_cv_search_opendir+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char opendir ();
+int
+main ()
+{
+return opendir ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' dir; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_opendir=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_opendir+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_opendir+:} false; then :
+
+else
+ ac_cv_search_opendir=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5
+$as_echo "$ac_cv_search_opendir" >&6; }
+ac_res=$ac_cv_search_opendir
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5
+$as_echo_n "checking for library containing opendir... " >&6; }
+if ${ac_cv_search_opendir+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char opendir ();
+int
+main ()
+{
+return opendir ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' x; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_opendir=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_opendir+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_opendir+:} false; then :
+
+else
+ ac_cv_search_opendir=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5
+$as_echo "$ac_cv_search_opendir" >&6; }
+ac_res=$ac_cv_search_opendir
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stat file-mode macros are broken" >&5
+$as_echo_n "checking whether stat file-mode macros are broken... " >&6; }
+if ${ac_cv_header_stat_broken+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#if defined S_ISBLK && defined S_IFDIR
+extern char c1[S_ISBLK (S_IFDIR) ? -1 : 1];
+#endif
+
+#if defined S_ISBLK && defined S_IFCHR
+extern char c2[S_ISBLK (S_IFCHR) ? -1 : 1];
+#endif
+
+#if defined S_ISLNK && defined S_IFREG
+extern char c3[S_ISLNK (S_IFREG) ? -1 : 1];
+#endif
+
+#if defined S_ISSOCK && defined S_IFREG
+extern char c4[S_ISSOCK (S_IFREG) ? -1 : 1];
+#endif
+
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_header_stat_broken=no
+else
+ ac_cv_header_stat_broken=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stat_broken" >&5
+$as_echo "$ac_cv_header_stat_broken" >&6; }
+if test $ac_cv_header_stat_broken = yes; then
+
+$as_echo "@%:@define STAT_MACROS_BROKEN 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sys/wait.h that is POSIX.1 compatible" >&5
+$as_echo_n "checking for sys/wait.h that is POSIX.1 compatible... " >&6; }
+if ${ac_cv_header_sys_wait_h+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/wait.h>
+#ifndef WEXITSTATUS
+# define WEXITSTATUS(stat_val) ((unsigned int) (stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+
+int
+main ()
+{
+ int s;
+ wait (&s);
+ s = WIFEXITED (s) ? WEXITSTATUS (s) : 1;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_header_sys_wait_h=yes
+else
+ ac_cv_header_sys_wait_h=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_sys_wait_h" >&5
+$as_echo "$ac_cv_header_sys_wait_h" >&6; }
+if test $ac_cv_header_sys_wait_h = yes; then
+
+$as_echo "@%:@define HAVE_SYS_WAIT_H 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether time.h and sys/time.h may both be included" >&5
+$as_echo_n "checking whether time.h and sys/time.h may both be included... " >&6; }
+if ${ac_cv_header_time+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
+
+int
+main ()
+{
+if ((struct tm *) 0)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_header_time=yes
+else
+ ac_cv_header_time=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_time" >&5
+$as_echo "$ac_cv_header_time" >&6; }
+if test $ac_cv_header_time = yes; then
+
+$as_echo "@%:@define TIME_WITH_SYS_TIME 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether termios.h defines TIOCGWINSZ" >&5
+$as_echo_n "checking whether termios.h defines TIOCGWINSZ... " >&6; }
+if ${ac_cv_sys_tiocgwinsz_in_termios_h+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <termios.h>
+#ifdef TIOCGWINSZ
+ yes
+#endif
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "yes" >/dev/null 2>&1; then :
+ ac_cv_sys_tiocgwinsz_in_termios_h=yes
+else
+ ac_cv_sys_tiocgwinsz_in_termios_h=no
+fi
+rm -f conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_tiocgwinsz_in_termios_h" >&5
+$as_echo "$ac_cv_sys_tiocgwinsz_in_termios_h" >&6; }
+
+if test $ac_cv_sys_tiocgwinsz_in_termios_h != yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether sys/ioctl.h defines TIOCGWINSZ" >&5
+$as_echo_n "checking whether sys/ioctl.h defines TIOCGWINSZ... " >&6; }
+if ${ac_cv_sys_tiocgwinsz_in_sys_ioctl_h+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#ifdef TIOCGWINSZ
+ yes
+#endif
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "yes" >/dev/null 2>&1; then :
+ ac_cv_sys_tiocgwinsz_in_sys_ioctl_h=yes
+else
+ ac_cv_sys_tiocgwinsz_in_sys_ioctl_h=no
+fi
+rm -f conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_tiocgwinsz_in_sys_ioctl_h" >&5
+$as_echo "$ac_cv_sys_tiocgwinsz_in_sys_ioctl_h" >&6; }
+
+ if test $ac_cv_sys_tiocgwinsz_in_sys_ioctl_h = yes; then
+
+$as_echo "@%:@define GWINSZ_IN_SYS_IOCTL 1" >>confdefs.h
+
+ fi
+fi
+
+
+for ac_header in unistd.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "unistd.h" "ac_cv_header_unistd_h" "$ac_includes_default"
+if test "x$ac_cv_header_unistd_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_UNISTD_H 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in errno.h \
+ ctype.h \
+ fcntl.h \
+ signal.h \
+ setjmp.h \
+ memory.h \
+ sys/param.h \
+ sys/socket.h \
+ sys/uio.h \
+ sys/un.h \
+ limits.h \
+ wchar.h \
+ sys/poll.h \
+ stropts.h \
+ netdb.h \
+ syslog.h \
+ sys/syslog.h \
+ locale.h \
+ langinfo.h \
+ utime.h \
+ sys/utime.h \
+ pthread.h \
+ pwd.h \
+ assert.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+ac_fn_c_check_header_mongrel "$LINENO" "termios.h" "ac_cv_header_termios_h" "$ac_includes_default"
+if test "x$ac_cv_header_termios_h" = xyes; then :
+
+$as_echo "@%:@define HAS_TERMIOS 1" >>confdefs.h
+
+else
+
+ ac_fn_c_check_header_mongrel "$LINENO" "termio.h" "ac_cv_header_termio_h" "$ac_includes_default"
+if test "x$ac_cv_header_termio_h" = xyes; then :
+
+$as_echo "@%:@define HAS_TERMIO 1" >>confdefs.h
+
+else
+
+ ac_fn_c_check_header_mongrel "$LINENO" "sgtty.h" "ac_cv_header_sgtty_h" "$ac_includes_default"
+if test "x$ac_cv_header_sgtty_h" = xyes; then :
+
+$as_echo "@%:@define HAS_SGTTY 1" >>confdefs.h
+
+else
+
+ as_fn_error $? "Unable to figure out terminal control method" "$LINENO" 5
+
+fi
+
+
+
+fi
+
+
+
+fi
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking return type of signal handlers" >&5
+$as_echo_n "checking return type of signal handlers... " >&6; }
+if ${ac_cv_type_signal+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <signal.h>
+
+int
+main ()
+{
+return *(signal (0, 0)) (0) == 1;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_type_signal=int
+else
+ ac_cv_type_signal=void
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_signal" >&5
+$as_echo "$ac_cv_type_signal" >&6; }
+
+cat >>confdefs.h <<_ACEOF
+@%:@define RETSIGTYPE $ac_cv_type_signal
+_ACEOF
+
+
+ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default"
+if test "x$ac_cv_type_size_t" = xyes; then :
+
+else
+
+cat >>confdefs.h <<_ACEOF
+@%:@define size_t unsigned int
+_ACEOF
+
+fi
+
+ac_fn_c_check_type "$LINENO" "mode_t" "ac_cv_type_mode_t" "$ac_includes_default"
+if test "x$ac_cv_type_mode_t" = xyes; then :
+
+else
+
+cat >>confdefs.h <<_ACEOF
+@%:@define mode_t int
+_ACEOF
+
+fi
+
+ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default"
+if test "x$ac_cv_type_pid_t" = xyes; then :
+
+else
+
+cat >>confdefs.h <<_ACEOF
+@%:@define pid_t int
+_ACEOF
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for uid_t in sys/types.h" >&5
+$as_echo_n "checking for uid_t in sys/types.h... " >&6; }
+if ${ac_cv_type_uid_t+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "uid_t" >/dev/null 2>&1; then :
+ ac_cv_type_uid_t=yes
+else
+ ac_cv_type_uid_t=no
+fi
+rm -f conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_uid_t" >&5
+$as_echo "$ac_cv_type_uid_t" >&6; }
+if test $ac_cv_type_uid_t = no; then
+
+$as_echo "@%:@define uid_t int" >>confdefs.h
+
+
+$as_echo "@%:@define gid_t int" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether struct tm is in sys/time.h or time.h" >&5
+$as_echo_n "checking whether struct tm is in sys/time.h or time.h... " >&6; }
+if ${ac_cv_struct_tm+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <time.h>
+
+int
+main ()
+{
+struct tm tm;
+ int *p = &tm.tm_sec;
+ return !p;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_struct_tm=time.h
+else
+ ac_cv_struct_tm=sys/time.h
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_struct_tm" >&5
+$as_echo "$ac_cv_struct_tm" >&6; }
+if test $ac_cv_struct_tm = sys/time.h; then
+
+$as_echo "@%:@define TM_IN_SYS_TIME 1" >>confdefs.h
+
+fi
+
+
+ac_fn_c_check_type "$LINENO" "union wait" "ac_cv_type_union_wait" "$ac_includes_default"
+if test "x$ac_cv_type_union_wait" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_UNION_WAIT 1
+_ACEOF
+
+
+fi
+
+
+for ac_header in stdint.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "stdint.h" "ac_cv_header_stdint_h" "$ac_includes_default"
+if test "x$ac_cv_header_stdint_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_STDINT_H 1
+_ACEOF
+ uint16=uint16_t
+else
+
+ for ac_header in inttypes.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "inttypes.h" "ac_cv_header_inttypes_h" "$ac_includes_default"
+if test "x$ac_cv_header_inttypes_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_INTTYPES_H 1
+_ACEOF
+ uint16=uint16_t
+else
+
+ for ac_header in sys/types.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "sys/types.h" "ac_cv_header_sys_types_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_types_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_SYS_TYPES_H 1
+_ACEOF
+ uint16=u_int16_t
+else
+
+ # The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned short" >&5
+$as_echo_n "checking size of unsigned short... " >&6; }
+if ${ac_cv_sizeof_unsigned_short+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned short))" "ac_cv_sizeof_unsigned_short" "$ac_includes_default"; then :
+
+else
+ if test "$ac_cv_type_unsigned_short" = yes; then
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (unsigned short)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_unsigned_short=0
+ fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_short" >&5
+$as_echo "$ac_cv_sizeof_unsigned_short" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SIZEOF_UNSIGNED_SHORT $ac_cv_sizeof_unsigned_short
+_ACEOF
+
+
+ if test $ac_cv_sizeof_unsigned_short -eq 2 ; then
+ uint16="unsigned short"
+ else
+ # The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned int" >&5
+$as_echo_n "checking size of unsigned int... " >&6; }
+if ${ac_cv_sizeof_unsigned_int+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned int))" "ac_cv_sizeof_unsigned_int" "$ac_includes_default"; then :
+
+else
+ if test "$ac_cv_type_unsigned_int" = yes; then
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (unsigned int)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_unsigned_int=0
+ fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_int" >&5
+$as_echo "$ac_cv_sizeof_unsigned_int" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SIZEOF_UNSIGNED_INT $ac_cv_sizeof_unsigned_int
+_ACEOF
+
+
+ if $ac_cv_sizeof_unsigned_int -eq 2 ; then
+ uint16="unsigned int"
+ else
+ as_fn_error $? "Unable to determine 16 bit integer type" "$LINENO" 5
+ fi
+ fi
+
+fi
+
+done
+
+
+fi
+
+done
+
+
+fi
+
+done
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define UINT16 $uint16
+_ACEOF
+
+
+for ac_header in stdint.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "stdint.h" "ac_cv_header_stdint_h" "$ac_includes_default"
+if test "x$ac_cv_header_stdint_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_STDINT_H 1
+_ACEOF
+ uint32=uint32_t
+else
+
+ for ac_header in inttypes.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "inttypes.h" "ac_cv_header_inttypes_h" "$ac_includes_default"
+if test "x$ac_cv_header_inttypes_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_INTTYPES_H 1
+_ACEOF
+ uint32=uint32_t
+else
+
+ for ac_header in sys/types.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "sys/types.h" "ac_cv_header_sys_types_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_types_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_SYS_TYPES_H 1
+_ACEOF
+ uint32=u_int32_t
+else
+
+ # The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned int" >&5
+$as_echo_n "checking size of unsigned int... " >&6; }
+if ${ac_cv_sizeof_unsigned_int+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned int))" "ac_cv_sizeof_unsigned_int" "$ac_includes_default"; then :
+
+else
+ if test "$ac_cv_type_unsigned_int" = yes; then
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (unsigned int)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_unsigned_int=0
+ fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_int" >&5
+$as_echo "$ac_cv_sizeof_unsigned_int" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SIZEOF_UNSIGNED_INT $ac_cv_sizeof_unsigned_int
+_ACEOF
+
+
+ if test $ac_cv_sizeof_unsigned_int -eq 4 ; then
+ uint32="unsigned int"
+ else
+ # The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned long" >&5
+$as_echo_n "checking size of unsigned long... " >&6; }
+if ${ac_cv_sizeof_unsigned_long+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned long))" "ac_cv_sizeof_unsigned_long" "$ac_includes_default"; then :
+
+else
+ if test "$ac_cv_type_unsigned_long" = yes; then
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (unsigned long)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_unsigned_long=0
+ fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_long" >&5
+$as_echo "$ac_cv_sizeof_unsigned_long" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SIZEOF_UNSIGNED_LONG $ac_cv_sizeof_unsigned_long
+_ACEOF
+
+
+ if $ac_cv_sizeof_unsigned_long -eq 4 ; then
+ uint32="unsigned long"
+ else
+ as_fn_error $? "Unable to determine 32 bit integer type" "$LINENO" 5
+ fi
+ fi
+
+fi
+
+done
+
+
+fi
+
+done
+
+
+fi
+
+done
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define UINT32 $uint32
+_ACEOF
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking argument pointer type of qsort compare function and base" >&5
+$as_echo_n "checking argument pointer type of qsort compare function and base... " >&6; }
+if ${ac_cv_func_qsort_argtype+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#if HAVE_STDLIB_H
+#include "stdlib.h"
+#endif
+
+extern void *base;
+extern sortf(const void *, const void *);
+int sortf(a, b)
+ const void *a;
+ const void *b; { return 0; }
+
+int
+main ()
+{
+
+qsort(base, 2, sizeof(char *), sortf);
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_func_qsort_argtype=void
+else
+ ac_cv_func_qsort_argtype=char
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_qsort_argtype" >&5
+$as_echo "$ac_cv_func_qsort_argtype" >&6; }
+
+cat >>confdefs.h <<_ACEOF
+@%:@define qsort_t $ac_cv_func_qsort_argtype
+_ACEOF
+
+
+
+for ac_header in sys/select.h sys/socket.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking types of arguments for select" >&5
+$as_echo_n "checking types of arguments for select... " >&6; }
+if ${ac_cv_func_select_args+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ for ac_arg234 in 'fd_set *' 'int *' 'void *'; do
+ for ac_arg1 in 'int' 'size_t' 'unsigned long int' 'unsigned int'; do
+ for ac_arg5 in 'struct timeval *' 'const struct timeval *'; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_includes_default
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+
+int
+main ()
+{
+extern int select ($ac_arg1,
+ $ac_arg234, $ac_arg234, $ac_arg234,
+ $ac_arg5);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_func_select_args="$ac_arg1,$ac_arg234,$ac_arg5"; break 3
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ done
+ done
+done
+# Provide a safe default value.
+: "${ac_cv_func_select_args=int,int *,struct timeval *}"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_select_args" >&5
+$as_echo "$ac_cv_func_select_args" >&6; }
+ac_save_IFS=$IFS; IFS=','
+set dummy `echo "$ac_cv_func_select_args" | sed 's/\*/\*/g'`
+IFS=$ac_save_IFS
+shift
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SELECT_TYPE_ARG1 $1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SELECT_TYPE_ARG234 ($2)
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SELECT_TYPE_ARG5 ($3)
+_ACEOF
+
+rm -f conftest*
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working strcoll" >&5
+$as_echo_n "checking for working strcoll... " >&6; }
+if ${ac_cv_func_strcoll_works+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then :
+ ac_cv_func_strcoll_works=no
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+return (strcoll ("abc", "def") >= 0 ||
+ strcoll ("ABC", "DEF") >= 0 ||
+ strcoll ("123", "456") >= 0)
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ac_cv_func_strcoll_works=yes
+else
+ ac_cv_func_strcoll_works=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_strcoll_works" >&5
+$as_echo "$ac_cv_func_strcoll_works" >&6; }
+if test $ac_cv_func_strcoll_works = yes; then
+
+$as_echo "@%:@define HAVE_STRCOLL 1" >>confdefs.h
+
+fi
+
+
+
+for ac_header in vfork.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "vfork.h" "ac_cv_header_vfork_h" "$ac_includes_default"
+if test "x$ac_cv_header_vfork_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define HAVE_VFORK_H 1
+_ACEOF
+
+fi
+
+done
+
+for ac_func in fork vfork
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+if test "x$ac_cv_func_fork" = xyes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working fork" >&5
+$as_echo_n "checking for working fork... " >&6; }
+if ${ac_cv_func_fork_works+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then :
+ ac_cv_func_fork_works=cross
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+
+ /* By Ruediger Kuhlmann. */
+ return fork () < 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ac_cv_func_fork_works=yes
+else
+ ac_cv_func_fork_works=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_fork_works" >&5
+$as_echo "$ac_cv_func_fork_works" >&6; }
+
+else
+ ac_cv_func_fork_works=$ac_cv_func_fork
+fi
+if test "x$ac_cv_func_fork_works" = xcross; then
+ case $host in
+ *-*-amigaos* | *-*-msdosdjgpp*)
+ # Override, as these systems have only a dummy fork() stub
+ ac_cv_func_fork_works=no
+ ;;
+ *)
+ ac_cv_func_fork_works=yes
+ ;;
+ esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5
+$as_echo "$as_me: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&2;}
+fi
+ac_cv_func_vfork_works=$ac_cv_func_vfork
+if test "x$ac_cv_func_vfork" = xyes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working vfork" >&5
+$as_echo_n "checking for working vfork... " >&6; }
+if ${ac_cv_func_vfork_works+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then :
+ ac_cv_func_vfork_works=cross
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+/* Thanks to Paul Eggert for this test. */
+$ac_includes_default
+#include <sys/wait.h>
+#ifdef HAVE_VFORK_H
+# include <vfork.h>
+#endif
+/* On some sparc systems, changes by the child to local and incoming
+ argument registers are propagated back to the parent. The compiler
+ is told about this with #include <vfork.h>, but some compilers
+ (e.g. gcc -O) don't grok <vfork.h>. Test for this by using a
+ static variable whose address is put into a register that is
+ clobbered by the vfork. */
+static void
+#ifdef __cplusplus
+sparc_address_test (int arg)
+# else
+sparc_address_test (arg) int arg;
+#endif
+{
+ static pid_t child;
+ if (!child) {
+ child = vfork ();
+ if (child < 0) {
+ perror ("vfork");
+ _exit(2);
+ }
+ if (!child) {
+ arg = getpid();
+ write(-1, "", 0);
+ _exit (arg);
+ }
+ }
+}
+
+int
+main ()
+{
+ pid_t parent = getpid ();
+ pid_t child;
+
+ sparc_address_test (0);
+
+ child = vfork ();
+
+ if (child == 0) {
+ /* Here is another test for sparc vfork register problems. This
+ test uses lots of local variables, at least as many local
+ variables as main has allocated so far including compiler
+ temporaries. 4 locals are enough for gcc 1.40.3 on a Solaris
+ 4.1.3 sparc, but we use 8 to be safe. A buggy compiler should
+ reuse the register of parent for one of the local variables,
+ since it will think that parent can't possibly be used any more
+ in this routine. Assigning to the local variable will thus
+ munge parent in the parent process. */
+ pid_t
+ p = getpid(), p1 = getpid(), p2 = getpid(), p3 = getpid(),
+ p4 = getpid(), p5 = getpid(), p6 = getpid(), p7 = getpid();
+ /* Convince the compiler that p..p7 are live; otherwise, it might
+ use the same hardware register for all 8 local variables. */
+ if (p != p1 || p != p2 || p != p3 || p != p4
+ || p != p5 || p != p6 || p != p7)
+ _exit(1);
+
+ /* On some systems (e.g. IRIX 3.3), vfork doesn't separate parent
+ from child file descriptors. If the child closes a descriptor
+ before it execs or exits, this munges the parent's descriptor
+ as well. Test for this by closing stdout in the child. */
+ _exit(close(fileno(stdout)) != 0);
+ } else {
+ int status;
+ struct stat st;
+
+ while (wait(&status) != child)
+ ;
+ return (
+ /* Was there some problem with vforking? */
+ child < 0
+
+ /* Did the child fail? (This shouldn't happen.) */
+ || status
+
+ /* Did the vfork/compiler bug occur? */
+ || parent != getpid()
+
+ /* Did the file descriptor bug occur? */
+ || fstat(fileno(stdout), &st) != 0
+ );
+ }
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ac_cv_func_vfork_works=yes
+else
+ ac_cv_func_vfork_works=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_vfork_works" >&5
+$as_echo "$ac_cv_func_vfork_works" >&6; }
+
+fi;
+if test "x$ac_cv_func_fork_works" = xcross; then
+ ac_cv_func_vfork_works=$ac_cv_func_vfork
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5
+$as_echo "$as_me: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&2;}
+fi
+
+if test "x$ac_cv_func_vfork_works" = xyes; then
+
+$as_echo "@%:@define HAVE_WORKING_VFORK 1" >>confdefs.h
+
+else
+
+$as_echo "@%:@define vfork fork" >>confdefs.h
+
+fi
+if test "x$ac_cv_func_fork_works" = xyes; then
+
+$as_echo "@%:@define HAVE_WORKING_FORK 1" >>confdefs.h
+
+fi
+
+for ac_func in strchr \
+ memcpy \
+ strtol \
+ strtoul \
+ select \
+ poll \
+ qsort \
+ getuid \
+ getpwuid \
+ getpwnam \
+ gettimeofday \
+ tmpfile \
+ uname \
+ rename \
+ read \
+ signal \
+ setjmp \
+ chown \
+ wait4 \
+ waitpid \
+ wait \
+ srandom \
+ popen \
+ pclose \
+ fsync \
+ truncate \
+ listen \
+ wcwidth \
+ mbstowcs \
+ wcrtomb \
+ putenv \
+ setenv
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing gethostname" >&5
+$as_echo_n "checking for library containing gethostname... " >&6; }
+if ${ac_cv_search_gethostname+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char gethostname ();
+int
+main ()
+{
+return gethostname ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' nsl; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_gethostname=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_gethostname+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_gethostname+:} false; then :
+
+else
+ ac_cv_search_gethostname=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_gethostname" >&5
+$as_echo "$ac_cv_search_gethostname" >&6; }
+ac_res=$ac_cv_search_gethostname
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing socket" >&5
+$as_echo_n "checking for library containing socket... " >&6; }
+if ${ac_cv_search_socket+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char socket ();
+int
+main ()
+{
+return socket ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' socket; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_socket=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_socket+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_socket+:} false; then :
+
+else
+ ac_cv_search_socket=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_socket" >&5
+$as_echo "$ac_cv_search_socket" >&6; }
+ac_res=$ac_cv_search_socket
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+else
+
+ WEB_BUILD=
+
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing bind" >&5
+$as_echo_n "checking for library containing bind... " >&6; }
+if ${ac_cv_search_bind+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char bind ();
+int
+main ()
+{
+return bind ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' bind; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_bind=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_bind+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_bind+:} false; then :
+
+else
+ ac_cv_search_bind=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_bind" >&5
+$as_echo "$ac_cv_search_bind" >&6; }
+ac_res=$ac_cv_search_bind
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+else
+
+ WEB_BUILD=
+
+fi
+
+
+for ac_func in sigaction sigemptyset sigaddset sigprocmask
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+
+$as_echo "@%:@define POSIX_SIGNALS /**/" >>confdefs.h
+
+
+else
+
+ for ac_func in sigset sigrelse
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+@%:@define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+
+$as_echo "@%:@define SYSV_SIGNALS /**/" >>confdefs.h
+
+
+fi
+done
+
+
+fi
+done
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing syslog" >&5
+$as_echo_n "checking for library containing syslog... " >&6; }
+if ${ac_cv_search_syslog+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char syslog ();
+int
+main ()
+{
+return syslog ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' bsd socket inet; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_syslog=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_syslog+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_syslog+:} false; then :
+
+else
+ ac_cv_search_syslog=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_syslog" >&5
+$as_echo "$ac_cv_search_syslog" >&6; }
+ac_res=$ac_cv_search_syslog
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+
+$as_echo "@%:@define HAVE_SYSLOG 1" >>confdefs.h
+
+
+fi
+
+
+
+case "$host" in
+ *-linux-gnu*|*-k*bsd*-gnu*)
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ if test -f /etc/fedora-release ; then
+ systype="LFD"
+ if test -d /etc/pki/tls ; then
+ alpine_c_client_target="lfd"
+ else
+ alpine_c_client_target="lrh"
+ fi
+ elif test -f /etc/mandrake-release ; then
+ systype="LMD"
+ alpine_c_client_target="lmd"
+ elif test -f /etc/redhat-release -o -f /etc/redhat_version ; then
+ systype="LRH"
+ if test -d /etc/pki/tls ; then
+ alpine_c_client_target="lr5"
+ else
+ alpine_c_client_target="lrh"
+ fi
+ elif test -f /etc/debian_release -o -f /etc/debian_version ; then
+ if test -d /etc/osso-af-init ; then
+ systype="LN8"
+ alpine_c_client_target="ln8"
+ else
+ systype="DEB"
+ alpine_c_client_target="ldb"
+ fi
+ elif test -f /etc/SuSE-release ; then
+ systype="LSU"
+ alpine_c_client_target="lsu"
+ else
+ systype="LNX"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pam_start in -lpam" >&5
+$as_echo_n "checking for pam_start in -lpam... " >&6; }
+if ${ac_cv_lib_pam_pam_start+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpam $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pam_start ();
+int
+main ()
+{
+return pam_start ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_pam_pam_start=yes
+else
+ ac_cv_lib_pam_pam_start=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pam_pam_start" >&5
+$as_echo "$ac_cv_lib_pam_pam_start" >&6; }
+if test "x$ac_cv_lib_pam_pam_start" = xyes; then :
+
+ alpine_c_client_target="lnp"
+
+else
+
+ if test -f /etc/shadow ; then
+ alpine_c_client_target="slx"
+ else
+ alpine_c_client_target="lnx"
+ fi
+
+fi
+
+ fi
+ ;;
+ *-apple-darwin*)
+ systype="OSX"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ LIBS="$LIBS -framework Carbon -framework ApplicationServices -framework Security"
+ AM_CFLAGS="$AM_CFLAGS -Dbsd"
+
+$as_echo "@%:@define OSX_TARGET 1" >>confdefs.h
+
+ case "$alpine_os_credential_cache" in
+ no)
+ ;;
+ *)
+
+$as_echo "@%:@define APPLEKEYCHAIN 1" >>confdefs.h
+
+ ;;
+ esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pam_start in -lpam" >&5
+$as_echo_n "checking for pam_start in -lpam... " >&6; }
+if ${ac_cv_lib_pam_pam_start+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpam $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pam_start ();
+int
+main ()
+{
+return pam_start ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_pam_pam_start=yes
+else
+ ac_cv_lib_pam_pam_start=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pam_pam_start" >&5
+$as_echo "$ac_cv_lib_pam_pam_start" >&6; }
+if test "x$ac_cv_lib_pam_pam_start" = xyes; then :
+
+ alpine_c_client_target="oxp"
+
+else
+
+ alpine_c_client_target="osx"
+
+fi
+
+ ;;
+ *-*-solaris*)
+ if test x$GCC = "xyes" ; then
+ systype="GSO"
+ alpine_c_client_target="gso"
+ else
+ systype="SOC"
+ alpine_c_client_target="soc"
+
+$as_echo "@%:@define __EXTENSIONS__ 1" >>confdefs.h
+
+ fi
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ ;;
+ *-*-sunos4*)
+ systype="SUN"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="s40"
+ ;;
+ *-*-sco3.2v5*)
+ if test x$GCC = "xyes" ; then
+ systype="GO5"
+ alpine_c_client_target="go5"
+ else
+ systype="SC5"
+ alpine_c_client_target="sc5"
+ fi
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ ;;
+ *-next-*)
+ systype="NXT"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="nx3"
+ ;;
+ *-*-netbsd*)
+ systype="NEB"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="neb"
+ ;;
+ *-*-dragonfly*)
+ systype="DFB"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="neb"
+ ;;
+ *-*-bsdi*)
+ systype="BSI"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="bsi"
+ ;;
+ *-*-freebsd*)
+ systype="BSF"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="bsf"
+ LIBS="$LIBS $LIBINTL"
+ ;;
+ *-*-openbsd*)
+ systype="BSO"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="bso"
+ LIBS="$LIBS $LIBINTL"
+ ;;
+ *-*-aix5*)
+ systype="A52"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="a52"
+ ;;
+ *-*-aix4*)
+ systype="A41"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="a41"
+ ;;
+ *-*-aix3*)
+ systype="A32"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="a32"
+ ;;
+ *-*UNIX_SV | *-*-sysv5UnixWare7* | *-*OpenUNIX*)
+ systype="UW2"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="uw2"
+ ;;
+ *-*-osf5*)
+ systype="OSF"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="osf"
+ ;;
+ *-*-cygwin)
+ systype="CYG"
+ alpine_path_delim="\\\\"
+ alpine_mode_readonly="(S_IREAD | S_IWRITE)"
+ alpine_c_client_target="cyg"
+ LIBS="$LIBS $LIBINTL"
+ ;;
+ windows* | *-*-pw32*)
+ systype="WNT"
+ alpine_path_delim="\\"
+ alpine_mode_readonly="(S_IREAD | S_IWRITE)"
+ alpine_c_client_target="wnt"
+
+$as_echo "@%:@define _WINDOWS 1" >>confdefs.h
+
+ ;;
+ *)
+ as_fn_error $? "Unrecognized system: $host" "$LINENO" 5
+ ;;
+esac
+
+if test -n "$alpine_with_local_maildir" ; then
+ alpine_local_maildir=$alpine_with_local_maildir
+elif test -d /var/spool/mail ; then
+ alpine_local_maildir="/var/spool/mail"
+elif test -d /var/mail ; then
+ alpine_local_maildir="/var/mail"
+else
+ alpine_local_maildir="/usr/spool/mail"
+fi
+
+if test -n "$alpine_with_c_client_target" ; then
+ alpine_c_client_target=$alpine_with_c_client_target
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SYSTYPE "$systype"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define C_FILESEP '$alpine_path_delim'
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define S_FILESEP "$alpine_path_delim"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define MAILDIR "$alpine_local_maildir"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define MODE_READONLY $alpine_mode_readonly
+_ACEOF
+
+
+C_CLIENT_TARGET=$alpine_c_client_target
+
+C_CLIENT_WITH_IPV6=$c_client_ip6
+
+if test "x$alpine_SSLTYPE" = "xnone" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * NOT Including SSL Support" >&5
+$as_echo "$as_me: * * * NOT Including SSL Support" >&6;}
+ c_client_specials="${c_client_specials}SSLTYPE=none "
+else
+
+ if test -n "$alpine_SSLCERTS" -a -d "$alpine_SSLCERTS" ; then
+ certdir="$alpine_SSLCERTS"
+ elif test -n "$alpine_SSLDIR" -a -d "${alpine_SSLDIR}/certs" ; then
+ certdir="${alpine_SSLDIR}/certs"
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: SSL Problem: certificate directory not found" >&5
+$as_echo "$as_me: SSL Problem: certificate directory not found" >&6;}
+ fi
+
+ if test "x$with_smime" != "xno" ; then
+ if test -n "$certdir" ; then
+
+$as_echo "@%:@define SMIME /**/" >>confdefs.h
+
+
+cat >>confdefs.h <<_ACEOF
+@%:@define SMIME_SSLCERTS "$certdir"
+_ACEOF
+
+ fi
+ fi
+
+ if test ! -f ${certdir}/factory.pem ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * SSL file \"${certdir}/factory.pem\" is missing." >&5
+$as_echo "$as_me: * * * SSL file \"${certdir}/factory.pem\" is missing." >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * This might indicate that CA certs did not get properly" >&5
+$as_echo "$as_me: * * * This might indicate that CA certs did not get properly" >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * installed. If you get certificate validation failures" >&5
+$as_echo "$as_me: * * * installed. If you get certificate validation failures" >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * in Alpine, this might be the reason for them." >&5
+$as_echo "$as_me: * * * in Alpine, this might be the reason for them." >&6;}
+ fi
+
+ if test -z "`ls ${certdir} | $EGREP '^@<:@0-9A-Fa-f@:>@{8}\.@<:@0-9@:>@'`" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * No 8-hexdigit symlinks in certificate directory \"${certdir}\"." >&5
+$as_echo "$as_me: * * * No 8-hexdigit symlinks in certificate directory \"${certdir}\"." >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * This might indicate that CA certs did not get properly" >&5
+$as_echo "$as_me: * * * This might indicate that CA certs did not get properly" >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * installed. If you get certificate validation failures" >&5
+$as_echo "$as_me: * * * installed. If you get certificate validation failures" >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * in Alpine, this might be the reason for them." >&5
+$as_echo "$as_me: * * * in Alpine, this might be the reason for them." >&6;}
+ fi
+
+ if test -n "$alpine_SSLDIR" ; then
+ c_client_specials="${c_client_specials}SSLDIR=$alpine_SSLDIR "
+ fi
+
+ if test -n "$alpine_SSLCERTS" ; then
+ c_client_specials="${c_client_specials}SSLCERTS=$alpine_SSLCERTS "
+ fi
+
+ if test -n "$alpine_SSLINCLUDE" ; then
+ c_client_specials="${c_client_specials}SSLINCLUDE=$alpine_SSLINCLUDE "
+ fi
+
+ if test -n "$alpine_SSLLIB" ; then
+ c_client_specials="${c_client_specials}SSLLIB=$alpine_SSLLIB "
+ fi
+
+fi
+
+if test "x$alpine_GSSTYPE" != "xnone" ; then
+ c_client_specials="${c_client_specials}EXTRAAUTHENTICATORS=gss "
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * Including Kerberos5 functionality" >&5
+$as_echo "$as_me: * * * Including Kerberos5 functionality" >&6;}
+fi
+
+if test -n "$CPPFLAGS" ; then
+ alpine_c_client_cflags="$alpine_c_client_cflags ${CPPFLAGS}"
+fi
+if test -n "$CFLAGS" ; then
+ alpine_c_client_cflags="$alpine_c_client_cflags ${CFLAGS}"
+fi
+
+if test -n "$alpine_c_client_cflags" ; then
+ C_CLIENT_CFLAGS=EXTRACFLAGS=\"$alpine_c_client_cflags\"
+
+fi
+
+if test -n "$LDFLAGS" ; then
+ alpine_c_client_ldflags="$alpine_c_client_ldflags ${LDFLAGS}"
+fi
+if test -n "$LIBS" ; then
+ alpine_c_client_ldflags="$alpine_c_client_ldflags ${LIBS}"
+fi
+
+if test -n "$alpine_c_client_ldflags" ; then
+ C_CLIENT_LDFLAGS=EXTRALDFLAGS=\"$alpine_c_client_ldflags\"
+
+fi
+
+if test -n "$alpine_c_client_gccoptlevel" ; then
+ C_CLIENT_GCCOPTLEVEL=GCCOPTLEVEL=\"$alpine_c_client_gccoptlevel\"
+
+fi
+
+C_CLIENT_SPECIALS=$c_client_specials
+
+
+if test -z "$WEB_BUILD" ; then
+ WEB_PUBCOOKIE_BUILD=
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * TCL libraries could not be found." >&5
+$as_echo "$as_me: * * * TCL libraries could not be found." >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * WEB ALPINE COMPONENT WILL NOT BE BUILT." >&5
+$as_echo "$as_me: * * * WEB ALPINE COMPONENT WILL NOT BE BUILT." >&6;}
+else
+ if test -n "$WEB_PUBCOOKIE_BUILD" ; then
+ if test "x$alpine_GSSTYPE" = "xnone" ; then
+ WEB_PUBCOOKIE_BUILD=
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * Kerberos5 support not found." >&5
+$as_echo "$as_me: * * * Kerberos5 support not found." >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * WEB ALPINE PUBCOOKIE COMPONENT WILL NOT BE BUILT." >&5
+$as_echo "$as_me: * * * WEB ALPINE PUBCOOKIE COMPONENT WILL NOT BE BUILT." >&6;}
+ elif test -z "$WEB_BINDIR" ; then
+ WEB_PUBCOOKIE_BUILD=
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * Web Alpine binary directory not provided." >&5
+$as_echo "$as_me: * * * Web Alpine binary directory not provided." >&6;}
+ as_fn_error $? "* * * --with-pubcookie requires --with-web-bin=PATH.
+ Please re-run configure with these options:
+ --with-pubcookie --with-web-bin=/usr/local/libexec/alpine/bin" "$LINENO" 5
+ else
+
+$as_echo "@%:@define PUBCOOKIE 1" >>confdefs.h
+
+ WEB_PUBCOOKIE_LIB=../pubcookie/libauthgssproxy.a
+ WEB_PUBCOOKIE_LINK=gssapi_proxy.l
+ fi
+ fi
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+ac_config_files="$ac_config_files m4/Makefile po/Makefile.in regex/Makefile pith/osdep/Makefile pith/charconv/Makefile pith/Makefile pico/osdep/Makefile pico/Makefile alpine/osdep/Makefile alpine/Makefile web/src/Makefile web/src/pubcookie/Makefile web/src/alpined.d/Makefile Makefile"
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems. If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+ for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+ *) { eval $ac_var=; unset $ac_var;} ;;
+ esac ;;
+ esac
+ done
+
+ (set) 2>&1 |
+ case $as_nl`(ac_space=' '; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ # `set' does not quote correctly, so add quotes: double-quote
+ # substitution turns \\\\ into \\, and sed turns \\ into \.
+ sed -n \
+ "s/'/'\\\\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+ ;; #(
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+) |
+ sed '
+ /^ac_cv_env_/b end
+ t clear
+ :clear
+ s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+ t end
+ s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+ :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+ if test -w "$cache_file"; then
+ if test "x$cache_file" != "x/dev/null"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+$as_echo "$as_me: updating cache $cache_file" >&6;}
+ if test ! -f "$cache_file" || test -h "$cache_file"; then
+ cat confcache >"$cache_file"
+ else
+ case $cache_file in #(
+ */* | ?:*)
+ mv -f confcache "$cache_file"$$ &&
+ mv -f "$cache_file"$$ "$cache_file" ;; #(
+ *)
+ mv -f confcache "$cache_file" ;;
+ esac
+ fi
+ fi
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+ fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+U=
+for ac_i in : $LIB@&t@OBJS; do test "x$ac_i" = x: && continue
+ # 1. Remove the extension, and $U if already installed.
+ ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+ ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
+ # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR
+ # will be set to the directory where LIBOBJS objects are built.
+ as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+ as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIB@&t@OBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+ if test -n "$EXEEXT"; then
+ am__EXEEXT_TRUE=
+ am__EXEEXT_FALSE='#'
+else
+ am__EXEEXT_TRUE='#'
+ am__EXEEXT_FALSE=
+fi
+
+if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then
+ as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then
+ as_fn_error $? "conditional \"AMDEP\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
+ as_fn_error $? "conditional \"am__fastdepCC\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+
+: "${CONFIG_STATUS=./config.status}"
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
+as_write_fail=0
+cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in @%:@(
+ *posix*) :
+ set -o posix ;; @%:@(
+ *) :
+ ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='print -r --'
+ as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='printf %s\n'
+ as_echo_n='printf %s'
+else
+ if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+ as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+ as_echo_n='/usr/ucb/echo -n'
+ else
+ as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+ as_echo_n_body='eval
+ arg=$1;
+ case $arg in @%:@(
+ *"$as_nl"*)
+ expr "X$arg" : "X\\(.*\\)$as_nl";
+ arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+ esac;
+ expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+ '
+ export as_echo_n_body
+ as_echo_n='sh -c $as_echo_n_body as_echo'
+ fi
+ export as_echo_body
+ as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+as_myself=
+case $0 in @%:@((
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there. '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+
+@%:@ as_fn_error STATUS ERROR [LINENO LOG_FD]
+@%:@ ----------------------------------------
+@%:@ Output "`basename @S|@0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+@%:@ provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+@%:@ script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+ as_status=$1; test $as_status -eq 0 && as_status=1
+ if test "$4"; then
+ as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+ fi
+ $as_echo "$as_me: error: $2" >&2
+ as_fn_exit $as_status
+} @%:@ as_fn_error
+
+
+@%:@ as_fn_set_status STATUS
+@%:@ -----------------------
+@%:@ Set @S|@? to STATUS, without forking.
+as_fn_set_status ()
+{
+ return $1
+} @%:@ as_fn_set_status
+
+@%:@ as_fn_exit STATUS
+@%:@ -----------------
+@%:@ Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+ set +e
+ as_fn_set_status $1
+ exit $1
+} @%:@ as_fn_exit
+
+@%:@ as_fn_unset VAR
+@%:@ ---------------
+@%:@ Portably unset VAR.
+as_fn_unset ()
+{
+ { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+@%:@ as_fn_append VAR VALUE
+@%:@ ----------------------
+@%:@ Append the text in VALUE to the end of the definition contained in VAR. Take
+@%:@ advantage of any shell optimizations that allow amortized linear growth over
+@%:@ repeated appends, instead of the typical quadratic growth present in naive
+@%:@ implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+ eval 'as_fn_append ()
+ {
+ eval $1+=\$2
+ }'
+else
+ as_fn_append ()
+ {
+ eval $1=\$$1\$2
+ }
+fi # as_fn_append
+
+@%:@ as_fn_arith ARG...
+@%:@ ------------------
+@%:@ Perform arithmetic evaluation on the ARGs, and store the result in the
+@%:@ global @S|@as_val. Take advantage of shells that can avoid forks. The arguments
+@%:@ must be portable across @S|@(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+ eval 'as_fn_arith ()
+ {
+ as_val=$(( $* ))
+ }'
+else
+ as_fn_arith ()
+ {
+ as_val=`expr "$@" || test $? -eq 1`
+ }
+fi # as_fn_arith
+
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in @%:@(((((
+-n*)
+ case `echo 'xy\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ xy) ECHO_C='\c';;
+ *) echo `echo ksh88 bug on AIX 6.1` > /dev/null
+ ECHO_T=' ';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+ if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -p'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -p'
+ elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+ else
+ as_ln_s='cp -p'
+ fi
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+
+@%:@ as_fn_mkdir_p
+@%:@ -------------
+@%:@ Create "@S|@as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || eval $as_mkdir_p || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} @%:@ as_fn_mkdir_p
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p='mkdir -p "$as_dir"'
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+if test -x / >/dev/null 2>&1; then
+ as_test_x='test -x'
+else
+ if ls -dL / >/dev/null 2>&1; then
+ as_ls_L_option=L
+ else
+ as_ls_L_option=
+ fi
+ as_test_x='
+ eval sh -c '\''
+ if test -d "$1"; then
+ test -d "$1/.";
+ else
+ case $1 in @%:@(
+ -*)set "./$1";;
+ esac;
+ case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in @%:@((
+ ???[sx]*):;;*)false;;esac;fi
+ '\'' sh
+ '
+fi
+as_executable_p=$as_test_x
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+## ----------------------------------- ##
+## Main body of $CONFIG_STATUS script. ##
+## ----------------------------------- ##
+_ASEOF
+test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# Save the log message, to keep $0 and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by alpine $as_me 2.10, which was
+generated by GNU Autoconf 2.68. Invocation command line was
+
+ CONFIG_FILES = $CONFIG_FILES
+ CONFIG_HEADERS = $CONFIG_HEADERS
+ CONFIG_LINKS = $CONFIG_LINKS
+ CONFIG_COMMANDS = $CONFIG_COMMANDS
+ $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+case $ac_config_headers in *"
+"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
+esac
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+config_headers="$ac_config_headers"
+config_commands="$ac_config_commands"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files and other configuration actions
+from templates according to the current configuration. Unless the files
+and actions are specified as TAGs, all are instantiated by default.
+
+Usage: $0 [OPTION]... [TAG]...
+
+ -h, --help print this help, then exit
+ -V, --version print version number and configuration settings, then exit
+ --config print configuration, then exit
+ -q, --quiet, --silent
+ do not print progress messages
+ -d, --debug don't remove temporary files
+ --recheck update $as_me by reconfiguring in the same conditions
+ --file=FILE[:TEMPLATE]
+ instantiate the configuration file FILE
+ --header=FILE[:TEMPLATE]
+ instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Configuration commands:
+$config_commands
+
+Report bugs to <alpine-contact@u.washington.edu>."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
+ac_cs_version="\\
+alpine config.status 2.10
+configured by $0, generated by GNU Autoconf 2.68,
+ with options \\"\$ac_cs_config\\"
+
+Copyright (C) 2010 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+INSTALL='$INSTALL'
+MKDIR_P='$MKDIR_P'
+AWK='$AWK'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+ case $1 in
+ --*=?*)
+ ac_option=`expr "X$1" : 'X\([^=]*\)='`
+ ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+ ac_shift=:
+ ;;
+ --*=)
+ ac_option=`expr "X$1" : 'X\([^=]*\)='`
+ ac_optarg=
+ ac_shift=:
+ ;;
+ *)
+ ac_option=$1
+ ac_optarg=$2
+ ac_shift=shift
+ ;;
+ esac
+
+ case $ac_option in
+ # Handling of the options.
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ ac_cs_recheck=: ;;
+ --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+ $as_echo "$ac_cs_version"; exit ;;
+ --config | --confi | --conf | --con | --co | --c )
+ $as_echo "$ac_cs_config"; exit ;;
+ --debug | --debu | --deb | --de | --d | -d )
+ debug=: ;;
+ --file | --fil | --fi | --f )
+ $ac_shift
+ case $ac_optarg in
+ *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ '') as_fn_error $? "missing file argument" ;;
+ esac
+ as_fn_append CONFIG_FILES " '$ac_optarg'"
+ ac_need_defaults=false;;
+ --header | --heade | --head | --hea )
+ $ac_shift
+ case $ac_optarg in
+ *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ as_fn_append CONFIG_HEADERS " '$ac_optarg'"
+ ac_need_defaults=false;;
+ --he | --h)
+ # Conflict between --help and --header
+ as_fn_error $? "ambiguous option: \`$1'
+Try \`$0 --help' for more information.";;
+ --help | --hel | -h )
+ $as_echo "$ac_cs_usage"; exit ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil | --si | --s)
+ ac_cs_silent=: ;;
+
+ # This is an error.
+ -*) as_fn_error $? "unrecognized option: \`$1'
+Try \`$0 --help' for more information." ;;
+
+ *) as_fn_append ac_config_targets " $1"
+ ac_need_defaults=false ;;
+
+ esac
+ shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+ exec 6>/dev/null
+ ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+ set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+ shift
+ \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
+ CONFIG_SHELL='$SHELL'
+ export CONFIG_SHELL
+ exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+ echo
+ sed 'h;s/./-/g;s/^.../@%:@@%:@ /;s/...$/ @%:@@%:@/;p;x;p;x' <<_ASBOX
+@%:@@%:@ Running $as_me. @%:@@%:@
+_ASBOX
+ $as_echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+#
+# INIT-COMMANDS
+#
+AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"
+
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+sed_quote_subst='$sed_quote_subst'
+double_quote_subst='$double_quote_subst'
+delay_variable_subst='$delay_variable_subst'
+macro_version='`$ECHO "X$macro_version" | $Xsed -e "$delay_single_quote_subst"`'
+macro_revision='`$ECHO "X$macro_revision" | $Xsed -e "$delay_single_quote_subst"`'
+enable_shared='`$ECHO "X$enable_shared" | $Xsed -e "$delay_single_quote_subst"`'
+enable_static='`$ECHO "X$enable_static" | $Xsed -e "$delay_single_quote_subst"`'
+pic_mode='`$ECHO "X$pic_mode" | $Xsed -e "$delay_single_quote_subst"`'
+enable_fast_install='`$ECHO "X$enable_fast_install" | $Xsed -e "$delay_single_quote_subst"`'
+host_alias='`$ECHO "X$host_alias" | $Xsed -e "$delay_single_quote_subst"`'
+host='`$ECHO "X$host" | $Xsed -e "$delay_single_quote_subst"`'
+host_os='`$ECHO "X$host_os" | $Xsed -e "$delay_single_quote_subst"`'
+build_alias='`$ECHO "X$build_alias" | $Xsed -e "$delay_single_quote_subst"`'
+build='`$ECHO "X$build" | $Xsed -e "$delay_single_quote_subst"`'
+build_os='`$ECHO "X$build_os" | $Xsed -e "$delay_single_quote_subst"`'
+SED='`$ECHO "X$SED" | $Xsed -e "$delay_single_quote_subst"`'
+Xsed='`$ECHO "X$Xsed" | $Xsed -e "$delay_single_quote_subst"`'
+GREP='`$ECHO "X$GREP" | $Xsed -e "$delay_single_quote_subst"`'
+EGREP='`$ECHO "X$EGREP" | $Xsed -e "$delay_single_quote_subst"`'
+FGREP='`$ECHO "X$FGREP" | $Xsed -e "$delay_single_quote_subst"`'
+LD='`$ECHO "X$LD" | $Xsed -e "$delay_single_quote_subst"`'
+NM='`$ECHO "X$NM" | $Xsed -e "$delay_single_quote_subst"`'
+LN_S='`$ECHO "X$LN_S" | $Xsed -e "$delay_single_quote_subst"`'
+max_cmd_len='`$ECHO "X$max_cmd_len" | $Xsed -e "$delay_single_quote_subst"`'
+ac_objext='`$ECHO "X$ac_objext" | $Xsed -e "$delay_single_quote_subst"`'
+exeext='`$ECHO "X$exeext" | $Xsed -e "$delay_single_quote_subst"`'
+lt_unset='`$ECHO "X$lt_unset" | $Xsed -e "$delay_single_quote_subst"`'
+lt_SP2NL='`$ECHO "X$lt_SP2NL" | $Xsed -e "$delay_single_quote_subst"`'
+lt_NL2SP='`$ECHO "X$lt_NL2SP" | $Xsed -e "$delay_single_quote_subst"`'
+reload_flag='`$ECHO "X$reload_flag" | $Xsed -e "$delay_single_quote_subst"`'
+reload_cmds='`$ECHO "X$reload_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+OBJDUMP='`$ECHO "X$OBJDUMP" | $Xsed -e "$delay_single_quote_subst"`'
+deplibs_check_method='`$ECHO "X$deplibs_check_method" | $Xsed -e "$delay_single_quote_subst"`'
+file_magic_cmd='`$ECHO "X$file_magic_cmd" | $Xsed -e "$delay_single_quote_subst"`'
+AR='`$ECHO "X$AR" | $Xsed -e "$delay_single_quote_subst"`'
+AR_FLAGS='`$ECHO "X$AR_FLAGS" | $Xsed -e "$delay_single_quote_subst"`'
+STRIP='`$ECHO "X$STRIP" | $Xsed -e "$delay_single_quote_subst"`'
+RANLIB='`$ECHO "X$RANLIB" | $Xsed -e "$delay_single_quote_subst"`'
+old_postinstall_cmds='`$ECHO "X$old_postinstall_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+old_postuninstall_cmds='`$ECHO "X$old_postuninstall_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+old_archive_cmds='`$ECHO "X$old_archive_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+CC='`$ECHO "X$CC" | $Xsed -e "$delay_single_quote_subst"`'
+CFLAGS='`$ECHO "X$CFLAGS" | $Xsed -e "$delay_single_quote_subst"`'
+compiler='`$ECHO "X$compiler" | $Xsed -e "$delay_single_quote_subst"`'
+GCC='`$ECHO "X$GCC" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_pipe='`$ECHO "X$lt_cv_sys_global_symbol_pipe" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_cdecl='`$ECHO "X$lt_cv_sys_global_symbol_to_cdecl" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "X$lt_cv_sys_global_symbol_to_c_name_address" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "X$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $Xsed -e "$delay_single_quote_subst"`'
+objdir='`$ECHO "X$objdir" | $Xsed -e "$delay_single_quote_subst"`'
+SHELL='`$ECHO "X$SHELL" | $Xsed -e "$delay_single_quote_subst"`'
+ECHO='`$ECHO "X$ECHO" | $Xsed -e "$delay_single_quote_subst"`'
+MAGIC_CMD='`$ECHO "X$MAGIC_CMD" | $Xsed -e "$delay_single_quote_subst"`'
+lt_prog_compiler_no_builtin_flag='`$ECHO "X$lt_prog_compiler_no_builtin_flag" | $Xsed -e "$delay_single_quote_subst"`'
+lt_prog_compiler_wl='`$ECHO "X$lt_prog_compiler_wl" | $Xsed -e "$delay_single_quote_subst"`'
+lt_prog_compiler_pic='`$ECHO "X$lt_prog_compiler_pic" | $Xsed -e "$delay_single_quote_subst"`'
+lt_prog_compiler_static='`$ECHO "X$lt_prog_compiler_static" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_prog_compiler_c_o='`$ECHO "X$lt_cv_prog_compiler_c_o" | $Xsed -e "$delay_single_quote_subst"`'
+need_locks='`$ECHO "X$need_locks" | $Xsed -e "$delay_single_quote_subst"`'
+DSYMUTIL='`$ECHO "X$DSYMUTIL" | $Xsed -e "$delay_single_quote_subst"`'
+NMEDIT='`$ECHO "X$NMEDIT" | $Xsed -e "$delay_single_quote_subst"`'
+LIPO='`$ECHO "X$LIPO" | $Xsed -e "$delay_single_quote_subst"`'
+OTOOL='`$ECHO "X$OTOOL" | $Xsed -e "$delay_single_quote_subst"`'
+OTOOL64='`$ECHO "X$OTOOL64" | $Xsed -e "$delay_single_quote_subst"`'
+libext='`$ECHO "X$libext" | $Xsed -e "$delay_single_quote_subst"`'
+shrext_cmds='`$ECHO "X$shrext_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+extract_expsyms_cmds='`$ECHO "X$extract_expsyms_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+archive_cmds_need_lc='`$ECHO "X$archive_cmds_need_lc" | $Xsed -e "$delay_single_quote_subst"`'
+enable_shared_with_static_runtimes='`$ECHO "X$enable_shared_with_static_runtimes" | $Xsed -e "$delay_single_quote_subst"`'
+export_dynamic_flag_spec='`$ECHO "X$export_dynamic_flag_spec" | $Xsed -e "$delay_single_quote_subst"`'
+whole_archive_flag_spec='`$ECHO "X$whole_archive_flag_spec" | $Xsed -e "$delay_single_quote_subst"`'
+compiler_needs_object='`$ECHO "X$compiler_needs_object" | $Xsed -e "$delay_single_quote_subst"`'
+old_archive_from_new_cmds='`$ECHO "X$old_archive_from_new_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+old_archive_from_expsyms_cmds='`$ECHO "X$old_archive_from_expsyms_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+archive_cmds='`$ECHO "X$archive_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+archive_expsym_cmds='`$ECHO "X$archive_expsym_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+module_cmds='`$ECHO "X$module_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+module_expsym_cmds='`$ECHO "X$module_expsym_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+with_gnu_ld='`$ECHO "X$with_gnu_ld" | $Xsed -e "$delay_single_quote_subst"`'
+allow_undefined_flag='`$ECHO "X$allow_undefined_flag" | $Xsed -e "$delay_single_quote_subst"`'
+no_undefined_flag='`$ECHO "X$no_undefined_flag" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_libdir_flag_spec='`$ECHO "X$hardcode_libdir_flag_spec" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_libdir_flag_spec_ld='`$ECHO "X$hardcode_libdir_flag_spec_ld" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_libdir_separator='`$ECHO "X$hardcode_libdir_separator" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_direct='`$ECHO "X$hardcode_direct" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_direct_absolute='`$ECHO "X$hardcode_direct_absolute" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_minus_L='`$ECHO "X$hardcode_minus_L" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_shlibpath_var='`$ECHO "X$hardcode_shlibpath_var" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_automatic='`$ECHO "X$hardcode_automatic" | $Xsed -e "$delay_single_quote_subst"`'
+inherit_rpath='`$ECHO "X$inherit_rpath" | $Xsed -e "$delay_single_quote_subst"`'
+link_all_deplibs='`$ECHO "X$link_all_deplibs" | $Xsed -e "$delay_single_quote_subst"`'
+fix_srcfile_path='`$ECHO "X$fix_srcfile_path" | $Xsed -e "$delay_single_quote_subst"`'
+always_export_symbols='`$ECHO "X$always_export_symbols" | $Xsed -e "$delay_single_quote_subst"`'
+export_symbols_cmds='`$ECHO "X$export_symbols_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+exclude_expsyms='`$ECHO "X$exclude_expsyms" | $Xsed -e "$delay_single_quote_subst"`'
+include_expsyms='`$ECHO "X$include_expsyms" | $Xsed -e "$delay_single_quote_subst"`'
+prelink_cmds='`$ECHO "X$prelink_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+file_list_spec='`$ECHO "X$file_list_spec" | $Xsed -e "$delay_single_quote_subst"`'
+variables_saved_for_relink='`$ECHO "X$variables_saved_for_relink" | $Xsed -e "$delay_single_quote_subst"`'
+need_lib_prefix='`$ECHO "X$need_lib_prefix" | $Xsed -e "$delay_single_quote_subst"`'
+need_version='`$ECHO "X$need_version" | $Xsed -e "$delay_single_quote_subst"`'
+version_type='`$ECHO "X$version_type" | $Xsed -e "$delay_single_quote_subst"`'
+runpath_var='`$ECHO "X$runpath_var" | $Xsed -e "$delay_single_quote_subst"`'
+shlibpath_var='`$ECHO "X$shlibpath_var" | $Xsed -e "$delay_single_quote_subst"`'
+shlibpath_overrides_runpath='`$ECHO "X$shlibpath_overrides_runpath" | $Xsed -e "$delay_single_quote_subst"`'
+libname_spec='`$ECHO "X$libname_spec" | $Xsed -e "$delay_single_quote_subst"`'
+library_names_spec='`$ECHO "X$library_names_spec" | $Xsed -e "$delay_single_quote_subst"`'
+soname_spec='`$ECHO "X$soname_spec" | $Xsed -e "$delay_single_quote_subst"`'
+postinstall_cmds='`$ECHO "X$postinstall_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+postuninstall_cmds='`$ECHO "X$postuninstall_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+finish_cmds='`$ECHO "X$finish_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+finish_eval='`$ECHO "X$finish_eval" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_into_libs='`$ECHO "X$hardcode_into_libs" | $Xsed -e "$delay_single_quote_subst"`'
+sys_lib_search_path_spec='`$ECHO "X$sys_lib_search_path_spec" | $Xsed -e "$delay_single_quote_subst"`'
+sys_lib_dlsearch_path_spec='`$ECHO "X$sys_lib_dlsearch_path_spec" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_action='`$ECHO "X$hardcode_action" | $Xsed -e "$delay_single_quote_subst"`'
+enable_dlopen='`$ECHO "X$enable_dlopen" | $Xsed -e "$delay_single_quote_subst"`'
+enable_dlopen_self='`$ECHO "X$enable_dlopen_self" | $Xsed -e "$delay_single_quote_subst"`'
+enable_dlopen_self_static='`$ECHO "X$enable_dlopen_self_static" | $Xsed -e "$delay_single_quote_subst"`'
+old_striplib='`$ECHO "X$old_striplib" | $Xsed -e "$delay_single_quote_subst"`'
+striplib='`$ECHO "X$striplib" | $Xsed -e "$delay_single_quote_subst"`'
+
+LTCC='$LTCC'
+LTCFLAGS='$LTCFLAGS'
+compiler='$compiler_DEFAULT'
+
+# Quote evaled strings.
+for var in SED \
+GREP \
+EGREP \
+FGREP \
+LD \
+NM \
+LN_S \
+lt_SP2NL \
+lt_NL2SP \
+reload_flag \
+OBJDUMP \
+deplibs_check_method \
+file_magic_cmd \
+AR \
+AR_FLAGS \
+STRIP \
+RANLIB \
+CC \
+CFLAGS \
+compiler \
+lt_cv_sys_global_symbol_pipe \
+lt_cv_sys_global_symbol_to_cdecl \
+lt_cv_sys_global_symbol_to_c_name_address \
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \
+SHELL \
+ECHO \
+lt_prog_compiler_no_builtin_flag \
+lt_prog_compiler_wl \
+lt_prog_compiler_pic \
+lt_prog_compiler_static \
+lt_cv_prog_compiler_c_o \
+need_locks \
+DSYMUTIL \
+NMEDIT \
+LIPO \
+OTOOL \
+OTOOL64 \
+shrext_cmds \
+export_dynamic_flag_spec \
+whole_archive_flag_spec \
+compiler_needs_object \
+with_gnu_ld \
+allow_undefined_flag \
+no_undefined_flag \
+hardcode_libdir_flag_spec \
+hardcode_libdir_flag_spec_ld \
+hardcode_libdir_separator \
+fix_srcfile_path \
+exclude_expsyms \
+include_expsyms \
+file_list_spec \
+variables_saved_for_relink \
+libname_spec \
+library_names_spec \
+soname_spec \
+finish_eval \
+old_striplib \
+striplib; do
+ case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in
+ *[\\\\\\\`\\"\\\$]*)
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$sed_quote_subst\\"\\\`\\\\\\""
+ ;;
+ *)
+ eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+ ;;
+ esac
+done
+
+# Double-quote double-evaled strings.
+for var in reload_cmds \
+old_postinstall_cmds \
+old_postuninstall_cmds \
+old_archive_cmds \
+extract_expsyms_cmds \
+old_archive_from_new_cmds \
+old_archive_from_expsyms_cmds \
+archive_cmds \
+archive_expsym_cmds \
+module_cmds \
+module_expsym_cmds \
+export_symbols_cmds \
+prelink_cmds \
+postinstall_cmds \
+postuninstall_cmds \
+finish_cmds \
+sys_lib_search_path_spec \
+sys_lib_dlsearch_path_spec; do
+ case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in
+ *[\\\\\\\`\\"\\\$]*)
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\""
+ ;;
+ *)
+ eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+ ;;
+ esac
+done
+
+# Fix-up fallback echo if it was mangled by the above quoting rules.
+case \$lt_ECHO in
+*'\\\$0 --fallback-echo"') lt_ECHO=\`\$ECHO "X\$lt_ECHO" | \$Xsed -e 's/\\\\\\\\\\\\\\\$0 --fallback-echo"\$/\$0 --fallback-echo"/'\`
+ ;;
+esac
+
+ac_aux_dir='$ac_aux_dir'
+xsi_shell='$xsi_shell'
+lt_shell_append='$lt_shell_append'
+
+# See if we are running on zsh, and set the options which allow our
+# commands through without removal of \ escapes INIT.
+if test -n "\${ZSH_VERSION+set}" ; then
+ setopt NO_GLOB_SUBST
+fi
+
+
+ PACKAGE='$PACKAGE'
+ VERSION='$VERSION'
+ TIMESTAMP='$TIMESTAMP'
+ RM='$RM'
+ ofile='$ofile'
+
+
+
+# Capture the value of obsolete ALL_LINGUAS because we need it to compute
+ # POFILES, UPDATEPOFILES, DUMMYPOFILES, GMOFILES, CATALOGS. But hide it
+ # from automake < 1.5.
+ eval 'OBSOLETE_ALL_LINGUAS''="$ALL_LINGUAS"'
+ # Capture the value of LINGUAS because we need it to compute CATALOGS.
+ LINGUAS="${LINGUAS-%UNSET%}"
+
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+ case $ac_config_target in
+ "include/config.h") CONFIG_HEADERS="$CONFIG_HEADERS include/config.h" ;;
+ "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;;
+ "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;;
+ "po-directories") CONFIG_COMMANDS="$CONFIG_COMMANDS po-directories" ;;
+ "m4/Makefile") CONFIG_FILES="$CONFIG_FILES m4/Makefile" ;;
+ "po/Makefile.in") CONFIG_FILES="$CONFIG_FILES po/Makefile.in" ;;
+ "regex/Makefile") CONFIG_FILES="$CONFIG_FILES regex/Makefile" ;;
+ "pith/osdep/Makefile") CONFIG_FILES="$CONFIG_FILES pith/osdep/Makefile" ;;
+ "pith/charconv/Makefile") CONFIG_FILES="$CONFIG_FILES pith/charconv/Makefile" ;;
+ "pith/Makefile") CONFIG_FILES="$CONFIG_FILES pith/Makefile" ;;
+ "pico/osdep/Makefile") CONFIG_FILES="$CONFIG_FILES pico/osdep/Makefile" ;;
+ "pico/Makefile") CONFIG_FILES="$CONFIG_FILES pico/Makefile" ;;
+ "alpine/osdep/Makefile") CONFIG_FILES="$CONFIG_FILES alpine/osdep/Makefile" ;;
+ "alpine/Makefile") CONFIG_FILES="$CONFIG_FILES alpine/Makefile" ;;
+ "web/src/Makefile") CONFIG_FILES="$CONFIG_FILES web/src/Makefile" ;;
+ "web/src/pubcookie/Makefile") CONFIG_FILES="$CONFIG_FILES web/src/pubcookie/Makefile" ;;
+ "web/src/alpined.d/Makefile") CONFIG_FILES="$CONFIG_FILES web/src/alpined.d/Makefile" ;;
+ "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+
+ *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+ esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used. Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+ test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+ test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+ test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands
+fi
+
+# Have a temporary directory for convenience. Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+ tmp= ac_tmp=
+ trap 'exit_status=$?
+ : "${ac_tmp:=$tmp}"
+ { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
+' 0
+ trap 'as_fn_exit 1' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+ tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+ test -d "$tmp"
+} ||
+{
+ tmp=./conf$$-$RANDOM
+ (umask 077 && mkdir "$tmp")
+} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
+ac_tmp=$tmp
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr=`echo X | tr X '\015'`
+# On cygwin, bash can eat \r inside `` if the user requested igncr.
+# But we know of no other shell where ac_cr would be empty at this
+# point, so we can use a bashism as a fallback.
+if test "x$ac_cr" = x; then
+ eval ac_cr=\$\'\\r\'
+fi
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+ ac_cs_awk_cr='\\r'
+else
+ ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+ echo "cat >conf$$subs.awk <<_ACEOF" &&
+ echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+ echo "_ACEOF"
+} >conf$$subs.sh ||
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+ . ./conf$$subs.sh ||
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+
+ ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+ if test $ac_delim_n = $ac_delim_num; then
+ break
+ elif $ac_last_try; then
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\)..*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\)..*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+ N
+ s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
+ for (key in S) S_is_set[key] = 1
+ FS = ""
+
+}
+{
+ line = $ 0
+ nfields = split(line, field, "@")
+ substed = 0
+ len = length(field[1])
+ for (i = 2; i < nfields; i++) {
+ key = field[i]
+ keylen = length(key)
+ if (S_is_set[key]) {
+ value = S[key]
+ line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+ len += length(value) + length(field[++i])
+ substed = 1
+ } else
+ len += 1 + keylen
+ }
+
+ print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+ sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+ cat
+fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
+ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
+# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{
+h
+s///
+s/^/:/
+s/[ ]*$/:/
+s/:\$(srcdir):/:/g
+s/:\${srcdir}:/:/g
+s/:@srcdir@:/:/g
+s/^:*//
+s/:*$//
+x
+s/\(=[ ]*\).*/\1/
+G
+s/\n//
+s/^[^=]*=[ ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+# Set up the scripts for CONFIG_HEADERS section.
+# No need to generate them if there are no CONFIG_HEADERS.
+# This happens for instance with `./config.status Makefile'.
+if test -n "$CONFIG_HEADERS"; then
+cat >"$ac_tmp/defines.awk" <<\_ACAWK ||
+BEGIN {
+_ACEOF
+
+# Transform confdefs.h into an awk script `defines.awk', embedded as
+# here-document in config.status, that substitutes the proper values into
+# config.h.in to produce config.h.
+
+# Create a delimiter string that does not exist in confdefs.h, to ease
+# handling of long lines.
+ac_delim='%!_!# '
+for ac_last_try in false false :; do
+ ac_tt=`sed -n "/$ac_delim/p" confdefs.h`
+ if test -z "$ac_tt"; then
+ break
+ elif $ac_last_try; then
+ as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+done
+
+# For the awk script, D is an array of macro values keyed by name,
+# likewise P contains macro parameters if any. Preserve backslash
+# newline sequences.
+
+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
+sed -n '
+s/.\{148\}/&'"$ac_delim"'/g
+t rset
+:rset
+s/^[ ]*#[ ]*define[ ][ ]*/ /
+t def
+d
+:def
+s/\\$//
+t bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3"/p
+s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p
+d
+:bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3\\\\\\n"\\/p
+t cont
+s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
+t cont
+d
+:cont
+n
+s/.\{148\}/&'"$ac_delim"'/g
+t clear
+:clear
+s/\\$//
+t bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/"/p
+d
+:bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
+b cont
+' <confdefs.h | sed '
+s/'"$ac_delim"'/"\\\
+"/g' >>$CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ for (key in D) D_is_set[key] = 1
+ FS = ""
+}
+/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
+ line = \$ 0
+ split(line, arg, " ")
+ if (arg[1] == "#") {
+ defundef = arg[2]
+ mac1 = arg[3]
+ } else {
+ defundef = substr(arg[1], 2)
+ mac1 = arg[2]
+ }
+ split(mac1, mac2, "(") #)
+ macro = mac2[1]
+ prefix = substr(line, 1, index(line, defundef) - 1)
+ if (D_is_set[macro]) {
+ # Preserve the white space surrounding the "#".
+ print prefix "define", macro P[macro] D[macro]
+ next
+ } else {
+ # Replace #undef with comments. This is necessary, for example,
+ # in the case of _POSIX_SOURCE, which is predefined and required
+ # on some systems where configure will not decide to define it.
+ if (defundef == "undef") {
+ print "/*", prefix defundef, macro, "*/"
+ next
+ }
+ }
+}
+{ print }
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
+fi # test -n "$CONFIG_HEADERS"
+
+
+eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS"
+shift
+for ac_tag
+do
+ case $ac_tag in
+ :[FHLC]) ac_mode=$ac_tag; continue;;
+ esac
+ case $ac_mode$ac_tag in
+ :[FHL]*:*);;
+ :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
+ :[FH]-) ac_tag=-:-;;
+ :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+ esac
+ ac_save_IFS=$IFS
+ IFS=:
+ set x $ac_tag
+ IFS=$ac_save_IFS
+ shift
+ ac_file=$1
+ shift
+
+ case $ac_mode in
+ :L) ac_source=$1;;
+ :[FH])
+ ac_file_inputs=
+ for ac_f
+ do
+ case $ac_f in
+ -) ac_f="$ac_tmp/stdin";;
+ *) # Look for the file first in the build tree, then in the source tree
+ # (if the path is not absolute). The absolute path cannot be DOS-style,
+ # because $ac_f cannot contain `:'.
+ test -f "$ac_f" ||
+ case $ac_f in
+ [\\/$]*) false;;
+ *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+ esac ||
+ as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+ esac
+ case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+ as_fn_append ac_file_inputs " '$ac_f'"
+ done
+
+ # Let's still pretend it is `configure' which instantiates (i.e., don't
+ # use $as_me), people would be surprised to read:
+ # /* config.h. Generated by config.status. */
+ configure_input='Generated from '`
+ $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+ `' by configure.'
+ if test x"$ac_file" != x-; then
+ configure_input="$ac_file. $configure_input"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+$as_echo "$as_me: creating $ac_file" >&6;}
+ fi
+ # Neutralize special characters interpreted by sed in replacement strings.
+ case $configure_input in #(
+ *\&* | *\|* | *\\* )
+ ac_sed_conf_input=`$as_echo "$configure_input" |
+ sed 's/[\\\\&|]/\\\\&/g'`;; #(
+ *) ac_sed_conf_input=$configure_input;;
+ esac
+
+ case $ac_tag in
+ *:-:* | *:-) cat >"$ac_tmp/stdin" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
+ esac
+ ;;
+ esac
+
+ ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_file" : 'X\(//\)[^/]' \| \
+ X"$ac_file" : 'X\(//\)$' \| \
+ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$ac_file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ as_dir="$ac_dir"; as_fn_mkdir_p
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+ case $ac_mode in
+ :F)
+ #
+ # CONFIG_FILE
+ #
+
+ case $INSTALL in
+ [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+ *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
+ esac
+ ac_MKDIR_P=$MKDIR_P
+ case $MKDIR_P in
+ [\\/$]* | ?:[\\/]* ) ;;
+ */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;;
+ esac
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+ac_sed_dataroot='
+/datarootdir/ {
+ p
+ q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ ac_datarootdir_hack='
+ s&@datadir@&$datadir&g
+ s&@docdir@&$docdir&g
+ s&@infodir@&$infodir&g
+ s&@localedir@&$localedir&g
+ s&@mandir@&$mandir&g
+ s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+s&@INSTALL@&$ac_INSTALL&;t t
+s&@MKDIR_P@&$ac_MKDIR_P&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
+ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+ { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
+ { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \
+ "$ac_tmp/out"`; test -z "$ac_out"; } &&
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined" >&5
+$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined" >&2;}
+
+ rm -f "$ac_tmp/stdin"
+ case $ac_file in
+ -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
+ *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
+ esac \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ ;;
+ :H)
+ #
+ # CONFIG_HEADER
+ #
+ if test x"$ac_file" != x-; then
+ {
+ $as_echo "/* $configure_input */" \
+ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
+ } >"$ac_tmp/config.h" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
+$as_echo "$as_me: $ac_file is unchanged" >&6;}
+ else
+ rm -f "$ac_file"
+ mv "$ac_tmp/config.h" "$ac_file" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ fi
+ else
+ $as_echo "/* $configure_input */" \
+ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
+ || as_fn_error $? "could not create -" "$LINENO" 5
+ fi
+# Compute "$ac_file"'s index in $config_headers.
+_am_arg="$ac_file"
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+ case $_am_header in
+ $_am_arg | $_am_arg:* )
+ break ;;
+ * )
+ _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+ esac
+done
+echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" ||
+$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$_am_arg" : 'X\(//\)[^/]' \| \
+ X"$_am_arg" : 'X\(//\)$' \| \
+ X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$_am_arg" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`/stamp-h$_am_stamp_count
+ ;;
+
+ :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5
+$as_echo "$as_me: executing $ac_file commands" >&6;}
+ ;;
+ esac
+
+
+ case $ac_file$ac_mode in
+ "depfiles":C) test x"$AMDEP_TRUE" != x"" || {
+ # Autoconf 2.62 quotes --file arguments for eval, but not when files
+ # are listed without --file. Let's play safe and only enable the eval
+ # if we detect the quoting.
+ case $CONFIG_FILES in
+ *\'*) eval set x "$CONFIG_FILES" ;;
+ *) set x $CONFIG_FILES ;;
+ esac
+ shift
+ for mf
+ do
+ # Strip MF so we end up with the name of the file.
+ mf=`echo "$mf" | sed -e 's/:.*$//'`
+ # Check whether this is an Automake generated Makefile or not.
+ # We used to match only the files named `Makefile.in', but
+ # some people rename them; so instead we look at the file content.
+ # Grep'ing the first line is not enough: some people post-process
+ # each Makefile.in and add a new line on top of each file to say so.
+ # Grep'ing the whole file is not good either: AIX grep has a line
+ # limit of 2048, but all sed's we know have understand at least 4000.
+ if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
+ dirpart=`$as_dirname -- "$mf" ||
+$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$mf" : 'X\(//\)[^/]' \| \
+ X"$mf" : 'X\(//\)$' \| \
+ X"$mf" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$mf" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ else
+ continue
+ fi
+ # Extract the definition of DEPDIR, am__include, and am__quote
+ # from the Makefile without running `make'.
+ DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
+ test -z "$DEPDIR" && continue
+ am__include=`sed -n 's/^am__include = //p' < "$mf"`
+ test -z "am__include" && continue
+ am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
+ # When using ansi2knr, U may be empty or an underscore; expand it
+ U=`sed -n 's/^U = //p' < "$mf"`
+ # Find all dependency output files, they are included files with
+ # $(DEPDIR) in their names. We invoke sed twice because it is the
+ # simplest approach to changing $(DEPDIR) to its actual value in the
+ # expansion.
+ for file in `sed -n "
+ s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
+ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
+ # Make sure the directory exists.
+ test -f "$dirpart/$file" && continue
+ fdir=`$as_dirname -- "$file" ||
+$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$file" : 'X\(//\)[^/]' \| \
+ X"$file" : 'X\(//\)$' \| \
+ X"$file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ as_dir=$dirpart/$fdir; as_fn_mkdir_p
+ # echo "creating $dirpart/$file"
+ echo '# dummy' > "$dirpart/$file"
+ done
+ done
+}
+ ;;
+ "libtool":C)
+
+ # See if we are running on zsh, and set the options which allow our
+ # commands through without removal of \ escapes.
+ if test -n "${ZSH_VERSION+set}" ; then
+ setopt NO_GLOB_SUBST
+ fi
+
+ cfgfile="${ofile}T"
+ trap "$RM \"$cfgfile\"; exit 1" 1 2 15
+ $RM "$cfgfile"
+
+ cat <<_LT_EOF >> "$cfgfile"
+#! $SHELL
+
+# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services.
+# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION
+# NOTE: Changes made to this file will be lost: look at ltmain.sh.
+#
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005,
+# 2006, 2007, 2008 Free Software Foundation, Inc.
+# Written by Gordon Matzigkeit, 1996
+#
+# This file is part of GNU Libtool.
+#
+# GNU Libtool 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.
+#
+# As a special exception to the GNU General Public License,
+# if you distribute this file as part of a program or library that
+# is built using GNU Libtool, you may include this file under the
+# same distribution terms that you use for the rest of that program.
+#
+# GNU Libtool 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 GNU Libtool; see the file COPYING. If not, a copy
+# can be downloaded from http://www.gnu.org/licenses/gpl.html, or
+# obtained by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+
+# The names of the tagged configurations supported by this script.
+available_tags=""
+
+# ### BEGIN LIBTOOL CONFIG
+
+# Which release of libtool.m4 was used?
+macro_version=$macro_version
+macro_revision=$macro_revision
+
+# Whether or not to build shared libraries.
+build_libtool_libs=$enable_shared
+
+# Whether or not to build static libraries.
+build_old_libs=$enable_static
+
+# What type of objects to build.
+pic_mode=$pic_mode
+
+# Whether or not to optimize for fast installation.
+fast_install=$enable_fast_install
+
+# The host system.
+host_alias=$host_alias
+host=$host
+host_os=$host_os
+
+# The build system.
+build_alias=$build_alias
+build=$build
+build_os=$build_os
+
+# A sed program that does not truncate output.
+SED=$lt_SED
+
+# Sed that helps us avoid accidentally triggering echo(1) options like -n.
+Xsed="\$SED -e 1s/^X//"
+
+# A grep program that handles long lines.
+GREP=$lt_GREP
+
+# An ERE matcher.
+EGREP=$lt_EGREP
+
+# A literal string matcher.
+FGREP=$lt_FGREP
+
+# A BSD- or MS-compatible name lister.
+NM=$lt_NM
+
+# Whether we need soft or hard links.
+LN_S=$lt_LN_S
+
+# What is the maximum length of a command?
+max_cmd_len=$max_cmd_len
+
+# Object file suffix (normally "o").
+objext=$ac_objext
+
+# Executable file suffix (normally "").
+exeext=$exeext
+
+# whether the shell understands "unset".
+lt_unset=$lt_unset
+
+# turn spaces into newlines.
+SP2NL=$lt_lt_SP2NL
+
+# turn newlines into spaces.
+NL2SP=$lt_lt_NL2SP
+
+# How to create reloadable object files.
+reload_flag=$lt_reload_flag
+reload_cmds=$lt_reload_cmds
+
+# An object symbol dumper.
+OBJDUMP=$lt_OBJDUMP
+
+# Method to check whether dependent libraries are shared objects.
+deplibs_check_method=$lt_deplibs_check_method
+
+# Command to use when deplibs_check_method == "file_magic".
+file_magic_cmd=$lt_file_magic_cmd
+
+# The archiver.
+AR=$lt_AR
+AR_FLAGS=$lt_AR_FLAGS
+
+# A symbol stripping program.
+STRIP=$lt_STRIP
+
+# Commands used to install an old-style archive.
+RANLIB=$lt_RANLIB
+old_postinstall_cmds=$lt_old_postinstall_cmds
+old_postuninstall_cmds=$lt_old_postuninstall_cmds
+
+# A C compiler.
+LTCC=$lt_CC
+
+# LTCC compiler flags.
+LTCFLAGS=$lt_CFLAGS
+
+# Take the output of nm and produce a listing of raw symbols and C names.
+global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe
+
+# Transform the output of nm in a proper C declaration.
+global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl
+
+# Transform the output of nm in a C name address pair.
+global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address
+
+# Transform the output of nm in a C name address pair when lib prefix is needed.
+global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix
+
+# The name of the directory that contains temporary libtool files.
+objdir=$objdir
+
+# Shell to use when invoking shell scripts.
+SHELL=$lt_SHELL
+
+# An echo program that does not interpret backslashes.
+ECHO=$lt_ECHO
+
+# Used to examine libraries when file_magic_cmd begins with "file".
+MAGIC_CMD=$MAGIC_CMD
+
+# Must we lock files when doing compilation?
+need_locks=$lt_need_locks
+
+# Tool to manipulate archived DWARF debug symbol files on Mac OS X.
+DSYMUTIL=$lt_DSYMUTIL
+
+# Tool to change global to local symbols on Mac OS X.
+NMEDIT=$lt_NMEDIT
+
+# Tool to manipulate fat objects and archives on Mac OS X.
+LIPO=$lt_LIPO
+
+# ldd/readelf like tool for Mach-O binaries on Mac OS X.
+OTOOL=$lt_OTOOL
+
+# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4.
+OTOOL64=$lt_OTOOL64
+
+# Old archive suffix (normally "a").
+libext=$libext
+
+# Shared library suffix (normally ".so").
+shrext_cmds=$lt_shrext_cmds
+
+# The commands to extract the exported symbol list from a shared archive.
+extract_expsyms_cmds=$lt_extract_expsyms_cmds
+
+# Variables whose values should be saved in libtool wrapper scripts and
+# restored at link time.
+variables_saved_for_relink=$lt_variables_saved_for_relink
+
+# Do we need the "lib" prefix for modules?
+need_lib_prefix=$need_lib_prefix
+
+# Do we need a version for libraries?
+need_version=$need_version
+
+# Library versioning type.
+version_type=$version_type
+
+# Shared library runtime path variable.
+runpath_var=$runpath_var
+
+# Shared library path variable.
+shlibpath_var=$shlibpath_var
+
+# Is shlibpath searched before the hard-coded library search path?
+shlibpath_overrides_runpath=$shlibpath_overrides_runpath
+
+# Format of library name prefix.
+libname_spec=$lt_libname_spec
+
+# List of archive names. First name is the real one, the rest are links.
+# The last name is the one that the linker finds with -lNAME
+library_names_spec=$lt_library_names_spec
+
+# The coded name of the library, if different from the real name.
+soname_spec=$lt_soname_spec
+
+# Command to use after installation of a shared archive.
+postinstall_cmds=$lt_postinstall_cmds
+
+# Command to use after uninstallation of a shared archive.
+postuninstall_cmds=$lt_postuninstall_cmds
+
+# Commands used to finish a libtool library installation in a directory.
+finish_cmds=$lt_finish_cmds
+
+# As "finish_cmds", except a single script fragment to be evaled but
+# not shown.
+finish_eval=$lt_finish_eval
+
+# Whether we should hardcode library paths into libraries.
+hardcode_into_libs=$hardcode_into_libs
+
+# Compile-time system search path for libraries.
+sys_lib_search_path_spec=$lt_sys_lib_search_path_spec
+
+# Run-time system search path for libraries.
+sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec
+
+# Whether dlopen is supported.
+dlopen_support=$enable_dlopen
+
+# Whether dlopen of programs is supported.
+dlopen_self=$enable_dlopen_self
+
+# Whether dlopen of statically linked programs is supported.
+dlopen_self_static=$enable_dlopen_self_static
+
+# Commands to strip libraries.
+old_striplib=$lt_old_striplib
+striplib=$lt_striplib
+
+
+# The linker used to build libraries.
+LD=$lt_LD
+
+# Commands used to build an old-style archive.
+old_archive_cmds=$lt_old_archive_cmds
+
+# A language specific compiler.
+CC=$lt_compiler
+
+# Is the compiler the GNU compiler?
+with_gcc=$GCC
+
+# Compiler flag to turn off builtin functions.
+no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag
+
+# How to pass a linker flag through the compiler.
+wl=$lt_lt_prog_compiler_wl
+
+# Additional compiler flags for building library objects.
+pic_flag=$lt_lt_prog_compiler_pic
+
+# Compiler flag to prevent dynamic linking.
+link_static_flag=$lt_lt_prog_compiler_static
+
+# Does compiler simultaneously support -c and -o options?
+compiler_c_o=$lt_lt_cv_prog_compiler_c_o
+
+# Whether or not to add -lc for building shared libraries.
+build_libtool_need_lc=$archive_cmds_need_lc
+
+# Whether or not to disallow shared libs when runtime libs are static.
+allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes
+
+# Compiler flag to allow reflexive dlopens.
+export_dynamic_flag_spec=$lt_export_dynamic_flag_spec
+
+# Compiler flag to generate shared objects directly from archives.
+whole_archive_flag_spec=$lt_whole_archive_flag_spec
+
+# Whether the compiler copes with passing no objects directly.
+compiler_needs_object=$lt_compiler_needs_object
+
+# Create an old-style archive from a shared archive.
+old_archive_from_new_cmds=$lt_old_archive_from_new_cmds
+
+# Create a temporary old-style archive to link instead of a shared archive.
+old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds
+
+# Commands used to build a shared archive.
+archive_cmds=$lt_archive_cmds
+archive_expsym_cmds=$lt_archive_expsym_cmds
+
+# Commands used to build a loadable module if different from building
+# a shared archive.
+module_cmds=$lt_module_cmds
+module_expsym_cmds=$lt_module_expsym_cmds
+
+# Whether we are building with GNU ld or not.
+with_gnu_ld=$lt_with_gnu_ld
+
+# Flag that allows shared libraries with undefined symbols to be built.
+allow_undefined_flag=$lt_allow_undefined_flag
+
+# Flag that enforces no undefined symbols.
+no_undefined_flag=$lt_no_undefined_flag
+
+# Flag to hardcode \$libdir into a binary during linking.
+# This must work even if \$libdir does not exist
+hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec
+
+# If ld is used when linking, flag to hardcode \$libdir into a binary
+# during linking. This must work even if \$libdir does not exist.
+hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld
+
+# Whether we need a single "-rpath" flag with a separated argument.
+hardcode_libdir_separator=$lt_hardcode_libdir_separator
+
+# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
+# DIR into the resulting binary.
+hardcode_direct=$hardcode_direct
+
+# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
+# DIR into the resulting binary and the resulting library dependency is
+# "absolute",i.e impossible to change by setting \${shlibpath_var} if the
+# library is relocated.
+hardcode_direct_absolute=$hardcode_direct_absolute
+
+# Set to "yes" if using the -LDIR flag during linking hardcodes DIR
+# into the resulting binary.
+hardcode_minus_L=$hardcode_minus_L
+
+# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
+# into the resulting binary.
+hardcode_shlibpath_var=$hardcode_shlibpath_var
+
+# Set to "yes" if building a shared library automatically hardcodes DIR
+# into the library and all subsequent libraries and executables linked
+# against it.
+hardcode_automatic=$hardcode_automatic
+
+# Set to yes if linker adds runtime paths of dependent libraries
+# to runtime path list.
+inherit_rpath=$inherit_rpath
+
+# Whether libtool must link a program against all its dependency libraries.
+link_all_deplibs=$link_all_deplibs
+
+# Fix the shell variable \$srcfile for the compiler.
+fix_srcfile_path=$lt_fix_srcfile_path
+
+# Set to "yes" if exported symbols are required.
+always_export_symbols=$always_export_symbols
+
+# The commands to list exported symbols.
+export_symbols_cmds=$lt_export_symbols_cmds
+
+# Symbols that should not be listed in the preloaded symbols.
+exclude_expsyms=$lt_exclude_expsyms
+
+# Symbols that must always be exported.
+include_expsyms=$lt_include_expsyms
+
+# Commands necessary for linking programs (against libraries) with templates.
+prelink_cmds=$lt_prelink_cmds
+
+# Specify filename containing input files.
+file_list_spec=$lt_file_list_spec
+
+# How to hardcode a shared library path into an executable.
+hardcode_action=$hardcode_action
+
+# ### END LIBTOOL CONFIG
+
+_LT_EOF
+
+ case $host_os in
+ aix3*)
+ cat <<\_LT_EOF >> "$cfgfile"
+# AIX sometimes has problems with the GCC collect2 program. For some
+# reason, if we set the COLLECT_NAMES environment variable, the problems
+# vanish in a puff of smoke.
+if test "X${COLLECT_NAMES+set}" != Xset; then
+ COLLECT_NAMES=
+ export COLLECT_NAMES
+fi
+_LT_EOF
+ ;;
+ esac
+
+
+ltmain="$ac_aux_dir/ltmain.sh"
+
+
+ # We use sed instead of cat because bash on DJGPP gets confused if
+ # if finds mixed CR/LF and LF-only lines. Since sed operates in
+ # text mode, it properly converts lines to CR/LF. This bash problem
+ # is reportedly fixed, but why not run on old versions too?
+ sed '/^# Generated shell functions inserted here/q' "$ltmain" >> "$cfgfile" \
+ || (rm -f "$cfgfile"; exit 1)
+
+ case $xsi_shell in
+ yes)
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_dirname file append nondir_replacement
+# Compute the dirname of FILE. If nonempty, add APPEND to the result,
+# otherwise set result to NONDIR_REPLACEMENT.
+func_dirname ()
+{
+ case ${1} in
+ */*) func_dirname_result="${1%/*}${2}" ;;
+ * ) func_dirname_result="${3}" ;;
+ esac
+}
+
+# func_basename file
+func_basename ()
+{
+ func_basename_result="${1##*/}"
+}
+
+# func_dirname_and_basename file append nondir_replacement
+# perform func_basename and func_dirname in a single function
+# call:
+# dirname: Compute the dirname of FILE. If nonempty,
+# add APPEND to the result, otherwise set result
+# to NONDIR_REPLACEMENT.
+# value returned in "$func_dirname_result"
+# basename: Compute filename of FILE.
+# value retuned in "$func_basename_result"
+# Implementation must be kept synchronized with func_dirname
+# and func_basename. For efficiency, we do not delegate to
+# those functions but instead duplicate the functionality here.
+func_dirname_and_basename ()
+{
+ case ${1} in
+ */*) func_dirname_result="${1%/*}${2}" ;;
+ * ) func_dirname_result="${3}" ;;
+ esac
+ func_basename_result="${1##*/}"
+}
+
+# func_stripname prefix suffix name
+# strip PREFIX and SUFFIX off of NAME.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+func_stripname ()
+{
+ # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are
+ # positional parameters, so assign one to ordinary parameter first.
+ func_stripname_result=${3}
+ func_stripname_result=${func_stripname_result#"${1}"}
+ func_stripname_result=${func_stripname_result%"${2}"}
+}
+
+# func_opt_split
+func_opt_split ()
+{
+ func_opt_split_opt=${1%%=*}
+ func_opt_split_arg=${1#*=}
+}
+
+# func_lo2o object
+func_lo2o ()
+{
+ case ${1} in
+ *.lo) func_lo2o_result=${1%.lo}.${objext} ;;
+ *) func_lo2o_result=${1} ;;
+ esac
+}
+
+# func_xform libobj-or-source
+func_xform ()
+{
+ func_xform_result=${1%.*}.lo
+}
+
+# func_arith arithmetic-term...
+func_arith ()
+{
+ func_arith_result=$(( $* ))
+}
+
+# func_len string
+# STRING may not start with a hyphen.
+func_len ()
+{
+ func_len_result=${#1}
+}
+
+_LT_EOF
+ ;;
+ *) # Bourne compatible functions.
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_dirname file append nondir_replacement
+# Compute the dirname of FILE. If nonempty, add APPEND to the result,
+# otherwise set result to NONDIR_REPLACEMENT.
+func_dirname ()
+{
+ # Extract subdirectory from the argument.
+ func_dirname_result=`$ECHO "X${1}" | $Xsed -e "$dirname"`
+ if test "X$func_dirname_result" = "X${1}"; then
+ func_dirname_result="${3}"
+ else
+ func_dirname_result="$func_dirname_result${2}"
+ fi
+}
+
+# func_basename file
+func_basename ()
+{
+ func_basename_result=`$ECHO "X${1}" | $Xsed -e "$basename"`
+}
+
+
+# func_stripname prefix suffix name
+# strip PREFIX and SUFFIX off of NAME.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+# func_strip_suffix prefix name
+func_stripname ()
+{
+ case ${2} in
+ .*) func_stripname_result=`$ECHO "X${3}" \
+ | $Xsed -e "s%^${1}%%" -e "s%\\\\${2}\$%%"`;;
+ *) func_stripname_result=`$ECHO "X${3}" \
+ | $Xsed -e "s%^${1}%%" -e "s%${2}\$%%"`;;
+ esac
+}
+
+# sed scripts:
+my_sed_long_opt='1s/^\(-[^=]*\)=.*/\1/;q'
+my_sed_long_arg='1s/^-[^=]*=//'
+
+# func_opt_split
+func_opt_split ()
+{
+ func_opt_split_opt=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_opt"`
+ func_opt_split_arg=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_arg"`
+}
+
+# func_lo2o object
+func_lo2o ()
+{
+ func_lo2o_result=`$ECHO "X${1}" | $Xsed -e "$lo2o"`
+}
+
+# func_xform libobj-or-source
+func_xform ()
+{
+ func_xform_result=`$ECHO "X${1}" | $Xsed -e 's/\.[^.]*$/.lo/'`
+}
+
+# func_arith arithmetic-term...
+func_arith ()
+{
+ func_arith_result=`expr "$@"`
+}
+
+# func_len string
+# STRING may not start with a hyphen.
+func_len ()
+{
+ func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len`
+}
+
+_LT_EOF
+esac
+
+case $lt_shell_append in
+ yes)
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_append var value
+# Append VALUE to the end of shell variable VAR.
+func_append ()
+{
+ eval "$1+=\$2"
+}
+_LT_EOF
+ ;;
+ *)
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_append var value
+# Append VALUE to the end of shell variable VAR.
+func_append ()
+{
+ eval "$1=\$$1\$2"
+}
+
+_LT_EOF
+ ;;
+ esac
+
+
+ sed -n '/^# Generated shell functions inserted here/,$p' "$ltmain" >> "$cfgfile" \
+ || (rm -f "$cfgfile"; exit 1)
+
+ mv -f "$cfgfile" "$ofile" ||
+ (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
+ chmod +x "$ofile"
+
+ ;;
+ "po-directories":C)
+ for ac_file in $CONFIG_FILES; do
+ # Support "outfile[:infile[:infile...]]"
+ case "$ac_file" in
+ *:*) ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ esac
+ # PO directories have a Makefile.in generated from Makefile.in.in.
+ case "$ac_file" in */Makefile.in)
+ # Adjust a relative srcdir.
+ ac_dir=`echo "$ac_file"|sed 's%/[^/][^/]*$%%'`
+ ac_dir_suffix="/`echo "$ac_dir"|sed 's%^\./%%'`"
+ ac_dots=`echo "$ac_dir_suffix"|sed 's%/[^/]*%../%g'`
+ # In autoconf-2.13 it is called $ac_given_srcdir.
+ # In autoconf-2.50 it is called $srcdir.
+ test -n "$ac_given_srcdir" || ac_given_srcdir="$srcdir"
+ case "$ac_given_srcdir" in
+ .) top_srcdir=`echo $ac_dots|sed 's%/$%%'` ;;
+ /*) top_srcdir="$ac_given_srcdir" ;;
+ *) top_srcdir="$ac_dots$ac_given_srcdir" ;;
+ esac
+ # Treat a directory as a PO directory if and only if it has a
+ # POTFILES.in file. This allows packages to have multiple PO
+ # directories under different names or in different locations.
+ if test -f "$ac_given_srcdir/$ac_dir/POTFILES.in"; then
+ rm -f "$ac_dir/POTFILES"
+ test -n "$as_me" && echo "$as_me: creating $ac_dir/POTFILES" || echo "creating $ac_dir/POTFILES"
+ cat "$ac_given_srcdir/$ac_dir/POTFILES.in" | sed -e "/^#/d" -e "/^[ ]*\$/d" -e "s,.*, $top_srcdir/& \\\\," | sed -e "\$s/\(.*\) \\\\/\1/" > "$ac_dir/POTFILES"
+ POMAKEFILEDEPS="POTFILES.in"
+ # ALL_LINGUAS, POFILES, UPDATEPOFILES, DUMMYPOFILES, GMOFILES depend
+ # on $ac_dir but don't depend on user-specified configuration
+ # parameters.
+ if test -f "$ac_given_srcdir/$ac_dir/LINGUAS"; then
+ # The LINGUAS file contains the set of available languages.
+ if test -n "$OBSOLETE_ALL_LINGUAS"; then
+ test -n "$as_me" && echo "$as_me: setting ALL_LINGUAS in configure.in is obsolete" || echo "setting ALL_LINGUAS in configure.in is obsolete"
+ fi
+ ALL_LINGUAS_=`sed -e "/^#/d" -e "s/#.*//" "$ac_given_srcdir/$ac_dir/LINGUAS"`
+ # Hide the ALL_LINGUAS assigment from automake < 1.5.
+ eval 'ALL_LINGUAS''=$ALL_LINGUAS_'
+ POMAKEFILEDEPS="$POMAKEFILEDEPS LINGUAS"
+ else
+ # The set of available languages was given in configure.in.
+ # Hide the ALL_LINGUAS assigment from automake < 1.5.
+ eval 'ALL_LINGUAS''=$OBSOLETE_ALL_LINGUAS'
+ fi
+ # Compute POFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).po)
+ # Compute UPDATEPOFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(lang).po-update)
+ # Compute DUMMYPOFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(lang).nop)
+ # Compute GMOFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).gmo)
+ case "$ac_given_srcdir" in
+ .) srcdirpre= ;;
+ *) srcdirpre='$(srcdir)/' ;;
+ esac
+ POFILES=
+ UPDATEPOFILES=
+ DUMMYPOFILES=
+ GMOFILES=
+ for lang in $ALL_LINGUAS; do
+ POFILES="$POFILES $srcdirpre$lang.po"
+ UPDATEPOFILES="$UPDATEPOFILES $lang.po-update"
+ DUMMYPOFILES="$DUMMYPOFILES $lang.nop"
+ GMOFILES="$GMOFILES $srcdirpre$lang.gmo"
+ done
+ # CATALOGS depends on both $ac_dir and the user's LINGUAS
+ # environment variable.
+ INST_LINGUAS=
+ if test -n "$ALL_LINGUAS"; then
+ for presentlang in $ALL_LINGUAS; do
+ useit=no
+ if test "%UNSET%" != "$LINGUAS"; then
+ desiredlanguages="$LINGUAS"
+ else
+ desiredlanguages="$ALL_LINGUAS"
+ fi
+ for desiredlang in $desiredlanguages; do
+ # Use the presentlang catalog if desiredlang is
+ # a. equal to presentlang, or
+ # b. a variant of presentlang (because in this case,
+ # presentlang can be used as a fallback for messages
+ # which are not translated in the desiredlang catalog).
+ case "$desiredlang" in
+ "$presentlang"*) useit=yes;;
+ esac
+ done
+ if test $useit = yes; then
+ INST_LINGUAS="$INST_LINGUAS $presentlang"
+ fi
+ done
+ fi
+ CATALOGS=
+ if test -n "$INST_LINGUAS"; then
+ for lang in $INST_LINGUAS; do
+ CATALOGS="$CATALOGS $lang.gmo"
+ done
+ fi
+ test -n "$as_me" && echo "$as_me: creating $ac_dir/Makefile" || echo "creating $ac_dir/Makefile"
+ sed -e "/^POTFILES =/r $ac_dir/POTFILES" -e "/^# Makevars/r $ac_given_srcdir/$ac_dir/Makevars" -e "s|@POFILES@|$POFILES|g" -e "s|@UPDATEPOFILES@|$UPDATEPOFILES|g" -e "s|@DUMMYPOFILES@|$DUMMYPOFILES|g" -e "s|@GMOFILES@|$GMOFILES|g" -e "s|@CATALOGS@|$CATALOGS|g" -e "s|@POMAKEFILEDEPS@|$POMAKEFILEDEPS|g" "$ac_dir/Makefile.in" > "$ac_dir/Makefile"
+ for f in "$ac_given_srcdir/$ac_dir"/Rules-*; do
+ if test -f "$f"; then
+ case "$f" in
+ *.orig | *.bak | *~) ;;
+ *) cat "$f" >> "$ac_dir/Makefile" ;;
+ esac
+ fi
+ done
+ fi
+ ;;
+ esac
+ done ;;
+
+ esac
+done # for ac_tag
+
+
+as_fn_exit 0
+_ACEOF
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+ as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded. So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status. When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+ ac_cs_success=:
+ ac_config_status_args=
+ test "$silent" = yes &&
+ ac_config_status_args="$ac_config_status_args --quiet"
+ exec 5>/dev/null
+ $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+ exec 5>>config.log
+ # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+ # would make configure fail if this is the last instruction.
+ $ac_cs_success || as_fn_exit 1
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
diff --git a/autom4te.cache/requests b/autom4te.cache/requests
new file mode 100644
index 00000000..fc87f7e4
--- /dev/null
+++ b/autom4te.cache/requests
@@ -0,0 +1,666 @@
+# This file was generated.
+# It contains the lists of macros which have been traced.
+# It can be safely removed.
+
+@request = (
+ bless( [
+ '0',
+ 1,
+ [
+ '/usr/share/autoconf'
+ ],
+ [
+ '/usr/share/autoconf/autoconf/autoconf.m4f',
+ '/usr/share/aclocal/argz.m4',
+ '/usr/share/aclocal/libtool.m4',
+ '/usr/share/aclocal/ltdl.m4',
+ '/usr/share/aclocal/ltoptions.m4',
+ '/usr/share/aclocal/ltsugar.m4',
+ '/usr/share/aclocal/ltversion.m4',
+ '/usr/share/aclocal/lt~obsolete.m4',
+ '/usr/share/aclocal-1.11/amversion.m4',
+ '/usr/share/aclocal-1.11/auxdir.m4',
+ '/usr/share/aclocal-1.11/cond.m4',
+ '/usr/share/aclocal-1.11/depend.m4',
+ '/usr/share/aclocal-1.11/depout.m4',
+ '/usr/share/aclocal-1.11/init.m4',
+ '/usr/share/aclocal-1.11/install-sh.m4',
+ '/usr/share/aclocal-1.11/lead-dot.m4',
+ '/usr/share/aclocal-1.11/maintainer.m4',
+ '/usr/share/aclocal-1.11/make.m4',
+ '/usr/share/aclocal-1.11/missing.m4',
+ '/usr/share/aclocal-1.11/mkdirp.m4',
+ '/usr/share/aclocal-1.11/options.m4',
+ '/usr/share/aclocal-1.11/runlog.m4',
+ '/usr/share/aclocal-1.11/sanity.m4',
+ '/usr/share/aclocal-1.11/silent.m4',
+ '/usr/share/aclocal-1.11/strip.m4',
+ '/usr/share/aclocal-1.11/substnot.m4',
+ '/usr/share/aclocal-1.11/tar.m4',
+ 'm4/acx_pthread.m4',
+ 'm4/codeset.m4',
+ 'm4/gettext.m4',
+ 'm4/glibc2.m4',
+ 'm4/glibc21.m4',
+ 'm4/iconv.m4',
+ 'm4/intdiv0.m4',
+ 'm4/intl.m4',
+ 'm4/intmax.m4',
+ 'm4/inttypes-pri.m4',
+ 'm4/inttypes_h.m4',
+ 'm4/lcmessage.m4',
+ 'm4/lib-ld.m4',
+ 'm4/lib-link.m4',
+ 'm4/lib-prefix.m4',
+ 'm4/lock.m4',
+ 'm4/longdouble.m4',
+ 'm4/longlong.m4',
+ 'm4/nls.m4',
+ 'm4/po.m4',
+ 'm4/printf-posix.m4',
+ 'm4/progtest.m4',
+ 'm4/size_max.m4',
+ 'm4/stdint_h.m4',
+ 'm4/uintmax_t.m4',
+ 'm4/ulonglong.m4',
+ 'm4/visibility.m4',
+ 'm4/wchar_t.m4',
+ 'm4/wint_t.m4',
+ 'm4/xsize.m4',
+ 'configure.ac'
+ ],
+ {
+ 'AM_ENABLE_STATIC' => 1,
+ 'AC_LIBTOOL_LANG_RC_CONFIG' => 1,
+ '_LT_AC_SHELL_INIT' => 1,
+ 'AC_DEFUN' => 1,
+ 'AC_PROG_LIBTOOL' => 1,
+ '_LT_AC_LANG_CXX_CONFIG' => 1,
+ 'AM_PROG_MKDIR_P' => 1,
+ 'gl_AC_HEADER_STDINT_H' => 1,
+ 'AM_AUTOMAKE_VERSION' => 1,
+ 'gl_SIZE_MAX' => 1,
+ 'AC_LIB_RPATH' => 1,
+ 'AM_SUBST_NOTMAKE' => 1,
+ 'AM_MISSING_PROG' => 1,
+ 'gt_PRINTF_POSIX' => 1,
+ 'AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH' => 1,
+ '_LT_AC_LANG_C_CONFIG' => 1,
+ 'AM_PROG_INSTALL_STRIP' => 1,
+ 'gl_LOCK_EARLY' => 1,
+ '_m4_warn' => 1,
+ 'AC_LIBTOOL_OBJDIR' => 1,
+ 'gl_FUNC_ARGZ' => 1,
+ 'AM_SANITY_CHECK' => 1,
+ 'LTOBSOLETE_VERSION' => 1,
+ 'AC_LIBTOOL_LANG_GCJ_CONFIG' => 1,
+ 'AC_LIBTOOL_PROG_COMPILER_PIC' => 1,
+ 'LT_LIB_M' => 1,
+ '_LT_AC_CHECK_DLFCN' => 1,
+ 'AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE' => 1,
+ 'LTSUGAR_VERSION' => 1,
+ '_LT_PROG_LTMAIN' => 1,
+ 'gl_AC_TYPE_LONG_LONG' => 1,
+ '_AM_PROG_TAR' => 1,
+ 'LT_SYS_SYMBOL_USCORE' => 1,
+ 'AC_LIBTOOL_GCJ' => 1,
+ 'LT_FUNC_DLSYM_USCORE' => 1,
+ 'LT_SYS_DLOPEN_DEPLIBS' => 1,
+ '_LT_AC_LANG_F77' => 1,
+ 'AC_LIBTOOL_CONFIG' => 1,
+ 'AC_LIB_ARG_WITH' => 1,
+ '_AM_SUBST_NOTMAKE' => 1,
+ 'AC_LTDL_DLLIB' => 1,
+ '_AM_AUTOCONF_VERSION' => 1,
+ 'AM_DISABLE_SHARED' => 1,
+ '_LTDL_SETUP' => 1,
+ '_LT_AC_LANG_CXX' => 1,
+ 'AM_PROG_LIBTOOL' => 1,
+ 'AC_LIB_LTDL' => 1,
+ '_LT_AC_FILE_LTDLL_C' => 1,
+ 'AM_PROG_LD' => 1,
+ 'gt_INTL_MACOSX' => 1,
+ 'AM_ICONV_LINK' => 1,
+ 'AC_LIB_PREPARE_MULTILIB' => 1,
+ 'AU_DEFUN' => 1,
+ 'AC_PROG_NM' => 1,
+ 'AC_LIBTOOL_DLOPEN' => 1,
+ 'AC_PROG_LD' => 1,
+ 'AC_PROG_LD_GNU' => 1,
+ 'AC_ENABLE_FAST_INSTALL' => 1,
+ 'gt_TYPE_INTMAX_T' => 1,
+ 'AC_LIB_HAVE_LINKFLAGS' => 1,
+ 'AC_LIBTOOL_FC' => 1,
+ 'AM_ICONV_LINKFLAGS_BODY' => 1,
+ 'LTDL_CONVENIENCE' => 1,
+ '_AM_SET_OPTION' => 1,
+ 'AC_LTDL_PREOPEN' => 1,
+ '_LT_LINKER_BOILERPLATE' => 1,
+ 'gl_LOCK_EARLY_BODY' => 1,
+ 'AC_LIBTOOL_PROG_CC_C_O' => 1,
+ 'AC_LIBTOOL_LANG_CXX_CONFIG' => 1,
+ 'gl_PREREQ_ARGZ' => 1,
+ 'AC_LIB_PREFIX' => 1,
+ 'gt_TYPE_LONGDOUBLE' => 1,
+ 'AM_OUTPUT_DEPENDENCY_COMMANDS' => 1,
+ 'LT_SUPPORTED_TAG' => 1,
+ 'LT_SYS_MODULE_EXT' => 1,
+ 'LT_PROG_RC' => 1,
+ 'AC_DEFUN_ONCE' => 1,
+ '_LT_AC_LANG_GCJ' => 1,
+ 'AC_' => 1,
+ 'AC_LTDL_OBJDIR' => 1,
+ '_LT_PATH_TOOL_PREFIX' => 1,
+ 'gt_INTDIV0' => 1,
+ 'AC_LIBTOOL_RC' => 1,
+ 'AM_ICONV' => 1,
+ 'AM_SILENT_RULES' => 1,
+ 'AC_DISABLE_FAST_INSTALL' => 1,
+ '_LT_AC_PROG_ECHO_BACKSLASH' => 1,
+ '_LT_AC_SYS_LIBPATH_AIX' => 1,
+ '_LT_AC_TRY_DLOPEN_SELF' => 1,
+ 'include' => 1,
+ 'LT_AC_PROG_SED' => 1,
+ 'AM_ENABLE_SHARED' => 1,
+ 'gl_AC_TYPE_UNSIGNED_LONG_LONG' => 1,
+ 'AC_LIB_APPENDTOVAR' => 1,
+ 'LTDL_INSTALLABLE' => 1,
+ 'AM_GNU_GETTEXT' => 1,
+ '_LT_AC_LANG_GCJ_CONFIG' => 1,
+ 'AC_ENABLE_SHARED' => 1,
+ 'AM_POSTPROCESS_PO_MAKEFILE' => 1,
+ 'AC_LIB_WITH_FINAL_PREFIX' => 1,
+ 'gt_TYPE_WINT_T' => 1,
+ 'AC_ENABLE_STATIC' => 1,
+ 'AC_LIBTOOL_SYS_HARD_LINK_LOCKS' => 1,
+ '_LT_AC_TAGVAR' => 1,
+ 'AC_LIBTOOL_LANG_F77_CONFIG' => 1,
+ 'AM_CONDITIONAL' => 1,
+ 'LT_LIB_DLLOAD' => 1,
+ 'AM_LANGINFO_CODESET' => 1,
+ 'gl_AC_HEADER_INTTYPES_H' => 1,
+ 'LTDL_INIT' => 1,
+ 'LTVERSION_VERSION' => 1,
+ 'AM_PROG_INSTALL_SH' => 1,
+ 'm4_include' => 1,
+ 'AC_PROG_EGREP' => 1,
+ '_AC_AM_CONFIG_HEADER_HOOK' => 1,
+ 'AC_PATH_MAGIC' => 1,
+ 'PINEVAR' => 1,
+ 'AC_LTDL_SYSSEARCHPATH' => 1,
+ 'gl_PREREQ_LOCK' => 1,
+ 'AM_MAKE_INCLUDE' => 1,
+ 'LT_CMD_MAX_LEN' => 1,
+ '_LT_AC_TAGCONFIG' => 1,
+ 'm4_pattern_forbid' => 1,
+ 'gt_INTTYPES_PRI' => 1,
+ 'AM_PO_SUBDIRS' => 1,
+ 'AC_LIB_PREPARE_PREFIX' => 1,
+ '_LT_LINKER_OPTION' => 1,
+ 'AC_LIBTOOL_COMPILER_OPTION' => 1,
+ 'AC_DISABLE_SHARED' => 1,
+ '_LT_COMPILER_BOILERPLATE' => 1,
+ 'AC_LIBTOOL_SETUP' => 1,
+ 'AC_LIBTOOL_WIN32_DLL' => 1,
+ 'AC_PROG_LD_RELOAD_FLAG' => 1,
+ 'AC_LTDL_DLSYM_USCORE' => 1,
+ 'LT_LANG' => 1,
+ 'AM_MISSING_HAS_RUN' => 1,
+ 'LT_SYS_DLSEARCH_PATH' => 1,
+ 'AC_TYPE_LONG_LONG_INT' => 1,
+ 'LT_CONFIG_LTDL_DIR' => 1,
+ 'LT_OUTPUT' => 1,
+ 'AC_LIBTOOL_DLOPEN_SELF' => 1,
+ 'AM_GNU_GETTEXT_VERSION' => 1,
+ 'AC_LIB_PROG_LD_GNU' => 1,
+ 'AC_LIBTOOL_PROG_LD_SHLIBS' => 1,
+ 'AM_NLS' => 1,
+ 'AC_LIBTOOL_LINKER_OPTION' => 1,
+ 'AC_WITH_LTDL' => 1,
+ 'gt_INTL_SUBDIR_CORE' => 1,
+ 'AC_LIBTOOL_CXX' => 1,
+ 'LT_AC_PROG_RC' => 1,
+ 'LT_INIT' => 1,
+ 'LT_SYS_DLOPEN_SELF' => 1,
+ 'LT_AC_PROG_GCJ' => 1,
+ 'AM_DISABLE_STATIC' => 1,
+ 'AM_DEP_TRACK' => 1,
+ '_AC_PROG_LIBTOOL' => 1,
+ '_AM_IF_OPTION' => 1,
+ 'AC_PATH_TOOL_PREFIX' => 1,
+ 'AC_LIBTOOL_F77' => 1,
+ 'm4_pattern_allow' => 1,
+ 'AM_PATH_PROG_WITH_TEST' => 1,
+ 'AM_SET_LEADING_DOT' => 1,
+ 'LT_AC_PROG_EGREP' => 1,
+ '_AM_DEPENDENCIES' => 1,
+ 'AC_LIBTOOL_LANG_C_CONFIG' => 1,
+ 'gt_CHECK_DECL' => 1,
+ 'LTOPTIONS_VERSION' => 1,
+ '_LT_AC_SYS_COMPILER' => 1,
+ 'AC_LIB_LINKFLAGS' => 1,
+ 'AM_PROG_NM' => 1,
+ 'AC_LIBLTDL_CONVENIENCE' => 1,
+ 'AC_DEPLIBS_CHECK_METHOD' => 1,
+ 'AM_GNU_GETTEXT_NEED' => 1,
+ 'AM_SET_CURRENT_AUTOMAKE_VERSION' => 1,
+ 'jm_MAINTAINER_MODE' => 1,
+ 'AC_LIBLTDL_INSTALLABLE' => 1,
+ 'AC_LTDL_ENABLE_INSTALL' => 1,
+ 'gl_XSIZE' => 1,
+ 'AC_LIBTOOL_SYS_DYNAMIC_LINKER' => 1,
+ 'LT_PROG_GCJ' => 1,
+ 'gt_GLIBC2' => 1,
+ 'AM_INIT_AUTOMAKE' => 1,
+ 'gl_AC_TYPE_UINTMAX_T' => 1,
+ 'gl_LOCK' => 1,
+ 'AM_INTL_SUBDIR' => 1,
+ 'AC_DISABLE_STATIC' => 1,
+ 'gl_VISIBILITY' => 1,
+ 'gt_TYPE_WCHAR_T' => 1,
+ 'PINEVAR_UNQUOTED' => 1,
+ 'LT_PATH_NM' => 1,
+ 'AM_MAINTAINER_MODE' => 1,
+ 'AC_LTDL_SHLIBEXT' => 1,
+ '_LT_AC_LOCK' => 1,
+ '_LT_AC_LANG_RC_CONFIG' => 1,
+ 'LT_SYS_MODULE_PATH' => 1,
+ 'AC_LIBTOOL_POSTDEP_PREDEP' => 1,
+ 'LT_WITH_LTDL' => 1,
+ 'AC_LTDL_SHLIBPATH' => 1,
+ 'AM_AUX_DIR_EXPAND' => 1,
+ 'gl_GLIBC21' => 1,
+ 'AC_LIB_LINKFLAGS_FROM_LIBS' => 1,
+ '_LT_AC_LANG_F77_CONFIG' => 1,
+ 'AC_LIBTOOL_PROG_COMPILER_NO_RTTI' => 1,
+ '_AM_SET_OPTIONS' => 1,
+ '_LT_COMPILER_OPTION' => 1,
+ '_AM_OUTPUT_DEPENDENCY_COMMANDS' => 1,
+ 'AM_RUN_LOG' => 1,
+ 'AC_LIBTOOL_SYS_OLD_ARCHIVE' => 1,
+ 'AC_LTDL_SYS_DLOPEN_DEPLIBS' => 1,
+ 'AC_LIBTOOL_PICMODE' => 1,
+ 'AC_LIB_LINKFLAGS_BODY' => 1,
+ 'LT_PATH_LD' => 1,
+ 'AC_CHECK_LIBM' => 1,
+ 'ACX_PTHREAD' => 1,
+ 'AC_LIBTOOL_SYS_LIB_STRIP' => 1,
+ '_AM_MANGLE_OPTION' => 1,
+ 'gt_LC_MESSAGES' => 1,
+ 'AC_TYPE_UNSIGNED_LONG_LONG_INT' => 1,
+ 'AC_LTDL_SYMBOL_USCORE' => 1,
+ 'AC_LIBTOOL_SYS_MAX_CMD_LEN' => 1,
+ 'AM_SET_DEPDIR' => 1,
+ '_LT_CC_BASENAME' => 1,
+ 'gl_LOCK_BODY' => 1,
+ '_LT_LIBOBJ' => 1,
+ 'AC_LIB_PROG_LD' => 1
+ }
+ ], 'Autom4te::Request' ),
+ bless( [
+ '1',
+ 1,
+ [
+ '/usr/share/autoconf'
+ ],
+ [
+ '/usr/share/autoconf/autoconf/autoconf.m4f',
+ 'aclocal.m4',
+ 'configure.ac'
+ ],
+ {
+ '_LT_AC_TAGCONFIG' => 1,
+ 'AM_PROG_F77_C_O' => 1,
+ 'AC_LIBLTDL_CONVENIENCE' => 1,
+ 'm4_pattern_forbid' => 1,
+ 'AC_INIT' => 1,
+ '_AM_COND_IF' => 1,
+ 'AC_CANONICAL_TARGET' => 1,
+ 'AC_LIBLTDL_INSTALLABLE' => 1,
+ 'AC_SUBST' => 1,
+ 'AC_CONFIG_LIBOBJ_DIR' => 1,
+ 'AC_FC_SRCEXT' => 1,
+ 'AC_CANONICAL_HOST' => 1,
+ 'AC_PROG_LIBTOOL' => 1,
+ 'AM_INIT_AUTOMAKE' => 1,
+ 'AM_PATH_GUILE' => 1,
+ 'AC_CONFIG_SUBDIRS' => 1,
+ 'AM_AUTOMAKE_VERSION' => 1,
+ 'LT_CONFIG_LTDL_DIR' => 1,
+ 'AC_REQUIRE_AUX_FILE' => 1,
+ 'AC_CONFIG_LINKS' => 1,
+ 'm4_sinclude' => 1,
+ 'LT_SUPPORTED_TAG' => 1,
+ 'AM_MAINTAINER_MODE' => 1,
+ 'AM_NLS' => 1,
+ 'AM_GNU_GETTEXT_INTL_SUBDIR' => 1,
+ 'AM_MAKEFILE_INCLUDE' => 1,
+ '_m4_warn' => 1,
+ 'AM_PROG_CXX_C_O' => 1,
+ '_AM_COND_ENDIF' => 1,
+ '_AM_MAKEFILE_INCLUDE' => 1,
+ 'AM_ENABLE_MULTILIB' => 1,
+ 'AM_SILENT_RULES' => 1,
+ 'AM_PROG_MOC' => 1,
+ 'AC_CONFIG_FILES' => 1,
+ 'LT_INIT' => 1,
+ 'include' => 1,
+ 'AM_PROG_AR' => 1,
+ 'AM_GNU_GETTEXT' => 1,
+ 'AC_LIBSOURCE' => 1,
+ 'AM_PROG_FC_C_O' => 1,
+ 'AC_CANONICAL_BUILD' => 1,
+ 'AC_FC_FREEFORM' => 1,
+ 'AH_OUTPUT' => 1,
+ '_AM_SUBST_NOTMAKE' => 1,
+ 'AC_CONFIG_AUX_DIR' => 1,
+ 'sinclude' => 1,
+ 'AM_PROG_CC_C_O' => 1,
+ 'm4_pattern_allow' => 1,
+ 'AM_XGETTEXT_OPTION' => 1,
+ 'AC_CANONICAL_SYSTEM' => 1,
+ 'AM_CONDITIONAL' => 1,
+ 'AC_CONFIG_HEADERS' => 1,
+ 'AC_DEFINE_TRACE_LITERAL' => 1,
+ 'AM_POT_TOOLS' => 1,
+ 'm4_include' => 1,
+ '_AM_COND_ELSE' => 1,
+ 'AC_SUBST_TRACE' => 1
+ }
+ ], 'Autom4te::Request' ),
+ bless( [
+ '2',
+ 1,
+ [
+ '/usr/share/autoconf'
+ ],
+ [
+ '/usr/share/autoconf/autoconf/autoconf.m4f',
+ '/usr/share/aclocal/argz.m4',
+ '/usr/share/aclocal/ltdl.m4',
+ '/usr/share/aclocal-1.11/amversion.m4',
+ '/usr/share/aclocal-1.11/auxdir.m4',
+ '/usr/share/aclocal-1.11/cond.m4',
+ '/usr/share/aclocal-1.11/depend.m4',
+ '/usr/share/aclocal-1.11/depout.m4',
+ '/usr/share/aclocal-1.11/init.m4',
+ '/usr/share/aclocal-1.11/install-sh.m4',
+ '/usr/share/aclocal-1.11/lead-dot.m4',
+ '/usr/share/aclocal-1.11/maintainer.m4',
+ '/usr/share/aclocal-1.11/make.m4',
+ '/usr/share/aclocal-1.11/missing.m4',
+ '/usr/share/aclocal-1.11/mkdirp.m4',
+ '/usr/share/aclocal-1.11/options.m4',
+ '/usr/share/aclocal-1.11/runlog.m4',
+ '/usr/share/aclocal-1.11/sanity.m4',
+ '/usr/share/aclocal-1.11/silent.m4',
+ '/usr/share/aclocal-1.11/strip.m4',
+ '/usr/share/aclocal-1.11/substnot.m4',
+ '/usr/share/aclocal-1.11/tar.m4',
+ 'm4/acx_pthread.m4',
+ 'm4/codeset.m4',
+ 'm4/gettext.m4',
+ 'm4/glibc2.m4',
+ 'm4/glibc21.m4',
+ 'm4/iconv.m4',
+ 'm4/intdiv0.m4',
+ 'm4/intl.m4',
+ 'm4/intmax.m4',
+ 'm4/inttypes-pri.m4',
+ 'm4/inttypes_h.m4',
+ 'm4/lcmessage.m4',
+ 'm4/lib-ld.m4',
+ 'm4/lib-link.m4',
+ 'm4/lib-prefix.m4',
+ 'm4/libtool.m4',
+ 'm4/lock.m4',
+ 'm4/longdouble.m4',
+ 'm4/longlong.m4',
+ 'm4/ltoptions.m4',
+ 'm4/ltsugar.m4',
+ 'm4/ltversion.m4',
+ 'm4/lt~obsolete.m4',
+ 'm4/nls.m4',
+ 'm4/po.m4',
+ 'm4/printf-posix.m4',
+ 'm4/progtest.m4',
+ 'm4/size_max.m4',
+ 'm4/stdint_h.m4',
+ 'm4/uintmax_t.m4',
+ 'm4/ulonglong.m4',
+ 'm4/visibility.m4',
+ 'm4/wchar_t.m4',
+ 'm4/wint_t.m4',
+ 'm4/xsize.m4',
+ 'configure.ac'
+ ],
+ {
+ 'AM_ENABLE_STATIC' => 1,
+ 'AC_LIBTOOL_LANG_RC_CONFIG' => 1,
+ '_LT_AC_SHELL_INIT' => 1,
+ 'AC_DEFUN' => 1,
+ 'AC_PROG_LIBTOOL' => 1,
+ '_LT_AC_LANG_CXX_CONFIG' => 1,
+ 'AM_PROG_MKDIR_P' => 1,
+ 'gl_AC_HEADER_STDINT_H' => 1,
+ 'AM_AUTOMAKE_VERSION' => 1,
+ 'gl_SIZE_MAX' => 1,
+ 'AC_LIB_RPATH' => 1,
+ 'AM_SUBST_NOTMAKE' => 1,
+ 'AM_MISSING_PROG' => 1,
+ 'gt_PRINTF_POSIX' => 1,
+ 'AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH' => 1,
+ '_LT_AC_LANG_C_CONFIG' => 1,
+ 'AM_PROG_INSTALL_STRIP' => 1,
+ 'gl_LOCK_EARLY' => 1,
+ '_m4_warn' => 1,
+ 'AC_LIBTOOL_OBJDIR' => 1,
+ 'gl_FUNC_ARGZ' => 1,
+ 'AM_SANITY_CHECK' => 1,
+ 'LTOBSOLETE_VERSION' => 1,
+ 'AC_LIBTOOL_LANG_GCJ_CONFIG' => 1,
+ 'AC_LIBTOOL_PROG_COMPILER_PIC' => 1,
+ 'LT_LIB_M' => 1,
+ '_LT_AC_CHECK_DLFCN' => 1,
+ 'AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE' => 1,
+ 'LTSUGAR_VERSION' => 1,
+ '_LT_PROG_LTMAIN' => 1,
+ 'gl_AC_TYPE_LONG_LONG' => 1,
+ '_AM_PROG_TAR' => 1,
+ 'LT_SYS_SYMBOL_USCORE' => 1,
+ 'AC_LIBTOOL_GCJ' => 1,
+ 'LT_FUNC_DLSYM_USCORE' => 1,
+ 'LT_SYS_DLOPEN_DEPLIBS' => 1,
+ '_LT_AC_LANG_F77' => 1,
+ 'AC_LIBTOOL_CONFIG' => 1,
+ 'AC_LIB_ARG_WITH' => 1,
+ '_AM_SUBST_NOTMAKE' => 1,
+ 'AC_LTDL_DLLIB' => 1,
+ '_AM_AUTOCONF_VERSION' => 1,
+ 'AM_DISABLE_SHARED' => 1,
+ '_LTDL_SETUP' => 1,
+ '_LT_AC_LANG_CXX' => 1,
+ 'AM_PROG_LIBTOOL' => 1,
+ 'AC_LIB_LTDL' => 1,
+ '_LT_AC_FILE_LTDLL_C' => 1,
+ 'AM_PROG_LD' => 1,
+ 'gt_INTL_MACOSX' => 1,
+ 'AM_ICONV_LINK' => 1,
+ 'AC_LIB_PREPARE_MULTILIB' => 1,
+ 'AU_DEFUN' => 1,
+ 'AC_PROG_NM' => 1,
+ 'AC_LIBTOOL_DLOPEN' => 1,
+ 'AC_PROG_LD' => 1,
+ 'AC_PROG_LD_GNU' => 1,
+ 'AC_ENABLE_FAST_INSTALL' => 1,
+ 'gt_TYPE_INTMAX_T' => 1,
+ 'AC_LIB_HAVE_LINKFLAGS' => 1,
+ 'AC_LIBTOOL_FC' => 1,
+ 'AM_ICONV_LINKFLAGS_BODY' => 1,
+ 'LTDL_CONVENIENCE' => 1,
+ '_AM_SET_OPTION' => 1,
+ 'AC_LTDL_PREOPEN' => 1,
+ '_LT_LINKER_BOILERPLATE' => 1,
+ 'gl_LOCK_EARLY_BODY' => 1,
+ 'AC_LIBTOOL_PROG_CC_C_O' => 1,
+ 'AC_LIBTOOL_LANG_CXX_CONFIG' => 1,
+ 'gl_PREREQ_ARGZ' => 1,
+ 'AC_LIB_PREFIX' => 1,
+ 'gt_TYPE_LONGDOUBLE' => 1,
+ 'AM_OUTPUT_DEPENDENCY_COMMANDS' => 1,
+ 'LT_SUPPORTED_TAG' => 1,
+ 'LT_SYS_MODULE_EXT' => 1,
+ 'LT_PROG_RC' => 1,
+ 'AC_DEFUN_ONCE' => 1,
+ '_LT_AC_LANG_GCJ' => 1,
+ 'AC_' => 1,
+ 'AC_LTDL_OBJDIR' => 1,
+ '_LT_PATH_TOOL_PREFIX' => 1,
+ 'gt_INTDIV0' => 1,
+ 'AC_LIBTOOL_RC' => 1,
+ 'AM_ICONV' => 1,
+ 'AM_SILENT_RULES' => 1,
+ 'AC_DISABLE_FAST_INSTALL' => 1,
+ '_LT_AC_PROG_ECHO_BACKSLASH' => 1,
+ '_LT_AC_SYS_LIBPATH_AIX' => 1,
+ '_LT_AC_TRY_DLOPEN_SELF' => 1,
+ 'include' => 1,
+ 'LT_AC_PROG_SED' => 1,
+ 'AM_ENABLE_SHARED' => 1,
+ 'gl_AC_TYPE_UNSIGNED_LONG_LONG' => 1,
+ 'AC_LIB_APPENDTOVAR' => 1,
+ 'LTDL_INSTALLABLE' => 1,
+ 'AM_GNU_GETTEXT' => 1,
+ '_LT_AC_LANG_GCJ_CONFIG' => 1,
+ 'AC_ENABLE_SHARED' => 1,
+ 'AM_POSTPROCESS_PO_MAKEFILE' => 1,
+ 'AC_LIB_WITH_FINAL_PREFIX' => 1,
+ 'gt_TYPE_WINT_T' => 1,
+ 'AC_ENABLE_STATIC' => 1,
+ 'AC_LIBTOOL_SYS_HARD_LINK_LOCKS' => 1,
+ '_LT_AC_TAGVAR' => 1,
+ 'AC_LIBTOOL_LANG_F77_CONFIG' => 1,
+ 'AM_CONDITIONAL' => 1,
+ 'LT_LIB_DLLOAD' => 1,
+ 'AM_LANGINFO_CODESET' => 1,
+ 'gl_AC_HEADER_INTTYPES_H' => 1,
+ 'LTDL_INIT' => 1,
+ 'LTVERSION_VERSION' => 1,
+ 'AM_PROG_INSTALL_SH' => 1,
+ 'm4_include' => 1,
+ 'AC_PROG_EGREP' => 1,
+ '_AC_AM_CONFIG_HEADER_HOOK' => 1,
+ 'AC_PATH_MAGIC' => 1,
+ 'PINEVAR' => 1,
+ 'AC_LTDL_SYSSEARCHPATH' => 1,
+ 'gl_PREREQ_LOCK' => 1,
+ 'AM_MAKE_INCLUDE' => 1,
+ 'LT_CMD_MAX_LEN' => 1,
+ '_LT_AC_TAGCONFIG' => 1,
+ 'm4_pattern_forbid' => 1,
+ 'gt_INTTYPES_PRI' => 1,
+ 'AM_PO_SUBDIRS' => 1,
+ 'AC_LIB_PREPARE_PREFIX' => 1,
+ '_LT_LINKER_OPTION' => 1,
+ 'AC_LIBTOOL_COMPILER_OPTION' => 1,
+ 'AC_DISABLE_SHARED' => 1,
+ '_LT_COMPILER_BOILERPLATE' => 1,
+ 'AC_LIBTOOL_SETUP' => 1,
+ 'AC_LIBTOOL_WIN32_DLL' => 1,
+ 'AC_PROG_LD_RELOAD_FLAG' => 1,
+ 'AC_LTDL_DLSYM_USCORE' => 1,
+ 'LT_LANG' => 1,
+ 'AM_MISSING_HAS_RUN' => 1,
+ 'LT_SYS_DLSEARCH_PATH' => 1,
+ 'AC_TYPE_LONG_LONG_INT' => 1,
+ 'LT_CONFIG_LTDL_DIR' => 1,
+ 'LT_OUTPUT' => 1,
+ 'AC_LIBTOOL_DLOPEN_SELF' => 1,
+ 'AM_GNU_GETTEXT_VERSION' => 1,
+ 'AC_LIB_PROG_LD_GNU' => 1,
+ 'AC_LIBTOOL_PROG_LD_SHLIBS' => 1,
+ 'AM_NLS' => 1,
+ 'AC_LIBTOOL_LINKER_OPTION' => 1,
+ 'AC_WITH_LTDL' => 1,
+ 'gt_INTL_SUBDIR_CORE' => 1,
+ 'AC_LIBTOOL_CXX' => 1,
+ 'LT_AC_PROG_RC' => 1,
+ 'LT_INIT' => 1,
+ 'LT_SYS_DLOPEN_SELF' => 1,
+ 'LT_AC_PROG_GCJ' => 1,
+ 'AM_DISABLE_STATIC' => 1,
+ 'AM_DEP_TRACK' => 1,
+ '_AC_PROG_LIBTOOL' => 1,
+ '_AM_IF_OPTION' => 1,
+ 'AC_PATH_TOOL_PREFIX' => 1,
+ 'AC_LIBTOOL_F77' => 1,
+ 'm4_pattern_allow' => 1,
+ 'AM_PATH_PROG_WITH_TEST' => 1,
+ 'AM_SET_LEADING_DOT' => 1,
+ 'LT_AC_PROG_EGREP' => 1,
+ '_AM_DEPENDENCIES' => 1,
+ 'AC_LIBTOOL_LANG_C_CONFIG' => 1,
+ 'gt_CHECK_DECL' => 1,
+ 'LTOPTIONS_VERSION' => 1,
+ '_LT_AC_SYS_COMPILER' => 1,
+ 'AC_LIB_LINKFLAGS' => 1,
+ 'AM_PROG_NM' => 1,
+ 'AC_LIBLTDL_CONVENIENCE' => 1,
+ 'AC_DEPLIBS_CHECK_METHOD' => 1,
+ 'AM_GNU_GETTEXT_NEED' => 1,
+ 'AM_SET_CURRENT_AUTOMAKE_VERSION' => 1,
+ 'jm_MAINTAINER_MODE' => 1,
+ 'AC_LIBLTDL_INSTALLABLE' => 1,
+ 'AC_LTDL_ENABLE_INSTALL' => 1,
+ 'gl_XSIZE' => 1,
+ 'AC_LIBTOOL_SYS_DYNAMIC_LINKER' => 1,
+ 'LT_PROG_GCJ' => 1,
+ 'gt_GLIBC2' => 1,
+ 'AM_INIT_AUTOMAKE' => 1,
+ 'gl_AC_TYPE_UINTMAX_T' => 1,
+ 'gl_LOCK' => 1,
+ 'AM_INTL_SUBDIR' => 1,
+ 'AC_DISABLE_STATIC' => 1,
+ 'gl_VISIBILITY' => 1,
+ 'gt_TYPE_WCHAR_T' => 1,
+ 'PINEVAR_UNQUOTED' => 1,
+ 'LT_PATH_NM' => 1,
+ 'AM_MAINTAINER_MODE' => 1,
+ 'AC_LTDL_SHLIBEXT' => 1,
+ '_LT_AC_LOCK' => 1,
+ '_LT_AC_LANG_RC_CONFIG' => 1,
+ 'LT_SYS_MODULE_PATH' => 1,
+ 'AC_LIBTOOL_POSTDEP_PREDEP' => 1,
+ 'LT_WITH_LTDL' => 1,
+ 'AC_LTDL_SHLIBPATH' => 1,
+ 'AM_AUX_DIR_EXPAND' => 1,
+ 'gl_GLIBC21' => 1,
+ 'AC_LIB_LINKFLAGS_FROM_LIBS' => 1,
+ '_LT_AC_LANG_F77_CONFIG' => 1,
+ 'AC_LIBTOOL_PROG_COMPILER_NO_RTTI' => 1,
+ '_AM_SET_OPTIONS' => 1,
+ '_LT_COMPILER_OPTION' => 1,
+ '_AM_OUTPUT_DEPENDENCY_COMMANDS' => 1,
+ 'AM_RUN_LOG' => 1,
+ 'AC_LIBTOOL_SYS_OLD_ARCHIVE' => 1,
+ 'AC_LTDL_SYS_DLOPEN_DEPLIBS' => 1,
+ 'AC_LIBTOOL_PICMODE' => 1,
+ 'AC_LIB_LINKFLAGS_BODY' => 1,
+ 'LT_PATH_LD' => 1,
+ 'AC_CHECK_LIBM' => 1,
+ 'ACX_PTHREAD' => 1,
+ 'AC_LIBTOOL_SYS_LIB_STRIP' => 1,
+ '_AM_MANGLE_OPTION' => 1,
+ 'gt_LC_MESSAGES' => 1,
+ 'AC_TYPE_UNSIGNED_LONG_LONG_INT' => 1,
+ 'AC_LTDL_SYMBOL_USCORE' => 1,
+ 'AC_LIBTOOL_SYS_MAX_CMD_LEN' => 1,
+ 'AM_SET_DEPDIR' => 1,
+ '_LT_CC_BASENAME' => 1,
+ 'gl_LOCK_BODY' => 1,
+ '_LT_LIBOBJ' => 1,
+ 'AC_LIB_PROG_LD' => 1
+ }
+ ], 'Autom4te::Request' )
+ );
+
diff --git a/autom4te.cache/traces.0 b/autom4te.cache/traces.0
new file mode 100644
index 00000000..eb3d43ea
--- /dev/null
+++ b/autom4te.cache/traces.0
@@ -0,0 +1,6015 @@
+m4trace:/usr/share/aclocal/argz.m4:12: -1- AC_DEFUN([gl_FUNC_ARGZ], [gl_PREREQ_ARGZ
+
+AC_CHECK_HEADERS([argz.h], [], [], [AC_INCLUDES_DEFAULT])
+
+AC_CHECK_TYPES([error_t],
+ [],
+ [AC_DEFINE([error_t], [int],
+ [Define to a type to use for `error_t' if it is not otherwise available.])
+ AC_DEFINE([__error_t_defined], [1], [Define so that glibc/gnulib argp.h
+ does not typedef error_t.])],
+ [#if defined(HAVE_ARGZ_H)
+# include <argz.h>
+#endif])
+
+ARGZ_H=
+AC_CHECK_FUNCS([argz_add argz_append argz_count argz_create_sep argz_insert \
+ argz_next argz_stringify], [], [ARGZ_H=argz.h; AC_LIBOBJ([argz])])
+
+dnl if have system argz functions, allow forced use of
+dnl libltdl-supplied implementation (and default to do so
+dnl on "known bad" systems). Could use a runtime check, but
+dnl (a) detecting malloc issues is notoriously unreliable
+dnl (b) only known system that declares argz functions,
+dnl provides them, yet they are broken, is cygwin
+dnl releases prior to 16-Mar-2007 (1.5.24 and earlier)
+dnl So, it's more straightforward simply to special case
+dnl this for known bad systems.
+AS_IF([test -z "$ARGZ_H"],
+ [AC_CACHE_CHECK(
+ [if argz actually works],
+ [lt_cv_sys_argz_works],
+ [[case $host_os in #(
+ *cygwin*)
+ lt_cv_sys_argz_works=no
+ if test "$cross_compiling" != no; then
+ lt_cv_sys_argz_works="guessing no"
+ else
+ lt_sed_extract_leading_digits='s/^\([0-9\.]*\).*/\1/'
+ save_IFS=$IFS
+ IFS=-.
+ set x `uname -r | sed -e "$lt_sed_extract_leading_digits"`
+ IFS=$save_IFS
+ lt_os_major=${2-0}
+ lt_os_minor=${3-0}
+ lt_os_micro=${4-0}
+ if test "$lt_os_major" -gt 1 \
+ || { test "$lt_os_major" -eq 1 \
+ && { test "$lt_os_minor" -gt 5 \
+ || { test "$lt_os_minor" -eq 5 \
+ && test "$lt_os_micro" -gt 24; }; }; }; then
+ lt_cv_sys_argz_works=yes
+ fi
+ fi
+ ;; #(
+ *) lt_cv_sys_argz_works=yes ;;
+ esac]])
+ AS_IF([test $lt_cv_sys_argz_works = yes],
+ [AC_DEFINE([HAVE_WORKING_ARGZ], 1,
+ [This value is set to 1 to indicate that the system argz facility works])],
+ [ARGZ_H=argz.h
+ AC_LIBOBJ([argz])])])
+
+AC_SUBST([ARGZ_H])
+])
+m4trace:/usr/share/aclocal/argz.m4:79: -1- AC_DEFUN([gl_PREREQ_ARGZ], [:])
+m4trace:/usr/share/aclocal/libtool.m4:67: -1- AC_DEFUN([LT_INIT], [AC_PREREQ([2.58])dnl We use AC_INCLUDES_DEFAULT
+AC_BEFORE([$0], [LT_LANG])dnl
+AC_BEFORE([$0], [LT_OUTPUT])dnl
+AC_BEFORE([$0], [LTDL_INIT])dnl
+m4_require([_LT_CHECK_BUILDDIR])dnl
+
+dnl Autoconf doesn't catch unexpanded LT_ macros by default:
+m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl
+m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl
+dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4
+dnl unless we require an AC_DEFUNed macro:
+AC_REQUIRE([LTOPTIONS_VERSION])dnl
+AC_REQUIRE([LTSUGAR_VERSION])dnl
+AC_REQUIRE([LTVERSION_VERSION])dnl
+AC_REQUIRE([LTOBSOLETE_VERSION])dnl
+m4_require([_LT_PROG_LTMAIN])dnl
+
+dnl Parse OPTIONS
+_LT_SET_OPTIONS([$0], [$1])
+
+# This can be used to rebuild libtool when needed
+LIBTOOL_DEPS="$ltmain"
+
+# Always use our own libtool.
+LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+AC_SUBST(LIBTOOL)dnl
+
+_LT_SETUP
+
+# Only expand once:
+m4_define([LT_INIT])
+])
+m4trace:/usr/share/aclocal/libtool.m4:102: -1- AU_DEFUN([AC_PROG_LIBTOOL], [m4_if($#, 0, [LT_INIT], [LT_INIT($@)])])
+m4trace:/usr/share/aclocal/libtool.m4:102: -1- AC_DEFUN([AC_PROG_LIBTOOL], [AC_DIAGNOSE([obsolete], [The macro `AC_PROG_LIBTOOL' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_INIT], [LT_INIT($@)])])
+m4trace:/usr/share/aclocal/libtool.m4:103: -1- AU_DEFUN([AM_PROG_LIBTOOL], [m4_if($#, 0, [LT_INIT], [LT_INIT($@)])])
+m4trace:/usr/share/aclocal/libtool.m4:103: -1- AC_DEFUN([AM_PROG_LIBTOOL], [AC_DIAGNOSE([obsolete], [The macro `AM_PROG_LIBTOOL' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_INIT], [LT_INIT($@)])])
+m4trace:/usr/share/aclocal/libtool.m4:562: -1- AC_DEFUN([LT_OUTPUT], [: ${CONFIG_LT=./config.lt}
+AC_MSG_NOTICE([creating $CONFIG_LT])
+cat >"$CONFIG_LT" <<_LTEOF
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate a libtool stub with the current configuration.
+
+lt_cl_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_LTEOF
+
+cat >>"$CONFIG_LT" <<\_LTEOF
+AS_SHELL_SANITIZE
+_AS_PREPARE
+
+exec AS_MESSAGE_FD>&1
+exec AS_MESSAGE_LOG_FD>>config.log
+{
+ echo
+ AS_BOX([Running $as_me.])
+} >&AS_MESSAGE_LOG_FD
+
+lt_cl_help="\
+\`$as_me' creates a local libtool stub from the current configuration,
+for use in further configure time tests before the real libtool is
+generated.
+
+Usage: $[0] [[OPTIONS]]
+
+ -h, --help print this help, then exit
+ -V, --version print version number, then exit
+ -q, --quiet do not print progress messages
+ -d, --debug don't remove temporary files
+
+Report bugs to <bug-libtool@gnu.org>."
+
+lt_cl_version="\
+m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl
+m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION])
+configured by $[0], generated by m4_PACKAGE_STRING.
+
+Copyright (C) 2008 Free Software Foundation, Inc.
+This config.lt script is free software; the Free Software Foundation
+gives unlimited permision to copy, distribute and modify it."
+
+while test $[#] != 0
+do
+ case $[1] in
+ --version | --v* | -V )
+ echo "$lt_cl_version"; exit 0 ;;
+ --help | --h* | -h )
+ echo "$lt_cl_help"; exit 0 ;;
+ --debug | --d* | -d )
+ debug=: ;;
+ --quiet | --q* | --silent | --s* | -q )
+ lt_cl_silent=: ;;
+
+ -*) AC_MSG_ERROR([unrecognized option: $[1]
+Try \`$[0] --help' for more information.]) ;;
+
+ *) AC_MSG_ERROR([unrecognized argument: $[1]
+Try \`$[0] --help' for more information.]) ;;
+ esac
+ shift
+done
+
+if $lt_cl_silent; then
+ exec AS_MESSAGE_FD>/dev/null
+fi
+_LTEOF
+
+cat >>"$CONFIG_LT" <<_LTEOF
+_LT_OUTPUT_LIBTOOL_COMMANDS_INIT
+_LTEOF
+
+cat >>"$CONFIG_LT" <<\_LTEOF
+AC_MSG_NOTICE([creating $ofile])
+_LT_OUTPUT_LIBTOOL_COMMANDS
+AS_EXIT(0)
+_LTEOF
+chmod +x "$CONFIG_LT"
+
+# configure is writing to config.log, but config.lt does its own redirection,
+# appending to config.log, which fails on DOS, as config.log is still kept
+# open by configure. Here we exec the FD to /dev/null, effectively closing
+# config.log, so it can be properly (re)opened and appended to by config.lt.
+if test "$no_create" != yes; then
+ lt_cl_success=:
+ test "$silent" = yes &&
+ lt_config_lt_args="$lt_config_lt_args --quiet"
+ exec AS_MESSAGE_LOG_FD>/dev/null
+ $SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false
+ exec AS_MESSAGE_LOG_FD>>config.log
+ $lt_cl_success || AS_EXIT(1)
+fi
+])
+m4trace:/usr/share/aclocal/libtool.m4:756: -1- AC_DEFUN([LT_SUPPORTED_TAG], [])
+m4trace:/usr/share/aclocal/libtool.m4:767: -1- AC_DEFUN([LT_LANG], [AC_BEFORE([$0], [LT_OUTPUT])dnl
+m4_case([$1],
+ [C], [_LT_LANG(C)],
+ [C++], [_LT_LANG(CXX)],
+ [Java], [_LT_LANG(GCJ)],
+ [Fortran 77], [_LT_LANG(F77)],
+ [Fortran], [_LT_LANG(FC)],
+ [Windows Resource], [_LT_LANG(RC)],
+ [m4_ifdef([_LT_LANG_]$1[_CONFIG],
+ [_LT_LANG($1)],
+ [m4_fatal([$0: unsupported language: "$1"])])])dnl
+])
+m4trace:/usr/share/aclocal/libtool.m4:829: -1- AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)])
+m4trace:/usr/share/aclocal/libtool.m4:829: -1- AC_DEFUN([AC_LIBTOOL_CXX], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_CXX' is obsolete.
+You should run autoupdate.])dnl
+LT_LANG(C++)])
+m4trace:/usr/share/aclocal/libtool.m4:830: -1- AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)])
+m4trace:/usr/share/aclocal/libtool.m4:830: -1- AC_DEFUN([AC_LIBTOOL_F77], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_F77' is obsolete.
+You should run autoupdate.])dnl
+LT_LANG(Fortran 77)])
+m4trace:/usr/share/aclocal/libtool.m4:831: -1- AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)])
+m4trace:/usr/share/aclocal/libtool.m4:831: -1- AC_DEFUN([AC_LIBTOOL_FC], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_FC' is obsolete.
+You should run autoupdate.])dnl
+LT_LANG(Fortran)])
+m4trace:/usr/share/aclocal/libtool.m4:832: -1- AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)])
+m4trace:/usr/share/aclocal/libtool.m4:832: -1- AC_DEFUN([AC_LIBTOOL_GCJ], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_GCJ' is obsolete.
+You should run autoupdate.])dnl
+LT_LANG(Java)])
+m4trace:/usr/share/aclocal/libtool.m4:1401: -1- AC_DEFUN([_LT_COMPILER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_SED])dnl
+AC_CACHE_CHECK([$1], [$2],
+ [$2=no
+ m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4])
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+ lt_compiler_flag="$3"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ # The option is referenced via a variable to avoid confusing sed.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD)
+ (eval "$lt_compile" 2>conftest.err)
+ ac_status=$?
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+ if (exit $ac_status) && test -s "$ac_outfile"; then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings other than the usual output.
+ $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+ $2=yes
+ fi
+ fi
+ $RM conftest*
+])
+
+if test x"[$]$2" = xyes; then
+ m4_if([$5], , :, [$5])
+else
+ m4_if([$6], , :, [$6])
+fi
+])
+m4trace:/usr/share/aclocal/libtool.m4:1443: -1- AU_DEFUN([AC_LIBTOOL_COMPILER_OPTION], [m4_if($#, 0, [_LT_COMPILER_OPTION], [_LT_COMPILER_OPTION($@)])])
+m4trace:/usr/share/aclocal/libtool.m4:1443: -1- AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_COMPILER_OPTION' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [_LT_COMPILER_OPTION], [_LT_COMPILER_OPTION($@)])])
+m4trace:/usr/share/aclocal/libtool.m4:1452: -1- AC_DEFUN([_LT_LINKER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_SED])dnl
+AC_CACHE_CHECK([$1], [$2],
+ [$2=no
+ save_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS $3"
+ echo "$lt_simple_link_test_code" > conftest.$ac_ext
+ if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+ # The linker can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ if test -s conftest.err; then
+ # Append any errors to the config.log.
+ cat conftest.err 1>&AS_MESSAGE_LOG_FD
+ $ECHO "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if diff conftest.exp conftest.er2 >/dev/null; then
+ $2=yes
+ fi
+ else
+ $2=yes
+ fi
+ fi
+ $RM -r conftest*
+ LDFLAGS="$save_LDFLAGS"
+])
+
+if test x"[$]$2" = xyes; then
+ m4_if([$4], , :, [$4])
+else
+ m4_if([$5], , :, [$5])
+fi
+])
+m4trace:/usr/share/aclocal/libtool.m4:1487: -1- AU_DEFUN([AC_LIBTOOL_LINKER_OPTION], [m4_if($#, 0, [_LT_LINKER_OPTION], [_LT_LINKER_OPTION($@)])])
+m4trace:/usr/share/aclocal/libtool.m4:1487: -1- AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_LINKER_OPTION' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [_LT_LINKER_OPTION], [_LT_LINKER_OPTION($@)])])
+m4trace:/usr/share/aclocal/libtool.m4:1494: -1- AC_DEFUN([LT_CMD_MAX_LEN], [AC_REQUIRE([AC_CANONICAL_HOST])dnl
+# find the maximum length of command line arguments
+AC_MSG_CHECKING([the maximum length of command line arguments])
+AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl
+ i=0
+ teststring="ABCD"
+
+ case $build_os in
+ msdosdjgpp*)
+ # On DJGPP, this test can blow up pretty badly due to problems in libc
+ # (any single argument exceeding 2000 bytes causes a buffer overrun
+ # during glob expansion). Even if it were fixed, the result of this
+ # check would be larger than it should be.
+ lt_cv_sys_max_cmd_len=12288; # 12K is about right
+ ;;
+
+ gnu*)
+ # Under GNU Hurd, this test is not required because there is
+ # no limit to the length of command line arguments.
+ # Libtool will interpret -1 as no limit whatsoever
+ lt_cv_sys_max_cmd_len=-1;
+ ;;
+
+ cygwin* | mingw* | cegcc*)
+ # On Win9x/ME, this test blows up -- it succeeds, but takes
+ # about 5 minutes as the teststring grows exponentially.
+ # Worse, since 9x/ME are not pre-emptively multitasking,
+ # you end up with a "frozen" computer, even though with patience
+ # the test eventually succeeds (with a max line length of 256k).
+ # Instead, let's just punt: use the minimum linelength reported by
+ # all of the supported platforms: 8192 (on NT/2K/XP).
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ amigaos*)
+ # On AmigaOS with pdksh, this test takes hours, literally.
+ # So we just punt and use a minimum line length of 8192.
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ netbsd* | freebsd* | openbsd* | darwin* | dragonfly*)
+ # This has been around since 386BSD, at least. Likely further.
+ if test -x /sbin/sysctl; then
+ lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax`
+ elif test -x /usr/sbin/sysctl; then
+ lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax`
+ else
+ lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs
+ fi
+ # And add a safety zone
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+ ;;
+
+ interix*)
+ # We know the value 262144 and hardcode it with a safety zone (like BSD)
+ lt_cv_sys_max_cmd_len=196608
+ ;;
+
+ osf*)
+ # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure
+ # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not
+ # nice to cause kernel panics so lets avoid the loop below.
+ # First set a reasonable default.
+ lt_cv_sys_max_cmd_len=16384
+ #
+ if test -x /sbin/sysconfig; then
+ case `/sbin/sysconfig -q proc exec_disable_arg_limit` in
+ *1*) lt_cv_sys_max_cmd_len=-1 ;;
+ esac
+ fi
+ ;;
+ sco3.2v5*)
+ lt_cv_sys_max_cmd_len=102400
+ ;;
+ sysv5* | sco5v6* | sysv4.2uw2*)
+ kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null`
+ if test -n "$kargmax"; then
+ lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'`
+ else
+ lt_cv_sys_max_cmd_len=32768
+ fi
+ ;;
+ *)
+ lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null`
+ if test -n "$lt_cv_sys_max_cmd_len"; then
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+ else
+ # Make teststring a little bigger before we do anything with it.
+ # a 1K string should be a reasonable start.
+ for i in 1 2 3 4 5 6 7 8 ; do
+ teststring=$teststring$teststring
+ done
+ SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}}
+ # If test is not a shell built-in, we'll probably end up computing a
+ # maximum length that is only half of the actual maximum length, but
+ # we can't tell.
+ while { test "X"`$SHELL [$]0 --fallback-echo "X$teststring$teststring" 2>/dev/null` \
+ = "XX$teststring$teststring"; } >/dev/null 2>&1 &&
+ test $i != 17 # 1/2 MB should be enough
+ do
+ i=`expr $i + 1`
+ teststring=$teststring$teststring
+ done
+ # Only check the string length outside the loop.
+ lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1`
+ teststring=
+ # Add a significant safety factor because C++ compilers can tack on
+ # massive amounts of additional arguments before passing them to the
+ # linker. It appears as though 1/2 is a usable value.
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2`
+ fi
+ ;;
+ esac
+])
+if test -n $lt_cv_sys_max_cmd_len ; then
+ AC_MSG_RESULT($lt_cv_sys_max_cmd_len)
+else
+ AC_MSG_RESULT(none)
+fi
+max_cmd_len=$lt_cv_sys_max_cmd_len
+_LT_DECL([], [max_cmd_len], [0],
+ [What is the maximum length of a command?])
+])
+m4trace:/usr/share/aclocal/libtool.m4:1622: -1- AU_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], [m4_if($#, 0, [LT_CMD_MAX_LEN], [LT_CMD_MAX_LEN($@)])])
+m4trace:/usr/share/aclocal/libtool.m4:1622: -1- AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_SYS_MAX_CMD_LEN' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_CMD_MAX_LEN], [LT_CMD_MAX_LEN($@)])])
+m4trace:/usr/share/aclocal/libtool.m4:1723: -1- AC_DEFUN([LT_SYS_DLOPEN_SELF], [m4_require([_LT_HEADER_DLFCN])dnl
+if test "x$enable_dlopen" != xyes; then
+ enable_dlopen=unknown
+ enable_dlopen_self=unknown
+ enable_dlopen_self_static=unknown
+else
+ lt_cv_dlopen=no
+ lt_cv_dlopen_libs=
+
+ case $host_os in
+ beos*)
+ lt_cv_dlopen="load_add_on"
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+ ;;
+
+ mingw* | pw32* | cegcc*)
+ lt_cv_dlopen="LoadLibrary"
+ lt_cv_dlopen_libs=
+ ;;
+
+ cygwin*)
+ lt_cv_dlopen="dlopen"
+ lt_cv_dlopen_libs=
+ ;;
+
+ darwin*)
+ # if libdl is installed we need to link against it
+ AC_CHECK_LIB([dl], [dlopen],
+ [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],[
+ lt_cv_dlopen="dyld"
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+ ])
+ ;;
+
+ *)
+ AC_CHECK_FUNC([shl_load],
+ [lt_cv_dlopen="shl_load"],
+ [AC_CHECK_LIB([dld], [shl_load],
+ [lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"],
+ [AC_CHECK_FUNC([dlopen],
+ [lt_cv_dlopen="dlopen"],
+ [AC_CHECK_LIB([dl], [dlopen],
+ [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],
+ [AC_CHECK_LIB([svld], [dlopen],
+ [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"],
+ [AC_CHECK_LIB([dld], [dld_link],
+ [lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"])
+ ])
+ ])
+ ])
+ ])
+ ])
+ ;;
+ esac
+
+ if test "x$lt_cv_dlopen" != xno; then
+ enable_dlopen=yes
+ else
+ enable_dlopen=no
+ fi
+
+ case $lt_cv_dlopen in
+ dlopen)
+ save_CPPFLAGS="$CPPFLAGS"
+ test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+
+ save_LDFLAGS="$LDFLAGS"
+ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
+
+ save_LIBS="$LIBS"
+ LIBS="$lt_cv_dlopen_libs $LIBS"
+
+ AC_CACHE_CHECK([whether a program can dlopen itself],
+ lt_cv_dlopen_self, [dnl
+ _LT_TRY_DLOPEN_SELF(
+ lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes,
+ lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross)
+ ])
+
+ if test "x$lt_cv_dlopen_self" = xyes; then
+ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\"
+ AC_CACHE_CHECK([whether a statically linked program can dlopen itself],
+ lt_cv_dlopen_self_static, [dnl
+ _LT_TRY_DLOPEN_SELF(
+ lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes,
+ lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross)
+ ])
+ fi
+
+ CPPFLAGS="$save_CPPFLAGS"
+ LDFLAGS="$save_LDFLAGS"
+ LIBS="$save_LIBS"
+ ;;
+ esac
+
+ case $lt_cv_dlopen_self in
+ yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+ *) enable_dlopen_self=unknown ;;
+ esac
+
+ case $lt_cv_dlopen_self_static in
+ yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+ *) enable_dlopen_self_static=unknown ;;
+ esac
+fi
+_LT_DECL([dlopen_support], [enable_dlopen], [0],
+ [Whether dlopen is supported])
+_LT_DECL([dlopen_self], [enable_dlopen_self], [0],
+ [Whether dlopen of programs is supported])
+_LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0],
+ [Whether dlopen of statically linked programs is supported])
+])
+m4trace:/usr/share/aclocal/libtool.m4:1840: -1- AU_DEFUN([AC_LIBTOOL_DLOPEN_SELF], [m4_if($#, 0, [LT_SYS_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF($@)])])
+m4trace:/usr/share/aclocal/libtool.m4:1840: -1- AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_DLOPEN_SELF' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_SYS_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF($@)])])
+m4trace:/usr/share/aclocal/libtool.m4:2728: -1- AC_DEFUN([_LT_PATH_TOOL_PREFIX], [m4_require([_LT_DECL_EGREP])dnl
+AC_MSG_CHECKING([for $1])
+AC_CACHE_VAL(lt_cv_path_MAGIC_CMD,
+[case $MAGIC_CMD in
+[[\\/*] | ?:[\\/]*])
+ lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+ ;;
+*)
+ lt_save_MAGIC_CMD="$MAGIC_CMD"
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+dnl $ac_dummy forces splitting on constant user-supplied paths.
+dnl POSIX.2 word splitting is done only on the output of word expansions,
+dnl not every word. This closes a longstanding sh security hole.
+ ac_dummy="m4_if([$2], , $PATH, [$2])"
+ for ac_dir in $ac_dummy; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$1; then
+ lt_cv_path_MAGIC_CMD="$ac_dir/$1"
+ if test -n "$file_magic_test_file"; then
+ case $deplibs_check_method in
+ "file_magic "*)
+ file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+ MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+ if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+ $EGREP "$file_magic_regex" > /dev/null; then
+ :
+ else
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such. This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem. Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+ fi ;;
+ esac
+ fi
+ break
+ fi
+ done
+ IFS="$lt_save_ifs"
+ MAGIC_CMD="$lt_save_MAGIC_CMD"
+ ;;
+esac])
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+ AC_MSG_RESULT($MAGIC_CMD)
+else
+ AC_MSG_RESULT(no)
+fi
+_LT_DECL([], [MAGIC_CMD], [0],
+ [Used to examine libraries when file_magic_cmd begins with "file"])dnl
+])
+m4trace:/usr/share/aclocal/libtool.m4:2790: -1- AU_DEFUN([AC_PATH_TOOL_PREFIX], [m4_if($#, 0, [_LT_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX($@)])])
+m4trace:/usr/share/aclocal/libtool.m4:2790: -1- AC_DEFUN([AC_PATH_TOOL_PREFIX], [AC_DIAGNOSE([obsolete], [The macro `AC_PATH_TOOL_PREFIX' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [_LT_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX($@)])])
+m4trace:/usr/share/aclocal/libtool.m4:2813: -1- AC_DEFUN([LT_PATH_LD], [AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_DECL_EGREP])dnl
+
+AC_ARG_WITH([gnu-ld],
+ [AS_HELP_STRING([--with-gnu-ld],
+ [assume the C compiler uses GNU ld @<:@default=no@:>@])],
+ [test "$withval" = no || with_gnu_ld=yes],
+ [with_gnu_ld=no])dnl
+
+ac_prog=ld
+if test "$GCC" = yes; then
+ # Check if gcc -print-prog-name=ld gives a path.
+ AC_MSG_CHECKING([for ld used by $CC])
+ case $host in
+ *-*-mingw*)
+ # gcc leaves a trailing carriage return which upsets mingw
+ ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+ *)
+ ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+ esac
+ case $ac_prog in
+ # Accept absolute paths.
+ [[\\/]]* | ?:[[\\/]]*)
+ re_direlt='/[[^/]][[^/]]*/\.\./'
+ # Canonicalize the pathname of ld
+ ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'`
+ while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
+ ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
+ done
+ test -z "$LD" && LD="$ac_prog"
+ ;;
+ "")
+ # If it fails, then pretend we aren't using GCC.
+ ac_prog=ld
+ ;;
+ *)
+ # If it is relative, then search for the first ld in PATH.
+ with_gnu_ld=unknown
+ ;;
+ esac
+elif test "$with_gnu_ld" = yes; then
+ AC_MSG_CHECKING([for GNU ld])
+else
+ AC_MSG_CHECKING([for non-GNU ld])
+fi
+AC_CACHE_VAL(lt_cv_path_LD,
+[if test -z "$LD"; then
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+ lt_cv_path_LD="$ac_dir/$ac_prog"
+ # Check to see if the program is GNU ld. I'd rather use --version,
+ # but apparently some variants of GNU ld only accept -v.
+ # Break only if it was the GNU/non-GNU ld that we prefer.
+ case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
+ *GNU* | *'with BFD'*)
+ test "$with_gnu_ld" != no && break
+ ;;
+ *)
+ test "$with_gnu_ld" != yes && break
+ ;;
+ esac
+ fi
+ done
+ IFS="$lt_save_ifs"
+else
+ lt_cv_path_LD="$LD" # Let the user override the test with a path.
+fi])
+LD="$lt_cv_path_LD"
+if test -n "$LD"; then
+ AC_MSG_RESULT($LD)
+else
+ AC_MSG_RESULT(no)
+fi
+test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH])
+_LT_PATH_LD_GNU
+AC_SUBST([LD])
+
+_LT_TAGDECL([], [LD], [1], [The linker used to build libraries])
+])
+m4trace:/usr/share/aclocal/libtool.m4:2901: -1- AU_DEFUN([AM_PROG_LD], [m4_if($#, 0, [LT_PATH_LD], [LT_PATH_LD($@)])])
+m4trace:/usr/share/aclocal/libtool.m4:2901: -1- AC_DEFUN([AM_PROG_LD], [AC_DIAGNOSE([obsolete], [The macro `AM_PROG_LD' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_PATH_LD], [LT_PATH_LD($@)])])
+m4trace:/usr/share/aclocal/libtool.m4:2902: -1- AU_DEFUN([AC_PROG_LD], [m4_if($#, 0, [LT_PATH_LD], [LT_PATH_LD($@)])])
+m4trace:/usr/share/aclocal/libtool.m4:2902: -1- AC_DEFUN([AC_PROG_LD], [AC_DIAGNOSE([obsolete], [The macro `AC_PROG_LD' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_PATH_LD], [LT_PATH_LD($@)])])
+m4trace:/usr/share/aclocal/libtool.m4:3166: -1- AC_DEFUN([LT_PATH_NM], [AC_REQUIRE([AC_PROG_CC])dnl
+AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM,
+[if test -n "$NM"; then
+ # Let the user override the test.
+ lt_cv_path_NM="$NM"
+else
+ lt_nm_to_check="${ac_tool_prefix}nm"
+ if test -n "$ac_tool_prefix" && test "$build" = "$host"; then
+ lt_nm_to_check="$lt_nm_to_check nm"
+ fi
+ for lt_tmp_nm in $lt_nm_to_check; do
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ tmp_nm="$ac_dir/$lt_tmp_nm"
+ if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then
+ # Check to see if the nm accepts a BSD-compat flag.
+ # Adding the `sed 1q' prevents false positives on HP-UX, which says:
+ # nm: unknown option "B" ignored
+ # Tru64's nm complains that /dev/null is an invalid object file
+ case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in
+ */dev/null* | *'Invalid file or object type'*)
+ lt_cv_path_NM="$tmp_nm -B"
+ break
+ ;;
+ *)
+ case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in
+ */dev/null*)
+ lt_cv_path_NM="$tmp_nm -p"
+ break
+ ;;
+ *)
+ lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
+ continue # so that we can try to find one that supports BSD flags
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ done
+ IFS="$lt_save_ifs"
+ done
+ : ${lt_cv_path_NM=no}
+fi])
+if test "$lt_cv_path_NM" != "no"; then
+ NM="$lt_cv_path_NM"
+else
+ # Didn't find any BSD compatible name lister, look for dumpbin.
+ AC_CHECK_TOOLS(DUMPBIN, ["dumpbin -symbols" "link -dump -symbols"], :)
+ AC_SUBST([DUMPBIN])
+ if test "$DUMPBIN" != ":"; then
+ NM="$DUMPBIN"
+ fi
+fi
+test -z "$NM" && NM=nm
+AC_SUBST([NM])
+_LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl
+
+AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface],
+ [lt_cv_nm_interface="BSD nm"
+ echo "int some_variable = 0;" > conftest.$ac_ext
+ (eval echo "\"\$as_me:__oline__: $ac_compile\"" >&AS_MESSAGE_LOG_FD)
+ (eval "$ac_compile" 2>conftest.err)
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ (eval echo "\"\$as_me:__oline__: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD)
+ (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ (eval echo "\"\$as_me:__oline__: output\"" >&AS_MESSAGE_LOG_FD)
+ cat conftest.out >&AS_MESSAGE_LOG_FD
+ if $GREP 'External.*some_variable' conftest.out > /dev/null; then
+ lt_cv_nm_interface="MS dumpbin"
+ fi
+ rm -f conftest*])
+])
+m4trace:/usr/share/aclocal/libtool.m4:3244: -1- AU_DEFUN([AM_PROG_NM], [m4_if($#, 0, [LT_PATH_NM], [LT_PATH_NM($@)])])
+m4trace:/usr/share/aclocal/libtool.m4:3244: -1- AC_DEFUN([AM_PROG_NM], [AC_DIAGNOSE([obsolete], [The macro `AM_PROG_NM' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_PATH_NM], [LT_PATH_NM($@)])])
+m4trace:/usr/share/aclocal/libtool.m4:3245: -1- AU_DEFUN([AC_PROG_NM], [m4_if($#, 0, [LT_PATH_NM], [LT_PATH_NM($@)])])
+m4trace:/usr/share/aclocal/libtool.m4:3245: -1- AC_DEFUN([AC_PROG_NM], [AC_DIAGNOSE([obsolete], [The macro `AC_PROG_NM' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_PATH_NM], [LT_PATH_NM($@)])])
+m4trace:/usr/share/aclocal/libtool.m4:3254: -1- AC_DEFUN([LT_LIB_M], [AC_REQUIRE([AC_CANONICAL_HOST])dnl
+LIBM=
+case $host in
+*-*-beos* | *-*-cygwin* | *-*-pw32* | *-*-darwin*)
+ # These system don't have libm, or don't need it
+ ;;
+*-ncr-sysv4.3*)
+ AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw")
+ AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm")
+ ;;
+*)
+ AC_CHECK_LIB(m, cos, LIBM="-lm")
+ ;;
+esac
+AC_SUBST([LIBM])
+])
+m4trace:/usr/share/aclocal/libtool.m4:3273: -1- AU_DEFUN([AC_CHECK_LIBM], [m4_if($#, 0, [LT_LIB_M], [LT_LIB_M($@)])])
+m4trace:/usr/share/aclocal/libtool.m4:3273: -1- AC_DEFUN([AC_CHECK_LIBM], [AC_DIAGNOSE([obsolete], [The macro `AC_CHECK_LIBM' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_LIB_M], [LT_LIB_M($@)])])
+m4trace:/usr/share/aclocal/libtool.m4:6975: -1- AC_DEFUN([LT_PROG_GCJ], [m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ],
+ [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ],
+ [AC_CHECK_TOOL(GCJ, gcj,)
+ test "x${GCJFLAGS+set}" = xset || GCJFLAGS="-g -O2"
+ AC_SUBST(GCJFLAGS)])])[]dnl
+])
+m4trace:/usr/share/aclocal/libtool.m4:6984: -1- AU_DEFUN([LT_AC_PROG_GCJ], [m4_if($#, 0, [LT_PROG_GCJ], [LT_PROG_GCJ($@)])])
+m4trace:/usr/share/aclocal/libtool.m4:6984: -1- AC_DEFUN([LT_AC_PROG_GCJ], [AC_DIAGNOSE([obsolete], [The macro `LT_AC_PROG_GCJ' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_PROG_GCJ], [LT_PROG_GCJ($@)])])
+m4trace:/usr/share/aclocal/libtool.m4:6991: -1- AC_DEFUN([LT_PROG_RC], [AC_CHECK_TOOL(RC, windres,)
+])
+m4trace:/usr/share/aclocal/libtool.m4:6996: -1- AU_DEFUN([LT_AC_PROG_RC], [m4_if($#, 0, [LT_PROG_RC], [LT_PROG_RC($@)])])
+m4trace:/usr/share/aclocal/libtool.m4:6996: -1- AC_DEFUN([LT_AC_PROG_RC], [AC_DIAGNOSE([obsolete], [The macro `LT_AC_PROG_RC' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_PROG_RC], [LT_PROG_RC($@)])])
+m4trace:/usr/share/aclocal/libtool.m4:7107: -1- AU_DEFUN([LT_AC_PROG_SED], [m4_if($#, 0, [AC_PROG_SED], [AC_PROG_SED($@)])])
+m4trace:/usr/share/aclocal/libtool.m4:7107: -1- AC_DEFUN([LT_AC_PROG_SED], [AC_DIAGNOSE([obsolete], [The macro `LT_AC_PROG_SED' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [AC_PROG_SED], [AC_PROG_SED($@)])])
+m4trace:/usr/share/aclocal/ltdl.m4:16: -1- AC_DEFUN([LT_CONFIG_LTDL_DIR], [AC_BEFORE([$0], [LTDL_INIT])
+_$0($*)
+])
+m4trace:/usr/share/aclocal/ltdl.m4:68: -1- AC_DEFUN([LTDL_CONVENIENCE], [AC_BEFORE([$0], [LTDL_INIT])dnl
+dnl Although the argument is deprecated and no longer documented,
+dnl LTDL_CONVENIENCE used to take a DIRECTORY orgument, if we have one
+dnl here make sure it is the same as any other declaration of libltdl's
+dnl location! This also ensures lt_ltdl_dir is set when configure.ac is
+dnl not yet using an explicit LT_CONFIG_LTDL_DIR.
+m4_ifval([$1], [_LT_CONFIG_LTDL_DIR([$1])])dnl
+_$0()
+])
+m4trace:/usr/share/aclocal/ltdl.m4:81: -1- AU_DEFUN([AC_LIBLTDL_CONVENIENCE], [_LT_CONFIG_LTDL_DIR([m4_default([$1], [libltdl])])
+_LTDL_CONVENIENCE])
+m4trace:/usr/share/aclocal/ltdl.m4:81: -1- AC_DEFUN([AC_LIBLTDL_CONVENIENCE], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBLTDL_CONVENIENCE' is obsolete.
+You should run autoupdate.])dnl
+_LT_CONFIG_LTDL_DIR([m4_default([$1], [libltdl])])
+_LTDL_CONVENIENCE])
+m4trace:/usr/share/aclocal/ltdl.m4:124: -1- AC_DEFUN([LTDL_INSTALLABLE], [AC_BEFORE([$0], [LTDL_INIT])dnl
+dnl Although the argument is deprecated and no longer documented,
+dnl LTDL_INSTALLABLE used to take a DIRECTORY orgument, if we have one
+dnl here make sure it is the same as any other declaration of libltdl's
+dnl location! This also ensures lt_ltdl_dir is set when configure.ac is
+dnl not yet using an explicit LT_CONFIG_LTDL_DIR.
+m4_ifval([$1], [_LT_CONFIG_LTDL_DIR([$1])])dnl
+_$0()
+])
+m4trace:/usr/share/aclocal/ltdl.m4:137: -1- AU_DEFUN([AC_LIBLTDL_INSTALLABLE], [_LT_CONFIG_LTDL_DIR([m4_default([$1], [libltdl])])
+_LTDL_INSTALLABLE])
+m4trace:/usr/share/aclocal/ltdl.m4:137: -1- AC_DEFUN([AC_LIBLTDL_INSTALLABLE], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBLTDL_INSTALLABLE' is obsolete.
+You should run autoupdate.])dnl
+_LT_CONFIG_LTDL_DIR([m4_default([$1], [libltdl])])
+_LTDL_INSTALLABLE])
+m4trace:/usr/share/aclocal/ltdl.m4:213: -1- AC_DEFUN([_LT_LIBOBJ], [
+ m4_pattern_allow([^_LT_LIBOBJS$])
+ _LT_LIBOBJS="$_LT_LIBOBJS $1.$ac_objext"
+])
+m4trace:/usr/share/aclocal/ltdl.m4:226: -1- AC_DEFUN([LTDL_INIT], [dnl Parse OPTIONS
+_LT_SET_OPTIONS([$0], [$1])
+
+dnl We need to keep our own list of libobjs separate from our parent project,
+dnl and the easiest way to do that is redefine the AC_LIBOBJs macro while
+dnl we look for our own LIBOBJs.
+m4_pushdef([AC_LIBOBJ], m4_defn([_LT_LIBOBJ]))
+m4_pushdef([AC_LIBSOURCES])
+
+dnl If not otherwise defined, default to the 1.5.x compatible subproject mode:
+m4_if(_LTDL_MODE, [],
+ [m4_define([_LTDL_MODE], m4_default([$2], [subproject]))
+ m4_if([-1], [m4_bregexp(_LTDL_MODE, [\(subproject\|\(non\)?recursive\)])],
+ [m4_fatal([unknown libltdl mode: ]_LTDL_MODE)])])
+
+AC_ARG_WITH([included_ltdl],
+ [AS_HELP_STRING([--with-included-ltdl],
+ [use the GNU ltdl sources included here])])
+
+if test "x$with_included_ltdl" != xyes; then
+ # We are not being forced to use the included libltdl sources, so
+ # decide whether there is a useful installed version we can use.
+ AC_CHECK_HEADER([ltdl.h],
+ [AC_CHECK_DECL([lt_dlinterface_register],
+ [AC_CHECK_LIB([ltdl], [lt_dladvise_preload],
+ [with_included_ltdl=no],
+ [with_included_ltdl=yes])],
+ [with_included_ltdl=yes],
+ [AC_INCLUDES_DEFAULT
+ #include <ltdl.h>])],
+ [with_included_ltdl=yes],
+ [AC_INCLUDES_DEFAULT]
+ )
+fi
+
+dnl If neither LT_CONFIG_LTDL_DIR, LTDL_CONVENIENCE nor LTDL_INSTALLABLE
+dnl was called yet, then for old times' sake, we assume libltdl is in an
+dnl eponymous directory:
+AC_PROVIDE_IFELSE([LT_CONFIG_LTDL_DIR], [], [_LT_CONFIG_LTDL_DIR([libltdl])])
+
+AC_ARG_WITH([ltdl_include],
+ [AS_HELP_STRING([--with-ltdl-include=DIR],
+ [use the ltdl headers installed in DIR])])
+
+if test -n "$with_ltdl_include"; then
+ if test -f "$with_ltdl_include/ltdl.h"; then :
+ else
+ AC_MSG_ERROR([invalid ltdl include directory: `$with_ltdl_include'])
+ fi
+else
+ with_ltdl_include=no
+fi
+
+AC_ARG_WITH([ltdl_lib],
+ [AS_HELP_STRING([--with-ltdl-lib=DIR],
+ [use the libltdl.la installed in DIR])])
+
+if test -n "$with_ltdl_lib"; then
+ if test -f "$with_ltdl_lib/libltdl.la"; then :
+ else
+ AC_MSG_ERROR([invalid ltdl library directory: `$with_ltdl_lib'])
+ fi
+else
+ with_ltdl_lib=no
+fi
+
+case ,$with_included_ltdl,$with_ltdl_include,$with_ltdl_lib, in
+ ,yes,no,no,)
+ m4_case(m4_default(_LTDL_TYPE, [convenience]),
+ [convenience], [_LTDL_CONVENIENCE],
+ [installable], [_LTDL_INSTALLABLE],
+ [m4_fatal([unknown libltdl build type: ]_LTDL_TYPE)])
+ ;;
+ ,no,no,no,)
+ # If the included ltdl is not to be used, then use the
+ # preinstalled libltdl we found.
+ AC_DEFINE([HAVE_LTDL], [1],
+ [Define this if a modern libltdl is already installed])
+ LIBLTDL=-lltdl
+ LTDLDEPS=
+ LTDLINCL=
+ ;;
+ ,no*,no,*)
+ AC_MSG_ERROR([`--with-ltdl-include' and `--with-ltdl-lib' options must be used together])
+ ;;
+ *) with_included_ltdl=no
+ LIBLTDL="-L$with_ltdl_lib -lltdl"
+ LTDLDEPS=
+ LTDLINCL="-I$with_ltdl_include"
+ ;;
+esac
+INCLTDL="$LTDLINCL"
+
+# Report our decision...
+AC_MSG_CHECKING([where to find libltdl headers])
+AC_MSG_RESULT([$LTDLINCL])
+AC_MSG_CHECKING([where to find libltdl library])
+AC_MSG_RESULT([$LIBLTDL])
+
+_LTDL_SETUP
+
+dnl restore autoconf definition.
+m4_popdef([AC_LIBOBJ])
+m4_popdef([AC_LIBSOURCES])
+
+AC_CONFIG_COMMANDS_PRE([
+ _ltdl_libobjs=
+ _ltdl_ltlibobjs=
+ if test -n "$_LT_LIBOBJS"; then
+ # Remove the extension.
+ _lt_sed_drop_objext='s/\.o$//;s/\.obj$//'
+ for i in `for i in $_LT_LIBOBJS; do echo "$i"; done | sed "$_lt_sed_drop_objext" | sort -u`; do
+ _ltdl_libobjs="$_ltdl_libobjs $lt_libobj_prefix$i.$ac_objext"
+ _ltdl_ltlibobjs="$_ltdl_ltlibobjs $lt_libobj_prefix$i.lo"
+ done
+ fi
+ AC_SUBST([ltdl_LIBOBJS], [$_ltdl_libobjs])
+ AC_SUBST([ltdl_LTLIBOBJS], [$_ltdl_ltlibobjs])
+])
+
+# Only expand once:
+m4_define([LTDL_INIT])
+])
+m4trace:/usr/share/aclocal/ltdl.m4:352: -1- AU_DEFUN([AC_LIB_LTDL], [LTDL_INIT($@)])
+m4trace:/usr/share/aclocal/ltdl.m4:352: -1- AC_DEFUN([AC_LIB_LTDL], [AC_DIAGNOSE([obsolete], [The macro `AC_LIB_LTDL' is obsolete.
+You should run autoupdate.])dnl
+LTDL_INIT($@)])
+m4trace:/usr/share/aclocal/ltdl.m4:353: -1- AU_DEFUN([AC_WITH_LTDL], [LTDL_INIT($@)])
+m4trace:/usr/share/aclocal/ltdl.m4:353: -1- AC_DEFUN([AC_WITH_LTDL], [AC_DIAGNOSE([obsolete], [The macro `AC_WITH_LTDL' is obsolete.
+You should run autoupdate.])dnl
+LTDL_INIT($@)])
+m4trace:/usr/share/aclocal/ltdl.m4:354: -1- AU_DEFUN([LT_WITH_LTDL], [LTDL_INIT($@)])
+m4trace:/usr/share/aclocal/ltdl.m4:354: -1- AC_DEFUN([LT_WITH_LTDL], [AC_DIAGNOSE([obsolete], [The macro `LT_WITH_LTDL' is obsolete.
+You should run autoupdate.])dnl
+LTDL_INIT($@)])
+m4trace:/usr/share/aclocal/ltdl.m4:367: -1- AC_DEFUN([_LTDL_SETUP], [AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([LT_SYS_MODULE_EXT])dnl
+AC_REQUIRE([LT_SYS_MODULE_PATH])dnl
+AC_REQUIRE([LT_SYS_DLSEARCH_PATH])dnl
+AC_REQUIRE([LT_LIB_DLLOAD])dnl
+AC_REQUIRE([LT_SYS_SYMBOL_USCORE])dnl
+AC_REQUIRE([LT_FUNC_DLSYM_USCORE])dnl
+AC_REQUIRE([LT_SYS_DLOPEN_DEPLIBS])dnl
+AC_REQUIRE([gl_FUNC_ARGZ])dnl
+
+m4_require([_LT_CHECK_OBJDIR])dnl
+m4_require([_LT_HEADER_DLFCN])dnl
+m4_require([_LT_CHECK_DLPREOPEN])dnl
+m4_require([_LT_DECL_SED])dnl
+
+dnl Don't require this, or it will be expanded earlier than the code
+dnl that sets the variables it relies on:
+_LT_ENABLE_INSTALL
+
+dnl _LTDL_MODE specific code must be called at least once:
+_LTDL_MODE_DISPATCH
+
+# In order that ltdl.c can compile, find out the first AC_CONFIG_HEADERS
+# the user used. This is so that ltdl.h can pick up the parent projects
+# config.h file, The first file in AC_CONFIG_HEADERS must contain the
+# definitions required by ltdl.c.
+# FIXME: Remove use of undocumented AC_LIST_HEADERS (2.59 compatibility).
+AC_CONFIG_COMMANDS_PRE([dnl
+m4_pattern_allow([^LT_CONFIG_H$])dnl
+m4_ifset([AH_HEADER],
+ [LT_CONFIG_H=AH_HEADER],
+ [m4_ifset([AC_LIST_HEADERS],
+ [LT_CONFIG_H=`echo "AC_LIST_HEADERS" | $SED 's,^[[ ]]*,,;s,[[ :]].*$,,'`],
+ [])])])
+AC_SUBST([LT_CONFIG_H])
+
+AC_CHECK_HEADERS([unistd.h dl.h sys/dl.h dld.h mach-o/dyld.h dirent.h],
+ [], [], [AC_INCLUDES_DEFAULT])
+
+AC_CHECK_FUNCS([closedir opendir readdir], [], [AC_LIBOBJ([lt__dirent])])
+AC_CHECK_FUNCS([strlcat strlcpy], [], [AC_LIBOBJ([lt__strl])])
+
+AC_DEFINE_UNQUOTED([LT_LIBEXT],["$libext"],[The archive extension])
+
+name=ltdl
+LTDLOPEN=`eval "\\$ECHO \"$libname_spec\""`
+AC_SUBST([LTDLOPEN])
+])
+m4trace:/usr/share/aclocal/ltdl.m4:437: -1- AC_DEFUN([LT_SYS_DLOPEN_DEPLIBS], [AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_CACHE_CHECK([whether deplibs are loaded by dlopen],
+ [lt_cv_sys_dlopen_deplibs],
+ [# PORTME does your system automatically load deplibs for dlopen?
+ # or its logical equivalent (e.g. shl_load for HP-UX < 11)
+ # For now, we just catch OSes we know something about -- in the
+ # future, we'll try test this programmatically.
+ lt_cv_sys_dlopen_deplibs=unknown
+ case $host_os in
+ aix3*|aix4.1.*|aix4.2.*)
+ # Unknown whether this is true for these versions of AIX, but
+ # we want this `case' here to explicitly catch those versions.
+ lt_cv_sys_dlopen_deplibs=unknown
+ ;;
+ aix[[4-9]]*)
+ lt_cv_sys_dlopen_deplibs=yes
+ ;;
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ lt_cv_sys_dlopen_deplibs=no
+ ;;
+ esac
+ ;;
+ darwin*)
+ # Assuming the user has installed a libdl from somewhere, this is true
+ # If you are looking for one http://www.opendarwin.org/projects/dlcompat
+ lt_cv_sys_dlopen_deplibs=yes
+ ;;
+ freebsd* | dragonfly*)
+ lt_cv_sys_dlopen_deplibs=yes
+ ;;
+ gnu* | linux* | k*bsd*-gnu)
+ # GNU and its variants, using gnu ld.so (Glibc)
+ lt_cv_sys_dlopen_deplibs=yes
+ ;;
+ hpux10*|hpux11*)
+ lt_cv_sys_dlopen_deplibs=yes
+ ;;
+ interix*)
+ lt_cv_sys_dlopen_deplibs=yes
+ ;;
+ irix[[12345]]*|irix6.[[01]]*)
+ # Catch all versions of IRIX before 6.2, and indicate that we don't
+ # know how it worked for any of those versions.
+ lt_cv_sys_dlopen_deplibs=unknown
+ ;;
+ irix*)
+ # The case above catches anything before 6.2, and it's known that
+ # at 6.2 and later dlopen does load deplibs.
+ lt_cv_sys_dlopen_deplibs=yes
+ ;;
+ netbsd*)
+ lt_cv_sys_dlopen_deplibs=yes
+ ;;
+ openbsd*)
+ lt_cv_sys_dlopen_deplibs=yes
+ ;;
+ osf[[1234]]*)
+ # dlopen did load deplibs (at least at 4.x), but until the 5.x series,
+ # it did *not* use an RPATH in a shared library to find objects the
+ # library depends on, so we explicitly say `no'.
+ lt_cv_sys_dlopen_deplibs=no
+ ;;
+ osf5.0|osf5.0a|osf5.1)
+ # dlopen *does* load deplibs and with the right loader patch applied
+ # it even uses RPATH in a shared library to search for shared objects
+ # that the library depends on, but there's no easy way to know if that
+ # patch is installed. Since this is the case, all we can really
+ # say is unknown -- it depends on the patch being installed. If
+ # it is, this changes to `yes'. Without it, it would be `no'.
+ lt_cv_sys_dlopen_deplibs=unknown
+ ;;
+ osf*)
+ # the two cases above should catch all versions of osf <= 5.1. Read
+ # the comments above for what we know about them.
+ # At > 5.1, deplibs are loaded *and* any RPATH in a shared library
+ # is used to find them so we can finally say `yes'.
+ lt_cv_sys_dlopen_deplibs=yes
+ ;;
+ qnx*)
+ lt_cv_sys_dlopen_deplibs=yes
+ ;;
+ solaris*)
+ lt_cv_sys_dlopen_deplibs=yes
+ ;;
+ sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+ libltdl_cv_sys_dlopen_deplibs=yes
+ ;;
+ esac
+ ])
+if test "$lt_cv_sys_dlopen_deplibs" != yes; then
+ AC_DEFINE([LTDL_DLOPEN_DEPLIBS], [1],
+ [Define if the OS needs help to load dependent libraries for dlopen().])
+fi
+])
+m4trace:/usr/share/aclocal/ltdl.m4:536: -1- AU_DEFUN([AC_LTDL_SYS_DLOPEN_DEPLIBS], [m4_if($#, 0, [LT_SYS_DLOPEN_DEPLIBS], [LT_SYS_DLOPEN_DEPLIBS($@)])])
+m4trace:/usr/share/aclocal/ltdl.m4:536: -1- AC_DEFUN([AC_LTDL_SYS_DLOPEN_DEPLIBS], [AC_DIAGNOSE([obsolete], [The macro `AC_LTDL_SYS_DLOPEN_DEPLIBS' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_SYS_DLOPEN_DEPLIBS], [LT_SYS_DLOPEN_DEPLIBS($@)])])
+m4trace:/usr/share/aclocal/ltdl.m4:543: -1- AC_DEFUN([LT_SYS_MODULE_EXT], [m4_require([_LT_SYS_DYNAMIC_LINKER])dnl
+AC_CACHE_CHECK([which extension is used for runtime loadable modules],
+ [libltdl_cv_shlibext],
+[
+module=yes
+eval libltdl_cv_shlibext=$shrext_cmds
+ ])
+if test -n "$libltdl_cv_shlibext"; then
+ m4_pattern_allow([LT_MODULE_EXT])dnl
+ AC_DEFINE_UNQUOTED([LT_MODULE_EXT], ["$libltdl_cv_shlibext"],
+ [Define to the extension used for runtime loadable modules, say, ".so".])
+fi
+])
+m4trace:/usr/share/aclocal/ltdl.m4:559: -1- AU_DEFUN([AC_LTDL_SHLIBEXT], [m4_if($#, 0, [LT_SYS_MODULE_EXT], [LT_SYS_MODULE_EXT($@)])])
+m4trace:/usr/share/aclocal/ltdl.m4:559: -1- AC_DEFUN([AC_LTDL_SHLIBEXT], [AC_DIAGNOSE([obsolete], [The macro `AC_LTDL_SHLIBEXT' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_SYS_MODULE_EXT], [LT_SYS_MODULE_EXT($@)])])
+m4trace:/usr/share/aclocal/ltdl.m4:566: -1- AC_DEFUN([LT_SYS_MODULE_PATH], [m4_require([_LT_SYS_DYNAMIC_LINKER])dnl
+AC_CACHE_CHECK([which variable specifies run-time module search path],
+ [lt_cv_module_path_var], [lt_cv_module_path_var="$shlibpath_var"])
+if test -n "$lt_cv_module_path_var"; then
+ m4_pattern_allow([LT_MODULE_PATH_VAR])dnl
+ AC_DEFINE_UNQUOTED([LT_MODULE_PATH_VAR], ["$lt_cv_module_path_var"],
+ [Define to the name of the environment variable that determines the run-time module search path.])
+fi
+])
+m4trace:/usr/share/aclocal/ltdl.m4:578: -1- AU_DEFUN([AC_LTDL_SHLIBPATH], [m4_if($#, 0, [LT_SYS_MODULE_PATH], [LT_SYS_MODULE_PATH($@)])])
+m4trace:/usr/share/aclocal/ltdl.m4:578: -1- AC_DEFUN([AC_LTDL_SHLIBPATH], [AC_DIAGNOSE([obsolete], [The macro `AC_LTDL_SHLIBPATH' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_SYS_MODULE_PATH], [LT_SYS_MODULE_PATH($@)])])
+m4trace:/usr/share/aclocal/ltdl.m4:585: -1- AC_DEFUN([LT_SYS_DLSEARCH_PATH], [m4_require([_LT_SYS_DYNAMIC_LINKER])dnl
+AC_CACHE_CHECK([for the default library search path],
+ [lt_cv_sys_dlsearch_path],
+ [lt_cv_sys_dlsearch_path="$sys_lib_dlsearch_path_spec"])
+if test -n "$lt_cv_sys_dlsearch_path"; then
+ sys_dlsearch_path=
+ for dir in $lt_cv_sys_dlsearch_path; do
+ if test -z "$sys_dlsearch_path"; then
+ sys_dlsearch_path="$dir"
+ else
+ sys_dlsearch_path="$sys_dlsearch_path$PATH_SEPARATOR$dir"
+ fi
+ done
+ m4_pattern_allow([LT_DLSEARCH_PATH])dnl
+ AC_DEFINE_UNQUOTED([LT_DLSEARCH_PATH], ["$sys_dlsearch_path"],
+ [Define to the system default library search path.])
+fi
+])
+m4trace:/usr/share/aclocal/ltdl.m4:606: -1- AU_DEFUN([AC_LTDL_SYSSEARCHPATH], [m4_if($#, 0, [LT_SYS_DLSEARCH_PATH], [LT_SYS_DLSEARCH_PATH($@)])])
+m4trace:/usr/share/aclocal/ltdl.m4:606: -1- AC_DEFUN([AC_LTDL_SYSSEARCHPATH], [AC_DIAGNOSE([obsolete], [The macro `AC_LTDL_SYSSEARCHPATH' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_SYS_DLSEARCH_PATH], [LT_SYS_DLSEARCH_PATH($@)])])
+m4trace:/usr/share/aclocal/ltdl.m4:632: -1- AC_DEFUN([LT_LIB_DLLOAD], [m4_pattern_allow([^LT_DLLOADERS$])
+LT_DLLOADERS=
+AC_SUBST([LT_DLLOADERS])
+
+AC_LANG_PUSH([C])
+
+LIBADD_DLOPEN=
+AC_SEARCH_LIBS([dlopen], [dl],
+ [AC_DEFINE([HAVE_LIBDL], [1],
+ [Define if you have the libdl library or equivalent.])
+ if test "$ac_cv_search_dlopen" != "none required" ; then
+ LIBADD_DLOPEN="-ldl"
+ fi
+ libltdl_cv_lib_dl_dlopen="yes"
+ LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}dlopen.la"],
+ [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#if HAVE_DLFCN_H
+# include <dlfcn.h>
+#endif
+ ]], [[dlopen(0, 0);]])],
+ [AC_DEFINE([HAVE_LIBDL], [1],
+ [Define if you have the libdl library or equivalent.])
+ libltdl_cv_func_dlopen="yes"
+ LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}dlopen.la"],
+ [AC_CHECK_LIB([svld], [dlopen],
+ [AC_DEFINE([HAVE_LIBDL], [1],
+ [Define if you have the libdl library or equivalent.])
+ LIBADD_DLOPEN="-lsvld" libltdl_cv_func_dlopen="yes"
+ LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}dlopen.la"])])])
+if test x"$libltdl_cv_func_dlopen" = xyes || test x"$libltdl_cv_lib_dl_dlopen" = xyes
+then
+ lt_save_LIBS="$LIBS"
+ LIBS="$LIBS $LIBADD_DLOPEN"
+ AC_CHECK_FUNCS([dlerror])
+ LIBS="$lt_save_LIBS"
+fi
+AC_SUBST([LIBADD_DLOPEN])
+
+LIBADD_SHL_LOAD=
+AC_CHECK_FUNC([shl_load],
+ [AC_DEFINE([HAVE_SHL_LOAD], [1],
+ [Define if you have the shl_load function.])
+ LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}shl_load.la"],
+ [AC_CHECK_LIB([dld], [shl_load],
+ [AC_DEFINE([HAVE_SHL_LOAD], [1],
+ [Define if you have the shl_load function.])
+ LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}shl_load.la"
+ LIBADD_SHL_LOAD="-ldld"])])
+AC_SUBST([LIBADD_SHL_LOAD])
+
+case $host_os in
+darwin[[1567]].*)
+# We only want this for pre-Mac OS X 10.4.
+ AC_CHECK_FUNC([_dyld_func_lookup],
+ [AC_DEFINE([HAVE_DYLD], [1],
+ [Define if you have the _dyld_func_lookup function.])
+ LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}dyld.la"])
+ ;;
+beos*)
+ LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}load_add_on.la"
+ ;;
+cygwin* | mingw* | os2* | pw32*)
+ AC_CHECK_DECLS([cygwin_conv_path], [], [], [[#include <sys/cygwin.h>]])
+ LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}loadlibrary.la"
+ ;;
+esac
+
+AC_CHECK_LIB([dld], [dld_link],
+ [AC_DEFINE([HAVE_DLD], [1],
+ [Define if you have the GNU dld library.])
+ LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}dld_link.la"])
+AC_SUBST([LIBADD_DLD_LINK])
+
+m4_pattern_allow([^LT_DLPREOPEN$])
+LT_DLPREOPEN=
+if test -n "$LT_DLLOADERS"
+then
+ for lt_loader in $LT_DLLOADERS; do
+ LT_DLPREOPEN="$LT_DLPREOPEN-dlpreopen $lt_loader "
+ done
+ AC_DEFINE([HAVE_LIBDLLOADER], [1],
+ [Define if libdlloader will be built on this platform])
+fi
+AC_SUBST([LT_DLPREOPEN])
+
+dnl This isn't used anymore, but set it for backwards compatibility
+LIBADD_DL="$LIBADD_DLOPEN $LIBADD_SHL_LOAD"
+AC_SUBST([LIBADD_DL])
+
+AC_LANG_POP
+])
+m4trace:/usr/share/aclocal/ltdl.m4:725: -1- AU_DEFUN([AC_LTDL_DLLIB], [m4_if($#, 0, [LT_LIB_DLLOAD], [LT_LIB_DLLOAD($@)])])
+m4trace:/usr/share/aclocal/ltdl.m4:725: -1- AC_DEFUN([AC_LTDL_DLLIB], [AC_DIAGNOSE([obsolete], [The macro `AC_LTDL_DLLIB' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_LIB_DLLOAD], [LT_LIB_DLLOAD($@)])])
+m4trace:/usr/share/aclocal/ltdl.m4:733: -1- AC_DEFUN([LT_SYS_SYMBOL_USCORE], [m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl
+AC_CACHE_CHECK([for _ prefix in compiled symbols],
+ [lt_cv_sys_symbol_underscore],
+ [lt_cv_sys_symbol_underscore=no
+ cat > conftest.$ac_ext <<_LT_EOF
+void nm_test_func(){}
+int main(){nm_test_func;return 0;}
+_LT_EOF
+ if AC_TRY_EVAL(ac_compile); then
+ # Now try to grab the symbols.
+ ac_nlist=conftest.nm
+ if AC_TRY_EVAL(NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $ac_nlist) && test -s "$ac_nlist"; then
+ # See whether the symbols have a leading underscore.
+ if grep '^. _nm_test_func' "$ac_nlist" >/dev/null; then
+ lt_cv_sys_symbol_underscore=yes
+ else
+ if grep '^. nm_test_func ' "$ac_nlist" >/dev/null; then
+ :
+ else
+ echo "configure: cannot find nm_test_func in $ac_nlist" >&AS_MESSAGE_LOG_FD
+ fi
+ fi
+ else
+ echo "configure: cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD
+ fi
+ else
+ echo "configure: failed program was:" >&AS_MESSAGE_LOG_FD
+ cat conftest.c >&AS_MESSAGE_LOG_FD
+ fi
+ rm -rf conftest*
+ ])
+ sys_symbol_underscore=$lt_cv_sys_symbol_underscore
+ AC_SUBST([sys_symbol_underscore])
+])
+m4trace:/usr/share/aclocal/ltdl.m4:770: -1- AU_DEFUN([AC_LTDL_SYMBOL_USCORE], [m4_if($#, 0, [LT_SYS_SYMBOL_USCORE], [LT_SYS_SYMBOL_USCORE($@)])])
+m4trace:/usr/share/aclocal/ltdl.m4:770: -1- AC_DEFUN([AC_LTDL_SYMBOL_USCORE], [AC_DIAGNOSE([obsolete], [The macro `AC_LTDL_SYMBOL_USCORE' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_SYS_SYMBOL_USCORE], [LT_SYS_SYMBOL_USCORE($@)])])
+m4trace:/usr/share/aclocal/ltdl.m4:777: -1- AC_DEFUN([LT_FUNC_DLSYM_USCORE], [AC_REQUIRE([LT_SYS_SYMBOL_USCORE])dnl
+if test x"$lt_cv_sys_symbol_underscore" = xyes; then
+ if test x"$libltdl_cv_func_dlopen" = xyes ||
+ test x"$libltdl_cv_lib_dl_dlopen" = xyes ; then
+ AC_CACHE_CHECK([whether we have to add an underscore for dlsym],
+ [libltdl_cv_need_uscore],
+ [libltdl_cv_need_uscore=unknown
+ save_LIBS="$LIBS"
+ LIBS="$LIBS $LIBADD_DLOPEN"
+ _LT_TRY_DLOPEN_SELF(
+ [libltdl_cv_need_uscore=no], [libltdl_cv_need_uscore=yes],
+ [], [libltdl_cv_need_uscore=cross])
+ LIBS="$save_LIBS"
+ ])
+ fi
+fi
+
+if test x"$libltdl_cv_need_uscore" = xyes; then
+ AC_DEFINE([NEED_USCORE], [1],
+ [Define if dlsym() requires a leading underscore in symbol names.])
+fi
+])
+m4trace:/usr/share/aclocal/ltdl.m4:802: -1- AU_DEFUN([AC_LTDL_DLSYM_USCORE], [m4_if($#, 0, [LT_FUNC_DLSYM_USCORE], [LT_FUNC_DLSYM_USCORE($@)])])
+m4trace:/usr/share/aclocal/ltdl.m4:802: -1- AC_DEFUN([AC_LTDL_DLSYM_USCORE], [AC_DIAGNOSE([obsolete], [The macro `AC_LTDL_DLSYM_USCORE' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_FUNC_DLSYM_USCORE], [LT_FUNC_DLSYM_USCORE($@)])])
+m4trace:/usr/share/aclocal/ltoptions.m4:13: -1- AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])])
+m4trace:/usr/share/aclocal/ltoptions.m4:110: -1- AU_DEFUN([AC_LIBTOOL_DLOPEN], [_LT_SET_OPTION([LT_INIT], [dlopen])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `dlopen' option into LT_INIT's first parameter.])
+])
+m4trace:/usr/share/aclocal/ltoptions.m4:110: -1- AC_DEFUN([AC_LIBTOOL_DLOPEN], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_DLOPEN' is obsolete.
+You should run autoupdate.])dnl
+_LT_SET_OPTION([LT_INIT], [dlopen])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `dlopen' option into LT_INIT's first parameter.])
+])
+m4trace:/usr/share/aclocal/ltoptions.m4:145: -1- AU_DEFUN([AC_LIBTOOL_WIN32_DLL], [AC_REQUIRE([AC_CANONICAL_HOST])dnl
+_LT_SET_OPTION([LT_INIT], [win32-dll])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `win32-dll' option into LT_INIT's first parameter.])
+])
+m4trace:/usr/share/aclocal/ltoptions.m4:145: -1- AC_DEFUN([AC_LIBTOOL_WIN32_DLL], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_WIN32_DLL' is obsolete.
+You should run autoupdate.])dnl
+AC_REQUIRE([AC_CANONICAL_HOST])dnl
+_LT_SET_OPTION([LT_INIT], [win32-dll])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `win32-dll' option into LT_INIT's first parameter.])
+])
+m4trace:/usr/share/aclocal/ltoptions.m4:194: -1- AC_DEFUN([AC_ENABLE_SHARED], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared])
+])
+m4trace:/usr/share/aclocal/ltoptions.m4:198: -1- AC_DEFUN([AC_DISABLE_SHARED], [_LT_SET_OPTION([LT_INIT], [disable-shared])
+])
+m4trace:/usr/share/aclocal/ltoptions.m4:202: -1- AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)])
+m4trace:/usr/share/aclocal/ltoptions.m4:202: -1- AC_DEFUN([AM_ENABLE_SHARED], [AC_DIAGNOSE([obsolete], [The macro `AM_ENABLE_SHARED' is obsolete.
+You should run autoupdate.])dnl
+AC_ENABLE_SHARED($@)])
+m4trace:/usr/share/aclocal/ltoptions.m4:203: -1- AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)])
+m4trace:/usr/share/aclocal/ltoptions.m4:203: -1- AC_DEFUN([AM_DISABLE_SHARED], [AC_DIAGNOSE([obsolete], [The macro `AM_DISABLE_SHARED' is obsolete.
+You should run autoupdate.])dnl
+AC_DISABLE_SHARED($@)])
+m4trace:/usr/share/aclocal/ltoptions.m4:248: -1- AC_DEFUN([AC_ENABLE_STATIC], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static])
+])
+m4trace:/usr/share/aclocal/ltoptions.m4:252: -1- AC_DEFUN([AC_DISABLE_STATIC], [_LT_SET_OPTION([LT_INIT], [disable-static])
+])
+m4trace:/usr/share/aclocal/ltoptions.m4:256: -1- AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)])
+m4trace:/usr/share/aclocal/ltoptions.m4:256: -1- AC_DEFUN([AM_ENABLE_STATIC], [AC_DIAGNOSE([obsolete], [The macro `AM_ENABLE_STATIC' is obsolete.
+You should run autoupdate.])dnl
+AC_ENABLE_STATIC($@)])
+m4trace:/usr/share/aclocal/ltoptions.m4:257: -1- AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)])
+m4trace:/usr/share/aclocal/ltoptions.m4:257: -1- AC_DEFUN([AM_DISABLE_STATIC], [AC_DIAGNOSE([obsolete], [The macro `AM_DISABLE_STATIC' is obsolete.
+You should run autoupdate.])dnl
+AC_DISABLE_STATIC($@)])
+m4trace:/usr/share/aclocal/ltoptions.m4:302: -1- AU_DEFUN([AC_ENABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you put
+the `fast-install' option into LT_INIT's first parameter.])
+])
+m4trace:/usr/share/aclocal/ltoptions.m4:302: -1- AC_DEFUN([AC_ENABLE_FAST_INSTALL], [AC_DIAGNOSE([obsolete], [The macro `AC_ENABLE_FAST_INSTALL' is obsolete.
+You should run autoupdate.])dnl
+_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you put
+the `fast-install' option into LT_INIT's first parameter.])
+])
+m4trace:/usr/share/aclocal/ltoptions.m4:309: -1- AU_DEFUN([AC_DISABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], [disable-fast-install])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you put
+the `disable-fast-install' option into LT_INIT's first parameter.])
+])
+m4trace:/usr/share/aclocal/ltoptions.m4:309: -1- AC_DEFUN([AC_DISABLE_FAST_INSTALL], [AC_DIAGNOSE([obsolete], [The macro `AC_DISABLE_FAST_INSTALL' is obsolete.
+You should run autoupdate.])dnl
+_LT_SET_OPTION([LT_INIT], [disable-fast-install])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you put
+the `disable-fast-install' option into LT_INIT's first parameter.])
+])
+m4trace:/usr/share/aclocal/ltoptions.m4:342: -1- AU_DEFUN([AC_LIBTOOL_PICMODE], [_LT_SET_OPTION([LT_INIT], [pic-only])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `pic-only' option into LT_INIT's first parameter.])
+])
+m4trace:/usr/share/aclocal/ltoptions.m4:342: -1- AC_DEFUN([AC_LIBTOOL_PICMODE], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_PICMODE' is obsolete.
+You should run autoupdate.])dnl
+_LT_SET_OPTION([LT_INIT], [pic-only])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `pic-only' option into LT_INIT's first parameter.])
+])
+m4trace:/usr/share/aclocal/ltsugar.m4:13: -1- AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])])
+m4trace:/usr/share/aclocal/ltversion.m4:18: -1- AC_DEFUN([LTVERSION_VERSION], [macro_version='2.2.6b'
+macro_revision='1.3018'
+_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?])
+_LT_DECL(, macro_revision, 0)
+])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:36: -1- AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:40: -1- AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:41: -1- AC_DEFUN([_LT_AC_SHELL_INIT])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:42: -1- AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:44: -1- AC_DEFUN([_LT_AC_TAGVAR])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:45: -1- AC_DEFUN([AC_LTDL_ENABLE_INSTALL])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:46: -1- AC_DEFUN([AC_LTDL_PREOPEN])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:47: -1- AC_DEFUN([_LT_AC_SYS_COMPILER])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:48: -1- AC_DEFUN([_LT_AC_LOCK])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:49: -1- AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:50: -1- AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:51: -1- AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:52: -1- AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:53: -1- AC_DEFUN([AC_LIBTOOL_OBJDIR])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:54: -1- AC_DEFUN([AC_LTDL_OBJDIR])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:55: -1- AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:56: -1- AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:57: -1- AC_DEFUN([AC_PATH_MAGIC])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:58: -1- AC_DEFUN([AC_PROG_LD_GNU])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:59: -1- AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:60: -1- AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:61: -1- AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:62: -1- AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:63: -1- AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:64: -1- AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:65: -1- AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:66: -1- AC_DEFUN([LT_AC_PROG_EGREP])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:71: -1- AC_DEFUN([_AC_PROG_LIBTOOL])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:72: -1- AC_DEFUN([AC_LIBTOOL_SETUP])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:73: -1- AC_DEFUN([_LT_AC_CHECK_DLFCN])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:74: -1- AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:75: -1- AC_DEFUN([_LT_AC_TAGCONFIG])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:77: -1- AC_DEFUN([_LT_AC_LANG_CXX])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:78: -1- AC_DEFUN([_LT_AC_LANG_F77])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:79: -1- AC_DEFUN([_LT_AC_LANG_GCJ])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:80: -1- AC_DEFUN([AC_LIBTOOL_RC])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:81: -1- AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:82: -1- AC_DEFUN([_LT_AC_LANG_C_CONFIG])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:83: -1- AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:84: -1- AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:85: -1- AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:86: -1- AC_DEFUN([_LT_AC_LANG_F77_CONFIG])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:87: -1- AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:88: -1- AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:89: -1- AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:90: -1- AC_DEFUN([_LT_AC_LANG_RC_CONFIG])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:91: -1- AC_DEFUN([AC_LIBTOOL_CONFIG])
+m4trace:/usr/share/aclocal/lt~obsolete.m4:92: -1- AC_DEFUN([_LT_AC_FILE_LTDLL_C])
+m4trace:/usr/share/aclocal-1.11/amversion.m4:14: -1- AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version='1.11'
+dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
+dnl require some minimum version. Point them to the right macro.
+m4_if([$1], [1.11.1], [],
+ [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
+])
+m4trace:/usr/share/aclocal-1.11/amversion.m4:33: -1- AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], [AM_AUTOMAKE_VERSION([1.11.1])dnl
+m4_ifndef([AC_AUTOCONF_VERSION],
+ [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
+_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
+m4trace:/usr/share/aclocal-1.11/auxdir.m4:47: -1- AC_DEFUN([AM_AUX_DIR_EXPAND], [dnl Rely on autoconf to set up CDPATH properly.
+AC_PREREQ([2.50])dnl
+# expand $ac_aux_dir to an absolute path
+am_aux_dir=`cd $ac_aux_dir && pwd`
+])
+m4trace:/usr/share/aclocal-1.11/cond.m4:15: -1- AC_DEFUN([AM_CONDITIONAL], [AC_PREREQ(2.52)dnl
+ ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])],
+ [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
+AC_SUBST([$1_TRUE])dnl
+AC_SUBST([$1_FALSE])dnl
+_AM_SUBST_NOTMAKE([$1_TRUE])dnl
+_AM_SUBST_NOTMAKE([$1_FALSE])dnl
+m4_define([_AM_COND_VALUE_$1], [$2])dnl
+if $2; then
+ $1_TRUE=
+ $1_FALSE='#'
+else
+ $1_TRUE='#'
+ $1_FALSE=
+fi
+AC_CONFIG_COMMANDS_PRE(
+[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then
+ AC_MSG_ERROR([[conditional "$1" was never defined.
+Usually this means the macro was only invoked conditionally.]])
+fi])])
+m4trace:/usr/share/aclocal-1.11/depend.m4:28: -1- AC_DEFUN([_AM_DEPENDENCIES], [AC_REQUIRE([AM_SET_DEPDIR])dnl
+AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl
+AC_REQUIRE([AM_MAKE_INCLUDE])dnl
+AC_REQUIRE([AM_DEP_TRACK])dnl
+
+ifelse([$1], CC, [depcc="$CC" am_compiler_list=],
+ [$1], CXX, [depcc="$CXX" am_compiler_list=],
+ [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
+ [$1], UPC, [depcc="$UPC" am_compiler_list=],
+ [$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'],
+ [depcc="$$1" am_compiler_list=])
+
+AC_CACHE_CHECK([dependency style of $depcc],
+ [am_cv_$1_dependencies_compiler_type],
+[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+ # We make a subdir and do the tests there. Otherwise we can end up
+ # making bogus files that we don't know about and never remove. For
+ # instance it was reported that on HP-UX the gcc test will end up
+ # making a dummy file named `D' -- because `-MD' means `put the output
+ # in D'.
+ mkdir conftest.dir
+ # Copy depcomp to subdir because otherwise we won't find it if we're
+ # using a relative directory.
+ cp "$am_depcomp" conftest.dir
+ cd conftest.dir
+ # We will build objects and dependencies in a subdirectory because
+ # it helps to detect inapplicable dependency modes. For instance
+ # both Tru64's cc and ICC support -MD to output dependencies as a
+ # side effect of compilation, but ICC will put the dependencies in
+ # the current directory while Tru64 will put them in the object
+ # directory.
+ mkdir sub
+
+ am_cv_$1_dependencies_compiler_type=none
+ if test "$am_compiler_list" = ""; then
+ am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp`
+ fi
+ am__universal=false
+ m4_case([$1], [CC],
+ [case " $depcc " in #(
+ *\ -arch\ *\ -arch\ *) am__universal=true ;;
+ esac],
+ [CXX],
+ [case " $depcc " in #(
+ *\ -arch\ *\ -arch\ *) am__universal=true ;;
+ esac])
+
+ for depmode in $am_compiler_list; do
+ # Setup a source with many dependencies, because some compilers
+ # like to wrap large dependency lists on column 80 (with \), and
+ # we should not choose a depcomp mode which is confused by this.
+ #
+ # We need to recreate these files for each test, as the compiler may
+ # overwrite some of them when testing with obscure command lines.
+ # This happens at least with the AIX C compiler.
+ : > sub/conftest.c
+ for i in 1 2 3 4 5 6; do
+ echo '#include "conftst'$i'.h"' >> sub/conftest.c
+ # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
+ # Solaris 8's {/usr,}/bin/sh.
+ touch sub/conftst$i.h
+ done
+ echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+ # We check with `-c' and `-o' for the sake of the "dashmstdout"
+ # mode. It turns out that the SunPro C++ compiler does not properly
+ # handle `-M -o', and we need to detect this. Also, some Intel
+ # versions had trouble with output in subdirs
+ am__obj=sub/conftest.${OBJEXT-o}
+ am__minus_obj="-o $am__obj"
+ case $depmode in
+ gcc)
+ # This depmode causes a compiler race in universal mode.
+ test "$am__universal" = false || continue
+ ;;
+ nosideeffect)
+ # after this tag, mechanisms are not by side-effect, so they'll
+ # only be used when explicitly requested
+ if test "x$enable_dependency_tracking" = xyes; then
+ continue
+ else
+ break
+ fi
+ ;;
+ msvisualcpp | msvcmsys)
+ # This compiler won't grok `-c -o', but also, the minuso test has
+ # not run yet. These depmodes are late enough in the game, and
+ # so weak that their functioning should not be impacted.
+ am__obj=conftest.${OBJEXT-o}
+ am__minus_obj=
+ ;;
+ none) break ;;
+ esac
+ if depmode=$depmode \
+ source=sub/conftest.c object=$am__obj \
+ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
+ >/dev/null 2>conftest.err &&
+ grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
+ ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+ # icc doesn't choke on unknown options, it will just issue warnings
+ # or remarks (even with -Werror). So we grep stderr for any message
+ # that says an option was ignored or not supported.
+ # When given -MP, icc 7.0 and 7.1 complain thusly:
+ # icc: Command line warning: ignoring option '-M'; no argument required
+ # The diagnosis changed in icc 8.0:
+ # icc: Command line remark: option '-MP' not supported
+ if (grep 'ignoring option' conftest.err ||
+ grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+ am_cv_$1_dependencies_compiler_type=$depmode
+ break
+ fi
+ fi
+ done
+
+ cd ..
+ rm -rf conftest.dir
+else
+ am_cv_$1_dependencies_compiler_type=none
+fi
+])
+AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type])
+AM_CONDITIONAL([am__fastdep$1], [
+ test "x$enable_dependency_tracking" != xno \
+ && test "$am_cv_$1_dependencies_compiler_type" = gcc3])
+])
+m4trace:/usr/share/aclocal-1.11/depend.m4:163: -1- AC_DEFUN([AM_SET_DEPDIR], [AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl
+])
+m4trace:/usr/share/aclocal-1.11/depend.m4:171: -1- AC_DEFUN([AM_DEP_TRACK], [AC_ARG_ENABLE(dependency-tracking,
+[ --disable-dependency-tracking speeds up one-time build
+ --enable-dependency-tracking do not reject slow dependency extractors])
+if test "x$enable_dependency_tracking" != xno; then
+ am_depcomp="$ac_aux_dir/depcomp"
+ AMDEPBACKSLASH='\'
+fi
+AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
+AC_SUBST([AMDEPBACKSLASH])dnl
+_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl
+])
+m4trace:/usr/share/aclocal-1.11/depout.m4:14: -1- AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], [{
+ # Autoconf 2.62 quotes --file arguments for eval, but not when files
+ # are listed without --file. Let's play safe and only enable the eval
+ # if we detect the quoting.
+ case $CONFIG_FILES in
+ *\'*) eval set x "$CONFIG_FILES" ;;
+ *) set x $CONFIG_FILES ;;
+ esac
+ shift
+ for mf
+ do
+ # Strip MF so we end up with the name of the file.
+ mf=`echo "$mf" | sed -e 's/:.*$//'`
+ # Check whether this is an Automake generated Makefile or not.
+ # We used to match only the files named `Makefile.in', but
+ # some people rename them; so instead we look at the file content.
+ # Grep'ing the first line is not enough: some people post-process
+ # each Makefile.in and add a new line on top of each file to say so.
+ # Grep'ing the whole file is not good either: AIX grep has a line
+ # limit of 2048, but all sed's we know have understand at least 4000.
+ if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
+ dirpart=`AS_DIRNAME("$mf")`
+ else
+ continue
+ fi
+ # Extract the definition of DEPDIR, am__include, and am__quote
+ # from the Makefile without running `make'.
+ DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
+ test -z "$DEPDIR" && continue
+ am__include=`sed -n 's/^am__include = //p' < "$mf"`
+ test -z "am__include" && continue
+ am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
+ # When using ansi2knr, U may be empty or an underscore; expand it
+ U=`sed -n 's/^U = //p' < "$mf"`
+ # Find all dependency output files, they are included files with
+ # $(DEPDIR) in their names. We invoke sed twice because it is the
+ # simplest approach to changing $(DEPDIR) to its actual value in the
+ # expansion.
+ for file in `sed -n "
+ s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
+ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
+ # Make sure the directory exists.
+ test -f "$dirpart/$file" && continue
+ fdir=`AS_DIRNAME(["$file"])`
+ AS_MKDIR_P([$dirpart/$fdir])
+ # echo "creating $dirpart/$file"
+ echo '# dummy' > "$dirpart/$file"
+ done
+ done
+}
+])
+m4trace:/usr/share/aclocal-1.11/depout.m4:75: -1- AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], [AC_CONFIG_COMMANDS([depfiles],
+ [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS],
+ [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"])
+])
+m4trace:/usr/share/aclocal-1.11/init.m4:26: -1- AC_DEFUN([AM_INIT_AUTOMAKE], [AC_PREREQ([2.62])dnl
+dnl Autoconf wants to disallow AM_ names. We explicitly allow
+dnl the ones we care about.
+m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
+AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
+AC_REQUIRE([AC_PROG_INSTALL])dnl
+if test "`cd $srcdir && pwd`" != "`pwd`"; then
+ # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
+ # is not polluted with repeated "-I."
+ AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl
+ # test to see if srcdir already configured
+ if test -f $srcdir/config.status; then
+ AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
+ fi
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+ if (cygpath --version) >/dev/null 2>/dev/null; then
+ CYGPATH_W='cygpath -w'
+ else
+ CYGPATH_W=echo
+ fi
+fi
+AC_SUBST([CYGPATH_W])
+
+# Define the identity of the package.
+dnl Distinguish between old-style and new-style calls.
+m4_ifval([$2],
+[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
+ AC_SUBST([PACKAGE], [$1])dnl
+ AC_SUBST([VERSION], [$2])],
+[_AM_SET_OPTIONS([$1])dnl
+dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT.
+m4_if(m4_ifdef([AC_PACKAGE_NAME], 1)m4_ifdef([AC_PACKAGE_VERSION], 1), 11,,
+ [m4_fatal([AC_INIT should be called with package and version arguments])])dnl
+ AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
+ AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
+
+_AM_IF_OPTION([no-define],,
+[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package])
+ AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl
+
+# Some tools Automake needs.
+AC_REQUIRE([AM_SANITY_CHECK])dnl
+AC_REQUIRE([AC_ARG_PROGRAM])dnl
+AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version})
+AM_MISSING_PROG(AUTOCONF, autoconf)
+AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version})
+AM_MISSING_PROG(AUTOHEADER, autoheader)
+AM_MISSING_PROG(MAKEINFO, makeinfo)
+AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
+AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl
+AC_REQUIRE([AM_PROG_MKDIR_P])dnl
+# We need awk for the "check" target. The system "awk" is bad on
+# some platforms.
+AC_REQUIRE([AC_PROG_AWK])dnl
+AC_REQUIRE([AC_PROG_MAKE_SET])dnl
+AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])],
+ [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])],
+ [_AM_PROG_TAR([v7])])])
+_AM_IF_OPTION([no-dependencies],,
+[AC_PROVIDE_IFELSE([AC_PROG_CC],
+ [_AM_DEPENDENCIES(CC)],
+ [define([AC_PROG_CC],
+ defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_CXX],
+ [_AM_DEPENDENCIES(CXX)],
+ [define([AC_PROG_CXX],
+ defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_OBJC],
+ [_AM_DEPENDENCIES(OBJC)],
+ [define([AC_PROG_OBJC],
+ defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])dnl
+])
+_AM_IF_OPTION([silent-rules], [AC_REQUIRE([AM_SILENT_RULES])])dnl
+dnl The `parallel-tests' driver may need to know about EXEEXT, so add the
+dnl `am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This macro
+dnl is hooked onto _AC_COMPILER_EXEEXT early, see below.
+AC_CONFIG_COMMANDS_PRE(dnl
+[m4_provide_if([_AM_COMPILER_EXEEXT],
+ [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl
+])
+m4trace:/usr/share/aclocal-1.11/init.m4:126: -1- AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], [# Compute $1's index in $config_headers.
+_am_arg=$1
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+ case $_am_header in
+ $_am_arg | $_am_arg:* )
+ break ;;
+ * )
+ _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+ esac
+done
+echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
+m4trace:/usr/share/aclocal-1.11/install-sh.m4:11: -1- AC_DEFUN([AM_PROG_INSTALL_SH], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+if test x"${install_sh}" != xset; then
+ case $am_aux_dir in
+ *\ * | *\ *)
+ install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
+ *)
+ install_sh="\${SHELL} $am_aux_dir/install-sh"
+ esac
+fi
+AC_SUBST(install_sh)])
+m4trace:/usr/share/aclocal-1.11/lead-dot.m4:12: -1- AC_DEFUN([AM_SET_LEADING_DOT], [rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+ am__leading_dot=.
+else
+ am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+AC_SUBST([am__leading_dot])])
+m4trace:/usr/share/aclocal-1.11/maintainer.m4:19: -1- AC_DEFUN([AM_MAINTAINER_MODE], [m4_case(m4_default([$1], [disable]),
+ [enable], [m4_define([am_maintainer_other], [disable])],
+ [disable], [m4_define([am_maintainer_other], [enable])],
+ [m4_define([am_maintainer_other], [enable])
+ m4_warn([syntax], [unexpected argument to AM@&t@_MAINTAINER_MODE: $1])])
+AC_MSG_CHECKING([whether to am_maintainer_other maintainer-specific portions of Makefiles])
+ dnl maintainer-mode's default is 'disable' unless 'enable' is passed
+ AC_ARG_ENABLE([maintainer-mode],
+[ --][am_maintainer_other][-maintainer-mode am_maintainer_other make rules and dependencies not useful
+ (and sometimes confusing) to the casual installer],
+ [USE_MAINTAINER_MODE=$enableval],
+ [USE_MAINTAINER_MODE=]m4_if(am_maintainer_other, [enable], [no], [yes]))
+ AC_MSG_RESULT([$USE_MAINTAINER_MODE])
+ AM_CONDITIONAL([MAINTAINER_MODE], [test $USE_MAINTAINER_MODE = yes])
+ MAINT=$MAINTAINER_MODE_TRUE
+ AC_SUBST([MAINT])dnl
+
+])
+m4trace:/usr/share/aclocal-1.11/maintainer.m4:39: -1- AU_DEFUN([jm_MAINTAINER_MODE], [AM_MAINTAINER_MODE])
+m4trace:/usr/share/aclocal-1.11/maintainer.m4:39: -1- AC_DEFUN([jm_MAINTAINER_MODE], [AC_DIAGNOSE([obsolete], [The macro `jm_MAINTAINER_MODE' is obsolete.
+You should run autoupdate.])dnl
+AM_MAINTAINER_MODE])
+m4trace:/usr/share/aclocal-1.11/make.m4:14: -1- AC_DEFUN([AM_MAKE_INCLUDE], [am_make=${MAKE-make}
+cat > confinc << 'END'
+am__doit:
+ @echo this is the am__doit target
+.PHONY: am__doit
+END
+# If we don't find an include directive, just comment out the code.
+AC_MSG_CHECKING([for style of include used by $am_make])
+am__include="#"
+am__quote=
+_am_result=none
+# First try GNU make style include.
+echo "include confinc" > confmf
+# Ignore all kinds of additional output from `make'.
+case `$am_make -s -f confmf 2> /dev/null` in #(
+*the\ am__doit\ target*)
+ am__include=include
+ am__quote=
+ _am_result=GNU
+ ;;
+esac
+# Now try BSD make style include.
+if test "$am__include" = "#"; then
+ echo '.include "confinc"' > confmf
+ case `$am_make -s -f confmf 2> /dev/null` in #(
+ *the\ am__doit\ target*)
+ am__include=.include
+ am__quote="\""
+ _am_result=BSD
+ ;;
+ esac
+fi
+AC_SUBST([am__include])
+AC_SUBST([am__quote])
+AC_MSG_RESULT([$_am_result])
+rm -f confinc confmf
+])
+m4trace:/usr/share/aclocal-1.11/missing.m4:14: -1- AC_DEFUN([AM_MISSING_PROG], [AC_REQUIRE([AM_MISSING_HAS_RUN])
+$1=${$1-"${am_missing_run}$2"}
+AC_SUBST($1)])
+m4trace:/usr/share/aclocal-1.11/missing.m4:24: -1- AC_DEFUN([AM_MISSING_HAS_RUN], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+AC_REQUIRE_AUX_FILE([missing])dnl
+if test x"${MISSING+set}" != xset; then
+ case $am_aux_dir in
+ *\ * | *\ *)
+ MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
+ *)
+ MISSING="\${SHELL} $am_aux_dir/missing" ;;
+ esac
+fi
+# Use eval to expand $SHELL
+if eval "$MISSING --run true"; then
+ am_missing_run="$MISSING --run "
+else
+ am_missing_run=
+ AC_MSG_WARN([`missing' script is too old or missing])
+fi
+])
+m4trace:/usr/share/aclocal-1.11/mkdirp.m4:11: -1- AC_DEFUN([AM_PROG_MKDIR_P], [AC_PREREQ([2.60])dnl
+AC_REQUIRE([AC_PROG_MKDIR_P])dnl
+dnl Automake 1.8 to 1.9.6 used to define mkdir_p. We now use MKDIR_P,
+dnl while keeping a definition of mkdir_p for backward compatibility.
+dnl @MKDIR_P@ is magic: AC_OUTPUT adjusts its value for each Makefile.
+dnl However we cannot define mkdir_p as $(MKDIR_P) for the sake of
+dnl Makefile.ins that do not define MKDIR_P, so we do our own
+dnl adjustment using top_builddir (which is defined more often than
+dnl MKDIR_P).
+AC_SUBST([mkdir_p], ["$MKDIR_P"])dnl
+case $mkdir_p in
+ [[\\/$]]* | ?:[[\\/]]*) ;;
+ */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;;
+esac
+])
+m4trace:/usr/share/aclocal-1.11/options.m4:13: -1- AC_DEFUN([_AM_MANGLE_OPTION], [[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])])
+m4trace:/usr/share/aclocal-1.11/options.m4:19: -1- AC_DEFUN([_AM_SET_OPTION], [m4_define(_AM_MANGLE_OPTION([$1]), 1)])
+m4trace:/usr/share/aclocal-1.11/options.m4:25: -1- AC_DEFUN([_AM_SET_OPTIONS], [m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])])
+m4trace:/usr/share/aclocal-1.11/options.m4:31: -1- AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
+m4trace:/usr/share/aclocal-1.11/runlog.m4:12: -1- AC_DEFUN([AM_RUN_LOG], [{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD
+ ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+ (exit $ac_status); }])
+m4trace:/usr/share/aclocal-1.11/sanity.m4:14: -1- AC_DEFUN([AM_SANITY_CHECK], [AC_MSG_CHECKING([whether build environment is sane])
+# Just in case
+sleep 1
+echo timestamp > conftest.file
+# Reject unsafe characters in $srcdir or the absolute working directory
+# name. Accept space and tab only in the latter.
+am_lf='
+'
+case `pwd` in
+ *[[\\\"\#\$\&\'\`$am_lf]]*)
+ AC_MSG_ERROR([unsafe absolute working directory name]);;
+esac
+case $srcdir in
+ *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*)
+ AC_MSG_ERROR([unsafe srcdir value: `$srcdir']);;
+esac
+
+# Do `set' in a subshell so we don't clobber the current shell's
+# arguments. Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+ set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
+ if test "$[*]" = "X"; then
+ # -L didn't work.
+ set X `ls -t "$srcdir/configure" conftest.file`
+ fi
+ rm -f conftest.file
+ if test "$[*]" != "X $srcdir/configure conftest.file" \
+ && test "$[*]" != "X conftest.file $srcdir/configure"; then
+
+ # If neither matched, then we have a broken ls. This can happen
+ # if, for instance, CONFIG_SHELL is bash and it inherits a
+ # broken ls alias from the environment. This has actually
+ # happened. Such a system could not be considered "sane".
+ AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken
+alias in your environment])
+ fi
+
+ test "$[2]" = conftest.file
+ )
+then
+ # Ok.
+ :
+else
+ AC_MSG_ERROR([newly created file is older than distributed files!
+Check your system clock])
+fi
+AC_MSG_RESULT(yes)])
+m4trace:/usr/share/aclocal-1.11/silent.m4:14: -1- AC_DEFUN([AM_SILENT_RULES], [AC_ARG_ENABLE([silent-rules],
+[ --enable-silent-rules less verbose build output (undo: `make V=1')
+ --disable-silent-rules verbose build output (undo: `make V=0')])
+case $enable_silent_rules in
+yes) AM_DEFAULT_VERBOSITY=0;;
+no) AM_DEFAULT_VERBOSITY=1;;
+*) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);;
+esac
+AC_SUBST([AM_DEFAULT_VERBOSITY])dnl
+AM_BACKSLASH='\'
+AC_SUBST([AM_BACKSLASH])dnl
+_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
+])
+m4trace:/usr/share/aclocal-1.11/strip.m4:17: -1- AC_DEFUN([AM_PROG_INSTALL_STRIP], [AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
+# Installed binaries are usually stripped using `strip' when the user
+# run `make install-strip'. However `strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the `STRIP' environment variable to overrule this program.
+dnl Don't test for $cross_compiling = yes, because it might be `maybe'.
+if test "$cross_compiling" != no; then
+ AC_CHECK_TOOL([STRIP], [strip], :)
+fi
+INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
+AC_SUBST([INSTALL_STRIP_PROGRAM])])
+m4trace:/usr/share/aclocal-1.11/substnot.m4:14: -1- AC_DEFUN([_AM_SUBST_NOTMAKE])
+m4trace:/usr/share/aclocal-1.11/substnot.m4:19: -1- AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
+m4trace:/usr/share/aclocal-1.11/tar.m4:24: -1- AC_DEFUN([_AM_PROG_TAR], [# Always define AMTAR for backward compatibility.
+AM_MISSING_PROG([AMTAR], [tar])
+m4_if([$1], [v7],
+ [am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'],
+ [m4_case([$1], [ustar],, [pax],,
+ [m4_fatal([Unknown tar format])])
+AC_MSG_CHECKING([how to create a $1 tar archive])
+# Loop over all known methods to create a tar archive until one works.
+_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none'
+_am_tools=${am_cv_prog_tar_$1-$_am_tools}
+# Do not fold the above two line into one, because Tru64 sh and
+# Solaris sh will not grok spaces in the rhs of `-'.
+for _am_tool in $_am_tools
+do
+ case $_am_tool in
+ gnutar)
+ for _am_tar in tar gnutar gtar;
+ do
+ AM_RUN_LOG([$_am_tar --version]) && break
+ done
+ am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
+ am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
+ am__untar="$_am_tar -xf -"
+ ;;
+ plaintar)
+ # Must skip GNU tar: if it does not support --format= it doesn't create
+ # ustar tarball either.
+ (tar --version) >/dev/null 2>&1 && continue
+ am__tar='tar chf - "$$tardir"'
+ am__tar_='tar chf - "$tardir"'
+ am__untar='tar xf -'
+ ;;
+ pax)
+ am__tar='pax -L -x $1 -w "$$tardir"'
+ am__tar_='pax -L -x $1 -w "$tardir"'
+ am__untar='pax -r'
+ ;;
+ cpio)
+ am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
+ am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
+ am__untar='cpio -i -H $1 -d'
+ ;;
+ none)
+ am__tar=false
+ am__tar_=false
+ am__untar=false
+ ;;
+ esac
+
+ # If the value was cached, stop now. We just wanted to have am__tar
+ # and am__untar set.
+ test -n "${am_cv_prog_tar_$1}" && break
+
+ # tar/untar a dummy directory, and stop if the command works
+ rm -rf conftest.dir
+ mkdir conftest.dir
+ echo GrepMe > conftest.dir/file
+ AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
+ rm -rf conftest.dir
+ if test -s conftest.tar; then
+ AM_RUN_LOG([$am__untar <conftest.tar])
+ grep GrepMe conftest.dir/file >/dev/null 2>&1 && break
+ fi
+done
+rm -rf conftest.dir
+
+AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
+AC_MSG_RESULT([$am_cv_prog_tar_$1])])
+AC_SUBST([am__tar])
+AC_SUBST([am__untar])
+])
+m4trace:m4/acx_pthread.m4:49: -1- AC_DEFUN([ACX_PTHREAD], [
+AC_REQUIRE([AC_CANONICAL_HOST])
+AC_LANG_SAVE
+AC_LANG_C
+acx_pthread_ok=no
+
+# We used to check for pthread.h first, but this fails if pthread.h
+# requires special compiler flags (e.g. on True64 or Sequent).
+# It gets checked for in the link test anyway.
+
+# First of all, check if the user has set any of the PTHREAD_LIBS,
+# etcetera environment variables, and if threads linking works using
+# them:
+if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+ save_LIBS="$LIBS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
+ AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes)
+ AC_MSG_RESULT($acx_pthread_ok)
+ if test x"$acx_pthread_ok" = xno; then
+ PTHREAD_LIBS=""
+ PTHREAD_CFLAGS=""
+ fi
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+fi
+
+# We must check for the threads library under a number of different
+# names; the ordering is very important because some systems
+# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
+# libraries is broken (non-POSIX).
+
+# Create a list of thread flags to try. Items starting with a "-" are
+# C compiler flags, and other items are library names, except for "none"
+# which indicates that we try without any flags at all, and "pthread-config"
+# which is a program returning the flags for the Pth emulation library.
+
+acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
+
+# The ordering *is* (sometimes) important. Some notes on the
+# individual items follow:
+
+# pthreads: AIX (must check this before -lpthread)
+# none: in case threads are in libc; should be tried before -Kthread and
+# other compiler flags to prevent continual compiler warnings
+# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
+# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
+# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
+# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
+# -pthreads: Solaris/gcc
+# -mthreads: Mingw32/gcc, Lynx/gcc
+# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
+# doesn't hurt to check since this sometimes defines pthreads too;
+# also defines -D_REENTRANT)
+# ... -mt is also the pthreads flag for HP/aCC
+# pthread: Linux, etcetera
+# --thread-safe: KAI C++
+# pthread-config: use pthread-config program (for GNU Pth library)
+
+case "${host_cpu}-${host_os}" in
+ *solaris*)
+
+ # On Solaris (at least, for some versions), libc contains stubbed
+ # (non-functional) versions of the pthreads routines, so link-based
+ # tests will erroneously succeed. (We need to link with -pthreads/-mt/
+ # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
+ # a function called by this macro, so we could check for that, but
+ # who knows whether they'll stub that too in a future libc.) So,
+ # we'll just look for -pthreads and -lpthread first:
+
+ acx_pthread_flags="-pthreads pthread -mt -pthread $acx_pthread_flags"
+ ;;
+esac
+
+if test x"$acx_pthread_ok" = xno; then
+for flag in $acx_pthread_flags; do
+
+ case $flag in
+ none)
+ AC_MSG_CHECKING([whether pthreads work without any flags])
+ ;;
+
+ -*)
+ AC_MSG_CHECKING([whether pthreads work with $flag])
+ PTHREAD_CFLAGS="$flag"
+ ;;
+
+ pthread-config)
+ AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no)
+ if test x"$acx_pthread_config" = xno; then continue; fi
+ PTHREAD_CFLAGS="`pthread-config --cflags`"
+ PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
+ ;;
+
+ *)
+ AC_MSG_CHECKING([for the pthreads library -l$flag])
+ PTHREAD_LIBS="-l$flag"
+ ;;
+ esac
+
+ save_LIBS="$LIBS"
+ save_CFLAGS="$CFLAGS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+ # Check for various functions. We must include pthread.h,
+ # since some functions may be macros. (On the Sequent, we
+ # need a special flag -Kthread to make this header compile.)
+ # We check for pthread_join because it is in -lpthread on IRIX
+ # while pthread_create is in libc. We check for pthread_attr_init
+ # due to DEC craziness with -lpthreads. We check for
+ # pthread_cleanup_push because it is one of the few pthread
+ # functions on Solaris that doesn't have a non-functional libc stub.
+ # We try pthread_create on general principles.
+ AC_TRY_LINK([#include <pthread.h>],
+ [pthread_t th; pthread_join(th, 0);
+ pthread_attr_init(0); pthread_cleanup_push(0, 0);
+ pthread_create(0,0,0,0); pthread_cleanup_pop(0); ],
+ [acx_pthread_ok=yes])
+
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+
+ AC_MSG_RESULT($acx_pthread_ok)
+ if test "x$acx_pthread_ok" = xyes; then
+ break;
+ fi
+
+ PTHREAD_LIBS=""
+ PTHREAD_CFLAGS=""
+done
+fi
+
+# Various other checks:
+if test "x$acx_pthread_ok" = xyes; then
+ save_LIBS="$LIBS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+ # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
+ AC_MSG_CHECKING([for joinable pthread attribute])
+ attr_name=unknown
+ for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
+ AC_TRY_LINK([#include <pthread.h>], [int attr=$attr; return attr;],
+ [attr_name=$attr; break])
+ done
+ AC_MSG_RESULT($attr_name)
+ if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
+ AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
+ [Define to necessary symbol if this constant
+ uses a non-standard name on your system.])
+ fi
+
+ AC_MSG_CHECKING([if more special flags are required for pthreads])
+ flag=no
+ case "${host_cpu}-${host_os}" in
+ *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";;
+ *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
+ esac
+ AC_MSG_RESULT(${flag})
+ if test "x$flag" != xno; then
+ PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
+ fi
+
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+
+ # More AIX lossage: must compile with xlc_r or cc_r
+ if test x"$GCC" != xyes; then
+ AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC})
+ else
+ PTHREAD_CC=$CC
+ fi
+else
+ PTHREAD_CC="$CC"
+fi
+
+AC_SUBST(PTHREAD_LIBS)
+AC_SUBST(PTHREAD_CFLAGS)
+AC_SUBST(PTHREAD_CC)
+
+# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
+if test x"$acx_pthread_ok" = xyes; then
+ ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
+ :
+else
+ acx_pthread_ok=no
+ $2
+fi
+AC_LANG_RESTORE
+])
+m4trace:m4/codeset.m4:9: -1- AC_DEFUN([AM_LANGINFO_CODESET], [
+ AC_CACHE_CHECK([for nl_langinfo and CODESET], am_cv_langinfo_codeset,
+ [AC_TRY_LINK([#include <langinfo.h>],
+ [char* cs = nl_langinfo(CODESET); return !cs;],
+ am_cv_langinfo_codeset=yes,
+ am_cv_langinfo_codeset=no)
+ ])
+ if test $am_cv_langinfo_codeset = yes; then
+ AC_DEFINE(HAVE_LANGINFO_CODESET, 1,
+ [Define if you have <langinfo.h> and nl_langinfo(CODESET).])
+ fi
+])
+m4trace:m4/gettext.m4:57: -1- AC_DEFUN([AM_GNU_GETTEXT], [
+ dnl Argument checking.
+ ifelse([$1], [], , [ifelse([$1], [external], , [ifelse([$1], [no-libtool], , [ifelse([$1], [use-libtool], ,
+ [errprint([ERROR: invalid first argument to AM_GNU_GETTEXT
+])])])])])
+ ifelse([$2], [], , [ifelse([$2], [need-ngettext], , [ifelse([$2], [need-formatstring-macros], ,
+ [errprint([ERROR: invalid second argument to AM_GNU_GETTEXT
+])])])])
+ define([gt_included_intl],
+ ifelse([$1], [external],
+ ifdef([AM_GNU_GETTEXT_][INTL_SUBDIR], [yes], [no]),
+ [yes]))
+ define([gt_libtool_suffix_prefix], ifelse([$1], [use-libtool], [l], []))
+ gt_NEEDS_INIT
+ AM_GNU_GETTEXT_NEED([$2])
+
+ AC_REQUIRE([AM_PO_SUBDIRS])dnl
+ ifelse(gt_included_intl, yes, [
+ AC_REQUIRE([AM_INTL_SUBDIR])dnl
+ ])
+
+ dnl Prerequisites of AC_LIB_LINKFLAGS_BODY.
+ AC_REQUIRE([AC_LIB_PREPARE_PREFIX])
+ AC_REQUIRE([AC_LIB_RPATH])
+
+ dnl Sometimes libintl requires libiconv, so first search for libiconv.
+ dnl Ideally we would do this search only after the
+ dnl if test "$USE_NLS" = "yes"; then
+ dnl if { eval "gt_val=\$$gt_func_gnugettext_libc"; test "$gt_val" != "yes"; }; then
+ dnl tests. But if configure.in invokes AM_ICONV after AM_GNU_GETTEXT
+ dnl the configure script would need to contain the same shell code
+ dnl again, outside any 'if'. There are two solutions:
+ dnl - Invoke AM_ICONV_LINKFLAGS_BODY here, outside any 'if'.
+ dnl - Control the expansions in more detail using AC_PROVIDE_IFELSE.
+ dnl Since AC_PROVIDE_IFELSE is only in autoconf >= 2.52 and not
+ dnl documented, we avoid it.
+ ifelse(gt_included_intl, yes, , [
+ AC_REQUIRE([AM_ICONV_LINKFLAGS_BODY])
+ ])
+
+ dnl Sometimes, on MacOS X, libintl requires linking with CoreFoundation.
+ gt_INTL_MACOSX
+
+ dnl Set USE_NLS.
+ AC_REQUIRE([AM_NLS])
+
+ ifelse(gt_included_intl, yes, [
+ BUILD_INCLUDED_LIBINTL=no
+ USE_INCLUDED_LIBINTL=no
+ ])
+ LIBINTL=
+ LTLIBINTL=
+ POSUB=
+
+ dnl Add a version number to the cache macros.
+ case " $gt_needs " in
+ *" need-formatstring-macros "*) gt_api_version=3 ;;
+ *" need-ngettext "*) gt_api_version=2 ;;
+ *) gt_api_version=1 ;;
+ esac
+ gt_func_gnugettext_libc="gt_cv_func_gnugettext${gt_api_version}_libc"
+ gt_func_gnugettext_libintl="gt_cv_func_gnugettext${gt_api_version}_libintl"
+
+ dnl If we use NLS figure out what method
+ if test "$USE_NLS" = "yes"; then
+ gt_use_preinstalled_gnugettext=no
+ ifelse(gt_included_intl, yes, [
+ AC_MSG_CHECKING([whether included gettext is requested])
+ AC_ARG_WITH(included-gettext,
+ [ --with-included-gettext use the GNU gettext library included here],
+ nls_cv_force_use_gnu_gettext=$withval,
+ nls_cv_force_use_gnu_gettext=no)
+ AC_MSG_RESULT($nls_cv_force_use_gnu_gettext)
+
+ nls_cv_use_gnu_gettext="$nls_cv_force_use_gnu_gettext"
+ if test "$nls_cv_force_use_gnu_gettext" != "yes"; then
+ ])
+ dnl User does not insist on using GNU NLS library. Figure out what
+ dnl to use. If GNU gettext is available we use this. Else we have
+ dnl to fall back to GNU NLS library.
+
+ if test $gt_api_version -ge 3; then
+ gt_revision_test_code='
+#ifndef __GNU_GETTEXT_SUPPORTED_REVISION
+#define __GNU_GETTEXT_SUPPORTED_REVISION(major) ((major) == 0 ? 0 : -1)
+#endif
+changequote(,)dnl
+typedef int array [2 * (__GNU_GETTEXT_SUPPORTED_REVISION(0) >= 1) - 1];
+changequote([,])dnl
+'
+ else
+ gt_revision_test_code=
+ fi
+ if test $gt_api_version -ge 2; then
+ gt_expression_test_code=' + * ngettext ("", "", 0)'
+ else
+ gt_expression_test_code=
+ fi
+
+ AC_CACHE_CHECK([for GNU gettext in libc], [$gt_func_gnugettext_libc],
+ [AC_TRY_LINK([#include <libintl.h>
+$gt_revision_test_code
+extern int _nl_msg_cat_cntr;
+extern int *_nl_domain_bindings;],
+ [bindtextdomain ("", "");
+return * gettext ("")$gt_expression_test_code + _nl_msg_cat_cntr + *_nl_domain_bindings],
+ [eval "$gt_func_gnugettext_libc=yes"],
+ [eval "$gt_func_gnugettext_libc=no"])])
+
+ if { eval "gt_val=\$$gt_func_gnugettext_libc"; test "$gt_val" != "yes"; }; then
+ dnl Sometimes libintl requires libiconv, so first search for libiconv.
+ ifelse(gt_included_intl, yes, , [
+ AM_ICONV_LINK
+ ])
+ dnl Search for libintl and define LIBINTL, LTLIBINTL and INCINTL
+ dnl accordingly. Don't use AC_LIB_LINKFLAGS_BODY([intl],[iconv])
+ dnl because that would add "-liconv" to LIBINTL and LTLIBINTL
+ dnl even if libiconv doesn't exist.
+ AC_LIB_LINKFLAGS_BODY([intl])
+ AC_CACHE_CHECK([for GNU gettext in libintl],
+ [$gt_func_gnugettext_libintl],
+ [gt_save_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $INCINTL"
+ gt_save_LIBS="$LIBS"
+ LIBS="$LIBS $LIBINTL"
+ dnl Now see whether libintl exists and does not depend on libiconv.
+ AC_TRY_LINK([#include <libintl.h>
+$gt_revision_test_code
+extern int _nl_msg_cat_cntr;
+extern
+#ifdef __cplusplus
+"C"
+#endif
+const char *_nl_expand_alias (const char *);],
+ [bindtextdomain ("", "");
+return * gettext ("")$gt_expression_test_code + _nl_msg_cat_cntr + *_nl_expand_alias ("")],
+ [eval "$gt_func_gnugettext_libintl=yes"],
+ [eval "$gt_func_gnugettext_libintl=no"])
+ dnl Now see whether libintl exists and depends on libiconv.
+ if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" != yes; } && test -n "$LIBICONV"; then
+ LIBS="$LIBS $LIBICONV"
+ AC_TRY_LINK([#include <libintl.h>
+$gt_revision_test_code
+extern int _nl_msg_cat_cntr;
+extern
+#ifdef __cplusplus
+"C"
+#endif
+const char *_nl_expand_alias (const char *);],
+ [bindtextdomain ("", "");
+return * gettext ("")$gt_expression_test_code + _nl_msg_cat_cntr + *_nl_expand_alias ("")],
+ [LIBINTL="$LIBINTL $LIBICONV"
+ LTLIBINTL="$LTLIBINTL $LTLIBICONV"
+ eval "$gt_func_gnugettext_libintl=yes"
+ ])
+ fi
+ CPPFLAGS="$gt_save_CPPFLAGS"
+ LIBS="$gt_save_LIBS"])
+ fi
+
+ dnl If an already present or preinstalled GNU gettext() is found,
+ dnl use it. But if this macro is used in GNU gettext, and GNU
+ dnl gettext is already preinstalled in libintl, we update this
+ dnl libintl. (Cf. the install rule in intl/Makefile.in.)
+ if { eval "gt_val=\$$gt_func_gnugettext_libc"; test "$gt_val" = "yes"; } \
+ || { { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = "yes"; } \
+ && test "$PACKAGE" != gettext-runtime \
+ && test "$PACKAGE" != gettext-tools; }; then
+ gt_use_preinstalled_gnugettext=yes
+ else
+ dnl Reset the values set by searching for libintl.
+ LIBINTL=
+ LTLIBINTL=
+ INCINTL=
+ fi
+
+ ifelse(gt_included_intl, yes, [
+ if test "$gt_use_preinstalled_gnugettext" != "yes"; then
+ dnl GNU gettext is not found in the C library.
+ dnl Fall back on included GNU gettext library.
+ nls_cv_use_gnu_gettext=yes
+ fi
+ fi
+
+ if test "$nls_cv_use_gnu_gettext" = "yes"; then
+ dnl Mark actions used to generate GNU NLS library.
+ BUILD_INCLUDED_LIBINTL=yes
+ USE_INCLUDED_LIBINTL=yes
+ LIBINTL="ifelse([$3],[],\${top_builddir}/intl,[$3])/libintl.[]gt_libtool_suffix_prefix[]a $LIBICONV $LIBTHREAD"
+ LTLIBINTL="ifelse([$3],[],\${top_builddir}/intl,[$3])/libintl.[]gt_libtool_suffix_prefix[]a $LTLIBICONV $LTLIBTHREAD"
+ LIBS=`echo " $LIBS " | sed -e 's/ -lintl / /' -e 's/^ //' -e 's/ $//'`
+ fi
+
+ CATOBJEXT=
+ if test "$gt_use_preinstalled_gnugettext" = "yes" \
+ || test "$nls_cv_use_gnu_gettext" = "yes"; then
+ dnl Mark actions to use GNU gettext tools.
+ CATOBJEXT=.gmo
+ fi
+ ])
+
+ if test -n "$INTL_MACOSX_LIBS"; then
+ if test "$gt_use_preinstalled_gnugettext" = "yes" \
+ || test "$nls_cv_use_gnu_gettext" = "yes"; then
+ dnl Some extra flags are needed during linking.
+ LIBINTL="$LIBINTL $INTL_MACOSX_LIBS"
+ LTLIBINTL="$LTLIBINTL $INTL_MACOSX_LIBS"
+ fi
+ fi
+
+ if test "$gt_use_preinstalled_gnugettext" = "yes" \
+ || test "$nls_cv_use_gnu_gettext" = "yes"; then
+ AC_DEFINE(ENABLE_NLS, 1,
+ [Define to 1 if translation of program messages to the user's native language
+ is requested.])
+ else
+ USE_NLS=no
+ fi
+ fi
+
+ AC_MSG_CHECKING([whether to use NLS])
+ AC_MSG_RESULT([$USE_NLS])
+ if test "$USE_NLS" = "yes"; then
+ AC_MSG_CHECKING([where the gettext function comes from])
+ if test "$gt_use_preinstalled_gnugettext" = "yes"; then
+ if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = "yes"; }; then
+ gt_source="external libintl"
+ else
+ gt_source="libc"
+ fi
+ else
+ gt_source="included intl directory"
+ fi
+ AC_MSG_RESULT([$gt_source])
+ fi
+
+ if test "$USE_NLS" = "yes"; then
+
+ if test "$gt_use_preinstalled_gnugettext" = "yes"; then
+ if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = "yes"; }; then
+ AC_MSG_CHECKING([how to link with libintl])
+ AC_MSG_RESULT([$LIBINTL])
+ AC_LIB_APPENDTOVAR([CPPFLAGS], [$INCINTL])
+ fi
+
+ dnl For backward compatibility. Some packages may be using this.
+ AC_DEFINE(HAVE_GETTEXT, 1,
+ [Define if the GNU gettext() function is already present or preinstalled.])
+ AC_DEFINE(HAVE_DCGETTEXT, 1,
+ [Define if the GNU dcgettext() function is already present or preinstalled.])
+ fi
+
+ dnl We need to process the po/ directory.
+ POSUB=po
+ fi
+
+ ifelse(gt_included_intl, yes, [
+ dnl If this is used in GNU gettext we have to set BUILD_INCLUDED_LIBINTL
+ dnl to 'yes' because some of the testsuite requires it.
+ if test "$PACKAGE" = gettext-runtime || test "$PACKAGE" = gettext-tools; then
+ BUILD_INCLUDED_LIBINTL=yes
+ fi
+
+ dnl Make all variables we use known to autoconf.
+ AC_SUBST(BUILD_INCLUDED_LIBINTL)
+ AC_SUBST(USE_INCLUDED_LIBINTL)
+ AC_SUBST(CATOBJEXT)
+
+ dnl For backward compatibility. Some configure.ins may be using this.
+ nls_cv_header_intl=
+ nls_cv_header_libgt=
+
+ dnl For backward compatibility. Some Makefiles may be using this.
+ DATADIRNAME=share
+ AC_SUBST(DATADIRNAME)
+
+ dnl For backward compatibility. Some Makefiles may be using this.
+ INSTOBJEXT=.mo
+ AC_SUBST(INSTOBJEXT)
+
+ dnl For backward compatibility. Some Makefiles may be using this.
+ GENCAT=gencat
+ AC_SUBST(GENCAT)
+
+ dnl For backward compatibility. Some Makefiles may be using this.
+ INTLOBJS=
+ if test "$USE_INCLUDED_LIBINTL" = yes; then
+ INTLOBJS="\$(GETTOBJS)"
+ fi
+ AC_SUBST(INTLOBJS)
+
+ dnl Enable libtool support if the surrounding package wishes it.
+ INTL_LIBTOOL_SUFFIX_PREFIX=gt_libtool_suffix_prefix
+ AC_SUBST(INTL_LIBTOOL_SUFFIX_PREFIX)
+ ])
+
+ dnl For backward compatibility. Some Makefiles may be using this.
+ INTLLIBS="$LIBINTL"
+ AC_SUBST(INTLLIBS)
+
+ dnl Make all documented variables known to autoconf.
+ AC_SUBST(LIBINTL)
+ AC_SUBST(LTLIBINTL)
+ AC_SUBST(POSUB)
+])
+m4trace:m4/gettext.m4:367: -1- AC_DEFUN([gt_INTL_MACOSX], [
+ dnl Check for API introduced in MacOS X 10.2.
+ AC_CACHE_CHECK([for CFPreferencesCopyAppValue],
+ gt_cv_func_CFPreferencesCopyAppValue,
+ [gt_save_LIBS="$LIBS"
+ LIBS="$LIBS -Wl,-framework -Wl,CoreFoundation"
+ AC_TRY_LINK([#include <CoreFoundation/CFPreferences.h>],
+ [CFPreferencesCopyAppValue(NULL, NULL)],
+ [gt_cv_func_CFPreferencesCopyAppValue=yes],
+ [gt_cv_func_CFPreferencesCopyAppValue=no])
+ LIBS="$gt_save_LIBS"])
+ if test $gt_cv_func_CFPreferencesCopyAppValue = yes; then
+ AC_DEFINE([HAVE_CFPREFERENCESCOPYAPPVALUE], 1,
+ [Define to 1 if you have the MacOS X function CFPreferencesCopyAppValue in the CoreFoundation framework.])
+ fi
+ dnl Check for API introduced in MacOS X 10.3.
+ AC_CACHE_CHECK([for CFLocaleCopyCurrent], gt_cv_func_CFLocaleCopyCurrent,
+ [gt_save_LIBS="$LIBS"
+ LIBS="$LIBS -Wl,-framework -Wl,CoreFoundation"
+ AC_TRY_LINK([#include <CoreFoundation/CFLocale.h>], [CFLocaleCopyCurrent();],
+ [gt_cv_func_CFLocaleCopyCurrent=yes],
+ [gt_cv_func_CFLocaleCopyCurrent=no])
+ LIBS="$gt_save_LIBS"])
+ if test $gt_cv_func_CFLocaleCopyCurrent = yes; then
+ AC_DEFINE([HAVE_CFLOCALECOPYCURRENT], 1,
+ [Define to 1 if you have the MacOS X function CFLocaleCopyCurrent in the CoreFoundation framework.])
+ fi
+ INTL_MACOSX_LIBS=
+ if test $gt_cv_func_CFPreferencesCopyAppValue = yes || test $gt_cv_func_CFLocaleCopyCurrent = yes; then
+ INTL_MACOSX_LIBS="-Wl,-framework -Wl,CoreFoundation"
+ fi
+ AC_SUBST([INTL_MACOSX_LIBS])
+])
+m4trace:m4/gettext.m4:412: -1- AC_DEFUN([AM_GNU_GETTEXT_NEED], [
+ m4_divert_text([INIT_PREPARE], [gt_needs="$gt_needs $1"])
+])
+m4trace:m4/gettext.m4:419: -1- AC_DEFUN([AM_GNU_GETTEXT_VERSION], [])
+m4trace:m4/glibc2.m4:10: -1- AC_DEFUN([gt_GLIBC2], [
+ AC_CACHE_CHECK(whether we are using the GNU C Library 2 or newer,
+ ac_cv_gnu_library_2,
+ [AC_EGREP_CPP([Lucky GNU user],
+ [
+#include <features.h>
+#ifdef __GNU_LIBRARY__
+ #if (__GLIBC__ >= 2)
+ Lucky GNU user
+ #endif
+#endif
+ ],
+ ac_cv_gnu_library_2=yes,
+ ac_cv_gnu_library_2=no)
+ ]
+ )
+ AC_SUBST(GLIBC2)
+ GLIBC2="$ac_cv_gnu_library_2"
+
+])
+m4trace:m4/glibc21.m4:10: -1- AC_DEFUN([gl_GLIBC21], [
+ AC_CACHE_CHECK(whether we are using the GNU C Library 2.1 or newer,
+ ac_cv_gnu_library_2_1,
+ [AC_EGREP_CPP([Lucky GNU user],
+ [
+#include <features.h>
+#ifdef __GNU_LIBRARY__
+ #if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1) || (__GLIBC__ > 2)
+ Lucky GNU user
+ #endif
+#endif
+ ],
+ ac_cv_gnu_library_2_1=yes,
+ ac_cv_gnu_library_2_1=no)
+ ]
+ )
+ AC_SUBST(GLIBC21)
+ GLIBC21="$ac_cv_gnu_library_2_1"
+
+])
+m4trace:m4/iconv.m4:9: -1- AC_DEFUN([AM_ICONV_LINKFLAGS_BODY], [
+ dnl Prerequisites of AC_LIB_LINKFLAGS_BODY.
+ AC_REQUIRE([AC_LIB_PREPARE_PREFIX])
+ AC_REQUIRE([AC_LIB_RPATH])
+
+ dnl Search for libiconv and define LIBICONV, LTLIBICONV and INCICONV
+ dnl accordingly.
+ AC_LIB_LINKFLAGS_BODY([iconv])
+])
+m4trace:m4/iconv.m4:20: -1- AC_DEFUN([AM_ICONV_LINK], [
+ dnl Some systems have iconv in libc, some have it in libiconv (OSF/1 and
+ dnl those with the standalone portable GNU libiconv installed).
+
+ dnl Search for libiconv and define LIBICONV, LTLIBICONV and INCICONV
+ dnl accordingly.
+ AC_REQUIRE([AM_ICONV_LINKFLAGS_BODY])
+
+ dnl Add $INCICONV to CPPFLAGS before performing the following checks,
+ dnl because if the user has installed libiconv and not disabled its use
+ dnl via --without-libiconv-prefix, he wants to use it. The first
+ dnl AC_TRY_LINK will then fail, the second AC_TRY_LINK will succeed.
+ am_save_CPPFLAGS="$CPPFLAGS"
+ AC_LIB_APPENDTOVAR([CPPFLAGS], [$INCICONV])
+
+ AC_CACHE_CHECK(for iconv, am_cv_func_iconv, [
+ am_cv_func_iconv="no, consider installing GNU libiconv"
+ am_cv_lib_iconv=no
+ AC_TRY_LINK([#include <stdlib.h>
+#include <iconv.h>],
+ [iconv_t cd = iconv_open("","");
+ iconv(cd,NULL,NULL,NULL,NULL);
+ iconv_close(cd);],
+ am_cv_func_iconv=yes)
+ if test "$am_cv_func_iconv" != yes; then
+ am_save_LIBS="$LIBS"
+ LIBS="$LIBS $LIBICONV"
+ AC_TRY_LINK([#include <stdlib.h>
+#include <iconv.h>],
+ [iconv_t cd = iconv_open("","");
+ iconv(cd,NULL,NULL,NULL,NULL);
+ iconv_close(cd);],
+ am_cv_lib_iconv=yes
+ am_cv_func_iconv=yes)
+ LIBS="$am_save_LIBS"
+ fi
+ ])
+ if test "$am_cv_func_iconv" = yes; then
+ AC_DEFINE(HAVE_ICONV, 1, [Define if you have the iconv() function.])
+ fi
+ if test "$am_cv_lib_iconv" = yes; then
+ AC_MSG_CHECKING([how to link with libiconv])
+ AC_MSG_RESULT([$LIBICONV])
+ else
+ dnl If $LIBICONV didn't lead to a usable library, we don't need $INCICONV
+ dnl either.
+ CPPFLAGS="$am_save_CPPFLAGS"
+ LIBICONV=
+ LTLIBICONV=
+ fi
+ AC_SUBST(LIBICONV)
+ AC_SUBST(LTLIBICONV)
+])
+m4trace:m4/iconv.m4:75: -1- AC_DEFUN([AM_ICONV], [
+ AM_ICONV_LINK
+ if test "$am_cv_func_iconv" = yes; then
+ AC_MSG_CHECKING([for iconv declaration])
+ AC_CACHE_VAL(am_cv_proto_iconv, [
+ AC_TRY_COMPILE([
+#include <stdlib.h>
+#include <iconv.h>
+extern
+#ifdef __cplusplus
+"C"
+#endif
+#if defined(__STDC__) || defined(__cplusplus)
+size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);
+#else
+size_t iconv();
+#endif
+], [], am_cv_proto_iconv_arg1="", am_cv_proto_iconv_arg1="const")
+ am_cv_proto_iconv="extern size_t iconv (iconv_t cd, $am_cv_proto_iconv_arg1 char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);"])
+ am_cv_proto_iconv=`echo "[$]am_cv_proto_iconv" | tr -s ' ' | sed -e 's/( /(/'`
+ AC_MSG_RESULT([$]{ac_t:-
+ }[$]am_cv_proto_iconv)
+ AC_DEFINE_UNQUOTED(ICONV_CONST, $am_cv_proto_iconv_arg1,
+ [Define as const if the declaration of iconv() needs const.])
+ fi
+])
+m4trace:m4/intdiv0.m4:9: -1- AC_DEFUN([gt_INTDIV0], [
+ AC_REQUIRE([AC_PROG_CC])dnl
+ AC_REQUIRE([AC_CANONICAL_HOST])dnl
+
+ AC_CACHE_CHECK([whether integer division by zero raises SIGFPE],
+ gt_cv_int_divbyzero_sigfpe,
+ [
+ AC_TRY_RUN([
+#include <stdlib.h>
+#include <signal.h>
+
+static void
+#ifdef __cplusplus
+sigfpe_handler (int sig)
+#else
+sigfpe_handler (sig) int sig;
+#endif
+{
+ /* Exit with code 0 if SIGFPE, with code 1 if any other signal. */
+ exit (sig != SIGFPE);
+}
+
+int x = 1;
+int y = 0;
+int z;
+int nan;
+
+int main ()
+{
+ signal (SIGFPE, sigfpe_handler);
+/* IRIX and AIX (when "xlc -qcheck" is used) yield signal SIGTRAP. */
+#if (defined (__sgi) || defined (_AIX)) && defined (SIGTRAP)
+ signal (SIGTRAP, sigfpe_handler);
+#endif
+/* Linux/SPARC yields signal SIGILL. */
+#if defined (__sparc__) && defined (__linux__)
+ signal (SIGILL, sigfpe_handler);
+#endif
+
+ z = x / y;
+ nan = y / y;
+ exit (1);
+}
+], gt_cv_int_divbyzero_sigfpe=yes, gt_cv_int_divbyzero_sigfpe=no,
+ [
+ # Guess based on the CPU.
+ case "$host_cpu" in
+ alpha* | i[34567]86 | m68k | s390*)
+ gt_cv_int_divbyzero_sigfpe="guessing yes";;
+ *)
+ gt_cv_int_divbyzero_sigfpe="guessing no";;
+ esac
+ ])
+ ])
+ case "$gt_cv_int_divbyzero_sigfpe" in
+ *yes) value=1;;
+ *) value=0;;
+ esac
+ AC_DEFINE_UNQUOTED(INTDIV0_RAISES_SIGFPE, $value,
+ [Define if integer division by zero raises signal SIGFPE.])
+])
+m4trace:m4/intl.m4:25: -1- AC_DEFUN([AM_INTL_SUBDIR], [
+ AC_REQUIRE([AC_PROG_INSTALL])dnl
+ AC_REQUIRE([AM_PROG_MKDIR_P])dnl defined by automake
+ AC_REQUIRE([AC_PROG_CC])dnl
+ AC_REQUIRE([AC_CANONICAL_HOST])dnl
+ AC_REQUIRE([gt_GLIBC2])dnl
+ AC_REQUIRE([AC_PROG_RANLIB])dnl
+ AC_REQUIRE([gl_VISIBILITY])dnl
+ AC_REQUIRE([gt_INTL_SUBDIR_CORE])dnl
+ AC_REQUIRE([AC_TYPE_LONG_LONG_INT])dnl
+ AC_REQUIRE([gt_TYPE_LONGDOUBLE])dnl
+ AC_REQUIRE([gt_TYPE_WCHAR_T])dnl
+ AC_REQUIRE([gt_TYPE_WINT_T])dnl
+ AC_REQUIRE([gl_AC_HEADER_INTTYPES_H])
+ AC_REQUIRE([gt_TYPE_INTMAX_T])
+ AC_REQUIRE([gt_PRINTF_POSIX])
+ AC_REQUIRE([gl_GLIBC21])dnl
+ AC_REQUIRE([gl_XSIZE])dnl
+ AC_REQUIRE([gt_INTL_MACOSX])dnl
+
+ AC_CHECK_TYPE([ptrdiff_t], ,
+ [AC_DEFINE([ptrdiff_t], [long],
+ [Define as the type of the result of subtracting two pointers, if the system doesn't define it.])
+ ])
+ AC_CHECK_HEADERS([stddef.h stdlib.h string.h])
+ AC_CHECK_FUNCS([asprintf fwprintf putenv setenv setlocale snprintf wcslen])
+
+ dnl Use the _snprintf function only if it is declared (because on NetBSD it
+ dnl is defined as a weak alias of snprintf; we prefer to use the latter).
+ gt_CHECK_DECL(_snprintf, [#include <stdio.h>])
+ gt_CHECK_DECL(_snwprintf, [#include <stdio.h>])
+
+ dnl Use the *_unlocked functions only if they are declared.
+ dnl (because some of them were defined without being declared in Solaris
+ dnl 2.5.1 but were removed in Solaris 2.6, whereas we want binaries built
+ dnl on Solaris 2.5.1 to run on Solaris 2.6).
+ dnl Don't use AC_CHECK_DECLS because it isn't supported in autoconf-2.13.
+ gt_CHECK_DECL(getc_unlocked, [#include <stdio.h>])
+
+ case $gt_cv_func_printf_posix in
+ *yes) HAVE_POSIX_PRINTF=1 ;;
+ *) HAVE_POSIX_PRINTF=0 ;;
+ esac
+ AC_SUBST([HAVE_POSIX_PRINTF])
+ if test "$ac_cv_func_asprintf" = yes; then
+ HAVE_ASPRINTF=1
+ else
+ HAVE_ASPRINTF=0
+ fi
+ AC_SUBST([HAVE_ASPRINTF])
+ if test "$ac_cv_func_snprintf" = yes; then
+ HAVE_SNPRINTF=1
+ else
+ HAVE_SNPRINTF=0
+ fi
+ AC_SUBST([HAVE_SNPRINTF])
+ if test "$ac_cv_func_wprintf" = yes; then
+ HAVE_WPRINTF=1
+ else
+ HAVE_WPRINTF=0
+ fi
+ AC_SUBST([HAVE_WPRINTF])
+
+ AM_LANGINFO_CODESET
+ gt_LC_MESSAGES
+
+ dnl Compilation on mingw and Cygwin needs special Makefile rules, because
+ dnl 1. when we install a shared library, we must arrange to export
+ dnl auxiliary pointer variables for every exported variable,
+ dnl 2. when we install a shared library and a static library simultaneously,
+ dnl the include file specifies __declspec(dllimport) and therefore we
+ dnl must arrange to define the auxiliary pointer variables for the
+ dnl exported variables _also_ in the static library.
+ if test "$enable_shared" = yes; then
+ case "$host_os" in
+ cygwin*) is_woe32dll=yes ;;
+ *) is_woe32dll=no ;;
+ esac
+ else
+ is_woe32dll=no
+ fi
+ WOE32DLL=$is_woe32dll
+ AC_SUBST([WOE32DLL])
+
+ dnl Rename some macros and functions used for locking.
+ AH_BOTTOM([
+#define __libc_lock_t gl_lock_t
+#define __libc_lock_define gl_lock_define
+#define __libc_lock_define_initialized gl_lock_define_initialized
+#define __libc_lock_init gl_lock_init
+#define __libc_lock_lock gl_lock_lock
+#define __libc_lock_unlock gl_lock_unlock
+#define __libc_lock_recursive_t gl_recursive_lock_t
+#define __libc_lock_define_recursive gl_recursive_lock_define
+#define __libc_lock_define_initialized_recursive gl_recursive_lock_define_initialized
+#define __libc_lock_init_recursive gl_recursive_lock_init
+#define __libc_lock_lock_recursive gl_recursive_lock_lock
+#define __libc_lock_unlock_recursive gl_recursive_lock_unlock
+#define glthread_in_use libintl_thread_in_use
+#define glthread_lock_init libintl_lock_init
+#define glthread_lock_lock libintl_lock_lock
+#define glthread_lock_unlock libintl_lock_unlock
+#define glthread_lock_destroy libintl_lock_destroy
+#define glthread_rwlock_init libintl_rwlock_init
+#define glthread_rwlock_rdlock libintl_rwlock_rdlock
+#define glthread_rwlock_wrlock libintl_rwlock_wrlock
+#define glthread_rwlock_unlock libintl_rwlock_unlock
+#define glthread_rwlock_destroy libintl_rwlock_destroy
+#define glthread_recursive_lock_init libintl_recursive_lock_init
+#define glthread_recursive_lock_lock libintl_recursive_lock_lock
+#define glthread_recursive_lock_unlock libintl_recursive_lock_unlock
+#define glthread_recursive_lock_destroy libintl_recursive_lock_destroy
+#define glthread_once libintl_once
+#define glthread_once_call libintl_once_call
+#define glthread_once_singlethreaded libintl_once_singlethreaded
+])
+])
+m4trace:m4/intl.m4:162: -1- AC_DEFUN([gt_INTL_SUBDIR_CORE], [
+ AC_REQUIRE([AC_C_INLINE])dnl
+ AC_REQUIRE([AC_TYPE_SIZE_T])dnl
+ AC_REQUIRE([gl_AC_HEADER_STDINT_H])
+ AC_REQUIRE([AC_FUNC_ALLOCA])dnl
+ AC_REQUIRE([AC_FUNC_MMAP])dnl
+ AC_REQUIRE([gt_INTDIV0])dnl
+ AC_REQUIRE([gl_AC_TYPE_UINTMAX_T])dnl
+ AC_REQUIRE([gt_INTTYPES_PRI])dnl
+ AC_REQUIRE([gl_LOCK])dnl
+
+ AC_TRY_LINK(
+ [int foo (int a) { a = __builtin_expect (a, 10); return a == 10 ? 0 : 1; }],
+ [],
+ [AC_DEFINE([HAVE_BUILTIN_EXPECT], 1,
+ [Define to 1 if the compiler understands __builtin_expect.])])
+
+ AC_CHECK_HEADERS([argz.h inttypes.h limits.h unistd.h sys/param.h])
+ AC_CHECK_FUNCS([getcwd getegid geteuid getgid getuid mempcpy munmap \
+ stpcpy strcasecmp strdup strtoul tsearch argz_count argz_stringify \
+ argz_next __fsetlocking])
+
+ dnl Use the *_unlocked functions only if they are declared.
+ dnl (because some of them were defined without being declared in Solaris
+ dnl 2.5.1 but were removed in Solaris 2.6, whereas we want binaries built
+ dnl on Solaris 2.5.1 to run on Solaris 2.6).
+ dnl Don't use AC_CHECK_DECLS because it isn't supported in autoconf-2.13.
+ gt_CHECK_DECL(feof_unlocked, [#include <stdio.h>])
+ gt_CHECK_DECL(fgets_unlocked, [#include <stdio.h>])
+
+ AM_ICONV
+
+ dnl glibc >= 2.4 has a NL_LOCALE_NAME macro when _GNU_SOURCE is defined,
+ dnl and a _NL_LOCALE_NAME macro always.
+ AC_CACHE_CHECK([for NL_LOCALE_NAME macro], gt_cv_nl_locale_name,
+ [AC_TRY_LINK([#include <langinfo.h>
+#include <locale.h>],
+ [char* cs = nl_langinfo(_NL_LOCALE_NAME(LC_MESSAGES));],
+ gt_cv_nl_locale_name=yes,
+ gt_cv_nl_locale_name=no)
+ ])
+ if test $gt_cv_nl_locale_name = yes; then
+ AC_DEFINE(HAVE_NL_LOCALE_NAME, 1,
+ [Define if you have <langinfo.h> and it defines the NL_LOCALE_NAME macro if _GNU_SOURCE is defined.])
+ fi
+
+ dnl intl/plural.c is generated from intl/plural.y. It requires bison,
+ dnl because plural.y uses bison specific features. It requires at least
+ dnl bison-1.26 because earlier versions generate a plural.c that doesn't
+ dnl compile.
+ dnl bison is only needed for the maintainer (who touches plural.y). But in
+ dnl order to avoid separate Makefiles or --enable-maintainer-mode, we put
+ dnl the rule in general Makefile. Now, some people carelessly touch the
+ dnl files or have a broken "make" program, hence the plural.c rule will
+ dnl sometimes fire. To avoid an error, defines BISON to ":" if it is not
+ dnl present or too old.
+ AC_CHECK_PROGS([INTLBISON], [bison])
+ if test -z "$INTLBISON"; then
+ ac_verc_fail=yes
+ else
+ dnl Found it, now check the version.
+ AC_MSG_CHECKING([version of bison])
+changequote(<<,>>)dnl
+ ac_prog_version=`$INTLBISON --version 2>&1 | sed -n 's/^.*GNU Bison.* \([0-9]*\.[0-9.]*\).*$/\1/p'`
+ case $ac_prog_version in
+ '') ac_prog_version="v. ?.??, bad"; ac_verc_fail=yes;;
+ 1.2[6-9]* | 1.[3-9][0-9]* | [2-9].*)
+changequote([,])dnl
+ ac_prog_version="$ac_prog_version, ok"; ac_verc_fail=no;;
+ *) ac_prog_version="$ac_prog_version, bad"; ac_verc_fail=yes;;
+ esac
+ AC_MSG_RESULT([$ac_prog_version])
+ fi
+ if test $ac_verc_fail = yes; then
+ INTLBISON=:
+ fi
+])
+m4trace:m4/intl.m4:244: -1- AC_DEFUN([gt_CHECK_DECL], [
+ AC_CACHE_CHECK([whether $1 is declared], ac_cv_have_decl_$1,
+ [AC_TRY_COMPILE([$2], [
+#ifndef $1
+ char *p = (char *) $1;
+#endif
+], ac_cv_have_decl_$1=yes, ac_cv_have_decl_$1=no)])
+ if test $ac_cv_have_decl_$1 = yes; then
+ gt_value=1
+ else
+ gt_value=0
+ fi
+ AC_DEFINE_UNQUOTED([HAVE_DECL_]translit($1, [a-z], [A-Z]), [$gt_value],
+ [Define to 1 if you have the declaration of `$1', and to 0 if you don't.])
+])
+m4trace:m4/intmax.m4:11: -1- AC_DEFUN([gt_TYPE_INTMAX_T], [
+ AC_REQUIRE([gl_AC_HEADER_INTTYPES_H])
+ AC_REQUIRE([gl_AC_HEADER_STDINT_H])
+ AC_CACHE_CHECK(for intmax_t, gt_cv_c_intmax_t,
+ [AC_TRY_COMPILE([
+#include <stddef.h>
+#include <stdlib.h>
+#if HAVE_STDINT_H_WITH_UINTMAX
+#include <stdint.h>
+#endif
+#if HAVE_INTTYPES_H_WITH_UINTMAX
+#include <inttypes.h>
+#endif
+], [intmax_t x = -1;
+ return !x;],
+ gt_cv_c_intmax_t=yes,
+ gt_cv_c_intmax_t=no)])
+ if test $gt_cv_c_intmax_t = yes; then
+ AC_DEFINE(HAVE_INTMAX_T, 1,
+ [Define if you have the 'intmax_t' type in <stdint.h> or <inttypes.h>.])
+ fi
+])
+m4trace:m4/inttypes-pri.m4:14: -1- AC_DEFUN([gt_INTTYPES_PRI], [
+ AC_CHECK_HEADERS([inttypes.h])
+ if test $ac_cv_header_inttypes_h = yes; then
+ AC_CACHE_CHECK([whether the inttypes.h PRIxNN macros are broken],
+ gt_cv_inttypes_pri_broken,
+ [
+ AC_TRY_COMPILE([#include <inttypes.h>
+#ifdef PRId32
+char *p = PRId32;
+#endif
+], [], gt_cv_inttypes_pri_broken=no, gt_cv_inttypes_pri_broken=yes)
+ ])
+ fi
+ if test "$gt_cv_inttypes_pri_broken" = yes; then
+ AC_DEFINE_UNQUOTED(PRI_MACROS_BROKEN, 1,
+ [Define if <inttypes.h> exists and defines unusable PRI* macros.])
+ PRI_MACROS_BROKEN=1
+ else
+ PRI_MACROS_BROKEN=0
+ fi
+ AC_SUBST([PRI_MACROS_BROKEN])
+])
+m4trace:m4/inttypes_h.m4:12: -1- AC_DEFUN([gl_AC_HEADER_INTTYPES_H], [
+ AC_CACHE_CHECK([for inttypes.h], gl_cv_header_inttypes_h,
+ [AC_TRY_COMPILE(
+ [#include <sys/types.h>
+#include <inttypes.h>],
+ [uintmax_t i = (uintmax_t) -1; return !i;],
+ gl_cv_header_inttypes_h=yes,
+ gl_cv_header_inttypes_h=no)])
+ if test $gl_cv_header_inttypes_h = yes; then
+ AC_DEFINE_UNQUOTED(HAVE_INTTYPES_H_WITH_UINTMAX, 1,
+ [Define if <inttypes.h> exists, doesn't clash with <sys/types.h>,
+ and declares uintmax_t. ])
+ fi
+])
+m4trace:m4/lcmessage.m4:21: -1- AC_DEFUN([gt_LC_MESSAGES], [
+ AC_CACHE_CHECK([for LC_MESSAGES], gt_cv_val_LC_MESSAGES,
+ [AC_TRY_LINK([#include <locale.h>], [return LC_MESSAGES],
+ gt_cv_val_LC_MESSAGES=yes, gt_cv_val_LC_MESSAGES=no)])
+ if test $gt_cv_val_LC_MESSAGES = yes; then
+ AC_DEFINE(HAVE_LC_MESSAGES, 1,
+ [Define if your <locale.h> file defines LC_MESSAGES.])
+ fi
+])
+m4trace:m4/lib-ld.m4:12: -1- AC_DEFUN([AC_LIB_PROG_LD_GNU], [AC_CACHE_CHECK([if the linker ($LD) is GNU ld], acl_cv_prog_gnu_ld,
+[# I'd rather use --version here, but apparently some GNU ld's only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+ acl_cv_prog_gnu_ld=yes ;;
+*)
+ acl_cv_prog_gnu_ld=no ;;
+esac])
+with_gnu_ld=$acl_cv_prog_gnu_ld
+])
+m4trace:m4/lib-ld.m4:25: -1- AC_DEFUN([AC_LIB_PROG_LD], [AC_ARG_WITH(gnu-ld,
+[ --with-gnu-ld assume the C compiler uses GNU ld [default=no]],
+test "$withval" = no || with_gnu_ld=yes, with_gnu_ld=no)
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_CANONICAL_HOST])dnl
+# Prepare PATH_SEPARATOR.
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+ac_prog=ld
+if test "$GCC" = yes; then
+ # Check if gcc -print-prog-name=ld gives a path.
+ AC_MSG_CHECKING([for ld used by GCC])
+ case $host in
+ *-*-mingw*)
+ # gcc leaves a trailing carriage return which upsets mingw
+ ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+ *)
+ ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+ esac
+ case $ac_prog in
+ # Accept absolute paths.
+ [[\\/]* | [A-Za-z]:[\\/]*)]
+ [re_direlt='/[^/][^/]*/\.\./']
+ # Canonicalize the path of ld
+ ac_prog=`echo $ac_prog| sed 's%\\\\%/%g'`
+ while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do
+ ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"`
+ done
+ test -z "$LD" && LD="$ac_prog"
+ ;;
+ "")
+ # If it fails, then pretend we aren't using GCC.
+ ac_prog=ld
+ ;;
+ *)
+ # If it is relative, then search for the first ld in PATH.
+ with_gnu_ld=unknown
+ ;;
+ esac
+elif test "$with_gnu_ld" = yes; then
+ AC_MSG_CHECKING([for GNU ld])
+else
+ AC_MSG_CHECKING([for non-GNU ld])
+fi
+AC_CACHE_VAL(acl_cv_path_LD,
+[if test -z "$LD"; then
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR-:}"
+ for ac_dir in $PATH; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+ acl_cv_path_LD="$ac_dir/$ac_prog"
+ # Check to see if the program is GNU ld. I'd rather use --version,
+ # but apparently some GNU ld's only accept -v.
+ # Break only if it was the GNU/non-GNU ld that we prefer.
+ case `"$acl_cv_path_LD" -v 2>&1 < /dev/null` in
+ *GNU* | *'with BFD'*)
+ test "$with_gnu_ld" != no && break ;;
+ *)
+ test "$with_gnu_ld" != yes && break ;;
+ esac
+ fi
+ done
+ IFS="$ac_save_ifs"
+else
+ acl_cv_path_LD="$LD" # Let the user override the test with a path.
+fi])
+LD="$acl_cv_path_LD"
+if test -n "$LD"; then
+ AC_MSG_RESULT($LD)
+else
+ AC_MSG_RESULT(no)
+fi
+test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH])
+AC_LIB_PROG_LD_GNU
+])
+m4trace:m4/lib-link.m4:15: -1- AC_DEFUN([AC_LIB_LINKFLAGS], [
+ AC_REQUIRE([AC_LIB_PREPARE_PREFIX])
+ AC_REQUIRE([AC_LIB_RPATH])
+ define([Name],[translit([$1],[./-], [___])])
+ define([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-],
+ [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])])
+ AC_CACHE_CHECK([how to link with lib[]$1], [ac_cv_lib[]Name[]_libs], [
+ AC_LIB_LINKFLAGS_BODY([$1], [$2])
+ ac_cv_lib[]Name[]_libs="$LIB[]NAME"
+ ac_cv_lib[]Name[]_ltlibs="$LTLIB[]NAME"
+ ac_cv_lib[]Name[]_cppflags="$INC[]NAME"
+ ])
+ LIB[]NAME="$ac_cv_lib[]Name[]_libs"
+ LTLIB[]NAME="$ac_cv_lib[]Name[]_ltlibs"
+ INC[]NAME="$ac_cv_lib[]Name[]_cppflags"
+ AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME)
+ AC_SUBST([LIB]NAME)
+ AC_SUBST([LTLIB]NAME)
+ dnl Also set HAVE_LIB[]NAME so that AC_LIB_HAVE_LINKFLAGS can reuse the
+ dnl results of this search when this library appears as a dependency.
+ HAVE_LIB[]NAME=yes
+ undefine([Name])
+ undefine([NAME])
+])
+m4trace:m4/lib-link.m4:49: -1- AC_DEFUN([AC_LIB_HAVE_LINKFLAGS], [
+ AC_REQUIRE([AC_LIB_PREPARE_PREFIX])
+ AC_REQUIRE([AC_LIB_RPATH])
+ define([Name],[translit([$1],[./-], [___])])
+ define([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-],
+ [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])])
+
+ dnl Search for lib[]Name and define LIB[]NAME, LTLIB[]NAME and INC[]NAME
+ dnl accordingly.
+ AC_LIB_LINKFLAGS_BODY([$1], [$2])
+
+ dnl Add $INC[]NAME to CPPFLAGS before performing the following checks,
+ dnl because if the user has installed lib[]Name and not disabled its use
+ dnl via --without-lib[]Name-prefix, he wants to use it.
+ ac_save_CPPFLAGS="$CPPFLAGS"
+ AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME)
+
+ AC_CACHE_CHECK([for lib[]$1], [ac_cv_lib[]Name], [
+ ac_save_LIBS="$LIBS"
+ LIBS="$LIBS $LIB[]NAME"
+ AC_TRY_LINK([$3], [$4], [ac_cv_lib[]Name=yes], [ac_cv_lib[]Name=no])
+ LIBS="$ac_save_LIBS"
+ ])
+ if test "$ac_cv_lib[]Name" = yes; then
+ HAVE_LIB[]NAME=yes
+ AC_DEFINE([HAVE_LIB]NAME, 1, [Define if you have the $1 library.])
+ AC_MSG_CHECKING([how to link with lib[]$1])
+ AC_MSG_RESULT([$LIB[]NAME])
+ else
+ HAVE_LIB[]NAME=no
+ dnl If $LIB[]NAME didn't lead to a usable library, we don't need
+ dnl $INC[]NAME either.
+ CPPFLAGS="$ac_save_CPPFLAGS"
+ LIB[]NAME=
+ LTLIB[]NAME=
+ fi
+ AC_SUBST([HAVE_LIB]NAME)
+ AC_SUBST([LIB]NAME)
+ AC_SUBST([LTLIB]NAME)
+ undefine([Name])
+ undefine([NAME])
+])
+m4trace:m4/lib-link.m4:96: -1- AC_DEFUN([AC_LIB_RPATH], [
+ dnl Tell automake >= 1.10 to complain if config.rpath is missing.
+ m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([config.rpath])])
+ AC_REQUIRE([AC_PROG_CC]) dnl we use $CC, $GCC, $LDFLAGS
+ AC_REQUIRE([AC_LIB_PROG_LD]) dnl we use $LD, $with_gnu_ld
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl we use $host
+ AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT]) dnl we use $ac_aux_dir
+ AC_CACHE_CHECK([for shared library run path origin], acl_cv_rpath, [
+ CC="$CC" GCC="$GCC" LDFLAGS="$LDFLAGS" LD="$LD" with_gnu_ld="$with_gnu_ld" \
+ ${CONFIG_SHELL-/bin/sh} "$ac_aux_dir/config.rpath" "$host" > conftest.sh
+ . ./conftest.sh
+ rm -f ./conftest.sh
+ acl_cv_rpath=done
+ ])
+ wl="$acl_cv_wl"
+ libext="$acl_cv_libext"
+ shlibext="$acl_cv_shlibext"
+ hardcode_libdir_flag_spec="$acl_cv_hardcode_libdir_flag_spec"
+ hardcode_libdir_separator="$acl_cv_hardcode_libdir_separator"
+ hardcode_direct="$acl_cv_hardcode_direct"
+ hardcode_minus_L="$acl_cv_hardcode_minus_L"
+ dnl Determine whether the user wants rpath handling at all.
+ AC_ARG_ENABLE(rpath,
+ [ --disable-rpath do not hardcode runtime library paths],
+ :, enable_rpath=yes)
+])
+m4trace:m4/lib-link.m4:127: -1- AC_DEFUN([AC_LIB_LINKFLAGS_BODY], [
+ AC_REQUIRE([AC_LIB_PREPARE_MULTILIB])
+ define([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-],
+ [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])])
+ dnl By default, look in $includedir and $libdir.
+ use_additional=yes
+ AC_LIB_WITH_FINAL_PREFIX([
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+ ])
+ AC_LIB_ARG_WITH([lib$1-prefix],
+[ --with-lib$1-prefix[=DIR] search for lib$1 in DIR/include and DIR/lib
+ --without-lib$1-prefix don't search for lib$1 in includedir and libdir],
+[
+ if test "X$withval" = "Xno"; then
+ use_additional=no
+ else
+ if test "X$withval" = "X"; then
+ AC_LIB_WITH_FINAL_PREFIX([
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+ ])
+ else
+ additional_includedir="$withval/include"
+ additional_libdir="$withval/$acl_libdirstem"
+ fi
+ fi
+])
+ dnl Search the library and its dependencies in $additional_libdir and
+ dnl $LDFLAGS. Using breadth-first-seach.
+ LIB[]NAME=
+ LTLIB[]NAME=
+ INC[]NAME=
+ rpathdirs=
+ ltrpathdirs=
+ names_already_handled=
+ names_next_round='$1 $2'
+ while test -n "$names_next_round"; do
+ names_this_round="$names_next_round"
+ names_next_round=
+ for name in $names_this_round; do
+ already_handled=
+ for n in $names_already_handled; do
+ if test "$n" = "$name"; then
+ already_handled=yes
+ break
+ fi
+ done
+ if test -z "$already_handled"; then
+ names_already_handled="$names_already_handled $name"
+ dnl See if it was already located by an earlier AC_LIB_LINKFLAGS
+ dnl or AC_LIB_HAVE_LINKFLAGS call.
+ uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'`
+ eval value=\"\$HAVE_LIB$uppername\"
+ if test -n "$value"; then
+ if test "$value" = yes; then
+ eval value=\"\$LIB$uppername\"
+ test -z "$value" || LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$value"
+ eval value=\"\$LTLIB$uppername\"
+ test -z "$value" || LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$value"
+ else
+ dnl An earlier call to AC_LIB_HAVE_LINKFLAGS has determined
+ dnl that this library doesn't exist. So just drop it.
+ :
+ fi
+ else
+ dnl Search the library lib$name in $additional_libdir and $LDFLAGS
+ dnl and the already constructed $LIBNAME/$LTLIBNAME.
+ found_dir=
+ found_la=
+ found_so=
+ found_a=
+ if test $use_additional = yes; then
+ if test -n "$shlibext" \
+ && { test -f "$additional_libdir/lib$name.$shlibext" \
+ || { test "$shlibext" = dll \
+ && test -f "$additional_libdir/lib$name.dll.a"; }; }; then
+ found_dir="$additional_libdir"
+ if test -f "$additional_libdir/lib$name.$shlibext"; then
+ found_so="$additional_libdir/lib$name.$shlibext"
+ else
+ found_so="$additional_libdir/lib$name.dll.a"
+ fi
+ if test -f "$additional_libdir/lib$name.la"; then
+ found_la="$additional_libdir/lib$name.la"
+ fi
+ else
+ if test -f "$additional_libdir/lib$name.$libext"; then
+ found_dir="$additional_libdir"
+ found_a="$additional_libdir/lib$name.$libext"
+ if test -f "$additional_libdir/lib$name.la"; then
+ found_la="$additional_libdir/lib$name.la"
+ fi
+ fi
+ fi
+ fi
+ if test "X$found_dir" = "X"; then
+ for x in $LDFLAGS $LTLIB[]NAME; do
+ AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+ case "$x" in
+ -L*)
+ dir=`echo "X$x" | sed -e 's/^X-L//'`
+ if test -n "$shlibext" \
+ && { test -f "$dir/lib$name.$shlibext" \
+ || { test "$shlibext" = dll \
+ && test -f "$dir/lib$name.dll.a"; }; }; then
+ found_dir="$dir"
+ if test -f "$dir/lib$name.$shlibext"; then
+ found_so="$dir/lib$name.$shlibext"
+ else
+ found_so="$dir/lib$name.dll.a"
+ fi
+ if test -f "$dir/lib$name.la"; then
+ found_la="$dir/lib$name.la"
+ fi
+ else
+ if test -f "$dir/lib$name.$libext"; then
+ found_dir="$dir"
+ found_a="$dir/lib$name.$libext"
+ if test -f "$dir/lib$name.la"; then
+ found_la="$dir/lib$name.la"
+ fi
+ fi
+ fi
+ ;;
+ esac
+ if test "X$found_dir" != "X"; then
+ break
+ fi
+ done
+ fi
+ if test "X$found_dir" != "X"; then
+ dnl Found the library.
+ LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$found_dir -l$name"
+ if test "X$found_so" != "X"; then
+ dnl Linking with a shared library. We attempt to hardcode its
+ dnl directory into the executable's runpath, unless it's the
+ dnl standard /usr/lib.
+ if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/$acl_libdirstem"; then
+ dnl No hardcoding is needed.
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so"
+ else
+ dnl Use an explicit option to hardcode DIR into the resulting
+ dnl binary.
+ dnl Potentially add DIR to ltrpathdirs.
+ dnl The ltrpathdirs will be appended to $LTLIBNAME at the end.
+ haveit=
+ for x in $ltrpathdirs; do
+ if test "X$x" = "X$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ ltrpathdirs="$ltrpathdirs $found_dir"
+ fi
+ dnl The hardcoding into $LIBNAME is system dependent.
+ if test "$hardcode_direct" = yes; then
+ dnl Using DIR/libNAME.so during linking hardcodes DIR into the
+ dnl resulting binary.
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so"
+ else
+ if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then
+ dnl Use an explicit option to hardcode DIR into the resulting
+ dnl binary.
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so"
+ dnl Potentially add DIR to rpathdirs.
+ dnl The rpathdirs will be appended to $LIBNAME at the end.
+ haveit=
+ for x in $rpathdirs; do
+ if test "X$x" = "X$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ rpathdirs="$rpathdirs $found_dir"
+ fi
+ else
+ dnl Rely on "-L$found_dir".
+ dnl But don't add it if it's already contained in the LDFLAGS
+ dnl or the already constructed $LIBNAME
+ haveit=
+ for x in $LDFLAGS $LIB[]NAME; do
+ AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+ if test "X$x" = "X-L$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir"
+ fi
+ if test "$hardcode_minus_L" != no; then
+ dnl FIXME: Not sure whether we should use
+ dnl "-L$found_dir -l$name" or "-L$found_dir $found_so"
+ dnl here.
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so"
+ else
+ dnl We cannot use $hardcode_runpath_var and LD_RUN_PATH
+ dnl here, because this doesn't fit in flags passed to the
+ dnl compiler. So give up. No hardcoding. This affects only
+ dnl very old systems.
+ dnl FIXME: Not sure whether we should use
+ dnl "-L$found_dir -l$name" or "-L$found_dir $found_so"
+ dnl here.
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name"
+ fi
+ fi
+ fi
+ fi
+ else
+ if test "X$found_a" != "X"; then
+ dnl Linking with a static library.
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_a"
+ else
+ dnl We shouldn't come here, but anyway it's good to have a
+ dnl fallback.
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir -l$name"
+ fi
+ fi
+ dnl Assume the include files are nearby.
+ additional_includedir=
+ case "$found_dir" in
+ */$acl_libdirstem | */$acl_libdirstem/)
+ basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem/"'*$,,'`
+ additional_includedir="$basedir/include"
+ ;;
+ esac
+ if test "X$additional_includedir" != "X"; then
+ dnl Potentially add $additional_includedir to $INCNAME.
+ dnl But don't add it
+ dnl 1. if it's the standard /usr/include,
+ dnl 2. if it's /usr/local/include and we are using GCC on Linux,
+ dnl 3. if it's already present in $CPPFLAGS or the already
+ dnl constructed $INCNAME,
+ dnl 4. if it doesn't exist as a directory.
+ if test "X$additional_includedir" != "X/usr/include"; then
+ haveit=
+ if test "X$additional_includedir" = "X/usr/local/include"; then
+ if test -n "$GCC"; then
+ case $host_os in
+ linux* | gnu* | k*bsd*-gnu) haveit=yes;;
+ esac
+ fi
+ fi
+ if test -z "$haveit"; then
+ for x in $CPPFLAGS $INC[]NAME; do
+ AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+ if test "X$x" = "X-I$additional_includedir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_includedir"; then
+ dnl Really add $additional_includedir to $INCNAME.
+ INC[]NAME="${INC[]NAME}${INC[]NAME:+ }-I$additional_includedir"
+ fi
+ fi
+ fi
+ fi
+ fi
+ dnl Look for dependencies.
+ if test -n "$found_la"; then
+ dnl Read the .la file. It defines the variables
+ dnl dlname, library_names, old_library, dependency_libs, current,
+ dnl age, revision, installed, dlopen, dlpreopen, libdir.
+ save_libdir="$libdir"
+ case "$found_la" in
+ */* | *\\*) . "$found_la" ;;
+ *) . "./$found_la" ;;
+ esac
+ libdir="$save_libdir"
+ dnl We use only dependency_libs.
+ for dep in $dependency_libs; do
+ case "$dep" in
+ -L*)
+ additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'`
+ dnl Potentially add $additional_libdir to $LIBNAME and $LTLIBNAME.
+ dnl But don't add it
+ dnl 1. if it's the standard /usr/lib,
+ dnl 2. if it's /usr/local/lib and we are using GCC on Linux,
+ dnl 3. if it's already present in $LDFLAGS or the already
+ dnl constructed $LIBNAME,
+ dnl 4. if it doesn't exist as a directory.
+ if test "X$additional_libdir" != "X/usr/$acl_libdirstem"; then
+ haveit=
+ if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem"; then
+ if test -n "$GCC"; then
+ case $host_os in
+ linux* | gnu* | k*bsd*-gnu) haveit=yes;;
+ esac
+ fi
+ fi
+ if test -z "$haveit"; then
+ haveit=
+ for x in $LDFLAGS $LIB[]NAME; do
+ AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+ if test "X$x" = "X-L$additional_libdir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_libdir"; then
+ dnl Really add $additional_libdir to $LIBNAME.
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$additional_libdir"
+ fi
+ fi
+ haveit=
+ for x in $LDFLAGS $LTLIB[]NAME; do
+ AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+ if test "X$x" = "X-L$additional_libdir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_libdir"; then
+ dnl Really add $additional_libdir to $LTLIBNAME.
+ LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$additional_libdir"
+ fi
+ fi
+ fi
+ fi
+ ;;
+ -R*)
+ dir=`echo "X$dep" | sed -e 's/^X-R//'`
+ if test "$enable_rpath" != no; then
+ dnl Potentially add DIR to rpathdirs.
+ dnl The rpathdirs will be appended to $LIBNAME at the end.
+ haveit=
+ for x in $rpathdirs; do
+ if test "X$x" = "X$dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ rpathdirs="$rpathdirs $dir"
+ fi
+ dnl Potentially add DIR to ltrpathdirs.
+ dnl The ltrpathdirs will be appended to $LTLIBNAME at the end.
+ haveit=
+ for x in $ltrpathdirs; do
+ if test "X$x" = "X$dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ ltrpathdirs="$ltrpathdirs $dir"
+ fi
+ fi
+ ;;
+ -l*)
+ dnl Handle this in the next round.
+ names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'`
+ ;;
+ *.la)
+ dnl Handle this in the next round. Throw away the .la's
+ dnl directory; it is already contained in a preceding -L
+ dnl option.
+ names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'`
+ ;;
+ *)
+ dnl Most likely an immediate library name.
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$dep"
+ LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$dep"
+ ;;
+ esac
+ done
+ fi
+ else
+ dnl Didn't find the library; assume it is in the system directories
+ dnl known to the linker and runtime loader. (All the system
+ dnl directories known to the linker should also be known to the
+ dnl runtime loader, otherwise the system is severely misconfigured.)
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name"
+ LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-l$name"
+ fi
+ fi
+ fi
+ done
+ done
+ if test "X$rpathdirs" != "X"; then
+ if test -n "$hardcode_libdir_separator"; then
+ dnl Weird platform: only the last -rpath option counts, the user must
+ dnl pass all path elements in one option. We can arrange that for a
+ dnl single library, but not when more than one $LIBNAMEs are used.
+ alldirs=
+ for found_dir in $rpathdirs; do
+ alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$found_dir"
+ done
+ dnl Note: hardcode_libdir_flag_spec uses $libdir and $wl.
+ acl_save_libdir="$libdir"
+ libdir="$alldirs"
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ libdir="$acl_save_libdir"
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag"
+ else
+ dnl The -rpath options are cumulative.
+ for found_dir in $rpathdirs; do
+ acl_save_libdir="$libdir"
+ libdir="$found_dir"
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ libdir="$acl_save_libdir"
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag"
+ done
+ fi
+ fi
+ if test "X$ltrpathdirs" != "X"; then
+ dnl When using libtool, the option that works for both libraries and
+ dnl executables is -R. The -R options are cumulative.
+ for found_dir in $ltrpathdirs; do
+ LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-R$found_dir"
+ done
+ fi
+])
+m4trace:m4/lib-link.m4:553: -1- AC_DEFUN([AC_LIB_APPENDTOVAR], [
+ for element in [$2]; do
+ haveit=
+ for x in $[$1]; do
+ AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+ if test "X$x" = "X$element"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ [$1]="${[$1]}${[$1]:+ }$element"
+ fi
+ done
+])
+m4trace:m4/lib-link.m4:577: -1- AC_DEFUN([AC_LIB_LINKFLAGS_FROM_LIBS], [
+ AC_REQUIRE([AC_LIB_RPATH])
+ AC_REQUIRE([AC_LIB_PREPARE_MULTILIB])
+ $1=
+ if test "$enable_rpath" != no; then
+ if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then
+ dnl Use an explicit option to hardcode directories into the resulting
+ dnl binary.
+ rpathdirs=
+ next=
+ for opt in $2; do
+ if test -n "$next"; then
+ dir="$next"
+ dnl No need to hardcode the standard /usr/lib.
+ if test "X$dir" != "X/usr/$acl_libdirstem"; then
+ rpathdirs="$rpathdirs $dir"
+ fi
+ next=
+ else
+ case $opt in
+ -L) next=yes ;;
+ -L*) dir=`echo "X$opt" | sed -e 's,^X-L,,'`
+ dnl No need to hardcode the standard /usr/lib.
+ if test "X$dir" != "X/usr/$acl_libdirstem"; then
+ rpathdirs="$rpathdirs $dir"
+ fi
+ next= ;;
+ *) next= ;;
+ esac
+ fi
+ done
+ if test "X$rpathdirs" != "X"; then
+ if test -n ""$3""; then
+ dnl libtool is used for linking. Use -R options.
+ for dir in $rpathdirs; do
+ $1="${$1}${$1:+ }-R$dir"
+ done
+ else
+ dnl The linker is used for linking directly.
+ if test -n "$hardcode_libdir_separator"; then
+ dnl Weird platform: only the last -rpath option counts, the user
+ dnl must pass all path elements in one option.
+ alldirs=
+ for dir in $rpathdirs; do
+ alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$dir"
+ done
+ acl_save_libdir="$libdir"
+ libdir="$alldirs"
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ libdir="$acl_save_libdir"
+ $1="$flag"
+ else
+ dnl The -rpath options are cumulative.
+ for dir in $rpathdirs; do
+ acl_save_libdir="$libdir"
+ libdir="$dir"
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ libdir="$acl_save_libdir"
+ $1="${$1}${$1:+ }$flag"
+ done
+ fi
+ fi
+ fi
+ fi
+ fi
+ AC_SUBST([$1])
+])
+m4trace:m4/lib-prefix.m4:12: -1- AC_DEFUN([AC_LIB_ARG_WITH], [AC_ARG_WITH([$1],[[$2]],[$3],[$4])])
+m4trace:m4/lib-prefix.m4:22: -1- AC_DEFUN([AC_LIB_PREFIX], [
+ AC_BEFORE([$0], [AC_LIB_LINKFLAGS])
+ AC_REQUIRE([AC_PROG_CC])
+ AC_REQUIRE([AC_CANONICAL_HOST])
+ AC_REQUIRE([AC_LIB_PREPARE_MULTILIB])
+ AC_REQUIRE([AC_LIB_PREPARE_PREFIX])
+ dnl By default, look in $includedir and $libdir.
+ use_additional=yes
+ AC_LIB_WITH_FINAL_PREFIX([
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+ ])
+ AC_LIB_ARG_WITH([lib-prefix],
+[ --with-lib-prefix[=DIR] search for libraries in DIR/include and DIR/lib
+ --without-lib-prefix don't search for libraries in includedir and libdir],
+[
+ if test "X$withval" = "Xno"; then
+ use_additional=no
+ else
+ if test "X$withval" = "X"; then
+ AC_LIB_WITH_FINAL_PREFIX([
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+ ])
+ else
+ additional_includedir="$withval/include"
+ additional_libdir="$withval/$acl_libdirstem"
+ fi
+ fi
+])
+ if test $use_additional = yes; then
+ dnl Potentially add $additional_includedir to $CPPFLAGS.
+ dnl But don't add it
+ dnl 1. if it's the standard /usr/include,
+ dnl 2. if it's already present in $CPPFLAGS,
+ dnl 3. if it's /usr/local/include and we are using GCC on Linux,
+ dnl 4. if it doesn't exist as a directory.
+ if test "X$additional_includedir" != "X/usr/include"; then
+ haveit=
+ for x in $CPPFLAGS; do
+ AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+ if test "X$x" = "X-I$additional_includedir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test "X$additional_includedir" = "X/usr/local/include"; then
+ if test -n "$GCC"; then
+ case $host_os in
+ linux* | gnu* | k*bsd*-gnu) haveit=yes;;
+ esac
+ fi
+ fi
+ if test -z "$haveit"; then
+ if test -d "$additional_includedir"; then
+ dnl Really add $additional_includedir to $CPPFLAGS.
+ CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }-I$additional_includedir"
+ fi
+ fi
+ fi
+ fi
+ dnl Potentially add $additional_libdir to $LDFLAGS.
+ dnl But don't add it
+ dnl 1. if it's the standard /usr/lib,
+ dnl 2. if it's already present in $LDFLAGS,
+ dnl 3. if it's /usr/local/lib and we are using GCC on Linux,
+ dnl 4. if it doesn't exist as a directory.
+ if test "X$additional_libdir" != "X/usr/$acl_libdirstem"; then
+ haveit=
+ for x in $LDFLAGS; do
+ AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+ if test "X$x" = "X-L$additional_libdir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem"; then
+ if test -n "$GCC"; then
+ case $host_os in
+ linux*) haveit=yes;;
+ esac
+ fi
+ fi
+ if test -z "$haveit"; then
+ if test -d "$additional_libdir"; then
+ dnl Really add $additional_libdir to $LDFLAGS.
+ LDFLAGS="${LDFLAGS}${LDFLAGS:+ }-L$additional_libdir"
+ fi
+ fi
+ fi
+ fi
+ fi
+])
+m4trace:m4/lib-prefix.m4:122: -1- AC_DEFUN([AC_LIB_PREPARE_PREFIX], [
+ dnl Unfortunately, prefix and exec_prefix get only finally determined
+ dnl at the end of configure.
+ if test "X$prefix" = "XNONE"; then
+ acl_final_prefix="$ac_default_prefix"
+ else
+ acl_final_prefix="$prefix"
+ fi
+ if test "X$exec_prefix" = "XNONE"; then
+ acl_final_exec_prefix='${prefix}'
+ else
+ acl_final_exec_prefix="$exec_prefix"
+ fi
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ eval acl_final_exec_prefix=\"$acl_final_exec_prefix\"
+ prefix="$acl_save_prefix"
+])
+m4trace:m4/lib-prefix.m4:145: -1- AC_DEFUN([AC_LIB_WITH_FINAL_PREFIX], [
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ $1
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+])
+m4trace:m4/lib-prefix.m4:158: -1- AC_DEFUN([AC_LIB_PREPARE_MULTILIB], [
+ dnl There is no formal standard regarding lib and lib64. The current
+ dnl practice is that on a system supporting 32-bit and 64-bit instruction
+ dnl sets or ABIs, 64-bit libraries go under $prefix/lib64 and 32-bit
+ dnl libraries go under $prefix/lib. We determine the compiler's default
+ dnl mode by looking at the compiler's library search path. If at least
+ dnl of its elements ends in /lib64 or points to a directory whose absolute
+ dnl pathname ends in /lib64, we assume a 64-bit ABI. Otherwise we use the
+ dnl default, namely "lib".
+ acl_libdirstem=lib
+ searchpath=`(LC_ALL=C $CC -print-search-dirs) 2>/dev/null | sed -n -e 's,^libraries: ,,p' | sed -e 's,^=,,'`
+ if test -n "$searchpath"; then
+ acl_save_IFS="${IFS= }"; IFS=":"
+ for searchdir in $searchpath; do
+ if test -d "$searchdir"; then
+ case "$searchdir" in
+ */lib64/ | */lib64 ) acl_libdirstem=lib64 ;;
+ *) searchdir=`cd "$searchdir" && pwd`
+ case "$searchdir" in
+ */lib64 ) acl_libdirstem=lib64 ;;
+ esac ;;
+ esac
+ fi
+ done
+ IFS="$acl_save_IFS"
+ fi
+])
+m4trace:m4/lock.m4:22: -1- AC_DEFUN([gl_LOCK_EARLY], [
+ AC_REQUIRE([gl_LOCK_EARLY_BODY])
+])
+m4trace:m4/lock.m4:29: -1- AC_DEFUN([gl_LOCK_EARLY_BODY], [
+ dnl Ordering constraints: This macro modifies CPPFLAGS in a way that
+ dnl influences the result of the autoconf tests that test for *_unlocked
+ dnl declarations, on AIX 5 at least. Therefore it must come early.
+ AC_BEFORE([$0], [gl_FUNC_GLIBC_UNLOCKED_IO])dnl
+ AC_BEFORE([$0], [gl_ARGP])dnl
+
+ AC_REQUIRE([AC_CANONICAL_HOST])
+ AC_REQUIRE([AC_GNU_SOURCE]) dnl needed for pthread_rwlock_t on glibc systems
+ dnl Check for multithreading.
+ AC_ARG_ENABLE(threads,
+AC_HELP_STRING([--enable-threads={posix|solaris|pth|win32}], [specify multithreading API])
+AC_HELP_STRING([--disable-threads], [build without multithread safety]),
+ [gl_use_threads=$enableval],
+ [case "$host_os" in
+ dnl Disable multithreading by default on OSF/1, because it interferes
+ dnl with fork()/exec(): When msgexec is linked with -lpthread, its child
+ dnl process gets an endless segmentation fault inside execvp().
+ osf*) gl_use_threads=no ;;
+ *) gl_use_threads=yes ;;
+ esac
+ ])
+ if test "$gl_use_threads" = yes || test "$gl_use_threads" = posix; then
+ # For using <pthread.h>:
+ case "$host_os" in
+ osf*)
+ # On OSF/1, the compiler needs the flag -D_REENTRANT so that it
+ # groks <pthread.h>. cc also understands the flag -pthread, but
+ # we don't use it because 1. gcc-2.95 doesn't understand -pthread,
+ # 2. putting a flag into CPPFLAGS that has an effect on the linker
+ # causes the AC_TRY_LINK test below to succeed unexpectedly,
+ # leading to wrong values of LIBTHREAD and LTLIBTHREAD.
+ CPPFLAGS="$CPPFLAGS -D_REENTRANT"
+ ;;
+ esac
+ # Some systems optimize for single-threaded programs by default, and
+ # need special flags to disable these optimizations. For example, the
+ # definition of 'errno' in <errno.h>.
+ case "$host_os" in
+ aix* | freebsd*) CPPFLAGS="$CPPFLAGS -D_THREAD_SAFE" ;;
+ solaris*) CPPFLAGS="$CPPFLAGS -D_REENTRANT" ;;
+ esac
+ fi
+])
+m4trace:m4/lock.m4:77: -1- AC_DEFUN([gl_LOCK_BODY], [
+ AC_REQUIRE([gl_LOCK_EARLY_BODY])
+ gl_threads_api=none
+ LIBTHREAD=
+ LTLIBTHREAD=
+ LIBMULTITHREAD=
+ LTLIBMULTITHREAD=
+ if test "$gl_use_threads" != no; then
+ dnl Check whether the compiler and linker support weak declarations.
+ AC_MSG_CHECKING([whether imported symbols can be declared weak])
+ gl_have_weak=no
+ AC_TRY_LINK([extern void xyzzy ();
+#pragma weak xyzzy], [xyzzy();], [gl_have_weak=yes])
+ AC_MSG_RESULT([$gl_have_weak])
+ if test "$gl_use_threads" = yes || test "$gl_use_threads" = posix; then
+ # On OSF/1, the compiler needs the flag -pthread or -D_REENTRANT so that
+ # it groks <pthread.h>. It's added above, in gl_LOCK_EARLY_BODY.
+ AC_CHECK_HEADER(pthread.h, gl_have_pthread_h=yes, gl_have_pthread_h=no)
+ if test "$gl_have_pthread_h" = yes; then
+ # Other possible tests:
+ # -lpthreads (FSU threads, PCthreads)
+ # -lgthreads
+ gl_have_pthread=
+ # Test whether both pthread_mutex_lock and pthread_mutexattr_init exist
+ # in libc. IRIX 6.5 has the first one in both libc and libpthread, but
+ # the second one only in libpthread, and lock.c needs it.
+ AC_TRY_LINK([#include <pthread.h>],
+ [pthread_mutex_lock((pthread_mutex_t*)0);
+ pthread_mutexattr_init((pthread_mutexattr_t*)0);],
+ [gl_have_pthread=yes])
+ # Test for libpthread by looking for pthread_kill. (Not pthread_self,
+ # since it is defined as a macro on OSF/1.)
+ if test -n "$gl_have_pthread"; then
+ # The program links fine without libpthread. But it may actually
+ # need to link with libpthread in order to create multiple threads.
+ AC_CHECK_LIB(pthread, pthread_kill,
+ [LIBMULTITHREAD=-lpthread LTLIBMULTITHREAD=-lpthread
+ # On Solaris and HP-UX, most pthread functions exist also in libc.
+ # Therefore pthread_in_use() needs to actually try to create a
+ # thread: pthread_create from libc will fail, whereas
+ # pthread_create will actually create a thread.
+ case "$host_os" in
+ solaris* | hpux*)
+ AC_DEFINE([PTHREAD_IN_USE_DETECTION_HARD], 1,
+ [Define if the pthread_in_use() detection is hard.])
+ esac
+ ])
+ else
+ # Some library is needed. Try libpthread and libc_r.
+ AC_CHECK_LIB(pthread, pthread_kill,
+ [gl_have_pthread=yes
+ LIBTHREAD=-lpthread LTLIBTHREAD=-lpthread
+ LIBMULTITHREAD=-lpthread LTLIBMULTITHREAD=-lpthread])
+ if test -z "$gl_have_pthread"; then
+ # For FreeBSD 4.
+ AC_CHECK_LIB(c_r, pthread_kill,
+ [gl_have_pthread=yes
+ LIBTHREAD=-lc_r LTLIBTHREAD=-lc_r
+ LIBMULTITHREAD=-lc_r LTLIBMULTITHREAD=-lc_r])
+ fi
+ fi
+ if test -n "$gl_have_pthread"; then
+ gl_threads_api=posix
+ AC_DEFINE([USE_POSIX_THREADS], 1,
+ [Define if the POSIX multithreading library can be used.])
+ if test -n "$LIBMULTITHREAD" || test -n "$LTLIBMULTITHREAD"; then
+ if test $gl_have_weak = yes; then
+ AC_DEFINE([USE_POSIX_THREADS_WEAK], 1,
+ [Define if references to the POSIX multithreading library should be made weak.])
+ LIBTHREAD=
+ LTLIBTHREAD=
+ fi
+ fi
+ # OSF/1 4.0 and MacOS X 10.1 lack the pthread_rwlock_t type and the
+ # pthread_rwlock_* functions.
+ AC_CHECK_TYPE([pthread_rwlock_t],
+ [AC_DEFINE([HAVE_PTHREAD_RWLOCK], 1,
+ [Define if the POSIX multithreading library has read/write locks.])],
+ [],
+ [#include <pthread.h>])
+ # glibc defines PTHREAD_MUTEX_RECURSIVE as enum, not as a macro.
+ AC_TRY_COMPILE([#include <pthread.h>],
+ [#if __FreeBSD__ == 4
+error "No, in FreeBSD 4.0 recursive mutexes actually don't work."
+#else
+int x = (int)PTHREAD_MUTEX_RECURSIVE;
+return !x;
+#endif],
+ [AC_DEFINE([HAVE_PTHREAD_MUTEX_RECURSIVE], 1,
+ [Define if the <pthread.h> defines PTHREAD_MUTEX_RECURSIVE.])])
+ fi
+ fi
+ fi
+ if test -z "$gl_have_pthread"; then
+ if test "$gl_use_threads" = yes || test "$gl_use_threads" = solaris; then
+ gl_have_solaristhread=
+ gl_save_LIBS="$LIBS"
+ LIBS="$LIBS -lthread"
+ AC_TRY_LINK([#include <thread.h>
+#include <synch.h>],
+ [thr_self();],
+ [gl_have_solaristhread=yes])
+ LIBS="$gl_save_LIBS"
+ if test -n "$gl_have_solaristhread"; then
+ gl_threads_api=solaris
+ LIBTHREAD=-lthread
+ LTLIBTHREAD=-lthread
+ LIBMULTITHREAD="$LIBTHREAD"
+ LTLIBMULTITHREAD="$LTLIBTHREAD"
+ AC_DEFINE([USE_SOLARIS_THREADS], 1,
+ [Define if the old Solaris multithreading library can be used.])
+ if test $gl_have_weak = yes; then
+ AC_DEFINE([USE_SOLARIS_THREADS_WEAK], 1,
+ [Define if references to the old Solaris multithreading library should be made weak.])
+ LIBTHREAD=
+ LTLIBTHREAD=
+ fi
+ fi
+ fi
+ fi
+ if test "$gl_use_threads" = pth; then
+ gl_save_CPPFLAGS="$CPPFLAGS"
+ AC_LIB_LINKFLAGS(pth)
+ gl_have_pth=
+ gl_save_LIBS="$LIBS"
+ LIBS="$LIBS -lpth"
+ AC_TRY_LINK([#include <pth.h>], [pth_self();], gl_have_pth=yes)
+ LIBS="$gl_save_LIBS"
+ if test -n "$gl_have_pth"; then
+ gl_threads_api=pth
+ LIBTHREAD="$LIBPTH"
+ LTLIBTHREAD="$LTLIBPTH"
+ LIBMULTITHREAD="$LIBTHREAD"
+ LTLIBMULTITHREAD="$LTLIBTHREAD"
+ AC_DEFINE([USE_PTH_THREADS], 1,
+ [Define if the GNU Pth multithreading library can be used.])
+ if test -n "$LIBMULTITHREAD" || test -n "$LTLIBMULTITHREAD"; then
+ if test $gl_have_weak = yes; then
+ AC_DEFINE([USE_PTH_THREADS_WEAK], 1,
+ [Define if references to the GNU Pth multithreading library should be made weak.])
+ LIBTHREAD=
+ LTLIBTHREAD=
+ fi
+ fi
+ else
+ CPPFLAGS="$gl_save_CPPFLAGS"
+ fi
+ fi
+ if test -z "$gl_have_pthread"; then
+ if test "$gl_use_threads" = yes || test "$gl_use_threads" = win32; then
+ if { case "$host_os" in
+ mingw*) true;;
+ *) false;;
+ esac
+ }; then
+ gl_threads_api=win32
+ AC_DEFINE([USE_WIN32_THREADS], 1,
+ [Define if the Win32 multithreading API can be used.])
+ fi
+ fi
+ fi
+ fi
+ AC_MSG_CHECKING([for multithread API to use])
+ AC_MSG_RESULT([$gl_threads_api])
+ AC_SUBST(LIBTHREAD)
+ AC_SUBST(LTLIBTHREAD)
+ AC_SUBST(LIBMULTITHREAD)
+ AC_SUBST(LTLIBMULTITHREAD)
+])
+m4trace:m4/lock.m4:248: -1- AC_DEFUN([gl_LOCK], [
+ AC_REQUIRE([gl_LOCK_EARLY])
+ AC_REQUIRE([gl_LOCK_BODY])
+ gl_PREREQ_LOCK
+])
+m4trace:m4/lock.m4:256: -1- AC_DEFUN([gl_PREREQ_LOCK], [
+ AC_REQUIRE([AC_C_INLINE])
+])
+m4trace:m4/longdouble.m4:14: -1- AC_DEFUN([gt_TYPE_LONGDOUBLE], [
+ AC_CACHE_CHECK([for long double], gt_cv_c_long_double,
+ [if test "$GCC" = yes; then
+ gt_cv_c_long_double=yes
+ else
+ AC_TRY_COMPILE([
+ /* The Stardent Vistra knows sizeof(long double), but does not support it. */
+ long double foo = 0.0;
+ /* On Ultrix 4.3 cc, long double is 4 and double is 8. */
+ int array [2*(sizeof(long double) >= sizeof(double)) - 1];
+ ], ,
+ gt_cv_c_long_double=yes, gt_cv_c_long_double=no)
+ fi])
+ if test $gt_cv_c_long_double = yes; then
+ AC_DEFINE(HAVE_LONG_DOUBLE, 1, [Define if you have the 'long double' type.])
+ fi
+])
+m4trace:m4/longlong.m4:17: -1- AC_DEFUN([AC_TYPE_LONG_LONG_INT], [
+ AC_CACHE_CHECK([for long long int], [ac_cv_type_long_long_int],
+ [AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[long long int ll = 9223372036854775807ll;
+ long long int nll = -9223372036854775807LL;
+ typedef int a[((-9223372036854775807LL < 0
+ && 0 < 9223372036854775807ll)
+ ? 1 : -1)];
+ int i = 63;]],
+ [[long long int llmax = 9223372036854775807ll;
+ return ((ll << 63) | (ll >> 63) | (ll < i) | (ll > i)
+ | (llmax / ll) | (llmax % ll));]])],
+ [ac_cv_type_long_long_int=yes],
+ [ac_cv_type_long_long_int=no])])
+ if test $ac_cv_type_long_long_int = yes; then
+ AC_DEFINE([HAVE_LONG_LONG_INT], 1,
+ [Define to 1 if the system has the type `long long int'.])
+ fi
+])
+m4trace:m4/longlong.m4:40: -1- AC_DEFUN([gl_AC_TYPE_LONG_LONG], [
+ AC_REQUIRE([AC_TYPE_LONG_LONG_INT])
+ ac_cv_type_long_long=$ac_cv_type_long_long_int
+ if test $ac_cv_type_long_long = yes; then
+ AC_DEFINE(HAVE_LONG_LONG, 1,
+ [Define if you have the 'long long' type.])
+ fi
+])
+m4trace:m4/nls.m4:22: -1- AC_DEFUN([AM_NLS], [
+ AC_MSG_CHECKING([whether NLS is requested])
+ dnl Default is enabled NLS
+ AC_ARG_ENABLE(nls,
+ [ --disable-nls do not use Native Language Support],
+ USE_NLS=$enableval, USE_NLS=yes)
+ AC_MSG_RESULT($USE_NLS)
+ AC_SUBST(USE_NLS)
+])
+m4trace:m4/po.m4:23: -1- AC_DEFUN([AM_PO_SUBDIRS], [
+ AC_REQUIRE([AC_PROG_MAKE_SET])dnl
+ AC_REQUIRE([AC_PROG_INSTALL])dnl
+ AC_REQUIRE([AM_PROG_MKDIR_P])dnl defined by automake
+ AC_REQUIRE([AM_NLS])dnl
+
+ dnl Perform the following tests also if --disable-nls has been given,
+ dnl because they are needed for "make dist" to work.
+
+ dnl Search for GNU msgfmt in the PATH.
+ dnl The first test excludes Solaris msgfmt and early GNU msgfmt versions.
+ dnl The second test excludes FreeBSD msgfmt.
+ AM_PATH_PROG_WITH_TEST(MSGFMT, msgfmt,
+ [$ac_dir/$ac_word --statistics /dev/null >&]AS_MESSAGE_LOG_FD[ 2>&1 &&
+ (if $ac_dir/$ac_word --statistics /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi)],
+ :)
+ AC_PATH_PROG(GMSGFMT, gmsgfmt, $MSGFMT)
+
+ dnl Test whether it is GNU msgfmt >= 0.15.
+changequote(,)dnl
+ case `$MSGFMT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in
+ '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) MSGFMT_015=: ;;
+ *) MSGFMT_015=$MSGFMT ;;
+ esac
+changequote([,])dnl
+ AC_SUBST([MSGFMT_015])
+changequote(,)dnl
+ case `$GMSGFMT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in
+ '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) GMSGFMT_015=: ;;
+ *) GMSGFMT_015=$GMSGFMT ;;
+ esac
+changequote([,])dnl
+ AC_SUBST([GMSGFMT_015])
+
+ dnl Search for GNU xgettext 0.12 or newer in the PATH.
+ dnl The first test excludes Solaris xgettext and early GNU xgettext versions.
+ dnl The second test excludes FreeBSD xgettext.
+ AM_PATH_PROG_WITH_TEST(XGETTEXT, xgettext,
+ [$ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= /dev/null >&]AS_MESSAGE_LOG_FD[ 2>&1 &&
+ (if $ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi)],
+ :)
+ dnl Remove leftover from FreeBSD xgettext call.
+ rm -f messages.po
+
+ dnl Test whether it is GNU xgettext >= 0.15.
+changequote(,)dnl
+ case `$XGETTEXT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in
+ '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) XGETTEXT_015=: ;;
+ *) XGETTEXT_015=$XGETTEXT ;;
+ esac
+changequote([,])dnl
+ AC_SUBST([XGETTEXT_015])
+
+ dnl Search for GNU msgmerge 0.11 or newer in the PATH.
+ AM_PATH_PROG_WITH_TEST(MSGMERGE, msgmerge,
+ [$ac_dir/$ac_word --update -q /dev/null /dev/null >&]AS_MESSAGE_LOG_FD[ 2>&1], :)
+
+ dnl Installation directories.
+ dnl Autoconf >= 2.60 defines localedir. For older versions of autoconf, we
+ dnl have to define it here, so that it can be used in po/Makefile.
+ test -n "$localedir" || localedir='${datadir}/locale'
+ AC_SUBST([localedir])
+
+ AC_CONFIG_COMMANDS([po-directories], [[
+ for ac_file in $CONFIG_FILES; do
+ # Support "outfile[:infile[:infile...]]"
+ case "$ac_file" in
+ *:*) ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ esac
+ # PO directories have a Makefile.in generated from Makefile.in.in.
+ case "$ac_file" in */Makefile.in)
+ # Adjust a relative srcdir.
+ ac_dir=`echo "$ac_file"|sed 's%/[^/][^/]*$%%'`
+ ac_dir_suffix="/`echo "$ac_dir"|sed 's%^\./%%'`"
+ ac_dots=`echo "$ac_dir_suffix"|sed 's%/[^/]*%../%g'`
+ # In autoconf-2.13 it is called $ac_given_srcdir.
+ # In autoconf-2.50 it is called $srcdir.
+ test -n "$ac_given_srcdir" || ac_given_srcdir="$srcdir"
+ case "$ac_given_srcdir" in
+ .) top_srcdir=`echo $ac_dots|sed 's%/$%%'` ;;
+ /*) top_srcdir="$ac_given_srcdir" ;;
+ *) top_srcdir="$ac_dots$ac_given_srcdir" ;;
+ esac
+ # Treat a directory as a PO directory if and only if it has a
+ # POTFILES.in file. This allows packages to have multiple PO
+ # directories under different names or in different locations.
+ if test -f "$ac_given_srcdir/$ac_dir/POTFILES.in"; then
+ rm -f "$ac_dir/POTFILES"
+ test -n "$as_me" && echo "$as_me: creating $ac_dir/POTFILES" || echo "creating $ac_dir/POTFILES"
+ cat "$ac_given_srcdir/$ac_dir/POTFILES.in" | sed -e "/^#/d" -e "/^[ ]*\$/d" -e "s,.*, $top_srcdir/& \\\\," | sed -e "\$s/\(.*\) \\\\/\1/" > "$ac_dir/POTFILES"
+ POMAKEFILEDEPS="POTFILES.in"
+ # ALL_LINGUAS, POFILES, UPDATEPOFILES, DUMMYPOFILES, GMOFILES depend
+ # on $ac_dir but don't depend on user-specified configuration
+ # parameters.
+ if test -f "$ac_given_srcdir/$ac_dir/LINGUAS"; then
+ # The LINGUAS file contains the set of available languages.
+ if test -n "$OBSOLETE_ALL_LINGUAS"; then
+ test -n "$as_me" && echo "$as_me: setting ALL_LINGUAS in configure.in is obsolete" || echo "setting ALL_LINGUAS in configure.in is obsolete"
+ fi
+ ALL_LINGUAS_=`sed -e "/^#/d" -e "s/#.*//" "$ac_given_srcdir/$ac_dir/LINGUAS"`
+ # Hide the ALL_LINGUAS assigment from automake < 1.5.
+ eval 'ALL_LINGUAS''=$ALL_LINGUAS_'
+ POMAKEFILEDEPS="$POMAKEFILEDEPS LINGUAS"
+ else
+ # The set of available languages was given in configure.in.
+ # Hide the ALL_LINGUAS assigment from automake < 1.5.
+ eval 'ALL_LINGUAS''=$OBSOLETE_ALL_LINGUAS'
+ fi
+ # Compute POFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).po)
+ # Compute UPDATEPOFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(lang).po-update)
+ # Compute DUMMYPOFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(lang).nop)
+ # Compute GMOFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).gmo)
+ case "$ac_given_srcdir" in
+ .) srcdirpre= ;;
+ *) srcdirpre='$(srcdir)/' ;;
+ esac
+ POFILES=
+ UPDATEPOFILES=
+ DUMMYPOFILES=
+ GMOFILES=
+ for lang in $ALL_LINGUAS; do
+ POFILES="$POFILES $srcdirpre$lang.po"
+ UPDATEPOFILES="$UPDATEPOFILES $lang.po-update"
+ DUMMYPOFILES="$DUMMYPOFILES $lang.nop"
+ GMOFILES="$GMOFILES $srcdirpre$lang.gmo"
+ done
+ # CATALOGS depends on both $ac_dir and the user's LINGUAS
+ # environment variable.
+ INST_LINGUAS=
+ if test -n "$ALL_LINGUAS"; then
+ for presentlang in $ALL_LINGUAS; do
+ useit=no
+ if test "%UNSET%" != "$LINGUAS"; then
+ desiredlanguages="$LINGUAS"
+ else
+ desiredlanguages="$ALL_LINGUAS"
+ fi
+ for desiredlang in $desiredlanguages; do
+ # Use the presentlang catalog if desiredlang is
+ # a. equal to presentlang, or
+ # b. a variant of presentlang (because in this case,
+ # presentlang can be used as a fallback for messages
+ # which are not translated in the desiredlang catalog).
+ case "$desiredlang" in
+ "$presentlang"*) useit=yes;;
+ esac
+ done
+ if test $useit = yes; then
+ INST_LINGUAS="$INST_LINGUAS $presentlang"
+ fi
+ done
+ fi
+ CATALOGS=
+ if test -n "$INST_LINGUAS"; then
+ for lang in $INST_LINGUAS; do
+ CATALOGS="$CATALOGS $lang.gmo"
+ done
+ fi
+ test -n "$as_me" && echo "$as_me: creating $ac_dir/Makefile" || echo "creating $ac_dir/Makefile"
+ sed -e "/^POTFILES =/r $ac_dir/POTFILES" -e "/^# Makevars/r $ac_given_srcdir/$ac_dir/Makevars" -e "s|@POFILES@|$POFILES|g" -e "s|@UPDATEPOFILES@|$UPDATEPOFILES|g" -e "s|@DUMMYPOFILES@|$DUMMYPOFILES|g" -e "s|@GMOFILES@|$GMOFILES|g" -e "s|@CATALOGS@|$CATALOGS|g" -e "s|@POMAKEFILEDEPS@|$POMAKEFILEDEPS|g" "$ac_dir/Makefile.in" > "$ac_dir/Makefile"
+ for f in "$ac_given_srcdir/$ac_dir"/Rules-*; do
+ if test -f "$f"; then
+ case "$f" in
+ *.orig | *.bak | *~) ;;
+ *) cat "$f" >> "$ac_dir/Makefile" ;;
+ esac
+ fi
+ done
+ fi
+ ;;
+ esac
+ done]],
+ [# Capture the value of obsolete ALL_LINGUAS because we need it to compute
+ # POFILES, UPDATEPOFILES, DUMMYPOFILES, GMOFILES, CATALOGS. But hide it
+ # from automake < 1.5.
+ eval 'OBSOLETE_ALL_LINGUAS''="$ALL_LINGUAS"'
+ # Capture the value of LINGUAS because we need it to compute CATALOGS.
+ LINGUAS="${LINGUAS-%UNSET%}"
+ ])
+])
+m4trace:m4/po.m4:210: -1- AC_DEFUN([AM_POSTPROCESS_PO_MAKEFILE], [
+ # When this code is run, in config.status, two variables have already been
+ # set:
+ # - OBSOLETE_ALL_LINGUAS is the value of LINGUAS set in configure.in,
+ # - LINGUAS is the value of the environment variable LINGUAS at configure
+ # time.
+
+changequote(,)dnl
+ # Adjust a relative srcdir.
+ ac_dir=`echo "$ac_file"|sed 's%/[^/][^/]*$%%'`
+ ac_dir_suffix="/`echo "$ac_dir"|sed 's%^\./%%'`"
+ ac_dots=`echo "$ac_dir_suffix"|sed 's%/[^/]*%../%g'`
+ # In autoconf-2.13 it is called $ac_given_srcdir.
+ # In autoconf-2.50 it is called $srcdir.
+ test -n "$ac_given_srcdir" || ac_given_srcdir="$srcdir"
+ case "$ac_given_srcdir" in
+ .) top_srcdir=`echo $ac_dots|sed 's%/$%%'` ;;
+ /*) top_srcdir="$ac_given_srcdir" ;;
+ *) top_srcdir="$ac_dots$ac_given_srcdir" ;;
+ esac
+
+ # Find a way to echo strings without interpreting backslash.
+ if test "X`(echo '\t') 2>/dev/null`" = 'X\t'; then
+ gt_echo='echo'
+ else
+ if test "X`(printf '%s\n' '\t') 2>/dev/null`" = 'X\t'; then
+ gt_echo='printf %s\n'
+ else
+ echo_func () {
+ cat <<EOT
+$*
+EOT
+ }
+ gt_echo='echo_func'
+ fi
+ fi
+
+ # A sed script that extracts the value of VARIABLE from a Makefile.
+ sed_x_variable='
+# Test if the hold space is empty.
+x
+s/P/P/
+x
+ta
+# Yes it was empty. Look if we have the expected variable definition.
+/^[ ]*VARIABLE[ ]*=/{
+ # Seen the first line of the variable definition.
+ s/^[ ]*VARIABLE[ ]*=//
+ ba
+}
+bd
+:a
+# Here we are processing a line from the variable definition.
+# Remove comment, more precisely replace it with a space.
+s/#.*$/ /
+# See if the line ends in a backslash.
+tb
+:b
+s/\\$//
+# Print the line, without the trailing backslash.
+p
+tc
+# There was no trailing backslash. The end of the variable definition is
+# reached. Clear the hold space.
+s/^.*$//
+x
+bd
+:c
+# A trailing backslash means that the variable definition continues in the
+# next line. Put a nonempty string into the hold space to indicate this.
+s/^.*$/P/
+x
+:d
+'
+changequote([,])dnl
+
+ # Set POTFILES to the value of the Makefile variable POTFILES.
+ sed_x_POTFILES=`$gt_echo "$sed_x_variable" | sed -e '/^ *#/d' -e 's/VARIABLE/POTFILES/g'`
+ POTFILES=`sed -n -e "$sed_x_POTFILES" < "$ac_file"`
+ # Compute POTFILES_DEPS as
+ # $(foreach file, $(POTFILES), $(top_srcdir)/$(file))
+ POTFILES_DEPS=
+ for file in $POTFILES; do
+ POTFILES_DEPS="$POTFILES_DEPS "'$(top_srcdir)/'"$file"
+ done
+ POMAKEFILEDEPS=""
+
+ if test -n "$OBSOLETE_ALL_LINGUAS"; then
+ test -n "$as_me" && echo "$as_me: setting ALL_LINGUAS in configure.in is obsolete" || echo "setting ALL_LINGUAS in configure.in is obsolete"
+ fi
+ if test -f "$ac_given_srcdir/$ac_dir/LINGUAS"; then
+ # The LINGUAS file contains the set of available languages.
+ ALL_LINGUAS_=`sed -e "/^#/d" -e "s/#.*//" "$ac_given_srcdir/$ac_dir/LINGUAS"`
+ POMAKEFILEDEPS="$POMAKEFILEDEPS LINGUAS"
+ else
+ # Set ALL_LINGUAS to the value of the Makefile variable LINGUAS.
+ sed_x_LINGUAS=`$gt_echo "$sed_x_variable" | sed -e '/^ *#/d' -e 's/VARIABLE/LINGUAS/g'`
+ ALL_LINGUAS_=`sed -n -e "$sed_x_LINGUAS" < "$ac_file"`
+ fi
+ # Hide the ALL_LINGUAS assigment from automake < 1.5.
+ eval 'ALL_LINGUAS''=$ALL_LINGUAS_'
+ # Compute POFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).po)
+ # Compute UPDATEPOFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(lang).po-update)
+ # Compute DUMMYPOFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(lang).nop)
+ # Compute GMOFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).gmo)
+ # Compute PROPERTIESFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(top_srcdir)/$(DOMAIN)_$(lang).properties)
+ # Compute CLASSFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(top_srcdir)/$(DOMAIN)_$(lang).class)
+ # Compute QMFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).qm)
+ # Compute MSGFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(frob $(lang)).msg)
+ # Compute RESOURCESDLLFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(frob $(lang))/$(DOMAIN).resources.dll)
+ case "$ac_given_srcdir" in
+ .) srcdirpre= ;;
+ *) srcdirpre='$(srcdir)/' ;;
+ esac
+ POFILES=
+ UPDATEPOFILES=
+ DUMMYPOFILES=
+ GMOFILES=
+ PROPERTIESFILES=
+ CLASSFILES=
+ QMFILES=
+ MSGFILES=
+ RESOURCESDLLFILES=
+ for lang in $ALL_LINGUAS; do
+ POFILES="$POFILES $srcdirpre$lang.po"
+ UPDATEPOFILES="$UPDATEPOFILES $lang.po-update"
+ DUMMYPOFILES="$DUMMYPOFILES $lang.nop"
+ GMOFILES="$GMOFILES $srcdirpre$lang.gmo"
+ PROPERTIESFILES="$PROPERTIESFILES \$(top_srcdir)/\$(DOMAIN)_$lang.properties"
+ CLASSFILES="$CLASSFILES \$(top_srcdir)/\$(DOMAIN)_$lang.class"
+ QMFILES="$QMFILES $srcdirpre$lang.qm"
+ frobbedlang=`echo $lang | sed -e 's/\..*$//' -e 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/'`
+ MSGFILES="$MSGFILES $srcdirpre$frobbedlang.msg"
+ frobbedlang=`echo $lang | sed -e 's/_/-/g' -e 's/^sr-CS/sr-SP/' -e 's/@latin$/-Latn/' -e 's/@cyrillic$/-Cyrl/' -e 's/^sr-SP$/sr-SP-Latn/' -e 's/^uz-UZ$/uz-UZ-Latn/'`
+ RESOURCESDLLFILES="$RESOURCESDLLFILES $srcdirpre$frobbedlang/\$(DOMAIN).resources.dll"
+ done
+ # CATALOGS depends on both $ac_dir and the user's LINGUAS
+ # environment variable.
+ INST_LINGUAS=
+ if test -n "$ALL_LINGUAS"; then
+ for presentlang in $ALL_LINGUAS; do
+ useit=no
+ if test "%UNSET%" != "$LINGUAS"; then
+ desiredlanguages="$LINGUAS"
+ else
+ desiredlanguages="$ALL_LINGUAS"
+ fi
+ for desiredlang in $desiredlanguages; do
+ # Use the presentlang catalog if desiredlang is
+ # a. equal to presentlang, or
+ # b. a variant of presentlang (because in this case,
+ # presentlang can be used as a fallback for messages
+ # which are not translated in the desiredlang catalog).
+ case "$desiredlang" in
+ "$presentlang"*) useit=yes;;
+ esac
+ done
+ if test $useit = yes; then
+ INST_LINGUAS="$INST_LINGUAS $presentlang"
+ fi
+ done
+ fi
+ CATALOGS=
+ JAVACATALOGS=
+ QTCATALOGS=
+ TCLCATALOGS=
+ CSHARPCATALOGS=
+ if test -n "$INST_LINGUAS"; then
+ for lang in $INST_LINGUAS; do
+ CATALOGS="$CATALOGS $lang.gmo"
+ JAVACATALOGS="$JAVACATALOGS \$(DOMAIN)_$lang.properties"
+ QTCATALOGS="$QTCATALOGS $lang.qm"
+ frobbedlang=`echo $lang | sed -e 's/\..*$//' -e 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/'`
+ TCLCATALOGS="$TCLCATALOGS $frobbedlang.msg"
+ frobbedlang=`echo $lang | sed -e 's/_/-/g' -e 's/^sr-CS/sr-SP/' -e 's/@latin$/-Latn/' -e 's/@cyrillic$/-Cyrl/' -e 's/^sr-SP$/sr-SP-Latn/' -e 's/^uz-UZ$/uz-UZ-Latn/'`
+ CSHARPCATALOGS="$CSHARPCATALOGS $frobbedlang/\$(DOMAIN).resources.dll"
+ done
+ fi
+
+ sed -e "s|@POTFILES_DEPS@|$POTFILES_DEPS|g" -e "s|@POFILES@|$POFILES|g" -e "s|@UPDATEPOFILES@|$UPDATEPOFILES|g" -e "s|@DUMMYPOFILES@|$DUMMYPOFILES|g" -e "s|@GMOFILES@|$GMOFILES|g" -e "s|@PROPERTIESFILES@|$PROPERTIESFILES|g" -e "s|@CLASSFILES@|$CLASSFILES|g" -e "s|@QMFILES@|$QMFILES|g" -e "s|@MSGFILES@|$MSGFILES|g" -e "s|@RESOURCESDLLFILES@|$RESOURCESDLLFILES|g" -e "s|@CATALOGS@|$CATALOGS|g" -e "s|@JAVACATALOGS@|$JAVACATALOGS|g" -e "s|@QTCATALOGS@|$QTCATALOGS|g" -e "s|@TCLCATALOGS@|$TCLCATALOGS|g" -e "s|@CSHARPCATALOGS@|$CSHARPCATALOGS|g" -e 's,^#distdir:,distdir:,' < "$ac_file" > "$ac_file.tmp"
+ if grep -l '@TCLCATALOGS@' "$ac_file" > /dev/null; then
+ # Add dependencies that cannot be formulated as a simple suffix rule.
+ for lang in $ALL_LINGUAS; do
+ frobbedlang=`echo $lang | sed -e 's/\..*$//' -e 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/'`
+ cat >> "$ac_file.tmp" <<EOF
+$frobbedlang.msg: $lang.po
+ @echo "\$(MSGFMT) -c --tcl -d \$(srcdir) -l $lang $srcdirpre$lang.po"; \
+ \$(MSGFMT) -c --tcl -d "\$(srcdir)" -l $lang $srcdirpre$lang.po || { rm -f "\$(srcdir)/$frobbedlang.msg"; exit 1; }
+EOF
+ done
+ fi
+ if grep -l '@CSHARPCATALOGS@' "$ac_file" > /dev/null; then
+ # Add dependencies that cannot be formulated as a simple suffix rule.
+ for lang in $ALL_LINGUAS; do
+ frobbedlang=`echo $lang | sed -e 's/_/-/g' -e 's/^sr-CS/sr-SP/' -e 's/@latin$/-Latn/' -e 's/@cyrillic$/-Cyrl/' -e 's/^sr-SP$/sr-SP-Latn/' -e 's/^uz-UZ$/uz-UZ-Latn/'`
+ cat >> "$ac_file.tmp" <<EOF
+$frobbedlang/\$(DOMAIN).resources.dll: $lang.po
+ @echo "\$(MSGFMT) -c --csharp -d \$(srcdir) -l $lang $srcdirpre$lang.po -r \$(DOMAIN)"; \
+ \$(MSGFMT) -c --csharp -d "\$(srcdir)" -l $lang $srcdirpre$lang.po -r "\$(DOMAIN)" || { rm -f "\$(srcdir)/$frobbedlang.msg"; exit 1; }
+EOF
+ done
+ fi
+ if test -n "$POMAKEFILEDEPS"; then
+ cat >> "$ac_file.tmp" <<EOF
+Makefile: $POMAKEFILEDEPS
+EOF
+ fi
+ mv "$ac_file.tmp" "$ac_file"
+])
+m4trace:m4/printf-posix.m4:11: -1- AC_DEFUN([gt_PRINTF_POSIX], [
+ AC_REQUIRE([AC_PROG_CC])
+ AC_CACHE_CHECK([whether printf() supports POSIX/XSI format strings],
+ gt_cv_func_printf_posix,
+ [
+ AC_TRY_RUN([
+#include <stdio.h>
+#include <string.h>
+/* The string "%2$d %1$d", with dollar characters protected from the shell's
+ dollar expansion (possibly an autoconf bug). */
+static char format[] = { '%', '2', '$', 'd', ' ', '%', '1', '$', 'd', '\0' };
+static char buf[100];
+int main ()
+{
+ sprintf (buf, format, 33, 55);
+ return (strcmp (buf, "55 33") != 0);
+}], gt_cv_func_printf_posix=yes, gt_cv_func_printf_posix=no,
+ [
+ AC_EGREP_CPP(notposix, [
+#if defined __NetBSD__ || defined _MSC_VER || defined __MINGW32__ || defined __CYGWIN__
+ notposix
+#endif
+ ], gt_cv_func_printf_posix="guessing no",
+ gt_cv_func_printf_posix="guessing yes")
+ ])
+ ])
+ case $gt_cv_func_printf_posix in
+ *yes)
+ AC_DEFINE(HAVE_POSIX_PRINTF, 1,
+ [Define if your printf() function supports format strings with positions.])
+ ;;
+ esac
+])
+m4trace:m4/progtest.m4:25: -1- AC_DEFUN([AM_PATH_PROG_WITH_TEST], [
+# Prepare PATH_SEPARATOR.
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+# Find out how to test for executable files. Don't use a zero-byte file,
+# as systems may use methods other than mode bits to determine executability.
+cat >conf$$.file <<_ASEOF
+#! /bin/sh
+exit 0
+_ASEOF
+chmod +x conf$$.file
+if test -x conf$$.file >/dev/null 2>&1; then
+ ac_executable_p="test -x"
+else
+ ac_executable_p="test -f"
+fi
+rm -f conf$$.file
+
+# Extract the first word of "$2", so it can be a program name with args.
+set dummy $2; ac_word=[$]2
+AC_MSG_CHECKING([for $ac_word])
+AC_CACHE_VAL(ac_cv_path_$1,
+[case "[$]$1" in
+ [[\\/]]* | ?:[[\\/]]*)
+ ac_cv_path_$1="[$]$1" # Let the user override the test with a path.
+ ;;
+ *)
+ ac_save_IFS="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in ifelse([$5], , $PATH, [$5]); do
+ IFS="$ac_save_IFS"
+ test -z "$ac_dir" && ac_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $ac_executable_p "$ac_dir/$ac_word$ac_exec_ext"; then
+ echo "$as_me: trying $ac_dir/$ac_word..." >&AS_MESSAGE_LOG_FD
+ if [$3]; then
+ ac_cv_path_$1="$ac_dir/$ac_word$ac_exec_ext"
+ break 2
+ fi
+ fi
+ done
+ done
+ IFS="$ac_save_IFS"
+dnl If no 4th arg is given, leave the cache variable unset,
+dnl so AC_PATH_PROGS will keep looking.
+ifelse([$4], , , [ test -z "[$]ac_cv_path_$1" && ac_cv_path_$1="$4"
+])dnl
+ ;;
+esac])dnl
+$1="$ac_cv_path_$1"
+if test ifelse([$4], , [-n "[$]$1"], ["[$]$1" != "$4"]); then
+ AC_MSG_RESULT([$]$1)
+else
+ AC_MSG_RESULT(no)
+fi
+AC_SUBST($1)dnl
+])
+m4trace:m4/size_max.m4:9: -1- AC_DEFUN([gl_SIZE_MAX], [
+ AC_CHECK_HEADERS(stdint.h)
+ dnl First test whether the system already has SIZE_MAX.
+ AC_MSG_CHECKING([for SIZE_MAX])
+ AC_CACHE_VAL([gl_cv_size_max], [
+ gl_cv_size_max=
+ AC_EGREP_CPP([Found it], [
+#include <limits.h>
+#if HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#ifdef SIZE_MAX
+Found it
+#endif
+], gl_cv_size_max=yes)
+ if test -z "$gl_cv_size_max"; then
+ dnl Define it ourselves. Here we assume that the type 'size_t' is not wider
+ dnl than the type 'unsigned long'. Try hard to find a definition that can
+ dnl be used in a preprocessor #if, i.e. doesn't contain a cast.
+ _AC_COMPUTE_INT([sizeof (size_t) * CHAR_BIT - 1], size_t_bits_minus_1,
+ [#include <stddef.h>
+#include <limits.h>], size_t_bits_minus_1=)
+ _AC_COMPUTE_INT([sizeof (size_t) <= sizeof (unsigned int)], fits_in_uint,
+ [#include <stddef.h>], fits_in_uint=)
+ if test -n "$size_t_bits_minus_1" && test -n "$fits_in_uint"; then
+ if test $fits_in_uint = 1; then
+ dnl Even though SIZE_MAX fits in an unsigned int, it must be of type
+ dnl 'unsigned long' if the type 'size_t' is the same as 'unsigned long'.
+ AC_TRY_COMPILE([#include <stddef.h>
+ extern size_t foo;
+ extern unsigned long foo;
+ ], [], fits_in_uint=0)
+ fi
+ dnl We cannot use 'expr' to simplify this expression, because 'expr'
+ dnl works only with 'long' integers in the host environment, while we
+ dnl might be cross-compiling from a 32-bit platform to a 64-bit platform.
+ if test $fits_in_uint = 1; then
+ gl_cv_size_max="(((1U << $size_t_bits_minus_1) - 1) * 2 + 1)"
+ else
+ gl_cv_size_max="(((1UL << $size_t_bits_minus_1) - 1) * 2 + 1)"
+ fi
+ else
+ dnl Shouldn't happen, but who knows...
+ gl_cv_size_max='((size_t)~(size_t)0)'
+ fi
+ fi
+ ])
+ AC_MSG_RESULT([$gl_cv_size_max])
+ if test "$gl_cv_size_max" != yes; then
+ AC_DEFINE_UNQUOTED([SIZE_MAX], [$gl_cv_size_max],
+ [Define as the maximum value of type 'size_t', if the system doesn't define it.])
+ fi
+])
+m4trace:m4/stdint_h.m4:12: -1- AC_DEFUN([gl_AC_HEADER_STDINT_H], [
+ AC_CACHE_CHECK([for stdint.h], gl_cv_header_stdint_h,
+ [AC_TRY_COMPILE(
+ [#include <sys/types.h>
+#include <stdint.h>],
+ [uintmax_t i = (uintmax_t) -1; return !i;],
+ gl_cv_header_stdint_h=yes,
+ gl_cv_header_stdint_h=no)])
+ if test $gl_cv_header_stdint_h = yes; then
+ AC_DEFINE_UNQUOTED(HAVE_STDINT_H_WITH_UINTMAX, 1,
+ [Define if <stdint.h> exists, doesn't clash with <sys/types.h>,
+ and declares uintmax_t. ])
+ fi
+])
+m4trace:m4/uintmax_t.m4:14: -1- AC_DEFUN([gl_AC_TYPE_UINTMAX_T], [
+ AC_REQUIRE([gl_AC_HEADER_INTTYPES_H])
+ AC_REQUIRE([gl_AC_HEADER_STDINT_H])
+ if test $gl_cv_header_inttypes_h = no && test $gl_cv_header_stdint_h = no; then
+ AC_REQUIRE([gl_AC_TYPE_UNSIGNED_LONG_LONG])
+ test $ac_cv_type_unsigned_long_long = yes \
+ && ac_type='unsigned long long' \
+ || ac_type='unsigned long'
+ AC_DEFINE_UNQUOTED(uintmax_t, $ac_type,
+ [Define to unsigned long or unsigned long long
+ if <stdint.h> and <inttypes.h> don't define.])
+ else
+ AC_DEFINE(HAVE_UINTMAX_T, 1,
+ [Define if you have the 'uintmax_t' type in <stdint.h> or <inttypes.h>.])
+ fi
+])
+m4trace:m4/ulonglong.m4:18: -1- AC_DEFUN([AC_TYPE_UNSIGNED_LONG_LONG_INT], [
+ AC_CACHE_CHECK([for unsigned long long int],
+ [ac_cv_type_unsigned_long_long_int],
+ [AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[unsigned long long int ull = 18446744073709551615ULL;
+ typedef int a[(18446744073709551615ULL <= (unsigned long long int) -1
+ ? 1 : -1)];
+ int i = 63;]],
+ [[unsigned long long int ullmax = 18446744073709551615ull;
+ return (ull << 63 | ull >> 63 | ull << i | ull >> i
+ | ullmax / ull | ullmax % ull);]])],
+ [ac_cv_type_unsigned_long_long_int=yes],
+ [ac_cv_type_unsigned_long_long_int=no])])
+ if test $ac_cv_type_unsigned_long_long_int = yes; then
+ AC_DEFINE([HAVE_UNSIGNED_LONG_LONG_INT], 1,
+ [Define to 1 if the system has the type `unsigned long long int'.])
+ fi
+])
+m4trace:m4/ulonglong.m4:40: -1- AC_DEFUN([gl_AC_TYPE_UNSIGNED_LONG_LONG], [
+ AC_REQUIRE([AC_TYPE_UNSIGNED_LONG_LONG_INT])
+ ac_cv_type_unsigned_long_long=$ac_cv_type_unsigned_long_long_int
+ if test $ac_cv_type_unsigned_long_long = yes; then
+ AC_DEFINE(HAVE_UNSIGNED_LONG_LONG, 1,
+ [Define if you have the 'unsigned long long' type.])
+ fi
+])
+m4trace:m4/visibility.m4:23: -1- AC_DEFUN([gl_VISIBILITY], [
+ AC_REQUIRE([AC_PROG_CC])
+ CFLAG_VISIBILITY=
+ HAVE_VISIBILITY=0
+ if test -n "$GCC"; then
+ AC_MSG_CHECKING([for simple visibility declarations])
+ AC_CACHE_VAL(gl_cv_cc_visibility, [
+ gl_save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS -fvisibility=hidden"
+ AC_TRY_COMPILE(
+ [extern __attribute__((__visibility__("hidden"))) int hiddenvar;
+ extern __attribute__((__visibility__("default"))) int exportedvar;
+ extern __attribute__((__visibility__("hidden"))) int hiddenfunc (void);
+ extern __attribute__((__visibility__("default"))) int exportedfunc (void);],
+ [],
+ gl_cv_cc_visibility=yes,
+ gl_cv_cc_visibility=no)
+ CFLAGS="$gl_save_CFLAGS"])
+ AC_MSG_RESULT([$gl_cv_cc_visibility])
+ if test $gl_cv_cc_visibility = yes; then
+ CFLAG_VISIBILITY="-fvisibility=hidden"
+ HAVE_VISIBILITY=1
+ fi
+ fi
+ AC_SUBST([CFLAG_VISIBILITY])
+ AC_SUBST([HAVE_VISIBILITY])
+ AC_DEFINE_UNQUOTED([HAVE_VISIBILITY], [$HAVE_VISIBILITY],
+ [Define to 1 or 0, depending whether the compiler supports simple visibility declarations.])
+])
+m4trace:m4/wchar_t.m4:11: -1- AC_DEFUN([gt_TYPE_WCHAR_T], [
+ AC_CACHE_CHECK([for wchar_t], gt_cv_c_wchar_t,
+ [AC_TRY_COMPILE([#include <stddef.h>
+ wchar_t foo = (wchar_t)'\0';], ,
+ gt_cv_c_wchar_t=yes, gt_cv_c_wchar_t=no)])
+ if test $gt_cv_c_wchar_t = yes; then
+ AC_DEFINE(HAVE_WCHAR_T, 1, [Define if you have the 'wchar_t' type.])
+ fi
+])
+m4trace:m4/wint_t.m4:11: -1- AC_DEFUN([gt_TYPE_WINT_T], [
+ AC_CACHE_CHECK([for wint_t], gt_cv_c_wint_t,
+ [AC_TRY_COMPILE([#include <wchar.h>
+ wint_t foo = (wchar_t)'\0';], ,
+ gt_cv_c_wint_t=yes, gt_cv_c_wint_t=no)])
+ if test $gt_cv_c_wint_t = yes; then
+ AC_DEFINE(HAVE_WINT_T, 1, [Define if you have the 'wint_t' type.])
+ fi
+])
+m4trace:m4/xsize.m4:7: -1- AC_DEFUN([gl_XSIZE], [
+ dnl Prerequisites of lib/xsize.h.
+ AC_REQUIRE([gl_SIZE_MAX])
+ AC_REQUIRE([AC_C_INLINE])
+ AC_CHECK_HEADERS(stdint.h)
+])
+m4trace:configure.ac:20: -3- m4_include([VERSION])
+m4trace:configure.ac:20: -1- m4_pattern_forbid([^_?A[CHUM]_])
+m4trace:configure.ac:20: -1- m4_pattern_forbid([_AC_])
+m4trace:configure.ac:20: -1- m4_pattern_forbid([^LIBOBJS$], [do not use LIBOBJS directly, use AC_LIBOBJ (see section `AC_LIBOBJ vs LIBOBJS'])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^AS_FLAGS$])
+m4trace:configure.ac:20: -1- m4_pattern_forbid([^_?m4_])
+m4trace:configure.ac:20: -1- m4_pattern_forbid([^dnl$])
+m4trace:configure.ac:20: -1- m4_pattern_forbid([^_?AS_])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^SHELL$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PATH_SEPARATOR$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_NAME$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_TARNAME$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_VERSION$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_STRING$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_BUGREPORT$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_URL$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^exec_prefix$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^prefix$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^program_transform_name$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^bindir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^sbindir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^libexecdir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^datarootdir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^datadir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^sysconfdir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^sharedstatedir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^localstatedir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^includedir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^oldincludedir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^docdir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^infodir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^htmldir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^dvidir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^pdfdir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^psdir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^libdir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^localedir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^mandir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_NAME$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_TARNAME$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_VERSION$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_STRING$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_BUGREPORT$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_URL$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^DEFS$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^ECHO_C$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^ECHO_N$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^ECHO_T$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^LIBS$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^build_alias$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^host_alias$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^target_alias$])
+m4trace:configure.ac:25: -1- AM_INIT_AUTOMAKE([foreign nostdinc])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^AM_[A-Z]+FLAGS$])
+m4trace:configure.ac:25: -1- AM_SET_CURRENT_AUTOMAKE_VERSION
+m4trace:configure.ac:25: -1- AM_AUTOMAKE_VERSION([1.11.1])
+m4trace:configure.ac:25: -1- _AM_AUTOCONF_VERSION([2.68])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^INSTALL_PROGRAM$])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^INSTALL_SCRIPT$])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^INSTALL_DATA$])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^am__isrc$])
+m4trace:configure.ac:25: -1- _AM_SUBST_NOTMAKE([am__isrc])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^CYGPATH_W$])
+m4trace:configure.ac:25: -1- _AM_SET_OPTIONS([foreign nostdinc])
+m4trace:configure.ac:25: -1- _AM_SET_OPTION([foreign])
+m4trace:configure.ac:25: -2- _AM_MANGLE_OPTION([foreign])
+m4trace:configure.ac:25: -1- _AM_SET_OPTION([nostdinc])
+m4trace:configure.ac:25: -2- _AM_MANGLE_OPTION([nostdinc])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^PACKAGE$])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^VERSION$])
+m4trace:configure.ac:25: -1- _AM_IF_OPTION([no-define], [], [AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package])
+ AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])
+m4trace:configure.ac:25: -2- _AM_MANGLE_OPTION([no-define])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^PACKAGE$])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^VERSION$])
+m4trace:configure.ac:25: -1- AM_SANITY_CHECK
+m4trace:configure.ac:25: -1- AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}])
+m4trace:configure.ac:25: -1- AM_MISSING_HAS_RUN
+m4trace:configure.ac:25: -1- AM_AUX_DIR_EXPAND
+m4trace:configure.ac:25: -1- m4_pattern_allow([^ACLOCAL$])
+m4trace:configure.ac:25: -1- AM_MISSING_PROG([AUTOCONF], [autoconf])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^AUTOCONF$])
+m4trace:configure.ac:25: -1- AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^AUTOMAKE$])
+m4trace:configure.ac:25: -1- AM_MISSING_PROG([AUTOHEADER], [autoheader])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^AUTOHEADER$])
+m4trace:configure.ac:25: -1- AM_MISSING_PROG([MAKEINFO], [makeinfo])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^MAKEINFO$])
+m4trace:configure.ac:25: -1- AM_PROG_INSTALL_SH
+m4trace:configure.ac:25: -1- m4_pattern_allow([^install_sh$])
+m4trace:configure.ac:25: -1- AM_PROG_INSTALL_STRIP
+m4trace:configure.ac:25: -1- m4_pattern_allow([^STRIP$])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^INSTALL_STRIP_PROGRAM$])
+m4trace:configure.ac:25: -1- AM_PROG_MKDIR_P
+m4trace:configure.ac:25: -1- m4_pattern_allow([^MKDIR_P$])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^mkdir_p$])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^AWK$])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^SET_MAKE$])
+m4trace:configure.ac:25: -1- AM_SET_LEADING_DOT
+m4trace:configure.ac:25: -1- m4_pattern_allow([^am__leading_dot$])
+m4trace:configure.ac:25: -1- _AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])],
+ [_AM_PROG_TAR([v7])])])
+m4trace:configure.ac:25: -2- _AM_MANGLE_OPTION([tar-ustar])
+m4trace:configure.ac:25: -1- _AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], [_AM_PROG_TAR([v7])])
+m4trace:configure.ac:25: -2- _AM_MANGLE_OPTION([tar-pax])
+m4trace:configure.ac:25: -1- _AM_PROG_TAR([v7])
+m4trace:configure.ac:25: -1- AM_MISSING_PROG([AMTAR], [tar])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^AMTAR$])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^am__tar$])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^am__untar$])
+m4trace:configure.ac:25: -1- _AM_IF_OPTION([no-dependencies], [], [AC_PROVIDE_IFELSE([AC_PROG_CC],
+ [_AM_DEPENDENCIES(CC)],
+ [define([AC_PROG_CC],
+ defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_CXX],
+ [_AM_DEPENDENCIES(CXX)],
+ [define([AC_PROG_CXX],
+ defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_OBJC],
+ [_AM_DEPENDENCIES(OBJC)],
+ [define([AC_PROG_OBJC],
+ defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])dnl
+])
+m4trace:configure.ac:25: -2- _AM_MANGLE_OPTION([no-dependencies])
+m4trace:configure.ac:25: -1- _AM_IF_OPTION([silent-rules], [AC_REQUIRE([AM_SILENT_RULES])])
+m4trace:configure.ac:25: -2- _AM_MANGLE_OPTION([silent-rules])
+m4trace:configure.ac:27: -1- AM_MAINTAINER_MODE
+m4trace:configure.ac:27: -1- AM_CONDITIONAL([MAINTAINER_MODE], [test $USE_MAINTAINER_MODE = yes])
+m4trace:configure.ac:27: -1- m4_pattern_allow([^MAINTAINER_MODE_TRUE$])
+m4trace:configure.ac:27: -1- m4_pattern_allow([^MAINTAINER_MODE_FALSE$])
+m4trace:configure.ac:27: -1- _AM_SUBST_NOTMAKE([MAINTAINER_MODE_TRUE])
+m4trace:configure.ac:27: -1- _AM_SUBST_NOTMAKE([MAINTAINER_MODE_FALSE])
+m4trace:configure.ac:27: -1- m4_pattern_allow([^MAINT$])
+m4trace:configure.ac:29: -1- m4_pattern_allow([^build$])
+m4trace:configure.ac:29: -1- m4_pattern_allow([^build_cpu$])
+m4trace:configure.ac:29: -1- m4_pattern_allow([^build_vendor$])
+m4trace:configure.ac:29: -1- m4_pattern_allow([^build_os$])
+m4trace:configure.ac:29: -1- m4_pattern_allow([^host$])
+m4trace:configure.ac:29: -1- m4_pattern_allow([^host_cpu$])
+m4trace:configure.ac:29: -1- m4_pattern_allow([^host_vendor$])
+m4trace:configure.ac:29: -1- m4_pattern_allow([^host_os$])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^CC$])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^CFLAGS$])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^LDFLAGS$])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^LIBS$])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^CPPFLAGS$])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^CC$])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^CC$])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^CC$])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^CC$])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^ac_ct_CC$])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^EXEEXT$])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^OBJEXT$])
+m4trace:configure.ac:40: -1- _AM_DEPENDENCIES([CC])
+m4trace:configure.ac:40: -1- AM_SET_DEPDIR
+m4trace:configure.ac:40: -1- m4_pattern_allow([^DEPDIR$])
+m4trace:configure.ac:40: -1- AM_OUTPUT_DEPENDENCY_COMMANDS
+m4trace:configure.ac:40: -1- AM_MAKE_INCLUDE
+m4trace:configure.ac:40: -1- m4_pattern_allow([^am__include$])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^am__quote$])
+m4trace:configure.ac:40: -1- AM_DEP_TRACK
+m4trace:configure.ac:40: -1- AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^AMDEP_TRUE$])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^AMDEP_FALSE$])
+m4trace:configure.ac:40: -1- _AM_SUBST_NOTMAKE([AMDEP_TRUE])
+m4trace:configure.ac:40: -1- _AM_SUBST_NOTMAKE([AMDEP_FALSE])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^AMDEPBACKSLASH$])
+m4trace:configure.ac:40: -1- _AM_SUBST_NOTMAKE([AMDEPBACKSLASH])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^CCDEPMODE$])
+m4trace:configure.ac:40: -1- AM_CONDITIONAL([am__fastdepCC], [
+ test "x$enable_dependency_tracking" != xno \
+ && test "$am_cv_CC_dependencies_compiler_type" = gcc3])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^am__fastdepCC_TRUE$])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^am__fastdepCC_FALSE$])
+m4trace:configure.ac:40: -1- _AM_SUBST_NOTMAKE([am__fastdepCC_TRUE])
+m4trace:configure.ac:40: -1- _AM_SUBST_NOTMAKE([am__fastdepCC_FALSE])
+m4trace:configure.ac:43: -1- m4_pattern_allow([^SET_MAKE$])
+m4trace:configure.ac:44: -1- m4_pattern_allow([^LN_S$])
+m4trace:configure.ac:45: -1- m4_pattern_allow([^AWK$])
+m4trace:configure.ac:46: -1- m4_pattern_allow([^RANLIB$])
+m4trace:configure.ac:47: -1- AC_PROG_LIBTOOL
+m4trace:configure.ac:47: -1- _m4_warn([obsolete], [The macro `AC_PROG_LIBTOOL' is obsolete.
+You should run autoupdate.], [/usr/share/aclocal/libtool.m4:102: AC_PROG_LIBTOOL is expanded from...
+configure.ac:47: the top level])
+m4trace:configure.ac:47: -1- LT_INIT
+m4trace:configure.ac:47: -1- m4_pattern_forbid([^_?LT_[A-Z_]+$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])
+m4trace:configure.ac:47: -1- LTOPTIONS_VERSION
+m4trace:configure.ac:47: -1- LTSUGAR_VERSION
+m4trace:configure.ac:47: -1- LTVERSION_VERSION
+m4trace:configure.ac:47: -1- LTOBSOLETE_VERSION
+m4trace:configure.ac:47: -1- _LT_PROG_LTMAIN
+m4trace:configure.ac:47: -1- m4_pattern_allow([^LIBTOOL$])
+m4trace:configure.ac:47: -1- LT_PATH_LD
+m4trace:configure.ac:47: -1- m4_pattern_allow([^SED$])
+m4trace:configure.ac:47: -1- AC_PROG_EGREP
+m4trace:configure.ac:47: -1- m4_pattern_allow([^GREP$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^EGREP$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^FGREP$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^GREP$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^LD$])
+m4trace:configure.ac:47: -1- LT_PATH_NM
+m4trace:configure.ac:47: -1- m4_pattern_allow([^DUMPBIN$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^ac_ct_DUMPBIN$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^DUMPBIN$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^NM$])
+m4trace:configure.ac:47: -1- LT_CMD_MAX_LEN
+m4trace:configure.ac:47: -1- m4_pattern_allow([^OBJDUMP$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^OBJDUMP$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^AR$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^STRIP$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^RANLIB$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([LT_OBJDIR])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^LT_OBJDIR$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^lt_ECHO$])
+m4trace:configure.ac:47: -1- _LT_CC_BASENAME([$compiler])
+m4trace:configure.ac:47: -1- _LT_PATH_TOOL_PREFIX([${ac_tool_prefix}file], [/usr/bin$PATH_SEPARATOR$PATH])
+m4trace:configure.ac:47: -1- _LT_PATH_TOOL_PREFIX([file], [/usr/bin$PATH_SEPARATOR$PATH])
+m4trace:configure.ac:47: -1- LT_SUPPORTED_TAG([CC])
+m4trace:configure.ac:47: -1- _LT_COMPILER_BOILERPLATE
+m4trace:configure.ac:47: -1- _LT_LINKER_BOILERPLATE
+m4trace:configure.ac:47: -1- _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions], [lt_cv_prog_compiler_rtti_exceptions], [-fno-rtti -fno-exceptions], [], [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, )="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, ) -fno-rtti -fno-exceptions"])
+m4trace:configure.ac:47: -1- _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, ) works], [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, )], [$_LT_TAGVAR(lt_prog_compiler_pic, )@&t@m4_if([],[],[ -DPIC],[m4_if([],[CXX],[ -DPIC],[])])], [], [case $_LT_TAGVAR(lt_prog_compiler_pic, ) in
+ "" | " "*) ;;
+ *) _LT_TAGVAR(lt_prog_compiler_pic, )=" $_LT_TAGVAR(lt_prog_compiler_pic, )" ;;
+ esac], [_LT_TAGVAR(lt_prog_compiler_pic, )=
+ _LT_TAGVAR(lt_prog_compiler_can_build_shared, )=no])
+m4trace:configure.ac:47: -1- _LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works], [lt_cv_prog_compiler_static_works], [$lt_tmp_static_flag], [], [_LT_TAGVAR(lt_prog_compiler_static, )=])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^DSYMUTIL$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^NMEDIT$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^LIPO$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^OTOOL$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^OTOOL64$])
+m4trace:configure.ac:47: -1- LT_SYS_DLOPEN_SELF
+m4trace:configure.ac:47: -1- m4_pattern_allow([^CPP$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^CPPFLAGS$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^CPP$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^STDC_HEADERS$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^HAVE_DLFCN_H$])
+m4trace:configure.ac:49: -1- m4_pattern_allow([^AR$])
+m4trace:configure.ac:50: -1- m4_pattern_allow([^RM$])
+m4trace:configure.ac:51: -1- m4_pattern_allow([^CP$])
+m4trace:configure.ac:52: -1- m4_pattern_allow([^LN$])
+m4trace:configure.ac:53: -1- m4_pattern_allow([^SED$])
+m4trace:configure.ac:54: -1- m4_pattern_allow([^MAKE$])
+m4trace:configure.ac:58: -1- AM_GNU_GETTEXT_VERSION([0.16.1])
+m4trace:configure.ac:59: -1- AM_GNU_GETTEXT([external])
+m4trace:configure.ac:59: -1- AM_GNU_GETTEXT_NEED([])
+m4trace:configure.ac:59: -1- AM_PO_SUBDIRS
+m4trace:configure.ac:59: -1- AM_NLS
+m4trace:configure.ac:59: -1- m4_pattern_allow([^USE_NLS$])
+m4trace:configure.ac:59: -1- AM_PATH_PROG_WITH_TEST([MSGFMT], [msgfmt], [$ac_dir/$ac_word --statistics /dev/null >&5 2>&1 &&
+ (if $ac_dir/$ac_word --statistics /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi)], [:])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^MSGFMT$])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^GMSGFMT$])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^MSGFMT_015$])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^GMSGFMT_015$])
+m4trace:configure.ac:59: -1- AM_PATH_PROG_WITH_TEST([XGETTEXT], [xgettext], [$ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= /dev/null >&5 2>&1 &&
+ (if $ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi)], [:])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^XGETTEXT$])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^XGETTEXT_015$])
+m4trace:configure.ac:59: -1- AM_PATH_PROG_WITH_TEST([MSGMERGE], [msgmerge], [$ac_dir/$ac_word --update -q /dev/null /dev/null >&5 2>&1], [:])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^MSGMERGE$])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^localedir$])
+m4trace:configure.ac:59: -1- AC_LIB_PREPARE_PREFIX
+m4trace:configure.ac:59: -1- AC_LIB_RPATH
+m4trace:configure.ac:59: -1- AC_LIB_PROG_LD
+m4trace:configure.ac:59: -1- AC_LIB_PROG_LD_GNU
+m4trace:configure.ac:59: -1- AM_ICONV_LINKFLAGS_BODY
+m4trace:configure.ac:59: -1- AC_LIB_LINKFLAGS_BODY([iconv])
+m4trace:configure.ac:59: -1- AC_LIB_PREPARE_MULTILIB
+m4trace:configure.ac:59: -1- AC_LIB_WITH_FINAL_PREFIX([
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+ ])
+m4trace:configure.ac:59: -1- AC_LIB_ARG_WITH([libiconv-prefix], [ --with-libiconv-prefix[=DIR] search for libiconv in DIR/include and DIR/lib
+ --without-libiconv-prefix don't search for libiconv in includedir and libdir], [
+ if test "X$withval" = "Xno"; then
+ use_additional=no
+ else
+ if test "X$withval" = "X"; then
+ AC_LIB_WITH_FINAL_PREFIX([
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+ ])
+ else
+ additional_includedir="$withval/include"
+ additional_libdir="$withval/$acl_libdirstem"
+ fi
+ fi
+])
+m4trace:configure.ac:59: -1- AC_LIB_WITH_FINAL_PREFIX([
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+ ])
+m4trace:configure.ac:59: -1- AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+m4trace:configure.ac:59: -1- AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+m4trace:configure.ac:59: -1- AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+m4trace:configure.ac:59: -1- AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+m4trace:configure.ac:59: -1- AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+m4trace:configure.ac:59: -1- gt_INTL_MACOSX
+m4trace:configure.ac:59: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:2688: AC_TRY_LINK is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/general.m4:2032: AC_CACHE_VAL is expanded from...
+../../lib/autoconf/general.m4:2053: AC_CACHE_CHECK is expanded from...
+m4/gettext.m4:367: gt_INTL_MACOSX is expanded from...
+m4/gettext.m4:57: AM_GNU_GETTEXT is expanded from...
+configure.ac:59: the top level])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^HAVE_CFPREFERENCESCOPYAPPVALUE$])
+m4trace:configure.ac:59: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:2688: AC_TRY_LINK is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/general.m4:2032: AC_CACHE_VAL is expanded from...
+../../lib/autoconf/general.m4:2053: AC_CACHE_CHECK is expanded from...
+m4/gettext.m4:367: gt_INTL_MACOSX is expanded from...
+m4/gettext.m4:57: AM_GNU_GETTEXT is expanded from...
+configure.ac:59: the top level])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^HAVE_CFLOCALECOPYCURRENT$])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^INTL_MACOSX_LIBS$])
+m4trace:configure.ac:59: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:2688: AC_TRY_LINK is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/general.m4:2032: AC_CACHE_VAL is expanded from...
+../../lib/autoconf/general.m4:2053: AC_CACHE_CHECK is expanded from...
+m4/gettext.m4:57: AM_GNU_GETTEXT is expanded from...
+configure.ac:59: the top level])
+m4trace:configure.ac:59: -1- AM_ICONV_LINK
+m4trace:configure.ac:59: -1- AC_LIB_APPENDTOVAR([CPPFLAGS], [$INCICONV])
+m4trace:configure.ac:59: -1- AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+m4trace:configure.ac:59: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:2688: AC_TRY_LINK is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/general.m4:2032: AC_CACHE_VAL is expanded from...
+../../lib/autoconf/general.m4:2053: AC_CACHE_CHECK is expanded from...
+m4/iconv.m4:20: AM_ICONV_LINK is expanded from...
+m4/gettext.m4:57: AM_GNU_GETTEXT is expanded from...
+configure.ac:59: the top level])
+m4trace:configure.ac:59: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:2688: AC_TRY_LINK is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/general.m4:2032: AC_CACHE_VAL is expanded from...
+../../lib/autoconf/general.m4:2053: AC_CACHE_CHECK is expanded from...
+m4/iconv.m4:20: AM_ICONV_LINK is expanded from...
+m4/gettext.m4:57: AM_GNU_GETTEXT is expanded from...
+configure.ac:59: the top level])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^HAVE_ICONV$])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^LIBICONV$])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^LTLIBICONV$])
+m4trace:configure.ac:59: -1- AC_LIB_LINKFLAGS_BODY([intl])
+m4trace:configure.ac:59: -1- AC_LIB_WITH_FINAL_PREFIX([
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+ ])
+m4trace:configure.ac:59: -1- AC_LIB_ARG_WITH([libintl-prefix], [ --with-libintl-prefix[=DIR] search for libintl in DIR/include and DIR/lib
+ --without-libintl-prefix don't search for libintl in includedir and libdir], [
+ if test "X$withval" = "Xno"; then
+ use_additional=no
+ else
+ if test "X$withval" = "X"; then
+ AC_LIB_WITH_FINAL_PREFIX([
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+ ])
+ else
+ additional_includedir="$withval/include"
+ additional_libdir="$withval/$acl_libdirstem"
+ fi
+ fi
+])
+m4trace:configure.ac:59: -1- AC_LIB_WITH_FINAL_PREFIX([
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+ ])
+m4trace:configure.ac:59: -1- AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+m4trace:configure.ac:59: -1- AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+m4trace:configure.ac:59: -1- AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+m4trace:configure.ac:59: -1- AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+m4trace:configure.ac:59: -1- AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+m4trace:configure.ac:59: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:2688: AC_TRY_LINK is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/general.m4:2032: AC_CACHE_VAL is expanded from...
+../../lib/autoconf/general.m4:2053: AC_CACHE_CHECK is expanded from...
+m4/gettext.m4:57: AM_GNU_GETTEXT is expanded from...
+configure.ac:59: the top level])
+m4trace:configure.ac:59: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:2688: AC_TRY_LINK is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/general.m4:2032: AC_CACHE_VAL is expanded from...
+../../lib/autoconf/general.m4:2053: AC_CACHE_CHECK is expanded from...
+m4/gettext.m4:57: AM_GNU_GETTEXT is expanded from...
+configure.ac:59: the top level])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^ENABLE_NLS$])
+m4trace:configure.ac:59: -1- AC_LIB_APPENDTOVAR([CPPFLAGS], [$INCINTL])
+m4trace:configure.ac:59: -1- AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^HAVE_GETTEXT$])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^HAVE_DCGETTEXT$])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^INTLLIBS$])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^LIBINTL$])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^LTLIBINTL$])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^POSUB$])
+m4trace:configure.ac:64: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:64: the top level])
+m4trace:configure.ac:72: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:72: the top level])
+m4trace:configure.ac:82: -1- m4_pattern_allow([^ENABLE_DMALLOC$])
+m4trace:configure.ac:87: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:87: the top level])
+m4trace:configure.ac:99: -1- m4_pattern_allow([^localedir$])
+m4trace:configure.ac:106: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:106: the top level])
+m4trace:configure.ac:131: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:131: the top level])
+m4trace:configure.ac:147: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:147: the top level])
+m4trace:configure.ac:163: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:163: the top level])
+m4trace:configure.ac:172: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:172: the top level])
+m4trace:configure.ac:187: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:187: the top level])
+m4trace:configure.ac:190: -1- m4_pattern_allow([^DEBUG$])
+m4trace:configure.ac:191: -1- m4_pattern_allow([^DEBUGJOURNAL$])
+m4trace:configure.ac:200: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:200: the top level])
+m4trace:configure.ac:211: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:211: the top level])
+m4trace:configure.ac:213: -1- m4_pattern_allow([^MOUSE$])
+m4trace:configure.ac:221: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:221: the top level])
+m4trace:configure.ac:223: -1- m4_pattern_allow([^USE_QUOTAS$])
+m4trace:configure.ac:230: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:230: the top level])
+m4trace:configure.ac:234: -1- m4_pattern_allow([^NEVER_ALLOW_CHANGING_FROM$])
+m4trace:configure.ac:240: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:240: the top level])
+m4trace:configure.ac:242: -1- m4_pattern_allow([^BACKGROUND_POST$])
+m4trace:configure.ac:250: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:250: the top level])
+m4trace:configure.ac:252: -1- m4_pattern_allow([^KEYBOARD_LOCK$])
+m4trace:configure.ac:260: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:260: the top level])
+m4trace:configure.ac:262: -1- m4_pattern_allow([^ENCODE_FROMS$])
+m4trace:configure.ac:271: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:271: the top level])
+m4trace:configure.ac:270: -1- m4_pattern_allow([^SENDMAIL$])
+m4trace:configure.ac:270: -1- m4_pattern_allow([^SENDMAIL$])
+m4trace:configure.ac:288: -1- m4_pattern_allow([^SENDMAIL$])
+m4trace:configure.ac:294: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:294: the top level])
+m4trace:configure.ac:300: -1- m4_pattern_allow([^SENDMAILFLAGS$])
+m4trace:configure.ac:305: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:305: the top level])
+m4trace:configure.ac:304: -1- m4_pattern_allow([^NPA_PROG$])
+m4trace:configure.ac:304: -1- m4_pattern_allow([^NPA_PROG$])
+m4trace:configure.ac:324: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:324: the top level])
+m4trace:configure.ac:331: -1- m4_pattern_allow([^SENDNEWS$])
+m4trace:configure.ac:336: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:336: the top level])
+m4trace:configure.ac:335: -1- m4_pattern_allow([^PWPROG$])
+m4trace:configure.ac:335: -1- m4_pattern_allow([^PWPROG$])
+m4trace:configure.ac:335: -1- m4_pattern_allow([^PWPROG$])
+m4trace:configure.ac:353: -1- m4_pattern_allow([^PASSWD_PROG$])
+m4trace:configure.ac:358: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:358: the top level])
+m4trace:configure.ac:357: -1- m4_pattern_allow([^SPELLPROG$])
+m4trace:configure.ac:357: -1- m4_pattern_allow([^SPELLPROG$])
+m4trace:configure.ac:357: -1- m4_pattern_allow([^SPELLPROG$])
+m4trace:configure.ac:378: -1- m4_pattern_allow([^alpine_simple_spellcheck$])
+m4trace:configure.ac:398: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:398: the top level])
+m4trace:configure.ac:397: -1- m4_pattern_allow([^ISPELLPROG$])
+m4trace:configure.ac:397: -1- m4_pattern_allow([^ISPELLPROG$])
+m4trace:configure.ac:415: -1- m4_pattern_allow([^alpine_interactive_spellcheck$])
+m4trace:configure.ac:428: -1- m4_pattern_allow([^DF_VAR_SPELLER$])
+m4trace:configure.ac:434: -1- m4_pattern_allow([^SPELLER$])
+m4trace:configure.ac:445: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:445: the top level])
+m4trace:configure.ac:457: -1- m4_pattern_allow([^SYSTEM_PINERC$])
+m4trace:configure.ac:468: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:468: the top level])
+m4trace:configure.ac:480: -1- m4_pattern_allow([^SYSTEM_PINERC_FIXED$])
+m4trace:configure.ac:484: -1- AC_DEFUN([PINEVAR], [
+ dpv=$3
+ AC_ARG_WITH($1,
+ AC_HELP_STRING(--with-$1=VALUE, [$4 ($3)]),
+ [
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+ ])
+ AC_DEFINE_UNQUOTED($2, "$dpv", [Default configuration value])
+ ])
+m4trace:configure.ac:499: -1- AC_DEFUN([PINEVAR_UNQUOTED], [
+ dpv=$3
+ AC_ARG_WITH($1,
+ AC_HELP_STRING(--with-$1=VALUE, [$4 ($3)]),
+ [
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+ ])
+ AC_DEFINE_UNQUOTED($2, $dpv, [Default configuration value])
+ ])
+m4trace:configure.ac:512: -1- PINEVAR([mailcheck-interval], [DF_MAILCHECK], [150], [Specify default mail-check-interval])
+m4trace:configure.ac:512: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:512: the top level])
+m4trace:configure.ac:512: -1- m4_pattern_allow([^DF_MAILCHECK$])
+m4trace:configure.ac:513: -1- PINEVAR_UNQUOTED([checkpoint-interval], [CHECK_POINT_TIME], [420], [Specify default checkpoint-interval])
+m4trace:configure.ac:513: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:499: PINEVAR_UNQUOTED is expanded from...
+configure.ac:513: the top level])
+m4trace:configure.ac:513: -1- m4_pattern_allow([^CHECK_POINT_TIME$])
+m4trace:configure.ac:514: -1- PINEVAR_UNQUOTED([checkpoint-frequency], [CHECK_POINT_FREQ], [12], [State change count before checkpoint])
+m4trace:configure.ac:514: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:499: PINEVAR_UNQUOTED is expanded from...
+configure.ac:514: the top level])
+m4trace:configure.ac:514: -1- m4_pattern_allow([^CHECK_POINT_FREQ$])
+m4trace:configure.ac:515: -1- PINEVAR_UNQUOTED([display-rows], [DEFAULT_LINES_ON_TERMINAL], [24], [Initial rows on display])
+m4trace:configure.ac:515: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:499: PINEVAR_UNQUOTED is expanded from...
+configure.ac:515: the top level])
+m4trace:configure.ac:515: -1- m4_pattern_allow([^DEFAULT_LINES_ON_TERMINAL$])
+m4trace:configure.ac:516: -1- PINEVAR_UNQUOTED([display-columns], [DEFAULT_COLUMNS_ON_TERMINAL], [80], [Initial columns on display])
+m4trace:configure.ac:516: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:499: PINEVAR_UNQUOTED is expanded from...
+configure.ac:516: the top level])
+m4trace:configure.ac:516: -1- m4_pattern_allow([^DEFAULT_COLUMNS_ON_TERMINAL$])
+m4trace:configure.ac:517: -1- PINEVAR_UNQUOTED([max-display-rows], [MAX_SCREEN_ROWS], [200], [Maximum display rows])
+m4trace:configure.ac:517: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:499: PINEVAR_UNQUOTED is expanded from...
+configure.ac:517: the top level])
+m4trace:configure.ac:517: -1- m4_pattern_allow([^MAX_SCREEN_ROWS$])
+m4trace:configure.ac:518: -1- PINEVAR_UNQUOTED([max-display-columns], [MAX_SCREEN_COLS], [500], [Maximum display columns])
+m4trace:configure.ac:518: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:499: PINEVAR_UNQUOTED is expanded from...
+configure.ac:518: the top level])
+m4trace:configure.ac:518: -1- m4_pattern_allow([^MAX_SCREEN_COLS$])
+m4trace:configure.ac:519: -1- PINEVAR([fill-column], [DF_FILLCOL], [74], [Default fill column])
+m4trace:configure.ac:519: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:519: the top level])
+m4trace:configure.ac:519: -1- m4_pattern_allow([^DF_FILLCOL$])
+m4trace:configure.ac:520: -1- PINEVAR_UNQUOTED([max_fill-column], [MAX_FILLCOL], [80], [Maximum fill column])
+m4trace:configure.ac:520: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:499: PINEVAR_UNQUOTED is expanded from...
+configure.ac:520: the top level])
+m4trace:configure.ac:520: -1- m4_pattern_allow([^MAX_FILLCOL$])
+m4trace:configure.ac:521: -1- PINEVAR_UNQUOTED([debug-level], [DEFAULT_DEBUG], [2], [Specify default debug verbosity level])
+m4trace:configure.ac:521: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:499: PINEVAR_UNQUOTED is expanded from...
+configure.ac:521: the top level])
+m4trace:configure.ac:521: -1- m4_pattern_allow([^DEFAULT_DEBUG$])
+m4trace:configure.ac:522: -1- PINEVAR_UNQUOTED([debug-files], [NUMDEBUGFILES], [4], [Specify number of debug files])
+m4trace:configure.ac:522: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:499: PINEVAR_UNQUOTED is expanded from...
+configure.ac:522: the top level])
+m4trace:configure.ac:522: -1- m4_pattern_allow([^NUMDEBUGFILES$])
+m4trace:configure.ac:523: -1- PINEVAR([debug-file], [DEBUGFILE], [.pine-debug], [Specify debug file name])
+m4trace:configure.ac:523: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:523: the top level])
+m4trace:configure.ac:523: -1- m4_pattern_allow([^DEBUGFILE$])
+m4trace:configure.ac:524: -1- PINEVAR([forwarded-keyword], [FORWARDED_FLAG], ["\$Forwarded"], [IMAP (c-client) keyword to store forwarded status])
+m4trace:configure.ac:524: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:524: the top level])
+m4trace:configure.ac:524: -1- m4_pattern_allow([^FORWARDED_FLAG$])
+m4trace:configure.ac:525: -1- PINEVAR([display-overlap], [DF_OVERLAP], [2], [Lines preserved while paging])
+m4trace:configure.ac:525: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:525: the top level])
+m4trace:configure.ac:525: -1- m4_pattern_allow([^DF_OVERLAP$])
+m4trace:configure.ac:526: -1- PINEVAR([display-margin], [DF_MARGIN], [0], [Lines visible while scrolling])
+m4trace:configure.ac:526: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:526: the top level])
+m4trace:configure.ac:526: -1- m4_pattern_allow([^DF_MARGIN$])
+m4trace:configure.ac:527: -1- PINEVAR([default-fcc], [DF_DEFAULT_FCC], [sent-mail], [Default sent mail folder])
+m4trace:configure.ac:527: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:527: the top level])
+m4trace:configure.ac:527: -1- m4_pattern_allow([^DF_DEFAULT_FCC$])
+m4trace:configure.ac:528: -1- PINEVAR([default-save-folder], [DEFAULT_SAVE], [saved-messages], [Default save folder])
+m4trace:configure.ac:528: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:528: the top level])
+m4trace:configure.ac:528: -1- m4_pattern_allow([^DEFAULT_SAVE$])
+m4trace:configure.ac:529: -1- PINEVAR([default-legacy-postponed-folder], [POSTPONED_MAIL], [postponed-mail], [Pre Pine 3.90 postponed folder])
+m4trace:configure.ac:529: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:529: the top level])
+m4trace:configure.ac:529: -1- m4_pattern_allow([^POSTPONED_MAIL$])
+m4trace:configure.ac:530: -1- PINEVAR([default-postponed-folder], [POSTPONED_MSGS], [postponed-msgs], [Default postponed folder])
+m4trace:configure.ac:530: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:530: the top level])
+m4trace:configure.ac:530: -1- m4_pattern_allow([^POSTPONED_MSGS$])
+m4trace:configure.ac:531: -1- PINEVAR([default-trash-folder], [TRASH_FOLDER], [Trash], [Default Trash folder for Web Alpine])
+m4trace:configure.ac:531: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:531: the top level])
+m4trace:configure.ac:531: -1- m4_pattern_allow([^TRASH_FOLDER$])
+m4trace:configure.ac:532: -1- PINEVAR([default-interrupted-mail], [INTERRUPTED_MAIL], [.pine-interrupted-mail], [Default folder for interrupted mail])
+m4trace:configure.ac:532: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:532: the top level])
+m4trace:configure.ac:532: -1- m4_pattern_allow([^INTERRUPTED_MAIL$])
+m4trace:configure.ac:533: -1- PINEVAR([default-dead-letter-folder], [DEADLETTER], [dead.letter], [Default dead letter folder])
+m4trace:configure.ac:533: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:533: the top level])
+m4trace:configure.ac:533: -1- m4_pattern_allow([^DEADLETTER$])
+m4trace:configure.ac:534: -1- PINEVAR([default-mail-directory], [DF_MAIL_DIRECTORY], [mail], [Default mail directory])
+m4trace:configure.ac:534: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:534: the top level])
+m4trace:configure.ac:534: -1- m4_pattern_allow([^DF_MAIL_DIRECTORY$])
+m4trace:configure.ac:535: -1- PINEVAR([default-inbox-name], [INBOX_NAME], [INBOX], [Default inbox name])
+m4trace:configure.ac:535: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:535: the top level])
+m4trace:configure.ac:535: -1- m4_pattern_allow([^INBOX_NAME$])
+m4trace:configure.ac:536: -1- PINEVAR([default-signature-file], [DF_SIGNATURE_FILE], [.signature], [Default signature file])
+m4trace:configure.ac:536: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:536: the top level])
+m4trace:configure.ac:536: -1- m4_pattern_allow([^DF_SIGNATURE_FILE$])
+m4trace:configure.ac:537: -1- PINEVAR([default-elm-style-save], [DF_ELM_STYLE_SAVE], [no], [Default to Elm style save])
+m4trace:configure.ac:537: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:537: the top level])
+m4trace:configure.ac:537: -1- m4_pattern_allow([^DF_ELM_STYLE_SAVE$])
+m4trace:configure.ac:538: -1- PINEVAR([default-header-in-reply], [DF_HEADER_IN_REPLY], [no], [Include header in reply])
+m4trace:configure.ac:538: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:538: the top level])
+m4trace:configure.ac:538: -1- m4_pattern_allow([^DF_HEADER_IN_REPLY$])
+m4trace:configure.ac:539: -1- PINEVAR([default-old-style-reply], [DF_OLD_STYLE_REPLY], [no], [Default to old style reply])
+m4trace:configure.ac:539: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:539: the top level])
+m4trace:configure.ac:539: -1- m4_pattern_allow([^DF_OLD_STYLE_REPLY$])
+m4trace:configure.ac:540: -1- PINEVAR([default-use-only-domain-name], [DF_USE_ONLY_DOMAIN_NAME], [no], [Default to using only the domain name])
+m4trace:configure.ac:540: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:540: the top level])
+m4trace:configure.ac:540: -1- m4_pattern_allow([^DF_USE_ONLY_DOMAIN_NAME$])
+m4trace:configure.ac:541: -1- PINEVAR([default-save-by-sender], [DF_SAVE_BY_SENDER], [no], [Default to save by sender])
+m4trace:configure.ac:541: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:541: the top level])
+m4trace:configure.ac:541: -1- m4_pattern_allow([^DF_SAVE_BY_SENDER$])
+m4trace:configure.ac:542: -1- PINEVAR([default-sort-key], [DF_SORT_KEY], [arrival], [Default sort key])
+m4trace:configure.ac:542: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:542: the top level])
+m4trace:configure.ac:542: -1- m4_pattern_allow([^DF_SORT_KEY$])
+m4trace:configure.ac:543: -1- PINEVAR([default-addressbook-sort-rule], [DF_AB_SORT_RULE], [fullname-with-lists-last], [Default addressbook sort rule])
+m4trace:configure.ac:543: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:543: the top level])
+m4trace:configure.ac:543: -1- m4_pattern_allow([^DF_AB_SORT_RULE$])
+m4trace:configure.ac:544: -1- PINEVAR([default-folder-sort-rule], [DF_FLD_SORT_RULE], [alphabetical], [Default folder sort rule])
+m4trace:configure.ac:544: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:544: the top level])
+m4trace:configure.ac:544: -1- m4_pattern_allow([^DF_FLD_SORT_RULE$])
+m4trace:configure.ac:545: -1- PINEVAR([default-saved-message-name-rule], [DF_SAVED_MSG_NAME_RULE], [default-folder], [Default saved message name rule])
+m4trace:configure.ac:545: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:545: the top level])
+m4trace:configure.ac:545: -1- m4_pattern_allow([^DF_SAVED_MSG_NAME_RULE$])
+m4trace:configure.ac:546: -1- PINEVAR([default-fcc-rule], [DF_FCC_RULE], [default-fcc], [Default fcc rule])
+m4trace:configure.ac:546: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:546: the top level])
+m4trace:configure.ac:546: -1- m4_pattern_allow([^DF_FCC_RULE$])
+m4trace:configure.ac:547: -1- PINEVAR([default-standard-printer], [DF_STANDARD_PRINTER], [lpr], [Default standard printern])
+m4trace:configure.ac:547: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:547: the top level])
+m4trace:configure.ac:547: -1- m4_pattern_allow([^DF_STANDARD_PRINTER$])
+m4trace:configure.ac:548: -1- PINEVAR([default-ansi-printer], [ANSI_PRINTER], [attached-to-ansi], [ANSI printer definition])
+m4trace:configure.ac:548: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:548: the top level])
+m4trace:configure.ac:548: -1- m4_pattern_allow([^ANSI_PRINTER$])
+m4trace:configure.ac:549: -1- PINEVAR([default-addressbook], [DF_ADDRESSBOOK], [.addressbook], [Default addressbook name])
+m4trace:configure.ac:549: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:549: the top level])
+m4trace:configure.ac:549: -1- m4_pattern_allow([^DF_ADDRESSBOOK$])
+m4trace:configure.ac:550: -1- PINEVAR([default-local-fullname], [DF_LOCAL_FULLNAME], ["Local Support"], [Default local support fullname])
+m4trace:configure.ac:550: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:550: the top level])
+m4trace:configure.ac:550: -1- m4_pattern_allow([^DF_LOCAL_FULLNAME$])
+m4trace:configure.ac:551: -1- PINEVAR([default-local-address], [DF_LOCAL_ADDRESS], [postmaster], [Default local support address])
+m4trace:configure.ac:551: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:551: the top level])
+m4trace:configure.ac:551: -1- m4_pattern_allow([^DF_LOCAL_ADDRESS$])
+m4trace:configure.ac:552: -1- PINEVAR([default-keyboard-lock-count], [DF_KBLOCK_PASSWD_COUNT], [1], [Default keyboard lock count])
+m4trace:configure.ac:552: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:552: the top level])
+m4trace:configure.ac:552: -1- m4_pattern_allow([^DF_KBLOCK_PASSWD_COUNT$])
+m4trace:configure.ac:553: -1- PINEVAR([default-remote-addressbook-history], [DF_REMOTE_ABOOK_HISTORY], [3], [Default address book history count])
+m4trace:configure.ac:553: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:553: the top level])
+m4trace:configure.ac:553: -1- m4_pattern_allow([^DF_REMOTE_ABOOK_HISTORY$])
+m4trace:configure.ac:554: -1- PINEVAR([smime-public-cert-directory], [DF_PUBLICCERT_DIR], [.alpine-smime/public], [Default Public Cert Directory])
+m4trace:configure.ac:554: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:554: the top level])
+m4trace:configure.ac:554: -1- m4_pattern_allow([^DF_PUBLICCERT_DIR$])
+m4trace:configure.ac:555: -1- PINEVAR([smime-private-key-directory], [DF_PRIVATEKEY_DIR], [.alpine-smime/private], [Default Private Key Directory])
+m4trace:configure.ac:555: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:555: the top level])
+m4trace:configure.ac:555: -1- m4_pattern_allow([^DF_PRIVATEKEY_DIR$])
+m4trace:configure.ac:556: -1- PINEVAR([smime-cacert-directory], [DF_CACERT_DIR], [.alpine-smime/ca], [Default Cert Authority Directory])
+m4trace:configure.ac:556: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:556: the top level])
+m4trace:configure.ac:556: -1- m4_pattern_allow([^DF_CACERT_DIR$])
+m4trace:configure.ac:557: -1- PINEVAR_UNQUOTED([default-printer], [DF_DEFAULT_PRINTER], [ANSI_PRINTER], [Default printer])
+m4trace:configure.ac:557: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:499: PINEVAR_UNQUOTED is expanded from...
+configure.ac:557: the top level])
+m4trace:configure.ac:557: -1- m4_pattern_allow([^DF_DEFAULT_PRINTER$])
+m4trace:configure.ac:561: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:561: the top level])
+m4trace:configure.ac:576: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:576: the top level])
+m4trace:configure.ac:583: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:583: the top level])
+m4trace:configure.ac:597: -1- m4_pattern_allow([^PASSFILE$])
+m4trace:configure.ac:602: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:602: the top level])
+m4trace:configure.ac:601: -1- m4_pattern_allow([^DF_SSHPATH$])
+m4trace:configure.ac:617: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:617: the top level])
+m4trace:configure.ac:616: -1- m4_pattern_allow([^DF_SSHCMD$])
+m4trace:configure.ac:633: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:633: the top level])
+m4trace:configure.ac:695: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:695: the top level])
+m4trace:configure.ac:703: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:703: the top level])
+m4trace:configure.ac:711: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:711: the top level])
+m4trace:configure.ac:719: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:719: the top level])
+m4trace:configure.ac:741: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:741: the top level])
+m4trace:configure.ac:750: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:750: the top level])
+m4trace:configure.ac:759: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:759: the top level])
+m4trace:configure.ac:767: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:767: the top level])
+m4trace:configure.ac:777: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:777: the top level])
+m4trace:configure.ac:788: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:788: the top level])
+m4trace:configure.ac:797: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:797: the top level])
+m4trace:configure.ac:805: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:805: the top level])
+m4trace:configure.ac:815: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:815: the top level])
+m4trace:configure.ac:820: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:820: the top level])
+m4trace:configure.ac:828: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:828: the top level])
+m4trace:configure.ac:835: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:835: the top level])
+m4trace:configure.ac:845: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:845: the top level])
+m4trace:configure.ac:849: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:849: the top level])
+m4trace:configure.ac:853: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:853: the top level])
+m4trace:configure.ac:861: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:861: the top level])
+m4trace:configure.ac:871: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:871: the top level])
+m4trace:configure.ac:892: -1- _m4_warn([obsolete], [The macro `AC_ERROR' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/oldnames.m4:34: AC_ERROR is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/libs.m4:100: AC_CHECK_LIB is expanded from...
+configure.ac:892: the top level])
+m4trace:configure.ac:899: -1- _m4_warn([obsolete], [The macro `AC_ERROR' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/oldnames.m4:34: AC_ERROR is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/libs.m4:100: AC_CHECK_LIB is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/libs.m4:100: AC_CHECK_LIB is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/libs.m4:100: AC_CHECK_LIB is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/libs.m4:100: AC_CHECK_LIB is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/libs.m4:100: AC_CHECK_LIB is expanded from...
+configure.ac:899: the top level])
+m4trace:configure.ac:937: -1- m4_pattern_allow([^HAS_TERMINFO$])
+m4trace:configure.ac:940: -1- m4_pattern_allow([^HAS_TERMCAP$])
+m4trace:configure.ac:964: -1- m4_pattern_allow([^ENABLE_LDAP$])
+m4trace:configure.ac:969: -1- m4_pattern_allow([^LDAP_DEPRECATED$])
+m4trace:configure.ac:1053: -1- _m4_warn([cross], [cannot check for file existence when cross compiling], [../../lib/autoconf/general.m4:2778: AC_CHECK_FILE is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/headers.m4:129: _AC_CHECK_HEADER_MONGREL is expanded from...
+../../lib/autoconf/headers.m4:67: AC_CHECK_HEADER is expanded from...
+configure.ac:1053: the top level])
+m4trace:configure.ac:1071: -1- _m4_warn([obsolete], [The macro `AC_ERROR' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/oldnames.m4:34: AC_ERROR is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/libs.m4:48: AC_SEARCH_LIBS is expanded from...
+configure.ac:1071: the top level])
+m4trace:configure.ac:1081: -1- _m4_warn([obsolete], [The macro `AC_ERROR' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/oldnames.m4:34: AC_ERROR is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/headers.m4:129: _AC_CHECK_HEADER_MONGREL is expanded from...
+../../lib/autoconf/headers.m4:67: AC_CHECK_HEADER is expanded from...
+configure.ac:1081: the top level])
+m4trace:configure.ac:1090: -1- m4_pattern_allow([^HAVE_REGEX_H$])
+m4trace:configure.ac:1099: -1- ACX_PTHREAD([
+ AC_MSG_RESULT([yes])
+ case "$target" in
+ *openbsd*)
+ AC_MSG_NOTICE([WARNING: pthread support on OpenBSD is unstable!])
+ AM_CFLAGS="$AM_CFLAGS -pthread"
+ ;;
+ esac
+ LIBS="$PTHREAD_LIBS $LIBS"
+ AM_CFLAGS="$AM_CFLAGS $PTHREAD_CFLAGS"
+ CC="$PTHREAD_CC"
+ AC_DEFINE([HAVE_PTHREAD], [1], [System has pthread support])
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+m4trace:configure.ac:1099: -1- _m4_warn([obsolete], [The macro `AC_LANG_SAVE' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/lang.m4:126: AC_LANG_SAVE is expanded from...
+m4/acx_pthread.m4:49: ACX_PTHREAD is expanded from...
+configure.ac:1099: the top level])
+m4trace:configure.ac:1099: -1- _m4_warn([obsolete], [The macro `AC_LANG_C' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/c.m4:73: AC_LANG_C is expanded from...
+m4/acx_pthread.m4:49: ACX_PTHREAD is expanded from...
+configure.ac:1099: the top level])
+m4trace:configure.ac:1099: -1- m4_pattern_allow([^acx_pthread_config$])
+m4trace:configure.ac:1099: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:2688: AC_TRY_LINK is expanded from...
+m4/acx_pthread.m4:49: ACX_PTHREAD is expanded from...
+configure.ac:1099: the top level])
+m4trace:configure.ac:1099: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:2688: AC_TRY_LINK is expanded from...
+m4/acx_pthread.m4:49: ACX_PTHREAD is expanded from...
+configure.ac:1099: the top level])
+m4trace:configure.ac:1099: -1- m4_pattern_allow([^PTHREAD_CREATE_JOINABLE$])
+m4trace:configure.ac:1099: -1- m4_pattern_allow([^PTHREAD_CC$])
+m4trace:configure.ac:1099: -1- m4_pattern_allow([^PTHREAD_LIBS$])
+m4trace:configure.ac:1099: -1- m4_pattern_allow([^PTHREAD_CFLAGS$])
+m4trace:configure.ac:1099: -1- m4_pattern_allow([^PTHREAD_CC$])
+m4trace:configure.ac:1099: -2- m4_pattern_allow([^HAVE_PTHREAD$])
+m4trace:configure.ac:1099: -1- m4_pattern_allow([^HAVE_PTHREAD$])
+m4trace:configure.ac:1099: -1- _m4_warn([obsolete], [The macro `AC_LANG_RESTORE' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/lang.m4:135: AC_LANG_RESTORE is expanded from...
+m4/acx_pthread.m4:49: ACX_PTHREAD is expanded from...
+configure.ac:1099: the top level])
+m4trace:configure.ac:1117: -1- m4_pattern_allow([^HAVE_NANOSLEEP$])
+m4trace:configure.ac:1127: -1- m4_pattern_allow([^STDC_HEADERS$])
+m4trace:configure.ac:1129: -1- m4_pattern_allow([^STAT_MACROS_BROKEN$])
+m4trace:configure.ac:1130: -1- m4_pattern_allow([^HAVE_SYS_WAIT_H$])
+m4trace:configure.ac:1131: -1- m4_pattern_allow([^TIME_WITH_SYS_TIME$])
+m4trace:configure.ac:1132: -1- m4_pattern_allow([^GWINSZ_IN_SYS_IOCTL$])
+m4trace:configure.ac:1134: -1- _m4_warn([obsolete], [The macro `AC_UNISTD_H' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/headers.m4:824: AC_UNISTD_H is expanded from...
+configure.ac:1134: the top level])
+m4trace:configure.ac:1134: -1- m4_pattern_allow([^HAVE_UNISTD_H$])
+m4trace:configure.ac:1163: -2- m4_pattern_allow([^HAS_TERMIOS$])
+m4trace:configure.ac:1162: -2- m4_pattern_allow([^HAS_TERMIO$])
+m4trace:configure.ac:1162: -2- m4_pattern_allow([^HAS_SGTTY$])
+m4trace:configure.ac:1162: -1- _m4_warn([obsolete], [The macro `AC_ERROR' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/oldnames.m4:34: AC_ERROR is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/headers.m4:129: _AC_CHECK_HEADER_MONGREL is expanded from...
+../../lib/autoconf/headers.m4:67: AC_CHECK_HEADER is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/headers.m4:129: _AC_CHECK_HEADER_MONGREL is expanded from...
+../../lib/autoconf/headers.m4:67: AC_CHECK_HEADER is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/headers.m4:129: _AC_CHECK_HEADER_MONGREL is expanded from...
+../../lib/autoconf/headers.m4:67: AC_CHECK_HEADER is expanded from...
+configure.ac:1162: the top level])
+m4trace:configure.ac:1179: -1- _m4_warn([obsolete], [The macro `AC_TYPE_SIGNAL' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/types.m4:738: AC_TYPE_SIGNAL is expanded from...
+configure.ac:1179: the top level])
+m4trace:configure.ac:1179: -1- m4_pattern_allow([^RETSIGTYPE$])
+m4trace:configure.ac:1180: -1- m4_pattern_allow([^size_t$])
+m4trace:configure.ac:1181: -1- m4_pattern_allow([^mode_t$])
+m4trace:configure.ac:1182: -1- m4_pattern_allow([^pid_t$])
+m4trace:configure.ac:1183: -1- m4_pattern_allow([^uid_t$])
+m4trace:configure.ac:1183: -1- m4_pattern_allow([^gid_t$])
+m4trace:configure.ac:1184: -1- m4_pattern_allow([^TM_IN_SYS_TIME$])
+m4trace:configure.ac:1186: -1- m4_pattern_allow([^HAVE_UNION_WAIT$])
+m4trace:configure.ac:1188: -1- m4_pattern_allow([^HAVE_STDINT_H$])
+m4trace:configure.ac:1188: -1- m4_pattern_allow([^HAVE_INTTYPES_H$])
+m4trace:configure.ac:1188: -1- m4_pattern_allow([^HAVE_SYS_TYPES_H$])
+m4trace:configure.ac:1188: -1- m4_pattern_allow([^SIZEOF_UNSIGNED_SHORT$])
+m4trace:configure.ac:1188: -1- m4_pattern_allow([^SIZEOF_UNSIGNED_INT$])
+m4trace:configure.ac:1188: -1- _m4_warn([obsolete], [The macro `AC_ERROR' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/oldnames.m4:34: AC_ERROR is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/headers.m4:129: _AC_CHECK_HEADER_MONGREL is expanded from...
+../../lib/autoconf/headers.m4:67: AC_CHECK_HEADER is expanded from...
+../../lib/m4sugar/m4sh.m4:574: AS_FOR is expanded from...
+../../lib/autoconf/headers.m4:249: AC_CHECK_HEADERS is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/headers.m4:129: _AC_CHECK_HEADER_MONGREL is expanded from...
+../../lib/autoconf/headers.m4:67: AC_CHECK_HEADER is expanded from...
+../../lib/m4sugar/m4sh.m4:574: AS_FOR is expanded from...
+../../lib/autoconf/headers.m4:249: AC_CHECK_HEADERS is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/headers.m4:129: _AC_CHECK_HEADER_MONGREL is expanded from...
+../../lib/autoconf/headers.m4:67: AC_CHECK_HEADER is expanded from...
+../../lib/m4sugar/m4sh.m4:574: AS_FOR is expanded from...
+../../lib/autoconf/headers.m4:249: AC_CHECK_HEADERS is expanded from...
+configure.ac:1188: the top level])
+m4trace:configure.ac:1205: -1- m4_pattern_allow([^UINT16$])
+m4trace:configure.ac:1207: -1- m4_pattern_allow([^HAVE_STDINT_H$])
+m4trace:configure.ac:1207: -1- m4_pattern_allow([^HAVE_INTTYPES_H$])
+m4trace:configure.ac:1207: -1- m4_pattern_allow([^HAVE_SYS_TYPES_H$])
+m4trace:configure.ac:1207: -1- m4_pattern_allow([^SIZEOF_UNSIGNED_INT$])
+m4trace:configure.ac:1207: -1- m4_pattern_allow([^SIZEOF_UNSIGNED_LONG$])
+m4trace:configure.ac:1207: -1- _m4_warn([obsolete], [The macro `AC_ERROR' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/oldnames.m4:34: AC_ERROR is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/headers.m4:129: _AC_CHECK_HEADER_MONGREL is expanded from...
+../../lib/autoconf/headers.m4:67: AC_CHECK_HEADER is expanded from...
+../../lib/m4sugar/m4sh.m4:574: AS_FOR is expanded from...
+../../lib/autoconf/headers.m4:249: AC_CHECK_HEADERS is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/headers.m4:129: _AC_CHECK_HEADER_MONGREL is expanded from...
+../../lib/autoconf/headers.m4:67: AC_CHECK_HEADER is expanded from...
+../../lib/m4sugar/m4sh.m4:574: AS_FOR is expanded from...
+../../lib/autoconf/headers.m4:249: AC_CHECK_HEADERS is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/headers.m4:129: _AC_CHECK_HEADER_MONGREL is expanded from...
+../../lib/autoconf/headers.m4:67: AC_CHECK_HEADER is expanded from...
+../../lib/m4sugar/m4sh.m4:574: AS_FOR is expanded from...
+../../lib/autoconf/headers.m4:249: AC_CHECK_HEADERS is expanded from...
+configure.ac:1207: the top level])
+m4trace:configure.ac:1224: -1- m4_pattern_allow([^UINT32$])
+m4trace:configure.ac:1226: -1- _m4_warn([obsolete], [The macro `AC_TRY_COMPILE' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:2615: AC_TRY_COMPILE is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/general.m4:2032: AC_CACHE_VAL is expanded from...
+../../lib/autoconf/general.m4:2053: AC_CACHE_CHECK is expanded from...
+configure.ac:1226: the top level])
+m4trace:configure.ac:1243: -1- m4_pattern_allow([^qsort_t$])
+m4trace:configure.ac:1247: -1- m4_pattern_allow([^SELECT_TYPE_ARG1$])
+m4trace:configure.ac:1247: -1- m4_pattern_allow([^SELECT_TYPE_ARG234$])
+m4trace:configure.ac:1247: -1- m4_pattern_allow([^SELECT_TYPE_ARG5$])
+m4trace:configure.ac:1249: -1- m4_pattern_allow([^HAVE_STRCOLL$])
+m4trace:configure.ac:1253: -1- m4_pattern_allow([^HAVE_VFORK_H$])
+m4trace:configure.ac:1253: -1- m4_pattern_allow([^HAVE_WORKING_VFORK$])
+m4trace:configure.ac:1253: -1- m4_pattern_allow([^vfork$])
+m4trace:configure.ac:1253: -1- m4_pattern_allow([^HAVE_WORKING_FORK$])
+m4trace:configure.ac:1300: -1- m4_pattern_allow([^POSIX_SIGNALS$])
+m4trace:configure.ac:1300: -1- m4_pattern_allow([^SYSV_SIGNALS$])
+m4trace:configure.ac:1311: -1- m4_pattern_allow([^HAVE_SYSLOG$])
+m4trace:configure.ac:1375: -1- m4_pattern_allow([^OSX_TARGET$])
+m4trace:configure.ac:1380: -1- m4_pattern_allow([^APPLEKEYCHAIN$])
+m4trace:configure.ac:1398: -1- m4_pattern_allow([^__EXTENSIONS__$])
+m4trace:configure.ac:1503: -1- m4_pattern_allow([^_WINDOWS$])
+m4trace:configure.ac:1506: -1- _m4_warn([obsolete], [The macro `AC_ERROR' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/oldnames.m4:34: AC_ERROR is expanded from...
+configure.ac:1506: the top level])
+m4trace:configure.ac:1526: -1- m4_pattern_allow([^SYSTYPE$])
+m4trace:configure.ac:1527: -1- m4_pattern_allow([^C_FILESEP$])
+m4trace:configure.ac:1528: -1- m4_pattern_allow([^S_FILESEP$])
+m4trace:configure.ac:1529: -1- m4_pattern_allow([^MAILDIR$])
+m4trace:configure.ac:1530: -1- m4_pattern_allow([^MODE_READONLY$])
+m4trace:configure.ac:1533: -1- m4_pattern_allow([^C_CLIENT_TARGET$])
+m4trace:configure.ac:1534: -1- m4_pattern_allow([^C_CLIENT_WITH_IPV6$])
+m4trace:configure.ac:1551: -1- m4_pattern_allow([^SMIME$])
+m4trace:configure.ac:1552: -1- m4_pattern_allow([^SMIME_SSLCERTS$])
+m4trace:configure.ac:1601: -1- m4_pattern_allow([^C_CLIENT_CFLAGS$])
+m4trace:configure.ac:1612: -1- m4_pattern_allow([^C_CLIENT_LDFLAGS$])
+m4trace:configure.ac:1616: -1- m4_pattern_allow([^C_CLIENT_GCCOPTLEVEL$])
+m4trace:configure.ac:1619: -1- m4_pattern_allow([^C_CLIENT_SPECIALS$])
+m4trace:configure.ac:1639: -1- m4_pattern_allow([^PUBCOOKIE$])
+m4trace:configure.ac:1646: -1- m4_pattern_allow([^REGEX_BUILD$])
+m4trace:configure.ac:1648: -1- m4_pattern_allow([^WEB_BUILD$])
+m4trace:configure.ac:1649: -1- m4_pattern_allow([^WEB_BINDIR$])
+m4trace:configure.ac:1650: -1- m4_pattern_allow([^WEB_PUBCOOKIE_BUILD$])
+m4trace:configure.ac:1651: -1- m4_pattern_allow([^WEB_PUBCOOKIE_LIB$])
+m4trace:configure.ac:1652: -1- m4_pattern_allow([^WEB_PUBCOOKIE_LINK$])
+m4trace:configure.ac:1654: -1- m4_pattern_allow([^AM_CFLAGS$])
+m4trace:configure.ac:1655: -1- m4_pattern_allow([^AM_LDFLAGS$])
+m4trace:configure.ac:1657: -1- _m4_warn([obsolete], [AC_OUTPUT should be used without arguments.
+You should run autoupdate.], [])
+m4trace:configure.ac:1657: -1- m4_pattern_allow([^LIB@&t@OBJS$])
+m4trace:configure.ac:1657: -1- m4_pattern_allow([^LTLIBOBJS$])
+m4trace:configure.ac:1657: -1- AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])
+m4trace:configure.ac:1657: -1- m4_pattern_allow([^am__EXEEXT_TRUE$])
+m4trace:configure.ac:1657: -1- m4_pattern_allow([^am__EXEEXT_FALSE$])
+m4trace:configure.ac:1657: -1- _AM_SUBST_NOTMAKE([am__EXEEXT_TRUE])
+m4trace:configure.ac:1657: -1- _AM_SUBST_NOTMAKE([am__EXEEXT_FALSE])
+m4trace:configure.ac:1657: -1- _AC_AM_CONFIG_HEADER_HOOK(["$ac_file"])
+m4trace:configure.ac:1657: -1- _AM_OUTPUT_DEPENDENCY_COMMANDS
+m4trace:configure.ac:1657: -1- _LT_PROG_LTMAIN
diff --git a/autom4te.cache/traces.1 b/autom4te.cache/traces.1
new file mode 100644
index 00000000..587c8cec
--- /dev/null
+++ b/autom4te.cache/traces.1
@@ -0,0 +1,1893 @@
+m4trace:aclocal.m4:992: -1- m4_include([m4/acx_pthread.m4])
+m4trace:aclocal.m4:993: -1- m4_include([m4/gettext.m4])
+m4trace:aclocal.m4:994: -1- m4_include([m4/iconv.m4])
+m4trace:aclocal.m4:995: -1- m4_include([m4/lib-ld.m4])
+m4trace:aclocal.m4:996: -1- m4_include([m4/lib-link.m4])
+m4trace:aclocal.m4:997: -1- m4_include([m4/lib-prefix.m4])
+m4trace:aclocal.m4:998: -1- m4_include([m4/libtool.m4])
+m4trace:aclocal.m4:999: -1- m4_include([m4/ltoptions.m4])
+m4trace:aclocal.m4:1000: -1- m4_include([m4/ltsugar.m4])
+m4trace:aclocal.m4:1001: -1- m4_include([m4/ltversion.m4])
+m4trace:aclocal.m4:1002: -1- m4_include([m4/lt~obsolete.m4])
+m4trace:aclocal.m4:1003: -1- m4_include([m4/nls.m4])
+m4trace:aclocal.m4:1004: -1- m4_include([m4/po.m4])
+m4trace:aclocal.m4:1005: -1- m4_include([m4/progtest.m4])
+m4trace:configure.ac:20: -3- m4_include([VERSION])
+m4trace:configure.ac:20: -1- AC_INIT([alpine], [2.10], [alpine-contact@u.washington.edu])
+m4trace:configure.ac:20: -1- m4_pattern_forbid([^_?A[CHUM]_])
+m4trace:configure.ac:20: -1- m4_pattern_forbid([_AC_])
+m4trace:configure.ac:20: -1- m4_pattern_forbid([^LIBOBJS$], [do not use LIBOBJS directly, use AC_LIBOBJ (see section `AC_LIBOBJ vs LIBOBJS'])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^AS_FLAGS$])
+m4trace:configure.ac:20: -1- m4_pattern_forbid([^_?m4_])
+m4trace:configure.ac:20: -1- m4_pattern_forbid([^dnl$])
+m4trace:configure.ac:20: -1- m4_pattern_forbid([^_?AS_])
+m4trace:configure.ac:20: -1- AC_SUBST([SHELL])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([SHELL])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^SHELL$])
+m4trace:configure.ac:20: -1- AC_SUBST([PATH_SEPARATOR])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([PATH_SEPARATOR])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PATH_SEPARATOR$])
+m4trace:configure.ac:20: -1- AC_SUBST([PACKAGE_NAME], [m4_ifdef([AC_PACKAGE_NAME], ['AC_PACKAGE_NAME'])])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([PACKAGE_NAME])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_NAME$])
+m4trace:configure.ac:20: -1- AC_SUBST([PACKAGE_TARNAME], [m4_ifdef([AC_PACKAGE_TARNAME], ['AC_PACKAGE_TARNAME'])])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([PACKAGE_TARNAME])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_TARNAME$])
+m4trace:configure.ac:20: -1- AC_SUBST([PACKAGE_VERSION], [m4_ifdef([AC_PACKAGE_VERSION], ['AC_PACKAGE_VERSION'])])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([PACKAGE_VERSION])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_VERSION$])
+m4trace:configure.ac:20: -1- AC_SUBST([PACKAGE_STRING], [m4_ifdef([AC_PACKAGE_STRING], ['AC_PACKAGE_STRING'])])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([PACKAGE_STRING])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_STRING$])
+m4trace:configure.ac:20: -1- AC_SUBST([PACKAGE_BUGREPORT], [m4_ifdef([AC_PACKAGE_BUGREPORT], ['AC_PACKAGE_BUGREPORT'])])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([PACKAGE_BUGREPORT])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_BUGREPORT$])
+m4trace:configure.ac:20: -1- AC_SUBST([PACKAGE_URL], [m4_ifdef([AC_PACKAGE_URL], ['AC_PACKAGE_URL'])])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([PACKAGE_URL])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_URL$])
+m4trace:configure.ac:20: -1- AC_SUBST([exec_prefix], [NONE])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([exec_prefix])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^exec_prefix$])
+m4trace:configure.ac:20: -1- AC_SUBST([prefix], [NONE])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([prefix])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^prefix$])
+m4trace:configure.ac:20: -1- AC_SUBST([program_transform_name], [s,x,x,])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([program_transform_name])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^program_transform_name$])
+m4trace:configure.ac:20: -1- AC_SUBST([bindir], ['${exec_prefix}/bin'])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([bindir])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^bindir$])
+m4trace:configure.ac:20: -1- AC_SUBST([sbindir], ['${exec_prefix}/sbin'])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([sbindir])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^sbindir$])
+m4trace:configure.ac:20: -1- AC_SUBST([libexecdir], ['${exec_prefix}/libexec'])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([libexecdir])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^libexecdir$])
+m4trace:configure.ac:20: -1- AC_SUBST([datarootdir], ['${prefix}/share'])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([datarootdir])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^datarootdir$])
+m4trace:configure.ac:20: -1- AC_SUBST([datadir], ['${datarootdir}'])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([datadir])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^datadir$])
+m4trace:configure.ac:20: -1- AC_SUBST([sysconfdir], ['${prefix}/etc'])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([sysconfdir])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^sysconfdir$])
+m4trace:configure.ac:20: -1- AC_SUBST([sharedstatedir], ['${prefix}/com'])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([sharedstatedir])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^sharedstatedir$])
+m4trace:configure.ac:20: -1- AC_SUBST([localstatedir], ['${prefix}/var'])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([localstatedir])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^localstatedir$])
+m4trace:configure.ac:20: -1- AC_SUBST([includedir], ['${prefix}/include'])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([includedir])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^includedir$])
+m4trace:configure.ac:20: -1- AC_SUBST([oldincludedir], ['/usr/include'])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([oldincludedir])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^oldincludedir$])
+m4trace:configure.ac:20: -1- AC_SUBST([docdir], [m4_ifset([AC_PACKAGE_TARNAME],
+ ['${datarootdir}/doc/${PACKAGE_TARNAME}'],
+ ['${datarootdir}/doc/${PACKAGE}'])])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([docdir])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^docdir$])
+m4trace:configure.ac:20: -1- AC_SUBST([infodir], ['${datarootdir}/info'])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([infodir])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^infodir$])
+m4trace:configure.ac:20: -1- AC_SUBST([htmldir], ['${docdir}'])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([htmldir])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^htmldir$])
+m4trace:configure.ac:20: -1- AC_SUBST([dvidir], ['${docdir}'])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([dvidir])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^dvidir$])
+m4trace:configure.ac:20: -1- AC_SUBST([pdfdir], ['${docdir}'])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([pdfdir])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^pdfdir$])
+m4trace:configure.ac:20: -1- AC_SUBST([psdir], ['${docdir}'])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([psdir])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^psdir$])
+m4trace:configure.ac:20: -1- AC_SUBST([libdir], ['${exec_prefix}/lib'])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([libdir])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^libdir$])
+m4trace:configure.ac:20: -1- AC_SUBST([localedir], ['${datarootdir}/locale'])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([localedir])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^localedir$])
+m4trace:configure.ac:20: -1- AC_SUBST([mandir], ['${datarootdir}/man'])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([mandir])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^mandir$])
+m4trace:configure.ac:20: -1- AC_DEFINE_TRACE_LITERAL([PACKAGE_NAME])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_NAME$])
+m4trace:configure.ac:20: -1- AH_OUTPUT([PACKAGE_NAME], [/* Define to the full name of this package. */
+@%:@undef PACKAGE_NAME])
+m4trace:configure.ac:20: -1- AC_DEFINE_TRACE_LITERAL([PACKAGE_TARNAME])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_TARNAME$])
+m4trace:configure.ac:20: -1- AH_OUTPUT([PACKAGE_TARNAME], [/* Define to the one symbol short name of this package. */
+@%:@undef PACKAGE_TARNAME])
+m4trace:configure.ac:20: -1- AC_DEFINE_TRACE_LITERAL([PACKAGE_VERSION])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_VERSION$])
+m4trace:configure.ac:20: -1- AH_OUTPUT([PACKAGE_VERSION], [/* Define to the version of this package. */
+@%:@undef PACKAGE_VERSION])
+m4trace:configure.ac:20: -1- AC_DEFINE_TRACE_LITERAL([PACKAGE_STRING])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_STRING$])
+m4trace:configure.ac:20: -1- AH_OUTPUT([PACKAGE_STRING], [/* Define to the full name and version of this package. */
+@%:@undef PACKAGE_STRING])
+m4trace:configure.ac:20: -1- AC_DEFINE_TRACE_LITERAL([PACKAGE_BUGREPORT])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_BUGREPORT$])
+m4trace:configure.ac:20: -1- AH_OUTPUT([PACKAGE_BUGREPORT], [/* Define to the address where bug reports for this package should be sent. */
+@%:@undef PACKAGE_BUGREPORT])
+m4trace:configure.ac:20: -1- AC_DEFINE_TRACE_LITERAL([PACKAGE_URL])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_URL$])
+m4trace:configure.ac:20: -1- AH_OUTPUT([PACKAGE_URL], [/* Define to the home page for this package. */
+@%:@undef PACKAGE_URL])
+m4trace:configure.ac:20: -1- AC_SUBST([DEFS])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([DEFS])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^DEFS$])
+m4trace:configure.ac:20: -1- AC_SUBST([ECHO_C])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([ECHO_C])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^ECHO_C$])
+m4trace:configure.ac:20: -1- AC_SUBST([ECHO_N])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([ECHO_N])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^ECHO_N$])
+m4trace:configure.ac:20: -1- AC_SUBST([ECHO_T])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([ECHO_T])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^ECHO_T$])
+m4trace:configure.ac:20: -1- AC_SUBST([LIBS])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([LIBS])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^LIBS$])
+m4trace:configure.ac:20: -1- AC_SUBST([build_alias])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([build_alias])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^build_alias$])
+m4trace:configure.ac:20: -1- AC_SUBST([host_alias])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([host_alias])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^host_alias$])
+m4trace:configure.ac:20: -1- AC_SUBST([target_alias])
+m4trace:configure.ac:20: -1- AC_SUBST_TRACE([target_alias])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^target_alias$])
+m4trace:configure.ac:23: -1- AC_CONFIG_HEADERS([include/config.h])
+m4trace:configure.ac:25: -1- AM_INIT_AUTOMAKE([foreign nostdinc])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^AM_[A-Z]+FLAGS$])
+m4trace:configure.ac:25: -1- AM_AUTOMAKE_VERSION([1.11.1])
+m4trace:configure.ac:25: -1- AC_REQUIRE_AUX_FILE([install-sh])
+m4trace:configure.ac:25: -1- AC_SUBST([INSTALL_PROGRAM])
+m4trace:configure.ac:25: -1- AC_SUBST_TRACE([INSTALL_PROGRAM])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^INSTALL_PROGRAM$])
+m4trace:configure.ac:25: -1- AC_SUBST([INSTALL_SCRIPT])
+m4trace:configure.ac:25: -1- AC_SUBST_TRACE([INSTALL_SCRIPT])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^INSTALL_SCRIPT$])
+m4trace:configure.ac:25: -1- AC_SUBST([INSTALL_DATA])
+m4trace:configure.ac:25: -1- AC_SUBST_TRACE([INSTALL_DATA])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^INSTALL_DATA$])
+m4trace:configure.ac:25: -1- AC_SUBST([am__isrc], [' -I$(srcdir)'])
+m4trace:configure.ac:25: -1- AC_SUBST_TRACE([am__isrc])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^am__isrc$])
+m4trace:configure.ac:25: -1- _AM_SUBST_NOTMAKE([am__isrc])
+m4trace:configure.ac:25: -1- AC_SUBST([CYGPATH_W])
+m4trace:configure.ac:25: -1- AC_SUBST_TRACE([CYGPATH_W])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^CYGPATH_W$])
+m4trace:configure.ac:25: -1- AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])
+m4trace:configure.ac:25: -1- AC_SUBST_TRACE([PACKAGE])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^PACKAGE$])
+m4trace:configure.ac:25: -1- AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])
+m4trace:configure.ac:25: -1- AC_SUBST_TRACE([VERSION])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^VERSION$])
+m4trace:configure.ac:25: -1- AC_DEFINE_TRACE_LITERAL([PACKAGE])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^PACKAGE$])
+m4trace:configure.ac:25: -1- AH_OUTPUT([PACKAGE], [/* Name of package */
+@%:@undef PACKAGE])
+m4trace:configure.ac:25: -1- AC_DEFINE_TRACE_LITERAL([VERSION])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^VERSION$])
+m4trace:configure.ac:25: -1- AH_OUTPUT([VERSION], [/* Version number of package */
+@%:@undef VERSION])
+m4trace:configure.ac:25: -1- AC_REQUIRE_AUX_FILE([missing])
+m4trace:configure.ac:25: -1- AC_SUBST([ACLOCAL])
+m4trace:configure.ac:25: -1- AC_SUBST_TRACE([ACLOCAL])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^ACLOCAL$])
+m4trace:configure.ac:25: -1- AC_SUBST([AUTOCONF])
+m4trace:configure.ac:25: -1- AC_SUBST_TRACE([AUTOCONF])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^AUTOCONF$])
+m4trace:configure.ac:25: -1- AC_SUBST([AUTOMAKE])
+m4trace:configure.ac:25: -1- AC_SUBST_TRACE([AUTOMAKE])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^AUTOMAKE$])
+m4trace:configure.ac:25: -1- AC_SUBST([AUTOHEADER])
+m4trace:configure.ac:25: -1- AC_SUBST_TRACE([AUTOHEADER])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^AUTOHEADER$])
+m4trace:configure.ac:25: -1- AC_SUBST([MAKEINFO])
+m4trace:configure.ac:25: -1- AC_SUBST_TRACE([MAKEINFO])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^MAKEINFO$])
+m4trace:configure.ac:25: -1- AC_SUBST([install_sh])
+m4trace:configure.ac:25: -1- AC_SUBST_TRACE([install_sh])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^install_sh$])
+m4trace:configure.ac:25: -1- AC_SUBST([STRIP])
+m4trace:configure.ac:25: -1- AC_SUBST_TRACE([STRIP])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^STRIP$])
+m4trace:configure.ac:25: -1- AC_SUBST([INSTALL_STRIP_PROGRAM])
+m4trace:configure.ac:25: -1- AC_SUBST_TRACE([INSTALL_STRIP_PROGRAM])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^INSTALL_STRIP_PROGRAM$])
+m4trace:configure.ac:25: -1- AC_REQUIRE_AUX_FILE([install-sh])
+m4trace:configure.ac:25: -1- AC_SUBST([MKDIR_P])
+m4trace:configure.ac:25: -1- AC_SUBST_TRACE([MKDIR_P])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^MKDIR_P$])
+m4trace:configure.ac:25: -1- AC_SUBST([mkdir_p], ["$MKDIR_P"])
+m4trace:configure.ac:25: -1- AC_SUBST_TRACE([mkdir_p])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^mkdir_p$])
+m4trace:configure.ac:25: -1- AC_SUBST([AWK])
+m4trace:configure.ac:25: -1- AC_SUBST_TRACE([AWK])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^AWK$])
+m4trace:configure.ac:25: -1- AC_SUBST([SET_MAKE])
+m4trace:configure.ac:25: -1- AC_SUBST_TRACE([SET_MAKE])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^SET_MAKE$])
+m4trace:configure.ac:25: -1- AC_SUBST([am__leading_dot])
+m4trace:configure.ac:25: -1- AC_SUBST_TRACE([am__leading_dot])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^am__leading_dot$])
+m4trace:configure.ac:25: -1- AC_SUBST([AMTAR])
+m4trace:configure.ac:25: -1- AC_SUBST_TRACE([AMTAR])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^AMTAR$])
+m4trace:configure.ac:25: -1- AC_SUBST([am__tar])
+m4trace:configure.ac:25: -1- AC_SUBST_TRACE([am__tar])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^am__tar$])
+m4trace:configure.ac:25: -1- AC_SUBST([am__untar])
+m4trace:configure.ac:25: -1- AC_SUBST_TRACE([am__untar])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^am__untar$])
+m4trace:configure.ac:27: -1- AM_MAINTAINER_MODE
+m4trace:configure.ac:27: -1- AM_CONDITIONAL([MAINTAINER_MODE], [test $USE_MAINTAINER_MODE = yes])
+m4trace:configure.ac:27: -1- AC_SUBST([MAINTAINER_MODE_TRUE])
+m4trace:configure.ac:27: -1- AC_SUBST_TRACE([MAINTAINER_MODE_TRUE])
+m4trace:configure.ac:27: -1- m4_pattern_allow([^MAINTAINER_MODE_TRUE$])
+m4trace:configure.ac:27: -1- AC_SUBST([MAINTAINER_MODE_FALSE])
+m4trace:configure.ac:27: -1- AC_SUBST_TRACE([MAINTAINER_MODE_FALSE])
+m4trace:configure.ac:27: -1- m4_pattern_allow([^MAINTAINER_MODE_FALSE$])
+m4trace:configure.ac:27: -1- _AM_SUBST_NOTMAKE([MAINTAINER_MODE_TRUE])
+m4trace:configure.ac:27: -1- _AM_SUBST_NOTMAKE([MAINTAINER_MODE_FALSE])
+m4trace:configure.ac:27: -1- AC_SUBST([MAINT])
+m4trace:configure.ac:27: -1- AC_SUBST_TRACE([MAINT])
+m4trace:configure.ac:27: -1- m4_pattern_allow([^MAINT$])
+m4trace:configure.ac:29: -1- AC_CANONICAL_HOST
+m4trace:configure.ac:29: -1- AC_CANONICAL_BUILD
+m4trace:configure.ac:29: -1- AC_REQUIRE_AUX_FILE([config.sub])
+m4trace:configure.ac:29: -1- AC_REQUIRE_AUX_FILE([config.guess])
+m4trace:configure.ac:29: -1- AC_SUBST([build], [$ac_cv_build])
+m4trace:configure.ac:29: -1- AC_SUBST_TRACE([build])
+m4trace:configure.ac:29: -1- m4_pattern_allow([^build$])
+m4trace:configure.ac:29: -1- AC_SUBST([build_cpu], [$[1]])
+m4trace:configure.ac:29: -1- AC_SUBST_TRACE([build_cpu])
+m4trace:configure.ac:29: -1- m4_pattern_allow([^build_cpu$])
+m4trace:configure.ac:29: -1- AC_SUBST([build_vendor], [$[2]])
+m4trace:configure.ac:29: -1- AC_SUBST_TRACE([build_vendor])
+m4trace:configure.ac:29: -1- m4_pattern_allow([^build_vendor$])
+m4trace:configure.ac:29: -1- AC_SUBST([build_os])
+m4trace:configure.ac:29: -1- AC_SUBST_TRACE([build_os])
+m4trace:configure.ac:29: -1- m4_pattern_allow([^build_os$])
+m4trace:configure.ac:29: -1- AC_SUBST([host], [$ac_cv_host])
+m4trace:configure.ac:29: -1- AC_SUBST_TRACE([host])
+m4trace:configure.ac:29: -1- m4_pattern_allow([^host$])
+m4trace:configure.ac:29: -1- AC_SUBST([host_cpu], [$[1]])
+m4trace:configure.ac:29: -1- AC_SUBST_TRACE([host_cpu])
+m4trace:configure.ac:29: -1- m4_pattern_allow([^host_cpu$])
+m4trace:configure.ac:29: -1- AC_SUBST([host_vendor], [$[2]])
+m4trace:configure.ac:29: -1- AC_SUBST_TRACE([host_vendor])
+m4trace:configure.ac:29: -1- m4_pattern_allow([^host_vendor$])
+m4trace:configure.ac:29: -1- AC_SUBST([host_os])
+m4trace:configure.ac:29: -1- AC_SUBST_TRACE([host_os])
+m4trace:configure.ac:29: -1- m4_pattern_allow([^host_os$])
+m4trace:configure.ac:40: -1- AC_SUBST([CC])
+m4trace:configure.ac:40: -1- AC_SUBST_TRACE([CC])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^CC$])
+m4trace:configure.ac:40: -1- AC_SUBST([CFLAGS])
+m4trace:configure.ac:40: -1- AC_SUBST_TRACE([CFLAGS])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^CFLAGS$])
+m4trace:configure.ac:40: -1- AC_SUBST([LDFLAGS])
+m4trace:configure.ac:40: -1- AC_SUBST_TRACE([LDFLAGS])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^LDFLAGS$])
+m4trace:configure.ac:40: -1- AC_SUBST([LIBS])
+m4trace:configure.ac:40: -1- AC_SUBST_TRACE([LIBS])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^LIBS$])
+m4trace:configure.ac:40: -1- AC_SUBST([CPPFLAGS])
+m4trace:configure.ac:40: -1- AC_SUBST_TRACE([CPPFLAGS])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^CPPFLAGS$])
+m4trace:configure.ac:40: -1- AC_SUBST([CC])
+m4trace:configure.ac:40: -1- AC_SUBST_TRACE([CC])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^CC$])
+m4trace:configure.ac:40: -1- AC_SUBST([CC])
+m4trace:configure.ac:40: -1- AC_SUBST_TRACE([CC])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^CC$])
+m4trace:configure.ac:40: -1- AC_SUBST([CC])
+m4trace:configure.ac:40: -1- AC_SUBST_TRACE([CC])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^CC$])
+m4trace:configure.ac:40: -1- AC_SUBST([CC])
+m4trace:configure.ac:40: -1- AC_SUBST_TRACE([CC])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^CC$])
+m4trace:configure.ac:40: -1- AC_SUBST([ac_ct_CC])
+m4trace:configure.ac:40: -1- AC_SUBST_TRACE([ac_ct_CC])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^ac_ct_CC$])
+m4trace:configure.ac:40: -1- AC_SUBST([EXEEXT], [$ac_cv_exeext])
+m4trace:configure.ac:40: -1- AC_SUBST_TRACE([EXEEXT])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^EXEEXT$])
+m4trace:configure.ac:40: -1- AC_SUBST([OBJEXT], [$ac_cv_objext])
+m4trace:configure.ac:40: -1- AC_SUBST_TRACE([OBJEXT])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^OBJEXT$])
+m4trace:configure.ac:40: -1- AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])
+m4trace:configure.ac:40: -1- AC_SUBST_TRACE([DEPDIR])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^DEPDIR$])
+m4trace:configure.ac:40: -1- AC_SUBST([am__include])
+m4trace:configure.ac:40: -1- AC_SUBST_TRACE([am__include])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^am__include$])
+m4trace:configure.ac:40: -1- AC_SUBST([am__quote])
+m4trace:configure.ac:40: -1- AC_SUBST_TRACE([am__quote])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^am__quote$])
+m4trace:configure.ac:40: -1- AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
+m4trace:configure.ac:40: -1- AC_SUBST([AMDEP_TRUE])
+m4trace:configure.ac:40: -1- AC_SUBST_TRACE([AMDEP_TRUE])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^AMDEP_TRUE$])
+m4trace:configure.ac:40: -1- AC_SUBST([AMDEP_FALSE])
+m4trace:configure.ac:40: -1- AC_SUBST_TRACE([AMDEP_FALSE])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^AMDEP_FALSE$])
+m4trace:configure.ac:40: -1- _AM_SUBST_NOTMAKE([AMDEP_TRUE])
+m4trace:configure.ac:40: -1- _AM_SUBST_NOTMAKE([AMDEP_FALSE])
+m4trace:configure.ac:40: -1- AC_SUBST([AMDEPBACKSLASH])
+m4trace:configure.ac:40: -1- AC_SUBST_TRACE([AMDEPBACKSLASH])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^AMDEPBACKSLASH$])
+m4trace:configure.ac:40: -1- _AM_SUBST_NOTMAKE([AMDEPBACKSLASH])
+m4trace:configure.ac:40: -1- AC_SUBST([CCDEPMODE], [depmode=$am_cv_CC_dependencies_compiler_type])
+m4trace:configure.ac:40: -1- AC_SUBST_TRACE([CCDEPMODE])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^CCDEPMODE$])
+m4trace:configure.ac:40: -1- AM_CONDITIONAL([am__fastdepCC], [
+ test "x$enable_dependency_tracking" != xno \
+ && test "$am_cv_CC_dependencies_compiler_type" = gcc3])
+m4trace:configure.ac:40: -1- AC_SUBST([am__fastdepCC_TRUE])
+m4trace:configure.ac:40: -1- AC_SUBST_TRACE([am__fastdepCC_TRUE])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^am__fastdepCC_TRUE$])
+m4trace:configure.ac:40: -1- AC_SUBST([am__fastdepCC_FALSE])
+m4trace:configure.ac:40: -1- AC_SUBST_TRACE([am__fastdepCC_FALSE])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^am__fastdepCC_FALSE$])
+m4trace:configure.ac:40: -1- _AM_SUBST_NOTMAKE([am__fastdepCC_TRUE])
+m4trace:configure.ac:40: -1- _AM_SUBST_NOTMAKE([am__fastdepCC_FALSE])
+m4trace:configure.ac:43: -1- AC_SUBST([SET_MAKE])
+m4trace:configure.ac:43: -1- AC_SUBST_TRACE([SET_MAKE])
+m4trace:configure.ac:43: -1- m4_pattern_allow([^SET_MAKE$])
+m4trace:configure.ac:44: -1- AC_SUBST([LN_S], [$as_ln_s])
+m4trace:configure.ac:44: -1- AC_SUBST_TRACE([LN_S])
+m4trace:configure.ac:44: -1- m4_pattern_allow([^LN_S$])
+m4trace:configure.ac:45: -1- AC_SUBST([AWK])
+m4trace:configure.ac:45: -1- AC_SUBST_TRACE([AWK])
+m4trace:configure.ac:45: -1- m4_pattern_allow([^AWK$])
+m4trace:configure.ac:46: -1- AC_SUBST([RANLIB])
+m4trace:configure.ac:46: -1- AC_SUBST_TRACE([RANLIB])
+m4trace:configure.ac:46: -1- m4_pattern_allow([^RANLIB$])
+m4trace:configure.ac:47: -1- AC_PROG_LIBTOOL
+m4trace:configure.ac:47: -1- _m4_warn([obsolete], [The macro `AC_PROG_LIBTOOL' is obsolete.
+You should run autoupdate.], [m4/libtool.m4:102: AC_PROG_LIBTOOL is expanded from...
+configure.ac:47: the top level])
+m4trace:configure.ac:47: -1- LT_INIT
+m4trace:configure.ac:47: -1- m4_pattern_forbid([^_?LT_[A-Z_]+$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])
+m4trace:configure.ac:47: -1- AC_REQUIRE_AUX_FILE([ltmain.sh])
+m4trace:configure.ac:47: -1- AC_SUBST([LIBTOOL])
+m4trace:configure.ac:47: -1- AC_SUBST_TRACE([LIBTOOL])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^LIBTOOL$])
+m4trace:configure.ac:47: -1- AC_SUBST([SED])
+m4trace:configure.ac:47: -1- AC_SUBST_TRACE([SED])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^SED$])
+m4trace:configure.ac:47: -1- AC_SUBST([GREP])
+m4trace:configure.ac:47: -1- AC_SUBST_TRACE([GREP])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^GREP$])
+m4trace:configure.ac:47: -1- AC_SUBST([EGREP])
+m4trace:configure.ac:47: -1- AC_SUBST_TRACE([EGREP])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^EGREP$])
+m4trace:configure.ac:47: -1- AC_SUBST([FGREP])
+m4trace:configure.ac:47: -1- AC_SUBST_TRACE([FGREP])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^FGREP$])
+m4trace:configure.ac:47: -1- AC_SUBST([GREP])
+m4trace:configure.ac:47: -1- AC_SUBST_TRACE([GREP])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^GREP$])
+m4trace:configure.ac:47: -1- AC_SUBST([LD])
+m4trace:configure.ac:47: -1- AC_SUBST_TRACE([LD])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^LD$])
+m4trace:configure.ac:47: -1- AC_SUBST([DUMPBIN])
+m4trace:configure.ac:47: -1- AC_SUBST_TRACE([DUMPBIN])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^DUMPBIN$])
+m4trace:configure.ac:47: -1- AC_SUBST([ac_ct_DUMPBIN])
+m4trace:configure.ac:47: -1- AC_SUBST_TRACE([ac_ct_DUMPBIN])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^ac_ct_DUMPBIN$])
+m4trace:configure.ac:47: -1- AC_SUBST([DUMPBIN])
+m4trace:configure.ac:47: -1- AC_SUBST_TRACE([DUMPBIN])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^DUMPBIN$])
+m4trace:configure.ac:47: -1- AC_SUBST([NM])
+m4trace:configure.ac:47: -1- AC_SUBST_TRACE([NM])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^NM$])
+m4trace:configure.ac:47: -1- AC_SUBST([OBJDUMP])
+m4trace:configure.ac:47: -1- AC_SUBST_TRACE([OBJDUMP])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^OBJDUMP$])
+m4trace:configure.ac:47: -1- AC_SUBST([OBJDUMP])
+m4trace:configure.ac:47: -1- AC_SUBST_TRACE([OBJDUMP])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^OBJDUMP$])
+m4trace:configure.ac:47: -1- AC_SUBST([AR])
+m4trace:configure.ac:47: -1- AC_SUBST_TRACE([AR])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^AR$])
+m4trace:configure.ac:47: -1- AC_SUBST([STRIP])
+m4trace:configure.ac:47: -1- AC_SUBST_TRACE([STRIP])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^STRIP$])
+m4trace:configure.ac:47: -1- AC_SUBST([RANLIB])
+m4trace:configure.ac:47: -1- AC_SUBST_TRACE([RANLIB])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^RANLIB$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([LT_OBJDIR])
+m4trace:configure.ac:47: -1- AC_DEFINE_TRACE_LITERAL([LT_OBJDIR])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^LT_OBJDIR$])
+m4trace:configure.ac:47: -1- AH_OUTPUT([LT_OBJDIR], [/* Define to the sub-directory in which libtool stores uninstalled libraries.
+ */
+@%:@undef LT_OBJDIR])
+m4trace:configure.ac:47: -1- AC_SUBST([lt_ECHO])
+m4trace:configure.ac:47: -1- AC_SUBST_TRACE([lt_ECHO])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^lt_ECHO$])
+m4trace:configure.ac:47: -1- LT_SUPPORTED_TAG([CC])
+m4trace:configure.ac:47: -1- AC_SUBST([DSYMUTIL])
+m4trace:configure.ac:47: -1- AC_SUBST_TRACE([DSYMUTIL])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^DSYMUTIL$])
+m4trace:configure.ac:47: -1- AC_SUBST([NMEDIT])
+m4trace:configure.ac:47: -1- AC_SUBST_TRACE([NMEDIT])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^NMEDIT$])
+m4trace:configure.ac:47: -1- AC_SUBST([LIPO])
+m4trace:configure.ac:47: -1- AC_SUBST_TRACE([LIPO])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^LIPO$])
+m4trace:configure.ac:47: -1- AC_SUBST([OTOOL])
+m4trace:configure.ac:47: -1- AC_SUBST_TRACE([OTOOL])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^OTOOL$])
+m4trace:configure.ac:47: -1- AC_SUBST([OTOOL64])
+m4trace:configure.ac:47: -1- AC_SUBST_TRACE([OTOOL64])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^OTOOL64$])
+m4trace:configure.ac:47: -1- AH_OUTPUT([HAVE_DLFCN_H], [/* Define to 1 if you have the <dlfcn.h> header file. */
+@%:@undef HAVE_DLFCN_H])
+m4trace:configure.ac:47: -1- AC_SUBST([CPP])
+m4trace:configure.ac:47: -1- AC_SUBST_TRACE([CPP])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^CPP$])
+m4trace:configure.ac:47: -1- AC_SUBST([CPPFLAGS])
+m4trace:configure.ac:47: -1- AC_SUBST_TRACE([CPPFLAGS])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^CPPFLAGS$])
+m4trace:configure.ac:47: -1- AC_SUBST([CPP])
+m4trace:configure.ac:47: -1- AC_SUBST_TRACE([CPP])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^CPP$])
+m4trace:configure.ac:47: -1- AC_DEFINE_TRACE_LITERAL([STDC_HEADERS])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^STDC_HEADERS$])
+m4trace:configure.ac:47: -1- AH_OUTPUT([STDC_HEADERS], [/* Define to 1 if you have the ANSI C header files. */
+@%:@undef STDC_HEADERS])
+m4trace:configure.ac:47: -1- AH_OUTPUT([HAVE_SYS_TYPES_H], [/* Define to 1 if you have the <sys/types.h> header file. */
+@%:@undef HAVE_SYS_TYPES_H])
+m4trace:configure.ac:47: -1- AH_OUTPUT([HAVE_SYS_STAT_H], [/* Define to 1 if you have the <sys/stat.h> header file. */
+@%:@undef HAVE_SYS_STAT_H])
+m4trace:configure.ac:47: -1- AH_OUTPUT([HAVE_STDLIB_H], [/* Define to 1 if you have the <stdlib.h> header file. */
+@%:@undef HAVE_STDLIB_H])
+m4trace:configure.ac:47: -1- AH_OUTPUT([HAVE_STRING_H], [/* Define to 1 if you have the <string.h> header file. */
+@%:@undef HAVE_STRING_H])
+m4trace:configure.ac:47: -1- AH_OUTPUT([HAVE_MEMORY_H], [/* Define to 1 if you have the <memory.h> header file. */
+@%:@undef HAVE_MEMORY_H])
+m4trace:configure.ac:47: -1- AH_OUTPUT([HAVE_STRINGS_H], [/* Define to 1 if you have the <strings.h> header file. */
+@%:@undef HAVE_STRINGS_H])
+m4trace:configure.ac:47: -1- AH_OUTPUT([HAVE_INTTYPES_H], [/* Define to 1 if you have the <inttypes.h> header file. */
+@%:@undef HAVE_INTTYPES_H])
+m4trace:configure.ac:47: -1- AH_OUTPUT([HAVE_STDINT_H], [/* Define to 1 if you have the <stdint.h> header file. */
+@%:@undef HAVE_STDINT_H])
+m4trace:configure.ac:47: -1- AH_OUTPUT([HAVE_UNISTD_H], [/* Define to 1 if you have the <unistd.h> header file. */
+@%:@undef HAVE_UNISTD_H])
+m4trace:configure.ac:47: -1- AC_DEFINE_TRACE_LITERAL([HAVE_DLFCN_H])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^HAVE_DLFCN_H$])
+m4trace:configure.ac:49: -1- AC_SUBST([AR])
+m4trace:configure.ac:49: -1- AC_SUBST_TRACE([AR])
+m4trace:configure.ac:49: -1- m4_pattern_allow([^AR$])
+m4trace:configure.ac:50: -1- AC_SUBST([RM])
+m4trace:configure.ac:50: -1- AC_SUBST_TRACE([RM])
+m4trace:configure.ac:50: -1- m4_pattern_allow([^RM$])
+m4trace:configure.ac:51: -1- AC_SUBST([CP])
+m4trace:configure.ac:51: -1- AC_SUBST_TRACE([CP])
+m4trace:configure.ac:51: -1- m4_pattern_allow([^CP$])
+m4trace:configure.ac:52: -1- AC_SUBST([LN])
+m4trace:configure.ac:52: -1- AC_SUBST_TRACE([LN])
+m4trace:configure.ac:52: -1- m4_pattern_allow([^LN$])
+m4trace:configure.ac:53: -1- AC_SUBST([SED])
+m4trace:configure.ac:53: -1- AC_SUBST_TRACE([SED])
+m4trace:configure.ac:53: -1- m4_pattern_allow([^SED$])
+m4trace:configure.ac:54: -1- AC_SUBST([MAKE])
+m4trace:configure.ac:54: -1- AC_SUBST_TRACE([MAKE])
+m4trace:configure.ac:54: -1- m4_pattern_allow([^MAKE$])
+m4trace:configure.ac:59: -1- AM_GNU_GETTEXT([external])
+m4trace:configure.ac:59: -1- AM_NLS
+m4trace:configure.ac:59: -1- AC_SUBST([USE_NLS])
+m4trace:configure.ac:59: -1- AC_SUBST_TRACE([USE_NLS])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^USE_NLS$])
+m4trace:configure.ac:59: -1- AC_SUBST([MSGFMT])
+m4trace:configure.ac:59: -1- AC_SUBST_TRACE([MSGFMT])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^MSGFMT$])
+m4trace:configure.ac:59: -1- AC_SUBST([GMSGFMT])
+m4trace:configure.ac:59: -1- AC_SUBST_TRACE([GMSGFMT])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^GMSGFMT$])
+m4trace:configure.ac:59: -1- AC_SUBST([MSGFMT_015])
+m4trace:configure.ac:59: -1- AC_SUBST_TRACE([MSGFMT_015])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^MSGFMT_015$])
+m4trace:configure.ac:59: -1- AC_SUBST([GMSGFMT_015])
+m4trace:configure.ac:59: -1- AC_SUBST_TRACE([GMSGFMT_015])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^GMSGFMT_015$])
+m4trace:configure.ac:59: -1- AC_SUBST([XGETTEXT])
+m4trace:configure.ac:59: -1- AC_SUBST_TRACE([XGETTEXT])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^XGETTEXT$])
+m4trace:configure.ac:59: -1- AC_SUBST([XGETTEXT_015])
+m4trace:configure.ac:59: -1- AC_SUBST_TRACE([XGETTEXT_015])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^XGETTEXT_015$])
+m4trace:configure.ac:59: -1- AC_SUBST([MSGMERGE])
+m4trace:configure.ac:59: -1- AC_SUBST_TRACE([MSGMERGE])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^MSGMERGE$])
+m4trace:configure.ac:59: -1- AC_SUBST([localedir])
+m4trace:configure.ac:59: -1- AC_SUBST_TRACE([localedir])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^localedir$])
+m4trace:configure.ac:59: -1- AC_REQUIRE_AUX_FILE([config.rpath])
+m4trace:configure.ac:59: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:2688: AC_TRY_LINK is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/general.m4:2032: AC_CACHE_VAL is expanded from...
+../../lib/autoconf/general.m4:2053: AC_CACHE_CHECK is expanded from...
+m4/gettext.m4:367: gt_INTL_MACOSX is expanded from...
+m4/gettext.m4:57: AM_GNU_GETTEXT is expanded from...
+configure.ac:59: the top level])
+m4trace:configure.ac:59: -1- AC_DEFINE_TRACE_LITERAL([HAVE_CFPREFERENCESCOPYAPPVALUE])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^HAVE_CFPREFERENCESCOPYAPPVALUE$])
+m4trace:configure.ac:59: -1- AH_OUTPUT([HAVE_CFPREFERENCESCOPYAPPVALUE], [/* Define to 1 if you have the MacOS X function CFPreferencesCopyAppValue in
+ the CoreFoundation framework. */
+@%:@undef HAVE_CFPREFERENCESCOPYAPPVALUE])
+m4trace:configure.ac:59: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:2688: AC_TRY_LINK is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/general.m4:2032: AC_CACHE_VAL is expanded from...
+../../lib/autoconf/general.m4:2053: AC_CACHE_CHECK is expanded from...
+m4/gettext.m4:367: gt_INTL_MACOSX is expanded from...
+m4/gettext.m4:57: AM_GNU_GETTEXT is expanded from...
+configure.ac:59: the top level])
+m4trace:configure.ac:59: -1- AC_DEFINE_TRACE_LITERAL([HAVE_CFLOCALECOPYCURRENT])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^HAVE_CFLOCALECOPYCURRENT$])
+m4trace:configure.ac:59: -1- AH_OUTPUT([HAVE_CFLOCALECOPYCURRENT], [/* Define to 1 if you have the MacOS X function CFLocaleCopyCurrent in the
+ CoreFoundation framework. */
+@%:@undef HAVE_CFLOCALECOPYCURRENT])
+m4trace:configure.ac:59: -1- AC_SUBST([INTL_MACOSX_LIBS])
+m4trace:configure.ac:59: -1- AC_SUBST_TRACE([INTL_MACOSX_LIBS])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^INTL_MACOSX_LIBS$])
+m4trace:configure.ac:59: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:2688: AC_TRY_LINK is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/general.m4:2032: AC_CACHE_VAL is expanded from...
+../../lib/autoconf/general.m4:2053: AC_CACHE_CHECK is expanded from...
+m4/gettext.m4:57: AM_GNU_GETTEXT is expanded from...
+configure.ac:59: the top level])
+m4trace:configure.ac:59: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:2688: AC_TRY_LINK is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/general.m4:2032: AC_CACHE_VAL is expanded from...
+../../lib/autoconf/general.m4:2053: AC_CACHE_CHECK is expanded from...
+m4/iconv.m4:20: AM_ICONV_LINK is expanded from...
+m4/gettext.m4:57: AM_GNU_GETTEXT is expanded from...
+configure.ac:59: the top level])
+m4trace:configure.ac:59: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:2688: AC_TRY_LINK is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/general.m4:2032: AC_CACHE_VAL is expanded from...
+../../lib/autoconf/general.m4:2053: AC_CACHE_CHECK is expanded from...
+m4/iconv.m4:20: AM_ICONV_LINK is expanded from...
+m4/gettext.m4:57: AM_GNU_GETTEXT is expanded from...
+configure.ac:59: the top level])
+m4trace:configure.ac:59: -1- AC_DEFINE_TRACE_LITERAL([HAVE_ICONV])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^HAVE_ICONV$])
+m4trace:configure.ac:59: -1- AH_OUTPUT([HAVE_ICONV], [/* Define if you have the iconv() function. */
+@%:@undef HAVE_ICONV])
+m4trace:configure.ac:59: -1- AC_SUBST([LIBICONV])
+m4trace:configure.ac:59: -1- AC_SUBST_TRACE([LIBICONV])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^LIBICONV$])
+m4trace:configure.ac:59: -1- AC_SUBST([LTLIBICONV])
+m4trace:configure.ac:59: -1- AC_SUBST_TRACE([LTLIBICONV])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^LTLIBICONV$])
+m4trace:configure.ac:59: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:2688: AC_TRY_LINK is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/general.m4:2032: AC_CACHE_VAL is expanded from...
+../../lib/autoconf/general.m4:2053: AC_CACHE_CHECK is expanded from...
+m4/gettext.m4:57: AM_GNU_GETTEXT is expanded from...
+configure.ac:59: the top level])
+m4trace:configure.ac:59: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:2688: AC_TRY_LINK is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/general.m4:2032: AC_CACHE_VAL is expanded from...
+../../lib/autoconf/general.m4:2053: AC_CACHE_CHECK is expanded from...
+m4/gettext.m4:57: AM_GNU_GETTEXT is expanded from...
+configure.ac:59: the top level])
+m4trace:configure.ac:59: -1- AC_DEFINE_TRACE_LITERAL([ENABLE_NLS])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^ENABLE_NLS$])
+m4trace:configure.ac:59: -1- AH_OUTPUT([ENABLE_NLS], [/* Define to 1 if translation of program messages to the user\'s native
+ language is requested. */
+@%:@undef ENABLE_NLS])
+m4trace:configure.ac:59: -1- AC_DEFINE_TRACE_LITERAL([HAVE_GETTEXT])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^HAVE_GETTEXT$])
+m4trace:configure.ac:59: -1- AH_OUTPUT([HAVE_GETTEXT], [/* Define if the GNU gettext() function is already present or preinstalled. */
+@%:@undef HAVE_GETTEXT])
+m4trace:configure.ac:59: -1- AC_DEFINE_TRACE_LITERAL([HAVE_DCGETTEXT])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^HAVE_DCGETTEXT$])
+m4trace:configure.ac:59: -1- AH_OUTPUT([HAVE_DCGETTEXT], [/* Define if the GNU dcgettext() function is already present or preinstalled.
+ */
+@%:@undef HAVE_DCGETTEXT])
+m4trace:configure.ac:59: -1- AC_SUBST([INTLLIBS])
+m4trace:configure.ac:59: -1- AC_SUBST_TRACE([INTLLIBS])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^INTLLIBS$])
+m4trace:configure.ac:59: -1- AC_SUBST([LIBINTL])
+m4trace:configure.ac:59: -1- AC_SUBST_TRACE([LIBINTL])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^LIBINTL$])
+m4trace:configure.ac:59: -1- AC_SUBST([LTLIBINTL])
+m4trace:configure.ac:59: -1- AC_SUBST_TRACE([LTLIBINTL])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^LTLIBINTL$])
+m4trace:configure.ac:59: -1- AC_SUBST([POSUB])
+m4trace:configure.ac:59: -1- AC_SUBST_TRACE([POSUB])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^POSUB$])
+m4trace:configure.ac:64: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:64: the top level])
+m4trace:configure.ac:72: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:72: the top level])
+m4trace:configure.ac:82: -1- AC_DEFINE_TRACE_LITERAL([ENABLE_DMALLOC])
+m4trace:configure.ac:82: -1- m4_pattern_allow([^ENABLE_DMALLOC$])
+m4trace:configure.ac:82: -1- AH_OUTPUT([ENABLE_DMALLOC], [/* Define enable dmalloc debugging */
+@%:@undef ENABLE_DMALLOC])
+m4trace:configure.ac:87: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:87: the top level])
+m4trace:configure.ac:99: -1- AC_SUBST([localedir], ["$localedir"])
+m4trace:configure.ac:99: -1- AC_SUBST_TRACE([localedir])
+m4trace:configure.ac:99: -1- m4_pattern_allow([^localedir$])
+m4trace:configure.ac:106: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:106: the top level])
+m4trace:configure.ac:131: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:131: the top level])
+m4trace:configure.ac:147: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:147: the top level])
+m4trace:configure.ac:163: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:163: the top level])
+m4trace:configure.ac:172: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:172: the top level])
+m4trace:configure.ac:187: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:187: the top level])
+m4trace:configure.ac:190: -1- AC_DEFINE_TRACE_LITERAL([DEBUG])
+m4trace:configure.ac:190: -1- m4_pattern_allow([^DEBUG$])
+m4trace:configure.ac:190: -1- AH_OUTPUT([DEBUG], [/* Compile in debugging */
+@%:@undef DEBUG])
+m4trace:configure.ac:191: -1- AC_DEFINE_TRACE_LITERAL([DEBUGJOURNAL])
+m4trace:configure.ac:191: -1- m4_pattern_allow([^DEBUGJOURNAL$])
+m4trace:configure.ac:191: -1- AH_OUTPUT([DEBUGJOURNAL], [/* Display debug messages in journal */
+@%:@undef DEBUGJOURNAL])
+m4trace:configure.ac:200: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:200: the top level])
+m4trace:configure.ac:211: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:211: the top level])
+m4trace:configure.ac:213: -1- AC_DEFINE_TRACE_LITERAL([MOUSE])
+m4trace:configure.ac:213: -1- m4_pattern_allow([^MOUSE$])
+m4trace:configure.ac:213: -1- AH_OUTPUT([MOUSE], [/* Compile in mouse support */
+@%:@undef MOUSE])
+m4trace:configure.ac:221: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:221: the top level])
+m4trace:configure.ac:223: -1- AC_DEFINE_TRACE_LITERAL([USE_QUOTAS])
+m4trace:configure.ac:223: -1- m4_pattern_allow([^USE_QUOTAS$])
+m4trace:configure.ac:223: -1- AH_OUTPUT([USE_QUOTAS], [/* Compile in quota check on startup */
+@%:@undef USE_QUOTAS])
+m4trace:configure.ac:230: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:230: the top level])
+m4trace:configure.ac:234: -1- AC_DEFINE_TRACE_LITERAL([NEVER_ALLOW_CHANGING_FROM])
+m4trace:configure.ac:234: -1- m4_pattern_allow([^NEVER_ALLOW_CHANGING_FROM$])
+m4trace:configure.ac:234: -1- AH_OUTPUT([NEVER_ALLOW_CHANGING_FROM], [/* Disallow users changing their From address */
+@%:@undef NEVER_ALLOW_CHANGING_FROM])
+m4trace:configure.ac:240: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:240: the top level])
+m4trace:configure.ac:242: -1- AC_DEFINE_TRACE_LITERAL([BACKGROUND_POST])
+m4trace:configure.ac:242: -1- m4_pattern_allow([^BACKGROUND_POST$])
+m4trace:configure.ac:242: -1- AH_OUTPUT([BACKGROUND_POST], [/* Enable background posting support */
+@%:@undef BACKGROUND_POST])
+m4trace:configure.ac:250: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:250: the top level])
+m4trace:configure.ac:252: -1- AC_DEFINE_TRACE_LITERAL([KEYBOARD_LOCK])
+m4trace:configure.ac:252: -1- m4_pattern_allow([^KEYBOARD_LOCK$])
+m4trace:configure.ac:252: -1- AH_OUTPUT([KEYBOARD_LOCK], [/* Enable keyboard lock support */
+@%:@undef KEYBOARD_LOCK])
+m4trace:configure.ac:260: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:260: the top level])
+m4trace:configure.ac:262: -1- AC_DEFINE_TRACE_LITERAL([ENCODE_FROMS])
+m4trace:configure.ac:262: -1- m4_pattern_allow([^ENCODE_FROMS$])
+m4trace:configure.ac:262: -1- AH_OUTPUT([ENCODE_FROMS], [/* Enable From address encoding in sent messages */
+@%:@undef ENCODE_FROMS])
+m4trace:configure.ac:271: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:271: the top level])
+m4trace:configure.ac:270: -1- AC_SUBST([SENDMAIL])
+m4trace:configure.ac:270: -1- AC_SUBST_TRACE([SENDMAIL])
+m4trace:configure.ac:270: -1- m4_pattern_allow([^SENDMAIL$])
+m4trace:configure.ac:270: -1- AC_SUBST([SENDMAIL])
+m4trace:configure.ac:270: -1- AC_SUBST_TRACE([SENDMAIL])
+m4trace:configure.ac:270: -1- m4_pattern_allow([^SENDMAIL$])
+m4trace:configure.ac:288: -1- AC_DEFINE_TRACE_LITERAL([SENDMAIL])
+m4trace:configure.ac:288: -1- m4_pattern_allow([^SENDMAIL$])
+m4trace:configure.ac:288: -1- AH_OUTPUT([SENDMAIL], [/* Local mail submission agent */
+@%:@undef SENDMAIL])
+m4trace:configure.ac:294: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:294: the top level])
+m4trace:configure.ac:300: -1- AC_DEFINE_TRACE_LITERAL([SENDMAILFLAGS])
+m4trace:configure.ac:300: -1- m4_pattern_allow([^SENDMAILFLAGS$])
+m4trace:configure.ac:300: -1- AH_OUTPUT([SENDMAILFLAGS], [/* Local MSA flags for SMTP on stdin/stdout */
+@%:@undef SENDMAILFLAGS])
+m4trace:configure.ac:305: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:305: the top level])
+m4trace:configure.ac:304: -1- AC_SUBST([NPA_PROG])
+m4trace:configure.ac:304: -1- AC_SUBST_TRACE([NPA_PROG])
+m4trace:configure.ac:304: -1- m4_pattern_allow([^NPA_PROG$])
+m4trace:configure.ac:304: -1- AC_SUBST([NPA_PROG])
+m4trace:configure.ac:304: -1- AC_SUBST_TRACE([NPA_PROG])
+m4trace:configure.ac:304: -1- m4_pattern_allow([^NPA_PROG$])
+m4trace:configure.ac:324: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:324: the top level])
+m4trace:configure.ac:331: -1- AC_DEFINE_TRACE_LITERAL([SENDNEWS])
+m4trace:configure.ac:331: -1- m4_pattern_allow([^SENDNEWS$])
+m4trace:configure.ac:331: -1- AH_OUTPUT([SENDNEWS], [/* Posting agent to use when no nntp-servers defined */
+@%:@undef SENDNEWS])
+m4trace:configure.ac:336: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:336: the top level])
+m4trace:configure.ac:335: -1- AC_SUBST([PWPROG])
+m4trace:configure.ac:335: -1- AC_SUBST_TRACE([PWPROG])
+m4trace:configure.ac:335: -1- m4_pattern_allow([^PWPROG$])
+m4trace:configure.ac:335: -1- AC_SUBST([PWPROG])
+m4trace:configure.ac:335: -1- AC_SUBST_TRACE([PWPROG])
+m4trace:configure.ac:335: -1- m4_pattern_allow([^PWPROG$])
+m4trace:configure.ac:335: -1- AC_SUBST([PWPROG])
+m4trace:configure.ac:335: -1- AC_SUBST_TRACE([PWPROG])
+m4trace:configure.ac:335: -1- m4_pattern_allow([^PWPROG$])
+m4trace:configure.ac:353: -1- AC_DEFINE_TRACE_LITERAL([PASSWD_PROG])
+m4trace:configure.ac:353: -1- m4_pattern_allow([^PASSWD_PROG$])
+m4trace:configure.ac:353: -1- AH_OUTPUT([PASSWD_PROG], [/* Program users use to change their password */
+@%:@undef PASSWD_PROG])
+m4trace:configure.ac:358: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:358: the top level])
+m4trace:configure.ac:357: -1- AC_SUBST([SPELLPROG])
+m4trace:configure.ac:357: -1- AC_SUBST_TRACE([SPELLPROG])
+m4trace:configure.ac:357: -1- m4_pattern_allow([^SPELLPROG$])
+m4trace:configure.ac:357: -1- AC_SUBST([SPELLPROG])
+m4trace:configure.ac:357: -1- AC_SUBST_TRACE([SPELLPROG])
+m4trace:configure.ac:357: -1- m4_pattern_allow([^SPELLPROG$])
+m4trace:configure.ac:357: -1- AC_SUBST([SPELLPROG])
+m4trace:configure.ac:357: -1- AC_SUBST_TRACE([SPELLPROG])
+m4trace:configure.ac:357: -1- m4_pattern_allow([^SPELLPROG$])
+m4trace:configure.ac:378: -1- AC_SUBST([alpine_simple_spellcheck])
+m4trace:configure.ac:378: -1- AC_SUBST_TRACE([alpine_simple_spellcheck])
+m4trace:configure.ac:378: -1- m4_pattern_allow([^alpine_simple_spellcheck$])
+m4trace:configure.ac:398: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:398: the top level])
+m4trace:configure.ac:397: -1- AC_SUBST([ISPELLPROG])
+m4trace:configure.ac:397: -1- AC_SUBST_TRACE([ISPELLPROG])
+m4trace:configure.ac:397: -1- m4_pattern_allow([^ISPELLPROG$])
+m4trace:configure.ac:397: -1- AC_SUBST([ISPELLPROG])
+m4trace:configure.ac:397: -1- AC_SUBST_TRACE([ISPELLPROG])
+m4trace:configure.ac:397: -1- m4_pattern_allow([^ISPELLPROG$])
+m4trace:configure.ac:415: -1- AC_SUBST([alpine_interactive_spellcheck])
+m4trace:configure.ac:415: -1- AC_SUBST_TRACE([alpine_interactive_spellcheck])
+m4trace:configure.ac:415: -1- m4_pattern_allow([^alpine_interactive_spellcheck$])
+m4trace:configure.ac:428: -1- AC_DEFINE_TRACE_LITERAL([DF_VAR_SPELLER])
+m4trace:configure.ac:428: -1- m4_pattern_allow([^DF_VAR_SPELLER$])
+m4trace:configure.ac:428: -1- AH_OUTPUT([DF_VAR_SPELLER], [/* Interactive, filewise spell checker */
+@%:@undef DF_VAR_SPELLER])
+m4trace:configure.ac:434: -1- AC_DEFINE_TRACE_LITERAL([SPELLER])
+m4trace:configure.ac:434: -1- m4_pattern_allow([^SPELLER$])
+m4trace:configure.ac:434: -1- AH_OUTPUT([SPELLER], [/* Simple spell checker: reads stdin, emits misspellings on stdout */
+@%:@undef SPELLER])
+m4trace:configure.ac:445: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:445: the top level])
+m4trace:configure.ac:457: -1- AC_DEFINE_TRACE_LITERAL([SYSTEM_PINERC])
+m4trace:configure.ac:457: -1- m4_pattern_allow([^SYSTEM_PINERC$])
+m4trace:configure.ac:457: -1- AH_OUTPUT([SYSTEM_PINERC], [/* System pinerc */
+@%:@undef SYSTEM_PINERC])
+m4trace:configure.ac:468: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:468: the top level])
+m4trace:configure.ac:480: -1- AC_DEFINE_TRACE_LITERAL([SYSTEM_PINERC_FIXED])
+m4trace:configure.ac:480: -1- m4_pattern_allow([^SYSTEM_PINERC_FIXED$])
+m4trace:configure.ac:480: -1- AH_OUTPUT([SYSTEM_PINERC_FIXED], [/* System fixed pinerc */
+@%:@undef SYSTEM_PINERC_FIXED])
+m4trace:configure.ac:512: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:512: the top level])
+m4trace:configure.ac:512: -1- AC_DEFINE_TRACE_LITERAL([DF_MAILCHECK])
+m4trace:configure.ac:512: -1- m4_pattern_allow([^DF_MAILCHECK$])
+m4trace:configure.ac:512: -1- AH_OUTPUT([DF_MAILCHECK], [/* Default configuration value */
+@%:@undef DF_MAILCHECK])
+m4trace:configure.ac:513: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:499: PINEVAR_UNQUOTED is expanded from...
+configure.ac:513: the top level])
+m4trace:configure.ac:513: -1- AC_DEFINE_TRACE_LITERAL([CHECK_POINT_TIME])
+m4trace:configure.ac:513: -1- m4_pattern_allow([^CHECK_POINT_TIME$])
+m4trace:configure.ac:513: -1- AH_OUTPUT([CHECK_POINT_TIME], [/* Default configuration value */
+@%:@undef CHECK_POINT_TIME])
+m4trace:configure.ac:514: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:499: PINEVAR_UNQUOTED is expanded from...
+configure.ac:514: the top level])
+m4trace:configure.ac:514: -1- AC_DEFINE_TRACE_LITERAL([CHECK_POINT_FREQ])
+m4trace:configure.ac:514: -1- m4_pattern_allow([^CHECK_POINT_FREQ$])
+m4trace:configure.ac:514: -1- AH_OUTPUT([CHECK_POINT_FREQ], [/* Default configuration value */
+@%:@undef CHECK_POINT_FREQ])
+m4trace:configure.ac:515: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:499: PINEVAR_UNQUOTED is expanded from...
+configure.ac:515: the top level])
+m4trace:configure.ac:515: -1- AC_DEFINE_TRACE_LITERAL([DEFAULT_LINES_ON_TERMINAL])
+m4trace:configure.ac:515: -1- m4_pattern_allow([^DEFAULT_LINES_ON_TERMINAL$])
+m4trace:configure.ac:515: -1- AH_OUTPUT([DEFAULT_LINES_ON_TERMINAL], [/* Default configuration value */
+@%:@undef DEFAULT_LINES_ON_TERMINAL])
+m4trace:configure.ac:516: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:499: PINEVAR_UNQUOTED is expanded from...
+configure.ac:516: the top level])
+m4trace:configure.ac:516: -1- AC_DEFINE_TRACE_LITERAL([DEFAULT_COLUMNS_ON_TERMINAL])
+m4trace:configure.ac:516: -1- m4_pattern_allow([^DEFAULT_COLUMNS_ON_TERMINAL$])
+m4trace:configure.ac:516: -1- AH_OUTPUT([DEFAULT_COLUMNS_ON_TERMINAL], [/* Default configuration value */
+@%:@undef DEFAULT_COLUMNS_ON_TERMINAL])
+m4trace:configure.ac:517: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:499: PINEVAR_UNQUOTED is expanded from...
+configure.ac:517: the top level])
+m4trace:configure.ac:517: -1- AC_DEFINE_TRACE_LITERAL([MAX_SCREEN_ROWS])
+m4trace:configure.ac:517: -1- m4_pattern_allow([^MAX_SCREEN_ROWS$])
+m4trace:configure.ac:517: -1- AH_OUTPUT([MAX_SCREEN_ROWS], [/* Default configuration value */
+@%:@undef MAX_SCREEN_ROWS])
+m4trace:configure.ac:518: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:499: PINEVAR_UNQUOTED is expanded from...
+configure.ac:518: the top level])
+m4trace:configure.ac:518: -1- AC_DEFINE_TRACE_LITERAL([MAX_SCREEN_COLS])
+m4trace:configure.ac:518: -1- m4_pattern_allow([^MAX_SCREEN_COLS$])
+m4trace:configure.ac:518: -1- AH_OUTPUT([MAX_SCREEN_COLS], [/* Default configuration value */
+@%:@undef MAX_SCREEN_COLS])
+m4trace:configure.ac:519: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:519: the top level])
+m4trace:configure.ac:519: -1- AC_DEFINE_TRACE_LITERAL([DF_FILLCOL])
+m4trace:configure.ac:519: -1- m4_pattern_allow([^DF_FILLCOL$])
+m4trace:configure.ac:519: -1- AH_OUTPUT([DF_FILLCOL], [/* Default configuration value */
+@%:@undef DF_FILLCOL])
+m4trace:configure.ac:520: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:499: PINEVAR_UNQUOTED is expanded from...
+configure.ac:520: the top level])
+m4trace:configure.ac:520: -1- AC_DEFINE_TRACE_LITERAL([MAX_FILLCOL])
+m4trace:configure.ac:520: -1- m4_pattern_allow([^MAX_FILLCOL$])
+m4trace:configure.ac:520: -1- AH_OUTPUT([MAX_FILLCOL], [/* Default configuration value */
+@%:@undef MAX_FILLCOL])
+m4trace:configure.ac:521: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:499: PINEVAR_UNQUOTED is expanded from...
+configure.ac:521: the top level])
+m4trace:configure.ac:521: -1- AC_DEFINE_TRACE_LITERAL([DEFAULT_DEBUG])
+m4trace:configure.ac:521: -1- m4_pattern_allow([^DEFAULT_DEBUG$])
+m4trace:configure.ac:521: -1- AH_OUTPUT([DEFAULT_DEBUG], [/* Default configuration value */
+@%:@undef DEFAULT_DEBUG])
+m4trace:configure.ac:522: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:499: PINEVAR_UNQUOTED is expanded from...
+configure.ac:522: the top level])
+m4trace:configure.ac:522: -1- AC_DEFINE_TRACE_LITERAL([NUMDEBUGFILES])
+m4trace:configure.ac:522: -1- m4_pattern_allow([^NUMDEBUGFILES$])
+m4trace:configure.ac:522: -1- AH_OUTPUT([NUMDEBUGFILES], [/* Default configuration value */
+@%:@undef NUMDEBUGFILES])
+m4trace:configure.ac:523: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:523: the top level])
+m4trace:configure.ac:523: -1- AC_DEFINE_TRACE_LITERAL([DEBUGFILE])
+m4trace:configure.ac:523: -1- m4_pattern_allow([^DEBUGFILE$])
+m4trace:configure.ac:523: -1- AH_OUTPUT([DEBUGFILE], [/* Default configuration value */
+@%:@undef DEBUGFILE])
+m4trace:configure.ac:524: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:524: the top level])
+m4trace:configure.ac:524: -1- AC_DEFINE_TRACE_LITERAL([FORWARDED_FLAG])
+m4trace:configure.ac:524: -1- m4_pattern_allow([^FORWARDED_FLAG$])
+m4trace:configure.ac:524: -1- AH_OUTPUT([FORWARDED_FLAG], [/* Default configuration value */
+@%:@undef FORWARDED_FLAG])
+m4trace:configure.ac:525: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:525: the top level])
+m4trace:configure.ac:525: -1- AC_DEFINE_TRACE_LITERAL([DF_OVERLAP])
+m4trace:configure.ac:525: -1- m4_pattern_allow([^DF_OVERLAP$])
+m4trace:configure.ac:525: -1- AH_OUTPUT([DF_OVERLAP], [/* Default configuration value */
+@%:@undef DF_OVERLAP])
+m4trace:configure.ac:526: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:526: the top level])
+m4trace:configure.ac:526: -1- AC_DEFINE_TRACE_LITERAL([DF_MARGIN])
+m4trace:configure.ac:526: -1- m4_pattern_allow([^DF_MARGIN$])
+m4trace:configure.ac:526: -1- AH_OUTPUT([DF_MARGIN], [/* Default configuration value */
+@%:@undef DF_MARGIN])
+m4trace:configure.ac:527: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:527: the top level])
+m4trace:configure.ac:527: -1- AC_DEFINE_TRACE_LITERAL([DF_DEFAULT_FCC])
+m4trace:configure.ac:527: -1- m4_pattern_allow([^DF_DEFAULT_FCC$])
+m4trace:configure.ac:527: -1- AH_OUTPUT([DF_DEFAULT_FCC], [/* Default configuration value */
+@%:@undef DF_DEFAULT_FCC])
+m4trace:configure.ac:528: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:528: the top level])
+m4trace:configure.ac:528: -1- AC_DEFINE_TRACE_LITERAL([DEFAULT_SAVE])
+m4trace:configure.ac:528: -1- m4_pattern_allow([^DEFAULT_SAVE$])
+m4trace:configure.ac:528: -1- AH_OUTPUT([DEFAULT_SAVE], [/* Default configuration value */
+@%:@undef DEFAULT_SAVE])
+m4trace:configure.ac:529: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:529: the top level])
+m4trace:configure.ac:529: -1- AC_DEFINE_TRACE_LITERAL([POSTPONED_MAIL])
+m4trace:configure.ac:529: -1- m4_pattern_allow([^POSTPONED_MAIL$])
+m4trace:configure.ac:529: -1- AH_OUTPUT([POSTPONED_MAIL], [/* Default configuration value */
+@%:@undef POSTPONED_MAIL])
+m4trace:configure.ac:530: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:530: the top level])
+m4trace:configure.ac:530: -1- AC_DEFINE_TRACE_LITERAL([POSTPONED_MSGS])
+m4trace:configure.ac:530: -1- m4_pattern_allow([^POSTPONED_MSGS$])
+m4trace:configure.ac:530: -1- AH_OUTPUT([POSTPONED_MSGS], [/* Default configuration value */
+@%:@undef POSTPONED_MSGS])
+m4trace:configure.ac:531: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:531: the top level])
+m4trace:configure.ac:531: -1- AC_DEFINE_TRACE_LITERAL([TRASH_FOLDER])
+m4trace:configure.ac:531: -1- m4_pattern_allow([^TRASH_FOLDER$])
+m4trace:configure.ac:531: -1- AH_OUTPUT([TRASH_FOLDER], [/* Default configuration value */
+@%:@undef TRASH_FOLDER])
+m4trace:configure.ac:532: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:532: the top level])
+m4trace:configure.ac:532: -1- AC_DEFINE_TRACE_LITERAL([INTERRUPTED_MAIL])
+m4trace:configure.ac:532: -1- m4_pattern_allow([^INTERRUPTED_MAIL$])
+m4trace:configure.ac:532: -1- AH_OUTPUT([INTERRUPTED_MAIL], [/* Default configuration value */
+@%:@undef INTERRUPTED_MAIL])
+m4trace:configure.ac:533: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:533: the top level])
+m4trace:configure.ac:533: -1- AC_DEFINE_TRACE_LITERAL([DEADLETTER])
+m4trace:configure.ac:533: -1- m4_pattern_allow([^DEADLETTER$])
+m4trace:configure.ac:533: -1- AH_OUTPUT([DEADLETTER], [/* Default configuration value */
+@%:@undef DEADLETTER])
+m4trace:configure.ac:534: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:534: the top level])
+m4trace:configure.ac:534: -1- AC_DEFINE_TRACE_LITERAL([DF_MAIL_DIRECTORY])
+m4trace:configure.ac:534: -1- m4_pattern_allow([^DF_MAIL_DIRECTORY$])
+m4trace:configure.ac:534: -1- AH_OUTPUT([DF_MAIL_DIRECTORY], [/* Default configuration value */
+@%:@undef DF_MAIL_DIRECTORY])
+m4trace:configure.ac:535: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:535: the top level])
+m4trace:configure.ac:535: -1- AC_DEFINE_TRACE_LITERAL([INBOX_NAME])
+m4trace:configure.ac:535: -1- m4_pattern_allow([^INBOX_NAME$])
+m4trace:configure.ac:535: -1- AH_OUTPUT([INBOX_NAME], [/* Default configuration value */
+@%:@undef INBOX_NAME])
+m4trace:configure.ac:536: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:536: the top level])
+m4trace:configure.ac:536: -1- AC_DEFINE_TRACE_LITERAL([DF_SIGNATURE_FILE])
+m4trace:configure.ac:536: -1- m4_pattern_allow([^DF_SIGNATURE_FILE$])
+m4trace:configure.ac:536: -1- AH_OUTPUT([DF_SIGNATURE_FILE], [/* Default configuration value */
+@%:@undef DF_SIGNATURE_FILE])
+m4trace:configure.ac:537: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:537: the top level])
+m4trace:configure.ac:537: -1- AC_DEFINE_TRACE_LITERAL([DF_ELM_STYLE_SAVE])
+m4trace:configure.ac:537: -1- m4_pattern_allow([^DF_ELM_STYLE_SAVE$])
+m4trace:configure.ac:537: -1- AH_OUTPUT([DF_ELM_STYLE_SAVE], [/* Default configuration value */
+@%:@undef DF_ELM_STYLE_SAVE])
+m4trace:configure.ac:538: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:538: the top level])
+m4trace:configure.ac:538: -1- AC_DEFINE_TRACE_LITERAL([DF_HEADER_IN_REPLY])
+m4trace:configure.ac:538: -1- m4_pattern_allow([^DF_HEADER_IN_REPLY$])
+m4trace:configure.ac:538: -1- AH_OUTPUT([DF_HEADER_IN_REPLY], [/* Default configuration value */
+@%:@undef DF_HEADER_IN_REPLY])
+m4trace:configure.ac:539: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:539: the top level])
+m4trace:configure.ac:539: -1- AC_DEFINE_TRACE_LITERAL([DF_OLD_STYLE_REPLY])
+m4trace:configure.ac:539: -1- m4_pattern_allow([^DF_OLD_STYLE_REPLY$])
+m4trace:configure.ac:539: -1- AH_OUTPUT([DF_OLD_STYLE_REPLY], [/* Default configuration value */
+@%:@undef DF_OLD_STYLE_REPLY])
+m4trace:configure.ac:540: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:540: the top level])
+m4trace:configure.ac:540: -1- AC_DEFINE_TRACE_LITERAL([DF_USE_ONLY_DOMAIN_NAME])
+m4trace:configure.ac:540: -1- m4_pattern_allow([^DF_USE_ONLY_DOMAIN_NAME$])
+m4trace:configure.ac:540: -1- AH_OUTPUT([DF_USE_ONLY_DOMAIN_NAME], [/* Default configuration value */
+@%:@undef DF_USE_ONLY_DOMAIN_NAME])
+m4trace:configure.ac:541: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:541: the top level])
+m4trace:configure.ac:541: -1- AC_DEFINE_TRACE_LITERAL([DF_SAVE_BY_SENDER])
+m4trace:configure.ac:541: -1- m4_pattern_allow([^DF_SAVE_BY_SENDER$])
+m4trace:configure.ac:541: -1- AH_OUTPUT([DF_SAVE_BY_SENDER], [/* Default configuration value */
+@%:@undef DF_SAVE_BY_SENDER])
+m4trace:configure.ac:542: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:542: the top level])
+m4trace:configure.ac:542: -1- AC_DEFINE_TRACE_LITERAL([DF_SORT_KEY])
+m4trace:configure.ac:542: -1- m4_pattern_allow([^DF_SORT_KEY$])
+m4trace:configure.ac:542: -1- AH_OUTPUT([DF_SORT_KEY], [/* Default configuration value */
+@%:@undef DF_SORT_KEY])
+m4trace:configure.ac:543: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:543: the top level])
+m4trace:configure.ac:543: -1- AC_DEFINE_TRACE_LITERAL([DF_AB_SORT_RULE])
+m4trace:configure.ac:543: -1- m4_pattern_allow([^DF_AB_SORT_RULE$])
+m4trace:configure.ac:543: -1- AH_OUTPUT([DF_AB_SORT_RULE], [/* Default configuration value */
+@%:@undef DF_AB_SORT_RULE])
+m4trace:configure.ac:544: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:544: the top level])
+m4trace:configure.ac:544: -1- AC_DEFINE_TRACE_LITERAL([DF_FLD_SORT_RULE])
+m4trace:configure.ac:544: -1- m4_pattern_allow([^DF_FLD_SORT_RULE$])
+m4trace:configure.ac:544: -1- AH_OUTPUT([DF_FLD_SORT_RULE], [/* Default configuration value */
+@%:@undef DF_FLD_SORT_RULE])
+m4trace:configure.ac:545: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:545: the top level])
+m4trace:configure.ac:545: -1- AC_DEFINE_TRACE_LITERAL([DF_SAVED_MSG_NAME_RULE])
+m4trace:configure.ac:545: -1- m4_pattern_allow([^DF_SAVED_MSG_NAME_RULE$])
+m4trace:configure.ac:545: -1- AH_OUTPUT([DF_SAVED_MSG_NAME_RULE], [/* Default configuration value */
+@%:@undef DF_SAVED_MSG_NAME_RULE])
+m4trace:configure.ac:546: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:546: the top level])
+m4trace:configure.ac:546: -1- AC_DEFINE_TRACE_LITERAL([DF_FCC_RULE])
+m4trace:configure.ac:546: -1- m4_pattern_allow([^DF_FCC_RULE$])
+m4trace:configure.ac:546: -1- AH_OUTPUT([DF_FCC_RULE], [/* Default configuration value */
+@%:@undef DF_FCC_RULE])
+m4trace:configure.ac:547: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:547: the top level])
+m4trace:configure.ac:547: -1- AC_DEFINE_TRACE_LITERAL([DF_STANDARD_PRINTER])
+m4trace:configure.ac:547: -1- m4_pattern_allow([^DF_STANDARD_PRINTER$])
+m4trace:configure.ac:547: -1- AH_OUTPUT([DF_STANDARD_PRINTER], [/* Default configuration value */
+@%:@undef DF_STANDARD_PRINTER])
+m4trace:configure.ac:548: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:548: the top level])
+m4trace:configure.ac:548: -1- AC_DEFINE_TRACE_LITERAL([ANSI_PRINTER])
+m4trace:configure.ac:548: -1- m4_pattern_allow([^ANSI_PRINTER$])
+m4trace:configure.ac:548: -1- AH_OUTPUT([ANSI_PRINTER], [/* Default configuration value */
+@%:@undef ANSI_PRINTER])
+m4trace:configure.ac:549: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:549: the top level])
+m4trace:configure.ac:549: -1- AC_DEFINE_TRACE_LITERAL([DF_ADDRESSBOOK])
+m4trace:configure.ac:549: -1- m4_pattern_allow([^DF_ADDRESSBOOK$])
+m4trace:configure.ac:549: -1- AH_OUTPUT([DF_ADDRESSBOOK], [/* Default configuration value */
+@%:@undef DF_ADDRESSBOOK])
+m4trace:configure.ac:550: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:550: the top level])
+m4trace:configure.ac:550: -1- AC_DEFINE_TRACE_LITERAL([DF_LOCAL_FULLNAME])
+m4trace:configure.ac:550: -1- m4_pattern_allow([^DF_LOCAL_FULLNAME$])
+m4trace:configure.ac:550: -1- AH_OUTPUT([DF_LOCAL_FULLNAME], [/* Default configuration value */
+@%:@undef DF_LOCAL_FULLNAME])
+m4trace:configure.ac:551: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:551: the top level])
+m4trace:configure.ac:551: -1- AC_DEFINE_TRACE_LITERAL([DF_LOCAL_ADDRESS])
+m4trace:configure.ac:551: -1- m4_pattern_allow([^DF_LOCAL_ADDRESS$])
+m4trace:configure.ac:551: -1- AH_OUTPUT([DF_LOCAL_ADDRESS], [/* Default configuration value */
+@%:@undef DF_LOCAL_ADDRESS])
+m4trace:configure.ac:552: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:552: the top level])
+m4trace:configure.ac:552: -1- AC_DEFINE_TRACE_LITERAL([DF_KBLOCK_PASSWD_COUNT])
+m4trace:configure.ac:552: -1- m4_pattern_allow([^DF_KBLOCK_PASSWD_COUNT$])
+m4trace:configure.ac:552: -1- AH_OUTPUT([DF_KBLOCK_PASSWD_COUNT], [/* Default configuration value */
+@%:@undef DF_KBLOCK_PASSWD_COUNT])
+m4trace:configure.ac:553: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:553: the top level])
+m4trace:configure.ac:553: -1- AC_DEFINE_TRACE_LITERAL([DF_REMOTE_ABOOK_HISTORY])
+m4trace:configure.ac:553: -1- m4_pattern_allow([^DF_REMOTE_ABOOK_HISTORY$])
+m4trace:configure.ac:553: -1- AH_OUTPUT([DF_REMOTE_ABOOK_HISTORY], [/* Default configuration value */
+@%:@undef DF_REMOTE_ABOOK_HISTORY])
+m4trace:configure.ac:554: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:554: the top level])
+m4trace:configure.ac:554: -1- AC_DEFINE_TRACE_LITERAL([DF_PUBLICCERT_DIR])
+m4trace:configure.ac:554: -1- m4_pattern_allow([^DF_PUBLICCERT_DIR$])
+m4trace:configure.ac:554: -1- AH_OUTPUT([DF_PUBLICCERT_DIR], [/* Default configuration value */
+@%:@undef DF_PUBLICCERT_DIR])
+m4trace:configure.ac:555: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:555: the top level])
+m4trace:configure.ac:555: -1- AC_DEFINE_TRACE_LITERAL([DF_PRIVATEKEY_DIR])
+m4trace:configure.ac:555: -1- m4_pattern_allow([^DF_PRIVATEKEY_DIR$])
+m4trace:configure.ac:555: -1- AH_OUTPUT([DF_PRIVATEKEY_DIR], [/* Default configuration value */
+@%:@undef DF_PRIVATEKEY_DIR])
+m4trace:configure.ac:556: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:556: the top level])
+m4trace:configure.ac:556: -1- AC_DEFINE_TRACE_LITERAL([DF_CACERT_DIR])
+m4trace:configure.ac:556: -1- m4_pattern_allow([^DF_CACERT_DIR$])
+m4trace:configure.ac:556: -1- AH_OUTPUT([DF_CACERT_DIR], [/* Default configuration value */
+@%:@undef DF_CACERT_DIR])
+m4trace:configure.ac:557: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:499: PINEVAR_UNQUOTED is expanded from...
+configure.ac:557: the top level])
+m4trace:configure.ac:557: -1- AC_DEFINE_TRACE_LITERAL([DF_DEFAULT_PRINTER])
+m4trace:configure.ac:557: -1- m4_pattern_allow([^DF_DEFAULT_PRINTER$])
+m4trace:configure.ac:557: -1- AH_OUTPUT([DF_DEFAULT_PRINTER], [/* Default configuration value */
+@%:@undef DF_DEFAULT_PRINTER])
+m4trace:configure.ac:561: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:561: the top level])
+m4trace:configure.ac:576: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:576: the top level])
+m4trace:configure.ac:583: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:583: the top level])
+m4trace:configure.ac:597: -1- AC_DEFINE_TRACE_LITERAL([PASSFILE])
+m4trace:configure.ac:597: -1- m4_pattern_allow([^PASSFILE$])
+m4trace:configure.ac:597: -1- AH_OUTPUT([PASSFILE], [/* Password cache file (NOT secure. NOT recommended) */
+@%:@undef PASSFILE])
+m4trace:configure.ac:602: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:602: the top level])
+m4trace:configure.ac:601: -1- AC_DEFINE_TRACE_LITERAL([DF_SSHPATH])
+m4trace:configure.ac:601: -1- m4_pattern_allow([^DF_SSHPATH$])
+m4trace:configure.ac:601: -1- AH_OUTPUT([DF_SSHPATH], [/* set default value of ssh command path (defining should cause ssh to be
+ preferred to rsh) */
+@%:@undef DF_SSHPATH])
+m4trace:configure.ac:617: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:617: the top level])
+m4trace:configure.ac:616: -1- AC_DEFINE_TRACE_LITERAL([DF_SSHCMD])
+m4trace:configure.ac:616: -1- m4_pattern_allow([^DF_SSHCMD$])
+m4trace:configure.ac:616: -1- AH_OUTPUT([DF_SSHCMD], [/* set default value of ssh command string (usually "%s %s -l %s exec
+ /etc/r%sd") */
+@%:@undef DF_SSHCMD])
+m4trace:configure.ac:633: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:633: the top level])
+m4trace:configure.ac:696: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:696: the top level])
+m4trace:configure.ac:704: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:704: the top level])
+m4trace:configure.ac:712: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:712: the top level])
+m4trace:configure.ac:720: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:720: the top level])
+m4trace:configure.ac:742: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:742: the top level])
+m4trace:configure.ac:751: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:751: the top level])
+m4trace:configure.ac:760: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:760: the top level])
+m4trace:configure.ac:768: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:768: the top level])
+m4trace:configure.ac:778: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:778: the top level])
+m4trace:configure.ac:789: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:789: the top level])
+m4trace:configure.ac:798: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:798: the top level])
+m4trace:configure.ac:806: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:806: the top level])
+m4trace:configure.ac:816: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:816: the top level])
+m4trace:configure.ac:821: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:821: the top level])
+m4trace:configure.ac:829: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:829: the top level])
+m4trace:configure.ac:836: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:836: the top level])
+m4trace:configure.ac:846: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:846: the top level])
+m4trace:configure.ac:850: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:850: the top level])
+m4trace:configure.ac:854: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:854: the top level])
+m4trace:configure.ac:862: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:862: the top level])
+m4trace:configure.ac:872: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:872: the top level])
+m4trace:configure.ac:893: -1- _m4_warn([obsolete], [The macro `AC_ERROR' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/oldnames.m4:34: AC_ERROR is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/libs.m4:100: AC_CHECK_LIB is expanded from...
+configure.ac:893: the top level])
+m4trace:configure.ac:900: -1- _m4_warn([obsolete], [The macro `AC_ERROR' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/oldnames.m4:34: AC_ERROR is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/libs.m4:100: AC_CHECK_LIB is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/libs.m4:100: AC_CHECK_LIB is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/libs.m4:100: AC_CHECK_LIB is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/libs.m4:100: AC_CHECK_LIB is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/libs.m4:100: AC_CHECK_LIB is expanded from...
+configure.ac:900: the top level])
+m4trace:configure.ac:938: -1- AC_DEFINE_TRACE_LITERAL([HAS_TERMINFO])
+m4trace:configure.ac:938: -1- m4_pattern_allow([^HAS_TERMINFO$])
+m4trace:configure.ac:938: -1- AH_OUTPUT([HAS_TERMINFO], [/* Define if systems uses terminfo terminal database */
+@%:@undef HAS_TERMINFO])
+m4trace:configure.ac:941: -1- AC_DEFINE_TRACE_LITERAL([HAS_TERMCAP])
+m4trace:configure.ac:941: -1- m4_pattern_allow([^HAS_TERMCAP$])
+m4trace:configure.ac:941: -1- AH_OUTPUT([HAS_TERMCAP], [/* Define if systems uses termcap terminal database */
+@%:@undef HAS_TERMCAP])
+m4trace:configure.ac:965: -1- AC_DEFINE_TRACE_LITERAL([ENABLE_LDAP])
+m4trace:configure.ac:965: -1- m4_pattern_allow([^ENABLE_LDAP$])
+m4trace:configure.ac:965: -1- AH_OUTPUT([ENABLE_LDAP], [/* Enable LDAP query support */
+@%:@undef ENABLE_LDAP])
+m4trace:configure.ac:970: -1- AC_DEFINE_TRACE_LITERAL([LDAP_DEPRECATED])
+m4trace:configure.ac:970: -1- m4_pattern_allow([^LDAP_DEPRECATED$])
+m4trace:configure.ac:970: -1- AH_OUTPUT([LDAP_DEPRECATED], [/* Define if you use OpenLDAP 2.3.x deprecated functions */
+@%:@undef LDAP_DEPRECATED])
+m4trace:configure.ac:1054: -1- _m4_warn([cross], [cannot check for file existence when cross compiling], [../../lib/autoconf/general.m4:2778: AC_CHECK_FILE is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/headers.m4:129: _AC_CHECK_HEADER_MONGREL is expanded from...
+../../lib/autoconf/headers.m4:67: AC_CHECK_HEADER is expanded from...
+configure.ac:1054: the top level])
+m4trace:configure.ac:1072: -1- _m4_warn([obsolete], [The macro `AC_ERROR' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/oldnames.m4:34: AC_ERROR is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/libs.m4:48: AC_SEARCH_LIBS is expanded from...
+configure.ac:1072: the top level])
+m4trace:configure.ac:1082: -1- _m4_warn([obsolete], [The macro `AC_ERROR' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/oldnames.m4:34: AC_ERROR is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/headers.m4:129: _AC_CHECK_HEADER_MONGREL is expanded from...
+../../lib/autoconf/headers.m4:67: AC_CHECK_HEADER is expanded from...
+configure.ac:1082: the top level])
+m4trace:configure.ac:1091: -1- AC_DEFINE_TRACE_LITERAL([HAVE_REGEX_H])
+m4trace:configure.ac:1091: -1- m4_pattern_allow([^HAVE_REGEX_H$])
+m4trace:configure.ac:1091: -1- AH_OUTPUT([HAVE_REGEX_H], [/* Regular expression header file exists */
+@%:@undef HAVE_REGEX_H])
+m4trace:configure.ac:1100: -1- _m4_warn([obsolete], [The macro `AC_LANG_SAVE' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/lang.m4:126: AC_LANG_SAVE is expanded from...
+m4/acx_pthread.m4:49: ACX_PTHREAD is expanded from...
+configure.ac:1100: the top level])
+m4trace:configure.ac:1100: -1- _m4_warn([obsolete], [The macro `AC_LANG_C' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/c.m4:73: AC_LANG_C is expanded from...
+m4/acx_pthread.m4:49: ACX_PTHREAD is expanded from...
+configure.ac:1100: the top level])
+m4trace:configure.ac:1100: -1- AC_SUBST([acx_pthread_config])
+m4trace:configure.ac:1100: -1- AC_SUBST_TRACE([acx_pthread_config])
+m4trace:configure.ac:1100: -1- m4_pattern_allow([^acx_pthread_config$])
+m4trace:configure.ac:1100: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:2688: AC_TRY_LINK is expanded from...
+m4/acx_pthread.m4:49: ACX_PTHREAD is expanded from...
+configure.ac:1100: the top level])
+m4trace:configure.ac:1100: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:2688: AC_TRY_LINK is expanded from...
+m4/acx_pthread.m4:49: ACX_PTHREAD is expanded from...
+configure.ac:1100: the top level])
+m4trace:configure.ac:1100: -1- AC_DEFINE_TRACE_LITERAL([PTHREAD_CREATE_JOINABLE])
+m4trace:configure.ac:1100: -1- m4_pattern_allow([^PTHREAD_CREATE_JOINABLE$])
+m4trace:configure.ac:1100: -1- AH_OUTPUT([PTHREAD_CREATE_JOINABLE], [/* Define to necessary symbol if this constant uses a non-standard name on
+ your system. */
+@%:@undef PTHREAD_CREATE_JOINABLE])
+m4trace:configure.ac:1100: -1- AC_SUBST([PTHREAD_CC])
+m4trace:configure.ac:1100: -1- AC_SUBST_TRACE([PTHREAD_CC])
+m4trace:configure.ac:1100: -1- m4_pattern_allow([^PTHREAD_CC$])
+m4trace:configure.ac:1100: -1- AC_SUBST([PTHREAD_LIBS])
+m4trace:configure.ac:1100: -1- AC_SUBST_TRACE([PTHREAD_LIBS])
+m4trace:configure.ac:1100: -1- m4_pattern_allow([^PTHREAD_LIBS$])
+m4trace:configure.ac:1100: -1- AC_SUBST([PTHREAD_CFLAGS])
+m4trace:configure.ac:1100: -1- AC_SUBST_TRACE([PTHREAD_CFLAGS])
+m4trace:configure.ac:1100: -1- m4_pattern_allow([^PTHREAD_CFLAGS$])
+m4trace:configure.ac:1100: -1- AC_SUBST([PTHREAD_CC])
+m4trace:configure.ac:1100: -1- AC_SUBST_TRACE([PTHREAD_CC])
+m4trace:configure.ac:1100: -1- m4_pattern_allow([^PTHREAD_CC$])
+m4trace:configure.ac:1100: -2- AC_DEFINE_TRACE_LITERAL([HAVE_PTHREAD])
+m4trace:configure.ac:1100: -2- m4_pattern_allow([^HAVE_PTHREAD$])
+m4trace:configure.ac:1100: -2- AH_OUTPUT([HAVE_PTHREAD], [/* Define if you have POSIX threads libraries and header files. */
+@%:@undef HAVE_PTHREAD])
+m4trace:configure.ac:1100: -1- AC_DEFINE_TRACE_LITERAL([HAVE_PTHREAD])
+m4trace:configure.ac:1100: -1- m4_pattern_allow([^HAVE_PTHREAD$])
+m4trace:configure.ac:1100: -1- AH_OUTPUT([HAVE_PTHREAD], [/* System has pthread support */
+@%:@undef HAVE_PTHREAD])
+m4trace:configure.ac:1100: -1- _m4_warn([obsolete], [The macro `AC_LANG_RESTORE' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/lang.m4:135: AC_LANG_RESTORE is expanded from...
+m4/acx_pthread.m4:49: ACX_PTHREAD is expanded from...
+configure.ac:1100: the top level])
+m4trace:configure.ac:1118: -1- AC_DEFINE_TRACE_LITERAL([HAVE_NANOSLEEP])
+m4trace:configure.ac:1118: -1- m4_pattern_allow([^HAVE_NANOSLEEP$])
+m4trace:configure.ac:1118: -1- AH_OUTPUT([HAVE_NANOSLEEP], [/* Define if system supports subsecond, non-alarm sleep */
+@%:@undef HAVE_NANOSLEEP])
+m4trace:configure.ac:1128: -1- AC_DEFINE_TRACE_LITERAL([STDC_HEADERS])
+m4trace:configure.ac:1128: -1- m4_pattern_allow([^STDC_HEADERS$])
+m4trace:configure.ac:1128: -1- AH_OUTPUT([STDC_HEADERS], [/* Define to 1 if you have the ANSI C header files. */
+@%:@undef STDC_HEADERS])
+m4trace:configure.ac:1129: -1- AH_OUTPUT([HAVE_DIRENT_H], [/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR\'.
+ */
+@%:@undef HAVE_DIRENT_H])
+m4trace:configure.ac:1129: -1- AH_OUTPUT([HAVE_SYS_NDIR_H], [/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR\'.
+ */
+@%:@undef HAVE_SYS_NDIR_H])
+m4trace:configure.ac:1129: -1- AH_OUTPUT([HAVE_SYS_DIR_H], [/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR\'.
+ */
+@%:@undef HAVE_SYS_DIR_H])
+m4trace:configure.ac:1129: -1- AH_OUTPUT([HAVE_NDIR_H], [/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR\'. */
+@%:@undef HAVE_NDIR_H])
+m4trace:configure.ac:1130: -1- AC_DEFINE_TRACE_LITERAL([STAT_MACROS_BROKEN])
+m4trace:configure.ac:1130: -1- m4_pattern_allow([^STAT_MACROS_BROKEN$])
+m4trace:configure.ac:1130: -1- AH_OUTPUT([STAT_MACROS_BROKEN], [/* Define to 1 if the `S_IS*\' macros in <sys/stat.h> do not work properly. */
+@%:@undef STAT_MACROS_BROKEN])
+m4trace:configure.ac:1131: -1- AC_DEFINE_TRACE_LITERAL([HAVE_SYS_WAIT_H])
+m4trace:configure.ac:1131: -1- m4_pattern_allow([^HAVE_SYS_WAIT_H$])
+m4trace:configure.ac:1131: -1- AH_OUTPUT([HAVE_SYS_WAIT_H], [/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */
+@%:@undef HAVE_SYS_WAIT_H])
+m4trace:configure.ac:1132: -1- AC_DEFINE_TRACE_LITERAL([TIME_WITH_SYS_TIME])
+m4trace:configure.ac:1132: -1- m4_pattern_allow([^TIME_WITH_SYS_TIME$])
+m4trace:configure.ac:1132: -1- AH_OUTPUT([TIME_WITH_SYS_TIME], [/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
+@%:@undef TIME_WITH_SYS_TIME])
+m4trace:configure.ac:1133: -1- AC_DEFINE_TRACE_LITERAL([GWINSZ_IN_SYS_IOCTL])
+m4trace:configure.ac:1133: -1- m4_pattern_allow([^GWINSZ_IN_SYS_IOCTL$])
+m4trace:configure.ac:1133: -1- AH_OUTPUT([GWINSZ_IN_SYS_IOCTL], [/* Define to 1 if `TIOCGWINSZ\' requires <sys/ioctl.h>. */
+@%:@undef GWINSZ_IN_SYS_IOCTL])
+m4trace:configure.ac:1135: -1- _m4_warn([obsolete], [The macro `AC_UNISTD_H' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/headers.m4:824: AC_UNISTD_H is expanded from...
+configure.ac:1135: the top level])
+m4trace:configure.ac:1135: -1- AH_OUTPUT([HAVE_UNISTD_H], [/* Define to 1 if you have the <unistd.h> header file. */
+@%:@undef HAVE_UNISTD_H])
+m4trace:configure.ac:1135: -1- AC_DEFINE_TRACE_LITERAL([HAVE_UNISTD_H])
+m4trace:configure.ac:1135: -1- m4_pattern_allow([^HAVE_UNISTD_H$])
+m4trace:configure.ac:1137: -1- AH_OUTPUT([HAVE_ERRNO_H], [/* Define to 1 if you have the <errno.h> header file. */
+@%:@undef HAVE_ERRNO_H])
+m4trace:configure.ac:1137: -1- AH_OUTPUT([HAVE_CTYPE_H], [/* Define to 1 if you have the <ctype.h> header file. */
+@%:@undef HAVE_CTYPE_H])
+m4trace:configure.ac:1137: -1- AH_OUTPUT([HAVE_FCNTL_H], [/* Define to 1 if you have the <fcntl.h> header file. */
+@%:@undef HAVE_FCNTL_H])
+m4trace:configure.ac:1137: -1- AH_OUTPUT([HAVE_SIGNAL_H], [/* Define to 1 if you have the <signal.h> header file. */
+@%:@undef HAVE_SIGNAL_H])
+m4trace:configure.ac:1137: -1- AH_OUTPUT([HAVE_SETJMP_H], [/* Define to 1 if you have the <setjmp.h> header file. */
+@%:@undef HAVE_SETJMP_H])
+m4trace:configure.ac:1137: -1- AH_OUTPUT([HAVE_MEMORY_H], [/* Define to 1 if you have the <memory.h> header file. */
+@%:@undef HAVE_MEMORY_H])
+m4trace:configure.ac:1137: -1- AH_OUTPUT([HAVE_SYS_PARAM_H], [/* Define to 1 if you have the <sys/param.h> header file. */
+@%:@undef HAVE_SYS_PARAM_H])
+m4trace:configure.ac:1137: -1- AH_OUTPUT([HAVE_SYS_SOCKET_H], [/* Define to 1 if you have the <sys/socket.h> header file. */
+@%:@undef HAVE_SYS_SOCKET_H])
+m4trace:configure.ac:1137: -1- AH_OUTPUT([HAVE_SYS_UIO_H], [/* Define to 1 if you have the <sys/uio.h> header file. */
+@%:@undef HAVE_SYS_UIO_H])
+m4trace:configure.ac:1137: -1- AH_OUTPUT([HAVE_SYS_UN_H], [/* Define to 1 if you have the <sys/un.h> header file. */
+@%:@undef HAVE_SYS_UN_H])
+m4trace:configure.ac:1137: -1- AH_OUTPUT([HAVE_LIMITS_H], [/* Define to 1 if you have the <limits.h> header file. */
+@%:@undef HAVE_LIMITS_H])
+m4trace:configure.ac:1137: -1- AH_OUTPUT([HAVE_WCHAR_H], [/* Define to 1 if you have the <wchar.h> header file. */
+@%:@undef HAVE_WCHAR_H])
+m4trace:configure.ac:1137: -1- AH_OUTPUT([HAVE_SYS_POLL_H], [/* Define to 1 if you have the <sys/poll.h> header file. */
+@%:@undef HAVE_SYS_POLL_H])
+m4trace:configure.ac:1137: -1- AH_OUTPUT([HAVE_STROPTS_H], [/* Define to 1 if you have the <stropts.h> header file. */
+@%:@undef HAVE_STROPTS_H])
+m4trace:configure.ac:1137: -1- AH_OUTPUT([HAVE_NETDB_H], [/* Define to 1 if you have the <netdb.h> header file. */
+@%:@undef HAVE_NETDB_H])
+m4trace:configure.ac:1137: -1- AH_OUTPUT([HAVE_SYSLOG_H], [/* Define to 1 if you have the <syslog.h> header file. */
+@%:@undef HAVE_SYSLOG_H])
+m4trace:configure.ac:1137: -1- AH_OUTPUT([HAVE_SYS_SYSLOG_H], [/* Define to 1 if you have the <sys/syslog.h> header file. */
+@%:@undef HAVE_SYS_SYSLOG_H])
+m4trace:configure.ac:1137: -1- AH_OUTPUT([HAVE_LOCALE_H], [/* Define to 1 if you have the <locale.h> header file. */
+@%:@undef HAVE_LOCALE_H])
+m4trace:configure.ac:1137: -1- AH_OUTPUT([HAVE_LANGINFO_H], [/* Define to 1 if you have the <langinfo.h> header file. */
+@%:@undef HAVE_LANGINFO_H])
+m4trace:configure.ac:1137: -1- AH_OUTPUT([HAVE_UTIME_H], [/* Define to 1 if you have the <utime.h> header file. */
+@%:@undef HAVE_UTIME_H])
+m4trace:configure.ac:1137: -1- AH_OUTPUT([HAVE_SYS_UTIME_H], [/* Define to 1 if you have the <sys/utime.h> header file. */
+@%:@undef HAVE_SYS_UTIME_H])
+m4trace:configure.ac:1137: -1- AH_OUTPUT([HAVE_PTHREAD_H], [/* Define to 1 if you have the <pthread.h> header file. */
+@%:@undef HAVE_PTHREAD_H])
+m4trace:configure.ac:1137: -1- AH_OUTPUT([HAVE_PWD_H], [/* Define to 1 if you have the <pwd.h> header file. */
+@%:@undef HAVE_PWD_H])
+m4trace:configure.ac:1137: -1- AH_OUTPUT([HAVE_ASSERT_H], [/* Define to 1 if you have the <assert.h> header file. */
+@%:@undef HAVE_ASSERT_H])
+m4trace:configure.ac:1164: -2- AC_DEFINE_TRACE_LITERAL([HAS_TERMIOS])
+m4trace:configure.ac:1164: -2- m4_pattern_allow([^HAS_TERMIOS$])
+m4trace:configure.ac:1164: -2- AH_OUTPUT([HAS_TERMIOS], [/* Define if systems uses termios terminal control */
+@%:@undef HAS_TERMIOS])
+m4trace:configure.ac:1163: -2- AC_DEFINE_TRACE_LITERAL([HAS_TERMIO])
+m4trace:configure.ac:1163: -2- m4_pattern_allow([^HAS_TERMIO$])
+m4trace:configure.ac:1163: -2- AH_OUTPUT([HAS_TERMIO], [/* Define if systems uses termio terminal control */
+@%:@undef HAS_TERMIO])
+m4trace:configure.ac:1163: -2- AC_DEFINE_TRACE_LITERAL([HAS_SGTTY])
+m4trace:configure.ac:1163: -2- m4_pattern_allow([^HAS_SGTTY$])
+m4trace:configure.ac:1163: -2- AH_OUTPUT([HAS_SGTTY], [/* Define if systems uses old BSD-style terminal control */
+@%:@undef HAS_SGTTY])
+m4trace:configure.ac:1163: -1- _m4_warn([obsolete], [The macro `AC_ERROR' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/oldnames.m4:34: AC_ERROR is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/headers.m4:129: _AC_CHECK_HEADER_MONGREL is expanded from...
+../../lib/autoconf/headers.m4:67: AC_CHECK_HEADER is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/headers.m4:129: _AC_CHECK_HEADER_MONGREL is expanded from...
+../../lib/autoconf/headers.m4:67: AC_CHECK_HEADER is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/headers.m4:129: _AC_CHECK_HEADER_MONGREL is expanded from...
+../../lib/autoconf/headers.m4:67: AC_CHECK_HEADER is expanded from...
+configure.ac:1163: the top level])
+m4trace:configure.ac:1180: -1- _m4_warn([obsolete], [The macro `AC_TYPE_SIGNAL' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/types.m4:738: AC_TYPE_SIGNAL is expanded from...
+configure.ac:1180: the top level])
+m4trace:configure.ac:1180: -1- AC_DEFINE_TRACE_LITERAL([RETSIGTYPE])
+m4trace:configure.ac:1180: -1- m4_pattern_allow([^RETSIGTYPE$])
+m4trace:configure.ac:1180: -1- AH_OUTPUT([RETSIGTYPE], [/* Define as the return type of signal handlers (`int\' or `void\'). */
+@%:@undef RETSIGTYPE])
+m4trace:configure.ac:1181: -1- AC_DEFINE_TRACE_LITERAL([size_t])
+m4trace:configure.ac:1181: -1- m4_pattern_allow([^size_t$])
+m4trace:configure.ac:1181: -1- AH_OUTPUT([size_t], [/* Define to `unsigned int\' if <sys/types.h> does not define. */
+@%:@undef size_t])
+m4trace:configure.ac:1182: -1- AC_DEFINE_TRACE_LITERAL([mode_t])
+m4trace:configure.ac:1182: -1- m4_pattern_allow([^mode_t$])
+m4trace:configure.ac:1182: -1- AH_OUTPUT([mode_t], [/* Define to `int\' if <sys/types.h> does not define. */
+@%:@undef mode_t])
+m4trace:configure.ac:1183: -1- AC_DEFINE_TRACE_LITERAL([pid_t])
+m4trace:configure.ac:1183: -1- m4_pattern_allow([^pid_t$])
+m4trace:configure.ac:1183: -1- AH_OUTPUT([pid_t], [/* Define to `int\' if <sys/types.h> does not define. */
+@%:@undef pid_t])
+m4trace:configure.ac:1184: -1- AC_DEFINE_TRACE_LITERAL([uid_t])
+m4trace:configure.ac:1184: -1- m4_pattern_allow([^uid_t$])
+m4trace:configure.ac:1184: -1- AH_OUTPUT([uid_t], [/* Define to `int\' if <sys/types.h> doesn\'t define. */
+@%:@undef uid_t])
+m4trace:configure.ac:1184: -1- AC_DEFINE_TRACE_LITERAL([gid_t])
+m4trace:configure.ac:1184: -1- m4_pattern_allow([^gid_t$])
+m4trace:configure.ac:1184: -1- AH_OUTPUT([gid_t], [/* Define to `int\' if <sys/types.h> doesn\'t define. */
+@%:@undef gid_t])
+m4trace:configure.ac:1185: -1- AC_DEFINE_TRACE_LITERAL([TM_IN_SYS_TIME])
+m4trace:configure.ac:1185: -1- m4_pattern_allow([^TM_IN_SYS_TIME$])
+m4trace:configure.ac:1185: -1- AH_OUTPUT([TM_IN_SYS_TIME], [/* Define to 1 if your <sys/time.h> declares `struct tm\'. */
+@%:@undef TM_IN_SYS_TIME])
+m4trace:configure.ac:1187: -1- AC_DEFINE_TRACE_LITERAL([HAVE_UNION_WAIT])
+m4trace:configure.ac:1187: -1- m4_pattern_allow([^HAVE_UNION_WAIT$])
+m4trace:configure.ac:1187: -1- AH_OUTPUT([HAVE_UNION_WAIT], [/* Define to 1 if the system has the type `union wait\'. */
+@%:@undef HAVE_UNION_WAIT])
+m4trace:configure.ac:1189: -1- AH_OUTPUT([HAVE_STDINT_H], [/* Define to 1 if you have the <stdint.h> header file. */
+@%:@undef HAVE_STDINT_H])
+m4trace:configure.ac:1189: -1- AC_DEFINE_TRACE_LITERAL([HAVE_STDINT_H])
+m4trace:configure.ac:1189: -1- m4_pattern_allow([^HAVE_STDINT_H$])
+m4trace:configure.ac:1189: -1- AH_OUTPUT([HAVE_INTTYPES_H], [/* Define to 1 if you have the <inttypes.h> header file. */
+@%:@undef HAVE_INTTYPES_H])
+m4trace:configure.ac:1189: -1- AC_DEFINE_TRACE_LITERAL([HAVE_INTTYPES_H])
+m4trace:configure.ac:1189: -1- m4_pattern_allow([^HAVE_INTTYPES_H$])
+m4trace:configure.ac:1189: -1- AH_OUTPUT([HAVE_SYS_TYPES_H], [/* Define to 1 if you have the <sys/types.h> header file. */
+@%:@undef HAVE_SYS_TYPES_H])
+m4trace:configure.ac:1189: -1- AC_DEFINE_TRACE_LITERAL([HAVE_SYS_TYPES_H])
+m4trace:configure.ac:1189: -1- m4_pattern_allow([^HAVE_SYS_TYPES_H$])
+m4trace:configure.ac:1189: -1- AC_DEFINE_TRACE_LITERAL([SIZEOF_UNSIGNED_SHORT])
+m4trace:configure.ac:1189: -1- m4_pattern_allow([^SIZEOF_UNSIGNED_SHORT$])
+m4trace:configure.ac:1189: -1- AH_OUTPUT([SIZEOF_UNSIGNED_SHORT], [/* The size of `unsigned short\', as computed by sizeof. */
+@%:@undef SIZEOF_UNSIGNED_SHORT])
+m4trace:configure.ac:1189: -1- AC_DEFINE_TRACE_LITERAL([SIZEOF_UNSIGNED_INT])
+m4trace:configure.ac:1189: -1- m4_pattern_allow([^SIZEOF_UNSIGNED_INT$])
+m4trace:configure.ac:1189: -1- AH_OUTPUT([SIZEOF_UNSIGNED_INT], [/* The size of `unsigned int\', as computed by sizeof. */
+@%:@undef SIZEOF_UNSIGNED_INT])
+m4trace:configure.ac:1189: -1- _m4_warn([obsolete], [The macro `AC_ERROR' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/oldnames.m4:34: AC_ERROR is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/headers.m4:129: _AC_CHECK_HEADER_MONGREL is expanded from...
+../../lib/autoconf/headers.m4:67: AC_CHECK_HEADER is expanded from...
+../../lib/m4sugar/m4sh.m4:574: AS_FOR is expanded from...
+../../lib/autoconf/headers.m4:249: AC_CHECK_HEADERS is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/headers.m4:129: _AC_CHECK_HEADER_MONGREL is expanded from...
+../../lib/autoconf/headers.m4:67: AC_CHECK_HEADER is expanded from...
+../../lib/m4sugar/m4sh.m4:574: AS_FOR is expanded from...
+../../lib/autoconf/headers.m4:249: AC_CHECK_HEADERS is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/headers.m4:129: _AC_CHECK_HEADER_MONGREL is expanded from...
+../../lib/autoconf/headers.m4:67: AC_CHECK_HEADER is expanded from...
+../../lib/m4sugar/m4sh.m4:574: AS_FOR is expanded from...
+../../lib/autoconf/headers.m4:249: AC_CHECK_HEADERS is expanded from...
+configure.ac:1189: the top level])
+m4trace:configure.ac:1206: -1- AC_DEFINE_TRACE_LITERAL([UINT16])
+m4trace:configure.ac:1206: -1- m4_pattern_allow([^UINT16$])
+m4trace:configure.ac:1206: -1- AH_OUTPUT([UINT16], [/* System defined unsigned 16 bit integer */
+@%:@undef UINT16])
+m4trace:configure.ac:1208: -1- AH_OUTPUT([HAVE_STDINT_H], [/* Define to 1 if you have the <stdint.h> header file. */
+@%:@undef HAVE_STDINT_H])
+m4trace:configure.ac:1208: -1- AC_DEFINE_TRACE_LITERAL([HAVE_STDINT_H])
+m4trace:configure.ac:1208: -1- m4_pattern_allow([^HAVE_STDINT_H$])
+m4trace:configure.ac:1208: -1- AH_OUTPUT([HAVE_INTTYPES_H], [/* Define to 1 if you have the <inttypes.h> header file. */
+@%:@undef HAVE_INTTYPES_H])
+m4trace:configure.ac:1208: -1- AC_DEFINE_TRACE_LITERAL([HAVE_INTTYPES_H])
+m4trace:configure.ac:1208: -1- m4_pattern_allow([^HAVE_INTTYPES_H$])
+m4trace:configure.ac:1208: -1- AH_OUTPUT([HAVE_SYS_TYPES_H], [/* Define to 1 if you have the <sys/types.h> header file. */
+@%:@undef HAVE_SYS_TYPES_H])
+m4trace:configure.ac:1208: -1- AC_DEFINE_TRACE_LITERAL([HAVE_SYS_TYPES_H])
+m4trace:configure.ac:1208: -1- m4_pattern_allow([^HAVE_SYS_TYPES_H$])
+m4trace:configure.ac:1208: -1- AC_DEFINE_TRACE_LITERAL([SIZEOF_UNSIGNED_INT])
+m4trace:configure.ac:1208: -1- m4_pattern_allow([^SIZEOF_UNSIGNED_INT$])
+m4trace:configure.ac:1208: -1- AH_OUTPUT([SIZEOF_UNSIGNED_INT], [/* The size of `unsigned int\', as computed by sizeof. */
+@%:@undef SIZEOF_UNSIGNED_INT])
+m4trace:configure.ac:1208: -1- AC_DEFINE_TRACE_LITERAL([SIZEOF_UNSIGNED_LONG])
+m4trace:configure.ac:1208: -1- m4_pattern_allow([^SIZEOF_UNSIGNED_LONG$])
+m4trace:configure.ac:1208: -1- AH_OUTPUT([SIZEOF_UNSIGNED_LONG], [/* The size of `unsigned long\', as computed by sizeof. */
+@%:@undef SIZEOF_UNSIGNED_LONG])
+m4trace:configure.ac:1208: -1- _m4_warn([obsolete], [The macro `AC_ERROR' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/oldnames.m4:34: AC_ERROR is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/headers.m4:129: _AC_CHECK_HEADER_MONGREL is expanded from...
+../../lib/autoconf/headers.m4:67: AC_CHECK_HEADER is expanded from...
+../../lib/m4sugar/m4sh.m4:574: AS_FOR is expanded from...
+../../lib/autoconf/headers.m4:249: AC_CHECK_HEADERS is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/headers.m4:129: _AC_CHECK_HEADER_MONGREL is expanded from...
+../../lib/autoconf/headers.m4:67: AC_CHECK_HEADER is expanded from...
+../../lib/m4sugar/m4sh.m4:574: AS_FOR is expanded from...
+../../lib/autoconf/headers.m4:249: AC_CHECK_HEADERS is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/headers.m4:129: _AC_CHECK_HEADER_MONGREL is expanded from...
+../../lib/autoconf/headers.m4:67: AC_CHECK_HEADER is expanded from...
+../../lib/m4sugar/m4sh.m4:574: AS_FOR is expanded from...
+../../lib/autoconf/headers.m4:249: AC_CHECK_HEADERS is expanded from...
+configure.ac:1208: the top level])
+m4trace:configure.ac:1225: -1- AC_DEFINE_TRACE_LITERAL([UINT32])
+m4trace:configure.ac:1225: -1- m4_pattern_allow([^UINT32$])
+m4trace:configure.ac:1225: -1- AH_OUTPUT([UINT32], [/* System defined unsigned 32 bit integer */
+@%:@undef UINT32])
+m4trace:configure.ac:1227: -1- _m4_warn([obsolete], [The macro `AC_TRY_COMPILE' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:2615: AC_TRY_COMPILE is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/general.m4:2032: AC_CACHE_VAL is expanded from...
+../../lib/autoconf/general.m4:2053: AC_CACHE_CHECK is expanded from...
+configure.ac:1227: the top level])
+m4trace:configure.ac:1244: -1- AC_DEFINE_TRACE_LITERAL([qsort_t])
+m4trace:configure.ac:1244: -1- m4_pattern_allow([^qsort_t$])
+m4trace:configure.ac:1244: -1- AH_OUTPUT([qsort_t], [/* qsort compare function argument type */
+@%:@undef qsort_t])
+m4trace:configure.ac:1248: -1- AH_OUTPUT([HAVE_SYS_SELECT_H], [/* Define to 1 if you have the <sys/select.h> header file. */
+@%:@undef HAVE_SYS_SELECT_H])
+m4trace:configure.ac:1248: -1- AH_OUTPUT([HAVE_SYS_SOCKET_H], [/* Define to 1 if you have the <sys/socket.h> header file. */
+@%:@undef HAVE_SYS_SOCKET_H])
+m4trace:configure.ac:1248: -1- AC_DEFINE_TRACE_LITERAL([SELECT_TYPE_ARG1])
+m4trace:configure.ac:1248: -1- m4_pattern_allow([^SELECT_TYPE_ARG1$])
+m4trace:configure.ac:1248: -1- AH_OUTPUT([SELECT_TYPE_ARG1], [/* Define to the type of arg 1 for `select\'. */
+@%:@undef SELECT_TYPE_ARG1])
+m4trace:configure.ac:1248: -1- AC_DEFINE_TRACE_LITERAL([SELECT_TYPE_ARG234])
+m4trace:configure.ac:1248: -1- m4_pattern_allow([^SELECT_TYPE_ARG234$])
+m4trace:configure.ac:1248: -1- AH_OUTPUT([SELECT_TYPE_ARG234], [/* Define to the type of args 2, 3 and 4 for `select\'. */
+@%:@undef SELECT_TYPE_ARG234])
+m4trace:configure.ac:1248: -1- AC_DEFINE_TRACE_LITERAL([SELECT_TYPE_ARG5])
+m4trace:configure.ac:1248: -1- m4_pattern_allow([^SELECT_TYPE_ARG5$])
+m4trace:configure.ac:1248: -1- AH_OUTPUT([SELECT_TYPE_ARG5], [/* Define to the type of arg 5 for `select\'. */
+@%:@undef SELECT_TYPE_ARG5])
+m4trace:configure.ac:1250: -1- AC_DEFINE_TRACE_LITERAL([HAVE_STRCOLL])
+m4trace:configure.ac:1250: -1- m4_pattern_allow([^HAVE_STRCOLL$])
+m4trace:configure.ac:1250: -1- AH_OUTPUT([HAVE_STRCOLL], [/* Define to 1 if you have the `strcoll\' function and it is properly defined.
+ */
+@%:@undef HAVE_STRCOLL])
+m4trace:configure.ac:1254: -1- AH_OUTPUT([HAVE_VFORK_H], [/* Define to 1 if you have the <vfork.h> header file. */
+@%:@undef HAVE_VFORK_H])
+m4trace:configure.ac:1254: -1- AC_DEFINE_TRACE_LITERAL([HAVE_VFORK_H])
+m4trace:configure.ac:1254: -1- m4_pattern_allow([^HAVE_VFORK_H$])
+m4trace:configure.ac:1254: -1- AH_OUTPUT([HAVE_FORK], [/* Define to 1 if you have the `fork\' function. */
+@%:@undef HAVE_FORK])
+m4trace:configure.ac:1254: -1- AH_OUTPUT([HAVE_VFORK], [/* Define to 1 if you have the `vfork\' function. */
+@%:@undef HAVE_VFORK])
+m4trace:configure.ac:1254: -1- AC_DEFINE_TRACE_LITERAL([HAVE_WORKING_VFORK])
+m4trace:configure.ac:1254: -1- m4_pattern_allow([^HAVE_WORKING_VFORK$])
+m4trace:configure.ac:1254: -1- AH_OUTPUT([HAVE_WORKING_VFORK], [/* Define to 1 if `vfork\' works. */
+@%:@undef HAVE_WORKING_VFORK])
+m4trace:configure.ac:1254: -1- AC_DEFINE_TRACE_LITERAL([vfork])
+m4trace:configure.ac:1254: -1- m4_pattern_allow([^vfork$])
+m4trace:configure.ac:1254: -1- AH_OUTPUT([vfork], [/* Define as `fork\' if `vfork\' does not work. */
+@%:@undef vfork])
+m4trace:configure.ac:1254: -1- AC_DEFINE_TRACE_LITERAL([HAVE_WORKING_FORK])
+m4trace:configure.ac:1254: -1- m4_pattern_allow([^HAVE_WORKING_FORK$])
+m4trace:configure.ac:1254: -1- AH_OUTPUT([HAVE_WORKING_FORK], [/* Define to 1 if `fork\' works. */
+@%:@undef HAVE_WORKING_FORK])
+m4trace:configure.ac:1255: -1- AH_OUTPUT([HAVE_STRCHR], [/* Define to 1 if you have the `strchr\' function. */
+@%:@undef HAVE_STRCHR])
+m4trace:configure.ac:1255: -1- AH_OUTPUT([HAVE_MEMCPY], [/* Define to 1 if you have the `memcpy\' function. */
+@%:@undef HAVE_MEMCPY])
+m4trace:configure.ac:1255: -1- AH_OUTPUT([HAVE_STRTOL], [/* Define to 1 if you have the `strtol\' function. */
+@%:@undef HAVE_STRTOL])
+m4trace:configure.ac:1255: -1- AH_OUTPUT([HAVE_STRTOUL], [/* Define to 1 if you have the `strtoul\' function. */
+@%:@undef HAVE_STRTOUL])
+m4trace:configure.ac:1255: -1- AH_OUTPUT([HAVE_SELECT], [/* Define to 1 if you have the `select\' function. */
+@%:@undef HAVE_SELECT])
+m4trace:configure.ac:1255: -1- AH_OUTPUT([HAVE_POLL], [/* Define to 1 if you have the `poll\' function. */
+@%:@undef HAVE_POLL])
+m4trace:configure.ac:1255: -1- AH_OUTPUT([HAVE_QSORT], [/* Define to 1 if you have the `qsort\' function. */
+@%:@undef HAVE_QSORT])
+m4trace:configure.ac:1255: -1- AH_OUTPUT([HAVE_GETUID], [/* Define to 1 if you have the `getuid\' function. */
+@%:@undef HAVE_GETUID])
+m4trace:configure.ac:1255: -1- AH_OUTPUT([HAVE_GETPWUID], [/* Define to 1 if you have the `getpwuid\' function. */
+@%:@undef HAVE_GETPWUID])
+m4trace:configure.ac:1255: -1- AH_OUTPUT([HAVE_GETPWNAM], [/* Define to 1 if you have the `getpwnam\' function. */
+@%:@undef HAVE_GETPWNAM])
+m4trace:configure.ac:1255: -1- AH_OUTPUT([HAVE_GETTIMEOFDAY], [/* Define to 1 if you have the `gettimeofday\' function. */
+@%:@undef HAVE_GETTIMEOFDAY])
+m4trace:configure.ac:1255: -1- AH_OUTPUT([HAVE_TMPFILE], [/* Define to 1 if you have the `tmpfile\' function. */
+@%:@undef HAVE_TMPFILE])
+m4trace:configure.ac:1255: -1- AH_OUTPUT([HAVE_UNAME], [/* Define to 1 if you have the `uname\' function. */
+@%:@undef HAVE_UNAME])
+m4trace:configure.ac:1255: -1- AH_OUTPUT([HAVE_RENAME], [/* Define to 1 if you have the `rename\' function. */
+@%:@undef HAVE_RENAME])
+m4trace:configure.ac:1255: -1- AH_OUTPUT([HAVE_READ], [/* Define to 1 if you have the `read\' function. */
+@%:@undef HAVE_READ])
+m4trace:configure.ac:1255: -1- AH_OUTPUT([HAVE_SIGNAL], [/* Define to 1 if you have the `signal\' function. */
+@%:@undef HAVE_SIGNAL])
+m4trace:configure.ac:1255: -1- AH_OUTPUT([HAVE_SETJMP], [/* Define to 1 if you have the `setjmp\' function. */
+@%:@undef HAVE_SETJMP])
+m4trace:configure.ac:1255: -1- AH_OUTPUT([HAVE_CHOWN], [/* Define to 1 if you have the `chown\' function. */
+@%:@undef HAVE_CHOWN])
+m4trace:configure.ac:1255: -1- AH_OUTPUT([HAVE_WAIT4], [/* Define to 1 if you have the `wait4\' function. */
+@%:@undef HAVE_WAIT4])
+m4trace:configure.ac:1255: -1- AH_OUTPUT([HAVE_WAITPID], [/* Define to 1 if you have the `waitpid\' function. */
+@%:@undef HAVE_WAITPID])
+m4trace:configure.ac:1255: -1- AH_OUTPUT([HAVE_WAIT], [/* Define to 1 if you have the `wait\' function. */
+@%:@undef HAVE_WAIT])
+m4trace:configure.ac:1255: -1- AH_OUTPUT([HAVE_SRANDOM], [/* Define to 1 if you have the `srandom\' function. */
+@%:@undef HAVE_SRANDOM])
+m4trace:configure.ac:1255: -1- AH_OUTPUT([HAVE_POPEN], [/* Define to 1 if you have the `popen\' function. */
+@%:@undef HAVE_POPEN])
+m4trace:configure.ac:1255: -1- AH_OUTPUT([HAVE_PCLOSE], [/* Define to 1 if you have the `pclose\' function. */
+@%:@undef HAVE_PCLOSE])
+m4trace:configure.ac:1255: -1- AH_OUTPUT([HAVE_FSYNC], [/* Define to 1 if you have the `fsync\' function. */
+@%:@undef HAVE_FSYNC])
+m4trace:configure.ac:1255: -1- AH_OUTPUT([HAVE_TRUNCATE], [/* Define to 1 if you have the `truncate\' function. */
+@%:@undef HAVE_TRUNCATE])
+m4trace:configure.ac:1255: -1- AH_OUTPUT([HAVE_LISTEN], [/* Define to 1 if you have the `listen\' function. */
+@%:@undef HAVE_LISTEN])
+m4trace:configure.ac:1255: -1- AH_OUTPUT([HAVE_WCWIDTH], [/* Define to 1 if you have the `wcwidth\' function. */
+@%:@undef HAVE_WCWIDTH])
+m4trace:configure.ac:1255: -1- AH_OUTPUT([HAVE_MBSTOWCS], [/* Define to 1 if you have the `mbstowcs\' function. */
+@%:@undef HAVE_MBSTOWCS])
+m4trace:configure.ac:1255: -1- AH_OUTPUT([HAVE_WCRTOMB], [/* Define to 1 if you have the `wcrtomb\' function. */
+@%:@undef HAVE_WCRTOMB])
+m4trace:configure.ac:1255: -1- AH_OUTPUT([HAVE_PUTENV], [/* Define to 1 if you have the `putenv\' function. */
+@%:@undef HAVE_PUTENV])
+m4trace:configure.ac:1255: -1- AH_OUTPUT([HAVE_SETENV], [/* Define to 1 if you have the `setenv\' function. */
+@%:@undef HAVE_SETENV])
+m4trace:configure.ac:1301: -1- AH_OUTPUT([HAVE_SIGACTION], [/* Define to 1 if you have the `sigaction\' function. */
+@%:@undef HAVE_SIGACTION])
+m4trace:configure.ac:1301: -1- AH_OUTPUT([HAVE_SIGEMPTYSET], [/* Define to 1 if you have the `sigemptyset\' function. */
+@%:@undef HAVE_SIGEMPTYSET])
+m4trace:configure.ac:1301: -1- AH_OUTPUT([HAVE_SIGADDSET], [/* Define to 1 if you have the `sigaddset\' function. */
+@%:@undef HAVE_SIGADDSET])
+m4trace:configure.ac:1301: -1- AH_OUTPUT([HAVE_SIGPROCMASK], [/* Define to 1 if you have the `sigprocmask\' function. */
+@%:@undef HAVE_SIGPROCMASK])
+m4trace:configure.ac:1301: -1- AC_DEFINE_TRACE_LITERAL([POSIX_SIGNALS])
+m4trace:configure.ac:1301: -1- m4_pattern_allow([^POSIX_SIGNALS$])
+m4trace:configure.ac:1301: -1- AH_OUTPUT([POSIX_SIGNALS], [/* Define if system supports POSIX signal interface */
+@%:@undef POSIX_SIGNALS])
+m4trace:configure.ac:1301: -1- AH_OUTPUT([HAVE_SIGSET], [/* Define to 1 if you have the `sigset\' function. */
+@%:@undef HAVE_SIGSET])
+m4trace:configure.ac:1301: -1- AH_OUTPUT([HAVE_SIGRELSE], [/* Define to 1 if you have the `sigrelse\' function. */
+@%:@undef HAVE_SIGRELSE])
+m4trace:configure.ac:1301: -1- AC_DEFINE_TRACE_LITERAL([SYSV_SIGNALS])
+m4trace:configure.ac:1301: -1- m4_pattern_allow([^SYSV_SIGNALS$])
+m4trace:configure.ac:1301: -1- AH_OUTPUT([SYSV_SIGNALS], [/* Define if system supports SYSV signal interface */
+@%:@undef SYSV_SIGNALS])
+m4trace:configure.ac:1312: -1- AC_DEFINE_TRACE_LITERAL([HAVE_SYSLOG])
+m4trace:configure.ac:1312: -1- m4_pattern_allow([^HAVE_SYSLOG$])
+m4trace:configure.ac:1312: -1- AH_OUTPUT([HAVE_SYSLOG], [/* Define if system supplies syslog() logging */
+@%:@undef HAVE_SYSLOG])
+m4trace:configure.ac:1376: -1- AC_DEFINE_TRACE_LITERAL([OSX_TARGET])
+m4trace:configure.ac:1376: -1- m4_pattern_allow([^OSX_TARGET$])
+m4trace:configure.ac:1376: -1- AH_OUTPUT([OSX_TARGET], [/* OSX TARGET */
+@%:@undef OSX_TARGET])
+m4trace:configure.ac:1381: -1- AC_DEFINE_TRACE_LITERAL([APPLEKEYCHAIN])
+m4trace:configure.ac:1381: -1- m4_pattern_allow([^APPLEKEYCHAIN$])
+m4trace:configure.ac:1381: -1- AH_OUTPUT([APPLEKEYCHAIN], [/* Use Apple OS X key chain for credential caching */
+@%:@undef APPLEKEYCHAIN])
+m4trace:configure.ac:1399: -1- AC_DEFINE_TRACE_LITERAL([__EXTENSIONS__])
+m4trace:configure.ac:1399: -1- m4_pattern_allow([^__EXTENSIONS__$])
+m4trace:configure.ac:1399: -1- AH_OUTPUT([__EXTENSIONS__], [/* Enable extended pthread features on Solaris */
+@%:@undef __EXTENSIONS__])
+m4trace:configure.ac:1505: -1- AC_DEFINE_TRACE_LITERAL([_WINDOWS])
+m4trace:configure.ac:1505: -1- m4_pattern_allow([^_WINDOWS$])
+m4trace:configure.ac:1505: -1- AH_OUTPUT([_WINDOWS], [/* Windows is just too different */
+@%:@undef _WINDOWS])
+m4trace:configure.ac:1508: -1- _m4_warn([obsolete], [The macro `AC_ERROR' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/oldnames.m4:34: AC_ERROR is expanded from...
+configure.ac:1508: the top level])
+m4trace:configure.ac:1528: -1- AC_DEFINE_TRACE_LITERAL([SYSTYPE])
+m4trace:configure.ac:1528: -1- m4_pattern_allow([^SYSTYPE$])
+m4trace:configure.ac:1528: -1- AH_OUTPUT([SYSTYPE], [/* Pine-Centric Host Specifier */
+@%:@undef SYSTYPE])
+m4trace:configure.ac:1529: -1- AC_DEFINE_TRACE_LITERAL([C_FILESEP])
+m4trace:configure.ac:1529: -1- m4_pattern_allow([^C_FILESEP$])
+m4trace:configure.ac:1529: -1- AH_OUTPUT([C_FILESEP], [/* File name separator as character constant */
+@%:@undef C_FILESEP])
+m4trace:configure.ac:1530: -1- AC_DEFINE_TRACE_LITERAL([S_FILESEP])
+m4trace:configure.ac:1530: -1- m4_pattern_allow([^S_FILESEP$])
+m4trace:configure.ac:1530: -1- AH_OUTPUT([S_FILESEP], [/* File name separator as string constant */
+@%:@undef S_FILESEP])
+m4trace:configure.ac:1531: -1- AC_DEFINE_TRACE_LITERAL([MAILDIR])
+m4trace:configure.ac:1531: -1- m4_pattern_allow([^MAILDIR$])
+m4trace:configure.ac:1531: -1- AH_OUTPUT([MAILDIR], [/* Path to local inboxes for pico newmail check */
+@%:@undef MAILDIR])
+m4trace:configure.ac:1532: -1- AC_DEFINE_TRACE_LITERAL([MODE_READONLY])
+m4trace:configure.ac:1532: -1- m4_pattern_allow([^MODE_READONLY$])
+m4trace:configure.ac:1532: -1- AH_OUTPUT([MODE_READONLY], [/* File mode used to set readonly access */
+@%:@undef MODE_READONLY])
+m4trace:configure.ac:1535: -1- AC_SUBST([C_CLIENT_TARGET], [$alpine_c_client_target])
+m4trace:configure.ac:1535: -1- AC_SUBST_TRACE([C_CLIENT_TARGET])
+m4trace:configure.ac:1535: -1- m4_pattern_allow([^C_CLIENT_TARGET$])
+m4trace:configure.ac:1536: -1- AC_SUBST([C_CLIENT_WITH_IPV6], [$c_client_ip6])
+m4trace:configure.ac:1536: -1- AC_SUBST_TRACE([C_CLIENT_WITH_IPV6])
+m4trace:configure.ac:1536: -1- m4_pattern_allow([^C_CLIENT_WITH_IPV6$])
+m4trace:configure.ac:1553: -1- AC_DEFINE_TRACE_LITERAL([SMIME])
+m4trace:configure.ac:1553: -1- m4_pattern_allow([^SMIME$])
+m4trace:configure.ac:1553: -1- AH_OUTPUT([SMIME], [/* Enable S/MIME code */
+@%:@undef SMIME])
+m4trace:configure.ac:1554: -1- AC_DEFINE_TRACE_LITERAL([SMIME_SSLCERTS])
+m4trace:configure.ac:1554: -1- m4_pattern_allow([^SMIME_SSLCERTS$])
+m4trace:configure.ac:1554: -1- AH_OUTPUT([SMIME_SSLCERTS], [/* Directory where S/MIME CACerts are located */
+@%:@undef SMIME_SSLCERTS])
+m4trace:configure.ac:1603: -1- AC_SUBST([C_CLIENT_CFLAGS], [EXTRACFLAGS=\"$alpine_c_client_cflags\"])
+m4trace:configure.ac:1603: -1- AC_SUBST_TRACE([C_CLIENT_CFLAGS])
+m4trace:configure.ac:1603: -1- m4_pattern_allow([^C_CLIENT_CFLAGS$])
+m4trace:configure.ac:1614: -1- AC_SUBST([C_CLIENT_LDFLAGS], [EXTRALDFLAGS=\"$alpine_c_client_ldflags\"])
+m4trace:configure.ac:1614: -1- AC_SUBST_TRACE([C_CLIENT_LDFLAGS])
+m4trace:configure.ac:1614: -1- m4_pattern_allow([^C_CLIENT_LDFLAGS$])
+m4trace:configure.ac:1618: -1- AC_SUBST([C_CLIENT_GCCOPTLEVEL], [GCCOPTLEVEL=\"$alpine_c_client_gccoptlevel\"])
+m4trace:configure.ac:1618: -1- AC_SUBST_TRACE([C_CLIENT_GCCOPTLEVEL])
+m4trace:configure.ac:1618: -1- m4_pattern_allow([^C_CLIENT_GCCOPTLEVEL$])
+m4trace:configure.ac:1621: -1- AC_SUBST([C_CLIENT_SPECIALS], [$c_client_specials])
+m4trace:configure.ac:1621: -1- AC_SUBST_TRACE([C_CLIENT_SPECIALS])
+m4trace:configure.ac:1621: -1- m4_pattern_allow([^C_CLIENT_SPECIALS$])
+m4trace:configure.ac:1641: -1- AC_DEFINE_TRACE_LITERAL([PUBCOOKIE])
+m4trace:configure.ac:1641: -1- m4_pattern_allow([^PUBCOOKIE$])
+m4trace:configure.ac:1641: -1- AH_OUTPUT([PUBCOOKIE], [/* Include support for UW Pubcookie Web Authentication */
+@%:@undef PUBCOOKIE])
+m4trace:configure.ac:1648: -1- AC_SUBST([REGEX_BUILD])
+m4trace:configure.ac:1648: -1- AC_SUBST_TRACE([REGEX_BUILD])
+m4trace:configure.ac:1648: -1- m4_pattern_allow([^REGEX_BUILD$])
+m4trace:configure.ac:1650: -1- AC_SUBST([WEB_BUILD])
+m4trace:configure.ac:1650: -1- AC_SUBST_TRACE([WEB_BUILD])
+m4trace:configure.ac:1650: -1- m4_pattern_allow([^WEB_BUILD$])
+m4trace:configure.ac:1651: -1- AC_SUBST([WEB_BINDIR])
+m4trace:configure.ac:1651: -1- AC_SUBST_TRACE([WEB_BINDIR])
+m4trace:configure.ac:1651: -1- m4_pattern_allow([^WEB_BINDIR$])
+m4trace:configure.ac:1652: -1- AC_SUBST([WEB_PUBCOOKIE_BUILD])
+m4trace:configure.ac:1652: -1- AC_SUBST_TRACE([WEB_PUBCOOKIE_BUILD])
+m4trace:configure.ac:1652: -1- m4_pattern_allow([^WEB_PUBCOOKIE_BUILD$])
+m4trace:configure.ac:1653: -1- AC_SUBST([WEB_PUBCOOKIE_LIB])
+m4trace:configure.ac:1653: -1- AC_SUBST_TRACE([WEB_PUBCOOKIE_LIB])
+m4trace:configure.ac:1653: -1- m4_pattern_allow([^WEB_PUBCOOKIE_LIB$])
+m4trace:configure.ac:1654: -1- AC_SUBST([WEB_PUBCOOKIE_LINK])
+m4trace:configure.ac:1654: -1- AC_SUBST_TRACE([WEB_PUBCOOKIE_LINK])
+m4trace:configure.ac:1654: -1- m4_pattern_allow([^WEB_PUBCOOKIE_LINK$])
+m4trace:configure.ac:1656: -1- AC_SUBST([AM_CFLAGS])
+m4trace:configure.ac:1656: -1- AC_SUBST_TRACE([AM_CFLAGS])
+m4trace:configure.ac:1656: -1- m4_pattern_allow([^AM_CFLAGS$])
+m4trace:configure.ac:1657: -1- AC_SUBST([AM_LDFLAGS])
+m4trace:configure.ac:1657: -1- AC_SUBST_TRACE([AM_LDFLAGS])
+m4trace:configure.ac:1657: -1- m4_pattern_allow([^AM_LDFLAGS$])
+m4trace:configure.ac:1659: -1- AC_CONFIG_FILES([m4/Makefile po/Makefile.in regex/Makefile \
+ pith/osdep/Makefile pith/charconv/Makefile pith/Makefile \
+ pico/osdep/Makefile pico/Makefile \
+ alpine/osdep/Makefile alpine/Makefile \
+ web/src/Makefile web/src/pubcookie/Makefile \
+ web/src/alpined.d/Makefile \
+ Makefile])
+m4trace:configure.ac:1659: -1- _m4_warn([obsolete], [AC_OUTPUT should be used without arguments.
+You should run autoupdate.], [])
+m4trace:configure.ac:1659: -1- AC_SUBST([LIB@&t@OBJS], [$ac_libobjs])
+m4trace:configure.ac:1659: -1- AC_SUBST_TRACE([LIB@&t@OBJS])
+m4trace:configure.ac:1659: -1- m4_pattern_allow([^LIB@&t@OBJS$])
+m4trace:configure.ac:1659: -1- AC_SUBST([LTLIBOBJS], [$ac_ltlibobjs])
+m4trace:configure.ac:1659: -1- AC_SUBST_TRACE([LTLIBOBJS])
+m4trace:configure.ac:1659: -1- m4_pattern_allow([^LTLIBOBJS$])
+m4trace:configure.ac:1659: -1- AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])
+m4trace:configure.ac:1659: -1- AC_SUBST([am__EXEEXT_TRUE])
+m4trace:configure.ac:1659: -1- AC_SUBST_TRACE([am__EXEEXT_TRUE])
+m4trace:configure.ac:1659: -1- m4_pattern_allow([^am__EXEEXT_TRUE$])
+m4trace:configure.ac:1659: -1- AC_SUBST([am__EXEEXT_FALSE])
+m4trace:configure.ac:1659: -1- AC_SUBST_TRACE([am__EXEEXT_FALSE])
+m4trace:configure.ac:1659: -1- m4_pattern_allow([^am__EXEEXT_FALSE$])
+m4trace:configure.ac:1659: -1- _AM_SUBST_NOTMAKE([am__EXEEXT_TRUE])
+m4trace:configure.ac:1659: -1- _AM_SUBST_NOTMAKE([am__EXEEXT_FALSE])
+m4trace:configure.ac:1659: -1- AC_SUBST_TRACE([top_builddir])
+m4trace:configure.ac:1659: -1- AC_SUBST_TRACE([top_build_prefix])
+m4trace:configure.ac:1659: -1- AC_SUBST_TRACE([srcdir])
+m4trace:configure.ac:1659: -1- AC_SUBST_TRACE([abs_srcdir])
+m4trace:configure.ac:1659: -1- AC_SUBST_TRACE([top_srcdir])
+m4trace:configure.ac:1659: -1- AC_SUBST_TRACE([abs_top_srcdir])
+m4trace:configure.ac:1659: -1- AC_SUBST_TRACE([builddir])
+m4trace:configure.ac:1659: -1- AC_SUBST_TRACE([abs_builddir])
+m4trace:configure.ac:1659: -1- AC_SUBST_TRACE([abs_top_builddir])
+m4trace:configure.ac:1659: -1- AC_SUBST_TRACE([INSTALL])
+m4trace:configure.ac:1659: -1- AC_SUBST_TRACE([MKDIR_P])
+m4trace:configure.ac:1659: -1- AC_REQUIRE_AUX_FILE([ltmain.sh])
diff --git a/autom4te.cache/traces.2 b/autom4te.cache/traces.2
new file mode 100644
index 00000000..f1f7ebcd
--- /dev/null
+++ b/autom4te.cache/traces.2
@@ -0,0 +1,6015 @@
+m4trace:/usr/share/aclocal/argz.m4:12: -1- AC_DEFUN([gl_FUNC_ARGZ], [gl_PREREQ_ARGZ
+
+AC_CHECK_HEADERS([argz.h], [], [], [AC_INCLUDES_DEFAULT])
+
+AC_CHECK_TYPES([error_t],
+ [],
+ [AC_DEFINE([error_t], [int],
+ [Define to a type to use for `error_t' if it is not otherwise available.])
+ AC_DEFINE([__error_t_defined], [1], [Define so that glibc/gnulib argp.h
+ does not typedef error_t.])],
+ [#if defined(HAVE_ARGZ_H)
+# include <argz.h>
+#endif])
+
+ARGZ_H=
+AC_CHECK_FUNCS([argz_add argz_append argz_count argz_create_sep argz_insert \
+ argz_next argz_stringify], [], [ARGZ_H=argz.h; AC_LIBOBJ([argz])])
+
+dnl if have system argz functions, allow forced use of
+dnl libltdl-supplied implementation (and default to do so
+dnl on "known bad" systems). Could use a runtime check, but
+dnl (a) detecting malloc issues is notoriously unreliable
+dnl (b) only known system that declares argz functions,
+dnl provides them, yet they are broken, is cygwin
+dnl releases prior to 16-Mar-2007 (1.5.24 and earlier)
+dnl So, it's more straightforward simply to special case
+dnl this for known bad systems.
+AS_IF([test -z "$ARGZ_H"],
+ [AC_CACHE_CHECK(
+ [if argz actually works],
+ [lt_cv_sys_argz_works],
+ [[case $host_os in #(
+ *cygwin*)
+ lt_cv_sys_argz_works=no
+ if test "$cross_compiling" != no; then
+ lt_cv_sys_argz_works="guessing no"
+ else
+ lt_sed_extract_leading_digits='s/^\([0-9\.]*\).*/\1/'
+ save_IFS=$IFS
+ IFS=-.
+ set x `uname -r | sed -e "$lt_sed_extract_leading_digits"`
+ IFS=$save_IFS
+ lt_os_major=${2-0}
+ lt_os_minor=${3-0}
+ lt_os_micro=${4-0}
+ if test "$lt_os_major" -gt 1 \
+ || { test "$lt_os_major" -eq 1 \
+ && { test "$lt_os_minor" -gt 5 \
+ || { test "$lt_os_minor" -eq 5 \
+ && test "$lt_os_micro" -gt 24; }; }; }; then
+ lt_cv_sys_argz_works=yes
+ fi
+ fi
+ ;; #(
+ *) lt_cv_sys_argz_works=yes ;;
+ esac]])
+ AS_IF([test $lt_cv_sys_argz_works = yes],
+ [AC_DEFINE([HAVE_WORKING_ARGZ], 1,
+ [This value is set to 1 to indicate that the system argz facility works])],
+ [ARGZ_H=argz.h
+ AC_LIBOBJ([argz])])])
+
+AC_SUBST([ARGZ_H])
+])
+m4trace:/usr/share/aclocal/argz.m4:79: -1- AC_DEFUN([gl_PREREQ_ARGZ], [:])
+m4trace:/usr/share/aclocal/ltdl.m4:16: -1- AC_DEFUN([LT_CONFIG_LTDL_DIR], [AC_BEFORE([$0], [LTDL_INIT])
+_$0($*)
+])
+m4trace:/usr/share/aclocal/ltdl.m4:68: -1- AC_DEFUN([LTDL_CONVENIENCE], [AC_BEFORE([$0], [LTDL_INIT])dnl
+dnl Although the argument is deprecated and no longer documented,
+dnl LTDL_CONVENIENCE used to take a DIRECTORY orgument, if we have one
+dnl here make sure it is the same as any other declaration of libltdl's
+dnl location! This also ensures lt_ltdl_dir is set when configure.ac is
+dnl not yet using an explicit LT_CONFIG_LTDL_DIR.
+m4_ifval([$1], [_LT_CONFIG_LTDL_DIR([$1])])dnl
+_$0()
+])
+m4trace:/usr/share/aclocal/ltdl.m4:81: -1- AU_DEFUN([AC_LIBLTDL_CONVENIENCE], [_LT_CONFIG_LTDL_DIR([m4_default([$1], [libltdl])])
+_LTDL_CONVENIENCE])
+m4trace:/usr/share/aclocal/ltdl.m4:81: -1- AC_DEFUN([AC_LIBLTDL_CONVENIENCE], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBLTDL_CONVENIENCE' is obsolete.
+You should run autoupdate.])dnl
+_LT_CONFIG_LTDL_DIR([m4_default([$1], [libltdl])])
+_LTDL_CONVENIENCE])
+m4trace:/usr/share/aclocal/ltdl.m4:124: -1- AC_DEFUN([LTDL_INSTALLABLE], [AC_BEFORE([$0], [LTDL_INIT])dnl
+dnl Although the argument is deprecated and no longer documented,
+dnl LTDL_INSTALLABLE used to take a DIRECTORY orgument, if we have one
+dnl here make sure it is the same as any other declaration of libltdl's
+dnl location! This also ensures lt_ltdl_dir is set when configure.ac is
+dnl not yet using an explicit LT_CONFIG_LTDL_DIR.
+m4_ifval([$1], [_LT_CONFIG_LTDL_DIR([$1])])dnl
+_$0()
+])
+m4trace:/usr/share/aclocal/ltdl.m4:137: -1- AU_DEFUN([AC_LIBLTDL_INSTALLABLE], [_LT_CONFIG_LTDL_DIR([m4_default([$1], [libltdl])])
+_LTDL_INSTALLABLE])
+m4trace:/usr/share/aclocal/ltdl.m4:137: -1- AC_DEFUN([AC_LIBLTDL_INSTALLABLE], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBLTDL_INSTALLABLE' is obsolete.
+You should run autoupdate.])dnl
+_LT_CONFIG_LTDL_DIR([m4_default([$1], [libltdl])])
+_LTDL_INSTALLABLE])
+m4trace:/usr/share/aclocal/ltdl.m4:213: -1- AC_DEFUN([_LT_LIBOBJ], [
+ m4_pattern_allow([^_LT_LIBOBJS$])
+ _LT_LIBOBJS="$_LT_LIBOBJS $1.$ac_objext"
+])
+m4trace:/usr/share/aclocal/ltdl.m4:226: -1- AC_DEFUN([LTDL_INIT], [dnl Parse OPTIONS
+_LT_SET_OPTIONS([$0], [$1])
+
+dnl We need to keep our own list of libobjs separate from our parent project,
+dnl and the easiest way to do that is redefine the AC_LIBOBJs macro while
+dnl we look for our own LIBOBJs.
+m4_pushdef([AC_LIBOBJ], m4_defn([_LT_LIBOBJ]))
+m4_pushdef([AC_LIBSOURCES])
+
+dnl If not otherwise defined, default to the 1.5.x compatible subproject mode:
+m4_if(_LTDL_MODE, [],
+ [m4_define([_LTDL_MODE], m4_default([$2], [subproject]))
+ m4_if([-1], [m4_bregexp(_LTDL_MODE, [\(subproject\|\(non\)?recursive\)])],
+ [m4_fatal([unknown libltdl mode: ]_LTDL_MODE)])])
+
+AC_ARG_WITH([included_ltdl],
+ [AS_HELP_STRING([--with-included-ltdl],
+ [use the GNU ltdl sources included here])])
+
+if test "x$with_included_ltdl" != xyes; then
+ # We are not being forced to use the included libltdl sources, so
+ # decide whether there is a useful installed version we can use.
+ AC_CHECK_HEADER([ltdl.h],
+ [AC_CHECK_DECL([lt_dlinterface_register],
+ [AC_CHECK_LIB([ltdl], [lt_dladvise_preload],
+ [with_included_ltdl=no],
+ [with_included_ltdl=yes])],
+ [with_included_ltdl=yes],
+ [AC_INCLUDES_DEFAULT
+ #include <ltdl.h>])],
+ [with_included_ltdl=yes],
+ [AC_INCLUDES_DEFAULT]
+ )
+fi
+
+dnl If neither LT_CONFIG_LTDL_DIR, LTDL_CONVENIENCE nor LTDL_INSTALLABLE
+dnl was called yet, then for old times' sake, we assume libltdl is in an
+dnl eponymous directory:
+AC_PROVIDE_IFELSE([LT_CONFIG_LTDL_DIR], [], [_LT_CONFIG_LTDL_DIR([libltdl])])
+
+AC_ARG_WITH([ltdl_include],
+ [AS_HELP_STRING([--with-ltdl-include=DIR],
+ [use the ltdl headers installed in DIR])])
+
+if test -n "$with_ltdl_include"; then
+ if test -f "$with_ltdl_include/ltdl.h"; then :
+ else
+ AC_MSG_ERROR([invalid ltdl include directory: `$with_ltdl_include'])
+ fi
+else
+ with_ltdl_include=no
+fi
+
+AC_ARG_WITH([ltdl_lib],
+ [AS_HELP_STRING([--with-ltdl-lib=DIR],
+ [use the libltdl.la installed in DIR])])
+
+if test -n "$with_ltdl_lib"; then
+ if test -f "$with_ltdl_lib/libltdl.la"; then :
+ else
+ AC_MSG_ERROR([invalid ltdl library directory: `$with_ltdl_lib'])
+ fi
+else
+ with_ltdl_lib=no
+fi
+
+case ,$with_included_ltdl,$with_ltdl_include,$with_ltdl_lib, in
+ ,yes,no,no,)
+ m4_case(m4_default(_LTDL_TYPE, [convenience]),
+ [convenience], [_LTDL_CONVENIENCE],
+ [installable], [_LTDL_INSTALLABLE],
+ [m4_fatal([unknown libltdl build type: ]_LTDL_TYPE)])
+ ;;
+ ,no,no,no,)
+ # If the included ltdl is not to be used, then use the
+ # preinstalled libltdl we found.
+ AC_DEFINE([HAVE_LTDL], [1],
+ [Define this if a modern libltdl is already installed])
+ LIBLTDL=-lltdl
+ LTDLDEPS=
+ LTDLINCL=
+ ;;
+ ,no*,no,*)
+ AC_MSG_ERROR([`--with-ltdl-include' and `--with-ltdl-lib' options must be used together])
+ ;;
+ *) with_included_ltdl=no
+ LIBLTDL="-L$with_ltdl_lib -lltdl"
+ LTDLDEPS=
+ LTDLINCL="-I$with_ltdl_include"
+ ;;
+esac
+INCLTDL="$LTDLINCL"
+
+# Report our decision...
+AC_MSG_CHECKING([where to find libltdl headers])
+AC_MSG_RESULT([$LTDLINCL])
+AC_MSG_CHECKING([where to find libltdl library])
+AC_MSG_RESULT([$LIBLTDL])
+
+_LTDL_SETUP
+
+dnl restore autoconf definition.
+m4_popdef([AC_LIBOBJ])
+m4_popdef([AC_LIBSOURCES])
+
+AC_CONFIG_COMMANDS_PRE([
+ _ltdl_libobjs=
+ _ltdl_ltlibobjs=
+ if test -n "$_LT_LIBOBJS"; then
+ # Remove the extension.
+ _lt_sed_drop_objext='s/\.o$//;s/\.obj$//'
+ for i in `for i in $_LT_LIBOBJS; do echo "$i"; done | sed "$_lt_sed_drop_objext" | sort -u`; do
+ _ltdl_libobjs="$_ltdl_libobjs $lt_libobj_prefix$i.$ac_objext"
+ _ltdl_ltlibobjs="$_ltdl_ltlibobjs $lt_libobj_prefix$i.lo"
+ done
+ fi
+ AC_SUBST([ltdl_LIBOBJS], [$_ltdl_libobjs])
+ AC_SUBST([ltdl_LTLIBOBJS], [$_ltdl_ltlibobjs])
+])
+
+# Only expand once:
+m4_define([LTDL_INIT])
+])
+m4trace:/usr/share/aclocal/ltdl.m4:352: -1- AU_DEFUN([AC_LIB_LTDL], [LTDL_INIT($@)])
+m4trace:/usr/share/aclocal/ltdl.m4:352: -1- AC_DEFUN([AC_LIB_LTDL], [AC_DIAGNOSE([obsolete], [The macro `AC_LIB_LTDL' is obsolete.
+You should run autoupdate.])dnl
+LTDL_INIT($@)])
+m4trace:/usr/share/aclocal/ltdl.m4:353: -1- AU_DEFUN([AC_WITH_LTDL], [LTDL_INIT($@)])
+m4trace:/usr/share/aclocal/ltdl.m4:353: -1- AC_DEFUN([AC_WITH_LTDL], [AC_DIAGNOSE([obsolete], [The macro `AC_WITH_LTDL' is obsolete.
+You should run autoupdate.])dnl
+LTDL_INIT($@)])
+m4trace:/usr/share/aclocal/ltdl.m4:354: -1- AU_DEFUN([LT_WITH_LTDL], [LTDL_INIT($@)])
+m4trace:/usr/share/aclocal/ltdl.m4:354: -1- AC_DEFUN([LT_WITH_LTDL], [AC_DIAGNOSE([obsolete], [The macro `LT_WITH_LTDL' is obsolete.
+You should run autoupdate.])dnl
+LTDL_INIT($@)])
+m4trace:/usr/share/aclocal/ltdl.m4:367: -1- AC_DEFUN([_LTDL_SETUP], [AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([LT_SYS_MODULE_EXT])dnl
+AC_REQUIRE([LT_SYS_MODULE_PATH])dnl
+AC_REQUIRE([LT_SYS_DLSEARCH_PATH])dnl
+AC_REQUIRE([LT_LIB_DLLOAD])dnl
+AC_REQUIRE([LT_SYS_SYMBOL_USCORE])dnl
+AC_REQUIRE([LT_FUNC_DLSYM_USCORE])dnl
+AC_REQUIRE([LT_SYS_DLOPEN_DEPLIBS])dnl
+AC_REQUIRE([gl_FUNC_ARGZ])dnl
+
+m4_require([_LT_CHECK_OBJDIR])dnl
+m4_require([_LT_HEADER_DLFCN])dnl
+m4_require([_LT_CHECK_DLPREOPEN])dnl
+m4_require([_LT_DECL_SED])dnl
+
+dnl Don't require this, or it will be expanded earlier than the code
+dnl that sets the variables it relies on:
+_LT_ENABLE_INSTALL
+
+dnl _LTDL_MODE specific code must be called at least once:
+_LTDL_MODE_DISPATCH
+
+# In order that ltdl.c can compile, find out the first AC_CONFIG_HEADERS
+# the user used. This is so that ltdl.h can pick up the parent projects
+# config.h file, The first file in AC_CONFIG_HEADERS must contain the
+# definitions required by ltdl.c.
+# FIXME: Remove use of undocumented AC_LIST_HEADERS (2.59 compatibility).
+AC_CONFIG_COMMANDS_PRE([dnl
+m4_pattern_allow([^LT_CONFIG_H$])dnl
+m4_ifset([AH_HEADER],
+ [LT_CONFIG_H=AH_HEADER],
+ [m4_ifset([AC_LIST_HEADERS],
+ [LT_CONFIG_H=`echo "AC_LIST_HEADERS" | $SED 's,^[[ ]]*,,;s,[[ :]].*$,,'`],
+ [])])])
+AC_SUBST([LT_CONFIG_H])
+
+AC_CHECK_HEADERS([unistd.h dl.h sys/dl.h dld.h mach-o/dyld.h dirent.h],
+ [], [], [AC_INCLUDES_DEFAULT])
+
+AC_CHECK_FUNCS([closedir opendir readdir], [], [AC_LIBOBJ([lt__dirent])])
+AC_CHECK_FUNCS([strlcat strlcpy], [], [AC_LIBOBJ([lt__strl])])
+
+AC_DEFINE_UNQUOTED([LT_LIBEXT],["$libext"],[The archive extension])
+
+name=ltdl
+LTDLOPEN=`eval "\\$ECHO \"$libname_spec\""`
+AC_SUBST([LTDLOPEN])
+])
+m4trace:/usr/share/aclocal/ltdl.m4:437: -1- AC_DEFUN([LT_SYS_DLOPEN_DEPLIBS], [AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_CACHE_CHECK([whether deplibs are loaded by dlopen],
+ [lt_cv_sys_dlopen_deplibs],
+ [# PORTME does your system automatically load deplibs for dlopen?
+ # or its logical equivalent (e.g. shl_load for HP-UX < 11)
+ # For now, we just catch OSes we know something about -- in the
+ # future, we'll try test this programmatically.
+ lt_cv_sys_dlopen_deplibs=unknown
+ case $host_os in
+ aix3*|aix4.1.*|aix4.2.*)
+ # Unknown whether this is true for these versions of AIX, but
+ # we want this `case' here to explicitly catch those versions.
+ lt_cv_sys_dlopen_deplibs=unknown
+ ;;
+ aix[[4-9]]*)
+ lt_cv_sys_dlopen_deplibs=yes
+ ;;
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ lt_cv_sys_dlopen_deplibs=no
+ ;;
+ esac
+ ;;
+ darwin*)
+ # Assuming the user has installed a libdl from somewhere, this is true
+ # If you are looking for one http://www.opendarwin.org/projects/dlcompat
+ lt_cv_sys_dlopen_deplibs=yes
+ ;;
+ freebsd* | dragonfly*)
+ lt_cv_sys_dlopen_deplibs=yes
+ ;;
+ gnu* | linux* | k*bsd*-gnu)
+ # GNU and its variants, using gnu ld.so (Glibc)
+ lt_cv_sys_dlopen_deplibs=yes
+ ;;
+ hpux10*|hpux11*)
+ lt_cv_sys_dlopen_deplibs=yes
+ ;;
+ interix*)
+ lt_cv_sys_dlopen_deplibs=yes
+ ;;
+ irix[[12345]]*|irix6.[[01]]*)
+ # Catch all versions of IRIX before 6.2, and indicate that we don't
+ # know how it worked for any of those versions.
+ lt_cv_sys_dlopen_deplibs=unknown
+ ;;
+ irix*)
+ # The case above catches anything before 6.2, and it's known that
+ # at 6.2 and later dlopen does load deplibs.
+ lt_cv_sys_dlopen_deplibs=yes
+ ;;
+ netbsd*)
+ lt_cv_sys_dlopen_deplibs=yes
+ ;;
+ openbsd*)
+ lt_cv_sys_dlopen_deplibs=yes
+ ;;
+ osf[[1234]]*)
+ # dlopen did load deplibs (at least at 4.x), but until the 5.x series,
+ # it did *not* use an RPATH in a shared library to find objects the
+ # library depends on, so we explicitly say `no'.
+ lt_cv_sys_dlopen_deplibs=no
+ ;;
+ osf5.0|osf5.0a|osf5.1)
+ # dlopen *does* load deplibs and with the right loader patch applied
+ # it even uses RPATH in a shared library to search for shared objects
+ # that the library depends on, but there's no easy way to know if that
+ # patch is installed. Since this is the case, all we can really
+ # say is unknown -- it depends on the patch being installed. If
+ # it is, this changes to `yes'. Without it, it would be `no'.
+ lt_cv_sys_dlopen_deplibs=unknown
+ ;;
+ osf*)
+ # the two cases above should catch all versions of osf <= 5.1. Read
+ # the comments above for what we know about them.
+ # At > 5.1, deplibs are loaded *and* any RPATH in a shared library
+ # is used to find them so we can finally say `yes'.
+ lt_cv_sys_dlopen_deplibs=yes
+ ;;
+ qnx*)
+ lt_cv_sys_dlopen_deplibs=yes
+ ;;
+ solaris*)
+ lt_cv_sys_dlopen_deplibs=yes
+ ;;
+ sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+ libltdl_cv_sys_dlopen_deplibs=yes
+ ;;
+ esac
+ ])
+if test "$lt_cv_sys_dlopen_deplibs" != yes; then
+ AC_DEFINE([LTDL_DLOPEN_DEPLIBS], [1],
+ [Define if the OS needs help to load dependent libraries for dlopen().])
+fi
+])
+m4trace:/usr/share/aclocal/ltdl.m4:536: -1- AU_DEFUN([AC_LTDL_SYS_DLOPEN_DEPLIBS], [m4_if($#, 0, [LT_SYS_DLOPEN_DEPLIBS], [LT_SYS_DLOPEN_DEPLIBS($@)])])
+m4trace:/usr/share/aclocal/ltdl.m4:536: -1- AC_DEFUN([AC_LTDL_SYS_DLOPEN_DEPLIBS], [AC_DIAGNOSE([obsolete], [The macro `AC_LTDL_SYS_DLOPEN_DEPLIBS' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_SYS_DLOPEN_DEPLIBS], [LT_SYS_DLOPEN_DEPLIBS($@)])])
+m4trace:/usr/share/aclocal/ltdl.m4:543: -1- AC_DEFUN([LT_SYS_MODULE_EXT], [m4_require([_LT_SYS_DYNAMIC_LINKER])dnl
+AC_CACHE_CHECK([which extension is used for runtime loadable modules],
+ [libltdl_cv_shlibext],
+[
+module=yes
+eval libltdl_cv_shlibext=$shrext_cmds
+ ])
+if test -n "$libltdl_cv_shlibext"; then
+ m4_pattern_allow([LT_MODULE_EXT])dnl
+ AC_DEFINE_UNQUOTED([LT_MODULE_EXT], ["$libltdl_cv_shlibext"],
+ [Define to the extension used for runtime loadable modules, say, ".so".])
+fi
+])
+m4trace:/usr/share/aclocal/ltdl.m4:559: -1- AU_DEFUN([AC_LTDL_SHLIBEXT], [m4_if($#, 0, [LT_SYS_MODULE_EXT], [LT_SYS_MODULE_EXT($@)])])
+m4trace:/usr/share/aclocal/ltdl.m4:559: -1- AC_DEFUN([AC_LTDL_SHLIBEXT], [AC_DIAGNOSE([obsolete], [The macro `AC_LTDL_SHLIBEXT' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_SYS_MODULE_EXT], [LT_SYS_MODULE_EXT($@)])])
+m4trace:/usr/share/aclocal/ltdl.m4:566: -1- AC_DEFUN([LT_SYS_MODULE_PATH], [m4_require([_LT_SYS_DYNAMIC_LINKER])dnl
+AC_CACHE_CHECK([which variable specifies run-time module search path],
+ [lt_cv_module_path_var], [lt_cv_module_path_var="$shlibpath_var"])
+if test -n "$lt_cv_module_path_var"; then
+ m4_pattern_allow([LT_MODULE_PATH_VAR])dnl
+ AC_DEFINE_UNQUOTED([LT_MODULE_PATH_VAR], ["$lt_cv_module_path_var"],
+ [Define to the name of the environment variable that determines the run-time module search path.])
+fi
+])
+m4trace:/usr/share/aclocal/ltdl.m4:578: -1- AU_DEFUN([AC_LTDL_SHLIBPATH], [m4_if($#, 0, [LT_SYS_MODULE_PATH], [LT_SYS_MODULE_PATH($@)])])
+m4trace:/usr/share/aclocal/ltdl.m4:578: -1- AC_DEFUN([AC_LTDL_SHLIBPATH], [AC_DIAGNOSE([obsolete], [The macro `AC_LTDL_SHLIBPATH' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_SYS_MODULE_PATH], [LT_SYS_MODULE_PATH($@)])])
+m4trace:/usr/share/aclocal/ltdl.m4:585: -1- AC_DEFUN([LT_SYS_DLSEARCH_PATH], [m4_require([_LT_SYS_DYNAMIC_LINKER])dnl
+AC_CACHE_CHECK([for the default library search path],
+ [lt_cv_sys_dlsearch_path],
+ [lt_cv_sys_dlsearch_path="$sys_lib_dlsearch_path_spec"])
+if test -n "$lt_cv_sys_dlsearch_path"; then
+ sys_dlsearch_path=
+ for dir in $lt_cv_sys_dlsearch_path; do
+ if test -z "$sys_dlsearch_path"; then
+ sys_dlsearch_path="$dir"
+ else
+ sys_dlsearch_path="$sys_dlsearch_path$PATH_SEPARATOR$dir"
+ fi
+ done
+ m4_pattern_allow([LT_DLSEARCH_PATH])dnl
+ AC_DEFINE_UNQUOTED([LT_DLSEARCH_PATH], ["$sys_dlsearch_path"],
+ [Define to the system default library search path.])
+fi
+])
+m4trace:/usr/share/aclocal/ltdl.m4:606: -1- AU_DEFUN([AC_LTDL_SYSSEARCHPATH], [m4_if($#, 0, [LT_SYS_DLSEARCH_PATH], [LT_SYS_DLSEARCH_PATH($@)])])
+m4trace:/usr/share/aclocal/ltdl.m4:606: -1- AC_DEFUN([AC_LTDL_SYSSEARCHPATH], [AC_DIAGNOSE([obsolete], [The macro `AC_LTDL_SYSSEARCHPATH' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_SYS_DLSEARCH_PATH], [LT_SYS_DLSEARCH_PATH($@)])])
+m4trace:/usr/share/aclocal/ltdl.m4:632: -1- AC_DEFUN([LT_LIB_DLLOAD], [m4_pattern_allow([^LT_DLLOADERS$])
+LT_DLLOADERS=
+AC_SUBST([LT_DLLOADERS])
+
+AC_LANG_PUSH([C])
+
+LIBADD_DLOPEN=
+AC_SEARCH_LIBS([dlopen], [dl],
+ [AC_DEFINE([HAVE_LIBDL], [1],
+ [Define if you have the libdl library or equivalent.])
+ if test "$ac_cv_search_dlopen" != "none required" ; then
+ LIBADD_DLOPEN="-ldl"
+ fi
+ libltdl_cv_lib_dl_dlopen="yes"
+ LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}dlopen.la"],
+ [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#if HAVE_DLFCN_H
+# include <dlfcn.h>
+#endif
+ ]], [[dlopen(0, 0);]])],
+ [AC_DEFINE([HAVE_LIBDL], [1],
+ [Define if you have the libdl library or equivalent.])
+ libltdl_cv_func_dlopen="yes"
+ LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}dlopen.la"],
+ [AC_CHECK_LIB([svld], [dlopen],
+ [AC_DEFINE([HAVE_LIBDL], [1],
+ [Define if you have the libdl library or equivalent.])
+ LIBADD_DLOPEN="-lsvld" libltdl_cv_func_dlopen="yes"
+ LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}dlopen.la"])])])
+if test x"$libltdl_cv_func_dlopen" = xyes || test x"$libltdl_cv_lib_dl_dlopen" = xyes
+then
+ lt_save_LIBS="$LIBS"
+ LIBS="$LIBS $LIBADD_DLOPEN"
+ AC_CHECK_FUNCS([dlerror])
+ LIBS="$lt_save_LIBS"
+fi
+AC_SUBST([LIBADD_DLOPEN])
+
+LIBADD_SHL_LOAD=
+AC_CHECK_FUNC([shl_load],
+ [AC_DEFINE([HAVE_SHL_LOAD], [1],
+ [Define if you have the shl_load function.])
+ LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}shl_load.la"],
+ [AC_CHECK_LIB([dld], [shl_load],
+ [AC_DEFINE([HAVE_SHL_LOAD], [1],
+ [Define if you have the shl_load function.])
+ LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}shl_load.la"
+ LIBADD_SHL_LOAD="-ldld"])])
+AC_SUBST([LIBADD_SHL_LOAD])
+
+case $host_os in
+darwin[[1567]].*)
+# We only want this for pre-Mac OS X 10.4.
+ AC_CHECK_FUNC([_dyld_func_lookup],
+ [AC_DEFINE([HAVE_DYLD], [1],
+ [Define if you have the _dyld_func_lookup function.])
+ LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}dyld.la"])
+ ;;
+beos*)
+ LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}load_add_on.la"
+ ;;
+cygwin* | mingw* | os2* | pw32*)
+ AC_CHECK_DECLS([cygwin_conv_path], [], [], [[#include <sys/cygwin.h>]])
+ LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}loadlibrary.la"
+ ;;
+esac
+
+AC_CHECK_LIB([dld], [dld_link],
+ [AC_DEFINE([HAVE_DLD], [1],
+ [Define if you have the GNU dld library.])
+ LT_DLLOADERS="$LT_DLLOADERS ${lt_dlopen_dir+$lt_dlopen_dir/}dld_link.la"])
+AC_SUBST([LIBADD_DLD_LINK])
+
+m4_pattern_allow([^LT_DLPREOPEN$])
+LT_DLPREOPEN=
+if test -n "$LT_DLLOADERS"
+then
+ for lt_loader in $LT_DLLOADERS; do
+ LT_DLPREOPEN="$LT_DLPREOPEN-dlpreopen $lt_loader "
+ done
+ AC_DEFINE([HAVE_LIBDLLOADER], [1],
+ [Define if libdlloader will be built on this platform])
+fi
+AC_SUBST([LT_DLPREOPEN])
+
+dnl This isn't used anymore, but set it for backwards compatibility
+LIBADD_DL="$LIBADD_DLOPEN $LIBADD_SHL_LOAD"
+AC_SUBST([LIBADD_DL])
+
+AC_LANG_POP
+])
+m4trace:/usr/share/aclocal/ltdl.m4:725: -1- AU_DEFUN([AC_LTDL_DLLIB], [m4_if($#, 0, [LT_LIB_DLLOAD], [LT_LIB_DLLOAD($@)])])
+m4trace:/usr/share/aclocal/ltdl.m4:725: -1- AC_DEFUN([AC_LTDL_DLLIB], [AC_DIAGNOSE([obsolete], [The macro `AC_LTDL_DLLIB' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_LIB_DLLOAD], [LT_LIB_DLLOAD($@)])])
+m4trace:/usr/share/aclocal/ltdl.m4:733: -1- AC_DEFUN([LT_SYS_SYMBOL_USCORE], [m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl
+AC_CACHE_CHECK([for _ prefix in compiled symbols],
+ [lt_cv_sys_symbol_underscore],
+ [lt_cv_sys_symbol_underscore=no
+ cat > conftest.$ac_ext <<_LT_EOF
+void nm_test_func(){}
+int main(){nm_test_func;return 0;}
+_LT_EOF
+ if AC_TRY_EVAL(ac_compile); then
+ # Now try to grab the symbols.
+ ac_nlist=conftest.nm
+ if AC_TRY_EVAL(NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $ac_nlist) && test -s "$ac_nlist"; then
+ # See whether the symbols have a leading underscore.
+ if grep '^. _nm_test_func' "$ac_nlist" >/dev/null; then
+ lt_cv_sys_symbol_underscore=yes
+ else
+ if grep '^. nm_test_func ' "$ac_nlist" >/dev/null; then
+ :
+ else
+ echo "configure: cannot find nm_test_func in $ac_nlist" >&AS_MESSAGE_LOG_FD
+ fi
+ fi
+ else
+ echo "configure: cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD
+ fi
+ else
+ echo "configure: failed program was:" >&AS_MESSAGE_LOG_FD
+ cat conftest.c >&AS_MESSAGE_LOG_FD
+ fi
+ rm -rf conftest*
+ ])
+ sys_symbol_underscore=$lt_cv_sys_symbol_underscore
+ AC_SUBST([sys_symbol_underscore])
+])
+m4trace:/usr/share/aclocal/ltdl.m4:770: -1- AU_DEFUN([AC_LTDL_SYMBOL_USCORE], [m4_if($#, 0, [LT_SYS_SYMBOL_USCORE], [LT_SYS_SYMBOL_USCORE($@)])])
+m4trace:/usr/share/aclocal/ltdl.m4:770: -1- AC_DEFUN([AC_LTDL_SYMBOL_USCORE], [AC_DIAGNOSE([obsolete], [The macro `AC_LTDL_SYMBOL_USCORE' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_SYS_SYMBOL_USCORE], [LT_SYS_SYMBOL_USCORE($@)])])
+m4trace:/usr/share/aclocal/ltdl.m4:777: -1- AC_DEFUN([LT_FUNC_DLSYM_USCORE], [AC_REQUIRE([LT_SYS_SYMBOL_USCORE])dnl
+if test x"$lt_cv_sys_symbol_underscore" = xyes; then
+ if test x"$libltdl_cv_func_dlopen" = xyes ||
+ test x"$libltdl_cv_lib_dl_dlopen" = xyes ; then
+ AC_CACHE_CHECK([whether we have to add an underscore for dlsym],
+ [libltdl_cv_need_uscore],
+ [libltdl_cv_need_uscore=unknown
+ save_LIBS="$LIBS"
+ LIBS="$LIBS $LIBADD_DLOPEN"
+ _LT_TRY_DLOPEN_SELF(
+ [libltdl_cv_need_uscore=no], [libltdl_cv_need_uscore=yes],
+ [], [libltdl_cv_need_uscore=cross])
+ LIBS="$save_LIBS"
+ ])
+ fi
+fi
+
+if test x"$libltdl_cv_need_uscore" = xyes; then
+ AC_DEFINE([NEED_USCORE], [1],
+ [Define if dlsym() requires a leading underscore in symbol names.])
+fi
+])
+m4trace:/usr/share/aclocal/ltdl.m4:802: -1- AU_DEFUN([AC_LTDL_DLSYM_USCORE], [m4_if($#, 0, [LT_FUNC_DLSYM_USCORE], [LT_FUNC_DLSYM_USCORE($@)])])
+m4trace:/usr/share/aclocal/ltdl.m4:802: -1- AC_DEFUN([AC_LTDL_DLSYM_USCORE], [AC_DIAGNOSE([obsolete], [The macro `AC_LTDL_DLSYM_USCORE' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_FUNC_DLSYM_USCORE], [LT_FUNC_DLSYM_USCORE($@)])])
+m4trace:/usr/share/aclocal-1.11/amversion.m4:14: -1- AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version='1.11'
+dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
+dnl require some minimum version. Point them to the right macro.
+m4_if([$1], [1.11.1], [],
+ [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
+])
+m4trace:/usr/share/aclocal-1.11/amversion.m4:33: -1- AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], [AM_AUTOMAKE_VERSION([1.11.1])dnl
+m4_ifndef([AC_AUTOCONF_VERSION],
+ [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
+_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
+m4trace:/usr/share/aclocal-1.11/auxdir.m4:47: -1- AC_DEFUN([AM_AUX_DIR_EXPAND], [dnl Rely on autoconf to set up CDPATH properly.
+AC_PREREQ([2.50])dnl
+# expand $ac_aux_dir to an absolute path
+am_aux_dir=`cd $ac_aux_dir && pwd`
+])
+m4trace:/usr/share/aclocal-1.11/cond.m4:15: -1- AC_DEFUN([AM_CONDITIONAL], [AC_PREREQ(2.52)dnl
+ ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])],
+ [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
+AC_SUBST([$1_TRUE])dnl
+AC_SUBST([$1_FALSE])dnl
+_AM_SUBST_NOTMAKE([$1_TRUE])dnl
+_AM_SUBST_NOTMAKE([$1_FALSE])dnl
+m4_define([_AM_COND_VALUE_$1], [$2])dnl
+if $2; then
+ $1_TRUE=
+ $1_FALSE='#'
+else
+ $1_TRUE='#'
+ $1_FALSE=
+fi
+AC_CONFIG_COMMANDS_PRE(
+[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then
+ AC_MSG_ERROR([[conditional "$1" was never defined.
+Usually this means the macro was only invoked conditionally.]])
+fi])])
+m4trace:/usr/share/aclocal-1.11/depend.m4:28: -1- AC_DEFUN([_AM_DEPENDENCIES], [AC_REQUIRE([AM_SET_DEPDIR])dnl
+AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl
+AC_REQUIRE([AM_MAKE_INCLUDE])dnl
+AC_REQUIRE([AM_DEP_TRACK])dnl
+
+ifelse([$1], CC, [depcc="$CC" am_compiler_list=],
+ [$1], CXX, [depcc="$CXX" am_compiler_list=],
+ [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
+ [$1], UPC, [depcc="$UPC" am_compiler_list=],
+ [$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'],
+ [depcc="$$1" am_compiler_list=])
+
+AC_CACHE_CHECK([dependency style of $depcc],
+ [am_cv_$1_dependencies_compiler_type],
+[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+ # We make a subdir and do the tests there. Otherwise we can end up
+ # making bogus files that we don't know about and never remove. For
+ # instance it was reported that on HP-UX the gcc test will end up
+ # making a dummy file named `D' -- because `-MD' means `put the output
+ # in D'.
+ mkdir conftest.dir
+ # Copy depcomp to subdir because otherwise we won't find it if we're
+ # using a relative directory.
+ cp "$am_depcomp" conftest.dir
+ cd conftest.dir
+ # We will build objects and dependencies in a subdirectory because
+ # it helps to detect inapplicable dependency modes. For instance
+ # both Tru64's cc and ICC support -MD to output dependencies as a
+ # side effect of compilation, but ICC will put the dependencies in
+ # the current directory while Tru64 will put them in the object
+ # directory.
+ mkdir sub
+
+ am_cv_$1_dependencies_compiler_type=none
+ if test "$am_compiler_list" = ""; then
+ am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp`
+ fi
+ am__universal=false
+ m4_case([$1], [CC],
+ [case " $depcc " in #(
+ *\ -arch\ *\ -arch\ *) am__universal=true ;;
+ esac],
+ [CXX],
+ [case " $depcc " in #(
+ *\ -arch\ *\ -arch\ *) am__universal=true ;;
+ esac])
+
+ for depmode in $am_compiler_list; do
+ # Setup a source with many dependencies, because some compilers
+ # like to wrap large dependency lists on column 80 (with \), and
+ # we should not choose a depcomp mode which is confused by this.
+ #
+ # We need to recreate these files for each test, as the compiler may
+ # overwrite some of them when testing with obscure command lines.
+ # This happens at least with the AIX C compiler.
+ : > sub/conftest.c
+ for i in 1 2 3 4 5 6; do
+ echo '#include "conftst'$i'.h"' >> sub/conftest.c
+ # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
+ # Solaris 8's {/usr,}/bin/sh.
+ touch sub/conftst$i.h
+ done
+ echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+ # We check with `-c' and `-o' for the sake of the "dashmstdout"
+ # mode. It turns out that the SunPro C++ compiler does not properly
+ # handle `-M -o', and we need to detect this. Also, some Intel
+ # versions had trouble with output in subdirs
+ am__obj=sub/conftest.${OBJEXT-o}
+ am__minus_obj="-o $am__obj"
+ case $depmode in
+ gcc)
+ # This depmode causes a compiler race in universal mode.
+ test "$am__universal" = false || continue
+ ;;
+ nosideeffect)
+ # after this tag, mechanisms are not by side-effect, so they'll
+ # only be used when explicitly requested
+ if test "x$enable_dependency_tracking" = xyes; then
+ continue
+ else
+ break
+ fi
+ ;;
+ msvisualcpp | msvcmsys)
+ # This compiler won't grok `-c -o', but also, the minuso test has
+ # not run yet. These depmodes are late enough in the game, and
+ # so weak that their functioning should not be impacted.
+ am__obj=conftest.${OBJEXT-o}
+ am__minus_obj=
+ ;;
+ none) break ;;
+ esac
+ if depmode=$depmode \
+ source=sub/conftest.c object=$am__obj \
+ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
+ >/dev/null 2>conftest.err &&
+ grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
+ ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+ # icc doesn't choke on unknown options, it will just issue warnings
+ # or remarks (even with -Werror). So we grep stderr for any message
+ # that says an option was ignored or not supported.
+ # When given -MP, icc 7.0 and 7.1 complain thusly:
+ # icc: Command line warning: ignoring option '-M'; no argument required
+ # The diagnosis changed in icc 8.0:
+ # icc: Command line remark: option '-MP' not supported
+ if (grep 'ignoring option' conftest.err ||
+ grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+ am_cv_$1_dependencies_compiler_type=$depmode
+ break
+ fi
+ fi
+ done
+
+ cd ..
+ rm -rf conftest.dir
+else
+ am_cv_$1_dependencies_compiler_type=none
+fi
+])
+AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type])
+AM_CONDITIONAL([am__fastdep$1], [
+ test "x$enable_dependency_tracking" != xno \
+ && test "$am_cv_$1_dependencies_compiler_type" = gcc3])
+])
+m4trace:/usr/share/aclocal-1.11/depend.m4:163: -1- AC_DEFUN([AM_SET_DEPDIR], [AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl
+])
+m4trace:/usr/share/aclocal-1.11/depend.m4:171: -1- AC_DEFUN([AM_DEP_TRACK], [AC_ARG_ENABLE(dependency-tracking,
+[ --disable-dependency-tracking speeds up one-time build
+ --enable-dependency-tracking do not reject slow dependency extractors])
+if test "x$enable_dependency_tracking" != xno; then
+ am_depcomp="$ac_aux_dir/depcomp"
+ AMDEPBACKSLASH='\'
+fi
+AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
+AC_SUBST([AMDEPBACKSLASH])dnl
+_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl
+])
+m4trace:/usr/share/aclocal-1.11/depout.m4:14: -1- AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], [{
+ # Autoconf 2.62 quotes --file arguments for eval, but not when files
+ # are listed without --file. Let's play safe and only enable the eval
+ # if we detect the quoting.
+ case $CONFIG_FILES in
+ *\'*) eval set x "$CONFIG_FILES" ;;
+ *) set x $CONFIG_FILES ;;
+ esac
+ shift
+ for mf
+ do
+ # Strip MF so we end up with the name of the file.
+ mf=`echo "$mf" | sed -e 's/:.*$//'`
+ # Check whether this is an Automake generated Makefile or not.
+ # We used to match only the files named `Makefile.in', but
+ # some people rename them; so instead we look at the file content.
+ # Grep'ing the first line is not enough: some people post-process
+ # each Makefile.in and add a new line on top of each file to say so.
+ # Grep'ing the whole file is not good either: AIX grep has a line
+ # limit of 2048, but all sed's we know have understand at least 4000.
+ if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
+ dirpart=`AS_DIRNAME("$mf")`
+ else
+ continue
+ fi
+ # Extract the definition of DEPDIR, am__include, and am__quote
+ # from the Makefile without running `make'.
+ DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
+ test -z "$DEPDIR" && continue
+ am__include=`sed -n 's/^am__include = //p' < "$mf"`
+ test -z "am__include" && continue
+ am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
+ # When using ansi2knr, U may be empty or an underscore; expand it
+ U=`sed -n 's/^U = //p' < "$mf"`
+ # Find all dependency output files, they are included files with
+ # $(DEPDIR) in their names. We invoke sed twice because it is the
+ # simplest approach to changing $(DEPDIR) to its actual value in the
+ # expansion.
+ for file in `sed -n "
+ s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
+ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
+ # Make sure the directory exists.
+ test -f "$dirpart/$file" && continue
+ fdir=`AS_DIRNAME(["$file"])`
+ AS_MKDIR_P([$dirpart/$fdir])
+ # echo "creating $dirpart/$file"
+ echo '# dummy' > "$dirpart/$file"
+ done
+ done
+}
+])
+m4trace:/usr/share/aclocal-1.11/depout.m4:75: -1- AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], [AC_CONFIG_COMMANDS([depfiles],
+ [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS],
+ [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"])
+])
+m4trace:/usr/share/aclocal-1.11/init.m4:26: -1- AC_DEFUN([AM_INIT_AUTOMAKE], [AC_PREREQ([2.62])dnl
+dnl Autoconf wants to disallow AM_ names. We explicitly allow
+dnl the ones we care about.
+m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
+AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
+AC_REQUIRE([AC_PROG_INSTALL])dnl
+if test "`cd $srcdir && pwd`" != "`pwd`"; then
+ # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
+ # is not polluted with repeated "-I."
+ AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl
+ # test to see if srcdir already configured
+ if test -f $srcdir/config.status; then
+ AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
+ fi
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+ if (cygpath --version) >/dev/null 2>/dev/null; then
+ CYGPATH_W='cygpath -w'
+ else
+ CYGPATH_W=echo
+ fi
+fi
+AC_SUBST([CYGPATH_W])
+
+# Define the identity of the package.
+dnl Distinguish between old-style and new-style calls.
+m4_ifval([$2],
+[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
+ AC_SUBST([PACKAGE], [$1])dnl
+ AC_SUBST([VERSION], [$2])],
+[_AM_SET_OPTIONS([$1])dnl
+dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT.
+m4_if(m4_ifdef([AC_PACKAGE_NAME], 1)m4_ifdef([AC_PACKAGE_VERSION], 1), 11,,
+ [m4_fatal([AC_INIT should be called with package and version arguments])])dnl
+ AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
+ AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
+
+_AM_IF_OPTION([no-define],,
+[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package])
+ AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl
+
+# Some tools Automake needs.
+AC_REQUIRE([AM_SANITY_CHECK])dnl
+AC_REQUIRE([AC_ARG_PROGRAM])dnl
+AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version})
+AM_MISSING_PROG(AUTOCONF, autoconf)
+AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version})
+AM_MISSING_PROG(AUTOHEADER, autoheader)
+AM_MISSING_PROG(MAKEINFO, makeinfo)
+AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
+AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl
+AC_REQUIRE([AM_PROG_MKDIR_P])dnl
+# We need awk for the "check" target. The system "awk" is bad on
+# some platforms.
+AC_REQUIRE([AC_PROG_AWK])dnl
+AC_REQUIRE([AC_PROG_MAKE_SET])dnl
+AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])],
+ [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])],
+ [_AM_PROG_TAR([v7])])])
+_AM_IF_OPTION([no-dependencies],,
+[AC_PROVIDE_IFELSE([AC_PROG_CC],
+ [_AM_DEPENDENCIES(CC)],
+ [define([AC_PROG_CC],
+ defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_CXX],
+ [_AM_DEPENDENCIES(CXX)],
+ [define([AC_PROG_CXX],
+ defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_OBJC],
+ [_AM_DEPENDENCIES(OBJC)],
+ [define([AC_PROG_OBJC],
+ defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])dnl
+])
+_AM_IF_OPTION([silent-rules], [AC_REQUIRE([AM_SILENT_RULES])])dnl
+dnl The `parallel-tests' driver may need to know about EXEEXT, so add the
+dnl `am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This macro
+dnl is hooked onto _AC_COMPILER_EXEEXT early, see below.
+AC_CONFIG_COMMANDS_PRE(dnl
+[m4_provide_if([_AM_COMPILER_EXEEXT],
+ [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl
+])
+m4trace:/usr/share/aclocal-1.11/init.m4:126: -1- AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], [# Compute $1's index in $config_headers.
+_am_arg=$1
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+ case $_am_header in
+ $_am_arg | $_am_arg:* )
+ break ;;
+ * )
+ _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+ esac
+done
+echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
+m4trace:/usr/share/aclocal-1.11/install-sh.m4:11: -1- AC_DEFUN([AM_PROG_INSTALL_SH], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+if test x"${install_sh}" != xset; then
+ case $am_aux_dir in
+ *\ * | *\ *)
+ install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
+ *)
+ install_sh="\${SHELL} $am_aux_dir/install-sh"
+ esac
+fi
+AC_SUBST(install_sh)])
+m4trace:/usr/share/aclocal-1.11/lead-dot.m4:12: -1- AC_DEFUN([AM_SET_LEADING_DOT], [rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+ am__leading_dot=.
+else
+ am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+AC_SUBST([am__leading_dot])])
+m4trace:/usr/share/aclocal-1.11/maintainer.m4:19: -1- AC_DEFUN([AM_MAINTAINER_MODE], [m4_case(m4_default([$1], [disable]),
+ [enable], [m4_define([am_maintainer_other], [disable])],
+ [disable], [m4_define([am_maintainer_other], [enable])],
+ [m4_define([am_maintainer_other], [enable])
+ m4_warn([syntax], [unexpected argument to AM@&t@_MAINTAINER_MODE: $1])])
+AC_MSG_CHECKING([whether to am_maintainer_other maintainer-specific portions of Makefiles])
+ dnl maintainer-mode's default is 'disable' unless 'enable' is passed
+ AC_ARG_ENABLE([maintainer-mode],
+[ --][am_maintainer_other][-maintainer-mode am_maintainer_other make rules and dependencies not useful
+ (and sometimes confusing) to the casual installer],
+ [USE_MAINTAINER_MODE=$enableval],
+ [USE_MAINTAINER_MODE=]m4_if(am_maintainer_other, [enable], [no], [yes]))
+ AC_MSG_RESULT([$USE_MAINTAINER_MODE])
+ AM_CONDITIONAL([MAINTAINER_MODE], [test $USE_MAINTAINER_MODE = yes])
+ MAINT=$MAINTAINER_MODE_TRUE
+ AC_SUBST([MAINT])dnl
+
+])
+m4trace:/usr/share/aclocal-1.11/maintainer.m4:39: -1- AU_DEFUN([jm_MAINTAINER_MODE], [AM_MAINTAINER_MODE])
+m4trace:/usr/share/aclocal-1.11/maintainer.m4:39: -1- AC_DEFUN([jm_MAINTAINER_MODE], [AC_DIAGNOSE([obsolete], [The macro `jm_MAINTAINER_MODE' is obsolete.
+You should run autoupdate.])dnl
+AM_MAINTAINER_MODE])
+m4trace:/usr/share/aclocal-1.11/make.m4:14: -1- AC_DEFUN([AM_MAKE_INCLUDE], [am_make=${MAKE-make}
+cat > confinc << 'END'
+am__doit:
+ @echo this is the am__doit target
+.PHONY: am__doit
+END
+# If we don't find an include directive, just comment out the code.
+AC_MSG_CHECKING([for style of include used by $am_make])
+am__include="#"
+am__quote=
+_am_result=none
+# First try GNU make style include.
+echo "include confinc" > confmf
+# Ignore all kinds of additional output from `make'.
+case `$am_make -s -f confmf 2> /dev/null` in #(
+*the\ am__doit\ target*)
+ am__include=include
+ am__quote=
+ _am_result=GNU
+ ;;
+esac
+# Now try BSD make style include.
+if test "$am__include" = "#"; then
+ echo '.include "confinc"' > confmf
+ case `$am_make -s -f confmf 2> /dev/null` in #(
+ *the\ am__doit\ target*)
+ am__include=.include
+ am__quote="\""
+ _am_result=BSD
+ ;;
+ esac
+fi
+AC_SUBST([am__include])
+AC_SUBST([am__quote])
+AC_MSG_RESULT([$_am_result])
+rm -f confinc confmf
+])
+m4trace:/usr/share/aclocal-1.11/missing.m4:14: -1- AC_DEFUN([AM_MISSING_PROG], [AC_REQUIRE([AM_MISSING_HAS_RUN])
+$1=${$1-"${am_missing_run}$2"}
+AC_SUBST($1)])
+m4trace:/usr/share/aclocal-1.11/missing.m4:24: -1- AC_DEFUN([AM_MISSING_HAS_RUN], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+AC_REQUIRE_AUX_FILE([missing])dnl
+if test x"${MISSING+set}" != xset; then
+ case $am_aux_dir in
+ *\ * | *\ *)
+ MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
+ *)
+ MISSING="\${SHELL} $am_aux_dir/missing" ;;
+ esac
+fi
+# Use eval to expand $SHELL
+if eval "$MISSING --run true"; then
+ am_missing_run="$MISSING --run "
+else
+ am_missing_run=
+ AC_MSG_WARN([`missing' script is too old or missing])
+fi
+])
+m4trace:/usr/share/aclocal-1.11/mkdirp.m4:11: -1- AC_DEFUN([AM_PROG_MKDIR_P], [AC_PREREQ([2.60])dnl
+AC_REQUIRE([AC_PROG_MKDIR_P])dnl
+dnl Automake 1.8 to 1.9.6 used to define mkdir_p. We now use MKDIR_P,
+dnl while keeping a definition of mkdir_p for backward compatibility.
+dnl @MKDIR_P@ is magic: AC_OUTPUT adjusts its value for each Makefile.
+dnl However we cannot define mkdir_p as $(MKDIR_P) for the sake of
+dnl Makefile.ins that do not define MKDIR_P, so we do our own
+dnl adjustment using top_builddir (which is defined more often than
+dnl MKDIR_P).
+AC_SUBST([mkdir_p], ["$MKDIR_P"])dnl
+case $mkdir_p in
+ [[\\/$]]* | ?:[[\\/]]*) ;;
+ */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;;
+esac
+])
+m4trace:/usr/share/aclocal-1.11/options.m4:13: -1- AC_DEFUN([_AM_MANGLE_OPTION], [[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])])
+m4trace:/usr/share/aclocal-1.11/options.m4:19: -1- AC_DEFUN([_AM_SET_OPTION], [m4_define(_AM_MANGLE_OPTION([$1]), 1)])
+m4trace:/usr/share/aclocal-1.11/options.m4:25: -1- AC_DEFUN([_AM_SET_OPTIONS], [m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])])
+m4trace:/usr/share/aclocal-1.11/options.m4:31: -1- AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
+m4trace:/usr/share/aclocal-1.11/runlog.m4:12: -1- AC_DEFUN([AM_RUN_LOG], [{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD
+ ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+ (exit $ac_status); }])
+m4trace:/usr/share/aclocal-1.11/sanity.m4:14: -1- AC_DEFUN([AM_SANITY_CHECK], [AC_MSG_CHECKING([whether build environment is sane])
+# Just in case
+sleep 1
+echo timestamp > conftest.file
+# Reject unsafe characters in $srcdir or the absolute working directory
+# name. Accept space and tab only in the latter.
+am_lf='
+'
+case `pwd` in
+ *[[\\\"\#\$\&\'\`$am_lf]]*)
+ AC_MSG_ERROR([unsafe absolute working directory name]);;
+esac
+case $srcdir in
+ *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*)
+ AC_MSG_ERROR([unsafe srcdir value: `$srcdir']);;
+esac
+
+# Do `set' in a subshell so we don't clobber the current shell's
+# arguments. Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+ set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
+ if test "$[*]" = "X"; then
+ # -L didn't work.
+ set X `ls -t "$srcdir/configure" conftest.file`
+ fi
+ rm -f conftest.file
+ if test "$[*]" != "X $srcdir/configure conftest.file" \
+ && test "$[*]" != "X conftest.file $srcdir/configure"; then
+
+ # If neither matched, then we have a broken ls. This can happen
+ # if, for instance, CONFIG_SHELL is bash and it inherits a
+ # broken ls alias from the environment. This has actually
+ # happened. Such a system could not be considered "sane".
+ AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken
+alias in your environment])
+ fi
+
+ test "$[2]" = conftest.file
+ )
+then
+ # Ok.
+ :
+else
+ AC_MSG_ERROR([newly created file is older than distributed files!
+Check your system clock])
+fi
+AC_MSG_RESULT(yes)])
+m4trace:/usr/share/aclocal-1.11/silent.m4:14: -1- AC_DEFUN([AM_SILENT_RULES], [AC_ARG_ENABLE([silent-rules],
+[ --enable-silent-rules less verbose build output (undo: `make V=1')
+ --disable-silent-rules verbose build output (undo: `make V=0')])
+case $enable_silent_rules in
+yes) AM_DEFAULT_VERBOSITY=0;;
+no) AM_DEFAULT_VERBOSITY=1;;
+*) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);;
+esac
+AC_SUBST([AM_DEFAULT_VERBOSITY])dnl
+AM_BACKSLASH='\'
+AC_SUBST([AM_BACKSLASH])dnl
+_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
+])
+m4trace:/usr/share/aclocal-1.11/strip.m4:17: -1- AC_DEFUN([AM_PROG_INSTALL_STRIP], [AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
+# Installed binaries are usually stripped using `strip' when the user
+# run `make install-strip'. However `strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the `STRIP' environment variable to overrule this program.
+dnl Don't test for $cross_compiling = yes, because it might be `maybe'.
+if test "$cross_compiling" != no; then
+ AC_CHECK_TOOL([STRIP], [strip], :)
+fi
+INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
+AC_SUBST([INSTALL_STRIP_PROGRAM])])
+m4trace:/usr/share/aclocal-1.11/substnot.m4:14: -1- AC_DEFUN([_AM_SUBST_NOTMAKE])
+m4trace:/usr/share/aclocal-1.11/substnot.m4:19: -1- AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
+m4trace:/usr/share/aclocal-1.11/tar.m4:24: -1- AC_DEFUN([_AM_PROG_TAR], [# Always define AMTAR for backward compatibility.
+AM_MISSING_PROG([AMTAR], [tar])
+m4_if([$1], [v7],
+ [am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'],
+ [m4_case([$1], [ustar],, [pax],,
+ [m4_fatal([Unknown tar format])])
+AC_MSG_CHECKING([how to create a $1 tar archive])
+# Loop over all known methods to create a tar archive until one works.
+_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none'
+_am_tools=${am_cv_prog_tar_$1-$_am_tools}
+# Do not fold the above two line into one, because Tru64 sh and
+# Solaris sh will not grok spaces in the rhs of `-'.
+for _am_tool in $_am_tools
+do
+ case $_am_tool in
+ gnutar)
+ for _am_tar in tar gnutar gtar;
+ do
+ AM_RUN_LOG([$_am_tar --version]) && break
+ done
+ am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
+ am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
+ am__untar="$_am_tar -xf -"
+ ;;
+ plaintar)
+ # Must skip GNU tar: if it does not support --format= it doesn't create
+ # ustar tarball either.
+ (tar --version) >/dev/null 2>&1 && continue
+ am__tar='tar chf - "$$tardir"'
+ am__tar_='tar chf - "$tardir"'
+ am__untar='tar xf -'
+ ;;
+ pax)
+ am__tar='pax -L -x $1 -w "$$tardir"'
+ am__tar_='pax -L -x $1 -w "$tardir"'
+ am__untar='pax -r'
+ ;;
+ cpio)
+ am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
+ am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
+ am__untar='cpio -i -H $1 -d'
+ ;;
+ none)
+ am__tar=false
+ am__tar_=false
+ am__untar=false
+ ;;
+ esac
+
+ # If the value was cached, stop now. We just wanted to have am__tar
+ # and am__untar set.
+ test -n "${am_cv_prog_tar_$1}" && break
+
+ # tar/untar a dummy directory, and stop if the command works
+ rm -rf conftest.dir
+ mkdir conftest.dir
+ echo GrepMe > conftest.dir/file
+ AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
+ rm -rf conftest.dir
+ if test -s conftest.tar; then
+ AM_RUN_LOG([$am__untar <conftest.tar])
+ grep GrepMe conftest.dir/file >/dev/null 2>&1 && break
+ fi
+done
+rm -rf conftest.dir
+
+AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
+AC_MSG_RESULT([$am_cv_prog_tar_$1])])
+AC_SUBST([am__tar])
+AC_SUBST([am__untar])
+])
+m4trace:m4/acx_pthread.m4:49: -1- AC_DEFUN([ACX_PTHREAD], [
+AC_REQUIRE([AC_CANONICAL_HOST])
+AC_LANG_SAVE
+AC_LANG_C
+acx_pthread_ok=no
+
+# We used to check for pthread.h first, but this fails if pthread.h
+# requires special compiler flags (e.g. on True64 or Sequent).
+# It gets checked for in the link test anyway.
+
+# First of all, check if the user has set any of the PTHREAD_LIBS,
+# etcetera environment variables, and if threads linking works using
+# them:
+if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+ save_LIBS="$LIBS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
+ AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes)
+ AC_MSG_RESULT($acx_pthread_ok)
+ if test x"$acx_pthread_ok" = xno; then
+ PTHREAD_LIBS=""
+ PTHREAD_CFLAGS=""
+ fi
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+fi
+
+# We must check for the threads library under a number of different
+# names; the ordering is very important because some systems
+# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
+# libraries is broken (non-POSIX).
+
+# Create a list of thread flags to try. Items starting with a "-" are
+# C compiler flags, and other items are library names, except for "none"
+# which indicates that we try without any flags at all, and "pthread-config"
+# which is a program returning the flags for the Pth emulation library.
+
+acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
+
+# The ordering *is* (sometimes) important. Some notes on the
+# individual items follow:
+
+# pthreads: AIX (must check this before -lpthread)
+# none: in case threads are in libc; should be tried before -Kthread and
+# other compiler flags to prevent continual compiler warnings
+# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
+# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
+# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
+# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
+# -pthreads: Solaris/gcc
+# -mthreads: Mingw32/gcc, Lynx/gcc
+# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
+# doesn't hurt to check since this sometimes defines pthreads too;
+# also defines -D_REENTRANT)
+# ... -mt is also the pthreads flag for HP/aCC
+# pthread: Linux, etcetera
+# --thread-safe: KAI C++
+# pthread-config: use pthread-config program (for GNU Pth library)
+
+case "${host_cpu}-${host_os}" in
+ *solaris*)
+
+ # On Solaris (at least, for some versions), libc contains stubbed
+ # (non-functional) versions of the pthreads routines, so link-based
+ # tests will erroneously succeed. (We need to link with -pthreads/-mt/
+ # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
+ # a function called by this macro, so we could check for that, but
+ # who knows whether they'll stub that too in a future libc.) So,
+ # we'll just look for -pthreads and -lpthread first:
+
+ acx_pthread_flags="-pthreads pthread -mt -pthread $acx_pthread_flags"
+ ;;
+esac
+
+if test x"$acx_pthread_ok" = xno; then
+for flag in $acx_pthread_flags; do
+
+ case $flag in
+ none)
+ AC_MSG_CHECKING([whether pthreads work without any flags])
+ ;;
+
+ -*)
+ AC_MSG_CHECKING([whether pthreads work with $flag])
+ PTHREAD_CFLAGS="$flag"
+ ;;
+
+ pthread-config)
+ AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no)
+ if test x"$acx_pthread_config" = xno; then continue; fi
+ PTHREAD_CFLAGS="`pthread-config --cflags`"
+ PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
+ ;;
+
+ *)
+ AC_MSG_CHECKING([for the pthreads library -l$flag])
+ PTHREAD_LIBS="-l$flag"
+ ;;
+ esac
+
+ save_LIBS="$LIBS"
+ save_CFLAGS="$CFLAGS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+ # Check for various functions. We must include pthread.h,
+ # since some functions may be macros. (On the Sequent, we
+ # need a special flag -Kthread to make this header compile.)
+ # We check for pthread_join because it is in -lpthread on IRIX
+ # while pthread_create is in libc. We check for pthread_attr_init
+ # due to DEC craziness with -lpthreads. We check for
+ # pthread_cleanup_push because it is one of the few pthread
+ # functions on Solaris that doesn't have a non-functional libc stub.
+ # We try pthread_create on general principles.
+ AC_TRY_LINK([#include <pthread.h>],
+ [pthread_t th; pthread_join(th, 0);
+ pthread_attr_init(0); pthread_cleanup_push(0, 0);
+ pthread_create(0,0,0,0); pthread_cleanup_pop(0); ],
+ [acx_pthread_ok=yes])
+
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+
+ AC_MSG_RESULT($acx_pthread_ok)
+ if test "x$acx_pthread_ok" = xyes; then
+ break;
+ fi
+
+ PTHREAD_LIBS=""
+ PTHREAD_CFLAGS=""
+done
+fi
+
+# Various other checks:
+if test "x$acx_pthread_ok" = xyes; then
+ save_LIBS="$LIBS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+ # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
+ AC_MSG_CHECKING([for joinable pthread attribute])
+ attr_name=unknown
+ for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
+ AC_TRY_LINK([#include <pthread.h>], [int attr=$attr; return attr;],
+ [attr_name=$attr; break])
+ done
+ AC_MSG_RESULT($attr_name)
+ if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
+ AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
+ [Define to necessary symbol if this constant
+ uses a non-standard name on your system.])
+ fi
+
+ AC_MSG_CHECKING([if more special flags are required for pthreads])
+ flag=no
+ case "${host_cpu}-${host_os}" in
+ *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";;
+ *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
+ esac
+ AC_MSG_RESULT(${flag})
+ if test "x$flag" != xno; then
+ PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
+ fi
+
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+
+ # More AIX lossage: must compile with xlc_r or cc_r
+ if test x"$GCC" != xyes; then
+ AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC})
+ else
+ PTHREAD_CC=$CC
+ fi
+else
+ PTHREAD_CC="$CC"
+fi
+
+AC_SUBST(PTHREAD_LIBS)
+AC_SUBST(PTHREAD_CFLAGS)
+AC_SUBST(PTHREAD_CC)
+
+# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
+if test x"$acx_pthread_ok" = xyes; then
+ ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
+ :
+else
+ acx_pthread_ok=no
+ $2
+fi
+AC_LANG_RESTORE
+])
+m4trace:m4/codeset.m4:9: -1- AC_DEFUN([AM_LANGINFO_CODESET], [
+ AC_CACHE_CHECK([for nl_langinfo and CODESET], am_cv_langinfo_codeset,
+ [AC_TRY_LINK([#include <langinfo.h>],
+ [char* cs = nl_langinfo(CODESET); return !cs;],
+ am_cv_langinfo_codeset=yes,
+ am_cv_langinfo_codeset=no)
+ ])
+ if test $am_cv_langinfo_codeset = yes; then
+ AC_DEFINE(HAVE_LANGINFO_CODESET, 1,
+ [Define if you have <langinfo.h> and nl_langinfo(CODESET).])
+ fi
+])
+m4trace:m4/gettext.m4:57: -1- AC_DEFUN([AM_GNU_GETTEXT], [
+ dnl Argument checking.
+ ifelse([$1], [], , [ifelse([$1], [external], , [ifelse([$1], [no-libtool], , [ifelse([$1], [use-libtool], ,
+ [errprint([ERROR: invalid first argument to AM_GNU_GETTEXT
+])])])])])
+ ifelse([$2], [], , [ifelse([$2], [need-ngettext], , [ifelse([$2], [need-formatstring-macros], ,
+ [errprint([ERROR: invalid second argument to AM_GNU_GETTEXT
+])])])])
+ define([gt_included_intl],
+ ifelse([$1], [external],
+ ifdef([AM_GNU_GETTEXT_][INTL_SUBDIR], [yes], [no]),
+ [yes]))
+ define([gt_libtool_suffix_prefix], ifelse([$1], [use-libtool], [l], []))
+ gt_NEEDS_INIT
+ AM_GNU_GETTEXT_NEED([$2])
+
+ AC_REQUIRE([AM_PO_SUBDIRS])dnl
+ ifelse(gt_included_intl, yes, [
+ AC_REQUIRE([AM_INTL_SUBDIR])dnl
+ ])
+
+ dnl Prerequisites of AC_LIB_LINKFLAGS_BODY.
+ AC_REQUIRE([AC_LIB_PREPARE_PREFIX])
+ AC_REQUIRE([AC_LIB_RPATH])
+
+ dnl Sometimes libintl requires libiconv, so first search for libiconv.
+ dnl Ideally we would do this search only after the
+ dnl if test "$USE_NLS" = "yes"; then
+ dnl if { eval "gt_val=\$$gt_func_gnugettext_libc"; test "$gt_val" != "yes"; }; then
+ dnl tests. But if configure.in invokes AM_ICONV after AM_GNU_GETTEXT
+ dnl the configure script would need to contain the same shell code
+ dnl again, outside any 'if'. There are two solutions:
+ dnl - Invoke AM_ICONV_LINKFLAGS_BODY here, outside any 'if'.
+ dnl - Control the expansions in more detail using AC_PROVIDE_IFELSE.
+ dnl Since AC_PROVIDE_IFELSE is only in autoconf >= 2.52 and not
+ dnl documented, we avoid it.
+ ifelse(gt_included_intl, yes, , [
+ AC_REQUIRE([AM_ICONV_LINKFLAGS_BODY])
+ ])
+
+ dnl Sometimes, on MacOS X, libintl requires linking with CoreFoundation.
+ gt_INTL_MACOSX
+
+ dnl Set USE_NLS.
+ AC_REQUIRE([AM_NLS])
+
+ ifelse(gt_included_intl, yes, [
+ BUILD_INCLUDED_LIBINTL=no
+ USE_INCLUDED_LIBINTL=no
+ ])
+ LIBINTL=
+ LTLIBINTL=
+ POSUB=
+
+ dnl Add a version number to the cache macros.
+ case " $gt_needs " in
+ *" need-formatstring-macros "*) gt_api_version=3 ;;
+ *" need-ngettext "*) gt_api_version=2 ;;
+ *) gt_api_version=1 ;;
+ esac
+ gt_func_gnugettext_libc="gt_cv_func_gnugettext${gt_api_version}_libc"
+ gt_func_gnugettext_libintl="gt_cv_func_gnugettext${gt_api_version}_libintl"
+
+ dnl If we use NLS figure out what method
+ if test "$USE_NLS" = "yes"; then
+ gt_use_preinstalled_gnugettext=no
+ ifelse(gt_included_intl, yes, [
+ AC_MSG_CHECKING([whether included gettext is requested])
+ AC_ARG_WITH(included-gettext,
+ [ --with-included-gettext use the GNU gettext library included here],
+ nls_cv_force_use_gnu_gettext=$withval,
+ nls_cv_force_use_gnu_gettext=no)
+ AC_MSG_RESULT($nls_cv_force_use_gnu_gettext)
+
+ nls_cv_use_gnu_gettext="$nls_cv_force_use_gnu_gettext"
+ if test "$nls_cv_force_use_gnu_gettext" != "yes"; then
+ ])
+ dnl User does not insist on using GNU NLS library. Figure out what
+ dnl to use. If GNU gettext is available we use this. Else we have
+ dnl to fall back to GNU NLS library.
+
+ if test $gt_api_version -ge 3; then
+ gt_revision_test_code='
+#ifndef __GNU_GETTEXT_SUPPORTED_REVISION
+#define __GNU_GETTEXT_SUPPORTED_REVISION(major) ((major) == 0 ? 0 : -1)
+#endif
+changequote(,)dnl
+typedef int array [2 * (__GNU_GETTEXT_SUPPORTED_REVISION(0) >= 1) - 1];
+changequote([,])dnl
+'
+ else
+ gt_revision_test_code=
+ fi
+ if test $gt_api_version -ge 2; then
+ gt_expression_test_code=' + * ngettext ("", "", 0)'
+ else
+ gt_expression_test_code=
+ fi
+
+ AC_CACHE_CHECK([for GNU gettext in libc], [$gt_func_gnugettext_libc],
+ [AC_TRY_LINK([#include <libintl.h>
+$gt_revision_test_code
+extern int _nl_msg_cat_cntr;
+extern int *_nl_domain_bindings;],
+ [bindtextdomain ("", "");
+return * gettext ("")$gt_expression_test_code + _nl_msg_cat_cntr + *_nl_domain_bindings],
+ [eval "$gt_func_gnugettext_libc=yes"],
+ [eval "$gt_func_gnugettext_libc=no"])])
+
+ if { eval "gt_val=\$$gt_func_gnugettext_libc"; test "$gt_val" != "yes"; }; then
+ dnl Sometimes libintl requires libiconv, so first search for libiconv.
+ ifelse(gt_included_intl, yes, , [
+ AM_ICONV_LINK
+ ])
+ dnl Search for libintl and define LIBINTL, LTLIBINTL and INCINTL
+ dnl accordingly. Don't use AC_LIB_LINKFLAGS_BODY([intl],[iconv])
+ dnl because that would add "-liconv" to LIBINTL and LTLIBINTL
+ dnl even if libiconv doesn't exist.
+ AC_LIB_LINKFLAGS_BODY([intl])
+ AC_CACHE_CHECK([for GNU gettext in libintl],
+ [$gt_func_gnugettext_libintl],
+ [gt_save_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $INCINTL"
+ gt_save_LIBS="$LIBS"
+ LIBS="$LIBS $LIBINTL"
+ dnl Now see whether libintl exists and does not depend on libiconv.
+ AC_TRY_LINK([#include <libintl.h>
+$gt_revision_test_code
+extern int _nl_msg_cat_cntr;
+extern
+#ifdef __cplusplus
+"C"
+#endif
+const char *_nl_expand_alias (const char *);],
+ [bindtextdomain ("", "");
+return * gettext ("")$gt_expression_test_code + _nl_msg_cat_cntr + *_nl_expand_alias ("")],
+ [eval "$gt_func_gnugettext_libintl=yes"],
+ [eval "$gt_func_gnugettext_libintl=no"])
+ dnl Now see whether libintl exists and depends on libiconv.
+ if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" != yes; } && test -n "$LIBICONV"; then
+ LIBS="$LIBS $LIBICONV"
+ AC_TRY_LINK([#include <libintl.h>
+$gt_revision_test_code
+extern int _nl_msg_cat_cntr;
+extern
+#ifdef __cplusplus
+"C"
+#endif
+const char *_nl_expand_alias (const char *);],
+ [bindtextdomain ("", "");
+return * gettext ("")$gt_expression_test_code + _nl_msg_cat_cntr + *_nl_expand_alias ("")],
+ [LIBINTL="$LIBINTL $LIBICONV"
+ LTLIBINTL="$LTLIBINTL $LTLIBICONV"
+ eval "$gt_func_gnugettext_libintl=yes"
+ ])
+ fi
+ CPPFLAGS="$gt_save_CPPFLAGS"
+ LIBS="$gt_save_LIBS"])
+ fi
+
+ dnl If an already present or preinstalled GNU gettext() is found,
+ dnl use it. But if this macro is used in GNU gettext, and GNU
+ dnl gettext is already preinstalled in libintl, we update this
+ dnl libintl. (Cf. the install rule in intl/Makefile.in.)
+ if { eval "gt_val=\$$gt_func_gnugettext_libc"; test "$gt_val" = "yes"; } \
+ || { { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = "yes"; } \
+ && test "$PACKAGE" != gettext-runtime \
+ && test "$PACKAGE" != gettext-tools; }; then
+ gt_use_preinstalled_gnugettext=yes
+ else
+ dnl Reset the values set by searching for libintl.
+ LIBINTL=
+ LTLIBINTL=
+ INCINTL=
+ fi
+
+ ifelse(gt_included_intl, yes, [
+ if test "$gt_use_preinstalled_gnugettext" != "yes"; then
+ dnl GNU gettext is not found in the C library.
+ dnl Fall back on included GNU gettext library.
+ nls_cv_use_gnu_gettext=yes
+ fi
+ fi
+
+ if test "$nls_cv_use_gnu_gettext" = "yes"; then
+ dnl Mark actions used to generate GNU NLS library.
+ BUILD_INCLUDED_LIBINTL=yes
+ USE_INCLUDED_LIBINTL=yes
+ LIBINTL="ifelse([$3],[],\${top_builddir}/intl,[$3])/libintl.[]gt_libtool_suffix_prefix[]a $LIBICONV $LIBTHREAD"
+ LTLIBINTL="ifelse([$3],[],\${top_builddir}/intl,[$3])/libintl.[]gt_libtool_suffix_prefix[]a $LTLIBICONV $LTLIBTHREAD"
+ LIBS=`echo " $LIBS " | sed -e 's/ -lintl / /' -e 's/^ //' -e 's/ $//'`
+ fi
+
+ CATOBJEXT=
+ if test "$gt_use_preinstalled_gnugettext" = "yes" \
+ || test "$nls_cv_use_gnu_gettext" = "yes"; then
+ dnl Mark actions to use GNU gettext tools.
+ CATOBJEXT=.gmo
+ fi
+ ])
+
+ if test -n "$INTL_MACOSX_LIBS"; then
+ if test "$gt_use_preinstalled_gnugettext" = "yes" \
+ || test "$nls_cv_use_gnu_gettext" = "yes"; then
+ dnl Some extra flags are needed during linking.
+ LIBINTL="$LIBINTL $INTL_MACOSX_LIBS"
+ LTLIBINTL="$LTLIBINTL $INTL_MACOSX_LIBS"
+ fi
+ fi
+
+ if test "$gt_use_preinstalled_gnugettext" = "yes" \
+ || test "$nls_cv_use_gnu_gettext" = "yes"; then
+ AC_DEFINE(ENABLE_NLS, 1,
+ [Define to 1 if translation of program messages to the user's native language
+ is requested.])
+ else
+ USE_NLS=no
+ fi
+ fi
+
+ AC_MSG_CHECKING([whether to use NLS])
+ AC_MSG_RESULT([$USE_NLS])
+ if test "$USE_NLS" = "yes"; then
+ AC_MSG_CHECKING([where the gettext function comes from])
+ if test "$gt_use_preinstalled_gnugettext" = "yes"; then
+ if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = "yes"; }; then
+ gt_source="external libintl"
+ else
+ gt_source="libc"
+ fi
+ else
+ gt_source="included intl directory"
+ fi
+ AC_MSG_RESULT([$gt_source])
+ fi
+
+ if test "$USE_NLS" = "yes"; then
+
+ if test "$gt_use_preinstalled_gnugettext" = "yes"; then
+ if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = "yes"; }; then
+ AC_MSG_CHECKING([how to link with libintl])
+ AC_MSG_RESULT([$LIBINTL])
+ AC_LIB_APPENDTOVAR([CPPFLAGS], [$INCINTL])
+ fi
+
+ dnl For backward compatibility. Some packages may be using this.
+ AC_DEFINE(HAVE_GETTEXT, 1,
+ [Define if the GNU gettext() function is already present or preinstalled.])
+ AC_DEFINE(HAVE_DCGETTEXT, 1,
+ [Define if the GNU dcgettext() function is already present or preinstalled.])
+ fi
+
+ dnl We need to process the po/ directory.
+ POSUB=po
+ fi
+
+ ifelse(gt_included_intl, yes, [
+ dnl If this is used in GNU gettext we have to set BUILD_INCLUDED_LIBINTL
+ dnl to 'yes' because some of the testsuite requires it.
+ if test "$PACKAGE" = gettext-runtime || test "$PACKAGE" = gettext-tools; then
+ BUILD_INCLUDED_LIBINTL=yes
+ fi
+
+ dnl Make all variables we use known to autoconf.
+ AC_SUBST(BUILD_INCLUDED_LIBINTL)
+ AC_SUBST(USE_INCLUDED_LIBINTL)
+ AC_SUBST(CATOBJEXT)
+
+ dnl For backward compatibility. Some configure.ins may be using this.
+ nls_cv_header_intl=
+ nls_cv_header_libgt=
+
+ dnl For backward compatibility. Some Makefiles may be using this.
+ DATADIRNAME=share
+ AC_SUBST(DATADIRNAME)
+
+ dnl For backward compatibility. Some Makefiles may be using this.
+ INSTOBJEXT=.mo
+ AC_SUBST(INSTOBJEXT)
+
+ dnl For backward compatibility. Some Makefiles may be using this.
+ GENCAT=gencat
+ AC_SUBST(GENCAT)
+
+ dnl For backward compatibility. Some Makefiles may be using this.
+ INTLOBJS=
+ if test "$USE_INCLUDED_LIBINTL" = yes; then
+ INTLOBJS="\$(GETTOBJS)"
+ fi
+ AC_SUBST(INTLOBJS)
+
+ dnl Enable libtool support if the surrounding package wishes it.
+ INTL_LIBTOOL_SUFFIX_PREFIX=gt_libtool_suffix_prefix
+ AC_SUBST(INTL_LIBTOOL_SUFFIX_PREFIX)
+ ])
+
+ dnl For backward compatibility. Some Makefiles may be using this.
+ INTLLIBS="$LIBINTL"
+ AC_SUBST(INTLLIBS)
+
+ dnl Make all documented variables known to autoconf.
+ AC_SUBST(LIBINTL)
+ AC_SUBST(LTLIBINTL)
+ AC_SUBST(POSUB)
+])
+m4trace:m4/gettext.m4:367: -1- AC_DEFUN([gt_INTL_MACOSX], [
+ dnl Check for API introduced in MacOS X 10.2.
+ AC_CACHE_CHECK([for CFPreferencesCopyAppValue],
+ gt_cv_func_CFPreferencesCopyAppValue,
+ [gt_save_LIBS="$LIBS"
+ LIBS="$LIBS -Wl,-framework -Wl,CoreFoundation"
+ AC_TRY_LINK([#include <CoreFoundation/CFPreferences.h>],
+ [CFPreferencesCopyAppValue(NULL, NULL)],
+ [gt_cv_func_CFPreferencesCopyAppValue=yes],
+ [gt_cv_func_CFPreferencesCopyAppValue=no])
+ LIBS="$gt_save_LIBS"])
+ if test $gt_cv_func_CFPreferencesCopyAppValue = yes; then
+ AC_DEFINE([HAVE_CFPREFERENCESCOPYAPPVALUE], 1,
+ [Define to 1 if you have the MacOS X function CFPreferencesCopyAppValue in the CoreFoundation framework.])
+ fi
+ dnl Check for API introduced in MacOS X 10.3.
+ AC_CACHE_CHECK([for CFLocaleCopyCurrent], gt_cv_func_CFLocaleCopyCurrent,
+ [gt_save_LIBS="$LIBS"
+ LIBS="$LIBS -Wl,-framework -Wl,CoreFoundation"
+ AC_TRY_LINK([#include <CoreFoundation/CFLocale.h>], [CFLocaleCopyCurrent();],
+ [gt_cv_func_CFLocaleCopyCurrent=yes],
+ [gt_cv_func_CFLocaleCopyCurrent=no])
+ LIBS="$gt_save_LIBS"])
+ if test $gt_cv_func_CFLocaleCopyCurrent = yes; then
+ AC_DEFINE([HAVE_CFLOCALECOPYCURRENT], 1,
+ [Define to 1 if you have the MacOS X function CFLocaleCopyCurrent in the CoreFoundation framework.])
+ fi
+ INTL_MACOSX_LIBS=
+ if test $gt_cv_func_CFPreferencesCopyAppValue = yes || test $gt_cv_func_CFLocaleCopyCurrent = yes; then
+ INTL_MACOSX_LIBS="-Wl,-framework -Wl,CoreFoundation"
+ fi
+ AC_SUBST([INTL_MACOSX_LIBS])
+])
+m4trace:m4/gettext.m4:412: -1- AC_DEFUN([AM_GNU_GETTEXT_NEED], [
+ m4_divert_text([INIT_PREPARE], [gt_needs="$gt_needs $1"])
+])
+m4trace:m4/gettext.m4:419: -1- AC_DEFUN([AM_GNU_GETTEXT_VERSION], [])
+m4trace:m4/glibc2.m4:10: -1- AC_DEFUN([gt_GLIBC2], [
+ AC_CACHE_CHECK(whether we are using the GNU C Library 2 or newer,
+ ac_cv_gnu_library_2,
+ [AC_EGREP_CPP([Lucky GNU user],
+ [
+#include <features.h>
+#ifdef __GNU_LIBRARY__
+ #if (__GLIBC__ >= 2)
+ Lucky GNU user
+ #endif
+#endif
+ ],
+ ac_cv_gnu_library_2=yes,
+ ac_cv_gnu_library_2=no)
+ ]
+ )
+ AC_SUBST(GLIBC2)
+ GLIBC2="$ac_cv_gnu_library_2"
+
+])
+m4trace:m4/glibc21.m4:10: -1- AC_DEFUN([gl_GLIBC21], [
+ AC_CACHE_CHECK(whether we are using the GNU C Library 2.1 or newer,
+ ac_cv_gnu_library_2_1,
+ [AC_EGREP_CPP([Lucky GNU user],
+ [
+#include <features.h>
+#ifdef __GNU_LIBRARY__
+ #if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1) || (__GLIBC__ > 2)
+ Lucky GNU user
+ #endif
+#endif
+ ],
+ ac_cv_gnu_library_2_1=yes,
+ ac_cv_gnu_library_2_1=no)
+ ]
+ )
+ AC_SUBST(GLIBC21)
+ GLIBC21="$ac_cv_gnu_library_2_1"
+
+])
+m4trace:m4/iconv.m4:9: -1- AC_DEFUN([AM_ICONV_LINKFLAGS_BODY], [
+ dnl Prerequisites of AC_LIB_LINKFLAGS_BODY.
+ AC_REQUIRE([AC_LIB_PREPARE_PREFIX])
+ AC_REQUIRE([AC_LIB_RPATH])
+
+ dnl Search for libiconv and define LIBICONV, LTLIBICONV and INCICONV
+ dnl accordingly.
+ AC_LIB_LINKFLAGS_BODY([iconv])
+])
+m4trace:m4/iconv.m4:20: -1- AC_DEFUN([AM_ICONV_LINK], [
+ dnl Some systems have iconv in libc, some have it in libiconv (OSF/1 and
+ dnl those with the standalone portable GNU libiconv installed).
+
+ dnl Search for libiconv and define LIBICONV, LTLIBICONV and INCICONV
+ dnl accordingly.
+ AC_REQUIRE([AM_ICONV_LINKFLAGS_BODY])
+
+ dnl Add $INCICONV to CPPFLAGS before performing the following checks,
+ dnl because if the user has installed libiconv and not disabled its use
+ dnl via --without-libiconv-prefix, he wants to use it. The first
+ dnl AC_TRY_LINK will then fail, the second AC_TRY_LINK will succeed.
+ am_save_CPPFLAGS="$CPPFLAGS"
+ AC_LIB_APPENDTOVAR([CPPFLAGS], [$INCICONV])
+
+ AC_CACHE_CHECK(for iconv, am_cv_func_iconv, [
+ am_cv_func_iconv="no, consider installing GNU libiconv"
+ am_cv_lib_iconv=no
+ AC_TRY_LINK([#include <stdlib.h>
+#include <iconv.h>],
+ [iconv_t cd = iconv_open("","");
+ iconv(cd,NULL,NULL,NULL,NULL);
+ iconv_close(cd);],
+ am_cv_func_iconv=yes)
+ if test "$am_cv_func_iconv" != yes; then
+ am_save_LIBS="$LIBS"
+ LIBS="$LIBS $LIBICONV"
+ AC_TRY_LINK([#include <stdlib.h>
+#include <iconv.h>],
+ [iconv_t cd = iconv_open("","");
+ iconv(cd,NULL,NULL,NULL,NULL);
+ iconv_close(cd);],
+ am_cv_lib_iconv=yes
+ am_cv_func_iconv=yes)
+ LIBS="$am_save_LIBS"
+ fi
+ ])
+ if test "$am_cv_func_iconv" = yes; then
+ AC_DEFINE(HAVE_ICONV, 1, [Define if you have the iconv() function.])
+ fi
+ if test "$am_cv_lib_iconv" = yes; then
+ AC_MSG_CHECKING([how to link with libiconv])
+ AC_MSG_RESULT([$LIBICONV])
+ else
+ dnl If $LIBICONV didn't lead to a usable library, we don't need $INCICONV
+ dnl either.
+ CPPFLAGS="$am_save_CPPFLAGS"
+ LIBICONV=
+ LTLIBICONV=
+ fi
+ AC_SUBST(LIBICONV)
+ AC_SUBST(LTLIBICONV)
+])
+m4trace:m4/iconv.m4:75: -1- AC_DEFUN([AM_ICONV], [
+ AM_ICONV_LINK
+ if test "$am_cv_func_iconv" = yes; then
+ AC_MSG_CHECKING([for iconv declaration])
+ AC_CACHE_VAL(am_cv_proto_iconv, [
+ AC_TRY_COMPILE([
+#include <stdlib.h>
+#include <iconv.h>
+extern
+#ifdef __cplusplus
+"C"
+#endif
+#if defined(__STDC__) || defined(__cplusplus)
+size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);
+#else
+size_t iconv();
+#endif
+], [], am_cv_proto_iconv_arg1="", am_cv_proto_iconv_arg1="const")
+ am_cv_proto_iconv="extern size_t iconv (iconv_t cd, $am_cv_proto_iconv_arg1 char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);"])
+ am_cv_proto_iconv=`echo "[$]am_cv_proto_iconv" | tr -s ' ' | sed -e 's/( /(/'`
+ AC_MSG_RESULT([$]{ac_t:-
+ }[$]am_cv_proto_iconv)
+ AC_DEFINE_UNQUOTED(ICONV_CONST, $am_cv_proto_iconv_arg1,
+ [Define as const if the declaration of iconv() needs const.])
+ fi
+])
+m4trace:m4/intdiv0.m4:9: -1- AC_DEFUN([gt_INTDIV0], [
+ AC_REQUIRE([AC_PROG_CC])dnl
+ AC_REQUIRE([AC_CANONICAL_HOST])dnl
+
+ AC_CACHE_CHECK([whether integer division by zero raises SIGFPE],
+ gt_cv_int_divbyzero_sigfpe,
+ [
+ AC_TRY_RUN([
+#include <stdlib.h>
+#include <signal.h>
+
+static void
+#ifdef __cplusplus
+sigfpe_handler (int sig)
+#else
+sigfpe_handler (sig) int sig;
+#endif
+{
+ /* Exit with code 0 if SIGFPE, with code 1 if any other signal. */
+ exit (sig != SIGFPE);
+}
+
+int x = 1;
+int y = 0;
+int z;
+int nan;
+
+int main ()
+{
+ signal (SIGFPE, sigfpe_handler);
+/* IRIX and AIX (when "xlc -qcheck" is used) yield signal SIGTRAP. */
+#if (defined (__sgi) || defined (_AIX)) && defined (SIGTRAP)
+ signal (SIGTRAP, sigfpe_handler);
+#endif
+/* Linux/SPARC yields signal SIGILL. */
+#if defined (__sparc__) && defined (__linux__)
+ signal (SIGILL, sigfpe_handler);
+#endif
+
+ z = x / y;
+ nan = y / y;
+ exit (1);
+}
+], gt_cv_int_divbyzero_sigfpe=yes, gt_cv_int_divbyzero_sigfpe=no,
+ [
+ # Guess based on the CPU.
+ case "$host_cpu" in
+ alpha* | i[34567]86 | m68k | s390*)
+ gt_cv_int_divbyzero_sigfpe="guessing yes";;
+ *)
+ gt_cv_int_divbyzero_sigfpe="guessing no";;
+ esac
+ ])
+ ])
+ case "$gt_cv_int_divbyzero_sigfpe" in
+ *yes) value=1;;
+ *) value=0;;
+ esac
+ AC_DEFINE_UNQUOTED(INTDIV0_RAISES_SIGFPE, $value,
+ [Define if integer division by zero raises signal SIGFPE.])
+])
+m4trace:m4/intl.m4:25: -1- AC_DEFUN([AM_INTL_SUBDIR], [
+ AC_REQUIRE([AC_PROG_INSTALL])dnl
+ AC_REQUIRE([AM_PROG_MKDIR_P])dnl defined by automake
+ AC_REQUIRE([AC_PROG_CC])dnl
+ AC_REQUIRE([AC_CANONICAL_HOST])dnl
+ AC_REQUIRE([gt_GLIBC2])dnl
+ AC_REQUIRE([AC_PROG_RANLIB])dnl
+ AC_REQUIRE([gl_VISIBILITY])dnl
+ AC_REQUIRE([gt_INTL_SUBDIR_CORE])dnl
+ AC_REQUIRE([AC_TYPE_LONG_LONG_INT])dnl
+ AC_REQUIRE([gt_TYPE_LONGDOUBLE])dnl
+ AC_REQUIRE([gt_TYPE_WCHAR_T])dnl
+ AC_REQUIRE([gt_TYPE_WINT_T])dnl
+ AC_REQUIRE([gl_AC_HEADER_INTTYPES_H])
+ AC_REQUIRE([gt_TYPE_INTMAX_T])
+ AC_REQUIRE([gt_PRINTF_POSIX])
+ AC_REQUIRE([gl_GLIBC21])dnl
+ AC_REQUIRE([gl_XSIZE])dnl
+ AC_REQUIRE([gt_INTL_MACOSX])dnl
+
+ AC_CHECK_TYPE([ptrdiff_t], ,
+ [AC_DEFINE([ptrdiff_t], [long],
+ [Define as the type of the result of subtracting two pointers, if the system doesn't define it.])
+ ])
+ AC_CHECK_HEADERS([stddef.h stdlib.h string.h])
+ AC_CHECK_FUNCS([asprintf fwprintf putenv setenv setlocale snprintf wcslen])
+
+ dnl Use the _snprintf function only if it is declared (because on NetBSD it
+ dnl is defined as a weak alias of snprintf; we prefer to use the latter).
+ gt_CHECK_DECL(_snprintf, [#include <stdio.h>])
+ gt_CHECK_DECL(_snwprintf, [#include <stdio.h>])
+
+ dnl Use the *_unlocked functions only if they are declared.
+ dnl (because some of them were defined without being declared in Solaris
+ dnl 2.5.1 but were removed in Solaris 2.6, whereas we want binaries built
+ dnl on Solaris 2.5.1 to run on Solaris 2.6).
+ dnl Don't use AC_CHECK_DECLS because it isn't supported in autoconf-2.13.
+ gt_CHECK_DECL(getc_unlocked, [#include <stdio.h>])
+
+ case $gt_cv_func_printf_posix in
+ *yes) HAVE_POSIX_PRINTF=1 ;;
+ *) HAVE_POSIX_PRINTF=0 ;;
+ esac
+ AC_SUBST([HAVE_POSIX_PRINTF])
+ if test "$ac_cv_func_asprintf" = yes; then
+ HAVE_ASPRINTF=1
+ else
+ HAVE_ASPRINTF=0
+ fi
+ AC_SUBST([HAVE_ASPRINTF])
+ if test "$ac_cv_func_snprintf" = yes; then
+ HAVE_SNPRINTF=1
+ else
+ HAVE_SNPRINTF=0
+ fi
+ AC_SUBST([HAVE_SNPRINTF])
+ if test "$ac_cv_func_wprintf" = yes; then
+ HAVE_WPRINTF=1
+ else
+ HAVE_WPRINTF=0
+ fi
+ AC_SUBST([HAVE_WPRINTF])
+
+ AM_LANGINFO_CODESET
+ gt_LC_MESSAGES
+
+ dnl Compilation on mingw and Cygwin needs special Makefile rules, because
+ dnl 1. when we install a shared library, we must arrange to export
+ dnl auxiliary pointer variables for every exported variable,
+ dnl 2. when we install a shared library and a static library simultaneously,
+ dnl the include file specifies __declspec(dllimport) and therefore we
+ dnl must arrange to define the auxiliary pointer variables for the
+ dnl exported variables _also_ in the static library.
+ if test "$enable_shared" = yes; then
+ case "$host_os" in
+ cygwin*) is_woe32dll=yes ;;
+ *) is_woe32dll=no ;;
+ esac
+ else
+ is_woe32dll=no
+ fi
+ WOE32DLL=$is_woe32dll
+ AC_SUBST([WOE32DLL])
+
+ dnl Rename some macros and functions used for locking.
+ AH_BOTTOM([
+#define __libc_lock_t gl_lock_t
+#define __libc_lock_define gl_lock_define
+#define __libc_lock_define_initialized gl_lock_define_initialized
+#define __libc_lock_init gl_lock_init
+#define __libc_lock_lock gl_lock_lock
+#define __libc_lock_unlock gl_lock_unlock
+#define __libc_lock_recursive_t gl_recursive_lock_t
+#define __libc_lock_define_recursive gl_recursive_lock_define
+#define __libc_lock_define_initialized_recursive gl_recursive_lock_define_initialized
+#define __libc_lock_init_recursive gl_recursive_lock_init
+#define __libc_lock_lock_recursive gl_recursive_lock_lock
+#define __libc_lock_unlock_recursive gl_recursive_lock_unlock
+#define glthread_in_use libintl_thread_in_use
+#define glthread_lock_init libintl_lock_init
+#define glthread_lock_lock libintl_lock_lock
+#define glthread_lock_unlock libintl_lock_unlock
+#define glthread_lock_destroy libintl_lock_destroy
+#define glthread_rwlock_init libintl_rwlock_init
+#define glthread_rwlock_rdlock libintl_rwlock_rdlock
+#define glthread_rwlock_wrlock libintl_rwlock_wrlock
+#define glthread_rwlock_unlock libintl_rwlock_unlock
+#define glthread_rwlock_destroy libintl_rwlock_destroy
+#define glthread_recursive_lock_init libintl_recursive_lock_init
+#define glthread_recursive_lock_lock libintl_recursive_lock_lock
+#define glthread_recursive_lock_unlock libintl_recursive_lock_unlock
+#define glthread_recursive_lock_destroy libintl_recursive_lock_destroy
+#define glthread_once libintl_once
+#define glthread_once_call libintl_once_call
+#define glthread_once_singlethreaded libintl_once_singlethreaded
+])
+])
+m4trace:m4/intl.m4:162: -1- AC_DEFUN([gt_INTL_SUBDIR_CORE], [
+ AC_REQUIRE([AC_C_INLINE])dnl
+ AC_REQUIRE([AC_TYPE_SIZE_T])dnl
+ AC_REQUIRE([gl_AC_HEADER_STDINT_H])
+ AC_REQUIRE([AC_FUNC_ALLOCA])dnl
+ AC_REQUIRE([AC_FUNC_MMAP])dnl
+ AC_REQUIRE([gt_INTDIV0])dnl
+ AC_REQUIRE([gl_AC_TYPE_UINTMAX_T])dnl
+ AC_REQUIRE([gt_INTTYPES_PRI])dnl
+ AC_REQUIRE([gl_LOCK])dnl
+
+ AC_TRY_LINK(
+ [int foo (int a) { a = __builtin_expect (a, 10); return a == 10 ? 0 : 1; }],
+ [],
+ [AC_DEFINE([HAVE_BUILTIN_EXPECT], 1,
+ [Define to 1 if the compiler understands __builtin_expect.])])
+
+ AC_CHECK_HEADERS([argz.h inttypes.h limits.h unistd.h sys/param.h])
+ AC_CHECK_FUNCS([getcwd getegid geteuid getgid getuid mempcpy munmap \
+ stpcpy strcasecmp strdup strtoul tsearch argz_count argz_stringify \
+ argz_next __fsetlocking])
+
+ dnl Use the *_unlocked functions only if they are declared.
+ dnl (because some of them were defined without being declared in Solaris
+ dnl 2.5.1 but were removed in Solaris 2.6, whereas we want binaries built
+ dnl on Solaris 2.5.1 to run on Solaris 2.6).
+ dnl Don't use AC_CHECK_DECLS because it isn't supported in autoconf-2.13.
+ gt_CHECK_DECL(feof_unlocked, [#include <stdio.h>])
+ gt_CHECK_DECL(fgets_unlocked, [#include <stdio.h>])
+
+ AM_ICONV
+
+ dnl glibc >= 2.4 has a NL_LOCALE_NAME macro when _GNU_SOURCE is defined,
+ dnl and a _NL_LOCALE_NAME macro always.
+ AC_CACHE_CHECK([for NL_LOCALE_NAME macro], gt_cv_nl_locale_name,
+ [AC_TRY_LINK([#include <langinfo.h>
+#include <locale.h>],
+ [char* cs = nl_langinfo(_NL_LOCALE_NAME(LC_MESSAGES));],
+ gt_cv_nl_locale_name=yes,
+ gt_cv_nl_locale_name=no)
+ ])
+ if test $gt_cv_nl_locale_name = yes; then
+ AC_DEFINE(HAVE_NL_LOCALE_NAME, 1,
+ [Define if you have <langinfo.h> and it defines the NL_LOCALE_NAME macro if _GNU_SOURCE is defined.])
+ fi
+
+ dnl intl/plural.c is generated from intl/plural.y. It requires bison,
+ dnl because plural.y uses bison specific features. It requires at least
+ dnl bison-1.26 because earlier versions generate a plural.c that doesn't
+ dnl compile.
+ dnl bison is only needed for the maintainer (who touches plural.y). But in
+ dnl order to avoid separate Makefiles or --enable-maintainer-mode, we put
+ dnl the rule in general Makefile. Now, some people carelessly touch the
+ dnl files or have a broken "make" program, hence the plural.c rule will
+ dnl sometimes fire. To avoid an error, defines BISON to ":" if it is not
+ dnl present or too old.
+ AC_CHECK_PROGS([INTLBISON], [bison])
+ if test -z "$INTLBISON"; then
+ ac_verc_fail=yes
+ else
+ dnl Found it, now check the version.
+ AC_MSG_CHECKING([version of bison])
+changequote(<<,>>)dnl
+ ac_prog_version=`$INTLBISON --version 2>&1 | sed -n 's/^.*GNU Bison.* \([0-9]*\.[0-9.]*\).*$/\1/p'`
+ case $ac_prog_version in
+ '') ac_prog_version="v. ?.??, bad"; ac_verc_fail=yes;;
+ 1.2[6-9]* | 1.[3-9][0-9]* | [2-9].*)
+changequote([,])dnl
+ ac_prog_version="$ac_prog_version, ok"; ac_verc_fail=no;;
+ *) ac_prog_version="$ac_prog_version, bad"; ac_verc_fail=yes;;
+ esac
+ AC_MSG_RESULT([$ac_prog_version])
+ fi
+ if test $ac_verc_fail = yes; then
+ INTLBISON=:
+ fi
+])
+m4trace:m4/intl.m4:244: -1- AC_DEFUN([gt_CHECK_DECL], [
+ AC_CACHE_CHECK([whether $1 is declared], ac_cv_have_decl_$1,
+ [AC_TRY_COMPILE([$2], [
+#ifndef $1
+ char *p = (char *) $1;
+#endif
+], ac_cv_have_decl_$1=yes, ac_cv_have_decl_$1=no)])
+ if test $ac_cv_have_decl_$1 = yes; then
+ gt_value=1
+ else
+ gt_value=0
+ fi
+ AC_DEFINE_UNQUOTED([HAVE_DECL_]translit($1, [a-z], [A-Z]), [$gt_value],
+ [Define to 1 if you have the declaration of `$1', and to 0 if you don't.])
+])
+m4trace:m4/intmax.m4:11: -1- AC_DEFUN([gt_TYPE_INTMAX_T], [
+ AC_REQUIRE([gl_AC_HEADER_INTTYPES_H])
+ AC_REQUIRE([gl_AC_HEADER_STDINT_H])
+ AC_CACHE_CHECK(for intmax_t, gt_cv_c_intmax_t,
+ [AC_TRY_COMPILE([
+#include <stddef.h>
+#include <stdlib.h>
+#if HAVE_STDINT_H_WITH_UINTMAX
+#include <stdint.h>
+#endif
+#if HAVE_INTTYPES_H_WITH_UINTMAX
+#include <inttypes.h>
+#endif
+], [intmax_t x = -1;
+ return !x;],
+ gt_cv_c_intmax_t=yes,
+ gt_cv_c_intmax_t=no)])
+ if test $gt_cv_c_intmax_t = yes; then
+ AC_DEFINE(HAVE_INTMAX_T, 1,
+ [Define if you have the 'intmax_t' type in <stdint.h> or <inttypes.h>.])
+ fi
+])
+m4trace:m4/inttypes-pri.m4:14: -1- AC_DEFUN([gt_INTTYPES_PRI], [
+ AC_CHECK_HEADERS([inttypes.h])
+ if test $ac_cv_header_inttypes_h = yes; then
+ AC_CACHE_CHECK([whether the inttypes.h PRIxNN macros are broken],
+ gt_cv_inttypes_pri_broken,
+ [
+ AC_TRY_COMPILE([#include <inttypes.h>
+#ifdef PRId32
+char *p = PRId32;
+#endif
+], [], gt_cv_inttypes_pri_broken=no, gt_cv_inttypes_pri_broken=yes)
+ ])
+ fi
+ if test "$gt_cv_inttypes_pri_broken" = yes; then
+ AC_DEFINE_UNQUOTED(PRI_MACROS_BROKEN, 1,
+ [Define if <inttypes.h> exists and defines unusable PRI* macros.])
+ PRI_MACROS_BROKEN=1
+ else
+ PRI_MACROS_BROKEN=0
+ fi
+ AC_SUBST([PRI_MACROS_BROKEN])
+])
+m4trace:m4/inttypes_h.m4:12: -1- AC_DEFUN([gl_AC_HEADER_INTTYPES_H], [
+ AC_CACHE_CHECK([for inttypes.h], gl_cv_header_inttypes_h,
+ [AC_TRY_COMPILE(
+ [#include <sys/types.h>
+#include <inttypes.h>],
+ [uintmax_t i = (uintmax_t) -1; return !i;],
+ gl_cv_header_inttypes_h=yes,
+ gl_cv_header_inttypes_h=no)])
+ if test $gl_cv_header_inttypes_h = yes; then
+ AC_DEFINE_UNQUOTED(HAVE_INTTYPES_H_WITH_UINTMAX, 1,
+ [Define if <inttypes.h> exists, doesn't clash with <sys/types.h>,
+ and declares uintmax_t. ])
+ fi
+])
+m4trace:m4/lcmessage.m4:21: -1- AC_DEFUN([gt_LC_MESSAGES], [
+ AC_CACHE_CHECK([for LC_MESSAGES], gt_cv_val_LC_MESSAGES,
+ [AC_TRY_LINK([#include <locale.h>], [return LC_MESSAGES],
+ gt_cv_val_LC_MESSAGES=yes, gt_cv_val_LC_MESSAGES=no)])
+ if test $gt_cv_val_LC_MESSAGES = yes; then
+ AC_DEFINE(HAVE_LC_MESSAGES, 1,
+ [Define if your <locale.h> file defines LC_MESSAGES.])
+ fi
+])
+m4trace:m4/lib-ld.m4:12: -1- AC_DEFUN([AC_LIB_PROG_LD_GNU], [AC_CACHE_CHECK([if the linker ($LD) is GNU ld], acl_cv_prog_gnu_ld,
+[# I'd rather use --version here, but apparently some GNU ld's only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+ acl_cv_prog_gnu_ld=yes ;;
+*)
+ acl_cv_prog_gnu_ld=no ;;
+esac])
+with_gnu_ld=$acl_cv_prog_gnu_ld
+])
+m4trace:m4/lib-ld.m4:25: -1- AC_DEFUN([AC_LIB_PROG_LD], [AC_ARG_WITH(gnu-ld,
+[ --with-gnu-ld assume the C compiler uses GNU ld [default=no]],
+test "$withval" = no || with_gnu_ld=yes, with_gnu_ld=no)
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_CANONICAL_HOST])dnl
+# Prepare PATH_SEPARATOR.
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+ac_prog=ld
+if test "$GCC" = yes; then
+ # Check if gcc -print-prog-name=ld gives a path.
+ AC_MSG_CHECKING([for ld used by GCC])
+ case $host in
+ *-*-mingw*)
+ # gcc leaves a trailing carriage return which upsets mingw
+ ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+ *)
+ ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+ esac
+ case $ac_prog in
+ # Accept absolute paths.
+ [[\\/]* | [A-Za-z]:[\\/]*)]
+ [re_direlt='/[^/][^/]*/\.\./']
+ # Canonicalize the path of ld
+ ac_prog=`echo $ac_prog| sed 's%\\\\%/%g'`
+ while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do
+ ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"`
+ done
+ test -z "$LD" && LD="$ac_prog"
+ ;;
+ "")
+ # If it fails, then pretend we aren't using GCC.
+ ac_prog=ld
+ ;;
+ *)
+ # If it is relative, then search for the first ld in PATH.
+ with_gnu_ld=unknown
+ ;;
+ esac
+elif test "$with_gnu_ld" = yes; then
+ AC_MSG_CHECKING([for GNU ld])
+else
+ AC_MSG_CHECKING([for non-GNU ld])
+fi
+AC_CACHE_VAL(acl_cv_path_LD,
+[if test -z "$LD"; then
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR-:}"
+ for ac_dir in $PATH; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+ acl_cv_path_LD="$ac_dir/$ac_prog"
+ # Check to see if the program is GNU ld. I'd rather use --version,
+ # but apparently some GNU ld's only accept -v.
+ # Break only if it was the GNU/non-GNU ld that we prefer.
+ case `"$acl_cv_path_LD" -v 2>&1 < /dev/null` in
+ *GNU* | *'with BFD'*)
+ test "$with_gnu_ld" != no && break ;;
+ *)
+ test "$with_gnu_ld" != yes && break ;;
+ esac
+ fi
+ done
+ IFS="$ac_save_ifs"
+else
+ acl_cv_path_LD="$LD" # Let the user override the test with a path.
+fi])
+LD="$acl_cv_path_LD"
+if test -n "$LD"; then
+ AC_MSG_RESULT($LD)
+else
+ AC_MSG_RESULT(no)
+fi
+test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH])
+AC_LIB_PROG_LD_GNU
+])
+m4trace:m4/lib-link.m4:15: -1- AC_DEFUN([AC_LIB_LINKFLAGS], [
+ AC_REQUIRE([AC_LIB_PREPARE_PREFIX])
+ AC_REQUIRE([AC_LIB_RPATH])
+ define([Name],[translit([$1],[./-], [___])])
+ define([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-],
+ [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])])
+ AC_CACHE_CHECK([how to link with lib[]$1], [ac_cv_lib[]Name[]_libs], [
+ AC_LIB_LINKFLAGS_BODY([$1], [$2])
+ ac_cv_lib[]Name[]_libs="$LIB[]NAME"
+ ac_cv_lib[]Name[]_ltlibs="$LTLIB[]NAME"
+ ac_cv_lib[]Name[]_cppflags="$INC[]NAME"
+ ])
+ LIB[]NAME="$ac_cv_lib[]Name[]_libs"
+ LTLIB[]NAME="$ac_cv_lib[]Name[]_ltlibs"
+ INC[]NAME="$ac_cv_lib[]Name[]_cppflags"
+ AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME)
+ AC_SUBST([LIB]NAME)
+ AC_SUBST([LTLIB]NAME)
+ dnl Also set HAVE_LIB[]NAME so that AC_LIB_HAVE_LINKFLAGS can reuse the
+ dnl results of this search when this library appears as a dependency.
+ HAVE_LIB[]NAME=yes
+ undefine([Name])
+ undefine([NAME])
+])
+m4trace:m4/lib-link.m4:49: -1- AC_DEFUN([AC_LIB_HAVE_LINKFLAGS], [
+ AC_REQUIRE([AC_LIB_PREPARE_PREFIX])
+ AC_REQUIRE([AC_LIB_RPATH])
+ define([Name],[translit([$1],[./-], [___])])
+ define([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-],
+ [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])])
+
+ dnl Search for lib[]Name and define LIB[]NAME, LTLIB[]NAME and INC[]NAME
+ dnl accordingly.
+ AC_LIB_LINKFLAGS_BODY([$1], [$2])
+
+ dnl Add $INC[]NAME to CPPFLAGS before performing the following checks,
+ dnl because if the user has installed lib[]Name and not disabled its use
+ dnl via --without-lib[]Name-prefix, he wants to use it.
+ ac_save_CPPFLAGS="$CPPFLAGS"
+ AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME)
+
+ AC_CACHE_CHECK([for lib[]$1], [ac_cv_lib[]Name], [
+ ac_save_LIBS="$LIBS"
+ LIBS="$LIBS $LIB[]NAME"
+ AC_TRY_LINK([$3], [$4], [ac_cv_lib[]Name=yes], [ac_cv_lib[]Name=no])
+ LIBS="$ac_save_LIBS"
+ ])
+ if test "$ac_cv_lib[]Name" = yes; then
+ HAVE_LIB[]NAME=yes
+ AC_DEFINE([HAVE_LIB]NAME, 1, [Define if you have the $1 library.])
+ AC_MSG_CHECKING([how to link with lib[]$1])
+ AC_MSG_RESULT([$LIB[]NAME])
+ else
+ HAVE_LIB[]NAME=no
+ dnl If $LIB[]NAME didn't lead to a usable library, we don't need
+ dnl $INC[]NAME either.
+ CPPFLAGS="$ac_save_CPPFLAGS"
+ LIB[]NAME=
+ LTLIB[]NAME=
+ fi
+ AC_SUBST([HAVE_LIB]NAME)
+ AC_SUBST([LIB]NAME)
+ AC_SUBST([LTLIB]NAME)
+ undefine([Name])
+ undefine([NAME])
+])
+m4trace:m4/lib-link.m4:96: -1- AC_DEFUN([AC_LIB_RPATH], [
+ dnl Tell automake >= 1.10 to complain if config.rpath is missing.
+ m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([config.rpath])])
+ AC_REQUIRE([AC_PROG_CC]) dnl we use $CC, $GCC, $LDFLAGS
+ AC_REQUIRE([AC_LIB_PROG_LD]) dnl we use $LD, $with_gnu_ld
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl we use $host
+ AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT]) dnl we use $ac_aux_dir
+ AC_CACHE_CHECK([for shared library run path origin], acl_cv_rpath, [
+ CC="$CC" GCC="$GCC" LDFLAGS="$LDFLAGS" LD="$LD" with_gnu_ld="$with_gnu_ld" \
+ ${CONFIG_SHELL-/bin/sh} "$ac_aux_dir/config.rpath" "$host" > conftest.sh
+ . ./conftest.sh
+ rm -f ./conftest.sh
+ acl_cv_rpath=done
+ ])
+ wl="$acl_cv_wl"
+ libext="$acl_cv_libext"
+ shlibext="$acl_cv_shlibext"
+ hardcode_libdir_flag_spec="$acl_cv_hardcode_libdir_flag_spec"
+ hardcode_libdir_separator="$acl_cv_hardcode_libdir_separator"
+ hardcode_direct="$acl_cv_hardcode_direct"
+ hardcode_minus_L="$acl_cv_hardcode_minus_L"
+ dnl Determine whether the user wants rpath handling at all.
+ AC_ARG_ENABLE(rpath,
+ [ --disable-rpath do not hardcode runtime library paths],
+ :, enable_rpath=yes)
+])
+m4trace:m4/lib-link.m4:127: -1- AC_DEFUN([AC_LIB_LINKFLAGS_BODY], [
+ AC_REQUIRE([AC_LIB_PREPARE_MULTILIB])
+ define([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-],
+ [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])])
+ dnl By default, look in $includedir and $libdir.
+ use_additional=yes
+ AC_LIB_WITH_FINAL_PREFIX([
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+ ])
+ AC_LIB_ARG_WITH([lib$1-prefix],
+[ --with-lib$1-prefix[=DIR] search for lib$1 in DIR/include and DIR/lib
+ --without-lib$1-prefix don't search for lib$1 in includedir and libdir],
+[
+ if test "X$withval" = "Xno"; then
+ use_additional=no
+ else
+ if test "X$withval" = "X"; then
+ AC_LIB_WITH_FINAL_PREFIX([
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+ ])
+ else
+ additional_includedir="$withval/include"
+ additional_libdir="$withval/$acl_libdirstem"
+ fi
+ fi
+])
+ dnl Search the library and its dependencies in $additional_libdir and
+ dnl $LDFLAGS. Using breadth-first-seach.
+ LIB[]NAME=
+ LTLIB[]NAME=
+ INC[]NAME=
+ rpathdirs=
+ ltrpathdirs=
+ names_already_handled=
+ names_next_round='$1 $2'
+ while test -n "$names_next_round"; do
+ names_this_round="$names_next_round"
+ names_next_round=
+ for name in $names_this_round; do
+ already_handled=
+ for n in $names_already_handled; do
+ if test "$n" = "$name"; then
+ already_handled=yes
+ break
+ fi
+ done
+ if test -z "$already_handled"; then
+ names_already_handled="$names_already_handled $name"
+ dnl See if it was already located by an earlier AC_LIB_LINKFLAGS
+ dnl or AC_LIB_HAVE_LINKFLAGS call.
+ uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'`
+ eval value=\"\$HAVE_LIB$uppername\"
+ if test -n "$value"; then
+ if test "$value" = yes; then
+ eval value=\"\$LIB$uppername\"
+ test -z "$value" || LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$value"
+ eval value=\"\$LTLIB$uppername\"
+ test -z "$value" || LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$value"
+ else
+ dnl An earlier call to AC_LIB_HAVE_LINKFLAGS has determined
+ dnl that this library doesn't exist. So just drop it.
+ :
+ fi
+ else
+ dnl Search the library lib$name in $additional_libdir and $LDFLAGS
+ dnl and the already constructed $LIBNAME/$LTLIBNAME.
+ found_dir=
+ found_la=
+ found_so=
+ found_a=
+ if test $use_additional = yes; then
+ if test -n "$shlibext" \
+ && { test -f "$additional_libdir/lib$name.$shlibext" \
+ || { test "$shlibext" = dll \
+ && test -f "$additional_libdir/lib$name.dll.a"; }; }; then
+ found_dir="$additional_libdir"
+ if test -f "$additional_libdir/lib$name.$shlibext"; then
+ found_so="$additional_libdir/lib$name.$shlibext"
+ else
+ found_so="$additional_libdir/lib$name.dll.a"
+ fi
+ if test -f "$additional_libdir/lib$name.la"; then
+ found_la="$additional_libdir/lib$name.la"
+ fi
+ else
+ if test -f "$additional_libdir/lib$name.$libext"; then
+ found_dir="$additional_libdir"
+ found_a="$additional_libdir/lib$name.$libext"
+ if test -f "$additional_libdir/lib$name.la"; then
+ found_la="$additional_libdir/lib$name.la"
+ fi
+ fi
+ fi
+ fi
+ if test "X$found_dir" = "X"; then
+ for x in $LDFLAGS $LTLIB[]NAME; do
+ AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+ case "$x" in
+ -L*)
+ dir=`echo "X$x" | sed -e 's/^X-L//'`
+ if test -n "$shlibext" \
+ && { test -f "$dir/lib$name.$shlibext" \
+ || { test "$shlibext" = dll \
+ && test -f "$dir/lib$name.dll.a"; }; }; then
+ found_dir="$dir"
+ if test -f "$dir/lib$name.$shlibext"; then
+ found_so="$dir/lib$name.$shlibext"
+ else
+ found_so="$dir/lib$name.dll.a"
+ fi
+ if test -f "$dir/lib$name.la"; then
+ found_la="$dir/lib$name.la"
+ fi
+ else
+ if test -f "$dir/lib$name.$libext"; then
+ found_dir="$dir"
+ found_a="$dir/lib$name.$libext"
+ if test -f "$dir/lib$name.la"; then
+ found_la="$dir/lib$name.la"
+ fi
+ fi
+ fi
+ ;;
+ esac
+ if test "X$found_dir" != "X"; then
+ break
+ fi
+ done
+ fi
+ if test "X$found_dir" != "X"; then
+ dnl Found the library.
+ LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$found_dir -l$name"
+ if test "X$found_so" != "X"; then
+ dnl Linking with a shared library. We attempt to hardcode its
+ dnl directory into the executable's runpath, unless it's the
+ dnl standard /usr/lib.
+ if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/$acl_libdirstem"; then
+ dnl No hardcoding is needed.
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so"
+ else
+ dnl Use an explicit option to hardcode DIR into the resulting
+ dnl binary.
+ dnl Potentially add DIR to ltrpathdirs.
+ dnl The ltrpathdirs will be appended to $LTLIBNAME at the end.
+ haveit=
+ for x in $ltrpathdirs; do
+ if test "X$x" = "X$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ ltrpathdirs="$ltrpathdirs $found_dir"
+ fi
+ dnl The hardcoding into $LIBNAME is system dependent.
+ if test "$hardcode_direct" = yes; then
+ dnl Using DIR/libNAME.so during linking hardcodes DIR into the
+ dnl resulting binary.
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so"
+ else
+ if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then
+ dnl Use an explicit option to hardcode DIR into the resulting
+ dnl binary.
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so"
+ dnl Potentially add DIR to rpathdirs.
+ dnl The rpathdirs will be appended to $LIBNAME at the end.
+ haveit=
+ for x in $rpathdirs; do
+ if test "X$x" = "X$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ rpathdirs="$rpathdirs $found_dir"
+ fi
+ else
+ dnl Rely on "-L$found_dir".
+ dnl But don't add it if it's already contained in the LDFLAGS
+ dnl or the already constructed $LIBNAME
+ haveit=
+ for x in $LDFLAGS $LIB[]NAME; do
+ AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+ if test "X$x" = "X-L$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir"
+ fi
+ if test "$hardcode_minus_L" != no; then
+ dnl FIXME: Not sure whether we should use
+ dnl "-L$found_dir -l$name" or "-L$found_dir $found_so"
+ dnl here.
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so"
+ else
+ dnl We cannot use $hardcode_runpath_var and LD_RUN_PATH
+ dnl here, because this doesn't fit in flags passed to the
+ dnl compiler. So give up. No hardcoding. This affects only
+ dnl very old systems.
+ dnl FIXME: Not sure whether we should use
+ dnl "-L$found_dir -l$name" or "-L$found_dir $found_so"
+ dnl here.
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name"
+ fi
+ fi
+ fi
+ fi
+ else
+ if test "X$found_a" != "X"; then
+ dnl Linking with a static library.
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_a"
+ else
+ dnl We shouldn't come here, but anyway it's good to have a
+ dnl fallback.
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir -l$name"
+ fi
+ fi
+ dnl Assume the include files are nearby.
+ additional_includedir=
+ case "$found_dir" in
+ */$acl_libdirstem | */$acl_libdirstem/)
+ basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem/"'*$,,'`
+ additional_includedir="$basedir/include"
+ ;;
+ esac
+ if test "X$additional_includedir" != "X"; then
+ dnl Potentially add $additional_includedir to $INCNAME.
+ dnl But don't add it
+ dnl 1. if it's the standard /usr/include,
+ dnl 2. if it's /usr/local/include and we are using GCC on Linux,
+ dnl 3. if it's already present in $CPPFLAGS or the already
+ dnl constructed $INCNAME,
+ dnl 4. if it doesn't exist as a directory.
+ if test "X$additional_includedir" != "X/usr/include"; then
+ haveit=
+ if test "X$additional_includedir" = "X/usr/local/include"; then
+ if test -n "$GCC"; then
+ case $host_os in
+ linux* | gnu* | k*bsd*-gnu) haveit=yes;;
+ esac
+ fi
+ fi
+ if test -z "$haveit"; then
+ for x in $CPPFLAGS $INC[]NAME; do
+ AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+ if test "X$x" = "X-I$additional_includedir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_includedir"; then
+ dnl Really add $additional_includedir to $INCNAME.
+ INC[]NAME="${INC[]NAME}${INC[]NAME:+ }-I$additional_includedir"
+ fi
+ fi
+ fi
+ fi
+ fi
+ dnl Look for dependencies.
+ if test -n "$found_la"; then
+ dnl Read the .la file. It defines the variables
+ dnl dlname, library_names, old_library, dependency_libs, current,
+ dnl age, revision, installed, dlopen, dlpreopen, libdir.
+ save_libdir="$libdir"
+ case "$found_la" in
+ */* | *\\*) . "$found_la" ;;
+ *) . "./$found_la" ;;
+ esac
+ libdir="$save_libdir"
+ dnl We use only dependency_libs.
+ for dep in $dependency_libs; do
+ case "$dep" in
+ -L*)
+ additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'`
+ dnl Potentially add $additional_libdir to $LIBNAME and $LTLIBNAME.
+ dnl But don't add it
+ dnl 1. if it's the standard /usr/lib,
+ dnl 2. if it's /usr/local/lib and we are using GCC on Linux,
+ dnl 3. if it's already present in $LDFLAGS or the already
+ dnl constructed $LIBNAME,
+ dnl 4. if it doesn't exist as a directory.
+ if test "X$additional_libdir" != "X/usr/$acl_libdirstem"; then
+ haveit=
+ if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem"; then
+ if test -n "$GCC"; then
+ case $host_os in
+ linux* | gnu* | k*bsd*-gnu) haveit=yes;;
+ esac
+ fi
+ fi
+ if test -z "$haveit"; then
+ haveit=
+ for x in $LDFLAGS $LIB[]NAME; do
+ AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+ if test "X$x" = "X-L$additional_libdir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_libdir"; then
+ dnl Really add $additional_libdir to $LIBNAME.
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$additional_libdir"
+ fi
+ fi
+ haveit=
+ for x in $LDFLAGS $LTLIB[]NAME; do
+ AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+ if test "X$x" = "X-L$additional_libdir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_libdir"; then
+ dnl Really add $additional_libdir to $LTLIBNAME.
+ LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$additional_libdir"
+ fi
+ fi
+ fi
+ fi
+ ;;
+ -R*)
+ dir=`echo "X$dep" | sed -e 's/^X-R//'`
+ if test "$enable_rpath" != no; then
+ dnl Potentially add DIR to rpathdirs.
+ dnl The rpathdirs will be appended to $LIBNAME at the end.
+ haveit=
+ for x in $rpathdirs; do
+ if test "X$x" = "X$dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ rpathdirs="$rpathdirs $dir"
+ fi
+ dnl Potentially add DIR to ltrpathdirs.
+ dnl The ltrpathdirs will be appended to $LTLIBNAME at the end.
+ haveit=
+ for x in $ltrpathdirs; do
+ if test "X$x" = "X$dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ ltrpathdirs="$ltrpathdirs $dir"
+ fi
+ fi
+ ;;
+ -l*)
+ dnl Handle this in the next round.
+ names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'`
+ ;;
+ *.la)
+ dnl Handle this in the next round. Throw away the .la's
+ dnl directory; it is already contained in a preceding -L
+ dnl option.
+ names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'`
+ ;;
+ *)
+ dnl Most likely an immediate library name.
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$dep"
+ LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$dep"
+ ;;
+ esac
+ done
+ fi
+ else
+ dnl Didn't find the library; assume it is in the system directories
+ dnl known to the linker and runtime loader. (All the system
+ dnl directories known to the linker should also be known to the
+ dnl runtime loader, otherwise the system is severely misconfigured.)
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name"
+ LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-l$name"
+ fi
+ fi
+ fi
+ done
+ done
+ if test "X$rpathdirs" != "X"; then
+ if test -n "$hardcode_libdir_separator"; then
+ dnl Weird platform: only the last -rpath option counts, the user must
+ dnl pass all path elements in one option. We can arrange that for a
+ dnl single library, but not when more than one $LIBNAMEs are used.
+ alldirs=
+ for found_dir in $rpathdirs; do
+ alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$found_dir"
+ done
+ dnl Note: hardcode_libdir_flag_spec uses $libdir and $wl.
+ acl_save_libdir="$libdir"
+ libdir="$alldirs"
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ libdir="$acl_save_libdir"
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag"
+ else
+ dnl The -rpath options are cumulative.
+ for found_dir in $rpathdirs; do
+ acl_save_libdir="$libdir"
+ libdir="$found_dir"
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ libdir="$acl_save_libdir"
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag"
+ done
+ fi
+ fi
+ if test "X$ltrpathdirs" != "X"; then
+ dnl When using libtool, the option that works for both libraries and
+ dnl executables is -R. The -R options are cumulative.
+ for found_dir in $ltrpathdirs; do
+ LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-R$found_dir"
+ done
+ fi
+])
+m4trace:m4/lib-link.m4:553: -1- AC_DEFUN([AC_LIB_APPENDTOVAR], [
+ for element in [$2]; do
+ haveit=
+ for x in $[$1]; do
+ AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+ if test "X$x" = "X$element"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ [$1]="${[$1]}${[$1]:+ }$element"
+ fi
+ done
+])
+m4trace:m4/lib-link.m4:577: -1- AC_DEFUN([AC_LIB_LINKFLAGS_FROM_LIBS], [
+ AC_REQUIRE([AC_LIB_RPATH])
+ AC_REQUIRE([AC_LIB_PREPARE_MULTILIB])
+ $1=
+ if test "$enable_rpath" != no; then
+ if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then
+ dnl Use an explicit option to hardcode directories into the resulting
+ dnl binary.
+ rpathdirs=
+ next=
+ for opt in $2; do
+ if test -n "$next"; then
+ dir="$next"
+ dnl No need to hardcode the standard /usr/lib.
+ if test "X$dir" != "X/usr/$acl_libdirstem"; then
+ rpathdirs="$rpathdirs $dir"
+ fi
+ next=
+ else
+ case $opt in
+ -L) next=yes ;;
+ -L*) dir=`echo "X$opt" | sed -e 's,^X-L,,'`
+ dnl No need to hardcode the standard /usr/lib.
+ if test "X$dir" != "X/usr/$acl_libdirstem"; then
+ rpathdirs="$rpathdirs $dir"
+ fi
+ next= ;;
+ *) next= ;;
+ esac
+ fi
+ done
+ if test "X$rpathdirs" != "X"; then
+ if test -n ""$3""; then
+ dnl libtool is used for linking. Use -R options.
+ for dir in $rpathdirs; do
+ $1="${$1}${$1:+ }-R$dir"
+ done
+ else
+ dnl The linker is used for linking directly.
+ if test -n "$hardcode_libdir_separator"; then
+ dnl Weird platform: only the last -rpath option counts, the user
+ dnl must pass all path elements in one option.
+ alldirs=
+ for dir in $rpathdirs; do
+ alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$dir"
+ done
+ acl_save_libdir="$libdir"
+ libdir="$alldirs"
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ libdir="$acl_save_libdir"
+ $1="$flag"
+ else
+ dnl The -rpath options are cumulative.
+ for dir in $rpathdirs; do
+ acl_save_libdir="$libdir"
+ libdir="$dir"
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ libdir="$acl_save_libdir"
+ $1="${$1}${$1:+ }$flag"
+ done
+ fi
+ fi
+ fi
+ fi
+ fi
+ AC_SUBST([$1])
+])
+m4trace:m4/lib-prefix.m4:12: -1- AC_DEFUN([AC_LIB_ARG_WITH], [AC_ARG_WITH([$1],[[$2]],[$3],[$4])])
+m4trace:m4/lib-prefix.m4:22: -1- AC_DEFUN([AC_LIB_PREFIX], [
+ AC_BEFORE([$0], [AC_LIB_LINKFLAGS])
+ AC_REQUIRE([AC_PROG_CC])
+ AC_REQUIRE([AC_CANONICAL_HOST])
+ AC_REQUIRE([AC_LIB_PREPARE_MULTILIB])
+ AC_REQUIRE([AC_LIB_PREPARE_PREFIX])
+ dnl By default, look in $includedir and $libdir.
+ use_additional=yes
+ AC_LIB_WITH_FINAL_PREFIX([
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+ ])
+ AC_LIB_ARG_WITH([lib-prefix],
+[ --with-lib-prefix[=DIR] search for libraries in DIR/include and DIR/lib
+ --without-lib-prefix don't search for libraries in includedir and libdir],
+[
+ if test "X$withval" = "Xno"; then
+ use_additional=no
+ else
+ if test "X$withval" = "X"; then
+ AC_LIB_WITH_FINAL_PREFIX([
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+ ])
+ else
+ additional_includedir="$withval/include"
+ additional_libdir="$withval/$acl_libdirstem"
+ fi
+ fi
+])
+ if test $use_additional = yes; then
+ dnl Potentially add $additional_includedir to $CPPFLAGS.
+ dnl But don't add it
+ dnl 1. if it's the standard /usr/include,
+ dnl 2. if it's already present in $CPPFLAGS,
+ dnl 3. if it's /usr/local/include and we are using GCC on Linux,
+ dnl 4. if it doesn't exist as a directory.
+ if test "X$additional_includedir" != "X/usr/include"; then
+ haveit=
+ for x in $CPPFLAGS; do
+ AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+ if test "X$x" = "X-I$additional_includedir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test "X$additional_includedir" = "X/usr/local/include"; then
+ if test -n "$GCC"; then
+ case $host_os in
+ linux* | gnu* | k*bsd*-gnu) haveit=yes;;
+ esac
+ fi
+ fi
+ if test -z "$haveit"; then
+ if test -d "$additional_includedir"; then
+ dnl Really add $additional_includedir to $CPPFLAGS.
+ CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }-I$additional_includedir"
+ fi
+ fi
+ fi
+ fi
+ dnl Potentially add $additional_libdir to $LDFLAGS.
+ dnl But don't add it
+ dnl 1. if it's the standard /usr/lib,
+ dnl 2. if it's already present in $LDFLAGS,
+ dnl 3. if it's /usr/local/lib and we are using GCC on Linux,
+ dnl 4. if it doesn't exist as a directory.
+ if test "X$additional_libdir" != "X/usr/$acl_libdirstem"; then
+ haveit=
+ for x in $LDFLAGS; do
+ AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+ if test "X$x" = "X-L$additional_libdir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem"; then
+ if test -n "$GCC"; then
+ case $host_os in
+ linux*) haveit=yes;;
+ esac
+ fi
+ fi
+ if test -z "$haveit"; then
+ if test -d "$additional_libdir"; then
+ dnl Really add $additional_libdir to $LDFLAGS.
+ LDFLAGS="${LDFLAGS}${LDFLAGS:+ }-L$additional_libdir"
+ fi
+ fi
+ fi
+ fi
+ fi
+])
+m4trace:m4/lib-prefix.m4:122: -1- AC_DEFUN([AC_LIB_PREPARE_PREFIX], [
+ dnl Unfortunately, prefix and exec_prefix get only finally determined
+ dnl at the end of configure.
+ if test "X$prefix" = "XNONE"; then
+ acl_final_prefix="$ac_default_prefix"
+ else
+ acl_final_prefix="$prefix"
+ fi
+ if test "X$exec_prefix" = "XNONE"; then
+ acl_final_exec_prefix='${prefix}'
+ else
+ acl_final_exec_prefix="$exec_prefix"
+ fi
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ eval acl_final_exec_prefix=\"$acl_final_exec_prefix\"
+ prefix="$acl_save_prefix"
+])
+m4trace:m4/lib-prefix.m4:145: -1- AC_DEFUN([AC_LIB_WITH_FINAL_PREFIX], [
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ $1
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+])
+m4trace:m4/lib-prefix.m4:158: -1- AC_DEFUN([AC_LIB_PREPARE_MULTILIB], [
+ dnl There is no formal standard regarding lib and lib64. The current
+ dnl practice is that on a system supporting 32-bit and 64-bit instruction
+ dnl sets or ABIs, 64-bit libraries go under $prefix/lib64 and 32-bit
+ dnl libraries go under $prefix/lib. We determine the compiler's default
+ dnl mode by looking at the compiler's library search path. If at least
+ dnl of its elements ends in /lib64 or points to a directory whose absolute
+ dnl pathname ends in /lib64, we assume a 64-bit ABI. Otherwise we use the
+ dnl default, namely "lib".
+ acl_libdirstem=lib
+ searchpath=`(LC_ALL=C $CC -print-search-dirs) 2>/dev/null | sed -n -e 's,^libraries: ,,p' | sed -e 's,^=,,'`
+ if test -n "$searchpath"; then
+ acl_save_IFS="${IFS= }"; IFS=":"
+ for searchdir in $searchpath; do
+ if test -d "$searchdir"; then
+ case "$searchdir" in
+ */lib64/ | */lib64 ) acl_libdirstem=lib64 ;;
+ *) searchdir=`cd "$searchdir" && pwd`
+ case "$searchdir" in
+ */lib64 ) acl_libdirstem=lib64 ;;
+ esac ;;
+ esac
+ fi
+ done
+ IFS="$acl_save_IFS"
+ fi
+])
+m4trace:m4/libtool.m4:67: -1- AC_DEFUN([LT_INIT], [AC_PREREQ([2.58])dnl We use AC_INCLUDES_DEFAULT
+AC_BEFORE([$0], [LT_LANG])dnl
+AC_BEFORE([$0], [LT_OUTPUT])dnl
+AC_BEFORE([$0], [LTDL_INIT])dnl
+m4_require([_LT_CHECK_BUILDDIR])dnl
+
+dnl Autoconf doesn't catch unexpanded LT_ macros by default:
+m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl
+m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl
+dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4
+dnl unless we require an AC_DEFUNed macro:
+AC_REQUIRE([LTOPTIONS_VERSION])dnl
+AC_REQUIRE([LTSUGAR_VERSION])dnl
+AC_REQUIRE([LTVERSION_VERSION])dnl
+AC_REQUIRE([LTOBSOLETE_VERSION])dnl
+m4_require([_LT_PROG_LTMAIN])dnl
+
+dnl Parse OPTIONS
+_LT_SET_OPTIONS([$0], [$1])
+
+# This can be used to rebuild libtool when needed
+LIBTOOL_DEPS="$ltmain"
+
+# Always use our own libtool.
+LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+AC_SUBST(LIBTOOL)dnl
+
+_LT_SETUP
+
+# Only expand once:
+m4_define([LT_INIT])
+])
+m4trace:m4/libtool.m4:102: -1- AU_DEFUN([AC_PROG_LIBTOOL], [m4_if($#, 0, [LT_INIT], [LT_INIT($@)])])
+m4trace:m4/libtool.m4:102: -1- AC_DEFUN([AC_PROG_LIBTOOL], [AC_DIAGNOSE([obsolete], [The macro `AC_PROG_LIBTOOL' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_INIT], [LT_INIT($@)])])
+m4trace:m4/libtool.m4:103: -1- AU_DEFUN([AM_PROG_LIBTOOL], [m4_if($#, 0, [LT_INIT], [LT_INIT($@)])])
+m4trace:m4/libtool.m4:103: -1- AC_DEFUN([AM_PROG_LIBTOOL], [AC_DIAGNOSE([obsolete], [The macro `AM_PROG_LIBTOOL' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_INIT], [LT_INIT($@)])])
+m4trace:m4/libtool.m4:562: -1- AC_DEFUN([LT_OUTPUT], [: ${CONFIG_LT=./config.lt}
+AC_MSG_NOTICE([creating $CONFIG_LT])
+cat >"$CONFIG_LT" <<_LTEOF
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate a libtool stub with the current configuration.
+
+lt_cl_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_LTEOF
+
+cat >>"$CONFIG_LT" <<\_LTEOF
+AS_SHELL_SANITIZE
+_AS_PREPARE
+
+exec AS_MESSAGE_FD>&1
+exec AS_MESSAGE_LOG_FD>>config.log
+{
+ echo
+ AS_BOX([Running $as_me.])
+} >&AS_MESSAGE_LOG_FD
+
+lt_cl_help="\
+\`$as_me' creates a local libtool stub from the current configuration,
+for use in further configure time tests before the real libtool is
+generated.
+
+Usage: $[0] [[OPTIONS]]
+
+ -h, --help print this help, then exit
+ -V, --version print version number, then exit
+ -q, --quiet do not print progress messages
+ -d, --debug don't remove temporary files
+
+Report bugs to <bug-libtool@gnu.org>."
+
+lt_cl_version="\
+m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl
+m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION])
+configured by $[0], generated by m4_PACKAGE_STRING.
+
+Copyright (C) 2008 Free Software Foundation, Inc.
+This config.lt script is free software; the Free Software Foundation
+gives unlimited permision to copy, distribute and modify it."
+
+while test $[#] != 0
+do
+ case $[1] in
+ --version | --v* | -V )
+ echo "$lt_cl_version"; exit 0 ;;
+ --help | --h* | -h )
+ echo "$lt_cl_help"; exit 0 ;;
+ --debug | --d* | -d )
+ debug=: ;;
+ --quiet | --q* | --silent | --s* | -q )
+ lt_cl_silent=: ;;
+
+ -*) AC_MSG_ERROR([unrecognized option: $[1]
+Try \`$[0] --help' for more information.]) ;;
+
+ *) AC_MSG_ERROR([unrecognized argument: $[1]
+Try \`$[0] --help' for more information.]) ;;
+ esac
+ shift
+done
+
+if $lt_cl_silent; then
+ exec AS_MESSAGE_FD>/dev/null
+fi
+_LTEOF
+
+cat >>"$CONFIG_LT" <<_LTEOF
+_LT_OUTPUT_LIBTOOL_COMMANDS_INIT
+_LTEOF
+
+cat >>"$CONFIG_LT" <<\_LTEOF
+AC_MSG_NOTICE([creating $ofile])
+_LT_OUTPUT_LIBTOOL_COMMANDS
+AS_EXIT(0)
+_LTEOF
+chmod +x "$CONFIG_LT"
+
+# configure is writing to config.log, but config.lt does its own redirection,
+# appending to config.log, which fails on DOS, as config.log is still kept
+# open by configure. Here we exec the FD to /dev/null, effectively closing
+# config.log, so it can be properly (re)opened and appended to by config.lt.
+if test "$no_create" != yes; then
+ lt_cl_success=:
+ test "$silent" = yes &&
+ lt_config_lt_args="$lt_config_lt_args --quiet"
+ exec AS_MESSAGE_LOG_FD>/dev/null
+ $SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false
+ exec AS_MESSAGE_LOG_FD>>config.log
+ $lt_cl_success || AS_EXIT(1)
+fi
+])
+m4trace:m4/libtool.m4:756: -1- AC_DEFUN([LT_SUPPORTED_TAG], [])
+m4trace:m4/libtool.m4:767: -1- AC_DEFUN([LT_LANG], [AC_BEFORE([$0], [LT_OUTPUT])dnl
+m4_case([$1],
+ [C], [_LT_LANG(C)],
+ [C++], [_LT_LANG(CXX)],
+ [Java], [_LT_LANG(GCJ)],
+ [Fortran 77], [_LT_LANG(F77)],
+ [Fortran], [_LT_LANG(FC)],
+ [Windows Resource], [_LT_LANG(RC)],
+ [m4_ifdef([_LT_LANG_]$1[_CONFIG],
+ [_LT_LANG($1)],
+ [m4_fatal([$0: unsupported language: "$1"])])])dnl
+])
+m4trace:m4/libtool.m4:829: -1- AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)])
+m4trace:m4/libtool.m4:829: -1- AC_DEFUN([AC_LIBTOOL_CXX], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_CXX' is obsolete.
+You should run autoupdate.])dnl
+LT_LANG(C++)])
+m4trace:m4/libtool.m4:830: -1- AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)])
+m4trace:m4/libtool.m4:830: -1- AC_DEFUN([AC_LIBTOOL_F77], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_F77' is obsolete.
+You should run autoupdate.])dnl
+LT_LANG(Fortran 77)])
+m4trace:m4/libtool.m4:831: -1- AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)])
+m4trace:m4/libtool.m4:831: -1- AC_DEFUN([AC_LIBTOOL_FC], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_FC' is obsolete.
+You should run autoupdate.])dnl
+LT_LANG(Fortran)])
+m4trace:m4/libtool.m4:832: -1- AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)])
+m4trace:m4/libtool.m4:832: -1- AC_DEFUN([AC_LIBTOOL_GCJ], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_GCJ' is obsolete.
+You should run autoupdate.])dnl
+LT_LANG(Java)])
+m4trace:m4/libtool.m4:1401: -1- AC_DEFUN([_LT_COMPILER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_SED])dnl
+AC_CACHE_CHECK([$1], [$2],
+ [$2=no
+ m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4])
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+ lt_compiler_flag="$3"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ # The option is referenced via a variable to avoid confusing sed.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD)
+ (eval "$lt_compile" 2>conftest.err)
+ ac_status=$?
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+ if (exit $ac_status) && test -s "$ac_outfile"; then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings other than the usual output.
+ $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+ $2=yes
+ fi
+ fi
+ $RM conftest*
+])
+
+if test x"[$]$2" = xyes; then
+ m4_if([$5], , :, [$5])
+else
+ m4_if([$6], , :, [$6])
+fi
+])
+m4trace:m4/libtool.m4:1443: -1- AU_DEFUN([AC_LIBTOOL_COMPILER_OPTION], [m4_if($#, 0, [_LT_COMPILER_OPTION], [_LT_COMPILER_OPTION($@)])])
+m4trace:m4/libtool.m4:1443: -1- AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_COMPILER_OPTION' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [_LT_COMPILER_OPTION], [_LT_COMPILER_OPTION($@)])])
+m4trace:m4/libtool.m4:1452: -1- AC_DEFUN([_LT_LINKER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_SED])dnl
+AC_CACHE_CHECK([$1], [$2],
+ [$2=no
+ save_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS $3"
+ echo "$lt_simple_link_test_code" > conftest.$ac_ext
+ if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+ # The linker can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ if test -s conftest.err; then
+ # Append any errors to the config.log.
+ cat conftest.err 1>&AS_MESSAGE_LOG_FD
+ $ECHO "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if diff conftest.exp conftest.er2 >/dev/null; then
+ $2=yes
+ fi
+ else
+ $2=yes
+ fi
+ fi
+ $RM -r conftest*
+ LDFLAGS="$save_LDFLAGS"
+])
+
+if test x"[$]$2" = xyes; then
+ m4_if([$4], , :, [$4])
+else
+ m4_if([$5], , :, [$5])
+fi
+])
+m4trace:m4/libtool.m4:1487: -1- AU_DEFUN([AC_LIBTOOL_LINKER_OPTION], [m4_if($#, 0, [_LT_LINKER_OPTION], [_LT_LINKER_OPTION($@)])])
+m4trace:m4/libtool.m4:1487: -1- AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_LINKER_OPTION' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [_LT_LINKER_OPTION], [_LT_LINKER_OPTION($@)])])
+m4trace:m4/libtool.m4:1494: -1- AC_DEFUN([LT_CMD_MAX_LEN], [AC_REQUIRE([AC_CANONICAL_HOST])dnl
+# find the maximum length of command line arguments
+AC_MSG_CHECKING([the maximum length of command line arguments])
+AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl
+ i=0
+ teststring="ABCD"
+
+ case $build_os in
+ msdosdjgpp*)
+ # On DJGPP, this test can blow up pretty badly due to problems in libc
+ # (any single argument exceeding 2000 bytes causes a buffer overrun
+ # during glob expansion). Even if it were fixed, the result of this
+ # check would be larger than it should be.
+ lt_cv_sys_max_cmd_len=12288; # 12K is about right
+ ;;
+
+ gnu*)
+ # Under GNU Hurd, this test is not required because there is
+ # no limit to the length of command line arguments.
+ # Libtool will interpret -1 as no limit whatsoever
+ lt_cv_sys_max_cmd_len=-1;
+ ;;
+
+ cygwin* | mingw* | cegcc*)
+ # On Win9x/ME, this test blows up -- it succeeds, but takes
+ # about 5 minutes as the teststring grows exponentially.
+ # Worse, since 9x/ME are not pre-emptively multitasking,
+ # you end up with a "frozen" computer, even though with patience
+ # the test eventually succeeds (with a max line length of 256k).
+ # Instead, let's just punt: use the minimum linelength reported by
+ # all of the supported platforms: 8192 (on NT/2K/XP).
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ amigaos*)
+ # On AmigaOS with pdksh, this test takes hours, literally.
+ # So we just punt and use a minimum line length of 8192.
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ netbsd* | freebsd* | openbsd* | darwin* | dragonfly*)
+ # This has been around since 386BSD, at least. Likely further.
+ if test -x /sbin/sysctl; then
+ lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax`
+ elif test -x /usr/sbin/sysctl; then
+ lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax`
+ else
+ lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs
+ fi
+ # And add a safety zone
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+ ;;
+
+ interix*)
+ # We know the value 262144 and hardcode it with a safety zone (like BSD)
+ lt_cv_sys_max_cmd_len=196608
+ ;;
+
+ osf*)
+ # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure
+ # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not
+ # nice to cause kernel panics so lets avoid the loop below.
+ # First set a reasonable default.
+ lt_cv_sys_max_cmd_len=16384
+ #
+ if test -x /sbin/sysconfig; then
+ case `/sbin/sysconfig -q proc exec_disable_arg_limit` in
+ *1*) lt_cv_sys_max_cmd_len=-1 ;;
+ esac
+ fi
+ ;;
+ sco3.2v5*)
+ lt_cv_sys_max_cmd_len=102400
+ ;;
+ sysv5* | sco5v6* | sysv4.2uw2*)
+ kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null`
+ if test -n "$kargmax"; then
+ lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'`
+ else
+ lt_cv_sys_max_cmd_len=32768
+ fi
+ ;;
+ *)
+ lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null`
+ if test -n "$lt_cv_sys_max_cmd_len"; then
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+ else
+ # Make teststring a little bigger before we do anything with it.
+ # a 1K string should be a reasonable start.
+ for i in 1 2 3 4 5 6 7 8 ; do
+ teststring=$teststring$teststring
+ done
+ SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}}
+ # If test is not a shell built-in, we'll probably end up computing a
+ # maximum length that is only half of the actual maximum length, but
+ # we can't tell.
+ while { test "X"`$SHELL [$]0 --fallback-echo "X$teststring$teststring" 2>/dev/null` \
+ = "XX$teststring$teststring"; } >/dev/null 2>&1 &&
+ test $i != 17 # 1/2 MB should be enough
+ do
+ i=`expr $i + 1`
+ teststring=$teststring$teststring
+ done
+ # Only check the string length outside the loop.
+ lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1`
+ teststring=
+ # Add a significant safety factor because C++ compilers can tack on
+ # massive amounts of additional arguments before passing them to the
+ # linker. It appears as though 1/2 is a usable value.
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2`
+ fi
+ ;;
+ esac
+])
+if test -n $lt_cv_sys_max_cmd_len ; then
+ AC_MSG_RESULT($lt_cv_sys_max_cmd_len)
+else
+ AC_MSG_RESULT(none)
+fi
+max_cmd_len=$lt_cv_sys_max_cmd_len
+_LT_DECL([], [max_cmd_len], [0],
+ [What is the maximum length of a command?])
+])
+m4trace:m4/libtool.m4:1622: -1- AU_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], [m4_if($#, 0, [LT_CMD_MAX_LEN], [LT_CMD_MAX_LEN($@)])])
+m4trace:m4/libtool.m4:1622: -1- AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_SYS_MAX_CMD_LEN' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_CMD_MAX_LEN], [LT_CMD_MAX_LEN($@)])])
+m4trace:m4/libtool.m4:1723: -1- AC_DEFUN([LT_SYS_DLOPEN_SELF], [m4_require([_LT_HEADER_DLFCN])dnl
+if test "x$enable_dlopen" != xyes; then
+ enable_dlopen=unknown
+ enable_dlopen_self=unknown
+ enable_dlopen_self_static=unknown
+else
+ lt_cv_dlopen=no
+ lt_cv_dlopen_libs=
+
+ case $host_os in
+ beos*)
+ lt_cv_dlopen="load_add_on"
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+ ;;
+
+ mingw* | pw32* | cegcc*)
+ lt_cv_dlopen="LoadLibrary"
+ lt_cv_dlopen_libs=
+ ;;
+
+ cygwin*)
+ lt_cv_dlopen="dlopen"
+ lt_cv_dlopen_libs=
+ ;;
+
+ darwin*)
+ # if libdl is installed we need to link against it
+ AC_CHECK_LIB([dl], [dlopen],
+ [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],[
+ lt_cv_dlopen="dyld"
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+ ])
+ ;;
+
+ *)
+ AC_CHECK_FUNC([shl_load],
+ [lt_cv_dlopen="shl_load"],
+ [AC_CHECK_LIB([dld], [shl_load],
+ [lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"],
+ [AC_CHECK_FUNC([dlopen],
+ [lt_cv_dlopen="dlopen"],
+ [AC_CHECK_LIB([dl], [dlopen],
+ [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],
+ [AC_CHECK_LIB([svld], [dlopen],
+ [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"],
+ [AC_CHECK_LIB([dld], [dld_link],
+ [lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"])
+ ])
+ ])
+ ])
+ ])
+ ])
+ ;;
+ esac
+
+ if test "x$lt_cv_dlopen" != xno; then
+ enable_dlopen=yes
+ else
+ enable_dlopen=no
+ fi
+
+ case $lt_cv_dlopen in
+ dlopen)
+ save_CPPFLAGS="$CPPFLAGS"
+ test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+
+ save_LDFLAGS="$LDFLAGS"
+ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
+
+ save_LIBS="$LIBS"
+ LIBS="$lt_cv_dlopen_libs $LIBS"
+
+ AC_CACHE_CHECK([whether a program can dlopen itself],
+ lt_cv_dlopen_self, [dnl
+ _LT_TRY_DLOPEN_SELF(
+ lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes,
+ lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross)
+ ])
+
+ if test "x$lt_cv_dlopen_self" = xyes; then
+ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\"
+ AC_CACHE_CHECK([whether a statically linked program can dlopen itself],
+ lt_cv_dlopen_self_static, [dnl
+ _LT_TRY_DLOPEN_SELF(
+ lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes,
+ lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross)
+ ])
+ fi
+
+ CPPFLAGS="$save_CPPFLAGS"
+ LDFLAGS="$save_LDFLAGS"
+ LIBS="$save_LIBS"
+ ;;
+ esac
+
+ case $lt_cv_dlopen_self in
+ yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+ *) enable_dlopen_self=unknown ;;
+ esac
+
+ case $lt_cv_dlopen_self_static in
+ yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+ *) enable_dlopen_self_static=unknown ;;
+ esac
+fi
+_LT_DECL([dlopen_support], [enable_dlopen], [0],
+ [Whether dlopen is supported])
+_LT_DECL([dlopen_self], [enable_dlopen_self], [0],
+ [Whether dlopen of programs is supported])
+_LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0],
+ [Whether dlopen of statically linked programs is supported])
+])
+m4trace:m4/libtool.m4:1840: -1- AU_DEFUN([AC_LIBTOOL_DLOPEN_SELF], [m4_if($#, 0, [LT_SYS_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF($@)])])
+m4trace:m4/libtool.m4:1840: -1- AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_DLOPEN_SELF' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_SYS_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF($@)])])
+m4trace:m4/libtool.m4:2728: -1- AC_DEFUN([_LT_PATH_TOOL_PREFIX], [m4_require([_LT_DECL_EGREP])dnl
+AC_MSG_CHECKING([for $1])
+AC_CACHE_VAL(lt_cv_path_MAGIC_CMD,
+[case $MAGIC_CMD in
+[[\\/*] | ?:[\\/]*])
+ lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+ ;;
+*)
+ lt_save_MAGIC_CMD="$MAGIC_CMD"
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+dnl $ac_dummy forces splitting on constant user-supplied paths.
+dnl POSIX.2 word splitting is done only on the output of word expansions,
+dnl not every word. This closes a longstanding sh security hole.
+ ac_dummy="m4_if([$2], , $PATH, [$2])"
+ for ac_dir in $ac_dummy; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$1; then
+ lt_cv_path_MAGIC_CMD="$ac_dir/$1"
+ if test -n "$file_magic_test_file"; then
+ case $deplibs_check_method in
+ "file_magic "*)
+ file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+ MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+ if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+ $EGREP "$file_magic_regex" > /dev/null; then
+ :
+ else
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such. This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem. Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+ fi ;;
+ esac
+ fi
+ break
+ fi
+ done
+ IFS="$lt_save_ifs"
+ MAGIC_CMD="$lt_save_MAGIC_CMD"
+ ;;
+esac])
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+ AC_MSG_RESULT($MAGIC_CMD)
+else
+ AC_MSG_RESULT(no)
+fi
+_LT_DECL([], [MAGIC_CMD], [0],
+ [Used to examine libraries when file_magic_cmd begins with "file"])dnl
+])
+m4trace:m4/libtool.m4:2790: -1- AU_DEFUN([AC_PATH_TOOL_PREFIX], [m4_if($#, 0, [_LT_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX($@)])])
+m4trace:m4/libtool.m4:2790: -1- AC_DEFUN([AC_PATH_TOOL_PREFIX], [AC_DIAGNOSE([obsolete], [The macro `AC_PATH_TOOL_PREFIX' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [_LT_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX($@)])])
+m4trace:m4/libtool.m4:2813: -1- AC_DEFUN([LT_PATH_LD], [AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_DECL_EGREP])dnl
+
+AC_ARG_WITH([gnu-ld],
+ [AS_HELP_STRING([--with-gnu-ld],
+ [assume the C compiler uses GNU ld @<:@default=no@:>@])],
+ [test "$withval" = no || with_gnu_ld=yes],
+ [with_gnu_ld=no])dnl
+
+ac_prog=ld
+if test "$GCC" = yes; then
+ # Check if gcc -print-prog-name=ld gives a path.
+ AC_MSG_CHECKING([for ld used by $CC])
+ case $host in
+ *-*-mingw*)
+ # gcc leaves a trailing carriage return which upsets mingw
+ ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+ *)
+ ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+ esac
+ case $ac_prog in
+ # Accept absolute paths.
+ [[\\/]]* | ?:[[\\/]]*)
+ re_direlt='/[[^/]][[^/]]*/\.\./'
+ # Canonicalize the pathname of ld
+ ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'`
+ while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
+ ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
+ done
+ test -z "$LD" && LD="$ac_prog"
+ ;;
+ "")
+ # If it fails, then pretend we aren't using GCC.
+ ac_prog=ld
+ ;;
+ *)
+ # If it is relative, then search for the first ld in PATH.
+ with_gnu_ld=unknown
+ ;;
+ esac
+elif test "$with_gnu_ld" = yes; then
+ AC_MSG_CHECKING([for GNU ld])
+else
+ AC_MSG_CHECKING([for non-GNU ld])
+fi
+AC_CACHE_VAL(lt_cv_path_LD,
+[if test -z "$LD"; then
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+ lt_cv_path_LD="$ac_dir/$ac_prog"
+ # Check to see if the program is GNU ld. I'd rather use --version,
+ # but apparently some variants of GNU ld only accept -v.
+ # Break only if it was the GNU/non-GNU ld that we prefer.
+ case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
+ *GNU* | *'with BFD'*)
+ test "$with_gnu_ld" != no && break
+ ;;
+ *)
+ test "$with_gnu_ld" != yes && break
+ ;;
+ esac
+ fi
+ done
+ IFS="$lt_save_ifs"
+else
+ lt_cv_path_LD="$LD" # Let the user override the test with a path.
+fi])
+LD="$lt_cv_path_LD"
+if test -n "$LD"; then
+ AC_MSG_RESULT($LD)
+else
+ AC_MSG_RESULT(no)
+fi
+test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH])
+_LT_PATH_LD_GNU
+AC_SUBST([LD])
+
+_LT_TAGDECL([], [LD], [1], [The linker used to build libraries])
+])
+m4trace:m4/libtool.m4:2901: -1- AU_DEFUN([AM_PROG_LD], [m4_if($#, 0, [LT_PATH_LD], [LT_PATH_LD($@)])])
+m4trace:m4/libtool.m4:2901: -1- AC_DEFUN([AM_PROG_LD], [AC_DIAGNOSE([obsolete], [The macro `AM_PROG_LD' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_PATH_LD], [LT_PATH_LD($@)])])
+m4trace:m4/libtool.m4:2902: -1- AU_DEFUN([AC_PROG_LD], [m4_if($#, 0, [LT_PATH_LD], [LT_PATH_LD($@)])])
+m4trace:m4/libtool.m4:2902: -1- AC_DEFUN([AC_PROG_LD], [AC_DIAGNOSE([obsolete], [The macro `AC_PROG_LD' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_PATH_LD], [LT_PATH_LD($@)])])
+m4trace:m4/libtool.m4:3166: -1- AC_DEFUN([LT_PATH_NM], [AC_REQUIRE([AC_PROG_CC])dnl
+AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM,
+[if test -n "$NM"; then
+ # Let the user override the test.
+ lt_cv_path_NM="$NM"
+else
+ lt_nm_to_check="${ac_tool_prefix}nm"
+ if test -n "$ac_tool_prefix" && test "$build" = "$host"; then
+ lt_nm_to_check="$lt_nm_to_check nm"
+ fi
+ for lt_tmp_nm in $lt_nm_to_check; do
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ tmp_nm="$ac_dir/$lt_tmp_nm"
+ if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then
+ # Check to see if the nm accepts a BSD-compat flag.
+ # Adding the `sed 1q' prevents false positives on HP-UX, which says:
+ # nm: unknown option "B" ignored
+ # Tru64's nm complains that /dev/null is an invalid object file
+ case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in
+ */dev/null* | *'Invalid file or object type'*)
+ lt_cv_path_NM="$tmp_nm -B"
+ break
+ ;;
+ *)
+ case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in
+ */dev/null*)
+ lt_cv_path_NM="$tmp_nm -p"
+ break
+ ;;
+ *)
+ lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
+ continue # so that we can try to find one that supports BSD flags
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ done
+ IFS="$lt_save_ifs"
+ done
+ : ${lt_cv_path_NM=no}
+fi])
+if test "$lt_cv_path_NM" != "no"; then
+ NM="$lt_cv_path_NM"
+else
+ # Didn't find any BSD compatible name lister, look for dumpbin.
+ AC_CHECK_TOOLS(DUMPBIN, ["dumpbin -symbols" "link -dump -symbols"], :)
+ AC_SUBST([DUMPBIN])
+ if test "$DUMPBIN" != ":"; then
+ NM="$DUMPBIN"
+ fi
+fi
+test -z "$NM" && NM=nm
+AC_SUBST([NM])
+_LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl
+
+AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface],
+ [lt_cv_nm_interface="BSD nm"
+ echo "int some_variable = 0;" > conftest.$ac_ext
+ (eval echo "\"\$as_me:__oline__: $ac_compile\"" >&AS_MESSAGE_LOG_FD)
+ (eval "$ac_compile" 2>conftest.err)
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ (eval echo "\"\$as_me:__oline__: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD)
+ (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ (eval echo "\"\$as_me:__oline__: output\"" >&AS_MESSAGE_LOG_FD)
+ cat conftest.out >&AS_MESSAGE_LOG_FD
+ if $GREP 'External.*some_variable' conftest.out > /dev/null; then
+ lt_cv_nm_interface="MS dumpbin"
+ fi
+ rm -f conftest*])
+])
+m4trace:m4/libtool.m4:3244: -1- AU_DEFUN([AM_PROG_NM], [m4_if($#, 0, [LT_PATH_NM], [LT_PATH_NM($@)])])
+m4trace:m4/libtool.m4:3244: -1- AC_DEFUN([AM_PROG_NM], [AC_DIAGNOSE([obsolete], [The macro `AM_PROG_NM' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_PATH_NM], [LT_PATH_NM($@)])])
+m4trace:m4/libtool.m4:3245: -1- AU_DEFUN([AC_PROG_NM], [m4_if($#, 0, [LT_PATH_NM], [LT_PATH_NM($@)])])
+m4trace:m4/libtool.m4:3245: -1- AC_DEFUN([AC_PROG_NM], [AC_DIAGNOSE([obsolete], [The macro `AC_PROG_NM' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_PATH_NM], [LT_PATH_NM($@)])])
+m4trace:m4/libtool.m4:3254: -1- AC_DEFUN([LT_LIB_M], [AC_REQUIRE([AC_CANONICAL_HOST])dnl
+LIBM=
+case $host in
+*-*-beos* | *-*-cygwin* | *-*-pw32* | *-*-darwin*)
+ # These system don't have libm, or don't need it
+ ;;
+*-ncr-sysv4.3*)
+ AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw")
+ AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm")
+ ;;
+*)
+ AC_CHECK_LIB(m, cos, LIBM="-lm")
+ ;;
+esac
+AC_SUBST([LIBM])
+])
+m4trace:m4/libtool.m4:3273: -1- AU_DEFUN([AC_CHECK_LIBM], [m4_if($#, 0, [LT_LIB_M], [LT_LIB_M($@)])])
+m4trace:m4/libtool.m4:3273: -1- AC_DEFUN([AC_CHECK_LIBM], [AC_DIAGNOSE([obsolete], [The macro `AC_CHECK_LIBM' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_LIB_M], [LT_LIB_M($@)])])
+m4trace:m4/libtool.m4:6975: -1- AC_DEFUN([LT_PROG_GCJ], [m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ],
+ [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ],
+ [AC_CHECK_TOOL(GCJ, gcj,)
+ test "x${GCJFLAGS+set}" = xset || GCJFLAGS="-g -O2"
+ AC_SUBST(GCJFLAGS)])])[]dnl
+])
+m4trace:m4/libtool.m4:6984: -1- AU_DEFUN([LT_AC_PROG_GCJ], [m4_if($#, 0, [LT_PROG_GCJ], [LT_PROG_GCJ($@)])])
+m4trace:m4/libtool.m4:6984: -1- AC_DEFUN([LT_AC_PROG_GCJ], [AC_DIAGNOSE([obsolete], [The macro `LT_AC_PROG_GCJ' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_PROG_GCJ], [LT_PROG_GCJ($@)])])
+m4trace:m4/libtool.m4:6991: -1- AC_DEFUN([LT_PROG_RC], [AC_CHECK_TOOL(RC, windres,)
+])
+m4trace:m4/libtool.m4:6996: -1- AU_DEFUN([LT_AC_PROG_RC], [m4_if($#, 0, [LT_PROG_RC], [LT_PROG_RC($@)])])
+m4trace:m4/libtool.m4:6996: -1- AC_DEFUN([LT_AC_PROG_RC], [AC_DIAGNOSE([obsolete], [The macro `LT_AC_PROG_RC' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [LT_PROG_RC], [LT_PROG_RC($@)])])
+m4trace:m4/libtool.m4:7107: -1- AU_DEFUN([LT_AC_PROG_SED], [m4_if($#, 0, [AC_PROG_SED], [AC_PROG_SED($@)])])
+m4trace:m4/libtool.m4:7107: -1- AC_DEFUN([LT_AC_PROG_SED], [AC_DIAGNOSE([obsolete], [The macro `LT_AC_PROG_SED' is obsolete.
+You should run autoupdate.])dnl
+m4_if($#, 0, [AC_PROG_SED], [AC_PROG_SED($@)])])
+m4trace:m4/lock.m4:22: -1- AC_DEFUN([gl_LOCK_EARLY], [
+ AC_REQUIRE([gl_LOCK_EARLY_BODY])
+])
+m4trace:m4/lock.m4:29: -1- AC_DEFUN([gl_LOCK_EARLY_BODY], [
+ dnl Ordering constraints: This macro modifies CPPFLAGS in a way that
+ dnl influences the result of the autoconf tests that test for *_unlocked
+ dnl declarations, on AIX 5 at least. Therefore it must come early.
+ AC_BEFORE([$0], [gl_FUNC_GLIBC_UNLOCKED_IO])dnl
+ AC_BEFORE([$0], [gl_ARGP])dnl
+
+ AC_REQUIRE([AC_CANONICAL_HOST])
+ AC_REQUIRE([AC_GNU_SOURCE]) dnl needed for pthread_rwlock_t on glibc systems
+ dnl Check for multithreading.
+ AC_ARG_ENABLE(threads,
+AC_HELP_STRING([--enable-threads={posix|solaris|pth|win32}], [specify multithreading API])
+AC_HELP_STRING([--disable-threads], [build without multithread safety]),
+ [gl_use_threads=$enableval],
+ [case "$host_os" in
+ dnl Disable multithreading by default on OSF/1, because it interferes
+ dnl with fork()/exec(): When msgexec is linked with -lpthread, its child
+ dnl process gets an endless segmentation fault inside execvp().
+ osf*) gl_use_threads=no ;;
+ *) gl_use_threads=yes ;;
+ esac
+ ])
+ if test "$gl_use_threads" = yes || test "$gl_use_threads" = posix; then
+ # For using <pthread.h>:
+ case "$host_os" in
+ osf*)
+ # On OSF/1, the compiler needs the flag -D_REENTRANT so that it
+ # groks <pthread.h>. cc also understands the flag -pthread, but
+ # we don't use it because 1. gcc-2.95 doesn't understand -pthread,
+ # 2. putting a flag into CPPFLAGS that has an effect on the linker
+ # causes the AC_TRY_LINK test below to succeed unexpectedly,
+ # leading to wrong values of LIBTHREAD and LTLIBTHREAD.
+ CPPFLAGS="$CPPFLAGS -D_REENTRANT"
+ ;;
+ esac
+ # Some systems optimize for single-threaded programs by default, and
+ # need special flags to disable these optimizations. For example, the
+ # definition of 'errno' in <errno.h>.
+ case "$host_os" in
+ aix* | freebsd*) CPPFLAGS="$CPPFLAGS -D_THREAD_SAFE" ;;
+ solaris*) CPPFLAGS="$CPPFLAGS -D_REENTRANT" ;;
+ esac
+ fi
+])
+m4trace:m4/lock.m4:77: -1- AC_DEFUN([gl_LOCK_BODY], [
+ AC_REQUIRE([gl_LOCK_EARLY_BODY])
+ gl_threads_api=none
+ LIBTHREAD=
+ LTLIBTHREAD=
+ LIBMULTITHREAD=
+ LTLIBMULTITHREAD=
+ if test "$gl_use_threads" != no; then
+ dnl Check whether the compiler and linker support weak declarations.
+ AC_MSG_CHECKING([whether imported symbols can be declared weak])
+ gl_have_weak=no
+ AC_TRY_LINK([extern void xyzzy ();
+#pragma weak xyzzy], [xyzzy();], [gl_have_weak=yes])
+ AC_MSG_RESULT([$gl_have_weak])
+ if test "$gl_use_threads" = yes || test "$gl_use_threads" = posix; then
+ # On OSF/1, the compiler needs the flag -pthread or -D_REENTRANT so that
+ # it groks <pthread.h>. It's added above, in gl_LOCK_EARLY_BODY.
+ AC_CHECK_HEADER(pthread.h, gl_have_pthread_h=yes, gl_have_pthread_h=no)
+ if test "$gl_have_pthread_h" = yes; then
+ # Other possible tests:
+ # -lpthreads (FSU threads, PCthreads)
+ # -lgthreads
+ gl_have_pthread=
+ # Test whether both pthread_mutex_lock and pthread_mutexattr_init exist
+ # in libc. IRIX 6.5 has the first one in both libc and libpthread, but
+ # the second one only in libpthread, and lock.c needs it.
+ AC_TRY_LINK([#include <pthread.h>],
+ [pthread_mutex_lock((pthread_mutex_t*)0);
+ pthread_mutexattr_init((pthread_mutexattr_t*)0);],
+ [gl_have_pthread=yes])
+ # Test for libpthread by looking for pthread_kill. (Not pthread_self,
+ # since it is defined as a macro on OSF/1.)
+ if test -n "$gl_have_pthread"; then
+ # The program links fine without libpthread. But it may actually
+ # need to link with libpthread in order to create multiple threads.
+ AC_CHECK_LIB(pthread, pthread_kill,
+ [LIBMULTITHREAD=-lpthread LTLIBMULTITHREAD=-lpthread
+ # On Solaris and HP-UX, most pthread functions exist also in libc.
+ # Therefore pthread_in_use() needs to actually try to create a
+ # thread: pthread_create from libc will fail, whereas
+ # pthread_create will actually create a thread.
+ case "$host_os" in
+ solaris* | hpux*)
+ AC_DEFINE([PTHREAD_IN_USE_DETECTION_HARD], 1,
+ [Define if the pthread_in_use() detection is hard.])
+ esac
+ ])
+ else
+ # Some library is needed. Try libpthread and libc_r.
+ AC_CHECK_LIB(pthread, pthread_kill,
+ [gl_have_pthread=yes
+ LIBTHREAD=-lpthread LTLIBTHREAD=-lpthread
+ LIBMULTITHREAD=-lpthread LTLIBMULTITHREAD=-lpthread])
+ if test -z "$gl_have_pthread"; then
+ # For FreeBSD 4.
+ AC_CHECK_LIB(c_r, pthread_kill,
+ [gl_have_pthread=yes
+ LIBTHREAD=-lc_r LTLIBTHREAD=-lc_r
+ LIBMULTITHREAD=-lc_r LTLIBMULTITHREAD=-lc_r])
+ fi
+ fi
+ if test -n "$gl_have_pthread"; then
+ gl_threads_api=posix
+ AC_DEFINE([USE_POSIX_THREADS], 1,
+ [Define if the POSIX multithreading library can be used.])
+ if test -n "$LIBMULTITHREAD" || test -n "$LTLIBMULTITHREAD"; then
+ if test $gl_have_weak = yes; then
+ AC_DEFINE([USE_POSIX_THREADS_WEAK], 1,
+ [Define if references to the POSIX multithreading library should be made weak.])
+ LIBTHREAD=
+ LTLIBTHREAD=
+ fi
+ fi
+ # OSF/1 4.0 and MacOS X 10.1 lack the pthread_rwlock_t type and the
+ # pthread_rwlock_* functions.
+ AC_CHECK_TYPE([pthread_rwlock_t],
+ [AC_DEFINE([HAVE_PTHREAD_RWLOCK], 1,
+ [Define if the POSIX multithreading library has read/write locks.])],
+ [],
+ [#include <pthread.h>])
+ # glibc defines PTHREAD_MUTEX_RECURSIVE as enum, not as a macro.
+ AC_TRY_COMPILE([#include <pthread.h>],
+ [#if __FreeBSD__ == 4
+error "No, in FreeBSD 4.0 recursive mutexes actually don't work."
+#else
+int x = (int)PTHREAD_MUTEX_RECURSIVE;
+return !x;
+#endif],
+ [AC_DEFINE([HAVE_PTHREAD_MUTEX_RECURSIVE], 1,
+ [Define if the <pthread.h> defines PTHREAD_MUTEX_RECURSIVE.])])
+ fi
+ fi
+ fi
+ if test -z "$gl_have_pthread"; then
+ if test "$gl_use_threads" = yes || test "$gl_use_threads" = solaris; then
+ gl_have_solaristhread=
+ gl_save_LIBS="$LIBS"
+ LIBS="$LIBS -lthread"
+ AC_TRY_LINK([#include <thread.h>
+#include <synch.h>],
+ [thr_self();],
+ [gl_have_solaristhread=yes])
+ LIBS="$gl_save_LIBS"
+ if test -n "$gl_have_solaristhread"; then
+ gl_threads_api=solaris
+ LIBTHREAD=-lthread
+ LTLIBTHREAD=-lthread
+ LIBMULTITHREAD="$LIBTHREAD"
+ LTLIBMULTITHREAD="$LTLIBTHREAD"
+ AC_DEFINE([USE_SOLARIS_THREADS], 1,
+ [Define if the old Solaris multithreading library can be used.])
+ if test $gl_have_weak = yes; then
+ AC_DEFINE([USE_SOLARIS_THREADS_WEAK], 1,
+ [Define if references to the old Solaris multithreading library should be made weak.])
+ LIBTHREAD=
+ LTLIBTHREAD=
+ fi
+ fi
+ fi
+ fi
+ if test "$gl_use_threads" = pth; then
+ gl_save_CPPFLAGS="$CPPFLAGS"
+ AC_LIB_LINKFLAGS(pth)
+ gl_have_pth=
+ gl_save_LIBS="$LIBS"
+ LIBS="$LIBS -lpth"
+ AC_TRY_LINK([#include <pth.h>], [pth_self();], gl_have_pth=yes)
+ LIBS="$gl_save_LIBS"
+ if test -n "$gl_have_pth"; then
+ gl_threads_api=pth
+ LIBTHREAD="$LIBPTH"
+ LTLIBTHREAD="$LTLIBPTH"
+ LIBMULTITHREAD="$LIBTHREAD"
+ LTLIBMULTITHREAD="$LTLIBTHREAD"
+ AC_DEFINE([USE_PTH_THREADS], 1,
+ [Define if the GNU Pth multithreading library can be used.])
+ if test -n "$LIBMULTITHREAD" || test -n "$LTLIBMULTITHREAD"; then
+ if test $gl_have_weak = yes; then
+ AC_DEFINE([USE_PTH_THREADS_WEAK], 1,
+ [Define if references to the GNU Pth multithreading library should be made weak.])
+ LIBTHREAD=
+ LTLIBTHREAD=
+ fi
+ fi
+ else
+ CPPFLAGS="$gl_save_CPPFLAGS"
+ fi
+ fi
+ if test -z "$gl_have_pthread"; then
+ if test "$gl_use_threads" = yes || test "$gl_use_threads" = win32; then
+ if { case "$host_os" in
+ mingw*) true;;
+ *) false;;
+ esac
+ }; then
+ gl_threads_api=win32
+ AC_DEFINE([USE_WIN32_THREADS], 1,
+ [Define if the Win32 multithreading API can be used.])
+ fi
+ fi
+ fi
+ fi
+ AC_MSG_CHECKING([for multithread API to use])
+ AC_MSG_RESULT([$gl_threads_api])
+ AC_SUBST(LIBTHREAD)
+ AC_SUBST(LTLIBTHREAD)
+ AC_SUBST(LIBMULTITHREAD)
+ AC_SUBST(LTLIBMULTITHREAD)
+])
+m4trace:m4/lock.m4:248: -1- AC_DEFUN([gl_LOCK], [
+ AC_REQUIRE([gl_LOCK_EARLY])
+ AC_REQUIRE([gl_LOCK_BODY])
+ gl_PREREQ_LOCK
+])
+m4trace:m4/lock.m4:256: -1- AC_DEFUN([gl_PREREQ_LOCK], [
+ AC_REQUIRE([AC_C_INLINE])
+])
+m4trace:m4/longdouble.m4:14: -1- AC_DEFUN([gt_TYPE_LONGDOUBLE], [
+ AC_CACHE_CHECK([for long double], gt_cv_c_long_double,
+ [if test "$GCC" = yes; then
+ gt_cv_c_long_double=yes
+ else
+ AC_TRY_COMPILE([
+ /* The Stardent Vistra knows sizeof(long double), but does not support it. */
+ long double foo = 0.0;
+ /* On Ultrix 4.3 cc, long double is 4 and double is 8. */
+ int array [2*(sizeof(long double) >= sizeof(double)) - 1];
+ ], ,
+ gt_cv_c_long_double=yes, gt_cv_c_long_double=no)
+ fi])
+ if test $gt_cv_c_long_double = yes; then
+ AC_DEFINE(HAVE_LONG_DOUBLE, 1, [Define if you have the 'long double' type.])
+ fi
+])
+m4trace:m4/longlong.m4:17: -1- AC_DEFUN([AC_TYPE_LONG_LONG_INT], [
+ AC_CACHE_CHECK([for long long int], [ac_cv_type_long_long_int],
+ [AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[long long int ll = 9223372036854775807ll;
+ long long int nll = -9223372036854775807LL;
+ typedef int a[((-9223372036854775807LL < 0
+ && 0 < 9223372036854775807ll)
+ ? 1 : -1)];
+ int i = 63;]],
+ [[long long int llmax = 9223372036854775807ll;
+ return ((ll << 63) | (ll >> 63) | (ll < i) | (ll > i)
+ | (llmax / ll) | (llmax % ll));]])],
+ [ac_cv_type_long_long_int=yes],
+ [ac_cv_type_long_long_int=no])])
+ if test $ac_cv_type_long_long_int = yes; then
+ AC_DEFINE([HAVE_LONG_LONG_INT], 1,
+ [Define to 1 if the system has the type `long long int'.])
+ fi
+])
+m4trace:m4/longlong.m4:40: -1- AC_DEFUN([gl_AC_TYPE_LONG_LONG], [
+ AC_REQUIRE([AC_TYPE_LONG_LONG_INT])
+ ac_cv_type_long_long=$ac_cv_type_long_long_int
+ if test $ac_cv_type_long_long = yes; then
+ AC_DEFINE(HAVE_LONG_LONG, 1,
+ [Define if you have the 'long long' type.])
+ fi
+])
+m4trace:m4/ltoptions.m4:13: -1- AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])])
+m4trace:m4/ltoptions.m4:110: -1- AU_DEFUN([AC_LIBTOOL_DLOPEN], [_LT_SET_OPTION([LT_INIT], [dlopen])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `dlopen' option into LT_INIT's first parameter.])
+])
+m4trace:m4/ltoptions.m4:110: -1- AC_DEFUN([AC_LIBTOOL_DLOPEN], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_DLOPEN' is obsolete.
+You should run autoupdate.])dnl
+_LT_SET_OPTION([LT_INIT], [dlopen])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `dlopen' option into LT_INIT's first parameter.])
+])
+m4trace:m4/ltoptions.m4:145: -1- AU_DEFUN([AC_LIBTOOL_WIN32_DLL], [AC_REQUIRE([AC_CANONICAL_HOST])dnl
+_LT_SET_OPTION([LT_INIT], [win32-dll])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `win32-dll' option into LT_INIT's first parameter.])
+])
+m4trace:m4/ltoptions.m4:145: -1- AC_DEFUN([AC_LIBTOOL_WIN32_DLL], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_WIN32_DLL' is obsolete.
+You should run autoupdate.])dnl
+AC_REQUIRE([AC_CANONICAL_HOST])dnl
+_LT_SET_OPTION([LT_INIT], [win32-dll])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `win32-dll' option into LT_INIT's first parameter.])
+])
+m4trace:m4/ltoptions.m4:194: -1- AC_DEFUN([AC_ENABLE_SHARED], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared])
+])
+m4trace:m4/ltoptions.m4:198: -1- AC_DEFUN([AC_DISABLE_SHARED], [_LT_SET_OPTION([LT_INIT], [disable-shared])
+])
+m4trace:m4/ltoptions.m4:202: -1- AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)])
+m4trace:m4/ltoptions.m4:202: -1- AC_DEFUN([AM_ENABLE_SHARED], [AC_DIAGNOSE([obsolete], [The macro `AM_ENABLE_SHARED' is obsolete.
+You should run autoupdate.])dnl
+AC_ENABLE_SHARED($@)])
+m4trace:m4/ltoptions.m4:203: -1- AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)])
+m4trace:m4/ltoptions.m4:203: -1- AC_DEFUN([AM_DISABLE_SHARED], [AC_DIAGNOSE([obsolete], [The macro `AM_DISABLE_SHARED' is obsolete.
+You should run autoupdate.])dnl
+AC_DISABLE_SHARED($@)])
+m4trace:m4/ltoptions.m4:248: -1- AC_DEFUN([AC_ENABLE_STATIC], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static])
+])
+m4trace:m4/ltoptions.m4:252: -1- AC_DEFUN([AC_DISABLE_STATIC], [_LT_SET_OPTION([LT_INIT], [disable-static])
+])
+m4trace:m4/ltoptions.m4:256: -1- AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)])
+m4trace:m4/ltoptions.m4:256: -1- AC_DEFUN([AM_ENABLE_STATIC], [AC_DIAGNOSE([obsolete], [The macro `AM_ENABLE_STATIC' is obsolete.
+You should run autoupdate.])dnl
+AC_ENABLE_STATIC($@)])
+m4trace:m4/ltoptions.m4:257: -1- AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)])
+m4trace:m4/ltoptions.m4:257: -1- AC_DEFUN([AM_DISABLE_STATIC], [AC_DIAGNOSE([obsolete], [The macro `AM_DISABLE_STATIC' is obsolete.
+You should run autoupdate.])dnl
+AC_DISABLE_STATIC($@)])
+m4trace:m4/ltoptions.m4:302: -1- AU_DEFUN([AC_ENABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you put
+the `fast-install' option into LT_INIT's first parameter.])
+])
+m4trace:m4/ltoptions.m4:302: -1- AC_DEFUN([AC_ENABLE_FAST_INSTALL], [AC_DIAGNOSE([obsolete], [The macro `AC_ENABLE_FAST_INSTALL' is obsolete.
+You should run autoupdate.])dnl
+_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you put
+the `fast-install' option into LT_INIT's first parameter.])
+])
+m4trace:m4/ltoptions.m4:309: -1- AU_DEFUN([AC_DISABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], [disable-fast-install])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you put
+the `disable-fast-install' option into LT_INIT's first parameter.])
+])
+m4trace:m4/ltoptions.m4:309: -1- AC_DEFUN([AC_DISABLE_FAST_INSTALL], [AC_DIAGNOSE([obsolete], [The macro `AC_DISABLE_FAST_INSTALL' is obsolete.
+You should run autoupdate.])dnl
+_LT_SET_OPTION([LT_INIT], [disable-fast-install])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you put
+the `disable-fast-install' option into LT_INIT's first parameter.])
+])
+m4trace:m4/ltoptions.m4:342: -1- AU_DEFUN([AC_LIBTOOL_PICMODE], [_LT_SET_OPTION([LT_INIT], [pic-only])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `pic-only' option into LT_INIT's first parameter.])
+])
+m4trace:m4/ltoptions.m4:342: -1- AC_DEFUN([AC_LIBTOOL_PICMODE], [AC_DIAGNOSE([obsolete], [The macro `AC_LIBTOOL_PICMODE' is obsolete.
+You should run autoupdate.])dnl
+_LT_SET_OPTION([LT_INIT], [pic-only])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `pic-only' option into LT_INIT's first parameter.])
+])
+m4trace:m4/ltsugar.m4:13: -1- AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])])
+m4trace:m4/ltversion.m4:18: -1- AC_DEFUN([LTVERSION_VERSION], [macro_version='2.2.6b'
+macro_revision='1.3018'
+_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?])
+_LT_DECL(, macro_revision, 0)
+])
+m4trace:m4/lt~obsolete.m4:36: -1- AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])])
+m4trace:m4/lt~obsolete.m4:40: -1- AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])
+m4trace:m4/lt~obsolete.m4:41: -1- AC_DEFUN([_LT_AC_SHELL_INIT])
+m4trace:m4/lt~obsolete.m4:42: -1- AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])
+m4trace:m4/lt~obsolete.m4:44: -1- AC_DEFUN([_LT_AC_TAGVAR])
+m4trace:m4/lt~obsolete.m4:45: -1- AC_DEFUN([AC_LTDL_ENABLE_INSTALL])
+m4trace:m4/lt~obsolete.m4:46: -1- AC_DEFUN([AC_LTDL_PREOPEN])
+m4trace:m4/lt~obsolete.m4:47: -1- AC_DEFUN([_LT_AC_SYS_COMPILER])
+m4trace:m4/lt~obsolete.m4:48: -1- AC_DEFUN([_LT_AC_LOCK])
+m4trace:m4/lt~obsolete.m4:49: -1- AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])
+m4trace:m4/lt~obsolete.m4:50: -1- AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])
+m4trace:m4/lt~obsolete.m4:51: -1- AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])
+m4trace:m4/lt~obsolete.m4:52: -1- AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])
+m4trace:m4/lt~obsolete.m4:53: -1- AC_DEFUN([AC_LIBTOOL_OBJDIR])
+m4trace:m4/lt~obsolete.m4:54: -1- AC_DEFUN([AC_LTDL_OBJDIR])
+m4trace:m4/lt~obsolete.m4:55: -1- AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])
+m4trace:m4/lt~obsolete.m4:56: -1- AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])
+m4trace:m4/lt~obsolete.m4:57: -1- AC_DEFUN([AC_PATH_MAGIC])
+m4trace:m4/lt~obsolete.m4:58: -1- AC_DEFUN([AC_PROG_LD_GNU])
+m4trace:m4/lt~obsolete.m4:59: -1- AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])
+m4trace:m4/lt~obsolete.m4:60: -1- AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])
+m4trace:m4/lt~obsolete.m4:61: -1- AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])
+m4trace:m4/lt~obsolete.m4:62: -1- AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])
+m4trace:m4/lt~obsolete.m4:63: -1- AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])
+m4trace:m4/lt~obsolete.m4:64: -1- AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])
+m4trace:m4/lt~obsolete.m4:65: -1- AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])
+m4trace:m4/lt~obsolete.m4:66: -1- AC_DEFUN([LT_AC_PROG_EGREP])
+m4trace:m4/lt~obsolete.m4:71: -1- AC_DEFUN([_AC_PROG_LIBTOOL])
+m4trace:m4/lt~obsolete.m4:72: -1- AC_DEFUN([AC_LIBTOOL_SETUP])
+m4trace:m4/lt~obsolete.m4:73: -1- AC_DEFUN([_LT_AC_CHECK_DLFCN])
+m4trace:m4/lt~obsolete.m4:74: -1- AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])
+m4trace:m4/lt~obsolete.m4:75: -1- AC_DEFUN([_LT_AC_TAGCONFIG])
+m4trace:m4/lt~obsolete.m4:77: -1- AC_DEFUN([_LT_AC_LANG_CXX])
+m4trace:m4/lt~obsolete.m4:78: -1- AC_DEFUN([_LT_AC_LANG_F77])
+m4trace:m4/lt~obsolete.m4:79: -1- AC_DEFUN([_LT_AC_LANG_GCJ])
+m4trace:m4/lt~obsolete.m4:80: -1- AC_DEFUN([AC_LIBTOOL_RC])
+m4trace:m4/lt~obsolete.m4:81: -1- AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])
+m4trace:m4/lt~obsolete.m4:82: -1- AC_DEFUN([_LT_AC_LANG_C_CONFIG])
+m4trace:m4/lt~obsolete.m4:83: -1- AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])
+m4trace:m4/lt~obsolete.m4:84: -1- AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])
+m4trace:m4/lt~obsolete.m4:85: -1- AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])
+m4trace:m4/lt~obsolete.m4:86: -1- AC_DEFUN([_LT_AC_LANG_F77_CONFIG])
+m4trace:m4/lt~obsolete.m4:87: -1- AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])
+m4trace:m4/lt~obsolete.m4:88: -1- AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])
+m4trace:m4/lt~obsolete.m4:89: -1- AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])
+m4trace:m4/lt~obsolete.m4:90: -1- AC_DEFUN([_LT_AC_LANG_RC_CONFIG])
+m4trace:m4/lt~obsolete.m4:91: -1- AC_DEFUN([AC_LIBTOOL_CONFIG])
+m4trace:m4/lt~obsolete.m4:92: -1- AC_DEFUN([_LT_AC_FILE_LTDLL_C])
+m4trace:m4/nls.m4:22: -1- AC_DEFUN([AM_NLS], [
+ AC_MSG_CHECKING([whether NLS is requested])
+ dnl Default is enabled NLS
+ AC_ARG_ENABLE(nls,
+ [ --disable-nls do not use Native Language Support],
+ USE_NLS=$enableval, USE_NLS=yes)
+ AC_MSG_RESULT($USE_NLS)
+ AC_SUBST(USE_NLS)
+])
+m4trace:m4/po.m4:23: -1- AC_DEFUN([AM_PO_SUBDIRS], [
+ AC_REQUIRE([AC_PROG_MAKE_SET])dnl
+ AC_REQUIRE([AC_PROG_INSTALL])dnl
+ AC_REQUIRE([AM_PROG_MKDIR_P])dnl defined by automake
+ AC_REQUIRE([AM_NLS])dnl
+
+ dnl Perform the following tests also if --disable-nls has been given,
+ dnl because they are needed for "make dist" to work.
+
+ dnl Search for GNU msgfmt in the PATH.
+ dnl The first test excludes Solaris msgfmt and early GNU msgfmt versions.
+ dnl The second test excludes FreeBSD msgfmt.
+ AM_PATH_PROG_WITH_TEST(MSGFMT, msgfmt,
+ [$ac_dir/$ac_word --statistics /dev/null >&]AS_MESSAGE_LOG_FD[ 2>&1 &&
+ (if $ac_dir/$ac_word --statistics /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi)],
+ :)
+ AC_PATH_PROG(GMSGFMT, gmsgfmt, $MSGFMT)
+
+ dnl Test whether it is GNU msgfmt >= 0.15.
+changequote(,)dnl
+ case `$MSGFMT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in
+ '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) MSGFMT_015=: ;;
+ *) MSGFMT_015=$MSGFMT ;;
+ esac
+changequote([,])dnl
+ AC_SUBST([MSGFMT_015])
+changequote(,)dnl
+ case `$GMSGFMT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in
+ '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) GMSGFMT_015=: ;;
+ *) GMSGFMT_015=$GMSGFMT ;;
+ esac
+changequote([,])dnl
+ AC_SUBST([GMSGFMT_015])
+
+ dnl Search for GNU xgettext 0.12 or newer in the PATH.
+ dnl The first test excludes Solaris xgettext and early GNU xgettext versions.
+ dnl The second test excludes FreeBSD xgettext.
+ AM_PATH_PROG_WITH_TEST(XGETTEXT, xgettext,
+ [$ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= /dev/null >&]AS_MESSAGE_LOG_FD[ 2>&1 &&
+ (if $ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi)],
+ :)
+ dnl Remove leftover from FreeBSD xgettext call.
+ rm -f messages.po
+
+ dnl Test whether it is GNU xgettext >= 0.15.
+changequote(,)dnl
+ case `$XGETTEXT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in
+ '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) XGETTEXT_015=: ;;
+ *) XGETTEXT_015=$XGETTEXT ;;
+ esac
+changequote([,])dnl
+ AC_SUBST([XGETTEXT_015])
+
+ dnl Search for GNU msgmerge 0.11 or newer in the PATH.
+ AM_PATH_PROG_WITH_TEST(MSGMERGE, msgmerge,
+ [$ac_dir/$ac_word --update -q /dev/null /dev/null >&]AS_MESSAGE_LOG_FD[ 2>&1], :)
+
+ dnl Installation directories.
+ dnl Autoconf >= 2.60 defines localedir. For older versions of autoconf, we
+ dnl have to define it here, so that it can be used in po/Makefile.
+ test -n "$localedir" || localedir='${datadir}/locale'
+ AC_SUBST([localedir])
+
+ AC_CONFIG_COMMANDS([po-directories], [[
+ for ac_file in $CONFIG_FILES; do
+ # Support "outfile[:infile[:infile...]]"
+ case "$ac_file" in
+ *:*) ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ esac
+ # PO directories have a Makefile.in generated from Makefile.in.in.
+ case "$ac_file" in */Makefile.in)
+ # Adjust a relative srcdir.
+ ac_dir=`echo "$ac_file"|sed 's%/[^/][^/]*$%%'`
+ ac_dir_suffix="/`echo "$ac_dir"|sed 's%^\./%%'`"
+ ac_dots=`echo "$ac_dir_suffix"|sed 's%/[^/]*%../%g'`
+ # In autoconf-2.13 it is called $ac_given_srcdir.
+ # In autoconf-2.50 it is called $srcdir.
+ test -n "$ac_given_srcdir" || ac_given_srcdir="$srcdir"
+ case "$ac_given_srcdir" in
+ .) top_srcdir=`echo $ac_dots|sed 's%/$%%'` ;;
+ /*) top_srcdir="$ac_given_srcdir" ;;
+ *) top_srcdir="$ac_dots$ac_given_srcdir" ;;
+ esac
+ # Treat a directory as a PO directory if and only if it has a
+ # POTFILES.in file. This allows packages to have multiple PO
+ # directories under different names or in different locations.
+ if test -f "$ac_given_srcdir/$ac_dir/POTFILES.in"; then
+ rm -f "$ac_dir/POTFILES"
+ test -n "$as_me" && echo "$as_me: creating $ac_dir/POTFILES" || echo "creating $ac_dir/POTFILES"
+ cat "$ac_given_srcdir/$ac_dir/POTFILES.in" | sed -e "/^#/d" -e "/^[ ]*\$/d" -e "s,.*, $top_srcdir/& \\\\," | sed -e "\$s/\(.*\) \\\\/\1/" > "$ac_dir/POTFILES"
+ POMAKEFILEDEPS="POTFILES.in"
+ # ALL_LINGUAS, POFILES, UPDATEPOFILES, DUMMYPOFILES, GMOFILES depend
+ # on $ac_dir but don't depend on user-specified configuration
+ # parameters.
+ if test -f "$ac_given_srcdir/$ac_dir/LINGUAS"; then
+ # The LINGUAS file contains the set of available languages.
+ if test -n "$OBSOLETE_ALL_LINGUAS"; then
+ test -n "$as_me" && echo "$as_me: setting ALL_LINGUAS in configure.in is obsolete" || echo "setting ALL_LINGUAS in configure.in is obsolete"
+ fi
+ ALL_LINGUAS_=`sed -e "/^#/d" -e "s/#.*//" "$ac_given_srcdir/$ac_dir/LINGUAS"`
+ # Hide the ALL_LINGUAS assigment from automake < 1.5.
+ eval 'ALL_LINGUAS''=$ALL_LINGUAS_'
+ POMAKEFILEDEPS="$POMAKEFILEDEPS LINGUAS"
+ else
+ # The set of available languages was given in configure.in.
+ # Hide the ALL_LINGUAS assigment from automake < 1.5.
+ eval 'ALL_LINGUAS''=$OBSOLETE_ALL_LINGUAS'
+ fi
+ # Compute POFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).po)
+ # Compute UPDATEPOFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(lang).po-update)
+ # Compute DUMMYPOFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(lang).nop)
+ # Compute GMOFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).gmo)
+ case "$ac_given_srcdir" in
+ .) srcdirpre= ;;
+ *) srcdirpre='$(srcdir)/' ;;
+ esac
+ POFILES=
+ UPDATEPOFILES=
+ DUMMYPOFILES=
+ GMOFILES=
+ for lang in $ALL_LINGUAS; do
+ POFILES="$POFILES $srcdirpre$lang.po"
+ UPDATEPOFILES="$UPDATEPOFILES $lang.po-update"
+ DUMMYPOFILES="$DUMMYPOFILES $lang.nop"
+ GMOFILES="$GMOFILES $srcdirpre$lang.gmo"
+ done
+ # CATALOGS depends on both $ac_dir and the user's LINGUAS
+ # environment variable.
+ INST_LINGUAS=
+ if test -n "$ALL_LINGUAS"; then
+ for presentlang in $ALL_LINGUAS; do
+ useit=no
+ if test "%UNSET%" != "$LINGUAS"; then
+ desiredlanguages="$LINGUAS"
+ else
+ desiredlanguages="$ALL_LINGUAS"
+ fi
+ for desiredlang in $desiredlanguages; do
+ # Use the presentlang catalog if desiredlang is
+ # a. equal to presentlang, or
+ # b. a variant of presentlang (because in this case,
+ # presentlang can be used as a fallback for messages
+ # which are not translated in the desiredlang catalog).
+ case "$desiredlang" in
+ "$presentlang"*) useit=yes;;
+ esac
+ done
+ if test $useit = yes; then
+ INST_LINGUAS="$INST_LINGUAS $presentlang"
+ fi
+ done
+ fi
+ CATALOGS=
+ if test -n "$INST_LINGUAS"; then
+ for lang in $INST_LINGUAS; do
+ CATALOGS="$CATALOGS $lang.gmo"
+ done
+ fi
+ test -n "$as_me" && echo "$as_me: creating $ac_dir/Makefile" || echo "creating $ac_dir/Makefile"
+ sed -e "/^POTFILES =/r $ac_dir/POTFILES" -e "/^# Makevars/r $ac_given_srcdir/$ac_dir/Makevars" -e "s|@POFILES@|$POFILES|g" -e "s|@UPDATEPOFILES@|$UPDATEPOFILES|g" -e "s|@DUMMYPOFILES@|$DUMMYPOFILES|g" -e "s|@GMOFILES@|$GMOFILES|g" -e "s|@CATALOGS@|$CATALOGS|g" -e "s|@POMAKEFILEDEPS@|$POMAKEFILEDEPS|g" "$ac_dir/Makefile.in" > "$ac_dir/Makefile"
+ for f in "$ac_given_srcdir/$ac_dir"/Rules-*; do
+ if test -f "$f"; then
+ case "$f" in
+ *.orig | *.bak | *~) ;;
+ *) cat "$f" >> "$ac_dir/Makefile" ;;
+ esac
+ fi
+ done
+ fi
+ ;;
+ esac
+ done]],
+ [# Capture the value of obsolete ALL_LINGUAS because we need it to compute
+ # POFILES, UPDATEPOFILES, DUMMYPOFILES, GMOFILES, CATALOGS. But hide it
+ # from automake < 1.5.
+ eval 'OBSOLETE_ALL_LINGUAS''="$ALL_LINGUAS"'
+ # Capture the value of LINGUAS because we need it to compute CATALOGS.
+ LINGUAS="${LINGUAS-%UNSET%}"
+ ])
+])
+m4trace:m4/po.m4:210: -1- AC_DEFUN([AM_POSTPROCESS_PO_MAKEFILE], [
+ # When this code is run, in config.status, two variables have already been
+ # set:
+ # - OBSOLETE_ALL_LINGUAS is the value of LINGUAS set in configure.in,
+ # - LINGUAS is the value of the environment variable LINGUAS at configure
+ # time.
+
+changequote(,)dnl
+ # Adjust a relative srcdir.
+ ac_dir=`echo "$ac_file"|sed 's%/[^/][^/]*$%%'`
+ ac_dir_suffix="/`echo "$ac_dir"|sed 's%^\./%%'`"
+ ac_dots=`echo "$ac_dir_suffix"|sed 's%/[^/]*%../%g'`
+ # In autoconf-2.13 it is called $ac_given_srcdir.
+ # In autoconf-2.50 it is called $srcdir.
+ test -n "$ac_given_srcdir" || ac_given_srcdir="$srcdir"
+ case "$ac_given_srcdir" in
+ .) top_srcdir=`echo $ac_dots|sed 's%/$%%'` ;;
+ /*) top_srcdir="$ac_given_srcdir" ;;
+ *) top_srcdir="$ac_dots$ac_given_srcdir" ;;
+ esac
+
+ # Find a way to echo strings without interpreting backslash.
+ if test "X`(echo '\t') 2>/dev/null`" = 'X\t'; then
+ gt_echo='echo'
+ else
+ if test "X`(printf '%s\n' '\t') 2>/dev/null`" = 'X\t'; then
+ gt_echo='printf %s\n'
+ else
+ echo_func () {
+ cat <<EOT
+$*
+EOT
+ }
+ gt_echo='echo_func'
+ fi
+ fi
+
+ # A sed script that extracts the value of VARIABLE from a Makefile.
+ sed_x_variable='
+# Test if the hold space is empty.
+x
+s/P/P/
+x
+ta
+# Yes it was empty. Look if we have the expected variable definition.
+/^[ ]*VARIABLE[ ]*=/{
+ # Seen the first line of the variable definition.
+ s/^[ ]*VARIABLE[ ]*=//
+ ba
+}
+bd
+:a
+# Here we are processing a line from the variable definition.
+# Remove comment, more precisely replace it with a space.
+s/#.*$/ /
+# See if the line ends in a backslash.
+tb
+:b
+s/\\$//
+# Print the line, without the trailing backslash.
+p
+tc
+# There was no trailing backslash. The end of the variable definition is
+# reached. Clear the hold space.
+s/^.*$//
+x
+bd
+:c
+# A trailing backslash means that the variable definition continues in the
+# next line. Put a nonempty string into the hold space to indicate this.
+s/^.*$/P/
+x
+:d
+'
+changequote([,])dnl
+
+ # Set POTFILES to the value of the Makefile variable POTFILES.
+ sed_x_POTFILES=`$gt_echo "$sed_x_variable" | sed -e '/^ *#/d' -e 's/VARIABLE/POTFILES/g'`
+ POTFILES=`sed -n -e "$sed_x_POTFILES" < "$ac_file"`
+ # Compute POTFILES_DEPS as
+ # $(foreach file, $(POTFILES), $(top_srcdir)/$(file))
+ POTFILES_DEPS=
+ for file in $POTFILES; do
+ POTFILES_DEPS="$POTFILES_DEPS "'$(top_srcdir)/'"$file"
+ done
+ POMAKEFILEDEPS=""
+
+ if test -n "$OBSOLETE_ALL_LINGUAS"; then
+ test -n "$as_me" && echo "$as_me: setting ALL_LINGUAS in configure.in is obsolete" || echo "setting ALL_LINGUAS in configure.in is obsolete"
+ fi
+ if test -f "$ac_given_srcdir/$ac_dir/LINGUAS"; then
+ # The LINGUAS file contains the set of available languages.
+ ALL_LINGUAS_=`sed -e "/^#/d" -e "s/#.*//" "$ac_given_srcdir/$ac_dir/LINGUAS"`
+ POMAKEFILEDEPS="$POMAKEFILEDEPS LINGUAS"
+ else
+ # Set ALL_LINGUAS to the value of the Makefile variable LINGUAS.
+ sed_x_LINGUAS=`$gt_echo "$sed_x_variable" | sed -e '/^ *#/d' -e 's/VARIABLE/LINGUAS/g'`
+ ALL_LINGUAS_=`sed -n -e "$sed_x_LINGUAS" < "$ac_file"`
+ fi
+ # Hide the ALL_LINGUAS assigment from automake < 1.5.
+ eval 'ALL_LINGUAS''=$ALL_LINGUAS_'
+ # Compute POFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).po)
+ # Compute UPDATEPOFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(lang).po-update)
+ # Compute DUMMYPOFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(lang).nop)
+ # Compute GMOFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).gmo)
+ # Compute PROPERTIESFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(top_srcdir)/$(DOMAIN)_$(lang).properties)
+ # Compute CLASSFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(top_srcdir)/$(DOMAIN)_$(lang).class)
+ # Compute QMFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).qm)
+ # Compute MSGFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(frob $(lang)).msg)
+ # Compute RESOURCESDLLFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(frob $(lang))/$(DOMAIN).resources.dll)
+ case "$ac_given_srcdir" in
+ .) srcdirpre= ;;
+ *) srcdirpre='$(srcdir)/' ;;
+ esac
+ POFILES=
+ UPDATEPOFILES=
+ DUMMYPOFILES=
+ GMOFILES=
+ PROPERTIESFILES=
+ CLASSFILES=
+ QMFILES=
+ MSGFILES=
+ RESOURCESDLLFILES=
+ for lang in $ALL_LINGUAS; do
+ POFILES="$POFILES $srcdirpre$lang.po"
+ UPDATEPOFILES="$UPDATEPOFILES $lang.po-update"
+ DUMMYPOFILES="$DUMMYPOFILES $lang.nop"
+ GMOFILES="$GMOFILES $srcdirpre$lang.gmo"
+ PROPERTIESFILES="$PROPERTIESFILES \$(top_srcdir)/\$(DOMAIN)_$lang.properties"
+ CLASSFILES="$CLASSFILES \$(top_srcdir)/\$(DOMAIN)_$lang.class"
+ QMFILES="$QMFILES $srcdirpre$lang.qm"
+ frobbedlang=`echo $lang | sed -e 's/\..*$//' -e 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/'`
+ MSGFILES="$MSGFILES $srcdirpre$frobbedlang.msg"
+ frobbedlang=`echo $lang | sed -e 's/_/-/g' -e 's/^sr-CS/sr-SP/' -e 's/@latin$/-Latn/' -e 's/@cyrillic$/-Cyrl/' -e 's/^sr-SP$/sr-SP-Latn/' -e 's/^uz-UZ$/uz-UZ-Latn/'`
+ RESOURCESDLLFILES="$RESOURCESDLLFILES $srcdirpre$frobbedlang/\$(DOMAIN).resources.dll"
+ done
+ # CATALOGS depends on both $ac_dir and the user's LINGUAS
+ # environment variable.
+ INST_LINGUAS=
+ if test -n "$ALL_LINGUAS"; then
+ for presentlang in $ALL_LINGUAS; do
+ useit=no
+ if test "%UNSET%" != "$LINGUAS"; then
+ desiredlanguages="$LINGUAS"
+ else
+ desiredlanguages="$ALL_LINGUAS"
+ fi
+ for desiredlang in $desiredlanguages; do
+ # Use the presentlang catalog if desiredlang is
+ # a. equal to presentlang, or
+ # b. a variant of presentlang (because in this case,
+ # presentlang can be used as a fallback for messages
+ # which are not translated in the desiredlang catalog).
+ case "$desiredlang" in
+ "$presentlang"*) useit=yes;;
+ esac
+ done
+ if test $useit = yes; then
+ INST_LINGUAS="$INST_LINGUAS $presentlang"
+ fi
+ done
+ fi
+ CATALOGS=
+ JAVACATALOGS=
+ QTCATALOGS=
+ TCLCATALOGS=
+ CSHARPCATALOGS=
+ if test -n "$INST_LINGUAS"; then
+ for lang in $INST_LINGUAS; do
+ CATALOGS="$CATALOGS $lang.gmo"
+ JAVACATALOGS="$JAVACATALOGS \$(DOMAIN)_$lang.properties"
+ QTCATALOGS="$QTCATALOGS $lang.qm"
+ frobbedlang=`echo $lang | sed -e 's/\..*$//' -e 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/'`
+ TCLCATALOGS="$TCLCATALOGS $frobbedlang.msg"
+ frobbedlang=`echo $lang | sed -e 's/_/-/g' -e 's/^sr-CS/sr-SP/' -e 's/@latin$/-Latn/' -e 's/@cyrillic$/-Cyrl/' -e 's/^sr-SP$/sr-SP-Latn/' -e 's/^uz-UZ$/uz-UZ-Latn/'`
+ CSHARPCATALOGS="$CSHARPCATALOGS $frobbedlang/\$(DOMAIN).resources.dll"
+ done
+ fi
+
+ sed -e "s|@POTFILES_DEPS@|$POTFILES_DEPS|g" -e "s|@POFILES@|$POFILES|g" -e "s|@UPDATEPOFILES@|$UPDATEPOFILES|g" -e "s|@DUMMYPOFILES@|$DUMMYPOFILES|g" -e "s|@GMOFILES@|$GMOFILES|g" -e "s|@PROPERTIESFILES@|$PROPERTIESFILES|g" -e "s|@CLASSFILES@|$CLASSFILES|g" -e "s|@QMFILES@|$QMFILES|g" -e "s|@MSGFILES@|$MSGFILES|g" -e "s|@RESOURCESDLLFILES@|$RESOURCESDLLFILES|g" -e "s|@CATALOGS@|$CATALOGS|g" -e "s|@JAVACATALOGS@|$JAVACATALOGS|g" -e "s|@QTCATALOGS@|$QTCATALOGS|g" -e "s|@TCLCATALOGS@|$TCLCATALOGS|g" -e "s|@CSHARPCATALOGS@|$CSHARPCATALOGS|g" -e 's,^#distdir:,distdir:,' < "$ac_file" > "$ac_file.tmp"
+ if grep -l '@TCLCATALOGS@' "$ac_file" > /dev/null; then
+ # Add dependencies that cannot be formulated as a simple suffix rule.
+ for lang in $ALL_LINGUAS; do
+ frobbedlang=`echo $lang | sed -e 's/\..*$//' -e 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/'`
+ cat >> "$ac_file.tmp" <<EOF
+$frobbedlang.msg: $lang.po
+ @echo "\$(MSGFMT) -c --tcl -d \$(srcdir) -l $lang $srcdirpre$lang.po"; \
+ \$(MSGFMT) -c --tcl -d "\$(srcdir)" -l $lang $srcdirpre$lang.po || { rm -f "\$(srcdir)/$frobbedlang.msg"; exit 1; }
+EOF
+ done
+ fi
+ if grep -l '@CSHARPCATALOGS@' "$ac_file" > /dev/null; then
+ # Add dependencies that cannot be formulated as a simple suffix rule.
+ for lang in $ALL_LINGUAS; do
+ frobbedlang=`echo $lang | sed -e 's/_/-/g' -e 's/^sr-CS/sr-SP/' -e 's/@latin$/-Latn/' -e 's/@cyrillic$/-Cyrl/' -e 's/^sr-SP$/sr-SP-Latn/' -e 's/^uz-UZ$/uz-UZ-Latn/'`
+ cat >> "$ac_file.tmp" <<EOF
+$frobbedlang/\$(DOMAIN).resources.dll: $lang.po
+ @echo "\$(MSGFMT) -c --csharp -d \$(srcdir) -l $lang $srcdirpre$lang.po -r \$(DOMAIN)"; \
+ \$(MSGFMT) -c --csharp -d "\$(srcdir)" -l $lang $srcdirpre$lang.po -r "\$(DOMAIN)" || { rm -f "\$(srcdir)/$frobbedlang.msg"; exit 1; }
+EOF
+ done
+ fi
+ if test -n "$POMAKEFILEDEPS"; then
+ cat >> "$ac_file.tmp" <<EOF
+Makefile: $POMAKEFILEDEPS
+EOF
+ fi
+ mv "$ac_file.tmp" "$ac_file"
+])
+m4trace:m4/printf-posix.m4:11: -1- AC_DEFUN([gt_PRINTF_POSIX], [
+ AC_REQUIRE([AC_PROG_CC])
+ AC_CACHE_CHECK([whether printf() supports POSIX/XSI format strings],
+ gt_cv_func_printf_posix,
+ [
+ AC_TRY_RUN([
+#include <stdio.h>
+#include <string.h>
+/* The string "%2$d %1$d", with dollar characters protected from the shell's
+ dollar expansion (possibly an autoconf bug). */
+static char format[] = { '%', '2', '$', 'd', ' ', '%', '1', '$', 'd', '\0' };
+static char buf[100];
+int main ()
+{
+ sprintf (buf, format, 33, 55);
+ return (strcmp (buf, "55 33") != 0);
+}], gt_cv_func_printf_posix=yes, gt_cv_func_printf_posix=no,
+ [
+ AC_EGREP_CPP(notposix, [
+#if defined __NetBSD__ || defined _MSC_VER || defined __MINGW32__ || defined __CYGWIN__
+ notposix
+#endif
+ ], gt_cv_func_printf_posix="guessing no",
+ gt_cv_func_printf_posix="guessing yes")
+ ])
+ ])
+ case $gt_cv_func_printf_posix in
+ *yes)
+ AC_DEFINE(HAVE_POSIX_PRINTF, 1,
+ [Define if your printf() function supports format strings with positions.])
+ ;;
+ esac
+])
+m4trace:m4/progtest.m4:25: -1- AC_DEFUN([AM_PATH_PROG_WITH_TEST], [
+# Prepare PATH_SEPARATOR.
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+# Find out how to test for executable files. Don't use a zero-byte file,
+# as systems may use methods other than mode bits to determine executability.
+cat >conf$$.file <<_ASEOF
+#! /bin/sh
+exit 0
+_ASEOF
+chmod +x conf$$.file
+if test -x conf$$.file >/dev/null 2>&1; then
+ ac_executable_p="test -x"
+else
+ ac_executable_p="test -f"
+fi
+rm -f conf$$.file
+
+# Extract the first word of "$2", so it can be a program name with args.
+set dummy $2; ac_word=[$]2
+AC_MSG_CHECKING([for $ac_word])
+AC_CACHE_VAL(ac_cv_path_$1,
+[case "[$]$1" in
+ [[\\/]]* | ?:[[\\/]]*)
+ ac_cv_path_$1="[$]$1" # Let the user override the test with a path.
+ ;;
+ *)
+ ac_save_IFS="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in ifelse([$5], , $PATH, [$5]); do
+ IFS="$ac_save_IFS"
+ test -z "$ac_dir" && ac_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $ac_executable_p "$ac_dir/$ac_word$ac_exec_ext"; then
+ echo "$as_me: trying $ac_dir/$ac_word..." >&AS_MESSAGE_LOG_FD
+ if [$3]; then
+ ac_cv_path_$1="$ac_dir/$ac_word$ac_exec_ext"
+ break 2
+ fi
+ fi
+ done
+ done
+ IFS="$ac_save_IFS"
+dnl If no 4th arg is given, leave the cache variable unset,
+dnl so AC_PATH_PROGS will keep looking.
+ifelse([$4], , , [ test -z "[$]ac_cv_path_$1" && ac_cv_path_$1="$4"
+])dnl
+ ;;
+esac])dnl
+$1="$ac_cv_path_$1"
+if test ifelse([$4], , [-n "[$]$1"], ["[$]$1" != "$4"]); then
+ AC_MSG_RESULT([$]$1)
+else
+ AC_MSG_RESULT(no)
+fi
+AC_SUBST($1)dnl
+])
+m4trace:m4/size_max.m4:9: -1- AC_DEFUN([gl_SIZE_MAX], [
+ AC_CHECK_HEADERS(stdint.h)
+ dnl First test whether the system already has SIZE_MAX.
+ AC_MSG_CHECKING([for SIZE_MAX])
+ AC_CACHE_VAL([gl_cv_size_max], [
+ gl_cv_size_max=
+ AC_EGREP_CPP([Found it], [
+#include <limits.h>
+#if HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#ifdef SIZE_MAX
+Found it
+#endif
+], gl_cv_size_max=yes)
+ if test -z "$gl_cv_size_max"; then
+ dnl Define it ourselves. Here we assume that the type 'size_t' is not wider
+ dnl than the type 'unsigned long'. Try hard to find a definition that can
+ dnl be used in a preprocessor #if, i.e. doesn't contain a cast.
+ _AC_COMPUTE_INT([sizeof (size_t) * CHAR_BIT - 1], size_t_bits_minus_1,
+ [#include <stddef.h>
+#include <limits.h>], size_t_bits_minus_1=)
+ _AC_COMPUTE_INT([sizeof (size_t) <= sizeof (unsigned int)], fits_in_uint,
+ [#include <stddef.h>], fits_in_uint=)
+ if test -n "$size_t_bits_minus_1" && test -n "$fits_in_uint"; then
+ if test $fits_in_uint = 1; then
+ dnl Even though SIZE_MAX fits in an unsigned int, it must be of type
+ dnl 'unsigned long' if the type 'size_t' is the same as 'unsigned long'.
+ AC_TRY_COMPILE([#include <stddef.h>
+ extern size_t foo;
+ extern unsigned long foo;
+ ], [], fits_in_uint=0)
+ fi
+ dnl We cannot use 'expr' to simplify this expression, because 'expr'
+ dnl works only with 'long' integers in the host environment, while we
+ dnl might be cross-compiling from a 32-bit platform to a 64-bit platform.
+ if test $fits_in_uint = 1; then
+ gl_cv_size_max="(((1U << $size_t_bits_minus_1) - 1) * 2 + 1)"
+ else
+ gl_cv_size_max="(((1UL << $size_t_bits_minus_1) - 1) * 2 + 1)"
+ fi
+ else
+ dnl Shouldn't happen, but who knows...
+ gl_cv_size_max='((size_t)~(size_t)0)'
+ fi
+ fi
+ ])
+ AC_MSG_RESULT([$gl_cv_size_max])
+ if test "$gl_cv_size_max" != yes; then
+ AC_DEFINE_UNQUOTED([SIZE_MAX], [$gl_cv_size_max],
+ [Define as the maximum value of type 'size_t', if the system doesn't define it.])
+ fi
+])
+m4trace:m4/stdint_h.m4:12: -1- AC_DEFUN([gl_AC_HEADER_STDINT_H], [
+ AC_CACHE_CHECK([for stdint.h], gl_cv_header_stdint_h,
+ [AC_TRY_COMPILE(
+ [#include <sys/types.h>
+#include <stdint.h>],
+ [uintmax_t i = (uintmax_t) -1; return !i;],
+ gl_cv_header_stdint_h=yes,
+ gl_cv_header_stdint_h=no)])
+ if test $gl_cv_header_stdint_h = yes; then
+ AC_DEFINE_UNQUOTED(HAVE_STDINT_H_WITH_UINTMAX, 1,
+ [Define if <stdint.h> exists, doesn't clash with <sys/types.h>,
+ and declares uintmax_t. ])
+ fi
+])
+m4trace:m4/uintmax_t.m4:14: -1- AC_DEFUN([gl_AC_TYPE_UINTMAX_T], [
+ AC_REQUIRE([gl_AC_HEADER_INTTYPES_H])
+ AC_REQUIRE([gl_AC_HEADER_STDINT_H])
+ if test $gl_cv_header_inttypes_h = no && test $gl_cv_header_stdint_h = no; then
+ AC_REQUIRE([gl_AC_TYPE_UNSIGNED_LONG_LONG])
+ test $ac_cv_type_unsigned_long_long = yes \
+ && ac_type='unsigned long long' \
+ || ac_type='unsigned long'
+ AC_DEFINE_UNQUOTED(uintmax_t, $ac_type,
+ [Define to unsigned long or unsigned long long
+ if <stdint.h> and <inttypes.h> don't define.])
+ else
+ AC_DEFINE(HAVE_UINTMAX_T, 1,
+ [Define if you have the 'uintmax_t' type in <stdint.h> or <inttypes.h>.])
+ fi
+])
+m4trace:m4/ulonglong.m4:18: -1- AC_DEFUN([AC_TYPE_UNSIGNED_LONG_LONG_INT], [
+ AC_CACHE_CHECK([for unsigned long long int],
+ [ac_cv_type_unsigned_long_long_int],
+ [AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[unsigned long long int ull = 18446744073709551615ULL;
+ typedef int a[(18446744073709551615ULL <= (unsigned long long int) -1
+ ? 1 : -1)];
+ int i = 63;]],
+ [[unsigned long long int ullmax = 18446744073709551615ull;
+ return (ull << 63 | ull >> 63 | ull << i | ull >> i
+ | ullmax / ull | ullmax % ull);]])],
+ [ac_cv_type_unsigned_long_long_int=yes],
+ [ac_cv_type_unsigned_long_long_int=no])])
+ if test $ac_cv_type_unsigned_long_long_int = yes; then
+ AC_DEFINE([HAVE_UNSIGNED_LONG_LONG_INT], 1,
+ [Define to 1 if the system has the type `unsigned long long int'.])
+ fi
+])
+m4trace:m4/ulonglong.m4:40: -1- AC_DEFUN([gl_AC_TYPE_UNSIGNED_LONG_LONG], [
+ AC_REQUIRE([AC_TYPE_UNSIGNED_LONG_LONG_INT])
+ ac_cv_type_unsigned_long_long=$ac_cv_type_unsigned_long_long_int
+ if test $ac_cv_type_unsigned_long_long = yes; then
+ AC_DEFINE(HAVE_UNSIGNED_LONG_LONG, 1,
+ [Define if you have the 'unsigned long long' type.])
+ fi
+])
+m4trace:m4/visibility.m4:23: -1- AC_DEFUN([gl_VISIBILITY], [
+ AC_REQUIRE([AC_PROG_CC])
+ CFLAG_VISIBILITY=
+ HAVE_VISIBILITY=0
+ if test -n "$GCC"; then
+ AC_MSG_CHECKING([for simple visibility declarations])
+ AC_CACHE_VAL(gl_cv_cc_visibility, [
+ gl_save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS -fvisibility=hidden"
+ AC_TRY_COMPILE(
+ [extern __attribute__((__visibility__("hidden"))) int hiddenvar;
+ extern __attribute__((__visibility__("default"))) int exportedvar;
+ extern __attribute__((__visibility__("hidden"))) int hiddenfunc (void);
+ extern __attribute__((__visibility__("default"))) int exportedfunc (void);],
+ [],
+ gl_cv_cc_visibility=yes,
+ gl_cv_cc_visibility=no)
+ CFLAGS="$gl_save_CFLAGS"])
+ AC_MSG_RESULT([$gl_cv_cc_visibility])
+ if test $gl_cv_cc_visibility = yes; then
+ CFLAG_VISIBILITY="-fvisibility=hidden"
+ HAVE_VISIBILITY=1
+ fi
+ fi
+ AC_SUBST([CFLAG_VISIBILITY])
+ AC_SUBST([HAVE_VISIBILITY])
+ AC_DEFINE_UNQUOTED([HAVE_VISIBILITY], [$HAVE_VISIBILITY],
+ [Define to 1 or 0, depending whether the compiler supports simple visibility declarations.])
+])
+m4trace:m4/wchar_t.m4:11: -1- AC_DEFUN([gt_TYPE_WCHAR_T], [
+ AC_CACHE_CHECK([for wchar_t], gt_cv_c_wchar_t,
+ [AC_TRY_COMPILE([#include <stddef.h>
+ wchar_t foo = (wchar_t)'\0';], ,
+ gt_cv_c_wchar_t=yes, gt_cv_c_wchar_t=no)])
+ if test $gt_cv_c_wchar_t = yes; then
+ AC_DEFINE(HAVE_WCHAR_T, 1, [Define if you have the 'wchar_t' type.])
+ fi
+])
+m4trace:m4/wint_t.m4:11: -1- AC_DEFUN([gt_TYPE_WINT_T], [
+ AC_CACHE_CHECK([for wint_t], gt_cv_c_wint_t,
+ [AC_TRY_COMPILE([#include <wchar.h>
+ wint_t foo = (wchar_t)'\0';], ,
+ gt_cv_c_wint_t=yes, gt_cv_c_wint_t=no)])
+ if test $gt_cv_c_wint_t = yes; then
+ AC_DEFINE(HAVE_WINT_T, 1, [Define if you have the 'wint_t' type.])
+ fi
+])
+m4trace:m4/xsize.m4:7: -1- AC_DEFUN([gl_XSIZE], [
+ dnl Prerequisites of lib/xsize.h.
+ AC_REQUIRE([gl_SIZE_MAX])
+ AC_REQUIRE([AC_C_INLINE])
+ AC_CHECK_HEADERS(stdint.h)
+])
+m4trace:configure.ac:20: -3- m4_include([VERSION])
+m4trace:configure.ac:20: -1- m4_pattern_forbid([^_?A[CHUM]_])
+m4trace:configure.ac:20: -1- m4_pattern_forbid([_AC_])
+m4trace:configure.ac:20: -1- m4_pattern_forbid([^LIBOBJS$], [do not use LIBOBJS directly, use AC_LIBOBJ (see section `AC_LIBOBJ vs LIBOBJS'])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^AS_FLAGS$])
+m4trace:configure.ac:20: -1- m4_pattern_forbid([^_?m4_])
+m4trace:configure.ac:20: -1- m4_pattern_forbid([^dnl$])
+m4trace:configure.ac:20: -1- m4_pattern_forbid([^_?AS_])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^SHELL$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PATH_SEPARATOR$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_NAME$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_TARNAME$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_VERSION$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_STRING$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_BUGREPORT$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_URL$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^exec_prefix$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^prefix$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^program_transform_name$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^bindir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^sbindir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^libexecdir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^datarootdir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^datadir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^sysconfdir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^sharedstatedir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^localstatedir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^includedir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^oldincludedir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^docdir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^infodir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^htmldir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^dvidir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^pdfdir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^psdir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^libdir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^localedir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^mandir$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_NAME$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_TARNAME$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_VERSION$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_STRING$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_BUGREPORT$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^PACKAGE_URL$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^DEFS$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^ECHO_C$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^ECHO_N$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^ECHO_T$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^LIBS$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^build_alias$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^host_alias$])
+m4trace:configure.ac:20: -1- m4_pattern_allow([^target_alias$])
+m4trace:configure.ac:25: -1- AM_INIT_AUTOMAKE([foreign nostdinc])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^AM_[A-Z]+FLAGS$])
+m4trace:configure.ac:25: -1- AM_SET_CURRENT_AUTOMAKE_VERSION
+m4trace:configure.ac:25: -1- AM_AUTOMAKE_VERSION([1.11.1])
+m4trace:configure.ac:25: -1- _AM_AUTOCONF_VERSION([2.68])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^INSTALL_PROGRAM$])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^INSTALL_SCRIPT$])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^INSTALL_DATA$])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^am__isrc$])
+m4trace:configure.ac:25: -1- _AM_SUBST_NOTMAKE([am__isrc])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^CYGPATH_W$])
+m4trace:configure.ac:25: -1- _AM_SET_OPTIONS([foreign nostdinc])
+m4trace:configure.ac:25: -1- _AM_SET_OPTION([foreign])
+m4trace:configure.ac:25: -2- _AM_MANGLE_OPTION([foreign])
+m4trace:configure.ac:25: -1- _AM_SET_OPTION([nostdinc])
+m4trace:configure.ac:25: -2- _AM_MANGLE_OPTION([nostdinc])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^PACKAGE$])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^VERSION$])
+m4trace:configure.ac:25: -1- _AM_IF_OPTION([no-define], [], [AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package])
+ AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])
+m4trace:configure.ac:25: -2- _AM_MANGLE_OPTION([no-define])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^PACKAGE$])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^VERSION$])
+m4trace:configure.ac:25: -1- AM_SANITY_CHECK
+m4trace:configure.ac:25: -1- AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}])
+m4trace:configure.ac:25: -1- AM_MISSING_HAS_RUN
+m4trace:configure.ac:25: -1- AM_AUX_DIR_EXPAND
+m4trace:configure.ac:25: -1- m4_pattern_allow([^ACLOCAL$])
+m4trace:configure.ac:25: -1- AM_MISSING_PROG([AUTOCONF], [autoconf])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^AUTOCONF$])
+m4trace:configure.ac:25: -1- AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^AUTOMAKE$])
+m4trace:configure.ac:25: -1- AM_MISSING_PROG([AUTOHEADER], [autoheader])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^AUTOHEADER$])
+m4trace:configure.ac:25: -1- AM_MISSING_PROG([MAKEINFO], [makeinfo])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^MAKEINFO$])
+m4trace:configure.ac:25: -1- AM_PROG_INSTALL_SH
+m4trace:configure.ac:25: -1- m4_pattern_allow([^install_sh$])
+m4trace:configure.ac:25: -1- AM_PROG_INSTALL_STRIP
+m4trace:configure.ac:25: -1- m4_pattern_allow([^STRIP$])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^INSTALL_STRIP_PROGRAM$])
+m4trace:configure.ac:25: -1- AM_PROG_MKDIR_P
+m4trace:configure.ac:25: -1- m4_pattern_allow([^MKDIR_P$])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^mkdir_p$])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^AWK$])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^SET_MAKE$])
+m4trace:configure.ac:25: -1- AM_SET_LEADING_DOT
+m4trace:configure.ac:25: -1- m4_pattern_allow([^am__leading_dot$])
+m4trace:configure.ac:25: -1- _AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])],
+ [_AM_PROG_TAR([v7])])])
+m4trace:configure.ac:25: -2- _AM_MANGLE_OPTION([tar-ustar])
+m4trace:configure.ac:25: -1- _AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], [_AM_PROG_TAR([v7])])
+m4trace:configure.ac:25: -2- _AM_MANGLE_OPTION([tar-pax])
+m4trace:configure.ac:25: -1- _AM_PROG_TAR([v7])
+m4trace:configure.ac:25: -1- AM_MISSING_PROG([AMTAR], [tar])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^AMTAR$])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^am__tar$])
+m4trace:configure.ac:25: -1- m4_pattern_allow([^am__untar$])
+m4trace:configure.ac:25: -1- _AM_IF_OPTION([no-dependencies], [], [AC_PROVIDE_IFELSE([AC_PROG_CC],
+ [_AM_DEPENDENCIES(CC)],
+ [define([AC_PROG_CC],
+ defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_CXX],
+ [_AM_DEPENDENCIES(CXX)],
+ [define([AC_PROG_CXX],
+ defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_OBJC],
+ [_AM_DEPENDENCIES(OBJC)],
+ [define([AC_PROG_OBJC],
+ defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])dnl
+])
+m4trace:configure.ac:25: -2- _AM_MANGLE_OPTION([no-dependencies])
+m4trace:configure.ac:25: -1- _AM_IF_OPTION([silent-rules], [AC_REQUIRE([AM_SILENT_RULES])])
+m4trace:configure.ac:25: -2- _AM_MANGLE_OPTION([silent-rules])
+m4trace:configure.ac:27: -1- AM_MAINTAINER_MODE
+m4trace:configure.ac:27: -1- AM_CONDITIONAL([MAINTAINER_MODE], [test $USE_MAINTAINER_MODE = yes])
+m4trace:configure.ac:27: -1- m4_pattern_allow([^MAINTAINER_MODE_TRUE$])
+m4trace:configure.ac:27: -1- m4_pattern_allow([^MAINTAINER_MODE_FALSE$])
+m4trace:configure.ac:27: -1- _AM_SUBST_NOTMAKE([MAINTAINER_MODE_TRUE])
+m4trace:configure.ac:27: -1- _AM_SUBST_NOTMAKE([MAINTAINER_MODE_FALSE])
+m4trace:configure.ac:27: -1- m4_pattern_allow([^MAINT$])
+m4trace:configure.ac:29: -1- m4_pattern_allow([^build$])
+m4trace:configure.ac:29: -1- m4_pattern_allow([^build_cpu$])
+m4trace:configure.ac:29: -1- m4_pattern_allow([^build_vendor$])
+m4trace:configure.ac:29: -1- m4_pattern_allow([^build_os$])
+m4trace:configure.ac:29: -1- m4_pattern_allow([^host$])
+m4trace:configure.ac:29: -1- m4_pattern_allow([^host_cpu$])
+m4trace:configure.ac:29: -1- m4_pattern_allow([^host_vendor$])
+m4trace:configure.ac:29: -1- m4_pattern_allow([^host_os$])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^CC$])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^CFLAGS$])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^LDFLAGS$])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^LIBS$])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^CPPFLAGS$])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^CC$])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^CC$])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^CC$])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^CC$])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^ac_ct_CC$])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^EXEEXT$])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^OBJEXT$])
+m4trace:configure.ac:40: -1- _AM_DEPENDENCIES([CC])
+m4trace:configure.ac:40: -1- AM_SET_DEPDIR
+m4trace:configure.ac:40: -1- m4_pattern_allow([^DEPDIR$])
+m4trace:configure.ac:40: -1- AM_OUTPUT_DEPENDENCY_COMMANDS
+m4trace:configure.ac:40: -1- AM_MAKE_INCLUDE
+m4trace:configure.ac:40: -1- m4_pattern_allow([^am__include$])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^am__quote$])
+m4trace:configure.ac:40: -1- AM_DEP_TRACK
+m4trace:configure.ac:40: -1- AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^AMDEP_TRUE$])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^AMDEP_FALSE$])
+m4trace:configure.ac:40: -1- _AM_SUBST_NOTMAKE([AMDEP_TRUE])
+m4trace:configure.ac:40: -1- _AM_SUBST_NOTMAKE([AMDEP_FALSE])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^AMDEPBACKSLASH$])
+m4trace:configure.ac:40: -1- _AM_SUBST_NOTMAKE([AMDEPBACKSLASH])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^CCDEPMODE$])
+m4trace:configure.ac:40: -1- AM_CONDITIONAL([am__fastdepCC], [
+ test "x$enable_dependency_tracking" != xno \
+ && test "$am_cv_CC_dependencies_compiler_type" = gcc3])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^am__fastdepCC_TRUE$])
+m4trace:configure.ac:40: -1- m4_pattern_allow([^am__fastdepCC_FALSE$])
+m4trace:configure.ac:40: -1- _AM_SUBST_NOTMAKE([am__fastdepCC_TRUE])
+m4trace:configure.ac:40: -1- _AM_SUBST_NOTMAKE([am__fastdepCC_FALSE])
+m4trace:configure.ac:43: -1- m4_pattern_allow([^SET_MAKE$])
+m4trace:configure.ac:44: -1- m4_pattern_allow([^LN_S$])
+m4trace:configure.ac:45: -1- m4_pattern_allow([^AWK$])
+m4trace:configure.ac:46: -1- m4_pattern_allow([^RANLIB$])
+m4trace:configure.ac:47: -1- AC_PROG_LIBTOOL
+m4trace:configure.ac:47: -1- _m4_warn([obsolete], [The macro `AC_PROG_LIBTOOL' is obsolete.
+You should run autoupdate.], [m4/libtool.m4:102: AC_PROG_LIBTOOL is expanded from...
+configure.ac:47: the top level])
+m4trace:configure.ac:47: -1- LT_INIT
+m4trace:configure.ac:47: -1- m4_pattern_forbid([^_?LT_[A-Z_]+$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])
+m4trace:configure.ac:47: -1- LTOPTIONS_VERSION
+m4trace:configure.ac:47: -1- LTSUGAR_VERSION
+m4trace:configure.ac:47: -1- LTVERSION_VERSION
+m4trace:configure.ac:47: -1- LTOBSOLETE_VERSION
+m4trace:configure.ac:47: -1- _LT_PROG_LTMAIN
+m4trace:configure.ac:47: -1- m4_pattern_allow([^LIBTOOL$])
+m4trace:configure.ac:47: -1- LT_PATH_LD
+m4trace:configure.ac:47: -1- m4_pattern_allow([^SED$])
+m4trace:configure.ac:47: -1- AC_PROG_EGREP
+m4trace:configure.ac:47: -1- m4_pattern_allow([^GREP$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^EGREP$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^FGREP$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^GREP$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^LD$])
+m4trace:configure.ac:47: -1- LT_PATH_NM
+m4trace:configure.ac:47: -1- m4_pattern_allow([^DUMPBIN$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^ac_ct_DUMPBIN$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^DUMPBIN$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^NM$])
+m4trace:configure.ac:47: -1- LT_CMD_MAX_LEN
+m4trace:configure.ac:47: -1- m4_pattern_allow([^OBJDUMP$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^OBJDUMP$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^AR$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^STRIP$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^RANLIB$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([LT_OBJDIR])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^LT_OBJDIR$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^lt_ECHO$])
+m4trace:configure.ac:47: -1- _LT_CC_BASENAME([$compiler])
+m4trace:configure.ac:47: -1- _LT_PATH_TOOL_PREFIX([${ac_tool_prefix}file], [/usr/bin$PATH_SEPARATOR$PATH])
+m4trace:configure.ac:47: -1- _LT_PATH_TOOL_PREFIX([file], [/usr/bin$PATH_SEPARATOR$PATH])
+m4trace:configure.ac:47: -1- LT_SUPPORTED_TAG([CC])
+m4trace:configure.ac:47: -1- _LT_COMPILER_BOILERPLATE
+m4trace:configure.ac:47: -1- _LT_LINKER_BOILERPLATE
+m4trace:configure.ac:47: -1- _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions], [lt_cv_prog_compiler_rtti_exceptions], [-fno-rtti -fno-exceptions], [], [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, )="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, ) -fno-rtti -fno-exceptions"])
+m4trace:configure.ac:47: -1- _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, ) works], [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, )], [$_LT_TAGVAR(lt_prog_compiler_pic, )@&t@m4_if([],[],[ -DPIC],[m4_if([],[CXX],[ -DPIC],[])])], [], [case $_LT_TAGVAR(lt_prog_compiler_pic, ) in
+ "" | " "*) ;;
+ *) _LT_TAGVAR(lt_prog_compiler_pic, )=" $_LT_TAGVAR(lt_prog_compiler_pic, )" ;;
+ esac], [_LT_TAGVAR(lt_prog_compiler_pic, )=
+ _LT_TAGVAR(lt_prog_compiler_can_build_shared, )=no])
+m4trace:configure.ac:47: -1- _LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works], [lt_cv_prog_compiler_static_works], [$lt_tmp_static_flag], [], [_LT_TAGVAR(lt_prog_compiler_static, )=])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^DSYMUTIL$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^NMEDIT$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^LIPO$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^OTOOL$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^OTOOL64$])
+m4trace:configure.ac:47: -1- LT_SYS_DLOPEN_SELF
+m4trace:configure.ac:47: -1- m4_pattern_allow([^CPP$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^CPPFLAGS$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^CPP$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^STDC_HEADERS$])
+m4trace:configure.ac:47: -1- m4_pattern_allow([^HAVE_DLFCN_H$])
+m4trace:configure.ac:49: -1- m4_pattern_allow([^AR$])
+m4trace:configure.ac:50: -1- m4_pattern_allow([^RM$])
+m4trace:configure.ac:51: -1- m4_pattern_allow([^CP$])
+m4trace:configure.ac:52: -1- m4_pattern_allow([^LN$])
+m4trace:configure.ac:53: -1- m4_pattern_allow([^SED$])
+m4trace:configure.ac:54: -1- m4_pattern_allow([^MAKE$])
+m4trace:configure.ac:58: -1- AM_GNU_GETTEXT_VERSION([0.16.1])
+m4trace:configure.ac:59: -1- AM_GNU_GETTEXT([external])
+m4trace:configure.ac:59: -1- AM_GNU_GETTEXT_NEED([])
+m4trace:configure.ac:59: -1- AM_PO_SUBDIRS
+m4trace:configure.ac:59: -1- AM_NLS
+m4trace:configure.ac:59: -1- m4_pattern_allow([^USE_NLS$])
+m4trace:configure.ac:59: -1- AM_PATH_PROG_WITH_TEST([MSGFMT], [msgfmt], [$ac_dir/$ac_word --statistics /dev/null >&5 2>&1 &&
+ (if $ac_dir/$ac_word --statistics /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi)], [:])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^MSGFMT$])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^GMSGFMT$])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^MSGFMT_015$])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^GMSGFMT_015$])
+m4trace:configure.ac:59: -1- AM_PATH_PROG_WITH_TEST([XGETTEXT], [xgettext], [$ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= /dev/null >&5 2>&1 &&
+ (if $ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi)], [:])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^XGETTEXT$])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^XGETTEXT_015$])
+m4trace:configure.ac:59: -1- AM_PATH_PROG_WITH_TEST([MSGMERGE], [msgmerge], [$ac_dir/$ac_word --update -q /dev/null /dev/null >&5 2>&1], [:])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^MSGMERGE$])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^localedir$])
+m4trace:configure.ac:59: -1- AC_LIB_PREPARE_PREFIX
+m4trace:configure.ac:59: -1- AC_LIB_RPATH
+m4trace:configure.ac:59: -1- AC_LIB_PROG_LD
+m4trace:configure.ac:59: -1- AC_LIB_PROG_LD_GNU
+m4trace:configure.ac:59: -1- AM_ICONV_LINKFLAGS_BODY
+m4trace:configure.ac:59: -1- AC_LIB_LINKFLAGS_BODY([iconv])
+m4trace:configure.ac:59: -1- AC_LIB_PREPARE_MULTILIB
+m4trace:configure.ac:59: -1- AC_LIB_WITH_FINAL_PREFIX([
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+ ])
+m4trace:configure.ac:59: -1- AC_LIB_ARG_WITH([libiconv-prefix], [ --with-libiconv-prefix[=DIR] search for libiconv in DIR/include and DIR/lib
+ --without-libiconv-prefix don't search for libiconv in includedir and libdir], [
+ if test "X$withval" = "Xno"; then
+ use_additional=no
+ else
+ if test "X$withval" = "X"; then
+ AC_LIB_WITH_FINAL_PREFIX([
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+ ])
+ else
+ additional_includedir="$withval/include"
+ additional_libdir="$withval/$acl_libdirstem"
+ fi
+ fi
+])
+m4trace:configure.ac:59: -1- AC_LIB_WITH_FINAL_PREFIX([
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+ ])
+m4trace:configure.ac:59: -1- AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+m4trace:configure.ac:59: -1- AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+m4trace:configure.ac:59: -1- AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+m4trace:configure.ac:59: -1- AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+m4trace:configure.ac:59: -1- AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+m4trace:configure.ac:59: -1- gt_INTL_MACOSX
+m4trace:configure.ac:59: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:2688: AC_TRY_LINK is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/general.m4:2032: AC_CACHE_VAL is expanded from...
+../../lib/autoconf/general.m4:2053: AC_CACHE_CHECK is expanded from...
+m4/gettext.m4:367: gt_INTL_MACOSX is expanded from...
+m4/gettext.m4:57: AM_GNU_GETTEXT is expanded from...
+configure.ac:59: the top level])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^HAVE_CFPREFERENCESCOPYAPPVALUE$])
+m4trace:configure.ac:59: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:2688: AC_TRY_LINK is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/general.m4:2032: AC_CACHE_VAL is expanded from...
+../../lib/autoconf/general.m4:2053: AC_CACHE_CHECK is expanded from...
+m4/gettext.m4:367: gt_INTL_MACOSX is expanded from...
+m4/gettext.m4:57: AM_GNU_GETTEXT is expanded from...
+configure.ac:59: the top level])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^HAVE_CFLOCALECOPYCURRENT$])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^INTL_MACOSX_LIBS$])
+m4trace:configure.ac:59: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:2688: AC_TRY_LINK is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/general.m4:2032: AC_CACHE_VAL is expanded from...
+../../lib/autoconf/general.m4:2053: AC_CACHE_CHECK is expanded from...
+m4/gettext.m4:57: AM_GNU_GETTEXT is expanded from...
+configure.ac:59: the top level])
+m4trace:configure.ac:59: -1- AM_ICONV_LINK
+m4trace:configure.ac:59: -1- AC_LIB_APPENDTOVAR([CPPFLAGS], [$INCICONV])
+m4trace:configure.ac:59: -1- AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+m4trace:configure.ac:59: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:2688: AC_TRY_LINK is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/general.m4:2032: AC_CACHE_VAL is expanded from...
+../../lib/autoconf/general.m4:2053: AC_CACHE_CHECK is expanded from...
+m4/iconv.m4:20: AM_ICONV_LINK is expanded from...
+m4/gettext.m4:57: AM_GNU_GETTEXT is expanded from...
+configure.ac:59: the top level])
+m4trace:configure.ac:59: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:2688: AC_TRY_LINK is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/general.m4:2032: AC_CACHE_VAL is expanded from...
+../../lib/autoconf/general.m4:2053: AC_CACHE_CHECK is expanded from...
+m4/iconv.m4:20: AM_ICONV_LINK is expanded from...
+m4/gettext.m4:57: AM_GNU_GETTEXT is expanded from...
+configure.ac:59: the top level])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^HAVE_ICONV$])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^LIBICONV$])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^LTLIBICONV$])
+m4trace:configure.ac:59: -1- AC_LIB_LINKFLAGS_BODY([intl])
+m4trace:configure.ac:59: -1- AC_LIB_WITH_FINAL_PREFIX([
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+ ])
+m4trace:configure.ac:59: -1- AC_LIB_ARG_WITH([libintl-prefix], [ --with-libintl-prefix[=DIR] search for libintl in DIR/include and DIR/lib
+ --without-libintl-prefix don't search for libintl in includedir and libdir], [
+ if test "X$withval" = "Xno"; then
+ use_additional=no
+ else
+ if test "X$withval" = "X"; then
+ AC_LIB_WITH_FINAL_PREFIX([
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+ ])
+ else
+ additional_includedir="$withval/include"
+ additional_libdir="$withval/$acl_libdirstem"
+ fi
+ fi
+])
+m4trace:configure.ac:59: -1- AC_LIB_WITH_FINAL_PREFIX([
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+ ])
+m4trace:configure.ac:59: -1- AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+m4trace:configure.ac:59: -1- AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+m4trace:configure.ac:59: -1- AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+m4trace:configure.ac:59: -1- AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+m4trace:configure.ac:59: -1- AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+m4trace:configure.ac:59: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:2688: AC_TRY_LINK is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/general.m4:2032: AC_CACHE_VAL is expanded from...
+../../lib/autoconf/general.m4:2053: AC_CACHE_CHECK is expanded from...
+m4/gettext.m4:57: AM_GNU_GETTEXT is expanded from...
+configure.ac:59: the top level])
+m4trace:configure.ac:59: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:2688: AC_TRY_LINK is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/general.m4:2032: AC_CACHE_VAL is expanded from...
+../../lib/autoconf/general.m4:2053: AC_CACHE_CHECK is expanded from...
+m4/gettext.m4:57: AM_GNU_GETTEXT is expanded from...
+configure.ac:59: the top level])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^ENABLE_NLS$])
+m4trace:configure.ac:59: -1- AC_LIB_APPENDTOVAR([CPPFLAGS], [$INCINTL])
+m4trace:configure.ac:59: -1- AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^HAVE_GETTEXT$])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^HAVE_DCGETTEXT$])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^INTLLIBS$])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^LIBINTL$])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^LTLIBINTL$])
+m4trace:configure.ac:59: -1- m4_pattern_allow([^POSUB$])
+m4trace:configure.ac:64: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:64: the top level])
+m4trace:configure.ac:72: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:72: the top level])
+m4trace:configure.ac:82: -1- m4_pattern_allow([^ENABLE_DMALLOC$])
+m4trace:configure.ac:87: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:87: the top level])
+m4trace:configure.ac:99: -1- m4_pattern_allow([^localedir$])
+m4trace:configure.ac:106: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:106: the top level])
+m4trace:configure.ac:131: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:131: the top level])
+m4trace:configure.ac:147: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:147: the top level])
+m4trace:configure.ac:163: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:163: the top level])
+m4trace:configure.ac:172: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:172: the top level])
+m4trace:configure.ac:187: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:187: the top level])
+m4trace:configure.ac:190: -1- m4_pattern_allow([^DEBUG$])
+m4trace:configure.ac:191: -1- m4_pattern_allow([^DEBUGJOURNAL$])
+m4trace:configure.ac:200: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:200: the top level])
+m4trace:configure.ac:211: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:211: the top level])
+m4trace:configure.ac:213: -1- m4_pattern_allow([^MOUSE$])
+m4trace:configure.ac:221: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:221: the top level])
+m4trace:configure.ac:223: -1- m4_pattern_allow([^USE_QUOTAS$])
+m4trace:configure.ac:230: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:230: the top level])
+m4trace:configure.ac:234: -1- m4_pattern_allow([^NEVER_ALLOW_CHANGING_FROM$])
+m4trace:configure.ac:240: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:240: the top level])
+m4trace:configure.ac:242: -1- m4_pattern_allow([^BACKGROUND_POST$])
+m4trace:configure.ac:250: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:250: the top level])
+m4trace:configure.ac:252: -1- m4_pattern_allow([^KEYBOARD_LOCK$])
+m4trace:configure.ac:260: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:260: the top level])
+m4trace:configure.ac:262: -1- m4_pattern_allow([^ENCODE_FROMS$])
+m4trace:configure.ac:271: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:271: the top level])
+m4trace:configure.ac:270: -1- m4_pattern_allow([^SENDMAIL$])
+m4trace:configure.ac:270: -1- m4_pattern_allow([^SENDMAIL$])
+m4trace:configure.ac:288: -1- m4_pattern_allow([^SENDMAIL$])
+m4trace:configure.ac:294: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:294: the top level])
+m4trace:configure.ac:300: -1- m4_pattern_allow([^SENDMAILFLAGS$])
+m4trace:configure.ac:305: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:305: the top level])
+m4trace:configure.ac:304: -1- m4_pattern_allow([^NPA_PROG$])
+m4trace:configure.ac:304: -1- m4_pattern_allow([^NPA_PROG$])
+m4trace:configure.ac:324: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:324: the top level])
+m4trace:configure.ac:331: -1- m4_pattern_allow([^SENDNEWS$])
+m4trace:configure.ac:336: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:336: the top level])
+m4trace:configure.ac:335: -1- m4_pattern_allow([^PWPROG$])
+m4trace:configure.ac:335: -1- m4_pattern_allow([^PWPROG$])
+m4trace:configure.ac:335: -1- m4_pattern_allow([^PWPROG$])
+m4trace:configure.ac:353: -1- m4_pattern_allow([^PASSWD_PROG$])
+m4trace:configure.ac:358: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:358: the top level])
+m4trace:configure.ac:357: -1- m4_pattern_allow([^SPELLPROG$])
+m4trace:configure.ac:357: -1- m4_pattern_allow([^SPELLPROG$])
+m4trace:configure.ac:357: -1- m4_pattern_allow([^SPELLPROG$])
+m4trace:configure.ac:378: -1- m4_pattern_allow([^alpine_simple_spellcheck$])
+m4trace:configure.ac:398: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:398: the top level])
+m4trace:configure.ac:397: -1- m4_pattern_allow([^ISPELLPROG$])
+m4trace:configure.ac:397: -1- m4_pattern_allow([^ISPELLPROG$])
+m4trace:configure.ac:415: -1- m4_pattern_allow([^alpine_interactive_spellcheck$])
+m4trace:configure.ac:428: -1- m4_pattern_allow([^DF_VAR_SPELLER$])
+m4trace:configure.ac:434: -1- m4_pattern_allow([^SPELLER$])
+m4trace:configure.ac:445: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:445: the top level])
+m4trace:configure.ac:457: -1- m4_pattern_allow([^SYSTEM_PINERC$])
+m4trace:configure.ac:468: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:468: the top level])
+m4trace:configure.ac:480: -1- m4_pattern_allow([^SYSTEM_PINERC_FIXED$])
+m4trace:configure.ac:484: -1- AC_DEFUN([PINEVAR], [
+ dpv=$3
+ AC_ARG_WITH($1,
+ AC_HELP_STRING(--with-$1=VALUE, [$4 ($3)]),
+ [
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+ ])
+ AC_DEFINE_UNQUOTED($2, "$dpv", [Default configuration value])
+ ])
+m4trace:configure.ac:499: -1- AC_DEFUN([PINEVAR_UNQUOTED], [
+ dpv=$3
+ AC_ARG_WITH($1,
+ AC_HELP_STRING(--with-$1=VALUE, [$4 ($3)]),
+ [
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+ ])
+ AC_DEFINE_UNQUOTED($2, $dpv, [Default configuration value])
+ ])
+m4trace:configure.ac:512: -1- PINEVAR([mailcheck-interval], [DF_MAILCHECK], [150], [Specify default mail-check-interval])
+m4trace:configure.ac:512: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:512: the top level])
+m4trace:configure.ac:512: -1- m4_pattern_allow([^DF_MAILCHECK$])
+m4trace:configure.ac:513: -1- PINEVAR_UNQUOTED([checkpoint-interval], [CHECK_POINT_TIME], [420], [Specify default checkpoint-interval])
+m4trace:configure.ac:513: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:499: PINEVAR_UNQUOTED is expanded from...
+configure.ac:513: the top level])
+m4trace:configure.ac:513: -1- m4_pattern_allow([^CHECK_POINT_TIME$])
+m4trace:configure.ac:514: -1- PINEVAR_UNQUOTED([checkpoint-frequency], [CHECK_POINT_FREQ], [12], [State change count before checkpoint])
+m4trace:configure.ac:514: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:499: PINEVAR_UNQUOTED is expanded from...
+configure.ac:514: the top level])
+m4trace:configure.ac:514: -1- m4_pattern_allow([^CHECK_POINT_FREQ$])
+m4trace:configure.ac:515: -1- PINEVAR_UNQUOTED([display-rows], [DEFAULT_LINES_ON_TERMINAL], [24], [Initial rows on display])
+m4trace:configure.ac:515: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:499: PINEVAR_UNQUOTED is expanded from...
+configure.ac:515: the top level])
+m4trace:configure.ac:515: -1- m4_pattern_allow([^DEFAULT_LINES_ON_TERMINAL$])
+m4trace:configure.ac:516: -1- PINEVAR_UNQUOTED([display-columns], [DEFAULT_COLUMNS_ON_TERMINAL], [80], [Initial columns on display])
+m4trace:configure.ac:516: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:499: PINEVAR_UNQUOTED is expanded from...
+configure.ac:516: the top level])
+m4trace:configure.ac:516: -1- m4_pattern_allow([^DEFAULT_COLUMNS_ON_TERMINAL$])
+m4trace:configure.ac:517: -1- PINEVAR_UNQUOTED([max-display-rows], [MAX_SCREEN_ROWS], [200], [Maximum display rows])
+m4trace:configure.ac:517: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:499: PINEVAR_UNQUOTED is expanded from...
+configure.ac:517: the top level])
+m4trace:configure.ac:517: -1- m4_pattern_allow([^MAX_SCREEN_ROWS$])
+m4trace:configure.ac:518: -1- PINEVAR_UNQUOTED([max-display-columns], [MAX_SCREEN_COLS], [500], [Maximum display columns])
+m4trace:configure.ac:518: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:499: PINEVAR_UNQUOTED is expanded from...
+configure.ac:518: the top level])
+m4trace:configure.ac:518: -1- m4_pattern_allow([^MAX_SCREEN_COLS$])
+m4trace:configure.ac:519: -1- PINEVAR([fill-column], [DF_FILLCOL], [74], [Default fill column])
+m4trace:configure.ac:519: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:519: the top level])
+m4trace:configure.ac:519: -1- m4_pattern_allow([^DF_FILLCOL$])
+m4trace:configure.ac:520: -1- PINEVAR_UNQUOTED([max_fill-column], [MAX_FILLCOL], [80], [Maximum fill column])
+m4trace:configure.ac:520: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:499: PINEVAR_UNQUOTED is expanded from...
+configure.ac:520: the top level])
+m4trace:configure.ac:520: -1- m4_pattern_allow([^MAX_FILLCOL$])
+m4trace:configure.ac:521: -1- PINEVAR_UNQUOTED([debug-level], [DEFAULT_DEBUG], [2], [Specify default debug verbosity level])
+m4trace:configure.ac:521: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:499: PINEVAR_UNQUOTED is expanded from...
+configure.ac:521: the top level])
+m4trace:configure.ac:521: -1- m4_pattern_allow([^DEFAULT_DEBUG$])
+m4trace:configure.ac:522: -1- PINEVAR_UNQUOTED([debug-files], [NUMDEBUGFILES], [4], [Specify number of debug files])
+m4trace:configure.ac:522: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:499: PINEVAR_UNQUOTED is expanded from...
+configure.ac:522: the top level])
+m4trace:configure.ac:522: -1- m4_pattern_allow([^NUMDEBUGFILES$])
+m4trace:configure.ac:523: -1- PINEVAR([debug-file], [DEBUGFILE], [.pine-debug], [Specify debug file name])
+m4trace:configure.ac:523: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:523: the top level])
+m4trace:configure.ac:523: -1- m4_pattern_allow([^DEBUGFILE$])
+m4trace:configure.ac:524: -1- PINEVAR([forwarded-keyword], [FORWARDED_FLAG], ["\$Forwarded"], [IMAP (c-client) keyword to store forwarded status])
+m4trace:configure.ac:524: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:524: the top level])
+m4trace:configure.ac:524: -1- m4_pattern_allow([^FORWARDED_FLAG$])
+m4trace:configure.ac:525: -1- PINEVAR([display-overlap], [DF_OVERLAP], [2], [Lines preserved while paging])
+m4trace:configure.ac:525: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:525: the top level])
+m4trace:configure.ac:525: -1- m4_pattern_allow([^DF_OVERLAP$])
+m4trace:configure.ac:526: -1- PINEVAR([display-margin], [DF_MARGIN], [0], [Lines visible while scrolling])
+m4trace:configure.ac:526: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:526: the top level])
+m4trace:configure.ac:526: -1- m4_pattern_allow([^DF_MARGIN$])
+m4trace:configure.ac:527: -1- PINEVAR([default-fcc], [DF_DEFAULT_FCC], [sent-mail], [Default sent mail folder])
+m4trace:configure.ac:527: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:527: the top level])
+m4trace:configure.ac:527: -1- m4_pattern_allow([^DF_DEFAULT_FCC$])
+m4trace:configure.ac:528: -1- PINEVAR([default-save-folder], [DEFAULT_SAVE], [saved-messages], [Default save folder])
+m4trace:configure.ac:528: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:528: the top level])
+m4trace:configure.ac:528: -1- m4_pattern_allow([^DEFAULT_SAVE$])
+m4trace:configure.ac:529: -1- PINEVAR([default-legacy-postponed-folder], [POSTPONED_MAIL], [postponed-mail], [Pre Pine 3.90 postponed folder])
+m4trace:configure.ac:529: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:529: the top level])
+m4trace:configure.ac:529: -1- m4_pattern_allow([^POSTPONED_MAIL$])
+m4trace:configure.ac:530: -1- PINEVAR([default-postponed-folder], [POSTPONED_MSGS], [postponed-msgs], [Default postponed folder])
+m4trace:configure.ac:530: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:530: the top level])
+m4trace:configure.ac:530: -1- m4_pattern_allow([^POSTPONED_MSGS$])
+m4trace:configure.ac:531: -1- PINEVAR([default-trash-folder], [TRASH_FOLDER], [Trash], [Default Trash folder for Web Alpine])
+m4trace:configure.ac:531: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:531: the top level])
+m4trace:configure.ac:531: -1- m4_pattern_allow([^TRASH_FOLDER$])
+m4trace:configure.ac:532: -1- PINEVAR([default-interrupted-mail], [INTERRUPTED_MAIL], [.pine-interrupted-mail], [Default folder for interrupted mail])
+m4trace:configure.ac:532: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:532: the top level])
+m4trace:configure.ac:532: -1- m4_pattern_allow([^INTERRUPTED_MAIL$])
+m4trace:configure.ac:533: -1- PINEVAR([default-dead-letter-folder], [DEADLETTER], [dead.letter], [Default dead letter folder])
+m4trace:configure.ac:533: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:533: the top level])
+m4trace:configure.ac:533: -1- m4_pattern_allow([^DEADLETTER$])
+m4trace:configure.ac:534: -1- PINEVAR([default-mail-directory], [DF_MAIL_DIRECTORY], [mail], [Default mail directory])
+m4trace:configure.ac:534: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:534: the top level])
+m4trace:configure.ac:534: -1- m4_pattern_allow([^DF_MAIL_DIRECTORY$])
+m4trace:configure.ac:535: -1- PINEVAR([default-inbox-name], [INBOX_NAME], [INBOX], [Default inbox name])
+m4trace:configure.ac:535: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:535: the top level])
+m4trace:configure.ac:535: -1- m4_pattern_allow([^INBOX_NAME$])
+m4trace:configure.ac:536: -1- PINEVAR([default-signature-file], [DF_SIGNATURE_FILE], [.signature], [Default signature file])
+m4trace:configure.ac:536: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:536: the top level])
+m4trace:configure.ac:536: -1- m4_pattern_allow([^DF_SIGNATURE_FILE$])
+m4trace:configure.ac:537: -1- PINEVAR([default-elm-style-save], [DF_ELM_STYLE_SAVE], [no], [Default to Elm style save])
+m4trace:configure.ac:537: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:537: the top level])
+m4trace:configure.ac:537: -1- m4_pattern_allow([^DF_ELM_STYLE_SAVE$])
+m4trace:configure.ac:538: -1- PINEVAR([default-header-in-reply], [DF_HEADER_IN_REPLY], [no], [Include header in reply])
+m4trace:configure.ac:538: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:538: the top level])
+m4trace:configure.ac:538: -1- m4_pattern_allow([^DF_HEADER_IN_REPLY$])
+m4trace:configure.ac:539: -1- PINEVAR([default-old-style-reply], [DF_OLD_STYLE_REPLY], [no], [Default to old style reply])
+m4trace:configure.ac:539: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:539: the top level])
+m4trace:configure.ac:539: -1- m4_pattern_allow([^DF_OLD_STYLE_REPLY$])
+m4trace:configure.ac:540: -1- PINEVAR([default-use-only-domain-name], [DF_USE_ONLY_DOMAIN_NAME], [no], [Default to using only the domain name])
+m4trace:configure.ac:540: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:540: the top level])
+m4trace:configure.ac:540: -1- m4_pattern_allow([^DF_USE_ONLY_DOMAIN_NAME$])
+m4trace:configure.ac:541: -1- PINEVAR([default-save-by-sender], [DF_SAVE_BY_SENDER], [no], [Default to save by sender])
+m4trace:configure.ac:541: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:541: the top level])
+m4trace:configure.ac:541: -1- m4_pattern_allow([^DF_SAVE_BY_SENDER$])
+m4trace:configure.ac:542: -1- PINEVAR([default-sort-key], [DF_SORT_KEY], [arrival], [Default sort key])
+m4trace:configure.ac:542: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:542: the top level])
+m4trace:configure.ac:542: -1- m4_pattern_allow([^DF_SORT_KEY$])
+m4trace:configure.ac:543: -1- PINEVAR([default-addressbook-sort-rule], [DF_AB_SORT_RULE], [fullname-with-lists-last], [Default addressbook sort rule])
+m4trace:configure.ac:543: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:543: the top level])
+m4trace:configure.ac:543: -1- m4_pattern_allow([^DF_AB_SORT_RULE$])
+m4trace:configure.ac:544: -1- PINEVAR([default-folder-sort-rule], [DF_FLD_SORT_RULE], [alphabetical], [Default folder sort rule])
+m4trace:configure.ac:544: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:544: the top level])
+m4trace:configure.ac:544: -1- m4_pattern_allow([^DF_FLD_SORT_RULE$])
+m4trace:configure.ac:545: -1- PINEVAR([default-saved-message-name-rule], [DF_SAVED_MSG_NAME_RULE], [default-folder], [Default saved message name rule])
+m4trace:configure.ac:545: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:545: the top level])
+m4trace:configure.ac:545: -1- m4_pattern_allow([^DF_SAVED_MSG_NAME_RULE$])
+m4trace:configure.ac:546: -1- PINEVAR([default-fcc-rule], [DF_FCC_RULE], [default-fcc], [Default fcc rule])
+m4trace:configure.ac:546: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:546: the top level])
+m4trace:configure.ac:546: -1- m4_pattern_allow([^DF_FCC_RULE$])
+m4trace:configure.ac:547: -1- PINEVAR([default-standard-printer], [DF_STANDARD_PRINTER], [lpr], [Default standard printern])
+m4trace:configure.ac:547: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:547: the top level])
+m4trace:configure.ac:547: -1- m4_pattern_allow([^DF_STANDARD_PRINTER$])
+m4trace:configure.ac:548: -1- PINEVAR([default-ansi-printer], [ANSI_PRINTER], [attached-to-ansi], [ANSI printer definition])
+m4trace:configure.ac:548: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:548: the top level])
+m4trace:configure.ac:548: -1- m4_pattern_allow([^ANSI_PRINTER$])
+m4trace:configure.ac:549: -1- PINEVAR([default-addressbook], [DF_ADDRESSBOOK], [.addressbook], [Default addressbook name])
+m4trace:configure.ac:549: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:549: the top level])
+m4trace:configure.ac:549: -1- m4_pattern_allow([^DF_ADDRESSBOOK$])
+m4trace:configure.ac:550: -1- PINEVAR([default-local-fullname], [DF_LOCAL_FULLNAME], ["Local Support"], [Default local support fullname])
+m4trace:configure.ac:550: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:550: the top level])
+m4trace:configure.ac:550: -1- m4_pattern_allow([^DF_LOCAL_FULLNAME$])
+m4trace:configure.ac:551: -1- PINEVAR([default-local-address], [DF_LOCAL_ADDRESS], [postmaster], [Default local support address])
+m4trace:configure.ac:551: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:551: the top level])
+m4trace:configure.ac:551: -1- m4_pattern_allow([^DF_LOCAL_ADDRESS$])
+m4trace:configure.ac:552: -1- PINEVAR([default-keyboard-lock-count], [DF_KBLOCK_PASSWD_COUNT], [1], [Default keyboard lock count])
+m4trace:configure.ac:552: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:552: the top level])
+m4trace:configure.ac:552: -1- m4_pattern_allow([^DF_KBLOCK_PASSWD_COUNT$])
+m4trace:configure.ac:553: -1- PINEVAR([default-remote-addressbook-history], [DF_REMOTE_ABOOK_HISTORY], [3], [Default address book history count])
+m4trace:configure.ac:553: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:553: the top level])
+m4trace:configure.ac:553: -1- m4_pattern_allow([^DF_REMOTE_ABOOK_HISTORY$])
+m4trace:configure.ac:554: -1- PINEVAR([smime-public-cert-directory], [DF_PUBLICCERT_DIR], [.alpine-smime/public], [Default Public Cert Directory])
+m4trace:configure.ac:554: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:554: the top level])
+m4trace:configure.ac:554: -1- m4_pattern_allow([^DF_PUBLICCERT_DIR$])
+m4trace:configure.ac:555: -1- PINEVAR([smime-private-key-directory], [DF_PRIVATEKEY_DIR], [.alpine-smime/private], [Default Private Key Directory])
+m4trace:configure.ac:555: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:555: the top level])
+m4trace:configure.ac:555: -1- m4_pattern_allow([^DF_PRIVATEKEY_DIR$])
+m4trace:configure.ac:556: -1- PINEVAR([smime-cacert-directory], [DF_CACERT_DIR], [.alpine-smime/ca], [Default Cert Authority Directory])
+m4trace:configure.ac:556: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:484: PINEVAR is expanded from...
+configure.ac:556: the top level])
+m4trace:configure.ac:556: -1- m4_pattern_allow([^DF_CACERT_DIR$])
+m4trace:configure.ac:557: -1- PINEVAR_UNQUOTED([default-printer], [DF_DEFAULT_PRINTER], [ANSI_PRINTER], [Default printer])
+m4trace:configure.ac:557: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:499: PINEVAR_UNQUOTED is expanded from...
+configure.ac:557: the top level])
+m4trace:configure.ac:557: -1- m4_pattern_allow([^DF_DEFAULT_PRINTER$])
+m4trace:configure.ac:561: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:561: the top level])
+m4trace:configure.ac:576: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:576: the top level])
+m4trace:configure.ac:583: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:583: the top level])
+m4trace:configure.ac:597: -1- m4_pattern_allow([^PASSFILE$])
+m4trace:configure.ac:602: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:602: the top level])
+m4trace:configure.ac:601: -1- m4_pattern_allow([^DF_SSHPATH$])
+m4trace:configure.ac:617: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:617: the top level])
+m4trace:configure.ac:616: -1- m4_pattern_allow([^DF_SSHCMD$])
+m4trace:configure.ac:633: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:633: the top level])
+m4trace:configure.ac:696: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:696: the top level])
+m4trace:configure.ac:704: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:704: the top level])
+m4trace:configure.ac:712: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:712: the top level])
+m4trace:configure.ac:720: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:720: the top level])
+m4trace:configure.ac:742: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:742: the top level])
+m4trace:configure.ac:751: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:751: the top level])
+m4trace:configure.ac:760: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:760: the top level])
+m4trace:configure.ac:768: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:768: the top level])
+m4trace:configure.ac:778: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:778: the top level])
+m4trace:configure.ac:789: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:789: the top level])
+m4trace:configure.ac:798: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:798: the top level])
+m4trace:configure.ac:806: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:806: the top level])
+m4trace:configure.ac:816: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:816: the top level])
+m4trace:configure.ac:821: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:821: the top level])
+m4trace:configure.ac:829: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:829: the top level])
+m4trace:configure.ac:836: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:836: the top level])
+m4trace:configure.ac:846: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:846: the top level])
+m4trace:configure.ac:850: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:850: the top level])
+m4trace:configure.ac:854: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:854: the top level])
+m4trace:configure.ac:862: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:862: the top level])
+m4trace:configure.ac:872: -2- _m4_warn([obsolete], [The macro `AC_HELP_STRING' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:209: AC_HELP_STRING is expanded from...
+configure.ac:872: the top level])
+m4trace:configure.ac:893: -1- _m4_warn([obsolete], [The macro `AC_ERROR' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/oldnames.m4:34: AC_ERROR is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/libs.m4:100: AC_CHECK_LIB is expanded from...
+configure.ac:893: the top level])
+m4trace:configure.ac:900: -1- _m4_warn([obsolete], [The macro `AC_ERROR' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/oldnames.m4:34: AC_ERROR is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/libs.m4:100: AC_CHECK_LIB is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/libs.m4:100: AC_CHECK_LIB is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/libs.m4:100: AC_CHECK_LIB is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/libs.m4:100: AC_CHECK_LIB is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/libs.m4:100: AC_CHECK_LIB is expanded from...
+configure.ac:900: the top level])
+m4trace:configure.ac:938: -1- m4_pattern_allow([^HAS_TERMINFO$])
+m4trace:configure.ac:941: -1- m4_pattern_allow([^HAS_TERMCAP$])
+m4trace:configure.ac:965: -1- m4_pattern_allow([^ENABLE_LDAP$])
+m4trace:configure.ac:970: -1- m4_pattern_allow([^LDAP_DEPRECATED$])
+m4trace:configure.ac:1054: -1- _m4_warn([cross], [cannot check for file existence when cross compiling], [../../lib/autoconf/general.m4:2778: AC_CHECK_FILE is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/headers.m4:129: _AC_CHECK_HEADER_MONGREL is expanded from...
+../../lib/autoconf/headers.m4:67: AC_CHECK_HEADER is expanded from...
+configure.ac:1054: the top level])
+m4trace:configure.ac:1072: -1- _m4_warn([obsolete], [The macro `AC_ERROR' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/oldnames.m4:34: AC_ERROR is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/libs.m4:48: AC_SEARCH_LIBS is expanded from...
+configure.ac:1072: the top level])
+m4trace:configure.ac:1082: -1- _m4_warn([obsolete], [The macro `AC_ERROR' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/oldnames.m4:34: AC_ERROR is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/headers.m4:129: _AC_CHECK_HEADER_MONGREL is expanded from...
+../../lib/autoconf/headers.m4:67: AC_CHECK_HEADER is expanded from...
+configure.ac:1082: the top level])
+m4trace:configure.ac:1091: -1- m4_pattern_allow([^HAVE_REGEX_H$])
+m4trace:configure.ac:1100: -1- ACX_PTHREAD([
+ AC_MSG_RESULT([yes])
+ case "$target" in
+ *openbsd*)
+ AC_MSG_NOTICE([WARNING: pthread support on OpenBSD is unstable!])
+ AM_CFLAGS="$AM_CFLAGS -pthread"
+ ;;
+ esac
+ LIBS="$PTHREAD_LIBS $LIBS"
+ AM_CFLAGS="$AM_CFLAGS $PTHREAD_CFLAGS"
+ CC="$PTHREAD_CC"
+ AC_DEFINE([HAVE_PTHREAD], [1], [System has pthread support])
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+m4trace:configure.ac:1100: -1- _m4_warn([obsolete], [The macro `AC_LANG_SAVE' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/lang.m4:126: AC_LANG_SAVE is expanded from...
+m4/acx_pthread.m4:49: ACX_PTHREAD is expanded from...
+configure.ac:1100: the top level])
+m4trace:configure.ac:1100: -1- _m4_warn([obsolete], [The macro `AC_LANG_C' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/c.m4:73: AC_LANG_C is expanded from...
+m4/acx_pthread.m4:49: ACX_PTHREAD is expanded from...
+configure.ac:1100: the top level])
+m4trace:configure.ac:1100: -1- m4_pattern_allow([^acx_pthread_config$])
+m4trace:configure.ac:1100: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:2688: AC_TRY_LINK is expanded from...
+m4/acx_pthread.m4:49: ACX_PTHREAD is expanded from...
+configure.ac:1100: the top level])
+m4trace:configure.ac:1100: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:2688: AC_TRY_LINK is expanded from...
+m4/acx_pthread.m4:49: ACX_PTHREAD is expanded from...
+configure.ac:1100: the top level])
+m4trace:configure.ac:1100: -1- m4_pattern_allow([^PTHREAD_CREATE_JOINABLE$])
+m4trace:configure.ac:1100: -1- m4_pattern_allow([^PTHREAD_CC$])
+m4trace:configure.ac:1100: -1- m4_pattern_allow([^PTHREAD_LIBS$])
+m4trace:configure.ac:1100: -1- m4_pattern_allow([^PTHREAD_CFLAGS$])
+m4trace:configure.ac:1100: -1- m4_pattern_allow([^PTHREAD_CC$])
+m4trace:configure.ac:1100: -2- m4_pattern_allow([^HAVE_PTHREAD$])
+m4trace:configure.ac:1100: -1- m4_pattern_allow([^HAVE_PTHREAD$])
+m4trace:configure.ac:1100: -1- _m4_warn([obsolete], [The macro `AC_LANG_RESTORE' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/lang.m4:135: AC_LANG_RESTORE is expanded from...
+m4/acx_pthread.m4:49: ACX_PTHREAD is expanded from...
+configure.ac:1100: the top level])
+m4trace:configure.ac:1118: -1- m4_pattern_allow([^HAVE_NANOSLEEP$])
+m4trace:configure.ac:1128: -1- m4_pattern_allow([^STDC_HEADERS$])
+m4trace:configure.ac:1130: -1- m4_pattern_allow([^STAT_MACROS_BROKEN$])
+m4trace:configure.ac:1131: -1- m4_pattern_allow([^HAVE_SYS_WAIT_H$])
+m4trace:configure.ac:1132: -1- m4_pattern_allow([^TIME_WITH_SYS_TIME$])
+m4trace:configure.ac:1133: -1- m4_pattern_allow([^GWINSZ_IN_SYS_IOCTL$])
+m4trace:configure.ac:1135: -1- _m4_warn([obsolete], [The macro `AC_UNISTD_H' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/headers.m4:824: AC_UNISTD_H is expanded from...
+configure.ac:1135: the top level])
+m4trace:configure.ac:1135: -1- m4_pattern_allow([^HAVE_UNISTD_H$])
+m4trace:configure.ac:1164: -2- m4_pattern_allow([^HAS_TERMIOS$])
+m4trace:configure.ac:1163: -2- m4_pattern_allow([^HAS_TERMIO$])
+m4trace:configure.ac:1163: -2- m4_pattern_allow([^HAS_SGTTY$])
+m4trace:configure.ac:1163: -1- _m4_warn([obsolete], [The macro `AC_ERROR' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/oldnames.m4:34: AC_ERROR is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/headers.m4:129: _AC_CHECK_HEADER_MONGREL is expanded from...
+../../lib/autoconf/headers.m4:67: AC_CHECK_HEADER is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/headers.m4:129: _AC_CHECK_HEADER_MONGREL is expanded from...
+../../lib/autoconf/headers.m4:67: AC_CHECK_HEADER is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/headers.m4:129: _AC_CHECK_HEADER_MONGREL is expanded from...
+../../lib/autoconf/headers.m4:67: AC_CHECK_HEADER is expanded from...
+configure.ac:1163: the top level])
+m4trace:configure.ac:1180: -1- _m4_warn([obsolete], [The macro `AC_TYPE_SIGNAL' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/types.m4:738: AC_TYPE_SIGNAL is expanded from...
+configure.ac:1180: the top level])
+m4trace:configure.ac:1180: -1- m4_pattern_allow([^RETSIGTYPE$])
+m4trace:configure.ac:1181: -1- m4_pattern_allow([^size_t$])
+m4trace:configure.ac:1182: -1- m4_pattern_allow([^mode_t$])
+m4trace:configure.ac:1183: -1- m4_pattern_allow([^pid_t$])
+m4trace:configure.ac:1184: -1- m4_pattern_allow([^uid_t$])
+m4trace:configure.ac:1184: -1- m4_pattern_allow([^gid_t$])
+m4trace:configure.ac:1185: -1- m4_pattern_allow([^TM_IN_SYS_TIME$])
+m4trace:configure.ac:1187: -1- m4_pattern_allow([^HAVE_UNION_WAIT$])
+m4trace:configure.ac:1189: -1- m4_pattern_allow([^HAVE_STDINT_H$])
+m4trace:configure.ac:1189: -1- m4_pattern_allow([^HAVE_INTTYPES_H$])
+m4trace:configure.ac:1189: -1- m4_pattern_allow([^HAVE_SYS_TYPES_H$])
+m4trace:configure.ac:1189: -1- m4_pattern_allow([^SIZEOF_UNSIGNED_SHORT$])
+m4trace:configure.ac:1189: -1- m4_pattern_allow([^SIZEOF_UNSIGNED_INT$])
+m4trace:configure.ac:1189: -1- _m4_warn([obsolete], [The macro `AC_ERROR' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/oldnames.m4:34: AC_ERROR is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/headers.m4:129: _AC_CHECK_HEADER_MONGREL is expanded from...
+../../lib/autoconf/headers.m4:67: AC_CHECK_HEADER is expanded from...
+../../lib/m4sugar/m4sh.m4:574: AS_FOR is expanded from...
+../../lib/autoconf/headers.m4:249: AC_CHECK_HEADERS is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/headers.m4:129: _AC_CHECK_HEADER_MONGREL is expanded from...
+../../lib/autoconf/headers.m4:67: AC_CHECK_HEADER is expanded from...
+../../lib/m4sugar/m4sh.m4:574: AS_FOR is expanded from...
+../../lib/autoconf/headers.m4:249: AC_CHECK_HEADERS is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/headers.m4:129: _AC_CHECK_HEADER_MONGREL is expanded from...
+../../lib/autoconf/headers.m4:67: AC_CHECK_HEADER is expanded from...
+../../lib/m4sugar/m4sh.m4:574: AS_FOR is expanded from...
+../../lib/autoconf/headers.m4:249: AC_CHECK_HEADERS is expanded from...
+configure.ac:1189: the top level])
+m4trace:configure.ac:1206: -1- m4_pattern_allow([^UINT16$])
+m4trace:configure.ac:1208: -1- m4_pattern_allow([^HAVE_STDINT_H$])
+m4trace:configure.ac:1208: -1- m4_pattern_allow([^HAVE_INTTYPES_H$])
+m4trace:configure.ac:1208: -1- m4_pattern_allow([^HAVE_SYS_TYPES_H$])
+m4trace:configure.ac:1208: -1- m4_pattern_allow([^SIZEOF_UNSIGNED_INT$])
+m4trace:configure.ac:1208: -1- m4_pattern_allow([^SIZEOF_UNSIGNED_LONG$])
+m4trace:configure.ac:1208: -1- _m4_warn([obsolete], [The macro `AC_ERROR' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/oldnames.m4:34: AC_ERROR is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/headers.m4:129: _AC_CHECK_HEADER_MONGREL is expanded from...
+../../lib/autoconf/headers.m4:67: AC_CHECK_HEADER is expanded from...
+../../lib/m4sugar/m4sh.m4:574: AS_FOR is expanded from...
+../../lib/autoconf/headers.m4:249: AC_CHECK_HEADERS is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/headers.m4:129: _AC_CHECK_HEADER_MONGREL is expanded from...
+../../lib/autoconf/headers.m4:67: AC_CHECK_HEADER is expanded from...
+../../lib/m4sugar/m4sh.m4:574: AS_FOR is expanded from...
+../../lib/autoconf/headers.m4:249: AC_CHECK_HEADERS is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/headers.m4:129: _AC_CHECK_HEADER_MONGREL is expanded from...
+../../lib/autoconf/headers.m4:67: AC_CHECK_HEADER is expanded from...
+../../lib/m4sugar/m4sh.m4:574: AS_FOR is expanded from...
+../../lib/autoconf/headers.m4:249: AC_CHECK_HEADERS is expanded from...
+configure.ac:1208: the top level])
+m4trace:configure.ac:1225: -1- m4_pattern_allow([^UINT32$])
+m4trace:configure.ac:1227: -1- _m4_warn([obsolete], [The macro `AC_TRY_COMPILE' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/general.m4:2615: AC_TRY_COMPILE is expanded from...
+../../lib/m4sugar/m4sh.m4:606: AS_IF is expanded from...
+../../lib/autoconf/general.m4:2032: AC_CACHE_VAL is expanded from...
+../../lib/autoconf/general.m4:2053: AC_CACHE_CHECK is expanded from...
+configure.ac:1227: the top level])
+m4trace:configure.ac:1244: -1- m4_pattern_allow([^qsort_t$])
+m4trace:configure.ac:1248: -1- m4_pattern_allow([^SELECT_TYPE_ARG1$])
+m4trace:configure.ac:1248: -1- m4_pattern_allow([^SELECT_TYPE_ARG234$])
+m4trace:configure.ac:1248: -1- m4_pattern_allow([^SELECT_TYPE_ARG5$])
+m4trace:configure.ac:1250: -1- m4_pattern_allow([^HAVE_STRCOLL$])
+m4trace:configure.ac:1254: -1- m4_pattern_allow([^HAVE_VFORK_H$])
+m4trace:configure.ac:1254: -1- m4_pattern_allow([^HAVE_WORKING_VFORK$])
+m4trace:configure.ac:1254: -1- m4_pattern_allow([^vfork$])
+m4trace:configure.ac:1254: -1- m4_pattern_allow([^HAVE_WORKING_FORK$])
+m4trace:configure.ac:1301: -1- m4_pattern_allow([^POSIX_SIGNALS$])
+m4trace:configure.ac:1301: -1- m4_pattern_allow([^SYSV_SIGNALS$])
+m4trace:configure.ac:1312: -1- m4_pattern_allow([^HAVE_SYSLOG$])
+m4trace:configure.ac:1376: -1- m4_pattern_allow([^OSX_TARGET$])
+m4trace:configure.ac:1381: -1- m4_pattern_allow([^APPLEKEYCHAIN$])
+m4trace:configure.ac:1399: -1- m4_pattern_allow([^__EXTENSIONS__$])
+m4trace:configure.ac:1505: -1- m4_pattern_allow([^_WINDOWS$])
+m4trace:configure.ac:1508: -1- _m4_warn([obsolete], [The macro `AC_ERROR' is obsolete.
+You should run autoupdate.], [../../lib/autoconf/oldnames.m4:34: AC_ERROR is expanded from...
+configure.ac:1508: the top level])
+m4trace:configure.ac:1528: -1- m4_pattern_allow([^SYSTYPE$])
+m4trace:configure.ac:1529: -1- m4_pattern_allow([^C_FILESEP$])
+m4trace:configure.ac:1530: -1- m4_pattern_allow([^S_FILESEP$])
+m4trace:configure.ac:1531: -1- m4_pattern_allow([^MAILDIR$])
+m4trace:configure.ac:1532: -1- m4_pattern_allow([^MODE_READONLY$])
+m4trace:configure.ac:1535: -1- m4_pattern_allow([^C_CLIENT_TARGET$])
+m4trace:configure.ac:1536: -1- m4_pattern_allow([^C_CLIENT_WITH_IPV6$])
+m4trace:configure.ac:1553: -1- m4_pattern_allow([^SMIME$])
+m4trace:configure.ac:1554: -1- m4_pattern_allow([^SMIME_SSLCERTS$])
+m4trace:configure.ac:1603: -1- m4_pattern_allow([^C_CLIENT_CFLAGS$])
+m4trace:configure.ac:1614: -1- m4_pattern_allow([^C_CLIENT_LDFLAGS$])
+m4trace:configure.ac:1618: -1- m4_pattern_allow([^C_CLIENT_GCCOPTLEVEL$])
+m4trace:configure.ac:1621: -1- m4_pattern_allow([^C_CLIENT_SPECIALS$])
+m4trace:configure.ac:1641: -1- m4_pattern_allow([^PUBCOOKIE$])
+m4trace:configure.ac:1648: -1- m4_pattern_allow([^REGEX_BUILD$])
+m4trace:configure.ac:1650: -1- m4_pattern_allow([^WEB_BUILD$])
+m4trace:configure.ac:1651: -1- m4_pattern_allow([^WEB_BINDIR$])
+m4trace:configure.ac:1652: -1- m4_pattern_allow([^WEB_PUBCOOKIE_BUILD$])
+m4trace:configure.ac:1653: -1- m4_pattern_allow([^WEB_PUBCOOKIE_LIB$])
+m4trace:configure.ac:1654: -1- m4_pattern_allow([^WEB_PUBCOOKIE_LINK$])
+m4trace:configure.ac:1656: -1- m4_pattern_allow([^AM_CFLAGS$])
+m4trace:configure.ac:1657: -1- m4_pattern_allow([^AM_LDFLAGS$])
+m4trace:configure.ac:1659: -1- _m4_warn([obsolete], [AC_OUTPUT should be used without arguments.
+You should run autoupdate.], [])
+m4trace:configure.ac:1659: -1- m4_pattern_allow([^LIB@&t@OBJS$])
+m4trace:configure.ac:1659: -1- m4_pattern_allow([^LTLIBOBJS$])
+m4trace:configure.ac:1659: -1- AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])
+m4trace:configure.ac:1659: -1- m4_pattern_allow([^am__EXEEXT_TRUE$])
+m4trace:configure.ac:1659: -1- m4_pattern_allow([^am__EXEEXT_FALSE$])
+m4trace:configure.ac:1659: -1- _AM_SUBST_NOTMAKE([am__EXEEXT_TRUE])
+m4trace:configure.ac:1659: -1- _AM_SUBST_NOTMAKE([am__EXEEXT_FALSE])
+m4trace:configure.ac:1659: -1- _AC_AM_CONFIG_HEADER_HOOK(["$ac_file"])
+m4trace:configure.ac:1659: -1- _AM_OUTPUT_DEPENDENCY_COMMANDS
+m4trace:configure.ac:1659: -1- _LT_PROG_LTMAIN
diff --git a/build.bat b/build.bat
new file mode 100644
index 00000000..f73c0fbd
--- /dev/null
+++ b/build.bat
@@ -0,0 +1,223 @@
+@echo OFF
+rem $Id: build.bat 14098 2005-10-03 18:54:13Z jpf@u.washington.edu $
+rem ========================================================================
+rem Copyright 2006-2007 University of Washington
+rem
+rem Licensed under the Apache License, Version 2.0 (the "License");
+rem you may not use this file except in compliance with the License.
+rem You may obtain a copy of the License at
+rem
+rem http://www.apache.org/licenses/LICENSE-2.0
+rem
+rem ========================================================================
+
+if "%1"=="" goto blank
+if "%1"=="wnt" goto wnt
+if "%1"=="w2k" goto w2k
+if "%1"=="clean" goto clean
+echo Unknown build command: %1 %2 %3 %4
+goto usage
+:blank
+echo Must specify build command!
+:usage
+echo usage: BUILD cmd
+echo where "cmd" is one of either:
+echo wnt -- Windows
+echo w2k -- Windows with Win2k Kerb
+echo clean -- to remove obj, lib, and exe files from source
+goto fini
+
+:wnt
+echo PC-Pine for Windows/Winsock (Win32) build sequence
+set cclntmake=makefile.nt
+set alpinemake=makefile.wnt
+if not defined ALPINE_LDAP set ALPINE_LDAP=%cd%\ldap
+if exist "%ALPINE_LDAP%" goto yesldapwnt
+echo NOT including LDAP functionality
+set ldapinclude=
+set ldaplibes=
+goto noldapwnt
+:yesldapwnt
+echo including LDAP functionality
+set ldapflags=-I\"%ALPINE_LDAP%\"\inckit -DENABLE_LDAP
+set ldaplibes=\"%ALPINE_LDAP%\"\binaries\release\ldap32.lib
+:noldapwnt
+set extracflagsnq=/Zi -Od %ldapflags% -D_USE_32BIT_TIME_T -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE -DSPCL_REMARKS=\"\\\"\\\"\"
+set extralibes=
+set extralibesalpine=%ldaplibes%
+set extrarcflags="/D_PCP_WNT"
+set extramakecommand=
+goto buildsetup
+
+:w2k
+echo Krb5ized PC-Pine for Windows/Winsock (Win32) build sequence
+set cclntmake=makefile.w2k
+set alpinemake=makefile.wnt
+if not defined ALPINE_LDAP set ALPINE_LDAP=%cd%\ldap
+if exist "%ALPINE_LDAP%" goto yesldapw2k
+echo NOT including LDAP functionality
+set ldapinclude=
+set ldaplibes=
+goto noldapw2k
+:yesldapw2k
+echo including LDAP functionality
+set ldapflags=-I\"%ALPINE_LDAP%\"\inckit -DENABLE_LDAP
+set ldaplibes=\"%ALPINE_LDAP%\"\binaries\release\ldap32.lib
+:noldapw2k
+set extracflagsnq=/Zi -Od %ldapflags% -D_USE_32BIT_TIME_T -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE -DSPCFC_WINVER=\"\\\" 2000\\\"\" -DSPCL_REMARKS=\"\\\" with krb5\\\"\"
+set extralibes="secur32.lib"
+set extralibesalpine="secur32.lib %ldaplibes%"
+set extrarcflags="/D_PCP_W2K"
+set extramakecommand=
+goto buildsetup
+
+:clean
+echo Sure you want to delete object, library and executable files?!?!
+echo If NOT, type Ctrl-C to terminate build script NOW. Type ENTER if you do.
+pause
+echo Cleaning alpine, pico, mailutil, mapi, and c-client directories
+set alpinemake=makefile.wnt
+set extramakecommand=clean
+if NOT exist c-client goto nocclient
+del /Q c-client\*
+rmdir c-client
+:nocclient
+if NOT exist c-client-dll goto nocclientdll
+del /Q c-client-dll\*
+rmdir c-client-dll
+:nocclientdll
+set cclntmake=makefile.w2k
+goto buildmailutil
+
+:buildsetup
+if not exist c-client mkdir c-client
+if not defined ALPINE_IMAP set ALPINE_IMAP=imap
+echo Copying imap files to c-client directory
+copy /Y "%ALPINE_IMAP%"\src\c-client\* c-client\ > garbageout.txt
+copy /Y "%ALPINE_IMAP%"\src\charset\* c-client\ > garbageout.txt
+copy /Y "%ALPINE_IMAP%"\src\osdep\nt\* c-client\ > garbageout.txt
+del garbageout.txt
+if not exist c-client-dll mkdir c-client-dll
+copy /Y "%ALPINE_IMAP%"\src\c-client\* c-client-dll\ > garbageout.txt
+copy /Y "%ALPINE_IMAP%"\src\charset\* c-client-dll\ > garbageout.txt
+copy /Y "%ALPINE_IMAP%"\src\osdep\nt\* c-client-dll\ > garbageout.txt
+del garbageout.txt
+if not exist mailutil mkdir mailutil
+copy /Y "%ALPINE_IMAP%"\src\mailutil\* mailutil\ > garbageout.txt
+del garbageout.txt
+goto build
+
+:build
+set extraldflags="/DEBUG /DEBUGTYPE:CV"
+set extracflags="%extracflagsnq%"
+set extradllcflags="%extracflagsnq% /D_DLL"
+goto buildcclnt
+
+:buildcclnt
+echo Building c-client...
+cd c-client
+nmake -nologo -f %cclntmake% EXTRACFLAGS=%extracflags% %extramakecommand%
+if errorlevel 1 goto bogus
+cd ..
+goto buildmailutil
+
+:buildmailutil
+if exist mailutil goto yesbuildmailutil
+goto nobuildmailutil
+:yesbuildmailutil
+echo Building mailutil
+cd mailutil
+nmake -nologo -f %cclntmake% EXTRACFLAGS=%extracflags% %extramakecommand%
+if errorlevel 1 goto bogus
+cd ..
+:nobuildmailutil
+goto buildpithosd
+
+:buildpithosd
+echo Building pith-osdep...
+cd pith\osdep
+nmake -nologo -f %alpinemake% wnt=1 EXTRACFLAGS=%extracflags% EXTRALDFLAGS=%extraldflags% EXTRALIBES=%extralibes% %extramakecommand%
+if errorlevel 1 goto bogus
+cd ..\..
+goto buildpithcc
+
+:buildpithcc
+echo Building pith-charconv...
+cd pith\charconv
+nmake -nologo -f %alpinemake% wnt=1 EXTRACFLAGS=%extracflags% EXTRALDFLAGS=%extraldflags% EXTRALIBES=%extralibes% %extramakecommand%
+if errorlevel 1 goto bogus
+cd ..\..
+goto buildpith
+
+:buildpith
+echo Building pith...
+cd pith
+nmake -nologo -f %alpinemake% wnt=1 EXTRACFLAGS=%extracflags% EXTRALDFLAGS=%extraldflags% EXTRALIBES=%extralibes% %extramakecommand%
+if errorlevel 1 goto bogus
+cd ..
+goto buildregex
+
+:buildregex
+echo Building regex...
+cd regex
+nmake -nologo -f %alpinemake% wnt=1 EXTRACFLAGS=%extracflags% EXTRALDFLAGS=%extraldflags% EXTRALIBES=%extralibes% %extramakecommand%
+if errorlevel 1 goto bogus
+cd ..
+goto buildpicoosd
+
+:buildpicoosd
+echo Building pico-osdep...
+cd pico\osdep
+nmake -nologo -f %alpinemake% wnt=1 EXTRACFLAGS=%extracflags% EXTRALDFLAGS=%extraldflags% EXTRALIBES=%extralibes% %extramakecommand%
+if errorlevel 1 goto bogus
+cd ..\..
+goto buildpico
+
+:buildpico
+echo Building pico...
+cd pico
+nmake -nologo -f %alpinemake% wnt=1 EXTRACFLAGS=%extracflags% EXTRALDFLAGS=%extraldflags% EXTRALIBES=%extralibes% %extramakecommand%
+if errorlevel 1 goto bogus
+cd ..
+goto buildalpineosd
+
+:buildalpineosd
+echo Building alpine-osdep...
+cd alpine\osdep
+nmake -nologo -f %alpinemake% wnt=1 EXTRACFLAGS=%extracflags% EXTRALDFLAGS=%extraldflags% EXTRALIBES=%extralibes% EXTRARCFLAGS=%extrarcflags% %extramakecommand%
+if errorlevel 1 goto bogus
+cd ..\..
+goto buildalpine
+
+:buildalpine
+echo Building alpine...
+cd alpine
+nmake -nologo -f %alpinemake% wnt=1 EXTRACFLAGS=%extracflags% EXTRALDFLAGS=%extraldflags% EXTRALIBES=%extralibesalpine% EXTRARCFLAGS=%extrarcflags% %extramakecommand%
+if errorlevel 1 goto bogus
+cd ..
+goto buildcclntdll
+
+:buildcclntdll
+if NOT exist c-client-dll goto buildmapi
+echo Building c-client-dll
+cd c-client-dll
+nmake -nologo -f %cclntmake% EXTRACFLAGS=%extradllcflags% %extramakecommand%
+if errorlevel 1 goto bogus
+cd ..
+goto buildmapi
+
+:buildmapi
+echo Building mapi
+cd mapi
+nmake -nologo -f makefile EXTRACFLAGS=%extracflags% EXTRALDFLAGS=%extraldflags% EXTRALIBES=%extralibes% %extramakecommand%
+if errorlevel 1 goto bogus
+cd ..
+
+:nobuildmapi
+echo Alpine build complete.
+goto fini
+
+:bogus
+echo Problems building Alpine!
+
+:fini
diff --git a/config.guess b/config.guess
new file mode 100755
index 00000000..666c5ad9
--- /dev/null
+++ b/config.guess
@@ -0,0 +1,1511 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+# Free Software Foundation, Inc.
+
+timestamp='2009-11-20'
+
+# This file 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., 51 Franklin Street - Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+
+# Originally written by Per Bothner. Please send patches (context
+# diff format) to <config-patches@gnu.org> and include a ChangeLog
+# entry.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub. If it succeeds, it prints the system name on stdout, and
+# exits with 0. Otherwise, it exits with 1.
+#
+# You can get the latest version of this script from:
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit ;;
+ --version | -v )
+ echo "$version" ; exit ;;
+ --help | --h* | -h )
+ echo "$usage"; exit ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help" >&2
+ exit 1 ;;
+ * )
+ break ;;
+ esac
+done
+
+if test $# != 0; then
+ echo "$me: too many arguments$help" >&2
+ exit 1
+fi
+
+trap 'exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+set_cc_for_build='
+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
+: ${TMPDIR=/tmp} ;
+ { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
+dummy=$tmp/dummy ;
+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
+case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,) echo "int x;" > $dummy.c ;
+ for c in cc gcc c89 c99 ; do
+ if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
+ CC_FOR_BUILD="$c"; break ;
+ fi ;
+ done ;
+ if test x"$CC_FOR_BUILD" = x ; then
+ CC_FOR_BUILD=no_compiler_found ;
+ fi
+ ;;
+ ,,*) CC_FOR_BUILD=$CC ;;
+ ,*,*) CC_FOR_BUILD=$HOST_CC ;;
+esac ; set_cc_for_build= ;'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+ PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+case "${UNAME_MACHINE}" in
+ i?86)
+ test -z "$VENDOR" && VENDOR=pc
+ ;;
+ *)
+ test -z "$VENDOR" && VENDOR=unknown
+ ;;
+esac
+test -f /etc/SuSE-release -o -f /.buildenv && VENDOR=suse
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+ *:NetBSD:*:*)
+ # NetBSD (nbsd) targets should (where applicable) match one or
+ # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+ # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
+ # switched to ELF, *-*-netbsd* would select the old
+ # object file format. This provides both forward
+ # compatibility and a consistent mechanism for selecting the
+ # object file format.
+ #
+ # Note: NetBSD doesn't particularly care about the vendor
+ # portion of the name. We always set it to "unknown".
+ sysctl="sysctl -n hw.machine_arch"
+ UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
+ /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+ case "${UNAME_MACHINE_ARCH}" in
+ armeb) machine=armeb-unknown ;;
+ arm*) machine=arm-unknown ;;
+ sh3el) machine=shl-unknown ;;
+ sh3eb) machine=sh-unknown ;;
+ sh5el) machine=sh5le-unknown ;;
+ *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
+ esac
+ # The Operating System including object format, if it has switched
+ # to ELF recently, or will in the future.
+ case "${UNAME_MACHINE_ARCH}" in
+ arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+ eval $set_cc_for_build
+ if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ELF__
+ then
+ # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+ # Return netbsd for either. FIX?
+ os=netbsd
+ else
+ os=netbsdelf
+ fi
+ ;;
+ *)
+ os=netbsd
+ ;;
+ esac
+ # The OS release
+ # Debian GNU/NetBSD machines have a different userland, and
+ # thus, need a distinct triplet. However, they do not need
+ # kernel version information, so it can be replaced with a
+ # suitable tag, in the style of linux-gnu.
+ case "${UNAME_VERSION}" in
+ Debian*)
+ release='-gnu'
+ ;;
+ *)
+ release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ ;;
+ esac
+ # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+ # contains redundant information, the shorter form:
+ # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+ echo "${machine}-${os}${release}"
+ exit ;;
+ *:OpenBSD:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
+ echo ${UNAME_MACHINE_ARCH}-${VENDOR}-openbsd${UNAME_RELEASE}
+ exit ;;
+ *:ekkoBSD:*:*)
+ echo ${UNAME_MACHINE}-${VENDOR}-ekkobsd${UNAME_RELEASE}
+ exit ;;
+ *:SolidBSD:*:*)
+ echo ${UNAME_MACHINE}-${VENDOR}-solidbsd${UNAME_RELEASE}
+ exit ;;
+ macppc:MirBSD:*:*)
+ echo powerpc-${VENDOR}-mirbsd${UNAME_RELEASE}
+ exit ;;
+ *:MirBSD:*:*)
+ echo ${UNAME_MACHINE}-${VENDOR}-mirbsd${UNAME_RELEASE}
+ exit ;;
+ alpha:OSF1:*:*)
+ case $UNAME_RELEASE in
+ *4.0)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+ ;;
+ *5.*)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+ ;;
+ esac
+ # According to Compaq, /usr/sbin/psrinfo has been available on
+ # OSF/1 and Tru64 systems produced since 1995. I hope that
+ # covers most systems running today. This code pipes the CPU
+ # types through head -n 1, so we only detect the type of CPU 0.
+ ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+ case "$ALPHA_CPU_TYPE" in
+ "EV4 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "EV4.5 (21064)")
+ UNAME_MACHINE="alpha" ;;
+ "LCA4 (21066/21068)")
+ UNAME_MACHINE="alpha" ;;
+ "EV5 (21164)")
+ UNAME_MACHINE="alphaev5" ;;
+ "EV5.6 (21164A)")
+ UNAME_MACHINE="alphaev56" ;;
+ "EV5.6 (21164PC)")
+ UNAME_MACHINE="alphapca56" ;;
+ "EV5.7 (21164PC)")
+ UNAME_MACHINE="alphapca57" ;;
+ "EV6 (21264)")
+ UNAME_MACHINE="alphaev6" ;;
+ "EV6.7 (21264A)")
+ UNAME_MACHINE="alphaev67" ;;
+ "EV6.8CB (21264C)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8AL (21264B)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.8CX (21264D)")
+ UNAME_MACHINE="alphaev68" ;;
+ "EV6.9A (21264/EV69A)")
+ UNAME_MACHINE="alphaev69" ;;
+ "EV7 (21364)")
+ UNAME_MACHINE="alphaev7" ;;
+ "EV7.9 (21364A)")
+ UNAME_MACHINE="alphaev79" ;;
+ esac
+ # A Pn.n version is a patched version.
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+ echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ exit ;;
+ Alpha\ *:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # Should we change UNAME_MACHINE based on the output of uname instead
+ # of the specific Alpha model?
+ echo alpha-pc-interix
+ exit ;;
+ 21064:Windows_NT:50:3)
+ echo alpha-dec-winnt3.5
+ exit ;;
+ Amiga*:UNIX_System_V:4.0:*)
+ echo m68k-${VENDOR}-sysv4
+ exit ;;
+ *:[Aa]miga[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-${VENDOR}-amigaos
+ exit ;;
+ *:[Mm]orph[Oo][Ss]:*:*)
+ echo ${UNAME_MACHINE}-${VENDOR}-morphos
+ exit ;;
+ *:OS/390:*:*)
+ echo i370-ibm-openedition
+ exit ;;
+ *:z/VM:*:*)
+ echo s390-ibm-zvmoe
+ exit ;;
+ *:OS400:*:*)
+ echo powerpc-ibm-os400
+ exit ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ echo arm-acorn-riscix${UNAME_RELEASE}
+ exit ;;
+ arm:riscos:*:*|arm:RISCOS:*:*)
+ echo arm-${VENDOR}-riscos
+ exit ;;
+ SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+ echo hppa1.1-hitachi-hiuxmpp
+ exit ;;
+ Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+ # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+ if test "`(/bin/universe) 2>/dev/null`" = att ; then
+ echo pyramid-pyramid-sysv3
+ else
+ echo pyramid-pyramid-bsd
+ fi
+ exit ;;
+ NILE*:*:*:dcosx)
+ echo pyramid-pyramid-svr4
+ exit ;;
+ DRS?6000:unix:4.0:6*)
+ echo sparc-icl-nx6
+ exit ;;
+ DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
+ case `/usr/bin/uname -p` in
+ sparc) echo sparc-icl-nx7; exit ;;
+ esac ;;
+ s390x:SunOS:*:*)
+ echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4H:SunOS:5.*:*)
+ echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+ echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
+ echo i386-pc-auroraux${UNAME_RELEASE}
+ exit ;;
+ i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
+ eval $set_cc_for_build
+ SUN_ARCH="i386"
+ # If there is a compiler, see if it is configured for 64-bit objects.
+ # Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
+ # This test works for both compilers.
+ if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+ if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ SUN_ARCH="x86_64"
+ fi
+ fi
+ echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4*:SunOS:6*:*)
+ # According to config.sub, this is the proper way to canonicalize
+ # SunOS6. Hard to guess exactly what SunOS6 will be like, but
+ # it's likely to be more like Solaris than SunOS4.
+ echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ sun4*:SunOS:*:*)
+ case "`/usr/bin/arch -k`" in
+ Series*|S4*)
+ UNAME_RELEASE=`uname -v`
+ ;;
+ esac
+ # Japanese Language versions have a version number like `4.1.3-JL'.
+ echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+ exit ;;
+ sun3*:SunOS:*:*)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ exit ;;
+ sun*:*:4.2BSD:*)
+ UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+ test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+ case "`/bin/arch`" in
+ sun3)
+ echo m68k-sun-sunos${UNAME_RELEASE}
+ ;;
+ sun4)
+ echo sparc-sun-sunos${UNAME_RELEASE}
+ ;;
+ esac
+ exit ;;
+ aushp:SunOS:*:*)
+ echo sparc-auspex-sunos${UNAME_RELEASE}
+ exit ;;
+ # The situation for MiNT is a little confusing. The machine name
+ # can be virtually everything (everything which is not
+ # "atarist" or "atariste" at least should have a processor
+ # > m68000). The system name ranges from "MiNT" over "FreeMiNT"
+ # to the lowercase version "mint" (or "freemint"). Finally
+ # the system name "TOS" denotes a system which is actually not
+ # MiNT. But MiNT is downward compatible to TOS, so this should
+ # be no problem.
+ atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+ echo m68k-atari-mint${UNAME_RELEASE}
+ exit ;;
+ milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+ echo m68k-milan-mint${UNAME_RELEASE}
+ exit ;;
+ hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+ echo m68k-hades-mint${UNAME_RELEASE}
+ exit ;;
+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+ echo m68k-${VENDOR}-mint${UNAME_RELEASE}
+ exit ;;
+ m68k:machten:*:*)
+ echo m68k-apple-machten${UNAME_RELEASE}
+ exit ;;
+ powerpc:machten:*:*)
+ echo powerpc-apple-machten${UNAME_RELEASE}
+ exit ;;
+ RISC*:Mach:*:*)
+ echo mips-dec-mach_bsd4.3
+ exit ;;
+ RISC*:ULTRIX:*:*)
+ echo mips-dec-ultrix${UNAME_RELEASE}
+ exit ;;
+ VAX*:ULTRIX*:*:*)
+ echo vax-dec-ultrix${UNAME_RELEASE}
+ exit ;;
+ 2020:CLIX:*:* | 2430:CLIX:*:*)
+ echo clipper-intergraph-clix${UNAME_RELEASE}
+ exit ;;
+ mips:*:*:UMIPS | mips:*:*:RISCos)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h> /* for printf() prototype */
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+ #if defined (host_mips) && defined (MIPSEB)
+ #if defined (SYSTYPE_SYSV)
+ printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_SVR4)
+ printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+ printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+ #endif
+ #endif
+ exit (-1);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c &&
+ dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` &&
+ SYSTEM_NAME=`$dummy $dummyarg` &&
+ { echo "$SYSTEM_NAME"; exit; }
+ echo mips-mips-riscos${UNAME_RELEASE}
+ exit ;;
+ Motorola:PowerMAX_OS:*:*)
+ echo powerpc-motorola-powermax
+ exit ;;
+ Motorola:*:4.3:PL8-*)
+ echo powerpc-harris-powermax
+ exit ;;
+ Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+ echo powerpc-harris-powermax
+ exit ;;
+ Night_Hawk:Power_UNIX:*:*)
+ echo powerpc-harris-powerunix
+ exit ;;
+ m88k:CX/UX:7*:*)
+ echo m88k-harris-cxux7
+ exit ;;
+ m88k:*:4*:R4*)
+ echo m88k-motorola-sysv4
+ exit ;;
+ m88k:*:3*:R3*)
+ echo m88k-motorola-sysv3
+ exit ;;
+ AViiON:dgux:*:*)
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+ then
+ if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+ [ ${TARGET_BINARY_INTERFACE}x = x ]
+ then
+ echo m88k-dg-dgux${UNAME_RELEASE}
+ else
+ echo m88k-dg-dguxbcs${UNAME_RELEASE}
+ fi
+ else
+ echo i586-dg-dgux${UNAME_RELEASE}
+ fi
+ exit ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ echo m88k-dolphin-sysv3
+ exit ;;
+ M88*:*:R3*:*)
+ # Delta 88k system running SVR3
+ echo m88k-motorola-sysv3
+ exit ;;
+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+ echo m88k-tektronix-sysv3
+ exit ;;
+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+ echo m68k-tektronix-bsd
+ exit ;;
+ *:IRIX*:*:*)
+ echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+ exit ;;
+ ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+ echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
+ exit ;; # Note that: echo "'`uname -s`'" gives 'AIX '
+ i*86:AIX:*:*)
+ echo i386-ibm-aix
+ exit ;;
+ ia64:AIX:*:*)
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+ exit ;;
+ *:AIX:2:3)
+ if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <sys/systemcfg.h>
+
+ main()
+ {
+ if (!__power_pc())
+ exit(1);
+ puts("powerpc-ibm-aix3.2.5");
+ exit(0);
+ }
+EOF
+ if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`
+ then
+ echo "$SYSTEM_NAME"
+ else
+ echo rs6000-ibm-aix3.2.5
+ fi
+ elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+ echo rs6000-ibm-aix3.2.4
+ else
+ echo rs6000-ibm-aix3.2
+ fi
+ exit ;;
+ *:AIX:*:[456])
+ IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+ if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+ IBM_ARCH=rs6000
+ else
+ IBM_ARCH=powerpc
+ fi
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ fi
+ echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+ exit ;;
+ *:AIX:*:*)
+ echo rs6000-ibm-aix
+ exit ;;
+ ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+ echo romp-ibm-bsd4.4
+ exit ;;
+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
+ echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to
+ exit ;; # report: romp-ibm BSD 4.3
+ *:BOSX:*:*)
+ echo rs6000-bull-bosx
+ exit ;;
+ DPX/2?00:B.O.S.:*:*)
+ echo m68k-bull-sysv3
+ exit ;;
+ 9000/[34]??:4.3bsd:1.*:*)
+ echo m68k-hp-bsd
+ exit ;;
+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+ echo m68k-hp-bsd4.4
+ exit ;;
+ 9000/[34678]??:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ case "${UNAME_MACHINE}" in
+ 9000/31? ) HP_ARCH=m68000 ;;
+ 9000/[34]?? ) HP_ARCH=m68k ;;
+ 9000/[678][0-9][0-9])
+ if [ -x /usr/bin/getconf ]; then
+ sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+ sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+ case "${sc_cpu_version}" in
+ 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+ 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+ 532) # CPU_PA_RISC2_0
+ case "${sc_kernel_bits}" in
+ 32) HP_ARCH="hppa2.0n" ;;
+ 64) HP_ARCH="hppa2.0w" ;;
+ '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20
+ esac ;;
+ esac
+ fi
+ if [ "${HP_ARCH}" = "" ]; then
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+
+ #define _HPUX_SOURCE
+ #include <stdlib.h>
+ #include <unistd.h>
+
+ int main ()
+ {
+ #if defined(_SC_KERNEL_BITS)
+ long bits = sysconf(_SC_KERNEL_BITS);
+ #endif
+ long cpu = sysconf (_SC_CPU_VERSION);
+
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+ case CPU_PA_RISC2_0:
+ #if defined(_SC_KERNEL_BITS)
+ switch (bits)
+ {
+ case 64: puts ("hppa2.0w"); break;
+ case 32: puts ("hppa2.0n"); break;
+ default: puts ("hppa2.0"); break;
+ } break;
+ #else /* !defined(_SC_KERNEL_BITS) */
+ puts ("hppa2.0"); break;
+ #endif
+ default: puts ("hppa1.0"); break;
+ }
+ exit (0);
+ }
+EOF
+ (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+ test -z "$HP_ARCH" && HP_ARCH=hppa
+ fi ;;
+ esac
+ if [ ${HP_ARCH} = "hppa2.0w" ]
+ then
+ eval $set_cc_for_build
+
+ # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
+ # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler
+ # generating 64-bit code. GNU and HP use different nomenclature:
+ #
+ # $ CC_FOR_BUILD=cc ./config.guess
+ # => hppa2.0w-hp-hpux11.23
+ # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
+ # => hppa64-hp-hpux11.23
+
+ if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
+ grep -q __LP64__
+ then
+ HP_ARCH="hppa2.0w"
+ else
+ HP_ARCH="hppa64"
+ fi
+ fi
+ echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+ exit ;;
+ ia64:HP-UX:*:*)
+ HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+ echo ia64-hp-hpux${HPUX_REV}
+ exit ;;
+ 3050*:HI-UX:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #include <unistd.h>
+ int
+ main ()
+ {
+ long cpu = sysconf (_SC_CPU_VERSION);
+ /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+ true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
+ results, however. */
+ if (CPU_IS_PA_RISC (cpu))
+ {
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+ default: puts ("hppa-hitachi-hiuxwe2"); break;
+ }
+ }
+ else if (CPU_IS_HP_MC68K (cpu))
+ puts ("m68k-hitachi-hiuxwe2");
+ else puts ("unknown-hitachi-hiuxwe2");
+ exit (0);
+ }
+EOF
+ $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` &&
+ { echo "$SYSTEM_NAME"; exit; }
+ echo unknown-hitachi-hiuxwe2
+ exit ;;
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+ echo hppa1.1-hp-bsd
+ exit ;;
+ 9000/8??:4.3bsd:*:*)
+ echo hppa1.0-hp-bsd
+ exit ;;
+ *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+ echo hppa1.0-hp-mpeix
+ exit ;;
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+ echo hppa1.1-hp-osf
+ exit ;;
+ hp8??:OSF1:*:*)
+ echo hppa1.0-hp-osf
+ exit ;;
+ i*86:OSF1:*:*)
+ if [ -x /usr/sbin/sysversion ] ; then
+ echo ${UNAME_MACHINE}-${VENDOR}-osf1mk
+ else
+ echo ${UNAME_MACHINE}-${VENDOR}-osf1
+ fi
+ exit ;;
+ parisc*:Lites*:*:*)
+ echo hppa1.1-hp-lites
+ exit ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ echo c1-convex-bsd
+ exit ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ echo c34-convex-bsd
+ exit ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ echo c38-convex-bsd
+ exit ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ echo c4-convex-bsd
+ exit ;;
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*[A-Z]90:*:*:*)
+ echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+ -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*TS:*:*:*)
+ echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*T3E:*:*:*)
+ echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*SV1:*:*:*)
+ echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ *:UNICOS/mp:*:*)
+ echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+ FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+ echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
+ 5000:UNIX_System_V:4.*:*)
+ FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
+ echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
+ i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+ echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+ exit ;;
+ sparc*:BSD/OS:*:*)
+ echo sparc-${VENDOR}-bsdi${UNAME_RELEASE}
+ exit ;;
+ *:BSD/OS:*:*)
+ echo ${UNAME_MACHINE}-${VENDOR}-bsdi${UNAME_RELEASE}
+ exit ;;
+ *:FreeBSD:*:*)
+ case ${UNAME_MACHINE} in
+ pc98)
+ echo i386-${VENDOR}-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ amd64)
+ echo x86_64-${VENDOR}-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ *)
+ echo ${UNAME_MACHINE}-${VENDOR}-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ esac
+ exit ;;
+ i*:CYGWIN*:*)
+ echo ${UNAME_MACHINE}-pc-cygwin
+ exit ;;
+ *:MINGW*:*)
+ echo ${UNAME_MACHINE}-pc-mingw32
+ exit ;;
+ i*:windows32*:*)
+ # uname -m includes "-pc" on this system.
+ echo ${UNAME_MACHINE}-mingw32
+ exit ;;
+ i*:PW*:*)
+ echo ${UNAME_MACHINE}-pc-pw32
+ exit ;;
+ *:Interix*:*)
+ case ${UNAME_MACHINE} in
+ x86)
+ echo i586-pc-interix${UNAME_RELEASE}
+ exit ;;
+ authenticamd | genuineintel | EM64T)
+ echo x86_64-${VENDOR}-interix${UNAME_RELEASE}
+ exit ;;
+ IA64)
+ echo ia64-${VENDOR}-interix${UNAME_RELEASE}
+ exit ;;
+ esac ;;
+ [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
+ echo i${UNAME_MACHINE}-pc-mks
+ exit ;;
+ 8664:Windows_NT:*)
+ echo x86_64-pc-mks
+ exit ;;
+ i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+ # How do we know it's Interix rather than the generic POSIX subsystem?
+ # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+ # UNAME_MACHINE based on the output of uname instead of i386?
+ echo i586-pc-interix
+ exit ;;
+ i*:UWIN*:*)
+ echo ${UNAME_MACHINE}-pc-uwin
+ exit ;;
+ amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
+ echo x86_64-${VENDOR}-cygwin
+ exit ;;
+ p*:CYGWIN*:*)
+ echo powerpcle-${VENDOR}-cygwin
+ exit ;;
+ prep*:SunOS:5.*:*)
+ echo powerpcle-${VENDOR}-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ exit ;;
+ *:GNU:*:*)
+ # the GNU system
+ echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-${VENDOR}-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+ exit ;;
+ *:GNU/*:*:*)
+ # other systems with GNU libc and userland
+ echo ${UNAME_MACHINE}-${VENDOR}-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
+ exit ;;
+ i*86:Minix:*:*)
+ echo ${UNAME_MACHINE}-pc-minix
+ exit ;;
+ alpha:Linux:*:*)
+ case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+ EV5) UNAME_MACHINE=alphaev5 ;;
+ EV56) UNAME_MACHINE=alphaev56 ;;
+ PCA56) UNAME_MACHINE=alphapca56 ;;
+ PCA57) UNAME_MACHINE=alphapca56 ;;
+ EV6) UNAME_MACHINE=alphaev6 ;;
+ EV67) UNAME_MACHINE=alphaev67 ;;
+ EV68*) UNAME_MACHINE=alphaev68 ;;
+ esac
+ objdump --private-headers /bin/sh | grep -q ld.so.1
+ if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+ echo ${UNAME_MACHINE}-${VENDOR}-linux-gnu${LIBC}
+ exit ;;
+ arm*:Linux:*:*)
+ eval $set_cc_for_build
+ if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_EABI__
+ then
+ echo ${UNAME_MACHINE}-${VENDOR}-linux-gnu
+ else
+ echo ${UNAME_MACHINE}-${VENDOR}-linux-gnueabi
+ fi
+ exit ;;
+ avr32*:Linux:*:*)
+ echo ${UNAME_MACHINE}-${VENDOR}-linux-gnu
+ exit ;;
+ cris:Linux:*:*)
+ echo cris-axis-linux-gnu
+ exit ;;
+ crisv32:Linux:*:*)
+ echo crisv32-axis-linux-gnu
+ exit ;;
+ frv:Linux:*:*)
+ echo frv-${VENDOR}-linux-gnu
+ exit ;;
+ i*86:Linux:*:*)
+ LIBC=gnu
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #ifdef __dietlibc__
+ LIBC=dietlibc
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'`
+ echo "${UNAME_MACHINE}-${VENDOR}-linux-${LIBC}"
+ exit ;;
+ ia64:Linux:*:*)
+ echo ${UNAME_MACHINE}-${VENDOR}-linux-gnu
+ exit ;;
+ m32r*:Linux:*:*)
+ echo ${UNAME_MACHINE}-${VENDOR}-linux-gnu
+ exit ;;
+ m68*:Linux:*:*)
+ echo ${UNAME_MACHINE}-${VENDOR}-linux-gnu
+ exit ;;
+ mips:Linux:*:* | mips64:Linux:*:*)
+ eval $set_cc_for_build
+ sed 's/^ //' << EOF >$dummy.c
+ #undef CPU
+ #undef ${UNAME_MACHINE}
+ #undef ${UNAME_MACHINE}el
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=${UNAME_MACHINE}el
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=${UNAME_MACHINE}
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
+ test x"${CPU}" != x && { echo "${CPU}-${VENDOR}-linux-gnu"; exit; }
+ ;;
+ or32:Linux:*:*)
+ echo or32-${VENDOR}-linux-gnu
+ exit ;;
+ padre:Linux:*:*)
+ echo sparc-${VENDOR}-linux-gnu
+ exit ;;
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ echo hppa64-${VENDOR}-linux-gnu
+ exit ;;
+ parisc:Linux:*:* | hppa:Linux:*:*)
+ # Look for CPU level
+ case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+ PA7*) echo hppa1.1-${VENDOR}-linux-gnu ;;
+ PA8*) echo hppa2.0-${VENDOR}-linux-gnu ;;
+ *) echo hppa-${VENDOR}-linux-gnu ;;
+ esac
+ exit ;;
+ ppc64:Linux:*:*)
+ echo powerpc64-${VENDOR}-linux-gnu
+ exit ;;
+ ppc:Linux:*:*)
+ echo powerpc-${VENDOR}-linux-gnu
+ exit ;;
+ s390:Linux:*:* | s390x:Linux:*:*)
+ echo ${UNAME_MACHINE}-ibm-linux
+ exit ;;
+ sh64*:Linux:*:*)
+ echo ${UNAME_MACHINE}-${VENDOR}-linux-gnu
+ exit ;;
+ sh*:Linux:*:*)
+ echo ${UNAME_MACHINE}-${VENDOR}-linux-gnu
+ exit ;;
+ sparc:Linux:*:* | sparc64:Linux:*:*)
+ echo ${UNAME_MACHINE}-${VENDOR}-linux-gnu
+ exit ;;
+ vax:Linux:*:*)
+ echo ${UNAME_MACHINE}-dec-linux-gnu
+ exit ;;
+ x86_64:Linux:*:*)
+ echo x86_64-${VENDOR}-linux-gnu
+ exit ;;
+ xtensa*:Linux:*:*)
+ echo ${UNAME_MACHINE}-${VENDOR}-linux-gnu
+ exit ;;
+ i*86:DYNIX/ptx:4*:*)
+ # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+ # earlier versions are messed up and put the nodename in both
+ # sysname and nodename.
+ echo i386-sequent-sysv4
+ exit ;;
+ i*86:UNIX_SV:4.2MP:2.*)
+ # Unixware is an offshoot of SVR4, but it has its own version
+ # number series starting with 2...
+ # I am not positive that other SVR4 systems won't match this,
+ # I just have to hope. -- rms.
+ # Use sysv4.2uw... so that sysv4* matches it.
+ echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+ exit ;;
+ i*86:OS/2:*:*)
+ # If we were able to find `uname', then EMX Unix compatibility
+ # is probably installed.
+ echo ${UNAME_MACHINE}-pc-os2-emx
+ exit ;;
+ i*86:XTS-300:*:STOP)
+ echo ${UNAME_MACHINE}-${VENDOR}-stop
+ exit ;;
+ i*86:atheos:*:*)
+ echo ${UNAME_MACHINE}-${VENDOR}-atheos
+ exit ;;
+ i*86:syllable:*:*)
+ echo ${UNAME_MACHINE}-pc-syllable
+ exit ;;
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
+ echo i386-${VENDOR}-lynxos${UNAME_RELEASE}
+ exit ;;
+ i*86:*DOS:*:*)
+ echo ${UNAME_MACHINE}-pc-msdosdjgpp
+ exit ;;
+ i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+ UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+ if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+ echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+ else
+ echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+ fi
+ exit ;;
+ i*86:*:5:[678]*)
+ # UnixWare 7.x, OpenUNIX and OpenServer 6.
+ case `/bin/uname -X | grep "^Machine"` in
+ *486*) UNAME_MACHINE=i486 ;;
+ *Pentium) UNAME_MACHINE=i586 ;;
+ *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+ esac
+ echo ${UNAME_MACHINE}-${VENDOR}-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+ exit ;;
+ i*86:*:3.2:*)
+ if test -f /usr/options/cb.name; then
+ UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+ echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+ elif /bin/uname -X 2>/dev/null >/dev/null ; then
+ UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+ (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+ (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+ && UNAME_MACHINE=i686
+ (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+ && UNAME_MACHINE=i686
+ echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+ else
+ echo ${UNAME_MACHINE}-pc-sysv32
+ fi
+ exit ;;
+ pc:*:*:*)
+ # Left here for compatibility:
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i586.
+ # Note: whatever this is, it MUST be the same as what config.sub
+ # prints for the "djgpp" host, or else GDB configury will decide that
+ # this is a cross-build.
+ echo i586-pc-msdosdjgpp
+ exit ;;
+ Intel:Mach:3*:*)
+ echo i386-pc-mach3
+ exit ;;
+ paragon:*:*:*)
+ echo i860-intel-osf1
+ exit ;;
+ i860:*:4.*:*) # i860-SVR4
+ if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+ echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+ else # Add other i860-SVR4 vendors below as they are discovered.
+ echo i860-${VENDOR}-sysv${UNAME_RELEASE} # Unknown i860-SVR4
+ fi
+ exit ;;
+ mini*:CTIX:SYS*5:*)
+ # "miniframe"
+ echo m68010-convergent-sysv
+ exit ;;
+ mc68k:UNIX:SYSTEM5:3.51m)
+ echo m68k-convergent-sysv
+ exit ;;
+ M680?0:D-NIX:5.3:*)
+ echo m68k-diab-dnix
+ exit ;;
+ M68*:*:R3V[5678]*:*)
+ test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
+ 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
+ OS_REL=''
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4; exit; } ;;
+ NCR*:*:4.2:* | MPRAS*:*:4.2:*)
+ OS_REL='.3'
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && { echo i586-ncr-sysv4.3${OS_REL}; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
+ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
+ m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+ echo m68k-${VENDOR}-lynxos${UNAME_RELEASE}
+ exit ;;
+ mc68030:UNIX_System_V:4.*:*)
+ echo m68k-atari-sysv4
+ exit ;;
+ TSUNAMI:LynxOS:2.*:*)
+ echo sparc-${VENDOR}-lynxos${UNAME_RELEASE}
+ exit ;;
+ rs6000:LynxOS:2.*:*)
+ echo rs6000-${VENDOR}-lynxos${UNAME_RELEASE}
+ exit ;;
+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
+ echo powerpc-${VENDOR}-lynxos${UNAME_RELEASE}
+ exit ;;
+ SM[BE]S:UNIX_SV:*:*)
+ echo mips-dde-sysv${UNAME_RELEASE}
+ exit ;;
+ RM*:ReliantUNIX-*:*:*)
+ echo mips-sni-sysv4
+ exit ;;
+ RM*:SINIX-*:*:*)
+ echo mips-sni-sysv4
+ exit ;;
+ *:SINIX-*:*:*)
+ if uname -p 2>/dev/null >/dev/null ; then
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ echo ${UNAME_MACHINE}-sni-sysv4
+ else
+ echo ns32k-sni-sysv
+ fi
+ exit ;;
+ PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says <Richard.M.Bartel@ccMail.Census.GOV>
+ echo i586-unisys-sysv4
+ exit ;;
+ *:UNIX_System_V:4*:FTX*)
+ # From Gerald Hewes <hewes@openmarket.com>.
+ # How about differentiating between stratus architectures? -djm
+ echo hppa1.1-stratus-sysv4
+ exit ;;
+ *:*:*:FTX*)
+ # From seanf@swdc.stratus.com.
+ echo i860-stratus-sysv4
+ exit ;;
+ i*86:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo ${UNAME_MACHINE}-stratus-vos
+ exit ;;
+ *:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo hppa1.1-stratus-vos
+ exit ;;
+ mc68*:A/UX:*:*)
+ echo m68k-apple-aux${UNAME_RELEASE}
+ exit ;;
+ news*:NEWS-OS:6*:*)
+ echo mips-sony-newsos6
+ exit ;;
+ R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+ if [ -d /usr/nec ]; then
+ echo mips-nec-sysv${UNAME_RELEASE}
+ else
+ echo mips-${VENDOR}-sysv${UNAME_RELEASE}
+ fi
+ exit ;;
+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
+ echo powerpc-be-beos
+ exit ;;
+ BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
+ echo powerpc-apple-beos
+ exit ;;
+ BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
+ echo i586-pc-beos
+ exit ;;
+ BePC:Haiku:*:*) # Haiku running on Intel PC compatible.
+ echo i586-pc-haiku
+ exit ;;
+ SX-4:SUPER-UX:*:*)
+ echo sx4-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-5:SUPER-UX:*:*)
+ echo sx5-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-6:SUPER-UX:*:*)
+ echo sx6-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-7:SUPER-UX:*:*)
+ echo sx7-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-8:SUPER-UX:*:*)
+ echo sx8-nec-superux${UNAME_RELEASE}
+ exit ;;
+ SX-8R:SUPER-UX:*:*)
+ echo sx8r-nec-superux${UNAME_RELEASE}
+ exit ;;
+ Power*:Rhapsody:*:*)
+ echo powerpc-apple-rhapsody${UNAME_RELEASE}
+ exit ;;
+ *:Rhapsody:*:*)
+ echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+ exit ;;
+ *:Darwin:*:*)
+ UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
+ case $UNAME_PROCESSOR in
+ i386)
+ eval $set_cc_for_build
+ if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+ if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ UNAME_PROCESSOR="x86_64"
+ fi
+ fi ;;
+ unknown) UNAME_PROCESSOR=powerpc ;;
+ esac
+ echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+ exit ;;
+ *:procnto*:*:* | *:QNX:[0123456789]*:*)
+ UNAME_PROCESSOR=`uname -p`
+ if test "$UNAME_PROCESSOR" = "x86"; then
+ UNAME_PROCESSOR=i386
+ UNAME_MACHINE=pc
+ fi
+ echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
+ exit ;;
+ *:QNX:*:4*)
+ echo i386-pc-qnx
+ exit ;;
+ NSE-?:NONSTOP_KERNEL:*:*)
+ echo nse-tandem-nsk${UNAME_RELEASE}
+ exit ;;
+ NSR-?:NONSTOP_KERNEL:*:*)
+ echo nsr-tandem-nsk${UNAME_RELEASE}
+ exit ;;
+ *:NonStop-UX:*:*)
+ echo mips-compaq-nonstopux
+ exit ;;
+ BS2000:POSIX*:*:*)
+ echo bs2000-siemens-sysv
+ exit ;;
+ DS/*:UNIX_System_V:*:*)
+ echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+ exit ;;
+ *:Plan9:*:*)
+ # "uname -m" is not consistent, so use $cputype instead. 386
+ # is converted to i386 for consistency with other x86
+ # operating systems.
+ if test "$cputype" = "386"; then
+ UNAME_MACHINE=i386
+ else
+ UNAME_MACHINE="$cputype"
+ fi
+ echo ${UNAME_MACHINE}-${VENDOR}-plan9
+ exit ;;
+ *:TOPS-10:*:*)
+ echo pdp10-${VENDOR}-tops10
+ exit ;;
+ *:TENEX:*:*)
+ echo pdp10-${VENDOR}-tenex
+ exit ;;
+ KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+ echo pdp10-dec-tops20
+ exit ;;
+ XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+ echo pdp10-xkl-tops20
+ exit ;;
+ *:TOPS-20:*:*)
+ echo pdp10-${VENDOR}-tops20
+ exit ;;
+ *:ITS:*:*)
+ echo pdp10-${VENDOR}-its
+ exit ;;
+ SEI:*:*:SEIUX)
+ echo mips-sei-seiux${UNAME_RELEASE}
+ exit ;;
+ *:DragonFly:*:*)
+ echo ${UNAME_MACHINE}-${VENDOR}-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+ exit ;;
+ *:*VMS:*:*)
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ case "${UNAME_MACHINE}" in
+ A*) echo alpha-dec-vms ; exit ;;
+ I*) echo ia64-dec-vms ; exit ;;
+ V*) echo vax-dec-vms ; exit ;;
+ esac ;;
+ *:XENIX:*:SysV)
+ echo i386-pc-xenix
+ exit ;;
+ i*86:skyos:*:*)
+ echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//'
+ exit ;;
+ i*86:rdos:*:*)
+ echo ${UNAME_MACHINE}-pc-rdos
+ exit ;;
+ i*86:AROS:*:*)
+ echo ${UNAME_MACHINE}-pc-aros
+ exit ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+eval $set_cc_for_build
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+ /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
+ I don't know.... */
+ printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+ printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+ "4"
+#else
+ ""
+#endif
+ ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+ printf ("arm-acorn-riscix\n"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+ printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+ int version;
+ version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+ if (version < 4)
+ printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+ else
+ printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+ exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+ printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+ printf ("ns32k-encore-mach\n"); exit (0);
+#else
+ printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+ printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+ printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+ printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+ struct utsname un;
+
+ uname(&un);
+
+ if (strncmp(un.version, "V2", 2) == 0) {
+ printf ("i386-sequent-ptx2\n"); exit (0);
+ }
+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+ printf ("i386-sequent-ptx1\n"); exit (0);
+ }
+ printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+# include <sys/param.h>
+# if defined (BSD)
+# if BSD == 43
+ printf ("vax-dec-bsd4.3\n"); exit (0);
+# else
+# if BSD == 199006
+ printf ("vax-dec-bsd4.3reno\n"); exit (0);
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# endif
+# else
+ printf ("vax-dec-bsd\n"); exit (0);
+# endif
+# else
+ printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+ printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+ exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` &&
+ { echo "$SYSTEM_NAME"; exit; }
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+ case `getsysinfo -f cpu_type` in
+ c1*)
+ echo c1-convex-bsd
+ exit ;;
+ c2*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit ;;
+ c34*)
+ echo c34-convex-bsd
+ exit ;;
+ c38*)
+ echo c38-convex-bsd
+ exit ;;
+ c4*)
+ echo c4-convex-bsd
+ exit ;;
+ esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+ http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
+and
+ http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo = `(hostinfo) 2>/dev/null`
+/bin/universe = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/config.rpath b/config.rpath
new file mode 100755
index 00000000..c492a93b
--- /dev/null
+++ b/config.rpath
@@ -0,0 +1,614 @@
+#! /bin/sh
+# Output a system dependent set of variables, describing how to set the
+# run time search path of shared libraries in an executable.
+#
+# Copyright 1996-2006 Free Software Foundation, Inc.
+# Taken from GNU libtool, 2001
+# Originally by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+#
+# The first argument passed to this file is the canonical host specification,
+# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or
+# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# The environment variables CC, GCC, LDFLAGS, LD, with_gnu_ld
+# should be set by the caller.
+#
+# The set of defined variables is at the end of this script.
+
+# Known limitations:
+# - On IRIX 6.5 with CC="cc", the run time search patch must not be longer
+# than 256 bytes, otherwise the compiler driver will dump core. The only
+# known workaround is to choose shorter directory names for the build
+# directory and/or the installation directory.
+
+# All known linkers require a `.a' archive for static linking (except MSVC,
+# which needs '.lib').
+libext=a
+shrext=.so
+
+host="$1"
+host_cpu=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+host_vendor=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+host_os=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+
+# Code taken from libtool.m4's _LT_CC_BASENAME.
+
+for cc_temp in $CC""; do
+ case $cc_temp in
+ compile | *[\\/]compile | ccache | *[\\/]ccache ) ;;
+ distcc | *[\\/]distcc | purify | *[\\/]purify ) ;;
+ \-*) ;;
+ *) break;;
+ esac
+done
+cc_basename=`echo "$cc_temp" | sed -e 's%^.*/%%'`
+
+# Code taken from libtool.m4's AC_LIBTOOL_PROG_COMPILER_PIC.
+
+wl=
+if test "$GCC" = yes; then
+ wl='-Wl,'
+else
+ case "$host_os" in
+ aix*)
+ wl='-Wl,'
+ ;;
+ darwin*)
+ case $cc_basename in
+ xlc*)
+ wl='-Wl,'
+ ;;
+ esac
+ ;;
+ mingw* | pw32* | os2*)
+ ;;
+ hpux9* | hpux10* | hpux11*)
+ wl='-Wl,'
+ ;;
+ irix5* | irix6* | nonstopux*)
+ wl='-Wl,'
+ ;;
+ newsos6)
+ ;;
+ linux*)
+ case $cc_basename in
+ icc* | ecc*)
+ wl='-Wl,'
+ ;;
+ pgcc | pgf77 | pgf90)
+ wl='-Wl,'
+ ;;
+ ccc*)
+ wl='-Wl,'
+ ;;
+ como)
+ wl='-lopt='
+ ;;
+ *)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*)
+ wl='-Wl,'
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ osf3* | osf4* | osf5*)
+ wl='-Wl,'
+ ;;
+ sco3.2v5*)
+ ;;
+ solaris*)
+ wl='-Wl,'
+ ;;
+ sunos4*)
+ wl='-Qoption ld '
+ ;;
+ sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+ wl='-Wl,'
+ ;;
+ sysv4*MP*)
+ ;;
+ unicos*)
+ wl='-Wl,'
+ ;;
+ uts4*)
+ ;;
+ esac
+fi
+
+# Code taken from libtool.m4's AC_LIBTOOL_PROG_LD_SHLIBS.
+
+hardcode_libdir_flag_spec=
+hardcode_libdir_separator=
+hardcode_direct=no
+hardcode_minus_L=no
+
+case "$host_os" in
+ cygwin* | mingw* | pw32*)
+ # FIXME: the MSVC++ port hasn't been tested in a loooong time
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ if test "$GCC" != yes; then
+ with_gnu_ld=no
+ fi
+ ;;
+ interix*)
+ # we just hope/assume this is gcc and not c89 (= MSVC++)
+ with_gnu_ld=yes
+ ;;
+ openbsd*)
+ with_gnu_ld=no
+ ;;
+esac
+
+ld_shlibs=yes
+if test "$with_gnu_ld" = yes; then
+ # Set some defaults for GNU ld with shared library support. These
+ # are reset later if shared libraries are not supported. Putting them
+ # here allows them to be overridden if necessary.
+ # Unlike libtool, we use -rpath here, not --rpath, since the documented
+ # option of GNU ld is called -rpath, not --rpath.
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ case "$host_os" in
+ aix3* | aix4* | aix5*)
+ # On AIX/PPC, the GNU linker is very broken
+ if test "$host_cpu" != ia64; then
+ ld_shlibs=no
+ fi
+ ;;
+ amigaos*)
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ # Samuel A. Falvo II <kc5tja@dolphin.openprojects.net> reports
+ # that the semantics of dynamic libraries on AmigaOS, at least up
+ # to version 4, is to share data among multiple programs linked
+ # with the same dynamic library. Since this doesn't match the
+ # behavior of shared libraries on other platforms, we cannot use
+ # them.
+ ld_shlibs=no
+ ;;
+ beos*)
+ if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
+ :
+ else
+ ld_shlibs=no
+ fi
+ ;;
+ cygwin* | mingw* | pw32*)
+ # hardcode_libdir_flag_spec is actually meaningless, as there is
+ # no search path for DLLs.
+ hardcode_libdir_flag_spec='-L$libdir'
+ if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then
+ :
+ else
+ ld_shlibs=no
+ fi
+ ;;
+ interix3*)
+ hardcode_direct=no
+ hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+ ;;
+ linux*)
+ if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
+ :
+ else
+ ld_shlibs=no
+ fi
+ ;;
+ netbsd*)
+ ;;
+ solaris*)
+ if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then
+ ld_shlibs=no
+ elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
+ :
+ else
+ ld_shlibs=no
+ fi
+ ;;
+ sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*)
+ case `$LD -v 2>&1` in
+ *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*)
+ ld_shlibs=no
+ ;;
+ *)
+ if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
+ hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-rpath,$libdir`'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+ esac
+ ;;
+ sunos4*)
+ hardcode_direct=yes
+ ;;
+ *)
+ if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
+ :
+ else
+ ld_shlibs=no
+ fi
+ ;;
+ esac
+ if test "$ld_shlibs" = no; then
+ hardcode_libdir_flag_spec=
+ fi
+else
+ case "$host_os" in
+ aix3*)
+ # Note: this linker hardcodes the directories in LIBPATH if there
+ # are no directories specified by -L.
+ hardcode_minus_L=yes
+ if test "$GCC" = yes; then
+ # Neither direct hardcoding nor static linking is supported with a
+ # broken collect2.
+ hardcode_direct=unsupported
+ fi
+ ;;
+ aix4* | aix5*)
+ if test "$host_cpu" = ia64; then
+ # On IA64, the linker does run time linking by default, so we don't
+ # have to do anything special.
+ aix_use_runtimelinking=no
+ else
+ aix_use_runtimelinking=no
+ # Test if we are trying to use run time linking or normal
+ # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+ # need to do runtime linking.
+ case $host_os in aix4.[23]|aix4.[23].*|aix5*)
+ for ld_flag in $LDFLAGS; do
+ if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then
+ aix_use_runtimelinking=yes
+ break
+ fi
+ done
+ ;;
+ esac
+ fi
+ hardcode_direct=yes
+ hardcode_libdir_separator=':'
+ if test "$GCC" = yes; then
+ case $host_os in aix4.[012]|aix4.[012].*)
+ collect2name=`${CC} -print-prog-name=collect2`
+ if test -f "$collect2name" && \
+ strings "$collect2name" | grep resolve_lib_name >/dev/null
+ then
+ # We have reworked collect2
+ hardcode_direct=yes
+ else
+ # We have old collect2
+ hardcode_direct=unsupported
+ hardcode_minus_L=yes
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_libdir_separator=
+ fi
+ ;;
+ esac
+ fi
+ # Begin _LT_AC_SYS_LIBPATH_AIX.
+ echo 'int main () { return 0; }' > conftest.c
+ ${CC} ${LDFLAGS} conftest.c -o conftest
+ aix_libpath=`dump -H conftest 2>/dev/null | sed -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; }
+}'`
+ if test -z "$aix_libpath"; then
+ aix_libpath=`dump -HX64 conftest 2>/dev/null | sed -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; }
+}'`
+ fi
+ if test -z "$aix_libpath"; then
+ aix_libpath="/usr/lib:/lib"
+ fi
+ rm -f conftest.c conftest
+ # End _LT_AC_SYS_LIBPATH_AIX.
+ if test "$aix_use_runtimelinking" = yes; then
+ hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath"
+ else
+ if test "$host_cpu" = ia64; then
+ hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib'
+ else
+ hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath"
+ fi
+ fi
+ ;;
+ amigaos*)
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ # see comment about different semantics on the GNU ld section
+ ld_shlibs=no
+ ;;
+ bsdi[45]*)
+ ;;
+ cygwin* | mingw* | pw32*)
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ # hardcode_libdir_flag_spec is actually meaningless, as there is
+ # no search path for DLLs.
+ hardcode_libdir_flag_spec=' '
+ libext=lib
+ ;;
+ darwin* | rhapsody*)
+ hardcode_direct=no
+ if test "$GCC" = yes ; then
+ :
+ else
+ case $cc_basename in
+ xlc*)
+ ;;
+ *)
+ ld_shlibs=no
+ ;;
+ esac
+ fi
+ ;;
+ dgux*)
+ hardcode_libdir_flag_spec='-L$libdir'
+ ;;
+ freebsd1*)
+ ld_shlibs=no
+ ;;
+ freebsd2.2*)
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_direct=yes
+ ;;
+ freebsd2*)
+ hardcode_direct=yes
+ hardcode_minus_L=yes
+ ;;
+ freebsd* | kfreebsd*-gnu | dragonfly*)
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_direct=yes
+ ;;
+ hpux9*)
+ hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+ hardcode_libdir_separator=:
+ hardcode_direct=yes
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ hardcode_minus_L=yes
+ ;;
+ hpux10*)
+ if test "$with_gnu_ld" = no; then
+ hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+ hardcode_libdir_separator=:
+ hardcode_direct=yes
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ hardcode_minus_L=yes
+ fi
+ ;;
+ hpux11*)
+ if test "$with_gnu_ld" = no; then
+ hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+ hardcode_libdir_separator=:
+ case $host_cpu in
+ hppa*64*|ia64*)
+ hardcode_direct=no
+ ;;
+ *)
+ hardcode_direct=yes
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ hardcode_minus_L=yes
+ ;;
+ esac
+ fi
+ ;;
+ irix5* | irix6* | nonstopux*)
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ hardcode_libdir_separator=:
+ ;;
+ netbsd*)
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_direct=yes
+ ;;
+ newsos6)
+ hardcode_direct=yes
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ hardcode_libdir_separator=:
+ ;;
+ openbsd*)
+ hardcode_direct=yes
+ if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+ hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+ else
+ case "$host_os" in
+ openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*)
+ hardcode_libdir_flag_spec='-R$libdir'
+ ;;
+ *)
+ hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+ ;;
+ esac
+ fi
+ ;;
+ os2*)
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ ;;
+ osf3*)
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ hardcode_libdir_separator=:
+ ;;
+ osf4* | osf5*)
+ if test "$GCC" = yes; then
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ else
+ # Both cc and cxx compiler support -rpath directly
+ hardcode_libdir_flag_spec='-rpath $libdir'
+ fi
+ hardcode_libdir_separator=:
+ ;;
+ solaris*)
+ hardcode_libdir_flag_spec='-R$libdir'
+ ;;
+ sunos4*)
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_direct=yes
+ hardcode_minus_L=yes
+ ;;
+ sysv4)
+ case $host_vendor in
+ sni)
+ hardcode_direct=yes # is this really true???
+ ;;
+ siemens)
+ hardcode_direct=no
+ ;;
+ motorola)
+ hardcode_direct=no #Motorola manual says yes, but my tests say they lie
+ ;;
+ esac
+ ;;
+ sysv4.3*)
+ ;;
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ ld_shlibs=yes
+ fi
+ ;;
+ sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7*)
+ ;;
+ sysv5* | sco3.2v5* | sco5v6*)
+ hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-R,$libdir`'
+ hardcode_libdir_separator=':'
+ ;;
+ uts4*)
+ hardcode_libdir_flag_spec='-L$libdir'
+ ;;
+ *)
+ ld_shlibs=no
+ ;;
+ esac
+fi
+
+# Check dynamic linker characteristics
+# Code taken from libtool.m4's AC_LIBTOOL_SYS_DYNAMIC_LINKER.
+libname_spec='lib$name'
+case "$host_os" in
+ aix3*)
+ ;;
+ aix4* | aix5*)
+ ;;
+ amigaos*)
+ ;;
+ beos*)
+ ;;
+ bsdi[45]*)
+ ;;
+ cygwin* | mingw* | pw32*)
+ shrext=.dll
+ ;;
+ darwin* | rhapsody*)
+ shrext=.dylib
+ ;;
+ dgux*)
+ ;;
+ freebsd1*)
+ ;;
+ kfreebsd*-gnu)
+ ;;
+ freebsd* | dragonfly*)
+ ;;
+ gnu*)
+ ;;
+ hpux9* | hpux10* | hpux11*)
+ case $host_cpu in
+ ia64*)
+ shrext=.so
+ ;;
+ hppa*64*)
+ shrext=.sl
+ ;;
+ *)
+ shrext=.sl
+ ;;
+ esac
+ ;;
+ interix3*)
+ ;;
+ irix5* | irix6* | nonstopux*)
+ case "$host_os" in
+ irix5* | nonstopux*)
+ libsuff= shlibsuff=
+ ;;
+ *)
+ case $LD in
+ *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= ;;
+ *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 ;;
+ *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 ;;
+ *) libsuff= shlibsuff= ;;
+ esac
+ ;;
+ esac
+ ;;
+ linux*oldld* | linux*aout* | linux*coff*)
+ ;;
+ linux*)
+ ;;
+ knetbsd*-gnu)
+ ;;
+ netbsd*)
+ ;;
+ newsos6)
+ ;;
+ nto-qnx*)
+ ;;
+ openbsd*)
+ ;;
+ os2*)
+ libname_spec='$name'
+ shrext=.dll
+ ;;
+ osf3* | osf4* | osf5*)
+ ;;
+ solaris*)
+ ;;
+ sunos4*)
+ ;;
+ sysv4 | sysv4.3*)
+ ;;
+ sysv4*MP*)
+ ;;
+ sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+ ;;
+ uts4*)
+ ;;
+esac
+
+sed_quote_subst='s/\(["`$\\]\)/\\\1/g'
+escaped_wl=`echo "X$wl" | sed -e 's/^X//' -e "$sed_quote_subst"`
+shlibext=`echo "$shrext" | sed -e 's,^\.,,'`
+escaped_hardcode_libdir_flag_spec=`echo "X$hardcode_libdir_flag_spec" | sed -e 's/^X//' -e "$sed_quote_subst"`
+
+LC_ALL=C sed -e 's/^\([a-zA-Z0-9_]*\)=/acl_cv_\1=/' <<EOF
+
+# How to pass a linker flag through the compiler.
+wl="$escaped_wl"
+
+# Static library suffix (normally "a").
+libext="$libext"
+
+# Shared library suffix (normally "so").
+shlibext="$shlibext"
+
+# Flag to hardcode \$libdir into a binary during linking.
+# This must work even if \$libdir does not exist.
+hardcode_libdir_flag_spec="$escaped_hardcode_libdir_flag_spec"
+
+# Whether we need a single -rpath flag with a separated argument.
+hardcode_libdir_separator="$hardcode_libdir_separator"
+
+# Set to yes if using DIR/libNAME.so during linking hardcodes DIR into the
+# resulting binary.
+hardcode_direct="$hardcode_direct"
+
+# Set to yes if using the -LDIR flag during linking hardcodes DIR into the
+# resulting binary.
+hardcode_minus_L="$hardcode_minus_L"
+
+EOF
diff --git a/config.sub b/config.sub
new file mode 100755
index 00000000..2a55a507
--- /dev/null
+++ b/config.sub
@@ -0,0 +1,1705 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+# Free Software Foundation, Inc.
+
+timestamp='2009-11-20'
+
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine. It does not imply ALL GNU software can.
+#
+# This file 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., 51 Franklin Street - Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+
+# Please send patches to <config-patches@gnu.org>. Submit a context
+# diff and a properly formatted GNU ChangeLog entry.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# You can get the latest version of this script from:
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support. The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+ $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit ;;
+ --version | -v )
+ echo "$version" ; exit ;;
+ --help | --h* | -h )
+ echo "$usage"; exit ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help"
+ exit 1 ;;
+
+ *local*)
+ # First pass through any local machine types.
+ echo $1
+ exit ;;
+
+ * )
+ break ;;
+ esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+ exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+ exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+ nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \
+ uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \
+ kopensolaris*-gnu* | \
+ storm-chaos* | os2-emx* | rtmk-nova*)
+ os=-$maybe_os
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+ ;;
+ *)
+ basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+ if [ $basic_machine != $1 ]
+ then os=`echo $1 | sed 's/.*-/-/'`
+ else os=; fi
+ ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work. We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+ -sun*os*)
+ # Prevent following clause from handling this invalid input.
+ ;;
+ -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+ -apple | -axis | -knuth | -cray | -microblaze)
+ os=
+ basic_machine=$1
+ ;;
+ -bluegene*)
+ os=-cnk
+ ;;
+ -sim | -cisco | -oki | -wec | -winbond)
+ os=
+ basic_machine=$1
+ ;;
+ -scout)
+ ;;
+ -wrs)
+ os=-vxworks
+ basic_machine=$1
+ ;;
+ -chorusos*)
+ os=-chorusos
+ basic_machine=$1
+ ;;
+ -chorusrdb)
+ os=-chorusrdb
+ basic_machine=$1
+ ;;
+ -hiux*)
+ os=-hiuxwe2
+ ;;
+ -sco6)
+ os=-sco5v6
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco5)
+ os=-sco3.2v5
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco4)
+ os=-sco3.2v4
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2.[4-9]*)
+ os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2v[4-9]*)
+ # Don't forget version if it is 3.2v4 or newer.
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco5v6*)
+ # Don't forget version if it is 3.2v4 or newer.
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco*)
+ os=-sco3.2v2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -udk*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -isc)
+ os=-isc2.2
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -clix*)
+ basic_machine=clipper-intergraph
+ ;;
+ -isc*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -lynx*)
+ os=-lynxos
+ ;;
+ -ptx*)
+ basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+ ;;
+ -windowsnt*)
+ os=`echo $os | sed -e 's/windowsnt/winnt/'`
+ ;;
+ -psos*)
+ os=-psos
+ ;;
+ -mint | -mint[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+ # Recognize the basic CPU types without company name.
+ # Some are omitted here because they have special meanings below.
+ 1750a | 580 \
+ | a29k \
+ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
+ | am33_2.0 \
+ | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \
+ | bfin \
+ | c4x | clipper \
+ | d10v | d30v | dlx | dsp16xx \
+ | fido | fr30 | frv \
+ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | i370 | i860 | i960 | ia64 \
+ | ip2k | iq2000 \
+ | lm32 \
+ | m32c | m32r | m32rle | m68000 | m68k | m88k \
+ | maxq | mb | microblaze | mcore | mep | metag \
+ | mips | mipsbe | mipseb | mipsel | mipsle \
+ | mips16 \
+ | mips64 | mips64el \
+ | mips64octeon | mips64octeonel \
+ | mips64orion | mips64orionel \
+ | mips64r5900 | mips64r5900el \
+ | mips64vr | mips64vrel \
+ | mips64vr4100 | mips64vr4100el \
+ | mips64vr4300 | mips64vr4300el \
+ | mips64vr5000 | mips64vr5000el \
+ | mips64vr5900 | mips64vr5900el \
+ | mipsisa32 | mipsisa32el \
+ | mipsisa32r2 | mipsisa32r2el \
+ | mipsisa64 | mipsisa64el \
+ | mipsisa64r2 | mipsisa64r2el \
+ | mipsisa64sb1 | mipsisa64sb1el \
+ | mipsisa64sr71k | mipsisa64sr71kel \
+ | mipstx39 | mipstx39el \
+ | mn10200 | mn10300 \
+ | moxie \
+ | mt \
+ | msp430 \
+ | nios | nios2 \
+ | ns16k | ns32k \
+ | or32 \
+ | pdp10 | pdp11 | pj | pjl \
+ | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+ | pyramid \
+ | rx \
+ | score \
+ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
+ | sh64 | sh64le \
+ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
+ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \
+ | spu | strongarm \
+ | tahoe | thumb | tic4x | tic80 | tron \
+ | ubicom32 \
+ | v850 | v850e \
+ | we32k \
+ | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \
+ | z8k | z80)
+ basic_machine=$basic_machine-unknown
+ ;;
+ m6811 | m68hc11 | m6812 | m68hc12 | picochip)
+ # Motorola 68HC11/12.
+ basic_machine=$basic_machine-unknown
+ os=-none
+ ;;
+ m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+ ;;
+ ms1)
+ basic_machine=mt-unknown
+ ;;
+
+ # We use `pc' rather than `unknown'
+ # because (1) that's what they normally are, and
+ # (2) the word "unknown" tends to confuse beginning users.
+ i*86 | x86_64)
+ basic_machine=$basic_machine-pc
+ ;;
+ # Object if more than one company name word.
+ *-*-*)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+ # Recognize the basic CPU types with company name.
+ 580-* \
+ | a29k-* \
+ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
+ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
+ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \
+ | avr-* | avr32-* \
+ | bfin-* | bs2000-* \
+ | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
+ | clipper-* | craynv-* | cydra-* \
+ | d10v-* | d30v-* | dlx-* \
+ | elxsi-* \
+ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
+ | h8300-* | h8500-* \
+ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+ | i*86-* | i860-* | i960-* | ia64-* \
+ | ip2k-* | iq2000-* \
+ | lm32-* \
+ | m32c-* | m32r-* | m32rle-* \
+ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
+ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \
+ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
+ | mips16-* \
+ | mips64-* | mips64el-* \
+ | mips64octeon-* | mips64octeonel-* \
+ | mips64orion-* | mips64orionel-* \
+ | mips64r5900-* | mips64r5900el-* \
+ | mips64vr-* | mips64vrel-* \
+ | mips64vr4100-* | mips64vr4100el-* \
+ | mips64vr4300-* | mips64vr4300el-* \
+ | mips64vr5000-* | mips64vr5000el-* \
+ | mips64vr5900-* | mips64vr5900el-* \
+ | mipsisa32-* | mipsisa32el-* \
+ | mipsisa32r2-* | mipsisa32r2el-* \
+ | mipsisa64-* | mipsisa64el-* \
+ | mipsisa64r2-* | mipsisa64r2el-* \
+ | mipsisa64sb1-* | mipsisa64sb1el-* \
+ | mipsisa64sr71k-* | mipsisa64sr71kel-* \
+ | mipstx39-* | mipstx39el-* \
+ | mmix-* \
+ | mt-* \
+ | msp430-* \
+ | nios-* | nios2-* \
+ | none-* | np1-* | ns16k-* | ns32k-* \
+ | orion-* \
+ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+ | pyramid-* \
+ | romp-* | rs6000-* | rx-* \
+ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
+ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
+ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
+ | sparclite-* \
+ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \
+ | tahoe-* | thumb-* \
+ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* | tile-* \
+ | tron-* \
+ | ubicom32-* \
+ | v850-* | v850e-* | vax-* \
+ | we32k-* \
+ | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \
+ | xstormy16-* | xtensa*-* \
+ | ymp-* \
+ | z8k-* | z80-*)
+ ;;
+ # Recognize the basic CPU types without company name, with glob match.
+ xtensa*)
+ basic_machine=$basic_machine-unknown
+ ;;
+ # Recognize the various machine names and aliases which stand
+ # for a CPU type and a company and sometimes even an OS.
+ 386bsd)
+ basic_machine=i386-unknown
+ os=-bsd
+ ;;
+ 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+ basic_machine=m68000-att
+ ;;
+ 3b*)
+ basic_machine=we32k-att
+ ;;
+ a29khif)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ abacus)
+ basic_machine=abacus-unknown
+ ;;
+ adobe68k)
+ basic_machine=m68010-adobe
+ os=-scout
+ ;;
+ alliant | fx80)
+ basic_machine=fx80-alliant
+ ;;
+ altos | altos3068)
+ basic_machine=m68k-altos
+ ;;
+ am29k)
+ basic_machine=a29k-none
+ os=-bsd
+ ;;
+ amd64)
+ basic_machine=x86_64-pc
+ ;;
+ amd64-*)
+ basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ amdahl)
+ basic_machine=580-amdahl
+ os=-sysv
+ ;;
+ amiga | amiga-*)
+ basic_machine=m68k-unknown
+ ;;
+ amigaos | amigados)
+ basic_machine=m68k-unknown
+ os=-amigaos
+ ;;
+ amigaunix | amix)
+ basic_machine=m68k-unknown
+ os=-sysv4
+ ;;
+ apollo68)
+ basic_machine=m68k-apollo
+ os=-sysv
+ ;;
+ apollo68bsd)
+ basic_machine=m68k-apollo
+ os=-bsd
+ ;;
+ aros)
+ basic_machine=i386-pc
+ os=-aros
+ ;;
+ aux)
+ basic_machine=m68k-apple
+ os=-aux
+ ;;
+ balance)
+ basic_machine=ns32k-sequent
+ os=-dynix
+ ;;
+ blackfin)
+ basic_machine=bfin-unknown
+ os=-linux
+ ;;
+ blackfin-*)
+ basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
+ bluegene*)
+ basic_machine=powerpc-ibm
+ os=-cnk
+ ;;
+ c90)
+ basic_machine=c90-cray
+ os=-unicos
+ ;;
+ cegcc)
+ basic_machine=arm-unknown
+ os=-cegcc
+ ;;
+ convex-c1)
+ basic_machine=c1-convex
+ os=-bsd
+ ;;
+ convex-c2)
+ basic_machine=c2-convex
+ os=-bsd
+ ;;
+ convex-c32)
+ basic_machine=c32-convex
+ os=-bsd
+ ;;
+ convex-c34)
+ basic_machine=c34-convex
+ os=-bsd
+ ;;
+ convex-c38)
+ basic_machine=c38-convex
+ os=-bsd
+ ;;
+ cray | j90)
+ basic_machine=j90-cray
+ os=-unicos
+ ;;
+ craynv)
+ basic_machine=craynv-cray
+ os=-unicosmp
+ ;;
+ cr16)
+ basic_machine=cr16-unknown
+ os=-elf
+ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ ;;
+ crisv32 | crisv32-* | etraxfs*)
+ basic_machine=crisv32-axis
+ ;;
+ cris | cris-* | etrax*)
+ basic_machine=cris-axis
+ ;;
+ crx)
+ basic_machine=crx-unknown
+ os=-elf
+ ;;
+ da30 | da30-*)
+ basic_machine=m68k-da30
+ ;;
+ decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+ basic_machine=mips-dec
+ ;;
+ decsystem10* | dec10*)
+ basic_machine=pdp10-dec
+ os=-tops10
+ ;;
+ decsystem20* | dec20*)
+ basic_machine=pdp10-dec
+ os=-tops20
+ ;;
+ delta | 3300 | motorola-3300 | motorola-delta \
+ | 3300-motorola | delta-motorola)
+ basic_machine=m68k-motorola
+ ;;
+ delta88)
+ basic_machine=m88k-motorola
+ os=-sysv3
+ ;;
+ dicos)
+ basic_machine=i686-pc
+ os=-dicos
+ ;;
+ djgpp)
+ basic_machine=i586-pc
+ os=-msdosdjgpp
+ ;;
+ dpx20 | dpx20-*)
+ basic_machine=rs6000-bull
+ os=-bosx
+ ;;
+ dpx2* | dpx2*-bull)
+ basic_machine=m68k-bull
+ os=-sysv3
+ ;;
+ ebmon29k)
+ basic_machine=a29k-amd
+ os=-ebmon
+ ;;
+ elxsi)
+ basic_machine=elxsi-elxsi
+ os=-bsd
+ ;;
+ encore | umax | mmax)
+ basic_machine=ns32k-encore
+ ;;
+ es1800 | OSE68k | ose68k | ose | OSE)
+ basic_machine=m68k-ericsson
+ os=-ose
+ ;;
+ fx2800)
+ basic_machine=i860-alliant
+ ;;
+ genix)
+ basic_machine=ns32k-ns
+ ;;
+ gmicro)
+ basic_machine=tron-gmicro
+ os=-sysv
+ ;;
+ go32)
+ basic_machine=i386-pc
+ os=-go32
+ ;;
+ h3050r* | hiux*)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ h8300hms)
+ basic_machine=h8300-hitachi
+ os=-hms
+ ;;
+ h8300xray)
+ basic_machine=h8300-hitachi
+ os=-xray
+ ;;
+ h8500hms)
+ basic_machine=h8500-hitachi
+ os=-hms
+ ;;
+ harris)
+ basic_machine=m88k-harris
+ os=-sysv3
+ ;;
+ hp300-*)
+ basic_machine=m68k-hp
+ ;;
+ hp300bsd)
+ basic_machine=m68k-hp
+ os=-bsd
+ ;;
+ hp300hpux)
+ basic_machine=m68k-hp
+ os=-hpux
+ ;;
+ hp3k9[0-9][0-9] | hp9[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k2[0-9][0-9] | hp9k31[0-9])
+ basic_machine=m68000-hp
+ ;;
+ hp9k3[2-9][0-9])
+ basic_machine=m68k-hp
+ ;;
+ hp9k6[0-9][0-9] | hp6[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k7[0-79][0-9] | hp7[0-79][0-9])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k78[0-9] | hp78[0-9])
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][13679] | hp8[0-9][13679])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][0-9] | hp8[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hppa-next)
+ os=-nextstep3
+ ;;
+ hppaosf)
+ basic_machine=hppa1.1-hp
+ os=-osf
+ ;;
+ hppro)
+ basic_machine=hppa1.1-hp
+ os=-proelf
+ ;;
+ i370-ibm* | ibm*)
+ basic_machine=i370-ibm
+ ;;
+# I'm not sure what "Sysv32" means. Should this be sysv3.2?
+ i*86v32)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv32
+ ;;
+ i*86v4*)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv4
+ ;;
+ i*86v)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-sysv
+ ;;
+ i*86sol2)
+ basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+ os=-solaris2
+ ;;
+ i386mach)
+ basic_machine=i386-mach
+ os=-mach
+ ;;
+ i386-vsta | vsta)
+ basic_machine=i386-unknown
+ os=-vsta
+ ;;
+ iris | iris4d)
+ basic_machine=mips-sgi
+ case $os in
+ -irix*)
+ ;;
+ *)
+ os=-irix4
+ ;;
+ esac
+ ;;
+ isi68 | isi)
+ basic_machine=m68k-isi
+ os=-sysv
+ ;;
+ m68knommu)
+ basic_machine=m68k-unknown
+ os=-linux
+ ;;
+ m68knommu-*)
+ basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
+ m88k-omron*)
+ basic_machine=m88k-omron
+ ;;
+ magnum | m3230)
+ basic_machine=mips-mips
+ os=-sysv
+ ;;
+ merlin)
+ basic_machine=ns32k-utek
+ os=-sysv
+ ;;
+ microblaze)
+ basic_machine=microblaze-xilinx
+ ;;
+ mingw32)
+ basic_machine=i386-pc
+ os=-mingw32
+ ;;
+ mingw32ce)
+ basic_machine=arm-unknown
+ os=-mingw32ce
+ ;;
+ miniframe)
+ basic_machine=m68000-convergent
+ ;;
+ *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+ mips3*-*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+ ;;
+ mips3*)
+ basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+ ;;
+ monitor)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ morphos)
+ basic_machine=powerpc-unknown
+ os=-morphos
+ ;;
+ msdos)
+ basic_machine=i386-pc
+ os=-msdos
+ ;;
+ ms1-*)
+ basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
+ ;;
+ mvs)
+ basic_machine=i370-ibm
+ os=-mvs
+ ;;
+ ncr3000)
+ basic_machine=i486-ncr
+ os=-sysv4
+ ;;
+ netbsd386)
+ basic_machine=i386-unknown
+ os=-netbsd
+ ;;
+ netwinder)
+ basic_machine=armv4l-rebel
+ os=-linux
+ ;;
+ news | news700 | news800 | news900)
+ basic_machine=m68k-sony
+ os=-newsos
+ ;;
+ news1000)
+ basic_machine=m68030-sony
+ os=-newsos
+ ;;
+ news-3600 | risc-news)
+ basic_machine=mips-sony
+ os=-newsos
+ ;;
+ necv70)
+ basic_machine=v70-nec
+ os=-sysv
+ ;;
+ next | m*-next )
+ basic_machine=m68k-next
+ case $os in
+ -nextstep* )
+ ;;
+ -ns2*)
+ os=-nextstep2
+ ;;
+ *)
+ os=-nextstep3
+ ;;
+ esac
+ ;;
+ nh3000)
+ basic_machine=m68k-harris
+ os=-cxux
+ ;;
+ nh[45]000)
+ basic_machine=m88k-harris
+ os=-cxux
+ ;;
+ nindy960)
+ basic_machine=i960-intel
+ os=-nindy
+ ;;
+ mon960)
+ basic_machine=i960-intel
+ os=-mon960
+ ;;
+ nonstopux)
+ basic_machine=mips-compaq
+ os=-nonstopux
+ ;;
+ np1)
+ basic_machine=np1-gould
+ ;;
+ nsr-tandem)
+ basic_machine=nsr-tandem
+ ;;
+ op50n-* | op60c-*)
+ basic_machine=hppa1.1-oki
+ os=-proelf
+ ;;
+ openrisc | openrisc-*)
+ basic_machine=or32-unknown
+ ;;
+ os400)
+ basic_machine=powerpc-ibm
+ os=-os400
+ ;;
+ OSE68000 | ose68000)
+ basic_machine=m68000-ericsson
+ os=-ose
+ ;;
+ os68k)
+ basic_machine=m68k-none
+ os=-os68k
+ ;;
+ pa-hitachi)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ paragon)
+ basic_machine=i860-intel
+ os=-osf
+ ;;
+ parisc)
+ basic_machine=hppa-unknown
+ os=-linux
+ ;;
+ parisc-*)
+ basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
+ pbd)
+ basic_machine=sparc-tti
+ ;;
+ pbb)
+ basic_machine=m68k-tti
+ ;;
+ pc532 | pc532-*)
+ basic_machine=ns32k-pc532
+ ;;
+ pc98)
+ basic_machine=i386-pc
+ ;;
+ pc98-*)
+ basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentium | p5 | k5 | k6 | nexgen | viac3)
+ basic_machine=i586-pc
+ ;;
+ pentiumpro | p6 | 6x86 | athlon | athlon_*)
+ basic_machine=i686-pc
+ ;;
+ pentiumii | pentium2 | pentiumiii | pentium3)
+ basic_machine=i686-pc
+ ;;
+ pentium4)
+ basic_machine=i786-pc
+ ;;
+ pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+ basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumpro-* | p6-* | 6x86-* | athlon-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+ basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pentium4-*)
+ basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ pn)
+ basic_machine=pn-gould
+ ;;
+ power) basic_machine=power-ibm
+ ;;
+ ppc) basic_machine=powerpc-unknown
+ ;;
+ ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppcle | powerpclittle | ppc-le | powerpc-little)
+ basic_machine=powerpcle-unknown
+ ;;
+ ppcle-* | powerpclittle-*)
+ basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64) basic_machine=powerpc64-unknown
+ ;;
+ ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+ basic_machine=powerpc64le-unknown
+ ;;
+ ppc64le-* | powerpc64little-*)
+ basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ ps2)
+ basic_machine=i386-ibm
+ ;;
+ pw32)
+ basic_machine=i586-unknown
+ os=-pw32
+ ;;
+ rdos)
+ basic_machine=i386-pc
+ os=-rdos
+ ;;
+ rom68k)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ rm[46]00)
+ basic_machine=mips-siemens
+ ;;
+ rtpc | rtpc-*)
+ basic_machine=romp-ibm
+ ;;
+ s390 | s390-*)
+ basic_machine=s390-ibm
+ ;;
+ s390x | s390x-*)
+ basic_machine=s390x-ibm
+ ;;
+ sa29200)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ sb1)
+ basic_machine=mipsisa64sb1-unknown
+ ;;
+ sb1el)
+ basic_machine=mipsisa64sb1el-unknown
+ ;;
+ sde)
+ basic_machine=mipsisa32-sde
+ os=-elf
+ ;;
+ sei)
+ basic_machine=mips-sei
+ os=-seiux
+ ;;
+ sequent)
+ basic_machine=i386-sequent
+ ;;
+ sh)
+ basic_machine=sh-hitachi
+ os=-hms
+ ;;
+ sh5el)
+ basic_machine=sh5le-unknown
+ ;;
+ sh64)
+ basic_machine=sh64-unknown
+ ;;
+ sparclite-wrs | simso-wrs)
+ basic_machine=sparclite-wrs
+ os=-vxworks
+ ;;
+ sps7)
+ basic_machine=m68k-bull
+ os=-sysv2
+ ;;
+ spur)
+ basic_machine=spur-unknown
+ ;;
+ st2000)
+ basic_machine=m68k-tandem
+ ;;
+ stratus)
+ basic_machine=i860-stratus
+ os=-sysv4
+ ;;
+ sun2)
+ basic_machine=m68000-sun
+ ;;
+ sun2os3)
+ basic_machine=m68000-sun
+ os=-sunos3
+ ;;
+ sun2os4)
+ basic_machine=m68000-sun
+ os=-sunos4
+ ;;
+ sun3os3)
+ basic_machine=m68k-sun
+ os=-sunos3
+ ;;
+ sun3os4)
+ basic_machine=m68k-sun
+ os=-sunos4
+ ;;
+ sun4os3)
+ basic_machine=sparc-sun
+ os=-sunos3
+ ;;
+ sun4os4)
+ basic_machine=sparc-sun
+ os=-sunos4
+ ;;
+ sun4sol2)
+ basic_machine=sparc-sun
+ os=-solaris2
+ ;;
+ sun3 | sun3-*)
+ basic_machine=m68k-sun
+ ;;
+ sun4)
+ basic_machine=sparc-sun
+ ;;
+ sun386 | sun386i | roadrunner)
+ basic_machine=i386-sun
+ ;;
+ sv1)
+ basic_machine=sv1-cray
+ os=-unicos
+ ;;
+ symmetry)
+ basic_machine=i386-sequent
+ os=-dynix
+ ;;
+ t3e)
+ basic_machine=alphaev5-cray
+ os=-unicos
+ ;;
+ t90)
+ basic_machine=t90-cray
+ os=-unicos
+ ;;
+ tic54x | c54x*)
+ basic_machine=tic54x-unknown
+ os=-coff
+ ;;
+ tic55x | c55x*)
+ basic_machine=tic55x-unknown
+ os=-coff
+ ;;
+ tic6x | c6x*)
+ basic_machine=tic6x-unknown
+ os=-coff
+ ;;
+ tile*)
+ basic_machine=tile-unknown
+ os=-linux-gnu
+ ;;
+ tx39)
+ basic_machine=mipstx39-unknown
+ ;;
+ tx39el)
+ basic_machine=mipstx39el-unknown
+ ;;
+ toad1)
+ basic_machine=pdp10-xkl
+ os=-tops20
+ ;;
+ tower | tower-32)
+ basic_machine=m68k-ncr
+ ;;
+ tpf)
+ basic_machine=s390x-ibm
+ os=-tpf
+ ;;
+ udi29k)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ ultra3)
+ basic_machine=a29k-nyu
+ os=-sym1
+ ;;
+ v810 | necv810)
+ basic_machine=v810-nec
+ os=-none
+ ;;
+ vaxv)
+ basic_machine=vax-dec
+ os=-sysv
+ ;;
+ vms)
+ basic_machine=vax-dec
+ os=-vms
+ ;;
+ vpp*|vx|vx-*)
+ basic_machine=f301-fujitsu
+ ;;
+ vxworks960)
+ basic_machine=i960-wrs
+ os=-vxworks
+ ;;
+ vxworks68)
+ basic_machine=m68k-wrs
+ os=-vxworks
+ ;;
+ vxworks29k)
+ basic_machine=a29k-wrs
+ os=-vxworks
+ ;;
+ w65*)
+ basic_machine=w65-wdc
+ os=-none
+ ;;
+ w89k-*)
+ basic_machine=hppa1.1-winbond
+ os=-proelf
+ ;;
+ xbox)
+ basic_machine=i686-pc
+ os=-mingw32
+ ;;
+ xps | xps100)
+ basic_machine=xps100-honeywell
+ ;;
+ ymp)
+ basic_machine=ymp-cray
+ os=-unicos
+ ;;
+ z8k-*-coff)
+ basic_machine=z8k-unknown
+ os=-sim
+ ;;
+ z80-*-coff)
+ basic_machine=z80-unknown
+ os=-sim
+ ;;
+ none)
+ basic_machine=none-none
+ os=-none
+ ;;
+
+# Here we handle the default manufacturer of certain CPU types. It is in
+# some cases the only manufacturer, in others, it is the most popular.
+ w89k)
+ basic_machine=hppa1.1-winbond
+ ;;
+ op50n)
+ basic_machine=hppa1.1-oki
+ ;;
+ op60c)
+ basic_machine=hppa1.1-oki
+ ;;
+ romp)
+ basic_machine=romp-ibm
+ ;;
+ mmix)
+ basic_machine=mmix-knuth
+ ;;
+ rs6000)
+ basic_machine=rs6000-ibm
+ ;;
+ vax)
+ basic_machine=vax-dec
+ ;;
+ pdp10)
+ # there are many clones, so DEC is not a safe bet
+ basic_machine=pdp10-unknown
+ ;;
+ pdp11)
+ basic_machine=pdp11-dec
+ ;;
+ we32k)
+ basic_machine=we32k-att
+ ;;
+ sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele)
+ basic_machine=sh-unknown
+ ;;
+ sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
+ basic_machine=sparc-sun
+ ;;
+ cydra)
+ basic_machine=cydra-cydrome
+ ;;
+ orion)
+ basic_machine=orion-highlevel
+ ;;
+ orion105)
+ basic_machine=clipper-highlevel
+ ;;
+ mac | mpw | mac-mpw)
+ basic_machine=m68k-apple
+ ;;
+ pmac | pmac-mpw)
+ basic_machine=powerpc-apple
+ ;;
+ *-unknown)
+ # Make sure to match an already-canonicalized machine name.
+ ;;
+ *)
+ echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+ *-digital*)
+ basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+ ;;
+ *-commodore*)
+ basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+ ;;
+ *)
+ ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+ # First match some system type aliases
+ # that might get confused with valid system types.
+ # -solaris* is a basic system type, with this one exception.
+ -auroraux)
+ os=-auroraux
+ ;;
+ -solaris1 | -solaris1.*)
+ os=`echo $os | sed -e 's|solaris1|sunos4|'`
+ ;;
+ -solaris)
+ os=-solaris2
+ ;;
+ -svr4*)
+ os=-sysv4
+ ;;
+ -unixware*)
+ os=-sysv4.2uw
+ ;;
+ -gnu/linux*)
+ os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+ ;;
+ # First accept the basic system types.
+ # The portable systems comes first.
+ # Each alternative MUST END IN A *, to match a version number.
+ # -sysv* is not here because it comes later, after sysvr4.
+ -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
+ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
+ | -sym* | -kopensolaris* \
+ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+ | -aos* | -aros* \
+ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
+ | -openbsd* | -solidbsd* \
+ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
+ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+ | -chorusos* | -chorusrdb* | -cegcc* \
+ | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+ | -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \
+ | -uxpv* | -beos* | -mpeix* | -udk* \
+ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
+ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
+ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
+ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
+ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*)
+ # Remember, each alternative MUST END IN *, to match a version number.
+ ;;
+ -qnx*)
+ case $basic_machine in
+ x86-* | i*86-*)
+ ;;
+ *)
+ os=-nto$os
+ ;;
+ esac
+ ;;
+ -nto-qnx*)
+ ;;
+ -nto*)
+ os=`echo $os | sed -e 's|nto|nto-qnx|'`
+ ;;
+ -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+ | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \
+ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+ ;;
+ -mac*)
+ os=`echo $os | sed -e 's|mac|macos|'`
+ ;;
+ -linux-dietlibc)
+ os=-linux-dietlibc
+ ;;
+ -linux*)
+ os=`echo $os | sed -e 's|linux|linux-gnu|'`
+ ;;
+ -sunos5*)
+ os=`echo $os | sed -e 's|sunos5|solaris2|'`
+ ;;
+ -sunos6*)
+ os=`echo $os | sed -e 's|sunos6|solaris3|'`
+ ;;
+ -opened*)
+ os=-openedition
+ ;;
+ -os400*)
+ os=-os400
+ ;;
+ -wince*)
+ os=-wince
+ ;;
+ -osfrose*)
+ os=-osfrose
+ ;;
+ -osf*)
+ os=-osf
+ ;;
+ -utek*)
+ os=-bsd
+ ;;
+ -dynix*)
+ os=-bsd
+ ;;
+ -acis*)
+ os=-aos
+ ;;
+ -atheos*)
+ os=-atheos
+ ;;
+ -syllable*)
+ os=-syllable
+ ;;
+ -386bsd)
+ os=-bsd
+ ;;
+ -ctix* | -uts*)
+ os=-sysv
+ ;;
+ -nova*)
+ os=-rtmk-nova
+ ;;
+ -ns2 )
+ os=-nextstep2
+ ;;
+ -nsk*)
+ os=-nsk
+ ;;
+ # Preserve the version number of sinix5.
+ -sinix5.*)
+ os=`echo $os | sed -e 's|sinix|sysv|'`
+ ;;
+ -sinix*)
+ os=-sysv4
+ ;;
+ -tpf*)
+ os=-tpf
+ ;;
+ -triton*)
+ os=-sysv3
+ ;;
+ -oss*)
+ os=-sysv3
+ ;;
+ -svr4)
+ os=-sysv4
+ ;;
+ -svr3)
+ os=-sysv3
+ ;;
+ -sysvr4)
+ os=-sysv4
+ ;;
+ # This must come after -sysvr4.
+ -sysv*)
+ ;;
+ -ose*)
+ os=-ose
+ ;;
+ -es1800*)
+ os=-ose
+ ;;
+ -xenix)
+ os=-xenix
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ os=-mint
+ ;;
+ -aros*)
+ os=-aros
+ ;;
+ -kaos*)
+ os=-kaos
+ ;;
+ -zvmoe)
+ os=-zvmoe
+ ;;
+ -dicos*)
+ os=-dicos
+ ;;
+ -none)
+ ;;
+ *)
+ # Get rid of the `-' at the beginning of $os.
+ os=`echo $os | sed 's/[^-]*-//'`
+ echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system. Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+ score-*)
+ os=-elf
+ ;;
+ spu-*)
+ os=-elf
+ ;;
+ *-acorn)
+ os=-riscix1.2
+ ;;
+ arm*-rebel)
+ os=-linux
+ ;;
+ arm*-semi)
+ os=-aout
+ ;;
+ c4x-* | tic4x-*)
+ os=-coff
+ ;;
+ # This must come before the *-dec entry.
+ pdp10-*)
+ os=-tops20
+ ;;
+ pdp11-*)
+ os=-none
+ ;;
+ *-dec | vax-*)
+ os=-ultrix4.2
+ ;;
+ m68*-apollo)
+ os=-domain
+ ;;
+ i386-sun)
+ os=-sunos4.0.2
+ ;;
+ m68000-sun)
+ os=-sunos3
+ # This also exists in the configure program, but was not the
+ # default.
+ # os=-sunos4
+ ;;
+ m68*-cisco)
+ os=-aout
+ ;;
+ mep-*)
+ os=-elf
+ ;;
+ mips*-cisco)
+ os=-elf
+ ;;
+ mips*-*)
+ os=-elf
+ ;;
+ or32-*)
+ os=-coff
+ ;;
+ *-tti) # must be before sparc entry or we get the wrong os.
+ os=-sysv3
+ ;;
+ sparc-* | *-sun)
+ os=-sunos4.1.1
+ ;;
+ *-be)
+ os=-beos
+ ;;
+ *-haiku)
+ os=-haiku
+ ;;
+ *-ibm)
+ os=-aix
+ ;;
+ *-knuth)
+ os=-mmixware
+ ;;
+ *-wec)
+ os=-proelf
+ ;;
+ *-winbond)
+ os=-proelf
+ ;;
+ *-oki)
+ os=-proelf
+ ;;
+ *-hp)
+ os=-hpux
+ ;;
+ *-hitachi)
+ os=-hiux
+ ;;
+ i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+ os=-sysv
+ ;;
+ *-cbm)
+ os=-amigaos
+ ;;
+ *-dg)
+ os=-dgux
+ ;;
+ *-dolphin)
+ os=-sysv3
+ ;;
+ m68k-ccur)
+ os=-rtu
+ ;;
+ m88k-omron*)
+ os=-luna
+ ;;
+ *-next )
+ os=-nextstep
+ ;;
+ *-sequent)
+ os=-ptx
+ ;;
+ *-crds)
+ os=-unos
+ ;;
+ *-ns)
+ os=-genix
+ ;;
+ i370-*)
+ os=-mvs
+ ;;
+ *-next)
+ os=-nextstep3
+ ;;
+ *-gould)
+ os=-sysv
+ ;;
+ *-highlevel)
+ os=-bsd
+ ;;
+ *-encore)
+ os=-bsd
+ ;;
+ *-sgi)
+ os=-irix
+ ;;
+ *-siemens)
+ os=-sysv4
+ ;;
+ *-masscomp)
+ os=-rtu
+ ;;
+ f30[01]-fujitsu | f700-fujitsu)
+ os=-uxpv
+ ;;
+ *-rom68k)
+ os=-coff
+ ;;
+ *-*bug)
+ os=-coff
+ ;;
+ *-apple)
+ os=-macos
+ ;;
+ *-atari*)
+ os=-mint
+ ;;
+ *)
+ os=-none
+ ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer. We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+ *-unknown)
+ case $os in
+ -riscix*)
+ vendor=acorn
+ ;;
+ -sunos*)
+ vendor=sun
+ ;;
+ -cnk*|-aix*)
+ vendor=ibm
+ ;;
+ -beos*)
+ vendor=be
+ ;;
+ -hpux*)
+ vendor=hp
+ ;;
+ -mpeix*)
+ vendor=hp
+ ;;
+ -hiux*)
+ vendor=hitachi
+ ;;
+ -unos*)
+ vendor=crds
+ ;;
+ -dgux*)
+ vendor=dg
+ ;;
+ -luna*)
+ vendor=omron
+ ;;
+ -genix*)
+ vendor=ns
+ ;;
+ -mvs* | -opened*)
+ vendor=ibm
+ ;;
+ -os400*)
+ vendor=ibm
+ ;;
+ -ptx*)
+ vendor=sequent
+ ;;
+ -tpf*)
+ vendor=ibm
+ ;;
+ -vxsim* | -vxworks* | -windiss*)
+ vendor=wrs
+ ;;
+ -aux*)
+ vendor=apple
+ ;;
+ -hms*)
+ vendor=hitachi
+ ;;
+ -mpw* | -macos*)
+ vendor=apple
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ vendor=atari
+ ;;
+ -vos*)
+ vendor=stratus
+ ;;
+ esac
+ basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+ ;;
+esac
+
+echo $basic_machine$os
+exit
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/configure b/configure
new file mode 100755
index 00000000..1fbd2696
--- /dev/null
+++ b/configure
@@ -0,0 +1,21687 @@
+#! /bin/sh
+# From configure.ac Id: configure.ac 1266 2009-07-14 18:39:12Z hubert@u.washington.edu .
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.68 for alpine 2.10.
+#
+# Report bugs to <alpine-contact@u.washington.edu>.
+#
+#
+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
+# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software
+# Foundation, Inc.
+#
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in #(
+ *posix*) :
+ set -o posix ;; #(
+ *) :
+ ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='print -r --'
+ as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='printf %s\n'
+ as_echo_n='printf %s'
+else
+ if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+ as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+ as_echo_n='/usr/ucb/echo -n'
+ else
+ as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+ as_echo_n_body='eval
+ arg=$1;
+ case $arg in #(
+ *"$as_nl"*)
+ expr "X$arg" : "X\\(.*\\)$as_nl";
+ arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+ esac;
+ expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+ '
+ export as_echo_n_body
+ as_echo_n='sh -c $as_echo_n_body as_echo'
+ fi
+ export as_echo_body
+ as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there. '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+if test "x$CONFIG_SHELL" = x; then
+ as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '\${1+\"\$@\"}'='\"\$@\"'
+ setopt NO_GLOB_SUBST
+else
+ case \`(set -o) 2>/dev/null\` in #(
+ *posix*) :
+ set -o posix ;; #(
+ *) :
+ ;;
+esac
+fi
+"
+ as_required="as_fn_return () { (exit \$1); }
+as_fn_success () { as_fn_return 0; }
+as_fn_failure () { as_fn_return 1; }
+as_fn_ret_success () { return 0; }
+as_fn_ret_failure () { return 1; }
+
+exitcode=0
+as_fn_success || { exitcode=1; echo as_fn_success failed.; }
+as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
+as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
+as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
+if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
+
+else
+ exitcode=1; echo positional parameters were not saved.
+fi
+test x\$exitcode = x0 || exit 1"
+ as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
+ as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
+ eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
+ test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1
+test \$(( 1 + 1 )) = 2 || exit 1"
+ if (eval "$as_required") 2>/dev/null; then :
+ as_have_required=yes
+else
+ as_have_required=no
+fi
+ if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then :
+
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ as_found=:
+ case $as_dir in #(
+ /*)
+ for as_base in sh bash ksh sh5; do
+ # Try only shells that exist, to save several forks.
+ as_shell=$as_dir/$as_base
+ if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+ { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then :
+ CONFIG_SHELL=$as_shell as_have_required=yes
+ if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then :
+ break 2
+fi
+fi
+ done;;
+ esac
+ as_found=false
+done
+$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
+ { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
+ CONFIG_SHELL=$SHELL as_have_required=yes
+fi; }
+IFS=$as_save_IFS
+
+
+ if test "x$CONFIG_SHELL" != x; then :
+ # We cannot yet assume a decent shell, so we have to provide a
+ # neutralization value for shells without unset; and this also
+ # works around shells that cannot unset nonexistent variables.
+ # Preserve -v and -x to the replacement shell.
+ BASH_ENV=/dev/null
+ ENV=/dev/null
+ (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+ export CONFIG_SHELL
+ case $- in # ((((
+ *v*x* | *x*v* ) as_opts=-vx ;;
+ *v* ) as_opts=-v ;;
+ *x* ) as_opts=-x ;;
+ * ) as_opts= ;;
+ esac
+ exec "$CONFIG_SHELL" $as_opts "$as_myself" ${1+"$@"}
+fi
+
+ if test x$as_have_required = xno; then :
+ $as_echo "$0: This script requires a shell more modern than all"
+ $as_echo "$0: the shells that I found on your system."
+ if test x${ZSH_VERSION+set} = xset ; then
+ $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
+ $as_echo "$0: be upgraded to zsh 4.3.4 or later."
+ else
+ $as_echo "$0: Please tell bug-autoconf@gnu.org and
+$0: alpine-contact@u.washington.edu about your system,
+$0: including any error possibly output before this
+$0: message. Then install a modern shell, or manually run
+$0: the script under such a shell if you do have one."
+ fi
+ exit 1
+fi
+fi
+fi
+SHELL=${CONFIG_SHELL-/bin/sh}
+export SHELL
+# Unset more variables known to interfere with behavior of common tools.
+CLICOLOR_FORCE= GREP_OPTIONS=
+unset CLICOLOR_FORCE GREP_OPTIONS
+
+## --------------------- ##
+## M4sh Shell Functions. ##
+## --------------------- ##
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+ { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+ return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+ set +e
+ as_fn_set_status $1
+ exit $1
+} # as_fn_exit
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || eval $as_mkdir_p || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+ eval 'as_fn_append ()
+ {
+ eval $1+=\$2
+ }'
+else
+ as_fn_append ()
+ {
+ eval $1=\$$1\$2
+ }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+ eval 'as_fn_arith ()
+ {
+ as_val=$(( $* ))
+ }'
+else
+ as_fn_arith ()
+ {
+ as_val=`expr "$@" || test $? -eq 1`
+ }
+fi # as_fn_arith
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+ as_status=$1; test $as_status -eq 0 && as_status=1
+ if test "$4"; then
+ as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+ fi
+ $as_echo "$as_me: error: $2" >&2
+ as_fn_exit $as_status
+} # as_fn_error
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+ as_lineno_1=$LINENO as_lineno_1a=$LINENO
+ as_lineno_2=$LINENO as_lineno_2a=$LINENO
+ eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
+ test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
+ # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-)
+ sed -n '
+ p
+ /[$]LINENO/=
+ ' <$as_myself |
+ sed '
+ s/[$]LINENO.*/&-/
+ t lineno
+ b
+ :lineno
+ N
+ :loop
+ s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+ t loop
+ s/-\n.*//
+ ' >$as_me.lineno &&
+ chmod +x "$as_me.lineno" ||
+ { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensitive to this).
+ . "./$as_me.lineno"
+ # Exit status is that of the last command.
+ exit
+}
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+ case `echo 'xy\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ xy) ECHO_C='\c';;
+ *) echo `echo ksh88 bug on AIX 6.1` > /dev/null
+ ECHO_T=' ';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+ if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -p'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -p'
+ elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+ else
+ as_ln_s='cp -p'
+ fi
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p='mkdir -p "$as_dir"'
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+if test -x / >/dev/null 2>&1; then
+ as_test_x='test -x'
+else
+ if ls -dL / >/dev/null 2>&1; then
+ as_ls_L_option=L
+ else
+ as_ls_L_option=
+ fi
+ as_test_x='
+ eval sh -c '\''
+ if test -d "$1"; then
+ test -d "$1/.";
+ else
+ case $1 in #(
+ -*)set "./$1";;
+ esac;
+ case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #((
+ ???[sx]*):;;*)false;;esac;fi
+ '\'' sh
+ '
+fi
+as_executable_p=$as_test_x
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+
+# Check that we are running under the correct shell.
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+case X$lt_ECHO in
+X*--fallback-echo)
+ # Remove one level of quotation (which was required for Make).
+ ECHO=`echo "$lt_ECHO" | sed 's,\\\\\$\\$0,'$0','`
+ ;;
+esac
+
+ECHO=${lt_ECHO-echo}
+if test "X$1" = X--no-reexec; then
+ # Discard the --no-reexec flag, and continue.
+ shift
+elif test "X$1" = X--fallback-echo; then
+ # Avoid inline document here, it may be left over
+ :
+elif test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' ; then
+ # Yippee, $ECHO works!
+ :
+else
+ # Restart under the correct shell.
+ exec $SHELL "$0" --no-reexec ${1+"$@"}
+fi
+
+if test "X$1" = X--fallback-echo; then
+ # used as fallback echo
+ shift
+ cat <<_LT_EOF
+$*
+_LT_EOF
+ exit 0
+fi
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+if test -z "$lt_ECHO"; then
+ if test "X${echo_test_string+set}" != Xset; then
+ # find a string as large as possible, as long as the shell can cope with it
+ for cmd in 'sed 50q "$0"' 'sed 20q "$0"' 'sed 10q "$0"' 'sed 2q "$0"' 'echo test'; do
+ # expected sizes: less than 2Kb, 1Kb, 512 bytes, 16 bytes, ...
+ if { echo_test_string=`eval $cmd`; } 2>/dev/null &&
+ { test "X$echo_test_string" = "X$echo_test_string"; } 2>/dev/null
+ then
+ break
+ fi
+ done
+ fi
+
+ if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ :
+ else
+ # The Solaris, AIX, and Digital Unix default echo programs unquote
+ # backslashes. This makes it impossible to quote backslashes using
+ # echo "$something" | sed 's/\\/\\\\/g'
+ #
+ # So, first we look for a working echo in the user's PATH.
+
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ for dir in $PATH /usr/ucb; do
+ IFS="$lt_save_ifs"
+ if (test -f $dir/echo || test -f $dir/echo$ac_exeext) &&
+ test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`($dir/echo "$echo_test_string") 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ ECHO="$dir/echo"
+ break
+ fi
+ done
+ IFS="$lt_save_ifs"
+
+ if test "X$ECHO" = Xecho; then
+ # We didn't find a better echo, so look for alternatives.
+ if test "X`{ print -r '\t'; } 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`{ print -r "$echo_test_string"; } 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ # This shell has a builtin print -r that does the trick.
+ ECHO='print -r'
+ elif { test -f /bin/ksh || test -f /bin/ksh$ac_exeext; } &&
+ test "X$CONFIG_SHELL" != X/bin/ksh; then
+ # If we have ksh, try running configure again with it.
+ ORIGINAL_CONFIG_SHELL=${CONFIG_SHELL-/bin/sh}
+ export ORIGINAL_CONFIG_SHELL
+ CONFIG_SHELL=/bin/ksh
+ export CONFIG_SHELL
+ exec $CONFIG_SHELL "$0" --no-reexec ${1+"$@"}
+ else
+ # Try using printf.
+ ECHO='printf %s\n'
+ if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ # Cool, printf works
+ :
+ elif echo_testing_string=`($ORIGINAL_CONFIG_SHELL "$0" --fallback-echo '\t') 2>/dev/null` &&
+ test "X$echo_testing_string" = 'X\t' &&
+ echo_testing_string=`($ORIGINAL_CONFIG_SHELL "$0" --fallback-echo "$echo_test_string") 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ CONFIG_SHELL=$ORIGINAL_CONFIG_SHELL
+ export CONFIG_SHELL
+ SHELL="$CONFIG_SHELL"
+ export SHELL
+ ECHO="$CONFIG_SHELL $0 --fallback-echo"
+ elif echo_testing_string=`($CONFIG_SHELL "$0" --fallback-echo '\t') 2>/dev/null` &&
+ test "X$echo_testing_string" = 'X\t' &&
+ echo_testing_string=`($CONFIG_SHELL "$0" --fallback-echo "$echo_test_string") 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ ECHO="$CONFIG_SHELL $0 --fallback-echo"
+ else
+ # maybe with a smaller string...
+ prev=:
+
+ for cmd in 'echo test' 'sed 2q "$0"' 'sed 10q "$0"' 'sed 20q "$0"' 'sed 50q "$0"'; do
+ if { test "X$echo_test_string" = "X`eval $cmd`"; } 2>/dev/null
+ then
+ break
+ fi
+ prev="$cmd"
+ done
+
+ if test "$prev" != 'sed 50q "$0"'; then
+ echo_test_string=`eval $prev`
+ export echo_test_string
+ exec ${ORIGINAL_CONFIG_SHELL-${CONFIG_SHELL-/bin/sh}} "$0" ${1+"$@"}
+ else
+ # Oops. We lost completely, so just stick with echo.
+ ECHO=echo
+ fi
+ fi
+ fi
+ fi
+ fi
+fi
+
+# Copy echo and quote the copy suitably for passing to libtool from
+# the Makefile, instead of quoting the original, which is used later.
+lt_ECHO=$ECHO
+if test "X$lt_ECHO" = "X$CONFIG_SHELL $0 --fallback-echo"; then
+ lt_ECHO="$CONFIG_SHELL \\\$\$0 --fallback-echo"
+fi
+
+
+
+
+test -n "$DJDIR" || exec 7<&0 </dev/null
+exec 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+
+# Identity of this package.
+PACKAGE_NAME='alpine'
+PACKAGE_TARNAME='alpine'
+PACKAGE_VERSION='2.10'
+PACKAGE_STRING='alpine 2.10'
+PACKAGE_BUGREPORT='alpine-contact@u.washington.edu'
+PACKAGE_URL=''
+
+ac_unique_file="include/system.h"
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_STRING_H
+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+# include <memory.h>
+# endif
+# include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+gt_needs=
+ac_subst_vars='am__EXEEXT_FALSE
+am__EXEEXT_TRUE
+LTLIBOBJS
+LIBOBJS
+AM_LDFLAGS
+AM_CFLAGS
+WEB_PUBCOOKIE_LINK
+WEB_PUBCOOKIE_LIB
+WEB_PUBCOOKIE_BUILD
+WEB_BINDIR
+WEB_BUILD
+REGEX_BUILD
+C_CLIENT_SPECIALS
+C_CLIENT_GCCOPTLEVEL
+C_CLIENT_LDFLAGS
+C_CLIENT_CFLAGS
+C_CLIENT_WITH_IPV6
+C_CLIENT_TARGET
+PTHREAD_CFLAGS
+PTHREAD_LIBS
+PTHREAD_CC
+acx_pthread_config
+alpine_interactive_spellcheck
+ISPELLPROG
+alpine_simple_spellcheck
+SPELLPROG
+PWPROG
+NPA_PROG
+SENDMAIL
+POSUB
+LTLIBINTL
+LIBINTL
+INTLLIBS
+LTLIBICONV
+LIBICONV
+INTL_MACOSX_LIBS
+MSGMERGE
+XGETTEXT_015
+XGETTEXT
+GMSGFMT_015
+MSGFMT_015
+GMSGFMT
+MSGFMT
+USE_NLS
+MAKE
+LN
+CP
+RM
+CPP
+OTOOL64
+OTOOL
+LIPO
+NMEDIT
+DSYMUTIL
+lt_ECHO
+AR
+OBJDUMP
+NM
+ac_ct_DUMPBIN
+DUMPBIN
+LD
+FGREP
+EGREP
+GREP
+SED
+LIBTOOL
+RANLIB
+LN_S
+am__fastdepCC_FALSE
+am__fastdepCC_TRUE
+CCDEPMODE
+AMDEPBACKSLASH
+AMDEP_FALSE
+AMDEP_TRUE
+am__quote
+am__include
+DEPDIR
+OBJEXT
+EXEEXT
+ac_ct_CC
+CPPFLAGS
+LDFLAGS
+CFLAGS
+CC
+host_os
+host_vendor
+host_cpu
+host
+build_os
+build_vendor
+build_cpu
+build
+MAINT
+MAINTAINER_MODE_FALSE
+MAINTAINER_MODE_TRUE
+am__untar
+am__tar
+AMTAR
+am__leading_dot
+SET_MAKE
+AWK
+mkdir_p
+MKDIR_P
+INSTALL_STRIP_PROGRAM
+STRIP
+install_sh
+MAKEINFO
+AUTOHEADER
+AUTOMAKE
+AUTOCONF
+ACLOCAL
+VERSION
+PACKAGE
+CYGPATH_W
+am__isrc
+INSTALL_DATA
+INSTALL_SCRIPT
+INSTALL_PROGRAM
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_URL
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+enable_maintainer_mode
+enable_dependency_tracking
+enable_shared
+enable_static
+with_pic
+enable_fast_install
+with_gnu_ld
+enable_libtool_lock
+enable_nls
+enable_rpath
+with_libiconv_prefix
+with_libintl_prefix
+enable_dmalloc
+with_dmalloc_dir
+with_localedir
+enable_osx_universal_binaries
+with_include_path
+with_lib_path
+with_pubcookie
+with_web_bin
+enable_debug
+enable_optimization
+enable_mouse
+enable_quotas
+enable_from_changing
+enable_background_post
+enable_keyboard_lock
+enable_from_encoding
+with_smtp_msa
+with_smtp_msa_flags
+with_npa
+with_npa_flags
+with_password_prog
+with_simple_spellcheck
+with_interactive_spellcheck
+with_system_pinerc
+with_system_fixed_pinerc
+with_mailcheck_interval
+with_checkpoint_interval
+with_checkpoint_frequency
+with_display_rows
+with_display_columns
+with_max_display_rows
+with_max_display_columns
+with_fill_column
+with_max_fill_column
+with_debug_level
+with_debug_files
+with_debug_file
+with_forwarded_keyword
+with_display_overlap
+with_display_margin
+with_default_fcc
+with_default_save_folder
+with_default_legacy_postponed_folder
+with_default_postponed_folder
+with_default_trash_folder
+with_default_interrupted_mail
+with_default_dead_letter_folder
+with_default_mail_directory
+with_default_inbox_name
+with_default_signature_file
+with_default_elm_style_save
+with_default_header_in_reply
+with_default_old_style_reply
+with_default_use_only_domain_name
+with_default_save_by_sender
+with_default_sort_key
+with_default_addressbook_sort_rule
+with_default_folder_sort_rule
+with_default_saved_message_name_rule
+with_default_fcc_rule
+with_default_standard_printer
+with_default_ansi_printer
+with_default_addressbook
+with_default_local_fullname
+with_default_local_address
+with_default_keyboard_lock_count
+with_default_remote_addressbook_history
+with_smime_public_cert_directory
+with_smime_private_key_directory
+with_smime_cacert_directory
+with_default_printer
+with_passfile
+with_local_password_cache
+with_local_password_cache_method
+with_default_sshpath
+with_default_sshcmd
+with_ssl
+with_ssl_dir
+with_ssl_certs_dir
+with_ssl_include_dir
+with_ssl_lib_dir
+with_krb5
+with_krb5_dir
+with_krb5_include_dir
+with_krb5_lib_dir
+with_ldap
+with_ldap_dir
+with_ldap_include_dir
+with_ldap_lib_dir
+with_smime
+with_tcl
+with_tcl_lib
+with_tcl_include
+with_supplied_regex
+with_pthread
+with_system_mail_directory
+with_c_client_target
+with_ipv6
+'
+ ac_precious_vars='build_alias
+host_alias
+target_alias
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+CPP'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval $ac_prev=\$ac_option
+ ac_prev=
+ continue
+ fi
+
+ case $ac_option in
+ *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+ *=) ac_optarg= ;;
+ *) ac_optarg=yes ;;
+ esac
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case $ac_dashdash$ac_option in
+ --)
+ ac_dashdash=yes ;;
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir=$ac_optarg ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build_alias ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build_alias=$ac_optarg ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file=$ac_optarg ;;
+
+ --config-cache | -C)
+ cache_file=config.cache ;;
+
+ -datadir | --datadir | --datadi | --datad)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=*)
+ datadir=$ac_optarg ;;
+
+ -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+ | --dataroo | --dataro | --datar)
+ ac_prev=datarootdir ;;
+ -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+ datarootdir=$ac_optarg ;;
+
+ -disable-* | --disable-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid feature name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"enable_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval enable_$ac_useropt=no ;;
+
+ -docdir | --docdir | --docdi | --doc | --do)
+ ac_prev=docdir ;;
+ -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+ docdir=$ac_optarg ;;
+
+ -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+ ac_prev=dvidir ;;
+ -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+ dvidir=$ac_optarg ;;
+
+ -enable-* | --enable-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid feature name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"enable_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval enable_$ac_useropt=\$ac_optarg ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix=$ac_optarg ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he | -h)
+ ac_init_help=long ;;
+ -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+ ac_init_help=recursive ;;
+ -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+ ac_init_help=short ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host_alias ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host_alias=$ac_optarg ;;
+
+ -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+ ac_prev=htmldir ;;
+ -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+ | --ht=*)
+ htmldir=$ac_optarg ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir=$ac_optarg ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir=$ac_optarg ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir=$ac_optarg ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir=$ac_optarg ;;
+
+ -localedir | --localedir | --localedi | --localed | --locale)
+ ac_prev=localedir ;;
+ -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+ localedir=$ac_optarg ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst | --locals)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+ localstatedir=$ac_optarg ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir=$ac_optarg ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c | -n)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir=$ac_optarg ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix=$ac_optarg ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix=$ac_optarg ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix=$ac_optarg ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name=$ac_optarg ;;
+
+ -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+ ac_prev=pdfdir ;;
+ -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+ pdfdir=$ac_optarg ;;
+
+ -psdir | --psdir | --psdi | --psd | --ps)
+ ac_prev=psdir ;;
+ -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+ psdir=$ac_optarg ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir=$ac_optarg ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir=$ac_optarg ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site=$ac_optarg ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir=$ac_optarg ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir=$ac_optarg ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target_alias ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target_alias=$ac_optarg ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers | -V)
+ ac_init_version=: ;;
+
+ -with-* | --with-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid package name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"with_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval with_$ac_useropt=\$ac_optarg ;;
+
+ -without-* | --without-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid package name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"with_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval with_$ac_useropt=no ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes=$ac_optarg ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries=$ac_optarg ;;
+
+ -*) as_fn_error $? "unrecognized option: \`$ac_option'
+Try \`$0 --help' for more information"
+ ;;
+
+ *=*)
+ ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+ # Reject names that are not valid shell variable names.
+ case $ac_envvar in #(
+ '' | [0-9]* | *[!_$as_cr_alnum]* )
+ as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
+ esac
+ eval $ac_envvar=\$ac_optarg
+ export $ac_envvar ;;
+
+ *)
+ # FIXME: should be removed in autoconf 3.0.
+ $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+ expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+ : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+ as_fn_error $? "missing argument to $ac_option"
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+ case $enable_option_checking in
+ no) ;;
+ fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
+ *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+ esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
+ datadir sysconfdir sharedstatedir localstatedir includedir \
+ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+ libdir localedir mandir
+do
+ eval ac_val=\$$ac_var
+ # Remove trailing slashes.
+ case $ac_val in
+ */ )
+ ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+ eval $ac_var=\$ac_val;;
+ esac
+ # Be sure to have absolute directory names.
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* ) continue;;
+ NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+ esac
+ as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+ if test "x$build_alias" = x; then
+ cross_compiling=maybe
+ $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host.
+ If a cross compiler is detected then cross compile mode will be used" >&2
+ elif test "x$build_alias" != "x$host_alias"; then
+ cross_compiling=yes
+ fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+ as_fn_error $? "working directory cannot be determined"
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+ as_fn_error $? "pwd does not report name of working directory"
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then the parent directory.
+ ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_myself" : 'X\(//\)[^/]' \| \
+ X"$as_myself" : 'X\(//\)$' \| \
+ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_myself" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ srcdir=$ac_confdir
+ if test ! -r "$srcdir/$ac_unique_file"; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+ test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+ as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+ cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
+ pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+ srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+ eval ac_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_env_${ac_var}_value=\$${ac_var}
+ eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat <<_ACEOF
+\`configure' configures alpine 2.10 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE. See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+ -h, --help display this help and exit
+ --help=short display options specific to this package
+ --help=recursive display the short help of all the included packages
+ -V, --version display version information and exit
+ -q, --quiet, --silent do not print \`checking ...' messages
+ --cache-file=FILE cache test results in FILE [disabled]
+ -C, --config-cache alias for \`--cache-file=config.cache'
+ -n, --no-create do not create output files
+ --srcdir=DIR find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+ --bindir=DIR user executables [EPREFIX/bin]
+ --sbindir=DIR system admin executables [EPREFIX/sbin]
+ --libexecdir=DIR program executables [EPREFIX/libexec]
+ --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --libdir=DIR object code libraries [EPREFIX/lib]
+ --includedir=DIR C header files [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc [/usr/include]
+ --datarootdir=DIR read-only arch.-independent data root [PREFIX/share]
+ --datadir=DIR read-only architecture-independent data [DATAROOTDIR]
+ --infodir=DIR info documentation [DATAROOTDIR/info]
+ --localedir=DIR locale-dependent data [DATAROOTDIR/locale]
+ --mandir=DIR man documentation [DATAROOTDIR/man]
+ --docdir=DIR documentation root [DATAROOTDIR/doc/alpine]
+ --htmldir=DIR html documentation [DOCDIR]
+ --dvidir=DIR dvi documentation [DOCDIR]
+ --pdfdir=DIR pdf documentation [DOCDIR]
+ --psdir=DIR ps documentation [DOCDIR]
+_ACEOF
+
+ cat <<\_ACEOF
+
+Program names:
+ --program-prefix=PREFIX prepend PREFIX to installed program names
+ --program-suffix=SUFFIX append SUFFIX to installed program names
+ --program-transform-name=PROGRAM run sed PROGRAM on installed program names
+
+System types:
+ --build=BUILD configure for building on BUILD [guessed]
+ --host=HOST cross-compile to build programs to run on HOST [BUILD]
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+ case $ac_init_help in
+ short | recursive ) echo "Configuration of alpine 2.10:";;
+ esac
+ cat <<\_ACEOF
+
+Optional Features:
+ --disable-option-checking ignore unrecognized --enable/--with options
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --enable-maintainer-mode enable make rules and dependencies not useful
+ (and sometimes confusing) to the casual installer
+ --disable-dependency-tracking speeds up one-time build
+ --enable-dependency-tracking do not reject slow dependency extractors
+ --enable-shared[=PKGS] build shared libraries [default=yes]
+ --enable-static[=PKGS] build static libraries [default=yes]
+ --enable-fast-install[=PKGS]
+ optimize for fast installation [default=yes]
+ --disable-libtool-lock avoid locking (might break parallel builds)
+ --disable-nls do not use Native Language Support
+ --disable-rpath do not hardcode runtime library paths
+ --enable-dmalloc Enable dmalloc debugging
+ --enable-osx-universal-binaries
+ Produce universal binaries under OS X [[default=no]]
+ --disable-debug Exclude debug messages from source
+ --disable-optimization Exclude optimizing compiler flags
+ --disable-mouse Disable mouse support
+ --enable-quotas Enable disk quota checking on startup
+ --disable-from-changing Disallow users changing From addresss
+ --disable-background-post
+ Disable background posting
+ --disable-keyboard-lock Disable keyboard locking
+ --enable-from-encoding Enable From encoding in sent messages
+
+Optional Packages:
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --with-pic try to use only PIC/non-PIC objects [default=use
+ both]
+ --with-gnu-ld assume the C compiler uses GNU ld [default=no]
+ --with-gnu-ld assume the C compiler uses GNU ld default=no
+ --with-libiconv-prefix[=DIR] search for libiconv in DIR/include and DIR/lib
+ --without-libiconv-prefix don't search for libiconv in includedir and libdir
+ --with-libintl-prefix[=DIR] search for libintl in DIR/include and DIR/lib
+ --without-libintl-prefix don't search for libintl in includedir and libdir
+ --with-dmalloc-dir=DIR Root of dmalloc lib/include path
+ --with-localedir=DIR Name of gettext locale directory
+ --with-include-path=PATHS
+ Colon-separated list of directories used for include
+ file search
+ --with-lib-path=PATHS Colon-separated list of directories used for library
+ search
+ --with-pubcookie Include support for UW-Pubcookie Web Authentication
+ --with-web-bin=PATH Directory to hold Web Alpine component binary files
+ --with-smtp-msa=PATH Local Mail Submission Agent (sendmail)
+ --with-smtp-msa-flags=FLAGS
+ MSA flags for SMTP on stdin/stdout (-bs -odb -oem)
+ --with-npa=PATH Posting agent when no nntp-servers defined (inews)
+ --with-npa-flags=FLAGS Flags to allow posting via local agent (-h)
+ --with-password-prog=PATH
+ Password change program (/bin/passwd)
+ --with-simple-spellcheck=PROG
+ Spellcheck program reads stdin, emits misspellings
+ on stdout
+ --with-interactive-spellcheck=PROG
+ Interactive, filewise spell checker
+ --with-system-pinerc=VALUE
+ System pinerc (/usr/local/lib/pine.conf)
+ --with-system-fixed-pinerc=VALUE
+ System fixed pinerc (/usr/local/lib/pine.conf.fixed)
+ --with-mailcheck-interval=VALUE
+ Specify default mail-check-interval (150)
+ --with-checkpoint-interval=VALUE
+ Specify default checkpoint-interval (420)
+ --with-checkpoint-frequency=VALUE
+ State change count before checkpoint (12)
+ --with-display-rows=VALUE
+ Initial rows on display (24)
+ --with-display-columns=VALUE
+ Initial columns on display (80)
+ --with-max-display-rows=VALUE
+ Maximum display rows (200)
+ --with-max-display-columns=VALUE
+ Maximum display columns (500)
+ --with-fill-column=VALUE
+ Default fill column (74)
+ --with-max_fill-column=VALUE
+ Maximum fill column (80)
+ --with-debug-level=VALUE
+ Specify default debug verbosity level (2)
+ --with-debug-files=VALUE
+ Specify number of debug files (4)
+ --with-debug-file=VALUE Specify debug file name (.pine-debug)
+ --with-forwarded-keyword=VALUE
+ IMAP (c-client) keyword to store forwarded status
+ ("\$Forwarded")
+ --with-display-overlap=VALUE
+ Lines preserved while paging (2)
+ --with-display-margin=VALUE
+ Lines visible while scrolling (0)
+ --with-default-fcc=VALUE
+ Default sent mail folder (sent-mail)
+ --with-default-save-folder=VALUE
+ Default save folder (saved-messages)
+ --with-default-legacy-postponed-folder=VALUE
+ Pre Pine 3.90 postponed folder (postponed-mail)
+ --with-default-postponed-folder=VALUE
+ Default postponed folder (postponed-msgs)
+ --with-default-trash-folder=VALUE
+ Default Trash folder for Web Alpine (Trash)
+ --with-default-interrupted-mail=VALUE
+ Default folder for interrupted mail
+ (.pine-interrupted-mail)
+ --with-default-dead-letter-folder=VALUE
+ Default dead letter folder (dead.letter)
+ --with-default-mail-directory=VALUE
+ Default mail directory (mail)
+ --with-default-inbox-name=VALUE
+ Default inbox name (INBOX)
+ --with-default-signature-file=VALUE
+ Default signature file (.signature)
+ --with-default-elm-style-save=VALUE
+ Default to Elm style save (no)
+ --with-default-header-in-reply=VALUE
+ Include header in reply (no)
+ --with-default-old-style-reply=VALUE
+ Default to old style reply (no)
+ --with-default-use-only-domain-name=VALUE
+ Default to using only the domain name (no)
+ --with-default-save-by-sender=VALUE
+ Default to save by sender (no)
+ --with-default-sort-key=VALUE
+ Default sort key (arrival)
+ --with-default-addressbook-sort-rule=VALUE
+ Default addressbook sort rule
+ (fullname-with-lists-last)
+ --with-default-folder-sort-rule=VALUE
+ Default folder sort rule (alphabetical)
+ --with-default-saved-message-name-rule=VALUE
+ Default saved message name rule (default-folder)
+ --with-default-fcc-rule=VALUE
+ Default fcc rule (default-fcc)
+ --with-default-standard-printer=VALUE
+ Default standard printern (lpr)
+ --with-default-ansi-printer=VALUE
+ ANSI printer definition (attached-to-ansi)
+ --with-default-addressbook=VALUE
+ Default addressbook name (.addressbook)
+ --with-default-local-fullname=VALUE
+ Default local support fullname ("Local Support")
+ --with-default-local-address=VALUE
+ Default local support address (postmaster)
+ --with-default-keyboard-lock-count=VALUE
+ Default keyboard lock count (1)
+ --with-default-remote-addressbook-history=VALUE
+ Default address book history count (3)
+ --with-smime-public-cert-directory=VALUE
+ Default Public Cert Directory (.alpine-smime/public)
+ --with-smime-private-key-directory=VALUE
+ Default Private Key Directory
+ (.alpine-smime/private)
+ --with-smime-cacert-directory=VALUE
+ Default Cert Authority Directory (.alpine-smime/ca)
+ --with-default-printer=VALUE
+ Default printer (ANSI_PRINTER)
+ --with-passfile=FILENAME
+ Password cache file (NOT secure, NOT recommended)
+ --without-local-password-cache
+ Disable OS-specific password cache, if supported
+ --with-local-password-cache-method
+ OS-specific credential cache (OSX=APPLEKEYCHAIN,
+ Windows=WINCRED)
+ --with-default-sshpath=FILENAME
+ set default value of ssh command path (defining
+ should cause ssh to be preferred to rsh)
+ --with-default-sshcmd=PERCENT_S_STRING
+ set default value of ssh command string (usually "%s
+ %s -l %s exec /etc/r%sd")
+ --without-ssl Disable SSL support (OpenSSL)
+ --with-ssl-dir=DIR Root of SSL lib/include path
+ --with-ssl-certs-dir=DIR
+ Path to SSL certificate directory
+ --with-ssl-include-dir=DIR
+ SSL include file path
+ --with-ssl-lib-dir=DIR SSL library path
+ --without-krb5 Disable Kerberos support
+ --with-krb5-dir=DIR Root of Kerberos lib/include path
+ --with-krb5-include-dir=DIR
+ Kerberos include file path
+ --with-krb5-lib-dir=DIR Kerberos library path
+ --without-ldap Disable LDAP query support
+ --with-ldap-dir=DIR Root of LDAP lib/include path
+ --with-ldap-include-dir=DIR
+ Directory containing LDAP include files
+ --with-ldap-lib-dir=DIR LDAP library path
+ --without-smime Disable S/MIME
+ --without-tcl Disable TCL, thus Web Alpine support
+ --with-tcl-lib=LIBRARY Specific TCL Library, like \"tcl8.4\"
+ --with-tcl-include=DIR Directory containing TCL include files
+ --with-supplied-regex Use regex library supplied with alpine
+ --without-pthread Do NOT test for nor build with POSIX thread support
+ --with-system-mail-directory=DIR
+ Directory where local mail is delivered
+ --with-c-client-target=TARGET
+ IMAP build target (see imap/Makefile)
+ --without-ipv6 Disable IPv6, primarily to work around resolver
+ problems
+
+Some influential environment variables:
+ CC C compiler command
+ CFLAGS C compiler flags
+ LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
+ nonstandard directory <lib dir>
+ LIBS libraries to pass to the linker, e.g. -l<library>
+ CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
+ you have headers in a nonstandard directory <include dir>
+ CPP C preprocessor
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to <alpine-contact@u.washington.edu>.
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+ # If there are subdirs, report their specific --help.
+ for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+ test -d "$ac_dir" ||
+ { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+ continue
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+ cd "$ac_dir" || { ac_status=$?; continue; }
+ # Check for guested configure.
+ if test -f "$ac_srcdir/configure.gnu"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+ elif test -f "$ac_srcdir/configure"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure" --help=recursive
+ else
+ $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+ fi || ac_status=$?
+ cd "$ac_pwd" || { ac_status=$?; break; }
+ done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+ cat <<\_ACEOF
+alpine configure 2.10
+generated by GNU Autoconf 2.68
+
+Copyright (C) 2010 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+ exit
+fi
+
+## ------------------------ ##
+## Autoconf initialization. ##
+## ------------------------ ##
+
+# ac_fn_c_try_compile LINENO
+# --------------------------
+# Try to compile conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_compile ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ rm -f conftest.$ac_objext
+ if { { ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compile") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_compile
+
+# ac_fn_c_try_link LINENO
+# -----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_link ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ rm -f conftest.$ac_objext conftest$ac_exeext
+ if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
+ # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
+ # interfere with the next link command; also delete a directory that is
+ # left behind by Apple's compiler. We do this before executing the actions.
+ rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_link
+
+# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
+# -------------------------------------------------------
+# Tests whether HEADER exists and can be compiled using the include files in
+# INCLUDES, setting the cache variable VAR accordingly.
+ac_fn_c_check_header_compile ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ eval "$3=yes"
+else
+ eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_header_compile
+
+# ac_fn_c_try_cpp LINENO
+# ----------------------
+# Try to preprocess conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_cpp ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if { { ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } > conftest.i && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_cpp
+
+# ac_fn_c_try_run LINENO
+# ----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes
+# that executables *can* be run.
+ac_fn_c_try_run ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'
+ { { case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; }; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: program exited with status $ac_status" >&5
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=$ac_status
+fi
+ rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_run
+
+# ac_fn_c_check_func LINENO FUNC VAR
+# ----------------------------------
+# Tests whether FUNC exists, setting the cache variable VAR accordingly
+ac_fn_c_check_func ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $2 innocuous_$2
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $2 (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $2
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $2 ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_$2 || defined __stub___$2
+choke me
+#endif
+
+int
+main ()
+{
+return $2 ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ eval "$3=yes"
+else
+ eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_func
+
+# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES
+# -------------------------------------------------------
+# Tests whether HEADER exists, giving a warning if it cannot be compiled using
+# the include files in INCLUDES and setting the cache variable VAR
+# accordingly.
+ac_fn_c_check_header_mongrel ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if eval \${$3+:} false; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5
+$as_echo_n "checking $2 usability... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_header_compiler=yes
+else
+ ac_header_compiler=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5
+$as_echo_n "checking $2 presence... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <$2>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ ac_header_preproc=yes
+else
+ ac_header_preproc=no
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #((
+ yes:no: )
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+( $as_echo "## ---------------------------------------------- ##
+## Report this to alpine-contact@u.washington.edu ##
+## ---------------------------------------------- ##"
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ eval "$3=\$ac_header_compiler"
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_header_mongrel
+
+# ac_fn_c_check_type LINENO TYPE VAR INCLUDES
+# -------------------------------------------
+# Tests whether TYPE exists after having included INCLUDES, setting cache
+# variable VAR accordingly.
+ac_fn_c_check_type ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ eval "$3=no"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+if (sizeof ($2))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+if (sizeof (($2)))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+ eval "$3=yes"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_type
+
+# ac_fn_c_compute_int LINENO EXPR VAR INCLUDES
+# --------------------------------------------
+# Tries to find the compile-time value of EXPR in a program that includes
+# INCLUDES, setting VAR accordingly. Returns whether the value could be
+# computed
+ac_fn_c_compute_int ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if test "$cross_compiling" = yes; then
+ # Depending upon the size, compute the lo and hi bounds.
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) >= 0)];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_lo=0 ac_mid=0
+ while :; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) <= $ac_mid)];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_hi=$ac_mid; break
+else
+ as_fn_arith $ac_mid + 1 && ac_lo=$as_val
+ if test $ac_lo -le $ac_mid; then
+ ac_lo= ac_hi=
+ break
+ fi
+ as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ done
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) < 0)];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_hi=-1 ac_mid=-1
+ while :; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) >= $ac_mid)];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_lo=$ac_mid; break
+else
+ as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val
+ if test $ac_mid -le $ac_hi; then
+ ac_lo= ac_hi=
+ break
+ fi
+ as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ done
+else
+ ac_lo= ac_hi=
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+# Binary search between lo and hi bounds.
+while test "x$ac_lo" != "x$ac_hi"; do
+ as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) <= $ac_mid)];
+test_array [0] = 0
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_hi=$ac_mid
+else
+ as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+case $ac_lo in #((
+?*) eval "$3=\$ac_lo"; ac_retval=0 ;;
+'') ac_retval=1 ;;
+esac
+ else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+static long int longval () { return $2; }
+static unsigned long int ulongval () { return $2; }
+#include <stdio.h>
+#include <stdlib.h>
+int
+main ()
+{
+
+ FILE *f = fopen ("conftest.val", "w");
+ if (! f)
+ return 1;
+ if (($2) < 0)
+ {
+ long int i = longval ();
+ if (i != ($2))
+ return 1;
+ fprintf (f, "%ld", i);
+ }
+ else
+ {
+ unsigned long int i = ulongval ();
+ if (i != ($2))
+ return 1;
+ fprintf (f, "%lu", i);
+ }
+ /* Do not output a trailing newline, as this causes \r\n confusion
+ on some platforms. */
+ return ferror (f) || fclose (f) != 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ echo >>conftest.val; read $3 <conftest.val; ac_retval=0
+else
+ ac_retval=1
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+rm -f conftest.val
+
+ fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_compute_int
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by alpine $as_me 2.10, which was
+generated by GNU Autoconf 2.68. Invocation command line was
+
+ $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`
+
+/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown`
+/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`
+/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ $as_echo "PATH: $as_dir"
+ done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+ for ac_arg
+ do
+ case $ac_arg in
+ -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ continue ;;
+ *\'*)
+ ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ case $ac_pass in
+ 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
+ 2)
+ as_fn_append ac_configure_args1 " '$ac_arg'"
+ if test $ac_must_keep_next = true; then
+ ac_must_keep_next=false # Got value, back to normal.
+ else
+ case $ac_arg in
+ *=* | --config-cache | -C | -disable-* | --disable-* \
+ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+ | -with-* | --with-* | -without-* | --without-* | --x)
+ case "$ac_configure_args0 " in
+ "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+ esac
+ ;;
+ -* ) ac_must_keep_next=true ;;
+ esac
+ fi
+ as_fn_append ac_configure_args " '$ac_arg'"
+ ;;
+ esac
+ done
+done
+{ ac_configure_args0=; unset ac_configure_args0;}
+{ ac_configure_args1=; unset ac_configure_args1;}
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log. We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+ # Save into config.log some information that might help in debugging.
+ {
+ echo
+
+ $as_echo "## ---------------- ##
+## Cache variables. ##
+## ---------------- ##"
+ echo
+ # The following way of writing the cache mishandles newlines in values,
+(
+ for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+ *) { eval $ac_var=; unset $ac_var;} ;;
+ esac ;;
+ esac
+ done
+ (set) 2>&1 |
+ case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ sed -n \
+ "s/'\''/'\''\\\\'\'''\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+ ;; #(
+ *)
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+)
+ echo
+
+ $as_echo "## ----------------- ##
+## Output variables. ##
+## ----------------- ##"
+ echo
+ for ac_var in $ac_subst_vars
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ $as_echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+
+ if test -n "$ac_subst_files"; then
+ $as_echo "## ------------------- ##
+## File substitutions. ##
+## ------------------- ##"
+ echo
+ for ac_var in $ac_subst_files
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ $as_echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+ fi
+
+ if test -s confdefs.h; then
+ $as_echo "## ----------- ##
+## confdefs.h. ##
+## ----------- ##"
+ echo
+ cat confdefs.h
+ echo
+ fi
+ test "$ac_signal" != 0 &&
+ $as_echo "$as_me: caught signal $ac_signal"
+ $as_echo "$as_me: exit $exit_status"
+ } >&5
+ rm -f core *.core core.conftest.* &&
+ rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+ exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+ trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+$as_echo "/* confdefs.h */" > confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_URL "$PACKAGE_URL"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+ac_site_file1=NONE
+ac_site_file2=NONE
+if test -n "$CONFIG_SITE"; then
+ # We do not want a PATH search for config.site.
+ case $CONFIG_SITE in #((
+ -*) ac_site_file1=./$CONFIG_SITE;;
+ */*) ac_site_file1=$CONFIG_SITE;;
+ *) ac_site_file1=./$CONFIG_SITE;;
+ esac
+elif test "x$prefix" != xNONE; then
+ ac_site_file1=$prefix/share/config.site
+ ac_site_file2=$prefix/etc/config.site
+else
+ ac_site_file1=$ac_default_prefix/share/config.site
+ ac_site_file2=$ac_default_prefix/etc/config.site
+fi
+for ac_site_file in "$ac_site_file1" "$ac_site_file2"
+do
+ test "x$ac_site_file" = xNONE && continue
+ if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+$as_echo "$as_me: loading site script $ac_site_file" >&6;}
+ sed 's/^/| /' "$ac_site_file" >&5
+ . "$ac_site_file" \
+ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed to load site script $ac_site_file
+See \`config.log' for more details" "$LINENO" 5; }
+ fi
+done
+
+if test -r "$cache_file"; then
+ # Some versions of bash will fail to source /dev/null (special files
+ # actually), so we avoid doing that. DJGPP emulates it as a regular file.
+ if test /dev/null != "$cache_file" && test -f "$cache_file"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
+$as_echo "$as_me: loading cache $cache_file" >&6;}
+ case $cache_file in
+ [\\/]* | ?:[\\/]* ) . "$cache_file";;
+ *) . "./$cache_file";;
+ esac
+ fi
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
+$as_echo "$as_me: creating cache $cache_file" >&6;}
+ >$cache_file
+fi
+
+gt_needs="$gt_needs "
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+ eval ac_old_set=\$ac_cv_env_${ac_var}_set
+ eval ac_new_set=\$ac_env_${ac_var}_set
+ eval ac_old_val=\$ac_cv_env_${ac_var}_value
+ eval ac_new_val=\$ac_env_${ac_var}_value
+ case $ac_old_set,$ac_new_set in
+ set,)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,set)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,);;
+ *)
+ if test "x$ac_old_val" != "x$ac_new_val"; then
+ # differences in whitespace do not lead to failure.
+ ac_old_val_w=`echo x $ac_old_val`
+ ac_new_val_w=`echo x $ac_new_val`
+ if test "$ac_old_val_w" != "$ac_new_val_w"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
+$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+ ac_cache_corrupted=:
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+ eval $ac_var=\$ac_old_val
+ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5
+$as_echo "$as_me: former value: \`$ac_old_val'" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5
+$as_echo "$as_me: current value: \`$ac_new_val'" >&2;}
+ fi;;
+ esac
+ # Pass precious variables to config.status.
+ if test "$ac_new_set" = set; then
+ case $ac_new_val in
+ *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+ *) ac_arg=$ac_var=$ac_new_val ;;
+ esac
+ case " $ac_configure_args " in
+ *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
+ *) as_fn_append ac_configure_args " '$ac_arg'" ;;
+ esac
+ fi
+done
+if $ac_cache_corrupted; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
+$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+ as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
+fi
+## -------------------- ##
+## Main body of script. ##
+## -------------------- ##
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+ac_config_headers="$ac_config_headers include/config.h"
+
+
+am__api_version='1.11'
+
+ac_aux_dir=
+for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
+ if test -f "$ac_dir/install-sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f "$ac_dir/install.sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ elif test -f "$ac_dir/shtool"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/shtool install -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5
+fi
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var.
+ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var.
+ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
+
+
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+# Reject install programs that cannot install multiple files.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5
+$as_echo_n "checking for a BSD-compatible install... " >&6; }
+if test -z "$INSTALL"; then
+if ${ac_cv_path_install+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in #((
+ ./ | .// | /[cC]/* | \
+ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \
+ /usr/ucb/* ) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ # Don't use installbsd from OSF since it installs stuff as root
+ # by default.
+ for ac_prog in ginstall scoinst install; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then
+ if test $ac_prog = install &&
+ grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ :
+ elif test $ac_prog = install &&
+ grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # program-specific install script used by HP pwplus--don't use.
+ :
+ else
+ rm -rf conftest.one conftest.two conftest.dir
+ echo one > conftest.one
+ echo two > conftest.two
+ mkdir conftest.dir
+ if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" &&
+ test -s conftest.one && test -s conftest.two &&
+ test -s conftest.dir/conftest.one &&
+ test -s conftest.dir/conftest.two
+ then
+ ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+ break 3
+ fi
+ fi
+ fi
+ done
+ done
+ ;;
+esac
+
+ done
+IFS=$as_save_IFS
+
+rm -rf conftest.one conftest.two conftest.dir
+
+fi
+ if test "${ac_cv_path_install+set}" = set; then
+ INSTALL=$ac_cv_path_install
+ else
+ # As a last resort, use the slow shell script. Don't cache a
+ # value for INSTALL within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the value is a relative name.
+ INSTALL=$ac_install_sh
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5
+$as_echo "$INSTALL" >&6; }
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5
+$as_echo_n "checking whether build environment is sane... " >&6; }
+# Just in case
+sleep 1
+echo timestamp > conftest.file
+# Reject unsafe characters in $srcdir or the absolute working directory
+# name. Accept space and tab only in the latter.
+am_lf='
+'
+case `pwd` in
+ *[\\\"\#\$\&\'\`$am_lf]*)
+ as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;;
+esac
+case $srcdir in
+ *[\\\"\#\$\&\'\`$am_lf\ \ ]*)
+ as_fn_error $? "unsafe srcdir value: \`$srcdir'" "$LINENO" 5;;
+esac
+
+# Do `set' in a subshell so we don't clobber the current shell's
+# arguments. Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+ set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
+ if test "$*" = "X"; then
+ # -L didn't work.
+ set X `ls -t "$srcdir/configure" conftest.file`
+ fi
+ rm -f conftest.file
+ if test "$*" != "X $srcdir/configure conftest.file" \
+ && test "$*" != "X conftest.file $srcdir/configure"; then
+
+ # If neither matched, then we have a broken ls. This can happen
+ # if, for instance, CONFIG_SHELL is bash and it inherits a
+ # broken ls alias from the environment. This has actually
+ # happened. Such a system could not be considered "sane".
+ as_fn_error $? "ls -t appears to fail. Make sure there is not a broken
+alias in your environment" "$LINENO" 5
+ fi
+
+ test "$2" = conftest.file
+ )
+then
+ # Ok.
+ :
+else
+ as_fn_error $? "newly created file is older than distributed files!
+Check your system clock" "$LINENO" 5
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+test "$program_prefix" != NONE &&
+ program_transform_name="s&^&$program_prefix&;$program_transform_name"
+# Use a double $ so make ignores it.
+test "$program_suffix" != NONE &&
+ program_transform_name="s&\$&$program_suffix&;$program_transform_name"
+# Double any \ or $.
+# By default was `s,x,x', remove it if useless.
+ac_script='s/[\\$]/&&/g;s/;s,x,x,$//'
+program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"`
+
+# expand $ac_aux_dir to an absolute path
+am_aux_dir=`cd $ac_aux_dir && pwd`
+
+if test x"${MISSING+set}" != xset; then
+ case $am_aux_dir in
+ *\ * | *\ *)
+ MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
+ *)
+ MISSING="\${SHELL} $am_aux_dir/missing" ;;
+ esac
+fi
+# Use eval to expand $SHELL
+if eval "$MISSING --run true"; then
+ am_missing_run="$MISSING --run "
+else
+ am_missing_run=
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`missing' script is too old or missing" >&5
+$as_echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;}
+fi
+
+if test x"${install_sh}" != xset; then
+ case $am_aux_dir in
+ *\ * | *\ *)
+ install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
+ *)
+ install_sh="\${SHELL} $am_aux_dir/install-sh"
+ esac
+fi
+
+# Installed binaries are usually stripped using `strip' when the user
+# run `make install-strip'. However `strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the `STRIP' environment variable to overrule this program.
+if test "$cross_compiling" != no; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_STRIP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$STRIP"; then
+ ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5
+$as_echo "$STRIP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_STRIP"; then
+ ac_ct_STRIP=$STRIP
+ # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_STRIP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_STRIP"; then
+ ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_STRIP="strip"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5
+$as_echo "$ac_ct_STRIP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_STRIP" = x; then
+ STRIP=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ STRIP=$ac_ct_STRIP
+ fi
+else
+ STRIP="$ac_cv_prog_STRIP"
+fi
+
+fi
+INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5
+$as_echo_n "checking for a thread-safe mkdir -p... " >&6; }
+if test -z "$MKDIR_P"; then
+ if ${ac_cv_path_mkdir+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in mkdir gmkdir; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; } || continue
+ case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #(
+ 'mkdir (GNU coreutils) '* | \
+ 'mkdir (coreutils) '* | \
+ 'mkdir (fileutils) '4.1*)
+ ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext
+ break 3;;
+ esac
+ done
+ done
+ done
+IFS=$as_save_IFS
+
+fi
+
+ test -d ./--version && rmdir ./--version
+ if test "${ac_cv_path_mkdir+set}" = set; then
+ MKDIR_P="$ac_cv_path_mkdir -p"
+ else
+ # As a last resort, use the slow shell script. Don't cache a
+ # value for MKDIR_P within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the value is a relative name.
+ MKDIR_P="$ac_install_sh -d"
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5
+$as_echo "$MKDIR_P" >&6; }
+
+mkdir_p="$MKDIR_P"
+case $mkdir_p in
+ [\\/$]* | ?:[\\/]*) ;;
+ */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;;
+esac
+
+for ac_prog in gawk mawk nawk awk
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_AWK+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$AWK"; then
+ ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_AWK="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+AWK=$ac_cv_prog_AWK
+if test -n "$AWK"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5
+$as_echo "$AWK" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$AWK" && break
+done
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5
+$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; }
+set x ${MAKE-make}
+ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
+if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.make <<\_ACEOF
+SHELL = /bin/sh
+all:
+ @echo '@@@%%%=$(MAKE)=@@@%%%'
+_ACEOF
+# GNU make sometimes prints "make[1]: Entering ...", which would confuse us.
+case `${MAKE-make} -f conftest.make 2>/dev/null` in
+ *@@@%%%=?*=@@@%%%*)
+ eval ac_cv_prog_make_${ac_make}_set=yes;;
+ *)
+ eval ac_cv_prog_make_${ac_make}_set=no;;
+esac
+rm -f conftest.make
+fi
+if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ SET_MAKE=
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+ am__leading_dot=.
+else
+ am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+
+if test "`cd $srcdir && pwd`" != "`pwd`"; then
+ # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
+ # is not polluted with repeated "-I."
+ am__isrc=' -I$(srcdir)'
+ # test to see if srcdir already configured
+ if test -f $srcdir/config.status; then
+ as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5
+ fi
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+ if (cygpath --version) >/dev/null 2>/dev/null; then
+ CYGPATH_W='cygpath -w'
+ else
+ CYGPATH_W=echo
+ fi
+fi
+
+
+# Define the identity of the package.
+ PACKAGE='alpine'
+ VERSION='2.10'
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE "$PACKAGE"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define VERSION "$VERSION"
+_ACEOF
+
+# Some tools Automake needs.
+
+ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"}
+
+
+AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"}
+
+
+AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"}
+
+
+AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"}
+
+
+MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"}
+
+# We need awk for the "check" target. The system "awk" is bad on
+# some platforms.
+# Always define AMTAR for backward compatibility.
+
+AMTAR=${AMTAR-"${am_missing_run}tar"}
+
+am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable maintainer-specific portions of Makefiles" >&5
+$as_echo_n "checking whether to enable maintainer-specific portions of Makefiles... " >&6; }
+ # Check whether --enable-maintainer-mode was given.
+if test "${enable_maintainer_mode+set}" = set; then :
+ enableval=$enable_maintainer_mode; USE_MAINTAINER_MODE=$enableval
+else
+ USE_MAINTAINER_MODE=no
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $USE_MAINTAINER_MODE" >&5
+$as_echo "$USE_MAINTAINER_MODE" >&6; }
+ if test $USE_MAINTAINER_MODE = yes; then
+ MAINTAINER_MODE_TRUE=
+ MAINTAINER_MODE_FALSE='#'
+else
+ MAINTAINER_MODE_TRUE='#'
+ MAINTAINER_MODE_FALSE=
+fi
+
+ MAINT=$MAINTAINER_MODE_TRUE
+
+
+
+# Make sure we can run config.sub.
+$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
+ as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5
+$as_echo_n "checking build system type... " >&6; }
+if ${ac_cv_build+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_build_alias=$build_alias
+test "x$ac_build_alias" = x &&
+ ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"`
+test "x$ac_build_alias" = x &&
+ as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5
+ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` ||
+ as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5
+$as_echo "$ac_cv_build" >&6; }
+case $ac_cv_build in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;;
+esac
+build=$ac_cv_build
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_build
+shift
+build_cpu=$1
+build_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+build_os=$*
+IFS=$ac_save_IFS
+case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5
+$as_echo_n "checking host system type... " >&6; }
+if ${ac_cv_host+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "x$host_alias" = x; then
+ ac_cv_host=$ac_cv_build
+else
+ ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` ||
+ as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5
+$as_echo "$ac_cv_host" >&6; }
+case $ac_cv_host in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;;
+esac
+host=$ac_cv_host
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_host
+shift
+host_cpu=$1
+host_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+host_os=$*
+IFS=$ac_save_IFS
+case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: Configuring for $PACKAGE_STRING ($host))" >&5
+$as_echo "$as_me: Configuring for $PACKAGE_STRING ($host))" >&6;}
+
+# start out with intent to build Web Alpine
+WEB_BUILD=web/src/alpined.d
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="${ac_tool_prefix}gcc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_CC="gcc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="${ac_tool_prefix}cc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ fi
+fi
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# != 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+ fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in cl.exe
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$CC" && break
+ done
+fi
+if test -z "$CC"; then
+ ac_ct_CC=$CC
+ for ac_prog in cl.exe
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_CC="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_CC" && break
+done
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+fi
+
+fi
+
+
+test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "no acceptable C compiler found in \$PATH
+See \`config.log' for more details" "$LINENO" 5; }
+
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+ { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ sed '10a\
+... rest of stderr output deleted ...
+ 10q' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ fi
+ rm -f conftest.er1 conftest.err
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+done
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
+$as_echo_n "checking whether the C compiler works... " >&6; }
+ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+ * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+ esac
+done
+rm -f $ac_rmfiles
+
+if { { ac_try="$ac_link_default"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link_default") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile. We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+ ;;
+ [ab].out )
+ # We found the default executable, but exeext='' is most
+ # certainly right.
+ break;;
+ *.* )
+ if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+ then :; else
+ ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ fi
+ # We set ac_cv_exeext here because the later test for it is not
+ # safe: cross compilers may not add the suffix if given an `-o'
+ # argument, so we may need to know it at that point already.
+ # Even if this section looks crufty: it has the advantage of
+ # actually working.
+ break;;
+ * )
+ break;;
+ esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+ ac_file=''
+fi
+if test -z "$ac_file"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "C compiler cannot create executables
+See \`config.log' for more details" "$LINENO" 5; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
+$as_echo_n "checking for C compiler default output file name... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+$as_echo "$ac_file" >&6; }
+ac_exeext=$ac_cv_exeext
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
+$as_echo_n "checking for suffix of executables... " >&6; }
+if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+ *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ break;;
+ * ) break;;
+ esac
+done
+else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest conftest$ac_cv_exeext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+$as_echo "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdio.h>
+int
+main ()
+{
+FILE *f = fopen ("conftest.out", "w");
+ return ferror (f) || fclose (f) != 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files="$ac_clean_files conftest.out"
+# Check that the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+$as_echo_n "checking whether we are cross compiling... " >&6; }
+if test "$cross_compiling" != yes; then
+ { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+ if { ac_try='./conftest$ac_cv_exeext'
+ { { case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; }; then
+ cross_compiling=no
+ else
+ if test "$cross_compiling" = maybe; then
+ cross_compiling=yes
+ else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details" "$LINENO" 5; }
+ fi
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
+$as_echo "$cross_compiling" >&6; }
+
+rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
+$as_echo_n "checking for suffix of object files... " >&6; }
+if ${ac_cv_objext+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { { ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compile") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ for ac_file in conftest.o conftest.obj conftest.*; do
+ test -f "$ac_file" || continue;
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+ *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+ break;;
+ esac
+done
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of object files: cannot compile
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
+$as_echo "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
+$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
+if ${ac_cv_c_compiler_gnu+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_compiler_gnu=yes
+else
+ ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+ GCC=yes
+else
+ GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+$as_echo_n "checking whether $CC accepts -g... " >&6; }
+if ${ac_cv_prog_cc_g+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_save_c_werror_flag=$ac_c_werror_flag
+ ac_c_werror_flag=yes
+ ac_cv_prog_cc_g=no
+ CFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_g=yes
+else
+ CFLAGS=""
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+ ac_c_werror_flag=$ac_save_c_werror_flag
+ CFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+$as_echo "$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if ${ac_cv_prog_cc_c89+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
+ function prototypes and stuff, but not '\xHH' hex character constants.
+ These don't provoke an error unfortunately, instead are silently treated
+ as 'x'. The following induces an error, until -std is added to get
+ proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
+ array size at least. It's necessary to write '\x00'==0 to get something
+ that's true only with -std. */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+ inside strings and character constants. */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
+ ;
+ return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+ test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+ x)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+ xno)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+ *)
+ CC="$CC $ac_cv_prog_cc_c89"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c89" != xno; then :
+
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+DEPDIR="${am__leading_dot}deps"
+
+ac_config_commands="$ac_config_commands depfiles"
+
+
+am_make=${MAKE-make}
+cat > confinc << 'END'
+am__doit:
+ @echo this is the am__doit target
+.PHONY: am__doit
+END
+# If we don't find an include directive, just comment out the code.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5
+$as_echo_n "checking for style of include used by $am_make... " >&6; }
+am__include="#"
+am__quote=
+_am_result=none
+# First try GNU make style include.
+echo "include confinc" > confmf
+# Ignore all kinds of additional output from `make'.
+case `$am_make -s -f confmf 2> /dev/null` in #(
+*the\ am__doit\ target*)
+ am__include=include
+ am__quote=
+ _am_result=GNU
+ ;;
+esac
+# Now try BSD make style include.
+if test "$am__include" = "#"; then
+ echo '.include "confinc"' > confmf
+ case `$am_make -s -f confmf 2> /dev/null` in #(
+ *the\ am__doit\ target*)
+ am__include=.include
+ am__quote="\""
+ _am_result=BSD
+ ;;
+ esac
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5
+$as_echo "$_am_result" >&6; }
+rm -f confinc confmf
+
+# Check whether --enable-dependency-tracking was given.
+if test "${enable_dependency_tracking+set}" = set; then :
+ enableval=$enable_dependency_tracking;
+fi
+
+if test "x$enable_dependency_tracking" != xno; then
+ am_depcomp="$ac_aux_dir/depcomp"
+ AMDEPBACKSLASH='\'
+fi
+ if test "x$enable_dependency_tracking" != xno; then
+ AMDEP_TRUE=
+ AMDEP_FALSE='#'
+else
+ AMDEP_TRUE='#'
+ AMDEP_FALSE=
+fi
+
+
+
+depcc="$CC" am_compiler_list=
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5
+$as_echo_n "checking dependency style of $depcc... " >&6; }
+if ${am_cv_CC_dependencies_compiler_type+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+ # We make a subdir and do the tests there. Otherwise we can end up
+ # making bogus files that we don't know about and never remove. For
+ # instance it was reported that on HP-UX the gcc test will end up
+ # making a dummy file named `D' -- because `-MD' means `put the output
+ # in D'.
+ mkdir conftest.dir
+ # Copy depcomp to subdir because otherwise we won't find it if we're
+ # using a relative directory.
+ cp "$am_depcomp" conftest.dir
+ cd conftest.dir
+ # We will build objects and dependencies in a subdirectory because
+ # it helps to detect inapplicable dependency modes. For instance
+ # both Tru64's cc and ICC support -MD to output dependencies as a
+ # side effect of compilation, but ICC will put the dependencies in
+ # the current directory while Tru64 will put them in the object
+ # directory.
+ mkdir sub
+
+ am_cv_CC_dependencies_compiler_type=none
+ if test "$am_compiler_list" = ""; then
+ am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
+ fi
+ am__universal=false
+ case " $depcc " in #(
+ *\ -arch\ *\ -arch\ *) am__universal=true ;;
+ esac
+
+ for depmode in $am_compiler_list; do
+ # Setup a source with many dependencies, because some compilers
+ # like to wrap large dependency lists on column 80 (with \), and
+ # we should not choose a depcomp mode which is confused by this.
+ #
+ # We need to recreate these files for each test, as the compiler may
+ # overwrite some of them when testing with obscure command lines.
+ # This happens at least with the AIX C compiler.
+ : > sub/conftest.c
+ for i in 1 2 3 4 5 6; do
+ echo '#include "conftst'$i'.h"' >> sub/conftest.c
+ # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
+ # Solaris 8's {/usr,}/bin/sh.
+ touch sub/conftst$i.h
+ done
+ echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+ # We check with `-c' and `-o' for the sake of the "dashmstdout"
+ # mode. It turns out that the SunPro C++ compiler does not properly
+ # handle `-M -o', and we need to detect this. Also, some Intel
+ # versions had trouble with output in subdirs
+ am__obj=sub/conftest.${OBJEXT-o}
+ am__minus_obj="-o $am__obj"
+ case $depmode in
+ gcc)
+ # This depmode causes a compiler race in universal mode.
+ test "$am__universal" = false || continue
+ ;;
+ nosideeffect)
+ # after this tag, mechanisms are not by side-effect, so they'll
+ # only be used when explicitly requested
+ if test "x$enable_dependency_tracking" = xyes; then
+ continue
+ else
+ break
+ fi
+ ;;
+ msvisualcpp | msvcmsys)
+ # This compiler won't grok `-c -o', but also, the minuso test has
+ # not run yet. These depmodes are late enough in the game, and
+ # so weak that their functioning should not be impacted.
+ am__obj=conftest.${OBJEXT-o}
+ am__minus_obj=
+ ;;
+ none) break ;;
+ esac
+ if depmode=$depmode \
+ source=sub/conftest.c object=$am__obj \
+ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
+ >/dev/null 2>conftest.err &&
+ grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
+ ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+ # icc doesn't choke on unknown options, it will just issue warnings
+ # or remarks (even with -Werror). So we grep stderr for any message
+ # that says an option was ignored or not supported.
+ # When given -MP, icc 7.0 and 7.1 complain thusly:
+ # icc: Command line warning: ignoring option '-M'; no argument required
+ # The diagnosis changed in icc 8.0:
+ # icc: Command line remark: option '-MP' not supported
+ if (grep 'ignoring option' conftest.err ||
+ grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+ am_cv_CC_dependencies_compiler_type=$depmode
+ break
+ fi
+ fi
+ done
+
+ cd ..
+ rm -rf conftest.dir
+else
+ am_cv_CC_dependencies_compiler_type=none
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5
+$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; }
+CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type
+
+ if
+ test "x$enable_dependency_tracking" != xno \
+ && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then
+ am__fastdepCC_TRUE=
+ am__fastdepCC_FALSE='#'
+else
+ am__fastdepCC_TRUE='#'
+ am__fastdepCC_FALSE=
+fi
+
+
+ case $ac_cv_prog_cc_stdc in #(
+ no) :
+ ac_cv_prog_cc_c99=no; ac_cv_prog_cc_c89=no ;; #(
+ *) :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C99" >&5
+$as_echo_n "checking for $CC option to accept ISO C99... " >&6; }
+if ${ac_cv_prog_cc_c99+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_prog_cc_c99=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <wchar.h>
+#include <stdio.h>
+
+// Check varargs macros. These examples are taken from C99 6.10.3.5.
+#define debug(...) fprintf (stderr, __VA_ARGS__)
+#define showlist(...) puts (#__VA_ARGS__)
+#define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__))
+static void
+test_varargs_macros (void)
+{
+ int x = 1234;
+ int y = 5678;
+ debug ("Flag");
+ debug ("X = %d\n", x);
+ showlist (The first, second, and third items.);
+ report (x>y, "x is %d but y is %d", x, y);
+}
+
+// Check long long types.
+#define BIG64 18446744073709551615ull
+#define BIG32 4294967295ul
+#define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0)
+#if !BIG_OK
+ your preprocessor is broken;
+#endif
+#if BIG_OK
+#else
+ your preprocessor is broken;
+#endif
+static long long int bignum = -9223372036854775807LL;
+static unsigned long long int ubignum = BIG64;
+
+struct incomplete_array
+{
+ int datasize;
+ double data[];
+};
+
+struct named_init {
+ int number;
+ const wchar_t *name;
+ double average;
+};
+
+typedef const char *ccp;
+
+static inline int
+test_restrict (ccp restrict text)
+{
+ // See if C++-style comments work.
+ // Iterate through items via the restricted pointer.
+ // Also check for declarations in for loops.
+ for (unsigned int i = 0; *(text+i) != '\0'; ++i)
+ continue;
+ return 0;
+}
+
+// Check varargs and va_copy.
+static void
+test_varargs (const char *format, ...)
+{
+ va_list args;
+ va_start (args, format);
+ va_list args_copy;
+ va_copy (args_copy, args);
+
+ const char *str;
+ int number;
+ float fnumber;
+
+ while (*format)
+ {
+ switch (*format++)
+ {
+ case 's': // string
+ str = va_arg (args_copy, const char *);
+ break;
+ case 'd': // int
+ number = va_arg (args_copy, int);
+ break;
+ case 'f': // float
+ fnumber = va_arg (args_copy, double);
+ break;
+ default:
+ break;
+ }
+ }
+ va_end (args_copy);
+ va_end (args);
+}
+
+int
+main ()
+{
+
+ // Check bool.
+ _Bool success = false;
+
+ // Check restrict.
+ if (test_restrict ("String literal") == 0)
+ success = true;
+ char *restrict newvar = "Another string";
+
+ // Check varargs.
+ test_varargs ("s, d' f .", "string", 65, 34.234);
+ test_varargs_macros ();
+
+ // Check flexible array members.
+ struct incomplete_array *ia =
+ malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10));
+ ia->datasize = 10;
+ for (int i = 0; i < ia->datasize; ++i)
+ ia->data[i] = i * 1.234;
+
+ // Check named initializers.
+ struct named_init ni = {
+ .number = 34,
+ .name = L"Test wide string",
+ .average = 543.34343,
+ };
+
+ ni.number = 58;
+
+ int dynamic_array[ni.number];
+ dynamic_array[ni.number - 1] = 543;
+
+ // work around unused variable warnings
+ return (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == 'x'
+ || dynamic_array[ni.number - 1] != 543);
+
+ ;
+ return 0;
+}
+_ACEOF
+for ac_arg in '' -std=gnu99 -std=c99 -c99 -AC99 -xc99=all -qlanglvl=extc99
+do
+ CC="$ac_save_CC $ac_arg"
+ if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_c99=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+ test "x$ac_cv_prog_cc_c99" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c99" in
+ x)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+ xno)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+ *)
+ CC="$CC $ac_cv_prog_cc_c99"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5
+$as_echo "$ac_cv_prog_cc_c99" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c99" != xno; then :
+ ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if ${ac_cv_prog_cc_c89+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
+ function prototypes and stuff, but not '\xHH' hex character constants.
+ These don't provoke an error unfortunately, instead are silently treated
+ as 'x'. The following induces an error, until -std is added to get
+ proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
+ array size at least. It's necessary to write '\x00'==0 to get something
+ that's true only with -std. */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+ inside strings and character constants. */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
+ ;
+ return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+ test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+ x)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+ xno)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+ *)
+ CC="$CC $ac_cv_prog_cc_c89"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c89" != xno; then :
+ ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89
+else
+ ac_cv_prog_cc_stdc=no
+fi
+
+fi
+ ;;
+esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO Standard C" >&5
+$as_echo_n "checking for $CC option to accept ISO Standard C... " >&6; }
+ if ${ac_cv_prog_cc_stdc+:} false; then :
+ $as_echo_n "(cached) " >&6
+fi
+
+ case $ac_cv_prog_cc_stdc in #(
+ no) :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;; #(
+ '') :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;; #(
+ *) :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_stdc" >&5
+$as_echo "$ac_cv_prog_cc_stdc" >&6; } ;;
+esac
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5
+$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; }
+set x ${MAKE-make}
+ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
+if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.make <<\_ACEOF
+SHELL = /bin/sh
+all:
+ @echo '@@@%%%=$(MAKE)=@@@%%%'
+_ACEOF
+# GNU make sometimes prints "make[1]: Entering ...", which would confuse us.
+case `${MAKE-make} -f conftest.make 2>/dev/null` in
+ *@@@%%%=?*=@@@%%%*)
+ eval ac_cv_prog_make_${ac_make}_set=yes;;
+ *)
+ eval ac_cv_prog_make_${ac_make}_set=no;;
+esac
+rm -f conftest.make
+fi
+if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ SET_MAKE=
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5
+$as_echo_n "checking whether ln -s works... " >&6; }
+LN_S=$as_ln_s
+if test "$LN_S" = "ln -s"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5
+$as_echo "no, using $LN_S" >&6; }
+fi
+
+for ac_prog in gawk mawk nawk awk
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_AWK+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$AWK"; then
+ ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_AWK="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+AWK=$ac_cv_prog_AWK
+if test -n "$AWK"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5
+$as_echo "$AWK" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$AWK" && break
+done
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_RANLIB+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$RANLIB"; then
+ ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5
+$as_echo "$RANLIB" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+ ac_ct_RANLIB=$RANLIB
+ # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_RANLIB+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_RANLIB"; then
+ ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_RANLIB="ranlib"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5
+$as_echo "$ac_ct_RANLIB" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_RANLIB" = x; then
+ RANLIB=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ RANLIB=$ac_ct_RANLIB
+ fi
+else
+ RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+case `pwd` in
+ *\ * | *\ *)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5
+$as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;;
+esac
+
+
+
+macro_version='2.2.6b'
+macro_revision='1.3018'
+
+
+
+
+
+
+
+
+
+
+
+
+
+ltmain="$ac_aux_dir/ltmain.sh"
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5
+$as_echo_n "checking for a sed that does not truncate output... " >&6; }
+if ${ac_cv_path_SED+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
+ for ac_i in 1 2 3 4 5 6 7; do
+ ac_script="$ac_script$as_nl$ac_script"
+ done
+ echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed
+ { ac_script=; unset ac_script;}
+ if test -z "$SED"; then
+ ac_path_SED_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in sed gsed; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_SED="$as_dir/$ac_prog$ac_exec_ext"
+ { test -f "$ac_path_SED" && $as_test_x "$ac_path_SED"; } || continue
+# Check for GNU ac_path_SED and select it if it is found.
+ # Check for GNU $ac_path_SED
+case `"$ac_path_SED" --version 2>&1` in
+*GNU*)
+ ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo '' >> "conftest.nl"
+ "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_SED_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_SED="$ac_path_SED"
+ ac_path_SED_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_SED_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_SED"; then
+ as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5
+ fi
+else
+ ac_cv_path_SED=$SED
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5
+$as_echo "$ac_cv_path_SED" >&6; }
+ SED="$ac_cv_path_SED"
+ rm -f conftest.sed
+
+test -z "$SED" && SED=sed
+Xsed="$SED -e 1s/^X//"
+
+
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
+$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
+if ${ac_cv_path_GREP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$GREP"; then
+ ac_path_GREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in grep ggrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
+ { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue
+# Check for GNU ac_path_GREP and select it if it is found.
+ # Check for GNU $ac_path_GREP
+case `"$ac_path_GREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo 'GREP' >> "conftest.nl"
+ "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_GREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_GREP="$ac_path_GREP"
+ ac_path_GREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_GREP_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_GREP"; then
+ as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ fi
+else
+ ac_cv_path_GREP=$GREP
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5
+$as_echo "$ac_cv_path_GREP" >&6; }
+ GREP="$ac_cv_path_GREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5
+$as_echo_n "checking for egrep... " >&6; }
+if ${ac_cv_path_EGREP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
+ then ac_cv_path_EGREP="$GREP -E"
+ else
+ if test -z "$EGREP"; then
+ ac_path_EGREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in egrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
+ { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue
+# Check for GNU ac_path_EGREP and select it if it is found.
+ # Check for GNU $ac_path_EGREP
+case `"$ac_path_EGREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo 'EGREP' >> "conftest.nl"
+ "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_EGREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_EGREP="$ac_path_EGREP"
+ ac_path_EGREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_EGREP_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_EGREP"; then
+ as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ fi
+else
+ ac_cv_path_EGREP=$EGREP
+fi
+
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5
+$as_echo "$ac_cv_path_EGREP" >&6; }
+ EGREP="$ac_cv_path_EGREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5
+$as_echo_n "checking for fgrep... " >&6; }
+if ${ac_cv_path_FGREP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1
+ then ac_cv_path_FGREP="$GREP -F"
+ else
+ if test -z "$FGREP"; then
+ ac_path_FGREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in fgrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext"
+ { test -f "$ac_path_FGREP" && $as_test_x "$ac_path_FGREP"; } || continue
+# Check for GNU ac_path_FGREP and select it if it is found.
+ # Check for GNU $ac_path_FGREP
+case `"$ac_path_FGREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo 'FGREP' >> "conftest.nl"
+ "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_FGREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_FGREP="$ac_path_FGREP"
+ ac_path_FGREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_FGREP_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_FGREP"; then
+ as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ fi
+else
+ ac_cv_path_FGREP=$FGREP
+fi
+
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5
+$as_echo "$ac_cv_path_FGREP" >&6; }
+ FGREP="$ac_cv_path_FGREP"
+
+
+test -z "$GREP" && GREP=grep
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Check whether --with-gnu-ld was given.
+if test "${with_gnu_ld+set}" = set; then :
+ withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes
+else
+ with_gnu_ld=no
+fi
+
+ac_prog=ld
+if test "$GCC" = yes; then
+ # Check if gcc -print-prog-name=ld gives a path.
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5
+$as_echo_n "checking for ld used by $CC... " >&6; }
+ case $host in
+ *-*-mingw*)
+ # gcc leaves a trailing carriage return which upsets mingw
+ ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+ *)
+ ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+ esac
+ case $ac_prog in
+ # Accept absolute paths.
+ [\\/]* | ?:[\\/]*)
+ re_direlt='/[^/][^/]*/\.\./'
+ # Canonicalize the pathname of ld
+ ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'`
+ while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
+ ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
+ done
+ test -z "$LD" && LD="$ac_prog"
+ ;;
+ "")
+ # If it fails, then pretend we aren't using GCC.
+ ac_prog=ld
+ ;;
+ *)
+ # If it is relative, then search for the first ld in PATH.
+ with_gnu_ld=unknown
+ ;;
+ esac
+elif test "$with_gnu_ld" = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5
+$as_echo_n "checking for GNU ld... " >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5
+$as_echo_n "checking for non-GNU ld... " >&6; }
+fi
+if ${lt_cv_path_LD+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$LD"; then
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+ lt_cv_path_LD="$ac_dir/$ac_prog"
+ # Check to see if the program is GNU ld. I'd rather use --version,
+ # but apparently some variants of GNU ld only accept -v.
+ # Break only if it was the GNU/non-GNU ld that we prefer.
+ case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
+ *GNU* | *'with BFD'*)
+ test "$with_gnu_ld" != no && break
+ ;;
+ *)
+ test "$with_gnu_ld" != yes && break
+ ;;
+ esac
+ fi
+ done
+ IFS="$lt_save_ifs"
+else
+ lt_cv_path_LD="$LD" # Let the user override the test with a path.
+fi
+fi
+
+LD="$lt_cv_path_LD"
+if test -n "$LD"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LD" >&5
+$as_echo "$LD" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5
+$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; }
+if ${lt_cv_prog_gnu_ld+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ # I'd rather use --version here, but apparently some GNU lds only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+ lt_cv_prog_gnu_ld=yes
+ ;;
+*)
+ lt_cv_prog_gnu_ld=no
+ ;;
+esac
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_gnu_ld" >&5
+$as_echo "$lt_cv_prog_gnu_ld" >&6; }
+with_gnu_ld=$lt_cv_prog_gnu_ld
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5
+$as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; }
+if ${lt_cv_path_NM+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$NM"; then
+ # Let the user override the test.
+ lt_cv_path_NM="$NM"
+else
+ lt_nm_to_check="${ac_tool_prefix}nm"
+ if test -n "$ac_tool_prefix" && test "$build" = "$host"; then
+ lt_nm_to_check="$lt_nm_to_check nm"
+ fi
+ for lt_tmp_nm in $lt_nm_to_check; do
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ tmp_nm="$ac_dir/$lt_tmp_nm"
+ if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then
+ # Check to see if the nm accepts a BSD-compat flag.
+ # Adding the `sed 1q' prevents false positives on HP-UX, which says:
+ # nm: unknown option "B" ignored
+ # Tru64's nm complains that /dev/null is an invalid object file
+ case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in
+ */dev/null* | *'Invalid file or object type'*)
+ lt_cv_path_NM="$tmp_nm -B"
+ break
+ ;;
+ *)
+ case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in
+ */dev/null*)
+ lt_cv_path_NM="$tmp_nm -p"
+ break
+ ;;
+ *)
+ lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
+ continue # so that we can try to find one that supports BSD flags
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ done
+ IFS="$lt_save_ifs"
+ done
+ : ${lt_cv_path_NM=no}
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5
+$as_echo "$lt_cv_path_NM" >&6; }
+if test "$lt_cv_path_NM" != "no"; then
+ NM="$lt_cv_path_NM"
+else
+ # Didn't find any BSD compatible name lister, look for dumpbin.
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in "dumpbin -symbols" "link -dump -symbols"
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_DUMPBIN+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$DUMPBIN"; then
+ ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+DUMPBIN=$ac_cv_prog_DUMPBIN
+if test -n "$DUMPBIN"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5
+$as_echo "$DUMPBIN" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$DUMPBIN" && break
+ done
+fi
+if test -z "$DUMPBIN"; then
+ ac_ct_DUMPBIN=$DUMPBIN
+ for ac_prog in "dumpbin -symbols" "link -dump -symbols"
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_DUMPBIN+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_DUMPBIN"; then
+ ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_DUMPBIN="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN
+if test -n "$ac_ct_DUMPBIN"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5
+$as_echo "$ac_ct_DUMPBIN" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_DUMPBIN" && break
+done
+
+ if test "x$ac_ct_DUMPBIN" = x; then
+ DUMPBIN=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ DUMPBIN=$ac_ct_DUMPBIN
+ fi
+fi
+
+
+ if test "$DUMPBIN" != ":"; then
+ NM="$DUMPBIN"
+ fi
+fi
+test -z "$NM" && NM=nm
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5
+$as_echo_n "checking the name lister ($NM) interface... " >&6; }
+if ${lt_cv_nm_interface+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_nm_interface="BSD nm"
+ echo "int some_variable = 0;" > conftest.$ac_ext
+ (eval echo "\"\$as_me:5560: $ac_compile\"" >&5)
+ (eval "$ac_compile" 2>conftest.err)
+ cat conftest.err >&5
+ (eval echo "\"\$as_me:5563: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
+ (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
+ cat conftest.err >&5
+ (eval echo "\"\$as_me:5566: output\"" >&5)
+ cat conftest.out >&5
+ if $GREP 'External.*some_variable' conftest.out > /dev/null; then
+ lt_cv_nm_interface="MS dumpbin"
+ fi
+ rm -f conftest*
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5
+$as_echo "$lt_cv_nm_interface" >&6; }
+
+# find the maximum length of command line arguments
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5
+$as_echo_n "checking the maximum length of command line arguments... " >&6; }
+if ${lt_cv_sys_max_cmd_len+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ i=0
+ teststring="ABCD"
+
+ case $build_os in
+ msdosdjgpp*)
+ # On DJGPP, this test can blow up pretty badly due to problems in libc
+ # (any single argument exceeding 2000 bytes causes a buffer overrun
+ # during glob expansion). Even if it were fixed, the result of this
+ # check would be larger than it should be.
+ lt_cv_sys_max_cmd_len=12288; # 12K is about right
+ ;;
+
+ gnu*)
+ # Under GNU Hurd, this test is not required because there is
+ # no limit to the length of command line arguments.
+ # Libtool will interpret -1 as no limit whatsoever
+ lt_cv_sys_max_cmd_len=-1;
+ ;;
+
+ cygwin* | mingw* | cegcc*)
+ # On Win9x/ME, this test blows up -- it succeeds, but takes
+ # about 5 minutes as the teststring grows exponentially.
+ # Worse, since 9x/ME are not pre-emptively multitasking,
+ # you end up with a "frozen" computer, even though with patience
+ # the test eventually succeeds (with a max line length of 256k).
+ # Instead, let's just punt: use the minimum linelength reported by
+ # all of the supported platforms: 8192 (on NT/2K/XP).
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ amigaos*)
+ # On AmigaOS with pdksh, this test takes hours, literally.
+ # So we just punt and use a minimum line length of 8192.
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ netbsd* | freebsd* | openbsd* | darwin* | dragonfly*)
+ # This has been around since 386BSD, at least. Likely further.
+ if test -x /sbin/sysctl; then
+ lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax`
+ elif test -x /usr/sbin/sysctl; then
+ lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax`
+ else
+ lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs
+ fi
+ # And add a safety zone
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+ ;;
+
+ interix*)
+ # We know the value 262144 and hardcode it with a safety zone (like BSD)
+ lt_cv_sys_max_cmd_len=196608
+ ;;
+
+ osf*)
+ # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure
+ # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not
+ # nice to cause kernel panics so lets avoid the loop below.
+ # First set a reasonable default.
+ lt_cv_sys_max_cmd_len=16384
+ #
+ if test -x /sbin/sysconfig; then
+ case `/sbin/sysconfig -q proc exec_disable_arg_limit` in
+ *1*) lt_cv_sys_max_cmd_len=-1 ;;
+ esac
+ fi
+ ;;
+ sco3.2v5*)
+ lt_cv_sys_max_cmd_len=102400
+ ;;
+ sysv5* | sco5v6* | sysv4.2uw2*)
+ kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null`
+ if test -n "$kargmax"; then
+ lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'`
+ else
+ lt_cv_sys_max_cmd_len=32768
+ fi
+ ;;
+ *)
+ lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null`
+ if test -n "$lt_cv_sys_max_cmd_len"; then
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+ else
+ # Make teststring a little bigger before we do anything with it.
+ # a 1K string should be a reasonable start.
+ for i in 1 2 3 4 5 6 7 8 ; do
+ teststring=$teststring$teststring
+ done
+ SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}}
+ # If test is not a shell built-in, we'll probably end up computing a
+ # maximum length that is only half of the actual maximum length, but
+ # we can't tell.
+ while { test "X"`$SHELL $0 --fallback-echo "X$teststring$teststring" 2>/dev/null` \
+ = "XX$teststring$teststring"; } >/dev/null 2>&1 &&
+ test $i != 17 # 1/2 MB should be enough
+ do
+ i=`expr $i + 1`
+ teststring=$teststring$teststring
+ done
+ # Only check the string length outside the loop.
+ lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1`
+ teststring=
+ # Add a significant safety factor because C++ compilers can tack on
+ # massive amounts of additional arguments before passing them to the
+ # linker. It appears as though 1/2 is a usable value.
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2`
+ fi
+ ;;
+ esac
+
+fi
+
+if test -n $lt_cv_sys_max_cmd_len ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5
+$as_echo "$lt_cv_sys_max_cmd_len" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5
+$as_echo "none" >&6; }
+fi
+max_cmd_len=$lt_cv_sys_max_cmd_len
+
+
+
+
+
+
+: ${CP="cp -f"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands some XSI constructs" >&5
+$as_echo_n "checking whether the shell understands some XSI constructs... " >&6; }
+# Try some XSI features
+xsi_shell=no
+( _lt_dummy="a/b/c"
+ test "${_lt_dummy##*/},${_lt_dummy%/*},"${_lt_dummy%"$_lt_dummy"}, \
+ = c,a/b,, \
+ && eval 'test $(( 1 + 1 )) -eq 2 \
+ && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \
+ && xsi_shell=yes
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $xsi_shell" >&5
+$as_echo "$xsi_shell" >&6; }
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands \"+=\"" >&5
+$as_echo_n "checking whether the shell understands \"+=\"... " >&6; }
+lt_shell_append=no
+( foo=bar; set foo baz; eval "$1+=\$2" && test "$foo" = barbaz ) \
+ >/dev/null 2>&1 \
+ && lt_shell_append=yes
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_shell_append" >&5
+$as_echo "$lt_shell_append" >&6; }
+
+
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ lt_unset=unset
+else
+ lt_unset=false
+fi
+
+
+
+
+
+# test EBCDIC or ASCII
+case `echo X|tr X '\101'` in
+ A) # ASCII based system
+ # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr
+ lt_SP2NL='tr \040 \012'
+ lt_NL2SP='tr \015\012 \040\040'
+ ;;
+ *) # EBCDIC based system
+ lt_SP2NL='tr \100 \n'
+ lt_NL2SP='tr \r\n \100\100'
+ ;;
+esac
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5
+$as_echo_n "checking for $LD option to reload object files... " >&6; }
+if ${lt_cv_ld_reload_flag+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_ld_reload_flag='-r'
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5
+$as_echo "$lt_cv_ld_reload_flag" >&6; }
+reload_flag=$lt_cv_ld_reload_flag
+case $reload_flag in
+"" | " "*) ;;
+*) reload_flag=" $reload_flag" ;;
+esac
+reload_cmds='$LD$reload_flag -o $output$reload_objs'
+case $host_os in
+ darwin*)
+ if test "$GCC" = yes; then
+ reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs'
+ else
+ reload_cmds='$LD$reload_flag -o $output$reload_objs'
+ fi
+ ;;
+esac
+
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args.
+set dummy ${ac_tool_prefix}objdump; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_OBJDUMP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$OBJDUMP"; then
+ ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+OBJDUMP=$ac_cv_prog_OBJDUMP
+if test -n "$OBJDUMP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5
+$as_echo "$OBJDUMP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OBJDUMP"; then
+ ac_ct_OBJDUMP=$OBJDUMP
+ # Extract the first word of "objdump", so it can be a program name with args.
+set dummy objdump; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_OBJDUMP"; then
+ ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_OBJDUMP="objdump"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP
+if test -n "$ac_ct_OBJDUMP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5
+$as_echo "$ac_ct_OBJDUMP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_OBJDUMP" = x; then
+ OBJDUMP="false"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ OBJDUMP=$ac_ct_OBJDUMP
+ fi
+else
+ OBJDUMP="$ac_cv_prog_OBJDUMP"
+fi
+
+test -z "$OBJDUMP" && OBJDUMP=objdump
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5
+$as_echo_n "checking how to recognize dependent libraries... " >&6; }
+if ${lt_cv_deplibs_check_method+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_file_magic_cmd='$MAGIC_CMD'
+lt_cv_file_magic_test_file=
+lt_cv_deplibs_check_method='unknown'
+# Need to set the preceding variable on all platforms that support
+# interlibrary dependencies.
+# 'none' -- dependencies not supported.
+# `unknown' -- same as none, but documents that we really don't know.
+# 'pass_all' -- all dependencies passed with no checks.
+# 'test_compile' -- check by making test program.
+# 'file_magic [[regex]]' -- check by looking for files in library path
+# which responds to the $file_magic_cmd with a given extended regex.
+# If you have `file' or equivalent on your system and you're not sure
+# whether `pass_all' will *always* work, you probably want this one.
+
+case $host_os in
+aix[4-9]*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+beos*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+bsdi[45]*)
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)'
+ lt_cv_file_magic_cmd='/usr/bin/file -L'
+ lt_cv_file_magic_test_file=/shlib/libc.so
+ ;;
+
+cygwin*)
+ # func_win32_libid is a shell function defined in ltmain.sh
+ lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+ lt_cv_file_magic_cmd='func_win32_libid'
+ ;;
+
+mingw* | pw32*)
+ # Base MSYS/MinGW do not provide the 'file' command needed by
+ # func_win32_libid shell function, so use a weaker test based on 'objdump',
+ # unless we find 'file', for example because we are cross-compiling.
+ if ( file / ) >/dev/null 2>&1; then
+ lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+ lt_cv_file_magic_cmd='func_win32_libid'
+ else
+ lt_cv_deplibs_check_method='file_magic file format pei*-i386(.*architecture: i386)?'
+ lt_cv_file_magic_cmd='$OBJDUMP -f'
+ fi
+ ;;
+
+cegcc)
+ # use the weaker test based on 'objdump'. See mingw*.
+ lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?'
+ lt_cv_file_magic_cmd='$OBJDUMP -f'
+ ;;
+
+darwin* | rhapsody*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+freebsd* | dragonfly*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+ case $host_cpu in
+ i*86 )
+ # Not sure whether the presence of OpenBSD here was a mistake.
+ # Let's accept both of them until this is cleared up.
+ lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library'
+ lt_cv_file_magic_cmd=/usr/bin/file
+ lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
+ ;;
+ esac
+ else
+ lt_cv_deplibs_check_method=pass_all
+ fi
+ ;;
+
+gnu*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+hpux10.20* | hpux11*)
+ lt_cv_file_magic_cmd=/usr/bin/file
+ case $host_cpu in
+ ia64*)
+ lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64'
+ lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so
+ ;;
+ hppa*64*)
+ lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - PA-RISC [0-9].[0-9]'
+ lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl
+ ;;
+ *)
+ lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9].[0-9]) shared library'
+ lt_cv_file_magic_test_file=/usr/lib/libc.sl
+ ;;
+ esac
+ ;;
+
+interix[3-9]*)
+ # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$'
+ ;;
+
+irix5* | irix6* | nonstopux*)
+ case $LD in
+ *-32|*"-32 ") libmagic=32-bit;;
+ *-n32|*"-n32 ") libmagic=N32;;
+ *-64|*"-64 ") libmagic=64-bit;;
+ *) libmagic=never-match;;
+ esac
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+# This must be Linux ELF.
+linux* | k*bsd*-gnu)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
+ else
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$'
+ fi
+ ;;
+
+newos6*)
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)'
+ lt_cv_file_magic_cmd=/usr/bin/file
+ lt_cv_file_magic_test_file=/usr/lib/libnls.so
+ ;;
+
+*nto* | *qnx*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+openbsd*)
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$'
+ else
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
+ fi
+ ;;
+
+osf3* | osf4* | osf5*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+rdos*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+solaris*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+sysv4 | sysv4.3*)
+ case $host_vendor in
+ motorola)
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]'
+ lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*`
+ ;;
+ ncr)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ sequent)
+ lt_cv_file_magic_cmd='/bin/file'
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )'
+ ;;
+ sni)
+ lt_cv_file_magic_cmd='/bin/file'
+ lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib"
+ lt_cv_file_magic_test_file=/lib/libc.so
+ ;;
+ siemens)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ pc)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ esac
+ ;;
+
+tpf*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+esac
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5
+$as_echo "$lt_cv_deplibs_check_method" >&6; }
+file_magic_cmd=$lt_cv_file_magic_cmd
+deplibs_check_method=$lt_cv_deplibs_check_method
+test -z "$deplibs_check_method" && deplibs_check_method=unknown
+
+
+
+
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ar; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_AR+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$AR"; then
+ ac_cv_prog_AR="$AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_AR="${ac_tool_prefix}ar"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+AR=$ac_cv_prog_AR
+if test -n "$AR"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5
+$as_echo "$AR" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_AR"; then
+ ac_ct_AR=$AR
+ # Extract the first word of "ar", so it can be a program name with args.
+set dummy ar; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_AR+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_AR"; then
+ ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_AR="ar"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_AR=$ac_cv_prog_ac_ct_AR
+if test -n "$ac_ct_AR"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5
+$as_echo "$ac_ct_AR" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_AR" = x; then
+ AR="false"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ AR=$ac_ct_AR
+ fi
+else
+ AR="$ac_cv_prog_AR"
+fi
+
+test -z "$AR" && AR=ar
+test -z "$AR_FLAGS" && AR_FLAGS=cru
+
+
+
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_STRIP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$STRIP"; then
+ ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5
+$as_echo "$STRIP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_STRIP"; then
+ ac_ct_STRIP=$STRIP
+ # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_STRIP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_STRIP"; then
+ ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_STRIP="strip"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5
+$as_echo "$ac_ct_STRIP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_STRIP" = x; then
+ STRIP=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ STRIP=$ac_ct_STRIP
+ fi
+else
+ STRIP="$ac_cv_prog_STRIP"
+fi
+
+test -z "$STRIP" && STRIP=:
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_RANLIB+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$RANLIB"; then
+ ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5
+$as_echo "$RANLIB" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+ ac_ct_RANLIB=$RANLIB
+ # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_RANLIB+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_RANLIB"; then
+ ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_RANLIB="ranlib"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5
+$as_echo "$ac_ct_RANLIB" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_RANLIB" = x; then
+ RANLIB=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ RANLIB=$ac_ct_RANLIB
+ fi
+else
+ RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+test -z "$RANLIB" && RANLIB=:
+
+
+
+
+
+
+# Determine commands to create old-style static archives.
+old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs'
+old_postinstall_cmds='chmod 644 $oldlib'
+old_postuninstall_cmds=
+
+if test -n "$RANLIB"; then
+ case $host_os in
+ openbsd*)
+ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$oldlib"
+ ;;
+ *)
+ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$oldlib"
+ ;;
+ esac
+ old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib"
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+
+# Check for command to grab the raw symbol name followed by C symbol from nm.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5
+$as_echo_n "checking command to parse $NM output from $compiler object... " >&6; }
+if ${lt_cv_sys_global_symbol_pipe+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+# These are sane defaults that work on at least a few old systems.
+# [They come from Ultrix. What could be older than Ultrix?!! ;)]
+
+# Character class describing NM global symbol codes.
+symcode='[BCDEGRST]'
+
+# Regexp to match symbols that can be accessed directly from C.
+sympat='\([_A-Za-z][_A-Za-z0-9]*\)'
+
+# Define system-specific variables.
+case $host_os in
+aix*)
+ symcode='[BCDT]'
+ ;;
+cygwin* | mingw* | pw32* | cegcc*)
+ symcode='[ABCDGISTW]'
+ ;;
+hpux*)
+ if test "$host_cpu" = ia64; then
+ symcode='[ABCDEGRST]'
+ fi
+ ;;
+irix* | nonstopux*)
+ symcode='[BCDEGRST]'
+ ;;
+osf*)
+ symcode='[BCDEGQRST]'
+ ;;
+solaris*)
+ symcode='[BDRT]'
+ ;;
+sco3.2v5*)
+ symcode='[DT]'
+ ;;
+sysv4.2uw2*)
+ symcode='[DT]'
+ ;;
+sysv5* | sco5v6* | unixware* | OpenUNIX*)
+ symcode='[ABDT]'
+ ;;
+sysv4)
+ symcode='[DFNSTU]'
+ ;;
+esac
+
+# If we're using GNU nm, then use its standard symbol codes.
+case `$NM -V 2>&1` in
+*GNU* | *'with BFD'*)
+ symcode='[ABCDGIRSTW]' ;;
+esac
+
+# Transform an extracted symbol line into a proper C declaration.
+# Some systems (esp. on ia64) link data and code symbols differently,
+# so use this general approach.
+lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'"
+
+# Transform an extracted symbol line into symbol name and symbol address
+lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"\2\", (void *) \&\2},/p'"
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([^ ]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \(lib[^ ]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"lib\2\", (void *) \&\2},/p'"
+
+# Handle CRLF in mingw tool chain
+opt_cr=
+case $build_os in
+mingw*)
+ opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp
+ ;;
+esac
+
+# Try without a prefix underscore, then with it.
+for ac_symprfx in "" "_"; do
+
+ # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol.
+ symxfrm="\\1 $ac_symprfx\\2 \\2"
+
+ # Write the raw and C identifiers.
+ if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+ # Fake it for dumpbin and say T for any non-static function
+ # and D for any global variable.
+ # Also find C++ and __fastcall symbols from MSVC++,
+ # which start with @ or ?.
+ lt_cv_sys_global_symbol_pipe="$AWK '"\
+" {last_section=section; section=\$ 3};"\
+" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\
+" \$ 0!~/External *\|/{next};"\
+" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\
+" {if(hide[section]) next};"\
+" {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\
+" {split(\$ 0, a, /\||\r/); split(a[2], s)};"\
+" s[1]~/^[@?]/{print s[1], s[1]; next};"\
+" s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\
+" ' prfx=^$ac_symprfx"
+ else
+ lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'"
+ fi
+
+ # Check to see that the pipe works correctly.
+ pipe_works=no
+
+ rm -f conftest*
+ cat > conftest.$ac_ext <<_LT_EOF
+#ifdef __cplusplus
+extern "C" {
+#endif
+char nm_test_var;
+void nm_test_func(void);
+void nm_test_func(void){}
+#ifdef __cplusplus
+}
+#endif
+int main(){nm_test_var='a';nm_test_func();return(0);}
+_LT_EOF
+
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ # Now try to grab the symbols.
+ nlist=conftest.nm
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist\""; } >&5
+ (eval $NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && test -s "$nlist"; then
+ # Try sorting and uniquifying the output.
+ if sort "$nlist" | uniq > "$nlist"T; then
+ mv -f "$nlist"T "$nlist"
+ else
+ rm -f "$nlist"T
+ fi
+
+ # Make sure that we snagged all the symbols we need.
+ if $GREP ' nm_test_var$' "$nlist" >/dev/null; then
+ if $GREP ' nm_test_func$' "$nlist" >/dev/null; then
+ cat <<_LT_EOF > conftest.$ac_ext
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+_LT_EOF
+ # Now generate the symbol file.
+ eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext'
+
+ cat <<_LT_EOF >> conftest.$ac_ext
+
+/* The mapping between symbol names and symbols. */
+const struct {
+ const char *name;
+ void *address;
+}
+lt__PROGRAM__LTX_preloaded_symbols[] =
+{
+ { "@PROGRAM@", (void *) 0 },
+_LT_EOF
+ $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext
+ cat <<\_LT_EOF >> conftest.$ac_ext
+ {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+ return lt__PROGRAM__LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+_LT_EOF
+ # Now try linking the two files.
+ mv conftest.$ac_objext conftstm.$ac_objext
+ lt_save_LIBS="$LIBS"
+ lt_save_CFLAGS="$CFLAGS"
+ LIBS="conftstm.$ac_objext"
+ CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag"
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && test -s conftest${ac_exeext}; then
+ pipe_works=yes
+ fi
+ LIBS="$lt_save_LIBS"
+ CFLAGS="$lt_save_CFLAGS"
+ else
+ echo "cannot find nm_test_func in $nlist" >&5
+ fi
+ else
+ echo "cannot find nm_test_var in $nlist" >&5
+ fi
+ else
+ echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5
+ fi
+ else
+ echo "$progname: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ fi
+ rm -rf conftest* conftst*
+
+ # Do not use the global_symbol_pipe unless it works.
+ if test "$pipe_works" = yes; then
+ break
+ else
+ lt_cv_sys_global_symbol_pipe=
+ fi
+done
+
+fi
+
+if test -z "$lt_cv_sys_global_symbol_pipe"; then
+ lt_cv_sys_global_symbol_to_cdecl=
+fi
+if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5
+$as_echo "failed" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5
+$as_echo "ok" >&6; }
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Check whether --enable-libtool-lock was given.
+if test "${enable_libtool_lock+set}" = set; then :
+ enableval=$enable_libtool_lock;
+fi
+
+test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes
+
+# Some flags need to be propagated to the compiler or linker for good
+# libtool support.
+case $host in
+ia64-*-hpux*)
+ # Find out which ABI we are using.
+ echo 'int i;' > conftest.$ac_ext
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ case `/usr/bin/file conftest.$ac_objext` in
+ *ELF-32*)
+ HPUX_IA64_MODE="32"
+ ;;
+ *ELF-64*)
+ HPUX_IA64_MODE="64"
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+*-*-irix6*)
+ # Find out which ABI we are using.
+ echo '#line 6761 "configure"' > conftest.$ac_ext
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ if test "$lt_cv_prog_gnu_ld" = yes; then
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ LD="${LD-ld} -melf32bsmip"
+ ;;
+ *N32*)
+ LD="${LD-ld} -melf32bmipn32"
+ ;;
+ *64-bit*)
+ LD="${LD-ld} -melf64bmip"
+ ;;
+ esac
+ else
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ LD="${LD-ld} -32"
+ ;;
+ *N32*)
+ LD="${LD-ld} -n32"
+ ;;
+ *64-bit*)
+ LD="${LD-ld} -64"
+ ;;
+ esac
+ fi
+ fi
+ rm -rf conftest*
+ ;;
+
+x86_64-*kfreebsd*-gnu|x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*| \
+s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
+ # Find out which ABI we are using.
+ echo 'int i;' > conftest.$ac_ext
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ case `/usr/bin/file conftest.o` in
+ *32-bit*)
+ case $host in
+ x86_64-*kfreebsd*-gnu)
+ LD="${LD-ld} -m elf_i386_fbsd"
+ ;;
+ x86_64-*linux*)
+ LD="${LD-ld} -m elf_i386"
+ ;;
+ ppc64-*linux*|powerpc64-*linux*)
+ LD="${LD-ld} -m elf32ppclinux"
+ ;;
+ s390x-*linux*)
+ LD="${LD-ld} -m elf_s390"
+ ;;
+ sparc64-*linux*)
+ LD="${LD-ld} -m elf32_sparc"
+ ;;
+ esac
+ ;;
+ *64-bit*)
+ case $host in
+ x86_64-*kfreebsd*-gnu)
+ LD="${LD-ld} -m elf_x86_64_fbsd"
+ ;;
+ x86_64-*linux*)
+ LD="${LD-ld} -m elf_x86_64"
+ ;;
+ ppc*-*linux*|powerpc*-*linux*)
+ LD="${LD-ld} -m elf64ppc"
+ ;;
+ s390*-*linux*|s390*-*tpf*)
+ LD="${LD-ld} -m elf64_s390"
+ ;;
+ sparc*-*linux*)
+ LD="${LD-ld} -m elf64_sparc"
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+
+*-*-sco3.2v5*)
+ # On SCO OpenServer 5, we need -belf to get full-featured binaries.
+ SAVE_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS -belf"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5
+$as_echo_n "checking whether the C compiler needs -belf... " >&6; }
+if ${lt_cv_cc_needs_belf+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ lt_cv_cc_needs_belf=yes
+else
+ lt_cv_cc_needs_belf=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5
+$as_echo "$lt_cv_cc_needs_belf" >&6; }
+ if test x"$lt_cv_cc_needs_belf" != x"yes"; then
+ # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
+ CFLAGS="$SAVE_CFLAGS"
+ fi
+ ;;
+sparc*-*solaris*)
+ # Find out which ABI we are using.
+ echo 'int i;' > conftest.$ac_ext
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ case `/usr/bin/file conftest.o` in
+ *64-bit*)
+ case $lt_cv_prog_gnu_ld in
+ yes*) LD="${LD-ld} -m elf64_sparc" ;;
+ *)
+ if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then
+ LD="${LD-ld} -64"
+ fi
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+esac
+
+need_locks="$enable_libtool_lock"
+
+
+ case $host_os in
+ rhapsody* | darwin*)
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args.
+set dummy ${ac_tool_prefix}dsymutil; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_DSYMUTIL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$DSYMUTIL"; then
+ ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+DSYMUTIL=$ac_cv_prog_DSYMUTIL
+if test -n "$DSYMUTIL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5
+$as_echo "$DSYMUTIL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_DSYMUTIL"; then
+ ac_ct_DSYMUTIL=$DSYMUTIL
+ # Extract the first word of "dsymutil", so it can be a program name with args.
+set dummy dsymutil; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_DSYMUTIL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_DSYMUTIL"; then
+ ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_DSYMUTIL="dsymutil"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL
+if test -n "$ac_ct_DSYMUTIL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5
+$as_echo "$ac_ct_DSYMUTIL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_DSYMUTIL" = x; then
+ DSYMUTIL=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ DSYMUTIL=$ac_ct_DSYMUTIL
+ fi
+else
+ DSYMUTIL="$ac_cv_prog_DSYMUTIL"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args.
+set dummy ${ac_tool_prefix}nmedit; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_NMEDIT+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$NMEDIT"; then
+ ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+NMEDIT=$ac_cv_prog_NMEDIT
+if test -n "$NMEDIT"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5
+$as_echo "$NMEDIT" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_NMEDIT"; then
+ ac_ct_NMEDIT=$NMEDIT
+ # Extract the first word of "nmedit", so it can be a program name with args.
+set dummy nmedit; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_NMEDIT+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_NMEDIT"; then
+ ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_NMEDIT="nmedit"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT
+if test -n "$ac_ct_NMEDIT"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5
+$as_echo "$ac_ct_NMEDIT" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_NMEDIT" = x; then
+ NMEDIT=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ NMEDIT=$ac_ct_NMEDIT
+ fi
+else
+ NMEDIT="$ac_cv_prog_NMEDIT"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args.
+set dummy ${ac_tool_prefix}lipo; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_LIPO+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$LIPO"; then
+ ac_cv_prog_LIPO="$LIPO" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_LIPO="${ac_tool_prefix}lipo"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+LIPO=$ac_cv_prog_LIPO
+if test -n "$LIPO"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5
+$as_echo "$LIPO" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_LIPO"; then
+ ac_ct_LIPO=$LIPO
+ # Extract the first word of "lipo", so it can be a program name with args.
+set dummy lipo; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_LIPO+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_LIPO"; then
+ ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_LIPO="lipo"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO
+if test -n "$ac_ct_LIPO"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5
+$as_echo "$ac_ct_LIPO" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_LIPO" = x; then
+ LIPO=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ LIPO=$ac_ct_LIPO
+ fi
+else
+ LIPO="$ac_cv_prog_LIPO"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args.
+set dummy ${ac_tool_prefix}otool; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_OTOOL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$OTOOL"; then
+ ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_OTOOL="${ac_tool_prefix}otool"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+OTOOL=$ac_cv_prog_OTOOL
+if test -n "$OTOOL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5
+$as_echo "$OTOOL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OTOOL"; then
+ ac_ct_OTOOL=$OTOOL
+ # Extract the first word of "otool", so it can be a program name with args.
+set dummy otool; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_OTOOL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_OTOOL"; then
+ ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_OTOOL="otool"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL
+if test -n "$ac_ct_OTOOL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5
+$as_echo "$ac_ct_OTOOL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_OTOOL" = x; then
+ OTOOL=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ OTOOL=$ac_ct_OTOOL
+ fi
+else
+ OTOOL="$ac_cv_prog_OTOOL"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args.
+set dummy ${ac_tool_prefix}otool64; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_OTOOL64+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$OTOOL64"; then
+ ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+OTOOL64=$ac_cv_prog_OTOOL64
+if test -n "$OTOOL64"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5
+$as_echo "$OTOOL64" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OTOOL64"; then
+ ac_ct_OTOOL64=$OTOOL64
+ # Extract the first word of "otool64", so it can be a program name with args.
+set dummy otool64; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_OTOOL64+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_OTOOL64"; then
+ ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ac_ct_OTOOL64="otool64"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64
+if test -n "$ac_ct_OTOOL64"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5
+$as_echo "$ac_ct_OTOOL64" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_OTOOL64" = x; then
+ OTOOL64=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ OTOOL64=$ac_ct_OTOOL64
+ fi
+else
+ OTOOL64="$ac_cv_prog_OTOOL64"
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5
+$as_echo_n "checking for -single_module linker flag... " >&6; }
+if ${lt_cv_apple_cc_single_mod+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_apple_cc_single_mod=no
+ if test -z "${LT_MULTI_MODULE}"; then
+ # By default we will add the -single_module flag. You can override
+ # by either setting the environment variable LT_MULTI_MODULE
+ # non-empty at configure time, or by adding -multi_module to the
+ # link flags.
+ rm -rf libconftest.dylib*
+ echo "int foo(void){return 1;}" > conftest.c
+ echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+-dynamiclib -Wl,-single_module conftest.c" >&5
+ $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err
+ _lt_result=$?
+ if test -f libconftest.dylib && test ! -s conftest.err && test $_lt_result = 0; then
+ lt_cv_apple_cc_single_mod=yes
+ else
+ cat conftest.err >&5
+ fi
+ rm -rf libconftest.dylib*
+ rm -f conftest.*
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5
+$as_echo "$lt_cv_apple_cc_single_mod" >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5
+$as_echo_n "checking for -exported_symbols_list linker flag... " >&6; }
+if ${lt_cv_ld_exported_symbols_list+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_ld_exported_symbols_list=no
+ save_LDFLAGS=$LDFLAGS
+ echo "_main" > conftest.sym
+ LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ lt_cv_ld_exported_symbols_list=yes
+else
+ lt_cv_ld_exported_symbols_list=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LDFLAGS="$save_LDFLAGS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5
+$as_echo "$lt_cv_ld_exported_symbols_list" >&6; }
+ case $host_os in
+ rhapsody* | darwin1.[012])
+ _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;;
+ darwin1.*)
+ _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+ darwin*) # darwin 5.x on
+ # if running on 10.5 or later, the deployment target defaults
+ # to the OS version, if on x86, and 10.4, the deployment
+ # target defaults to 10.4. Don't you love it?
+ case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
+ 10.0,*86*-darwin8*|10.0,*-darwin[91]*)
+ _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+ 10.[012]*)
+ _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+ 10.*)
+ _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+ esac
+ ;;
+ esac
+ if test "$lt_cv_apple_cc_single_mod" = "yes"; then
+ _lt_dar_single_mod='$single_module'
+ fi
+ if test "$lt_cv_ld_exported_symbols_list" = "yes"; then
+ _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym'
+ else
+ _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}'
+ fi
+ if test "$DSYMUTIL" != ":"; then
+ _lt_dsymutil='~$DSYMUTIL $lib || :'
+ else
+ _lt_dsymutil=
+ fi
+ ;;
+ esac
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
+$as_echo_n "checking how to run the C preprocessor... " >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+ if ${ac_cv_prog_CPP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ # Double quotes because CPP needs to be expanded
+ for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+ do
+ ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ # Broken: success on invalid input.
+continue
+else
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+ break
+fi
+
+ done
+ ac_cv_prog_CPP=$CPP
+
+fi
+ CPP=$ac_cv_prog_CPP
+else
+ ac_cv_prog_CPP=$CPP
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
+$as_echo "$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ # Broken: success on invalid input.
+continue
+else
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+
+else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if ${ac_cv_header_stdc+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_header_stdc=yes
+else
+ ac_cv_header_stdc=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "memchr" >/dev/null 2>&1; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "free" >/dev/null 2>&1; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+ if test "$cross_compiling" = yes; then :
+ :
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+ (('a' <= (c) && (c) <= 'i') \
+ || ('j' <= (c) && (c) <= 'r') \
+ || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ if (XOR (islower (i), ISLOWER (i))
+ || toupper (i) != TOUPPER (i))
+ return 2;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+$as_echo "#define STDC_HEADERS 1" >>confdefs.h
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+ inttypes.h stdint.h unistd.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
+"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in dlfcn.h
+do :
+ ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default
+"
+if test "x$ac_cv_header_dlfcn_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_DLFCN_H 1
+_ACEOF
+
+fi
+
+done
+
+
+
+# Set options
+
+
+
+ enable_dlopen=no
+
+
+ enable_win32_dll=no
+
+
+ # Check whether --enable-shared was given.
+if test "${enable_shared+set}" = set; then :
+ enableval=$enable_shared; p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_shared=yes ;;
+ no) enable_shared=no ;;
+ *)
+ enable_shared=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ for pkg in $enableval; do
+ IFS="$lt_save_ifs"
+ if test "X$pkg" = "X$p"; then
+ enable_shared=yes
+ fi
+ done
+ IFS="$lt_save_ifs"
+ ;;
+ esac
+else
+ enable_shared=yes
+fi
+
+
+
+
+
+
+
+
+
+ # Check whether --enable-static was given.
+if test "${enable_static+set}" = set; then :
+ enableval=$enable_static; p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_static=yes ;;
+ no) enable_static=no ;;
+ *)
+ enable_static=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ for pkg in $enableval; do
+ IFS="$lt_save_ifs"
+ if test "X$pkg" = "X$p"; then
+ enable_static=yes
+ fi
+ done
+ IFS="$lt_save_ifs"
+ ;;
+ esac
+else
+ enable_static=yes
+fi
+
+
+
+
+
+
+
+
+
+
+# Check whether --with-pic was given.
+if test "${with_pic+set}" = set; then :
+ withval=$with_pic; pic_mode="$withval"
+else
+ pic_mode=default
+fi
+
+
+test -z "$pic_mode" && pic_mode=default
+
+
+
+
+
+
+
+ # Check whether --enable-fast-install was given.
+if test "${enable_fast_install+set}" = set; then :
+ enableval=$enable_fast_install; p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_fast_install=yes ;;
+ no) enable_fast_install=no ;;
+ *)
+ enable_fast_install=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ for pkg in $enableval; do
+ IFS="$lt_save_ifs"
+ if test "X$pkg" = "X$p"; then
+ enable_fast_install=yes
+ fi
+ done
+ IFS="$lt_save_ifs"
+ ;;
+ esac
+else
+ enable_fast_install=yes
+fi
+
+
+
+
+
+
+
+
+
+
+
+# This can be used to rebuild libtool when needed
+LIBTOOL_DEPS="$ltmain"
+
+# Always use our own libtool.
+LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+test -z "$LN_S" && LN_S="ln -s"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+if test -n "${ZSH_VERSION+set}" ; then
+ setopt NO_GLOB_SUBST
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5
+$as_echo_n "checking for objdir... " >&6; }
+if ${lt_cv_objdir+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ rm -f .libs 2>/dev/null
+mkdir .libs 2>/dev/null
+if test -d .libs; then
+ lt_cv_objdir=.libs
+else
+ # MS-DOS does not allow filenames that begin with a dot.
+ lt_cv_objdir=_libs
+fi
+rmdir .libs 2>/dev/null
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5
+$as_echo "$lt_cv_objdir" >&6; }
+objdir=$lt_cv_objdir
+
+
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define LT_OBJDIR "$lt_cv_objdir/"
+_ACEOF
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+case $host_os in
+aix3*)
+ # AIX sometimes has problems with the GCC collect2 program. For some
+ # reason, if we set the COLLECT_NAMES environment variable, the problems
+ # vanish in a puff of smoke.
+ if test "X${COLLECT_NAMES+set}" != Xset; then
+ COLLECT_NAMES=
+ export COLLECT_NAMES
+ fi
+ ;;
+esac
+
+# Sed substitution that helps us do robust quoting. It backslashifies
+# metacharacters that are still active within double-quoted strings.
+sed_quote_subst='s/\(["`$\\]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\(["`\\]\)/\\\1/g'
+
+# Sed substitution to delay expansion of an escaped shell variable in a
+# double_quote_subst'ed string.
+delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
+
+# Sed substitution to delay expansion of an escaped single quote.
+delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g'
+
+# Sed substitution to avoid accidental globbing in evaled expressions
+no_glob_subst='s/\*/\\\*/g'
+
+# Global variables:
+ofile=libtool
+can_build_shared=yes
+
+# All known linkers require a `.a' archive for static linking (except MSVC,
+# which needs '.lib').
+libext=a
+
+with_gnu_ld="$lt_cv_prog_gnu_ld"
+
+old_CC="$CC"
+old_CFLAGS="$CFLAGS"
+
+# Set sane defaults for various variables
+test -z "$CC" && CC=cc
+test -z "$LTCC" && LTCC=$CC
+test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS
+test -z "$LD" && LD=ld
+test -z "$ac_objext" && ac_objext=o
+
+for cc_temp in $compiler""; do
+ case $cc_temp in
+ compile | *[\\/]compile | ccache | *[\\/]ccache ) ;;
+ distcc | *[\\/]distcc | purify | *[\\/]purify ) ;;
+ \-*) ;;
+ *) break;;
+ esac
+done
+cc_basename=`$ECHO "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"`
+
+
+# Only perform the check for file, if the check method requires it
+test -z "$MAGIC_CMD" && MAGIC_CMD=file
+case $deplibs_check_method in
+file_magic*)
+ if test "$file_magic_cmd" = '$MAGIC_CMD'; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5
+$as_echo_n "checking for ${ac_tool_prefix}file... " >&6; }
+if ${lt_cv_path_MAGIC_CMD+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $MAGIC_CMD in
+[\\/*] | ?:[\\/]*)
+ lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+ ;;
+*)
+ lt_save_MAGIC_CMD="$MAGIC_CMD"
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
+ for ac_dir in $ac_dummy; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/${ac_tool_prefix}file; then
+ lt_cv_path_MAGIC_CMD="$ac_dir/${ac_tool_prefix}file"
+ if test -n "$file_magic_test_file"; then
+ case $deplibs_check_method in
+ "file_magic "*)
+ file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+ MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+ if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+ $EGREP "$file_magic_regex" > /dev/null; then
+ :
+ else
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such. This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem. Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+ fi ;;
+ esac
+ fi
+ break
+ fi
+ done
+ IFS="$lt_save_ifs"
+ MAGIC_CMD="$lt_save_MAGIC_CMD"
+ ;;
+esac
+fi
+
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5
+$as_echo "$MAGIC_CMD" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+
+
+if test -z "$lt_cv_path_MAGIC_CMD"; then
+ if test -n "$ac_tool_prefix"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5
+$as_echo_n "checking for file... " >&6; }
+if ${lt_cv_path_MAGIC_CMD+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $MAGIC_CMD in
+[\\/*] | ?:[\\/]*)
+ lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+ ;;
+*)
+ lt_save_MAGIC_CMD="$MAGIC_CMD"
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
+ for ac_dir in $ac_dummy; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/file; then
+ lt_cv_path_MAGIC_CMD="$ac_dir/file"
+ if test -n "$file_magic_test_file"; then
+ case $deplibs_check_method in
+ "file_magic "*)
+ file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+ MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+ if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+ $EGREP "$file_magic_regex" > /dev/null; then
+ :
+ else
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such. This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem. Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+ fi ;;
+ esac
+ fi
+ break
+ fi
+ done
+ IFS="$lt_save_ifs"
+ MAGIC_CMD="$lt_save_MAGIC_CMD"
+ ;;
+esac
+fi
+
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5
+$as_echo "$MAGIC_CMD" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ else
+ MAGIC_CMD=:
+ fi
+fi
+
+ fi
+ ;;
+esac
+
+# Use C for the default configuration in the libtool script
+
+lt_save_CC="$CC"
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+# Source file extension for C test sources.
+ac_ext=c
+
+# Object file extension for compiled C test sources.
+objext=o
+objext=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="int some_variable = 0;"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='int main(){return(0);}'
+
+
+
+
+
+
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+# Save the default compiler, since it gets overwritten when the other
+# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP.
+compiler_DEFAULT=$CC
+
+# save warnings/boilerplate of simple test code
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_compile_test_code" >conftest.$ac_ext
+eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_compiler_boilerplate=`cat conftest.err`
+$RM conftest*
+
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_link_test_code" >conftest.$ac_ext
+eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_linker_boilerplate=`cat conftest.err`
+$RM -r conftest*
+
+
+## CAVEAT EMPTOR:
+## There is no encapsulation within the following macros, do not change
+## the running order or otherwise move them around unless you know exactly
+## what you are doing...
+if test -n "$compiler"; then
+
+lt_prog_compiler_no_builtin_flag=
+
+if test "$GCC" = yes; then
+ lt_prog_compiler_no_builtin_flag=' -fno-builtin'
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5
+$as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; }
+if ${lt_cv_prog_compiler_rtti_exceptions+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_rtti_exceptions=no
+ ac_outfile=conftest.$ac_objext
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+ lt_compiler_flag="-fno-rtti -fno-exceptions"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ # The option is referenced via a variable to avoid confusing sed.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:8290: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>conftest.err)
+ ac_status=$?
+ cat conftest.err >&5
+ echo "$as_me:8294: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s "$ac_outfile"; then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings other than the usual output.
+ $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_rtti_exceptions=yes
+ fi
+ fi
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5
+$as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; }
+
+if test x"$lt_cv_prog_compiler_rtti_exceptions" = xyes; then
+ lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions"
+else
+ :
+fi
+
+fi
+
+
+
+
+
+
+ lt_prog_compiler_wl=
+lt_prog_compiler_pic=
+lt_prog_compiler_static=
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5
+$as_echo_n "checking for $compiler option to produce PIC... " >&6; }
+
+ if test "$GCC" = yes; then
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_static='-static'
+
+ case $host_os in
+ aix*)
+ # All AIX code is PIC.
+ if test "$host_cpu" = ia64; then
+ # AIX 5 now supports IA64 processor
+ lt_prog_compiler_static='-Bstatic'
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ lt_prog_compiler_pic='-fPIC'
+ ;;
+ m68k)
+ # FIXME: we need at least 68020 code to build shared libraries, but
+ # adding the `-m68020' flag to GCC prevents building anything better,
+ # like `-m68040'.
+ lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4'
+ ;;
+ esac
+ ;;
+
+ beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+ # PIC is the default for these OSes.
+ ;;
+
+ mingw* | cygwin* | pw32* | os2* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ # Although the cygwin gcc ignores -fPIC, still need this for old-style
+ # (--disable-auto-import) libraries
+ lt_prog_compiler_pic='-DDLL_EXPORT'
+ ;;
+
+ darwin* | rhapsody*)
+ # PIC is the default on this platform
+ # Common symbols not allowed in MH_DYLIB files
+ lt_prog_compiler_pic='-fno-common'
+ ;;
+
+ hpux*)
+ # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+ # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag
+ # sets the default TLS model and affects inlining.
+ case $host_cpu in
+ hppa*64*)
+ # +Z the default
+ ;;
+ *)
+ lt_prog_compiler_pic='-fPIC'
+ ;;
+ esac
+ ;;
+
+ interix[3-9]*)
+ # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+ # Instead, we relocate shared libraries at runtime.
+ ;;
+
+ msdosdjgpp*)
+ # Just because we use GCC doesn't mean we suddenly get shared libraries
+ # on systems that don't support them.
+ lt_prog_compiler_can_build_shared=no
+ enable_shared=no
+ ;;
+
+ *nto* | *qnx*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ lt_prog_compiler_pic='-fPIC -shared'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ lt_prog_compiler_pic=-Kconform_pic
+ fi
+ ;;
+
+ *)
+ lt_prog_compiler_pic='-fPIC'
+ ;;
+ esac
+ else
+ # PORTME Check for flag to pass linker flags through the system compiler.
+ case $host_os in
+ aix*)
+ lt_prog_compiler_wl='-Wl,'
+ if test "$host_cpu" = ia64; then
+ # AIX 5 now supports IA64 processor
+ lt_prog_compiler_static='-Bstatic'
+ else
+ lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp'
+ fi
+ ;;
+
+ mingw* | cygwin* | pw32* | os2* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ lt_prog_compiler_pic='-DDLL_EXPORT'
+ ;;
+
+ hpux9* | hpux10* | hpux11*)
+ lt_prog_compiler_wl='-Wl,'
+ # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+ # not for PA HP-UX.
+ case $host_cpu in
+ hppa*64*|ia64*)
+ # +Z the default
+ ;;
+ *)
+ lt_prog_compiler_pic='+Z'
+ ;;
+ esac
+ # Is there a better lt_prog_compiler_static that works with the bundled CC?
+ lt_prog_compiler_static='${wl}-a ${wl}archive'
+ ;;
+
+ irix5* | irix6* | nonstopux*)
+ lt_prog_compiler_wl='-Wl,'
+ # PIC (with -KPIC) is the default.
+ lt_prog_compiler_static='-non_shared'
+ ;;
+
+ linux* | k*bsd*-gnu)
+ case $cc_basename in
+ # old Intel for x86_64 which still supported -KPIC.
+ ecc*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-static'
+ ;;
+ # icc used to be incompatible with GCC.
+ # ICC 10 doesn't accept -KPIC any more.
+ icc* | ifort*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-fPIC'
+ lt_prog_compiler_static='-static'
+ ;;
+ # Lahey Fortran 8.1.
+ lf95*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='--shared'
+ lt_prog_compiler_static='--static'
+ ;;
+ pgcc* | pgf77* | pgf90* | pgf95*)
+ # Portland Group compilers (*not* the Pentium gcc compiler,
+ # which looks to be a dead project)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-fpic'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+ ccc*)
+ lt_prog_compiler_wl='-Wl,'
+ # All Alpha code is PIC.
+ lt_prog_compiler_static='-non_shared'
+ ;;
+ xl*)
+ # IBM XL C 8.0/Fortran 10.1 on PPC
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-qpic'
+ lt_prog_compiler_static='-qstaticlink'
+ ;;
+ *)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*)
+ # Sun C 5.9
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ lt_prog_compiler_wl='-Wl,'
+ ;;
+ *Sun\ F*)
+ # Sun Fortran 8.3 passes all unrecognized flags to the linker
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ lt_prog_compiler_wl=''
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+
+ newsos6)
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ *nto* | *qnx*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ lt_prog_compiler_pic='-fPIC -shared'
+ ;;
+
+ osf3* | osf4* | osf5*)
+ lt_prog_compiler_wl='-Wl,'
+ # All OSF/1 code is PIC.
+ lt_prog_compiler_static='-non_shared'
+ ;;
+
+ rdos*)
+ lt_prog_compiler_static='-non_shared'
+ ;;
+
+ solaris*)
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ case $cc_basename in
+ f77* | f90* | f95*)
+ lt_prog_compiler_wl='-Qoption ld ';;
+ *)
+ lt_prog_compiler_wl='-Wl,';;
+ esac
+ ;;
+
+ sunos4*)
+ lt_prog_compiler_wl='-Qoption ld '
+ lt_prog_compiler_pic='-PIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ sysv4 | sysv4.2uw2* | sysv4.3*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec ;then
+ lt_prog_compiler_pic='-Kconform_pic'
+ lt_prog_compiler_static='-Bstatic'
+ fi
+ ;;
+
+ sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ unicos*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_can_build_shared=no
+ ;;
+
+ uts4*)
+ lt_prog_compiler_pic='-pic'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ *)
+ lt_prog_compiler_can_build_shared=no
+ ;;
+ esac
+ fi
+
+case $host_os in
+ # For platforms which do not support PIC, -DPIC is meaningless:
+ *djgpp*)
+ lt_prog_compiler_pic=
+ ;;
+ *)
+ lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC"
+ ;;
+esac
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_prog_compiler_pic" >&5
+$as_echo "$lt_prog_compiler_pic" >&6; }
+
+
+
+
+
+
+#
+# Check to make sure the PIC flag actually works.
+#
+if test -n "$lt_prog_compiler_pic"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5
+$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; }
+if ${lt_cv_prog_compiler_pic_works+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_pic_works=no
+ ac_outfile=conftest.$ac_objext
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+ lt_compiler_flag="$lt_prog_compiler_pic -DPIC"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ # The option is referenced via a variable to avoid confusing sed.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:8629: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>conftest.err)
+ ac_status=$?
+ cat conftest.err >&5
+ echo "$as_me:8633: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s "$ac_outfile"; then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings other than the usual output.
+ $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_pic_works=yes
+ fi
+ fi
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5
+$as_echo "$lt_cv_prog_compiler_pic_works" >&6; }
+
+if test x"$lt_cv_prog_compiler_pic_works" = xyes; then
+ case $lt_prog_compiler_pic in
+ "" | " "*) ;;
+ *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;;
+ esac
+else
+ lt_prog_compiler_pic=
+ lt_prog_compiler_can_build_shared=no
+fi
+
+fi
+
+
+
+
+
+
+#
+# Check to make sure the static flag actually works.
+#
+wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5
+$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; }
+if ${lt_cv_prog_compiler_static_works+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_static_works=no
+ save_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS $lt_tmp_static_flag"
+ echo "$lt_simple_link_test_code" > conftest.$ac_ext
+ if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+ # The linker can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ if test -s conftest.err; then
+ # Append any errors to the config.log.
+ cat conftest.err 1>&5
+ $ECHO "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if diff conftest.exp conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_static_works=yes
+ fi
+ else
+ lt_cv_prog_compiler_static_works=yes
+ fi
+ fi
+ $RM -r conftest*
+ LDFLAGS="$save_LDFLAGS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5
+$as_echo "$lt_cv_prog_compiler_static_works" >&6; }
+
+if test x"$lt_cv_prog_compiler_static_works" = xyes; then
+ :
+else
+ lt_prog_compiler_static=
+fi
+
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5
+$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
+if ${lt_cv_prog_compiler_c_o+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_c_o=no
+ $RM -r conftest 2>/dev/null
+ mkdir conftest
+ cd conftest
+ mkdir out
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ lt_compiler_flag="-o out/conftest2.$ac_objext"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:8734: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>out/conftest.err)
+ ac_status=$?
+ cat out/conftest.err >&5
+ echo "$as_me:8738: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s out/conftest2.$ac_objext
+ then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp
+ $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+ if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_c_o=yes
+ fi
+ fi
+ chmod u+w . 2>&5
+ $RM conftest*
+ # SGI C++ compiler will create directory out/ii_files/ for
+ # template instantiation
+ test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+ $RM out/* && rmdir out
+ cd ..
+ $RM -r conftest
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5
+$as_echo "$lt_cv_prog_compiler_c_o" >&6; }
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5
+$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
+if ${lt_cv_prog_compiler_c_o+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_c_o=no
+ $RM -r conftest 2>/dev/null
+ mkdir conftest
+ cd conftest
+ mkdir out
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ lt_compiler_flag="-o out/conftest2.$ac_objext"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:8789: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>out/conftest.err)
+ ac_status=$?
+ cat out/conftest.err >&5
+ echo "$as_me:8793: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s out/conftest2.$ac_objext
+ then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp
+ $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+ if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_c_o=yes
+ fi
+ fi
+ chmod u+w . 2>&5
+ $RM conftest*
+ # SGI C++ compiler will create directory out/ii_files/ for
+ # template instantiation
+ test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+ $RM out/* && rmdir out
+ cd ..
+ $RM -r conftest
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5
+$as_echo "$lt_cv_prog_compiler_c_o" >&6; }
+
+
+
+
+hard_links="nottested"
+if test "$lt_cv_prog_compiler_c_o" = no && test "$need_locks" != no; then
+ # do not overwrite the value of need_locks provided by the user
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5
+$as_echo_n "checking if we can lock with hard links... " >&6; }
+ hard_links=yes
+ $RM conftest*
+ ln conftest.a conftest.b 2>/dev/null && hard_links=no
+ touch conftest.a
+ ln conftest.a conftest.b 2>&5 || hard_links=no
+ ln conftest.a conftest.b 2>/dev/null && hard_links=no
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5
+$as_echo "$hard_links" >&6; }
+ if test "$hard_links" = no; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5
+$as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;}
+ need_locks=warn
+ fi
+else
+ need_locks=no
+fi
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5
+$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; }
+
+ runpath_var=
+ allow_undefined_flag=
+ always_export_symbols=no
+ archive_cmds=
+ archive_expsym_cmds=
+ compiler_needs_object=no
+ enable_shared_with_static_runtimes=no
+ export_dynamic_flag_spec=
+ export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+ hardcode_automatic=no
+ hardcode_direct=no
+ hardcode_direct_absolute=no
+ hardcode_libdir_flag_spec=
+ hardcode_libdir_flag_spec_ld=
+ hardcode_libdir_separator=
+ hardcode_minus_L=no
+ hardcode_shlibpath_var=unsupported
+ inherit_rpath=no
+ link_all_deplibs=unknown
+ module_cmds=
+ module_expsym_cmds=
+ old_archive_from_new_cmds=
+ old_archive_from_expsyms_cmds=
+ thread_safe_flag_spec=
+ whole_archive_flag_spec=
+ # include_expsyms should be a list of space-separated symbols to be *always*
+ # included in the symbol list
+ include_expsyms=
+ # exclude_expsyms can be an extended regexp of symbols to exclude
+ # it will be wrapped by ` (' and `)$', so one must not match beginning or
+ # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc',
+ # as well as any symbol that contains `d'.
+ exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'
+ # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
+ # platforms (ab)use it in PIC code, but their linkers get confused if
+ # the symbol is explicitly referenced. Since portable code cannot
+ # rely on this symbol name, it's probably fine to never include it in
+ # preloaded symbol tables.
+ # Exclude shared library initialization/finalization symbols.
+ extract_expsyms_cmds=
+
+ case $host_os in
+ cygwin* | mingw* | pw32* | cegcc*)
+ # FIXME: the MSVC++ port hasn't been tested in a loooong time
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ if test "$GCC" != yes; then
+ with_gnu_ld=no
+ fi
+ ;;
+ interix*)
+ # we just hope/assume this is gcc and not c89 (= MSVC++)
+ with_gnu_ld=yes
+ ;;
+ openbsd*)
+ with_gnu_ld=no
+ ;;
+ esac
+
+ ld_shlibs=yes
+ if test "$with_gnu_ld" = yes; then
+ # If archive_cmds runs LD, not CC, wlarc should be empty
+ wlarc='${wl}'
+
+ # Set some defaults for GNU ld with shared library support. These
+ # are reset later if shared libraries are not supported. Putting them
+ # here allows them to be overridden if necessary.
+ runpath_var=LD_RUN_PATH
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ export_dynamic_flag_spec='${wl}--export-dynamic'
+ # ancient GNU ld didn't support --whole-archive et. al.
+ if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then
+ whole_archive_flag_spec="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+ else
+ whole_archive_flag_spec=
+ fi
+ supports_anon_versioning=no
+ case `$LD -v 2>&1` in
+ *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11
+ *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
+ *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
+ *\ 2.11.*) ;; # other 2.11 versions
+ *) supports_anon_versioning=yes ;;
+ esac
+
+ # See if GNU ld supports shared libraries.
+ case $host_os in
+ aix[3-9]*)
+ # On AIX/PPC, the GNU linker is very broken
+ if test "$host_cpu" != ia64; then
+ ld_shlibs=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the GNU linker, at least up to release 2.9.1, is reported
+*** to be unable to reliably create shared libraries on AIX.
+*** Therefore, libtool is disabling shared libraries support. If you
+*** really care for shared libraries, you may want to modify your PATH
+*** so that a non-GNU linker is found, and then restart.
+
+_LT_EOF
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds=''
+ ;;
+ m68k)
+ archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ ;;
+ esac
+ ;;
+
+ beos*)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ allow_undefined_flag=unsupported
+ # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+ # support --undefined. This deserves some investigation. FIXME
+ archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ cygwin* | mingw* | pw32* | cegcc*)
+ # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless,
+ # as there is no search path for DLLs.
+ hardcode_libdir_flag_spec='-L$libdir'
+ allow_undefined_flag=unsupported
+ always_export_symbols=no
+ enable_shared_with_static_runtimes=yes
+ export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols'
+
+ if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ # If the export-symbols file already is a .def file (1st line
+ # is EXPORTS), use it as is; otherwise, prepend...
+ archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+ cp $export_symbols $output_objdir/$soname.def;
+ else
+ echo EXPORTS > $output_objdir/$soname.def;
+ cat $export_symbols >> $output_objdir/$soname.def;
+ fi~
+ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ interix[3-9]*)
+ hardcode_direct=no
+ hardcode_shlibpath_var=no
+ hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+ export_dynamic_flag_spec='${wl}-E'
+ # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+ # Instead, shared libraries are loaded at an image base (0x10000000 by
+ # default) and relocated if they conflict, which is a slow very memory
+ # consuming and fragmenting process. To avoid this, we pick a random,
+ # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+ # time. Moving up from 0x10000000 also allows more sbrk(2) space.
+ archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ archive_expsym_cmds='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ ;;
+
+ gnu* | linux* | tpf* | k*bsd*-gnu)
+ tmp_diet=no
+ if test "$host_os" = linux-dietlibc; then
+ case $cc_basename in
+ diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn)
+ esac
+ fi
+ if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \
+ && test "$tmp_diet" = no
+ then
+ tmp_addflag=
+ tmp_sharedflag='-shared'
+ case $cc_basename,$host_cpu in
+ pgcc*) # Portland Group C compiler
+ whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+ tmp_addflag=' $pic_flag'
+ ;;
+ pgf77* | pgf90* | pgf95*) # Portland Group f77 and f90 compilers
+ whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+ tmp_addflag=' $pic_flag -Mnomain' ;;
+ ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64
+ tmp_addflag=' -i_dynamic' ;;
+ efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64
+ tmp_addflag=' -i_dynamic -nofor_main' ;;
+ ifc* | ifort*) # Intel Fortran compiler
+ tmp_addflag=' -nofor_main' ;;
+ lf95*) # Lahey Fortran 8.1
+ whole_archive_flag_spec=
+ tmp_sharedflag='--shared' ;;
+ xl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below)
+ tmp_sharedflag='-qmkshrobj'
+ tmp_addflag= ;;
+ esac
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*) # Sun C 5.9
+ whole_archive_flag_spec='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+ compiler_needs_object=yes
+ tmp_sharedflag='-G' ;;
+ *Sun\ F*) # Sun Fortran 8.3
+ tmp_sharedflag='-G' ;;
+ esac
+ archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+
+ if test "x$supports_anon_versioning" = xyes; then
+ archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+ fi
+
+ case $cc_basename in
+ xlf*)
+ # IBM XL Fortran 10.1 on PPC cannot create shared libs itself
+ whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive'
+ hardcode_libdir_flag_spec=
+ hardcode_libdir_flag_spec_ld='-rpath $libdir'
+ archive_cmds='$LD -shared $libobjs $deplibs $compiler_flags -soname $soname -o $lib'
+ if test "x$supports_anon_versioning" = xyes; then
+ archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $LD -shared $libobjs $deplibs $compiler_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib'
+ fi
+ ;;
+ esac
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
+ wlarc=
+ else
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ fi
+ ;;
+
+ solaris*)
+ if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then
+ ld_shlibs=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: The releases 2.8.* of the GNU linker cannot reliably
+*** create shared libraries on Solaris systems. Therefore, libtool
+*** is disabling shared libraries support. We urge you to upgrade GNU
+*** binutils to release 2.9.1 or newer. Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+ elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*)
+ case `$LD -v 2>&1` in
+ *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*)
+ ld_shlibs=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not
+*** reliably create shared libraries on SCO systems. Therefore, libtool
+*** is disabling shared libraries support. We urge you to upgrade GNU
+*** binutils to release 2.16.91.0.3 or newer. Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+ ;;
+ *)
+ # For security reasons, it is highly recommended that you always
+ # use absolute paths for naming shared libraries, and exclude the
+ # DT_RUNPATH tag from executables and libraries. But doing so
+ # requires that you compile everything twice, which is a pain.
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+ esac
+ ;;
+
+ sunos4*)
+ archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ wlarc=
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ *)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+ esac
+
+ if test "$ld_shlibs" = no; then
+ runpath_var=
+ hardcode_libdir_flag_spec=
+ export_dynamic_flag_spec=
+ whole_archive_flag_spec=
+ fi
+ else
+ # PORTME fill in a description of your system's linker (not GNU ld)
+ case $host_os in
+ aix3*)
+ allow_undefined_flag=unsupported
+ always_export_symbols=yes
+ archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'
+ # Note: this linker hardcodes the directories in LIBPATH if there
+ # are no directories specified by -L.
+ hardcode_minus_L=yes
+ if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then
+ # Neither direct hardcoding nor static linking is supported with a
+ # broken collect2.
+ hardcode_direct=unsupported
+ fi
+ ;;
+
+ aix[4-9]*)
+ if test "$host_cpu" = ia64; then
+ # On IA64, the linker does run time linking by default, so we don't
+ # have to do anything special.
+ aix_use_runtimelinking=no
+ exp_sym_flag='-Bexport'
+ no_entry_flag=""
+ else
+ # If we're using GNU nm, then we don't want the "-C" option.
+ # -C means demangle to AIX nm, but means don't demangle with GNU nm
+ if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+ export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+ else
+ export_symbols_cmds='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+ fi
+ aix_use_runtimelinking=no
+
+ # Test if we are trying to use run time linking or normal
+ # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+ # need to do runtime linking.
+ case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*)
+ for ld_flag in $LDFLAGS; do
+ if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then
+ aix_use_runtimelinking=yes
+ break
+ fi
+ done
+ ;;
+ esac
+
+ exp_sym_flag='-bexport'
+ no_entry_flag='-bnoentry'
+ fi
+
+ # When large executables or shared objects are built, AIX ld can
+ # have problems creating the table of contents. If linking a library
+ # or program results in "error TOC overflow" add -mminimal-toc to
+ # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not
+ # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+ archive_cmds=''
+ hardcode_direct=yes
+ hardcode_direct_absolute=yes
+ hardcode_libdir_separator=':'
+ link_all_deplibs=yes
+ file_list_spec='${wl}-f,'
+
+ if test "$GCC" = yes; then
+ case $host_os in aix4.[012]|aix4.[012].*)
+ # We only want to do this on AIX 4.2 and lower, the check
+ # below for broken collect2 doesn't work under 4.3+
+ collect2name=`${CC} -print-prog-name=collect2`
+ if test -f "$collect2name" &&
+ strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+ then
+ # We have reworked collect2
+ :
+ else
+ # We have old collect2
+ hardcode_direct=unsupported
+ # It fails to find uninstalled libraries when the uninstalled
+ # path is not listed in the libpath. Setting hardcode_minus_L
+ # to unsupported forces relinking
+ hardcode_minus_L=yes
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_libdir_separator=
+ fi
+ ;;
+ esac
+ shared_flag='-shared'
+ if test "$aix_use_runtimelinking" = yes; then
+ shared_flag="$shared_flag "'${wl}-G'
+ fi
+ else
+ # not using gcc
+ if test "$host_cpu" = ia64; then
+ # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+ # chokes on -Wl,-G. The following line is correct:
+ shared_flag='-G'
+ else
+ if test "$aix_use_runtimelinking" = yes; then
+ shared_flag='${wl}-G'
+ else
+ shared_flag='${wl}-bM:SRE'
+ fi
+ fi
+ fi
+
+ export_dynamic_flag_spec='${wl}-bexpall'
+ # It seems that -bexpall does not export symbols beginning with
+ # underscore (_), so it is better to generate a list of symbols to export.
+ always_export_symbols=yes
+ if test "$aix_use_runtimelinking" = yes; then
+ # Warning - without using the other runtime loading flags (-brtl),
+ # -berok will link without error, but may produce a broken library.
+ allow_undefined_flag='-berok'
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+lt_aix_libpath_sed='
+ /Import File Strings/,/^$/ {
+ /^0/ {
+ s/^0 *\(.*\)$/\1/
+ p
+ }
+ }'
+aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+# Check for a 64-bit object if we didn't find anything.
+if test -z "$aix_libpath"; then
+ aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+fi
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+
+ hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath"
+ archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then $ECHO "X${wl}${allow_undefined_flag}" | $Xsed; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+ else
+ if test "$host_cpu" = ia64; then
+ hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib'
+ allow_undefined_flag="-z nodefs"
+ archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols"
+ else
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+lt_aix_libpath_sed='
+ /Import File Strings/,/^$/ {
+ /^0/ {
+ s/^0 *\(.*\)$/\1/
+ p
+ }
+ }'
+aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+# Check for a 64-bit object if we didn't find anything.
+if test -z "$aix_libpath"; then
+ aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+fi
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+
+ hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath"
+ # Warning - without using the other run time loading flags,
+ # -berok will link without error, but may produce a broken library.
+ no_undefined_flag=' ${wl}-bernotok'
+ allow_undefined_flag=' ${wl}-berok'
+ # Exported symbols can be pulled into shared objects from archives
+ whole_archive_flag_spec='$convenience'
+ archive_cmds_need_lc=yes
+ # This is similar to how AIX traditionally builds its shared libraries.
+ archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+ fi
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_expsym_cmds=''
+ ;;
+ m68k)
+ archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ ;;
+ esac
+ ;;
+
+ bsdi[45]*)
+ export_dynamic_flag_spec=-rdynamic
+ ;;
+
+ cygwin* | mingw* | pw32* | cegcc*)
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ # hardcode_libdir_flag_spec is actually meaningless, as there is
+ # no search path for DLLs.
+ hardcode_libdir_flag_spec=' '
+ allow_undefined_flag=unsupported
+ # Tell ltmain to make .lib files, not .a files.
+ libext=lib
+ # Tell ltmain to make .dll files, not .so files.
+ shrext_cmds=".dll"
+ # FIXME: Setting linknames here is a bad hack.
+ archive_cmds='$CC -o $lib $libobjs $compiler_flags `$ECHO "X$deplibs" | $Xsed -e '\''s/ -lc$//'\''` -link -dll~linknames='
+ # The linker will automatically build a .lib file if we build a DLL.
+ old_archive_from_new_cmds='true'
+ # FIXME: Should let the user specify the lib program.
+ old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs'
+ fix_srcfile_path='`cygpath -w "$srcfile"`'
+ enable_shared_with_static_runtimes=yes
+ ;;
+
+ darwin* | rhapsody*)
+
+
+ archive_cmds_need_lc=no
+ hardcode_direct=no
+ hardcode_automatic=yes
+ hardcode_shlibpath_var=unsupported
+ whole_archive_flag_spec=''
+ link_all_deplibs=yes
+ allow_undefined_flag="$_lt_dar_allow_undefined"
+ case $cc_basename in
+ ifort*) _lt_dar_can_shared=yes ;;
+ *) _lt_dar_can_shared=$GCC ;;
+ esac
+ if test "$_lt_dar_can_shared" = "yes"; then
+ output_verbose_link_cmd=echo
+ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}"
+ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}"
+ archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}"
+ module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}"
+
+ else
+ ld_shlibs=no
+ fi
+
+ ;;
+
+ dgux*)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_shlibpath_var=no
+ ;;
+
+ freebsd1*)
+ ld_shlibs=no
+ ;;
+
+ # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
+ # support. Future versions do this automatically, but an explicit c++rt0.o
+ # does not break anything, and helps significantly (at the cost of a little
+ # extra space).
+ freebsd2.2*)
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ # Unfortunately, older versions of FreeBSD 2 do not have this feature.
+ freebsd2*)
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=yes
+ hardcode_minus_L=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
+ freebsd* | dragonfly*)
+ archive_cmds='$CC -shared -o $lib $libobjs $deplibs $compiler_flags'
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ hpux9*)
+ if test "$GCC" = yes; then
+ archive_cmds='$RM $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+ else
+ archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+ fi
+ hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+ hardcode_libdir_separator=:
+ hardcode_direct=yes
+
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ hardcode_minus_L=yes
+ export_dynamic_flag_spec='${wl}-E'
+ ;;
+
+ hpux10*)
+ if test "$GCC" = yes -a "$with_gnu_ld" = no; then
+ archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+ fi
+ if test "$with_gnu_ld" = no; then
+ hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+ hardcode_libdir_flag_spec_ld='+b $libdir'
+ hardcode_libdir_separator=:
+ hardcode_direct=yes
+ hardcode_direct_absolute=yes
+ export_dynamic_flag_spec='${wl}-E'
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ hardcode_minus_L=yes
+ fi
+ ;;
+
+ hpux11*)
+ if test "$GCC" = yes -a "$with_gnu_ld" = no; then
+ case $host_cpu in
+ hppa*64*)
+ archive_cmds='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ ia64*)
+ archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ else
+ case $host_cpu in
+ hppa*64*)
+ archive_cmds='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ ia64*)
+ archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ fi
+ if test "$with_gnu_ld" = no; then
+ hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+ hardcode_libdir_separator=:
+
+ case $host_cpu in
+ hppa*64*|ia64*)
+ hardcode_direct=no
+ hardcode_shlibpath_var=no
+ ;;
+ *)
+ hardcode_direct=yes
+ hardcode_direct_absolute=yes
+ export_dynamic_flag_spec='${wl}-E'
+
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ hardcode_minus_L=yes
+ ;;
+ esac
+ fi
+ ;;
+
+ irix5* | irix6* | nonstopux*)
+ if test "$GCC" = yes; then
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ # Try to use the -exported_symbol ld option, if it does not
+ # work, assume that -exports_file does not work either and
+ # implicitly export all symbols.
+ save_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+int foo (void) { return 0; }
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib'
+
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LDFLAGS="$save_LDFLAGS"
+ else
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib'
+ fi
+ archive_cmds_need_lc='no'
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ hardcode_libdir_separator=:
+ inherit_rpath=yes
+ link_all_deplibs=yes
+ ;;
+
+ netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out
+ else
+ archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF
+ fi
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ newsos6)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=yes
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ hardcode_libdir_separator=:
+ hardcode_shlibpath_var=no
+ ;;
+
+ *nto* | *qnx*)
+ ;;
+
+ openbsd*)
+ if test -f /usr/libexec/ld.so; then
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ hardcode_direct_absolute=yes
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+ archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols'
+ hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+ export_dynamic_flag_spec='${wl}-E'
+ else
+ case $host_os in
+ openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*)
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_libdir_flag_spec='-R$libdir'
+ ;;
+ *)
+ archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+ ;;
+ esac
+ fi
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ os2*)
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ allow_undefined_flag=unsupported
+ archive_cmds='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$ECHO DATA >> $output_objdir/$libname.def~$ECHO " SINGLE NONSHARED" >> $output_objdir/$libname.def~$ECHO EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def'
+ old_archive_from_new_cmds='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def'
+ ;;
+
+ osf3*)
+ if test "$GCC" = yes; then
+ allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*'
+ archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ else
+ allow_undefined_flag=' -expect_unresolved \*'
+ archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+ fi
+ archive_cmds_need_lc='no'
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ hardcode_libdir_separator=:
+ ;;
+
+ osf4* | osf5*) # as osf3* with the addition of -msym flag
+ if test "$GCC" = yes; then
+ allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*'
+ archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ else
+ allow_undefined_flag=' -expect_unresolved \*'
+ archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+ archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~
+ $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp'
+
+ # Both c and cxx compiler support -rpath directly
+ hardcode_libdir_flag_spec='-rpath $libdir'
+ fi
+ archive_cmds_need_lc='no'
+ hardcode_libdir_separator=:
+ ;;
+
+ solaris*)
+ no_undefined_flag=' -z defs'
+ if test "$GCC" = yes; then
+ wlarc='${wl}'
+ archive_cmds='$CC -shared ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -shared ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ else
+ case `$CC -V 2>&1` in
+ *"Compilers 5.0"*)
+ wlarc=''
+ archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp'
+ ;;
+ *)
+ wlarc='${wl}'
+ archive_cmds='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ ;;
+ esac
+ fi
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_shlibpath_var=no
+ case $host_os in
+ solaris2.[0-5] | solaris2.[0-5].*) ;;
+ *)
+ # The compiler driver will combine and reorder linker options,
+ # but understands `-z linker_flag'. GCC discards it without `$wl',
+ # but is careful enough not to reorder.
+ # Supported since Solaris 2.6 (maybe 2.5.1?)
+ if test "$GCC" = yes; then
+ whole_archive_flag_spec='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
+ else
+ whole_archive_flag_spec='-z allextract$convenience -z defaultextract'
+ fi
+ ;;
+ esac
+ link_all_deplibs=yes
+ ;;
+
+ sunos4*)
+ if test "x$host_vendor" = xsequent; then
+ # Use $CC to link under sequent, because it throws in some extra .o
+ # files that make .init and .fini sections work.
+ archive_cmds='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
+ fi
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_direct=yes
+ hardcode_minus_L=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ sysv4)
+ case $host_vendor in
+ sni)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=yes # is this really true???
+ ;;
+ siemens)
+ ## LD is ld it makes a PLAMLIB
+ ## CC just makes a GrossModule.
+ archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+ reload_cmds='$CC -r -o $output$reload_objs'
+ hardcode_direct=no
+ ;;
+ motorola)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=no #Motorola manual says yes, but my tests say they lie
+ ;;
+ esac
+ runpath_var='LD_RUN_PATH'
+ hardcode_shlibpath_var=no
+ ;;
+
+ sysv4.3*)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_shlibpath_var=no
+ export_dynamic_flag_spec='-Bexport'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_shlibpath_var=no
+ runpath_var=LD_RUN_PATH
+ hardcode_runpath_var=yes
+ ld_shlibs=yes
+ fi
+ ;;
+
+ sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*)
+ no_undefined_flag='${wl}-z,text'
+ archive_cmds_need_lc=no
+ hardcode_shlibpath_var=no
+ runpath_var='LD_RUN_PATH'
+
+ if test "$GCC" = yes; then
+ archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ fi
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6*)
+ # Note: We can NOT use -z defs as we might desire, because we do not
+ # link with -lc, and that would cause any symbols used from libc to
+ # always be unresolved, which means just about no library would
+ # ever link correctly. If we're not using GNU ld we use -z text
+ # though, which does catch some bad symbols but isn't as heavy-handed
+ # as -z defs.
+ no_undefined_flag='${wl}-z,text'
+ allow_undefined_flag='${wl}-z,nodefs'
+ archive_cmds_need_lc=no
+ hardcode_shlibpath_var=no
+ hardcode_libdir_flag_spec='${wl}-R,$libdir'
+ hardcode_libdir_separator=':'
+ link_all_deplibs=yes
+ export_dynamic_flag_spec='${wl}-Bexport'
+ runpath_var='LD_RUN_PATH'
+
+ if test "$GCC" = yes; then
+ archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ fi
+ ;;
+
+ uts4*)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_shlibpath_var=no
+ ;;
+
+ *)
+ ld_shlibs=no
+ ;;
+ esac
+
+ if test x$host_vendor = xsni; then
+ case $host in
+ sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+ export_dynamic_flag_spec='${wl}-Blargedynsym'
+ ;;
+ esac
+ fi
+ fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5
+$as_echo "$ld_shlibs" >&6; }
+test "$ld_shlibs" = no && can_build_shared=no
+
+with_gnu_ld=$with_gnu_ld
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#
+# Do we need to explicitly link libc?
+#
+case "x$archive_cmds_need_lc" in
+x|xyes)
+ # Assume -lc should be added
+ archive_cmds_need_lc=yes
+
+ if test "$enable_shared" = yes && test "$GCC" = yes; then
+ case $archive_cmds in
+ *'~'*)
+ # FIXME: we may have to deal with multi-command sequences.
+ ;;
+ '$CC '*)
+ # Test whether the compiler implicitly links with -lc since on some
+ # systems, -lgcc has to come before -lc. If gcc already passes -lc
+ # to ld, don't add -lc before -lgcc.
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5
+$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; }
+ $RM conftest*
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } 2>conftest.err; then
+ soname=conftest
+ lib=conftest
+ libobjs=conftest.$ac_objext
+ deplibs=
+ wl=$lt_prog_compiler_wl
+ pic_flag=$lt_prog_compiler_pic
+ compiler_flags=-v
+ linker_flags=-v
+ verstring=
+ output_objdir=.
+ libname=conftest
+ lt_save_allow_undefined_flag=$allow_undefined_flag
+ allow_undefined_flag=
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5
+ (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+ then
+ archive_cmds_need_lc=no
+ else
+ archive_cmds_need_lc=yes
+ fi
+ allow_undefined_flag=$lt_save_allow_undefined_flag
+ else
+ cat conftest.err 1>&5
+ fi
+ $RM conftest*
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $archive_cmds_need_lc" >&5
+$as_echo "$archive_cmds_need_lc" >&6; }
+ ;;
+ esac
+ fi
+ ;;
+esac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5
+$as_echo_n "checking dynamic linker characteristics... " >&6; }
+
+if test "$GCC" = yes; then
+ case $host_os in
+ darwin*) lt_awk_arg="/^libraries:/,/LR/" ;;
+ *) lt_awk_arg="/^libraries:/" ;;
+ esac
+ lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e "s,=/,/,g"`
+ if $ECHO "$lt_search_path_spec" | $GREP ';' >/dev/null ; then
+ # if the path contains ";" then we assume it to be the separator
+ # otherwise default to the standard path separator (i.e. ":") - it is
+ # assumed that no part of a normal pathname contains ";" but that should
+ # okay in the real world where ";" in dirpaths is itself problematic.
+ lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e 's/;/ /g'`
+ else
+ lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+ fi
+ # Ok, now we have the path, separated by spaces, we can step through it
+ # and add multilib dir if necessary.
+ lt_tmp_lt_search_path_spec=
+ lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null`
+ for lt_sys_path in $lt_search_path_spec; do
+ if test -d "$lt_sys_path/$lt_multi_os_dir"; then
+ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir"
+ else
+ test -d "$lt_sys_path" && \
+ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path"
+ fi
+ done
+ lt_search_path_spec=`$ECHO $lt_tmp_lt_search_path_spec | awk '
+BEGIN {RS=" "; FS="/|\n";} {
+ lt_foo="";
+ lt_count=0;
+ for (lt_i = NF; lt_i > 0; lt_i--) {
+ if ($lt_i != "" && $lt_i != ".") {
+ if ($lt_i == "..") {
+ lt_count++;
+ } else {
+ if (lt_count == 0) {
+ lt_foo="/" $lt_i lt_foo;
+ } else {
+ lt_count--;
+ }
+ }
+ }
+ }
+ if (lt_foo != "") { lt_freq[lt_foo]++; }
+ if (lt_freq[lt_foo] == 1) { print lt_foo; }
+}'`
+ sys_lib_search_path_spec=`$ECHO $lt_search_path_spec`
+else
+ sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+fi
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+shrext_cmds=".so"
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+need_lib_prefix=unknown
+hardcode_into_libs=no
+
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+need_version=unknown
+
+case $host_os in
+aix3*)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a'
+ shlibpath_var=LIBPATH
+
+ # AIX 3 has no versioning support, so we append a major version to the name.
+ soname_spec='${libname}${release}${shared_ext}$major'
+ ;;
+
+aix[4-9]*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ hardcode_into_libs=yes
+ if test "$host_cpu" = ia64; then
+ # AIX 5 supports IA64
+ library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ else
+ # With GCC up to 2.95.x, collect2 would create an import file
+ # for dependence libraries. The import file would start with
+ # the line `#! .'. This would cause the generated library to
+ # depend on `.', always an invalid library. This was fixed in
+ # development snapshots of GCC prior to 3.0.
+ case $host_os in
+ aix4 | aix4.[01] | aix4.[01].*)
+ if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
+ echo ' yes '
+ echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then
+ :
+ else
+ can_build_shared=no
+ fi
+ ;;
+ esac
+ # AIX (on Power*) has no versioning support, so currently we can not hardcode correct
+ # soname into executable. Probably we can add versioning support to
+ # collect2, so additional links can be useful in future.
+ if test "$aix_use_runtimelinking" = yes; then
+ # If using run time linking (on AIX 4.2 or later) use lib<name>.so
+ # instead of lib<name>.a to let people know that these are not
+ # typical AIX shared libraries.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ else
+ # We preserve .a as extension for shared libraries through AIX4.2
+ # and later when we are not doing run time linking.
+ library_names_spec='${libname}${release}.a $libname.a'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ fi
+ shlibpath_var=LIBPATH
+ fi
+ ;;
+
+amigaos*)
+ case $host_cpu in
+ powerpc)
+ # Since July 2007 AmigaOS4 officially supports .so libraries.
+ # When compiling the executable, add -use-dynld -Lsobjs: to the compileline.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ ;;
+ m68k)
+ library_names_spec='$libname.ixlibrary $libname.a'
+ # Create ${libname}_ixlibrary.a entries in /sys/libs.
+ finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$ECHO "X$lib" | $Xsed -e '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+ ;;
+ esac
+ ;;
+
+beos*)
+ library_names_spec='${libname}${shared_ext}'
+ dynamic_linker="$host_os ld.so"
+ shlibpath_var=LIBRARY_PATH
+ ;;
+
+bsdi[45]*)
+ version_type=linux
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+ sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+ # the default ld.so.conf also contains /usr/contrib/lib and
+ # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+ # libtool to hard-code these into programs
+ ;;
+
+cygwin* | mingw* | pw32* | cegcc*)
+ version_type=windows
+ shrext_cmds=".dll"
+ need_version=no
+ need_lib_prefix=no
+
+ case $GCC,$host_os in
+ yes,cygwin* | yes,mingw* | yes,pw32* | yes,cegcc*)
+ library_names_spec='$libname.dll.a'
+ # DLL is installed to $(libdir)/../bin by postinstall_cmds
+ postinstall_cmds='base_file=`basename \${file}`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~
+ dldir=$destdir/`dirname \$dlpath`~
+ test -d \$dldir || mkdir -p \$dldir~
+ $install_prog $dir/$dlname \$dldir/$dlname~
+ chmod a+x \$dldir/$dlname~
+ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+ eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+ fi'
+ postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+ dlpath=$dir/\$dldll~
+ $RM \$dlpath'
+ shlibpath_overrides_runpath=yes
+
+ case $host_os in
+ cygwin*)
+ # Cygwin DLLs use 'cyg' prefix rather than 'lib'
+ soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+ sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib"
+ ;;
+ mingw* | cegcc*)
+ # MinGW DLLs use traditional 'lib' prefix
+ soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+ sys_lib_search_path_spec=`$CC -print-search-dirs | $GREP "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"`
+ if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then
+ # It is most probably a Windows format PATH printed by
+ # mingw gcc, but we are running on Cygwin. Gcc prints its search
+ # path with ; separators, and with drive letters. We can handle the
+ # drive letters (cygwin fileutils understands them), so leave them,
+ # especially as we might pass files found there to a mingw objdump,
+ # which wouldn't understand a cygwinified path. Ahh.
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+ else
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+ fi
+ ;;
+ pw32*)
+ # pw32 DLLs use 'pw' prefix rather than 'lib'
+ library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+ ;;
+ esac
+ ;;
+
+ *)
+ library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib'
+ ;;
+ esac
+ dynamic_linker='Win32 ld.exe'
+ # FIXME: first we should search . and the directory the executable is in
+ shlibpath_var=PATH
+ ;;
+
+darwin* | rhapsody*)
+ dynamic_linker="$host_os dyld"
+ version_type=darwin
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext'
+ soname_spec='${libname}${release}${major}$shared_ext'
+ shlibpath_overrides_runpath=yes
+ shlibpath_var=DYLD_LIBRARY_PATH
+ shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'
+
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"
+ sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
+ ;;
+
+dgux*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+freebsd1*)
+ dynamic_linker=no
+ ;;
+
+freebsd* | dragonfly*)
+ # DragonFly does not have aout. When/if they implement a new
+ # versioning mechanism, adjust this.
+ if test -x /usr/bin/objformat; then
+ objformat=`/usr/bin/objformat`
+ else
+ case $host_os in
+ freebsd[123]*) objformat=aout ;;
+ *) objformat=elf ;;
+ esac
+ fi
+ version_type=freebsd-$objformat
+ case $version_type in
+ freebsd-elf*)
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+ need_version=no
+ need_lib_prefix=no
+ ;;
+ freebsd-*)
+ library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix'
+ need_version=yes
+ ;;
+ esac
+ shlibpath_var=LD_LIBRARY_PATH
+ case $host_os in
+ freebsd2*)
+ shlibpath_overrides_runpath=yes
+ ;;
+ freebsd3.[01]* | freebsdelf3.[01]*)
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+ freebsd3.[2-9]* | freebsdelf3.[2-9]* | \
+ freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1)
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+ *) # from 4.6 on, and DragonFly
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+ esac
+ ;;
+
+gnu*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ hardcode_into_libs=yes
+ ;;
+
+hpux9* | hpux10* | hpux11*)
+ # Give a soname corresponding to the major version so that dld.sl refuses to
+ # link against other versions.
+ version_type=sunos
+ need_lib_prefix=no
+ need_version=no
+ case $host_cpu in
+ ia64*)
+ shrext_cmds='.so'
+ hardcode_into_libs=yes
+ dynamic_linker="$host_os dld.so"
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ if test "X$HPUX_IA64_MODE" = X32; then
+ sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
+ else
+ sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
+ fi
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ ;;
+ hppa*64*)
+ shrext_cmds='.sl'
+ hardcode_into_libs=yes
+ dynamic_linker="$host_os dld.sl"
+ shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
+ shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ ;;
+ *)
+ shrext_cmds='.sl'
+ dynamic_linker="$host_os dld.sl"
+ shlibpath_var=SHLIB_PATH
+ shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ ;;
+ esac
+ # HP-UX runs *really* slowly unless shared libraries are mode 555.
+ postinstall_cmds='chmod 555 $lib'
+ ;;
+
+interix[3-9]*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+
+irix5* | irix6* | nonstopux*)
+ case $host_os in
+ nonstopux*) version_type=nonstopux ;;
+ *)
+ if test "$lt_cv_prog_gnu_ld" = yes; then
+ version_type=linux
+ else
+ version_type=irix
+ fi ;;
+ esac
+ need_lib_prefix=no
+ need_version=no
+ soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}'
+ case $host_os in
+ irix5* | nonstopux*)
+ libsuff= shlibsuff=
+ ;;
+ *)
+ case $LD in # libtool.m4 will add one of these switches to LD
+ *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
+ libsuff= shlibsuff= libmagic=32-bit;;
+ *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
+ libsuff=32 shlibsuff=N32 libmagic=N32;;
+ *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
+ libsuff=64 shlibsuff=64 libmagic=64-bit;;
+ *) libsuff= shlibsuff= libmagic=never-match;;
+ esac
+ ;;
+ esac
+ shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+ shlibpath_overrides_runpath=no
+ sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}"
+ sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}"
+ hardcode_into_libs=yes
+ ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux*oldld* | linux*aout* | linux*coff*)
+ dynamic_linker=no
+ ;;
+
+# This must be Linux ELF.
+linux* | k*bsd*-gnu)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ # Some binutils ld are patched to set DT_RUNPATH
+ save_LDFLAGS=$LDFLAGS
+ save_libdir=$libdir
+ eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \
+ LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\""
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then :
+ shlibpath_overrides_runpath=yes
+fi
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LDFLAGS=$save_LDFLAGS
+ libdir=$save_libdir
+
+ # This implies no fast_install, which is unacceptable.
+ # Some rework will be needed to allow for fast_install
+ # before this can be enabled.
+ hardcode_into_libs=yes
+
+ # Append ld.so.conf contents to the search path
+ if test -f /etc/ld.so.conf; then
+ lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '`
+ sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
+ fi
+
+ # We used to test for /lib/ld.so.1 and disable shared libraries on
+ # powerpc, because MkLinux only supported shared libraries with the
+ # GNU dynamic linker. Since this was broken with cross compilers,
+ # most powerpc-linux boxes support dynamic linking these days and
+ # people can always --disable-shared, the test was removed, and we
+ # assume the GNU/Linux dynamic linker is in use.
+ dynamic_linker='GNU/Linux ld.so'
+ ;;
+
+netbsd*)
+ version_type=sunos
+ need_lib_prefix=no
+ need_version=no
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ dynamic_linker='NetBSD (a.out) ld.so'
+ else
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ dynamic_linker='NetBSD ld.elf_so'
+ fi
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+
+newsos6)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ ;;
+
+*nto* | *qnx*)
+ version_type=qnx
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ dynamic_linker='ldqnx.so'
+ ;;
+
+openbsd*)
+ version_type=sunos
+ sys_lib_dlsearch_path_spec="/usr/lib"
+ need_lib_prefix=no
+ # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs.
+ case $host_os in
+ openbsd3.3 | openbsd3.3.*) need_version=yes ;;
+ *) need_version=no ;;
+ esac
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+ case $host_os in
+ openbsd2.[89] | openbsd2.[89].*)
+ shlibpath_overrides_runpath=no
+ ;;
+ *)
+ shlibpath_overrides_runpath=yes
+ ;;
+ esac
+ else
+ shlibpath_overrides_runpath=yes
+ fi
+ ;;
+
+os2*)
+ libname_spec='$name'
+ shrext_cmds=".dll"
+ need_lib_prefix=no
+ library_names_spec='$libname${shared_ext} $libname.a'
+ dynamic_linker='OS/2 ld.exe'
+ shlibpath_var=LIBPATH
+ ;;
+
+osf3* | osf4* | osf5*)
+ version_type=osf
+ need_lib_prefix=no
+ need_version=no
+ soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+ sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec"
+ ;;
+
+rdos*)
+ dynamic_linker=no
+ ;;
+
+solaris*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ # ldd complains unless libraries are executable
+ postinstall_cmds='chmod +x $lib'
+ ;;
+
+sunos4*)
+ version_type=sunos
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ if test "$with_gnu_ld" = yes; then
+ need_lib_prefix=no
+ fi
+ need_version=yes
+ ;;
+
+sysv4 | sysv4.3*)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ case $host_vendor in
+ sni)
+ shlibpath_overrides_runpath=no
+ need_lib_prefix=no
+ runpath_var=LD_RUN_PATH
+ ;;
+ siemens)
+ need_lib_prefix=no
+ ;;
+ motorola)
+ need_lib_prefix=no
+ need_version=no
+ shlibpath_overrides_runpath=no
+ sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+ ;;
+ esac
+ ;;
+
+sysv4*MP*)
+ if test -d /usr/nec ;then
+ version_type=linux
+ library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}'
+ soname_spec='$libname${shared_ext}.$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ fi
+ ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+ version_type=freebsd-elf
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ if test "$with_gnu_ld" = yes; then
+ sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib'
+ else
+ sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'
+ case $host_os in
+ sco3.2v5*)
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /lib"
+ ;;
+ esac
+ fi
+ sys_lib_dlsearch_path_spec='/usr/lib'
+ ;;
+
+tpf*)
+ # TPF is a cross-target only. Preferred cross-host = GNU/Linux.
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+
+uts4*)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+*)
+ dynamic_linker=no
+ ;;
+esac
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5
+$as_echo "$dynamic_linker" >&6; }
+test "$dynamic_linker" = no && can_build_shared=no
+
+variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
+if test "$GCC" = yes; then
+ variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
+fi
+
+if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then
+ sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec"
+fi
+if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then
+ sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec"
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5
+$as_echo_n "checking how to hardcode library paths into programs... " >&6; }
+hardcode_action=
+if test -n "$hardcode_libdir_flag_spec" ||
+ test -n "$runpath_var" ||
+ test "X$hardcode_automatic" = "Xyes" ; then
+
+ # We can hardcode non-existent directories.
+ if test "$hardcode_direct" != no &&
+ # If the only mechanism to avoid hardcoding is shlibpath_var, we
+ # have to relink, otherwise we might link with an installed library
+ # when we should be linking with a yet-to-be-installed one
+ ## test "$_LT_TAGVAR(hardcode_shlibpath_var, )" != no &&
+ test "$hardcode_minus_L" != no; then
+ # Linking always hardcodes the temporary library directory.
+ hardcode_action=relink
+ else
+ # We can link without hardcoding, and we can hardcode nonexisting dirs.
+ hardcode_action=immediate
+ fi
+else
+ # We cannot hardcode anything, or else we can only hardcode existing
+ # directories.
+ hardcode_action=unsupported
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5
+$as_echo "$hardcode_action" >&6; }
+
+if test "$hardcode_action" = relink ||
+ test "$inherit_rpath" = yes; then
+ # Fast installation is not supported
+ enable_fast_install=no
+elif test "$shlibpath_overrides_runpath" = yes ||
+ test "$enable_shared" = no; then
+ # Fast installation is not necessary
+ enable_fast_install=needless
+fi
+
+
+
+
+
+
+ if test "x$enable_dlopen" != xyes; then
+ enable_dlopen=unknown
+ enable_dlopen_self=unknown
+ enable_dlopen_self_static=unknown
+else
+ lt_cv_dlopen=no
+ lt_cv_dlopen_libs=
+
+ case $host_os in
+ beos*)
+ lt_cv_dlopen="load_add_on"
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+ ;;
+
+ mingw* | pw32* | cegcc*)
+ lt_cv_dlopen="LoadLibrary"
+ lt_cv_dlopen_libs=
+ ;;
+
+ cygwin*)
+ lt_cv_dlopen="dlopen"
+ lt_cv_dlopen_libs=
+ ;;
+
+ darwin*)
+ # if libdl is installed we need to link against it
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
+$as_echo_n "checking for dlopen in -ldl... " >&6; }
+if ${ac_cv_lib_dl_dlopen+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_dl_dlopen=yes
+else
+ ac_cv_lib_dl_dlopen=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
+$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
+if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
+ lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"
+else
+
+ lt_cv_dlopen="dyld"
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+
+fi
+
+ ;;
+
+ *)
+ ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load"
+if test "x$ac_cv_func_shl_load" = xyes; then :
+ lt_cv_dlopen="shl_load"
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5
+$as_echo_n "checking for shl_load in -ldld... " >&6; }
+if ${ac_cv_lib_dld_shl_load+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char shl_load ();
+int
+main ()
+{
+return shl_load ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_dld_shl_load=yes
+else
+ ac_cv_lib_dld_shl_load=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5
+$as_echo "$ac_cv_lib_dld_shl_load" >&6; }
+if test "x$ac_cv_lib_dld_shl_load" = xyes; then :
+ lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"
+else
+ ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen"
+if test "x$ac_cv_func_dlopen" = xyes; then :
+ lt_cv_dlopen="dlopen"
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
+$as_echo_n "checking for dlopen in -ldl... " >&6; }
+if ${ac_cv_lib_dl_dlopen+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_dl_dlopen=yes
+else
+ ac_cv_lib_dl_dlopen=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
+$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
+if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
+ lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5
+$as_echo_n "checking for dlopen in -lsvld... " >&6; }
+if ${ac_cv_lib_svld_dlopen+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsvld $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_svld_dlopen=yes
+else
+ ac_cv_lib_svld_dlopen=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5
+$as_echo "$ac_cv_lib_svld_dlopen" >&6; }
+if test "x$ac_cv_lib_svld_dlopen" = xyes; then :
+ lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5
+$as_echo_n "checking for dld_link in -ldld... " >&6; }
+if ${ac_cv_lib_dld_dld_link+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dld_link ();
+int
+main ()
+{
+return dld_link ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_dld_dld_link=yes
+else
+ ac_cv_lib_dld_dld_link=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5
+$as_echo "$ac_cv_lib_dld_dld_link" >&6; }
+if test "x$ac_cv_lib_dld_dld_link" = xyes; then :
+ lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+ ;;
+ esac
+
+ if test "x$lt_cv_dlopen" != xno; then
+ enable_dlopen=yes
+ else
+ enable_dlopen=no
+ fi
+
+ case $lt_cv_dlopen in
+ dlopen)
+ save_CPPFLAGS="$CPPFLAGS"
+ test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+
+ save_LDFLAGS="$LDFLAGS"
+ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
+
+ save_LIBS="$LIBS"
+ LIBS="$lt_cv_dlopen_libs $LIBS"
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5
+$as_echo_n "checking whether a program can dlopen itself... " >&6; }
+if ${lt_cv_dlopen_self+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then :
+ lt_cv_dlopen_self=cross
+else
+ lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+ lt_status=$lt_dlunknown
+ cat > conftest.$ac_ext <<_LT_EOF
+#line 11157 "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+# define LT_DLGLOBAL RTLD_GLOBAL
+#else
+# ifdef DL_GLOBAL
+# define LT_DLGLOBAL DL_GLOBAL
+# else
+# define LT_DLGLOBAL 0
+# endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+ find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+# ifdef RTLD_LAZY
+# define LT_DLLAZY_OR_NOW RTLD_LAZY
+# else
+# ifdef DL_LAZY
+# define LT_DLLAZY_OR_NOW DL_LAZY
+# else
+# ifdef RTLD_NOW
+# define LT_DLLAZY_OR_NOW RTLD_NOW
+# else
+# ifdef DL_NOW
+# define LT_DLLAZY_OR_NOW DL_NOW
+# else
+# define LT_DLLAZY_OR_NOW 0
+# endif
+# endif
+# endif
+# endif
+#endif
+
+void fnord() { int i=42;}
+int main ()
+{
+ void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+ int status = $lt_dlunknown;
+
+ if (self)
+ {
+ if (dlsym (self,"fnord")) status = $lt_dlno_uscore;
+ else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+ /* dlclose (self); */
+ }
+ else
+ puts (dlerror ());
+
+ return status;
+}
+_LT_EOF
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then
+ (./conftest; exit; ) >&5 2>/dev/null
+ lt_status=$?
+ case x$lt_status in
+ x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;;
+ x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;;
+ x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;;
+ esac
+ else :
+ # compilation failed
+ lt_cv_dlopen_self=no
+ fi
+fi
+rm -fr conftest*
+
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5
+$as_echo "$lt_cv_dlopen_self" >&6; }
+
+ if test "x$lt_cv_dlopen_self" = xyes; then
+ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5
+$as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; }
+if ${lt_cv_dlopen_self_static+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then :
+ lt_cv_dlopen_self_static=cross
+else
+ lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+ lt_status=$lt_dlunknown
+ cat > conftest.$ac_ext <<_LT_EOF
+#line 11253 "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+# define LT_DLGLOBAL RTLD_GLOBAL
+#else
+# ifdef DL_GLOBAL
+# define LT_DLGLOBAL DL_GLOBAL
+# else
+# define LT_DLGLOBAL 0
+# endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+ find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+# ifdef RTLD_LAZY
+# define LT_DLLAZY_OR_NOW RTLD_LAZY
+# else
+# ifdef DL_LAZY
+# define LT_DLLAZY_OR_NOW DL_LAZY
+# else
+# ifdef RTLD_NOW
+# define LT_DLLAZY_OR_NOW RTLD_NOW
+# else
+# ifdef DL_NOW
+# define LT_DLLAZY_OR_NOW DL_NOW
+# else
+# define LT_DLLAZY_OR_NOW 0
+# endif
+# endif
+# endif
+# endif
+#endif
+
+void fnord() { int i=42;}
+int main ()
+{
+ void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+ int status = $lt_dlunknown;
+
+ if (self)
+ {
+ if (dlsym (self,"fnord")) status = $lt_dlno_uscore;
+ else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+ /* dlclose (self); */
+ }
+ else
+ puts (dlerror ());
+
+ return status;
+}
+_LT_EOF
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then
+ (./conftest; exit; ) >&5 2>/dev/null
+ lt_status=$?
+ case x$lt_status in
+ x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;;
+ x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;;
+ x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;;
+ esac
+ else :
+ # compilation failed
+ lt_cv_dlopen_self_static=no
+ fi
+fi
+rm -fr conftest*
+
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5
+$as_echo "$lt_cv_dlopen_self_static" >&6; }
+ fi
+
+ CPPFLAGS="$save_CPPFLAGS"
+ LDFLAGS="$save_LDFLAGS"
+ LIBS="$save_LIBS"
+ ;;
+ esac
+
+ case $lt_cv_dlopen_self in
+ yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+ *) enable_dlopen_self=unknown ;;
+ esac
+
+ case $lt_cv_dlopen_self_static in
+ yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+ *) enable_dlopen_self_static=unknown ;;
+ esac
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+striplib=
+old_striplib=
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5
+$as_echo_n "checking whether stripping libraries is possible... " >&6; }
+if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then
+ test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
+ test -z "$striplib" && striplib="$STRIP --strip-unneeded"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+# FIXME - insert some real tests, host_os isn't really good enough
+ case $host_os in
+ darwin*)
+ if test -n "$STRIP" ; then
+ striplib="$STRIP -x"
+ old_striplib="$STRIP -S"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ fi
+ ;;
+ *)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ ;;
+ esac
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+ # Report which library types will actually be built
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5
+$as_echo_n "checking if libtool supports shared libraries... " >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5
+$as_echo "$can_build_shared" >&6; }
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5
+$as_echo_n "checking whether to build shared libraries... " >&6; }
+ test "$can_build_shared" = "no" && enable_shared=no
+
+ # On AIX, shared libraries and static libraries use the same namespace, and
+ # are all built from PIC.
+ case $host_os in
+ aix3*)
+ test "$enable_shared" = yes && enable_static=no
+ if test -n "$RANLIB"; then
+ archive_cmds="$archive_cmds~\$RANLIB \$lib"
+ postinstall_cmds='$RANLIB $lib'
+ fi
+ ;;
+
+ aix[4-9]*)
+ if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+ test "$enable_shared" = yes && enable_static=no
+ fi
+ ;;
+ esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5
+$as_echo "$enable_shared" >&6; }
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5
+$as_echo_n "checking whether to build static libraries... " >&6; }
+ # Make sure either enable_shared or enable_static is yes.
+ test "$enable_shared" = yes || enable_static=yes
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5
+$as_echo "$enable_static" >&6; }
+
+
+
+
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+CC="$lt_save_CC"
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ac_config_commands="$ac_config_commands libtool"
+
+
+
+
+# Only expand once:
+
+
+
+# Extract the first word of "ar", so it can be a program name with args.
+set dummy ar; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_AR+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $AR in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_AR="$AR" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_AR="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_AR" && ac_cv_path_AR="ar"
+ ;;
+esac
+fi
+AR=$ac_cv_path_AR
+if test -n "$AR"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5
+$as_echo "$AR" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+# Extract the first word of "rm", so it can be a program name with args.
+set dummy rm; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_RM+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $RM in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_RM="$RM" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_RM="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_RM" && ac_cv_path_RM="rm"
+ ;;
+esac
+fi
+RM=$ac_cv_path_RM
+if test -n "$RM"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RM" >&5
+$as_echo "$RM" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+# Extract the first word of "cp", so it can be a program name with args.
+set dummy cp; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_CP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $CP in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_CP="$CP" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_CP="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_CP" && ac_cv_path_CP="cp"
+ ;;
+esac
+fi
+CP=$ac_cv_path_CP
+if test -n "$CP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CP" >&5
+$as_echo "$CP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+# Extract the first word of "ln", so it can be a program name with args.
+set dummy ln; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_LN+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $LN in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_LN="$LN" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_LN="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_LN" && ac_cv_path_LN="ln"
+ ;;
+esac
+fi
+LN=$ac_cv_path_LN
+if test -n "$LN"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LN" >&5
+$as_echo "$LN" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+# Extract the first word of "sed", so it can be a program name with args.
+set dummy sed; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_SED+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $SED in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_SED="$SED" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_SED="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_SED" && ac_cv_path_SED="sed"
+ ;;
+esac
+fi
+SED=$ac_cv_path_SED
+if test -n "$SED"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SED" >&5
+$as_echo "$SED" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+# Extract the first word of "make", so it can be a program name with args.
+set dummy make; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_MAKE+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $MAKE in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_MAKE="$MAKE" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_MAKE="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+MAKE=$ac_cv_path_MAKE
+if test -n "$MAKE"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAKE" >&5
+$as_echo "$MAKE" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether NLS is requested" >&5
+$as_echo_n "checking whether NLS is requested... " >&6; }
+ # Check whether --enable-nls was given.
+if test "${enable_nls+set}" = set; then :
+ enableval=$enable_nls; USE_NLS=$enableval
+else
+ USE_NLS=yes
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $USE_NLS" >&5
+$as_echo "$USE_NLS" >&6; }
+
+
+
+
+
+
+# Prepare PATH_SEPARATOR.
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+# Find out how to test for executable files. Don't use a zero-byte file,
+# as systems may use methods other than mode bits to determine executability.
+cat >conf$$.file <<_ASEOF
+#! /bin/sh
+exit 0
+_ASEOF
+chmod +x conf$$.file
+if test -x conf$$.file >/dev/null 2>&1; then
+ ac_executable_p="test -x"
+else
+ ac_executable_p="test -f"
+fi
+rm -f conf$$.file
+
+# Extract the first word of "msgfmt", so it can be a program name with args.
+set dummy msgfmt; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_MSGFMT+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case "$MSGFMT" in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_MSGFMT="$MSGFMT" # Let the user override the test with a path.
+ ;;
+ *)
+ ac_save_IFS="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH; do
+ IFS="$ac_save_IFS"
+ test -z "$ac_dir" && ac_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $ac_executable_p "$ac_dir/$ac_word$ac_exec_ext"; then
+ echo "$as_me: trying $ac_dir/$ac_word..." >&5
+ if $ac_dir/$ac_word --statistics /dev/null >&5 2>&1 &&
+ (if $ac_dir/$ac_word --statistics /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi); then
+ ac_cv_path_MSGFMT="$ac_dir/$ac_word$ac_exec_ext"
+ break 2
+ fi
+ fi
+ done
+ done
+ IFS="$ac_save_IFS"
+ test -z "$ac_cv_path_MSGFMT" && ac_cv_path_MSGFMT=":"
+ ;;
+esac
+fi
+MSGFMT="$ac_cv_path_MSGFMT"
+if test "$MSGFMT" != ":"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MSGFMT" >&5
+$as_echo "$MSGFMT" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ # Extract the first word of "gmsgfmt", so it can be a program name with args.
+set dummy gmsgfmt; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_GMSGFMT+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $GMSGFMT in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_GMSGFMT="$GMSGFMT" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_GMSGFMT="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_GMSGFMT" && ac_cv_path_GMSGFMT="$MSGFMT"
+ ;;
+esac
+fi
+GMSGFMT=$ac_cv_path_GMSGFMT
+if test -n "$GMSGFMT"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $GMSGFMT" >&5
+$as_echo "$GMSGFMT" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+ case `$MSGFMT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in
+ '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) MSGFMT_015=: ;;
+ *) MSGFMT_015=$MSGFMT ;;
+ esac
+
+ case `$GMSGFMT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in
+ '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) GMSGFMT_015=: ;;
+ *) GMSGFMT_015=$GMSGFMT ;;
+ esac
+
+
+
+# Prepare PATH_SEPARATOR.
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+# Find out how to test for executable files. Don't use a zero-byte file,
+# as systems may use methods other than mode bits to determine executability.
+cat >conf$$.file <<_ASEOF
+#! /bin/sh
+exit 0
+_ASEOF
+chmod +x conf$$.file
+if test -x conf$$.file >/dev/null 2>&1; then
+ ac_executable_p="test -x"
+else
+ ac_executable_p="test -f"
+fi
+rm -f conf$$.file
+
+# Extract the first word of "xgettext", so it can be a program name with args.
+set dummy xgettext; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_XGETTEXT+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case "$XGETTEXT" in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_XGETTEXT="$XGETTEXT" # Let the user override the test with a path.
+ ;;
+ *)
+ ac_save_IFS="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH; do
+ IFS="$ac_save_IFS"
+ test -z "$ac_dir" && ac_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $ac_executable_p "$ac_dir/$ac_word$ac_exec_ext"; then
+ echo "$as_me: trying $ac_dir/$ac_word..." >&5
+ if $ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= /dev/null >&5 2>&1 &&
+ (if $ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi); then
+ ac_cv_path_XGETTEXT="$ac_dir/$ac_word$ac_exec_ext"
+ break 2
+ fi
+ fi
+ done
+ done
+ IFS="$ac_save_IFS"
+ test -z "$ac_cv_path_XGETTEXT" && ac_cv_path_XGETTEXT=":"
+ ;;
+esac
+fi
+XGETTEXT="$ac_cv_path_XGETTEXT"
+if test "$XGETTEXT" != ":"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $XGETTEXT" >&5
+$as_echo "$XGETTEXT" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ rm -f messages.po
+
+ case `$XGETTEXT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in
+ '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) XGETTEXT_015=: ;;
+ *) XGETTEXT_015=$XGETTEXT ;;
+ esac
+
+
+
+# Prepare PATH_SEPARATOR.
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+# Find out how to test for executable files. Don't use a zero-byte file,
+# as systems may use methods other than mode bits to determine executability.
+cat >conf$$.file <<_ASEOF
+#! /bin/sh
+exit 0
+_ASEOF
+chmod +x conf$$.file
+if test -x conf$$.file >/dev/null 2>&1; then
+ ac_executable_p="test -x"
+else
+ ac_executable_p="test -f"
+fi
+rm -f conf$$.file
+
+# Extract the first word of "msgmerge", so it can be a program name with args.
+set dummy msgmerge; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_MSGMERGE+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case "$MSGMERGE" in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_MSGMERGE="$MSGMERGE" # Let the user override the test with a path.
+ ;;
+ *)
+ ac_save_IFS="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH; do
+ IFS="$ac_save_IFS"
+ test -z "$ac_dir" && ac_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $ac_executable_p "$ac_dir/$ac_word$ac_exec_ext"; then
+ echo "$as_me: trying $ac_dir/$ac_word..." >&5
+ if $ac_dir/$ac_word --update -q /dev/null /dev/null >&5 2>&1; then
+ ac_cv_path_MSGMERGE="$ac_dir/$ac_word$ac_exec_ext"
+ break 2
+ fi
+ fi
+ done
+ done
+ IFS="$ac_save_IFS"
+ test -z "$ac_cv_path_MSGMERGE" && ac_cv_path_MSGMERGE=":"
+ ;;
+esac
+fi
+MSGMERGE="$ac_cv_path_MSGMERGE"
+if test "$MSGMERGE" != ":"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MSGMERGE" >&5
+$as_echo "$MSGMERGE" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$localedir" || localedir='${datadir}/locale'
+
+
+ ac_config_commands="$ac_config_commands po-directories"
+
+
+
+ if test "X$prefix" = "XNONE"; then
+ acl_final_prefix="$ac_default_prefix"
+ else
+ acl_final_prefix="$prefix"
+ fi
+ if test "X$exec_prefix" = "XNONE"; then
+ acl_final_exec_prefix='${prefix}'
+ else
+ acl_final_exec_prefix="$exec_prefix"
+ fi
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ eval acl_final_exec_prefix=\"$acl_final_exec_prefix\"
+ prefix="$acl_save_prefix"
+
+
+# Check whether --with-gnu-ld was given.
+if test "${with_gnu_ld+set}" = set; then :
+ withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes
+else
+ with_gnu_ld=no
+fi
+
+# Prepare PATH_SEPARATOR.
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+ac_prog=ld
+if test "$GCC" = yes; then
+ # Check if gcc -print-prog-name=ld gives a path.
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by GCC" >&5
+$as_echo_n "checking for ld used by GCC... " >&6; }
+ case $host in
+ *-*-mingw*)
+ # gcc leaves a trailing carriage return which upsets mingw
+ ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+ *)
+ ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+ esac
+ case $ac_prog in
+ # Accept absolute paths.
+ [\\/]* | [A-Za-z]:[\\/]*)
+ re_direlt='/[^/][^/]*/\.\./'
+ # Canonicalize the path of ld
+ ac_prog=`echo $ac_prog| sed 's%\\\\%/%g'`
+ while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do
+ ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"`
+ done
+ test -z "$LD" && LD="$ac_prog"
+ ;;
+ "")
+ # If it fails, then pretend we aren't using GCC.
+ ac_prog=ld
+ ;;
+ *)
+ # If it is relative, then search for the first ld in PATH.
+ with_gnu_ld=unknown
+ ;;
+ esac
+elif test "$with_gnu_ld" = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5
+$as_echo_n "checking for GNU ld... " >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5
+$as_echo_n "checking for non-GNU ld... " >&6; }
+fi
+if ${acl_cv_path_LD+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$LD"; then
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR-:}"
+ for ac_dir in $PATH; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+ acl_cv_path_LD="$ac_dir/$ac_prog"
+ # Check to see if the program is GNU ld. I'd rather use --version,
+ # but apparently some GNU ld's only accept -v.
+ # Break only if it was the GNU/non-GNU ld that we prefer.
+ case `"$acl_cv_path_LD" -v 2>&1 < /dev/null` in
+ *GNU* | *'with BFD'*)
+ test "$with_gnu_ld" != no && break ;;
+ *)
+ test "$with_gnu_ld" != yes && break ;;
+ esac
+ fi
+ done
+ IFS="$ac_save_ifs"
+else
+ acl_cv_path_LD="$LD" # Let the user override the test with a path.
+fi
+fi
+
+LD="$acl_cv_path_LD"
+if test -n "$LD"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LD" >&5
+$as_echo "$LD" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5
+$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; }
+if ${acl_cv_prog_gnu_ld+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ # I'd rather use --version here, but apparently some GNU ld's only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+ acl_cv_prog_gnu_ld=yes ;;
+*)
+ acl_cv_prog_gnu_ld=no ;;
+esac
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $acl_cv_prog_gnu_ld" >&5
+$as_echo "$acl_cv_prog_gnu_ld" >&6; }
+with_gnu_ld=$acl_cv_prog_gnu_ld
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shared library run path origin" >&5
+$as_echo_n "checking for shared library run path origin... " >&6; }
+if ${acl_cv_rpath+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ CC="$CC" GCC="$GCC" LDFLAGS="$LDFLAGS" LD="$LD" with_gnu_ld="$with_gnu_ld" \
+ ${CONFIG_SHELL-/bin/sh} "$ac_aux_dir/config.rpath" "$host" > conftest.sh
+ . ./conftest.sh
+ rm -f ./conftest.sh
+ acl_cv_rpath=done
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $acl_cv_rpath" >&5
+$as_echo "$acl_cv_rpath" >&6; }
+ wl="$acl_cv_wl"
+ libext="$acl_cv_libext"
+ shlibext="$acl_cv_shlibext"
+ hardcode_libdir_flag_spec="$acl_cv_hardcode_libdir_flag_spec"
+ hardcode_libdir_separator="$acl_cv_hardcode_libdir_separator"
+ hardcode_direct="$acl_cv_hardcode_direct"
+ hardcode_minus_L="$acl_cv_hardcode_minus_L"
+ # Check whether --enable-rpath was given.
+if test "${enable_rpath+set}" = set; then :
+ enableval=$enable_rpath; :
+else
+ enable_rpath=yes
+fi
+
+
+
+ acl_libdirstem=lib
+ searchpath=`(LC_ALL=C $CC -print-search-dirs) 2>/dev/null | sed -n -e 's,^libraries: ,,p' | sed -e 's,^=,,'`
+ if test -n "$searchpath"; then
+ acl_save_IFS="${IFS= }"; IFS=":"
+ for searchdir in $searchpath; do
+ if test -d "$searchdir"; then
+ case "$searchdir" in
+ */lib64/ | */lib64 ) acl_libdirstem=lib64 ;;
+ *) searchdir=`cd "$searchdir" && pwd`
+ case "$searchdir" in
+ */lib64 ) acl_libdirstem=lib64 ;;
+ esac ;;
+ esac
+ fi
+ done
+ IFS="$acl_save_IFS"
+ fi
+
+
+
+
+
+
+
+
+ use_additional=yes
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+
+# Check whether --with-libiconv-prefix was given.
+if test "${with_libiconv_prefix+set}" = set; then :
+ withval=$with_libiconv_prefix;
+ if test "X$withval" = "Xno"; then
+ use_additional=no
+ else
+ if test "X$withval" = "X"; then
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ else
+ additional_includedir="$withval/include"
+ additional_libdir="$withval/$acl_libdirstem"
+ fi
+ fi
+
+fi
+
+ LIBICONV=
+ LTLIBICONV=
+ INCICONV=
+ rpathdirs=
+ ltrpathdirs=
+ names_already_handled=
+ names_next_round='iconv '
+ while test -n "$names_next_round"; do
+ names_this_round="$names_next_round"
+ names_next_round=
+ for name in $names_this_round; do
+ already_handled=
+ for n in $names_already_handled; do
+ if test "$n" = "$name"; then
+ already_handled=yes
+ break
+ fi
+ done
+ if test -z "$already_handled"; then
+ names_already_handled="$names_already_handled $name"
+ uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'`
+ eval value=\"\$HAVE_LIB$uppername\"
+ if test -n "$value"; then
+ if test "$value" = yes; then
+ eval value=\"\$LIB$uppername\"
+ test -z "$value" || LIBICONV="${LIBICONV}${LIBICONV:+ }$value"
+ eval value=\"\$LTLIB$uppername\"
+ test -z "$value" || LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }$value"
+ else
+ :
+ fi
+ else
+ found_dir=
+ found_la=
+ found_so=
+ found_a=
+ if test $use_additional = yes; then
+ if test -n "$shlibext" \
+ && { test -f "$additional_libdir/lib$name.$shlibext" \
+ || { test "$shlibext" = dll \
+ && test -f "$additional_libdir/lib$name.dll.a"; }; }; then
+ found_dir="$additional_libdir"
+ if test -f "$additional_libdir/lib$name.$shlibext"; then
+ found_so="$additional_libdir/lib$name.$shlibext"
+ else
+ found_so="$additional_libdir/lib$name.dll.a"
+ fi
+ if test -f "$additional_libdir/lib$name.la"; then
+ found_la="$additional_libdir/lib$name.la"
+ fi
+ else
+ if test -f "$additional_libdir/lib$name.$libext"; then
+ found_dir="$additional_libdir"
+ found_a="$additional_libdir/lib$name.$libext"
+ if test -f "$additional_libdir/lib$name.la"; then
+ found_la="$additional_libdir/lib$name.la"
+ fi
+ fi
+ fi
+ fi
+ if test "X$found_dir" = "X"; then
+ for x in $LDFLAGS $LTLIBICONV; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ case "$x" in
+ -L*)
+ dir=`echo "X$x" | sed -e 's/^X-L//'`
+ if test -n "$shlibext" \
+ && { test -f "$dir/lib$name.$shlibext" \
+ || { test "$shlibext" = dll \
+ && test -f "$dir/lib$name.dll.a"; }; }; then
+ found_dir="$dir"
+ if test -f "$dir/lib$name.$shlibext"; then
+ found_so="$dir/lib$name.$shlibext"
+ else
+ found_so="$dir/lib$name.dll.a"
+ fi
+ if test -f "$dir/lib$name.la"; then
+ found_la="$dir/lib$name.la"
+ fi
+ else
+ if test -f "$dir/lib$name.$libext"; then
+ found_dir="$dir"
+ found_a="$dir/lib$name.$libext"
+ if test -f "$dir/lib$name.la"; then
+ found_la="$dir/lib$name.la"
+ fi
+ fi
+ fi
+ ;;
+ esac
+ if test "X$found_dir" != "X"; then
+ break
+ fi
+ done
+ fi
+ if test "X$found_dir" != "X"; then
+ LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-L$found_dir -l$name"
+ if test "X$found_so" != "X"; then
+ if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/$acl_libdirstem"; then
+ LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so"
+ else
+ haveit=
+ for x in $ltrpathdirs; do
+ if test "X$x" = "X$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ ltrpathdirs="$ltrpathdirs $found_dir"
+ fi
+ if test "$hardcode_direct" = yes; then
+ LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so"
+ else
+ if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then
+ LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so"
+ haveit=
+ for x in $rpathdirs; do
+ if test "X$x" = "X$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ rpathdirs="$rpathdirs $found_dir"
+ fi
+ else
+ haveit=
+ for x in $LDFLAGS $LIBICONV; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-L$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ LIBICONV="${LIBICONV}${LIBICONV:+ }-L$found_dir"
+ fi
+ if test "$hardcode_minus_L" != no; then
+ LIBICONV="${LIBICONV}${LIBICONV:+ }$found_so"
+ else
+ LIBICONV="${LIBICONV}${LIBICONV:+ }-l$name"
+ fi
+ fi
+ fi
+ fi
+ else
+ if test "X$found_a" != "X"; then
+ LIBICONV="${LIBICONV}${LIBICONV:+ }$found_a"
+ else
+ LIBICONV="${LIBICONV}${LIBICONV:+ }-L$found_dir -l$name"
+ fi
+ fi
+ additional_includedir=
+ case "$found_dir" in
+ */$acl_libdirstem | */$acl_libdirstem/)
+ basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem/"'*$,,'`
+ additional_includedir="$basedir/include"
+ ;;
+ esac
+ if test "X$additional_includedir" != "X"; then
+ if test "X$additional_includedir" != "X/usr/include"; then
+ haveit=
+ if test "X$additional_includedir" = "X/usr/local/include"; then
+ if test -n "$GCC"; then
+ case $host_os in
+ linux* | gnu* | k*bsd*-gnu) haveit=yes;;
+ esac
+ fi
+ fi
+ if test -z "$haveit"; then
+ for x in $CPPFLAGS $INCICONV; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-I$additional_includedir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_includedir"; then
+ INCICONV="${INCICONV}${INCICONV:+ }-I$additional_includedir"
+ fi
+ fi
+ fi
+ fi
+ fi
+ if test -n "$found_la"; then
+ save_libdir="$libdir"
+ case "$found_la" in
+ */* | *\\*) . "$found_la" ;;
+ *) . "./$found_la" ;;
+ esac
+ libdir="$save_libdir"
+ for dep in $dependency_libs; do
+ case "$dep" in
+ -L*)
+ additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'`
+ if test "X$additional_libdir" != "X/usr/$acl_libdirstem"; then
+ haveit=
+ if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem"; then
+ if test -n "$GCC"; then
+ case $host_os in
+ linux* | gnu* | k*bsd*-gnu) haveit=yes;;
+ esac
+ fi
+ fi
+ if test -z "$haveit"; then
+ haveit=
+ for x in $LDFLAGS $LIBICONV; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-L$additional_libdir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_libdir"; then
+ LIBICONV="${LIBICONV}${LIBICONV:+ }-L$additional_libdir"
+ fi
+ fi
+ haveit=
+ for x in $LDFLAGS $LTLIBICONV; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-L$additional_libdir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_libdir"; then
+ LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-L$additional_libdir"
+ fi
+ fi
+ fi
+ fi
+ ;;
+ -R*)
+ dir=`echo "X$dep" | sed -e 's/^X-R//'`
+ if test "$enable_rpath" != no; then
+ haveit=
+ for x in $rpathdirs; do
+ if test "X$x" = "X$dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ rpathdirs="$rpathdirs $dir"
+ fi
+ haveit=
+ for x in $ltrpathdirs; do
+ if test "X$x" = "X$dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ ltrpathdirs="$ltrpathdirs $dir"
+ fi
+ fi
+ ;;
+ -l*)
+ names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'`
+ ;;
+ *.la)
+ names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'`
+ ;;
+ *)
+ LIBICONV="${LIBICONV}${LIBICONV:+ }$dep"
+ LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }$dep"
+ ;;
+ esac
+ done
+ fi
+ else
+ LIBICONV="${LIBICONV}${LIBICONV:+ }-l$name"
+ LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-l$name"
+ fi
+ fi
+ fi
+ done
+ done
+ if test "X$rpathdirs" != "X"; then
+ if test -n "$hardcode_libdir_separator"; then
+ alldirs=
+ for found_dir in $rpathdirs; do
+ alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$found_dir"
+ done
+ acl_save_libdir="$libdir"
+ libdir="$alldirs"
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ libdir="$acl_save_libdir"
+ LIBICONV="${LIBICONV}${LIBICONV:+ }$flag"
+ else
+ for found_dir in $rpathdirs; do
+ acl_save_libdir="$libdir"
+ libdir="$found_dir"
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ libdir="$acl_save_libdir"
+ LIBICONV="${LIBICONV}${LIBICONV:+ }$flag"
+ done
+ fi
+ fi
+ if test "X$ltrpathdirs" != "X"; then
+ for found_dir in $ltrpathdirs; do
+ LTLIBICONV="${LTLIBICONV}${LTLIBICONV:+ }-R$found_dir"
+ done
+ fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for CFPreferencesCopyAppValue" >&5
+$as_echo_n "checking for CFPreferencesCopyAppValue... " >&6; }
+if ${gt_cv_func_CFPreferencesCopyAppValue+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ gt_save_LIBS="$LIBS"
+ LIBS="$LIBS -Wl,-framework -Wl,CoreFoundation"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <CoreFoundation/CFPreferences.h>
+int
+main ()
+{
+CFPreferencesCopyAppValue(NULL, NULL)
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ gt_cv_func_CFPreferencesCopyAppValue=yes
+else
+ gt_cv_func_CFPreferencesCopyAppValue=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LIBS="$gt_save_LIBS"
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gt_cv_func_CFPreferencesCopyAppValue" >&5
+$as_echo "$gt_cv_func_CFPreferencesCopyAppValue" >&6; }
+ if test $gt_cv_func_CFPreferencesCopyAppValue = yes; then
+
+$as_echo "#define HAVE_CFPREFERENCESCOPYAPPVALUE 1" >>confdefs.h
+
+ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for CFLocaleCopyCurrent" >&5
+$as_echo_n "checking for CFLocaleCopyCurrent... " >&6; }
+if ${gt_cv_func_CFLocaleCopyCurrent+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ gt_save_LIBS="$LIBS"
+ LIBS="$LIBS -Wl,-framework -Wl,CoreFoundation"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <CoreFoundation/CFLocale.h>
+int
+main ()
+{
+CFLocaleCopyCurrent();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ gt_cv_func_CFLocaleCopyCurrent=yes
+else
+ gt_cv_func_CFLocaleCopyCurrent=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LIBS="$gt_save_LIBS"
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gt_cv_func_CFLocaleCopyCurrent" >&5
+$as_echo "$gt_cv_func_CFLocaleCopyCurrent" >&6; }
+ if test $gt_cv_func_CFLocaleCopyCurrent = yes; then
+
+$as_echo "#define HAVE_CFLOCALECOPYCURRENT 1" >>confdefs.h
+
+ fi
+ INTL_MACOSX_LIBS=
+ if test $gt_cv_func_CFPreferencesCopyAppValue = yes || test $gt_cv_func_CFLocaleCopyCurrent = yes; then
+ INTL_MACOSX_LIBS="-Wl,-framework -Wl,CoreFoundation"
+ fi
+
+
+
+
+
+
+ LIBINTL=
+ LTLIBINTL=
+ POSUB=
+
+ case " $gt_needs " in
+ *" need-formatstring-macros "*) gt_api_version=3 ;;
+ *" need-ngettext "*) gt_api_version=2 ;;
+ *) gt_api_version=1 ;;
+ esac
+ gt_func_gnugettext_libc="gt_cv_func_gnugettext${gt_api_version}_libc"
+ gt_func_gnugettext_libintl="gt_cv_func_gnugettext${gt_api_version}_libintl"
+
+ if test "$USE_NLS" = "yes"; then
+ gt_use_preinstalled_gnugettext=no
+
+
+ if test $gt_api_version -ge 3; then
+ gt_revision_test_code='
+#ifndef __GNU_GETTEXT_SUPPORTED_REVISION
+#define __GNU_GETTEXT_SUPPORTED_REVISION(major) ((major) == 0 ? 0 : -1)
+#endif
+typedef int array [2 * (__GNU_GETTEXT_SUPPORTED_REVISION(0) >= 1) - 1];
+'
+ else
+ gt_revision_test_code=
+ fi
+ if test $gt_api_version -ge 2; then
+ gt_expression_test_code=' + * ngettext ("", "", 0)'
+ else
+ gt_expression_test_code=
+ fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU gettext in libc" >&5
+$as_echo_n "checking for GNU gettext in libc... " >&6; }
+if eval \${$gt_func_gnugettext_libc+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <libintl.h>
+$gt_revision_test_code
+extern int _nl_msg_cat_cntr;
+extern int *_nl_domain_bindings;
+int
+main ()
+{
+bindtextdomain ("", "");
+return * gettext ("")$gt_expression_test_code + _nl_msg_cat_cntr + *_nl_domain_bindings
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ eval "$gt_func_gnugettext_libc=yes"
+else
+ eval "$gt_func_gnugettext_libc=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+eval ac_res=\$$gt_func_gnugettext_libc
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+
+ if { eval "gt_val=\$$gt_func_gnugettext_libc"; test "$gt_val" != "yes"; }; then
+
+
+
+
+
+ am_save_CPPFLAGS="$CPPFLAGS"
+
+ for element in $INCICONV; do
+ haveit=
+ for x in $CPPFLAGS; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X$element"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }$element"
+ fi
+ done
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for iconv" >&5
+$as_echo_n "checking for iconv... " >&6; }
+if ${am_cv_func_iconv+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ am_cv_func_iconv="no, consider installing GNU libiconv"
+ am_cv_lib_iconv=no
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <iconv.h>
+int
+main ()
+{
+iconv_t cd = iconv_open("","");
+ iconv(cd,NULL,NULL,NULL,NULL);
+ iconv_close(cd);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ am_cv_func_iconv=yes
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ if test "$am_cv_func_iconv" != yes; then
+ am_save_LIBS="$LIBS"
+ LIBS="$LIBS $LIBICONV"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <iconv.h>
+int
+main ()
+{
+iconv_t cd = iconv_open("","");
+ iconv(cd,NULL,NULL,NULL,NULL);
+ iconv_close(cd);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ am_cv_lib_iconv=yes
+ am_cv_func_iconv=yes
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LIBS="$am_save_LIBS"
+ fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_func_iconv" >&5
+$as_echo "$am_cv_func_iconv" >&6; }
+ if test "$am_cv_func_iconv" = yes; then
+
+$as_echo "#define HAVE_ICONV 1" >>confdefs.h
+
+ fi
+ if test "$am_cv_lib_iconv" = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link with libiconv" >&5
+$as_echo_n "checking how to link with libiconv... " >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIBICONV" >&5
+$as_echo "$LIBICONV" >&6; }
+ else
+ CPPFLAGS="$am_save_CPPFLAGS"
+ LIBICONV=
+ LTLIBICONV=
+ fi
+
+
+
+
+
+
+
+ use_additional=yes
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+
+# Check whether --with-libintl-prefix was given.
+if test "${with_libintl_prefix+set}" = set; then :
+ withval=$with_libintl_prefix;
+ if test "X$withval" = "Xno"; then
+ use_additional=no
+ else
+ if test "X$withval" = "X"; then
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ else
+ additional_includedir="$withval/include"
+ additional_libdir="$withval/$acl_libdirstem"
+ fi
+ fi
+
+fi
+
+ LIBINTL=
+ LTLIBINTL=
+ INCINTL=
+ rpathdirs=
+ ltrpathdirs=
+ names_already_handled=
+ names_next_round='intl '
+ while test -n "$names_next_round"; do
+ names_this_round="$names_next_round"
+ names_next_round=
+ for name in $names_this_round; do
+ already_handled=
+ for n in $names_already_handled; do
+ if test "$n" = "$name"; then
+ already_handled=yes
+ break
+ fi
+ done
+ if test -z "$already_handled"; then
+ names_already_handled="$names_already_handled $name"
+ uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'`
+ eval value=\"\$HAVE_LIB$uppername\"
+ if test -n "$value"; then
+ if test "$value" = yes; then
+ eval value=\"\$LIB$uppername\"
+ test -z "$value" || LIBINTL="${LIBINTL}${LIBINTL:+ }$value"
+ eval value=\"\$LTLIB$uppername\"
+ test -z "$value" || LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }$value"
+ else
+ :
+ fi
+ else
+ found_dir=
+ found_la=
+ found_so=
+ found_a=
+ if test $use_additional = yes; then
+ if test -n "$shlibext" \
+ && { test -f "$additional_libdir/lib$name.$shlibext" \
+ || { test "$shlibext" = dll \
+ && test -f "$additional_libdir/lib$name.dll.a"; }; }; then
+ found_dir="$additional_libdir"
+ if test -f "$additional_libdir/lib$name.$shlibext"; then
+ found_so="$additional_libdir/lib$name.$shlibext"
+ else
+ found_so="$additional_libdir/lib$name.dll.a"
+ fi
+ if test -f "$additional_libdir/lib$name.la"; then
+ found_la="$additional_libdir/lib$name.la"
+ fi
+ else
+ if test -f "$additional_libdir/lib$name.$libext"; then
+ found_dir="$additional_libdir"
+ found_a="$additional_libdir/lib$name.$libext"
+ if test -f "$additional_libdir/lib$name.la"; then
+ found_la="$additional_libdir/lib$name.la"
+ fi
+ fi
+ fi
+ fi
+ if test "X$found_dir" = "X"; then
+ for x in $LDFLAGS $LTLIBINTL; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ case "$x" in
+ -L*)
+ dir=`echo "X$x" | sed -e 's/^X-L//'`
+ if test -n "$shlibext" \
+ && { test -f "$dir/lib$name.$shlibext" \
+ || { test "$shlibext" = dll \
+ && test -f "$dir/lib$name.dll.a"; }; }; then
+ found_dir="$dir"
+ if test -f "$dir/lib$name.$shlibext"; then
+ found_so="$dir/lib$name.$shlibext"
+ else
+ found_so="$dir/lib$name.dll.a"
+ fi
+ if test -f "$dir/lib$name.la"; then
+ found_la="$dir/lib$name.la"
+ fi
+ else
+ if test -f "$dir/lib$name.$libext"; then
+ found_dir="$dir"
+ found_a="$dir/lib$name.$libext"
+ if test -f "$dir/lib$name.la"; then
+ found_la="$dir/lib$name.la"
+ fi
+ fi
+ fi
+ ;;
+ esac
+ if test "X$found_dir" != "X"; then
+ break
+ fi
+ done
+ fi
+ if test "X$found_dir" != "X"; then
+ LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }-L$found_dir -l$name"
+ if test "X$found_so" != "X"; then
+ if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/$acl_libdirstem"; then
+ LIBINTL="${LIBINTL}${LIBINTL:+ }$found_so"
+ else
+ haveit=
+ for x in $ltrpathdirs; do
+ if test "X$x" = "X$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ ltrpathdirs="$ltrpathdirs $found_dir"
+ fi
+ if test "$hardcode_direct" = yes; then
+ LIBINTL="${LIBINTL}${LIBINTL:+ }$found_so"
+ else
+ if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then
+ LIBINTL="${LIBINTL}${LIBINTL:+ }$found_so"
+ haveit=
+ for x in $rpathdirs; do
+ if test "X$x" = "X$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ rpathdirs="$rpathdirs $found_dir"
+ fi
+ else
+ haveit=
+ for x in $LDFLAGS $LIBINTL; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-L$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ LIBINTL="${LIBINTL}${LIBINTL:+ }-L$found_dir"
+ fi
+ if test "$hardcode_minus_L" != no; then
+ LIBINTL="${LIBINTL}${LIBINTL:+ }$found_so"
+ else
+ LIBINTL="${LIBINTL}${LIBINTL:+ }-l$name"
+ fi
+ fi
+ fi
+ fi
+ else
+ if test "X$found_a" != "X"; then
+ LIBINTL="${LIBINTL}${LIBINTL:+ }$found_a"
+ else
+ LIBINTL="${LIBINTL}${LIBINTL:+ }-L$found_dir -l$name"
+ fi
+ fi
+ additional_includedir=
+ case "$found_dir" in
+ */$acl_libdirstem | */$acl_libdirstem/)
+ basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem/"'*$,,'`
+ additional_includedir="$basedir/include"
+ ;;
+ esac
+ if test "X$additional_includedir" != "X"; then
+ if test "X$additional_includedir" != "X/usr/include"; then
+ haveit=
+ if test "X$additional_includedir" = "X/usr/local/include"; then
+ if test -n "$GCC"; then
+ case $host_os in
+ linux* | gnu* | k*bsd*-gnu) haveit=yes;;
+ esac
+ fi
+ fi
+ if test -z "$haveit"; then
+ for x in $CPPFLAGS $INCINTL; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-I$additional_includedir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_includedir"; then
+ INCINTL="${INCINTL}${INCINTL:+ }-I$additional_includedir"
+ fi
+ fi
+ fi
+ fi
+ fi
+ if test -n "$found_la"; then
+ save_libdir="$libdir"
+ case "$found_la" in
+ */* | *\\*) . "$found_la" ;;
+ *) . "./$found_la" ;;
+ esac
+ libdir="$save_libdir"
+ for dep in $dependency_libs; do
+ case "$dep" in
+ -L*)
+ additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'`
+ if test "X$additional_libdir" != "X/usr/$acl_libdirstem"; then
+ haveit=
+ if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem"; then
+ if test -n "$GCC"; then
+ case $host_os in
+ linux* | gnu* | k*bsd*-gnu) haveit=yes;;
+ esac
+ fi
+ fi
+ if test -z "$haveit"; then
+ haveit=
+ for x in $LDFLAGS $LIBINTL; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-L$additional_libdir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_libdir"; then
+ LIBINTL="${LIBINTL}${LIBINTL:+ }-L$additional_libdir"
+ fi
+ fi
+ haveit=
+ for x in $LDFLAGS $LTLIBINTL; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-L$additional_libdir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_libdir"; then
+ LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }-L$additional_libdir"
+ fi
+ fi
+ fi
+ fi
+ ;;
+ -R*)
+ dir=`echo "X$dep" | sed -e 's/^X-R//'`
+ if test "$enable_rpath" != no; then
+ haveit=
+ for x in $rpathdirs; do
+ if test "X$x" = "X$dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ rpathdirs="$rpathdirs $dir"
+ fi
+ haveit=
+ for x in $ltrpathdirs; do
+ if test "X$x" = "X$dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ ltrpathdirs="$ltrpathdirs $dir"
+ fi
+ fi
+ ;;
+ -l*)
+ names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'`
+ ;;
+ *.la)
+ names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'`
+ ;;
+ *)
+ LIBINTL="${LIBINTL}${LIBINTL:+ }$dep"
+ LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }$dep"
+ ;;
+ esac
+ done
+ fi
+ else
+ LIBINTL="${LIBINTL}${LIBINTL:+ }-l$name"
+ LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }-l$name"
+ fi
+ fi
+ fi
+ done
+ done
+ if test "X$rpathdirs" != "X"; then
+ if test -n "$hardcode_libdir_separator"; then
+ alldirs=
+ for found_dir in $rpathdirs; do
+ alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$found_dir"
+ done
+ acl_save_libdir="$libdir"
+ libdir="$alldirs"
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ libdir="$acl_save_libdir"
+ LIBINTL="${LIBINTL}${LIBINTL:+ }$flag"
+ else
+ for found_dir in $rpathdirs; do
+ acl_save_libdir="$libdir"
+ libdir="$found_dir"
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ libdir="$acl_save_libdir"
+ LIBINTL="${LIBINTL}${LIBINTL:+ }$flag"
+ done
+ fi
+ fi
+ if test "X$ltrpathdirs" != "X"; then
+ for found_dir in $ltrpathdirs; do
+ LTLIBINTL="${LTLIBINTL}${LTLIBINTL:+ }-R$found_dir"
+ done
+ fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU gettext in libintl" >&5
+$as_echo_n "checking for GNU gettext in libintl... " >&6; }
+if eval \${$gt_func_gnugettext_libintl+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ gt_save_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $INCINTL"
+ gt_save_LIBS="$LIBS"
+ LIBS="$LIBS $LIBINTL"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <libintl.h>
+$gt_revision_test_code
+extern int _nl_msg_cat_cntr;
+extern
+#ifdef __cplusplus
+"C"
+#endif
+const char *_nl_expand_alias (const char *);
+int
+main ()
+{
+bindtextdomain ("", "");
+return * gettext ("")$gt_expression_test_code + _nl_msg_cat_cntr + *_nl_expand_alias ("")
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ eval "$gt_func_gnugettext_libintl=yes"
+else
+ eval "$gt_func_gnugettext_libintl=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" != yes; } && test -n "$LIBICONV"; then
+ LIBS="$LIBS $LIBICONV"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <libintl.h>
+$gt_revision_test_code
+extern int _nl_msg_cat_cntr;
+extern
+#ifdef __cplusplus
+"C"
+#endif
+const char *_nl_expand_alias (const char *);
+int
+main ()
+{
+bindtextdomain ("", "");
+return * gettext ("")$gt_expression_test_code + _nl_msg_cat_cntr + *_nl_expand_alias ("")
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ LIBINTL="$LIBINTL $LIBICONV"
+ LTLIBINTL="$LTLIBINTL $LTLIBICONV"
+ eval "$gt_func_gnugettext_libintl=yes"
+
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ fi
+ CPPFLAGS="$gt_save_CPPFLAGS"
+ LIBS="$gt_save_LIBS"
+fi
+eval ac_res=\$$gt_func_gnugettext_libintl
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ fi
+
+ if { eval "gt_val=\$$gt_func_gnugettext_libc"; test "$gt_val" = "yes"; } \
+ || { { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = "yes"; } \
+ && test "$PACKAGE" != gettext-runtime \
+ && test "$PACKAGE" != gettext-tools; }; then
+ gt_use_preinstalled_gnugettext=yes
+ else
+ LIBINTL=
+ LTLIBINTL=
+ INCINTL=
+ fi
+
+
+
+ if test -n "$INTL_MACOSX_LIBS"; then
+ if test "$gt_use_preinstalled_gnugettext" = "yes" \
+ || test "$nls_cv_use_gnu_gettext" = "yes"; then
+ LIBINTL="$LIBINTL $INTL_MACOSX_LIBS"
+ LTLIBINTL="$LTLIBINTL $INTL_MACOSX_LIBS"
+ fi
+ fi
+
+ if test "$gt_use_preinstalled_gnugettext" = "yes" \
+ || test "$nls_cv_use_gnu_gettext" = "yes"; then
+
+$as_echo "#define ENABLE_NLS 1" >>confdefs.h
+
+ else
+ USE_NLS=no
+ fi
+ fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use NLS" >&5
+$as_echo_n "checking whether to use NLS... " >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $USE_NLS" >&5
+$as_echo "$USE_NLS" >&6; }
+ if test "$USE_NLS" = "yes"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking where the gettext function comes from" >&5
+$as_echo_n "checking where the gettext function comes from... " >&6; }
+ if test "$gt_use_preinstalled_gnugettext" = "yes"; then
+ if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = "yes"; }; then
+ gt_source="external libintl"
+ else
+ gt_source="libc"
+ fi
+ else
+ gt_source="included intl directory"
+ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gt_source" >&5
+$as_echo "$gt_source" >&6; }
+ fi
+
+ if test "$USE_NLS" = "yes"; then
+
+ if test "$gt_use_preinstalled_gnugettext" = "yes"; then
+ if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = "yes"; }; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link with libintl" >&5
+$as_echo_n "checking how to link with libintl... " >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIBINTL" >&5
+$as_echo "$LIBINTL" >&6; }
+
+ for element in $INCINTL; do
+ haveit=
+ for x in $CPPFLAGS; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X$element"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }$element"
+ fi
+ done
+
+ fi
+
+
+$as_echo "#define HAVE_GETTEXT 1" >>confdefs.h
+
+
+$as_echo "#define HAVE_DCGETTEXT 1" >>confdefs.h
+
+ fi
+
+ POSUB=po
+ fi
+
+
+
+ INTLLIBS="$LIBINTL"
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: dmalloc enabled" >&5
+$as_echo_n "checking option: dmalloc enabled... " >&6; }
+# Check whether --enable-dmalloc was given.
+if test "${enable_dmalloc+set}" = set; then :
+ enableval=$enable_dmalloc;
+fi
+
+if test x$enable_dmalloc = "xyes" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+# Check whether --with-dmalloc-dir was given.
+if test "${with_dmalloc_dir+set}" = set; then :
+ withval=$with_dmalloc_dir;
+ if test "x$withval" != "xno" ; then
+ enable_dmalloc = "yes"
+ CPPFLAGS="$CPPCFLAGS -I${withval}"
+ LDFLAGS="$LDFLAGS -L${withval}"
+ fi
+
+fi
+
+
+if test x$enable_dmalloc = "xyes" ; then
+
+$as_echo "#define ENABLE_DMALLOC 1" >>confdefs.h
+
+fi
+
+localedir="\${datadir}/locale"
+
+# Check whether --with-localedir was given.
+if test "${with_localedir+set}" = set; then :
+ withval=$with_localedir;
+ case $withval in
+ yes)
+ ;;
+ no)
+ ;;
+ *)
+ localedir=$withval
+ ;;
+ esac
+
+fi
+
+localedir="$localedir"
+
+
+# Setup OS-Specific features
+case "$host" in
+ *darwin*)
+ # Check whether --enable-osx-universal-binaries was given.
+if test "${enable_osx_universal_binaries+set}" = set; then :
+ enableval=$enable_osx_universal_binaries;
+fi
+
+ if test "x$enable_osx_universal_binaries" = "xyes" ; then
+ if test "x$enable_dependency_tracking" != xno ; then
+ as_fn_error $? "--enable-osx-universal-binary requires --disable-dependency-tracking.
+Please re-run configure with these options:
+ --disable-dependency-tracking --enable-osx-universal-binary" "$LINENO" 5
+ fi
+ if test -d /Developer/SDKs/MacOSX10.5.sdk ; then
+ alpine_sysroot=/Developer/SDKs/MacOSX10.5.sdk
+ elif test -d /Developer/SDKs/MacOSX10.4u.sdk ; then
+ alpine_sysroot=/Developer/SDKs/MacOSX10.4u.sdk
+ else
+ as_fn_error $? "No suitable MacOSX SDK found. Make sure Xcode tools are installed" "$LINENO" 5
+ fi
+ ub_cflags="-isysroot $alpine_sysroot -arch ppc -arch i386"
+ ub_ldflags="-Wl,-syslibroot,$alpine_sysroot -arch ppc -arch i386"
+ AM_CFLAGS="$AM_CFLAGS $ub_cflags"
+ AM_LDFLAGS="$AM_LDFLAGS $ub_ldflags"
+ alpine_c_client_cflags="$alpine_c_client_cflags $ub_cflags"
+ alpine_c_client_ldflags="$alpine_c_client_ldflags $ub_ldflags"
+ fi
+ ;;
+esac
+
+
+# Check whether --with-include-path was given.
+if test "${with_include_path+set}" = set; then :
+ withval=$with_include_path;
+ case $withval in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+ new_cppflags="-I`echo ${withval} | ${SED} 's/:/ -I/g'`"
+ CPPFLAGS="$CPPFLAGS ${new_cppflags}"
+ alpine_c_client_cflags="$alpine_c_client_cflags ${new_cppflags}"
+ ;;
+ esac
+
+fi
+
+
+
+# Check whether --with-lib-path was given.
+if test "${with_lib_path+set}" = set; then :
+ withval=$with_lib_path;
+ case $withval in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+ new_ldflags="-L`echo ${withval} | ${SED} 's/:/ -L/g'`"
+ LDFLAGS="$LDFLAGS $new_ldflags"
+ alpine_c_client_ldflags="$alpine_c_client_ldflags ${new_ldflags}"
+ ;;
+ esac
+
+fi
+
+
+
+# Check whether --with-pubcookie was given.
+if test "${with_pubcookie+set}" = set; then :
+ withval=$with_pubcookie;
+ if test "x$withval" != "xno" ; then
+ WEB_PUBCOOKIE_BUILD=web/src/pubcookie
+ fi
+
+fi
+
+
+
+
+# Check whether --with-web-bin was given.
+if test "${with_web_bin+set}" = set; then :
+ withval=$with_web_bin;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+ WEB_BINDIR=$withval
+ ;;
+ esac
+
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: debugging is enabled" >&5
+$as_echo_n "checking option: debugging is enabled... " >&6; }
+# Check whether --enable-debug was given.
+if test "${enable_debug+set}" = set; then :
+ enableval=$enable_debug;
+fi
+
+if test x$enable_debug != "xno" ; then
+ AM_CFLAGS="$AM_CFLAGS -g"
+
+$as_echo "#define DEBUG 1" >>confdefs.h
+
+
+$as_echo "#define DEBUGJOURNAL 1" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: optimization is enabled" >&5
+$as_echo_n "checking option: optimization is enabled... " >&6; }
+# Check whether --enable-optimization was given.
+if test "${enable_optimization+set}" = set; then :
+ enableval=$enable_optimization;
+fi
+
+if test x$enable_optimization != "xno" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ CFLAGS="`echo $AM_CFLAGS | ${SED} 's/-O2//'`"
+ alpine_c_client_gccoptlevel="-O0"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: mouse support enabled" >&5
+$as_echo_n "checking option: mouse support enabled... " >&6; }
+# Check whether --enable-mouse was given.
+if test "${enable_mouse+set}" = set; then :
+ enableval=$enable_mouse;
+fi
+
+if test x$enable_mouse != "xno" ; then
+
+$as_echo "#define MOUSE /**/" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: quotas enabled" >&5
+$as_echo_n "checking option: quotas enabled... " >&6; }
+# Check whether --enable-quotas was given.
+if test "${enable_quotas+set}" = set; then :
+ enableval=$enable_quotas;
+fi
+
+if test x$enable_quotas = "xyes" ; then
+
+$as_echo "#define USE_QUOTAS /**/" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: From changing enabled" >&5
+$as_echo_n "checking option: From changing enabled... " >&6; }
+# Check whether --enable-from_changing was given.
+if test "${enable_from_changing+set}" = set; then :
+ enableval=$enable_from_changing;
+fi
+
+if test x$enable_from_changing != "xno" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+
+$as_echo "#define NEVER_ALLOW_CHANGING_FROM /**/" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: background post enabled" >&5
+$as_echo_n "checking option: background post enabled... " >&6; }
+# Check whether --enable-background-post was given.
+if test "${enable_background_post+set}" = set; then :
+ enableval=$enable_background_post;
+fi
+
+if test x$enable_background_post != "xno" ; then
+
+$as_echo "#define BACKGROUND_POST /**/" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: keyboard lock enabled" >&5
+$as_echo_n "checking option: keyboard lock enabled... " >&6; }
+# Check whether --enable-keyboard-lock was given.
+if test "${enable_keyboard_lock+set}" = set; then :
+ enableval=$enable_keyboard_lock;
+fi
+
+if test x$enable_keyboard_lock != "xno" ; then
+
+$as_echo "#define KEYBOARD_LOCK /**/" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking option: from encoding enabled" >&5
+$as_echo_n "checking option: from encoding enabled... " >&6; }
+# Check whether --enable-from-encoding was given.
+if test "${enable_from_encoding+set}" = set; then :
+ enableval=$enable_from_encoding;
+fi
+
+if test x$enable_from_encoding = "xyes" ; then
+
+$as_echo "#define ENCODE_FROMS /**/" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+# Check whether --with-smtp-msa was given.
+if test "${with_smtp_msa+set}" = set; then :
+ withval=$with_smtp_msa;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ # Extract the first word of "sendmail", so it can be a program name with args.
+set dummy sendmail; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_SENDMAIL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $SENDMAIL in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_SENDMAIL="$SENDMAIL" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_dummy="$PATH:/usr/sbin:/usr/lib"
+for as_dir in $as_dummy
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_SENDMAIL="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_SENDMAIL" && ac_cv_path_SENDMAIL=""""
+ ;;
+esac
+fi
+SENDMAIL=$ac_cv_path_SENDMAIL
+if test -n "$SENDMAIL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SENDMAIL" >&5
+$as_echo "$SENDMAIL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ ;;
+ *)
+ SENDMAIL=$withval
+ ;;
+ esac
+
+else
+
+ # Extract the first word of "sendmail", so it can be a program name with args.
+set dummy sendmail; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_SENDMAIL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $SENDMAIL in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_SENDMAIL="$SENDMAIL" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_dummy="$PATH:/usr/sbin:/usr/lib"
+for as_dir in $as_dummy
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_SENDMAIL="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_SENDMAIL" && ac_cv_path_SENDMAIL=""""
+ ;;
+esac
+fi
+SENDMAIL=$ac_cv_path_SENDMAIL
+if test -n "$SENDMAIL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SENDMAIL" >&5
+$as_echo "$SENDMAIL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+fi
+
+if test -n "$SENDMAIL" ; then
+
+cat >>confdefs.h <<_ACEOF
+#define SENDMAIL "$SENDMAIL"
+_ACEOF
+
+fi
+
+smtp_msa_flags="-bs -odb -oem"
+
+# Check whether --with-smtp-msa-flags was given.
+if test "${with_smtp_msa_flags+set}" = set; then :
+ withval=$with_smtp_msa_flags;
+ if test "x$withval" != "xno" ; then
+ smtp_msa_flags=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define SENDMAILFLAGS "$smtp_msa_flags"
+_ACEOF
+
+
+npa="inews"
+
+# Check whether --with-npa was given.
+if test "${with_npa+set}" = set; then :
+ withval=$with_npa;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ # Extract the first word of "inews", so it can be a program name with args.
+set dummy inews; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_NPA_PROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $NPA_PROG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_NPA_PROG="$NPA_PROG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_dummy="$PATH:/usr/sbin:/usr/lib"
+for as_dir in $as_dummy
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_NPA_PROG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_NPA_PROG" && ac_cv_path_NPA_PROG=""""
+ ;;
+esac
+fi
+NPA_PROG=$ac_cv_path_NPA_PROG
+if test -n "$NPA_PROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NPA_PROG" >&5
+$as_echo "$NPA_PROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ ;;
+ *)
+ NPA_PROG=$withval
+ ;;
+ esac
+
+else
+
+ # Extract the first word of "inews", so it can be a program name with args.
+set dummy inews; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_NPA_PROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $NPA_PROG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_NPA_PROG="$NPA_PROG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_dummy="$PATH:/usr/sbin:/usr/lib"
+for as_dir in $as_dummy
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_NPA_PROG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_NPA_PROG" && ac_cv_path_NPA_PROG=""""
+ ;;
+esac
+fi
+NPA_PROG=$ac_cv_path_NPA_PROG
+if test -n "$NPA_PROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NPA_PROG" >&5
+$as_echo "$NPA_PROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+fi
+
+
+npa_flags="-h"
+
+# Check whether --with-npa-flags was given.
+if test "${with_npa_flags+set}" = set; then :
+ withval=$with_npa_flags;
+ if test "x$withval" != "xno" ; then
+ npa_flags=$withval
+ fi
+
+fi
+
+if test -n "$NPA_PROG" ; then
+
+cat >>confdefs.h <<_ACEOF
+#define SENDNEWS "$NPA_PROG $npa_flags"
+_ACEOF
+
+fi
+
+
+# Check whether --with-password-prog was given.
+if test "${with_password_prog+set}" = set; then :
+ withval=$with_password_prog;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ # Extract the first word of "passwd", so it can be a program name with args.
+set dummy passwd; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_PWPROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $PWPROG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_PWPROG="$PWPROG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_dummy="$PATH:/usr/sbin:/usr/lib"
+for as_dir in $as_dummy
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_PWPROG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_PWPROG" && ac_cv_path_PWPROG=""""
+ ;;
+esac
+fi
+PWPROG=$ac_cv_path_PWPROG
+if test -n "$PWPROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PWPROG" >&5
+$as_echo "$PWPROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ ;;
+ *)
+ # Extract the first word of "$withval", so it can be a program name with args.
+set dummy $withval; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_PWPROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $PWPROG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_PWPROG="$PWPROG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_dummy="$PATH:/usr/sbin:/usr/lib"
+for as_dir in $as_dummy
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_PWPROG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_PWPROG" && ac_cv_path_PWPROG=""""
+ ;;
+esac
+fi
+PWPROG=$ac_cv_path_PWPROG
+if test -n "$PWPROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PWPROG" >&5
+$as_echo "$PWPROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ ;;
+ esac
+
+else
+
+ # Extract the first word of "passwd", so it can be a program name with args.
+set dummy passwd; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_PWPROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $PWPROG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_PWPROG="$PWPROG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_dummy="$PATH:/usr/sbin:/usr/lib"
+for as_dir in $as_dummy
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_PWPROG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_PWPROG" && ac_cv_path_PWPROG=""""
+ ;;
+esac
+fi
+PWPROG=$ac_cv_path_PWPROG
+if test -n "$PWPROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PWPROG" >&5
+$as_echo "$PWPROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+fi
+
+if test -n "$PWPROG" ; then
+
+cat >>confdefs.h <<_ACEOF
+#define PASSWD_PROG "$PWPROG"
+_ACEOF
+
+fi
+
+
+# Check whether --with-simple-spellcheck was given.
+if test "${with_simple_spellcheck+set}" = set; then :
+ withval=$with_simple_spellcheck;
+ if test "x$withval" != "xno" ; then
+ SPELLPROG=$withval
+ fi
+
+else
+
+ # Extract the first word of "hunspell", so it can be a program name with args.
+set dummy hunspell; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_SPELLPROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$SPELLPROG"; then
+ ac_cv_prog_SPELLPROG="$SPELLPROG" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_SPELLPROG="hunspell"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+SPELLPROG=$ac_cv_prog_SPELLPROG
+if test -n "$SPELLPROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SPELLPROG" >&5
+$as_echo "$SPELLPROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test -z "$SPELLPROG" ; then
+ # Extract the first word of "aspell", so it can be a program name with args.
+set dummy aspell; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_SPELLPROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$SPELLPROG"; then
+ ac_cv_prog_SPELLPROG="$SPELLPROG" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_SPELLPROG="aspell"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+SPELLPROG=$ac_cv_prog_SPELLPROG
+if test -n "$SPELLPROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SPELLPROG" >&5
+$as_echo "$SPELLPROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test -z "$SPELLPROG" ; then
+ # Extract the first word of "ispell", so it can be a program name with args.
+set dummy ispell; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_SPELLPROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$SPELLPROG"; then
+ ac_cv_prog_SPELLPROG="$SPELLPROG" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_SPELLPROG="ispell"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+SPELLPROG=$ac_cv_prog_SPELLPROG
+if test -n "$SPELLPROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SPELLPROG" >&5
+$as_echo "$SPELLPROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test -z "$SPELLPROG" ; then
+ SPELLPROG="spell"
+ fi
+ fi
+ fi
+
+fi
+
+
+if test "x$SPELLPROG" != "xno" ; then
+ # Extract the first word of "$SPELLPROG", so it can be a program name with args.
+set dummy $SPELLPROG; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_alpine_simple_spellcheck+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $alpine_simple_spellcheck in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_alpine_simple_spellcheck="$alpine_simple_spellcheck" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_alpine_simple_spellcheck="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+alpine_simple_spellcheck=$ac_cv_path_alpine_simple_spellcheck
+if test -n "$alpine_simple_spellcheck"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $alpine_simple_spellcheck" >&5
+$as_echo "$alpine_simple_spellcheck" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test -n "$alpine_simple_spellcheck" ; then
+ case "$SPELLPROG" in
+ hunspell)
+ alpine_simple_spellcheck="$alpine_simple_spellcheck -l"
+ ;;
+ aspell)
+ alpine_simple_spellcheck="$alpine_simple_spellcheck --dont-backup --mode=email list"
+ ;;
+ ispell)
+ alpine_simple_spellcheck="$alpine_simple_spellcheck -l"
+ ;;
+ *)
+ ;;
+ esac
+ fi
+fi
+
+
+# Check whether --with-interactive-spellcheck was given.
+if test "${with_interactive_spellcheck+set}" = set; then :
+ withval=$with_interactive_spellcheck;
+ if test "x$withval" != "xno" ; then
+ ISPELLPROG=$withval
+ fi
+
+else
+
+ # Extract the first word of "hunspell", so it can be a program name with args.
+set dummy hunspell; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ISPELLPROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ISPELLPROG"; then
+ ac_cv_prog_ISPELLPROG="$ISPELLPROG" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ISPELLPROG="hunspell"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ISPELLPROG=$ac_cv_prog_ISPELLPROG
+if test -n "$ISPELLPROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ISPELLPROG" >&5
+$as_echo "$ISPELLPROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test -z "$ISPELLPROG" ; then
+ # Extract the first word of "aspell", so it can be a program name with args.
+set dummy aspell; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ISPELLPROG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ISPELLPROG"; then
+ ac_cv_prog_ISPELLPROG="$ISPELLPROG" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_ISPELLPROG="aspell"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ISPELLPROG=$ac_cv_prog_ISPELLPROG
+if test -n "$ISPELLPROG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ISPELLPROG" >&5
+$as_echo "$ISPELLPROG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test -z "$SPELLPROG" ; then
+ ISPELLPROG="ispell"
+ fi
+ fi
+
+fi
+
+
+if test "x$ISPELLPROG" != "xno" ; then
+ # Extract the first word of "$ISPELLPROG", so it can be a program name with args.
+set dummy $ISPELLPROG; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_alpine_interactive_spellcheck+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $alpine_interactive_spellcheck in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_alpine_interactive_spellcheck="$alpine_interactive_spellcheck" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_path_alpine_interactive_spellcheck="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+alpine_interactive_spellcheck=$ac_cv_path_alpine_interactive_spellcheck
+if test -n "$alpine_interactive_spellcheck"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $alpine_interactive_spellcheck" >&5
+$as_echo "$alpine_interactive_spellcheck" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test -n "$alpine_interactive_spellcheck" ; then
+ case "$ISPELLPROG" in
+ aspell)
+ alpine_interactive_spellcheck="$alpine_interactive_spellcheck --dont-backup --mode=email check"
+ ;;
+ *)
+ ;;
+ esac
+ fi
+fi
+
+if test -n "$alpine_interactive_spellcheck" ; then
+
+cat >>confdefs.h <<_ACEOF
+#define DF_VAR_SPELLER "$alpine_interactive_spellcheck"
+_ACEOF
+
+fi
+
+if test -z "$alpine_simple_spellcheck" -a -n "$alpine_interactive_spellcheck" ; then
+ alpine_simple_spellcheck=test
+fi
+
+cat >>confdefs.h <<_ACEOF
+#define SPELLER "$alpine_simple_spellcheck"
+_ACEOF
+
+
+case "$prefix" in
+ NONE) dpv=/usr/local/lib/pine.conf ;;
+ *) dpv=${prefix}/lib/pine.conf ;;
+esac
+
+# Check whether --with-system-pinerc was given.
+if test "${with_system_pinerc+set}" = set; then :
+ withval=$with_system_pinerc;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+ dpv=$withval
+ ;;
+ esac
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define SYSTEM_PINERC "$dpv"
+_ACEOF
+
+
+case "$prefix" in
+ NONE) dpv=/usr/local/lib/pine.conf.fixed ;;
+ *) dpv=${prefix}/lib/pine.conf.fixed ;;
+esac
+
+# Check whether --with-system-fixed-pinerc was given.
+if test "${with_system_fixed_pinerc+set}" = set; then :
+ withval=$with_system_fixed_pinerc;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+ dpv=$withval
+ ;;
+ esac
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define SYSTEM_PINERC_FIXED "$dpv"
+_ACEOF
+
+
+
+
+
+
+
+ dpv=150
+
+# Check whether --with-mailcheck-interval was given.
+if test "${with_mailcheck_interval+set}" = set; then :
+ withval=$with_mailcheck_interval;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DF_MAILCHECK "$dpv"
+_ACEOF
+
+
+
+ dpv=420
+
+# Check whether --with-checkpoint-interval was given.
+if test "${with_checkpoint_interval+set}" = set; then :
+ withval=$with_checkpoint_interval;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define CHECK_POINT_TIME $dpv
+_ACEOF
+
+
+
+ dpv=12
+
+# Check whether --with-checkpoint-frequency was given.
+if test "${with_checkpoint_frequency+set}" = set; then :
+ withval=$with_checkpoint_frequency;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define CHECK_POINT_FREQ $dpv
+_ACEOF
+
+
+
+ dpv=24
+
+# Check whether --with-display-rows was given.
+if test "${with_display_rows+set}" = set; then :
+ withval=$with_display_rows;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DEFAULT_LINES_ON_TERMINAL $dpv
+_ACEOF
+
+
+
+ dpv=80
+
+# Check whether --with-display-columns was given.
+if test "${with_display_columns+set}" = set; then :
+ withval=$with_display_columns;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DEFAULT_COLUMNS_ON_TERMINAL $dpv
+_ACEOF
+
+
+
+ dpv=200
+
+# Check whether --with-max-display-rows was given.
+if test "${with_max_display_rows+set}" = set; then :
+ withval=$with_max_display_rows;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define MAX_SCREEN_ROWS $dpv
+_ACEOF
+
+
+
+ dpv=500
+
+# Check whether --with-max-display-columns was given.
+if test "${with_max_display_columns+set}" = set; then :
+ withval=$with_max_display_columns;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define MAX_SCREEN_COLS $dpv
+_ACEOF
+
+
+
+ dpv=74
+
+# Check whether --with-fill-column was given.
+if test "${with_fill_column+set}" = set; then :
+ withval=$with_fill_column;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DF_FILLCOL "$dpv"
+_ACEOF
+
+
+
+ dpv=80
+
+# Check whether --with-max_fill-column was given.
+if test "${with_max_fill_column+set}" = set; then :
+ withval=$with_max_fill_column;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define MAX_FILLCOL $dpv
+_ACEOF
+
+
+
+ dpv=2
+
+# Check whether --with-debug-level was given.
+if test "${with_debug_level+set}" = set; then :
+ withval=$with_debug_level;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DEFAULT_DEBUG $dpv
+_ACEOF
+
+
+
+ dpv=4
+
+# Check whether --with-debug-files was given.
+if test "${with_debug_files+set}" = set; then :
+ withval=$with_debug_files;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define NUMDEBUGFILES $dpv
+_ACEOF
+
+
+
+ dpv=.pine-debug
+
+# Check whether --with-debug-file was given.
+if test "${with_debug_file+set}" = set; then :
+ withval=$with_debug_file;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DEBUGFILE "$dpv"
+_ACEOF
+
+
+
+ dpv="\$Forwarded"
+
+# Check whether --with-forwarded-keyword was given.
+if test "${with_forwarded_keyword+set}" = set; then :
+ withval=$with_forwarded_keyword;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define FORWARDED_FLAG "$dpv"
+_ACEOF
+
+
+
+ dpv=2
+
+# Check whether --with-display-overlap was given.
+if test "${with_display_overlap+set}" = set; then :
+ withval=$with_display_overlap;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DF_OVERLAP "$dpv"
+_ACEOF
+
+
+
+ dpv=0
+
+# Check whether --with-display-margin was given.
+if test "${with_display_margin+set}" = set; then :
+ withval=$with_display_margin;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DF_MARGIN "$dpv"
+_ACEOF
+
+
+
+ dpv=sent-mail
+
+# Check whether --with-default-fcc was given.
+if test "${with_default_fcc+set}" = set; then :
+ withval=$with_default_fcc;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DF_DEFAULT_FCC "$dpv"
+_ACEOF
+
+
+
+ dpv=saved-messages
+
+# Check whether --with-default-save-folder was given.
+if test "${with_default_save_folder+set}" = set; then :
+ withval=$with_default_save_folder;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DEFAULT_SAVE "$dpv"
+_ACEOF
+
+
+
+ dpv=postponed-mail
+
+# Check whether --with-default-legacy-postponed-folder was given.
+if test "${with_default_legacy_postponed_folder+set}" = set; then :
+ withval=$with_default_legacy_postponed_folder;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define POSTPONED_MAIL "$dpv"
+_ACEOF
+
+
+
+ dpv=postponed-msgs
+
+# Check whether --with-default-postponed-folder was given.
+if test "${with_default_postponed_folder+set}" = set; then :
+ withval=$with_default_postponed_folder;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define POSTPONED_MSGS "$dpv"
+_ACEOF
+
+
+
+ dpv=Trash
+
+# Check whether --with-default-trash-folder was given.
+if test "${with_default_trash_folder+set}" = set; then :
+ withval=$with_default_trash_folder;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define TRASH_FOLDER "$dpv"
+_ACEOF
+
+
+
+ dpv=.pine-interrupted-mail
+
+# Check whether --with-default-interrupted-mail was given.
+if test "${with_default_interrupted_mail+set}" = set; then :
+ withval=$with_default_interrupted_mail;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define INTERRUPTED_MAIL "$dpv"
+_ACEOF
+
+
+
+ dpv=dead.letter
+
+# Check whether --with-default-dead-letter-folder was given.
+if test "${with_default_dead_letter_folder+set}" = set; then :
+ withval=$with_default_dead_letter_folder;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DEADLETTER "$dpv"
+_ACEOF
+
+
+
+ dpv=mail
+
+# Check whether --with-default-mail-directory was given.
+if test "${with_default_mail_directory+set}" = set; then :
+ withval=$with_default_mail_directory;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DF_MAIL_DIRECTORY "$dpv"
+_ACEOF
+
+
+
+ dpv=INBOX
+
+# Check whether --with-default-inbox-name was given.
+if test "${with_default_inbox_name+set}" = set; then :
+ withval=$with_default_inbox_name;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define INBOX_NAME "$dpv"
+_ACEOF
+
+
+
+ dpv=.signature
+
+# Check whether --with-default-signature-file was given.
+if test "${with_default_signature_file+set}" = set; then :
+ withval=$with_default_signature_file;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DF_SIGNATURE_FILE "$dpv"
+_ACEOF
+
+
+
+ dpv=no
+
+# Check whether --with-default-elm-style-save was given.
+if test "${with_default_elm_style_save+set}" = set; then :
+ withval=$with_default_elm_style_save;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DF_ELM_STYLE_SAVE "$dpv"
+_ACEOF
+
+
+
+ dpv=no
+
+# Check whether --with-default-header-in-reply was given.
+if test "${with_default_header_in_reply+set}" = set; then :
+ withval=$with_default_header_in_reply;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DF_HEADER_IN_REPLY "$dpv"
+_ACEOF
+
+
+
+ dpv=no
+
+# Check whether --with-default-old-style-reply was given.
+if test "${with_default_old_style_reply+set}" = set; then :
+ withval=$with_default_old_style_reply;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DF_OLD_STYLE_REPLY "$dpv"
+_ACEOF
+
+
+
+ dpv=no
+
+# Check whether --with-default-use-only-domain-name was given.
+if test "${with_default_use_only_domain_name+set}" = set; then :
+ withval=$with_default_use_only_domain_name;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DF_USE_ONLY_DOMAIN_NAME "$dpv"
+_ACEOF
+
+
+
+ dpv=no
+
+# Check whether --with-default-save-by-sender was given.
+if test "${with_default_save_by_sender+set}" = set; then :
+ withval=$with_default_save_by_sender;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DF_SAVE_BY_SENDER "$dpv"
+_ACEOF
+
+
+
+ dpv=arrival
+
+# Check whether --with-default-sort-key was given.
+if test "${with_default_sort_key+set}" = set; then :
+ withval=$with_default_sort_key;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DF_SORT_KEY "$dpv"
+_ACEOF
+
+
+
+ dpv=fullname-with-lists-last
+
+# Check whether --with-default-addressbook-sort-rule was given.
+if test "${with_default_addressbook_sort_rule+set}" = set; then :
+ withval=$with_default_addressbook_sort_rule;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DF_AB_SORT_RULE "$dpv"
+_ACEOF
+
+
+
+ dpv=alphabetical
+
+# Check whether --with-default-folder-sort-rule was given.
+if test "${with_default_folder_sort_rule+set}" = set; then :
+ withval=$with_default_folder_sort_rule;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DF_FLD_SORT_RULE "$dpv"
+_ACEOF
+
+
+
+ dpv=default-folder
+
+# Check whether --with-default-saved-message-name-rule was given.
+if test "${with_default_saved_message_name_rule+set}" = set; then :
+ withval=$with_default_saved_message_name_rule;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DF_SAVED_MSG_NAME_RULE "$dpv"
+_ACEOF
+
+
+
+ dpv=default-fcc
+
+# Check whether --with-default-fcc-rule was given.
+if test "${with_default_fcc_rule+set}" = set; then :
+ withval=$with_default_fcc_rule;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DF_FCC_RULE "$dpv"
+_ACEOF
+
+
+
+ dpv=lpr
+
+# Check whether --with-default-standard-printer was given.
+if test "${with_default_standard_printer+set}" = set; then :
+ withval=$with_default_standard_printer;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DF_STANDARD_PRINTER "$dpv"
+_ACEOF
+
+
+
+ dpv=attached-to-ansi
+
+# Check whether --with-default-ansi-printer was given.
+if test "${with_default_ansi_printer+set}" = set; then :
+ withval=$with_default_ansi_printer;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define ANSI_PRINTER "$dpv"
+_ACEOF
+
+
+
+ dpv=.addressbook
+
+# Check whether --with-default-addressbook was given.
+if test "${with_default_addressbook+set}" = set; then :
+ withval=$with_default_addressbook;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DF_ADDRESSBOOK "$dpv"
+_ACEOF
+
+
+
+ dpv="Local Support"
+
+# Check whether --with-default-local-fullname was given.
+if test "${with_default_local_fullname+set}" = set; then :
+ withval=$with_default_local_fullname;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DF_LOCAL_FULLNAME "$dpv"
+_ACEOF
+
+
+
+ dpv=postmaster
+
+# Check whether --with-default-local-address was given.
+if test "${with_default_local_address+set}" = set; then :
+ withval=$with_default_local_address;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DF_LOCAL_ADDRESS "$dpv"
+_ACEOF
+
+
+
+ dpv=1
+
+# Check whether --with-default-keyboard-lock-count was given.
+if test "${with_default_keyboard_lock_count+set}" = set; then :
+ withval=$with_default_keyboard_lock_count;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DF_KBLOCK_PASSWD_COUNT "$dpv"
+_ACEOF
+
+
+
+ dpv=3
+
+# Check whether --with-default-remote-addressbook-history was given.
+if test "${with_default_remote_addressbook_history+set}" = set; then :
+ withval=$with_default_remote_addressbook_history;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DF_REMOTE_ABOOK_HISTORY "$dpv"
+_ACEOF
+
+
+
+ dpv=.alpine-smime/public
+
+# Check whether --with-smime-public-cert-directory was given.
+if test "${with_smime_public_cert_directory+set}" = set; then :
+ withval=$with_smime_public_cert_directory;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DF_PUBLICCERT_DIR "$dpv"
+_ACEOF
+
+
+
+ dpv=.alpine-smime/private
+
+# Check whether --with-smime-private-key-directory was given.
+if test "${with_smime_private_key_directory+set}" = set; then :
+ withval=$with_smime_private_key_directory;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DF_PRIVATEKEY_DIR "$dpv"
+_ACEOF
+
+
+
+ dpv=.alpine-smime/ca
+
+# Check whether --with-smime-cacert-directory was given.
+if test "${with_smime_cacert_directory+set}" = set; then :
+ withval=$with_smime_cacert_directory;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DF_CACERT_DIR "$dpv"
+_ACEOF
+
+
+
+ dpv=ANSI_PRINTER
+
+# Check whether --with-default-printer was given.
+if test "${with_default_printer+set}" = set; then :
+ withval=$with_default_printer;
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define DF_DEFAULT_PRINTER $dpv
+_ACEOF
+
+
+
+
+# Check whether --with-passfile was given.
+if test "${with_passfile+set}" = set; then :
+ withval=$with_passfile;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+ alpine_PASSFILE=$withval
+ ;;
+ esac
+
+fi
+
+
+
+# Check whether --with-local-password-cache was given.
+if test "${with_local_password_cache+set}" = set; then :
+ withval=$with_local_password_cache;
+ alpine_os_credential_cache=$withval
+
+fi
+
+
+
+# Check whether --with-local-password-cache-method was given.
+if test "${with_local_password_cache_method+set}" = set; then :
+ withval=$with_local_password_cache_method;
+ alpine_os_credential_cache_method=$withval
+
+fi
+
+
+if test -n "$alpine_PASSFILE" ; then
+ case $alpine_os_credential_cache in
+ no)
+ ;;
+ *)
+ alpine_os_credential_cache="no"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: --with-passfile definition overrides OS-Specific password caching" >&5
+$as_echo "$as_me: --with-passfile definition overrides OS-Specific password caching" >&6;}
+ ;;
+ esac
+
+cat >>confdefs.h <<_ACEOF
+#define PASSFILE "$alpine_PASSFILE"
+_ACEOF
+
+fi
+
+
+# Check whether --with-default-sshpath was given.
+if test "${with_default_sshpath+set}" = set; then :
+ withval=$with_default_sshpath;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+
+cat >>confdefs.h <<_ACEOF
+#define DF_SSHPATH "$withval"
+_ACEOF
+
+ ;;
+ esac
+
+fi
+
+
+
+# Check whether --with-default-sshcmd was given.
+if test "${with_default_sshcmd+set}" = set; then :
+ withval=$with_default_sshcmd;
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+
+cat >>confdefs.h <<_ACEOF
+#define DF_SSHCMD "$withval"
+_ACEOF
+
+ ;;
+ esac
+
+fi
+
+
+
+# Check whether --with-ssl was given.
+if test "${with_ssl+set}" = set; then :
+ withval=$with_ssl; with_ssl=$withval
+fi
+
+
+if test "x$with_ssl" = "xno" ; then
+ alpine_SSLTYPE="none"
+else
+ case $host in
+ *-linux-gnu)
+ if test -f /etc/fedora-release -o -f /etc/redhat-release -o -f /etc/redhat_version ; then
+ alpine_SSLTYPE="nopwd"
+ if test -d /etc/pki/tls ; then
+ alpine_SSLDIR="/etc/pki/tls"
+ else
+ alpine_SSLDIR="/usr/share/ssl"
+ fi
+ elif test -f /etc/SuSE-release ; then
+ alpine_SSLTYPE="nopwd"
+ alpine_SSLDIR="/usr/share/ssl"
+ alpine_SSLCERTS="/etc/ssl/certs"
+ elif test -d /etc/osso-af-init ; then
+ alpine_SSLTYPE="nopwd"
+ alpine_SSLDIR="/usr"
+ alpine_SSLCERTS="/usr/share/certs"
+ else
+ alpine_SSLTYPE="nopwd"
+ alpine_SSLDIR="/usr"
+ alpine_SSLCERTS="/etc/ssl/certs"
+ fi
+ ;;
+ *-apple-darwin*)
+ alpine_SSLTYPE="nopwd"
+ alpine_SSLCERTS="/System/Library/OpenSSL/certs"
+ ;;
+ *-openbsd*)
+ alpine_SSLTYPE="nopwd"
+ alpine_SSLDIR="/usr"
+ alpine_SSLCERTS="/etc/ssl/certs"
+ ;;
+ *-sco-sysv* | *-sysv*UnixWare | *-sysv*OpenUNIX)
+ alpine_SSLTYPE="sco.nopwd"
+ alpine_SSLDIR=/usr/local/ssl
+ ;;
+ *-*-solaris*)
+ if test -d /usr/sfw/include/openssl ; then
+ alpine_SSLDIR="/usr/sfw"
+ elif test -d /opt/csw/include/openssl ; then
+ alpine_SSLDIR="/opt/csw"
+ if test -d /opt/csw/ssl/certs ; then
+ alpine_SSLCERTS="/opt/csw/ssl/certs"
+ fi
+ fi
+ if test -z "$alpine_SSLCERTS" -a -d /etc/certs ; then
+ alpine_SSLCERTS="/etc/certs"
+ fi
+ ;;
+ *)
+ alpine_SSLTYPE="nopwd"
+ alpine_SSLDIR=/usr/local/ssl
+ ;;
+ esac
+
+
+# Check whether --with-ssl-dir was given.
+if test "${with_ssl_dir+set}" = set; then :
+ withval=$with_ssl_dir;
+ if test "x$withval" != "xno" ; then
+ alpine_SSLDIR=$withval
+ fi
+
+fi
+
+
+
+# Check whether --with-ssl-certs-dir was given.
+if test "${with_ssl_certs_dir+set}" = set; then :
+ withval=$with_ssl_certs_dir;
+ if test "x$withval" != "xno" ; then
+ alpine_SSLCERTS=$withval
+ fi
+
+fi
+
+
+
+# Check whether --with-ssl-include-dir was given.
+if test "${with_ssl_include_dir+set}" = set; then :
+ withval=$with_ssl_include_dir;
+ if test "x$withval" != "xno" ; then
+ alpine_SSLINCLUDE=$withval
+ fi
+
+fi
+
+
+
+# Check whether --with-ssl-lib-dir was given.
+if test "${with_ssl_lib_dir+set}" = set; then :
+ withval=$with_ssl_lib_dir;
+ if test "x$withval" != "xno" ; then
+ alpine_SSLLIB=$withval
+ fi
+
+fi
+
+ if test -n "$alpine_SSLINCLUDE" ; then
+ CPPCFLAGS="-I$alpine_SSLINCLUDE $CPPFLAGS"
+ elif test -n "$alpine_SSLDIR" ; then
+ CPPFLAGS="-I${alpine_SSLDIR}/include $CPPFLAGS"
+ fi
+ if test -n "$alpine_SSLLIB" ; then
+ LDFLAGS="-L$alpine_SSLLIB $LDFLAGS"
+ elif test -n "$alpine_SSLDIR" ; then
+ LDFLAGS="-L${alpine_SSLDIR}/lib $LDFLAGS"
+ fi
+fi
+
+
+# Check whether --with-krb5 was given.
+if test "${with_krb5+set}" = set; then :
+ withval=$with_krb5; with_krb5=$withval
+fi
+
+
+if test "x$with_krb5" = "xno" ; then
+ alpine_GSSTYPE="none"
+else
+ alpine_GSSTYPE=
+
+
+# Check whether --with-krb5-dir was given.
+if test "${with_krb5_dir+set}" = set; then :
+ withval=$with_krb5_dir;
+ if test "x$withval" != "xno" ; then
+ CPPFLAGS="$CPPFLAGS -I${withval}/include"
+ LDFLAGS="$LDFLAGS -L${withval}/lib"
+ fi
+
+fi
+
+
+
+# Check whether --with-krb5-include-dir was given.
+if test "${with_krb5_include_dir+set}" = set; then :
+ withval=$with_krb5_include_dir;
+ if test "x$withval" != "xno" ; then
+ CPPFLAGS="$CPPFLAGS -I$withval"
+ fi
+
+fi
+
+
+
+# Check whether --with-krb5-lib-dir was given.
+if test "${with_krb5_lib_dir+set}" = set; then :
+ withval=$with_krb5_lib_dir;
+ if test "x$withval" != "xno" ; then
+ LDFLAGS="$LDFLAGS -L$withval"
+ fi
+
+fi
+
+fi
+
+
+# Check whether --with-ldap was given.
+if test "${with_ldap+set}" = set; then :
+ withval=$with_ldap; with_ldap=$withval
+fi
+
+
+if test "x$with_ldap" = "xno" ; then
+ alpine_with_ldap=
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Excluding LDAP Support" >&5
+$as_echo "$as_me: Excluding LDAP Support" >&6;}
+else
+
+ alpine_with_ldap=yes
+
+# Check whether --with-ldap-dir was given.
+if test "${with_ldap_dir+set}" = set; then :
+ withval=$with_ldap_dir;
+ if test "x$withval" != "xno" ; then
+ CPPFLAGS="$CPPFLAGS -I${withval}/include"
+ LDFLAGS="$LDFLAGS -L${withval}/lib"
+ fi
+
+fi
+
+
+
+# Check whether --with-ldap-include-dir was given.
+if test "${with_ldap_include_dir+set}" = set; then :
+ withval=$with_ldap_include_dir;
+ if test "x$withval" != "xno" ; then
+ CPPFLAGS="$CPPFLAGS -I$withval"
+ fi
+
+fi
+
+
+
+# Check whether --with-ldap-lib-dir was given.
+if test "${with_ldap_lib_dir+set}" = set; then :
+ withval=$with_ldap_lib_dir;
+ if test "x$withval" != "xno" ; then
+ LDFLAGS="$LDFLAGS -L$withval"
+ fi
+
+fi
+
+fi
+
+
+# Check whether --with-smime was given.
+if test "${with_smime+set}" = set; then :
+ withval=$with_smime; with_smime=$withval
+fi
+
+
+
+# Check whether --with-tcl was given.
+if test "${with_tcl+set}" = set; then :
+ withval=$with_tcl; with_tcl=$withval
+fi
+
+
+if test "x$with_tcl" = "xno" ; then
+ WEB_BUILD=
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Excluding TCL Support, and thus Web Alpine Support" >&5
+$as_echo "$as_me: Excluding TCL Support, and thus Web Alpine Support" >&6;}
+else
+
+# Check whether --with-tcl-lib was given.
+if test "${with_tcl_lib+set}" = set; then :
+ withval=$with_tcl_lib;
+ if test "x$withval" != "xno" ; then
+ alpine_TCLLIB=$withval
+ fi
+
+fi
+
+
+# Check whether --with-tcl-include was given.
+if test "${with_tcl_include+set}" = set; then :
+ withval=$with_tcl_include;
+ if test "x$withval" != "xno" ; then
+ CPPFLAGS="$CPPFLAGS -I$withval"
+ alpine_TCLINC=$withval
+ fi
+
+fi
+
+fi
+
+
+# Check whether --with-supplied-regex was given.
+if test "${with_supplied_regex+set}" = set; then :
+ withval=$with_supplied_regex; alpine_REGEX=$withval
+fi
+
+
+
+# Check whether --with-pthread was given.
+if test "${with_pthread+set}" = set; then :
+ withval=$with_pthread; with_pthread=$withval
+fi
+
+
+
+# Check whether --with-system-mail-directory was given.
+if test "${with_system_mail_directory+set}" = set; then :
+ withval=$with_system_mail_directory;
+ if test "x$withval" != "xno" ; then
+ alpine_with_local_maildir="$withval"
+ fi
+
+fi
+
+
+
+# Check whether --with-c-client-target was given.
+if test "${with_c_client_target+set}" = set; then :
+ withval=$with_c_client_target;
+ if test "x$withval" != "xno" ;then
+ alpine_with_c_client_target="$withval"
+ fi
+
+fi
+
+
+
+
+# Check whether --with-ipv6 was given.
+if test "${with_ipv6+set}" = set; then :
+ withval=$with_ipv6; with_ipv6=$withval
+fi
+
+
+if test "x$with_ipv6" = "xno" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Excluding IPv6 Support" >&5
+$as_echo "$as_me: Excluding IPv6 Support" >&6;}
+ c_client_specials="${c_client_specials}IP6=4 "
+ c_client_ip6="true"
+else
+ c_client_ip6="touch imap/ip6"
+fi
+
+
+
+if test x$enable_dmalloc = "xyes" ; then
+ if test "x$with_pthread" = "xyes" ; then
+ dmalloc_lib=dmallocth
+ else
+ dmalloc_lib=dmalloc
+ fi
+
+ as_ac_Lib=`$as_echo "ac_cv_lib_$dmalloc_lib''_dmalloc_shutdown" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dmalloc_shutdown in -l$dmalloc_lib" >&5
+$as_echo_n "checking for dmalloc_shutdown in -l$dmalloc_lib... " >&6; }
+if eval \${$as_ac_Lib+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-l$dmalloc_lib $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dmalloc_shutdown ();
+int
+main ()
+{
+return dmalloc_shutdown ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ eval "$as_ac_Lib=yes"
+else
+ eval "$as_ac_Lib=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+eval ac_res=\$$as_ac_Lib
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_LIB$dmalloc_lib" | $as_tr_cpp` 1
+_ACEOF
+
+ LIBS="-l$dmalloc_lib $LIBS"
+
+else
+
+ as_fn_error $? "$dmalloc_lib requested, but -ldmalloc not found" "$LINENO" 5
+
+fi
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for setupterm in -ltinfo" >&5
+$as_echo_n "checking for setupterm in -ltinfo... " >&6; }
+if ${ac_cv_lib_tinfo_setupterm+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ltinfo $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char setupterm ();
+int
+main ()
+{
+return setupterm ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_tinfo_setupterm=yes
+else
+ ac_cv_lib_tinfo_setupterm=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_tinfo_setupterm" >&5
+$as_echo "$ac_cv_lib_tinfo_setupterm" >&6; }
+if test "x$ac_cv_lib_tinfo_setupterm" = xyes; then :
+
+ alpine_termdata=info
+ LIBS="$LIBS -ltinfo"
+
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for setupterm in -lncurses" >&5
+$as_echo_n "checking for setupterm in -lncurses... " >&6; }
+if ${ac_cv_lib_ncurses_setupterm+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lncurses $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char setupterm ();
+int
+main ()
+{
+return setupterm ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_ncurses_setupterm=yes
+else
+ ac_cv_lib_ncurses_setupterm=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ncurses_setupterm" >&5
+$as_echo "$ac_cv_lib_ncurses_setupterm" >&6; }
+if test "x$ac_cv_lib_ncurses_setupterm" = xyes; then :
+
+ alpine_termdata=info
+ LIBS="$LIBS -lncurses"
+
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for setupterm in -lcurses" >&5
+$as_echo_n "checking for setupterm in -lcurses... " >&6; }
+if ${ac_cv_lib_curses_setupterm+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lcurses $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char setupterm ();
+int
+main ()
+{
+return setupterm ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_curses_setupterm=yes
+else
+ ac_cv_lib_curses_setupterm=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_curses_setupterm" >&5
+$as_echo "$ac_cv_lib_curses_setupterm" >&6; }
+if test "x$ac_cv_lib_curses_setupterm" = xyes; then :
+
+ alpine_termdata=info
+ LIBS="$LIBS -lcurses"
+
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for tgetent in -ltermlib" >&5
+$as_echo_n "checking for tgetent in -ltermlib... " >&6; }
+if ${ac_cv_lib_termlib_tgetent+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ltermlib $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char tgetent ();
+int
+main ()
+{
+return tgetent ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_termlib_tgetent=yes
+else
+ ac_cv_lib_termlib_tgetent=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_termlib_tgetent" >&5
+$as_echo "$ac_cv_lib_termlib_tgetent" >&6; }
+if test "x$ac_cv_lib_termlib_tgetent" = xyes; then :
+
+ alpine_termdata=cap
+ LIBS="$LIBS -ltermlib"
+
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for tgetent in -ltermcap" >&5
+$as_echo_n "checking for tgetent in -ltermcap... " >&6; }
+if ${ac_cv_lib_termcap_tgetent+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ltermcap $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char tgetent ();
+int
+main ()
+{
+return tgetent ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_termcap_tgetent=yes
+else
+ ac_cv_lib_termcap_tgetent=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_termcap_tgetent" >&5
+$as_echo "$ac_cv_lib_termcap_tgetent" >&6; }
+if test "x$ac_cv_lib_termcap_tgetent" = xyes; then :
+
+ alpine_termdata=cap
+ LIBS="$LIBS -ltermcap"
+
+else
+
+ as_fn_error $? "Terminfo/termcap not found" "$LINENO" 5
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+case $alpine_termdata in
+ info)
+
+$as_echo "#define HAS_TERMINFO 1" >>confdefs.h
+
+ ;;
+ cap)
+
+$as_echo "#define HAS_TERMCAP 1" >>confdefs.h
+
+ ;;
+esac
+
+if test "$alpine_with_ldap" = "yes" ; then
+ alpine_has_ldap=
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ber_alloc in -llber" >&5
+$as_echo_n "checking for ber_alloc in -llber... " >&6; }
+if ${ac_cv_lib_lber_ber_alloc+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-llber $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ber_alloc ();
+int
+main ()
+{
+return ber_alloc ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_lber_ber_alloc=yes
+else
+ ac_cv_lib_lber_ber_alloc=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lber_ber_alloc" >&5
+$as_echo "$ac_cv_lib_lber_ber_alloc" >&6; }
+if test "x$ac_cv_lib_lber_ber_alloc" = xyes; then :
+
+ LIBS="$LIBS -llber"
+
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing ldap_init" >&5
+$as_echo_n "checking for library containing ldap_init... " >&6; }
+if ${ac_cv_search_ldap_init+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ldap_init ();
+int
+main ()
+{
+return ldap_init ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' ldap; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_ldap_init=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_ldap_init+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_ldap_init+:} false; then :
+
+else
+ ac_cv_search_ldap_init=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_ldap_init" >&5
+$as_echo "$ac_cv_search_ldap_init" >&6; }
+ac_res=$ac_cv_search_ldap_init
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+ alpine_has_ldap=yes
+
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing ldap_open" >&5
+$as_echo_n "checking for library containing ldap_open... " >&6; }
+if ${ac_cv_search_ldap_open+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ldap_open ();
+int
+main ()
+{
+return ldap_open ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' ldap; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_ldap_open=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_ldap_open+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_ldap_open+:} false; then :
+
+else
+ ac_cv_search_ldap_open=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_ldap_open" >&5
+$as_echo "$ac_cv_search_ldap_open" >&6; }
+ac_res=$ac_cv_search_ldap_open
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+ alpine_has_ldap=yes
+
+fi
+
+
+fi
+
+
+ if test "$alpine_has_ldap" = "yes" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Including LDAP Support" >&5
+$as_echo "$as_me: Including LDAP Support" >&6;}
+
+$as_echo "#define ENABLE_LDAP /**/" >>confdefs.h
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we should define LDAP_DEPRECATED" >&5
+$as_echo_n "checking if we should define LDAP_DEPRECATED... " >&6; }
+ if test "$cross_compiling" = yes; then :
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cross compiling: not checking" >&5
+$as_echo "$as_me: WARNING: cross compiling: not checking" >&2;}
+
+
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#include <stdio.h>
+#include <string.h>
+#include <ldap.h>
+int main(void) {
+
+ if (LDAP_VENDOR_VERSION >= 20300)
+ exit(0);
+
+ exit(2);
+}
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+$as_echo "#define LDAP_DEPRECATED 1" >>confdefs.h
+
+
+
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Cannot find LDAP functions! Excluding LDAP support." >&5
+$as_echo "$as_me: Cannot find LDAP functions! Excluding LDAP support." >&6;}
+ fi
+fi
+
+if test "x$alpine_SSLTYPE" != "xnone" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing SSL_library_init" >&5
+$as_echo_n "checking for library containing SSL_library_init... " >&6; }
+if ${ac_cv_search_SSL_library_init+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char SSL_library_init ();
+int
+main ()
+{
+return SSL_library_init ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' ssl; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_SSL_library_init=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_SSL_library_init+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_SSL_library_init+:} false; then :
+
+else
+ ac_cv_search_SSL_library_init=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_SSL_library_init" >&5
+$as_echo "$ac_cv_search_SSL_library_init" >&6; }
+ac_res=$ac_cv_search_SSL_library_init
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+ LIBS="$LIBS -lssl"
+
+fi
+
+ if test "x$alpine_SSLTYPE" = "xnone" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: OpenSSL libraries NOT found" >&5
+$as_echo "$as_me: OpenSSL libraries NOT found" >&6;}
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: OpenSSL libraries FOUND" >&5
+$as_echo "$as_me: OpenSSL libraries FOUND" >&6;}
+ fi
+fi
+
+if test "x$alpine_GSSTYPE" != "xnone" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing gss_init_sec_context" >&5
+$as_echo_n "checking for library containing gss_init_sec_context... " >&6; }
+if ${ac_cv_search_gss_init_sec_context+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char gss_init_sec_context ();
+int
+main ()
+{
+return gss_init_sec_context ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' gss gssapi gssapi_krb5; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_gss_init_sec_context=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_gss_init_sec_context+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_gss_init_sec_context+:} false; then :
+
+else
+ ac_cv_search_gss_init_sec_context=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_gss_init_sec_context" >&5
+$as_echo "$ac_cv_search_gss_init_sec_context" >&6; }
+ac_res=$ac_cv_search_gss_init_sec_context
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+ ac_fn_c_check_header_mongrel "$LINENO" "gssapi/gssapi_generic.h" "ac_cv_header_gssapi_gssapi_generic_h" "$ac_includes_default"
+if test "x$ac_cv_header_gssapi_gssapi_generic_h" = xyes; then :
+
+else
+
+ if test ! -d /usr/kerberos/include ; then
+ alpine_GSSTYPE="none"
+ alpine_gss_none_reason="header files not found"
+ fi
+
+fi
+
+
+
+else
+
+ alpine_GSSTYPE="none"
+ alpine_gss_none_reason="libraries not found"
+
+fi
+
+ if test -n "$alpine_gss_none_reason" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: NOT including Kerberos Support: $alpine_gss_none_reason" >&5
+$as_echo "$as_me: NOT including Kerberos Support: $alpine_gss_none_reason" >&6;}
+ fi
+fi
+
+if test -n "$WEB_BUILD" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing Tcl_Eval" >&5
+$as_echo_n "checking for library containing Tcl_Eval... " >&6; }
+if ${ac_cv_search_Tcl_Eval+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char Tcl_Eval ();
+int
+main ()
+{
+return Tcl_Eval ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' $alpine_TCLLIB tcl8.4 tcl8.3 tcl84 tcl83 tcl; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_Tcl_Eval=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_Tcl_Eval+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_Tcl_Eval+:} false; then :
+
+else
+ ac_cv_search_Tcl_Eval=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_Tcl_Eval" >&5
+$as_echo "$ac_cv_search_Tcl_Eval" >&6; }
+ac_res=$ac_cv_search_Tcl_Eval
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+else
+
+ WEB_BUILD=
+
+fi
+
+
+ if test -n "$alpine_TCLINC" ; then
+ as_ac_Header=`$as_echo "ac_cv_header_$alpine_TCLINC" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$alpine_TCLINC" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+
+else
+
+ WEB_BUILD=
+
+fi
+
+
+ if test -z "$WEB_BUILD" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: Tcl Include file NOT found" >&5
+$as_echo "$as_me: Tcl Include file NOT found" >&6;}
+ fi
+ else
+ ac_fn_c_check_header_mongrel "$LINENO" "tcl.h" "ac_cv_header_tcl_h" "$ac_includes_default"
+if test "x$ac_cv_header_tcl_h" = xyes; then :
+
+else
+
+ for dir in tcl8.4 tcl8.3 tcl84 tcl83 ; do
+ as_ac_File=`$as_echo "ac_cv_file_/usr/include/$dir/tcl.h" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for /usr/include/$dir/tcl.h" >&5
+$as_echo_n "checking for /usr/include/$dir/tcl.h... " >&6; }
+if eval \${$as_ac_File+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ test "$cross_compiling" = yes &&
+ as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5
+if test -r "/usr/include/$dir/tcl.h"; then
+ eval "$as_ac_File=yes"
+else
+ eval "$as_ac_File=no"
+fi
+fi
+eval ac_res=\$$as_ac_File
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_File"\" = x"yes"; then :
+
+ found=yes
+
+fi
+
+ if test "$found" = "yes" ; then
+ CPPFLAGS="$CPPFLAGS -I/usr/include/$dir"
+ break
+ fi
+ done
+
+fi
+
+
+ fi
+fi
+
+if test x$alpine_REGEX != "xyes" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing regcomp" >&5
+$as_echo_n "checking for library containing regcomp... " >&6; }
+if ${ac_cv_search_regcomp+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char regcomp ();
+int
+main ()
+{
+return regcomp ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' posix regexp regex re; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_regcomp=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_regcomp+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_regcomp+:} false; then :
+
+else
+ ac_cv_search_regcomp=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_regcomp" >&5
+$as_echo "$ac_cv_search_regcomp" >&6; }
+ac_res=$ac_cv_search_regcomp
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+else
+
+ if test x$alpine_REGEX = "xno" ; then
+ as_fn_error $? "Unable to find system regex library" "$LINENO" 5
+ else
+ alpine_REGEX=yes
+ fi
+
+fi
+
+fi
+if test x$alpine_REGEX != "xyes" ; then
+ ac_fn_c_check_header_mongrel "$LINENO" "regex.h" "ac_cv_header_regex_h" "$ac_includes_default"
+if test "x$ac_cv_header_regex_h" = xyes; then :
+
+else
+
+ if test x$alpine_REGEX = "xno" ; then
+ as_fn_error $? "Unable to find system regex include file" "$LINENO" 5
+ else
+ alpine_REGEX=yes
+ fi
+
+fi
+
+
+fi
+
+$as_echo "#define HAVE_REGEX_H 1" >>confdefs.h
+
+if test x$alpine_REGEX = "xyes" ; then
+ CPPFLAGS="$CPPFLAGS -I${top_builddir}/regex"
+ LDFLAGS="$LDFLAGS -L${top_builddir}/regex -lregex"
+ REGEX_BUILD=regex
+fi
+
+if test "x$with_pthread" != "xno" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread support" >&5
+$as_echo_n "checking for pthread support... " >&6; }
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+acx_pthread_ok=no
+
+# We used to check for pthread.h first, but this fails if pthread.h
+# requires special compiler flags (e.g. on True64 or Sequent).
+# It gets checked for in the link test anyway.
+
+# First of all, check if the user has set any of the PTHREAD_LIBS,
+# etcetera environment variables, and if threads linking works using
+# them:
+if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+ save_LIBS="$LIBS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS" >&5
+$as_echo_n "checking for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS... " >&6; }
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pthread_join ();
+int
+main ()
+{
+return pthread_join ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ acx_pthread_ok=yes
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $acx_pthread_ok" >&5
+$as_echo "$acx_pthread_ok" >&6; }
+ if test x"$acx_pthread_ok" = xno; then
+ PTHREAD_LIBS=""
+ PTHREAD_CFLAGS=""
+ fi
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+fi
+
+# We must check for the threads library under a number of different
+# names; the ordering is very important because some systems
+# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
+# libraries is broken (non-POSIX).
+
+# Create a list of thread flags to try. Items starting with a "-" are
+# C compiler flags, and other items are library names, except for "none"
+# which indicates that we try without any flags at all, and "pthread-config"
+# which is a program returning the flags for the Pth emulation library.
+
+acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
+
+# The ordering *is* (sometimes) important. Some notes on the
+# individual items follow:
+
+# pthreads: AIX (must check this before -lpthread)
+# none: in case threads are in libc; should be tried before -Kthread and
+# other compiler flags to prevent continual compiler warnings
+# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
+# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
+# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
+# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
+# -pthreads: Solaris/gcc
+# -mthreads: Mingw32/gcc, Lynx/gcc
+# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
+# doesn't hurt to check since this sometimes defines pthreads too;
+# also defines -D_REENTRANT)
+# ... -mt is also the pthreads flag for HP/aCC
+# pthread: Linux, etcetera
+# --thread-safe: KAI C++
+# pthread-config: use pthread-config program (for GNU Pth library)
+
+case "${host_cpu}-${host_os}" in
+ *solaris*)
+
+ # On Solaris (at least, for some versions), libc contains stubbed
+ # (non-functional) versions of the pthreads routines, so link-based
+ # tests will erroneously succeed. (We need to link with -pthreads/-mt/
+ # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
+ # a function called by this macro, so we could check for that, but
+ # who knows whether they'll stub that too in a future libc.) So,
+ # we'll just look for -pthreads and -lpthread first:
+
+ acx_pthread_flags="-pthreads pthread -mt -pthread $acx_pthread_flags"
+ ;;
+esac
+
+if test x"$acx_pthread_ok" = xno; then
+for flag in $acx_pthread_flags; do
+
+ case $flag in
+ none)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work without any flags" >&5
+$as_echo_n "checking whether pthreads work without any flags... " >&6; }
+ ;;
+
+ -*)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work with $flag" >&5
+$as_echo_n "checking whether pthreads work with $flag... " >&6; }
+ PTHREAD_CFLAGS="$flag"
+ ;;
+
+ pthread-config)
+ # Extract the first word of "pthread-config", so it can be a program name with args.
+set dummy pthread-config; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_acx_pthread_config+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$acx_pthread_config"; then
+ ac_cv_prog_acx_pthread_config="$acx_pthread_config" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_acx_pthread_config="yes"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_prog_acx_pthread_config" && ac_cv_prog_acx_pthread_config="no"
+fi
+fi
+acx_pthread_config=$ac_cv_prog_acx_pthread_config
+if test -n "$acx_pthread_config"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $acx_pthread_config" >&5
+$as_echo "$acx_pthread_config" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test x"$acx_pthread_config" = xno; then continue; fi
+ PTHREAD_CFLAGS="`pthread-config --cflags`"
+ PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
+ ;;
+
+ *)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for the pthreads library -l$flag" >&5
+$as_echo_n "checking for the pthreads library -l$flag... " >&6; }
+ PTHREAD_LIBS="-l$flag"
+ ;;
+ esac
+
+ save_LIBS="$LIBS"
+ save_CFLAGS="$CFLAGS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+ # Check for various functions. We must include pthread.h,
+ # since some functions may be macros. (On the Sequent, we
+ # need a special flag -Kthread to make this header compile.)
+ # We check for pthread_join because it is in -lpthread on IRIX
+ # while pthread_create is in libc. We check for pthread_attr_init
+ # due to DEC craziness with -lpthreads. We check for
+ # pthread_cleanup_push because it is one of the few pthread
+ # functions on Solaris that doesn't have a non-functional libc stub.
+ # We try pthread_create on general principles.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <pthread.h>
+int
+main ()
+{
+pthread_t th; pthread_join(th, 0);
+ pthread_attr_init(0); pthread_cleanup_push(0, 0);
+ pthread_create(0,0,0,0); pthread_cleanup_pop(0);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ acx_pthread_ok=yes
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $acx_pthread_ok" >&5
+$as_echo "$acx_pthread_ok" >&6; }
+ if test "x$acx_pthread_ok" = xyes; then
+ break;
+ fi
+
+ PTHREAD_LIBS=""
+ PTHREAD_CFLAGS=""
+done
+fi
+
+# Various other checks:
+if test "x$acx_pthread_ok" = xyes; then
+ save_LIBS="$LIBS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+ # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for joinable pthread attribute" >&5
+$as_echo_n "checking for joinable pthread attribute... " >&6; }
+ attr_name=unknown
+ for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <pthread.h>
+int
+main ()
+{
+int attr=$attr; return attr;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ attr_name=$attr; break
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ done
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $attr_name" >&5
+$as_echo "$attr_name" >&6; }
+ if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
+
+cat >>confdefs.h <<_ACEOF
+#define PTHREAD_CREATE_JOINABLE $attr_name
+_ACEOF
+
+ fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if more special flags are required for pthreads" >&5
+$as_echo_n "checking if more special flags are required for pthreads... " >&6; }
+ flag=no
+ case "${host_cpu}-${host_os}" in
+ *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";;
+ *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
+ esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${flag}" >&5
+$as_echo "${flag}" >&6; }
+ if test "x$flag" != xno; then
+ PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
+ fi
+
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+
+ # More AIX lossage: must compile with xlc_r or cc_r
+ if test x"$GCC" != xyes; then
+ for ac_prog in xlc_r cc_r
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_PTHREAD_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$PTHREAD_CC"; then
+ ac_cv_prog_PTHREAD_CC="$PTHREAD_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ ac_cv_prog_PTHREAD_CC="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+PTHREAD_CC=$ac_cv_prog_PTHREAD_CC
+if test -n "$PTHREAD_CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PTHREAD_CC" >&5
+$as_echo "$PTHREAD_CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$PTHREAD_CC" && break
+done
+test -n "$PTHREAD_CC" || PTHREAD_CC="${CC}"
+
+ else
+ PTHREAD_CC=$CC
+ fi
+else
+ PTHREAD_CC="$CC"
+fi
+
+
+
+
+
+# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
+if test x"$acx_pthread_ok" = xyes; then
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ case "$target" in
+ *openbsd*)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: pthread support on OpenBSD is unstable!" >&5
+$as_echo "$as_me: WARNING: pthread support on OpenBSD is unstable!" >&6;}
+ AM_CFLAGS="$AM_CFLAGS -pthread"
+ ;;
+ esac
+ LIBS="$PTHREAD_LIBS $LIBS"
+ AM_CFLAGS="$AM_CFLAGS $PTHREAD_CFLAGS"
+ CC="$PTHREAD_CC"
+
+$as_echo "#define HAVE_PTHREAD 1" >>confdefs.h
+
+
+ :
+else
+ acx_pthread_ok=no
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing nanosleep" >&5
+$as_echo_n "checking for library containing nanosleep... " >&6; }
+if ${ac_cv_search_nanosleep+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char nanosleep ();
+int
+main ()
+{
+return nanosleep ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' rt posix4; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_nanosleep=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_nanosleep+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_nanosleep+:} false; then :
+
+else
+ ac_cv_search_nanosleep=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_nanosleep" >&5
+$as_echo "$ac_cv_search_nanosleep" >&6; }
+ac_res=$ac_cv_search_nanosleep
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+
+$as_echo "#define HAVE_NANOSLEEP 1" >>confdefs.h
+
+
+fi
+
+fi
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if ${ac_cv_header_stdc+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_header_stdc=yes
+else
+ ac_cv_header_stdc=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "memchr" >/dev/null 2>&1; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "free" >/dev/null 2>&1; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+ if test "$cross_compiling" = yes; then :
+ :
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+ (('a' <= (c) && (c) <= 'i') \
+ || ('j' <= (c) && (c) <= 'r') \
+ || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ if (XOR (islower (i), ISLOWER (i))
+ || toupper (i) != TOUPPER (i))
+ return 2;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+$as_echo "#define STDC_HEADERS 1" >>confdefs.h
+
+fi
+
+ac_header_dirent=no
+for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do
+ as_ac_Header=`$as_echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_hdr that defines DIR" >&5
+$as_echo_n "checking for $ac_hdr that defines DIR... " >&6; }
+if eval \${$as_ac_Header+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <$ac_hdr>
+
+int
+main ()
+{
+if ((DIR *) 0)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ eval "$as_ac_Header=yes"
+else
+ eval "$as_ac_Header=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$as_ac_Header
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_hdr" | $as_tr_cpp` 1
+_ACEOF
+
+ac_header_dirent=$ac_hdr; break
+fi
+
+done
+# Two versions of opendir et al. are in -ldir and -lx on SCO Xenix.
+if test $ac_header_dirent = dirent.h; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5
+$as_echo_n "checking for library containing opendir... " >&6; }
+if ${ac_cv_search_opendir+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char opendir ();
+int
+main ()
+{
+return opendir ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' dir; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_opendir=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_opendir+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_opendir+:} false; then :
+
+else
+ ac_cv_search_opendir=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5
+$as_echo "$ac_cv_search_opendir" >&6; }
+ac_res=$ac_cv_search_opendir
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5
+$as_echo_n "checking for library containing opendir... " >&6; }
+if ${ac_cv_search_opendir+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char opendir ();
+int
+main ()
+{
+return opendir ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' x; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_opendir=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_opendir+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_opendir+:} false; then :
+
+else
+ ac_cv_search_opendir=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5
+$as_echo "$ac_cv_search_opendir" >&6; }
+ac_res=$ac_cv_search_opendir
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stat file-mode macros are broken" >&5
+$as_echo_n "checking whether stat file-mode macros are broken... " >&6; }
+if ${ac_cv_header_stat_broken+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#if defined S_ISBLK && defined S_IFDIR
+extern char c1[S_ISBLK (S_IFDIR) ? -1 : 1];
+#endif
+
+#if defined S_ISBLK && defined S_IFCHR
+extern char c2[S_ISBLK (S_IFCHR) ? -1 : 1];
+#endif
+
+#if defined S_ISLNK && defined S_IFREG
+extern char c3[S_ISLNK (S_IFREG) ? -1 : 1];
+#endif
+
+#if defined S_ISSOCK && defined S_IFREG
+extern char c4[S_ISSOCK (S_IFREG) ? -1 : 1];
+#endif
+
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_header_stat_broken=no
+else
+ ac_cv_header_stat_broken=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stat_broken" >&5
+$as_echo "$ac_cv_header_stat_broken" >&6; }
+if test $ac_cv_header_stat_broken = yes; then
+
+$as_echo "#define STAT_MACROS_BROKEN 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sys/wait.h that is POSIX.1 compatible" >&5
+$as_echo_n "checking for sys/wait.h that is POSIX.1 compatible... " >&6; }
+if ${ac_cv_header_sys_wait_h+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/wait.h>
+#ifndef WEXITSTATUS
+# define WEXITSTATUS(stat_val) ((unsigned int) (stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+
+int
+main ()
+{
+ int s;
+ wait (&s);
+ s = WIFEXITED (s) ? WEXITSTATUS (s) : 1;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_header_sys_wait_h=yes
+else
+ ac_cv_header_sys_wait_h=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_sys_wait_h" >&5
+$as_echo "$ac_cv_header_sys_wait_h" >&6; }
+if test $ac_cv_header_sys_wait_h = yes; then
+
+$as_echo "#define HAVE_SYS_WAIT_H 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether time.h and sys/time.h may both be included" >&5
+$as_echo_n "checking whether time.h and sys/time.h may both be included... " >&6; }
+if ${ac_cv_header_time+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
+
+int
+main ()
+{
+if ((struct tm *) 0)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_header_time=yes
+else
+ ac_cv_header_time=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_time" >&5
+$as_echo "$ac_cv_header_time" >&6; }
+if test $ac_cv_header_time = yes; then
+
+$as_echo "#define TIME_WITH_SYS_TIME 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether termios.h defines TIOCGWINSZ" >&5
+$as_echo_n "checking whether termios.h defines TIOCGWINSZ... " >&6; }
+if ${ac_cv_sys_tiocgwinsz_in_termios_h+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <termios.h>
+#ifdef TIOCGWINSZ
+ yes
+#endif
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "yes" >/dev/null 2>&1; then :
+ ac_cv_sys_tiocgwinsz_in_termios_h=yes
+else
+ ac_cv_sys_tiocgwinsz_in_termios_h=no
+fi
+rm -f conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_tiocgwinsz_in_termios_h" >&5
+$as_echo "$ac_cv_sys_tiocgwinsz_in_termios_h" >&6; }
+
+if test $ac_cv_sys_tiocgwinsz_in_termios_h != yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether sys/ioctl.h defines TIOCGWINSZ" >&5
+$as_echo_n "checking whether sys/ioctl.h defines TIOCGWINSZ... " >&6; }
+if ${ac_cv_sys_tiocgwinsz_in_sys_ioctl_h+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#ifdef TIOCGWINSZ
+ yes
+#endif
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "yes" >/dev/null 2>&1; then :
+ ac_cv_sys_tiocgwinsz_in_sys_ioctl_h=yes
+else
+ ac_cv_sys_tiocgwinsz_in_sys_ioctl_h=no
+fi
+rm -f conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_tiocgwinsz_in_sys_ioctl_h" >&5
+$as_echo "$ac_cv_sys_tiocgwinsz_in_sys_ioctl_h" >&6; }
+
+ if test $ac_cv_sys_tiocgwinsz_in_sys_ioctl_h = yes; then
+
+$as_echo "#define GWINSZ_IN_SYS_IOCTL 1" >>confdefs.h
+
+ fi
+fi
+
+
+for ac_header in unistd.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "unistd.h" "ac_cv_header_unistd_h" "$ac_includes_default"
+if test "x$ac_cv_header_unistd_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_UNISTD_H 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in errno.h \
+ ctype.h \
+ fcntl.h \
+ signal.h \
+ setjmp.h \
+ memory.h \
+ sys/param.h \
+ sys/socket.h \
+ sys/uio.h \
+ sys/un.h \
+ limits.h \
+ wchar.h \
+ sys/poll.h \
+ stropts.h \
+ netdb.h \
+ syslog.h \
+ sys/syslog.h \
+ locale.h \
+ langinfo.h \
+ utime.h \
+ sys/utime.h \
+ pthread.h \
+ pwd.h \
+ assert.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+ac_fn_c_check_header_mongrel "$LINENO" "termios.h" "ac_cv_header_termios_h" "$ac_includes_default"
+if test "x$ac_cv_header_termios_h" = xyes; then :
+
+$as_echo "#define HAS_TERMIOS 1" >>confdefs.h
+
+else
+
+ ac_fn_c_check_header_mongrel "$LINENO" "termio.h" "ac_cv_header_termio_h" "$ac_includes_default"
+if test "x$ac_cv_header_termio_h" = xyes; then :
+
+$as_echo "#define HAS_TERMIO 1" >>confdefs.h
+
+else
+
+ ac_fn_c_check_header_mongrel "$LINENO" "sgtty.h" "ac_cv_header_sgtty_h" "$ac_includes_default"
+if test "x$ac_cv_header_sgtty_h" = xyes; then :
+
+$as_echo "#define HAS_SGTTY 1" >>confdefs.h
+
+else
+
+ as_fn_error $? "Unable to figure out terminal control method" "$LINENO" 5
+
+fi
+
+
+
+fi
+
+
+
+fi
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking return type of signal handlers" >&5
+$as_echo_n "checking return type of signal handlers... " >&6; }
+if ${ac_cv_type_signal+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <signal.h>
+
+int
+main ()
+{
+return *(signal (0, 0)) (0) == 1;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_type_signal=int
+else
+ ac_cv_type_signal=void
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_signal" >&5
+$as_echo "$ac_cv_type_signal" >&6; }
+
+cat >>confdefs.h <<_ACEOF
+#define RETSIGTYPE $ac_cv_type_signal
+_ACEOF
+
+
+ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default"
+if test "x$ac_cv_type_size_t" = xyes; then :
+
+else
+
+cat >>confdefs.h <<_ACEOF
+#define size_t unsigned int
+_ACEOF
+
+fi
+
+ac_fn_c_check_type "$LINENO" "mode_t" "ac_cv_type_mode_t" "$ac_includes_default"
+if test "x$ac_cv_type_mode_t" = xyes; then :
+
+else
+
+cat >>confdefs.h <<_ACEOF
+#define mode_t int
+_ACEOF
+
+fi
+
+ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default"
+if test "x$ac_cv_type_pid_t" = xyes; then :
+
+else
+
+cat >>confdefs.h <<_ACEOF
+#define pid_t int
+_ACEOF
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for uid_t in sys/types.h" >&5
+$as_echo_n "checking for uid_t in sys/types.h... " >&6; }
+if ${ac_cv_type_uid_t+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "uid_t" >/dev/null 2>&1; then :
+ ac_cv_type_uid_t=yes
+else
+ ac_cv_type_uid_t=no
+fi
+rm -f conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_uid_t" >&5
+$as_echo "$ac_cv_type_uid_t" >&6; }
+if test $ac_cv_type_uid_t = no; then
+
+$as_echo "#define uid_t int" >>confdefs.h
+
+
+$as_echo "#define gid_t int" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether struct tm is in sys/time.h or time.h" >&5
+$as_echo_n "checking whether struct tm is in sys/time.h or time.h... " >&6; }
+if ${ac_cv_struct_tm+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <time.h>
+
+int
+main ()
+{
+struct tm tm;
+ int *p = &tm.tm_sec;
+ return !p;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_struct_tm=time.h
+else
+ ac_cv_struct_tm=sys/time.h
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_struct_tm" >&5
+$as_echo "$ac_cv_struct_tm" >&6; }
+if test $ac_cv_struct_tm = sys/time.h; then
+
+$as_echo "#define TM_IN_SYS_TIME 1" >>confdefs.h
+
+fi
+
+
+ac_fn_c_check_type "$LINENO" "union wait" "ac_cv_type_union_wait" "$ac_includes_default"
+if test "x$ac_cv_type_union_wait" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_UNION_WAIT 1
+_ACEOF
+
+
+fi
+
+
+for ac_header in stdint.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "stdint.h" "ac_cv_header_stdint_h" "$ac_includes_default"
+if test "x$ac_cv_header_stdint_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_STDINT_H 1
+_ACEOF
+ uint16=uint16_t
+else
+
+ for ac_header in inttypes.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "inttypes.h" "ac_cv_header_inttypes_h" "$ac_includes_default"
+if test "x$ac_cv_header_inttypes_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_INTTYPES_H 1
+_ACEOF
+ uint16=uint16_t
+else
+
+ for ac_header in sys/types.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "sys/types.h" "ac_cv_header_sys_types_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_types_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_SYS_TYPES_H 1
+_ACEOF
+ uint16=u_int16_t
+else
+
+ # The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned short" >&5
+$as_echo_n "checking size of unsigned short... " >&6; }
+if ${ac_cv_sizeof_unsigned_short+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned short))" "ac_cv_sizeof_unsigned_short" "$ac_includes_default"; then :
+
+else
+ if test "$ac_cv_type_unsigned_short" = yes; then
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (unsigned short)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_unsigned_short=0
+ fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_short" >&5
+$as_echo "$ac_cv_sizeof_unsigned_short" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF_UNSIGNED_SHORT $ac_cv_sizeof_unsigned_short
+_ACEOF
+
+
+ if test $ac_cv_sizeof_unsigned_short -eq 2 ; then
+ uint16="unsigned short"
+ else
+ # The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned int" >&5
+$as_echo_n "checking size of unsigned int... " >&6; }
+if ${ac_cv_sizeof_unsigned_int+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned int))" "ac_cv_sizeof_unsigned_int" "$ac_includes_default"; then :
+
+else
+ if test "$ac_cv_type_unsigned_int" = yes; then
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (unsigned int)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_unsigned_int=0
+ fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_int" >&5
+$as_echo "$ac_cv_sizeof_unsigned_int" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF_UNSIGNED_INT $ac_cv_sizeof_unsigned_int
+_ACEOF
+
+
+ if $ac_cv_sizeof_unsigned_int -eq 2 ; then
+ uint16="unsigned int"
+ else
+ as_fn_error $? "Unable to determine 16 bit integer type" "$LINENO" 5
+ fi
+ fi
+
+fi
+
+done
+
+
+fi
+
+done
+
+
+fi
+
+done
+
+
+cat >>confdefs.h <<_ACEOF
+#define UINT16 $uint16
+_ACEOF
+
+
+for ac_header in stdint.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "stdint.h" "ac_cv_header_stdint_h" "$ac_includes_default"
+if test "x$ac_cv_header_stdint_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_STDINT_H 1
+_ACEOF
+ uint32=uint32_t
+else
+
+ for ac_header in inttypes.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "inttypes.h" "ac_cv_header_inttypes_h" "$ac_includes_default"
+if test "x$ac_cv_header_inttypes_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_INTTYPES_H 1
+_ACEOF
+ uint32=uint32_t
+else
+
+ for ac_header in sys/types.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "sys/types.h" "ac_cv_header_sys_types_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_types_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_SYS_TYPES_H 1
+_ACEOF
+ uint32=u_int32_t
+else
+
+ # The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned int" >&5
+$as_echo_n "checking size of unsigned int... " >&6; }
+if ${ac_cv_sizeof_unsigned_int+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned int))" "ac_cv_sizeof_unsigned_int" "$ac_includes_default"; then :
+
+else
+ if test "$ac_cv_type_unsigned_int" = yes; then
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (unsigned int)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_unsigned_int=0
+ fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_int" >&5
+$as_echo "$ac_cv_sizeof_unsigned_int" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF_UNSIGNED_INT $ac_cv_sizeof_unsigned_int
+_ACEOF
+
+
+ if test $ac_cv_sizeof_unsigned_int -eq 4 ; then
+ uint32="unsigned int"
+ else
+ # The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned long" >&5
+$as_echo_n "checking size of unsigned long... " >&6; }
+if ${ac_cv_sizeof_unsigned_long+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned long))" "ac_cv_sizeof_unsigned_long" "$ac_includes_default"; then :
+
+else
+ if test "$ac_cv_type_unsigned_long" = yes; then
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (unsigned long)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_unsigned_long=0
+ fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_long" >&5
+$as_echo "$ac_cv_sizeof_unsigned_long" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF_UNSIGNED_LONG $ac_cv_sizeof_unsigned_long
+_ACEOF
+
+
+ if $ac_cv_sizeof_unsigned_long -eq 4 ; then
+ uint32="unsigned long"
+ else
+ as_fn_error $? "Unable to determine 32 bit integer type" "$LINENO" 5
+ fi
+ fi
+
+fi
+
+done
+
+
+fi
+
+done
+
+
+fi
+
+done
+
+
+cat >>confdefs.h <<_ACEOF
+#define UINT32 $uint32
+_ACEOF
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking argument pointer type of qsort compare function and base" >&5
+$as_echo_n "checking argument pointer type of qsort compare function and base... " >&6; }
+if ${ac_cv_func_qsort_argtype+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#if HAVE_STDLIB_H
+#include "stdlib.h"
+#endif
+
+extern void *base;
+extern sortf(const void *, const void *);
+int sortf(a, b)
+ const void *a;
+ const void *b; { return 0; }
+
+int
+main ()
+{
+
+qsort(base, 2, sizeof(char *), sortf);
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_func_qsort_argtype=void
+else
+ ac_cv_func_qsort_argtype=char
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_qsort_argtype" >&5
+$as_echo "$ac_cv_func_qsort_argtype" >&6; }
+
+cat >>confdefs.h <<_ACEOF
+#define qsort_t $ac_cv_func_qsort_argtype
+_ACEOF
+
+
+
+for ac_header in sys/select.h sys/socket.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking types of arguments for select" >&5
+$as_echo_n "checking types of arguments for select... " >&6; }
+if ${ac_cv_func_select_args+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ for ac_arg234 in 'fd_set *' 'int *' 'void *'; do
+ for ac_arg1 in 'int' 'size_t' 'unsigned long int' 'unsigned int'; do
+ for ac_arg5 in 'struct timeval *' 'const struct timeval *'; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_includes_default
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+
+int
+main ()
+{
+extern int select ($ac_arg1,
+ $ac_arg234, $ac_arg234, $ac_arg234,
+ $ac_arg5);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_func_select_args="$ac_arg1,$ac_arg234,$ac_arg5"; break 3
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ done
+ done
+done
+# Provide a safe default value.
+: "${ac_cv_func_select_args=int,int *,struct timeval *}"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_select_args" >&5
+$as_echo "$ac_cv_func_select_args" >&6; }
+ac_save_IFS=$IFS; IFS=','
+set dummy `echo "$ac_cv_func_select_args" | sed 's/\*/\*/g'`
+IFS=$ac_save_IFS
+shift
+
+cat >>confdefs.h <<_ACEOF
+#define SELECT_TYPE_ARG1 $1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define SELECT_TYPE_ARG234 ($2)
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define SELECT_TYPE_ARG5 ($3)
+_ACEOF
+
+rm -f conftest*
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working strcoll" >&5
+$as_echo_n "checking for working strcoll... " >&6; }
+if ${ac_cv_func_strcoll_works+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then :
+ ac_cv_func_strcoll_works=no
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+return (strcoll ("abc", "def") >= 0 ||
+ strcoll ("ABC", "DEF") >= 0 ||
+ strcoll ("123", "456") >= 0)
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ac_cv_func_strcoll_works=yes
+else
+ ac_cv_func_strcoll_works=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_strcoll_works" >&5
+$as_echo "$ac_cv_func_strcoll_works" >&6; }
+if test $ac_cv_func_strcoll_works = yes; then
+
+$as_echo "#define HAVE_STRCOLL 1" >>confdefs.h
+
+fi
+
+
+
+for ac_header in vfork.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "vfork.h" "ac_cv_header_vfork_h" "$ac_includes_default"
+if test "x$ac_cv_header_vfork_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_VFORK_H 1
+_ACEOF
+
+fi
+
+done
+
+for ac_func in fork vfork
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+if test "x$ac_cv_func_fork" = xyes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working fork" >&5
+$as_echo_n "checking for working fork... " >&6; }
+if ${ac_cv_func_fork_works+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then :
+ ac_cv_func_fork_works=cross
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+
+ /* By Ruediger Kuhlmann. */
+ return fork () < 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ac_cv_func_fork_works=yes
+else
+ ac_cv_func_fork_works=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_fork_works" >&5
+$as_echo "$ac_cv_func_fork_works" >&6; }
+
+else
+ ac_cv_func_fork_works=$ac_cv_func_fork
+fi
+if test "x$ac_cv_func_fork_works" = xcross; then
+ case $host in
+ *-*-amigaos* | *-*-msdosdjgpp*)
+ # Override, as these systems have only a dummy fork() stub
+ ac_cv_func_fork_works=no
+ ;;
+ *)
+ ac_cv_func_fork_works=yes
+ ;;
+ esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5
+$as_echo "$as_me: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&2;}
+fi
+ac_cv_func_vfork_works=$ac_cv_func_vfork
+if test "x$ac_cv_func_vfork" = xyes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working vfork" >&5
+$as_echo_n "checking for working vfork... " >&6; }
+if ${ac_cv_func_vfork_works+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then :
+ ac_cv_func_vfork_works=cross
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+/* Thanks to Paul Eggert for this test. */
+$ac_includes_default
+#include <sys/wait.h>
+#ifdef HAVE_VFORK_H
+# include <vfork.h>
+#endif
+/* On some sparc systems, changes by the child to local and incoming
+ argument registers are propagated back to the parent. The compiler
+ is told about this with #include <vfork.h>, but some compilers
+ (e.g. gcc -O) don't grok <vfork.h>. Test for this by using a
+ static variable whose address is put into a register that is
+ clobbered by the vfork. */
+static void
+#ifdef __cplusplus
+sparc_address_test (int arg)
+# else
+sparc_address_test (arg) int arg;
+#endif
+{
+ static pid_t child;
+ if (!child) {
+ child = vfork ();
+ if (child < 0) {
+ perror ("vfork");
+ _exit(2);
+ }
+ if (!child) {
+ arg = getpid();
+ write(-1, "", 0);
+ _exit (arg);
+ }
+ }
+}
+
+int
+main ()
+{
+ pid_t parent = getpid ();
+ pid_t child;
+
+ sparc_address_test (0);
+
+ child = vfork ();
+
+ if (child == 0) {
+ /* Here is another test for sparc vfork register problems. This
+ test uses lots of local variables, at least as many local
+ variables as main has allocated so far including compiler
+ temporaries. 4 locals are enough for gcc 1.40.3 on a Solaris
+ 4.1.3 sparc, but we use 8 to be safe. A buggy compiler should
+ reuse the register of parent for one of the local variables,
+ since it will think that parent can't possibly be used any more
+ in this routine. Assigning to the local variable will thus
+ munge parent in the parent process. */
+ pid_t
+ p = getpid(), p1 = getpid(), p2 = getpid(), p3 = getpid(),
+ p4 = getpid(), p5 = getpid(), p6 = getpid(), p7 = getpid();
+ /* Convince the compiler that p..p7 are live; otherwise, it might
+ use the same hardware register for all 8 local variables. */
+ if (p != p1 || p != p2 || p != p3 || p != p4
+ || p != p5 || p != p6 || p != p7)
+ _exit(1);
+
+ /* On some systems (e.g. IRIX 3.3), vfork doesn't separate parent
+ from child file descriptors. If the child closes a descriptor
+ before it execs or exits, this munges the parent's descriptor
+ as well. Test for this by closing stdout in the child. */
+ _exit(close(fileno(stdout)) != 0);
+ } else {
+ int status;
+ struct stat st;
+
+ while (wait(&status) != child)
+ ;
+ return (
+ /* Was there some problem with vforking? */
+ child < 0
+
+ /* Did the child fail? (This shouldn't happen.) */
+ || status
+
+ /* Did the vfork/compiler bug occur? */
+ || parent != getpid()
+
+ /* Did the file descriptor bug occur? */
+ || fstat(fileno(stdout), &st) != 0
+ );
+ }
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ac_cv_func_vfork_works=yes
+else
+ ac_cv_func_vfork_works=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_vfork_works" >&5
+$as_echo "$ac_cv_func_vfork_works" >&6; }
+
+fi;
+if test "x$ac_cv_func_fork_works" = xcross; then
+ ac_cv_func_vfork_works=$ac_cv_func_vfork
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5
+$as_echo "$as_me: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&2;}
+fi
+
+if test "x$ac_cv_func_vfork_works" = xyes; then
+
+$as_echo "#define HAVE_WORKING_VFORK 1" >>confdefs.h
+
+else
+
+$as_echo "#define vfork fork" >>confdefs.h
+
+fi
+if test "x$ac_cv_func_fork_works" = xyes; then
+
+$as_echo "#define HAVE_WORKING_FORK 1" >>confdefs.h
+
+fi
+
+for ac_func in strchr \
+ memcpy \
+ strtol \
+ strtoul \
+ select \
+ poll \
+ qsort \
+ getuid \
+ getpwuid \
+ getpwnam \
+ gettimeofday \
+ tmpfile \
+ uname \
+ rename \
+ read \
+ signal \
+ setjmp \
+ chown \
+ wait4 \
+ waitpid \
+ wait \
+ srandom \
+ popen \
+ pclose \
+ fsync \
+ truncate \
+ listen \
+ wcwidth \
+ mbstowcs \
+ wcrtomb \
+ putenv \
+ setenv
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing gethostname" >&5
+$as_echo_n "checking for library containing gethostname... " >&6; }
+if ${ac_cv_search_gethostname+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char gethostname ();
+int
+main ()
+{
+return gethostname ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' nsl; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_gethostname=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_gethostname+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_gethostname+:} false; then :
+
+else
+ ac_cv_search_gethostname=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_gethostname" >&5
+$as_echo "$ac_cv_search_gethostname" >&6; }
+ac_res=$ac_cv_search_gethostname
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing socket" >&5
+$as_echo_n "checking for library containing socket... " >&6; }
+if ${ac_cv_search_socket+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char socket ();
+int
+main ()
+{
+return socket ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' socket; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_socket=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_socket+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_socket+:} false; then :
+
+else
+ ac_cv_search_socket=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_socket" >&5
+$as_echo "$ac_cv_search_socket" >&6; }
+ac_res=$ac_cv_search_socket
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+else
+
+ WEB_BUILD=
+
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing bind" >&5
+$as_echo_n "checking for library containing bind... " >&6; }
+if ${ac_cv_search_bind+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char bind ();
+int
+main ()
+{
+return bind ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' bind; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_bind=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_bind+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_bind+:} false; then :
+
+else
+ ac_cv_search_bind=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_bind" >&5
+$as_echo "$ac_cv_search_bind" >&6; }
+ac_res=$ac_cv_search_bind
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+else
+
+ WEB_BUILD=
+
+fi
+
+
+for ac_func in sigaction sigemptyset sigaddset sigprocmask
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+
+$as_echo "#define POSIX_SIGNALS /**/" >>confdefs.h
+
+
+else
+
+ for ac_func in sigset sigrelse
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+
+$as_echo "#define SYSV_SIGNALS /**/" >>confdefs.h
+
+
+fi
+done
+
+
+fi
+done
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing syslog" >&5
+$as_echo_n "checking for library containing syslog... " >&6; }
+if ${ac_cv_search_syslog+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char syslog ();
+int
+main ()
+{
+return syslog ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' bsd socket inet; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_syslog=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_syslog+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_syslog+:} false; then :
+
+else
+ ac_cv_search_syslog=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_syslog" >&5
+$as_echo "$ac_cv_search_syslog" >&6; }
+ac_res=$ac_cv_search_syslog
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+
+$as_echo "#define HAVE_SYSLOG 1" >>confdefs.h
+
+
+fi
+
+
+
+case "$host" in
+ *-linux-gnu*|*-k*bsd*-gnu*)
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ if test -f /etc/fedora-release ; then
+ systype="LFD"
+ if test -d /etc/pki/tls ; then
+ alpine_c_client_target="lfd"
+ else
+ alpine_c_client_target="lrh"
+ fi
+ elif test -f /etc/mandrake-release ; then
+ systype="LMD"
+ alpine_c_client_target="lmd"
+ elif test -f /etc/redhat-release -o -f /etc/redhat_version ; then
+ systype="LRH"
+ if test -d /etc/pki/tls ; then
+ alpine_c_client_target="lr5"
+ else
+ alpine_c_client_target="lrh"
+ fi
+ elif test -f /etc/debian_release -o -f /etc/debian_version ; then
+ if test -d /etc/osso-af-init ; then
+ systype="LN8"
+ alpine_c_client_target="ln8"
+ else
+ systype="DEB"
+ alpine_c_client_target="ldb"
+ fi
+ elif test -f /etc/SuSE-release ; then
+ systype="LSU"
+ alpine_c_client_target="lsu"
+ else
+ systype="LNX"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pam_start in -lpam" >&5
+$as_echo_n "checking for pam_start in -lpam... " >&6; }
+if ${ac_cv_lib_pam_pam_start+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpam $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pam_start ();
+int
+main ()
+{
+return pam_start ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_pam_pam_start=yes
+else
+ ac_cv_lib_pam_pam_start=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pam_pam_start" >&5
+$as_echo "$ac_cv_lib_pam_pam_start" >&6; }
+if test "x$ac_cv_lib_pam_pam_start" = xyes; then :
+
+ alpine_c_client_target="lnp"
+
+else
+
+ if test -f /etc/shadow ; then
+ alpine_c_client_target="slx"
+ else
+ alpine_c_client_target="lnx"
+ fi
+
+fi
+
+ fi
+ ;;
+ *-apple-darwin*)
+ systype="OSX"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ LIBS="$LIBS -framework Carbon -framework ApplicationServices -framework Security"
+ AM_CFLAGS="$AM_CFLAGS -Dbsd"
+
+$as_echo "#define OSX_TARGET 1" >>confdefs.h
+
+ case "$alpine_os_credential_cache" in
+ no)
+ ;;
+ *)
+
+$as_echo "#define APPLEKEYCHAIN 1" >>confdefs.h
+
+ ;;
+ esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pam_start in -lpam" >&5
+$as_echo_n "checking for pam_start in -lpam... " >&6; }
+if ${ac_cv_lib_pam_pam_start+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpam $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pam_start ();
+int
+main ()
+{
+return pam_start ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_pam_pam_start=yes
+else
+ ac_cv_lib_pam_pam_start=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pam_pam_start" >&5
+$as_echo "$ac_cv_lib_pam_pam_start" >&6; }
+if test "x$ac_cv_lib_pam_pam_start" = xyes; then :
+
+ alpine_c_client_target="oxp"
+
+else
+
+ alpine_c_client_target="osx"
+
+fi
+
+ ;;
+ *-*-solaris*)
+ if test x$GCC = "xyes" ; then
+ systype="GSO"
+ alpine_c_client_target="gso"
+ else
+ systype="SOC"
+ alpine_c_client_target="soc"
+
+$as_echo "#define __EXTENSIONS__ 1" >>confdefs.h
+
+ fi
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ ;;
+ *-*-sunos4*)
+ systype="SUN"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="s40"
+ ;;
+ *-*-sco3.2v5*)
+ if test x$GCC = "xyes" ; then
+ systype="GO5"
+ alpine_c_client_target="go5"
+ else
+ systype="SC5"
+ alpine_c_client_target="sc5"
+ fi
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ ;;
+ *-next-*)
+ systype="NXT"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="nx3"
+ ;;
+ *-*-netbsd*)
+ systype="NEB"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="neb"
+ ;;
+ *-*-dragonfly*)
+ systype="DFB"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="neb"
+ ;;
+ *-*-bsdi*)
+ systype="BSI"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="bsi"
+ ;;
+ *-*-freebsd*)
+ systype="BSF"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="bsf"
+ LIBS="$LIBS $LIBINTL"
+ ;;
+ *-*-openbsd*)
+ systype="BSO"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="bso"
+ LIBS="$LIBS $LIBINTL"
+ ;;
+ *-*-aix5*)
+ systype="A52"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="a52"
+ ;;
+ *-*-aix4*)
+ systype="A41"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="a41"
+ ;;
+ *-*-aix3*)
+ systype="A32"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="a32"
+ ;;
+ *-*UNIX_SV | *-*-sysv5UnixWare7* | *-*OpenUNIX*)
+ systype="UW2"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="uw2"
+ ;;
+ *-*-osf5*)
+ systype="OSF"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="osf"
+ ;;
+ *-*-cygwin)
+ systype="CYG"
+ alpine_path_delim="\\\\"
+ alpine_mode_readonly="(S_IREAD | S_IWRITE)"
+ alpine_c_client_target="cyg"
+ LIBS="$LIBS $LIBINTL"
+ ;;
+ windows* | *-*-pw32*)
+ systype="WNT"
+ alpine_path_delim="\\"
+ alpine_mode_readonly="(S_IREAD | S_IWRITE)"
+ alpine_c_client_target="wnt"
+
+$as_echo "#define _WINDOWS 1" >>confdefs.h
+
+ ;;
+ *)
+ as_fn_error $? "Unrecognized system: $host" "$LINENO" 5
+ ;;
+esac
+
+if test -n "$alpine_with_local_maildir" ; then
+ alpine_local_maildir=$alpine_with_local_maildir
+elif test -d /var/spool/mail ; then
+ alpine_local_maildir="/var/spool/mail"
+elif test -d /var/mail ; then
+ alpine_local_maildir="/var/mail"
+else
+ alpine_local_maildir="/usr/spool/mail"
+fi
+
+if test -n "$alpine_with_c_client_target" ; then
+ alpine_c_client_target=$alpine_with_c_client_target
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define SYSTYPE "$systype"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define C_FILESEP '$alpine_path_delim'
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define S_FILESEP "$alpine_path_delim"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define MAILDIR "$alpine_local_maildir"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define MODE_READONLY $alpine_mode_readonly
+_ACEOF
+
+
+C_CLIENT_TARGET=$alpine_c_client_target
+
+C_CLIENT_WITH_IPV6=$c_client_ip6
+
+if test "x$alpine_SSLTYPE" = "xnone" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * NOT Including SSL Support" >&5
+$as_echo "$as_me: * * * NOT Including SSL Support" >&6;}
+ c_client_specials="${c_client_specials}SSLTYPE=none "
+else
+
+ if test -n "$alpine_SSLCERTS" -a -d "$alpine_SSLCERTS" ; then
+ certdir="$alpine_SSLCERTS"
+ elif test -n "$alpine_SSLDIR" -a -d "${alpine_SSLDIR}/certs" ; then
+ certdir="${alpine_SSLDIR}/certs"
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: SSL Problem: certificate directory not found" >&5
+$as_echo "$as_me: SSL Problem: certificate directory not found" >&6;}
+ fi
+
+ if test "x$with_smime" != "xno" ; then
+ if test -n "$certdir" ; then
+
+$as_echo "#define SMIME /**/" >>confdefs.h
+
+
+cat >>confdefs.h <<_ACEOF
+#define SMIME_SSLCERTS "$certdir"
+_ACEOF
+
+ fi
+ fi
+
+ if test ! -f ${certdir}/factory.pem ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * SSL file \"${certdir}/factory.pem\" is missing." >&5
+$as_echo "$as_me: * * * SSL file \"${certdir}/factory.pem\" is missing." >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * This might indicate that CA certs did not get properly" >&5
+$as_echo "$as_me: * * * This might indicate that CA certs did not get properly" >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * installed. If you get certificate validation failures" >&5
+$as_echo "$as_me: * * * installed. If you get certificate validation failures" >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * in Alpine, this might be the reason for them." >&5
+$as_echo "$as_me: * * * in Alpine, this might be the reason for them." >&6;}
+ fi
+
+ if test -z "`ls ${certdir} | $EGREP '^[0-9A-Fa-f]{8}\.[0-9]'`" ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * No 8-hexdigit symlinks in certificate directory \"${certdir}\"." >&5
+$as_echo "$as_me: * * * No 8-hexdigit symlinks in certificate directory \"${certdir}\"." >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * This might indicate that CA certs did not get properly" >&5
+$as_echo "$as_me: * * * This might indicate that CA certs did not get properly" >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * installed. If you get certificate validation failures" >&5
+$as_echo "$as_me: * * * installed. If you get certificate validation failures" >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * in Alpine, this might be the reason for them." >&5
+$as_echo "$as_me: * * * in Alpine, this might be the reason for them." >&6;}
+ fi
+
+ if test -n "$alpine_SSLDIR" ; then
+ c_client_specials="${c_client_specials}SSLDIR=$alpine_SSLDIR "
+ fi
+
+ if test -n "$alpine_SSLCERTS" ; then
+ c_client_specials="${c_client_specials}SSLCERTS=$alpine_SSLCERTS "
+ fi
+
+ if test -n "$alpine_SSLINCLUDE" ; then
+ c_client_specials="${c_client_specials}SSLINCLUDE=$alpine_SSLINCLUDE "
+ fi
+
+ if test -n "$alpine_SSLLIB" ; then
+ c_client_specials="${c_client_specials}SSLLIB=$alpine_SSLLIB "
+ fi
+
+fi
+
+if test "x$alpine_GSSTYPE" != "xnone" ; then
+ c_client_specials="${c_client_specials}EXTRAAUTHENTICATORS=gss "
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * Including Kerberos5 functionality" >&5
+$as_echo "$as_me: * * * Including Kerberos5 functionality" >&6;}
+fi
+
+if test -n "$CPPFLAGS" ; then
+ alpine_c_client_cflags="$alpine_c_client_cflags ${CPPFLAGS}"
+fi
+if test -n "$CFLAGS" ; then
+ alpine_c_client_cflags="$alpine_c_client_cflags ${CFLAGS}"
+fi
+
+if test -n "$alpine_c_client_cflags" ; then
+ C_CLIENT_CFLAGS=EXTRACFLAGS=\"$alpine_c_client_cflags\"
+
+fi
+
+if test -n "$LDFLAGS" ; then
+ alpine_c_client_ldflags="$alpine_c_client_ldflags ${LDFLAGS}"
+fi
+if test -n "$LIBS" ; then
+ alpine_c_client_ldflags="$alpine_c_client_ldflags ${LIBS}"
+fi
+
+if test -n "$alpine_c_client_ldflags" ; then
+ C_CLIENT_LDFLAGS=EXTRALDFLAGS=\"$alpine_c_client_ldflags\"
+
+fi
+
+if test -n "$alpine_c_client_gccoptlevel" ; then
+ C_CLIENT_GCCOPTLEVEL=GCCOPTLEVEL=\"$alpine_c_client_gccoptlevel\"
+
+fi
+
+C_CLIENT_SPECIALS=$c_client_specials
+
+
+if test -z "$WEB_BUILD" ; then
+ WEB_PUBCOOKIE_BUILD=
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * TCL libraries could not be found." >&5
+$as_echo "$as_me: * * * TCL libraries could not be found." >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * WEB ALPINE COMPONENT WILL NOT BE BUILT." >&5
+$as_echo "$as_me: * * * WEB ALPINE COMPONENT WILL NOT BE BUILT." >&6;}
+else
+ if test -n "$WEB_PUBCOOKIE_BUILD" ; then
+ if test "x$alpine_GSSTYPE" = "xnone" ; then
+ WEB_PUBCOOKIE_BUILD=
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * Kerberos5 support not found." >&5
+$as_echo "$as_me: * * * Kerberos5 support not found." >&6;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * WEB ALPINE PUBCOOKIE COMPONENT WILL NOT BE BUILT." >&5
+$as_echo "$as_me: * * * WEB ALPINE PUBCOOKIE COMPONENT WILL NOT BE BUILT." >&6;}
+ elif test -z "$WEB_BINDIR" ; then
+ WEB_PUBCOOKIE_BUILD=
+ { $as_echo "$as_me:${as_lineno-$LINENO}: * * * Web Alpine binary directory not provided." >&5
+$as_echo "$as_me: * * * Web Alpine binary directory not provided." >&6;}
+ as_fn_error $? "* * * --with-pubcookie requires --with-web-bin=PATH.
+ Please re-run configure with these options:
+ --with-pubcookie --with-web-bin=/usr/local/libexec/alpine/bin" "$LINENO" 5
+ else
+
+$as_echo "#define PUBCOOKIE 1" >>confdefs.h
+
+ WEB_PUBCOOKIE_LIB=../pubcookie/libauthgssproxy.a
+ WEB_PUBCOOKIE_LINK=gssapi_proxy.l
+ fi
+ fi
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+ac_config_files="$ac_config_files m4/Makefile po/Makefile.in regex/Makefile pith/osdep/Makefile pith/charconv/Makefile pith/Makefile pico/osdep/Makefile pico/Makefile alpine/osdep/Makefile alpine/Makefile web/src/Makefile web/src/pubcookie/Makefile web/src/alpined.d/Makefile Makefile"
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems. If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+ for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+ *) { eval $ac_var=; unset $ac_var;} ;;
+ esac ;;
+ esac
+ done
+
+ (set) 2>&1 |
+ case $as_nl`(ac_space=' '; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ # `set' does not quote correctly, so add quotes: double-quote
+ # substitution turns \\\\ into \\, and sed turns \\ into \.
+ sed -n \
+ "s/'/'\\\\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+ ;; #(
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+) |
+ sed '
+ /^ac_cv_env_/b end
+ t clear
+ :clear
+ s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+ t end
+ s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+ :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+ if test -w "$cache_file"; then
+ if test "x$cache_file" != "x/dev/null"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+$as_echo "$as_me: updating cache $cache_file" >&6;}
+ if test ! -f "$cache_file" || test -h "$cache_file"; then
+ cat confcache >"$cache_file"
+ else
+ case $cache_file in #(
+ */* | ?:*)
+ mv -f confcache "$cache_file"$$ &&
+ mv -f "$cache_file"$$ "$cache_file" ;; #(
+ *)
+ mv -f confcache "$cache_file" ;;
+ esac
+ fi
+ fi
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+ fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+U=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+ # 1. Remove the extension, and $U if already installed.
+ ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+ ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
+ # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR
+ # will be set to the directory where LIBOBJS objects are built.
+ as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+ as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+ if test -n "$EXEEXT"; then
+ am__EXEEXT_TRUE=
+ am__EXEEXT_FALSE='#'
+else
+ am__EXEEXT_TRUE='#'
+ am__EXEEXT_FALSE=
+fi
+
+if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then
+ as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then
+ as_fn_error $? "conditional \"AMDEP\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
+ as_fn_error $? "conditional \"am__fastdepCC\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+
+: "${CONFIG_STATUS=./config.status}"
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
+as_write_fail=0
+cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in #(
+ *posix*) :
+ set -o posix ;; #(
+ *) :
+ ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='print -r --'
+ as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='printf %s\n'
+ as_echo_n='printf %s'
+else
+ if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+ as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+ as_echo_n='/usr/ucb/echo -n'
+ else
+ as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+ as_echo_n_body='eval
+ arg=$1;
+ case $arg in #(
+ *"$as_nl"*)
+ expr "X$arg" : "X\\(.*\\)$as_nl";
+ arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+ esac;
+ expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+ '
+ export as_echo_n_body
+ as_echo_n='sh -c $as_echo_n_body as_echo'
+ fi
+ export as_echo_body
+ as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there. '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+ as_status=$1; test $as_status -eq 0 && as_status=1
+ if test "$4"; then
+ as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+ fi
+ $as_echo "$as_me: error: $2" >&2
+ as_fn_exit $as_status
+} # as_fn_error
+
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+ return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+ set +e
+ as_fn_set_status $1
+ exit $1
+} # as_fn_exit
+
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+ { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+ eval 'as_fn_append ()
+ {
+ eval $1+=\$2
+ }'
+else
+ as_fn_append ()
+ {
+ eval $1=\$$1\$2
+ }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+ eval 'as_fn_arith ()
+ {
+ as_val=$(( $* ))
+ }'
+else
+ as_fn_arith ()
+ {
+ as_val=`expr "$@" || test $? -eq 1`
+ }
+fi # as_fn_arith
+
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+ case `echo 'xy\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ xy) ECHO_C='\c';;
+ *) echo `echo ksh88 bug on AIX 6.1` > /dev/null
+ ECHO_T=' ';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+ if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -p'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -p'
+ elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+ else
+ as_ln_s='cp -p'
+ fi
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || eval $as_mkdir_p || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p='mkdir -p "$as_dir"'
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+if test -x / >/dev/null 2>&1; then
+ as_test_x='test -x'
+else
+ if ls -dL / >/dev/null 2>&1; then
+ as_ls_L_option=L
+ else
+ as_ls_L_option=
+ fi
+ as_test_x='
+ eval sh -c '\''
+ if test -d "$1"; then
+ test -d "$1/.";
+ else
+ case $1 in #(
+ -*)set "./$1";;
+ esac;
+ case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #((
+ ???[sx]*):;;*)false;;esac;fi
+ '\'' sh
+ '
+fi
+as_executable_p=$as_test_x
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+## ----------------------------------- ##
+## Main body of $CONFIG_STATUS script. ##
+## ----------------------------------- ##
+_ASEOF
+test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# Save the log message, to keep $0 and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by alpine $as_me 2.10, which was
+generated by GNU Autoconf 2.68. Invocation command line was
+
+ CONFIG_FILES = $CONFIG_FILES
+ CONFIG_HEADERS = $CONFIG_HEADERS
+ CONFIG_LINKS = $CONFIG_LINKS
+ CONFIG_COMMANDS = $CONFIG_COMMANDS
+ $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+case $ac_config_headers in *"
+"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
+esac
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+config_headers="$ac_config_headers"
+config_commands="$ac_config_commands"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files and other configuration actions
+from templates according to the current configuration. Unless the files
+and actions are specified as TAGs, all are instantiated by default.
+
+Usage: $0 [OPTION]... [TAG]...
+
+ -h, --help print this help, then exit
+ -V, --version print version number and configuration settings, then exit
+ --config print configuration, then exit
+ -q, --quiet, --silent
+ do not print progress messages
+ -d, --debug don't remove temporary files
+ --recheck update $as_me by reconfiguring in the same conditions
+ --file=FILE[:TEMPLATE]
+ instantiate the configuration file FILE
+ --header=FILE[:TEMPLATE]
+ instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Configuration commands:
+$config_commands
+
+Report bugs to <alpine-contact@u.washington.edu>."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
+ac_cs_version="\\
+alpine config.status 2.10
+configured by $0, generated by GNU Autoconf 2.68,
+ with options \\"\$ac_cs_config\\"
+
+Copyright (C) 2010 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+INSTALL='$INSTALL'
+MKDIR_P='$MKDIR_P'
+AWK='$AWK'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+ case $1 in
+ --*=?*)
+ ac_option=`expr "X$1" : 'X\([^=]*\)='`
+ ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+ ac_shift=:
+ ;;
+ --*=)
+ ac_option=`expr "X$1" : 'X\([^=]*\)='`
+ ac_optarg=
+ ac_shift=:
+ ;;
+ *)
+ ac_option=$1
+ ac_optarg=$2
+ ac_shift=shift
+ ;;
+ esac
+
+ case $ac_option in
+ # Handling of the options.
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ ac_cs_recheck=: ;;
+ --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+ $as_echo "$ac_cs_version"; exit ;;
+ --config | --confi | --conf | --con | --co | --c )
+ $as_echo "$ac_cs_config"; exit ;;
+ --debug | --debu | --deb | --de | --d | -d )
+ debug=: ;;
+ --file | --fil | --fi | --f )
+ $ac_shift
+ case $ac_optarg in
+ *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ '') as_fn_error $? "missing file argument" ;;
+ esac
+ as_fn_append CONFIG_FILES " '$ac_optarg'"
+ ac_need_defaults=false;;
+ --header | --heade | --head | --hea )
+ $ac_shift
+ case $ac_optarg in
+ *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ as_fn_append CONFIG_HEADERS " '$ac_optarg'"
+ ac_need_defaults=false;;
+ --he | --h)
+ # Conflict between --help and --header
+ as_fn_error $? "ambiguous option: \`$1'
+Try \`$0 --help' for more information.";;
+ --help | --hel | -h )
+ $as_echo "$ac_cs_usage"; exit ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil | --si | --s)
+ ac_cs_silent=: ;;
+
+ # This is an error.
+ -*) as_fn_error $? "unrecognized option: \`$1'
+Try \`$0 --help' for more information." ;;
+
+ *) as_fn_append ac_config_targets " $1"
+ ac_need_defaults=false ;;
+
+ esac
+ shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+ exec 6>/dev/null
+ ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+ set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+ shift
+ \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
+ CONFIG_SHELL='$SHELL'
+ export CONFIG_SHELL
+ exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+ echo
+ sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+ $as_echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+#
+# INIT-COMMANDS
+#
+AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"
+
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+sed_quote_subst='$sed_quote_subst'
+double_quote_subst='$double_quote_subst'
+delay_variable_subst='$delay_variable_subst'
+macro_version='`$ECHO "X$macro_version" | $Xsed -e "$delay_single_quote_subst"`'
+macro_revision='`$ECHO "X$macro_revision" | $Xsed -e "$delay_single_quote_subst"`'
+enable_shared='`$ECHO "X$enable_shared" | $Xsed -e "$delay_single_quote_subst"`'
+enable_static='`$ECHO "X$enable_static" | $Xsed -e "$delay_single_quote_subst"`'
+pic_mode='`$ECHO "X$pic_mode" | $Xsed -e "$delay_single_quote_subst"`'
+enable_fast_install='`$ECHO "X$enable_fast_install" | $Xsed -e "$delay_single_quote_subst"`'
+host_alias='`$ECHO "X$host_alias" | $Xsed -e "$delay_single_quote_subst"`'
+host='`$ECHO "X$host" | $Xsed -e "$delay_single_quote_subst"`'
+host_os='`$ECHO "X$host_os" | $Xsed -e "$delay_single_quote_subst"`'
+build_alias='`$ECHO "X$build_alias" | $Xsed -e "$delay_single_quote_subst"`'
+build='`$ECHO "X$build" | $Xsed -e "$delay_single_quote_subst"`'
+build_os='`$ECHO "X$build_os" | $Xsed -e "$delay_single_quote_subst"`'
+SED='`$ECHO "X$SED" | $Xsed -e "$delay_single_quote_subst"`'
+Xsed='`$ECHO "X$Xsed" | $Xsed -e "$delay_single_quote_subst"`'
+GREP='`$ECHO "X$GREP" | $Xsed -e "$delay_single_quote_subst"`'
+EGREP='`$ECHO "X$EGREP" | $Xsed -e "$delay_single_quote_subst"`'
+FGREP='`$ECHO "X$FGREP" | $Xsed -e "$delay_single_quote_subst"`'
+LD='`$ECHO "X$LD" | $Xsed -e "$delay_single_quote_subst"`'
+NM='`$ECHO "X$NM" | $Xsed -e "$delay_single_quote_subst"`'
+LN_S='`$ECHO "X$LN_S" | $Xsed -e "$delay_single_quote_subst"`'
+max_cmd_len='`$ECHO "X$max_cmd_len" | $Xsed -e "$delay_single_quote_subst"`'
+ac_objext='`$ECHO "X$ac_objext" | $Xsed -e "$delay_single_quote_subst"`'
+exeext='`$ECHO "X$exeext" | $Xsed -e "$delay_single_quote_subst"`'
+lt_unset='`$ECHO "X$lt_unset" | $Xsed -e "$delay_single_quote_subst"`'
+lt_SP2NL='`$ECHO "X$lt_SP2NL" | $Xsed -e "$delay_single_quote_subst"`'
+lt_NL2SP='`$ECHO "X$lt_NL2SP" | $Xsed -e "$delay_single_quote_subst"`'
+reload_flag='`$ECHO "X$reload_flag" | $Xsed -e "$delay_single_quote_subst"`'
+reload_cmds='`$ECHO "X$reload_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+OBJDUMP='`$ECHO "X$OBJDUMP" | $Xsed -e "$delay_single_quote_subst"`'
+deplibs_check_method='`$ECHO "X$deplibs_check_method" | $Xsed -e "$delay_single_quote_subst"`'
+file_magic_cmd='`$ECHO "X$file_magic_cmd" | $Xsed -e "$delay_single_quote_subst"`'
+AR='`$ECHO "X$AR" | $Xsed -e "$delay_single_quote_subst"`'
+AR_FLAGS='`$ECHO "X$AR_FLAGS" | $Xsed -e "$delay_single_quote_subst"`'
+STRIP='`$ECHO "X$STRIP" | $Xsed -e "$delay_single_quote_subst"`'
+RANLIB='`$ECHO "X$RANLIB" | $Xsed -e "$delay_single_quote_subst"`'
+old_postinstall_cmds='`$ECHO "X$old_postinstall_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+old_postuninstall_cmds='`$ECHO "X$old_postuninstall_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+old_archive_cmds='`$ECHO "X$old_archive_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+CC='`$ECHO "X$CC" | $Xsed -e "$delay_single_quote_subst"`'
+CFLAGS='`$ECHO "X$CFLAGS" | $Xsed -e "$delay_single_quote_subst"`'
+compiler='`$ECHO "X$compiler" | $Xsed -e "$delay_single_quote_subst"`'
+GCC='`$ECHO "X$GCC" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_pipe='`$ECHO "X$lt_cv_sys_global_symbol_pipe" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_cdecl='`$ECHO "X$lt_cv_sys_global_symbol_to_cdecl" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "X$lt_cv_sys_global_symbol_to_c_name_address" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "X$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $Xsed -e "$delay_single_quote_subst"`'
+objdir='`$ECHO "X$objdir" | $Xsed -e "$delay_single_quote_subst"`'
+SHELL='`$ECHO "X$SHELL" | $Xsed -e "$delay_single_quote_subst"`'
+ECHO='`$ECHO "X$ECHO" | $Xsed -e "$delay_single_quote_subst"`'
+MAGIC_CMD='`$ECHO "X$MAGIC_CMD" | $Xsed -e "$delay_single_quote_subst"`'
+lt_prog_compiler_no_builtin_flag='`$ECHO "X$lt_prog_compiler_no_builtin_flag" | $Xsed -e "$delay_single_quote_subst"`'
+lt_prog_compiler_wl='`$ECHO "X$lt_prog_compiler_wl" | $Xsed -e "$delay_single_quote_subst"`'
+lt_prog_compiler_pic='`$ECHO "X$lt_prog_compiler_pic" | $Xsed -e "$delay_single_quote_subst"`'
+lt_prog_compiler_static='`$ECHO "X$lt_prog_compiler_static" | $Xsed -e "$delay_single_quote_subst"`'
+lt_cv_prog_compiler_c_o='`$ECHO "X$lt_cv_prog_compiler_c_o" | $Xsed -e "$delay_single_quote_subst"`'
+need_locks='`$ECHO "X$need_locks" | $Xsed -e "$delay_single_quote_subst"`'
+DSYMUTIL='`$ECHO "X$DSYMUTIL" | $Xsed -e "$delay_single_quote_subst"`'
+NMEDIT='`$ECHO "X$NMEDIT" | $Xsed -e "$delay_single_quote_subst"`'
+LIPO='`$ECHO "X$LIPO" | $Xsed -e "$delay_single_quote_subst"`'
+OTOOL='`$ECHO "X$OTOOL" | $Xsed -e "$delay_single_quote_subst"`'
+OTOOL64='`$ECHO "X$OTOOL64" | $Xsed -e "$delay_single_quote_subst"`'
+libext='`$ECHO "X$libext" | $Xsed -e "$delay_single_quote_subst"`'
+shrext_cmds='`$ECHO "X$shrext_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+extract_expsyms_cmds='`$ECHO "X$extract_expsyms_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+archive_cmds_need_lc='`$ECHO "X$archive_cmds_need_lc" | $Xsed -e "$delay_single_quote_subst"`'
+enable_shared_with_static_runtimes='`$ECHO "X$enable_shared_with_static_runtimes" | $Xsed -e "$delay_single_quote_subst"`'
+export_dynamic_flag_spec='`$ECHO "X$export_dynamic_flag_spec" | $Xsed -e "$delay_single_quote_subst"`'
+whole_archive_flag_spec='`$ECHO "X$whole_archive_flag_spec" | $Xsed -e "$delay_single_quote_subst"`'
+compiler_needs_object='`$ECHO "X$compiler_needs_object" | $Xsed -e "$delay_single_quote_subst"`'
+old_archive_from_new_cmds='`$ECHO "X$old_archive_from_new_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+old_archive_from_expsyms_cmds='`$ECHO "X$old_archive_from_expsyms_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+archive_cmds='`$ECHO "X$archive_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+archive_expsym_cmds='`$ECHO "X$archive_expsym_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+module_cmds='`$ECHO "X$module_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+module_expsym_cmds='`$ECHO "X$module_expsym_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+with_gnu_ld='`$ECHO "X$with_gnu_ld" | $Xsed -e "$delay_single_quote_subst"`'
+allow_undefined_flag='`$ECHO "X$allow_undefined_flag" | $Xsed -e "$delay_single_quote_subst"`'
+no_undefined_flag='`$ECHO "X$no_undefined_flag" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_libdir_flag_spec='`$ECHO "X$hardcode_libdir_flag_spec" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_libdir_flag_spec_ld='`$ECHO "X$hardcode_libdir_flag_spec_ld" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_libdir_separator='`$ECHO "X$hardcode_libdir_separator" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_direct='`$ECHO "X$hardcode_direct" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_direct_absolute='`$ECHO "X$hardcode_direct_absolute" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_minus_L='`$ECHO "X$hardcode_minus_L" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_shlibpath_var='`$ECHO "X$hardcode_shlibpath_var" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_automatic='`$ECHO "X$hardcode_automatic" | $Xsed -e "$delay_single_quote_subst"`'
+inherit_rpath='`$ECHO "X$inherit_rpath" | $Xsed -e "$delay_single_quote_subst"`'
+link_all_deplibs='`$ECHO "X$link_all_deplibs" | $Xsed -e "$delay_single_quote_subst"`'
+fix_srcfile_path='`$ECHO "X$fix_srcfile_path" | $Xsed -e "$delay_single_quote_subst"`'
+always_export_symbols='`$ECHO "X$always_export_symbols" | $Xsed -e "$delay_single_quote_subst"`'
+export_symbols_cmds='`$ECHO "X$export_symbols_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+exclude_expsyms='`$ECHO "X$exclude_expsyms" | $Xsed -e "$delay_single_quote_subst"`'
+include_expsyms='`$ECHO "X$include_expsyms" | $Xsed -e "$delay_single_quote_subst"`'
+prelink_cmds='`$ECHO "X$prelink_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+file_list_spec='`$ECHO "X$file_list_spec" | $Xsed -e "$delay_single_quote_subst"`'
+variables_saved_for_relink='`$ECHO "X$variables_saved_for_relink" | $Xsed -e "$delay_single_quote_subst"`'
+need_lib_prefix='`$ECHO "X$need_lib_prefix" | $Xsed -e "$delay_single_quote_subst"`'
+need_version='`$ECHO "X$need_version" | $Xsed -e "$delay_single_quote_subst"`'
+version_type='`$ECHO "X$version_type" | $Xsed -e "$delay_single_quote_subst"`'
+runpath_var='`$ECHO "X$runpath_var" | $Xsed -e "$delay_single_quote_subst"`'
+shlibpath_var='`$ECHO "X$shlibpath_var" | $Xsed -e "$delay_single_quote_subst"`'
+shlibpath_overrides_runpath='`$ECHO "X$shlibpath_overrides_runpath" | $Xsed -e "$delay_single_quote_subst"`'
+libname_spec='`$ECHO "X$libname_spec" | $Xsed -e "$delay_single_quote_subst"`'
+library_names_spec='`$ECHO "X$library_names_spec" | $Xsed -e "$delay_single_quote_subst"`'
+soname_spec='`$ECHO "X$soname_spec" | $Xsed -e "$delay_single_quote_subst"`'
+postinstall_cmds='`$ECHO "X$postinstall_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+postuninstall_cmds='`$ECHO "X$postuninstall_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+finish_cmds='`$ECHO "X$finish_cmds" | $Xsed -e "$delay_single_quote_subst"`'
+finish_eval='`$ECHO "X$finish_eval" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_into_libs='`$ECHO "X$hardcode_into_libs" | $Xsed -e "$delay_single_quote_subst"`'
+sys_lib_search_path_spec='`$ECHO "X$sys_lib_search_path_spec" | $Xsed -e "$delay_single_quote_subst"`'
+sys_lib_dlsearch_path_spec='`$ECHO "X$sys_lib_dlsearch_path_spec" | $Xsed -e "$delay_single_quote_subst"`'
+hardcode_action='`$ECHO "X$hardcode_action" | $Xsed -e "$delay_single_quote_subst"`'
+enable_dlopen='`$ECHO "X$enable_dlopen" | $Xsed -e "$delay_single_quote_subst"`'
+enable_dlopen_self='`$ECHO "X$enable_dlopen_self" | $Xsed -e "$delay_single_quote_subst"`'
+enable_dlopen_self_static='`$ECHO "X$enable_dlopen_self_static" | $Xsed -e "$delay_single_quote_subst"`'
+old_striplib='`$ECHO "X$old_striplib" | $Xsed -e "$delay_single_quote_subst"`'
+striplib='`$ECHO "X$striplib" | $Xsed -e "$delay_single_quote_subst"`'
+
+LTCC='$LTCC'
+LTCFLAGS='$LTCFLAGS'
+compiler='$compiler_DEFAULT'
+
+# Quote evaled strings.
+for var in SED \
+GREP \
+EGREP \
+FGREP \
+LD \
+NM \
+LN_S \
+lt_SP2NL \
+lt_NL2SP \
+reload_flag \
+OBJDUMP \
+deplibs_check_method \
+file_magic_cmd \
+AR \
+AR_FLAGS \
+STRIP \
+RANLIB \
+CC \
+CFLAGS \
+compiler \
+lt_cv_sys_global_symbol_pipe \
+lt_cv_sys_global_symbol_to_cdecl \
+lt_cv_sys_global_symbol_to_c_name_address \
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \
+SHELL \
+ECHO \
+lt_prog_compiler_no_builtin_flag \
+lt_prog_compiler_wl \
+lt_prog_compiler_pic \
+lt_prog_compiler_static \
+lt_cv_prog_compiler_c_o \
+need_locks \
+DSYMUTIL \
+NMEDIT \
+LIPO \
+OTOOL \
+OTOOL64 \
+shrext_cmds \
+export_dynamic_flag_spec \
+whole_archive_flag_spec \
+compiler_needs_object \
+with_gnu_ld \
+allow_undefined_flag \
+no_undefined_flag \
+hardcode_libdir_flag_spec \
+hardcode_libdir_flag_spec_ld \
+hardcode_libdir_separator \
+fix_srcfile_path \
+exclude_expsyms \
+include_expsyms \
+file_list_spec \
+variables_saved_for_relink \
+libname_spec \
+library_names_spec \
+soname_spec \
+finish_eval \
+old_striplib \
+striplib; do
+ case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in
+ *[\\\\\\\`\\"\\\$]*)
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$sed_quote_subst\\"\\\`\\\\\\""
+ ;;
+ *)
+ eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+ ;;
+ esac
+done
+
+# Double-quote double-evaled strings.
+for var in reload_cmds \
+old_postinstall_cmds \
+old_postuninstall_cmds \
+old_archive_cmds \
+extract_expsyms_cmds \
+old_archive_from_new_cmds \
+old_archive_from_expsyms_cmds \
+archive_cmds \
+archive_expsym_cmds \
+module_cmds \
+module_expsym_cmds \
+export_symbols_cmds \
+prelink_cmds \
+postinstall_cmds \
+postuninstall_cmds \
+finish_cmds \
+sys_lib_search_path_spec \
+sys_lib_dlsearch_path_spec; do
+ case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in
+ *[\\\\\\\`\\"\\\$]*)
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\""
+ ;;
+ *)
+ eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+ ;;
+ esac
+done
+
+# Fix-up fallback echo if it was mangled by the above quoting rules.
+case \$lt_ECHO in
+*'\\\$0 --fallback-echo"') lt_ECHO=\`\$ECHO "X\$lt_ECHO" | \$Xsed -e 's/\\\\\\\\\\\\\\\$0 --fallback-echo"\$/\$0 --fallback-echo"/'\`
+ ;;
+esac
+
+ac_aux_dir='$ac_aux_dir'
+xsi_shell='$xsi_shell'
+lt_shell_append='$lt_shell_append'
+
+# See if we are running on zsh, and set the options which allow our
+# commands through without removal of \ escapes INIT.
+if test -n "\${ZSH_VERSION+set}" ; then
+ setopt NO_GLOB_SUBST
+fi
+
+
+ PACKAGE='$PACKAGE'
+ VERSION='$VERSION'
+ TIMESTAMP='$TIMESTAMP'
+ RM='$RM'
+ ofile='$ofile'
+
+
+
+# Capture the value of obsolete ALL_LINGUAS because we need it to compute
+ # POFILES, UPDATEPOFILES, DUMMYPOFILES, GMOFILES, CATALOGS. But hide it
+ # from automake < 1.5.
+ eval 'OBSOLETE_ALL_LINGUAS''="$ALL_LINGUAS"'
+ # Capture the value of LINGUAS because we need it to compute CATALOGS.
+ LINGUAS="${LINGUAS-%UNSET%}"
+
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+ case $ac_config_target in
+ "include/config.h") CONFIG_HEADERS="$CONFIG_HEADERS include/config.h" ;;
+ "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;;
+ "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;;
+ "po-directories") CONFIG_COMMANDS="$CONFIG_COMMANDS po-directories" ;;
+ "m4/Makefile") CONFIG_FILES="$CONFIG_FILES m4/Makefile" ;;
+ "po/Makefile.in") CONFIG_FILES="$CONFIG_FILES po/Makefile.in" ;;
+ "regex/Makefile") CONFIG_FILES="$CONFIG_FILES regex/Makefile" ;;
+ "pith/osdep/Makefile") CONFIG_FILES="$CONFIG_FILES pith/osdep/Makefile" ;;
+ "pith/charconv/Makefile") CONFIG_FILES="$CONFIG_FILES pith/charconv/Makefile" ;;
+ "pith/Makefile") CONFIG_FILES="$CONFIG_FILES pith/Makefile" ;;
+ "pico/osdep/Makefile") CONFIG_FILES="$CONFIG_FILES pico/osdep/Makefile" ;;
+ "pico/Makefile") CONFIG_FILES="$CONFIG_FILES pico/Makefile" ;;
+ "alpine/osdep/Makefile") CONFIG_FILES="$CONFIG_FILES alpine/osdep/Makefile" ;;
+ "alpine/Makefile") CONFIG_FILES="$CONFIG_FILES alpine/Makefile" ;;
+ "web/src/Makefile") CONFIG_FILES="$CONFIG_FILES web/src/Makefile" ;;
+ "web/src/pubcookie/Makefile") CONFIG_FILES="$CONFIG_FILES web/src/pubcookie/Makefile" ;;
+ "web/src/alpined.d/Makefile") CONFIG_FILES="$CONFIG_FILES web/src/alpined.d/Makefile" ;;
+ "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+
+ *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+ esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used. Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+ test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+ test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+ test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands
+fi
+
+# Have a temporary directory for convenience. Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+ tmp= ac_tmp=
+ trap 'exit_status=$?
+ : "${ac_tmp:=$tmp}"
+ { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
+' 0
+ trap 'as_fn_exit 1' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+ tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+ test -d "$tmp"
+} ||
+{
+ tmp=./conf$$-$RANDOM
+ (umask 077 && mkdir "$tmp")
+} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
+ac_tmp=$tmp
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr=`echo X | tr X '\015'`
+# On cygwin, bash can eat \r inside `` if the user requested igncr.
+# But we know of no other shell where ac_cr would be empty at this
+# point, so we can use a bashism as a fallback.
+if test "x$ac_cr" = x; then
+ eval ac_cr=\$\'\\r\'
+fi
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+ ac_cs_awk_cr='\\r'
+else
+ ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+ echo "cat >conf$$subs.awk <<_ACEOF" &&
+ echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+ echo "_ACEOF"
+} >conf$$subs.sh ||
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+ . ./conf$$subs.sh ||
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+
+ ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+ if test $ac_delim_n = $ac_delim_num; then
+ break
+ elif $ac_last_try; then
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\)..*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\)..*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+ N
+ s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
+ for (key in S) S_is_set[key] = 1
+ FS = ""
+
+}
+{
+ line = $ 0
+ nfields = split(line, field, "@")
+ substed = 0
+ len = length(field[1])
+ for (i = 2; i < nfields; i++) {
+ key = field[i]
+ keylen = length(key)
+ if (S_is_set[key]) {
+ value = S[key]
+ line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+ len += length(value) + length(field[++i])
+ substed = 1
+ } else
+ len += 1 + keylen
+ }
+
+ print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+ sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+ cat
+fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
+ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
+# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{
+h
+s///
+s/^/:/
+s/[ ]*$/:/
+s/:\$(srcdir):/:/g
+s/:\${srcdir}:/:/g
+s/:@srcdir@:/:/g
+s/^:*//
+s/:*$//
+x
+s/\(=[ ]*\).*/\1/
+G
+s/\n//
+s/^[^=]*=[ ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+# Set up the scripts for CONFIG_HEADERS section.
+# No need to generate them if there are no CONFIG_HEADERS.
+# This happens for instance with `./config.status Makefile'.
+if test -n "$CONFIG_HEADERS"; then
+cat >"$ac_tmp/defines.awk" <<\_ACAWK ||
+BEGIN {
+_ACEOF
+
+# Transform confdefs.h into an awk script `defines.awk', embedded as
+# here-document in config.status, that substitutes the proper values into
+# config.h.in to produce config.h.
+
+# Create a delimiter string that does not exist in confdefs.h, to ease
+# handling of long lines.
+ac_delim='%!_!# '
+for ac_last_try in false false :; do
+ ac_tt=`sed -n "/$ac_delim/p" confdefs.h`
+ if test -z "$ac_tt"; then
+ break
+ elif $ac_last_try; then
+ as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+done
+
+# For the awk script, D is an array of macro values keyed by name,
+# likewise P contains macro parameters if any. Preserve backslash
+# newline sequences.
+
+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
+sed -n '
+s/.\{148\}/&'"$ac_delim"'/g
+t rset
+:rset
+s/^[ ]*#[ ]*define[ ][ ]*/ /
+t def
+d
+:def
+s/\\$//
+t bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3"/p
+s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p
+d
+:bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3\\\\\\n"\\/p
+t cont
+s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
+t cont
+d
+:cont
+n
+s/.\{148\}/&'"$ac_delim"'/g
+t clear
+:clear
+s/\\$//
+t bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/"/p
+d
+:bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
+b cont
+' <confdefs.h | sed '
+s/'"$ac_delim"'/"\\\
+"/g' >>$CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ for (key in D) D_is_set[key] = 1
+ FS = ""
+}
+/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
+ line = \$ 0
+ split(line, arg, " ")
+ if (arg[1] == "#") {
+ defundef = arg[2]
+ mac1 = arg[3]
+ } else {
+ defundef = substr(arg[1], 2)
+ mac1 = arg[2]
+ }
+ split(mac1, mac2, "(") #)
+ macro = mac2[1]
+ prefix = substr(line, 1, index(line, defundef) - 1)
+ if (D_is_set[macro]) {
+ # Preserve the white space surrounding the "#".
+ print prefix "define", macro P[macro] D[macro]
+ next
+ } else {
+ # Replace #undef with comments. This is necessary, for example,
+ # in the case of _POSIX_SOURCE, which is predefined and required
+ # on some systems where configure will not decide to define it.
+ if (defundef == "undef") {
+ print "/*", prefix defundef, macro, "*/"
+ next
+ }
+ }
+}
+{ print }
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
+fi # test -n "$CONFIG_HEADERS"
+
+
+eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS"
+shift
+for ac_tag
+do
+ case $ac_tag in
+ :[FHLC]) ac_mode=$ac_tag; continue;;
+ esac
+ case $ac_mode$ac_tag in
+ :[FHL]*:*);;
+ :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
+ :[FH]-) ac_tag=-:-;;
+ :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+ esac
+ ac_save_IFS=$IFS
+ IFS=:
+ set x $ac_tag
+ IFS=$ac_save_IFS
+ shift
+ ac_file=$1
+ shift
+
+ case $ac_mode in
+ :L) ac_source=$1;;
+ :[FH])
+ ac_file_inputs=
+ for ac_f
+ do
+ case $ac_f in
+ -) ac_f="$ac_tmp/stdin";;
+ *) # Look for the file first in the build tree, then in the source tree
+ # (if the path is not absolute). The absolute path cannot be DOS-style,
+ # because $ac_f cannot contain `:'.
+ test -f "$ac_f" ||
+ case $ac_f in
+ [\\/$]*) false;;
+ *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+ esac ||
+ as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+ esac
+ case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+ as_fn_append ac_file_inputs " '$ac_f'"
+ done
+
+ # Let's still pretend it is `configure' which instantiates (i.e., don't
+ # use $as_me), people would be surprised to read:
+ # /* config.h. Generated by config.status. */
+ configure_input='Generated from '`
+ $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+ `' by configure.'
+ if test x"$ac_file" != x-; then
+ configure_input="$ac_file. $configure_input"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+$as_echo "$as_me: creating $ac_file" >&6;}
+ fi
+ # Neutralize special characters interpreted by sed in replacement strings.
+ case $configure_input in #(
+ *\&* | *\|* | *\\* )
+ ac_sed_conf_input=`$as_echo "$configure_input" |
+ sed 's/[\\\\&|]/\\\\&/g'`;; #(
+ *) ac_sed_conf_input=$configure_input;;
+ esac
+
+ case $ac_tag in
+ *:-:* | *:-) cat >"$ac_tmp/stdin" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
+ esac
+ ;;
+ esac
+
+ ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_file" : 'X\(//\)[^/]' \| \
+ X"$ac_file" : 'X\(//\)$' \| \
+ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$ac_file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ as_dir="$ac_dir"; as_fn_mkdir_p
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+ case $ac_mode in
+ :F)
+ #
+ # CONFIG_FILE
+ #
+
+ case $INSTALL in
+ [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+ *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
+ esac
+ ac_MKDIR_P=$MKDIR_P
+ case $MKDIR_P in
+ [\\/$]* | ?:[\\/]* ) ;;
+ */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;;
+ esac
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+ac_sed_dataroot='
+/datarootdir/ {
+ p
+ q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ ac_datarootdir_hack='
+ s&@datadir@&$datadir&g
+ s&@docdir@&$docdir&g
+ s&@infodir@&$infodir&g
+ s&@localedir@&$localedir&g
+ s&@mandir@&$mandir&g
+ s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+s&@INSTALL@&$ac_INSTALL&;t t
+s&@MKDIR_P@&$ac_MKDIR_P&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
+ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+ { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
+ { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \
+ "$ac_tmp/out"`; test -z "$ac_out"; } &&
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined" >&5
+$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined" >&2;}
+
+ rm -f "$ac_tmp/stdin"
+ case $ac_file in
+ -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
+ *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
+ esac \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ ;;
+ :H)
+ #
+ # CONFIG_HEADER
+ #
+ if test x"$ac_file" != x-; then
+ {
+ $as_echo "/* $configure_input */" \
+ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
+ } >"$ac_tmp/config.h" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
+$as_echo "$as_me: $ac_file is unchanged" >&6;}
+ else
+ rm -f "$ac_file"
+ mv "$ac_tmp/config.h" "$ac_file" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ fi
+ else
+ $as_echo "/* $configure_input */" \
+ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
+ || as_fn_error $? "could not create -" "$LINENO" 5
+ fi
+# Compute "$ac_file"'s index in $config_headers.
+_am_arg="$ac_file"
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+ case $_am_header in
+ $_am_arg | $_am_arg:* )
+ break ;;
+ * )
+ _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+ esac
+done
+echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" ||
+$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$_am_arg" : 'X\(//\)[^/]' \| \
+ X"$_am_arg" : 'X\(//\)$' \| \
+ X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$_am_arg" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`/stamp-h$_am_stamp_count
+ ;;
+
+ :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5
+$as_echo "$as_me: executing $ac_file commands" >&6;}
+ ;;
+ esac
+
+
+ case $ac_file$ac_mode in
+ "depfiles":C) test x"$AMDEP_TRUE" != x"" || {
+ # Autoconf 2.62 quotes --file arguments for eval, but not when files
+ # are listed without --file. Let's play safe and only enable the eval
+ # if we detect the quoting.
+ case $CONFIG_FILES in
+ *\'*) eval set x "$CONFIG_FILES" ;;
+ *) set x $CONFIG_FILES ;;
+ esac
+ shift
+ for mf
+ do
+ # Strip MF so we end up with the name of the file.
+ mf=`echo "$mf" | sed -e 's/:.*$//'`
+ # Check whether this is an Automake generated Makefile or not.
+ # We used to match only the files named `Makefile.in', but
+ # some people rename them; so instead we look at the file content.
+ # Grep'ing the first line is not enough: some people post-process
+ # each Makefile.in and add a new line on top of each file to say so.
+ # Grep'ing the whole file is not good either: AIX grep has a line
+ # limit of 2048, but all sed's we know have understand at least 4000.
+ if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
+ dirpart=`$as_dirname -- "$mf" ||
+$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$mf" : 'X\(//\)[^/]' \| \
+ X"$mf" : 'X\(//\)$' \| \
+ X"$mf" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$mf" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ else
+ continue
+ fi
+ # Extract the definition of DEPDIR, am__include, and am__quote
+ # from the Makefile without running `make'.
+ DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
+ test -z "$DEPDIR" && continue
+ am__include=`sed -n 's/^am__include = //p' < "$mf"`
+ test -z "am__include" && continue
+ am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
+ # When using ansi2knr, U may be empty or an underscore; expand it
+ U=`sed -n 's/^U = //p' < "$mf"`
+ # Find all dependency output files, they are included files with
+ # $(DEPDIR) in their names. We invoke sed twice because it is the
+ # simplest approach to changing $(DEPDIR) to its actual value in the
+ # expansion.
+ for file in `sed -n "
+ s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
+ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
+ # Make sure the directory exists.
+ test -f "$dirpart/$file" && continue
+ fdir=`$as_dirname -- "$file" ||
+$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$file" : 'X\(//\)[^/]' \| \
+ X"$file" : 'X\(//\)$' \| \
+ X"$file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ as_dir=$dirpart/$fdir; as_fn_mkdir_p
+ # echo "creating $dirpart/$file"
+ echo '# dummy' > "$dirpart/$file"
+ done
+ done
+}
+ ;;
+ "libtool":C)
+
+ # See if we are running on zsh, and set the options which allow our
+ # commands through without removal of \ escapes.
+ if test -n "${ZSH_VERSION+set}" ; then
+ setopt NO_GLOB_SUBST
+ fi
+
+ cfgfile="${ofile}T"
+ trap "$RM \"$cfgfile\"; exit 1" 1 2 15
+ $RM "$cfgfile"
+
+ cat <<_LT_EOF >> "$cfgfile"
+#! $SHELL
+
+# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services.
+# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION
+# NOTE: Changes made to this file will be lost: look at ltmain.sh.
+#
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005,
+# 2006, 2007, 2008 Free Software Foundation, Inc.
+# Written by Gordon Matzigkeit, 1996
+#
+# This file is part of GNU Libtool.
+#
+# GNU Libtool 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.
+#
+# As a special exception to the GNU General Public License,
+# if you distribute this file as part of a program or library that
+# is built using GNU Libtool, you may include this file under the
+# same distribution terms that you use for the rest of that program.
+#
+# GNU Libtool 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 GNU Libtool; see the file COPYING. If not, a copy
+# can be downloaded from http://www.gnu.org/licenses/gpl.html, or
+# obtained by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+
+# The names of the tagged configurations supported by this script.
+available_tags=""
+
+# ### BEGIN LIBTOOL CONFIG
+
+# Which release of libtool.m4 was used?
+macro_version=$macro_version
+macro_revision=$macro_revision
+
+# Whether or not to build shared libraries.
+build_libtool_libs=$enable_shared
+
+# Whether or not to build static libraries.
+build_old_libs=$enable_static
+
+# What type of objects to build.
+pic_mode=$pic_mode
+
+# Whether or not to optimize for fast installation.
+fast_install=$enable_fast_install
+
+# The host system.
+host_alias=$host_alias
+host=$host
+host_os=$host_os
+
+# The build system.
+build_alias=$build_alias
+build=$build
+build_os=$build_os
+
+# A sed program that does not truncate output.
+SED=$lt_SED
+
+# Sed that helps us avoid accidentally triggering echo(1) options like -n.
+Xsed="\$SED -e 1s/^X//"
+
+# A grep program that handles long lines.
+GREP=$lt_GREP
+
+# An ERE matcher.
+EGREP=$lt_EGREP
+
+# A literal string matcher.
+FGREP=$lt_FGREP
+
+# A BSD- or MS-compatible name lister.
+NM=$lt_NM
+
+# Whether we need soft or hard links.
+LN_S=$lt_LN_S
+
+# What is the maximum length of a command?
+max_cmd_len=$max_cmd_len
+
+# Object file suffix (normally "o").
+objext=$ac_objext
+
+# Executable file suffix (normally "").
+exeext=$exeext
+
+# whether the shell understands "unset".
+lt_unset=$lt_unset
+
+# turn spaces into newlines.
+SP2NL=$lt_lt_SP2NL
+
+# turn newlines into spaces.
+NL2SP=$lt_lt_NL2SP
+
+# How to create reloadable object files.
+reload_flag=$lt_reload_flag
+reload_cmds=$lt_reload_cmds
+
+# An object symbol dumper.
+OBJDUMP=$lt_OBJDUMP
+
+# Method to check whether dependent libraries are shared objects.
+deplibs_check_method=$lt_deplibs_check_method
+
+# Command to use when deplibs_check_method == "file_magic".
+file_magic_cmd=$lt_file_magic_cmd
+
+# The archiver.
+AR=$lt_AR
+AR_FLAGS=$lt_AR_FLAGS
+
+# A symbol stripping program.
+STRIP=$lt_STRIP
+
+# Commands used to install an old-style archive.
+RANLIB=$lt_RANLIB
+old_postinstall_cmds=$lt_old_postinstall_cmds
+old_postuninstall_cmds=$lt_old_postuninstall_cmds
+
+# A C compiler.
+LTCC=$lt_CC
+
+# LTCC compiler flags.
+LTCFLAGS=$lt_CFLAGS
+
+# Take the output of nm and produce a listing of raw symbols and C names.
+global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe
+
+# Transform the output of nm in a proper C declaration.
+global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl
+
+# Transform the output of nm in a C name address pair.
+global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address
+
+# Transform the output of nm in a C name address pair when lib prefix is needed.
+global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix
+
+# The name of the directory that contains temporary libtool files.
+objdir=$objdir
+
+# Shell to use when invoking shell scripts.
+SHELL=$lt_SHELL
+
+# An echo program that does not interpret backslashes.
+ECHO=$lt_ECHO
+
+# Used to examine libraries when file_magic_cmd begins with "file".
+MAGIC_CMD=$MAGIC_CMD
+
+# Must we lock files when doing compilation?
+need_locks=$lt_need_locks
+
+# Tool to manipulate archived DWARF debug symbol files on Mac OS X.
+DSYMUTIL=$lt_DSYMUTIL
+
+# Tool to change global to local symbols on Mac OS X.
+NMEDIT=$lt_NMEDIT
+
+# Tool to manipulate fat objects and archives on Mac OS X.
+LIPO=$lt_LIPO
+
+# ldd/readelf like tool for Mach-O binaries on Mac OS X.
+OTOOL=$lt_OTOOL
+
+# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4.
+OTOOL64=$lt_OTOOL64
+
+# Old archive suffix (normally "a").
+libext=$libext
+
+# Shared library suffix (normally ".so").
+shrext_cmds=$lt_shrext_cmds
+
+# The commands to extract the exported symbol list from a shared archive.
+extract_expsyms_cmds=$lt_extract_expsyms_cmds
+
+# Variables whose values should be saved in libtool wrapper scripts and
+# restored at link time.
+variables_saved_for_relink=$lt_variables_saved_for_relink
+
+# Do we need the "lib" prefix for modules?
+need_lib_prefix=$need_lib_prefix
+
+# Do we need a version for libraries?
+need_version=$need_version
+
+# Library versioning type.
+version_type=$version_type
+
+# Shared library runtime path variable.
+runpath_var=$runpath_var
+
+# Shared library path variable.
+shlibpath_var=$shlibpath_var
+
+# Is shlibpath searched before the hard-coded library search path?
+shlibpath_overrides_runpath=$shlibpath_overrides_runpath
+
+# Format of library name prefix.
+libname_spec=$lt_libname_spec
+
+# List of archive names. First name is the real one, the rest are links.
+# The last name is the one that the linker finds with -lNAME
+library_names_spec=$lt_library_names_spec
+
+# The coded name of the library, if different from the real name.
+soname_spec=$lt_soname_spec
+
+# Command to use after installation of a shared archive.
+postinstall_cmds=$lt_postinstall_cmds
+
+# Command to use after uninstallation of a shared archive.
+postuninstall_cmds=$lt_postuninstall_cmds
+
+# Commands used to finish a libtool library installation in a directory.
+finish_cmds=$lt_finish_cmds
+
+# As "finish_cmds", except a single script fragment to be evaled but
+# not shown.
+finish_eval=$lt_finish_eval
+
+# Whether we should hardcode library paths into libraries.
+hardcode_into_libs=$hardcode_into_libs
+
+# Compile-time system search path for libraries.
+sys_lib_search_path_spec=$lt_sys_lib_search_path_spec
+
+# Run-time system search path for libraries.
+sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec
+
+# Whether dlopen is supported.
+dlopen_support=$enable_dlopen
+
+# Whether dlopen of programs is supported.
+dlopen_self=$enable_dlopen_self
+
+# Whether dlopen of statically linked programs is supported.
+dlopen_self_static=$enable_dlopen_self_static
+
+# Commands to strip libraries.
+old_striplib=$lt_old_striplib
+striplib=$lt_striplib
+
+
+# The linker used to build libraries.
+LD=$lt_LD
+
+# Commands used to build an old-style archive.
+old_archive_cmds=$lt_old_archive_cmds
+
+# A language specific compiler.
+CC=$lt_compiler
+
+# Is the compiler the GNU compiler?
+with_gcc=$GCC
+
+# Compiler flag to turn off builtin functions.
+no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag
+
+# How to pass a linker flag through the compiler.
+wl=$lt_lt_prog_compiler_wl
+
+# Additional compiler flags for building library objects.
+pic_flag=$lt_lt_prog_compiler_pic
+
+# Compiler flag to prevent dynamic linking.
+link_static_flag=$lt_lt_prog_compiler_static
+
+# Does compiler simultaneously support -c and -o options?
+compiler_c_o=$lt_lt_cv_prog_compiler_c_o
+
+# Whether or not to add -lc for building shared libraries.
+build_libtool_need_lc=$archive_cmds_need_lc
+
+# Whether or not to disallow shared libs when runtime libs are static.
+allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes
+
+# Compiler flag to allow reflexive dlopens.
+export_dynamic_flag_spec=$lt_export_dynamic_flag_spec
+
+# Compiler flag to generate shared objects directly from archives.
+whole_archive_flag_spec=$lt_whole_archive_flag_spec
+
+# Whether the compiler copes with passing no objects directly.
+compiler_needs_object=$lt_compiler_needs_object
+
+# Create an old-style archive from a shared archive.
+old_archive_from_new_cmds=$lt_old_archive_from_new_cmds
+
+# Create a temporary old-style archive to link instead of a shared archive.
+old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds
+
+# Commands used to build a shared archive.
+archive_cmds=$lt_archive_cmds
+archive_expsym_cmds=$lt_archive_expsym_cmds
+
+# Commands used to build a loadable module if different from building
+# a shared archive.
+module_cmds=$lt_module_cmds
+module_expsym_cmds=$lt_module_expsym_cmds
+
+# Whether we are building with GNU ld or not.
+with_gnu_ld=$lt_with_gnu_ld
+
+# Flag that allows shared libraries with undefined symbols to be built.
+allow_undefined_flag=$lt_allow_undefined_flag
+
+# Flag that enforces no undefined symbols.
+no_undefined_flag=$lt_no_undefined_flag
+
+# Flag to hardcode \$libdir into a binary during linking.
+# This must work even if \$libdir does not exist
+hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec
+
+# If ld is used when linking, flag to hardcode \$libdir into a binary
+# during linking. This must work even if \$libdir does not exist.
+hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld
+
+# Whether we need a single "-rpath" flag with a separated argument.
+hardcode_libdir_separator=$lt_hardcode_libdir_separator
+
+# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
+# DIR into the resulting binary.
+hardcode_direct=$hardcode_direct
+
+# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
+# DIR into the resulting binary and the resulting library dependency is
+# "absolute",i.e impossible to change by setting \${shlibpath_var} if the
+# library is relocated.
+hardcode_direct_absolute=$hardcode_direct_absolute
+
+# Set to "yes" if using the -LDIR flag during linking hardcodes DIR
+# into the resulting binary.
+hardcode_minus_L=$hardcode_minus_L
+
+# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
+# into the resulting binary.
+hardcode_shlibpath_var=$hardcode_shlibpath_var
+
+# Set to "yes" if building a shared library automatically hardcodes DIR
+# into the library and all subsequent libraries and executables linked
+# against it.
+hardcode_automatic=$hardcode_automatic
+
+# Set to yes if linker adds runtime paths of dependent libraries
+# to runtime path list.
+inherit_rpath=$inherit_rpath
+
+# Whether libtool must link a program against all its dependency libraries.
+link_all_deplibs=$link_all_deplibs
+
+# Fix the shell variable \$srcfile for the compiler.
+fix_srcfile_path=$lt_fix_srcfile_path
+
+# Set to "yes" if exported symbols are required.
+always_export_symbols=$always_export_symbols
+
+# The commands to list exported symbols.
+export_symbols_cmds=$lt_export_symbols_cmds
+
+# Symbols that should not be listed in the preloaded symbols.
+exclude_expsyms=$lt_exclude_expsyms
+
+# Symbols that must always be exported.
+include_expsyms=$lt_include_expsyms
+
+# Commands necessary for linking programs (against libraries) with templates.
+prelink_cmds=$lt_prelink_cmds
+
+# Specify filename containing input files.
+file_list_spec=$lt_file_list_spec
+
+# How to hardcode a shared library path into an executable.
+hardcode_action=$hardcode_action
+
+# ### END LIBTOOL CONFIG
+
+_LT_EOF
+
+ case $host_os in
+ aix3*)
+ cat <<\_LT_EOF >> "$cfgfile"
+# AIX sometimes has problems with the GCC collect2 program. For some
+# reason, if we set the COLLECT_NAMES environment variable, the problems
+# vanish in a puff of smoke.
+if test "X${COLLECT_NAMES+set}" != Xset; then
+ COLLECT_NAMES=
+ export COLLECT_NAMES
+fi
+_LT_EOF
+ ;;
+ esac
+
+
+ltmain="$ac_aux_dir/ltmain.sh"
+
+
+ # We use sed instead of cat because bash on DJGPP gets confused if
+ # if finds mixed CR/LF and LF-only lines. Since sed operates in
+ # text mode, it properly converts lines to CR/LF. This bash problem
+ # is reportedly fixed, but why not run on old versions too?
+ sed '/^# Generated shell functions inserted here/q' "$ltmain" >> "$cfgfile" \
+ || (rm -f "$cfgfile"; exit 1)
+
+ case $xsi_shell in
+ yes)
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_dirname file append nondir_replacement
+# Compute the dirname of FILE. If nonempty, add APPEND to the result,
+# otherwise set result to NONDIR_REPLACEMENT.
+func_dirname ()
+{
+ case ${1} in
+ */*) func_dirname_result="${1%/*}${2}" ;;
+ * ) func_dirname_result="${3}" ;;
+ esac
+}
+
+# func_basename file
+func_basename ()
+{
+ func_basename_result="${1##*/}"
+}
+
+# func_dirname_and_basename file append nondir_replacement
+# perform func_basename and func_dirname in a single function
+# call:
+# dirname: Compute the dirname of FILE. If nonempty,
+# add APPEND to the result, otherwise set result
+# to NONDIR_REPLACEMENT.
+# value returned in "$func_dirname_result"
+# basename: Compute filename of FILE.
+# value retuned in "$func_basename_result"
+# Implementation must be kept synchronized with func_dirname
+# and func_basename. For efficiency, we do not delegate to
+# those functions but instead duplicate the functionality here.
+func_dirname_and_basename ()
+{
+ case ${1} in
+ */*) func_dirname_result="${1%/*}${2}" ;;
+ * ) func_dirname_result="${3}" ;;
+ esac
+ func_basename_result="${1##*/}"
+}
+
+# func_stripname prefix suffix name
+# strip PREFIX and SUFFIX off of NAME.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+func_stripname ()
+{
+ # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are
+ # positional parameters, so assign one to ordinary parameter first.
+ func_stripname_result=${3}
+ func_stripname_result=${func_stripname_result#"${1}"}
+ func_stripname_result=${func_stripname_result%"${2}"}
+}
+
+# func_opt_split
+func_opt_split ()
+{
+ func_opt_split_opt=${1%%=*}
+ func_opt_split_arg=${1#*=}
+}
+
+# func_lo2o object
+func_lo2o ()
+{
+ case ${1} in
+ *.lo) func_lo2o_result=${1%.lo}.${objext} ;;
+ *) func_lo2o_result=${1} ;;
+ esac
+}
+
+# func_xform libobj-or-source
+func_xform ()
+{
+ func_xform_result=${1%.*}.lo
+}
+
+# func_arith arithmetic-term...
+func_arith ()
+{
+ func_arith_result=$(( $* ))
+}
+
+# func_len string
+# STRING may not start with a hyphen.
+func_len ()
+{
+ func_len_result=${#1}
+}
+
+_LT_EOF
+ ;;
+ *) # Bourne compatible functions.
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_dirname file append nondir_replacement
+# Compute the dirname of FILE. If nonempty, add APPEND to the result,
+# otherwise set result to NONDIR_REPLACEMENT.
+func_dirname ()
+{
+ # Extract subdirectory from the argument.
+ func_dirname_result=`$ECHO "X${1}" | $Xsed -e "$dirname"`
+ if test "X$func_dirname_result" = "X${1}"; then
+ func_dirname_result="${3}"
+ else
+ func_dirname_result="$func_dirname_result${2}"
+ fi
+}
+
+# func_basename file
+func_basename ()
+{
+ func_basename_result=`$ECHO "X${1}" | $Xsed -e "$basename"`
+}
+
+
+# func_stripname prefix suffix name
+# strip PREFIX and SUFFIX off of NAME.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+# func_strip_suffix prefix name
+func_stripname ()
+{
+ case ${2} in
+ .*) func_stripname_result=`$ECHO "X${3}" \
+ | $Xsed -e "s%^${1}%%" -e "s%\\\\${2}\$%%"`;;
+ *) func_stripname_result=`$ECHO "X${3}" \
+ | $Xsed -e "s%^${1}%%" -e "s%${2}\$%%"`;;
+ esac
+}
+
+# sed scripts:
+my_sed_long_opt='1s/^\(-[^=]*\)=.*/\1/;q'
+my_sed_long_arg='1s/^-[^=]*=//'
+
+# func_opt_split
+func_opt_split ()
+{
+ func_opt_split_opt=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_opt"`
+ func_opt_split_arg=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_arg"`
+}
+
+# func_lo2o object
+func_lo2o ()
+{
+ func_lo2o_result=`$ECHO "X${1}" | $Xsed -e "$lo2o"`
+}
+
+# func_xform libobj-or-source
+func_xform ()
+{
+ func_xform_result=`$ECHO "X${1}" | $Xsed -e 's/\.[^.]*$/.lo/'`
+}
+
+# func_arith arithmetic-term...
+func_arith ()
+{
+ func_arith_result=`expr "$@"`
+}
+
+# func_len string
+# STRING may not start with a hyphen.
+func_len ()
+{
+ func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len`
+}
+
+_LT_EOF
+esac
+
+case $lt_shell_append in
+ yes)
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_append var value
+# Append VALUE to the end of shell variable VAR.
+func_append ()
+{
+ eval "$1+=\$2"
+}
+_LT_EOF
+ ;;
+ *)
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_append var value
+# Append VALUE to the end of shell variable VAR.
+func_append ()
+{
+ eval "$1=\$$1\$2"
+}
+
+_LT_EOF
+ ;;
+ esac
+
+
+ sed -n '/^# Generated shell functions inserted here/,$p' "$ltmain" >> "$cfgfile" \
+ || (rm -f "$cfgfile"; exit 1)
+
+ mv -f "$cfgfile" "$ofile" ||
+ (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
+ chmod +x "$ofile"
+
+ ;;
+ "po-directories":C)
+ for ac_file in $CONFIG_FILES; do
+ # Support "outfile[:infile[:infile...]]"
+ case "$ac_file" in
+ *:*) ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ esac
+ # PO directories have a Makefile.in generated from Makefile.in.in.
+ case "$ac_file" in */Makefile.in)
+ # Adjust a relative srcdir.
+ ac_dir=`echo "$ac_file"|sed 's%/[^/][^/]*$%%'`
+ ac_dir_suffix="/`echo "$ac_dir"|sed 's%^\./%%'`"
+ ac_dots=`echo "$ac_dir_suffix"|sed 's%/[^/]*%../%g'`
+ # In autoconf-2.13 it is called $ac_given_srcdir.
+ # In autoconf-2.50 it is called $srcdir.
+ test -n "$ac_given_srcdir" || ac_given_srcdir="$srcdir"
+ case "$ac_given_srcdir" in
+ .) top_srcdir=`echo $ac_dots|sed 's%/$%%'` ;;
+ /*) top_srcdir="$ac_given_srcdir" ;;
+ *) top_srcdir="$ac_dots$ac_given_srcdir" ;;
+ esac
+ # Treat a directory as a PO directory if and only if it has a
+ # POTFILES.in file. This allows packages to have multiple PO
+ # directories under different names or in different locations.
+ if test -f "$ac_given_srcdir/$ac_dir/POTFILES.in"; then
+ rm -f "$ac_dir/POTFILES"
+ test -n "$as_me" && echo "$as_me: creating $ac_dir/POTFILES" || echo "creating $ac_dir/POTFILES"
+ cat "$ac_given_srcdir/$ac_dir/POTFILES.in" | sed -e "/^#/d" -e "/^[ ]*\$/d" -e "s,.*, $top_srcdir/& \\\\," | sed -e "\$s/\(.*\) \\\\/\1/" > "$ac_dir/POTFILES"
+ POMAKEFILEDEPS="POTFILES.in"
+ # ALL_LINGUAS, POFILES, UPDATEPOFILES, DUMMYPOFILES, GMOFILES depend
+ # on $ac_dir but don't depend on user-specified configuration
+ # parameters.
+ if test -f "$ac_given_srcdir/$ac_dir/LINGUAS"; then
+ # The LINGUAS file contains the set of available languages.
+ if test -n "$OBSOLETE_ALL_LINGUAS"; then
+ test -n "$as_me" && echo "$as_me: setting ALL_LINGUAS in configure.in is obsolete" || echo "setting ALL_LINGUAS in configure.in is obsolete"
+ fi
+ ALL_LINGUAS_=`sed -e "/^#/d" -e "s/#.*//" "$ac_given_srcdir/$ac_dir/LINGUAS"`
+ # Hide the ALL_LINGUAS assigment from automake < 1.5.
+ eval 'ALL_LINGUAS''=$ALL_LINGUAS_'
+ POMAKEFILEDEPS="$POMAKEFILEDEPS LINGUAS"
+ else
+ # The set of available languages was given in configure.in.
+ # Hide the ALL_LINGUAS assigment from automake < 1.5.
+ eval 'ALL_LINGUAS''=$OBSOLETE_ALL_LINGUAS'
+ fi
+ # Compute POFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).po)
+ # Compute UPDATEPOFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(lang).po-update)
+ # Compute DUMMYPOFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(lang).nop)
+ # Compute GMOFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).gmo)
+ case "$ac_given_srcdir" in
+ .) srcdirpre= ;;
+ *) srcdirpre='$(srcdir)/' ;;
+ esac
+ POFILES=
+ UPDATEPOFILES=
+ DUMMYPOFILES=
+ GMOFILES=
+ for lang in $ALL_LINGUAS; do
+ POFILES="$POFILES $srcdirpre$lang.po"
+ UPDATEPOFILES="$UPDATEPOFILES $lang.po-update"
+ DUMMYPOFILES="$DUMMYPOFILES $lang.nop"
+ GMOFILES="$GMOFILES $srcdirpre$lang.gmo"
+ done
+ # CATALOGS depends on both $ac_dir and the user's LINGUAS
+ # environment variable.
+ INST_LINGUAS=
+ if test -n "$ALL_LINGUAS"; then
+ for presentlang in $ALL_LINGUAS; do
+ useit=no
+ if test "%UNSET%" != "$LINGUAS"; then
+ desiredlanguages="$LINGUAS"
+ else
+ desiredlanguages="$ALL_LINGUAS"
+ fi
+ for desiredlang in $desiredlanguages; do
+ # Use the presentlang catalog if desiredlang is
+ # a. equal to presentlang, or
+ # b. a variant of presentlang (because in this case,
+ # presentlang can be used as a fallback for messages
+ # which are not translated in the desiredlang catalog).
+ case "$desiredlang" in
+ "$presentlang"*) useit=yes;;
+ esac
+ done
+ if test $useit = yes; then
+ INST_LINGUAS="$INST_LINGUAS $presentlang"
+ fi
+ done
+ fi
+ CATALOGS=
+ if test -n "$INST_LINGUAS"; then
+ for lang in $INST_LINGUAS; do
+ CATALOGS="$CATALOGS $lang.gmo"
+ done
+ fi
+ test -n "$as_me" && echo "$as_me: creating $ac_dir/Makefile" || echo "creating $ac_dir/Makefile"
+ sed -e "/^POTFILES =/r $ac_dir/POTFILES" -e "/^# Makevars/r $ac_given_srcdir/$ac_dir/Makevars" -e "s|@POFILES@|$POFILES|g" -e "s|@UPDATEPOFILES@|$UPDATEPOFILES|g" -e "s|@DUMMYPOFILES@|$DUMMYPOFILES|g" -e "s|@GMOFILES@|$GMOFILES|g" -e "s|@CATALOGS@|$CATALOGS|g" -e "s|@POMAKEFILEDEPS@|$POMAKEFILEDEPS|g" "$ac_dir/Makefile.in" > "$ac_dir/Makefile"
+ for f in "$ac_given_srcdir/$ac_dir"/Rules-*; do
+ if test -f "$f"; then
+ case "$f" in
+ *.orig | *.bak | *~) ;;
+ *) cat "$f" >> "$ac_dir/Makefile" ;;
+ esac
+ fi
+ done
+ fi
+ ;;
+ esac
+ done ;;
+
+ esac
+done # for ac_tag
+
+
+as_fn_exit 0
+_ACEOF
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+ as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded. So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status. When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+ ac_cs_success=:
+ ac_config_status_args=
+ test "$silent" = yes &&
+ ac_config_status_args="$ac_config_status_args --quiet"
+ exec 5>/dev/null
+ $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+ exec 5>>config.log
+ # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+ # would make configure fail if this is the last instruction.
+ $ac_cs_success || as_fn_exit 1
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 00000000..27a9f95f
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,1665 @@
+dnl Process this file with autoconf to produce a configure script.
+
+dnl /* ========================================================================
+dnl * Copyright 2006-2008 University of Washington
+dnl *
+dnl * Licensed under the Apache License, Version 2.0 (the "License");
+dnl * you may not use this file except in compliance with the License.
+dnl * You may obtain a copy of the License at
+dnl *
+dnl * http://www.apache.org/licenses/LICENSE-2.0
+dnl *
+dnl * ========================================================================
+dnl */
+
+AC_PREREQ([2.57])
+
+AC_REVISION([$Id: configure.ac 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $])
+
+dnl Alpine Version Number is in $srcdir/VERSION
+AC_INIT(alpine, m4_normalize(m4_include([VERSION])), [alpine-contact@u.washington.edu])
+
+AC_CONFIG_SRCDIR([include/system.h])
+AC_CONFIG_HEADERS([include/config.h])
+
+AM_INIT_AUTOMAKE([foreign nostdinc])
+
+AM_MAINTAINER_MODE
+
+AC_CANONICAL_HOST
+
+AC_LANG(C)
+
+AC_MSG_NOTICE([Configuring for $PACKAGE_STRING ($host))])
+
+# start out with intent to build Web Alpine
+WEB_BUILD=web/src/alpined.d
+
+dnl CHECK PROGRAMS
+
+AC_PROG_CC
+AC_PROG_CC_STDC
+AC_PROG_INSTALL
+AC_PROG_MAKE_SET
+AC_PROG_LN_S
+AC_PROG_AWK
+AC_PROG_RANLIB
+AC_PROG_LIBTOOL
+
+AC_PATH_PROG(AR, ar, ar)
+AC_PATH_PROG(RM, rm, rm)
+AC_PATH_PROG(CP, cp, cp)
+AC_PATH_PROG(LN, ln, ln)
+AC_PATH_PROG(SED, sed, sed)
+AC_PATH_PROG(MAKE, make)
+
+dnl COMPILE-TIME OPTIONS
+
+AM_GNU_GETTEXT_VERSION([0.16.1])
+AM_GNU_GETTEXT([external])
+
+dnl enable dmalloc per http://dmalloc.com
+dnl NOTE: does not check c-client
+AC_MSG_CHECKING([option: dmalloc enabled])
+AC_ARG_ENABLE(dmalloc, AC_HELP_STRING([--enable-dmalloc],[Enable dmalloc debugging]))
+if test x$enable_dmalloc = "xyes" ; then
+ AC_MSG_RESULT([yes])
+else
+ AC_MSG_RESULT([no])
+fi
+
+AC_ARG_WITH(dmalloc-dir,
+ AC_HELP_STRING([--with-dmalloc-dir=DIR], [Root of dmalloc lib/include path]),
+ [
+ if test "x$withval" != "xno" ; then
+ enable_dmalloc = "yes"
+ CPPFLAGS="$CPPCFLAGS -I${withval}"
+ LDFLAGS="$LDFLAGS -L${withval}"
+ fi
+ ])
+
+if test x$enable_dmalloc = "xyes" ; then
+ AC_DEFINE(ENABLE_DMALLOC, 1, [Define enable dmalloc debugging])
+fi
+
+localedir="\${datadir}/locale"
+AC_ARG_WITH(localedir,
+ AC_HELP_STRING([--with-localedir=DIR], [Name of gettext locale directory]),
+ [
+ case $withval in
+ yes)
+ ;;
+ no)
+ ;;
+ *)
+ localedir=$withval
+ ;;
+ esac
+ ])
+AC_SUBST(localedir, "[$localedir]")
+
+# Setup OS-Specific features
+case "$host" in
+ *darwin*)
+ dnl OS X Universal Binary Support
+ AC_ARG_ENABLE(osx-universal-binaries,
+ AC_HELP_STRING([--enable-osx-universal-binaries],[Produce universal binaries under OS X [[default=no]]]))
+ if test "x$enable_osx_universal_binaries" = "xyes" ; then
+ if test "x$enable_dependency_tracking" != xno ; then
+ AC_MSG_ERROR([--enable-osx-universal-binary requires --disable-dependency-tracking.
+Please re-run configure with these options:
+ --disable-dependency-tracking --enable-osx-universal-binary])
+ fi
+ if [test -d /Developer/SDKs/MacOSX10.5.sdk] ; then
+ alpine_sysroot=/Developer/SDKs/MacOSX10.5.sdk
+ elif [test -d /Developer/SDKs/MacOSX10.4u.sdk] ; then
+ alpine_sysroot=/Developer/SDKs/MacOSX10.4u.sdk
+ else
+ AC_MSG_ERROR([No suitable MacOSX SDK found. Make sure Xcode tools are installed])
+ fi
+ ub_cflags="-isysroot $alpine_sysroot -arch ppc -arch i386"
+ ub_ldflags="-Wl,-syslibroot,$alpine_sysroot -arch ppc -arch i386"
+ AM_CFLAGS="$AM_CFLAGS $ub_cflags"
+ AM_LDFLAGS="$AM_LDFLAGS $ub_ldflags"
+ alpine_c_client_cflags="$alpine_c_client_cflags $ub_cflags"
+ alpine_c_client_ldflags="$alpine_c_client_ldflags $ub_ldflags"
+ fi
+ ;;
+esac
+
+AC_ARG_WITH(include-path,
+ AC_HELP_STRING([--with-include-path=PATHS], [Colon-separated list of directories used for include file search]),
+ [
+ case $withval in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+ new_cppflags="-I`echo ${withval} | ${SED} 's/:/ -I/g'`"
+ CPPFLAGS="$CPPFLAGS ${new_cppflags}"
+ alpine_c_client_cflags="$alpine_c_client_cflags ${new_cppflags}"
+ ;;
+ esac
+ ])
+
+AC_ARG_WITH(lib-path,
+ AC_HELP_STRING([--with-lib-path=PATHS], [Colon-separated list of directories used for library search]),
+ [
+ case $withval in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+ new_ldflags="-L`echo ${withval} | ${SED} 's/:/ -L/g'`"
+ LDFLAGS="$LDFLAGS $new_ldflags"
+ alpine_c_client_ldflags="$alpine_c_client_ldflags ${new_ldflags}"
+ ;;
+ esac
+ ])
+
+AC_ARG_WITH(pubcookie,
+ AC_HELP_STRING([--with-pubcookie], [Include support for UW-Pubcookie Web Authentication]),
+ [
+ if test "x$withval" != "xno" ; then
+ WEB_PUBCOOKIE_BUILD=web/src/pubcookie
+ fi
+ ])
+
+
+AC_ARG_WITH(web-bin,
+ AC_HELP_STRING([--with-web-bin=PATH], [Directory to hold Web Alpine component binary files]),
+ [
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+ WEB_BINDIR=$withval
+ ;;
+ esac
+ ])
+
+dnl disable debug, turned on by default
+AC_MSG_CHECKING([option: debugging is enabled])
+AC_ARG_ENABLE(debug, AC_HELP_STRING([--disable-debug],[Exclude debug messages from source]))
+if test x$enable_debug != "xno" ; then
+ AM_CFLAGS="$AM_CFLAGS -g"
+ AC_DEFINE([DEBUG], [1], [Compile in debugging])
+ AC_DEFINE([DEBUGJOURNAL], [1], [Display debug messages in journal])
+ AC_MSG_RESULT([yes])
+else
+dnl ??? set AM_CFLAGS to optimize ???
+ AC_MSG_RESULT([no])
+fi
+
+dnl disable optimization, on by default
+AC_MSG_CHECKING([option: optimization is enabled])
+AC_ARG_ENABLE(optimization, AC_HELP_STRING([--disable-optimization],[Exclude optimizing compiler flags]))
+if test x$enable_optimization != "xno" ; then
+ AC_MSG_RESULT([yes])
+else
+ CFLAGS="`echo $AM_CFLAGS | ${SED} 's/-O2//'`"
+ alpine_c_client_gccoptlevel="-O0"
+ AC_MSG_RESULT([no])
+fi
+
+dnl disable mouse support
+AC_MSG_CHECKING([option: mouse support enabled])
+AC_ARG_ENABLE(mouse, AC_HELP_STRING([--disable-mouse], [Disable mouse support]))
+if test x$enable_mouse != "xno" ; then
+ AC_DEFINE([MOUSE], [], [Compile in mouse support])
+ AC_MSG_RESULT([yes])
+else
+ AC_MSG_RESULT([no])
+fi
+
+dnl enable quotas
+AC_MSG_CHECKING([option: quotas enabled])
+AC_ARG_ENABLE(quotas, AC_HELP_STRING([--enable-quotas],[Enable disk quota checking on startup]))
+if test x$enable_quotas = "xyes" ; then
+ AC_DEFINE([USE_QUOTAS], [], [Compile in quota check on startup])
+ AC_MSG_RESULT([yes])
+else
+ AC_MSG_RESULT([no])
+fi
+
+AC_MSG_CHECKING([option: From changing enabled])
+AC_ARG_ENABLE(from_changing, AC_HELP_STRING([--disable-from-changing],[Disallow users changing From addresss]))
+if test x$enable_from_changing != "xno" ; then
+ AC_MSG_RESULT([yes])
+else
+ AC_DEFINE([NEVER_ALLOW_CHANGING_FROM], [], [Disallow users changing their From address])
+ AC_MSG_RESULT([no])
+fi
+
+dnl enable background posting support
+AC_MSG_CHECKING([option: background post enabled])
+AC_ARG_ENABLE(background-post, AC_HELP_STRING([--disable-background-post],[Disable background posting]))
+if test x$enable_background_post != "xno" ; then
+ AC_DEFINE([BACKGROUND_POST], [], [Enable background posting support])
+ AC_MSG_RESULT([yes])
+else
+ AC_MSG_RESULT([no])
+fi
+
+dnl enable keyboard locking support
+AC_MSG_CHECKING([option: keyboard lock enabled])
+AC_ARG_ENABLE(keyboard-lock, AC_HELP_STRING([--disable-keyboard-lock],[Disable keyboard locking]))
+if test x$enable_keyboard_lock != "xno" ; then
+ AC_DEFINE([KEYBOARD_LOCK], [], [Enable keyboard lock support])
+ AC_MSG_RESULT([yes])
+else
+ AC_MSG_RESULT([no])
+fi
+
+dnl enable from encoding support
+AC_MSG_CHECKING([option: from encoding enabled])
+AC_ARG_ENABLE(from-encoding, AC_HELP_STRING([--enable-from-encoding],[Enable From encoding in sent messages]))
+if test x$enable_from_encoding = "xyes" ; then
+ AC_DEFINE([ENCODE_FROMS], [], [Enable From address encoding in sent messages])
+ AC_MSG_RESULT([yes])
+else
+ AC_MSG_RESULT([no])
+fi
+
+dnl OPTION: name of local submission agent
+dnl Might not be sendmail, but it MUST speak SMTP on stdin/stdout
+AC_ARG_WITH(smtp-msa,
+ AC_HELP_STRING([--with-smtp-msa=PATH],[Local Mail Submission Agent (sendmail)]),
+ [
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ AC_PATH_PROG(SENDMAIL, sendmail, "", $PATH:/usr/sbin:/usr/lib)
+ ;;
+ *)
+ SENDMAIL=$withval
+ ;;
+ esac
+ ],
+ [
+ AC_PATH_PROG(SENDMAIL, sendmail, "", $PATH:/usr/sbin:/usr/lib)
+ ])
+if test -n "$SENDMAIL" ; then
+ AC_DEFINE_UNQUOTED([SENDMAIL], "$SENDMAIL", [Local mail submission agent])
+fi
+
+dnl OPTION: local msa arguments
+smtp_msa_flags="-bs -odb -oem"
+AC_ARG_WITH(smtp-msa-flags,
+ AC_HELP_STRING([--with-smtp-msa-flags=FLAGS],[MSA flags for SMTP on stdin/stdout (-bs -odb -oem)]),
+ [
+ if test "x$withval" != "xno" ; then
+ smtp_msa_flags=$withval
+ fi
+ ])
+AC_DEFINE_UNQUOTED([SENDMAILFLAGS], "$smtp_msa_flags", [Local MSA flags for SMTP on stdin/stdout])
+
+dnl OPTION: name of local news posting agent and flags
+npa="inews"
+AC_ARG_WITH(npa,
+ AC_HELP_STRING([--with-npa=PATH],[Posting agent when no nntp-servers defined (inews)]),
+ [
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ AC_PATH_PROG(NPA_PROG, inews, "", $PATH:/usr/sbin:/usr/lib)
+ ;;
+ *)
+ NPA_PROG=$withval
+ ;;
+ esac
+ ],
+ [
+ AC_PATH_PROG(NPA_PROG, inews, "", $PATH:/usr/sbin:/usr/lib)
+ ])
+
+npa_flags="-h"
+AC_ARG_WITH(npa-flags,
+ AC_HELP_STRING([--with-npa-flags=FLAGS],[Flags to allow posting via local agent (-h)]),
+ [
+ if test "x$withval" != "xno" ; then
+ npa_flags=$withval
+ fi
+ ])
+if test -n "$NPA_PROG" ; then
+ AC_DEFINE_UNQUOTED([SENDNEWS], "$NPA_PROG $npa_flags", [Posting agent to use when no nntp-servers defined])
+fi
+
+dnl OPTION: password changing program
+AC_ARG_WITH(password-prog,
+ AC_HELP_STRING([--with-password-prog=PATH],[Password change program (/bin/passwd)]),
+ [
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ AC_PATH_PROG(PWPROG, passwd, "", $PATH:/usr/sbin:/usr/lib)
+ ;;
+ *)
+ AC_PATH_PROG(PWPROG, $withval, "", $PATH:/usr/sbin:/usr/lib)
+ ;;
+ esac
+ ],
+ [
+ AC_PATH_PROG(PWPROG, passwd, "", $PATH:/usr/sbin:/usr/lib)
+ ])
+if test -n "$PWPROG" ; then
+ AC_DEFINE_UNQUOTED([PASSWD_PROG], "$PWPROG", [Program users use to change their password])
+fi
+
+dnl OPTION: basic spell checking program
+AC_ARG_WITH(simple-spellcheck,
+ AC_HELP_STRING([--with-simple-spellcheck=PROG],[Spellcheck program reads stdin, emits misspellings on stdout]),
+ [
+ if test "x$withval" != "xno" ; then
+ SPELLPROG=$withval
+ fi
+ ],
+ [
+ AC_CHECK_PROG([SPELLPROG], [hunspell], [hunspell], [])
+ if test -z "$SPELLPROG" ; then
+ AC_CHECK_PROG([SPELLPROG], [aspell], [aspell], [])
+ if test -z "$SPELLPROG" ; then
+ AC_CHECK_PROG([SPELLPROG], [ispell], [ispell], [])
+ if test -z "$SPELLPROG" ; then
+ SPELLPROG="spell"
+ fi
+ fi
+ fi
+ ])
+
+if test "x$SPELLPROG" != "xno" ; then
+ AC_PATH_PROG(alpine_simple_spellcheck, $SPELLPROG)
+ if test -n "$alpine_simple_spellcheck" ; then
+ case "$SPELLPROG" in
+ hunspell)
+ alpine_simple_spellcheck="$alpine_simple_spellcheck -l"
+ ;;
+ aspell)
+ alpine_simple_spellcheck="$alpine_simple_spellcheck --dont-backup --mode=email list"
+ ;;
+ ispell)
+ alpine_simple_spellcheck="$alpine_simple_spellcheck -l"
+ ;;
+ *)
+ ;;
+ esac
+ fi
+fi
+
+dnl OPTION: interactive spell checking program
+AC_ARG_WITH(interactive-spellcheck,
+ AC_HELP_STRING([--with-interactive-spellcheck=PROG],[Interactive, filewise spell checker]),
+ [
+ if test "x$withval" != "xno" ; then
+ ISPELLPROG=$withval
+ fi
+ ],
+ [
+ AC_CHECK_PROG([ISPELLPROG], [hunspell], [hunspell], [])
+ if test -z "$ISPELLPROG" ; then
+ AC_CHECK_PROG([ISPELLPROG], [aspell], [aspell], [])
+ if test -z "$SPELLPROG" ; then
+ ISPELLPROG="ispell"
+ fi
+ fi
+ ])
+
+if test "x$ISPELLPROG" != "xno" ; then
+ AC_PATH_PROG(alpine_interactive_spellcheck, $ISPELLPROG)
+ if test -n "$alpine_interactive_spellcheck" ; then
+ case "$ISPELLPROG" in
+ aspell)
+ alpine_interactive_spellcheck="$alpine_interactive_spellcheck --dont-backup --mode=email check"
+ ;;
+ *)
+ ;;
+ esac
+ fi
+fi
+
+if test -n "$alpine_interactive_spellcheck" ; then
+ AC_DEFINE_UNQUOTED([DF_VAR_SPELLER], "$alpine_interactive_spellcheck", [Interactive, filewise spell checker])
+fi
+
+if test -z "$alpine_simple_spellcheck" -a -n "$alpine_interactive_spellcheck" ; then
+ alpine_simple_spellcheck=test
+fi
+AC_DEFINE_UNQUOTED([SPELLER], "$alpine_simple_spellcheck", [Simple spell checker: reads stdin, emits misspellings on stdout])
+
+dnl OPTION: system-pinerc
+dnl NOTE: historically we used /lib for the config dir.
+dnl don't ask, it was a long time ago. but, we can't
+dnl change it now without breaking compatibility
+case "$prefix" in
+ NONE) dpv=/usr/local/lib/pine.conf ;;
+ *) dpv=${prefix}/lib/pine.conf ;;
+esac
+AC_ARG_WITH(system-pinerc,
+ AC_HELP_STRING([--with-system-pinerc=VALUE], [System pinerc (/usr/local/lib/pine.conf)]),
+ [
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+ dpv=$withval
+ ;;
+ esac
+ ])
+AC_DEFINE_UNQUOTED(SYSTEM_PINERC, "$dpv", [System pinerc])
+
+dnl OPTION: system-fixed-pinerc
+dnl NOTE: historically we used /lib for the config dir.
+dnl don't ask, it was a long time ago. but, we can't
+dnl change it now without breaking compatibility
+case "$prefix" in
+ NONE) dpv=/usr/local/lib/pine.conf.fixed ;;
+ *) dpv=${prefix}/lib/pine.conf.fixed ;;
+esac
+AC_ARG_WITH(system-fixed-pinerc,
+ AC_HELP_STRING([--with-system-fixed-pinerc=VALUE], [System fixed pinerc (/usr/local/lib/pine.conf.fixed)]),
+ [
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+ dpv=$withval
+ ;;
+ esac
+ ])
+AC_DEFINE_UNQUOTED(SYSTEM_PINERC_FIXED, "$dpv", [System fixed pinerc])
+
+dnl Function to simplify setting alpine/pico defaults
+dnl usage: PINEVAR(configure-name, definition-name, definition-value, help)
+AC_DEFUN([PINEVAR],
+ [
+ dpv=$3
+ AC_ARG_WITH($1,
+ AC_HELP_STRING(--with-$1=VALUE, [$4 ($3)]),
+ [
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+ ])
+ AC_DEFINE_UNQUOTED($2, "$dpv", [Default configuration value])
+ ])
+
+dnl Function to simplify setting pine/pico defaults
+dnl usage: PINEVAR_UNQUOTED(configure-name, definition-name, definition-value, help)
+AC_DEFUN([PINEVAR_UNQUOTED],
+ [
+ dpv=$3
+ AC_ARG_WITH($1,
+ AC_HELP_STRING(--with-$1=VALUE, [$4 ($3)]),
+ [
+ if test "x$withval" != "xno" ; then
+ dpv=$withval
+ fi
+ ])
+ AC_DEFINE_UNQUOTED($2, $dpv, [Default configuration value])
+ ])
+
+PINEVAR(mailcheck-interval, DF_MAILCHECK, 150, [Specify default mail-check-interval])
+PINEVAR_UNQUOTED(checkpoint-interval, CHECK_POINT_TIME, 420, [Specify default checkpoint-interval])
+PINEVAR_UNQUOTED(checkpoint-frequency, CHECK_POINT_FREQ, 12, [State change count before checkpoint])
+PINEVAR_UNQUOTED(display-rows, DEFAULT_LINES_ON_TERMINAL, 24, [Initial rows on display])
+PINEVAR_UNQUOTED(display-columns, DEFAULT_COLUMNS_ON_TERMINAL, 80, [Initial columns on display])
+PINEVAR_UNQUOTED(max-display-rows, MAX_SCREEN_ROWS, 200, [Maximum display rows])
+PINEVAR_UNQUOTED(max-display-columns, MAX_SCREEN_COLS, 500, [Maximum display columns])
+PINEVAR(fill-column, DF_FILLCOL, 74, [Default fill column])
+PINEVAR_UNQUOTED(max_fill-column, MAX_FILLCOL, 80, [Maximum fill column])
+PINEVAR_UNQUOTED(debug-level, DEFAULT_DEBUG, 2, [Specify default debug verbosity level])
+PINEVAR_UNQUOTED(debug-files, NUMDEBUGFILES, 4, [Specify number of debug files])
+PINEVAR(debug-file, DEBUGFILE, [.pine-debug], [Specify debug file name])
+PINEVAR(forwarded-keyword, FORWARDED_FLAG, ["\$Forwarded"], [IMAP (c-client) keyword to store forwarded status])
+PINEVAR(display-overlap, DF_OVERLAP, [2], [Lines preserved while paging])
+PINEVAR(display-margin, DF_MARGIN, [0], [Lines visible while scrolling])
+PINEVAR(default-fcc, DF_DEFAULT_FCC, [sent-mail], [Default sent mail folder])
+PINEVAR(default-save-folder, DEFAULT_SAVE, [saved-messages], [Default save folder])
+PINEVAR(default-legacy-postponed-folder, POSTPONED_MAIL, [postponed-mail], [Pre Pine 3.90 postponed folder])
+PINEVAR(default-postponed-folder, POSTPONED_MSGS, [postponed-msgs], [Default postponed folder])
+PINEVAR(default-trash-folder, TRASH_FOLDER, [Trash], [Default Trash folder for Web Alpine])
+PINEVAR(default-interrupted-mail, INTERRUPTED_MAIL, [.pine-interrupted-mail], [Default folder for interrupted mail])
+PINEVAR(default-dead-letter-folder, DEADLETTER, [dead.letter], [Default dead letter folder])
+PINEVAR(default-mail-directory, DF_MAIL_DIRECTORY, [mail], [Default mail directory])
+PINEVAR(default-inbox-name, INBOX_NAME, [INBOX], [Default inbox name])
+PINEVAR(default-signature-file, DF_SIGNATURE_FILE, [.signature], [Default signature file])
+PINEVAR(default-elm-style-save, DF_ELM_STYLE_SAVE, [no], [Default to Elm style save])
+PINEVAR(default-header-in-reply, DF_HEADER_IN_REPLY, [no], [Include header in reply])
+PINEVAR(default-old-style-reply, DF_OLD_STYLE_REPLY, [no], [Default to old style reply])
+PINEVAR(default-use-only-domain-name, DF_USE_ONLY_DOMAIN_NAME, [no], [Default to using only the domain name])
+PINEVAR(default-save-by-sender, DF_SAVE_BY_SENDER, [no], [Default to save by sender])
+PINEVAR(default-sort-key, DF_SORT_KEY, [arrival], [Default sort key])
+PINEVAR(default-addressbook-sort-rule, DF_AB_SORT_RULE, [fullname-with-lists-last], [Default addressbook sort rule])
+PINEVAR(default-folder-sort-rule, DF_FLD_SORT_RULE, [alphabetical], [Default folder sort rule])
+PINEVAR(default-saved-message-name-rule, DF_SAVED_MSG_NAME_RULE, [default-folder], [Default saved message name rule])
+PINEVAR(default-fcc-rule, DF_FCC_RULE, [default-fcc], [Default fcc rule])
+PINEVAR(default-standard-printer, DF_STANDARD_PRINTER, [lpr], [Default standard printern])
+PINEVAR(default-ansi-printer, ANSI_PRINTER, [attached-to-ansi], [ANSI printer definition])
+PINEVAR(default-addressbook, DF_ADDRESSBOOK, [.addressbook], [Default addressbook name])
+PINEVAR(default-local-fullname, DF_LOCAL_FULLNAME, ["Local Support"], [Default local support fullname])
+PINEVAR(default-local-address, DF_LOCAL_ADDRESS, [postmaster], [Default local support address])
+PINEVAR(default-keyboard-lock-count, DF_KBLOCK_PASSWD_COUNT, [1], [Default keyboard lock count])
+PINEVAR(default-remote-addressbook-history, DF_REMOTE_ABOOK_HISTORY, [3], [Default address book history count])
+PINEVAR(smime-public-cert-directory, DF_PUBLICCERT_DIR, [.alpine-smime/public], [Default Public Cert Directory])
+PINEVAR(smime-private-key-directory, DF_PRIVATEKEY_DIR, [.alpine-smime/private], [Default Private Key Directory])
+PINEVAR(smime-cacert-directory, DF_CACERT_DIR, [.alpine-smime/ca], [Default Cert Authority Directory])
+PINEVAR_UNQUOTED(default-printer, DF_DEFAULT_PRINTER, [ANSI_PRINTER], [Default printer])
+
+dnl set PASSFILE?
+AC_ARG_WITH(passfile,
+ AC_HELP_STRING([--with-passfile=FILENAME],[Password cache file (NOT secure, NOT recommended)]),
+ [
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+ alpine_PASSFILE=$withval
+ ;;
+ esac
+ ])
+
+dnl os-specific credential cache?
+AC_ARG_WITH(local-password-cache,
+ AC_HELP_STRING([--without-local-password-cache],[Disable OS-specific password cache, if supported]),
+ [
+ alpine_os_credential_cache=$withval
+ ])
+
+dnl Particular os-specific credential cache?
+AC_ARG_WITH(local-password-cache-method,
+ AC_HELP_STRING([--with-local-password-cache-method],[OS-specific credential cache (OSX=APPLEKEYCHAIN, Windows=WINCRED)]),
+ [
+ alpine_os_credential_cache_method=$withval
+ ])
+
+if test -n "$alpine_PASSFILE" ; then
+ case $alpine_os_credential_cache in
+ no)
+ ;;
+ *)
+ alpine_os_credential_cache="no"
+ AC_MSG_NOTICE([--with-passfile definition overrides OS-Specific password caching])
+ ;;
+ esac
+ AC_DEFINE_UNQUOTED([PASSFILE], "$alpine_PASSFILE", [Password cache file (NOT secure. NOT recommended)])
+fi
+
+dnl set DF_SSHPATH?
+AC_ARG_WITH(default-sshpath,
+ AC_HELP_STRING([--with-default-sshpath=FILENAME],[set default value of ssh command path (defining should cause ssh to be preferred to rsh)]),
+ [
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+ AC_DEFINE_UNQUOTED([DF_SSHPATH], "$withval", [set default value of ssh command path (defining should cause ssh to be preferred to rsh)])
+ ;;
+ esac
+ ])
+
+dnl set DF_SSHCMD?
+AC_ARG_WITH(default-sshcmd,
+ AC_HELP_STRING([--with-default-sshcmd=PERCENT_S_STRING],[set default value of ssh command string (usually "%s %s -l %s exec /etc/r%sd")]),
+ [
+ case "$withval" in
+ no)
+ ;;
+ yes)
+ ;;
+ *)
+ AC_DEFINE_UNQUOTED([DF_SSHCMD], "$withval", [set default value of ssh command string (usually "%s %s -l %s exec /etc/r%sd")])
+ ;;
+ esac
+ ])
+
+dnl Include SSL?
+dnl Set SSLDIR for c-client make
+AC_ARG_WITH(ssl,
+ AC_HELP_STRING([--without-ssl],[Disable SSL support (OpenSSL)]),
+ [ with_ssl=$withval ])
+
+if test "x$with_ssl" = "xno" ; then
+ alpine_SSLTYPE="none"
+else
+ dnl preload c-client default locations/options
+ case $host in
+ *-linux-gnu)
+ if test -f /etc/fedora-release -o -f /etc/redhat-release -o -f /etc/redhat_version ; then
+ alpine_SSLTYPE="nopwd"
+ if test -d /etc/pki/tls ; then
+ alpine_SSLDIR="/etc/pki/tls"
+ else
+ alpine_SSLDIR="/usr/share/ssl"
+ fi
+ elif test -f /etc/SuSE-release ; then
+ alpine_SSLTYPE="nopwd"
+ alpine_SSLDIR="/usr/share/ssl"
+ alpine_SSLCERTS="/etc/ssl/certs"
+ elif test -d /etc/osso-af-init ; then
+ alpine_SSLTYPE="nopwd"
+ alpine_SSLDIR="/usr"
+ alpine_SSLCERTS="/usr/share/certs"
+ else
+ alpine_SSLTYPE="nopwd"
+ alpine_SSLDIR="/usr"
+ alpine_SSLCERTS="/etc/ssl/certs"
+ fi
+ ;;
+ *-apple-darwin*)
+ alpine_SSLTYPE="nopwd"
+ alpine_SSLCERTS="/System/Library/OpenSSL/certs"
+ ;;
+ *-openbsd*)
+ alpine_SSLTYPE="nopwd"
+ alpine_SSLDIR="/usr"
+ alpine_SSLCERTS="/etc/ssl/certs"
+ ;;
+ *-sco-sysv* | *-sysv*UnixWare | *-sysv*OpenUNIX)
+ alpine_SSLTYPE="sco.nopwd"
+ alpine_SSLDIR=/usr/local/ssl
+ ;;
+ *-*-solaris*)
+ if test -d /usr/sfw/include/openssl ; then
+ alpine_SSLDIR="/usr/sfw"
+ elif test -d /opt/csw/include/openssl ; then
+ alpine_SSLDIR="/opt/csw"
+ if test -d /opt/csw/ssl/certs ; then
+ alpine_SSLCERTS="/opt/csw/ssl/certs"
+ fi
+ fi
+ if test -z "$alpine_SSLCERTS" -a -d /etc/certs ; then
+ alpine_SSLCERTS="/etc/certs"
+ fi
+ ;;
+ *)
+ alpine_SSLTYPE="nopwd"
+ alpine_SSLDIR=/usr/local/ssl
+ ;;
+ esac
+
+ AC_ARG_WITH(ssl-dir,
+ AC_HELP_STRING([--with-ssl-dir=DIR], [Root of SSL lib/include path]),
+ [
+ if test "x$withval" != "xno" ; then
+ alpine_SSLDIR=$withval
+ fi
+ ])
+
+ AC_ARG_WITH(ssl-certs-dir,
+ AC_HELP_STRING([--with-ssl-certs-dir=DIR], [Path to SSL certificate directory]),
+ [
+ if test "x$withval" != "xno" ; then
+ alpine_SSLCERTS=$withval
+ fi
+ ])
+
+ AC_ARG_WITH(ssl-include-dir,
+ AC_HELP_STRING([--with-ssl-include-dir=DIR], [SSL include file path]),
+ [
+ if test "x$withval" != "xno" ; then
+ alpine_SSLINCLUDE=$withval
+ fi
+ ])
+
+ AC_ARG_WITH(ssl-lib-dir,
+ AC_HELP_STRING([--with-ssl-lib-dir=DIR], [SSL library path]),
+ [
+ if test "x$withval" != "xno" ; then
+ alpine_SSLLIB=$withval
+ fi
+ ])
+ dnl setup globals so tests below work
+ if test -n "$alpine_SSLINCLUDE" ; then
+ CPPCFLAGS="-I$alpine_SSLINCLUDE $CPPFLAGS"
+ elif test -n "$alpine_SSLDIR" ; then
+ CPPFLAGS="-I${alpine_SSLDIR}/include $CPPFLAGS"
+ fi
+ if test -n "$alpine_SSLLIB" ; then
+ LDFLAGS="-L$alpine_SSLLIB $LDFLAGS"
+ elif test -n "$alpine_SSLDIR" ; then
+ LDFLAGS="-L${alpine_SSLDIR}/lib $LDFLAGS"
+ fi
+fi
+
+dnl Include Kerberos?
+dnl Set GSSDIR for c-client make
+AC_ARG_WITH(krb5,
+ AC_HELP_STRING([--without-krb5],[Disable Kerberos support]),
+ [ with_krb5=$withval ])
+
+if test "x$with_krb5" = "xno" ; then
+ alpine_GSSTYPE="none"
+else
+ alpine_GSSTYPE=
+
+ AC_ARG_WITH(krb5-dir,
+ AC_HELP_STRING([--with-krb5-dir=DIR], [Root of Kerberos lib/include path]),
+ [
+ if test "x$withval" != "xno" ; then
+ CPPFLAGS="$CPPFLAGS -I${withval}/include"
+ LDFLAGS="$LDFLAGS -L${withval}/lib"
+ fi
+ ])
+
+ AC_ARG_WITH(krb5-include-dir,
+ AC_HELP_STRING([--with-krb5-include-dir=DIR], [Kerberos include file path]),
+ [
+ if test "x$withval" != "xno" ; then
+ CPPFLAGS="$CPPFLAGS -I$withval"
+ fi
+ ])
+
+ AC_ARG_WITH(krb5-lib-dir,
+ AC_HELP_STRING([--with-krb5-lib-dir=DIR], [Kerberos library path]),
+ [
+ if test "x$withval" != "xno" ; then
+ LDFLAGS="$LDFLAGS -L$withval"
+ fi
+ ])
+fi
+
+dnl Include LDAP?
+AC_ARG_WITH(ldap,
+ AC_HELP_STRING([--without-ldap],[Disable LDAP query support]),
+ [ with_ldap=$withval ])
+
+if test "x$with_ldap" = "xno" ; then
+ alpine_with_ldap=
+ AC_MSG_NOTICE([Excluding LDAP Support])
+else
+ dnl Do stuff to figure out where OpenLDAP is
+
+ alpine_with_ldap=yes
+ AC_ARG_WITH(ldap-dir,
+ AC_HELP_STRING([--with-ldap-dir=DIR], [Root of LDAP lib/include path]),
+ [
+ if test "x$withval" != "xno" ; then
+ CPPFLAGS="$CPPFLAGS -I${withval}/include"
+ LDFLAGS="$LDFLAGS -L${withval}/lib"
+ fi
+ ])
+
+ AC_ARG_WITH(ldap-include-dir,
+ AC_HELP_STRING([--with-ldap-include-dir=DIR], [Directory containing LDAP include files]),
+ [
+ if test "x$withval" != "xno" ; then
+ CPPFLAGS="$CPPFLAGS -I$withval"
+ fi
+ ])
+
+ AC_ARG_WITH(ldap-lib-dir,
+ AC_HELP_STRING([--with-ldap-lib-dir=DIR], [LDAP library path]),
+ [
+ if test "x$withval" != "xno" ; then
+ LDFLAGS="$LDFLAGS -L$withval"
+ fi
+ ])
+fi
+
+dnl Include SMIME?
+AC_ARG_WITH(smime,
+ AC_HELP_STRING([--without-smime],[Disable S/MIME]),
+ [ with_smime=$withval ])
+
+dnl Include TCL?
+AC_ARG_WITH(tcl,
+ AC_HELP_STRING([--without-tcl],[Disable TCL, thus Web Alpine support]),
+ [ with_tcl=$withval ])
+
+if test "x$with_tcl" = "xno" ; then
+ WEB_BUILD=
+ AC_MSG_NOTICE([Excluding TCL Support, and thus Web Alpine Support])
+else
+ AC_ARG_WITH(tcl-lib,
+ AC_HELP_STRING([--with-tcl-lib=LIBRARY], [Specific TCL Library, like \"tcl8.4\"]),
+ [
+ if test "x$withval" != "xno" ; then
+ alpine_TCLLIB=$withval
+ fi
+ ])
+ AC_ARG_WITH(tcl-include,
+ AC_HELP_STRING([--with-tcl-include=DIR], [Directory containing TCL include files]),
+ [
+ if test "x$withval" != "xno" ; then
+ CPPFLAGS="$CPPFLAGS -I$withval"
+ alpine_TCLINC=$withval
+ fi
+ ])
+fi
+
+AC_ARG_WITH(supplied-regex,
+ AC_HELP_STRING([--with-supplied-regex],[Use regex library supplied with alpine]),
+ [ alpine_REGEX=$withval ])
+
+AC_ARG_WITH(pthread,
+ AC_HELP_STRING([--without-pthread],[Do NOT test for nor build with POSIX thread support]),
+ [ with_pthread=$withval ])
+
+AC_ARG_WITH(system-mail-directory,
+ AC_HELP_STRING([--with-system-mail-directory=DIR], [Directory where local mail is delivered]),
+ [
+ if test "x$withval" != "xno" ; then
+ alpine_with_local_maildir="$withval"
+ fi
+ ])
+
+AC_ARG_WITH(c-client-target,
+ AC_HELP_STRING([--with-c-client-target=TARGET], [IMAP build target (see imap/Makefile)]),
+ [
+ if test "x$withval" != "xno" ;then
+ alpine_with_c_client_target="$withval"
+ fi
+ ])
+
+
+dnl Without IPv6?
+AC_ARG_WITH(ipv6,
+ AC_HELP_STRING([--without-ipv6],[Disable IPv6, primarily to work around resolver problems]),
+ [ with_ipv6=$withval ])
+
+if test "x$with_ipv6" = "xno" ; then
+ AC_MSG_NOTICE([Excluding IPv6 Support])
+ c_client_specials="${c_client_specials}IP6=4 "
+ c_client_ip6="true"
+else
+ c_client_ip6="touch imap/ip6"
+fi
+
+
+dnl CHECK LIBRARIES
+
+if test x$enable_dmalloc = "xyes" ; then
+ if test "x$with_pthread" = "xyes" ; then
+ dmalloc_lib=dmallocth
+ else
+ dmalloc_lib=dmalloc
+ fi
+
+ AC_CHECK_LIB($dmalloc_lib,dmalloc_shutdown,,
+ [
+ AC_ERROR([$dmalloc_lib requested, but -ldmalloc not found])
+ ])
+fi
+
+dnl which terminal cabability database
+AC_CHECK_LIB(tinfo, setupterm,
+ [
+ alpine_termdata=info
+ LIBS="$LIBS -ltinfo"
+ ],
+ [
+ AC_CHECK_LIB(ncurses, setupterm,
+ [
+ alpine_termdata=info
+ LIBS="$LIBS -lncurses"
+ ],
+ [
+ AC_CHECK_LIB(curses, setupterm,
+ [
+ alpine_termdata=info
+ LIBS="$LIBS -lcurses"
+ ],
+ [
+ AC_CHECK_LIB(termlib, tgetent,
+ [
+ alpine_termdata=cap
+ LIBS="$LIBS -ltermlib"
+ ],
+ [
+ AC_CHECK_LIB(termcap, tgetent,
+ [
+ alpine_termdata=cap
+ LIBS="$LIBS -ltermcap"
+ ],
+ [
+ AC_ERROR([Terminfo/termcap not found])
+ ])
+ ])
+ ])
+ ])
+ ])
+case $alpine_termdata in
+ info)
+ AC_DEFINE(HAS_TERMINFO, [1], [Define if systems uses terminfo terminal database])
+ ;;
+ cap)
+ AC_DEFINE(HAS_TERMCAP, [1], [Define if systems uses termcap terminal database])
+ ;;
+esac
+
+dnl provide LDAP support?
+if test "$alpine_with_ldap" = "yes" ; then
+ alpine_has_ldap=
+ AC_CHECK_LIB(lber, ber_alloc,
+ [
+ LIBS="$LIBS -llber"
+ ])
+ AC_SEARCH_LIBS(ldap_init,ldap,
+ [
+ alpine_has_ldap=yes
+ ],
+ [
+ AC_SEARCH_LIBS(ldap_open,ldap,
+ [
+ alpine_has_ldap=yes
+ ])
+ ])
+
+ if test "$alpine_has_ldap" = "yes" ; then
+ AC_MSG_NOTICE([Including LDAP Support])
+ AC_DEFINE([ENABLE_LDAP], [], [Enable LDAP query support])
+
+ dnl we use deprecated functions (ldap_get_values)
+ dnl OpenLDAP 2.3.x doesn't define LDAP_DEPRECATED by default like 2.2.x
+ AC_MSG_CHECKING([if we should define LDAP_DEPRECATED])
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#include <stdio.h>
+#include <string.h>
+#include <ldap.h>
+int main(void) {
+
+ if (LDAP_VENDOR_VERSION >= 20300)
+ exit(0);
+
+ exit(2);
+}
+ ]])],
+ [
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(LDAP_DEPRECATED, 1,
+ [Define if you use OpenLDAP 2.3.x deprecated functions])
+
+ ],
+ [
+ AC_MSG_RESULT(no)
+ ],
+ [
+ AC_MSG_WARN([cross compiling: not checking])
+ ]
+ )
+
+ else
+ AC_MSG_NOTICE([Cannot find LDAP functions! Excluding LDAP support.])
+ fi
+fi
+
+dnl provide SSL support?
+if test "x$alpine_SSLTYPE" != "xnone" ; then
+ AC_SEARCH_LIBS(SSL_library_init,ssl,
+ [
+ LIBS="$LIBS -lssl"
+ ])
+ if test "x$alpine_SSLTYPE" = "xnone" ; then
+ AC_MSG_NOTICE([OpenSSL libraries NOT found])
+ else
+ AC_MSG_NOTICE([OpenSSL libraries FOUND])
+ fi
+fi
+
+dnl provide KRB5 support?
+if test "x$alpine_GSSTYPE" != "xnone" ; then
+ AC_SEARCH_LIBS(gss_init_sec_context,gss gssapi gssapi_krb5,
+ [
+ dnl MIT-based?
+ AC_CHECK_HEADER([gssapi/gssapi_generic.h],,
+ [
+ if test ! -d /usr/kerberos/include ; then
+ alpine_GSSTYPE="none"
+ alpine_gss_none_reason="header files not found"
+ fi
+ ])
+ ],
+ [
+ alpine_GSSTYPE="none"
+ alpine_gss_none_reason="libraries not found"
+ ])
+ if test -n "$alpine_gss_none_reason" ; then
+ AC_MSG_NOTICE([NOT including Kerberos Support: $alpine_gss_none_reason])
+ fi
+fi
+
+dnl check for tcl libraries for Web Alpine (HACKY)
+if test -n "$WEB_BUILD" ; then
+ AC_SEARCH_LIBS([Tcl_Eval],[$alpine_TCLLIB tcl8.4 tcl8.3 tcl84 tcl83 tcl],,
+ [
+ WEB_BUILD=
+ ])
+
+ dnl look for header file
+ if test -n "$alpine_TCLINC" ; then
+ AC_CHECK_HEADER($alpine_TCLINC,,
+ [
+ WEB_BUILD=
+ ])
+ if test -z "$WEB_BUILD" ; then
+ AC_MSG_NOTICE([Tcl Include file NOT found])
+ fi
+ else
+ AC_CHECK_HEADER(tcl.h,,
+ [
+ for dir in tcl8.4 tcl8.3 tcl84 tcl83 ; do
+ AC_CHECK_FILE([/usr/include/$dir/tcl.h],
+ [
+ found=yes
+ ])
+ if test "$found" = "yes" ; then
+ CPPFLAGS="$CPPFLAGS -I/usr/include/$dir"
+ break
+ fi
+ done
+ ])
+ fi
+fi
+
+dnl Local or supplied regex?
+if test x$alpine_REGEX != "xyes" ; then
+ AC_SEARCH_LIBS([regcomp],posix regexp regex re,,
+ [
+ if test x$alpine_REGEX = "xno" ; then
+ AC_ERROR([Unable to find system regex library])
+ else
+ alpine_REGEX=yes
+ fi
+ ])
+fi
+if test x$alpine_REGEX != "xyes" ; then
+ AC_CHECK_HEADER([regex.h],,
+ [
+ if test x$alpine_REGEX = "xno" ; then
+ AC_ERROR([Unable to find system regex include file])
+ else
+ alpine_REGEX=yes
+ fi
+ ])
+fi
+AC_DEFINE(HAVE_REGEX_H,1,[Regular expression header file exists])
+if test x$alpine_REGEX = "xyes" ; then
+ CPPFLAGS="$CPPFLAGS -I${top_builddir}/regex"
+ LDFLAGS="$LDFLAGS -L${top_builddir}/regex -lregex"
+ REGEX_BUILD=regex
+fi
+
+if test "x$with_pthread" != "xno" ; then
+ AC_MSG_CHECKING([for pthread support])
+ ACX_PTHREAD(
+ [
+ AC_MSG_RESULT([yes])
+ case "$target" in
+ *openbsd*)
+ AC_MSG_NOTICE([WARNING: pthread support on OpenBSD is unstable!])
+ AM_CFLAGS="$AM_CFLAGS -pthread"
+ ;;
+ esac
+ LIBS="$PTHREAD_LIBS $LIBS"
+ AM_CFLAGS="$AM_CFLAGS $PTHREAD_CFLAGS"
+ CC="$PTHREAD_CC"
+ AC_DEFINE([HAVE_PTHREAD], [1], [System has pthread support])
+ ],
+ [
+ AC_MSG_RESULT([no])
+ ])
+
+ AC_SEARCH_LIBS([nanosleep], [rt posix4],
+ [
+ AC_DEFINE([HAVE_NANOSLEEP], [1], [Define if system supports subsecond, non-alarm sleep])
+ ])
+fi
+
+
+
+dnl CHECK HEADERS
+
+AC_HEADER_STDC
+AC_HEADER_DIRENT
+AC_HEADER_STAT
+AC_HEADER_SYS_WAIT
+AC_HEADER_TIME
+AC_HEADER_TIOCGWINSZ
+
+AC_UNISTD_H
+
+AC_CHECK_HEADERS([errno.h \
+ ctype.h \
+ fcntl.h \
+ signal.h \
+ setjmp.h \
+ memory.h \
+ sys/param.h \
+ sys/socket.h \
+ sys/uio.h \
+ sys/un.h \
+ limits.h \
+ wchar.h \
+ sys/poll.h \
+ stropts.h \
+ netdb.h \
+ syslog.h \
+ sys/syslog.h \
+ locale.h \
+ langinfo.h \
+ utime.h \
+ sys/utime.h \
+ pthread.h \
+ pwd.h \
+ assert.h])
+
+dnl terminal line discipline?
+AC_CHECK_HEADER([termios.h],
+ AC_DEFINE(HAS_TERMIOS, [1], [Define if systems uses termios terminal control]),
+ [
+ AC_CHECK_HEADER([termio.h],
+ AC_DEFINE(HAS_TERMIO, [1], [Define if systems uses termio terminal control]),
+ [
+ AC_CHECK_HEADER([sgtty.h],
+ AC_DEFINE(HAS_SGTTY, [1], [Define if systems uses old BSD-style terminal control]),
+ [
+ AC_ERROR([Unable to figure out terminal control method])
+ ])
+ ])
+ ])
+
+
+dnl CHECK TYPEDEFS
+
+AC_TYPE_SIGNAL
+AC_TYPE_SIZE_T
+AC_TYPE_MODE_T
+AC_TYPE_PID_T
+AC_TYPE_UID_T
+AC_STRUCT_TM
+
+AC_CHECK_TYPES([union wait])
+
+AC_CHECK_HEADERS([stdint.h], [uint16=uint16_t], [
+ AC_CHECK_HEADERS([inttypes.h], [uint16=uint16_t], [
+ AC_CHECK_HEADERS([sys/types.h], [uint16=u_int16_t], [
+ AC_CHECK_SIZEOF(unsigned short)
+ if test $ac_cv_sizeof_unsigned_short -eq 2 ; then
+ uint16="unsigned short"
+ else
+ AC_CHECK_SIZEOF(unsigned int)
+ if $ac_cv_sizeof_unsigned_int -eq 2 ; then
+ uint16="unsigned int"
+ else
+ AC_ERROR([Unable to determine 16 bit integer type])
+ fi
+ fi
+ ])
+ ])
+])
+AC_DEFINE_UNQUOTED([UINT16], $uint16, [System defined unsigned 16 bit integer])
+
+AC_CHECK_HEADERS([stdint.h], [uint32=uint32_t], [
+ AC_CHECK_HEADERS([inttypes.h], [uint32=uint32_t], [
+ AC_CHECK_HEADERS([sys/types.h], [uint32=u_int32_t], [
+ AC_CHECK_SIZEOF(unsigned int)
+ if test $ac_cv_sizeof_unsigned_int -eq 4 ; then
+ uint32="unsigned int"
+ else
+ AC_CHECK_SIZEOF(unsigned long)
+ if $ac_cv_sizeof_unsigned_long -eq 4 ; then
+ uint32="unsigned long"
+ else
+ AC_ERROR([Unable to determine 32 bit integer type])
+ fi
+ fi
+ ])
+ ])
+])
+AC_DEFINE_UNQUOTED([UINT32], $uint32, [System defined unsigned 32 bit integer])
+
+AC_CACHE_CHECK(argument pointer type of qsort compare function and base,
+ac_cv_func_qsort_argtype,
+[AC_TRY_COMPILE([
+#if HAVE_STDLIB_H
+#include "stdlib.h"
+#endif
+
+extern void *base;
+extern sortf(const void *, const void *);
+int sortf(a, b)
+ const void *a;
+ const void *b; { return 0; }
+],[
+qsort(base, 2, sizeof(char *), sortf);
+],
+ ac_cv_func_qsort_argtype=void, ac_cv_func_qsort_argtype=char)
+])
+AC_DEFINE_UNQUOTED([qsort_t], $ac_cv_func_qsort_argtype, [qsort compare function argument type])
+
+dnl check for "struct passwd" ?
+
+AC_FUNC_SELECT_ARGTYPES
+
+AC_FUNC_STRCOLL
+
+dnl CHECK FOR LIBRARY FUNCTIONS
+
+AC_FUNC_FORK
+AC_CHECK_FUNCS([strchr \
+ memcpy \
+ strtol \
+ strtoul \
+ select \
+ poll \
+ qsort \
+ getuid \
+ getpwuid \
+ getpwnam \
+ gettimeofday \
+ tmpfile \
+ uname \
+ rename \
+ read \
+ signal \
+ setjmp \
+ chown \
+ wait4 \
+ waitpid \
+ wait \
+ srandom \
+ popen \
+ pclose \
+ fsync \
+ truncate \
+ listen \
+ wcwidth \
+ mbstowcs \
+ wcrtomb \
+ putenv \
+ setenv])
+
+AC_SEARCH_LIBS([gethostname],[nsl])
+
+AC_SEARCH_LIBS([socket],[socket],,
+ [
+ WEB_BUILD=
+ ])
+
+AC_SEARCH_LIBS([bind],[bind],,
+ [
+ WEB_BUILD=
+ ])
+
+dnl check for POSIX signal interface
+AC_CHECK_FUNCS([sigaction sigemptyset sigaddset sigprocmask],
+ [
+ AC_DEFINE([POSIX_SIGNALS], [], [Define if system supports POSIX signal interface])
+ ],
+ [
+ AC_CHECK_FUNCS([sigset sigrelse],
+ [
+ AC_DEFINE([SYSV_SIGNALS], [], [Define if system supports SYSV signal interface])
+ ])
+ ])
+
+AC_SEARCH_LIBS([syslog], [bsd socket inet],
+ [
+ AC_DEFINE([HAVE_SYSLOG], [1], [Define if system supplies syslog() logging])
+ ])
+
+
+dnl HOST-OS SPECIFIC DEFINITIONS
+dnl Tests and assignments below are mostly to coax the appropriate
+dnl build from c-client. Most of this will go away when c-client
+dnl adopts configure
+case "$host" in
+ *-linux-gnu*|*-k*bsd*-gnu*)
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ if test -f /etc/fedora-release ; then
+ systype="LFD"
+ if test -d /etc/pki/tls ; then
+ alpine_c_client_target="lfd"
+ else
+ alpine_c_client_target="lrh"
+ fi
+ elif test -f /etc/mandrake-release ; then
+ systype="LMD"
+ alpine_c_client_target="lmd"
+ elif test -f /etc/redhat-release -o -f /etc/redhat_version ; then
+ systype="LRH"
+ if test -d /etc/pki/tls ; then
+ alpine_c_client_target="lr5"
+ else
+ alpine_c_client_target="lrh"
+ fi
+ elif test -f /etc/debian_release -o -f /etc/debian_version ; then
+ if test -d /etc/osso-af-init ; then
+ systype="LN8"
+ alpine_c_client_target="ln8"
+ else
+ systype="DEB"
+ alpine_c_client_target="ldb"
+ fi
+ elif test -f /etc/SuSE-release ; then
+ systype="LSU"
+ alpine_c_client_target="lsu"
+ else
+ systype="LNX"
+ AC_CHECK_LIB(pam, pam_start,
+ [
+ alpine_c_client_target="lnp"
+ ],
+ [
+ if test -f /etc/shadow ; then
+ alpine_c_client_target="slx"
+ else
+ alpine_c_client_target="lnx"
+ fi
+ ])
+ fi
+ ;;
+ *-apple-darwin*)
+ systype="OSX"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ LIBS="$LIBS -framework Carbon -framework ApplicationServices -framework Security"
+ AM_CFLAGS="$AM_CFLAGS -Dbsd"
+ dnl SEE include/system.h
+ AC_DEFINE([OSX_TARGET],[1],[OSX TARGET])
+ case "$alpine_os_credential_cache" in
+ no)
+ ;;
+ *)
+ AC_DEFINE([APPLEKEYCHAIN], [1], [Use Apple OS X key chain for credential caching])
+ ;;
+ esac
+ AC_CHECK_LIB(pam, pam_start,
+ [
+ alpine_c_client_target="oxp"
+ ],
+ [
+ alpine_c_client_target="osx"
+ ])
+ ;;
+ *-*-solaris*)
+ if test x$GCC = "xyes" ; then
+ systype="GSO"
+ alpine_c_client_target="gso"
+ else
+ systype="SOC"
+ alpine_c_client_target="soc"
+ AC_DEFINE([__EXTENSIONS__], [1], [Enable extended pthread features on Solaris])
+ fi
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ dnl possible other OS's need this.
+ dnl Use autoconf 2.61's AC_USE_SYSTEM_EXTENSIONS at some point
+ ;;
+ *-*-sunos4*)
+ systype="SUN"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="s40"
+ ;;
+ *-*-sco3.2v5*)
+ if test x$GCC = "xyes" ; then
+ systype="GO5"
+ alpine_c_client_target="go5"
+ else
+ systype="SC5"
+ alpine_c_client_target="sc5"
+ fi
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ dnl possible other OS's need this.
+ dnl Use autoconf 2.61's AC_USE_SYSTEM_EXTENSIONS at some point
+ ;;
+ *-next-*)
+ systype="NXT"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="nx3"
+ ;;
+ *-*-netbsd*)
+ systype="NEB"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="neb"
+ ;;
+ *-*-dragonfly*)
+ systype="DFB"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="neb"
+ ;;
+ *-*-bsdi*)
+ systype="BSI"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="bsi"
+ ;;
+ *-*-freebsd*)
+ systype="BSF"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="bsf"
+ LIBS="$LIBS $LIBINTL"
+ ;;
+ *-*-openbsd*)
+ systype="BSO"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="bso"
+ LIBS="$LIBS $LIBINTL"
+ ;;
+ *-*-aix5*)
+ systype="A52"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="a52"
+ ;;
+ *-*-aix4*)
+ systype="A41"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="a41"
+ ;;
+ *-*-aix3*)
+ systype="A32"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="a32"
+ ;;
+ *-*UNIX_SV | *-*-sysv5UnixWare7* | *-*OpenUNIX*)
+ systype="UW2"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="uw2"
+ ;;
+ *-*-osf5*)
+ systype="OSF"
+ alpine_path_delim="/"
+ alpine_mode_readonly="(0600)"
+ alpine_c_client_target="osf"
+ ;;
+ *-*-cygwin)
+ systype="CYG"
+ alpine_path_delim="\\\\"
+ alpine_mode_readonly="(S_IREAD | S_IWRITE)"
+ alpine_c_client_target="cyg"
+ LIBS="$LIBS $LIBINTL"
+ ;;
+ windows* | *-*-pw32*)
+ systype="WNT"
+ alpine_path_delim="\\"
+ alpine_mode_readonly="(S_IREAD | S_IWRITE)"
+ alpine_c_client_target="wnt"
+ AC_DEFINE([_WINDOWS], [1], [Windows is just too different])
+ ;;
+ *)
+ AC_ERROR([Unrecognized system: $host])
+ ;;
+esac
+
+dnl set pico newmail check directory
+if test -n "$alpine_with_local_maildir" ; then
+ alpine_local_maildir=$alpine_with_local_maildir
+elif test -d /var/spool/mail ; then
+ alpine_local_maildir="/var/spool/mail"
+elif test -d /var/mail ; then
+ alpine_local_maildir="/var/mail"
+else
+ alpine_local_maildir="/usr/spool/mail"
+fi
+
+dnl set c-client make target
+if test -n "$alpine_with_c_client_target" ; then
+ alpine_c_client_target=$alpine_with_c_client_target
+fi
+
+AC_DEFINE_UNQUOTED([SYSTYPE], "$systype", [Pine-Centric Host Specifier])
+AC_DEFINE_UNQUOTED([C_FILESEP],'$alpine_path_delim',[File name separator as character constant])
+AC_DEFINE_UNQUOTED([S_FILESEP],"$alpine_path_delim",[File name separator as string constant])
+AC_DEFINE_UNQUOTED([MAILDIR],"$alpine_local_maildir",[Path to local inboxes for pico newmail check])
+AC_DEFINE_UNQUOTED([MODE_READONLY], $alpine_mode_readonly, [File mode used to set readonly access])
+
+dnl c-client make particulars
+AC_SUBST([C_CLIENT_TARGET], $alpine_c_client_target)
+AC_SUBST([C_CLIENT_WITH_IPV6], $c_client_ip6)
+if test "x$alpine_SSLTYPE" = "xnone" ; then
+ AC_MSG_NOTICE([* * * NOT Including SSL Support])
+ c_client_specials="${c_client_specials}SSLTYPE=none "
+else
+ dnl issue any warnings for common OpenSSL issues
+
+ if test -n "$alpine_SSLCERTS" -a -d "$alpine_SSLCERTS" ; then
+ certdir="$alpine_SSLCERTS"
+ elif test -n "$alpine_SSLDIR" -a -d "${alpine_SSLDIR}/certs" ; then
+ certdir="${alpine_SSLDIR}/certs"
+ else
+ AC_MSG_NOTICE([SSL Problem: certificate directory not found])
+ fi
+
+ if test "x$with_smime" != "xno" ; then
+ if test -n "$certdir" ; then
+ AC_DEFINE([SMIME], [], [Enable S/MIME code])
+ AC_DEFINE_UNQUOTED([SMIME_SSLCERTS],"$certdir",[Directory where S/MIME CACerts are located])
+ fi
+ fi
+
+ if test ! -f ${certdir}/factory.pem ; then
+ AC_MSG_NOTICE([* * * SSL file "${certdir}/factory.pem" is missing.])
+ AC_MSG_NOTICE([* * * This might indicate that CA certs did not get properly])
+ AC_MSG_NOTICE([* * * installed. If you get certificate validation failures])
+ AC_MSG_NOTICE([* * * in Alpine, this might be the reason for them.])
+ fi
+
+ if test -z "`ls ${certdir} | $EGREP '^@<:@0-9A-Fa-f@:>@{8}\.@<:@0-9@:>@'`" ; then
+ AC_MSG_NOTICE([* * * No 8-hexdigit symlinks in certificate directory "${certdir}".])
+ AC_MSG_NOTICE([* * * This might indicate that CA certs did not get properly])
+ AC_MSG_NOTICE([* * * installed. If you get certificate validation failures])
+ AC_MSG_NOTICE([* * * in Alpine, this might be the reason for them.])
+ fi
+
+ if test -n "$alpine_SSLDIR" ; then
+ c_client_specials="${c_client_specials}SSLDIR=$alpine_SSLDIR "
+ fi
+
+ if test -n "$alpine_SSLCERTS" ; then
+ c_client_specials="${c_client_specials}SSLCERTS=$alpine_SSLCERTS "
+ fi
+
+ if test -n "$alpine_SSLINCLUDE" ; then
+ c_client_specials="${c_client_specials}SSLINCLUDE=$alpine_SSLINCLUDE "
+ fi
+
+ if test -n "$alpine_SSLLIB" ; then
+ c_client_specials="${c_client_specials}SSLLIB=$alpine_SSLLIB "
+ fi
+
+fi
+
+if test "x$alpine_GSSTYPE" != "xnone" ; then
+ c_client_specials="${c_client_specials}EXTRAAUTHENTICATORS=gss "
+ AC_MSG_NOTICE([* * * Including Kerberos5 functionality])
+fi
+
+if test -n "$CPPFLAGS" ; then
+ alpine_c_client_cflags="$alpine_c_client_cflags ${CPPFLAGS}"
+fi
+if test -n "$CFLAGS" ; then
+ alpine_c_client_cflags="$alpine_c_client_cflags ${CFLAGS}"
+fi
+
+if test -n "$alpine_c_client_cflags" ; then
+ AC_SUBST(C_CLIENT_CFLAGS, EXTRACFLAGS=\"$alpine_c_client_cflags\")
+fi
+
+if test -n "$LDFLAGS" ; then
+ alpine_c_client_ldflags="$alpine_c_client_ldflags ${LDFLAGS}"
+fi
+if test -n "$LIBS" ; then
+ alpine_c_client_ldflags="$alpine_c_client_ldflags ${LIBS}"
+fi
+
+if test -n "$alpine_c_client_ldflags" ; then
+ AC_SUBST(C_CLIENT_LDFLAGS, EXTRALDFLAGS=\"$alpine_c_client_ldflags\")
+fi
+
+if test -n "$alpine_c_client_gccoptlevel" ; then
+ AC_SUBST(C_CLIENT_GCCOPTLEVEL, GCCOPTLEVEL=\"$alpine_c_client_gccoptlevel\")
+fi
+
+AC_SUBST([C_CLIENT_SPECIALS], $c_client_specials)
+
+dnl Deal with Web Alpine
+if test -z "$WEB_BUILD" ; then
+ WEB_PUBCOOKIE_BUILD=
+ AC_MSG_NOTICE([* * * TCL libraries could not be found.])
+ AC_MSG_NOTICE([* * * WEB ALPINE COMPONENT WILL NOT BE BUILT.])
+else
+ if test -n "$WEB_PUBCOOKIE_BUILD" ; then
+ if test "x$alpine_GSSTYPE" = "xnone" ; then
+ WEB_PUBCOOKIE_BUILD=
+ AC_MSG_NOTICE([* * * Kerberos5 support not found.])
+ AC_MSG_NOTICE([* * * WEB ALPINE PUBCOOKIE COMPONENT WILL NOT BE BUILT.])
+ elif test -z "$WEB_BINDIR" ; then
+ WEB_PUBCOOKIE_BUILD=
+ AC_MSG_NOTICE([* * * Web Alpine binary directory not provided.])
+ AC_MSG_ERROR([* * * --with-pubcookie requires --with-web-bin=PATH.
+ Please re-run configure with these options:
+ --with-pubcookie --with-web-bin=/usr/local/libexec/alpine/bin])
+ else
+ AC_DEFINE([PUBCOOKIE],[1],[Include support for UW Pubcookie Web Authentication])
+ WEB_PUBCOOKIE_LIB=../pubcookie/libauthgssproxy.a
+ WEB_PUBCOOKIE_LINK=gssapi_proxy.l
+ fi
+ fi
+fi
+
+AC_SUBST([REGEX_BUILD])
+
+AC_SUBST([WEB_BUILD])
+AC_SUBST([WEB_BINDIR])
+AC_SUBST([WEB_PUBCOOKIE_BUILD])
+AC_SUBST([WEB_PUBCOOKIE_LIB])
+AC_SUBST([WEB_PUBCOOKIE_LINK])
+
+AC_SUBST([AM_CFLAGS])
+AC_SUBST([AM_LDFLAGS])
+
+AC_OUTPUT(m4/Makefile po/Makefile.in regex/Makefile \
+ pith/osdep/Makefile pith/charconv/Makefile pith/Makefile \
+ pico/osdep/Makefile pico/Makefile \
+ alpine/osdep/Makefile alpine/Makefile \
+ web/src/Makefile web/src/pubcookie/Makefile \
+ web/src/alpined.d/Makefile \
+ Makefile)
diff --git a/contrib/bitmaps/pine.icon b/contrib/bitmaps/pine.icon
new file mode 100644
index 00000000..e5cd051b
--- /dev/null
+++ b/contrib/bitmaps/pine.icon
@@ -0,0 +1,43 @@
+#define icon_width 60
+#define icon_height 60
+static char icon_bits[] = {
+ 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xe3, 0x50,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x50, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1c, 0xe8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0xd8,
+ 0x02, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0xe8, 0x05, 0x00, 0x00, 0x00,
+ 0x80, 0x71, 0x00, 0x5c, 0x05, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0xf4,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x6a, 0x05, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x00, 0xf4, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f,
+ 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xfa, 0x0c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x76, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77,
+ 0x07, 0x00, 0x18, 0x07, 0x00, 0x00, 0x80, 0xec, 0x0a, 0x00, 0xe0, 0x01,
+ 0x00, 0x00, 0xe0, 0xff, 0x13, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x30, 0xfb,
+ 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x7c, 0x35, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xef, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xfd,
+ 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xfe, 0x45, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xe0, 0xff, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xf9,
+ 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0xfe, 0xfd, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0xf7, 0xce, 0x01, 0x00, 0x00, 0x00, 0x00, 0x40, 0xee,
+ 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0xf9, 0xc9, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x7c, 0xfe, 0x1f, 0x06, 0x00, 0x00, 0x00, 0x00, 0x8e, 0xff,
+ 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0xf7, 0xbf, 0x07, 0x00, 0x00,
+ 0x00, 0x00, 0xc0, 0xed, 0xf7, 0x7f, 0x00, 0x00, 0x00, 0x00, 0xb0, 0xf3,
+ 0xcf, 0xf0, 0x01, 0x00, 0x00, 0x00, 0xfc, 0xff, 0x7c, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0xef, 0xf8, 0xe7, 0x07, 0x00, 0x00, 0x00, 0xc0, 0x33, 0xff,
+ 0xf9, 0x1d, 0x00, 0x00, 0x00, 0x60, 0xde, 0xfd, 0x8e, 0xf6, 0x03, 0x00,
+ 0x00, 0x00, 0xfa, 0xf3, 0xff, 0x39, 0x06, 0x00, 0x00, 0x00, 0xfe, 0xff,
+ 0xf6, 0x40, 0x00, 0x00, 0x00, 0x80, 0xf7, 0xfc, 0xdb, 0x0f, 0x00, 0x00,
+ 0x00, 0x70, 0x8f, 0xfb, 0xfb, 0x3f, 0x00, 0x00, 0x00, 0xfc, 0x60, 0xff,
+ 0xff, 0xe0, 0x00, 0x00, 0x00, 0x8f, 0x7d, 0xff, 0xff, 0x3f, 0x00, 0x00,
+ 0x00, 0x00, 0xef, 0xfc, 0xec, 0xf7, 0x01, 0x00, 0x00, 0xe0, 0x9e, 0xff,
+ 0xfd, 0xdd, 0x03, 0x00, 0x00, 0xf8, 0xf1, 0xff, 0x0f, 0xff, 0x3f, 0x00,
+ 0x00, 0x5c, 0xcc, 0xfe, 0x7f, 0x0c, 0x00, 0x00, 0x80, 0xb3, 0xfb, 0xfb,
+ 0xbf, 0x73, 0x00, 0x00, 0xc0, 0x60, 0xff, 0xf5, 0xfb, 0x9c, 0x01, 0x00,
+ 0x00, 0xfc, 0xfc, 0xfb, 0xed, 0xe1, 0x06, 0x00, 0x80, 0x0f, 0x8e, 0xff,
+ 0xc5, 0x03, 0x1f, 0x00, 0xe0, 0x81, 0x83, 0xf2, 0x69, 0x07, 0x70, 0x00,
+ 0x18, 0xc0, 0x00, 0xf0, 0x09, 0x1c, 0x80, 0x01, 0x00, 0x00, 0x00, 0xf0,
+ 0x01, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x01, 0x40, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xf0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x01, 0x00, 0x00, 0x00};
diff --git a/contrib/bitmaps/pine2.icon b/contrib/bitmaps/pine2.icon
new file mode 100755
index 00000000..ae5fa74f
--- /dev/null
+++ b/contrib/bitmaps/pine2.icon
@@ -0,0 +1,33 @@
+#define crow_width 50
+#define crow_height 50
+static char crow_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x38,
+ 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x80, 0x08, 0x00,
+ 0x00, 0xfe, 0x00, 0x00, 0x40, 0x10, 0x00, 0x00, 0xff, 0x01, 0x00, 0x80,
+ 0x08, 0x80, 0x00, 0xfe, 0x00, 0x00, 0x40, 0x10, 0xc0, 0x01, 0xff, 0x01,
+ 0x00, 0x20, 0x20, 0xe0, 0x83, 0xff, 0x03, 0x00, 0x10, 0x40, 0xf0, 0xc7,
+ 0xff, 0x07, 0x00, 0x22, 0x20, 0xf8, 0xef, 0xff, 0x0f, 0x00, 0x17, 0x40,
+ 0xe0, 0xc3, 0xfd, 0x87, 0x00, 0x0f, 0x80, 0xf0, 0xe7, 0xf8, 0xcf, 0x01,
+ 0x07, 0x00, 0xf9, 0x7f, 0xf0, 0xff, 0x03, 0x0f, 0x80, 0xfc, 0x3f, 0xf0,
+ 0xff, 0x03, 0x07, 0x00, 0xff, 0x7f, 0xf8, 0xff, 0x03, 0x03, 0x00, 0xff,
+ 0x7f, 0xf0, 0xff, 0x03, 0x01, 0x00, 0xfe, 0x1f, 0xe0, 0xfd, 0x03, 0x02,
+ 0x00, 0xfe, 0x3f, 0xc0, 0xf8, 0x03, 0x01, 0x00, 0xff, 0x7f, 0x40, 0xf0,
+ 0x03, 0x00, 0x80, 0xff, 0xff, 0x80, 0xf8, 0x03, 0x00, 0xc0, 0xff, 0xff,
+ 0x41, 0xf0, 0x03, 0x00, 0xe0, 0xff, 0xff, 0x23, 0xe0, 0x03, 0x00, 0x80,
+ 0xff, 0xff, 0x40, 0xf0, 0x03, 0x00, 0xc0, 0xff, 0xff, 0x21, 0xe0, 0x03,
+ 0x00, 0xe0, 0xff, 0xff, 0x13, 0xc0, 0x03, 0x00, 0xf0, 0xff, 0xff, 0x27,
+ 0xe0, 0x03, 0x00, 0xf8, 0xff, 0xff, 0x1f, 0xc0, 0x03, 0x10, 0xfc, 0xff,
+ 0xff, 0x1f, 0x80, 0x03, 0x28, 0xf0, 0xff, 0xff, 0x0f, 0xc0, 0x03, 0x44,
+ 0xf8, 0xff, 0xff, 0x0f, 0x80, 0x03, 0x82, 0xfc, 0xff, 0xff, 0x1f, 0x00,
+ 0x03, 0x44, 0xfe, 0xff, 0xff, 0x3f, 0x80, 0x03, 0x82, 0xff, 0xff, 0xff,
+ 0x7f, 0x00, 0x03, 0x01, 0xff, 0xff, 0xff, 0xff, 0x00, 0x02, 0x00, 0xfe,
+ 0xff, 0xff, 0x3f, 0x00, 0x01, 0x00, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x02,
+ 0x00, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xff, 0xff,
+ 0x01, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0x03, 0x00, 0x00, 0xfc, 0xff,
+ 0xff, 0xff, 0x07, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00,
+ 0xf0, 0xff, 0xff, 0xff, 0x03, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff,
+ 0x03, 0x00, 0xf0, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0xe0, 0xff, 0xff,
+ 0xff, 0x1f, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00};
diff --git a/contrib/bitmaps/unixware/README b/contrib/bitmaps/unixware/README
new file mode 100644
index 00000000..685a45bb
--- /dev/null
+++ b/contrib/bitmaps/unixware/README
@@ -0,0 +1,20 @@
+
+
+Tim Rice tim@trr.metro.net Fri Apr 26 17:16:54 PDT 1996
+
+Here are the files needed to have the pine icon show up on the desktop.
+
+pine.cdb
+pine.icon
+pine48.icon
+
+--------< cut here for install script >-------
+:
+# quick & nasty install script
+cp pine.cdb /usr/X/lib/classdb
+cp pine.icon /usr/X/lib/pixmaps
+cp pine48.icon /usr/X/lib/pixmaps
+# tune permissions so "system owners" can modify
+chgrp dtadmin /usr/X/lib/classdb/pine.cdb /usr/X/lib/pixmaps/pine.icon /usr/X/lib/pixmaps/pine48.icon
+chmod g+w /usr/X/lib/classdb/pine.cdb /usr/X/lib/pixmaps/pine.icon /usr/X/lib/pixmaps/pine48.icon
+echo "INCLUDE pine.cdb;" >> /usr/X/lib/classdb/system
diff --git a/contrib/bitmaps/unixware/pine.cdb b/contrib/bitmaps/unixware/pine.cdb
new file mode 100644
index 00000000..a9e02d07
--- /dev/null
+++ b/contrib/bitmaps/unixware/pine.cdb
@@ -0,0 +1,11 @@
+CLASS 'Pine'
+BEGIN
+ _CLASSNAME "Pine";
+ _FILETYPE "EXEC";
+ _PROG_TYPE "UNIX Character";
+ _ICONFILE "pine.icon";
+ _MINIMIZED_ICONFILE "pine48.icon";
+ MENU _Open 'exec xterm -e "%F" &';
+ _LPATTERN "pine";
+END
+
diff --git a/contrib/bitmaps/unixware/pine.icon b/contrib/bitmaps/unixware/pine.icon
new file mode 100644
index 00000000..84b0f70c
--- /dev/null
+++ b/contrib/bitmaps/unixware/pine.icon
@@ -0,0 +1,45 @@
+#define pine_format 1
+#define pine_width 32
+#define pine_height 32
+#define pine_ncolors 4
+#define pine_chars_per_pixel 1
+static char * pine_colors[] = {
+" " , "#FFFFFFFFFFFF",
+"." , "#0000CF3C0000",
+"X" , "#30C234D33CF3",
+"o" , "#5D7555555144"
+} ;
+static char * pine_pixels[] = {
+" . ",
+" . ",
+" ... ",
+" ... ",
+"XXXX XXX . . . ",
+" XX XX . . . ",
+" XXX . ... . ",
+" X .. .. . ",
+" . .... . ",
+" ... . . . ",
+" . .oo. . ",
+" . . oo . . XXX XX",
+" . .oo. . . XXXX ",
+" .... oo ... XX ",
+" . . ..oo. .. ",
+" .. .oo . . ",
+" ... .oo ... ",
+" . .. oo. . . ",
+" ........oo... . ",
+" .. .. ..oo..... ",
+" . ...oo . . .. ",
+" .... oo. . .. . ",
+" .. ...oo... ... ",
+" .. ....oo.. . . ",
+" ........oo. ... . ",
+" ... . ..oo..... . ",
+" .... .oo. ... ... ",
+" .. .. ..oo.. . ... ",
+" ..... ....oo...... .. ",
+" ... ......oo.. ...... ",
+" oo ",
+" oo "
+} ;
diff --git a/contrib/bitmaps/unixware/pine48.icon b/contrib/bitmaps/unixware/pine48.icon
new file mode 100644
index 00000000..ac9d18d3
--- /dev/null
+++ b/contrib/bitmaps/unixware/pine48.icon
@@ -0,0 +1,59 @@
+#define pine48_format 1
+#define pine48_width 48
+#define pine48_height 48
+#define pine48_ncolors 2
+#define pine48_chars_per_pixel 1
+static char * pine48_colors[] = {
+" " , "#FFFFFFFFFFFF",
+"." , "#000000000000"
+} ;
+static char * pine48_pixels[] = {
+" . ",
+" . ... ",
+" . . ..... ",
+" . . ....... ",
+" . . ......... ",
+" . . . ....... ",
+" . . ... ......... ",
+" . . ..... ........... ",
+" . . ....... ............. ",
+" . . . ......... ............... ",
+"... . . ..... ... ......... .",
+".... . ....... ... ......... ..",
+"... . ............ ............",
+".... . ............ ............",
+"... ............... .............",
+".. ............... ............",
+". ............ .... ......",
+" . ............. .. .....",
+". ............... . ....",
+" ................. . .....",
+" ................... . ....",
+" ..................... . ...",
+" ................. . ....",
+" ................... . ...",
+" ..................... . ..",
+" ....................... . ...",
+" .......................... ..",
+" . ........................... .",
+" . . ........................ ..",
+" . . ......................... .",
+" . . ........................... ",
+" . . ............................. .",
+" . ................................ ",
+". ................................ ",
+" ............................. ",
+" ............................... ",
+" ............................... ",
+" ............................... ",
+" ............................... ",
+" ................................. ",
+" .............................. ",
+" .............................. ",
+" ...................................",
+" ................................ ",
+" ................................ ",
+" ................................ ",
+" ",
+" "
+} ;
diff --git a/contrib/carmel/README b/contrib/carmel/README
new file mode 100644
index 00000000..f893ad78
--- /dev/null
+++ b/contrib/carmel/README
@@ -0,0 +1,34 @@
+Further details on the Carmel driver and the modifications for
+the X-BWC-Glyph character set are under the doc directory.
+
+Building the Carmel driver
+--------------------------
+1. Build pine, the c-client and imapd as you would normally so
+ all machine dependent source directory links get set up correctly
+
+2. Copy all files from contrib/carmel/c-client into the Pine c-client
+ directory
+
+3. run "patch < Makefile.patch" to modify the Makefile.
+
+4. If you want the X-BWC-GLYPH character set stuff turned on in the
+ c-client for mail generated before 1994 make sure that
+ EXTRACFLAGS=-DBWC in the Makefile
+
+5. If you want the carmel driver to be the default for non-fully
+ qualified names, change that in the Makefile for your system.
+
+6. If you want the X-BWC-Glyph display code turned on some patches
+ have to be applied to Pine. If not, these steps can be skipped.
+ 6a. Copy all the files from carmel/pine into the pine source
+ directory.
+ 6b. Run patch on each of them: "patch < makefile.ult",
+ "patch < filter.c.pathc".
+ 6c. You may have to add a -DBWC to the CFLAGS= line in the makefile
+ for your system if there is no patch file for your system.
+
+7. Remove the imapd executable imapd/imapd from the Pine source tree
+ to force it to be rebuilt (Make won't figure it out).
+
+8. Run build again from the top level directory
+
diff --git a/contrib/carmel/c-client/Makefile.patch b/contrib/carmel/c-client/Makefile.patch
new file mode 100644
index 00000000..9134d31a
--- /dev/null
+++ b/contrib/carmel/c-client/Makefile.patch
@@ -0,0 +1,49 @@
+*** Makefile Tue Aug 23 16:42:37 1994
+--- Makefile.new Wed Aug 31 18:40:39 1994
+***************
+*** 31,43 ****
+ # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+
+ ARCHIVE=c-client.a
+ ARRC=ar rc
+ BINARIES=mail.o bezerk.o mtx.o tenex2.o mbox.o mh.o mmdf.o imap2.o pop3.o \
+ news.o nntpcunx.o phile.o dummy.o smtp.o nntp.o rfc822.o misc.o \
+! osdep.o sm_unix.o
+ CFLAGS=$(EXTRACFLAGS)
+! DEFAULTDRIVERS=imap nntp pop3 mh mtx tenex mmdf bezerk news phile dummy
+ LDFLAGS=$(EXTRALDFLAGS)
+ LN=ln -s
+ MAKE=make
+--- 31,44 ----
+ # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+
++ EXTRACFLAGS=-DBWC
+ ARCHIVE=c-client.a
+ ARRC=ar rc
+ BINARIES=mail.o bezerk.o mtx.o tenex2.o mbox.o mh.o mmdf.o imap2.o pop3.o \
+ news.o nntpcunx.o phile.o dummy.o smtp.o nntp.o rfc822.o misc.o \
+! osdep.o sm_unix.o carmel.o carmel2.o
+ CFLAGS=$(EXTRACFLAGS)
+! DEFAULTDRIVERS=imap carmel carmel2 nntp pop3 mh mtx tenex mmdf bezerk news phile dummy
+ LDFLAGS=$(EXTRALDFLAGS)
+ LN=ln -s
+ MAKE=make
+***************
+*** 207,213 ****
+
+ ult: # Ultrix
+ $(MAKE) mtest OS=$@ EXTRADRIVERS="$(EXTRADRIVERS)" \
+! STDPROTO=bezerkproto \
+ CFLAGS="-g3 -O2 -Olimit 800 -Dconst= $(EXTRACFLAGS)" \
+ LDFLAGS="-lauth $(EXTRALDFLAGS)"
+
+--- 208,214 ----
+
+ ult: # Ultrix
+ $(MAKE) mtest OS=$@ EXTRADRIVERS="$(EXTRADRIVERS)" \
+! STDPROTO=carmelproto \
+ CFLAGS="-g3 -O2 -Olimit 800 -Dconst= $(EXTRACFLAGS)" \
+ LDFLAGS="-lauth $(EXTRALDFLAGS)"
+
diff --git a/contrib/carmel/c-client/bzk2cml.c b/contrib/carmel/c-client/bzk2cml.c
new file mode 100644
index 00000000..fcff6438
--- /dev/null
+++ b/contrib/carmel/c-client/bzk2cml.c
@@ -0,0 +1,103 @@
+#include <stdio.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <sys/dir.h>
+#include "mail.h"
+#include "osdep.h"
+#include "carmel2.h"
+#include "carmel.h"
+
+char *last_path(), *cpystr();
+void carmel2_bezerk_mail();
+extern DRIVER carmeldriver;
+
+extern char carmel_20k_buf[20000];
+
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ char carmel_folder[500], *bezerk_folder;
+ MAILSTREAM *stream;
+
+ if(argc <= 1) {
+ fprintf(stderr, "Usage: bzk2cml <folder>\n");
+ exit(-1);
+ }
+
+ for(argv++; *argv != NULL; argv++) {
+ bezerk_folder = last_path(*argv); /* Only last component of path */
+ sprintf(carmel_folder, "#carmel#%s", bezerk_folder);
+
+ if(carmel_create(NULL, carmel_folder) == 0) {
+ continue;
+ }
+
+
+ stream = (MAILSTREAM *)fs_get(sizeof(MAILSTREAM));
+ stream->dtb = &carmeldriver;
+ stream->mailbox = cpystr(carmel_folder);
+ stream->local = NULL;
+
+ if(carmel_open(stream) == NULL) {
+ continue;
+ }
+
+ if(carmel2_lock(stream, stream->mailbox, WRITE_LOCK) < 0) {
+ fprintf(stderr, "Carmel folder %s locked\n", carmel_folder);
+ carmel_close(stream);
+ continue;
+ }
+
+ carmel2_spool_mail(stream, *argv, stream->mailbox, 0);
+ carmel2_unlock(stream, stream->mailbox, WRITE_LOCK);
+ fprintf(stderr, "Folder \"%s\" copied to \"%s\"\n", *argv,
+ carmel_folder);
+ carmel_close(stream);
+ }
+}
+
+
+
+
+
+
+
+
+char *last_path(path)
+ char *path;
+{
+ char *p;
+
+ p = path + strlen(path) - 1;
+
+ while(p > path && *p != '/')
+ p--;
+
+ if(*p == '/')
+ p++;
+ return(p);
+}
+
+void
+mm_log(mess, code)
+ char *mess;
+ int code;
+{
+ fprintf(stderr, "%s\n", mess);
+}
+
+void
+mm_fatal()
+{
+ abort();
+}
+
+void mm_mailbox() {}
+void mm_critical() {}
+void mm_nocritical() {}
+void mm_searched() {}
+void mm_exists() {}
+void mm_expunged() {}
+
diff --git a/contrib/carmel/c-client/carmel-purge.sh b/contrib/carmel/c-client/carmel-purge.sh
new file mode 100644
index 00000000..87d35ceb
--- /dev/null
+++ b/contrib/carmel/c-client/carmel-purge.sh
@@ -0,0 +1,89 @@
+#!/bin/csh -f
+# vmail.purge - zaps all unindexed mail messages.
+#
+# The_Epoch vick created this.
+# 1988 Nov 10 Thu 12:00 bob added code to allow a pathname for $1.
+# 1988 Nov 14 Mon 10:14 bob rewrote the code that was breaking inside `quotes`.
+# 1988 Nov 15 Tue 16:44 bob added code to track spurious .vmail files
+# 1988 Nov 16 Wed 10:44 bob redirected error messages to stderr.
+# 1989 Feb 14 Tue 13:37 bob add exceptions for corrupt indexes
+# 1991 Sun Nov 24 10:30 Shoa add removal of RECOVER index.
+# 1992 Oct 21 Wed 17:01 bob check for Pine read lock files, and abort.
+
+#
+# Usage: vmail.purge [ username | somepath/username/.vmail ]
+
+
+set pname = $0
+set pname = ${pname:t} # name of this script for error messages
+
+set indexed = /tmp/VMprg_idx.$$ # a list of indexed messages
+set messages = /tmp/VMprg_msg.$$ # a list of all messages
+set unindexed = /tmp/VMprg_Csh.$$ # script to remove unindexed messages
+
+if ( 1 <= $#argv ) then
+ if ( ".vmail" == $1:t ) then # assume /u/dp/bob/.vmail
+ set vpath = $1:h # /u/dp/bob
+ setenv USER $vpath:t # bob
+ else
+ setenv USER $1
+ endif
+endif
+
+if ( ! -d ~$USER/.vmail ) then
+ echo -n "${pname}: "
+ if ( 1 <= $#argv ) then
+ sh -c "echo 1>&2 ${pname}: invalid argument $1"
+ else
+ sh -c "echo 1>&2 ${pname}: $USER has no .vmail directory"
+ endif
+ exit 1
+endif
+
+cd ~$USER/.vmail
+
+if (!( -d index && -d msg)) then
+ sh -c "echo 1>&2 ${pname}: $cwd is missing required components"
+ exit 1
+endif
+#
+# check for Pine inbox read lock file
+if ( -e index/.MAIL.rl ) then
+ sh -c "echo 1>&2 ${pname}: $cwd is now running Pine"
+ exit 1
+endif
+#
+#
+#remove the temporary RECOVER index *must* be done before deletion of messages
+# start.
+if ( -e index/RECOVER ) then
+ rm index/RECOVER
+ if ( -e ~$USER/.inda ) rm ~$USER/.inda
+ if ( -e ~$USER/.indf ) rm ~$USER/.indf
+endif
+#
+#
+# create the shell script
+#
+awk '$1 ~ /^[0-9]/ {print substr($1,1,6)}' index/* | sort | uniq > $indexed
+if ( $status ) then
+ sh -c "echo 1>&2 ${pname}: some index probably corrupt"
+ exit 1
+endif
+
+ls msg | awk '{ print substr($1,1,6) }' > $messages
+comm -23 $messages $indexed | awk '{ print "rm -f " $1 " " $1 ".wid" }' > $unindexed
+
+# provide verbose statistics
+#
+echo $USER " total:" `wc -l < $messages` " indexed:" `wc -l < $indexed`\
+ " purging: " `wc -l < $unindexed`
+
+# do the work
+#
+cd msg
+csh -f $unindexed
+
+# cleanup
+#
+rm $messages $indexed $unindexed
diff --git a/contrib/carmel/c-client/carmel.c b/contrib/carmel/c-client/carmel.c
new file mode 100644
index 00000000..5265097d
--- /dev/null
+++ b/contrib/carmel/c-client/carmel.c
@@ -0,0 +1,1232 @@
+/*----------------------------------------------------------------------
+
+ T H E C A R M E L M A I L D R I V E R
+
+ Author(s): Laurence Lundblade
+ Baha'i World Centre
+ Data Processing
+ Haifa, Israel
+ Internet: lgl@cac.washington.edu or laurence@bwc.org
+ September 1992
+
+ Last Edited: Aug 31, 1994
+
+The "Carmel" mail format is cutsom mail file format that has messages
+saved in individual files and an index file that references them. It
+has also been called the "pod" or "vmail" format. This is probably not
+of interest to anyone away from it's origin, though the Carmel2 format
+might be.
+
+This driver reads and writes the format in conjunction with the carmel2
+driver. The carmel2 file driver does most of the work as most of the
+operations are done on the created auxiliary carmel2 index and the
+carmel index is updated when the mail file is closed.
+
+ ----------------------------------------------------------------------*/
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <netdb.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <sys/dir.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "carmel2.h"
+#include "carmel.h"
+#include "rfc822.h"
+#include "misc.h"
+
+
+/* Driver dispatch used by MAIL. The carmel 2driver shares most of
+ its data structures and functions with the carmel driver
+ */
+
+DRIVER carmeldriver = {
+ "carmel",
+ (DRIVER *) NIL, /* next driver */
+ carmel_valid, /* mailbox is valid for us */
+ carmel_parameters, /* Set parameters */
+ carmel_find, /* find mailboxes */
+ carmel_find_bboards, /* find bboards */
+ carmel_find_all, /* find all mailboxes */
+ carmel_find_bboards, /* find all the bboards ; just a noop here */
+ carmel_subscribe, /* subscribe to mailbox */
+ carmel_unsubscribe, /* unsubscribe from mailbox */
+ carmel_subscribe_bboard, /* subscribe to bboard */
+ carmel_unsubscribe_bboard, /* unsubscribe from bboard */
+ carmel_create, /* create mailbox */
+ carmel_delete, /* delete mailbox */
+ carmel_rename, /* rename mailbox */
+ carmel_open, /* open mailbox */
+ carmel_close, /* close mailbox */
+ carmel2_fetchfast, /* fetch message "fast" attributes */
+ carmel2_fetchflags, /* fetch message flags */
+ carmel2_fetchstructure, /* fetch message envelopes */
+ carmel2_fetchheader, /* fetch message header only */
+ carmel2_fetchtext, /* fetch message body only */
+ carmel2_fetchbody, /* fetch message body section */
+ carmel2_setflag, /* set message flag */
+ carmel2_clearflag, /* clear message flag */
+ carmel2_search, /* search for message based on criteria */
+ carmel2_ping, /* ping mailbox to see if still alive */
+ carmel_check, /* check for new messages */
+ carmel_expunge, /* expunge deleted messages */
+ carmel2_copy, /* copy messages to another mailbox */
+ carmel2_move, /* move messages to another mailbox */
+ carmel_append, /* Append message to a mailbox */
+ carmel2_gc, /* garbage collect stream */
+};
+
+MAILSTREAM carmelproto ={&carmeldriver}; /* HACK for default_driver in pine.c*/
+
+#ifdef ANSI
+static int carmel_sift_files(struct direct *);
+static void carmel_recreate_index(MAILSTREAM *);
+static char *carmel_calc_paths(int, char *, int);
+static int vmail2carmel(char *, char *, MAILSTREAM *, char *, char *);
+static int write_carmel_index(FILE *, MESSAGECACHE *, ENVELOPE *);
+static int carmel_reset_index_list();
+static void carmel_kill_locks(char *);
+#else
+static int carmel_sift_files();
+static void carmel_recreate_index();
+static char *carmel_calc_paths();
+static int vmail2carmel();
+static int write_carmel_index();
+static int carmel_reset_index_list();
+static void carmel_kill_locks();
+#endif
+
+
+
+extern char carmel_20k_buf[20000], carmel_path_buf[], carmel_error_buf[];
+
+static int disk_setup_checked = 0;
+
+/*----------------------------------------------------------------------
+ Carmel mail validate mailbox
+
+Args: name -- name to check
+
+Returns: our driver if name is valid, otherwise calls valid in next driver
+ ---*/
+
+DRIVER *carmel_valid (name)
+ char *name;
+{
+ return(carmel_isvalid(name) ? &carmeldriver : NIL);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Check and see if a named mailbox is a valid carmel mail index
+
+Args: name -- name or path of mail file. It is a FQN, e.g. #carmel#sent-mail
+
+Returns: 0 if it is not a valid mailbox
+ 1 if it is.
+
+ A carmel index must be a regular file, readable, and have the second word in
+the file be "index", upper or lower case.
+ ----*/
+int
+carmel_isvalid (name)
+ char *name;
+{
+ struct stat sbuf;
+ int fd;
+ char *p, *carmel_index_file;
+ struct carmel_mb_name *parsed_name;
+
+ if(!disk_setup_checked){
+ disk_setup_checked = 1;
+ carmel_init(NULL);
+ }
+
+ /* Don't accept plain "inbox". If we do then the carmel driver
+ takes over all other drivers linked after it. This way the
+ inbox-path in Pine can be set to #carmel#MAIL to make the
+ inbox the carmel one
+ */
+
+ parsed_name = carmel_parse_mb_name(name, '\0');
+ if(parsed_name == NULL)
+ return(0);
+
+ carmel_free_mb_name(parsed_name);
+
+ carmel_index_file = carmel_calc_paths(CalcPathCarmelIndex, name, 0);
+
+ if(carmel_index_file == NULL)
+ return(0); /* Will get two error messages here, one from dummy driver */
+
+ if(stat(carmel_index_file, &sbuf) < 0)
+ return(1); /* If the name matches and it doesn't exist, it's OK */
+
+ if(!(sbuf.st_mode & S_IFREG))
+ return(0);
+
+ fd = open(carmel_index_file, O_RDONLY);
+ if(fd < 0)
+ return(0);
+
+ if(read(fd, carmel_20k_buf, 200) <= 0)
+ return(0);
+ carmel_20k_buf[199] = '\0';
+
+ close(fd);
+
+ for(p = carmel_20k_buf; *p && !isspace(*p); p++); /* frist word */
+ for(; *p && isspace(*p); p++); /* space */
+ lcase(p); /* lower case the "index" */
+ if(!*p || strncmp(p, "index", 5))
+ return(0);
+
+ return(1);
+}
+
+
+
+
+/*----------------------------------------------------------------------
+
+ ----*/
+void *
+carmel_parameters(function, value)
+ long function;
+ char *value;
+{}
+
+
+
+/*----------------------------------------------------------------------
+ This is used by scandir to determine which files in the directory
+ are treated as mail files
+ ----*/
+static char *sift_pattern = NULL;
+static int
+carmel_sift_files(dir)
+ struct direct *dir;
+{
+ if(dir->d_name[0] == '.')
+ return(0);
+ else if(strcmp(dir->d_name, "MAIL") == 0)
+ /* Never return the inbox. "INBOX" is always included by default */
+ return(0);
+ else if(pmatch(dir->d_name, sift_pattern))
+ return(1);
+ else
+ return(0);
+}
+
+int alphasort();
+
+/* ----------------------------------------------------------------------
+ Carmel find list of mail boxes
+ Args: mail stream
+ pattern to search
+
+ This scans the ".vmail/index" directory for a list of folders
+ (indexes in vmail terms).
+
+BUG -- doesn't really match patterns (yet)
+ ----*/
+void
+carmel_find(stream, pat)
+ MAILSTREAM *stream;
+ char *pat;
+{
+ char tmp[MAILTMPLEN];
+ struct direct **namelist, **n;
+ int num;
+ struct carmel_mb_name *parsed_name;
+ struct passwd *pw;
+
+ parsed_name = carmel_parse_mb_name(pat, '\0');
+
+ if(parsed_name == NULL)
+ return;
+
+ if(parsed_name->user == NULL) {
+ sprintf(tmp, "%s/%s", myhomedir(), CARMEL_INDEX_DIR);
+ } else {
+ pw = getpwnam(parsed_name->user);
+ if(pw == NULL) {
+ sprintf(carmel_error_buf,
+ "Error accessing mailbox \"%s\". No such user name \"%s\"\n",
+ pat, parsed_name->user);
+ mm_log(carmel_error_buf, ERROR);
+ return;
+ }
+ sprintf(tmp, "%s/%s", pw->pw_dir, CARMEL_INDEX_DIR);
+ }
+
+ sift_pattern = parsed_name->mailbox;
+
+ num = scandir(tmp, &namelist, carmel_sift_files, alphasort);
+
+ if(num <= 0) {
+/* sprintf(carmel_error_buf, "Error finding mailboxes \"%s\": %s",
+ pat, strerror(errno));
+ mm_log(carmel_error_buf, ERROR);*/
+ return;
+ }
+
+ for(n = namelist; num > 0; num--, n++) {
+ if(parsed_name->user == NULL) {
+ sprintf(tmp, "%s%s%c%s", CARMEL_NAME_PREFIX, parsed_name->version,
+ CARMEL_NAME_CHAR, (*n)->d_name);
+ } else {
+ sprintf(tmp, "%s%s%c%s%c%s", CARMEL_NAME_PREFIX,
+ parsed_name->version, CARMEL_NAME_CHAR,
+ parsed_name->user, CARMEL_NAME_CHAR,
+ (*n)->d_name);
+ }
+ mm_mailbox(tmp);
+ free(*n);
+ }
+ free(namelist);
+ carmel_free_mb_name(parsed_name);
+}
+
+
+/*----------------------------------------------------------------------
+ Find_all is the same for find in the carmel driver; there is no
+difference between subscribed and unsubscribed mailboxes
+ ----*/
+void
+carmel_find_all (stream, pat)
+ MAILSTREAM *stream;
+ char *pat;
+{
+ carmel_find(stream, pat);
+}
+
+
+/*----------------------------------------------------------------------
+ Find bulliten boards is a no-op for carmel, there are none (yet)
+ */
+void
+carmel_find_bboards (stream,pat)
+ MAILSTREAM *stream;
+ char *pat;
+{
+ /* Always a no-op */
+}
+
+
+/*----------------------------------------------------------------------
+ No subscription management for the carmel format. Probably should work
+ with the .mailbox list format, but that's probably implemented in mail.c
+ ----*/
+long carmel_subscribe() {}
+long carmel_unsubscribe() {}
+long carmel_subscribe_bboard() {}
+long carmel_unsubscribe_bboard() {}
+
+
+
+/*----------------------------------------------------------------------
+ Create a carmel folder
+
+Args: stream -- Unused here
+ mailbox -- the FQN of mailbox to create
+
+Returns: T on success, NIL on failure.
+
+An error message is logged on failure.
+ ----*/
+long
+carmel_create(stream, mailbox)
+ MAILSTREAM *stream;
+ char *mailbox;
+{
+ char *carmel_index_file, *carmel2_index_file;
+ struct stat sb;
+ FILE *f;
+
+ carmel_index_file = carmel_calc_paths(CalcPathCarmelIndex, mailbox, 0);
+
+ if(carmel_index_file == NULL)
+ return(NIL);
+
+ /*--- Make sure it doesn't already exist ---*/
+ if(stat(carmel_index_file, &sb) >= 0)
+ return(NIL);
+
+ /*--- The carmel index ----*/
+ f = fopen(carmel_index_file, "w");
+ if(f == NULL)
+ goto bomb;
+
+ if(fprintf(f, "%-13.13s index.....\n",carmel_pretty_mailbox(mailbox))==EOF)
+ goto bomb;
+
+ if(fclose(f) == EOF)
+ goto bomb;
+
+ /*--- The carmel2 index ----*/
+ carmel2_index_file = carmel_calc_paths(CalcPathCarmel2Index, mailbox,0);
+ f = fopen(carmel2_index_file, "w");
+ if(f == NULL)
+ goto bomb;
+
+ if(fprintf(f, "\254\312--CARMEL-MAIL-FILE-INDEX--\n") == EOF)
+ goto bomb;
+
+ if(fclose(f) == EOF)
+ goto bomb;
+
+
+ carmel_kill_locks(mailbox); /* Get rid of any left over locks */
+ carmel_reset_index_list();
+ return(T);
+
+ bomb:
+ unlink(carmel_calc_paths(CalcPathCarmelIndex, mailbox, 0));
+ sprintf(carmel_error_buf, "Error creating mailbox %s: %s",
+ carmel_pretty_mailbox(mailbox), strerror(errno));
+ mm_log(carmel_error_buf, ERROR);
+ return(NIL);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Delete a carmel mailbox, and it's carmel2 index
+
+Args: stream -- unsed here
+ mailbox -- FQN of mailbox to delete
+
+Returns: NIL -- if delete fails
+ T -- if successful
+ ----*/
+long
+carmel_delete(stream, mailbox)
+ MAILSTREAM *stream;
+ char *mailbox;
+{
+ char *carmel_index_file, *carmel2_index_file;
+
+ carmel_index_file = carmel_calc_paths(CalcPathCarmelIndex, mailbox, 0);
+ if(carmel_index_file == NULL)
+ return(NIL);
+
+ if(unlink(carmel_index_file) < 0) {
+ sprintf(carmel_error_buf, "Error deleting mailbox %s: %s",
+ carmel_pretty_mailbox(mailbox), strerror(errno));
+ mm_log(carmel_error_buf, ERROR);
+ return(NIL);
+ }
+
+ /*------ Second the carmel2 index, quietly -----*/
+ carmel2_index_file = carmel_calc_paths(CalcPathCarmel2Index, mailbox, 0);
+
+ unlink(carmel2_index_file);
+
+ carmel_kill_locks(mailbox); /* Make sure all locks are clear */
+ carmel_reset_index_list();
+ return(T);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Rename a carmel index, and its carmel2 index
+
+Args: stream -- unused
+ orig -- FQN of original mailbox
+ new -- FQN of new name
+
+Returns: NIL if rename failed, T if it succeeded.
+
+BUG: theoretically this could rename a folder from one user name
+to another. Either that should be disallowed here, or code to make
+it work should be written.
+ ----*/
+long
+carmel_rename(stream, orig, new)
+ MAILSTREAM *stream;
+ char *orig;
+ char *new;
+{
+ char path_buf[CARMEL_PATHBUF_SIZE], *new_path, *orig_index_file;
+
+ /*---- First the Carmel index -----*/
+ orig_index_file = carmel_calc_paths(CalcPathCarmelIndex, orig, 0);
+ if(orig_index_file == NULL)
+ return(NIL);
+ strcpy(path_buf, orig_index_file);
+ new_path = carmel_calc_paths(CalcPathCarmelIndex, new, 0);
+ if(new_path == NULL)
+ return(NIL);
+
+ if(rename(path_buf, new_path) < 0) {
+ sprintf(carmel_error_buf, "Error renaming mailbox %s: %s",
+ carmel_pretty_mailbox(orig), strerror(errno));
+ mm_log(carmel_error_buf, ERROR);
+ return(NIL);
+ }
+
+ /*----- Next the Carmel index, quietly ------*/
+ strcpy(path_buf, carmel_calc_paths(CalcPathCarmel2Index, orig, 0));
+ new_path = carmel_calc_paths(CalcPathCarmel2Index, new, 0);
+
+ rename(path_buf, new_path);
+
+ carmel_reset_index_list();
+ return(T);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Carmel mail open
+
+ Args: stream
+
+ Returns: stream on success, NIL on failure.
+
+The complex set of comparison determine if we get it for read, write or not
+at all.
+
+The folder will be open for write if:
+ - It is not locked
+ - The carmel index is writeable
+ - The carmel index is writeable
+ - The carmel index is openable and not corrupt
+
+The folder open will fail if:
+ - The carmel index does exist
+ - The carmel carmel index is missing
+ AND the folder is locked or unwritable
+
+The folder is opened readonly if:
+ - It is locked
+ - The carmel index is not writable
+ - The carmel index is not writable
+
+ ----*/
+
+MAILSTREAM *
+carmel_open(stream)
+ MAILSTREAM *stream;
+{
+ char carmel_index_path[CARMEL_PATHBUF_SIZE];
+ char carmel2_index_path[CARMEL_PATHBUF_SIZE];
+ char tmp[MAILTMPLEN], tmp2[MAILTMPLEN];
+ struct stat carmel_stat, carmel2_stat;
+ int carmel2_up_to_date;
+
+ if(!stream)
+ return(&carmelproto); /* Must be a OP_PROTOTYPE call */
+
+ mailcache = carmel2_cache;
+
+ /* close old file if stream being recycled */
+ if (LOCAL) {
+ carmel_close (stream); /* dump and save the changes */
+ stream->dtb = &carmeldriver; /* reattach this driver */
+ mail_free_cache (stream); /* clean up cache */
+ }
+
+ /* Allocate local stream */
+ stream->local = fs_get (sizeof (CARMEL2LOCAL));
+ LOCAL->carmel = 1;
+ LOCAL->msg_buf = NULL;
+ LOCAL->msg_buf_size = 0;
+ LOCAL->buffered_file = NULL;
+ LOCAL->msg_buf_text_start = NULL;
+ LOCAL->msg_buf_text_offset = 0;
+ LOCAL->stdio_buf = NULL;
+ LOCAL->dirty = NIL;
+ stream->msgno = -1;
+ stream->env = NULL;
+ stream->body = NULL;
+ stream->scache = 1;
+ LOCAL->calc_paths = carmel_calc_paths;
+ LOCAL->aux_copy = carmel_copy;
+ LOCAL->index_stream = NULL;
+ LOCAL->new_file_on_copy = 0;
+
+ /*------ Figure out the file paths -------*/
+ if(carmel_calc_paths(CalcPathCarmelIndex,stream->mailbox,0) == NULL)
+ return(NULL);
+ strcpy(carmel_index_path,
+ carmel_calc_paths(CalcPathCarmelIndex,stream->mailbox,0));
+ strcpy(carmel2_index_path,
+ carmel_calc_paths(CalcPathCarmel2Index,stream->mailbox,0));
+
+ /*------ Does the CARMEL index exist? Fail if not ------*/
+ if(stat(carmel_index_path, &carmel_stat) < 0) {
+ sprintf(carmel_error_buf, "Can't open mailbox: %s", strerror(errno));
+ mm_log(carmel_error_buf, ERROR);
+ return(NULL); /* FAIL, no carmel index */
+ }
+
+ /*----- Determine if carmel index is up to date -----*/
+ if(stat(carmel2_index_path, &carmel2_stat) >= 0 &&
+ carmel2_stat.st_mtime >= carmel_stat.st_mtime)
+ carmel2_up_to_date = 1;
+ else
+ carmel2_up_to_date = 0;
+
+ /*------ Do we have write access to the Carmel mail index? ----*/
+ if(access(carmel2_index_path, W_OK) != 0 && errno != ENOENT)
+ stream->readonly = 1; /* fail later if dir index is uncreatable */
+
+ /*------ If CARMEL index R/O and Carmel out of date then fail -----*/
+ if(stream->readonly && !carmel2_up_to_date) {
+ mm_log("Can't open mailbox; Unable to write carmel index", ERROR);
+ return(NULL);
+ }
+
+ /*-------- Case for R/O stream or failed lock ---------*/
+ if(stream->readonly ||
+ carmel2_lock(stream->local, stream->mailbox, READ_LOCK)< 0){
+ /* If carmel is out of date here that's OK, we just will see old data*/
+ if(access(carmel_index_path, R_OK) == 0) {
+ stream->readonly = 1;
+ goto open_it;
+ } else {
+ /*-- Can't lock, and there's no carmel index, best to fail -*/
+ mm_log("Can't open mailbox, can't lock to create carmel index",
+ ERROR);
+ return(NULL);
+ }
+ }
+ /* Index is now read locked */
+
+ /*---- Got an up to date carmel index and write access too ----*/
+ if(carmel2_up_to_date)
+ if(access(carmel2_index_path, W_OK) == 0) {
+ goto open_it;
+ } else {
+ stream->readonly = 1;
+ goto open_it;
+ }
+
+ /*---- If needed recreation of carmel index fails, go readonly -----*/
+ if(vmail2carmel(carmel_index_path, carmel2_index_path, stream,
+ stream->mailbox) < 0) {
+ carmel2_unlock(stream->local, stream->mailbox, READ_LOCK);
+ stream->readonly = 1;
+ }
+
+ open_it:
+ /*-- carmel_open2 shouldn't fail after all this check, but just in case -*/
+ if(carmel_open2(stream, carmel2_index_path) < 0) {
+ /* carmel_open2 will take care of the error message */
+ carmel2_unlock(stream->local, stream->mailbox, READ_LOCK);
+ if (LOCAL->msg_buf) fs_give ((void **) &LOCAL->msg_buf);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ return(NULL);
+ }
+
+ if(stream->readonly)
+ mm_log("Mailbox opened READONLY", WARN);
+
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,stream->recent);
+
+ return(stream);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Create a carmel2 index for a carmel index
+
+ Args: folder -- the full UNIX path name existing carmel folder
+ carmel_folder -- the full UNIX path name of carmel index to create
+ stream -- MAILSTREAM to operate on
+
+ Returns: 0 if all is OK
+ -1 if it failed
+
+ This reads the Carmel index, and the headers of the carmel messages to
+construct entries for a carmel index.
+ ----*/
+static
+vmail2carmel(folder, carmel2_folder, stream, mailbox)
+ MAILSTREAM *stream;
+ char *folder, *carmel2_folder;
+ char *mailbox;
+{
+ FILE *carmel_stream;
+ FILE *carmel2_stream;
+ ENVELOPE *e;
+ BODY *b;
+ MESSAGECACHE mc;
+ char pathbuf[CARMEL_PATHBUF_SIZE], *message, *p, *save_subject;
+ struct stat st;
+ STRING string_struct;
+
+ sprintf(pathbuf, "Recreating Carmel2 index for \"%s\"...",
+ carmel_pretty_mailbox(mailbox));
+ mm_log(pathbuf, WARN);
+
+ mm_critical(stream);
+ if(carmel2_lock(stream->local, mailbox, WRITE_LOCK) < 0)
+ return(-1);
+
+ carmel_stream = fopen(folder,"r");
+ if(carmel_stream == NULL) {
+ carmel2_unlock(stream->local, mailbox, WRITE_LOCK);
+ mm_nocritical(stream);
+ return(-1);
+ }
+
+ carmel2_stream = fopen(carmel2_folder, "w");
+ if(carmel2_stream == NULL) {
+ carmel2_unlock(stream->local, mailbox, WRITE_LOCK);
+ mm_nocritical(stream);
+ return(-1);
+ }
+
+ fprintf(carmel2_stream, "\254\312--CARMEL-MAIL-FILE-INDEX--\n");
+
+ /* The first .... line */
+ fgets(carmel_20k_buf, sizeof(carmel_20k_buf), carmel_stream);
+
+ /*---- Loop through all entries in carmel index -----*/
+ while(fgets(carmel_20k_buf,sizeof(carmel_20k_buf),carmel_stream) != NULL){
+
+ mc.data1 = 0L;
+ mc.data2 = atol(carmel_20k_buf); /* The file name/number */
+ mc.deleted = 0;
+ mc.seen = 1;
+ mc.flagged = 0;
+ mc.answered = 0;
+ mc.recent = 0;
+ mc.user_flags = 0;
+
+ strcpy(pathbuf,carmel_calc_paths(CalcPathCarmel2Data,
+ mailbox, mc.data2));
+
+ if(stat(pathbuf, &st) >= 0 ||
+ stat(strcat(pathbuf, ".wid"), &st) >= 0) {
+ save_subject = cpystr(carmel_20k_buf + 27);
+ save_subject[strlen(save_subject) - 1] = '\0';
+ mc.rfc822_size = st.st_size;
+ message = carmel2_readmsg(stream, 1, 0, mc.data2);
+ INIT(&string_struct, mail_string, (void *)"", 0);
+ rfc822_parse_msg(&e, &b, message, strlen(message), &string_struct,
+ mylocalhost(), carmel_20k_buf);
+ carmel2_parse_bezerk_status(&mc, message);
+ carmel2_rfc822_date(&mc, message);
+ if(e->subject == NULL ||
+ strncmp(save_subject,e->subject,strlen(save_subject))){
+ if(e->subject != NULL)
+ fs_give((void **)(&(e->subject)));
+ if(strlen(save_subject))
+ e->subject = cpystr(save_subject);
+ else
+ e->subject = NULL;
+ }
+ } else {
+ /* Data file is missing; copy record as best we can */
+ carmel_20k_buf[strlen(carmel_20k_buf) - 1] = '\0';
+ e = mail_newenvelope();
+ e->subject = cpystr(carmel_20k_buf+27);
+ e->from = mail_newaddr();
+ e->from->mailbox = cpystr(carmel_20k_buf+17);
+ for(p = e->from->mailbox; *p && !isspace(*p); p++);
+ *p = '\0';
+ mc.hours = 1;
+ mc.minutes = 0;
+ mc.seconds = 0;
+ mc.zoccident = 0;
+ mc.zhours = 0;
+ mc.zminutes = 0;
+ mc.day = 1;
+ mc.month = 1;
+ mc.year = 1;
+ }
+ carmel2_write_index(e, &mc, carmel2_stream);
+ mail_free_envelope(&e);
+ }
+
+ fclose(carmel_stream);
+ fclose(carmel2_stream);
+
+ carmel2_unlock(stream->local, mailbox, WRITE_LOCK);
+ mm_nocritical(stream);
+
+ mm_log("Recreating Carmel2 index.....Done", WARN);
+
+ return(1);
+}
+
+
+
+#define TESTING /* Make a copy of carmel index before rewriting */
+
+/*----------------------------------------------------------------------`
+ Close a carmel mail folder
+
+Args: stream -- Mail stream to close
+
+This will cause the carmel index to be recreated
+
+ ----*/
+void
+carmel_close(stream)
+ MAILSTREAM *stream;
+{
+ if (LOCAL && LOCAL->index_stream != NULL) {
+ /* only if a file is open */
+ if(!stream->readonly) {
+ carmel_check (stream); /* dump final checkpoint */
+ carmel2_unlock(stream->local, stream->mailbox, READ_LOCK);
+ }
+
+ fclose(LOCAL->index_stream);
+ if (LOCAL->msg_buf) fs_give ((void **) &LOCAL->msg_buf);
+ if(LOCAL->stdio_buf)
+ fs_give ((void **) &LOCAL->stdio_buf);
+ fs_give ((void **) &stream->local);
+
+ }
+ stream->dtb = NIL; /* log out the DTB */
+}
+
+
+
+/*----------------------------------------------------------------------
+ Check for new mail and write out the indexes as a checkpoint
+
+Args -- The stream to checkpoint
+ ----*/
+void
+carmel_check(stream)
+ MAILSTREAM *stream;
+{
+ if(carmel2_check2(stream))
+ carmel_recreate_index(stream); /* only if carmel2 index changed */
+}
+
+
+
+
+/*----------------------------------------------------------------------
+ Expunge the mail stream
+
+Args -- The stream to checkpoint
+
+Here we expunge the carmel2 index and then recreate the carmel index from
+it.
+ ----*/
+void
+carmel_expunge(stream)
+ MAILSTREAM *stream;
+{
+ carmel2_expunge(stream);
+ carmel_recreate_index(stream);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Rewrite the carmel index from the carmel2 index
+
+Args: -- stream to be recreated
+
+BUG: could probably use some error checking here
+ ----*/
+static void
+carmel_recreate_index(stream)
+ MAILSTREAM *stream;
+{
+#ifdef TESTING
+ char copy1[CARMEL_PATHBUF_SIZE];
+ char copy2[CARMEL_PATHBUF_SIZE];
+#define MAXBACKUPS 4
+ int nback;
+#endif
+ long msgno;
+ MESSAGECACHE *mc;
+ ENVELOPE *envelope;
+ FILE *carmel_index;
+ struct stat sb;
+ char *cm_index_file;
+ struct timeval tp[2];
+
+ mm_critical(stream);
+
+ /*---- calculate the carmel index path -----*/
+ strcpy(carmel_path_buf,
+ (*(LOCAL->calc_paths))(CalcPathCarmelIndex, stream->mailbox, 0));
+
+ /*---- Get the first index line out of old index ----*/
+ *carmel_20k_buf = '\0';
+ carmel_index = fopen(carmel_path_buf, "r");
+ if(carmel_index != NULL) {
+ fgets(carmel_20k_buf,sizeof(carmel_20k_buf), carmel_index);
+ fclose(carmel_index);
+ }
+
+#ifdef TESTING
+ /*--- rotate Backup copies of the index --- crude*/
+ for (nback = MAXBACKUPS; 1 < nback; --nback) {
+ strcpy(copy2,
+ (*(LOCAL->calc_paths))(CalcPathCarmelBackup, stream->mailbox, nback));
+ strcpy(copy1,
+ (*(LOCAL->calc_paths))(CalcPathCarmelBackup, stream->mailbox, nback-1));
+ unlink(copy2);
+ link(copy1,copy2);
+ }
+ unlink(copy1);
+ link(carmel_path_buf, copy1);
+ unlink(carmel_path_buf);
+#endif
+
+ /*---- Reopen the carmel index for write, truncating it ------*/
+ carmel_index = fopen(carmel_path_buf, "w");
+
+ if(carmel_index == NULL)
+ goto bomb;
+
+ if(fputs(carmel_20k_buf, carmel_index) == EOF)
+ goto bomb;
+
+ /*---- Loop through all the message the stream has ----*/
+ for(msgno = 1; msgno <= stream->nmsgs; msgno++) {
+ mc = MC(msgno);
+ envelope = carmel2_fetchstructure(stream, msgno, NULL);
+ if(write_carmel_index(carmel_index, mc, envelope) < 0)
+ goto bomb;
+ }
+ if(fclose(carmel_index) == EOF)
+ goto bomb;
+
+ /*---- Get mod time of carmel index ---*/
+ cm_index_file = (*(LOCAL->calc_paths))(CalcPathCarmelIndex,
+ stream->mailbox, 0);
+ if(stat(cm_index_file, &sb) >= 0) {
+ tp[0].tv_sec = sb.st_atime;
+ tp[0].tv_usec = 0;
+ tp[1].tv_sec = sb.st_mtime;
+ tp[1].tv_usec = 0;
+
+ /*---- Set Carmel index to have same mod time ---*/
+ utimes(stream->mailbox, tp);
+ }
+ mm_nocritical(stream);
+
+#ifdef CHATTY
+ sprintf(carmel_error_buf, "Rewrote Carmel index \"%s\" with %d messages",
+ carmel_pretty_mailbox(stream->mailbox), msgno-1);
+ mm_log(carmel_error_buf, WARN);
+#endif
+
+ return;
+
+ bomb:
+ mm_nocritical(stream);
+ sprintf(carmel_error_buf, "Error recreating carmel index: %s",
+ strerror(errno));
+ mm_log(carmel_error_buf, ERROR);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Do all the file name generation for the carmel driver
+
+Args: operation -- The type of calculation to perform
+ mailbox -- The canonical mailbox name
+ data_file_num -- For calculating the name of a file storing a message
+
+Returns: string with the path name in static storage or NULL on error
+
+The Path passed in will be in the format #carmel#user-id#folder or
+ #carmel#folder
+
+The only that error occurs is in looking up the user-id. A error is logged
+when that happens.
+ ----*/
+static char *
+carmel_calc_paths(operation, mailbox, data_file_num)
+ char *mailbox;
+ int data_file_num, operation;
+{
+ static char path[CARMEL_PATHBUF_SIZE];
+ char *home_dir, *end_user_name;
+ struct passwd *pw;
+ struct carmel_mb_name *parsed_name;
+
+ parsed_name = carmel_parse_mb_name(mailbox, '\0');
+
+ if(parsed_name == NULL) {
+ mm_log("Internal error (bug). Invalid Carmel folder name",ERROR);
+ return(NULL);
+ }
+
+ if(parsed_name->user != NULL) {
+ /*---- has a user in mailbox name -----*/
+ pw = getpwnam(parsed_name->user);
+ if(pw == NULL) {
+ sprintf(carmel_error_buf,
+ "Error accessing mailbox \"%s\". No such user name \"%s\"\n",
+ mailbox, parsed_name->user);
+ mm_log(carmel_error_buf, ERROR);
+ carmel_free_mb_name(parsed_name);
+ return(NULL);
+ }
+ home_dir = pw->pw_dir;
+ } else {
+ home_dir = myhomedir();
+ }
+ mailbox = parsed_name->mailbox;
+
+ if(strucmp2(mailbox, "inbox") == 0) /* Map "inbox" into "MAIL" */
+ mailbox = "MAIL";
+
+
+ switch(operation) {
+
+ case CalcPathCarmelIndex:
+ sprintf(path, "%s/%s/%s", home_dir, CARMEL_INDEX_DIR, mailbox);
+ break;
+
+ case CalcPathCarmelBackup:
+ sprintf(path, "%s/%s/.%s.COD-FUR-%d", home_dir, CARMEL_INDEX_DIR,
+ mailbox, data_file_num);
+ break;
+
+ case CalcPathCarmel2Data:
+ sprintf(path, "%s/%s/%d", home_dir, CARMEL_MSG_DIR, data_file_num);
+ break;
+
+ case CalcPathCarmel2Index:
+ sprintf(path, "%s/%s/.%s.cx", home_dir, CARMEL_INDEX_DIR, mailbox);
+ break;
+
+ case CalcPathCarmel2MAXNAME:
+ sprintf(path, "%s/%s", home_dir, CARMEL_MAXFILE);
+ break;
+
+ case CalcPathCarmel2WriteLock:
+ sprintf(path, "%s/%s/.%s.wl", home_dir, CARMEL_INDEX_DIR, mailbox);
+ break;
+
+ case CalcPathCarmel2ReadLock:
+ sprintf(path, "%s/%s/.%s.rl", home_dir, CARMEL_INDEX_DIR, mailbox);
+ break;
+ }
+
+/* sprintf(carmel_debug, "CARMEL: calc_paths returning \"%s\"\n", path);
+ mm_dlog(carmel_debug); */
+
+ carmel_free_mb_name(parsed_name);
+
+ return(path);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Copy carmel messages, this is the auxiliary copy, called from carmel2_copy
+
+Args: local -- Fake CARMELLOCAL structure
+ mailbox -- Destination mailbox, in FQN format, e.g. #carmel#fooo
+ e -- envelope
+ mc -- MESSAGECACHE entry
+
+Retuns: -1 on failure
+ ----*/
+long
+carmel_copy(local, mailbox, e, mc)
+ CARMEL2LOCAL *local;
+ char *mailbox;
+ ENVELOPE *e;
+ MESSAGECACHE *mc;
+{
+ FILE *carmel_index;
+ int new;
+ struct stat sb;
+ char *carmel_index_file;
+
+ carmel_index_file = (*(local->calc_paths))(CalcPathCarmelIndex, mailbox,0);
+ if(carmel_index_file == NULL)
+ return(-1);
+
+ if(stat(carmel_index_file, &sb) < 0)
+ new = 1;
+ else
+ new = 0;
+
+ carmel_index = fopen(carmel_index_file, "a+");
+ if(carmel_index == NULL)
+ return(-1);
+
+ if(new)
+ fprintf(carmel_index, "%-13.13s index.....\n",
+ carmel_pretty_mailbox(mailbox));
+
+ if(write_carmel_index(carmel_index, mc, e) < 0) {
+ fclose(carmel_index);
+ return(-1);
+ }
+
+ if(fclose(carmel_index) == EOF)
+ return(-1);
+
+ return(0);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Make sure all the directories are there and writable for carmel
+
+Args: folders_dir - suggested directory, ignored by carmel
+
+Result: returns 0 if OK, -1 if not
+ ----*/
+carmel_init(folders_dir)
+ char *folders_dir;
+{
+ char *p;
+ FILE *mail_file;
+ struct stat sb;
+
+ /*----- The ~/.vmail directory ----*/
+ sprintf(carmel_path_buf, "%s/%s", myhomedir(), CARMEL_DIR);
+ carmel2_check_dir(carmel_path_buf);
+
+ /*---- The ~/.vmail/.MAXNAME file ------*/
+ sprintf(carmel_path_buf,"%s/%s", myhomedir(), CARMEL_MAXFILE);
+ if(stat(carmel_path_buf, &sb) < 0) {
+ mail_file = fopen(carmel_path_buf, "w");
+ if(mail_file == NULL) {
+ mm_log("Error creating .MAXNAME file", ERROR);
+ } else {
+ fprintf(mail_file, "100000");
+ fclose(mail_file);
+ }
+ }
+
+ /*----- The ~/.vmail/index directory -----*/
+ sprintf(carmel_path_buf,"%s/%s",myhomedir(),CARMEL_INDEX_DIR);
+ carmel2_check_dir(carmel_path_buf);
+
+ /*----- The ~/.vmail/msg directory -----/*
+ sprintf(carmel_path_buf,"%s/%s",myhomedir(),CARMEL_MSG_DIR);
+ carmel2_check_dir(carmel_path_buf);
+
+ /*----- The ~/.vmail/MAIL file -----*/
+ sprintf(carmel_path_buf, "%s/%s/MAIL", myhomedir(), CARMEL_INDEX_DIR);
+ if(stat(carmel_path_buf, &sb) < 0) {
+ mail_file = fopen(carmel_path_buf, "w");
+ if(mail_file == NULL) {
+ mm_log("Error creating \"MAIL\" folder", WARN);
+ } else {
+ fprintf(mail_file, "MAIL index.....\n");
+ fclose(mail_file);
+ }
+ }
+
+ return(0);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Write an entry into a carmel index
+
+Args: file -- The open file stream
+ mc -- the MESSAGECACHE
+ envelope -- The envelope to write
+
+Returns: 0 if all is OK, -1 if not
+ ----*/
+static
+write_carmel_index(file, mc, envelope)
+ FILE *file;
+ MESSAGECACHE *mc;
+ ENVELOPE *envelope;
+{
+ if(fprintf(file, "%6d %02d %-.3s %2d %-9.9s %-.50s\n",
+ mc->data2, mc->day,
+ month_abbrev2(mc->month),
+ (mc->year + 1969) % 100,
+ envelope != NULL && envelope->from != NULL &&
+ envelope->from->mailbox != NULL ?
+ envelope->from->mailbox : "",
+ envelope != NULL && envelope->subject != NULL ?
+ envelope->subject : "") == EOF)
+ return(-1);
+ else
+ return(0);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Append a message to a carmel mailbox
+
+Args: stream -- a suggested stream, ignored here
+ mailbox -- FQN of mailbox to append message to
+ message -- Text string of message
+
+Returns: T on success, NIL on failure
+ ----*/
+long
+carmel_append(stream, mailbox, flags, date, message)
+ MAILSTREAM *stream;
+ char *mailbox, *flags, *date;
+ STRING *message;
+{
+ CARMEL2LOCAL local;
+
+ /*---- A fake local data structure to pass to other functions---*/
+ local.calc_paths = carmel_calc_paths;
+ local.carmel = 1;
+ local.aux_copy = carmel_copy;
+
+
+ return(carmel2_append2(stream, &local, mailbox, flags, date, message));
+}
+
+
+
+
+
+/*----------------------------------------------------------------------
+ This really just removes the file Carmel uses for the list of
+ folders, because Carmel will just recreate it from the indexes themselves.
+ ----*/
+static
+carmel_reset_index_list()
+{
+ sprintf(carmel_path_buf, "%s/.inda", myhomedir());
+ unlink(carmel_path_buf);
+
+ sprintf(carmel_path_buf, "%s/.indf", myhomedir());
+ unlink(carmel_path_buf);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Used to make sure there are no old locks of any sort left around
+ when a folder is deleted or when one is created.
+ --*/
+static void
+carmel_kill_locks(mailbox)
+ char *mailbox;
+{
+ unlink(carmel_calc_paths(CalcPathCarmel2WriteLock, mailbox, 0));
+ unlink(carmel_calc_paths(CalcPathCarmel2ReadLock, mailbox, 0));
+ unlink(carmel_calc_paths(CalcPathCarmel2Expunge, mailbox, 0));
+}
diff --git a/contrib/carmel/c-client/carmel.h b/contrib/carmel/c-client/carmel.h
new file mode 100644
index 00000000..ffdabaaa
--- /dev/null
+++ b/contrib/carmel/c-client/carmel.h
@@ -0,0 +1,77 @@
+/*----------------------------------------------------------------------
+
+ T H E C A R M E L M A I L D R I V E R
+
+ Author(s): Laurence Lundblade
+ Baha'i World Centre
+ Data Processing
+ Haifa, Israel
+ Internet: lgl@cac.washington.edu or laurence@bwc.org
+ September 1992
+ Last edited: Aug 31, 1994
+
+ ----------------------------------------------------------------------*/
+
+
+/* Most of the real stuff is defined in carmel2.h */
+
+/* Function prototypes */
+
+#ifdef ANSI
+DRIVER *carmel_valid(char *);
+int carmel_isvalid(char *);
+void *carmel_parameters();
+char *carmel_file (char *, char *);
+void carmel_find(MAILSTREAM *, char *);
+void carmel_find_all(MAILSTREAM *, char *);
+void carmel_find_bboards(MAILSTREAM *, char *);
+long carmel_subscribe();
+long carmel_unsubscribe();
+long carmel_subscribe_bboard();
+long carmel_unsubscribe_bboard();
+long carmel_create(MAILSTREAM *, char *);
+long carmel_delete(MAILSTREAM *, char *);
+long carmel_rename(MAILSTREAM *, char *, char *);
+MAILSTREAM *carmel_open(MAILSTREAM *);
+void carmel_close(MAILSTREAM *);
+void carmel_check(MAILSTREAM *);
+void carmel_void(MAILSTREAM *);
+long carmel_copy(CARMEL2LOCAL *, char *, ENVELOPE *, MESSAGECACHE *);
+int carmel_init(char *);
+long carmel_append(MAILSTREAM *, char *, char *, char *, STRING *);
+#else
+DRIVER *carmel_valid();
+int carmel_isvalid();
+void *carmel_parameters();
+char *carmel_file ();
+void carmel_find();
+void carmel_find_all();
+void carmel_find_bboards();
+long carmel_subscribe();
+long carmel_unsubscribe();
+long carmel_subscribe_bboard();
+long carmel_unsubscribe_bboard();
+long carmel_create();
+long carmel_delete();
+long carmel_rename();
+MAILSTREAM *carmel_open();
+void carmel_close();
+void carmel_check();
+void carmel_expunge();
+long carmel_copy();
+int carmel_init();
+long carmel_append();
+#endif
+
+
+/* These are all relative to the users home directory */
+#define CARMEL_INDEX_DIR ".vmail/index"
+#define CARMEL_DIR ".vmail"
+#define CARMEL_MSG_DIR ".vmail/msg"
+#define CARMEL_MAXFILE ".vmail/.MAXNAME"
+
+/* operations for carmel_calc_path -- must mesh with operations for
+ carmel2_calc_path in carmel2.h
+ */
+#define CalcPathCarmelIndex 100
+#define CalcPathCarmelBackup 101
diff --git a/contrib/carmel/c-client/carmel2.c b/contrib/carmel/c-client/carmel2.c
new file mode 100644
index 00000000..c56f8900
--- /dev/null
+++ b/contrib/carmel/c-client/carmel2.c
@@ -0,0 +1,4412 @@
+#define TESTING
+/*----------------------------------------------------------------------
+
+ T H E C A R M E L M A I L F I L E D R I V E R
+
+ Author(s): Laurence Lundblade
+ Baha'i World Centre
+ Data Processing
+ Haifa, Israel
+ Internet: lgl@cac.washington.edu or laurence@bwc.org
+ September 1992
+
+ Last edited: Aug 31, 1994
+
+The Carmel2 mail file stores messages in individual files and
+implements folders or mailboxes with index files that contain
+references to the files a nd a full c-client envelope in an easily
+parsed form. It was written as a needed part of the pod mail file
+driver with hopes that it might be useful otherwise some day. It has
+only been run with the carmel driver.
+
+Advantages over Berkeley format and driver:
+ + Opening mail folder is very fast
+ + Expunge is fast
+ + Check point is very fast
+ + Memory usage is much lower
+ + Search of message headers is fast
+Disadvantages:
+ - Fetching a large message is slow
+ - Searching the message bodies is slow
+ - Sorting the mailbox is slow
+
+ Index File Format
+ -----------------
+
+The first line of the file is always:
+ "\254\312--CARMEL2-MAIL-FILE-INDEX--\n"
+
+It is followed by index entries which are of the format:
+---START-OF-MESSAGE---\007\001nnnnnnnnnn
+Ufrads______________________________
+Zxxxxxx
+D..... <Many fields here, labeled by the first letter in the line>
+ <Fields may be repeated>
+
+
+The index is almost an ASCII file. With the first version of this
+driver it is not advisable to edit this file, lest the byte counts get
+disrupted. In the future it will be editable. The file starts with
+two binary bytes so the file(1) utility will report it as "data" to
+discourage would be hackers from messing with it. The ^G is also in
+each index entry for the same reason. If you are reading this source you
+probably know enough to edit the index file without destroying it.
+Other parts of the format are designed for the easiest possible
+parsing. The idea was to have a file format that was reasonable to
+fiddle for debugging, to discourage inexperienced folks for fiddling
+it and to be efficient for use by the program.
+
+
+ Routines and data structures
+ ----------------------------
+
+C-CLIENT INTERFACE FUCTIONS
+ carmel2_valid - check to see if a mailbox is valid for carmel2 mail files
+ carmel2_isvalid - actual work of checking
+ carmel2_find - generate list of carmel2 mailboxes
+ carmel2_sift_files - select mailboxes out of list, used with scandir
+ carmel2_find_bboars - dummy routine, doesn't do anything
+
+ carmel2_open - initial phase of opening a mailbox
+ carmel2_open2 - real work of opening mailbox, shared with carmel driver
+ carmel2_close - close a mail stream
+
+ carmel2_fetchfast - fetch "fast" message info, noop for this driver
+ carmel2_fetchflags - fetch the flags, a noop for this driver
+ carmel2_fetchstructure - fetch and envelope and possibly body
+
+ carmel2_fetchheader - fetch the text header of the message
+ carmel2_fetchtext - fetch the text of the message (no header included)
+ carmel2_fetchbody - fetch the text of a body part of a message
+
+ carmel2_setflag - Set a flag for a message sequence
+ carmel2_clearflag - Clear a flag for a message sequence
+
+ carmel2_search - Invoke the search facilities
+
+ carmel2_ping - Check for new mail and see if still alive
+ carmel2_check - Checkpoint the message statuses to the disk
+ carmel2_expunge - Delete all the messages marked for delete
+
+ carmel2_copy - Copy a message to another mailbox
+ carmel2_move - Move a message to another mailbox
+
+ carmel_gc - Garbage collection, a noop for this driver
+ carmel_cache - The required c-client cache handler, doesn't do much
+
+ carmel2_append - Append a message to a mail folder
+
+SUPPORT FUNCTIONS
+ carmel_bodystruct - Fetch the body structure for a carmel message
+ carmel_parse_address - Parse the address out of a carmel index
+ carmel_parse_addr_field - Parse individual address field out of carmel index
+ carmel2_write_index - Write an entry into a carmel index
+ carmel2_index_address - Write an address into a carmel index
+
+ carmel_readmsg - Read a message file into memory, header text or both
+ carmel_spool_mail - Get new mail out of spoold mail file
+ carmel_copy_msg_file - Make copy of messsage file when copying (unused)
+
+ carmel2_lock - Lock a carmel index for read or write
+ carmel2_unlock - Unlock a carmel index
+ carmel2_update_lock - Touch lock mod time, marking it as active
+ carmel2_bezerk_lock - Lock the spool mail file Berkeley style
+ carmel2_bezerk_unlock - Unlock the spool mail file
+
+ carmel2_check_dir - Check that directory exists and is writeable
+ carmel2_new_data_file - Get file number for new message file
+ carmel2_calc_paths - Calculate path names for carmel driver
+
+ carmel_parse_bezerk_status - Parse the "Status: OR" field in mail headers
+ carmel2_getflags - Turn the named flags into a bit mask
+ carmel_new_mc - Get pointer to new MESSAGECACHE, allocating if needed
+
+ carmel_append2 - The real work of append a message to a mailbox
+
+ carmel2_search-* - A bunch of search support routines
+
+ month_abbrev2 - Returns three letter month abbreviation given a name
+ carmel2_rfc822_date - Parse a date string, into MESSAGECACHE structure
+ carmel2_date2string - Generate a date string from MESSAGECACHE
+ carmel2_parse_date - Parse date out of a carmel index
+ next_num - Called to parse dates
+
+ strucmp2 - Case insensitive strcmp()
+ struncmp2 - Case insensitive strncmp()
+
+ ----------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <netdb.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <sys/dir.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "carmel2.h"
+#include "rfc822.h"
+#include "misc.h"
+
+
+char *strindex2(), *strchr();
+
+#ifdef ANSI
+static int carmel2_sift_files(struct direct *);
+static BODY *carmel2_bodystruct(MAILSTREAM *, MESSAGECACHE *);
+static ADDRESS *carmel2_parse_address(char *, ADDRESS *, char *);
+static char *carmel2_parse_addr_field(char **);
+static int carmel2_index_address(ADDRESS *, int , FILE *);
+static int carmel2_parse_mail(MAILSTREAM *, long);
+void carmel2_spool_mail(MAILSTREAM *, char *, char *, int);
+static int carmel2_copy_msg_file(MAILSTREAM *, int, char *);
+static int carmel2_bezerk_lock(char *, char *);
+static void carmel2_bezerk_unlock(int, char *);
+static int carmel2_new_data_file(CARMEL2LOCAL *, char *);
+static char *carmel2_calc_paths(int, char *, int);
+static short carmel2_getflags(MAILSTREAM *, char *);
+static MESSAGECACHE *carmel2_new_mc(MAILSTREAM *, int);
+static char carmel2_search_all(MAILSTREAM *, long, char *, long);
+static char carmel2_search_answered(MAILSTREAM *, long, char *, long);
+static char carmel2_search_deleted(MAILSTREAM *, long, char *, long);
+static char carmel2_search_flagged(MAILSTREAM *, long, char *, long);
+static char carmel2_search_keyword(MAILSTREAM *, long, char *, long);
+static char carmel2_search_new(MAILSTREAM *, long, char *, long);
+static char carmel2_search_old(MAILSTREAM *, long, char *, long);
+static char carmel2_search_recent(MAILSTREAM *, long, char *, long);
+static char carmel2_search_seen(MAILSTREAM *, long, char *, long);
+static char carmel2_search_unanswered(MAILSTREAM *, long, char *, long);
+static char carmel2_search_undeleted(MAILSTREAM *, long, char *, long);
+static char carmel2_search_unflagged(MAILSTREAM *, long, char *, long);
+static char carmel2_search_unkeyword(MAILSTREAM *, long, char *, long);
+static char carmel2_search_unseen(MAILSTREAM *, long, char *, long);
+static char carmel2_search_before(MAILSTREAM *, long, char *, long);
+static char carmel2_search_on(MAILSTREAM *, long, char *, long);
+static char carmel2_search_since(MAILSTREAM *, long, char *, long);
+static unsigned long carmel2_msgdate(MAILSTREAM *, long);
+static char carmel2_search_body(MAILSTREAM *, long, char *, long);
+static char carmel2_search_subject(MAILSTREAM *, long, char *, long);
+static char carmel2_search_text(MAILSTREAM *, long, char *, long);
+static char carmel2_search_bcc(MAILSTREAM *, long, char *, long);
+static char carmel2_search_cc(MAILSTREAM *, long, char *, long);
+static char carmel2_search_from(MAILSTREAM *, long, char *, long);
+static char carmel2_search_to(MAILSTREAM *, long, char *, long);
+typedef char (*search_t) ();
+static search_t carmel2_search_date(search_t, long *);
+static search_t carmel2_search_flag(search_t, char **);
+static search_t carmel2_search_string(search_t, char **, long *);
+static void carmel2_date2string(char *, MESSAGECACHE *);
+static void carmel2_parse_date(MESSAGECACHE *, char *);
+static int next_num(char **);
+#else
+static int carmel2_sift_files();
+static BODY *carmel2_bodystruct();
+static ADDRESS *carmel2_parse_address();
+static char *carmel2_parse_addr_field();
+static int carmel2_index_address();
+static int carmel2_parse_mail();
+void carmel2_spool_mail();
+static int carmel2_copy_msg_file();
+static int carmel2_bezerk_lock();
+static void carmel2_bezerk_unlock();
+static int carmel2_new_data_file();
+static char *carmel2_calc_paths();
+static short carmel2_getflags();
+static MESSAGECACHE *carmel2_new_mc();
+static char carmel2_search_all();
+static char carmel2_search_answered();
+static char carmel2_search_deleted();
+static char carmel2_search_flagged();
+static char carmel2_search_keyword();
+static char carmel2_search_new();
+static char carmel2_search_old();
+static char carmel2_search_recent();
+static char carmel2_search_seen();
+static char carmel2_search_unanswered();
+static char carmel2_search_undeleted();
+static char carmel2_search_unflagged();
+static char carmel2_search_unkeyword();
+static char carmel2_search_unseen();
+static char carmel2_search_before();
+static char carmel2_search_on();
+static char carmel2_search_since();
+static unsigned long carmel2_msgdate();
+static char carmel2_search_body();
+static char carmel2_search_subject();
+static char carmel2_search_text();
+static char carmel2_search_bcc();
+static char carmel2_search_cc();
+static char carmel2_search_from();
+static char carmel2_search_to();
+typedef char (*search_t) ();
+static search_t carmel2_search_date();
+static search_t carmel2_search_flag();
+static search_t carmel2_search_string();
+static void carmel2_date2string();
+static void carmel2_parse_date();
+static int next_num();
+#endif
+
+
+/*------ Driver dispatch used by MAIL -------*/
+
+DRIVER carmel2driver = {
+ "carmel2",
+ (DRIVER *) NIL, /* next driver */
+ carmel2_valid, /* mailbox is valid for us */
+ carmel2_parameters, /* manipulate parameters */
+ carmel2_find, /* find mailboxes */
+ carmel2_find_bboards, /* find bboards */
+ carmel2_find_all, /* find all mailboxes */
+ carmel2_find_bboards, /* find all bboards ; just a noop here */
+ carmel2_subscribe, /* subscribe to mailbox */
+ carmel2_unsubscribe, /* unsubscribe from mailbox */
+ carmel2_subscribe_bboard, /* subscribe to bboard */
+ carmel2_unsubscribe_bboard, /* unsubscribe from bboard */
+ carmel2_create,
+ carmel2_delete,
+ carmel2_rename,
+ carmel2_open, /* open mailbox */
+ carmel2_close, /* close mailbox */
+ carmel2_fetchfast, /* fetch message "fast" attributes */
+ carmel2_fetchflags, /* fetch message flags */
+ carmel2_fetchstructure, /* fetch message envelopes */
+ carmel2_fetchheader, /* fetch message header only */
+ carmel2_fetchtext, /* fetch message body only */
+ carmel2_fetchbody, /* fetch message body section */
+ carmel2_setflag, /* set message flag */
+ carmel2_clearflag, /* clear message flag */
+ carmel2_search, /* search for message based on criteria */
+ carmel2_ping, /* ping mailbox to see if still alive */
+ carmel2_check, /* check for new messages */
+ carmel2_expunge, /* expunge deleted messages */
+ carmel2_copy, /* copy messages to another mailbox */
+ carmel2_move, /* move messages to another mailbox */
+ carmel2_append, /* Append message to a mailbox */
+ carmel2_gc, /* garbage collect stream */
+};
+
+MAILSTREAM carmel2proto ={&carmel2driver};/*HACK for default_driver in pine.c*/
+
+/*-- Some string constants used in carmel2 indexes --*/
+char *carmel2_s_o_m = "---START-OF-MESSAGE---";
+int carmel2_s_o_m_len = 22;
+char *carmel2_s_o_f = "\254\312--CARMEL2-MAIL-FILE-INDEX--\n";
+
+
+
+/*-- Buffers used for various reasons, also used by carmel driver --*/
+char carmel_20k_buf[20000], carmel_path_buf[CARMEL_PATHBUF_SIZE],
+ carmel_error_buf[200];
+
+
+/*----------------------------------------------------------------------
+ Carmel mail validate mailbox
+
+Args: - mailbox name
+
+Returns: our driver if name is valid, otherwise calls valid in next driver
+ ---*/
+
+DRIVER *carmel2_valid (name)
+ char *name;
+{
+ return (carmel2_isvalid (name) ? &carmel2driver : NIL);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Open the mailbox and see if it's the correct format
+
+Args: name -- name of the mailbox, not fully qualified path
+
+Returns: 0 if is is not valid, 1 if it is valid
+
+The file must be a regular file and start with the string in the
+variable carmel2_s_o_f. It has a magic number of sorts
+ ----*/
+int
+carmel2_isvalid (name)
+ char *name;
+{
+ char *carmel_index_file;
+ struct stat sbuf;
+ int fd;
+ struct carmel_mb_name *parsed_name;
+
+ /*---- Must match FQN name of carmel mailboxes ----*/
+ parsed_name = carmel_parse_mb_name(name, '2');
+ if(parsed_name == NULL)
+ return;
+ carmel_free_mb_name(parsed_name);
+
+ carmel_index_file = carmel2_calc_paths(CalcPathCarmel2Index, name, 0);
+ if(carmel_index_file == NULL)
+ return(0); /* Will get two error messages here, one from dummy driver */
+
+ if(stat(carmel_index_file, &sbuf) < 0)
+ return(1); /* If the names match and it doesn't exist, it's valid */
+
+ if(!(sbuf.st_mode & S_IFREG))
+ return(0);
+
+ fd = open(carmel_index_file, O_RDONLY);
+ if(fd < 0)
+ return(0);
+
+ if(read(fd, carmel_20k_buf, 200) <= 0)
+ return(0);
+
+ close(fd);
+
+ if(strncmp(carmel_20k_buf, carmel2_s_o_f, strlen(carmel2_s_o_f)))
+ return(0);
+
+ return(1);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Set parameters for the carmel driver.
+
+ Currently does nothing
+ ----*/
+void *
+carmel2_parameters()
+{
+}
+
+
+
+
+/*----------------------------------------------------------------------
+ Carmel mail find list of mailboxes
+
+Args: stream -- mail stream to find mailboxes for
+ pat -- wildcard pattern to match (currently unused)
+
+Returns nothing, the results are passed back by calls to mm_log
+
+This needs testing
+ ----*/
+
+void
+carmel2_find (stream, pat)
+ MAILSTREAM *stream;
+ char *pat;
+{
+ char tmp[MAILTMPLEN];
+ struct direct **namelist, **n;
+ int num;
+ extern int alphasort();
+ struct carmel_mb_name *parsed_name;
+ struct passwd *pw;
+
+ parsed_name = carmel_parse_mb_name(pat, '2');
+ if(parsed_name == NULL)
+ return;
+
+ if(parsed_name->user == NULL) {
+ sprintf(tmp, "%s/%s", myhomedir(), CARMEL2_INDEX_DIR);
+ } else {
+ pw = getpwnam(parsed_name->user);
+ if(pw == NULL) {
+ sprintf(carmel_error_buf,
+ "Error accessing mailbox \"%s\". No such user name \"%s\"\n",
+ pat, parsed_name->user);
+ mm_log(carmel_error_buf, ERROR);
+ return;
+ }
+ sprintf(tmp, "%s/%s", pw->pw_dir, CARMEL2_INDEX_DIR);
+ }
+
+ sprintf(tmp, "%s/%s", myhomedir(), CARMEL2_INDEX_DIR);
+
+ num = scandir(tmp, &namelist, carmel2_sift_files, alphasort);
+
+ if(num <= 0) {
+/* Seems this error message should not be logged
+ sprintf(carmel_error_buf, "Error finding mailboxes \"%s\": %s",
+ pat, strerror(errno));
+ mm_log(carmel_error_buf, ERROR); */
+ return;
+ }
+
+ for(n = namelist; num > 0; num--, n++) {
+ if(parsed_name->user == NULL) {
+ sprintf(tmp, "%s%s%c%s", CARMEL_NAME_PREFIX, parsed_name->version,
+ CARMEL_NAME_CHAR, (*n)->d_name);
+ } else {
+ sprintf(tmp, "%s%s%c%s%c%s", CARMEL_NAME_PREFIX,
+ parsed_name->version, CARMEL_NAME_CHAR,
+ parsed_name->user, CARMEL_NAME_CHAR,
+ (*n)->d_name);
+ }
+ mm_mailbox(tmp);
+ free(*n);
+ }
+ free(namelist);
+ carmel_free_mb_name(parsed_name);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Find_all is the same as find for the carmel2 format -- no notion
+ of subscribed and unsubscribed
+ ---*/
+void
+carmel2_find_all (stream, pat)
+ MAILSTREAM *stream;
+ char *pat;
+{
+ carmel2_find(stream, pat);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Carmel2 mail find list of bboards; always NULL, no bboards
+ ----*/
+void carmel2_find_bboards (stream,pat)
+ MAILSTREAM *stream;
+ char *pat;
+{
+ /* Always a no-op, Carmel2 file format doesn't do news */
+}
+
+
+
+/*----------------------------------------------------------------------
+ Carmel2 mail find list of bboards; always NULL, no bboards
+ ----*/
+void carmel2_find_all_bboards (stream,pat)
+ MAILSTREAM *stream;
+ char *pat;
+{
+ /* Always a no-op, Carmel2 file format doesn't do news */
+}
+
+
+
+/*----------------------------------------------------------------------
+ This is used by scandir to determine which files in the directory
+ are treated as mail files
+ ----*/
+static int
+carmel2_sift_files(dir)
+ struct direct *dir;
+{
+ if(dir->d_name[0] == '.')
+ return(0);
+ else
+ return(1);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Dummy subscribe/unsubscribe routines.
+ Carmel driver does support this. Maybe it should support the UNIX
+ sm sometime
+ ----*/
+long carmel2_subscribe() {}
+long carmel2_unsubscribe() {}
+long carmel2_subscribe_bboard() {}
+long carmel2_unsubscribe_bboard() {}
+
+
+
+/*----------------------------------------------------------------------
+ ----*/
+long
+carmel2_create(stream, mailbox)
+ MAILSTREAM *stream;
+ char *mailbox;
+{
+}
+
+
+
+/*----------------------------------------------------------------------
+ ----*/
+long
+carmel2_delete(stream, mailbox)
+ MAILSTREAM *stream;
+ char *mailbox;
+{
+}
+
+
+
+/*----------------------------------------------------------------------
+ ----*/
+long
+carmel2_rename(stream, orig_mailbox, new_mailbox)
+ MAILSTREAM *stream;
+ char *orig_mailbox, *new_mailbox;
+{
+}
+
+
+/*----------------------------------------------------------------------
+ Open a carmel2 mail folder/stream
+
+Args: stream -- stream to by fully opened
+
+Returns: it's argument or NULL of the open fails
+
+This needs testing and more code, see pod_open for examples
+ ----*/
+
+MAILSTREAM *
+carmel2_open (stream)
+ MAILSTREAM *stream;
+{
+ char tmp[MAILTMPLEN];
+ struct hostent *host_name;
+
+ if(!stream) return &carmel2proto; /* Must be a OP_PROTOTYPE call */
+
+ /* close old file if stream being recycled */
+ if (LOCAL) {
+ carmel2_close (stream); /* dump and save the changes */
+ stream->dtb = &carmel2driver; /* reattach this driver */
+ mail_free_cache (stream); /* clean up cache */
+ }
+
+ mailcache = carmel2_cache;
+
+ /* Allocate local stream */
+ stream->local = fs_get (sizeof (CARMEL2LOCAL));
+
+ stream->readonly = 1; /* Read-only till more work is done */
+
+ LOCAL->msg_buf = NULL;
+ LOCAL->msg_buf_size = 0;
+ LOCAL->buffered_file = NULL;
+ LOCAL->msg_buf_text_start = NULL;
+ LOCAL->msg_buf_text_offset = 0;
+ LOCAL->stdio_buf = NULL;
+ stream->msgno = -1;
+ stream->env = NULL;
+ stream->body = NULL;
+ stream->scache = 1;
+ LOCAL->calc_paths = carmel2_calc_paths;
+ LOCAL->aux_copy = NULL;
+ LOCAL->new_file_on_copy = 1;
+
+ if(carmel_open2(stream,
+ (*(LOCAL->calc_paths))(CalcPathCarmel2Index, stream->mailbox,0)) < 0)
+ return(NULL);
+
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,stream->recent);
+
+ return(stream);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Do the real work of opening a Carmel folder.
+
+Args: stream -- The mail stream being opened
+ file -- Path name of the actual Carmel index file
+
+Returns: -1 if the open fails
+ 0 if it succeeds
+
+This is shared between the Carmel driver and the Pod driver.
+
+Here, the status, size and date (fast info) of a message entry
+is read out of the index file into the MESSAGECACHE structures.
+To make the reading efficient these items are at the beginning
+of each entry and there is a byte offset to the next entry.
+If the byte offset is wrong (as detected by looking for the
+start of message string) then the index is read line by line
+until it synchs up. This allows manual editing of the index.
+However, the first two lines of an index entry cannot be
+edited because mail_check() writes them in place. If these
+lines have been edited it is detected here and the folder is
+deemed corrupt.
+ ---*/
+carmel_open2(stream, file)
+ MAILSTREAM *stream;
+ char *file;
+{
+ long file_pos, recent;
+ MESSAGECACHE *mc;
+ struct stat sb;
+
+ if(stat(file, &sb) < 0) {
+ sprintf(carmel_error_buf, "Can't open mailbox: %s", strerror(errno));
+ mm_log(carmel_error_buf, ERROR);
+ return(-1);
+ }
+
+ LOCAL->index_stream = fopen(file, stream->readonly ? "r" : "r+");
+ LOCAL->stdio_buf = fs_get(CARMEL2_INDEX_BUF_SIZE);
+ setbuffer(LOCAL->index_stream, LOCAL->stdio_buf, CARMEL2_INDEX_BUF_SIZE);
+ if(LOCAL->index_stream == NULL) {
+ sprintf(carmel_error_buf, "Can't open mailbox: %s", strerror(errno));
+ mm_log(carmel_error_buf, ERROR);
+ return(-1);
+ }
+
+ recent = 0;
+ stream->nmsgs = 0;
+ LOCAL->cache_size = 0;
+ LOCAL->mc_blocks = NULL;
+ LOCAL->index_size = sb.st_size;
+
+ /*---- Read line with magic number, which we already checked ------*/
+ if(fgets(carmel_20k_buf,sizeof(carmel_20k_buf),LOCAL->index_stream)==NULL){
+ fclose(LOCAL->index_stream);
+ mm_log("Mailbox is corrupt. Open failed", ERROR);
+ return(-1);
+ }
+
+ file_pos = ftell(LOCAL->index_stream);
+ if(carmel2_parse_mail(stream, file_pos) < 0) {
+ fclose(LOCAL->index_stream);
+ mm_log("Mailbox is corrupt. Open failed", ERROR);
+ return(-1);
+ }
+
+ /* Bug, this doesn't really do the recent correctly */
+
+ stream->recent = recent;
+ return(0);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Carmel mail close
+
+Args: stream -- stream to close
+
+ ----*/
+
+void
+carmel2_close (stream)
+ MAILSTREAM *stream;
+{
+ if (LOCAL) { /* only if a file is open */
+ carmel2_check (stream); /* dump final checkpoint */
+ if(LOCAL->msg_buf)
+ fs_give ((void **) &LOCAL->msg_buf);
+ if(LOCAL->stdio_buf)
+ fs_give ((void **) &LOCAL->stdio_buf);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+
+
+/*----------------------------------------------------------------------
+ Carmel mail fetch fast information.
+
+This is a no-op because the data is always available as it is read in to
+the message cache blocks when the folder is opened
+----*/
+void
+carmel2_fetchfast (stream, sequence)
+ MAILSTREAM *stream;
+ char *sequence;
+{
+ return;
+}
+
+
+
+/*----------------------------------------------------------------------
+ Carmel2 mail fetch flags.
+
+This is a no-op because the data is always available as it is read in to
+the message cache blocks when the folder is opened
+----*/
+void
+carmel2_fetchflags(stream, sequence)
+ MAILSTREAM *stream;
+ char *sequence;
+{
+ return; /* no-op for local mail */
+}
+
+
+
+/*----------------------------------------------------------------------
+ Carmel mail fetch message structure
+ Args: stream -- stream to get structure for
+ msgno -- Message number to fetch data for
+ body -- Pointer to place to return body strucuture, may be NULL
+
+If the request is the for the same msgno as last time, the saved copy
+of the envelope and/or body structure is returned.
+
+To get the envelope the Carmel index file itself must be read and parsed,
+but this is fast because it is easy to parse (by design) and the amount of
+data is small.
+
+To get the body, the whole message is read into memory and then parsed
+by routines called from here. Doing this for a large message can be slow,
+so it is best not to request the body if it is not needed. (body == NULL)
+ ----*/
+
+ENVELOPE *
+carmel2_fetchstructure (stream, msgno, body)
+ MAILSTREAM *stream;
+ long msgno;
+ BODY **body;
+{
+ MESSAGECACHE *mc;
+ ENVELOPE **env;
+ BODY **b;
+
+ env = &stream->env;
+ b = &stream->body;
+
+ if (msgno != stream->msgno){
+ /* flush old poop if a different message */
+ mail_free_envelope (env);
+ mail_free_body (b);
+ }
+ stream->msgno = msgno;
+
+ mc = MC(msgno);
+
+ if(*env == NULL) {
+ *env = mail_newenvelope();
+
+ fseek(LOCAL->index_stream, mc->data1, 0);
+ fgets(carmel_20k_buf, sizeof(carmel_20k_buf), LOCAL->index_stream);
+ if(strncmp(carmel_20k_buf, carmel2_s_o_m, carmel2_s_o_m_len))
+ return(NULL); /* Oh ooo */
+
+ carmel2_date2string(carmel_20k_buf, mc);
+ (*env)->date = cpystr(carmel_20k_buf);
+
+ while(fgets(carmel_20k_buf, sizeof(carmel_20k_buf),
+ LOCAL->index_stream) != NULL &&
+ strncmp(carmel_20k_buf, carmel2_s_o_m, carmel2_s_o_m_len)) {
+ carmel_20k_buf[strlen(carmel_20k_buf) - 1] = '\0';
+ switch(*carmel_20k_buf) {
+ case 'F':
+ (*env)->from = carmel2_parse_address(carmel_20k_buf,
+ (*env)->from,
+ mylocalhost());
+ break;
+ case 'T':
+ (*env)->to = carmel2_parse_address(carmel_20k_buf,
+ (*env)->to,
+ mylocalhost());
+ break;
+ case 'C':
+ (*env)->cc = carmel2_parse_address(carmel_20k_buf,
+ (*env)->cc,
+ mylocalhost());
+ break;
+ case 'B':
+ (*env)->bcc = carmel2_parse_address(carmel_20k_buf,
+ (*env)->bcc,
+ mylocalhost());
+ break;
+ case 'E':
+ (*env)->sender = carmel2_parse_address(carmel_20k_buf,
+ (*env)->sender,
+ mylocalhost());
+ break;
+ case 'R':
+ (*env)->reply_to = carmel2_parse_address(carmel_20k_buf,
+ (*env)->reply_to,
+ mylocalhost());
+ break;
+ case 'H':
+ (*env)->return_path =carmel2_parse_address(carmel_20k_buf,
+ (*env)->return_path,
+ mylocalhost());
+ break;
+ case 'J':
+ (*env)->subject = cpystr(carmel_20k_buf+1);
+ break;
+ case 'I':
+ (*env)->message_id = cpystr(carmel_20k_buf+1);
+ break;
+ case 'L':
+ (*env)->in_reply_to = cpystr(carmel_20k_buf+1);
+ break;
+ case 'N':
+ (*env)->newsgroups = cpystr(carmel_20k_buf+1);
+ break;
+ case 'r':
+ (*env)->remail = cpystr(carmel_20k_buf+1);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if(body != NULL) {
+ if(*b == NULL) {
+ /* Have to fetch the body structure too */
+ *b = carmel2_bodystruct(stream, mc);
+ }
+ *body = *b;
+ }
+
+ return(*env); /* return the envelope */
+}
+
+
+
+
+/*----------------------------------------------------------------------
+ Carmel mail fetch message header
+
+Args: stream --
+ msgno
+
+Returns: pointer to text of mail header. Returned string is null terminated
+ and consists of internel storage. It will be valid till the next
+ call to mail_fetchheader() or mail_fetchtext(). An empty
+ string is returned if the fetch fails.
+ ----*/
+
+char *
+carmel2_fetchheader (stream, msgno)
+ MAILSTREAM *stream;
+ long msgno;
+{
+ char *header;
+ MESSAGECACHE *mc;
+
+ mc = MC(msgno);
+ header = carmel2_readmsg(stream, 1, 0, mc->data2);
+
+ return(header == NULL ? "" : header);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Carmel mail fetch message text (only)
+
+Args: stream --
+ msgno
+
+Returns: pointer to text of mail message. Returned string is null terminated
+ and consists of internel storage. It will be valid till the next
+ call to mail_fetchheader() or mail_fetchtext(). An empty
+ string is returned if the fetch fails.
+ ----*/
+
+char *
+carmel2_fetchtext(stream, msgno)
+ MAILSTREAM *stream;
+ long msgno;
+{
+ MESSAGECACHE *mc;
+ char *m;
+
+ mc = MC(msgno);
+
+ if (!mc->seen) { /* if message not seen before */
+ mc->seen = T; /* mark as seen */
+ LOCAL->dirty = T; /* and that stream is now dirty */
+ }
+
+ m = carmel2_readmsg(stream, 0, mc->rfc822_size, mc->data2);
+
+ return(m);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Fetch MIME body parts
+
+Args: stream
+ msgno
+ section -- string section number spec. i.e. "1.3.4"
+ len -- pointer to return len in
+
+Returns: The contents of the body part, or NULL
+ ---*/
+
+char *
+carmel2_fetchbody (stream, msgno, section, len)
+ MAILSTREAM *stream;
+ long msgno;
+ char *section;
+ unsigned long *len;
+{
+ char *base;
+ BODY *b;
+ PART *part;
+ int part_num;
+ long offset;
+ MESSAGECACHE *mc;
+
+ if (carmel2_fetchstructure(stream, msgno, &b) == NULL || b == NULL)
+ return(NULL);
+
+ if(section == NULL || *section == '\0')
+ return(NULL);
+
+ part_num = strtol(section, &section, 10);
+ if(part_num <= 0)
+ return(NULL);
+
+ base = carmel2_fetchtext(stream, msgno);
+ if(base == NULL)
+ base = "";
+ offset = 0;
+
+ do { /* until find desired body part */
+ if (b->type == TYPEMULTIPART) {
+ part = b->contents.part; /* yes, find desired part */
+ while (--part_num && (part = part->next));
+ if (!part)
+ return (NIL); /* bad specifier */
+
+ /* Found part, get ready to go further */
+ b = &part->body;
+ if(b->type == TYPEMULTIPART && *section == '\0')
+ return(NULL); /* Ran out of section numbers, needed more */
+
+ offset = part->offset; /* and its offset */
+
+ } else if (part_num != 1) {
+ return NIL; /* Not Multipart, better be part 1 */
+ }
+
+ /* need to go down further? */
+
+ if(*section == 0) {
+ break;
+ } else {
+ switch (b->type) {
+ case TYPEMESSAGE: /* embedded message, calculate new base */
+ offset = b->contents.msg.offset;
+ b = b->contents.msg.body;
+ /* got its body, drop into multipart case*/
+
+ case TYPEMULTIPART: /* multipart, get next section */
+ if ((*section++ == '.') &&
+ (part_num = strtol (section, &section,10)) > 0)
+ break; /* Found the part */
+
+ default: /* bogus subpart specification */
+ return(NULL);
+ }
+ }
+ } while (part_num);
+
+ mc = MC(msgno);
+ /* lose if body bogus */
+ if ((!b) || b->type == TYPEMULTIPART)
+ return(NULL);
+
+ if (!mc->seen) { /* if message not seen before */
+ mc->seen = T; /* mark as seen */
+ LOCAL->dirty = T; /* and that stream is now dirty */
+ }
+
+ *len = b->size.ibytes;
+ return(base + offset);
+}
+
+
+
+/*----------------------------------------------------------------------
+ * Carmel mail set flag
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ */
+
+void
+carmel2_setflag (stream,sequence,flag)
+ MAILSTREAM *stream;
+ char *sequence;
+ char *flag;
+{
+ MESSAGECACHE *elt;
+ long i;
+ short f = carmel2_getflags (stream,flag);
+ if (!f) return; /* no-op if no flags to modify */
+ /* get sequence and loop on it */
+ if (mail_sequence (stream,sequence))
+ for (i = 1; i <= stream->nmsgs; i++){
+ elt = MC(i);
+ if (elt->sequence) {
+ /* set all requested flags */
+ if (f&fSEEN) elt->seen = T;
+ if (f&fDELETED) elt->deleted = T;
+ if (f&fFLAGGED) elt->flagged = T;
+ if (f&fANSWERED) elt->answered = T;
+ /* Could be more creative about keeping track to see if something
+ really changed */
+ LOCAL->dirty = T;
+ }
+ }
+}
+
+
+
+/*----------------------------------------------------------------------
+ * Carmel mail clear flag
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ */
+
+void
+carmel2_clearflag (stream,sequence,flag)
+ MAILSTREAM *stream;
+ char *sequence;
+ char *flag;
+{
+ MESSAGECACHE *elt;
+ long i = stream->nmsgs;
+ short f = carmel2_getflags (stream,flag);
+ if (!f) return; /* no-op if no flags to modify */
+ /* get sequence and loop on it */
+ if (mail_sequence (stream,sequence))
+ for(i = 1; i <= stream->nmsgs; i++) {
+ elt = MC(i);
+ if (elt->sequence) {
+ /* clear all requested flags */
+ if (f&fSEEN) elt->seen = NIL;
+ if (f&fDELETED) elt->deleted = NIL;
+ if (f&fFLAGGED) elt->flagged = NIL;
+ if (f&fANSWERED) elt->answered = NIL;
+ /* clearing either seen or deleted does this */
+ /* Could be more creative about keeping track to see if something
+ really changed */
+ LOCAL->dirty = T; /* mark stream as dirty */
+ }
+ }
+}
+
+
+
+/* Carmel mail search for messages
+ * Accepts: MAIL stream
+ * search criteria
+ */
+
+void
+carmel2_search (stream,criteria)
+ MAILSTREAM *stream;
+ char *criteria;
+{
+ long i,n;
+ char *d;
+ search_t f;
+
+ /* initially all searched */
+ for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched = T;
+ /* get first criterion */
+ if (criteria && (criteria = strtok (criteria," "))) {
+ /* for each criterion */
+ for (; criteria; (criteria = strtok (NIL," "))) {
+ f = NIL; d = NIL; n = 0; /* init then scan the criterion */
+ switch (*ucase (criteria)) {
+ case 'A': /* possible ALL, ANSWERED */
+ if (!strcmp (criteria+1,"LL")) f = carmel2_search_all;
+ else if (!strcmp (criteria+1,"NSWERED")) f = carmel2_search_answered;
+ break;
+ case 'B': /* possible BCC, BEFORE, BODY */
+ if (!strcmp (criteria+1,"CC"))
+ f = carmel2_search_string (carmel2_search_bcc,&d,&n);
+ else if (!strcmp (criteria+1,"EFORE"))
+ f = carmel2_search_date (carmel2_search_before,&n);
+ else if (!strcmp (criteria+1,"ODY"))
+ f = carmel2_search_string (carmel2_search_body,&d,&n);
+ break;
+ case 'C': /* possible CC */
+ if (!strcmp (criteria+1,"C"))
+ f = carmel2_search_string (carmel2_search_cc,&d,&n);
+ break;
+ case 'D': /* possible DELETED */
+ if (!strcmp (criteria+1,"ELETED")) f = carmel2_search_deleted;
+ break;
+ case 'F': /* possible FLAGGED, FROM */
+ if (!strcmp (criteria+1,"LAGGED")) f = carmel2_search_flagged;
+ else if (!strcmp (criteria+1,"ROM"))
+ f = carmel2_search_string (carmel2_search_from,&d,&n);
+ break;
+ case 'K': /* possible KEYWORD */
+ if (!strcmp (criteria+1,"EYWORD"))
+ f = carmel2_search_flag (carmel2_search_keyword,&d);
+ break;
+ case 'N': /* possible NEW */
+ if (!strcmp (criteria+1,"EW")) f = carmel2_search_new;
+ break;
+
+ case 'O': /* possible OLD, ON */
+ if (!strcmp (criteria+1,"LD")) f = carmel2_search_old;
+ else if (!strcmp (criteria+1,"N"))
+ f = carmel2_search_date (carmel2_search_on,&n);
+ break;
+ case 'R': /* possible RECENT */
+ if (!strcmp (criteria+1,"ECENT")) f = carmel2_search_recent;
+ break;
+ case 'S': /* possible SEEN, SINCE, SUBJECT */
+ if (!strcmp (criteria+1,"EEN")) f = carmel2_search_seen;
+ else if (!strcmp (criteria+1,"INCE"))
+ f = carmel2_search_date (carmel2_search_since,&n);
+ else if (!strcmp (criteria+1,"UBJECT"))
+ f = carmel2_search_string (carmel2_search_subject,&d,&n);
+ break;
+ case 'T': /* possible TEXT, TO */
+ if (!strcmp (criteria+1,"EXT"))
+ f = carmel2_search_string (carmel2_search_text,&d,&n);
+ else if (!strcmp (criteria+1,"O"))
+ f = carmel2_search_string (carmel2_search_to,&d,&n);
+ break;
+ case 'U': /* possible UN* */
+ if (criteria[1] == 'N') {
+ if (!strcmp (criteria+2,"ANSWERED")) f = carmel2_search_unanswered;
+ else if (!strcmp (criteria+2,"DELETED")) f = carmel2_search_undeleted;
+ else if (!strcmp (criteria+2,"FLAGGED")) f = carmel2_search_unflagged;
+ else if (!strcmp (criteria+2,"KEYWORD"))
+ f = carmel2_search_flag (carmel2_search_unkeyword,&d);
+ else if (!strcmp (criteria+2,"SEEN")) f = carmel2_search_unseen;
+ }
+ break;
+ default: /* we will barf below */
+ break;
+ }
+ if (!f) { /* if can't determine any criteria */
+ sprintf(carmel_error_buf,"Unknown search criterion: %s",criteria);
+ mm_log (carmel_error_buf,ERROR);
+ return;
+ }
+ /* run the search criterion */
+ for (i = 1; i <= stream->nmsgs; ++i)
+ if (mail_elt (stream,i)->searched && !(*f) (stream,i,d,n))
+ mail_elt (stream,i)->searched = NIL;
+ }
+ /* report search results to main program */
+ for (i = 1; i <= stream->nmsgs; ++i)
+ if (mail_elt (stream,i)->searched) mail_searched (stream,i);
+ }
+}
+
+
+
+
+/*----------------------------------------------------------------------
+ * Carmel mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long
+carmel2_ping (stream)
+ MAILSTREAM *stream;
+{
+ struct stat sb;
+ char path[1000], *mailfile;
+#ifdef TESTING
+ char debug_buf[1000];
+#endif
+
+ if(!stream->readonly &&
+ carmel2_update_lock(stream->local, stream->mailbox, READ_LOCK) < 0) {
+ /* Yuck! Someone messed up our lock file, this stream is hosed */
+ mm_log("Mailbox updated unexpectedly! Mailbox closed", ERROR);
+ stream->readonly = 1;
+ return NIL;
+ }
+
+ /*--- First check to see if the Carmel index grew ----*/
+ /* BUG, will this really work on NFS since the file is held open? */
+ stat((*LOCAL->calc_paths)(CalcPathCarmel2Index, stream->mailbox, 0), &sb);
+ if(sb.st_size > LOCAL->index_size) {
+#ifdef TESTING
+ mm_log("!!!!! Carmel index grew", NIL);
+#endif
+ /* Pull in the new mail.... */
+#ifdef MAY_NEED_FOR_NFS
+ /*---- First close and reopen the mail file -----*/
+ fclose(LOCAL->index_stream);
+ LOCAL->index_stream = fopen((*LOCAL->calc_paths)(CalcPathCarmel2Index,
+ stream->mailbox, 0),
+ stream->readonly ? "r" : "r+");
+ if(LOCAL->index_stream == NULL) {
+ mm_log("Mailbox stolen. Mailbox closed", ERROR);
+ stream->readonly = 1;
+ return NIL;
+ }
+#endif
+ if(carmel2_parse_mail(stream, LOCAL->index_size) < 0) {
+ mm_log("Mailbox corrupted. Mailbox closed", ERROR);
+ stream->readonly = 1;
+ return NIL;
+ }
+ LOCAL->index_size = sb.st_size;
+ }
+
+ if(sb.st_size < LOCAL->index_size) {
+ /* Something bad happened, mail box shrunk on us, shutdown */
+ stream->readonly = 1;
+ mm_log("Mailbox shrank unexpectedly! Mailbox closed", ERROR);
+ return NIL;
+ }
+
+#ifdef TESTING
+ sprintf(debug_buf, "!!!!! Mailbox:\"%s\" pretty_mailbox:\"%s\"",
+ stream->mailbox, carmel_pretty_mailbox(stream->mailbox));
+ mm_log(debug_buf, NIL);
+ sprintf(debug_buf, "!!!! Readonly:%d, Carmel:%d", stream->readonly,
+ LOCAL->carmel);
+ mm_log(debug_buf, NIL);
+#endif
+
+ if(!stream->readonly &&
+ ((LOCAL->carmel &&
+ strcmp(carmel_pretty_mailbox(stream->mailbox), "MAIL") == 0) ||
+ strucmp2(carmel_pretty_mailbox(stream->mailbox), "inbox") == 0)) {
+ /* If it's the inbox we've got to check the spool file */
+ mailfile = getenv("MAIL") == NULL ? MAILFILE : getenv("MAIL");
+ sprintf(path, mailfile, myhomedir());
+#ifdef TESTING
+ sprintf(debug_buf, "!!!!! Checking spool mail\"%s\"", path);
+ mm_log(debug_buf, NIL);
+#endif
+ if(stat(path, &sb) < 0)
+ return(T); /* No new mail...*/
+ if(sb.st_size > 0) {
+ mm_critical(stream);
+ if(carmel2_lock(stream->local, stream->mailbox, WRITE_LOCK) < 0) {
+ mm_nocritical(stream);
+ return(T);
+ }
+ mm_log("!!!! Inbox locked, sucking in mail", NIL);
+ carmel2_spool_mail(stream, path, stream->mailbox, 1); /*go get it*/
+ carmel2_unlock(stream->local, stream->mailbox, WRITE_LOCK);
+ mm_nocritical(stream);
+ stat((*LOCAL->calc_paths)(CalcPathCarmel2Index, stream->mailbox,0),
+ &sb);
+ LOCAL->index_size = sb.st_size;
+ }
+ }
+
+ return T;
+}
+
+
+
+
+
+/*----------------------------------------------------------------------
+ This checks for new mail and checkpoints the mail file
+
+Args -- The stream to check point
+
+ ----*/
+void
+carmel2_check(stream)
+ MAILSTREAM *stream;
+{
+ (void)carmel2_check2(stream);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Do actual work of a check on carmel2 index, returning status
+
+Returns: 0 if no checkpoint was done, 1 if it was done.
+ ----*/
+carmel2_check2(stream)
+ MAILSTREAM *stream;
+{
+ int msgno;
+ MESSAGECACHE *mc;
+
+ if(stream->readonly || LOCAL == NULL)
+ return(0); /* Nothing to do in readonly or closed case */
+
+ carmel2_ping(stream); /* check for new mail (Ping always checks) */
+
+ if(!LOCAL->dirty)
+ return(0); /* Nothing to do */
+
+ mm_critical(stream);
+ if(carmel2_lock(stream->local, stream->mailbox, WRITE_LOCK) < 0) {
+ mm_nocritical(stream);
+ return(0); /* Couldn't get a write lock, nothing we can do */
+ }
+
+ for(msgno = 1; msgno <= stream->nmsgs; msgno++) {
+ mc = MC(msgno);
+ fseek(LOCAL->index_stream, mc->data1 + carmel2_s_o_m_len + 13, 0);
+ if(fprintf(LOCAL->index_stream,
+ "U%c%c%c%c%c____________________________\n",
+ mc->flagged ? 'F' : 'f',
+ mc->recent ? 'R' : 'r',
+ mc->answered ? 'A' : 'a',
+ mc->deleted ? 'D' : 'd',
+ mc->seen ? 'S' : 's') == EOF) {
+ /* BUG .. Need some error check here */
+ }
+ }
+ fflush(LOCAL->index_stream);
+
+ carmel2_unlock(stream->local, stream->mailbox, WRITE_LOCK);
+ mm_nocritical(stream);
+ mm_log("Check completed", NIL);
+ return(1);
+}
+
+
+
+
+
+/*----------------------------------------------------------------------
+ Carmel2 mail expunge mailbox
+
+Args: stream -- mail stream to expunge
+
+ ----*/
+
+void carmel2_expunge (stream)
+ MAILSTREAM *stream;
+{
+ char *index_file;
+ long msgno, new_msgno;
+ FILE *new_index;
+ MESSAGECACHE *mc, *new_mc;
+ ENVELOPE *envelope;
+ int save_e;
+ struct stat sb;
+
+ if (stream->readonly) {
+ if(!stream->silent)
+ mm_log ("Expunge ignored on readonly mailbox",NIL);
+ return;
+ }
+
+ mm_critical(stream);
+ carmel2_lock(stream->local, stream->mailbox, WRITE_LOCK);
+
+ strcpy(carmel_path_buf,
+ (*LOCAL->calc_paths)(CalcPathCarmel2Expunge, stream->mailbox, 0));
+
+ new_index = fopen(carmel_path_buf, "w");
+ if(new_index == NULL) {
+ goto fail;
+ }
+ if(fprintf(new_index, carmel2_s_o_f) == EOF)
+ goto fail;
+
+ new_msgno = 1;
+ for(msgno = 1; msgno <= stream->nmsgs; msgno++) {
+ mc = MC(msgno);
+ if(mc->deleted) {
+ mm_expunged(stream, new_msgno);
+ continue;
+ }
+
+ if(msgno != new_msgno) {
+ new_mc = MC(new_msgno);
+ *new_mc = *mc;
+ new_mc->msgno = new_msgno;
+ mc = new_mc;
+ }
+
+ envelope = carmel2_fetchstructure(stream, msgno, NULL);
+ if(envelope == NULL)
+ goto fail;
+
+ /* get this after envelope to offset is still valid in old file */
+ mc->data1 = ftell(new_index);
+
+ if(carmel2_write_index(envelope, mc, new_index) < 0)
+ goto fail;
+ new_msgno++;
+ }
+
+ index_file = (*LOCAL->calc_paths)(CalcPathCarmel2Index, stream->mailbox, 0);
+
+ /*--- Close it to make sure bits are really on disk across NFS. ---*/
+ if(fclose(new_index) != EOF) {
+ /*--- copy of index was a success, rename it ---*/
+ unlink(index_file);
+ link(carmel_path_buf, index_file);
+
+ /*--- Save the mail index size ----*/
+ stat(index_file, &sb);
+ LOCAL->index_size = sb.st_size;
+
+ stream->nmsgs = new_msgno - 1;
+ mm_exists(stream, stream->nmsgs);
+
+ fclose(LOCAL->index_stream);
+ LOCAL->index_stream = fopen(index_file, "r+");
+ }
+ unlink(carmel_path_buf);
+ carmel2_unlock(stream->local, stream->mailbox, WRITE_LOCK);
+ mm_nocritical(stream);
+ return;
+
+ fail:
+ save_e = errno;
+ if(new_index != NULL)
+ fclose(new_index);
+ unlink(carmel_path_buf);
+ sprintf(carmel_error_buf, "Expunge failed: %s", strerror(save_e));
+ mm_log(carmel_error_buf, WARN);
+ carmel2_unlock(stream->local, stream->mailbox, WRITE_LOCK);
+ mm_nocritical(stream);
+ return;
+}
+
+
+
+/*----------------------------------------------------------------------
+ Carmel2 mail copy message(s)
+
+ Args: stream - mail stream
+ sequence - message sequence
+ mailbox - destination mailbox, FQN
+
+ Returns: T if copy successful, else NIL
+ ----*/
+long
+carmel2_copy(stream, sequence, mailbox)
+ MAILSTREAM *stream;
+ char *sequence;
+ char *mailbox;
+{
+ ENVELOPE *e;
+ MESSAGECACHE *mc, new_mc;
+ long msgno, file_no, file_pos;
+ int fail;
+ char *file_name, *line;
+ FILE *dest_stream;
+ struct stat sb;
+
+ if (!mail_sequence (stream,sequence)) /* get sequence to do */
+ return(NIL);
+
+ /*--- Get the file open (creating if needed) first ----*/
+ file_name = (*LOCAL->calc_paths)(CalcPathCarmel2Index, mailbox, 0, 0);
+ if(file_name == NULL)
+ return(NIL);
+
+ if(stat(file_name, &sb) < 0) {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before copy", NIL);
+ return(NIL);
+ }
+
+ dest_stream = fopen(file_name, "a+");
+ if(dest_stream == NULL) {
+ sprintf(carmel_error_buf, "Can't copy message to %s: %s",
+ mailbox, strerror(errno));
+ mm_log(carmel_error_buf, ERROR);
+ return(NIL);
+ }
+
+ mm_critical(stream);
+
+ if(carmel2_lock(stream->local, mailbox, WRITE_LOCK) < 0) {
+ mm_nocritical(stream);
+ sprintf(carmel_error_buf,
+ "Mailbox %s locked, can't copy messages to it",
+ mailbox);
+ mm_log(carmel_error_buf, ERROR);
+ fclose(dest_stream);
+ return(NIL);
+ }
+
+
+ /*--- Get the destination Carmel index open ----*/
+ file_pos = ftell(dest_stream);
+ fail = 0;
+
+
+ for(msgno = 1; msgno <= stream->nmsgs; msgno++) {
+ mc = MC(msgno);
+ if(!mc->sequence)
+ continue;
+
+ new_mc = *mc;
+
+ if(LOCAL->new_file_on_copy) {
+ new_mc.data2 = carmel2_copy_msg_file(stream, mc->data2, mailbox);
+ if((long)new_mc.data2 < 0) {
+ fail = 1;
+ break;
+ }
+ }
+
+ e = carmel2_fetchstructure(stream, msgno, NULL);
+ if(carmel2_write_index(e, &new_mc, dest_stream) < 0) {
+ fail = 1;
+ break;
+ }
+
+ if(LOCAL->carmel && LOCAL->aux_copy != NULL) {
+ if((*LOCAL->aux_copy)(stream->local, mailbox, e, &new_mc)) {
+ fail = 1;
+ break;
+ }
+ }
+ }
+
+ if(fail) {
+ ftruncate(fileno(dest_stream), file_pos);
+ }
+
+ fclose(dest_stream);
+
+ carmel2_unlock(stream->local, mailbox, WRITE_LOCK);
+
+ mm_nocritical(stream);
+
+ return(fail ? NIL : T);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Carmel2 mail move message(s)
+
+
+ Returns: T if move successful, else NIL
+ ----*/
+
+long
+carmel2_move (stream,sequence,mailbox)
+ MAILSTREAM *stream;
+ char *sequence;
+ char *mailbox;
+{
+ long i;
+ MESSAGECACHE *elt;
+
+ if (!(mail_sequence (stream,sequence) &&
+ carmel2_copy (stream,sequence,mailbox))) return NIL;
+ /* delete all requested messages */
+ for (i = 1; i <= stream->nmsgs; i++)
+ elt = MC(i);
+ if (elt->sequence) {
+ elt->deleted = T; /* mark message deleted */
+ LOCAL->dirty = T; /* mark mailbox as dirty */
+ }
+ return T;
+}
+
+
+
+/*----------------------------------------------------------------------
+ * Carmel2 garbage collect stream
+ * Accepts: Mail stream
+ * garbage collection flags
+ */
+
+void carmel2_gc (stream, gcflags)
+ MAILSTREAM *stream;
+ long gcflags;
+{
+ /* No garbage collection in Carmel2 driver, not much to collect */
+}
+
+
+
+/*----------------------------------------------------------------------
+ Handle MessageCache for carmel2 mail driver
+
+Args: stream --
+ msgno -- message number
+ op -- operation code
+
+The carmel2 format keeps MESSAGECACHE entries in core for all messages
+in the open mail folder so there isn't any cache flushing and rereading
+that has to go on.
+ ----*/
+void *
+carmel2_cache(stream, msgno, op)
+ MAILSTREAM *stream;
+ long msgno;
+ long op;
+{
+ /* It's a carmel driver if first 6 letters of name are carmel */
+ if(stream->dtb == NULL)
+ return(0);
+
+ if(struncmp2(stream->dtb->name, "carmel", 6) == 0) {
+ if(op == CH_MAKEELT)
+ return(MC(msgno));
+ else
+ return(0);
+ }
+
+ /* Not a carmel2 or carmel driver, call the standard function. This works
+ as long as there is only one other driver since we know it must be
+ mm_cache().
+ */
+ return(mm_cache(stream, msgno, op));
+}
+
+
+
+/*----------------------------------------------------------------------
+ Append a message to a mailbox
+
+Args: mailbox -- The message to append the mail folder too
+ message -- Message header and text in rfc822 \r\n format to append
+
+Returns: T if all went OK, NIL if not
+ ----*/
+long
+carmel2_append(stream, mailbox, flags, date, message)
+ MAILSTREAM *stream;
+ char *mailbox, *flags, *date;
+ STRING *message;
+{
+ CARMEL2LOCAL local;
+
+ /*---- A fake local data structure to pass to other functions---*/
+ local.calc_paths = carmel2_calc_paths;
+ local.carmel = 0;
+ local.aux_copy = NULL;
+
+ return(carmel2_append2(stream, &local, mailbox, flags, date, message));
+}
+
+
+
+
+/*----------------------------------------------------------------------
+ Fetch the body structure for a camel message
+
+Args: stream -- MAILSTREAM
+ mc -- The incore message cache entry for the message
+
+Returns: body structure
+
+Note, the envelope that results from the parse here is ignored. Only
+the envelope from the index is actually used in the Carmel2 format.
+ ----*/
+static BODY *
+carmel2_bodystruct(stream, mc)
+ MESSAGECACHE *mc;
+ MAILSTREAM *stream;
+{
+ char *header, *text, *file, *p;
+ ENVELOPE *e_place_holder;
+ BODY *b;
+ STRING string_struct;
+
+ header = carmel2_readmsg(stream, 1, mc->rfc822_size, mc->data2);
+ if(header == NULL)
+ return(NULL);
+
+ text = carmel2_readmsg(stream, 0, mc->rfc822_size, mc->data2);
+ if(text == NULL)
+ return(NULL);
+
+ INIT(&string_struct, mail_string, (void *)text, strlen(text));
+ rfc822_parse_msg(&e_place_holder, &b, header, strlen(header),
+ &string_struct, mylocalhost(), carmel_20k_buf);
+
+ mail_free_envelope(&e_place_holder);
+
+#ifdef BWC
+ /* If there's no content type in the header and it's the carmel
+ driver at the BWC set the type X-BWC-Glyph
+ */
+ for(p = header; *p; p++)
+ if(*p=='\n' && (*(p+1)=='C' || *(p+1)=='c') &&
+ struncmp2(p+1,"content-type:", 13) == 0)
+ break;
+
+ if(!*p && LOCAL->carmel && /* Carmel, not Carmel2 */
+ b->type == TYPETEXT && /* Type text */
+ (b->subtype == NULL || strcmp(b->subtype,"PLAIN") == 0) &&
+ ((int)mc->year) + 1969 < 1994) {
+ /* Most mail in a pod mail store is in the BWC-Glyph character
+ set, but there is no tag in the data on disk, so we fake it here.
+ We expect after 1994 all mail generated in BWC-Glyph format
+ will have proper tags!
+ */
+ b->subtype = cpystr("X-BWC-Glyph");
+ }
+#endif
+
+ return(b);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Parse an address in a Carmel2 format mail index
+
+Args: line -- The line from the index to parse
+ addr -- The address list to add to (if there is one)
+
+Returns: address list
+ ----*/
+static ADDRESS *
+carmel2_parse_address(line, addr, localhost)
+ char *line, *localhost;
+ ADDRESS *addr;
+{
+ ADDRESS *a;
+
+ if(addr == NULL) {
+ addr = mail_newaddr();
+ a = addr;
+ } else {
+ for(a = addr; a!= NULL && a->next != NULL; a = a->next);
+ a->next = mail_newaddr();
+ a = a->next;
+ }
+
+ line++; /* Skip the tag chacater */
+ a->personal = carmel2_parse_addr_field(&line);
+ a->mailbox = carmel2_parse_addr_field(&line);
+ a->host = carmel2_parse_addr_field(&line);
+ a->adl = carmel2_parse_addr_field(&line);
+/* if(a->host == NULL)
+ a->host = cpystr(localhost); */ /* host can't be NULL */
+ /* Yes it can for Internet group syntax */
+ return(addr);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Parse the next address field out of a carmel address index entry
+
+Args: string -- pointer to pointer to string
+
+Returns: field in malloced string or NULL
+
+Input string is a bunch of substrings separated by ^A. This function scans
+for the next ^A or end of string, cuts it out and returns it. The original
+strings passed in is mangled
+----*/
+static char *
+carmel2_parse_addr_field(string)
+ char **string;
+{
+ char *p, end, *start;
+
+ start = p = *string;
+ while(*p > '\001') /* Find end of sub string or string */
+ p++;
+ if((p - *string) == 0) {
+ if(*p) p++;
+ *string = p;
+ return(NULL); /* If nothing found return nothing */
+ }
+ end = *p; /* Save terminating character (^A or \0) */
+ *p = '\0';
+ if(end) /* If not end of string, get ready for next call */
+ p++;
+ *string = p; /* Return pointer to next substring */
+ return(cpystr(start));
+}
+
+
+
+
+/*----------------------------------------------------------------------
+ Write an entry into a carmel2 index
+
+Args: e -- Envelope
+ mc -- Message Cache element
+ stream -- File stream to write to
+
+Returns: 0 if OK, -1 if failed
+----*/
+carmel2_write_index(e, mc, stream)
+ ENVELOPE *e;
+ MESSAGECACHE *mc;
+ FILE *stream;
+{
+ long f_start, f_end;
+
+ f_start = ftell(stream);
+
+ if(fprintf(stream, "%s\007\001xxxxxxxxxx\n", carmel2_s_o_m) == EOF)
+ goto blah;
+ if(fprintf(stream, "U%c%c%c%c%c____________________________\n",
+ mc->flagged ? 'F' : 'f',
+ mc->recent ? 'R' : 'r',
+ mc->answered ? 'A' : 'a',
+ mc->deleted ? 'D' : 'd',
+ mc->seen ? 'S' : 's') == EOF)
+ goto blah;
+ if(fprintf(stream, "Z%d\n", mc->rfc822_size) == EOF)
+ goto blah;
+ if(fprintf(stream, "D%d\001%d\001%d\001%d\001%d\001%d\001%d\001%d\n",
+ mc->year+1969, mc->month, mc->day, mc->hours, mc->minutes,
+ mc->seconds, mc->zhours * (mc->zoccident ? 1 : -1),
+ mc->zminutes) == EOF)
+ goto blah;
+ if(fprintf(stream, "Svmail\n") == EOF)
+ goto blah;
+ if(fprintf(stream, "P%d\n",mc->data2) == EOF)
+ goto blah;
+ if(carmel2_index_address(e->to, 'T', stream) < 0)
+ goto blah;
+ if(carmel2_index_address(e->from, 'F', stream) < 0)
+ goto blah;
+ if(carmel2_index_address(e->cc, 'C', stream) < 0)
+ goto blah;
+ if(carmel2_index_address(e->bcc, 'B', stream) < 0)
+ goto blah;
+#ifdef HAVE_RESENT
+ if(carmel2_index_address(e->resent_to, 't', stream) < 0)
+ goto blah;
+ if(carmel2_index_address(e->resent_from, 'f', stream) < 0)
+ goto blah;
+ if(carmel2_index_address(e->resent_cc, 'c', stream) < 0)
+ goto blah;
+ if(carmel2_index_address(e->resent_bcc, 'b', stream) < 0)
+ goto blah;
+#endif
+ if(carmel2_index_address(e->return_path, 'H', stream) < 0)
+ goto blah;
+ if(carmel2_index_address(e->sender, 'E', stream) < 0)
+ goto blah;
+ if(carmel2_index_address(e->reply_to, 'R', stream) < 0)
+ goto blah;
+ if(e->in_reply_to != NULL)
+ if(fprintf(stream, "L%s\n", e->in_reply_to) == EOF)
+ goto blah;
+ if(e->remail != NULL)
+ if(fprintf(stream, "r%s\n", e->remail) == EOF)
+ goto blah;
+ if(e->message_id != NULL)
+ if(fprintf(stream, "I%s\n", e->message_id) == EOF)
+ goto blah;
+ if(e->newsgroups != NULL)
+ if(fprintf(stream, "N%s\n", e->newsgroups) == EOF)
+ goto blah;
+ if(e->subject != NULL)
+ if(fprintf(stream, "J%s\n", e->subject) == EOF)
+ goto blah;
+
+ /*--- figure out and write the offset ---*/
+ f_end = ftell(stream);
+ if(fseek(stream, f_start, 0) < 0)
+ goto blah;
+ if(fprintf(stream, "%s\007\001%10d\n", carmel2_s_o_m, f_end - f_start)==EOF)
+ goto blah;
+ if(fseek(stream, f_end, 0) < 0)
+ goto blah;
+
+ return(0);
+
+ blah:
+ /* Index entry is a bit of a mess now. Maybe we should try to truncate */
+ return(-1);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Write an address entry into a carmel2 index
+
+Args: addr -- addresslist
+ field -- Field character specifier
+ stream -- File stream to write to
+
+Returns 0 if OK, -1 on error writing
+ ---*/
+static
+carmel2_index_address(addr, field, stream)
+ ADDRESS *addr;
+ int field;
+ FILE *stream;
+{
+ ADDRESS *a;
+
+ for(a = addr; a != NULL; a = a->next) {
+ if(fprintf(stream, "%c%s\001%s\001%s\001%s\n",
+ field,
+ a->personal == NULL ? "" : a->personal,
+ a->mailbox == NULL ? "" : a->mailbox,
+ a->host == NULL ? "" : a->host,
+ a->adl == NULL ? "" : a->adl) == EOF)
+ return(-1);
+ }
+ return(0);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Real work of reading mail data files
+
+Args: stream
+ header_only -- return header if set, text if not
+ file_size -- The file size if known (saves a stat)
+ file -- name of the file to read
+
+Returns buffer with text stored in internel buffer. The Carmel2 format
+buffers the text of the current message and header in an internal
+buffer. The buffer never shrinks and is expanded as needed, up to a
+maximum. The text in the buffer is in CRLF format and is read in line
+by line using stdio. It is believed this will be efficient on whatever
+machine it is running on and will not use too much memory. (There's
+some extra memory used for buffering in stdio.) If a request is made
+first for only the header, then only the header will be read in. This
+is a big efficiency win when the file is large and only the header is
+needed. (In the Carmel2 format the header is genera lly not used, and
+when it is it is with the body to do a MIME parse, but the pod format
+does read the headers in to create the Carmel2 index.) An estimate is
+made of the size needed to expand the file to convert the line ends
+from LF to CRLF. The estimate alloca tes 10% extra space. If it
+doesn't fit, the whole buffer will be expanded by 50% and the whole
+read done over. When the header is read in a 30K buffer is allocated
+initially (if the buffer is smaller than that initially). The 50%
+increase is applied to the buffer when reading only the header.
+ ----*/
+
+char *
+carmel2_readmsg(stream, header_only, file_size, file_num)
+ MAILSTREAM *stream;
+ int header_only;
+ int file_num;
+ long file_size;
+{
+ FILE *msg_stream;
+ char *p, *p_end, *file_name;
+ int past_header, not_eof;
+ long max_read;
+ struct stat st;
+#define DEBUGDEBUG 1
+#ifdef DEBUGDEBUG
+ char debug_buf[500];
+#endif
+
+ file_name = (*LOCAL->calc_paths)(CalcPathCarmel2Data,
+ stream->mailbox, file_num);
+ if(file_name == NULL)
+ return(NULL); /* Just in case; should never be invalid if open */
+#ifdef DEBUGDEBUG
+ sprintf(debug_buf, "READ RQ:\"%s\" HV:\"%s\" RQ_HD:%d HV_TXT:%d\n",
+ file_name,
+ LOCAL->buffered_file == NULL ? "" : LOCAL->buffered_file,
+ header_only, LOCAL->buffered_header_and_text);
+ mm_log(debug_buf, NIL);
+#endif
+
+ /*---- Check out what we have read already -----*/
+ if(LOCAL->buffered_file != NULL &&
+ strcmp(LOCAL->buffered_file, file_name) == 0) {
+ /* The file is the same. Now have we read in the part
+ that is wanted? If so return it.
+ */
+ if(header_only || LOCAL->buffered_header_and_text) {
+ if(header_only) {
+#ifdef DEBUGDEBUG
+ mm_log("....Returning buffered header\n", NIL);
+#endif
+ return(LOCAL->msg_buf);
+ } else {
+#ifdef DEBUGDEBUG
+ mm_log("....Returning buffered text\n", NIL);
+#endif
+ return(LOCAL->msg_buf_text_start);
+ }
+ }
+ } else {
+ /*-- Different file than last time, reset a few things --*/
+ LOCAL->buffered_header_and_text = 0;
+ LOCAL->msg_buf_text_offset = 0L;
+ if(LOCAL->buffered_file != NULL)
+ fs_give((void **)&(LOCAL->buffered_file));
+ }
+
+#ifdef DEBUGDEBUG
+ mm_log("....Reading file\n", NIL);
+#endif
+
+ /*----- Open the file ------*/
+ /* Would actually be more efficient not to use stdio here */
+ msg_stream = fopen(file_name, "r");
+ if(msg_stream == NULL) {
+ strcat(file_name, ".wid");
+ msg_stream = fopen(file_name, "r");
+ if(msg_stream == NULL)
+ return(NULL);
+ }
+
+ /*---- Check the size of the file ----*/
+ if(file_size == 0 && stat(file_name, &st) >= 0)
+ file_size = st.st_size;
+
+
+ /* ------Pick an amount to read -------
+ Assume the header is less than MAX_HEADER. We don't want to
+ allocate buffer for the whole message if we are just getting
+ the header.
+ */
+ max_read = (file_size * 11) / 10;
+ past_header = 0;
+ p = LOCAL->msg_buf;
+ if(header_only) {
+ max_read = min(max_read, CARMEL_MAX_HEADER);
+ } else if(LOCAL->msg_buf_text_offset > 0) {
+ past_header = 1;
+ p = LOCAL->msg_buf_text_start;
+ fseek(msg_stream, LOCAL->msg_buf_text_offset, 0);
+ }
+ if(max_read > CARMEL_MAXMESSAGESIZE)
+ max_read = CARMEL_MAXMESSAGESIZE;
+ if(max_read == 0)
+ max_read = 1; /* Forces buffer allocation below */
+
+
+ /*----- Loop (re)reading the whole file 'till it fits ---*/
+ /* This will fit the first time for all but the
+ strangest cases.
+ */
+ do {
+ /*---- Make sure buffer is the right size ----*/
+ if(LOCAL->msg_buf_size < max_read) {
+ /* Buffer not big, enough start whole read over */
+ if(LOCAL->msg_buf != NULL)
+ fs_give((void **)&(LOCAL->msg_buf));
+ LOCAL->msg_buf = fs_get(max_read);
+ LOCAL->msg_buf_size = max_read;
+ fseek(msg_stream, 0, 0);
+ past_header = 0;
+ p = LOCAL->msg_buf;
+ }
+
+ p_end = LOCAL->msg_buf + LOCAL->msg_buf_size - 3;
+
+ while(p < p_end && (not_eof =(fgets(p, p_end-p, msg_stream) != NULL))){
+ if(*p == '\n' && !past_header) {
+ *p++ = '\r';
+ *p++ = '\n';
+ *p++ = '\0';
+ past_header = 1;
+ LOCAL->msg_buf_text_offset = ftell(msg_stream);
+ LOCAL->msg_buf_text_start = p;
+ if(header_only)
+ goto done;
+ else
+ continue;
+ }
+ p += strlen(p) - 1;
+ *p++ = '\r';
+ *p++ = '\n';
+ }
+ *p = '\0';
+ if(!not_eof)
+ goto done;
+
+ /* If we're here, the buffer wasn't big enough, which
+ is due to a message with most lines less than 10 characters
+ (the 10% addition for turning LF to CRLF wasn't enough).
+ Increase it by 50% and try again, till we hit
+ the largest message we can read
+ */
+ max_read = min(CARMEL_MAXMESSAGESIZE, (max_read * 15) / 10);
+ fseek(msg_stream, 0, 0);
+ } while (1);
+ done:
+ if(p == p_end)
+ mm_log("Message truncated. It's is too large", WARN);
+
+ fclose(msg_stream);
+
+ /*---- Save the name of the file buffered file ----*/
+ LOCAL->buffered_file = cpystr(file_name);
+ LOCAL->buffered_header_and_text |= !header_only;
+
+ return(header_only ? LOCAL->msg_buf : LOCAL->msg_buf_text_start);
+}
+
+
+/*----------------------------------------------------------------------
+ Parse basic/quick entries in a Carmel mailbox
+
+Args: stream -- stream for mailbox
+ file_pos -- File position in the index to start parsing at
+
+Returns: 0 if parse succeeds
+ -1 if it fails
+ ----*/
+static int
+carmel2_parse_mail(stream, file_pos)
+ MAILSTREAM *stream;
+ long file_pos;
+{
+ MESSAGECACHE *mc;
+ long nmsgs, next, last_line_read;
+ int found;
+
+ nmsgs = stream->nmsgs;
+
+ /*---- Get the start-of-message record ------*/
+ fseek(LOCAL->index_stream, file_pos, 0);
+ if(fgets(carmel_20k_buf,sizeof(carmel_20k_buf),LOCAL->index_stream)==NULL)
+ goto done_reading;
+ if(strncmp(carmel_20k_buf, carmel2_s_o_m, carmel2_s_o_m_len) != 0)
+ goto bomb;
+
+ while(1) {
+ if(strlen(carmel_20k_buf) != carmel2_s_o_m_len + 13)
+ goto bomb;
+
+ nmsgs++;
+ next = atol(carmel_20k_buf+24);
+ mc = carmel2_new_mc(stream, nmsgs);
+ mc->data1 = file_pos;
+
+ /*-- Get the status line, It must be the first line in the entry ----*/
+ if(fgets(carmel_20k_buf,sizeof(carmel_20k_buf),
+ LOCAL->index_stream) == NULL)
+ goto done_reading;
+ if(*carmel_20k_buf != 'U' || strlen(carmel_20k_buf) != 35)
+ goto bomb; /* Invalid format! */
+ mc->flagged = carmel_20k_buf[1] == 'F';
+ mc->answered = carmel_20k_buf[3] == 'A';
+ mc->deleted = carmel_20k_buf[4] == 'D';
+ mc->seen = carmel_20k_buf[5] == 'S';
+ mc->recent = 0;
+
+ /*---- Get the rest of the other interesting entries -----*/
+ found = 0;
+ while(fgets(carmel_20k_buf,sizeof(carmel_20k_buf),
+ LOCAL->index_stream) != NULL &&
+ found < 3 &&
+ strncmp(carmel_20k_buf, carmel2_s_o_m, carmel2_s_o_m_len))
+ if (*carmel_20k_buf == 'Z') {
+ mc->rfc822_size = atol(carmel_20k_buf+1);
+ found++;
+ } else if(*carmel_20k_buf == 'D') {
+ carmel2_parse_date(mc, carmel_20k_buf+1);
+ found++;
+ } else if(*carmel_20k_buf == 'P') {
+ mc->data2 = atoi(carmel_20k_buf+1);
+ found++;
+ }
+
+ /*-------- Now find the next index entry ---------*/
+ last_line_read = ftell(LOCAL->index_stream);
+ file_pos += next;
+ fseek(LOCAL->index_stream, file_pos, 0); /* try the offset first */
+ if(fgets(carmel_20k_buf, sizeof(carmel_20k_buf),
+ LOCAL->index_stream) == NULL)
+ break;
+ if(strncmp(carmel_20k_buf, carmel2_s_o_m, carmel2_s_o_m_len) != 0){
+ /*-- Didn't match what was seeked to, back off and read lines --*/
+ fseek(LOCAL->index_stream, last_line_read, 0);
+ do {
+ file_pos = ftell(LOCAL->index_stream);
+ if(fgets(carmel_20k_buf, sizeof(carmel_20k_buf),
+ LOCAL->index_stream) == NULL)
+ goto done_reading;
+ }while(strncmp(carmel_20k_buf,carmel2_s_o_m,carmel2_s_o_m_len)!=0);
+ }
+ }
+ done_reading:
+ if(stream->nmsgs != nmsgs) {
+ stream->nmsgs = nmsgs;
+ mm_exists(stream, nmsgs);
+ }
+ return(0);
+
+ bomb:
+ return(-1);
+}
+
+
+
+/* This killer macro is from bezerk.h. It's only needed at sites that don't
+ * escape "From " lines with ">From " unless absolutely necessary (The UW).
+ */
+
+/* Validate line known to start with ``F''
+ * Accepts: pointer to candidate string to validate as a From header
+ * return pointer to end of date/time field
+ * return pointer to offset from t of time (hours of ``mmm dd hh:mm'')
+ * return pointer to offset from t of time zone (if non-zero)
+ * Returns: T if valid From string, t,ti,zn set; else NIL
+ */
+
+#define VALID(s,x,ti,zn) \
+ (s[1] == 'r') && (s[2] == 'o') && (s[3] == 'm') && (s[4] == ' ') && \
+ (x = strchr (s+5,'\n')) && \
+ ((x-s < 41) || ((ti = ((x[-2] == ' ') ? -14 : (x[-3] == ' ') ? -15 : \
+ (x[-4] == ' ') ? -16 : (x[-5] == ' ') ? -17 : \
+ (x[-6] == ' ') ? -18 : (x[-7] == ' ') ? -19 : \
+ (x[-8] == ' ') ? -20 : (x[-9] == ' ') ? -21 : \
+ (x[-10]== ' ') ? -22 : (x[-11]== ' ') ? -23 : 0)) && \
+ (x[ti] == ' ') && (x[ti+1] == 'r') && (x[ti+2] == 'e') && \
+ (x[ti+3] == 'm') && (x[ti+4] == 'o') && (x[ti+5] == 't') && \
+ (x[ti+6] == 'e') && (x[ti+7] == ' ') && (x[ti+8] == 'f') && \
+ (x[ti+9] == 'r') && (x[ti+10]== 'o') && (x[ti+11]== 'm') && \
+ (x += ti)) || T) && \
+ (x-s >= 27) && \
+ ((x[ti = -5] == ' ') ? ((x[-8] == ':') ? !(zn = 0) : \
+ ((x[ti = zn = -9] == ' ') || \
+ ((x[ti = zn = -11] == ' ') && \
+ ((x[-10] == '+') || (x[-10] == '-'))))) : \
+ ((x[zn = -4] == ' ') ? (x[ti = -9] == ' ') : \
+ ((x[zn = -6] == ' ') && ((x[-5] == '+') || (x[-5] == '-')) && \
+ (x[ti = -11] == ' ')))) && \
+ (x[ti - 3] == ':') && (x[ti -= ((x[ti - 6] == ':') ? 9 : 6)] == ' ') && \
+ (x[ti - 3] == ' ') && (x[ti - 7] == ' ') && (x[ti - 11] == ' ')
+
+
+/* You are not expected to understand this macro, but read the next page if
+ * you are not faint of heart.
+ *
+ * Known formats to the VALID macro are:
+ * From user Wed Dec 2 05:53 1992
+ * BSD From user Wed Dec 2 05:53:22 1992
+ * SysV From user Wed Dec 2 05:53 PST 1992
+ * rn From user Wed Dec 2 05:53:22 PST 1992
+ * From user Wed Dec 2 05:53 -0700 1992
+ * From user Wed Dec 2 05:53:22 -0700 1992
+ * From user Wed Dec 2 05:53 1992 PST
+ * From user Wed Dec 2 05:53:22 1992 PST
+ * From user Wed Dec 2 05:53 1992 -0700
+ * Solaris From user Wed Dec 2 05:53:22 1992 -0700
+ *
+ * Plus all of the above with `` remote from xxx'' after it. Thank you very
+ * much, smail and Solaris, for making my life considerably more complicated.
+ */
+
+/*
+ * What? You want to understand the VALID macro anyway? Alright, since you
+ * insist. Actually, it isn't really all that difficult, provided that you
+ * take it step by step.
+ *
+ * Line 1 Validates that the 2-5th characters are ``rom ''.
+ * Line 2 Sets x to point to the end of the line.
+ * Lines 3-12 First checks to see if the line is at least 41 characters long.
+ * If so, it scans backwards up to 10 characters (the UUCP system
+ * name length limit due to old versions of UNIX) to find a space.
+ * If one is found, it backs up 12 characters from that point, and
+ * sees if the string from there is `` remote from''. If so, it
+ * sets x to that position. The ``|| T'' is there so the parse
+ * will still continue.
+ * Line 13 Makes sure that there are at least 27 characters in the line.
+ * Lines 14-17 Checks if the date/time ends with the year. If so, It sees if
+ * there is a colon 3 characters further back; this would mean
+ * that there is no timezone field and zn is set to 0 and ti is
+ * left in front of the year. If not, then it expects there to
+ * either to be a space four characters back for a three-letter
+ * timezone, or a space six characters back followed by a + or -
+ * for a numeric timezone. If a timezone is found, both zn and
+ * ti are the offset of the space immediately before it.
+ * Lines 18-20 Are the failure case for a date/time not ending with a year in
+ * line 14. If there is a space four characters back, it is a
+ * three-letter timezone; there must be a space for the year nine
+ * characters back. Otherwise, there must be a space six
+ * characters back and a + or - five characters back to indicate a
+ * numeric timezone and a space eleven characters back to indicate
+ * a year. zn and ti are set appropriately.
+ * Line 21 Make sure that the string before ti is of the form hh:mm or
+ * hh:mm:ss. There must be a colon three characters back, and a
+ * space six or nine characters back (depending upon whether or
+ * not the character six characters back is a colon). ti is set
+ * to be the offset of the space immediately before the time.
+ * Line 22 Make sure the string before ti is of the form www mmm dd.
+ * There must be a space three characters back (in front of the
+ * day), one seven characters back (in front of the month), and
+ * one eleven characters back (in front of the day of week).
+ *
+ * Why a macro? It gets invoked a *lot* in a tight loop. On some of the
+ * newer pipelined machines it is faster being open-coded than it would be if
+ * subroutines are called.
+ *
+ * Why does it scan backwards from the end of the line, instead of doing the
+ * much easier forward scan? There is no deterministic way to parse the
+ * ``user'' field, because it may contain unquoted spaces! Yes, I tested it to
+ * see if unquoted spaces were possible. They are, and I've encountered enough
+ * evil mail to be totally unwilling to trust that ``it will never happen''.
+ */
+
+
+/*----------------------------------------------------------------------
+ Get the new mail out of the spooled mail file
+
+ Args: stream -- The inbox stream to add mail too
+ spool -- The path name of the spool file
+ mailbox -- Name user sees for this, used only for error reporting
+
+ Result:
+
+ - Lock the spool mail file with bezerk_lock
+ - Get the carmel2 index open and remember where we started in it
+ - Make buffer big enough for biggest header we'll mess with
+ - Loop reading all the message out of the spool file:
+ - Get a new data file for the message and open it
+ - Read the header of the message into the big buffer while...
+ - writing the message into the new data file
+ - finish writing the text of the message into the data file
+ - If all went well bump the message count and say it exists
+ - Parse the header of the message to get an envelope, date and size
+ - Write an entry into the carmel2 index
+ - Unlink and create (to zero) the spool file and unlock it
+
+The carmel inbox should be locked when this is called and mm_critical
+should be called around this function.
+ ----*/
+void
+carmel2_spool_mail(stream, spool, mailbox, clear_spool_file)
+ MAILSTREAM *stream;
+ char *spool, *mailbox;
+ int clear_spool_file;
+{
+ char *p, *eof, *from_p;
+ int n, size, fd, in_header, from_i1, from_i2;
+ long file_pos, index_file_pos, byte_count, start_of_append;
+ FILE *spool_stream, *data_stream;
+ ENVELOPE *envelope;
+ BODY *b;
+ MESSAGECACHE *mc;
+ STRING string_struct;
+#ifdef BWC
+ int is_wide;
+#endif
+
+ /*--- Get the locks set and files open-----*/
+ fd = carmel2_bezerk_lock(spool, mailbox);
+ if(fd < 0) {
+ return;
+ }
+ spool_stream = fdopen(fd, "r");
+ fseek(LOCAL->index_stream, 0L, 2);
+ start_of_append = ftell(LOCAL->index_stream);
+
+ /*--- Make buffer big enough for largest allowable header ----*/
+ if(LOCAL->msg_buf_size < CARMEL_MAX_HEADER) {
+ if(LOCAL->msg_buf != NULL)
+ fs_give((void **)&(LOCAL->msg_buf));
+ LOCAL->msg_buf_size = CARMEL_MAX_HEADER;
+ LOCAL->msg_buf = fs_get(CARMEL_MAX_HEADER);
+ }
+ LOCAL->buffered_header_and_text = 0;
+ LOCAL->msg_buf_text_offset = 0L;
+ if(LOCAL->buffered_file != NULL)
+ fs_give((void **)&(LOCAL->buffered_file));
+
+
+ /*---- Read (and ignore) the first line with the "From " in it ----*/
+ eof = fgets(carmel_20k_buf, sizeof(carmel_20k_buf), spool_stream);
+
+ /*----- Loop getting messages ----*/
+ while(eof != NULL) {
+
+ /*----- get a data file for the new message ----*/
+ n = carmel2_new_data_file(stream->local, stream->mailbox);
+ data_stream = fopen((*LOCAL->calc_paths)(CalcPathCarmel2Data,
+ stream->mailbox, n),"w");
+ if(data_stream == NULL)
+ goto backout;
+ file_pos = ftell(spool_stream);
+ p = LOCAL->msg_buf;
+ in_header = 1;
+ byte_count = 0L;
+#ifdef BWC
+ is_wide = 0;
+#endif
+
+
+ /*--------------------------------------------------------------------
+ Read the message in line by line, writing it out to the
+ new data file. Also acculamuate a copy of the header in
+ a buffer for parsing
+ ---*/
+ eof = fgets(carmel_20k_buf, sizeof(carmel_20k_buf), spool_stream);
+ while(eof != NULL){
+ if(VALID(carmel_20k_buf, from_p, from_i1, from_i2))
+ break;
+
+ if(in_header) {
+#ifdef BWC
+ is_wide |= carmel_match_glyph_wide(carmel_20k_buf);
+#endif
+ if(*carmel_20k_buf == '\n') {
+ /* Encountered first blank line, end of header */
+ in_header = 0;
+ *p = '\0';
+ } else {
+ if(p - LOCAL->msg_buf + strlen(carmel_20k_buf) >
+ LOCAL->msg_buf_size){
+ /* out of room in buffer, end it */
+ in_header = 0;
+ *p = '\0';
+ } else {
+ strcpy(p, carmel_20k_buf);
+ p +=strlen(p);
+ }
+ }
+ }
+
+ /*----- Write the message into the file -----*/
+ byte_count += strlen(carmel_20k_buf);
+ if(carmel_20k_buf[0] == '>' && carmel_20k_buf[1] == 'F' &&
+ carmel_20k_buf[2] == 'r' && carmel_20k_buf[3] == 'o' &&
+ carmel_20k_buf[4] == 'm' && carmel_20k_buf[5] == ' ') {
+ if(fputs(carmel_20k_buf + 1, data_stream) == EOF)
+ goto backout;
+ byte_count -= 1;
+ } else {
+ if(fputs(carmel_20k_buf, data_stream) == EOF)
+ goto backout;
+ }
+ eof = fgets(carmel_20k_buf, sizeof(carmel_20k_buf), spool_stream);
+ }
+ fclose(data_stream);
+#ifdef BWC
+ if(is_wide) {
+ sprintf(carmel_path_buf, "%s.wid",
+ (*LOCAL->calc_paths)(CalcPathCarmel2Data,stream->mailbox,n)
+ );
+ rename((*LOCAL->calc_paths)(CalcPathCarmel2Data,stream->mailbox,n),
+ carmel_path_buf);
+ }
+#endif
+
+ /*---- get a new MESSAGECACHE to store it in -----*/
+ mc = carmel2_new_mc(stream, stream->nmsgs + 1);
+
+ /*------ Parse the message header ------*/
+ INIT(&string_struct, mail_string, (void *)"", 0);
+ rfc822_parse_msg(&envelope, &b, LOCAL->msg_buf, strlen(LOCAL->msg_buf),
+ &string_struct, mylocalhost(), carmel_20k_buf);
+ carmel2_parse_bezerk_status(mc, LOCAL->msg_buf);
+ carmel2_rfc822_date(mc, LOCAL->msg_buf);
+ mc->rfc822_size = byte_count;
+ mc->data2 = n;
+ mc->data1 = ftell(LOCAL->index_stream);
+ mc->recent = 1;
+
+ /*----- Now add the message to the Carmel2 index ------*/
+ if(carmel2_write_index(envelope, mc, LOCAL->index_stream) < 0)
+ goto backout;
+
+ /*----- Write message into auxiliary index (plain carmel) ----*/
+ if(LOCAL->carmel && LOCAL->aux_copy != NULL) {
+ if((*LOCAL->aux_copy)(stream->local, mailbox, envelope, mc)) {
+ /* BUG - this error may leave things half done, but will
+ only result in duplicated mail */
+ goto backout;
+ }
+ }
+
+ /*---- All went well, let the user know -----*/
+ stream->nmsgs++;
+ mm_exists(stream, stream->nmsgs);
+
+ mail_free_envelope(&envelope);
+ }
+
+ fflush(LOCAL->index_stream); /* Force new index entries onto disk */
+ fclose(spool_stream);
+ if(clear_spool_file) {
+ unlink(spool);
+ close(creat(spool, 0600));
+ }
+ carmel2_bezerk_unlock(fd, spool);
+ return;
+
+ backout:
+ sprintf(carmel_error_buf, "Error incorporating new mail into \"%s\": %s",
+ carmel_parse_mb_name(mailbox,'\0')->mailbox, strerror(errno));
+ /* bug in above call to parse_mb -- should have version passed in */
+ mm_log(carmel_error_buf, ERROR);
+ fflush(LOCAL->index_stream);
+ ftruncate(fileno(LOCAL->index_stream), start_of_append);
+ carmel2_bezerk_unlock(fd, spool);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Copy the actual data file when copying a message
+
+Returns: -1 for failure
+ otherwise the number of the new file,
+
+ ----*/
+static
+carmel2_copy_msg_file(stream, orig_file_number, new_mailbox)
+ MAILSTREAM *stream;
+ int orig_file_number;
+ char *new_mailbox;
+{
+ char *file_name;
+ int wide, e, new_file_num, n, orig_fd, new_fd;
+
+ /*---- Open the orginal file ----*/
+ wide = 0;
+ file_name = (*LOCAL->calc_paths)(CalcPathCarmel2Data,
+ new_mailbox,orig_file_number);
+ if(file_name == NULL)
+ return(-1);
+
+ orig_fd = open(file_name, O_RDONLY);
+ if(orig_fd < 0 && LOCAL->carmel) {
+ strcat(file_name, ".wid");
+ orig_fd = open(file_name, O_RDONLY);
+ if(orig_fd < 0)
+ goto bomb;
+ wide = 1;
+ } else {
+ goto bomb;
+ }
+
+ /*----- Open and create the new file ------*/
+ new_file_num = carmel2_new_data_file(stream->local, new_mailbox);
+ if(new_file_num < 0)
+ goto bomb;
+ file_name = (*LOCAL->calc_paths)(CalcPathCarmel2Data,
+ new_mailbox, new_file_num);
+ if(wide)
+ strcat(file_name, ".wid");
+ new_fd = open(file_name, O_WRONLY | O_CREAT, 0600);
+ if(new_fd < 0) {
+ goto bomb;
+ }
+
+ /*---- Copy the bits ------*/
+ e = 0;
+ while((n = read(orig_fd, carmel_20k_buf, sizeof(carmel_20k_buf))) >0) {
+ if(write(new_fd, carmel_20k_buf, n) < 0) {
+ e = errno;
+ break;
+ }
+ }
+
+ /*---- Close the streams and handle any errors ----*/
+ close(orig_fd);
+ close(new_fd);
+
+ if(e == 0)
+ return(new_file_num); /* All is OK */
+
+ /*--- something didn't go right ---*/
+ bomb:
+ unlink(file_name);
+ sprintf(carmel_error_buf, "Error copying message to %s: %s",
+ new_mailbox, strerror(errno));
+ mm_log(carmel_error_buf, ERROR);
+ return(-1);
+}
+
+
+
+
+
+
+/*----------------------------------------------------------------------
+ Locking for Carmel and Pod formats
+
+Args: stream -- Mail stream we're operating on
+ file -- Mail file name
+ write -- Flag indicating we want write access
+
+Retuns: -1 if lock fails, 0 if it succeeds
+
+- There are two locks. Plain locks and write locks. They are created
+ about the same way, but have different names. The time out on the write
+ locks is much shorter, because it should never be held for very long.
+
+- Hitching (links in file system) post locking is used so it will work
+ across NFS. Flock() could be used as it has two advantages. First it
+ is more efficient, second the locks will disolve automatically when the
+ process dies. The efficiency is not of great concern, and the
+ process should not (theoretically) die unless it crashes due to a bug
+ or it is abnormally terminated. The advantage of locking across NFS
+ is considered greater than the advantages of flock().
+
+- The mod time of the lock file is updated everytime mail_check()
+ or mail_ping() is called and the mod time on the lock file is recorded.
+ This is so it can be determined that the lock file is current.
+
+- When a read lock file over 30 or a write lock over 5 minutes old is
+ encountered it is assumed the lock is old and should be overridden
+ (because the process crashed or was killed).
+
+- Everytime the mod time on the lock file is updated (on calls to
+ mail_check() and mail_ping()), the mod time of the lock file is
+ checked against the record of what it was last set to. If the mod times
+ don't match the lock has been broken and overridden. Then the original
+ Pine should go into read-only mode.... This is only likely to happen if
+ someone suspends (^Z's) the process for more than 30 minutes, and another
+ process is started.
+ ----*/
+int
+carmel2_lock(local, file, write)
+ CARMEL2LOCAL *local;
+ char *file;
+ int write;
+{
+ char *hitch, lock[CARMEL_PATHBUF_SIZE], error_mess[200], *l_path;
+ struct stat sb;
+ int n, link_result, hitch_fd, timer_set, l;
+ long override, timer;
+
+ /* Set the length of time for override. It is 5 minutes for a
+ write lock (no process should have it for that long) and
+ 30 minutes for a read lock, that's 30 minutes since the
+ lock file was last touched. */
+ override = 60 * (write ? 5 : 30);
+ timer = -1;
+
+ /*----- Get the lock file and hitch names -----*/
+ l_path = (*local->calc_paths)(write ? CalcPathCarmel2WriteLock:
+ CalcPathCarmel2ReadLock,
+ file, 0);
+ if(l_path == NULL)
+ return(-1);
+ strcpy(lock, l_path);
+ /* copy lock file into bufferl call it hitch, unique thing added below */
+ hitch = carmel_path_buf;
+ hitch = strcpy(hitch, lock);
+ l = strlen(hitch);
+
+ do {
+ /*--- First create hitching post ----*/
+ for(n = time(0) % 6400; ; n += 10007) {
+ /* Get random file name, that's not too long */
+ sprintf(hitch + l, "_%c%c", '0' + (n % 80) , '0' + (n/80));
+ if(stat(hitch, &sb) < 0)
+ break; /* Got a name that doesn't exist */
+ }
+ hitch_fd = open(hitch, O_CREAT, 0666);
+ if(hitch_fd < 0) {
+ sprintf(error_mess, "Error creating lock file \"%s\": %s",
+ hitch, strerror(errno));
+ mm_log(error_mess, WARN);
+ return(-1);
+ }
+ close(hitch_fd);
+
+ /*----- Got a hitching post, now try link -----*/
+ link_result = link(hitch, lock);
+ stat(lock, &sb);
+ unlink(hitch);
+ if(link_result == 0 && sb.st_nlink == 2) {
+ /*------ Got the lock! ------*/
+ stat(lock, &sb);
+ if(write)
+ local->write_lock_mod_time = sb.st_mtime;
+ else
+ local->read_lock_mod_time = sb.st_mtime;
+ return(0);
+ }
+
+ /*----- Check and override if lock is too old -----*/
+ if(sb.st_mtime + override < time(0)) {
+ unlink(lock); /* Lock is old, override it */
+ timer = 100; /* Get us around the loop again */
+ continue;
+ } else {
+ if(timer < 0) /* timer not set */
+ timer = sb.st_mtime + override - time(0);
+ }
+
+ /*----- Will user wait till time for override? -----*/
+ if(!write || timer > 5 * 60) {
+ return(-1); /* Not more that 5 minutes */
+ }
+
+ /*----- Try again, and tell user we're trying -------*/
+ if(!(timer % 15)) {
+ sprintf(error_mess,
+ "Please wait. Mailbox %s is locked for %d more seconds",
+ file, timer);
+ mm_log(error_mess, WARN);
+ }
+ timer--;
+ sleep(1);
+ } while(timer > 0);
+
+ return(-1);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Unlock a carmel mail stream
+
+Args: stream -- The mailstream that is locked
+ mailbox -- FQN of mailbox to lock ( e.g. #carmel#foo )
+ write -- flag to set if it is a write lock
+
+Nothing is returned
+ ----*/
+void
+carmel2_unlock(local, mailbox, write)
+ CARMEL2LOCAL *local;
+ char *mailbox;
+ int write;
+{
+ char lock[CARMEL_PATHBUF_SIZE];
+ struct stat sb;
+
+ strcpy(lock, (*local->calc_paths)(write ? CalcPathCarmel2WriteLock:
+ CalcPathCarmel2ReadLock,
+ mailbox, 0));
+
+ if(stat(lock, &sb) < 0)
+ return; /* Hmmm... lock already broken */
+
+ if(sb.st_mtime !=
+ (write ? local->write_lock_mod_time : local->read_lock_mod_time))
+ return; /* Hmmm... not our lock */
+
+ unlink(lock);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Keep the mod date on the lock file fresh
+
+Args: stream --
+ file -- the name of the mailbox the lock is for
+ write -- set if this is a write lock
+
+Returns: 0 if update was OK
+ -1 if something bad happened, like the lock was stolen
+ ----*/
+static int
+carmel2_update_lock(local, file, write)
+ CARMEL2LOCAL *local;
+ char *file;
+ int write;
+{
+ char lock[CARMEL_PATHBUF_SIZE];
+ struct timeval tp[2];
+ struct stat sb;
+
+ strcpy(lock, (*local->calc_paths)(write ? CalcPathCarmel2WriteLock:
+ CalcPathCarmel2ReadLock,
+ file, 0));
+
+ if(stat(lock, &sb) < 0) {
+ /* Lock file stolen, oh oh */
+ return(-1);
+ }
+
+ if(sb.st_mtime !=
+ (write ? local->write_lock_mod_time : local->read_lock_mod_time)) {
+ /* Not our lock anymore , oh oh */
+ return(-1);
+ }
+
+ gettimeofday (&tp[0],NIL); /* set atime to now */
+ gettimeofday (&tp[1],NIL); /* set mtime to now */
+ utimes(lock, tp);
+
+ if(write)
+ local->write_lock_mod_time = tp[1].tv_sec;
+ else
+ local->read_lock_mod_time = tp[1].tv_sec;
+ return(0);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Berkeley open and lock mailbox
+
+This is mostly ripped off from the Bezerk driver
+ ----*/
+
+static int
+carmel2_bezerk_lock (spool, file)
+ char *spool, *file;
+{
+ int fd,ld,j;
+ int i = BEZERKLOCKTIMEOUT * 60 - 1;
+ struct timeval tp;
+ struct stat sb;
+ char *hitch, *lock;
+
+ lock = carmel_path_buf;
+ sprintf(lock, "%s.lock", spool);
+ do { /* until OK or out of tries */
+ gettimeofday (&tp,NIL); /* get the time now */
+#ifdef NFSKLUDGE
+ /* SUN-OS had an NFS, As kludgy as an albatross;
+ * And everywhere that it was installed, It was a total loss. -- MRC 9/25/91
+ */
+ /* build hitching post file name */
+ hitch = carmel_20k_buf;
+ sprintf(hitch, "%s.%d.%d.",carmel_path_buf,time (0),getpid ());
+ j = strlen (hitch); /* append local host name */
+ gethostname (hitch + j,(MAILTMPLEN - j) - 1);
+ /* try to get hitching-post file */
+ if ((ld = open (hitch, O_WRONLY|O_CREAT|O_EXCL,0666)) < 0) {
+ /* prot fail & non-ex, don't use lock files */
+ if ((errno == EACCES) && (stat (hitch, &sb))) *lock = '\0';
+ else { /* otherwise something strange is going on */
+ sprintf (carmel_20k_buf,"Error creating %s: %s",lock,strerror (errno));
+ mm_log (carmel_20k_buf, WARN); /* this is probably not good */
+ /* don't use lock files if not one of these */
+ if ((errno != EEXIST) && (errno != EACCES)) *lock = '\0';
+ }
+ }
+ else { /* got a hitching-post */
+ chmod (hitch,0666); /* make sure others can break the lock */
+ close (ld); /* close the hitching-post */
+ link (hitch,lock); /* tie hitching-post to lock, ignore failure */
+ stat (hitch, &sb); /* get its data */
+ unlink (hitch); /* flush hitching post */
+ /* If link count .ne. 2, hitch failed. Set ld to -1 as if open() failed
+ so we try again. If extant lock file and time now is .gt. file time
+ plus timeout interval, flush the lock so can win next time around. */
+ if ((ld = (sb.st_nlink != 2) ? -1 : 0) && (!stat (lock,&sb)) &&
+ (tp.tv_sec > sb.st_ctime + BEZERKLOCKTIMEOUT * 60)) unlink (lock);
+ }
+
+#else
+ /* This works on modern Unix systems which are not afflicted with NFS mail.
+ * "Modern" means that O_EXCL works. I think that NFS mail is a terrible
+ * idea -- that's what IMAP is for -- but some people insist upon losing...
+ */
+ /* try to get the lock */
+ if ((ld = open (lock,O_WRONLY|O_CREAT|O_EXCL,0666)) < 0) switch (errno) {
+ case EEXIST: /* if extant and old, grab it for ourselves */
+ if ((!stat (lock,&sb)) && tp.tv_sec > sb.st_ctime + LOCKTIMEOUT * 60)
+ ld = open (lock,O_WRONLY|O_CREAT,0666);
+ break;
+ case EACCES: /* protection fail, ignore if non-ex or old */
+ if (stat (lock,&sb) || (tp.tv_sec > sb.st_ctime + LOCKTIMEOUT * 60))
+ *lock = '\0'; /* assume no world write mail spool dir */
+ break;
+ default: /* some other failure */
+ sprintf (tmp,"Error creating %s: %s",lock,strerror (errno));
+ mm_log (tmp,WARN); /* this is probably not good */
+ *lock = '\0'; /* don't use lock files */
+ break;
+ }
+ if (ld >= 0) { /* if made a lock file */
+ chmod (tmp,0666); /* make sure others can break the lock */
+ close (ld); /* close the lock file */
+ }
+#endif
+ if ((ld < 0) && *lock) { /* if failed to make lock file and retry OK */
+ if (!(i%15)) {
+ sprintf (carmel_20k_buf,"Mailbox %s is locked, will override in %d seconds...",
+ file,i);
+ mm_log (carmel_20k_buf, WARN);
+ }
+ sleep (1); /* wait 1 second before next try */
+ }
+ } while (i-- && ld < 0 && *lock);
+ /* open file */
+ if ((fd = open (spool, O_RDONLY)) >= 0) flock (fd, LOCK_SH);
+ else { /* open failed */
+ j = errno; /* preserve error code */
+ if (*lock) unlink (lock); /* flush the lock file if any */
+ errno = j; /* restore error code */
+ }
+ return(fd);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Berkeley unlock and close mailbox
+ ----*/
+static void
+carmel2_bezerk_unlock (fd, spool)
+ int fd;
+ char *spool;
+{
+ sprintf(carmel_path_buf, "%s.lock", spool);
+
+ flock (fd, LOCK_UN); /* release flock'ers */
+ close (fd); /* close the file */
+ /* flush the lock file if any */
+ if (*carmel_path_buf) unlink (carmel_path_buf);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Make sure directory exists and is writable
+
+Args: dir - directory to check, should be full path
+
+Result: returns -1 if not OK along with mm_logging a message
+ 0 if OK
+----*/
+
+carmel2_check_dir(dir)
+ char *dir;
+{
+ struct stat sb;
+ char error_mess[150];
+
+ if(stat(dir, &sb) < 0) {
+ if(mkdir(dir, 0700) < 0) {
+ sprintf(error_mess, "Error creating directory %-.30s %s",
+ dir, strerror(errno));
+ mm_log(error_mess, WARN);
+ return(-1);
+ }
+ } else if(!(sb.st_mode & S_IFDIR)) {
+ sprintf(error_mess, "Warning: %s is not a directory",dir);
+ mm_log(error_mess, WARN);
+ return(-1);
+
+ } else if(access(dir, W_OK) != 0) {
+ sprintf(error_mess, "Warning: unable to write to %-.30s %s",
+ dir, strerror(errno));
+ mm_log(error_mess, WARN);
+ return(-1);
+ }
+ return(0);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Return the number for the next message file
+
+Args: stream -- the Mail stream for the new data file
+ mailbox -- The FQN of mailbox data file is for
+
+Returns: file number or -1
+ ----*/
+static
+carmel2_new_data_file(local, mailbox)
+ CARMEL2LOCAL *local;
+ char *mailbox;
+{
+ char *path, num_buf[50], e_buf[100];
+ int fd, num, bytes_read;
+
+ /*---- Get the full path of the .MAXNAME file for index ----*/
+ path = (*local->calc_paths)(CalcPathCarmel2MAXNAME, mailbox, 0);
+ if(path == NULL)
+ return(-1);
+
+ fd = open(path, O_RDWR|O_CREAT, 0666);
+ if(fd < 0) {
+ sprintf(e_buf, "Error getting next number of mail file: %s",
+ strerror(errno));
+ mm_log(e_buf, ERROR);
+ return(-1);
+ }
+
+ bytes_read = read(fd, num_buf, sizeof(num_buf));
+ if(bytes_read < 6) {
+ num = 100000;
+ } else {
+ num = atoi(num_buf) + 1;
+ if(num >= 999999)
+ num = 100000;
+ }
+ sprintf(num_buf, "%-6d\n", num);
+ lseek(fd, 0, 0);
+ write(fd, num_buf, strlen(num_buf));
+ close(fd);
+ return(num);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Do all the file name generation for carmel2 driver
+
+ The mailbox passed in is in either the format: #carmel2#folder or
+ #carmel2#user%folder
+This generates that absolute paths for an index, or a data file or
+the .MAXNAME file.
+
+Bug: This is untested!
+ ----*/
+static char *
+carmel2_calc_paths(operation, mailbox, data_file_num)
+ int operation;
+ char *mailbox;
+ int data_file_num;
+{
+ static char path[CARMEL_PATHBUF_SIZE], num[20];
+ char *p, *home_dir;
+ struct carmel_mb_name *parsed_name;
+ struct passwd *pw;
+
+ parsed_name = carmel_parse_mb_name(mailbox,'2');
+
+ if(parsed_name == NULL) {
+ mm_log("Internal error (bug). Invalid Carmel folder name",ERROR);
+ return(NULL);
+ }
+
+
+ if(parsed_name->user != NULL) {
+ /*---- has a user in mailbox name -----*/
+ pw = getpwnam(parsed_name->user);
+ if(pw == NULL) {
+ sprintf(carmel_error_buf,
+ "Error accessing mailbox \"%s\". No such user name \"%s\"\n",
+ mailbox, parsed_name->user);
+ mm_log(carmel_error_buf, ERROR);
+ carmel_free_mb_name(parsed_name);
+ return(0);
+ }
+ home_dir = pw->pw_dir;
+ } else {
+ home_dir = myhomedir();
+ }
+ mailbox = parsed_name->mailbox;
+
+ switch(operation) {
+
+ case CalcPathCarmel2Data:
+ sprintf(path, "%s/%s/%d", home_dir, CARMEL2_MSG_DIR, data_file_num);
+
+
+ case CalcPathCarmel2Index:
+ sprintf(path, "%s/%s/%s", home_dir, CARMEL2_INDEX_DIR, mailbox);
+ break;
+
+ case CalcPathCarmel2MAXNAME:
+ sprintf(path, "%s/%s", home_dir, CARMEL2_MAXFILE);
+ break;
+
+ case CalcPathCarmel2WriteLock:
+ sprintf(path, "%s/%s/.%s.wl", home_dir, CARMEL2_INDEX_DIR, mailbox);
+ break;
+
+ case CalcPathCarmel2ReadLock:
+ sprintf(path, "%s/%s/.%s.rl", home_dir, CARMEL2_INDEX_DIR, mailbox);
+ break;
+ }
+
+ carmel_free_mb_name(parsed_name);
+ return(path);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Find and parse the status line in a mail header
+
+Args: mc -- the message cache where status is to be stored
+ header -- the message header to parsen
+ ----*/
+void
+carmel2_parse_bezerk_status(mc, header)
+ MESSAGECACHE *mc;
+ char *header;
+{
+ register char *p;
+ for(p = header; *p; p++) {
+ if(*p != '\n')
+ continue;
+ p++;
+ if(*p != 'S' && *p != 'X')
+ continue;
+ if(strncmp(p, "Status: ", 8) == 0 || strncmp(p,"X-Status: ",10)== 0) {
+ mc->recent = 1;
+ for(p += *p == 'X' ? 10: 8; *p && *p != '\n'; p++)
+ switch(*p) {
+ case 'R': mc->seen = 1; break;
+ case 'O': mc->recent = 0; break;
+ case 'D': mc->deleted = 1; break;
+ case 'F': mc->flagged = 1; break;
+ case 'A': mc->flagged = 1; break;
+ }
+ }
+ }
+}
+
+
+
+/*----------------------------------------------------------------------
+ Turn a string of describing flags into a bit mask
+
+Args: stream -- mail stream, unused
+ flag -- string flag list
+
+Returns: returns short with bits set; bits are defined in carmel2.h
+ ----*/
+static short
+carmel2_getflags (stream, flag)
+ MAILSTREAM *stream;
+ char *flag;
+{
+ char *t, tmp[100];
+ short f = 0;
+ short i,j;
+ if (flag && *flag) { /* no-op if no flag string */
+ /* check if a list and make sure valid */
+ if ((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) {
+ mm_log ("Bad flag list",ERROR);
+ return NIL;
+ }
+ /* copy the flag string w/o list construct */
+ strncpy (tmp,flag+i,(j = strlen (flag) - (2*i)));
+ tmp[j] = '\0';
+ t = ucase (tmp); /* uppercase only from now on */
+
+ while (*t) { /* parse the flags */
+ if (*t == '\\') { /* system flag? */
+ switch (*++t) { /* dispatch based on first character */
+ case 'S': /* possible \Seen flag */
+ if (t[1] == 'E' && t[2] == 'E' && t[3] == 'N') i = fSEEN;
+ t += 4; /* skip past flag name */
+ break;
+ case 'D': /* possible \Deleted flag */
+ if (t[1] == 'E' && t[2] == 'L' && t[3] == 'E' && t[4] == 'T' &&
+ t[5] == 'E' && t[6] == 'D') i = fDELETED;
+ t += 7; /* skip past flag name */
+ break;
+ case 'F': /* possible \Flagged flag */
+ if (t[1] == 'L' && t[2] == 'A' && t[3] == 'G' && t[4] == 'G' &&
+ t[5] == 'E' && t[6] == 'D') i = fFLAGGED;
+ t += 7; /* skip past flag name */
+ break;
+ case 'A': /* possible \Answered flag */
+ if (t[1] == 'N' && t[2] == 'S' && t[3] == 'W' && t[4] == 'E' &&
+ t[5] == 'R' && t[6] == 'E' && t[7] == 'D') i = fANSWERED;
+ t += 8; /* skip past flag name */
+ break;
+ case 'R': /* possible \Answered flag */
+ if (t[1] == 'E' && t[2] == 'C' && t[3] == 'E' && t[4] == 'N' &&
+ t[5] == 'T') i = fRECENT;
+ t += 6; /* skip past flag name */
+ break;
+ default: /* unknown */
+ i = 0;
+ break;
+ }
+ /* add flag to flags list */
+ if (i && ((*t == '\0') || (*t++ == ' '))) f |= i;
+ else { /* bitch about bogus flag */
+ mm_log ("Unknown system flag",ERROR);
+ return NIL;
+ }
+ }
+ else { /* no user flags yet */
+ mm_log ("Unknown flag",ERROR);
+ return NIL;
+ }
+ }
+ }
+ return f;
+}
+
+
+/*----------------------------------------------------------------------
+ Get a pointer to a MESSAGECACHE entry, allocating if needed
+
+Args: stream -- message stream
+ num -- Message number to allocate on for
+
+Returns: The MESSAGECACHE entry
+
+The message caches are allocated in blocks of 256 to save space taken up by
+pointers in a linked list and allocation overhead. The mc_blocks
+data structure is an array that points to each block. The macro MC()
+defined in carmel.h returns a pointer to a MESSAGECACHE given
+a message number. This function here can be called when a MESSAGECACHE
+is needed to a message number that might be new. It allocates a new
+block if needed and clears the MESSAGECACHE returned.
+
+The MESSAGECACHE entries are currently about 28 bytes which implies 28Kb
+must be used per 1000 messages. If memory is limited this driver will be
+limited in the number of messages it can handle, and the limit is due to
+the fact that these MESSAGECACHEs must be in core.
+
+It might be possible to reduce the size of each entry by a few bytes if
+the message numbers were reduced to a short, and the mostly unused keywords
+were removed. It would also be nice to add a day of the week (3 bits)
+ ----*/
+static MESSAGECACHE *
+carmel2_new_mc(stream, num)
+ MAILSTREAM *stream;
+ int num;
+{
+ MESSAGECACHE *mc;
+
+ /* Make sure we've got a cache_entry */
+ if(num >= LOCAL->cache_size) {
+ if(LOCAL->mc_blocks == NULL)
+ LOCAL->mc_blocks=(MESSAGECACHE **)fs_get(sizeof(MESSAGECACHE *));
+ else
+ fs_resize((void **)&(LOCAL->mc_blocks), ((num >>8) + 1) *
+ sizeof(MESSAGECACHE *));
+ LOCAL->mc_blocks[num >> 8] = (MESSAGECACHE *)
+ fs_get(256 * sizeof(MESSAGECACHE));
+ LOCAL->cache_size = ((num >> 8) + 1) * 256;
+ }
+
+ mc = MC(num);
+
+ mc->user_flags = 0;
+ mc->lockcount = 0;
+ mc->seen = 0;
+ mc->deleted = 0;
+ mc->flagged = 0;
+ mc->answered = 0;
+ mc->recent = 0;
+ mc->searched = 0;
+ mc->sequence = 0;
+ mc->spare = 0;
+ mc->spare2 = 0;
+ mc->spare3 = 0;
+ mc->msgno = num;
+
+ /* Don't set the date, the size and the extra data,
+ assume they will be set
+ */
+ return(mc);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Do the real work of appending a message to a mailbox
+
+Args: local -- The carmel2 local data structure, (a some what fake incomplete
+ one, set up for the purposes here)
+ mailbox -- Name of mailbox to append to
+ message -- The rfc822 format of the message with \r\n's
+
+Returns: T if all went OK, NIL if it did not
+
+ - Make sure index exists or can be created
+ - lock index for write
+ - get a data file name
+ - Put the text in the file
+ - Parse the string to get envelope and message cache
+ - Write the entry into the index
+ - Unlock the index
+
+BUG: This needs some locking and some error messages
+
+ ----*/
+carmel2_append2(stream, local, mailbox, flags, date, message)
+ char *mailbox, *flags, *date;
+ CARMEL2LOCAL *local;
+ STRING *message;
+ MAILSTREAM *stream;
+{
+ char *index_name, *data_name, *p, c, *header_string;
+ ENVELOPE *envelope;
+ BODY *b;
+ MESSAGECACHE mc;
+ FILE *index_file, *data_file;
+ struct stat sb;
+ int last_was_crlf, saved_errno;
+ STRING string_struct;
+ long size;
+ short flagbits;
+
+ /*------ Lock the mailbox for write ------*/
+ if(carmel2_lock(local, mailbox, WRITE_LOCK) < 0) {
+ sprintf(carmel_error_buf,
+ "Mailbox \"%s\" is locked. Can't append to it.",
+ mailbox);
+ mm_log(carmel_error_buf, ERROR);
+ return(NIL);
+ }
+
+ /*----- Get the file name of carmel2 index -----*/
+ index_name = (*local->calc_paths)(CalcPathCarmel2Index, mailbox, 0);
+ if(index_name == NULL) {
+ saved_errno = 0;
+ goto bomb;
+ }
+
+ /*------ See if it exists already or not ------*/
+ if(stat(index_name, &sb) < 0) {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before copy", NIL);
+ carmel2_unlock(local, mailbox, WRITE_LOCK);
+ return(NIL);
+ }
+
+ index_file = fopen(index_name, "a");
+ if(index_file == NULL)
+ goto bomb;
+
+ mc.data2 = carmel2_new_data_file(local, mailbox);
+
+ flagbits = carmel2_getflags(NULL, flags);
+
+ if(flagbits & fSEEN) mc.seen = T;
+ if(flagbits & fDELETED) mc.deleted = T;
+ if(flagbits & fFLAGGED) mc.flagged = T;
+ if(flagbits & fANSWERED) mc.answered = T;
+ if(flagbits & fRECENT) mc.recent = T;
+ mc.user_flags = 0;
+
+ /*----- Open the data file -----*/
+ data_name = (*local->calc_paths)(CalcPathCarmel2Data, mailbox, mc.data2);
+ if(data_name == NULL) {
+ errno = 0; /* Don't generate an error message at all */
+ goto bomb;
+ }
+ data_file = fopen(data_name, "w");
+ if(data_file == NULL)
+ goto bomb;
+
+ /*--- accumulate header as we go for later parsing ---*/
+ header_string = carmel_20k_buf;
+
+ /*------ Write the message to the file, and get header in a string -----*/
+ for(size = SIZE(message); size > 0; size--){
+ c = SNX(message);
+ if(c == '\r' && size > 1) {
+ /* Turn CRLF into LF for UNIX */
+ c = SNX(message);
+ size--;
+ if(c != '\n') {
+ if(fputc('\r', data_file) < 0 || fputc(c, data_file) < 0)
+ goto bomb;
+ if(header_string != NULL) {
+ *header_string++ = '\r';
+ *header_string++ = c;
+ }
+ } else {
+ if(fputc('\n', data_file) < 0)
+ goto bomb;
+ if(header_string != NULL) {
+ if(last_was_crlf) {
+ *header_string = '\0';
+ header_string = NULL;
+ } else {
+ *header_string++ = '\r';
+ *header_string++ = '\n';
+ }
+ }
+ last_was_crlf = 1;
+ }
+ } else {
+ if(fputc(c, data_file) == EOF)
+ goto bomb;
+ if(header_string != NULL)
+ *header_string++ = c;
+ last_was_crlf = 0;
+ }
+ }
+ if(fclose(data_file) == EOF)
+ goto bomb;
+ data_file = NULL;
+
+
+ /*----Get the size that we actually wrote -----*/
+ stat(data_name, &sb);
+ mc.rfc822_size = sb.st_size;
+
+ /* This blows the nice tight memory usage for the carmel2 driver :-( */
+ header_string = cpystr(carmel_20k_buf);
+
+#ifdef BWC
+ /*--- For MIME type x-bwc-glyph-wide, store in a nnnn.wid file ----*/
+ for(p = header_string; *p; p++) {
+ if((p == header_string && carmel_match_glyph_wide(p)) ||
+ (*p == '\r' && *(p+1) == '\n' && carmel_match_glyph_wide(p+2))) {
+ sprintf(carmel_path_buf, "%s.wid", data_name);
+ rename(data_name, carmel_path_buf);
+ break;
+ }
+ }
+#endif
+
+ /*------ Parse the message to get envelope and message cache ----*/
+ INIT(&string_struct, mail_string, (void *)"", 0);
+ rfc822_parse_msg(&envelope, &b, header_string, strlen(header_string),
+ &string_struct, mylocalhost(), carmel_20k_buf);
+ carmel2_parse_bezerk_status(&mc, header_string);
+ carmel2_rfc822_date(&mc, header_string);
+
+ /*------ Write the entry into the index ------*/
+ if(carmel2_write_index(envelope, &mc, index_file) < 0)
+ goto bomb;
+
+ if(local->aux_copy != NULL) /* Write carmel index if needed */
+ (*local->aux_copy)(local, mailbox, envelope, &mc);
+
+ mail_free_envelope(&envelope);
+ fs_give((void **)&header_string);
+
+ if(fclose(index_file) == EOF)
+ goto bomb;
+ carmel2_unlock(local, mailbox, WRITE_LOCK);
+ return(T);
+
+ bomb:
+ saved_errno = errno;
+ if(index_file != NULL) {
+ fclose(index_file);
+ }
+ if(data_file != NULL) {
+ fclose(data_file);
+ unlink(data_name);
+ }
+ carmel2_unlock(local, mailbox, WRITE_LOCK);
+ if(saved_errno != 0) {
+ sprintf(carmel_error_buf,"Message append failed: %s",
+ strerror(saved_errno));
+ mm_log(carmel_error_buf, ERROR);
+ }
+ return(NIL);
+}
+
+
+
+
+/* Search support routines
+ * Accepts: MAIL stream
+ * message number
+ * pointer to additional data
+ * pointer to temporary buffer
+ * Returns: T if search matches, else NIL
+ */
+
+static char
+carmel2_search_all (stream,msgno,d,n)
+ MAILSTREAM *stream;
+ long msgno;
+ char *d;
+ long n;
+{
+ return T; /* ALL always succeeds */
+}
+
+
+static char
+carmel2_search_answered (stream,msgno,d,n)
+ MAILSTREAM *stream;
+ long msgno;
+ char *d;
+ long n;
+{
+ return MC(msgno)->answered ? T : NIL;
+}
+
+
+static char
+carmel2_search_deleted (stream,msgno,d,n)
+ MAILSTREAM *stream;
+ long msgno;
+ char *d;
+ long n;
+{
+ return MC(msgno)->deleted ? T : NIL;
+}
+
+
+static char
+carmel2_search_flagged (stream,msgno,d,n)
+ MAILSTREAM *stream;
+ long msgno;
+ char *d;
+ long n;
+{
+ return MC(msgno)->flagged ? T : NIL;
+}
+
+
+static char
+carmel2_search_keyword (stream,msgno,d,n)
+ MAILSTREAM *stream;
+ long msgno;
+ char *d;
+ long n;
+{
+ return NIL; /* keywords not supported yet */
+}
+
+
+static char
+carmel2_search_new (stream,msgno,d,n)
+ MAILSTREAM *stream;
+ long msgno;
+ char *d;
+ long n;
+{
+ MESSAGECACHE *elt = MC(msgno);
+ return (elt->recent && !elt->seen) ? T : NIL;
+}
+
+static char
+carmel2_search_old (stream,msgno,d,n)
+ MAILSTREAM *stream;
+ long msgno;
+ char *d;
+ long n;
+{
+ return MC(msgno)->recent ? NIL : T;
+}
+
+
+static char
+carmel2_search_recent (stream,msgno,d,n)
+ MAILSTREAM *stream;
+ long msgno;
+ char *d;
+ long n;
+{
+ return MC(msgno)->recent ? T : NIL;
+}
+
+
+static char
+carmel2_search_seen (stream,msgno,d,n)
+ MAILSTREAM *stream;
+ long msgno;
+ char *d;
+ long n;
+{
+ return MC(msgno)->seen ? T : NIL;
+}
+
+
+static char
+carmel2_search_unanswered (stream,msgno,d,n)
+ MAILSTREAM *stream;
+ long msgno;
+ char *d;
+ long n;
+{
+ return MC(msgno)->answered ? NIL : T;
+}
+
+
+static char
+carmel2_search_undeleted (stream,msgno,d,n)
+ MAILSTREAM *stream;
+ long msgno;
+ char *d;
+ long n;
+{
+ return MC(msgno)->deleted ? NIL : T;
+}
+
+
+static char
+carmel2_search_unflagged (stream,msgno,d,n)
+ MAILSTREAM *stream;
+ long msgno;
+ char *d;
+ long n;
+{
+ return MC(msgno)->flagged ? NIL : T;
+}
+
+
+static char
+carmel2_search_unkeyword (stream,msgno,d,n)
+ MAILSTREAM *stream;
+ long msgno;
+ char *d;
+ long n;
+{
+ return T; /* keywords not supported yet */
+}
+
+
+static char
+carmel2_search_unseen (stream,msgno,d,n)
+ MAILSTREAM *stream;
+ long msgno;
+ char *d;
+ long n;
+{
+ return MC(msgno)->seen ? NIL : T;
+}
+
+static char
+carmel2_search_before (stream,msgno,d,n)
+ MAILSTREAM *stream;
+ long msgno;
+ char *d;
+ long n;
+{
+ return (char) (carmel2_msgdate (stream,msgno) < n);
+}
+
+
+static char
+carmel2_search_on (stream,msgno,d,n)
+ MAILSTREAM *stream;
+ long msgno;
+ char *d;
+ long n;
+{
+ return (char) (carmel2_msgdate (stream,msgno) == n);
+}
+
+
+static char
+carmel2_search_since (stream,msgno,d,n)
+ MAILSTREAM *stream;
+ long msgno;
+ char *d;
+ long n;
+{
+ /* everybody interprets "since" as .GE. */
+ return (char) (carmel2_msgdate (stream,msgno) >= n);
+}
+
+
+static unsigned long
+carmel2_msgdate (stream,msgno)
+ MAILSTREAM *stream;
+ long msgno;
+{
+ struct stat sbuf;
+ struct tm *tm;
+ MESSAGECACHE *elt = MC(msgno);
+
+ return (long) (elt->year << 9) + (elt->month << 5) + elt->day;
+}
+
+/*----------------------------------------------------------------------
+ Search only the body of the text.
+ BUG, probably need to unencode before searching
+ ---*/
+static char
+carmel2_search_body (stream,msgno, pat, pat_len)
+ MAILSTREAM *stream;
+ long msgno,pat_len;
+ char *pat;
+{
+ char *t = carmel2_fetchtext(stream, msgno);
+ return (t && search (t,strlen(t), pat, pat_len));
+}
+
+
+/*----------------------------------------------------------------------
+ Search the subject field
+ ----*/
+static char
+carmel2_search_subject (stream,msgno, pat, pat_len)
+ MAILSTREAM *stream;
+ long msgno, pat_len;
+ char *pat;
+{
+ char *t = carmel2_fetchstructure (stream,msgno,NULL)->subject;
+ return t ? search (t, strlen(t), pat, pat_len) : NIL;
+}
+
+
+/*----------------------------------------------------------------------
+ Search the full header and body text of the message
+ ---*/
+static char
+carmel2_search_text (stream, msgno, pat, pat_len)
+ MAILSTREAM *stream;
+ long msgno, pat_len;
+ char *pat;
+{
+ char *t = carmel2_fetchheader(stream,msgno);
+ return (t && search(t, strlen(t), pat, pat_len)) ||
+ carmel2_search_body(stream,msgno, pat, pat_len);
+}
+
+
+/*----------------------------------------------------------------------
+ Search the Bcc field
+ ---*/
+static char
+carmel2_search_bcc (stream,msgno,d,n)
+ MAILSTREAM *stream;
+ long msgno;
+ char *d;
+ long n;
+{
+ carmel_20k_buf[0] = '\0';
+ /* get text for address */
+ rfc822_write_address (carmel_20k_buf,
+ carmel2_fetchstructure (stream,msgno,NULL)->bcc);
+ return search (carmel_20k_buf, strlen(carmel_20k_buf),d,n);
+}
+
+
+static char
+carmel2_search_cc (stream,msgno,d,n)
+ MAILSTREAM *stream;
+ long msgno;
+ char *d;
+ long n;
+{
+ carmel_20k_buf[0] = '\0';
+ /* get text for address */
+ rfc822_write_address (carmel_20k_buf,
+ carmel2_fetchstructure (stream, msgno, NULL)->cc);
+ return search (carmel_20k_buf, strlen(carmel_20k_buf),d,n);
+}
+
+
+static char
+carmel2_search_from (stream,msgno,d,n)
+ MAILSTREAM *stream;
+ long msgno;
+ char *d;
+ long n;
+{
+ carmel_20k_buf[0] = '\0';
+ /* get text for address */
+ rfc822_write_address (carmel_20k_buf,
+ carmel2_fetchstructure (stream,msgno,NULL)->from);
+ return search (carmel_20k_buf, strlen(carmel_20k_buf),d,n);
+}
+
+
+static char
+carmel2_search_to (stream,msgno, pat, pat_len)
+ MAILSTREAM *stream;
+ long msgno;
+ char *pat;
+ long pat_len;
+{
+ carmel_20k_buf[0] = '\0';
+ /* get text for address */
+ rfc822_write_address (carmel_20k_buf,
+ carmel2_fetchstructure (stream, msgno, NULL)->to);
+ return(search(carmel_20k_buf,strlen(carmel_20k_buf), pat, pat_len));
+}
+
+/* Search parsers */
+
+
+/* Parse a date
+ * Accepts: function to return
+ * pointer to date integer to return
+ * Returns: function to return
+ */
+
+static search_t
+carmel2_search_date (f, n)
+ search_t f;
+ long *n;
+{
+ long i;
+ char *s;
+ MESSAGECACHE elt;
+ /* parse the date and return fn if OK */
+ return (carmel2_search_string (f,&s,&i) && mail_parse_date (&elt,s) &&
+ (*n = (elt.year << 9) + (elt.month << 5) + elt.day)) ? f : NIL;
+}
+
+/* Parse a flag
+ * Accepts: function to return
+ * pointer to string to return
+ * Returns: function to return
+ */
+
+static search_t
+carmel2_search_flag (f,d)
+ search_t f;
+ char **d;
+{
+ /* get a keyword, return if OK */
+ return (*d = strtok (NIL," ")) ? f : NIL;
+}
+
+
+/* Parse a string
+ * Accepts: function to return
+ * pointer to string to return
+ * pointer to string length to return
+ * Returns: function to return
+ */
+
+static search_t
+carmel2_search_string (f,d,n)
+ search_t f;
+ char **d;
+ long *n;
+{
+ char *c = strtok (NIL,""); /* remainder of criteria */
+ if (c) { /* better be an argument */
+ switch (*c) { /* see what the argument is */
+ case '\0': /* catch bogons */
+ case ' ':
+ return NIL;
+ case '"': /* quoted string */
+ if (!(strchr (c+1,'"') && (*d = strtok (c,"\"")) && (*n = strlen (*d))))
+ return NIL;
+ break;
+ case '{': /* literal string */
+ *n = strtol (c+1,&c,10); /* get its length */
+ if (*c++ != '}' || *c++ != '\015' || *c++ != '\012' ||
+ *n > strlen (*d = c)) return NIL;
+ c[*n] = '\255'; /* write new delimiter */
+ strtok (c,"\255"); /* reset the strtok mechanism */
+ break;
+ default: /* atomic string */
+ *n = strlen (*d = strtok (c," "));
+ break;
+ }
+ return f;
+ }
+ else return NIL;
+}
+
+
+
+
+
+
+/*----------------------------------------------------------------------
+ Some stuff to help out with the date parsing
+ ---*/
+char *xdays2[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL};
+
+char *
+month_abbrev2(month_num)
+ int month_num;
+{
+ static char *xmonths[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
+ if(month_num < 1 || month_num > 12)
+ return("xxx");
+ return(xmonths[month_num - 1]);
+}
+
+struct time_zone_names {
+ char *name;
+ int hours;
+ int minutes;
+} tz_names[] = {
+ {"GMT", 0, 0},
+ {"PST", -8, 0},
+ {"PDT", -7, 0},
+ {"MST", -7, 0},
+ {"MDT", -6, 0},
+ {"CST", -6, 0},
+ {"CDT", -5, 0},
+ {"EST", -5, 0},
+ {"EDT", -4, 0},
+ {"JST", 9, 0},
+ {"IST", 2, 0},
+ {"IDT", 3, 0},
+ {NULL, 0, 0}};
+
+/*----------------------------------------------------------------------
+ Parse a date string into into a structure
+
+Args: mc -- mesage cache to with structure to receive data
+ given_date -- full header with date string somewhere to be found
+
+This parses a number of date formats and produces a cannonical date in
+a structure. The basic format is:
+
+ dd mm yy hh:mm:ss.t tz
+
+It will also handle:
+ ww dd mm yy hh:mm:ss.t tz mm dd yy hh:mm:ss.t tz
+ ww dd mm hh:mm:ss.t yy tz mm dd hh:mm:ss.t yy tz
+
+It knows many abbreviations for timezones, but is not complete.
+In general absolute time zones in hours +/- GMT are best.
+ ----*/
+void
+carmel2_rfc822_date(mc, given_date)
+ char *given_date;
+ MESSAGECACHE *mc;
+{
+ char *p, **i, *q;
+ int month, year;
+ struct time_zone_names *tz;
+
+ mc->seconds = 0;
+ mc->minutes = 0;
+ mc->hours = 30;
+ mc->day = 0;
+ mc->month = 0;
+ mc->year = 0;
+ mc->zhours = 0;
+ mc->zminutes = 0;
+
+ if(given_date == NULL)
+ return;
+
+ p = given_date;
+
+ if(*p != 'D' && strncmp(p, "Date:",5))
+ while(*p) {
+ if(*p == '\n' && *(p+1) == 'D' && strncmp(p+1, "Date:", 5) == 0)
+ break;
+ p++;
+ }
+ if(!*p)
+ return;
+
+ p += 6; /* Skip "\nDate: " */
+ while(isspace(*p))
+ p++;
+
+ /* Start with month, weekday or day ? */
+ for(i = xdays2; *i != NULL; i++)
+ if(struncmp2(p, *i, 3) == 0) /* Match first 3 letters */
+ break;
+ if(*i != NULL) {
+ /* Started with week day .. buz over it*/
+ while(*p && !isspace(*p) && *p != ',')
+ p++;
+ while(*p && (isspace(*p) || *p == ','))
+ p++;
+ }
+ if(isdigit(*p)) {
+ mc->day = atoi(p);
+ while(*p && isdigit(*p))
+ p++;
+ while(*p && (*p == '-' || *p == ',' || isspace(*p)))
+ p++;
+ }
+ for(month = 1; month <= 12; month++)
+ if(struncmp2(p, month_abbrev2(month), 3) == 0)
+ break;
+ if(month < 13) {
+ mc->month = month;
+
+ }
+ /* Move over month, (or whatever is there) */
+ while(*p && !isspace(*p) && *p != ',' && *p != '-')
+ p++;
+ while(*p && (isspace(*p) || *p == ',' || *p == '-'))
+ p++;
+
+ /* Check again for day */
+ if(isdigit(*p) && mc->day == -1) {
+ mc->day = atoi(p);
+ while(*p && isdigit(*p))
+ p++;
+ while(*p && (*p == '-' || *p == ',' || isspace(*p)))
+ p++;
+ }
+
+ /*-- Check for time --*/
+ for(q = p; *q && isdigit(*q); q++);
+ if(*q == ':') {
+ /* It's the time (out of place) */
+ mc->hours = atoi(p);
+ while(*p && *p != ':' && !isspace(*p))
+ p++;
+ if(*p == ':') {
+ mc->minutes = atoi(p);
+ while(*p && *p != ':' && !isspace(*p))
+ p++;
+ if(*p == ':') {
+ mc->seconds = atoi(p);
+ while(*p && !isspace(*p))
+ p++;
+ }
+ }
+ while(*p && isspace(*p))
+ p++;
+ }
+
+ /* Get the year 0-50 is 2000-2050; 50-100 is 1950-1999 and
+ 101-9999 is 101-9999 */
+ if(isdigit(*p)) {
+ year = atoi(p);
+ if(year < 50)
+ year += 2000;
+ else if(year < 100)
+ year += 1900;
+ mc->year = year - 1969;
+ while(*p && isdigit(*p))
+ p++;
+ while(*p && (*p == '-' || *p == ',' || isspace(*p)))
+ p++;
+ } else {
+ /* Something wierd, skip it and try to resynch */
+ while(*p && !isspace(*p) && *p != ',' && *p != '-')
+ p++;
+ while(*p && (isspace(*p) || *p == ',' || *p == '-'))
+ p++;
+ }
+
+ /*-- Now get hours minutes, seconds and ignore tenths --*/
+ for(q = p; *q && isdigit(*q); q++);
+ if(*q == ':' && mc->hours == 30) {
+ mc->hours = atoi(p);
+ while(*p && *p != ':' && !isspace(*p))
+ p++;
+ if(*p == ':') {
+ p++;
+ mc->minutes = atoi(p);
+ while(*p && *p != ':' && !isspace(*p))
+ p++;
+ if(*p == ':') {
+ p++;
+ mc->seconds = atoi(p);
+ while(*p && !isspace(*p) && *p != '+' && *p != '-')
+ p++;
+ }
+ }
+ }
+ while(*p && isspace(*p))
+ p++;
+
+
+ /*-- The time zone --*/
+ if(*p) {
+ if(*p == '+' || *p == '-') {
+ char tmp[3];
+ mc->zoccident = (*p == '+' ? 1 : 0);
+ p++;
+ tmp[0] = *p++;
+ tmp[1] = *p++;
+ tmp[2] = '\0';
+ mc->zhours = atoi(tmp);
+ tmp[0] = *p++;
+ tmp[1] = *p++;
+ tmp[2] = '\0';
+ mc->zminutes *= atoi(tmp);
+ } else {
+ for(tz = tz_names; tz->name != NULL; tz++) {
+ if(struncmp2(p, tz->name, strlen(tz->name)) ==0) {
+ if(tz->hours >= 0) {
+ mc->zhours = tz->hours;
+ mc->zoccident = 1;
+ } else {
+ mc->zhours = -tz->hours;
+ mc->zoccident = 0;
+ }
+ mc->zminutes = tz->minutes;
+ break;
+ }
+ }
+ }
+ }
+ if(mc->hours == 30)
+ mc->hours = 0;
+}
+
+
+
+/*----------------------------------------------------------------------
+ Print the date from the MESSAGECACHE into the string
+ ----*/
+static void
+carmel2_date2string(string, mc)
+ char *string;
+ MESSAGECACHE *mc;
+{
+ sprintf(string, "%d %s %d %d:%02d:%02d %s%04d",
+ mc->day, month_abbrev2(mc->month), 1969+mc->year,
+ mc->hours, mc->minutes, mc->seconds, mc->zoccident ? "-" : "",
+ mc->zhours * 100 + mc->zminutes);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Read the date into a structure from line in Carmel index
+
+ Args: mc -- The structure to contain the date (and other things)
+ string -- String to be parsed. Format is:
+ "yyyy^Amm^Add^Ahh^Amm^Ass^Azh^Azm"
+
+ ----*/
+static void
+carmel2_parse_date(mc, string)
+ MESSAGECACHE *mc;
+ char *string;
+{
+ int n;
+ mc->year = next_num(&string) - 1969;
+ mc->month = next_num(&string);
+ mc->day = next_num(&string);
+ mc->hours = next_num(&string);
+ mc->minutes = next_num(&string);
+ mc->seconds = next_num(&string);
+
+ n = next_num(&string);
+ if(n < 0) {
+ mc->zoccident = 0;
+ mc->zhours = -n;
+ } else {
+ mc->zoccident = 1;
+ mc->zhours = n;
+ }
+ mc->zminutes = next_num(&string);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Read the next number out of string and return it, advancing the string
+ ----*/
+static
+next_num(string)
+ char **string;
+{
+ int n;
+ char *p;
+
+ if(string == NULL)
+ return(0);
+
+ p = *string;
+ n = atoi(p);
+ while(*p > '\001')
+ p++;
+ if(*p)
+ *string = p+1;
+ else
+ *string = NULL;
+ return(n);
+}
+
+
+/*----------------------------------------------------------------------
+ Take a (slightly ugly) FQ mailbox and and return the prettier
+ last part of it
+ ----*/
+char *
+carmel_pretty_mailbox(mailbox)
+ char *mailbox;
+{
+ char *pretty_mb;
+
+ for(pretty_mb = mailbox + strlen(mailbox) - 1;
+ *pretty_mb != '#' && pretty_mb > mailbox;
+ pretty_mb--)
+ ;
+ if(*pretty_mb == '#')
+ pretty_mb++;
+ return(pretty_mb);
+}
+
+/*----------------------------------------------------------------------
+ Parse a carmel mailbox name into its parts
+
+Args: fq_name: The name to parse
+ given_version: The version that must match; currently either \0 or '2'
+
+Returns: NULL if not a valid carmel name, version of name and given_version
+ do not match, or a malloced structure if it is.
+ ----*/
+struct carmel_mb_name *
+carmel_parse_mb_name(fq_name, given_version)
+ char *fq_name;
+ char given_version;
+{
+ char *p, *q, version[2];
+ struct carmel_mb_name *parsed_name;
+
+ if(struncmp2(fq_name, CARMEL_NAME_PREFIX, strlen(CARMEL_NAME_PREFIX))!= 0){
+ return(0); /* BUG -- we won't work with non-FQN names for now */
+ p = fq_name;
+ version[0] = given_version;
+ version[1] = '\0';
+ } else {
+ if(fq_name[7] == CARMEL_NAME_CHAR) {
+ version[0] = '\0';
+ p = fq_name + 8;
+ } else if(fq_name[8] == CARMEL_NAME_CHAR) {
+ version[0] = fq_name[7];
+ version[1] = '\0';
+ p = fq_name + 9;
+ } else {
+ return(NULL);
+ }
+ }
+
+ if(given_version != version[0])
+ return(NULL);
+
+ parsed_name=(struct carmel_mb_name *)fs_get(sizeof(struct carmel_mb_name));
+ parsed_name->version[0] = version[0];
+ parsed_name->version[1] = version[1];
+
+ /*---- Find second # if there is one ---*/
+ for(q = p; *q && *q != CARMEL_NAME_CHAR; q++);
+
+ if(*q == CARMEL_NAME_CHAR) {
+ /*----- There is a user name -----*/
+ parsed_name->user = fs_get((q - p) + 1);
+ strncpy(parsed_name->user, p, q - p);
+ parsed_name->user[q - p] = '\0';
+ p = q + 1;
+ } else {
+ parsed_name->user = NULL;
+ }
+
+ parsed_name->mailbox = cpystr(p);
+
+ return(parsed_name);
+}
+
+
+void
+carmel_free_mb_name(mb_name)
+ struct carmel_mb_name *mb_name;
+{
+ if(mb_name->user != NULL)
+ fs_give((void **)(&(mb_name->user)));
+ fs_give((void **)(&(mb_name->mailbox)));
+ fs_give((void **)&mb_name);
+}
+
+
+
+
+
+
+
+
+
+
+/*--------------------------------------------------
+ A case insensitive strcmp()
+
+ Args: o, r -- The two strings to compare
+
+ Result: integer indicating which is greater
+ ---*/
+strucmp2(o, r)
+ register char *r, *o;
+{
+ if(r == NULL && o == NULL)
+ return(0);
+ if(o == NULL)
+ return(1);
+ if(r == NULL)
+ return(-1);
+
+ while(*o && *r && (isupper(*o) ? tolower(*o) : *o) ==
+ (isupper(*r) ? tolower(*r) : *r)) {
+ o++, r++;
+ }
+ return((isupper(*o) ? tolower(*o): *o)-(isupper(*r) ? tolower(*r) : *r));
+}
+
+
+
+/*----------------------------------------------------------------------
+ A case insensitive strncmp()
+
+ Args: o, r -- The two strings to compare
+ n -- length to stop comparing strings at
+
+ Result: integer indicating which is greater
+
+ ----*/
+struncmp2(o, r, n)
+ register char *r, *o;
+ register int n;
+{
+ if(r == NULL && o == NULL)
+ return(0);
+ if(o == NULL)
+ return(1);
+ if(r == NULL)
+ return(-1);
+
+ n--;
+ while(n && *o && *r &&
+ (isupper(*o)? tolower(*o): *o) == (isupper(*r)? tolower(*r): *r)) {
+ o++; r++; n--;
+ }
+ return((isupper(*o)? tolower(*o): *o) - (isupper(*r)? tolower(*r): *r));
+}
+
+/*----------------------------------------------------------------------
+ A replacement for strchr or index ...
+
+ ....so we don't have to worry if it's there or not. We bring our own.
+If we really care about efficiency and think the local one is more
+efficient the local one can be used, but most of the things that take
+a long time are in the c-client and not in pine.
+ ----*/
+char *
+strindex2(buffer, ch)
+ char *buffer;
+ int ch;
+{
+ /** Returns a pointer to the first occurance of the character
+ 'ch' in the specified string or NULL if it doesn't occur **/
+
+ do
+ if(*buffer == ch)
+ return(buffer);
+ while (*buffer++ != '\0');
+
+ return(NULL);
+}
+
+
+/*======================================================================
+ */
+
+xopen(file, mode)
+ char *file, *mode;
+{}
+
+
+xclose(stream)
+ FILE *stream;
+{}
+
+xread() {}
+
+xwrite() {}
+
+xlseek() {}
+
+#ifdef BWC
+carmel_match_glyph_wide(string)
+ char *string;
+{
+ extern char *ptspecials;
+ char *s;
+
+ if(struncmp2(string, "content-type", 12))
+ return(0); /* Nope */
+ string += 12;
+ while(*string && isspace(*string)) string++;
+ if(*string != ':')
+ return(0);
+ string++;
+ while(*string && isspace(*string)) string++;
+ s = string;
+ string = rfc822_parse_word(string, ptspecials);
+ if(string == NULL)
+ return(0);
+ if(struncmp2(s, "text", 4))
+ return(0);
+ while(*string && isspace(*string)) string++;
+ if(*string != '/')
+ return;
+ string++;
+ while(*string && isspace(*string)) string++;
+ s = string;
+ string = rfc822_parse_word(string, ptspecials);
+ if(string == NULL)
+ return(0);
+ if(struncmp2(s, "x-bwc-glyph-wide", 16))
+ return(0);
+ return(1);
+}
+#endif
+
diff --git a/contrib/carmel/c-client/carmel2.h b/contrib/carmel/c-client/carmel2.h
new file mode 100644
index 00000000..550a4bf3
--- /dev/null
+++ b/contrib/carmel/c-client/carmel2.h
@@ -0,0 +1,200 @@
+/*----------------------------------------------------------------------
+
+ T H E C A R M E L 2 M A I L F I L E D R I V E R
+
+ Author(s): Laurence Lundblade
+ Baha'i World Centre
+ Data Processing
+ Haifa, Israel
+ Internet: lgl@cac.washington.edu or laurence@bwc.org
+ September 1992
+
+ Last Edited: Aug 31, 1994
+ ----------------------------------------------------------------------*/
+
+/* Command bits from carmel2_getflags() */
+
+#define fSEEN 1
+#define fDELETED 2
+#define fFLAGGED 4
+#define fANSWERED 8
+#define fRECENT 16
+
+/* Kinds of locks for carmel2_lock() */
+
+#define READ_LOCK 0
+#define WRITE_LOCK 1
+
+
+/* Carmel2 I/O stream local data */
+
+typedef struct _local2 {
+ MESSAGECACHE **mc_blocks;
+ long cache_size;
+ FILE *index_stream;
+ char *stdio_buf;
+ char *msg_buf;
+ unsigned long msg_buf_size;
+ unsigned long msg_buf_text_offset;
+ char *msg_buf_text_start;
+ char *buffered_file;
+ long read_lock_mod_time, write_lock_mod_time;
+ long index_size;
+ unsigned int dirty:1;
+ unsigned int carmel:1; /* It's a carmel file instead of carmel2 */
+ unsigned int buffered_header_and_text:1;
+ unsigned int new_file_on_copy:1;
+ char *(*calc_paths)();
+ long (*aux_copy)();
+} CARMEL2LOCAL;
+
+
+struct carmel_mb_name {
+ char version[2]; /* \0 for version 1, ASCII digit and \0 for other */
+ char *user; /* String of userid for other user names */
+ char *mailbox; /* Mailbox name */
+};
+
+
+#define MC(x) (&(LOCAL->mc_blocks[(x) >> 8][(x) & 0xff]))
+#define LOCAL ((CARMEL2LOCAL *) stream->local)
+#define CARMEL_MAXMESSAGESIZE (20000000) /* 20Mb */
+#define CARMEL_MAX_HEADER (64000) /* 64K for DOS (someday?) */
+#define CARMEL_PATHBUF_SIZE (1024)
+#define CARMEL2_INDEX_BUF_SIZE (20000) /* Size for carmel2 index FILE buf */
+#define CARMEL_NAME_CHAR ('#') /* Separator for carmel names */
+#define CARMEL_NAME_PREFIX "#carmel" /* Prefix for all mailbox names */
+
+
+/* Kinds of paths that for carmel_calc_path */
+
+#define CalcPathCarmel2Index 1
+#define CalcPathCarmel2Data 2
+#define CalcPathCarmel2MAXNAME 3
+#define CalcPathCarmel2WriteLock 4
+#define CalcPathCarmel2ReadLock 5
+#define CalcPathCarmel2Expunge 6
+
+
+/* These are all relative to the users home directory */
+
+#define CARMEL2_INDEX_DIR "Carmel2Mail"
+#define CARMEL2_DIR "Carmel2Mail"
+#define CARMEL2_MSG_DIR "Carmel2Mail/.Messages"
+#define CARMEL2_MAXFILE "Carmel2Mail/.MAXNAME"
+
+
+#define BEZERKLOCKTIMEOUT 5
+
+/* Function prototypes */
+
+#ifdef ANSI
+DRIVER *carmel2_valid(char *);
+int carmel2_isvalid(char *);
+char *carmel2_file(char *, char *);
+void *carmel2_parameters();
+void carmel2_find(MAILSTREAM *, char *);
+void carmel2_find_bboards(MAILSTREAM *, char *);
+void carmel2_find_all(MAILSTREAM *, char *);
+void carmel2_find_all_bboards(MAILSTREAM *, char *);
+long carmel2_subscribe();
+long carmel2_unsubscribe();
+long carmel2_subscribe_bboard();
+long carmel2_unsubscribe_bboard();
+long carmel2_create(MAILSTREAM *, char *);
+long carmel2_delete(MAILSTREAM *, char *);
+long carmel2_rename(MAILSTREAM *, char *, char *);
+MAILSTREAM *carmel2_open(MAILSTREAM *);
+int carmel2_open2(MAILSTREAM *, char *);
+void carmel2_close(MAILSTREAM *);
+void carmel2_fetchfast(MAILSTREAM *, char *);
+void carmel2_fetchflags(MAILSTREAM *, char *);
+ENVELOPE *carmel2_fetchstructure(MAILSTREAM *, long, BODY **);
+char *carmel2_fetchheader(MAILSTREAM *, long);
+char *carmel2_fetchtext(MAILSTREAM *, long);
+char *carmel2_fetchbody(MAILSTREAM *, long, char *, unsigned long *);
+void carmel2_setflag(MAILSTREAM *, char *, char *);
+void carmel2_clearflag(MAILSTREAM *, char *, char *);
+void carmel2_search(MAILSTREAM *, char *);
+long carmel2_ping(MAILSTREAM *);
+void carmel2_check(MAILSTREAM *);
+void carmel2_expunge(MAILSTREAM *);
+long carmel2_copy(MAILSTREAM *, char *, char *);
+long carmel2_move(MAILSTREAM *, char *, char *);
+void carmel2_gc(MAILSTREAM *, long);
+void *carmel2_cache(MAILSTREAM *, long, long);
+long carmel2_append(MAILSTREAM *, char *, STRING *);
+int carmel2_write_index(ENVELOPE *, MESSAGECACHE *, FILE *);
+char *carmel2_readmsg(MAILSTREAM *, int, long, int);
+int carmel2_lock(CARMEL2LOCAL *, char *, int);
+void carmel2_unlock(CARMEL2LOCAL *, char *, int);
+int carmel2_update_lock(CARMEL2LOCAL *, char *, int);
+int carmel2_check_dir(char *);
+void carmel2_parse_bezerk_status(MESSAGECACHE *, char *);
+int carmel2_append2(MAILSTREAM *, CARMEL2LOCAL *, char *, char *,
+ char *, STRING *);
+char *month_abbrev2(int);
+void carmel2_rfc822_date(MESSAGECACHE *, char *);
+char *carmel_pretty_mailbox(char *);
+struct carmel_mb_name *
+ carmel_parse_mb_name(char *, char);
+void carmel_free_mb_name(struct carmel_mb_name *);
+int strucmp2(char *, char *);
+int struncmp2(char *, char *, int);
+
+#else /* ANSI */
+
+
+DRIVER *carmel2_valid();
+int carmel2_isvalid();
+char *carmel2_file();
+void *carmel2_parameters();
+void carmel2_find();
+void carmel2_find_bboards();
+void carmel2_find_all();
+void carmel2_find_all_bboards();
+long carmel2_subscribe();
+long carmel2_unsubscribe();
+long carmel2_subscribe_bboard();
+long carmel2_unsubscribe_bboard();
+long carmel2_create();
+long carmel2_delete();
+long carmel2_rename();
+MAILSTREAM *carmel2_open();
+int carmel2_open2();
+void carmel2_close();
+void carmel2_fetchfast();
+void carmel2_fetchflags();
+ENVELOPE *carmel2_fetchstructure();
+char *carmel2_fetchheader();
+char *carmel2_fetchtext();
+char *carmel2_fetchbody();
+void carmel2_setflag();
+void carmel2_clearflag();
+void carmel2_search();
+long carmel2_ping();
+void carmel2_check();
+void carmel2_expunge();
+long carmel2_copy();
+long carmel2_move();
+void carmel2_gc();
+void *carmel2_cache();
+long carmel2_append();
+int carmel2_write_index();
+char *carmel2_readmsg();
+int carmel2_lock();
+void carmel2_unlock();
+int carmel2_update_lock();
+int carmel2_check_dir();
+void carmel2_parse_bezerk_status();
+int carmel2_append2();
+char *month_abbrev2();
+void carmel2_rfc822_date();
+char *carmel_pretty_mailbox();
+struct carmel_mb_name *
+ carmel_parse_mb_name();
+void carmel_free_mb_name();
+int strucmp2();
+int struncmp2();
+
+#endif /* ANSI */
diff --git a/contrib/carmel/doc/carmel-driver b/contrib/carmel/doc/carmel-driver
new file mode 100644
index 00000000..aa157cf8
--- /dev/null
+++ b/contrib/carmel/doc/carmel-driver
@@ -0,0 +1,157 @@
+
+
+Carmel driver notes
+-------------------
+Set folder-collections=#carmel#[] to configure Pine to use the
+carmel driver. You may include other colllections like a directory full
+of Berkeley mail folder
+
+
+
+Be sure you don't have a file called ".mailboxlist" in your home
+directory. If you do the list of mailboxes presented to be by
+the c-client will be out of that file rather than by querying each
+driver.
+
+If you have files in your home or mail directory, be sure they aren't
+called anything that starts with "#carmel#" or %carmel%. They will be
+incorrectly included by mail_find for the bezerk and other drivers.
+
+The carmel driver doesn't grab "inbox". This is to allow other drivers
+a shot at it. This may or may not work out in the long run. With the
+current version of Pine you must set your inbox-path=#carmel#inbox to
+cause the carmel inbox to be used.
+
+
+Patching/Compilation for Pine
+-----------------------------
+
+This set of files and patches was created for Pine 3.85 and the
+corresponding version 3.0 of the c-client and 7.63 of imapd. Hopefully
+they will continue to work with future versions of all of these. To
+apply the patches copy all the files in the subdirectories here (pine,
+c-client and imapd) to the corresponding directories in the Pine
+source. The run patch on each of the patch files and finally run the
+build script in Pine source root directory. This should result in a
+pine, pico and imapd binary in the bin directory ready to be
+installed. In the c-client directory there will be a bzk2cml binary
+for converting a Berkeley format mailbox to a Carmel format mailbox
+for the user running the program.
+
+The modifications to the Pine source are minimal. The two parts are
+the calls to link in the carmel driver, and the patches to display
+BWC-GLYPH text. Nearly all the patches are for the later. One
+additional patch is needed to specify the right driver for creating
+new mailboxes. This is currently a bit of a hack in Pine and will
+hopefully be fixed future versions of Pine. The patches to imapd are
+of exactly the same nature as those to Pine.
+
+If some of these patches fail, it's probably due to changes made in
+Pine (applying them to some other version than 3.85). In that cases
+the patches will have to be made manually.
+
+
+
+Fixed since the version of Sept '92.
+------------------------------------
+* Updated to conform with the changes in UW internal changes from
+ version 3.5X to 3.84. With these changes all the internal Pine calls
+ to the c-client are stabilized, possibly except some changes for
+ mail_create. This makes the carmel driver trivial to integrate with
+ future versions of Pine.
+
+* Driver now checkpoints both carmel and carmel2 indexes. This should
+ save the nightly from deleting mail that it shouldn't.
+
+* The driver only defaults messages without any content-type to
+ X-bwc-glyph instead of messages that were tagged text/plain. This will
+ cause the the glyph to richtext translations not to be applied when
+ the message already has a MIME header.
+
+* Some bug fixes to display of BWC-GLYPH text, including bug causing
+ periodic core dumps.
+
+* Handles case where data file for a message is missing properly
+
+* Bug fixes so critical code in driver is properly protected from
+ signals and interrupts.
+
+* Bzk2cml utility included to convert Berkeley mail folder into carmel
+ folders.
+
+* Fixed bug causing text of currently viewed message to be redisplayed
+ incorrectly after the arrival of new mail.
+
+* Carmel text served up by imapd is tagged type "plain" instead of
+ "X-bwc-glyph", though the text is not yet converted to richtext
+ and quoted-printable encoded like it should be to work with other
+ clients.
+
+
+Fixed in the Aug 1 version
+--------------------------
+* Only creates a maximum of four Carmel index backup files (COD.FUR files)
+
+* Folder listing now works with standard IMAP patterns
+
+* Carmel index is check-pointed on expunges
+
+* Carmel driver checks for new mail with folder name #carmel#inbox
+
+* New mail is incorporated on other than inbox
+
+* MIME decoding works properly now
+
+* Safer incorporation of new mail from /usr/spool/mail (carmel2 writes flushed)
+
+
+Fixed in Aug 23 version
+-----------------------
+
+* Fetchheader includes the separator so append will work
+
+* Fixed append to get size right and not insert random NULL's, \n's...
+
+* Conversion of glyph format to rich text in imapd
+
+
+Fixed in Aug 29 version
+-----------------------
+* Further fixes to append, including full locking
+
+* The #carmel#user#folder syntax now works properly
+
+* Further fixes to imapd to convert to richtext only when appropriate
+
+
+Fixed in Sep 2 version
+----------------------
+* Nearly complete conversion from glyph to richtext
+
+* Fixed bug causing lost new mail on quick in/out Pine sessions
+
+* A few related Pine bugs fixed: sorting, wrapping
+
+
+Fixed in Sep 25 version
+-----------------------
+
+* Recognizes all messages with subtype beginning with x-bwc-glyph as Glyph text.
+* Saves messages of type x-bwc-glyph-wide in files ending with .wid
+
+* When saving messages, the link is used rather than making a new file
+
+* Glyph messages after 1994 must have the x-bwc-glyph tag to be recognized
+
+* Bug fix to properly display '@' in glyph text
+
+* Incorporation of latest version of c-client (minor bug fixes)
+
+
+
+General Features
+----------------
+* The driver checks the MAIL environment variable for the place to
+look for new incoming mail for the "inbox".
+
+(There's a lot more than this, but it's not documented yet)
diff --git a/contrib/carmel/doc/todo b/contrib/carmel/doc/todo
new file mode 100644
index 00000000..acf99f19
--- /dev/null
+++ b/contrib/carmel/doc/todo
@@ -0,0 +1,43 @@
+Legend: P - Pine, C - Carmel, W - BWC specific hacks, I - Imapd
+ Capitial letters are bugs, small are feature additions
+
+Size is in days it will take LL to accomplish. A * means the UW or
+other are interested and might actually do this for us if we wait.
+
+Size Where What
+---- ----- -------------
+ 3 c Make readonly mode parameters work
+ 3 C Occaisional problem/error message with Fcc
+ 6 W Copying/recognizing wide documents as wide (a new mime type?)
+ 2 b Can't handle _ in folder names
+ 4 c Testing to make sure all works in disk full conditions
+ 1 c Quick hacks to work with ECS mail
+ 5* i Imapd modifications to work with ECS mail properly
+ 2 b/p A default Bcc to oneself instead of Fcc (can you Fcc:inbox?)
+ 2* p Better handling of non-recognized attachment
+ 5 w Printing wide documents
+ 2 W Problems with glyph text column alignment for torture test
+15 Gateway to convert glyph to standard format as leaving BWC
+ 3 c Initial account set up
+10 c Kiss of death to steal write access from another running Pine
+ 6 C Full IMAP searching in Carmel (not used by Pine yet)
+ 6 c Carmel driver performance enhancments (for folders > 500 msgs)
+ 6* ci Auto updating deleted status for folder open by several users
+ 3 C Doesn't incorporate new mail on start up like other IMAPware
+ 6 I/C Handling of non-FQN's via IMAP (will solve ECS problems above)
+
+Things fixed for Aug 31 version
+-------------------------------
+- fixed bug with 1994 cut off date for forcing Carmel mail to x-bwc-glyph
+- fixed bugs in text header searching
+- changed to new c-client way of looking up hostname
+- works with new c-client for remote creates with FQNs
+- now works with new c-client scheme for including new mail file drivers
+- integrated x-bwc-glyph stuff with Pine 3.90
++ (Fixed in by UW) Handles passwords for multiple imap servers/accounts
+- Implemented TRYCREATE protocol for IMAP copies and appends
+
+Previous fixes
+--------------
+- Better handling of case of full disk
+
diff --git a/contrib/carmel/imapd/imapd.c.patch.old b/contrib/carmel/imapd/imapd.c.patch.old
new file mode 100644
index 00000000..16ed2981
--- /dev/null
+++ b/contrib/carmel/imapd/imapd.c.patch.old
@@ -0,0 +1,110 @@
+*** imapd.c Sun Jul 18 21:48:29 1993
+--- imapd.c.new Sun Jul 18 21:47:53 1993
+***************
+*** 109,115 ****
+--- 109,119 ----
+ extern DRIVER bezerkdriver,tenexdriver,imapdriver,newsdriver,nntpdriver,
+ dummydriver;
+
++ #ifdef BWC
++ extern DRIVER carmeldriver, carmel2driver;
++ #endif
+
++
+ /* Function prototypes */
+
+ void main ();
+***************
+*** 135,140 ****
+--- 139,148 ----
+ long cstring ();
+ long caddr ();
+
++ #ifdef BWC
++ char *glyph2richtext();
++ #endif
++
+ extern char *crypt ();
+
+ /* Main program */
+***************
+*** 147,152 ****
+--- 155,164 ----
+ char *s,*t = "OK",*u,*v;
+ struct hostent *hst;
+ void (*f) () = NIL;
++ #ifdef BWC
++ mail_link (&carmeldriver);
++ mail_link (&carmel2driver);
++ #endif
+ mail_link (&tenexdriver); /* install the Tenex mail driver */
+ mail_link (&bezerkdriver); /* install the Berkeley mail driver */
+ mail_link (&imapdriver); /* install the IMAP driver */
+***************
+*** 164,170 ****
+--- 176,186 ----
+ state = SELECT; /* enter select state */
+ t = "PREAUTH"; /* pre-authorized */
+ }
++ #ifdef BWC
++ printf ("* %s %s IMAP2bis Service %sBWC at %s\015\012",t,host,version,cmdbuf);
++ #else
+ printf ("* %s %s IMAP2bis Service %s at %s\015\012",t,host,version,cmdbuf);
++ #endif
+ fflush (stdout); /* dump output buffer */
+ signal (SIGALRM,clkint); /* prepare for clock interrupt */
+ signal (SIGUSR2,kodint); /* prepare for Kiss Of Death */
+***************
+*** 782,788 ****
+--- 798,817 ----
+ if (body && (s = mail_fetchbody (stream,i,s,&j))) {
+ /* and literal string */
+ printf ("{%d}\015\012",j);
++ #ifdef BWC_NOT_WORKING_YET
++ {
++ char *ss;
++ s[j] = '\0';
++ s = glyph2richtext(s);
++ j = strlen(s);
++ ss = rfc822_8bit(s, j, &j);
++ fs_give((void **)&s);
++ while (j -= k) k = fwrite (ss += k,1,j,stdout);
++ fs_give((void **)&ss);
++ }
++ #else
+ while (j -= k) k = fwrite (s += k,1,j,stdout);
++ #endif
+ changed_flags (i,f); /* output changed flags */
+ }
+ else fputs ("NIL",stdout); /* can't output anything at all */
+***************
+*** 983,995 ****
+--- 1012,1039 ----
+ else { /* non-multipart body type */
+ pstring ((char *) body_types[body->type]);
+ putchar (' ');
++ #ifdef BWC
++ if(body->type == TYPETEXT && strcmp(lcase(body->subtype), "x-bwc-glyph") == 0)
++ pstring("plain"); /* Make it plain now, richtext later when fixed */
++ else
++ pstring (body->subtype);
++ #else
+ pstring (body->subtype);
++ #endif
+ if (param = body->parameter) {
+ fputs (" (",stdout);
+ do {
+ pstring (param->attribute);
+ putchar (' ');
++ #ifdef BWC_NOT_WORKING_YET
++ if(strucmp2(param->attribute, "charset") == 0) {
++ pstring("ISO-8859-1");
++ } else {
++ pstring (param->value);
++ }
++ #else
+ pstring (param->value);
++ #endif
+ if (param = param->next) putchar (' ');
+ } while (param);
+ fputs (") ",stdout);
diff --git a/contrib/carmel/pine/filter.c.patch b/contrib/carmel/pine/filter.c.patch
new file mode 100644
index 00000000..094fc9ae
--- /dev/null
+++ b/contrib/carmel/pine/filter.c.patch
@@ -0,0 +1,409 @@
+*** filter.c Fri Aug 5 18:44:21 1994
+--- filter.c.new Wed Aug 31 18:31:47 1994
+***************
+*** 1937,1939 ****
+--- 1937,2340 ----
+
+ fflush(stdout);
+ }
++
++
++ #ifdef BWC
++ #ifdef NODEF
++ /*-*/
++ #include <stdio.h>
++ #include <sys/types.h>
++ #include <sys/stat.h>
++ #include <sys/file.h>
++ struct stat buf;
++ char *malloc();
++ char *glyph2richtext();
++ int convert2graph();
++ #endif
++
++ char *glyph2richtext();
++
++
++ char *gf_glyph2richtext(f, c, flg)
++ FILTER_S *f;
++ int c, flg;
++ {
++ char *line, *p;
++ if(flg == GF_RESET){
++ f->linep = f->line = (char *)fs_get(1000 * sizeof(char ));
++
++ } else if(flg == GF_DATA) {
++ if(c == '\n' && *(f->linep - 1) == '\r'){
++ *(f->linep)++ = '\n'; /* Tie of line */
++ *(f->linep) = '\0'; /* Tie of line */
++ line = glyph2richtext(f->line);
++ for(p = line; *p; p++)
++ (*f->next->f)(f->next, *p, GF_DATA);
++ fs_give((void **)&line);
++ f->linep = f->line;
++ } else {
++ *(f->linep)++ = c;
++ }
++ } else if(flg == GF_EOD) {
++ *(f->linep) = '\0'; /* Tie of line */
++ line = glyph2richtext(f->line);
++ for(p = line; *p; p++)
++ (*f->next->f)(f->next, *p, GF_DATA);
++ fs_give((void **)&line);
++ f->linep = f->line;
++
++ fs_give((void **)&(f->line));
++ (*f->next->f)(f->next, 0 , GF_EOD);
++ }
++ }
++
++
++ char *glyph2richtext(textin)
++ char *textin;
++ {
++ register char *p, *textout;
++ int stack=10, underline=0, bold=0, graphics=0,i=0,two_underline=0 ;
++ int memory_aloc=0;
++ char stack_str[10][20];
++
++ memory_aloc = max(strlen(textin)*8, 80);
++ textout = malloc(memory_aloc+1);
++ *textout = '\0';
++ dprint(9, (debugfile, "GLYPH2RICH strlen: %d allocated: %d\n",
++ strlen(textin), memory_aloc + 1));
++
++ while (stack--) stack_str[stack][0] = '\0'; /*Initialize the array */
++ stack=0;
++
++ for(p=textout; *textin;){ /*everything is done in the loop */
++
++ if ((p -textout) >(memory_aloc -200)){ /*allocate more memory before */
++ realloc(textout,(memory_aloc +=1024)); /*we ran out of it */
++ dprint(9, (debugfile, "GLYPH2RICH left: %d reallocated: %d\n",
++ p - textout, memory_aloc));
++ }
++
++ /*======= THE LINE AND PAGE BREAKS =======*/
++ if(*textin == '\r' && *(textin+1)=='\n'){ /*---- LINE BREAK -----*/
++ while (stack--){ /*End attributes*/
++ sprintf(p, "</%s>", stack_str[stack]);
++ p += strlen(p);
++ stack_str[stack][0] = '\0';
++ }
++ if(two_underline) { /* In case '_' occured near end of line */
++ strcpy(p, "<\\underline>");
++ p += strlen(p);
++ }
++
++ strcpy(p,"\r\n\r\n"); /*Add richtext newline */
++ p += strlen(p);
++ textin += 2;
++
++ stack=underline=bold=graphics=two_underline=0;
++ continue;
++ }
++
++ if( *textin == '{' && /*-------- PAGE BREAK --------*/
++ *(textin+1) == '~'){
++ strcpy(p,"<np>");
++ p += strlen(p);
++ textin +=2;
++ continue;
++ }
++
++ /*==================== ATTRIBUTES ===============*/
++
++ if(*textin == '_'){ /*------next two characters are underlined-----*/
++ if(underline){
++ *textin++; /*skip if already underlined*/
++ } else {
++ two_underline=2;
++ strcpy(p,"<underline>");
++ p += strlen(p);
++ *textin++;
++ }
++ continue;
++ }
++
++ if( *textin == '{' &&
++ *(textin+1) == '\\'){ /*Some type of attribute found*/
++ textin += 2;
++
++ while (stack--){ /*End previous attributes*/
++ sprintf(p, "</%s>", stack_str[stack]);
++ p += strlen(p);
++ stack_str[stack][0] = '\0';
++ }
++
++ stack=underline=bold=0;
++ if ( (*textin - 0x20) & 0x01){
++ strcpy(stack_str[stack++],"bold"); /*bold*/
++ bold=1;
++ }
++ if (((*textin - 0x20) >> 1) & 0x01){ /*underline*/
++ strcpy(stack_str[stack++],"underline");
++ underline=1;
++ }
++ if (((*textin - 0x20) >> 2) & 0x01){ /*blinking*/
++ if(bold) strcpy(stack_str[stack++],"superscript");
++ else if (underline) strcpy(stack_str[stack++],"subscript");
++ else strcpy(stack_str[stack++],"no-op"); /*can't do blinking alone!*/
++ }
++ if (((*textin - 0x20) >> 3) & 0x01){ /*reverse video*/
++ strcat(stack_str[stack++],"italic"); /*Italic on paper*/
++ }
++ if (((*textin - 0x20) >> 4) & 0x01){ /*graphics*/
++ graphics=1;
++ } else graphics=0;
++
++
++ for(i=0; i<stack; i++){ /*write the staring attributes*/
++ sprintf(p, "<%s>", stack_str[i]);
++ p += strlen(p);
++ }
++ textin++;
++ continue;
++ }
++
++ /*========== SPECIAL CHARACTERS ===========*/
++ if(two_underline){
++ /* Time to turn off underline? */
++ if(--two_underline ==0 && underline == 0){
++ strcpy(p,"</underline>");
++ p += strlen(p);
++ }
++ }
++
++ if(*textin == '<'){
++ /*add richtext smaller sign */
++ strcpy(p,"<lt>");
++ p += strlen(p);
++ *textin++;
++
++ } else if(graphics){ /*convert graphics */
++ if ( *textin == 'j' ||
++ *textin == 'k' ||
++ *textin == 'l' ||
++ *textin == 'm' ||
++ *textin == 'n' ||
++ *textin == 't' ||
++ *textin == 'u' ||
++ *textin == 'v' ||
++ *textin == 'w' ) { *p = '+'; *textin++;}
++ else if ( *textin == 'q' ) { *p = '-'; *textin++;}
++ else if ( *textin == 'x' ) { *p = '|'; *textin++;}
++ else *p= *textin++;
++ p++;
++ } else if(*textin == '^'){ /* Accent (similar to \a notation)*/
++ textin++;
++ if (*textin == 'A' ){*p='\301'; textin++;}
++ else if(*textin == 'E' ){*p='\311'; textin++;}
++ else if(*textin == 'I' ){*p='\315'; textin++;}
++ else if(*textin == 'O' ){*p='\323'; textin++;}
++ else if(*textin == 'U' ){*p='\332'; textin++;}
++ else if(*textin == 'a' ){*p='\341'; textin++;}
++ else if(*textin == 'e' ){*p='\351'; textin++;}
++ else if(*textin == 'i' ){*p='\355'; textin++;}
++ else if(*textin == 'o' ){*p='\363'; textin++;}
++ else if(*textin == 'u' ){*p='\372'; textin++;}
++ else *p= '^';
++ *++p = '\0';
++
++ } else if(*textin == '|') { /* Dot under letter */
++ textin++;
++ switch(*textin) {
++ case 'd':
++ case 'D':
++ case 's':
++ case 'S':
++ case 'T':
++ case 't':
++ case 'Z':
++ case 'z':
++ *p = *textin++; break;
++ default:
++ *p = '|'; break;
++ }
++ *++p = '\0';
++
++ } else if( *textin == '\\'){ /* Decode \ notation for 8 bit chars */
++ switch(*(textin+1)){
++ case '@':
++ *p = '@'; textin+=2; /* *textin++;*textin++; */
++ break;
++ case '\\': /* back slash*/
++ *p = '\\'; textin+=2;
++ break;
++ case '!': /* reverse exclaimation */
++ *p = '\241'; textin+=2;
++ break;
++ case '#': /* superscript 1 2 3 */
++ if (*(textin+2) == '1' ){*p='\271'; textin += 3;}
++ else if(*(textin+2) == '2' ){*p='\262'; textin += 3;}
++ else if(*(textin+2) == '3' ){*p='\263'; textin += 3;}
++ else *p= *textin++;
++ break;
++ case '$': /* cent*/
++ *p = '\242'; textin+=2;
++ break;
++ case '&': /* ligature*/
++ *p = '\247'; textin+=2;
++ break;
++ case '*': /* crossed o*/
++ *p = '\250'; textin+=2;
++ break;
++ case '-': /* minus*/
++ *p = '-'; textin+=2;
++ break;
++ case '.': /* degree*/
++ *p = '\260'; textin+=2;
++ break;
++ case '/': /* o slashed*/
++ if (*(textin+2) == 'O' ){*p='\330'; textin += 3;}
++ else if(*(textin+2) == 'o' ){*p='\370'; textin += 3;}
++ else *p= *textin++;
++ break;
++ case '<': /* double <*/
++ *p = '\253'; textin+=2;
++ break;
++ case 'n': /* back quote (ayn)*/
++ *p = '`'; textin+=2;
++ break;
++ case '>': /* double >*/
++ *p = '\273'; textin+=2;
++ break;
++ case '=': /* ligature*/
++ if (*(textin+2) == 'A' &&
++ *(textin+3) == 'E' ) {*p='\306'; *(textin+=4);}
++ else if(*(textin+2) == 'O' &&
++ *(textin+3) == 'E' ) {*p='\327'; *(textin+=4);}
++ else if(*(textin+2) == 'a' &&
++ *(textin+3) == 'e' ) {*p='\346'; *(textin+=4);}
++ else if(*(textin+2) == 'o' &&
++ *(textin+3) == 'e' ) {*p='\367'; *(textin+=4);}
++
++ else *p= *textin++;
++ break;
++ case '?': /* reverse question mark*/
++ *p = '\277'; textin+=2;
++ break;
++ case 'B': /* german SS (B)*/
++ *p = '\337'; textin+=2;
++ break;
++ case 'C': /* underlined a & o */
++ if (*(textin+2) == 'a' ){*p='\252'; textin += 3;}
++ else if(*(textin+2) == 'o' ){*p='\272'; textin += 3;}
++ else *p= *textin++;
++ break;
++ case 'H': /* One half (1/2) */
++ *p = '\275'; textin+=2;
++ break;
++ case 'P': /* paragrph marker */
++ *p = '\266'; textin+=2;
++ break;
++ case 'Q': /* One quarter (1/4)*/
++ *p = '\274'; textin+=2;
++ break;
++ case 'U': /* greek mu */
++ *p = '\265'; textin+=2;
++ break;
++ case 'Y': /* Yen */
++ *p = '\245'; textin+=2;
++ break;
++ case 'a': /* Accent*/
++ if (*(textin+2) == 'A' ){*p='\301'; textin += 3;}
++ else if(*(textin+2) == 'E' ){*p='\311'; textin += 3;}
++ else if(*(textin+2) == 'I' ){*p='\315'; textin += 3;}
++ else if(*(textin+2) == 'O' ){*p='\323'; textin += 3;}
++ else if(*(textin+2) == 'U' ){*p='\332'; textin += 3;}
++ else if(*(textin+2) == 'a' ){*p='\341'; textin += 3;}
++ else if(*(textin+2) == 'e' ){*p='\351'; textin += 3;}
++ else if(*(textin+2) == 'i' ){*p='\355'; textin += 3;}
++ else if(*(textin+2) == 'o' ){*p='\363'; textin += 3;}
++ else if(*(textin+2) == 'u' ){*p='\372'; textin += 3;}
++ else *p= *textin++;
++ break;
++ case 'c': /* C cedilla*/
++ if (*(textin+2) == 'C' ){*p='\307'; textin += 3;}
++ else if(*(textin+2) == 'c' ){*p='\347'; textin += 3;}
++ else *p= *textin++;
++ break;
++ case 'g': /* Grave */
++ if (*(textin+2) == 'A' ){*p='\300'; textin += 3;}
++ else if(*(textin+2) == 'E' ){*p='\310'; textin += 3;}
++ else if(*(textin+2) == 'I' ){*p='\314'; textin += 3;}
++ else if(*(textin+2) == 'O' ){*p='\322'; textin += 3;}
++ else if(*(textin+2) == 'U' ){*p='\331'; textin += 3;}
++ else if(*(textin+2) == 'a' ){*p='\340'; textin += 3;}
++ else if(*(textin+2) == 'e' ){*p='\350'; textin += 3;}
++ else if(*(textin+2) == 'i' ){*p='\354'; textin += 3;}
++ else if(*(textin+2) == 'o' ){*p='\362'; textin += 3;}
++ else if(*(textin+2) == 'u' ){*p='\371'; textin += 3;}
++ else *p= *textin++;
++ break;
++ case 'm': /* A angstrom*/
++ if (*(textin+2) == 'A' ){*p='\305'; textin += 3;}
++ else if(*(textin+2) == 'a' ){*p='\345'; textin += 3;}
++ else *p= *textin++;
++ break;
++ case 'p': /* pound sterling */
++ *p = '\243'; textin+=2;
++ break;
++ case 's': /* solidus */
++ *p = '\267'; textin+=2;
++ break;
++ case 't': /* Tilda */
++ if (*(textin+2) == 'A' ){*p='\303'; textin += 3;}
++ else if(*(textin+2) == 'N' ){*p='\321'; textin += 3;}
++ else if(*(textin+2) == 'O' ){*p='\325'; textin += 3;}
++ else if(*(textin+2) == 'a' ){*p='\343'; textin += 3;}
++ else if(*(textin+2) == 'n' ){*p='\361'; textin += 3;}
++ else if(*(textin+2) == 'o' ){*p='\365'; textin += 3;}
++ else *p= *textin++;
++ break;
++ case 'u': /* Umlaut */
++ if (*(textin+2) == 'A' ){*p='\304'; textin += 3;}
++ else if(*(textin+2) == 'E' ){*p='\313'; textin += 3;}
++ else if(*(textin+2) == 'I' ){*p='\317'; textin += 3;}
++ else if(*(textin+2) == 'O' ){*p='\326'; textin += 3;}
++ else if(*(textin+2) == 'U' ){*p='\334'; textin += 3;}
++ else if(*(textin+2) == 'Y' ){*p='\335'; textin += 3;}
++ else if(*(textin+2) == 'a' ){*p='\344'; textin += 3;}
++ else if(*(textin+2) == 'e' ){*p='\353'; textin += 3;}
++ else if(*(textin+2) == 'i' ){*p='\357'; textin += 3;}
++ else if(*(textin+2) == 'o' ){*p='\366'; textin += 3;}
++ else if(*(textin+2) == 'u' ){*p='\374'; textin += 3;}
++ else if(*(textin+2) == 'y' ){*p='\375'; textin += 3;}
++ else *p= *textin++;
++ break;
++ case 'x': /* Circumflex */
++ if (*(textin+2) == 'A' ){*p='\302'; textin += 3;}
++ else if(*(textin+2) == 'E' ){*p='\312'; textin += 3;}
++ else if(*(textin+2) == 'I' ){*p='\316'; textin += 3;}
++ else if(*(textin+2) == 'O' ){*p='\324'; textin += 3;}
++ else if(*(textin+2) == 'U' ){*p='\333'; textin += 3;}
++ else if(*(textin+2) == 'a' ){*p='\342'; textin += 3;}
++ else if(*(textin+2) == 'e' ){*p='\352'; textin += 3;}
++ else if(*(textin+2) == 'i' ){*p='\356'; textin += 3;}
++ else if(*(textin+2) == 'o' ){*p='\364'; textin += 3;}
++ else if(*(textin+2) == 'u' ){*p='\373'; textin += 3;}
++ else *p= *textin++;
++ break;
++ default:
++ textin +=2; /* Skip the \x and ignore it */
++ *p = *textin++; /* Copy third character in group */
++ break;
++ } /*End switch */
++ p++;
++ } else{
++ *p++ = *textin++; /* Finally a plain ordinary ASCII character */
++ }
++ } /* End for loop over string */
++
++ *p=0; /*end of string or file */
++
++ return textout;
++ }
++
++ #endif
++
diff --git a/contrib/carmel/pine/mailview.c.patch b/contrib/carmel/pine/mailview.c.patch
new file mode 100644
index 00000000..5dc9c9af
--- /dev/null
+++ b/contrib/carmel/pine/mailview.c.patch
@@ -0,0 +1,39 @@
+*** mailview.c Wed Aug 17 17:19:56 1994
+--- mailview.c.new Wed Aug 31 19:04:19 1994
+***************
+*** 132,139 ****
+--- 132,147 ----
+ void redraw_scroll_text();
+ #endif
+
++ #ifdef BWC
++ #ifdef ANSI
++ void gf_glyph2richtext(FILTER_S *, int, int);
++ #else
++ void gf_glyph2richtext();
++ #endif
++ #endif
+
+
++
+ /*----------------------------------------------------------------------
+ Format a buffer with the text of the current message for browser
+
+***************
+*** 892,897 ****
+--- 900,914 ----
+ filter_t aux_filter[4];
+ int filtcnt = 0, error_found = 0;
+ char *err;
++
++ #ifdef BWC
++ if(att->body->subtype != NULL &&
++ strucmp(att->body->subtype, "X-bwc-glyph") == 0) {
++ aux_filter[filtcnt++] = gf_glyph2richtext;
++ gf_enriched2plain_opt(0); /* don't strip everything! */
++ aux_filter[filtcnt++] = gf_enriched2plain;
++ }
++ #endif
+
+ if(att->body->subtype){
+ if(!strucmp(att->body->subtype, "richtext")) {
diff --git a/contrib/carmel/pine/makefile.sun.patch b/contrib/carmel/pine/makefile.sun.patch
new file mode 100644
index 00000000..cab73d4a
--- /dev/null
+++ b/contrib/carmel/pine/makefile.sun.patch
@@ -0,0 +1,19 @@
+*** makefile.sun Mon Jul 25 15:21:59 1994
+--- makefile.sun.new Wed Aug 31 19:08:25 1994
+***************
+*** 59,65 ****
+ MAKE= make
+ OPTIMIZE= # -O
+ PROFILE= # -pg
+! DEBUG= -g -DDEBUG
+
+ IMAPDIR= ../c-client
+ PICODIR= ../pico
+--- 59,65 ----
+ MAKE= make
+ OPTIMIZE= # -O
+ PROFILE= # -pg
+! DEBUG= -DBWC -g -DDEBUG
+
+ IMAPDIR= ../c-client
+ PICODIR= ../pico
diff --git a/contrib/carmel/pine/makefile.ult.patch b/contrib/carmel/pine/makefile.ult.patch
new file mode 100644
index 00000000..a50e504d
--- /dev/null
+++ b/contrib/carmel/pine/makefile.ult.patch
@@ -0,0 +1,19 @@
+*** makefile.ult Mon Jul 25 15:22:01 1994
+--- makefile.ult.new Wed Aug 31 18:19:59 1994
+***************
+*** 67,73 ****
+ LIBES= -ltermlib -lauth
+ LOCLIBES= $(PICODIR)/libpico.a $(IMAPDIR)/c-client.a
+
+! CFLAGS= -DULT $(OPTIMIZE) $(PROFILE) $(DEBUG) -DSYSTYPE=\"ULT\"
+
+ obj= addrbook.o adrbklib.o args.o context.o filter.o \
+ folder.o help.o helptext.o imap.o init.o mailcap.o mailcmd.o \
+--- 67,73 ----
+ LIBES= -ltermlib -lauth
+ LOCLIBES= $(PICODIR)/libpico.a $(IMAPDIR)/c-client.a
+
+! CFLAGS= -DBWC -DULT $(OPTIMIZE) $(PROFILE) $(DEBUG) -DSYSTYPE=\"ULT\"
+
+ obj= addrbook.o adrbklib.o args.o context.o filter.o \
+ folder.o help.o helptext.o imap.o init.o mailcap.o mailcmd.o \
diff --git a/contrib/keypad.enable/ReadMe b/contrib/keypad.enable/ReadMe
new file mode 100644
index 00000000..927519ce
--- /dev/null
+++ b/contrib/keypad.enable/ReadMe
@@ -0,0 +1,7 @@
+These context diffs when applied to pico/tcap.c enable entrance
+and exit of "keypad mode". This mode is especially useful (and
+necessary!) for HP terminals which require this mode to make the
+arrow keys useful.
+
+These changes are based on a bug report submitted by Jochiam Richter
+(zjr@rz.uni-jena.de).
diff --git a/contrib/keypad.enable/keypad.enable.diff b/contrib/keypad.enable/keypad.enable.diff
new file mode 100644
index 00000000..80643f0d
--- /dev/null
+++ b/contrib/keypad.enable/keypad.enable.diff
@@ -0,0 +1,122 @@
+*** pine/ttyout.c.REAL Thu Dec 21 02:08:20 1995
+--- pine/ttyout.c Sat Feb 10 15:47:12 1996
+***************
+*** 131,137 ****
+ *_startinsert, *_endinsert, *_insertchar, *_deletechar,
+ *_deleteline, *_insertline,
+ *_scrollregion, *_scrollup, *_scrolldown,
+! *_termcap_init, *_termcap_end;
+ char term_name[40];
+ #ifndef USE_TERMINFO
+ static char _terminal[1024]; /* Storage for terminal entry */
+--- 131,138 ----
+ *_startinsert, *_endinsert, *_insertchar, *_deletechar,
+ *_deleteline, *_insertline,
+ *_scrollregion, *_scrollup, *_scrolldown,
+! *_termcap_init, *_termcap_end,
+! *_keypad_init, *_keypad_end;
+ char term_name[40];
+ #ifndef USE_TERMINFO
+ static char _terminal[1024]; /* Storage for terminal entry */
+***************
+*** 228,233 ****
+--- 229,236 ----
+ _termcap_end = tigetstr("rmcup");
+ _lines = tigetnum("lines");
+ _columns = tigetnum("cols");
++ _keypad_init = tigetnum("smkx");
++ _keypad_end = tigetnum("rmkx");
+ _ku = tigetstr("kcuu1");
+ _kd = tigetstr("kcud1");
+ _kl = tigetstr("kcub1");
+***************
+*** 295,300 ****
+--- 298,305 ----
+ _termcap_end = tgetstr("te", &ptr);
+ _lines = tgetnum("li");
+ _columns = tgetnum("co");
++ _keypad_init = tgetstr("ks", &ptr);
++ _keypad_end = tgetstr("ke", &ptr);
+ _ku = tgetstr("ku", &ptr);
+ _kd = tgetstr("kd", &ptr);
+ _kl = tgetstr("kl", &ptr);
+***************
+*** 435,440 ****
+--- 440,448 ----
+ BeginScroll(0, ps_global->ttyo->screen_rows);
+ fflush(stdout);
+ }
++
++ if(_keypad_init)
++ tputs(_keypad_init, 1, outchar);
+ }
+
+
+***************
+*** 510,515 ****
+--- 518,526 ----
+ MoveCursor(_lines - 2, 0);
+ if(_termcap_end != NULL)
+ tputs(_termcap_end, 1, outchar);
++
++ if(_keypad_end != NULL)
++ tputs(_keypad_end, 1, outchar);
+
+ if(message){
+ StartInverse();
+*** pico/tcap.c.REAL Thu Dec 21 01:54:35 1995
+--- pico/tcap.c Sat Feb 10 16:08:47 1996
+***************
+*** 92,98 ****
+ *SF, /* scroll text up */
+ *SR, /* scroll text down */
+ *TI, /* string to start termcap */
+! *TE; /* string to end termcap */
+
+
+ TERM term = {
+--- 92,100 ----
+ *SF, /* scroll text up */
+ *SR, /* scroll text down */
+ *TI, /* string to start termcap */
+! *TE, /* string to end termcap */
+! *KS, /* string to enter application keypad mode */
+! *KE; /* string to end application keypad mode */
+
+
+ TERM term = {
+***************
+*** 178,183 ****
+--- 180,187 ----
+ SR = tgetstr("sr", &p);
+ TI = tgetstr("ti", &p);
+ TE = tgetstr("te", &p);
++ KS = tgetstr("ks", &p);
++ KE = tgetstr("ke", &p);
+
+ row = tgetnum("li");
+ if(row == -1){
+***************
+*** 399,404 ****
+--- 403,411 ----
+ if (CS)
+ putpad(tgoto(CS, term.t_nrow, 0)) ;
+ }
++
++ if(KS && !Pmaster) /* enter app/keypad mode (cursor) */
++ putpad(KS);
+ }
+
+
+***************
+*** 410,415 ****
+--- 417,425 ----
+
+ if(TE) /* any cleanup termcap requires */
+ putpad(TE);
++
++ if(KE)
++ putpad(KE); /* end app/keypad mode */
+ }
+
+ kbdestroy(pico_kbesc); /* clean up key board sequence trie */
diff --git a/contrib/ports/aos/README b/contrib/ports/aos/README
new file mode 100644
index 00000000..6503853f
--- /dev/null
+++ b/contrib/ports/aos/README
@@ -0,0 +1,20 @@
+From tenser@snafu.eec.psu.edu Tue May 7 10:14:10 1996
+Date: Tue, 16 Apr 1996 14:57:45 -0000
+From: Dan Cross <tenser@snafu.eec.psu.edu>
+To: pine@cac.washington.edu
+Subject: Pine patches for the IBM RT (Pine 3.93, 4.3BSD)
+
+Hi there!
+ I ported pine 3.93 to the IBM RT running AOS using the BSD port. (AOS
+is IBM's port of 4.3BSD to the RT architecture.) There were only minor
+changes that had to be made to the source to get it running under AOS, so
+I just stuck with the BSD port. (It seemed like a waste to change, since
+there was so much duplication between the BSD port and an AOS port.)
+ I wanted to send back the patches to you all to get them re-integrated
+into the main distribution so that the RT becomes a supported architecture.
+So, without further inane babble by myself, here they are. Thanks!
+
+ - Dan C.
+
+(ps- if you would like my binaries to put up for FTP, let me know and I'll
+pack them up for ya'll!)
diff --git a/contrib/ports/aos/aos.diff b/contrib/ports/aos/aos.diff
new file mode 100644
index 00000000..a8858529
--- /dev/null
+++ b/contrib/ports/aos/aos.diff
@@ -0,0 +1,125 @@
+*** os_bsd.c.orig Mon Apr 15 17:46:29 1996
+--- pine3.93/imap/non-ANSI/c-client/os_bsd.c Mon Apr 15 14:02:14 1996
+***************
+*** 67,72 ****
+--- 67,74 ----
+ #include "memmove.c"
+ #include "strerror.c"
+ #include "strstr.c"
++ #ifndef ibm032
+ #include "strtol.c"
++ #endif /* !ibm032 */
+ #include "strtoul.c"
+ #include "tz_bsd.c"
+*** os_bsd.h.orig Mon Apr 15 17:46:31 1996
+--- pine3.93/imap/non-ANSI/c-client/os_bsd.h Mon Apr 15 13:45:22 1996
+***************
+*** 40,46 ****
+--- 40,48 ----
+ #include <fcntl.h>
+ #include <syslog.h>
+ #include <sys/file.h>
++ #ifndef ibm032
+ #include <machine/endian.h> /* needed for htons() prototypes */
++ #endif /* !ibm032 */
+
+
+ char *getenv ();
+*** tz_bsd.c.orig Mon Apr 15 17:46:32 1996
+--- pine3.93/imap/non-ANSI/c-client/tz_bsd.c Mon Apr 15 16:22:38 1996
+***************
+*** 42,47 ****
+--- 42,61 ----
+ char *s;
+ void *t;
+ {
++ #ifndef ibm032
+ /* append timezone from tm struct */
+ sprintf (s + strlen (s)," (%s)",((struct tm *) t)->tm_zone);
++ #else
++ struct timeval tv;
++ struct timezone tz;
++
++ (void)memset((char *)&tv, 0, sizeof(tv));
++ (void)memset((char *)&tz, 0, sizeof(tz));
++
++ if (gettimeofday(&tv, &tz) < 0)
++ return; /* Silent error. */
++
++ (void)sprintf(s + strlen(s), " (%s)",
++ timezone(tz.tz_minuteswest, tz.tz_dsttime));
++ #endif /* !ibm032 */
+ }
+*** other.c.orig Mon Apr 15 17:47:02 1996
+--- pine3.93/pine/other.c Mon Apr 15 15:18:02 1996
+***************
+*** 353,359 ****
+ pbuf.exittest = sigedit_exit_for_pico;
+ pbuf.upload = (ps_global->VAR_UPLOAD_CMD
+ && ps_global->VAR_UPLOAD_CMD[0])
+! ? upload_msg_to_pico : NULL;
+ pbuf.keybinit = init_keyboard;
+ pbuf.helper = helper;
+ pbuf.resize = resize_for_pico;
+--- 353,359 ----
+ pbuf.exittest = sigedit_exit_for_pico;
+ pbuf.upload = (ps_global->VAR_UPLOAD_CMD
+ && ps_global->VAR_UPLOAD_CMD[0])
+! ? (int(*)())upload_msg_to_pico : NULL;
+ pbuf.keybinit = init_keyboard;
+ pbuf.helper = helper;
+ pbuf.resize = resize_for_pico;
+*** send.c.orig Mon Apr 15 17:47:12 1996
+--- pine3.93/pine/send.c Mon Apr 15 15:36:46 1996
+***************
+*** 1656,1662 ****
+ #else
+ pbuf.upload = (ps_global->VAR_UPLOAD_CMD
+ && ps_global->VAR_UPLOAD_CMD[0])
+! ? upload_msg_to_pico : NULL;
+ #endif
+
+ pbuf.raw_io = Raw;
+--- 1656,1662 ----
+ #else
+ pbuf.upload = (ps_global->VAR_UPLOAD_CMD
+ && ps_global->VAR_UPLOAD_CMD[0])
+! ? (int (*)())upload_msg_to_pico : NULL;
+ #endif
+
+ pbuf.raw_io = Raw;
+*** tempfile.orig Mon Apr 15 17:48:22 1996
+--- pine3.93/pine/osdep/tempfile Mon Apr 15 15:39:52 1996
+***************
+*** 2,11 ****
+--- 2,31 ----
+ Create a temporary file, the name of which we don't care about
+ and that goes away when it is closed. Just like ANSI C tmpfile.
+ ----*/
++ #ifdef ibm032
++ #define FILENAMESIZE 20
++ #define FILETMPLATE "/tmp/ptmpXXXXXX"
++ #endif /* ibm032 */
++
+ FILE *
+ create_tmpfile()
+ {
++ #ifndef ibm032
+ return(tmpfile());
++ #else
++ char buf[FILENAMESIZE];
++ int fd;
++
++ (void)memset(buf, 0, FILENAMESIZE);
++ (void)strcpy(buf, FILETMPLATE);
++
++ if ((fd = mkstemp(buf)) < 0)
++ return(NULL);
++
++ (void)unlink(buf);
++
++ return(fdopen(fd, "w+"));
++ #endif /* !ibm032 */
+ }
+
+
diff --git a/contrib/ports/sequent_ptx_4.4.6 b/contrib/ports/sequent_ptx_4.4.6
new file mode 100644
index 00000000..064ed460
--- /dev/null
+++ b/contrib/ports/sequent_ptx_4.4.6
@@ -0,0 +1,188 @@
+
+Larry Mascarenhas <lmascare@cscinfo.com> found he had to make some changesx
+to get Pine 4.21 to compile on Sequent ptx v4.4.6. We didn't have time to
+integrate these into the distribution. He was kind enough to send us the
+diffs of what he had to do to make it work.
+
+
+
+diff -cr pine.dist/imap/src/osdep/unix/Makefile pine4.20/imap/src/osdep/unix/Makefile
+*** pine.dist/imap/src/osdep/unix/Makefile Thu Sep 30 01:54:13 1999
+--- pine4.20/imap/src/osdep/unix/Makefile Fri Nov 12 15:26:40 1999
+***************
+*** 418,424 ****
+ MAILSPOOL=/usr/mail \
+ RSHPATH=/usr/bin/resh \
+ BASECFLAGS="-Wc,-O3 -Wc,-seq -Dprivate=PRIVATE -DNFSKLUDGE" \
+! BASELDFLAGS="-lseq -lsec -lsocket -linet -lnsl -lgen" \
+ RANLIB=true
+
+ pyr: # Pyramid
+--- 418,424 ----
+ MAILSPOOL=/usr/mail \
+ RSHPATH=/usr/bin/resh \
+ BASECFLAGS="-Wc,-O3 -Wc,-seq -Dprivate=PRIVATE -DNFSKLUDGE" \
+! BASELDFLAGS="-lseq -lsec -lsocket -lnsl -lgen" \
+ RANLIB=true
+
+ pyr: # Pyramid
+diff -cr pine.dist/pico/makefile.ptx pine4.20/pico/makefile.ptx
+*** pine.dist/pico/makefile.ptx Mon Jun 29 18:23:52 1998
+--- pine4.20/pico/makefile.ptx Fri Nov 12 15:37:28 1999
+***************
+*** 42,48 ****
+ LIBARGS= ru
+ RANLIB= true
+
+! LIBS= $(EXTRALIBES) -ltermlib -linet
+
+ OFILES= attach.o basic.o bind.o browse.o buffer.o \
+ composer.o display.o file.o fileio.o line.o pico_os.o \
+--- 42,48 ----
+ LIBARGS= ru
+ RANLIB= true
+
+! LIBS= $(EXTRALIBES) -ltermlib -lsocket -l nsl
+
+ OFILES= attach.o basic.o bind.o browse.o buffer.o \
+ composer.o display.o file.o fileio.o line.o pico_os.o \
+diff -cr pine.dist/pine/cmplhelp.sh pine4.20/pine/cmplhelp.sh
+*** pine.dist/pine/cmplhelp.sh Fri Feb 20 19:50:38 1998
+--- pine4.20/pine/cmplhelp.sh Fri Nov 12 15:16:17 1999
+***************
+*** 49,55 ****
+ s/-sed-backslash-quote-/\\"/g
+ ' |
+
+! awk 'BEGIN {in_text = 0;
+ count = 0;
+ printf("#include <stdio.h>\n#include \"headers.h\"\n\n\n");
+ }
+--- 49,55 ----
+ s/-sed-backslash-quote-/\\"/g
+ ' |
+
+! nawk 'BEGIN {in_text = 0;
+ count = 0;
+ printf("#include <stdio.h>\n#include \"headers.h\"\n\n\n");
+ }
+diff -cr pine.dist/pine/cmplhlp2.sh pine4.20/pine/cmplhlp2.sh
+*** pine.dist/pine/cmplhlp2.sh Wed Feb 18 20:46:52 1998
+--- pine4.20/pine/cmplhlp2.sh Fri Nov 12 15:16:28 1999
+***************
+*** 44,50 ****
+ #
+
+
+! awk ' BEGIN { printf("\n\n\t\t/*\n");
+ printf("\t\t * AUTMATICALLY GENERATED FILE!\n");
+ printf("\t\t * DO NOT EDIT!!\n\t\t */\n\n\n");
+ printf("#define\tHelpType\tchar **\n");
+--- 44,50 ----
+ #
+
+
+! nawk ' BEGIN { printf("\n\n\t\t/*\n");
+ printf("\t\t * AUTMATICALLY GENERATED FILE!\n");
+ printf("\t\t * DO NOT EDIT!!\n\t\t */\n\n\n");
+ printf("#define\tHelpType\tchar **\n");
+diff -cr pine.dist/pine/mailtrfc.sh pine4.20/pine/mailtrfc.sh
+*** pine.dist/pine/mailtrfc.sh Fri Mar 15 02:14:43 1996
+--- pine4.20/pine/mailtrfc.sh Fri Nov 12 15:18:35 1999
+***************
+*** 47,53 ****
+
+
+
+! org=`awk '/^domain/ {print $2}' < /etc/resolv.conf`
+ domain=`echo $org | sed -e 's/^[^.]*\.//'`
+ host=`hostname`".$org"
+
+--- 47,53 ----
+
+
+
+! org=`nawk '/^domain/ {print $2}' < /etc/resolv.conf`
+ domain=`echo $org | sed -e 's/^[^.]*\.//'`
+ host=`hostname`".$org"
+
+***************
+*** 56,62 ****
+ echo "Hostname: $host"
+
+ sed -n -e '/message-id/s/^.*</</p' |
+! awk 'BEGIN {mailers[0] = "Other";
+ mailers[1] = "Pine";
+ mailers[2] = "MailManager";
+ mailers[3] = "sendmail";
+--- 56,62 ----
+ echo "Hostname: $host"
+
+ sed -n -e '/message-id/s/^.*</</p' |
+! nawk 'BEGIN {mailers[0] = "Other";
+ mailers[1] = "Pine";
+ mailers[2] = "MailManager";
+ mailers[3] = "sendmail";
+***************
+*** 115,121 ****
+
+
+ echo $host $org $domain | \
+! awk '{printf(" %.17s %.11s %.11s Off Campus Total\n", $1, $2, $3)}'
+ egrep -v 'TOTAL|----|^-->' /tmp/syslogx.$$ | sort +0.60rn
+ egrep 'TOTAL|----' /tmp/syslogx.$$
+ grep '^-->' /tmp/syslogx.$$ | sed -e 's/-->//' > other-traffic
+--- 115,121 ----
+
+
+ echo $host $org $domain | \
+! nawk '{printf(" %.17s %.11s %.11s Off Campus Total\n", $1, $2, $3)}'
+ egrep -v 'TOTAL|----|^-->' /tmp/syslogx.$$ | sort +0.60rn
+ egrep 'TOTAL|----' /tmp/syslogx.$$
+ grep '^-->' /tmp/syslogx.$$ | sed -e 's/-->//' > other-traffic
+diff -cr pine.dist/pine/send.c pine4.20/pine/send.c
+*** pine.dist/pine/send.c Wed Oct 6 16:18:27 1999
+--- pine4.20/pine/send.c Mon Nov 15 12:46:22 1999
+***************
+*** 50,56 ****
+ #include "../c-client/smtp.h"
+ #include "../c-client/nntp.h"
+
+-
+ #ifndef TCPSTREAM
+ #define TCPSTREAM void
+ #endif
+--- 50,55 ----
+***************
+*** 3933,3940 ****
+ if((ps_global->post->pid = fork()) == 0){
+ /*
+ * Put us in new process group...
+- */
+ setpgrp(0, ps_global->post->pid);
+
+ /* BUG: should fix argv[0] to indicate what we're up to */
+
+--- 3932,3940 ----
+ if((ps_global->post->pid = fork()) == 0){
+ /*
+ * Put us in new process group...
+ setpgrp(0, ps_global->post->pid);
++ */
++ setpgrp();
+
+ /* BUG: should fix argv[0] to indicate what we're up to */
+
+diff -cr pine.dist/pine/signals.c pine4.20/pine/signals.c
+*** pine.dist/pine/signals.c Tue Apr 20 20:25:18 1999
+--- pine4.20/pine/signals.c Mon Nov 15 12:46:59 1999
+***************
+*** 53,58 ****
+--- 53,59 ----
+ ====*/
+
+ #include "headers.h"
++ #undef sigmask
+
+ /*
+ * call used by TERM and HUP handlers to quickly close streams
diff --git a/contrib/ports/vms/readme.1st b/contrib/ports/vms/readme.1st
new file mode 100644
index 00000000..42652494
--- /dev/null
+++ b/contrib/ports/vms/readme.1st
@@ -0,0 +1,9 @@
+
+To build the VMS version of pine, just move the VMSBUILD.COM command
+script into the top level pine directory. If the script fails moving
+the pine-specific c-client build script (VMSBUILD_CCLIENT.COM) into
+place, just copy it by hand into the C-CLIENT.DIR it creates. This
+RENAME was added just prior to release by the pine team and was not
+part of the contributed port.
+
+- The Pine Team
diff --git a/contrib/ports/vms/readme.vms b/contrib/ports/vms/readme.vms
new file mode 100644
index 00000000..da14481f
--- /dev/null
+++ b/contrib/ports/vms/readme.vms
@@ -0,0 +1,102 @@
+ VMS readme for PINE/C-CLEINT/PICO
+ ========================
+
+Building
+========
+
+ There are three executables:
+Pico/PICO.EXE: This is the stand-alone version of the editor.
+C-Client/MTEST: Testing program for debugging purposes.
+Pine/PINE: The pine...
+
+In order to build SET DEF into the top directory (i.e. the one above PINE.DIR,
+PICO.DIR, etc.) and then @VMSBUILD. It will rename the C-CLient directory and
+then compile Pico, C-Client and Pine.
+
+Optional parameters to the VMSBUILD command:
+
+NETLIB - Use the Netlib library. It must be preloaded into [.NETLIB]NETLIB.OLB
+MULTINET - Call Multinet's transport directly.
+
+If more than one option is used - separate them with coma and no spaces.
+
+There are a few warnnings during the link - ignore them...
+On VAX we have to link the objects themselves and can't use libraries since the
+linker/librarian lose the global variables; on AXP it is ok...
+
+
+Using
+=====
+ All the user needs is the PINE.EXE; Pine reads the mail from the user's
+VMS/MAIL files and send outgoing mail either via mail routines using some
+foreign protocol or via direct SMTP to some SMTP server (I preffer this
+metod). You use the latter by defining SMTP-SERVER field with some host.
+If you do not set it you must define PINE_MAIL_PROTOCOL to the prefix of
+the foreign protocol used. For example, if you use SMTP% you have to define
+it to SMTP.
+ The global PINE configuration file (if needed) is located at UTIL$:PINE.CONF;
+if you want to recompile it with a different name then modify PINE/OS.H;
+
+
+Why TcpIp communication is needed?
+==================================
+ It is not really needed, but helps much. It is needed in three places:
+1. Sending mail: You can either send mail using the xxx% mechanism by defning
+ PINE_MAIL_PROTOCOL; in this case no TcpIp is needed.
+ you can use another mechanism: Don't define the above but set some SMTP
+ server node name in PINE.CONF or .PINERC. In this case you need some
+ SMTP package.
+2. PINE can read NEWS via the NNTP protocol which runs over TcpIp...
+3. Remote nodes (usually PC) can access the IMAP daemon and PINE can access
+ remote IMAP servers using TcpIp.
+
+
+Restrictions
+============
+1. In order to not modify the source too much the handling of the special
+ INBOX folder was not modified. Hence, it always try to open the (empty)
+ INBOX folder instead of NEWMAIL.
+ It is possible to define in the system's wide PINE.CONF that
+ inbox-path=NEWMAIL. In this case NEWMAIL will be opened when PINE is
+ started. However, the user must not then switch to another folder as long
+ as NEWMAIL has items.
+2. WASTEBASKET folder is not used.
+3. .PINERC and .ADDRBOOK are fixed to the user's login directory and cannot
+ be defined to be elsewhere (the definition is ignored).
+4-100. Probably exists and I forgot to mention :-)
+
+
+IMAPD
+=====
+ IMAPD of version 3.89 is available with the old Pine VMS port from
+VMS.HUJI.AC.IL; the current version of IMAPD will be ported soon.
+
+
+NETLIB
+======
+ NETLIB can be obtained from PUBLIC.TGV.COM:/MADISON/NETLIB. NETLIB supports
+all the common TcpIp packages like Multinet, UCX, Fusion, Wollongong, etc.
+
+
+Suggested PINE.CONF file.
+=========================
+here is the PINE.CONF file we use here:
+
+# Our fully-qualified machine name:
+user-domain=vms.huji.ac.il
+
+# Where to connect to send outputgoing mail.
+smtp-server=vms.huji.ac.il
+
+# Which viewer to see GIF/JPEG/etc.
+image-viewer=xv
+
+# Which folder will be opened automatically when entering PINE. See note above.
+inbox-path=NEWMAIL
+
+
+Notes:
+=====
+1. Due to the readonly definition in the source files we use an external
+ #define to redefine it to something else. Due to that CTYPE.H fails, so
+ we use a private copy of it.
diff --git a/contrib/ports/vms/vms_link.opt b/contrib/ports/vms/vms_link.opt
new file mode 100644
index 00000000..0f454490
--- /dev/null
+++ b/contrib/ports/vms/vms_link.opt
@@ -0,0 +1 @@
+SYS$SHARE:VAXCRTL/SHARE
diff --git a/contrib/ports/vms/vms_multinet_link.opt b/contrib/ports/vms/vms_multinet_link.opt
new file mode 100644
index 00000000..5eb99104
--- /dev/null
+++ b/contrib/ports/vms/vms_multinet_link.opt
@@ -0,0 +1 @@
+MULTINET:MULTINET_SOCKET_LIBRARY/SHARE
diff --git a/contrib/ports/vms/vms_netlib_link.opt b/contrib/ports/vms/vms_netlib_link.opt
new file mode 100644
index 00000000..4bec72f1
--- /dev/null
+++ b/contrib/ports/vms/vms_netlib_link.opt
@@ -0,0 +1 @@
+netlib_shr/share
diff --git a/contrib/ports/vms/vmsbuild.com b/contrib/ports/vms/vmsbuild.com
new file mode 100644
index 00000000..e3f58ebc
--- /dev/null
+++ b/contrib/ports/vms/vmsbuild.com
@@ -0,0 +1,31 @@
+$! VMSBUILD.COM - Calls the subdirectories VMSBUILD.
+$! Can be called with options separated with commas and no spaces. The
+$! options are:
+$!
+$! NETLIB - For linking with Netlib. NETLIB.OLB must be pre-loaded into
+$! [.NETLIb]NETLIB.OLB
+$! MULTINET - Used with Multinet transport (no NETLIB is used).
+$! HEBREW - Build the Hebrew version.
+$!
+$!
+$ IF F$EXISTS("[.IMAP.ANSI]C-CLIENT.DIR") THEN RENAME [.IMAP.ANSI]C-CLIENT.DIR [];
+$ IF F$EXISTS("[.CONTRIB.VMS]VMSBUILD_CCLIENT.COM") THEN RENAME [.CONTRIB.VMS]VMSBUILD_CCLIENT.COM [.C-CLIENT]VMSBUILD.COM;
+$ SET DEF [.PICO]
+$@VMSBUILD 'P1'
+$ SET DEF [-.C-CLIENT]
+$@VMSBUILD 'P1'
+$ SET DEF [-.PINE]
+$@VMSBUILD 'P1'
+$ SET DEF [-]
+$!
+$ TYPE SYS$INPUT:
+Executables can be found in:
+
+PICO/PICO.EXE - The stand-alone editor (not needed).
+C-CLIENT/MTEST.EXE - Interactive testing program (not needed).
+C-CLIENT/IMAPD.EXE - the IMAP daemon. Should be copied elsewhere and defined
+ in the INETD.CONF file or equivalent.
+PINE/PINE.EXE - What you waited for...
+
+$!
+$ EXIT
diff --git a/contrib/ports/vms/vmsbuild_cclient.com b/contrib/ports/vms/vmsbuild_cclient.com
new file mode 100644
index 00000000..7d4c0a20
--- /dev/null
+++ b/contrib/ports/vms/vmsbuild_cclient.com
@@ -0,0 +1,103 @@
+$! Program: Operating-system dependent routines -- VMS version
+$!
+$! Author: Yehavi Bourvine, The Hebrew University of Jerusalem.
+$! Internet: Yehavi@VMS.huji.ac.il
+$!
+$! Date: 2 August 1994
+$! Last Edited: 2 August 1994
+$!
+$! Copyright 1994 by the University of Washington
+$!
+$! Permission to use, copy, modify, and distribute this software and its
+$! documentation for any purpose and without fee is hereby granted, provided
+$! that the above copyright notice appears in all copies and that both the
+$! above copyright notice and this permission notice appear in supporting
+$! documentation, and that the name of the University of Washington not be
+$! used in advertising or publicity pertaining to distribution of the software
+$! without specific, written prior permission. This software is made available
+$! "as is", and
+$! THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
+$! WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
+$! WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
+$! NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
+$! INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+$! LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
+$! (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
+$! WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+$!
+$! VMSBUILD.COM for C-CLIENT.
+$ CREATE LINKAGE.H
+$ DECK
+extern DRIVER imapdriver, nntpdriver, vmsmaildriver;
+$ EOD
+$ CREATE LINKAGE.C
+$ DECK
+ mail_link((DRIVER *)&imapdriver);
+ mail_link((DRIVER *)&nntpdriver);
+ mail_link((DRIVER *)&vmsmaildriver);
+$ EOD
+$!
+$ DEFINE SYS SYS$LIBRARY: ! Normal .H location.
+$ DEFINE NETINET SYS$LIBRARY:
+$ DEFINE ARPA SYS$LIBRARY:
+$!
+$ COPY OS_VMS.H OSDEP.H;
+$ COPY TCP_VMSN.C TCP_VMS.C; ! Default - no TcpIp support.
+$!
+$ CC_DEF = ",''P1'"
+$ LINK_OPT = ""
+$ IF P1 .EQS. "" THEN CC_DEF=""
+$ IF F$LOCATE("MULTINET", P1) .LT. F$LENGTH(P1)
+$ THEN
+$ DEFINE SYS MULTINET_ROOT:[MULTINET.INCLUDE.SYS],sys$library
+$ DEFINE NETINET MULTINET_ROOT:[MULTINET.INCLUDE.NETINET]
+$ DEFINE ARPA MULTINET_ROOT:[MULTINET.INCLUDE.ARPA]
+$ COPY TCP_VMSM.C TCP_VMS.C; ! Multinet support.
+$ LINK_OPT = ",[-.CONTRIB.VMS]VMS_MULTINET_LINK/OPTION"
+$ ENDIF
+$ IF F$LOCATE("NETLIB", P1) .LT. F$LENGTH(P1)
+$ THEN
+$ LINK_OPT = ",[-.CONTRIB.VMS]VMS_NETLIB_LINK/OPTION"
+$ COPY TCP_VMSL.C TCP_VMS.C; ! Netlib support.
+$ ENDIF
+$!
+$ CC_PREF = ""
+$ IF F$LOCATE("VAX", F$GETSYI("HW_NAME")) .EQS. F$LENGTH(F$GETSYI("HW_NAME"))
+$ THEN
+$ CC_PREF = "/PREFIX=(ALL,EXCEPT=(SOCKET,CONNECT,BIND,LISTEN,SOCKET_READ,SOCKET_WRITE,SOCKET_CLOSE,SELECT,ACCEPT,BCMP,BCOPY,BZERO,GETHOSTBYNAME,"
+$ CC_PREF = CC_PREF + "GETHOSTBYADDR,GETPEERNAME,GETDTABLESIZE,HTONS,HTONL,NTOHS,NTOHL,SEND,SENDTO,RECV,RECVFROM))"
+$ CC_PREF = CC_PREF + "/STANDARD=VAXC"
+$ ELSE
+$ CC_PREF = "/INCLUDE=[]"
+$ LINK_OPT = LINK_OPT + ",[-.CONTRIB.VMS]VMS_LINK/OPTION"
+$ COPY SYS$LIBRARY:CTYPE.H *.*;
+$ EDIT/EDT CTYPE.H
+s/readonly// w
+exit
+$ ENDIF
+$ SET VERIFY
+$ CC/NOOPTIMIZE'CC_PREF'/define=("readonly"="ReadOnly"'cc_def') OS_VMS
+$ CC/NOOPTIMIZE'CC_PREF'/define=("readonly"="ReadOnly"'cc_def') vms_mail
+$ CC/NOOPTIMIZE'CC_PREF'/define=("readonly"="ReadOnly"'cc_def') MAIL
+$ CC/NOOPTIMIZE'CC_PREF'/define=("readonly"="ReadOnly"'cc_def') SMTP
+$ CC/NOOPTIMIZE'CC_PREF'/define=("readonly"="ReadOnly"'cc_def') RFC822
+$ CC/NOOPTIMIZE'CC_PREF'/define=("readonly"="ReadOnly"'cc_def') NNTP
+$ CC/NOOPTIMIZE'CC_PREF'/define=("readonly"="ReadOnly"'cc_def') nntpcvms
+$ CC/NOOPTIMIZE'CC_PREF'/define=("readonly"="ReadOnly"'cc_def') MISC
+$ CC/NOOPTIMIZE'CC_PREF'/define=("readonly"="ReadOnly"'cc_def') IMAP2
+$ CC/NOOPTIMIZE'CC_PREF'/define=("readonly"="ReadOnly"'cc_def', -
+ L_SET=0) SM_VMS
+$!
+$ CC/NOOPTIMIZE'CC_PREF'/define=("readonly"="ReadOnly"'cc_def') MTEST
+$! CC/NOOPTIMIZE'CC_PREF'/define=("readonly"="ReadOnly"'cc_def') IMAPD
+$!
+$ LIBRARY/OBJECT/CREATE/INSERT C-CLIENT OS_VMS,vms_mail,MAIL,SMTP,RFC822,-
+ NNTP,nntpcvms,MISC,IMAP2,SM_VMS
+$!
+$ SET NOVERIFY
+$ LINK MTEST,IMAP2,MAIL,MISC,NNTP,nntpcvms,OS_VMS,RFC822,SMTP,-
+ SM_VMS,VMS_MAIL,SYS$INPUT:/OPTION'LINK_OPT'
+PSECT=_CTYPE_,NOWRT
+$! LINK IMAPD,imapd_vms,IMAP2,MAIL,MISC,NNTP,nntpcvms,OS_VMS,RFC822,SMTP,-
+$! SM_VMS,VMS_MAIL'LINK_OPT'
+$ EXIT
diff --git a/contrib/smime/README b/contrib/smime/README
new file mode 100644
index 00000000..fc9c7895
--- /dev/null
+++ b/contrib/smime/README
@@ -0,0 +1,65 @@
+S/MIME capabilities in Pine
+
+Patch Author: Jonathan Paisley
+Contact Info: paisleyj@dcs.gla.ac.uk
+WWW: http://www.dcs.gla.ac.uk/~paisleyj/
+
+This patch adds S/MIME functionality to Pine. It was
+last tested thoroughly with Pine 4.33, and it has been updated
+to work with the latest version (4.52).
+
+Check out the unix 'patch' command. An example of using
+patch would be:
+
+$ cd ../.. # Change to top level directory (containing contrib, pine, etc)
+$ patch -p1 < contrib/smime/pine-smime-151101.diff
+
+*** After patching, read pine/README.smime for more details. ***
+
+The patch will create the following files:
+
+pine/README.smime
+pine/TODO.smime
+pine/bss_so.c
+pine/bss_so.h
+pine/smime.c
+pine/smime.h
+pine/smkeys.c
+pine/smkeys.h
+
+It will modify the following files:
+
+imap/src/c-client/mail.c
+imap/src/c-client/mail.h
+pine/filter.c
+pine/init.c
+pine/mailcmd.c
+pine/mailpart.c
+pine/mailview.c
+pine/makefile.lnx
+pine/makefile.so5
+pine/pine.h
+pine/pine.hlp
+pine/send.c
+
+Note that this patch has only been tested on Solaris and Linux (thus only
+those makefiles have been patched). The changes that have been made to
+the makefiles are as follows:
+
+ Add these SSL variables (these were roughly lifted from imap/src/osdep/unix/Makefile)
+
+ SSLDIR= $(HOME)/local/ssl
+ SSLCERTS= $(SSLDIR)/certs
+ SSLINCLUDE= $(SSLDIR)/include
+ SSLLIB= $(SSLDIR)/lib
+
+ SSLCFLAGS= -I$(SSLINCLUDE) \
+ -DSSL_CERT_DIRECTORY=\"$(SSLCERTS)\" \
+ -DSMIME
+ SSLLDFLAGS= -L$(SSLLIB) -lcrypto
+
+ Add $(SSLLDFLAGS) to the LIBS variable.
+ Add $(SSLCFLAGS) to the CFLAGS variable.
+ Add bss_so.o smime.o smkeys.o to the OFILES= variable.
+
+Similar changes would have to be made to the makefile for other ports.
diff --git a/contrib/smime/pine-smime-20030115.diff b/contrib/smime/pine-smime-20030115.diff
new file mode 100644
index 00000000..5744ce0d
--- /dev/null
+++ b/contrib/smime/pine-smime-20030115.diff
@@ -0,0 +1,3265 @@
+diff -Ncr pine4.53/imap/src/c-client/mail.c pine4.53-smime/imap/src/c-client/mail.c
+*** pine4.53/imap/src/c-client/mail.c Thu Jan 2 17:28:42 2003
+--- pine4.53-smime/imap/src/c-client/mail.c Wed Jan 15 12:48:15 2003
+***************
+*** 5198,5203 ****
+--- 5198,5207 ----
+
+ void mail_free_body_data (BODY *body)
+ {
++ /* cleanup body if requested by application */
++ if (body->cleanup)
++ (*body->cleanup)(body);
++
+ switch (body->type) { /* free contents */
+ case TYPEMULTIPART: /* multiple part */
+ mail_free_body_part (&body->nested.part);
+diff -Ncr pine4.53/imap/src/c-client/mail.h pine4.53-smime/imap/src/c-client/mail.h
+*** pine4.53/imap/src/c-client/mail.h Tue Jan 7 12:33:50 2003
+--- pine4.53-smime/imap/src/c-client/mail.h Wed Jan 15 12:48:15 2003
+***************
+*** 655,660 ****
+--- 655,663 ----
+ unsigned long bytes; /* size of text in octets */
+ } size;
+ char *md5; /* MD5 checksum */
++
++ void *sparep; /* spare pointer reserved for main program */
++ void (*cleanup)(BODY *); /* cleanup function */
+ };
+
+
+diff -Ncr pine4.53/pine/README.smime pine4.53-smime/pine/README.smime
+*** pine4.53/pine/README.smime Wed Dec 31 16:00:00 1969
+--- pine4.53-smime/pine/README.smime Wed Jan 15 12:48:15 2003
+***************
+*** 0 ****
+--- 1,129 ----
++ Quick Start
++ ===========
++
++ To enable S/MIME support, ensure the SSL related lines in the platform
++ makefile are uncommented (they're all next to one another).
++
++ CA certificates are expected to be found in the OpenSSL 'cert' dir, in the
++ standard hashed format.
++
++ User Configuration
++ ==================
++
++ A directory '~/.pine-smime' must be created. Within this, a further
++ three directories are required:
++
++ ~/.pine-smime/
++ ca/
++ private/
++ public/
++
++ Other people's certificate files should be copied into the 'public' directory.
++ Your certificate file(s) should be copied into the 'private' directory.
++ Certificates for any additional trusted CAs should be put in the 'ca' directory.
++
++ There are three extra configuration options:
++
++ sign-default-on
++ encrypt-default-on
++ remember-smime-passphrase
++
++ Certificates
++ ============
++
++ The certificate files specified above should have the following form:
++
++ public certificates: user@emaildomain.crt
++ private keys: user@emaildomain.key
++
++ Thus, a typical installation might look like this:
++
++ ~/.pine-smime/
++ ca/
++ [additional trusted CAs here]
++ private/
++ paisleyj@dcs.gla.ac.uk.crt
++ paisleyj@dcs.gla.ac.uk.key
++ public/
++ myfriend@dcs.gla.ac.uk.crt
++ myotherfriend@dcs.gla.ac.uk.crt
++
++ Implementation Details
++ ======================
++
++ Link with the OpenSSL crypto library for PKCS7 support.
++ Only tested on linux (slx) and solaris (so5).
++
++ Added three extra source files (+headers):
++
++ smime.c Main S/MIME support code
++ smkeys.c Very basic X509 key handling/storage (using the above dirs)
++ bss_so.c OpenSSL BIO using pine STORE_S objects
++
++ Patches to existing pine sources:
++
++ init.c
++ Add references to new configuration options.
++
++ mailcmd.c
++ Add implementation of MC_DECRYPT command which prompts
++ the user for a passphrase if it's required.
++
++ mailpart.c
++ Comment added to help me remember what I'd done.
++
++ mailview.c
++ Added description of Decrypt menu option.
++ Make calls out to smime.c functions to handle the decryption.
++ This is done shortly after the BODY of a message is
++ obtained.
++ Added function to describe encrypted messages when they're
++ being displayed.
++ Added code to describe the special case of PKCS7 attachments.
++
++ makefile.lnx
++ makefile.so5
++ Added SSL variables etc.
++
++ pine.h
++ Add enumerations for new configuration options and definition
++ of MC_DECRYPT command
++ Exported the prototype of pine_write_body_header,
++ pine_rfc822_output_body and pine_encode_body since they're
++ needed in smime.c.
++
++ pine.hlp
++ Added help info for new configuration options.
++
++ send.c
++ Added 'Encrypt' and 'Sign' menu options when sending email.
++ Make calls to smime.c functions to fiddle message on the
++ way out.
++ Extend pine_encode_body so it makes a few more checks
++ before adding a boundary.
++
++ Basic method:
++
++ Incoming
++
++ Scan BODY of viewed message before it is formatted. If it contains
++ PKCS7 parts, decode them and attempt to decrypt/verify signatures. The
++ original BODY is fiddled in place and turned into a multipart body
++ with one subpart -- the decrypted data. This may consist of a multipart
++ with attachments, for example.
++
++ This all depends on stashing a pointer to the decrypted data in
++ body->contents.text.data and relying on the fact that the mail_* routines
++ will use this data in preference to fetching it over the network. We
++ also depend on it not being garbage collected while the message is
++ being viewed!
++
++ Outgoing
++
++ smime.c pre-builds the message using pine_encode_body, pine_write_body_header
++ and pine_rfc822_output_body, encrypting/signing the resulting data. The
++ body that was going to be sent is then fiddled appropriately after
++ the PKCS7 objects have been built.
++
++ paisleyj@dcs.gla.ac.uk
++ Mar 7 2001
+diff -Ncr pine4.53/pine/TODO.smime pine4.53-smime/pine/TODO.smime
+*** pine4.53/pine/TODO.smime Wed Dec 31 16:00:00 1969
+--- pine4.53-smime/pine/TODO.smime Wed Jan 15 12:48:15 2003
+***************
+*** 0 ****
+--- 1,31 ----
++
++ Need to be able to view stored certificates to see details
++ (particularly the fingerprint for comparing over the phone, say)
++ --> proper key management system
++
++ Add client private key and certificate request generation.
++
++ Send certificate for CA along with certificate of signer.
++
++ Verify recipient certificate before sending encrypted message.
++
++ Verify certificates in general.
++
++ Cache the result of pre-formatting the message during the send/encrypt/sign
++ phase rather than letting call_mailer re-format it all over again.
++
++ Tidy up the use of global variables considerably.
++
++ Intelligently pick a certificate for signing purposes based on the
++ From address rather than just picking the first one on the list.
++
++ Figure out platform dependancies from using readdir() in smkeys.c
++
++ Handle message/rfc822 sub-parts!
++
++ Consider what happens with all our cached data.
++
++ S/MIME info screen help.
++
++ paisleyj@dcs.gla.ac.uk
++ Mar 7 2001
+diff -Ncr pine4.53/pine/bss_so.c pine4.53-smime/pine/bss_so.c
+*** pine4.53/pine/bss_so.c Wed Dec 31 16:00:00 1969
+--- pine4.53-smime/pine/bss_so.c Wed Jan 15 12:48:15 2003
+***************
+*** 0 ****
+--- 1,184 ----
++ #ifdef SMIME
++
++ /*
++ bss_so.c
++
++ Basic implementation of an OpenSSL BIO which is
++ backed by a pine STORE_S object
++ */
++
++ #include "headers.h"
++
++ #include <stdio.h>
++ #include <errno.h>
++ #include <openssl/bio.h>
++ #include <openssl/err.h>
++
++ static int bss_so_write(BIO *h, char *buf, int num);
++ static int bss_so_read(BIO *h, char *buf, int size);
++ static int bss_so_puts(BIO *h, char *str);
++ static int bss_so_gets(BIO *h, char *str, int size);
++ static long bss_so_ctrl(BIO *h, int cmd, long arg1, char *arg2);
++ static int bss_so_new(BIO *h);
++ static int bss_so_free(BIO *data);
++ static BIO_METHOD methods_sop =
++ {
++ BIO_TYPE_MEM,
++ "Storage Object",
++ bss_so_write,
++ bss_so_read,
++ bss_so_puts,
++ bss_so_gets,
++ bss_so_ctrl,
++ bss_so_new,
++ bss_so_free,
++ NULL,
++ };
++
++ BIO *BIO_new_so(STORE_S *so)
++ {
++ BIO *ret;
++
++ if ((ret = BIO_new(&methods_sop)) == NULL)
++ return (NULL);
++
++ BIO_set_fp(ret, (FILE*) so, 0);
++ return (ret);
++ }
++
++
++ static int bss_so_new(BIO *bi)
++ {
++ bi->init = 0;
++ bi->num = 0;
++ bi->ptr = NULL;
++ return (1);
++ }
++
++ static int bss_so_free(BIO *a)
++ {
++ if (a == NULL) return (0);
++ if (a->shutdown) {
++ if ((a->init) && (a->ptr != NULL)) {
++ a->ptr = NULL;
++ }
++ a->init = 0;
++ }
++ return (1);
++ }
++
++ static int bss_so_read(BIO *b, char *out, int outl)
++ {
++ int ret = 0;
++ STORE_S *so = (STORE_S*) b->ptr;
++
++ if (b->init && (out != NULL)) {
++
++ while (ret < outl) {
++ if (!so->readc((unsigned char *)out, so))
++ break;
++ out++;
++ ret++;
++ }
++
++ }
++ return (ret);
++ }
++
++ static int bss_so_write(BIO *b, char *in, int inl)
++ {
++ int ret = 0;
++
++ if (b->init && (in != NULL)) {
++ if (so_nputs((STORE_S *)b->ptr, in, inl))
++ ret = inl;
++
++ }
++ return (ret);
++ }
++
++ static long bss_so_ctrl(BIO *b, int cmd, long num, char *ptr)
++ {
++ long ret = 1;
++ STORE_S *so = (STORE_S *)b->ptr;
++ FILE **fpp;
++ char p[4];
++
++ switch (cmd) {
++ case BIO_C_FILE_SEEK:
++ case BIO_CTRL_RESET:
++ ret = so_seek(so, num, 0);
++ break;
++ case BIO_CTRL_EOF:
++ ret = 0;
++ break;
++ case BIO_C_FILE_TELL:
++ case BIO_CTRL_INFO:
++ ret = 0;
++ break;
++ case BIO_C_SET_FILE_PTR:
++ bss_so_free(b);
++ b->shutdown = (int)num & BIO_CLOSE;
++ b->ptr = (char *)ptr;
++ b->init = 1;
++ break;
++ case BIO_C_SET_FILENAME:
++ ret = 0;
++ break;
++ case BIO_C_GET_FILE_PTR:
++ if (ptr != NULL) {
++ fpp = (FILE **)ptr;
++ *fpp = (FILE *)NULL;
++ }
++ break;
++ case BIO_CTRL_GET_CLOSE:
++ ret = (long)b->shutdown;
++ break;
++ case BIO_CTRL_SET_CLOSE:
++ b->shutdown = (int)num;
++ break;
++ case BIO_CTRL_FLUSH:
++ break;
++ case BIO_CTRL_DUP:
++ ret = 1;
++ break;
++
++ case BIO_CTRL_WPENDING:
++ case BIO_CTRL_PENDING:
++ case BIO_CTRL_PUSH:
++ case BIO_CTRL_POP:
++ default:
++ ret = 0;
++ break;
++ }
++ return (ret);
++ }
++
++ static int bss_so_gets(BIO *bp, char *buf, int size)
++ {
++ int ret = 0;
++ char *b = buf;
++ char *bend = buf + size - 1;
++ STORE_S *so = (STORE_S*) bp->ptr;
++
++ do {
++ if (!so->readc((unsigned char *)b, so))
++ break;
++ b++;
++ } while (b < bend && b[ -1] != '\n');
++
++ *b = 0;
++
++ ret = b - buf;
++ return (ret);
++ }
++
++ static int bss_so_puts(BIO *bp, char *str)
++ {
++ STORE_S *so = (STORE_S*) bp->ptr;
++
++ return so->puts(so, str);
++ }
++
++
++ #endif /* SMIME */
+diff -Ncr pine4.53/pine/bss_so.h pine4.53-smime/pine/bss_so.h
+*** pine4.53/pine/bss_so.h Wed Dec 31 16:00:00 1969
+--- pine4.53-smime/pine/bss_so.h Wed Jan 15 12:48:15 2003
+***************
+*** 0 ****
+--- 1 ----
++ BIO *BIO_new_so(STORE_S *so);
+diff -Ncr pine4.53/pine/filter.c pine4.53-smime/pine/filter.c
+*** pine4.53/pine/filter.c Wed Jan 8 12:13:50 2003
+--- pine4.53-smime/pine/filter.c Wed Jan 15 12:48:15 2003
+***************
+*** 903,909 ****
+
+
+ /* get a character from a file */
+! /* assumes gf_out struct is filled in */
+ int
+ gf_freadc(c)
+ unsigned char *c;
+--- 903,909 ----
+
+
+ /* get a character from a file */
+! /* assumes gf_in struct is filled in */
+ int
+ gf_freadc(c)
+ unsigned char *c;
+***************
+*** 938,944 ****
+
+
+ /* get a character from a string, return nonzero if things OK */
+! /* assumes gf_out struct is filled in */
+ int
+ gf_sreadc(c)
+ unsigned char *c;
+--- 938,944 ----
+
+
+ /* get a character from a string, return nonzero if things OK */
+! /* assumes gf_in struct is filled in */
+ int
+ gf_sreadc(c)
+ unsigned char *c;
+diff -Ncr pine4.53/pine/init.c pine4.53-smime/pine/init.c
+*** pine4.53/pine/init.c Mon Jan 6 19:39:16 2003
+--- pine4.53-smime/pine/init.c Wed Jan 15 12:48:15 2003
+***************
+*** 2445,2451 ****
+ F_MARK_FCC_SEEN, h_config_mark_fcc_seen, PREF_SEND},
+ {"use-sender-not-x-sender",
+ F_USE_SENDER_NOT_X, h_config_use_sender_not_x, PREF_SEND},
+!
+ /* Folder */
+ {"combined-subdirectory-display",
+ F_CMBND_SUBDIR_DISP, h_config_combined_subdir_display, PREF_FLDR},
+--- 2445,2458 ----
+ F_MARK_FCC_SEEN, h_config_mark_fcc_seen, PREF_SEND},
+ {"use-sender-not-x-sender",
+ F_USE_SENDER_NOT_X, h_config_use_sender_not_x, PREF_SEND},
+! #ifdef SMIME
+! {"sign-default-on",
+! F_SIGN_DEFAULT_ON, h_config_sign_default_on, PREF_SEND},
+! {"encrypt-default-on",
+! F_ENCRYPT_DEFAULT_ON, h_config_encrypt_default_on, PREF_SEND},
+! {"remember-smime-passphrase",
+! F_REMEMBER_SMIME_PASSPHRASE, h_config_remember_smime_passphrase, PREF_SEND},
+! #endif
+ /* Folder */
+ {"combined-subdirectory-display",
+ F_CMBND_SUBDIR_DISP, h_config_combined_subdir_display, PREF_FLDR},
+diff -Ncr pine4.53/pine/mailcmd.c pine4.53-smime/pine/mailcmd.c
+*** pine4.53/pine/mailcmd.c Fri Jan 10 15:29:29 2003
+--- pine4.53-smime/pine/mailcmd.c Wed Jan 15 12:48:15 2003
+***************
+*** 53,58 ****
+--- 53,59 ----
+ #include "headers.h"
+ #include "../c-client/imap4r1.h"
+
++ #include "smime.h"
+
+ /*
+ * Internal Prototypes
+***************
+*** 1439,1444 ****
+--- 1440,1458 ----
+ break;
+
+
++ #ifdef SMIME
++ /*------- Try to decrypt message -----------*/
++ case MC_DECRYPT:
++ if (g_need_passphrase)
++ get_passphrase();
++ a_changed = TRUE;
++ break;
++
++ case MC_SECURITY:
++ state->next_screen = smime_info_screen;
++ break;
++ #endif
++
+ /*------- Bounce -----------*/
+ case MC_BOUNCE :
+ cmd_bounce(state, msgmap, 0);
+diff -Ncr pine4.53/pine/mailpart.c pine4.53-smime/pine/mailpart.c
+*** pine4.53/pine/mailpart.c Wed Nov 27 15:22:54 2002
+--- pine4.53-smime/pine/mailpart.c Wed Jan 15 12:48:15 2003
+***************
+*** 4534,4539 ****
+--- 4534,4545 ----
+ frd->flags = flags;
+ frd->size = size;
+ frd->readc = fetch_readc;
++
++ /* The call to imap_cache below will return true in the case where
++ we've already stashed fake data in the content of the part.
++ This happens when an S/MIME message is decrypted.
++ */
++
+ if(modern_imap_stream(stream)
+ && !imap_cache(stream, msgno, section, NULL, NULL)
+ && size > INIT_FETCH_CHUNK
+diff -Ncr pine4.53/pine/mailview.c pine4.53-smime/pine/mailview.c
+*** pine4.53/pine/mailview.c Tue Jan 7 16:17:00 2003
+--- pine4.53-smime/pine/mailview.c Wed Jan 15 12:48:15 2003
+***************
+*** 47,52 ****
+--- 47,53 ----
+
+
+ #include "headers.h"
++ #include "smime.h"
+
+
+ /*----------------------------------------------------------------------
+***************
+*** 207,214 ****
+--- 208,220 ----
+
+ HELP_MENU,
+ OTHER_MENU,
++ #ifdef SMIME
++ {"^D","Decrypt", {MC_DECRYPT,1,{ctrl('d')},KS_NONE}},
++ {"^E","Security", {MC_SECURITY,1,{ctrl('e')},KS_NONE}},
++ #else
+ NULL_MENU,
+ NULL_MENU,
++ #endif
+ RCOMPOSE_MENU,
+ NULL_MENU,
+ NULL_MENU,
+***************
+*** 227,232 ****
+--- 233,240 ----
+ #define BOUNCE_KEY 33
+ #define FLAG_KEY 34
+ #define VIEW_PIPE_KEY 35
++ #define DECRYPT_KEY (VIEW_PIPE_KEY + 3)
++ #define SECURITY_KEY (DECRYPT_KEY + 1)
+
+ static struct key simple_text_keys[] =
+ {HELP_MENU,
+***************
+*** 432,437 ****
+--- 440,447 ----
+ else
+ ps->unseen_in_view = !mc->seen;
+
++ flags = 0;
++
+ #if defined(DOS) && !defined(WIN32)
+ /*
+ * Handle big text for DOS here.
+***************
+*** 459,465 ****
+ ps->ttyo->screen_rows - (SCROLL_LINES_ABOVE(ps)
+ + SCROLL_LINES_BELOW(ps)));
+
+! flags = FM_DISPLAY;
+ if((last_message_viewed != mn_get_cur(ps->msgmap)
+ || last_was_full_header == 1))
+ flags |= FM_NEW_MESS;
+--- 469,475 ----
+ ps->ttyo->screen_rows - (SCROLL_LINES_ABOVE(ps)
+ + SCROLL_LINES_BELOW(ps)));
+
+! flags |= FM_DISPLAY;
+ if((last_message_viewed != mn_get_cur(ps->msgmap)
+ || last_was_full_header == 1))
+ flags |= FM_NEW_MESS;
+***************
+*** 467,472 ****
+--- 477,488 ----
+ if(offset) /* no pre-paint during resize */
+ view_writec_killbuf();
+
++ #ifdef SMIME
++ /* Attempt to handle S/MIME bodies */
++ if (fiddle_smime_message(body,raw_msgno,(flags&FM_NEW_MESS)!=0))
++ flags |= FM_NEW_MESS; /* body was changed, force a reload */
++ #endif
++
+ #ifdef _WINDOWS
+ mswin_noscrollupdate(1);
+ #endif
+***************
+*** 541,546 ****
+--- 557,567 ----
+
+ if(F_OFF(F_ENABLE_FULL_HDR, ps_global))
+ clrbitn(VIEW_FULL_HEADERS_KEY, scrollargs.keys.bitmap);
++
++ #ifdef SMIME
++ if (!g_need_passphrase)
++ clrbitn(DECRYPT_KEY, scrollargs.keys.bitmap);
++ #endif
+
+ if(!handles){
+ /*
+***************
+*** 754,760 ****
+--- 775,821 ----
+ }
+
+
++ #ifdef SMIME
++ /*----------------------------------------------------------------------
++ Add descriptive lines to the top of a message being formatted
++ that describe the status of any S/MIME enclosures that
++ have been encountered.
++
++ Args: body -- top-level body of the message being described
++ pc -- output function for writing to the message display
++
++ ----*/
++ static int describe_smime_bodies(BODY *body,gf_io_t pc)
++ {
++ PART *part;
++ int result = 0;
++
++ if (!body)
++ return result;
++
++ if (body->type == TYPEMULTIPART) {
++
++ if (body->subtype && strucmp(body->subtype,"x-pkcs7-enclosure")==0) {
++
++ if (body->description) {
++ format_editorial(body->description,ps_global->ttyo->screen_cols,pc);
++ gf_puts(NEWLINE,pc);
++ result = 1;
++ }
+
++ for (part=body->nested.part; part; part=part->next) {
++ result |= describe_smime_bodies(&(part->body),pc);
++ }
++
++ }
++ } else if (body->type == TYPEMESSAGE &&
++ body->subtype && strucmp(body->subtype, "rfc822")==0) {
++ result |= describe_smime_bodies(body->nested.msg->body,pc);
++ }
++
++ return result;
++ }
++ #endif
+
+ /*----------------------------------------------------------------------
+ Add lines to the attachments structure
+***************
+*** 1677,1682 ****
+--- 1738,1751 ----
+
+ show_parts = 0;
+
++ #ifdef SMIME
++ if (flgs & FM_DISPLAY) {
++ if (describe_smime_bodies(body,pc)) {
++ gf_puts(NEWLINE, pc);
++ }
++ }
++ #endif
++
+ /*======== Now loop through formatting all the parts =======*/
+ for(a = ps_global->atmts; a->description != NULL; a++) {
+
+***************
+*** 6150,6155 ****
+--- 6219,6236 ----
+
+ t = &tmp_20k_buf[strlen(tmp_20k_buf)];
+
++ #ifdef SMIME
++ if (is_pkcs7_body(body) && type!=3) { /* if smime and not attempting print */
++
++ sstrcpy(&t,"\015\012");
++
++ sstrcpy(&t,
++ "This part is a PKCS7 S/MIME enclosure. "
++ "You may be able to view it by entering the correct passphrase "
++ "with the \"^D\" command. Press \"^E\" for more information.");
++
++ } else
++ #endif
+ if(type){
+ sstrcpy(&t, "\015\012");
+ switch(type) {
+diff -Ncr pine4.53/pine/makefile.lnx pine4.53-smime/pine/makefile.lnx
+*** pine4.53/pine/makefile.lnx Tue Sep 10 14:34:39 2002
+--- pine4.53-smime/pine/makefile.lnx Wed Jan 15 12:48:15 2003
+***************
+*** 60,79 ****
+ LDAPOFILES= addrbook.o adrbkcmd.o args.o bldaddr.o init.o \
+ mailview.o other.o pine.o strings.o takeaddr.o
+
+ STDLIBS= -lncurses
+ LOCLIBS= $(PICODIR)/libpico.a $(CCLIENTDIR)/c-client.a
+ LIBS= $(LOCLIBS) $(LDAPLIBS) $(STDLIBS) \
+ `cat $(CCLIENTDIR)/LDFLAGS`
+
+ STDCFLAGS= -DLNX -DSYSTYPE=\"LNX\" -DMOUSE
+ CFLAGS= $(OPTIMIZE) $(PROFILE) $(DEBUG) $(EXTRACFLAGS) $(LDAPCFLAGS) \
+ $(STDCFLAGS)
+
+ OFILES= addrbook.o adrbkcmd.o adrbklib.o args.o bldaddr.o context.o filter.o \
+ folder.o help.o helptext.o imap.o init.o mailcap.o mailcmd.o \
+ mailindx.o mailpart.o mailview.o newmail.o other.o pine.o \
+ reply.o screen.o send.o signals.o status.o strings.o takeaddr.o \
+! os.o
+
+ HFILES= headers.h os.h pine.h context.h helptext.h \
+ $(PICODIR)/headers.h $(PICODIR)/estruct.h \
+--- 60,92 ----
+ LDAPOFILES= addrbook.o adrbkcmd.o args.o bldaddr.o init.o \
+ mailview.o other.o pine.o strings.o takeaddr.o
+
++ SSLDIR= $(HOME)/local/ssl
++ SSLCERTS= $(SSLDIR)/certs
++ SSLINCLUDE= $(SSLDIR)/include
++ SSLLIB= $(SSLDIR)/lib
++
++ SSLCFLAGS= -I$(SSLINCLUDE) \
++ -DSSL_CERT_DIRECTORY=\"$(SSLCERTS)\" \
++ -DSMIME
++ SSLLDFLAGS= -L$(SSLLIB) -lcrypto
++
++
+ STDLIBS= -lncurses
+ LOCLIBS= $(PICODIR)/libpico.a $(CCLIENTDIR)/c-client.a
+ LIBS= $(LOCLIBS) $(LDAPLIBS) $(STDLIBS) \
++ $(SSLLDFLAGS) \
+ `cat $(CCLIENTDIR)/LDFLAGS`
+
+ STDCFLAGS= -DLNX -DSYSTYPE=\"LNX\" -DMOUSE
+ CFLAGS= $(OPTIMIZE) $(PROFILE) $(DEBUG) $(EXTRACFLAGS) $(LDAPCFLAGS) \
++ $(SSLCFLAGS) \
+ $(STDCFLAGS)
+
+ OFILES= addrbook.o adrbkcmd.o adrbklib.o args.o bldaddr.o context.o filter.o \
+ folder.o help.o helptext.o imap.o init.o mailcap.o mailcmd.o \
+ mailindx.o mailpart.o mailview.o newmail.o other.o pine.o \
+ reply.o screen.o send.o signals.o status.o strings.o takeaddr.o \
+! os.o bss_so.o smime.o smkeys.o
+
+ HFILES= headers.h os.h pine.h context.h helptext.h \
+ $(PICODIR)/headers.h $(PICODIR)/estruct.h \
+***************
+*** 135,137 ****
+--- 148,154 ----
+ osdep/sendmail osdep/execview \
+ osdep/postreap.wtp osdep/os-lnx.ic
+ cd osdep; $(MAKE) includer os-lnx.c; cd ..
++
++ jon.o: jon.c
++ $(CC) $(CFLAGS) -Wall -Wstrict-prototypes -c $< -o $@
++
+diff -Ncr pine4.53/pine/makefile.so5 pine4.53-smime/pine/makefile.so5
+*** pine4.53/pine/makefile.so5 Tue Oct 23 15:24:51 2001
+--- pine4.53-smime/pine/makefile.so5 Wed Jan 15 12:48:15 2003
+***************
+*** 62,67 ****
+--- 62,78 ----
+ LDAPOFILES= addrbook.o adrbkcmd.o args.o bldaddr.o init.o \
+ mailview.o other.o pine.o strings.o takeaddr.o
+
++ SSLDIR= $(HOME)/local/ssl
++ SSLCERTS= $(SSLDIR)/certs
++ SSLINCLUDE= $(SSLDIR)/include
++ SSLLIB= $(SSLDIR)/lib
++
++ SSLCFLAGS= -I$(SSLINCLUDE) \
++ -DSSL_CERT_DIRECTORY=\"$(SSLCERTS)\" \
++ -DSMIME
++ SSLLDFLAGS= -L$(SSLLIB) -lcrypto
++
++
+ # LDCC= /usr/bin/cc
+ # If you don't have /usr/bin/cc (our Solaris 2.2 doesn't seem to have it,
+ # it only has /usr/ucb/cc) then change LDCC to the following line and
+***************
+*** 72,88 ****
+ STDLIBS= -ltermlib
+ LOCLIBS= $(PICODIR)/libpico.a $(CCLIENTDIR)/c-client.a
+ LIBS= $(LOCLIBS) $(LDAPLIBS) $(STDLIBS) \
+ `cat $(CCLIENTDIR)/LDFLAGS`
+
+ STDCFLAGS= -Dconst= -DSV4 -DSYSTYPE=\"SOL\" -DMOUSE
+ CFLAGS= $(OPTIMIZE) $(PROFILE) $(DEBUG) $(EXTRACFLAGS) $(LDAPCFLAGS) \
+ $(STDCFLAGS)
+
+ OFILES= addrbook.o adrbkcmd.o adrbklib.o args.o bldaddr.o context.o filter.o \
+ folder.o help.o helptext.o imap.o init.o mailcap.o mailcmd.o \
+ mailindx.o mailpart.o mailview.o newmail.o other.o pine.o \
+ reply.o screen.o send.o signals.o status.o strings.o takeaddr.o \
+! os.o
+
+ HFILES= headers.h os.h pine.h context.h helptext.h \
+ $(PICODIR)/headers.h $(PICODIR)/estruct.h \
+--- 83,101 ----
+ STDLIBS= -ltermlib
+ LOCLIBS= $(PICODIR)/libpico.a $(CCLIENTDIR)/c-client.a
+ LIBS= $(LOCLIBS) $(LDAPLIBS) $(STDLIBS) \
++ $(SSLLDFLAGS) \
+ `cat $(CCLIENTDIR)/LDFLAGS`
+
+ STDCFLAGS= -Dconst= -DSV4 -DSYSTYPE=\"SOL\" -DMOUSE
+ CFLAGS= $(OPTIMIZE) $(PROFILE) $(DEBUG) $(EXTRACFLAGS) $(LDAPCFLAGS) \
++ $(SSLCFLAGS) \
+ $(STDCFLAGS)
+
+ OFILES= addrbook.o adrbkcmd.o adrbklib.o args.o bldaddr.o context.o filter.o \
+ folder.o help.o helptext.o imap.o init.o mailcap.o mailcmd.o \
+ mailindx.o mailpart.o mailview.o newmail.o other.o pine.o \
+ reply.o screen.o send.o signals.o status.o strings.o takeaddr.o \
+! os.o bss_so.o smime.o smkeys.o
+
+ HFILES= headers.h os.h pine.h context.h helptext.h \
+ $(PICODIR)/headers.h $(PICODIR)/estruct.h \
+diff -Ncr pine4.53/pine/pine.h pine4.53-smime/pine/pine.h
+*** pine4.53/pine/pine.h Fri Jan 10 15:25:55 2003
+--- pine4.53-smime/pine/pine.h Wed Jan 15 12:48:15 2003
+***************
+*** 1134,1139 ****
+--- 1134,1144 ----
+ F_DISABLE_SHARED_NAMESPACES,
+ F_EXPOSE_HIDDEN_CONFIG,
+ F_ALT_COMPOSE_MENU,
++ #ifdef SMIME
++ F_SIGN_DEFAULT_ON,
++ F_ENCRYPT_DEFAULT_ON,
++ F_REMEMBER_SMIME_PASSPHRASE,
++ #endif
+ F_ALWAYS_SPELL_CHECK,
+ F_QUELL_TIMEZONE,
+ F_COLOR_LINE_IMPORTANT,
+***************
+*** 2031,2036 ****
+--- 2036,2043 ----
+ } cmdline_val; /* user typed as cmdline arg */
+ };
+
++ #define MC_DECRYPT 800
++ #define MC_SECURITY 801
+
+
+ /*
+***************
+*** 4368,4373 ****
+--- 4375,4383 ----
+ char *pine_send_status PROTO((int, char *, char *, int *));
+ void phone_home PROTO((char *));
+ void pine_free_body PROTO((BODY **));
++ int pine_write_body_header PROTO((BODY *, soutr_t, TCPSTREAM *));
++ long pine_rfc822_output_body PROTO((BODY *,soutr_t,TCPSTREAM *));
++ void pine_encode_body PROTO((BODY *));
+ void simple_header_parse PROTO((char *, char **, char **));
+ int valid_subject PROTO((char *, char **, char **,BUILDER_ARG *,int *));
+ long new_mail_for_pico PROTO((int, int));
+diff -Ncr pine4.53/pine/pine.hlp pine4.53-smime/pine/pine.hlp
+*** pine4.53/pine/pine.hlp Wed Jan 15 11:55:09 2003
+--- pine4.53-smime/pine/pine.hlp Wed Jan 15 12:48:15 2003
+***************
+*** 24338,24340 ****
+--- 24338,24347 ----
+ ========== h_select_by_smaller_size ==========
+ Enter a number or ^C to cancel. All messages less than this many characters
+ in size will be selected. Examples: 2176, 1.53K (1530), or 3M (3000000).
++ ========== h_config_sign_default_on ==========
++ If enabled, the 'Sign' option will default to on when sending messages.
++ ========== h_config_encrypt_default_on ==========
++ If enabled, the 'Encrypt' option will default to on when sending messages.
++ ========== h_config_remember_smime_passphrase ==========
++ If enabled, you will only have to enter your passphrase for your private key
++ once during a pine session.
+diff -Ncr pine4.53/pine/send.c pine4.53-smime/pine/send.c
+*** pine4.53/pine/send.c Tue Jan 14 13:22:59 2003
+--- pine4.53-smime/pine/send.c Wed Jan 15 12:48:15 2003
+***************
+*** 50,55 ****
+--- 50,56 ----
+ #include "../c-client/smtp.h"
+ #include "../c-client/nntp.h"
+
++ #include "smime.h"
+
+ #ifndef TCPSTREAM
+ #define TCPSTREAM void
+***************
+*** 5490,5495 ****
+--- 5491,5513 ----
+ opts[i++].label = "";
+ }
+
++ #ifdef SMIME
++ {
++ opts[i].ch = 'e';
++ opts[i].rval = 'e';
++ opts[i].name = "E";
++ opts[i++].label = "Encrypt";
++
++ opts[i].ch = 'g';
++ opts[i].rval = 'g';
++ opts[i].name = "G";
++ opts[i++].label = "Sign";
++
++ g_do_encrypt = F_ON(F_ENCRYPT_DEFAULT_ON,ps_global);
++ g_do_sign = F_ON(F_SIGN_DEFAULT_ON,ps_global);
++ }
++ #endif
++
+ opts[i].ch = -1;
+ no_help = (i >= 12);
+
+***************
+*** 5574,5579 ****
+--- 5592,5627 ----
+ sstrcpy(&optp, dsn_string);
+ }
+
++ #ifdef SMIME
++ if (g_do_encrypt) {
++ if(!lparen){
++ *optp++ = ' ';
++ *optp++ = '(';
++ lparen++;
++ }
++ else{
++ *optp++ = ',';
++ *optp++ = ' ';
++ }
++
++ sstrcpy(&optp, "Encrypted");
++ }
++
++ if (g_do_sign) {
++ if(!lparen){
++ *optp++ = ' ';
++ *optp++ = '(';
++ lparen++;
++ }
++ else{
++ *optp++ = ',';
++ *optp++ = ' ';
++ }
++
++ sstrcpy(&optp, "Signed");
++ }
++ #endif
++
+ if(lparen)
+ *optp++ = ')';
+
+***************
+*** 5675,5680 ****
+--- 5723,5734 ----
+ * body on failure.
+ */
+ dsn_requested = (DSN_SHOW | DSN_SUCCESS | DSN_DELAY | DSN_FULL);
++ #ifdef SMIME
++ } else if (rv=='e') {
++ g_do_encrypt = !g_do_encrypt;
++ } else if (rv=='g') {
++ g_do_sign = !g_do_sign;
++ #endif
+ }
+
+ sprintf(dsn_string, "DSN requested[%s%s%s%s]",
+***************
+*** 6418,6423 ****
+--- 6472,6478 ----
+ char *verbose_file = NULL;
+ BODY *bp = NULL;
+ PINEFIELD *pf;
++ BODY *origBody = body;
+
+ #define MAX_ADDR_ERROR 2 /* Only display 2 address errors */
+
+***************
+*** 6434,6439 ****
+--- 6489,6518 ----
+ return(0);
+ }
+
++
++ #ifdef SMIME
++ if (g_do_encrypt || g_do_sign) {
++ int result;
++
++ STORE_S *so = lmc.so;
++ lmc.so = NULL;
++
++ result = 1;
++
++ if (g_do_encrypt)
++ result = encrypt_outgoing_message(header,&body);
++
++ /* need to free new body from encrypt if sign fails? */
++ if (result && g_do_sign)
++ result = sign_outgoing_message(header,&body,g_do_encrypt);
++
++ lmc.so = so;
++
++ if (!result)
++ return 0;
++ }
++ #endif
++
+ /* set up counts and such to keep track sent percentage */
+ send_bytes_sent = 0;
+ gf_filter_init(); /* zero piped byte count, 'n */
+***************
+*** 6742,6747 ****
+--- 6821,6844 ----
+ mail_free_envelope(&fake_env);
+
+ done:
++
++ #ifdef SMIME
++ /* Free replacement encrypted body */
++ if (body != origBody) {
++
++ if (body->type==TYPEMULTIPART) {
++ /* Just get rid of first part, it's actually origBody */
++ void *x = body->nested.part;
++
++ body->nested.part = body->nested.part->next;
++
++ fs_give(&x);
++ }
++
++ pine_free_body(&body);
++ }
++ #endif
++
+ if(we_cancel)
+ cancel_busy_alarm(0);
+
+***************
+*** 8721,8733 ****
+ dprint(4, (debugfile, "-- pine_encode_body: %d\n", body ? body->type : 0));
+ if (body) switch (body->type) {
+ case TYPEMULTIPART: /* multi-part */
+! if (!body->parameter) { /* cookie not set up yet? */
+ char tmp[MAILTMPLEN]; /* make cookie not in BASE64 or QUOTEPRINT*/
+ sprintf (tmp,"%ld-%ld-%ld=:%ld",gethostid (),random (),time (0),
+ getpid ());
+! body->parameter = mail_newbody_parameter ();
+! body->parameter->attribute = cpystr ("BOUNDARY");
+! body->parameter->value = cpystr (tmp);
+ }
+ part = body->nested.part; /* encode body parts */
+ do pine_encode_body (&part->body);
+--- 8818,8834 ----
+ dprint(4, (debugfile, "-- pine_encode_body: %d\n", body ? body->type : 0));
+ if (body) switch (body->type) {
+ case TYPEMULTIPART: /* multi-part */
+! if (!body->parameter || strucmp(body->parameter->attribute,"BOUNDARY")!=0) { /* cookie not set up yet? */
+ char tmp[MAILTMPLEN]; /* make cookie not in BASE64 or QUOTEPRINT*/
++ PARAMETER *param;
++
+ sprintf (tmp,"%ld-%ld-%ld=:%ld",gethostid (),random (),time (0),
+ getpid ());
+! param = mail_newbody_parameter ();
+! param->next = body->parameter;
+! param->attribute = cpystr ("BOUNDARY");
+! param->value = cpystr (tmp);
+! body->parameter = param;
+ }
+ part = body->nested.part; /* encode body parts */
+ do pine_encode_body (&part->body);
+diff -Ncr pine4.53/pine/smime.c pine4.53-smime/pine/smime.c
+*** pine4.53/pine/smime.c Wed Dec 31 16:00:00 1969
+--- pine4.53-smime/pine/smime.c Wed Jan 15 12:48:15 2003
+***************
+*** 0 ****
+--- 1,1776 ----
++ /*
++ File: smime.c
++ Author: paisleyj@dcs.gla.ac.uk
++ Date: 01/2001
++
++ Description:
++ This file contains all the low-level functions
++ required for dealing with S/MIME objects.
++
++ References are made to the functions in this file
++ from the following locations:
++
++ mailview.c:part_desc() -> is_pkcs7_body()
++ send.c:call_mailer() -> encrypt_outgoing_message()
++ send.c:call_mailer() -> sign_outgoing_message()
++ mailcmd.c:process_cmd() -> get_passphrase()
++ mailcmd.c:process_cmd() -> smime_info_screen()
++ */
++
++ #ifdef SMIME
++
++ #include "headers.h"
++
++ #include <stdio.h>
++ #include <stdlib.h>
++ #include <string.h>
++ #include <time.h>
++
++ #include <openssl/err.h>
++ #include <openssl/objects.h>
++ #include <openssl/evp.h>
++
++ #include <openssl/x509.h>
++ #include <openssl/pkcs7.h>
++ #include <openssl/pem.h>
++ #include <openssl/rand.h>
++
++ #include "bss_so.h"
++ #include "smkeys.h"
++ #include "smime.h"
++
++ #define PINE_SMIME_DIRNAME ".pine-smime"
++
++ /* Set true if loading a key failed due to lack of passphrase.
++ Queried in mailcmd.c:process_cmd() before calling get_passphrase()
++ */
++ int g_need_passphrase = 0;
++ /* User has entered a passphrase */
++ static int s_entered_passphrase = 0;
++ /* Storage for the entered passphrase */
++ static char s_passphrase[80];
++ static char *s_passphrase_emailaddr;
++
++ /* Set true if encrypting/signing (respectively)
++ Referenced from send.c:call_mailer() and send.c:send_exit_for_pico
++ */
++ int g_do_encrypt;
++ int g_do_sign;
++
++ /* Full pathname to ~/.pine-smime */
++ static char *g_pine_smime_dir;
++
++ static BIO *bio_err;
++
++ /* Linked list of PERSONAL_CERT objects */
++ static PERSONAL_CERT *s_personal_certs;
++
++ static X509_STORE *s_cert_store;
++
++ /* State management for randomness functions below */
++ static int seeded = 0;
++ static int egdsocket = 0;
++
++ /* Forget any cached private keys */
++ static void forget_private_keys()
++ {
++ PERSONAL_CERT *pcert;
++
++ for (pcert=s_personal_certs; pcert; pcert=pcert->next) {
++
++ if (pcert->key) {
++ EVP_PKEY_free(pcert->key);
++ pcert->key = NULL;
++ }
++ }
++ }
++
++ /* taken from openssl/apps/app_rand.c */
++ static int app_RAND_load_file(const char *file, BIO *bio_e, int dont_warn)
++ {
++ int consider_randfile = (file == NULL);
++ char buffer[200];
++
++ if (file == NULL)
++ file = RAND_file_name(buffer, sizeof buffer);
++ else if (RAND_egd(file) > 0)
++ {
++ /* we try if the given filename is an EGD socket.
++ if it is, we don't write anything back to the file. */
++ egdsocket = 1;
++ return 1;
++ }
++ if (file == NULL || !RAND_load_file(file, -1))
++ {
++ if (RAND_status() == 0 && !dont_warn)
++ {
++ BIO_printf(bio_e,"unable to load 'random state'\n");
++ BIO_printf(bio_e,"This means that the random number generator has not been seeded\n");
++ BIO_printf(bio_e,"with much random data.\n");
++ if (consider_randfile) /* explanation does not apply when a file is explicitly named */
++ {
++ BIO_printf(bio_e,"Consider setting the RANDFILE environment variable to point at a file that\n");
++ BIO_printf(bio_e,"'random' data can be kept in (the file will be overwritten).\n");
++ }
++ }
++ return 0;
++ }
++ seeded = 1;
++ return 1;
++ }
++
++ /* copied and fiddled from pine/imap/src/osdep/unix/auth_ssl.c */
++ static void openssl_extra_randomness(void)
++ {
++ #if !defined(WIN32)
++ int fd;
++ unsigned long i;
++ char tmp[MAXPATH];
++ struct stat sbuf;
++ /* if system doesn't have /dev/urandom */
++ if (stat ("/dev/urandom",&sbuf)) {
++ if ((fd = open (tmpnam (tmp),O_WRONLY|O_CREAT,0600)) < 0)
++ i = (unsigned long) tmp;
++ else {
++ unlink (tmp); /* don't need the file */
++ fstat (fd,&sbuf); /* get information about the file */
++ i = sbuf.st_ino; /* remember its inode */
++ close (fd); /* or its descriptor */
++ }
++ /* not great but it'll have to do */
++ sprintf (tmp + strlen (tmp),"%.80s%lx%lx%lx",
++ tcp_serverhost (),i,
++ (unsigned long) (time (0) ^ gethostid ()),
++ (unsigned long) getpid ());
++ RAND_seed (tmp,strlen (tmp));
++ }
++ #endif
++ }
++
++ /* taken from openssl/apps/app_rand.c */
++ static int app_RAND_write_file(const char *file, BIO *bio_e)
++ {
++ char buffer[200];
++
++ if (egdsocket || !seeded)
++ /* If we did not manage to read the seed file,
++ * we should not write a low-entropy seed file back --
++ * it would suppress a crucial warning the next time
++ * we want to use it. */
++ return 0;
++
++ if (file == NULL)
++ file = RAND_file_name(buffer, sizeof buffer);
++ if (file == NULL || !RAND_write_file(file))
++ {
++ BIO_printf(bio_e,"unable to write 'random state'\n");
++ return 0;
++ }
++ return 1;
++ }
++
++ /* Installed as an atexit() handler to save the random data */
++ static void openssl_deinit(void)
++ {
++ app_RAND_write_file(NULL, bio_err);
++ }
++
++ /* Initialise openssl stuff if needed */
++ static void openssl_init(void)
++ {
++ static int inited = 0;
++
++ if (!inited) {
++
++ char *p;
++ char buf[MAXPATH];
++
++ /* Find where that .pine-smime thing is */
++ /* Perhaps we should just use the user's home directory as a start? */
++ p = last_cmpnt(ps_global->pinerc);
++ buf[0] = '\0';
++ if(p != NULL) {
++ strncpy(buf, ps_global->pinerc, min(p - ps_global->pinerc, sizeof(buf)-1));
++ buf[min(p - ps_global->pinerc, sizeof(buf)-1)] = '\0';
++ }
++
++ strncat(buf, PINE_SMIME_DIRNAME, sizeof(buf)-1-strlen(buf));
++ buf[sizeof(buf)-1] = 0;
++
++ if (can_access(buf, ACCESS_EXISTS)==0) {
++
++ g_pine_smime_dir = cpystr(buf);
++ s_cert_store = get_ca_store(g_pine_smime_dir);
++ s_personal_certs = get_personal_certs(g_pine_smime_dir);
++
++ } else g_pine_smime_dir = ""; /* prevent null dereference later */
++
++ SSLeay_add_all_algorithms();
++ ERR_load_crypto_strings();
++
++ /* this won't make any sense (since the terminal's in a funny mode) */
++ if ((bio_err=BIO_new(BIO_s_file())) != NULL)
++ BIO_set_fp(bio_err,stderr,BIO_NOCLOSE|BIO_FP_TEXT);
++
++ app_RAND_load_file(NULL, bio_err, 1);
++
++ openssl_extra_randomness();
++
++ /* save the rand file when we're done */
++ atexit(openssl_deinit);
++
++ inited = 1;
++ }
++
++ ERR_clear_error();
++ }
++
++ /* Get a pointer to a string describing the most recent OpenSSL error.
++ It's statically allocated, so don't change or attempt to free it.
++ */
++ static const char *openssl_error_string(void)
++ {
++ char *errs;
++ const char *data = NULL;
++ long errn;
++
++ errn = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
++ errs = (char*)ERR_reason_error_string(ERR_GET_REASON(errn));
++
++ if (errs)
++ return errs;
++ else if (data)
++ return data;
++
++ return "unknown error";
++ }
++
++ /* Return true if the body looks like a PKCS7 object */
++ int is_pkcs7_body(BODY *body)
++ {
++ int result;
++
++ result = body->type==TYPEAPPLICATION &&
++ body->subtype &&
++ (strucmp(body->subtype,"pkcs7-mime")==0 ||
++ strucmp(body->subtype,"x-pkcs7-mime")==0 ||
++ strucmp(body->subtype,"pkcs7-signature")==0 ||
++ strucmp(body->subtype,"x-pkcs7-signature")==0);
++
++ return result;
++ }
++
++ /* debug utility to dump the contents of a BIO to a file */
++ static void dump_bio_to_file(BIO *in,char *filename)
++ {
++ char iobuf[4096];
++ int len;
++ BIO *out;
++
++ out = BIO_new_file(filename,"w");
++
++ if (out) {
++ BIO_reset(in);
++
++ while ((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
++ BIO_write(out, iobuf, len);
++ BIO_free(out);
++ }
++ }
++
++ /* prompt the user for their passphrase
++ (possibly prompting with the email address in s_passphrase_emailaddr)
++ */
++ int get_passphrase(void)
++ {
++ int rc;
++ int flags;
++ char prompt[50];
++ HelpType help = NO_HELP;
++
++ sprintf(prompt,
++ "Enter passphrase for <%s>: ",s_passphrase_emailaddr ? s_passphrase_emailaddr : "unknown");
++
++ do {
++ flags = OE_PASSWD | OE_DISALLOW_HELP;
++ rc = optionally_enter(s_passphrase, -FOOTER_ROWS(ps_global), 0, sizeof(s_passphrase),
++ prompt, NULL, help, &flags);
++ } while (rc!=0 && rc!=1 && rc>0);
++
++ if (rc==0)
++ s_entered_passphrase = 1;
++
++ return rc==0;
++ }
++
++ /* Recursively stash a pointer to the decrypted data in our
++ manufactured body.
++ */
++ static void create_local_cache(char *base,BODY *b)
++ {
++ if (b->type==TYPEMULTIPART) {
++ PART *p;
++
++ #if 0
++ cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
++ #else
++ /* don't really want to copy the real body contents. It shouldn't be
++ used, and in the case of a message with attachments, we'll be
++ duplicating the files multiple times
++ */
++ cpytxt(&b->contents.text, "BODY UNAVAILABLE", 16);
++ #endif
++
++ for (p=b->nested.part;p;p=p->next) {
++ create_local_cache(base,(BODY*) p);
++ }
++ } else {
++ cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
++ }
++ }
++
++ static long rfc822_output_func(void *stream,char *string)
++ {
++ STORE_S *so = (STORE_S*) stream;
++
++ return so_puts(so,string)!=0;
++ }
++
++ /* Load a private key from the given file */
++ static EVP_PKEY *load_key(char *file, char *pass)
++ {
++ BIO *in;
++ EVP_PKEY *key;
++ if(!(in = BIO_new_file(file, "r"))) return NULL;
++ key = PEM_read_bio_PrivateKey(in, NULL,NULL,pass);
++ BIO_free(in);
++ return key;
++ }
++
++ /* Attempt to load the private key for the given PERSONAL_CERT.
++ This sets the appropriate passphrase globals in order to
++ interact with the user correctly.
++ */
++ static int load_private_key(PERSONAL_CERT *pcert)
++ {
++ if (!pcert->key) {
++
++ /* Try empty password by default */
++ char *password = "";
++
++ if (g_need_passphrase) {
++ /* We've already been in here and discovered we need a different password */
++
++ if (s_entered_passphrase)
++ password = s_passphrase; /* Use the passphrase if it's been entered */
++ else return 0;
++ }
++
++ ERR_clear_error();
++
++ if(!(pcert->key = load_key(pcert->file, password))) {
++ long err = ERR_get_error();
++
++ /* Couldn't load key... */
++
++ if (s_entered_passphrase) {
++
++ /* The user got the password wrong maybe? */
++
++ if ((ERR_GET_LIB(err)==ERR_LIB_EVP && ERR_GET_REASON(err)==EVP_R_BAD_DECRYPT) ||
++ (ERR_GET_LIB(err)==ERR_LIB_PEM && ERR_GET_REASON(err)==PEM_R_BAD_DECRYPT))
++ q_status_message(SM_ORDER | SM_DING,1,1,"Wrong password");
++ else q_status_message1(SM_ORDER,1,1,"Couldn't read key: %s",(char*)openssl_error_string());
++
++ /* This passphrase is no good; forget it */
++ s_entered_passphrase = 0;
++ }
++
++ /* Indicate to the UI that we need re-entry (see mailcmd.c:process_cmd())*/
++ g_need_passphrase = 1;
++
++ fs_give((void**) &s_passphrase_emailaddr);
++ s_passphrase_emailaddr = get_x509_subject_email(pcert->cert);
++ return 0;
++ } else {
++ /* This key will be cached, so we won't be called again */
++ s_entered_passphrase = 0;
++ g_need_passphrase = 0;
++ }
++
++ return 1;
++ }
++
++ return 0;
++ }
++
++ static void setup_pkcs7_body_for_signature(BODY *b,char *description,char *type,char *filename)
++ {
++ b->type = TYPEAPPLICATION;
++ b->subtype = cpystr(type);
++ b->encoding = ENCBINARY;
++
++ b->description = cpystr(description);
++
++ b->disposition.type = cpystr("attachment");
++ b->disposition.parameter = mail_newbody_parameter();
++ b->disposition.parameter->attribute = cpystr("filename");
++ b->disposition.parameter->value = cpystr(filename);
++
++ b->parameter = mail_newbody_parameter();
++ b->parameter->attribute = cpystr("name");
++ b->parameter->value = cpystr(filename);
++ }
++
++ /*
++ Look for a personal certificate matching the
++ given address
++ */
++ PERSONAL_CERT *match_personal_cert_to_email(ADDRESS *a)
++ {
++ PERSONAL_CERT *pcert;
++ char buf[MAXPATH];
++ char *email;
++
++ if (!a || !a->mailbox || !a->host)
++ return NULL;
++
++ snprintf(buf,sizeof(buf),"%s@%s",a->mailbox,a->host);
++
++ for (pcert=s_personal_certs;pcert;pcert=pcert->next) {
++
++ if (!pcert->cert)
++ continue;
++
++ email = get_x509_subject_email(pcert->cert);
++
++ if (email && strucmp(email,buf)==0) {
++ fs_give((void**) &email);
++ break;
++ }
++
++ fs_give((void**) &email);
++ }
++
++ return pcert;
++ }
++
++ /*
++ Look for a personal certificate matching the from
++ (or reply_to? in the given envelope)
++ */
++ PERSONAL_CERT *match_personal_cert(ENVELOPE *env)
++ {
++ PERSONAL_CERT *pcert;
++
++ pcert = match_personal_cert_to_email(env->reply_to);
++ if (!pcert)
++ pcert = match_personal_cert_to_email(env->from);
++
++ return pcert;
++ }
++
++ /*
++ Flatten the given body into its MIME representation.
++ Return the result in a CharStar STORE_S.
++ */
++ static STORE_S *body_to_store(BODY *body)
++ {
++ STORE_S *store;
++ store = so_get(CharStar, NULL, EDIT_ACCESS);
++ if (!store)
++ return NULL;
++
++ pine_encode_body(body); /* this attaches random boundary strings to multiparts */
++ pine_write_body_header (body, rfc822_output_func,store);
++ pine_rfc822_output_body(body, rfc822_output_func,store);
++
++ /* now need to truncate by two characters since the above
++ appends CRLF to the stream
++ */
++
++ /** Eek! No way of telling size of a STORE_S. We depend on knowing it's
++ a CharStar */
++ so_truncate(store,((char*)store->eod-(char*)store->txt)-2);
++
++ so_seek(store,0,SEEK_SET);
++
++ return store;
++ }
++
++
++
++ /*
++ Sign a message. Called from call_mailer in send.c.
++
++ This takes the header for the outgoing message as well as a pointer
++ to the current body (which may be reallocated).
++ */
++ int sign_outgoing_message(METAENV *header,BODY **bodyP,int dont_detach)
++ {
++ STORE_S *store = NULL;
++ STORE_S *outs = NULL;
++ BODY *body = *bodyP;
++ BODY *newBody = NULL;
++ PART *p1 = NULL;
++ PART *p2 = NULL;
++ PERSONAL_CERT *pcert;
++ BIO *in = NULL;
++ BIO *out = NULL;
++ PKCS7 *p7 = NULL;
++ int result = 0;
++ PARAMETER *param;
++
++ int flags = dont_detach ? 0 : PKCS7_DETACHED;
++
++ openssl_init();
++
++ store = body_to_store(body);
++
++ /* Look for a private key matching the sender address... */
++
++ pcert = match_personal_cert(header->env);
++
++ if (!pcert) {
++ q_status_message(SM_ORDER,1,1,"Couldn't find the certificate needed to sign.");
++ goto end;
++ }
++
++ if (!load_private_key(pcert) && g_need_passphrase) {
++ /* Couldn't load key with blank password, try again */
++ get_passphrase();
++ load_private_key(pcert);
++ }
++
++ if (!pcert->key)
++ goto end;
++
++ in = BIO_new_so(store);
++
++ #if 0
++ dump_bio_to_file(in,"/tmp/signed-data");
++ #endif
++
++ BIO_reset(in);
++
++ p7 = PKCS7_sign(pcert->cert, pcert->key, NULL, in, flags);
++ if (!p7) {
++ q_status_message(SM_ORDER,1,1,"Error creating PKCS7 object.");
++ goto end;
++ }
++
++ outs = so_get(CharStar,NULL,EDIT_ACCESS);
++ out = BIO_new_so(outs);
++
++ i2d_PKCS7_bio(out, p7);
++ BIO_flush(out);
++
++ so_seek(outs,0,SEEK_SET);
++
++ if ((flags&PKCS7_DETACHED)==0) {
++
++ /* the simple case: the signed data is in the pkcs7 object */
++
++ newBody = mail_newbody();
++
++ setup_pkcs7_body_for_signature(newBody,"S/MIME Cryptographically Signed Message","x-pkcs7-mime","smime.p7m");
++
++ newBody->contents.text.data = (char*) outs;
++ *bodyP = newBody;
++
++ result = 1;
++ } else {
++
++ /* OK.
++ We have to create a new body as follows:
++
++ multipart/signed; blah blah blah
++ reference to existing body
++
++ pkcs7 object
++ */
++
++ newBody = mail_newbody();
++
++ newBody->type = TYPEMULTIPART;
++ newBody->subtype = cpystr("signed");
++ newBody->encoding = ENC7BIT;
++
++ newBody->parameter = param = mail_newbody_parameter();
++ param->attribute = cpystr("protocol");
++ param->value = cpystr("application/x-pkcs7-signature");
++
++ newBody->parameter->next = param = mail_newbody_parameter();
++ param->attribute = cpystr("micalg");
++ param->value = cpystr("sha1");
++
++ p1 = mail_newbody_part();
++ p2 = mail_newbody_part();
++
++ /* this is nasty. We're just copying the body in here,
++ but since our newBody is freed at the end of call_mailer,
++ we mustn't let this body (the original one) be freed twice.
++ */
++ p1->body = *body; /* ARRGH. This is special cased at the end of call_mailer */
++
++ p1->next = p2;
++
++ setup_pkcs7_body_for_signature(&p2->body,"S/MIME Cryptographic Signature","x-pkcs7-signature","smime.p7s");
++ p2->body.contents.text.data = (char*) outs;
++
++ newBody->nested.part = p1;
++
++ *bodyP = newBody;
++
++ result = 1;
++ }
++
++ end:
++
++ PKCS7_free(p7);
++ BIO_free(in);
++ BIO_free(out);
++ if (store)
++ so_give(&store);
++
++ return result;
++ }
++
++ /*
++ Encrypt a message on the way out. Called from call_mailer in send.c
++ The body may be reallocated.
++ */
++ int encrypt_outgoing_message(METAENV *header,BODY **bodyP)
++ {
++ PKCS7 *p7 = NULL;
++ BIO *in = NULL;
++ BIO *out = NULL;
++ EVP_CIPHER *cipher = NULL;
++ STACK_OF(X509) *encerts = NULL;
++ STORE_S *store = NULL;
++ STORE_S *outs = NULL;
++ PINEFIELD *pf;
++ ADDRESS *a;
++ BODY *body = *bodyP;
++ BODY *newBody = NULL;
++ int result = 0;
++
++ openssl_init();
++
++ cipher = EVP_des_cbc();
++
++ encerts = sk_X509_new_null();
++
++ /* Look for a certificate for each of the recipients */
++ for(pf = header->local; pf && pf->name; pf = pf->next)
++ if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr) {
++
++ for (a=*pf->addr;a;a=a->next) {
++ X509 *cert;
++ char buf[MAXPATH];
++
++ snprintf(buf,sizeof(buf),"%s@%s",a->mailbox,a->host);
++
++ cert = get_cert_for(g_pine_smime_dir,buf);
++ if (cert)
++ sk_X509_push(encerts,cert);
++ else {
++ q_status_message2(SM_ORDER,1,1,
++ "Unable to find certificate for <%s@%s>",a->mailbox,a->host);
++ goto end;
++ }
++ }
++ }
++
++
++ store = body_to_store(body);
++
++ in = BIO_new_so(store);
++
++ p7 = PKCS7_encrypt(encerts, in, cipher, 0);
++
++ outs = so_get(CharStar,NULL,EDIT_ACCESS);
++ out = BIO_new_so(outs);
++
++ i2d_PKCS7_bio(out, p7);
++ BIO_flush(out);
++ so_seek(outs,0,SEEK_SET);
++
++ newBody = mail_newbody();
++
++ newBody->type = TYPEAPPLICATION;
++ newBody->subtype = cpystr("x-pkcs7-mime");
++ newBody->encoding = ENCBINARY;
++
++ newBody->description = cpystr("S/MIME Encrypted Message");
++
++ newBody->contents.text.data = (char*) outs;
++
++ *bodyP = newBody;
++
++ result = 1;
++
++ end:
++
++ BIO_free(in);
++ BIO_free(out);
++ PKCS7_free(p7);
++ sk_X509_pop_free(encerts, X509_free);
++ if (store)
++ so_give(&store);
++
++ return result;
++ }
++
++ /*
++ Plonk the contents (mime headers and body) of the given
++ section of a message to a CharStar STORE_S object.
++ */
++ static STORE_S *get_raw_part(int msgno,const char *section)
++ {
++ long len;
++ STORE_S *store = NULL;
++ char *text;
++
++ store = so_get(CharStar, NULL, EDIT_ACCESS);
++ if (store) {
++
++ /* First grab headers of the chap */
++ text = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) section, &len, 0);
++
++ if (text) {
++ so_nputs(store,text,len);
++
++ /** Now grab actual body */
++ text = mail_fetch_body (ps_global->mail_stream, msgno, (char*) section, &len, 0);
++ if (text) {
++ so_nputs(store,text,len);
++
++ so_seek(store,0,SEEK_SET);
++
++ } else so_give(&store);
++
++ } else so_give(&store);
++
++ }
++ return store;
++ }
++
++ /*
++ Get (and decode) the body of the given section of msg
++ */
++ static STORE_S *get_part_contents(int msgno,const char *section)
++ {
++ long len;
++ gf_io_t pc;
++ STORE_S *store = NULL;
++ char *err;
++
++ store = so_get(CharStar, NULL, EDIT_ACCESS);
++ if (store) {
++ gf_set_so_writec(&pc,store);
++
++ err = detach(ps_global->mail_stream, msgno, (char*) section,&len, pc, NULL);
++
++ gf_clear_so_writec(store);
++
++ so_seek(store,0,SEEK_SET);
++
++ if (err)
++ so_give(&store);
++ }
++ return store;
++ }
++
++ static PKCS7 *get_pkcs7_from_part(int msgno,const char *section)
++ {
++ STORE_S *store = NULL;
++ PKCS7 *p7 = NULL;
++ BIO *in = NULL;
++
++ store = get_part_contents(msgno,section);
++
++ if (store) {
++ in = BIO_new_so(store);
++ if (in) {
++ p7=d2i_PKCS7_bio(in,NULL);
++ }
++ }
++
++ if (store)
++ so_give(&store);
++
++ BIO_free(in);
++
++ return p7;
++ }
++
++ /*
++ Try to verify a signature.
++
++ p7 - the pkcs7 object to verify
++ in - the plain data to verify (NULL if not detached)
++ out - BIO to which to write the opaque data
++ */
++ static int do_signature_verify(PKCS7 *p7,BIO *in,BIO *out)
++ {
++ STACK_OF(X509) *otherCerts = NULL;
++ int result;
++ const char *data;
++ long err;
++
++ #if 0
++ dump_bio_to_file(in,"/tmp/verified-data");
++ #endif
++
++ BIO_reset(in);
++
++ #if 0
++ /* testing verification stuff */
++ {
++ X509 *c;
++
++ c = get_cert_for(g_pine_smime_dir,"xx");
++ if (c) {
++ X509_add1_reject_object(c, OBJ_nid2obj(NID_email_protect));
++
++ X509_STORE_add_cert(s_cert_store,c);
++
++ save_cert_for(g_pine_smime_dir,cpystr("yy"),c);
++ }
++
++
++ ERR_clear_error();
++
++ }
++ #endif
++
++ result = PKCS7_verify(p7, otherCerts, s_cert_store,
++ in, out, 0);
++
++ if (result) {
++ q_status_message(SM_ORDER,1,1,"S/MIME signature verified ok");
++ } else {
++ err = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
++
++ if (out && err==ERR_PACK(ERR_LIB_PKCS7,PKCS7_F_PKCS7_VERIFY,PKCS7_R_CERTIFICATE_VERIFY_ERROR)) {
++
++ /* Retry verification so we can get the plain text */
++ /* Might be better to reimplement PKCS7_verify here? */
++
++ PKCS7_verify(p7, otherCerts, s_cert_store,
++ in, out, PKCS7_NOVERIFY);
++
++ }
++
++ q_status_message1(SM_ORDER | SM_DING,1,1,"Couldn't verify S/MIME signature: %s",(char*) openssl_error_string());
++
++ return result;
++ }
++
++ /* now try to extract the certificates of any signers */
++ {
++ STACK_OF(X509) *signers;
++ int i;
++
++ signers = PKCS7_get0_signers(p7, NULL, 0);
++
++ if (signers)
++ for (i=0;i<sk_X509_num(signers);i++) {
++ char *email;
++ X509 *x = sk_X509_value(signers,i);
++ X509 *cert;
++
++ if (!x)
++ continue;
++
++ email = get_x509_subject_email(x);
++
++ if (email) {
++ cert = get_cert_for(g_pine_smime_dir,email);
++ if (cert) {
++ X509_free(cert);
++ } else {
++ save_cert_for(g_pine_smime_dir,email,x);
++ }
++ fs_give((void**) &email);
++ }
++ }
++
++ sk_X509_free(signers);
++ }
++
++ return result;
++ }
++
++ /* Hook inside BODY structure for cleaning up S/MIME message bodies */
++ static void smime_body_cleanup(BODY *b)
++ {
++ #if 0
++ q_status_message(SM_ORDER,1,1,"smime_body_cleanup called");
++ #endif
++
++ if (b->sparep) {
++ PKCS7_free((PKCS7*) b->sparep);
++ b->sparep = NULL;
++ }
++ }
++
++ /*
++ Given a multipart body of type multipart/signed, attempt to verify
++ it
++ */
++ static int do_detached_signature_verify(BODY *b,int msgno,char *section)
++ {
++ STORE_S *toVerify = NULL;
++ PKCS7 *p7 = NULL;
++ BIO *in = NULL;
++ PART *p;
++ int result = 0;
++ char seq[100];
++ char *what_we_did;
++
++ openssl_init();
++
++ snprintf(seq,sizeof(seq),"%s1",section);
++ toVerify = get_raw_part(msgno,seq);
++
++ if (toVerify) {
++
++ in = BIO_new_so(toVerify);
++ if (!in)
++ goto end;
++
++ snprintf(seq,sizeof(seq),"%s2",section);
++ p7 = get_pkcs7_from_part(msgno,seq);
++
++ if (!p7)
++ goto end;
++
++ result = do_signature_verify(p7,in,NULL);
++
++ if (b->subtype) fs_give((void**) &b->subtype);
++ b->subtype = cpystr("x-pkcs7-enclosure");
++ b->encoding = ENC8BIT;
++
++ if (b->description) fs_give ((void**) &b->description);
++
++ what_we_did = result ? "This message was cryptographically signed." :
++ "This message was cryptographically signed but the signature could not be verified.";
++
++ b->description = cpystr(what_we_did);
++
++ b->sparep = p7;
++ p7 = NULL;
++ b->cleanup = smime_body_cleanup;
++
++ p = b->nested.part;
++
++ /* p is signed plaintext */
++ if (p && p->next)
++ mail_free_body_part(&p->next); /* hide the pkcs7 from the viewer */
++
++ result = 0;
++ }
++ end:
++ BIO_free(in);
++
++ PKCS7_free(p7);
++
++ if (toVerify)
++ so_give(&toVerify);
++
++ return result;
++ }
++
++ static PERSONAL_CERT *find_certificate_matching_recip_info(PKCS7_RECIP_INFO *ri)
++ {
++ PERSONAL_CERT *x;
++
++ for (x=s_personal_certs;x;x=x->next) {
++ X509 *mine;
++
++ mine = x->cert;
++
++ if (!X509_NAME_cmp(ri->issuer_and_serial->issuer,mine->cert_info->issuer) &&
++ !ASN1_INTEGER_cmp(ri->issuer_and_serial->serial,mine->cert_info->serialNumber)) {
++ break;
++ }
++ }
++
++ return x;
++ }
++
++ static PERSONAL_CERT *find_certificate_matching_pkcs7(PKCS7 *p7)
++ {
++ int i;
++ STACK_OF(PKCS7_RECIP_INFO) *recips;
++ PERSONAL_CERT *x = NULL;
++
++ recips = p7->d.enveloped->recipientinfo;
++
++ for (i=0; i<sk_PKCS7_RECIP_INFO_num(recips); i++) {
++ PKCS7_RECIP_INFO *ri;
++
++ ri=sk_PKCS7_RECIP_INFO_value(recips,i);
++
++ if ((x=find_certificate_matching_recip_info(ri))!=0) {
++ break;
++ }
++ }
++
++ return x;
++ }
++
++ /*
++ Try to decode (decrypt or verify a signature) a PKCS7 body
++ */
++ static int do_decoding(BODY *b,int msgno,const char *section)
++ {
++ STORE_S *outs = NULL;
++ int result = 0;
++
++ BIO *out = NULL;
++ PKCS7 *p7 = NULL;
++ X509 *recip = NULL;
++ EVP_PKEY *key = NULL;
++ PERSONAL_CERT *pcert = NULL;
++
++ char *what_we_did = "";
++
++ openssl_init();
++
++ /*
++ Extract binary data from part to an in-memory store
++ */
++
++ if (b->sparep) {
++
++ p7 = (PKCS7*) b->sparep;
++
++ } else {
++
++ p7 = get_pkcs7_from_part(msgno,section);
++ if (p7 == NULL) {
++ q_status_message1(SM_ORDER,1,1,"Couldn't load PKCS7 object: %s",(char*)openssl_error_string());
++ goto end;
++ }
++
++ /* Save the PKCS7 object for later dealings by the user interface.
++ It will be cleaned up when the body is garbage collected
++ */
++ b->sparep = p7;
++ b->cleanup = smime_body_cleanup;
++ }
++
++ if (PKCS7_type_is_signed(p7)) {
++ int sigok;
++
++ outs = so_get(CharStar, NULL, EDIT_ACCESS);
++ so_puts(outs,"MIME-Version: 1.0\r\n"); /* needed so rfc822_parse_msg_full believes it's MIME */
++ out = BIO_new_so(outs);
++
++ sigok = do_signature_verify(p7,NULL,out);
++
++ /* shouldn't really duplicate these messages */
++ what_we_did = sigok ? "This message was cryptographically signed." :
++ "This message was cryptographically signed but the signature could not be verified.";
++
++ } else if (!PKCS7_type_is_enveloped(p7)) {
++ q_status_message(SM_ORDER,1,1,"PKCS7 object not recognised.");
++ goto end;
++ } else { /* It *is* enveloped */
++
++ what_we_did = "This message was encrypted.";
++
++ /*
++ Now need to find a cert that can decrypt this boy
++ */
++ pcert = find_certificate_matching_pkcs7(p7);
++
++ if (!pcert) {
++ q_status_message(SM_ORDER,1,1,"Couldn't find the certificate needed to decrypt.");
++ goto end;
++ }
++
++ recip = pcert->cert;
++
++ load_private_key(pcert);
++
++ key = pcert->key;
++ if (!key)
++ goto end;
++
++ outs = so_get(CharStar, NULL, EDIT_ACCESS);
++ so_puts(outs,"MIME-Version: 1.0\r\n");
++
++ out = BIO_new_so(outs);
++
++ if(!PKCS7_decrypt(p7, key, recip, out, 0)) {
++ q_status_message1(SM_ORDER,1,1,"Error decrypting PKCS7: %s",(char*) openssl_error_string());
++ goto end;
++ }
++
++ }
++
++ /* We've now produced a flattened MIME object in store outs.
++ It needs to be turned back into a BODY
++ */
++
++ {
++ BODY *body;
++ ENVELOPE *env;
++ char *h;
++ char *bstart;
++ STRING s;
++
++ h = so_text(outs);
++
++ /* look for start of body */
++ bstart = strstr(h,"\r\n\r\n");
++
++ if (!bstart) {
++ q_status_message(SM_ORDER,1,1,"Encrypted data couldn't be parsed.");
++ } else {
++ bstart += 4; /* skip over CRLF*2 */
++
++ INIT(&s,mail_string,bstart,strlen(bstart));
++ rfc822_parse_msg_full(&env,&body,h,bstart-h-2,&s,BADHOST,0,0);
++ mail_free_envelope(&env); /* Don't care about this */
++
++ /*
++ Now convert original body (application/pkcs7-mime)
++ to a multipart body with one sub-part (the decrypted body)
++ Note that the sub-part may also be multipart!
++ */
++
++ b->type = TYPEMULTIPART;
++ if (b->subtype) fs_give((void**) &b->subtype);
++
++ /* This subtype is used in mailview.c to annotate the display of
++ encrypted or signed messages. We know for sure then that it's a PKCS7
++ part because the sparep field is set to the PKCS7 object (see above)
++ */
++ b->subtype = cpystr("x-pkcs7-enclosure");
++ b->encoding = ENC8BIT;
++
++ if (b->description) fs_give ((void**) &b->description);
++ b->description = cpystr(what_we_did);
++
++ if (b->disposition.type) fs_give ((void **) &b->disposition.type);
++
++ if (b->contents.text.data) fs_give ((void **) &b->contents.text.data);
++
++ if (b->parameter) mail_free_body_parameter(&b->parameter);
++
++ /* Allocate mem for the sub-part, and copy over the contents of our parsed body */
++ b->nested.part = fs_get(sizeof (PART));
++ b->nested.part->body = *body;
++ b->nested.part->next = NULL;
++
++ fs_give((void**) &body);
++
++ /* IMPORTANT BIT: set the body->contents.text.data elements to contain the decrypted
++ data. Otherwise, it'll try to load it from the original data. Eek.
++ */
++ create_local_cache(bstart,&b->nested.part->body);
++
++ result = 1;
++ }
++ }
++
++ end:
++
++ BIO_free(out);
++
++ if (outs)
++ so_give(&outs);
++
++ return result;
++ }
++
++ /*
++ Recursively handle PKCS7 bodies in our message.
++
++ Returns non-zero if some fiddling was done.
++ */
++ static int do_fiddle_smime_message(BODY *b,int msgno,char *section)
++ {
++ int result = 0;
++
++ if (is_pkcs7_body(b)) {
++
++ if (do_decoding(b,msgno,*section ? section : "1")) {
++ /*
++ b should now be a multipart message: fiddle it too in case it's been multiply-encrypted!
++ */
++
++ /* fallthru */
++ result = 1;
++ }
++ }
++
++ if (b->type==TYPEMULTIPART) {
++
++ PART *p;
++ int partNum;
++ char newSec[100];
++
++ if (b->subtype && strucmp(b->subtype,"signed")==0) {
++
++ /* Ahah. We have a multipart signed entity. */
++
++ /* part 1 (signed thing)
++ part 2 (the pkcs7 object)
++ */
++
++ do_detached_signature_verify(b,msgno,section);
++
++ } else {
++
++ for (p=b->nested.part,partNum=1;p;p=p->next,partNum++) {
++
++ /* Append part number to the section string */
++
++ snprintf(newSec,sizeof(newSec),"%s%s%d",section,*section ? "." : "",partNum);
++
++ result |= do_fiddle_smime_message(&p->body,msgno,newSec);
++ }
++
++ }
++
++ }
++
++ return result;
++ }
++
++ /*
++ Fiddle a message in-place by decrypting/verifying S/MIME entities.
++ Returns non-zero if something was changed.
++ */
++
++ int fiddle_smime_message(BODY *b,int msgno,int is_new_message)
++ {
++ if (F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
++ forget_private_keys();
++ return do_fiddle_smime_message(b,msgno,"");
++ }
++
++
++ /********************************************************************************/
++
++ static struct key smime_info_keys[] =
++ {HELP_MENU,
++ OTHER_MENU,
++ {"<","Back",{MC_VIEW_TEXT,2,{'<',','}},KS_EXITMODE},
++ NULL_MENU,
++ NULL_MENU,
++ NULL_MENU,
++ PREVPAGE_MENU,
++ NEXTPAGE_MENU,
++ NULL_MENU,
++ NULL_MENU,
++ NULL_MENU,
++ NULL_MENU,
++
++ HELP_MENU,
++ OTHER_MENU,
++ MAIN_MENU,
++ QUIT_MENU,
++ NULL_MENU,
++ NULL_MENU,
++ NULL_MENU,
++ NULL_MENU,
++ NULL_MENU,
++ INDEX_MENU,
++ NULL_MENU,
++ NULL_MENU,
++ };
++ INST_KEY_MENU(smime_info_keymenu, smime_info_keys);
++
++ #define SMIME_PARENT_KEY 2
++
++ static void get_fingerprint(X509 *cert,const EVP_MD *type,char *buf,int maxLen)
++ {
++ unsigned char md[128];
++ char *b;
++ int len,i;
++
++ len = sizeof(md);
++
++ X509_digest(cert,type,md,&len);
++
++ b = buf;
++ *b = 0;
++ for (i=0; i<len; i++)
++ {
++ if (b-buf+3>=maxLen)
++ break;
++
++ if (i != 0)
++ *b++ = ':';
++ sprintf(b,"%02x",md[i]);
++ b+=2;
++ }
++ }
++
++ static void output_X509_NAME(X509_NAME *name,gf_io_t pc)
++ {
++ int i,c;
++ int nid;
++ char buf[256];
++
++ c = X509_NAME_entry_count(name);
++
++ for (i=c-1;i>=0;i--) {
++ X509_NAME_ENTRY *e;
++
++ e = X509_NAME_get_entry(name,i);
++ if (!e) continue;
++
++ X509_NAME_get_text_by_OBJ(name, e->object,buf,sizeof(buf));
++
++ gf_puts(buf,pc);
++ gf_puts(NEWLINE,pc);
++ }
++
++ }
++
++ /*
++ Output a string in a distinctive style
++ */
++ static void gf_puts_uline(const char *txt,gf_io_t pc)
++ {
++ #if 0
++ pc(TAG_EMBED); pc(TAG_ULINEON);
++ gf_puts(txt,pc);
++ pc(TAG_EMBED); pc(TAG_ULINEOFF);
++ #else
++ pc(TAG_EMBED); pc(TAG_BOLDON);
++ gf_puts(txt,pc);
++ pc(TAG_EMBED); pc(TAG_BOLDOFF);
++ #endif
++ }
++
++ /*
++ Get a line from the given store (including \n)
++ */
++ static int so_gets(STORE_S *store,char *buf,int len)
++ {
++ unsigned char c;
++ char *bend = buf + len - 1;
++ char *b = buf;
++
++ do {
++ if (!store->readc(&c,store)) {
++ *b = 0;
++ return b!=buf;
++ }
++ *b++ = c;
++ } while (c!='\n' && b<bend);
++
++ *b = 0;
++
++ return 1;
++ }
++
++ /*
++ Wrap the text in the given store to the given width.
++ A new store is created for the result.
++ */
++ static STORE_S *wrap_store(STORE_S *in,int width)
++ {
++ STORE_S *result;
++ void *ws;
++ gf_io_t ipc,opc;
++
++ if (width<10)
++ width = 10;
++
++ result = so_get(CharStar,NULL,EDIT_ACCESS);
++ ws = gf_wrap_filter_opt(width,width,0,0);
++
++ gf_filter_init();
++ gf_link_filter(gf_wrap,ws);
++
++ gf_set_so_writec(&opc,result);
++ gf_set_so_readc(&ipc,in);
++
++ gf_pipe(ipc,opc);
++
++ gf_clear_so_readc(in);
++ gf_clear_so_writec(result);
++
++ return result;
++ }
++
++ /*
++ Output the contents of the given stores (left and right)
++ to the given gf_io_t.
++ The width of the terminal is inspected and two columns
++ are created to fit the stores into. They are then wrapped
++ and merged.
++ */
++ static void side_by_side(STORE_S *left,STORE_S *right,gf_io_t pc)
++ {
++ STORE_S *left_wrapped;
++ STORE_S *right_wrapped;
++
++ char buf_l[256];
++ char buf_r[256];
++ char *b;
++ int i;
++ int w = ps_global->ttyo->screen_cols/2 - 1;
++
++ so_seek(left,0,0);
++ so_seek(right,0,0);
++
++ left_wrapped = wrap_store(left,w);
++ right_wrapped = wrap_store(right,w);
++
++ so_seek(left_wrapped,0,0);
++ so_seek(right_wrapped,0,0);
++
++ for (;;) {
++
++ i = so_gets(left_wrapped,buf_l,sizeof(buf_l));
++ i += so_gets(right_wrapped,buf_r,sizeof(buf_r));
++
++ if (i==0)
++ break;
++
++ for (i=0, b=buf_l;i<w && *b && *b!='\r' && *b!='\n';i++,b++) {
++ pc(*b);
++ /* reduce accumulated width if an embed tag is discovered */
++ if (*b==TAG_EMBED)
++ i-=2;
++ }
++
++ if (buf_r[0]) {
++
++ while (i<w) {
++ pc(' ');
++ i++;
++ }
++
++ for (i=0, b=buf_r;i<w && *b && *b!='\r' && *b!='\n';i++,b++) {
++ pc(*b);
++ }
++ }
++
++ gf_puts(NEWLINE,pc);
++ }
++
++ so_give(&left_wrapped);
++ so_give(&right_wrapped);
++ }
++
++ static void print_separator_line(int percent,int ch,gf_io_t pc)
++ {
++ int i;
++ int start,len;
++
++ len = ps_global->ttyo->screen_cols * percent / 100;
++ start = (ps_global->ttyo->screen_cols - len)/2;
++
++ for (i=0;i<start;i++)
++ pc(' ');
++ for (i=start;i<start+len;i++)
++ pc(ch);
++ gf_puts(NEWLINE,pc);
++ }
++
++ static void output_cert_info(X509 *cert,gf_io_t pc)
++ {
++ char buf[256];
++ STORE_S *left,*right;
++ gf_io_t spc;
++ int i;
++
++ left = so_get(CharStar,NULL,EDIT_ACCESS);
++ right = so_get(CharStar,NULL,EDIT_ACCESS);
++
++ gf_set_so_writec(&spc,left);
++
++ if (!cert->cert_info) {
++ gf_puts("Couldn't find certificate info.",spc);
++ gf_puts(NEWLINE,spc);
++ } else {
++
++ gf_puts_uline("Subject (whose certificate it is)",spc);
++ gf_puts(NEWLINE, spc);
++
++ output_X509_NAME(cert->cert_info->subject,spc);
++ gf_puts(NEWLINE,spc);
++
++ gf_puts_uline("Serial Number",spc);
++ gf_puts(NEWLINE,spc);
++
++ sprintf(buf,"%d",ASN1_INTEGER_get(cert->cert_info->serialNumber));
++ gf_puts(buf,spc);
++ gf_puts(NEWLINE,spc);
++ gf_puts(NEWLINE,spc);
++
++ gf_puts_uline("Validity",spc);
++ gf_puts(NEWLINE,spc);
++ {
++ BIO *mb = BIO_new_so(left);
++
++ gf_puts("Not Before: ",spc);
++ ASN1_UTCTIME_print(mb,cert->cert_info->validity->notBefore);
++ BIO_flush(mb);
++ gf_puts(NEWLINE,spc);
++
++ gf_puts("Not After: ",spc);
++ ASN1_UTCTIME_print(mb,cert->cert_info->validity->notAfter);
++ BIO_flush(mb);
++
++ gf_puts(NEWLINE,spc);
++ gf_puts(NEWLINE,spc);
++
++ BIO_free(mb);
++ }
++
++ }
++
++ gf_clear_so_writec(left);
++
++ gf_set_so_writec(&spc,right);
++
++ if (!cert->cert_info) {
++ gf_puts("Couldn't find certificate info.",spc);
++ gf_puts(NEWLINE,spc);
++ } else {
++ gf_puts_uline("Issuer",spc);
++ gf_puts(NEWLINE, spc);
++
++ output_X509_NAME(cert->cert_info->issuer,spc);
++ gf_puts(NEWLINE,spc);
++ }
++
++ gf_clear_so_writec(right);
++
++ side_by_side(left,right,pc);
++
++ gf_puts_uline("SHA1 Fingerprint",pc);
++ gf_puts(NEWLINE,pc);
++ get_fingerprint(cert,EVP_sha1(),buf,sizeof(buf));
++ gf_puts(buf,pc);
++ gf_puts(NEWLINE,pc);
++
++ gf_puts_uline("MD5 Fingerprint",pc);
++ gf_puts(NEWLINE,pc);
++ get_fingerprint(cert,EVP_md5(),buf,sizeof(buf));
++ gf_puts(buf,pc);
++ gf_puts(NEWLINE,pc);
++
++ so_give(&left);
++ so_give(&right);
++ }
++
++ void format_smime_info(int pass,BODY *body,int msgno,gf_io_t pc)
++ {
++ PKCS7 *p7;
++ int i;
++
++ if (body->type==TYPEMULTIPART) {
++ PART *p;
++
++ for (p=body->nested.part;p;p=p->next) {
++ format_smime_info(pass,&p->body,msgno,pc);
++ }
++ }
++
++ p7 = body->sparep;
++ if (p7) {
++
++ if (PKCS7_type_is_signed(p7)) {
++ STACK_OF(X509) *signers;
++
++ switch (pass) {
++ case 1:
++ gf_puts("This message was cryptographically signed." NEWLINE,pc);
++ break;
++ case 2:
++
++ signers = PKCS7_get0_signers(p7, NULL, 0);
++
++ if (signers) {
++
++ sprintf(tmp_20k_buf,"Certificate%s used for signing",plural(sk_X509_num(signers)));
++ gf_puts_uline(tmp_20k_buf,pc);
++ gf_puts(NEWLINE,pc);
++ print_separator_line(100,'-',pc);
++
++ for (i=0;i<sk_X509_num(signers);i++) {
++ X509 *x = sk_X509_value(signers,i);
++
++ if (x) {
++ output_cert_info(x,pc);
++ gf_puts(NEWLINE,pc);
++ }
++
++ }
++ }
++
++ sk_X509_free(signers);
++ break;
++ }
++
++ } else if (PKCS7_type_is_enveloped(p7)) {
++
++ switch (pass) {
++ case 1:
++ gf_puts("This message was encrypted." NEWLINE,pc);
++ break;
++ case 2:
++
++ if (p7->d.enveloped && p7->d.enveloped->enc_data) {
++ X509_ALGOR *alg = p7->d.enveloped->enc_data->algorithm;
++ STACK_OF(PKCS7_RECIP_INFO) *ris = p7->d.enveloped->recipientinfo;
++ int found = 0;
++
++ gf_puts("The algorithm used to encrypt was ",pc);
++
++
++ if (alg) {
++ char *n = OBJ_nid2sn( OBJ_obj2nid(alg->algorithm ));
++
++ gf_puts(n ? n : "<unknown>",pc);
++
++ } else gf_puts("<unknown>",pc);
++
++ gf_puts("." NEWLINE NEWLINE,pc);
++
++ sprintf(tmp_20k_buf,"Certificate%s for decrypting",plural(sk_PKCS7_RECIP_INFO_num(ris)));
++ gf_puts_uline(tmp_20k_buf,pc);
++ gf_puts(NEWLINE,pc);
++ print_separator_line(100,'-',pc);
++
++ for (i=0;i<sk_PKCS7_RECIP_INFO_num(ris);i++) {
++ PKCS7_RECIP_INFO *ri;
++ PERSONAL_CERT *pcert;
++
++ ri = sk_PKCS7_RECIP_INFO_value(ris,i);
++ if (!ri) continue;
++
++ pcert = find_certificate_matching_recip_info(ri);
++
++ if (pcert) {
++
++ if (found) {
++ print_separator_line(25,'*',pc);
++ gf_puts(NEWLINE,pc);
++ }
++ found = 1;
++
++ output_cert_info(pcert->cert,pc);
++ gf_puts(NEWLINE,pc);
++
++ }
++ }
++
++ if (!found) {
++ gf_puts("No certificate capable of decrypting could not be found.",pc);
++ }
++ }
++ break;
++ }
++
++ }
++ }
++ }
++
++ void view_writec();
++
++ void smime_info_screen(struct pine *ps)
++ {
++ int msgno;
++ OtherMenu what;
++ int cmd;
++ char backtag[64];
++ BODY *body;
++ ENVELOPE *env;
++ HANDLE_S *handles = NULL;
++ SCROLL_S scrollargs;
++ STORE_S *store = NULL;
++ int offset = 0;
++
++ ps->prev_screen = smime_info_screen;
++ ps->next_screen = SCREEN_FUN_NULL;
++
++ if(mn_total_cur(ps->msgmap) > 1L){
++ q_status_message(SM_ORDER | SM_DING, 0, 3,
++ "Can only view one message's information at a time.");
++ return;
++ }
++ /* else check for existence of smime bits */
++
++ msgno = mn_m2raw(ps->msgmap, mn_get_cur(ps->msgmap));
++
++ env = mail_fetch_structure (ps->mail_stream,msgno,
++ &body,0);
++ if (!env || !body) {
++ q_status_message(SM_ORDER, 0, 3,
++ "Can't fetch body of message.");
++ return;
++ }
++
++ what = FirstMenu;
++
++ store = so_get(CharStar, NULL, EDIT_ACCESS);
++
++ while(ps->next_screen == SCREEN_FUN_NULL){
++
++ ClearLine(1);
++
++ so_truncate(store,0);
++
++ view_writec_init(store, &handles, HEADER_ROWS(ps),
++ HEADER_ROWS(ps) +
++ ps->ttyo->screen_rows - (HEADER_ROWS(ps)
++ + HEADER_ROWS(ps)));
++
++ gf_puts_uline("Overview",view_writec);
++ gf_puts(NEWLINE,view_writec);
++
++ format_smime_info(1,body,msgno,view_writec);
++ gf_puts(NEWLINE,view_writec);
++ format_smime_info(2,body,msgno,view_writec);
++
++ view_writec_destroy();
++
++
++ ps->next_screen = SCREEN_FUN_NULL;
++
++ memset(&scrollargs, 0, sizeof(SCROLL_S));
++ scrollargs.text.text = so_text(store);
++ scrollargs.text.src = CharStar;
++ scrollargs.text.desc = "S/MIME information";
++ scrollargs.body_valid = 1;
++
++ if(offset){ /* resize? preserve paging! */
++ scrollargs.start.on = Offset;
++ scrollargs.start.loc.offset = offset;
++ offset = 0L;
++ }
++
++ scrollargs.bar.title = "S/MIME INFORMATION";
++ /* scrollargs.end_scroll = view_end_scroll; */
++ scrollargs.resize_exit = 1;
++ scrollargs.help.text = NULL;
++ scrollargs.help.title = "HELP FOR S/MIME INFORMATION VIEW";
++ scrollargs.keys.menu = &smime_info_keymenu;
++ scrollargs.keys.what = what;
++ setbitmap(scrollargs.keys.bitmap);
++
++ if(scrolltool(&scrollargs) == MC_RESIZE)
++ offset = scrollargs.start.loc.offset;
++ }
++
++ so_give(&store);
++
++ }
++
++
++ #endif /* SMIME */
+diff -Ncr pine4.53/pine/smime.h pine4.53-smime/pine/smime.h
+*** pine4.53/pine/smime.h Wed Dec 31 16:00:00 1969
+--- pine4.53-smime/pine/smime.h Wed Jan 15 12:48:15 2003
+***************
+*** 0 ****
+--- 1,17 ----
++ #ifdef SMIME
++
++ int is_pkcs7_body(BODY *b);
++
++ int fiddle_smime_message(BODY *b,int msgno,int is_new_message);
++
++ int encrypt_outgoing_message(METAENV *header,BODY **bodyP);
++ int sign_outgoing_message(METAENV *header,BODY **bodyP,int dont_detach);
++ int get_passphrase(void);
++ void smime_info_screen(struct pine *ps);
++
++ extern int g_need_passphrase;
++
++ extern int g_do_encrypt;
++ extern int g_do_sign;
++
++ #endif /* SMIME */
+diff -Ncr pine4.53/pine/smkeys.c pine4.53-smime/pine/smkeys.c
+*** pine4.53/pine/smkeys.c Wed Dec 31 16:00:00 1969
+--- pine4.53-smime/pine/smkeys.c Wed Jan 15 12:48:15 2003
+***************
+*** 0 ****
+--- 1,343 ----
++ #ifdef SMIME
++
++ #include "headers.h"
++
++ #include <openssl/err.h>
++ #include <openssl/objects.h>
++ #include <openssl/evp.h>
++ #include <openssl/x509.h>
++ #include <openssl/pkcs7.h>
++ #include <openssl/pem.h>
++
++ #include "smkeys.h"
++
++ /*---------------------------------------------------
++ Remove leading whitespace, trailing whitespace and convert
++ to lowercase. Also remove slash characters
++
++ Args: s, -- The string to clean
++
++ Result: the cleaned string
++ ----*/
++ static char *
++ emailstrclean(string)
++ char *string;
++ {
++ char *s = string, *sc = NULL, *p = NULL;
++
++ for(; *s; s++){ /* single pass */
++ if(!isspace((unsigned char)*s)){
++ p = NULL; /* not start of blanks */
++ if(!sc) /* first non-blank? */
++ sc = string; /* start copying */
++ }
++ else if(!p) /* it's OK if sc == NULL */
++ p = sc; /* start of blanks? */
++
++ if(sc && *s!='/' && *s!='\\') /* if copying, copy */
++ *sc++ = isupper((unsigned char)(*s))
++ ? (unsigned char)tolower((unsigned char)(*s))
++ : (unsigned char)(*s);
++ }
++
++ if(p) /* if ending blanks */
++ *p = '\0'; /* tie off beginning */
++ else if(!sc) /* never saw a non-blank */
++ *string = '\0'; /* so tie whole thing off */
++
++ return(string);
++ }
++
++ /*
++ Add a lookup for each "*.crt" file in the given directory.
++ */
++ static void add_certs_in_dir(X509_LOOKUP *lookup,const char *path)
++ {
++ char buf[MAXPATH];
++ struct direct *d;
++ DIR *dirp;
++ PERSONAL_CERT *result;
++
++ result = NULL;
++
++ dirp = opendir(path);
++ if (dirp) {
++
++ while (d=readdir(dirp)) {
++ BIO *in;
++ X509 *cert;
++
++ if (srchrstr(d->d_name,".crt")) {
++
++ build_path(buf,(char*) path,d->d_name,sizeof(buf));
++
++ X509_LOOKUP_load_file(lookup,buf,X509_FILETYPE_PEM);
++ }
++
++ }
++
++ closedir(dirp);
++ }
++ }
++
++ /* Get an X509_STORE. This consists of the system
++ certs directory and any certificates in the user's
++ ~/.pine-smime/ca directory.
++ */
++ X509_STORE *get_ca_store(const char *path)
++ {
++ X509_LOOKUP *lookup;
++ char buf[MAXPATH];
++
++ X509_STORE *store;
++
++ store=X509_STORE_new();
++
++ lookup=X509_STORE_add_lookup(store,X509_LOOKUP_file());
++
++ build_path(buf,(char*) path,"ca",sizeof(buf));
++
++ add_certs_in_dir(lookup,buf);
++
++ /* X509_LOOKUP_load_file(lookup,NULL,X509_FILETYPE_DEFAULT); */
++
++ lookup = X509_STORE_add_lookup(store,X509_LOOKUP_hash_dir());
++
++ X509_LOOKUP_add_dir(lookup,SSL_CERT_DIRECTORY,X509_FILETYPE_PEM);
++
++ /* X509_STORE_set_default_paths(cert_store);
++ X509_STORE_load_locations(cert_store,NULL,"../../certs");
++ X509_STORE_set_verify_cb_func(cert_store,verify_callback);
++ */
++
++ return store;
++ }
++
++ static EVP_PKEY *load_key(char *file, char *pass)
++ {
++ BIO *in;
++ EVP_PKEY *key;
++ if(!(in = BIO_new_file(file, "r"))) return NULL;
++ key = PEM_read_bio_PrivateKey(in, NULL,NULL,pass);
++ BIO_free(in);
++ return key;
++ }
++
++ char *get_x509_name_entry(const char *key,X509_NAME *name)
++ {
++ int i,c,n;
++ char buf[256];
++ char *id;
++
++ if (!name)
++ return NULL;
++
++ c = X509_NAME_entry_count(name);
++
++ for (i=0;i<c;i++) {
++ X509_NAME_ENTRY *e;
++
++ e = X509_NAME_get_entry(name,i);
++ if (!e) continue;
++
++ buf[0] = 0;
++ id = buf;
++
++ n = OBJ_obj2nid(e->object);
++ if ((n == NID_undef) || ((id=(char*) OBJ_nid2sn(n)) == NULL)) {
++ i2t_ASN1_OBJECT(buf,sizeof(buf),e->object);
++ id = buf;
++ }
++
++ if ((strucmp(id,"email")==0) || (strucmp(id,"emailAddress")==0)) {
++ X509_NAME_get_text_by_OBJ(name, e->object,(char*) buf,sizeof(buf)-1);
++ return cpystr(buf);
++ }
++ }
++ return NULL;
++ }
++
++ char *get_x509_subject_email(X509 *x)
++ {
++ char* result;
++ result = get_x509_name_entry("email",X509_get_subject_name(x));
++ if ( !result ) {
++ result = get_x509_name_entry("emailAddress",X509_get_subject_name(x));
++ }
++
++ return result;
++ }
++
++ /* Save the certificate for the given email address in
++ ~/.pine-smime/public.
++
++ Should consider the security hazards in making a file with
++ the email address that has come from the certificate.
++
++ The argument email is destroyed.
++ */
++
++ void save_cert_for(const char *path,char *email,X509 *cert)
++ {
++ char sf[MAXPATH];
++ char sf2[MAXPATH];
++ BIO *tmp;
++
++ build_path(sf,(char*) path,"public",sizeof(sf));
++
++ build_path(sf2,sf,emailstrclean(email),sizeof(sf2));
++ strncat(sf2,".crt",sizeof(sf2)-1-strlen(sf2));
++ sf2[sizeof(sf2)-1] = 0;
++
++ tmp = BIO_new_file(sf2, "w");
++ if (tmp) {
++ X509_print(tmp,cert);
++ PEM_write_bio_X509_AUX(tmp, cert);
++ BIO_free(tmp);
++ q_status_message1(SM_ORDER,1,1,"Saved certificate for <%s>",(char*)email);
++ } else {
++ q_status_message1(SM_ORDER,1,1,"Couldn't save certificate for <%s>",(char*)email);
++ }
++ }
++
++ /*
++ Try to retrieve the certificate for the given email address.
++ */
++ X509 *get_cert_for(const char *path,const char *email)
++ {
++ char buf[MAXPATH];
++ char buf2[MAXPATH];
++ char buf3[MAXPATH];
++ char *p;
++ X509 *cert = NULL;
++ BIO *in;
++
++ strncpy(buf3,email,sizeof(buf3)-1);
++ buf3[sizeof(buf3)-1] = 0;
++
++ /* clean it up (lowercase, space removal) */
++ emailstrclean(buf3);
++
++ build_path(buf,(char*)path,"public",sizeof(buf));
++ build_path(buf2,buf,buf3,sizeof(buf2));
++ strncat(buf2,".crt",sizeof(buf2)-1-strlen(buf2));
++ buf2[sizeof(buf2)-1] = 0;
++
++ if((in = BIO_new_file(buf2, "r"))!=0) {
++
++ cert = PEM_read_bio_X509(in, NULL, NULL,NULL);
++
++ if (cert) {
++ /* could check email addr in cert matches */
++ }
++ }
++ BIO_free(in);
++
++ return cert;
++ }
++
++ EVP_PKEY *get_key_for(const char *path,const char *email,const char *pass)
++ {
++ char buf[MAXPATH];
++ char buf2[MAXPATH];
++ char buf3[MAXPATH];
++ char *p;
++ EVP_PKEY *key = NULL;
++ BIO *in;
++
++ strncpy(buf3,email,sizeof(buf3)-1);
++ buf3[sizeof(buf3)-1] = 0;
++
++ /* clean it up (lowercase, space removal) */
++ emailstrclean(buf3);
++
++ build_path(buf,(char*)path,"private",sizeof(buf));
++ build_path(buf2,buf,buf3,sizeof(buf2));
++ strncat(buf2,".key",sizeof(buf2)-1-strlen(buf2));
++ buf2[sizeof(buf2)-1] = 0;
++
++ key = load_key(buf2,pass);
++
++ return key;
++ }
++
++ /*
++ Load the user's personal certificates from
++ ~/.pine-smime/private
++ */
++ PERSONAL_CERT *get_personal_certs(const char *path)
++ {
++ char buf[MAXPATH];
++ char buf2[MAXPATH];
++ struct direct *d;
++ DIR *dirp;
++ PERSONAL_CERT *result;
++
++ result = NULL;
++
++ build_path(buf,(char*) path,"private",sizeof(buf));
++
++ dirp = opendir(buf);
++ if (dirp) {
++
++ while (d=readdir(dirp)) {
++ BIO *in;
++ X509 *cert;
++
++ if (srchrstr(d->d_name,".key")) {
++
++ /* copy file name to temp buffer */
++ strcpy(buf2,d->d_name);
++ /* chop off ".key" trailier */
++ buf2[strlen(buf2)-4] = 0;
++ /* Look for certificate */
++ cert = get_cert_for(path,buf2);
++
++ if (cert) {
++ PERSONAL_CERT *pc;
++
++ /* create a new PERSONAL_CERT, fill it in */
++
++ pc = fs_get(sizeof(PERSONAL_CERT));
++ pc->cert = cert;
++ build_path(buf2,buf,d->d_name,sizeof(buf2));
++ pc->file = cpystr(buf2);
++
++ strcpy(pc->file + strlen(pc->file) - 4, ".key");
++
++ /* Try to load the key with an empty password */
++ pc->key = load_key(pc->file,"");
++
++ pc->next = result;
++ result = pc;
++ }
++ }
++
++ }
++
++ closedir(dirp);
++ }
++
++ return result;
++ }
++
++ void personal_cert_free(PERSONAL_CERT **pcp)
++ {
++ if (pcp && *pcp) {
++
++ PERSONAL_CERT *pc = *pcp;
++
++ fs_give((void**) &pc->file);
++
++ X509_free(pc->cert);
++
++ if (pc->key)
++ EVP_PKEY_free(pc->key);
++
++ personal_cert_free(&pc->next);
++
++ fs_give((void**) pcp);
++ }
++ }
++
++ #endif /* SMIME */
+diff -Ncr pine4.53/pine/smkeys.h pine4.53-smime/pine/smkeys.h
+*** pine4.53/pine/smkeys.h Wed Dec 31 16:00:00 1969
+--- pine4.53-smime/pine/smkeys.h Wed Jan 15 12:48:15 2003
+***************
+*** 0 ****
+--- 1,20 ----
++
++ #define PERSONAL_CERT struct personal_cert
++
++ PERSONAL_CERT {
++ X509 *cert;
++ EVP_PKEY *key;
++ char *file;
++ PERSONAL_CERT *next;
++ };
++
++ X509_STORE *get_ca_store(const char *d);
++
++ PERSONAL_CERT *get_personal_certs(const char *d);
++
++ X509 *get_cert_for(const char *path,const char *email);
++ void save_cert_for(const char *path,char *email,X509 *cert);
++
++ void personal_cert_free(PERSONAL_CERT **pc);
++
++ char *get_x509_subject_email(X509 *x);
diff --git a/contrib/utils/ansiprt.c b/contrib/utils/ansiprt.c
new file mode 100755
index 00000000..17d6e068
--- /dev/null
+++ b/contrib/utils/ansiprt.c
@@ -0,0 +1,60 @@
+/*
+ * ansiprt.c
+ *
+ * Simple filter to wrap ANSI media copy escape sequences around
+ * text on stdin. Writes /dev/tty to get around things that might be
+ * trapping stdout. This is actually a feature because it was written
+ * to be used with pine's personal print option set up to take "enscript"
+ * output and send it displayward to be captured/printed to a postscript
+ * device. Pine, of course, uses popen() to invoke the personal print
+ * command, and interprets stdout as diagnostic messages from the command.
+ *
+ * Michael Seibel, mikes@cac.washington.edu
+ *
+ * 21 Apr 92
+ *
+ */
+#include <stdio.h>
+#include <sys/file.h>
+
+#define BUFSIZ 8192
+
+main(argc, argv)
+int argc;
+char **argv;
+{
+ char c[BUFSIZ];
+ int n, d;
+ int ctrld = 0;
+
+ if(argc > 1){
+ n = 0;
+ while(argc > ++n){
+ if(argv[n][0] == '-'){
+ switch(argv[n][1]){
+ case 'd':
+ ctrld++;
+ break;
+ default :
+ fprintf(stderr,"unknown option: %c\n", argv[n][1]);
+ break;
+ }
+ }
+ }
+ }
+
+ if((d=open("/dev/tty",O_WRONLY)) < 0){
+ perror("/dev/tty");
+ exit(1);
+ }
+
+ write(d,"\033[5i", 4);
+ while((n=read(0, c, BUFSIZ)) > 0)
+ write(d, c, n);
+
+ if(ctrld)
+ write(d, "\004", 1);
+
+ write(d,"\033[4i", 4);
+ close(d);
+}
diff --git a/contrib/utils/brk2pine.sh b/contrib/utils/brk2pine.sh
new file mode 100755
index 00000000..fab1f885
--- /dev/null
+++ b/contrib/utils/brk2pine.sh
@@ -0,0 +1,74 @@
+#!/bin/sh
+#
+# T H E P I N E M A I L S Y S T E M
+#
+# Laurence Lundblade and Mike Seibel
+# Networks and Distributed Computing
+# Computing and Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, Washington, 98195, USA
+# Internet: lgl@CAC.Washington.EDU
+# mikes@CAC.Washington.EDU
+#
+# Please address all bugs and comments to "pine-bugs@cac.washington.edu"
+#
+# Copyright 1989, 1990, 1991, 1992 University of Washington
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose and without fee to the University of
+# Washington is hereby granted, provided that the above copyright notice
+# appears in all copies and that both the above copyright notice and this
+# permission notice appear in supporting documentation, and that the name of
+# the University of Washington not be used in advertising or publicity
+# pertaining to distribution of the software without specific, written prior
+# permission. This software is made available "as is", and
+# THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
+# WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
+# NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
+# INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
+# (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+#
+# Pine is in part based on The Elm Mail System:
+# ***********************************************************************
+# * The Elm Mail System - $Revision: 2.13 $ $State: Exp $ *
+# * *
+# * Copyright (c) 1986, 1987 Dave Taylor *
+# * Copyright (c) 1988, 1989 USENET Community Trust *
+# ***********************************************************************
+#
+#
+
+
+
+#
+# A filter to convert personal mail aliases in a .mailrc file into
+# pine address book format.
+#
+# Usage: program [.mailrc] >> .addressbook
+#
+# Corey Satten, corey@cac.washington.edu, 9/25/91
+#
+sed -n '
+# first fold continued lines (ending in \) into a single long line
+ /\\[ ]*$/ {
+ : more
+ s/\\//g
+ N
+ s/\n/ /
+ /\\/b more
+ }
+# next convert all sequences of whitespace into single space
+ s/[ ][ ]*/ /g
+# finally, reformat and print lines containing alias as the first word
+ /^ *alias / {
+ s/^ *alias \([!-~][!-~]*\) \(.*\)$/\1 \1 (\2)/
+ s/ /,/g
+ s/(\([^,]*\))/\1/
+ p
+ }
+' ${*-$HOME/.mailrc}
diff --git a/contrib/utils/mailtrfc.sh b/contrib/utils/mailtrfc.sh
new file mode 100755
index 00000000..2ef69cdc
--- /dev/null
+++ b/contrib/utils/mailtrfc.sh
@@ -0,0 +1,130 @@
+#!/bin/sh
+#
+# T H E P I N E M A I L S Y S T E M
+#
+# Laurence Lundblade and Mike Seibel
+# Networks and Distributed Computing
+# Computing and Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, Washington, 98195, USA
+# Internet: lgl@CAC.Washington.EDU
+# mikes@CAC.Washington.EDU
+#
+# Please address all bugs and comments to "pine-bugs@cac.washington.edu"
+#
+# Copyright 1991, 1992 University of Washington
+#
+# Permission to use, copy, modify, and distribute this software and its
+# documentation for any purpose and without fee is hereby granted, provided
+# that the above copyright notice appears in all copies and that both the
+# above copyright notice and this permission notice appear in supporting
+# documentation, and that the name of the University of Washington not be
+# used in advertising or publicity pertaining to distribution of the software
+# without specific, written prior permission. This software is made
+# available "as is", and
+# THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
+# WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
+# NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
+# INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
+# (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+#
+# Pine is in part based on The Elm Mail System:
+# ***********************************************************************
+# * The Elm Mail System - $Revision: 2.13 $ $State: Exp $ *
+# * *
+# * Copyright (c) 1986, 1987 Dave Taylor *
+# * Copyright (c) 1988, 1989 USENET Community Trust *
+# ***********************************************************************
+#
+#
+
+
+#
+# mailtrfc.sh -- A shell script to analyze the mail traffic as logged in
+# /usr/spool/mqueue/syslog*. This currently as the University of Washington
+# domains wired in and needs to be made more general. Also, lots more
+# formats of message ID's could be added.
+#
+
+
+
+org=`awk '/^domain/ {print $2}' < /etc/resolv.conf`
+domain=`echo $org | sed -e 's/^[^.]*\.//'`
+host=`hostname`".$org"
+
+echo "Domain: $domain"
+echo "Organization: $org"
+echo "Hostname: $host"
+
+sed -n -e '/message-id/s/^.*</</p' |
+awk 'BEGIN {mailers[0] = "Other";
+ mailers[1] = "Pine";
+ mailers[2] = "MailManager";
+ mailers[3] = "sendmail";
+ mailers[4] = "BITNET";
+ mailers[5] = "? news ?";
+ mailers[6] = "Sprint";
+ mailers[7] = "X.400";
+ mailers[8] = "Mac MS";
+ mailers[9] = "MMDF";
+ mailers[10] = "Andrew";
+ mailers[11] = "Columbia MM";
+ mailers[12] = "Unknown #1";
+ mailers[13] = "EasyMail";
+ mailers[14] = "CompuServe";
+ mailers[15] = "smail";
+ mailers[16] = "MCI Mail";
+ mailers[17] = "VAX MAIL(?)";
+ mailers[18] = "Gator Mail (?)";
+ mailers[19] = "TOTAL";
+ max = 19;}
+ {mailer = 0;}
+ /^<Pine/ {mailer = 1;}
+ /^<MailManager/ {mailer = 2;}
+ /^<[12]?[90]?9[0-9]1?[0-9][1-3]?[0-9]+\.[AaBb][AaBb][0-9]+@/ {mailer = 3;}
+ /^<[0-9A-Z]+@/ {mailer = 4;}
+ /^<199[0-9][A-Za-z]..[0-9]*\./ {mailer = 5;}
+ /@sprint.com/ {mailer = 6;}
+ /\/[A-Z]*=.*\/[A-Z]*=.*/ {mailer = 7;}
+ /^<MacMS\.[0-9]+\.[0-9]+\.[a-z]+@/ {mailer = 8;}
+ /^<MAILQUEUE-[0-9]+\.[0-9]+/ {mailer = 9;}
+ /^<.[d-l][A-Z0-9a-z=_]+00[A-Za-z0-9_=]+@/ {mailer = 10;}
+ /^<CMM\.[0-9]+\.[0-9]+\.[0-9]+/ {mailer = 11 ;}
+ /^<9[0-9][JFMASOND][aepuco][nbrylgptvc][0-9][0-9]?\.[0-9]+[a-z]+\./ {mailer = 12;}
+ /^<EasyMail\.[0-9]+/ {mailer = 13;}
+ /@CompuServe.COM/ {mailer = 14;}
+ /^<m[A-Za-z0-9].....-[0-9A-Za-z].....C@/ {mailer = 15;}
+ /@mcimail.com/ {mailer = 16;}
+ /^<9[0-9][01][0-9][0-3][0-9][0-2][0-9][0-5][0-9][0-5][0-9].[0-9a-z]*@/ {mailer = 17;}
+ /^<0[0-9][0-9]+\.[0-9][0-9][0-9][0-9]+\.[0-9][0-9]+@/ {mailer=18;}
+
+
+
+ '"/$domain>/"' {campus[mailer]++; campus[max]++}
+ '"/$org>/"' {u[mailer]++; u[max]++}
+ '"/$host>/"' {milton[mailer]++; milton[max]++}
+ {total[mailer]++; total[max]++}
+ {if(mailer == 0) printf("-->%s\n",$0)}
+ END {
+ for(m = 0; m <= max; m++) {
+ printf("%-10.10s", mailers[m]);
+ printf(" %11d %11d %11d %11d %11d (%3d%%)\n", milton[m], u[m] - milton[m], campus[m] -u[m], total[m] - campus[m], total[m], (total[m]*100)/total[max]);
+ }
+ printf(" ---- (%3d%%) (%3d%%) (%3d%%) (%3d%%)\n", (milton[max]*100)/total[max], ((u[max] - milton[max])*100)/total[max], ((campus[max] - u[max])*100)/total[max], ((total[max] - campus[max])*100)/total[max], (u[max]*100)/total[max]);
+
+ }' > /tmp/syslogx.$$
+
+
+echo $host $org $domain | \
+ awk '{printf(" %.17s %.11s %.11s Off Campus Total\n", $1, $2, $3)}'
+egrep -v 'TOTAL|----|^-->' /tmp/syslogx.$$ | sort +0.60rn
+egrep 'TOTAL|----' /tmp/syslogx.$$
+grep '^-->' /tmp/syslogx.$$ | sed -e 's/-->//' > other-traffic
+rm -f /tmp/syslogx.$$
+
+
diff --git a/contrib/utils/pwd2pine b/contrib/utils/pwd2pine
new file mode 100755
index 00000000..49b3de65
--- /dev/null
+++ b/contrib/utils/pwd2pine
@@ -0,0 +1,73 @@
+#!/usr/local/bin/perl
+#
+# passwd2pine - translate passwd to pine addressbook
+#
+# Author: Paul J Murphy <pjm@dcs.ed.ac.uk>
+#
+# The fullname field will end up falling into one of the following cases:
+#
+# Last, First - A person
+# }group: fullname - A non-person (determined by group or uid/gid < 1024)
+# ~user: fullname - A non-person (determined by username or fullname)
+#
+# This allows the output to be sorted by fullname, with administrative
+# accounts left to last (ie the real people come first).
+#
+# The comment field will contain the groupname of the user, followed by
+# anything from the gecos field which seems to be a comment and not part
+# of the name.
+
+### Start of configurable stuff
+
+$domain = "dcs.ed.ac.uk";
+
+# Regular expression for groups which don't contain people
+# Case is significant.
+# The expression must match the entire groupname string.
+$non_people_groups = "local|misc|aliens|cs_dept|\\d*";
+
+# Regular expression for users which are not people
+# Case is significant.
+# The expression must match the entire username string.
+$non_people_users = ".*\\d.*";
+
+# Regular expression for words within a fullname which signify a non-person
+# Case is not significant.
+# The expression must match an entire word within the gecos fullname.
+$non_people_names = "account|admin|user|system|crew|test|dummy|\
+unit|department|student|project|.*\\d.*";
+
+### End of configurable stuff
+
+while(<>) {
+ chop;
+ ($user, $passwd, $uid, $gid, $gecos, $homedir, $shell) = split(/:/, $_, 7);
+ unless ($gr_name = getgrgid($gid)) {
+ $gr_name = $gid;
+ }
+
+ $nickname = $user;
+
+ $fullname = $gecos;
+ $fullname =~ s/,.*//g; # Reduce gecos to fullname
+ $fullname =~ s/[._]/ /g; # Translate alternate name separators to space
+ if ($uid < 1024 ||
+ $gid < 1024 ||
+ $gr_name =~ /^($non_people_groups)$/o) {
+ $fullname = "}$gr_name: $fullname";
+ } elsif ($user =~ /^($non_people_users)$/o ||
+ $fullname =~ /\b($non_people_names)\b/io) {
+ $fullname = "~$user: $fullname";
+ } elsif (($firstname, $lastname, $gecos_comment) =
+ ($fullname =~ /^(.*)[._ ]([^._ \d(][^._ \d]+)( *\(.*)?$/)) {
+ $fullname = "$lastname, $firstname";
+ }
+
+ $address = "$user\@$domain";
+
+# $fcc = "";
+
+ $comment = "$gr_name$gecos_comment";
+
+ print "$nickname\t$fullname\t$address\t$fcc\t$comment\n";
+}
diff --git a/contrib/utils/sendit.sh b/contrib/utils/sendit.sh
new file mode 100755
index 00000000..a8600f00
--- /dev/null
+++ b/contrib/utils/sendit.sh
@@ -0,0 +1,49 @@
+#! /bin/sh
+#
+# $Id:$
+#
+# T H E P I N E M A I L S Y S T E M
+#
+# Mike Seibel
+# Networks and Distributed Computing
+# Computing and Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, Washington, 98195, USA
+# Internet: mikes@CAC.Washington.EDU
+#
+# Please address all bugs and comments to "pine-bugs@cac.washington.edu"
+#
+#
+# Pine and Pico are registered trademarks of the University of Washington.
+# No commercial use of these trademarks may be made without prior written
+# permission of the University of Washington.
+#
+# Pine, Pico, and Pilot software and its included text are Copyright
+# 1989-1996 by the University of Washington.
+#
+# The full text of our legal notices is contained in the file called
+# CPYRIGHT, included with this distribution.
+#
+
+
+#
+# Simple script to expedite mail posting at the expense of timely
+# error reporting and 8BITMIME support.
+#
+# NOTE: If for any reason POSTFILE below is created on an nfs mounted
+# file system, the trap statement below must get removed or
+# altered, and the last line must get replaced with:
+#
+# ( ${POSTTOOL} ${POSTARGS} < ${POSTFILE} ; rm -f ${POSTFILE} ) &
+#
+
+POSTFILE=/tmp/send$$
+POSTTOOL=/usr/lib/sendmail
+POSTARGS="-oi -oem -t"
+
+umask 077
+trap "rm -f ${POSTFILE}; exit 0" 0 1 2 13 15
+
+cat > ${POSTFILE}
+${POSTTOOL} ${POSTARGS} < ${POSTFILE} &
diff --git a/contrib/utils/sendtoall b/contrib/utils/sendtoall
new file mode 100644
index 00000000..f929f527
--- /dev/null
+++ b/contrib/utils/sendtoall
@@ -0,0 +1,49 @@
+Date: 13 Sep 1996 22:49:40 GMT
+From: William Burrow <aa126@fan1.fan.nb.ca>
+To: pine-info@cac.washington.edu
+Subject: Re: Composing mail to all entries in addressbook
+
+Ari Y. Weintraub (aweintra@umabnet.ab.umd.edu) wrote:
+: Is there any way in Pine 3.94 to compose mail to all of the entries in my
+: addressbook. For example, I recently changed my phone number and would
+: like to send one message to everybody with the new number. I can't find a
+: way to do this - does anyone out there have any idea? TIA
+
+This would be a nice suggestion for Pine, however, you can do pretty much
+the same in Unix using the following awk script. Copy everything between the
+lines into a file named: everyone
+
+-------------%< clip --------------
+{
+ printf("everyone\tEveryone\t(");
+
+ if ( $0 ~ /\(/ )
+ continue
+ else
+ printf("%s", $1);
+
+ while ( getline > 0 ) {
+ if ( $0 ~ /\(/ )
+ continue
+ else
+ printf(",%s", $1);
+ }
+ printf(")\n");
+}
+-------------%< clip --------------
+
+Now, type the following exactly at the Unix prompt:
+
+awk -f everyone < ~/.addressbook >> ~/.addressbook && echo Done.
+
+You may wish to first copy your ~/.addressbook file to a backup file,
+before executing this command.
+
+If everything went well, it should say Done. When you start Pine, there
+should now be a list in your address book with the name: everyone,
+containing all of your address book nicknames (but NOT other lists).
+
+
+--
+--
+William Burrow -- Fredericton Area Network
diff --git a/contrib/utils/txtcc.sh b/contrib/utils/txtcc.sh
new file mode 100755
index 00000000..0d1bb8c9
--- /dev/null
+++ b/contrib/utils/txtcc.sh
@@ -0,0 +1,85 @@
+#!/bin/sh
+
+# On ultrix, this will compile string constants into the text segment
+# where they can be shared. Use to build pine by saying:
+#
+# build CC=txtcc gul
+#
+# As of Pine4.03, this moves about 675k per process into sharable memory
+#
+# Corey Satten, corey@cac.washington.edu, 9/11/98
+
+TMP1=/tmp/cc$$.s
+TMP2=/tmp/cc$$.delayed
+trap "rm -f $TMP1 $TMP2" 0 1 2 13 15
+
+
+for i in "$@"; do
+ case "$oflag//$i" in
+ //-c) cflag=1;;
+ //-o) oflag=1;;
+ //-g) gflag=1;;
+ 1//*) ofile="$i"; oflag=;;
+ //*.c) cfile="$i"; : ${ofile=`basename $i .c`.o};;
+ esac
+done
+
+case "$cflag" in
+ 1) sfile=`basename $ofile .o`.s
+ gcc -S "$@"
+
+ # Postprocess assembly language to move strings to .text segment.
+ #
+ # Use sed to collect .ascii statements and their preceding label
+ # and .align and emit those to $TMP2, the rest to $TMP1.
+ #
+ # Start in state0
+ # In state0: if .align is seen -> state1; else print, -> state0
+ # In state1: if a label is seen -> state2; else -> punt
+ # In state2: if .ascii is seen -> state3; else -> punt
+ # In state3: if .ascii is seen -> state4; else write TMP2, -> state0
+ # In state4: append to buffer, -> state3
+ # In state punt: print unprinted lines, -> state0
+ #
+ sed '
+ : state0
+ /^[ ][ ]*\.align/ b state1
+ b
+
+ : state1
+ h
+ N; s/^[^\n]*\n//; H
+ /^[!-~]*:/ b state2
+ b punt
+
+ : state2
+ N; s/^[^\n]*\n//; H
+ /^[ ][ ]*\.ascii/ b state3
+ b punt
+
+ : state3
+ N; s/^[^\n]*\n//
+ /^[ ][ ]*\.ascii/ b state4
+ x
+ w '"$TMP2"'
+ x
+ b state0
+
+ : state4
+ H
+ b state3
+
+ : punt
+ x
+ ' $sfile > $TMP1
+
+ # now add the deferred .ascii statements to .text segment in $TMP1
+ echo ' .text' >> $TMP1
+ cat $TMP2 >> $TMP1
+
+ rm `basename $ofile .o`.s
+ gcc ${gflag+"-g"} -c -o $ofile $TMP1
+ ;;
+ *) gcc "$@"
+ ;;
+esac
diff --git a/depcomp b/depcomp
new file mode 100755
index 00000000..df8eea7e
--- /dev/null
+++ b/depcomp
@@ -0,0 +1,630 @@
+#! /bin/sh
+# depcomp - compile a program generating dependencies as side-effects
+
+scriptversion=2009-04-28.21; # UTC
+
+# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2006, 2007, 2009 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, see <http://www.gnu.org/licenses/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
+
+case $1 in
+ '')
+ echo "$0: No command. Try \`$0 --help' for more information." 1>&2
+ exit 1;
+ ;;
+ -h | --h*)
+ cat <<\EOF
+Usage: depcomp [--help] [--version] PROGRAM [ARGS]
+
+Run PROGRAMS ARGS to compile a file, generating dependencies
+as side-effects.
+
+Environment variables:
+ depmode Dependency tracking mode.
+ source Source file read by `PROGRAMS ARGS'.
+ object Object file output by `PROGRAMS ARGS'.
+ DEPDIR directory where to store dependencies.
+ depfile Dependency file to output.
+ tmpdepfile Temporary file to use when outputing dependencies.
+ libtool Whether libtool is used (yes/no).
+
+Report bugs to <bug-automake@gnu.org>.
+EOF
+ exit $?
+ ;;
+ -v | --v*)
+ echo "depcomp $scriptversion"
+ exit $?
+ ;;
+esac
+
+if test -z "$depmode" || test -z "$source" || test -z "$object"; then
+ echo "depcomp: Variables source, object and depmode must be set" 1>&2
+ exit 1
+fi
+
+# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
+depfile=${depfile-`echo "$object" |
+ sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
+tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
+
+rm -f "$tmpdepfile"
+
+# Some modes work just like other modes, but use different flags. We
+# parameterize here, but still list the modes in the big case below,
+# to make depend.m4 easier to write. Note that we *cannot* use a case
+# here, because this file can only contain one case statement.
+if test "$depmode" = hp; then
+ # HP compiler uses -M and no extra arg.
+ gccflag=-M
+ depmode=gcc
+fi
+
+if test "$depmode" = dashXmstdout; then
+ # This is just like dashmstdout with a different argument.
+ dashmflag=-xM
+ depmode=dashmstdout
+fi
+
+cygpath_u="cygpath -u -f -"
+if test "$depmode" = msvcmsys; then
+ # This is just like msvisualcpp but w/o cygpath translation.
+ # Just convert the backslash-escaped backslashes to single forward
+ # slashes to satisfy depend.m4
+ cygpath_u="sed s,\\\\\\\\,/,g"
+ depmode=msvisualcpp
+fi
+
+case "$depmode" in
+gcc3)
+## gcc 3 implements dependency tracking that does exactly what
+## we want. Yay! Note: for some reason libtool 1.4 doesn't like
+## it if -MD -MP comes after the -MF stuff. Hmm.
+## Unfortunately, FreeBSD c89 acceptance of flags depends upon
+## the command line argument order; so add the flags where they
+## appear in depend2.am. Note that the slowdown incurred here
+## affects only configure: in makefiles, %FASTDEP% shortcuts this.
+ for arg
+ do
+ case $arg in
+ -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;;
+ *) set fnord "$@" "$arg" ;;
+ esac
+ shift # fnord
+ shift # $arg
+ done
+ "$@"
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ mv "$tmpdepfile" "$depfile"
+ ;;
+
+gcc)
+## There are various ways to get dependency output from gcc. Here's
+## why we pick this rather obscure method:
+## - Don't want to use -MD because we'd like the dependencies to end
+## up in a subdir. Having to rename by hand is ugly.
+## (We might end up doing this anyway to support other compilers.)
+## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
+## -MM, not -M (despite what the docs say).
+## - Using -M directly means running the compiler twice (even worse
+## than renaming).
+ if test -z "$gccflag"; then
+ gccflag=-MD,
+ fi
+ "$@" -Wp,"$gccflag$tmpdepfile"
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ rm -f "$depfile"
+ echo "$object : \\" > "$depfile"
+ alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
+## The second -e expression handles DOS-style file names with drive letters.
+ sed -e 's/^[^:]*: / /' \
+ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
+## This next piece of magic avoids the `deleted header file' problem.
+## The problem is that when a header file which appears in a .P file
+## is deleted, the dependency causes make to die (because there is
+## typically no way to rebuild the header). We avoid this by adding
+## dummy dependencies for each header file. Too bad gcc doesn't do
+## this for us directly.
+ tr ' ' '
+' < "$tmpdepfile" |
+## Some versions of gcc put a space before the `:'. On the theory
+## that the space means something, we add a space to the output as
+## well.
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly. Breaking it into two sed invocations is a workaround.
+ sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+hp)
+ # This case exists only to let depend.m4 do its work. It works by
+ # looking at the text of this script. This case will never be run,
+ # since it is checked for above.
+ exit 1
+ ;;
+
+sgi)
+ if test "$libtool" = yes; then
+ "$@" "-Wp,-MDupdate,$tmpdepfile"
+ else
+ "$@" -MDupdate "$tmpdepfile"
+ fi
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ rm -f "$depfile"
+
+ if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
+ echo "$object : \\" > "$depfile"
+
+ # Clip off the initial element (the dependent). Don't try to be
+ # clever and replace this with sed code, as IRIX sed won't handle
+ # lines with more than a fixed number of characters (4096 in
+ # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines;
+ # the IRIX cc adds comments like `#:fec' to the end of the
+ # dependency line.
+ tr ' ' '
+' < "$tmpdepfile" \
+ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \
+ tr '
+' ' ' >> "$depfile"
+ echo >> "$depfile"
+
+ # The second pass generates a dummy entry for each header file.
+ tr ' ' '
+' < "$tmpdepfile" \
+ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
+ >> "$depfile"
+ else
+ # The sourcefile does not contain any dependencies, so just
+ # store a dummy comment line, to avoid errors with the Makefile
+ # "include basename.Plo" scheme.
+ echo "#dummy" > "$depfile"
+ fi
+ rm -f "$tmpdepfile"
+ ;;
+
+aix)
+ # The C for AIX Compiler uses -M and outputs the dependencies
+ # in a .u file. In older versions, this file always lives in the
+ # current directory. Also, the AIX compiler puts `$object:' at the
+ # start of each line; $object doesn't have directory information.
+ # Version 6 uses the directory in both cases.
+ dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
+ test "x$dir" = "x$object" && dir=
+ base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
+ if test "$libtool" = yes; then
+ tmpdepfile1=$dir$base.u
+ tmpdepfile2=$base.u
+ tmpdepfile3=$dir.libs/$base.u
+ "$@" -Wc,-M
+ else
+ tmpdepfile1=$dir$base.u
+ tmpdepfile2=$dir$base.u
+ tmpdepfile3=$dir$base.u
+ "$@" -M
+ fi
+ stat=$?
+
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+ exit $stat
+ fi
+
+ for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+ do
+ test -f "$tmpdepfile" && break
+ done
+ if test -f "$tmpdepfile"; then
+ # Each line is of the form `foo.o: dependent.h'.
+ # Do two passes, one to just change these to
+ # `$object: dependent.h' and one to simply `dependent.h:'.
+ sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
+ # That's a tab and a space in the [].
+ sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
+ else
+ # The sourcefile does not contain any dependencies, so just
+ # store a dummy comment line, to avoid errors with the Makefile
+ # "include basename.Plo" scheme.
+ echo "#dummy" > "$depfile"
+ fi
+ rm -f "$tmpdepfile"
+ ;;
+
+icc)
+ # Intel's C compiler understands `-MD -MF file'. However on
+ # icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c
+ # ICC 7.0 will fill foo.d with something like
+ # foo.o: sub/foo.c
+ # foo.o: sub/foo.h
+ # which is wrong. We want:
+ # sub/foo.o: sub/foo.c
+ # sub/foo.o: sub/foo.h
+ # sub/foo.c:
+ # sub/foo.h:
+ # ICC 7.1 will output
+ # foo.o: sub/foo.c sub/foo.h
+ # and will wrap long lines using \ :
+ # foo.o: sub/foo.c ... \
+ # sub/foo.h ... \
+ # ...
+
+ "$@" -MD -MF "$tmpdepfile"
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ rm -f "$depfile"
+ # Each line is of the form `foo.o: dependent.h',
+ # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
+ # Do two passes, one to just change these to
+ # `$object: dependent.h' and one to simply `dependent.h:'.
+ sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
+ # Some versions of the HPUX 10.20 sed can't process this invocation
+ # correctly. Breaking it into two sed invocations is a workaround.
+ sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" |
+ sed -e 's/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+hp2)
+ # The "hp" stanza above does not work with aCC (C++) and HP's ia64
+ # compilers, which have integrated preprocessors. The correct option
+ # to use with these is +Maked; it writes dependencies to a file named
+ # 'foo.d', which lands next to the object file, wherever that
+ # happens to be.
+ # Much of this is similar to the tru64 case; see comments there.
+ dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
+ test "x$dir" = "x$object" && dir=
+ base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
+ if test "$libtool" = yes; then
+ tmpdepfile1=$dir$base.d
+ tmpdepfile2=$dir.libs/$base.d
+ "$@" -Wc,+Maked
+ else
+ tmpdepfile1=$dir$base.d
+ tmpdepfile2=$dir$base.d
+ "$@" +Maked
+ fi
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile1" "$tmpdepfile2"
+ exit $stat
+ fi
+
+ for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2"
+ do
+ test -f "$tmpdepfile" && break
+ done
+ if test -f "$tmpdepfile"; then
+ sed -e "s,^.*\.[a-z]*:,$object:," "$tmpdepfile" > "$depfile"
+ # Add `dependent.h:' lines.
+ sed -ne '2,${
+ s/^ *//
+ s/ \\*$//
+ s/$/:/
+ p
+ }' "$tmpdepfile" >> "$depfile"
+ else
+ echo "#dummy" > "$depfile"
+ fi
+ rm -f "$tmpdepfile" "$tmpdepfile2"
+ ;;
+
+tru64)
+ # The Tru64 compiler uses -MD to generate dependencies as a side
+ # effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'.
+ # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
+ # dependencies in `foo.d' instead, so we check for that too.
+ # Subdirectories are respected.
+ dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
+ test "x$dir" = "x$object" && dir=
+ base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
+
+ if test "$libtool" = yes; then
+ # With Tru64 cc, shared objects can also be used to make a
+ # static library. This mechanism is used in libtool 1.4 series to
+ # handle both shared and static libraries in a single compilation.
+ # With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d.
+ #
+ # With libtool 1.5 this exception was removed, and libtool now
+ # generates 2 separate objects for the 2 libraries. These two
+ # compilations output dependencies in $dir.libs/$base.o.d and
+ # in $dir$base.o.d. We have to check for both files, because
+ # one of the two compilations can be disabled. We should prefer
+ # $dir$base.o.d over $dir.libs/$base.o.d because the latter is
+ # automatically cleaned when .libs/ is deleted, while ignoring
+ # the former would cause a distcleancheck panic.
+ tmpdepfile1=$dir.libs/$base.lo.d # libtool 1.4
+ tmpdepfile2=$dir$base.o.d # libtool 1.5
+ tmpdepfile3=$dir.libs/$base.o.d # libtool 1.5
+ tmpdepfile4=$dir.libs/$base.d # Compaq CCC V6.2-504
+ "$@" -Wc,-MD
+ else
+ tmpdepfile1=$dir$base.o.d
+ tmpdepfile2=$dir$base.d
+ tmpdepfile3=$dir$base.d
+ tmpdepfile4=$dir$base.d
+ "$@" -MD
+ fi
+
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
+ exit $stat
+ fi
+
+ for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
+ do
+ test -f "$tmpdepfile" && break
+ done
+ if test -f "$tmpdepfile"; then
+ sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
+ # That's a tab and a space in the [].
+ sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
+ else
+ echo "#dummy" > "$depfile"
+ fi
+ rm -f "$tmpdepfile"
+ ;;
+
+#nosideeffect)
+ # This comment above is used by automake to tell side-effect
+ # dependency tracking mechanisms from slower ones.
+
+dashmstdout)
+ # Important note: in order to support this mode, a compiler *must*
+ # always write the preprocessed file to stdout, regardless of -o.
+ "$@" || exit $?
+
+ # Remove the call to Libtool.
+ if test "$libtool" = yes; then
+ while test "X$1" != 'X--mode=compile'; do
+ shift
+ done
+ shift
+ fi
+
+ # Remove `-o $object'.
+ IFS=" "
+ for arg
+ do
+ case $arg in
+ -o)
+ shift
+ ;;
+ $object)
+ shift
+ ;;
+ *)
+ set fnord "$@" "$arg"
+ shift # fnord
+ shift # $arg
+ ;;
+ esac
+ done
+
+ test -z "$dashmflag" && dashmflag=-M
+ # Require at least two characters before searching for `:'
+ # in the target name. This is to cope with DOS-style filenames:
+ # a dependency such as `c:/foo/bar' could be seen as target `c' otherwise.
+ "$@" $dashmflag |
+ sed 's:^[ ]*[^: ][^:][^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile"
+ rm -f "$depfile"
+ cat < "$tmpdepfile" > "$depfile"
+ tr ' ' '
+' < "$tmpdepfile" | \
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly. Breaking it into two sed invocations is a workaround.
+ sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+dashXmstdout)
+ # This case only exists to satisfy depend.m4. It is never actually
+ # run, as this mode is specially recognized in the preamble.
+ exit 1
+ ;;
+
+makedepend)
+ "$@" || exit $?
+ # Remove any Libtool call
+ if test "$libtool" = yes; then
+ while test "X$1" != 'X--mode=compile'; do
+ shift
+ done
+ shift
+ fi
+ # X makedepend
+ shift
+ cleared=no eat=no
+ for arg
+ do
+ case $cleared in
+ no)
+ set ""; shift
+ cleared=yes ;;
+ esac
+ if test $eat = yes; then
+ eat=no
+ continue
+ fi
+ case "$arg" in
+ -D*|-I*)
+ set fnord "$@" "$arg"; shift ;;
+ # Strip any option that makedepend may not understand. Remove
+ # the object too, otherwise makedepend will parse it as a source file.
+ -arch)
+ eat=yes ;;
+ -*|$object)
+ ;;
+ *)
+ set fnord "$@" "$arg"; shift ;;
+ esac
+ done
+ obj_suffix=`echo "$object" | sed 's/^.*\././'`
+ touch "$tmpdepfile"
+ ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
+ rm -f "$depfile"
+ cat < "$tmpdepfile" > "$depfile"
+ sed '1,2d' "$tmpdepfile" | tr ' ' '
+' | \
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly. Breaking it into two sed invocations is a workaround.
+ sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile" "$tmpdepfile".bak
+ ;;
+
+cpp)
+ # Important note: in order to support this mode, a compiler *must*
+ # always write the preprocessed file to stdout.
+ "$@" || exit $?
+
+ # Remove the call to Libtool.
+ if test "$libtool" = yes; then
+ while test "X$1" != 'X--mode=compile'; do
+ shift
+ done
+ shift
+ fi
+
+ # Remove `-o $object'.
+ IFS=" "
+ for arg
+ do
+ case $arg in
+ -o)
+ shift
+ ;;
+ $object)
+ shift
+ ;;
+ *)
+ set fnord "$@" "$arg"
+ shift # fnord
+ shift # $arg
+ ;;
+ esac
+ done
+
+ "$@" -E |
+ sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
+ -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' |
+ sed '$ s: \\$::' > "$tmpdepfile"
+ rm -f "$depfile"
+ echo "$object : \\" > "$depfile"
+ cat < "$tmpdepfile" >> "$depfile"
+ sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+msvisualcpp)
+ # Important note: in order to support this mode, a compiler *must*
+ # always write the preprocessed file to stdout.
+ "$@" || exit $?
+
+ # Remove the call to Libtool.
+ if test "$libtool" = yes; then
+ while test "X$1" != 'X--mode=compile'; do
+ shift
+ done
+ shift
+ fi
+
+ IFS=" "
+ for arg
+ do
+ case "$arg" in
+ -o)
+ shift
+ ;;
+ $object)
+ shift
+ ;;
+ "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
+ set fnord "$@"
+ shift
+ shift
+ ;;
+ *)
+ set fnord "$@" "$arg"
+ shift
+ shift
+ ;;
+ esac
+ done
+ "$@" -E 2>/dev/null |
+ sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
+ rm -f "$depfile"
+ echo "$object : \\" > "$depfile"
+ sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile"
+ echo " " >> "$depfile"
+ sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+msvcmsys)
+ # This case exists only to let depend.m4 do its work. It works by
+ # looking at the text of this script. This case will never be run,
+ # since it is checked for above.
+ exit 1
+ ;;
+
+none)
+ exec "$@"
+ ;;
+
+*)
+ echo "Unknown depmode $depmode" 1>&2
+ exit 1
+ ;;
+esac
+
+exit 0
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/doc/alpine.1 b/doc/alpine.1
new file mode 100644
index 00000000..6ae54416
--- /dev/null
+++ b/doc/alpine.1
@@ -0,0 +1,356 @@
+.TH alpine 1 "Version 2.10"
+.SH NAME
+alpine \- an Alternatively Licensed Program for Internet News and Email
+.SH SYNTAX
+
+.B alpine
+[
+.I options
+] [
+.I address
+,
+.I address
+]
+
+.B alpinef
+[
+.I options
+] [
+.I address
+,
+.I address
+]
+.SH DESCRIPTION
+
+Alpine is a screen-oriented message-handling tool. In its default
+configuration, Alpine offers an intentionally limited set of
+functions geared toward the novice user, but it also has a large
+list of optional "power-user" and personal-preference features.
+.I alpinef
+is a variant of Alpine that uses function keys rather than mnemonic
+single-letter commands.
+Alpine's basic feature set includes:
+.IP
+View, Save, Export, Delete, Print, Reply and Forward messages.
+.IP
+Compose messages in a simple editor (Pico) with word-wrap and a spelling
+checker. Messages may be postponed for later completion.
+.IP
+Full-screen selection and management of message folders.
+.IP
+Address book to keep a list of long or frequently-used addresses.
+Personal distribution lists may be defined.
+Addresses may be taken into the address book from
+incoming mail without retyping them.
+.IP
+New mail checking and notification occurs automatically every 2.5 minutes
+and after certain commands, e.g. refresh-screen (Ctrl-L).
+.IP
+On-line, context-sensitive help screens.
+.PP
+Alpine supports MIME (Multipurpose Internet Mail Extensions), an Internet
+Standard for representing multipart and multimedia data in email.
+Alpine allows you to save MIME objects to files, and in some
+cases, can also initiate the correct program for viewing the object.
+It uses the system's
+.I mailcap
+configuration file to determine what program can process a particular MIME
+object type.
+Alpine's message composer does not have integral multimedia capability, but
+any type of data file --including multimedia-- can be attached to a text
+message and sent using MIME's encoding rules. This allows any group of
+individuals with MIME-capable mail software (e.g. Alpine, PC-Alpine, or many
+other programs) to exchange formatted documents, spread-sheets, image
+files, etc, via Internet email.
+.PP
+Alpine uses the
+.I c-client
+messaging API to access local and remote mail folders. This
+library provides a variety of low-level message-handling functions,
+including drivers
+for a variety of different mail file formats, as well as routines
+to access remote mail and news servers, using IMAP (Internet Message
+Access Protocol) and NNTP (Network News Transport Protocol). Outgoing mail
+is usually posted directly via SMTP
+(Simple Mail Transfer Protocol).
+.SH OPTIONS
+.if n .ta 2.8i
+.if t .ta 2.1i
+
+The command line options/arguments are:
+.IP \fIaddress\fR 20
+Send mail to
+.I address.
+This will cause Alpine to go directly into the message composer.
+.IP \fB-attach\ \fIfile\fR 20
+Send mail with the listed
+.I file
+as an attachment.
+.IP \fB-attachlist\ \fIfile-list\fR 20
+Send mail with the listed
+.I file-list
+as an attachments.
+.IP \fB-attach_and_delete\ \fIfile\fR 20
+Send mail with the listed
+.I file
+as an attachment, and remove the file
+after the message is sent.
+.IP \fB-aux\ \fIlocal_directory\fR 20
+PC-Alpine only. When using a remote configuration (-p <remote_config>) this tells
+PC-Alpine the local directory to use for storing auxiliary files, like debug
+files, address books, and signature files.
+.IP \fB-bail\fR 20
+Exit if the pinerc file does not exist. This might be useful if the config
+file is accessed using some remote filesystem protocol. If the remote mount
+is missing this will cause Alpine to quit instead of creating a new pinerc.
+.IP \fB-c\ \fIcontext-number\fR 20
+context-number is the number corresponding to the
+folder-collection to which the
+.I -f
+command line argument should be applied. By default the
+.I -f
+argument is applied to the first defined folder-collection.
+.IP \fB-conf\fR 20
+Produce a sample/fresh copy of the
+system-wide configuration file,
+.I pine.conf,
+on the standard output. This is distinct from the per-user
+.I .pinerc
+file.
+.IP \fB-convert_sigs\ \fI-p\ pinerc\fR 20
+Convert signature files into literal signatures.
+.IP \fB-copy_abook\ <\fIlocal_abook\fR>\ <\fIremote_abook\fR> 20
+Copy the local address book file to a remote address book folder.
+.IP \fB-copy_pinerc\ <\fIlocal_pinerc\fR>\ <\fIremote_pinerc\fR> 20
+Copy the local pinerc file to a remote pinerc folder.
+.IP \fB-d\ \fIdebug-level\fR 20
+Output diagnostic info at
+.I debug-level
+(0-9) to the current
+.I .pine-debug[1-4]
+file. A value of 0 turns debugging off and suppresses the
+.I .pine-debug
+file.
+.IP \fB-d\ \fIkey[=val]\fR 20
+Fine tuned output of diagnostic messages where "flush" causes
+debug file writing without buffering, "timestamp" appends
+each message with a timestamp, "imap=n" where n is between
+0 and 4 representing none to verbose IMAP telemetry reporting,
+"numfiles=n" where n is between 0 and 31 corresponding to the
+number of debug files to maintain, and "verbose=n" where n is
+between 0 and 9 indicating an inverse threshold for message
+output.
+.IP \fB-f\ \fIfolder\fR 20
+Open
+.I folder
+(in first defined folder collection, use
+.I -c n
+to specify another collection) instead of INBOX.
+.IP \fB-F\ \fIfile\fR 20
+Open named text file and view with Alpine's browser.
+.IP \fB-h\fR 20
+Help: list valid command-line options.
+.IP \fB-i\fR 20
+Start up in the FOLDER INDEX screen.
+.IP \fB-I\ \fIkeystrokes\fR 20
+Initial (comma separated list of) keystrokes which Alpine should execute
+on startup.
+.IP \fB-install\fR 20
+For PC-Alpine only, this option causes PC-Alpine to prompt for some basic
+setup information, then exits.
+.IP \fB-k\fR 20
+Use function keys for commands. This is the same as running the command
+.IR alpinef .
+.IP \fB-n\ \fInumber\fR 20
+Start up with current message-number set to
+.I number.
+.IP \fB-o\fR 20
+Open first folder read-only.
+.IP \fB-p\ \fIconfig-file\fR 20
+Use
+.I config-file
+as the personal configuration file instead of the default
+.IR .pinerc .
+.IP \fB-P\ \fIconfig-file\fR 20
+Use
+.I config-file
+as the configuration file instead of default
+system-wide configuration file
+.IR pine.conf .
+.IP \fB-pinerc\ \fIfile\fR 20
+Output fresh pinerc configuration to
+.I file, preserving the settings of variables that the user has made.
+Use \fIfile\fR set to ``-'' to make output go to standard out.
+<IP> \fB-registry\ \fIcmd\fR 20
+For PC-Alpine only, this option affects the values of
+Alpine's registry entries.
+Possible values for \fIcmd\fR are set, clear, and dump.
+\fISet\fR will always reset Alpine's registry
+entries according to its current settings.
+\fIClear\fR will clear the registry values.
+\fIClearsilent\fR will silently clear the registry values.
+\fIDump\fR will display the values of current registry settings.
+Note that the dump command is currently disabled.
+Without the -registry option, PC-Alpine will write values into
+the registry only if there currently aren't any values set.
+.IP \fB-r\fR 20
+Use restricted/demo mode.
+.I Alpine
+will only send mail to itself
+and functions like save and export are restricted.
+.IP \fB-sort\ \fIorder\fR
+Sort the FOLDER INDEX display in one of the following orders:
+.I arrival, date, subject, orderedsubj, thread, from, size, score, to, cc,
+or
+.I reverse. Arrival
+order is the default.
+The OrderedSubj choice simulates a threaded sort.
+Any sort may be reversed by adding
+.I /reverse
+to it.
+.I Reverse
+by itself is the same as
+.IR arrival/reverse .
+.IP \fB-supported\fR 20
+Some options may or may not be supported depending on how Alpine
+was compiled.
+This is a way to determine which options are supported in the particular
+copy of Alpine you are using.
+.IP \fB-uninstall\fR 20
+For PC-Alpine only, this option causes PC-Alpine to remove references to
+Alpine in Windows settings.
+.IP \fB-url\ \fIurl\fR 20
+Open the given
+.I url.
+Cannot be used with
+.I -f
+or
+.I -F
+options.
+.IP \fB-v\fR 20
+Version: Print version information.
+.IP \fB-version\fR 20
+Version: Print version information.
+.IP \fB-x\ \fIconfig\fR 20
+Use configuration exceptions in
+.I config.
+Exceptions are used to override your default pinerc
+settings for a particular platform, can be a local file or
+a remote folder.
+.IP \fB-z\fR 20
+Enable ^Z and SIGTSTP so alpine may be suspended.
+.IP \fI-option\=\fIvalue\fR 20
+Assign
+.I value
+to the config option
+.I option
+e.g. -signature-file=sig1 or -feature-list=signature-at-bottom
+(Note: feature-list values are additive)
+.SH CONFIGURATION
+
+There are several levels of Alpine configuration. Configuration values at
+a given level over-ride corresponding values at lower levels. In order of
+increasing precedence:
+
+ o built-in defaults.
+.br
+ o system-wide
+.I pine.conf
+file.
+.br
+ o personal
+.I .pinerc
+file (may be set via built-in Setup/Config menu.)
+.br
+ o command-line options.
+.br
+ o system-wide
+.I pine.conf.fixed
+file.
+
+There is one exception to the rule that configuration values are replaced
+by the value of the same option in a higher-precedence file: the
+feature-list variable has values that are additive, but can be negated by
+prepending "no-" in front of an individual feature name. Unix Alpine also
+uses the following environment variables:
+
+ TERM
+.br
+ DISPLAY (determines if Alpine can display IMAGE attachments.)
+.br
+ SHELL (if not set, default is /bin/sh )
+.br
+ MAILCAPS (semicolon delimited list of path names to mailcap files)
+.SH FILES
+.if n .ta 2.8i
+.if t .ta 2.1i
+
+/usr/spool/mail/xxxx Default folder for incoming mail.
+.br
+~/mail Default directory for mail folders.
+.br
+~/.addressbook Default address book file.
+.br
+~/.pine-debug[1-4] Diagnostic log for debugging.
+.br
+~/.pinerc Personal alpine config file.
+.br
+~/.newsrc News subscription/state file.
+.br
+~/.mailcap Personal mail capabilities file.
+.br
+~/.mime.types Personal file extension to MIME type mapping
+.br
+/etc/mailcap System-wide mail capabilities file.
+.br
+/etc/mime.types System-wide file ext. to MIME type mapping
+.br
+/usr/local/lib/pine.info Local pointer to system administrator.
+.br
+/usr/local/lib/pine.conf System-wide configuration file.
+.br
+/usr/local/lib/pine.conf.fixed Non-overridable configuration file.
+.br
+/tmp/.\\usr\\spool\\mail\\xxxx Per-folder mailbox lock files.
+.br
+~/.pine-interrupted-mail Message which was interrupted.
+.br
+~/mail/postponed-msgs For postponed messages.
+.br
+~/mail/sent-mail Outgoing message archive (FCC).
+.br
+~/mail/saved-messages Default destination for Saving messages.
+.SH "SEE ALSO"
+
+pico(1), binmail(1), aliases(5), mailaddr(7), sendmail(8), spell(1), imapd(8)
+
+.br
+Newsgroup: comp.mail.pine
+.br
+Alpine Information Center: http://www.washington.edu/alpine
+.br
+Source distribution: ftp://ftp.cac.washington.edu/alpine/alpine.tar.gz
+.br
+Alpine Technical Notes, included in the source distribution.
+.br
+C-Client messaging API library, included in the source distribution.
+.SH ACKNOWLEDGMENTS
+.na
+.nf
+
+The University of Washington Alpine development team (part of the UW Office
+of Computing & Communications) includes:
+
+ Project Leader: Mike Seibel
+ Principal authors: Mike Seibel, Steve Hubert, Jeff Franklin
+ C-Client library & IMAPd: Mark Crispin
+ Documentation: Many people!
+ Project oversight: Terry Gray, Lori Stevens
+ Principal Patrons: Ron Johnson, Mike Bryant
+ Initial Alpine code base: Pine - by the University of Washington,
+ Elm - by Dave Taylor & USENET Community Trust
+ Initial Pico code base: MicroEmacs 3.6, by Dave G. Conroy
+ User Interface design: Inspired by UCLA's "Ben" mailer for MVS
+ Suggestions/fixes/ports: Folks from all over!
+
+$Date: 2009-02-02 13:54:23 -0600 (Mon, 02 Feb 2009) $
diff --git a/doc/brochure.txt b/doc/brochure.txt
new file mode 100644
index 00000000..eba004a8
--- /dev/null
+++ b/doc/brochure.txt
@@ -0,0 +1,74 @@
+
+ The Alpine Message System
+
+BACKGROUND
+
+Alpine, is a tool for reading, sending, and managing electronic
+messages built upon the venerable Pine Message System. It was
+designed specifically with novice computer users in mind, but can be
+tailored to accommodate the needs of "power users" as well. Alpine
+uses Internet message protocols (e.g. RFC-822, SMTP, MIME, IMAP, NNTP)
+and runs on Unix and PCs.
+
+The guiding principles for Alpine's user-interface were: careful
+limitation of features, one-character mnemonic commands,
+always-present command menus, immediate user feedback, and high
+tolerance for user mistakes. It is intended that Alpine can be learned
+by exploration rather than by reading manuals. At the same time, Alpine
+is highly customizable and provides numerous advanced features
+intended to help the most experienced users manage large mail
+collections.
+
+Alpine's message composition editor (Pico) and its file browser (Pilot) are
+also available as separate stand-alone programs. Pico is a very simple
+and easy-to-use text editor offering paragraph justification, cut/paste,
+and a spelling checker.
+
+FEATURES
+
+ - Online help specific to each screen and context.
+
+ - Message index showing a message summary which includes the status,
+ sender, size, date and subject of messages.
+
+ - Commands to view and process messages: Forward, Reply, Save,
+ Export, Print, Delete, capture address, and search.
+
+ - Message composer with easy-to-use editor and spelling checker.
+ The message composer also assists entering and formatting
+ addresses and provides direct access to the address book.
+
+ - Address book for saving long complex addresses and personal
+ distribution lists under a nickname.
+
+ - Message attachments via the Multipurpose Internet Mail Extensions
+ (MIME) specification. MIME allows sending/receiving non-text
+ objects, such as binary files, spreadsheets, graphics, and sound.
+
+ - Folder management commands for creating, deleting, listing, or
+ renaming message folders. Folders may be local or on remote hosts.
+
+ - Access to remote message folders and archives via the Internet
+ Message Access Protocol version 4.1 (IMAP) as defined in RFC-2060.
+
+ - Internet news support via either NNTP or IMAP.
+
+ - Aggregate operations, e.g. saving a selected set of messages at once.
+
+AVAILABILITY
+
+Alpine, Web Alpine, PC-Alpine, Pico, and UW's IMAP server are made
+freely available under the Apache License, Version 2.0. The latest
+version, including source code, can be found on the Internet host
+"ftp.cac.washington.edu" in the file "alpine/alpine.tar.Z" (accessible
+via anonymous FTP).
+
+For further information, visit the Alpine Information Center at
+ http://www.washington.edu/alpine/
+
+Alpine is brought to you by the Office of Computing & Communications at the
+University of Washington.
+
+2007.12.13
+
+
diff --git a/doc/mailcap.unx b/doc/mailcap.unx
new file mode 100644
index 00000000..a0a0342a
--- /dev/null
+++ b/doc/mailcap.unx
@@ -0,0 +1,131 @@
+# This is a sample mailcap file based on the sample mailcap file
+# contained in the metamail distribution (version 2.7) from Bellcore.
+# This sample is for a Unix system. Look at the original sample from
+# the metamail distribution for more ideas. This is a simplified version
+# to explain how it works with Pine. As of October, 1994, metamail was
+# available via anonymous ftp from the host thumper.bellcore.com in the
+# file /pub/nsb/mm2.7.tar.Z.
+#
+# Metamail is:
+# Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore)
+#
+# Permission to use, copy, modify, and distribute this material
+# for any purpose and without fee is hereby granted, provided
+# that the above copyright notice and this permission notice
+# appear in all copies, and that the name of Bellcore not be
+# used in advertising or publicity pertaining to this
+# material without the specific, prior written permission
+# of an authorized representative of Bellcore. BELLCORE
+# MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY
+# OF THIS MATERIAL FOR ANY PURPOSE. IT IS PROVIDED "AS IS",
+# WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
+#
+# The mailcap viewers are used by Pine when viewing pieces of a message
+# from within the attachment viewer. That is, you type the "V" command
+# when already viewing a message.
+#
+# Pine expects the mailcap file to be in /etc/mailcap on Unix systems.
+# Users may override or extend this with a .mailcap file in their home
+# directory. The contents of that will be combined with /etc/mailcap.
+# Users may override this standard Pine mailcap path
+# ("~/.mailcap:/etc/mailcap") by defining the environment variable
+# MAILCAPS to be equal to the colon separated path.
+#
+# On PC's (DOS or Windows) the file MAILCAP is searched for first in the
+# same directory where the user's PINERC is located and then in the same
+# directory where PINE.EXE is located. The first would be the user's personal
+# override file and the second the common file used by all users. Users
+# may override this location by defining the environment variable MAILCAPS
+# to be equal to the *semicolon* separated path.
+#
+# Pine does not use the "compose=" portion of mailcap entries (and doesn't
+# provide a general method of composing different types of messages).
+# Pine doesn't pay attention to "copiousoutput", but always pipes the output
+# to its standard scrolling text window if "needsterminal" is not set.
+# If "needsterminal" is set, then Pine sets the terminal or terminal window
+# back to the state it was in when Pine was started and lets the viewer run.
+# When the viewer finishes, Pine resets the terminal and redraws the screen.
+# If any user interaction with the viewer is required and the viewer runs
+# in the same terminal window as Pine, then "needsterminal" should be set.
+# The "test=" commands are used as defined in RFC1524, except that the
+# data file is not available to the test command.
+#
+# Since mailcap is only used from the attachment viewer, the message being
+# viewed will always be a single part, so "multipart" entries in mailcap have
+# no effect on Pine. Type "text/plain" with "charset=usascii" or charset
+# matching the character-set variable are intercepted and displayed by Pine
+# in the normal way, not displayed by a mailcap viewer. Besides those
+# exceptions just listed, all other types and subtypes are subject to
+# being displayed by a mailcap viewer. If no match is found for types text
+# or message, Pine will display them in its usual way.
+#
+# As a special case, the "image-viewer" variable from the pinerc file is
+# supported as if an extra entry for type image/* came first in the
+# personal mailcap file. That's for backwards compatibility.
+#
+#
+# The following line causes the xv program to be used to display all
+# image types if the DISPLAY variable is set (indicating the user is
+# using X). (xv is written by John Bradley, bradley@cis.upenn.edu. There
+# are also other X image viewer programs you could use, such as xloadimage.)
+image/*; xv %s; test=test -n "$DISPLAY"
+
+# The effect of the following is to send ALL audio subtypes to the
+# showaudio program. If possible, it would be desirable to also include
+# a test command that could decide whether or not the user could play audio.
+# That would be something like "test=can_do_audio %t". (Showaudio is a shell
+# script included in the metamail distribution.)
+audio/*; showaudio %s
+
+# (Showexternal is a shell script included in the metamail distribution.)
+message/external-body; showexternal %s %{access-type} %{name} \
+ %{site} %{directory} %{mode} %{server}; \
+ needsterminal; composetyped = extcompose %s; \
+ description="A reference to data stored in an external location"
+
+# If you have an interactive Postscript interpreter, you should think carefully
+# before replacing lpr with it in the following line, because PostScript
+# can be an enormous security hole. It is RELATIVELY harmless
+# when sent to the printer...
+application/postscript ; lpr %s \; echo SENT FILE TO PRINTER ;\
+ description="A Postscript File";
+# unsafe alternative
+#application/postscript; gspreview %s ; test=test -n "$DISPLAY"
+
+# The following gives rudimentary capability for receiving
+# text mail in the ISO-8859-1 character set, which covers many European
+# languages, and the ISO-8859-8 character set, which includes Hebrew
+# Note that the pipe to tr ensures that the "ISO" is case-insensitive.
+# (This is also from metamail.)
+#
+#### However, they are commented out here as they use a "test" method
+#### that can cause malicious data in the message's charset parameter
+#### to get executed. A better alternative would be to replace the "test"
+#### command with a script that does a safer case-insensitive comparison.
+#text/plain; shownonascii iso-8859-8 %s; test=test "`echo %{charset} | tr '[A-Z]' '[a-z]'`" = iso-8859-8 -a -n "$DISPLAY" ; copiousoutput
+#text/plain; shownonascii iso-8859-8 %s | more ; test=test "`echo %{charset} | tr '[A-Z]' '[a-z]'`" = iso-8859-8; needsterminal
+#text/plain; shownonascii iso-8859-1 %s; test=test "`echo %{charset} | tr '[A-Z]' '[a-z]'`" = iso-8859-1 -a -n "$DISPLAY" ; copiousoutput
+#text/plain; shownonascii iso-8859-1 %s | more ; test=test "`echo %{charset} | tr '[A-Z]' '[a-z]'`" = iso-8859-1 ; needsterminal
+
+# The following displays Japanese text at sites where
+# the "kterm" program is installed:
+#text/plain; kterm -geometry +0+0 -e more %s /dev/null; \
+ test=test "`echo %{charset} | tr '[A-Z]' '[a-z]'`" = iso-2022-jp
+
+# This maps MPEG video data to the viewer 'mpeg_play'.
+# (Mpeg_play is part of the MPEG distribution from The Berkeley Plateau
+# Research Group and is available via anonymous ftp from toe.cs.berkeley.edu.)
+video/mpeg; mpeg_play %s ; test=test -n "$DISPLAY"
+
+# This maps all other types of video to the xanim viewer. (Xanim is written
+# by Mark Podlipec, podlipec@wellfleet.com.)
+video/*; xanim %s ; test=test -n "$DISPLAY"
+
+# The xdvi program display TeX dvi files on an X server.
+application/x-dvi; xdvi %s ; test=test -n "$DISPLAY"
+
+# Type octet-stream (binary) data can be displayed as a hex dump before
+# you decide whether or not you want to save it to a file. (Hd is just
+# a standard hex dump program. You could use "od" if you don't have an
+# "hd". Naive users may find the output from this entry confusing.)
+application/octet-stream; hd; copiousoutput; description="Hex dump of data"
diff --git a/doc/mime.types b/doc/mime.types
new file mode 100644
index 00000000..7675b638
--- /dev/null
+++ b/doc/mime.types
@@ -0,0 +1,28 @@
+# Sample Pine mime.types file.
+#
+# This file illustrates the format of data Pine
+# uses to map MIME type and subtype information
+# to the file name extension of attachments it sends.
+# Pine first looks for "~/.mime.types", then adds
+# any unbound MIME types found in "/etc/mime.types"
+# and "/usr/local/lib/mime.types".
+
+application/postscript ai eps ps
+application/rtf rtf
+application/x-tex tex
+application/x-texinfo texinfo texi
+application/x-troff t tr roff
+audio/basic au snd
+audio/x-aiff aif aiff aifc
+audio/x-wav wav
+image/gif gif
+image/ief ief
+image/jpeg jpeg jpg jpe
+image/tiff tiff tif
+image/x-xwindowdump xwd
+text/html html
+text/plain txt c cc h
+video/mpeg mpeg mpg mpe
+video/quicktime qt mov
+video/x-msvideo avi
+video/x-sgi-movie movie
diff --git a/doc/pico.1 b/doc/pico.1
new file mode 100644
index 00000000..705ceff9
--- /dev/null
+++ b/doc/pico.1
@@ -0,0 +1,175 @@
+.TH pico 1 "Version 5.05"
+.SH Name
+pico \- simple text editor in the style of the Alpine Composer
+.SH Syntax
+.B pico
+[
+.I options
+] [
+.I file
+]
+.SH Description
+\fIPico\fR is a simple, display-oriented text editor based on
+the Alpine message system composer. As with Alpine, commands are
+displayed at the bottom of the screen, and context-sensitive
+help is provided. As characters are typed they are immediately
+inserted into the text.
+.PP
+Editing commands are entered using control-key
+combinations. As a work-around for communications programs that
+swallow certain control characters, you can emulate a control key
+by pressing ESCAPE twice, followed by the desired control character,
+e.g. "ESC ESC c" would be equivalent to entering a ctrl-c.
+The editor has five basic features: paragraph justification,
+searching, block cut/paste, a spelling checker, and a file browser.
+.PP
+Paragraph justification (or filling) takes place in the paragraph that
+contains the cursor, or, if the cursor is between lines, in the paragraph
+immediately below. Paragraphs are delimited by blank lines, or by lines
+beginning with a space or tab. Unjustification can be done immediately
+after justification using the control-U key combination.
+.PP
+String searches are not sensitive to case. A search begins at the current
+cursor position and wraps around the end of the text. The most recent
+search string is offered as the default in subsequent searches.
+.PP
+Blocks of text can be moved, copied or deleted with creative use of the
+command for mark (ctrl-^), delete (ctrl-k) and undelete (ctrl-u).
+The delete command will remove text between the "mark" and the current
+cursor position, and place it in the "cut" buffer. The undelete command
+effects a "paste" at the current cursor position.
+.PP
+The spell checker examines all words in the text. It then offers, in
+turn, each misspelled word for correction while
+highlighting it in the text. Spell checking can be cancelled at any time.
+Alternatively, \fIpico\fR will substitute for the default spell checking
+routine a routine defined by the SPELL environment variable. The replacement
+routine should read standard input and write standard output.
+.PP
+The file browser is offered as an option in the "Read File" and "Write Out"
+command prompts. It is intended to help in searching for specific files
+and navigating directory hierarchies. Filenames with sizes and names of
+directories in the current working directory are presented for selection.
+The current working directory is displayed on the top line of the display
+while the list of available commands takes up the bottom two. Several
+basic file manipulation functions are supported: file renaming, copying,
+and deletion.
+.PP
+More specific help is available in \fIpico\fR's online help.
+.SH Options
+.IP \fB+\fIn\fB\fR
+Causes \fIpico\fR to be started with the cursor located \fIn\fR lines
+into the file. (Note: no space between "+" sign and number)
+.IP \fB-a\fR
+Display all files including those beginning with a period (.).
+.IP \fB-b\fR
+Enable the option to Replace text matches found using the
+"Where is" command. This now does nothing. Instead, the option is
+always turned on (as if the -b flag had been specified).
+.IP \fB-d\fR
+Rebind the "delete" key so the character the cursor is on is rubbed out
+rather than the character to its left.
+.IP \fB-e\fR
+Enable file name completion.
+.IP \fB-f\fR
+Use function keys for commands. This option supported only in
+conjunction with UW Enhanced NCSA telnet.
+.IP \fB-h\fR
+List valid command line options.
+.IP \fB-j\fR
+Enable "Goto" command in the file browser. This enables the command to
+permit explicitly telling \fIpilot\fR which directory to visit.
+.IP \fB-g\fR
+Enable "Show Cursor" mode in file browser. Cause cursor to be positioned
+before the current selection rather than placed at the lower left of the
+display.
+.IP \fB-k\fR
+Causes "Cut Text" command to remove characters from the cursor position
+to the end of the line rather than remove the entire line.
+.IP \fB-m\fR
+Enable mouse functionality. This only works when \fIpico\fR is run from
+within an X Window System "xterm" window.
+.IP \fB-n\fIn\fB\fR
+The \-n\fIn\fR option enables new mail notification. The \fIn\fR
+argument is optional, and specifies how often, in seconds, your
+mailbox is checked for new mail. For example, \-n60 causes \fIpico\fR
+to check for new mail once every minute. The default interval is 180
+seconds, while the minimum allowed is 30. (Note: no space between "n" and
+the number)
+.IP \fB-o\ \fIdir\fB\fR
+Sets operating directory. Only files within this directory are accessible.
+Likewise, the file browser is limited to the specified directory subtree.
+.IP \fB-r\fIn\fB\fR
+Sets column used to limit the "Justify" command's right margin
+.IP \fB-s\ \fIspeller\fR
+Specify an alternate program
+.I spell
+to use when spell checking.
+.IP \fB-t\fR
+Enable "tool" mode. Intended for when \fIpico\fR is used as the
+editor within other tools (e.g., Elm, Pnews). \fIPico\fR will not prompt
+for save on exit, and will not rename the buffer during the "Write Out"
+command.
+.IP \fB-v\fR
+View the file only, disallowing any editing.
+.IP \fB-version\fR
+Print Pico version and exit.
+.IP \fB-w\fR
+Disable word wrap (thus allow editing of long lines).
+.IP \fB-x\fR
+Disable keymenu at the bottom of the screen.
+.IP \fB-z\fR
+Enable ^Z suspension of \fIpico\fR.
+.IP \fB-p\fR
+Preserve the "start" and "stop" characters, typically Ctrl-Q and Ctrl-S,
+which are sometimes used in communications paths to control data flow
+between devices that operate at different speeds.
+.IP \fB-Q\ \fIquotestr\fB\fR
+Set the quote string. Especially useful when composing email, setting this
+allows the quote string to be checked for when Justifying paragraphs.
+A common quote string is "> ".
+.IP \fB-W\ \fIword_separators\fB\fR
+If characters listed here appear in the middle of a word surrounded by
+alphanumeric characters that word is split into two words. This is used by
+the Forward and Backward word commands and by the spell checker.
+.IP \fB-q\fR
+Termcap or terminfo definition for input escape sequences are used in
+preference to sequences defined by default. This option is only available
+if \fIpico\fR was compiled with the TERMCAP_WINS define turned on.
+.IP \fB-setlocale_ctype\fR
+Do setlocale(LC_CTYPE) if available. Default is to not do this setlocale.
+.IP \fB-no_setlocale_collate\fR
+Do not do setlocale(LC_COLLATE). Default is to do this setlocale.
+.PP
+Lastly, when a running \fIpico\fR is disconnected (i.e., receives a
+SIGHUP), \fIpico\fR will save the current work if needed before exiting.
+Work is saved under the current filename with ".save" appended.
+If the current work is unnamed, it is saved under the filename "pico.save".
+.PP
+.SH Bugs
+The manner in which lines longer than the display width are dealt
+is not immediately obvious. Lines that continue beyond the edge
+of the display are indicated by a '$' character at the end
+of the line. Long lines are scrolled horizontally as the cursor
+moves through them.
+.SH Files
+.ta 1.75i
+.nf
+pico.save Unnamed interrupted work saved here.
+*.save Interrupted work on a named file is saved here.
+.fi
+.SH Authors
+Michael Seibel <mikes@cac.washington.edu>
+.br
+Laurence Lundblade <lgl@cac.washington.edu>
+.br
+Pico was originally derived from MicroEmacs 3.6, by Dave G. Conroy.
+.br
+Copyright 1989-2008 by the University of Washington.
+.SH "See Also"
+alpine(1)
+.br
+Source distribution (part of the Alpine Message System):
+
+.nf
+$Date: 2009-02-02 13:54:23 -0600 (Mon, 02 Feb 2009) $
diff --git a/doc/pilot.1 b/doc/pilot.1
new file mode 100644
index 00000000..4ddcd16d
--- /dev/null
+++ b/doc/pilot.1
@@ -0,0 +1,84 @@
+.TH pilot 1 "Version 1.1"
+.SH Name
+pilot \- simple file system browser in the style of the Alpine Composer
+.SH Syntax
+.B pilot
+[
+.I options
+] [
+.I directory
+]
+.SH Description
+\fIPilot\fR is a simple, display-oriented file system browser based on
+the Alpine message system composer. As with Alpine, commands are
+displayed at the bottom of the screen, and context-sensitive
+help is provided.
+.PP
+\fIPilot\fR displays the current working directory at the top of the
+screen. The directory's contents are displayed in columns of file name,
+file size pairs. Names that are directories are indicated by the name
+"(dir)" in place of the file size. The parent of the current working
+directory is indicated by the file name ".." and size of "(parent dir)".
+File names that are symbolic links to other files are displayed with a
+file size of "--".
+.PP
+Several basic file manipulation commands are provided: Delete, Rename,
+Copy, View, Launch, and Edit. The "View" and "Edit" commands operate on
+text files only. By default, the "View" command displays files
+using "alpine -F", but will respect the environment variable PAGER if set.
+The "Edit" command simply invokes "pico". The "Launch" command provides
+a convenient way to either execute the selected file or to run an
+application on it.
+.PP
+More specific help is available in \fIpilot\fR's online help.
+.SH Options
+.IP \fB-a\fR
+Display all files including those beginning with a period (.).
+.IP \fB-f\fR
+Use function keys for commands. This option supported only in
+conjunction with UW Enhanced NCSA telnet.
+.IP \fB-g\fR
+Enable "Show Cursor" mode. Cause cursor to be positioned
+before the current selection rather than placed at the lower left of the
+display.
+.IP \fB-j\fR
+Enable "Goto" command. This enables the command to permit explicitly
+telling \fIpilot\fR which directory to visit.
+.IP \fB-m\fR
+Enable mouse functionality. This only works when \fIpilot\fR is run from
+within an X Window System "xterm" window.
+.IP \fB-n\fIn\fB\fR
+The \-n\fIn\fR option enables new mail notification. The \fIn\fR
+argument is optional, and specifies how often, in seconds, your
+mailbox is checked for new mail. For example, \-n60 causes \fIpilot\fR
+to check for new mail once every minute. The default interval is 180
+seconds, while the minimum allowed is 30. (Note: no space between "n" and
+the number)
+.IP \fB-o\ \fIdir\fB\fR
+Sets operating directory. Only files within the specified directory are
+accessible and browsing is limited to the specified directory subtree.
+.IP \fB-v\fR
+Enable single vertical column display.
+.IP \fB-x\fR
+Disable keymenu at the bottom of the screen.
+.IP \fB-z\fR
+Enable ^Z suspension of \fIpilot\fR.
+.IP \fB-q\fR
+Termcap or terminfo definition for input escape sequences are used in
+preference to sequences defined by default. This option is only available
+if \fIpilot\fR was compiled with the TERMCAP_WINS define turned on.
+.IP \fB-setlocale_ctype\fR
+Do setlocale(LC_CTYPE) if available. Default is to not do this setlocale.
+.IP \fB-no_setlocale_collate\fR
+Do not do setlocale(LC_COLLATE). Default is to do this setlocale.
+.fi
+.SH Authors
+Michael Seibel <mikes@cac.washington.edu>
+.br
+Copyright 1994-2007 by the University of Washington.
+.SH "See Also"
+alpine(1)
+.br
+Source distribution (part of the Alpine Message System):
+
+$Date: 2005/01/14 20:40:14 $
diff --git a/doc/rpdump.1 b/doc/rpdump.1
new file mode 100644
index 00000000..0d24f3ff
--- /dev/null
+++ b/doc/rpdump.1
@@ -0,0 +1,38 @@
+.TH rpdump 1
+.SH NAME
+rpdump \- alpine remote data utility
+.SH SYNTAX
+
+.B rpdump
+[ -f ] -l Local_file -r Remote_folder
+.SH DESCRIPTION
+
+Rpdump may be used to copy the actual data from
+remote Alpine configuration files or address
+books into a local file.
+It is intended to be used by system administrators.
+Regular users should normally use the facilities provided within Alpine.
+.LP
+Local_file will normally be a local temporary file.
+Remote_folder is the IMAP folder being used as a remote Alpine configuration
+(with the help of Alpine's -P, -p, and -x commands or PINECONF, PINERC,
+and PINERCEX environment variables) or remote Alpine address book folder.
+A copy of the data from Remote_folder will be copied to Local_file.
+.IP \fB-f\fR 20
+Force the dump even if the remote folder is in an unrecognized format.
+.IP \fB-l\fR\ \fBLocal_file\fR 20
+The file on this system that is to be copied to.
+.IP \fB-r\fR\ \fBRemote_folder\fR 20
+A remote folder name to be copied from.
+See the Alpine documentation for the syntax of a remote folder name.
+One example is
+.br
+{my.imap.server}remote_pinerc.
+.SH DIAGNOSTICS
+Exit status is zero if all goes well, -1 otherwise.
+.SH "SEE ALSO"
+Rpload(1).
+.LP
+Copyright 1989-2007 by the University of Washington.
+
+$Date: 2005/01/14 20:40:14 $
diff --git a/doc/rpload.1 b/doc/rpload.1
new file mode 100644
index 00000000..f645a248
--- /dev/null
+++ b/doc/rpload.1
@@ -0,0 +1,49 @@
+.TH rpload 1
+.SH NAME
+rpload \- alpine remote data utility
+.SH SYNTAX
+
+.B rpload
+[ -f ] [ -s trimSize ] -t Type -l Local_file -r Remote_folder
+.SH DESCRIPTION
+
+Rpload may be used to convert local Alpine configuration files or address
+books into remote configurations or address books.
+It is intended to be used by system administrators.
+Regular users should normally use the facilities provided within Alpine.
+.LP
+Local_file will usually be a user's alpine configuration file, and
+Remote_folder is the IMAP folder which will be used
+(with the help of Alpine's \fB-p\fR, \fB-P\fR, and \fB-x\fR commands or
+PINECONF, PINERC, and PINERCEX environment variables)
+as the user's remote configuration folder.
+A copy of Local_file will be placed in the folder with the correct header
+lines to satisfy Alpine.
+.IP \fB-f\fR 20
+Force the load even if the remote folder is in the wrong format.
+This will \fBdelete\fR the contents of the folder so use it carefully.
+.IP \fB-s\fR\ \fBtrimSize\fR 20
+If the number of messages in the remote folder is more than one plus
+trimsize (one is for the header message), then messages 2, 3, and so on
+will be deleted until there are only one plus trimsize messages left.
+If this option is not set no trimming will be done.
+.IP \fB-t\fR\ \fBType\fR 20
+The possible Types are \fBpinerc\fR, \fBabook\fR, and \fBsig\fR.
+(Sig is mostly obsolete. Literal signatures contained within the remote
+pinerc should be used instead.)
+.IP \fB-l\fR\ \fBLocal_file\fR 20
+The file on this system that is to be copied.
+.IP \fB-r\fR\ \fBRemote_folder\fR 20
+A remote folder name to be copied to.
+See the Alpine documentation for the syntax of a remote folder name.
+One example is
+.br
+{my.imap.server}remote_pinerc.
+.SH DIAGNOSTICS
+Exit status is zero if all goes well, -1 otherwise.
+.SH "SEE ALSO"
+Rpdump(1).
+.LP
+Copyright 1989-2007 by the University of Washington.
+
+$Date: 2005/01/14 20:40:14 $
diff --git a/doc/tech-notes.txt b/doc/tech-notes.txt
new file mode 100644
index 00000000..443f26c4
--- /dev/null
+++ b/doc/tech-notes.txt
@@ -0,0 +1,11840 @@
+
+ Alpine Technical Notes
+
+ Version 2.10, January 2013
+
+Table of Contents
+
+ Introduction
+
+ * Design Goals
+ * Alpine Components
+
+ Background Details
+
+ * Domain Names
+ * RFC 2822 Compliance
+ * SMTP and Sendmail
+ * Internet Message Access Protocol (IMAP)
+ * Multipurpose Internet Mail Extensions (MIME)
+ * Folder Collections
+
+ Building and Installation
+
+ * Compile-time Options
+ * Including LDAP Functionality
+ * Including Kerberos 5 Functionality
+ * Other Alpine Compile-time Options
+ * IMAPd Compile-time Options
+ * Building the Alpine Programs
+ * Installing Alpine and Pico on UNIX Platforms
+ * Installing PC-Alpine
+ * Installing IMAPd
+ * Support Files and Environment Variables: UNIX Alpine
+ * Support Files, Environment Variables, and Registry Values: PC-Alpine
+
+ Command Line Arguments
+
+ * Alpine
+ * Pico
+ * Pilot
+
+ Configuration and Preferences
+
+ * Alpine Configuration
+ * General Configuration Variables
+ * Configuration Features
+ * Hidden Config Variables and Features
+ * Retired Variables
+ * Tokens for Index and Replying
+ * Conditional Inclusion of Text for Reply-Leadin, Signatures, and
+ Templates
+ * Per Server Directory Configuration
+ * Color Configuration
+ * Index Line Color Configuration
+ * Role Configuration
+ * Filtering Configuration
+ * Scoring Configuration
+ * Other Rules Configuration
+ * Search Rules Configuration
+ * Patterns
+ * Configuring News
+ Configuration Notes
+ + Alpine in Function Key Mode
+ + Domain Settings
+ + Syntax for Collections
+ + Syntax for Folder Names
+ + Server Name Syntax
+ + Folder Namespaces
+ + What is a Mail Drop?
+ + Sorting a Folder
+ + Alternate Editor
+ + Signatures and Signature Placement
+ + Feature List Variable
+ + Configuration Inheritance
+ + Using Environment Variables
+ + SMTP Servers
+ + MIME.Types file
+ + Color Details
+ + S/MIME Overview
+ + Additional Notes on PC-Alpine
+
+ Behind the Scenes
+
+ * Address Books
+ * Remote Configuration
+ * Checkpointing
+ * Debug Files
+ * INBOX and Special Folders
+ * Internal Help Files
+ * International Character Sets
+ * Interrupted and Postponed Messages
+ * Message Status
+ * MIME: Reading a Message
+ * MIME: Sending a Message
+ * New Mail Notification
+ * NFS
+ * Printers and Printing
+ * Save and Export
+ * Sent Mail
+ * Spell Checker
+ * Terminal Emulation and Key Mapping
+
+ Introduction
+
+Design Goals
+
+ Throughout _Alpine_ development, we have had to strike a balance between the
+ need to include features which advanced users require and the need to keep
+ things simple for beginning users. To strike this balance, we have tried to
+ adhere to these design principles:
+
+ - The model presented to the user has to be simple and clear.
+ Underlying system operation is hidden as much as possible.
+ - It's better to have a few easily understood commands that can be
+ repeated than to have some more sophisticated command that will do
+ the job all at once.
+ - Whenever the user has to select a command, file name, address,
+ etc., the user should be given (or can get) a menu from which to make
+ the selection. Menus need to be complete, small, organized and well
+ thought out.
+ - _Alpine_ must provide immediate feedback for the user with each
+ operation.
+ - _Alpine_ must be very tolerant of user errors. Any time a user is
+ about to perform an irreversible act (send a message, expunge
+ messages from a folder), _Alpine_ should ask for confirmation.
+ - Users should be able to learn by exploration without fear of doing
+ anything wrong. This is an important feature so the user can get
+ started quickly without reading any manuals and so fewer manuals are
+ required.
+ - The core set of _Alpine_ functions should be kept to a minimum so
+ new users don't feel "lost" in seemingly extraneous commands and
+ concepts.
+
+ Just as there were goals relating to the look and feel of _Alpine_, there
+ were equally important goals having to do with _Alpine_'s structure-the
+ things that users never see but still rely on every time they use _Alpine_.
+ While _Alpine_ can be used as a stand-alone mail user agent, one of its
+ strongest assets is its use of the Internet Message Access Protocol (IMAP)
+ for accessing remote email folders. In addition, _Pine_ (the predecessor of
+ _Alpine_) was one of the first programs to support the Multipurpose Internet
+ Mail Extensions (MIME) specification. With MIME, _Alpine_ users can reliably
+ send any binary file to any other person on the Internet who uses a MIME
+ compliant email program.
+
+ The decision to use IMAP and MIME reflects the importance of
+ interoperability, standardization and robustness in _Alpine_. As you work
+ with _Alpine_ more, you will see other features which reflect the same
+ values. For example, _Alpine_ enforces strict compliance with RFC 2822,
+ implements a strong mail folder locking mechanism and verifies a process
+ before overwriting any files (e.g. addressbook, expunging messages).
+
+Alpine Components
+
+ If you have picked up the _Alpine_ distribution, then you already know that
+ _Alpine_ comes in a few different pieces. They are:
+
+ _Alpine_
+ The main code from which the _Alpine_ program is compiled.
+ _Pico_
+ _Pico_ is the name for the _Alpine_ composer. The _Pico_ code is used
+ in two ways: (1) it is compiled on its own to be a stand-alone editor
+ and, (2) it is compiled as a library for _Alpine_ to support
+ composition of messages within _Alpine_. _Pico_ is _Alpine_'s
+ internal editor invoked when users need to fill in header lines or
+ type the text of an email message.
+ _Imap_
+ An API for IMAP. Includes the C-Client library, which is compiled
+ into _Alpine_, and the IMAP server _IMAPd_. C-Client implements the
+ IMAP protocol and also negotiates all access between _Alpine_ and the
+ mail folders it operates on, even if the folders are local. The
+ C-Client routines are used for email folder parsing and interpreting
+ MIME messages. _IMAPd_ is a separate server that handles IMAP
+ connections from any IMAP-compliant email program. When _Alpine_
+ accesses a remote mailbox, the _Alpine_ program is the IMAP client
+ and the _IMAPd_ program is the IMAP server. Of course, _Alpine_ can
+ use any IMAP-compliant IMAP server, not just _IMAPd_.
+
+ Background Details
+
+Domain Names
+
+ Domain names are used to uniquely name each host on the Internet. A domain
+ name has a number of parts separated by periods. Each label represents a
+ level in the hierarchy. An example of a name is:
+
+ olive.cac.washington.edu
+
+ In this domain name the top-level label is _edu_, indicating it is at an
+ educational institution, the second-level label is _washington_, indicating
+ the University of Washington. _cac_ is a specific department within the
+ University of Washington, and _olive_ is the host name. The top-level names
+ are assigned by Internet organizations, and other names are assigned at the
+ appropriate level. The Domain Name Service, DNS, is the distributed database
+ used to look up these names.
+
+ _Alpine_ relies on domain names in multiple places. A domain name is
+ embedded into the message-id line generated for each piece of email. A
+ domain name is needed to contact an IMAP server to get access to remote
+ INBOXes and folders. Most importantly, domain names are needed to construct
+ the From: line of your outgoing messages so that people on the Internet will
+ be able to get email back to you.
+
+ On UNIX systems, you can set the domain via the user-domain variable in the
+ _Alpine_ configuration file, or rely on the file /etc/hosts which usually
+ sets the name of the local host. While _Alpine_ can often deliver email
+ without the domain name being properly configured, it is best to have this
+ set correctly. Problems can usually be solved by adjusting the system's
+ entry in the /etc/hosts file. The fully-qualified name should be listed
+ before any abbreviations. For example,
+
+ 128.95.112.99 olive.cac.washington.edu olive
+
+ is preferred over
+
+ 128.95.112.99 olive olive.cac.washington.edu
+
+ On PCs, the task of configuring the domain name is a bit different. Often
+ times PCs do not have domain names-they have _IP addresses_. IP addresses
+ are the numbers which uniquely identify a computer on the network. The way
+ you configure your IP address depends on the networking software which you
+ use on the PC. You can refer to the documentation which came with your
+ networking software or see the PC specific installation notes for help
+ configuring the IP address with your network software.
+
+ With PCs, it is vital that users set the variable user-domain in the _Alpine_
+ configuration file (PINERC).
+
+ Details on configuring _Alpine_ with correct domain names can be found in
+ the Domain Settings section of this document.
+ _________________________________________________________________
+
+RFC 2822 Compliance
+
+ _Alpine_ tries to adhere to RFC 2822 fairly strictly.
+
+ As far as outgoing email is concerned, _Alpine_ fully-qualifies addresses
+ whenever possible. They are even displayed in fully-qualified form on the
+ terminal as the user composes a message. This makes addresses more clear and
+ gives a hint to the user that the network extends beyond the local
+ organization. _Alpine_ implements fully-qualified domain names by tacking on
+ the local domain to all unqualified addresses which a user types in. Any
+ address which does not contain an "@" is considered unqualified.
+
+ The format for addresses allows for spaces and special characters in the
+ full name of an address. For this reason, commas are required to separate
+ addresses. If any special characters as defined in RFC 2822 appear in the
+ full name, quotes are required around the address. _Alpine_ will insert the
+ quotes automatically if needed. The common cases where this happens are with
+ periods after initials and parentheses.
+
+ _Alpine_ expects dates to be in the standard RFC 822 format which is
+ something like:
+ [www, ] dd mmm yy hh:mm[:ss] [timezone]
+
+ It will attempt to parse dates that are not in this format. When an
+ unparsable date is encountered it is shown as question marks in the FOLDER
+ INDEX screen.
+ _________________________________________________________________
+
+SMTP and Sendmail
+
+ _Alpine_ is a _user agent_ not a _message transfer agent_ (MTA). In plain
+ English, that means _Alpine_ does not know how to interact with other
+ computers on the Internet to deliver or receive email. What _Alpine_ does
+ know how to do is help users read, organize and create email. The "dirty
+ work" of delivering and accepting email is handled by other programs.
+
+ All outgoing email is delivered to an SMTP server or to a mail transfer
+ agent. A common mail transfer agent is sendmail. The usual method of
+ delivery used by _Alpine_ is to use either a local or a remote SMTP server.
+
+ The selection of which MTA to use depends on the settings of smtp-server,
+ sendmail-path, and compile-time options. The first MTA specified in the
+ following list is used:
+ 1. _sendmail-path_ in /usr/local/lib/pine.conf.fixed
+ 2. _smtp-server_ in /usr/local/pine.conf.fixed
+ 3. _sendmail-path_ specified on the command line.
+ 4. _smtp-server_ specified on the command line.
+ 5. _sendmail-path_ in the user's .pinerc file.
+ 6. _smtp-server_ in the user's .pinerc file.
+ 7. _sendmail-path_ in /usr/local/lib/pine.conf
+ 8. _smtp-server_ in /usr/local/pine.conf
+ 9. DF_SENDMAIL_PATH defined at compile time.
+ 10. SENDMAIL and SENDMAILFLAGS defined at compile time.
+
+ If the _sendmail-path_ form is used, a child process is forked, and the
+ specified command is executed with the message passed on standard input.
+ Standard output is then passed back and displayed for the user. _NOTE: The
+ program MUST read the message to be posted on standard input, AND operate in
+ the style of sendmail's "-t" option. This method is not recommended unless
+ there are special reasons you want to do this. _
+
+ If an _smtp-server_ is specified, _Alpine_ operates as an SMTP client. SMTP
+ stands for _Simple Mail Transfer Protocol_; it specifies the rules by which
+ computers on the Internet pass email to one another. In this case, _Alpine_
+ passes outgoing email messages to a designated SMTP server instead of to a
+ mail transfer program on the local machine. A program on the server then
+ takes care of delivering the message. To make _Alpine_ operate as an SMTP
+ client, the smtp-server variable must be set to the IP address or host name
+ of the SMTP server within your organization. This variable accepts a comma
+ separated list of servers, so you can specify multiple alternate SMTP
+ servers. _PC-Alpine_ only runs as an SMTP client so the _smtp-server_ option
+ is mandatory.
+
+ For UNIX _Alpine_, if neither _smtp-server_ or _sendmail-path_ is set, the
+ default sendmail program is invoked with the "-bs -odb -oem" flags, and the
+ message is sent using the SMTP protocol.
+ _________________________________________________________________
+
+Internet Message Access Protocol (IMAP)
+
+ IMAP is a remote access protocol for message stores. _Alpine_ uses IMAP to
+ get at messages and folders which reside on remote machines. With IMAP,
+ messages are kept on the server. An IMAP client (such as _Alpine_) can
+ request specific messages, headers, message structures, message parts, etc.
+ The client can also issue commands which delete messages from folders on the
+ server. IMAP's closest kin is POP, the Post Office Protocol, which works by
+ transferring an entire mailbox to the client where all the mail is kept. For
+ a comparison of IMAP and POP, see the paper "Comparing Two Approaches to
+ Remote Mailbox Access: IMAP vs. POP" by Terry Gray. A more detailed
+ exploration of message access may be found in the paper " Message Access
+ Paradigms and Protocols."
+
+ IMAP Features:
+ * Allows access to mail folders from more than one client computer.
+ * Works well over low-bandwidth lines because information is sent in small
+ pieces as needed by the user. For example, only header information is
+ sent to build index lists, and if someone sends a large audio file via
+ MIME, you can choose when (or if) you want to get that part of the
+ message.
+ * Email can be delivered and stored on a well-maintained and reliable
+ server which is "always-up".
+ * Folders can be accessed and manipulated from anywhere on the Internet.
+ * Users can get to messages stored in different folders within the same
+ _Alpine_ session.
+ * Allows use of IMAP server for searching and parsing.
+ * The latest revision of IMAP (IMAP4) also provides for disconnected
+ operation, including resynchronization of message state between mail
+ servers and message caches on clients. _Alpine_ does not support this
+ capability, however.
+
+ IMAP4rev1 is described in RFC 3501. Further information about IMAP may be
+ obtained from the University of Washington's IMAP Information Center.
+
+ _Alpine_ is an IMAP4rev1 client.
+ _________________________________________________________________
+
+Multipurpose Internet Mail Extensions (MIME)
+
+ MIME is a way of encoding a multipart message structure into a standard
+ Internet email message. The parts may be nested and may be of seven
+ different types: Text, Audio, Image, Video, Message, Application and
+ Multipart (nested). The MIME specification allows email programs such as
+ _Alpine_ to reliably and simply exchange binary data (images, spreadsheets,
+ etc.). MIME includes support for international character sets, tagging each
+ part of a message with the character set it is written in, and providing
+ 7-bit encoding of 8-bit character sets.
+
+ The MIME standard was officially published in June of 1992 as RFC 1341 and
+ subsequently revised in RFC 2045 when it became a full Internet Standard.
+ _Pine_ 3.0 was one of the first email programs to Implement MIME. Now, there
+ are dozens of commercial and freely available MIME-capable email programs.
+ In addition, MIME is being added to newsreaders so MIME messages can be
+ posted and read in USENET newsgroups.
+
+ The MIME standard also includes support for non-ASCII text in message
+ headers through the extensions described in RFC 1342 and subsequently
+ revised in RFC 2047.
+
+ An actual MIME message looks something like this:
+Date: Tue, 12 Mar 1996 15:39:35 -0800 (PST)
+From: David L Miller <dlm@cac.washington.edu>
+To: David L Miller <dlm@cac.washington.edu>
+Subject: =?iso-8859-1?Q?Test_MIME_message_with_RFC-1522_headers_=28=E1?= =?i
+so-8859-1?Q?=E2=E3=29?=
+Message-Id: <Pine.ULT.3.92.960312150851.21583I-101000@shiva2.cac.washington.edu
+>
+Mime-Version: 1.0
+Content-Type: MULTIPART/MIXED; BOUNDARY="0-1737669234-826673975=:21583"
+Content-Id: <Pine.ULT.3.92.960312153928.21583O@shiva2.cac.washington.edu>
+
+ This message is in MIME format. The first part should be readable text,
+ while the remaining parts are likely unreadable without MIME-aware tools.
+ Send mail to mime@docserver.cac.washington.edu for more info.
+
+--0-1737669234-826673975=:21583
+Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
+Content-ID: <Pine.ULT.3.92.960312153104.21583L@shiva2.cac.washington.edu>
+
+The text of the message would go here. It is readable if
+one doesn't mind wading around a little bit of the MIME
+formatting. After this is a binary file in base 64
+encoding.
+
+|\ | |\/| David L. Miller dlm@cac.washington.edu (206) 685-6240
+|/ |_ | | Software Engineer, Pine Development Team (206) 685-4045 (FAX)
+University of Washington, Networks & Distributed Computing, JE-20
+4545 15th Ave NE, Seattle WA 98105, USA
+
+--0-1737669234-826673975=:21583
+Content-Type: APPLICATION/ZIP; NAME="test.zip"
+Content-Transfer-Encoding: BASE64
+Content-ID: <Pine.ULT.3.92.960312153638.21583N@shiva2.cac.washington.edu>
+Content-Description: Test Attachment
+
+UEsDBBQAAAAIAGh8bCBbZKT4ygIAAHgFAAAEAAAAdGVzdIVUX2vbMBB/16c4
+9rSBNyjsYX1UHSUROLInycv2qNhKI5ZYxlLa5dvvpDRLw6CFgJF09/t3Rxo3
+WDBDD43rPJjJQpxMbw9m+h3AbyHuLLSDe7JTcPGUbtYm7NzwGP3wBYQnnT8c
+7NQ5s4djsC8t4QbmYE6wsfjpLTy7uPPHCOPk/ATPk4vRDmS008GF4PzwPich
+zY3m4LfxOQlPNy4GcEO3P/a2h2j/xGyp9ONpco+7CHf33+4/393ff4XNibzL
+c1UVfXJXQIdIBRx877b4TYy9C3Fym2NEyzsX/pNDet8dD3aIJiagLbo2wwnG
+4zT6cK66ZLK1NhH9J4tcZQEy7OxkNyd4nMwQbV9glP7JZb87E3O32fgnm7We
+XQ8+us4SM47WTCkgMPt9enc2ZAW5c+Pj7o32l0IXXk/r8pSRE3A4jqOfIqqF
+G+PFlSdRDOaQduXNESTwtDcYfJ8191gWXUjYmOJ43Oxdh11JTzRuSPcY37+B
+vNqmf0O5RB1G27mt64rLCp4X8pW1L6BvxunCeYHNk3F7s9lb+GAwyvAhOyNE
+Lxm0gv9gUnH9C+o5rKlacrHQtYAZV2VF+UoBrSp8kJIKzZkqgP1sJFMKagl8
+1VSczQqy5noJki2onIGuQS+5AlXPNfaxArgoq3aGwJDq6lZDxVdcU82RKMG/
+4JArTVKzYrJc4pE+8CoJpGIGc65FIp8jO4WGSs3LtqISmlY2tUKyVMUFETWw
+H0xoUMvE8KbXB4aC6EPFzrDiF6iGlZxWBeFixiUrdXJb1kKx7y2C4hPM6Iou
+WI4hdVyO6yXVqkZqiXmottLJ9lzWK1LVKttqk8oZ1TS1NrJGS5jqeslQI0aK
+ieCvzNlgNZJqiccCc5WafLxmKdii4gsmSvYpISkteamzkRwXJiG5SoUpcERK
+8xIE8QQ7o+eh5WAUy1qYRP8rioip/maI+OfyF1BLAQIUAxQAAAAIAGh8bCBb
+ZKT4ygIAAHgFAAAEAAAAAAAAAAEAAACkgQAAAAB0ZXN0UEsFBgAAAAABAAEA
+MgAAAOwCAAAAAA==
+--0-1737669234-826673975=:21583--
+
+ For details about _Alpine_'s implementation of MIME, see the two MIME
+ sections "MIME: Reading a Message" and "MIME: Sending a Message" later in
+ this document.
+ _________________________________________________________________
+
+Folder Collections
+
+ Folder Collections are _Alpine_'s way of dealing with more than a single
+ group of folders.
+
+ For a more complete description of Folder Collections, see the section on
+ "Syntax for Collections."
+
+ The _Alpine_ distribution is designed to require as little configuration and
+ effort at compile time as possible. Still, there are some _Alpine_ behaviors
+ which are set at the time you compile _Alpine_. For each of these, there is
+ a reasonable (our opinion) default built into the code, so most systems
+ administrators will have no need for these steps.
+
+ Building and Installation
+
+Compile-time Options
+
+ _Alpine_'s UNIX build environment is based on Autotools (the GNU Build
+ System). Once you've unpacked the source distribution find the file
+ configure in the top-level directory. You may look at the many options
+ available by typing
+
+ ./configure --help
+
+ or you could just try building with the command
+
+ ./configure
+
+ followed by
+
+ make
+
+ Note, while the UW IMAP Toolkit (whose c-client library _Alpine_ uses for
+ mailbox access) build is not based on Autotools, _Alpine_'s configure script
+ should set an appropriate make target and compilation options for most
+ systems.
+
+ Some of the following can only be set when you build. Others, however, can
+ be overridden by command-line flags to _Alpine_ or settings in _Alpine_'s
+ user or system configuration files. Some of the options which can be set
+ when building:
+
+ Including LDAP Functionality
+
+ By default, the configure script will attempt to find the LDAP library
+ support for you. If you are having trouble with LDAP take a look at the
+ configure options
+ --with-ldap-dir=DIR
+ Specify the root of the LDAP lib/include path.
+ --with-ldap-include-dir=DIR
+ Specify the LDAP include path.
+ --with-ldap-lib-dir=DIR
+ Specify the LDAP library path.
+ --without-ldap
+ Disable LDAP support.
+
+ _Alpine_ uses LDAPv3 protocol. When using the LDAPv3 protocol, the results
+ are assumed to be in the UTF-8 character set, which _Alpine_ handles well.
+ If the LDAP server returns non-ascii data which is not encoded as UTF-8 you
+ will probably run into problems.
+
+ Including Kerberos 5 Functionality
+
+ This works analogously to the LDAP build. By default, the configure script
+ will attempt to find the Kerberos library support for you. If you are having
+ trouble with Kerberos take a look at the configure options
+ --with-krb5-dir=DIR
+ Specify the root of the Kerberos lib/include path.
+ --with-krb5-include-dir=DIR
+ Specify the Kerberos include path.
+ --with-krb5-lib-dir=DIR
+ Specify the Kerberos library path.
+ --without-krb5
+ Disable Kerberos support.
+
+ Other Alpine Compile-time Options
+
+ --disable-nls
+ Do not use Native Language Support. NLS refers to the use of GNU
+ gettext utilities to localize a program, in the sense that English is
+ translated to some other language. At the time this was written the
+ low-level support for NSL is included in _Alpine_ but no translations
+ have been done. If there is no translation available, that means that
+ disabling NLS will make no difference. If you have trouble building
+ which is due to gettext or libintl you could try this option, or one
+ of the following.
+ --with-libintl-prefix[=DIR]
+ --without-libintl-prefix
+ --with-ssl-dir=DIR
+ Specify the root of the SSL lib/include path (OpenSSL).
+ --with-ssl-include-dir=DIR
+ Specify the SSL include path.
+ --with-ssl-lib-dir=DIR
+ Specify the SSL library path.
+ --with-ssl-certs-dir=DIR
+ Specify the path to the SSL certificates directory.
+ --without-ssl
+ Disable SSL support.
+ --without-pthread
+ Do not test for nor build with POSIX thread support, which is used
+ only for the Busy-Cue in the status line at this time.
+ --without-smime
+ Disable S/MIME support.
+ --disable-debug
+ Never create debug files.
+ --with-smtp-msa=PATH
+ Local Mail Submission Agent (sendmail, by default).
+ --with-smtp-msa-flags=FLAGS
+ MSA flags for SMTP on stdin/stdout (-bs -odb -oem).
+
+ There are many more options which you can see using the
+
+ ./configure --help
+
+ command.
+
+ IMAPd Compile-time Options
+
+ There are no options or settings required for the version of _IMAPd_
+ distributed with _Alpine_. If you need to be doing more complex
+ modifications to IMAP, then you should pick up the IMAP development package
+ and work with that code. The developer's version of IMAP is available for
+ anonymous ftp from ftp.cac.washington.edu in the directory mail. The file is
+ called imap.tar.Z. Unless it has changed since _Alpine_ was released, the
+ directory imap in the _Alpine_ distribution is the IMAP development package.
+
+ The c-client library has not been converted to use the GNU Build System's
+ autotools. The _Alpine_ configure script will try to correctly guess the
+ arguments needed for the c-client make command and will build the library,
+ but if you need to change anything you should take a look at imap/docs/BUILD
+ for more detailed instructions.
+ _________________________________________________________________
+
+Building the Alpine Programs
+
+ You may have already compiled _Alpine_ and tried it out. If so, great! If
+ not, you should be able to do it without too much trouble by following these
+ step-by-step instructions:
+
+ 1. Make sure you're in the root of the _Alpine_ source. When you type ls
+ you should see the following files and directories (or something close
+ to it):
+aclocal.m4 config.sub imap Makefile.am packages web
+alpine configure include Makefile.in pico
+build.bat configure.ac install-sh mapi pith
+build.cmd contrib LICENSE missing po
+config.guess depcomp ltmain.sh mkinstalldirs README
+config.rpath doc m4 NOTICE VERSION
+ 2. Give the command ./configure Configure should grind away for a few
+ minutes.
+ 3. When configure is complete, give the command make. If make stops and
+ asks
+
+ Do you want to build with IPv6 anyway? Type y or n please:
+ you should answer with a 'y'. The compiler should grind away for a few
+ minutes. The _Alpine_ binary will end up in .../alpine/alpine and the
+ Pico and Pilot binaries in .../pico/pico and .../pico/pilot. Other
+ binaries you may be interested in are .../alpine/rpdump and
+ .../alpine/rpload and c-client binaries in the directories
+ .../imap/imapd, .../imap/ipopd, .../imap/mailutil, and so on.
+ 4. If you need to try again, make sure you're getting a clean start by
+ giving the command make clean.
+ _________________________________________________________________
+
+Installing Alpine and Pico on UNIX Platforms
+
+ Installing _Alpine_ and _Pico_ is simple. You take the program files which
+ you have just transferred or built and you move them to the correct
+ directory on your system. Most often the binaries go in /usr/local/bin
+ though sometimes they are placed in /usr/bin. All the help text is compiled
+ into _Alpine_ so there are no _required_ auxiliary files. Instead of copying
+ the binaries manually, you may use make install to install them.
+
+ There are three optional auxiliary files: /usr/local/lib/pine.info,
+ /usr/local/lib/pine.conf, and /usr/local/lib/pine.conf.fixed. The file
+ pine.info contains text on how to get further help on the local system. It
+ is part of the help text for the main menu and should probably refer to the
+ local help desk or the system administrator. If this file doesn't exist a
+ generic version which suggests ``talking to the computer support staff at
+ your site'' is shown. The file pine.conf is used to set system-wide default
+ configurations for _Alpine_. The file pine.conf.fixed is also used to set
+ system-wide default configurations for _Alpine_. The difference between
+ these two files is that configuration variables set in the pine.conf.fixed
+ file may not normally be over-ridden by a user. See the section on Alpine
+ Configuration later in this document for details about the pine.conf and
+ pine.conf.fixed files.
+ _________________________________________________________________
+
+Installing PC-Alpine
+
+ The PC-Alpine distribution comes as a .zip file. To install, unzip the files
+ to a directory where you would like the program to reside. Modern Windows
+ versions come with the capability of unzipping .zip files. Failing that, you
+ can use one of the many .zip file extractors out there. Following current
+ Windows conventions, a common directory into which the files could be
+ extracted would be C:\Program Files\PC-Alpine\.
+
+ Having extracted PC-Alpine's .zip file to the directory of choice, you can
+ now run that directory's alpine.exe, which is the actual PC-Alpine program.
+ For convenience, you could place shortcuts to it on the task bar, start
+ menu, etc.
+
+ Upon first running PC-Alpine, you may be asked where you would like to
+ access your Configuration file (called the _pinerc_). This is useful in
+ accessing already existing configuration files, and it does not matter where
+ this file gets created. If you are connecting to an IMAP server to access
+ your email, it is also possible to store this Configuration data on that
+ server, which facilitates accessing the same configuration from multiple
+ machines (in fact, your configuration may have already been set up this way
+ for use with other _Alpine_ programs).
+
+ After having established the location of the configuration file, it may be
+ necessary to specify a few configuration settings before reading or sending
+ mail. You may be prompted for the following (which may also be edited from
+ the (S)etup (C)onfig screen from the Main Menu):
+
+ Folder to open as inbox (or _inbox-path_) - This can be an inbox residing
+ on an IMAP or POP3 server, or one residing locally. An example of an INBOX
+ for an IMAP server is: {server.example.com}INBOX.
+
+ User-id, Personal name, and host/domain, which are to be used as your
+ email address.
+
+ SMTP server to forward message - You must enter your SMTP server before
+ you can send any messages.
+
+ At this point, you will be able to read and send email messages. There are,
+ however, many more preferences that you can set in the Configuration screen.
+ _________________________________________________________________
+
+Installing IMAPd
+
+ When the _Alpine_ distribution is built on a UNIX system, the IMAP server
+ binary, imapd, is compiled. Installing imapd requires placing the binary in
+ the appropriate directory, usually /usr/etc, and adding entries to
+ /etc/services and /etc/inetd.conf or their counterparts.
+
+ Instead of including installation instructions here we'll just include a
+ pointer to detailed instructions in the c-client distribution. Please take a
+ look at the file imap/docs/BUILD in the source tree.
+ _________________________________________________________________
+
+Support Files and Environment Variables: UNIX Alpine
+
+ This section lists the various files which _Alpine_ uses which are not email
+ folders. All of these are the default names of files, they may vary based on
+ _Alpine_'s configuration.
+ /usr/local/lib/pine.conf
+ Pine's global configuration file.
+ /usr/local/lib/pine.conf.fixed
+ Non-overridable global configuration file.
+ /usr/local/lib/pine.info
+ Local pointer to system administrator.
+ ~/.pinerc
+ Personal configuration file for each user.
+ ~/.pinercex
+ Personal exceptions configuration file for each user.
+ ~/.addressbook
+ Personal addressbook
+ ~/.newsrc
+ Personal USENET subscription list. This is shared with other
+ newsreading programs.
+ ~/.pine-debugX
+ The files created for debugging _Alpine_ problems. By default, there
+ are 4 .pine-debug files kept at any time.
+ ~/.signature
+ A signature file which will be included in all outgoing email
+ messages.
+ ~/.pine-interrupted-mail
+ The text of a message which was interrupted by some unexpected error
+ which _Alpine_ detected.
+ ~/mail/postponed-msgs
+ A folder of messages which the user chose to postpone.
+ /etc/mailcap
+ System-wide mail capabilities file. Only used if $MAILCAPS not set.
+ ~/.mailcap
+ Personal mail capabilities file. Combines with system-wide mailcap.
+ Only used if $MAILCAPS not set.
+
+ The location of the following support files may be controlled by variables
+ in the personal or global _Alpine_ configuration file: signature,
+ addressbook and its index file, postponed messages, and newsrc.
+
+ Unix _Alpine_ uses the following environment variables:
+ TERM
+ Tells _Alpine_ what kind of terminal is being used.
+ DISPLAY
+ Determines if _Alpine_ will try to display IMAGE attachments.
+ TMPDIR, TMP, or TEMP
+ Specifies location of temporary storage area, first one set wins
+ SHELL
+ If not set, default is /bin/sh
+ MAILCAPS
+ A semicolon delimited list of path names to mailcap files.
+ _________________________________________________________________
+
+Support Files, Environment Variables, and Registry Settings: PC-Alpine
+
+ This section lists the various files which _PC-Alpine_ uses which are not
+ normal mail folders. All of these are the default names of files, they may
+ vary based on _Alpine_'s configuration.
+
+ $PINERC or <PineRC registry value> or $HOME\PINE\PINERC or <PINE.EXE
+ dir>\PINERC
+ Path to (required) personal configuration file.
+ $PINERCEX or $HOME\PINE\PINERCEX or <PINE.EXE dir>\PINERCEX
+ Path to personal exceptions configuration file.
+ $PINECONF
+ Path of optional global configuration file.
+ <PINERC directory>\ADDRBOOK
+ Personal addressbook
+ <PINERC directory>\PINEDEBG.TXT
+ Location of _Alpine_ debug file.
+ <PINERC directory>\MAILCAP and/or <PINE.EXE dir>\MAILCAP
+ These paths are only used if $MAILCAPS not set.
+ $HOME\NEWSRC or <PINERC directory>\NEWSRC
+ Personal USENET subscription list. This may be shared with other
+ newsreading programs.
+ $HOME\MAIL\INTRUPTD
+ The text of a message which was interrupted by some unexpected error
+ which _Alpine_ detected.
+ $HOME\MAIL\POSTPOND
+ A folder of messages which the user chose to postpone.
+
+ Registry Values:
+ HKEY_LOCAL_MACHINE\Software\University of Washington\Alpine\1.0
+ _Pinedir_: The directory that contains the _Alpine_ executable.
+ _PineEXE_: The name of the _Alpine_ executable (most commonly
+ "alpine.exe").
+ HKEY_CURRENT_USER\Software\University of Washington\Alpine\1.0
+ _PineRC_: The path that points to the default pinerc to use.
+ HKEY_LOCAL_MACHINE\Software\Clients\Mail\Alpine
+ _DLLPath_: The path that points to _Alpine_'s pmapi32.dll.
+ HKLM\Software\Clients\Mail\Alpine\shell\open\command
+ _(Default)_: When set as the default mailer, this is the command that
+ is run by external programs.
+ HKLM\Software\Clients\Mail\Alpine\Protocols\Mailto\DefaultIcon
+ _(Default)_: This points to the icon to display in relation to
+ _Alpine_'s mailto URL rendering.
+ HKLM\Software\Clients\Mail\Alpine\Protocols\Mailto\shell\open\command
+ _(Default)_: This value is the command that gets run by external
+ programs when a mailto URL is run with _PC-Alpine_ set as the default
+ mailer.
+ HKLM\Software\Clients\News\Alpine\shell\open\command
+ _(Default)_: When set as the default newsreader, this is the command
+ that is run by external programs.
+ HKLM\Software\Clients\News\Alpine\Protocols\news\DefaultIcon
+ _(Default)_: This points to the icon to display in relation to
+ _Alpine_'s news URL rendering.
+ HKLM\Software\Clients\News\Alpine\Protocols\news\shell\open\command
+ _(Default)_: This value is the command that gets run by external
+ programs when a news URL is run with _Alpine_ set as the default
+ newsreader.
+ HKLM\Software\Clients\News\Alpine\Protocols\nntp\DefaultIcon
+ _(Default)_: This points to the icon to display in relation to
+ _Alpine_'s nntp URL rendering.
+ HKLM\Software\Clients\News\Alpine\Protocols\nntp\shell\open\command
+ _(Default)_: This value is the command that gets run by external
+ programs when a nntp URL is run with _Alpine_ set as the default
+ newsreader.
+
+ _Alpine_'s personal configuration file may be in the same directory as the
+ executable, or if that is inconvenient because the executable is on a shared
+ or read-only drive, then it can be in a file named by the $PINERC
+ environment variable, or in $HOME\ALPINE\PINERC, where if not set, $HOME
+ defaults to the root of the current working drive.
+
+ Most of the other support files key off of the location of the PINERC file.
+ However, in the case of the NEWSRC file, the path $HOME\NEWSRC is checked
+ first. Also, the postponed messages and interrupted message folders are
+ placed in the default folder collection, normally in the directory
+ $HOME\MAIL.
+
+ The location of the following support files may be controlled by variables
+ in the personal or global _Alpine_ configuration file: signature,
+ addressbook (and its index file), postponed messages, and newsrc.
+
+ _PC-Alpine_ uses the following environment variables:
+ PINERC
+ Overrides default path to pinerc file.
+ PINERCEX
+ Overrides default path to personal exceptions configuration file.
+ PINECONF
+ Optional path to global _Alpine_ config file.
+ HOME
+ If not set, _Alpine_ uses the root of the current drive, e.g. C:
+ TMPDIR, TMP, or TEMP
+ Specifies location of temporary storage area, first one set wins
+ COMSPEC
+ Specifies shell for external commands.
+ MAILCAPS
+ A semicolon delimited list of path names to mailcap files.
+
+ Command Line Arguments
+
+Alpine
+
+ _Alpine_ and _PC-Alpine_ can accept quite a few command-line arguments. Many
+ of these arguments overlap with variables in the _Alpine_ configuration
+ file. If there is a difference, then a flag set in the command line takes
+ precedence. Both _Alpine_ and _PC-Alpine_ expect command line arguments
+ (other than addresses) to be preceded by the "-" (dash) as normally used by
+ UNIX programs.
+
+ _[addresses]_
+ Send-to: If you give _Alpine_ an argument or arguments which do not
+ begin with a dash, _Alpine_ treats them as email addresses. _Alpine_
+ will startup in the composer with a message started to the addresses
+ specified. Once the message is sent, the _Alpine_ session closes.
+ Standard input redirection is allowed. Separate multiple addresses
+ with a space between them. Addresses are placed in the "To" field
+ only.
+ < _file_
+ _Alpine_ will startup in the composer with _file_ read into the body
+ of the message. Once the message is sent, the _Alpine_ session
+ closes.
+ -attach _file_
+ Go directly into composer with given file attached.
+ -attachlist _file-list_
+ Go directly into composer with given files attached. This must be the
+ last option on the command line.
+ -attach_and_delete _file_
+ Go directly into composer with given file attached, delete when
+ finished.
+ -aux _local_directory_
+ _PC-Alpine_ only. This tells _PC-Alpine_ the local directory to use
+ for storing auxiliary files, like debug files, address books, and
+ signature files. The pinerc may be local or remote.
+ -nosplash
+ _PC-Alpine_ only. This tells _PC-Alpine_ to not display the splash
+ screen upon startup. This may be helpful for certain troubleshooting
+ or terminal server scenarios.
+ -bail
+ If the personal configuration file doesn't already exist, exit. This
+ might be useful if the configuration file is accessed using some
+ remote filesystem protocol. If the remote mount is missing this will
+ cause _Alpine_ to quit instead of creating a new pinerc.
+ -c _n_
+ When used with the -f option, apply the _n_th context. This is used
+ when there are multiple folder collections (contexts) and you want to
+ open a folder not in the primary collection.
+ -conf
+ Configuration: Prints a sample system configuration file to the
+ screen or standard output. To generate an initial system
+ configuration file, execute
+ alpine -conf > /usr/local/lib/pine.conf
+ To generate a system configuration file using settings from an old
+ system configuration file, execute
+ alpine -P old-pine.conf -conf > /usr/local/lib/pine.conf
+ A system configuration file is not required.
+ -convert_sigs _-p pinerc_
+ Convert signatures contained in signature files into literal
+ signatures.
+ -copy_abook _<local_abook_file> <remote_abook_folder>_
+ Copy an address book file to a remote address book folder. If the
+ remote folder doesn't exist, it will be created. If it exists but the
+ first message in the folder isn't a remote address book header
+ message, the copy will be aborted. This flag will not usually be used
+ by a user. Instead, the user will create a remote address book from
+ within _Alpine_ and copy entries from the local address book by using
+ aggregate Save in the address book screen.
+ -copy_pinerc _<local_pinerc_file> <remote_pinerc_folder>_
+ Copy a pinerc configuration file to a remote pinerc folder. If the
+ remote folder doesn't exist, it will be created. If it exists but the
+ first message in the folder isn't a remote pinerc header message, the
+ copy will be aborted. This flag may be useful to users who already
+ have a local pinerc file and would like to convert it to a remote
+ pinerc folder and use that instead. This gives a way to bootstrap
+ that conversion without having to manually reset all of the variables
+ in the remote pinerc folder.
+ -d _debug-level_
+ Debug Level: Sets the level of debugging information written by
+ _Alpine_. _Debug-level_ can be set to any integer 0-9. A debug level
+ of 0 turns off debugging for the session. (Actually there are some
+ levels higher than 9, but you probably don't want to see them.
+ Sensitive authentication information is hidden at levels less than
+ 10.)
+ -d _keywords_
+ You may use a more detailed version of the debugging flag to set the
+ debug level in separate parts of _Alpine_. The possibilities are
+ flush, timestamp, imap=0..4, tcp, numfiles=0..31, and verbose=0..9.
+ _Flush_ causes debugging information to be flushed immediately to the
+ debug file as it is written. _Verbose_ is the general debugging
+ verbosity level. _Timestamp_ causes timestamps to be added to the
+ debug file, which is useful when you are trying to figure out what is
+ responsible for delays. _Numfiles_ sets the number of debug files
+ saved. _Imap_ sets the debug level for the debugging statements
+ related to the conversation with the IMAP server, and more generally,
+ for the debugging related to _Alpine_'s interaction with the C-Client
+ library. If _imap_ is set higher than 4, sensitive authentication
+ information will be included in the debug file. _Tcp_ adds more
+ TCP/IP debugging information.
+ -f _folder_
+ Startup folder: _Alpine_ will open this folder in place of the
+ standard INBOX.
+ -F _file_
+ Open named text file for viewing and forwarding.
+ -h
+ Help: Prints the list of available command-line arguments to the
+ screen.
+ -i
+ _Alpine_ will start up in the FOLDER INDEX screen instead of the MAIN
+ MENU.
+ Configuration equivalent: _initial-keystroke-list=i_.
+ -I _a,b,c,..._
+ Initial Keystrokes: _Alpine_ will execute this comma-separated
+ sequence of commands upon startup. This allows users to get _Alpine_
+ to start in any of its menus/screens. You cannot include any input to
+ the composer in the initial keystrokes. The key <Return> is
+ represented by a ``CR'' in the keystroke list; the spacebar is
+ designated by the letters ``SPACE''. Control keys are two character
+ sequences beginning with ``^'', such as ``^I''. A tab character is
+ ``TAB''. Function keys are ``F1'' - ``F12'' and the arrow keys are
+ ``UP'', ``DOWN'', ``LEFT'', and ``RIGHT''. A restriction is that you
+ can't mix function keys and character keys in this list even though
+ you can, in some cases, mix them when running _Alpine_. A user can
+ always use only _character_ keys in the startup list even if he or
+ she is using _function_ keys normally, or vice versa. If an element
+ in this list is a string of characters surrounded by double quotes
+ (") then it will be expanded into the individual characters in the
+ string, excluding the double quotes.
+ Configuration equivalent: _initial-keystroke-list_
+ -install
+ For _PC-Alpine_ only, this option prompts for some basic setup
+ information, then exits.
+ -k
+ Function-Key Mode: When invoked in this way, _Alpine_ expects the
+ input of commands to be function-keys. Otherwise, commands are linked
+ to the regular character keys.
+ Configuration equivalent: _use-function-keys_ included in
+ _feature-list_.
+ -n _n_
+ Message-Number: When specified, _Alpine_ starts up in the FOLDER
+ INDEX screen with the current message being the specified message
+ number.
+ -nowrite_password_cache
+ This tells _Alpine_ to use the local password cache if there is one,
+ but to never offer writing new passwords to the cache.
+ -o _folder_
+ Opens the INBOX (or a folder specified via the -f argument) ReadOnly.
+ -p _pinerc_
+ Uses the named file as the personal configuration file instead of
+ _~/.pinerc_ or the default PINERC search sequence _PC-Alpine_ uses.
+ Pinerc may be either a local file or a remote configuration folder.
+ -P _pinerc_
+ Uses the named file as the system wide configuration file instead of
+ _/usr/local/lib/pine.conf_ on UNIX, or nothing on _PC-Alpine_. Pinerc
+ may be either a local file or a remote configuration folder.
+ -passfile _passfile_
+ This tells _Alpine_ what file should be used as the password file.
+ This should be a fully-qualified filename.
+ -pinerc _file_
+ Output fresh pinerc configuration to _file_, preserving the settings
+ of variables that the user has made. Use _file_ set to ``-'' to make
+ output go to standard out.
+ -r
+ Restricted Mode: For UNIX _Alpine_ only. _Alpine_ in restricted mode
+ can only send email to itself. Save and export are limited.
+ -registry _cmd_
+ For _PC-Alpine_ only, this option affects the values of _Alpine_'s
+ registry entries. Possible values for _cmd_ are set, noset, clear,
+ clearsilent, and dump. _Set_ will always reset _Alpine_'s registry
+ entries according to its current settings. _NoSet_ will never set any
+ values in the registry, but it will still use the values already set
+ in the registry. _Clear_ will clear the registry values. _Clearsilent_
+ will silently clear the registry values. _Dump_ will display the
+ values of current registry settings. Note that the dump command is
+ currently disabled. Without the -registry option, _PC-Alpine_ will
+ write values into the registry only if there currently aren't any
+ values set.
+ -sort _key_
+ Sort-Key: Specifies the order messages will be displayed in for the
+ FOLDER INDEX screen. _Key_ can have the following values: arrival,
+ date, subject, orderedsubj, thread, from, size, score, to, cc,
+ arrival/reverse, date/reverse, subject/reverse, orderedsubj/reverse,
+ thread/reverse, from/reverse, size/reverse, score/reverse,
+ to/reverse, and cc/reverse. The default value is "arrival". The _key_
+ value reverse is equivalent to arrival/reverse.
+ Configuration equivalent: _sort-key_.
+ -supported
+ Some options may or may not be supported depending on how _Alpine_
+ was compiled. This is a way to determine which options are supported
+ in the particular copy of _Alpine_ you are using.
+ -install
+ For _PC-Alpine_ only, this option removes references to Alpine in
+ Windows settings. The registry settings are removed and the password
+ cache is cleared.
+ -url _url_
+ Open the given URL.
+ -v
+ Version: Print version information to the screen.
+ -version
+ Version: Print version information to the screen.
+ -x _exceptions_config_
+ Configuration settings in the exceptions config override your normal
+ default settings. _Exceptions_config_ may be either a local file or a
+ remote pinerc folder.
+ -z
+ Enable Suspend: When run with this flag, the key sequence ctrl-z will
+ suspend the _Alpine_ session.
+ Configuration equivalent: _enable-suspend_ included in
+ _feature-list_.
+ -_option_=_value_
+ Assign _value_ to the config option _option_. For example,
+ _-signature-file=sig1_ or _-feature-list=signature-at-bottom_. (Note:
+ feature-list values are additive and features may be preceded with
+ no- to turn them off).
+
+Pico
+
+ The following command line options are supported in _Pico_:
+
+ +_n_
+ Causes _Pico_ to be started with the cursor located _n_ lines into
+ the file. (Note: no space between "+" sign and number)
+
+ -a
+ Display all files and directories, including those beginning with a
+ period (.).
+
+ -b
+ Enable the option to Replace text matches found using the "Where is"
+ command. This now does nothing. Instead, the option is always turned
+ on (as if the -b flag had been specified).
+
+ -d
+ Rebind the "delete" key so the character the cursor is on is rubbed
+ out rather than the character to its left.
+
+ -e
+ Enable file name completion.
+
+ -f
+ Use function keys for commands. _This option supported only in
+ conjunction with UW Enhanced NCSA telnet._
+
+ -g
+ Enable "Show Cursor" mode in file browser. Cause cursor to be
+ positioned before the current selection rather than placed at the
+ lower left of the display.
+
+ -k
+ Causes "Cut Text" command to remove characters from the cursor
+ position to the end of the line rather than remove the entire line.
+
+ -m
+ Enable mouse functionality. This only works when _Pico_ is run from
+ within an X Window System "xterm" window.
+
+ -n_n_
+ The -n_n_ option enables new mail notification. The _n_ argument is
+ optional, and specifies how often, in seconds, your mailbox is
+ checked for new mail. For example, -n60 causes _Pico_ to check for
+ new mail once every minute. The default interval is 180 seconds,
+ while the minimum allowed is 30. (Note: no space between "n" and the
+ number)
+
+ -o _dir_
+ Sets operating directory. Only files within this directory are
+ accessible. Likewise, the file browser is limited to the specified
+ directory subtree.
+
+ -p
+ Preserve the "start" and "stop" characters, typically Ctrl-Q and
+ Ctrl-S, which are sometimes used in communications paths to control
+ data flow between devices that operate at different speeds.
+
+ -q
+ TermdefWins. Termcap or terminfo escape sequences are used in
+ preference to default escape sequences.
+
+ -Q _quotestr_
+ Set the quote string. Especially useful when composing email, setting
+ this allows the quote string to be checked for when Justifying
+ paragraphs. A common quote string is "> ".
+
+ -r_n_
+ Sets column used to limit the "Justify" command's right margin.
+
+ -t
+ Enable "tool" mode. Intended for when _Pico_ is used as the editor
+ within other tools (e.g., Elm, Pnews). _Pico_ will not prompt for
+ save on exit, and will not rename the buffer during the "Write Out"
+ command.
+
+ -v
+ View the file only, disallowing any editing.
+
+ -version
+ Print version information.
+
+ -w
+ Disable word wrap (thus allow editing of long lines).
+
+ _Note: Pico will break any lines over 255 characters when reading a
+ file, regardless of word wrapping._
+
+ -x
+ Disable keymenu at the bottom of the screen.
+
+ -z
+ Enable ^Z suspension of _Pico_.
+
+Pilot
+
+ The following command line options are supported in _Pilot_:
+
+ -a
+ Display all files including those beginning with a period (.).
+
+ -f
+ Use function keys for commands. _This option supported only in
+ conjunction with UW Enhanced NCSA telnet._
+
+ -g
+ Enable "Show Cursor" mode. Cause cursor to be positioned before the
+ current selection rather than placed at the lower left of the
+ display.
+
+ -m
+ Enable mouse functionality. This only works when _Pilot_ is run from
+ within an X Window System "xterm" window.
+
+ -n_n_
+ The -n_n_ option enables new mail notification. The _n_ argument is
+ optional, and specifies how often, in seconds, your mailbox is
+ checked for new mail. For example, -n60 causes _Pilot_ to check for
+ new mail once every minute. The default interval is 180 seconds,
+ while the minimum allowed is 30. (Note: no space between "n" and the
+ number)
+
+ -o _dir_
+ Sets operating directory. Only files within the specified directory
+ are accessible and browsing is limited to the specified directory
+ subtree.
+
+ -v
+ Enable single vertical column display.
+
+ -x
+ Disable keymenu at the bottom of the screen.
+
+ -z
+ Enable ^Z suspension of _Pilot_.
+
+ Configuration and Preferences
+
+Alpine Configuration
+
+ There is very little in _Alpine_ which _requires_ compile-time
+ configuration. In most cases, the compiled-in preferences will suit users
+ and administrators just fine. When running _Alpine_ on a UNIX system, the
+ default built-in configuration can be changed by setting variables in the
+ system configuration files, /usr/local/lib/pine.conf or
+ /usr/local/lib/pine.conf.fixed. (Actually, these files can be changed using
+ the configure arguments --with-system-pinerc=VALUE or
+ --with-system-fixed-pinerc=VALUE.) The location of the pine.conf file can be
+ changed with the -P command line argument. Both _Alpine_ and _PC-Alpine_
+ also use personal (user-based) configuration files. On UNIX machines, the
+ personal configuration file is the file ~/.pinerc. For _PC-Alpine_ systems,
+ the personal configuration file is in $PINERC or <PineRC registry value> or
+ ${HOME}\ALPINE\PINERC or <ALPINE.EXE dir>\PINERC. Or the personal
+ configuration file can be specified with the -p command line argument.
+
+ All of these configuration files, other than the fixed system config
+ pine.conf.fixed on UNIX systems, may optionally be remote configuration
+ files instead of local files. This is discussed further in the following
+ section and in Remote Configuration.
+
+ After the personal configuration, _Alpine_ may optionally use a personal
+ exceptions configuration file which is specified with the command line
+ option "-x exceptions_config". "Exceptions_config" may also be either a
+ local file or a remote configuration folder. For Unix _Alpine_, if you don't
+ have a "-x" command line option, _Alpine_ will look for the file ".pinercex"
+ in the same local directory that the regular config file is located in. If
+ the regular config file is remote then Unix _Alpine_ looks in the home
+ directory for ".pinercex".
+
+ For _PC-Alpine_, if you don't have a "-x" command line option, _PC-Alpine_
+ will use the value of the environment variable $PINERCEX. If that is not
+ set, _PC-Alpine_ will look for the local file "PINERCEX" in the same local
+ directory that the regular config file is located in. If the regular config
+ file is remote then _PC-Alpine_ looks in the local directory specfied by the
+ "-aux local_directory" command line argument, or the directory
+ ${HOME}\ALPINE, or in <ALPINE.EXE directory>.
+
+ The syntax of a non-list configuration variable is this:
+
+ <variable> = <value>
+
+ If the value is absent then the variable is unset. To set a variable to the
+ empty value two double quotes (""). This is equivalent to an absent value
+ except that it overrides any system-wide default value that may be set.
+ Quotes may be used around any value. All values are strings and end at the
+ end of the line or the closing quote. Leading and trailing space is ignored
+ unless it is included in the quotes. There is one variable,
+ _use-only-domain-name_, for which the only appropriate values are _yes_ and
+ _no_. That's because it is a variable from the early days of _Alpine_ before
+ features existed.
+
+ There is also a second type of variable, lists. A list is a comma-separated
+ list of values. The syntax for a list is:
+
+ <variable> = <value> [, <value> , ... ]
+
+ A list can be continued on subsequent lines by beginning the line with
+ white-space. Both the per-user and global configuration files may contain
+ comments which are lines beginning with a #.
+
+ For UNIX _Alpine_, there are five ways in which each variable can be set. In
+ decreasing order of precedence they are:
+ 1. the system-wide _fixed_ configuration file
+ 2. a command line argument
+ 3. the personal exceptions file
+ 4. the personal configuration file
+ 5. the system-wide configuration file.
+
+ If the variable is not set in any of those places, there is a default
+ setting in the source code.
+
+ So, system-wide fixed settings always take precedence over command line
+ flags, which take precedence over per-user exception settings, which take
+ precedence over per-user settings, which take precedence over system-wide
+ configuration settings. _PC-Alpine_ has the same list, except that it does
+ not use a system-wide _fixed_ configuration file. This can be modified
+ slightly by using inheritance, which is covered below.
+
+ You may get a sample/fresh copy of the system configuration file by running
+ _alpine -conf_. The result will be printed on the standard output with very
+ short comments describing each variable. (The online help in the Setup
+ screens provides much longer comments.) If you need to fix some of the
+ configuration variables, you would use the same template for the fixed
+ configuration file as for the regular system-wide configuration file. (If it
+ isn't clear, the purpose of the fixed configuration file is to allow system
+ administrators to restrict the configurability of _Alpine_. It is by no
+ means a bullet-proof method.) _Alpine_ will automatically create the
+ personal configuration file the first time it is run, so there is no need to
+ generate a sample. _Alpine_ reads and writes the personal configuration file
+ occasionally during normal operation. Users will not normally look at their
+ personal configuration file, but will use the Setup screens from within
+ _Alpine_ to set the values in this file. If a user does add additional
+ comments to the personal configuration file they will be retained.
+
+ References to environment variables may be included in the _Alpine_
+ configuration files. The format is $variable or ${variable}. The character ~
+ will be expanded to the $HOME environment variable. For a more complete
+ explanation of how environment variables work, see the section Using
+ Environment Variables.
+
+ When environment variables are used for _Alpine_ settings which take lists,
+ you must have an environment variable set for each member of the list. That
+ is, _Alpine_ won't properly recognize an environment variable which is set
+ equal to a comma-delimited list. It is OK to reference unset environment
+ variables in the _Alpine_ configuration file, which will expand to nothing.
+
+ Remote and Local Configuration
+
+ There are two types of storage for configuration information. _Local_
+ configuration files are used by default. These are just regular files on the
+ UNIX system or on the PC. _Remote_ configuration folders are stored on an
+ IMAP server. The advantage of using a remote configuration is that the same
+ information may be accessed from multiple platforms. For example, if you use
+ one computer at work and another at home, the same configuration could be
+ used from both places. A configuration change from one place would be seen
+ in both places. Technical information about remote configuration is in
+ Remote Configuration.
+
+ Generic and Exceptional Configuration
+
+ If you use _Alpine_ from more than one platform it may be convenient to
+ split your configuration information into two pieces, a generic piece and
+ exceptions which apply to a particular platform. For example, suppose you
+ use _Alpine_ from home and from work. Most of your configuration settings
+ are probably the same in both locations, so those settings belong in the
+ generic settings configuration. However, you may use a different SMTP server
+ and INBOX from home than you do from work. The "smtp-server" and
+ "inbox-path" variables could be part of your exceptional configuration so
+ that they could be different in the two places.
+
+ You can use the command line option "-x config" to split your configuration
+ into generic and exceptional pieces. Config may be either local or remote.
+
+ For most people, splitting the configuration information into two pieces is
+ only going to be useful if the generic information is accessed remotely. If
+ you already have a local pinerc file with settings you like you may find
+ that the command Setup/RemoteConfigSetup will be useful in helping you
+ convert to a remote configuration. The command line flag copy_pinerc may
+ also be useful.
+
+ Configuration Inheritance
+
+ Configuration inheritance is a power user feature. It is confusing and not
+ completely supported by the configuration user interface.
+
+ For configuration variables which are lists, like "smtp-server" or
+ "incoming-folders", the inheritance mechanism makes it possible to _combine_
+ the values of options from different configuration locations instead of
+ _replacing_ the value. Configuration Inheritance has more information about
+ how inheritance is used.
+ _________________________________________________________________
+
+General Configuration Variables
+
+ The following is a list of all _Alpine_ configuration variables, in
+ alphabetical order. Note that not all variables apply to all versions of
+ _Alpine_ and that some variables are only applicable in a system
+ configuration file and some are only applicable in a personal configuration
+ file. These are configuration _variables_. Configuration Features are in a
+ separate section.
+
+ _addrbook-sort-rule_
+ This variable sets up the default address book sorting. Currently,
+ _Alpine_ will accept the values _dont-sort_,
+ _fullname-with-lists-last_, _fullname_, _nickname-with-lists-last_, and
+ _nickname_. The default is to sort by fullname with lists last. If
+ you use an address book from more than one computer and those
+ computers sort the address book differently then the sort order will
+ be the order where the last change to the address book was made.
+ There are two reasons the sorting might be different on different
+ systems. First, the addrbook-sort-rule may be set differently in the
+ two places. Second, the collation rules on the two computers may be
+ different. For example, one system might ignore special characters
+ while the other doesn't or one may sort upper and lower case letters
+ together while the other doesn't. In any case, the order you see is
+ the order on the system where the last change was made, for example
+ by an address book edit or a Take Address command.
+ This option is displayed as "Addressbook Sort Rule".
+ _address-book_
+ A list of personal address books. Each entry in the list is an
+ optional nickname followed by a pathname or file name relative to the
+ home directory. The nickname is separated from the rest of the line
+ with whitespace. Instead of a local pathname or file name, a remote
+ folder name can be given. This causes the address book to be a Remote
+ address book. Remote folder syntax is discussed in Syntax for Remote
+ Folders. This list of address books will be combined with the
+ global-address-book list to arrive at the complete set of address
+ books.
+ _addressbook-formats_
+ This option specifies the format that address books are displayed in.
+ By default, address books are displayed with the nicknames in the
+ first column, the fullnames in the second column, and addresses in
+ the third column. The system figures out reasonable defaults for the
+ widths of the columns. An address book may be given a different
+ format by listing special tokens in the order you want them to
+ display. The possible tokens are NICKNAME, FULLNAME, ADDRESS, FCC,
+ and COMMENT. More details are included in the online help for this
+ variable.
+ _alt-addresses_
+ This option provides a place for you to list alternate email
+ addresses you may have. Each address in the list should be the actual
+ email address part of an address, without the full name field or the
+ angle brackets. For example:
+
+ user@example.com
+ The matching is case-insensitive, so this would match any of
+ User@example.com, user@Example.Com, or USER@EXAMPLE.COM as well.
+ If set, the option affects the behavior of the Reply command and the
+ "+" symbol in the MESSAGE INDEX, which denotes that a message has
+ been addressed specifically to you.
+ In the default INDEX display the personal name (or email address) of
+ the person listed in the message's "From:" header field is usually
+ displayed except when that address is yours or one of your alternate
+ addresses. In that case you will usually see the name of the first
+ person specified in the message's "To:" header field with the prefix
+ "To: " prepended.
+ With respect to Reply, the reply-to-all option will exclude addresses
+ listed here.
+ The feature copy-to-address-to-from-if-it-is-us is somewhat related
+ to this option.
+ In addition to a list of actual addresses, you may use regular
+ expressions (as used with egrep with the ignore case flag) to
+ describe the addresses you want to match. _Alpine_ will somewhat
+ arbitrarily interpret your entry as a regular expression if it
+ contains any of the characters *, |, +, ?, {, [, ^, $, or \.
+ Otherwise, it will be treated literally. The feature
+ disable-regular-expression-matching-for-alternate-addresses may be
+ used to turn off regular expression processing regardless of whether
+ or not special characters appear in the entry.
+ A description of how regular expressions work is beyond the scope of
+ this help text, but some examples follow.
+ The entry
+
+ .*@example.com
+ in the alt-addresses list would mean that any address with a domain
+ name of example.com (such as fred@example.com or wilma@example.com)
+ will be considered one of your alternate addresses. Strictly
+ speaking, the dot in example.com ought to be escaped with a
+ backslash, as in example\.com, and a dollar sign anchor ought to come
+ at the end of the expression to prevent a match of example.com.org.
+ Complicating things further, the dollar sign is special in the
+ _Alpine_ configuration (it signifies environment variable expansion)
+ so the dollar sign should be doubled or backslash escaped for
+ _Alpine_'s sake. Quotes around the whole expression will not escape the
+ dollar sign successfully. So this example should look like
+
+ .*@example\.com$$
+ The entry
+
+ ^fred[0-9]*@example.com$$
+ would match fred3@example.com or fred17@example.com as well as
+ fred@example.com.
+ You could match all addresses that look like fred+stuff@example.com
+ for any value of stuff with the entry
+
+ ^fred\+.*@example.com$$
+ Notice that you have to escape the plus sign with a backslash because
+ plus is a special character in regular expressions. If you wanted to
+ match plain fred as well as fred+stuff the expression
+
+ ^fred(()|\+.*)@example.com$$
+ would do it, but it would be easier to just add fred@example.com as a
+ separate entry.
+ One more example, a match of all first-level subdomains, is given by
+
+ ^fred@[[:alnum:]_-]*\.example\.com$$
+ Because the regular expression matching is based on an old library
+ (hs_regex) the regular expressions might not work exactly as you
+ expect, but they should be close.
+ This option is displayed as "Alternate Addresses".
+ _bugs-additional-data_
+ System-wide configuration files only. Program/Script used by _Report
+ Bug_ command. Output from the program/script is captured and attached
+ to the bug report.
+ _bugs-fullname_, _bugs-address_, _local-fullname_, _local-address_,
+ _suggest-fullname_, and _suggest-address_
+ System-wide configuration files only. These are used by the bug
+ report commands which can be accessed from some of the Help screens.
+ _busy-cue-rate_
+ When _Alpine_ is delayed for some reason it usually shows that
+ something is happening with a small animated display in the status
+ message line near the bottom of the screen. This option sets how
+ frequently the characters (for example, a spinning bar) in the active
+ status message lines are updated. At most, it can be set to be
+ udpated 20 times per second.
+ Setting this value to zero will prevent display of the animations
+ altogether.
+ The option busy-cue-spinner-only can be used to remove the randomness
+ from this animated display.
+ _character-set_
+ This is now obsolete, replaced by three separate variables:
+ _display-character-set_, _keyboard-character-set_, and
+ _posting-character-set_. See the section on International Character
+ Sets for more details.
+ _color-style_
+ UNIX _Alpine_ only (color is automatically on with _PC-Alpine_). If
+ the terminal or terminal emulator you are using is capable of
+ displaying colors, this variable controls whether or not color will
+ be used in _Alpine_. If you turn color on and things are set up
+ correctly, you should see color appear on the screen immmediately.
+ Modern terminal emulators are usually capable of displaying colors.
+ This variable may be set to any of the following values:
+
+ no-color
+ Don't use color.
+
+ use-termdef
+ In order to decide if your terminal is capable of color,
+ _Alpine_ looks in the terminal capabilities database, TERMINFO
+ or TERMCAP, depending on how _Alpine_ was compiled. This is a
+ good option to choose if you switch between a color and a
+ non-color terminal with the same _Alpine_ configuration.
+ _Alpine_ will know to use color on the color terminal because it
+ is described in the termcap entry, and _Alpine_ will know to
+ use black and white on the non-color terminal. Color Details
+ has more information about configuring a termcap entry for
+ color. This is usually something a system administrator does.
+
+ force-ansi-8color
+ Because setting up a termcap entry is confusing and because the
+ terminal capabilities database is often not correctly
+ configured for color, this choice and the next may be easier
+ for you to use. If your terminal emulator responds to ANSI
+ color escape sequences, which many do, this option will cause
+ _Alpine_ to believe your terminal will respond to the escape
+ sequences which produce eight different foreground and
+ background colors. The escape sequences used to set the
+ foreground colors are
+
+ ESC [ 3 <color_number> m
+
+ where the color_number is an ASCII digit between 0 and 7. The
+ numbers 0 through 7 should correspond to the colors black, red,
+ green, yellow, blue, magenta, cyan, and white. Some terminal
+ emulators use a pre-ANSI scheme which swaps the colors blue and
+ red and the colors yellow and cyan. This will cause the default
+ colors to be different, but other than that things should work
+ fine. There is also a 9th color available, the last one shown,
+ which is the default color from the terminal emulator. When
+ used as a background color some people refer to this color as
+ "transparent", which is why the letters "TRAN" are shown in the
+ color swatch of the SETUP COLOR screen. The foreground
+ transparent color is shown as the color of the "TRAN" text.
+ (The transparent color will not work correctly in a PC-Alpine
+ configuration.) The escape sequences used to set the background
+ colors are the same as for the foreground colors except a "4"
+ replaces the "3".
+
+ Note: With the Tera Term terminal emulator this setting works
+ well. You should also have the Tera Term "Full color" option
+ turned OFF. You may find the "Full color" option in Tera Term's
+ "Setup" menu, in the "Window" submenu.
+
+ force-ansi-16color
+ Many terminal emulators know about the same eight colors above
+ plus eight more. This option attempts to use all 16 colors. The
+ same escape sequences as for the eight-color terminal are used
+ for the first eight colors. The escape sequences used to set
+ foreground colors 8-15 are the same as for 0-7 except the "3"
+ is replaced with a "9". The background color sequences for
+ colors 8-15 are the same as for 0-7 except the "4" is replaced
+ with "10". You can tell if the 16 colors are working by turning
+ on this option and then going into one of the color
+ configuration screens, for example, the configuration screen
+ for Normal Color. If you see 16 different colors to select from
+ (plus a 17th for the transparent color), it's working.
+
+ force-xterm-256color
+ Some versions of xterm (and some other terminal emulators) have
+ support for 256 colors. The escape sequences used to set the
+ foreground colors are
+
+ ESC [ 38 ; 5 ; <color_number> m
+
+ where the color_number is an ASCII digit between 0 and 255.
+ Background colors are the same with the 38 replaced with a 48.
+ The numbers 0 through 15 are probably similar to the 16 color
+ version above, then comes a 6x6x6 color cube, followed by 24
+ colors of gray. The terminal default (transparent) color is the
+ 257th color at the bottom. Some terminal emulators will
+ misinterpret these escape sequences causing the terminal to
+ blink or overstrike characters or to do something else
+ undesirable.
+
+ The PuTTY terminal emulator has an option called "Allow
+ terminal to use xterm 256-colour mode" which allows PuTTY to
+ work well with this 256-color setting.
+
+ There are two other possible color values which may be useful in some
+ situations. In the color configuration screens there will sometimes
+ be a color which has the label "NORM" inside its color swatch. If
+ this is selected the corresponding foreground or background Normal
+ Color will be used. Another similar color is the one that has the
+ label "NONE" inside its color swatch. The meaning of this setting is
+ that no color changing will be done. This NONE color is only useful
+ in contexts where _Alpine_ is already coloring the text some color
+ other than the Normal Color. For example, if the Reverse Color is set
+ then the current line in the MESSAGE INDEX will be colored. If one of
+ the index symbols (for example, the Index-to-me Symbol) has the NONE
+ color as its background then the symbol's foreground color will be
+ used to draw the actual text but the background color will be the
+ same as whatever the background color already was. The color values
+ which end up in the configuration file for these special values are
+ the 11-character words "norm-padded", "none-padded", and
+ "transparent".
+ The normal default is "no-color".
+ Once you've turned on color you may set the colors of many objects on
+ the screen individually. The Color Configuration section has more
+ information, or you may just try it by running the "Setup" command
+ and typing "K" for Kolor to enter the color configuration screen
+ (Kolor instead of Color because C means Config). Most categories of
+ color which _Alpine_ supports are configurable there. Index line
+ color is configured separately.
+ _composer-word-separators_
+ This option affects how a "word" is defined in the composer. The
+ definition of a word is used when using the Forward Word and Backward
+ Word commands in the composer, as well as when using the spell
+ checker. Whitespace is always considered a word separator.
+ Punctuation (like question marks, periods, commas, and so on) is
+ always a word separator if it comes at the end of a word. By default,
+ a punctuation character which is in the middle of a word does not
+ break up that word as long as the character before and the character
+ after it are both alphanumeric. If you add a character to this option
+ it will be considered a word separator even when it occurs in the
+ middle of an alphanumeric word. For example, if you want to skip
+ through each part of an address instead of skipping the whole address
+ at once you might want to include"@" and "." in this list. If you
+ want the word-skipper to stop on each part of a UNIX filename you
+ could add "/" to the list. The equal sign and dash are other
+ possibilities you might find helpful.
+ _composer-wrap-column_
+ This option specifies an aspect of _Alpine_'s Composer. This gives
+ the maximum width that auto-wrapped lines will have. It's also the
+ maximum width of lines justified using the ^J Justify command. The
+ normal default is _74_. The largest allowed setting is normally _80_
+ in order to prevent very long lines from being sent in outgoing mail.
+ When the mail is actually sent, trailing spaces will be stripped off
+ of each line.
+ _current-indexline-style_
+ current-indexline-style.
+ _customized-hdrs_
+ You may add your own custom headers to outgoing messages. Each header
+ you specify here must include the header tag (Reply-To:, Approved:,
+ etc.) and may optionally include a value for that header. If you want
+ to see these custom headers each time you compose a message, you must
+ add them to your default-composer-hdrs list, otherwise they become
+ part of the rich header set which you only see when you press the
+ rich header command. (If you are looking for a way to change which
+ headers are _displayed_ when you view a message, take a look at the
+ viewer-hdrs option instead.) Here's an example which shows how you
+ might set your From address
+
+ From: Full Name <user@example.com>
+ and another showing how you might set a Reply-To address
+
+ Reply-To: user@example.com
+ You may also set non-standard header values here. For example, you
+ could add
+
+ Organization: My Organization Name
+ or even
+
+ X-Favorite-Colors: Purple and Gold
+ If you include a value after the colon then that header will be
+ included in your outgoing messages unless you delete it before
+ sending. If a header in the Customized-Headers list has only a tag
+ but no value, then it will not be included in outgoing messages
+ unless you edit a value in manually. For example, if
+
+ Reply-To:
+ is in the list, then the Reply-To header will be available for
+ editing but won't be included unless a value is added while in the
+ composer.
+ It's actually a little more complicated than that. The values of
+ headers that you set with the Customized-Headers option are defaults.
+ If the message you are about to compose already has a value for a
+ header, that value is used instead of a value from your
+ Customized-Headers. For example, if you are Replying to a message the
+ Subject field will already be filled in. In that case, if the
+ Customized-Headers list contains a Subject line, the custom subject
+ will _NOT_ be used. The subject derived from the subject of the
+ message you are Replying to will be used instead.
+ It is also possible to make header setting even more complicated and
+ more automatic by using Roles, but if all you want to do is set a
+ default value for a header, you don't need to think about Roles.
+ If you change your From address you may also find it useful to add
+ the changed From address to the alt-addresses configuration option.
+ Limitation: Because commas are used to separate the list of
+ Customized-Headers, it is not possible to have the value of a header
+ contain a comma. Nor is there currently an "escape" mechanism
+ provided to make this work.
+ This option is displayed as "Customized Headers".
+ _dead-letter-files_
+ This option affects _Alpine_'s behavior when you cancel a message
+ being composed. _Alpine_'s usual behavior is to write the canceled
+ message to a file named "dead.letter" in your home directory, or
+ "DEADLETR" when using _PC-Alpine_, overwriting any previous message.
+ If you set this option to a value higher than one, then that many
+ copies of dead letter files will be saved. For example, if you set
+ this option to "3" then you may have files named "DEADLETR",
+ "DEADLETR2", and "DEADLETR3"; or "dead.letter", "dead.letter2", and
+ "dead.letter3". In this example, the most recently cancelled message
+ will be in "dead.letter", and the third most recently cancelled
+ message will be in "dead.letter3". The fourth most recently cancelled
+ message will no longer be saved.
+ If you set this option to zero, then NO record of canceled messages
+ is maintained.
+ If the feature Quell-Dead-Letter-On-Cancel is set, that overrides
+ whatever you set for this option. If this option had existed at the
+ time, then the Quell feature would not have been added, but it is
+ still there for backwards compatibility. So, in order for this option
+ to have the desired effect, make sure the Quell feature is turned
+ off.
+ _default-composer-hdrs_
+ You can control which headers you want visible when composing
+ outgoing email using this option. You can specify any of the regular
+ set, any Rich Header, or any Customized-Hdrs which you have already
+ defined. If you use this setting at all, you must specify all the
+ headers you want to see, you can't just add to the regular header
+ set. The default set is To:, Cc:, Attchmnt:, and Subject:.
+ Note that the "Newsgroups:" header will be abbreviated in the
+ Composer display, but should be spelled out in full here.
+ This option is displayed as "Default Composer Headers".
+ _default-fcc_
+ The name of the folder to which all outgoing mail goes is set here.
+ The compiled-in default is _sent-mail_ (UNIX) or _sentmail_ (PC). It
+ can be set to "" (two double quotes with nothing between them) to
+ turn off saving copies of outgoing mail. If _default-fcc_ is a
+ relative file name, then it is relative to your default collection
+ for saves (see folder-collections).
+ This option is displayed as "Default Fcc (File carbon copy)".
+ _default-saved-msg-folder_
+ This option determines the default folder name for _Saves_... If this
+ is not a path name, it will be in the default collection for saves.
+ Any valid folder specification, local or IMAP, is allowed. This
+ default folder only applies when the saved-msg-name-rule doesn't
+ override it. Unix _Alpine_ default is normally _saved-messages_ in
+ the default folder collection. _PC-Alpine_ default is _SAVEMAIL_
+ (normally stored as _SAVEMAIL.MTX_).
+ This option is displayed as "Default Saved Message Folder".
+ _disable-these-authenticators_
+ This variable is a list of SASL (Simple Authentication and Security
+ Layer) authenticators which will be disabled. SASL is a mechanism for
+ authenticating to IMAP, POP3, SMTP, and other network servers.
+ _Alpine_ matches its list of supported authenticators with the server
+ to determine the most secure authenticator that is supported by both.
+ If no matching authenticators are found, _Alpine_ will revert to
+ plaintext login (or, in the case of SMTP, will be unable to
+ authenticate at all).
+ The candidates for disabling are listed below. There may be more if
+ you compile _Alpine_ with additional authenticators and/or a newer
+ version of the c-client library.
+ + GSSAPI
+ + CRAM-MD5
+ + PLAIN
+ + LOGIN
+ Normally, you will not disable any authenticators. There are two
+ exceptions:
+ 1. You use a broken server that advertises an authenticator, but does
+ not actually implement it.
+ 2. You have a Kerberos-capable version of _Alpine_ and the server is
+ also Kerberos-capable, but you can not obtain Kerberos credentials
+ on the server machine, thus you desire to disable GSSAPI (which in
+ turn disables _Alpine_'s Kerberos support).
+ It is never necessary to disable authenticators, since _Alpine_ will
+ try other authenticators before giving up. However, disabling the
+ relevant authenticator avoids annoying error messages.
+ _disable-these-drivers_
+ This variable is a list of mail drivers which will be disabled. The
+ candidates for disabling are listed below. There may be more in the
+ future if you compile _Alpine_ with a newer version of the c-client
+ library.
+ + mbox
+ + mbx
+ + mh
+ + mix
+ + mmdf
+ + mtx
+ + mx
+ + news
+ + phile
+ + tenex
+ + unix
+ The _mbox_ driver enables the following behavior: if there is a file
+ called mbox in your home directory, and if that file is either empty
+ or in Unix mailbox format, then every time you open _INBOX_ the _mbox_
+ driver will automatically transfer mail from the system mail spool
+ directory into the mbox file and delete it from the spool directory.
+ If you disable the _mbox_ driver, this will not happen.
+ It is not recommended to disable the driver which supports the system
+ default mailbox format. On most non-SCO systems, that driver is the
+ _unix_ driver. On most SCO systems, it is the _mmdf_ driver. The
+ system default driver may be configured to something else on your
+ system; check with your system manager for additional information.
+ It is most likely not very useful for you to disable any of the
+ drivers other than possibly _mbox_. You could disable some of the
+ others if you know for certain that you don't need them but the
+ performance gain in doing so is very modest.
+ _display-character-set_
+ See the discussion in International Character Sets for details.
+ _display-filters_
+ This option defines a list of text-filtering commands (programs or
+ scripts) that may be used to filter text portions of received
+ messages prior to their use (e.g., presentation in the "Message Text"
+ display screen). For security reasons, the full path name of the
+ filter command must be specified.
+ Display filters do not work with _PC-Alpine_.
+ The command is executed and the message is piped into its standard
+ input. The standard output of the command is read back by _Alpine_.
+ The __TMPFILE__ token (see below) overrides this default behavior.
+ The filter's use is based on the configured _trigger_ string. The
+ format of a filter definition is:
+
+ <trigger> <command> <arguments>
+ You can specify as many filters as you wish, separating them with a
+ comma. Each filter can have only one trigger and command. Thus, two
+ trigger strings which invoke the same command require separate filter
+ specifications.
+ The _trigger_ is simply text that, if found in the message, will
+ invoke the associated command. If the trigger contains any space
+ characters, it must be placed within quotes. Likewise, should you
+ wish a filter to be invoked unconditionally, define the trigger as
+ the null string, "" (two consecutive double-quote characters). If the
+ trigger string is found anywhere in the text of the message the
+ filter is invoked. Placing the trigger text within the tokens defined
+ below changes where within the text the trigger must be before
+ considering it a match.
+ Trigger Modifying Tokens:
+
+ __CHARSET(string)__
+ This token tells _Alpine_ to invoke the supplied command if the
+ text is in a character set matching string (e.g., ISO-8859-2 or
+ ISO-2022-JP).
+
+ __LEADING(string)__
+ This token tells _Alpine_ to invoke the supplied command if the
+ enclosed string is found to be the first non-whitespace text.
+ NOTE: Quotes are necessary if string contains the space
+ character.
+
+ __BEGINNING(string)__
+ This token tells _Alpine_ to invoke the supplied command if the
+ enclosed string is found at the beginning of any line in the
+ text.
+ NOTE: Quotes are necessary if string contains the space
+ character.
+
+ The "command" and "arguments" portion is simply the command line to
+ be invoked if the trigger string is found. Below are tokens that
+ _Alpine_ will recognize and replace with special values when the
+ command is actually invoked.
+ Command Modifying Tokens:
+
+ __TMPFILE__
+ When the command is executed, this token is replaced with the
+ path and name of the temporary file containing the text to be
+ filtered. _Alpine_ expects the filter to replace this data with
+ the filter's result. NOTE: Use of this token implies that the
+ text to be filtered is not piped into standard input of the
+ executed command and its standard output is ignored. _Alpine_
+ restores the tty modes before invoking the filter in case the
+ filter interacts with the user via its own standard input and
+ output.
+
+ __RESULTFILE__
+ When the command is executed, this token is replaced with the
+ path and name of a temporary file intended to contain a status
+ message from the filter. _Alpine_ displays this in the message
+ status field.
+
+ __DATAFILE__
+ When the command is executed, this token is replaced with the
+ path and name of a temporary file that _Alpine_ creates once
+ per session and deletes upon exit. The file is intended to be
+ used by the filter to store state information between instances
+ of the filter.
+
+ __PREPENDKEY__
+ When the command is executed, this token indicates that a
+ random number will be passed down the input stream before the
+ message text. This number could be used as a session key. It
+ does not appear as a command-line argument. It is sent in this
+ way to improve security. The number is unique to the current
+ _Alpine_ session and is only generated once per session.
+
+ The feature disable-terminal-reset-for-display-filters is related.
+ Performance caveat/considerations:
+ Testing for the trigger and invoking the filter doesn't come for
+ free. There is overhead associated with searching for the trigger
+ string, testing for the filter's existence and actually piping the
+ text through the filter. The impact can be reduced if the Trigger
+ Modifying Tokens above are employed.
+ Limitation:
+ If Header Colors are being used, the sequences of bytes which
+ indicate color changes will be contained in the text which is passed
+ to the display-filter. If this causes problems you'll need to turn
+ off Header Colors. The thirteen bytes which indicate a color change
+ are the character \377 followed by \010 for a foreground color or
+ \011 for a background color. Then comes eleven characters of RGB data
+ which looks something like 255, 0,255, depending on the particular
+ color, of course.
+ _download-command_
+ This option affects the behavior of the _Export_ command. It
+ specifies a Unix program name, and any necessary command line
+ arguments, that _Alpine_ can use to transfer the exported message to
+ your personal computer's disk.
+ _download-command-prefix_
+ This option is used in conjunction with the _download-command_
+ option. It defines text to be written to the terminal emulator (via
+ standard output) immediately prior to starting the download command.
+ This is useful for integrated serial line file transfer agents that
+ permit command passing (e.g., Kermit's APC method).
+ _editor_
+ UNIX _Alpine_ only. Sets the name of the alternate editor for
+ composing mail (message text only, not headers). It will be invoked
+ with the "^_" command or it will be invoked automatically if the
+ enable-alternate-editor-implicitly feature is set.
+ _empty-header-message_
+ When sending, if both the To and Cc fields are empty and you are
+ sending the message to a Bcc, _Alpine_ will put a special address in
+ the To line. The default value is "undisclosed-recipients: ;". The
+ reason for this is to avoid embarrassment caused by some Internet
+ mail transfer software that interprets a "missing" To: header as an
+ error and replaces it with an Apparently-to: header that may contain
+ the addresses you entered on the Bcc: line, defeating the purpose of
+ the Bcc. You may change the part of this message that comes before
+ the ": ;" by setting the _empty-header-message_ variable to something
+ else.
+ _fcc-name-rule_
+ Determines default folder name for fcc when composing. Currently,
+ _Alpine_ will accept the values _default-fcc_, _by-recipient_, or
+ _last-fcc-used_. If set to _default-fcc_, then _Alpine_ will use the
+ value defined in the default-fcc variable (which itself has a
+ default) for the Fcc header field. If set to _by-recipient_, then
+ _Alpine_ will use the name of the recipient as a folder name for the
+ fcc. The relevant recipient is the first address in the To field. If
+ set to "last-fcc-used", then _Alpine_ will offer to Fcc to whatever
+ folder you used previously. In all cases, the field can still be
+ edited after it is initially assigned. If the fcc field in the
+ address book is set for the first To address, that value over-rides
+ any value derived from this rule.
+ _feature-list_
+ This is a list of the many features (options) which may be turned on
+ or off. There is a separate section titled Configuration Features
+ which explains each of the features. There is some additional
+ explanation about the _feature-list_ variable itself in Feature List
+ Variable.
+ _file-directory_
+ _PC-Alpine_ only. This value affects the Composer's "^J Attach"
+ command, the Attachment Index Screen's "S Save" command, and the
+ Message Index's "E Export" command.
+ Normally, when a filename is supplied that lacks a leading "path"
+ component, _Alpine_ assumes the file exists in the user's home
+ directory. Under Windows operating systems, this definition isn't
+ always clear. This feature allows you to explictly set where _Alpine_
+ should look for files without a leading path.
+ NOTE: this feature's value is ignored if either use-current-dir
+ feature is set or the PINERC has a value for the operating-dir
+ variable.
+ _folder-collections_
+ This is a list of one or more collections where saved mail is stored.
+ See the sections describing folder collections and collection syntax
+ for more information. The first collection in this list is the
+ default collection for _Save_s, including default-fcc's.
+ _folder-extension_
+ _PC-Alpine_ only. File extension used for local folder names. This is
+ .MTX by default.
+ _folder-reopen-rule_
+ _Alpine_ normally checks for new mail in the currently open folder
+ and in the INBOX every few minutes.
+ There are some situations where automatic new-mail checking does not
+ work. For example, if a mail folder is opened using the POP protocol
+ or a newsgroup is being read using the NNTP protocol, then new-mail
+ checking is disabled.
+ It may be possible to check for new mail in these cases by reopening
+ the folder. _Alpine_ does not do this for you automatically, but you
+ may do the commands manually to cause this to happen. You reopen by
+ going back to the folder list screen from the message index screen
+ with the "<" command, and then going back into the message index
+ screen with the ">" command. (Actually, any method you would normally
+ use to open a folder will work the same as the "<" followed by ">"
+ method. For example, the GoTo Folder command will work, or you may
+ use L to go to the Folder List screen and Carriage Return to reopen
+ the folder.)
+ There are some cases where _Alpine_ knows that reopening the folder
+ should be useful as a way to discover new mail. At the time of this
+ writing, connections made using the POP protocol, news reading using
+ the NNTP protocol, local news reading, and local ReadOnly folders
+ which are in the traditional UNIX or the MMDF format all fall into
+ this category. There are other cases where it _may_ be a way to
+ discover new mail, but _Alpine_ has no way of knowing, so it might
+ also just be an exercise in futility. All remote, ReadOnly folders
+ other than those listed just above fall into this category. The
+ setting of this option together with the type of folder controls how
+ _Alpine_ will react to the apparent attempt to reopen a folder.
+ If you don't reopen, then you will just be back in the message index
+ with no change. You left the index and came back, but the folder
+ remained "open" the whole time. However, if you do reopen the folder,
+ the folder is closed and then reopened. In this case, the current
+ state of the open folder is lost. The New status, Important and
+ Answered flags, selected state, Zoom state, collapsed or expanded
+ state of threads, current message number, and any other temporary
+ state is all lost when the reopen happens. For POP folders (but not
+ NNTP newsgroups) the Deleted flags are also lost.
+ In the possibilities listed below, the text says "POP/NNTP" in
+ several places. That really implies the case where _Alpine_ knows it
+ is a good way to discover new mail, which is more than just POP and
+ NNTP, but POP and NNTP are the cases of most interest. This option
+ probably has more possible values than it deserves. They are:
+
+ Always reopen
+ _Alpine_ will not ask whether you want to reopen but will just
+ do the reopen whenever you type a command that implies a
+ reopen, regardless of the access method. In other words, it is
+ assumed you would always answer Yes if asked about reopening.
+
+ Yes for POP/NNTP, Ask about other remote [Yes]
+ _Alpine_ will assume a Yes answer if the access method is POP
+ or NNTP, but will ask you whether to reopen other remote
+ folders, with a default answer of Yes.
+
+ Yes for POP/NNTP, Ask about other remote [No]
+ _Alpine_ will assume a Yes answer if the access method is POP
+ or NNTP, but will ask you whether to reopen other remote
+ folders, with a default answer of No.
+
+ Yes for POP/NNTP, No for other remote
+ _Alpine_ will assume a Yes answer if the access method is POP
+ or NNTP, and will assume a No answer for all other remote
+ folders.
+
+ Always ask [Yes]
+ _Alpine_ will not differentiate based on access method. It will
+ always ask for all remote folders, with a default answer of
+ Yes.
+
+ Always ask [No]
+ _Alpine_ will not differentiate based on access method. It will
+ always ask for all remote folders, with a default answer of No.
+
+ Ask about POP/NNTP [Yes], No for other remote
+ _Alpine_ will ask if the access method is POP or NNTP, with a
+ default answer of Yes. It will never attempt to reopen other
+ remote folders.
+
+ Ask about POP/NNTP [No], No for other remote
+ This is the default. _Alpine_ will ask if the access method is
+ POP or NNTP, with a default answer of No. It will never attempt
+ to reopen other remote folders.
+
+ Never reopen
+ _Alpine_ will never attempt to reopen already open folders.
+
+ Remember, wherever it says POP or NNTP above it really means POP or
+ NNTP or any of the other situations where it is likely that reopening
+ is a good way to discover new mail.
+ There is an alternative that may be of useful in some situations.
+ Instead of manually checking for new mail you can set up a Mail Drop
+ and automatically check for new mail.
+ _folder-sort-rule_
+ This option controls the order in which folder list entries will be
+ presented in the FOLDER LIST screen. Choose one of the following:
+
+ _Alphabetical_
+ sort by alphabetical name independent of type
+
+ _Alpha-with-dirs-last_
+ sort by alphabetical name grouping directory entries to the end
+ of the list
+
+ _Alpha-with-dirs-first_
+ sort by alphabetical name grouping directory entries to the
+ start of the list
+
+ The normal default is _Alphabetical_.
+ _font-name_
+ Winsock version of _PC-Alpine_ only.
+ _font-size_
+ Winsock version of _PC-Alpine_ only.
+ _font-style_
+ Winsock version of _PC-Alpine_ only.
+ _forced-abook-entry_
+ System-wide _Alpine_ configuration files only. Force these address
+ book entries into all writable personal address books. This is a list
+ variable. Each item in the list has the form:
+
+ Nickname | Fullname | Address
+ with optional whitespace in all the obvious places.
+ _form-letter-folder_
+ A Form Letter Folder is a mail folder that is intended to contain
+ messages that you have composed and that are intended to be sent in
+ their original form repeatedly.
+ Setting this variable will alter _Alpine_'s usual behavior when you
+ execute the Compose command. Normally, _Alpine_ offers a chance to
+ continue a postponed or interrupted message should one or the other
+ exist. When this variable is set to a folder name that exists,
+ _Alpine_ will also offer the chance to select a message from the
+ folder to insert into the composer, much like when continuing a
+ postponed message. The difference, however, is that _Alpine_ will not
+ automatically delete the selected message from the Form Letter
+ Folder.
+ Setting this variable will also affect _Alpine_'s behavior when you
+ Postpone a message from the composer. Normally, _Alpine_ simply
+ stashes the message away in your Postponed-Folder. Regardless of the
+ specified folder's existence, _Alpine_ will ask which folder you
+ intend the message to be stored in. Choose the "F" option to store
+ the message in your Form Letter Folder. This is the most common way
+ to add a message to the folder.
+ Another method of adding messages to the folder is via the _Alpine_
+ composer's Fcc: field. If you are sending a message that you expect
+ to send in the same form again, you can enter the Form Letter
+ Folder's name in this field. _Alpine_, as usual, will copy the
+ message as it's sent. Note, when you later select this message from
+ your Form Letter Folder, it will have the same recipients as the
+ original message.
+ To delete a message from the Form Letter Folder, you can either
+ select the folder from a suitable FOLDER LIST screen, or use the
+ Delete command in the MESSAGE INDEX offered when selecting from the
+ folder as part of the Compose command. You can delete a Form Letter
+ Folder just as any other folder from a suitable FOLDER LIST screen.
+ You may find that the Roles facility can be used to replace the Form
+ Letter Folder.
+ _global-address-book_
+ A list of shared address books. Each entry in the list is an optional
+ nickname followed by a pathname or file name relative to the home
+ directory. A SPACE character separates the nickname from the rest of
+ the line. Instead of a local pathname or file name, a remote folder
+ name can be given. This causes the address book to be a Remote
+ address book. Remote folder syntax is discussed in Syntax for Remote
+ Folders. This list will be added to the address-book list to arrive
+ at the complete set of address books. Global address books are
+ defined to be ReadOnly.
+ _goto-default-rule_
+ This value affects _Alpine_'s behavior when using the _Goto_ command.
+ There are five possible values for this option:
+
+ _folder-in-first-collection_
+ _Alpine_ will offer the most recently visited folder in the
+ default collection found in the "Collection List" screen as the
+ default.
+
+ _inbox-or-folder-in-first-collection_
+ If the current folder is _INBOX_, _Alpine_ will offer the most
+ recently visited folder in the default collection found in the
+ "Collection List" screen. If the current folder is other than
+ _INBOX_, _INBOX_ is offered as the default.
+
+ _inbox-or-folder-in-recent-collection_
+ This is _Alpine_'s default behavior. If the current folder is
+ _INBOX_, _Alpine_ will offer the last open folder as the
+ default. If the current folder is other than _INBOX_, _INBOX_
+ is offered as the default.
+
+ _first-collection-with-inbox-default_
+ Instead of offering the most recently visited folder in the
+ default collection, the default collection is offered but with
+ _INBOX_ as the default folder. If you type in a folder name it
+ will be in the default collection. If you simply accept the
+ default, however, your _INBOX_ will be opened.
+
+ _most-recent-folder_
+ The last accepted value simply causes the most recently opened
+ folder to be offered as the default regardless of the currently
+ opened folder.
+
+ NOTE: The default while a newsgroup is open remains the same; the
+ last open newsgroup.
+ _header-general-background-color_
+ _header-general-foreground-color_
+ Header Colors.
+ _image-viewer_
+ This variable names the program to call for displaying parts of a
+ MIME message that are of type IMAGE. If your system supports the
+ _mailcap_ system, you don't need to set this variable.
+ _inbox-path_
+ This specifies the name of the folder to use for the _INBOX_. By
+ default this is unset and the system's default is used. The most
+ common reason for setting this is to open an IMAP mailbox for the
+ _INBOX_. For example, _{imap5.u.example.edu}inbox_ will open the
+ user's standard _INBOX_ on the mail server, _imap5_.
+ _incoming-archive-folders_
+ This is like read-message-folder, only more general. This is a list
+ of folder pairs, with the first separated from the second in the pair
+ by a space. The first folder in a pair is the folder you want to
+ archive, and the second folder is the folder that read messages from
+ the first should be moved to. Depending on how you define the
+ auto-move-read-msgs feature, you may or may not be asked when you
+ leave the first folder if you want read messages to be moved to the
+ second folder. In either case, moving the messages means they will be
+ deleted from the first folder.
+ If these are not path names, they will be in the default collection
+ for _Save_s. Any valid folder specification, local or remote (via
+ IMAP), is allowed. There is no default.
+ _incoming-check-interval_
+ This option has no effect unless the feature
+ enable-incoming-folders-checking is set, which in turn has no effect
+ unless incoming-folders is set.
+ This option specifies, in seconds, how often _Alpine_ will check for
+ new mail and state changes in Incoming Folders when Incoming Folders
+ Checking is turned on. The default is 3 minutes (180). This value
+ applies only to folders that are local to the system that _Alpine_ is
+ running on or that are accessed using the IMAP protocol. The similar
+ option incoming-check-interval-secondary applies to all other
+ monitored folders.
+ _incoming-check-interval-secondary_
+ This option has no effect unless the feature
+ enable-incoming-folders-checking is set, which in turn has no effect
+ unless incoming-folders is set.
+ This option together with the option incoming-check-interval
+ specifies, in seconds, how often _Alpine_ will check for new mail and
+ state changes in Incoming Folders when Incoming Folders Checking is
+ turned on. The default for this option is 3 minutes (180). For
+ folders that are local to this system or that are accessed using the
+ IMAP protocol the value of the option incoming-check-interval is
+ used. For all other monitored folders, the value of this option is
+ used.
+ The reason there are two separate options is because it is usually
+ less expensive to check local and IMAP folders than it is to check
+ other types, like POP or NNTP folders. You may want to set this
+ secondary value to a higher number than the primary check interval.
+ _incoming-check-list_
+ This option has no effect unless the feature
+ enable-incoming-folders-checking is set, which in turn has no effect
+ unless incoming-folders is set.
+ When monitoring the Incoming Message Folders for Unseen messages
+ Alpine will normally monitor all Incoming Folders. You may use this
+ option to restrict the list of monitored folders to a subset of all
+ Incoming Folders.
+ _incoming-check-timeout_
+ This option has no effect unless the feature
+ enable-incoming-folders-checking is set, which in turn has no effect
+ unless incoming-folders is set.
+ Sets the time in seconds that Alpine will attempt to open a network
+ connection used for monitoring for Unseen messages in Incoming
+ Folders. The default is 5. If a connection has not completed within
+ this many seconds Alpine will give up and consider it a failed
+ connection.
+ _incoming-folders_
+ This is a list of one or more folders other than _INBOX_ that may
+ receive new messages. This list is slightly special in that it is
+ always expanded in the folder lister. In the future, it may become
+ more special. For example, it would be nice if _Alpine_ would monitor
+ the folders in this list for new mail.
+ _incoming-startup-rule_
+ This rule affects _Alpine_'s behavior when opening the _INBOX_ or
+ another folder from the "INCOMING MESSAGE FOLDERS". This rule tells
+ _Alpine_ which message to make the current message when an incoming
+ folder is opened. There are seven possible values for this option:
+
+ _first-unseen_
+ The current message will be the first unseen message which has
+ not been marked deleted, or the last message if all of the
+ messages have been seen. This is the default setting.
+
+ _first-recent_
+ This is similar to _first-unseen_. Instead of first unseen it
+ is the first recent message. A message is considered to be
+ recent if it arrived since the last time the folder was open
+ (by any mail client, not just the current one). So this option
+ causes the current message to be set to the first
+ undeleted-recent message, or the last message if none is both
+ undeleted and recent.
+
+ _first-important_
+ This will result in the current message being set to the first
+ message marked Important (but not Deleted). If no messages are
+ marked Important, then it will be the last message.
+
+ _first-important-or-unseen_
+ This selects the minimum of the first unseen and the first
+ important messages.
+
+ _first-important-or-recent_
+ This selects the first of the first recent and the first
+ important messages.
+
+ _first_
+ Set the current message to the first undeleted message unless
+ all are deleted. In that case set it to the last message.
+
+ _last_
+ Set the current message to the last undeleted message unless
+ all are deleted. In that case set it to the last message.
+
+ _incoming-unseen-background-color_
+ _incoming-unseen-foreground-color_
+ Incoming Unseen Color.
+ _index-answered-background-color_
+ _index-answered-foreground-color_
+ _index-arrow-background-color_
+ _index-arrow-foreground-color_
+ _index-deleted-background-color_
+ _index-deleted-foreground-color_
+ _index-from-background-color_
+ _index-from-foreground-color_
+ _index-highpriority-background-color_
+ _index-highpriority-foreground-color_
+ _index-important-background-color_
+ _index-important-foreground-color_
+ _index-lowpriority-background-color_
+ _index-lowpriority-foreground-color_
+ _index-new-background-color_
+ _index-new-foreground-color_
+ _index-opening-background-color_
+ _index-opening-foreground-color_
+ _index-recent-background-color_
+ _index-recent-foreground-color_
+ _index-subject-background-color_
+ _index-subject-foreground-color_
+ _index-to-me-background-color_
+ _index-to-me-foreground-color_
+ _index-unseen-background-color_
+ _index-unseen-foreground-color_
+ Index Colors.
+ _index-format_
+ This option is used to customize the content of lines in the MESSAGE
+ INDEX screen. Each line is intended to convey some amount of
+ immediately relevant information about each message in the current
+ folder.
+ _Alpine_ provides a pre-defined set of informational fields with
+ reasonable column widths automatically computed. You can, however,
+ replace this default set by listing special tokens in the order you
+ want them displayed.
+ The list of available tokens is here.
+ Spaces are used to separate listed tokens. Additionally, you can
+ specify how much of the screen's width the taken's associated data
+ should occupy on the index line by appending the token with a pair of
+ parentheses enclosing either a number or percentage. For example,
+ "SUBJECT(13)" means to allocate 13 characters of space to the subject
+ column, and "SUBJECT(20%)" means to allocate 20% of the available
+ space to the subjects column, while plain "SUBJECT" means the system
+ will attempt to figure out a reasonable amount of space.
+ There is always one space between every pair of columns, so if you
+ use fixed column widths (like 13) you should remember to take that
+ into account. Several of the fields are virtually fixed-width, so it
+ doesn't make much sense to specify the width for them. The fields
+ STATUS, FULLSTATUS, IMAPSTATUS, MSGNO, the DATE fields, SIZE, and
+ DESCRIPSIZE all fall into that category. You _may_ specify widths for
+ those if you wish, but you're probably better off letting the system
+ pick those widths.
+ The default is equivalent to:
+
+index-format=STATUS MSGNO SMARTDATETIME24 FROMORTO(33%) SIZENARROW SUBJKEY(6
+ 7%)
+ This means that the four fields without percentages will be allocated
+ first, and then 33% and 67% of the _remaining_ space will go to the
+ from and subject fields. If one of those two fields is specified as a
+ percentage and the other is left for the system to choose, then the
+ percentage is taken as an absolute percentage of the screen, not of
+ the space remaining after allocating the first four columns. It
+ doesn't usually make sense to do it that way. If you leave off all
+ the widths, then the subject and from fields (if both are present)
+ are allocated space in a 2 to 1 ratio, which is almost exactly the
+ same as the default.
+ What you are most likely to do with this configuration option is to
+ specify which fields appear at all, which order they appear in, and
+ the percentage of screen that is used for the from and subject fields
+ if you don't like the 2 to 1 default.
+ If you want to retain the default format that _Pine_ 4.64 had, use
+
+ Index-Format=STATUS MSGNO DATE FROMORTO(33%) SIZE SUBJKEY(67%)
+ _and_ set the feature Disable-Index-Locale-Dates.
+ _initial-keystroke-list_
+ This is a comma-separated list of keystrokes which _Alpine_ executes
+ on startup. Items in the list are usually just characters, but there
+ are some special values. _SPACE,_ _TAB,_ and _CR_ mean a space
+ character, tab character, and a carriage return, respectively. _F1_
+ through _F12_ stand for the twelve function keys. _UP, DOWN, LEFT,
+ _and_ RIGHT _stand for the arrow keys. Control characters are
+ represented with _^<char>_. A restriction is that you can't mix
+ function keys and character keys in this list even though you can, in
+ some cases, mix them when running _Alpine_. A user can always use
+ only _character_ keys in the startup list even if he or she is using
+ _function_ keys normally, or vice versa. If an element in this list is
+ a string surrounded by double quotes (") then it will be expanded
+ into the individual characters in the string, excluding the double
+ quotes.
+ _kblock-passwd-count_
+ System-wide _Alpine_ configuration files only. Number of times a user
+ will have to enter a password when they run the keyboard lock command
+ in the main menu.
+ _keyboard-character-set_
+ See the discussion in International Character Sets for details.
+ _keylabel-background-color_
+ _keylabel-foreground-color_
+ KeyLabel Color.
+ _keyname-background-color_
+ _keyname-foreground-color_
+ KeyName Color.
+ _keywords_
+ You may define your own set of keywords and optionally set them on a
+ message by message basis. These are similar to the "Important" flag
+ which the user may set using the Flag command. The difference is that
+ the Important flag is always present for each folder. User-defined
+ keywords are chosen by the user. You may set up the list of possible
+ keywords here, or you may add keywords from the Flag Details screen
+ that you can get to after typing the Flag (*) command. After the
+ keywords have been defined, then you use the Flag command to set or
+ clear the keywords in each message. The behavior of the flag command
+ may be modified by using the Enable-Flag-Screen-Implicitly option or
+ the Enable-Flag-Screen-Keyword-Shortcut option.
+ Keywords may be used when Selecting messages (Select Keyword).
+ Keywords may also be used in the Patterns of Rules (Filters,
+ Indexcolors, etc). Filter rules may be used to set keywords
+ automatically. Keywords may be displayed as part of the Subject of a
+ message by using the SUBJKEY or SUBJKEYINIT tokens in the
+ Index-Format option. The Keyword-Surrounding-Chars option may be used
+ to modify the display of keywords using SUBJKEY and SUBJKEYINIT
+ slightly. Keywords may also be displayed in a column of their own in
+ the MESSAGE INDEX screen by using the KEY or KEYINIT tokens. It is
+ also possible to color keywords in the index using the Setup/Kolor
+ screen (Keyword Colors). Keywords are not supported by all mail
+ servers.
+ You may give keywords nicknames if you wish. If the keyword
+ definition you type in contains a SPACE character, then the actual
+ value of the keyword is everything after the last SPACE and the
+ nickname for that keyword is everything before the last SPACE. For
+ example, suppose you are trying to interoperate with another email
+ program which uses a particular keyword with an unpleasant name.
+ Maybe it uses a keyword called
+
+ VendorName.SoftwareName.08
+ but for you that keyword means that the message is work-related. You
+ could define a keyword to have the value
+
+ Work VendorName.SoftwareName.08
+ and then you would use the name "Work" when dealing with that keyword
+ in _Alpine_. If you defined it as
+
+ My Work VendorName.SoftwareName.08
+ the nickname would be everything before the last SPACE, that is the
+ nickname would be "My Work".
+ Some commonly used keywords begin with dollar signs. This presents a
+ slight complication, because the dollar sign is normally used to
+ signify environment variable expansion in the _Alpine_ configuration.
+ In order to specify a keyword which begins with a dollar sign you
+ must precede the dollar sign with a second dollar sign to escape its
+ special meaning. For example, if you want to include the keyword
+
+ $Label1
+ as one of your possible keywords, you must enter the text
+
+ $$Label1
+ instead.
+ _keyword-surrounding-chars_
+ This option controls a minor aspect of _Alpine_'s MESSAGE INDEX and
+ MESSAGE TEXT screens. If you have modified the Index-Format option so
+ that either the "SUBJKEY" or "SUBJKEYINIT" tokens are used to display
+ keywords or their initials along with the Subject; then this option
+ may be used to modify the resulting display slightly. By default, the
+ keywords or initials displayed for these tokens will be surrounded
+ with curly braces ({ and }) and a trailing space. For example, if
+ keywords "Work" and "Now" are set for a message, the "SUBJKEY" token
+ will normally look like
+
+ {Work Now} actual subject
+ and the SUBJKEYINIT token would look like
+
+ {WN} actual subject
+ The default character before the keywords is the left brace ({) and
+ the default after the keywords is the right brace followed by a space
+ (} ).
+ This option allows you to change that. You should set it to two
+ values separated by a space. The values may be quoted if they include
+ space characters. So, for example, the default value could be
+ specified explicitly by setting this option to
+
+ Keyword-Surrounding-Chars="{" "} "
+ The first part wouldn't need to be quoted (but it doesn't hurt). The
+ second part does need the quotes because it includes a space
+ character. If you wanted to change the braces to brackets you could
+ use
+
+ Keyword-Surrounding-Chars="[" "] "
+ Inside the quotes you can use backslash quote to mean quote, so
+
+ Keyword-Surrounding-Chars="\"" "\" "
+ would produce
+
+ "Work Now" actual subject
+ It is also possible to color keywords in the index using the
+ Setup/Kolor screen (Keyword Colors).
+ It is not possible to change the fact that a space character is used
+ to separate the keywords if more than one keyword is set for a
+ message. It is also not possible to change the fact that there are no
+ separators between the keyword initials if more than one keyword is
+ set.
+ This option is displayed as "Keyword Surrounding Characters".
+ _last-time-prune-questioned_
+ Personal configuration file only. This variable records the month the
+ user was last asked if his or her _sent-mail_ folders should be
+ pruned. The format is _yy.mm_. This is automatically updated by
+ _Alpine_ when the the pruning is done or declined. If a user wanted to
+ make _Alpine_ stop asking this question he or she could set this time
+ to something far in the future. This may not be set in the
+ system-wide configuration files. Note: The _yy_ year is actually the
+ number of years since 1900, so it will be equal to 101 in the year
+ 2001.
+ _last-version-used_
+ Personal configuration file only. This is set automatically by
+ _Alpine_. It is used to keep track of the last version of _Alpine_
+ that was run by the user. Whenever the version number increases, a
+ new version message is printed out. This may not be set in the
+ system-wide configuration files.
+ _ldap-servers_
+ This is only available if _Alpine_ was linked with an LDAP library
+ when it was compiled. This variable is normally managed by _Alpine_
+ though it can be set in the system-wide configuration files as well
+ as the personal configuration. It is a list variable. Each item in
+ the list contains quite a bit of extra information besides just the
+ server name. To put this into a system-wide config file the easiest
+ thing to do is to configure a personal _Alpine_ for the LDAP server
+ then copy the configuration line into the system-wide config file.
+ Each item in the list looks like:
+
+ server_name[:port] "quoted stuff"
+ The server_name is just a hostname and it is followed by an optional
+ colon and port number. The default port is 389. Following the server
+ name is a single SPACE character followed by a bunch of characters
+ inside double quotes. The part inside the quotes is a set of _tag_ =
+ _value_ pairs. Each tag is preceded by a slash (/) and followed by an
+ equal sign. The value for that tag is the text up to the next slash.
+ An example of some quoted stuff is:
+
+ "/base=o=University of Washington, c=US/impl=0/.../nick=My Server"
+ This would set the search base for this server to o=University of
+ Washington, c=US, set the implicit bit to zero, and set the nickname
+ for the server to My Server. All of the tags correspond directly to
+ items in the Setup/Directory screen so experiment with that if you
+ want to see what the possible tags and values are.
+ _literal-signature_
+ With this option your actual signature, as opposed to the name of a
+ file containing your signature, is stored in the _Alpine_
+ configuration file. If this is defined it takes precedence over the
+ _signature-file_ option.
+ This is simply a different way to store the signature data. The
+ signature is stored inside your _Alpine_ configuration file instead
+ of in a separate signature file. Tokens contained in the signature
+ work the same way they do with the regular signature-file.
+ The Setup/Signature command in _Alpine_'s Main Menu will edit the
+ _literal-signature_ by default. However, if no _literal-signature_ is
+ defined and the file named in the _signature-file_ option exists,
+ then the latter will be used instead. Compose (Reply, Forward, ...)
+ will default to using the _literal-signature_ if defined, otherwise
+ it will use the contents of the file named in _signature-file_.
+ The _Alpine_ composer is used to edit the literal-signature. The
+ result of that edit is first converted to a C-style string before it
+ is stored in the configuration file. In particular, the two character
+ sequence \n (backslash followed by the character "n") will be used to
+ signify a line-break in the signature. You don't have to enter the
+ \n, but it will be visible in the SETUP CONFIGURATION window after
+ you are done editing the signature.
+ _mail-check-interval_
+ This option specifies, in seconds, how often _Alpine_ will check for
+ new mail. If set to zero, new-mail checking is disabled. (You can
+ always manually force a new-mail check by typing ^L (Ctrl-L), which
+ is also the command to refresh the screen, or by typing the Next
+ command when the current message is the last message of the folder.)
+ There is a minimum value for this option, normally 15 seconds. The
+ default value is normally 150 seconds. The higher you set this
+ option, the easier it is on the server.
+ There are some situations where automatic new-mail checking does not
+ work. See the discussion about new-mail checking in
+ folder-reopen-rule.
+ The new-mail checking will not happen exactly at the frequency that
+ you specify. For example, _Alpine_ may elect to defer a non-INBOX
+ mail check if you are busy typing. Or, it may check more frequently
+ than you have specified if that is thought to be necessary to keep
+ the server from closing the connection to the folder due to
+ inactivity. If _Alpine_ checks for new mail as a side effect of
+ another command, it will reset the timer, so that new-mail checking
+ may seem to happen irregularly instead of every X seconds like
+ clockwork.
+ If you are anxious to know about new mail as soon as possible, set
+ the check interval low, and you'll know about the new mail by
+ approximately that amount of time after it arrives. If you aren't so
+ worried about knowing right away, set this option to a higher value.
+ That will save the server some processing time and may save you some
+ of the time you spend waiting for new-mail checks to happen if you
+ are dealing with a slow server or slow network connection.
+ If you suspect that new-mail checking is causing slow downs for you,
+ you may want to look into the options
+ Quell-Mailchecks-Composing-Except-Inbox,
+ Quell-Mailchecks-Composing-InboxandMail-Check-Interval-Noncurrent,
+ which refine when mail checking is done.
+ If the mailbox being check uses a Mail Drop then there is a minimum
+ time (maildrop-check-minimum) between new-mail checks. Because of
+ this minimum you may notice that new mail does not appear promptly
+ when you expect it. The reason for this is to protect the server from
+ over-zealous opening and closing of the Mail Drop folder, since that
+ is a costly operation.
+ A side effect of disabling mail checking is that there will be
+ situations in which the user's IMAP connection will be broken due to
+ inactivity timers on the server. Another side effect is that the
+ user-input-timeout option won't work.
+ _mail-check-interval-noncurrent_
+ This option is closely related to the Mail-Check-Interval option, as
+ well as the Quell-Mailchecks-Composing-Except-Inbox and
+ Quell-Mailchecks-Composing-Inbox options. If the
+ "Mail-Check-Interval" option is set to zero, then automatic new-mail
+ checking is disabled and this option will have no effect.
+ Normally this option is set to zero, which means that the value used
+ will be the same as the value for the "Mail-Check-Interval". If you
+ set this option to a value different from zero (usually larger than
+ the value for "Mail-Check-Interval") then that is the check interval
+ that will be used for folders which are not the currently open folder
+ or the INBOX. You may not even have any folders that are noncurrent
+ and not the INBOX. If you do, it is likely that they are due to
+ Stay-Open-Folders you have configured. This option also affects the
+ rate of mail checking done on cached connections to folders you
+ previously had open but are no longer actively using. You aren't
+ expected to understand that last sentence, but if you are interested
+ take a look at Max-Remote-Connections, and the related options.
+ _mail-directory_
+ This variable was more important in previous versions of _Alpine_.
+ Now it is used only as the default for storing personal folders (and
+ only if there are no folder-collections defined). The default value
+ is _~/mail_ on UNIX and _${HOME}\MAIL_ on a PC.
+ _mailcap-search-path_
+ This variable is used to replace _Alpine_'s default mailcap file
+ search path. It takes one or more file names (full paths must be
+ specified) in which to look for mail capability data.
+ _maildrop-check-minimum_
+ New-mail checking for a Mail Drop is a little different from new mail
+ checking for a regular folder. One of the differences is that the
+ connection to the Mail Drop is not kept open and so the cost of
+ checking (delay for you and additional load for the server) may be
+ significant. Because of this additional cost we set a minimum time
+ that must pass between checks. This minimum only applies to the
+ automatic checking done by _Alpine_. If you force a check by typing
+ ^L (Ctrl-L) or by typing the Next command when you are at the end of
+ a folder index, then the check is done right away.
+ This option specifies, in seconds, the _minimum_ time between Mail
+ Drop new-mail checks. You may want to set this minimum high in order
+ to avoid experiencing some of the delays associated with the checks.
+ Note that the time between checks is still controlled by the regular
+ Mail-Check-Interval option. When _Alpine_ is about to do an automatic
+ check for new mail (because the Mail-Check-Interval has expired) then
+ if the time since the last new-mail check of any open Mail Drops has
+ been greater than the MailDrop-Check-Minimum, the Mail Drop is
+ checked for new mail as well. Therefore, it is only useful to set
+ this option to a value that is higher than the Mail-Check-Interval.
+ If this option is set to zero, automatic Mail Drop new-mail checking
+ is disabled. There is a minimum value, normally 60 seconds. The
+ default value is normally 60 seconds as well. This applies to the
+ INBOX and to the currently open folder if that is different from the
+ INBOX.
+ _max-remote-connections_
+ This option affects low-level behavior of _Alpine_. The default value
+ for this option is _2_. If your INBOX is accessed using the IMAP
+ protocol from an IMAP server, that connection is kept open throughout
+ the duration of your _Alpine_ session, independent of the value of
+ this option. The same is true of any Stay-Open-Folders you have
+ defined. This option controls _Alpine_'s behavior when connecting to
+ remote IMAP folders other than your INBOX or your Stay-Open-Folders.
+ It specifies the maximum number of remote IMAP connections (other
+ than those mentioned above) that _Alpine_ will use for accessing the
+ rest of your folders. If you set this option to zero, you will turn
+ off most remote connection re-use. It's difficult to understand
+ exactly what this option does, and it is usually fine to leave it set
+ to its default value. It is probably more likely that you will be
+ interested in setting the Stay-Open-Folders option instead of
+ changing the value of this option. A slightly longer explanation of
+ what is going on with this option is given in the next paragraphs.
+ There are some time costs involved in opening and closing remote IMAP
+ folders, the main costs being the time you have to wait for the
+ connection to the server and the time for the folder to open. Opening
+ a folder may involve not only the time the server takes to do its
+ processing but time that _Alpine_ uses to do filtering. These times
+ can vary widely. They depend on how loaded the server is, how large
+ the folder being opened is, and how you set up filtering, among other
+ things. Once _Alpine_ has opened a connection to a particular folder,
+ it will attempt to keep that connection open in case you use it
+ again. In order to do this, _Alpine_ will attempt to use the
+ Max-Remote-Connections (the value of this option) IMAP connections
+ you have alloted for this purpose.
+ For example, suppose the value of this option is set to "2". If your
+ INBOX is accessed on a remote server using the IMAP protocol, that
+ doesn't count as one of the remote connections but it is always kept
+ open. If you then open another IMAP folder, that would be your first
+ remote connection counted as one of the Max-Remote-Connections
+ connections. If you open a third folder the second will be left open,
+ in case you return to it. You won't be able to tell it has been left
+ open. It will appear to be closed when you leave the folder but the
+ connection will remain in the background. Now suppose you go back to
+ the second folder (the first folder after the INBOX). A connection to
+ that folder is still open so you won't have to wait for the startup
+ time to open it. Meanwhile, the connection to the third folder will
+ be left behind. Now, if you open a fourth folder, you will bump into
+ the Max-Remote-Connections limit, because this will be the third
+ folder other than INBOX and you have the option set to "2". The
+ connection that is being used for the third folder will be re-used
+ for this new fourth folder. If you go back to the third folder after
+ this, it is no longer already connected when you get there. You'll
+ still save some time since _Alpine_ will re-use the connection to the
+ fourth folder and you have already logged in on that connection, but
+ the folder will have to be re-opened from scratch.
+ If a folder is large and the startup cost is dominated by the time it
+ takes to open that folder or to run filters on it, then it will pay
+ to make the value of this option large enough to keep it open. On the
+ other hand, if you only revisit a handful of folders or if the
+ folders are small, then it might make more sense to keep this number
+ small so that the reconnect time (the time to start up a new
+ connection and authenticate) is eliminated instead.
+ You may also need to consider the impact on the server. On the
+ surface, a larger number here may cause a larger impact on the
+ server, since you will have more connections open to the server. On
+ the other hand, not only will _you_ be avoiding the startup costs
+ associated with reopening a folder, but the _server_ will be avoiding
+ those costs as well.
+ When twenty five minutes pass without any active use of an IMAP
+ connection being saved for possible re-use, that connection will be
+ shut down,
+ This option is displayed as "Maximum Remote Connections".
+ _meta-message-background-color_
+ _meta-message-foreground-color_
+ Meta-message Color.
+ _mimetype-search-path_
+ This variable is used to replace _Alpine_'s default mime.types file
+ search path. It takes one or more file names (full paths must be
+ specified) in which to look for file-name-extension to MIME type
+ mapping data. See the Config Notes for details on _Alpine_'s usage of
+ the MIME.Types File.
+ _new-version-threshold_
+ When a new version of _Alpine_ is run for the first time it offers a
+ special explanatory screen to the user upon startup. This option
+ helps control when and if that special screen appears for users that
+ have previously run _Alpine_. It takes as its value a _Alpine_
+ version number. _Alpine_ versions less than the specified value will
+ supress this special screen while versions equal to or greater than
+ that specified will behave normally.
+ _newmail-fifo-path_
+ This option is only available in UNIX _Alpine_. However, there is a
+ very similar feature built in to _PC-Alpine_. In _PC-Alpine_'s Config
+ menu at the top of the screen is an option called "New Mail Window".
+ You may have _Alpine_ create a FIFO special file (also called a named
+ pipe, see mkfifo(3) and fifo(4)) where it will send a one-line
+ message each time a new message is received in the current folder,
+ the INBOX, or any open Stay-Open-Folders. To protect against two
+ different _Alpine_s both writing to the same FIFO, _Alpine_ will only
+ create the FIFO and write to it if it doesn't already exist.
+ A possible way to use this option would be to have a separate window
+ on your screen running the command
+
+ cat filename
+ where "filename" is the name of the file given for this option.
+ Because the file won't exist until after you start _Alpine_, you must
+ _first_ start _Alpine_ and _then_ run the "cat" command. You may be
+ tempted to use "tail -f filename" to view the new mail log. However,
+ the common implementations of the tail command will not do what you
+ are hoping.
+ The width of the messages produced for the FIFO may be altered with
+ the NewMail-Window-Width option.
+ On some systems, fifos may only be created in a local filesystem. In
+ other words, they may not be in NFS filesystems. This requirement is
+ not universal. If the system you are using supports it, it should
+ work. (It is often the case that your home directory is in an NFS
+ filesystem. If that is the case, you might try using a file in the
+ "/tmp" filesystem, which is usually a local filesytem.) Even when it
+ is possible to use an NFS-mounted filesystem as a place to name the
+ fifo (for example, your home directory), it will still be the case
+ that the reader (probably the "cat" command) and the writer
+ (_Alpine_) of the fifo must be running on the same system.
+ _newmail-window-width_
+ UNIX _Alpine_ only.
+ This option is only useful if you have turned on the
+ NewMail-FIFO-Path option. That option causes new mail messages to be
+ sent to a fifo file. Those messages will be 80 characters wide by
+ default. You can change the width of the messages by changing this
+ option. For example, if you are reading those messages in another
+ window you might want to set this width to the width of that other
+ window.
+ For UNIX _Alpine_, this option is only useful if you have turned on
+ the NewMail-FIFO-Path option. That option causes new mail messages to
+ be sent to a fifo file. Those messages will be 80 characters wide by
+ default. You can change the width of those messages by changing this
+ option. For example, if you are reading those messages in another
+ window you might want to set this width to the width of that other
+ window.
+ If you are using _PC-Alpine_, it has an option in the Config menu to
+ turn on the "New Mail Window". The present option also controls the
+ width of that window.
+ _news-active-file-path_
+ This option tells _Alpine_ where to look for the "active file" for
+ newsgroups when accessing news locally, rather than via NNTP. The
+ default path is usually /usr/lib/news/active.
+ _news-collections_
+ This is a list of collections where news folders are located. See the
+ section describing collections for more information.
+ _news-spool-directory_
+ This option tells _Alpine_ where to look for the "news spool" for
+ newsgroups when accessing news locally, rather than via NNTP. The
+ default path is usually /usr/spool/news.
+ _newsrc-path_
+ This option overrides the default name _Alpine_ uses for your
+ "newsrc" news status and subscription file. If set, _Alpine_ will
+ take this value as the full pathname for the desired newsrc file.
+ _nntp-range_
+ This option applies only to newsgroups accessed using the NNTP
+ protocol. It does not, for example, apply to newsgroups accessed
+ using an IMAP-to-NNTP proxy.
+ When you open a connection to a News server using the NNTP protocol,
+ you normally have access to all of the articles in each newsgroup. If
+ a server keeps a large backlog of messages it may speed performance
+ some to restrict attention to only the newer messages in a group.
+ This option allows you to set how many article numbers should be
+ checked when opening a newsgroup. You can think of "nntp-range" as
+ specifying the maximum number of messages you ever want to see. For
+ example, if you only ever wanted to look at the last 500 messages in
+ each newsgroup you could set this option to 500. In actuality, it
+ isn't quite that. Instead, for performance reasons, it specifies the
+ range of article numbers to be checked, beginning with the highest
+ numbered article and going backwards from there. If there are
+ messages that have been canceled or deleted their article numbers are
+ still counted as part of the range.
+ So, more precisely, setting the "nntp-range" will cause article
+ numbers
+
+ last_article_number - nntp-range + 1 through last_article_number
+ to be considered when reading a newsgroup. The number of messages
+ that show up in your index will be less than or equal to the value of
+ "nntp-range".
+ The purpose of this option is simply to speed up access when reading
+ news. The speedup comes because _Alpine_ can ignore all but the last
+ nntp-range article numbers, and can avoid downloading any information
+ about the ignored articles. There is a cost you pay for this speedup.
+ That cost is that there is no way for you to see those ignored
+ articles. The articles that come before the range you specify are
+ invisible to you and to _Alpine_, as if they did not exist at all.
+ There is no way to see those messages using, for example, an
+ unexclude command or something similar. The only way to see those
+ articles is to set this option high enough (or set it to zero) and
+ then to reopen the newsgroup.
+ If this option is set to 0 (which is also the default), then the
+ range is unlimited. This option applies globally to all NNTP servers
+ and to all newsgroups on those servers. There is no way to set
+ different values for different newsgroups or servers.
+ _nntp-server_
+ One or more NNTP servers (host name or IP address) which _Alpine_
+ will use for reading and posting news. If you read and post news to
+ and from a single NNTP server, you can get away with only setting the
+ _nntp-server_ variable and leaving the _news-collections_ variable
+ unset.
+ When you define an NNTP server, _Alpine_ implicitly defines a news
+ collection for you, assuming that server as the news server and
+ assuming that you will use the NNTP protocol and a local newsrc
+ configuration file for reading news. See also Configuring News.
+ Your NNTP server may offer NNTP "AUTHINFO SASL" or "AUTHINFO USER"
+ authentication. It may even require it. If your NNTP server does
+ offer such authentication you may specify a user name parameter to
+ cause _Alpine_ to attempt to authenticate. The same is true for the
+ server name in a folder collection which uses NNTP. This parameter
+ requires an associated value, the username identifier with which to
+ establish the server connection. An example might be:
+
+ nntpserver.example.com/user=katie
+ If authentication is offered by the server, this will cause _Alpine_
+ to attempt to use it. If authentication is not offered by the server,
+ this will cause _Alpine_ to fail with an error similar to:
+
+ Error: NNTP authentication not available
+ For more details about the server name possibilities see Server Name
+ Syntax.
+ _normal-background-color_
+ _normal-foreground-color_
+ Normal Color.
+ _opening-text-separator-chars_
+ This option controls a minor aspect of _Alpine_'s MESSAGE INDEX
+ screen. With some setups the text of the subject is followed by the
+ opening text of the message if there is any room available in the
+ index line. If you have configured your Index-Format option to
+ include one of the Subject tokens which causes this behavior
+ (SUBJECTTEXT, SUBJKEYTEXT, or SUBJKEYINITTEXT), then this option may
+ be used to modify what is displayed slightly. By default, the Subject
+ is separated from the opening text of the message by the three
+ characters space dash space;
+
+ " - "
+ Use this option to set it to something different. The value must be
+ quoted if it includes any space characters. For example, the default
+ value could be specified explicitly by setting this option to
+
+ Opening-Text-Separator-Chars=" - "
+ This option is displayed as "Opening Text Separator Characters".
+ _operating-dir_
+ System-wide _Alpine_ configuration files only. This names the root of
+ the tree to which the user is restricted when reading and writing
+ folders and files. It is usually used in the _fixed_ configuration
+ file.
+ _patterns-filters2_
+ Matching patterns and their corresponding actions are stored in this
+ variable. These patterns are used with Filtering. This variable is
+ normally maintained through the Setup/Rules/Filters configuration
+ screen. It is a list variable. Each member of the list is a single
+ pattern/action pair, or it can be a file which contains zero or more
+ lines of pattern/action pairs. The only way to create a filters file
+ is to use the InsertFile command in the Setup/Rules/Filters screen
+ with a filename which doesn't yet exist. Then use the Shuffle command
+ to move existing filter patterns into the file. This isn't very
+ convenient but it isn't thought that many users will need this
+ functionality. The purpose of filter files is for sharing filters.
+ This option is displayed as "Patterns Filters".
+ _patterns-indexcolors_
+ Matching patterns and their corresponding actions are stored in this
+ variable. These patterns are used for Index Line Colors. This
+ variable is normally maintained through the Setup/Rules/Indexcolor
+ configuration screen. It is a list variable. Each member of the list
+ is a single pattern/action pair, or it can be a file which contains
+ zero or more lines of pattern/action pairs. The only way to create a
+ indexcolor file is to use the InsertFile command in the
+ Setup/Rules/Indexcolor screen with a filename which doesn't yet
+ exist. Then use the Shuffle command to move existing patterns into
+ the file. This isn't very convenient but it isn't thought that many
+ users will need this functionality. The purpose of indexcolor files
+ is for sharing indexcolors.
+ _patterns-other_
+ Matching patterns and their corresponding actions are stored in this
+ variable. These patterns are used with Miscellaneous Rules
+ configuration. This variable is normally maintained through the
+ Setup/Rules/Other configuration screen. It is a list variable. Each
+ member of the list is a single pattern/action pair, or it can be a
+ file which contains zero or more lines of pattern/action pairs. The
+ only way to create a rules file is to use the InsertFile command in
+ the Setup/Rules/Other screen with a filename which doesn't yet exist.
+ Then use the Shuffle command to move existing rules into the file.
+ This isn't very convenient but it isn't thought that many users will
+ need this functionality.
+ _patterns-roles_
+ Matching patterns and their corresponding actions are stored in this
+ variable. These patterns are used with Roles. This variable is
+ normally maintained through the Setup/Rules/Roles configuration
+ screen. It is a list variable. Each member of the list is a single
+ pattern/action pair, or it can be a file which contains zero or more
+ lines of pattern/action pairs. The only way to create a roles file is
+ to use the InsertFile command in the Setup/Rules/Roles screen with a
+ filename which doesn't yet exist. Then use the Shuffle command to
+ move existing roles into the file. This isn't very convenient but it
+ isn't thought that many users will need this functionality. The
+ purpose of role files is for sharing roles.
+ _patterns-scores2_
+ Matching patterns and their corresponding actions are stored in this
+ variable. These patterns are used with Scoring. This variable is
+ normally maintained through the Setup/Rules/SetScores configuration
+ screen. It is a list variable. Each member of the list is a single
+ pattern/action pair, or it can be a file which contains zero or more
+ lines of pattern/action pairs. The only way to create a scores file
+ is to use the InsertFile command in the Setup/Rules/SetScores screen
+ with a filename which doesn't yet exist. Then use the Shuffle command
+ to move existing scoring patterns into the file. This isn't very
+ convenient but it isn't thought that many users will need this
+ functionality. The purpose of scoring files is for sharing scoring
+ rules.
+ This option is displayed as "Patterns Scores".
+ _patterns-search_
+ Matching patterns for use with the Select command are stored in this
+ variable. These patterns are used with Search Rules configuration.
+ This variable is normally maintained through the Setup/Rules/searCh
+ configuration screen. It is a list variable. Each member of the list
+ is a single pattern, or it can be a file which contains zero or more
+ lines of patterns. The only way to create a rules file is to use the
+ InsertFile command in the Setup/Rules/searCh screen with a filename
+ which doesn't yet exist. Then use the Shuffle command to move
+ existing rules into the file. This isn't very convenient but it isn't
+ thought that many users will need this functionality.
+ _personal-name_
+ Personal configuration file only. User's full personal name. On UNIX
+ systems, the default is taken from the accounts data base
+ (/etc/passwd). The easiest way to change the full From address is
+ with the customized-hdrs variable.
+ _personal-print-category_
+ Personal configuration file only. This is the category that the
+ default print command belongs to. There are three categories.
+ Category 1 is an attached printer which uses the ANSI escape
+ sequence, category 2 is the standard system print command, and
+ category 3 is the set of custom printer commands defined by the user.
+ This just helps _Alpine_ figure out where to put the cursor when the
+ user runs the _Setup/Printer_ command. This is not used by
+ _PC-Alpine_.
+ _personal-print-command_
+ Personal configuration file only. This corresponds to the third
+ category in the printer menu, the personally selected print commands.
+ This variable contains the list of custom commands that the user has
+ entered in the _Setup/Printer_ screen. This is not used by
+ _PC-Alpine_.
+ _posting-character-set_
+ See the discussion in International Character Sets for details.
+ _postponed-folder_
+ The folder where postponed messages are stored. The default is
+ _postponed-msgs_ (Unix) or _POSTPOND_ (PC).
+ _print-font-name_
+ Winsock version of _PC-Alpine_ only.
+ _print-font-size_
+ Winsock version of _PC-Alpine_ only.
+ _print-font-style_
+ Winsock version of _PC-Alpine_ only.
+ _printer_
+ Personal configuration file only. This is the current setting for a
+ user's printer. This variable is set from _Alpine_'s _Setup/Printer_
+ screen.
+ _prompt-background-color_
+ _prompt-foreground-color_
+ Prompt Color.
+ _pruned-folders_
+ This variable allows you to define a list of one or more folders that
+ _Alpine_ will offer to prune for you in the same way it automatically
+ offers to prune your "sent-mail" folder each month. Each folder in
+ this list must be a folder in your default folder collection (the
+ first folder collection if you have more than one), and it is just
+ the relative name of the folder in the collection, not the
+ fully-qualified name. It is similar to sent-mail. Instead of
+ something like
+
+ pruned-folders={servername}mail/folder
+ the correct value to use would be
+
+ folder
+ There is an assumption here that your first collection is the folders
+ in
+
+ {servername}mail
+ Once a month, for each folder listed, _Alpine_ will offer to move the
+ contents of the folder to a new folder of the same name but with the
+ previous month's date appended. _Alpine_ will then look for any such
+ date-appended folder names created for a previous month, and offer
+ each one it finds for deletion.
+ If you decline the first offer, no mail is moved and no new folder is
+ created.
+ The new folders will be created in your default folder collection.
+ _pruning-rule_
+ By default, _Alpine_ will ask at the beginning of each month whether
+ or not you want to rename your sent-mail folder to a name like
+ sent-mail-month-year. (See the feature prune-uses-yyyy-mm to change
+ the format of the folder to sent-mail-yyyy-mm.) It will also ask
+ whether you would like to delete old sent-mail folders. If you have
+ defined read-message-folder or pruned-folders _Alpine_ will also ask
+ about pruning those folders. With this option you may provide an
+ automatic answer to the rename questions and you may tell _Alpine_ to
+ not ask about deleting old folders.
+ _quote1-background-color_
+ _quote1-foreground-color_
+ _quote2-background-color_
+ _quote2-foreground-color_
+ _quote3-background-color_
+ _quote3-foreground-color_
+ Quote Colors.
+ _quote-replace-string_
+ This option specifies what string to use as a quote when _viewing_ a
+ message. The standard way of quoting messages when replying is the
+ string "> " (quote space). With this variable set, viewing a message
+ will replace occurrences of "> " with the replacement string. This
+ setting works best when Reply-Indent-String or the equivalent setting
+ in your correspondents' mail programs is set to the default "> ", but
+ it will also work fine with the Reply-Indent-String set to ">".
+ Enable the feature Quote-Replace-Nonflowed to also have
+ quote-replacement performed on non-flowed messages.
+ Setting this option will replace ">" and "> " with the new setting.
+ This string may include trailing spaces. To preserve those spaces
+ enclose the full string in double quotes.
+ No padding to separate the text of the message from the quote string
+ is added. This means that if you do not add trailing spaces to the
+ value of this variable, text will be displayed right next to the
+ quote string, which may be undesirable. This can be avoided by adding
+ a new string separated by a space from your selection of quote string
+ replacement. This last string will be used for padding. For example,
+ setting this variable to ">" " " has the effect of setting ">" as the
+ quote-replace-string, with the text padded by a space from the last
+ quote string to make it more readable.
+ One possible setting for this variable could be " " (four spaces
+ wrapped in quotes), which would have the effect of indenting each
+ level of quoting four spaces and removing the ">"'s. Different levels
+ of quoting could be made more discernible by setting colors for
+ quoted text.
+ Replying to or forwarding the viewed message will preserve the
+ original formatting of the message, so quote-replacement will not be
+ performed on messages that are being composed.
+ _quote-suppression-threshold_
+ This option should be used with care. It will cause some of the
+ quoted text to be eliminated from the display when viewing a message
+ in the MESSAGE TEXT screen. For example, if you set the
+ Quote-Suppression-Threshold to the value "5", this will cause quoted
+ text that is longer than five lines to be truncated. Quoted text of
+ five or fewer consecutive lines will be displayed in its entirety.
+ Quoted text of more than six lines will have the first five lines
+ displayed followed by a line that looks something like
+
+ [ 12 lines of quoted text hidden from view ]
+ As a special case, if exactly one line of quoted text would be
+ hidden, the entire quote will be shown instead. So for the above
+ example, quoted text which is exactly six lines long will will be
+ shown in its entirety. (In other words, instead of hiding a single
+ line and adding a line that announces that one line was hidden, the
+ line is just shown.)
+ If the sender of a message has carefully chosen the quotes that he or
+ she includes, hiding those quotes may change the meaning of the
+ message. For that reason, _Alpine_ requires that when you want to set
+ the value of this variable to something less than four lines, you
+ actually have to set it to the negative of that number. So if you
+ want to set this option to "3", you actually have to set it to "-3".
+ The only purpose of this is to get you to think about whether or not
+ you really want to do this! If you want to delete all quoted text you
+ set the value of this option to the special value "-10".
+ The legal values for this option are
+
+ 0 Default, don't hide anything
+ -1,-2,-3 Suppress quote lines past 1, 2, or 3 lines
+ 4,5,6,... Suppress if more than that many lines
+ -10 Suppress all quoted lines
+ If you set this option to a non-default value you may sometimes wish
+ to view the quoted text that is not shown. When this is the case, the
+ HdrMode (Header Mode) command may be used to show the hidden text.
+ Typing the "H" command once will show the hidden text. Typing a
+ second "H" will also turn on Full Header mode. The presence or
+ absence of the HdrMode command is determined by the
+ "Enable-Full-Header-Cmd" Feature-List option in your _Alpine_
+ configuration, so you will want to be sure that is turned on if you
+ use quote suppression.
+ For the purposes of this option, a quote is a line that begins with
+ the character ">".
+ Quotes are only suppressed when displaying a message on the screen.
+ The entire quote will be left intact when printing or forwarding or
+ something similar.
+ _read-message-folder_
+ If set, mail in the _INBOX_ that has been read but not deleted is
+ moved here, or rather, the user is asked whether or not he or she
+ wants to move it here upon quitting _Alpine_.
+ _remote-abook-history_
+ Sets how many extra copies of remote address book data will be kept
+ in each remote address book folder. The default is three. These extra
+ copies are simply old versions of the data. Each time a change is
+ made a new copy of the address book data is appended to the folder.
+ Old copies are trimmed, if possible, when _Alpine_ exits. An old copy
+ can be put back into use by deleting and expunging newer versions of
+ the data from the folder. Don't delete the first message from the
+ folder. It is a special header message for the remote address book
+ and it must be there. This is to prevent regular folders from being
+ used as remote address book folders and having their data destroyed.
+ _remote-abook-metafile_
+ Personal configuration file only. This is usually set by _Alpine_ and
+ is the name of a file that contains data about remote address books
+ and remote configuration files.
+ _remote-abook-validity_
+ Sets the minimum number of minutes that a remote address book will be
+ considered up to date. Whenever an entry contained in a remote
+ address book is used, if more than this many minutes have passed
+ since the last check the remote server will be queried to see if the
+ address book has changed. If it has changed, the local copy is
+ updated. The default value is five minutes. The special value of -1
+ means never check. The special value of zero means only check when
+ the address book is first opened.
+ No matter what the value, the validity check is always done when the
+ address book is about to be changed by the user. The check can be
+ initiated manually by typing _^L_ (Ctrl-L) while in the address book
+ maintenance screen for the remote address book.
+ _reply-indent-string_
+ This variable specifies an aspect of _Alpine_'s _Reply_ command. When
+ a message is replied to and the text of the message is included, the
+ included text usually has the string "> " prepended to each line
+ indicating it is quoted text.
+ This option specifies a different value for that string. If you wish
+ to use a string which begins or ends with a space, enclose the string
+ in double quotes.
+ Besides simple text, the prepended string can be based on the message
+ being replied to. The following tokens are substituted for the
+ message's corresponding value:
+
+ _FROM_
+ This token gets replaced with the message sender's "username".
+ At most six characters are used.
+
+ _NICK_
+ This token gets replaced with the nickname of the message
+ sender's address as found in your addressbook. If no
+ addressbook entry is found, Pine replaces the characters
+ "_NICK_" with nothing. At most six characters are used.
+
+ _INIT_
+ This token gets replaced with the initials of the sender of the
+ message.
+
+ When the enable-reply-indent-string-editing feature is enabled, you
+ are given the opportunity to edit the string, whether it is the
+ default or one automatically generated using the above tokens.
+ _reply-leadin_
+ This option is used to customize the content of the introduction line
+ that is included when replying to a message and including the
+ original message in the reply. The normal default (what you will get
+ if you delete this variable) looks something like:
+
+ On Sat, 24 Oct 1998, Fred Flintstone wrote:
+ where the day of the week is only included if it is available in the
+ original message. You can replace this default with text of your own.
+ The text may contain tokens that are replaced with text that depends
+ on the message you are replying to. For example, the default is
+ equivalent to:
+
+ On _DAYDATE_, _FROM_ wrote:
+ Since this variable includes regular text mixed with special tokens
+ the tokens have to be surrounded by underscore characters. For
+ example, to use the token "PREFDATE" you would need to use
+ "_PREFDATE_", not "PREFDATE".
+ The list of available tokens is here.
+ By default, the text is all on a single line and is followed by a
+ blank line. If your _Reply-Leadin_ turns out to be longer than 80
+ characters when replying to a particular message, it is shortened.
+ However, if you use the token
+
+ _NEWLINE_
+ anywhere in the value, no end of line or blank line is appended, and
+ no shortening is done. The _NEWLINE_ token may be used to get rid of
+ the blank line following the text, to add more blank lines, or to
+ form a multi-line _Reply-Leadin_. To clarify how _NEWLINE_ works
+ recall that the default value is:
+
+ On _DAYDATE_, _FROM_ wrote:
+ That is equivalent to
+
+ On _DAYDATE_, _FROM_ wrote:_NEWLINE__NEWLINE_
+ In the former case, two newlines are added automatically because no
+ _NEWLINE_ token appears in the value of the option (for backwards
+ compatibility). In the latter case, the newlines are explicit. If you
+ want to remove the blank line that follows the _Reply-Leadin_ text
+ use a single _NEWLINE_ token like
+
+ On _DAYDATE_, _FROM_ wrote:_NEWLINE_
+ Because of the backwards compatibility problem, it is not possible to
+ remove all of the ends of lines, because then there will be no
+ _NEWLINE_ tokens and that will cause the automatic adding of two
+ newlines! If you want, you may embed newlines in the middle of the
+ text, as well, producing a multi-line _Reply-Leadin_.
+ By default, no attempt is made to localize the date. If you prefer a
+ localized form you may find that one of the tokens _PREFDATE_ or
+ _PREFDATETIME_ is a satisfactory substitute. If you want more control
+ one of the many other date tokens, such as _DATEISO_, might be
+ better.
+ For the adventurous, there is a way to conditionally include text
+ based on whether or not a token would result in specific replacement
+ text. For example, you could include some text based on whether or
+ not the _NEWS_ token would result in any newsgroups if it was used.
+ It's explained in detail here.
+ In the very unlikely event that you want to include a literal token
+ in the introduction line you must precede it with a backslash
+ character. For example,
+
+ \_DAYDATE_ = _DAYDATE_
+ would produce something like
+
+ _DAYDATE_ = Sat, 24 Oct 1998
+ It is not possible to have a literal backslash followed by an
+ expanded token.
+ _reverse-background-color_
+ _reverse-foreground-color_
+ Reverse Color.
+ _rsh-command_
+ Sets the format of the command used to open a UNIX remote shell
+ connection. The default is "%s %s -l %s exec /etc/r%sd". All four
+ "%s" entries MUST exist in the provided command. The first is for the
+ command's pathname, the second is for the host to connnect to, the
+ third is for the user to connect as, and the fourth is for the
+ connection method (typically imap).
+ _rsh-open-timeout_
+ Sets the time in seconds that _Alpine_ will attempt to open a UNIX
+ remote shell connection. The default is 15, the minimum non-zero
+ value is 5, and the maximum is unlimited. If this is set to zero rsh
+ connections will be completely disabled.
+ _rsh-path_
+ Sets the name of the command used to open a UNIX remote shell
+ connection. The default is typically /usr/ucb/rsh.
+ _saved-msg-name-rule_
+ Determines default folder name when _Sav_ing. If set to
+ _default-folder_ (which is the default setting), then _Alpine_ will
+ offer the folder "saved-messages" (UNIX) or "SAVEMAIL" (PC) for
+ _Sav_ing messages. The default folder offered in this way may be
+ changed by using the configuration variable default-saved-msg-folder.
+ If this rule is set to _last-folder-used_, _Alpine_ offers to _Save_
+ to the folder you last successfully _Saved_ a message to (this
+ session). The first time you _Save_ a message in a session, _Alpine_
+ offers to _Save_ the message to the default folder.
+ Choosing any of the _by-_ options causes _Alpine_ to attempt to get
+ the chosen option's value for the message being _Saved_ (or for the
+ first message being Saved if using an aggregate Save). For example,
+ if _by-from_ is chosen, _Alpine_ attempts to get the value of who the
+ message came from (i.e. the from address). _Alpine_ then attempts to
+ _Save_ the message to a folder matching that value. If _by-from_ is
+ chosen and no value is obtained, _Alpine_ uses _by-sender_. The
+ opposite is also true. If _by-recipient_ was chosen and the message
+ was posted to a newsgroup, _Alpine_ will use the newsgroup name. If
+ _by-replyto_ is chosen and no value is obtained, _Alpine_ uses
+ _by-from_.
+ If any of the "by-realname" options are chosen, _Alpine_ will attempt
+ to use the personal name part of the address instead of the mailbox
+ part. If any of the "by-nick" options are chosen, the address is
+ looked up in your address book and if found, the nickname for that
+ entry is used. Only simple address book entries are checked, not
+ distribution lists. Similarly, if any of the "by-fcc" options are
+ chosen, the fcc from the corresponding address book entry is used. If
+ by-realname, or the by-nick or by-fcc lookups result in no value,
+ then if the chosen option ends with the "then-from", "then-sender",
+ "then-replyto", or "then-recip" suffix, _Alpine_ reverts to the same
+ behavior as "by-from", "by-sender", "by-replyto", or "by-recip"
+ depending on which option was specified. If the chosen option doesn't
+ end with one of the "then-" suffixes, then _Alpine_ reverts to the
+ default folder when no match is found in the address book.
+ Here is an example to make some of the options clearer. If the
+ message is From
+
+ Fred Flintstone <flint@bedrock.org>
+ and this rule is set to "by-from", then the default folder offered in
+ the save dialog would be "flint".
+ If this rule is set to "by-realname-of-from" then the default would
+ be "Fred Flintstone".
+ If this rule is set to "by-nick-of-from" then _Alpine_ will search
+ for the address "flint@bedrock.org" in your address book. If an entry
+ is found and it has a nickname associated with it, that nickname will
+ be offered as the default folder. If not, the default saved message
+ folder will be offered as the default.
+ If this rule is set to "by-fcc-of-from" then _Alpine_ will search for
+ the address "flint@bedrock.org" in your address book. If an entry is
+ found and it has an Fcc associated with it, that Fcc will be offered
+ as the default folder. If not, the default saved message folder will
+ be offered as the default.
+ If this rule is set to "by-nick-of-from-then-from" then _Alpine_ will
+ search for the address "flint@bedrock.org" in your address book. If
+ an entry is found and it has a nickname associated with it, that
+ nickname will be offered as the default folder. If it is not found
+ (or has no nickname) then the default offered will be the same as it
+ would be for the "by-from" rule. That is, it would be "flint"
+ This option is displayed as "Saved Message Name Rule".
+ _scroll-margin_
+ This option controls when _Alpine_'s line-by-line scrolling occurs.
+ Typically, when a selected item is at the top or bottom screen edge
+ and the UP or DOWN (and Ctrl-P or Ctrl-N) keys are pressed, the
+ displayed items are scrolled down or up by a single line.
+ This option allows you to tell _Alpine_ the number of lines from the
+ top and bottom screen edge that line-by-line scrolling should occur.
+ For example, setting this value to one (1) will cause _Alpine_ to
+ scroll the display when you move to select an item on the display's
+ top or bottom edge (instead of moving when you move off the edge of
+ the screen).
+ By default, this variable is zero (0), indicating that scrolling
+ happens when you move up or down to select an item immediately off
+ the display's top or bottom edge.
+ _selectable-item-background-color_
+ _selectable-item-foreground-color_
+ Selectable-item Color.
+ _sending-filters_
+ This option defines a list of text-filtering commands (programs and
+ scripts) that may be selectively invoked to process a message just
+ before it is sent. If set, the Composer's _^X Send_ command will
+ allow you to select which filter (or none) to apply to the message
+ before it is sent. For security reasons, the full path of the filter
+ program must be specified.
+ Sending filters do not work with _PC-Alpine_ and sending filters are
+ not used if the feature send-without-confirm is set.
+ Command Modifying Tokens:
+
+ __RECIPIENTS__
+ When the command is executed, this token is replaced with the
+ space delimited list of recipients of the message being sent.
+
+ __TMPFILE__
+ When the command is executed, this token is replaced with the
+ path and name of the temporary file containing the text to be
+ filtered. _Alpine_ expects the filter to replace this data with
+ the filter's result. NOTE: Use of this token implies that the
+ text to be filtered is not piped into standard input of the
+ executed command and its standard output is ignored. _Alpine_
+ restores the tty modes before invoking the filter in case the
+ filter interacts with the user via its own standard input and
+ output.
+
+ __RESULTFILE__
+ When the command is executed, this token is replaced with the
+ path and name of a temporary file intended to contain a status
+ message from the filter. _Alpine_ displays this in the message
+ status field.
+
+ __DATAFILE__
+ When the command is executed, this token is replaced in the
+ command line with the path and name of a temporary file that
+ _Alpine_ creates once per session and deletes upon exit. The
+ file is intended to be used by the filter to store state
+ information between instances of the filter.
+
+ __PREPENDKEY__
+ When the command is executed, this token indicates that a
+ random number will be passed down the input stream before the
+ message text. It is not included as a command-line argument.
+ This number could be used as a session key. It is sent in this
+ way to improve security. The number is unique to the current
+ _Alpine_ session and is only generated once per session.
+
+ __INCLUDEALLHDRS__
+ When the command is executed, this token indicates that the
+ headers of the message will be passed down the input stream
+ before the message text. It is not included as a command-line
+ argument. The filter should, of course, remove the headers
+ before returning control to _Alpine_.
+
+ __MIMETYPE__
+ When the command is executed, this token is replaced in the
+ command name with a temporary file name used to accept any new
+ MIME Content-Type information necessitated by the output of the
+ filter. Upon the filter's exit, if the file contains new MIME
+ type information, _Alpine_ verifies its format and replaces the
+ outgoing message's MIME type information with that contained in
+ the file. This is basically a cheap way of sending something
+ other than Text/Plain.
+
+ _sendmail-path_
+ This names the path to an alternative program, and any necessary
+ arguments, to be used in posting mail messages. See the section on
+ SMTP and Sendmail for more details.
+ _signature-file_
+ This is the name of a file which will be automatically inserted into
+ outgoing messages. It typically contains information such as your
+ name, email address and organizational affiliation. _Alpine_ adds the
+ signature into the message as soon as you enter the composer so you
+ can choose to remove it or edit it on a message by message basis.
+ Signature file placement in message replies is controlled by the
+ signature-at-bottom setting in the feature list.
+ This defaults to ~/.signature on UNIX and <PINERC directory>\PINE.SIG
+ on a PC.
+ To create or edit your signature file choose Setup from the Main Menu
+ and then select S for Signature (Main/Setup/Signature). This puts you
+ into the Signature Editor where you can enter a _few_ lines of text
+ containing your identity and affiliation.
+ If the filename is followed by a vertical bar (|) then instead of
+ reading the contents of the file the file is assumed to be a program
+ which will produce the text to be used on its standard output. The
+ program can't have any arguments and doesn't receive any input from
+ _Alpine_, but the rest of the processing works as if the contents came
+ from a file.
+ Instead of storing the data in a local file, the signature data may
+ be stored remotely in an IMAP folder. In order to do this, you must
+ use a remote name for the file. A remote signature-file name might
+ look like:
+
+ {myimaphost.myschool.k12.wa.us}mail/signature
+ or, if you have an SSL-capable version of _Alpine_, you might try
+
+ {myimaphost.myschool.k12.wa.us/user=loginname/ssl}mail/signature
+ The syntax used here is the same as the syntax used for remote
+ configuration files from the command line. Note that you may not
+ access an existing signature file remotely, you have to create a new
+ _folder_ which contains the signature data. If the name you use here
+ for the signature file is a remote name, then when you edit the file
+ from the Setup/Signature command the data will be stored remotely in
+ the folder. You aren't required to do anything special to create the
+ folder, it gets created automatically if you use a remote name.
+ Besides regular text, the signature file may also contain (or a
+ signature program may produce) tokens which are replaced with text
+ which usually depends on the message you are replying to or
+ forwarding. For example, if the signature file contains the token
+
+ _DATE_
+ anywhere in the text, then that token is replaced by the date the
+ message you are replying to or forwarding was sent. If it contains
+
+ _CURDATE_
+ that is replaced with the current date. The first is an example of a
+ token which depends on the message you are replying to (or
+ forwarding) and the second is an example which doesn't depend on
+ anything other than the current date. You have to be a little careful
+ with this facility since tokens which depend on the message you are
+ replying to or forwarding will be replaced by nothing in the case
+ where you are composing a new message from scratch. The use of roles
+ may help you in this respect. It allows you to use different
+ signature files in different cases.
+ The list of tokens available for use in the signature file is here.
+ Instead of, or along with the use of _roles_ to give you different
+ signature files in different situations, there is also a way to
+ conditionally include text based on whether or not a token would
+ result in specific replacement text. For example, you could include
+ some text based on whether or not the _NEWS_ token would result in
+ any newsgroups if it was used. This is explained in detail here. This
+ isn't for the faint of heart.
+ In the very unlikely event that you want to include a literal token
+ in the signature you must precede it with a backslash character. For
+ example,
+
+ \_DAYDATE_ = _DAYDATE_
+ would produce something like
+
+ _DAYDATE_ = Sat, 24 Oct 1998
+ It is not possible to have a literal backslash followed by an
+ expanded token.
+ _signature-background-color_
+ _signature-foreground-color_
+ Signature Color.
+ _smime-public-cert-directory_
+ UNIX _Alpine_ only.
+ If the option smime-public-cert-container is set then this option
+ will have no effect.
+ Normally, Public Certificates for use with S/MIME will be stored in
+ the directory which is the value of this option. Those certificates
+ will be stored in PEM format, one certificate per file. The name of
+ the file for the certificate corresponding to
+
+ emailaddress
+ should be
+
+ emailaddress.crt
+ For example, a file for user@example.com would be in the file
+
+ user@example.com.crt
+ in this directory.
+ Use the Setup/SMIME screen to modify this variable.
+ Typically, the public certificates that you have will come from
+ S/MIME signed messages that are sent to you. _Alpine_ will extract
+ the public certificate from the signed message and store it in the
+ certificates directory. These PEM format public certificates look
+ something like:
+-----BEGIN CERTIFICATE-----
+MIIFvTCCBKWgAwIBAgIQD4fYFHVI8T20yN4nus097DANBgkqhkiG9w0BAQUFADCB
+rjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
+Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
+...
+2b9KGqDyMWW/rjNnmpjzjT2ObGM7lRA8lke4FLOLajhrz4ogO3b4DFfAAM1VSZH8
+D6sOwOLJZkLY8FRsfk63K+2EMzA2+qAzMKupgeTLqXIf
+-----END CERTIFICATE-----
+ + General S/MIME Overview
+ This option is displayed as "S/MIME - Public Cert Directory".
+ _smime-public-cert-container_
+ UNIX _Alpine_ only.
+ If this option is set it will be used instead of
+ smime-public-cert-directory
+ This option gives you a way to store certificates remotely on an IMAP
+ server instead of storing the certificates one per file locally. In
+ order to do that you just give this option a remote folder name for a
+ folder which does not yet exist. The name is similar to the name you
+ might use for a remote configuration file. A remote folder name might
+ look something like:
+
+ {myimaphost.myschool.k12.wa.us}mail/publiccerts
+ Use the Setup/SMIME screen to modify this variable.
+ + General S/MIME Overview
+ This option is displayed as "S/MIME - Public Cert Container".
+ _smime-private-key-directory_
+ UNIX _Alpine_ only.
+ In order to sign outgoing S/MIME messages you will need a personal
+ digital ID certificate. You will usually get such a certificate from
+ a certificate authority such as Thawte or CAcert. (In order to
+ encrypt outgoing messages you don't need a personal digital ID, you
+ need the public certificate of the recipient instead.) If the option
+ smime-private-key-container is set then this option will have no
+ effect.
+ Normally, Private Keys for use with S/MIME will be stored in the
+ directory which is the value of this option. Those certificates will
+ be stored in PEM format, one certificate per file. The name of the
+ file for the certificate corresponding to your
+
+ emailaddress
+ should be
+
+ emailaddress.key
+ For example, if your address is user@example.com the name of the file
+ would be
+
+ user@example.com.key
+ in this directory.
+ Use the Setup/SMIME screen to modify this variable.
+ Typically, the private key that you have will come from a Certificate
+ Authority. The private key should be stored in a PEM format file that
+ looks something like:
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,2CBD328FD84CF5C6
+
+YBEXYLgLU9NJoc1V+vJ6UvcF08RX54S6jXsmgL0b5HGkudG6fhnmHkH7+UCvM5NI
+SXO/F8iuZDfs1VGG0NyitkFZ0Zn2vfaGovBvm15gx24b2xnZDLRB7/bNZkurnK5k
+VjAjZ2xXn2hFp2GJwqRdmxYNqsKGu52B99oti5HUWuZ2GFRaWjn5hYOqeApZE2uA
+...
+oSRqfI51UdSRt0tmGhHeTvybUVrHm9eKft8TTGf+qSBqzSc55CsmoVbRzw4Nfhix
+m+4TJybNGNfAgOctSkEyY/OCb49fRRQTCBZVIhzLGGmpYmkO55HbIA==
+-----END RSA PRIVATE KEY-----
+ + General S/MIME Overview
+ This option is displayed as "S/MIME - Private Key Directory".
+ _smime-private-key-container_
+ UNIX _Alpine_ only.
+ If this option is set it will be used instead of
+ smime-private-key-directory.
+ This option gives you a way to store keys remotely on an IMAP server
+ instead of storing the keys one per file locally. In order to do that
+ you just give this option a remote folder name for a folder which
+ does not yet exist. The name is similar to the name you might use for
+ a remote configuration file. A remote folder name might look
+ something like:
+
+ {myimaphost.myschool.k12.wa.us}mail/privatekeys
+ Use the Setup/SMIME screen to modify this variable.
+ + General S/MIME Overview
+ This option is displayed as "S/MIME - Private Key Container".
+ _smime-cacert-directory_
+ UNIX _Alpine_ only.
+ If the option smime-cacert-container is set then this option will
+ have no effect.
+ CACert is a shorthand name for certification authority certificate.
+ Normally _Alpine_ will use the CACerts that are located in the
+ standard system location for CACerts. It may be the case that one of
+ your correspondents has a Digital ID which has been signed by a
+ certificate authority that is not in the regular set of system
+ certificate authorities. You may supplement the system list by adding
+ further certificates of your own. These should be stored in the
+ directory which is the value of this option. The certificates will be
+ stored in PEM format, one certificate per file. The names of the
+ files can be anything ending in ".crt".
+ Use the Setup/SMIME screen to modify this variable.
+ These PEM format CA certificates look very similar to your public
+ certificates for particular email addresses
+ (smime-public-cert-directory).
+ + General S/MIME Overview
+ This option is displayed as "S/MIME - Cert Authority Directory".
+ _smime-cacert-container_
+ UNIX _Alpine_ only.
+ If this option is set it will be used instead of
+ smime-cacert-directory.
+ This option gives you a way to store certificates remotely on an IMAP
+ server instead of storing the certificates one per file locally. In
+ order to do that you just give this option a remote folder name for a
+ folder which does not yet exist. The name is similar to the name you
+ might use for a remote configuration file. A remote folder name might
+ look something like:
+
+ {myimaphost.myschool.k12.wa.us}mail/cacerts
+ Use the Setup/SMIME screen to modify this variable.
+ + General S/MIME Overview
+ This option is displayed as "S/MIME - Cert Authority Container".
+ _smtp-server_
+ One or more SMTP servers (host name or IP address) which _Alpine_
+ will use for outgoing mail. If not set, _Alpine_ passes outgoing
+ email to the _sendmail_ program on the local machine. _PC-Alpine_
+ users must have this variable set in order to send mail as they have
+ no _sendmail_ program.
+ Your SMTP server may offer SMTP AUTH authentication. It may even
+ require it. If your SMTP server offers SMTP AUTH authentication you
+ may specify a "user" name parameter to cause _Alpine_ to attempt to
+ authenticate. This parameter requires an associated value, the
+ username identifier with which to establish the server connection. An
+ example might be:
+
+ smtpserver.example.com/user=katie
+ If AUTH authentication is offered by the server, this will cause
+ _Alpine_ to attempt to use it. If AUTH authentication is not offered
+ by the server, this will cause _Alpine_ to fail sending with an error
+ similar to:
+
+ Error: SMTP authentication not available
+ Another type of authentication that is used by some ISPs is called
+ "POP before SMTP" or "IMAP before SMTP", which means that you have to
+ authenticate yourself to the POP or IMAP server by opening a mailbox
+ before you can send mail. To do this, you usually only have to open
+ your INBOX.
+ You may tell _Alpine_ to use the Message Submission port (587)
+ instead of the SMTP port (25) by including the "submit" parameter in
+ this option. At this time "/submit" is simply equivalent to
+ specifying port 587, though it may imply more than that at some point
+ in the future. Some ISPs are blocking port 25 in order to reduce the
+ amount of spam being sent to their users. You may find that the
+ submit option allows you to get around such a block.
+
+ smtpserver.example.com/submit
+ To specify any non-standard port number on the SMTP server you may
+ follow the hostname with a colon followed by the portnumber.
+
+ smtpserver.example.com:12345
+ Normally, when a connection is made to the Smtp-Server _Alpine_ will
+ attempt to negotiate a secure (encrypted) session using Transport
+ Layer Security (TLS). If that fails then a non-encrypted connection
+ will be attempted instead. You may specify that a TLS connection is
+ required if you wish. If you append "/tls" to the name then the
+ connection will fail instead of falling back to a non-secure
+ connection.
+
+ smtpserver.example.com/tls
+ See the SMTP Servers section or the Server Name Syntax section for
+ some more details.
+ This option is displayed as "SMTP Server (for sending)".
+ _sort-key_
+ This variable sets up the default Message Index sorting. The default
+ is to sort by arrival order (the order the messages arrived in the
+ folder). It has the same functionality as the _-sort_ command line
+ argument and the _$_ command in the "Folder Index". If a _sort-key_
+ is set, then all folders open during the session will have that as
+ the default sort order.
+ _speller_
+ UNIX _Alpine_ only.
+ For _PC-Alpine_, you must install the aspell library code that you
+ may get from http://aspell.net/win32/.
+ This option affects the behavior of the _^T_ (spell check) command in
+ the Composer. It specifies the program invoked by _^T_ in the
+ Composer. By default, _Alpine_ uses the system's "spell" command.
+ _Alpine_ will use the command defined by this option (if any) instead.
+ When invoking the spell-checking program, _Alpine_ appends a tempfile
+ name (where the message is passed) to the command line. _Alpine_
+ expects the speller to correct the spelling in that file. When you
+ exit from the speller program _Alpine_ will read the tmpfile back
+ into the composer.
+ For Unix _Alpine_ the program _ispell_ works well as an alternate
+ spell checker. If your Unix system has _ispell_ it is probably
+ reasonable to make it the default speller by configuring it as the
+ default in the system configuration file, /usr/local/lib/pine.conf.
+ If this option is not set, then the system's _spell_ command is used.
+ The spell command does not work the same as the alternate speller. It
+ produces a list of misspelled words on its standard output, instead,
+ and doesn't take a tempfile as an argument. Don't set this speller
+ option to the standard Unix spell command. That won't work. If you
+ want to use the standard Unix spell command, set the speller option
+ to nothing.
+ _ssh-command_
+ Sets the format of the command used to open a UNIX secure shell
+ connection. The default is "%s %s -l %s exec /etc/r%sd". All four
+ "%s" entries MUST exist in the provided command. The first is for the
+ command's pathname, the second is for the host to connnect to, the
+ third is for the user to connect as, and the fourth is for the
+ connection method (typically imap).
+ _ssh-open-timeout_
+ Sets the time in seconds that _Alpine_ will attempt to open a UNIX
+ secure shell connection. The default is 15, the minimum non-zero
+ value is 5, and the maximum is unlimited. If this is set to zero ssh
+ connections will be completely disabled.
+ _ssh-path_
+ Sets the name of the command used to open a UNIX secure shell
+ connection. The default is typically /usr/bin/ssh.
+ _standard-printer_
+ System-wide configuration file only. Specifies a list of commands for
+ category 2 of the _Setup/Printer_ screen, the standard print command
+ section. This is not used by _PC-Alpine_.
+ _status-background-color_
+ _status-foreground-color_
+ Status Color.
+ _status-message-delay_
+ This option has evolved over time, causing the possible values to be
+ counter-intuitive. Read carefully before you set this option. First
+ we explain what the option does, then there is a longer discussion
+ following that.
+ If this is set to zero, the default value, it has _no_ effect.
+ Positive and negative values serve two similar, but different
+ purposes.
+ If it is set to a positive number, it causes the cursor to move to
+ the status line whenever a status message is printed and pause there
+ for this many seconds. It will probably only be useful if the
+ show-cursor feature is also turned on. Setting this option to a
+ postive number can only be used to _increase_ the status message
+ delay. This may be useful for Braille displays, or other
+ non-traditional displays.
+ If it is set to a negative number the interpretation is a bit
+ complicated. Negative numbers are used to _decrease_ the amount of
+ delay _Alpine_ uses to allow you to read important status messages.
+ Of course, this may cause you to miss some important messages. If you
+ see a message flash by but miss what it says you can use the Journal
+ command from the Main menu to read it. If you set this option to a
+ negative value, the delay will be no more than one second less than
+ the absolute value of the value you set. So if you set it to -1, the
+ delay will be no more than zero seconds, no delay at all. If you set
+ it to -2, the delay will be no more than 1 second. And so on, -3 is 2
+ seconds, -4 is 3 seconds, ... If the delay that _Alpine_ would have
+ used by default is less than this delay, then the smaller delay set
+ by _Alpine_ will be used. Setting this option to a negative value can
+ only reduce the amount of delay, never increase it.
+ Here is a more detailed explanation. Status messages are the messages
+ which show up spontaneously in the status message line, the third
+ line from the bottom of the screen. By default, _Alpine_ assigns each
+ status message it produces a minimum display time. Some status
+ messages have a minimum display time of zero. You can see an example
+ of such a message by paging up in this help text until you reach the
+ top of the screen. If you try to page past the top you will see the
+ message
+
+ [Already at start of help text]
+ in the status line. If there is another more important use of the
+ status message line this message might be replaced quickly, or it
+ even might not be shown at all. However, if there is no reason to get
+ rid of the message, it might stay there for several seconds while you
+ read the help. An example where it is replaced immediately happens
+ when you page up in the help text past the top of the screen, but
+ then type the "WhereIs" command right after paging up. The message
+ will disappear immediately without causing a delay (unless you have
+ set this option to a positive value) to allow you to type input for
+ the "WhereIs" command. Since it isn't a very important message,
+ _Alpine_ has set its minimum display time to zero seconds.
+ Other messages have minimum display times of three or more seconds.
+ These are usually error messages that _Alpine_ thinks you ought to
+ see. For example, it might be a message about a failed Save or a
+ failed folder open. It is often the case that this minimum display
+ time won't delay you in any way because the status message line is
+ not needed for another reason. However, there are times when _Alpine_
+ has to delay what it is doing in order to display a status message
+ for the minimum display time. This happens when a message is being
+ displayed and _Alpine_ wants to ask for input from the keyboard. For
+ example, when you Save a message you use the status message line. You
+ get a prompt there asking for the name of the folder to save to. If
+ there is a status message being displayed that has not yet displayed
+ for its minimum time _Alpine_ will display that status message
+ surrounded with the characters > and < to show you that it is
+ delaying. That might happen, for example, if you tried to save to a
+ folder that caused an error, then followed that immediately with
+ another Save command. You might find yourself waiting for a status
+ message like
+
+ [>Can't get write access to mailbox, access is readonly<]
+ to finish displaying for three seconds. If that is something you find
+ happening to you frequently, you may use negative values of this
+ option to decrease or eliminate that delay, at the risk of missing
+ the message.
+ _stay-open-folders_
+ This option affects low-level behavior of _Alpine_. There is no
+ default value for this option. It is related to the options
+ Preopen-Stayopen-Folders, Max-Remote-Connections, and
+ offer-expunge-of-Stayopen-Folders.
+ Note: changes made to this list take effect the next time you open a
+ folder in the list.
+ This is a list of folders that will be permanently kept open once
+ they are first opened. The names in this list may be either the
+ nickname of an Incoming folder or the full technical specification of
+ a folder. The folders in this list need not be remote IMAP folders,
+ they could usefully be local folders, as well. If a folder in the
+ list is a newsgroup or is not accessed either locally or via IMAP,
+ then the entry will be ignored. For example, folders accessed via
+ NNTP or POP3 will not be kept open, since the way that new mail is
+ found with those protocols involves closing and reopening the
+ connection.
+ Once a Stay Open folder has been opened, new-mail checking will
+ continue to happen on that folder for the rest of the _Alpine_
+ session. Your INBOX is always implicitly included in this Stay-Open
+ list and doesn't need to be added explicitly.
+ Another difference that you may notice between a Stay Open folder and
+ a non-Stay Open folder is which message is selected as the current
+ message when you enter the folder index. Normally, the starting
+ position for an incoming folder (which most Stay Open folders will
+ likely be) is controlled by the Incoming-Startup-Rule. However, if a
+ folder is a Stay Open folder, when you re-enter the folder after the
+ first time the current message will be the same as it was when you
+ left the folder. An exception is made if you use the TAB command to
+ get to the folder. In that case, the message number will be
+ incremented by one from what it was when you left the folder.
+ The above special behavior is thought to be useful. However, it is
+ special and different from what you might at first expect. The
+ feature Use-Regular-Startup-Rule-for-Stayopen-Folders may be used to
+ turn off this special treatment.
+ If the message that was current when you left the folder no longer
+ exists, then the regular startup rule will be used instead.
+ This option is displayed as "Stayopen Folders".
+ _tcp-open-timeout_
+ Sets the time in seconds that _Alpine_ will attempt to open a network
+ connection. The default is 30, the minimum is 5, and the maximum is
+ system defined (typically 75). If a connection has not completed
+ within this many seconds _Alpine_ will give up and consider it a
+ failed connection.
+ _tcp-query-timeout_
+ When _Alpine_ times out a network read or write it will normally just
+ display a message saying "Still waiting". However, if enough time has
+ elapsed since it started waiting it will offer to let you break the
+ connection. That amount of time is set by this option, which defaults
+ to 60 seconds, has a minimum of 5 seconds, and a maximum of 1000
+ seconds.
+ _tcp-read-warning-timeout_
+ Sets the time in seconds that _Alpine_ will wait for a network read
+ before warning you that things are moving slowly and possibly giving
+ you the option to break the connection. The default is 15 seconds.
+ The minimum is 5 seconds and the maximumn is 1000 seconds.
+ _tcp-write-warning-timeout_
+ Sets the time in seconds that _Alpine_ will wait for a network write
+ before warning you that things are moving slowly and possibly giving
+ you the option to break the connection. The default is 0 which means
+ it is unset. If set to a non-zero value, the minimum is 5 and the
+ maximum is 1000.
+ _threading-display-style_
+ When a folder is sorted by Threads or OrderedSubject, this option
+ will affect the MESSAGE INDEX display. By default, _Alpine_ will
+ display the MESSAGE INDEX in the "show-thread-structure" style if a
+ folder is sorted by Threads or OrderedSubject. The possible values
+ are:
+
+ _none_
+ Regular index display. The same index line as would be
+ displayed without threading is used. The only difference will
+ be in the order of the messages.
+
+ _show-thread-structure_
+ Threaded Subjects will be indented and vertical bars and
+ horizontal lines will be added to make it easier to see the
+ relationships among the messages in a thread (a conversation).
+
+ _mutt-like_
+ This is the same as the option above except that the Subject is
+ suppressed (is blank) if it matches the previous Subject in the
+ thread. The name comes from the email client Mutt. Here is an
+ example of what a mutt-like index might look like. In this
+ example, the first column represents the message number, the
+ threading-index-style is set to
+ "regular-index-with-expanded-threads", and the
+ Threading-Lastreply-Character is set to a backslash:
+
+ 1 Some topic
+ 2 . Subject original message in thread
+ 3 |-> reply to 2
+ 4 . |-> another reply to 2
+ 5 . | \-> reply to 4
+ 6 . | \-> reply to 5
+ 7 | \-> reply to 6
+ 8 |-> another reply to 2
+ 9 . |->New subject another reply to 2 but with a New subject
+ 10 | |-> reply to 9
+ 11 | \-> another reply to 9
+ 12 | \-> reply to 11
+ 13 \-> final reply to 2
+ 14 Next topic
+
+ _indent-subject-1_
+ Threaded Subjects will be indented one space per level of the
+ conversation. The bars and lines that show up in the
+ show-thread-structure display will not be there with this
+ style.
+
+ _indent-subject-2_
+ Same as above but indent two spaces per level instead of one
+ space.
+
+ _indent-from-1_
+ Similar to indent-subject-1, except that instead of indenting
+ the Subject field one space the From field of a thread will be
+ indented one space per level of the conversation.
+
+ _indent-from-2_
+ Same as above but indent two spaces per level instead of one
+ space.
+
+ _show-structure-in-from_
+ The structure of the thread is illustrated with indenting,
+ vertical bars, and horizontal lines just like with the
+ show-thread-structure option, but the From field is used to
+ show the relationships instead of the Subject field.
+
+ _threading-expanded-character_
+ The Threading-Expanded-Character option has a small effect on the
+ MESSAGE INDEX display when using a threading-display-style other than
+ _none_. The value of this option is a single character. This
+ character is used to indicate that part of a thread has been expanded
+ and could be collapsed if desired with the "/" Collapse/Expand
+ command. By default, the value of this option is a dot (.).
+ If this option is set to the Empty Value, then the column (and the
+ following blank column) will be deleted from the display.
+ This option is closely related to the threading-indicator-character
+ option. Another similar option which affects the thread display is
+ the threading-lastreply-character option.
+ _threading-index-style_
+ When a folder is sorted by Threads or OrderedSubject, this option
+ will affect the INDEX displays. The possible values are:
+
+ _regular-index-with-expanded-threads_
+ This is the default display. If the configuration option
+ threading-display-style is set to something other than "none",
+ then this setting will cause _Alpine_ to start off with a
+ MESSAGE INDEX with all of the threads expanded. That is, each
+ message will have a line in the MESSAGE INDEX display. The
+ Collapse/Expand command (/) may be used to manually collapse or
+ expand a thread or subthread (see also
+ slash-collapses-entire-thread).
+
+ This setting affects the display when the folder is first
+ threaded. The collapsed state may also be re-initialized by
+ re-sorting the folder manually using the SortIndex command ($).
+ After re-sorting the threads will once again all be expanded,
+ even if you have previously collapsed some of them.
+
+ If "threading-display-style" is set to "none", then the display
+ will be the regular default _Alpine_ MESSAGE INDEX, but sorted
+ in a different order.
+
+ _regular-index-with-collapsed-threads_
+ If the configuration option threading-display-style is set to
+ something other than "none", then this setting will cause
+ _Alpine_ to start out with all of the threads collapsed instead
+ of starting out with all of the threads expanded. The
+ Collapse/Expand command (/) may be used to manually collapse or
+ expand a thread or subthread (see also
+ slash-collapses-entire-thread).
+
+ This setting affects the display when the folder is first
+ threaded. The collapsed state may also be re-initialized by
+ re-sorting the folder manually using the SortIndex command ($).
+ After re-sorting the threads will once again all be collapsed,
+ even if you have previously expanded some of them.
+
+ _separate-index-screen-always_
+ With this setting and the next, you will see an index of
+ threads instead of an index of messages, provided you have
+ sorted by Threads or OrderedSubject.
+
+ The THREAD INDEX contains a '*' in the first column if any
+ message in the thread is marked Important. If not, it contains
+ a '+' if any message in the thread is to you. The second column
+ is blank. The third column contains a 'D' if all of the
+ messages in the thread are deleted. Otherwise, it contains an
+ 'N' if any of the messages in the thread are New.
+
+ When you view a particular thread from the THREAD INDEX you
+ will be in the MESSAGE INDEX display but the index will only
+ contain messages from the thread you are viewing.
+
+ _separate-index-screen-except-for-single-messages_
+ This is very similar to the option above. When you are in the
+ THREAD INDEX, one of the available commands is "ViewThd". With
+ the setting "separate-index-screen-always" (the option above)
+ when you view a particular thread you will be in the MESSAGE
+ INDEX display and the index will only contain messages from the
+ thread you are viewing. If the thread you are viewing consists
+ of a single message, the MESSAGE INDEX will be an index with
+ only one message in it. If you use this
+ "separate-index-screen-except-for-single-messages" setting
+ instead, then that index which contains a single message will
+ be skipped and you will go directly from the THREAD INDEX into
+ the MESSAGE TEXT screen.
+
+ _threading-indicator-character_
+ The Threading-Indicator-Character option has a small effect on the
+ MESSAGE INDEX display when using a threading-display-style other than
+ _none_ and sorting by Threads or OrderedSubject. The value of this
+ option is a single character. This character is used to indicate that
+ part of a thread (a conversation) is hidden beneath a message. The
+ message could be expanded if desired with the "/" Collapse/Expand
+ command. By default, the value of this option is the greater than
+ sign (>).
+ If this option is set to the Empty Value, then the column (and the
+ following blank column) will be deleted from the display.
+ This option is closely related to the threading-expanded-character
+ option. Another similar option which affects the thread display is
+ the threading-lastreply-character option.
+ _threading-lastreply-character_
+ The Threading-Lastreply-Character option has a small effect on the
+ MESSAGE INDEX display when using a threading-display-style of
+ _show-thread-structure_, _mutt-like_, or _show-structure-in-from_; and
+ sorting by Threads or OrderedSubject. The value of this option is a
+ single character. This character is used instead of the vertical line
+ character when there are no more replies directly to the parent of
+ the current message. It can be used to "round-off" the bottom of the
+ vertical line by setting it to a character such as a backslash (\) or
+ a backquote (`). The default value of this option is the backslash
+ character (\). This option may not be set to the Empty Value. In that
+ case, the default will be used instead.
+ This option is displayed as "Threading Last Reply Character".
+ _title-background-color_
+ _title-foreground-color_
+ Title Color.
+ _title-closed-background-color_
+ _title-closed-foreground-color_
+ Title-closed Color.
+ _titlebar-color-style_
+ titlebar-color-style.
+ _unknown-character-set_
+ A text message should either be made up of all US-ASCII characters or
+ it should contain a charset label which tells the software which
+ character set encoding to use to interpret the message. Sometimes a
+ malformed message may be unlabeled but contain non-ascii text. This
+ message is outside of the standards so any attempt to read it could
+ fail. When _Alpine_ attempts to read such a message it will try to
+ interpret the text in the character set you specify here. For
+ example, if you have correspondents who send you unlabeled messages
+ that are usually made up of characters from the WINDOWS-1251
+ character set, setting this unknown-character-set to WINDOWS-1251
+ will allow you to read those messages. Of course, if the unlabeled
+ message is actually in some other character set, then you may see
+ garbage on your screen.
+ In the Setup/Config screen you may choose from a list of all the
+ character sets _Alpine_ knows about by using the "T" ToCharsets
+ command.
+ _upload-command_
+ This option affects the behavior of the Composer's _^R_ (Read File)
+ and _^J_ (Attach File, in the header) commands. It specifies a Unix
+ program name, and any necessary command line arguments, that _Alpine_
+ can use to transfer files from your personal computer into messages
+ that you are composing.
+ _upload-command-prefix_
+ This option is used in conjunction with the _upload-command_ option.
+ It defines text to be written to the terminal emulator (via standard
+ output) immediately prior to starting the upload command. This is
+ useful for integrated serial line file transfer agents that permit
+ command passing (e.g., Kermit's APC method).
+ _url-viewers_
+ List of programs to use to open Internet URLs. This value affects
+ _Alpine_'s handling of URLs that are found in the text of messages you
+ read. Normally, only URLs _Alpine_ can handle directly are
+ automatically offered for selection in the "Message Text" screen.
+ When one or more comma delimited Web browsers capable of deciphering
+ URLs on their command line are added here, _Alpine_ will choose the
+ first available browser to display URLs it doesn't recognize.
+ Additionally, to support various connection methods and browsers,
+ each entry in this list can begin with the special token
+ _TEST(test-string)_. The test-string is a shell command that _Alpine_
+ will run and which must exit with a status of zero for _Alpine_ to
+ consider that browser for use (the other criteria is that the browser
+ must exist as a full path or a path relative to your home directory).
+ Now for an example:
+
+ url-viewers=_TEST("test -n '${DISPLAY}'")_ /usr/local/bin/netscape,
+ /usr/local/bin/lynx, C:\BIN\NETSCAPE.BAT
+ This example shows that for the first browser in the list to be used
+ the environment variable DISPLAY must be defined. If it is, then the
+ file /usr/local/bin/netscape must exist. If either condition is not
+ met, then the file /usr/local/bin/lynx must exist. If it doesn't,
+ then the final path and file must exist. Note that the last entry is
+ a DOS/Windows path. This is one way to support _Alpine_ running on
+ more than one architecture with the same configuration file.
+ _use-only-domain-name_
+ Can be set to _yes_ or _no._ Anything but _yes_ means _no._ If set to
+ _yes_ the first label in the host name will be lopped off to get the
+ domain name and the domain name will be used for outgoing mail and
+ such. That is, if the host name is _carson.u.example.edu_ and this
+ variable is set to _yes,_ then _u.example.edu_ will be used on
+ outgoing mail. Only meaningful if user-domain is NOT set.
+ _user-domain_
+ Sets the domain or host name for the user, overriding the system host
+ or domain name. See the domain name section. The easiest way to
+ change the full From address is with the customized-hdrs variable.
+ _user-id_
+ _PC-Alpine_ only and personal configuration file only. Sets the
+ username that is placed on all outgoing messages. The username is the
+ part of the address that comes before the "@". The easiest way to
+ change the full From address is with the customized-hdrs variable.
+ _user-input-timeout_
+ If this is set to an integer greater than zero, then this is the
+ number of _hours_ to wait for user input before _Alpine_ times out.
+ If _Alpine_ is in the midst of composing a message or is waiting for
+ user response to a question, then it will not timeout. However, if
+ _Alpine_ is sitting idle waiting for the user to tell it what to do
+ next and the user does not give any input for this many hours,
+ _Alpine_ will exit. No expunging or moving of read messages will take
+ place. It will exit similarly to the way it would exit if it received
+ a hangup signal. This may be useful for cleaning up unused _Alpine_
+ sessions which have been forgotten by their owners. The _Alpine_
+ developers envision system administrators setting this to a value of
+ several hours (24?) so that it won't surprise a user who didn't want
+ to be disconnected.
+ _viewer-hdr-colors_
+ This variable holds the optional Header Colors and patterns which
+ have been defined by the user. This is usually modified by using the
+ Header Colors section of the Setup Color screen.
+ _viewer-hdrs_
+ You may change the default list of headers that are viewed by listing
+ the headers you want to view here. If the headers in your
+ _viewer-hdrs_ list are present in the message, then they will be
+ shown. The order of the headers you list will also be honored. If the
+ special value _all-except_ is included as the first header in the
+ _viewer-hdrs_ list, then all headers in the message except those in
+ the list will be shown. The values are all case insensitive.
+ This option is displayed as "Viewer Headers".
+ _viewer-margin-left_
+ This variable controls the left-hand vertical margin's width in
+ _Alpine_'s Message Viewing screen. Its value is the number of space
+ characters preceding each displayed line. For consistency with
+ Viewer-Margin-Right, you may specify the column number to start in
+ (column numbering begins with number 1) instead of the width of the
+ margin by appending a lower case letter "c" to the number. For
+ example, a value of "2c" means to start the text in column two, which
+ is entirely equivalent to a value of "1", which means to leave a
+ margin of 1 space.
+ The default is a left margin of 0 (zero). Misconfigurations (for
+ example, negative values or values with starting left columns greater
+ than the ending right column) are silently ignored. If the number of
+ columns for text between the Viewer-Margin-Left and the
+ Viewer-Margin-Right is fewer than 8, then margins of zero will be
+ used instead.
+ _viewer-margin-right_
+ This variable controls the right-hand vertical margin's width in
+ _Alpine_'s Message Viewing screen. Its value is the number of space
+ characters following each displayed line. You may specify the column
+ number to end the text in (column numbering begins with number 1)
+ instead of the width of the margin by appending a lower case letter
+ "c" to the number. For example, a value of "76c" means to end the
+ text in column 76. If the screen is 80 characters wide, this is
+ equivalent to a value of "4", which means to leave a margin of 4
+ spaces. However, if you use different size screens at different
+ times, then these two values are not equivalent.
+ The default right margin is 4. Misconfigurations (for example,
+ negative values or values with starting left columns greater than the
+ ending right column) are silently ignored. If the number of columns
+ for text between the Viewer-Margin-Left and the Viewer-Margin-Right
+ is fewer than 8, then margins of zero will be used instead.
+ _viewer-overlap_
+ This option specifies an aspect of _Alpine_'s Message Viewing screen.
+ When the space bar is used to page forward in a message, the number
+ of lines specified by the _viewer-overlap_ variable will be repeated
+ from the bottom of the screen. That is, if this was set to two lines,
+ then the bottom two lines of the screen would be repeated on the top
+ of the next screen. The normal default value is "2".
+ _window-position_
+ Winsock version of _PC-Alpine_ only. Window position in the format:
+ CxR+X+Yn Where C and R are the window size in characters and X and Y
+ are the screen position of the top left corner of the window.
+ _________________________________________________________________
+
+Configuration Features
+
+ There are several features (options) which may be turned off or on. The
+ configuration variable feature-list is a list of all the features that are
+ turned on or off. If the name of a feature is in the list it will be turned
+ on. If the name of a feature with the characters no- prepended is in the
+ list, it will turn the feature off. This is useful for overriding
+ system-wide defaults. This is because, unlike all the other configuration
+ variables, the _feature-list_ is additive. That is, first the system-wide
+ _feature-list_ is read and then the user's _feature-list_ is read. This makes
+ it possible for the system manager to turn some of the features on by
+ default while still allowing the user to cancel that default. For example,
+ if the system manager has turned on the _allow-talk_ feature by default then
+ a user may turn it back off by including the feature _no-allow-talk_ in his
+ or her personal configuration file. Of course, these details are usually
+ handled by _Alpine_ when the user turns an option on or off from inside the
+ _Setup/Config_ screen.
+
+ System managers should take some care when turning on features by default.
+ Some of the documentation assumes that all of the features are off by
+ default, so it could be confusing for a user if some are on by default
+ instead. Feature names are case-independent.
+
+ Here is an alphabetical list of possible features.
+ _allow-changing-from_
+ Prior to _Pine_ 4.00 there was a _compile_-time option called
+ ALLOW_CHANGING_FROM. That has been replaced by a _runtime_ feature.
+ If this feature is turned on then the From line can be changed just
+ like all the other header fields that can be changed. See the
+ configuration variables customized-hdrs and default-composer-hdrs for
+ more information on editing headers.
+ The default value for this feature is ON, so that editing of From
+ headers is allowed by default.
+ _allow-talk_
+ Unix _Alpine_ only. By default, permission for others to _talk_ to
+ your terminal is turned off when you are running _Alpine_. When this
+ feature is set, permission is instead turned on.
+ Note: The _talk_ program has nothing to do with _Alpine_ or email.
+ The _talk_ daemon on your system will attempt to print a message on
+ your screen when someone else is trying to contact you. If you wish
+ to see these messages while you are running _Alpine_, you should
+ enable this feature.
+ If you do enable this feature and see a _talk_ message, you must
+ suspend or quit _Alpine_ before you can respond.
+ _alternate-compose-menu_
+ This feature controls the menu that is displayed when Compose is
+ selected. If set, a list of options will be presented, with each
+ option representing the type of composition that could be used. This
+ feature is most useful for users who want to avoid being prompted
+ with each option separately, or who want to avoid the checking of
+ remote postponed or form letter folders. The possible types of
+ composition are:
+ New, for starting a new composition. Note that if New is selected and
+ roles are set, roles are checked for matches and applied according to
+ the setting of the matching role.
+ Interrupted, for continuing an interrupted composition. This option
+ is only offered if an interrupted message folder is detected.
+ Postponed, for continuing postponed compositions. This option is
+ offered if a postponed-folder is set in the config _REGARDLESS OF_
+ whether or not the postponed folder actually exists. This option is
+ especially handy for avoiding having to check for the existence of a
+ remote postponed folder.
+ Form, for using form letters. This option is offered if the
+ form-letter-folder is set in the config, and is not checked for
+ existence for reasons similar to those explained by the postponed
+ option.
+ setRole, for selecting a role to apply to a composition.
+ _alternate-role-menu_
+ Normally the Role Command allows you to choose a role and compose a
+ new message using that role. When this feature is set, the role
+ command will first ask whether you want to Compose a new message,
+ Forward the current message, Reply to the current message, or Bounce
+ the current message. If you are not in the MESSAGE INDEX and are not
+ viewing a message, then there is no current message and the question
+ will be skipped. After you have chosen to Compose, Forward, Reply or
+ Bounce you will then choose the role to be used.
+ When Bouncing the "Set From" address is used for the Resent-From
+ header, the "Set Fcc" value is used for the Fcc provided that the
+ option "Fcc-On-Bounce" is turned on, and the "Use SMTP Server" value
+ is used for the SMTP server, if set. Other actions of the role are
+ ignored when Bouncing.
+ This feature is displayed as "Alternate Role (#) Menu".
+ _assume-slow-link_
+ UNIX _Alpine_ only.
+ This feature affects _Alpine_'s display routines. If set, the normal
+ inverse-video cursor (used to highlight the current item in a list)
+ will be replaced by an _arrow_ cursor and other screen update
+ optimizations for low-speed links (e.g. 2400 bps dialup connections)
+ will be activated. One of the optimizations is that colored index
+ lines (set up with Indexcolor Rules) will not be colored. This might
+ be useful if _you_ know you have a slow speed link but for some
+ reason _Alpine_ doesn't know.
+ _auto-move-read-msgs_
+ This feature controls an aspect of _Alpine_'s behavior upon quitting.
+ If set, and the read-message-folder variable is also set, then
+ _Alpine_ will automatically transfer all read messages from the _INBOX_
+ to the designated folder and mark them as deleted in the _INBOX_.
+ Messages in the _INBOX_ marked with an _N_ (meaning New, or unseen)
+ are not affected.
+ This feature is displayed as "Auto Move Read Messages".
+ _auto-open-next-unread_
+ This feature controls the behavior of the TAB key when traversing
+ folders in the optional incoming-folders collection or in optional
+ news-collections.
+ When the TAB (Next New) key is pressed, and there are no more unseen
+ messages in the current (incoming message or news) folder, _Alpine_
+ will search the list of folders in the current collection for one
+ containing New or Recent (new since the last time the folder was
+ opened) messages. This behavior may be modified slightly with the
+ Tab-Uses-Unseen-For-Next-Folder feature which causes _Alpine_ to look
+ for Unseen messages instead of Recent messages. By default, when such
+ a folder is found, _Alpine_ will ask whether you wish to open the
+ folder. If this feature is set, _Alpine_ will automatically open the
+ folder without prompting.
+ _auto-unselect-after-apply_
+ This feature affects the behavior of the Apply command. If set, the
+ Apply command will do the operation you specify, but then will
+ implicitly do an "UnSelect All", so that you will automatically be
+ back in the normal Index view after the Apply.
+ _auto-unzoom-after-apply_
+ If set, and if you are currently looking at a Zoomed Index view of
+ selected messages, the _Apply_ command will do the operation you
+ specify, but then will implicitly do an _UnZoom_, so that you will
+ automatically be back in the normal Index view after the _Apply_.
+ This feature is set by default.
+ _auto-zoom-after-select_
+ If set, the _; select_ command will automatically perform a _Zoom_
+ after the _select_ is complete. This feature is set by default.
+ _busy-cue-spinner-only_
+ When _Alpine_ is delayed for some reason it usually shows that
+ something is happening with a small animated display in the status
+ message line near the bottom of the screen. Setting this feature will
+ cause that animation to be the same each time instead of having
+ _Alpine_ choose a random animation. You may turn the animation off
+ altogether by setting the busy-cue-rate option to zero.
+ _check-newmail-when-quitting_
+ If set, _Alpine_ will check for new mail after you give the Quit
+ command. If new mail has arrived since the previous check, you will
+ be notified and given the choice of quitting or not quitting.
+ _combined-addrbook-display_
+ This feature affects the address book display screens. Normally,
+ expanding an address book from the ADDRESS BOOK LIST screen will
+ cause the remaining address books and directory servers to disappear
+ from the screen, leaving only the entries of the expanded address
+ book. If this feature is set, then the other address books will
+ remain on the screen, so that all of the address books can be present
+ at once.
+ The way that commands work won't be changed. For example, the Select
+ All command will select all of the entries in the current address
+ book, not all of the entries in all of the address books. The WhereIs
+ command will change a little. It will search through all of the text
+ on the screen plus all of the entries from expanded address books.
+ When this feature is set, the setting of the feature
+ expanded-view-of-addressbooks has an effect.
+ This feature is displayed as "Combined Addressbook Display".
+ _combined-folder-display_
+ This feature affects the folder list display screens. Normally, each
+ folder list is viewed within its collection only. This command allows
+ folder lists to be viewed within a single screen that combines the
+ contents of all collections.
+ The way that commands work won't be changed. For example, the Select
+ All command will select all of the folders in the current collection,
+ not all of the entries in all of the collections. The WhereIs command
+ will change a little. It will search through all of the folders in
+ the current collection as well as all the folder in any other
+ expanded collection.
+ When this feature is set, the setting of the feature
+ expanded-view-of-folders has an effect.
+ _combined-subdirectory-display_
+ This feature affects the Folder List screen when the
+ combined-folder-display feature is enabled. Normally, selecting a
+ directory from the Folder List takes you into a new screen displaying
+ only the contents of that directory.
+ Enabling this feature will cause the contents of the selected
+ directory to be displayed within the boundaries of the Collection it
+ is a part of. All previously displayed collections will remain in the
+ screen.
+ The way that commands work won't be changed. For example, the Select
+ All command will select all of the folders in the directory, as
+ opposed to all of the entries in all of the collections. The WhereIs
+ command will change a little. It will search through all of the
+ folders in the current collection as well as all the folder in any
+ other expanded collection.
+ _compose-cancel-confirm-uses-yes_
+ This feature affects what happens when you type ^C to cancel a
+ composition. By default, if you attempt to cancel a composition by
+ typing ^C, you will be asked to confirm the cancellation by typing a
+ "C" for _C_onfirm. It logically ought to be a "Y" for _Y_es, but that
+ is risky because the "^C Y" needed to cancel a message is close (on
+ the keyboard) to the "^X Y" needed to send a message.
+ If this feature is set the confirmation asked for will be a "_Y_es"
+ instead of a "_C_onfirm" response.
+ _compose-cut-from-cursor_
+ If set, the _^K_ command in the composer will cut from the current
+ cursor position to the end of the line, rather than cutting the
+ entire line.
+ This feature is displayed as "Ctrl-K Cuts From Cursor".
+ _compose-maps-delete-key-to-ctrl-d_
+ If set, Delete will be equivalent to ^D, and delete the current
+ character. Normally _Alpine_ defines the Delete key to be equivalent
+ to ^H, which deletes the _previous_ character.
+ This feature is displayed as "Delete Key Maps to Ctrl-D".
+ _compose-rejects-unqualified-addrs_
+ If set, unqualified names entered as addresses will be treated as
+ errors unless they match an addressbook nickname or are looked up
+ successfully on an LDAP server. _Alpine_ will not attempt to turn
+ them into complete addresses by adding your local domain (which
+ _Alpine_ normally does by default).
+ A complete (fully-qualified) address is one containing a username
+ followed by an _@_ symbol, followed by a host or domain name (e.g.
+ _jsmith@example.com_). An unqualified name is one without the _@_
+ symbol and host or domain name (e.g. _jsmith_).
+ This feature is displayed as "Compose Rejects Unqualified Addresses".
+ _compose-send-offers-first-filter_
+ If you have sending-filters configured, setting this feature will
+ cause the first filter in the _sending-filters_ list to be offered as
+ the default instead of _unfiltered_, the usual default.
+ _compose-sets-newsgroup-without-confirm_
+ If you enter the composer while reading a newsgroup, you will
+ normally be prompted to determine whether you intend the new message
+ to be posted to the current newsgroup or not. If this feature is set,
+ _Alpine_ will not prompt you in this situation, and will assume that
+ you do indeed wish to post to the newsgroup you are reading.
+ This feature is displayed as "Compose Sets Newsgroup Without
+ Confirming".
+ _confirm-role-even-for-default_
+ If you have roles, when you Reply to or Forward a message, or Compose
+ a new message, _Alpine_ will search through your roles for one which
+ matches. Normally, if no matches are found you will be placed into
+ the composer with no opportunity to select a role. If this feature is
+ set, then you will be asked to confirm that you don't want a role.
+ This will give you the opportunity to select a role (with the ^T
+ command). If you confirm no role with a Return, you will be placed in
+ the composer with no role. You may also confirm with either an "N" or
+ a "Y". These behave the same as if you pressed the Return. (The "N"
+ and "Y" answers are available because they match what you might type
+ if there was a role match.)
+ If you are using the alternate form of the Compose command called
+ "Role", then all of your roles will be available to you, independent
+ of the value of this feauture and of the values set for all of Reply
+ Use, Forward Use, and Compose Use.
+ _continue-tab-without-confirm_
+ Normally, when you use the TAB NextNew command and there is a problem
+ checking a folder, you are asked whether you want to continue with
+ the search in the following folder or not. This gives you a chance to
+ stop the NextNew processing.
+ If this feature is set you will not be asked. It will be assumed that
+ you want to continue.
+ This feature is displayed as "Continue NextNew Without Confirming".
+ _convert-dates-to-localtime_
+ Normally, the message dates that you see in the MESSAGE INDEX and
+ MESSAGE VIEW are displayed in the timezone they were sent from. For
+ example, if a message was sent to you from a few timezones to the
+ east it might appear that it was sent from the future; or if it was
+ sent from somewhere to the west it might appear as if it is from
+ yesterday even though it was sent only a few minutes ago. If this
+ feature is set an attempt will be made to convert the dates to your
+ local timezone to be displayed.
+ Note that this does not affect the results of Select by Date or of
+ anything else other than these displayed dates. When viewing the
+ message you may look at the original unconverted value of the Date
+ header by using the HdrMode Command.
+ _copy-to-address-to-from-if-it-is-us_
+ This feature affects the From address used when Replying to a
+ message. It is probably only useful if you have some alt-addresses
+ defined. When enabled, it checks to see if any of the addresses in
+ the To or Cc fields of the message you are replying to is one of your
+ addresses. If it is, and there is only one of them, then that address
+ is used as the From address in the message you are composing. In
+ other words, you will be using a From address that is the same as the
+ To address that was used to get the mail to you in the first place.
+ If a role is being used and it has a From address defined, that From
+ address will be used rather than the one derived from this feature.
+ _delete-skips-deleted_
+ If set, this feature will cause the _Delete_ command to advance past
+ other messages that are marked deleted. In other words, pressing _D_
+ will both mark the current message deleted and advance to the next
+ message that is not marked deleted. This feature is set by default.
+ _disable-config-cmd_
+ If set, the configuration screen _Setup/Config_ will not be available
+ at all.
+ _disable-save-input-history_
+ Many of the prompts that ask for input in the status line near the
+ bottom of the screen will respond to Up Arrow and Down Arrow with the
+ history of previous entries. For example, in the MESSAGE INDEX screen
+ when you use the WhereIs command the text you entered will be
+ remembered and can be recalled by using the Up Arrow key. Another
+ example, when saving a message the folders saved to will be
+ remembered and can be recalled using the arrow keys.
+ In the Save prompt, some users prefer that the Up and Down arrow keys
+ be used for the Previous Collection and Next Collection commands
+ instead of for a history of previous saves. If this option is set the
+ Up and Down arrow keys will become synonyms for the Previous
+ Collection and Next Collection (^P and ^N) commands in the prompt for
+ the name of a folder to Save to or in the prompt for the name of a
+ folder to GoTo. When this feature is not set (the default), ^P and ^N
+ will change the collection and the arrow keys will show the history.
+ _disable-keyboard-lock-cmd_
+ In the Main _Alpine_ menu there is a Keyboard locking command
+ (_KBLock_). If this feature is set, that command won't be available to
+ the user.
+ _disable-keymenu_
+ If set, the command key menu that normally appears on the bottom two
+ lines of the screen will not usually be there. Asking for help with
+ _^G_ or _?_ will cause the key menu to appear instead of causing the
+ help message to come up. If you want to actually see the help text,
+ another _^G_ or _?_ will show it to you. After the key menu has
+ popped up with the help key it will remain there for an _O Other_
+ command but will disappear if any other command is typed.
+ _disable-password-caching_
+ Normally, loginname/password combinations are cached in _Alpine_ so
+ that the user does not have to enter the same password more than once
+ in a session. A disadvantage to this approach is that the password
+ must be stored in the memory image of the running _Alpine_ in order
+ that it can be reused. In the event that _Alpine_ crashes and
+ produces a core dump, and that core dump is readable by others, the
+ loginname and password could possibly be read from the core dump.
+ If this feature is set, then the passwords will not be cached and the
+ user will have to retype the password whenever _Alpine_ needs it.
+ Even with this feature set there is still some chance that the core
+ file will contain a password, so care should be taken to make the
+ core files unreadable.
+ NOTE: If PASSFILE caching is enabled, this does not disable it. That
+ is a separate and independent feature.
+ _disable-password-cmd_
+ If set the _Newpassword_ command usually available under the _Setup_
+ command will not be available.
+ _disable-pipes-in-sigs_
+ If set it will be an error to append a vertical bar (|) to the name
+ of a signature file. Appending a vertical bar normally causes the
+ signature file to be executed to produce the signature.
+ _disable-pipes-in-templates_
+ If set it will be an error to append a vertical bar (|) to the name
+ of a template file. Appending a vertical bar normally causes the
+ signature file to be executed to produce the signature.
+ _disable-regular-expression-matching-for-alternate-addresses_
+ Normally, the alt-addresses option is interpreted as a regular
+ expression. One type of address that might cause trouble is an
+ address that contains a plus sign. If you want to have an address
+ with a plus as one of your alternate addresses and you don't want to
+ use regular expressions, then setting this feature will cause _Alpine_
+ to treat the addresses you list literally instead.
+ _disable-roles-setup-cmd_
+ If set the _Roles_ command usually available under the _Setup_
+ command will not be available.
+ _disable-roles-sig-edit_
+ If set the roles editor in the _Setup/Roles_ command will not allow
+ editing of signature files with the F subcommand.
+ _disable-roles-template-edit_
+ If set the roles editor in the _Setup/Roles_ command will not allow
+ editing of template files with the F subcommand.
+ _disable-sender_
+ If set, _Alpine_ will not generate a "Sender:" or "X-X-Sender"
+ header. This may be desirable on a system which is virtually hosting
+ many domains, and the sysadmin has other methods available for
+ tracking a message to its originator.
+ This feature is displayed as "Do Not Generate Sender Header".
+ _disable-setlocale-collate_
+ This is a hard to understand feature that should only be used in rare
+ cases. Normally, the C function call
+
+ setlocale(LC_COLLATE, "")
+ is used by _Alpine_. If you want to try turning it off, setting this
+ feature will turn it off. This part of the locale has to do with the
+ sort order of characters in your locale.
+ _disable-shared-namespaces_
+ If this hidden feature is set the automatic search for namespaces
+ "ftp", "imapshared", and "imappublic" by the underlying library will
+ be disabled. The reason this feature exists is because there are some
+ implementations of system password lookup routines which are very
+ slow when presented with a long loginname which does not exist. This
+ feature could be set to prevent the delay at startup time when the
+ names above are searched for in the password file.
+ _disable-signature-edit-cmd_
+ If set the _Signature_ editing command usually available under the
+ _Setup_ command will not be available.
+ _disable-take-fullname-in-addresses_
+ Normally, when TakeAddr is used to copy an address or addresses from
+ a message into an address book entry, _Alpine_ will try to preserve
+ the full name associated with each address in the list of addresses.
+ The reason for this is so that if the entry is a list or later
+ becomes a list, then information about the individual addresses in
+ the list is preserved. If you would rather just have the simple
+ addresses in the list of addresses, set this feature. For example,
+ with the default setting you might see something like this in the
+ ADDRESS BOOK editor after you type TakeAddr
+ Nickname : nick
+ Fullname : Bedrock Elders
+ Fcc :
+ Comment :
+ Addresses : Fred Flintstone <flint@bedrock.org>,
+ Barney Rubble <rubble@bedrock.org>
+ but with this feature set it would look like
+ Nickname : nick
+ Fullname : Bedrock Elders
+ Fcc :
+ Comment :
+ Addresses : flint@bedrock.org,
+ rubble@bedrock.org
+ instead. Note the difference in the Addresses field.
+ _disable-take-last-comma-first_
+ Normally, when _TakeAddr_ is used to copy an address from a message
+ into an address book, _Alpine_ will attempt to rewrite the full name
+ of the address in the form:
+
+ Last, First
+ instead of
+
+ First Last
+ It does this because many people find it useful to sort by Last name
+ instead of First name. If this feature is set, then the _TakeAddr_
+ command will not attempt to reverse the name in this manner.
+ _disable-terminal-reset-for-display-filters_
+ UNIX _Alpine_ only.
+ This feature affects _Alpine_'s behavior when using Display-Filters.
+ Normally, before the display filter is run, the terminal mode is
+ reset to what it was before you started _Alpine_. This may be
+ necessary if the filter requires the use of the terminal. For
+ example, it may need to interact with you. If you set this feature,
+ then the terminal mode will not be reset. One thing that turning on
+ this feature should fix is the coloring of quoted text in the message
+ view, which breaks because the terminal reset resets the color state
+ of the terminal (Color Configuration).
+ _downgrade-multipart-to-text_
+ This feature affects _Alpine_'s behavior when sending mail. Internet
+ standards require _Alpine_ to translate all non-ASCII characters in
+ messages that it sends using MIME encoding. This encoding can be
+ ostensibly broken for recipients if any agent between _Alpine_ and
+ the recipient, such as an email list expander, appends text to the
+ message, such as list information or advertising. When sending such
+ messages _Alpine_ attempts to protect such encoding by placing extra
+ MIME boundaries around the message text.
+ These extra boundaries are invisible to recipients that use
+ MIME-aware email programs (the vast majority). However, if you
+ correspond with users of email programs that are not MIME-aware, or
+ do not handle the extra boundaries gracefully, you can use this
+ feature to prevent _Alpine_ from including the extra MIME
+ information. Of course, it will increase the likelihood that
+ non-ASCII text you send may appear corrupt to the recipient.
+ _enable-8bit-esmtp-negotiation_
+ This feature affects _Alpine_'s behavior when sending mail. By
+ default, this feature is set. Internet standards require that all
+ electronic mail messages traversing the global Internet consist of
+ 7bit ASCII characters unless a pair of cooperating mail transfer
+ agents explicitly agree to allow 8bit messages. In general, then,
+ exchanging messages in non-ASCII characters requires MIME encoding.
+ However, there are now Internet standards that allow for unencoded
+ 8bit exchange of messages between cooperating systems. When this
+ feature is set _Alpine_ will try to negotiate unencoded 8bit
+ transmission during the sending process. Should the negotiation fail,
+ _Alpine_ will fall back to its ordinary encoding rules.
+ Note, this feature relies on your system's mail transport agent or
+ configured smtp-server having the negotiation mechanism introduced in
+ "Extended SMTP" (ESMTP) and the specific extension called _8BITMIME_.
+ _enable-8bit-nntp-posting_
+ The Internet standard for exchanging USENET news messages (RFC-1036)
+ specifies that USENET messages should conform to Internet mail
+ standards and contain only 7bit characters, but much of the news
+ transport software in use today is capable of successfully sending
+ messages containing 8bit characters. Hence, many people believe that
+ it is appropriate to send 8bit news messages without any MIME
+ encoding.
+ Moreover, there is no Internet standard for explicitly negotiating
+ 8bit transfer, as there is for Internet email. Therefore, _Alpine_
+ provides the option of posting unencoded 8bit news messages, though
+ not as the default. Setting this feature will turn OFF _Alpine_'s
+ MIME encoding of newsgroup postings that contain 8bit characters.
+ Note, articles may cross a path or pass through news transport
+ software that is unsafe or even hostile to 8bit characters. At best
+ this will only cause the posting to become garbled. The safest way to
+ transmit 8bit characters is to leave _Alpine_'s MIME encoding turned
+ on, but recipients who lack MIME-aware tools are often annoyed when
+ they receive MIME-encoded messages.
+ _enable-aggregate-command-set_
+ When this feature is set you may use the commands and subcommands
+ that relate to performing operations on more than one message at a
+ time. We call these "aggregate operations". In particular, the _;
+ Select_, _A Apply_, and _Z Zoom_ commands are enabled by this
+ feature. _Select_ is used to _tag_ one or more messages meeting the
+ specified criteria. _Apply_ can then be used to apply any message
+ command to all of the selected/tagged messages. Further, the _Zoom_
+ command allows you to toggle the "Folder Index" view between just
+ those Selected and all messages in the folder.
+ This feature also enables the _^X_ subcommand in the "Folder Index"
+ _WhereIs_ command which causes all messages matching the _WhereIs_
+ argument to become selected.
+ You may also use aggregate operations in the address book screens
+ where you are operating on address book entries instead of on
+ messages.
+ _enable-alternate-editor-cmd_
+ If this feature is set (the default), and the editor variable is not
+ set, entering the _^__ (Control-underscore) key while composing a
+ message will prompt you for the name of the editor you would like to
+ use.
+ If the environment variable $EDITOR is set, this value will be
+ offered as a default. If the _editor_ variable is set, the _^__ key
+ will activate the specified editor without prompting, in which case
+ it is not necessary to set the _enable-alternate-editor-cmd_ feature.
+ This feature is not available in _PC-Alpine_.
+ This feature is displayed as "Enable Alternate Editor Command".
+ _enable-alternate-editor-implicitly_
+ If this feature and the editor variable are both set, _Alpine_ will
+ automatically activate the specified editor when the cursor is moved
+ from the header of the message being composed into the message text.
+ For replies, the alternate editor will be activated immediately. If
+ this feature is set but the _editor_ variable is not set, then
+ _Alpine_ will automatically ask for the name of an alternate editor
+ when the cursor is moved out of the headers, or if a reply is being
+ done. This feature is not available in _PC-Alpine_.
+ _enable-arrow-navigation_
+ This feature controls the behavior of the left and right arrow keys.
+ If set, the left and right arrow keys will operate like the usual
+ navigation keys _<_ and _>_. This feature is set by default.
+ If you set this feature, and do not like the changed behavior of the
+ up/down arrow keys when navigating through the FOLDER LIST screen --
+ _first_ from column to column, if more than one folder is displayed
+ per row, and _then_ from row to row -- you may either also wish to
+ set the feature enable-arrow-navigation-relaxed,
+ single-column-folder-list, or use the ^P/^N (instead of up/down
+ arrow) keys to move up/down the list of folders in each column.
+ _enable-arrow-navigation-relaxed_
+ This feature controls the behavior of the left and right arrow keys
+ in the FOLDER LIST screen when the enable-arrow-navigation feature is
+ set. This feature is set by default.
+ When this feature is set, the left and right arrow keys in the FOLDER
+ LIST screen move the highlight bar to the left or right, and the up
+ and down arrows move it up or down.
+ When the "Enable-Arrow-Navigation" feature is set and this feature is
+ not set; the left and right arrow keys in the Folder List screen
+ strictly track the commands bound to the '<' and '>' keys, and the up
+ and down arrow keys move the highlight bar to the previous and next
+ folder or directory name.
+ _enable-background-sending_
+ If set, this feature enables a subcommand in the composer's _Send?_
+ confirmation prompt. The subcommand allows you to tell _Alpine_ to
+ handle the actual posting in the background. While this feature
+ usually allows posting to appear to happen very fast, it has no
+ affect on the actual delivery time it takes a message to arrive at
+ its destination.
+ This feature isn't supported on all systems. All DOS and Windows, as
+ well as several Unix ports, do not recognize this feature. It is not
+ possible to use background sending if the feature
+ send-without-confirm is set.
+ Error handling is significantly different when this feature is
+ enabled. Any message posting failure results in the message being
+ appended to your _Interrupted_ mail folder. When you type the
+ _Compose_ command, _Alpine_ will notice this folder and offer to
+ extract any messages contained. Upon continuing a failed message,
+ _Alpine_ will display the nature of the failure in the status message
+ line.
+ Under extreme conditions, it is possible for message data to get
+ lost. Do not enable this feature if you typically run close to any
+ sort of disk-space limits or quotas.
+ _enable-bounce-cmd_
+ Setting this feature enables the _B Bounce_ command, which will
+ prompt for an address and _remail_ the message to the new recipient.
+ This command is used to re-direct messages that you have received in
+ error, or need to be redirected for some other reason (e.g. list
+ moderation). The final recipient will see a header indicating that
+ you have Resent the msg, but the message's From: header will show the
+ original author of the message, and replies to it will go back to
+ that author, and not to you.
+ This feature is displayed as "Enable Bounce Command".
+ _enable-cruise-mode_
+ This feature affects _Alpine_'s behavior when you hit the "Space Bar"
+ at the end of a displayed message. Typically, _Alpine_ complains that
+ the end of the text has already been reached. Setting this feature
+ causes such keystrokes to be interpreted as if the _Tab_ key had been
+ hit, thus taking you to the next _interesting_ message, or scanning
+ ahead to the next incoming folder with _interesting_ messages.
+ _enable-cruise-mode-delete_
+ This feature modifies the behavior of _Alpine_'s _enable-cruise-mode_
+ feature. Setting this feature causes _Alpine_ to implicitly delete
+ read messages when it moves on to display the next _interesting_
+ message.
+ NOTE: Beware when enabling this feature _and_ the
+ expunge-without-confirm feature.
+ This feature is displayed as "Enable Cruise Mode With Deleting".
+ _enable-delivery-status-notification_
+ If set, this feature enables a subcommand in the composer's "Send?"
+ confirmation prompt. The subcommand allows you to tell _Alpine_ to
+ request the type of Delivery Status Notification (DSN) which you
+ would like. Most users will be happy with the default, and need not
+ enable this feature. See the online help for more details.
+ It is not possible to use delivery status notifications if the
+ feature send-without-confirm is set.
+ Note that this is not a method to request _READ_ receipts, which
+ tells the sender when the receiver has read the message. In this case
+ we're talking about notification of delivery to the mailbox, not
+ notification that the message has been seen.
+ _enable-dot-files_
+ If set, files beginning with dot (".") will be visible in the file
+ browser. For example, you'll be able to select them when using the
+ browser to add an attachment to a message.
+ _enable-dot-folders_
+ If set, folders beginning with dot (".") may be added and viewed.
+ This feature is displayed as "Enable Hidden Folders".
+ _enable-exit-via-lessthan-command_
+ If set, then on screens where there is an _Exit_ command but no _<_
+ command, the _<_ key will perform the same function as the _Exit_
+ command. This feature is set by default.
+ _enable-fast-recent-test_
+ This feature controls the behavior of the TAB key when traversing
+ folders in the optional Incoming-Folders collection or in optional
+ News-Collections.
+ When the TAB (NextNew) key is pressed, the default behavior is to
+ explicitly examine the status of the folder for the number of recent
+ messages (messages delivered since the last time it was viewed).
+ Depending on the size and number of messages in the folder, this test
+ can be time consuming.
+ Enabling this feature will cause _Alpine_ to only test for the
+ existence of any recent messages rather than to obtain the count.
+ This is much faster in many cases. The downside is that you're not
+ given the number of recent messages when prompted to view the next
+ folder. If the feature Tab-Uses-Unseen-For-Next-Folder is turned on,
+ then the present feature will have no effect.
+ _enable-flag-cmd_
+ Setting this feature enables the _* Flag_ command, which allows you
+ to manipulate the status flags associated with a message. By default,
+ _Flag_ will set the _Important_ flag, which results in an asterisk
+ being displayed in column one of the "Folder Index" for such
+ messages.
+ This feature is displayed as "Enable Flag Command".
+ _enable-flag-screen-implicitly_
+ This feature modifies the behavior of the _* Flag_ command (provided
+ it too is enabled). By default, when the _* Flag_ command is
+ selected, _Alpine_ offers a prompt to set one of several flags and
+ also offers the option of entering the detailed flag manipulation
+ screen via the _^T_ key. Enabling this feature causes _Alpine_ to
+ immediately enter the detailed flag screen rather than first offer
+ the simple prompt. The Enable-Flag-Screen-Keyword-Shortcut option
+ offers a slightly different way of setting keywords.
+ _enable-flag-screen-keyword-shortcut_
+ This feature modifies the behavior of the Flag command and the Select
+ command. By default, when the "* Flag" command is selected, _Alpine_
+ offers a prompt to set one of several flags and also offers the
+ option of entering the detailed flag manipulation screen via the "^T"
+ key. If you have keywords defined, then enabling this feature adds a
+ shortcut way to set or unset keywords. You use "*" followed by the
+ first letter of a keyword (or the nickname of a keyword if you've
+ given it a nickname) and that will set the keyword.
+ An example is easier to understand than the explanation. The flag
+ command can always be used to set the system flags. For example, to
+ set the Answered flag you would type
+
+ * A
+ Now suppose you have defined a keyword "Work" using the Keywords
+ option in the Config screen. By default, to set a keyword like "Work"
+ you would usually have to go to the Flag Details screen using the "^T
+ To Flag Details" command. Instead, if you have enabled this feature,
+ you may type
+
+ * W
+ to set the Work flag, or
+
+ * ! W
+ to unset it. Just like for the other flag setting commands, the case
+ of the letter does not matter, so "w" or "W" both set the "Work"
+ keyword.
+ Notice that you can only use this trick for one keyword that begins
+ with "W". If you happen to have a "Work" keyword and another keyword
+ that is "WIFI" the "* W" command will set the first one in your list
+ of keywords. Also, there are five letters which are reserved for
+ system flags and the NOT command. If you type "* A" it will always
+ set the Answered flag, not your "Aardvark" keyword. In order to set
+ the "Aardvark" keyword you'll still have to use the Flag Details
+ screen.
+ Because enabling the Enable-Flag-Screen-Implicitly option causes
+ _Alpine_ to skip directly to the Flag Details screen when the Flag
+ command is used, setting it will cause this feature to have no effect
+ at all.
+ Similarly, when Selecting by Keyword, setting this option will allow
+ you to use Keyword initials instead of full keywords.
+ _enable-full-header-cmd_
+ This feature enables the _H Full Headers_ command which toggles
+ between the display of all headers in the message and the normal
+ edited view of headers. The _Full Header_ command also controls which
+ headers are included for _Export_, _Pipe_, _Print_, _Forward_, and
+ _Reply_ functions. (For _Reply_, the _Full Header_ mode will respect
+ the _include-headers-in-reply_ feature setting.)
+ If Full Header mode is turned on and you Forward a message, you will
+ be asked if you'd like to forward the message as an attachment, as
+ opposed to including the text of the message in the body of your new
+ message.
+ If you have also turned on the "Quote Suppression" option then the
+ Full Headers command actually rotates through three states instead of
+ just two. The first is the normal view with long quotes suppressed.
+ The second is the normal view but with the long quotes included. The
+ last enables the display of all headers in the message. When using
+ Export, Pipe, Print, Forward, or Reply the quotes are never
+ suppressed, so the first two states are identical.
+ Normally, the Header Mode will reset to the default behavior when
+ moving to a new message. The mode can be made to persist from message
+ to message by setting the feature Quell-Full-Header-Auto-Reset.
+ This feature is displayed as "Enable Full Header Command".
+ _enable-full-header-and-text_
+ This feature affects how the _H Full Headers_ command displays
+ message text. If set, the raw message text will be displayed. This
+ especially affects MIME formatted email, where the entire MIME format
+ will be displayed. This feature similarly affects how messages are
+ included for the _Export_, _Pipe_, _Print_, _Forward_, and _Reply_
+ functions.
+ _enable-goto-in-file-browser_
+ Setting this causes _Alpine_ to offer the _G Goto_ command in the
+ file browser. The Goto command allows you to explicitly type in the
+ desired directory. That is the default.
+ _enable-incoming-folders_
+ If set, this feature defines a pseudo-folder collection called
+ _INCOMING MESSAGE FOLDERS_. Initially, the only folder included in
+ this collection will be your _INBOX_, which will no longer show up in
+ your default saved-message folder collection.
+ This feature is displayed as "Enable Incoming Folders Collection".
+ _enable-incoming-folders-checking_
+ This feature is only operational if you have enabled the optional
+ incoming-folders If you do have Incoming Message Folders and you also
+ set this feature, then the number of Unseen messages in each folder
+ will be displayed in the FOLDER LIST screen for the Incoming Message
+ Folders. The number of Unseen messages in a folder will be displayed
+ in parentheses to the right of the name of each folder. If there are
+ no Unseen messages in a folder then only the name is displayed, not a
+ set of parentheses with zero inside them. A redraw command, Ctrl-L,
+ can be used in the FOLDER LIST screen for the Incoming Message
+ Folders to cause an immediate update.
+ If a check for Unseen messages fails for a particular folder then
+ Alpine will no longer attempt to check that folder for the duration
+ of the session and this will be indicated by a question mark inside
+ the parentheses.
+ The features incoming-checking-includes-total,
+ incoming-checking-uses-recent, incoming-check-list,
+ incoming-check-interval, incoming-check-interval-secondary, and
+ incoming-check-timeout all affect how this feature behaves.
+ _Disable-Index-Locale-Dates_
+ This feature affects the display of dates in the MESSAGE INDEX.
+ Normally an attempt is made to localize the dates used in the MESSAGE
+ INDEX display to your locale. This is controlled with the LC_TIME
+ locale setting on a UNIX system. On Windows the Regional Options
+ control panel may be used to set the date format. At the programming
+ level, _Alpine_ is using the strftime routine to print the parts of a
+ date.
+ If this feature is set, dates are displayed in English and with the
+ conventions of the United States.
+ _enable-jump-shortcut_
+ When this feature is set you may enter a number (followed by RETURN)
+ and jump to that message number, when in the MESSAGE INDEX or MESSAGE
+ TEXT screens. In other words, it obviates the need for typing the _J_
+ for the _Jump_ command.
+ _enable-lame-list-mode_
+ This feature modifies the method _Alpine_ uses to ask your IMAP
+ server for folder names to display in the the FOLDER LIST screen. It
+ is intended to compensate for a small set of IMAP servers that are
+ programmed to ignore a part of the request, and thus respond to
+ _Alpine_'s query with nonsensical results.
+ If you find that _Alpine_ is erroneously displaying blank folder
+ lists, try enabling this feature.
+ NOTE: Enabling this feature has consequences for the Goto and Save
+ commands. Many servers allow access to folders outside the area
+ reserved for your personal folders via some reserved character,
+ typically '#' (sharp), '~' (tilde) or '/' (slash). This mechanism
+ allows, at the Goto and Save prompts, quick access to folders outside
+ your personal folder collection without requiring a specific
+ collection definition. This behavior will generally not be available
+ when this feature is enabled.
+ This feature is displayed as "Compensate for Deficient IMAP servers".
+ _enable-mail-check-cue_
+ If set, this will cause an asterisk to appear in the upper left-hand
+ corner of the screen whenever _Alpine_ checks for new mail, and two
+ asterisks whenever _Alpine_ saves (checkpoints) the state of the
+ current mailbox to disk.
+ _enable-mailcap-param-substitution_
+ If set, this will allow mailcap named parameter substitution to occur
+ in mailcap entries. By default, this is turned off to prevent
+ security problems which may occur with some incorrect mailcap
+ configurations. For more information, RFC1524 and look for "named
+ parameters" in the text of the RFC.
+ This feature is displayed as "Enable Mailcap Parameter Substitution".
+ _enable-mouse-in-xterm_
+ This feature controls whether or not an X terminal mouse can be used
+ with _Alpine_. If set, and the $DISPLAY variable indicates that an X
+ terminal is being used, the left mouse button on the mouse can be
+ used to select text or commands. Clicking on a command at the bottom
+ of the screen will behave as if you had typed that command. Clicking
+ on an index line will move the current message highlight to that
+ line. Double-clicking on an index line will view the message.
+ Double-clicking on a link will view the link.
+ This type of mouse support will also work in some terminal emulators
+ which are not actually X terminals, but which have extra code to
+ support the xterm style mouse. For those emulators you not only need
+ to turn this feature on but you also have to set the $DISPLAY
+ environment variable even though it isn't needed for your terminal.
+ That will cause _Alpine_ to think that it is an xterm and to properly
+ interpret the escape sequences sent by the mouse.
+ Note: if this feature is set, the behavior of X terminal
+ cut-and-paste is also modified. It is sometimes possible to hold the
+ shift key down while clicking left or middle mouse buttons for the
+ normal xterm cut/paste operations. There is also an _Alpine_ command
+ to toggle this mode on or off. The command is Ctrl-\
+ (Control-backslash).
+ _enable-msg-view-addresses_
+ This feature modifies the behavior of _Alpine_'s "Message Text"
+ screen. Setting this feature causes _Alpine_ to select possible email
+ addresses from the displayed text and display them in boldface for
+ selection.
+ The first available email address is displayed in inverse. This is
+ the "selected" address. Pressing _RETURN_ will cause _Alpine_ to
+ enter the message composition screen with the To field filled in with
+ the selected address.
+ Use the up and down arrow keys to change which of the addresses
+ displayed in boldface is the current selection.
+ This feature is displayed as "Enable Message View Address Links".
+ _enable-msg-view-attachments_
+ This feature modifies the behavior of _Alpine_'s "Message Text"
+ screen. Setting this feature causes _Alpine_ to present attachments
+ in boldface. The first available attachment is displayed in inverse.
+ This is the "selected" attachment. Pressing _RETURN_ will cause
+ _Alpine_ to display the selected attachment. Use the up and down arrow
+ keys to change which of the attachments displayed in boldface is the
+ current selection.
+ Speaking of arrow keys, the Up and Down Arrows will select the next
+ and previous attachments if one is available on the screen for
+ selection. Otherwise, they will simply adjust the viewed text one
+ line up or down.
+ Similarly, when selectable items are present in a message, the Ctrl-F
+ key can be used to select the next item in the message independent of
+ which portion of the viewed message is currently displayed. The
+ Ctrl-B key can be used to select the previous item in the same way.
+ This feature is displayed as "Enable Message View Attachment Links".
+ _enable-msg-view-forced-arrows_
+ This feature modifies Up and Down arrow key behavior in _Alpine_'s
+ "Message Text" screen when selectable Attachments, URL's, or
+ web-hostnames are presented. _Alpine_'s usual behavior is to move to
+ the next or previous selectable item if currently displayed or simply
+ to adjust the screen view by one line if the next selectable line is
+ off the screen.
+ Setting this feature causes the Up and Down arrow keys to behave as
+ if no selectable items were present in the message.
+ Note, the _Ctrl-F_ (next selectable item) and _Ctrl-B_ (previous
+ selectable item) functionality is unchanged.
+ This feature is displayed as "Enable Message View Forced Arrows".
+ _enable-msg-view-urls_
+ This feature modifies the behavior of _Alpine_'s "Message Text"
+ screen. When this feature is set (the default) _Alpine_ will select
+ possible URLs from the displayed text and display them in boldface
+ for selection.
+ The first available URL is displayed in inverse. This is the
+ "selected" URL. Pressing _RETURN_ will cause _Alpine_ to display the
+ selected URL via either built-in means as with mailto:, imap:, news:,
+ and nntp:, or via an external application as defined by the
+ url-viewers variable.
+ Use the up and down arrow keys to change which of the URLs displayed
+ in boldface is the current selection.
+ This feature is displayed as "Enable Message View URL Links".
+ _enable-msg-view-web-hostnames_
+ This feature modifies the behavior of _Alpine_'s "Message Text"
+ screen. When this feature is set (the default) _Alpine_ will select
+ possible web hostnames from the displayed text and display them in
+ boldface for selection.
+ The first available hostname is displayed in inverse. This is the
+ "selected" hostname. Pressing _RETURN_ will cause _Alpine_ to display
+ the selected hostname via an external application as defined by the
+ url-viewers variable.
+ Use the up and down arrow keys to change which of the hostnames
+ displayed in boldface is the current selection.
+ This feature is displayed as "Enable Message View Web Hostname
+ Links".
+ _enable-multiple-newsrcs_
+ This feature makes it so _Alpine_ can use multiple newsrcs based on
+ the news server being connected to, which allows for separate lists
+ of subscribed-to newsgroups. When this feature is not set, there is
+ only one list of newsgroups.
+ Under this feature, the name of a newsrc is based on the news server.
+ For example, if your newsrc-path is set to ".newsrc", and the news
+ server you are connecting to is news.example.com, then the newsrc to
+ be used is .newsrc-news.example.com. Setting this feature for the
+ first time will allow for the option of using your old newsrc the
+ next time you read news.
+ If this feature is set, then the feature
+ Mult-Newsrc-Hostnames-As-Typed also may affect the name of the newsrc
+ file that is used.
+ _enable-newmail-in-xterm-icon_
+ This feature controls whether or not _Alpine_ will attempt to
+ announce new mail arrival when it is running in an X terminal window
+ and that window is iconified. If set, and the $DISPLAY variable
+ indicates that an X terminal is being used, _Alpine_ will send
+ appropriate escape sequences to the X terminal to modify the label on
+ _Alpine_'s icon to indicate that new mail has arrived. _Alpine_ will
+ also modify the _Alpine_ window's title to indicate new mail. See
+ also Enable-Newmail-Short-Text-in-Icon.
+ _enable-newmail-short-text-in-icon_
+ This feature controls the text to be displayed in an icon in the
+ event of a new message arrival. Normally, the message will be the one
+ that is displayed on the screen. This feature shortens the message to
+ a count of the number of new messages in brackets. This may be more
+ useful for those who use the window's title bar in the task bar as a
+ new mail indicator. This feature is only useful if the
+ Enable-Newmail-in-Xterm-Icon is also set. Like the
+ Enable-Newmail-in-Xterm-Icon feature, this feature is only relevant
+ when run in an xterm environment.
+ _enable-partial-match-lists_
+ This feature affects the subcommands available when _Sav_ing or
+ Opening a new folder. If set, the subcommand _^X ListMatches_ will be
+ available. This command allows you to type in a substring of the
+ folder you are looking for and when you type _^X_ it will display all
+ folders which contain that substring in their names. This feature is
+ set by default.
+ _enable-print-via-y-command_
+ By default, _Alpine_'s print command is available by pressing the _%_
+ key. In older versions of _Pine_, the print command was accessed by
+ pressing the _Y_ key.
+ Enabling this feature will cause _Alpine_ to recognize both the old
+ command, _Y_, and the new _%_ method for invoking printing. Note, key
+ menu labels are not changed as a result of enabling this feature.
+ _enable-reply-indent-string-editing_
+ This feature affects the Reply command's "Include original message in
+ Reply?" prompt. When enabled, it causes the "Edit Indent String"
+ sub-command to appear which allows you to edit the string _Alpine_
+ would otherwise use to denote included text from the message being
+ replied to.
+ Thus, you can change _Alpine_'s default message quote character
+ (usually an angle bracket) on a per message basis. So you could
+ change your quoted message to look, for example, like this:
+On Tues, 26 Jan 1999, John Q. Smith wrote:
+
+John: I just wanted to say hello and to congratulate you
+John: on a job well done!
+ The configuration option "reply-indent-string" may be used to change
+ what appears as the default string to be edited.
+ NOTE: Edited reply-indent-strings only apply to the message currently
+ being replied to.
+ _enable-rules-under-take_
+ Normally, the Take command takes addresses from a message and helps
+ you put them into your Address Book. If you use Rules for
+ Indexcolors, Roles, Filtering, or Scoring; you may find it useful to
+ be able to Take information from a message's headers and put it into
+ a new Rule. When this feature is set, you will be given an extra
+ prompt which gives you the choice to Take into the Address Book or
+ Take into a rule.
+ This feature is displayed as "Enable Take Rules".
+ _enable-search-and-replace_
+ If set _Alpine_'s composer offers the _R Replace_ command option
+ inside the _W WhereIs_ command.
+ _enable-sigdashes_
+ If set and a _signature-file_ exists, the line consisting of the
+ three characters "-- " (dash dash space) is included before the
+ signature. This only happens if the signature doesn't already contain
+ such a line.
+ In addition, when you Reply or Followup to a message containing one
+ of these special lines and choose to include its text, _Alpine_ will
+ observe the convention of not including text beyond the special line
+ in your reply.
+ _enable-suspend_
+ Setting this feature will allow you to type _^Z_ and temporarily
+ suspend _Alpine_. Not available on _PC-Alpine_.
+ _enable-tab-completion_
+ This feature enables the _TAB_ key when at a prompt for a filename.
+ In this case, _TAB_ will cause the partial name already entered to be
+ automatically completed, provided the partial name is unambiguous.
+ This feature is set by default.
+ Similarly, this feature also enables TAB completion of address book
+ nicknames when at a prompt for a nickname, or when typing in an
+ address field in the composer.
+ _enable-take-export_
+ Normally, the Take command takes addresses from a message and helps
+ you put them into your Address Book. When this feature is set, you
+ will be given an extra prompt which gives you the choice to Take
+ addresses into a file instead of your Address Book. Only the
+ user@domain_name part of the address is put in the file.
+ _enable-tray-icon_
+ _PC-Alpine_ only. This option restores a behavior of previous
+ versions of PC-Alpine. These versions, when started, installed a
+ PC-Alpine icon in the notification tray of Window's Taskbar. The
+ primary use of this icon was to indicate new mail arrival by turning
+ red (while the Taskbar icon remained green). Additionally, the icon
+ now changes to yellow to signify that a mail folder has been closed
+ unexpectedly.
+ Rather than add another icon to the Taskbar, this version of
+ PC-Alpine will color its Taskbar entry's icon red (as well as the
+ icon in the Window Title). This feature is only provided for
+ backwards compatibility.
+ _enable-unix-pipe-cmd_
+ This feature enables the _| Pipe_ command that sends the current
+ message to the specified Unix command for external processing.
+ This feature is displayed as "Enable Unix Pipe Command".
+ _enable-verbose-smtp-posting_
+ This feature controls an aspect of _Alpine_'s message sending. When
+ enabled, _Alpine_ will send a VERB (i.e., VERBose) command early in
+ the posting process intended to cause the server SMTP to provide a
+ more detailed account of the transaction. This feature is typically
+ only useful to system administrators and other support personel as an
+ aid in troublshooting problems. Note, this feature relies on a
+ specific capability of the system's mail transport agent or
+ configured smtp-server.
+ _expanded-view-of-addressbooks_
+ If multiple address books (either personal or global) are defined,
+ and you wish to have them all expanded implicitly upon entering the
+ ADDRESS BOOK screen, then set this feature. This feature will have no
+ effect unless the feature combined-addrbook-display is also set.
+ _expanded-view-of-distribution-lists_
+ If this feature is set, then distribution lists in the address book
+ screen will always be expanded automatically.
+ _expanded-view-of-folders_
+ If multiple folder collections are defined, and you wish to have them
+ all expanded implicitly upon entering the FOLDER LIST screen, then
+ set this feature. This feature will have no effect unless the feature
+ combined-folder-display is also set.
+ _expose-hidden-config_
+ The purpose of this feature is to allow you to change configuration
+ features and variables which are normally hidden. This is
+ particularly useful if you are using a remote configuration file,
+ where it is difficult to edit the file manually, but it may also be
+ used on a local pinerc configuration file.
+ If set, most configuration variables and features which are normally
+ hidden from view will show up in the Setup/Configuration screen. They
+ will be at the bottom of the configuration screen. You can find them
+ by searching for the word "hidden".
+ Note that this is an advanced feature which should be used with care.
+ The reason that this part of the configuration is normally hidden is
+ because there is a significant potential for causing problems if you
+ change these variables. If something breaks after a change try
+ changing it back to see if that is what is causing the problem. There
+ are also some variables which are normally hidden because they are
+ manipulated through _Alpine_ in other ways. For example, the
+ "address-book" variable is normally set using the Setup/AddressBooks
+ screen, so there is little reason to edit it directly. The
+ "incoming-folders" variable is normally changed by using the Add,
+ Delete, and Rename commands in the FOLDER LIST screen, and the
+ "last-time-prune-questioned" variable is normally used internally by
+ _Alpine_ and not set directly by the user.
+ _expunge-only-manually_
+ Normally, when you close a folder which contains deleted messages you
+ are asked if you want to expunge those messages from the folder
+ permanently. If this feature is set, you won't be asked and the
+ deleted messages will remain in the folder. If you choose to set this
+ feature you will have to expunge the messages manually using the
+ eXpunge command, which you can use while in the MESSAGE INDEX screen.
+ If you do not expunge deleted messages the size of your folder will
+ continue to increase until you are out of disk space.
+ _expunge-without-confirm_
+ If set, you will not be prompted to confirm your intent before the
+ expunge takes place. Actually, you will still be prompted for
+ confirmation if the folder is not the _INBOX_ folder or another
+ folder in the Incoming Folders collection. See the
+ _expunge-without-confirm-everywhere_ feature which follows.
+ This feature is displayed as "Expunge Without Confirming".
+ _expunge-without-confirm-everywhere_
+ The regular _expunge-without-confirm_ feature actually only works for
+ the _INBOX_ folder and for other folders in the "Incoming Folders"
+ collection. If this feature is set then you also won't be prompted to
+ confirm expunges for all other folders.
+ This feature is displayed as "Expunge Without Confirming Everywhere".
+ _fcc-on-bounce_
+ If set, normal Fcc (File Carbon Copy) processing will be done for
+ bounced messages, just as if you had composed a message to the
+ address you are bouncing to. If not set, no Fcc of the message will
+ be saved.
+ This feature is displayed as "Include Fcc When Bouncing Messages".
+ _fcc-only-without-confirm_
+ This features controls an aspect of _Alpine_'s composer. The only
+ time this feature will be used is if you attempt to send mail which
+ has no recipients but does have an Fcc. Normally, _Alpine_ will ask
+ if you really mean to copy the message only to the Fcc. That is, it
+ asks if you really meant to have no recipients. If this feature is
+ set, you will _not_ be prompted to confirm your intent to make only a
+ copy of a message with no recipients.
+ This feature is closely related to
+ warn-if-blank-to-and-cc-and-newsgroups. The difference between this
+ feature and that feature is that this feature considers a Bcc to be a
+ recipient while that feature will ask for confirmation even if there
+ is a Bcc when there is no To, Cc, or Newsgroup. The default values
+ also differ. This feature defaults to asking the question and you
+ have to turn it off. The warn-if-blank-to-and-cc-and-newsgroups
+ feature defaults to not asking unless you turn it on.
+ This feature is displayed as "Send to Fcc Only Without Confirming".
+ _fcc-without-attachments_
+ This features controls the way FCC's (File Carbon Copies) are made of
+ the messages you send.
+ Normally, _Alpine_ saves an exact copy of your message as it was
+ sent. When this feature is enabled, the "body" of the message you
+ send (the text you type in the composer) is preserved in the copy as
+ before, however all attachments are replaced with text explaining
+ what had been sent rather than the attachments themselves.
+ This feature also affects _Alpine_'s "Send ?" confirmation prompt in
+ that a new "^F Fcc Attchmnts" option becomes available which allows
+ you to interactively set whether or not attachments are saved to the
+ Fcc'd copy.
+ This feature is displayed as "Fcc Does Not Include Attachments".
+ _force-arrow-cursor_
+ This feature affects _Alpine_'s MESSAGE INDEX display routine. If
+ set, the normal inverse-video cursor will be replaced by a simple
+ "arrow" cursor, which normally occupies the second column of the
+ index display.
+ This is the same index cursor you get if you turn on
+ Assume-Slow-Link, but the index line coloring will still be present
+ if this feature is turned on and Assume-Slow-Link is off.
+ An alternative version of the Arrow cursor is available by including
+ the ARROW token in the Index-Format option.
+ It ought to be the case that this feature also affects the ATTACHMENT
+ INDEX, but that is not implemented.
+ _hide-nntp-path_
+ Normally the Path header that _Alpine_ generates when posting to a
+ newsgroup contains the name of the computer from which the message is
+ being sent and the user name. Some believe that this information is
+ used by spammers. If this feature is set, that information will be
+ replaced with the text
+
+ not-for-mail
+ instead.
+ It should be noted that many servers being connected to will still
+ reveal the information that this feature attempts to protect.
+ _include-attachments-in-reply_
+ If set, any MIME attachments that were part of the original message
+ will automatically be included in a _Reply_.
+ _include-header-in-reply_
+ If set, and a message being replied to is included in the _Reply_,
+ then headers from that message will also be part of the reply.
+ _include-text-in-reply_
+ Normally, _Alpine_ will ask whether you wish to include the original
+ message in your _Reply_. If this feature is set and the feature
+ enable-reply-indent-string-editing is _not_ set, then the original
+ message will be included in the reply automatically, without
+ prompting.
+ _incoming-checking-includes-total_
+ This option has no effect unless the feature
+ enable-incoming-folders-checking is set, which in turn has no effect
+ unless incoming-folders is set.
+ When incoming folder checking is turned on the default is to display
+ the number of unseen messages in each folder. More precisely, it is
+ the number of undeleted unseen messages. Using this option you may
+ also display the total number of messages in each folder. Instead of
+ a single number representing the number of unseen messages you will
+ get two numbers separated by a slash character. The first is the
+ number of unseen messages and the second is the total number of
+ messages.
+ You may also use the recent message count instead of the unseen
+ message count by turning on the feature
+ incoming-checking-uses-recent.
+ _incoming-checking-uses-recent_
+ This option has no effect unless the feature
+ enable-incoming-folders-checking is set, which in turn has no effect
+ unless incoming-folders is set.
+ When incoming folder checking is turned on the default is to display
+ the number of unseen messages in each folder. More precisely, it is
+ the number of undeleted unseen messages. Using this option you may
+ display the number of recent messages instead of the number of unseen
+ messages. A message is only counted as recent if this is the first
+ session to see it, so the recent count might be less than the unseen
+ count. The difference between the two would be accounted for by the
+ unseen messages in the folder which were there previously but have
+ not been looked at yet.
+ If you simultaneously run more than one email client at a time (for
+ example, you run more than one _Alpine_ in parallel) then turning
+ this feature on can cause some confusion. The confusion stems from
+ the fact that each message is only considered to be recent in one
+ session. That means that the counts of new messages may be different
+ in the two _Alpine_s running side by side, because each incoming
+ message will only be counted as recent in one of the two sessions.
+ You may also display the total number of messages in each folder by
+ using the incoming-checking-includes-total option.
+ _ldap-result-to-addrbook-add_
+ This is only available if _Alpine_ was linked with an LDAP library
+ when it was compiled. If both the per-directory-server option
+ use-implicitly-from-composer and this feature are set, then when an
+ implicit directory lookup is done from the composer you will
+ automatically be prompted to add the result of the directory lookup
+ to your address book.
+ This feature is displayed as "LDAP Result to Addressbook Add".
+ _maildrops-preserve-state_
+ This feature affects the way Mail Drops work. Normally, when mail is
+ moved from a Mail Drop folder to a destination folder, the state
+ changes that have taken place since the mail was originally delivered
+ are lost. Any Seen/New, Answered, Important/Flagged state that has
+ changed will be ignored. All of the mail will be considered unSeen,
+ unAnswered, and unImportant after it is moved.
+ If this feature is set, then the state changes will not be lost.
+ In any case, messages which are already marked Deleted when the mail
+ is to be copied from the Mail Drop will be ignored.
+ _mark-fcc-seen_
+ This features controls the way FCCs (File Carbon Copies) are made of
+ the messages you send. Normally, when _Alpine_ saves a copy of a
+ message you sent as an Fcc, that copy will be marked as Unseen. When
+ you look at the folder it was saved in the message will appear to be
+ a New message until you read it. When this feature is enabled, the
+ message will be marked as having been Seen.
+ _mark-for-cc_
+ This feature affects _Alpine_'s MESSAGE INDEX display. By default, a
+ '+' is displayed in the first column if the message is addressed
+ directly to you. When this feature is set and the message is not
+ addressed to you, then a '-' character is displayed if the message is
+ instead Cc'd directly to you.
+ _mult-newsrc-hostnames-as-typed_
+ This feature will be of little use to most users. It has no effect
+ unless the feature Enable-Multiple-Newsrcs is set. When the
+ Enable-Multiple-Newsrcs feature is set then the setting of this
+ feature may have an effect on the names of the newsrc files used.
+ Normally, the name of the news server will be canonicalized before it
+ is used in the newsrc file name. For example, if you type the news
+ server name
+
+ servername
+ it is likely that the canonical name will be something like
+
+ servername.example.com
+ Or it may be the case that
+
+ servername.example.com
+ is really an alias (a DNS CNAME) for
+
+ othername.example.com
+ If this feature is not set, then the canonicalized names will be
+ used. If this feature is set, then the name you typed in (or put in
+ your configuration) will be used.
+ This feature is displayed as "Multiple Newsrc Hostnames as Typed".
+ _news-approximates-new-status_
+ This feature causes certain messages to be marked as _New_ in the
+ MESSAGE INDEX of newsgroups. This feature is set by default.
+ When opening a newsgroup, _Alpine_ will consult your _newsrc_ file
+ and determine the last message you have previously disposed of via
+ the _D_ key. If this feature is set, any subsequent messages will be
+ shown in the Index with an _N_, and the first of these messages will
+ be highlighted. Although this is only an approximation of true _New_
+ or _Unseen_ status, it provides a useful cue to distinguish
+ more-or-less recent messages from those you have seen previously, but
+ are not yet ready to mark deleted.
+ Background: your _newsrc_ file (used to store message status
+ information for newsgroups) is only capable of storing a single flag,
+ and _Alpine_ uses this to record whether or not you are "done with" a
+ message, as indicated by marking the message as _Deleted_.
+ Unfortunately, this means that _Alpine_ has no way to record exactly
+ which messages you have previously seen, so it normally does not show
+ the _N_ status flag for any messages in a newsgroup. This feature
+ enables a starting _approximation_ of seen/unseen status that may be
+ useful.
+ _news-deletes-across-groups_
+ This feature controls what _Alpine_ does when you delete a message in
+ a newsgroup that appears in more than one newsgroup. Such a message
+ is sometimes termed a "crossposting" in that it was posted across
+ several newsgroups.
+ _Alpine_'s default behavior when you delete such a message is to
+ remove only the copy in the current newsgroup from view when you use
+ the "Exclude" command or the next time you visit the newsgroup.
+ Enabling this feature causes _Alpine_ to remove every occurrence of
+ the message from all newsgroups it appears in and to which you are
+ subscribed.
+ NOTE: As currently implemented, enabling this feature may increase
+ the time it takes the Expunge command and newsgroup closing to
+ complete.
+ _news-offers-catchup-on-close_
+ This feature controls what _Alpine_ does as it closes a newsgroup.
+ When set, _Alpine_ will offer to delete all messages from the
+ newsgroup as you are quitting _Alpine_ or opening a new folder.
+ This feature is useful if you typically read all the interesting
+ messages in a newsgroup each time you open it. This feature saves you
+ from having to delete each message in a newsgroup as you read it or
+ from selecting all the messages and doing an aggregate delete before
+ you move on to the next folder or newsgroup.
+ _news-post-without-validation_
+ This feature controls whether the NNTP server is queried as
+ newsgroups are entered for posting. Validation over slow links (e.g.
+ dialup using SLIP or PPP) can cause delays. Set this feature to
+ eliminate such delays.
+ _news-read-in-newsrc-order_
+ This feature controls the order that newsgroups will be presented. If
+ set, they will be presented in the same order as they occur in your
+ _newsrc_ file. If not set, the newsgroups will be presented in
+ alphabetical order.
+ _next-thread-without-confirm_
+ This feature controls an aspect of _Alpine_'s Next and Prev commands
+ in the case where you are using one of the "separate-index-screen"
+ styles for the configuration option threading-index-style and
+ currently have the folder sorted by a Threaded or OrderedSubject
+ sort. When you are Viewing a particular thread you have a MESSAGE
+ INDEX of only the messages in that thread. If you press the Next
+ command with the last message in the thread highlighted you will
+ normally be asked if you want to "View next thread?", assuming there
+ is a next thread to view. If this feature is set it will be assumed
+ that you always want to view the next thread and you won't be asked
+ to confirm that. Similarly, if the first message of the thread is
+ highlighted and you press the Prev command, this feature will prevent
+ the question "View previous thread".
+ This feature only has an effect in the MESSAGE INDEX screen. If you
+ then view a particular message from that screen and press the Next
+ command, you will be sent to the next thread without being asked,
+ independent of the setting of this feature.
+ The feature auto-open-next-unread, also has some similar effects.
+ This feature is displayed as "Read Next Thread Without Confirming".
+ _offer-expunge-of-inbox_
+ The INBOX is normally treated differently from regular folders in
+ several ways. One of the differences is that the normal "close"
+ sequence of events is deferred until _Alpine_ is exited, instead of
+ happening when you leave the INBOX to view another folder. The
+ "close" sequence normally includes the Expunging of deleted messages
+ (either automatically or after a prompt, controlled by the features
+ Expunge-Without-Confirm, Expunge-Without-Confirm-Everywhere, and
+ Expunge-Only-Manually), and the handling of the Read-Message-Folder.
+ If this feature is set the "close" sequence handling will take place
+ every time you leave the INBOX. The INBOX will still be kept open,
+ but the offer to Expunge and the archiving to the Read-Message-Folder
+ will take place each time you leave the INBOX instead of only once at
+ the end of the session.
+ _offer-expunge-of-stayopen-folders_
+ This feature is related to the option Stay-Open-Folders. Stay Open
+ folders are treated differently from regular folders in several ways.
+ One of the differences is that the normal "close" sequence of events
+ is deferred until _Alpine_ is exited, instead of happening when you
+ leave the folder to view another folder. The "close" sequence
+ normally includes the Expunging of deleted messages (either
+ automatically or after a prompt, controlled by the features
+ Expunge-Without-Confirm, Expunge-Without-Confirm-Everywhere, and
+ Expunge-Only-Manually), and the handling of Incoming-Archive-Folders.
+ If this feature is set the "close" sequence handling will take place
+ when you leave the Stay Open folder. The folder will still be kept
+ open, but the offer to Expunge and the archiving will take place each
+ time you leave the folder instead of only once at the end of the
+ session. This feature does not affect the INBOX, which will still
+ only be processed when you exit _Alpine_.
+ _pass-c1-control-characters-as-is_
+ It is probably not useful to set this option. This is a legacy option
+ left behind "just in case". Multi-byte characters which have an octet
+ which has the same value as a control character are permitted through
+ whether or not this option is turned on.
+ If the feature pass-control-characters-as-is is set, then this
+ feature has no effect. However, if you wish to filter out regular
+ control characters but pass the so-called C1 control characters (0x80
+ <= char < 0xA0) through unchanged, then you may leave
+ pass-control-characters-as-is unset and set this feature.
+ _pass-control-characters-as-is_
+ It is probably not useful to set this option. This is a legacy option
+ left behind "just in case". Multi-byte characters which have an octet
+ which has the same value as a control character are permitted through
+ whether or not this option is turned on.
+ If set, all characters in a message will be sent to the screen.
+ Normally, control characters are automatically suppressed in order to
+ avoid inadvertently changing terminal setup parameters. Control
+ characters are usually displayed as two character sequences like
+
+ ^C
+ for Control-C,
+
+ ^[
+ for ESCAPE,
+
+ ^?
+ for DELETE, and
+
+ ~E
+ for the character with value 133 (0x85). (The DEL character is
+ displayed as ^?, regular control characters are displayed as the
+ character ^ followed by the character obtained by adding the five
+ low-order bits of the character to 0x40, and the C1 control
+ characters 0x80 - 0x9F are displayed as the character ~ followed by
+ the character obtained by adding the five low-order bits of the
+ character to 0x40.) Sometimes, in cases where changing a single
+ control character into a two-character sequence would confuse
+ _Alpine_'s display routines, a question mark is substituted for the
+ control character.
+ If you wish to filter out regular control characters but pass the
+ so-called C1 control characters (0x80 <= char < 0xA0) through
+ unchanged, then you may leave this feature unset and set the feature
+ pass-c1-control-characters-as-is instead.
+ _predict-nntp-server_
+ This feature allows _Alpine_ to assume that the open NNTP server at
+ the time of composition is the NNTP server to which the message
+ should be posted. This is especially recommended when there are
+ multiple News collections. If this feature is not set, _Alpine_ will
+ try to post to the first server in the nntp-server variable. Setting
+ this feature also negates the need to add News collection servers to
+ the nntp-server variable.
+ This feature can be especially handy when used in conjunction with
+ enable-multiple-newsrcs.
+ This option is displayed as "NNTP Server (for news)".
+ _prefer-plain-text_
+ A message being viewed may contain alternate versions of the same
+ content. Those alternate versions are ordered by the sending software
+ such that the first alternative is the least preferred and the last
+ alternative is the most preferred. _Alpine_ will normally display the
+ most-preferred version that it knows how to display. This is most
+ often encountered where the two alternate versions are a plain text
+ version and an HTML version, with the HTML version listed last as the
+ most preferred.
+ If this option is set, then any plain text version will be preferred
+ to all other versions.
+ _preopen-stayopen-folders_
+ This feature is related to the option Stay-Open-Folders. Normally,
+ Stay Open folders are only opened on demand, when the user asks to
+ open them. From then on they are kept open for the duration of the
+ session. However, if this feature is set, then the Stay Open folders
+ will all be opened at startup, at the same time that the INBOX is
+ opened.
+ _preserve-start-stop-characters_
+ This feature controls how special control key characters, typically
+ _^S_ and _^Q_, are interpreted when input to _Alpine_. These
+ characters are known as the "start" and "stop" characters and are
+ sometimes used in communications paths to control data flow between
+ devices that operate at different speeds.
+ By default, _Alpine_ turns the system's handling of these special
+ characters off except during printing. However, if you see _Alpine_
+ reporting input errors such as:
+
+ [ Command "^Q" not defined for this screen. ]
+ and, at the same time, see your display become garbled, then it is
+ likely that setting this option will solve the problem. Be aware,
+ though, that enabling this feature will also cause _Alpine_ to
+ ostensibly "hang" whenever the _Ctrl-S_ key combination is entered as
+ the system is now interpreting such input as a "stop output" command.
+ To "start output" again, simply type _Ctrl-Q_.
+ This feature is displayed as "Preserve Start/Stop Characters".
+ _print-formfeed-between-messages_
+ Setting this feature causes a formfeed to be printed between messages
+ when printing multiple messages with the _Apply Print_ command.
+ _print-includes-from-line_
+ If this feature is set, then the Unix mail style From line is
+ included at the start of each message that is printed. This line
+ looks something like the following, with the address replaced by the
+ address from the From line of the message being printed:
+
+ From user@domain.somewhere.com Mon May 13 14:11:06 1996
+ _print-index-enabled_
+ This feature controls the behavior of the _Print_ command when in the
+ "Folder Index" screen. If set, the _Print_ command will give you a
+ prompt asking if you wish to print the message index, or the
+ currently highlighted message. If not set, the message will be
+ printed.
+ _print-offers-custom-cmd-prompt_
+ When this feature is set, the _Print_ command will have an additional
+ subcommand called _C CustomPrint_. If selected, you will have the
+ opportunity to enter any system print command, instead of being
+ restricted to using those that have been previously configured in the
+ _Setup/Printer_ screen.
+ This feature is displayed as "Print Offers Custom Command Prompt".
+ _prune-uses-yyyy-mm_
+ By default, _Alpine_ asks monthly whether or not you would like to
+ rename some folders to a new name containing the date. It also asks
+ whether or not you would like to delete some old folders. See the
+ pruning-rule option for an explanation.
+ By default, the name used when renaming a folder looks like
+
+ <foldername>-<month>-<year>
+ For example, the first time you run _Alpine_ in May of 2004, the
+ folder "sent-mail" might be renamed to
+
+ sent-mail-apr-2004
+ If this feature is set, the name used will be of the form
+
+ <foldername>-<yyyy>-<mm>
+ where "yyyy" is the year and "mm" is the two-digit month (01, 02,
+ ..., 12). For the April, 2004 example above, it would instead be
+
+ sent-mail-2004-04
+ because April is the 4th month of the year. A reason you might want
+ to set this feature is so that the folders will sort in chronological
+ order.
+ _publiccerts-in-keychain_
+ Mac OS X _Alpine_ only.
+ If this feature is set the Mac OS X default keychain will be used as
+ the place to store public certificates instead of a
+ smime-public-cert-directory or a smime-public-cert-container.
+ This feature is displayed as "S/MIME -- Public Certs in MacOS
+ Keychain".
+ _quell-attachment-extension-warn_
+ This feature suppresses the extra warning you can get when trying to
+ view an attachment for which there is no mime-type match. Turning on
+ this feature will just run the program according to extension instead
+ of first warning the user that it will run according to the file's
+ extension.
+ This feature can be used along side quell-attachment-extra-prompt to
+ preserve the behavior exhibited in _Pine_ versions prior to _Pine_
+ 4.50.
+ This feature is displayed as "Suppress Attachment Extension Warning".
+ _quell-attachment-extra-prompt_
+ By default, when you attempt to view an attachment externally from
+ the "Attachment View" screen, you are asked if you really want to
+ view the selected attachment.
+ If this feature is set, you will _not_ be prompted to confirm your
+ selection. Prior to _Pine_ 4.50, the default behavior was to not
+ prompt. This feature was added for those wanting to preserve that
+ behavior.
+ This feature is displayed as "Suppress Attachment Extra Prompt".
+ _quell-berkeley-format-timezone_
+ POSIX mandates a timezone in UNIX mailbox format folder delimiters
+ (the line which begins with From ). Some versions of Berkeley mail
+ have trouble with this, and don't recognize the line as a message
+ delimiter. If this feature is set, the timezone will be left off the
+ delimiter line.
+ This feature is displayed as "Suppress Berkeley Format Timezone".
+ _quell-charset-warning_
+ By default, if the message you are viewing contains characters that
+ are not representable in your display-character-set then _Alpine_
+ will add a warning to the start of the displayed text. If this option
+ is set, then that editorial message will be suppressed.
+ Setting this feature also suppresses the comment about the character
+ set in header lines. For example, when viewing a message you might
+ see
+
+ From: "[ISO-8859-2] Name" <address>
+ in the From header if your Character-Set is something other than
+ ISO-8859-2. If you set this feature, the comment about the character
+ set will no longer be there.
+ This feature is displayed as "Suppress Character Set Warning".
+ _quell-content-id_
+ This feature changes the behavior of _Alpine_ when sending messages.
+ It is intended to work around a bug in Microsoft's Outlook XP mail
+ user agent. As of this writing, Microsoft has acknowledged the bug
+ but has not added it to the Knowledge Base. We have been told that
+ there will be a post-SP1 hotfix for Outlook XP. This particular bug
+ has bug fix number OfficeQFE:4781. The nature of the bug is that
+ messages with attachments which contain a Content-ID header (which
+ standard _Alpine_ attachments do) do not show the attachment
+ indicator (a paperclip) when viewed with Outlook XP. So the user has
+ no indication that the message contains an attachment.
+ If this feature is set then _Alpine_ will remove most Content-ID
+ headers before sending a message. If an attachment is of type
+ MESSAGE, then the existing Content-ID headers inside the message will
+ be left intact. This would only happen with _Alpine_ if a message was
+ forwarded as an attachment or if a message with a message attached
+ was forwarded. Similarly if an attachment of type
+ MULTIPART/ALTERNATIVE is forwarded, the Content-ID headers of the
+ alternative parts will not be removed.
+ Because the Content-ID header is a standard part of MIME it is
+ possible that setting this feature will break something. For example,
+ if an attachment has a Content-ID header which is necessary for the
+ correct functioning of that attachment, it is possible that _Alpine_
+ may remove that header when the attachment is forwarded. However, it
+ seems fairly safe at this time.
+ This feature is displayed as "Suppress Content-ID".
+ _quell-dead-letter-on-cancel_
+ This feature affects _Alpine_'s behavior when you cancel a message
+ being composed. _Alpine_'s usual behavior is to write the canceled
+ message to a file named dead.letter in your home directory (under
+ UNIX; DEADLETR under WINDOWS/DOS) overwriting any previous message.
+ Under some conditions (some routine), this can introduce a noticeable
+ delay.
+ Setting this feature will cause _Alpine_ NOT to write canceled
+ compositions into the file called dead.letter.
+ This feature affects the newer option Dead-Letter-Files, which
+ specifies the number of dead letter files to keep around. If this
+ feature is set, then the Dead-Letter-Files option has no effect.
+ This feature is displayed as "Do Not Save to Deadletter on Cancel".
+ _quell-empty-directories_
+ This feature causes _Alpine_ to remove from the display any
+ directories that do not contain at least one file or directory. This
+ can be useful to prevent overly cluttered folder lists when a
+ collection is stored on a server that treats all names as both a
+ folder and a directory.
+ Note, enabling this feature can cause surprising behavior! For
+ example, you can still use Add to create a directory, but unless you
+ immediately enter that directory and create a folder, that newly
+ created directory may not be displayed next time you enter the folder
+ list.
+ This feature is displayed as "Hide Empty Directories".
+ _quell-extra-post-prompt_
+ This feature causes _Alpine_ to skip the extra question about posting
+ a message which may go to thousands of readers when you are about to
+ post to a newsgroup.
+ This feature is displayed as "Suppress Extra Posting Prompt".
+ _quell-filtering-done-message_
+ This feature causes _Alpine_ to suppress the "filtering done"
+ message.
+ This feature is displayed as "Suppress Filtering Done Message".
+ _quell-filtering-messages_
+ This feature causes _Alpine_ to suppress the messages about moving
+ filtered messages and setting flags in messages, due to Filter Rules.
+ This feature is displayed as "Suppress Filtering Messages".
+ _quell-flowed-text_
+ _Alpine_ generates flowed text where possible. The method for
+ generating flowed text is defined by RFC 3676, the benefit of doing
+ so is to send message text that can properly be viewed both on normal
+ width displays and on displays with smaller or larger than normal
+ screen widths. With flowed text, a space at the end of a line tells
+ the receiving mail client that the following line belongs to the same
+ paragraph. Quoted text will also be affected, with only the innermost
+ level of ">" quoting being followed by a space. However, if you have
+ changed the "Reply-Indent-String" so that it is not equal to the
+ default value of "> ", then quoted text will not be flowed. For this
+ reason, we recommend that you leave your "Reply-Indent-String" set to
+ the default.
+ This feature turns off the generation of flowed text, as it might be
+ desired to more tightly control how a message is displayed on the
+ receiving end.
+ If this feature is _not_ set, you can control on a message by message
+ basis whether or not flowed text is generated. You do this by typing
+ ^V at the Send confirmation prompt that you get after typing ^X to
+ send a message. ^V is a toggle which turns flowing off and back on if
+ typed again. If for some reason flowing cannot be done on a
+ particular message, then the ^V command will not be available. This
+ would be the case, for example, if this feature was set, or if your
+ "Reply-Indent-String" was set to a non-default value. If the feature
+ Send-Without-Confirm is set, then the opportunity to control on a
+ message by message basis whether or not flowed text is generated is
+ lost.
+ When this feature is not set and you have typed ^V to turn off
+ flowing, the Send confirmation prompt will change to look like
+
+ Send message (not flowed)?
+ Strip-Whitespace-Before-Send will also turn off the sending of flowed
+ text messages, but it differs in that it also trims all trailing
+ white space from a message before sending it.
+ If alternate editors are used extensively, be aware that a message
+ will still be sent flowed if this feature is unset. In most cases
+ this will be fine, but if the editor has a "flowed text" mode, it
+ would be best to use that.
+ This feature is displayed as "Do Not Send Flowed Text".
+ _quell-folder-internal-msg_
+ This feature determines whether or not _Alpine_ will create "pseudo
+ messages" in folders that are in standard Unix or MMDF format.
+ _Alpine_ will normally create these pseudo messages when they are not
+ already present in a standard Unix or MMDF folder. Their purpose is
+ to record certain mailbox state data needed for correct IMAP and POP
+ server operation, and also for _Alpine_ to be able to mark messages
+ as Answered when the Reply has been postponed.
+ Sites which do not use IMAP/POP for remote mail access, and which
+ need to support mail tools that are adversely affected by the
+ presence of the pseudo-messages (e.g. some mail notification tools)
+ may enable this feature to tell _Alpine_ not to create them. Note
+ that _Alpine_'s "Answered" flag capability will be adversely affected
+ if this is done.
+ Note too that, even if this feature is enabled, _Alpine_ will not
+ remove pseudo-messages when it encounters them (e.g. those created by
+ UW's imapd or ipopd servers.) This feature has no effect on folders
+ that are not in standard Unix or MMDF format, as pseudo-messages are
+ not needed in the other formats to record mailbox state information.
+ This feature is displayed as "Prevent Folder Internal Message".
+ _quell-full-header-auto-reset_
+ The HdrMode Command normally resets to the default state when
+ switching to a new message. For example, if you've used the "H"
+ command to turn on Full Headers for a message you are viewing, and
+ then you type the Next command to look at the next message, the full
+ headers will no longer be shown. Setting this feature disables that
+ reset. Instead, the Header Mode remains the same from message to
+ message.
+ The presence or absence of the HdrMode command is determined by the
+ "Enable-Full-Header-Cmd" Feature-List option.
+ This feature is displayed as "Suppress Full Header Auto Reset".
+ _quell-imap-envelope-update_
+ In the MESSAGE INDEX screen, if the open folder is being accessed
+ using IMAP, _Alpine_ normally tries to paint the index lines on the
+ screen as soon as the information arrives from the IMAP server. This
+ means that the index information makes it onto the screen more
+ quickly than it otherwise would. This sometimes results in behavior
+ that bothers some users. For example, when paging to a new page of
+ the index, it may be possible for the lines to be painted on the
+ screen in a random order, rather than from top to bottom.
+ Setting this feature causes _Alpine_ to wait for all of the
+ information to be gathered before it paints the index screen. Once it
+ collects all of the information, the screen will be painted quickly
+ from top to bottom.
+ This feature is displayed as "Suppress IMAP Envelope Update".
+ _quell-lock-failure-warnings_
+ This feature affects _Alpine_'s behavior when it encounters a problem
+ acquiring a mail folder lock. Typically, a secondary file associated
+ with the mail folder being opened is created as part of the locking
+ process. On some systems, such file creation has been
+ administratively precluded by the system configuration.
+ _Alpine_ issues a warning when such failures occur, which can become
+ bothersome if the system is configured to disallow such actions.
+ Setting this feature causes _Alpine_ to remain silent when this part
+ of lock creation fails.
+ WARNING: systems that have been configured in a way that precludes
+ locking introduce some risk of mail folder corruption when more than
+ one program attempts to modify the mail folder. This is most likely
+ to occur to one's _INBOX_ or other "Incoming Message Folder".
+ This feature is displayed as "Suppress Lock Failure Warnings".
+ _Quell-Mailchecks-Composing-Except-Inbox_
+ This option is closely related to the Mail-Check-Interval option, the
+ Mail-Check-Interval-Noncurrent option, and
+ Quell-Mailchecks-Composing-Inbox.
+ If this option is set, then the normal new-mail checking which
+ happens while you are composing will not happen for folders other
+ than your INBOX (which depends on the setting of
+ "Quell-Mailchecks-Composing-Inbox").
+ You might want to set this option if you are experiencing delays
+ while composing which you think might be related to the speed of the
+ new-mail checks.
+ Even with this option turned on, an occasional new-mail check may be
+ done in order to keep the server from killing the connection to the
+ folder. For example, IMAP servers may remove a connection to a folder
+ if there has been no activity on the connection for 30 minutes or
+ more. Instead of letting that happen, _Alpine_ will check for new
+ mail before the 30 minutes is up even though you have turned on this
+ feature to quell those checks.
+ Besides new-mail checks, checkpoint operations on the folders will
+ also be quelled when you set this option. The purpose of
+ checkpointing is to write the changes to a folder out to disk
+ periodically in order to avoid losing those changes when system or
+ software problems occur. New-mail checking and checkpointing while
+ you are not composing are not affected by this option.
+ This feature is displayed as "Prevent Mailchecks While Composing
+ Except for INBOX".
+ _Quell-Mailchecks-Composing-Inbox_
+ This option is closely related to the Mail-Check-Interval option, the
+ Mail-Check-Interval-Noncurrent option, and
+ Quell-Mailchecks-Composing-Except-Inbox.
+ If this option is set, then the normal new-mail checking which
+ happens while you are composing will not happen for your INBOX.
+ Checking of other folders is controlled in a similar way with the
+ "Quell-Mailchecks-Composing-Except-Inbox" option.
+ You might want to set this option if you are experiencing delays
+ while composing which you think might be related to the speed of the
+ new-mail checks.
+ Even with this option turned on, an occasional new-mail check may be
+ done in order to keep the server from killing the connection to the
+ folder. For example, IMAP servers may remove a connection to a folder
+ if there has been no activity on the connection for 30 minutes or
+ more. Instead of letting that happen, _Alpine_ will check for new
+ mail before the 30 minutes is up even though you have turned on this
+ feature to quell those checks.
+ Besides new-mail checks, checkpoint operations on the INBOX will also
+ be quelled when you set this option. The purpose of checkpointing is
+ to write the changes to a folder out to disk periodically in order to
+ avoid losing those changes when system or software problems occur.
+ New-mail checking and checkpointing while you are not composing are
+ not affected by this option.
+ This feature is displayed as "Prevent Mailchecks While Composing for
+ INBOX".
+ _quell-maildomain-warning_
+ When your configuration is set up so that your domain name contains
+ no dots, it is usually a configuration error. By default, _Alpine_
+ will warn you about this when you start it up. You will see a warning
+ message that looks like
+
+ Incomplete maildomain "<domain>".
+ If this feature is set, the warning is turned off. This feature is
+ displayed as "Suppress Maildomain Warning".
+ _quell-news-envelope-update_
+ In the MESSAGE INDEX screen, if the open folder is being accessed
+ using NNTP (News), _Alpine_ normally tries to paint the index lines
+ on the screen as soon as the information arrives from the NNTP
+ server. This means that the index information makes it onto the
+ screen more quickly than it otherwise would. This sometimes results
+ in behavior that bothers some users. For example, when paging to a
+ new page of the index, it may be possible for the lines to be painted
+ on the screen in a random order, rather than from top to bottom.
+ Setting this feature causes _Alpine_ to wait for all of the
+ information to be gathered before it paints the index screen. Once it
+ collects all of the information, the screen will be painted quickly
+ from top to bottom.
+ This feature is displayed as "Suppress News Envelope Update".
+ _quell-partial-fetching_
+ Partial fetching is a feature of the IMAP protocol. By default,
+ _Alpine_ will use partial fetching when copying the contents of a
+ message or attachment from the IMAP server to _Alpine_. This means
+ that the fetch will be done in many small chunks instead of one big
+ chunk. The main benefit of this approach is that the fetch becomes
+ interruptible. That is, the user can type _^C_ to stop the fetch
+ early. In some cases partial fetching may cause a performance problem
+ so that the fetching of data takes significantly longer when partial
+ fetching is used. Turning on this feature will turn off partial
+ fetching.
+ This feature is displayed as "Prevent Partial Fetching".
+ _quell-personal-name-prompt_
+ _PC-Alpine_ only. This feature quells the prompting for a
+ personal-name. This prompt normally happens before composing a
+ message, and only happens when there is no personal name already set.
+ _quell-server-after-link-in-html_
+ By default, links in HTML text are displayed with the host the link
+ references appended, within square brackets, to the link text.
+ _Alpine_ does this to help indicate where a link will take you,
+ particularly when the link text might suggest a different
+ destination.
+ Setting this feature will prevent the server name from being appended
+ to the displayed text.
+ This feature is displayed as "Suppress Server After Link in HTML".
+ _quell-ssl-largeblocks_
+ This feature (_PC-Alpine_ only) changes the behavior of fetching
+ messages and attachments so that the message data is fetched in
+ chunks no larger than 12K bytes. This works around a bug in
+ Microsoft's SSL/TLS support. Some versions of Microsoft SSL are not
+ able to read full-sized (16K) SSL/TLS packets. Some servers will send
+ such packets and this will cause _PC-Alpine_ to crash with the error
+
+ incomplete SecBuffer exceeds maximum buffer size
+ Microsoft is aware of the problem and has developed a hotfix for it,
+ but as of this writing the hotfix has not yet been added to the
+ Knowledge Base.
+ This feature is displayed as "Prevent SSL Largeblocks".
+ _quell-status-message-beeping_
+ If set status messages will never emit a beep.
+ This feature is displayed as "Suppress Status Message Beeping".
+ _quell-timezone-comment-when-sending_
+ Normally, when _Alpine_ generates a Date header for outgoing mail, it
+ will try to include the symbolic timezone at the end of the header
+ inside parentheses. The symbolic timezone is often three characters
+ long, but on some operating systems, it may be longer. Apparently
+ there are some SMTP servers in the world which will reject an
+ incoming message if it has a Date header longer than about 80
+ characters. If this feature is set, the symbolic timezone normally
+ generated by _Alpine_ will not be included. You probably don't need
+ to worry about this feature unless you run into the problem described
+ above.
+ This feature is displayed as "Suppress Timezone Comment When
+ Sending".
+ _quell-user-id-prompt_
+ _PC-Alpine_ only. This feature quells the prompting for a user-id if
+ the information can be obtained from the login name used to open the
+ INBOX. Normally, this prompt happens before composing a message, and
+ only happens when there is no user-id already set in the
+ configuration.
+ With this feature set, composing a message is only possible after
+ establishing a connection to the INBOX.
+ _quell-user-lookup-in-passwd-file_
+ This feature controls an aspect of _Alpine_'s Composer, and if
+ needed, will usually be set by the system manager in _Alpine_'s
+ system-wide configuration file. Specifically, if this feature is set,
+ _Alpine_ will not attempt to look in the system password file to find
+ a Full Name for the entered address.
+ Normally, names you enter into address fields (e.g. To: or Cc:) are
+ checked against your address book(s) to see if they match an address
+ book nickname. Failing that, (in Unix _Alpine_) the name is then
+ checked against the Unix password file. If the entered name matches a
+ username in the system password file, _Alpine_ extracts the
+ corresponding Full Name information for that individual, and adds
+ that to the address being entered.
+ However, password file matching can have surprising (incorrect)
+ results if other users of the system do not receive mail at the
+ domain you are using. That is, if either the user-domain or
+ use-only-domain-name option is set such that the administrative
+ domain of other users on the system isn't accurately reflected,
+ _Alpine_ should be told that a password file match is coincidental,
+ and Full Name info will be incorrect. For example, a personal name
+ from the password file could get falsely paired with the entered name
+ as it is turned into an address in the configured domain.
+ If you are seeing this behavior, enabling this feature will prevent
+ Unix _Alpine_ from looking up names in the password file to find the
+ Full Name for incomplete addresses you enter.
+ This feature is displayed as "Prevent User Lookup in Password File".
+ _quit-without-confirm_
+ This feature controls whether or not _Alpine_ will ask for
+ confirmation when a _Quit_ command is received.
+ This feature is displayed as "Quit Without Confirming".
+ _quote-replace-nonflowed_
+ This feature, which is only active when Quote-Replace-String is also
+ set, enables quote-replacement on non-flowed messages. It is off by
+ default because a non-flowed message is more dependent on its format,
+ and thus quote-replacement may cause less-than-pleasing results.
+ Setting this feature will cause quote-replacement similar to that of
+ flowed messages, but with the added possibility of long lines being
+ wrapped into new lines if the Quote-Replacement-String is longer than
+ the string it is replacing, which is "> ".
+ _reply-always-uses-reply-to_
+ If set, _Alpine_ will not prompt when a message being replied to
+ contains a _Reply-To:_ header value, but will simply use its value
+ (as opposed to using the _From:_ field's value).
+ _return-to-inbox-without-confirm_
+ Normally, when you use the TAB command and there are no more folders
+ or newsgroups to visit, you are asked if you want to return to the
+ INBOX. If this feature is set you will not be asked. It will be
+ assumed that you do want to return to the INBOX.
+ This feature is displayed as "Return to INBOX Without Confirming".
+ _save-aggregates-copy-sequence_
+ This feature will optimize an aggregate copy operation, if possible,
+ by issuing a single IMAP _COPY_ command with a list of the messages
+ to be copied. This feature is set by default. This may reduce network
+ traffic and elapsed time for the Save. _However, many IMAP servers
+ (including the UW IMAP server) do not preserve the order of messages
+ when this optimization is applied._ If this feature is not set,
+ _Alpine_ will copy each message individually and the order of the
+ messages will be preserved.
+ This feature is displayed as "Save Combines Copies (may be out of
+ order)".
+ _save-partial-msg-without-confirm_
+ This feature controls an aspect of _Alpine_'s Save command. By
+ default, when you Save a message that has some deleted parts, you
+ will be asked to confirm that you want to Save with a prompt that
+ looks like:
+
+ Saved copy will NOT include entire message! Continue?
+ If this feature is set, you will not be asked.
+ This feature is displayed as "Save Partial Message Without
+ Confirming".
+ _save-will-advance_
+ If set, _Save_ will (in addition to copying the current message to
+ the designated folder) also advance to the next message.
+ _save-will-not-delete_
+ If set, _Save_ will not mark the message Deleted (its default
+ behavior) after it has been copied to the designated folder.
+ _save-will-quote-leading-froms_
+ This feature controls an aspect of the _Save_ command (and also the
+ way outgoing messages are saved to an FCC folder). If set, _Alpine_
+ will add a leading > character in front of message lines beginning
+ with "From" when they are saved to another folder, including lines
+ syntactically distinguishable from the type of message separator line
+ commonly used on Unix systems.
+ The default behavior is that a > will be prepended only to lines
+ beginning with "From " that might otherwise be confused with a
+ message separator line on Unix systems. If _Alpine_ is the only mail
+ program you use, this default is reasonable. If another program you
+ use has trouble displaying a message with an unquoted From saved by
+ _Alpine_, you should enable this feature. This feature only applies to
+ the common Unix mailbox format that uses message separator lines
+ beginning with "From ". If _Alpine_ has been configured to use a
+ different mailbox format (possibly incompatible with other mail
+ programs), then this issue does not arise, and the feature is
+ irrelevant.
+ _scramble-message-id_
+ Normally the Message-ID header that _Alpine_ generates when sending a
+ message contains the name of the computer from which the message is
+ being sent. Some believe that this hostname could be used by spammers
+ or could be used by others for nefarious purposes. If this feature is
+ set, that name will be transformed with a simple Rot13
+ transformation. The result will still have the correct syntax for a
+ Message-ID but the part of the MessageID that is often a domain name
+ will not be an actual domain name because the letters will be
+ scrambled.
+ It is possible (but unlikely?) that some spam detection software will
+ use that as a reason to reject the mail as spam. It has also been
+ reported that some spam detection software uses the fact that there
+ are no dots after the "@" as a reason to reject messages. If your
+ _PC-Alpine_ Message-ID is using a name without a dot that is because
+ that is what Windows thinks is your "Full computer name". The method
+ used to set this varies from one type of Windows to another but check
+ under Settings -> Control Panel -> System and look for Network
+ Identification or Computer Name or something similar. How to set it
+ is beyond the scope of _Alpine_.
+ This feature is displayed as "Scramble the Message-ID When Sending".
+ _select-without-confirm_
+ This feature controls an aspect of _Alpine_'s _Save_, _Export_, and
+ _Goto_ commands. These commands all take text input to specify the
+ name of the folder or file to be used, but allow you to press _^T_
+ for a list of possible names. If set, the selected name will be used
+ immediately, without further opportunity to confirm or edit the name.
+ This feature is displayed as "Select Ctrl-T Foldername Without
+ Confirming".
+ _send-without-confirm_
+ By default, when you send or post a message you will be asked to
+ confirm with a question that looks something like:
+
+ Send message?
+ If this feature is set, you will _not_ be prompted to confirm your
+ intent to send and your message will be sent.
+ If this feature is set it disables some possibilities and renders
+ some other features meaningless. You will not be able to use Sending
+ Filters, Verbose sending mode, Background Sending, Delivery Status
+ Notifications, or ^V to turn off the generation of flowed text for
+ this message. These options are normally available as suboptions in
+ the Send prompt, but with no Send prompt the options are gone.
+ A somewhat related feature is quell-extra-post-prompt. which may be
+ used to eliminate the extra confirmation question when posting to a
+ newsgroup.
+ This feature is displayed as "Send Without Confirming".
+ _separate-folder-and-directory-display_
+ This feature affects folder collections wherein a folder and
+ directory can have the same name. By default, _Alpine_ displays them
+ only once, denoting that it is both a folder and directory by
+ appending the folder name with the hierarchy character enclosed in
+ square brackets.
+ Enabling this feature will cause _Alpine_ to display such names
+ separately marking the name representing a directory with a trailing
+ hierarchy delimiter (typically the slash, "/", character).
+ The feature also alters the command set slightly. By default, the
+ right-arrow descends into the directory, while hitting the Return key
+ will cause the folder by that name to be opened.
+ With this feature set, the Return key will open the highlighted
+ folder, or enter the highlighted directory.
+ _show-cursor_
+ If set, the system cursor will move to convenient locations in the
+ displays. For example, to the beginning of the status field of the
+ highlighted index line, or to the highlighted word after a successful
+ _WhereIs_ command. It is intended to draw your attention to the
+ _interesting_ spot on the screen.
+ _show-plain-text-internally_
+ This feature modifies the method _Alpine_ uses to display Text/Plain
+ MIME attachments from the Attachment Index screen. Normally, the
+ "View" command searches for any externally defined (usually via the
+ Mailcap file) viewer, and displays the selected text within that
+ viewer.
+ Enabling this feature causes _Alpine_ to ignore any external viewer
+ settings and always display text with _Alpine_'s internal viewer.
+ _show-selected-in-boldface_
+ This feature controls an aspect of _Alpine_'s aggregate operation
+ commands; in particular, the _Select_ and _WhereIs_ commands. _Select_
+ and _WhereIs_ (with the _^X_ subcommand) will search the current
+ folder for messages meeting a specified criteria, and _tag_ the
+ resulting messages with an _X_ in the first column of the applicable
+ lines in the "Folder Index". If this feature is set, instead of using
+ the _X_ to denote a selected message, _Alpine_ will attempt to
+ display those index lines in boldface. Whether this is preferable to
+ the _X_ will depend on personal taste and the type of terminal being
+ used.
+ _show-sort_
+ If this feature is set and there is sufficient space on the screen, a
+ short indication of the current sort order will be added in the
+ titlebar (the top line on the screen), before the name of the folder.
+ For example, with the default Arrival sort in effect, the display
+ would have the characters
+
+ [A]
+ added between the title of the screen and the folder name. The
+ letters are the same as the letters you may type to manually sort a
+ folder with the SortIndex command ($). The letters in the table below
+ are the ones that may show up in the titlebar line.
+
+ A _A_rrival
+ S _S_ubject
+ F _F_rom
+ T _T_o
+ C _C_c
+ D _D_ate
+ Z si_Z_e
+ O _O_rderedsubject
+ E scor_E_
+ H t_H_read
+ If the sort order is Reversed, the letter above will be preceded by
+ the letter "R", for example
+
+ [RS]
+ means that a Reverse Subject sort is in effect. For the case where
+ the sort is in Reverse Arrival order, the "A" is left out, and just
+ an "R" is shown.
+
+ [R]
+ This feature is displayed as "Show Sort in Titlebar".
+ _signature-at-bottom_
+ If this feature is set, and a message being _Repl_ied to is being
+ included in the reply, then the contents of the signature file (if
+ any) will be inserted after the included message. This feature does
+ not affect the results of a _Forward_ command.
+ _single-column-folder-list_
+ If set, the "Folder List" screen will list one folder per line
+ instead of several per line.
+ _slash-collapses-entire-thread_
+ Normally, the Collapse/Expand Thread command Collapses or Expands the
+ subthread which starts at the currently highlighted message, if any.
+ If this feature is set, then the slash command Collapses or Expands
+ the _entire_ current thread instead of just the subthread.
+ _smime-dont-do-smime_
+ UNIX _Alpine_ only.
+ Setting this feature turns off all of _Alpine_'s S/MIME support. You
+ might want to set this if you are having trouble due to the S/MIME
+ support.
+ + General S/MIME Overview
+ This feature is displayed as "S/MIME -- Turn off S/MIME".
+ _smime-encrypt-by-default_
+ UNIX _Alpine_ only.
+ This feature only has an effect if your version of _Alpine_ includes
+ support for S/MIME. It affects _Alpine_'s behavior when you send a
+ message. If this option is set, the "Encrypt" option will default to
+ ON when sending messages.
+ Only the default value is affected. In any case, you may still toggle
+ the Encrypt option on or off before sending with the "E Encrypt"
+ command (provided you have a the public digital ID for the
+ recipient).
+ + General S/MIME Overview
+ This feature is displayed as "S/MIME -- Encrypt by Default".
+ _smime-remember-passphrase_
+ UNIX _Alpine_ only.
+ This feature only has an effect if your version of _Alpine_ includes
+ support for S/MIME. If this option is set, you will only have to
+ enter your passphrase for your private key once during an _Alpine_
+ session.
+ + General S/MIME Overview
+ This feature is displayed as "S/MIME -- Remember S/MIME Passphrase".
+ _smime-sign-by-default_
+ UNIX _Alpine_ only.
+ This feature only has an effect if your version of _Alpine_ includes
+ support for S/MIME. It affects _Alpine_'s behavior when you send a
+ message. If this option is set, the "Sign" option will default to ON
+ when sending messages.
+ Only the default value is affected. In any case, you may still toggle
+ the Signing option on or off before sending with the "G Sign" command
+ (provided you have a personal digital ID certificate).
+ + General S/MIME Overview
+ This feature is displayed as "S/MIME -- Sign by Default".
+ _sort-default-fcc-alpha_
+ This feature controls an aspect of _Alpine_'s FOLDER LIST screen. If
+ set, the default FCC folder will be sorted alphabetically with the
+ other folders instead of appearing right after the INBOX.
+ This feature is displayed as "Sort Default Fcc Folder
+ Alphabetically".
+ _sort-default-save-alpha_
+ This feature controls an aspect of _Alpine_'s FOLDER LIST screen. If
+ set, the default save folder will be sorted alphabetically with the
+ other folders instead of appearing right after the INBOX (and default
+ FCC folder).
+ This feature is displayed as "Sort Default Save Folder
+ Alphabetically".
+ _spell-check-before-sending_
+ When this feature is set, every composed message will be
+ spell-checked before being sent.
+ _store-window-position-in-config_
+ Normally, _PC-Alpine_ will store its window size and position in the
+ Windows Registry. This is convenient if you want to use the same
+ remote configuration from more than one PC. If you use multiple
+ configuration files to start _PC-Alpine_, you may want to store the
+ window size and position in the configuration file instead of in the
+ Registry. Setting this feature causes that to happen.
+ _strip-from-sigdashes-on-reply_
+ This feature doesn't do anything if the feature enable-sigdashes is
+ turned on. However, if the _enable-sigdashes_ feature is not turned
+ on, then turning on this feature enables support for the convention
+ of not including text beyond the sigdashes line when Replying or
+ Following up to a message and including the text of that message.
+ In other words, this is a way to turn on the signature stripping
+ behavior without also turning on the dashes-adding behavior.
+ _strip-whitespace-before=send_
+ Trailing whitespace is not stripped from a message before sending.
+ Trailing whitespace should have no effect on an email message, and in
+ flowed text can aid in delimiting paragraphs. However, the old
+ behavior of stripping trailing whitespace was in place to better deal
+ with older clients that couldn't handle certain types of text
+ encodings. This feature restores the old behavior
+ Trailing whitespace is of aid to flowed-text-formatted messages,
+ which are generated by default but can be turned off via the
+ quell-flowed-text feature. strip-whitespace-before-send also has the
+ effect of turning off sending of flowed text.
+ This feature is displayed as "Strip Whitespace Before Sending".
+ _suppress-asterisks-in-password-prompt_
+ When you are running _Alpine_ you will sometimes be asked for a
+ password in a prompt on the third line from the bottom of the screen.
+ Normally each password character you type will cause an asterisk to
+ echo on the screen. That gives you some feedback to know that your
+ typing is being recognized. There is a very slight security risk in
+ doing it this way because someone watching over your shoulder might
+ be able to see how many characters there are in your password. If
+ you'd like to suppress the echoing of the asterisks set this feature.
+ _suppress-user-agent-when-sending_
+ If this feature is set then _Alpine_ will not generate a User-Agent
+ header in outgoing messages.
+ _tab-checks-recent_
+ In a FOLDER LIST screen, the TAB key usually just changes which
+ folder is highlighted. If this feature is set, then the TAB key will
+ cause the number of recent messages and the total number of messages
+ in the highlighted folder to be displayed instead.
+ This feature is displayed as "Tab Checks for Recent Messages".
+ _tab-uses-unseen-for-next-folder_
+ This feature affects _Alpine_'s behavior when using the TAB NextNew
+ Command to move from one folder to the next. _Alpine_'s usual
+ behavior is to search for folders with _Recent_ messages in them.
+ Recent messages are messages which have arrived since the last time
+ the folder was opened.
+ Setting this feature causes _Alpine_ to search for _Unseen_ messages
+ instead of Recent messages. Unseen messages remain Unseen until you
+ view them (or flag then as Seen with the Flag Command). Setting this
+ feature allows you to locate messages you have not read instead of
+ only recently received messages. When this feature is set, the
+ feature Enable-Fast-Recent-Test will have no effect, so the checking
+ may be slower.
+ Another reason why you might want to use this feature is that _Alpine_
+ sometimes opens folders implicitly behind the scenes, and this clears
+ the Recent status of all messages in the folder. One example where
+ this happens is when Saving or filtering a message to another folder.
+ If that message has some keywords set, then because of some
+ shortcomings in the IMAP specification, the best way to ensure that
+ those keywords are still set in the saved copy of the message is to
+ open the folder and set the keywords explicitly. Because this clears
+ the Recent status of all messages in that folder the folder will not
+ be found by the NextNew command unless this feature is set.
+ _tab-visits-next-new-message-only_
+ This feature affects _Alpine_'s behavior when using the _TAB_ key to
+ move from one message to the next. _Alpine_'s usual behavior is to
+ select the next _Unread_ message or message flagged as _Important_.
+ Setting this feature causes _Alpine_ to skip the messages flagged as
+ _Important_, and select _Unread_ messages exclusively. Tab behavior
+ when there are no new messages left to select remains unchanged.
+ _termdef-takes-precedence_
+ This feature may affect _Alpine_'s low-level input routines. Termcap
+ (or terminfo, depending on how your copy of _Alpine_ was compiled and
+ linked) is the name of the database which describes terminal
+ capabilities. In particular, it describes the sequences of characters
+ that various keys will emit.
+ An example would be the Up Arrow key on the keyboard. Up Arrow is not
+ a distinct character on most Unix systems. When you press the Up
+ Arrow key a short sequence of characters are produced. This sequence
+ is supposed to be described in the termcap database by the "ku"
+ capability (or by the "kcuu1" capability if you are using terminfo
+ instead of termcap).
+ By default, _Alpine_ defines some terminal escape sequences that are
+ commonly used. For example, the sequence "ESC O A" is recognized as
+ an Up Arrow key. The sequence "ESC [ A" is also recognized as an Up
+ Arrow key. These are chosen because common terminals like VT100's or
+ ANSI standard terminals produce these sequences when you press the Up
+ Arrow key.
+ If your system's termcap (terminfo) database assigns some other
+ function to the sequence "ESC O A" it is usually ignored by _Alpine_.
+ Also, if your termcap (terminfo) database assigns a sequence which
+ doesn't begin with an escape character (ESC) it is usually ignored by
+ _Alpine_. This usually works fine because most terminals emit the
+ escape sequences that _Alpine_ has defined by default. We have also
+ found that it is usually better to have these defaults take
+ precedence over the definitions contained in the database because the
+ defaults are more likely to be correct than the database.
+ There are some terminals where this breaks down. If you want _Alpine_
+ to believe the definitions given in your termcap (terminfo) database
+ in preference to the defaults the _Alpine_ itself sets up, then you
+ may turn this feature on. Then, sequences of characters which are
+ defined in both termcap (terminfo) and in _Alpine_'s set of defaults
+ will be interpreted the way that termcap (terminfo) says they should
+ be interpreted. Also, if your terminal capabilities database assigns
+ a sequence which doesn't begin with escape, it will not be ignored.
+ _thread-index-shows-important-color_
+ This option affects only the THREAD INDEX screen. Whether or not you
+ ever see a THREAD INDEX screen depends on the setting of the
+ configuration option threading-index-style and on the sort order of
+ the index. If a message within a thread is flagged as Important and
+ this option is set, then the entire line in the THREAD INDEX will be
+ colored the color of the Index-important Symbol, which can be set
+ using the Setup Kolor screen.
+ _try-alternative-authentication-driver-first_
+ This feature affects how _Alpine_ connects to IMAP servers. It's
+ utility has largely been overtaken by events, but it may still be
+ useful in some circumstances. If you only connect to modern IMAP
+ servers that support "TLS" you can ignore this feature.
+ Details:
+ By default, _Alpine_ will attempt to connect to an IMAP server on the
+ normal IMAP service port (143), and if the server offers "Transport
+ Layer Security" (TLS) and _Alpine_ has been compiled with encryption
+ capability, then a secure (encrypted) session will be negotiated.
+ With this feature enabled, before connecting on the normal IMAP port,
+ _Alpine_ will first attempt to connect to an alternate IMAP service
+ port (993) used specifically for encrypted IMAP sessions via the
+ Secure Sockets Layer (SSL) method. If the SSL attempt fails, _Alpine_
+ will then try the default behavior described in the previous
+ paragraph.
+ TLS negotiation on the normal port is preferred, and supersedes the
+ use of SSL on port 993, but older servers may not provide TLS
+ support. This feature may be convenient when accessing IMAP servers
+ that do not support TLS, but do support SSL connections on port 993.
+ However, it is important to understand that with this feature
+ enabled, _Alpine_ will _attempt_ to make a secure connection if that
+ is possible, but it will proceed to make an insecure connection if
+ that is the only option offered by the server, or if the _Alpine_ in
+ question has been built without encryption capability.
+ Note that this feature specifies a per-user (or system-wide) default
+ behavior, but host/folder specification flags may be used to control
+ the behavior of any specific connection. This feature interacts with
+ some of the possible host/folder path specification flags as follows:
+ The /tls host flag, for example,
+
+ {foo.example.com/tls}INBOX
+ will over-ride this feature for the specified host by bypassing the
+ SSL connection attempt. Moreover, with /tls specified, the connection
+ attempt will fail if the service on port 143 does not offer TLS
+ support.
+ The /ssl host flag, for example,
+
+ {foo.example.com/ssl}INBOX
+ will insist on an SSL connection for the specified host, and will
+ fail if the SSL service on port 993 is not available. _Alpine_ will
+ not subsequently retry a connection on port 143 if /ssl is specified.
+ _unselect-will-not-advance_
+ Normally, when the Unselect current message command (:) is typed when
+ the current message is selected, the message will be unselected and
+ the next message will become the current message. If this feature is
+ set, the cursor will not advance to the next message. Instead, the
+ current message will remain the current message after unselecting.
+ _use-current-dir_
+ This feature controls an aspect of several commands. If set, your
+ "current working directory" will be used instead of your home
+ directory for all of the following operations:
+ + _Export_ in the "Folder Index" and "Message Text" screens
+ + Attachment _Save_ in the "Message Text" and "Attachment Text"
+ screens
+ + _^R_ file inclusion in the Composer
+ + _^J_ file attachment in the Composer
+ This feature is displayed as "Use Current Directory".
+ _use-function-keys_
+ This feature specifies that _Alpine_ will respond to function keys
+ instead of the normal single-letter commands. In this mode, the key
+ menus at the bottom of each screen will show function key
+ designations instead of the normal mnemonic key.
+ _use-regular-startup-rule-for-stayopen-folders_
+ This feature affects which message is selected as the current message
+ when you enter a Stay Open folder.
+ Normally, the starting position for an incoming folder (which most
+ Stay Open folders will likely be) is controlled by the
+ Incoming-Startup-Rule. However, if a folder is a Stay Open folder,
+ when you re-enter the folder after the first time the current message
+ will be the same as it was when you left the folder. An exception is
+ made if you use the TAB command to get to the folder. In that case,
+ the message number will be incremented by one from what it was when
+ you left the folder.
+ The above special behavior is thought to be useful. However, it is
+ special and different from what you might at first expect. If this
+ feature is set, then Stay Open folders will not be treated specially
+ as far as the startup rule is concerned.
+ _use-resent-to-in-rules_
+ This feature is turned off by default because turning it on causes
+ problems with some deficient IMAP servers. In _Alpine_ Filters and
+ other types of Rules, if the Pattern contains a To header pattern and
+ this feature is turned on, then a check is made in the message to see
+ if a Resent-To header is present, and that is used instead of the To
+ header. If this feature is not turned on, then the regular To header
+ will always be used.
+ _use-sender-not-x-sender_
+ Normally _Alpine_ on Unix adds a header line labeled _X-X-Sender_, if
+ the sender is different from the _From:_ line.
+ The standard specifies that this header line should be labeled
+ _Sender_, not _X-X-Sender_. Setting this feature causes _Sender_ to be
+ used instead of _X-X-Sender_. The standard also states that the data
+ associated with this header field should not be used as a Reply
+ address. Unfortunately, certain implementations of mail list
+ management servers will use the Sender address for such purposes.
+ These implementations often even recognize the _X-Sender_ fields as
+ being equivalent to the _Sender_ field, and use it if present. This
+ is why _Alpine_ defaults to _X-X-Sender_.
+ Note, _PC-Alpine_ always adds either an _X-X-Sender_ line if there is
+ an open, remote mailbox, or an _X-Warning: UNAuthenticated User_
+ otherwise
+ This feature is displayed as "Use Sender Instead of X-X-Sender".
+ _use-subshell-for-suspend_
+ This feature affects _Alpine_'s behavior when process suspension is
+ enabled and then activated via the _^Z_ key. _Alpine_ suspension
+ allows one to temporarily interact with the operating system command
+ "shell" without quitting _Alpine_, and then subsequently resume the
+ still-active _Alpine_ session.
+ When the _enable-suspend_ feature is set and subsequently the _^Z_
+ key is pressed, _Alpine_ will normally suspend itself and return
+ temporary control to _Alpine_'s parent shell process. However, if
+ this feature is set, _Alpine_ will instead create an inferior
+ subshell process. This is useful when the parent process is not
+ intended to be used interactively. Examples include invoking _Alpine_
+ via the -e argument of the Unix _xterm_ program, or via a menu
+ system.
+ Note that one typically resumes a suspended _Alpine_ by entering the
+ Unix _fg_ command, but if this feature is set, it will be necessary
+ to enter the _exit_ command instead.
+ _use-system-translation_
+ UNIX _Alpine_ only. _Alpine_ normally uses its own internal software
+ to convert between the multi-byte representation of characters and
+ the Unicode representation of those same characters ( see the section
+ on International Character Sets). It converts from the multi-byte
+ characters your keyboard produces to Unicode, and from Unicode to the
+ multi-byte characters your display expects. Alpine also uses its own
+ internal software to decide how much space on the screen a particular
+ Unicode character will occupy.
+ Setting this feature tells _Alpine_ to use the system-supplied
+ routines to perform these tasks instead. In particular there are
+ three tasks and three system routines that will be used for these
+ tasks.
+ To convert from multi-byte to Unicode the routine
+
+ mbstowcs
+ is used. To convert from Unicode to multi-byte the routine
+
+ wcrtomb
+ is used. And to find the screen width a particular Unicode character
+ will occupy the routine used is
+
+ wcwidth
+ This feature has been only lightly tested. The internal routines
+ should normally be used unless you run into a problem that you think
+ may be solved by using the system routines. Note that your
+ environment needs to be set up for these routines to work correctly.
+ In particular, the LANG or LC_CTYPE variable in your environment will
+ need to be set.
+ _vertical-folder-list_
+ This feature controls an aspect of _Alpine_'s FOLDER LIST screen. If
+ set, the folders will be listed alphabetically down the columns
+ rather than across the columns as is the default.
+ This feature is displayed as "Use Vertical Folder List".
+ _warn-if-blank-subject_
+ This feature affects _Alpine_'s behavior when you send a message
+ being composed. If this option is set, _Alpine_ will check to see if
+ the message about to be sent has a subject or not. If not, you will
+ be asked if you want to send the message anyway.
+ _warn-if-blank-to-and-cc-and-newsgroups_
+ This feature affects _Alpine_'s behavior when you send a message
+ being composed. If this option is set, _Alpine_ will check to see if
+ the message about to be sent has either a To address, a Cc address,
+ or a Newsgroup. If none of these is set, you will be asked if you
+ want to send the message anyway.
+ This feature is closely related to fcc-only-without-confirm. _Alpine_
+ will normally ask if you want to copy a message only to the Fcc. This
+ feature also applies to cases where there is a Bcc but still no To,
+ Cc, or Newsgroup. If the Fcc-Only-Without-Confirm feature is set and
+ you are sending a message with only an Fcc, then you won't be asked
+ about sending with a blank To and Cc and Newsgroups header even if
+ this feature is set. Similarly, if you have already been asked if you
+ want to send to the Fcc only and you have answered Yes, then you
+ won't be asked again about sending with blank To, Cc, and Newsgroups
+ headers even if this feature is set.
+
+Hidden Config Variables and Features
+
+ There are several configuration variables and features which are normally
+ hidden from the user. That is, they don't appear on any of the configuration
+ screens. Some of these are suppressed because they are intended to be used
+ by system administrators, and in fact may only be set in system-wide
+ configuration files. Others are available to users but are thought to be of
+ such little value to most users that their presence on the Config screens
+ would cause more confusion than help. Others are hidden in the Setup/Config
+ screen because they are normally configured in one of the other
+ configuration screens. For example, all of the colors are hidden because the
+ normal way to configure colors is through Setup/Colors not Setup/Config. You
+ may set the feature expose-hidden-config to cause most of these hidden
+ variables and features to show up at the bottom of the Setup/Config screen.
+
+ Hidden Variables Not Settable by Users
+
+ These variables are settable only in system-wide configuration files.
+ * bugs-additional-data
+ * bugs-address
+ * bugs-fullname
+ * forced-abook-entry
+ * kblock-passwd-count
+ * local-address
+ * local-fullname
+ * mail-directory
+ * standard-printer
+ * suggest-address
+ * suggest-fullname
+
+ Hidden Variables Which are Settable by Users
+
+ These variables are not shown to users but are settable by means of hand
+ editing the personal configuration file. This first group is usually
+ maintained by _Alpine_ and there will usually be no reason to edit them by
+ hand.
+ * last-version-used
+ * patterns-filters2
+ * patterns-indexcolors
+ * patterns-roles
+ * patterns-scores2
+ * remote-abook-metafile
+
+ This group is usually correct but may be changed by system managers or users
+ in special cases.
+ * disable-these-authenticators
+ * disable-these-drivers
+ * last-time-prune-questioned
+ * new-version-threshold
+ * remote-abook-history
+ * remote-abook-validity
+ * rsh-command
+ * rsh-open-timeout
+ * rsh-path
+ * sendmail-path
+ * ssh-command
+ * ssh-open-timeout
+ * ssh-path
+ * tcp-open-timeout
+ * tcp-query-timeout
+ * tcp-read-warning-timeout
+ * tcp-write-warning-timeout
+ * use-function-keys
+
+ System managers are usually interested in setting these in the system-wide
+ configuration files, though users may set them if they wish.
+ * operating-dir
+ * user-input-timeout
+
+ Hidden Features Which are Settable by Users
+
+ These are _features_ (as opposed to variables) which users or system
+ administrators may set. Some of them only make sense for administrators. To
+ turn these on manually, the configuration file should be edited and the
+ feature added to the _feature-list_ variable. You may set the feature
+ expose-hidden-config to cause these hidden features to show up in the
+ Setup/Config screen. They will be at the bottom of the screen.
+ * disable-config-cmd
+ * disable-keyboard-lock-cmd
+ * disable-password-cmd
+ * disable-pipes-in-sigs
+ * disable-pipes-in-templates
+ * disable-roles-setup-cmd
+ * disable-roles-sig-edit
+ * disable-roles-template-edit
+ * disable-setlocale-collate
+ * disable-shared-namespaces
+ * disable-signature-edit-cmd
+
+Retired Variables and Features
+
+ Variables and features that are no longer used by the current _Alpine_
+ version. When an obsolete variable is encountered, its value is applied to
+ any new corresponding setting. The replaced values include:
+
+ _character-set_
+ Replaced by three separate variables: _display-character-set_,
+ _keyboard-character-set_, and _posting-character-set_.
+ _compose-mime_
+ _elm-style-save_
+ Replaced by _saved-msg-name-rule_
+ _feature-level_
+ Replaced by _feature-list._
+ _header-in-reply_
+ Replaced by _include-header-in-reply_ in the _feature-list._
+ _old-style-reply_
+ Replaced by _signature-at-bottom_ in the _feature-list._
+ _use-old-unix-format-write_
+ No replacement.
+ _patterns_
+ Replaced by four separate patterns variables: _patterns-roles_,
+ _patterns-filters_, _patterns-scores_, and _patterns-indexcolors_.
+ Since then, _patterns-filters_ has also become obsolete and is
+ replaced by _patterns-filters2_; _patterns-scores_ is replaced by
+ _patterns-scores2_.
+ _save-by-sender_
+ Replaced by _saved-msg-name-rule._
+ _show-all-characters_
+ No replacement, it always works this way now.
+
+Tokens for Index and Replying
+
+ This set of special tokens may be used in the index-format option, in the
+ reply-leadin option, in signature files, in template files used in roles,
+ and in the folder name that is the target of a Filter Rule. Some of them
+ aren't available in all situations.
+
+ The tokens are used as they appear below for the _Index-Format_ option, but
+ they must be surrounded by underscores for the _Reply-Leadin_ option, in
+ signature and template files, and in the target of Filter Rules.
+
+ _Tokens Available for all Cases (except Filter Rules)_
+
+ SUBJECT
+ This token represents the Subject the sender gave the message.
+ Alternatives for use in the index screen are SUBJKEY, SUBJKEYINIT,
+ SUBJECTTEXT, SUBJKEYTEXT, and SUBJKEYINITTEXT. You may color the
+ subject text in the MESSAGE INDEX screen differently by using the
+ Index Subject Color and the Index Opening Color. options available
+ from the Setup Kolor screen.
+
+ FROM
+ This token represents the personal name (or email address if the name
+ is unavailable) of the person specified in the message's "From:"
+ header field. You may color the from text in the MESSAGE INDEX screen
+ differently by using the Index From Color option available from the
+ Setup Kolor screen.
+
+ ADDRESS
+ This is similar to the "FROM" token, only it is always the email
+ address, never the personal name. For example, "mailbox@domain".
+
+ MAILBOX
+ This is the same as the "ADDRESS" except that the domain part of the
+ address is left off. For example, "mailbox".
+
+ SENDER
+ This token represents the personal name (or email address) of the
+ person listed in the message's "Sender:" header field.
+
+ TO
+ This token represents the personal names (or email addresses if the
+ names are unavailable) of the persons specified in the message's
+ "To:" header field.
+
+ NEWSANDTO
+ This token represents the newsgroups from the message's "Newsgroups:"
+ header field _and_ the personal names (or email addresses if the
+ names are unavailable) of the persons specified in the message's
+ "To:" header field.
+
+ TOANDNEWS
+ Same as "NEWSANDTO" except in the opposite order.
+
+ NEWS
+ This token represents the newsgroups from the message's "Newsgroups:"
+ header field.
+
+ CC
+ This token represents the personal names (or email addresses if the
+ names are unavailable) of the persons specified in the message's
+ "Cc:" header field.
+
+ RECIPS
+ This token represents the personal names (or email addresses if the
+ names are unavailable) of the persons specified in both the message's
+ "To:" header field and the message's "Cc:" header field.
+
+ NEWSANDRECIPS
+ This token represents the newsgroups from the message's "Newsgroups:"
+ header field _and_ the personal names (or email addresses if the
+ names are unavailable) of the persons specified in the message's
+ "To:" and "Cc:" header fields.
+
+ RECIPSANDNEWS
+ Same as "NEWSANDRECIPS" except in the opposite order.
+
+ INIT
+ This token represents the initials from the personal name of the
+ person specified in the message's "From:" header field. If there is
+ no personal name, it is blank.
+
+ DATE
+ This token represents the date on which the message was sent,
+ according to the "Date" header field. It has the format MMM DD. For
+ example, "Oct 23". The feature convert-dates-to-localtime, which
+ adjusts for the timezone the message was sent from, may have an
+ affect on the value of this token as well as the values of all of the
+ other DATE or TIME tokens. Some of the DATE and TIME tokens are
+ displayed in a locale-specific way unless the option
+ Disable-Index-Locale-Dates is set.
+
+ SMARTDATE
+ This token represents the date on which the message was sent,
+ according to the "Date" header field. It is "Today" if the message
+ was sent today, "Yesterday" for yesterday, "Wednesday" if it was last
+ Wednesday, and so on. If the message is from last year and is more
+ than six months old it includes the year, as well. There is no
+ adjustment made for different time zones, so you'll get the day the
+ message was sent according to the time zone the sender was in. See
+ the SMARTDATE alternatives below, as well.
+
+ SMARTTIME
+ This token represents the most relevant elements of the date on which
+ the message was sent (according to the "Date" header field), in a
+ compact form. If the message was sent today, only the time is used
+ (e.g. "9:22am", "10:07pm"); if it was sent during the past week, the
+ day of the week and the hour are used (e.g. "Wed09am", "Thu10pm");
+ other dates are given as date, month, and year (e.g. "23Aug00",
+ "9Apr98"). There is no adjustment made for different time zones, so
+ you'll get the day/time the message was sent according to the time
+ zone the sender was in.
+
+ SMARTDATETIME
+ This is a combination of SMARTDATE and SMARTTIME. It is SMARTDATE
+ unless the SMARTDATE value is "Today", in which case it is SMARTTIME.
+ See the SMARTDATETIME alternatives below, as well.
+
+ DATEISO
+ This token represents the date on which the message was sent,
+ according to the "Date" header field. It has the format YYYY-MM-DD.
+ For example, "1998-10-23".
+
+ SHORTDATEISO
+ This token represents the date on which the message was sent,
+ according to the "Date" header field. It has the format YY-MM-DD. For
+ example, "98-10-23".
+
+ SHORTDATE1
+ This token represents the date on which the message was sent,
+ according to the "Date" header field. It has the format MM/DD/YY. For
+ example, "10/23/98".
+
+ SHORTDATE2
+ This token represents the date on which the message was sent,
+ according to the "Date" header field. It has the format DD/MM/YY. For
+ example, "23/10/98".
+
+ SHORTDATE3
+ This token represents the date on which the message was sent,
+ according to the "Date" header field. It has the format DD.MM.YY. For
+ example, "23.10.98".
+
+ SHORTDATE4
+ This token represents the date on which the message was sent,
+ according to the "Date" header field. It has the format YY.MM.DD. For
+ example, "98.10.23".
+
+ LONGDATE
+ This token represents the date on which the message was sent,
+ according to the "Date" header field. It has the format MMM DD, YYYY.
+ For example, "Oct 23, 1998".
+
+ SMARTDATE alternatives
+ There are several versions of SMARTDATE which are all the same except
+ for the way they format dates far in the past. SMARTDATE formats the
+ date using the information from your locale settings to format the
+ date string. It may end up formatting dates so that they look like
+ DATEISO tokens, or SHORTDATE2 tokens, or something else entirely. The
+ feature convert-dates-to-localtime may have an affect on the values
+ of these tokens. If you want more control you may use one of the
+ following.
+
+ SMARTDATE
+ If the option Disable-Index-Locale-Dates is not set then this
+ will be locale specific. Control this with the LC_TIME locale
+ setting on a UNIX system. On Windows the Regional Options
+ control panel may be used to set the Short date format. At the
+ programming level, the strftime routine is what _Alpine_ uses
+ to print the date. If the Disable-Index-Locale-Dates option is
+ set then this is equivalent to SMARTDATES1.
+
+ SMARTDATEISO
+ DATEISO format. See text above.
+
+ SMARTDATESHORTISO
+ SHORTDATEISO format.
+
+ SMARTDATES1
+ SHORTDATE1 format.
+
+ SMARTDATES2
+ SHORTDATE2 format.
+
+ SMARTDATES3
+ SHORTDATE3 format.
+
+ SMARTDATES4
+ SHORTDATE4 format.
+
+ SMARTDATETIME alternatives
+ There are several versions of SMARTDATETIME which are all very
+ similar. The ones which end in 24 use a 24-hour clock for Today's
+ messages instead of a 12-hour clock. The other variation is for the
+ way they format dates far in the past. SMARTDATETIME and
+ SMARTDATETIME24 format the date using the information from your
+ locale settings to format the date string. It may end up formatting
+ dates so that they look like DATEISO tokens, or SHORTDATE2 tokens, or
+ something else entirely. The feature convert-dates-to-localtime may
+ have an affect on the values of these tokens. The possible choices
+ are:
+
+ SMARTDATETIME
+ Locale specific. Control this with the LC_TIME locale setting
+ on a UNIX system. On Windows the Regional Options control panel
+ may be used to set the Short date format. At the programming
+ level, the strftime routine is what _Alpine_ uses to print the
+ date.
+
+ SMARTDATETIME
+ If the option Disable-Index-Locale-Dates is not set then this
+ will be locale specific. Control this with the LC_TIME locale
+ setting on a UNIX system. On Windows the Regional Options
+ control panel may be used to set the Short date format. At the
+ programming level, the strftime routine is what _Alpine_ uses
+ to print the date. If the Disable-Index-Locale-Dates option is
+ set then this is equivalent to SMARTDATETIMES1.
+
+ SMARTDATETIME24
+ Use TIME24 for Today
+
+ SMARTDATETIMEISO
+ DATEISO format. See text above.
+
+ SMARTDATETIMEISO24
+ Use TIME24 for Today
+
+ SMARTDATETIMESHORTISO
+ SHORTDATEISO format.
+
+ SMARTDATETIMESHORTISO24
+ Use TIME24 for Today
+
+ SMARTDATETIMES1
+ SHORTDATE1 format.
+
+ SMARTDATETIMES124
+ Use TIME24 for Today
+
+ SMARTDATETIMES2
+ SHORTDATE2 format.
+
+ SMARTDATETIMES224
+ Use TIME24 for Today
+
+ SMARTDATETIMES3
+ SHORTDATE3 format.
+
+ SMARTDATETIMES324
+ Use TIME24 for Today
+
+ SMARTDATETIMES4
+ SHORTDATE4 format.
+
+ SMARTDATETIMES424
+ Use TIME24 for Today
+
+ DAYDATE
+ This token represents the date on which the message was sent,
+ according to the "Date" header field. It looks like "Sat, 23 Oct
+ 1998". This token is never converted in any locale-specific way.
+
+ PREFDATE
+ This token represents the date on which the message was sent,
+ according to the "Date" header field. It is your operating system's
+ idea of the preferred date representation for the current locale.
+ Internally it uses the %x version of the date from the strftime
+ routine.
+
+ PREFTIME
+ This token represents the time at which the message was sent,
+ according to the "Date" header field. It is the preferred time
+ representation for the current locale. Internally it uses the %X
+ version of the time from the strftime routine.
+
+ PREFDATETIME
+ This token represents the date and time at which the message was
+ sent, according to the "Date" header field. It is the preferred date
+ and time representation for the current locale. Internally it uses
+ the %c version of the time from the strftime routine.
+
+ DAY
+ This token represents the day of the month on which the message was
+ sent, according to the "Date" header field. For example, "23" or "9".
+
+ DAY2DIGIT
+ This token represents the day of the month on which the message was
+ sent, according to the "Date" header field. For example, "23" or
+ "09". It is always 2 digits.
+
+ DAYORDINAL
+ This token represents the ordinal number which is the day of the
+ month on which the message was sent, according to the "Date" header
+ field. For example, "23rd" or "9th".
+
+ DAYOFWEEK
+ This token represents the day of the week on which the message was
+ sent, according to the "Date" header field. For example, "Sunday" or
+ "Wednesday".
+
+ DAYOFWEEKABBREV
+ This token represents the day of the week on which the message was
+ sent, according to the "Date" header field. For example, "Sun" or
+ "Wed".
+
+ MONTHABBREV
+ This token represents the month the message was sent, according to
+ the "Date" header field. For example, "Oct".
+
+ MONTHLONG
+ This token represents the month in which the message was sent,
+ according to the "Date" header field. For example, "October".
+
+ MONTH
+ This token represents the month in which the message was sent,
+ according to the "Date" header field. For example, "10" or "9".
+
+ MONTH2DIGIT
+ This token represents the month in which the message was sent,
+ according to the "Date" header field. For example, "10" or "09". It
+ is always 2 digits.
+
+ YEAR
+ This token represents the year the message was sent, according to the
+ "Date" header field. For example, "1998" or "2001".
+
+ YEAR2DIGIT
+ This token represents the year the message was sent, according to the
+ "Date" header field. For example, "98" or "01". It is always 2
+ digits.
+
+ TIME24
+ This token represents the time at which the message was sent,
+ according to the "Date" header field. There is no adjustment made for
+ different time zones, so you'll get the time the message was sent
+ according to the time zone the sender was in. It has the format
+ HH:MM. For example, "17:28".
+
+ TIME12
+ This token represents the time at which the message was sent,
+ according to the "Date" header field. This time is for a 12 hour
+ clock. It has the format HH:MMpm. For example, "5:28pm" or "11:13am".
+
+ TIMEZONE
+ This token represents the numeric timezone from the "Date" header
+ field. It has the format [+-]HHMM. For example, "-0800".
+
+ _Tokens Available Only for Index-Format_
+
+ MSGNO
+ This token represents the message's current position in the folder
+ which, of course, may change as the folder is sorted or new mail
+ arrives.
+
+ STATUS
+ This token represents a three character wide field displaying various
+ aspects of the message's state. The first character is either blank,
+ a '*' for message marked Important, or a '+' indicating a message
+ addressed directly to you (as opposed to your having received it via
+ a mailing list, for example). When the feature mark-for-cc is set, if
+ the first character would have been blank then it will instead be a
+ '-' if the message is cc'd to you. The second character is typically
+ blank, though the arrow cursor may occupy it if either the
+ assume-slow-link or the force-arrow-cursor feature is set (or you
+ actually are on a slow link). The third character is either D
+ (Deleted), A (Answered), N (New), or blank.
+
+ If you are using a threaded view of the index and this message is at
+ the top of a collapsed portion of a thread, then this token refers to
+ all of the messages in the collapsed portion of the thread instead of
+ just the top message. The first character will be a '*' if _any_ of
+ the messages in the thread are marked Important, else a '+' if any of
+ the messages are addressed to you, else a '-' if any of the messages
+ are cc'd to you. The third character will be a 'D' if _all_ of the
+ messages in the collapsed thread are marked deleted, an 'A' if _all_
+ of the messages in the collapsed thread are marked answered, it will
+ be an 'N' if any of the messages are undeleted and unseen, and it
+ will be blank otherwise.
+
+ FULLSTATUS
+ This token represents a less abbreviated alternative to the "STATUS"
+ token. It is six characters wide. The first character is '+', '-', or
+ blank, the second blank, the third either '*' or blank, the fourth N
+ or blank, the fifth A or blank, and the sixth character is either D
+ or blank.
+
+ If you are using a threaded view of the index and this message is at
+ the top of a collapsed portion of a thread, then this token refers to
+ all of the messages in the collapsed portion of the thread instead of
+ just the top message. The first character is '+', '-', or blank
+ depending on whether _any_ of the messages in the collapsed thread
+ are addressed to you or cc'd to you. The third character will be '*'
+ if any of the messages are marked Important. The fourth character
+ will be 'N' if all of the messages in the thread are New, else 'n' if
+ some of the messages in the thread are New, else blank. The fifth
+ character will be 'A' or 'a' or blank, and the sixth character will
+ be 'D' or 'd' or blank.
+
+ IMAPSTATUS
+ This token represents an even less abbreviated alternative to the
+ "STATUS" token. It differs from "FULLSTATUS" in only the fourth
+ character which is an 'N' if the message is new to this folder since
+ the last time it was opened _and_ it has not been viewed, an 'R'
+ (Recent) if the message is new to the folder and has been viewed, a
+ 'U' (Unseen) if the message is not new to the folder since it was
+ last opened _but_ has not been viewed, or a blank if the message has
+ been in the folder since it was last opened and has been viewed.
+
+ If you are using a threaded view of the index and this message is at
+ the top of a collapsed portion of a thread, then the fourth character
+ will be 'N' if all of the messages in the thread are unseen and
+ recent; else 'n' if some of the messages in the thread are unseen and
+ recent; else 'U' if all of the messages in the thread are unseen and
+ not recent; else 'u' if some of the messages in the thread are unseen
+ and not recent; else 'R' if all of the messages in the thread are
+ seen and recent; else 'r' if some of the messages in the thread are
+ seen and recent; else blank.
+
+ SHORTIMAPSTATUS
+ This is the same as the last four of the six characters of
+ IMAPSTATUS, so the '+' To Me information will be missing.
+
+ SIZE
+ This token represents the total size, in bytes, of the message. If a
+ "K" (Kilobyte) follows the number, the size is approximately 1,000
+ times that many bytes (rounded to the nearest 1,000). If an "M"
+ (Megabyte) follows the number, the size is approximately 1,000,000
+ times that many bytes. Commas are not used in this field. This field
+ is seven characters wide, including the enclosing parentheses. Sizes
+ are rounded when "K" or "M" is present. The progression of sizes used
+ looks like:
+
+ 0 1 ... 9999 10K ... 999K 1.0M ... 99.9M 100M ... 2000M
+
+ SIZECOMMA
+ This token represents the total size, in bytes, of the message. If a
+ "K" (Kilobyte) follows the number, the size is approximately 1,000
+ times that many bytes (rounded to the nearest 1,000). If an "M"
+ (Megabyte) follows the number, the size is approximately 1,000,000
+ times that many bytes. Commas are used if the number shown is 1,000
+ or greater. The SIZECOMMA field is one character wider than the SIZE
+ field. Sizes are rounded when "K" or "M" is present. The progression
+ of sizes used looks like:
+
+ 0 1 ... 99,999 100K ... 9,999K 10.0M ... 999.9M 1,000M ... 2,000M
+
+ KSIZE
+ This token represents the total size of the message, expressed in
+ kilobytes or megabytes, as most appropriate. These are 1,024 byte
+ kilobytes and 1,024 x 1,024 byte megabytes. The progression of sizes
+ used looks like:
+
+ 0K 1K ... 1023K 1.0M ... 99.9M 100M ... 2047M
+
+ SIZENARROW
+ This token represents the total size, in bytes, of the message. If a
+ "K" (Kilobyte) follows the number, the size is approximately 1,000
+ times that many bytes. If an "M" (Megabyte) follows the number, the
+ size is approximately 1,000,000 times that many bytes. If a "G"
+ (Gigabyte) follows the number, the size is approximately
+ 1,000,000,000 times that many bytes. This field uses only five
+ characters of screen width, including the enclosing parentheses. The
+ progression of sizes used looks like:
+
+ 0 1 ... 999 1K ... 99K .1M ... .9M 1M ... 99M .1G ... .9G 1G 2G
+
+ DESCRIPSIZE
+ This token is intended to represent a more useful description of the
+ message than just its size, but it isn't very useful at this point.
+ The plus sign in this view means there are attachments. Note that
+ including this token in the "Index-Format" could slow down the
+ display a little while _Alpine_ collects the necessary information.
+
+ SUBJKEY
+ This token is the same as the SUBJECT token unless keywords are set
+ for the message. In that case, a list of keywords enclosed in braces
+ will be prepended to the subject of the message. Only those keywords
+ that you have defined in your Keywords option in Setup/Config are
+ considered in the list. In other words, keywords that have been set
+ by some other means, perhaps by another email program, won't show up
+ unless included in Keywords. Having this set in the Index-Format will
+ also cause the keywords to be prepended to the subject in the MESSAGE
+ TEXT screen. If you have given a keyword a nickname (keywords), that
+ nickname is displayed instead of the actual keyword. The
+ keyword-surrounding-chars option may be used to modify this token
+ slightly. It is also possible to color keywords in the index using
+ the Setup/Kolor screen.
+
+ SUBJKEYINIT
+ This token is the same as the SUBJKEY token except that instead of
+ prepending a list of keywords to the subject, a list of first
+ initials of keywords will be prepended instead. For example, if a
+ message has the keywords _Work_ and _Now_ set (or Work and Now are
+ the _Alpine_ nicknames of keywords which are set) then the SUBJKEY
+ token would cause a result like
+
+ {Work Now} actual subject
+
+ whereas the SUBJKEYINIT token would give
+
+ {WN} actual subject
+
+ Only those keywords that you have defined in your Keywords option in
+ Setup/Config are considered in the list. In other words, keywords
+ that have been set by some other means, perhaps by another email
+ program, won't show up unless included in Keywords. The
+ keyword-surrounding-chars option may be used to modify this token
+ slightly. It is also possible to color keywords in the index using
+ the Setup/Kolor screen.
+
+ SUBJECTTEXT
+ Same as SUBJECT but if there is room in the Subject field for more
+ text, the opening part of the text of the message is displayed after
+ the subject. The time needed to fetch the text may cause a
+ performance problem which can, of course, be avoided by using the
+ SUBJECT version of the Subject instead. You may color this opening
+ text differently by using the Index Opening Color option available
+ from the Setup Kolor screen. You may adjust the characters that are
+ displayed between the Subject and the opening text with the option
+ Opening-Text-Separator-Chars.
+
+ SUBJKEYTEXT
+ Same as SUBJKEY but with the opening message text.
+
+ SUBJKEYINITTEXT
+ Same as SUBJKEYINIT but with the opening message text.
+
+ OPENINGTEXT
+ This is similar to SUBJECTTEXT. Instead of combining the Subject and
+ the opening text in a single field in the index screen this token
+ allows you to allocate a separate column just for the opening text of
+ the message. The time needed to fetch this text may cause a
+ performance problem. You may color this opening text differently by
+ using the Index Opening Color option available from the Setup Kolor
+ screen.
+
+ OPENINGTEXTNQ
+ This is very similar to OPENINGTEXT. The NQ stands for No Quotes. The
+ only difference is that quoted text (lines beginning with >) is
+ deleted. For some messages this may be confusing. For example, a
+ message might have a line preceding some quoted text that reads
+ something like "On May 8th person A said." That no longer makes sense
+ after the quoted text is deleted and it will appear that person A
+ said whatever the text after the quote is, even though that is really
+ person B talking.
+
+ KEY
+ This is a space-delimited list of keywords that are set for the
+ message. Only those keywords that you have defined in your Keywords
+ option in Setup/Config are considered in the list. In other words,
+ keywords that have been set by some other means, perhaps by another
+ email program, won't show up unless included in Keywords. If you have
+ given a keyword a nickname that nickname is displayed instead of the
+ actual keyword. It is also possible to color keywords in the index
+ using the Setup/Kolor screen. This token defaults to an arbitrary
+ width of 5. You should set it to whatever width suits you using
+ something like KEY(17) in the Index-Format.
+
+ KEYINIT
+ This is a list of keyword initials that are set for the message. If
+ you have given a keyword a nickname the initial of that nickname is
+ displayed instead of the initial of the actual keyword. It is also
+ possible to color keyword initials in the index using the Setup/Kolor
+ screen. This token defaults to an arbitrary width of 2. You should
+ set it to whatever width suits you using something like KEYINIT(3) in
+ the Index-Format.
+
+ PRIORITY
+ The X-Priority header is a non-standard header that is used in a
+ somewhat standard way by many mail programs. _Alpine_ expects the
+ value of this header to be a digit with a value from 1 to 5, with 1
+ being the highest priority and 5 the lowest priority. Since this
+ priority is something that the sender sets it is only an indication
+ of the priority that the sender attaches to the mail and it is
+ therefore almost totally unreliable for use as a filtering criterion.
+ This token will display the numeric value of the priority if it is
+ between 1 and 5. It will be suppressed (blank) if the value is 3,
+ which is normal priority. It is also possible to set the color of the
+ PRIORITY field. By default the token is colored the same as the index
+ line it is part of. You may set it to be another color with the Index
+ Priority Colors options available from the Setup Kolor screen.
+
+ PRIORITYALPHA
+ This is a more verbose interpretation of the X-Priority field. Once
+ again nothing is displayed unless the value of the field is 1, 2, 4,
+ or 5. The values displayed for those values are:
+
+ 1 Highest
+ 2 High
+ 4 Low
+ 5 Lowest
+
+ You may color this token with the Index Priority Colors options.
+
+ PRIORITY!
+ This is a one character, non-numeric version of the X-Priority field.
+ If the value of the X-Priority header is 1 or 2 an exclamation point
+ is displayed. If the value is 4 or 5 a "v" (think down arrow) is
+ displayed. You may color this token with the Index Priority Colors
+ options.
+
+ ATT
+ This is a one column wide field which represents the number of
+ attachments a message has. It will be blank if there are no
+ attachments, a single digit for one to nine attachments, or an
+ asterisk for more than nine. Note that including this token in the
+ "Index-Format" could slow down the display a little while _Alpine_
+ collects the necessary information.
+
+ FROMORTO
+ This token represents _either_ the personal name (or email address)
+ of the person listed in the message's "From:" header field, _or_, if
+ that address is yours or one of your alternate addresses, the first
+ person specified in the message's "To:" header field with the prefix
+ "To: " prepended. If the from address is yours and there is also no
+ "To" address, _Alpine_ will use the address on the "Cc" line. If
+ there is no address there, either, _Alpine_ will look for a newsgroup
+ name from the "Newsgroups" header field and put that after the "To: "
+ prefix.
+
+ FROMORTONOTNEWS
+ This is almost the same as _FROMORTO_. The difference is that
+ newsgroups aren't considered. When a message is from you, doesn't
+ have a To or Cc, and does have a Newsgroups header; this token will
+ be your name instead of the name of the newsgroup (like it would be
+ with FROMORTO).
+
+ TEXT
+ This is a different sort of token. It allows you to display a label
+ within each index line. It will be the same fixed text for each line.
+ It is different from all the other tokens in that there is no space
+ column displayed after this token. Instead, it is butted up against
+ the following field. It also has a different syntax. The text to
+ display is given following a colon after the word "TEXT". For
+ example,
+
+ TEXT:abc=
+
+ would insert the literal text "abc=" (without the quotes) into the
+ index display line. You must quote the text if it includes space
+ characters, like
+
+ TEXT:"abc = "
+
+ HEADER
+ This allows you to display the text from a particular header line in
+ the message. The syntax for this token is substantially different
+ from all the others in order that you might be able to display a
+ portion of the text following a particular header. The header name
+ you are interested in is given following a colon after the word
+ "HEADER". For example,
+
+ HEADER:X-Spam
+
+ would display the text of the X-Spam header, if any. Like for other
+ index tokens a width field may (and probably should) follow this.
+
+ HEADER:X-Spam(10)
+
+ displays the first ten characters of the X-Spam header. Unlike other
+ index tokens, the syntax for HEADER is more flexible. An optional
+ second argument comes after a comma inside the parentheses. It
+ specifies the "field" number. By default, the field separator is a
+ space character. No extra space characters are allowed in the
+ argument list.
+
+ HEADER:X-Spam(10,2)
+
+ would display the second field, left-justified, in a 10 character
+ wide field. The second field would consist of all the text after the
+ first space up to the next space or the end of the header. The
+ default field number is zero, which stands for the entire line. There
+ is also an optional third argument which is a list of field
+ separators. It defaults to a space character. The example
+
+ HEADER:X-Spam(10,2,:% )
+
+ would cause the field separators to be any of colon, percent, or
+ space (there is a space character between the percent and the right
+ parenthesis). The first field runs from the start of the header value
+ up to the first colon, percent, or space; the second goes from there
+ to the next; and so on. In order to use a comma character as a field
+ separator you must escape it by preceding it with a backslash (\).
+ The same is true of the backslash character itself. There is one
+ further optional argument. It is an R or an L to specify right or
+ left adjustment of the text within the field. The default is to left
+ justify, however if you are displaying numbers you might prefer to
+ right justify.
+
+ Here's an example of a SpamAssassin header. The exact look of the
+ header will vary, but if your incoming mail contains headers that
+ look like the following
+
+X-Spam-Status: Yes, hits=10.6 tagged_above=-999.0 required=7.0 tests=BAYE...
+
+ you might want to display the hits value. The first field starts with
+ the Y in Yes. To get what you're interested in you might use "=" and
+ space as the field separators and display the third field, like
+
+ HEADER:X-Spam-Status(4,3,= )
+
+ or maybe you would break at the dot instead
+
+ HEADER:X-Spam-Status(2,2,=.,R)
+
+ Another example we've seen has headers that look like
+
+ X-Spam: Gauge=IIIIIII, Probability=7%, Report=...
+
+ Because there are two equals and a comma before the 7% and a comma
+ after it, the token
+
+ HEADER:X-Spam-Status(3,4,=\,,R)
+
+ should display the probability (for example 7% or 83%) right
+ justified in a 3-wide field.
+
+ ARROW
+ This gives an alternative way to display the current message in the
+ MESSAGE INDEX screen. Usually the current message is indicated by the
+ line being shown in reverse video. Instead, if the ARROW token is
+ included in your Index-Format, the current line will include an
+ "arrow" that looks like
+
+ ->
+
+ in the ARROW token's field. For all of the non-current messages, the
+ ARROW field will be filled with blanks. If you use the fixed-field
+ width feature the length of the "arrow" may be adjusted. The arrow
+ will be drawn as width-1 dashes followed by a greater than sign. For
+ example, if you use ARROW(3) you will get
+
+ -->
+
+ and ARROW(1) will give you just
+
+ >
+
+ It is also possible to set the color of the ARROW field. By default
+ (and for non-current messages) the arrow is colored the same as the
+ index line it is part of. You may set it to be another color with the
+ Index Arrow Color option available from the Setup Kolor screen.
+
+ SCORE
+ This gives the score of each message. This will be six columns wide
+ to accomodate the widest possible score. You will probably want to
+ use the Index-Format fixed-field width feature to limit the width of
+ the field to the widest score that you use (e.g. SCORE(3) if your
+ scores are always between 0 and 999). If you have not defined any
+ score rules the scores will all be zero. If any of your score rules
+ contain AllText or BodyText patterns then including SCORE in the
+ Index-Format may slow down the display of the MESSAGE INDEX screen.
+
+ _Tokens Available for all but Index-Format_
+
+ CURNEWS
+ This token represents the current newsgroup if there is one. For
+ example, "comp.mail.pine".
+
+ MSGID
+ This token represents the message ID of the message. This token does
+ not work with Filter Rule folder names.
+
+ CURDATE
+ This token represents the current date. It has the format MMM DD. For
+ example, "Oct 23".
+
+ CURDATEISO
+ This token represents the current date. It has the format YYYY-MM-DD.
+ For example, "1998-10-23".
+
+ CURDATEISOS
+ This token represents the current date. It has the format YY-MM-DD.
+ For example, "98-10-23".
+
+ CURPREFDATE
+ This token represents the current date. It is your operating system's
+ idea of the preferred date representation for the current locale.
+ Internally it uses the %x version of the date from the strftime
+ routine.
+
+ CURPREFTIME
+ This token represents the current time. It is the preferred time
+ representation for the current locale. Internally it uses the %X
+ version of the time from the strftime routine.
+
+ CURPREFDATETIME
+ This token represents the current date and time. It is the preferred
+ date and time representation for the current locale. Internally it
+ uses the %c version of the time from the strftime routine.
+
+ CURTIME24
+ This token represents the current time. It has the format HH:MM. For
+ example, "17:28".
+
+ CURTIME12
+ This token represents the current time. This time is for a 12 hour
+ clock. It has the format HH:MMpm. For example, "5:28pm" or "11:13am".
+
+ CURDAY
+ This token represents the current day of the month. For example, "23"
+ or "9".
+
+ CURDAY2DIGIT
+ This token represents the current day of the month. For example, "23"
+ or "09". It is always 2 digits.
+
+ CURDAYOFWEEK
+ This token represents the current day of the week. For example,
+ "Sunday" or "Wednesday".
+
+ CURDAYOFWEEKABBREV
+ This token represents the current day of the week. For example, "Sun"
+ or "Wed".
+
+ CURMONTH
+ This token represents the current month. For example, "10" or "9".
+
+ CURMONTH2DIGIT
+ This token represents the current month. For example, "10" or "09".
+ It is always 2 digits.
+
+ CURMONTHLONG
+ This token represents the current month. For example, "October".
+
+ CURMONTHABBREV
+ This token represents the current month. For example, "Oct".
+
+ CURYEAR
+ This token represents the current year. For example, "1998" or
+ "2001".
+
+ CURYEAR2DIGIT
+ This token represents the current year. For example, "98" or "01". It
+ is always 2 digits.
+
+ LASTMONTH
+ This token represents last month. For example, if this is November
+ (the 11th month), it is equal to "10" or if this is October (the 10th
+ month), it is "9". It is possible that this and the other tokens
+ beginning with LASTMONTH below could be useful when used with a
+ Filtering Rule that has the "Beginning of Month" option set.
+
+ LASTMONTH2DIGIT
+ This token represents last month. For example, if this is November
+ (the 11th month), it is equal to "10" or if this is October (the 10th
+ month), it is "09". It is always 2 digits.
+
+ LASTMONTHLONG
+ This token represents last month. For example, if this is November
+ the value is "October".
+
+ LASTMONTHABBREV
+ This token represents last month. For example, if this is November
+ the value is "Oct".
+
+ LASTMONTHYEAR
+ This token represents what the year was a month ago. For example, if
+ this is October, 1998, it is "1998". If this is January, 1998, it is
+ "1997".
+
+ LASTMONTHYEAR2DIGIT
+ This token represents what the year was a month ago. For example, if
+ this is October, 1998, it is "98". If this is January, 1998, it is
+ "97".
+
+ LASTYEAR
+ This token represents last year. For example, if this is 1998, it
+ equals "1997". It is possible that this could be useful when used
+ with a Filtering Rule that has the "Beginning of Year" option set.
+
+ LASTYEAR2DIGIT
+ This token represents last year. For example, if this is 1998, it
+ equals "97". It is always 2 digits.
+
+ ROLENICK
+ This token represents the nickname of the role currently being used.
+ If no role is being used, then no text will be printed for this
+ token. This token does not work with Filter Rule folder names.
+
+ _Token Available Only for Reply-Leadin_
+
+ See the help for the Reply-Leadin option, to see why you might want to use
+ this. Since the _Reply-Leadin_ contains free text this token must be
+ surrounded by underscores when used.
+
+ NEWLINE
+ This is an end of line marker.
+
+ _Token Available Only for Templates and Signatures_
+
+ CURSORPOS
+ This token is different from the others. When it is replaced it is
+ replaced with nothing, but it sets a _Alpine_ internal variable which
+ tells the composer to start with the cursor positioned at the
+ position where this token was. If both the template file and the
+ signature file contain a "CURSORPOS" token, then the position in the
+ template file is used. If there is a template file and neither it nor
+ the signature file contains a "CURSORPOS" token, then the cursor is
+ positioned after the end of the contents of the template file when
+ the composer starts up.
+
+Conditional Inclusion of Text for Reply-Leadin, Signatures, and Templates
+
+ Conditional text inclusion may be used with the Reply-Leadin option, in
+ signature files, and in template files used in roles. It may _not_ be used
+ with the _Index-Format_ option.
+
+ There is a limited if-else capability for including text. The if-else
+ condition is based on whether or not a given token would result in
+ replacement text you specify. The syntax of this conditional inclusion is
+
+ _token_(match_this, if_matched [ , if_not_matched ] )
+
+ The left parenthesis must follow the underscore immediately, with no
+ intervening space. It means the token is expanded and the results of that
+ expansion are compared against the "match_this" argument. If there is an
+ exact match, then the "if_matched" text is used as the replacement text.
+ Otherwise, the "if_not_matched" text is used. One of the most useful values
+ for the "match_this" argument is the empty string, "". In that case the
+ expansion is compared against the empty string.
+
+ Here's an example to make it clearer. This text could be included in one of
+ your template files:
+
+ _NEWS_("", "I'm replying to email","I'm replying to news")
+
+ If that is included in a template file which you are using while replying to
+ a message (because you chose to use the role it was part of), and that
+ message has a newsgroup header and a newsgroup in that header, then the text
+
+ I'm replying to news
+
+ will be included in the message you are about to compose. On the other hand,
+ if the message you are replying to does not have a newsgroup, then the text
+
+ I'm replying to email
+
+ would be included instead. This would also work in signature files and in
+ the "Reply-Leadin" option. If the "match_this", "if_matched", or
+ "if_not_matched" arguments contain spaces, parentheses, or commas; they have
+ to be quoted with double quotation marks (like in the example above). If you
+ want to include a literal quote in the text you must escape the quote by
+ preceding it with a backslash character. If you want to include a literal
+ backslash character you must escape it by preceding it with another
+ backslash.
+
+ The comma followed by "if_not_matched" is optional. If there is no
+ "if_not_matched" present then no text is included if the not_matched case is
+ true. Here's another example:
+
+ _NEWS_("", "", "This msg was seen in group: _NEWS_.")
+
+ Here you can see that tokens may appear in the arguments. The same is true
+ for tokens with the conditional parentheses. They may appear in arguments,
+ though you do have to be careful to get the quoting and escaping of nested
+ double quotes correct. If this was in the signature file being used and you
+ were replying to a message sent to comp.mail.pine the resulting text would
+ be:
+
+ This msg was seen in group: comp.mail.pine.
+
+ If you were replying to a message which wasn't sent to any newsgroup the
+ resulting text would be a single blank line. The reason you'd get a blank
+ line is because the end of the line is outside of the conditional, so is
+ always included. If you wanted to get rid of that blank line you could do so
+ by moving the end of line inside the conditional. In other words, it's ok to
+ have multi-line "if_matched" or "if_not_matched" arguments. The text just
+ continues until the next double quotation, even if it's not on the same
+ line.
+
+ Here's one more (contrived) example illustrating a matching argument which
+ is not the empty string.
+
+ _SMARTDATE_("Today", _SMARTDATE_, "On _DATE_") _FROM_ wrote:
+
+ If this was the value of your "Reply-Leadin" option and you were replying to
+ a message which was sent today, then the value of the "Reply-Leadin" would
+ be
+
+ Today Fred Flintstone wrote:
+
+ But if you were replying to a message sent on Oct. 27 (and that wasn't
+ today) you would get
+
+ On Oct 27 Fred Flintstone wrote:
+
+Per Server Directory Configuration
+
+ This is only available if _Alpine_ was built with LDAP support. If that's
+ the case, there will be a Directory option underneath the Setup command on
+ the Main Menu. Each server that is defined there has several configuration
+ variables which control the behavior when using it.
+ _ldap-server_
+ This is the name of the host where an LDAP server is running.
+ To find out whether your organization has its own LDAP server,
+ contact its computing support staff.
+ _search-base_
+ This is the search base to be used on this server. It functions as a
+ filter by restricting your searches in the LDAP server database to
+ the specified contents of the specified fields. Without it, searches
+ submitted to this directory server may fail. It might be something
+ like:
+ O = <Your Organization Name>, C = US
+ or it might be blank. (Some LDAP servers actually ignore anything
+ specified here.)
+ If in doubt what parameters you should specify here, contact the
+ maintainers of the LDAP server.
+ _port_
+ This is the TCP port number to be used with this LDAP server. If you
+ leave this blank port 389 will be used.
+ _nickname_
+ This is a nickname to be used in displays. If you don't supply a
+ nickname the server name from "ldap-server" will be used instead.
+ This option is strictly for your convenience.
+ _use-implicitly-from-composer_
+ Set this feature to have lookups done to this server implicitly from
+ the composer. If an address doesn't look like a fully-qualified
+ address, it will be looked up in your address books, and if it
+ doesn't match a nickname there, then it will be looked up on the LDAP
+ servers which have this feature set. The lookups will also be done
+ when using the address completion feature (TAB command) in the
+ composer if any of the serves have this feature set. Also see the
+ LDAP feature lookup-addrbook-contents and the Setup/Config feature
+ ldap-result-to-addrbook-add.
+ _lookup-addrbook-contents_
+ Normally implicit LDAP lookups from the composer are done only for
+ the strings you type in from the composer screen. In other words, you
+ type in something in the To or CC field and press return, then the
+ string is looked up. First that string is looked up in your address
+ books. If a match is found there, then the results of that match are
+ looked up again. If you place a string in your address book that you
+ want to have looked up on the LDAP directory server, you need to turn
+ on this feature. If you set this feature for a server, you almost
+ always will also want to set the use-implicitly-from-composer
+ feature. An example might serve to best illustrate this feature.
+ If an LDAP lookup of "William Clinton" normally returns an entry with
+ an address of pres@whitehouse.gov, then you might put an entry in
+ your address book that looks like:
+ Nickname Address
+ bill "William Clinton"
+ Now, when you type "bill" into an address field in the composer
+ _Alpine_ will find the "bill" entry in your address book. It will
+ replace "bill" with "William Clinton". It will then search for an
+ entry with that nickname in your address book and not find one. If
+ this feature is set, _Alpine_ will then attempt to lookup "William
+ Clinton" on the LDAP server and find the entry with address
+ pres@whitehouse.gov.
+ A better way to accomplish the same thing is probably to use the
+ feature save-search-criteria-not-result.
+ _save-search-criteria-not-result_
+ Normally when you save the results of an LDAP directory lookup to
+ your address book the _results_ of the lookup are saved. If this
+ feature is set and the entry being saved was found on this directory
+ server, then the search _criteria_ is saved instead of the _results_
+ of the search. When this address book entry is used in the future,
+ instead of copying the results from the address book the directory
+ lookup will be done again. This could be useful if the copied result
+ might become stale because the data on the directory server changes
+ (for example, the entry's email address changes). You probably don't
+ want to set this feature if the server is at all slow or unreliable.
+ The way this actually works is that instead of saving the email
+ address in your address book, _Alpine_ saves enough information to
+ look up the same directory entry again. In particular, it saves the
+ server name and the distinguished name of the entry. It's possible
+ that the server administrators might change the format of
+ distinguished names on the server, or that the entry might be removed
+ from the server. If _Alpine_ notices this, you will be warned and a
+ backup copy of the email address will be used. You may want to create
+ a new entry in this case, since you will get the annoying warning
+ every time you use the old entry. You may do that by Saving the entry
+ to a new nickname in the same address book. You will be asked whether
+ or not you want to use the backup email address.
+ A related feature in the Setup/Config screen is
+ ldap-result-to-addrbook-add.
+ _disable-ad-hoc-space-substitution_
+ Spaces in your input are normally handled specially. Each space
+ character is replaced by
+ * <SPACE>
+ in the search query (but not by "* <SPACE> *"). The reason this is
+ done is so the input string
+ Greg Donald
+ (which is converted to "Greg* Donald") will match the names "Greg
+ Donald", "Gregory Donald", "Greg F. Donald", and "Gregory F Donald";
+ but it won't match "Greg McDonald". If the "Search-Rule" you were
+ using was "begins-with", then it would also match the name "Greg
+ Donaldson".
+ Turning on this feature will disable this substitution.
+ _search-type_
+ This affects the way that LDAP searches are done. In particular, this
+ tells the server where to look for the string to be matched. If set
+ to "name" then the string that is being searched for will be compared
+ with the string in the "Name" field on the server (technically, it is
+ the "commonname" field on the server). "Surname" means we're looking
+ for a match in the "Surname" field on the server (actually the "sn"
+ field). "Givenname" really is "givenname" and "email" is the
+ electronic mail address (this is actually the field called "mail" or
+ "electronicmail" on the server). The other three types are
+ combinations of the types listed so far. "Name-or-email" means the
+ string should appear in either the "name" field OR the "email" field.
+ Likewise, "surname-or-givenname" means "surname" OR "givenname" and
+ "sur-or-given-or-name-or-email" means the obvious thing.
+ This search _type_ is combined with the search rule to form the
+ actual search query.
+ The usual default value for this option is
+ "sur-or-given-or-name-or-email". This type of search may be slow on
+ some servers. Try "name-or-email", which is often faster, or just
+ "name" if the performance seems to be a problem.
+ Some servers have been configured with different attribute names for
+ these four fields. In other words, instead of using the attribute
+ name "mail" for the email address field, the server might be
+ configured to use something else, for example, "rfc822mail" or
+ "internetemailaddress". _Alpine_ can be configured to use these
+ different attribute names by using the four per-server configuration
+ options:
+ + email-attribute
+ + name-attribute
+ + surname-attribute
+ + givenname-attribute
+ _search-rule_
+ This affects the way that LDAP searches are done. If set to "equals"
+ then only exact matches count. "Contains" means that the string you
+ type in is a substring of what you are matching against.
+ "Begins-with" and "ends-with" mean that the string starts or ends
+ with the string you type in.
+ Spaces in your input are normally handled specially, but you can turn
+ that special handling off with the disable-ad-hoc-space-substitution
+ feature.
+ The usual default value for this option is _begins-with_.
+ _email-attribute_
+ This is the name of the attribute which is searched for when looking
+ for an email address. The default value for this option is "mail" or
+ "electronicmail". If the server you are using uses a different
+ attribute name for the email address, put that attribute name here.
+ This will affect the search filter used if your Search-Type is one
+ that contains a search for "email". It will also cause the attribute
+ value matching this attribute name to be used as the email address
+ when you look up an entry from the composer.
+ _name-attribute_
+ This is the name of the attribute which is searched for when looking
+ for the name of the entry. The default value for this option is "cn",
+ which stands for common name. If the server you are using uses a
+ different attribute name for the name, put that attribute name here.
+ This will affect the search filter used if your Search-Type is one
+ that contains a search for "name".
+ _surname-attribute_
+ This is the name of the attribute which is searched for when looking
+ for the surname of the entry. The default value for this option is
+ "sn". If the server you are using uses a different attribute name for
+ the surname, put that attribute name here. This will affect the
+ search filter used if your Search-Type is one that contains a search
+ for "surname".
+ _givenname-attribute_
+ This is the name of the attribute which is searched for when looking
+ for the given name of the entry. The default value for this option is
+ "givenname". If the server you are using uses a different attribute
+ name for the given name, put that attribute name here. This will
+ affect the search filter used if your Search-Type is one that
+ contains a search for "givenname".
+ _timelimit_
+ This places a limit on the number of seconds the LDAP search will
+ continue. The default is 30 seconds. A value of 0 means no limit.
+ Note that some servers may place limits of their own on searches.
+ _sizelimit_
+ This places a limit on the number of entries returned by the LDAP
+ server. A value of 0 means no limit. The default is 0. Note that some
+ servers may place limits of their own on searches.
+ _custom-search-filter_
+ This one is for advanced users only! If you define this, then the
+ search-type and search-rule defined are both ignored. However, the
+ feature disable-ad-hoc-space-substitution is still in effect. That
+ is, the space substitution will take place even in a custom filter
+ unless you disable it.
+ If your LDAP service stops working and you suspect it might be
+ because of your custom filter, just delete this filter and try using
+ the _search-type_ and _search-rule_ instead. Another option that
+ sometimes causes trouble is the search-base option.
+ This variable may be set to the string representation of an LDAP
+ search filter (see RFC1960). In the places where you want the address
+ string to be substituted in, put a '%s' in this filter string. Here
+ are some examples:
+ A "Search-Type" of "name" with "Search-Rule" of "begins-with" is
+ equivalent to the "custom-search-filter"
+ (cn=%s*)
+ When you try to match against the string "string" the program
+ replaces the "%s" with "string" (without the quotes). You may have
+ multiple "%s"'s and they will all be replaced with the string. There
+ is a limit of 10 "%s"'s.
+ A "Search-Type" of "name-or-email" with "Search-Rule" of "contains"
+ is equivalent to
+ (|(cn=*%s*)(mail=*%s*))
+ If your server uses a different attribute _name_ than _Alpine_ uses
+ by default, (for example, it uses "rfc822mail" instead of "mail"),
+ then you may be able to use one or more of the four attribute
+ configuration options instead of defining a custom filter:
+ + email-attribute
+ + name-attribute
+ + surname-attribute
+ + givenname-attribute
+
+Color Configuration
+
+ If the terminal or terminal emulator you are using is capable of using color
+ (see color-style option), or if you are using _PC-Alpine_, then it is
+ possible to set up _Alpine_ so that various parts of the display will be
+ shown in colors you configure. This is done using the Setup Color screen.
+ The Setup Color screen is divided into five broad sections: Options, General
+ Colors, Index Colors, Header Colors, and Keyword Colors. In addition to
+ these five categories you may also color lines in the MESSAGE INDEX screen
+ by configuring the Index Line Color.
+
+ Each color is defined as a foreground color (the color of the actual text)
+ and a background color (the color of the area behind the text).
+
+ Color Options
+
+ _current-indexline-style_
+ This option affects the colors used to display the current line in
+ the MESSAGE INDEX screen. If you do not have Index Line Colors
+ defined, then this option will have no effect in the index. Those
+ Rules may be defined by going to the Setup/Rules/Indexcolor screen.
+
+ If the option enable-incoming-folders-checking is turned on and the
+ Incoming Unseen Color is set to something other than the default,
+ then this option also affects the color used to display the current
+ folder in the Incoming FOLDER LIST screen.
+
+ The available options include:
+
+ flip-colors
+ This is the default. If an index line is colored because it
+ matches one of your Index Color Rules, then its colors will be
+ reversed when it is the currently highlighted line. For
+ example, if the line is normally red text on a blue background,
+ then when it is the current line it will be drawn as blue text
+ on a red background.
+
+ The rest of the option values all revert to this flip-colors
+ behavior if there is no Reverse Color defined.
+
+ reverse
+ With this option the Reverse color is always used to highlight
+ the current line.
+
+ reverse-fg
+ The foreground part of the Reverse Color is used to highlight
+ the current line. If this would cause the text to be unreadable
+ (because the foreground and background colors are the same) or
+ if it would cause no change in the color of the index line,
+ then the colors are flipped instead.
+
+ Some people think this works particularly well if you use
+ different background colors to emphasize "interesting" lines,
+ but always with the same Normal foreground color, and you use a
+ different foreground color for the Reverse Color.
+
+ reverse-fg-no-ambiguity
+ With the "reverse-fg" rule above, it is possible that the
+ resulting color will be exactly the same as the regular Reverse
+ Color. That can lead to some possible confusion because an
+ "interesting" line which is the current line will be displayed
+ exactly the same as a non-interesting line which is current.
+ You can't tell whether the line is just a regular current line
+ or if it is an "interesting" current line by looking at the
+ color. Setting the option to this value removes that ambiguity.
+ It is the same as the "reverse-fg" setting unless the resulting
+ interesting current line would look just like a non-interesting
+ current line. In that case, the interesting line's colors are
+ simply flipped (like in the default behavior).
+
+ As an alternative way to preserve the line's interestingness in
+ this case, you may find that using both a different foreground
+ and a different background color for the interesting line will
+ help.
+
+ reverse-bg
+ The background part of the Reverse Color is used to highlight
+ the current line. If this would cause the text to be unreadable
+ (because the foreground and background colors are the same) or
+ if it would cause no change in the color of the index line,
+ then the colors are flipped instead.
+
+ Some people think this works particularly well if you use
+ different foreground colors to emphasize "interesting" lines,
+ but always with the same Normal background color, and you use a
+ different background color for the Reverse Color.
+
+ reverse-bg-no-ambiguity
+ As with the "reverse-fg" case, the "reverse-bg" rule may also
+ result in a color which is exactly the same as the regular
+ Reverse Color. Setting the option to this value removes that
+ ambiguity. It is the same as the "reverse-bg" setting unless
+ the resulting current line has the same color as the Reverse
+ Color. In that case, the interesting line's colors are simply
+ flipped (like in the default behavior).
+
+ _titlebar-color-style_
+ This option affects the colors used to display the titlebar (the top
+ line on the screen) when viewing a message.
+
+ The available options include:
+
+ default
+ The color of the titlebar will be the color you set for the
+ Title Color. The Title Color may be set by using the
+
+ indexline
+ The color of the titlebar will be the same as the color of the
+ index line corresponding to the message being viewed. The rules
+ which determine what color the index line will be may be set up
+ by going to the Setup/Rules/Indexcolor screen. If the index
+ line for a message is not colored explicitly by the Indexcolor
+ rules, then the titlebar will be colored the same as for the
+ "default" option above (which is not the same color that the
+ index line itself will have).
+
+ reverse-indexline
+ This is similar to the "indexline" option except the foreground
+ and background colors from the corresponding index line will be
+ reversed. For example, if the index line color is red letters
+ on a white background, then the titlebar will be white letters
+ on a red background. If the index line for a message is not
+ colored explicitly by the Indexcolor rules, then the titlebar
+ will be colored the same as for the "default" option above
+ (which is not the same color that the index line itself will
+ have).
+
+ General Colors
+
+ _Normal Color_
+ This is the color which most of the screen is painted in. By default
+ this color is black characters on a white background.
+ _Reverse Color_
+ The color _Alpine_ uses for reverse video characters. Actually, the
+ name is misleading. This used to be reverse video and so the name
+ remains. It is still used to highlight certain parts of the screen
+ but the color may be set to whatever you'd like.
+ _Title Color_
+ The color _Alpine_ uses for the titlebar (the top line on the
+ screen). By default, the Title Color is black characters on a yellow
+ background. The actual titlebar color may be different from the Title
+ Color if the option titlebar-color-style is set to some value other
+ than the default. It may also be different if the current folder is
+ closed and the Title Closed Color is set to something different from
+ the Title Color.
+ _Title-closed Color_
+ The color _Alpine_ uses for the titlebar (the top line on the screen)
+ when the current folder is closed. By default, the Title Color Closed
+ Color is white characters on a red background.
+ _Status Color_
+ The color _Alpine_ uses for messages written to the status message
+ line near the bottom of the screen. By default, the Status Color is
+ the same as the Reverse Color.
+ _KeyLabel Color_
+ The color _Alpine_ uses for the labels of the commands in the
+ two-line menu at the bottom of the screen. The label is the long
+ name, for example, "PrevMsg". By default, the KeyLabel Color is the
+ same as the Normal Color.
+ WARNING: Some terminal emulators have the property that the screen
+ will scroll down one line whenever a character is written to the
+ character cell in the lower right corner of the screen. _Alpine_ can
+ usually avoid writing a character in that corner of the screen.
+ However, if you have defined a KeyLabel Color then _Alpine_ does have
+ to write a character in that cell in order to color the cell
+ correctly. If you find that your display sometimes scrolls up a line
+ this could be the problem. The most obvious symptom is probably that
+ the titlebar at the top of the screen scrolls off the screen. Try
+ setting KeyLabel Color to Default to see if that fixes the problem.
+ _KeyName Color_
+ The color _Alpine_ uses for the names of the commands in the two-line
+ menu at the bottom of the screen. The KeyName is the shorter name in
+ the menu. For example, the "W" before the "WhereIs". By default, the
+ KeyName Color is the same as the Normal Color.
+ _Selectable-item Color_
+ The color _Alpine_ uses for displaying selectable items, such as
+ URLs. By default, the Selectable-item Color is the same as the Normal
+ Color, except it is also Bold.
+ _Meta-message Color_
+ The color _Alpine_ uses in the MESSAGE TEXT screen for messages to
+ you that aren't part of the message itself. By default, the
+ Meta-Message Color is black characters on a yellow background.
+ _Quote Colors_
+ The colors _Alpine_ uses for coloring quoted text in the MESSAGE TEXT
+ screen. If a line begins with a > character (or space followed by >)
+ it is considered a quote. That line will be given the Quote1 Color
+ (first level quote). If there is a second level of quoting then the
+ Quote2 Color will be used. _Alpine_ considers there to be a second
+ level of quoting if that first > is followed by another > (or space
+ followed by >). If there are characters other than whitespace and >
+ signs, then it isn't considered another level of quoting. Similarly,
+ if there is a third level of quoting the Quote3 Color will be used.
+ If there are more levels after that the Quote Colors are reused. If
+ you define all three colors then it would repeat like Color1, Color2,
+ Color3, Color1, Color2, Color3, ... If you only define the first two
+ it would be Color1, Color2, Color1, Color2, ... If you define only
+ the Quote1 Color, then the entire quote would be that color
+ regardless of the quoting levels. By default, the Quote1 Color is
+ black characters on a greenish-blue background; the Quote2 Color is
+ black characters on a dull yellow background; and the Quote3 Color is
+ black characters on a green background.
+ _Incoming Unseen Color_
+ If the option enable-incoming-folders-checking is turned on it is
+ possible to highlight the folders that contain unseen messages by
+ coloring them with this color. By default, this is the same as the
+ Normal Color and no highlighting is done.
+ Usually the "current" folder (the folder the cursor is on) is
+ highlighted using reverse video. If the current folder is colored
+ because it contains unseen messages then the color used to show that
+ it is also the current folder is controlled by the
+ current-indexline-style feature at the top of the SETUP COLOR screen.
+ _Signature Color_
+ The color _Alpine_ uses for coloring the signature in the MESSAGE
+ TEXT screen. According to USENET conventions, the signature is
+ defined as the paragraph following the "sigdashes", that is, the
+ special line consisting of the three characters "-- " (i.e., dash,
+ dash, and space). _Alpine_ allows for one empty line right after the
+ sigdashes to be considered as part of the signature. By default, the
+ Signature Color is blue characters on a white background.
+ _Prompt Color_
+ The color _Alpine_ uses for confirmation prompts and questions which
+ appear in the status message line near the bottom of the screen. By
+ default, the Prompt Color is the same as the Reverse Color.
+
+ Index Colors
+
+ You may add color to the single character symbols which give the status of
+ each message in the MESSAGE INDEX. By default the characters "+", "*", "D",
+ "A", and "N" show up near the left hand side of the screen, depending on
+ whether the message is addressed to you, and whether the message is marked
+ Important, is Deleted, is Answered, or is New. You may set the color of
+ those symbols. By default, all of these symbols are drawn with the same
+ color as the rest of the index line they are a part of.
+
+ Besides coloring the message status symbols, you may also color the entire
+ index line. This is done by using the Index Line Color configuration screen.
+ It is also possible to color (keywords in the index using the Setup/Kolor
+ screen (Keyword Colors); the ARROW cursor; the Subject using Index Subject
+ Color; the From using Index From Color; and the Index Opening text.
+
+ _Index-to-me Symbol Color_
+ The color used for drawing the "+" symbol which signifies a message
+ is addressed directly to you.
+ _Index-important Symbol Color_
+ The color used for drawing the "*" symbol which signifies a message
+ has been flagged Important.
+ _Index-deleted Symbol Color_
+ The color used for drawing the "D" symbol which signifies a message
+ has been marked Deleted.
+ _Index-answered Symbol Color_
+ The color used for drawing the "A" symbol which signifies a message
+ has been answered.
+ _Index-new Symbol Color_
+ The color used for drawing the "N" symbol which signifies a message
+ is New.
+ _Index-recent Symbol Color_
+ The color used for drawing the "R" symbol which signifies a message
+ is Recent (only visible if the "IMAPSTATUS" or "SHORTIMAPSTATUS"
+ token is part of the index-format option).
+ _Index-unseen Symbol Color_
+ The color used for drawing the "U" symbol which signifies a message
+ is Unseen (only visible if the "IMAPSTATUS" or "SHORTIMAPSTATUS"
+ token is part of the Index-Format option).
+ _Index-priority Symbol Colors_
+ The colors used for drawing the tokens "PRIORITY", "PRIORITYALPHA",
+ and "PRIORITY!" when these are configured as part of the Index-Format
+ option. You may set the color used to draw these tokens by use of the
+ colors Index High Priority Symbol Color and Index Low Priority Symbol
+ Color. This coloring takes place for all but the current index line,
+ and the Priority Color appears to be in front of any color from an
+ Index Color Rule. If the priority has a value of 1 or 2 the High
+ Priority color will be used, and if the value is 4 or 5 the Low
+ Priority color will be used.
+ If you don't set these colors the index line will be colored in the
+ same color as the bulk of the index line.
+ _Index-arrow Symbol Color_
+ The color used for drawing the "ARROW" token when it is configured as
+ part of the Index-Format option.
+ _Index-subject Symbol Color_
+ You may set the color used to draw the Subject part of the index
+ line. This coloring takes place for all but the current index line,
+ and the Subject Color appears to be in front of any color from an
+ Index Color Rule.
+ If you don't set this color it will be colored in the same color as
+ the bulk of the index line.
+ _Index-from Symbol Color_
+ You may set the color used to draw the From part of the index line.
+ This coloring takes place for all but the current index line, and the
+ From Color appears to be in front of any color from an Index Color
+ Rule.
+ If you don't set this color it will be colored in the same color as
+ the bulk of the index line.
+ _Index-opening Symbol Color_
+ It is possible to configure the Index-Format option so that it
+ includes the subject followed by the "opening" text of the message if
+ there is enough space. This is done by using one of the tokens
+ SUBJECTTEXT, SUBJKEYTEXT, or SUBJKEYINITTEXT. The color used for
+ drawing this opening text is given by this option. The coloring
+ happens for all but the current index line, and this opening color
+ appears to be in front of any color from an Index Color Rule.
+ By default the Index Opening Color is gray characters on a white
+ background.
+
+ The default colors for these symbols are:
+
+ Index-to-me black on cyan
+ Index-important white on bright red
+ Index-deleted same as Normal Color
+ Index-answered bright red on yellow
+ Index-new white on magenta
+ Index-recent same as Normal Color
+ Index-unseen same as Normal Color
+
+ Header Colors
+
+ You may add color to the header fields in the MESSAGE TEXT screen. The
+
+ _Header-general Color_
+ may be used to color all of the headers of the message.
+
+ It is also possible to set the colors for specific header fields, for
+ example for the Subject or From fields, using the viewer-hdr-colors option.
+
+ For Header Colors, there is an additional line on the configuration screen
+ labeled "Pattern to match". If you leave that blank, then the whole field
+ for that header will always be colored. However, if you give a pattern to
+ match, the coloring will only take place if there is a match for that
+ pattern in the value of the field. For example, if you are working on a
+ color for the Subject header and you fill in a pattern of "important", then
+ only Subjects which contain the word "important" will be colored. For
+ address fields like From or To, a pattern match will cause only the
+ addresses which match the pattern to be colored.
+
+ If the pattern you enter is a comma-separated list of patterns, then
+ coloring happens if any of those patterns matches.
+
+ Keyword Colors
+
+ Sets the colors _Alpine_ uses for Keyword fields in the MESSAGE INDEX
+ screen. Keywords may be displayed as part of the Subject of a message by
+ using the "SUBJKEY" or "SUBJKEYINIT" tokens in the Index-Format option.
+ Keywords may also be displayed in a column of their own in the MESSAGE INDEX
+ screen by using the "KEY" or "KEYINIT" tokens.
+
+ For example, you might have set up a Keyword "Work" using the Keywords
+ option in the Setup/Config screen. You could cause that Keyword to show up
+ as a special color by setting up the Keyword Color using this option, and
+ then including it in the MESSAGE INDEX screen using one of the tokens listed
+ above in the Index-Format.
+
+ Index Line Colors
+
+ You may color whole index lines by using roles. This isn't configured in the
+ Setup Colors screen, but is configured in the Setup Rules IndexColor screen.
+
+Index Line Color Configuration
+
+ Index Line Color causes lines in the MESSAGE INDEX screen to be colored.
+ This action is only available if your terminal is capable of displaying
+ color and color display has been enabled with the Color-Style option. (In
+ PC-Alpine, color is always enabled so there is no option to turn on.)
+
+ Each rule has a "Pattern", which is used to decide which of the rules is
+ used; and the color which is used if the Pattern matches a particular
+ message.
+
+ Rule Patterns
+
+ In order to determine whether or not a message matches a rule the message is
+ compared with the rule's Pattern. These Patterns are the same for use with
+ Roles, Filtering, Index Coloring, Scoring, Other Rules, and Search Rules, so
+ are described in only one place, "here".
+
+ Index Line Color
+
+ This is the color that index lines are colored when there is a matching
+ Pattern. This colors the whole index line, except possibly the status
+ letters which may be colored separately using the Setup Kolor screen.
+
+Role Configuration
+
+ You may play different roles depending on who you are replying to. For
+ example, if you are replying to a message addressed to _help-desk_ you may
+ be acting as a Help Desk Worker. That role may require that you use a
+ different return address and/or a different signature.
+
+ Roles are optional. If you set up roles they work like this: Each role has a
+ set of "Uses", which indicate whether or not a role is eligible to be
+ considered for a particular use; a "Pattern", which is used to decide which
+ of the eligible roles is used; and a set of "Actions", which are taken when
+ that role is used. When you reply to a message, the message you are replying
+ to is compared with the Patterns of the roles marked as eligible for use
+ when replying. The comparisons start with the first eligible role and keep
+ going until there is a match. If a match is found, the matching role's
+ Actions are taken.
+
+ It is also possible to set a default role and to change that role during
+ your _Alpine_ session. When you start _Alpine_ no default role will be set.
+ You may set or change the current default role by using the "D" command in
+ the role selection screen. You'll see that screen while composing a message
+ and being asked to select a role. An easy way to get to that screen is to
+ use the Role Command to compose a message. You may find a default role
+ useful if you normally perform the duties of one of your roles for a while,
+ then you switch to another role and stay in the new role for another period
+ of time. It may be easier than using the Role Command to select the role
+ each time you compose a message.
+
+ Role Uses
+
+ There are three types of use to be configured; one for Replying, one for
+ Forwarding, and one for Composing. These indicate whether or not you want a
+ role to be considered when you type the Reply, Forward, or Compose commands.
+ (The Role command is an alternate form of the Compose command, and it is not
+ affected by these settings.) Each of these Use types has three possible
+ values. The value "Never" means that the role will never be considered as a
+ candidate for use with the corresponding command. For example, if you set a
+ role's Reply Use to Never, then when you Reply to a message, the role won't
+ even be considered. (That isn't quite true. If the message you are replying
+ to matches some other role which requires confirmation, then there will be a
+ ^T command available which allows you to select a role from all of your
+ roles, not just the reply-eligible roles.)
+
+ The options "With confirmation" and "Without confirmation" both mean that
+ you do want to consider this role when using the corresponding command. For
+ either of these settings the role's Pattern will be checked to see if it
+ matches the message. For Reply Use, the message used to compare the Patterns
+ with is the message being replied to. For Forward Use, the message used to
+ compare the Pattern with is the message being forwarded. For Compose Use,
+ there is no message, so the parts of the Pattern which depend on a message
+ (everything other than Current Folder Type) are ignored. In all cases, the
+ Current Folder is checked if defined. If there is a match then this role
+ will either be used without confirmation or will be the default when
+ confirmation is asked for, depending on which of the two options is
+ selected. If confirmation is requested, you will have a chance to choose No
+ Role instead of the offered role, or to change the role to any one of your
+ other roles (with the ^T command).
+
+ Role Patterns
+
+ In order to determine whether or not a message matches a role the message is
+ compared with the Role Pattern. These Patterns are the same for use with
+ Roles, Filtering, Index Coloring, Scoring, Other Rules, and Search Rules, so
+ are described in only one place, "here".
+
+ Since header patterns, AllText patterns, and BodyText patterns which are
+ unset are ignored, a role which has all header patterns unset, the AllText
+ pattern unset, the BodyText pattern unset, the Score Interval unset, and the
+ Current Folder Type set to "Any" may be used as a default role. It should be
+ put last in the list of roles since the matching starts at the beginning and
+ proceeds until one of the roles is a match. If no roles at all match, then
+ _Alpine_ will use its regular methods of defining the role. If you wanted to,
+ you could define a different "default" role for Replying, Forwarding, and
+ Composing by setting the "Use" fields appropriately.
+
+ Role Actions
+
+ Once a role match is found, the role's Actions are taken. For each role
+ there are several possible actions that may be defined. They are actions to
+ set the From address, the Reply-To address, the Fcc, the Signature file, and
+ the Template file.
+
+ Initialize Settings Using Role
+
+ This is a power user feature. You will usually want to leave this field
+ empty. The value of this field is the nickname of another one of your roles.
+ The Action values from that other role are used as the initial values of the
+ Action items for this role. If you put something in any of the action fields
+ for this role, that will override whatever was in the corresponding field of
+ the initializer role.
+
+ You might use this field if the "Action" part of one of your roles is
+ something you want to use in more than one role. Instead of filling in those
+ action values again for each role, you may give the nickname of the role
+ where the values are filled in. It's just a shortcut way to define Role
+ Actions.
+
+ Here's an example to help explain how this works. Suppose you have a role
+ with nickname "role1" and role1 has (among other things)
+
+ Set Reply-To = The Pres <president@example.com>
+
+ set. If in "role2" you set "Initialize settings using role" to "role1", then
+ role2 will inherit the Set Reply-To value from role1 by default (and any of
+ the other inheritable action values that are set). So if role2 had
+
+ Set Reply-To = <No Value Set>
+
+ defined, the Reply-To used with role2 would be "The Pres
+ <president@example.com>" However, if role2 had
+
+ Set Reply-To = VP <vicepresident@example.com>
+
+ defined, then the Reply-To used with role2 would be "VP
+ <vicepresident@example.com>" instead.
+
+ If you wish, you may choose a nickname from your list of roles by using the
+ "T" command. If the role you are using to initialize also has a role it
+ initializes from, then that initialization happens first. That is,
+ inheritance works as expected with the grandparent and great-grandparent
+ (and so on) roles having the expected effect.
+
+ Set From
+
+ This field consists of a single address which will be used as the From
+ address on the message you are sending. This should be a fully-qualified
+ address like
+
+ Full Name <user@domain>
+
+ or just
+
+ user@domain
+
+ If this is left blank, then the normal From address will be used.
+
+ Set Reply-To
+
+ The Reply-To address is the address used on the Reply-To line of the message
+ you are sending. You don't need a Reply-To address unless it is different
+ from the From address. This should be a fully-qualified address like
+
+ Full Name <user@domain>
+
+ or just
+
+ user@domain
+
+ If this is left blank, then there won't be a Reply-To address unless you
+ have configured one specially with the customized-hdrs configuration option.
+
+ Set Other-Hdrs
+
+ This field gives you a way to set values for headers besides "From" and
+ "Reply-To". If you want to set either of those, use the specific "Set From"
+ and "Set Reply-To" settings.
+
+ This field is similar to the customized-hdrs option. Each header you specify
+ here must include the header tag ("To:", "Approved:", etc.) and may
+ optionally include a value for that header. In order to see these headers
+ when you compose using this role you must use the rich header command.
+ Here's an example which shows how you might set the To address.
+
+ Set Other Hdrs = To: Full Name <user@domain>
+
+ Headers set in this way are different from headers set with the
+ customized-hdrs option in that the value you give for a header here will
+ replace any value that already exists. For example, if you are Replying to a
+ message there will already be at least one address in the To header (the
+ address you are Replying to). However, if you Reply using a role which sets
+ the To header, that role's To header value will be used instead. The
+ customized-hdrs headers are defaults.
+
+ Limitation: Because commas are used to separate the list of Other Headers,
+ it is not possible to have the value of a header contain a comma; nor is
+ there currently an "escape" mechanism provided to make this work.
+
+ Set Fcc
+
+ This field consists of a single folder name which will be used in the Fcc
+ field of the message you are sending. You may put anything here that you
+ would normally type into the Fcc field from the composer.
+
+ In addition, an fcc of "" (two double quotation marks) means no Fcc.
+
+ A blank field here means that _Alpine_ will use its normal rules for
+ deciding the default value of the Fcc field. For many roles, perhaps most,
+ it may make more sense for you to use the other _Alpine_ facilities for
+ setting the Fcc. In particular, if you want the Fcc to depend on who you are
+ sending the message to then the fcc-name-rule is probably more useful. In
+ that case, you would want to leave the Fcc field here blank. However, if you
+ have a role that depends on who the message you are replying to was From, or
+ what address that message was sent to; then it might make sense to set the
+ Fcc for that role here.
+
+ Set LiteralSig
+
+ This field contains the actual text for your signature, as opposed to the
+ name of a file containing your signature. If this is defined it takes
+ precedence over any value set in the _Set Signature_ field.
+
+ This is simply a different way to store the signature. The signature is
+ stored inside your Alpine configuration file instead of in a separate
+ signature file. Tokens work the same way they do with _Set Signature_.
+
+ The two character sequence \n (backslash followed by the character n) will
+ be used to signify a line-break in your signature. You don't have to enter
+ the \n, but it will be visible in the CHANGE THIS ROLE RULE window after you
+ are done editing the signature.
+
+ Set Signature
+
+ The Signature is the name of a file to be used as the signature file when
+ this role is being used. If the filename is followed by a vertical bar (|)
+ then instead of reading the contents of the file the file is assumed to be a
+ program which will produce the text to be used on its standard output. The
+ program can't have any arguments and doesn't receive any input from
+ _Alpine_, but the rest of the processing works as if the contents came from a
+ file.
+
+ Signature files may be stored remotely on an IMAP server. In order to do
+ that you just give the file a remote name. This works just like the regular
+ signature-file option which is configured from the Setup/Configuration
+ screen. A remote signature file name might look like:
+
+ {myimaphost.myschool.k12.wa.us}mail/sig3
+
+ or, if you have an SSL-capable version of _Alpine_, you might try
+
+ {myimaphost.myschool.k12.wa.us/user=loginname/ssl}mail/sig3
+
+ Once you have named the remote signature file you create its contents by
+ using the "F" "editFile" command when the cursor is on the "Set Signature"
+ line of the role editor.
+
+ Besides containing regular text, a signature file may also contain (or a
+ signature program may produce) tokens which are replaced with text which
+ depends on the message you are replying to or forwarding. The tokens all
+ look like _word_ (a word surrounded by underscores). For example, if the
+ token
+
+ _DATE_
+
+ is included in the text of the signature file, then when you reply to or
+ forward a message, the token will be replaced with the actual date the
+ message you are replying to or forwarding was sent.
+
+ If you use a role which has a signature file for a plain composition (that
+ is, not a reply or forward) then there is no original message, so any tokens
+ which depend on the message will be replaced with nothing. So if you want a
+ signature file to be useful for new compositions it shouldn't include any of
+ the tokens which depend on the message being replied to or forwarded.
+
+ The list of available tokens is here.
+
+ Actually, for the adventurous, there is a way to conditionally include text
+ based on whether or not a token would result in specific replacement text.
+ For example, you could include some text based on whether or not the _NEWS_
+ token would result in any newsgroups if it was used. It's explained in
+ detail here.
+
+ In the very unlikely event that you want to include a literal token in a
+ signature file, you must precede it with a backslash character. For example,
+ to include the literal text _DATE_ you must actually use \_DATE_. It is not
+ possible to have a literal backslash followed by an expanded token.
+
+ A blank field here means that _Alpine_ will use its normal rules for
+ deciding which file (if any) to use for the signature file.
+
+ Set Template
+
+ A Template is the name of a file to be included in the message when this
+ role is being used. The template file is a file which is included at the top
+ of the message you are composing.
+
+ If the filename is followed by a vertical bar (|) then instead of reading
+ the contents of the file the file is assumed to be a program which will
+ produce the text to be used on its standard output. The program can't have
+ any arguments and doesn't receive any input from _Alpine_, but the rest of
+ the processing works as if the contents came from a file.
+
+ Template files may be stored remotely on an IMAP server. In order to do that
+ you just give the file a remote name. This works just like the regular
+ signature-file option which is configured from the Setup/Configuration
+ screen. A remote template file name might look like:
+
+ {myimaphost.myschool.k12.wa.us}mail/templ3
+
+ or, if you have an SSL-capable version of _Alpine_, you might try
+
+ {myimaphost.myschool.k12.wa.us/user=loginname/ssl}mail/templ3
+
+ Once you have named the remote template file you create its contents by
+ using the "F" "editFile" command when the cursor is on the "Set Template"
+ line of the role editor.
+
+ Besides containing regular text, a template file may also contain (or a
+ template file program may produce) tokens which are replaced with text which
+ depends on the message you are replying to or forwarding. The tokens all
+ look like _word_ (a word surrounded by underscores). For example, if the
+ token
+
+ _DATE_
+
+ is included in the text of the template file, then when you reply to or
+ forward a message, the token will be replaced with the actual date the
+ message you are replying to or forwarding was sent.
+
+ If you use a role which has a template file for a plain composition (that
+ is, not a reply or forward) then there is no original message, so any tokens
+ which depend on the message will be replaced with nothing. So if you want a
+ template file to be useful for new compositions it shouldn't include any of
+ the tokens which depend on the message being replied to or forwarded.
+
+ The list of available tokens is here.
+
+ Actually, for the adventurous, there is a way to conditionally include text
+ based on whether or not a token would result in specific replacement text.
+ For example, you could include some text based on whether or not the _NEWS_
+ token would result in any newsgroups if it was used. It's explained in
+ detail here.
+
+ In the very unlikely event that you want to include a literal token in a
+ template file, you must precede it with a backslash character. For example,
+ to include the literal text _DATE_ you must actually use \_DATE_. It is not
+ possible to have a literal backslash followed by an expanded token.
+
+ A blank field here means that _Alpine_ will not use a template file when
+ this role is being used.
+
+ Use SMTP Server
+
+ If this field has a value, then it will be used as the SMTP server to send
+ mail when this role is being used (unless the SMTP server variable is set in
+ the system-wide fixed configuration file). It has the same semantics as the
+ smtp-server variable in the Setup/Config screen. When you postpone the
+ composition this SMTP server list will be saved with the postponed
+ composition and it cannot be changed later. Because of that, you may want to
+ make this a list of SMTP servers with the preferred server at the front of
+ the list and alternate servers later in the list.
+
+ If any of the actions are left unset, then the action depends on what is
+ present in the "Initialize settings using role" field. If you've listed the
+ nickname of another one of your roles there, then the corresponding action
+ from that role will be used here. If that action is also blank, or if there
+ is no nickname specified, then _Alpine_ will do whatever it normally does to
+ set these actions. This depends on other configuration options and features
+ you've set.
+
+Filtering Configuration
+
+ The software which actually delivers mail (the stuff that happens before
+ _Alpine_ is involved) for you is in a better position to do mail filtering
+ than _Alpine_ itself. If possible, you may want to look into using that sort
+ of mail filtering to deliver mail to different folders, delete it, or
+ forward it. However, if you'd like _Alpine_ to help with this, _Alpine_'s
+ filtering is for you.
+
+ Filtering is a way to automatically move certain messages from one folder to
+ another or to delete messages. It can also be used to set message status
+ bits (Important, Deleted, New, Answered). _Alpine_ doesn't have the ability
+ to forward mail to another address.
+
+ Each filtering rule has a "Pattern" and a "Filter Action". When a folder is
+ opened, when new mail arrives in an open folder, or when mail is Expunged
+ from a folder; each message is compared with the Patterns of your filtering
+ rules. The comparisons start with the first rule and keep going until there
+ is a match. If a match is found, the message may be deleted or moved,
+ depending on the setting of the Filter Action. If the message is not
+ deleted, it may have its status altered.
+
+ For efficiency, each message is usually only checked once. When new mail
+ arrives, the new messages are checked but not the old. There are some
+ exceptions to this rule. The expunge command will cause all messages to be
+ rechecked, as will editing of the filtering rules.
+
+ _NOTE:_ When setting up a Pattern used to delete messages, it is recommended
+ that you test the Pattern first with a "Move" folder specified in case
+ unintended matches occur. Messages that are deleted will be removed from the
+ folder and _unrecoverable_ from within _Alpine_ after the next Expunge
+ command or once the folder being filtered has been closed.
+
+ Filter Patterns
+
+ In order to determine whether or not a message matches a filter the message
+ is compared with the Filter's Pattern. These Patterns are the same for use
+ with Roles, Filtering, Index Coloring, Scoring, Other Rules, and Search
+ Rules, so are described in only one place, "here".
+
+ Since filtering is a potentially destructive action, if you have a filtering
+ Pattern with nothing other than Current Folder Type set, that filtering rule
+ is ignored.
+
+ Filter Actions
+
+ Once a filter match is found for a particular message, there are some
+ actions which may be taken. First, the message may have its status changed.
+ This is the same message status that you can manipulate manually using the
+ Flag Command. There are four elements of message status that you can
+ control. You can set or clear the Important status, the New status, the
+ Deleted status, and the Answered status. Of course, if the filter is going
+ to delete the message, then there is no point in setting message status. You
+ may also set or clear user-defined keywords for a message.
+
+ Second, the filter may delete or move the message. Deleting the message
+ marks it Deleted and removes it from view. It is effectively gone forever
+ (though it technically is still there until the next expunge command, which
+ may happen implicitly). Moving the message moves it from the open folder
+ into the folder listed on the "Folder List" line of the filter
+ configuration. If you list more than one folder name (separated by commas)
+ then the message will be copied to each of those folders. In any case, if
+ "Delete" or "Move" is set then the message is removed from the current
+ folder. If you just want to set the messages status without deleting it from
+ the folder, then set the filter action to "Just Set Message Status".
+
+ (There is no way to do a Copy instead of a Move, due to the difficulties
+ involved in keeping track of whether or not a message has already been
+ copied by a previous _Alpine_ session.)
+
+ Move-only-if-not-deleted option
+
+ If you have specified a Move to Folder to filter messages into, then this
+ option has an effect. If this option is set then messages will only be moved
+ into the specified folder if they aren't already marked deleted. This might
+ be useful if you have more than one _Alpine_ session running simultaneously
+ and you don't want messages to be filtered into a folder more than once.
+ This method is not foolproof. There may be cases where a message gets marked
+ deleted and so it is never filtered into the folder. For example, if you
+ deleted it in another _Alpine_ or another mail program that didn't know
+ about the filtering rule.
+
+ This option has no effect if the Filter Action is not set to Move.
+
+ Dont-quit-even-if-rule-matches option
+
+ If this option is set then this is a non-terminating rule. Usually, for each
+ message, _Alpine_ searches through the filter rules until a match is found
+ and then it performs the action associated with that rule. Rules following
+ the match are not considered. If this option is set then the search for
+ matches will continue at the next rule.
+
+ If a non-terminating rule matches then the actions associated with that
+ rule, except for any implied deletion of the message, are performed before
+ the match for the next rule is checked. For example, if the non-terminating
+ rule sets the Important status, then that status will be set when the next
+ rule is considered. However, if the non-terminating rule Moves the message,
+ the message will actually be copied instead of copied and deleted so that it
+ is still there for the next rule. A moved message is deleted after all the
+ relevant rules have been checked. The name of the "Move" action is confusing
+ in this case because a single message can be moved to more than one folder.
+ It turns the Move into a Copy instead, but it is still followed by a
+ deletion at the end.
+
+ This option may be useful if you want to have a single message filtered to
+ two different folders because it matches two different Patterns. For
+ example, suppose you normally filter messages to a particular mailing list
+ into one folder, and messages addressed directly to you into a second
+ folder. If a message is sent to both you and the list (and you can tell that
+ by looking at the headers of the message) this option may give you a
+ convenient way to capture a copy to each folder. (It may also cause you to
+ capture two copies to each folder, depending on whether your mail system
+ delivers one or two copies of the message to you and on how the list works.)
+
+Scoring Configuration
+
+ Most people will not use scores at all, but if you do use them, here's how
+ they work in Alpine. Using this screen, you may define Scoring rules. The
+ score for a message is calculated by looking at every Score rule defined and
+ adding up the Score Values for the ones which match the message. If there
+ are no matches for a message, it has a score of zero. Message scores may be
+ used a couple of ways in Alpine.
+
+ Sorting by Score
+
+ One of the methods you may use to sort message indexes is to sort by score.
+ The scores of all the messages in a folder will be calculated and then the
+ index will be ordered by placing the messages in order of ascending or
+ descending score.
+
+ Scores for use in Patterns
+
+ The Patterns used for Roles, Index Line Coloring, and Filtering have a
+ category labeled "Score Interval". When a message is being compared with a
+ Pattern to check for a match, if the Score Interval is set only messages
+ which have a score somewhere in the interval are a match.
+
+ Scoring Rule Patterns
+
+ In order to determine whether or not a message matches a scoring rule the
+ message is compared with the rule's Pattern. These Patterns are the same for
+ use with Roles, Filtering, Index Coloring, Scoring, Other Rules, and Search
+ Rules, so are described in only one place, "here".
+
+ Actually, Scoring rule Patterns are slightly different from the other types
+ of Patterns because Scoring rule Patterns don't contain a Score Interval. In
+ other words, when calculating the score for a message, which is done by
+ looking at the Scoring rule Patterns, scores aren't used.
+
+ Score Value
+
+ This is the value that will be added to the score for a message if the
+ rule's Pattern is a match. Each individual Score Value is an integer between
+ -100 and 100, and the values from matching rules are added together to get a
+ message's score. There is also a way to extract the value from a particular
+ header of each message. See the help text for Score Value for further
+ information.
+
+Other Rules Configuration
+
+ Using this screen, you may define configuration Rules which don't fit nicely
+ into the other Rules categories.
+
+ Other Rule Patterns
+
+ Other Rules are a little different from the rest of the Rules because they
+ depend only on the current folder, and not on a particular message. In order
+ to determine whether or not a rule's actions should be applied the current
+ folder is compared with the rule's Pattern, which consists of only the
+ Current Folder Type. Current Folder Type works the same for Other Rules as
+ it does for Roles, Filtering, Index Coloring, and Scoring. Keep in mind that
+ the only part of the Pattern which applies to Other Rules is the Current
+ Folder Type when looking at the description of Patterns given "here".
+
+ Other Rule Actions
+
+ Once a pattern match is found, the rule's Actions are taken. Neither of the
+ following two rule's depends on a message for its match. That means that all
+ the parts of the Pattern which depend on matching an attribute of a message
+ are ignored. So the only part of the Pattern that matters for these Actions
+ is the Current Folder Type.
+
+ Set Sort Order
+
+ When you enter a new folder, these rules will be checked to see if you have
+ set a sort order which is different from your default sort order. The
+ default is set in the Setup/Config screen with the Sort-Key option. If the
+ Sort Order action is set, then the folder will be displayed sorted in that
+ sort order instead of in the default order.
+
+ A possible point of confusion arises when you change the configuration of
+ the Sort Order for the currently open folder. The folder will normally be
+ re-sorted when you go back to viewing the index. However, if you have
+ manually sorted the folder with the Sort command, it will not be re-sorted.
+
+ Set Index Format
+
+ When you enter a new folder, these rules will be checked to see if you have
+ set an Index Format which is different from your default Index Format, which
+ is set with the Index-Format option. If so, the index will be displayed with
+ this format instead of the default.
+
+ Set Startup Rule
+
+ When you enter a new folder, these rules will be checked to see if you have
+ set a startup rule which is different from the default startup rule. The
+ default for incoming folders is set in the Setup/Config screen with the
+ "incoming-startup-rule" option. The default for folders other than INBOX
+ that are not part of your incoming collection (see enable-incoming-folders
+ feature) is to start with the last message in the folder. If the Startup
+ Rule is set to something other than "default", then the rule will determine
+ which message will be the current message when the folder is first opened.
+
+ The various startup rule possibilities work the same here as they do in the
+ incoming collection, except that the folder can be any specific folder or
+ any folder type.
+
+Search Rules Configuration
+
+ One of the commands that becomes available when that feature is turned on is
+ the "; Select" command, which is used in the MESSAGE INDEX screen to select
+ a set of messages. One way of selecting messages is to use a Rule. All of
+ the messages which match (or don't match if you wish) a Rule's Pattern will
+ be selected.
+
+ Any of your Rules may be used for this purpose. You might already have Rules
+ set up for filtering, index line color, scores, or roles; and you may use
+ any of those Rules with the Select command. However, you might find it more
+ convenient to set up a separate set of Rules just for this purpose without
+ having to worry about what other effects they may cause. That is the purpose
+ of these Select Rules.
+
+ Rule Patterns
+
+ In order to determine whether or not a message is selected by a rule the
+ message is compared with the rule's Pattern. These Patterns are the same for
+ use with Roles, Filtering, Index Coloring, Scoring, Other Rules, and Search
+ Rules, so are described in only one place, "here".
+
+ There is no action associated with these Search Rules. Only their Patterns
+ are used.
+
+Patterns
+
+ Patterns are used with Roles, Filtering, Index Coloring, Scoring, Other
+ Rules, and Search Rules. Patterns are compared with a message to see if
+ there is a match. For Filtering, the messages being checked are all the
+ messages in the folder, one at a time. For Index Line Coloring, each message
+ that is visible on the screen is checked for matches with the Index Coloring
+ Patterns. Roles are used with the Reply, Forward, and Compose commands. For
+ Reply, the message used to compare the Pattern with is the message being
+ replied to; for Forward, the message used to compare the Pattern with is the
+ message being forwarded; and for Compose, there is no message, so the parts
+ of the Pattern which depend on a message (everything other than Current
+ Folder Type and the Beginning of Month and Year) are not used. Only the
+ Current Folder Type matters for Compose (plus the Beginning of Month or
+ Year, which you wouldn't usually use for a Role). For Scoring, the message
+ being scored is compared with all of the Score Patterns, and the Score
+ Values from the ones that match are added together to get the message's
+ score. For Other Rules, there is no message. Only the Current Folder Type is
+ checked for Other Rules.
+
+ Each Pattern has several possible parts, all of which are optional. In order
+ for there to be a match, _ALL_ of the _defined_ parts of the Pattern must
+ match the message. If a part is not defined it is considered a match. For
+ example, if the To pattern is not defined it will be displayed as
+
+ To pattern = <No Value Set>
+
+ That is considered a match because it is not defined. This means that the
+ Pattern with nothing defined is a match if the Current Folder Type matches,
+ but there is an exception. Because filtering is a potentially destructive
+ action, filtering Patterns with nothing other than Current Folder Type
+ defined are ignored. If you really want a filtering Pattern to match all
+ messages (subject to Current Folder Type) the best way to do it is to define
+ a Score interval which includes all possible scores. This would be the score
+ interval (-INF,INF). This can be used even if you haven't defined any rules
+ to Set Scores.
+
+ There are six predefined header patterns called the To, From, Sender, Cc,
+ News, and Subject patterns. Besides those six predefined header patterns,
+ you may add additional header patterns with header fieldnames of your
+ choosing. You add an extra header pattern by placing the cursor on one of
+ the patterns while in the role editor and using the "eXtraHdr" command. The
+ Recip pattern is a header pattern which stands for Recipient (To OR Cc) and
+ the Partic pattern is a header pattern which stands for Participant (From OR
+ To OR Cc). (Defining the Recip pattern does not have the same effect as
+ defining both the To and Cc patterns. Recip is To _OR_ Cc, not To _AND_ Cc.)
+ Similar to the header patterns are the AllText pattern and the BodyText
+ pattern. Instead of comparing this pattern's text against only the contents
+ of a particular header field, the text for the AllText pattern is compared
+ with text anywhere in the message's header or body, and the text for the
+ BodyText pattern is compared with text anywhere in the message's body.
+
+ Any of the header patterns, the AllText pattern, or the BodyText pattern may
+ be negated with the "!" "toggle NOT" command. You can tell that _NOT_ has
+ been turned on by looking for the character "!" at the beginning of the
+ pattern line. When the "!" is present, it reverses the meaning of the match.
+ That is, if the pattern matches then it is considered to NOT be a match, and
+ if it does not match it is considered to be a match.
+
+ Don't make the mistake of putting the "!" in the data field for a pattern.
+ For example, if you type the characters "!urgent" into the Subject pattern,
+ the pattern will look like:
+
+ Subject pattern = !urgent
+
+ This means you want to match the 7 character sequence "!urgent". In order to
+ match messages which do not have "urgent" in their Subject field, first type
+ the characters "urgent" followed by carriage return for the value of the
+ Subject pattern, then negate it by typing the "!" command. It should look
+ like
+
+ ! Subject pattern = urgent
+
+ The contents of each of these header patterns (or the AllText or BodyText
+ patterns) may be a complete email address, part of an address, or a random
+ set of characters to match against. It may also be a list of such patterns,
+ which means you are looking for a match against the first pattern in the
+ list _OR_ the second pattern _OR_ the third and so on. For example, a
+ Subject pattern equal to
+
+ Subject pattern = urgent
+ emergency
+ alert
+
+ would match all messages with a subject which contained at least one of
+ those words. It would also match subjects containing the words "alerts" or
+ "Urgently".
+
+ The same example with "NOT" turned on would be
+
+ ! Subject pattern = urgent
+ emergency
+ alert
+
+ which would match all messages with a subject which did NOT contain any of
+ those words. You can use the "Add Value" command to add new words to the
+ list, or you can enter them as a comma-separated list.
+
+ (It is not possible to specify two patterns which must _BOTH_ be present for
+ a match. It is only possible to specify that _EITHER_ pattern1 _OR_ pattern2
+ must be present, and that is exactly what using a list does.)
+
+ The "Current Folder Type" and the "Score Interval" are also part of the
+ Pattern, although the "Score Interval" is not used when checking for matches
+ for Scoring. There are five similar settings which relate to the status of
+ the message. These settings rely on the message being New or not, Deleted or
+ not, Answered or not, Important or not, and Recent or not. There are also
+ some other miscellaneous settings. The first is the Age of the message in
+ days. Another is the Size of the message in bytes. The third is a setting
+ which detects whether or not the Subject of a message contains raw 8-bit
+ characters (unencoded characters with the most significant bit set). There
+ is a setting which detects whether or not this is the first time _Alpine_
+ has been run this month (doesn't depend on individual messages), and another
+ which detects whether or not this is the first time _Alpine_ has been run
+ this year. Other parts of the Pattern detect whether or not the From address
+ of a message appears in your address book, whether or not certain keywords
+ are set for a message, and whether or not certain character sets are used in
+ a message.
+
+ Parts of a Pattern
+
+ Header patterns
+
+ A header pattern is simply text which is searched for in the corresponding
+ header field. For example, if a Pattern has a From header pattern with the
+ value "@company.com", then only messages which have a From header which
+ contains the text "@company.com" will be possible matches. Matches don't
+ have to be exact. For example, if the relevant field of a message contains
+ the text "mailbox@domain" somewhere in it, then header patterns of "box", or
+ "x@d", or "mailbox@domain" are all matches.
+
+ All parts of the Pattern must match so, for example, if a message matches a
+ defined From pattern, it still must be checked against the other parts of
+ the Pattern which have been defined. The To header pattern is a slightly
+ special case. If the message being checked has a Resent-To header and the
+ feature Use-Resent-To-in-Rules is turned on, the addresses there are used in
+ place of the addresses in the To header. This is only true for the To
+ header. Resent-cc and Resent-From headers are never used unless you add them
+ with the eXtraHdrs command.
+
+ The meaning of a header pattern may be negated with the "!" "toggle NOT"
+ command. You can tell that _NOT_ has been turned on by looking for the
+ character "!" at the beginning of the pattern line. It would look something
+ like
+
+ ! From pattern = susan@example.com
+
+ When the "!" is present, it reverses the meaning of the match.
+
+ If you want to check for the presence of a header field but don't care about
+ its value, then the empty pattern which you get by entering a pair of double
+ quotes ("") should match any message which has the corresponding header
+ field.
+
+ AllText patterns
+
+ AllText patterns are just like header patterns except that the text is
+ searched for anywhere in the message's headers or body, not just in the
+ contents of a particular header field.
+
+ BodyText patterns
+
+ BodyText patterns are just like header patterns except that the text is
+ searched for anywhere in the message's body, not just in the contents of a
+ particular header field.
+
+ If there is more than one header pattern or AllText pattern or BodyText
+ pattern for which you want to take the same action there is a shorthand
+ notation which may be used. Any of these patterns may be a list of patterns
+ instead of just a single pattern. If any one of the patterns in the list
+ matches the message then it is considered a match. For example, if
+ "company1" and "company2" both required you to use the same role when
+ replying to messages, you might have a To pattern which looks like
+
+ To pattern = company1.com
+ company2.com
+
+ This means that if the mail you are replying to was addressed to either
+ "anything@company1.com" or "anything@company2.com", then this Pattern is a
+ match and the same actions will be taken.
+
+ The meaning of an AllText or BodyText pattern may be negated with the "!"
+ "toggle NOT" command. You can tell that _NOT_ has been turned on by looking
+ for the character "!" at the beginning of the pattern line. When the "!" is
+ present, it reverses the meaning of the match.
+
+ A technicality: Since comma is the character used to separate multiple
+ values in any of the fields which may have multiple values (such as header
+ patterns, AllText patterns, BodyText patterns, keywords, folder lists, and
+ so on), you must escape comma with a backslash (\) if you want to include a
+ literal comma in one of those fields. In other words, if you type a
+ backslash followed by a comma it will be interpreted as a comma by _Alpine_,
+ instead of as a separator between pattern values. All other backslashes
+ (those not followed by a comma) are literal backslashes and should not be
+ escaped. It's unlikely you'll ever need to enter a literal comma or
+ backslash in any of the patterns.
+
+ Current Folder Type
+
+ The "Current Folder Type" may be set to one of four different values: "Any",
+ "News", "Email", or "Specific". If the value is set to "News", then the
+ Pattern will only match if the currently open folder is a newsgroup. The
+ value "Email" only matches if the current folder is not news and the value
+ "Any" causes any folder to match. If the value of "Current Folder Type" is
+ set to "Specific", then you must fill in a value for "Folder", which is on
+ the line below the "Specific" line. In this case you will only get a match
+ if the currently open folder is the specific folder you list. You may give a
+ list of folders instead of just a single folder name, in which case the
+ Pattern will match if the open folder is any one of the folders in the list.
+ The name of each folder in the list may be either "INBOX", the technical
+ specification of the folder (like what appears in your configuration file)
+ or, if the folder is one of your incoming folders, it may be the nickname
+ you've given the folder. Here are some samples of specific folder names:
+
+ {monet.art.example.com}mail/art-class
+
+ {news.example.com/nntp}#news.comp.mail.pine
+
+ mail/local-folder
+
+ The easiest way to fill in the "Folder" field is to use the "T" command
+ which is available when the "Folder" line is hilighted, or to use the "Take"
+ command with the configuration feature "enable-rules-under-take" turned on.
+
+ When reading a newsgroup, there may be a performance penalty incurred when
+ collecting the information necessary to check whether or not a Pattern
+ matches a message. For this reason, the default Current Folder Type is set
+ to "Email". If you have Patterns with a Current Folder Type of either "Any"
+ or "News" and those Patterns are used for Index Line Coloring or Scoring,
+ you may experience slower screen redrawing in the MESSAGE INDEX screen when
+ in a newsgroup.
+
+ Age Interval
+
+ The "Age Interval" may be set to an interval of message ages which should be
+ considered a match. Like the other parts of the Pattern, if it is unset it
+ will be ignored. The Age Interval looks like
+
+ (min_age,max_age)
+
+ where "min_age" and "max_age" are integers greater than or equal to zero.
+ The special value "INF" may be used for the max value. It represents
+ infinity.
+
+ Actually, this option may be defined as a list of intervals instead of just
+ a single interval. The list is separated by commas. It can look like
+
+ (min_age1,max_age1),(min_age2,max_age2),...
+
+ When there is an Age Interval defined, it is a match if the age, in days, of
+ the message is contained in any of the intervals. The intervals include both
+ endpoints.
+
+ Even though this option is called Age, it isn't actually the _age_ of the
+ message. Instead, it is how many days ago the message arrived in one of your
+ folders. If the current time is a little past midnight, then a message that
+ arrived just before midnight arrived yesterday, even though the message is
+ only a few minutes old. By default, the date being used is not the date in
+ the Date header of the message. It is the date that the message arrived in
+ one of your folders. When you Save a message from one folder to another that
+ arrival date is preserved. If you would like to use the date in the Date
+ header that is possible. Turn on the option _use-date-header-for-age_ near
+ the bottom of the rule definition.
+
+ A value of 0 is today, 1 is yesterday, 2 is the day before yesterday, and so
+ on.
+
+ Size Interval
+
+ The "Size Interval" may be set to an interval of message sizes which should
+ be considered a match. Like the other parts of the Pattern, if it is unset
+ it will be ignored. The Size Interval looks like
+
+ (min_size,max_size)
+
+ where "min_size" and "max_size" are integers greater than or equal to zero.
+ The special value "INF" may be used for the max value. It represents
+ infinity.
+
+ Actually, this option may be defined as a list of intervals instead of just
+ a single interval. The list is separated by commas. It can look like
+
+ (min_size1,max_size1),(min_size2,max_size2),...
+
+ When there is a Size Interval defined, it is a match if the size, in bytes,
+ of the message is contained in any of the intervals. The intervals include
+ both endpoints.
+
+ Score Interval
+
+ The "Score Interval" may be set to an interval of message scores which
+ should be considered a match. Like the other parts of the Pattern, if it is
+ unset it will be ignored. The Score Interval looks like
+
+ (min_score,max_score)
+
+ where "min_score" and "max_score" are integers between -32000 and 32000. The
+ special values "-INF" and "INF" may be used for the min and max values to
+ represent negative and positive infinity.
+
+ Actually, a list of intervals may be used if you wish. A list would look
+ like
+
+ (min_score1,max_score1),(min_score2,max_score2),...
+
+ When there is a Score Interval defined, it is a match if the score for the
+ message is contained in any of the intervals in the list. The intervals
+ include the endpoints. The score for a message is calculated by looking at
+ every Score rule defined and adding up the Score Values for the ones which
+ match the message. When deciding whether or not a Pattern matches a message
+ for purposes of calculating the score, the Score Interval is ignored.
+
+ Message Status
+
+ There are five separate message status settings. By default, all five are
+ set to the value "Don't care", which will match any message. The value "Yes"
+ means that the particular status must be true for a match, and the value
+ "No" means that the particular status must not be true for a match. For
+ example, one of the five Message Status settings is whether a message is
+ marked Important or not. A "Yes" means that the message must be Important to
+ be considered a match and "No" means that the message must not be Important
+ to be considered a match. The same is true of the other four message status
+ settings which depend on whether or not the message is New; whether the
+ message has been Answered or not; whether the message has been Deleted or
+ not, and whether the message is Recent or not.
+
+ The nomenclature for New and Recent is a bit confusing:
+
+ New means that the message is Unseen. It could have been in your mailbox for
+ a long time but if you haven't looked at it, it is still considered New.
+ That matches the default _Alpine_ index display that shows an N for such a
+ message.
+
+ Recent means that the message was added to this folder since the last time
+ you opened the folder. _Alpine_ also shows an N by default for these types
+ of messages. If you were to run two copies of _Alpine_ that opened a folder
+ one right after the other, a message would only show up as Recent in (at
+ most) the first _Alpine_ session.
+
+ Message Keywords
+
+ Keywords are similar to Message Status, but they are chosen by the user.
+ Provided the mail server allows for it, you may add a set of possible
+ keywords to a folder and then you may set those keywords or not for each
+ message in the folder. The syntax of this part of the Pattern is similar to
+ the header patterns. It is a list of keywords. The Keyword part of the
+ Pattern is a match if the message has any of the keywords in the list set.
+ Like other parts of the Pattern, if this is unset it will be ignored.
+
+ Message Character Set
+
+ A message may use one or more character sets. This part of the Pattern
+ matches messages which make use of one or more of the character sets
+ specified in the pattern. It will be considered a match if a message uses
+ any of the character sets in the list you give here. The syntax of this part
+ of the Pattern is similar to the header patterns and the Message Keywords
+ pattern. It is a list of character sets.
+
+ Besides actual character set names (for example, ISO-8859-7, KOI8-R, or
+ GB2312) you may also use some shorthand names that _Alpine_ provides. These
+ names are more understandable shorthand names for sets of character set
+ names. Two examples are "Cyrillic" and "Greek". Selecting one of these
+ shorthand names is equivalent to selecting all of the character sets that
+ make up the set. You can see all of these shorthand names and the lists of
+ character sets they stand for by typing the "T" command with the Character
+ Set pattern highlighted. The Character Set part of the Pattern is a match if
+ the message uses any of the character sets in the list. Like other parts of
+ the Pattern, if this is unset it will be ignored.
+
+ Raw 8-bit in Subject
+
+ It seems that lots of unwanted email contains unencoded 8-bit characters in
+ the Subject. Normally, characters with the 8th bit set are not allowed in
+ the Subject header unless they are MIME-encoded. This option gives you a way
+ to match messages which have Subjects which contain unencoded 8-bit
+ characters. Setting this option will affect performance in large folders
+ because the subject of each message in the folder has to be checked.
+
+ Beginning of Month
+
+ This option gives you a way to take some action once per month. The value
+ "Yes" means that this must be the first time _Alpine_ has been run this
+ month in order to count as a match,
+
+ Beginning of Year
+
+ This option gives you a way to take some action once per year. The value
+ "Yes" means that this must be the first time _Alpine_ has been run this year
+ in order to count as a match,
+
+ From or Reply-To address in Address Books
+
+ This option gives you a way to match messages which have a From or a
+ Reply-To address which is in one of your address books. Only the simple
+ entries in your address books are searched. Address book distribution lists
+ are ignored! Setting this option will affect performance in large folders
+ because the From and Reply-To of each message in the folder have to be
+ checked.
+
+ Categorizer Command
+
+ This is a command that is run with its standard input set to the message
+ being checked and its standard output discarded. The full directory path
+ should be specified. The command will be run and then its exit status will
+ be checked against the Exit Status Interval, which defaults to just the
+ value zero. If the exit status of the command falls in the interval, it is
+ considered a match, otherwise it is not a match.
+
+ This option may actually be a list of commands. The first one that exists
+ and is executable is used. That makes it possible to use the same
+ configuration with Unix _Alpine_ and _PC-Alpine_.
+
+ If none of the commands in the list exists and is executable then the rule
+ is _not_ a match. If it is possible that the command may not exist, you
+ should be careful to structure your rules so that nothing destructive
+ happens when the command does not exist. For example, you might have a
+ filter that filters away spam when there is a match but does nothing when
+ there is not a match. That would continue to work correctly if the command
+ didn't exist. However, if you have a filter which filters away spam when
+ there is not a match and keeps it when there is a match, that would filter
+ everything if the categorizer command didn't exist.
+
+ Help Configuring Pattern Fields
+
+ _Nickname_
+ This is a nickname to help you. You should have a different nickname
+ for each role you define. The nickname will be used in the SETUP ROLE
+ RULES screen to allow you to pick a role to edit. It will also be
+ used when you send a message to let you know you are sending with a
+ different role than you use by default, and it will be useful for
+ choosing a role when composing with the Role command or when
+ composing with one of the Role Uses set to With Confirmation. This
+ field is not used in the outgoing message.
+ _Comment_
+ This is a comment to help you. This comment does not play any
+ functional role, it is simply an optional comment to help you
+ remember what the rule is for.
+ _To pattern_
+ If this pattern is non-blank, then for this role to be considered a
+ match, at least one of the recipients from the To line of the message
+ being replied to or forwarded must match this pattern. In the case of
+ the Compose command, this pattern and the other header patterns are
+ ignored. If this pattern is a list of patterns, then at least one of
+ the recipients must match at least one of the patterns. (Any other
+ non-blank parts of the Pattern must match, too.) If the message being
+ replied to or forwarded has a Resent-To header line, then that is
+ used in place of the To line. (Note that this special Resent rule
+ only applies to the To header. The Resent-From, Resent-Subject, and
+ so on are not consulted.)
+ It is possible to add a _NOT_ to the To Pattern meaning with the "!"
+ "toggle NOT" command. This changes the meaning of the To pattern so
+ that it has the opposite meaning. It will be considered a match if
+ there are no matches between the addresses in the To: line and the
+ list of To patterns.
+ Don't make the mistake of putting the "!" in the data field for the
+ pattern. For example, if you type the characters "!frizzle" into the
+ To pattern, the pattern will look like:
+ To pattern = !frizzle
+ This means you want to match the 8 character sequence "!frizzle". In
+ order to match messages which do not have "frizzle" in their To
+ field, first type the characters "frizzle" followed by carriage
+ return for the value of the To pattern, then negate it by typing the
+ "!" command. It should end up looking like
+ ! To pattern = frizzle
+ _From pattern_
+ This is just like the To pattern except that it is compared with the
+ address from the From header of the message being replied to or
+ forwarded instead of the addresses from the To header.
+ _Sender pattern_
+ This is just like the To pattern except that it is compared with the
+ address from the Sender header of the message being replied to or
+ forwarded instead of the addresses from the To header. If there is no
+ Sender header, then the From header is used instead.
+ _Cc pattern_
+ This is just like the To pattern except that it is compared with the
+ address from the CC header of the message being replied to or
+ forwarded instead of the addresses from the To header.
+ _News pattern_
+ If this pattern is non-blank, then for this role to be considered a
+ match, at least one of the newsgroups from the Newsgroups line of the
+ message must match this pattern. If this pattern is a list of
+ patterns, then at least one of the newsgroups must match at least one
+ of the patterns. (Any other non-blank parts of the Pattern must
+ match, too.)
+ _Subject pattern_
+ This is similar to the other header patterns. It is compared with the
+ contents from the Subject of the message being replied to or
+ forwarded.
+ If you enter non-ascii characters in this field then the search will
+ be done using the character set you have defined with the
+ Character-Set configuration variable. (The truly sophisticated may
+ use an alternate character set for a search by entering the MIME
+ encoding of the header string here.)
+ _Extra header patterns_
+ There isn't actually a field called Extra header patterns, but you
+ may add extra header patterns by moving the cursor to one of the
+ header patterns and using the "eXtraHdr" command to add a new header
+ pattern. You would do this if the six predefined header patterns
+ don't cover the header you want to use for pattern matching. Once
+ you've added an extra header pattern, you use it just like the
+ Subject pattern. Of course, it is compared with the contents from the
+ particular header field of the message being replied to or forwarded
+ rather than the contents from the subject field. To remove an extra
+ header pattern from a role, use the &quotRemoveHdr" command on the
+ highlighted extra header.
+ If you enter non-ascii characters in this field then the search will
+ be done using the character set you have defined with the
+ Character-Set configuration variable. (The truly sophisticated may
+ use an alternate character set for a search by entering the MIME
+ encoding of the header string here.)
+ _Recipient pattern_
+ This is just like the To pattern except that it is compared with the
+ addresses from both the To header and the Cc header instead of just
+ the addresses from the To header. It's equivalent to having two
+ different rules; one with a To pattern and the other with the same Cc
+ pattern.
+ _Participant pattern_
+ This is just like the To pattern except that it is compared with the
+ addresses from the To header, the Cc header, and the From header
+ instead of just the addresses from the To header. It's equivalent to
+ having three different rules; one with a To pattern, another with the
+ same Cc pattern, and another with the same From pattern.
+ _AllText pattern_
+ This is similar to the header patterns. Instead of comparing with
+ text in a particular header field it is compared with all of the text
+ in the message header and body.
+ If you enter non-ascii characters in this field then the search will
+ be done using the character set you have defined with the
+ Character-Set configuration variable. (The truly sophisticated may
+ use an alternate character set for a search by entering the MIME
+ encoding of the header string here.)
+ _BodyText pattern_
+ Just like AllText, except it is compared only with the body of the
+ message, not the body and header.
+ If you enter non-ascii characters in this field then the search will
+ be done using the character set you have defined with the
+ Character-Set configuration variable. (The truly sophisticated may
+ use an alternate character set for a search by entering the MIME
+ encoding of the header string here.)
+ _Age Interval_
+ The Age Interval, if defined, is part of the Pattern. If you use
+ this, it should be set to something like:
+
+ (min_age,max_age)
+ where "min_age" and "max_age" are non-negative integers. The special
+ value "INF" may be used for the max value. It represents infinity.
+ In rare cases it may be useful to use the more general form of the
+ value, which is a comma-separated list of intervals. It would look
+ something like:
+
+ (min_age1,max_age1),(min_age2,max_age2),...
+ When there is an Age Interval defined, it is a match if the age, in
+ days, of the message is contained in the interval. The interval
+ includes both endpoints. If the option is set to a list of intervals
+ then it is a match if the age of the message is contained in any of
+ the intervals.
+ Even though this option is called Age, it isn't actually the _age_ of
+ the message. Instead, it is how many days ago the message arrived in
+ one of your folders. If the current time is a little past midnight,
+ then a message that arrived just before midnight arrived yesterday,
+ even though the message is only a few minutes old. By default, the
+ date being used is not the date in the Date header of the message. It
+ is the date that the message arrived in one of your folders. When you
+ Save a message from one folder to another that arrival date is
+ preserved. If you would like to use the date in the Date header that
+ is possible. Turn on the option _use-date-header-for-age_ near the
+ bottom of the rule definition.
+ A value of 0 is today, 1 is yesterday, 2 is the day before yesterday,
+ and so on. The age interval
+
+ (2,2)
+ matches all messages that arrived on the day before yesterday. The
+ interval
+
+ (180,INF)
+ matches all messages that arrived at least 180 days before today. The
+ interval
+
+ (0,1)
+ matches all messages that arrived today or yesterday.
+ _Score Interval_
+ The Score Interval, if defined, is part of the Pattern. If you use
+ this, it should be set to something like:
+
+ (min_score,max_score)
+ where "min_score" and "max_score" are integers between -32000 and
+ 32000. The special values "-INF" and "INF" can be used for the min
+ and max values. These represent negative and positive infinity.
+ Actually, the value may be a list of intervals rather than just a
+ single interval if that is useful. The elements of the list are
+ separated by commas like:
+
+ (min_score1,max_score1),(min_score2,max_score2),...
+ When there is a Score Interval defined, it is a match if the score
+ for the message is contained in any of the intervals. The intervals
+ include both endpoints. The score for a message is calculated by
+ looking at every scoring rule defined and adding up the Score Values
+ for the rules which match the message.
+ _Keyword pattern_
+ A folder may have user-defined keywords. These are similar to the
+ Important flag which the user may set using the Flag command. The
+ difference is that the Important flag is always present for each
+ folder. User-defined keywords are picked by the user. You may add new
+ keywords by defining them in the Keywords option in the Setup/Config
+ screen. After you have added a potential keyword with the Keywords
+ option, the Flag command may be used to set or clear the keyword on
+ individual messages. If you have given a keyword a nickname when
+ configuring it, that nickname may be used instead of the actual
+ keyword.
+ When filling in a value for this field, it may be easiest to use the
+ "T" command, which presents you with a list of the keywords you have
+ defined to choose from.
+ This part of the Pattern matches messages with certain keywords set.
+ It will be considered a match if a message has any of the keywords in
+ the list set.
+ It is possible to add a _NOT_ to the Keyword Pattern meaning with the
+ "!" "toggle NOT" command. This changes the meaning of the Keyword
+ pattern so that it has the opposite meaning. It will be considered a
+ match if none of the keywords in the list are set for a message.
+ Don't make the mistake of putting the "!" in the data field for the
+ pattern. For example, if you type the characters "!frizzle" into the
+ Keyword pattern, the pattern will look like:
+ Keyword pattern = !frizzle
+ This means you want to match the 8 character sequence "!frizzle". In
+ order to match messages which do not have the keyword "frizzle" set,
+ first type the characters "frizzle" followed by carriage return for
+ the value of the Keyword pattern, then negate it by typing the "!"
+ command. It should end up looking like
+ ! Keyword pattern = frizzle
+ _Character Set pattern_
+ A message may use one or more character sets. This part of the
+ Pattern matches messages which make use of certain specified
+ character sets. It will be considered a match if a message uses any
+ of the character sets in the list you give here.
+ When filling in a value for this field, you may use the "T" command,
+ which presents you with a large list of possible character sets to
+ choose from. You may also just type in the name of a character set,
+ and it need not be one that Alpine knows about.
+ Besides actual character set names (for example, ISO-8859-7, KOI8-R,
+ or GB2312) you may also use some shorthand names that Alpine
+ provides. These names are more understandable shorthand names for
+ sets of character set names. Two examples are "Cyrillic" and "Greek".
+ Selecting one of these shorthand names is equivalent to selecting all
+ of the character sets that make up the set. You can see all of these
+ shorthand names and the lists of character sets they stand for by
+ typing the "T" command.
+ For the purposes of this Pattern, _Alpine_ will search through a
+ message for all of the text parts and collect the character sets
+ declared for each part. It will also look in the Subject line for a
+ character set used there. _Alpine_ does not actually look at the text
+ of the message or the text of the Subject to determine if a declared
+ character set is actually used, it looks only at the declarations
+ themselves in the MIME part headers and in the Subject.
+ It is possible to add a _NOT_ to the Character Set Pattern meaning
+ with the "!" "toggle NOT" command. This changes the meaning of the
+ Character Set pattern so that it has the opposite meaning. It will be
+ considered a match if none of the character sets in the list are used
+ in a message.
+ Don't make the mistake of putting the "!" in the data field for the
+ pattern. For example, if you type the characters "!GB2312" into the
+ Character Set pattern, the pattern will look like:
+ Charset pattern = !GB2312
+ This means you want to match the 7 character sequence "!GB2312". In
+ order to match messages which do not have the character set "GB2312"
+ set, first type the characters "GB2312" followed by carriage return
+ for the value of the Character Set pattern, then negate it by typing
+ the "!" command. It should end up looking like
+ ! Charset pattern = GB2312
+ A technicality: Since comma is the character used to separate
+ multiple values in a pattern field, you have to escape comma with a
+ backslash (\) if you want to include a literal comma in the field. In
+ other words, if you type a backslash followed by a comma it will be
+ interpreted as a comma by _Alpine_, instead of as a separator between
+ pattern values. All other backslashes are literal backslashes and
+ should not be escaped.
+ _Current Folder Type_
+ The Current Folder Type is part of the Pattern. It refers to the type
+ of the currently open folder, which is the folder you were last
+ looking at from the MESSAGE INDEX or MESSAGE TEXT screen. In order
+ for a pattern to be considered a match, the current folder must be of
+ the type you set here. The three types "Any", "News", and "Email" are
+ all what you might think.
+ If the Current Folder Type for a Pattern is set to "News", for
+ example, then that will only be a match if the current folder is a
+ newsgroup and the rest of the Pattern matches. The value "Specific"
+ may be used when you want to limit the match to a specific folder
+ (not just a specific type of folder), or to a list of specific
+ folders. In order to match a specific folder you must Select the
+ "Specific" button _AND_ you must fill in the name (or list of names)
+ of the folder in the "Folder" field. If the current folder is any of
+ the folders in the list, that is considered a match. The name of each
+ folder in the list may be either "INBOX", the technical specification
+ of the folder (like what appears in your configuration file) or, if
+ the folder is one of your incoming folders, it may be the nickname
+ you've given the folder. Here are a couple samples of specific folder
+ names:
+
+ {monet.art.example.com}mail/art-class
+
+ {news.example.com/nntp}#news.comp.mail.pine
+ The easiest way to fill in the "Folder" field is to use the T command
+ which is available when the "Folder" line is hilighted. Note that you
+ won't be able to edit the "Folder" line unless the Current Folder
+ Type is set to "Specific", and any value that "Folder" has is ignored
+ unless the type is set to "Specific".
+ When reading a newsgroup, there may be a performance penalty incurred
+ when collecting the information necessary to check a Pattern. For
+ this reason, the default Current Folder Type is set to "Email". For
+ example, a role with a non-Normal Index Line Color and a Current
+ Folder Type of "Any" or "News" may cause the MESSAGE INDEX screen to
+ draw more slowly when in a newsgroup.
+ _Message Status Important_
+ This part of the Pattern may have one of three possible values. The
+ default value is "Don't care", which matches any message. The other
+ two values are "Yes", which means the message must be flagged
+ "Important" in order to be a match; or "No", which means the message
+ must _not_ be flagged "Important" in order to be considered a match.
+ _Message Status New_
+ This part of the Pattern may have one of three possible values. The
+ default value is "Don't care", which matches any message. The other
+ two values are "Yes", which means the message must be "New" in order
+ to be a match; or "No", which means the message must _not_ be "New"
+ in order to be a match. "New" is the same as _Unseen_ and not "New"
+ is the same as _Seen_.
+ The nomenclature for New and Recent is a bit confusing:
+ New means that the message is Unseen. It could have been in your
+ mailbox for a long time but if you haven't looked at it, it is still
+ considered New. That matches the default _Alpine_ index display that
+ shows an N for such a message.
+ Recent means that the message was added to this folder since the last
+ time you opened the folder. _Alpine_ also shows an N by default for
+ these types of messages. If you were to run two copies of _Alpine_
+ that opened a folder one right after the other, a message would only
+ show up as Recent in (at most) the first _Alpine_ session.
+ _Message Status Recent_
+ This part of the Pattern may have one of three possible values. The
+ default value is "Don't care", which matches any message. The other
+ two values are "Yes", which means the message must be "Recent" in
+ order to be a match; or "No", which means the message must _not_ be
+ "Recent" in order to be a match. "Recent" means that the message was
+ added to the folder since the last time the folder was opened. If
+ more than one mail client has the folder opened, the message will
+ appear to be "Recent" to only one of the clients.
+ The nomenclature for New and Recent is a bit confusing:
+ New means that the message is Unseen. It could have been in your
+ mailbox for a long time but if you haven't looked at it, it is still
+ considered New. That matches the default _Alpine_ index display that
+ shows an N for such a message.
+ Recent means that the message was added to this folder since the last
+ time you opened the folder. _Alpine_ also shows an N by default for
+ these types of messages. If you were to run two copies of _Alpine_
+ that opened a folder one right after the other, a message would only
+ show up as Recent in (at most) the first _Alpine_ session.
+ _Message Status Deleted_
+ This part of the Pattern may have one of three possible values. The
+ default value is "Don't care", which matches any message. The other
+ two values are "Yes", which means the message must be marked
+ "Deleted" in order to be a match; or "No", which means the message
+ must _not_ be marked "Deleted" in order to be a match.
+ If you are thinking of using this part of the Pattern as a way to
+ prevent messages from being filtered more than once in a Filter
+ Pattern, take a look at the Filter Option "move-only-if-not-deleted"
+ instead. It should work better than using this field since it will
+ hide the filtered messages even if they are already Deleted.
+ _Message Status Answered_
+ This part of the Pattern may have one of three possible values. The
+ default value is "Don't care", which matches any message. The other
+ two values are "Yes", which means the message must be marked
+ "Answered" in order to be a match; or "No", which means the message
+ must _not_ be marked "Answered" in order to be a match.
+ _Subject Contains Raw 8-bit_
+ This part of the Pattern may have one of three possible values. The
+ default value is "Don't care", which matches any message. The other
+ two values are "Yes", which means the Subject of the message must
+ contain unencoded 8-bit characters (characters with the most
+ significant bit set) in order to be a match; or "No", which means the
+ Subject must _not_ contain unencoded 8-bit characters in order to be
+ a match.
+ _Beginning of Month_
+ This part of the Pattern may have one of three possible values. The
+ default value is "Don't care", which matches any message. The other
+ two values are "Yes", which means this is the first time _Alpine_ has
+ been run this month; or "No", which means this is _not_ the first
+ time _Alpine_ has been run this month. The way that _Alpine_ decides
+ if it is the beginning of the month or not is to compare today's date
+ with the date stored in the Last-Time-Prune-Questioned variable in
+ the config file. If the month of today's date is later than the month
+ stored in the variable, then this is considered to be the first time
+ you have run Alpine this month, and that turns the Beginning of the
+ Month option on.
+ _Beginning of Year_
+ This part of the Pattern may have one of three possible values. The
+ default value is "Don't care", which matches any message. The other
+ two values are "Yes", which means this is the first time _Alpine_ has
+ been run this year; or "No", which means this is _not_ the first time
+ _Alpine_ has been run this year. The way that _Alpine_ decides if it
+ is the beginning of the year or not is to compare today's date with
+ the date stored in the Last-Time-Prune-Questioned variable in the
+ config file. If the year of today's date is later than the year
+ stored in the variable, then this is considered to be the first time
+ you have run Alpine this year, and that turns the Beginning of the
+ Year option on.
+ _From or Reply-To in Address Book_
+ This part of the Pattern may have one of five possible values. The
+ default value is "Don't care", which matches any message. The value
+ "Yes, in any address book" means either the From address or the
+ Reply-To address of the message must be in at least one of your
+ address books in order to be a match. The value "No, not in any
+ address book" means neither the From nor the Reply-To addresses may
+ be in any of your address books in order to be a match.
+ The values "Yes, in specific address books" and "No, not in any of
+ specific address books" are similar but instead of depending on all
+ address books you are allowed to give a list of address books to look
+ in. Usually this would be a single address book but it may be a list
+ of address books as well. For each of these "specific" address book
+ options you Select which of the Specific options you want (Yes or No)
+ _AND_ fill in the name (or list of names) of the address book in the
+ "Abook List" field. The names to be used are those that appear in the
+ ADDRESS BOOK LIST screen. The easiest way to fill in the Abook List
+ field it to use the "T" command which is available when the "Abook
+ List" line is highlighted. Note that you won't be able to edit the
+ "Abook List" line unless the option is set to one of the two
+ "Specific", values.
+ _Categorizer Command_
+ This is a command that is run with its standard input set to the
+ message being checked and its standard output discarded. The full
+ directory path should be specified. The command will be run and then
+ its exit status will be checked against the _Exit Status Interval_,
+ which defaults to just the value zero. If the exit status of the
+ command falls in the interval, it is considered a match, otherwise it
+ is not a match.
+ This option may actually be a list of commands. The first one that
+ exists and is executable is used. That makes it possible to use the
+ same configuration with Unix _Alpine_ and _PC-Alpine_.
+ If none of the commands in the list exists and is executable then the
+ rule is _not_ a match. If it is possible that the command may not
+ exist, you should be careful to structure your rules so that nothing
+ destructive happens when the command does not exist. For example, you
+ might have a filter that filters away spam when there is a match but
+ does nothing when there is not a match. That would continue to work
+ correctly if the command didn't exist. However, if you have a filter
+ which filters away spam when there is not a match and keeps it when
+ there is a match, that would filter everything if the categorizer
+ command didn't exist.
+ The categorizer command is run and the result is the exit status of
+ that command. If that exit status falls in the _Exit Status Interval_
+ then it is considered a match, otherwise it is not a match. Of course
+ for the entire rule to match, it must also be checked against the
+ other defined parts of the Pattern.
+ The _Exit Status Interval_ defaults to the single value 0 (zero). If
+ you define it, it should be set to something like:
+
+ (min_exit_value,max_exit_value)
+ where "min_exit_value" and "max_exit_value" are integers. The special
+ values "INF" and "-INF" may be used for large positive and negative
+ integers.
+ Actually, a list of intervals may be used if you wish. A list would
+ look like
+
+ (min_exit_value1,max_exit_value1),(min_exit_value2,max_exit_value2),...
+ When there is an _Exit Status Interval_ defined, it is a match if the
+ exit status of the categorizer command is contained in any of the
+ intervals. The intervals include both endpoints.
+ The default interval is
+
+ (0,0)
+ and it matches only if the command exits with exit status equal to
+ zero.
+ It is also possible to set a _Character Limit_ for the categorizer
+ command. Setting this option makes it possible to limit how much of
+ the message is made available to the categorizer command as input.
+ The default value (-1) means that the entire message is fed to the
+ command. A value of 0 (zero) means that only the headers of the
+ message are made available. A positive integer means that the headers
+ plus that many characters from the body of the message are passed to
+ the categorizer.
+
+Configuring News
+
+ _Alpine_ can access news folders in any one of three different ways:
+
+ REMOTE NNTP
+ Using the Network News Transport Protocol (NNTP) to access news on a
+ remote news server. In this case the newsrc file is stored on the
+ machine where _Alpine_ is running.
+
+ To specify a remote news-collection accessed via NNTP use the
+ SETUP/collectionList screen's "Add" command. Set the Server: value to
+ the NNTP server's hostname appended with the communication method
+ "/service=NNTP", and set the Path: value to the "#news." namespace
+ (without the quotes).
+
+ Instead of specifying a news-collection, you may simply set the
+ nntp-server option, which will cause _Alpine_ to create a default
+ news-collection for you. Another NNTP option which may be of interest
+ is nntp-range.
+
+ REMOTE IMAP
+ Using the Internet Message Access Protocol (IMAP) to access news on a
+ remote news server. In this case, your newsrc file is stored on the
+ news server, in your home directory, so you must have an account on
+ the news server, but you would be running _Alpine_ on a different
+ machine. The news server must be running an IMAPd server process.
+
+ To specify a remote news-collection accessed via IMAP use the
+ SETUP/collectionList screen's "Add" command. Set the Server: value to
+ the IMAP server's hostname, and set the Path: value to the "#news."
+ namespace (without the quotes).
+
+ LOCAL
+ Using local file access to the news database. In this case, your
+ newsrc file is stored on the news server, in your home directory, so
+ you must have an account on the news server, and you would be running
+ _Alpine_ on the same machine.
+
+ To specify a local news-collection use the SETUP/collectionList
+ screen's "Add" command. Leave the Server: value blank, and set the
+ Path: value to the "#news." namespace (without the quotes).
+
+ NOTE: Should no news-collection be defined as above, _Alpine_ will
+ automatically create one using the Setup/Config screen's "nntp-server"
+ variable's value if defined. The collection will be created as a "Remote
+ NNTP" as described above.
+
+ If you are a _PC-Alpine_ user, either option 1 (NNTP) or option 2 (IMAP) is
+ possible. If you don't have an account on the news server, or if the news
+ server is not running an IMAP daemon, then you must use NNTP. (If you are
+ not sure, ask your service provider, university, or company for help.) In
+ this case, your Unix .newsrc file can be transferred to your PC. A good
+ place to put it would be in the same directory as your PINERC file, under
+ the name NEWSRC, but you can specify a different location.
+
+ Other configuration features related to news are Enable-8bit-Nntp-Posting.
+ Compose-Sets-Newsgroup-Without-Confirm, News-Approximates-New-Status,
+ News-Deletes-Across-Groups, News-Offers-Catchup-On-Close,
+ News-Post-Without-Validation, News-Read-in-Newsrc-Order, and
+ Quell-Extra-Post-Prompt.
+ _________________________________________________________________
+
+ Notes on Configuration and Preferences
+
+Alpine in Function Key Mode
+
+ The standard _Alpine_ uses alphabetic keys for most commands, and control
+ keys in the composer. Despite possible appearances, the current bindings are
+ the result of much discussion and thought. All the commands in the composer
+ are single control characters. This keeps things very neat and simple for
+ users. Two character commands in the composer are a possibility, but we're
+ trying to avoid them because of the added complexity for the user.
+
+ _Alpine_ can also operate in a function-key mode. To go into this mode
+ invoke _alpine -k_ or (on some UNIX systems) _alpinef._ On a UNIX system,
+ you can link or copy the _Alpine_ executable to _alpinef_ to install
+ _alpinef._ Alternatively, users and systems administrators can set the
+ _use-function-keys_ feature in the personal or system-wide _Alpine_
+ configuration file. The command menus at the bottom of the screen will show
+ _F1-F12 _instead of the alphabetic commands. In addition, the help screens
+ will be written in terms of function keys and not alphabetic keys.
+
+ One of the results of using _Alpine_ in function-key mode is that users can
+ only choose from twelve commands at any given time. In alphabetic-key mode,
+ a user can press a key for a command (say, q to quit) and that command can
+ be fulfilled. In function-key mode, the command must be visible on the
+ bottom key-menu in order to be used. There are some screens where four
+ screens of commands are operational; function-key users can get to all of
+ them, just not all at once.
+ _________________________________________________________________
+
+Domain Settings
+
+ _Alpine_ uses the default domain for a few different tasks. First, it is
+ tacked onto the user-id for outgoing email. Second, it is tacked onto all
+ "local" (unqualified) addresses in the "To:" or "Cc:" fields of messages
+ being composed (unless they are found in the address book or on an LDAP
+ server). The domain name is also used to generate message-id lines for each
+ outgoing message and to allow _Alpine_ to check if an address is that of the
+ current _Alpine_ user.
+
+ _Alpine_ determines the domain name according to whichever of these it
+ finds. The list here is in decreasing order of precedence.
+ 1. Value of the variable user-domain in the system fixed configuration file
+ 2. Value of the variable _user-domain_ in the personal configuration file
+ 3. Value of the variable _user-domain_ in the system-wide configuration
+ file
+ 4. Value from an external database (DNS, /etc/hosts, NIS) as modified by a
+ system fixed configuration file if use-only-domain-name set to _yes_
+ 5. Value from an external database (DNS, /etc/hosts, NIS) as modified by a
+ personal configuration file if _use-only-domain-name_ set to _yes_
+ 6. Value from an external database (DNS, /etc/hosts, NIS) as modified by a
+ system configuration file if _use-only-domain-name_ set to _yes_
+ 7. Unmodified value (host name) from an external database
+
+ The easiest way for this system to work is for _PC-Alpine_ users and UNIX
+ _Alpine_ system administrators to set the _user-domain_ variable. The
+ variable _use-only-domain-name_ is helpful if your site supports/requires
+ hostless addressing, but for some reason you don't want to use the
+ _user-domain_ variable.
+ _________________________________________________________________
+
+Syntax for Collections
+
+ In many environments, it is quite common to have collections of archived
+ mail on various hosts around the network. Using the folder collections
+ facility in _Alpine_, access to these archives is just as simple as access
+ to folders on _Alpine_'s local disk.
+
+ "Collection" is the word we use in _Alpine_ to describe a set of folders. A
+ collection corresponds loosely to a "directory" containing mail folders.
+ Folders within a defined collection can be manipulated (opened, saved-to,
+ etc) using just their simple name. Any number of folder collections can be
+ defined, and _Alpine_ will adjust its menus and prompts to help navigate
+ them.
+
+ The way collections are defined in _Alpine_ is with the folder-collections
+ variable in the _Alpine_ configuration file. _Folder-collections_ takes a
+ list of one or more collections, each (optionally) preceded by a
+ user-defined logical name (label). Once collections are defined, _Alpine_
+ adjusts its menus and behavior to allow choosing files by their simple name
+ within the collection.
+
+ Consider the following:
+ folder-collections= Local-Mail C:\MAIL\[],
+ Remote-Mail {imap.u.example.edu}mail/[]
+
+ The example shows two collections defined (a comma separated list; newlines
+ in the list are OK if there's one or more spaces before the next entry), one
+ local and one remote. Each collection is a space-delimited pair of
+ elements-first an optional logical-name and second the collection specifier.
+ The logical-name can have spaces if it has quotes around it (but keeping the
+ logical name short and descriptive works best). _Alpine_ will use the
+ logical-name (if provided) to reference all folders in the collection, so
+ the user never has to see the ugliness of the collection specifier.
+
+ The collection specifier can be thought of as an extended IMAP format (see
+ the Remote Folders section for a description of IMAP format names).
+ Basically, a pair of square-brackets are placed in the fully qualified IMAP
+ path where the simple folder name (the part without the host name and path)
+ would appear. Like IMAP, the path can be either fully qualified (i.e., with
+ a leading '/') or relative to your home directory.
+
+ An advanced feature of this notation is that a pattern within the square
+ brackets allows the user to define a collection to be a subset of a
+ directory. For example, a collection defined with the specifier:
+ M-Mail C:MAIL/[m*]
+
+ will provide a view in the folder lister of all folders in the PC's "C:MAIL"
+ directory that start with the letter 'm' (case insensitive under DOS, of
+ course). Further, the wildcard matching will honor characters trailing the
+ '*' in the pattern.
+
+ From within _Alpine_, the "Folder List" display will be adjusted to allow
+ browsing of the folders in any defined collection. Even more, you'll notice
+ in the _Goto_ and _Save_ commands a pair of sub-commands to rotate through
+ the list of logical collection names, so only a simple name need be input in
+ order to operate on a folder in any collection.
+
+ The first collection specified in the _folder-collections_ has special
+ significance. That folder is the "default collection for saves". By default,
+ in cases where the user does not specify which collection should be used to
+ _Save_ a message, the default collection for saves will be used. Also, if the
+ default-fcc is a relative file name, then it is relative to the default
+ collection for saves. (See also saved-msg-name-rule.
+
+ The notion of collections encompasses both email folders and news reading.
+ The variable news-collections uses nearly the same format as
+ _folder-collections_. Newsgroups can be defined for convenient access via
+ either IMAP or NNTP. There are advantages and disadvantages to both access
+ methods. In the IMAP case, your news environment state is maintained on the
+ server and, thus, will be seen by any client. The downside is that, at the
+ moment, you must have an account on the server. In the NNTP case, server
+ access is mostly anonymous and no state/accounting need be maintained on it.
+ The downside is that each client, for now, must individually maintain news
+ environment state.
+
+ An example pinerc entry might be:
+ news-collections= Remote-State {news.u.example.edu}#news.[],
+ Local-State {news.u.example.edu/nntp}#news.[]
+
+ Only newsgroups to which you are subscribed are included in the collection.
+
+ The pattern matching facility can be applied so as to define a news
+ collection which is a subset of all the newsgroups you subscribe to. For
+ example, this could be a valid collection:
+ Newsfeed-News {news.u.example.edu/nntp}#news.[clari.*
+]
+
+ Collection handling is a tough problem to solve in a general way, and the
+ explanation of the syntax is a bit ugly. The upside is, hopefully, that for
+ a little complexity in the _Alpine_ configuration file you get simple
+ management of multiple folders in diverse locations.
+
+ Collection setup is handled by the _Setup/collectionList_ screen.
+ _________________________________________________________________
+
+Syntax for Folder Names
+
+ Remote folders are distinguished from local folders by a leading host name
+ bracketed by '{' and '}'. The path and folder name immediately following the
+ closing bracket, '}', is interpreted by the remote server and is in a form
+ compatible with that server (i.e., path delimiters and naming syntax
+ relative to that server).
+
+ The full syntax for a _Alpine_ folder name looks like
+
+ [{<remote-specification>}][#<namespace>]<namespace-specific-part>
+
+ The square brackets ([]) mean that the part is optional.
+
+ If there is no remote-specification, then the folder name is interpreted
+ locally on the computer running _Alpine_. Local folder names depend on the
+ operating system used by the computer running _Alpine_, as well as the
+ configuration of that system. For example, "C:\ALPINE\FOLDERS\OCT-94" might
+ exist on a PC, and "~/mail/september-1994" might be a reasonable folder name
+ on a system running Unix.
+
+ _Alpine_ users have the option of using folders which are stored on some
+ other computer. _Alpine_ accesses remote folders via IMAP (the Internet
+ Message Access Protocol), or in the case of news, via NNTP (the Network News
+ Transport Protocol). To be able to access remote folders in _Alpine_, the
+ remote host must be running the appropriate server software (imapd or nntpd)
+ and you must correctly specify the name of the folder to _Alpine_, including
+ the domain name of the remote machine. For example,
+
+ {monet.art.example.com}INBOX
+
+ could be a remote folder specification, and so could
+
+ {unixhost.art.example.com}~/mail/september-1994
+
+ and
+
+ {winhost.art.example.com}\mymail\SEP-94
+
+ Note that in the case of remote folders, the directory/file path in the
+ specification is determined by the operating system of the remote computer,
+ _not_ by the operating system of the computer on which you are running
+ _Alpine_.
+
+ As you can tell, the name of the computer is in {} brackets followed
+ immediately by the name of the folder. (In each of these cases the optional
+ namespace is missing.) If, as in these examples, there is no remote access
+ protocol specified, then IMAP is assumed. Check Server Name Syntax for a
+ more detailed look at what options can be placed between the brackets. If
+ there are no brackets at all, then the folder name is interpreted locally on
+ the computer on which you are running _Alpine_.
+
+ To the right of the brackets when a server name is present, or at the start
+ of the foldername if no server is present, the sharp sign, "#", holds
+ special meaning. It indicates a folder name outside the area reserved for
+ your personal folders. In fact, it's used to indicate both the name of the
+ folder, and a special phrase telling _Alpine_ how to interpret the name that
+ follows.
+
+ So, for example, _Alpine_ can be used to access a newsgroup that might be
+ available on your computer using:
+
+ #news.comp.mail.pine
+
+ The sharp sign indicates the folder name is outside your personal folder
+ area. The "news." phrase after it tells _Alpine_ to interpret the remainder
+ of the name as a newsgroup.
+
+ Similarly, to access a newsgroup on your IMAP server, you might use
+ something like:
+
+ {wharhol.art.example.com}#news.comp.mail.misc
+
+ There are a number of such special phrases (or "namespaces") available. For
+ a more detailed explanation read about Namespaces.
+
+ Note that "INBOX" has special meaning in both local and remote folder names.
+ The name INBOX refers to your "principal incoming message folder" and will
+ be mapped to the actual file name used for your INBOX on any given host.
+ Therefore, a name like "{xxx.art.example.com}INBOX" refers to whatever file
+ is used to store incoming mail for you on that particular host.
+ _________________________________________________________________
+
+Server Name Syntax
+
+ This section describes the syntax which may be used for server names which
+ may be associated with remote folders or SMTP servers.
+
+ A server name is the hostname of the server. It's a good idea to use the
+ host's fully-qualified network name.
+
+ foo.example.com
+
+ However, IP addresses are allowed if surrounded with square-brackets.
+
+ [127.0.0.1]
+
+ An optional network port number may be supplied by appending a colon (:)
+ followed by the port number to the server name. By default, the IMAP port
+ number, 143, is used.
+
+ foo.example.com:port
+
+ Besides server name and optional port number, various other optional
+ parameters may be supplied that alter _Alpine_'s interaction with the
+ server. A parameter is supplied by appending a slash (/) character followed
+ by the parameter's name and, depending on the particular parameter, the
+ value assigned to that name, to the server name (and optional port number).
+ Parameter names are _not_ case sensitive. Currently supported parameters
+ include:
+
+ User
+ This parameter requires an associated value, and is intended to
+ provide the username identifier with which to establish the server
+ connection. If your SMTP server offers SMTP AUTH authentication,
+ adding this parameter to the SMTP-Server option will cause _Alpine_
+ to attempt to authenticate to the server using the supplied username.
+ Similarly, if your NNTP server offers NNTP "AUTHINFO SASL" or
+ "AUTHINFO USER" authentication, adding this parameter to the
+ NNTP-Server option (or to the server name for any folder collection
+ using NNTP) will cause _Alpine_ to attempt to authenticate to the
+ server using the supplied username. An example might be:
+
+ /user=katie
+
+ TLS
+ Normally, when a new connection is made an attempt is made to
+ negotiate a secure (encrypted) session using Transport Layer Security
+ (TLS). If that fails then a non-encrypted connection will be
+ attempted instead. This is a unary parameter indicating communication
+ with the server must take place over a TLS connection. If the attempt
+ to use TLS fails then this parameter will cause the connection to
+ fail instead of falling back to an unsecure connection.
+
+ /tls
+
+ SSL
+ This is a unary parameter indicating communication with the server
+ should take place over a Secure Socket Layer connection. The server
+ must support this method, and be prepared to accept connections on
+ the appropriate port (993 by default). _Alpine_ must be linked with
+ an SSL library for this option to be operational.
+
+ /ssl
+
+ NoValidate-Cert
+ Do not validate certificates (for TLS or SSL connections) from the
+ server. This is needed if the server uses self-signed certificates or
+ if _Alpine_ cannot validate the certificate for some other known
+ reason.
+
+ Anonymous
+ This is a unary parameter (that means it does not have a value)
+ indicating that the connection be logged in as "anonymous" rather
+ than a specific user. Not all servers offer anonymous access; those
+ which do generally only offer read-only access to certain "public"
+ folders.
+
+ /anonymous
+
+ Secure
+ This is a unary parameter indicating that the connection use the most
+ secure authentication method mutually supported by _Alpine_ and the
+ server. _Alpine_ is capable of authenticating connections to the
+ server using several methods. By default, _Alpine_ will attempt each
+ method until either a connection is established or the list of
+ methods is exhausted. This parameter causes _Alpine_ to instead fail
+ the connection if the first (generally most "secure") method fails.
+
+ /secure
+
+ Submit
+ This is a unary parameter for use with the "SMTP-Server" option. It
+ indicates that the connection should be made to the Submit server
+ (RFC 3676) (port 587) instead of the SMTP port (25). At the time this
+ help was written the submit option was equivalent to specifying port
+ 587.
+
+ /submit
+
+ or
+
+ host:587
+
+ Debug
+ This is a unary parameter indicating that the connection be
+ established in a verbose mode. Basically, it causes _Alpine_ to log
+ the communication with the server in _Alpine_'s debug file. Normally,
+ the alpine -d command-line flag would be used instead.
+
+ NoRsh
+ By default, _Alpine_ attempts to login using "rsh", the UNIX remote
+ shell program. Including "NoRsh" will cause connections to this
+ server to skip the "rsh" attempt. This might be useful to avoid long
+ timeouts caused by rsh firewalls, for example.
+
+ Service
+ This parameter requires an associated value. The default value is
+ "IMAP" which indicates communication with the server based on the
+ IMAP4rev1 protocol (defined in RFC 3501 -- see
+ http://www.imap.org/docs/rfc3501.html). Other service values include:
+
+ NNTP
+ This value indicates communication with the server takes place
+ via the Network News Transfer Protocol. Use this to define a
+ collection of newsgroups on a remote news server. So
+
+ /service=NNTP
+
+ or just
+
+ /NNTP
+
+ is the way to specify NNTP access.
+
+ POP3
+ This value indicates communication with the server takes place
+ via the Post Office Protocol 3 protocol.
+
+ /service=POP3
+
+ or just
+
+ /POP3
+
+ Note that there are several important issues to consider when
+ selecting this option:
+
+ 1. POP3 provides access to only your INBOX. In other words,
+ secondary folders such as your "saved-messages" are
+ inaccessible.
+ 2. _Alpine_'s implementation of POP3 does not follow the
+ traditional POP model and will leave your mail on the server.
+ Refer to the Mail Drop functionality for a possible way around
+ this problem.
+ 3. See the discussion about new-mail checking in
+ Folder-Reopen-Rule.
+
+ Note that it is possible to include more than one parameter in a server
+ specification by concatenating the parameters. For example:
+
+ foo.example.com:port/user=katie/novalidate-cert/debug
+ _________________________________________________________________
+
+Folder Namespaces
+
+ A _Alpine_ folder name looks like
+
+ [{<remote-specification>}][#<namespace>][<namespace-specific-part>]
+
+ The local part of a folder name has an optional "Namespace" which tells
+ _Alpine_ how to interpret the rest of the name.
+
+ By default the folder name is interpreted as defining a section of your
+ personal folder area. This area and how you specify it are defined by the
+ server, if one is specified, or, typically, the home directory, if no server
+ is defined.
+
+ If a namespace is specified, it begins with the sharp, "#", character
+ followed by the name of the namespace and then the namespace's
+ path-element-delimiter. Aside from the path's format, namespaces can also
+ imply access rights, content policy, audience, location, and, occasionally,
+ access methods.
+
+ Each server exports its own set (possibly of size one) of namespaces. Hence,
+ it's likely communication with your server's administrator will be required
+ for specific configurations. Some of the more common namespaces, however,
+ include:
+
+ #news.
+ This specifies a set of folders in the newsgroup namespace. Newsgroup
+ names are hierarchically defined with each level delimited by a
+ period.
+
+ #news.comp.mail.pine
+
+ #public/
+ This specifies a folder area that the server may export to the
+ general public.
+
+ #shared/
+ This specifies a folder area that the folder may export to groups of
+ users.
+
+ #ftp/
+ This specifies a folder area that is the same as that it may have
+ exported via the "File Transfer Protocol".
+
+ #mh/
+ This specifies the personal folder area associated with folders and
+ directories that were created using the MH message handling system.
+
+ #move/
+ This namespace is interpreted locally by _Alpine_. It has an unusual
+ interpretation and format.
+
+ #move<DELIM><MailDropFolder><DELIM><DestinationFolder>
+
+ The #move namespace is followed by two folder names separated by a
+ delimiter character. The delimiter character may be any character
+ which does not appear in the MailDropFolder name. The meaning of
+ #move is that mail will be copied from the MailDropFolder to the
+ DestinationFolder and then deleted (if possible) from the
+ MailDropFolder. Periodic checks at frequency Mail-Check-Interval, but
+ with a minimum time between checks set by MailDrop-Check-Minimum, are
+ made for new mail arriving in the MailDropFolder. An example which
+ copies mail from a POP inbox to a local folder follows
+
+ #move+{popserver.example.com/pop3/ssl}inbox+local folder
+
+ To you it appears that mail is being delivered to the local folder
+ when it is copied from the MailDropFolder, and you read mail from the
+ local folder.
+
+ Note that if the DestinationFolder does not exist then the messages
+ are not copied from the MailDropFolder. A #move folder may only be
+ used as an Incoming folder or an Inbox. When you are in the FOLDER
+ LIST of Incoming Message Folders (after turning on the
+ enable-incoming-folders option) the Add command has a subcommand "Use
+ Mail Drop" which may be helpful for defining the folder in your
+ _Alpine_ configuration. The same is true when you edit the Inbox-Path
+ option in Setup/Config. Each of these configuration methods will also
+ create the DestinationFolder if it doesn't already exist. If you are
+ having problems, make sure the DestinationFolder exists.
+
+ In addition, the server may support access to other user's folders, provided
+ you have suitable permissions. Common methods use a prefix of either
+ "~user/", or "/user/" to indicate the root of the other user's folder area.
+ _________________________________________________________________
+
+What is a Mail Drop?
+
+ In some situaions it may make sense to have your mail delivered to one
+ folder (the Mail Drop) and then when you want to read mail that has been
+ delivered to the Mail Drop folder _Alpine_ will move it to another
+ destination folder. Often the Mail Drop will be a remote folder and messages
+ will be moved from there to a local destination folder.
+
+ One example where this might make sense is if the Mail Drop folder is
+ accessible only with the POP protocol. You could designate your POP inbox as
+ the Mail Drop folder and have _Alpine_ move mail from there to a local (on
+ the same machine _Alpine_ is running on) destination folder, where you'll
+ read it.
+
+ A Mail Drop may only be used as your Inbox or as an Incoming folder.
+
+ There is no attempt to synchronize the contents of the destination folder
+ with the contents of the Mail Drop folder. All that happens is that all of
+ the messages in the Mail Drop folder are copied to the destination folder
+ and then they are deleted and expunged (if possible) from the Mail Drop
+ folder. The next time a check for new mail is made, any messages in the Mail
+ Drop folder are once again copied to the destination folder and deleted and
+ expunged from the Mail Drop folder. (If the Mail Drop folder is a news
+ group, then the messages can't be expunged from the newsgroup. Instead, only
+ Recent messages are copied from the newsgroup to the destination folder.)
+
+ Configuration of a Mail Drop is a little different from configuration of a
+ folder which does not use a Mail Drop because you have to specify two folder
+ names instead of one. The two folders may be any types of folders that
+ _Alpine_ can normally use. They don't have to be a remote folder and a local
+ folder, that is simply the most common usage. When you use a Mail Drop
+ folder _Alpine_ will periodically re-open the Mail Drop to check for new
+ mail. The new-mail checks will happen at the frequency set with the
+ Mail-Check-Interval option, but with a minimum time (MailDrop-Check-Minimum)
+ between checks. Because of this minimum you may notice that new mail does
+ not appear promptly when you expect it. The reason for this is to protect
+ the server from over-zealous opening and closing of the Mail Drop folder. If
+ the user initiates the check by typing ^L (Ctrl-L) or the Next command when
+ at the end of the folder index, then the check will happen, regardless of
+ how long it has been since the previous check.
+
+ If there is new mail, that mail will be copied to the destination folder and
+ then will be deleted from the Mail Drop. Note that using a Mail Drop with a
+ local destination folder does not make sense if you read mail from more than
+ one machine, because the mail is downloaded to the destination folder (which
+ is accessible from only one machine) and deleted from the Mail Drop.
+
+ The feature Maildrops-Preserve-State modifies the operation of Mail Drops.
+
+ The actual syntax used by _Alpine_ for a folder that uses a Mail Drop is:
+
+ #move<DELIM><MailDropFolder><DELIM><DestinationFolder>
+
+ The brackets are not literal.
+
+ <DELIM>
+
+ is a single character which does not appear in the MailDropFolder name. If
+ the name doesn't contain spaces then it can be a space character. The two
+ folder names are full technical folder names as used by _Alpine_. Here are a
+ couple examples to give you an idea what is being talked about:
+
+ #move {popserver.example.com/pop3}inbox localfolder
+
+ #move+{nntpserver.example.com/nntp}#news.comp.mail.pine+local folder
+
+ A #move folder may only be used as an Incoming folder or an Inbox. When you
+ are in the FOLDER LIST of Incoming Message Folders (after turning on the
+ Enable-Incoming-Folders option) the Add command has a subcommand "Use Mail
+ Drop" which may be helpful for defining the folder in your _Alpine_
+ configuration. The same is true when you edit the Inbox-Path option in
+ Setup/Config.
+ if it doesn't already exist. If you are having problems, make sure the
+ DestinationFolder exists.
+ _________________________________________________________________
+
+Sorting a Folder
+
+ The mail index may be sorted by arrival, date, subject, from, size, score,
+ to, or cc order. Each sort order can also be reversed. The _$_ command will
+ prompt the user for the sort order. The sort order can also be specified on
+ the command line with the _-sort_ flag or (equivalently) with the sort-key
+ variable in the _pinerc_ file. When a user changes folders, the sort order
+ will go back to the original sort order. The command line (_-sort_) or
+ configuration file sort specification (_sort-key_) changes the original sort
+ order.
+
+ When a folder is sorted and new mail arrives in the folder it will be
+ inserted in its properly sorted place. This can be a little odd when the
+ folder is sorted by something like the subject. It can also be a little slow
+ if you are viewing a large, sorted _INBOX_, since the _INBOX_ will have to
+ be re-sorted whenever new mail arrives.
+
+ The sorts are all independent of case and ignore leading or trailing white
+ space. There are actually two forms of subject sort. One called _Subject_
+ and the other called _OrderedSubj_. They both ignore "Re:" at the beginning
+ and "(fwd)" at the end of the subjects. _Subject_ sorts all the subjects
+ alphabetically. _OrderedSubj_ sorts by subjects alphabetically, groups
+ messages with the same subject (pseudo-threads), then sorts the groups by
+ the date of the first message of the group. Sorting by _Thread_ was added
+ after _OrderedSubj_ and is usually a better method. Thread sorting uses
+ information in the message headers References, Message-ID, and Subject. It
+ is possible the sort will be slightly slower with a Thread sort than with an
+ OrderedSubj sort. The sort by sender sorts by the user-id (part before the
+ "@"), not the full name. The arrival sort is no sort at all and the date
+ sort depends on the format of the date. Some dates are in strange formats
+ and are unparsable. The time zone is also taken into account.
+
+ Sorting large mail folders can be very slow since it requires fetching all
+ the headers of the mail messages. With UNIX _Alpine_, only the first sort is
+ slow since _Alpine_ keeps a copy of all the headers. One exception is
+ sorting in reverse arrival order. This is fast because no headers have to be
+ examined. _Alpine_ will show progress as it is sorting.
+ _________________________________________________________________
+
+Alternate Editor
+
+ In the _Alpine_ composer you can use any text editor, such as _vi_ or
+ _emacs,_ for composing the message text. The addresses and subject still must
+ be edited using the standard _Alpine_ composer. If you include the feature
+ enable-alternate-editor-cmd in your _pinerc_ you can type _^__ while in the
+ body of the message in the composer and be prompted for the editor. If you
+ also set the editor variable in your _pinerc_ then _^__ will invoke the
+ configured editor when you type it.
+
+ Turning on the feature enable-alternate-editor-implicitly will automatically
+ invoke the editor you have defined with the _editor_ variable whenever you
+ enter the body of a message you are composing. For example, when you move
+ out of the last header line and into the body of the message, the alternate
+ editor will be automatically invoked.
+
+ We know that many people would like to use the alternate editor to edit the
+ mail header as well. We considered several designs for this and didn't come
+ up with one that we liked and that was easy to implement. One of the main
+ problems is that you lose access to the address book.
+ _________________________________________________________________
+
+Signatures and Signature Placement
+
+ If the file _~/.signature_ (UNIX) or _<PINERC_directory>\PINE.SIG (PC)
+ exists, it will be included in all outgoing messages. It is included before
+ composition starts so that the user has a chance to edit it out if he or she
+ likes. The file name for the signature can be changed by setting the
+ signature-file variable in the _pinerc_. If the feature enable-sigdashes is
+ turned on then the line consisting of the three characters "-- " is
+ prepended to the signature file. When Replying or Forwarding a message
+ different signatures my be automatically included by configuring them in the
+ Roles setup screen. It's easy to include different signatures by hand, by
+ having multiple signature files (_.sig1, .sig2, .sig3, etc_) and choosing to
+ include (^R in the composer) the correct one for the message being sent.
+
+ _Alpine_'s default behavior encourages a user to put his or her contribution
+ before the inclusion of the original text of the message being forwarded or
+ replied to, This is contrary to some conventions, but makes the conversation
+ more readable when a long original message is included in a reply for
+ context. The reader doesn't have to scroll through the original text that he
+ or she has probably already seen to find the new text. If the reader wishes
+ to see the old message(s), the reader can scroll further into the message.
+ Users who prefer to add their input at the end of a message should set the
+ signature-at-bottom feature. The signature will then be appended to the end
+ of the message after any included text. This feature applies when
+ _Reply_ing, not when _Forward_ing.
+ _________________________________________________________________
+
+Feature List Variable
+
+ _Alpine_ used to have _feature levels_ for users with different amounts of
+ experience. We found that this was too restrictive. _Alpine_ now has a
+ feature-list instead. Each user may pick and choose which features they
+ would like enabled (simple to do in the _Setup/Config_ screen). There is a
+ short description of each in Configuration Features. There is also a short
+ on-line help explaining the effect of each of the features in the
+ _Setup/Config_ screen. When the cursor is highlighting a feature, the _?_
+ command will show the help text for that feature. Features don't have
+ values, they are just turned on or off. They are all off by default.
+
+ The _feature-list_ variable is different from all other configuration
+ variables in that its value is additive. That is, the system-wide
+ configuration file can have some features turned on by default. The user can
+ select other features in their personal configuration file and those
+ features will be _added_ to the set of features turned on in the system-wide
+ configuration file. (With all other configuration variables, the user's
+ values _replace_ the system-wide values.) Likewise, additional features may
+ be set on the command-line with the argument "-feature-list=". These will be
+ added to the others.
+
+ The treatment of _feature-list_ in the system-wide _fixed_ configuration
+ file is also different from other variables. The system management can fix
+ the value of individual features by placing them in the fixed configuration
+ file. Users will not be able to alter those features, but will still be able
+ to set the other non-restricted features the way they like.
+
+ Because _feature-list_ is additive, there is a way to turn features off as
+ well as on. Prepending the prefix "no-" to any feature sets it to off. This
+ is useful for over-riding the system-wide default in the personal
+ configuration file or for over-riding the system-wide default or the
+ personal configuration value on the command line. For example, if the
+ system-wide default configuration has the _quit-without-confirm_ feature
+ set, the user can over-ride that (and turn it off) by including
+ _no-quit-without-confirm_ in the personal configuration file or by giving the
+ command line argument _-feature-list=no-quit-without-confirm._ More features
+ (options) will no doubt continue to be added.
+ _________________________________________________________________
+
+Configuration Inheritance
+
+ We start with an explanation of how configuration works in hopes of making
+ it easier to describe how inheritance works.
+
+ _Alpine_ uses a hierarchy of configuration values from different locations.
+ There are five ways in which each configuration option (configuration
+ variable) can be set. In increasing order of precedence they are:
+
+ 1. the system-wide configuration file.
+ 2. the personal configuration file
+ 3. the personal exceptions file
+ 4. a command line argument
+ 5. the system-wide _fixed_ configuration file (Unix _Alpine_ only)
+
+ The fixed configuration file is normally /usr/local/lib/pine.conf.fixed.
+
+ The system-wide configuration file is normally /usr/local/lib/pine.conf for
+ Unix _Alpine_ and is normally not set for _PC-Alpine_. For _PC-Alpine_, if
+ the environment variable _$PINECONF_ is set, that is used for the
+ system-wide configuration. This location can be set or changed on the
+ command line with the -P flag. The system-wide configuration file can be
+ either a local file or a remote configuration folder.
+
+ For Unix _Alpine_, the personal configuration file is normally the file
+ .pinerc in the user's home directory. This can be changed with the -p
+ command line flag. For _PC-Alpine_, the personal configuration file is in
+ $PINERC or <PineRC registry value> or ${HOME}\ALPINE\PINERC or <ALPINE.EXE
+ dir>\PINERC. This can be changed with the -p command line flag. If -p or
+ $PINERC is used, the configuration data may be in a local file or a remote
+ config folder.
+
+ For Unix _Alpine_, the personal exceptions configuration file is specified
+ with the "-x exceptions_config" command line argument. "Exceptions_config"
+ may be either a local file or a remote configuration folder. If there is no
+ "-x" command line option, _Alpine_ will look for the file ".pinercex" in the
+ same local directory that the regular config file is located in. If the
+ regular config file is remote then Unix _Alpine_ looks in the home directory
+ for ".pinercex".
+
+ For _PC-Alpine_, the personal exceptions configuration file is specified
+ with the "-x exceptions_config" command line argument. If there is no "-x"
+ command line argument the environment variable $PINERCEX may be set to the
+ name of the "exceptions_config" instead. "Exceptions_config" may be either a
+ local file or a remote configuration folder. If there is no "-x" command
+ line option and $PINERCEX is not set, _PC-Alpine_ will look for the file
+ "PINERCEX" in the same local directory that the regular config file is
+ located in. If the regular config file is remote then _PC-Alpine_ looks in
+ the local directory specified by the "-aux local_directory" command line
+ argument, or the directory ${HOME}\ALPINE, or in <ALPINE.EXE directory> for
+ a file named "PINERCEX".
+
+ To reiterate, the value of a configuration option is taken from the last
+ location in the list above in which it is set. Or, thinking about it
+ slightly differently, a default value for an option is established in the
+ system-wide configuration file (or in the source code if there is no value
+ in the system-wide file). That default remains in effect until and unless it
+ is overridden by a value in a location further down the list, in which case
+ a new "default" value is established. As we continue down the list of
+ locations we either retain the value at each step or establish a new value.
+ The value that is still set after going through the whole list of
+ configuration locations is the one that is used.
+
+ So, for example, if an option is set in the system-wide configuration file
+ and in the personal configuration file, but is not set in the exceptions, on
+ the command line, or in the fixed file; then the value from the personal
+ configuration file is the one that is used. Or, if it is set in the
+ system-wide config, in the personal config, not in the exceptions, but is
+ set on the command line; then the value on the command line is used.
+
+ Finally we get to inheritance. For configuration options which are lists,
+ like "smtp-server" or "incoming-folders", the inheritance mechanism makes it
+ possible to _combine_ the values from different locations instead of
+ _replacing_ the value. This is true of all configuration lists other than the
+ "feature-list", for which you may already set whatever you want at any
+ configuration location (by using the "no-" prefix if necessary).
+
+ To use inheritance, set the first item in a configuration list to the token
+ "INHERIT". If the first item is "INHERIT", then instead of replacing the
+ default value established so far, the rest of the list is appended to the
+ default value established so far and that is the new value.
+
+ Here is an example which may make it clearer. Suppose we have:
+
+ System-wide config : smtp-server = smtp1.corp.com, smtp2.corp.com
+ Personal config : smtp-server = INHERIT, mysmtp.home
+ Exceptions config : smtp-server = <No Value Set>
+ Command line : smtp-server = <No Value Set>
+ Fixed config : smtp-server = <No Value Set>
+
+ This would result in an effective smtp-server option of
+
+ smtp-server = smtp1.corp.com, smtp2.corp.com, mysmtp.home
+
+ The "INHERIT" token can be used in any of the configuration files and the
+ effect cascades. For example, if we change the above example to:
+
+ System-wide config : smtp-server = smtp1.corp.com, smtp2.corp.com
+ Personal config : smtp-server = INHERIT, mysmtp.home
+ Exceptions config : smtp-server = INHERIT, yoursmtp.org
+ Command line : smtp-server = <No Value Set>
+ Fixed config : smtp-server = <No Value Set>
+
+ This would result in:
+
+ smtp-server = smtp1.corp.com, smtp2.corp.com, mysmtp.home, yoursmtp.org
+
+ Unset variables are skipped over (the default value is carried forward) so
+ that, for example:
+
+ System-wide config : smtp-server = smtp1.corp.com, smtp2.corp.com
+ Personal config : smtp-server = <No Value Set>
+ Exceptions config : smtp-server = INHERIT, yoursmtp.org
+ Command line : smtp-server = <No Value Set>
+ Fixed config : smtp-server = <No Value Set>
+
+ produces:
+
+ smtp-server = smtp1.corp.com, smtp2.corp.com, yoursmtp.org
+
+ If any later configuration location has a value set (for a particular list
+ option) which does _not_ begin with "INHERIT", then that value replaces
+ whatever value has been defined up to that point. In other words, that
+ cancels out any previous inheritance.
+
+ System-wide config : smtp-server = smtp1.corp.com, smtp2.corp.com
+ Personal config : smtp-server = INHERIT, mysmtp.org
+ Exceptions config : smtp-server = yoursmtp.org
+ Command line : smtp-server = <No Value Set>
+ Fixed config : smtp-server = <No Value Set>
+
+ results in:
+
+ smtp-server = yoursmtp.org
+
+ For some configuration options, like "viewer-hdr-colors" or
+ "patterns-roles", it is difficult to insert the value "INHERIT" into the
+ list of values for the option using the normal Setup tools. In other words,
+ the color setting screen (for example) does not provide a way to input the
+ text "INHERIT" as the first item in the viewer-hdr-colors option. The way to
+ do this is to either edit the pinerc file directly and manually insert it,
+ or turn on the "expose-hidden-config" feature and insert it using the
+ Setup/Config screen.
+ _________________________________________________________________
+
+Using Environment Variables
+
+ The values of _Alpine_ configuration options may include environment
+ variables which are replaced by the value of the variable at the time
+ _Alpine_ is run (and also at the time the config option is changed). The
+ syntax to use environment variables is a subset of the common Unix shell
+ dollar-syntax. For example, if
+
+ $VAR
+
+ appears in the value of a _Alpine_ configuration option it is looked up in
+ the environent (using getenv("VAR")) and its looked-up value replaces the
+ $VAR part of the option value. To include a literal dollar sign you may
+ precede the dollar sign with another dollar sign. In other words, if the
+ text
+
+ $$text
+
+ is the value of a configuration option, it will be expanded to
+
+ $text
+
+ and no environment lookup will be done. For Unix _Alpine_ it will also work
+ to use a backslash character to escape the special meaning of the dollar
+ sign, but $$ is preferable since it works for both _PC-Alpine_ and Unix
+ _Alpine_, allowing the configuration option to be in a shared configuration
+ file.
+
+ This all sounds more complicated than it actually is. An example may make it
+ clearer. Unfortunately, the way in which environment variables are set is
+ OS-dependent and command shell-dependent. In some Unix command shells you
+ may use
+
+ PERSNAME="Fred Flintstone"
+
+ export PERSNAME
+
+ Now, if you use _Alpine_'s Setup/Config screen to set
+
+ personal-name=$PERSNAME
+
+ the $PERSNAME would be replaced by Fred Flintstone so that this would be
+ equivalent to
+
+ personal-name=Fred Flintstone
+
+ Note, environment variable substitution happens after configuration options
+ which are lists are split into the separate elements of the list, so a
+ single environment variable can't contain a list of values.
+
+ The environment variable doesn't have to be the only thing after the equal
+ sign. However, if the name of the variable is not at the end of the line or
+ followed by a space (so that you can tell where the variable name ends), it
+ must be enclosed in curly braces like
+
+ ${VAR}
+
+ It is always ok to use the braces even if you don't need to.
+
+ It is also possible to set a default value for an environment variable. This
+ default value will be used if the environment variable is not set (that is,
+ if getenv("VAR") returns NULL). The syntax used to set a default value is
+
+ ${VAR:-default value}
+
+ If the config file contains
+
+ personal-name=${VAR:-Fred Flintstone}
+
+ then when _Alpine_ is run VAR will be looked up in the environment. If VAR
+ is found then personal-name will have the value that VAR was set to,
+ otherwise, personal-name will be set to Fred Flintstone, the default value.
+
+ An example where an environment variable might be useful is the variable
+ inbox-path in the global configuration file. Suppose most users used the
+ server
+
+ imapserver.example.com
+
+ but that there were some exceptions who used
+
+ altimapserver.example.com
+
+ In this case, the system manager might include the following line in the
+ systemwide default _Alpine_ configuration file
+
+ inbox-path=${IMAPSERVER:-imapserver.example.com}
+
+ For the exceptional users adding
+
+ IMAPSERVER=altimapserver.example.com
+
+ to their environment should work.
+
+ Another example might be the case where a user has to use a different SMTP
+ server from work and from home. The setup might be something as simple as
+
+ smtp-server=$SMTP
+
+ or perhaps a default value could be given. Note that, as mentioned above,
+ the variable SMTP cannot contain a list of SMTP servers.
+ _________________________________________________________________
+
+SMTP Servers
+
+ It is sometimes desirable to set smtp-server=localhost instead of setting
+ sendmail-path to overcome the inability to negotiate ESMTP options when
+ _sendmail_ is invoked with the _-t_ option. Sendmail can also be subject to
+ unacceptable delays due to slow DNS lookups and other problems.
+
+ It is sometimes desirable to configure an SMTP server on a port other than
+ the default port 25. This may be used to provide an alternate service that
+ is optimized for a particular environment or provides different features
+ from the port 25 server. An example would be a program that negotiates ESMTP
+ options and queues a message, but does not attempt to deliver messages. This
+ would avoid delays frequently encountered when invoking _sendmail_ directly.
+
+ A typical configuration would consist of
+ * A program that implements the SMTP or ESMTP protocol via stdio.
+ * An entry in /etc/services for the alternate service.
+ * An entry in /etc/inetd.conf for the alternate service.
+ * An entry in /usr/local/lib/pine.conf, /usr/local/lib/pine.conf.fixed or
+ ~/.pinerc.
+ _________________________________________________________________
+
+MIME.Types file
+
+ _Alpine_'s MIME-TYPE support is based on code contributed by Hans Drexler
+ &LT;drexler@mpi.nl&GT;. _Alpine_ assigns MIME Content-Types according to
+ file name extensions found in the system-wide files
+ /usr/local/lib/mime.types and /etc/mime.types, and a user specific
+ ~/.mime.types file.
+
+ In Windows, _Alpine_ looks in the same directory as the PINERC file and the
+ same dir as ALPINE.EXE. This is similar to the UNIX situation with personal
+ config info coming before potentially shared config data. An alternate
+ search path can be specified by setting the mimetype-search-path variable in
+ the user or system-wide configuration or by setting the MIMETYPES
+ environment variable.
+
+ These files specify file extensions that will be connected to a mime type.
+ Lines beginning with a '#' character are treated as comments and ignored.
+ All other lines are treated as a mime type definition. The first word is a
+ _type/subtype_ specification. All following words are file _extensions_
+ belonging to that type/subtype. Words are separated by whitespace
+ characters. If a file extension occurs more than once, then the first
+ definition determines the file type and subtype. A couple sample lines from
+ a mime.types file follow:
+
+image/gif gif
+text/html html htm
+video/mpeg mpeg mpg mpe
+ _________________________________________________________________
+
+Color Details
+
+ UNIX _Alpine_ may display color if the terminal or terminal emulator you are
+ using is capable of displaying colors. If the terminal supports ANSI color
+ escape sequences you will be able to turn color on using the color-style
+ option and setting it to the value _force-ansi-8color_ or
+ _force-ansi-16color_. If instead you'd like _Alpine_ to automatically detect
+ whether or not you are on a color terminal, set _color-style_ to
+ _use-termdef_ _and_ configure the termcap entry to describe your terminal's
+ color capabilities.
+
+ If the _color-style_ option is set to _use-termdef_, _Alpine_ looks in the
+ terminal capabilities database, TERMINFO or TERMCAP, depending on how
+ _Alpine_ was compiled, to decide whether or not your terminal is capable of
+ color. For TERMINFO compiled _Alpine_s, the capabilities that are used for
+ color are "colors", "setaf", "setab", "op", and "bce". If you have a
+ terminal with color capabilities described by the "scp" capability, _Alpine_
+ does not support it. The capabilities "setf" and "setb" may be used instead
+ of "setaf" and "setab". The capability "bce" is optional and is used as an
+ optimization, the other capabilities are required. For TERMCAP compiled
+ _Alpine_s, the capabilities that are used for color are "Co", "AF", "AB",
+ "op", and "ut". The capabilities "Sf" and "Sb" may be used instead of "AF"
+ and "AB", though this isn't a useful feature.
+
+ Here are some short descriptions of the capabilities listed above. The
+ TERMINFO name is listed, followed by the TERMCAP name in parentheses.
+ _colors_ (_Co_)
+ The number of different colors.
+ _setaf_ (_AF_)
+ Set ANSI foreground color.
+ _setab_ (_AB_)
+ Set ANSI background color.
+ _setf_ (_Sf_)
+ Set foreground color. Alternate form of _setaf_.
+ _setb_ (_Sb_)
+ Set background color. Alternate form of _setab_.
+ _op_ (_op_)
+ Set default pair to its original value.
+ _bce_ (_ut_)
+ Screen is erased with current background color instead of default
+ background.
+
+ A standard ANSI terminal which supports color will have a TERMINFO entry
+ which contains:
+ colors#8
+ setaf=\E[3%p1%dm
+ setab=\E[4%p1%dm
+ op=\E[39;49m
+ bce
+
+ or the TERMCAP equivalent:
+ Co#8
+ AF=\E[3%dm
+ AB=\E[4%dm
+ op=\E[39;49m
+ ut
+
+ If there are eight colors, the program uses colors 0, 1, ..., 7. For an ANSI
+ terminal, the foreground color is set by sending the escape sequence "Escape
+ LeftBracket 3 color_number m" to the terminal. The background color is set
+ by sending the sequence "Escape LeftBracket 4 color_number m". ANSI colors
+ zero through seven are defined to be "black", "red", "green", "yellow",
+ "blue", "magenta", "cyan", and "white". Some terminal emulators will swap
+ blue and red and swap yellow and cyan. The capabilities "setf" and "setb"
+ are usually designed for those terminals so that they will flip the color
+ numbers 1 and 4 and the numbers 3 and 6 to compensate for this. _Alpine_
+ will use the ANSI versions of the capabilities if they exist, and will use
+ the non-ANSI versions (setf and setb) if the ANSI versions don't exist.
+ Here's a version which does the flipping. This can only be used with
+ TERMINFO _Alpine_s, because of the arithmetic, which is not supported by
+ TERMCAP.
+ colors#8
+ setf=\E[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m
+ setb=\E[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m
+ op=\E[39;49m
+ bce
+
+ Some terminal emulators are capable of displaying eight more colors when the
+ foreground colors 30-37 are replaced with 90-97 and the background colors
+ 40-47 are replaced with 100-107. These terminals require a fancy termcap
+ entry which can take foreground colors 0, 1, ..., 15 and map that into 30,
+ 31, ..., 37, 90, 91, ..., 97, and similarly for the background colors. Here
+ is a terminfo entry which will do just that:
+ colors#16
+ setaf=%p1%{8}%/%{6}%*%{3}%+\E[%d%p1%{8}%m%dm
+ setab=%p1%{8}%/%{6}%*%{4}%+\E[%d%p1%{8}%m%dm
+ op=\E[39;49m
+ bce
+
+ and here is the termcap equivalent:
+ Co#16
+ AF=\E[%i%i%>\001\034%>\045\064%dm
+ AB=\E[%i%i%>\001\046%>\057\064%dm
+ op=\E[39;49m
+ ut
+
+ This is a terminfo entry for 16 colors that also does the color flipping:
+ colors#16
+ setf=%p1%{8}%/%{6}%*%{3}%+\E[%d%p1%{8}%m%Pa%?%ga%{1}%=%t4%e%ga%{3}%=%t6%e%ga%
+{4}%=%t1%e%ga%{6}%=%t3%e%ga%d%;m
+ setb=%p1%{8}%/%{6}%*%{4}%+\E[%d%p1%{8}%m%Pa%?%ga%{1}%=%t4%e%ga%{3}%=%t6%e%ga%
+{4}%=%t1%e%ga%{6}%=%t3%e%ga%d%;m
+ op=\E[39;49m
+ bce
+
+ If you are always using the same display it probably won't matter to you if
+ the color pairs red/blue and cyan/yellow are flipped, since you'll always be
+ seeing them flipped. You will get different defaults than on a display with
+ them not flipped, but that's about all. If you are trying to use the same
+ pinerc file from displays with different color characteristics, or from
+ _Alpine_ and _PC-Alpine_, you will have to be more careful. The colors
+ numbered 0 through 7 may be used portably between different systems if you
+ are careful to make them correspond to the ANSI order mentioned above. You
+ can check this by looking at a color configuration screen for one of the
+ colors. The first eight colors should be in the order above. If they aren't,
+ you could fix that by modifying your termcap entry on the UNIX system. This
+ is not possible if your system uses TERMCAP instead of TERMINFO.
+ _________________________________________________________________
+
+S/MIME Overview
+
+ UNIX _Alpine_ only.
+
+ S/MIME is a standard for the public key encryption and signing of email.
+ UNIX _Alpine_ contains a basic implementation of S/MIME based on the OpenSSL
+ libraries.
+
+ Some limitations:
+ * There is no _PC-Alpine_ implementation.
+ * There is no provision for checking for CRLs (Certificate Revocation
+ Lists) in _Alpine_.
+ * This built-in S/MIME implementation is not compatible with and does not
+ help with PGP.
+ * There is no mechanism available for feeding either an entire incoming or
+ an entire outgoing message to an external filter and using that external
+ filter to do S/MIME or PGP processing.
+ * Because the implementation currently uses OpenSSL, there is only a very
+ limited integration with the Mac OS Keychain (the storing and access of
+ public certificates).
+ * There is no way to view or manipulate the lists of certificates from
+ within _Alpine_.
+
+ The S/MIME configuration screen is reached by going to the Main Menu and
+ typing the "S Setup" command followed by "M S/MIME".
+
+ S/MIME BASICS
+
+ In order to digitally sign messages you send you must have a public/private
+ key-pair. This may be obtained from a public Certificate Authority (CA) such
+ as Thawte, Verisign, Comodo, or GoDaddy; or from a smaller CA such as a
+ university which provides certificates for its users or a company which
+ provides certificates for its workers. These certificates are bound to an
+ email address, so the identity being verified is the email address not a
+ person's name.
+
+ Mail is signed by using the sender's private key, which only the owner of
+ the private key has access to. The signature is verified using the signer's
+ public key, which anyone can have access to. With _Alpine_, the first time
+ you receive a signed message the public key of the sender will be stored for
+ future use.
+
+ Mail is encrypted using the recipient's public key and decrypted by the
+ recipient with their private key.
+
+ You need a key of your own in order to sign outgoing messages and to have
+ others encrypt messages sent to you. You do not need a key of your own to
+ verify signed messages sent by others or to encrypt messages sent to others.
+
+ ALPINE S/MIME CERTIFICATE STORAGE
+
+ By default UNIX _Alpine_ stores the certificates it uses in a directory in
+ your home directory. The directory name is
+
+ .alpine-smime
+
+ Within that directory are three subdirectories. Each of the three
+ subdirectories contains files with PEM-encoded contents, the default format
+ for OpenSSL. The "public" directory contains public certificates. The files
+ within that directory have names that are email addresses with the suffix
+ ".crt" appended. An example filename is
+
+ user@example.com.crt
+
+ The "private" directory contains private keys, probably just one for your
+ private key. These are also email addresses but with the suffix ".key"
+ instead. The third directory is "ca" and it contains certificates for any
+ Certificate Authorities that you want to trust but that aren't contained in
+ the set of system CAs. Those files may have arbitrary names as long as they
+ end with the suffix ".crt".
+
+ HOW TO SIGN AND ENCRYPT
+
+ If you have a certificate you may sign outgoing messages. After typing the
+ Ctrl-X command to send a message you will see the prompt
+
+ Send message?
+
+ Available subcommands include "G Sign" and "E Encrypt". Typing the "G"
+ command will change the prompt to
+
+ Send message (Signed)?
+
+ Typing the "E" command will change the prompt to
+
+ Send message (Encrypted)?
+
+ You may even type both to get
+
+ Send message (Encrypted, Signed)?
+
+ HOW TO READ SIGNED OR ENCRYPTED MESSAGES
+
+ The reading of a signed message should not require any special action on
+ your part. There should be an editorial addition at the start of the message
+ which says either
+
+ This message was cryptographically signed.
+
+ or
+
+ This message was cryptographically signed but the signature could not be
+ verified.
+
+ If an encrypted message is sent to you the encrypted text will not be shown.
+ You will have to type the "Ctrl-D Decrypt" command (from the screen where
+ you are viewing the message) and supply your passphrase when asked.
+
+ For a signed or encrypted message there is also a "Ctrl-E Security" command
+ which gives you some information about the certificate used to sign or
+ encrypt the message.
+
+ MISCELLANEOUS
+
+ You may have access to a private certificate in the PKCS12 format, which
+ would sometimes be in a file with a ".p12" suffix. The UNIX shell command
+
+ openssl pkcs12 -in file.p12 -out file.pem
+
+ may work to convert that from the PKCS12 format to the PEM format. Then that
+ file could be placed in the "private" directory with a filename of your
+ email address followed by the suffix ".key".
+ _________________________________________________________________
+
+Additional Notes on PC-Alpine
+
+ Below are a few odds and ends worth mentioning about _PC-Alpine_. They have
+ to do with DOS-specific behavior that is either necessary or useful (and
+ sometimes both!).
+
+ As _PC-Alpine_ runs in an environment with limited access control,
+ accounting or auditing, an additional line is automatically inserted into
+ the header of mail messages generated by _PC-Alpine_:
+ X-Sender: <userid>@<imap.host>
+
+ By popular demand of system administrators, _PC-Alpine_ has been modified to
+ prevent sending messages until the user has successfully logged into a
+ remote mail server. Even though _PC-Alpine_ cannot prevent users from
+ changing the apparent identity of the sender of a message, the IMAP server
+ login name and host name included in the _X-Sender_ line provide some level
+ of traceability by the recipient. However, this should not be considered a
+ rigorous form of authentication. It is extremely lightweight, and is not a
+ replacement for true authentication.
+
+ Hand in hand with authentication and accounting is user information. Since
+ _PC-Alpine_ has no user database to consult for _user-id_, _personal-name_,
+ etc., necessary information must be provided by the user/installer before
+ _PC-Alpine_ can properly construct the "From" address required for outbound
+ messages. _PC-Alpine_ will, by default, prompt for the requisite pieces as
+ they are needed. This information corresponds to the _PINERC_ variables
+ user-id, personal-name, user-domain, and smtp-server.
+
+ The user is then asked whether or not this information should automatically
+ be saved to the _PINERC_. This is useful behavior in general, but can lead
+ to problems in a lab or other shared environment. Hence, these prompts and
+ automatic saving of configuration can be turned off on an entry by entry
+ basis by setting any of the above values in the _PINERC_ to the null string
+ (i.e., a pair of double quotes). This means that the user will be prompted
+ for the information once during each _Alpine_ session, and no opportunity to
+ save them in the _PINERC_ will be offered.
+
+ Another feature of DOS is the lack of standard scratch area for temporary
+ files. During the course of a session, _PC-Alpine_ may require numerous
+ temporary files (large message texts, various caches, etc.). Where to create
+ them can be a problem, particularly when running under certain network
+ operating systems. _PC-Alpine_ observes the _TMPDIR_, _TMP_, and _TEMP_
+ environment variables, and creates temporary files in the directory
+ specified by either. In their absence, _PC-Alpine_ creates these files in
+ the root of the current working drive. Some temporary files have to be
+ created in the same directory as the file they are a temporary copy of. For
+ example, a pinerc file or a address book file.
+
+ Behind the Scenes
+
+ Many people ask how certain _Alpine_ features are implemented. This section
+ outlines some of the details.
+
+Address Books
+
+ There are two types of address book storage. There are _local_ address
+ books, which are the address books that are stored in a local file; and
+ there are _remote_ address books, which are stored on an IMAP server.
+
+ Information About Remote Address Books
+
+ NOTE: The remote address book capability does not allow you to access an
+ existing local address book from a remote system! That is, you can't set
+ the remote address book to something like {remote.host}.addressbook and
+ expect to access the existing .addressbook _file_ on remote.host. Instead,
+ you need to create a new remote address book in a new, previously unused
+ remote mail _folder_. Then you can use the _Select_ and _Apply Save_
+ commands in the address book screen to _Save_ all of the entries from an
+ existing local address book to the new remote address book.
+
+ A remote address book is stored in a mail folder on an IMAP server. An
+ _Alpine_ remote address book is just like an _Alpine_ local address book in
+ that it is not interoperable with other email clients. The folder is a
+ regular folder containing mail messages but those messages are special. The
+ first message must be an alpine remote address book header message which
+ contains the header _x-pine-addrbook_. The last message in the folder
+ contains the address book data. In between the first and the last message
+ are old versions of the address book data. The address book data is simply
+ stored in the message as it would be on disk, with no MIME encoding. When it
+ is used the data from the last message in the folder is copied to a local
+ file and then that file is used exactly like a local address book file is
+ used. When a change is made the modified local file is appended to the
+ remote folder in a new message. In other words, the local file is just a
+ cache copy of the data in the remote folder. Each client which uses the
+ remote address book will have its own cache copy of the data. Whenever a
+ copy is done the entire address book is copied, not just the entries which
+ have changed.
+
+ _Alpine_ can tell that the remote data has changed by one of several
+ methods. If the date contained in the Date header of the last message has
+ changed then it knows it has changed. If the UID of the last message has
+ changed, or the number of messages in the folder has changed, it knows that
+ it has changed. When _Alpine_ discovers the folder has changed it gets a new
+ copy and puts it in the local cache file.
+
+ There is a configuration file variable for remote address books called
+ remote-abook-metafile. The variable is the name of a file in which
+ information about remote address books is stored. There is one line in the
+ metafile for each remote address book. The information stored there is the
+ name of the cache file and information to help figure out when the remote
+ folder was last changed. If the metafile or any of the cache files is
+ deleted then _Alpine_ will rebuild them the next time it runs.
+
+ Remote address books have names that look just like regular remote mail
+ folder names. For example:
+
+ {host.domain}foldername
+
+ _Alpine_ decides whether or not an address book is remote simply by looking
+ at the first character of the address book name and comparing it to '{'.
+
+ Information About All Address Books
+
+ The address book is named, by default, .addressbook in the user's Unix home
+ directory, or in the case of _PC-Alpine_, ADDRBOOK, in the same directory as
+ the PINERC file. There may be more than one address book, and the default
+ name can be overridden via an entry in any of the _Alpine_ configuration
+ files. The two configuration variables address-book and global-address-book
+ are used to specify the names of the address books. Each of these variables
+ is a list variable. The total set of address books for a user is the
+ combination of all the address books specified in these two lists. Each
+ entry in the list is an optional nickname followed by an address book name.
+ The nickname is everything up to the last space before the file name. The
+ _global-address-book_ list will typically be configured in the system-wide
+ configuration file, though a user may override it like most other variables.
+ Address books which are listed in the _global-address-book_ variable are
+ forced read-only, and are typically shared among multiple users.
+
+ Local address books (or local cache files for remote address books) are
+ simple text files with lines in the format:
+
+ <nickname>TAB<fullname>TAB<address>TAB<fcc>TAB<comments>
+
+ The last two fields are optional. A "line" may be made up of multiple actual
+ lines in the file by using continuation lines, which are lines beginning
+ with SPACE characters. The line breaks may be after TABs or in between
+ addresses in a distribution list. Each _actual_ line in the file must be
+ less than 1000 characters in length.
+
+ Nicknames (the first field) are short names that the user types instead of
+ typing in the full address. There are several characters which aren't
+ allowed in nicknames in order to avoid ambiguity when parsing the address
+ (SPACE, COMMA, @, ", ;, :, (, ), [, ], <, >, \). Nicknames aren't required.
+ In fact, none of the fields is required.
+
+ The _fullname_ field is usually stored as Last_name, First_name, in order
+ that a sort on the fullname field comes out sorted by Last_name. If there is
+ an unquoted comma in the fullname, _Alpine_ will flip the first and last
+ name around and get rid of the comma when using the entry in a composition.
+ It isn't required that there be a comma, that's only useful if the user
+ wants the entries to sort on last names.
+
+ The _address_ field takes one of two forms, depending on whether the entry
+ is a single (simple) address or a distribution list. For a simple entry, the
+ address field is an RFC 2822 address. This could be either the email-address
+ part of the address, i.e., the part that goes inside the brackets (<>), or
+ it could be a full RFC 2822 address. The phrase part of the address (the
+ fullname) is used unless there is a fullname present in the fullname field
+ of the address book entry. In that case, the fullname of the address book
+ entry replaces the fullname of the address. For a distribution list, the
+ <address> is in the format:
+
+ "(" <address>, <address>, <address>, ... ")"
+
+ The only purpose for the parentheses around the list of addresses is to make
+ it easier for the parsing routines to tell that it is a simple entry instead
+ of a list. The two are displayed differently and treated slightly
+ differently in some cases, though most of the distinction has disappeared.
+ Each of the addresses in a list can be a full RFC 2822 address with fullname
+ included, or it may be just the simple email-address part of the address.
+ This allows the user to have a list which includes the fullnames of all the
+ list members. In both the simple and list cases, addresses may also be other
+ nicknames which appear in this address book or in one of the other address
+ books. (Those nicknames are searched for by looking through the address
+ books in the order they appear in the address book screen, with the first
+ match winning.) Lists may be nested. If addresses refer to each other in a
+ loop (for example, list A includes list B which includes list A again) this
+ is detected and flagged. In that case, the address will be changed to "****
+ address loop ****".
+
+ The optional _fcc_ field is a folder name, just like the fcc field in the
+ composer headers. If the first address in the To field of a composition
+ comes from an address book entry with an fcc field, then that fcc is placed
+ in the fcc header in the composer.
+
+ The _comments_ field is just a free text field for storing comments about an
+ entry. By default, neither the fcc nor the comments field is shown on the
+ screen in the address book screen. You may make those fields visible by
+ configuring the variable addressbook-formats. They are also searched when
+ you use the _WhereIs_ command in the address book screen and are visible
+ when you _View_ or _Update_ an entry.
+
+ The address book is displayed in the order that it is stored. When the user
+ chooses a different sorting criterion, the data is actually sorted and
+ stored, as opposed to showing a sorted view of the data.
+
+ When the address book is written out, it is first written to a temporary
+ file and if that write is successful it is renamed. This guards against
+ errors writing the file that might destroy the whole address book. The
+ address book is re-written after each change. If the address book is a
+ remote address book, the file is then appended to the remote mail folder
+ using IMAP.
+
+ The end-of-line character(s) in the address book file are those native to
+ the system writing it. So it is <LF> on Unix and <CR><LF> on PC's. However,
+ both Unix and PC versions of _Alpine_ can read either format, so it should
+ be possible to share a read-only address book among the two populations
+ (using NFS, for example).
+ _________________________________________________________________
+
+ Address Book Lookup File
+
+ _Pine_ used an additional file for each address book, called the LookUp
+ file. It had the same name as the address book file with the suffix ".lu"
+ appended. _Alpine_ no longer uses a lookup file.
+
+ Validity Checking of Address Books
+
+ There is no file locking done on _Alpine_ address books, however, there is
+ considerable validity checking done to make sure that the address book
+ hasn't changed unexpectedly. Whenever the address book is about to be
+ changed, a check is made to see if the file is newer than when we read it or
+ the remote address book folder has changed since we last copied it. If
+ either of these is true, the change is aborted.
+
+ There is an automatic, behind-the-scene check that happens every so often,
+ also. For example, if someone else changes one of the address books that you
+ have configured, your _Alpine_'s copy of the address book will usually be
+ updated automatically without you noticing. This checking happens at the
+ same time as new mail checking takes place, unless you are actively using
+ the address book, in which case it happens more frequently.
+ _________________________________________________________________
+
+Remote Configuration
+
+ Configuration information may be stored remotely. Remote configuration
+ information is stored in a folder on an IMAP server. This should be a folder
+ which is used only for storing the configuration information. In other
+ words, it should be a folder which didn't exist before.
+
+ Remote configuration folders are very similar to remote address book
+ folders. They both consist of a header message, which serves to identify the
+ type of folder; the last message, which contains the data; and intermediate
+ messages, which contain old versions of the data. The first message must
+ contain the header _x-pine-pinerc_.
+
+ When a remote configuration is being used, the folder is checked to make
+ sure it is a remote configuration folder, then the data contained in the
+ last message is copied to a temporary file. That file is treated just like
+ any regular local configuration file from that point on. Whenever a
+ configuration change is made, the entire file is copied back to the IMAP
+ server and is appended to the folder as a new message.
+
+ Because remote configuration folders are so similar to remote address books,
+ the configuration variable remote-abook-metafile is used by both.
+
+ Remote configuration folders have names that look just like regular remote
+ mail folder names. For example:
+
+ {host.domain}mypinerc
+
+ _Alpine_ decides whether or not a configuration file is remote simply by
+ looking at the first character of the name and comparing it to '{'.
+ _________________________________________________________________
+
+Checkpointing
+
+ Periodically _Alpine_ will save the whole mail folder to disk to prevent
+ loss of any mail or mail status in the case that it gets interrupted,
+ disconnected, or crashes. The period of time _Alpine_ waits to do the
+ checkpoint is calculated to be minimally intrusive. The timing can be
+ changed (but usually isn't) at compile time. Folder checkpointing happens
+ for both local folders and those being accessed with IMAP. The delays are
+ divided into three categories:
+
+ The exact algorithm given below is no longer correct. It has gotten more
+ complicated over time. However, this gives the general idea _Alpine_ uses
+ when deciding whether or not to do a checkpoint.
+
+ Good Time:
+ This occurs when _Alpine_ has been idle for more than 30 seconds. In
+ this case _Alpine_ will checkpoint if 12 changes to the file have
+ been made or at least one change has been made and a checkpoint
+ hasn't been done for five minutes.
+ Bad Time:
+ This occurs just after _Alpine_ has executed some command. _Alpine_
+ will checkpoint if there are 36 outstanding changes to the mail file
+ or at least one change and no checkpoint for ten minutes.
+ Very Bad Time:
+ Done when composing a message. In this case, _Alpine_ will only
+ checkpoint if at least 48 changes have been made or at least one
+ change has been made in the last twenty minutes with no checkpoint.
+ _________________________________________________________________
+
+Debug Files
+
+ If UNIX _Alpine_ is compiled with the compiler _DEBUG_ option on (the
+ default), then _Alpine_ will produce debugging output to a file. This can be
+ disabled at compile-time with the --disable-debug configure option, or at
+ run-time with the command line flag -d0. The file is normally .pine-debugX
+ in the user's home directory where _X_ goes from 1 to 4. Number 1 is always
+ the most recent session and 4 the oldest. Four are saved because often the
+ user has gone in and out of _Alpine_ a few times after a problem has
+ occurred before the expert actually gets to look at it. The amount of output
+ in the debug files varies with the debug level set when _Alpine_ is compiled
+ and/or as a command line flag. The default is level 2. This shows very
+ general things and records errors. Level 9 produces copious amounts of
+ output for each keystroke.
+
+ Similarly, _PC-Alpine_ creates debug files named pinedebg.txtX in the same
+ directory as the PINERC file.
+ _________________________________________________________________
+
+INBOX and Special Folders
+
+ The _INBOX_ folder is treated specially. It is normally kept open constantly
+ so that the arrival of new mail can be detected. The name _INBOX_ refers to
+ wherever new mail is retrieved on the system. If the inbox-path variable is
+ set, then _INBOX_ refers to that. IMAP servers understand the concept of
+ _INBOX_, so specifying the folder _{imap.u.example.edu}INBOX_ is meaningful.
+ The case of the word _INBOX_ is not important, but _Alpine_ tends to display
+ it in all capital letters.
+
+ The folders for sent mail and saved messages folders are also somewhat
+ special. They are automatically created if they are absent and recreated if
+ they are deleted.
+ _________________________________________________________________
+
+Internal Help Files
+
+ The file pine.hlp in the alpine subdirectory of the distribution contains
+ all the help text for _Alpine_. It is compiled right into the _Alpine_
+ binary as strings. This is done to simplify installation and configuration.
+ The pine.hlp file is in a special format that is documented at the beginning
+ of the file. It is divided into sections, each with a name that winds up
+ being referenced as a global variable. This file is processed during the
+ build process and turned into a C file that is compiled into _Alpine_.
+ _________________________________________________________________
+
+International Character Sets
+
+ _Alpine_ uses Unicode characters internally and it is a goal for _Alpine_ to
+ handle email in many different languages. _Alpine_ will properly display
+ only left-to-right character sets in a fixed-width font. Specifically,
+ _Alpine_ assumes that a fixed-width font is in use, in the sense that
+ characters are assumed to take up zero, one, or two character cell widths
+ from left to right on the screen. This is true even in _PC-Alpine_.
+
+ _Alpine_ recognizes some local character sets which are right-to-left
+ (Arabic, Hebrew, and Thai) or not representable in a fixed-width font
+ (Arabic) and properly converts texts in these character sets to/from
+ Unicode; however, there are known display bugs with these character sets.
+
+ There are three possible configuration character settings and some
+ environment variable settings which can affect how _Alpine_ handles
+ international characters. The first two of these are only available in UNIX
+ _Alpine_. The three configuration options are _display-character-set_,
+ _keyboard-character-set_, and _posting-character-set_. The
+ _keyboard-character-set_ defaults to being the same value as the
+ _display-character-set_, and that is usually correct, because the keyboard
+ almost always produces characters in the same character set as the display
+ displays. The _display-character-set_ is the character set that _Alpine_
+ will attempt to use when sending characters to the display.
+
+ Besides those variables there is also use-system-translation which can be
+ used instead of these. That usage is only lightly tested and is not
+ recommended.
+
+ By default, the _display-character-set_ variable is not set and UNIX _Alpine_
+ will attempt to get this information from the environment. In particular,
+ the nl_langinfo(CODESET) call is used. This usually depends on the setting
+ of the environment variables LANG or LC_CTYPE. An explicit configuration
+ setting for _display-character-set_ will, of course, override any default
+ setting.
+
+ For _PC-Alpine_ the _display-character-set_ and the _keyboard-character-set_
+ are always equivalent to UTF-8 and this is not settable.
+
+ It is probably best to use UNIX _Alpine_ in a terminal emulator capable of
+ displaying UTF-8 characters, since that will allow you to view just about
+ any received text that is correctly formatted (note, however, the above
+ comments about known index display bugs with certain character sets). You'll
+ need to have an emulator which uses a UTF-8 font and you'll need to set up
+ your environment to use a UTF-8 charmap. For example, on a Linux system you
+ might include
+
+ setenv LANG en_US.UTF-8
+
+ or something similar in your UNIX startup files. You'd also have to select a
+ UTF-8 font in your terminal emulator.
+
+ The types of values that the character set variables may be set to are
+ UTF-8, ISO-8859-1, or EUC-JP. The ISO-2022 character sets are not supported
+ for input or for display, but as a special case, ISO-2022-JP is supported
+ for use only as a _posting-character-set_. In the Setup/Config screen you
+ may choose from a list of all the character sets _Alpine_ knows about by
+ using the "T" ToCharsets command. Here is a list of many of the possible
+ character sets:
+
+ UTF-8 Unicode
+ US-ASCII 7 bit American English characters
+ ISO-8859-1 8 bit European "Latin 1" character set
+ ISO-8859-2 8 bit European "Latin 2" character set
+ ISO-8859-3 8 bit European "Latin 3" character set
+ ISO-8859-4 8 bit European "Latin 4" character set
+ ISO-8859-5 8 bit Latin and Cyrillic
+ ISO-8859-6 8 bit Latin and Arabic
+ ISO-8859-7 8 bit Latin and Greek
+ ISO-8859-8 8 bit Latin and Hebrew
+ ISO-8859-9 8 bit European "Latin 5" character set
+ ISO-8859-10 8 bit European "Latin 6" character set
+ ISO-8859-11 Latin and Thai
+ ISO-8859-12 Reserved
+ ISO-8859-13 8 bit European "Latin 7" character set
+ ISO-8859-14 8 bit European "Latin 8" character set
+ ISO-8859-15 8 bit European "Latin 9" character set
+ ISO-8859-16 8 bit European "Latin 10" character set
+ KOI8-R 8 bit Latin and Russian
+ KOI8-U 8 bit Latin and Ukranian
+ WINDOWS-1251 8 bit Latin and Russian
+ TIS-620 8 bit Latin and Thai
+ VISCII 8 bit Latin and Vietnamese
+ GBK Latin and Chinese Simplified
+ GB2312 Latin and Chinese Simplified
+ CN-GB Latin and Chinese Simplified
+ BIG5 Latin and Chinese Traditional
+ BIG-5 Latin and Chinese Traditional
+ EUC-JP Latin and Japanese
+ SHIFT-JIS Latin and Japanese
+ EUC-KR Latin and Korean
+ KSC5601 Latin and Korean
+
+ When reading incoming email, _Alpine_ understands many different character
+ sets and is able to convert the incoming mail into Unicode. The Unicode will
+ be converted to the _display-character-set_ for display on your terminal.
+ Characters typed at the keyboard will be converted from the
+ _keyboard-character-set_ to Unicode for _Alpine_'s internal use. You may find
+ that you can read some malformed messages that do not contain a character
+ set label by setting the option unknown-character-set.
+
+ The _posting-character-set_ is used when sending messages. The default
+ behavior obtained by leaving this variable unset is usually what is wanted.
+ In that default case, _Alpine_ will attempt to label the message with the
+ most specific character set from the rather arbitrary set
+
+ US-ASCII, ISO-8859-15, ISO-8859-1, ISO-8859-2, VISCII, KOI8-R, KOI8-U,
+ ISO-8859-7, ISO-8859-6, ISO-8859-8, TIS-620, ISO-2022-JP, GB2312, BIG5,
+ EUC-KR, and UTF-8.
+
+ For example, if the message is made up of only US-ASCII characters, it will
+ be labeled US-ASCII. Otherwise, if it is all ISO-8859-15 characters, that
+ will be the label. If that doesn't work the same is tried for the remaining
+ members of the list.
+
+ It might make sense to set _posting-character-set_ to an explicit value
+ instead. For example, if you usually send messages in Greek, setting this
+ option to ISO-8859-7 will result in messages being labeled as US-ASCII if
+ there are no non-ascii characters, ISO-8859-7 if there are only Greek
+ characters, or UTF-8 if there are some characters which aren't representable
+ in ISO-8859-7. Another possibility is to set this option explicitly to
+ UTF-8. In that case _Alpine_ labels only ascii messages as US-ASCII and all
+ other messages as UTF-8.
+ _________________________________________________________________
+
+Interrupted and Postponed Messages
+
+ If the user is composing mail and is interrupted by being disconnected
+ (SIGHUP, SIGTERM or end of file on the standard input), _Alpine_ will save
+ the interrupted composition and allow the user to continue it when he or she
+ resumes _Alpine_. As the next _Alpine_ session starts, a message will be
+ given that an interrupted message can be continued. To continue the
+ interrupted message, simply go into the composer. To get rid of the
+ interrupted message, go into the composer and then cancel the message with
+ _^C._
+
+ Composition of half-done messages may be postponed to a later time by giving
+ the _^O_ command. Other messages can be composed while postponed messages
+ wait. All of the postponed messages are kept in a single folder. Postponing
+ is a good way to quickly reference other messages while composing.
+ _________________________________________________________________
+
+Message Status
+
+ The c-client library allows for several flags or status marks to be set for
+ each message. _Alpine_ uses four of these flags: UNSEEN, DELETED, ANSWERED,
+ and FLAGGED. The N in _Alpine_'s FOLDER INDEX means that a message is
+ unseen-it has not been read from this folder yet. The D means that a message
+ is marked for deletion. Messages marked with D are removed when the user
+ _Expunges_ the folder (which usually happens when the folder is closed or the
+ user quits _Alpine_). The A in _Alpine_'s FOLDER INDEX means that the
+ message has been replied-to. The * in _Alpine_'s FOLDER INDEX means that the
+ message has been ``flagged'' as important. That is, the user used the _Flag_
+ command to turn the FLAGGED flag on. This flag can mean whatever the user
+ wants it to mean. It is just a way to mark some messages as being different
+ from others. It will usually probably be used to mark a message as somehow
+ being ``important''. For Berkeley format folders, the message status is
+ written into the email folder itself on the header lines marked Status: and
+ X-Status.
+
+ It is also possible for a user to define their own flags in addition to the
+ standard system flags above. In _Alpine_ these user defined flags are called
+ Keywords.
+ _________________________________________________________________
+
+MIME: Reading a Message
+
+ _Alpine_ should be able to handle just about any MIME message. When a MIME
+ message is received, _Alpine_ will display a list of all the parts, their
+ types and sizes. It will display the attachments when possible and
+ appropriate and allow users to _Save_ all other attachments.
+
+ _Alpine_ honors the "mailcap" configuration system for specifying external
+ programs for handling attachments. The mailcap file maps MIME attachment
+ types to the external programs loaded on your system which can display
+ and/or print the file. A sample mailcap file comes bundled with the _Alpine_
+ distribution. It includes comments which explain the syntax you need to use
+ for mailcap. With the mailcap file, any program (mail readers, newsreaders,
+ WWW clients) can use the same configuration for handling MIME-encoded data.
+
+ If a MAILCAPS environment variable is defined, _Alpine_ will use that to
+ look for one or more mailcap files, which are combined. In the absence of
+ MAILCAPS, Unix _Alpine_ will look for a personal mailcap file in ~/.mailcap
+ and combine that with a system-wide file in /etc/mailcap. _PC-Alpine_ will
+ look for a file named MAILCAP in the same directory as the PINERC file,
+ and/or the directory containing the ALPINE.EXE executable.
+
+ Messages which include _rich text_ or _enriched text_ in the main body will
+ be displayed in a very limited way (it will show bold and underlining).
+
+ If _Alpine_ sees a MIME message part tagged as type IMAGE, and _Alpine_'s
+ image-viewer configuration variable is set, _Alpine_ will attempt to send
+ that attachment to the named image viewing program. In the case of UNIX
+ _Alpine_, the DISPLAY environment variable is checked to see if an X-terminal
+ is being used (which can handle the images). If the _image-viewer_ variable
+ is not set, _Alpine_ uses the _mailcap_ system to determine what to do with
+ IMAGE types, just as it does for any other non-TEXT type, e.g. type
+ APPLICATION. For MIME's generic "catch all" type, APPLICATION/OCTET-STREAM,
+ the _mailcap_ file will probably not specify any action, but _Alpine_ users
+ may always _Save_ any MIME attachment to a file.
+
+ MIME type "text/plain" is handled a little bit differently than the other
+ types. If you are viewing the main body part in the MESSAGE TEXT viewing
+ screen, then _Alpine_ will use its internal viewer to display it. This
+ happens even if there is a mailcap description which matches this particular
+ type. However, if you view a part of type "text/plain" from the ATTACHMENT
+ INDEX screen, then _Alpine_ will check the mailcap database for a matching
+ entry and use it in preference to its internal viewer.
+
+ Some text attachments, specifically those which are just other email
+ messages forwarded as MIME messages, are displayed as part of the main body
+ of the message. This distinction allows easy display when possible (the
+ forward as MIME case) and use of an attachment viewer when that is desirable
+ (the plain text file attachment case).
+
+ If the parts of a multipart message are alternate versions of the same thing
+ _Alpine_ will select and display the one best suited. For parts of type
+ "message/external-body", the parameters showing the retrieval method will be
+ displayed, and the retrieval process is automated. Messages of type
+ "message/partial" are not supported.
+ _________________________________________________________________
+
+MIME: Sending a Message
+
+ There are two important factors when trying to include an attachment in a
+ message: encoding and labeling. _Alpine_ has rules for both of these which
+ try to assure that the message goes out in a form that is robust and can be
+ handled by other MIME mail readers.
+
+ MIME has two ways of encoding data-Quoted-Printable and Base64.
+ Quoted-Printable leaves the ASCII text alone and only changes 8-bit
+ characters to "=" followed by the hex digits. For example, "=09" is a tab.
+ It has the advantage that it is mostly readable and that it allows for end
+ of line conversions between unlike systems. Base64 encoding is similar to
+ _uuencode_ or _btoa_ and just encodes a raw bit stream. This encoding is
+ designed to get text and binary files through even the most improperly
+ implemented and configured gateways intact, even those that distort
+ uuencoded data.
+
+ _All_ attachments are encoded using Base64 encoding. This is so that the
+ attachment will arrive at the other end looking exactly like it did when it
+ was sent. Since Base64 is completely unreadable except by MIME-capable
+ mailers or programs, there is an obvious tradeoff being made here. We chose
+ to ensure absolutely reliable transport of attachments at the cost of
+ requiring a MIME-capable mailer to read them. If the user doesn't want
+ absolute integrity he or she may always _include_ text (with the _^R_
+ command) in the body of a message instead of attaching it. With this policy,
+ the only time quoted-printable encoding is used is when the main body of a
+ message includes special foreign language characters.
+
+ When an attachment is to be sent, _Alpine_ sniffs through it to try to set
+ the right label (content-type and subtype). An attachment with any lines
+ longer than 500 characters in it or more than 10% of the characters are
+ 8-bit it will be considered binary data. _Alpine_ will recognize (and
+ correctly label) a few special types including GIF, JPEG, PostScript, and
+ some audio formats. Another method which can be more robust and flexible for
+ determining the content-type and subtype is to base it on the file
+ extension. This method uses a MIME.Types File.
+
+ If it is not binary data (has only a small proportion of 8-bit characters in
+ it,) the attachment is considered 8-bit text. 8-bit text attachments are
+ labeled "text/plain" with charset set to the value of the user's
+ _keyboard-character-set_ variable. If an attachment is ASCII (no 8-bit
+ characters) and contains no control characters then it is considered plain
+ ASCII text. Such attachments are given the MIME label "text/plain;
+ charset=US-ASCII", regardless of the setting of the user's
+ _keyboard-character-set_ variable.
+
+ All other attachments are unrecognized and therefore given the generic MIME
+ label "application/octet-stream".
+ _________________________________________________________________
+
+New Mail Notification
+
+ _Alpine_ checks for new mail in the _INBOX_ and in the currently open folder
+ every two and a half minutes by default. This default can be changed in the
+ system-wide configuration file or at compile-time with the
+ --with-mailcheck-interval=VALUE configuration option. A user can change it
+ by changing the option mail-check-interval. A new mail check can be manually
+ forced by redrawing the screen with a _^L_.
+
+ When there is new mail, the message(s) will appear in the index, the screen
+ will beep, and a notice showing the sender and subject will be displayed. If
+ there has been more than one new message since you last issued a command to
+ _Alpine_, the notice will show the count of new messages and the sender of
+ the most recent one.
+ _________________________________________________________________
+
+NFS
+
+ It is possible to access mail folders on _NFS_ mounted volumes with
+ _Alpine_, but there are some drawbacks to doing this, especially in the case
+ of incoming-message folders that may be concurrently updated by _Alpine_ and
+ the system's mail delivery agent. One concern is that _Alpine_'s
+ user-contention locks don't work because _/tmp_ is usually not shared, and
+ even if it was, _flock()_ doesn't work across _NFS._
+
+ The implementation of the standard UNIX ".lock" file locking has been
+ modified to work with _NFS_ as follows. Standard hitching post locking is
+ used so first a uniquely named file is created, usually something like
+ _xxxx.host.time.pid._ Then a link to it is created named _xxxx.lock_ where
+ the folder being locked is _xxxx._ This file constitutes the lock. This is a
+ standard UNIX locking scheme. After the link returns, a _stat(2)_ is done on
+ the file. If the file has two links, it is concluded that the lock succeeded
+ and it is safe to proceed.
+
+ In order to minimize the risks of locking failures via _NFS_, we strongly
+ recommend using IMAP rather than _NFS_ to access remote incoming message
+ folders, e.g. your _INBOX_. However, it is generally safe to access personal
+ saved-message folders via _NFS_ since it is unlikely that more than one
+ process will be updating those folders at any given time. Still, some
+ problems may occur when two _Alpine_ sessions try to access the same mail
+ folder from different hosts without using IMAP. Imagine the scenario:
+ _Alpine_-A performs a write that changes the folder. _Alpine_-B then attempts
+ to perform a write on the same folder. _Alpine_-B will get upset that the
+ file has been changed from underneath it and abort operations on the folder.
+ _Alpine_-B will continue to display mail from the folder that it has in its
+ internal cache, but it will not read or write any further data. The only
+ thing that will be lost out of the _Alpine_-B session when this happens is
+ the last few status changes.
+
+ If other mail readers besides _Alpine_ are involved, all bets are off.
+ Typically, mailers don't take any precautions against a user opening a
+ mailbox more than once and no special precautions are taken to prevent _NFS_
+ problems.
+ _________________________________________________________________
+
+Printers and Printing
+
+ UNIX _Alpine_ can print to the standard UNIX line printers or to generic
+ printers attached to ANSI terminals using the escape sequences to turn the
+ printer on and off. The user has a choice of three printers in the
+ configuration.
+
+ The first setting, _attached-to-ansi_, makes use of escape sequences on
+ ANSI/VT100 terminals. It uses "<ESC>[5i" to begin directing all output sent
+ to the terminal to the printer and then "<ESC>[4i" to return to normal.
+ _Alpine_ will send these escape sequences if the printer is set to
+ _attached-to-ansi._ This works with most ANSI/VT100 emulators on Macs and PCs
+ such as kermit, NCSA telnet, VersaTerm Pro, and WinQVT. Various terminal
+ emulators implement the print feature differently. There is also a closely
+ related method called _attached-to-ansi-no-formfeed_ which is the same
+ except for the lack of formfeed character at the end of the print job.
+
+ _Attached-to-wyse_ and _attached-to-wyse-no-formfeed_ are very similar to
+ "attached-to-ansi". The only difference is in the control characters sent to
+ turn the printer on and off. The Wyse version uses Ctrl-R for on, and Ctrl-T
+ for off.
+
+ The second selection is the standard UNIX print command. The default is
+ _lpr_, but it can be changed on a system basis to anything so desired in
+ /usr/local/lib/pine.conf.
+
+ The third selection is the user's personal choice for a UNIX print command.
+ The text to be printed is piped into the command. _Enscript_ or _lpr_ with
+ options are popular choices. The actual command is retained even if one of
+ the other print selections is used for a while.
+
+ Both the second and third sections are actually lists of possible commands
+ rather than single commands.
+
+ If you have a PostScript printer attached to a PC or Macintosh, then you
+ will need to use a utility called _ansiprt_ to get printouts on your
+ printer. _Ansiprt_ source code and details can be found in the ./contrib
+ directory of the _Alpine_ distribution.
+ _________________________________________________________________
+
+Save and Export
+
+ _Alpine_ users get two options for moving messages in _Alpine_: _Save_ and
+ _Export_. _Save_ is used when the message should remain ``in the _Alpine_
+ realm.'' Saved messages include the complete header (including header lines
+ normally hidden by _Alpine_), are placed in a _Alpine_ folder collection and
+ accumulate in a standard folder format which _Alpine_ can read. In contrast,
+ the _Export_ command is used to write the contents of a message to a file
+ for use outside of _Alpine_. Messages which have been exported are placed in
+ the user's home directory (unless the feature use-current-dir is turned on),
+ not in a _Alpine_ folder collection. Unless FullHeaderMode is toggled on,
+ all delivery-oriented headers are stripped from the message. Even with
+ _Export_, _Alpine_ retains message separators so that multiple messages can
+ accumulate in a single file and subsequently be accessed as a folder. On
+ UNIX systems, the _Export_ command pays attention to the standard _umask_
+ for the setting of the file permissions.
+ _________________________________________________________________
+
+Sent Mail
+
+ _Alpine_'s default behavior is to keep a copy of each outgoing message in a
+ special "sent mail" folder. This folder is also called the fcc for "file
+ carbon copy". The existence, location and name of the sent mail folder are
+ all configurable. Sent mail archiving can be turned off by setting the
+ configuration variable default-fcc="". The sent mail folder is assumed to be
+ in the default collection for _Save_s, which is the first collection named
+ in folder-collections. The name of the folder can be chosen by entering a
+ name in _default-fcc_. With _PC-Alpine_, this can be a bit complicated. If
+ the default collection for _Save_s is local (DOS), then the _default-fcc_
+ needs to be SENTMAIL, which is syntax for a DOS file. However, if the
+ default collection for _Save_s is remote, then the _default-fcc_ needs to be
+ sent-mail to match the UNIX syntax.
+
+ The configuration variable fcc-name-rule also plays a role in selecting the
+ folder to save sent mail in.
+
+ A danger here is that the sent mail could grow without bound. For this
+ reason, we thought it useful to encourage the users to periodically prune
+ their sent mail folder. The first time _Alpine_ is used each month it will
+ offer to archive all messages sent from the month before. _Alpine_ also
+ offers to delete all the sent mail archive folders which are more than 1
+ month old. If the user or system has disabled sent mail archiving (by
+ setting the configuration variable _default-fcc=""_) there will be no
+ pruning question.
+ _________________________________________________________________
+
+Spell Checker
+
+ Both UNIX _Alpine_ and _PC-Alpine_ depend on the system for their spell
+ checking and dictionary. _Pico_, the text editor, uses the same spell
+ checking scheme as _Alpine_.
+
+ Lines beginning with ">" (usually messages included in replies) are not
+ checked. The message text to be checked is on the standard input and the
+ incorrect words are expected on the standard output.
+
+ The default spell checker is UNIX _spell_. You can replace this by setting
+ the speller configuration variable. A common choice for a superior
+ replacement is _ispell_.
+
+ _PC-Alpine_ relies on the aspell library being installed. Aspell is
+ independent of Alpine. The Windows version has traditionally been available
+ at http://aspell.net/win32/. You'll need to download and install both Aspell
+ and a precompiled dictionary. Aspell is provided in an installer package.
+ Dictionaries, to be installed after Aspell, are in '.exe' files to download
+ and run.
+ _________________________________________________________________
+
+Terminal Emulation and Key Mapping
+
+ UNIX _Alpine_ has been designed to require as little as possible from the
+ terminal. At the minimum, _Alpine_ requires cursor positioning, clear to end
+ of line, and inverse video. Unfortunately, there are terminals that are
+ missing some of these such as a vt52. _Alpine_ makes no assumptions as to
+ whether the terminal wraps or doesn't wrap. If the terminal has other
+ capabilities it may use some of them. _Alpine_ won't run well on older
+ terminals that require a space on the screen to change video attributes,
+ such as the Televideo 925. One can get around this on some terminals by
+ using "protected field" mode. The terminal can be made to go into protected
+ mode for reverse video, and then reverse video is assigned to protected
+ mode.
+
+ _Alpine_ handles screens of most any size and resizing on the fly. It
+ catches SIGWINCH and does the appropriate thing.
+
+ On the input side of things, _Alpine_ uses all the standard keys, most of
+ the control keys and (in function-key mode) the function keys. _Alpine_
+ avoids certain control keys, specifically ^S, ^Q, ^H, and _^\_ because they
+ have other meanings outside of _Alpine_ (they control data flow, etc.) _^H_
+ is treated the same as the _delete_ key, so the _backspace_ or _delete_ keys
+ always work regardless of any configuration. There is a feature
+ _compose-maps-delete-key-to-ctrl-d_ which makes the delete key behave like ^D
+ rather than ^H (deletes current character instead of previous character).
+
+ Sometimes a communications program or communications server in between you
+ and the other end will eat certain control characters. There is a
+ work-around when you need it. If you type two escape characters followed by
+ a character that will be interpreted as the character with the control key
+ depressed. For example, _ESC ESC T_ is equivalent to _^T_.
+
+ When a function key is pressed and _Alpine_ is in regular (non-function key)
+ mode, _Alpine_ traps escape sequences for a number of common function keys
+ so users don't get an error message or have an unexpected command executed
+ for each character in the function key's escape sequence. _Alpine_ expects
+ the following escape sequences from terminals defined as VT100:
+
+ ANSI/VT100
+ F1: <ESC>OP
+ F2: <ESC>OQ
+ F3: <ESC>OR
+ F4: <ESC>OS
+ F5: <ESC>Op
+ F6: <ESC>Oq
+ F7: <ESC>Or
+ F8: <ESC>Os
+ F9: <ESC>Ot
+ F10: <ESC>Ou
+ F11: <ESC>Ov
+
+ Arrow keys are a special case. _Alpine_ has the escape sequences for a
+ number of conventions for arrow keys hard coded and does not use _termcap_
+ to discover them. This is because _termcap_ is sometimes incorrect, and
+ because many users have PC's running terminal emulators that don't conform
+ exactly to what they claim to emulate. There is a feature called
+ termdef-takes-precedence which can be set to cause the _termcap_ or
+ _terminfo_ definitions to be used instead of the built in definitions. Some
+ arrow keys on old terminals send single control characters like _^K_ (one
+ even sends _^\_). These arrow keys will not work with _Alpine_. The most
+ popular escape sequences for arrow keys are:
+
+ Up: <ESC>[A <ESC>?x <ESC>A <ESC>OA
+ Down: <ESC>[B <ESC>?r <ESC>B <ESC>OB
+ Right: <ESC>[C <ESC>?v <ESC>C <ESC>OC
+ Left: <ESC>[D <ESC>?t <ESC>D <ESC>OD
diff --git a/doc/tech-notes/Makefile b/doc/tech-notes/Makefile
new file mode 100644
index 00000000..ea7d19ef
--- /dev/null
+++ b/doc/tech-notes/Makefile
@@ -0,0 +1,70 @@
+# $Id: Makefile,v 1.6 1998/05/11 18:33:22 skramer Exp $
+#
+# T H E P I N E M A I L S Y S T E M
+#
+# Laurence Lundblade and Mike Seibel
+# Networks and Distributed Computing
+# Computing and Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, Washington, 98195, USA
+# Internet: lgl@CAC.Washington.EDU
+# mikes@CAC.Washington.EDU
+#
+# Please address all bugs and comments to "pine-bugs@cac.washington.edu"
+#
+#
+# Pine and Pico are registered trademarks of the University of Washington.
+# No commercial use of these trademarks may be made without prior written
+# permission of the University of Washington.
+#
+# Pine, Pico, and Pilot software and its included text are Copyright
+# 1989-1996 by the University of Washington.
+#
+# The full text of our legal notices is contained in the file called
+# CPYRIGHT, included with this distribution.
+#
+#
+# Pine is in part based on The Elm Mail System:
+# ***********************************************************************
+# * The Elm Mail System - Revision: 2.13 *
+# * *
+# * Copyright (c) 1986, 1987 Dave Taylor *
+# * Copyright (c) 1988, 1989 USENET Community Trust *
+# ***********************************************************************
+#
+#
+# These variables are specific to the University of Washington's
+# WWW server environment -- modify as needed for your own:
+# WWWDIR: location for HTML files on WWW server
+# PNUTS: script to run pnuts navigation bar generator; for details on PNUTS,
+# see: http://hopf.math.nwu.edu/docs/utility.html
+# If you do not have the PNUTS program, set variable to /bin/true
+
+SOURCES= index.html introduction.html background.html installation.html \
+ cmd-line.html config.html config-notes.html low-level.html
+
+ALLSRC= $(SOURCES) for.pnuts
+
+TXTS= index.txt introduction.txt background.txt installation.txt \
+ cmd-line.txt config.txt config-notes.txt low-level.txt
+
+HTML2TXT= lynx -underscore -nolist -dump
+
+WWWDIR= /usr/local/wwwdev/world/pine/tech-notes
+
+PNUTS= $(WWWDIR)/pn4tn
+
+.SUFFIXES: .html .txt
+
+tech-notes.txt: $(TXTS)
+ cat $(TXTS) > tech-notes.txt
+ rm $(TXTS)
+
+www: $(ALLSRC)
+ cp $(ALLSRC) $(WWWDIR)
+ ( cd $(WWWDIR) ; $(PNUTS) )
+
+.html.txt:
+ $(HTML2TXT) $< > $@
+
diff --git a/doc/tech-notes/background.html b/doc/tech-notes/background.html
new file mode 100644
index 00000000..ac4d91af
--- /dev/null
+++ b/doc/tech-notes/background.html
@@ -0,0 +1,350 @@
+<HTML><HEAD><TITLE>Alpine Technical Notes: Background Details</TITLE></HEAD><BODY>
+<H1>Background Details</H1>
+
+<H2><A NAME="DNS">Domain Names</A></H2>
+
+<BR><P>
+
+Domain names are used to uniquely name each host on the Internet. A
+domain name has a number of parts separated by periods. Each label
+represents a level in the hierarchy. An example of a name is:
+
+<BLOCKQUOTE><CODE>
+olive.cac.washington.edu
+</CODE></BLOCKQUOTE>
+
+In this domain name the top-level label is <EM>edu</EM>, indicating it is
+at an educational institution, the second-level label is
+<EM>washington</EM>, indicating the University of Washington.
+<EM>cac</EM> is a specific department within the University of Washington,
+and <EM>olive</EM> is the host name. The top-level names are assigned by
+Internet organizations, and other names are assigned at the appropriate
+level. The Domain Name Service, DNS, is the distributed database used to
+look up these names. <P>
+
+<EM>Alpine</EM> relies on domain names in multiple places.
+A domain name is embedded
+into the message-id line generated for each piece of email. A domain name
+is needed to contact an IMAP server to get access to remote INBOXes and
+folders. Most importantly, domain names are needed to construct the From:
+line of your outgoing messages so that people on the Internet will be able
+to get email back to you. <P>
+
+On UNIX systems, you can set the domain via the <A
+HREF="config.html#user-domain"><EM>user-domain</EM></A>
+variable in the <EM>Alpine</EM> configuration file, or rely on the file
+<CODE>/etc/hosts</CODE> which usually sets the name of the local host.
+While <EM>Alpine</EM> can often deliver email without the domain name
+being properly
+configured, it is best to have this set correctly. Problems can usually be
+solved by adjusting the system's entry in the <CODE>/etc/hosts</CODE>
+file. The fully-qualified name should be listed before any abbreviations.
+For example,
+
+<BLOCKQUOTE><CODE>
+128.95.112.99 olive.cac.washington.edu olive
+</CODE></BLOCKQUOTE>
+
+is preferred over
+
+<BLOCKQUOTE><CODE>
+128.95.112.99 olive olive.cac.washington.edu
+</CODE></BLOCKQUOTE>
+<P>
+
+On PCs, the task of configuring the domain name is a bit different. Often
+times PCs do not have domain names-they have <EM>IP addresses</EM>. IP
+addresses are the numbers which uniquely identify a computer on the
+network. The way you configure your IP address depends on the networking
+software which you use on the PC. You can refer to the documentation
+which came with your networking software or see the <A
+HREF="installation.html#install-pc">PC specific installation notes</A> for
+help configuring the IP address with your network software. <P>
+
+With PCs, it is vital that users set the variable <A
+HREF="config.html#user-domain"><EM>user-domain</EM></A> in the <EM>Alpine</EM>
+configuration file (<CODE>PINERC</CODE>). <P>
+
+Details on configuring <EM>Alpine</EM> with correct domain names can be
+found in the <A HREF="config-notes.html#domain">Domain Settings</A>
+section of this document. <P>
+
+<HR>
+
+<H2><A NAME="rfc2822">RFC 2822 Compliance</A></H2>
+
+<EM>Alpine</EM> tries to adhere
+to <A HREF="ftp://ftp.isi.edu/in-notes/rfc2822.txt">RFC 2822</A>
+fairly strictly. <P>
+
+As far as outgoing email is concerned, <EM>Alpine</EM> fully-qualifies addresses
+whenever possible. They are even displayed in fully-qualified form on the
+terminal as the user composes a message. This makes addresses more clear
+and gives a hint to the user that the network extends beyond the local
+organization.
+<EM>Alpine</EM> implements fully-qualified domain names by tacking on
+the local domain to all unqualified addresses which a user types in. Any
+address which does not contain an "@" is considered unqualified. <P>
+
+The format for addresses allows for spaces and special characters in
+the full name of an address. For this reason, commas are required to
+separate addresses. If any special characters as defined in RFC 2822
+appear in the full name, quotes are required around the address. <EM>Alpine</EM>
+will insert the quotes automatically if needed. The common cases where this happens
+are with periods after initials and parentheses. <P>
+
+<EM>Alpine</EM> expects dates to be in the standard RFC 822 format
+which is something like:
+
+<PRE>
+ [www, ] dd mmm yy hh:mm[:ss] [timezone]
+</PRE>
+
+It will attempt to parse dates that are not in this format. When an
+unparsable date is encountered it is shown as question marks
+in the FOLDER INDEX screen. <P>
+
+<HR>
+
+<H2><A NAME="SMTP">SMTP and Sendmail</A></H2>
+
+<EM>Alpine</EM> is a <EM>user agent</EM> not a <EM>message transfer agent</EM> (MTA). In
+plain English, that means <EM>Alpine</EM> does not know how to interact with other
+computers on the Internet to deliver or receive email. What <EM>Alpine</EM> does
+know how to do is help users read, organize and create email. The "dirty
+work" of delivering and accepting email is handled by other programs. <P>
+
+All outgoing email is delivered to an SMTP server
+or to a mail transfer agent.
+A common mail transfer agent is <CODE>sendmail</CODE>.
+The usual method of delivery used by <EM>Alpine</EM> is to use either a
+local or a remote SMTP server.
+
+<P> The selection of which MTA to use depends on the settings of
+<A HREF="config.html#smtp-server"><EM>smtp-server</EM></A>,
+<A HREF="config.html#sendmail-path"><EM>sendmail-path</EM></A>,
+and compile-time options.
+The first MTA specified in the following list is used:
+
+<OL>
+
+<LI><EM>sendmail-path</EM> in
+<CODE>/usr/local/lib/pine.conf.fixed</CODE>
+
+<LI><EM>smtp-server</EM> in <CODE>/usr/local/pine.conf.fixed</CODE>
+
+<LI><EM>sendmail-path</EM> specified on the command line.
+
+<LI><EM>smtp-server</EM> specified on the command line.
+
+<LI><EM>sendmail-path</EM> in the user's <CODE>.pinerc</CODE> file.
+
+<LI><EM>smtp-server</EM> in the user's <CODE>.pinerc</CODE> file.
+
+<LI><EM>sendmail-path</EM> in <CODE>/usr/local/lib/pine.conf</CODE>
+
+<LI><EM>smtp-server</EM> in <CODE>/usr/local/pine.conf</CODE>
+
+<LI><CODE>DF_SENDMAIL_PATH</CODE> defined at compile time.
+
+<LI><CODE>SENDMAIL</CODE> and <CODE>SENDMAILFLAGS</CODE> defined at
+compile time.
+
+</OL><P>
+
+If the <EM>sendmail-path</EM> form is used, a child process is forked,
+and the specified command is executed with the message passed on standard
+input. Standard output is then passed back and displayed for the user.
+<EM>NOTE: The program MUST read the message to be posted on standard
+input, AND operate in the style of sendmail's "-t" option. This
+method is not recommended unless there are special reasons you
+want to do this. </EM><P>
+
+If an <EM>smtp-server</EM> is specified,
+<EM>Alpine</EM> operates as an SMTP client. SMTP stands for <EM>Simple Mail
+Transfer Protocol</EM>; it specifies the rules by which computers on the
+Internet pass email to one another. In this case, <EM>Alpine</EM> passes outgoing
+email messages to a designated SMTP server instead of to a mail transfer
+program on the local machine. A program on the server then takes care of
+delivering the message. To make <EM>Alpine</EM> operate as an SMTP client, the
+<A HREF="config.html#smtp-server"><EM>smtp-server</EM></A> variable
+must be set to the IP address or
+host name of the SMTP server within your organization. This variable accepts a
+comma separated list of servers, so you can specify multiple alternate SMTP servers.
+<EM>PC-Alpine</EM> only runs as an SMTP client so the <EM>smtp-server</EM>
+option is mandatory. <P>
+
+For UNIX <EM>Alpine</EM>,
+if neither <EM>smtp-server</EM> or <EM>sendmail-path</EM> is set,
+the default <CODE>sendmail</CODE> program is
+invoked with the "<CODE>-bs -odb -oem</CODE>" flags, and the message
+is sent using the SMTP protocol.
+<P>
+
+<HR>
+
+<H2><A NAME="IMAP">Internet Message Access Protocol (IMAP)</A></H2>
+
+IMAP is a remote access protocol for message stores. <EM>Alpine</EM> uses IMAP to get
+at messages and folders which reside on remote machines. With IMAP,
+messages are kept on the server. An IMAP client (such as <EM>Alpine</EM>) can
+request specific messages, headers, message structures, message parts, etc. The client
+can also issue commands which delete messages from folders on the server.
+IMAP's closest kin is POP, the Post Office Protocol, which works by
+transferring an entire mailbox to the client where all the mail is kept.
+For a comparison of IMAP and POP, see the paper "<A
+HREF="http://www.imap.org/imap.vs.pop.brief.html">Comparing Two Approaches
+to Remote Mailbox Access: IMAP vs. POP</A>" by Terry Gray. A more
+detailed exploration of message access may be found in the paper "<A
+HREF="http://www.imap.org/imap.vs.pop.html"> Message Access Paradigms and
+Protocols</A>."
+<P>
+
+IMAP Features:
+
+<UL>
+
+<LI> Allows access to mail folders from more than one client computer.
+
+<LI> Works well over low-bandwidth lines because information is sent in
+small pieces as needed by the user. For example, only header information
+is sent to build index lists, and if someone sends a large audio file via
+MIME, you can choose when (or if) you want to get that part of the
+message.
+
+<LI> Email can be delivered and stored on a well-maintained and reliable
+server which is "always-up".
+
+<LI> Folders can be accessed and manipulated from anywhere on the
+Internet.
+
+<LI> Users can get to messages stored in different folders within the same
+<EM>Alpine</EM> session.
+
+<LI> Allows use of IMAP server for searching and parsing.
+
+<LI> The latest revision of IMAP (IMAP4) also provides for disconnected
+operation, including resynchronization of message state between mail
+servers and message caches on clients. <EM>Alpine</EM> does not support this
+capability, however.
+
+</UL>
+
+IMAP4rev1 is described in <A
+HREF="ftp://ftp.isi.edu/in-notes/rfc3501.txt">RFC 3501</A>. Further
+information about IMAP may be obtained from the University of Washington's
+<A HREF="http://www.washington.edu/imap/">IMAP Information Center</A>.<P>
+
+<EM>Alpine</EM> is an IMAP4rev1 client.
+<P>
+
+<HR>
+
+<H2><A NAME="MIME">Multipurpose Internet Mail Extensions (MIME)</A></H2>
+
+MIME is a way of encoding a multipart message structure into a standard
+Internet email message. The parts may be nested and may be of seven
+different types: Text, Audio, Image, Video, Message, Application and
+Multipart (nested). The MIME specification allows email programs such as
+<EM>Alpine</EM> to reliably and simply exchange binary data (images, spreadsheets,
+etc.). MIME includes support for international character sets, tagging each
+part of a message with the character set it is written in, and providing
+7-bit encoding of 8-bit character sets. <P>
+
+The MIME standard was officially published in June of 1992 as <A
+HREF="ftp://ftp.isi.edu/in-notes/rfc1341.txt">RFC 1341</A> and subsequently
+revised in <A HREF="ftp://ftp.isi.edu/in-notes/rfc2045.txt">RFC 2045</A>
+when it became a full Internet Standard. <EM>Pine</EM> 3.0 was one of the first
+email programs to Implement MIME. Now, there are dozens of commercial and
+freely available MIME-capable email programs. In addition, MIME is being
+added to newsreaders so MIME messages can be posted and read in USENET
+newsgroups. <P>
+
+The MIME standard also includes support for non-ASCII text in message
+headers through the extensions described in <A
+HREF="ftp://ftp.isi.edu/in-notes/rfc1342.txt">RFC 1342</A> and subsequently
+revised in <A HREF="ftp://ftp.isi.edu/in-notes/rfc2047.txt">RFC 2047</A>. <P>
+
+An actual MIME message looks something like this:
+
+<PRE>
+Date: Tue, 12 Mar 1996 15:39:35 -0800 (PST)
+From: David L Miller &lt;dlm@cac.washington.edu&gt;
+To: David L Miller &lt;dlm@cac.washington.edu&gt;
+Subject: =?iso-8859-1?Q?Test_MIME_message_with_RFC-1522_headers_=28=E1?= =?iso-8859-1?Q?=E2=E3=29?=
+Message-Id: &lt;Pine.ULT.3.92.960312150851.21583I-101000@shiva2.cac.washington.edu&gt;
+Mime-Version: 1.0
+Content-Type: MULTIPART/MIXED; BOUNDARY="0-1737669234-826673975=:21583"
+Content-Id: &lt;Pine.ULT.3.92.960312153928.21583O@shiva2.cac.washington.edu&gt;
+
+ This message is in MIME format. The first part should be readable text,
+ while the remaining parts are likely unreadable without MIME-aware tools.
+ Send mail to mime@docserver.cac.washington.edu for more info.
+
+--0-1737669234-826673975=:21583
+Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
+Content-ID: &lt;Pine.ULT.3.92.960312153104.21583L@shiva2.cac.washington.edu&gt;
+
+The text of the message would go here. It is readable if
+one doesn't mind wading around a little bit of the MIME
+formatting. After this is a binary file in base 64
+encoding.
+
+|\ | |\/| David L. Miller dlm@cac.washington.edu (206) 685-6240
+|/ |_ | | Software Engineer, Pine Development Team (206) 685-4045 (FAX)
+University of Washington, Networks & Distributed Computing, JE-20
+4545 15th Ave NE, Seattle WA 98105, USA
+
+--0-1737669234-826673975=:21583
+Content-Type: APPLICATION/ZIP; NAME="test.zip"
+Content-Transfer-Encoding: BASE64
+Content-ID: &lt;Pine.ULT.3.92.960312153638.21583N@shiva2.cac.washington.edu&gt;
+Content-Description: Test Attachment
+
+UEsDBBQAAAAIAGh8bCBbZKT4ygIAAHgFAAAEAAAAdGVzdIVUX2vbMBB/16c4
+9rSBNyjsYX1UHSUROLInycv2qNhKI5ZYxlLa5dvvpDRLw6CFgJF09/t3Rxo3
+WDBDD43rPJjJQpxMbw9m+h3AbyHuLLSDe7JTcPGUbtYm7NzwGP3wBYQnnT8c
+7NQ5s4djsC8t4QbmYE6wsfjpLTy7uPPHCOPk/ATPk4vRDmS008GF4PzwPich
+zY3m4LfxOQlPNy4GcEO3P/a2h2j/xGyp9ONpco+7CHf33+4/393ff4XNibzL
+c1UVfXJXQIdIBRx877b4TYy9C3Fym2NEyzsX/pNDet8dD3aIJiagLbo2wwnG
+4zT6cK66ZLK1NhH9J4tcZQEy7OxkNyd4nMwQbV9glP7JZb87E3O32fgnm7We
+XQ8+us4SM47WTCkgMPt9enc2ZAW5c+Pj7o32l0IXXk/r8pSRE3A4jqOfIqqF
+G+PFlSdRDOaQduXNESTwtDcYfJ8191gWXUjYmOJ43Oxdh11JTzRuSPcY37+B
+vNqmf0O5RB1G27mt64rLCp4X8pW1L6BvxunCeYHNk3F7s9lb+GAwyvAhOyNE
+Lxm0gv9gUnH9C+o5rKlacrHQtYAZV2VF+UoBrSp8kJIKzZkqgP1sJFMKagl8
+1VSczQqy5noJki2onIGuQS+5AlXPNfaxArgoq3aGwJDq6lZDxVdcU82RKMG/
+4JArTVKzYrJc4pE+8CoJpGIGc65FIp8jO4WGSs3LtqISmlY2tUKyVMUFETWw
+H0xoUMvE8KbXB4aC6EPFzrDiF6iGlZxWBeFixiUrdXJb1kKx7y2C4hPM6Iou
+WI4hdVyO6yXVqkZqiXmottLJ9lzWK1LVKttqk8oZ1TS1NrJGS5jqeslQI0aK
+ieCvzNlgNZJqiccCc5WafLxmKdii4gsmSvYpISkteamzkRwXJiG5SoUpcERK
+8xIE8QQ7o+eh5WAUy1qYRP8rioip/maI+OfyF1BLAQIUAxQAAAAIAGh8bCBb
+ZKT4ygIAAHgFAAAEAAAAAAAAAAEAAACkgQAAAAB0ZXN0UEsFBgAAAAABAAEA
+MgAAAOwCAAAAAA==
+--0-1737669234-826673975=:21583--
+
+</PRE>
+
+For details about <EM>Alpine</EM>'s implementation of MIME, see the two MIME sections
+"<A HREF="low-level.html#MIME-read">MIME: Reading a Message</A>" and
+"<A HREF="low-level.html#MIME-send">MIME: Sending a Message</A>" later
+in this document. <P>
+
+<HR>
+
+<H2><A NAME="collections">Folder Collections</A></H2>
+
+Folder Collections are <EM>Alpine</EM>'s way of dealing with more than a single group
+of folders. <P>
+
+For a more complete description of Folder Collections, see the section on
+"<A HREF="config-notes.html#collections">Syntax for Collections</A>." <P>
+
+The <EM>Alpine</EM> distribution is designed to require as little configuration and
+effort at compile time as possible. Still, there are some <EM>Alpine</EM> behaviors
+which are set at the time you compile <EM>Alpine</EM>. For each of these, there is a
+reasonable (our opinion) default built into the code, so most systems
+administrators will have no need for these steps. <P>
+
+<!-- pnuts -->
+</BODY>
+</HTML>
diff --git a/doc/tech-notes/cmd-line.html b/doc/tech-notes/cmd-line.html
new file mode 100644
index 00000000..cb6d1efb
--- /dev/null
+++ b/doc/tech-notes/cmd-line.html
@@ -0,0 +1,553 @@
+<HTML><HEAD><TITLE>Alpine Technical Notes: Command Line Arguments</TITLE></HEAD><BODY>
+<H1>Command Line Arguments</H1>
+
+<H2><A NAME="alpine">Alpine</A></H2>
+
+<EM>Alpine</EM> and <EM>PC-Alpine</EM> can accept quite a few
+command-line arguments.
+Many of these arguments overlap with variables
+in the <EM>Alpine</EM> configuration file.
+If there is a difference, then a flag set in the command line takes precedence.
+Both <EM>Alpine</EM> and <EM>PC-Alpine</EM> expect command line arguments (other
+than addresses) to be
+preceded by the "-" (dash) as normally used by UNIX programs.
+<P>
+
+<DL COMPACT>
+
+<DT> <EM>[addresses]</EM>
+
+<DD> Send-to: If you give <EM>Alpine</EM> an argument or arguments which
+do not begin with a dash, <EM>Alpine</EM> treats them as email addresses.
+<EM>Alpine</EM> will startup in
+the composer with a message started to the addresses specified.
+Once the message is sent, the <EM>Alpine</EM> session closes.
+Standard input redirection is allowed.
+Separate multiple addresses with a space between them.
+Addresses are placed in the &quot;To&quot; field only.
+<P>
+
+<DT> &lt; <EM>file</EM>
+
+<DD> <EM>Alpine</EM> will startup in the composer with <EM>file</EM> read
+into the body of the message.
+Once the message is sent, the <EM>Alpine</EM> session closes.
+<P>
+
+<DT> -attach <EM>file</EM>
+
+<DD> Go directly into composer with given file attached.
+<P>
+
+<DT> -attachlist <EM>file-list</EM>
+
+<DD> Go directly into composer with given files attached.
+This must be the last option on the command line.
+<P>
+
+<DT> -attach_and_delete <EM>file</EM>
+
+<DD> Go directly into composer with given file attached, delete when finished.
+<P>
+
+<DT> -aux <EM>local_directory</EM>
+
+<DD> <EM>PC-Alpine</EM> only.
+This tells <EM>PC-Alpine</EM> the local directory to use for storing auxiliary
+files, like debug files, address books, and signature files. The pinerc may
+be local or remote.
+<P>
+
+<DT> -nosplash
+
+<DD> <EM>PC-Alpine</EM> only.
+This tells <EM>PC-Alpine</EM> to not display the splash screen upon startup.
+This may be helpful for certain troubleshooting or terminal server scenarios.
+<P>
+
+<DT> -bail
+
+<DD> If the personal configuration file doesn't already exist, exit.
+This might be useful if the configuration file is accessed using some
+remote filesystem protocol. If the remote mount is missing this will cause
+<EM>Alpine</EM> to quit instead of creating a new pinerc.
+<P>
+
+<DT> -c <EM>n</EM>
+
+<DD> When used with the <CODE>-f</CODE> option, apply the <EM>n</EM>th context.
+This is used when there are multiple folder collections (contexts) and you
+want to open a folder not in the primary collection.
+<P>
+
+<DT> -conf
+
+<DD> Configuration: Prints a sample system configuration file to the
+screen or standard output. To generate an initial system configuration
+file, execute
+
+<PRE><CODE>
+ alpine -conf > /usr/local/lib/pine.conf
+</CODE></PRE>
+<P>
+
+To generate a system configuration file using settings from an old
+system configuration file, execute
+
+<PRE><CODE>
+ alpine -P old-pine.conf -conf > /usr/local/lib/pine.conf
+</CODE></PRE>
+<P>
+A system configuration file is not required.
+<P>
+
+<DT> -convert_sigs <EM>-p pinerc</EM>
+
+<DD> Convert signatures contained in signature files into literal signatures.
+<P>
+
+<DT> <A NAME="copy_abook">-copy_abook <EM>&lt;local_abook_file&gt; &lt;remote_abook_folder&gt;</EM>
+
+<DD> Copy an address book file to a remote address book folder.
+If the remote folder doesn't exist, it will be created.
+If it exists but the first message in the folder isn't a remote address
+book header message, the copy will be aborted.
+This flag will not usually be used by a user.
+Instead, the user will create a remote address book from within <EM>Alpine</EM>
+and copy entries from the local address book by using aggregate Save in
+the address book screen.
+<P>
+
+<DT> <A NAME="copy_pinerc">-copy_pinerc <EM>&lt;local_pinerc_file&gt; &lt;remote_pinerc_folder&gt;</EM>
+
+<DD> Copy a pinerc configuration file to a remote pinerc folder.
+If the remote folder doesn't exist, it will be created.
+If it exists but the first message in the folder isn't a remote pinerc
+header message, the copy will be aborted.
+This flag may be useful to users who already have a local pinerc file and
+would like to convert it to a remote pinerc folder and use that instead.
+This gives a way to bootstrap that conversion without having to manually
+reset all of the variables in the remote pinerc folder.
+<P>
+
+<DT> -d <EM>debug-level</EM>
+
+<DD> Debug Level: Sets the level of debugging information written by
+<EM>Alpine</EM>.
+<EM>Debug-level</EM> can be set to any integer 0-9.
+A debug level of 0 turns off debugging for the session.
+(Actually there are some levels higher than 9, but you probably don't
+want to see them. Sensitive authentication information is hidden at
+levels less than 10.)
+<P>
+
+<DT> -d <EM>keywords</EM>
+
+<DD> You may use a more detailed version of the debugging flag to set
+the debug level in separate parts of <EM>Alpine</EM>.
+The possibilities are flush, timestamp, imap=0..4, tcp, numfiles=0..31, and
+verbose=0..9.
+<EM>Flush</EM> causes debugging information to be flushed immediately to
+the debug file as it is written.
+<EM>Verbose</EM> is the general debugging verbosity level.
+<EM>Timestamp</EM> causes timestamps to be added to the debug file, which
+is useful when you are trying to figure out what is responsible for delays.
+<EM>Numfiles</EM> sets the number of debug files saved.
+<EM>Imap</EM> sets the debug level for the debugging statements related
+to the conversation with the IMAP server, and more generally, for the
+debugging related to <EM>Alpine</EM>'s interaction with the C-Client library.
+If <EM>imap</EM> is set higher than 4, sensitive authentication information
+will be included in the debug file.
+<EM>Tcp</EM> adds more TCP/IP debugging information.
+<P>
+
+<DT> -f <EM>folder</EM>
+
+<DD> Startup folder: <EM>Alpine</EM> will open this folder in place
+of the standard INBOX.
+<P>
+
+<DT> -F <EM>file</EM>
+
+<DD> Open named text file for viewing and forwarding.
+<P>
+
+<DT> -h
+
+<DD> Help: Prints the list of available command-line arguments to the
+screen.
+<P>
+
+<DT> -i
+
+<DD> <EM>Alpine</EM> will start up in the FOLDER INDEX
+screen instead of the MAIN MENU.
+<P>
+
+Configuration equivalent: <EM>initial-keystroke-list=i</EM>.
+<P>
+
+<DT> -I <EM>a,b,c,...</EM>
+
+<DD> Initial Keystrokes: <EM>Alpine</EM> will execute this comma-separated
+sequence of commands upon startup.
+This allows users to get <EM>Alpine</EM> to start in any
+of its menus/screens.
+You cannot include any input to the composer in the initial keystrokes.
+The key &lt;Return&gt; is represented by a ``CR'' in
+the keystroke list; the spacebar is designated by the letters ``SPACE''.
+Control keys are two character sequences beginning with ``^'', such as
+``^I''.
+A tab character is ``TAB''.
+Function keys are ``F1'' - ``F12'' and the arrow keys are ``UP'',
+``DOWN'', ``LEFT'', and ``RIGHT''.
+A restriction is that you can't mix function keys and character keys in this
+list even though you can, in some cases, mix them when running <EM>Alpine</EM>.
+A user can always use only <EM>character</EM> keys in the startup list even
+if he or she is using <EM>function</EM> keys normally, or vice versa.
+If an element in this list is a string of characters surrounded by double
+quotes (&quot;) then it will be expanded into the individual characters in
+the string, excluding the double quotes.
+<P>
+
+Configuration equivalent: <EM>initial-keystroke-list</EM>
+<P>
+
+<DT> -install
+
+<DD> For <EM>PC-Alpine</EM> only, this option prompts for some basic
+setup information, then exits.
+<P>
+
+<DT> -k
+
+<DD> Function-Key Mode: When invoked in this way, <EM>Alpine</EM> expects
+the input of commands to be function-keys.
+Otherwise, commands are linked to the regular character keys.
+<P>
+
+Configuration equivalent: <EM>use-function-keys</EM> included in
+<EM>feature-list</EM>.
+<P>
+
+<DT> -n <EM>n</EM>
+
+<DD> Message-Number: When specified, <EM>Alpine</EM> starts up in the
+FOLDER INDEX screen with the current message being the specified
+message number.
+<P>
+
+<DT> -nowrite_password_cache
+
+<DD> This tells <EM>Alpine</EM> to use the local password cache if there is one, but to
+never offer writing new passwords to the cache.
+<P>
+
+<DT> -o <EM>folder</EM>
+
+<DD> Opens the INBOX (or a folder specified via the -f argument) ReadOnly.
+<P>
+
+<DT> -p <EM>pinerc</EM>
+
+<DD> Uses the named file as the personal configuration file instead of
+<EM>~/.pinerc</EM> or the default PINERC search sequence <EM>PC-Alpine</EM> uses.
+Pinerc may be either a local file or a remote configuration folder.
+<P>
+
+<DT> -P <EM>pinerc</EM>
+
+<DD> Uses the named file as the system wide configuration file instead of
+<EM>/usr/local/lib/pine.conf</EM> on UNIX, or nothing on <EM>PC-Alpine</EM>.
+Pinerc may be either a local file or a remote configuration folder.
+<P>
+
+<DT> -passfile <EM>passfile</EM>
+
+<DD> This tells <EM>Alpine</EM> what file should be used as the password file.
+This should be a fully-qualified filename.
+<P>
+
+<DT> -pinerc <EM>file</EM>
+
+<DD> Output fresh pinerc configuration to <EM>file</EM>, preserving the
+settings of variables that the user has made.
+Use <EM>file</EM> set to ``-'' to make output go to standard out.
+<P>
+
+<DT> -r
+
+<DD> Restricted Mode: For UNIX <EM>Alpine</EM> only.
+<EM>Alpine</EM> in restricted mode can only send email to itself.
+Save and export are limited.
+<P>
+
+<DT> -registry <EM>cmd</EM>
+
+<DD> For <EM>PC-Alpine</EM> only, this option affects the values of
+<EM>Alpine</EM>'s registry entries.
+Possible values for <EM>cmd</EM> are set, noset, clear, clearsilent, and dump.
+<EM>Set</EM> will always reset <EM>Alpine</EM>'s registry
+entries according to its current settings.
+<EM>NoSet</EM> will never set any values in the registry, but it will
+still use the values already set in the registry.
+<EM>Clear</EM> will clear the registry values.
+<EM>Clearsilent</EM> will silently clear the registry values.
+<EM>Dump</EM> will display the values of current registry settings.
+Note that the dump command is currently disabled.
+Without the -registry option, <EM>PC-Alpine</EM> will write values into
+the registry only if there currently aren't any values set.
+<P>
+
+<DT> -sort <EM>key</EM>
+
+<DD> Sort-Key: Specifies the order messages will be displayed in for the
+FOLDER INDEX screen.
+<EM>Key</EM> can have the following values:
+arrival, date, subject, orderedsubj, thread, from, size, score, to, cc,
+arrival/reverse, date/reverse, subject/reverse, orderedsubj/reverse, thread/reverse,
+from/reverse, size/reverse, score/reverse, to/reverse, and cc/reverse.
+The default value is &quot;arrival&quot;.
+The <EM>key</EM> value reverse is equivalent to arrival/reverse.
+<P>
+
+Configuration equivalent: <EM>sort-key</EM>.
+<P>
+
+<DT> -supported
+
+<DD> Some options may or may not be supported depending on how <EM>Alpine</EM>
+was compiled.
+This is a way to determine which options are supported in the particular
+copy of <EM>Alpine</EM> you are using.
+<P>
+
+<DT> -install
+
+<DD> For <EM>PC-Alpine</EM> only, this option removes references to Alpine
+in Windows settings. The registry settings are removed and
+the password cache is cleared.
+<P>
+
+<DT> -url <EM>url</EM>
+
+<DD> Open the given URL.
+<P>
+
+<DT> -v
+
+<DD> Version: Print version information to the screen.
+<P>
+
+<DT> -version
+
+<DD> Version: Print version information to the screen.
+<P>
+
+<DT> -x <EM>exceptions_config</EM>
+
+<DD> Configuration settings in the exceptions config override your normal
+default settings.
+<EM>Exceptions_config</EM> may be either a local file or a remote pinerc folder.
+<P>
+
+<DT> -z
+
+<DD> Enable Suspend: When run with this flag, the key sequence ctrl-z
+will suspend the <EM>Alpine</EM> session.
+<P>
+
+Configuration equivalent: <EM>enable-suspend</EM> included in
+<EM>feature-list</EM>.
+<P>
+
+<DT> -<EM>option</EM>=<EM>value</EM>
+
+<DD> Assign <EM>value</EM> to the config option <EM>option</EM>.
+For example, <EM>-signature-file=sig1</EM> or
+<EM>-feature-list=signature-at-bottom</EM>.
+(Note: feature-list values are
+additive and features may be preceded with no- to turn them off).
+<P>
+
+</DL>
+<P>
+
+<H2><A NAME="pico">Pico</A></H2>
+
+The following command line options are supported in <EM>Pico</EM>:
+
+<DL>
+
+<DT> +<EM>n</EM>
+
+<DD> Causes <EM>Pico</EM> to be started with the cursor located <EM>n</EM>
+lines into the file. (Note: no space between "+" sign and number) <P>
+
+<DT> -a
+
+<DD> Display all files and directories, including those beginning
+with a period (.). <P>
+
+<DT> -b
+
+<DD> Enable the option to Replace text matches found using the
+"Where is" command. This now does nothing. Instead, the option is
+always turned on (as if the -b flag had been specified). <P>
+
+<DT> -d
+
+<DD> Rebind the "delete" key so the character the cursor is on is rubbed
+out rather than the character to its left. <P>
+
+<DT> -e
+
+<DD>Enable file name completion. <P>
+
+<DT> -f
+
+<DD> Use function keys for commands. <I>This option supported only in
+conjunction with UW Enhanced NCSA telnet.</I> <P>
+
+<DT> -g
+
+<DD> Enable "Show Cursor" mode in file browser. Cause cursor to be
+positioned before the current selection rather than placed at the lower
+left of the display. <P>
+
+<DT> -k
+
+<DD>Causes "Cut Text" command to remove characters from the cursor
+position to the end of the line rather than remove the entire line. <P>
+
+<DT> -m
+
+<DD> Enable mouse functionality. This only works when <EM>Pico</EM> is
+run from within an X Window System "xterm" window. <P>
+
+<DT>-n<EM>n</EM>
+
+<DD> The -n<EM>n</EM> option enables new mail notification. The
+<EM>n</EM> argument is optional, and specifies how often, in seconds, your
+mailbox is checked for new mail. For example, -n60 causes <EM>Pico</EM>
+to check for new mail once every minute. The default interval is 180
+seconds, while the minimum allowed is 30. (Note: no space between "n" and
+the number) <P>
+
+<DT> -o <EM>dir</EM>
+
+<DD> Sets operating directory. Only files within this directory are
+accessible. Likewise, the file browser is limited to the specified
+directory subtree. <P>
+
+<DT> -p
+
+<DD> Preserve the &quot;start&quot; and &quot;stop&quot; characters, typically Ctrl-Q
+ and Ctrl-S, which are sometimes used in communications paths to control data flow
+between devices that operate at different speeds.<P>
+
+<DT> -q
+
+<DD> TermdefWins. Termcap or terminfo escape sequences are used in preference
+to default escape sequences.<P>
+
+<DT> -Q <EM>quotestr</EM>
+
+<DD> Set the quote string. Especially useful when composing email, setting this
+allows the quote string to be checked for when Justifying paragraphs.
+A common quote string is "> ".<P>
+
+<DT> -r<EM>n</EM>
+
+<DD> Sets column used to limit the "Justify" command's right margin. <P>
+
+<DT> -t
+
+<DD> Enable "tool" mode. Intended for when <EM>Pico</EM> is used as the
+editor within other tools (e.g., Elm, Pnews). <EM>Pico</EM> will not
+prompt for save on exit, and will not rename the buffer during the "Write
+Out" command. <P>
+
+<DT> -v
+
+<DD> View the file only, disallowing any editing. <P>
+
+<DT> -version
+
+<DD> Print version information. <P>
+
+<DT> -w
+
+<DD> Disable word wrap (thus allow editing of long lines). <P>
+
+<I>Note: <EM>Pico</EM> will break any lines over 255 characters when reading a
+file, regardless of word wrapping.</I> <P>
+
+<DT> -x
+
+<DD> Disable keymenu at the bottom of the screen. <P>
+
+<DT> -z
+
+<DD> Enable ^Z suspension of <EM>Pico</EM>. <P>
+
+</DL>
+
+<H2><A NAME="pilot">Pilot</A></H2>
+
+The following command line options are supported in <EM>Pilot</EM>:
+
+<DL>
+
+<DT> -a
+
+<DD> Display all files including those beginning with a period (.). <P>
+
+<DT> -f
+
+<DD> Use function keys for commands. <I>This option supported only in
+conjunction with UW Enhanced NCSA telnet.</I> <P>
+
+<DT> -g
+
+<DD> Enable "Show Cursor" mode. Cause cursor to be positioned before the
+current selection rather than placed at the lower left of the display. <P>
+
+<DT> -m
+
+<DD> Enable mouse functionality. This only works when <EM>Pilot</EM> is
+run from within an X Window System "xterm" window. <P>
+
+<DT> -n<EM>n</EM>
+
+<DD> The -n<EM>n</EM> option enables new mail notification. The
+<EM>n</EM> argument is optional, and specifies how often, in seconds, your
+mailbox is checked for new mail. For example, -n60 causes <EM>Pilot</EM>
+to check for new mail once every minute. The default interval is 180
+seconds, while the minimum allowed is 30. (Note: no space between "n" and
+the number) <P>
+
+<DT> -o <EM>dir</EM>
+
+<DD>Sets operating directory. Only files within the specified directory
+are accessible and browsing is limited to the specified directory subtree.
+<P>
+
+<DT> -v
+
+<DD> Enable single vertical column display. <P>
+
+<DT> -x
+
+<DD> Disable keymenu at the bottom of the screen. <P>
+
+<DT> -z
+
+<DD> Enable ^Z suspension of <EM>Pilot</EM>.
+
+</DL>
+
+<!-- pnuts -->
+
+</BODY>
+</HTML>
diff --git a/doc/tech-notes/config-notes.html b/doc/tech-notes/config-notes.html
new file mode 100644
index 00000000..bc8e7459
--- /dev/null
+++ b/doc/tech-notes/config-notes.html
@@ -0,0 +1,1681 @@
+<HTML><HEAD><TITLE>Alpine Technical Notes: Notes on Configuration and
+Preferences</TITLE></HEAD><BODY>
+<H1>Notes on Configuration and Preferences</H1>
+
+<H2><A NAME="fkey">Alpine in Function Key Mode</A></H2>
+
+The standard <EM>Alpine</EM> uses alphabetic keys for most commands, and control keys
+in the composer. Despite possible appearances, the current bindings are
+the result of much discussion and thought. All the commands in the
+composer are single control characters. This keeps things very neat and
+simple for users. Two character commands in the composer are a
+possibility, but we're trying to avoid them because of the added
+complexity for the user. <P>
+
+<EM>Alpine</EM> can also operate in a function-key mode. To go into this mode invoke
+<EM>alpine -k</EM> or (on some UNIX systems) <EM>alpinef.</EM> On a UNIX
+system, you can link or copy the <EM>Alpine</EM> executable to
+<EM>alpinef</EM> to install <EM>alpinef.</EM> Alternatively, users and systems
+administrators can set the <EM>use-function-keys</EM> feature in the
+personal or system-wide <EM>Alpine</EM> configuration file.
+The command menus at the
+bottom of the screen will show <EM>F1-F12 </EM>instead of the alphabetic
+commands. In addition, the help screens will be written in terms of
+function keys and not alphabetic keys. <P>
+
+One of the results of using <EM>Alpine</EM> in function-key mode is that users can
+only choose from twelve commands at any given time. In alphabetic-key
+mode, a user can press a key for a command (say, q to quit) and that
+command can be fulfilled. In function-key mode, the command must be
+visible on the bottom key-menu in order to be used. There are some
+screens where four screens of commands are operational;
+function-key users can get to all of them, just not all at once. <P>
+
+<HR>
+
+<H2><A NAME="domain">Domain Settings</A></H2>
+
+<EM>Alpine</EM> uses the default domain for a few different tasks. First, it is
+tacked onto the user-id for outgoing email. Second, it is tacked onto all
+"local" (unqualified) addresses in the "To:" or "Cc:" fields of messages
+being composed (unless they are found in the address book or on an
+LDAP server). The domain
+name is also used to generate message-id lines for each outgoing message
+and to allow <EM>Alpine</EM> to check if an address is that of the current <EM>Alpine</EM> user.
+<P>
+
+<EM>Alpine</EM> determines the domain name according
+to whichever of these it finds.
+The list here is in decreasing order of precedence.
+
+<OL>
+
+<LI> Value of the variable
+<A HREF="config.html#user-domain"><EM>user-domain</EM></A>
+in the system fixed configuration file
+
+<LI> Value of the variable <EM>user-domain</EM> in the personal
+configuration file
+
+<LI> Value of the variable <EM>user-domain</EM> in the system-wide
+configuration file
+
+<LI> Value from an external database (DNS, <CODE>/etc/hosts</CODE>, NIS) as
+modified by a system fixed configuration file if
+<A HREF="config.html#use-only"><EM>use-only-domain-name</EM></A>
+set to <EM>yes</EM>
+
+<LI> Value from an external database (DNS, <CODE>/etc/hosts</CODE>, NIS) as
+modified by a personal configuration file if <EM>use-only-domain-name</EM>
+set to <EM>yes</EM>
+
+<LI> Value from an external database (DNS, <CODE>/etc/hosts</CODE>, NIS) as
+modified by a system configuration file if <EM>use-only-domain-name</EM>
+set to <EM>yes</EM>
+
+<LI> Unmodified value (host name) from an external database <P>
+
+</OL>
+
+<P> The easiest way for this system to work is for <EM>PC-Alpine</EM>
+users and UNIX <EM>Alpine</EM> system administrators to
+set the <EM>user-domain</EM> variable. The
+variable <EM>use-only-domain-name</EM> is helpful if your site
+supports/requires hostless addressing, but for some reason you don't want
+to use the <EM>user-domain</EM> variable. <P>
+
+<HR>
+
+<H2><A NAME="collections">Syntax for Collections</A></H2>
+
+In many environments, it is quite common to have collections of archived
+mail on various hosts around the network. Using the folder collections
+facility in <EM>Alpine</EM>, access to these archives is just as simple as access to
+folders on <EM>Alpine</EM>'s local disk. <P>
+
+"Collection" is the word we use in <EM>Alpine</EM> to describe a set of folders. A
+collection corresponds loosely to a "directory" containing mail folders.
+Folders within a defined collection can be manipulated (opened, saved-to,
+etc) using just their simple name. Any number of folder collections can
+be defined, and <EM>Alpine</EM> will adjust its menus and prompts to help navigate
+them. <P>
+
+The way collections are defined in <EM>Alpine</EM> is with the
+<A HREF="config.html#fold-coll"><EM>folder-collections</EM></A>
+variable in the <EM>Alpine</EM> configuration file.
+<EM>Folder-collections</EM> takes a list of one or more collections, each
+(optionally) preceded by a user-defined logical name (label). Once
+collections are defined, <EM>Alpine</EM> adjusts its menus and behavior to allow
+choosing files by their simple name within the collection.
+<P>
+
+Consider the following:
+
+<PRE>
+ folder-collections= Local-Mail C:\MAIL\[],
+ Remote-Mail {imap.u.example.edu}mail/[]
+</PRE>
+<P>
+
+The example shows two collections defined (a comma separated list;
+newlines in the list are OK if there's one or more spaces before the next
+entry), one local and one remote. Each collection is a space-delimited
+pair of elements-first an optional logical-name and second the collection
+specifier. The logical-name can have spaces if it has quotes around it
+(but keeping the logical name short and descriptive works best). <EM>Alpine</EM>
+will use the logical-name (if provided) to reference all folders in the
+collection, so the user never has to see the ugliness of the collection
+specifier. <P>
+
+The collection specifier can be thought of as an extended IMAP format (see
+the <A HREF="#remote-folders"><EM>Remote Folders</EM></A> section
+for a description of IMAP format names).
+Basically, a pair of square-brackets are placed in the fully qualified
+IMAP path where the simple folder name (the part without the host name and
+path) would appear. Like IMAP, the path can be either fully qualified
+(i.e., with a leading '/') or relative to your home directory. <P>
+
+An advanced feature of this notation is that a pattern within the square
+brackets allows the user to define a collection to be a subset of a
+directory. For example, a collection defined with the specifier:
+
+<PRE>
+ M-Mail C:MAIL/[m*]<BR>
+</PRE>
+
+will provide a view in the folder lister of all folders in the PC's
+"C:MAIL" directory that start with the letter 'm' (case insensitive under
+DOS, of course). Further, the wildcard matching will honor characters
+trailing the '*' in the pattern. <P>
+
+From within <EM>Alpine</EM>, the "Folder List" display will be adjusted to allow
+browsing of the folders in any defined collection. Even more, you'll
+notice in the <EM>Goto</EM> and <EM>Save</EM> commands a pair of
+sub-commands to rotate
+through the list of logical collection names, so only a simple name need
+be input in order to operate on a folder in any collection. <P>
+
+The first collection specified in the <EM>folder-collections</EM> has
+special significance. That folder is the "default collection for saves".
+By default, in cases where the user does not specify which
+collection should be used to <EM>Save</EM> a message,
+the default collection for saves will be used.
+Also, if the <A HREF="config.html#def-fcc"><EM>default-fcc</EM></A>
+is a relative file name, then it is relative
+to the default collection for saves. (See also
+<A HREF="config.html#saved-msg-name"><EM>saved-msg-name-rule</EM></A>. <P>
+
+The notion of collections encompasses both email folders and news reading.
+The variable <A HREF="config.html#news-coll"><EM>news-collections</EM></A>
+uses nearly the same format as <EM>folder-collections</EM>.
+Newsgroups can be defined for convenient
+access via either IMAP or NNTP. There are advantages and disadvantages to
+both access methods. In the IMAP case, your news environment state is
+maintained on the server and, thus, will be seen by any client. The
+downside is that, at the moment, you must have an account on the server.
+In the NNTP case, server access is mostly anonymous and no
+state/accounting need be maintained on it. The downside is that each
+client, for now, must individually maintain news environment state. <P>
+
+An example pinerc entry might be:
+
+<PRE>
+ news-collections= Remote-State {news.u.example.edu}#news.[],
+ Local-State {news.u.example.edu/nntp}#news.[]
+</PRE>
+
+Only newsgroups to which you are subscribed are included in the collection. <P>
+
+The pattern matching facility can be applied so as to define a news
+collection which is a subset of all the newsgroups you subscribe to. For
+example, this could be a valid collection:
+
+<PRE>
+ Newsfeed-News {news.u.example.edu/nntp}#news.[clari.*]
+</PRE>
+<P>
+
+Collection handling is a tough problem to solve in a general way, and the
+explanation of the syntax is a bit ugly. The upside is, hopefully, that
+for a little complexity in the <EM>Alpine</EM> configuration file you get simple
+management of multiple folders in diverse locations. <P>
+
+Collection setup is handled by the
+<EM>Setup/collectionList</EM> screen. <P>
+
+<HR>
+
+<H2><A NAME="remote-folders">Syntax for Folder Names</A></H2>
+
+Remote folders are distinguished from local folders by a leading host name
+bracketed by '{' and '}'. The path and folder name immediately following
+the closing bracket, '}', is interpreted by the remote server and is in a
+form compatible with that server (i.e., path delimiters and naming syntax
+relative to that server).
+<P>
+
+The full syntax for a <EM>Alpine</EM> folder name looks like
+
+<P>
+<CENTER><SAMP>[{&lt;remote-specification&gt;}][#&lt;namespace&gt;]&lt;namespace-specific-part&gt;</SAMP></CENTER>
+<P>
+
+The square brackets ([]) mean that the part is optional.
+<P>
+
+If there is no remote-specification, then the folder name is interpreted
+locally on the computer running <EM>Alpine</EM>.
+Local folder names depend on the operating system used by the computer
+running <EM>Alpine</EM>, as well as the configuration of that system. For example,
+&quot;C:&#92;ALPINE&#92;FOLDERS&#92;OCT-94&quot; might exist on a PC, and
+&quot;~/mail/september-1994&quot; might be a reasonable folder name on a
+system running Unix.
+
+<P>
+<EM>Alpine</EM> users have the option of using folders which are stored on some other
+computer. <EM>Alpine</EM> accesses remote folders via IMAP (the Internet Message
+Access Protocol), or in the case of news, via NNTP (the Network News
+Transport Protocol). To be able to access remote folders in <EM>Alpine</EM>, the
+remote host must be running the appropriate server software (imapd or
+nntpd) and you must correctly specify the name of the folder to <EM>Alpine</EM>,
+including the domain name of the remote machine. For example,
+<P>
+<CENTER><SAMP>&#123;monet.art.example.com}INBOX</SAMP></CENTER>
+<P>
+could be a remote folder specification, and so could
+<P>
+<CENTER><SAMP>&#123;unixhost.art.example.com}~/mail/september-1994</SAMP></CENTER>
+and
+<P>
+<CENTER><SAMP>&#123;winhost.art.example.com}&#92;mymail&#92;SEP-94</SAMP></CENTER>
+<P>
+Note that in the case of remote folders, the directory/file path in the specification is
+determined by the operating system of the remote computer, <B>not</B> by
+the operating system of the computer on which you are running <EM>Alpine</EM>.
+<P>
+As you can tell, the name of the computer is in &#123;} brackets
+followed immediately by the name of the folder. (In each of these cases the
+optional namespace is missing.) If, as in these
+examples, there is no remote access protocol specified, then IMAP is
+assumed. Check
+<A HREF="#server-name-syntax"><EM>Server Name Syntax</EM></A>
+for a more detailed look at what options can be placed between the brackets.
+If there are no brackets at all, then the folder name is interpreted locally
+on the computer on which you are running <EM>Alpine</EM>.
+
+<P>
+To the right of the brackets when a server name is present, or at the
+start of the foldername if no server is present, the sharp sign,
+&quot;#&quot;, holds special meaning. It indicates a folder name
+outside the area reserved for your personal folders. In fact, it's
+used to indicate both the name of the folder, and a special phrase
+telling <EM>Alpine</EM> how to interpret the name that follows.
+
+<P>
+So, for example, <EM>Alpine</EM> can be used to access a newsgroup that might be
+available on your computer using:
+<P>
+<CENTER><SAMP>#news.comp.mail.pine</SAMP></CENTER>
+<P>
+The sharp sign indicates the folder name is outside your personal
+folder area. The &quot;news.&quot; phrase after it tells <EM>Alpine</EM> to
+interpret the remainder of the name as a newsgroup.
+
+<P>
+Similarly, to access a newsgroup on your IMAP server, you might
+use something like:
+<P>
+<CENTER><SAMP>&#123;wharhol.art.example.com}#news.comp.mail.misc</SAMP></CENTER>
+
+<P>
+There are a number of such special phrases (or &quot;namespaces&quot;)
+available. For a more detailed explanation read about
+<A HREF="#folder-namespaces"><EM>Namespaces</EM></A>.
+
+<P>
+Note that &quot;INBOX&quot; has special meaning in both local and remote folder
+names. The name INBOX refers to your &quot;principal incoming
+message folder&quot; and will be mapped to the actual file name used for your
+INBOX on any given host. Therefore, a name like
+&quot;&#123;xxx.art.example.com}INBOX&quot; refers to whatever file is used to
+store incoming mail for you on that particular host.
+<P>
+
+<HR>
+
+<H2><A NAME="server-name-syntax">Server Name Syntax</A></H2>
+
+This section describes the syntax which may be used for server names
+which may be associated with remote folders or SMTP servers.
+
+<P>
+A server name is the hostname of the server.
+It's a good idea to use the host's fully-qualified network name.
+
+<P>
+<CENTER><SAMP>foo.example.com</SAMP></CENTER>
+<P>
+
+However, IP addresses are allowed if surrounded
+with square-brackets.
+
+<P>
+<CENTER><SAMP>[127.0.0.1]</SAMP></CENTER>
+<P>
+
+An optional network port number may be supplied by appending
+a colon (:) followed by the port number
+to the server name.
+By default, the IMAP port number, 143, is used.
+
+<P>
+<CENTER><SAMP>foo.example.com:port</SAMP></CENTER>
+<P>
+
+Besides server name and optional port number, various other optional
+parameters may be supplied that alter <EM>Alpine</EM>'s interaction with the server.
+A parameter is supplied by appending a slash (/) character followed by
+the parameter's name and,
+depending on the particular parameter, the value assigned to that
+name, to the server name (and optional port number).
+Parameter names are <EM>not</EM> case sensitive.
+Currently supported parameters include:
+
+<DL>
+
+<DT>User</DT>
+<DD>This parameter requires an associated value, and is intended to
+provide the username identifier with which to establish the server
+connection.
+If your SMTP server offers SMTP AUTH authentication, adding this
+parameter to the
+<A HREF="config.html#smtp-server"><EM>SMTP-Server</EM></A>
+option will cause <EM>Alpine</EM> to attempt to authenticate to the server using the
+supplied username.
+Similarly, if your NNTP server offers NNTP &quot;AUTHINFO SASL&quot;
+or &quot;AUTHINFO USER&quot; authentication, adding this parameter to the
+<A HREF="config.html#nntp-server"><EM>NNTP-Server</EM></A>
+option (or to the server name for any folder collection using NNTP)
+will cause <EM>Alpine</EM> to attempt
+to authenticate to the server using the supplied username.
+An example might be:
+
+<P>
+<CENTER><SAMP>/user=katie</SAMP></CENTER>
+<P>
+
+</DD>
+
+<DT>TLS</DT>
+<DD>
+Normally, when a new connection is made an attempt is made to
+negotiate a secure (encrypted) session using Transport Layer Security (TLS).
+If that fails then a non-encrypted connection will be attempted instead.
+This is a unary parameter indicating communication with the server must
+take place over a TLS connection. If the attempt to use TLS fails then
+this parameter will cause the connection to fail instead of falling
+back to an unsecure connection.
+
+<P>
+<CENTER><SAMP>/tls</SAMP></CENTER>
+<P>
+
+</DD>
+
+<DT>SSL</DT>
+<DD>
+This is a unary parameter indicating communication with the server should
+take place over a Secure Socket Layer connection. The server must support
+this method, and be prepared to accept connections on the appropriate
+port (993 by default).
+<EM>Alpine</EM> must be linked with an SSL library for this option to be operational.
+
+<P>
+<CENTER><SAMP>/ssl</SAMP></CENTER>
+<P>
+
+</DD>
+
+<DT>NoValidate-Cert</DT>
+<DD>Do not validate certificates (for TLS or SSL connections) from the server.
+This is needed if the server uses self-signed certificates or if <EM>Alpine</EM>
+cannot validate the certificate for some other known reason.
+<P>
+</DD>
+
+<DT>Anonymous</DT>
+<DD>This is a unary parameter (that means it does not have a value)
+indicating that the connection be logged in as
+&quot;anonymous&quot; rather than a specific user.
+Not all servers offer anonymous
+access; those which do generally only offer read-only access to certain
+&quot;public&quot; folders.
+
+<P>
+<CENTER><SAMP>/anonymous</SAMP></CENTER>
+<P>
+
+</DD>
+
+<DT>Secure</DT>
+<DD>This is a unary parameter indicating that the connection use the
+most secure authentication method mutually supported by <EM>Alpine</EM> and the
+server.
+<EM>Alpine</EM> is capable of authenticating connections to
+the server using several methods.
+By default, <EM>Alpine</EM> will attempt each
+method until either a connection is established or the
+list of methods is exhausted.
+This parameter causes <EM>Alpine</EM> to instead fail
+the connection if the first (generally most &quot;secure&quot;) method fails.
+
+<P>
+<CENTER><SAMP>/secure</SAMP></CENTER>
+<P>
+
+</DD>
+
+<DT>Submit</DT>
+<DD>This is a unary parameter for use with the
+&quot;SMTP-Server&quot; option.
+It indicates that the connection should be made to the Submit server
+(<A HREF="http://www.ietf.org/rfc/rfc2476.txt">RFC 3676</A>)
+(port 587) instead of the SMTP port (25).
+At the time this help was written the submit option was equivalent to
+specifying port 587.
+
+<P>
+<CENTER><SAMP>/submit</SAMP></CENTER>
+<P>
+or
+<P>
+<CENTER><SAMP>host:587</SAMP></CENTER>
+<P>
+
+</DD>
+
+<DT>Debug</DT>
+<DD>This is a unary parameter indicating that the connection be established
+in a verbose mode. Basically, it causes <EM>Alpine</EM> to log the communication with
+the server in <EM>Alpine</EM>'s debug file.
+Normally, the alpine -d command-line flag would be used instead.
+<P>
+</DD>
+
+<DT>NoRsh</DT>
+<DD>By default, <EM>Alpine</EM> attempts to login using &quot;rsh&quot;,
+the UNIX remote shell program.
+Including &quot;NoRsh&quot; will cause connections to this server to skip
+the &quot;rsh&quot; attempt.
+This might be useful to avoid long timeouts caused by rsh firewalls, for
+example.
+<P>
+</DD>
+
+<DT>Service</DT>
+<DD>This parameter requires an associated value. The default value is
+&quot;IMAP&quot; which indicates communication with the server based
+on the IMAP4rev1 protocol (defined in RFC 3501 -- see
+<A HREF="http://www.imap.org/docs/rfc3501.html">http://www.imap.org/docs/rfc3501.html</A>).</DD>
+
+Other service values include:
+ <DL>
+ <DT>NNTP</DT>
+ <DD>This value indicates communication with the server takes place via
+the Network News Transfer Protocol. Use this to define a collection
+of newsgroups on a remote news server. So
+
+<P>
+<CENTER><SAMP>/service=NNTP</SAMP></CENTER>
+<P>
+or just
+<P>
+<CENTER><SAMP>/NNTP</SAMP></CENTER>
+<P>
+
+is the way to specify NNTP access.
+<P>
+ </DD>
+
+ <DT>POP3</DT>
+ <DD>This value indicates communication with the server takes place via the
+Post Office Protocol 3 protocol.
+
+<P>
+<CENTER><SAMP>/service=POP3</SAMP></CENTER>
+<P>
+or just
+<P>
+<CENTER><SAMP>/POP3</SAMP></CENTER>
+<P>
+
+Note that there are several important issues
+to consider when selecting this option:
+<OL>
+ <LI> POP3 provides access to only your INBOX. In other words,
+secondary folders such as your &quot;saved-messages&quot; are inaccessible.
+ <LI> <EM>Alpine</EM>'s implementation of POP3 does not follow the traditional POP
+model and will leave your mail on the server. Refer to the
+<A HREF="#maildrop"><EM>Mail Drop</EM></A>
+functionality for a possible way around this problem.
+ <LI> See the discussion about new-mail checking in <A HREF="config.html#reopen-rule"><EM>Folder-Reopen-Rule</EM></A>.
+</OL>
+</DD>
+</DL>
+</DL>
+
+<P>
+Note that it is possible to include more than one parameter in a server
+specification by concatenating the parameters. For example:
+
+<P>
+<CENTER><SAMP>foo.example.com:port/user=katie/novalidate-cert/debug</SAMP></CENTER>
+<P>
+
+<HR>
+
+<H2><A NAME="folder-namespaces">Folder Namespaces</A></H2>
+
+A <EM>Alpine</EM> folder name looks like
+
+<P>
+<CENTER><SAMP>[{&lt;remote-specification&gt;}][#&lt;namespace&gt;][&lt;namespace-specific-part&gt;]</SAMP></CENTER>
+<P>
+
+The local part of a folder name has an optional &quot;Namespace&quot; which
+tells <EM>Alpine</EM> how to interpret the rest of the name.
+
+<P>
+By default the folder name is interpreted as defining a section of your personal
+folder area. This area and how you specify it are defined by the
+server, if one is specified, or, typically, the home
+directory, if no server is defined.
+
+<P>
+If a namespace is specified, it begins with the
+sharp, &quot;#&quot;, character followed by the name of the namespace
+and then the namespace's path-element-delimiter. Aside from the
+path's format, namespaces can also imply access rights, content
+policy, audience, location, and, occasionally, access methods.
+
+<P>
+Each server exports its own set (possibly of size one) of
+namespaces. Hence, it's likely communication with your server's
+administrator will be required for specific configurations. Some of
+the more common namespaces, however, include:
+
+<DL>
+<DT>#news.</DT>
+<DD>This specifies a set of folders in the newsgroup namespace. Newsgroup
+names are hierarchically defined with each level delimited by a period.
+<P>
+<CENTER><SAMP>#news.comp.mail.pine</SAMP></CENTER>
+<P>
+</DD>
+<DT>#public/</DT>
+<DD>This specifies a folder area that the server may export to the general
+public.
+</DD>
+<DT>#shared/</DT>
+<DD>This specifies a folder area that the folder may export to groups
+of users.
+</DD>
+<DT>#ftp/</DT>
+<DD>This specifies a folder area that is the same as that it may have
+exported via the &quot;File Transfer Protocol&quot;.
+</DD>
+<DT>#mh/</DT>
+<DD>This specifies the personal folder area associated with folders
+and directories that were created using the MH message handling system.
+</DD>
+<DT>#move/</DT>
+<DD>This namespace is interpreted locally by <EM>Alpine</EM>. It has an unusual interpretation and format.
+<P>
+<CENTER><SAMP>#move&lt;DELIM&gt;&lt;MailDropFolder&gt;&lt;DELIM&gt;&lt;DestinationFolder&gt;</SAMP></CENTER>
+<P>
+The <CODE>#move</CODE> namespace is followed by two folder names separated by a delimiter
+character.
+The delimiter character may be any character which does not appear in
+the <CODE>MailDropFolder</CODE> name.
+The meaning of <CODE>#move</CODE> is that mail will be copied from the <CODE>MailDropFolder</CODE> to
+the <CODE>DestinationFolder</CODE> and then deleted (if possible) from the <CODE>MailDropFolder</CODE>.
+Periodic checks at frequency
+<A HREF="config.html#mail-check"><EM>Mail-Check-Interval</EM></A>, but with a minimum time between checks set by
+<A HREF="config.html#maildrop-check-minimum"><EM>MailDrop-Check-Minimum</EM></A>,
+are made for new mail arriving in the <CODE>MailDropFolder</CODE>.
+An example which copies mail from a POP inbox to a local folder follows
+<P>
+<CENTER><SAMP>#move+{popserver.example.com/pop3/ssl}inbox+local folder</SAMP></CENTER>
+<P>
+To you it appears that mail is being delivered to the local folder when it
+is copied from the <CODE>MailDropFolder</CODE>, and you read mail from the local folder.
+<P>
+Note that if the <CODE>DestinationFolder</CODE> does not exist then the messages are not
+copied from the <CODE>MailDropFolder</CODE>.
+A <CODE>#move</CODE> folder may only be used as an
+<A HREF="config.html#enable-incoming-folders"><EM>Incoming folder</EM></A> or
+an Inbox.
+When you are in the FOLDER LIST of Incoming Message Folders (after turning
+on the enable-incoming-folders option)
+the Add command has a subcommand &quot;Use Mail Drop&quot;
+which may be helpful for defining the folder in your <EM>Alpine</EM> configuration.
+The same is true when you edit the
+<A HREF="config.html#inbox-path"><EM>Inbox-Path</EM></A>
+option in Setup/Config.
+Each of these configuration methods will also create the <CODE>DestinationFolder</CODE>
+if it doesn't already exist.
+If you are having problems, make sure the <CODE>DestinationFolder</CODE> exists.
+</DD>
+</DL>
+<P>
+
+In addition, the server may support access to other user's folders,
+provided you have suitable permissions. Common methods use a prefix
+of either &quot;~<VAR>user</VAR>/&quot;, or &quot;/<VAR>user</VAR>/&quot; to
+indicate the root of the other user's folder area.
+<P>
+
+<HR>
+
+<H2><A NAME="maildrop">What is a Mail Drop?</A></H2>
+
+In some situaions it may make sense to have your mail delivered to one
+folder (the Mail Drop) and then when you want to read mail that has been
+delivered to the Mail Drop folder <EM>Alpine</EM> will move it to another
+destination folder.
+Often the Mail Drop will be a remote folder and messages will be moved from
+there to a local destination folder.
+
+<P>
+One example where this might make sense is if the Mail Drop folder is accessible
+only with the POP protocol.
+You could designate your POP inbox as the Mail Drop folder and have <EM>Alpine</EM> move
+mail from there to a local (on the same machine <EM>Alpine</EM> is running on)
+destination folder, where you'll read it.
+
+<P>
+A Mail Drop may only be used as your Inbox or as an
+<A HREF="config.html#enable-incoming-folders"><EM>Incoming folder</EM></A>.
+
+<P>
+There is no attempt to synchronize the contents of the destination folder
+with the contents of the Mail Drop folder.
+All that happens is that all of the messages in the Mail Drop folder are
+copied to the destination folder and then they are deleted and expunged (if possible)
+from the Mail Drop folder.
+The next time a check for new mail is made, any messages in the Mail
+Drop folder are once again copied to the destination folder and deleted
+and expunged from the Mail Drop folder.
+(If the Mail Drop folder is a news group, then the messages can't be
+expunged from the newsgroup. Instead, only Recent messages are copied from
+the newsgroup to the destination folder.)
+
+<P>
+Configuration of a Mail Drop is a little different from configuration of
+a folder which does not use a Mail Drop because you have to specify two
+folder names instead of one.
+The two folders may be any types of folders that <EM>Alpine</EM> can normally use.
+They don't have to be a remote folder and a local folder, that is
+simply the most common usage.
+When you use a Mail Drop folder <EM>Alpine</EM> will periodically re-open the Mail
+Drop to check for new mail.
+The new-mail checks will happen at the frequency set with the
+<A HREF="config.html#mail-check"><EM>Mail-Check-Interval</EM></A> option,
+but with a minimum time
+(<A HREF="config.html#maildrop-check-minimum"><EM>MailDrop-Check-Minimum</EM></A>)
+between checks.
+Because of this minimum you may notice that new mail does not
+appear promptly when you expect it.
+The reason for this is to protect the server from over-zealous opening and
+closing of the Mail Drop folder.
+If the user initiates the check by typing ^L (Ctrl-L) or the Next command when at
+the end of the folder index, then the check will happen, regardless of how
+long it has been since the previous check.
+<P>
+If there is new mail, that mail will be copied to the destination folder
+and then will be deleted from the Mail Drop.
+Note that using a Mail Drop with a local destination folder does not make
+sense if you read mail from more than one machine, because the mail is
+downloaded to the destination folder (which is accessible from only one
+machine) and deleted from the Mail Drop.
+<P>
+The feature <A HREF="config.html#maildrops-preserve-state"><EM>Maildrops-Preserve-State</EM></A>
+modifies the operation of Mail Drops.
+
+<P>
+The actual syntax used by <EM>Alpine</EM> for a folder that uses a Mail Drop is:
+
+<P>
+<CENTER><SAMP>#move&lt;DELIM&gt;&lt;MailDropFolder&gt;&lt;DELIM&gt;&lt;DestinationFolder&gt;</SAMP></CENTER>
+<P>
+The brackets are not literal.
+<P>
+<CENTER><SAMP>&lt;DELIM&gt;</SAMP></CENTER>
+<P>
+is a single character which does not appear in the <CODE>MailDropFolder</CODE> name.
+If the name doesn't contain spaces then it can be a space character.
+The two folder names are full technical
+<A HREF="#server-name-syntax"><EM>folder names</EM></A>
+as used by <EM>Alpine</EM>.
+Here are a couple examples to give you an idea what is being talked about:
+
+<P>
+<CENTER><SAMP>#move&nbsp;{popserver.example.com/pop3}inbox&nbsp;localfolder</SAMP></CENTER>
+<P>
+<CENTER><SAMP>#move+{nntpserver.example.com/nntp}#news.comp.mail.pine+local&nbsp;folder</SAMP></CENTER>
+<P>
+
+A #move folder may only be used as an
+<A HREF="config.html#enable-incoming-folders"><EM>Incoming folder</EM></A> or
+an Inbox.
+When you are in the FOLDER LIST of Incoming Message Folders (after turning
+on the Enable-Incoming-Folders option)
+the Add command has a subcommand &quot;Use Mail Drop&quot;
+which may be helpful for defining the folder in your <EM>Alpine</EM> configuration.
+The same is true when you edit the
+<A HREF="config.html#inbox-path"><EM>Inbox-Path</EM></A>
+option in Setup/Config.
+</CODE>
+if it doesn't already exist.
+If you are having problems, make sure the <CODE>DestinationFolder</CODE> exists.
+<P>
+
+<HR>
+
+<H2><A NAME="sorting">Sorting a Folder</A></H2>
+
+The mail index may be sorted by arrival, date, subject,
+from, size, score, to, or cc order.
+Each sort order can also be reversed. The <EM>$</EM> command will
+prompt the user for the sort order. The sort order can also be specified
+on the command line with the <EM>-sort</EM> flag or (equivalently) with
+the <A HREF="config.html#sort-key"><EM>sort-key</EM></A> variable in
+the <EM>pinerc</EM> file.
+When a user changes folders, the sort order will go back to the original
+sort order.
+The command line (<EM>-sort</EM>) or configuration file sort specification
+(<EM>sort-key</EM>) changes the original sort order. <P>
+
+When a folder is sorted and new mail arrives in the folder it will be
+inserted in its properly sorted place. This can be a little odd when the
+folder is sorted by something like the subject. It can also be a little
+slow if you are viewing a large, sorted <EM>INBOX</EM>,
+since the <EM>INBOX</EM> will have
+to be re-sorted whenever new mail arrives. <P>
+
+The sorts are all independent of case and ignore leading or trailing white
+space. There are actually two forms of subject sort. One called
+<EM>Subject</EM> and the other called <EM>OrderedSubj</EM>.
+They both ignore "Re:" at
+the beginning and "(fwd)" at the end of the subjects.
+<EM>Subject</EM> sorts all the subjects alphabetically.
+<EM>OrderedSubj</EM> sorts by subjects alphabetically,
+groups messages with the same subject (pseudo-threads),
+then sorts the groups by the date of the first message of the group.
+Sorting by <EM>Thread</EM> was added after <EM>OrderedSubj</EM>
+and is usually a better method.
+Thread sorting uses information in the message headers References,
+Message-ID, and Subject.
+It is possible the sort will be slightly slower with a Thread sort than
+with an OrderedSubj sort.
+The
+sort by sender sorts by the user-id (part before the "@"), not the full name.
+The arrival sort is no sort at all and the date
+sort depends on the format of the date.
+Some dates are in strange formats and are unparsable.
+The time zone is also taken into account. <P>
+
+Sorting large mail folders can be very slow since it requires fetching all
+the headers of the mail messages.
+With UNIX <EM>Alpine</EM>, only the first sort is slow since <EM>Alpine</EM>
+keeps a copy of all the headers. One exception is sorting
+in reverse arrival order. This is fast because no headers have to be
+examined. <EM>Alpine</EM> will show progress as it is sorting. <P>
+
+<HR>
+
+<H2><A NAME="alt-ed">Alternate Editor</A></H2>
+
+In the <EM>Alpine</EM> composer you can use any text editor,
+such as <EM>vi</EM> or <EM>emacs,</EM> for composing the message text.
+The addresses and subject still must be edited using the standard
+<EM>Alpine</EM> composer.
+If you include the feature
+<A HREF="config.html#enable-alt-ed"><EM>enable-alternate-editor-cmd</EM></A>
+in your <EM>pinerc</EM> you can type <EM>^_</EM> while in the body of
+the message in the composer and be prompted for the editor.
+If you also set the <A HREF="config.html#editor"><EM>editor</EM></A> variable
+in your <EM>pinerc</EM> then <EM>^_</EM> will invoke the configured
+editor when you type it. <P>
+
+Turning on the feature
+<A HREF="config.html#enable-alt-imp"><EM>enable-alternate-editor-implicitly</EM></A>
+will automatically invoke the editor you have defined with the <EM>editor</EM>
+variable whenever you enter the body of a message you are composing. For
+example, when you move out of the last header line and into the body of
+the message, the alternate editor will be automatically invoked. <P>
+
+We know that many people would like to use the alternate editor to edit
+the mail header as well. We considered several designs for this and
+didn't come up with one that we liked and that was easy to implement. One
+of the main problems is that you lose access to the address book. <P>
+
+<HR>
+
+<H2><A NAME="signature">Signatures and Signature Placement</A></H2>
+
+If the file <EM>~/.signature</EM> (UNIX) or
+<EM>&lt;PINERC</EM>directory&gt;\PINE.SIG (PC) exists, it will be included
+in all outgoing messages. It is included before composition starts so
+that the user has a chance to edit it out if he or she likes. The file
+name for the signature can be changed by setting the
+<A HREF="config.html#sig-file"><EM>signature-file</EM></A>
+variable in the <EM>pinerc</EM>.
+If the feature <A HREF="config.html#sig-dash"><EM>enable-sigdashes</EM></A>
+is turned on then the line consisting of the
+three characters "<CODE>-- "</CODE> is prepended to the signature file.
+When Replying or Forwarding a message different signatures my be automatically
+included by configuring them in the
+<A HREF="config.html#role-config"><EM>Roles</EM></A>
+setup screen.
+It's easy to include different signatures by hand, by having multiple
+signature files (<EM>.sig1, .sig2, .sig3, etc</EM>) and choosing to
+include (^R in the composer) the correct one for the message being sent.
+<P>
+
+<EM>Alpine</EM>'s default behavior encourages
+a user to put his or her contribution
+before the inclusion of the original text of the message being forwarded
+or replied to, This is contrary to some conventions, but makes the
+conversation more readable when a long original message is included in a
+reply for context. The reader doesn't have to scroll through the original
+text that he or she has probably already seen to find the new text. If
+the reader wishes to see the old message(s), the reader can scroll further
+into the message. Users who prefer to add their input at the end of a
+message should set
+the <A HREF="config.html#sig-at-bot"><EM>signature-at-bottom</EM></A> feature.
+The signature will then be appended to the end of
+the message after any included text.
+This feature applies when <EM>Reply</EM>ing, not when <EM>Forward</EM>ing. <P>
+
+<HR>
+
+<H2><A NAME="feature-list">Feature List Variable</A></H2>
+
+<EM>Alpine</EM> used to have <EM>feature levels</EM> for
+users with different amounts of experience.
+We found that this was too restrictive. <EM>Alpine</EM> now has a
+<A HREF="config.html#feat-list"><EM>feature-list</EM></A> instead.
+Each user may pick and choose which
+features they would like enabled (simple to do in the <EM>Setup/Config</EM>
+screen). There is a short description of each in <A HREF="config.html#features-conf"><EM>Configuration Features</EM></A>. There is also a short on-line
+help explaining the effect of each of
+the features in the <EM>Setup/Config</EM> screen.
+When the cursor is highlighting
+a feature, the <EM>?</EM> command will show the help text for that feature.
+Features don't have values, they are just turned on or off. They are all
+off by default. <P>
+
+The <EM>feature-list</EM> variable is different from all other
+configuration variables in that its value is additive. That is, the
+system-wide configuration file can have some features turned on by
+default. The user can select other features in their personal
+configuration file and those features will be <STRONG>added</STRONG> to
+the set of features turned on in the system-wide configuration file.
+(With all other configuration variables, the user's values
+<STRONG>replace</STRONG> the system-wide values.) Likewise, additional
+features may be set on the command-line with the argument
+"-feature-list=". These will be added to the others. <P>
+
+The treatment of <EM>feature-list</EM> in the system-wide <EM>fixed</EM>
+configuration file is also different from other variables. The system
+management can fix the value of individual features by placing them in the
+fixed configuration file. Users will not be able to alter those features,
+but will still be able to set the other non-restricted features the way
+they like. <P>
+
+Because <EM>feature-list</EM> is additive, there is a way to turn features
+off as well as on. Prepending the prefix "no-" to any feature sets it to
+off. This is useful for over-riding the system-wide default in the
+personal configuration file or for over-riding the system-wide default or
+the personal configuration value on the command line. For example, if the
+system-wide default configuration has the <EM>quit-without-confirm</EM>
+feature set, the user can over-ride that (and turn it off) by including
+<EM>no-quit-without-confirm</EM> in the personal configuration file or by
+giving the command line argument
+<EM>-feature-list=no-quit-without-confirm.</EM> More features (options)
+will no doubt continue to be added. <P>
+
+<HR>
+
+<H2><A NAME="config-inheritance">Configuration Inheritance</A></H2>
+
+We start with an explanation of how configuration works in hopes of making
+it easier to describe how inheritance works.
+<P>
+<EM>Alpine</EM> uses a hierarchy of configuration values from different locations.
+There are five ways in which each configuration option (configuration
+variable) can be set.
+In increasing order of precedence they are:
+<P>
+<OL>
+<LI> the system-wide configuration file.
+
+<LI> the personal configuration file
+
+<LI> the personal exceptions file
+
+<LI> a command line argument
+
+<LI> the system-wide <EM>fixed</EM> configuration file (Unix <EM>Alpine</EM> only)
+</OL>
+<P>
+The fixed configuration file is normally
+<CODE>/usr/local/lib/pine.conf.fixed</CODE>.
+<P>
+The system-wide configuration file is normally
+<CODE>/usr/local/lib/pine.conf</CODE> for Unix <EM>Alpine</EM> and is normally not
+set for <EM>PC-Alpine</EM>.
+For <EM>PC-Alpine</EM>, if the environment variable <EM>$PINECONF</EM> is set, that
+is used for the system-wide configuration.
+This location can be set or changed on the command line with the -P flag.
+The system-wide configuration file can be either a local file or a
+remote configuration folder.
+<P>
+For Unix <EM>Alpine</EM>, the personal configuration file is normally the file
+<CODE>.pinerc</CODE> in the user's home directory.
+This can be changed with the -p command line flag.
+For <EM>PC-Alpine</EM>, the personal configuration file is in
+<CODE>$PINERC</CODE> or <CODE>&lt;PineRC registry value&gt;</CODE> or
+<CODE>${HOME}&#92;ALPINE&#92;PINERC</CODE> or
+<CODE>&lt;ALPINE.EXE </CODE>dir<CODE>&gt;&#92;PINERC</CODE>.
+This can be changed with the -p command line flag.
+If -p or <CODE>$PINERC</CODE> is used, the configuration data may be in
+a local file or a remote config folder.
+<P>
+For Unix <EM>Alpine</EM>, the personal exceptions configuration file is
+specified with the &quot;-x exceptions_config&quot; command line argument.
+&quot;Exceptions_config&quot; may be either a local file or a remote
+configuration folder.
+If there is no &quot;-x&quot; command line option,
+<EM>Alpine</EM> will look for the file &quot;<CODE>.pinercex</CODE>&quot;
+in the same local directory that the regular config file is located in.
+If the regular config file is remote then Unix <EM>Alpine</EM> looks in the home
+directory for &quot;<CODE>.pinercex</CODE>&quot;.
+<P>
+For <EM>PC-Alpine</EM>, the personal exceptions configuration file is
+specified with the &quot;-x exceptions_config&quot; command line argument.
+If there is no &quot;-x&quot; command line argument the environment
+variable <CODE>$PINERCEX</CODE> may be set to the name of the
+&quot;exceptions_config&quot; instead.
+&quot;Exceptions_config&quot; may be either a local file or a remote
+configuration folder.
+If there is no &quot;-x&quot; command line option and <CODE>$PINERCEX</CODE>
+is not set,
+<EM>PC-Alpine</EM> will look for the file &quot;<CODE>PINERCEX</CODE>&quot;
+in the same local directory that the regular config file is located in.
+If the regular config file is remote then <EM>PC-Alpine</EM> looks in the
+local directory specified by the &quot;-aux local_directory&quot; command
+line argument, or the directory <CODE>${HOME}&#92;ALPINE</CODE>, or
+in <CODE>&lt;ALPINE.EXE </CODE>directory<CODE>&gt;</CODE> for a file named
+&quot;<CODE>PINERCEX</CODE>&quot;.
+<P>
+To reiterate, the value of a configuration option is taken from the
+last location in the list above in which it is set.
+Or, thinking about it slightly differently, a default value for an option
+is established in the system-wide configuration file (or in the source
+code if there is no value in the system-wide file).
+That default remains in effect until and unless it is overridden by a value in a
+location further down the list, in which case a new &quot;default&quot;
+value is established.
+As we continue down the list of locations we either retain the
+value at each step or establish a new value.
+The value that is still set after going through the whole list of
+configuration locations is the one that is used.
+<P>
+So, for example, if an option is set in the system-wide configuration
+file and in the personal configuration file, but is not set in the
+exceptions, on the command line, or in the fixed file; then the value
+from the personal configuration file is the one that is used.
+Or, if it is set in the system-wide config, in the personal config, not
+in the exceptions, but is set on the command line; then the value
+on the command line is used.
+<P>
+Finally we get to inheritance.
+For configuration options which are lists, like &quot;smtp-server&quot; or
+&quot;incoming-folders&quot;,
+the inheritance mechanism makes it possible to <EM>combine</EM>
+the values from different locations instead of <EM>replacing</EM> the value.
+This is true of all configuration lists other than the &quot;feature-list&quot;,
+for which you may already set whatever you want at
+any configuration location (by using the &quot;no-&quot; prefix if
+necessary).
+<P>
+To use inheritance, set the first item in a configuration list to the
+token &quot;INHERIT&quot;.
+If the first item is &quot;INHERIT&quot;,
+then instead of replacing the default value established so far, the rest of
+the list is appended to the default value established so far and that is
+the new value.
+<P>
+Here is an example which may make it clearer. Suppose we have:
+<P>
+<PRE>
+ System-wide config : smtp-server = smtp1.corp.com, smtp2.corp.com
+ Personal config : smtp-server = INHERIT, mysmtp.home
+ Exceptions config : smtp-server = &lt;No Value Set&gt;
+ Command line : smtp-server = &lt;No Value Set&gt;
+ Fixed config : smtp-server = &lt;No Value Set&gt;
+</PRE>
+<P>
+
+This would result in an effective smtp-server option of
+<P>
+<PRE>
+ smtp-server = smtp1.corp.com, smtp2.corp.com, mysmtp.home
+</PRE>
+<P>
+The &quot;INHERIT&quot; token can be used in any of the configuration files
+and the effect cascades.
+For example, if we change the above example to:
+<P>
+<PRE>
+ System-wide config : smtp-server = smtp1.corp.com, smtp2.corp.com
+ Personal config : smtp-server = INHERIT, mysmtp.home
+ Exceptions config : smtp-server = INHERIT, yoursmtp.org
+ Command line : smtp-server = &lt;No Value Set&gt;
+ Fixed config : smtp-server = &lt;No Value Set&gt;
+</PRE>
+<P>
+
+This would result in:
+<P>
+<PRE>
+ smtp-server = smtp1.corp.com, smtp2.corp.com, mysmtp.home, yoursmtp.org
+</PRE>
+<P>
+Unset variables are skipped over (the default value is carried forward) so
+that, for example:
+<P>
+<PRE>
+ System-wide config : smtp-server = smtp1.corp.com, smtp2.corp.com
+ Personal config : smtp-server = &lt;No Value Set&gt;
+ Exceptions config : smtp-server = INHERIT, yoursmtp.org
+ Command line : smtp-server = &lt;No Value Set&gt;
+ Fixed config : smtp-server = &lt;No Value Set&gt;
+</PRE>
+<P>
+
+produces:
+<P>
+<PRE>
+ smtp-server = smtp1.corp.com, smtp2.corp.com, yoursmtp.org
+</PRE>
+<P>
+
+If any later configuration location has a value set (for a particular list
+option) which does <EM>not</EM> begin with &quot;INHERIT&quot;,
+then that value replaces whatever value has been defined up to that point.
+In other words, that cancels out any previous inheritance.
+<P>
+<PRE>
+ System-wide config : smtp-server = smtp1.corp.com, smtp2.corp.com
+ Personal config : smtp-server = INHERIT, mysmtp.org
+ Exceptions config : smtp-server = yoursmtp.org
+ Command line : smtp-server = &lt;No Value Set&gt;
+ Fixed config : smtp-server = &lt;No Value Set&gt;
+</PRE>
+<P>
+
+results in:
+<P>
+<PRE>
+ smtp-server = yoursmtp.org
+</PRE>
+<P>
+
+For some configuration options, like &quot;viewer-hdr-colors&quot; or
+&quot;patterns-roles&quot;, it is
+difficult to insert the value &quot;INHERIT&quot; into the list of values
+for the option using the normal Setup tools.
+In other words, the color setting screen (for example) does not
+provide a way to input the text &quot;INHERIT&quot; as the first
+item in the viewer-hdr-colors option.
+The way to do this is to either edit the pinerc file directly and manually
+insert it, or turn on the &quot;expose-hidden-config&quot; feature and insert it
+using the Setup/Config screen.
+<P>
+
+<HR>
+
+<H2><A NAME="env-variables">Using Environment Variables</A></H2>
+
+The values of <EM>Alpine</EM> configuration options may include environment variables
+which are replaced by the value of the variable at the time <EM>Alpine</EM> is run
+(and also at the time the config option is changed).
+The syntax to use environment variables is a subset of the common Unix
+shell dollar-syntax.
+For example, if
+
+<P><CENTER><SAMP>$VAR</SAMP></CENTER><P>
+
+appears in the value of a <EM>Alpine</EM> configuration option it is looked up in the
+environent (using getenv(&quot;VAR&quot;)) and its
+looked-up value replaces the <SAMP>$VAR</SAMP> part of the option value.
+To include a literal dollar sign you may precede the dollar sign with another
+dollar sign.
+In other words, if the text
+
+<P><CENTER><SAMP>$$text</SAMP></CENTER><P>
+
+is the value of a configuration option, it will be expanded to
+
+<P><CENTER><SAMP>$text</SAMP></CENTER><P>
+
+and no environment lookup will be done.
+For Unix <EM>Alpine</EM> it will also work to use a backslash character to
+escape the special meaning of the dollar sign, but $$ is preferable since
+it works for both <EM>PC-Alpine</EM> and Unix <EM>Alpine</EM>, allowing the configuration option
+to be in a shared configuration file.
+<P>
+
+This all sounds more complicated than it actually is.
+An example may make it clearer.
+Unfortunately, the way in which environment variables are set is OS-dependent
+and command shell-dependent.
+In some Unix command shells you may use
+
+<P><CENTER><SAMP>PERSNAME="Fred Flintstone"</SAMP></CENTER><P>
+ <CENTER><SAMP>export PERSNAME</SAMP></CENTER><P>
+
+Now, if you use <EM>Alpine</EM>'s Setup/Config screen to set
+
+<P><CENTER><SAMP>personal-name=$PERSNAME</SAMP></CENTER><P>
+
+the <SAMP>$PERSNAME</SAMP> would be replaced by <SAMP>Fred Flintstone</SAMP>
+so that this would be equivalent to
+
+<P><CENTER><SAMP>personal-name=Fred Flintstone</SAMP></CENTER><P>
+
+Note, environment variable substitution happens after configuration
+options which are lists are split into the separate elements of the list,
+so a single environment variable can't contain a list of values.
+
+<P>
+The environment variable doesn't have to be the only thing
+after the equal sign.
+However, if the name of the variable is not at the end of the line or
+followed by a space (so that you can tell where the variable name ends),
+it must be enclosed in curly braces like
+
+<P><CENTER><SAMP>${VAR}</SAMP></CENTER><P>
+
+It is always ok to use the braces even if you don't need to.
+<P>
+It is also possible to set a default value for an environment variable.
+This default value will be used if the environment variable is not
+set (that is, if getenv(&quot;VAR&quot;) returns NULL).
+The syntax used to set a default value is
+
+<P><CENTER><SAMP>${VAR:-default value}</SAMP></CENTER><P>
+
+If the config file contains
+
+<P><CENTER><SAMP>personal-name=${VAR:-Fred Flintstone}</SAMP></CENTER><P>
+
+then when <EM>Alpine</EM> is run <SAMP>VAR</SAMP> will be looked up in the environment.
+If <SAMP>VAR</SAMP> is found then <SAMP>personal-name</SAMP> will have
+the value that <SAMP>VAR</SAMP> was set to, otherwise,
+<SAMP>personal-name</SAMP> will be set to <SAMP>Fred Flintstone</SAMP>,
+the default value.
+
+<P>
+An example where an environment variable might be useful is the
+variable <SAMP>inbox-path</SAMP> in the global configuration file.
+Suppose most users used the server
+
+<P><CENTER><SAMP>imapserver.example.com</SAMP></CENTER><P>
+
+but that there were some exceptions who used
+
+<P><CENTER><SAMP>altimapserver.example.com</SAMP></CENTER><P>
+
+In this case, the system manager might include the following line in
+the systemwide default <EM>Alpine</EM> configuration file
+
+<P><CENTER><SAMP>inbox-path=${IMAPSERVER:-imapserver.example.com}</SAMP></CENTER><P>
+
+For the exceptional users adding
+
+<P><CENTER><SAMP>IMAPSERVER=altimapserver.example.com</SAMP></CENTER><P>
+
+to their environment should work.
+<P>
+Another example might be the case where a user has to use a different
+SMTP server from work and from home.
+The setup might be something as simple as
+
+<P><CENTER><SAMP>smtp-server=$SMTP</SAMP></CENTER><P>
+
+or perhaps a default value could be given.
+Note that, as mentioned above, the variable <SAMP>SMTP</SAMP> cannot contain
+a list of SMTP servers.
+<P>
+
+<HR>
+
+<H2><A NAME="smtp-server">SMTP Servers</A></H2>
+
+It is sometimes desirable to set <CODE>smtp-server=localhost</CODE>
+instead of setting
+<A HREF="config.html#sendmail-path"><EM>sendmail-path</EM></A>
+to overcome the inability to
+negotiate ESMTP options when <EM>sendmail</EM> is invoked with the
+<EM>-t</EM> option. Sendmail can also be subject to unacceptable delays
+due to slow DNS lookups and other problems. <P>
+
+It is sometimes desirable to configure an SMTP server on a port other
+than the default port 25. This may be used to provide an alternate
+service that is optimized for a particular environment or provides
+different features from the port 25 server. An example would be a program
+that negotiates ESMTP options and queues a message, but does not attempt
+to deliver messages. This would avoid delays frequently encountered when
+invoking <EM>sendmail</EM> directly. <P>
+
+A typical configuration would consist of
+
+<UL>
+
+<LI> A program that implements the SMTP or ESMTP protocol via stdio.
+
+<LI> An entry in <CODE>/etc/services</CODE> for the alternate service.
+
+<LI> An entry in <CODE>/etc/inetd.conf</CODE> for the alternate service.
+
+<LI> An entry in <CODE>/usr/local/lib/pine.conf</CODE>,
+<CODE>/usr/local/lib/pine.conf.fixed</CODE> or <CODE>~/.pinerc</CODE>.
+
+</UL>
+
+<HR>
+
+<H2><A NAME="mime.types">MIME.Types file</A></H2>
+
+<EM>Alpine</EM>'s MIME-TYPE support is based on code contributed by Hans Drexler
+&LT;drexler@mpi.nl&GT;. <EM>Alpine</EM> assigns MIME Content-Types according
+to file name extensions found in the system-wide files
+<CODE>/usr/local/lib/mime.types</CODE> and <CODE>/etc/mime.types</CODE>,
+and a user specific <CODE>~/.mime.types</CODE> file. <P>
+
+In Windows,
+<EM>Alpine</EM> looks in the same directory as the PINERC file and
+the same dir as ALPINE.EXE. This is similar to the UNIX situation with
+personal config info coming before potentially shared config data. An
+alternate search path can be specified by setting the
+<A HREF="config.html#mimetype-search-path"><EM>mimetype-search-path</EM></A>
+variable in the user or system-wide
+configuration or by setting the <CODE>MIMETYPES</CODE> environment
+variable. <P>
+
+These files specify file extensions that will be connected to a mime type.
+Lines beginning with a '<CODE>#</CODE>' character are treated as comments
+and ignored. All other lines are treated as a mime type definition. The
+first word is a <EM>type/subtype</EM> specification. All following words
+are file <EM>extensions</EM> belonging to that type/subtype. Words are
+separated by whitespace characters. If a file extension occurs more than
+once, then the first definition determines the file type and subtype.
+A couple sample lines from a mime.types file follow: <P>
+
+<PRE>
+image/gif gif
+text/html html htm
+video/mpeg mpeg mpg mpe<BR>
+</PRE>
+
+<HR>
+
+<H2><A NAME="color-config-notes">Color Details</A></H2>
+
+UNIX <EM>Alpine</EM> may display color if the terminal or terminal emulator you
+are using is capable of displaying colors.
+If the terminal supports ANSI color escape sequences you will be able to
+turn color on using the <A HREF="config.html#color-style">color-style</A>
+option and setting it to the value <EM>force-ansi-8color</EM> or
+<EM>force-ansi-16color</EM>.
+If instead you'd like <EM>Alpine</EM> to automatically detect whether or not
+you are on a color terminal, set <EM>color-style</EM> to <EM>use-termdef</EM>
+<EM>and</EM> configure the termcap entry to describe your terminal's
+color capabilities.
+<P>
+
+If the <EM>color-style</EM> option is set to <EM>use-termdef</EM>,
+<EM>Alpine</EM> looks in
+the terminal capabilities database, TERMINFO or TERMCAP, depending on
+how <EM>Alpine</EM> was compiled, to decide whether or not your terminal is
+capable of color.
+For TERMINFO compiled <EM>Alpine</EM>s, the capabilities that are used for
+color are
+&quot;colors&quot;, &quot;setaf&quot;, &quot;setab&quot;,
+&quot;op&quot;, and &quot;bce&quot;.
+If you have a terminal with color capabilities described by
+the &quot;scp&quot; capability, <EM>Alpine</EM> does not support it.
+The capabilities
+&quot;setf&quot; and &quot;setb&quot; may be used instead of
+&quot;setaf&quot; and &quot;setab&quot;.
+The capability &quot;bce&quot; is optional and is used as an optimization,
+the other capabilities are required.
+For TERMCAP compiled <EM>Alpine</EM>s, the capabilities that are used for
+color are
+&quot;Co&quot;, &quot;AF&quot;, &quot;AB&quot;, &quot;op&quot;,
+and &quot;ut&quot;.
+The capabilities
+&quot;Sf&quot; and &quot;Sb&quot; may be used instead of
+&quot;AF&quot; and &quot;AB&quot;, though this isn't a useful feature.
+<P>
+
+Here are some short descriptions of the capabilities listed
+above.
+The TERMINFO name is listed, followed by the TERMCAP name in parentheses.
+<DL COMPACT>
+
+<DT> <EM>colors</EM> (<EM>Co</EM>)
+
+<DD> The number of different colors.
+<P>
+
+<DT> <EM>setaf</EM> (<EM>AF</EM>)
+
+<DD> Set ANSI foreground color.
+<P>
+
+<DT> <EM>setab</EM> (<EM>AB</EM>)
+
+<DD> Set ANSI background color.
+<P>
+
+<DT> <EM>setf</EM> (<EM>Sf</EM>)
+
+<DD> Set foreground color. Alternate form of <EM>setaf</EM>.
+<P>
+
+<DT> <EM>setb</EM> (<EM>Sb</EM>)
+
+<DD> Set background color. Alternate form of <EM>setab</EM>.
+<P>
+
+<DT> <EM>op</EM> (<EM>op</EM>)
+
+<DD> Set default pair to its original value.
+<P>
+
+<DT> <EM>bce</EM> (<EM>ut</EM>)
+
+<DD> Screen is erased with current background color instead
+of default background.
+<P>
+
+<P>
+</DL>
+<P>
+A standard ANSI terminal which supports color will have
+a TERMINFO entry which contains:
+<PRE>
+ colors#8
+ setaf=\E[3%p1%dm
+ setab=\E[4%p1%dm
+ op=\E[39;49m
+ bce
+</PRE>
+<P>
+or the TERMCAP equivalent:
+<PRE>
+ Co#8
+ AF=\E[3%dm
+ AB=\E[4%dm
+ op=\E[39;49m
+ ut
+</PRE>
+<P>
+
+If there are eight colors, the program uses colors 0, 1, ..., 7.
+For an ANSI terminal, the foreground color is set by sending the escape
+sequence &quot;Escape LeftBracket 3 color_number m&quot; to the terminal.
+The background color is set by sending the sequence
+&quot;Escape LeftBracket 4 color_number m&quot;.
+ANSI colors zero through seven are defined to be &quot;black&quot;,
+&quot;red&quot;, &quot;green&quot;, &quot;yellow&quot;, &quot;blue&quot;,
+&quot;magenta&quot;, &quot;cyan&quot;, and &quot;white&quot;.
+Some terminal emulators will swap blue and red and swap yellow and cyan.
+The capabilities &quot;setf&quot; and &quot;setb&quot; are usually designed
+for those terminals so that they will flip the color numbers 1 and 4 and
+the numbers 3 and 6 to compensate for this.
+<EM>Alpine</EM> will use the ANSI versions of the capabilities if they exist,
+and will use the non-ANSI versions (setf and setb) if the ANSI versions
+don't exist.
+Here's a version which does the flipping.
+This can only be used with TERMINFO <EM>Alpine</EM>s, because of the arithmetic,
+which is not supported by TERMCAP.
+<PRE>
+ colors#8
+ setf=\E[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m
+ setb=\E[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m
+ op=\E[39;49m
+ bce
+</PRE>
+<P>
+
+Some terminal emulators are capable of displaying eight more colors when
+the foreground colors 30-37 are replaced with 90-97 and the background
+colors 40-47 are replaced with 100-107.
+These terminals require a fancy termcap entry which can take foreground
+colors 0, 1, ..., 15 and map that into 30, 31, ..., 37, 90, 91, ..., 97,
+and similarly for the background colors.
+Here is a terminfo entry which will do just that:
+<PRE>
+ colors#16
+ setaf=%p1%{8}%/%{6}%*%{3}%+\E[%d%p1%{8}%m%dm
+ setab=%p1%{8}%/%{6}%*%{4}%+\E[%d%p1%{8}%m%dm
+ op=\E[39;49m
+ bce
+</PRE>
+and here is the termcap equivalent:
+<PRE>
+ Co#16
+ AF=\E[%i%i%>\001\034%>\045\064%dm
+ AB=\E[%i%i%>\001\046%>\057\064%dm
+ op=\E[39;49m
+ ut
+</PRE>
+<P>
+This is a terminfo entry for 16 colors that also does the color flipping:
+<PRE>
+ colors#16
+ setf=%p1%{8}%/%{6}%*%{3}%+\E[%d%p1%{8}%m%Pa%?%ga%{1}%=%t4%e%ga%{3}%=%t6%e%ga%{4}%=%t1%e%ga%{6}%=%t3%e%ga%d%;m
+ setb=%p1%{8}%/%{6}%*%{4}%+\E[%d%p1%{8}%m%Pa%?%ga%{1}%=%t4%e%ga%{3}%=%t6%e%ga%{4}%=%t1%e%ga%{6}%=%t3%e%ga%d%;m
+ op=\E[39;49m
+ bce
+</PRE>
+<P>
+
+If you are always using the same display it probably won't matter to you
+if the color pairs red/blue and cyan/yellow are flipped, since you'll
+always be seeing them flipped.
+You will get different defaults than on a display with them not flipped,
+but that's about all.
+If you are trying to use the same pinerc file from displays with different
+color characteristics, or from <EM>Alpine</EM> and <EM>PC-Alpine</EM>, you will
+have to be more careful.
+The colors numbered 0 through 7 may be used portably between different
+systems if you are careful to make them correspond to the ANSI order mentioned
+above.
+You can check this by looking at a color configuration screen for one of the
+colors.
+The first eight colors should be in the order above.
+If they aren't, you could fix that by modifying your termcap entry on
+the UNIX system.
+This is not possible if your system uses TERMCAP instead of TERMINFO.
+
+<HR>
+
+<H2><A NAME="smime-general">S/MIME Overview</A></H2>
+
+UNIX <EM>Alpine</EM> only.
+<P>
+S/MIME is a standard for the public key encryption and signing of email.
+UNIX <EM>Alpine</EM> contains a basic implementation of S/MIME based on
+the <A HREF="http://www.openssl.org/">OpenSSL</A> libraries.
+<P>
+Some limitations:
+<UL>
+ <LI> There is no <EM>PC-Alpine</EM> implementation.
+ <LI> There is no provision for checking for CRLs
+ (Certificate Revocation Lists) in <EM>Alpine</EM>.
+ <LI> This built-in S/MIME implementation is not compatible with and does not help with PGP.
+ <LI> There is no mechanism available for feeding either an entire incoming
+ or an entire outgoing message to an external
+ filter and using that external filter to do S/MIME or PGP processing.
+ <LI> Because the implementation currently uses OpenSSL, there is only a very
+ limited integration with the Mac OS Keychain (the storing and access of
+ public certificates).
+ <LI> There is no way to view or manipulate the lists of certificates from
+ within <EM>Alpine</EM>.
+</UL>
+<P>
+The S/MIME configuration screen is reached by going to the Main Menu and typing
+the &quot;S&nbsp;Setup&quot; command followed by &quot;M&nbsp;S/MIME&quot;.
+<P>
+
+<H3>S/MIME BASICS</H3>
+
+In order to digitally sign messages you send you must have a public/private key-pair.
+This may be obtained from a public Certificate Authority (CA) such as Thawte, Verisign, Comodo,
+or GoDaddy; or from a smaller CA such as a university which provides certificates for its
+users or a company which provides certificates for its workers.
+These certificates are bound to an email address, so the identity being verified is the
+email address not a person's name.
+<P>
+Mail is signed by using the sender's private key, which only the owner of the private key
+has access to.
+The signature is verified using the signer's public key, which anyone can
+have access to.
+With <EM>Alpine</EM>, the first time you receive a signed message the public key of the
+sender will be stored for future use.
+
+<P>
+Mail is encrypted using the recipient's public key and decrypted by
+the recipient with their private key.
+
+<P>
+You need a key of your own in order to sign outgoing messages and to have others
+encrypt messages sent to you.
+You do not need a key of your own to verify signed messages sent by others or to
+encrypt messages sent to others.
+
+<H3>ALPINE S/MIME CERTIFICATE STORAGE</H3>
+
+By default UNIX <EM>Alpine</EM> stores the certificates it uses in a directory in your
+home directory.
+The directory name is
+<P>
+<CENTER><SAMP>.alpine-smime</SAMP></CENTER>
+<P>
+Within that directory are three subdirectories.
+Each of the three subdirectories contains files with PEM-encoded contents,
+the default format for OpenSSL.
+The &quot;<SAMP>public</SAMP>&quot; directory contains public certificates.
+The files within that directory have names that are email addresses with the
+suffix &quot;<SAMP>.crt</SAMP>&quot; appended.
+An example filename is
+<P>
+<CENTER><SAMP>user@example.com.crt</SAMP></CENTER>
+<P>
+The &quot;<SAMP>private</SAMP>&quot; directory contains private keys, probably just one for
+your private key.
+These are also email addresses but with the suffix &quot;<SAMP>.key</SAMP>&quot; instead.
+The third directory is &quot;<SAMP>ca</SAMP>&quot; and it contains certificates for any Certificate
+Authorities that you want to trust but that aren't contained in the set of system CAs.
+Those files may have arbitrary names as long as they end with the
+suffix &quot;<SAMP>.crt</SAMP>&quot;.
+
+<H3>HOW TO SIGN AND ENCRYPT</H3>
+
+If you have a certificate you may sign outgoing messages.
+After typing the Ctrl-X command to send a message you will see the prompt
+<P>
+<CENTER><SAMP>Send message?</SAMP></CENTER>
+<P>
+Available subcommands include &quot;G&nbsp;Sign&quot; and &quot;E&nbsp;Encrypt&quot;.
+Typing the &quot;G&quot; command will change the prompt to
+<P>
+<CENTER><SAMP>Send message (Signed)?</SAMP></CENTER>
+<P>
+Typing the &quot;E&quot; command will change the prompt to
+<P>
+<CENTER><SAMP>Send message (Encrypted)?</SAMP></CENTER>
+<P>
+You may even type both to get
+<P>
+<CENTER><SAMP>Send message (Encrypted, Signed)?</SAMP></CENTER>
+<P>
+
+<H3>HOW TO READ SIGNED OR ENCRYPTED MESSAGES</H3>
+
+The reading of a signed message should not require any special action on
+your part.
+There should be an editorial addition at the start of the message which
+says either
+<P>
+<CENTER><SAMP>This message was cryptographically signed.</SAMP></CENTER>
+<P>
+or
+<P>
+<CENTER><SAMP>This message was cryptographically signed but the signature could not be verified.</SAMP></CENTER>
+<P>
+If an encrypted message is sent to you the encrypted text will not
+be shown.
+You will have to type the &quot;Ctrl-D&nbsp;Decrypt&quot; command (from the screen where
+you are viewing the message) and supply your passphrase when asked.
+<P>
+For a signed or encrypted message there is also a &quot;Ctrl-E&nbsp;Security&quot; command
+which gives you some information about the certificate used to sign or encrypt the message.
+
+<H3>MISCELLANEOUS</H3>
+
+You may have access to a private certificate in the PKCS12 format,
+which would sometimes be in a file with a &quot;.p12&quot; suffix.
+The UNIX shell command
+<P>
+<CENTER><SAMP>openssl pkcs12 -in file.p12 -out file.pem</SAMP></CENTER>
+<P>
+may work to convert that from the PKCS12 format to the PEM format.
+Then that file could be placed in the &quot;<SAMP>private</SAMP>&quot;
+directory with a filename of your email address followed by the
+suffix &quot;<SAMP>.key</SAMP>&quot;.
+
+<HR>
+
+<H2><A NAME="pc-notes">Additional Notes on PC-Alpine</A></H2>
+
+Below are a few odds and ends worth mentioning about <EM>PC-Alpine</EM>. They have
+to do with DOS-specific behavior that is either necessary or useful (and
+sometimes both!). <P>
+
+As <EM>PC-Alpine</EM> runs in an environment with limited access control, accounting
+or auditing, an additional line is automatically inserted into the header
+of mail messages generated by <EM>PC-Alpine</EM>:
+
+<PRE>
+ X-Sender: &lt;userid&gt;@&lt;imap.host&gt;<BR>
+</PRE>
+<P>
+
+By popular demand of system administrators, <EM>PC-Alpine</EM> has been modified to
+prevent sending messages until the user has successfully logged into a
+remote mail server. Even though <EM>PC-Alpine</EM> cannot prevent users from
+changing the apparent identity of the sender of a message, the IMAP server
+login name and host name included in the <EM>X-Sender</EM> line
+provide some level of traceability by the recipient.
+However, this should not be considered a
+rigorous form of authentication. It is extremely lightweight, and is not
+a replacement for true authentication. <P>
+
+Hand in hand with authentication and accounting is user information.
+Since <EM>PC-Alpine</EM> has no user database to consult for <EM>user-id</EM>,
+<EM>personal-name</EM>, etc., necessary information must be provided by
+the user/installer before <EM>PC-Alpine</EM> can properly construct the "From"
+address required for outbound messages. <EM>PC-Alpine</EM> will, by default, prompt
+for the requisite pieces as they are needed. This information corresponds
+to the <EM>PINERC</EM> variables
+<A HREF="config.html#user-id"><EM>user-id</EM></A>,
+<A HREF="config.html#personal-name"><EM>personal-name</EM></A>,
+<A HREF="config.html#user-domain"><EM>user-domain</EM></A>,
+and <A HREF="config.html#smtp-server"><EM>smtp-server</EM></A>. <P>
+
+The user is then asked whether or not this information should
+automatically be saved to the <EM>PINERC</EM>. This is useful behavior in
+general, but can lead to problems in a lab or other shared environment.
+Hence, these prompts and automatic saving of configuration can be turned
+off on an entry by entry basis by setting any of the above values in the
+<EM>PINERC</EM> to the null string (i.e., a pair of double quotes). This
+means that the user will be prompted for the information once during each
+<EM>Alpine</EM> session, and no opportunity to save them in the <EM>PINERC</EM> will
+be offered. <P>
+
+Another feature of DOS is the lack of standard scratch area for temporary
+files. During the course of a session, <EM>PC-Alpine</EM> may require numerous
+temporary files (large message texts, various caches, etc.). Where to
+create them can be a problem, particularly when running under certain
+network operating systems. <EM>PC-Alpine</EM> observes the
+<EM>TMPDIR</EM>, <EM>TMP</EM>, and
+<EM>TEMP</EM> environment variables, and creates temporary files in the
+directory specified by either. In their absence, <EM>PC-Alpine</EM> creates these
+files in the root of the current working drive.
+Some temporary files have to be created in the same directory as the file
+they are a temporary copy of. For example, a pinerc file or a address book
+file.
+<P>
+
+<!-- pnuts -->
+
+</BODY>
+</HTML>
diff --git a/doc/tech-notes/config.html b/doc/tech-notes/config.html
new file mode 100644
index 00000000..b9354838
--- /dev/null
+++ b/doc/tech-notes/config.html
@@ -0,0 +1,12523 @@
+<HTML><HEAD><TITLE>Alpine Technical Notes: Configuration and Preferences</TITLE></HEAD><BODY>
+<H1>Configuration and Preferences</H1>
+
+<H2><A NAME="pine-conf">Alpine Configuration</A></H2>
+
+There is very little in <EM>Alpine</EM> which <STRONG>requires</STRONG> compile-time
+configuration. In most cases, the compiled-in preferences will suit users
+and administrators just fine. When running <EM>Alpine</EM> on a UNIX system, the
+default built-in configuration can be changed by setting variables in the
+system configuration files, <CODE>/usr/local/lib/pine.conf</CODE>
+or <CODE>/usr/local/lib/pine.conf.fixed</CODE>.
+(Actually, these files can be changed using the configure arguments
+--with-system-pinerc=VALUE or --with-system-fixed-pinerc=VALUE.)
+The location of the pine.conf file can be changed with the -P command line
+argument.
+Both <EM>Alpine</EM>
+and <EM>PC-Alpine</EM> also use personal (user-based) configuration files.
+On UNIX machines, the personal configuration file is the
+file <CODE>~/.pinerc</CODE>.
+For <EM>PC-Alpine</EM> systems, the personal configuration file is in
+<CODE>$PINERC</CODE> or <CODE>&lt;PineRC registry value&gt;</CODE> or
+<CODE>${HOME}\ALPINE\PINERC</CODE> or
+<CODE>&lt;ALPINE.EXE </CODE>dir<CODE>&gt;\PINERC</CODE>.
+Or the personal configuration file can be specified with the -p command
+line argument.
+<P>
+All of these configuration files, other than the fixed system
+config <CODE>pine.conf.fixed</CODE> on UNIX systems, may optionally
+be remote configuration files instead of local files.
+This is discussed further in the following section and in
+<A HREF="low-level.html#remote-config"><EM>Remote Configuration</EM></A>.
+
+<P>
+After the personal configuration, <EM>Alpine</EM> may optionally use
+a personal exceptions configuration file which is specified with the
+command line option &quot;-x exceptions_config&quot;.
+&quot;Exceptions_config&quot; may also be either a local file or a remote
+configuration folder.
+For Unix <EM>Alpine</EM>, if you don't have a &quot;-x&quot; command line option,
+<EM>Alpine</EM> will look for the file &quot;<CODE>.pinercex</CODE>&quot;
+in the same local directory that the regular config file is located in.
+If the regular config file is remote then Unix <EM>Alpine</EM> looks in the home
+directory for &quot;<CODE>.pinercex</CODE>&quot;.
+<P>
+For <EM>PC-Alpine</EM>, if you don't have a &quot;-x&quot; command line option,
+<EM>PC-Alpine</EM> will use the value of the
+environment variable <CODE>$PINERCEX</CODE>.
+If that is not set, <EM>PC-Alpine</EM> will look for
+the local file &quot;<CODE>PINERCEX</CODE>&quot;
+in the same local directory that the regular config file is located in.
+If the regular config file is remote then <EM>PC-Alpine</EM> looks in the
+local directory specfied by the &quot;-aux local_directory&quot; command
+line argument, or the directory <CODE>${HOME}&#92;ALPINE</CODE>, or
+in <CODE>&lt;ALPINE.EXE </CODE>directory<CODE>&gt;</CODE>.
+<P>
+
+The syntax of a non-list configuration variable is this:
+
+<BLOCKQUOTE>
+&lt;variable&gt; = &lt;value&gt;<BR>
+</BLOCKQUOTE>
+
+If the value is absent then the variable is unset. To set a variable to
+the empty value two double quotes (""). This is equivalent to an absent value
+except that it overrides any system-wide default value that may be set. Quotes
+may be used around any value. All values are strings and end at the end
+of the line or the closing quote. Leading and trailing space is ignored
+unless it is included in the quotes. There is one variable,
+<EM>use-only-domain-name</EM>, for which the only
+appropriate values are <EM>yes</EM> and <EM>no</EM>. That's because it is
+a variable from the early days of <EM>Alpine</EM> before features existed.
+<P>
+
+There is also a
+second type of variable, lists. A list is a comma-separated list of
+values. The syntax for a list is:
+
+<BLOCKQUOTE>
+&lt;variable&gt; = &lt;value&gt; [, &lt;value&gt; , ... ]<BR>
+</BLOCKQUOTE>
+
+A list can be continued on subsequent lines by beginning the line with
+white-space. Both the per-user and global configuration files may contain
+comments which are lines beginning with a <CODE>#</CODE>. <P>
+
+For UNIX <EM>Alpine</EM>, there are five ways in which each variable can be set.
+In decreasing order of precedence they are:
+
+<OL>
+
+<LI> the system-wide <EM>fixed</EM> configuration file
+
+<LI> a command line argument
+
+<LI> the personal exceptions file
+
+<LI> the personal configuration file
+
+<LI> the system-wide configuration file.
+
+</OL><P>
+
+If the variable is not set in any of those places, there is a default
+setting in the source code.
+<P>
+So, system-wide fixed settings always take precedence over command line
+flags, which take precedence over per-user exception settings, which take precedence over per-user settings, which take precedence
+over system-wide configuration settings.
+<EM>PC-Alpine</EM> has the same list, except that it
+does not use a system-wide <EM>fixed</EM> configuration file.
+This can be modified slightly by using inheritance, which is covered below.
+<P>
+
+You may get a sample/fresh copy of the system configuration file by
+running <EM>alpine -conf</EM>. The result will be printed on the standard
+output with very short comments describing each variable. (The online help in
+the Setup screens provides much longer comments.) If you need to fix some
+of the configuration variables, you would use the same template for the
+fixed configuration file as for the regular system-wide configuration
+file. (If it isn't clear, the purpose of the fixed configuration file is
+to allow system administrators to restrict the configurability of <EM>Alpine</EM>.
+It is by no means a bullet-proof method.) <EM>Alpine</EM> will automatically create
+the personal configuration file the first time it is run, so there is no
+need to generate a sample. <EM>Alpine</EM> reads and writes the personal
+configuration file occasionally during normal operation. Users will not
+normally look at their personal configuration file, but will use the
+Setup screens from within <EM>Alpine</EM> to set the values in this file. If a
+user does add additional comments to the personal configuration file they
+will be retained.
+<P>
+
+References to environment variables may be included in the <EM>Alpine</EM>
+configuration files. The format is <CODE>$variable</CODE> or
+<CODE>${variable}.</CODE> The character <CODE>~</CODE> will be expanded to the
+<CODE>$HOME</CODE> environment variable.
+For a more complete explanation of how environment variables work, see
+the section
+<A HREF="config-notes.html#env-variables">Using Environment Variables</A>.<P>
+
+When environment variables are used for <EM>Alpine</EM> settings which take lists,
+you must have an environment variable set for each member of the list.
+That is, <EM>Alpine</EM> won't properly recognize an environment variable which is
+set equal to a comma-delimited list. It is OK to reference unset
+environment variables in the <EM>Alpine</EM> configuration file, which
+will expand to nothing. <P>
+
+<H3>Remote and Local Configuration</H3>
+
+There are two types of storage for configuration information.
+<EM>Local</EM> configuration files are used by default.
+These are just regular files on the UNIX system or on the PC.
+<EM>Remote</EM> configuration folders are stored on an IMAP server.
+The advantage of using a remote configuration is that the same information
+may be accessed from multiple platforms.
+For example, if you use one computer at work and another at home, the same
+configuration could be used from both places.
+A configuration change from one place would be seen in both places.
+Technical information about remote configuration is in
+<A HREF="low-level.html#remote-config"><EM>Remote Configuration</EM></A>.
+
+<H3>Generic and Exceptional Configuration</H3>
+
+If you use <EM>Alpine</EM> from more than one platform it may be convenient
+to split your configuration information into two pieces, a generic piece
+and exceptions which apply to a particular platform.
+For example, suppose you use <EM>Alpine</EM> from home and from work.
+Most of your configuration settings are probably the
+same in both locations, so those settings belong in the generic settings
+configuration.
+However, you may use a different SMTP server and INBOX
+from home than you do from work.
+The &quot;smtp-server&quot; and &quot;inbox-path&quot; variables could be
+part of your exceptional configuration so that they could be different in the
+two places.
+<P>
+You can use the command line option &quot;-x config&quot;
+to split your configuration into generic and exceptional pieces.
+Config may be either local or remote.
+<P>
+For most people, splitting the configuration information into two pieces is
+only going to be useful if the generic information is accessed remotely.
+If you already have a local pinerc file with settings you like you may find
+that the command Setup/RemoteConfigSetup will be useful in helping you
+convert to a remote configuration.
+The command line flag
+<A HREF="cmd-line.html#copy_pinerc">copy_pinerc</A>
+may also be useful.
+
+<H3>Configuration Inheritance</H3>
+
+Configuration inheritance is a power user feature.
+It is confusing and not completely supported by the configuration
+user interface.
+<P>
+For configuration variables which are lists, like &quot;smtp-server&quot; or
+&quot;incoming-folders&quot;,
+the inheritance mechanism makes it possible to <EM>combine</EM>
+the values of options from different configuration locations instead
+of <EM>replacing</EM> the value.
+<A HREF="config-notes.html#config-inheritance">Configuration Inheritance</A>
+has more information about how inheritance is used.
+<P>
+
+<HR>
+
+<H2><A NAME="gen-conf">General Configuration Variables</A></H2>
+
+The following is a list of all <EM>Alpine</EM> configuration variables, in
+alphabetical order. Note that not all variables apply to all versions of
+<EM>Alpine</EM> and that some variables are only applicable in a system configuration
+file and some are only applicable in a personal configuration file.
+These are configuration <EM>variables</EM>.
+<A HREF="#features-conf">Configuration <EM>Features</EM></A>
+are in a separate section.
+<P>
+
+<DL COMPACT>
+
+<DT> <A NAME="addrbook-sort-rule"><EM>addrbook-sort-rule</EM></A>
+
+<DD> This variable sets up the default address book sorting. Currently,
+<EM>Alpine</EM> will accept the values <EM>dont-sort</EM>,
+<EM>fullname-with-lists-last</EM>, <EM>fullname</EM>,
+<EM>nickname-with-lists-last</EM>, and <EM>nickname</EM>. The default is
+to sort by fullname with lists last.
+If you use an address book from more than one computer and those
+computers sort the address book differently then the sort order
+will be the order where the last change to the address book was
+made.
+There are two reasons the sorting might be different on different
+systems.
+First, the addrbook-sort-rule may be set differently in the two
+places.
+Second, the collation rules on the two computers may be different.
+For example, one system might ignore special characters while the other
+doesn't or one may sort upper and lower case letters together while
+the other doesn't.
+In any case, the order you see is the order on the system where the
+last change was made, for example by an address book edit or a
+Take Address command.
+<P>
+This option is displayed as &quot;Addressbook Sort Rule&quot;.
+<P>
+
+<DT> <A NAME="pers-abook"><EM>address-book</EM></A>
+
+<DD> A list of personal address books.
+Each entry in the list is an
+optional nickname followed by a pathname or file name relative to the home
+directory.
+The nickname is separated from the rest of the line with whitespace.
+Instead of a local pathname or file name, a remote folder name can be given.
+This causes the address book to
+be a <A HREF="low-level.html#addrbook"><EM>Remote address book</EM></A>.
+Remote folder syntax is discussed in
+<A HREF="config-notes.html#remote-folders">Syntax for Remote Folders</A>.
+This list of address books will be combined with the
+<A HREF="#glob-abook"><EM>global-address-book</EM></A>
+list to arrive at the complete set of address books. <P>
+
+<DT> <A NAME="abook-formats"><EM>addressbook-formats</EM></A>
+
+<DD> This option specifies the format that address books are displayed in.
+By default, address books are displayed with the nicknames in the first
+column, the fullnames in the second column, and addresses in the third
+column. The system figures out reasonable defaults for the widths of the
+columns. An address book may be given a different format by listing
+special tokens in the order you want them to display. The possible tokens
+are NICKNAME, FULLNAME, ADDRESS, FCC, and COMMENT. More details are included
+in the online help for this variable. <P>
+
+<DT> <A NAME="alt-addresses"><EM>alt-addresses</EM></A>
+
+<DD> This option provides a place for you to list alternate email addresses
+you may have.
+Each address in the list should be the actual email address part of an
+address, without the full name field or the angle brackets.
+For example:
+
+<P>
+<CENTER><SAMP>user@example.com</SAMP></CENTER>
+<P>
+
+The matching is case-insensitive, so this would match any of
+<SAMP>User@example.com</SAMP>, <SAMP>user@Example.Com</SAMP>, or
+<SAMP>USER@EXAMPLE.COM</SAMP> as well.
+
+<P>
+If set, the option affects the behavior of the Reply
+command and the &quot;+&quot; symbol in the MESSAGE INDEX, which denotes that
+a message has been addressed specifically to you.
+
+<P>
+In the default INDEX display
+the personal name (or email address) of
+the person listed in the message's &quot;From:&quot; header
+field is usually displayed except when that address is yours or one of your
+alternate addresses.
+In that case you will usually see the name of
+the first person specified in the
+message's &quot;To:&quot; header field
+with the prefix &quot;To: &quot; prepended.
+
+<P>
+With respect to Reply, the reply-to-all option will exclude addresses
+listed here.
+
+<P>
+The feature
+<A HREF="#copy-to-to-from">copy-to-address-to-from-if-it-is-us</A>
+is somewhat related to this option.
+
+<P>
+In addition to a list of actual addresses,
+you may use regular expressions (as used with egrep with the ignore case flag)
+to describe the addresses you want to match.
+<EM>Alpine</EM> will somewhat arbitrarily interpret your entry as a regular
+expression if it contains any of the characters
+*, |, +, ?, {, [, ^, $, or &#92;.
+Otherwise, it will be treated literally.
+The feature
+<A HREF="#disable-regex">disable-regular-expression-matching-for-alternate-addresses</A>
+may be used to turn off regular expression processing regardless of whether or not
+special characters appear in the entry.
+
+<P>
+A description of how regular expressions work is beyond the
+scope of this help text, but some examples follow.
+
+<P>
+The entry
+
+<P>
+<CENTER><SAMP>.*@example.com</SAMP></CENTER>
+<P>
+
+in the alt-addresses list would mean that any
+address with a domain name of <SAMP>example.com</SAMP> (such as
+<SAMP>fred@example.com</SAMP> or <SAMP>wilma@example.com</SAMP>) will be considered
+one of your alternate addresses.
+Strictly speaking, the dot in <SAMP>example.com</SAMP> ought to be escaped with
+a backslash, as in <SAMP>example&#92;.com</SAMP>, and a dollar sign anchor ought
+to come at the end of the expression to prevent a match of <SAMP>example.com.org</SAMP>.
+Complicating things further, the dollar sign
+is special in the <EM>Alpine</EM> configuration (it signifies environment variable expansion)
+so the dollar sign should be doubled or backslash escaped for <EM>Alpine</EM>'s sake.
+Quotes around the whole expression will not escape the dollar sign successfully.
+So this example should look like
+
+<P>
+<CENTER><SAMP>.*@example&#92;.com$$</SAMP></CENTER>
+<P>
+
+<P>
+The entry
+
+<P>
+<CENTER><SAMP>^fred[0-9]*@example.com$$</SAMP></CENTER>
+<P>
+
+would match
+<SAMP>fred3@example.com</SAMP> or <SAMP>fred17@example.com</SAMP> as well
+as <SAMP>fred@example.com</SAMP>.
+
+<P>
+You could match all addresses that look like
+<SAMP>fred+stuff@example.com</SAMP> for any value of <SAMP>stuff</SAMP> with the
+entry
+
+<P>
+<CENTER><SAMP>^fred&#92;+.*@example.com$$</SAMP></CENTER>
+<P>
+
+Notice that you have to escape the plus sign with a backslash because plus
+is a special character in regular expressions.
+If you wanted to match plain <SAMP>fred</SAMP> as well as <SAMP>fred+stuff</SAMP>
+the expression
+
+<P>
+<CENTER><SAMP>^fred(()|&#92;+.*)@example.com$$</SAMP></CENTER>
+<P>
+
+would do it, but it would be easier to just add fred@example.com as a
+separate entry.
+
+<P>
+One more example, a match of all first-level subdomains, is given by
+
+<P>
+<CENTER><SAMP>^fred@[[:alnum:]_-]*&#92;.example&#92;.com$$</SAMP></CENTER>
+<P>
+
+<P>
+Because the regular expression matching is based on an old library
+(<SAMP>hs_regex</SAMP>) the regular expressions might not work exactly as you expect,
+but they should be close.
+
+<P>
+This option is displayed as &quot;Alternate Addresses&quot;.
+
+<DT> <A NAME="bugs-add"><EM>bugs-additional-data</EM></A>
+
+<DD> System-wide configuration files only. Program/Script used by
+<EM>Report Bug</EM> command. Output from the program/script is
+captured and attached to the bug report. <P>
+
+<DT> <A NAME="bugs"><EM>bugs-fullname</EM></A>,
+<EM>bugs-address</EM>, <EM>local-fullname</EM>, <EM>local-address</EM>,
+<EM>suggest-fullname</EM>, and <EM>suggest-address</EM>
+
+<DD> System-wide configuration files only. These are used by the bug
+report commands which can be accessed from some of the Help screens.
+<P>
+
+<DT> <A NAME="busy-cue-rate"><EM>busy-cue-rate</EM></A>
+
+<DD> When <EM>Alpine</EM> is delayed for some reason it usually shows that
+something is happening with a small animated display in the status
+message line near the bottom of the screen.
+This option sets how frequently the characters (for example, a spinning bar)
+in the active status message lines are updated.
+At most, it can be set to be udpated 20 times per second.
+
+<P>
+Setting this value to zero will prevent display of the animations
+altogether.
+
+<P>
+The option
+<A HREF="#busy-cue-spinner-only"><EM>busy-cue-spinner-only</EM></A>
+can be used to remove the randomness from this animated display.
+<P>
+
+<DT> <A NAME="char-set"><EM>character-set</EM></A>
+
+<DD> This is now obsolete, replaced by three separate variables:
+<EM>display-character-set</EM>,
+<EM>keyboard-character-set</EM>, and
+<EM>posting-character-set</EM>.
+See the section on
+<A HREF="low-level.html#char-set">International Character Sets</EM></A> for more
+details.<P>
+
+<DT> <A NAME="color-style"><EM>color-style</EM></A>
+
+<DD> UNIX <EM>Alpine</EM> only (color is automatically on with <EM>PC-Alpine</EM>).
+If the terminal or terminal emulator you are using is capable of displaying
+colors, this variable controls whether or not
+color will be used in <EM>Alpine</EM>.
+If you turn color on and things are set up correctly,
+you should see color appear on the screen immmediately.
+Modern terminal emulators are usually capable of displaying colors.
+<P>
+This variable may be set to any of the following values:
+<P>
+
+<DL>
+<DT>no-color</DT>
+<DD>Don't use color.
+</DD>
+
+<DT>use-termdef</DT>
+<DD>In order to decide if your terminal is capable of color,
+<EM>Alpine</EM> looks in
+the terminal capabilities database, TERMINFO or TERMCAP, depending on
+how <EM>Alpine</EM> was compiled.
+This is a good option to choose if you switch between a color and a non-color
+terminal with the same <EM>Alpine</EM> configuration.
+<EM>Alpine</EM> will know to use color on the color terminal because
+it is described
+in the termcap entry, and <EM>Alpine</EM> will know to use black and white on the
+non-color terminal.
+<A HREF="config-notes.html#color-config-notes">Color Details</A>
+has more information about configuring a termcap entry for color.
+This is usually something a system administrator does.
+</DD>
+
+<DT>force-ansi-8color</DT>
+<DD>Because setting up a termcap entry is confusing and because the
+terminal capabilities database is often not correctly configured for color,
+this choice and the next may be easier for you to use.
+If your terminal emulator responds to ANSI color escape sequences, which
+many do, this option will
+cause <EM>Alpine</EM> to believe your terminal will respond
+to the escape sequences which produce eight different foreground and background
+colors.
+The escape sequences used to set the foreground colors are
+
+ <P><CENTER>ESC&nbsp;[&nbsp;3&nbsp;&lt;color_number&gt;&nbsp;m</CENTER><P>
+
+where the color_number is an ASCII digit between 0 and 7.
+The numbers 0 through 7 should correspond to the colors black, red, green,
+yellow, blue, magenta, cyan, and white.
+Some terminal emulators use a pre-ANSI scheme which swaps
+the colors blue and red and the colors yellow and cyan.
+This will cause the default colors to be different, but other than that
+things should work fine.
+There is also a 9th color available, the last one shown, which is the default
+color from the terminal emulator.
+When used as a background color some people refer to this color as
+&quot;transparent&quot;, which is why the letters &quot;TRAN&quot; are
+shown in the color swatch of the SETUP COLOR screen.
+The foreground transparent color is shown as
+the color of the &quot;TRAN&quot; text.
+(The transparent color will not work correctly in a PC-Alpine configuration.)
+The escape sequences used to set the background colors are the same
+as for the foreground colors except a &quot;4&quot; replaces the &quot;3&quot;.
+<P>
+Note: With the Tera Term terminal emulator this setting works well.
+You should also have the Tera Term &quot;Full color&quot; option turned OFF.
+You may find the &quot;Full color&quot; option in Tera Term's &quot;Setup&quot;
+menu, in the &quot;Window&quot; submenu.
+</DD>
+
+<DT>force-ansi-16color</DT>
+<DD>Many terminal emulators know about the same eight colors above
+plus eight more.
+This option attempts to use all 16 colors.
+The same escape sequences as for the eight-color terminal are used
+for the first eight colors.
+The escape sequences used to set foreground colors 8-15 are the same as
+for 0-7 except the &quot;3&quot; is replaced with a &quot;9&quot;.
+The background color sequences for colors 8-15 are the same as for 0-7
+except the &quot;4&quot; is replaced with &quot;10&quot;.
+You can tell if the 16 colors are working by turning on this option
+and then going into one of the color configuration screens, for example,
+the configuration screen for Normal Color.
+If you see 16 different colors to select from (plus a 17th for
+the transparent color), it's working.
+</DD>
+
+<DT>force-xterm-256color</DT>
+<DD>Some versions of xterm (and some other terminal emulators)
+have support for 256 colors.
+The escape sequences used to set the foreground colors are
+
+ <P><CENTER>ESC&nbsp;[&nbsp;38&nbsp;;&nbsp;5&nbsp;;&nbsp;&lt;color_number&gt;&nbsp;m</CENTER><P>
+
+where the color_number is an ASCII digit between 0 and 255.
+Background colors are the same with the 38 replaced with a 48.
+The numbers 0 through 15 are probably similar to the 16 color version
+above, then comes a 6x6x6 color cube, followed by 24 colors of gray.
+The terminal default (transparent) color is the 257th color at the bottom.
+Some terminal emulators will misinterpret these escape sequences causing
+the terminal to blink or overstrike characters or to do something else
+undesirable.
+<P>
+The PuTTY terminal emulator has an option called &quot;Allow terminal to
+use xterm 256-colour mode&quot; which allows PuTTY to work well with
+this 256-color setting.
+
+</DD>
+</DL>
+
+<P>
+There are two other possible color values which may be useful in some
+situations.
+In the color configuration screens there will sometimes be a color which has
+the label &quot;NORM&quot; inside its color swatch.
+If this is selected the corresponding foreground or background
+<A HREF="#normal-color"><EM>Normal Color</EM></A>
+will be used.
+Another similar color is the one that has the
+label &quot;NONE&quot; inside its color swatch.
+The meaning of this setting is that no color changing will be done.
+This NONE color is only useful in contexts where <EM>Alpine</EM> is already
+coloring the text some color other than the Normal Color.
+For example, if the
+<A HREF="#reverse-color"><EM>Reverse Color</EM></A> is set then the current
+line in the MESSAGE INDEX will be colored.
+If one of the index symbols (for example, the Index-to-me Symbol) has the NONE
+color as its background then the symbol's foreground color will be used
+to draw the actual text but the background color will be the same as whatever
+the background color already was.
+The color values which end up in the configuration file for these special
+values are the 11-character words &quot;norm-padded&quot;, &quot;none-padded&quot;,
+and &quot;transparent&quot;.
+
+<P>
+The normal default is &quot;no-color&quot;.
+<P>
+Once you've turned on color you may set the
+colors of many objects on the screen individually.
+The <A HREF="#color-config">Color Configuration</A> section has more
+information, or you may just try it by
+running the &quot;Setup&quot; command and typing &quot;K&quot;
+for Kolor to enter the color configuration screen (Kolor instead of Color
+because C means Config).
+Most categories of color which <EM>Alpine</EM> supports are configurable there.
+<A HREF="#index-color-config">Index line color</A> is configured separately.
+<P>
+
+<DT> <A NAME="composer-word-separators"><EM>composer-word-separators</EM></A>
+
+<DD> This option affects how a &quot;word&quot; is defined in the composer.
+The definition of a word is used when using the Forward Word and Backward
+Word commands in the composer, as well as when using the spell checker.
+Whitespace is always considered a word separator.
+Punctuation (like question marks, periods, commas, and so on) is always
+a word separator if it comes at the end of a word.
+By default, a punctuation character which is in the middle of a word does
+not break up that word as long as the character before and the character
+after it are both alphanumeric.
+If you add a character to this option it will be considered a
+word separator even when it occurs in the middle of an alphanumeric word.
+For example, if you want to skip through each part of an address instead
+of skipping the whole address at once you might want to include&quot;@&quot;
+and &quot;.&quot; in this list.
+If you want the word-skipper to stop on each part of a UNIX filename you
+could add &quot;/&quot; to the list.
+The equal sign and dash are other possibilities you might find helpful.
+<P>
+
+<DT> <A NAME="composer-wrap-column"><EM>composer-wrap-column</EM></A>
+
+<DD> This option specifies an aspect of <EM>Alpine</EM>'s Composer. This gives the
+maximum width that auto-wrapped lines will have. It's also the maximum
+width of lines justified using the ^J Justify command. The normal default
+is <EM>74</EM>. The largest allowed setting is normally <EM>80</EM> in order to
+prevent very long lines from being sent in outgoing mail. When the mail
+is actually sent, trailing spaces will be stripped off of each line.
+<P>
+
+<DT> <A NAME="current-indexline-style"><EM>current-indexline-style</EM></A>
+
+<DD> <A HREF="#cur-il-style"><EM>current-indexline-style</EM></A>.
+<P>
+
+<DT> <A NAME="cust-hdr"><EM>customized-hdrs</EM></A>
+
+<DD> You may add your own custom headers to outgoing messages.
+Each header you specify here must include the header tag
+(Reply-To:, Approved:, etc.)
+and may optionally include a value for that header.
+If you want to see these custom headers each time you compose a message,
+you must add them to your
+<A HREF="#def-comp"><EM>default-composer-hdrs</EM></A> list,
+otherwise they become part
+of the rich header set which you only see when you press the rich header
+command.
+(If you are looking for a way to change which headers are <EM>displayed</EM>
+when you view a message, take a look at the
+<A HREF="#viewer-hdrs"><EM>viewer-hdrs</EM></A>
+option instead.)
+Here's an example which shows how you might set your From address
+<P>
+<CENTER><SAMP>From: Full Name &lt;user@example.com&gt;</SAMP></CENTER>
+<P>
+and another showing how you might set a Reply-To address
+<P>
+<CENTER><SAMP>Reply-To: user@example.com</SAMP></CENTER>
+<P>
+You may also set non-standard header values here.
+For example, you could add
+<P>
+<CENTER><SAMP>Organization: My Organization Name</SAMP></CENTER>
+<P>
+or even
+<P>
+<CENTER><SAMP>X-Favorite-Colors: Purple and Gold</SAMP></CENTER>
+<P>
+If you include a value after the colon then that header will be included
+in your outgoing messages unless you delete it before sending.
+If a header in the Customized-Headers list has only a tag but no value, then
+it will not be included in outgoing messages unless you edit a value
+in manually.
+For example, if
+<P>
+<CENTER><SAMP>Reply-To:</SAMP></CENTER>
+<P>
+is in the list, then the Reply-To header will be available for editing
+but won't be included unless a value is added while in the composer.
+<P>
+It's actually a little more complicated than that.
+The values of headers that you set with the Customized-Headers option are
+defaults.
+If the message you are about to compose already has a value for a header,
+that value is used instead of a value from your Customized-Headers.
+For example, if you are Replying to a message the Subject field
+will already be filled in.
+In that case, if the Customized-Headers list contains a Subject line, the
+custom subject will <EM>NOT</EM> be used.
+The subject derived from the subject of the message you are Replying
+to will be used instead.
+<P>
+It is also possible to make header setting even more complicated and more
+automatic by using
+<A HREF="#role-config"><EM>Roles</EM></A>,
+but if all you want to do is set a default value for a header, you don't
+need to think about Roles.
+<P>
+If you change your From address you may also find it useful to add the
+changed From address to the
+<A HREF="#alt-addresses"><EM>alt-addresses</EM></A>
+configuration option.
+<P>
+Limitation: Because commas are used to separate the list of
+Customized-Headers, it is not possible to have the value of a
+header contain a comma.
+Nor is there currently an &quot;escape&quot; mechanism provided
+to make this work.
+<P>
+This option is displayed as &quot;Customized Headers&quot;.
+<P>
+
+<DT> <A NAME="dead-letter-files"><EM>dead-letter-files</EM></A>
+
+<DD> This option affects <EM>Alpine</EM>'s behavior when you cancel a message being
+composed. <EM>Alpine</EM>'s usual behavior is to write the canceled message to
+a file named
+&quot;dead.letter&quot; in your home directory, or
+&quot;DEADLETR&quot; when using <EM>PC-Alpine</EM>,
+overwriting any previous message.
+<P>
+If you set this option to a value higher than one, then that many copies
+of dead letter files will be saved.
+For example, if you set this option to &quot;3&quot; then you may have
+files named
+&quot;DEADLETR&quot;,
+&quot;DEADLETR2&quot;, and
+&quot;DEADLETR3&quot;; or
+&quot;dead.letter&quot;,
+&quot;dead.letter2&quot;, and
+&quot;dead.letter3&quot;.
+In this example, the most recently cancelled message will be in
+&quot;dead.letter&quot;,
+and the third most recently cancelled message will be in
+&quot;dead.letter3&quot;.
+The fourth most recently cancelled message will no longer be saved.
+
+<P>
+If you set this option to zero, then NO record of canceled messages is
+maintained.
+<P>
+If the feature
+<A HREF="#quell-dead-letter-on-cancel">Quell-Dead-Letter-On-Cancel</A>
+is set, that overrides whatever you set for this option.
+If this option had existed at the time, then the Quell feature would not
+have been added, but it is still there for backwards compatibility.
+So, in order for this option to have the desired effect, make sure the
+Quell feature is turned off.
+<P>
+
+<DT> <A NAME="def-comp"><EM>default-composer-hdrs</EM></A>
+
+<DD> You can control which headers you want visible when composing outgoing
+email using this option.
+You can specify any of the regular set, any
+Rich Header,
+or any <A HREF="#cust-hdr"><EM>Customized-Hdrs</EM></A>
+which you have already defined.
+If you use this setting at all, you must specify all the
+headers you want to see, you can't just add to the regular header set.
+The default set is To:, Cc:, Attchmnt:, and Subject:.
+<P>
+
+Note that the "Newsgroups:" header will be abbreviated in the Composer
+display, but should be spelled out in full here.
+<P>
+This option is displayed as &quot;Default Composer Headers&quot;.
+<P>
+
+<DT> <A NAME="def-fcc"><EM>default-fcc</EM></A>
+
+<DD> The name of the folder to which all outgoing mail goes is set here.
+The compiled-in default is <EM>sent-mail</EM> (UNIX) or <EM>sentmail</EM>
+(PC). It can be set to "" (two double quotes with nothing between them)
+to turn off saving copies of outgoing mail. If <EM>default-fcc</EM> is a
+relative file name, then it is relative to your default collection for
+saves (see <A HREF="#fold-coll"><EM>folder-collections</EM></A>). <P>
+This option is displayed as &quot;Default Fcc (File carbon copy)&quot;.
+<P>
+
+<DT> <A NAME="def-save"><EM>default-saved-msg-folder</EM></A>
+
+<DD> This option determines the default folder name for <EM>Saves</EM>...
+If this is not a path name, it will be in the default collection for saves.
+Any valid folder specification, local or IMAP, is allowed. This default
+folder only applies when the
+<A HREF="#saved-msg-name"><EM>saved-msg-name-rule</EM></A>
+doesn't override it.
+Unix <EM>Alpine</EM> default is normally
+<EM>saved-messages</EM> in the default folder collection.
+<EM>PC-Alpine</EM> default is <EM>SAVEMAIL</EM>
+(normally stored as <EM>SAVEMAIL.MTX</EM>). <P>
+This option is displayed as &quot;Default Saved Message Folder&quot;.
+<P>
+
+<DT> <A NAME="disable-these-auths"><EM>disable-these-authenticators</EM></A>
+
+<DD> This variable is a list of SASL (Simple Authentication and Security
+Layer) authenticators which will be disabled.
+SASL is a mechanism for
+authenticating to IMAP, POP3, SMTP, and other network servers.
+<P>
+
+<EM>Alpine</EM> matches its list of supported authenticators with the server to
+determine the most secure authenticator that is supported by both.
+If no matching authenticators are found, <EM>Alpine</EM> will revert to plaintext
+login (or, in the case of SMTP, will be unable to authenticate at all).
+<P>
+
+The candidates for disabling are listed below.
+There may be more if you compile <EM>Alpine</EM> with additional authenticators
+and/or a newer version of the c-client library.
+<P>
+
+<UL>
+<LI> GSSAPI
+<LI> CRAM-MD5
+<LI> PLAIN
+<LI> LOGIN
+</UL>
+<P>
+
+Normally, you will not disable any authenticators.
+There are two exceptions:
+<P>
+
+<OL>
+<LI> You use a broken server that advertises an authenticator,
+but does not actually implement it.
+<LI> You have a Kerberos-capable version of <EM>Alpine</EM> and the server is
+also Kerberos-capable, but you can not obtain Kerberos
+credentials on the server machine, thus you desire to disable
+GSSAPI (which in turn disables <EM>Alpine</EM>'s Kerberos support).
+</OL>
+
+<P>
+It is never necessary to disable authenticators, since <EM>Alpine</EM> will try
+other authenticators before giving up.
+However, disabling the relevant authenticator avoids annoying error messages.
+<P>
+
+<DT> <A NAME="disable-these-drivers"><EM>disable-these-drivers</EM></A>
+
+<DD> This variable is a list of mail drivers which will be disabled.
+The candidates for disabling are listed below.
+There may be more in the future if you compile <EM>Alpine</EM> with
+a newer version of the c-client library.
+<P>
+
+<UL>
+<LI> mbox
+<LI> mbx
+<LI> mh
+<LI> mix
+<LI> mmdf
+<LI> mtx
+<LI> mx
+<LI> news
+<LI> phile
+<LI> tenex
+<LI> unix
+</UL>
+<P>
+
+The <EM>mbox</EM> driver enables the following behavior: if there is a
+file called <CODE>mbox</CODE>
+in your home directory, and if that file is either empty or in Unix mailbox
+format, then every time you open <EM>INBOX</EM> the <EM>mbox</EM> driver
+will automatically transfer mail from the system mail spool directory into the
+<CODE>mbox</CODE> file and
+delete it from the spool directory. If you disable the <EM>mbox</EM> driver,
+this will not happen.
+<P>
+
+It is not recommended to disable the driver which supports the system default
+mailbox format. On most non-SCO systems, that driver is the
+<EM>unix</EM> driver.
+On most SCO systems, it is the <EM>mmdf</EM> driver.
+The system default driver may be
+configured to something else on your system; check with your system manager
+for additional information.
+<P>
+
+It is most likely not very useful for you to disable any of the drivers other
+than possibly <EM>mbox</EM>.
+You could disable some of the others if you know for
+certain that you don't need them but the performance gain in doing so
+is very modest.
+<P>
+
+<DT> <A NAME="disp-char-set"><EM>display-character-set</EM></A>
+
+<DD> See the discussion in
+<A HREF="low-level.html#char-set">International Character Sets</EM></A> for
+details.<P>
+
+<DT> <A NAME="display-filters"><EM>display-filters</EM></A>
+
+<DD> This option defines a list of text-filtering commands (programs or
+scripts) that may be used to filter text portions of received messages
+prior to their use (e.g., presentation in the "Message Text" display
+screen). For security reasons, the full path name of the filter command
+must be specified.
+<P>
+Display filters do not work with <EM>PC-Alpine</EM>.
+<P>
+
+The command is executed and the message is piped into its standard input.
+The standard output of the command is read back by <EM>Alpine</EM>. The
+<EM>_TMPFILE_</EM> token (see below) overrides this default behavior.
+
+<P>
+The filter's use is based on the configured <EM>trigger</EM> string. The
+format of a filter definition is:
+
+<P>
+<CENTER>&lt;trigger&gt; &lt;command&gt; &lt;arguments&gt;</CENTER>
+
+<P>
+You can specify as many filters as you wish, separating them with a comma.
+Each filter can have only one trigger and command. Thus, two trigger
+strings which invoke the same command require separate filter
+specifications.
+
+<P>
+The <EM>trigger</EM> is simply text that, if found in the message,
+will invoke the associated command. If the trigger contains any space
+characters, it must be placed within quotes. Likewise, should you
+wish a filter to be invoked unconditionally, define the trigger as the
+null string, &quot;&quot; (two consecutive double-quote characters). If the
+trigger string is found anywhere in the text of the message the filter
+is invoked. Placing the trigger text within the tokens defined below
+changes where within the text the trigger must be before considering
+it a match.
+
+<P>
+Trigger Modifying Tokens:
+<DL>
+<DT><EM>_CHARSET(<VAR>string</VAR>)_</EM>
+<DD>This token tells <EM>Alpine</EM> to invoke the supplied command
+if the text is in a character set matching <VAR>string</VAR>
+(e.g., ISO-8859-2 or ISO-2022-JP).
+
+<DT><EM>_LEADING(<VAR>string</VAR>)_</EM>
+<DD>This token tells <EM>Alpine</EM> to invoke the supplied command
+if the enclosed <VAR>string</VAR> is found to be the first
+non-whitespace text.
+<BR>NOTE: Quotes are necessary if <VAR>string</VAR> contains
+the space character.
+
+<DT><EM>_BEGINNING(<VAR>string</VAR>)_</EM>
+<DD>This token tells <EM>Alpine</EM> to invoke the supplied command
+if the enclosed <VAR>string</VAR> is found at the beginning
+of any line in the text.
+<BR>NOTE: Quotes are necessary if <VAR>string</VAR> contains
+the space character.
+</DL>
+
+<P>
+The &quot;command&quot; and &quot;arguments&quot; portion is simply
+the command line to be invoked if the trigger string is found. Below
+are tokens that <EM>Alpine</EM> will recognize and replace with special values
+when the command is actually invoked.
+
+<P>
+Command Modifying Tokens:
+
+<DL>
+<DT><EM>_TMPFILE_</EM>
+<DD>When the command is executed, this token is
+replaced with the path and name of the temporary
+file containing the text to be filtered. <EM>Alpine</EM>
+expects the filter to replace this data with the
+filter's result.
+
+NOTE: Use of this token implies that the text to
+be filtered is not piped into standard input of the
+executed command and its standard output is ignored.
+<EM>Alpine</EM> restores the tty modes before invoking the
+filter in case the filter interacts with the user
+via its own standard input and output.
+
+<DT><EM>_RESULTFILE_</EM>
+<DD>When the command is executed, this token is
+replaced with the path and name of a temporary
+file intended to contain a status message from the
+filter. <EM>Alpine</EM> displays this in the message status
+field.
+
+<DT><EM>_DATAFILE_</EM>
+<DD>When the command is executed, this token is
+replaced with the path and name of a temporary
+file that <EM>Alpine</EM> creates once per session and deletes
+upon exit. The file is intended to be used by the
+filter to store state information between instances
+of the filter.
+
+<DT><EM>_PREPENDKEY_</EM>
+<DD>When the command is executed, this token indicates that a random
+number will be passed down the input stream before the message text.
+This number could be used as a session key.
+It does not appear as a command-line argument.
+It is sent in this way to improve security.
+The number is unique to the current <EM>Alpine</EM> session
+and is only generated once per session.
+</DL>
+
+<P>
+The feature
+<A HREF="#disable-terminal-reset-for-display-filters"><EM>disable-terminal-reset-for-display-filters</EM></A> is related.
+<P>
+Performance caveat/considerations:
+<BR>
+Testing for the trigger and invoking the filter doesn't come for free.
+There is overhead associated with searching for the trigger string, testing
+for the filter's existence and actually piping the text through the filter.
+The impact can be reduced if the Trigger Modifying Tokens above are
+employed.
+<P>
+Limitation:
+<BR>
+If Header Colors are being used, the sequences of bytes which indicate
+color changes will be contained in the text which is passed to the
+display-filter.
+If this causes problems you'll need to turn off Header Colors.
+The thirteen bytes which indicate a color change are
+the character \377 followed by
+\010 for a foreground color or \011 for a background color.
+Then comes eleven characters of RGB data which looks something like
+255,&nbsp;&nbsp;0,255, depending on the particular color, of course.
+<P>
+
+<DT> <A NAME="download-command"><EM>download-command</EM></A>
+
+<DD> This option affects the behavior of the <EM>Export</EM> command.
+It specifies a Unix program name, and any necessary command line arguments,
+that <EM>Alpine</EM> can use to transfer the exported message to your
+personal computer's disk.
+<P>
+
+<DT> <A NAME="download-command-prefix"><EM>download-command-prefix</EM></A>
+
+<DD> This option is used in conjunction with the <EM>download-command</EM>
+option.
+It defines text to be written to the terminal emulator (via standard
+output) immediately prior to starting the download command. This is
+useful for integrated serial line file transfer agents that permit command
+passing (e.g., Kermit's APC method). <P>
+
+<DT> <A NAME="editor"><EM>editor</EM></A>
+
+<DD> UNIX <EM>Alpine</EM> only. Sets the name of the alternate editor for composing
+mail (message text only, not headers). It will be invoked with the "^_"
+command or it will be invoked automatically if the
+<A HREF="#enable-alt-imp"><EM>enable-alternate-editor-implicitly</EM></A>
+feature is set. <P>
+
+<DT> <A NAME="empty-header-message"><EM>empty-header-message</EM></A>
+
+<DD> When sending, if both the To and Cc fields are empty and you
+are sending the message to a Bcc,
+<EM>Alpine</EM> will put a special address in the To line. The default value is
+"undisclosed-recipients: ;". The reason for this is to avoid
+embarrassment caused by some Internet mail transfer software that
+interprets a "missing" To: header as an error and replaces it with an
+Apparently-to: header that may contain the addresses you entered on the
+Bcc: line, defeating the purpose of the Bcc. You may change the part
+of this message that comes before the ": ;" by setting the
+<EM>empty-header-message</EM> variable to something else.
+<P>
+
+<DT> <A NAME="fcc-name-rule"><EM>fcc-name-rule</EM></A>
+
+<DD> Determines default folder name for fcc when composing. Currently,
+<EM>Alpine</EM> will accept the values <EM>default-fcc</EM>, <EM>by-recipient</EM>,
+or <EM>last-fcc-used</EM>. If set to <EM>default-fcc</EM>, then <EM>Alpine</EM> will
+use the value defined in the <A HREF="#def-fcc"><EM>default-fcc</EM></A>
+variable (which itself
+has a default) for the Fcc header field. If set to <EM>by-recipient</EM>,
+then <EM>Alpine</EM> will use the name of the recipient as a folder name for the fcc.
+The relevant recipient is the first address in the To field. If set to
+"last-fcc-used", then <EM>Alpine</EM> will offer to Fcc to whatever folder
+you used previously.
+In all cases, the field can still be edited after it is
+initially assigned. If the fcc field in the address book is set for the
+first To address, that value over-rides any value derived from this rule.
+<P>
+
+<DT> <A NAME="feat-list"><EM>feature-list</EM></A>
+
+<DD> This is a list of the many features (options) which may be turned on
+or off. There is a separate section titled
+<A HREF="#features-conf">Configuration Features</A> which explains
+each of the features. There is some additional explanation about the
+<EM>feature-list</EM> variable itself
+in <A HREF="config-notes.html#feature-list"><EM>Feature List Variable</EM></A>.
+<P>
+
+<DT> <A NAME="file-directory"><EM>file-directory</EM></A>
+
+<DD> <EM>PC-Alpine</EM> only.
+This value affects the Composer's &quot;^J Attach&quot; command,
+the Attachment Index Screen's &quot;S Save&quot; command, and the
+Message Index's &quot;E Export&quot; command.
+
+<P>
+Normally, when a filename is supplied that lacks a leading &quot;path&quot;
+component, <EM>Alpine</EM> assumes the file exists in the user's home directory.
+Under Windows operating systems, this definition isn't always clear. This
+feature allows you to explictly set where <EM>Alpine</EM> should look for files
+without a leading path.
+
+<P>
+NOTE: this feature's value is ignored if either
+<A HREF="#use-current-dir"><EM>use-current-dir</EM></A> feature
+is set or the PINERC has a value for the
+<A HREF="#operating-dir"><EM>operating-dir</EM></A> variable.
+<P>
+
+<DT> <A NAME="fold-coll"><EM>folder-collections</EM></A>
+
+<DD> This is a list of one or more collections where saved mail is stored.
+See the sections describing
+<A HREF="config-notes.html#collections">folder collections and collection syntax</A> for more information.
+The first collection in this list is the default
+collection for <EM>Save</EM>s,
+including <A HREF="#def-fcc"><EM>default-fcc</EM>'s</A>.
+<P>
+
+<DT> <A NAME="folder-ext"><EM>folder-extension</EM></A>
+
+<DD> <EM>PC-Alpine</EM> only. File extension used for local folder names. This
+is <CODE>.MTX</CODE> by default.
+<P>
+
+<DT> <A NAME="reopen-rule"><EM>folder-reopen-rule</EM></A>
+
+<DD> <EM>Alpine</EM> normally checks for new mail in the currently open folder
+and in the INBOX every few <A HREF="#mail-check"><EM>minutes</EM></A>.
+
+<P>
+There are some situations where automatic new-mail checking does not work.
+For example, if a mail folder is opened using the POP protocol or a newsgroup
+is being read using the NNTP protocol, then new-mail checking is disabled.
+
+<P>
+It may be possible to check for new mail in these cases by reopening the
+folder.
+<EM>Alpine</EM> does not do this for you automatically, but you may do the commands
+manually to cause this to happen.
+You reopen by going back to the folder list screen from the message
+index screen with the &quot;&lt;&quot; command,
+and then going back into the message index screen with
+the &quot;&gt;&quot; command.
+(Actually, any method you would normally use to open a folder will work the
+same as the &quot;&lt;&quot; followed by &quot;&gt;&quot; method.
+For example, the GoTo Folder command will work, or you may use L to go to the
+Folder List screen and Carriage Return to reopen the folder.)
+
+<P>
+There are some cases where <EM>Alpine</EM> knows that reopening the folder should
+be useful as a way to discover new mail.
+At the time of this writing, connections made using the POP protocol,
+news reading using the NNTP protocol, local news reading, and local
+ReadOnly folders which are in the traditional UNIX or the MMDF format all
+fall into this category.
+There are other cases where it <EM>may</EM> be a way to discover new mail, but <EM>Alpine</EM>
+has no way of knowing, so it might also just be an exercise in futility.
+All remote, ReadOnly folders other than those listed just above fall into this
+category.
+The setting of this option together with the type of folder
+controls how <EM>Alpine</EM> will react to the apparent attempt to reopen a folder.
+
+<P>
+If you don't reopen, then you will just be back in
+the message index with no change.
+You left the index and came back, but the folder remained &quot;open&quot;
+the whole time.
+However, if you do reopen the folder, the folder is closed and then reopened.
+In this case, the current state of the open folder is lost.
+The New status, Important and Answered flags,
+selected state, Zoom state, collapsed or expanded state of threads,
+current message number,
+and any other temporary state is all lost when the reopen happens.
+For POP folders (but not NNTP newsgroups) the Deleted flags are also lost.
+
+<P>
+In the possibilities listed below, the text says &quot;POP/NNTP&quot; in
+several places.
+That really implies the case where <EM>Alpine</EM> knows it is a good way to discover
+new mail, which is more than just POP and NNTP, but POP and NNTP are
+the cases of most interest.
+This option probably has more possible values than it deserves. They are:
+<P>
+
+<DL>
+<DT>Always reopen</DT>
+<DD><EM>Alpine</EM> will not ask whether you want to reopen but will just do the reopen
+whenever you type a command that implies a reopen, regardless of the
+access method.
+In other words, it is assumed you would always answer Yes if asked
+about reopening.
+</DD>
+
+<DT>Yes for POP/NNTP, Ask about other remote [Yes]</DT>
+<DD><EM>Alpine</EM> will assume a Yes answer if the access method is POP or NNTP, but
+will ask you whether to reopen other remote folders,
+with a default answer of Yes.
+</DD>
+
+<DT>Yes for POP/NNTP, Ask about other remote [No]</DT>
+<DD><EM>Alpine</EM> will assume a Yes answer if the access method is POP or NNTP, but
+will ask you whether to reopen other remote folders,
+with a default answer of No.
+</DD>
+
+<DT>Yes for POP/NNTP, No for other remote</DT>
+<DD><EM>Alpine</EM> will assume a Yes answer if the access method is POP or NNTP, and
+will assume a No answer for all other remote folders.
+</DD>
+
+<DT>Always ask [Yes]</DT>
+<DD><EM>Alpine</EM> will not differentiate based on access method.
+It will always ask for all remote folders, with a default answer of Yes.
+</DD>
+
+<DT>Always ask [No]</DT>
+<DD><EM>Alpine</EM> will not differentiate based on access method.
+It will always ask for all remote folders, with a default answer of No.
+</DD>
+
+<DT>Ask about POP/NNTP [Yes], No for other remote</DT>
+<DD><EM>Alpine</EM> will ask if the access method is POP or NNTP, with a default answer
+of Yes.
+It will never attempt to reopen other remote folders.
+</DD>
+
+<DT>Ask about POP/NNTP [No], No for other remote</DT>
+<DD>This is the default.
+<EM>Alpine</EM> will ask if the access method is POP or NNTP, with a default answer
+of No.
+It will never attempt to reopen other remote folders.
+</DD>
+
+<DT>Never reopen</DT>
+<DD><EM>Alpine</EM> will never attempt to reopen already open folders.
+</DD>
+</DL>
+
+<P>
+Remember, wherever it says POP or NNTP above it really means POP or NNTP or
+any of the other situations where it is likely that reopening is a good way
+to discover new mail.
+
+<P>
+There is an alternative that may be of useful in some situations.
+Instead of manually checking for new mail you can set up a
+<A HREF="config-notes.html#maildrop">Mail Drop</A>
+and automatically check for new mail.
+<P>
+
+<DT> <A NAME="folder-sort-rule"><EM>folder-sort-rule</EM></A>
+
+<DD> This option controls the order in which folder list entries will be
+presented in the FOLDER LIST screen. Choose one of the following:
+
+ <DL>
+ <DT> <EM>Alphabetical</EM>
+
+ <DD> sort by alphabetical name independent of type
+
+ <DT> <EM>Alpha-with-dirs-last</EM>
+
+ <DD> sort by alphabetical name grouping directory entries
+to the end of the list
+
+ <DT> <EM>Alpha-with-dirs-first</EM>
+
+ <DD> sort by alphabetical name grouping directory entries
+to the start of the list
+ </DL>
+
+The normal default is <EM>Alphabetical</EM>.
+<P>
+
+<DT> <A NAME="font-name"><EM>font-name</EM></A>
+
+<DD> Winsock version of <EM>PC-Alpine</EM> only. <P>
+
+<DT> <A NAME="font-size"><EM>font-size</EM></A>
+
+<DD> Winsock version of <EM>PC-Alpine</EM> only. <P>
+
+<DT> <A NAME="font-style"><EM>font-style</EM></A>
+
+<DD> Winsock version of <EM>PC-Alpine</EM> only. <P>
+
+<DT> <A NAME="forced-abook"><EM>forced-abook-entry</EM></A>
+
+<DD> System-wide <EM>Alpine</EM> configuration files only.
+Force these address book
+entries into all writable personal address books.
+This is a list variable. Each item in the list has the form:
+
+<BLOCKQUOTE>
+ Nickname | Fullname | Address <BR>
+</BLOCKQUOTE>
+
+with optional whitespace in all the obvious places.
+<P>
+
+<DT> <A NAME="form-letter-folder"><EM>form-letter-folder</EM></A>
+
+<DD> A Form Letter Folder is a mail folder that is intended to
+contain messages that you have composed and that are intended to be
+sent in their original form repeatedly.
+
+<P>
+Setting this variable will alter <EM>Alpine</EM>'s usual behavior when you
+execute the Compose command. Normally, <EM>Alpine</EM> offers a chance to
+continue a postponed or interrupted message should one or the other
+exist. When this variable is set to a folder name that exists, <EM>Alpine</EM>
+will also offer the chance to select a message from the folder to
+insert into the composer, much like when continuing a postponed message.
+The difference, however, is that <EM>Alpine</EM> will not automatically delete
+the selected message from the Form Letter Folder.
+<P>
+Setting this variable will also affect <EM>Alpine</EM>'s behavior when you
+Postpone a message from the composer. Normally, <EM>Alpine</EM> simply stashes
+the message away in your
+<A HREF="#postponed-folder"><EM>Postponed-Folder</EM></A>.
+Regardless of the specified folder's existence, <EM>Alpine</EM> will ask which
+folder you intend the message to be stored in. Choose the
+&quot;F&quot; option to store the message in your Form Letter Folder.
+This is the most common way to add a message to the folder.
+
+<P>
+Another method of adding messages to the folder is via the <EM>Alpine</EM>
+composer's <SAMP>Fcc:</SAMP> field. If you are sending a message that
+you expect to send in the same form again, you can enter the Form
+Letter Folder's name in this field. <EM>Alpine</EM>, as usual, will copy the
+message as it's sent. Note, when you later select this message from
+your Form Letter Folder, it will have the same recipients as the original
+message.
+
+<P>
+To delete a message from the Form Letter Folder, you can either select
+the folder from a suitable FOLDER LIST screen, or use the Delete
+command in the MESSAGE INDEX offered when selecting from the folder as
+part of the Compose command. You can delete a Form Letter Folder just
+as any other folder from a suitable FOLDER LIST screen.
+
+<P>
+You may find that the <A HREF="#role-config"><EM>Roles</EM></A>
+facility can be used
+to replace the Form Letter Folder.
+
+<P>
+
+<DT> <A NAME="glob-abook"><EM>global-address-book</EM></A>
+
+<DD> A list of shared address books. Each entry in the list is an
+optional nickname followed by a pathname or file name relative to the home
+directory.
+A SPACE character separates the nickname from the rest of the line.
+Instead of a local pathname or file name, a remote folder name can be given.
+This causes the address book to
+be a <A HREF="low-level.html#addrbook"><EM>Remote address book</EM></A>.
+Remote folder syntax is discussed in
+<A HREF="config-notes.html#remote-folders">Syntax for Remote Folders</A>.
+This list will be added to the
+<A HREF="#pers-abook"><EM>address-book</EM></A> list to
+arrive at the complete set of address books. Global address books are
+defined to be ReadOnly. <P>
+
+<DT> <A NAME="goto-default-rule"><EM>goto-default-rule</EM></A>
+
+<DD> This value affects <EM>Alpine</EM>'s behavior when using
+the <EM>Goto</EM> command.
+There are five possible values for this option:
+<P>
+
+ <DL>
+
+ <DT> <EM>folder-in-first-collection</EM>
+
+ <DD> <EM>Alpine</EM> will offer the most recently visited folder in the default
+collection found in the &quot;Collection List&quot; screen as the default.
+<P>
+
+ <DT> <EM>inbox-or-folder-in-first-collection</EM>
+
+ <DD> If the current folder is <EM>INBOX</EM>,
+<EM>Alpine</EM> will offer the most recently visited folder in the
+default collection found in the &quot;Collection List&quot; screen.
+If the current folder is other than <EM>INBOX</EM>,
+<EM>INBOX</EM> is offered as the default.
+<P>
+
+ <DT> <EM>inbox-or-folder-in-recent-collection</EM>
+
+ <DD> This is <EM>Alpine</EM>'s default behavior.
+If the current folder is <EM>INBOX</EM>,
+<EM>Alpine</EM> will offer the last open
+folder as the default.
+If the current folder is other than <EM>INBOX</EM>,
+<EM>INBOX</EM> is offered as the default.
+<P>
+
+ <DT> <EM>first-collection-with-inbox-default</EM>
+
+ <DD> Instead of offering the most recently visited folder in the default
+collection, the default collection is offered but with <EM>INBOX</EM> as
+the default folder.
+If you type in a folder name it will be in the default collection.
+If you simply accept the default, however, your <EM>INBOX</EM> will be opened.
+<P>
+
+ <DT> <EM>most-recent-folder</EM>
+
+ <DD> The last accepted value simply causes the most recently opened
+folder to be offered as the default regardless of the currently opened
+folder.
+<P>
+
+ </DL>
+
+NOTE: The default while a newsgroup is open remains the same; the last
+open newsgroup. <P>
+
+<DT> <A NAME="header-general-background-color"><EM>header-general-background-color</EM></A>
+<DT> <A NAME="header-general-foreground-color"><EM>header-general-foreground-color</EM></A>
+
+<DD> <A HREF="#header-colors"><EM>Header Colors</EM></A>.
+<P>
+
+<DT> <A NAME="image-viewer"><EM>image-viewer</EM></A>
+
+<DD> This variable names the program to call for displaying parts of a
+MIME message that are of type IMAGE. If your system supports the
+<EM>mailcap</EM> system, you don't need to set this variable. <P>
+
+<DT> <A NAME="inbox-path"><EM>inbox-path</EM></A>
+
+<DD> This specifies the name of the folder to use for the <EM>INBOX</EM>.
+By default this is unset and the system's default is used.
+The most common reason for
+setting this is to open an IMAP mailbox for the <EM>INBOX</EM>. For example,
+<EM>{imap5.u.example.edu}inbox</EM> will open the user's standard
+<EM>INBOX</EM> on the mail server, <EM>imap5</EM>. <P>
+
+<DT> <A NAME="incoming-archive-folders"><EM>incoming-archive-folders</EM></A>
+
+<DD> This is like <A HREF="#read-msg-fold"><EM>read-message-folder</EM></A>,
+only more general. This is a list
+of folder pairs, with the first separated from the second in the pair by a
+space. The first folder in a pair is the folder you want to archive, and
+the second folder is the folder that read messages from the first should
+be moved to. Depending on how you define the
+<A HREF="#auto-read-msg"><EM>auto-move-read-msgs</EM></A>
+feature, you may or may not be asked when you leave
+the first folder if you want read messages to be moved to the second
+folder. In either case, moving the messages means they will be deleted
+from the first folder. <P>
+
+If these are not path names, they will be in the default collection for
+<EM>Save</EM>s. Any valid folder specification, local or remote (via IMAP), is
+allowed. There is no default. <P>
+
+<DT> <A NAME="incoming-check-interval"><EM>incoming-check-interval</EM></A>
+
+<DD> This option has no effect unless the feature
+<A HREF="#enable-incoming-folders-checking"><EM>enable-incoming-folders-checking</EM></A>
+is set, which in turn has no effect unless
+<A HREF="#inc-fold"><EM>incoming-folders</EM></A>
+is set.
+<P>
+This option specifies, in seconds, how often <EM>Alpine</EM> will check
+for new mail and state changes in Incoming Folders when Incoming Folders
+Checking is turned on.
+The default is 3 minutes (180).
+This value applies only to folders that are local to the system that
+<EM>Alpine</EM> is running on or that are accessed using the IMAP protocol.
+The similar option
+<A HREF="#incoming-check-interval-secondary"><EM>incoming-check-interval-secondary</EM></A>
+applies to all other monitored folders.
+<P>
+
+<DT> <A NAME="incoming-check-interval-secondary"><EM>incoming-check-interval-secondary</EM></A>
+
+<DD> This option has no effect unless the feature
+<A HREF="#enable-incoming-folders-checking"><EM>enable-incoming-folders-checking</EM></A>
+is set, which in turn has no effect unless
+<A HREF="#inc-fold"><EM>incoming-folders</EM></A>
+is set.
+<P>
+This option together with the option
+<A HREF="#incoming-check-interval"><EM>incoming-check-interval</EM></A>
+specifies, in seconds, how often <EM>Alpine</EM> will check
+for new mail and state changes in Incoming Folders when Incoming Folders
+Checking is turned on.
+The default for this option is 3 minutes (180).
+For folders that are local to this system or
+that are accessed using the IMAP protocol
+the value of the option
+<A HREF="#incoming-check-interval"><EM>incoming-check-interval</EM></A>
+is used.
+For all other monitored folders, the value of this option is used.
+<P>
+The reason there are two separate options is because it is usually
+less expensive to check local and IMAP folders than it is to check
+other types, like POP or NNTP folders.
+You may want to set this secondary value to a higher number than
+the primary check interval.
+<P>
+
+<DT> <A NAME="incoming-check-list"><EM>incoming-check-list</EM></A>
+
+<DD> This option has no effect unless the feature
+<A HREF="#enable-incoming-folders-checking"><EM>enable-incoming-folders-checking</EM></A>
+is set, which in turn has no effect unless
+<A HREF="#inc-fold"><EM>incoming-folders</EM></A>
+is set.
+<P>
+When monitoring the Incoming Message Folders for Unseen messages Alpine will
+normally monitor all Incoming Folders.
+You may use this option to restrict the list of monitored folders to a
+subset of all Incoming Folders.
+<P>
+
+<DT> <A NAME="incoming-check-timeout"><EM>incoming-check-timeout</EM></A>
+
+<DD> This option has no effect unless the feature
+<A HREF="#enable-incoming-folders-checking"><EM>enable-incoming-folders-checking</EM></A>
+is set, which in turn has no effect unless
+<A HREF="#inc-fold"><EM>incoming-folders</EM></A>
+is set.
+<P>
+Sets the time in seconds that Alpine will
+attempt to open a network connection used for monitoring for Unseen
+messages in Incoming Folders. The default is 5.
+If a connection has not completed within this many seconds Alpine will
+give up and consider it a failed connection.
+<P>
+
+<DT> <A NAME="inc-fold"><EM>incoming-folders</EM></A>
+
+<DD> This is a list of one or more folders other than <EM>INBOX</EM> that
+may receive new messages. This list is slightly special in that it is
+always expanded in the folder lister. In the future, it may become more
+special. For example, it would be nice
+if <EM>Alpine</EM> would monitor the folders
+in this list for new mail. <P>
+
+<DT> <A NAME="incoming-startup-rule"><EM>incoming-startup-rule</EM></A>
+
+<DD> This rule affects <EM>Alpine</EM>'s behavior when opening
+the <EM>INBOX</EM> or
+another folder from the "INCOMING MESSAGE FOLDERS".
+This rule tells <EM>Alpine</EM>
+which message to make the current message when an incoming folder is opened.
+There are seven possible values for this option:
+<P>
+
+ <DL>
+
+ <DT> <EM>first-unseen</EM>
+
+ <DD> The current message will be the first unseen message which has not been
+marked deleted, or the last message if all of the messages have been seen.
+This is the default setting.
+<P>
+
+ <DT> <EM>first-recent</EM>
+
+ <DD> This is similar to <EM>first-unseen</EM>. Instead of first unseen
+it is the first recent message. A message is considered to be recent if it
+arrived since the last time the folder was open (by any mail client, not just
+the current one). So this option causes the
+current message to be set to the first undeleted-recent message, or the
+last message if none is both undeleted and recent.
+<P>
+
+ <DT> <EM>first-important</EM>
+
+ <DD> This will result in the current message being set to the first
+message marked Important (but not Deleted).
+If no messages are marked Important, then it will be the last message.
+<P>
+
+ <DT> <EM>first-important-or-unseen</EM>
+
+ <DD> This selects the minimum of the first unseen and the first important
+messages.
+<P>
+
+ <DT> <EM>first-important-or-recent</EM>
+
+ <DD> This selects the first of the first recent and the first important
+messages.
+<P>
+
+ <DT> <EM>first</EM>
+
+ <DD> Set the current message to the first undeleted message unless all
+are deleted. In that case set it to the last message.
+<P>
+
+ <DT> <EM>last</EM>
+
+ <DD> Set the current message to the last undeleted message unless all
+are deleted. In that case set it to the last message.
+<P>
+
+ </DL>
+<P>
+
+<DT> <A NAME="incoming-unseen-background-color"><EM>incoming-unseen-background-color</EM></A>
+<DT> <A NAME="incoming-unseen-foreground-color"><EM>incoming-unseen-foreground-color</EM></A>
+
+<DD> <A HREF="#incoming-unseen-color"><EM>Incoming Unseen Color</EM></A>.
+<P>
+
+<DT> <A NAME="index-answered-background-color"><EM>index-answered-background-color</EM></A>
+<DT> <A NAME="index-answered-foreground-color"><EM>index-answered-foreground-color</EM></A>
+<DT> <A NAME="index-arrow-background-color"><EM>index-arrow-background-color</EM></A>
+<DT> <A NAME="index-arrow-foreground-color"><EM>index-arrow-foreground-color</EM></A>
+<DT> <A NAME="index-deleted-background-color"><EM>index-deleted-background-color</EM></A>
+<DT> <A NAME="index-deleted-foreground-color"><EM>index-deleted-foreground-color</EM></A>
+<DT> <A NAME="index-from-background-color"><EM>index-from-background-color</EM></A>
+<DT> <A NAME="index-from-foreground-color"><EM>index-from-foreground-color</EM></A>
+<DT> <A NAME="index-highpriority-background-color"><EM>index-highpriority-background-color</EM></A>
+<DT> <A NAME="index-highpriority-foreground-color"><EM>index-highpriority-foreground-color</EM></A>
+<DT> <A NAME="index-important-background-color"><EM>index-important-background-color</EM></A>
+<DT> <A NAME="index-important-foreground-color"><EM>index-important-foreground-color</EM></A>
+<DT> <A NAME="index-lowpriority-background-color"><EM>index-lowpriority-background-color</EM></A>
+<DT> <A NAME="index-lowpriority-foreground-color"><EM>index-lowpriority-foreground-color</EM></A>
+<DT> <A NAME="index-new-background-color"><EM>index-new-background-color</EM></A>
+<DT> <A NAME="index-new-foreground-color"><EM>index-new-foreground-color</EM></A>
+<DT> <A NAME="index-opening-background-color"><EM>index-opening-background-color</EM></A>
+<DT> <A NAME="index-opening-foreground-color"><EM>index-opening-foreground-color</EM></A>
+<DT> <A NAME="index-recent-background-color"><EM>index-recent-background-color</EM></A>
+<DT> <A NAME="index-recent-foreground-color"><EM>index-recent-foreground-color</EM></A>
+<DT> <A NAME="index-subject-background-color"><EM>index-subject-background-color</EM></A>
+<DT> <A NAME="index-subject-foreground-color"><EM>index-subject-foreground-color</EM></A>
+<DT> <A NAME="index-to-me-background-color"><EM>index-to-me-background-color</EM></A>
+<DT> <A NAME="index-to-me-foreground-color"><EM>index-to-me-foreground-color</EM></A>
+<DT> <A NAME="index-unseen-background-color"><EM>index-unseen-background-color</EM></A>
+<DT> <A NAME="index-unseen-foreground-color"><EM>index-unseen-foreground-color</EM></A>
+
+<DD> <A HREF="#index-colors"><EM>Index Colors</EM></A>.
+<P>
+
+<DT> <A NAME="index-format"><EM>index-format</EM></A>
+
+<DD> This option is used to customize the content of lines in the
+MESSAGE INDEX screen. Each line is intended
+to convey some amount of immediately relevant information about each
+message in the current folder.
+<P>
+
+<EM>Alpine</EM> provides a pre-defined set of informational fields with
+reasonable column widths automatically computed. You can, however,
+replace this default set by listing special tokens in the order you
+want them displayed.
+<P>
+
+The list of available tokens is
+<A HREF="#index-tokens"><EM>here</EM></A>.
+<P>
+
+Spaces are used to separate listed tokens. Additionally, you can
+specify how much of the screen's width the taken's associated data
+should occupy on the index line by appending the token with a pair of
+parentheses enclosing either a number or percentage. For example,
+&quot;SUBJECT(13)&quot; means to allocate 13 characters of space to the subject
+column, and &quot;SUBJECT(20%)&quot; means to
+allocate 20% of the available space
+to the subjects column, while plain &quot;SUBJECT&quot; means the system will
+attempt to figure out a reasonable amount of space.
+<P>
+
+There is always one space between every pair of columns, so if you use fixed
+column widths (like 13) you should remember to take that into account.
+Several of the fields are virtually fixed-width, so it doesn't make
+much sense to specify the width for them. The fields STATUS,
+FULLSTATUS, IMAPSTATUS, MSGNO, the DATE fields, SIZE,
+and DESCRIPSIZE all fall into that category.
+You <EM>may</EM> specify widths for those if you wish, but
+you're probably better off letting the system pick those widths. <P>
+
+<P>
+The default is equivalent to:
+
+<P>
+<CENTER><SAMP>index-format=STATUS&nbsp;MSGNO&nbsp;SMARTDATETIME24&nbsp;FROMORTO(33%)&nbsp;SIZENARROW&nbsp;SUBJKEY(67%)</SAMP></CENTER>
+
+<P>
+This means that the four fields without percentages will be allocated
+first, and then 33% and 67% of the <EM>remaining</EM> space will go to
+the from and subject fields. If one of those two fields is specified
+as a percentage and the other is left for the system to choose, then
+the percentage is taken as an absolute percentage of the screen, not
+of the space remaining after allocating the first four columns. It
+doesn't usually make sense to do it that way. If you leave off all
+the widths, then the subject and from fields (if both are present) are
+allocated space in a 2 to 1 ratio, which is almost exactly the same as
+the default.
+
+<P>
+What you are most likely to do with this configuration option is to
+specify which fields appear at all, which order they appear in, and the
+percentage of screen that is used for the from and subject fields if you
+don't like the 2 to 1 default.
+<P>
+If you want to retain the default format that <EM>Pine</EM> 4.64 had, use
+
+<P>
+<CENTER><SAMP>Index-Format=STATUS MSGNO DATE FROMORTO(33%) SIZE SUBJKEY(67%)</SAMP></CENTER>
+<P>
+
+<EM>and</EM> set the feature
+<A HREF="#disable-index-locale-dates"><EM>Disable-Index-Locale-Dates</EM></A>.
+
+<P>
+
+<DT> <A NAME="initial-keystroke-list"><EM>initial-keystroke-list</EM></A>
+
+<DD> This is a comma-separated list of keystrokes which <EM>Alpine</EM> executes on
+startup. Items in the list are usually just characters, but there are
+some special values. <EM>SPACE,</EM> <EM>TAB,</EM> and <EM>CR</EM> mean a
+space character, tab character, and a carriage return, respectively.
+<EM>F1</EM> through <EM>F12</EM> stand for the twelve function keys.
+<EM>UP, DOWN, LEFT, </EM>and<EM> RIGHT </EM>stand for the arrow keys.
+Control characters are represented with <EM>^&lt;char&gt;</EM>. A
+restriction is that you can't mix function keys and character keys in this
+list even though you can, in some cases, mix them when running <EM>Alpine</EM>. A
+user can always use only <EM>character</EM> keys in the startup list even
+if he or she is using <EM>function</EM> keys normally, or vice versa. If
+an element in this list is a string surrounded by double quotes (")
+then it will be expanded into the individual characters in the string,
+excluding the double quotes. <P>
+
+<DT> <A NAME="kblock-count"><EM>kblock-passwd-count</EM></A>
+
+<DD> System-wide <EM>Alpine</EM> configuration files only. Number of times a user
+will have to enter a password when they run the keyboard lock command in
+the main menu. <P>
+
+<DT> <A NAME="keyb-char-set"><EM>keyboard-character-set</EM></A>
+
+<DD> See the discussion in
+<A HREF="low-level.html#char-set">International Character Sets</EM></A> for
+details.<P>
+
+<DT> <A NAME="keylabel-background-color"><EM>keylabel-background-color</EM></A>
+<DT> <A NAME="keylabel-foreground-color"><EM>keylabel-foreground-color</EM></A>
+
+<DD> <A HREF="#keylabel-color"><EM>KeyLabel Color</EM></A>.
+<P>
+
+<DT> <A NAME="keyname-background-color"><EM>keyname-background-color</EM></A>
+<DT> <A NAME="keyname-foreground-color"><EM>keyname-foreground-color</EM></A>
+
+<DD> <A HREF="#keyname-color"><EM>KeyName Color</EM></A>.
+<P>
+
+<DT> <A NAME="keywords"><EM>keywords</EM></A>
+
+<DD> You may define your own set of keywords and optionally set them on a
+message by message basis.
+These are similar to the &quot;Important&quot; flag which the user
+may set using the Flag command.
+The difference is that the Important flag is always present for each folder.
+User-defined keywords are chosen by the user.
+You may set up the list of possible keywords here, or you may add keywords
+from the Flag Details screen that you
+can get to after typing the
+Flag (*)
+command.
+After the keywords have been defined,
+then you use the Flag command
+to set or clear the keywords in each message.
+The behavior of the flag command may be modified by using the
+<A HREF="#enable-flag-screen-implicitly">Enable-Flag-Screen-Implicitly</A> option or the
+<A HREF="#enable-flag-screen-keyword-shortcut"><EM>Enable-Flag-Screen-Keyword-Shortcut</EM></A> option.
+
+<P>
+Keywords may be used when Selecting messages (Select Keyword).
+Keywords may also be used in the Patterns of Rules (Filters, Indexcolors, etc).
+Filter rules may be used to set keywords automatically.
+Keywords may be displayed as part of the Subject of a message by using
+the SUBJKEY or SUBJKEYINIT tokens in the
+<A HREF="#index-format">Index-Format</A> option.
+The <A HREF="#keyword-surrounding-chars"><EM>Keyword-Surrounding-Chars</EM></A>
+option may be used to modify the display of keywords using
+SUBJKEY and SUBJKEYINIT slightly.
+Keywords may also be displayed in a column of their own in the MESSAGE INDEX
+screen by using the KEY or KEYINIT tokens.
+It is also possible to color keywords in the index using the
+Setup/Kolor screen
+(<A HREF="#keyword-colors">Keyword Colors</A>).
+Keywords are not supported by all mail servers.
+<P>
+You may give keywords nicknames if you wish.
+If the keyword definition you type in contains a SPACE character, then the
+actual value of the keyword is everything after the last SPACE and the
+nickname for that keyword is everything before the last SPACE.
+For example, suppose you are trying to interoperate with another email program
+which uses a particular keyword with an unpleasant name.
+Maybe it uses a keyword called
+<P>
+<CENTER><SAMP>VendorName.SoftwareName.08</SAMP></CENTER>
+<P>
+but for you that keyword means that the message is work-related.
+You could define a keyword to have the value
+<P>
+<CENTER><SAMP>Work VendorName.SoftwareName.08</SAMP></CENTER>
+<P>
+and then you would use the name &quot;Work&quot; when dealing with
+that keyword in <EM>Alpine</EM>.
+If you defined it as
+<P>
+<CENTER><SAMP>My Work VendorName.SoftwareName.08</SAMP></CENTER>
+<P>
+the nickname would be everything before the last SPACE, that is the nickname
+would be &quot;My Work&quot;.
+<P>
+Some commonly used keywords begin with dollar signs.
+This presents a slight complication, because the dollar sign is normally used
+to signify
+<A HREF="config-notes.html#env-variables">environment variable expansion</A>
+in the <EM>Alpine</EM> configuration.
+In order to specify a keyword which begins with a dollar sign you must
+precede the dollar sign with a second dollar sign to escape its special
+meaning.
+For example, if you want to include the keyword
+<P>
+<CENTER><SAMP>$Label1</SAMP></CENTER>
+<P>
+as one of your possible keywords, you must enter the text
+<P>
+<CENTER><SAMP>$$Label1</SAMP></CENTER>
+<P>
+instead.
+<P>
+
+<DT> <A NAME="keyword-surrounding-chars"><EM>keyword-surrounding-chars</EM></A>
+
+<DD> This option controls a minor aspect of <EM>Alpine</EM>'s MESSAGE INDEX and MESSAGE
+TEXT screens.
+If you have modified the
+<A HREF="#index-format"><EM>Index-Format</EM></A> option
+so that either the &quot;SUBJKEY&quot; or &quot;SUBJKEYINIT&quot; tokens
+are used to display keywords or their initials along with the Subject; then
+this option may be used to modify the resulting display slightly.
+By default, the keywords or initials displayed for these tokens will be
+surrounded with curly braces ({ and }) and a trailing space.
+For example, if keywords &quot;Work&quot; and &quot;Now&quot; are set for
+a message, the &quot;SUBJKEY&quot; token will normally look like
+<P>
+<CENTER><SAMP>{Work Now} actual subject</SAMP></CENTER>
+<P>
+and the SUBJKEYINIT token would look like
+<P>
+<CENTER><SAMP>{WN} actual subject</SAMP></CENTER>
+<P>
+The default character before the keywords is the left brace ({) and the
+default after the keywords is the right brace followed by a space (} ).
+<P>
+This option allows you to change that.
+You should set it to two values separated by a space.
+The values may be quoted if they include space characters.
+So, for example, the default value could be specified explicitly by setting this
+option to
+<P>
+<CENTER><SAMP>Keyword-Surrounding-Chars="{" "} "</SAMP></CENTER>
+<P>
+The first part wouldn't need to be quoted (but it doesn't hurt).
+The second part does need the quotes because it includes a space character.
+If you wanted to change the braces to brackets you could use
+<P>
+<CENTER><SAMP>Keyword-Surrounding-Chars="[" "] "</SAMP></CENTER>
+<P>
+Inside the quotes you can use backslash quote to mean quote, so
+<P>
+<CENTER><SAMP>Keyword-Surrounding-Chars="&#92;"" "&#92;" "</SAMP></CENTER>
+<P>
+would produce
+<P>
+<CENTER><SAMP>"Work Now" actual subject</SAMP></CENTER>
+<P>
+It is also possible to color keywords in the index using the
+Setup/Kolor screen
+(<A HREF="#keyword-colors">Keyword Colors</A>).
+<P>
+It is not possible to change the fact that a space character is used to
+separate the keywords if more than one keyword is set for a message.
+It is also not possible to change the fact that there are no separators
+between the keyword initials if more than one keyword is set.
+<P>
+This option is displayed as &quot;Keyword Surrounding Characters&quot;.
+<P>
+
+<DT> <A NAME="last-time"><EM>last-time-prune-questioned</EM></A>
+
+<DD> Personal configuration file only. This variable records the month
+the user was last asked if his or her <EM>sent-mail</EM> folders should
+be pruned.
+The format is <EM>yy.mm</EM>.
+This is automatically updated by <EM>Alpine</EM> when
+the the pruning is done or declined.
+If a user wanted to make <EM>Alpine</EM> stop
+asking this question he or she could set this time to something
+far in the future.
+This may not be set in the system-wide configuration files.
+Note: The <EM>yy</EM> year is actually the number of years since 1900, so it
+will be equal to 101 in the year 2001.
+<P>
+
+<DT> <A NAME="last-version-used"><EM>last-version-used</EM></A>
+
+<DD> Personal configuration file only.
+This is set automatically by <EM>Alpine</EM>.
+It is used to keep track of the last version of <EM>Alpine</EM> that
+was run by the user.
+Whenever the version number increases, a new version message is printed out.
+This may not be set in the system-wide configuration files.
+<P>
+
+<DT> <A NAME="ldap-servers"><EM>ldap-servers</EM></A>
+
+<DD> This is only available if <EM>Alpine</EM> was linked with an LDAP library
+when it was compiled. This variable is normally managed by <EM>Alpine</EM> though
+it can be set in the system-wide configuration files as well as the personal
+configuration. It is a list variable. Each item in the
+list contains quite a bit of extra information besides just the server name.
+To put this into a system-wide config file the easiest thing to do is to
+configure a personal <EM>Alpine</EM> for the LDAP server then copy the
+configuration line
+into the system-wide config file. Each item in the list looks like:
+
+<BLOCKQUOTE>
+ <CODE>
+ server_name[:port] <SPACE> "quoted stuff" </CODE><BR>
+</BLOCKQUOTE>
+
+The <CODE>server_name</CODE> is just a hostname and it is followed by
+an optional colon and port number. The default <CODE>port</CODE> is 389.
+Following the server name is a single SPACE character followed by
+a bunch of characters inside double quotes. The part inside the quotes is
+a set of <EM>tag</EM> = <EM>value</EM> pairs.
+Each tag is preceded by a slash (/) and followed
+by an equal sign. The value for that tag is the text up to the next slash.
+An example of some <CODE>quoted stuff</CODE> is:
+
+<BLOCKQUOTE>
+ <CODE>
+ "/base=o=University of Washington, c=US/impl=0/.../nick=My Server"
+ </CODE><BR>
+</BLOCKQUOTE>
+
+This would set the search base for this server to
+<CODE>o=University of Washington, c=US</CODE>, set the implicit bit to zero,
+and set the nickname for the server to <CODE>My Server</CODE>.
+All of the tags correspond directly to items in the Setup/Directory screen
+so experiment with that if you want to see what the possible tags and values
+are.
+<P>
+
+<DT> <A NAME="literal-signature"><EM>literal-signature</EM></A>
+
+<DD> With this option your actual signature, as opposed to
+the name of a file containing your signature,
+is stored in the <EM>Alpine</EM> configuration file.
+If this is defined it takes precedence over the <EM>signature-file</EM> option.
+<P>
+
+This is simply a different way to store the signature data.
+The signature is stored inside your <EM>Alpine</EM> configuration file
+instead of in a separate signature file.
+Tokens contained in the signature work the same way they do with the regular
+<A HREF="#sig-file">signature-file</A>.
+<P>
+
+The Setup/Signature command in <EM>Alpine</EM>'s Main Menu will edit
+the <EM>literal-signature</EM> by default. However, if no
+<EM>literal-signature</EM> is defined and the file named in the
+<EM>signature-file</EM> option exists, then the latter will be used
+instead. Compose (Reply, Forward, ...) will default to using the
+<EM>literal-signature</EM> if defined, otherwise it will use the contents
+of the file named in <EM>signature-file</EM>.
+<P>
+
+The <EM>Alpine</EM> composer is used to edit the literal-signature.
+The result of that edit is first converted to a C-style string before it
+is stored in the configuration file.
+In particular, the two character sequence &#92;n (backslash followed by
+the character &quot;n&quot;) will be used to signify a
+line-break in the signature.
+You don't have to enter the &#92;n, but it will be visible in the
+SETUP CONFIGURATION window after you are done editing the signature.
+<P>
+
+<DT> <A NAME="mail-check"><EM>mail-check-interval</EM></A>
+
+<DD> This option specifies, in seconds,
+how often <EM>Alpine</EM> will check for new mail.
+If set to zero, new-mail checking is disabled.
+(You can always manually force a new-mail check by typing ^L (Ctrl-L), which is also
+the command to refresh the screen, or by typing the Next command when the
+current message is the last message of the folder.)
+There is a minimum value for this option, normally 15 seconds.
+The default value is normally 150 seconds.
+The higher you set this option, the easier it is on the server.
+<P>
+There are some situations where automatic new-mail checking does not work.
+See the discussion about new-mail checking in <A HREF="#reopen-rule"><EM>folder-reopen-rule</EM></A>.
+<P>
+The new-mail checking will not happen exactly at the frequency that you specify.
+For example, <EM>Alpine</EM> may elect to defer a non-INBOX mail check if you
+are busy typing.
+Or, it may check more frequently than you have specified if that is
+thought to be necessary to keep the server from closing the connection
+to the folder due to inactivity.
+If <EM>Alpine</EM> checks for new mail as a side effect of another command, it will reset
+the timer, so that new-mail checking may seem to happen irregularly instead of
+every X seconds like clockwork.
+<P>
+If you are anxious to know about new mail as soon as possible, set the check
+interval low, and you'll know about the new mail by approximately
+that amount of time after it arrives.
+If you aren't so worried about knowing right away, set this option to a
+higher value.
+That will save the server some processing time and may save you some of
+the time you spend waiting for new-mail checks to happen if you are
+dealing with a slow server or slow network connection.
+<P>
+If you suspect that new-mail checking is causing slow downs for you,
+you may want to look into the options
+<A HREF="#quell-mailchecks-composing-except"><EM>Quell-Mailchecks-Composing-Except-Inbox</EM></A>,
+<A HREF="#quell-mailchecks-composing-inbox"><EM>Quell-Mailchecks-Composing-Inbox</EM></A> and
+<A HREF="#mail-check-noncurr"><EM>Mail-Check-Interval-Noncurrent</EM></A>,
+which refine when mail checking is done.
+<P>
+If the mailbox being check uses a <A HREF="config-notes.html#maildrop">Mail Drop</A> then
+there is a minimum time
+(<A HREF="#maildrop-check-minimum"><EM>maildrop-check-minimum</EM></A>)
+between new-mail checks.
+Because of this minimum you may notice that new mail does not
+appear promptly when you expect it.
+The reason for this is to protect the server from over-zealous opening and
+closing of the Mail Drop folder, since that is a costly operation.
+<P>
+A side effect of disabling mail checking is that there will be situations
+in which the user's IMAP connection will be broken due to inactivity timers
+on the server. Another side effect is that the
+<A HREF="#user-input"><EM>user-input-timeout</EM></A>
+option won't work.
+<P>
+
+<DT> <A NAME="mail-check-noncurr"><EM>mail-check-interval-noncurrent</EM></A>
+
+<DD> This option is closely related to the
+<A HREF="#mail-check"><EM>Mail-Check-Interval</EM></A>
+option, as well as the
+<A HREF="#quell-mailchecks-composing-except"><EM>Quell-Mailchecks-Composing-Except-Inbox</EM></A> and
+<A HREF="#quell-mailchecks-composing-inbox"><EM>Quell-Mailchecks-Composing-Inbox</EM></A> options.
+If the &quot;Mail-Check-Interval&quot; option is set to zero, then automatic
+new-mail checking is disabled and this option will have no effect.
+<P>
+Normally this option is set to zero, which means that the value used will be
+the same as the value for the &quot;Mail-Check-Interval&quot;.
+If you set this option to a value different from zero
+(usually larger than the value for &quot;Mail-Check-Interval&quot;)
+then that is the check interval that will be used
+for folders which are not the currently open folder or the INBOX.
+You may not even have any folders that are noncurrent and not the INBOX.
+If you do, it is likely that they are due to
+<A HREF="#stay-open-folders">Stay-Open-Folders</A>
+you have configured.
+This option also affects the rate of mail checking done on cached
+connections to folders you previously had open but are no longer actively
+using.
+You aren't expected to understand that last sentence, but if you are interested
+take a look at
+<A HREF="#max-remote-connections">Max-Remote-Connections</A>,
+and the related options.
+<P>
+
+<DT> <A NAME="mail-directory"><EM>mail-directory</EM></A>
+
+<DD> This variable was more important in previous versions of <EM>Alpine</EM>. Now
+it is used only as the default for storing personal folders (and only if
+there are no <A HREF="#fold-coll"><EM>folder-collections</EM></A> defined).
+The default value is
+<EM>~/mail</EM> on UNIX and <EM>${HOME}\MAIL</EM> on a PC. <P>
+
+<DT> <A NAME="mailcap-search-path"><EM>mailcap-search-path</EM></A>
+
+<DD> This variable is used to replace <EM>Alpine</EM>'s default
+mailcap file search path.
+It takes one or more file names (full paths must be specified) in
+which to look for mail capability data. <P>
+
+<DT> <A NAME="maildrop-check-minimum"><EM>maildrop-check-minimum</EM></A>
+
+<DD> New-mail checking for a
+<A HREF="config-notes.html#maildrop">Mail Drop</A>
+is a little different from new
+mail checking for a regular folder.
+One of the differences is that the connection to the Mail Drop is not
+kept open and so the cost of checking
+(delay for you and additional load for the server) may be significant.
+Because of this additional cost we set a minimum time that
+must pass between checks.
+This minimum only applies to the automatic checking done by <EM>Alpine</EM>.
+If you force a check by typing ^L (Ctrl-L) or by typing the Next command when you are
+at the end of a folder index, then the check is done right away.
+<P>
+This option specifies, in seconds, the <EM>minimum</EM> time between Mail Drop
+new-mail checks.
+You may want to set this minimum high in order to avoid experiencing some
+of the delays associated with the checks.
+Note that the time between checks is still controlled by the regular
+<A HREF="#mail-check"><EM>Mail-Check-Interval</EM></A> option.
+When <EM>Alpine</EM> is about to do an automatic check for new mail (because
+the Mail-Check-Interval has expired) then if the time since the last
+new-mail check
+of any open Mail Drops has been greater than the MailDrop-Check-Minimum,
+the Mail Drop is checked for new mail as well.
+Therefore, it is only useful to set this option to a value that is higher
+than the Mail-Check-Interval.
+<P>
+If this option is set to zero, automatic Mail Drop new-mail
+checking is disabled.
+There is a minimum value, normally 60 seconds.
+The default value is normally 60 seconds as well.
+This applies to the INBOX and to the currently open folder if that is
+different from the INBOX.
+<P>
+
+<DT> <A NAME="max-remote-connections"><EM>max-remote-connections</EM></A>
+
+<DD> This option affects low-level behavior of <EM>Alpine</EM>.
+The default value for this option is <EM>2</EM>.
+If your INBOX is accessed using the IMAP protocol
+from an IMAP server, that connection is kept open throughout the
+duration of your <EM>Alpine</EM> session, independent of the value of this option.
+The same is true of any
+<A HREF="#stay-open-folders">Stay-Open-Folders</A>
+you have defined.
+This option controls <EM>Alpine</EM>'s behavior when connecting to remote IMAP folders
+other than your INBOX or your Stay-Open-Folders.
+It specifies the maximum number of remote IMAP connections (other than
+those mentioned above) that <EM>Alpine</EM> will use for accessing the rest of your
+folders.
+If you set this option to zero, you will turn off most remote connection
+re-use.
+It's difficult to understand exactly what this option does, and it is usually
+fine to leave it set to its default value.
+It is probably more likely that you will be interested in setting the
+<A HREF="#stay-open-folders">Stay-Open-Folders</A> option
+instead of changing the value of this option.
+A slightly longer explanation of what is going on with this option
+is given in the next paragraphs.
+
+<P>
+There are some time costs involved in opening and closing remote IMAP
+folders, the main costs being the time you have to wait for the connection
+to the server and the time for the folder to open.
+Opening a folder may involve not only the time the server takes to do its
+processing but time that <EM>Alpine</EM> uses to do filtering.
+These times can vary widely.
+They depend on how loaded the server is, how large
+the folder being opened is, and how you set up filtering, among other things.
+Once <EM>Alpine</EM> has opened a connection to a particular folder, it will attempt
+to keep that connection open in case you use it again.
+In order to do this,
+<EM>Alpine</EM> will attempt to use the Max-Remote-Connections (the value of
+this option) IMAP connections you have alloted for this purpose.
+<P>
+For example, suppose the value of this option is set to &quot;2&quot;.
+If your INBOX is accessed on a remote server using the IMAP protocol, that
+doesn't count as one of the remote connections but it is always kept open.
+If you then open another IMAP folder, that would be your first
+remote connection counted as one of the Max-Remote-Connections connections.
+If you open a third folder the second will be left open, in case you
+return to it.
+You won't be able to tell it has been left open.
+It will appear to be closed when you leave the folder but the connection
+will remain in the background.
+Now suppose you go back to the second folder (the first folder after the
+INBOX).
+A connection to that folder is still open so you won't have to wait
+for the startup time to open it.
+Meanwhile, the connection to the third folder will be left behind.
+Now, if you open a fourth folder, you will bump into the
+Max-Remote-Connections limit, because this will be the third folder other
+than INBOX and you have the option set to &quot;2&quot;.
+The connection that is being used for
+the third folder will be re-used for this new fourth folder.
+If you go back to the third folder after this, it is no longer already
+connected when you get there.
+You'll still save some time since <EM>Alpine</EM> will re-use the connection to the
+fourth folder and you have already logged in on that connection,
+but the folder will have to be re-opened from scratch.
+<P>
+If a folder is large and the startup cost is dominated by the time it takes
+to open that folder or to run filters on it, then it will pay to make the
+value of this option large enough to keep it open.
+On the other hand, if you only revisit a handful of folders or if
+the folders are small, then it might
+make more sense to keep this number small so that the reconnect
+time (the time to start up a new connection and authenticate)
+is eliminated instead.
+<P>
+You may also need to consider the impact on the server.
+On the surface, a larger number here may cause a larger impact on the
+server, since you will have more connections open to the server.
+On the other hand, not only will <EM>you</EM> be avoiding the startup costs
+associated with reopening a folder, but the <EM>server</EM> will be
+avoiding those costs as well.
+<P>
+When twenty five minutes pass without any active use of an IMAP connection
+being saved for possible re-use, that connection will be shut down,
+<P>
+This option is displayed as &quot;Maximum Remote Connections&quot;.
+<P>
+
+<DT> <A NAME="meta-message-background-color"><EM>meta-message-background-color</EM></A>
+<DT> <A NAME="meta-message-foreground-color"><EM>meta-message-foreground-color</EM></A>
+
+<DD> <A HREF="#meta-message-color"><EM>Meta-message Color</EM></A>.
+<P>
+
+<DT> <A NAME="mimetype-search-path"><EM>mimetype-search-path</EM></A>
+
+<DD> This variable is used to replace <EM>Alpine</EM>'s default mime.types file
+search path. It takes one or more file names (full paths must be
+specified) in which to look for file-name-extension to MIME type mapping
+data. See the Config Notes for details on <EM>Alpine</EM>'s usage of the <A
+HREF="config-notes.html#mime.types">MIME.Types File</A>. <P>
+
+<DT> <A NAME="new-version-threshold"><EM>new-version-threshold</EM></A>
+
+<DD> When a new version of <EM>Alpine</EM> is run for the first time it offers a
+special explanatory screen to the user upon startup. This option
+helps control when and if that special screen appears for users that
+have previously run <EM>Alpine</EM>. It takes as its value a <EM>Alpine</EM> version
+number. <EM>Alpine</EM> versions less than the specified value will supress this
+special screen while versions equal to or greater than that specified
+will behave normally. <P>
+
+<DT> <A NAME="newmail-fifo-path"><EM>newmail-fifo-path</EM></A>
+
+<DD> This option is only available in UNIX <EM>Alpine</EM>.
+However, there is a very similar feature built in to <EM>PC-Alpine</EM>.
+In <EM>PC-Alpine</EM>'s Config menu at the top of the screen
+is an option called &quot;New Mail Window&quot;.
+<P>
+You may have <EM>Alpine</EM> create a FIFO special file (also called a named pipe, see mkfifo(3) and fifo(4)) where
+it will send a one-line message each time a new message is received in
+the current folder, the INBOX, or any open
+<A HREF="#stay-open-folders">Stay-Open-Folders</A>.
+To protect against two different <EM>Alpine</EM>s both writing to the same FIFO, <EM>Alpine</EM>
+will only create the FIFO and write to it if it doesn't already exist.
+<P>
+A possible way to use this option would be to have a separate window
+on your screen running the command
+<P>
+<CENTER><SAMP>cat filename</SAMP></CENTER>
+<P>
+where &quot;filename&quot; is the name of the file given for this option.
+Because the file won't exist until after you start <EM>Alpine</EM>, you must <EM>first</EM>
+start <EM>Alpine</EM> and <EM>then</EM> run the &quot;cat&quot; command.
+You may be tempted to use &quot;tail -f filename&quot; to view the new
+mail log.
+However, the common implementations of the tail command will not do what you
+are hoping.
+<P>
+The width of the messages produced for the FIFO may be altered with the
+<A HREF="#newmail-window-width">NewMail-Window-Width</A> option.
+<P>
+On some systems, fifos may only be created in a local filesystem.
+In other words, they may not be in NFS filesystems.
+This requirement is not universal.
+If the system you are using supports it, it should work.
+(It is often the case that your home directory is in an NFS filesystem.
+If that is the case, you might try using a file in the &quot;/tmp&quot;
+filesystem, which is usually a local filesytem.)
+Even when it is possible to use an NFS-mounted filesystem as a place to name
+the fifo (for example, your home directory), it will still be the case that
+the reader (probably the &quot;cat&quot; command) and the
+writer (<EM>Alpine</EM>) of the fifo must be running on the same system. <P>
+
+<DT> <A NAME="newmail-window-width"><EM>newmail-window-width</EM></A>
+
+<DD> UNIX <EM>Alpine</EM> only.
+<P>
+This option is only useful if you have turned on the
+<A HREF="#newmail-fifo-path">NewMail-FIFO-Path</A> option.
+That option causes new mail messages to be sent to a fifo file.
+Those messages will be 80 characters wide by default.
+You can change the width of the messages by changing this option.
+For example, if you are reading those messages in another window you might
+want to set this width to the width of that other window. <P>
+For UNIX <EM>Alpine</EM>, this option is only useful if you have turned on the
+<A HREF="#newmail-fifo-path">NewMail-FIFO-Path</A> option.
+That option causes new mail messages to be sent to a fifo file.
+Those messages will be 80 characters wide by default.
+You can change the width of those messages by changing this option.
+For example, if you are reading those messages in another window you might
+want to set this width to the width of that other window.
+<P>
+If you are using <EM>PC-Alpine</EM>, it has an option in the Config menu to turn
+on the &quot;New Mail Window&quot;.
+The present option also controls the width of that window.
+
+<DT> <A NAME="news-active"><EM>news-active-file-path</EM></A>
+
+<DD> This option tells <EM>Alpine</EM> where to look for the "active file" for
+newsgroups when accessing news locally, rather than via NNTP. The default
+path is usually <CODE>/usr/lib/news/active</CODE>. <P>
+
+<DT> <A NAME="news-coll"><EM>news-collections</EM></A>
+
+<DD> This is a list of collections where news folders are located. See
+the section describing <A HREF="config-notes.html#collections">collections</A>
+for more information. <P>
+
+<DT> <A NAME="news-spool"><EM>news-spool-directory</EM></A>
+
+<DD> This option tells <EM>Alpine</EM> where to look for the "news spool" for
+newsgroups when accessing news locally, rather than via NNTP. The default
+path is usually <CODE>/usr/spool/news</CODE>. <P>
+
+<DT> <A NAME="newsrc-path"><EM>newsrc-path</EM></A>
+
+<DD> This option overrides the default name <EM>Alpine</EM> uses for your "newsrc"
+news status and subscription file. If set, <EM>Alpine</EM> will take this value as
+the full pathname for the desired newsrc file. <P>
+
+<DT> <A NAME="nntp-range"><EM>nntp-range</EM></A>
+
+<DD> This option applies only to newsgroups accessed using the NNTP protocol.
+It does not, for example,
+apply to newsgroups accessed using an IMAP-to-NNTP proxy.
+<P>
+When you open a connection to a News server using the NNTP protocol, you
+normally have access to all of the articles in each newsgroup.
+If a server keeps a large backlog of messages it may speed performance
+some to restrict attention to only the newer messages in a group.
+This option allows you to set how many article numbers should be checked
+when opening a newsgroup.
+You can think of &quot;nntp-range&quot; as specifying the maximum number
+of messages you ever want to see.
+For example, if you only ever wanted to look at the last 500 messages in each
+newsgroup you could set this option to 500.
+In actuality, it isn't quite that.
+Instead, for performance reasons, it specifies the range of article
+numbers to be checked, beginning
+with the highest numbered article and going backwards from there.
+If there are messages that have been canceled or deleted
+their article numbers are still counted as part of the range.
+<P>
+So, more precisely, setting the &quot;nntp-range&quot; will cause article
+numbers
+<P><CENTER>last_article_number - nntp-range + 1 through last_article_number</CENTER>
+<P>
+to be considered when reading a newsgroup.
+The number of messages that show up in your index will be less than or equal
+to the value of &quot;nntp-range&quot;.
+<P>
+The purpose of this option is simply to speed up access when reading news.
+The speedup comes because <EM>Alpine</EM> can ignore all but the last nntp-range article
+numbers, and can avoid downloading any information about the ignored articles.
+There is a cost you pay for this speedup.
+That cost is that there is no way for you to see those ignored articles.
+The articles that come before the range you specify are invisible to you and
+to <EM>Alpine</EM>, as if they did not exist at all.
+There is no way to see those messages using, for example, an unexclude command
+or something similar.
+The only way to see those articles is to set this option high enough (or
+set it to zero) and then to reopen the newsgroup.
+
+<P>
+If this option is set to 0 (which is also the default),
+then the range is unlimited.
+This option applies globally to all NNTP servers and to all newsgroups
+on those servers.
+There is no way to set different values for different newsgroups or servers.
+<P>
+
+<DT> <A NAME="nntp-server"><EM>nntp-server</EM></A>
+
+<DD> One or more NNTP servers (host name or IP address) which <EM>Alpine</EM> will
+use for reading and posting news.
+If you read and post news to and from a single
+NNTP server, you can get away with only setting the <EM>nntp-server</EM>
+variable and leaving the <EM>news-collections</EM> variable unset.
+<P>
+When you define an NNTP server, <EM>Alpine</EM> implicitly defines a
+news collection for you, assuming that server as the news server
+and assuming that you will use the NNTP protocol and a local newsrc
+configuration file for reading news.
+See also <A HREF="#configuring-news">Configuring News</A>.
+<P>
+Your NNTP server may offer NNTP &quot;AUTHINFO SASL&quot;
+or &quot;AUTHINFO USER&quot; authentication.
+It may even require it.
+If your NNTP server does offer such authentication you may specify a user name
+parameter to cause <EM>Alpine</EM> to attempt to authenticate.
+The same is true for the server name in a folder collection which uses NNTP.
+This parameter requires an associated value,
+the username identifier with which to establish the server connection.
+An example might be:
+
+<P>
+<CENTER><SAMP>nntpserver.example.com/user=katie</SAMP></CENTER>
+<P>
+
+If authentication is offered by the server, this will cause <EM>Alpine</EM> to
+attempt to use it.
+If authentication is not offered by the server, this will cause <EM>Alpine</EM>
+to fail with an error similar to:
+
+<P>
+<CENTER><SAMP>Error: NNTP authentication not available</SAMP></CENTER>
+<P>
+For more details about the server name possibilities see
+<A HREF="config-notes.html#server-name-syntax">Server Name Syntax</A>.
+<P>
+
+<DT> <A NAME="norm-back"><EM>normal-background-color</EM></A>
+<DT> <A NAME="normal-foreground-color"><EM>normal-foreground-color</EM></A>
+
+<DD> <A HREF="#normal-color"><EM>Normal Color</EM></A>.
+<P>
+
+<DT> <A NAME="opening-text-separator-chars"><EM>opening-text-separator-chars</EM></A>
+<DD> This option controls a minor aspect of <EM>Alpine</EM>'s MESSAGE INDEX screen.
+With some setups the text of the subject is followed
+by the opening text of the message if there is any room available in the index line.
+If you have configured your
+<A HREF="#index-format"><EM>Index-Format</EM></A> option
+to include one of the Subject tokens which causes this behavior
+(SUBJECTTEXT, SUBJKEYTEXT, or SUBJKEYINITTEXT), then this option may be used
+to modify what is displayed slightly.
+By default, the Subject is separated from the opening text of the message by
+the three characters space dash space;
+<P>
+<CENTER><SAMP>&quot;&nbsp;-&nbsp;&quot;</SAMP></CENTER>
+<P>
+Use this option to set it to something different.
+The value must be quoted if it includes any space characters.
+For example, the default value could be specified explicitly by setting this
+option to
+<P>
+<CENTER><SAMP>Opening-Text-Separator-Chars="&nbsp;-&nbsp;"</SAMP></CENTER>
+<P>
+This option is displayed as &quot;Opening Text Separator Characters&quot;.
+<P>
+
+<DT> <A NAME="operating-dir"><EM>operating-dir</EM></A>
+
+<DD> System-wide <EM>Alpine</EM> configuration files only.
+This names the root of the
+tree to which the user is restricted when reading and writing folders and
+files. It is usually used in the <EM>fixed</EM> configuration file. <P>
+
+<DT> <A NAME="patterns-filters2"><EM>patterns-filters2</EM></A>
+
+<DD> Matching patterns and their corresponding actions are stored in
+this variable.
+These patterns are used with
+<A HREF="#filter-config"><EM>Filtering</EM></A>.
+This variable is normally maintained through the Setup/Rules/Filters
+configuration screen.
+It is a list variable.
+Each member of the list is a single pattern/action pair, or it can be
+a file which contains zero or more lines of pattern/action pairs.
+The only way to create a filters file is to use the InsertFile command in
+the Setup/Rules/Filters screen with a filename which doesn't yet exist.
+Then use the Shuffle command to move existing filter patterns into the file.
+This isn't very convenient but it isn't thought that many users will
+need this functionality.
+The purpose of filter files is for sharing filters.
+<P>
+This option is displayed as &quot;Patterns Filters&quot;.
+<P>
+
+<DT> <A NAME="patterns-indexcolors"><EM>patterns-indexcolors</EM></A>
+
+<DD> Matching patterns and their corresponding actions are stored in
+this variable.
+These patterns are used for
+<A HREF="#index-color-config"><EM>Index Line Colors</EM></A>.
+This variable is normally maintained through the Setup/Rules/Indexcolor
+configuration screen.
+It is a list variable.
+Each member of the list is a single pattern/action pair, or it can be
+a file which contains zero or more lines of pattern/action pairs.
+The only way to create a indexcolor file is to use the InsertFile command in
+the Setup/Rules/Indexcolor screen with a filename which doesn't yet exist.
+Then use the Shuffle command to move existing patterns into the file.
+This isn't very convenient but it isn't thought that many users will
+need this functionality.
+The purpose of indexcolor files is for sharing indexcolors.
+<P>
+
+<DT> <A NAME="patterns-other"><EM>patterns-other</EM></A>
+
+<DD> Matching patterns and their corresponding actions are stored in
+this variable.
+These patterns are used with
+<A HREF="#other-config"><EM>Miscellaneous Rules</EM></A> configuration.
+This variable is normally maintained through the Setup/Rules/Other
+configuration screen.
+It is a list variable.
+Each member of the list is a single pattern/action pair, or it can be
+a file which contains zero or more lines of pattern/action pairs.
+The only way to create a rules file is to use the InsertFile command in
+the Setup/Rules/Other screen with a filename which doesn't yet exist. Then use
+the Shuffle command to move existing rules into the file.
+This isn't very convenient but it isn't thought that many users will
+need this functionality.
+<P>
+
+<DT> <A NAME="patterns-roles"><EM>patterns-roles</EM></A>
+
+<DD> Matching patterns and their corresponding actions are stored in
+this variable.
+These patterns are used with
+<A HREF="#role-config"><EM>Roles</EM></A>.
+This variable is normally maintained through the Setup/Rules/Roles
+configuration screen.
+It is a list variable.
+Each member of the list is a single pattern/action pair, or it can be
+a file which contains zero or more lines of pattern/action pairs.
+The only way to create a roles file is to use the InsertFile command in
+the Setup/Rules/Roles screen with a filename which doesn't yet exist. Then use
+the Shuffle command to move existing roles into the file.
+This isn't very convenient but it isn't thought that many users will
+need this functionality.
+The purpose of role files is for sharing roles.
+<P>
+
+<DT> <A NAME="patterns-scores2"><EM>patterns-scores2</EM></A>
+
+<DD> Matching patterns and their corresponding actions are stored in
+this variable.
+These patterns are used with
+<A HREF="#scoring-config"><EM>Scoring</EM></A>.
+This variable is normally maintained through the Setup/Rules/SetScores
+configuration screen.
+It is a list variable.
+Each member of the list is a single pattern/action pair, or it can be
+a file which contains zero or more lines of pattern/action pairs.
+The only way to create a scores file is to use the InsertFile command in
+the Setup/Rules/SetScores screen with a filename which doesn't yet exist.
+Then use the Shuffle command to move existing scoring patterns into the file.
+This isn't very convenient but it isn't thought that many users will
+need this functionality.
+The purpose of scoring files is for sharing scoring rules.
+<P>
+This option is displayed as &quot;Patterns Scores&quot;.
+<P>
+
+<DT> <A NAME="patterns-search"><EM>patterns-search</EM></A>
+
+<DD> Matching patterns for use with the Select command are stored in
+this variable.
+These patterns are used with
+<A HREF="#search-rules-config"><EM>Search Rules</EM></A> configuration.
+This variable is normally maintained through the Setup/Rules/searCh
+configuration screen.
+It is a list variable.
+Each member of the list is a single pattern, or it can be
+a file which contains zero or more lines of patterns.
+The only way to create a rules file is to use the InsertFile command in
+the Setup/Rules/searCh screen with a filename which doesn't yet exist. Then use
+the Shuffle command to move existing rules into the file.
+This isn't very convenient but it isn't thought that many users will
+need this functionality.
+<P>
+
+<DT> <A NAME="personal-name"><EM>personal-name</EM></A>
+
+<DD> Personal configuration file only.
+User's full personal name. On UNIX systems, the default is taken
+from the accounts data base (<CODE>/etc/passwd</CODE>).
+The easiest way to change the full From address is with the
+<A HREF="#cust-hdr"><EM>customized-hdrs</EM></A> variable.
+<P>
+
+<DT> <A NAME="personal-print-category"><EM>personal-print-category</EM></A>
+
+<DD> Personal configuration file only.
+This is the category that the default print command belongs to. There
+are three categories. Category 1 is an attached printer which uses the ANSI
+escape sequence, category 2 is the standard system print command, and
+category 3 is the set of custom printer commands defined by the user.
+This just helps <EM>Alpine</EM> figure out where to put the cursor when the user
+runs the <EM>Setup/Printer</EM> command. This is not used by <EM>PC-Alpine</EM>.
+<P>
+
+<DT> <A NAME="personal-print-command"><EM>personal-print-command</EM></A>
+
+<DD> Personal configuration file only.
+This corresponds to the third category in the printer menu, the
+personally selected print commands. This variable
+contains the list of custom commands that the user has entered in the
+<EM>Setup/Printer</EM> screen. This is not used by <EM>PC-Alpine</EM>.
+<P>
+
+<DT> <A NAME="posting-char-set"><EM>posting-character-set</EM></A>
+
+<DD> See the discussion in
+<A HREF="low-level.html#char-set">International Character Sets</EM></A> for
+details.<P>
+
+<DT> <A NAME="postponed-folder"><EM>postponed-folder</EM></A>
+
+<DD> The folder where postponed messages are stored. The default is
+<EM>postponed-msgs</EM> (Unix) or <EM>POSTPOND</EM> (PC). <P>
+
+<DT> <A NAME="print-font-name"><EM>print-font-name</EM></A>
+
+<DD> Winsock version of <EM>PC-Alpine</EM> only. <P>
+
+<DT> <A NAME="print-font-size"><EM>print-font-size</EM></A>
+
+<DD> Winsock version of <EM>PC-Alpine</EM> only. <P>
+
+<DT> <A NAME="print-font-style"><EM>print-font-style</EM></A>
+
+<DD> Winsock version of <EM>PC-Alpine</EM> only. <P>
+
+<DT> <A NAME="printer"><EM>printer</EM></A>
+
+<DD> Personal configuration file only.
+This is the current setting for a user's printer.
+This variable is set from <EM>Alpine</EM>'s <EM>Setup/Printer</EM> screen.
+<P>
+
+<DT> <A NAME="prompt-background-color"><EM>prompt-background-color</EM></A>
+<DT> <A NAME="prompt-foreground-color"><EM>prompt-foreground-color</EM></A>
+
+<DD> <A HREF="#prompt-color"><EM>Prompt Color</EM></A>.
+<P>
+
+<DT> <A NAME="pruned-folders"><EM>pruned-folders</EM></A>
+
+<DD> This variable allows you to define a list of one or more folders that
+<EM>Alpine</EM> will offer to prune for you in the same way it automatically offers
+to prune your "sent-mail" folder each month.
+Each folder in this list must be a folder in your default folder collection
+(the first folder collection if you have more than one), and it is just
+the relative name of the folder in the collection, not the fully-qualified name.
+It is similar to sent-mail.
+Instead of something like
+<P>
+<CENTER><SAMP>pruned-folders={servername}mail/folder</SAMP></CENTER>
+<P>
+the correct value to use would be
+<P>
+<CENTER><SAMP>folder</SAMP></CENTER>
+<P>
+There is an assumption here that your first collection is the folders in
+<P>
+<CENTER><SAMP>{servername}mail</SAMP></CENTER>
+<P>
+
+Once a month, for each folder listed, <EM>Alpine</EM> will offer to move
+the contents of the folder to a new folder of the same name but with
+the previous month's date appended. <EM>Alpine</EM> will then look for any such
+date-appended folder names created for a previous month, and offer each
+one it finds for deletion.
+<P>
+
+If you decline the first offer, no mail is moved and no new folder is
+created.
+<P>
+
+The new folders will be created
+in your default folder collection.
+<P>
+
+<DT> <A NAME="pruning-rule"><EM>pruning-rule</EM></A>
+
+<DD> By default, <EM>Alpine</EM> will ask at the beginning of each month whether or not
+you want to rename your sent-mail folder to a name like sent-mail-month-year.
+(See the feature <A HREF="#prune-uses-yyyy-mm">prune-uses-yyyy-mm</A> to
+change the format of the folder to sent-mail-yyyy-mm.)
+It will also ask whether you would like to delete old sent-mail folders.
+If you have defined
+<A HREF="#read-msg-fold"><EM>read-message-folder</EM></A>
+or
+<A HREF="#pruned-folders"><EM>pruned-folders</EM></A>
+<EM>Alpine</EM> will also ask about pruning those folders.
+With this option you may provide an automatic answer to the rename questions
+and you may tell <EM>Alpine</EM> to not ask about deleting old folders.<P>
+
+<DT> <A NAME="quote1-background-color"><EM>quote1-background-color</EM></A>
+<DT> <A NAME="quote1-foreground-color"><EM>quote1-foreground-color</EM></A>
+<DT> <A NAME="quote2-background-color"><EM>quote2-background-color</EM></A>
+<DT> <A NAME="quote2-foreground-color"><EM>quote2-foreground-color</EM></A>
+<DT> <A NAME="quote3-background-color"><EM>quote3-background-color</EM></A>
+<DT> <A NAME="quote3-foreground-color"><EM>quote3-foreground-color</EM></A>
+
+<DD> <A HREF="#quote-colors"><EM>Quote Colors</EM></A>.
+<P>
+
+<DT> <A NAME="quote-replace-string"><EM>quote-replace-string</EM></A>
+
+<DD> This option specifies what string to use as a quote when <b>viewing</b> a
+message. The standard way of quoting messages when replying is the string
+&quot;&gt;&nbsp;&quot; (quote space).
+With this variable set, viewing a message will
+replace occurrences of &quot;&gt;&nbsp;&quot; with the replacement string.
+This setting works best when
+<A HREF="#reply-ind-string">Reply-Indent-String</A>
+or the equivalent setting in your correspondents' mail programs
+is set to the default &quot;&gt;&nbsp;&quot;, but it will also work fine with the
+Reply-Indent-String set to &quot;&gt;&quot;.
+<P>
+Enable the feature
+<A HREF="#quote-replace-nonflowed">Quote-Replace-Nonflowed</A>
+to also have quote-replacement performed on non-flowed messages.
+<P>
+Setting this option will replace &quot;&gt;&quot; and
+&quot;&gt;&nbsp;&quot; with the new setting. This string may include trailing
+spaces. To preserve those spaces enclose the full string in double quotes.
+<P>
+No padding to separate the text of the message from the quote string is
+added. This means that if you do not add trailing spaces to the value of
+this variable, text will be displayed right next to the quote string,
+which may be undesirable. This can be avoided by adding a new string
+separated by a space from your selection of quote string replacement. This
+last string will be used for padding. For example, setting this variable to
+&quot;&gt;&quot;&nbsp;&quot; &quot; has the effect of setting
+&quot;&gt;&quot; as the quote-replace-string, with the text padded by
+a space from the last quote string to make it more readable.
+<P>
+One possible setting for this variable could be
+&quot;&nbsp;&nbsp;&nbsp;&nbsp;&quot; (four spaces wrapped in quotes), which
+would have the effect of indenting each level of quoting four spaces and
+removing the &quot;&gt;&quot;'s. Different levels of quoting could be made
+more discernible by setting colors for quoted text.
+<P>
+Replying to or forwarding the viewed message will preserve the original
+formatting of the message, so quote-replacement will not be performed on
+messages that are being composed.
+<P>
+
+<DT> <A NAME="quote-suppression-threshold"><EM>quote-suppression-threshold</EM></A>
+
+<DD> This option should be used with care.
+It will cause some of the quoted text to be eliminated from the
+display when viewing a message in the MESSAGE TEXT screen.
+For example, if you set the Quote-Suppression-Threshold to the
+value &quot;5&quot;,
+this will cause quoted text that is longer than five lines to be truncated.
+Quoted text of five or fewer consecutive lines will be displayed in its entirety.
+Quoted text of more than six lines will have the first five lines displayed
+followed by a line that looks something like
+<P>
+<CENTER><SAMP>[ 12 lines of quoted text hidden from view ]</SAMP></CENTER>
+<P>
+As a special case, if exactly one line of quoted text would be hidden, the
+entire quote will be shown instead.
+So for the above example, quoted text which is exactly six lines long will
+will be shown in its entirety.
+(In other words, instead of hiding a single line and adding a line
+that announces that one line was hidden, the line is just shown.)
+<P>
+If the sender of a message has carefully chosen the quotes that he or she
+includes, hiding those quotes may change the meaning of the message.
+For that reason, <EM>Alpine</EM> requires that when you want to set the value of this
+variable to something less than four lines, you actually have to set it
+to the negative of that number.
+So if you want to set this option to &quot;3&quot;, you actually have to
+set it to &quot;-3&quot;.
+The only purpose of this is to get you to think about whether or not you
+really want to do this!
+If you want to delete all quoted text you set the value of this option
+to the special value &quot;-10&quot;.
+<P>
+The legal values for this option are
+<P>
+<TABLE>
+<TR>
+ <TD> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </TD>
+ <TD> Default, don't hide anything </TD>
+</TR>
+<TR>
+ <TD> &nbsp;-1,-2,-3&nbsp;&nbsp; </TD>
+ <TD> Suppress quote lines past 1, 2, or 3 lines </TD>
+</TR>
+<TR>
+ <TD> &nbsp;4,5,6,...&nbsp; </TD>
+ <TD> Suppress if more than that many lines </TD>
+</TR>
+<TR>
+ <TD> &nbsp;&nbsp;&nbsp;-10&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </TD>
+ <TD> Suppress all quoted lines </TD>
+</TR>
+</TABLE>
+<P>
+If you set this option to a non-default value you may sometimes wish to
+view the quoted text that is not shown.
+When this is the case, the HdrMode (Header Mode) command may be used to
+show the hidden text.
+Typing the &quot;H&quot; command once will show the hidden text.
+Typing a second &quot;H&quot; will also turn on Full Header mode.
+The presence or absence of the HdrMode command is determined by the
+<A HREF="#enable-full-header-cmd">&quot;Enable-Full-Header-Cmd&quot;</A>
+Feature-List option in your <EM>Alpine</EM> configuration, so you will want to
+be sure that is turned on if you use quote suppression.
+<P>
+For the purposes of this option, a quote is a line that begins with the
+character &quot;&gt;&quot;.
+<P>
+Quotes are only suppressed when displaying a message on the screen.
+The entire quote will be left intact when printing or forwarding or something
+similar.
+<P>
+
+<DT> <A NAME="read-msg-fold"><EM>read-message-folder</EM></A>
+
+<DD> If set, mail in the <EM>INBOX</EM> that has been read but not deleted
+is moved here, or rather, the user is asked whether or not he or she wants
+to move it here upon quitting <EM>Alpine</EM>. <P>
+
+<DT> <A NAME="remote-abook-history"><EM>remote-abook-history</EM></A>
+
+<DD> Sets how many extra copies of
+<A HREF="low-level.html#addrbook">remote address book</A>
+data will be kept in each remote address book folder.
+The default is three.
+These extra copies are simply old versions of the data. Each time a change
+is made a new copy of the address book data is appended to the folder. Old
+copies are trimmed, if possible, when <EM>Alpine</EM> exits.
+An old copy can be put back into use by
+deleting and expunging newer versions of the data from the folder.
+Don't delete the first message from the folder. It is a special header
+message for the remote address book and it must be there.
+This is to prevent regular folders from being used as remote address book
+folders and having their data destroyed.
+<P>
+
+<DT> <A NAME="remote-abook-metafile"><EM>remote-abook-metafile</EM></A>
+
+<DD> Personal configuration file only.
+This is usually set by <EM>Alpine</EM> and is the name of a file
+that contains data about
+<A HREF="low-level.html#addrbook">remote address books</A> and
+<A HREF="low-level.html#remote-config">remote configuration files</A>.
+<P>
+
+<DT> <A NAME="remote-abook-validity"><EM>remote-abook-validity</EM></A>
+
+<DD> Sets the minimum number of minutes that a
+remote address book will be considered up to date.
+Whenever an entry contained in a remote address book is used,
+if more than this many minutes have
+passed since the last check the remote server will be queried to see if the
+address book has changed.
+If it has changed, the local copy is updated.
+The default value is five minutes.
+The special value of -1 means never check.
+The special value of zero means only check when the address book is first
+opened.
+<P>
+No matter what the value, the validity check is always done when the
+address book is about to be changed by the user.
+The check can be initiated manually by typing <EM>^L</EM> (Ctrl-L)
+while in the address book maintenance screen for the remote address book.
+<P>
+
+<DT> <A NAME="reply-ind-str"><EM>reply-indent-string</EM></A>
+
+<DD> This variable specifies an aspect of <EM>Alpine</EM>'s <EM>Reply</EM>
+command.
+When a message is replied to and the text of the message is included, the
+included text usually has the string &quot;&gt; &quot; prepended
+to each line indicating it is quoted text.
+
+<P>
+This option specifies a different value for that string.
+If you wish to use a string which begins or ends with a space,
+enclose the string in double quotes.
+
+<P>
+Besides simple text, the prepended string can be based
+on the message being replied to.
+The following tokens are substituted for the message's corresponding value:
+
+<DL>
+<DT>_FROM_</DT>
+<DD>This token gets replaced with the message sender's &quot;username&quot;.
+At most six characters are used.
+</DD>
+
+<DT>_NICK_</DT>
+<DD>This token gets replaced with the nickname of the message sender's
+address as found in your addressbook.
+If no addressbook entry is found,
+Pine replaces the characters &quot;_NICK_&quot; with nothing.
+At most six characters are used.
+</DD>
+
+<DT>_INIT_</DT>
+<DD>This token gets replaced with the initials of the sender of the message.
+</DD>
+
+</DL>
+
+When the
+<A HREF="#enable-reply-indent-string-editing"><EM>enable-reply-indent-string-editing</EM></A>
+feature is enabled, you are given the opportunity to edit the string, whether
+it is the default or one automatically generated using the above tokens.
+<P>
+
+<DT> <A NAME="reply-leadin"><EM>reply-leadin</EM></A>
+
+<DD> This option is used to customize the content of the introduction line
+that is included when replying to a message and including the original
+message in the reply.
+The normal default (what you will get if you delete this variable) looks
+something like:
+<P>
+<CENTER><SAMP>On Sat, 24 Oct 1998, Fred Flintstone wrote:</SAMP></CENTER>
+<P>
+where the day of the week is only included if it is available in the
+original message.
+You can replace this default with text of your own.
+The text may contain tokens that are replaced with text
+that depends on the message you are replying to.
+For example, the default is equivalent to:
+<P>
+<CENTER><SAMP>On _DAYDATE_, _FROM_ wrote:</SAMP></CENTER>
+<P>
+
+Since this variable includes regular text mixed with special tokens
+the tokens have to be surrounded by underscore characters.
+For example, to use the token &quot;<SAMP>PREFDATE</SAMP>&quot;
+you would need to use &quot;<SAMP>_PREFDATE_</SAMP>&quot;,
+not &quot;<SAMP>PREFDATE</SAMP>&quot;.
+<P>
+The list of available tokens is
+<A HREF="#index-tokens"><EM>here</EM></A>.
+
+<P>
+By default, the text is all on a single line and is followed by a blank line.
+If your <EM>Reply-Leadin</EM> turns out to be longer
+than 80 characters when replying to a particular message, it is shortened.
+However, if you use the token
+<P>
+<CENTER><SAMP>_NEWLINE_</SAMP></CENTER>
+<P>
+
+anywhere in the value, no end of line or blank line is appended, and no
+shortening is done.
+The _NEWLINE_ token may be used to get rid of the blank line following
+the text, to add more blank lines, or to form a multi-line
+<EM>Reply-Leadin</EM>.
+To clarify how _NEWLINE_ works recall that the default value is:
+<P>
+<CENTER><SAMP>On _DAYDATE_, _FROM_ wrote:</SAMP></CENTER>
+<P>
+
+That is equivalent to
+<P>
+<CENTER><SAMP>On _DAYDATE_, _FROM_ wrote:_NEWLINE__NEWLINE_</SAMP></CENTER>
+<P>
+
+In the former case, two newlines are added automatically because
+no _NEWLINE_ token appears in the value of the option (for backwards
+compatibility). In the latter case, the newlines are explicit.
+If you want to remove the blank line that follows the
+<EM>Reply-Leadin</EM> text use a single
+_NEWLINE_ token like
+<P>
+<CENTER><SAMP>On _DAYDATE_, _FROM_ wrote:_NEWLINE_</SAMP></CENTER>
+<P>
+
+Because of the backwards compatibility problem, it is not possible to
+remove all of the ends of lines, because then there will be no _NEWLINE_ tokens
+and that will cause the automatic adding of two newlines!
+If you want, you may embed newlines in the middle of the text, as well,
+producing a multi-line <EM>Reply-Leadin</EM>.
+
+<P>
+By default, no attempt is made to localize the date.
+If you prefer a localized form you may find that one of the tokens
+_PREFDATE_ or _PREFDATETIME_ is a satisfactory substitute.
+If you want more control one of the many other date tokens, such as _DATEISO_,
+might be better.
+
+<P>
+For the adventurous, there is a way to conditionally include text based
+on whether or not a token would result in specific replacement text.
+For example, you could include some text based on whether or not
+the _NEWS_ token would result in any newsgroups if it was used.
+It's explained in detail
+<A HREF="#reply-token-conditionals"><EM>here</EM></A>.
+
+<P>
+In the very unlikely event that you want to include a literal token
+in the introduction line you must precede it with a backslash character.
+For example,
+<P>
+<CENTER><SAMP>&#92;_DAYDATE_ = _DAYDATE_</SAMP></CENTER>
+<P>
+would produce something like
+<P>
+<CENTER><SAMP>_DAYDATE_ = Sat, 24 Oct 1998</SAMP></CENTER>
+<P>
+It is not possible to have a literal backslash followed by an expanded token.
+<P>
+
+<DT> <A NAME="reverse-background-color"><EM>reverse-background-color</EM></A>
+<DT> <A NAME="reverse-foreground-color"><EM>reverse-foreground-color</EM></A>
+
+<DD> <A HREF="#reverse-color"><EM>Reverse Color</EM></A>.
+<P>
+
+<DT> <A NAME="rsh-command"><EM>rsh-command</EM></A>
+
+<DD> Sets the format of the command used to
+open a UNIX remote shell connection. The default is
+"%s %s -l %s exec /etc/r%sd". All four "%s" entries MUST exist in the
+provided command. The first is for the command's pathname, the second is
+for the host to connnect to, the third is for the user to connect as, and
+the fourth is for the connection method (typically <CODE>imap</CODE>).
+<P>
+
+<DT> <A NAME="rsh-open-timeout"><EM>rsh-open-timeout</EM></A>
+
+<DD> Sets the time in seconds that <EM>Alpine</EM> will
+attempt to open a UNIX remote shell connection.
+The default is 15, the minimum non-zero value is 5,
+and the maximum is unlimited. If this is set to zero rsh connections
+will be completely disabled.
+<P>
+
+<DT> <A NAME="rsh-path"><EM>rsh-path</EM></A>
+
+<DD> Sets the name of the command used to open a UNIX remote shell
+connection. The default is typically <CODE>/usr/ucb/rsh</CODE>.
+<P>
+
+<DT> <A NAME="saved-msg-name"><EM>saved-msg-name-rule</EM></A>
+
+<DD> Determines default folder name when <EM>Sav</EM>ing.
+If set to <EM>default-folder</EM> (which is the default setting),
+then <EM>Alpine</EM> will offer the folder "saved-messages" (UNIX) or "SAVEMAIL"
+(PC) for <EM>Sav</EM>ing messages. The default folder offered in this way
+may be changed by using the configuration variable
+<A HREF="#def-save"><EM>default-saved-msg-folder</EM></A>.
+<P>
+
+If this rule is set to <EM>last-folder-used</EM>, <EM>Alpine</EM> offers to
+<EM>Save</EM> to the folder you last successfully <EM>Saved</EM> a message
+to (this session).
+The first time you <EM>Save</EM> a message in a session,
+<EM>Alpine</EM> offers to <EM>Save</EM> the message to the default folder.
+<P>
+
+Choosing any of the <EM>by-</EM> options causes <EM>Alpine</EM> to attempt
+to get the chosen option's value for the message being <EM>Saved</EM> (or
+for the first message being Saved if using an aggregate Save).
+For example, if <EM>by-from</EM> is chosen, <EM>Alpine</EM> attempts to
+get the value of who the message
+came from (i.e. the from address). <EM>Alpine</EM> then attempts to
+<EM>Save</EM> the message to a folder matching that value.
+If <EM>by-from</EM> is chosen and no value is
+obtained, <EM>Alpine</EM> uses <EM>by-sender</EM>.
+The opposite is also true.
+If <EM>by-recipient</EM> was chosen and the message was posted to a newsgroup,
+<EM>Alpine</EM> will use the newsgroup name.
+If <EM>by-replyto</EM> is chosen and no value is
+obtained, <EM>Alpine</EM> uses <EM>by-from</EM>.
+<P>
+
+If any of the &quot;by-realname&quot; options are chosen, <EM>Alpine</EM> will attempt
+to use the personal name part of the address instead of the mailbox part.
+If any of the &quot;by-nick&quot; options are chosen, the
+address is looked up in your address book and if found, the
+nickname for that entry is used.
+Only simple address book entries are checked, not distribution lists.
+Similarly, if any of the
+&quot;by-fcc&quot; options are chosen, the fcc from the corresponding
+address book entry is used.
+If by-realname, or the by-nick or by-fcc lookups result in no value,
+then if the chosen option ends with the &quot;then-from&quot;,
+&quot;then-sender&quot;, &quot;then-replyto&quot;,
+or &quot;then-recip&quot; suffix, <EM>Alpine</EM>
+reverts to the same behavior as &quot;by-from&quot;,
+&quot;by-sender&quot;, &quot;by-replyto&quot;, or &quot;by-recip&quot;
+depending on which option was specified.
+If the chosen option doesn't end with one of
+the &quot;then-&quot; suffixes, then <EM>Alpine</EM> reverts to the default
+folder when no match is found in the address book.
+
+<P>
+Here is an example to make some of the options clearer.
+If the message is From
+<P>
+<CENTER><SAMP>Fred Flintstone &lt;flint@bedrock.org&gt;</SAMP></CENTER>
+<P>
+and this rule is set to &quot;by-from&quot;, then the default folder offered
+in the save dialog would be &quot;flint&quot;.
+<P>
+If this rule is set to &quot;by-realname-of-from&quot; then the default would
+be &quot;Fred Flintstone&quot;.
+<P>
+If this rule is set to &quot;by-nick-of-from&quot; then <EM>Alpine</EM> will search
+for the address &quot;flint@bedrock.org&quot; in your address book.
+If an entry is found and it has a nickname associated with it, that nickname
+will be offered as the default folder.
+If not, the default saved message folder will be offered as the default.
+<P>
+If this rule is set to &quot;by-fcc-of-from&quot; then <EM>Alpine</EM> will search
+for the address &quot;flint@bedrock.org&quot; in your address book.
+If an entry is found and it has an Fcc associated with it, that Fcc
+will be offered as the default folder.
+If not, the default saved message folder will be offered as the default.
+<P>
+If this rule is set to &quot;by-nick-of-from-then-from&quot; then <EM>Alpine</EM> will search
+for the address &quot;flint@bedrock.org&quot; in your address book.
+If an entry is found and it has a nickname associated with it, that nickname
+will be offered as the default folder.
+If it is not found (or has no nickname) then the default offered will be
+the same as it would be for the &quot;by-from&quot; rule.
+That is, it would be &quot;flint&quot;
+<P>
+This option is displayed as &quot;Saved Message Name Rule&quot;.
+<P>
+
+<DT> <A NAME="scroll-margin"><EM>scroll-margin</EM></A>
+
+<DD> This option controls when <EM>Alpine</EM>'s line-by-line scrolling occurs.
+Typically, when a selected item is at the top or bottom screen edge
+and the UP or DOWN (and Ctrl-P or Ctrl-N) keys are pressed, the
+displayed items are scrolled down or up by a single line.
+<P>
+
+This option allows you to tell <EM>Alpine</EM> the number of lines from the top and
+bottom screen edge that line-by-line scrolling should occur. For example,
+setting this value to one (1) will cause <EM>Alpine</EM> to scroll the display
+when you move to select an item on the display's top or
+bottom edge (instead of moving when you move off the edge of the screen).
+<P>
+
+By default, this variable is zero (0), indicating that scrolling happens
+when you move up or down to select an item immediately off the display's
+top or bottom edge.
+<P>
+
+<DT> <A NAME="selectable-item-background-color"><EM>selectable-item-background-color</EM></A>
+<DT> <A NAME="selectable-item-foreground-color"><EM>selectable-item-foreground-color</EM></A>
+
+<DD> <A HREF="#selectable-item-color"><EM>Selectable-item Color</EM></A>.
+<P>
+
+<DT> <A NAME="sending-filters"><EM>sending-filters</EM></A>
+
+<DD> This option defines a list of text-filtering commands (programs and
+scripts) that may be selectively invoked to process a message just before
+it is sent. If set, the Composer's <EM>^X Send</EM> command will allow you to
+select which filter (or none) to apply to the message before it is sent.
+For security reasons, the full path of the filter program must be
+specified.
+<P>
+Sending filters do not work with <EM>PC-Alpine</EM> and sending filters are
+not used if the feature
+<A HREF="#send-without-confirm">send-without-confirm</A> is set.
+<P>
+Command Modifying Tokens:
+
+<DL>
+<DT><EM>_RECIPIENTS_</EM>
+<DD>When the command is executed, this token is replaced
+with the space delimited list of recipients of the
+message being sent.
+
+<DT><EM>_TMPFILE_</EM>
+<DD>
+When the command is executed, this token is
+replaced with the path and name of the temporary
+file containing the text to be filtered. <EM>Alpine</EM>
+expects the filter to replace this data with the
+filter's result.
+
+NOTE: Use of this token implies that the text to
+be filtered is not piped into standard input of the
+executed command and its standard output is ignored.
+<EM>Alpine</EM> restores the tty modes before invoking the
+filter in case the filter interacts with the user
+via its own standard input and output.
+
+<DT><EM>_RESULTFILE_</EM>
+<DD>When the command is executed, this token is
+replaced with the path and name of a temporary
+file intended to contain a status message from the
+filter. <EM>Alpine</EM> displays this in the message status
+field.
+
+<DT><EM>_DATAFILE_</EM>
+<DD>When the command is executed, this token is replaced
+in the command line with the path and name of a
+temporary file that <EM>Alpine</EM> creates once per session
+and deletes upon exit. The file is intended to be
+used by the filter to store state information between
+instances of the filter.
+
+<DT><EM>_PREPENDKEY_</EM>
+<DD>When the command is executed, this token indicates
+that a random number will be passed down the input
+stream before the message text. It is not included as a command-line argument.
+This number could be used as a session key. It is sent in this way
+to improve security. The number is unique to the
+current <EM>Alpine</EM> session and is only generated once per
+session.
+
+<DT><EM>_INCLUDEALLHDRS_</EM>
+<DD>When the command is executed, this token indicates
+that the headers of the message will be passed down the input stream
+before the message text.
+It is not included as a command-line argument.
+The filter should, of course, remove the headers before returning control
+to <EM>Alpine</EM>.
+
+<DT><EM>_MIMETYPE_</EM>
+<DD>When the command is executed, this token is replaced in the
+command name with a temporary file name used to accept any new MIME
+Content-Type information necessitated by the output of the filter.
+Upon the filter's exit, if the file contains new MIME type
+information, <EM>Alpine</EM> verifies its format and replaces the outgoing
+message's MIME type information with that contained in the file. This
+is basically a cheap way of sending something other than Text/Plain.
+</DL>
+<P>
+
+<DT> <A NAME="sendmail-path"><EM>sendmail-path</EM></A>
+
+<DD> This names the path to an
+alternative program, and any necessary arguments, to be used in posting
+mail messages. See the section on <A
+HREF="background.html#SMTP">SMTP and Sendmail</A> for more details.
+<P>
+
+<DT> <A NAME="sig-file"><EM>signature-file</EM></A></A>
+
+<DD> This is the name of a file which will be automatically inserted into
+outgoing messages.
+It typically contains information such as your
+name, email address and organizational affiliation.
+<EM>Alpine</EM> adds the
+signature into the message as soon as you enter the composer so you
+can choose to remove it or edit it on a message by message basis.
+Signature file placement in message replies is controlled by the
+<A HREF="#sig-at-bot">signature-at-bottom</A>
+setting in the feature list.
+<P>
+
+This defaults to
+<CODE>~/.signature</CODE> on UNIX and &lt;PINERC
+directory&gt;<CODE>\PINE.SIG</CODE> on a PC. <P>
+
+To create or edit your signature file choose Setup from the Main Menu
+and then select S for Signature (Main/Setup/Signature).
+This puts you
+into the Signature Editor where you can enter a <EM>few</EM> lines of
+text containing your identity and affiliation.
+
+<P>
+If the filename is followed by a vertical bar (|) then instead
+of reading the contents of the file the file is assumed to be a
+program which will produce the text to be used on its standard output.
+The program can't have any arguments and doesn't receive any input from <EM>Alpine</EM>,
+but the rest of the processing works as if the contents came from a file.
+
+<P>
+Instead of storing the data in a local file, the
+signature data may be stored remotely in an IMAP folder.
+In order to do this,
+you must use a remote name for the file.
+A remote signature-file name might look like:
+<P>
+<CENTER><SAMP>{myimaphost.myschool.k12.wa.us}mail/signature</SAMP></CENTER>
+<P>
+or, if you have an SSL-capable version of <EM>Alpine</EM>, you might try
+<P>
+<CENTER><SAMP>{myimaphost.myschool.k12.wa.us/user=loginname/ssl}mail/signature</SAMP></CENTER>
+<P>
+
+The syntax used here is the same as the syntax used for remote configuration
+files from the command line.
+Note that you may not access an existing signature file remotely,
+you have to create a new <EM>folder</EM> which contains the signature data.
+If the name you use here for the signature file is a remote name, then when
+you edit the file from the Setup/Signature command the data will be stored
+remotely in the folder.
+You aren't required to do anything special to create the folder, it
+gets created automatically if you use a remote name.
+
+<P>
+Besides regular text, the signature file may also contain
+(or a signature program may produce) tokens which
+are replaced with text which usually depends on the message you are replying
+to or forwarding.
+For example, if the signature file contains the token
+<P>
+<CENTER><SAMP>_DATE_</SAMP></CENTER>
+<P>
+anywhere in the text, then that token is replaced by the date
+the message you are replying to or forwarding was sent.
+If it contains
+<P>
+<CENTER><SAMP>_CURDATE_</SAMP></CENTER>
+<P>
+that is replaced with the current date.
+The first is an example of a token which depends on the message you
+are replying to (or forwarding) and the second is an example which
+doesn't depend on anything other than the current date.
+You have to be a little careful with this facility since tokens which
+depend on the message you are replying to or forwarding will be replaced
+by nothing in the case where you are composing a new message from scratch.
+The use of <A HREF="#role-config"><EM>roles</EM></A> may help you
+in this respect.
+It allows you to use different signature files in different cases.
+<P>
+
+The list of tokens available for use in the signature file is
+<A HREF="#index-tokens"><EM>here</EM></A>.
+<P>
+
+Instead of, or along with the use of <EM>roles</EM> to give you
+different signature files in different situations, there is also
+a way to conditionally include text based
+on whether or not a token would result in specific replacement text.
+For example, you could include some text based on whether or not
+the _NEWS_ token would result in any newsgroups if it was used.
+This is explained in detail
+<A HREF="#reply-token-conditionals"><EM>here</EM></A>.
+This isn't for the faint of heart.
+<P>
+In the very unlikely event that you want to include a literal token
+in the signature you must precede it with a backslash character.
+For example,
+<P>
+<CENTER><SAMP>&#92;_DAYDATE_ = _DAYDATE_</SAMP></CENTER>
+<P>
+would produce something like
+<P>
+<CENTER><SAMP>_DAYDATE_ = Sat, 24 Oct 1998</SAMP></CENTER>
+<P>
+It is not possible to have a literal backslash followed by an expanded token.
+<P>
+
+<DT> <A NAME="signature-background-color"><EM>signature-background-color</EM></A>
+<DT> <A NAME="signature-foreground-color"><EM>signature-foreground-color</EM></A>
+
+<DD> <A HREF="#signature-color"><EM>Signature Color</EM></A>.
+<P>
+
+<DT> <A NAME="smime-public-cert-directory"><EM>smime-public-cert-directory</EM></A>
+
+<DD> UNIX <EM>Alpine</EM> only.
+<P>
+If the option
+<A HREF="#smime-public-cert-container"><EM>smime-public-cert-container</EM></A>
+is set then this option will have no effect.
+<P>
+Normally, Public Certificates for use with S/MIME will be stored in the directory
+which is the value of this option.
+Those certificates will be stored in PEM format, one certificate per file.
+The name of the file for the certificate corresponding to
+<P>
+<CENTER><SAMP>emailaddress</SAMP></CENTER>
+<P>
+should be
+<P>
+<CENTER><SAMP>emailaddress.crt</SAMP></CENTER>
+<P>
+For example, a file for user@example.com would be in the file
+<P>
+<CENTER><SAMP>user@example.com.crt</SAMP></CENTER>
+<P>
+in this directory.
+<P>
+Use the Setup/SMIME screen to modify this variable.
+<P>
+Typically, the public certificates that you have will come from S/MIME signed
+messages that are sent to you.
+<EM>Alpine</EM> will extract the public certificate from the signed message and store
+it in the certificates directory.
+These PEM format public certificates look something like:
+<PRE>
+-----BEGIN CERTIFICATE-----
+MIIFvTCCBKWgAwIBAgIQD4fYFHVI8T20yN4nus097DANBgkqhkiG9w0BAQUFADCB
+rjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
+Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
+...
+2b9KGqDyMWW/rjNnmpjzjT2ObGM7lRA8lke4FLOLajhrz4ogO3b4DFfAAM1VSZH8
+D6sOwOLJZkLY8FRsfk63K+2EMzA2+qAzMKupgeTLqXIf
+-----END CERTIFICATE-----
+</PRE>
+<P>
+<UL>
+<LI><A HREF="config-notes.html#smime-general">General S/MIME Overview</A>
+</UL><P>
+This option is displayed as &quot;S/MIME - Public Cert Directory&quot;.
+<P>
+
+<DT> <A NAME="smime-public-cert-container"><EM>smime-public-cert-container</EM></A>
+
+<DD> UNIX <EM>Alpine</EM> only.
+<P>
+If this option is set it will be used instead of
+<A HREF="#smime-public-cert-directory"><EM>smime-public-cert-directory</EM></A>
+<P>
+This option gives you a way to store certificates remotely on an IMAP server
+instead of storing the certificates one per file locally.
+In order to do that you just give this option a remote folder name for a folder
+which does not yet exist.
+The name is similar to the name you might use for a remote configuration file.
+A remote folder name might look something like:
+<P>
+<CENTER><SAMP>{myimaphost.myschool.k12.wa.us}mail/publiccerts</SAMP></CENTER>
+<P>
+Use the Setup/SMIME screen to modify this variable.
+<P>
+<UL>
+<LI><A HREF="config-notes.html#smime-general">General S/MIME Overview</A>
+</UL><P>
+This option is displayed as &quot;S/MIME - Public Cert Container&quot;.
+<P>
+
+<DT> <A NAME="smime-private-key-directory"><EM>smime-private-key-directory</EM></A>
+
+<DD> UNIX <EM>Alpine</EM> only.
+<P>
+In order to sign outgoing S/MIME messages you will need a
+personal digital ID certificate.
+You will usually get such a certificate from a certificate authority such as
+Thawte or CAcert.
+(In order to encrypt outgoing messages you don't need a personal digital ID, you
+need the public certificate of the recipient instead.)
+If the option
+<A HREF="#smime-private-key-container"><EM>smime-private-key-container</EM></A>
+is set then this option will have no effect.
+<P>
+Normally, Private Keys for use with S/MIME will be stored in the directory
+which is the value of this option.
+Those certificates will be stored in PEM format, one certificate per file.
+The name of the file for the certificate corresponding to your
+<P>
+<CENTER><SAMP>emailaddress</SAMP></CENTER>
+<P>
+should be
+<P>
+<CENTER><SAMP>emailaddress.key</SAMP></CENTER>
+<P>
+For example, if your address is user@example.com the name of the file would be
+<P>
+<CENTER><SAMP>user@example.com.key</SAMP></CENTER>
+<P>
+in this directory.
+<P>
+Use the Setup/SMIME screen to modify this variable.
+<P>
+Typically, the private key that you have will come from a Certificate
+Authority.
+The private key should be stored in a PEM format file that
+looks something like:
+<PRE>
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,2CBD328FD84CF5C6
+
+YBEXYLgLU9NJoc1V+vJ6UvcF08RX54S6jXsmgL0b5HGkudG6fhnmHkH7+UCvM5NI
+SXO/F8iuZDfs1VGG0NyitkFZ0Zn2vfaGovBvm15gx24b2xnZDLRB7/bNZkurnK5k
+VjAjZ2xXn2hFp2GJwqRdmxYNqsKGu52B99oti5HUWuZ2GFRaWjn5hYOqeApZE2uA
+...
+oSRqfI51UdSRt0tmGhHeTvybUVrHm9eKft8TTGf+qSBqzSc55CsmoVbRzw4Nfhix
+m+4TJybNGNfAgOctSkEyY/OCb49fRRQTCBZVIhzLGGmpYmkO55HbIA==
+-----END RSA PRIVATE KEY-----
+</PRE>
+<P>
+<UL>
+<LI><A HREF="config-notes.html#smime-general">General S/MIME Overview</A>
+</UL><P>
+This option is displayed as &quot;S/MIME - Private Key Directory&quot;.
+<P>
+
+<DT> <A NAME="smime-private-key-container"><EM>smime-private-key-container</EM></A>
+
+<DD> UNIX <EM>Alpine</EM> only.
+<P>
+If this option is set it will be used instead of
+<A HREF="#smime-private-key-directory"><EM>smime-private-key-directory</EM></A>.
+<P>
+This option gives you a way to store keys remotely on an IMAP server
+instead of storing the keys one per file locally.
+In order to do that you just give this option a remote folder name for a folder
+which does not yet exist.
+The name is similar to the name you might use for a remote configuration file.
+A remote folder name might look something like:
+<P>
+<CENTER><SAMP>{myimaphost.myschool.k12.wa.us}mail/privatekeys</SAMP></CENTER>
+<P>
+Use the Setup/SMIME screen to modify this variable.
+<P>
+<UL>
+<LI><A HREF="config-notes.html#smime-general">General S/MIME Overview</A>
+</UL><P>
+This option is displayed as &quot;S/MIME - Private Key Container&quot;.
+<P>
+
+<DT> <A NAME="smime-cacert-directory"><EM>smime-cacert-directory</EM></A>
+
+<DD> UNIX <EM>Alpine</EM> only.
+<P>
+If the option
+<A HREF="#smime-cacert-container"><EM>smime-cacert-container</EM></A>
+is set then this option will have no effect.
+<P>
+CACert is a shorthand name for certification authority certificate.
+Normally <EM>Alpine</EM> will use the CACerts that are located in the standard system
+location for CACerts.
+It may be the case that one of your correspondents has a Digital ID which has
+been signed by a certificate authority that is not in the regular set of system certificate
+authorities.
+You may supplement the system list by adding further certificates of your own.
+These should be stored in the directory
+which is the value of this option.
+The certificates will be stored in PEM format, one certificate per file.
+The names of the files can be anything ending in &quot;.crt&quot;.
+<P>
+Use the Setup/SMIME screen to modify this variable.
+<P>
+These PEM format CA certificates look very similar to your public
+certificates for particular email addresses
+(<A HREF="#smime-public-cert-directory"><EM>smime-public-cert-directory</EM></A>).
+<P>
+<UL>
+<LI><A HREF="config-notes.html#smime-general">General S/MIME Overview</A>
+</UL><P>
+This option is displayed as &quot;S/MIME - Cert Authority Directory&quot;.
+<P>
+
+<DT> <A NAME="smime-cacert-container"><EM>smime-cacert-container</EM></A>
+
+<DD> UNIX <EM>Alpine</EM> only.
+<P>
+If this option is set it will be used instead of
+<A HREF="#smime-cacert-directory"><EM>smime-cacert-directory</EM></A>.
+<P>
+This option gives you a way to store certificates remotely on an IMAP server
+instead of storing the certificates one per file locally.
+In order to do that you just give this option a remote folder name for a folder
+which does not yet exist.
+The name is similar to the name you might use for a remote configuration file.
+A remote folder name might look something like:
+<P>
+<CENTER><SAMP>{myimaphost.myschool.k12.wa.us}mail/cacerts</SAMP></CENTER>
+<P>
+Use the Setup/SMIME screen to modify this variable.
+<P>
+<UL>
+<LI><A HREF="config-notes.html#smime-general">General S/MIME Overview</A>
+</UL><P>
+This option is displayed as &quot;S/MIME - Cert Authority Container&quot;.
+<P>
+
+<DT> <A NAME="smtp-server"><EM>smtp-server</EM></A>
+
+<DD> One or more SMTP servers (host name or IP address) which <EM>Alpine</EM> will
+use for outgoing mail. If not set, <EM>Alpine</EM> passes outgoing email to the
+<EM>sendmail</EM> program on the local machine. <EM>PC-Alpine</EM> users must have
+this variable set in order to send mail as they have no <EM>sendmail</EM>
+program.
+<P>
+Your SMTP server may offer SMTP AUTH authentication.
+It may even require it.
+If your SMTP server offers SMTP AUTH authentication you may specify a
+&quot;user&quot; name parameter to cause <EM>Alpine</EM> to attempt to authenticate.
+This parameter requires an associated value,
+the username identifier with which to establish the server
+connection.
+An example might be:
+
+<P>
+<CENTER><SAMP>smtpserver.example.com/user=katie</SAMP></CENTER>
+<P>
+
+If AUTH authentication is offered by the server, this will cause <EM>Alpine</EM> to
+attempt to use it.
+If AUTH authentication is not offered by the server, this will cause <EM>Alpine</EM>
+to fail sending with an error similar to:
+
+<P>
+<CENTER><SAMP>Error: SMTP authentication not available</SAMP></CENTER>
+<P>
+
+Another type of authentication that is used by some ISPs is called
+&quot;POP before SMTP&quot; or &quot;IMAP before SMTP&quot;,
+which means that you have to authenticate
+yourself to the POP or IMAP server by opening a mailbox before you
+can send mail.
+To do this, you usually only have to open your INBOX.
+<P>
+You may tell <EM>Alpine</EM> to use the
+<A HREF="http://www.ietf.org/rfc/rfc2476.txt">Message Submission</A>
+port (587) instead of the SMTP port (25) by including the &quot;submit&quot;
+parameter
+in this option.
+At this time &quot;/submit&quot; is simply equivalent to specifying
+port 587, though it may imply more than that at some point in the future.
+Some ISPs are blocking port 25 in order to reduce the amount of spam
+being sent to their users.
+You may find that the submit option allows you to get around such a block.
+
+<P>
+<CENTER><SAMP>smtpserver.example.com/submit</SAMP></CENTER>
+<P>
+
+To specify any non-standard port number on the SMTP server you may follow
+the hostname with a colon followed by the portnumber.
+
+<P>
+<CENTER><SAMP>smtpserver.example.com:12345</SAMP></CENTER>
+<P>
+
+Normally, when a connection is made to the Smtp-Server <EM>Alpine</EM> will attempt
+to negotiate a secure (encrypted) session using Transport Layer Security (TLS).
+If that fails then a non-encrypted connection will be attempted instead.
+You may specify that a TLS connection is required if you wish.
+If you append &quot;/tls&quot; to the name then the connection will fail
+instead of falling back to a non-secure connection.
+
+<P>
+<CENTER><SAMP>smtpserver.example.com/tls</SAMP></CENTER>
+<P>
+
+See the
+<A HREF="config-notes.html#smtp-server">SMTP Servers</A>
+section or the
+<A HREF="config-notes.html#server-name-syntax">Server Name Syntax</A>
+section for some more details.
+<P>
+This option is displayed as &quot;SMTP Server (for sending)&quot;.
+<P>
+
+<DT> <A NAME="sort-key"><EM>sort-key</EM></A>
+
+<DD> This variable sets up the default Message Index sorting.
+The default is to
+sort by arrival order (the order the messages arrived in the folder).
+It has the same functionality as the
+<EM>-sort</EM> command line argument and the <EM>$</EM> command in the
+"Folder Index". If a <EM>sort-key</EM> is set, then all folders open during
+the session will have that as the default sort order. <P>
+
+<DT> <A NAME="speller"><EM>speller</EM></A>
+
+<DD> UNIX <EM>Alpine</EM> only.
+<P>
+For <EM>PC-Alpine</EM>, you must install the aspell library code that you
+may get from
+<A HREF="http://aspell.net/win32/">http://aspell.net/win32/</A>.
+<P>
+This option affects the behavior of the <EM>^T</EM> (spell check)
+command in the Composer.
+It specifies the program invoked by <EM>^T</EM> in the Composer.
+By default, <EM>Alpine</EM> uses the system's "spell" command.
+<EM>Alpine</EM> will use the
+command defined by this option (if any) instead.
+When invoking the spell-checking program,
+<EM>Alpine</EM> appends a tempfile name (where the message is passed)
+to the command line.
+<EM>Alpine</EM> expects the speller to correct the
+spelling in that file. When you exit from the speller
+program <EM>Alpine</EM> will read the
+tmpfile back into the composer.
+<P>
+For Unix <EM>Alpine</EM> the program <EM>ispell</EM> works well as an
+alternate spell checker.
+If your Unix system has <EM>ispell</EM> it is probably reasonable to make
+it the default speller by configuring it as the default in the
+system configuration file, <CODE>/usr/local/lib/pine.conf</CODE>.
+
+<P>
+If this option is not set, then the system's <EM>spell</EM> command is used.
+The spell command does not work the same as the alternate speller.
+It produces a list of misspelled words on its standard output, instead,
+and doesn't take a tempfile as an argument.
+Don't set this speller option to the standard Unix spell command.
+That won't work. If you want to use the standard Unix spell command,
+set the speller option to nothing.
+
+<DT> <A NAME="ssh-command"><EM>ssh-command</EM></A>
+
+<DD> Sets the format of the command used to
+open a UNIX secure shell connection. The default is
+"%s %s -l %s exec /etc/r%sd". All four "%s" entries MUST exist in the
+provided command. The first is for the command's pathname, the second is
+for the host to connnect to, the third is for the user to connect as, and
+the fourth is for the connection method (typically <CODE>imap</CODE>).
+<P>
+
+<DT> <A NAME="ssh-open-timeout"><EM>ssh-open-timeout</EM></A>
+
+<DD> Sets the time in seconds that <EM>Alpine</EM> will
+attempt to open a UNIX secure shell connection.
+The default is 15, the minimum non-zero value is 5,
+and the maximum is unlimited. If this is set to zero ssh connections
+will be completely disabled.
+<P>
+
+<DT> <A NAME="ssh-path"><EM>ssh-path</EM></A>
+
+<DD> Sets the name of the command used to open a UNIX secure shell
+connection. The default is typically <CODE>/usr/bin/ssh</CODE>.
+<P>
+
+<DT> <A NAME="standard-printer"><EM>standard-printer</EM></A>
+
+<DD> System-wide configuration file only. Specifies a list of commands
+for category 2 of the <EM>Setup/Printer</EM> screen, the standard print command
+section. This is not used by <EM>PC-Alpine</EM>.
+<P>
+
+<DT> <A NAME="status-background-color"><EM>status-background-color</EM></A>
+<DT> <A NAME="status-foreground-color"><EM>status-foreground-color</EM></A>
+
+<DD> <A HREF="#status-color"><EM>Status Color</EM></A>.
+<P>
+
+<DT> <A NAME="status-message-delay"><EM>status-message-delay</EM></A>
+
+<DD> This option has evolved over time, causing the possible values to be
+counter-intuitive.
+Read carefully before you set this option.
+First we explain what the option does, then there is a longer discussion
+following that.
+<P>
+If this is set to zero, the default value, it has <EM>no</EM> effect.
+Positive and negative values serve two similar, but different purposes.
+<P>
+If it is set to a positive number, it causes the cursor to move to the
+status line whenever a status message is printed and pause there for this
+many seconds.
+It will probably only be useful if the
+<A HREF="#show-cursor"><EM>show-cursor</EM></A>
+feature is
+also turned on.
+Setting this option to a postive number can only be used to
+<EM>increase</EM> the status message delay.
+This may be useful for Braille displays, or other non-traditional displays.
+<P>
+If it is set to a negative number the interpretation is a bit complicated.
+Negative numbers are used to <EM>decrease</EM> the amount of delay <EM>Alpine</EM> uses to
+allow you to read important status messages.
+Of course, this may cause you to miss some important messages.
+If you see a message flash by but miss what it says you can use the
+Journal command from the Main menu to read it.
+If you set this option to a negative value, the delay will be
+no more than one second less than the absolute value
+of the value you set.
+So if you set it to -1, the delay will be no more than zero seconds, no
+delay at all.
+If you set it to -2, the delay will be no more than 1 second.
+And so on, -3 is 2 seconds, -4 is 3 seconds, ...
+If the delay that <EM>Alpine</EM> would have used by default is less than this delay,
+then the smaller delay set by <EM>Alpine</EM> will be used.
+Setting this option to a negative value can only reduce the amount of
+delay, never increase it.
+<P>
+Here is a more detailed explanation.
+Status messages are the messages which show up spontaneously in the
+status message line, the third line from the bottom of the screen.
+By default, <EM>Alpine</EM> assigns each status message it produces a minimum
+display time.
+Some status messages have a minimum display time of zero.
+You can see an example of such a message by paging up in this help text
+until you reach the top of the screen.
+If you try to page past the top you will see the message
+<P>
+<CENTER><SAMP>[Already at start of help text]</SAMP></CENTER>
+<P>
+in the status line.
+If there is another more important use of the status message line this message
+might be replaced quickly, or it even might not be shown at all.
+However, if there is no reason to get rid of the message, it might stay
+there for several seconds while you read the help.
+An example where it is replaced immediately happens when you page up in
+the help text past the top of the screen, but then type the &quot;WhereIs&quot;
+command right after paging up.
+The message will disappear immediately without causing a delay (unless you
+have set this option to a positive value) to allow you to type input for
+the &quot;WhereIs&quot; command.
+Since it isn't a very important message, <EM>Alpine</EM> has set its minimum display
+time to zero seconds.
+<P>
+Other messages have minimum display times of three or more seconds.
+These are usually error messages that <EM>Alpine</EM> thinks you ought to see.
+For example, it might be a message about a failed Save or a failed folder open.
+It is often the case that this minimum display time won't delay you in
+any way because the status message line is not needed for another reason.
+However, there are times when <EM>Alpine</EM> has to delay what it is doing in
+order to display a status message for the minimum display time.
+This happens when a message is being displayed and <EM>Alpine</EM> wants to ask
+for input from the keyboard.
+For example, when you Save a message you use the status message line.
+You get a prompt there asking for the name of the folder to save to.
+If there is a status message being displayed that has not
+yet displayed for its minimum
+time <EM>Alpine</EM> will display that status message surrounded with the characters
+&gt; and &lt; to show you that it is delaying.
+That might happen, for example, if you tried to save to a folder that
+caused an error, then followed that immediately with another Save command.
+You might find yourself waiting for a status message like
+<P>
+<CENTER><SAMP>[&gt;Can't get write access to mailbox, access is readonly&lt;]</SAMP></CENTER>
+<P>
+to finish displaying for three seconds.
+If that is something you find happening to you frequently, you may use
+negative values of this option to decrease or eliminate that delay, at
+the risk of missing the message.
+<P>
+
+<DT> <A NAME="stay-open-folders"><EM>stay-open-folders</EM></A>
+
+<DD> This option affects low-level behavior of <EM>Alpine</EM>.
+There is no default value for this option.
+It is related to the options
+<A HREF="#preopen-stayopen-folders">Preopen-Stayopen-Folders</A>,
+<A HREF="#max-remote-connections">Max-Remote-Connections</A>,
+and <A HREF="#offer-expunge-of-stayopen-folders">offer-expunge-of-Stayopen-Folders</A>.
+
+<P>
+Note: changes made to this list take effect the next time you open a
+folder in the list.
+
+<P>
+This is a list of folders that will be permanently kept open once they
+are first opened.
+The names in this list may be either the nickname of an Incoming folder
+or the full technical specification of a folder.
+The folders in this list need not be remote IMAP folders, they could usefully
+be local folders, as well.
+If a folder in the list is a newsgroup or is not accessed either locally
+or via IMAP, then the entry will be ignored.
+For example, folders accessed via NNTP or POP3 will not be kept open, since
+the way that new mail is found with those protocols involves closing and
+reopening the connection.
+<P>
+Once a Stay Open folder has been opened, new-mail checking will continue
+to happen on that folder for the rest of the <EM>Alpine</EM> session.
+Your INBOX is always implicitly included in this Stay-Open list and doesn't
+need to be added explicitly.
+<P>
+Another difference that you may notice between a Stay Open folder and a
+non-Stay Open folder is which message is selected as the current message
+when you enter the folder index.
+Normally, the starting position for an incoming folder (which most Stay Open
+folders will likely be) is controlled by the
+<A HREF="#incoming-startup-rule"><EM>Incoming-Startup-Rule</EM></A>.
+However, if a folder is a Stay Open folder, when you re-enter the folder
+after the first time the current message will be the same as it was when
+you left the folder.
+An exception is made if you use the TAB command to get to the folder.
+In that case, the message number will be incremented by one from what it
+was when you left the folder.
+<P>
+The above special behavior is thought to be useful.
+However, it is special and different from what you might at first expect.
+The feature
+<A HREF="#use-reg-start-rule"><EM>Use-Regular-Startup-Rule-for-Stayopen-Folders</EM></A>
+may be used to turn off this special treatment.
+<P>
+If the message that was current when you left the folder no longer exists,
+then the regular startup rule will be used instead.
+<P>
+This option is displayed as &quot;Stayopen Folders&quot;.
+<P>
+
+<DT> <A NAME="tcp-open-timeout"><EM>tcp-open-timeout</EM></A>
+
+<DD> Sets the time in seconds that <EM>Alpine</EM> will
+attempt to open a network connection. The default is 30, the minimum is 5,
+and the maximum is system defined (typically 75). If a connection has not
+completed within this many seconds <EM>Alpine</EM> will give up and consider it a
+failed connection.
+<P>
+
+<DT> <A NAME="tcp-query-timeout"><EM>tcp-query-timeout</EM></A>
+
+<DD> When <EM>Alpine</EM> times out a network read or write it will normally just display
+a message saying &quot;Still waiting&quot;.
+However, if enough time has elapsed since it started waiting it will offer
+to let you break the connection.
+That amount of time is set by this option, which defaults to 60 seconds,
+has a minimum of 5 seconds, and a maximum of 1000 seconds.
+<P>
+
+<DT> <A NAME="tcp-read-warning-timeout"><EM>tcp-read-warning-timeout</EM></A>
+
+<DD> Sets the time in seconds that <EM>Alpine</EM> will
+wait for a network read before warning you that things are moving slowly
+and possibly giving you the option to break the connection.
+The default is 15 seconds. The minimum is 5 seconds and the maximumn is
+1000 seconds.
+<P>
+
+<DT> <A NAME="tcp-write-warning-timeout"><EM>tcp-write-warning-timeout</EM></A>
+
+<DD> Sets the time in seconds that <EM>Alpine</EM> will
+wait for a network write before warning you that things are moving slowly
+and possibly giving you the option to break the connection.
+The default is 0 which means it is unset. If set to a non-zero value, the
+minimum is 5 and the maximum is 1000.
+
+<DT> <A NAME="threading-display-style"><EM>threading-display-style</EM></A>
+
+<DD> When a folder is sorted by Threads or OrderedSubject,
+this option will affect the MESSAGE INDEX display.
+By default, <EM>Alpine</EM> will display the MESSAGE INDEX in the
+&quot;show-thread-structure&quot; style if a folder is sorted
+by Threads or OrderedSubject.
+The possible values are:
+<P>
+
+<DL>
+<DT><EM>none</EM></DT>
+<DD>Regular index display.
+The same index line as would be displayed without threading is used.
+The only difference will be in the order of the messages.
+</DD>
+
+<DT><EM>show-thread-structure</EM></DT>
+<DD>Threaded Subjects will be indented and vertical bars and horizontal
+lines will be added to make it easier to see the relationships among
+the messages in a thread (a conversation).
+</DD>
+
+<DT><EM>mutt-like</EM></DT>
+<DD>This is the same as the option above except that the Subject
+is suppressed (is blank) if it matches the previous Subject in the thread.
+The name comes from the email client <A HREF="http://www.mutt.org/">Mutt</A>.
+Here is an example of what a mutt-like index might look like.
+In this example, the first column represents the message number, the
+<A HREF="#threading-index-style"><EM>threading-index-style</EM></A>
+is set to &quot;regular-index-with-expanded-threads&quot;, and the
+<A HREF="#threading-lastreply-character"><EM>Threading-Lastreply-Character</EM></A>
+is set to a backslash:
+<PRE>
+ 1 Some topic
+ 2 . Subject original message in thread
+ 3 |-> reply to 2
+ 4 . |-> another reply to 2
+ 5 . | &#92;-> reply to 4
+ 6 . | &#92;-> reply to 5
+ 7 | &#92;-> reply to 6
+ 8 |-> another reply to 2
+ 9 . |->New subject another reply to 2 but with a New subject
+ 10 | |-> reply to 9
+ 11 | &#92;-> another reply to 9
+ 12 | &#92;-> reply to 11
+ 13 &#92;-> final reply to 2
+ 14 Next topic
+</PRE>
+</DD>
+
+<DT><EM>indent-subject-1</EM></DT>
+<DD>Threaded Subjects will be indented one space per level of the conversation.
+The bars and lines that show up in the show-thread-structure display will
+not be there with this style.
+</DD>
+
+<DT><EM>indent-subject-2</EM></DT>
+<DD>Same as above but indent two spaces per level instead of one space.
+</DD>
+
+<DT><EM>indent-from-1</EM></DT>
+<DD>Similar to indent-subject-1, except that instead of indenting the
+Subject field one space the From field of a thread will be indented one
+space per level of the conversation.
+</DD>
+
+<DT><EM>indent-from-2</EM></DT>
+<DD>Same as above but indent two spaces per level instead of one space.
+</DD>
+
+<DT><EM>show-structure-in-from</EM></DT>
+<DD>The structure of the thread is illustrated with indenting, vertical bars,
+and horizontal lines just like with the show-thread-structure option, but
+the From field is used to show the relationships instead of the Subject field.
+</DD>
+
+</DL>
+<P>
+
+<DT> <A NAME="threading-expanded-character"><EM>threading-expanded-character</EM></A>
+
+<DD> The Threading-Expanded-Character option has a small effect on the MESSAGE
+INDEX display when using a
+<A HREF="#threading-display-style"><EM>threading-display-style</EM></A>
+other than <EM>none</EM>.
+The value of this option is a single character.
+This character is used to indicate that part of a thread has been expanded
+and could be collapsed if desired with
+the &quot;/&quot; Collapse/Expand command.
+By default, the value of this option is a dot (.).
+<P>
+If this option is set to the Empty Value, then the column (and the following
+blank column) will be deleted from the display.
+<P>
+This option is closely related to the
+<A HREF="#threading-indicator-character"><EM>threading-indicator-character</EM></A>
+option.
+Another similar option which affects the thread display is the
+<A HREF="#threading-lastreply-character"><EM>threading-lastreply-character</EM></A> option.
+
+<DT> <A NAME="threading-index-style"><EM>threading-index-style</EM></A>
+
+<DD> When a folder is sorted by Threads or OrderedSubject,
+this option will affect the INDEX displays.
+The possible values are:
+<P>
+
+<DL>
+<DT><EM>regular-index-with-expanded-threads</EM></DT>
+<DD>This is the default display.
+If the configuration option
+<A HREF="#threading-display-style"><EM>threading-display-style</EM></A>
+is set to something other than &quot;none&quot;, then this setting
+will cause <EM>Alpine</EM> to start off with a MESSAGE INDEX with all of
+the threads expanded.
+That is, each message will have a line in the MESSAGE INDEX display.
+The Collapse/Expand command (/) may be used to manually collapse or
+expand a thread or subthread (see also <A HREF="#slash-collapses-entire-thread"><EM>slash-collapses-entire-thread</EM></A>).
+<P>
+This setting affects the display when the folder is first threaded.
+The collapsed state may also be re-initialized by re-sorting the folder manually
+using the SortIndex command ($).
+After re-sorting the threads will once again all be expanded, even if you
+have previously collapsed some of them.
+<P>
+If &quot;threading-display-style&quot; is set to &quot;none&quot;, then
+the display will be the regular default <EM>Alpine</EM> MESSAGE INDEX, but sorted
+in a different order.
+</DD>
+
+<DT><EM>regular-index-with-collapsed-threads</EM></DT>
+<DD>If the configuration option
+<A HREF="#threading-display-style"><EM>threading-display-style</EM></A>
+is set to something other than &quot;none&quot;, then this setting
+will cause <EM>Alpine</EM> to start out with all of the threads collapsed instead of
+starting out with all of the threads expanded.
+The Collapse/Expand command (/) may be used to manually collapse or
+expand a thread or subthread (see also <A HREF="#slash-collapses-entire-thread"><EM>slash-collapses-entire-thread</EM></A>).
+<P>
+This setting affects the display when the folder is first threaded.
+The collapsed state may also be re-initialized by re-sorting the folder manually
+using the SortIndex command ($).
+After re-sorting the threads will once again all be collapsed, even if you
+have previously expanded some of them.
+</DD>
+
+<DT><EM>separate-index-screen-always</EM></DT>
+<DD>With this setting and the next, you will see an index of threads
+instead of an
+index of messages, provided you have sorted by Threads or OrderedSubject.
+<P>
+The THREAD INDEX contains a '*' in the first column if any message in the thread
+is marked Important.
+If not, it contains a '+' if any message in the thread is to you.
+The second column is blank. The third column contains a 'D' if all of the
+messages in the thread are deleted.
+Otherwise, it contains an 'N' if any of the messages in the thread are New.
+<P>
+When you view a particular thread from the THREAD INDEX you will be
+in the MESSAGE INDEX display
+but the index will only contain messages from the thread you are viewing.
+</DD>
+
+<DT><EM>separate-index-screen-except-for-single-messages</EM></DT>
+<DD>This is very similar to the option above.
+When you are in the THREAD INDEX, one of the available commands
+is &quot;ViewThd&quot;.
+With the setting &quot;separate-index-screen-always&quot; (the option above)
+when you view a particular thread you will be in the
+MESSAGE INDEX display and the index will only contain messages from
+the thread you are viewing.
+If the thread you are viewing consists of a single message, the MESSAGE INDEX
+will be an index with only one message in it.
+If you use this &quot;separate-index-screen-except-for-single-messages&quot;
+setting instead, then that index which contains a single message
+will be skipped and you will go directly from the THREAD INDEX into the
+MESSAGE TEXT screen.
+</DD>
+
+</DL>
+
+<P>
+
+<DT> <A NAME="threading-indicator-character"><EM>threading-indicator-character</EM></A>
+
+<DD> The Threading-Indicator-Character option has a small effect on the MESSAGE
+INDEX display when using a
+<A HREF="#threading-display-style"><EM>threading-display-style</EM></A>
+other than <EM>none</EM> and sorting by Threads or OrderedSubject.
+The value of this option is a single character.
+This character is used to indicate that part of a thread (a conversation) is
+hidden beneath a message.
+The message could be expanded
+if desired with the &quot;/&quot; Collapse/Expand command.
+By default, the value of this option is the greater than sign (&gt;).
+<P>
+If this option is set to the Empty Value, then the column (and the following
+blank column) will be deleted from the display.
+
+<P>
+This option is closely related to the
+<A HREF="#threading-expanded-character"><EM>threading-expanded-character</EM></A>
+option.
+Another similar option which affects the thread display is the
+<A HREF="#threading-lastreply-character"><EM>threading-lastreply-character</EM></A> option.
+<P>
+
+<DT> <A NAME="threading-lastreply-character"><EM>threading-lastreply-character</EM></A>
+
+<DD>The Threading-Lastreply-Character option has a small effect on the MESSAGE
+INDEX display when using a
+<A HREF="#threading-display-style"><EM>threading-display-style</EM></A>
+of <EM>show-thread-structure</EM>, <EM>mutt-like</EM>, or
+<EM>show-structure-in-from</EM>; and sorting by Threads or OrderedSubject.
+The value of this option is a single character.
+This character is used instead of the vertical line character when there are
+no more replies directly to the parent of the current message.
+It can be used to &quot;round-off&quot; the bottom of the vertical line
+by setting it to a character such as a backslash (&#92;) or
+a backquote (&#96;).
+The default value of this option is the backslash character (&#92;).
+This option may not be set to the Empty Value.
+In that case, the default will be used instead.
+<P>
+This option is displayed as &quot;Threading Last Reply Character&quot;.
+<P>
+
+<DT> <A NAME="title-background-color"><EM>title-background-color</EM></A>
+<DT> <A NAME="title-foreground-color"><EM>title-foreground-color</EM></A>
+
+<DD> <A HREF="#title-color"><EM>Title Color</EM></A>.
+<P>
+
+<DT> <A NAME="title-closed-background-color"><EM>title-closed-background-color</EM></A>
+<DT> <A NAME="title-closed-foreground-color"><EM>title-closed-foreground-color</EM></A>
+
+<DD> <A HREF="#title-closed-color"><EM>Title-closed Color</EM></A>.
+<P>
+
+<DT> <A NAME="titlebar-color-style"><EM>titlebar-color-style</EM></A>
+
+<DD> <A HREF="#titlebar-style"><EM>titlebar-color-style</EM></A>.
+<P>
+
+<DT> <A NAME="unknown-character-set"><EM>unknown-character-set</EM></A>
+
+<DD> A text message should either be made up of all US-ASCII characters
+or it should contain a charset label which tells the software which
+character set encoding to use to interpret the message.
+Sometimes a malformed message may be unlabeled but contain non-ascii text.
+This message is outside of the standards so any attempt to read it could fail.
+When <EM>Alpine</EM> attempts to read such a message it will try to interpret the
+text in the character set you specify here.
+For example, if you have correspondents who send you unlabeled messages that
+are usually made up of characters from the WINDOWS-1251 character set, setting
+this unknown-character-set to <CODE>WINDOWS-1251</CODE> will
+allow you to read those messages.
+Of course, if the unlabeled message is actually in some other character set,
+then you may see garbage on your screen.
+
+<P>
+In the Setup/Config screen you may choose from a list of all the
+character sets <EM>Alpine</EM> knows about by using the &quot;T&quot; ToCharsets command.
+<P>
+
+<DT> <A NAME="upload-command"><EM>upload-command</EM></A>
+
+<DD> This option affects the behavior of the Composer's <EM>^R</EM> (Read File)
+and <EM>^J</EM> (Attach File, in the header) commands. It
+specifies a Unix program name, and any necessary command line arguments,
+that <EM>Alpine</EM> can use to transfer files from your personal computer into
+messages that you are composing. <P>
+
+<DT> <A NAME="upload-command-prefix"><EM>upload-command-prefix</EM></A>
+
+<DD> This option is used in
+conjunction with the <EM>upload-command</EM> option.
+It defines text to be written to the terminal emulator (via standard
+output) immediately prior to starting the upload command. This is useful for
+integrated serial line file transfer agents that permit command passing
+(e.g., Kermit's APC method). <P>
+
+<DT> <A NAME="url-viewers"><EM>url-viewers</EM></A>
+
+<DD> List of programs to use to open Internet URLs.
+This value affects <EM>Alpine</EM>'s handling of URLs that are found in the text of
+messages you read. Normally, only URLs <EM>Alpine</EM> can handle directly are
+automatically offered for selection in the "Message Text" screen. When
+one or more comma delimited Web browsers capable of deciphering URLs on
+their command line are added here, <EM>Alpine</EM> will choose the first available
+browser to display URLs it doesn't recognize.
+<P>
+
+Additionally, to support various connection methods and browsers, each
+entry in this list can begin with the special token
+<CODE>_TEST(test-string)_</CODE>.
+The <CODE>test-string</CODE> is a shell command that <EM>Alpine</EM>
+will run and which must exit with a status of zero for <EM>Alpine</EM> to consider
+that browser for use (the other criteria is that the browser must exist
+as a full path or a path relative to your home directory).
+<P>
+
+Now for an example:
+
+<BLOCKQUOTE>
+ <CODE>
+ url-viewers=_TEST("test -n '$&#123;DISPLAY}'")_ /usr/local/bin/netscape,
+ /usr/local/bin/lynx,
+ C:&#92;BIN&#92;NETSCAPE.BAT </CODE><BR>
+</BLOCKQUOTE>
+
+This example shows that for the first browser in the list to be used the
+environment variable <CODE>DISPLAY</CODE> must be defined.
+If it is, then the file <CODE>/usr/local/bin/netscape</CODE> must exist.
+If either condition is not met, then the file
+<CODE>/usr/local/bin/lynx</CODE> must exist.
+If it doesn't, then the final path and file must
+exist. Note that the last entry is a DOS/Windows path. This is one way
+to support <EM>Alpine</EM> running on more than one architecture with the same
+configuration file.
+<P>
+
+<DT> <A NAME="use-only"><EM>use-only-domain-name</EM></A>
+
+<DD> Can be set to <EM>yes</EM> or <EM>no.</EM> Anything but
+<EM>yes</EM> means <EM>no.</EM> If set to <EM>yes</EM> the first label in
+the host name will be lopped off to get the domain name and the domain
+name will be used for outgoing mail and such. That is, if the host name
+is <EM>carson.u.example.edu</EM> and this variable is set to <EM>yes,</EM>
+then <EM>u.example.edu</EM> will be used on outgoing mail. Only
+meaningful if <A HREF="#user-domain"><EM>user-domain</EM></A> is NOT set. <P>
+
+<DT> <A NAME="user-domain"><EM>user-domain</EM></A>
+
+<DD> Sets the domain or host name for the user, overriding the system host
+or domain name. See the <A HREF="config-notes.html#domain"><EM>domain name section</EM></A>.
+The easiest way to change the full From address is with the
+<A HREF="#cust-hdr"><EM>customized-hdrs</EM></A> variable.
+<P>
+
+<DT> <A NAME="user-id"><EM>user-id</EM></A>
+
+<DD> <EM>PC-Alpine</EM> only and personal configuration file only.
+Sets the username that is placed on all outgoing
+messages. The username is the part of the address that comes before the "@".
+The easiest way to change the full From address is with the
+<A HREF="#cust-hdr"><EM>customized-hdrs</EM></A> variable.
+<P>
+
+<DT> <A NAME="user-input"><EM>user-input-timeout</EM></A>
+
+<DD> If this is set to an integer greater than zero, then this is the number
+of <EM>hours</EM> to wait for user input before <EM>Alpine</EM> times out.
+If <EM>Alpine</EM> is
+in the midst of composing a message or is waiting for user response to
+a question, then it will not timeout.
+However, if <EM>Alpine</EM> is sitting idle waiting for
+the user to tell it what to do next and the user does not give any
+input for this many hours, <EM>Alpine</EM> will exit.
+No expunging or moving of read
+messages will take place.
+It will exit similarly to the way it would exit
+if it received a hangup signal.
+This may be useful for cleaning up unused <EM>Alpine</EM> sessions which have been
+forgotten by their owners.
+The <EM>Alpine</EM> developers envision system administrators
+setting this to a value of several hours (24?) so that it won't surprise
+a user who didn't want to be disconnected.
+<P>
+
+<DT> <A NAME="viewer-hdr-colors"><EM>viewer-hdr-colors</EM></A>
+
+<DD> This variable holds the optional Header Colors and patterns which
+have been defined by the user. This is usually modified by using
+the <A HREF="#header-colors"><EM>Header Colors</EM></A> section
+of the Setup Color screen.
+<P>
+
+<DT> <A NAME="viewer-hdrs"><EM>viewer-hdrs</EM></A>
+
+<DD> You may change the default list of headers that are viewed by listing
+the headers you want to view here. If the headers in your <EM>viewer-hdrs</EM>
+list are present in the message, then they will be shown. The order of
+the headers you list will also be honored. If the special
+value <EM>all-except</EM>
+is included as the first header in the <EM>viewer-hdrs</EM> list, then all
+headers in the message except those in the list will be shown. The values
+are all case insensitive.<P>
+This option is displayed as &quot;Viewer Headers&quot;.
+<P>
+
+<DT> <A NAME="viewer-margin-left"><EM>viewer-margin-left</EM></A>
+
+<DD> This variable controls the left-hand vertical margin's width in
+<EM>Alpine</EM>'s Message Viewing screen.
+Its value is the number of space characters preceding each displayed line.
+For consistency with
+<A HREF="#viewer-margin-right">Viewer-Margin-Right</A>,
+you may specify the column number to start in
+(column numbering begins with number 1)
+instead of the width of the margin by appending a lower case letter
+&quot;c&quot; to the number.
+For example, a value of &quot;2c&quot; means to start the text in column two,
+which is entirely equivalent to a value of &quot;1&quot;, which means to
+leave a margin of 1 space.
+<P>
+The default is a left margin of 0 (zero).
+Misconfigurations (for example, negative values or values with starting
+left columns greater than the ending right column)
+are silently ignored.
+If the number of columns for text between the Viewer-Margin-Left and
+the Viewer-Margin-Right is fewer than 8, then margins of zero will be used
+instead.
+<P>
+
+<DT> <A NAME="viewer-margin-right"><EM>viewer-margin-right</EM></A>
+
+<DD> This variable controls the right-hand vertical margin's width in
+<EM>Alpine</EM>'s Message Viewing screen.
+Its value is the number of space characters following each displayed line.
+You may specify the column number to end the text in
+(column numbering begins with number 1)
+instead of the width of the margin by appending a lower case letter
+&quot;c&quot; to the number.
+For example, a value of &quot;76c&quot; means to end the text in column 76.
+If the screen is 80 characters wide, this is equivalent to a value
+of &quot;4&quot;, which means to leave a margin of 4 spaces.
+However, if you use different size screens at different times, then these
+two values are not equivalent.
+<P>
+The default right margin is 4.
+Misconfigurations (for example, negative values or values with starting
+left columns greater than the ending right column)
+are silently ignored.
+If the number of columns for text between the
+<A HREF="#viewer-margin-left">Viewer-Margin-Left</A> and
+the Viewer-Margin-Right is fewer than 8, then margins of zero will be used
+instead.
+<P>
+
+<DT> <A NAME="overlap"><EM>viewer-overlap</EM></A>
+
+<DD> This option specifies an aspect of <EM>Alpine</EM>'s Message Viewing screen.
+When the space bar is used to page forward in a message, the number of
+lines specified by the <EM>viewer-overlap</EM> variable
+will be repeated from the
+bottom of the screen. That is, if this was set to two lines, then the
+bottom two lines of the screen would be repeated on the top of the next
+screen. The normal default value is "2". <P>
+
+<DT> <A NAME="window-position"><EM>window-position</EM></A>
+
+<DD> Winsock version of <EM>PC-Alpine</EM> only. Window position in the format:
+CxR+X+Yn Where C and R are the window size in characters and X and Y are
+the screen position of the top left corner of the window. <P>
+
+</DL>
+
+<HR>
+
+<H2><A NAME="features-conf">Configuration Features</A></H2>
+
+There are several features (options) which may be turned off or on.
+The configuration variable
+<A HREF="#feat-list"><EM>feature-list</EM></A> is a list of all the
+features that are turned on or off.
+If the name of a feature is in
+the list it will be turned on.
+If the name of a feature with the characters
+<CODE>no-</CODE> prepended is in the list, it will turn the feature off.
+This is useful for overriding system-wide defaults.
+This is because, unlike all the other configuration variables,
+the <EM>feature-list</EM> is additive.
+That is, first the system-wide <EM>feature-list</EM> is read
+and then the user's <EM>feature-list</EM> is read. This makes it possible
+for the system manager to turn some of the features on by default while
+still allowing the user to cancel that default.
+For example, if the system manager has
+turned on the <EM>allow-talk</EM> feature by default then a user may turn
+it back off by including the feature <EM>no-allow-talk</EM> in his or her
+personal configuration file. Of course, these details are usually handled
+by <EM>Alpine</EM> when the user turns an option on or off from inside the
+<EM>Setup/Config</EM> screen.
+<P>
+
+System managers should take some care when turning on features by default.
+Some of the documentation assumes that all of the features are off by
+default, so it could be confusing for a user if some are on by default instead.
+Feature names are case-independent.
+<P>
+
+Here is an alphabetical list of possible features.
+
+<DL COMPACT>
+
+<DT> <A NAME="allow-from"><EM>allow-changing-from</EM></A>
+
+<DD> Prior to <EM>Pine</EM> 4.00 there was a <EM>compile</EM>-time option called
+ALLOW_CHANGING_FROM. That has been replaced by a <EM>runtime</EM> feature.
+If this feature is turned on then the From line can be changed just like
+all the other header fields that can be changed. See the configuration
+variables <A HREF="#cust-hdr"><EM>customized-hdrs</EM></A>
+and <A HREF="#def-comp"><EM>default-composer-hdrs</EM></A>
+for more information on editing headers.
+<P>
+The default value for this feature
+is ON, so that editing of From headers is allowed
+by default.
+<P>
+
+<DT> <A NAME="allow-talk"><EM>allow-talk</EM></A>
+
+<DD> Unix <EM>Alpine</EM> only. By default, permission for
+others to <EM>talk</EM> to your terminal is turned
+off when you are running <EM>Alpine</EM>. When this feature is set, permission is
+instead turned on.
+<P>
+
+Note: The <EM>talk</EM> program has nothing to do with <EM>Alpine</EM> or email. The
+<EM>talk</EM> daemon on your system will
+attempt to print a message on your screen
+when someone else is trying to contact you. If you wish to see these
+messages while you are running <EM>Alpine</EM>, you should enable this feature.
+<P>
+
+If you do enable this feature and see a <EM>talk</EM> message, you must
+suspend or quit <EM>Alpine</EM> before you can respond.
+<P>
+
+<DT> <A NAME="alternate-compose-menu"><EM>alternate-compose-menu</EM></A>
+
+<DD>This feature controls the menu that is displayed when Compose is selected.
+If set, a list of options will be presented, with each option representing
+the type of composition that could be used. This feature is most useful for
+users who want to avoid being prompted with each option separately, or who
+want to avoid the checking of remote postponed or form letter folders.
+The possible types of composition are:
+
+<P>
+New, for starting a new composition. Note that if New is selected and roles
+are set, roles are checked for matches and applied according to the setting
+of the matching role.
+
+<P>
+Interrupted, for continuing an interrupted composition. This option is only
+offered if an interrupted message folder is detected.
+
+<P>
+Postponed, for continuing postponed compositions. This option is offered
+if a postponed-folder is set in the config <EM>REGARDLESS OF</EM> whether or not
+the postponed folder actually exists. This option is especially handy
+for avoiding having to check for the existence of a remote postponed folder.
+
+<P>
+Form, for using form letters. This option is offered if the form-letter-folder
+is set in the config, and is not checked for existence for reasons similar
+to those explained by the postponed option.
+
+<P>
+setRole, for selecting a role to apply to a composition.
+
+<P>
+
+<DT> <A NAME="alternate-role-menu"><EM>alternate-role-menu</EM></A>
+
+<DD> Normally the Role Command allows you to choose
+a role and compose a new message using that role.
+When this feature is set, the role command will first ask whether you want to
+Compose a new message, Forward the current message, Reply to the
+current message, or Bounce the current message.
+If you are not in the MESSAGE INDEX and are not viewing a message,
+then there is no current message and the question will be skipped.
+After you have chosen to Compose, Forward, Reply or Bounce you will
+then choose the role to be used.
+<P>
+When Bouncing the &quot;Set From&quot; address is used for the
+Resent-From header, the &quot;Set Fcc&quot; value is used for the Fcc
+provided that the option
+<A HREF="#fcc-on-bounce">&quot;Fcc-On-Bounce&quot;</A> is turned on,
+and the &quot;Use SMTP Server&quot; value is used for the SMTP server, if
+set.
+Other actions of the role are ignored when Bouncing.
+<P>
+This feature is displayed as &quot;Alternate Role (#) Menu&quot;.
+<P>
+
+
+<DT> <A NAME="assume-slow-link"><EM>assume-slow-link</EM></A>
+
+<DD> UNIX <EM>Alpine</EM> only.
+<P>
+This feature affects <EM>Alpine</EM>'s display routines. If set, the normal
+inverse-video cursor (used to highlight the current item in a list) will be
+replaced by an <EM>arrow</EM> cursor and other screen update optimizations for
+low-speed links (e.g. 2400 bps dialup connections) will be activated.
+One of the optimizations is that colored index lines (set up with Indexcolor
+Rules) will not be colored.
+This might be useful if <I>you</I> know you have a slow speed link but for some
+reason <EM>Alpine</EM> doesn't know.
+<P>
+
+<DT> <A NAME="auto-read-msg"><EM>auto-move-read-msgs</EM></A>
+
+<DD> This feature controls an aspect
+of <EM>Alpine</EM>'s behavior upon quitting. If set,
+and the <A HREF="#read-msg-fold"><EM>read-message-folder</EM></A>
+variable is also set, then <EM>Alpine</EM> will
+automatically transfer all read messages from the <EM>INBOX</EM> to
+the designated folder and mark
+them as deleted in the <EM>INBOX</EM>. Messages in the <EM>INBOX</EM> marked
+with an <EM>N</EM> (meaning New, or unseen) are not affected.
+<P>
+This feature is displayed as &quot;Auto Move Read Messages&quot;.
+<P>
+
+<DT> <A NAME="auto-open-next-unread"><EM>auto-open-next-unread</EM></A>
+
+<DD> This feature controls the behavior of the TAB key when traversing folders
+in the optional <A HREF="#inc-fold"><EM>incoming-folders</EM></A>
+collection or in optional <A HREF="#news-coll"><EM>news-collections</EM></A>.
+<P>
+
+When the TAB (Next New) key is pressed, and there are no more unseen
+messages in the current (incoming message or news) folder, <EM>Alpine</EM> will
+search the list of folders in the current collection for one containing
+New or Recent (new since the last time the folder was opened) messages.
+This behavior may be modified slightly with the
+<A HREF="#tab-uses-unseen-for-next-folder">Tab-Uses-Unseen-For-Next-Folder</A>
+feature which causes <EM>Alpine</EM> to look for Unseen messages instead of Recent
+messages.
+By default, when such a folder is found,
+<EM>Alpine</EM> will ask whether you wish to
+open the folder.
+If this feature is set, <EM>Alpine</EM> will automatically open the
+folder without prompting.
+<P>
+
+<DT> <A NAME="auto-unselect-after-apply"><EM>auto-unselect-after-apply</EM></A>
+
+<DD> This feature affects the behavior of the Apply command. If set,
+the Apply command will do the operation you specify, but then will
+implicitly do an &quot;UnSelect All&quot;, so that you will automatically be back in
+the normal Index view after the Apply.
+<P>
+
+<DT> <A NAME="auto-unzoom-after-apply"><EM>auto-unzoom-after-apply</EM></A>
+
+<DD> If set, and if
+you are currently looking at a Zoomed Index view of selected messages,
+the <EM>Apply</EM> command will do the operation you specify, but then will
+implicitly do an <EM>UnZoom</EM>, so that you will automatically be back in
+the normal Index view after the <EM>Apply</EM>.
+This feature is set by default.
+<P>
+
+<DT> <A NAME="auto-zoom-after-select"><EM>auto-zoom-after-select</EM></A>
+
+<DD> If set, the <EM>; select</EM> command will automatically
+perform a <EM>Zoom</EM> after the <EM>select</EM> is complete.
+This feature is set by default.
+<P>
+
+<DT> <A NAME="busy-cue-spinner-only"><EM>busy-cue-spinner-only</EM></A>
+
+<DD> When <EM>Alpine</EM> is delayed for some reason it usually shows that
+something is happening with a small animated display in the status
+message line near the bottom of the screen.
+Setting this feature will cause that animation to be the same
+each time instead of having <EM>Alpine</EM> choose a random animation.
+You may turn the animation off altogether by setting the
+<A HREF="#busy-cue-rate"><EM>busy-cue-rate</EM></A>
+option to zero.
+<P>
+
+<DT> <A NAME="check-newmail-when-quitting"><EM>check-newmail-when-quitting</EM></A>
+
+<DD> If set, <EM>Alpine</EM> will check for new mail after you give the
+Quit command.
+If new mail has arrived since the previous check, you will be notified
+and given the choice of quitting or not quitting.
+<P>
+
+<DT> <A NAME="combined-addrbook-display"><EM>combined-addrbook-display</EM></A>
+
+<DD> This feature affects the address book display screens.
+Normally, expanding an address book from the ADDRESS BOOK LIST screen
+will cause the remaining address books and directory servers to disappear
+from the screen, leaving only the entries of the expanded address book.
+If this feature is set, then the other address books will remain on the screen,
+so that all of the address books can be present at once.
+
+<P>
+The way that commands work won't be changed.
+For example, the Select All command will select all of the entries in the
+current address book, not all of the entries in all of the address books.
+The WhereIs command will change a little.
+It will search through all of the text on the screen plus all of the entries
+from expanded address books.
+
+<P>
+When this feature is set, the setting of the feature
+<A HREF="#expanded-view-of-addressbooks"><EM>expanded-view-of-addressbooks</EM></A>
+has an effect.
+<P>
+This feature is displayed as &quot;Combined Addressbook Display&quot;.
+<P>
+
+<DT> <A NAME="combined-folder-display"><EM>combined-folder-display</EM></A>
+
+<DD> This feature affects the folder list display screens.
+Normally, each folder list is viewed within its collection only. This
+command allows folder lists to be viewed within a single screen that
+combines the contents of all collections.
+
+<P>
+The way that commands work won't be changed.
+For example, the Select All command will select all of the folders in the
+current collection, not all of the entries in all of the collections.
+The WhereIs command will change a little.
+It will search through all of the folders in the current collection as well
+as all the folder in any other expanded collection.
+
+<P>
+When this feature is set, the setting of the feature
+<A HREF="#expanded-view-of-folders"><EM>expanded-view-of-folders</EM></A>
+has an effect.
+<P>
+
+<DT> <A NAME="combined-subdirectory-display"><EM>combined-subdirectory-display</EM></A>
+
+<DD> This feature affects the Folder List screen when
+the
+<A HREF="#combined-folder-display"><EM>combined-folder-display</EM></A>
+feature is enabled. Normally, selecting a directory from the Folder
+List takes you into a new screen displaying only the contents of
+that directory.
+
+<P>
+Enabling this feature will cause the contents of the selected
+directory to be
+displayed within the boundaries of the
+<A HREF="background.html#collections"><EM>Collection</EM></A>
+it is a part of. All previously displayed collections will remain
+in the screen.
+
+<P>
+The way that commands work won't be changed.
+For example, the Select All command will select all of the folders in the
+directory, as opposed to all of the entries in all of the collections.
+The WhereIs command will change a little.
+It will search through all of the folders in the current collection as well
+as all the folder in any other expanded collection.
+
+<P>
+
+<DT> <A NAME="compose-cancel-confirm-uses-yes"><EM>compose-cancel-confirm-uses-yes</EM></A>
+
+<DD> This feature affects what happens when you type ^C to cancel a composition.
+By default, if you attempt to cancel a composition by typing ^C, you will be
+asked to confirm the cancellation by typing a &quot;C&quot;
+for <EM>C</EM>onfirm.
+It logically ought to be a &quot;Y&quot; for <EM>Y</EM>es, but that is
+risky because the &quot;^C Y&quot; needed to cancel a message
+is close (on the keyboard) to the &quot;^X Y&quot; needed to send a message.
+<P>
+If this feature is set the confirmation asked for
+will be a &quot;<EM>Y</EM>es&quot;
+instead of a &quot;<EM>C</EM>onfirm&quot; response.
+<P>
+
+<DT> <A NAME="compose-cut-from-cursor"><EM>compose-cut-from-cursor</EM></A>
+
+<DD> If set, the <EM>^K</EM> command in the composer will cut from the
+current cursor position to the end of the line,
+rather than cutting the entire line.
+<P>
+This feature is displayed as &quot;Ctrl-K Cuts From Cursor&quot;.
+<P>
+
+<DT> <A NAME="compose-maps-delete-key-to-ctrl-d"><EM>compose-maps-delete-key-to-ctrl-d</EM></A>
+
+<DD> If set, Delete will be equivalent to ^D, and delete
+the current character. Normally <EM>Alpine</EM> defines the Delete key
+to be equivalent to ^H, which deletes the <EM>previous</EM>
+character.
+<P>
+This feature is displayed as &quot;Delete Key Maps to Ctrl-D&quot;.
+<P>
+
+<DT> <A NAME="compose-rejects-unqualified-addrs"><EM>compose-rejects-unqualified-addrs</EM></A>
+
+<DD> If set, unqualified names entered as addresses will be treated as errors
+unless they match an addressbook nickname or are looked up successfully
+on an LDAP server. <EM>Alpine</EM> will not attempt to turn
+them into complete addresses by adding your local domain (which <EM>Alpine</EM> normally
+does by default).
+<P>
+
+A complete (fully-qualified) address is one containing a username
+followed by an <EM>@</EM> symbol, followed by a host or domain name (e.g.
+<EM>jsmith@example.com</EM>). An unqualified name is one without the
+<EM>@</EM> symbol
+and host or domain name (e.g. <EM>jsmith</EM>).
+<P>
+This feature is displayed as &quot;Compose Rejects Unqualified Addresses&quot;.
+<P>
+
+<DT> <A NAME="compose-send-offers-first-filter"><EM>compose-send-offers-first-filter</EM></A>
+
+<DD> If you have <A HREF="#sending-filters"><EM>sending-filters</EM></A>
+configured, setting this feature will cause the first filter in the
+<EM>sending-filters</EM> list to be offered as the default
+instead of <EM>unfiltered</EM>, the usual default.
+<P>
+
+<DT> <A NAME="compose-sets-newsgroup-without-confirm"><EM>compose-sets-newsgroup-without-confirm</EM></A>
+
+<DD> If you enter the
+composer while reading a newsgroup, you will normally be prompted to
+determine whether you intend the new message to be posted to the current
+newsgroup or not. If this feature is set, <EM>Alpine</EM> will not prompt you
+in this situation, and will assume that you do indeed wish to post
+to the newsgroup you are reading.
+<P>
+This feature is displayed as &quot;Compose Sets Newsgroup Without Confirming&quot;.
+<P>
+
+<DT> <A NAME="confirm-role-even-for-default"><EM>confirm-role-even-for-default</EM></A>
+
+<DD> If you have roles, when you Reply to or Forward a message, or Compose
+a new message, <EM>Alpine</EM>
+will search through your roles for one which matches.
+Normally, if no matches are found you will be placed into the composer
+with no opportunity to select a role.
+If this feature is set, then you will be asked to confirm that you don't
+want a role.
+This will give you the opportunity to select a role (with the ^T command).
+If you confirm no role with a Return, you will be placed in
+the composer with no role.
+You may also confirm with either an &quot;N&quot; or a &quot;Y&quot;.
+These behave the same as if you pressed the Return.
+(The &quot;N&quot; and &quot;Y&quot; answers are available because they
+match what you might type if there was a role match.)
+<P>
+If you are using the alternate form of the Compose command called
+&quot;Role&quot;, then all of your roles will be available to you,
+independent of the value of this feauture and of the values set for all of
+Reply Use, Forward Use, and Compose Use.
+<P>
+
+<DT> <A NAME="continue-tab-without-confirm"><EM>continue-tab-without-confirm</EM></A>
+
+<DD> Normally, when you use the TAB NextNew
+command and there is a problem checking a folder, you are asked
+whether you want to continue with the search in the following folder or not.
+This gives you a chance to stop the NextNew processing.
+<P>
+If this feature is set you will not be asked.
+It will be assumed that you want to continue.
+<P>
+This feature is displayed as &quot;Continue NextNew Without Confirming&quot;.
+<P>
+
+<DT> <A NAME="convert-dates-to-localtime"><EM>convert-dates-to-localtime</EM></A>
+
+<DD> Normally, the message dates that you see in the
+MESSAGE INDEX and MESSAGE VIEW are displayed in the timezone they were sent from.
+For example, if a message was sent to you from a few timezones to the east
+it might appear that it was sent from the future;
+or if it was sent from somewhere to the west it might appear
+as if it is from yesterday even though it was sent only a few minutes ago.
+If this feature is set an attempt will be made to convert the dates
+to your local timezone to be displayed.
+<P>
+Note that this does not affect the results of Select by Date or of
+anything else other than these displayed dates.
+When viewing the message you may look at the original unconverted value of the Date
+header by using the HdrMode Command.
+<P>
+
+<DT> <A NAME="copy-to-to-from"><EM>copy-to-address-to-from-if-it-is-us</EM></A>
+
+<DD> This feature affects the From address used when Replying to a message.
+It is probably only useful if you have some
+<A HREF="#alt-addresses">alt-addresses</A>
+defined.
+When enabled, it checks to see if any of the addresses in the To or Cc
+fields of the message you are replying to is one of your addresses.
+If it is, and there is only one of them, then that address is used as
+the From address in the message you are composing.
+In other words, you will be using a From address that is the same
+as the To address that was used to get the mail to you in the first place.
+
+<P>
+If a role is being used and it has a From address defined, that From address will
+be used rather than the one derived from this feature.
+<P>
+
+<DT> <A NAME="delete-skips-deleted"><EM>delete-skips-deleted</EM></A>
+
+<DD> If set, this
+feature will cause the <EM>Delete</EM> command to
+advance past other messages that
+are marked deleted. In other words, pressing <EM>D</EM> will both mark the
+current message deleted and advance to the next message that is not marked
+deleted.
+This feature is set by default.
+<P>
+
+<DT> <A NAME="disable-config-cmd"><EM>disable-config-cmd</EM></A>
+
+<DD> If set, the configuration
+screen <EM>Setup/Config</EM> will not be available at all.
+<P>
+
+<DT> <A NAME="disable-save-input-history"><EM>disable-save-input-history</EM></A>
+
+<DD> Many of the prompts that ask for input in the status line near the
+bottom of the screen will respond to Up Arrow and Down Arrow
+with the history of previous entries.
+For example, in the MESSAGE INDEX screen when you use the WhereIs
+command the text you entered will be remembered and can be recalled
+by using the Up Arrow key.
+Another example, when saving a message the folders saved to will
+be remembered and can be recalled using the arrow keys.
+<P>
+In the Save prompt, some users prefer that the Up and Down arrow keys
+be used for the Previous Collection and Next Collection commands
+instead of for a history of previous saves.
+If this option is set the Up and Down arrow keys will become synonyms for the
+Previous Collection and Next Collection (^P and ^N) commands in the
+prompt for the name of a folder to Save to or in the prompt for the
+name of a folder to GoTo.
+When this feature is not set (the default), ^P and ^N will change the
+collection and the arrow keys will show the history.
+<P>
+
+<DT> <A NAME="disable-kblock"><EM>disable-keyboard-lock-cmd</EM></A>
+
+<DD> In the Main <EM>Alpine</EM> menu there is a Keyboard locking
+command (<EM>KBLock</EM>). If this feature is set, that command won't be
+available to the user.
+<P>
+
+<DT> <A NAME="disable-keymenu"><EM>disable-keymenu</EM></A>
+
+<DD> If set, the command key menu that normally appears on the
+bottom two lines of the screen will not usually be there. Asking for
+help with <EM>^G</EM> or <EM>?</EM> will cause the key menu to
+appear instead of causing the help message to come up. If you want to
+actually see the help text,
+another <EM>^G</EM> or <EM>?</EM> will show it to you.
+After the key menu has popped
+up with the help key it will remain there for an <EM>O Other</EM> command but
+will disappear if any other command is typed.
+<P>
+
+<DT> <A NAME="disable-password-caching"><EM>disable-password-caching</EM></A>
+<P>
+
+<DD> Normally, loginname/password combinations are cached in <EM>Alpine</EM>
+so that the user does not have to enter the same password more than once
+in a session.
+A disadvantage to this approach is that the password must be stored in
+the memory image of the running <EM>Alpine</EM> in order that it can be reused.
+In the event that <EM>Alpine</EM> crashes and produces a core dump, and that core
+dump is readable by others, the loginname and password could possibly be read
+from the core dump.
+<P>
+If this feature is set, then the passwords will not be cached and
+the user will have to retype the password whenever <EM>Alpine</EM> needs it.
+Even with this feature set there is still some chance that the core
+file will contain a password, so care should be taken to make the
+core files unreadable.
+<P>
+NOTE: If PASSFILE caching is enabled, this does not disable it.
+That is a separate and independent feature.
+<P>
+
+<DT> <A NAME="disable-password-cmd"><EM>disable-password-cmd</EM></A>
+
+<DD> If set the <EM>Newpassword</EM> command usually available under the
+<EM>Setup</EM> command will not be available.
+<P>
+
+<DT> <A NAME="disable-pipes-in-sigs"><EM>disable-pipes-in-sigs</EM></A>
+
+<DD> If set it will be an error to append a vertical bar (|) to the name
+of a signature file.
+Appending a vertical bar normally causes the signature file to be executed
+to produce the signature.
+<P>
+
+<DT> <A NAME="disable-pipes-in-templates"><EM>disable-pipes-in-templates</EM></A>
+
+<DD> If set it will be an error to append a vertical bar (|) to the name
+of a template file.
+Appending a vertical bar normally causes the signature file to be executed
+to produce the signature.
+<P>
+
+<DT> <A NAME="disable-regex"><EM>disable-regular-expression-matching-for-alternate-addresses</EM></A>
+
+<DD> Normally, the
+<A HREF="#alt-addresses">alt-addresses</A>
+option is interpreted as a regular expression.
+One type of address that might cause trouble is an address that
+contains a plus sign.
+If you want to have an address with a plus as one of your
+alternate addresses
+and you don't want to use regular expressions, then setting this
+feature will cause <EM>Alpine</EM> to treat the addresses you list literally instead.
+<P>
+
+<DT> <A NAME="disable-roles-setup-cmd"><EM>disable-roles-setup-cmd</EM></A>
+
+<DD> If set the <EM>Roles</EM> command usually available under the
+<EM>Setup</EM> command will not be available.
+<P>
+
+<DT> <A NAME="disable-roles-sig-edit"><EM>disable-roles-sig-edit</EM></A>
+
+<DD> If set the roles editor in the <EM>Setup/Roles</EM> command will not allow
+editing of signature files with the F subcommand.
+<P>
+
+<DT> <A NAME="disable-roles-template-edit"><EM>disable-roles-template-edit</EM></A>
+
+<DD> If set the roles editor in the <EM>Setup/Roles</EM> command will not allow
+editing of template files with the F subcommand.
+<P>
+
+<DT> <A NAME="disable-sender"><EM>disable-sender</EM></A>
+
+<DD> If set, <EM>Alpine</EM> will not generate a &quot;Sender:&quot; or &quot;X-X-Sender&quot; header.
+This may be desirable on a system which is virtually hosting many domains,
+and the sysadmin has other methods available for tracking a message to
+its originator.
+<P>
+This feature is displayed as &quot;Do Not Generate Sender Header&quot;.
+<P>
+
+<DT> <A NAME="disable-setlocale-collate"><EM>disable-setlocale-collate</EM></A>
+
+<DD> This is a hard to understand feature that should only be used in rare cases.
+Normally, the C function call
+<P>
+<CENTER><SAMP>setlocale(LC_COLLATE, &quot;&quot;)</SAMP></CENTER>
+<P>
+is used by <EM>Alpine</EM>.
+If you want to try turning it off,
+setting this feature will turn it off.
+This part of the locale has to do with the sort order
+of characters in your locale.
+<P>
+
+<DT> <A NAME="disable-shared-namespaces"><EM>disable-shared-namespaces</EM></A>
+
+<DD> If this hidden feature is set
+the automatic search for namespaces &quot;ftp&quot;,
+&quot;imapshared&quot;, and &quot;imappublic&quot; by the underlying library
+will be disabled.
+The reason this feature exists is because there are some implementations
+of system password lookup routines which are very slow when presented with
+a long loginname which does not exist.
+This feature could be set to prevent the delay at startup time when the
+names above are searched for in the password file.
+<P>
+
+<DT> <A NAME="disable-signature-edit-cmd"><EM>disable-signature-edit-cmd</EM></A>
+
+<DD> If set the <EM>Signature</EM> editing command usually available under the
+<EM>Setup</EM> command will not be available.
+<P>
+
+<DT> <A NAME="disable-take-fullname"><EM>disable-take-fullname-in-addresses</EM></A>
+
+<DD> Normally, when TakeAddr is used to copy an address or addresses
+from a message into an address book entry, <EM>Alpine</EM> will try to preserve
+the full name associated with each address in the list of addresses.
+The reason for this is so that if the entry is a list or later becomes a
+list, then information about the individual addresses in the list
+is preserved.
+If you would rather just have the simple addresses in the list of addresses,
+set this feature. For example, with the default setting you might
+see something like this in the ADDRESS BOOK editor after you type TakeAddr
+<P>
+<PRE>
+ Nickname : nick
+ Fullname : Bedrock Elders
+ Fcc :
+ Comment :
+ Addresses : Fred Flintstone &lt;flint@bedrock.org&gt;,
+ Barney Rubble &lt;rubble@bedrock.org&gt;
+</PRE>
+<P>
+but with this feature set it would look like
+<P>
+<PRE>
+ Nickname : nick
+ Fullname : Bedrock Elders
+ Fcc :
+ Comment :
+ Addresses : flint@bedrock.org,
+ rubble@bedrock.org
+</PRE>
+<P>
+instead. Note the difference in the Addresses field.
+<P>
+
+<DT> <A NAME="disable-take-last-comma-first"><EM>disable-take-last-comma-first</EM></A>
+
+<DD> Normally, when <EM>TakeAddr</EM> is used to copy an address
+from a message into an address book, <EM>Alpine</EM> will attempt to rewrite the
+full name of the address in the form:
+
+<BLOCKQUOTE>
+ Last, First <BR>
+</BLOCKQUOTE>
+
+instead of
+
+<BLOCKQUOTE>
+ First Last <BR>
+</BLOCKQUOTE>
+
+It does this because many people find it useful to sort by Last name instead
+of First name. If this feature is set, then the <EM>TakeAddr</EM> command will
+not attempt to reverse the name in this manner.
+<P>
+
+<DT> <A NAME="disable-terminal-reset-for-display-filters"><EM>disable-terminal-reset-for-display-filters</EM></A>
+
+<DD> UNIX <EM>Alpine</EM> only.
+<P>
+This feature affects <EM>Alpine</EM>'s behavior when using
+<A HREF="#display-filters"><EM>Display-Filters</EM></A>.
+Normally, before the display filter is run, the terminal mode is reset
+to what it was before you started <EM>Alpine</EM>.
+This may be necessary if the filter requires the use of the terminal.
+For example, it may need to interact with you.
+If you set this feature, then the terminal mode will not be reset.
+One thing that turning on this feature should fix is the coloring of
+quoted text in the message view, which
+breaks because the terminal reset resets the color state of the terminal
+(<A HREF="#color-config">Color Configuration</A>).
+<P>
+
+<DT> <A NAME="downgrade-multipart-to-text"><EM>downgrade-multipart-to-text</EM></A>
+
+<DD> This feature affects <EM>Alpine</EM>'s behavior when sending mail. Internet
+standards require <EM>Alpine</EM> to translate all non-ASCII characters in
+messages that it sends using MIME encoding. This encoding can be
+ostensibly broken for recipients if any agent between <EM>Alpine</EM> and the
+recipient, such as an email list expander, appends text to the
+message, such as list information or advertising. When sending such
+messages <EM>Alpine</EM> attempts to protect such encoding by placing extra
+MIME boundaries around the message text.
+<P>
+These extra boundaries are invisible to recipients that
+use MIME-aware email programs (the vast majority). However, if
+you correspond with users of email programs that are not MIME-aware,
+or do not handle the extra boundaries gracefully, you can
+use this feature to prevent <EM>Alpine</EM> from including the extra
+MIME information. Of course, it will increase the likelihood
+that non-ASCII text you send may appear corrupt to the recipient.
+<P>
+
+<DT> <A NAME="enable-8bit-esmtp-negotiation"><EM>enable-8bit-esmtp-negotiation</EM></A>
+
+<DD> This feature affects <EM>Alpine</EM>'s behavior when sending mail.
+By default, this feature is set.
+Internet standards
+require that all electronic mail messages traversing the global Internet
+consist of 7bit ASCII characters unless a pair of cooperating mail
+transfer agents explicitly agree to allow 8bit messages. In general,
+then, exchanging messages in non-ASCII characters requires MIME encoding.
+<P>
+
+However, there are now Internet standards that allow for unencoded 8bit
+exchange of messages between cooperating systems. When this feature is set
+<EM>Alpine</EM> will try to negotiate unencoded 8bit transmission during the
+sending process. Should the negotiation fail, <EM>Alpine</EM> will fall back to its
+ordinary encoding rules.
+<P>
+
+Note, this feature relies on your system's mail transport agent or
+configured <A HREF="#smtp-server"><EM>smtp-server</EM></A>
+having the negotiation mechanism introduced in
+"Extended SMTP" (ESMTP) and the specific extension called <EM>8BITMIME</EM>.
+<P>
+
+<DT> <A NAME="enable-8bit-nntp-posting"><EM>enable-8bit-nntp-posting</EM></A>
+
+<DD> The Internet standard for exchanging USENET news messages (RFC-1036)
+specifies that USENET messages should conform to Internet mail standards
+and contain only 7bit characters, but much of the news transport software
+in use today is capable of successfully sending messages containing 8bit
+characters. Hence, many people believe that it is appropriate to send 8bit
+news messages without any MIME encoding.
+<P>
+
+Moreover, there is no Internet standard for explicitly negotiating 8bit
+transfer, as there is for Internet email. Therefore, <EM>Alpine</EM> provides the
+option of posting unencoded 8bit news messages, though not as the default.
+Setting this feature will turn OFF <EM>Alpine</EM>'s MIME encoding of newsgroup
+postings that contain 8bit characters.
+<P>
+
+Note, articles may cross a path or pass through news transport software
+that is unsafe or even hostile to 8bit characters. At best this will only
+cause the posting to become garbled. The safest way to transmit 8bit
+characters is to leave <EM>Alpine</EM>'s MIME encoding turned on, but recipients
+who lack MIME-aware tools are often annoyed when they receive MIME-encoded
+messages.
+<P>
+
+<DT> <A NAME="enable-aggregate-command-set"><EM>enable-aggregate-command-set</EM></A>
+
+<DD> When this feature is set you may use the commands and subcommands that relate to
+performing operations on more than one message at a time.
+We call these "aggregate operations".
+In particular, the <EM>; Select</EM>, <EM>A Apply</EM>, and
+<EM>Z Zoom</EM> commands are enabled by this feature.
+<EM>Select</EM> is used to <EM>tag</EM> one
+or more messages meeting the specified criteria. <EM>Apply</EM> can then be used
+to apply any message command to all of the selected/tagged messages.
+Further, the <EM>Zoom</EM> command allows you to toggle the "Folder Index" view
+between just those Selected and all messages in the folder.
+<P>
+
+This feature also enables the <EM>^X</EM> subcommand in
+the "Folder Index" <EM>WhereIs</EM>
+command which causes
+all messages matching the <EM>WhereIs</EM> argument to become
+selected.
+<P>
+
+You may also use aggregate operations in the address book screens where
+you are operating on address book entries instead of on messages.
+<P>
+
+<DT> <A NAME="enable-alt-ed"><EM>enable-alternate-editor-cmd</EM></A>
+
+<DD> If this feature is set (the default), and the <A HREF="#editor"><EM>editor</EM></A>
+variable is not set, entering
+the <EM>^_</EM> (Control-underscore) key while
+composing a message will prompt you
+for the name of the editor you would like to use.
+<P>
+
+If the environment variable <CODE>$EDITOR</CODE> is set,
+this value will be offered as a default.
+
+If the <EM>editor</EM> variable is set, the <EM>^_</EM> key will activate
+the specified editor without prompting, in which case it is not necessary to
+set the <EM>enable-alternate-editor-cmd</EM> feature.
+This feature is not available in <EM>PC-Alpine</EM>.
+<P>
+This feature is displayed as &quot;Enable Alternate Editor Command&quot;.
+<P>
+
+<DT> <A NAME="enable-alt-imp"><EM>enable-alternate-editor-implicitly</EM></A>
+
+<DD> If this feature and the <A HREF="#editor"><EM>editor</EM></A>
+variable are both set, <EM>Alpine</EM> will
+automatically activate the specified editor when the cursor is moved from
+the header of the message being composed into the message text. For
+replies, the alternate editor will be activated immediately. If this
+feature is set but the <EM>editor</EM> variable is not set,
+then <EM>Alpine</EM> will
+automatically ask for the name of an alternate editor when the cursor
+is moved out of the headers, or if a reply is being done.
+This feature is not available in <EM>PC-Alpine</EM>.
+<P>
+
+<DT> <A NAME="enable-arrow-navigation"><EM>enable-arrow-navigation</EM></A>
+
+<DD> This feature controls the behavior of the left and right arrow keys.
+If set, the left and right arrow keys will operate like the usual
+navigation keys <EM>&lt;</EM> and <EM>&gt;</EM>.
+This feature is set by default.
+
+<P>
+If you set this feature, and do not like the changed behavior of the up/down
+arrow keys when navigating through the FOLDER LIST screen --
+<B>first</B> from column to column, if more than one folder is
+displayed per row,
+and <B>then</B> from row to row -- you may either also wish to set the feature
+<A HREF="#enable-arrow-navigation-relaxed"><EM>enable-arrow-navigation-relaxed</EM></A>,
+<A HREF="#single-column-folder-list"><EM>single-column-folder-list</EM></A>,
+or use the ^P/^N (instead of up/down arrow) keys to move up/down the list of
+folders in each column.
+<P>
+
+<DT> <A NAME="enable-arrow-navigation-relaxed"><EM>enable-arrow-navigation-relaxed</EM></A>
+
+<DD> This feature controls the behavior of the left and right arrow keys
+in the FOLDER LIST screen when the
+<A HREF="#enable-arrow-navigation"><EM>enable-arrow-navigation</EM></A>
+feature is set.
+This feature is set by default.
+<P>
+
+When this feature is set, the left and right
+arrow keys in the FOLDER LIST screen
+move the highlight bar to the left or right, and the up and
+down arrows move it up or down.
+
+<P>
+When the &quot;Enable-Arrow-Navigation&quot; feature is set and this
+feature is not set;
+the left and right arrow keys in the Folder List screen strictly
+track the commands bound to the '&lt;' and '&gt;' keys, and the up
+and down arrow keys move the highlight bar to the previous and next
+folder or directory name.
+
+<P>
+
+<DT> <A NAME="enable-background-sending"><EM>enable-background-sending</EM></A>
+
+<DD> If set, this
+feature enables a subcommand in the composer's <EM>Send?</EM> confirmation
+prompt. The subcommand allows you to tell <EM>Alpine</EM> to handle the actual
+posting in the background. While this feature usually allows posting
+to appear to happen very fast, it has no affect on the actual delivery
+time it takes a message to arrive at its destination.
+<P>
+
+This feature isn't supported on all systems. All DOS and Windows,
+as well as several Unix ports, do not recognize this feature.
+It is not possible to use background sending if the feature
+<A HREF="#send-without-confirm">send-without-confirm</A> is set.
+<P>
+
+Error handling is significantly different when this feature is
+enabled. Any message posting failure results in the message
+being appended to your <EM>Interrupted</EM> mail folder. When you
+type the <EM>Compose</EM> command, <EM>Alpine</EM> will notice this folder and
+offer to extract any messages contained. Upon continuing a
+failed message, <EM>Alpine</EM> will display the nature of the failure
+in the status message line.
+<P>
+
+Under extreme conditions, it is possible for message data to
+get lost. Do not enable this feature if you typically run close
+to any sort of disk-space limits or quotas.
+<P>
+
+<DT> <A NAME="enable-bounce-cmd"><EM>enable-bounce-cmd</EM></A>
+
+<DD> Setting this feature enables the <EM>B Bounce</EM> command,
+which will prompt
+for an address and <EM>remail</EM> the message to the new recipient.
+This command
+is used to re-direct messages that you have received in error, or need to
+be redirected for some other reason (e.g. list moderation). The final
+recipient will see a header indicating that you have Resent the msg, but
+the message's From: header will show the original author of the message,
+and replies to it will go back to that author, and not to you.
+<P>
+This feature is displayed as &quot;Enable Bounce Command&quot;.
+<P>
+
+<DT> <A NAME="enable-cruise-mode"><EM>enable-cruise-mode</EM></A>
+
+<DD> This feature affects <EM>Alpine</EM>'s behavior when you hit the "Space Bar" at
+the end of a displayed message. Typically, <EM>Alpine</EM> complains that the end
+of the text has already been reached. Setting this feature causes such
+keystrokes to be interpreted as if the <EM>Tab</EM> key had been hit, thus
+taking you to the next <EM>interesting</EM> message, or scanning ahead to the
+next incoming folder with <EM>interesting</EM> messages.
+<P>
+
+<DT> <A NAME="enable-cruise-mode-delete"><EM>enable-cruise-mode-delete</EM></A>
+
+<DD> This feature modifies the behavior of <EM>Alpine</EM>'s <EM>enable-cruise-mode</EM>
+feature. Setting this feature causes <EM>Alpine</EM> to implicitly delete read
+messages when it moves on to display the next <EM>interesting</EM> message.
+<P>
+
+NOTE: Beware when enabling this feature <B>and</B> the
+<A HREF="#expunge-wo-confirm"><EM>expunge-without-confirm</EM></A> feature.
+<P>
+This feature is displayed as &quot;Enable Cruise Mode With Deleting&quot;.
+<P>
+
+<DT> <A NAME="enable-delivery-status-notification"><EM>enable-delivery-status-notification</EM></A>
+
+<DD> If set, this
+feature enables a subcommand in the composer's "Send?" confirmation
+prompt. The subcommand allows you to tell <EM>Alpine</EM> to request the type of
+Delivery Status Notification (DSN) which you would like. Most users will
+be happy with the default, and need not enable this feature. See the online
+help for more details.
+<P>
+It is not possible to use delivery status notifications if the feature
+<A HREF="#send-without-confirm">send-without-confirm</A> is set.
+<P>
+
+Note that this is not a method to request <EM>READ</EM> receipts, which tells
+the sender when the receiver has read the message. In this case we're talking
+about notification of delivery to the mailbox, not notification that the
+message has been seen.
+<P>
+
+<DT> <A NAME="enable-dot-files"><EM>enable-dot-files</EM></A>
+
+<DD> If set, files beginning with dot (".") will be
+visible in the file browser. For example, you'll be able to select them
+when using the browser to add an attachment to a message.
+<P>
+
+<DT> <A NAME="enable-dot-folders"><EM>enable-dot-folders</EM></A>
+
+<DD> If set, folders beginning with dot (".") may be added
+and viewed.
+This feature is displayed as &quot;Enable Hidden Folders&quot;.
+<P>
+
+<DT> <A NAME="enable-exit-via-lessthan-command"><EM>enable-exit-via-lessthan-command</EM></A>
+
+<DD> If set, then on screens where there is an <EM>Exit</EM> command
+but no <EM>&lt;</EM> command, the <EM>&lt;</EM> key will perform
+the same function as the <EM>Exit</EM> command.
+This feature is set by default.
+<P>
+
+<DT> <A NAME="enable-fast-recent-test"><EM>enable-fast-recent-test</EM></A>
+
+<DD> This feature controls the behavior of the TAB key when traversing folders
+in the optional
+<A HREF="#enable-incoming-folders">Incoming-Folders</A>
+collection or in optional News-Collections.
+
+<P>
+When the TAB
+(NextNew)
+key is pressed, the default behavior is to
+explicitly examine the status of the folder for the number of recent
+messages (messages delivered since the last time it was viewed).
+Depending on the size and number of messages in the folder, this test
+can be time consuming.
+
+<P>
+Enabling this feature will cause <EM>Alpine</EM> to only test for the existence of
+any recent messages rather than to obtain the count. This is much faster
+in many cases. The downside is that you're not given the number of recent
+messages when prompted to view the next folder.
+If the feature
+<A HREF="#tab-uses-unseen-for-next-folder">Tab-Uses-Unseen-For-Next-Folder</A>
+is turned on, then the present feature will have no effect.
+<P>
+
+<DT> <A NAME="enable-flag-cmd"><EM>enable-flag-cmd</EM></A>
+
+<DD> Setting this feature enables the <EM>* Flag</EM> command,
+which allows you to
+manipulate the status flags associated with a message.
+By default, <EM>Flag</EM>
+will set the <EM>Important</EM> flag, which results in an asterisk being
+displayed in column one of the "Folder Index" for such messages.
+<P>
+This feature is displayed as &quot;Enable Flag Command&quot;.
+<P>
+
+<DT> <A NAME="enable-flag-screen-implicitly"><EM>enable-flag-screen-implicitly</EM></A>
+
+<DD> This feature modifies the behavior of the <EM>* Flag</EM> command
+(provided it too is enabled).
+By default, when the <EM>* Flag</EM> command is selected,
+<EM>Alpine</EM> offers a prompt to set one of several flags and also offers the
+option of entering the detailed flag manipulation screen via the <EM>^T</EM>
+key. Enabling this feature causes <EM>Alpine</EM> to immediately enter the detailed
+flag screen rather than first offer the simple prompt.
+The
+<A HREF="#enable-flag-screen-keyword-shortcut">Enable-Flag-Screen-Keyword-Shortcut</A> option offers a slightly different way of setting keywords.
+<P>
+
+<DT> <A NAME="enable-flag-screen-keyword-shortcut"><EM>enable-flag-screen-keyword-shortcut</EM></A>
+
+<DD> This feature modifies the behavior of the
+Flag command and the Select command.
+By default, when the "* Flag" command is selected,
+<EM>Alpine</EM> offers a prompt to set one of several flags and also offers the
+option of entering the detailed flag manipulation screen via the "^T"
+key.
+If you have
+<A HREF="#keywords">keywords</A>
+defined, then enabling this feature adds a shortcut way to set or unset
+keywords.
+You use &quot;*&quot; followed by the first letter of a keyword (or the nickname of
+a keyword if you've given it a nickname) and that will set the keyword.
+<P>
+An example is easier to understand than the explanation.
+The flag command can always be used to set the system flags.
+For example, to set the Answered flag you would type
+<P>
+<CENTER><SAMP>* A</SAMP></CENTER>
+<P>
+Now suppose you have defined a keyword &quot;Work&quot; using the Keywords
+option in the Config screen.
+By default, to set a keyword like &quot;Work&quot; you would usually
+have to go to the Flag Details screen using
+the &quot;^T To Flag Details&quot; command.
+Instead, if you have enabled this feature, you may type
+<P>
+<CENTER><SAMP>* W</SAMP></CENTER>
+<P>
+to set the Work flag, or
+<P>
+<CENTER><SAMP>* ! W</SAMP></CENTER>
+<P>
+to unset it.
+Just like for the other flag setting commands, the case of the letter does
+not matter, so &quot;w&quot; or &quot;W&quot; both set the &quot;Work&quot;
+keyword.
+<P>
+Notice that you can only use this trick for one keyword that begins
+with &quot;W&quot;.
+If you happen to have a &quot;Work&quot; keyword and another keyword that is
+&quot;WIFI&quot; the &quot;* W&quot; command will set the first one in
+your list of keywords.
+Also, there are five letters which are reserved for system
+flags and the NOT command.
+If you type &quot;* A&quot; it will always set the Answered flag, not
+your &quot;Aardvark&quot; keyword.
+In order to set the &quot;Aardvark&quot; keyword you'll still have to use
+the Flag Details screen.
+<P>
+Because enabling the
+<A HREF="#enable-flag-screen-implicitly">Enable-Flag-Screen-Implicitly</A>
+option causes <EM>Alpine</EM> to skip directly to the Flag Details screen when the
+Flag command is used,
+setting it will cause this feature to have no effect at all.
+<P>
+Similarly, when Selecting by Keyword, setting this option will allow you
+to use Keyword initials instead of full keywords.
+<P>
+
+<DT> <A NAME="enable-full-header-cmd"><EM>enable-full-header-cmd</EM></A>
+
+<DD> This feature enables the <EM>H Full Headers</EM> command which
+toggles between
+the display of all headers in the message and the normal edited view of
+headers. The <EM>Full Header</EM> command also controls
+which headers are included
+for <EM>Export</EM>, <EM>Pipe</EM>, <EM>Print</EM>, <EM>Forward</EM>,
+and <EM>Reply</EM> functions. (For <EM>Reply</EM>, the
+<EM>Full Header</EM> mode will respect
+the <EM>include-headers-in-reply</EM> feature setting.)
+<P>
+If Full Header mode is turned on and you Forward a message, you will
+be asked if you'd like to forward the message as an attachment, as opposed
+to including the text of the message in the body of your new message.
+<P>
+If you have also turned on the
+<A HREF="#quote-suppression-threshold">&quot;Quote Suppression&quot;</A>
+option then the Full Headers command actually rotates through three states
+instead of just two.
+The first is the normal view with long quotes suppressed.
+The second is the normal view but with the long quotes included.
+The last enables the display of all headers in the message.
+When using Export, Pipe, Print, Forward, or Reply the quotes are
+never suppressed, so the first two states are identical.
+<P>
+Normally, the Header Mode will reset
+to the default behavior when moving to a new message.
+The mode can be made to persist from message to message by setting the feature
+<A HREF="#quell-full-header-auto-reset">Quell-Full-Header-Auto-Reset</A>.
+<P>
+This feature is displayed as &quot;Enable Full Header Command&quot;.
+<P>
+
+<DT> <A NAME="enable-full-header-and-text"><EM>enable-full-header-and-text</EM></A>
+
+<DD>
+<P>This feature affects how the <EM>H Full Headers</EM> command displays
+message text. If set, the raw message text will be displayed. This
+especially affects MIME formatted email, where the entire MIME format
+will be displayed. This feature similarly affects how messages are
+included for the <EM>Export</EM>, <EM>Pipe</EM>, <EM>Print</EM>, <EM>Forward</EM>,
+and <EM>Reply</EM> functions.
+
+<DT> <A NAME="enable-goto-in-file-browser"><EM>enable-goto-in-file-browser</EM></A>
+
+<DD> Setting this causes <EM>Alpine</EM> to offer the <EM>G Goto</EM> command in
+the file browser. The Goto command allows you to explicitly type in the
+desired directory.
+That is the default.
+<P>
+
+<DT> <A NAME="enable-incoming-folders"><EM>enable-incoming-folders</EM></A>
+
+<DD> If set, this feature defines a pseudo-folder collection called
+<EM>INCOMING MESSAGE FOLDERS</EM>.
+Initially, the only folder included in this collection
+will be your <EM>INBOX</EM>, which will no longer show up in your default
+saved-message folder collection.
+<P>
+This feature is displayed as &quot;Enable Incoming Folders Collection&quot;.
+<P>
+
+<DT> <A NAME="enable-incoming-folders-checking"><EM>enable-incoming-folders-checking</EM></A>
+
+<DD> This feature is only operational if you have enabled the optional
+<A HREF="#inc-fold"><EM>incoming-folders</EM></A>
+If you do have Incoming Message Folders and you also set this feature,
+then the number of Unseen messages in each folder will be displayed
+in the FOLDER LIST screen for the Incoming Message Folders.
+The number of Unseen messages in a folder will be displayed in parentheses
+to the right of the name of each folder.
+If there are no Unseen messages in a folder then only the name
+is displayed, not a set of parentheses with zero inside them.
+A redraw command, Ctrl-L, can be used in the FOLDER LIST screen for
+the Incoming Message Folders to cause an immediate update.
+<P>
+If a check for Unseen messages fails for a particular folder then Alpine
+will no longer attempt to check that folder for the duration of the
+session and this will be indicated by a question mark inside the
+parentheses.
+<P>
+The features
+<A HREF="#incoming-checking-includes-total"><EM>incoming-checking-includes-total</EM></A>,
+<A HREF="#incoming-checking-uses-recent"><EM>incoming-checking-uses-recent</EM></A>,
+<A HREF="#incoming-check-list"><EM>incoming-check-list</EM></A>,
+<A HREF="#incoming-check-interval"><EM>incoming-check-interval</EM></A>,
+<A HREF="#incoming-check-interval-secondary"><EM>incoming-check-interval-secondary</EM></A>, and
+<A HREF="#incoming-check-timeout"><EM>incoming-check-timeout</EM></A>
+all affect how this feature behaves.
+<P>
+
+<DT> <A NAME="disable-index-locale-dates"><EM>Disable-Index-Locale-Dates</EM></A>
+
+<DD> This feature affects the display of dates in the MESSAGE INDEX.
+Normally an attempt is made to localize the dates
+used in the MESSAGE INDEX display to your locale.
+This is controlled with the
+LC_TIME locale setting on a UNIX system.
+On Windows the Regional Options control panel may be used to set the date format.
+At the programming level, <EM>Alpine</EM> is using the strftime routine
+to print the parts of a date.
+<P>
+If this feature is set, dates are displayed in English and
+with the conventions of the United States.
+<P>
+
+<DT> <A NAME="enable-jump-shortcut"><EM>enable-jump-shortcut</EM></A>
+
+<DD> When this feature is set you may enter a number (followed by RETURN)
+and jump to that message number, when in the MESSAGE INDEX or MESSAGE TEXT
+screens. In other words, it obviates the need for typing the <EM>J</EM> for the
+<EM>Jump</EM> command.
+<P>
+
+<DT> <A NAME="enable-lame-list-mode"><EM>enable-lame-list-mode</EM></A>
+
+<DD> This feature modifies the method <EM>Alpine</EM> uses to ask your IMAP
+server for folder names to display in the the FOLDER LIST screen.
+It is intended to compensate for a small set of IMAP servers that
+are programmed to ignore a part of the request, and thus respond
+to <EM>Alpine</EM>'s query with nonsensical results.
+<P>
+
+If you find that <EM>Alpine</EM> is erroneously displaying blank folder lists,
+try enabling this feature.
+<P>
+
+NOTE: Enabling this feature has consequences for the Goto and Save
+commands. Many servers allow access to folders outside the area
+reserved for your personal folders via some reserved character,
+typically '#' (sharp), '~' (tilde) or '/' (slash). This mechanism
+allows, at the Goto and Save prompts, quick access to folders
+outside your personal folder collection without requiring a specific
+collection definition. This behavior will generally not be available
+when this feature is enabled.
+<P>
+This feature is displayed as &quot;Compensate for Deficient IMAP servers&quot;.
+<P>
+
+<DT> <A NAME="enable-mail-check-cue"><EM>enable-mail-check-cue</EM></A>
+
+<DD> If set, this will cause an asterisk to appear in the upper
+left-hand corner of the screen whenever <EM>Alpine</EM> checks for new mail, and two
+asterisks whenever <EM>Alpine</EM> saves (checkpoints) the state of the current
+mailbox to disk.
+<P>
+
+<DT> <A NAME="enable-mailcap-param-substitution"><EM>enable-mailcap-param-substitution</EM></A>
+
+<DD> If set, this will allow mailcap named parameter substitution to occur
+in mailcap entries.
+By default, this is turned off to prevent security problems which may occur
+with some incorrect mailcap configurations.
+For more information, RFC1524 and look for "named parameters" in the
+text of the RFC.
+<P>
+This feature is displayed as &quot;Enable Mailcap Parameter Substitution&quot;.
+<P>
+
+<DT> <A NAME="enable-mouse-in-xterm"><EM>enable-mouse-in-xterm</EM></A>
+
+<DD>
+This feature controls whether or not an X terminal mouse can be used with
+<EM>Alpine</EM>. If set, and the $DISPLAY variable indicates that an X terminal is
+being used, the left mouse button on the mouse can be used to select text
+or commands.
+Clicking on a command at the bottom of the screen will behave as if you had
+typed that command.
+Clicking on an index line will move the current message highlight to
+that line.
+Double-clicking on an index line will view the message.
+Double-clicking on a link will view the link.
+<P>
+This type of mouse support will also work in some terminal emulators which are
+not actually X terminals, but which have extra code to support the xterm
+style mouse.
+For those emulators you not only need to turn this feature on but you also
+have to set the $DISPLAY environment variable even though it isn't needed
+for your terminal.
+That will cause <EM>Alpine</EM> to think that it is an xterm and to properly interpret the
+escape sequences sent by the mouse.
+<P>
+Note: if this feature is set, the behavior of X terminal cut-and-paste is
+also modified. It is sometimes possible to hold the shift key down while clicking
+left or middle mouse buttons for the normal xterm cut/paste operations.
+There is also an <EM>Alpine</EM> command to toggle this mode on or off.
+The command is Ctrl-&#92; (Control-backslash).
+<P>
+
+<DT> <A NAME="enable-msg-view-addresses"><EM>enable-msg-view-addresses</EM></A>
+
+<DD> This feature modifies the behavior of <EM>Alpine</EM>'s "Message Text" screen.
+Setting this feature causes <EM>Alpine</EM> to select possible email addresses
+from the displayed text and display them in boldface for selection.
+<P>
+
+The first available email address is displayed in inverse. This is the
+"selected" address.
+Pressing <EM>RETURN</EM> will cause <EM>Alpine</EM> to enter the message
+composition screen with the To field filled in with the selected address.
+<P>
+
+Use the up and down arrow keys to change which of the addresses
+displayed in boldface is the current selection.
+<P>
+This feature is displayed as &quot;Enable Message View Address Links&quot;.
+<P>
+
+<DT> <A NAME="enable-msg-view-attachments"><EM>enable-msg-view-attachments</EM></A>
+
+<DD> This feature modifies the behavior of <EM>Alpine</EM>'s "Message Text" screen.
+Setting this feature causes <EM>Alpine</EM> to present attachments in boldface.
+The first available attachment is displayed in inverse. This is the
+"selected" attachment. Pressing <EM>RETURN</EM> will cause <EM>Alpine</EM> to display
+the selected attachment. Use the up and down arrow keys to change which of the
+attachments displayed in boldface is the current selection.
+<P>
+
+Speaking of arrow keys, the Up and Down Arrows will select the next
+and previous attachments if one is available on the screen for selection.
+Otherwise, they will simply adjust the viewed text one line up or down.
+<P>
+
+Similarly, when selectable items are present in a message, the Ctrl-F
+key can be used to select the next item in the message independent
+of which portion of the viewed message is currently displayed. The
+Ctrl-B key can be used to select the previous item in the same way.
+<P>
+This feature is displayed as &quot;Enable Message View Attachment Links&quot;.
+<P>
+
+<DT> <A NAME="enable-msg-view-forced-arrows"><EM>enable-msg-view-forced-arrows</EM></A>
+
+<DD> This feature modifies Up and Down arrow key behavior in <EM>Alpine</EM>'s
+"Message Text" screen when selectable Attachments, URL's, or
+web-hostnames are presented. <EM>Alpine</EM>'s usual behavior is to move to
+the next or previous selectable item if currently displayed or
+simply to adjust the screen view by one line if the next selectable line
+is off the screen.
+<P>
+
+Setting this feature causes the Up and Down arrow keys to behave as
+if no selectable items were present in the message.
+<P>
+
+Note, the <EM>Ctrl-F</EM> (next selectable item) and
+<EM>Ctrl-B</EM> (previous selectable item) functionality is unchanged.
+<P>
+This feature is displayed as &quot;Enable Message View Forced Arrows&quot;.
+<P>
+
+<DT> <A NAME="enable-msg-view-urls"><EM>enable-msg-view-urls</EM></A>
+
+<DD> This feature modifies the behavior of <EM>Alpine</EM>'s "Message Text" screen.
+When this feature is set (the default) <EM>Alpine</EM> will select possible URLs from the
+displayed text and display them in boldface for selection.
+<P>
+
+The first available URL is displayed in inverse. This is the
+"selected" URL. Pressing <EM>RETURN</EM> will cause <EM>Alpine</EM> to display
+the selected URL via either built-in means as with <CODE>mailto:</CODE>,
+<CODE>imap:</CODE>, <CODE>news:</CODE>, and <CODE>nntp:</CODE>,
+or via an external application as defined
+by the <A HREF="#url-viewers"><EM>url-viewers</EM></A> variable.
+<P>
+
+Use the up and down arrow keys to change which of the URLs displayed in boldface
+is the current selection.
+<P>
+This feature is displayed as &quot;Enable Message View URL Links&quot;.
+<P>
+
+<DT> <A NAME="enable-msg-view-web-hostnames"><EM>enable-msg-view-web-hostnames</EM></A>
+
+<DD> This feature modifies the behavior of <EM>Alpine</EM>'s "Message Text" screen.
+When this feature is set (the default) <EM>Alpine</EM> will select possible web hostnames
+from the displayed text and display them in boldface for selection.
+<P>
+
+The first available hostname is displayed in inverse. This is the
+"selected" hostname. Pressing <EM>RETURN</EM> will cause <EM>Alpine</EM> to display
+the selected hostname via an external application as defined
+by the <A HREF="#url-viewers"><EM>url-viewers</EM></A> variable.
+<P>
+
+Use the up and down arrow keys to change which of the hostnames displayed in
+boldface is the current selection.
+<P>
+This feature is displayed as &quot;Enable Message View Web Hostname Links&quot;.
+<P>
+
+<DT> <A NAME="enable-multiple-newsrcs"><EM>enable-multiple-newsrcs</EM></A>
+
+<DD> This feature makes it so <EM>Alpine</EM> can use multiple newsrcs based on
+the news server being connected to, which allows for separate lists
+of subscribed-to newsgroups. When this feature is not set, there is only
+one list of newsgroups.
+<P>
+Under this feature, the name of a newsrc is based on the news server.
+For example, if your <a href="#newsrc-path">newsrc-path</a>
+is set to &quot;.newsrc&quot;, and the news server you are connecting to is
+news.example.com, then the newsrc to be used is .newsrc-news.example.com.
+Setting this feature for the first time will allow for the option of using
+your old newsrc the next time you read news.
+<P>
+If this feature is set, then the feature
+<A HREF="#mult-newsrc-hostnames-as-typed">Mult-Newsrc-Hostnames-As-Typed</A>
+also may affect the name of the newsrc file that is used.
+
+<P>
+
+<DT> <A NAME="enable-newmail-in-xterm-icon"><EM>enable-newmail-in-xterm-icon</EM></A>
+
+<DD> This feature controls whether or not <EM>Alpine</EM> will attempt to announce new
+mail arrival when it is running in an X terminal window and that window
+is iconified.
+If set, and the <CODE>$DISPLAY</CODE> variable indicates that an X
+terminal is being used, <EM>Alpine</EM> will send appropriate escape sequences to
+the X terminal to modify the label on <EM>Alpine</EM>'s icon to indicate that new
+mail has arrived. <EM>Alpine</EM> will also modify the <EM>Alpine</EM> window's title to
+indicate new mail.
+See also <A HREF="#enable-newmail-short-text-in-icon">Enable-Newmail-Short-Text-in-Icon</A>.
+<P>
+
+<DT> <A NAME="enable-newmail-short-text-in-icon"><EM>enable-newmail-short-text-in-icon</EM></A>
+
+<DD> This feature controls the text to be displayed in an icon in the event
+of a new message arrival. Normally, the message will
+be the one that is displayed on the screen. This feature shortens the
+message to a count of the number of new messages in brackets. This may be
+more useful for those who use the window's title bar in the task bar as a
+new mail indicator. This feature is only useful if the
+<A HREF="#enable-newmail-in-xterm-icon">Enable-Newmail-in-Xterm-Icon</A>
+is also set. Like the Enable-Newmail-in-Xterm-Icon
+feature, this feature is only relevant when run in an xterm environment.
+<P>
+
+<DT> <A NAME="enable-partial-match-lists"><EM>enable-partial-match-lists</EM></A>
+
+<DD> This feature affects the subcommands available when <EM>Sav</EM>ing
+or Opening a new folder. If set, the subcommand <EM>^X ListMatches</EM> will be
+available. This command allows you to type in a substring of the folder
+you are looking for and when you type <EM>^X</EM> it will display all folders
+which contain that substring in their names.
+This feature is set by default.
+<P>
+
+<DT> <A NAME="enable-print-via-y-command"><EM>enable-print-via-y-command</EM></A>
+
+<DD> By default, <EM>Alpine</EM>'s print command is available by pressing the <EM>%</EM>
+key. In older versions of <EM>Pine</EM>, the print command was accessed by
+pressing the <EM>Y</EM> key.
+<P>
+
+Enabling this feature will cause <EM>Alpine</EM> to recognize both the old
+command, <EM>Y</EM>, and the new <EM>%</EM> method for invoking
+printing. Note, key menu labels are not changed as a result of
+enabling this feature.
+<P>
+
+<DT> <A NAME="enable-reply-indent-string-editing"><EM>enable-reply-indent-string-editing</EM></A>
+
+<DD> This feature affects the Reply command's &quot;Include original message
+in Reply?&quot; prompt. When enabled, it causes the
+&quot;Edit Indent String&quot; sub-command to appear which allows
+you to edit the string <EM>Alpine</EM> would otherwise use to denote included
+text from the message being replied to.<P>
+
+Thus, you can change <EM>Alpine</EM>'s default message quote character (usually
+an angle bracket) on a per message basis. So you could change your quoted message to
+look, for example, like this:<p>
+
+<pre>On Tues, 26 Jan 1999, John Q. Smith wrote:
+
+John: I just wanted to say hello and to congratulate you
+John: on a job well done!</pre><p>
+
+The configuration option
+<A HREF="#reply-ind-str">&quot;reply-indent-string&quot;</A>
+may be used to change what appears as the default string to be edited.
+<P>
+NOTE: Edited reply-indent-strings only apply to the message
+currently being replied to.
+<P>
+
+<DT> <A NAME="enable-rules-under-take"><EM>enable-rules-under-take</EM></A>
+
+<DD> Normally, the Take command takes addresses from a message and helps you
+put them into your Address Book.
+If you use Rules for Indexcolors, Roles, Filtering, or Scoring;
+you may find it useful
+to be able to Take information from a message's headers and put it into
+a new Rule.
+When this feature is set, you will be given an extra prompt which gives
+you the choice to Take into the Address Book or Take into a rule.
+<P>
+This feature is displayed as &quot;Enable Take Rules&quot;.
+<P>
+
+<DT> <A NAME="enable-search-and-replace"><EM>enable-search-and-replace</EM></A>
+
+<DD> If set <EM>Alpine</EM>'s composer offers the <EM>R Replace</EM> command
+option inside the <EM>W WhereIs</EM> command.
+<P>
+
+<DT> <A NAME="enable-sigdashes"><EM>enable-sigdashes</EM></A>
+
+<DD> If set and a <EM>signature-file</EM> exists, the line consisting of
+the three characters "<CODE>-- </CODE>" (dash dash space) is included
+before the signature.
+This only happens if the signature doesn't already contain such a line.
+<P>
+In addition, when you Reply or Followup to a message containing one of
+these special lines and choose to include its text, <EM>Alpine</EM> will observe
+the convention of not including text beyond the special line in your
+reply.
+<P>
+
+<DT> <A NAME="enable-suspend"><EM>enable-suspend</EM></A>
+
+<DD> Setting this feature will allow you to type <EM>^Z</EM>
+and temporarily suspend <EM>Alpine</EM>. Not available on <EM>PC-Alpine</EM>.
+<P>
+
+<DT> <A NAME="enable-tab-completion"><EM>enable-tab-completion</EM></A>
+
+<DD> This feature enables the <EM>TAB</EM> key when
+at a prompt for a filename. In this
+case, <EM>TAB</EM> will cause the
+partial name already entered to be automatically
+completed, provided the partial name is unambiguous.
+This feature is set by default.
+<P>
+Similarly, this feature also enables TAB completion of address book
+nicknames when at a prompt for a nickname,
+or when typing in an address field in the composer.
+<P>
+
+<DT> <A NAME="enable-take-export"><EM>enable-take-export</EM></A>
+
+<DD> Normally, the Take command takes addresses from a message and helps you
+put them into your Address Book.
+When this feature is set, you will be given an extra prompt which gives you
+the choice to Take addresses into a file instead of your Address
+Book.
+Only the user@domain_name part of the address is put in the file.
+<P>
+
+<DT> <A NAME="enable-tray-icon"><EM>enable-tray-icon</EM></A>
+
+<DD> <EM>PC-Alpine</EM> only. This option restores a behavior of
+previous versions of PC-Alpine. These
+versions, when started, installed a PC-Alpine icon in the notification
+tray of Window's Taskbar. The primary use of this icon was to indicate
+new mail arrival by turning red (while the Taskbar icon remained green).
+Additionally, the icon now changes to yellow to signify that a mail folder
+has been closed unexpectedly.
+<P>
+Rather than add another icon to the Taskbar, this version of PC-Alpine will
+color its Taskbar entry's icon red (as well as the icon in the Window
+Title). This feature is only provided for backwards compatibility.
+<P>
+
+<DT> <A NAME="enable-unix-pipe-cmd"><EM>enable-unix-pipe-cmd</EM></A>
+
+<DD> This feature enables the <EM>| Pipe</EM> command
+that sends the current message
+to the specified Unix command for external processing.
+<P>
+This feature is displayed as &quot;Enable Unix Pipe Command&quot;.
+<P>
+
+<DT> <A NAME="enable-verbose-smtp-posting"><EM>enable-verbose-smtp-posting</EM></A>
+
+<DD> This feature controls an aspect of <EM>Alpine</EM>'s message sending. When enabled,
+<EM>Alpine</EM> will send a <CODE>VERB</CODE> (i.e., VERBose) command
+early in the posting process
+intended to cause the server SMTP to provide a more detailed account of
+the transaction. This feature is typically only useful to system
+administrators and other support personel as an aid in troublshooting
+problems.
+
+Note, this feature relies on a specific capability of the system's mail
+transport agent or configured <A HREF="#smtp-server"><EM>smtp-server</EM></A>.
+<P>
+
+<DT> <A NAME="expanded-view-of-addressbooks"><EM>expanded-view-of-addressbooks</EM></A>
+
+<DD> If multiple address books (either personal or global) are defined, and you
+wish to have them all expanded implicitly upon entering the ADDRESS BOOK
+screen, then set this feature. This feature will have no effect unless the
+feature
+<A HREF="#combined-addrbook-display"><EM>combined-addrbook-display</EM></A>
+is also set.
+<P>
+
+<DT> <A NAME="expanded-view-of-distribution-lists"><EM>expanded-view-of-distribution-lists</EM></A>
+
+<DD> If this feature is set, then distribution lists in the address book
+screen will always be expanded automatically.
+<P>
+
+<DT> <A NAME="expanded-view-of-folders"><EM>expanded-view-of-folders</EM></A>
+
+<DD> If multiple folder collections are defined, and you
+wish to have them all expanded implicitly upon entering the FOLDER LIST
+screen, then set this feature. This feature will have no effect unless the
+feature
+<A HREF="#combined-folder-display"><EM>combined-folder-display</EM></A>
+is also set.
+<P>
+
+<DT> <A NAME="expose-hidden-config"><EM>expose-hidden-config</EM></A>
+
+<DD> The purpose of this feature is to allow you to change configuration
+features and variables which are normally hidden.
+This is particularly useful if you are using a remote configuration file,
+where it is difficult to edit the file manually, but it may also be used
+on a local pinerc configuration file.
+<P>
+If set, most configuration variables and features which are normally
+hidden from view will show up in the Setup/Configuration screen.
+They will be at the bottom of the configuration screen.
+You can find them by searching for the word &quot;hidden&quot;.
+<P>
+
+Note that this is an advanced feature which should be used with care.
+The reason that this part of the configuration is normally hidden is because
+there is a significant potential for causing problems if you change these
+variables.
+If something breaks after a change try changing it back to see if that is
+what is causing the problem.
+There are also some variables which are normally hidden because they are
+manipulated through <EM>Alpine</EM> in other ways.
+For example, the &quot;address-book&quot; variable is normally set using
+the Setup/AddressBooks screen, so there is little reason to edit it directly.
+The &quot;incoming-folders&quot; variable is normally changed by using
+the Add, Delete, and Rename commands in the FOLDER LIST screen,
+and the &quot;last-time-prune-questioned&quot; variable is normally used
+internally by <EM>Alpine</EM> and not set directly by the user.
+<P>
+
+<DT> <A NAME="expunge-only-manually"><EM>expunge-only-manually</EM></A>
+
+<DD> Normally, when you close a folder which contains deleted messages you are
+asked if you want to expunge those messages from the folder permanently.
+If this feature is set, you won't be asked and the deleted messages will
+remain in the folder.
+If you choose to set this feature you will have to expunge the
+messages manually using the eXpunge command, which you can use while
+in the MESSAGE INDEX screen.
+If you do not expunge deleted messages the size of your
+folder will continue to increase until you are out of disk space.
+<P>
+
+<DT> <A NAME="expunge-wo-confirm"><EM>expunge-without-confirm</EM></A>
+
+<DD> If set, you will not be prompted to confirm your intent before
+the expunge takes place. Actually, you will still be prompted for confirmation
+if the folder is not the <EM>INBOX</EM> folder or another folder in the
+Incoming Folders collection. See the <EM>expunge-without-confirm-everywhere</EM>
+feature which follows.
+<P>
+This feature is displayed as &quot;Expunge Without Confirming&quot;.
+<P>
+
+<DT> <A NAME="expunge-without-confirm-everywhere"><EM>expunge-without-confirm-everywhere</EM></A>
+
+<DD> The regular <EM>expunge-without-confirm</EM> feature actually only
+works for the <EM>INBOX</EM> folder and for other folders in the "Incoming
+Folders" collection. If this feature is set then you also won't be prompted
+to confirm expunges for all other folders.
+<P>
+This feature is displayed as &quot;Expunge Without Confirming Everywhere&quot;.
+<P>
+
+<DT> <A NAME="fcc-on-bounce"><EM>fcc-on-bounce</EM></A>
+
+<DD> If set, normal Fcc (File Carbon Copy) processing will be
+done for bounced messages,
+just as if you had composed a message to the address you are
+bouncing to. If not set, no Fcc of the message will be saved.
+<P>
+This feature is displayed as &quot;Include Fcc When Bouncing Messages&quot;.
+<P>
+
+<DT> <A NAME="fcc-only-without-confirm"><EM>fcc-only-without-confirm</EM></A>
+
+<DD> This features controls an aspect of <EM>Alpine</EM>'s composer.
+The only time this feature will be used is if you attempt to send mail
+which has no recipients but does have an Fcc.
+Normally, <EM>Alpine</EM> will ask if you really mean to copy the message only to
+the Fcc.
+That is, it asks if you really meant to have no recipients.
+If this feature is set, you
+will <B>not</B> be prompted to confirm your intent to make only a copy
+of a message with no recipients.
+<P>
+This feature is closely related to
+<A HREF="#warn-if-blank-to-and-cc-and-newsgroups"><EM>warn-if-blank-to-and-cc-and-newsgroups</EM></A>.
+The difference between this feature and that feature is that this feature
+considers a Bcc to be a recipient while that feature will ask for confirmation
+even if there is a Bcc when there is no To, Cc, or Newsgroup.
+The default values also differ. This feature defaults to asking the question
+and you have to turn it off.
+The warn-if-blank-to-and-cc-and-newsgroups feature defaults to not asking
+unless you turn it on.
+<P>
+This feature is displayed as &quot;Send to Fcc Only Without Confirming&quot;.
+<P>
+
+<DT> <A NAME="fcc-without-attachments"><EM>fcc-without-attachments</EM></A>
+
+<DD> This features controls the way FCC's (File Carbon Copies) are
+made of the messages you send.
+
+<P>
+Normally, <EM>Alpine</EM> saves an exact copy of your message as it was sent.
+When this feature is enabled, the &quot;body&quot; of the message
+you send (the text you type in the composer) is preserved in the
+copy as before, however all attachments are replaced with text
+explaining what had been sent rather than the attachments themselves.
+
+<P>
+This feature also affects <EM>Alpine</EM>'s &quot;Send ?&quot; confirmation prompt
+in that a new &quot;^F Fcc Attchmnts&quot; option becomes available which
+allows you to interactively set whether or not attachments are saved
+to the Fcc'd copy.
+<P>
+This feature is displayed as &quot;Fcc Does Not Include Attachments&quot;.
+<P>
+
+<DT> <A NAME="force-arrow-cursor"><EM>force-arrow-cursor</EM></A>
+
+<DD> This feature affects <EM>Alpine</EM>'s MESSAGE INDEX display routine.
+If set, the normal inverse-video cursor will be
+replaced by a simple &quot;arrow&quot; cursor, which normally occupies the
+second column of the index display.
+<P>
+This is the same index cursor you get if you turn on
+<A HREF="#assume-slow-link">Assume-Slow-Link</A>, but the index
+line coloring will still be present if this feature is turned on and
+Assume-Slow-Link is off.
+<P>
+An alternative version of the Arrow cursor is available by including the
+ARROW
+token in the
+<A HREF="#index-format"><EM>Index-Format</EM></A> option.
+<P>
+It ought to be the case that this feature also affects the ATTACHMENT INDEX,
+but that is not implemented.
+<P>
+
+<DT> <A NAME="hide-nntp-path"><EM>hide-nntp-path</EM></A>
+
+<DD> Normally the Path header that <EM>Alpine</EM> generates when posting to a newsgroup
+contains the name of the computer from which the message is being sent and
+the user name.
+Some believe that this information is used by spammers.
+If this feature is set, that information will be replaced with the text
+<P>
+<CENTER><SAMP>not-for-mail</SAMP></CENTER>
+<P>
+instead.
+<P>
+It should be noted that many servers being connected to will still reveal
+the information that this feature attempts to protect.
+<P>
+<DT> <A NAME="include-attachments-in-reply"><EM>include-attachments-in-reply</EM></A>
+
+<DD> If set, any MIME
+attachments that were part of the original message will automatically be
+included in a <EM>Reply</EM>.
+<P>
+
+<DT> <A NAME="include-header-in-reply"><EM>include-header-in-reply</EM></A>
+
+<DD> If set, and a
+message being replied to is included in the <EM>Reply</EM>,
+then headers from that
+message will also be part of the reply.
+<P>
+
+<DT> <A NAME="include-text-in-reply"><EM>include-text-in-reply</EM></A>
+
+<DD> Normally, <EM>Alpine</EM> will ask whether you
+wish to include the original message in your <EM>Reply</EM>.
+If this feature is set and the feature
+<A HREF="#enable-reply-indent-string-editing"><EM>enable-reply-indent-string-editing</EM></A>
+is <EM>not</EM> set, then the original message will be included in the reply
+automatically, without prompting.
+<P>
+
+<DT> <A NAME="incoming-checking-includes-total"><EM>incoming-checking-includes-total</EM></A>
+
+<DD> This option has no effect unless the feature
+<A HREF="#enable-incoming-folders-checking"><EM>enable-incoming-folders-checking</EM></A>
+is set, which in turn has no effect unless
+<A HREF="#inc-fold"><EM>incoming-folders</EM></A>
+is set.
+<P>
+When incoming folder checking is turned on the default is to display
+the number of unseen messages in each folder.
+More precisely, it is the number of undeleted unseen messages.
+Using this option you may also display the total number of messages
+in each folder.
+Instead of a single number representing the number of unseen messages
+you will get two numbers separated by a slash character.
+The first is the number of unseen messages and the second is the
+total number of messages.
+<P>
+You may also use the recent message count instead of the unseen message
+count by turning on the feature
+<A HREF="#incoming-checking-uses-recent"><EM>incoming-checking-uses-recent</EM></A>.
+<P>
+
+<DT> <A NAME="incoming-checking-uses-recent"><EM>incoming-checking-uses-recent</EM></A>
+
+<DD> This option has no effect unless the feature
+<A HREF="#enable-incoming-folders-checking"><EM>enable-incoming-folders-checking</EM></A>
+is set, which in turn has no effect unless
+<A HREF="#inc-fold"><EM>incoming-folders</EM></A>
+is set.
+<P>
+When incoming folder checking is turned on the default is to display
+the number of unseen messages in each folder.
+More precisely, it is the number of undeleted unseen messages.
+Using this option you may display the number of recent messages instead
+of the number of unseen messages.
+A message is only counted as recent if this is the first session to
+see it, so the recent count might be less than the unseen count.
+The difference between the two would be accounted for by the unseen messages
+in the folder which were there previously but have not been looked at yet.
+<P>
+If you simultaneously run more than one email client at a time
+(for example, you run more than one <EM>Alpine</EM> in parallel) then turning
+this feature on can cause some confusion.
+The confusion stems from the fact that each message is only considered to be
+recent in one session.
+That means that the counts of new messages may be different in the two
+<EM>Alpine</EM>s running side by side, because each incoming message will only be
+counted as recent in one of the two sessions.
+<P>
+You may also display the total number of messages
+in each folder by using the
+<A HREF="#incoming-checking-includes-total"><EM>incoming-checking-includes-total</EM></A>
+option.
+<P>
+
+<DT> <A NAME="ldap-result-to-addrbook-add"><EM>ldap-result-to-addrbook-add</EM></A>
+
+<DD> This is only available if <EM>Alpine</EM> was linked with an LDAP library
+when it was compiled.
+If both the per-directory-server option
+<A HREF="#use-implicitly-from-composer"><EM>use-implicitly-from-composer</EM></A>
+and this feature are set,
+then when an implicit directory lookup is done from the
+composer you will automatically be prompted to add the result of the
+directory lookup to your address book.
+<P>
+This feature is displayed as &quot;LDAP Result to Addressbook Add&quot;.
+<P>
+
+<DT> <A NAME="maildrops-preserve-state"><EM>maildrops-preserve-state</EM></A>
+
+<DD>
+This feature affects the way
+<A HREF="config-notes.html#maildrop">Mail Drops</A> work.
+Normally, when mail is moved from a Mail Drop folder to a destination
+folder, the state changes that have taken place since the mail was originally
+delivered are lost.
+Any Seen/New, Answered, Important/Flagged state that has changed will be
+ignored.
+All of the mail will be considered unSeen, unAnswered, and unImportant after
+it is moved.
+<P>
+If this feature is set, then the state changes will not be lost.
+<P>
+In any case, messages which are already marked Deleted when the
+mail is to be copied from the Mail Drop will be ignored.
+<P>
+
+<DT> <A NAME="mark-fcc-seen"><EM>mark-fcc-seen</EM></A>
+
+<DD> This features controls the way FCCs (File Carbon Copies) are
+made of the messages you send.
+Normally, when <EM>Alpine</EM> saves a copy of a message you sent as an Fcc, that
+copy will be marked as Unseen.
+When you look at the folder it was saved in the message will appear to
+be a New message until you read it.
+When this feature is enabled, the message will be marked as having
+been Seen.
+<P>
+
+<DT> <A NAME="mark-for-cc"><EM>mark-for-cc</EM></A>
+
+<DD> This feature affects <EM>Alpine</EM>'s MESSAGE INDEX display.
+By default, a '+' is displayed in the first column if the
+message is addressed directly to you.
+When this feature is set and the message is not addressed to you, then a
+'-' character is displayed if the message is instead Cc'd directly
+to you.
+<P>
+
+<DT> <A NAME="mult-newsrc-hostnames-as-typed"><EM>mult-newsrc-hostnames-as-typed</EM></A>
+
+<DD> This feature will be of little use to most users.
+It has no effect unless the feature
+<A HREF="#enable-multiple-newsrcs">Enable-Multiple-Newsrcs</A>
+is set.
+
+When the Enable-Multiple-Newsrcs feature is set
+then the setting of this feature may have an effect on the names of the
+newsrc files used.
+Normally, the name of the news server will be canonicalized before it is
+used in the newsrc file name.
+For example, if you type the news server name
+
+<P>
+<CENTER><SAMP>servername</SAMP></CENTER>
+<P>
+
+it is likely that the canonical name will be something like
+
+<P>
+<CENTER><SAMP>servername.example.com</SAMP></CENTER>
+<P>
+
+Or it may be the case that
+
+<P>
+<CENTER><SAMP>servername.example.com</SAMP></CENTER>
+<P>
+
+is really an alias (a DNS CNAME) for
+
+<P>
+<CENTER><SAMP>othername.example.com</SAMP></CENTER>
+<P>
+
+If this feature is not set, then the canonicalized names will be used.
+If this feature is set, then the name you typed in (or put in your
+configuration) will be used.
+<P>
+This feature is displayed as &quot;Multiple Newsrc Hostnames as Typed&quot;.
+<P>
+
+<DT> <A NAME="news-approximates-new-status"><EM>news-approximates-new-status</EM></A>
+
+<DD> This feature causes certain messages to be marked as <EM>New</EM> in the
+MESSAGE INDEX of newsgroups.
+This feature is set by default.
+<P>
+
+When opening a newsgroup, <EM>Alpine</EM> will consult
+your <EM>newsrc</EM> file and
+determine the last message you have previously disposed of via the <EM>D</EM>
+key. If this feature is set, any subsequent messages will be shown in the
+Index with an <EM>N</EM>, and the first of these messages will be highlighted.
+Although this is only an approximation of true <EM>New</EM> or <EM>Unseen</EM>
+status, it provides a useful cue to distinguish more-or-less recent
+messages from those you have seen previously, but are not yet ready to
+mark deleted.
+<P>
+
+Background: your <EM>newsrc</EM> file (used to store message status information
+for newsgroups) is only capable of storing a single flag, and <EM>Alpine</EM> uses
+this to record whether or not you are "done with" a message, as
+indicated by marking the message as <EM>Deleted</EM>. Unfortunately, this
+means that <EM>Alpine</EM> has no way to record exactly which messages you have
+previously seen, so it normally does not show the <EM>N</EM> status flag for
+any messages in a newsgroup. This feature enables a starting
+<I>approximation</I> of seen/unseen status that may be useful.
+<P>
+
+<DT> <A NAME="news-deletes-across-groups"><EM>news-deletes-across-groups</EM></A>
+
+<DD> This feature controls what <EM>Alpine</EM> does when you delete a
+message in a newsgroup that appears in more than one newsgroup.
+Such a message is sometimes termed a &quot;crossposting&quot;
+in that it was posted across several newsgroups.
+
+<P>
+<EM>Alpine</EM>'s default behavior when you delete such a message is to remove
+only the copy in the current newsgroup from view when you use the
+&quot;Exclude&quot; command or the next time you visit the newsgroup.
+
+<P>
+Enabling this feature causes <EM>Alpine</EM> to remove every occurrence of the
+message from all newsgroups it appears in and to which you are
+subscribed.
+
+<P>
+NOTE: As currently implemented, enabling this feature may increase the
+time it takes the Expunge command and newsgroup closing to complete.
+<P>
+
+<DT> <A NAME="news-offers-catchup-on-close"><EM>news-offers-catchup-on-close</EM></A>
+
+<DD> This feature controls what <EM>Alpine</EM> does as it closes a newsgroup.
+When set, <EM>Alpine</EM> will offer to delete all messages from the newsgroup
+as you are quitting <EM>Alpine</EM> or opening a new folder.
+
+<P>
+This feature is useful if you typically read all the interesting messages
+in a newsgroup each time you open it. This feature saves you from
+having to delete each message in a newsgroup as you read it or from
+selecting all the messages and doing an
+aggregate delete before you move on to the next folder or newsgroup.
+<P>
+
+<DT> <A NAME="news-post-without-validation"><EM>news-post-without-validation</EM></A>
+
+<DD> This feature controls whether the NNTP server is queried as newsgroups
+are entered for posting. Validation over slow links (e.g. dialup using
+SLIP or PPP) can cause delays. Set this feature to eliminate such delays.
+<P>
+
+<DT> <A NAME="news-read-in-newsrc-order"><EM>news-read-in-newsrc-order</EM></A>
+
+<DD> This feature controls the order that newsgroups will be presented. If
+set, they will be presented in the same order as they occur in
+your <EM>newsrc</EM> file.
+If not set, the newsgroups
+will be presented in alphabetical order.
+<P>
+
+<DT> <A NAME="next-thread-without-confirm"><EM>next-thread-without-confirm</EM></A>
+
+<DD> This feature controls an aspect of <EM>Alpine</EM>'s Next and Prev commands in
+the case where you are using one of the
+&quot;separate-index-screen&quot; styles for the configuration option
+<A HREF="#threading-index-style"><EM>threading-index-style</EM></A>
+and currently have the folder sorted by a Threaded or OrderedSubject sort.
+When you are Viewing a particular thread you have a
+MESSAGE INDEX of only the messages in that thread.
+If you press the Next command with the last message in the thread highlighted
+you will normally be asked if you want to &quot;View next thread?&quot;,
+assuming there is a next thread to view.
+If this feature is set it will be assumed that you always want to view the
+next thread and you won't be asked to confirm that.
+Similarly, if the first message of the thread is highlighted and you
+press the Prev command, this feature will prevent the question
+&quot;View previous thread&quot;.
+<P>
+This feature only has an effect in the MESSAGE INDEX screen.
+If you then view a particular message from that screen and press the
+Next command, you will be sent to the next thread without being asked,
+independent of the setting of this feature.
+<P>
+The feature
+<A HREF="#auto-open-next-unread">auto-open-next-unread</A>,
+also has some similar effects.
+<P>
+This feature is displayed as &quot;Read Next Thread Without Confirming&quot;.
+<P>
+
+<DT> <A NAME="offer-expunge-of-inbox"><EM>offer-expunge-of-inbox</EM></A>
+
+<DD> The INBOX is normally treated differently from regular folders in several
+ways.
+One of the differences is that the normal &quot;close&quot; sequence of
+events is deferred until <EM>Alpine</EM> is exited, instead of happening when you
+leave the INBOX to view another folder.
+The &quot;close&quot; sequence normally includes the Expunging
+of deleted messages
+(either automatically or after a prompt, controlled by the features
+<A HREF="#expunge-wo-confirm">Expunge-Without-Confirm</A>,
+<A HREF="#expunge-without-confirm-everywhere">Expunge-Without-Confirm-Everywhere</A>, and
+<A HREF="#expunge-only-manually">Expunge-Only-Manually</A>), and the
+handling of the
+<A HREF="#read-msg-fold">Read-Message-Folder</A>.
+
+<P>
+If this feature is set the &quot;close&quot; sequence handling will take
+place every time you leave the INBOX.
+The INBOX will still be kept open, but the offer to Expunge and the archiving
+to the Read-Message-Folder
+will take place each time you leave the INBOX instead of only once at the
+end of the session.
+<P>
+
+<DT> <A NAME="offer-expunge-of-stayopen-folders"><EM>offer-expunge-of-stayopen-folders</EM></A>
+
+<DD> This feature is related to the option
+<A HREF="#stay-open-folders">Stay-Open-Folders</A>.
+Stay Open folders are treated differently from regular folders in several
+ways.
+One of the differences is that the normal &quot;close&quot; sequence of
+events is deferred until <EM>Alpine</EM> is exited, instead of happening when you
+leave the folder to view another folder.
+The &quot;close&quot; sequence normally includes the Expunging
+of deleted messages
+(either automatically or after a prompt, controlled by the features
+<A HREF="#expunge-wo-confirm">Expunge-Without-Confirm</A>,
+<A HREF="#expunge-without-confirm-everywhere">Expunge-Without-Confirm-Everywhere</A>, and
+<A HREF="#expunge-only-manually">Expunge-Only-Manually</A>), and the
+handling of
+<A HREF="#incoming-archive-folders">Incoming-Archive-Folders</A>.
+
+<P>
+If this feature is set the &quot;close&quot; sequence handling will take
+place when you leave the Stay Open folder.
+The folder will still be kept open, but the offer to Expunge and the archiving
+will take place each time you leave the folder instead of only once at the
+end of the session.
+This feature does not affect the INBOX, which will still only be processed
+when you exit <EM>Alpine</EM>.
+<P>
+
+<DT> <A NAME="pass-c1-control-characters-as-is"><EM>pass-c1-control-characters-as-is</EM></A>
+
+<DD> It is probably not useful to set this option.
+This is a legacy option left behind &quot;just in case&quot;.
+Multi-byte characters which have an octet which has the same
+value as a control character are permitted through whether or not
+this option is turned on.
+<P>
+If the feature <A HREF="#pass-control-characters-as-is">pass-control-characters-as-is</A>
+is set, then this feature has no effect.
+However, if you wish to filter out regular control characters but pass the
+so-called C1 control characters (0x80 <= char < 0xA0) through unchanged, then
+you may leave <A HREF="#pass-control-characters-as-is">pass-control-characters-as-is</A>
+unset and set this feature.
+<P>
+
+<DT> <A NAME="pass-control-characters-as-is"><EM>pass-control-characters-as-is</EM></A>
+
+<DD> It is probably not useful to set this option.
+This is a legacy option left behind &quot;just in case&quot;.
+Multi-byte characters which have an octet which has the same
+value as a control character are permitted through whether or not
+this option is turned on.
+<P>
+If set, all characters in a message will be sent to the
+screen. Normally, control characters are automatically suppressed in
+order to avoid inadvertently changing terminal setup parameters.
+Control characters are usually displayed as two character sequences like
+<P><CENTER><SAMP> ^C </SAMP></CENTER><P>
+for Control-C,
+<P><CENTER><SAMP> ^[ </SAMP></CENTER><P>
+for ESCAPE,
+<P><CENTER><SAMP> ^? </SAMP></CENTER><P>
+for DELETE, and
+<P><CENTER><SAMP> ~E </SAMP></CENTER><P>
+for the character with value 133 (0x85).
+(The DEL character is displayed as ^?, regular control characters are displayed
+as the character ^ followed by the character obtained by adding the
+five low-order bits of the character to 0x40, and the C1
+control characters 0x80 - 0x9F are displayed as the character ~ followed by the
+character obtained by adding the
+five low-order bits of the character to 0x40.)
+Sometimes, in cases where changing a single control character into a
+two-character sequence would confuse <EM>Alpine</EM>'s display routines,
+a question mark is substituted for the control character.
+<P>
+If you wish to filter out regular control characters but pass the
+so-called C1 control characters (0x80 <= char < 0xA0) through unchanged, then
+you may leave this feature unset and set the feature <A HREF="#pass-c1-control-characters-as-is">pass-c1-control-characters-as-is</A> instead.
+<P>
+
+<DT> <A NAME="predict-nntp-server"><EM>predict-nntp-server</EM></A>
+
+<DD> This feature allows <EM>Alpine</EM> to assume that the open NNTP server at the
+time of composition is the NNTP server to which the message should be
+posted. This is especially recommended when there are multiple News
+collections. If this feature is not set, <EM>Alpine</EM> will try to post to the first server in
+the <a href="#nntp-server">nntp-server</a> variable. Setting
+this feature also negates the need to add News collection servers to
+the nntp-server variable.
+<P>
+This feature can be especially handy when used in conjunction with
+<a href="#enable-multiple-newsrcs">enable-multiple-newsrcs</a>.
+<P>
+This option is displayed as &quot;NNTP Server (for news)&quot;.
+<P>
+
+<DT> <A NAME="prefer-plain-text"><EM>prefer-plain-text</EM></A>
+
+<DD> A message being viewed may contain alternate versions of the same content.
+Those alternate versions are ordered by the sending software such that the
+first alternative is the least preferred and the last alternative is the
+most preferred. <EM>Alpine</EM> will normally display the most-preferred version that
+it knows how to display. This is most often encountered where the two
+alternate versions are a plain text version and an HTML version, with the
+HTML version listed last as the most preferred.
+<P>
+
+If this option is set, then any plain text version will be preferred to
+all other versions.
+<P>
+
+<DT> <A NAME="preopen-stayopen-folders"><EM>preopen-stayopen-folders</EM></A>
+
+<DD> This feature is related to the option
+<A HREF="#stay-open-folders">Stay-Open-Folders</A>.
+Normally, Stay Open folders are only opened on demand, when the user
+asks to open them.
+From then on they are kept open for the duration of the session.
+However, if this feature is set, then the Stay Open folders will all be
+opened at startup, at the same time that the INBOX is opened.
+<P>
+
+<DT> <A NAME="preserve-start-stop-characters"><EM>preserve-start-stop-characters</EM></A>
+
+<DD> This feature controls how special control key characters, typically
+<EM>^S</EM> and <EM>^Q</EM>, are interpreted when input to <EM>Alpine</EM>.
+These characters
+are known as the "start" and "stop" characters and are sometimes used in
+communications paths to control data flow between devices that operate at
+different speeds.
+<P>
+
+By default, <EM>Alpine</EM> turns the system's handling of these special characters
+off except during printing. However, if you see <EM>Alpine</EM> reporting input errors
+such as:
+
+<BLOCKQUOTE>
+ [ Command "^Q" not defined for this screen. ] <BR>
+</BLOCKQUOTE>
+
+and, at the same time, see your display become garbled, then it is likely
+that setting this option will solve the problem. Be aware, though, that
+enabling this feature will also cause <EM>Alpine</EM> to ostensibly "hang"
+whenever the <EM>Ctrl-S</EM> key combination is entered as the system is now
+interpreting such input as a "stop output" command. To "start
+output" again, simply type <EM>Ctrl-Q</EM>.
+<P>
+This feature is displayed as &quot;Preserve Start/Stop Characters&quot;.
+<P>
+
+<DT> <A NAME="print-formfeed-between-messages"><EM>print-formfeed-between-messages</EM></A>
+
+<DD> Setting this feature causes a formfeed to be printed between messages when
+printing multiple messages with the <EM>Apply Print</EM> command.
+<P>
+
+<DT> <A NAME="print-includes-from-line"><EM>print-includes-from-line</EM></A>
+
+<DD> If this feature is set, then the Unix mail style From line is included
+at the start of each message that is printed. This line looks something
+like the following, with the address replaced by the address from the
+From line of the message being printed:
+
+<BLOCKQUOTE>
+ From user@domain.somewhere.com Mon May 13 14:11:06 1996 <BR>
+</BLOCKQUOTE>
+<P>
+
+<DT> <A NAME="print-index-enabled"><EM>print-index-enabled</EM></A>
+
+<DD> This feature controls the behavior of the <EM>Print</EM> command
+when in the
+"Folder Index" screen.
+If set, the <EM>Print</EM> command will give you a prompt
+asking if you wish to print the message index, or the currently highlighted
+message. If not set, the message will be printed.
+<P>
+
+<DT> <A NAME="print-offers-custom-cmd-prompt"><EM>print-offers-custom-cmd-prompt</EM></A>
+
+<DD> When this feature is set, the <EM>Print</EM> command
+will have an additional
+subcommand called <EM>C CustomPrint</EM>.
+If selected, you will have
+the opportunity to enter any system print command, instead of being
+restricted to using those that have been previously configured in the
+<EM>Setup/Printer</EM> screen.
+<P>
+This feature is displayed as &quot;Print Offers Custom Command Prompt&quot;.
+<P>
+
+<DT> <A NAME="prune-uses-yyyy-mm"><EM>prune-uses-yyyy-mm</EM></A>
+
+<DD> By default, <EM>Alpine</EM> asks monthly whether or not you would like to rename
+some folders to a new name containing the date.
+It also asks whether or not you would like to delete some old folders.
+See the <A HREF="#pruning-rule">pruning-rule</A> option for an
+explanation.
+
+<P>
+By default, the name used when renaming a folder looks like
+<P>
+<CENTER><SAMP>&lt;foldername&gt;-&lt;month&gt;-&lt;year&gt;</SAMP></CENTER>
+<P>
+For example, the first time you run <EM>Alpine</EM> in May of 2004,
+the folder &quot;sent-mail&quot; might be renamed to
+<P>
+<CENTER><SAMP>sent-mail-apr-2004</SAMP></CENTER>
+<P>
+If this feature is set, the name used will be of the form
+<P>
+<CENTER><SAMP>&lt;foldername&gt;-&lt;yyyy&gt;-&lt;mm&gt;</SAMP></CENTER>
+<P>
+where &quot;yyyy&quot; is the year and &quot;mm&quot; is the two-digit
+month (01, 02, ..., 12).
+For the April, 2004 example above, it would instead be
+<P>
+<CENTER><SAMP>sent-mail-2004-04</SAMP></CENTER>
+<P>
+because April is the 4th month of the year.
+A reason you might want to set this feature is so that the folders
+will sort in chronological order.
+<P>
+
+<DT> <A NAME="publiccerts-in-keychain"><EM>publiccerts-in-keychain</EM></A>
+
+<DD> Mac OS X <EM>Alpine</EM> only.
+<P>
+If this feature is set the Mac OS X default keychain will be used as the place
+to store public certificates instead of a
+<A HREF="#smime-public-cert-directory"><EM>smime-public-cert-directory</EM></A>
+or a
+<A HREF="#smime-public-cert-container"><EM>smime-public-cert-container</EM></A>.
+<P>
+This feature is displayed as &quot;S/MIME -- Public Certs in MacOS Keychain&quot;.
+<P>
+
+<DT> <A NAME="quell-attachment-extension-warn"><EM>quell-attachment-extension-warn</EM></A>
+<DD> This feature suppresses the extra warning you can get when trying
+to view an attachment for which there is no mime-type match. Turning
+on this feature will just run the program according to extension
+instead of first warning the user that it will run according to the
+file's extension.
+<P>
+This feature can be used along side
+<A HREF="#quell-attachment-extra-prompt"><EM>quell-attachment-extra-prompt</EM></A>
+to preserve the behavior exhibited in <EM>Pine</EM> versions prior to <EM>Pine</EM> 4.50.
+<P>
+This feature is displayed as &quot;Suppress Attachment Extension Warning&quot;.
+<P>
+
+<DT> <A NAME="quell-attachment-extra-prompt"><EM>quell-attachment-extra-prompt</EM></A>
+<DD> By default, when you attempt to view an attachment externally
+from the &quot;Attachment View&quot; screen, you are asked if you
+really want to view the selected attachment.
+<P>
+If this feature is set, you will <B>not</B> be prompted to confirm
+your selection. Prior to <EM>Pine</EM> 4.50, the default behavior was to not
+prompt. This feature was added for those wanting to preserve that
+behavior.
+<P>
+This feature is displayed as &quot;Suppress Attachment Extra Prompt&quot;.
+<P>
+
+<DT> <A NAME="quell-berkeley-format-timezone"><EM>quell-berkeley-format-timezone</EM></A>
+
+<DD> POSIX mandates a timezone in UNIX mailbox format folder delimiters
+(the line which begins with From <SPACE>).
+Some versions of Berkeley mail have trouble with this, and don't recognize
+the line as a message delimiter.
+If this feature is set, the timezone will be left off the delimiter line.
+<P>
+This feature is displayed as &quot;Suppress Berkeley Format Timezone&quot;.
+<P>
+
+<DT> <A NAME="quell-charset-warning"><EM>quell-charset-warning</EM></A>
+
+<DD> By default, if the message you are viewing contains characters that are
+not representable in your
+<A HREF="#disp-char-set"><EM>display-character-set</EM></A>
+then <EM>Alpine</EM> will
+add a warning to the start of the displayed text.
+If this option is set, then that editorial message will be suppressed.
+<P>
+Setting this feature also suppresses the comment about the character set
+in header lines.
+For example, when viewing a message you might see
+<P>
+<CENTER><SAMP>From: &quot;[ISO-8859-2] Name&quot; &lt;address&gt;</SAMP></CENTER>
+<P>
+in the From header if your Character-Set is something other than ISO-8859-2.
+If you set this feature, the comment about the character set will
+no longer be there.
+<P>
+This feature is displayed as &quot;Suppress Character Set Warning&quot;.
+<P>
+
+<DT> <A NAME="quell-content-id"><EM>quell-content-id</EM></A>
+
+<DD> This feature changes the behavior of <EM>Alpine</EM> when sending messages.
+It is intended to work around a bug in Microsoft's Outlook XP mail user
+agent.
+As of this writing, Microsoft has acknowledged the bug but
+has not added it to the Knowledge Base.
+We have been told that there will be a post-SP1 hotfix for Outlook XP.
+This particular bug has bug fix number OfficeQFE:4781.
+The nature of the bug is that messages with attachments which
+contain a Content-ID header (which standard <EM>Alpine</EM> attachments do)
+do not show the attachment indicator (a paperclip) when viewed with
+Outlook XP.
+So the user has no indication that the message contains an attachment.
+
+<P>
+If this feature is set then <EM>Alpine</EM> will remove most Content-ID headers
+before sending a message.
+If an attachment is of type MESSAGE, then the existing Content-ID headers
+inside the message will be left intact.
+This would only happen with <EM>Alpine</EM> if a message was forwarded as an attachment
+or if a message with a message attached was forwarded.
+Similarly if an attachment of type MULTIPART/ALTERNATIVE is forwarded,
+the Content-ID headers of the alternative parts will not be removed.
+
+<P>
+Because the Content-ID header is a standard part of MIME it is possible
+that setting this feature will break something.
+For example, if an attachment has a Content-ID header which is necessary
+for the correct functioning of that attachment, it is possible that <EM>Alpine</EM>
+may remove that header when the attachment is forwarded.
+However, it seems fairly safe at this time.
+<P>
+This feature is displayed as &quot;Suppress Content-ID&quot;.
+<P>
+
+<DT> <A NAME="quell-dead-letter-on-cancel"><EM>quell-dead-letter-on-cancel</EM></A>
+
+<DD> This feature affects <EM>Alpine</EM>'s behavior when you cancel a message being
+composed. <EM>Alpine</EM>'s usual behavior is to write the canceled message to
+a file named <CODE>dead.letter</CODE> in your home directory (under UNIX;
+<CODE>DEADLETR</CODE> under WINDOWS/DOS) overwriting any previous message.
+Under some conditions (some routine), this can introduce a noticeable delay.
+<P>
+
+Setting this feature will cause <EM>Alpine</EM> NOT to write canceled compositions
+into the file called <CODE>dead.letter</CODE>.
+<P>
+This feature affects the newer option
+<A HREF="#dead-letter-files"><EM>Dead-Letter-Files</EM></A>, which specifies the
+number of dead letter files to keep around.
+If this feature is set, then the Dead-Letter-Files option has no effect.
+<P>
+This feature is displayed as &quot;Do Not Save to Deadletter on Cancel&quot;.
+<P>
+
+<DT> <A NAME="quell-empty-directories"><EM>quell-empty-directories</EM></A>
+
+<DD> This feature causes <EM>Alpine</EM> to remove from the display any directories
+that do not contain at least one file or directory. This can be useful
+to prevent overly cluttered folder lists when a collection is stored on
+a server that treats all names as both a folder and a directory.
+
+<P>
+Note, enabling this feature can cause surprising behavior! For example,
+you can still use Add to create a directory, but unless you immediately
+enter that directory and create a folder, that newly created directory
+may not be displayed next time you enter the folder list.
+
+<P>
+This feature is displayed as &quot;Hide Empty Directories&quot;.
+<P>
+
+<DT> <A NAME="quell-extra-post-prompt"><EM>quell-extra-post-prompt</EM></A>
+
+<DD> This feature causes <EM>Alpine</EM> to skip the extra question about
+posting a message which may go to thousands of readers when you
+are about to post to a newsgroup.
+
+<P>
+This feature is displayed as &quot;Suppress Extra Posting Prompt&quot;.
+<P>
+
+<DT> <A NAME="quell-filtering-done-message"><EM>quell-filtering-done-message</EM></A>
+
+<DD> This feature causes <EM>Alpine</EM> to suppress the &quot;filtering done&quot; message.
+
+<P>
+This feature is displayed as &quot;Suppress Filtering Done Message&quot;.
+<P>
+
+<DT> <A NAME="quell-filtering-messages"><EM>quell-filtering-messages</EM></A>
+
+<DD> This feature causes <EM>Alpine</EM> to suppress the messages about
+moving filtered messages and setting flags in messages, due to Filter Rules.
+
+<P>
+This feature is displayed as &quot;Suppress Filtering Messages&quot;.
+<P>
+
+<DT> <A NAME="quell-flowed-text"><EM>quell-flowed-text</EM></A>
+
+<DD> <EM>Alpine</EM> generates flowed text where possible.
+The method for generating flowed text is defined by
+<A HREF="http://www.ietf.org/rfc/rfc3676.txt">RFC 3676</A>,
+the benefit of doing so is
+to send message text that can properly be viewed both on normal width displays
+and on displays with smaller or larger than normal screen widths.
+With flowed text, a space at the end of a line tells the receiving mail
+client that the following line belongs to the same paragraph.
+Quoted text will also be affected, with only the innermost
+level of &quot;&gt;&quot; quoting being followed by a space.
+However, if you have changed the
+<A HREF="#reply-ind-str">&quot;Reply-Indent-String&quot;</A>
+so that it is not equal to the default value of &quot;&gt;&nbsp;&quot;, then
+quoted text will not be flowed.
+For this reason, we recommend that you leave your
+&quot;Reply-Indent-String&quot; set to the default.
+<P>
+This feature turns off the generation of flowed text, as it might be
+desired to more tightly control how a message is displayed on the receiving end.
+<P>
+If this feature is <EM>not</EM> set, you can control on a message by message
+basis whether or not flowed text is generated.
+You do this by typing ^V at the Send confirmation prompt that you get
+after typing ^X to send a message.
+^V is a toggle which turns flowing off and back on if typed again.
+If for some reason flowing cannot be done on a particular message, then the
+^V command will not be available.
+This would be the case, for example, if this feature was set, or if your
+&quot;Reply-Indent-String&quot; was set to a non-default value.
+If the feature
+<A HREF="#send-without-confirm">Send-Without-Confirm</A> is set,
+then the opportunity to control on a message by message basis
+whether or not flowed text is generated is lost.
+<P>
+When this feature is not set and you have typed ^V to turn off flowing,
+the Send confirmation prompt will change to look like
+<P>
+<CENTER><SAMP>Send message (not flowed)?</SAMP></CENTER>
+<P>
+<A HREF="#strip-whitespace-before-send">Strip-Whitespace-Before-Send</A> will
+also turn off the sending of flowed text messages, but it differs in that
+it also trims all trailing white space from a message before sending it.
+<P>
+If alternate editors are used extensively, be aware that a message will still
+be sent flowed if this feature is unset. In most cases this will be fine,
+but if the editor has a &quot;flowed text&quot; mode, it would be best to
+use that.
+<P>
+This feature is displayed as &quot;Do Not Send Flowed Text&quot;.
+<P>
+
+<DT> <A NAME="quell-folder-internal-msg"><EM>quell-folder-internal-msg</EM></A>
+
+<DD> This feature determines whether or not <EM>Alpine</EM> will create
+&quot;pseudo messages&quot; in folders that are in standard Unix or
+MMDF format.
+<P>
+
+<EM>Alpine</EM> will normally create these
+pseudo messages when they are not already
+present in a standard Unix or MMDF folder.
+Their purpose is to record
+certain mailbox state data needed for correct IMAP and POP server operation,
+and also for <EM>Alpine</EM> to be able to mark messages as Answered when
+the Reply has been postponed.
+<P>
+
+Sites which do not use IMAP/POP for remote mail access, and which need to
+support mail tools that are adversely affected by the presence of the
+pseudo-messages (e.g. some mail notification tools) may enable this
+feature to tell <EM>Alpine</EM> not to create them.
+Note that <EM>Alpine</EM>'s &quot;Answered&quot; flag
+capability will be adversely affected if this is done.
+<P>
+
+Note too that, even if this feature is enabled, <EM>Alpine</EM> will not remove
+pseudo-messages when it encounters them (e.g. those created by UW's imapd
+or ipopd servers.)
+This feature has no effect on folders that are not in
+standard Unix or MMDF format, as pseudo-messages are not needed in the
+other formats to record mailbox state information.
+<P>
+This feature is displayed as &quot;Prevent Folder Internal Message&quot;.
+<P>
+
+<DT> <A NAME="quell-full-header-auto-reset"><EM>quell-full-header-auto-reset</EM></A>
+
+<DD> The HdrMode Command
+normally resets to the default state when switching to a new message.
+For example, if you've used the &quot;H&quot; command to turn on Full
+Headers for a message you are viewing, and then you type the Next command
+to look at the next message, the full headers will no longer be shown.
+Setting this feature disables that reset.
+Instead, the Header Mode remains the same from message to message.
+
+<P>
+The presence or absence of the HdrMode command is determined by the
+<A HREF="#enable-full-header-cmd">&quot;Enable-Full-Header-Cmd&quot;</A>
+Feature-List option.
+<P>
+This feature is displayed as &quot;Suppress Full Header Auto Reset&quot;.
+<P>
+
+<DT> <A NAME="quell-imap-envelope-update"><EM>quell-imap-envelope-update</EM></A>
+
+<DD> In the MESSAGE INDEX screen, if the open folder is being accessed
+using IMAP, <EM>Alpine</EM> normally tries to paint the index lines on the screen
+as soon as the information arrives from the IMAP server.
+This means that the index information makes it onto the screen more quickly
+than it otherwise would.
+This sometimes results in behavior that bothers some users.
+For example, when paging to a new page of the index, it may be possible for
+the lines to be painted on the screen in a random order, rather than from
+top to bottom.
+<P>
+
+Setting this feature causes <EM>Alpine</EM> to wait for all of the information
+to be gathered before it paints the index screen.
+Once it collects all of the information, the screen will be painted quickly
+from top to bottom.
+<P>
+This feature is displayed as &quot;Suppress IMAP Envelope Update&quot;.
+<P>
+
+<DT> <A NAME="quell-lock-failure-warnings"><EM>quell-lock-failure-warnings</EM></A>
+
+<DD> This feature affects <EM>Alpine</EM>'s behavior when it encounters a problem
+acquiring a mail folder lock. Typically, a secondary file associated
+with the mail folder being opened is created as part of the locking
+process. On some systems, such file creation has been administratively
+precluded by the system configuration.
+<P>
+
+<EM>Alpine</EM> issues a warning when such failures occur, which can become bothersome
+if the system is configured to disallow such actions. Setting this
+feature causes <EM>Alpine</EM> to remain silent when this part of lock creation fails.
+<P>
+
+WARNING: systems that have been configured in a way that precludes locking
+introduce some risk of mail folder corruption when more than one program
+attempts to modify the mail folder. This is most likely to occur to one's
+<EM>INBOX</EM> or other "Incoming Message Folder".
+<P>
+This feature is displayed as &quot;Suppress Lock Failure Warnings&quot;.
+<P>
+
+<DT> <A NAME="quell-mailchecks-composing-except"><EM>Quell-Mailchecks-Composing-Except-Inbox</EM></A>
+
+<DD> This option is closely related to the
+<A HREF="#mail-check"><EM>Mail-Check-Interval</EM></A>
+option, the
+<A HREF="#mail-check-noncurr"><EM>Mail-Check-Interval-Noncurrent</EM></A> option, and
+<A HREF="#quell-mailchecks-composing-inbox"><EM>Quell-Mailchecks-Composing-Inbox</EM></A>.
+<P>
+If this option is set, then the normal new-mail checking which happens
+while you are composing will not happen for folders other than your
+INBOX (which depends on the setting
+of &quot;Quell-Mailchecks-Composing-Inbox&quot;).
+<P>
+You might want to set this option if you are experiencing delays while
+composing which you think might be related to the speed of the new-mail
+checks.
+<P>
+Even with this option turned on, an occasional new-mail check may be done
+in order to keep the server from killing the connection to the folder.
+For example, IMAP servers may remove a connection to a folder if there
+has been no activity on the connection for 30 minutes or more.
+Instead of letting that happen, <EM>Alpine</EM> will check for new mail before the
+30 minutes is up even though you have turned on this feature to quell
+those checks.
+<P>
+Besides new-mail checks, checkpoint operations on the folders
+will also be quelled when you set this option.
+The purpose of checkpointing is to write the changes to a folder out to
+disk periodically in order to avoid losing those changes when system or
+software problems occur.
+New-mail checking and checkpointing while you are not composing are not
+affected by this option.
+<P>
+This feature is displayed as &quot;Prevent Mailchecks While Composing Except for INBOX&quot;.
+<P>
+
+<DT> <A NAME="quell-mailchecks-composing-inbox"><EM>Quell-Mailchecks-Composing-Inbox</EM></A>
+
+<DD> This option is closely related to the
+<A HREF="#mail-check"><EM>Mail-Check-Interval</EM></A>
+option, the
+<A HREF="#mail-check-noncurr"><EM>Mail-Check-Interval-Noncurrent</EM></A> option, and
+<A HREF="#quell-mailchecks-composing-except"><EM>Quell-Mailchecks-Composing-Except-Inbox</EM></A>.
+<P>
+If this option is set, then the normal new-mail checking which happens
+while you are composing will not happen for your INBOX.
+Checking of other folders is controlled in a similar way with the
+&quot;Quell-Mailchecks-Composing-Except-Inbox&quot; option.
+<P>
+You might want to set this option if you are experiencing delays while
+composing which you think might be related to the speed of the new-mail
+checks.
+<P>
+Even with this option turned on, an occasional new-mail check may be done
+in order to keep the server from killing the connection to the folder.
+For example, IMAP servers may remove a connection to a folder if there
+has been no activity on the connection for 30 minutes or more.
+Instead of letting that happen, <EM>Alpine</EM> will check for new mail before the
+30 minutes is up even though you have turned on this feature to quell
+those checks.
+<P>
+Besides new-mail checks, checkpoint operations on the INBOX
+will also be quelled when you set this option.
+The purpose of checkpointing is to write the changes to a folder out to
+disk periodically in order to avoid losing those changes when system or
+software problems occur.
+New-mail checking and checkpointing while you are not composing are not
+affected by this option.
+<P>
+This feature is displayed as &quot;Prevent Mailchecks While Composing for INBOX&quot;.
+<P>
+
+<DT> <A NAME="quell-maildomain-warning"><EM>quell-maildomain-warning</EM></A>
+
+<DD> When your configuration is set up so that your domain name contains no dots,
+it is usually a configuration error.
+By default, <EM>Alpine</EM> will warn you about this when you start it up.
+You will see a warning message that looks like
+<P>
+<CENTER><SAMP>Incomplete maildomain &quot;&lt;domain&gt;&quot;.</SAMP></CENTER>
+
+<P>
+If this feature is set, the warning is turned off.
+This feature is displayed as &quot;Suppress Maildomain Warning&quot;.
+<P>
+
+<DT> <A NAME="quell-news-envelope-update"><EM>quell-news-envelope-update</EM></A>
+
+<DD> In the MESSAGE INDEX screen, if the open folder is being accessed
+using NNTP (News), <EM>Alpine</EM> normally tries to paint the index lines on the screen
+as soon as the information arrives from the NNTP server.
+This means that the index information makes it onto the screen more quickly
+than it otherwise would.
+This sometimes results in behavior that bothers some users.
+For example, when paging to a new page of the index, it may be possible for
+the lines to be painted on the screen in a random order, rather than from
+top to bottom.
+<P>
+
+Setting this feature causes <EM>Alpine</EM> to wait for all of the information
+to be gathered before it paints the index screen.
+Once it collects all of the information, the screen will be painted quickly
+from top to bottom.
+<P>
+This feature is displayed as &quot;Suppress News Envelope Update&quot;.
+<P>
+
+<DT> <A NAME="quell-partial-fetching"><EM>quell-partial-fetching</EM></A>
+
+<DD> Partial fetching is a feature of the IMAP protocol.
+By default, <EM>Alpine</EM>
+will use partial fetching when copying the contents of a message or attachment
+from the IMAP server to <EM>Alpine</EM>.
+This means that the fetch will be done in many
+small chunks instead of one big chunk. The main benefit of this approach is
+that the fetch becomes interruptible. That is, the user can type <EM>^C</EM>
+to stop the fetch early. In some cases partial fetching may cause a performance
+problem so that the fetching of data takes significantly longer when partial
+fetching is used. Turning on this feature will turn off partial fetching.
+<P>
+This feature is displayed as &quot;Prevent Partial Fetching&quot;.
+<P>
+
+<DT> <A NAME="quell-personal-name-prompt"><EM>quell-personal-name-prompt</EM></A>
+
+<DD> <EM>PC-Alpine</EM> only. This feature quells the prompting for a
+<A HREF="#personal-name">personal-name</A>. This prompt normally happens
+before composing a message, and only happens when there is no personal name
+already set.
+<P>
+
+<DT> <A NAME="quell-server-after-link-in-html"><EM>quell-server-after-link-in-html</EM></A>
+
+<DD> By default, links in HTML text are displayed with the host the link
+references appended, within square brackets, to the link text. <EM>Alpine</EM>
+does this to help indicate where a link will take you, particularly when
+the link text might suggest a different destination.
+
+<P>
+Setting this feature will prevent the server name from being appended
+to the displayed text.
+<P>
+This feature is displayed as &quot;Suppress Server After Link in HTML&quot;.
+<P>
+
+<DT> <A NAME="quell-ssl-largeblocks"><EM>quell-ssl-largeblocks</EM></A>
+
+<DD> This feature (<EM>PC-Alpine</EM> only) changes the behavior of fetching messages
+and attachments so that the message data is fetched in chunks no larger
+than 12K bytes.
+This works around a bug in Microsoft's SSL/TLS support.
+Some versions of Microsoft SSL are not able to read full-sized (16K)
+SSL/TLS packets.
+Some servers will send such packets and this will
+cause <EM>PC-Alpine</EM> to crash with the error
+
+<P>
+<CENTER><SAMP>incomplete SecBuffer exceeds maximum buffer size</SAMP></CENTER>
+
+<P>
+Microsoft is aware of the problem and has developed a hotfix for it, but as of
+this writing the hotfix has not yet been added to the Knowledge Base.
+<P>
+This feature is displayed as &quot;Prevent SSL Largeblocks&quot;.
+<P>
+
+<DT> <A NAME="quell-status-message-beeping"><EM>quell-status-message-beeping</EM></A>
+
+<DD> If set status messages will never emit a beep.
+<P>
+This feature is displayed as &quot;Suppress Status Message Beeping&quot;.
+<P>
+
+<DT> <A NAME="quell-timezone-comment-when-sending"><EM>quell-timezone-comment-when-sending</EM></A>
+
+<DD> Normally, when <EM>Alpine</EM> generates a Date header for outgoing mail,
+it will try to include the symbolic timezone at the end of the
+header inside parentheses.
+The symbolic timezone is often three characters long, but on
+some operating systems, it may be longer.
+Apparently there are some SMTP servers in the world which will reject an
+incoming message if it has a Date header longer than about 80 characters.
+If this feature is set, the symbolic timezone normally generated by
+<EM>Alpine</EM> will not be included.
+You probably don't need to worry about this feature unless you run into
+the problem described above.
+<P>
+This feature is displayed as &quot;Suppress Timezone Comment When Sending&quot;.
+<P>
+
+<DT> <A NAME="quell-user-id-prompt"><EM>quell-user-id-prompt</EM></A>
+
+<DD> <EM>PC-Alpine</EM> only. This feature quells the prompting for a
+<A HREF="#user-id">user-id</A>
+if the information can be obtained from the login name used
+to open the INBOX. Normally, this prompt happens before composing
+a message, and only happens when there is no user-id already set
+in the configuration.
+<P>
+With this feature set, composing a message is only possible after
+establishing a connection to the INBOX.
+<P>
+
+<DT> <A NAME="quell-user-lookup-in-passwd-file"><EM>quell-user-lookup-in-passwd-file</EM></A>
+
+<DD> This feature controls an aspect of
+<EM>Alpine</EM>'s Composer, and if needed, will usually be set by the
+system manager in <EM>Alpine</EM>'s system-wide configuration file.
+Specifically, if this feature is set, <EM>Alpine</EM> will not attempt to look
+in the system password file to find a Full Name for the entered address.
+<P>
+
+Normally, names you enter into address fields (e.g. To: or Cc:) are
+checked against your address book(s) to see if they match an address book
+nickname.
+Failing that, (in Unix <EM>Alpine</EM>) the name is then checked against
+the Unix password file. If the entered name matches a username in the
+system password file, <EM>Alpine</EM> extracts the corresponding Full Name information
+for that individual, and adds that to the address being entered.
+<P>
+
+However, password file matching can have surprising (incorrect) results if
+other users of the system do not receive mail at the domain you are using.
+That is, if either the <A HREF="#user-domain"><EM>user-domain</EM></A>
+or <A HREF="#use-only"><EM>use-only-domain-name</EM></A> option
+is set such that the administrative domain of other users on the system
+isn't accurately reflected, <EM>Alpine</EM> should be told that a password
+file match is coincidental,
+and Full Name info will be incorrect.
+For example, a
+personal name from the password file could get falsely paired with the
+entered name as it is turned into an address in the configured domain.
+<P>
+
+If you are seeing this behavior, enabling this feature will prevent Unix
+<EM>Alpine</EM> from looking up names in the password file to find the Full Name
+for incomplete addresses you enter.
+<P>
+This feature is displayed as &quot;Prevent User Lookup in Password File&quot;.
+<P>
+
+<DT> <A NAME="quit-without-confirm"><EM>quit-without-confirm</EM></A>
+
+<DD> This feature controls whether or not <EM>Alpine</EM> will ask for confirmation when a
+<EM>Quit</EM> command is received.
+<P>
+This feature is displayed as &quot;Quit Without Confirming&quot;.
+<P>
+
+<DT> <A NAME="quote-replace-nonflowed"><EM>quote-replace-nonflowed</EM></A>
+
+<DD> This feature, which is only active when
+<A HREF="#quote-replace-string">Quote-Replace-String</A> is
+also set,
+enables quote-replacement on non-flowed messages. It is off
+by default because a non-flowed message is more dependent on its format,
+and thus quote-replacement may cause less-than-pleasing results.
+Setting this feature will cause quote-replacement similar to that of flowed
+messages, but with the added possibility of long lines being wrapped
+into new lines if the Quote-Replacement-String is longer than the string
+it is replacing, which is &quot;&gt;&nbsp;&quot;.
+
+<P>
+
+<DT> <A NAME="reply-always-uses-reply-to"><EM>reply-always-uses-reply-to</EM></A>
+
+<DD> If set, <EM>Alpine</EM>
+will not prompt when a message being replied to contains a <EM>Reply-To:</EM>
+header value, but will simply use its value (as opposed to using the
+<EM>From:</EM> field's value).
+<P>
+
+<DT> <A NAME="return-to-inbox-without-confirm"><EM>return-to-inbox-without-confirm</EM></A>
+
+<DD>
+Normally, when you use the TAB
+command and there are no more folders or newsgroups to visit, you are asked
+if you want to return to the INBOX.
+If this feature is set you will not be asked.
+It will be assumed that you do want to return to the INBOX.
+<P>
+This feature is displayed as &quot;Return to INBOX Without Confirming&quot;.
+<P>
+
+<DT> <A NAME="save-aggregates-copy-sequence"><EM>save-aggregates-copy-sequence</EM></A>
+
+<DD> This feature will optimize an aggregate copy operation, if
+possible, by issuing a single IMAP <EM>COPY</EM> command with a
+list of the messages to be copied.
+This feature is set by default.
+This may reduce network traffic and elapsed time for the Save.
+<EM>However, many IMAP servers (including the UW IMAP server) do
+not preserve the order of messages when this optimization is applied.</EM>
+If this feature is not set,
+<EM>Alpine</EM> will copy each message individually and the order of the
+messages will be preserved.
+<P>
+This feature is displayed as &quot;Save Combines Copies (may be out of order)&quot;.
+<P>
+
+<DT> <A NAME="save-partial-wo-confirm"><EM>save-partial-msg-without-confirm</EM></A>
+
+<DD> This feature controls an aspect of <EM>Alpine</EM>'s Save command.
+By default, when you Save a message that has some deleted parts, you will
+be asked to confirm that you want to Save with a prompt that looks like:
+<P>
+<CENTER><SAMP>Saved copy will NOT include entire message! Continue?</SAMP></CENTER>
+<P>
+If this feature is set, you will not be asked.
+<P>
+This feature is displayed as &quot;Save Partial Message Without Confirming&quot;.
+<P>
+
+<DT> <A NAME="save-will-advance"><EM>save-will-advance</EM></A>
+
+<DD> If set, <EM>Save</EM> will
+(in addition to copying the current message to the designated folder) also
+advance to the next message.
+<P>
+
+<DT> <A NAME="save-will-not-delete"><EM>save-will-not-delete</EM></A>
+
+<DD> If set, <EM>Save</EM> will
+not mark the message Deleted (its default behavior) after it has been
+copied to the designated folder.
+<P>
+
+<DT> <A NAME="save-will-quote"><EM>save-will-quote-leading-froms</EM></A>
+
+<DD> This feature controls an aspect of the <EM>Save</EM> command
+(and also the way
+outgoing messages are saved to an FCC folder). If set, <EM>Alpine</EM> will add
+a leading <CODE>&gt;</CODE> character in front of message
+lines beginning with "From" when they are
+saved to another folder, including lines syntactically
+distinguishable from the type of message separator line commonly used on
+Unix systems.
+<P>
+
+The default behavior is that a <CODE>&gt;</CODE> will be prepended only to lines
+beginning with "From " that might otherwise be confused with a message
+separator line on Unix systems. If <EM>Alpine</EM> is the only mail program you use,
+this default is reasonable. If another program you use has trouble
+displaying a message with an unquoted From saved by <EM>Alpine</EM>, you should
+enable this feature. This feature only applies to the common Unix mailbox
+format that uses message separator lines beginning with "From ". If
+<EM>Alpine</EM> has been configured to use a different mailbox format (possibly
+incompatible with other mail programs), then this issue does not arise,
+and the feature is irrelevant.
+<P>
+
+<DT> <A NAME="scramble-message-id"><EM>scramble-message-id</EM></A>
+
+<DD> Normally the Message-ID header that <EM>Alpine</EM> generates when sending a message
+contains the name of the computer from which the message is being sent.
+Some believe that this hostname could be used by spammers or could
+be used by others for nefarious purposes.
+If this feature is set, that name will be transformed with a simple
+Rot13 transformation.
+The result will still have the correct syntax for a Message-ID but the
+part of the MessageID that is often a domain name will not be an actual
+domain name because the letters will be scrambled.
+<P>
+It is possible (but unlikely?) that some spam detection
+software will use that as a reason to reject the mail as spam.
+It has also been reported that some spam detection software uses the
+fact that there are no dots after the &quot;@&quot; as a reason to reject
+messages.
+If your <EM>PC-Alpine</EM> Message-ID is using a name without a dot that is because
+that is what Windows thinks is your &quot;Full computer name&quot;.
+The method used to set this varies from one type of Windows to another but
+check under Settings -> Control Panel -> System and
+look for Network Identification or Computer Name or something similar.
+How to set it is beyond the scope of <EM>Alpine</EM>.
+<P>
+This feature is displayed as &quot;Scramble the Message-ID When Sending&quot;.
+<P>
+
+<DT> <A NAME="select-without-confirm"><EM>select-without-confirm</EM></A>
+
+<DD> This feature controls an aspect of
+<EM>Alpine</EM>'s <EM>Save</EM>, <EM>Export</EM>, and <EM>Goto</EM> commands.
+These commands all take text input to specify the name of the folder or
+file to be used, but allow you to press <EM>^T</EM> for a
+list of possible names.
+If set, the selected name will be used immediately, without further
+opportunity to confirm or edit the name.
+<P>
+This feature is displayed as &quot;Select Ctrl-T Foldername Without Confirming&quot;.
+<P>
+
+<DT> <A NAME="send-without-confirm"><EM>send-without-confirm</EM></A>
+
+<DD> By default, when you send or post a message you will be asked to confirm
+with a question that looks something like:
+
+<P>
+<CENTER><SAMP>Send message?</SAMP></CENTER>
+<P>
+
+If this feature is set, you
+will <B>not</B> be prompted to confirm your intent to send
+and your message will be sent.
+<P>
+If this feature is set it disables some possibilities and renders some
+other features meaningless.
+You will not be able to use
+<A HREF="#sending-filters">Sending Filters</A>,
+Verbose sending mode,
+<A HREF="#enable-background-sending">Background Sending</A>,
+<A HREF="#enable-delivery-status-notification">Delivery Status Notifications</A>,
+or ^V to turn off the generation of flowed text for this message.
+These options are normally available as suboptions in the Send prompt, but
+with no Send prompt the options are gone.
+
+<P>
+A somewhat related feature is
+<A HREF="#quell-extra-post-prompt">quell-extra-post-prompt</A>.
+which may be used to eliminate the extra confirmation
+question when posting to a newsgroup.
+<P>
+This feature is displayed as &quot;Send Without Confirming&quot;.
+<P>
+
+<DT> <A NAME="separate-folder-and-directory-display"><EM>separate-folder-and-directory-display</EM></A>
+
+<DD> This feature affects folder collections wherein a folder
+and directory can have the same name. By default, <EM>Alpine</EM> displays them
+only once, denoting that it is both a folder and directory by appending
+the folder name with the hierarchy character enclosed
+in square brackets.
+<P>
+
+Enabling this feature will cause <EM>Alpine</EM> to display such names
+separately marking the name representing a directory with a trailing
+hierarchy delimiter (typically the slash, &quot;/&quot;, character).
+<P>
+
+The feature also alters the command set slightly. By default, the
+right-arrow descends into the directory, while hitting the Return key will
+cause the folder by that name to be opened.
+<P>
+
+With this feature set, the Return key will open the highlighted folder, or
+enter the highlighted directory.
+<P>
+
+<DT> <A NAME="show-cursor"><EM>show-cursor</EM></A>
+
+<DD> If set, the system
+cursor will move to convenient locations in the displays. For example,
+to the beginning of the status field of the highlighted index line, or
+to the highlighted word after a successful <EM>WhereIs</EM> command.
+It is intended to draw your attention to the <EM>interesting</EM>
+spot on the screen.
+<P>
+
+<DT> <A NAME="show-plain-text-internally"><EM>show-plain-text-internally</EM></A>
+
+<DD> This feature modifies the method <EM>Alpine</EM> uses to display Text/Plain
+MIME attachments from the Attachment Index screen. Normally, the
+&quot;View&quot; command searches for any externally defined (usually
+via the Mailcap file) viewer,
+and displays the selected text within that viewer.
+
+<P>
+Enabling this feature causes <EM>Alpine</EM> to ignore any external viewer
+settings and always display text with <EM>Alpine</EM>'s internal viewer.
+
+<P>
+
+<DT> <A NAME="show-selected-in-boldface"><EM>show-selected-in-boldface</EM></A>
+
+<DD> This feature controls an aspect of <EM>Alpine</EM>'s aggregate operation commands;
+in particular, the <EM>Select</EM> and <EM>WhereIs</EM> commands.
+<EM>Select</EM> and <EM>WhereIs</EM> (with
+the <EM>^X</EM> subcommand) will search the current folder
+for messages meeting a
+specified criteria, and <EM>tag</EM> the resulting
+messages with an <EM>X</EM> in the
+first column of the applicable lines in the "Folder Index". If this feature
+is set, instead of using the <EM>X</EM> to denote a selected message,
+<EM>Alpine</EM> will attempt to display those index lines in boldface.
+Whether this is preferable to the <EM>X</EM> will depend on personal
+taste and the type of terminal being used.
+<P>
+
+<DT> <A NAME="show-sort"><EM>show-sort</EM></A>
+
+<DD> If this feature is set and there is sufficient space on the screen,
+a short indication of the current sort order will be
+added in the titlebar (the top line on the screen), before the name
+of the folder.
+For example, with the default Arrival sort in effect,
+the display would have the characters
+
+<P><CENTER>[A]</CENTER><P>
+
+added between the title of the screen and the folder name.
+The letters are the same as the letters you may type to manually
+sort a folder with the SortIndex command ($).
+The letters in the table below are the ones that may show
+up in the titlebar line.
+<P>
+<TABLE>
+<TR> <TD> A </TD> <TD> <EM>A</EM>rrival </TD> </TR>
+<TR> <TD> S </TD> <TD> <EM>S</EM>ubject </TD> </TR>
+<TR> <TD> F </TD> <TD> <EM>F</EM>rom </TD> </TR>
+<TR> <TD> T </TD> <TD> <EM>T</EM>o </TD> </TR>
+<TR> <TD> C </TD> <TD> <EM>C</EM>c </TD> </TR>
+<TR> <TD> D </TD> <TD> <EM>D</EM>ate </TD> </TR>
+<TR> <TD> Z </TD> <TD> si<EM>Z</EM>e </TD> </TR>
+<TR> <TD> O </TD> <TD> <EM>O</EM>rderedsubject </TD> </TR>
+<TR> <TD> E </TD> <TD> scor<EM>E</EM> </TD> </TR>
+<TR> <TD> H </TD> <TD> t<EM>H</EM>read </TD> </TR>
+</TABLE>
+<P>
+If the sort order is Reversed, the letter above will be preceded by the letter
+&quot;R&quot;, for example
+
+<P><CENTER>[RS]</CENTER><P>
+
+means that a Reverse Subject sort is in effect.
+For the case where the sort is in Reverse Arrival order, the &quot;A&quot; is
+left out, and just an &quot;R&quot; is shown.
+
+<P><CENTER>[R]</CENTER>
+<P>
+This feature is displayed as &quot;Show Sort in Titlebar&quot;.
+<P>
+
+<DT> <A NAME="sig-at-bot"><EM>signature-at-bottom</EM></A>
+
+<DD> If this feature
+is set, and a message being <EM>Repl</EM>ied to is being included in
+the reply, then the
+contents of the signature file (if any) will be inserted after the included
+message.
+This feature does not affect the results of a <EM>Forward</EM> command.
+<P>
+
+<DT> <A NAME="single-column-folder-list"><EM>single-column-folder-list</EM></A>
+
+<DD> If set, the "Folder List" screen will list one folder per line
+instead of several per line.
+<P>
+
+<DT> <A NAME="slash-collapses-entire-thread"><EM>slash-collapses-entire-thread</EM></A>
+
+<DD> Normally, the Collapse/Expand Thread command Collapses or Expands the subthread which
+starts at the currently highlighted message, if any.
+If this feature is set, then the slash command Collapses or Expands the
+<EM>entire</EM> current thread instead of just the subthread.
+<P>
+
+<DT> <A NAME="smime-dont-do-smime"><EM>smime-dont-do-smime</EM></A>
+
+<DD> UNIX <EM>Alpine</EM> only.
+<P>
+Setting this feature turns off all of <EM>Alpine</EM>'s S/MIME support.
+You might want to set this if you are having trouble due to the S/MIME support.
+<P>
+<UL>
+<LI><A HREF="config-notes.html#smime-general">General S/MIME Overview</A>
+</UL><P>
+This feature is displayed as &quot;S/MIME -- Turn off S/MIME&quot;.
+<P>
+
+<DT> <A NAME="smime-encrypt-by-default"><EM>smime-encrypt-by-default</EM></A>
+
+<DD> UNIX <EM>Alpine</EM> only.
+<P>
+This feature only has an effect if your version of <EM>Alpine</EM> includes
+support for S/MIME.
+It affects <EM>Alpine</EM>'s behavior when you send a message.
+If this option is set, the &quot;Encrypt&quot; option will default to ON when sending messages.
+<P>
+Only the default value is affected.
+In any case, you may still toggle the Encrypt option on or off before sending
+with the &quot;E Encrypt&quot; command (provided you have a the public digital ID
+for the recipient).
+<P>
+<UL>
+<LI><A HREF="config-notes.html#smime-general">General S/MIME Overview</A>
+</UL><P>
+This feature is displayed as &quot;S/MIME -- Encrypt by Default&quot;.
+<P>
+
+<DT> <A NAME="smime-remember-passphrase"><EM>smime-remember-passphrase</EM></A>
+
+<DD> UNIX <EM>Alpine</EM> only.
+<P>
+This feature only has an effect if your version of <EM>Alpine</EM> includes
+support for S/MIME.
+If this option is set, you will only have to enter your passphrase for your private key
+once during an <EM>Alpine</EM> session.
+<P>
+<UL>
+<LI><A HREF="config-notes.html#smime-general">General S/MIME Overview</A>
+</UL><P>
+This feature is displayed as &quot;S/MIME -- Remember S/MIME Passphrase&quot;.
+<P>
+
+<DT> <A NAME="smime-sign-by-default"><EM>smime-sign-by-default</EM></A>
+
+<DD> UNIX <EM>Alpine</EM> only.
+<P>
+This feature only has an effect if your version of <EM>Alpine</EM> includes
+support for S/MIME.
+It affects <EM>Alpine</EM>'s behavior when you send a message.
+If this option is set, the &quot;Sign&quot; option will default to ON when sending messages.
+<P>
+Only the default value is affected.
+In any case, you may still toggle the Signing option on or off before sending
+with the &quot;G Sign&quot; command (provided you have a personal digital ID
+certificate).
+<P>
+<UL>
+<LI><A HREF="config-notes.html#smime-general">General S/MIME Overview</A>
+</UL><P>
+This feature is displayed as &quot;S/MIME -- Sign by Default&quot;.
+<P>
+
+<DT> <A NAME="sort-default-fcc-alpha"><EM>sort-default-fcc-alpha</EM></A>
+
+<DD> This feature controls an aspect of <EM>Alpine</EM>'s FOLDER LIST screen.
+If set, the default FCC folder will be sorted alphabetically with the other
+folders instead of appearing right after the INBOX.
+<P>
+This feature is displayed as &quot;Sort Default Fcc Folder Alphabetically&quot;.
+<P>
+
+<DT> <A NAME="sort-default-save-alpha"><EM>sort-default-save-alpha</EM></A>
+
+<DD> This feature controls an aspect of <EM>Alpine</EM>'s FOLDER LIST screen.
+If set, the default save folder will be sorted alphabetically with the other
+folders instead of appearing right after the INBOX (and default FCC folder).
+<P>
+This feature is displayed as &quot;Sort Default Save Folder Alphabetically&quot;.
+<P>
+
+<DT> <A NAME="spell-check-before-sending"><EM>spell-check-before-sending</EM></A>
+
+<DD> When this feature is set, every composed message will be spell-checked before
+being sent.
+
+<P>
+
+<DT> <A NAME="store-window-position-in-config"><EM>store-window-position-in-config</EM></A>
+
+<DD> Normally, <EM>PC-Alpine</EM> will store its window size and position in the
+Windows Registry.
+This is convenient if you want to use the same remote
+configuration from more than one PC.
+If you use multiple configuration files to start <EM>PC-Alpine</EM>, you may want
+to store the window size and position in the configuration file instead
+of in the Registry.
+Setting this feature causes that to happen.
+
+<P>
+
+<DT> <A NAME="strip-from-sigdashes-on-reply"><EM>strip-from-sigdashes-on-reply</EM></A>
+
+<DD> This feature doesn't do anything if the feature
+<A HREF="#enable-sigdashes"><EM>enable-sigdashes</EM></A> is turned on.
+However, if the <EM>enable-sigdashes</EM> feature is not turned on,
+then turning on this feature enables support for the convention
+of not including text beyond the sigdashes line when Replying or Following
+up to a message and including the text of that message.
+<P>
+In other words, this is a way to turn on the signature stripping behavior
+without also turning on the dashes-adding behavior.
+<P>
+
+<DT> <A NAME="strip-whitespace-before-send"><EM>strip-whitespace-before=send</EM></A>
+
+<DD> Trailing whitespace is not stripped from
+a message before sending. Trailing whitespace should have no effect on an
+email message, and in flowed text can aid in delimiting paragraphs.
+However, the old behavior of stripping trailing whitespace was in place
+to better deal with older clients that couldn't handle certain types of
+text encodings. This feature restores the old behavior
+<P>
+Trailing whitespace is of aid to flowed-text-formatted messages, which are
+generated by default but can be turned off via the
+<A HREF="#quell-flowed-text">quell-flowed-text</A> feature.
+strip-whitespace-before-send also has the effect of turning off sending
+of flowed text.
+<P>
+This feature is displayed as &quot;Strip Whitespace Before Sending&quot;.
+<P>
+
+<DT> <A NAME="suppress-asterisks-in-password-prompt"><EM>suppress-asterisks-in-password-prompt</EM></A>
+
+<DD> When you are running <EM>Alpine</EM> you will sometimes be asked for a password
+in a prompt on the third line from the bottom of the screen.
+Normally each password character you type will cause an asterisk to echo
+on the screen. That gives you some feedback to know that your typing is
+being recognized.
+There is a very slight security risk in doing it this way because someone
+watching over your shoulder might be able to see how many characters there
+are in your password.
+If you'd like to suppress the echoing of the asterisks set this feature.
+<P>
+
+<DT> <A NAME="suppress-user-agent-when-sending"><EM>suppress-user-agent-when-sending</EM></A>
+<DD> If this feature is set then <EM>Alpine</EM> will not generate a
+<CODE>User-Agent</CODE> header in outgoing messages.
+<P>
+
+<DT> <A NAME="tab-checks-recent"><EM>tab-checks-recent</EM></A>
+
+<DD> In a FOLDER LIST screen, the TAB key usually just changes which
+folder is highlighted.
+If this feature is set, then the TAB key will cause the number of
+recent messages and the total number of messages in the highlighted folder
+to be displayed instead.
+<P>
+This feature is displayed as &quot;Tab Checks for Recent Messages&quot;.
+<P>
+
+<DT> <A NAME="tab-uses-unseen-for-next-folder"><EM>tab-uses-unseen-for-next-folder</EM></A>
+
+<DD> This feature affects <EM>Alpine</EM>'s behavior when using the TAB
+NextNew Command
+to move from one folder to the next.
+<EM>Alpine</EM>'s usual behavior is to search for folders
+with <EM>Recent</EM> messages in them.
+Recent messages are messages which have arrived since the last time the
+folder was opened.
+
+<P>
+Setting this feature causes <EM>Alpine</EM> to search for <EM>Unseen</EM>
+messages instead of Recent messages.
+Unseen messages remain Unseen until you view them (or flag then as Seen with
+the Flag Command).
+Setting this feature allows you to locate messages you have not read
+instead of only recently received messages.
+When this feature is set, the feature
+<A HREF="#enable-fast-recent-test">Enable-Fast-Recent-Test</A>
+will have no effect, so the checking may be slower.
+
+<P>
+Another reason why you might want to use this feature is that <EM>Alpine</EM> sometimes
+opens folders implicitly behind the scenes, and this clears the
+Recent status of all messages in the folder.
+One example where this happens is when Saving or filtering a
+message to another folder.
+If that message has some <A HREF="#keywords">keywords</A>
+set, then because of some shortcomings
+in the IMAP specification, the best way to ensure that those keywords are
+still set in the saved copy of the message is to open the folder and
+set the keywords explicitly.
+Because this clears the Recent status of all messages in that folder the
+folder will not be found by the NextNew command unless this feature is set.
+<P>
+
+<DT> <A NAME="tab-visits-next-new-message-only"><EM>tab-visits-next-new-message-only</EM></A>
+
+<DD> This feature affects <EM>Alpine</EM>'s behavior when using the <EM>TAB</EM>
+key to move from one message to the next.
+<EM>Alpine</EM>'s usual behavior is to select the next
+<EM>Unread</EM> message or message flagged as <EM>Important</EM>.
+<P>
+
+Setting this feature causes <EM>Alpine</EM> to skip the
+messages flagged as <EM>Important</EM>,
+and select <EM>Unread</EM> messages exclusively.
+Tab behavior when there are no
+new messages left to select remains unchanged.
+<P>
+
+<DT> <A NAME="termdef-takes-precedence"><EM>termdef-takes-precedence</EM></A>
+
+<DD> This feature may affect <EM>Alpine</EM>'s low-level input routines. Termcap (or
+terminfo, depending on how your copy of <EM>Alpine</EM> was compiled and linked)
+is the name of the database which describes terminal capabilities. In
+particular, it describes the sequences of characters that various keys
+will emit.
+
+<P>
+An example would be the Up Arrow key on the keyboard. Up
+Arrow is not a distinct character on most Unix systems. When you press
+the Up Arrow key a short sequence of characters are produced. This
+sequence is supposed to be described in the termcap database by the
+&quot;ku&quot; capability (or by the &quot;kcuu1&quot; capability if you
+are using terminfo instead of termcap).
+
+<P>
+By default, <EM>Alpine</EM> defines some terminal
+escape sequences that are commonly used. For example, the sequence
+&quot;ESC&nbsp;O&nbsp;A&quot; is recognized as an Up Arrow key. The sequence
+&quot;ESC&nbsp;[&nbsp;A&quot;
+is also recognized as an Up Arrow key. These are chosen because common
+terminals like VT100's or ANSI standard terminals produce these
+sequences when you press the Up Arrow key.
+
+<P>
+If your system's termcap
+(terminfo) database assigns some other function to the sequence
+&quot;ESC&nbsp;O&nbsp;A&quot;
+it is usually ignored by <EM>Alpine</EM>. Also, if your termcap (terminfo)
+database assigns a sequence which doesn't begin with an escape
+character (<SAMP>ESC</SAMP>) it is usually ignored by <EM>Alpine</EM>.
+This usually works fine
+because most terminals emit the escape sequences that <EM>Alpine</EM> has defined
+by default. We have also found that it is usually better to have these
+defaults take precedence over the definitions contained in the database
+because the defaults are more likely to be correct than the database.
+
+<P>
+There are some terminals where this breaks down. If you want <EM>Alpine</EM> to
+believe the definitions given in your termcap (terminfo) database in
+preference to the defaults the <EM>Alpine</EM> itself sets up, then you may turn
+this feature on. Then, sequences of characters which are defined in
+both termcap (terminfo) and in <EM>Alpine</EM>'s set of defaults will be
+interpreted the way that termcap (terminfo) says they should be
+interpreted. Also, if your terminal capabilities database assigns a
+sequence which doesn't begin with escape, it will not be ignored.
+<P>
+
+<DT> <A NAME="thread-index-shows-important-color"><EM>thread-index-shows-important-color</EM></A>
+
+<DD> This option affects only the THREAD INDEX screen.
+Whether or not you ever see a THREAD INDEX screen depends on the setting
+of the configuration option
+<A HREF="#threading-index-style"><EM>threading-index-style</EM></A>
+and on the sort order of the index.
+If a message within a thread is flagged as Important
+and this option is set, then
+the entire line in the THREAD INDEX will be colored the color of the
+Index-important Symbol, which can be set using the
+Setup Kolor screen.
+<P>
+
+<DT> <A NAME="try-alternative-authentication-driver-first"><EM>try-alternative-authentication-driver-first</EM></A>
+
+<DD> This feature affects how <EM>Alpine</EM> connects to IMAP servers.
+It's utility has largely been overtaken by events,
+but it may still be useful in some circumstances.
+If you only connect to modern IMAP servers that support
+&quot;TLS&quot; you can ignore this feature.
+
+<P>
+Details:
+
+<P>
+By default, <EM>Alpine</EM> will attempt to connect to an IMAP server on the
+normal IMAP service port (143), and if the server offers &quot;Transport Layer
+Security&quot; (TLS) and <EM>Alpine</EM> has been compiled with encryption capability,
+then a secure (encrypted) session will be negotiated.
+
+<P>
+With this feature enabled, before connecting on the normal IMAP port, <EM>Alpine</EM>
+will first attempt to connect to an alternate IMAP service port (993) used
+specifically for encrypted IMAP sessions via the Secure Sockets Layer
+(SSL) method.
+If the SSL attempt fails, <EM>Alpine</EM> will then try the default
+behavior described in the previous paragraph.
+
+<P>
+TLS negotiation on the normal port is preferred, and supersedes the use of
+SSL on port 993, but older servers may not provide TLS support.
+This feature may be convenient when accessing IMAP servers that do not support
+TLS, but do support SSL connections on port 993.
+However, it is important to understand that with this feature enabled,
+<EM>Alpine</EM> will <EM>attempt</EM> to make a secure connection if that is possible,
+but it will proceed to make an insecure connection if that is the only
+option offered by the server, or if the <EM>Alpine</EM> in question has been built
+without encryption capability.
+
+<P>
+Note that this feature specifies a per-user (or system-wide) default
+behavior, but host/folder specification flags may be used to control the
+behavior of any specific connection.
+This feature interacts with some of
+the possible host/folder path specification flags as follows:
+
+<P>
+The <SAMP>/tls</SAMP> host flag, for example,
+
+<P>
+<CENTER><SAMP>{foo.example.com/tls}INBOX</SAMP></CENTER>
+<P>
+will over-ride this feature for the specified host by bypassing the
+SSL connection attempt.
+Moreover, with <SAMP>/tls</SAMP> specified,
+the connection attempt will fail if the
+service on port 143 does not offer TLS support.
+
+<P>
+The <SAMP>/ssl</SAMP> host flag, for example,
+
+<P>
+<CENTER><SAMP>{foo.example.com/ssl}INBOX</SAMP></CENTER>
+<P>
+will insist on an SSL connection for the specified host,
+and will fail if the SSL service on port 993 is not available.
+<EM>Alpine</EM> will not subsequently retry a connection
+on port 143 if <SAMP>/ssl</SAMP> is specified.
+<P>
+
+<DT> <A NAME="unselect-will-not-advance"><EM>unselect-will-not-advance</EM></A>
+
+<DD> Normally, when the Unselect current message command (:) is typed when the
+current message is selected, the message will be unselected and the next
+message will become the current message.
+If this feature is set, the cursor will not advance to the next message.
+Instead, the current message will remain the current message after
+unselecting.
+<P>
+
+<DT> <A NAME="use-current-dir"><EM>use-current-dir</EM></A>
+
+<DD> This feature controls an aspect of several commands. If set, your
+"current working directory" will be used instead of your home directory
+for all of the following operations:
+
+<UL>
+<LI> <EM>Export</EM> in the "Folder Index" and "Message Text" screens
+<LI> Attachment <EM>Save</EM> in the "Message Text" and "Attachment Text" screens
+<LI> <EM>^R</EM> file inclusion in the Composer
+<LI> <EM>^J</EM> file attachment in the Composer
+</UL>
+<P>
+This feature is displayed as &quot;Use Current Directory&quot;.
+<P>
+
+<DT> <A NAME="use-function-keys"><EM>use-function-keys</EM></A>
+
+<DD> This feature specifies that <EM>Alpine</EM> will
+respond to function keys instead of
+the normal single-letter commands. In this mode, the key menus at the
+bottom of each screen will show function key designations instead of the
+normal mnemonic key.
+<P>
+
+<DT> <A NAME="use-reg-start-rule"><EM>use-regular-startup-rule-for-stayopen-folders</EM></A>
+
+<DD> This feature affects which message is selected as the current message
+when you enter a
+<A HREF="#stay-open-folders">Stay Open</A> folder.
+<P>
+Normally, the starting position for an incoming folder (which most Stay Open
+folders will likely be) is controlled by the
+<A HREF="#incoming-startup-rule"><EM>Incoming-Startup-Rule</EM></A>.
+However, if a folder is a Stay Open folder, when you re-enter the folder
+after the first time the current message will be the same as it was when
+you left the folder.
+An exception is made if you use the TAB command to get to the folder.
+In that case, the message number will be incremented by one from what it
+was when you left the folder.
+<P>
+The above special behavior is thought to be useful.
+However, it is special and different from what you might at first expect.
+If this feature is set, then Stay Open folders will not be treated specially
+as far as the startup rule is concerned.
+<P>
+
+<DT> <A NAME="use-resent-to-in-rules"><EM>use-resent-to-in-rules</EM></A>
+
+<DD> This feature is turned off by default because turning it on causes problems
+with some deficient IMAP servers.
+In <EM>Alpine</EM> Filters and other types of Rules, if the
+Pattern
+contains a To header pattern and this feature is turned on,
+then a check is made in the message to see
+if a Resent-To header is present, and that is used instead of the To header.
+If this feature is not turned on, then the regular To header will always
+be used.
+<P>
+
+<DT> <A NAME="use-sender-not-x-sender"><EM>use-sender-not-x-sender</EM></A>
+
+<DD> Normally <EM>Alpine</EM> on Unix adds a header line labeled <EM>X-X-Sender</EM>,
+if the sender is different from the <EM>From:</EM> line.
+
+<P>
+The standard specifies that this header
+line should be labeled <EM>Sender</EM>, not <EM>X-X-Sender</EM>.
+Setting this feature causes
+<EM>Sender</EM> to be used instead of <EM>X-X-Sender</EM>. The standard also states
+that the data associated with this header field should not be used as a Reply address.
+Unfortunately, certain implementations of mail list management servers will use the
+Sender address for such purposes. These implementations often even recognize the
+<EM>X-Sender</EM> fields as being equivalent to the <EM>Sender</EM> field, and use it
+if present. This is why <EM>Alpine</EM> defaults to <EM>X-X-Sender</EM>.
+<P>
+Note, <EM>PC-Alpine</EM> always adds
+either an <EM>X-X-Sender</EM> line if there is an open, remote mailbox, or an
+<EM>X-Warning: UNAuthenticated User</EM> otherwise
+
+<P>
+This feature is displayed as &quot;Use Sender Instead of X-X-Sender&quot;.
+<P>
+
+<DT> <A NAME="use-subshell-for-suspend"><EM>use-subshell-for-suspend</EM></A>
+
+<DD> This feature affects <EM>Alpine</EM>'s behavior when process suspension
+is enabled and then activated via the <EM>^Z</EM> key.
+<EM>Alpine</EM> suspension allows one to
+temporarily interact with the operating system command "shell" without
+quitting <EM>Alpine</EM>,
+and then subsequently resume the still-active <EM>Alpine</EM> session.
+<P>
+
+When the <EM>enable-suspend</EM> feature is set and subsequently the
+<EM>^Z</EM> key is pressed,
+<EM>Alpine</EM> will normally suspend itself and return temporary
+control to <EM>Alpine</EM>'s parent shell process.
+However, if this feature is set, <EM>Alpine</EM> will instead create an
+inferior subshell process.
+This is useful when the parent process is not intended to be used
+interactively.
+Examples include invoking <EM>Alpine</EM> via the <CODE>-e</CODE> argument
+of the Unix <EM>xterm</EM> program, or via a menu system.
+<P>
+
+Note that one typically resumes a suspended <EM>Alpine</EM> by entering the Unix
+<EM>fg</EM> command, but if this feature is set, it will be necessary to enter
+the <EM>exit</EM> command instead.
+<P>
+
+<DT> <A NAME="use-system-translation"><EM>use-system-translation</EM></A>
+
+<DD> UNIX <EM>Alpine</EM> only. <EM>Alpine</EM> normally uses its own internal software to convert between the multi-byte
+representation of characters and the Unicode representation of those
+same characters
+( see the section on <A HREF="low-level.html#char-set">International Character Sets</EM></A>).
+It converts from the multi-byte characters your keyboard produces to Unicode,
+and from Unicode to the multi-byte characters your display expects.
+Alpine also uses its own internal software to decide how much space on
+the screen a particular Unicode character will occupy.
+
+<P>
+Setting this feature tells <EM>Alpine</EM> to use the system-supplied routines to
+perform these tasks instead.
+In particular there are three tasks and three system routines that will
+be used for these tasks.
+
+<P>
+To convert from multi-byte to Unicode the routine
+
+<P>
+<CENTER><SAMP>mbstowcs</SAMP></CENTER>
+<P>
+
+is used.
+To convert from Unicode to multi-byte the routine
+
+<P>
+<CENTER><SAMP>wcrtomb</SAMP></CENTER>
+<P>
+
+is used.
+And to find the screen width a particular Unicode character will
+occupy the routine used is
+
+<P>
+<CENTER><SAMP>wcwidth</SAMP></CENTER>
+<P>
+
+This feature has been only lightly tested.
+The internal routines should normally be used unless you run into
+a problem that you think may be solved by using the system routines.
+Note that your environment needs to be set up for these
+routines to work correctly.
+In particular, the LANG or LC_CTYPE variable in your environment will
+need to be set.
+<P>
+
+<DT> <A NAME="vertical-folder-list"><EM>vertical-folder-list</EM></A>
+
+<DD> This feature controls an aspect of <EM>Alpine</EM>'s FOLDER LIST screen. If set,
+the folders will be listed alphabetically down the columns rather
+than across the columns as is the default.
+
+<P>
+This feature is displayed as &quot;Use Vertical Folder List&quot;.
+<P>
+
+<DT> <A NAME="warn-if-blank-subject"><EM>warn-if-blank-subject</EM></A>
+
+<DD> This feature affects <EM>Alpine</EM>'s behavior when you send a message being
+composed.
+If this option is set, <EM>Alpine</EM> will check to see if the message about to be sent
+has a subject or not.
+If not, you will be asked if you want to send the message anyway.
+<P>
+
+<DT> <A NAME="warn-if-blank-to-and-cc-and-newsgroups"><EM>warn-if-blank-to-and-cc-and-newsgroups</EM></A>
+
+<DD> This feature affects <EM>Alpine</EM>'s behavior when you send a message being
+composed.
+If this option is set, <EM>Alpine</EM> will check to see if the message about to be sent
+has either a To address, a Cc address, or a Newsgroup.
+If none of these is set,
+you will be asked if you want to send the message anyway.
+<P>
+This feature is closely related to
+<A HREF="#fcc-only-without-confirm"><EM>fcc-only-without-confirm</EM></A>.
+<EM>Alpine</EM> will normally ask if you want to copy a message only to the Fcc.
+This feature also applies to cases where there is a Bcc but still no To, Cc,
+or Newsgroup.
+If the Fcc-Only-Without-Confirm feature is set and you are sending a
+message with only an Fcc, then you won't be asked about sending with
+a blank To and Cc and Newsgroups header even if this feature is set.
+Similarly, if you have already been asked if you want to send to the Fcc
+only and you have answered Yes, then you won't be asked again about sending with
+blank To, Cc, and Newsgroups headers even if this feature is set.
+
+<P>
+
+</DL>
+<P>
+
+<H2><A NAME="hidden-config">Hidden Config Variables and Features</A></H2>
+
+There are several configuration variables and features which are normally hidden
+from the user. That is, they don't appear on any of the configuration
+screens. Some of these are suppressed because they are intended to be used
+by system administrators, and in fact may only be set in system-wide
+configuration files. Others are available to users but are thought to be
+of such little value to most users that their presence on the Config
+screens would cause more confusion than help.
+Others are hidden in the Setup/Config screen because they are normally
+configured in one of the other configuration screens. For example, all
+of the colors are hidden because the normal way to configure colors is
+through Setup/Colors not Setup/Config.
+You may set the feature <A HREF="#expose-hidden-config">expose-hidden-config</A>
+to cause most of these hidden variables and features to show up at the bottom
+of the Setup/Config screen.
+
+<H3>Hidden Variables Not Settable by Users</H3>
+
+These variables are settable only in system-wide configuration files.
+
+<UL>
+<LI> <A HREF="#bugs-add">bugs-additional-data</A>
+<LI> <A HREF="#bugs">bugs-address</A>
+<LI> <A HREF="#bugs">bugs-fullname</A>
+<LI> <A HREF="#forced-abook">forced-abook-entry</A>
+<LI> <A HREF="#kblock-count">kblock-passwd-count</A>
+<LI> <A HREF="#bugs">local-address</A>
+<LI> <A HREF="#bugs">local-fullname</A>
+<LI> <A HREF="#mail-directory">mail-directory</A>
+<LI> <A HREF="#standard-printer">standard-printer</A>
+<LI> <A HREF="#bugs">suggest-address</A>
+<LI> <A HREF="#bugs">suggest-fullname</A>
+</UL>
+
+<H3>Hidden Variables Which are Settable by Users</H3>
+
+These variables are not shown to users but are settable by means
+of hand editing the personal configuration file. This first group
+is usually maintained by <EM>Alpine</EM> and there will usually
+be no reason to edit them by hand.
+
+<UL>
+<LI> <A HREF="#last-version-used">last-version-used</A>
+<LI> <A HREF="#patterns-filters2">patterns-filters2</A>
+<LI> <A HREF="#patterns-indexcolors">patterns-indexcolors</A>
+<LI> <A HREF="#patterns-roles">patterns-roles</A>
+<LI> <A HREF="#patterns-scores2">patterns-scores2</A>
+<LI> <A HREF="#remote-abook-metafile">remote-abook-metafile</A>
+</UL>
+<P>
+
+This group is usually correct but may be changed by system managers or
+users in special cases.
+
+<UL>
+<LI> <A HREF="#disable-these-auths">disable-these-authenticators</A>
+<LI> <A HREF="#disable-these-drivers">disable-these-drivers</A>
+<LI> <A HREF="#last-time">last-time-prune-questioned</A>
+<LI> <A HREF="#new-version-threshold">new-version-threshold</A>
+<LI> <A HREF="#remote-abook-history">remote-abook-history</A>
+<LI> <A HREF="#remote-abook-validity">remote-abook-validity</A>
+<LI> <A HREF="#rsh-command">rsh-command</A>
+<LI> <A HREF="#rsh-open-timeout">rsh-open-timeout</A>
+<LI> <A HREF="#rsh-path">rsh-path</A>
+<LI> <A HREF="#sendmail-path">sendmail-path</A>
+<LI> <A HREF="#ssh-command">ssh-command</A>
+<LI> <A HREF="#ssh-open-timeout">ssh-open-timeout</A>
+<LI> <A HREF="#ssh-path">ssh-path</A>
+<LI> <A HREF="#tcp-open-timeout">tcp-open-timeout</A>
+<LI> <A HREF="#tcp-query-timeout">tcp-query-timeout</A>
+<LI> <A HREF="#tcp-read-warning-timeout">tcp-read-warning-timeout</A>
+<LI> <A HREF="#tcp-write-warning-timeout">tcp-write-warning-timeout</A>
+<LI> <A HREF="#use-function-keys">use-function-keys</A>
+</UL>
+<P>
+
+System managers are usually interested in setting these in the system-wide
+configuration files, though users may set them if they wish.
+
+<UL>
+<LI> <A HREF="#operating-dir">operating-dir</A>
+<LI> <A HREF="#user-input">user-input-timeout</A>
+</UL>
+<P>
+
+<H3>Hidden Features Which are Settable by Users</H3>
+
+These are <EM>features</EM> (as opposed to variables) which users or system
+administrators may set. Some of them only make sense for administrators.
+To turn these on manually, the configuration file should be edited and the
+feature added to the <EM>feature-list</EM> variable.
+You may set the feature <A HREF="#expose-hidden-config">expose-hidden-config</A>
+to cause these hidden features to show up in the Setup/Config screen.
+They will be at the bottom of the screen.
+
+<UL>
+<LI> <A HREF="#disable-config-cmd">disable-config-cmd</A>
+<LI> <A HREF="#disable-kblock">disable-keyboard-lock-cmd</A>
+<LI> <A HREF="#disable-password-cmd">disable-password-cmd</A>
+<LI> <A HREF="#disable-pipes-in-sigs">disable-pipes-in-sigs</A>
+<LI> <A HREF="#disable-pipes-in-templates">disable-pipes-in-templates</A>
+<LI> <A HREF="#disable-roles-setup-cmd">disable-roles-setup-cmd</A>
+<LI> <A HREF="#disable-roles-sig-edit">disable-roles-sig-edit</A>
+<LI> <A HREF="#disable-roles-template-edit">disable-roles-template-edit</A>
+<LI> <A HREF="#disable-setlocale-collate">disable-setlocale-collate</A>
+<LI> <A HREF="#disable-shared-namespaces">disable-shared-namespaces</A>
+<LI> <A HREF="#disable-signature-edit-cmd">disable-signature-edit-cmd</A>
+</UL>
+<P>
+
+<H2><A NAME="ret-var">Retired Variables and Features</A></H2>
+
+Variables and features that are no longer used by the current <EM>Alpine</EM> version.
+When an obsolete variable is encountered, its value is applied to any new
+corresponding setting.
+The replaced values include:
+<P>
+
+<DL COMPACT>
+
+<DT> <EM>character-set</EM>
+
+<DD> Replaced by three separate variables:
+<EM>display-character-set</EM>,
+<EM>keyboard-character-set</EM>, and
+<EM>posting-character-set</EM>.
+
+<DT> <EM>compose-mime</EM>
+
+<DT> <EM>elm-style-save</EM>
+
+<DD> Replaced by <EM>saved-msg-name-rule</EM>
+
+<DT> <EM>feature-level</EM>
+
+<DD> Replaced by <EM>feature-list.</EM>
+
+<DT> <EM>header-in-reply</EM>
+
+<DD> Replaced by <EM>include-header-in-reply</EM> in the
+<EM>feature-list.</EM>
+
+<DT> <EM>old-style-reply</EM>
+
+<DD> Replaced by <EM>signature-at-bottom</EM> in the
+<EM>feature-list.</EM>
+
+<DT> <EM>use-old-unix-format-write</EM>
+
+<DD> No replacement.
+
+<DT> <EM>patterns</EM>
+
+<DD> Replaced by four separate patterns variables:
+<EM>patterns-roles</EM>,
+<EM>patterns-filters</EM>,
+<EM>patterns-scores</EM>, and
+<EM>patterns-indexcolors</EM>.
+Since then, <EM>patterns-filters</EM> has also become obsolete and is replaced
+by <EM>patterns-filters2</EM>; <EM>patterns-scores</EM> is replaced by
+<EM>patterns-scores2</EM>.
+
+<DT> <EM>save-by-sender</EM>
+
+<DD> Replaced by <EM>saved-msg-name-rule.</EM>
+
+<DT> <EM>show-all-characters</EM>
+
+<DD> No replacement, it always works this way now.
+
+</DL>
+
+<P>
+
+<H2><A NAME="index-tokens"></A>Tokens for Index and Replying</H2>
+
+This set of special tokens may be used in the
+<A HREF="#index-format"><EM>index-format</EM></A> option,
+in the <A HREF="#reply-leadin"><EM>reply-leadin</EM></A> option,
+in signature files,
+in template files used in
+<A HREF="#role-config"><EM>roles</EM></A>,
+and in the folder name that is the target of a Filter Rule.
+Some of them aren't available in all situations.
+<P>
+The tokens are used as they appear below for the <EM>Index-Format</EM>
+option, but they must be surrounded by underscores for the
+<EM>Reply-Leadin</EM> option, in signature and template files,
+and in the target of Filter Rules.
+<P>
+
+<H3><EM>Tokens Available for all Cases (except Filter Rules)</EM></H3>
+
+<DL>
+<DT>SUBJECT</DT>
+<DD>
+This token represents the Subject the sender gave the message.
+Alternatives for use in the index screen are
+SUBJKEY, SUBJKEYINIT, SUBJECTTEXT, SUBJKEYTEXT, and SUBJKEYINITTEXT.
+You may color the subject text in the MESSAGE INDEX screen differently by using the
+<A HREF="#index-subject-color">Index Subject Color</A>
+and the
+<A HREF="#index-opening-color">Index Opening Color</A>.
+options available from
+the Setup Kolor screen.
+</DD>
+
+<DT>FROM</DT>
+<DD>
+This token represents the personal name (or email address if the name
+is unavailable) of the person specified in the message's &quot;From:&quot;
+header field.
+You may color the from text in the MESSAGE INDEX screen differently by using the
+<A HREF="#index-from-color">Index From Color</A>
+option available from the Setup Kolor screen.
+</DD>
+
+<DT>ADDRESS</DT>
+<DD>
+This is similar to the &quot;FROM&quot; token, only it is always the
+email address, never the personal name.
+For example, &quot;mailbox@domain&quot;.
+</DD>
+
+<DT>MAILBOX</DT>
+<DD>
+This is the same as the &quot;ADDRESS&quot; except that the
+domain part of the address is left off.
+For example, &quot;mailbox&quot;.
+</DD>
+
+<DT>SENDER</DT>
+<DD>
+This token represents the personal name (or email address) of the person
+listed in the message's &quot;Sender:&quot; header field.
+</DD>
+
+<DT>TO</DT>
+<DD>
+This token represents the personal names (or email addresses if the names
+are unavailable) of the persons specified in the
+message's &quot;To:&quot; header field.
+</DD>
+
+<DT>NEWSANDTO</DT>
+<DD>
+This token represents the newsgroups from the
+message's &quot;Newsgroups:&quot; header field <EM>and</EM>
+the personal names (or email addresses if the names
+are unavailable) of the persons specified in the
+message's &quot;To:&quot; header field.
+</DD>
+
+<DT>TOANDNEWS</DT>
+<DD>
+Same as &quot;NEWSANDTO&quot; except in the opposite order.
+</DD>
+
+<DT>NEWS</DT>
+<DD>
+This token represents the newsgroups from the
+message's &quot;Newsgroups:&quot; header field.
+</DD>
+
+<DT>CC</DT>
+<DD>
+This token represents the personal names (or email addresses if the names
+are unavailable) of the persons specified in the
+message's &quot;Cc:&quot; header field.
+</DD>
+
+<DT>RECIPS</DT>
+<DD>
+This token represents the personal names (or email addresses if the names
+are unavailable) of the persons specified in both the
+message's &quot;To:&quot; header field and
+the message's &quot;Cc:&quot; header field.
+</DD>
+
+<DT>NEWSANDRECIPS</DT>
+<DD>
+This token represents the newsgroups from the
+message's &quot;Newsgroups:&quot; header field <EM>and</EM>
+the personal names (or email addresses if the names
+are unavailable) of the persons specified in the
+message's &quot;To:&quot; and &quot;Cc:&quot; header fields.
+</DD>
+
+<DT>RECIPSANDNEWS</DT>
+<DD>
+Same as &quot;NEWSANDRECIPS&quot; except in the opposite order.
+</DD>
+
+<DT>INIT</DT>
+<DD>
+This token represents the initials from the personal name
+of the person specified in the message's &quot;From:&quot;
+header field.
+If there is no personal name, it is blank.
+</DD>
+
+<DT>DATE</DT>
+<DD>
+This token represents the date on which the message was sent, according
+to the &quot;Date&quot; header field.
+It has the format MMM DD. For example, &quot;Oct 23&quot;.
+The feature
+<A HREF="#convert-dates-to-localtime"><EM>convert-dates-to-localtime</EM></A>,
+which adjusts for the timezone the message was sent from,
+may have an affect on the value of this token as well as the values of
+all of the other DATE or TIME tokens.
+Some of the DATE and TIME tokens are displayed in a locale-specific
+way unless the option
+<A HREF="#disable-index-locale-dates"><EM>Disable-Index-Locale-Dates</EM></A> is set.
+</DD>
+
+<DT>SMARTDATE</DT>
+<DD>
+This token represents the date on which the message was sent, according
+to the &quot;Date&quot; header field.
+It is &quot;Today&quot; if the message was sent today,
+&quot;Yesterday&quot; for yesterday,
+&quot;Wednesday&quot; if it was last Wednesday, and so on. If the
+message is from last year and is more than six months old it includes the year, as well.
+There is no adjustment made for different time zones, so you'll get
+the day the message was sent according to the time zone the sender
+was in.
+See the SMARTDATE alternatives below, as well.
+</DD>
+
+<DT>SMARTTIME</DT>
+<DD>
+This token represents the most relevant elements of the date on which
+the message was sent (according to the &quot;Date&quot; header field),
+in a compact form. If the message was sent today, only the time is used
+(e.g. &quot;9:22am&quot;, &quot;10:07pm&quot;); if it was sent during
+the past week, the day of the week and the hour are used
+(e.g. &quot;Wed09am&quot;, &quot;Thu10pm&quot;); other dates are
+given as date, month, and year (e.g. &quot;23Aug00&quot;,
+&quot;9Apr98&quot;).
+There is no adjustment made for different time zones, so you'll get
+the day/time the message was sent according to the time zone the sender
+was in.
+</DD>
+
+<DT>SMARTDATETIME</DT>
+<DD>
+This is a combination of SMARTDATE and SMARTTIME.
+It is SMARTDATE unless the SMARTDATE value is &quot;Today&quot;, in which
+case it is SMARTTIME.
+See the SMARTDATETIME alternatives below, as well.
+</DD>
+
+<DT>DATEISO</DT>
+<DD>
+This token represents the date on which the message was sent, according
+to the &quot;Date&quot; header field.
+It has the format YYYY-MM-DD. For example, &quot;1998-10-23&quot;.
+</DD>
+
+<DT>SHORTDATEISO</DT>
+<DD>
+This token represents the date on which the message was sent, according
+to the &quot;Date&quot; header field.
+It has the format YY-MM-DD. For example, &quot;98-10-23&quot;.
+</DD>
+
+<DT>SHORTDATE1</DT>
+<DD>
+This token represents the date on which the message was sent, according
+to the &quot;Date&quot; header field.
+It has the format MM/DD/YY. For example, &quot;10/23/98&quot;.
+</DD>
+
+<DT>SHORTDATE2</DT>
+<DD>
+This token represents the date on which the message was sent, according
+to the &quot;Date&quot; header field.
+It has the format DD/MM/YY. For example, &quot;23/10/98&quot;.
+</DD>
+
+<DT>SHORTDATE3</DT>
+<DD>
+This token represents the date on which the message was sent, according
+to the &quot;Date&quot; header field.
+It has the format DD.MM.YY. For example, &quot;23.10.98&quot;.
+</DD>
+
+<DT>SHORTDATE4</DT>
+<DD>
+This token represents the date on which the message was sent, according
+to the &quot;Date&quot; header field.
+It has the format YY.MM.DD. For example, &quot;98.10.23&quot;.
+</DD>
+
+<DT>LONGDATE</DT>
+<DD>
+This token represents the date on which the message was sent, according
+to the &quot;Date&quot; header field.
+It has the format MMM DD, YYYY. For example, &quot;Oct 23, 1998&quot;.
+</DD>
+
+<DT>SMARTDATE alternatives</DT>
+<DD>
+There are several versions of SMARTDATE which are all the same except
+for the way they format dates far in the past.
+SMARTDATE formats the date using the information from your locale settings
+to format the date string. It may end up formatting dates so that they look
+like DATEISO tokens, or SHORTDATE2 tokens, or something else entirely.
+The feature
+<A HREF="#convert-dates-to-localtime"><EM>convert-dates-to-localtime</EM></A>
+may have an affect on the values of these tokens.
+If you want more control you may use one of the following.
+ <DL>
+ <DT>SMARTDATE</DT> <DD>If the option
+<A HREF="#disable-index-locale-dates"><EM>Disable-Index-Locale-Dates</EM></A> is not set
+then this will be locale specific. Control this with the
+LC_TIME locale setting on a UNIX system. On Windows
+the Regional Options control panel may be used to set the Short date
+format. At the programming level, the strftime routine is what <EM>Alpine</EM>
+uses to print the date.
+If the Disable-Index-Locale-Dates option is set then this is equivalent
+to SMARTDATES1.</DD>
+ <DT>SMARTDATEISO</DT> <DD>DATEISO format. See text above.</DD>
+ <DT>SMARTDATESHORTISO</DT> <DD>SHORTDATEISO format.</DD>
+ <DT>SMARTDATES1</DT> <DD>SHORTDATE1 format.</DD>
+ <DT>SMARTDATES2</DT> <DD>SHORTDATE2 format.</DD>
+ <DT>SMARTDATES3</DT> <DD>SHORTDATE3 format.</DD>
+ <DT>SMARTDATES4</DT> <DD>SHORTDATE4 format.</DD>
+ </DL>
+</DD>
+
+<DT>SMARTDATETIME alternatives</DT>
+<DD>
+There are several versions of SMARTDATETIME which are all very similar.
+The ones which end in 24 use a 24-hour clock for Today's messages instead
+of a 12-hour clock.
+The other variation is
+for the way they format dates far in the past.
+SMARTDATETIME and SMARTDATETIME24 format the date using the information from your locale settings
+to format the date string. It may end up formatting dates so that they look
+like DATEISO tokens, or SHORTDATE2 tokens, or something else entirely.
+The feature
+<A HREF="#convert-dates-to-localtime"><EM>convert-dates-to-localtime</EM></A>
+may have an affect on the values of these tokens.
+The possible choices are:
+ <DL>
+ <DT>SMARTDATETIME</DT> <DD>Locale specific. Control this with the
+LC_TIME locale setting on a UNIX system. On Windows
+the Regional Options control panel may be used to set the Short date
+format. At the programming level, the strftime routine is what <EM>Alpine</EM>
+uses to print the date.</DD>
+ <DT>SMARTDATETIME</DT> <DD>If the option
+<A HREF="#disable-index-locale-dates"><EM>Disable-Index-Locale-Dates</EM></A> is not set
+then this will be locale specific. Control this with the
+LC_TIME locale setting on a UNIX system. On Windows
+the Regional Options control panel may be used to set the Short date
+format. At the programming level, the strftime routine is what <EM>Alpine</EM>
+uses to print the date.
+If the Disable-Index-Locale-Dates option is set then this is equivalent
+to SMARTDATETIMES1.</DD>
+ <DT>SMARTDATETIME24</DT> <DD>Use TIME24 for Today</DD>
+ <DT>SMARTDATETIMEISO</DT> <DD>DATEISO format. See text above.</DD>
+ <DT>SMARTDATETIMEISO24</DT> <DD>Use TIME24 for Today</DD>
+ <DT>SMARTDATETIMESHORTISO</DT> <DD>SHORTDATEISO format.</DD>
+ <DT>SMARTDATETIMESHORTISO24</DT> <DD>Use TIME24 for Today</DD>
+ <DT>SMARTDATETIMES1</DT> <DD>SHORTDATE1 format.</DD>
+ <DT>SMARTDATETIMES124</DT> <DD>Use TIME24 for Today</DD>
+ <DT>SMARTDATETIMES2</DT> <DD>SHORTDATE2 format.</DD>
+ <DT>SMARTDATETIMES224</DT> <DD>Use TIME24 for Today</DD>
+ <DT>SMARTDATETIMES3</DT> <DD>SHORTDATE3 format.</DD>
+ <DT>SMARTDATETIMES324</DT> <DD>Use TIME24 for Today</DD>
+ <DT>SMARTDATETIMES4</DT> <DD>SHORTDATE4 format.</DD>
+ <DT>SMARTDATETIMES424</DT> <DD>Use TIME24 for Today</DD>
+ </DL>
+</DD>
+
+<DT>DAYDATE</DT>
+<DD>
+This token represents the date on which the message was sent, according
+to the &quot;Date&quot; header field.
+It looks like &quot;Sat, 23 Oct 1998&quot;.
+This token is never converted in any locale-specific way.
+</DD>
+
+<DT>PREFDATE</DT>
+<DD>
+This token represents the date on which the message was sent, according
+to the &quot;Date&quot; header field.
+It is your operating system's idea of the preferred date representation for the current locale.
+Internally it uses the %x version of the date from the strftime routine.
+</DD>
+
+<DT>PREFTIME</DT>
+<DD>
+This token represents the time at which the message was sent, according
+to the &quot;Date&quot; header field.
+It is the preferred time representation for the current locale.
+Internally it uses the %X version of the time from the strftime routine.
+</DD>
+
+<DT>PREFDATETIME</DT>
+<DD>
+This token represents the date and time at which the message was sent, according
+to the &quot;Date&quot; header field.
+It is the preferred date and time representation for the current locale.
+Internally it uses the %c version of the time from the strftime routine.
+</DD>
+
+<DT>DAY</DT>
+<DD>
+This token represents the day of the month on which the message was sent,
+according to the &quot;Date&quot; header field.
+For example, &quot;23&quot; or &quot;9&quot;.
+</DD>
+
+<DT>DAY2DIGIT</DT>
+<DD>
+This token represents the day of the month on which the message was sent,
+according to the &quot;Date&quot; header field.
+For example, &quot;23&quot; or &quot;09&quot;.
+It is always 2 digits.
+</DD>
+
+<DT>DAYORDINAL</DT>
+<DD>
+This token represents the ordinal number which is the day of
+the month on which the message was sent,
+according to the &quot;Date&quot; header field.
+For example, &quot;23rd&quot; or &quot;9th&quot;.
+</DD>
+
+<DT>DAYOFWEEK</DT>
+<DD>
+This token represents the day of the week on which the message was sent,
+according to the &quot;Date&quot; header field.
+For example, &quot;Sunday&quot; or &quot;Wednesday&quot;.
+</DD>
+
+<DT>DAYOFWEEKABBREV</DT>
+<DD>
+This token represents the day of the week on which the message was sent,
+according to the &quot;Date&quot; header field.
+For example, &quot;Sun&quot; or &quot;Wed&quot;.
+</DD>
+
+<DT>MONTHABBREV</DT>
+<DD>
+This token represents the month the message was sent, according
+to the &quot;Date&quot; header field.
+For example, &quot;Oct&quot;.
+</DD>
+
+<DT>MONTHLONG</DT>
+<DD>
+This token represents the month in which the message was sent, according
+to the &quot;Date&quot; header field.
+For example, &quot;October&quot;.
+</DD>
+
+<DT>MONTH</DT>
+<DD>
+This token represents the month in which the message was sent, according
+to the &quot;Date&quot; header field.
+For example, &quot;10&quot; or &quot;9&quot;.
+</DD>
+
+<DT>MONTH2DIGIT</DT>
+<DD>
+This token represents the month in which the message was sent, according
+to the &quot;Date&quot; header field.
+For example, &quot;10&quot; or &quot;09&quot;.
+It is always 2 digits.
+</DD>
+
+<DT>YEAR</DT>
+<DD>
+This token represents the year the message was sent, according
+to the &quot;Date&quot; header field.
+For example, &quot;1998&quot; or &quot;2001&quot;.
+</DD>
+
+<DT>YEAR2DIGIT</DT>
+<DD>
+This token represents the year the message was sent, according
+to the &quot;Date&quot; header field.
+For example, &quot;98&quot; or &quot;01&quot;.
+It is always 2 digits.
+</DD>
+
+<DT>TIME24</DT>
+<DD>
+This token represents the time at which the message was sent, according
+to the &quot;Date&quot; header field.
+There is no adjustment made for different time zones, so you'll get
+the time the message was sent according to the time zone the sender
+was in.
+It has the format HH:MM. For example, &quot;17:28&quot;.
+</DD>
+
+<DT>TIME12</DT>
+<DD>
+This token represents the time at which the message was sent, according
+to the &quot;Date&quot; header field.
+This time is for a 12 hour clock.
+It has the format HH:MMpm.
+For example, &quot;5:28pm&quot; or &quot;11:13am&quot;.
+</DD>
+
+<DT>TIMEZONE</DT>
+<DD>
+This token represents the numeric timezone from
+the &quot;Date&quot; header field.
+It has the format [+-]HHMM. For example, &quot;-0800&quot;.
+</DD>
+
+</DL>
+
+<P>
+<H3><EM>Tokens Available Only for Index-Format</EM></H3>
+
+<DL>
+<DT>MSGNO</DT>
+<DD>
+This token represents the message's current position in the folder which,
+of course, may change as the folder is sorted or new mail arrives.
+</DD>
+
+<DT>STATUS</DT>
+<DD>
+This token represents a three character wide field displaying various
+aspects of the message's state.
+The first character is either blank,
+a '*' for message marked Important, or a '+' indicating a message
+addressed directly to you (as opposed to your having received it via a
+mailing list, for example).
+When the feature
+<A HREF="#mark-for-cc"><EM>mark-for-cc</EM></A>
+is set, if the first character would have been
+blank then it will instead be a '-' if the message is cc'd to you.
+The second character is typically blank,
+though the arrow cursor may occupy it if either the
+<A HREF="#assume-slow-link"><EM>assume-slow-link</EM></A>
+or the
+<A HREF="#force-arrow-cursor"><EM>force-arrow-cursor</EM></A> feature
+is set (or you actually are on a slow link).
+The third character is either D (Deleted),
+A (Answered),
+N (New), or blank.
+<P>
+If you are using a threaded view of the index and this message is at the
+top of a collapsed portion of a thread,
+then this token refers to all of the messages in the collapsed portion of
+the thread instead of just the top message.
+The first character will be a '*' if <EM>any</EM> of the messages in the thread
+are marked Important, else a '+' if any of the messages are addressed
+to you, else a '-' if any of the messages are cc'd to you.
+The third character will be a 'D' if <EM>all</EM> of the messages
+in the collapsed thread are marked deleted,
+an 'A' if <EM>all</EM> of the messages
+in the collapsed thread are marked answered,
+it will be an 'N' if any of
+the messages are undeleted and unseen, and it will be blank otherwise.
+</DD>
+
+<DT>FULLSTATUS</DT>
+<DD>
+This token represents a less abbreviated alternative
+to the &quot;STATUS&quot; token.
+It is six characters wide.
+The first character is '+', '-', or blank, the
+second blank, the third either '*' or blank, the fourth
+N or blank,
+the fifth A
+or blank, and the sixth character is
+either D or
+blank.
+<P>
+If you are using a threaded view of the index and this message is at the
+top of a collapsed portion of a thread,
+then this token refers to all of the messages in the collapsed portion of
+the thread instead of just the top message.
+The first character is '+', '-', or blank depending on whether <EM>any</EM>
+of the messages in the collapsed thread are addressed to you or cc'd to you.
+The third character will be '*' if any of the messages are marked
+Important.
+The fourth character will be 'N' if all of the messages in the thread
+are New, else 'n' if some of the messages in the thread are New, else blank.
+The fifth character will be 'A' or 'a' or blank, and the sixth character
+will be 'D' or 'd' or blank.
+</DD>
+
+<DT>IMAPSTATUS</DT>
+<DD>
+This token represents an even less abbreviated alternative to the
+&quot;STATUS&quot; token.
+It differs from &quot;FULLSTATUS&quot; in only the fourth character which is
+an 'N' if the message is new to this folder since the last time
+it was opened <EM>and</EM> it has not been viewed, an 'R' (Recent) if the message
+is new to the folder and has been viewed, a 'U' (Unseen) if the message is not
+new to the folder since it was last opened <EM>but</EM> has not been
+viewed, or a blank if the message has been in the folder since it was
+last opened and has been viewed.
+<P>
+If you are using a threaded view of the index and this message is at the
+top of a collapsed portion of a thread,
+then the fourth character will be
+'N' if all of the messages in the thread are unseen and recent;
+else 'n' if some of the messages in the thread are unseen and recent;
+else 'U' if all of the messages in the thread are unseen and not recent;
+else 'u' if some of the messages in the thread are unseen and not recent;
+else 'R' if all of the messages in the thread are seen and recent;
+else 'r' if some of the messages in the thread are seen and recent;
+else blank.
+</DD>
+
+<DT>SHORTIMAPSTATUS</DT>
+<DD>
+This is the same as the last four of the six characters of IMAPSTATUS,
+so the '+' To Me information will be missing.
+</DD>
+
+<DT>SIZE</DT>
+<DD>
+This token represents the total size, in bytes, of the message.
+If a &quot;K&quot; (Kilobyte)
+follows the number, the size is approximately 1,000
+times that many bytes (rounded to the nearest 1,000).
+If an &quot;M&quot; (Megabyte) follows the number, the size is approximately
+1,000,000 times that many bytes.
+Commas are not used in this field.
+This field is seven characters wide, including the enclosing parentheses.
+Sizes are rounded when &quot;K&quot; or &quot;M&quot; is present.
+The progression of sizes used looks like:
+
+<P>
+<CENTER><SAMP>0 1 ... 9999 10K ... 999K 1.0M ... 99.9M 100M ... 2000M</SAMP></CENTER>
+<P>
+</DD>
+
+<DT>SIZECOMMA</DT>
+<DD>
+This token represents the total size, in bytes, of the message.
+If a &quot;K&quot; (Kilobyte)
+follows the number, the size is approximately 1,000
+times that many bytes (rounded to the nearest 1,000).
+If an &quot;M&quot; (Megabyte) follows the number, the size is approximately
+1,000,000 times that many bytes.
+Commas are used if the number shown is 1,000 or greater.
+The SIZECOMMA field is one character wider than the SIZE field.
+Sizes are rounded when &quot;K&quot; or &quot;M&quot; is present.
+The progression of sizes used looks like:
+
+<P>
+<CENTER><SAMP>0 1 ... 99,999 100K ... 9,999K 10.0M ... 999.9M 1,000M ... 2,000M</SAMP></CENTER>
+<P>
+</DD>
+
+<DT>KSIZE</DT>
+<DD>
+This token represents the total size of the message, expressed in
+kilobytes or megabytes, as most appropriate.
+These are 1,024 byte kilobytes and 1,024 x 1,024 byte megabytes.
+The progression of sizes used looks like:
+
+<P>
+<CENTER><SAMP>0K 1K ... 1023K 1.0M ... 99.9M 100M ... 2047M</SAMP></CENTER>
+<P>
+</DD>
+
+<DT>SIZENARROW</DT>
+<DD>
+This token represents the total size, in bytes, of the message.
+If a &quot;K&quot; (Kilobyte)
+follows the number, the size is approximately 1,000
+times that many bytes.
+If an &quot;M&quot; (Megabyte) follows the number, the size is approximately
+1,000,000 times that many bytes.
+If a &quot;G&quot; (Gigabyte) follows the number, the size is approximately
+1,000,000,000 times that many bytes.
+This field uses only five characters of screen width, including the enclosing
+parentheses.
+The progression of sizes used looks like:
+
+<P>
+<CENTER><SAMP>0 1 ... 999 1K ... 99K .1M ... .9M 1M ... 99M .1G ... .9G 1G 2G</SAMP></CENTER>
+<P>
+</DD>
+
+<DT>DESCRIPSIZE</DT>
+<DD>
+This token is intended to represent a more useful description of the
+message than just its size, but it isn't very useful at this point.
+The plus sign in this view means there are attachments.
+Note that including this token in
+the &quot;Index-Format&quot; could slow down the
+display a little while <EM>Alpine</EM> collects the necessary information.
+</DD>
+
+<DT>SUBJKEY</DT>
+<DD>
+This token is the same as the SUBJECT token unless keywords are set for
+the message.
+In that case, a list of keywords enclosed in braces will be prepended to
+the subject of the message.
+Only those keywords that you have defined in your
+<A HREF="#keywords">Keywords</A> option
+in Setup/Config are considered in the list.
+In other words, keywords that have been set by some other means, perhaps
+by another email program, won't show up unless included in
+<A HREF="#keywords">Keywords</A>.
+Having this set in the Index-Format will also cause the keywords to be
+prepended to the subject in the MESSAGE TEXT screen.
+If you have given a keyword a nickname
+(<A HREF="#keywords"><EM>keywords</EM></A>), that nickname is displayed
+instead of the actual keyword.
+The <A HREF="#keyword-surrounding-chars"><EM>keyword-surrounding-chars</EM></A>
+option may be used to modify this token slightly.
+It is also possible to color keywords in the index using the
+Setup/Kolor screen.
+</DD>
+
+<DT>SUBJKEYINIT</DT>
+<DD>
+This token is the same as the SUBJKEY token except that instead of
+prepending a list of keywords to the subject, a list of first initials
+of keywords will be prepended instead.
+For example, if a message has the keywords <EM>Work</EM> and <EM>Now</EM>
+set (or Work and Now are the <EM>Alpine</EM> nicknames of keywords which are set)
+then the SUBJKEY token would cause a result like
+<P>
+<CENTER><SAMP>{Work Now} actual subject</SAMP></CENTER>
+<P>
+whereas the SUBJKEYINIT token would give
+<P>
+<CENTER><SAMP>{WN} actual subject</SAMP></CENTER>
+<P>
+Only those keywords that you have defined in your
+<A HREF="#keywords">Keywords</A> option
+in Setup/Config are considered in the list.
+In other words, keywords that have been set by some other means, perhaps
+by another email program, won't show up unless included in
+<A HREF="#keywords">Keywords</A>.
+The <A HREF="#keyword-surrounding-chars"><EM>keyword-surrounding-chars</EM></A>
+option may be used to modify this token slightly.
+It is also possible to color keywords in the index using the
+Setup/Kolor screen.
+</DD>
+
+<DT>SUBJECTTEXT</DT>
+<DD>
+Same as SUBJECT but if there is room in the Subject field for more text,
+the opening part of the text of the message is displayed after the subject.
+The time needed to fetch the text may cause a performance problem
+which can, of course, be avoided by using the SUBJECT version of
+the Subject instead.
+You may color this opening text differently by using the
+<A HREF="#index-opening-color"><EM>Index Opening Color</EM></A> option available from
+the Setup Kolor screen.
+You may adjust the characters that are displayed between the Subject and the
+opening text with the option
+<A HREF="#opening-text-separator-chars"><EM>Opening-Text-Separator-Chars</EM></A>.
+</DD>
+
+<DT>SUBJKEYTEXT</DT>
+<DD>
+Same as SUBJKEY but with the opening message text.
+</DD>
+
+<DT>SUBJKEYINITTEXT</DT>
+<DD>
+Same as SUBJKEYINIT but with the opening message text.
+</DD>
+
+<DT>OPENINGTEXT</DT>
+<DD>
+This is similar to SUBJECTTEXT.
+Instead of combining the Subject and the opening text in a single
+field in the index screen this token allows you to allocate a
+separate column just for the opening text of the message.
+The time needed to fetch this text may cause a performance problem.
+You may color this opening text differently by using the
+<A HREF="#index-opening-color"><EM>Index Opening Color</EM></A> option available from
+the Setup Kolor screen.
+</DD>
+
+<DT>OPENINGTEXTNQ</DT>
+<DD>
+This is very similar to OPENINGTEXT.
+The NQ stands for No Quotes.
+The only difference is that quoted text (lines beginning with &gt;) is deleted.
+For some messages this may be confusing.
+For example, a message might have a line preceding some quoted
+text that reads something like &quot;On May 8th person A said.&quot;
+That no longer makes sense after the quoted text is deleted and it
+will appear that person A said whatever the text after the quote
+is, even though that is really person B talking.
+</DD>
+
+<DT>KEY</DT>
+<DD>
+This is a space-delimited list of keywords that are set for the message.
+Only those keywords that you have defined in your
+<A HREF="#keywords">Keywords</A> option
+in Setup/Config are considered in the list.
+In other words, keywords that have been set by some other means, perhaps
+by another email program, won't show up unless included in
+<A HREF="#keywords">Keywords</A>.
+If you have given a keyword a nickname
+that nickname is displayed
+instead of the actual keyword.
+It is also possible to color keywords in the index using the
+Setup/Kolor screen.
+This token defaults to an arbitrary width of 5.
+You should set it to whatever width suits you using something
+like KEY(17) in the Index-Format.
+</DD>
+
+<DT>KEYINIT</DT>
+<DD>
+This is a list of keyword initials that are set for the message.
+If you have given a keyword a nickname
+the initial of that nickname
+is displayed instead of the initial of the actual keyword.
+It is also possible to color keyword initials in the index using the
+Setup/Kolor screen.
+This token defaults to an arbitrary width of 2.
+You should set it to whatever width suits you using something
+like KEYINIT(3) in the Index-Format.
+</DD>
+
+<DT>PRIORITY</DT>
+<DD>
+The X-Priority header is a non-standard header that is used in a
+somewhat standard way by many mail programs.
+<EM>Alpine</EM> expects the value of this header to be a digit with a value
+from 1 to 5, with 1 being the highest priority and 5 the lowest priority.
+Since this priority is something that the sender sets it is only an indication
+of the priority that the sender attaches to the mail and it is therefore almost
+totally unreliable for use as a filtering criterion.
+This token will display the numeric value of the priority if it is between
+1 and 5.
+It will be suppressed (blank) if the value is 3, which is normal priority.
+It is also possible to set the color of the PRIORITY field.
+By default the token is colored the same
+as the index line it is part of.
+You may set it to be another color with the
+<A HREF="#index-pri-color">Index Priority Colors</A> options available from
+the Setup Kolor screen.
+</DD>
+
+<DT>PRIORITYALPHA</DT>
+<DD>
+This is a more verbose interpretation of the X-Priority field.
+Once again nothing is displayed unless the value of the field
+is 1, 2, 4, or 5.
+The values displayed for those values are:
+<P>
+<TABLE>
+<TR> <TD>1</TD> <TD>Highest</TD> </TR>
+<TR> <TD>2</TD> <TD>High</TD> </TR>
+<TR> <TD>4</TD> <TD>Low</TD> </TR>
+<TR> <TD>5</TD> <TD>Lowest</TD> </TR>
+</TABLE>
+<P>
+You may color this token with the
+<A HREF="#index-pri-color">Index Priority Colors</A> options.
+</DD>
+
+<DT>PRIORITY!</DT>
+<DD>
+This is a one character, non-numeric version of the X-Priority field.
+If the value of the X-Priority header is 1 or 2 an exclamation
+point is displayed.
+If the value is 4 or 5 a &quot;v&quot; (think down arrow) is displayed.
+You may color this token with the
+<A HREF="#index-pri-color">Index Priority Colors</A> options.
+</DD>
+
+<DT>ATT</DT>
+<DD>
+This is a one column wide field which represents the number of attachments
+a message has. It will be blank if there are no attachments, a single
+digit for one to nine attachments, or an asterisk for more than nine.
+Note that including this token in
+the &quot;Index-Format&quot; could slow down the
+display a little while <EM>Alpine</EM> collects the necessary information.
+</DD>
+
+<DT>FROMORTO</DT>
+<DD>
+This token represents <EM>either</EM> the personal name (or email address) of
+the person listed in the message's &quot;From:&quot; header
+field, <EM>or</EM>, if that address is yours or one of your
+<A HREF="#alt-addresses">alternate addresses</A>,
+the first person specified in the
+message's &quot;To:&quot; header field
+with the prefix &quot;To: &quot; prepended.
+If the from address is yours and there is also no &quot;To&quot; address,
+<EM>Alpine</EM> will use the address on the &quot;Cc&quot; line.
+If there is no address there, either, <EM>Alpine</EM> will look for a newsgroup name
+from the &quot;Newsgroups&quot; header field and put
+that after the &quot;To: &quot; prefix.
+</DD>
+
+<DT>FROMORTONOTNEWS</DT>
+<DD>
+This is almost the same as <EM>FROMORTO</EM>.
+The difference is that newsgroups aren't considered.
+When a message is from you, doesn't have a To or Cc, and does have
+a Newsgroups header; this token will be your name instead of the name
+of the newsgroup (like it would be with FROMORTO).
+</DD>
+
+<DT>TEXT</DT>
+<DD>
+This is a different sort of token.
+It allows you to display a label within each index line.
+It will be the same fixed text for each line.
+It is different from all the other tokens in that there is no space column
+displayed after this token.
+Instead, it is butted up against the following field.
+It also has a different syntax.
+The text to display is given following a colon after the
+word &quot;TEXT&quot;.
+For example,
+<P>
+<CENTER><SAMP>TEXT:abc=</SAMP></CENTER>
+<P>
+would insert the literal text &quot;abc=&quot; (without the quotes)
+into the index display line.
+You must quote the text if it includes space characters, like
+<P>
+<CENTER><SAMP>TEXT:&quot;abc&nbsp;=&nbsp;&quot;</SAMP></CENTER>
+<P>
+</DD>
+
+<DT>HEADER</DT>
+<DD>
+This allows you to display the text from a particular header line in the
+message.
+The syntax for this token is substantially different from all the others
+in order that you might be able to display a portion of the text following
+a particular header.
+The header name you are interested in is given following a colon
+after the word &quot;HEADER&quot;.
+For example,
+<P>
+<CENTER><SAMP>HEADER:X-Spam</SAMP></CENTER>
+<P>
+would display the text of the X-Spam header, if any.
+Like for other index tokens a width field may (and probably should)
+follow this.
+<P>
+<CENTER><SAMP>HEADER:X-Spam(10)</SAMP></CENTER>
+<P>
+displays the first ten characters of the X-Spam header.
+Unlike other index tokens, the syntax for HEADER is more flexible.
+An optional second argument comes after a comma inside the parentheses.
+It specifies the &quot;field&quot; number.
+By default, the field separator is a space character.
+No extra space characters are allowed in the argument list.
+<P>
+<CENTER><SAMP>HEADER:X-Spam(10,2)</SAMP></CENTER>
+<P>
+would display the second field, left-justified, in a 10 character
+wide field.
+The second field would consist of all the text after the first space
+up to the next space or the end of the header.
+The default field number is zero, which stands for the entire line.
+There is also an optional third argument which is a list of field
+separators. It defaults to a space character.
+The example
+<P>
+<CENTER><SAMP>HEADER:X-Spam(10,2,:%&nbsp;)</SAMP></CENTER>
+<P>
+would cause the field separators to be any of colon, percent,
+or space (there is a space character between the percent and the
+right parenthesis).
+The first field runs from the start of the header value up to the first
+colon, percent, or space; the second goes from there to the next; and so on.
+In order to use a comma character as a field separator you must escape
+it by preceding it with a backslash (&#92;).
+The same is true of the backslash character itself.
+There is one further optional argument.
+It is an R or an L to specify right or left adjustment of the text
+within the field.
+The default is to left justify, however if you are displaying numbers
+you might prefer to right justify.
+<P>
+Here's an example of a SpamAssassin header.
+The exact look of the header will vary, but if your incoming mail
+contains headers that look like the following
+<P>
+<CENTER><SAMP>X-Spam-Status: Yes, hits=10.6 tagged_above=-999.0 required=7.0 tests=BAYE...</SAMP></CENTER>
+<P>
+you might want to display the hits value.
+The first field starts with the Y in Yes.
+To get what you're interested in you might use &quot;=&quot; and
+space as the field separators and display the third field, like
+<P>
+<CENTER><SAMP>HEADER:X-Spam-Status(4,3,=&nbsp;)</SAMP></CENTER>
+<P>
+or maybe you would break at the dot instead
+<P>
+<CENTER><SAMP>HEADER:X-Spam-Status(2,2,=.,R)</SAMP></CENTER>
+<P>
+Another example we've seen has headers that look like
+<P>
+<CENTER><SAMP>X-Spam: Gauge=IIIIIII, Probability=7%, Report=...</SAMP></CENTER>
+<P>
+Because there are two equals and a comma before the 7% and a comma
+after it, the token
+<P>
+<CENTER><SAMP>HEADER:X-Spam-Status(3,4,=&#92;,,R)</SAMP></CENTER>
+<P>
+should display the probability (for example 7% or 83%) right justified
+in a 3-wide field.
+</DD>
+
+
+<DT>ARROW</DT>
+<DD>
+This gives an alternative way to display the current message in the
+MESSAGE INDEX screen.
+Usually the current message is indicated by the line being shown in
+reverse video.
+Instead, if the ARROW token is included in your Index-Format,
+the current line will include an &quot;arrow&quot; that
+looks like
+<P>
+<CENTER><SAMP>-&gt;</SAMP></CENTER>
+<P>
+in the ARROW token's field.
+For all of the non-current messages, the ARROW field will be filled
+with blanks.
+If you use the fixed-field width feature the length of the &quot;arrow&quot;
+may be adjusted.
+The arrow will be drawn as width-1 dashes followed by a greater than sign.
+For example, if you use ARROW(3) you will get
+<P>
+<CENTER><SAMP>--&gt;</SAMP></CENTER>
+<P>
+and ARROW(1) will give you just
+<P>
+<CENTER><SAMP>&gt;</SAMP></CENTER>
+<P>
+It is also possible to set the color of the ARROW field.
+By default (and for non-current messages) the arrow is colored the same
+as the index line it is part of.
+You may set it to be another color with the
+<A HREF="#index-arrow-color">Index Arrow Color</A> option available from
+the Setup Kolor screen.
+</DD>
+
+<DT>SCORE</DT>
+<DD>
+This gives the
+score
+of each message.
+This will be six columns wide to accomodate the widest possible score.
+You will probably want to use the Index-Format fixed-field width feature
+to limit the width of the field to the widest score that
+you use (e.g. SCORE(3) if your scores are always between 0 and 999).
+If you have not defined any score rules the scores will all be zero.
+If any of your score rules contain AllText or BodyText patterns
+then including SCORE in the Index-Format
+may slow down the display of the MESSAGE INDEX screen.
+</DD>
+</DL>
+
+<P>
+<H3><EM>Tokens Available for all but Index-Format</EM></H3>
+
+<DL>
+<DT>CURNEWS</DT>
+<DD>
+This token represents the current newsgroup if there is one.
+For example, &quot;comp.mail.pine&quot;.
+</DD>
+
+<DT>MSGID</DT>
+<DD>
+This token represents the message ID of the message.
+This token does not work with Filter Rule folder names.
+</DD>
+
+<DT>CURDATE</DT>
+<DD>
+This token represents the current date.
+It has the format MMM DD. For example, &quot;Oct 23&quot;.
+</DD>
+
+<DT>CURDATEISO</DT>
+<DD>
+This token represents the current date.
+It has the format YYYY-MM-DD. For example, &quot;1998-10-23&quot;.
+</DD>
+
+<DT>CURDATEISOS</DT>
+<DD>
+This token represents the current date.
+It has the format YY-MM-DD. For example, &quot;98-10-23&quot;.
+</DD>
+
+<DT>CURPREFDATE</DT>
+<DD>
+This token represents the current date.
+It is your operating system's idea of the preferred date representation for the current locale.
+Internally it uses the %x version of the date from the strftime routine.
+</DD>
+
+<DT>CURPREFTIME</DT>
+<DD>
+This token represents the current time.
+It is the preferred time representation for the current locale.
+Internally it uses the %X version of the time from the strftime routine.
+</DD>
+
+<DT>CURPREFDATETIME</DT>
+<DD>
+This token represents the current date and time.
+It is the preferred date and time representation for the current locale.
+Internally it uses the %c version of the time from the strftime routine.
+</DD>
+
+<DT>CURTIME24</DT>
+<DD>
+This token represents the current time.
+It has the format HH:MM. For example, &quot;17:28&quot;.
+</DD>
+
+<DT>CURTIME12</DT>
+<DD>
+This token represents the current time.
+This time is for a 12 hour clock.
+It has the format HH:MMpm.
+For example, &quot;5:28pm&quot; or &quot;11:13am&quot;.
+</DD>
+
+<DT>CURDAY</DT>
+<DD>
+This token represents the current day of the month.
+For example, &quot;23&quot; or &quot;9&quot;.
+</DD>
+
+<DT>CURDAY2DIGIT</DT>
+<DD>
+This token represents the current day of the month.
+For example, &quot;23&quot; or &quot;09&quot;.
+It is always 2 digits.
+</DD>
+
+<DT>CURDAYOFWEEK</DT>
+<DD>
+This token represents the current day of the week.
+For example, &quot;Sunday&quot; or &quot;Wednesday&quot;.
+</DD>
+
+<DT>CURDAYOFWEEKABBREV</DT>
+<DD>
+This token represents the current day of the week.
+For example, &quot;Sun&quot; or &quot;Wed&quot;.
+</DD>
+
+<DT>CURMONTH</DT>
+<DD>
+This token represents the current month.
+For example, &quot;10&quot; or &quot;9&quot;.
+</DD>
+
+<DT>CURMONTH2DIGIT</DT>
+<DD>
+This token represents the current month.
+For example, &quot;10&quot; or &quot;09&quot;.
+It is always 2 digits.
+</DD>
+
+<DT>CURMONTHLONG</DT>
+<DD>
+This token represents the current month.
+For example, &quot;October&quot;.
+</DD>
+
+<DT>CURMONTHABBREV</DT>
+<DD>
+This token represents the current month.
+For example, &quot;Oct&quot;.
+</DD>
+
+<DT>CURYEAR</DT>
+<DD>
+This token represents the current year.
+For example, &quot;1998&quot; or &quot;2001&quot;.
+</DD>
+
+<DT>CURYEAR2DIGIT</DT>
+<DD>
+This token represents the current year.
+For example, &quot;98&quot; or &quot;01&quot;.
+It is always 2 digits.
+</DD>
+
+<DT>LASTMONTH</DT>
+<DD>
+This token represents last month.
+For example, if this is November (the 11th month),
+it is equal to &quot;10&quot; or if this is October (the 10th month),
+it is &quot;9&quot;.
+It is possible that this and the other tokens beginning with LASTMONTH
+below could be useful when used with a Filtering Rule that
+has the &quot;Beginning of Month&quot; option set.
+</DD>
+
+<DT>LASTMONTH2DIGIT</DT>
+<DD>
+This token represents last month.
+For example, if this is November (the 11th month),
+it is equal to &quot;10&quot; or if this is October (the 10th month),
+it is &quot;09&quot;.
+It is always 2 digits.
+</DD>
+
+<DT>LASTMONTHLONG</DT>
+<DD>
+This token represents last month.
+For example, if this is November the value is &quot;October&quot;.
+</DD>
+
+<DT>LASTMONTHABBREV</DT>
+<DD>
+This token represents last month.
+For example, if this is November the value is &quot;Oct&quot;.
+</DD>
+
+<DT>LASTMONTHYEAR</DT>
+<DD>
+This token represents what the year was a month ago.
+For example, if this is October, 1998, it is &quot;1998&quot;.
+If this is January, 1998, it is &quot;1997&quot;.
+</DD>
+
+<DT>LASTMONTHYEAR2DIGIT</DT>
+<DD>
+This token represents what the year was a month ago.
+For example, if this is October, 1998, it is &quot;98&quot;.
+If this is January, 1998, it is &quot;97&quot;.
+</DD>
+
+<DT>LASTYEAR</DT>
+<DD>
+This token represents last year.
+For example, if this is 1998, it equals &quot;1997&quot;.
+It is possible that this
+could be useful when used with a Filtering Rule that
+has the &quot;Beginning of Year&quot; option set.
+</DD>
+
+<DT>LASTYEAR2DIGIT</DT>
+<DD>
+This token represents last year.
+For example, if this is 1998, it equals &quot;97&quot;.
+It is always 2 digits.
+</DD>
+
+<DT>ROLENICK</DT>
+<DD>
+This token represents the nickname of the
+role currently being used. If no role is being used,
+then no text will be printed for this token.
+This token does not work with Filter Rule folder names.
+</DD>
+</DL>
+
+<P>
+<H3><EM>Token Available Only for Reply-Leadin</EM></H3>
+See the help for the
+<A HREF="#reply-leadin"><EM>Reply-Leadin</EM></A> option,
+to see why you might want to use this.
+Since the <EM>Reply-Leadin</EM> contains free text this token
+must be surrounded by underscores when used.
+
+<DL>
+<DT>NEWLINE</DT>
+<DD>
+This is an end of line marker.
+</DD>
+</DL>
+<P>
+<H3><EM>Token Available Only for Templates and Signatures</EM></H3>
+
+<DL>
+<DT>CURSORPOS</DT>
+<DD>
+This token is different from the others.
+When it is replaced it is replaced with nothing, but it sets a <EM>Alpine</EM>
+internal variable which tells the composer to start with the cursor
+positioned at the position where this token was.
+If both the template file and the signature file contain
+a &quot;CURSORPOS&quot; token, then the position in the template file
+is used.
+If there is a template file and neither it nor the signature file contains
+a &quot;CURSORPOS&quot; token, then the cursor is positioned
+after the end of the contents of the
+template file when the composer starts up.
+</DD>
+</DL>
+
+<H2><A NAME="reply-token-conditionals"></A>Conditional Inclusion of Text for Reply-Leadin, Signatures, and Templates</H2>
+
+Conditional text inclusion may be used with
+the <A HREF="#reply-leadin"><EM>Reply-Leadin</EM></A> option,
+in signature files, and in template files used in
+roles.
+It may <EM>not</EM> be used with the
+<EM>Index-Format</EM> option.
+
+<P>
+There is a limited if-else capability for including text.
+The if-else condition is based
+on whether or not a given token would result in replacement text you
+specify.
+The syntax of this conditional inclusion is
+<P>
+<CENTER><SAMP>_token_(match_this, if_matched [ , if_not_matched ] )</SAMP></CENTER>
+<P>
+The left parenthesis must follow the underscore immediately, with no
+intervening space.
+It means the token is expanded and the results of that expansion are
+compared against the &quot;match_this&quot; argument.
+If there is an exact match, then the &quot;if_matched&quot; text is used
+as the replacement text.
+Otherwise, the &quot;if_not_matched&quot; text is used.
+One of the most useful values for the &quot;match_this&quot; argument is
+the empty string, &quot;&quot;.
+In that case the expansion is compared against the empty string.
+<P>
+Here's an example to make it clearer.
+This text could be included in one of your template files:
+<P>
+<CENTER><SAMP>_NEWS_(&quot;&quot;, &quot;I'm replying to email&quot;,&quot;I'm replying to news&quot;)</SAMP></CENTER>
+<P>
+If that is included in a template file which you are using while replying
+to a message (because you chose to use the role it was part of),
+and that message has a newsgroup header and a newsgroup in that header,
+then the text
+<P>
+<CENTER><SAMP>I'm replying to news</SAMP></CENTER>
+<P>
+will be included in the message you are about to compose.
+On the other hand, if the message you are replying to does not have
+a newsgroup, then the text
+<P>
+<CENTER><SAMP>I'm replying to email</SAMP></CENTER>
+<P>
+would be included instead.
+This would also work in signature files and in
+the &quot;Reply-Leadin&quot; option.
+If the &quot;match_this&quot;, &quot;if_matched&quot;,
+or &quot;if_not_matched&quot; arguments contain
+spaces, parentheses, or commas;
+they have to be quoted with double quotation marks (like in the example
+above).
+If you want to include a literal quote in the text you must escape the
+quote by preceding it with a backslash character.
+If you want to include a literal backslash character you must escape it
+by preceding it with another backslash.
+<P>
+The comma followed by &quot;if_not_matched&quot; is optional.
+If there is no &quot;if_not_matched&quot;
+present then no text is included if the not_matched case is true.
+Here's another example:
+<P>
+<CENTER><SAMP>_NEWS_(&quot;&quot;, &quot;&quot;, &quot;This msg was seen in group: _NEWS_.&quot;)</SAMP></CENTER>
+<P>
+Here you can see that tokens may appear in the arguments.
+The same is true for tokens with the conditional parentheses.
+They may appear in arguments,
+though you do have to be careful to get the quoting and escaping of
+nested double quotes correct.
+If this was in the signature file being used and you were replying to a message
+sent to comp.mail.pine the resulting text would be:
+<P>
+<CENTER><SAMP>This msg was seen in group: comp.mail.pine.</SAMP></CENTER>
+<P>
+If you were replying to a message which wasn't sent to any newsgroup the
+resulting text would be a single blank line.
+The reason you'd get a blank line is because the end of the line is
+outside of the conditional, so is always included.
+If you wanted to get rid of that blank line you could do so by moving
+the end of line inside the conditional.
+In other words, it's ok to have multi-line
+&quot;if_matched&quot; or &quot;if_not_matched&quot; arguments.
+The text just continues until the next double quotation, even if it's not
+on the same line.
+<P>
+Here's one more (contrived) example illustrating a matching argument
+which is not the empty string.
+<P>
+<CENTER><SAMP>_SMARTDATE_("Today", _SMARTDATE_, "On _DATE_") _FROM_ wrote:</SAMP></CENTER>
+<P>
+If this was the value of your &quot;Reply-Leadin&quot; option and you
+were replying to
+a message which was sent today, then the value of the &quot;Reply-Leadin&quot;
+would be
+<P>
+<CENTER><SAMP>Today Fred Flintstone wrote:</SAMP></CENTER>
+<P>
+But if you were replying to a message sent on Oct. 27 (and that wasn't
+today) you would get
+<P>
+<CENTER><SAMP>On Oct 27 Fred Flintstone wrote:</SAMP></CENTER>
+<P>
+
+<H2><A NAME="per-server-ldap-config"></A>Per Server Directory Configuration</H2>
+
+This is only available if <EM>Alpine</EM> was built with LDAP support.
+If that's the case, there will be a Directory option underneath the Setup
+command on the Main Menu.
+Each server that is defined there has several configuration variables
+which control the behavior when using it.
+
+<DL COMPACT>
+
+<DT> <A NAME="ldap-server"><EM>ldap-server</EM></A>
+
+<DD> This is the name of the host where an LDAP server is running.
+<P>
+To find out whether your organization has its own LDAP server,
+contact its computing support staff.
+<P>
+
+<DT> <A NAME="search-base"><EM>search-base</EM></A>
+
+<DD> This is the search base to be used on this server.
+It functions as a filter
+by restricting your searches in the LDAP server database
+to the specified contents of the specified fields.
+Without it, searches submitted to this directory server may fail.
+It might be something like:
+
+<PRE>
+ O = &lt;Your Organization Name&gt;, C = US
+</PRE>
+
+or it might be blank.
+(Some LDAP servers actually ignore anything specified here.)
+<P>
+
+If in doubt what parameters you should specify here,
+contact the maintainers of the LDAP server.
+<P>
+
+<DT> <A NAME="port"><EM>port</EM></A>
+
+<DD> This is the TCP port number to be used with this LDAP server.
+If you leave this blank port <CODE>389</CODE> will be used.
+<P>
+
+<DT> <A NAME="ldap-nickname"><EM>nickname</EM></A>
+
+<DD> This is a nickname to be used in displays.
+If you don't supply a nickname the server name from
+&quot;ldap-server&quot; will be used instead.
+This option is strictly for your convenience.
+<P>
+
+<DT> <A NAME="use-implicitly-from-composer"><EM>use-implicitly-from-composer</EM></A>
+
+<DD> Set this feature to have lookups done to this server implicitly from
+the composer.
+If an address doesn't look like a fully-qualified address, it will be looked
+up in your address books, and if it doesn't match a nickname there, then it
+will be looked up on the LDAP servers which have this feature set.
+The lookups will also be done when using the address completion feature
+(TAB command) in the composer if any of the serves have this feature set.
+Also see the LDAP feature
+<A HREF="#lookup-addrbook-contents"><EM>lookup-addrbook-contents</EM></A>
+and the Setup/Config feature
+<A HREF="#ldap-result-to-addrbook-add"><EM>ldap-result-to-addrbook-add</EM></A>.
+<P>
+
+<DT> <A NAME="lookup-addrbook-contents"><EM>lookup-addrbook-contents</EM></A>
+
+<DD> Normally implicit LDAP lookups from the composer are done only for the
+strings you type in from the composer screen. In other words, you type in
+something in the To or CC field and press return, then the string is looked up.
+First that string is looked up in your address books. If a match is found
+there, then the results of that match are looked up again. If you place
+a string in your address book that you want to have looked up on the LDAP
+directory server, you need to turn on this feature. If you set this feature
+for a server, you almost always will also want to set the
+<A HREF="#use-implicitly-from-composer"><EM>use-implicitly-from-composer</EM></A>
+feature.
+An example might serve to best illustrate this feature.
+<P>
+If an LDAP lookup of &quot;William Clinton&quot; normally returns an
+entry with an
+address of pres@whitehouse.gov, then you might put an entry in your address
+book that looks like:
+<PRE>
+ Nickname Address
+ bill &quot;William Clinton&quot;
+</PRE>
+Now, when you type &quot;bill&quot; into an
+address field in the composer <EM>Alpine</EM> will
+find the &quot;bill&quot; entry in your address book.
+It will replace &quot;bill&quot; with
+&quot;William Clinton&quot;.
+It will then search for an entry with that nickname
+in your address book and not find one. If this feature
+is set, <EM>Alpine</EM> will then attempt to lookup
+&quot;William Clinton&quot; on the LDAP server and find the entry with address
+pres@whitehouse.gov.
+<P>
+A better way to accomplish the same thing is probably to use the feature
+<A HREF="#save-search-criteria-not-result"><EM>save-search-criteria-not-result</EM></A>.
+<P>
+
+<DT> <A NAME="save-search-criteria-not-result"><EM>save-search-criteria-not-result</EM></A>
+
+<DD> Normally when you save the results of an LDAP directory lookup to your
+address book the <EM>results</EM> of the lookup are saved.
+If this feature is set
+and the entry being saved was found on this directory server, then the
+search <EM>criteria</EM> is saved instead of the <EM>results</EM> of the search.
+When this address book entry is used in the future, instead of copying
+the results from the address book the directory lookup will be done again.
+This could be useful if the copied result might become stale because the data on
+the directory server changes (for example, the entry's email address changes).
+You probably don't want to set this feature if the server is at all slow or
+unreliable.
+<P>
+The way this actually works is that instead of saving the email address
+in your address book, <EM>Alpine</EM> saves enough
+information to look up the same directory entry again.
+In particular, it saves the server name and the
+distinguished name of the entry.
+It's possible that the server administrators
+might change the format of distinguished names on the server, or that the
+entry might be removed from the server. If <EM>Alpine</EM> notices this, you will be warned
+and a backup copy of the email address will be used. You may want to create
+a new entry in this case, since you will get the annoying warning every
+time you use the old entry. You may do that by Saving the entry to a new
+nickname in the same address book. You will be asked whether or not you
+want to use the backup email address.
+<P>
+A related feature in the Setup/Config screen is
+<A HREF="#ldap-result-to-addrbook-add"><EM>ldap-result-to-addrbook-add</EM></A>.
+<P>
+
+<DT> <A NAME="disable-ad-hoc-space-substitution"><EM>disable-ad-hoc-space-substitution</EM></A>
+
+<DD> Spaces in your input are normally handled specially.
+Each space character is replaced
+by
+<PRE>
+ * &lt;SPACE&gt;
+</PRE>
+in the search query (but not by &quot;* &lt;SPACE&gt; *&quot;).
+The reason this is done is so the input string
+<PRE>
+ Greg Donald
+</PRE>
+(which is converted to &quot;Greg* Donald&quot;) will match
+the names &quot;Greg Donald&quot;,
+&quot;Gregory Donald&quot;, &quot;Greg F. Donald&quot;, and
+&quot;Gregory F Donald&quot;; but it won't match &quot;Greg McDonald&quot;.
+If the &quot;Search-Rule&quot; you were using was &quot;begins-with&quot;,
+then it would also match the name &quot;Greg Donaldson&quot;.
+<P>
+Turning on this feature will disable this substitution.
+<P>
+
+<DT> <A NAME="search-type"><EM>search-type</EM></A>
+
+<DD> This affects the way that LDAP searches are done.
+In particular, this tells the server where to look for the string to be matched.
+If set to &quot;name&quot; then the string that is being searched for will
+be compared with the string in the
+&quot;Name&quot; field on the server
+(technically, it is the &quot;commonname&quot; field on the server).
+&quot;Surname&quot; means we're looking for a
+match in the &quot;Surname&quot; field on the
+server (actually the &quot;sn&quot; field).
+&quot;Givenname&quot; really is &quot;givenname&quot;
+and &quot;email&quot; is the electronic mail address (this is actually the field
+called &quot;mail&quot; or &quot;electronicmail&quot; on the server).
+The other three types are combinations of
+the types listed so far. &quot;Name-or-email&quot;
+means the string should appear
+in either the &quot;name&quot; field OR the &quot;email&quot; field.
+Likewise, &quot;surname-or-givenname&quot;
+means &quot;surname&quot; OR &quot;givenname&quot;
+and &quot;sur-or-given-or-name-or-email&quot; means the obvious thing.
+<P>
+This search <EM>type</EM> is combined with the
+search <A HREF="#search-rule"><EM>rule</EM></A>
+to form the actual search query.
+<P>
+The usual default value for this
+option is &quot;sur-or-given-or-name-or-email&quot;.
+This type of search may be slow on some servers.
+Try &quot;name-or-email&quot;, which is often
+faster, or just &quot;name&quot; if the performance seems to be a problem.
+<P>
+Some servers have been configured with different attribute names for
+these four fields.
+In other words, instead of using the attribute name &quot;mail&quot;
+for the email address field, the server might be configured to use something
+else, for example, &quot;rfc822mail&quot; or &quot;internetemailaddress&quot;.
+<EM>Alpine</EM> can be configured to use these different attribute names by using
+the four per-server configuration options:
+<P><UL>
+<LI><A HREF="#email-attribute"><EM>email-attribute</EM></A>
+</UL>
+<P><UL>
+<LI><A HREF="#name-attribute"><EM>name-attribute</EM></A>
+</UL>
+<P><UL>
+<LI><A HREF="#surname-attribute"><EM>surname-attribute</EM></A>
+</UL>
+<P><UL>
+<LI><A HREF="#givenname-attribute"><EM>givenname-attribute</EM></A>
+</UL>
+<P>
+
+<DT> <A NAME="search-rule"><EM>search-rule</EM></A>
+
+<DD> This affects the way that LDAP searches are done.
+If set to &quot;equals&quot; then
+only exact matches count.
+&quot;Contains&quot; means that the string you type in
+is a substring of what you are matching against.
+&quot;Begins-with&quot; and &quot;ends-with&quot;
+mean that the string starts or ends with the string you type in.
+<P>
+Spaces in your input are normally handled specially, but you can turn that
+special handling off with the
+<A HREF="#disable-ad-hoc-space-substitution"><EM>disable-ad-hoc-space-substitution</EM></A>
+feature.
+<P>
+The usual default value for this option is <EM>begins-with</EM>.
+<P>
+
+<DT> <A NAME="email-attribute"><EM>email-attribute</EM></A>
+
+<DD> This is the name of the attribute which is searched for when looking for
+an email address. The default value for this option is &quot;mail&quot; or
+&quot;electronicmail&quot;.
+If the server you are using uses a different attribute name for the email
+address, put that attribute name here.
+<P>
+This will affect the search filter used if your Search-Type is one that
+contains a search for &quot;email&quot;.
+It will also cause the attribute value matching this attribute name to be used
+as the email address when you look up an entry from the composer.
+<P>
+
+<DT> <A NAME="name-attribute"><EM>name-attribute</EM></A>
+
+<DD> This is the name of the attribute which is searched for when looking for
+the name of the entry. The default value for this option is &quot;cn&quot;, which
+stands for common name.
+If the server you are using uses a different attribute name for the name,
+put that attribute name here.
+This will affect the search filter used if your Search-Type is one that
+contains a search for &quot;name&quot;.
+<P>
+
+<DT> <A NAME="surname-attribute"><EM>surname-attribute</EM></A>
+
+<DD> This is the name of the attribute which is searched for when looking for
+the surname of the entry. The default value for this option is &quot;sn&quot;.
+If the server you are using uses a different attribute name for the surname,
+put that attribute name here.
+This will affect the search filter used if your Search-Type is one that
+contains a search for &quot;surname&quot;.
+<P>
+
+<DT> <A NAME="givenname-attribute"><EM>givenname-attribute</EM></A>
+
+<DD> This is the name of the attribute which is searched for when looking for
+the given name of the entry. The default value for this option is &quot;givenname&quot;.
+If the server you are using uses a different attribute name for the given name,
+put that attribute name here.
+This will affect the search filter used if your Search-Type is one that
+contains a search for &quot;givenname&quot;.
+<P>
+
+<DT> <A NAME="timelimit"><EM>timelimit</EM></A>
+
+<DD> This places a limit on the number of seconds the LDAP search will continue.
+The default is 30 seconds. A value of 0 means no limit. Note that some servers
+may place limits of their own on searches.
+<P>
+
+<DT> <A NAME="sizelimit"><EM>sizelimit</EM></A>
+
+<DD> This places a limit on the number of entries returned by the LDAP server.
+A value of 0 means no limit. The default is 0. Note that some servers
+may place limits of their own on searches.
+<P>
+
+<DT> <A NAME="custom-search-filter"><EM>custom-search-filter</EM></A>
+
+<DD> This one is for advanced users only! If you define this, then the
+<A HREF="#search-type"><EM>search-type</EM></A>
+and
+<A HREF="#search-rule"><EM>search-rule</EM></A>
+defined are both ignored.
+However, the feature
+<A HREF="#disable-ad-hoc-space-substitution"><EM>disable-ad-hoc-space-substitution</EM></A>
+is still in effect.
+That is, the space substitution will take place even in a custom filter unless
+you disable it.
+<P>
+If your LDAP service stops working and you suspect it might be because
+of your custom filter, just delete this filter and try using the
+<EM>search-type</EM> and <EM>search-rule</EM> instead.
+Another option that sometimes causes trouble is the
+<A HREF="#search-base"><EM>search-base</EM></A> option.
+<P>
+This variable may be set to the string representation of an LDAP search
+filter (see RFC1960). In the places where you want the address string to be
+substituted in, put a '%s' in this filter string. Here are some examples:
+<P>
+A &quot;Search-Type&quot; of &quot;name&quot; with &quot;Search-Rule&quot; of &quot;begins-with&quot;
+is equivalent to the &quot;custom-search-filter&quot;
+<PRE>
+ (cn=%s*)
+</PRE>
+When you try to match against the string &quot;string&quot; the program replaces
+the &quot;%s&quot; with &quot;string&quot; (without the quotes). You may have multiple &quot;%s&quot;'s and
+they will all be replaced with the string. There is a limit of 10 &quot;%s&quot;'s.
+<P>
+A &quot;Search-Type&quot; of &quot;name-or-email&quot; with &quot;Search-Rule&quot;
+of &quot;contains&quot; is equivalent to
+<PRE>
+ (|(cn=*%s*)(mail=*%s*))
+</PRE>
+<P>
+If your server uses a different attribute <EM>name</EM> than
+<EM>Alpine</EM> uses by default,
+(for example, it uses &quot;rfc822mail&quot; instead of &quot;mail&quot;),
+then you may be able to use one or more of the four attribute configuration
+options instead of defining a custom filter:
+<P><UL>
+<LI><A HREF="#email-attribute"><EM>email-attribute</EM></A>
+</UL>
+<P><UL>
+<LI><A HREF="#name-attribute"><EM>name-attribute</EM></A>
+</UL>
+<P><UL>
+<LI><A HREF="#surname-attribute"><EM>surname-attribute</EM></A>
+</UL>
+<P><UL>
+<LI><A HREF="#givenname-attribute"><EM>givenname-attribute</EM></A>
+</UL>
+
+</DL>
+<P>
+
+<H2><A NAME="color-config"></A>Color Configuration</H2>
+
+If the terminal or terminal emulator you are using is capable of using
+color (see <A HREF="#color-style"><EM>color-style</EM></A> option),
+or if you are using <EM>PC-Alpine</EM>, then it is possible to
+set up <EM>Alpine</EM> so that various parts of the display will be
+shown in colors you configure.
+This is done using the Setup Color screen.
+The Setup Color screen is divided into five broad sections: Options,
+General Colors, Index Colors, Header Colors, and Keyword Colors.
+In addition to these five categories you may also color lines in the
+MESSAGE INDEX screen by configuring the
+<A HREF="#index-color-config">Index Line Color</A>.
+<P>
+Each color is defined as a foreground color (the color of the actual text)
+and a background color (the color of the area behind the text).
+<P>
+<H3><A NAME="color-options"></A>Color Options</H3>
+
+<DL>
+
+<DT> <A NAME="cur-il-style"><EM>current-indexline-style</EM></A>
+<DD>
+This option affects the colors used to display the current line in the
+MESSAGE INDEX screen.
+If you do not have
+<A HREF="#index-color-config">Index Line Colors</A>
+defined, then this option will have no effect in the index.
+Those Rules may be defined by going to the Setup/Rules/Indexcolor screen.
+<P>
+If the option
+<A HREF="#enable-incoming-folders-checking"><EM>enable-incoming-folders-checking</EM></A>
+is turned on and the
+<A HREF="#incoming-unseen-color"><EM>Incoming Unseen Color</EM></A>
+is set to something other than the default, then
+this option also affects the color used to display the current folder
+in the Incoming FOLDER LIST screen.
+
+<P>
+The available options include:
+<P>
+
+<DL>
+<DT>flip-colors</DT>
+<DD>This is the default.
+If an index line is colored because it matches one of your
+Index Color Rules, then its colors will be reversed when it is the currently
+highlighted line.
+For example, if the line is normally red text on a blue background, then
+when it is the current line it will be drawn as blue text on a red background.
+<P>
+The rest of the option values all revert to this flip-colors behavior if
+there is no Reverse Color defined.
+</DD>
+
+<DT>reverse</DT>
+<DD>With this option the Reverse color is always used to highlight the
+current line.
+</DD>
+
+<DT>reverse-fg</DT>
+<DD>The foreground part of the Reverse Color is used to highlight
+the current line.
+If this would cause the text to be unreadable (because the foreground and
+background colors are the same) or if it would cause no change in the
+color of the index line, then the colors are flipped instead.
+<P>
+Some people think this works particularly well if you use different
+background colors to emphasize &quot;interesting&quot; lines,
+but always with the same Normal foreground color,
+and you use a different foreground color for the Reverse Color.
+</DD>
+
+<DT>reverse-fg-no-ambiguity</DT>
+<DD>With the &quot;reverse-fg&quot; rule above, it is possible that
+the resulting color will be exactly the same as the regular Reverse
+Color.
+That can lead to some possible confusion because an
+&quot;interesting&quot;
+line which is the current line will be displayed exactly the same as a
+non-interesting line which is current.
+You can't tell whether the line is just a regular current line or if it is
+an &quot;interesting&quot; current line by looking at the color.
+Setting the option to this value removes that ambiguity.
+It is the same as the &quot;reverse-fg&quot; setting unless the resulting
+interesting current line would look just like a non-interesting current line.
+In that case, the interesting line's colors are simply flipped (like in the
+default behavior).
+<P>
+As an alternative way to preserve the line's interestingness in this case,
+you may find that using both a different foreground and a different
+background color for the interesting line will help.
+</DD>
+
+<DT>reverse-bg</DT>
+<DD>The background part of the Reverse Color is used to highlight
+the current line.
+If this would cause the text to be unreadable (because the foreground and
+background colors are the same) or if it would cause no change in the
+color of the index line, then the colors are flipped instead.
+<P>
+Some people think this works particularly well if you use different
+foreground colors to emphasize &quot;interesting&quot; lines,
+but always with the same Normal background color,
+and you use a different background color for the Reverse Color.
+</DD>
+
+<DT>reverse-bg-no-ambiguity</DT>
+<DD>As with the &quot;reverse-fg&quot; case, the &quot;reverse-bg&quot;
+rule may also result in a color which is exactly the same as the regular
+Reverse Color.
+Setting the option to this value removes that ambiguity.
+It is the same as the &quot;reverse-bg&quot; setting unless the resulting
+current line has the same color as the Reverse Color.
+In that case, the interesting line's colors are simply flipped (like in the
+default behavior).
+</DD>
+</DL>
+
+<DT> <A NAME="titlebar-style"><EM>titlebar-color-style</EM></A>
+<DD>
+This option affects the colors used to display the titlebar (the top
+line on the screen) when viewing a message.
+
+<P>
+The available options include:
+<P>
+
+<DL>
+<DT>default</DT>
+<DD>The color of the titlebar will be the color you set for the
+<A HREF="#title-color"><EM>Title Color</EM></A>.
+The Title Color may be set by using the
+</DD>
+
+<DT>indexline</DT>
+<DD>The color of the titlebar will be the same as the color of the
+index line corresponding to the message being viewed.
+The rules which determine what color the index line will be may be set
+up by going to the Setup/Rules/Indexcolor screen.
+If the index line for a message is not colored explicitly by the
+Indexcolor rules, then the titlebar will be colored the same as for
+the &quot;default&quot; option above (which is not the same color that
+the index line itself will have).
+</DD>
+
+<DT>reverse-indexline</DT>
+<DD>This is similar to the &quot;indexline&quot; option except the
+foreground and background colors from the corresponding index line will
+be reversed.
+For example, if the index line color is red letters on a white background,
+then the titlebar will be white letters on a red background.
+If the index line for a message is not colored explicitly by the
+Indexcolor rules, then the titlebar will be colored the same as for
+the &quot;default&quot; option above (which is not the same color that
+the index line itself will have).
+</DD>
+</DL>
+
+</DL>
+
+<P>
+<H3><A NAME="general-colors"></A>General Colors</H3>
+
+<DL COMPACT>
+
+<DT> <A NAME="normal-color"><EM>Normal Color</EM></A>
+
+<DD> This is the color which most of the screen is painted in.
+By default this color is black characters on a white background.
+<P>
+
+<DT> <A NAME="reverse-color"><EM>Reverse Color</EM></A>
+
+<DD> The color <EM>Alpine</EM> uses for reverse video characters.
+Actually, the name is misleading. This used to be reverse video and so
+the name remains. It is still used to highlight certain parts of the
+screen but the color may be set to whatever you'd like.
+<P>
+
+<DT> <A NAME="title-color"><EM>Title Color</EM></A>
+
+<DD> The color <EM>Alpine</EM> uses for the titlebar (the top line on the
+screen).
+By default, the Title Color is black characters on a yellow background.
+The actual titlebar color may be different from the Title Color if
+the option
+<A HREF="#titlebar-color-style"><EM>titlebar-color-style</EM></A>
+is set to some value other than the default.
+It may also be different if the current folder is closed and the
+<A HREF="#title-closed-color">Title Closed Color</A>
+is set to something different from the Title Color.
+<P>
+
+<DT> <A NAME="title-closed-color"><EM>Title-closed Color</EM></A>
+
+<DD> The color <EM>Alpine</EM> uses for the titlebar (the top line on the
+screen) when the current folder is closed.
+By default, the Title Color Closed Color is white characters on a red background.
+<P>
+
+<DT> <A NAME="status-color"><EM>Status Color</EM></A>
+
+<DD> The color <EM>Alpine</EM> uses for messages written to the status
+message line near the bottom of the screen.
+By default, the Status Color is the same as the Reverse Color.
+<P>
+
+<DT> <A NAME="keylabel-color"><EM>KeyLabel Color</EM></A>
+
+<DD> The color <EM>Alpine</EM> uses for the labels of the commands in the
+two-line menu at the bottom of the screen.
+The label is the long name, for example, &quot;PrevMsg&quot;.
+By default, the KeyLabel Color is the same as the Normal Color.
+<P>
+WARNING: Some terminal emulators have the property that the screen will scroll
+down one line whenever a character is written to the character cell in the
+lower right corner of the screen.
+<EM>Alpine</EM> can usually avoid writing a character in that corner of the screen.
+However, if you have defined a KeyLabel Color then <EM>Alpine</EM> does have to write
+a character in that cell in order to color the cell correctly.
+If you find that your display sometimes scrolls up a line this could be
+the problem.
+The most obvious symptom is probably that the titlebar at the top of the
+screen scrolls off the screen.
+Try setting KeyLabel Color to Default to see if that fixes the problem.
+<P>
+
+<DT> <A NAME="keyname-color"><EM>KeyName Color</EM></A>
+
+<DD> The color <EM>Alpine</EM> uses for the names of the commands in the
+two-line menu at the bottom of the screen.
+The KeyName is the shorter name in the menu. For example, the &quot;W&quot;
+before the &quot;WhereIs&quot;.
+By default, the KeyName Color is the same as the Normal Color.
+<P>
+
+<DT> <A NAME="selectable-item-color"><EM>Selectable-item Color</EM></A>
+
+<DD> The color <EM>Alpine</EM> uses for displaying selectable items, such
+as URLs.
+By default, the Selectable-item Color is the same as the Normal Color, except
+it is also Bold.
+<P>
+
+<DT> <A NAME="meta-message-color"><EM>Meta-message Color</EM></A>
+
+<DD> The color <EM>Alpine</EM> uses in the MESSAGE TEXT screen for messages to you
+that aren't part of the message itself.
+By default, the Meta-Message Color is black characters on a yellow background.
+<P>
+
+<DT> <A NAME="quote-colors"><EM>Quote Colors</EM></A>
+
+<DD> The colors <EM>Alpine</EM> uses for coloring quoted text in the
+MESSAGE TEXT screen.
+If a line begins with a &gt; character (or space followed by &gt;) it
+is considered a quote. That line will be given the Quote1 Color (first
+level quote). If there is a second level of quoting then the Quote2 Color
+will be used. <EM>Alpine</EM> considers there to be a second level of quoting if
+that first &gt; is followed by another &gt; (or space followed by &gt;).
+If there are characters other than whitespace and &gt; signs, then it isn't
+considered another level of quoting. Similarly, if there is a third level
+of quoting the Quote3 Color will be used. If there are more levels after
+that the Quote Colors are reused. If you define all three colors then it
+would repeat like Color1, Color2, Color3, Color1, Color2, Color3, ...
+If you only define the first two it would be Color1, Color2,
+Color1, Color2, ...
+If you define only the Quote1 Color, then the entire quote would be
+that color regardless of the quoting levels.
+By default, the Quote1 Color is black characters on a greenish-blue background;
+the Quote2 Color is black characters on a dull yellow background; and
+the Quote3 Color is black characters on a green background.
+<P>
+
+<DT> <A NAME="incoming-unseen-color"><EM>Incoming Unseen Color</EM></A>
+
+<DD> If the option
+<A HREF="#enable-incoming-folders-checking"><EM>enable-incoming-folders-checking</EM></A>
+is turned on it is possible to highlight the folders that contain
+unseen messages by coloring them with this color.
+By default, this is the same as the Normal Color and no highlighting is done.
+<P>
+Usually the &quot;current&quot; folder (the folder the cursor is on)
+is highlighted using reverse video.
+If the current folder is colored because it contains unseen messages then
+the color used to show that it is also the current folder is controlled
+by the <A HREF="#cur-il-style"><EM>current-indexline-style</EM></A>
+feature at the top of the SETUP COLOR screen.
+<P>
+
+<DT> <A NAME="signature-color"><EM>Signature Color</EM></A>
+
+<DD> The color <EM>Alpine</EM> uses for coloring the signature in the MESSAGE TEXT
+screen. According to USENET conventions, the signature is defined as the
+paragraph following the &quot;sigdashes&quot;, that is, the special line
+consisting of the three characters
+&quot;--&nbsp;&quot; (i.e., dash, dash, and space). <EM>Alpine</EM> allows for one
+empty line right after the sigdashes to be considered as part of the
+signature.
+By default, the Signature Color is blue characters on a white background.
+<P>
+
+<DT> <A NAME="prompt-color"><EM>Prompt Color</EM></A>
+
+<DD> The color <EM>Alpine</EM> uses for confirmation prompts and questions
+which appear in the status message line near the bottom of the screen.
+By default, the Prompt Color is the same as the Reverse Color.
+<P>
+
+</DL>
+
+<H3><A NAME="index-colors"></A>Index Colors</H3>
+
+You may add color to the single character symbols which give the status
+of each message in the MESSAGE INDEX.
+By default the characters &quot;+&quot;, &quot;*&quot;, &quot;D&quot;,
+&quot;A&quot;, and &quot;N&quot; show up near the left hand side of the
+screen, depending on whether the message is addressed to you, and whether
+the message is marked Important, is Deleted, is Answered, or is New.
+You may set the color of those symbols.
+By default, all of these symbols are drawn with the same color as the rest
+of the index line they are a part of.
+<P>
+Besides coloring the message status symbols, you may also color the
+entire index line.
+This is done by using the
+<A HREF="#index-color-config">Index Line Color</A> configuration screen.
+It is also possible to color
+(<A HREF="#keywords">keywords</A>
+in the index using the
+Setup/Kolor screen (<A HREF="#keyword-colors">Keyword Colors</A>);
+the <A HREF="#index-arrow-color">ARROW</A> cursor;
+the Subject using
+<A HREF="#index-subject-color">Index Subject Color</A>;
+the From using
+<A HREF="#index-from-color">Index From Color</A>;
+and the
+<A HREF="#index-opening-color">Index Opening</A> text.
+<P>
+
+<DL COMPACT>
+
+<DT> <A NAME="index-to-me-color"><EM>Index-to-me Symbol Color</EM></A>
+
+<DD> The color used for drawing the &quot;+&quot; symbol which signifies a
+message is addressed directly to you.
+<P>
+
+<DT> <A NAME="index-important-color"><EM>Index-important Symbol Color</EM></A>
+
+<DD> The color used for drawing the &quot;*&quot; symbol which signifies a
+message has been flagged Important.
+<P>
+
+<DT> <A NAME="index-deleted-color"><EM>Index-deleted Symbol Color</EM></A>
+
+<DD> The color used for drawing the &quot;D&quot; symbol which signifies a
+message has been marked Deleted.
+<P>
+
+<DT> <A NAME="index-answered-color"><EM>Index-answered Symbol Color</EM></A>
+
+<DD> The color used for drawing the &quot;A&quot; symbol which signifies a
+message has been answered.
+<P>
+
+<DT> <A NAME="index-new-color"><EM>Index-new Symbol Color</EM></A>
+
+<DD> The color used for drawing the &quot;N&quot; symbol which signifies a
+message is New.
+<P>
+
+<DT> <A NAME="index-recent-color"><EM>Index-recent Symbol Color</EM></A>
+
+<DD> The color used for drawing the &quot;R&quot; symbol which signifies a
+message is Recent (only visible if the &quot;IMAPSTATUS&quot; or &quot;SHORTIMAPSTATUS&quot; token is
+part of the
+<A HREF="#index-format"><EM>index-format</EM></A> option).
+<P>
+
+<DT> <A NAME="index-unseen-color"><EM>Index-unseen Symbol Color</EM></A>
+
+<DD> The color used for drawing the &quot;U&quot; symbol which signifies a
+message is Unseen (only visible if the &quot;IMAPSTATUS&quot; or &quot;SHORTIMAPSTATUS&quot; token is
+part of the
+<A HREF="#index-format"><EM>Index-Format</EM></A> option).
+<P>
+
+<DT> <A NAME="index-pri-color"><EM>Index-priority Symbol Colors</EM></A>
+
+<DD> The colors used for drawing the tokens &quot;PRIORITY&quot;,
+ &quot;PRIORITYALPHA&quot;, and &quot;PRIORITY!&quot; when these are
+configured as part of the
+<A HREF="#index-format"><EM>Index-Format</EM></A> option.
+You may set the color used to draw these tokens by use of the colors
+Index High Priority Symbol Color and Index Low Priority Symbol Color.
+This coloring takes place for all but the current index line, and the Priority
+Color appears to be in front of any color from an Index Color Rule.
+If the priority has a value of 1 or 2 the High Priority color will be
+used,
+and if the value is 4 or 5 the Low Priority color will be used.
+<P>
+If you don't set these colors the index line will be colored in the same color as
+the bulk of the index line.
+<P>
+
+<DT> <A NAME="index-arrow-color"><EM>Index-arrow Symbol Color</EM></A>
+
+<DD> The color used for drawing the &quot;ARROW&quot; token when it is
+configured as part of the
+<A HREF="#index-format"><EM>Index-Format</EM></A> option.
+<P>
+
+<DT> <A NAME="index-subject-color"><EM>Index-subject Symbol Color</EM></A>
+
+<DD> You may set the color used to draw the Subject part of the index line.
+This coloring takes place for all but the current index line, and the Subject
+Color appears to be in front of any color from an Index Color Rule.
+<P>
+If you don't set this color it will be colored in the same color as
+the bulk of the index line.
+<P>
+
+<DT> <A NAME="index-from-color"><EM>Index-from Symbol Color</EM></A>
+
+<DD> You may set the color used to draw the From part of the index line.
+This coloring takes place for all but the current index line, and the From
+Color appears to be in front of any color from an Index Color Rule.
+<P>
+If you don't set this color it will be colored in the same color as
+the bulk of the index line.
+<P>
+
+<DT> <A NAME="index-opening-color"><EM>Index-opening Symbol Color</EM></A>
+
+<DD> It is possible to configure the
+<A HREF="#index-format"><EM>Index-Format</EM></A> option
+so that it includes the subject followed by the &quot;opening&quot; text of the
+message if there is enough space.
+This is done by using one of the tokens SUBJECTTEXT, SUBJKEYTEXT, or
+SUBJKEYINITTEXT.
+The color used for drawing this opening text is given by this option.
+The coloring happens for all but the current index line, and this opening
+color appears to be in front of any color from an Index Color Rule.
+<P>
+By default the Index Opening Color is gray characters on a white background.
+<P>
+
+</DL>
+<P>
+The default colors for these symbols are:
+<TABLE>
+<TR> <TD> &nbsp;&nbsp;Index-to-me&nbsp;&nbsp;&nbsp;&nbsp; </TD> <TD> black on cyan </TD> </TR>
+<TR> <TD> &nbsp;&nbsp;Index-important </TD> <TD> white on bright red </TD> </TR>
+<TR> <TD> &nbsp;&nbsp;Index-deleted&nbsp;&nbsp; </TD> <TD> same as Normal Color </TD> </TR>
+<TR> <TD> &nbsp;&nbsp;Index-answered&nbsp; </TD> <TD> bright red on yellow </TD> </TR>
+<TR> <TD> &nbsp;&nbsp;Index-new&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </TD> <TD> white on magenta </TD> </TR>
+<TR> <TD> &nbsp;&nbsp;Index-recent&nbsp;&nbsp;&nbsp; </TD> <TD> same as Normal Color </TD> </TR>
+<TR> <TD> &nbsp;&nbsp;Index-unseen&nbsp;&nbsp;&nbsp; </TD> <TD> same as Normal Color </TD> </TR>
+</TABLE>
+
+<H3><A NAME="header-colors"></A>Header Colors</H3>
+
+You may add color to the header fields in the MESSAGE TEXT screen.
+The
+<P>
+<DL COMPACT>
+<DT> <A NAME="header-general-color"><EM>Header-general Color</EM></A>
+
+<DD> may be used to color all of the headers of
+the message.
+</DL>
+<P>
+It is also possible to set the colors for specific header fields,
+for example for the Subject or From fields, using the
+<A HREF="#viewer-hdr-colors"><EM>viewer-hdr-colors</EM></A>
+option.
+<P>
+For Header Colors,
+there is an additional line on the configuration
+screen labeled &quot;Pattern to match&quot;.
+If you leave that blank, then the whole field for that header will
+always be colored.
+However, if you give a pattern to match, the coloring will only take place
+if there is a match for that pattern in the value of the field.
+For example, if you are working on a color for the Subject header and
+you fill in a pattern of &quot;important&quot;, then only Subjects which
+contain the word &quot;important&quot; will be colored.
+For address fields like From or To, a pattern match will cause only the
+addresses which match the pattern to be colored.
+<P>
+If the pattern you enter is a comma-separated list of patterns, then coloring
+happens if any of those patterns matches.
+<P>
+
+<H3><A NAME="keyword-colors"></A>Keyword Colors</H3>
+
+Sets the colors <EM>Alpine</EM> uses for Keyword fields in the MESSAGE INDEX screen.
+Keywords may be displayed as part of the Subject of a message by using
+the &quot;SUBJKEY&quot; or &quot;SUBJKEYINIT&quot; tokens in the
+<A HREF="#index-format">Index-Format</A> option.
+Keywords may also be displayed in a column of their own in the MESSAGE INDEX
+screen by using the &quot;KEY&quot; or &quot;KEYINIT&quot; tokens.
+<P>
+For example, you might have set up a Keyword
+&quot;Work&quot; using the
+<A HREF="#keywords">Keywords</A>
+option in the Setup/Config screen.
+You could cause that Keyword to show up as a special color
+by setting up the Keyword Color using this option, and then including it
+in the MESSAGE INDEX screen using one of the tokens listed above in the
+Index-Format.
+<P>
+
+<H3><A NAME="index-line-colors"></A>Index Line Colors</H3>
+
+You may color whole index lines by using roles.
+This isn't configured in the Setup Colors screen, but is configured in
+the <A HREF="#index-color-config">Setup Rules IndexColor</A> screen.
+
+<H2><A NAME="index-color-config"></A>Index Line Color Configuration</H2>
+
+Index Line Color causes lines in the MESSAGE INDEX screen to be colored.
+This action is only available if your terminal is capable of displaying
+color and color display has been enabled with the
+<A HREF="#color-style"><EM>Color-Style</EM></A> option.
+(In PC-Alpine, color is always enabled so there is no option to turn on.)
+<P>
+Each rule has a &quot;Pattern&quot;,
+which is used to decide which of the rules is used; and the color which
+is used if the Pattern matches a particular message.
+
+<H3>Rule Patterns</H3>
+
+In order to determine whether or not a message matches a rule the message is
+compared with the rule's Pattern.
+These Patterns are the same for use with Roles, Filtering, Index Coloring,
+Scoring, Other Rules, and Search Rules, so are described in only one place,
+&quot;<A HREF="#patterns-section">here</A>&quot;.
+
+<H3>Index Line Color</H3>
+
+This is the color that index lines are colored when there is a matching
+Pattern.
+This colors the whole index line, except possibly the status letters
+which may be colored separately using
+the <A HREF="#color-config">Setup Kolor</A> screen.
+
+<H2><A NAME="role-config"></A>Role Configuration</H2>
+
+You may play different roles depending on who you are replying to.
+For example, if you are replying to a message addressed to <EM>help-desk</EM> you
+may be acting as a Help Desk Worker.
+That role may require that you use a different return address and/or
+a different signature.
+<P>
+Roles are optional.
+If you set up roles they work like this: Each role has a set of
+&quot;Uses&quot;, which indicate whether or not a role is eligible to be
+considered for a particular use; a &quot;Pattern&quot;,
+which is used to decide which of the eligible roles is used; and a set
+of &quot;Actions&quot;, which are taken when that role is used.
+When you reply to a message, the message you are replying to is compared
+with the Patterns of the roles marked as eligible for use when replying.
+The comparisons start with the first eligible role and keep going until there
+is a match.
+If a match is found, the matching role's Actions are taken.
+<P>
+It is also possible to set a default role and to change that role during
+your <EM>Alpine</EM> session.
+When you start <EM>Alpine</EM> no default role will be set.
+You may set or change the current default role by using the &quot;D&quot;
+command in the role selection screen.
+You'll see that screen while composing a message and being asked to select
+a role.
+An easy way to get to that screen is to use the Role Command to
+compose a message.
+You may find a default role useful if you normally perform the duties of one
+of your roles for a while, then you switch to another role and stay in the
+new role for another period of time.
+It may be easier than using the Role Command to select the role each time you
+compose a message.
+
+<H3>Role Uses</H3>
+
+There are three types of use to be configured;
+one for Replying, one for Forwarding, and one for Composing.
+These indicate whether or not you want a role to be considered when you
+type the Reply, Forward, or Compose commands.
+(The Role command is an alternate form of the Compose command, and it is
+not affected by these settings.)
+Each of these Use types has three possible values.
+The value &quot;Never&quot;
+means that the role will never be considered as a candidate for use with
+the corresponding command.
+For example, if you set a role's Reply Use to Never, then when you Reply to
+a message, the role won't even be considered.
+(That isn't quite true. If the message you are replying to matches some other
+role which requires confirmation,
+then there will be a ^T command available which allows you to select a role
+from all of your roles, not just the reply-eligible roles.)
+<P>
+
+The options &quot;With confirmation&quot; and &quot;Without confirmation&quot;
+both mean that you do want to consider this role when using the corresponding
+command.
+For either of these settings the role's Pattern will
+be checked to see if it matches the message.
+For Reply Use, the message used to compare the Patterns with is the message
+being replied to.
+For Forward Use, the message used to compare the Pattern with is the message
+being forwarded.
+For Compose Use, there is no message, so the parts of the Pattern which depend
+on a message (everything other than Current Folder Type) are ignored.
+In all cases, the Current Folder is checked if defined.
+If there is a match then this role will either be used without confirmation
+or will be the default when confirmation is asked for, depending on
+which of the two options is selected.
+If confirmation is requested, you will have a chance to
+choose No Role instead of the offered role, or to
+change the role to any one of your other roles (with the ^T command).
+
+<H3>Role Patterns</H3>
+
+In order to determine whether or not a message matches a role the message is
+compared with the Role Pattern.
+These Patterns are the same for use with Roles, Filtering, Index Coloring,
+Scoring, Other Rules, and Search Rules, so are described in only one place,
+&quot;<A HREF="#patterns-section">here</A>&quot;.
+<P>
+Since header patterns, AllText patterns, and BodyText patterns which are unset are ignored,
+a role which has all header patterns unset, the AllText pattern unset,
+the BodyText pattern unset,
+the Score Interval unset, and the Current Folder Type set to
+&quot;Any&quot; may be used as a default role.
+It should be put last in the list of roles since the matching
+starts at the beginning and proceeds until one of the roles is a match.
+If no roles at all match, then <EM>Alpine</EM> will
+use its regular methods of defining the role.
+If you wanted to, you could define a different &quot;default&quot; role
+for Replying, Forwarding, and Composing by setting the
+&quot;Use&quot; fields appropriately.
+
+<H3>Role Actions</H3>
+
+Once a role match is found, the role's Actions are taken.
+For each role there are several possible actions that may be defined.
+They are actions to set the From address, the Reply-To address,
+the Fcc, the Signature file, and the Template file.
+
+<H4>Initialize Settings Using Role</H4>
+
+This is a power user feature.
+You will usually want to leave this field empty.
+The value of this field is the nickname of another one of your roles.
+The Action values from that other role
+are used as the initial values of the Action items for this role.
+If you put something in any of the action fields for this role, that will
+override whatever was in the corresponding field of the initializer role.
+<P>
+You might use this field if the &quot;Action&quot; part of one of your roles
+is something you want to use in more than one role.
+Instead of filling in those action values again for each role, you
+may give the nickname of the role where the values are filled in.
+It's just a shortcut way to define Role Actions.
+<P>
+Here's an example to help explain how this works.
+Suppose you have a role with nickname &quot;role1&quot; and role1 has
+(among other things)
+<P>
+<CENTER><SAMP>Set Reply-To = The Pres &lt;president@example.com&gt;</SAMP></CENTER>
+<P>
+set.
+If in &quot;role2&quot; you set &quot;Initialize settings using role&quot; to
+&quot;role1&quot;, then role2 will inherit the Set Reply-To value
+from role1 by default (and any of the other inheritable action values
+that are set).
+So if role2 had
+<P>
+<CENTER><SAMP>Set Reply-To = &lt;No Value Set&gt;</SAMP></CENTER>
+<P>
+defined, the Reply-To used with role2 would be &quot;The Pres &lt;president@example.com&gt;&quot;
+However, if role2 had
+<P>
+<CENTER><SAMP>Set Reply-To = VP &lt;vicepresident@example.com&gt;</SAMP></CENTER>
+<P>
+defined, then the Reply-To used with role2 would be &quot;VP &lt;vicepresident@example.com&gt;&quot; instead.
+<P>
+If you wish,
+you may choose a nickname from your list of roles by using the
+&quot;T&quot; command.
+If the role you are using to initialize also has a role it initializes from,
+then that initialization happens first.
+That is, inheritance works as expected with the grandparent and
+great-grandparent (and so on) roles having the expected effect.
+
+<H4>Set From</H4>
+
+This field consists of a single address which will be used as the From
+address on the message you are sending.
+This should be a fully-qualified address like
+<P>
+<CENTER><SAMP>Full Name &lt;user@domain&gt;</SAMP></CENTER>
+<P>
+or just
+<P>
+<CENTER><SAMP>user@domain</SAMP></CENTER>
+<P>
+If this is left blank, then the normal From address will be used.
+
+<H4>Set Reply-To</H4>
+
+The Reply-To address is the address used on the Reply-To line of the message
+you are sending.
+You don't need a Reply-To address unless it is different from the From address.
+This should be a fully-qualified address like
+<P>
+<CENTER><SAMP>Full Name &lt;user@domain&gt;</SAMP></CENTER>
+<P>
+or just
+<P>
+<CENTER><SAMP>user@domain</SAMP></CENTER>
+<P>
+If this is left blank, then there won't be a Reply-To address unless
+you have configured one specially with the
+<A HREF="#cust-hdr"><EM>customized-hdrs</EM></A>
+configuration option.
+
+<H4>Set Other-Hdrs</H4>
+
+This field gives you a way to set values for headers besides
+&quot;From&quot; and &quot;Reply-To&quot;.
+If you want to set either of those, use the specific
+&quot;Set From&quot; and &quot;Set Reply-To&quot; settings.
+<P>
+This field is similar to the
+<A HREF="#cust-hdr"><EM>customized-hdrs</EM></A> option.
+Each header you specify here must include the header tag
+(&quot;To:&quot;, &quot;Approved:&quot;, etc.)
+and may optionally include a value for that header.
+In order to see these headers when you compose using this role you
+must use the rich header command.
+Here's an example which shows how you might set the To address.
+<P>
+<CENTER><SAMP>Set Other Hdrs = To: Full Name &lt;user@domain&gt;</SAMP></CENTER>
+<P>
+Headers set in this way are different from headers set with the
+customized-hdrs option in that the value you give for a header here
+will replace any value that already exists.
+For example, if you are Replying to a message there will already be at
+least one address in the To header (the address you are Replying to).
+However, if you Reply using a role which sets the To header, that role's
+To header value will be used instead.
+The customized-hdrs headers are defaults.
+<P>
+Limitation: Because commas are used to separate the list of
+Other Headers, it is not possible to have the value of a
+header contain a comma;
+nor is there currently an &quot;escape&quot; mechanism provided
+to make this work.
+
+<H4>Set Fcc</H4>
+
+This field consists of a single folder name which will be used in
+the Fcc field of the message you are sending.
+You may put anything here that you would normally type into the Fcc
+field from the composer.
+<P>
+In addition, an fcc of &quot;&quot; (two double quotation marks) means
+no Fcc.
+<P>
+A blank field here means that <EM>Alpine</EM> will use its normal rules for deciding
+the default value of the Fcc field.
+For many roles, perhaps most, it may make more sense for you to use the
+other <EM>Alpine</EM> facilities for setting the Fcc.
+In particular, if you want the Fcc to depend on who you are sending the
+message to then the <A HREF="#fcc-name-rule"><EM>fcc-name-rule</EM></A>
+is probably more useful.
+In that case, you would want to leave the Fcc field here blank.
+However, if you have a role that depends on who the message you are replying
+to was From, or what address that message was sent to;
+then it might make sense to set the Fcc for that role here.
+
+<H4>Set LiteralSig</H4>
+
+This field contains the actual text for your signature, as opposed to
+the name of a file containing your signature.
+If this is defined it takes precedence over any value set in the
+<EM>Set Signature</EM> field.
+<P>
+This is simply a different way to store the signature.
+The signature is stored inside your Alpine configuration file instead of in
+a separate signature file.
+Tokens work the same way they do with <EM>Set Signature</EM>.
+<P>
+
+The two character sequence &#92;n (backslash followed by
+the character n) will be used to signify a line-break in your signature.
+You don't have to enter the &#92;n, but it will be visible in the
+CHANGE THIS ROLE RULE window after you are done editing the signature.
+
+<H4>Set Signature</H4>
+
+The Signature is the name of a file to be used as the signature file when
+this role is being used.
+If the filename is followed by a vertical bar (|) then instead
+of reading the contents of the file the file is assumed to be a
+program which will produce the text to be used on its standard output.
+The program can't have any arguments and doesn't receive any input from <EM>Alpine</EM>,
+but the rest of the processing works as if the contents came from a file.
+
+<P>
+Signature files may be stored remotely on an IMAP server.
+In order to do that you just give the file a remote name.
+This works just like the regular
+<A HREF="#sig-file">signature-file</A>
+option which is configured from the Setup/Configuration screen.
+A remote signature file name might look like:
+<P>
+<CENTER><SAMP>{myimaphost.myschool.k12.wa.us}mail/sig3</SAMP></CENTER>
+<P>
+or, if you have an SSL-capable version of <EM>Alpine</EM>, you might try
+<P>
+<CENTER><SAMP>{myimaphost.myschool.k12.wa.us/user=loginname/ssl}mail/sig3</SAMP></CENTER>
+<P>
+Once you have named the remote signature file you create its
+contents by using the &quot;F&quot; &quot;editFile&quot; command when the
+cursor is on the &quot;Set Signature&quot;
+line of the role editor.
+
+<P>
+Besides containing regular text, a signature file may also
+contain (or a signature program may produce) tokens which are replaced with text
+which depends on the message you are replying to or forwarding.
+The tokens all look like _word_ (a word surrounded by underscores).
+For example, if the token
+<P>
+<CENTER><SAMP>_DATE_</SAMP></CENTER>
+<P>
+is included in the text of the signature file, then when you reply to
+or forward a message, the token will be replaced with the actual date
+the message you are replying to or forwarding was sent.
+<P>
+If you use a role which has a signature file for a plain composition
+(that is, not a reply or forward) then there is no original message, so
+any tokens which depend on the message will be replaced with nothing.
+So if you want a signature file to be useful for new compositions it
+shouldn't include any of the tokens which depend on the message being
+replied to or forwarded.
+<P>
+The list of available tokens is
+<A HREF="#index-tokens">here</A>.
+<P>
+Actually, for the adventurous, there is a way to conditionally include text based
+on whether or not a token would result in specific replacement text.
+For example, you could include some text based on whether or not
+the _NEWS_ token would result in any newsgroups if it was used.
+It's explained in detail
+<A HREF="#reply-token-conditionals">here</A>.
+<P>
+In the very unlikely event that you want to include a literal token in
+a signature file, you must precede it with a backslash character.
+For example, to include the literal text _DATE_ you must actually use
+&#92;_DATE_.
+It is not possible to have a literal backslash followed by an expanded token.
+<P>
+A blank field here means that <EM>Alpine</EM> will use its normal rules for deciding
+which file (if any) to use for the signature file.
+
+<H4>Set Template</H4>
+
+A Template is the name of a file to be included in the message when this
+role is being used.
+The template file is a file which is included at the top of the message you
+are composing.
+<P>
+If the filename is followed by a vertical bar (|) then instead
+of reading the contents of the file the file is assumed to be a
+program which will produce the text to be used on its standard output.
+The program can't have any arguments and doesn't receive any input from <EM>Alpine</EM>,
+but the rest of the processing works as if the contents came from a file.
+<P>
+Template files may be stored remotely on an IMAP server.
+In order to do that you just give the file a remote name.
+This works just like the regular
+<A HREF="#sig-file">signature-file</A>
+option which is configured from the Setup/Configuration screen.
+A remote template file name might look like:
+<P>
+<CENTER><SAMP>{myimaphost.myschool.k12.wa.us}mail/templ3</SAMP></CENTER>
+<P>
+or, if you have an SSL-capable version of <EM>Alpine</EM>, you might try
+<P>
+<CENTER><SAMP>{myimaphost.myschool.k12.wa.us/user=loginname/ssl}mail/templ3</SAMP></CENTER>
+<P>
+Once you have named the remote template file you create its
+contents by using the &quot;F&quot; &quot;editFile&quot; command when the
+cursor is on the &quot;Set Template&quot;
+line of the role editor.
+<P>
+Besides containing regular text, a template file may also
+contain (or a template file program may produce) tokens which are replaced with text
+which depends on the message you are replying to or forwarding.
+The tokens all look like _word_ (a word surrounded by underscores).
+For example, if the token
+<P>
+<CENTER><SAMP>_DATE_</SAMP></CENTER>
+<P>
+is included in the text of the template file, then when you reply to
+or forward a message, the token will be replaced with the actual date
+the message you are replying to or forwarding was sent.
+<P>
+If you use a role which has a template file for a plain composition
+(that is, not a reply or forward) then there is no original message, so
+any tokens which depend on the message will be replaced with nothing.
+So if you want a template file to be useful for new compositions it
+shouldn't include any of the tokens which depend on the message being
+replied to or forwarded.
+<P>
+The list of available tokens is
+<A HREF="#index-tokens">here</A>.
+<P>
+Actually, for the adventurous, there is a way to conditionally include text based
+on whether or not a token would result in specific replacement text.
+For example, you could include some text based on whether or not
+the _NEWS_ token would result in any newsgroups if it was used.
+It's explained in detail
+<A HREF="#reply-token-conditionals">here</A>.
+<P>
+In the very unlikely event that you want to include a literal token in
+a template file, you must precede it with a backslash character.
+For example, to include the literal text _DATE_ you must actually use
+&#92;_DATE_.
+It is not possible to have a literal backslash followed by an expanded token.
+<P>
+A blank field here means that <EM>Alpine</EM> will not use a template file when
+this role is being used.
+
+<H4>Use SMTP Server</H4>
+
+If this field has a value, then it will be used as the SMTP server
+to send mail when this role is being used (unless the SMTP server variable
+is set in the system-wide fixed configuration file).
+It has the same semantics as the
+<A HREF="#smtp-server"><EM>smtp-server</EM></A>
+variable in the Setup/Config screen.
+When you postpone the composition this SMTP server list will be saved
+with the postponed composition and it cannot be changed later.
+Because of that, you may want to make this a list of SMTP servers
+with the preferred server at the front of the list and alternate servers
+later in the list.
+
+<P>
+If any of the actions are left unset, then the action depends on what
+is present in the &quot;Initialize settings using role&quot; field.
+If you've listed the nickname of another one of your roles there, then the
+corresponding action from that role will be used here.
+If that action is also blank, or if there is no nickname specified,
+then <EM>Alpine</EM> will do whatever it normally does to set these actions.
+This depends on other configuration options and features you've set.
+
+<H2><A NAME="filter-config"></A>Filtering Configuration</H2>
+
+The software which actually delivers mail (the stuff that happens
+before <EM>Alpine</EM> is involved) for you is in a better position to do mail filtering
+than <EM>Alpine</EM> itself.
+If possible, you may want to look into using that sort of mail filtering to
+deliver mail to different folders, delete it, or forward it.
+However, if you'd like <EM>Alpine</EM> to help with this, <EM>Alpine</EM>'s filtering is for you.
+<P>
+Filtering is a way to automatically move certain messages from one folder
+to another or to delete messages.
+It can also be used to set message status bits (Important, Deleted, New,
+Answered).
+<EM>Alpine</EM> doesn't have the ability to forward mail to another address.
+<P>
+Each filtering rule has a &quot;Pattern&quot; and a &quot;Filter Action&quot;.
+When a folder is opened, when new mail arrives in an open folder, or
+when mail is Expunged from a folder; each
+message is compared with the Patterns of your filtering rules.
+The comparisons start with the first rule and keep going until there
+is a match.
+If a match is found, the message may be deleted or moved, depending on
+the setting of the Filter Action.
+If the message is not deleted, it may have its status altered.
+<P>
+For efficiency, each message is usually only checked once.
+When new mail arrives, the new messages are checked but not the old.
+There are some exceptions to this rule.
+The expunge command will cause all messages to be rechecked, as will
+editing of the filtering rules.
+
+<P>
+<EM>NOTE:</EM>
+When setting up a Pattern used to delete messages,
+it is recommended that you test the Pattern first with a &quot;Move&quot;
+folder specified in
+case unintended matches occur. Messages that are deleted will be removed
+from the folder and <EM>unrecoverable</EM> from within <EM>Alpine</EM> after the
+next Expunge command or once the folder being filtered has been closed.
+
+<H3>Filter Patterns</H3>
+
+In order to determine whether or not a message matches a filter the message is
+compared with the Filter's Pattern.
+These Patterns are the same for use with Roles, Filtering, Index Coloring,
+Scoring, Other Rules, and Search Rules, so are described in only one place,
+&quot;<A HREF="#patterns-section">here</A>&quot;.
+<P>
+Since filtering is a potentially destructive action, if you have a filtering
+Pattern with nothing other than Current Folder Type set, that filtering
+rule is ignored.
+
+<H3>Filter Actions</H3>
+
+Once a filter match is found for a particular message, there are some actions
+which may be taken.
+First, the message may have its status changed.
+This is the same message status that you can manipulate manually using the
+Flag Command.
+There are four elements of message status that you can control.
+You can set or clear the Important status, the New status, the Deleted
+status, and the Answered status.
+Of course, if the filter is going to delete the message,
+then there is no point in setting message status.
+You may also set or clear user-defined
+<A HREF="#keywords">keywords</A> for a message.
+<P>
+Second, the filter may delete or move the message.
+Deleting the message marks it Deleted and removes it from view.
+It is effectively gone forever (though it technically is still there until
+the next expunge command, which may happen implicitly).
+Moving the message moves it from the open folder into the folder
+listed on the &quot;Folder List&quot; line of the filter configuration.
+If you list more than one folder name (separated by commas) then the message
+will be copied to each of those folders.
+In any case, if &quot;Delete&quot; or &quot;Move&quot; is set then the
+message is removed from the current folder.
+If you just want to set the messages status without deleting it from
+the folder, then set the filter action to
+&quot;Just Set Message Status&quot;.
+<P>
+(There is no way to do a Copy instead of a Move, due to the difficulties
+involved in keeping track of whether or not a message has
+already been copied by a previous <EM>Alpine</EM> session.)
+
+<H4><A NAME="move-only-if-not-deleted"></A>Move-only-if-not-deleted option</H4>
+
+If you have specified a Move to Folder to filter messages into, then this
+option has an effect.
+If this option is set then messages will only be moved into the specified folder
+if they aren't already marked deleted.
+This might be useful if you have more than one <EM>Alpine</EM> session running
+simultaneously and you don't want messages to be filtered into a folder
+more than once.
+This method is not foolproof.
+There may be cases where a message gets marked deleted and so it is never
+filtered into the folder.
+For example, if you deleted it in another <EM>Alpine</EM> or
+another mail program that didn't know about the filtering rule.
+<P>
+This option has no effect if the Filter Action is not set to Move.
+
+<H4><A NAME="dont-quit-even-if-rule-matches"></A>Dont-quit-even-if-rule-matches option</H4>
+
+If this option is set then this is a non-terminating rule.
+Usually, for each message, <EM>Alpine</EM> searches through the filter rules until
+a match is found and then it performs the action associated with that rule.
+Rules following the match are not considered.
+If this option is set then the search for matches will continue at the next
+rule.
+<P>
+If a non-terminating rule matches then the actions associated with
+that rule, except for any implied deletion of the message, are performed
+before the match for the next rule is checked.
+For example, if the non-terminating rule sets the Important status, then that
+status will be set when the next rule is considered.
+However, if the non-terminating rule Moves the message, the message will
+actually be copied instead of copied and deleted so that it is still there
+for the next rule.
+A moved message is deleted after all the relevant rules have been checked.
+The name of the &quot;Move&quot; action is confusing in this case because
+a single message can be moved to more than one folder.
+It turns the Move into a Copy instead, but it is still followed by a deletion
+at the end.
+<P>
+This option may be useful if you want to have a single message filtered to
+two different folders because it matches two different Patterns.
+For example, suppose you normally filter messages to a particular mailing
+list into one folder, and messages addressed directly to you into a second
+folder.
+If a message is sent to both you and the list (and you can tell that by
+looking at the headers of the message) this option may give you a convenient
+way to capture a copy to each folder.
+(It may also cause you to capture two copies to each folder,
+depending on whether your mail system delivers one or two copies of the
+message to you and on how the list works.)
+
+<H2><A NAME="scoring-config"></A>Scoring Configuration</H2>
+
+Most people will not use scores at all, but if you do use them, here's how
+they work in Alpine.
+Using this screen, you may define Scoring rules.
+The score for a message is calculated by looking at every Score rule defined
+and adding up the Score Values for the ones which match the message.
+If there are no matches for a message, it has a score of zero.
+Message scores may be used a couple of ways in Alpine.
+
+<H3>Sorting by Score</H3>
+
+One of the methods you may use to sort message indexes is to sort by
+score.
+The scores of all the messages in a folder will be calculated and then
+the index will be ordered by placing the messages in order of ascending or
+descending score.
+
+<H3>Scores for use in Patterns</H3>
+
+The Patterns used for Roles, Index Line Coloring, and Filtering have a
+category labeled &quot;Score Interval&quot;.
+When a message is being compared with a Pattern to check for a match, if
+the Score Interval is set only messages which have a score somewhere in
+the interval are a match.
+
+<H3>Scoring Rule Patterns</H3>
+
+In order to determine whether or not a message matches a scoring rule
+the message is compared with the rule's Pattern.
+These Patterns are the same for use with Roles, Filtering, Index Coloring,
+Scoring, Other Rules, and Search Rules, so are described in only one place,
+&quot;<A HREF="#patterns-section">here</A>&quot;.
+
+<P>
+Actually, Scoring rule Patterns are slightly different from the other types of
+Patterns because Scoring rule Patterns don't contain a Score Interval.
+In other words, when calculating the score for a message, which is done
+by looking at the Scoring rule Patterns, scores aren't used.
+
+<H3>Score Value</H3>
+
+This is the value that will be added to the score for a message if the
+rule's Pattern is a match.
+Each individual Score Value is an integer between -100 and 100, and the
+values from matching rules are added together to get a message's score.
+There is also a way to extract the value from a particular header of each
+message. See the help text for Score Value for further information.
+
+<H2><A NAME="other-config"></A>Other Rules Configuration</H2>
+
+Using this screen, you may define configuration Rules which don't fit
+nicely into the other Rules categories.
+
+<H3>Other Rule Patterns</H3>
+
+Other Rules are a little different from the rest of the Rules because
+they depend only on the current folder, and not on a particular message.
+In order to determine whether or not a rule's actions should be applied
+the current folder is compared with the rule's Pattern, which consists
+of only the Current Folder Type.
+Current Folder Type works the same for Other Rules as it does for Roles,
+Filtering, Index Coloring, and Scoring.
+Keep in mind that the only part of the Pattern which applies to Other
+Rules is the Current Folder Type when looking at the description of
+Patterns given
+&quot;<A HREF="#patterns-section">here</A>&quot;.
+
+<H3>Other Rule Actions</H3>
+
+Once a pattern match is found, the rule's Actions are taken.
+Neither of the following two rule's depends on a message for its match.
+That means that all the parts of the Pattern which depend on matching an
+attribute of a message are ignored.
+So the only part of the Pattern that matters for these Actions is
+the Current Folder Type.
+
+<H4>Set Sort Order</H3>
+
+When you enter a new folder, these rules will be checked to see if you
+have set a sort order which is different from your default sort order.
+The default is set in the Setup/Config screen with
+the <A HREF="#sort-key">Sort-Key</A> option.
+If the Sort Order action is set, then the folder will be displayed sorted in
+that sort order instead of in the default order.
+<P>
+A possible point of confusion arises when you change the configuration
+of the Sort Order for the currently open folder.
+The folder will normally be re-sorted when you go back to viewing the
+index.
+However, if you have manually sorted the folder with the
+Sort command, it will not be re-sorted.
+
+<H4>Set Index Format</H3>
+
+When you enter a new folder, these rules will be checked to see if you
+have set an Index Format which is different from your default Index Format,
+which is set with the
+<A HREF="#index-format"><EM>Index-Format</EM></A> option.
+If so, the index will be displayed with this format instead of the default.
+
+<H4>Set Startup Rule</H4>
+
+When you enter a new folder, these rules will be checked to see if you
+have set a startup rule which is different from the default startup rule.
+The default for incoming folders is set in the Setup/Config screen with
+the &quot;incoming-startup-rule&quot; option.
+The default for folders other than INBOX that are not part of your
+incoming collection
+(see <A HREF="#enable-incoming-folders">enable-incoming-folders</A> feature)
+is to start with the last message in the folder.
+If the Startup Rule is set to something other than &quot;default&quot;,
+then the rule will determine which message will be the current message when
+the folder is first opened.
+<P>
+The various startup rule possibilities work the same here as they do in
+the incoming collection, except that the folder can be any specific
+folder or any folder type.
+
+<H2><A NAME="search-rules-config"></A>Search Rules Configuration</H2>
+
+One of the commands that becomes available when that feature is turned on
+is the &quot;; Select&quot; command, which is used in the MESSAGE INDEX
+screen to select a set of messages.
+One way of selecting messages is to use a Rule.
+All of the messages which match (or don't match if you wish)
+a Rule's Pattern will be selected.
+<P>
+Any of your Rules may be used for this purpose.
+You might already have Rules set up for filtering, index line color, scores, or roles;
+and you may use any of those Rules with the Select command.
+However, you might find it more convenient to set up a separate set of Rules
+just for this purpose without having to worry about what other effects
+they may cause.
+That is the purpose of these Select Rules.
+
+<H3>Rule Patterns</H3>
+
+In order to determine whether or not a message is selected by a rule the
+message is
+compared with the rule's Pattern.
+These Patterns are the same for use with Roles, Filtering, Index Coloring,
+Scoring, Other Rules, and Search Rules, so are described in only one place,
+&quot;<A HREF="#patterns-section">here</A>&quot;.
+<P>
+There is no action associated with these Search Rules.
+Only their Patterns are used.
+
+<H2><A NAME="patterns-section"></A>Patterns</H2>
+
+Patterns are used with Roles, Filtering, Index Coloring,
+Scoring, Other Rules, and Search Rules.
+Patterns are compared with a message to see if there is a match.
+For Filtering, the messages being checked are all the messages in the
+folder, one at a time.
+For Index Line Coloring, each message that is visible on the screen is
+checked for matches with the Index Coloring Patterns.
+Roles are used with the Reply, Forward, and Compose commands.
+For Reply, the message used to compare the Pattern with is the message
+being replied to;
+for Forward, the message used to compare the Pattern with is the message
+being forwarded;
+and for Compose, there is no message, so the parts of the Pattern which depend
+on a message (everything other than Current Folder Type and the
+Beginning of Month and Year)
+are not used.
+Only the Current Folder Type matters for Compose (plus the Beginning of
+Month or Year, which you wouldn't usually use for a Role).
+For Scoring, the message being scored is compared with all of the Score
+Patterns, and the Score Values from the ones that match are added together to
+get the message's score.
+For Other Rules, there is no message. Only the Current Folder Type is checked
+for Other Rules.
+<P>
+Each Pattern has several possible parts, all of which are optional.
+In order for there to be a match, <EM>ALL</EM> of the
+<EM>defined</EM> parts of the Pattern must match the message.
+If a part is not defined it is considered a match.
+For example, if the To pattern is not defined it will be
+displayed as
+<P>
+<CENTER>To pattern = &lt;No Value Set&gt;</CENTER>
+<P>
+That is considered a match because it is not defined.
+This means that the Pattern with nothing defined is a match if the
+Current Folder Type matches, but there is an exception.
+Because filtering is a potentially destructive action, filtering Patterns
+with nothing other than Current Folder Type defined are ignored.
+If you really want a filtering Pattern to match all messages (subject to
+Current Folder Type) the best way to do it is to define a Score interval
+which includes all possible scores.
+This would be the score interval <SAMP>(-INF,INF)</SAMP>.
+This can be used even if you haven't defined any rules to Set Scores.
+<P>
+There are six predefined header patterns called the To, From, Sender, Cc, News,
+and Subject patterns.
+Besides those six predefined header patterns, you may add
+additional header patterns with header fieldnames of your choosing.
+You add an extra header pattern by placing the cursor on one of the
+patterns while in the role editor and using the &quot;eXtraHdr&quot; command.
+The Recip pattern is a header pattern which stands for Recipient (To OR Cc)
+and the Partic pattern is a header pattern which stands for
+Participant (From OR To OR Cc).
+(Defining the Recip pattern does not have the same effect as defining both
+the To and Cc patterns. Recip is To <EM>OR</EM> Cc, not To <EM>AND</EM> Cc.)
+Similar to the header patterns are the AllText pattern and the BodyText pattern.
+Instead of comparing this pattern's text against only the contents of
+a particular header field, the text for the AllText pattern is compared
+with text anywhere in the message's header or body, and the text for the
+BodyText pattern is compared with text anywhere in the message's body.
+<P>
+Any of the header patterns, the AllText pattern, or the BodyText pattern may be negated with the
+&quot;!&quot; &quot;toggle NOT&quot; command.
+You can tell that <EM>NOT</EM> has been turned on by looking for the character
+&quot;!&quot; at the beginning of the pattern line.
+When the &quot;!&quot; is present, it reverses the meaning of the match.
+That is, if the pattern matches then it is considered to NOT be a match, and
+if it does not match it is considered to be a match.
+<P>
+Don't make the mistake of putting the &quot;!&quot; in the data field for
+a pattern.
+For example, if you type the characters &quot;!urgent&quot; into the Subject
+pattern, the pattern will look like:
+<P>
+<PRE>
+ Subject pattern = !urgent
+</PRE>
+<P>
+This means you want to match the 7 character sequence &quot;!urgent&quot;.
+In order to match messages which do not have &quot;urgent&quot; in
+their Subject field, first type the characters &quot;urgent&quot; followed
+by carriage return for the value of the Subject pattern, then negate it
+by typing the &quot;!&quot; command.
+It should look like
+<P>
+<PRE>
+ ! Subject pattern = urgent
+</PRE>
+<P>
+The contents of each of these header patterns (or the AllText or BodyText patterns) may
+be a complete email address, part of an address, or a random set of
+characters to match against.
+It may also be a list of such patterns, which means you
+are looking for a match against the first pattern in the list <EM>OR</EM>
+the second pattern <EM>OR</EM> the third and so on.
+For example, a Subject pattern equal to
+<P>
+<PRE>
+ Subject pattern = urgent
+ emergency
+ alert
+</PRE>
+<P>
+would match all messages with a subject which contained at least one
+of those words.
+It would also match subjects containing the words &quot;alerts&quot; or
+&quot;Urgently&quot;.
+<P>
+The same example with &quot;NOT&quot; turned on would be
+<P>
+<PRE>
+ ! Subject pattern = urgent
+ emergency
+ alert
+</PRE>
+<P>
+which would match all messages with a subject which did NOT contain any of
+those words.
+You can use the &quot;Add Value&quot; command to add new words to the list,
+or you can enter them as a comma-separated list.
+<P>
+(It is not possible to specify two patterns which must <EM>BOTH</EM> be
+present for a match.
+It is only possible to specify that <EM>EITHER</EM> pattern1 <EM>OR</EM>
+pattern2 must be present,
+and that is exactly what using a list does.)
+<P>
+The &quot;Current Folder Type&quot; and the &quot;Score Interval&quot; are
+also part of the Pattern, although the &quot;Score Interval&quot; is not used
+when checking for matches for Scoring.
+There are five similar settings which relate to the status of the message.
+These settings rely on the message being New or not, Deleted or not,
+Answered or not, Important or not, and Recent or not.
+There are also some other miscellaneous settings.
+The first is the Age of the message in days.
+Another is the Size of the message in bytes.
+The third is a setting which detects whether or not the Subject of a
+message contains raw 8-bit characters (unencoded characters with the most
+significant bit set).
+There is a setting which detects whether or not this is the first time
+<EM>Alpine</EM> has been run this month (doesn't depend on individual messages),
+and another which detects whether or not this is the first time <EM>Alpine</EM> has
+been run this year.
+Other parts of the Pattern detect whether or not the From address of a
+message appears in your address book, whether or not certain keywords
+are set for a message, and whether or not certain character sets are
+used in a message.
+
+<H3>Parts of a Pattern</H3>
+
+<H4>Header patterns</H4>
+
+A header pattern is simply text which is searched for in the corresponding
+header field.
+For example, if a Pattern has a From header pattern with the value
+&quot;@company.com&quot;, then only messages which have a From header
+which contains the text &quot;@company.com&quot; will be possible
+matches.
+Matches don't have to be exact.
+For example, if the relevant field of a message contains the text
+&quot;mailbox@domain&quot; somewhere
+in it, then header patterns of &quot;box&quot;, or &quot;x@d&quot;, or
+&quot;mailbox@domain&quot; are all matches.
+<P>
+All parts of the Pattern must match so, for example,
+if a message matches a defined
+From pattern, it still must be checked against the other parts of the
+Pattern which have been defined.
+The To header pattern is a slightly special case.
+If the message being checked has a Resent-To header
+and the feature <A HREF="#use-resent-to-in-rules">Use-Resent-To-in-Rules</A> is turned on, the addresses
+there are used in place of the addresses in the To header.
+This is only true for the To header.
+Resent-cc and Resent-From headers are never used unless you add them
+with the eXtraHdrs command.
+<P>
+The meaning of a header pattern may be negated with the
+&quot;!&quot; &quot;toggle NOT&quot; command.
+You can tell that <EM>NOT</EM> has been turned on by looking for the character
+&quot;!&quot; at the beginning of the pattern line.
+It would look something like
+<P>
+<PRE>
+ ! From pattern = susan@example.com
+</PRE>
+<P>
+When the &quot;!&quot; is present, it reverses the meaning of the match.
+<P>
+If you want to check for the presence of a header field but don't care
+about its value, then
+the empty pattern which you get by entering a pair of
+double quotes (&quot;&quot;) should match any message which
+has the corresponding header field.
+
+<H4><A NAME="pattern_alltext">AllText patterns</A></H4>
+
+AllText patterns are just like header patterns except that the text is
+searched for anywhere in the message's headers or body, not just in the
+contents of a particular header field.
+<P>
+
+<H4><A NAME="pattern_bodytext">BodyText patterns</A></H3>
+
+BodyText patterns are just like header patterns except that the text is
+searched for anywhere in the message's body, not just in the
+contents of a particular header field.
+<P>
+
+If there is more than one header pattern or AllText pattern or BodyText pattern
+for which you want to take the
+same action there is a shorthand notation which may be used.
+Any of these patterns may be a list of patterns instead of
+just a single pattern.
+If any one of the patterns in the list matches the message
+then it is considered a match.
+For example, if &quot;company1&quot; and &quot;company2&quot; both required
+you to use the same role when replying to messages, you might have
+a To pattern which looks like
+<P>
+<PRE>
+ To pattern = company1.com
+ company2.com
+</PRE>
+<P>
+This means that if the mail you are replying to was addressed to
+either &quot;anything@company1.com&quot; or &quot;anything@company2.com&quot;,
+then this Pattern is a match and the same actions will be taken.
+<P>
+The meaning of an AllText or BodyText pattern may be negated with the
+&quot;!&quot; &quot;toggle NOT&quot; command.
+You can tell that <EM>NOT</EM> has been turned on by looking for the character
+&quot;!&quot; at the beginning of the pattern line.
+When the &quot;!&quot; is present, it reverses the meaning of the match.
+<P>
+A technicality: Since comma is the character used to separate multiple values
+in any of the fields which may have multiple values (such as header patterns,
+AllText patterns, BodyText patterns, keywords, folder lists, and so on),
+you must escape comma with a
+backslash (&#92;) if you want to include a literal comma in one of those fields.
+In other words, if you type a backslash followed by a comma it will
+be interpreted as a comma by <EM>Alpine</EM>, instead of as a separator between
+pattern values.
+All other backslashes (those not followed by a comma) are literal
+backslashes and should not be escaped.
+It's unlikely you'll ever need to enter a literal comma or backslash in
+any of the patterns.
+
+<H4>Current Folder Type</H4>
+
+The &quot;Current Folder Type&quot; may be set to one of four different
+values: &quot;Any&quot;, &quot;News&quot;, &quot;Email&quot;, or
+&quot;Specific&quot;.
+If the value is set to &quot;News&quot;, then the
+Pattern will only match if the currently open folder is a newsgroup.
+The value &quot;Email&quot; only matches if the current folder is not news and
+the value &quot;Any&quot; causes any folder to match.
+If the value of &quot;Current Folder Type&quot; is set to &quot;Specific&quot;,
+then you must fill in a value for &quot;Folder&quot;, which is on the line
+below the &quot;Specific&quot; line.
+In this case you will only get a match if the currently open folder is
+the specific folder you list.
+You may give a list of folders instead of just a single
+folder name, in which case the Pattern will match if the open folder is
+any one of the folders in the list.
+The name of each folder in the list may be either &quot;INBOX&quot;,
+the technical specification
+of the folder (like what appears in your configuration file) or, if the
+folder is one of your incoming folders, it may be the nickname you've given
+the folder.
+Here are some samples of specific folder names:
+<P>
+<CENTER><SAMP>{monet.art.example.com}mail/art-class</SAMP></CENTER>
+<P>
+<CENTER><SAMP>{news.example.com/nntp}#news.comp.mail.pine</SAMP></CENTER>
+<P>
+<CENTER><SAMP>mail/local-folder</SAMP></CENTER>
+<P>
+The easiest way to fill in the &quot;Folder&quot; field is to use
+the &quot;T&quot; command which is available when the &quot;Folder&quot; line is
+hilighted, or to use the &quot;Take&quot; command with the configuration
+feature
+<A HREF="#enable-rules-under-take">&quot;enable-rules-under-take&quot;</A>
+turned on.
+<P>
+When reading a newsgroup, there may be a performance penalty
+incurred when collecting the information necessary to check whether
+or not a Pattern matches a message.
+For this reason, the default Current Folder Type is set to &quot;Email&quot;.
+If you have Patterns with a Current Folder Type of either
+&quot;Any&quot; or &quot;News&quot; and those Patterns are used for
+Index Line Coloring or Scoring, you may experience
+slower screen redrawing in the MESSAGE INDEX screen when in a newsgroup.
+
+<H4>Age Interval</H4>
+
+The &quot;Age Interval&quot; may be set to an interval of message
+ages which should be considered a match.
+Like the other parts of the Pattern, if it is unset it will be ignored.
+The Age Interval looks like
+<P>
+<CENTER><SAMP>(min_age,max_age)</SAMP></CENTER>
+<P>
+where &quot;min_age&quot; and &quot;max_age&quot; are integers greater
+than or equal to zero.
+The special value &quot;INF&quot; may be used for
+the max value. It represents infinity.
+<P>
+Actually, this option may be defined as a list of intervals instead
+of just a single interval.
+The list is separated by commas.
+It can look like
+<P>
+<CENTER><SAMP>(min_age1,max_age1),(min_age2,max_age2),...</SAMP></CENTER>
+<P>
+When there is an Age Interval defined, it is a match if the age, in days, of
+the message is contained in any of the intervals.
+The intervals include both endpoints.
+<P>
+Even though this option is called Age, it isn't actually
+the <EM>age</EM> of the message.
+Instead, it is how many days ago the message arrived in one of your folders.
+If the current time is a little past midnight, then a message that arrived
+just before midnight arrived yesterday, even though the message is only
+a few minutes old.
+By default, the date being used is not the date in the Date
+header of the message.
+It is the date that the message arrived in one of your folders.
+When you Save a message from one folder to another that arrival date
+is preserved.
+If you would like to use the date in the Date header that is possible.
+Turn on the option
+<EM>use-date-header-for-age</EM>
+near the bottom of the rule definition.
+<P>
+A value of 0 is today, 1 is yesterday, 2 is the day before yesterday, and so on.
+
+<H4><A NAME="pattern_size_interval">Size Interval</A></H3>
+
+The &quot;Size Interval&quot; may be set to an interval of message
+sizes which should be considered a match.
+Like the other parts of the Pattern, if it is unset it will be ignored.
+The Size Interval looks like
+<P>
+<CENTER><SAMP>(min_size,max_size)</SAMP></CENTER>
+<P>
+where &quot;min_size&quot; and &quot;max_size&quot; are integers greater
+than or equal to zero.
+The special value &quot;INF&quot; may be used for
+the max value. It represents infinity.
+<P>
+Actually, this option may be defined as a list of intervals instead
+of just a single interval.
+The list is separated by commas.
+It can look like
+<P>
+<CENTER><SAMP>(min_size1,max_size1),(min_size2,max_size2),...</SAMP></CENTER>
+<P>
+When there is a Size Interval defined, it is a match if the size, in bytes, of
+the message is contained in any of the intervals.
+The intervals include both endpoints.
+
+<H4>Score Interval</H4>
+
+The &quot;Score Interval&quot; may be set to an interval of message
+scores which should be considered a match.
+Like the other parts of the Pattern, if it is unset it will be ignored.
+The Score Interval looks like
+<P>
+<CENTER><SAMP>(min_score,max_score)</SAMP></CENTER>
+<P>
+where &quot;min_score&quot; and &quot;max_score&quot; are integers between
+-32000 and 32000.
+The special values &quot;-INF&quot; and &quot;INF&quot; may be used for
+the min and max values to represent negative and positive infinity.
+<P>
+Actually, a list of intervals may be used if you wish.
+A list would look like
+<P>
+<CENTER><SAMP>(min_score1,max_score1),(min_score2,max_score2),...</SAMP></CENTER>
+<P>
+When there is a Score Interval defined, it is a match if the score for
+the message is contained in any of the intervals in the list.
+The intervals include the endpoints.
+The score for a message is calculated by looking at every Score rule defined and
+adding up the Score Values for the ones which match the message.
+When deciding whether or not a Pattern matches a message for purposes of
+calculating the score, the Score Interval is ignored.
+
+<H4>Message Status</H4>
+
+There are five separate message status settings.
+By default, all five are set to the value &quot;Don't care&quot;, which
+will match any message.
+The value &quot;Yes&quot; means that the particular status must be true
+for a match, and the value &quot;No&quot; means that the particular
+status must not be true for a match.
+For example, one of the five Message Status settings is whether a message
+is marked Important or not.
+A &quot;Yes&quot; means that the message must be Important to be
+considered a match and &quot;No&quot; means that the message must not be
+Important to be considered a match.
+The same is true of the other four message status settings which depend
+on whether or not the message is New; whether the message has
+been Answered or not; whether the message has been Deleted or not, and
+whether the message is Recent or not.
+<P>
+The nomenclature for New and Recent is a bit confusing:
+<P>
+New means that the message is Unseen.
+It could have been in your mailbox for a long time but if you haven't looked
+at it, it is still considered New.
+That matches the default <EM>Alpine</EM> index display that shows an N for such a
+message.
+<P>
+Recent means that the message was added to this folder since the last time
+you opened the folder.
+<EM>Alpine</EM> also shows an N by default for these types of messages.
+If you were to run two copies of <EM>Alpine</EM> that opened a folder one right after
+the other, a message would only show up as Recent in (at most) the first
+<EM>Alpine</EM> session.
+
+<H4>Message Keywords</H4>
+
+Keywords are similar to Message Status, but they are chosen by the user.
+Provided the mail server allows for it, you may add a set of possible keywords
+to a folder and then you may set those keywords or not for each message
+in the folder.
+The syntax of this part of the Pattern is similar to the header patterns.
+It is a list of keywords.
+The Keyword part of the Pattern is a match if the message has any of
+the keywords in the list set.
+Like other parts of the Pattern, if this is unset it will be ignored.
+
+<H4>Message Character Set</H4>
+
+A message may use one or more character sets.
+This part of the Pattern matches messages which make use of one or more of
+the character sets specified in the pattern.
+It will be considered a match if a message uses any of the character
+sets in the list you give here.
+The syntax of this part of the Pattern is similar to the header patterns
+and the Message Keywords pattern.
+It is a list of character sets.
+
+<P>
+Besides actual character set names (for example, ISO-8859-7, KOI8-R, or
+GB2312) you may also use some shorthand names that <EM>Alpine</EM> provides.
+These names are more understandable shorthand names for sets of
+character set names.
+Two examples are &quot;Cyrillic&quot; and &quot;Greek&quot;.
+Selecting one of these shorthand names is equivalent to selecting all of
+the character sets that make up the set.
+You can see all of these shorthand names and the lists of character sets
+they stand for by typing the &quot;T&quot; command with the Character
+Set pattern highlighted.
+The Character Set part of the Pattern is a match if the message uses any
+of the character sets in the list.
+Like other parts of the Pattern, if this is unset it will be ignored.
+
+<H4>Raw 8-bit in Subject</H4>
+
+It seems that lots of unwanted email contains unencoded 8-bit characters
+in the Subject.
+Normally, characters with the 8th bit set are not allowed in the Subject
+header unless they are MIME-encoded.
+This option gives you a way to match messages which have Subjects which
+contain unencoded 8-bit characters.
+Setting this option will affect performance in large folders because the
+subject of each message in the folder has to be checked.
+
+<H4>Beginning of Month</H4>
+
+This option gives you a way to take some action once per month.
+The value &quot;Yes&quot; means that this must be the first time <EM>Alpine</EM> has
+been run this month in order to count as a match,
+
+<H4>Beginning of Year</H4>
+
+This option gives you a way to take some action once per year.
+The value &quot;Yes&quot; means that this must be the first time <EM>Alpine</EM> has
+been run this year in order to count as a match,
+
+<H4>From or Reply-To address in Address Books</H4>
+
+This option gives you a way to match messages which have a From or
+a Reply-To address
+which is in one of your address books.
+Only the simple entries in your address books are searched.
+Address book distribution lists are ignored!
+Setting this option will affect performance in large folders because the
+From and Reply-To of each message in the folder have to be checked.
+
+<H4>Categorizer Command</H4>
+
+This is a command that is run with its standard input set to the message
+being checked and its standard output discarded.
+The full directory path should be specified.
+The command will be run and then its exit status will be checked against
+the Exit Status Interval, which defaults to just the value zero.
+If the exit status of the command falls in the interval, it is considered
+a match, otherwise it is not a match.
+<P>
+
+This option may actually be a list of commands.
+The first one that exists and is executable is used.
+That makes it possible to use the same configuration with Unix <EM>Alpine</EM> and
+<EM>PC-Alpine</EM>.
+<P>
+
+If none of the commands in the list exists and is executable then the rule
+is <EM>not</EM> a match.
+If it is possible that the command may not exist, you should be careful
+to structure your rules so that nothing destructive
+happens when the command does not exist.
+For example, you might have a filter that filters away spam when there is
+a match but does nothing when there is not a match.
+That would continue to work correctly if the command didn't exist.
+However, if you have a filter which filters away spam when there is not
+a match and keeps it when there is a match, that would filter everything
+if the categorizer command didn't exist.
+
+<H3><A NAME="help-for-pattern-config"></A>Help Configuring Pattern Fields</H3>
+
+<DL COMPACT>
+
+<DT> <A NAME="role-nickname"><EM>Nickname</EM></A>
+
+<DD> This is a nickname to help you.
+You should have a different nickname for each role you define.
+The nickname will be used in the SETUP ROLE RULES screen to allow you to
+pick a role to edit.
+It will also be used when you send a message to let you know you are
+sending with a different role than you use by default, and
+it will be useful for choosing a role when composing with the Role command
+or when composing with one of the Role Uses set to With Confirmation.
+This field is not used in the outgoing message.
+<P>
+
+<DT> <A NAME="role-comment"><EM>Comment</EM></A>
+
+<DD> This is a comment to help you.
+This comment does not play any functional role, it is simply an optional
+comment to help you remember what the rule is for.
+<P>
+
+<DT> <A NAME="to-pattern"><EM>To pattern</EM></A>
+
+<DD> If this pattern is non-blank, then for this role to be considered a
+match, at least one of the recipients from
+the To line of the message being replied to or forwarded
+must match this pattern.
+In the case of the Compose command, this pattern and the other header
+patterns are ignored.
+If this pattern is a list of patterns, then at least one of the
+recipients must match at least one of the patterns.
+(Any other non-blank parts of the Pattern must match, too.)
+If the message being replied to or forwarded has a Resent-To header line,
+then that is used in place of the To line.
+(Note that this special Resent rule only applies to the To header. The
+Resent-From, Resent-Subject, and so on are not consulted.)
+<P>
+It is possible to add a <EM>NOT</EM> to the To Pattern meaning with the
+&quot;!&quot; &quot;toggle NOT&quot; command.
+This changes the meaning of the To pattern so that it has the opposite meaning.
+It will be considered a match if there are no matches between the
+addresses in the To: line and the list of To patterns.
+<P>
+Don't make the mistake of putting the &quot;!&quot; in the data field for
+the pattern.
+For example, if you type the characters &quot;!frizzle&quot; into the To
+pattern, the pattern will look like:
+<P>
+<PRE>
+ To pattern = !frizzle
+</PRE>
+<P>
+This means you want to match the 8 character sequence &quot;!frizzle&quot;.
+In order to match messages which do not have &quot;frizzle&quot; in
+their To field, first type the characters &quot;frizzle&quot; followed
+by carriage return for the value of the To pattern, then negate it
+by typing the &quot;!&quot; command.
+It should end up looking like
+<P>
+<PRE>
+ ! To pattern = frizzle
+</PRE>
+<P>
+
+<DT> <A NAME="from-pattern"><EM>From pattern</EM></A>
+
+<DD> This is just like the
+<A HREF="#to-pattern"><EM>To pattern</EM></A>
+except that it is compared with
+the address from the From header of the message being replied to or forwarded instead
+of the addresses from the To header.
+<P>
+
+<DT> <A NAME="sender-pattern"><EM>Sender pattern</EM></A>
+
+<DD> This is just like the
+<A HREF="#to-pattern"><EM>To pattern</EM></A>
+except that it is compared with
+the address from the Sender header of the message being replied to or forwarded instead
+of the addresses from the To header.
+If there is no Sender header, then the From header is used instead.
+<P>
+
+<DT> <A NAME="cc-pattern"><EM>Cc pattern</EM></A>
+
+<DD> This is just like the
+<A HREF="#to-pattern"><EM>To pattern</EM></A>
+except that it is compared with
+the address from the CC header of the message being replied to or forwarded instead
+of the addresses from the To header.
+<P>
+
+<DT> <A NAME="news-pattern"><EM>News pattern</EM></A>
+
+<DD> If this pattern is non-blank, then for this role to be considered a
+match, at least one of the newsgroups from
+the Newsgroups line of the message must match this pattern.
+If this pattern is a list of patterns, then at least one of the
+newsgroups must match at least one of the patterns.
+(Any other non-blank parts of the Pattern must match, too.)
+<P>
+
+<DT> <A NAME="subject-pattern"><EM>Subject pattern</EM></A>
+
+<DD> This is similar to the other header patterns.
+It is compared with
+the contents from the Subject of the message being replied to or forwarded.
+<P>
+If you enter non-ascii characters in this field then the search will be
+done using the character set you have defined with the
+<A HREF="#char-set">Character-Set</A>
+configuration variable.
+(The truly sophisticated may use an alternate character set for a search
+by entering the MIME encoding of the header string here.)
+<P>
+
+<DT> <A NAME="extra-header-patterns"><EM>Extra header patterns</EM></A>
+
+<DD> There isn't actually a field called Extra header patterns, but you
+may add extra header patterns by moving the cursor to one of the header
+patterns and using the &quot;eXtraHdr&quot; command to add a new header
+pattern.
+You would do this if the six predefined header patterns don't cover the
+header you want to use for pattern matching.
+Once you've added an extra header pattern, you use it just like the
+Subject pattern.
+Of course, it is compared with
+the contents from the particular header field of the message being replied
+to or forwarded rather than the contents from the subject field.
+To remove an extra header pattern from a role, use the &quotRemoveHdr&quot;
+command on the highlighted extra header.
+<P>
+If you enter non-ascii characters in this field then the search will be
+done using the character set you have defined with the
+<A HREF="#char-set">Character-Set</A>
+configuration variable.
+(The truly sophisticated may use an alternate character set for a search
+by entering the MIME encoding of the header string here.)
+<P>
+
+<DT> <A NAME="recip-pattern"><EM>Recipient pattern</EM></A>
+
+<DD> This is just like the
+<A HREF="#to-pattern"><EM>To pattern</EM></A>
+except that it is compared with
+the addresses from both the To header and the Cc header
+instead of just the addresses from the To header.
+It's equivalent to having two different rules;
+one with a To pattern and the other with the same Cc pattern.
+<P>
+
+<DT> <A NAME="partic-pattern"><EM>Participant pattern</EM></A>
+
+<DD> This is just like the
+<A HREF="#to-pattern"><EM>To pattern</EM></A>
+except that it is compared with
+the addresses from the To header, the Cc header, and the From header
+instead of just the addresses from the To header.
+It's equivalent to having three different rules;
+one with a To pattern, another with the same Cc pattern, and another
+with the same From pattern.
+<P>
+
+<DT> <A NAME="alltext-pattern"><EM>AllText pattern</EM></A>
+
+<DD> This is similar to the header patterns.
+Instead of comparing with text in a particular header field it is compared with
+all of the text in the message header and body.
+<P>
+If you enter non-ascii characters in this field then the search will be
+done using the character set you have defined with the
+<A HREF="#char-set">Character-Set</A>
+configuration variable.
+(The truly sophisticated may use an alternate character set for a search
+by entering the MIME encoding of the header string here.)
+<P>
+
+<DT> <A NAME="bodytext-pattern"><EM>BodyText pattern</EM></A>
+
+<DD> Just like AllText, except it is compared only with the body of the
+message, not the body and header.
+
+<P>
+If you enter non-ascii characters in this field then the search will be
+done using the character set you have defined with the
+<A HREF="#char-set">Character-Set</A>
+configuration variable.
+(The truly sophisticated may use an alternate character set for a search
+by entering the MIME encoding of the header string here.)
+<P>
+
+<DT> <A NAME="age-interval"><EM>Age Interval</EM></A>
+
+<DD>The Age Interval, if defined, is part of the Pattern.
+If you use this, it should be set to something like:
+<P>
+<CENTER><SAMP>(min_age,max_age)</SAMP></CENTER>
+<P>
+where &quot;min_age&quot; and &quot;max_age&quot; are non-negative integers.
+The special value &quot;INF&quot; may be used for the max value.
+It represents infinity.
+<P>
+In rare cases it may be useful to use the more general form of the value,
+which is a comma-separated list of intervals.
+It would look something like:
+<P>
+<CENTER><SAMP>(min_age1,max_age1),(min_age2,max_age2),...</SAMP></CENTER>
+<P>
+When there is an Age Interval defined, it is a match if the age, in days, of
+the message is contained in the interval.
+The interval includes both endpoints.
+If the option is set to a list of intervals then it is a match if the
+age of the message is contained in any of the intervals.
+<P>
+Even though this option is called Age, it isn't actually
+the <EM>age</EM> of the message.
+Instead, it is how many days ago the message arrived in one of your folders.
+If the current time is a little past midnight, then a message that arrived
+just before midnight arrived yesterday, even though the message is only
+a few minutes old.
+By default, the date being used is not the date in the Date
+header of the message.
+It is the date that the message arrived in one of your folders.
+When you Save a message from one folder to another that arrival date
+is preserved.
+If you would like to use the date in the Date header that is possible.
+Turn on the option
+<EM>use-date-header-for-age</EM>
+near the bottom of the rule definition.
+<P>
+A value of 0 is today, 1 is yesterday, 2 is the day before yesterday, and so on.
+The age interval
+<P>
+<CENTER><SAMP>(2,2)</SAMP></CENTER>
+<P>
+matches all messages that arrived on the day before yesterday.
+The interval
+<P>
+<CENTER><SAMP>(180,INF)</SAMP></CENTER>
+<P>
+matches all messages that arrived at least 180 days before today.
+The interval
+<P>
+<CENTER><SAMP>(0,1)</SAMP></CENTER>
+<P>
+matches all messages that arrived today or yesterday.
+<P>
+
+<DT> <A NAME="score-interval"><EM>Score Interval</EM></A>
+
+<DD> The Score Interval, if defined, is part of the Pattern.
+If you use this, it should be set to something like:
+<P>
+<CENTER><SAMP>(min_score,max_score)</SAMP></CENTER>
+<P>
+where &quot;min_score&quot; and &quot;max_score&quot; are integers between
+-32000 and 32000.
+The special values &quot;-INF&quot; and &quot;INF&quot; can be used for
+the min and max values.
+These represent negative and positive infinity.
+<P>
+Actually, the value may be a list of intervals rather than just a
+single interval if that is useful.
+The elements of the list are separated by commas like:
+<P>
+<CENTER><SAMP>(min_score1,max_score1),(min_score2,max_score2),...</SAMP></CENTER>
+<P>
+When there is a Score Interval defined, it is a match if the score for
+the message is contained in any of the intervals.
+The intervals include both endpoints.
+The score for a message is calculated by looking at every scoring rule
+defined and adding up the Score Values for the rules which match the message.
+<P>
+
+<DT> <A NAME="keyword-pattern"><EM>Keyword pattern</EM></A>
+
+<DD> A folder may have user-defined keywords.
+These are similar to the Important flag which the user may set using the
+Flag command.
+The difference is that the Important flag is always present for each folder.
+User-defined keywords are picked by the user.
+You may add new keywords by defining them in the
+<A HREF="#keywords">Keywords</A> option in the Setup/Config screen.
+After you have added a potential keyword with the Keywords option,
+the Flag command may be used to set or clear the keyword on individual messages.
+If you have given a keyword a nickname when configuring it,
+that nickname may be used instead of the actual keyword.
+
+<P>
+When filling in a value for this field, it may be easiest to use
+the &quot;T&quot; command, which presents you with a list of the keywords
+you have defined to choose from.
+
+<P>
+This part of the Pattern matches messages with certain keywords set.
+It will be considered a match if a message has any of the keywords in the
+list set.
+
+<P>
+It is possible to add a <EM>NOT</EM> to the Keyword Pattern meaning with the
+&quot;!&quot; &quot;toggle NOT&quot; command.
+This changes the meaning of the Keyword pattern so that it has the opposite meaning.
+It will be considered a match if none of the keywords in the list are set
+for a message.
+<P>
+Don't make the mistake of putting the &quot;!&quot; in the data field for
+the pattern.
+For example, if you type the characters &quot;!frizzle&quot; into the Keyword
+pattern, the pattern will look like:
+<P>
+<PRE>
+ Keyword pattern = !frizzle
+</PRE>
+<P>
+This means you want to match the 8 character sequence &quot;!frizzle&quot;.
+In order to match messages which do not have the keyword &quot;frizzle&quot;
+set, first type the characters &quot;frizzle&quot; followed
+by carriage return for the value of the Keyword pattern, then negate it
+by typing the &quot;!&quot; command.
+It should end up looking like
+<P>
+<PRE>
+ ! Keyword pattern = frizzle
+</PRE>
+<P>
+
+<DT> <A NAME="charset-pattern"><EM>Character Set pattern</EM></A>
+
+<DD> A message may use one or more character sets.
+This part of the Pattern matches messages which make use of
+certain specified character sets.
+It will be considered a match if a message uses any of the character
+sets in the list you give here.
+
+<P>
+When filling in a value for this field, you may use
+the &quot;T&quot; command, which presents you with a large list of
+possible character sets to choose from.
+You may also just type in the name of a character set, and it need not
+be one that Alpine knows about.
+
+<P>
+Besides actual character set names (for example, ISO-8859-7, KOI8-R, or
+GB2312) you may also use some shorthand names that Alpine provides.
+These names are more understandable shorthand names for sets of
+character set names.
+Two examples are &quot;Cyrillic&quot; and &quot;Greek&quot;.
+Selecting one of these shorthand names is equivalent to selecting all of
+the character sets that make up the set.
+You can see all of these shorthand names and the lists of character sets
+they stand for by typing the &quot;T&quot; command.
+
+<P>
+For the purposes of this Pattern,
+<EM>Alpine</EM> will search through a message for all of the text parts and
+collect the character sets declared for each part.
+It will also look in the Subject line for a character set used there.
+<EM>Alpine</EM> does not actually look at the text of the message or the text
+of the Subject to determine if a declared character set is actually
+used, it looks only at the declarations themselves in the MIME part headers
+and in the Subject.
+
+<P>
+It is possible to add a <EM>NOT</EM> to the Character Set Pattern meaning with the
+&quot;!&quot; &quot;toggle NOT&quot; command.
+This changes the meaning of the Character Set pattern so that
+it has the opposite meaning.
+It will be considered a match if none of the character sets in the
+list are used in a message.
+<P>
+Don't make the mistake of putting the &quot;!&quot; in the data field for
+the pattern.
+For example, if you type the characters &quot;!GB2312&quot; into the
+Character Set pattern, the pattern will look like:
+<P>
+<PRE>
+ Charset pattern = !GB2312
+</PRE>
+<P>
+This means you want to match the 7 character sequence &quot;!GB2312&quot;.
+In order to match messages which do not have the
+character set &quot;GB2312&quot;
+set, first type the characters &quot;GB2312&quot; followed
+by carriage return for the value of the Character Set pattern, then negate it
+by typing the &quot;!&quot; command.
+It should end up looking like
+<P>
+<PRE>
+ ! Charset pattern = GB2312
+</PRE>
+<P>
+A technicality: Since comma is the character used to separate multiple
+values in a pattern field, you have to escape comma with a backslash (&#92;) if
+you want to include a literal comma in the field.
+In other words, if you type a backslash followed by a comma it will
+be interpreted as a comma by <EM>Alpine</EM>, instead of as a separator between
+pattern values.
+All other backslashes are literal backslashes and should not be escaped.
+<P>
+
+<DT> <A NAME="current-folder-type"><EM>Current Folder Type</EM></A>
+
+<DD> The Current Folder Type is part of the Pattern.
+It refers to the type of the currently open folder, which is the folder
+you were last looking at from the MESSAGE INDEX or MESSAGE TEXT screen.
+In order for a pattern to be considered a match, the current folder must
+be of the type you set here.
+The three types &quot;Any&quot;, &quot;News&quot;, and &quot;Email&quot; are
+all what you might think.
+<P>
+If the Current Folder Type for a Pattern is set to &quot;News&quot;, for
+example, then
+that will only be a match if the current folder is a newsgroup and
+the rest of the Pattern matches.
+The value &quot;Specific&quot; may be used when you want to limit the match
+to a specific folder (not just a specific type of folder), or to a list of
+specific folders.
+In order to match a specific folder you must Select the &quot;Specific&quot;
+button <EM>AND</EM> you must fill in
+the name (or list of names) of
+the folder in the &quot;Folder&quot; field.
+If the current folder is any of the folders in the list, that is considered
+a match.
+The name of each folder in the list may be either &quot;INBOX&quot;, the technical specification
+of the folder (like what appears in your configuration file) or, if the
+folder is one of your incoming folders, it may be the nickname you've given
+the folder.
+Here are a couple samples of specific folder names:
+<P>
+<CENTER><SAMP>{monet.art.example.com}mail/art-class</SAMP></CENTER>
+<P>
+<CENTER><SAMP>{news.example.com/nntp}#news.comp.mail.pine</SAMP></CENTER>
+<P>
+The easiest way to fill in the &quot;Folder&quot; field is to use
+the T command which is available when the &quot;Folder&quot; line is
+hilighted.
+Note that you won't be able to edit the &quot;Folder&quot; line unless the
+Current Folder Type is set to &quot;Specific&quot;, and any value that
+&quot;Folder&quot; has is ignored unless the type
+is set to &quot;Specific&quot;.
+<P>
+When reading a newsgroup, there may be a performance penalty
+incurred when collecting the information necessary to check a Pattern.
+For this reason, the default Current Folder Type is set to &quot;Email&quot;.
+For example, a role with a non-Normal Index Line Color
+and a Current Folder Type of
+&quot;Any&quot; or &quot;News&quot; may cause the MESSAGE INDEX
+screen to draw more slowly when in a newsgroup.
+<P>
+
+<DT> <A NAME="message-status-important"><EM>Message Status Important</EM></A>
+
+<DD> This part of the Pattern may have one of three possible values.
+The default value is &quot;Don't care&quot;, which matches any message.
+The other two values are &quot;Yes&quot;, which means the message must be
+flagged &quot;Important&quot; in order to be a match; or &quot;No&quot;, which
+means the message must <EM>not</EM> be flagged &quot;Important&quot; in order
+to be considered a match.
+<P>
+
+<DT> <A NAME="message-status-new"><EM>Message Status New</EM></A>
+
+<DD> This part of the Pattern may have one of three possible values.
+The default value is &quot;Don't care&quot;, which matches any message.
+The other two values are &quot;Yes&quot;, which means the message must be
+&quot;New&quot; in order to be a match; or &quot;No&quot;, which
+means the message must <EM>not</EM> be &quot;New&quot; in order
+to be a match.
+&quot;New&quot; is the same as <EM>Unseen</EM> and not &quot;New&quot; is the
+same as <EM>Seen</EM>.
+<P>
+The nomenclature for New and Recent is a bit confusing:
+<P>
+New means that the message is Unseen.
+It could have been in your mailbox for a long time but if you haven't looked
+at it, it is still considered New.
+That matches the default <EM>Alpine</EM> index display that shows an N for such a
+message.
+<P>
+Recent means that the message was added to this folder since the last time
+you opened the folder.
+<EM>Alpine</EM> also shows an N by default for these types of messages.
+If you were to run two copies of <EM>Alpine</EM> that opened a folder one right after
+the other, a message would only show up as Recent in (at most) the first
+<EM>Alpine</EM> session.
+<P>
+
+<DT> <A NAME="message-status-recent"><EM>Message Status Recent</EM></A>
+
+<DD> This part of the Pattern may have one of three possible values.
+The default value is &quot;Don't care&quot;, which matches any message.
+The other two values are &quot;Yes&quot;, which means the message must be
+&quot;Recent&quot; in order to be a match; or &quot;No&quot;, which
+means the message must <EM>not</EM> be &quot;Recent&quot; in order
+to be a match.
+&quot;Recent&quot; means that the message was added to the folder since
+the last time the folder was opened.
+If more than one mail client has the folder opened, the message will
+appear to be &quot;Recent&quot; to only one of the clients.
+<P>
+The nomenclature for New and Recent is a bit confusing:
+<P>
+New means that the message is Unseen.
+It could have been in your mailbox for a long time but if you haven't looked
+at it, it is still considered New.
+That matches the default <EM>Alpine</EM> index display that shows an N for such a
+message.
+<P>
+Recent means that the message was added to this folder since the last time
+you opened the folder.
+<EM>Alpine</EM> also shows an N by default for these types of messages.
+If you were to run two copies of <EM>Alpine</EM> that opened a folder one right after
+the other, a message would only show up as Recent in (at most) the first
+<EM>Alpine</EM> session.
+<P>
+
+<DT> <A NAME="message-status-deleted"><EM>Message Status Deleted</EM></A>
+
+<DD> This part of the Pattern may have one of three possible values.
+The default value is &quot;Don't care&quot;, which matches any message.
+The other two values are &quot;Yes&quot;, which means the message must be
+marked &quot;Deleted&quot; in order to be a match; or &quot;No&quot;, which
+means the message must <EM>not</EM> be marked &quot;Deleted&quot; in order
+to be a match.
+<P>
+If you are thinking of using this part of the Pattern as a way to prevent
+messages from being filtered more than once in a Filter Pattern,
+take a look at the Filter Option
+<A HREF="#move-only-if-not-deleted">&quot;move-only-if-not-deleted&quot;</A>
+instead.
+It should work better than using this field since it will hide the filtered
+messages even if they are already Deleted.
+<P>
+
+<DT> <A NAME="message-status-answered"><EM>Message Status Answered</EM></A>
+
+<DD> This part of the Pattern may have one of three possible values.
+The default value is &quot;Don't care&quot;, which matches any message.
+The other two values are &quot;Yes&quot;, which means the message must be
+marked &quot;Answered&quot; in order to be a match; or &quot;No&quot;, which
+means the message must <EM>not</EM> be marked &quot;Answered&quot; in order
+to be a match.
+<P>
+
+<DT> <A NAME="subject-contains-raw-8bit"><EM>Subject Contains Raw 8-bit</EM></A>
+
+<DD> This part of the Pattern may have one of three possible values.
+The default value is &quot;Don't care&quot;, which matches any message.
+The other two values are &quot;Yes&quot;, which means the Subject of
+the message must contain unencoded 8-bit characters (characters with the
+most significant bit set)
+in order to be a match; or &quot;No&quot;, which
+means the Subject must <EM>not</EM>
+contain unencoded 8-bit characters in order to be a match.
+<P>
+
+<DT> <A NAME="beginning-of-month"><EM>Beginning of Month</EM></A>
+
+<DD> This part of the Pattern may have one of three possible values.
+The default value is &quot;Don't care&quot;, which matches any message.
+The other two values are &quot;Yes&quot;, which means this is the first
+time <EM>Alpine</EM> has been run this month;
+or &quot;No&quot;, which
+means this is <EM>not</EM> the first time <EM>Alpine</EM> has been run this month.
+The way that <EM>Alpine</EM> decides if it is the beginning of the month or not is
+to compare today's date with the date stored in the
+<A HREF="#last-time">Last-Time-Prune-Questioned</A>
+variable in the config file.
+If the month of today's date is later than the month stored in the variable,
+then this is considered to be the first time you have run Alpine this month, and
+that turns the Beginning of the Month option on.
+<P>
+
+<DT> <A NAME="beginning-of-year"><EM>Beginning of Year</EM></A>
+
+<DD> This part of the Pattern may have one of three possible values.
+The default value is &quot;Don't care&quot;, which matches any message.
+The other two values are &quot;Yes&quot;, which means this is the first
+time <EM>Alpine</EM> has been run this year;
+or &quot;No&quot;, which
+means this is <EM>not</EM> the first time <EM>Alpine</EM> has been run this year.
+The way that <EM>Alpine</EM> decides if it is the beginning of the year or not is
+to compare today's date with the date stored in the
+<A HREF="#last-time">Last-Time-Prune-Questioned</A>
+variable in the config file.
+If the year of today's date is later than the year stored in the variable,
+then this is considered to be the first time you have run Alpine this year, and
+that turns the Beginning of the Year option on.
+<P>
+
+<DT> <A NAME="from-in-abook"><EM>From or Reply-To in Address Book</EM></A>
+
+<DD> This part of the Pattern may have one of five possible values.
+The default value is &quot;Don't care&quot;, which matches any message.
+The value &quot;Yes, in any address book&quot; means either the From address
+or the Reply-To address of the message must be in at least one of your
+address books in order to be a match.
+The value &quot;No, not in any address book&quot;
+means neither the From nor the Reply-To addresses may
+be in any of your address books in order to be a match.
+<P>
+The values &quot;Yes, in specific address books&quot; and
+&quot;No, not in any of specific address books&quot; are similar but instead
+of depending on all address books you are allowed to give a list of address
+books to look in.
+Usually this would be a single address book but it may be a
+list of address books as well.
+For each of these &quot;specific&quot; address book options you Select which
+of the Specific options you want (Yes or No) <EM>AND</EM> fill in the
+name (or list of names) of the address book in the
+&quot;Abook List&quot; field.
+The names to be used are those that appear in the ADDRESS BOOK LIST screen.
+The easiest way to fill in the Abook List field it to use
+the &quot;T&quot; command which is available when the &quot;Abook List&quot;
+line is highlighted.
+Note that you won't be able to edit the &quot;Abook List&quot; line unless the
+option is set to one of the two &quot;Specific&quot;, values.
+<P>
+
+<DT> <A NAME="categorizer-cmd-explained"><EM>Categorizer Command</EM></A>
+
+<DD> This is a command that is run with its standard input set to the message
+being checked and its standard output discarded.
+The full directory path should be specified.
+The command will be run and then its exit status will be checked against
+the <EM>Exit Status Interval</EM>, which defaults to just the value zero.
+If the exit status of the command falls in the interval, it is considered
+a match, otherwise it is not a match.
+<P>
+
+This option may actually be a list of commands.
+The first one that exists and is executable is used.
+That makes it possible to use the same configuration with Unix <EM>Alpine</EM> and
+<EM>PC-Alpine</EM>.
+<P>
+
+If none of the commands in the list exists and is executable then the rule
+is <EM>not</EM> a match.
+If it is possible that the command may not exist, you should be careful
+to structure your rules so that nothing destructive
+happens when the command does not exist.
+For example, you might have a filter that filters away spam when there is
+a match but does nothing when there is not a match.
+That would continue to work correctly if the command didn't exist.
+However, if you have a filter which filters away spam when there is not
+a match and keeps it when there is a match, that would filter everything
+if the categorizer command didn't exist.
+<P>
+
+The categorizer command is run and the result is the exit status of
+that command.
+If that exit status falls in the <EM>Exit Status Interval</EM>
+then it is considered a match, otherwise it is not a match.
+Of course for the entire rule to match, it must also be checked against
+the other defined parts of the Pattern.
+<P>
+The <EM>Exit Status Interval</EM> defaults to the single value 0 (zero).
+If you define it, it should be set to something like:
+<P>
+<CENTER><SAMP>(min_exit_value,max_exit_value)</SAMP></CENTER>
+<P>
+where &quot;min_exit_value&quot; and &quot;max_exit_value&quot; are integers.
+The special values &quot;INF&quot; and &quot;-INF&quot; may be used for large
+positive and negative integers.
+<P>
+Actually, a list of intervals may be used if you wish.
+A list would look like
+<P>
+<CENTER><SAMP>(min_exit_value1,max_exit_value1),(min_exit_value2,max_exit_value2),...</SAMP></CENTER>
+<P>
+When there is an <EM>Exit Status Interval</EM> defined, it is a match if the exit status
+of the categorizer command is contained in any of the intervals.
+The intervals include both endpoints.
+<P>
+The default interval is
+<P>
+<CENTER><SAMP>(0,0)</SAMP></CENTER>
+<P>
+and it matches only if the command exits with exit status equal to zero.
+<P>
+It is also possible to set a <EM>Character Limit</EM> for the categorizer command.
+Setting this option makes it possible to limit how much of the message
+is made available to the categorizer command as input.
+The default value (-1) means that the entire message is fed to the
+command.
+A value of 0 (zero) means that only the headers of the message are
+made available.
+A positive integer means that the headers plus that many characters from
+the body of the message are passed to the categorizer.
+<P>
+
+</DL>
+
+<H2><A NAME="configuring-news"></A>Configuring News</H2>
+
+<EM>Alpine</EM> can access news folders in any one of three different ways:
+<DL>
+<DT>REMOTE NNTP</DT>
+<DD>Using the Network News Transport Protocol (NNTP) to
+access news on a remote news server. In this case the newsrc file is
+stored on the machine where <EM>Alpine</EM> is running.
+
+<P>
+To specify a remote news-collection accessed via NNTP use the
+SETUP/collectionList screen's &quot;Add&quot; command. Set the
+Server: value to the NNTP server's hostname appended with the
+communication method &quot;/service=NNTP&quot;, and set the Path:
+value to the &quot;#news.&quot; namespace (without the quotes).
+<P>
+Instead of specifying a news-collection, you may simply set the
+<A HREF="#nntp-server">nntp-server</A>
+option, which will cause <EM>Alpine</EM> to create a default news-collection for you.
+Another NNTP option which may be of interest is
+<A HREF="#nntp-range">nntp-range</A>.
+
+<DT>REMOTE IMAP</DT>
+<DD>Using the Internet Message Access Protocol (IMAP) to
+access news on a remote news server. In this case, your newsrc file is
+stored on the news server, in your home directory, so you must have an
+account on the news server, but you would be running <EM>Alpine</EM> on a different
+machine. The news server must be running an IMAPd server process.
+
+<P>
+To specify a remote news-collection accessed via IMAP use the
+SETUP/collectionList screen's &quot;Add&quot; command. Set the
+Server: value to the IMAP server's hostname, and set the Path: value
+to the &quot;#news.&quot; namespace (without the quotes).
+
+</DD>
+
+<DT>LOCAL</DT>
+<DD>Using local file access to the news database. In this
+case, your newsrc file is stored on the news server, in your home
+directory, so you must have an account on the news server, and you would
+be running <EM>Alpine</EM> on the same machine.
+
+<P>
+To specify a local news-collection use the SETUP/collectionList
+screen's &quot;Add&quot; command. Leave the Server: value blank, and
+set the Path: value to the &quot;#news.&quot; namespace (without the
+quotes).
+
+</DD>
+</DL>
+
+<P>
+
+NOTE: Should no news-collection be defined as above, <EM>Alpine</EM> will
+automatically create one using the Setup/Config screen's
+&quot;nntp-server&quot; variable's value if defined. The collection
+will be created as a &quot;Remote NNTP&quot; as described above.
+
+<P>
+
+If you are a <EM>PC-Alpine</EM> user, either option 1 (NNTP) or option 2 (IMAP) is
+possible. If you don't have an account on the news server, or if the news
+server is not running an IMAP daemon, then you must use NNTP. (If you are not
+sure, ask your service provider, university, or company for help.) In
+this case, your Unix .newsrc file can be transferred to your PC. A good
+place to put it would be in the same directory as your PINERC file, under
+the name NEWSRC, but you can
+<A HREF="#newsrc-path">specify a different location</A>.
+
+<P>
+Other configuration features related to news are
+<A HREF="#enable-8bit-nntp-posting">Enable-8bit-Nntp-Posting</A>.
+<A HREF="#compose-sets-newsgroup-without-confirm">Compose-Sets-Newsgroup-Without-Confirm</A>,
+<A HREF="#news-approximates-new-status">News-Approximates-New-Status</A>,
+<A HREF="#news-deletes-across-groups">News-Deletes-Across-Groups</A>,
+<A HREF="#news-offers-catchup-on-close">News-Offers-Catchup-On-Close</A>,
+<A HREF="#news-post-without-validation">News-Post-Without-Validation</A>,
+<A HREF="#news-read-in-newsrc-order">News-Read-in-Newsrc-Order</A>, and
+<A HREF="#quell-extra-post-prompt">Quell-Extra-Post-Prompt</A>.
+
+<HR>
+
+<!-- pnuts -->
+
+</BODY>
+</HTML>
diff --git a/doc/tech-notes/for.pnuts b/doc/tech-notes/for.pnuts
new file mode 100644
index 00000000..ca60d61d
--- /dev/null
+++ b/doc/tech-notes/for.pnuts
@@ -0,0 +1,9 @@
+index.html
+introduction.html
+background.html
+installation.html
+cmd-line.html
+config.html
+ config-notes.html
+low-level.html
+porting.html
diff --git a/doc/tech-notes/index.html b/doc/tech-notes/index.html
new file mode 100644
index 00000000..92240e86
--- /dev/null
+++ b/doc/tech-notes/index.html
@@ -0,0 +1,122 @@
+<HTML>
+<HEAD><TITLE>Alpine Technical Notes</TITLE></HEAD>
+<BODY>
+<H1>Alpine Technical Notes</H1>
+
+Version 2.10, January 2013
+
+<H2><A NAME="TOC">Table of Contents</A></H2><P>
+
+<H3><A HREF="introduction.html">Introduction</A></H3>
+
+<UL>
+<LI><A HREF="introduction.html#design">Design Goals</A><BR>
+<LI><A HREF="introduction.html#components">Alpine Components</A><BR>
+</UL>
+
+<H3><A HREF="background.html">Background Details</A></H3>
+
+<UL>
+<LI><A HREF="background.html#DNS">Domain Names</A><BR>
+<LI><A HREF="background.html#rfc2822">RFC 2822 Compliance</A><BR>
+<LI><A HREF="background.html#SMTP">SMTP and Sendmail</A><BR>
+<LI><A HREF="background.html#IMAP">Internet Message Access Protocol (IMAP)</A><BR>
+<LI><A HREF="background.html#MIME">Multipurpose Internet Mail Extensions (MIME)</A><BR>
+<LI><A HREF="background.html#collections">Folder Collections</A><BR>
+</UL>
+
+<H3><A HREF="installation.html">Building and Installation</A></H3>
+
+<UL>
+<LI><A HREF="installation.html#compile">Compile-time Options</A><BR>
+<LI><A HREF="installation.html#ldap-compile">Including LDAP Functionality</A><BR>
+<LI><A HREF="installation.html#krb5-compile">Including Kerberos 5 Functionality</A><BR>
+<LI><A HREF="installation.html#pine-compile">Other Alpine Compile-time Options</A><BR>
+<LI><A HREF="installation.html#imapd-compile">IMAPd Compile-time Options</A><BR>
+<LI><A HREF="installation.html#build">Building the Alpine Programs</A><BR>
+<LI><A HREF="installation.html#install-unix">Installing Alpine and Pico on UNIX Platforms</A><BR>
+<LI><A HREF="installation.html#install-pc">Installing PC-Alpine</A><BR>
+<LI><A HREF="installation.html#install-imapd">Installing IMAPd</A><BR>
+<LI><A HREF="installation.html#files-unix">Support Files and Environment Variables: UNIX Alpine</A><BR>
+<LI><A HREF="installation.html#files-pc">Support Files, Environment Variables, and Registry Values: PC-Alpine</A><BR>
+</UL>
+
+<H3><A HREF="cmd-line.html">Command Line Arguments</A></H3>
+
+<UL>
+<LI><A HREF="cmd-line.html#alpine">Alpine</A><BR>
+<LI><A HREF="cmd-line.html#pico">Pico</A><BR>
+<LI><A HREF="cmd-line.html#pilot">Pilot</A><BR>
+</UL>
+
+<H3><A HREF="config.html">Configuration and Preferences</A></H3>
+
+<UL>
+<LI><A HREF="config.html#pine-conf">Alpine Configuration</A><BR>
+<LI><A HREF="config.html#gen-conf">General Configuration Variables</A><BR>
+<LI><A HREF="config.html#features-conf">Configuration Features</A><BR>
+<LI><A HREF="config.html#hidden-config">Hidden Config Variables and Features</A><BR>
+<LI><A HREF="config.html#ret-var">Retired Variables</A><BR>
+<LI><A HREF="config.html#index-tokens">Tokens for Index and Replying</A><BR>
+<LI><A HREF="config.html#reply-token-conditionals">Conditional Inclusion of Text for Reply-Leadin, Signatures, and Templates</A><BR>
+<LI><A HREF="config.html#per-server-ldap-config">Per Server Directory Configuration</A><BR>
+<LI><A HREF="config.html#color-config">Color Configuration</A><BR>
+<LI><A HREF="config.html#index-color-config">Index Line Color Configuration</A><BR>
+<LI><A HREF="config.html#role-config">Role Configuration</A><BR>
+<LI><A HREF="config.html#filter-config">Filtering Configuration</A><BR>
+<LI><A HREF="config.html#scoring-config">Scoring Configuration</A><BR>
+<LI><A HREF="config.html#other-config">Other Rules Configuration</A><BR>
+<LI><A HREF="config.html#search-rules-config">Search Rules Configuration</A><BR>
+<LI><A HREF="config.html#patterns-section">Patterns</A><BR>
+<LI><A HREF="config.html#configuring-news">Configuring News</A><BR>
+
+<H4><A HREF="config-notes.html">Configuration Notes</A></H4>
+
+<UL>
+<LI><A HREF="config-notes.html#fkey">Alpine in Function Key Mode</A><BR>
+<LI><A HREF="config-notes.html#domain">Domain Settings</A><BR>
+<LI><A HREF="config-notes.html#collections">Syntax for Collections</A><BR>
+<LI><A HREF="config-notes.html#remote-folders">Syntax for Folder Names</A><BR>
+<LI><A HREF="config-notes.html#server-name-syntax">Server Name Syntax</A><BR>
+<LI><A HREF="config-notes.html#folder-namespaces">Folder Namespaces</A><BR>
+<LI><A HREF="config-notes.html#maildrop">What is a Mail Drop?</A><BR>
+<LI><A HREF="config-notes.html#sorting">Sorting a Folder</A><BR>
+<LI><A HREF="config-notes.html#alt-ed">Alternate Editor</A><BR>
+<LI><A HREF="config-notes.html#signature">Signatures and Signature Placement</A><BR>
+<LI><A HREF="config-notes.html#feature-list">Feature List Variable</A><BR>
+<LI><A HREF="config-notes.html#config-inheritance">Configuration Inheritance</A><BR>
+<LI><A HREF="config-notes.html#env-variables">Using Environment Variables</A><BR>
+<LI><A HREF="config-notes.html#smtp-server">SMTP Servers</A><BR>
+<LI><A HREF="config-notes.html#mime.types">MIME.Types file</A><BR>
+<LI><A HREF="config-notes.html#color-config-notes">Color Details</A><BR>
+<LI><A HREF="config-notes.html#smime-general">S/MIME Overview</A><BR>
+<LI><A HREF="config-notes.html#pc-notes">Additional Notes on PC-Alpine</A><BR>
+</UL>
+
+</UL>
+
+<H3><A HREF="low-level.html">Behind the Scenes</A></H3>
+
+<UL>
+<LI><A HREF="low-level.html#addrbook">Address Books</A><BR>
+<LI><A HREF="low-level.html#remote-config">Remote Configuration</A><BR>
+<LI><A HREF="low-level.html#checkpoint">Checkpointing</A><BR>
+<LI><A HREF="low-level.html#debug">Debug Files</A><BR>
+<LI><A HREF="low-level.html#INBOX">INBOX and Special Folders</A><BR>
+<LI><A HREF="low-level.html#help">Internal Help Files</A><BR>
+<LI><A HREF="low-level.html#char-set">International Character Sets</A><BR>
+<LI><A HREF="low-level.html#interrupt">Interrupted and Postponed Messages</A><BR>
+<LI><A HREF="low-level.html#status">Message Status</A><BR>
+<LI><A HREF="low-level.html#MIME-read">MIME: Reading a Message</A><BR>
+<LI><A HREF="low-level.html#MIME-send">MIME: Sending a Message</A><BR>
+<LI><A HREF="low-level.html#new-mail">New Mail Notification</A><BR>
+<LI><A HREF="low-level.html#NFS">NFS</A><BR>
+<LI><A HREF="low-level.html#print">Printers and Printing</A><BR>
+<LI><A HREF="low-level.html#save">Save and Export</A><BR>
+<LI><A HREF="low-level.html#sent-mail">Sent Mail</A><BR>
+<LI><A HREF="low-level.html#spell">Spell Checker</A><BR>
+<LI><A HREF="low-level.html#terminal">Terminal Emulation and Key Mapping</A><BR>
+</UL>
+
+</BODY>
+</HTML>
diff --git a/doc/tech-notes/installation.html b/doc/tech-notes/installation.html
new file mode 100644
index 00000000..d36214ae
--- /dev/null
+++ b/doc/tech-notes/installation.html
@@ -0,0 +1,577 @@
+<HTML><HEAD><TITLE>Alpine Technical Notes: Building and Installation</TITLE></HEAD><BODY>
+<H1>Building and Installation</H1>
+
+<H2><A NAME="compile">Compile-time Options</A></H2>
+
+<EM>Alpine</EM>'s UNIX build environment
+is based on Autotools (the GNU Build System).
+Once you've unpacked the source distribution find the file
+<CODE>configure</CODE> in the top-level directory.
+You may look at the many options available by typing
+<P>
+<CENTER><SAMP>
+ ./configure --help
+</SAMP></CENTER>
+
+<P>
+or you could just try building with the command
+<P>
+<CENTER><SAMP>
+ ./configure
+</SAMP></CENTER>
+
+<P>
+
+followed by
+
+<P>
+<CENTER><SAMP>
+ make
+</SAMP></CENTER>
+
+<P>
+
+Note, while the UW IMAP Toolkit (whose c-client
+library <EM>Alpine</EM> uses for mailbox access) build is not based on
+Autotools, <EM>Alpine</EM>'s configure script should set an
+appropriate make target and compilation options for most systems.
+
+<P>
+
+Some of the following can only be set when you build. Others,
+however, can be overridden by command-line flags to <EM>Alpine</EM> or settings in
+<EM>Alpine</EM>'s user or system configuration files.
+Some of the options which can be set when building:
+
+<H3><A NAME="ldap-compile">Including LDAP Functionality</A></H3>
+
+By default, the configure script will attempt to find the LDAP library
+support for you.
+If you are having trouble with LDAP take a look at the configure options
+<DL COMPACT>
+<DT> --with-ldap-dir=DIR
+<DD> Specify the root of the LDAP lib/include path.
+<DT> --with-ldap-include-dir=DIR
+<DD> Specify the LDAP include path.
+<DT> --with-ldap-lib-dir=DIR
+<DD> Specify the LDAP library path.
+<DT> --without-ldap
+<DD> Disable LDAP support.
+</DL> <P>
+
+<EM>Alpine</EM> uses LDAPv3 protocol.
+When using the LDAPv3 protocol, the results are assumed to be in the
+UTF-8 character set, which <EM>Alpine</EM> handles well.
+If the LDAP server returns non-ascii data which is not encoded as UTF-8
+you will probably run into problems.
+<P>
+
+<H3><A NAME="krb5-compile">Including Kerberos 5 Functionality</A></H3>
+
+This works analogously to the LDAP build.
+By default, the configure script will attempt to find the Kerberos library
+support for you.
+If you are having trouble with Kerberos take a look at the configure options
+<DL COMPACT>
+<DT> --with-krb5-dir=DIR
+<DD> Specify the root of the Kerberos lib/include path.
+<DT> --with-krb5-include-dir=DIR
+<DD> Specify the Kerberos include path.
+<DT> --with-krb5-lib-dir=DIR
+<DD> Specify the Kerberos library path.
+<DT> --without-krb5
+<DD> Disable Kerberos support.
+</DL> <P>
+
+<H3><A NAME="pine-compile">Other Alpine Compile-time Options</A></H3>
+
+<DL COMPACT>
+
+<DT> --disable-nls
+<DD> Do not use Native Language Support. NLS refers to the use of GNU
+gettext utilities to localize a program, in the sense that English
+is translated to some other language.
+At the time this was written the low-level support for NSL is included
+in <EM>Alpine</EM> but no translations have been done.
+If there is no translation available, that means that disabling NLS
+will make no difference. If you have trouble building which is due
+to gettext or libintl you could try this option, or one of the following.
+
+<DT> --with-libintl-prefix[=DIR]
+<DT> --without-libintl-prefix
+
+<DT> --with-ssl-dir=DIR
+<DD> Specify the root of the SSL lib/include path (OpenSSL).
+<DT> --with-ssl-include-dir=DIR
+<DD> Specify the SSL include path.
+<DT> --with-ssl-lib-dir=DIR
+<DD> Specify the SSL library path.
+<DT> --with-ssl-certs-dir=DIR
+<DD> Specify the path to the SSL certificates directory.
+<DT> --without-ssl
+<DD> Disable SSL support.
+
+<DT> --without-pthread
+<DD> Do not test for nor build with POSIX thread support, which is used
+only for the Busy-Cue in the status line at this time.
+
+<DT> --without-smime
+<DD> Disable S/MIME support.
+
+<DT> --disable-debug
+<DD> Never create debug files.
+
+<DT> --with-smtp-msa=PATH
+<DD> Local Mail Submission Agent (sendmail, by default).
+<DT> --with-smtp-msa-flags=FLAGS
+<DD> MSA flags for SMTP on stdin/stdout (-bs -odb -oem).
+
+</DL> <P>
+
+There are many more options which you can see using the
+<P>
+<CENTER><SAMP>
+ ./configure --help
+</SAMP></CENTER>
+
+<P>
+command.
+<P>
+
+<H3><A NAME="imapd-compile">IMAPd Compile-time Options</A></H3>
+
+There are no options or settings required for the version of <EM>IMAPd</EM>
+distributed with <EM>Alpine</EM>. If you need to be doing more complex modifications
+to IMAP, then you should pick up the IMAP development package and work
+with that code. The developer's version of IMAP is available for
+anonymous ftp from <A
+HREF="ftp://ftp.cac.washington.edu"><CODE>ftp.cac.washington.edu</CODE></A>
+in the directory <CODE>mail</CODE>. The file is called <A
+HREF="ftp://ftp.cac.washington.edu/mail/imap.tar.Z"><CODE>imap.tar.Z</CODE></A>.
+Unless it has changed since <EM>Alpine</EM> was released, the directory
+<CODE>imap</CODE> in the <EM>Alpine</EM> distribution is the IMAP
+development package.
+<P>
+The c-client library has not been converted to use the
+GNU Build System's autotools.
+The <EM>Alpine</EM> configure script will try to correctly guess
+the arguments needed for the c-client make command and will build
+the library, but if you need to change anything you should take a
+look at <CODE>imap/docs/BUILD</CODE> for more detailed instructions.
+<P>
+
+<HR>
+
+<H2><A NAME="build">Building the Alpine Programs</A></H2>
+
+You may have already compiled <EM>Alpine</EM> and tried it out. If so, great! If
+not, you should be able to do it without too much trouble by following
+these step-by-step instructions: <P>
+
+<OL>
+
+<LI> Make sure you're in the root of the <EM>Alpine</EM> source. When you type
+<CODE>ls</CODE> you should see the following files and directories (or
+something close to it):
+
+<PRE>
+aclocal.m4 config.sub imap Makefile.am packages web
+alpine configure include Makefile.in pico
+build.bat configure.ac install-sh mapi pith
+build.cmd contrib LICENSE missing po
+config.guess depcomp ltmain.sh mkinstalldirs README
+config.rpath doc m4 NOTICE VERSION
+</PRE>
+<P>
+
+<LI> Give the command <CODE>./configure</CODE>
+Configure should grind away for a few minutes. <P>
+
+<LI> When configure is complete, give the command <CODE>make</CODE>.
+If make stops and asks
+
+<P>
+<CENTER><SAMP>
+ Do you want to build with IPv6 anyway? Type y or n please:
+</SAMP></CENTER>
+
+<P>
+you should answer with a 'y'.
+The compiler should grind away for a few minutes. The <EM>Alpine</EM>
+binary will end up in <CODE>.../alpine/alpine</CODE> and the
+Pico and Pilot binaries in <CODE>.../pico/pico</CODE>
+and <CODE>.../pico/pilot</CODE>. Other binaries you may be interested
+in are <CODE>.../alpine/rpdump</CODE> and <CODE>.../alpine/rpload</CODE>
+and c-client binaries in the directories <CODE>.../imap/imapd</CODE>,
+<CODE>.../imap/ipopd</CODE>, <CODE>.../imap/mailutil</CODE>, and so on.
+<P>
+
+<LI> If you need to try again, make sure you're getting a clean start by giving the command
+<CODE>make clean</CODE>. <P>
+
+</OL>
+<P>
+
+<HR>
+
+<H2><A NAME="install-unix">Installing Alpine and Pico on UNIX Platforms</A></H2>
+
+Installing <EM>Alpine</EM> and <EM>Pico</EM> is simple. You take the program files
+which you have just transferred or built and you move them to the correct
+directory on your system. Most often the binaries go in
+<CODE>/usr/local/bin</CODE> though sometimes they are placed in
+<CODE>/usr/bin</CODE>. All the help text is compiled into <EM>Alpine</EM> so there
+are no <STRONG>required</STRONG> auxiliary files. Instead of copying the
+binaries manually, you may use <CODE>make install</CODE> to install
+them. <P>
+
+There are three optional auxiliary files:
+<CODE>/usr/local/lib/pine.info</CODE>,
+<CODE>/usr/local/lib/pine.conf</CODE>, and
+<CODE>/usr/local/lib/pine.conf.fixed</CODE>. The file
+<CODE>pine.info</CODE> contains text on how to get further help on the
+local system. It is part of the help text for the
+main menu and should probably refer to the local help desk or the system
+administrator. If this file doesn't exist a generic version which
+suggests ``talking to the computer support staff at your site'' is shown.
+The file <CODE>pine.conf</CODE> is used to set system-wide default
+configurations for <EM>Alpine</EM>. The file <CODE>pine.conf.fixed</CODE> is also
+used to set system-wide default configurations for <EM>Alpine</EM>.
+The difference
+between these two files is that configuration variables set in the
+<CODE>pine.conf.fixed</CODE> file may not normally be over-ridden by a
+user. See the section on <A HREF="config.html"><EM>Alpine</EM> Configuration</A>
+later in this document for details about
+the <CODE>pine.conf</CODE> and <CODE>pine.conf.fixed</CODE> files. <P>
+
+<HR>
+
+<H2><A NAME="install-pc">Installing PC-Alpine</A></H2>
+
+<P>
+The PC-Alpine distribution comes as a .zip file. To install, unzip the
+files to a directory where you would like the program to reside. Modern
+Windows versions come with the capability of unzipping .zip files. Failing
+that, you can use one of the many .zip file extractors out there.
+Following current Windows conventions, a common directory into which the files
+could be extracted would be <CODE>C:\Program Files\PC-Alpine\</CODE>.
+
+<P>
+Having extracted PC-Alpine's .zip file to the directory of choice, you can
+now run that directory's alpine.exe, which is the actual PC-Alpine program.
+For convenience, you could place shortcuts to it on the task bar, start
+menu, etc.
+
+<P>
+Upon first running PC-Alpine, you may be asked where you would like to
+access your Configuration file (called the <I>pinerc</I>). This
+is useful in accessing already existing configuration files, and it
+does not matter where this file gets created. If you are connecting
+to an IMAP server to access your email, it is also possible to store
+this Configuration data on that server, which facilitates accessing
+the same configuration from multiple machines (in fact, your configuration
+may have already been set up this way for use with other <EM>Alpine</EM> programs).
+
+<P>
+After having established the location of the configuration file, it may be
+necessary to specify a few configuration settings before reading or sending
+mail. You may be prompted for the following (which may also be edited from
+the (S)etup (C)onfig screen from the Main Menu):
+
+<LI><CODE>Folder to open as inbox</CODE> (or <I>inbox-path</I>) - This can
+be an inbox residing on an IMAP or POP3 server, or one residing locally.
+An example of an INBOX for an IMAP server is:
+<CODE>{server.example.com}INBOX</CODE>.
+
+<LI><CODE>User-id</CODE>, <CODE>Personal name</CODE>, and
+<CODE>host/domain</CODE>, which are to be used as your email address.
+
+<LI><CODE>SMTP server to forward message</CODE> - You must enter your SMTP
+server before you can send any messages.
+
+<P>
+At this point, you will be able to read and send email messages. There are,
+however, many more preferences that you can set in the Configuration
+screen.
+<HR>
+
+<H2><A NAME="install-imapd">Installing IMAPd</A></H2>
+
+When the <EM>Alpine</EM> distribution is built on a UNIX system, the IMAP server
+binary, <CODE>imapd</CODE>, is compiled. Installing <CODE>imapd</CODE>
+requires placing the binary in the appropriate directory, usually
+<CODE>/usr/etc</CODE>, and adding entries to <CODE>/etc/services</CODE>
+and <CODE>/etc/inetd.conf</CODE> or their counterparts.
+<P>
+Instead of including installation instructions here we'll just include
+a pointer to detailed instructions in the c-client distribution.
+Please take a look at the file
+<CODE>imap/docs/BUILD</CODE> in the source tree.
+
+<HR>
+
+<H2><A NAME="files-unix">Support Files and Environment Variables: UNIX Alpine</A></H2>
+
+This section lists the various files which <EM>Alpine</EM> uses which are not email
+folders. All of these are the default names of files, they may vary based
+on <EM>Alpine</EM>'s configuration.
+
+<DL COMPACT>
+
+<DT> /usr/local/lib/pine.conf
+
+<DD> Pine's global configuration file.
+
+<DT> /usr/local/lib/pine.conf.fixed
+
+<DD> Non-overridable global configuration file.
+
+<DT> /usr/local/lib/pine.info
+
+<DD> Local pointer to system administrator.
+
+<DT> ~/.pinerc
+
+<DD> Personal configuration file for each user.
+
+<DT> ~/.pinercex
+
+<DD> Personal exceptions configuration file for each user.
+
+<DT> ~/.addressbook
+
+<DD> Personal addressbook
+
+<DT> ~/.newsrc
+
+<DD> Personal USENET subscription list. This is shared with other
+newsreading programs.
+
+<DT> ~/.pine-debugX
+
+<DD> The files created for debugging <EM>Alpine</EM> problems. By default, there are
+4 .pine-debug files kept at any time.
+
+<DT> ~/.signature
+
+<DD> A signature file which will be included in all outgoing email
+messages.
+
+<DT> ~/.pine-interrupted-mail
+
+<DD> The text of a message which was interrupted by some unexpected error
+which <EM>Alpine</EM> detected.
+
+<DT> ~/mail/postponed-msgs
+
+<DD> A folder of messages which the user chose to postpone.
+
+<DT> /etc/mailcap
+
+<DD> System-wide mail capabilities file. Only used if
+<CODE>$MAILCAPS</CODE> not set.
+
+<DT> ~/.mailcap
+
+<DD> Personal mail capabilities file. Combines with system-wide mailcap.
+Only used if <CODE>$MAILCAPS</CODE> not set.
+
+</DL> <P>
+
+The location of the following support files may be controlled by variables
+in the personal or global <EM>Alpine</EM> configuration file: signature, addressbook
+and its index file, postponed messages, and newsrc. <P>
+
+Unix <EM>Alpine</EM> uses the following environment variables:
+
+<DL COMPACT>
+
+<DT> TERM
+
+<DD> Tells <EM>Alpine</EM> what kind of terminal is being used.
+
+<DT> DISPLAY
+
+<DD> Determines if <EM>Alpine</EM> will try to display IMAGE attachments.
+
+<DT> TMPDIR, TMP, or TEMP
+
+<DD> Specifies location of temporary storage area, first one set wins
+
+<DT> SHELL
+
+<DD> If not set, default is /bin/sh
+
+<DT> MAILCAPS
+
+<DD> A semicolon delimited list of path names to mailcap files. <P>
+
+</DL>
+
+<HR>
+
+<H2><A NAME="files-pc">Support Files, Environment Variables, and Registry Settings: PC-Alpine</A></H2>
+
+This section lists the various files which <EM>PC-Alpine</EM> uses which are not
+normal mail folders. All of these are the default names of files, they
+may vary based on <EM>Alpine</EM>'s configuration. <P>
+
+<DL COMPACT>
+
+<DT> $PINERC or &lt;PineRC registry value&gt; or $HOME\PINE\PINERC or
+&lt;PINE.EXE dir&gt;\PINERC
+
+<DD> Path to (required) personal configuration file.
+
+<DT> $PINERCEX or $HOME\PINE\PINERCEX or &lt;PINE.EXE dir&gt;\PINERCEX
+
+<DD> Path to personal exceptions configuration file.
+
+<DT> $PINECONF
+
+<DD> Path of optional global configuration file.
+
+<DT> &lt;PINERC directory&gt;\ADDRBOOK
+
+<DD> Personal addressbook
+
+<DT> &lt;PINERC directory&gt;\PINEDEBG.TXT
+
+<DD> Location of <EM>Alpine</EM> debug file.
+
+<DT> &lt;PINERC directory&gt;\MAILCAP and/or &lt;PINE.EXE dir&gt;\MAILCAP
+
+<DD> These paths are only used if $MAILCAPS not set.
+
+<DT> $HOME\NEWSRC or &lt;PINERC directory&gt;\NEWSRC
+
+<DD> Personal USENET subscription list. This may be shared with other
+newsreading programs.
+
+<DT> $HOME\MAIL\INTRUPTD
+
+<DD> The text of a message which was interrupted by some unexpected error
+which <EM>Alpine</EM> detected.
+
+<DT> $HOME\MAIL\POSTPOND
+
+<DD> A folder of messages which the user chose to postpone.
+
+</DL> <P>
+Registry Values:
+<DL COMPACT>
+
+<DT> HKEY_LOCAL_MACHINE\Software\University of Washington\Alpine\1.0
+
+<DD> <EM>Pinedir</EM>: The directory that contains the <EM>Alpine</EM> executable.
+
+<DD> <EM>PineEXE</EM>: The name of the <EM>Alpine</EM> executable (most commonly
+"alpine.exe").
+
+<DT> HKEY_CURRENT_USER\Software\University of Washington\Alpine\1.0
+
+<DD> <EM>PineRC</EM>: The path that points to the default pinerc to use.
+
+<DT> HKEY_LOCAL_MACHINE\Software\Clients\Mail\Alpine
+
+<DD> <EM>DLLPath</EM>: The path that points to <EM>Alpine</EM>'s pmapi32.dll.
+
+<DT> HKLM\Software\Clients\Mail\Alpine\shell\open\command
+
+<DD> <EM>(Default)</EM>: When set as the default mailer, this is the
+command that is run by external programs.
+
+<DT> HKLM\Software\Clients\Mail\Alpine\Protocols\Mailto\DefaultIcon
+
+<DD> <EM>(Default)</EM>: This points to the icon to display in relation to
+<EM>Alpine</EM>'s mailto URL rendering.
+
+<DT> HKLM\Software\Clients\Mail\Alpine\Protocols\Mailto\shell\open\command
+
+<DD> <EM>(Default)</EM>: This value is the command that gets run by external
+programs when a mailto URL is run with <EM>PC-Alpine</EM> set as the
+default mailer.
+
+<DT> HKLM\Software\Clients\News\Alpine\shell\open\command
+
+<DD> <EM>(Default)</EM>: When set as the default newsreader, this is the
+ command that is run by external programs.
+
+<DT> HKLM\Software\Clients\News\Alpine\Protocols\news\DefaultIcon
+
+<DD> <EM>(Default)</EM>: This points to the icon to display in relation to
+ <EM>Alpine</EM>'s news URL rendering.
+
+<DT> HKLM\Software\Clients\News\Alpine\Protocols\news\shell\open\command
+
+<DD> <EM>(Default)</EM>: This value is the command that gets run by external
+programs when a news URL is run with <EM>Alpine</EM> set as the
+default newsreader.
+
+<DT> HKLM\Software\Clients\News\Alpine\Protocols\nntp\DefaultIcon
+
+<DD> <EM>(Default)</EM>: This points to the icon to display in relation to
+<EM>Alpine</EM>'s nntp URL rendering.
+
+<DT> HKLM\Software\Clients\News\Alpine\Protocols\nntp\shell\open\command
+
+<DD> <EM>(Default)</EM>: This value is the command that gets run by external
+programs when a nntp URL is run with <EM>Alpine</EM> set as the
+default newsreader.
+
+</DL> <P>
+<EM>Alpine</EM>'s personal configuration file
+may be in the same directory as the executable, or if that is inconvenient
+because the executable is on a shared or read-only drive, then it can be
+in a file named by the <CODE>$PINERC</CODE> environment variable, or in
+<CODE>$HOME\ALPINE\PINERC</CODE>, where if not set, <CODE>$HOME</CODE>
+defaults to the root of the current working drive. <P>
+
+Most of the other support files key off of the location of the
+<CODE>PINERC</CODE> file. However, in the case of the NEWSRC file, the
+path <CODE>$HOME\NEWSRC</CODE> is checked first. Also, the postponed
+messages and interrupted message folders are placed in the default folder
+collection, normally in the directory <CODE>$HOME\MAIL</CODE>. <P>
+
+The location of the following support files may be controlled by variables
+in the personal or global <EM>Alpine</EM> configuration file: signature, addressbook
+(and its index file), postponed messages, and newsrc. <P>
+
+<EM>PC-Alpine</EM> uses the following environment variables:
+
+<DL COMPACT>
+
+<DT> PINERC
+
+<DD> Overrides default path to pinerc file.
+
+<DT> PINERCEX
+
+<DD> Overrides default path to personal exceptions configuration file.
+
+<DT> PINECONF
+
+<DD> Optional path to global <EM>Alpine</EM> config file.
+
+<DT> HOME
+
+<DD> If not set, <EM>Alpine</EM> uses the root of the current drive, e.g. C:
+
+<DT> TMPDIR, TMP, or TEMP
+
+<DD> Specifies location of temporary storage area, first one set wins
+
+<DT> COMSPEC
+
+<DD> Specifies shell for external commands.
+
+<DT> MAILCAPS
+
+<DD> A semicolon delimited list of path names to mailcap files.
+
+</DL> <P>
+
+<!-- pnuts -->
+
+</BODY>
+</HTML>
diff --git a/doc/tech-notes/introduction.html b/doc/tech-notes/introduction.html
new file mode 100644
index 00000000..d7293842
--- /dev/null
+++ b/doc/tech-notes/introduction.html
@@ -0,0 +1,122 @@
+<HTML><HEAD><TITLE>Alpine Technical Notes: Introduction</TITLE></HEAD><BODY>
+<H1>Introduction</H1>
+
+<H2><A NAME="design">Design Goals</A></H2>
+
+Throughout <EM>Alpine</EM> development, we have had to strike a balance
+between the
+need to include features which advanced users require and the need to keep
+things simple for beginning users. To strike this balance, we have tried
+to adhere to these design principles: <P>
+
+<DL COMPACT>
+
+<DT>
+
+<DD> - The model presented to the user has to be simple and
+clear. Underlying system operation is hidden as much as possible.
+
+<DT>
+
+<DD> - It's better to have a few easily understood commands that can be
+repeated than to have some more sophisticated command that will do the job
+all at once.
+
+<DT>
+
+<DD> - Whenever the user has to select a command, file name, address,
+etc., the user should be given (or can get) a menu from which to make the
+selection. Menus need to be complete, small, organized and well thought
+out.
+
+<DT>
+
+<DD> - <EM>Alpine</EM> must provide immediate feedback for the user with each
+operation.
+
+<DT>
+
+<DD> - <EM>Alpine</EM> must be very tolerant of user errors. Any time a user is
+about to perform an irreversible act (send a message, expunge messages
+from a folder), <EM>Alpine</EM> should ask for confirmation.
+
+<DT>
+
+<DD> - Users should be able to learn by exploration without fear of doing
+anything wrong. This is an important feature so the user can get started
+quickly without reading any manuals and so fewer manuals are required.
+
+<DT>
+
+<DD> - The core set of <EM>Alpine</EM> functions should be kept to a
+minimum so new
+users don't feel "lost" in seemingly extraneous commands and concepts.
+<P>
+
+</DL> <P>
+
+Just as there were goals relating to the look and feel of <EM>Alpine</EM>,
+there were
+equally important goals having to do with <EM>Alpine</EM>'s structure-the
+things that
+users never see but still rely on every time they use <EM>Alpine</EM>.
+While <EM>Alpine</EM>
+can be used as a stand-alone mail user agent, one of its strongest assets
+is its use of the Internet Message Access Protocol (IMAP) for accessing
+remote email folders. In addition, <EM>Pine</EM> (the predecessor of <EM>Alpine</EM>) was one of
+the first programs to
+support the Multipurpose Internet Mail Extensions (MIME) specification.
+With MIME, <EM>Alpine</EM> users can reliably send any binary file to any other
+person on the Internet who uses a MIME compliant email program. <P>
+
+The decision to use IMAP and MIME reflects the importance of
+interoperability, standardization and robustness in <EM>Alpine</EM>. As you work
+with <EM>Alpine</EM> more, you will see other features which reflect
+the same values.
+For example, <EM>Alpine</EM> enforces strict compliance with RFC 2822, implements a
+strong mail folder locking mechanism and verifies a process before
+overwriting any files (e.g. addressbook, expunging messages). <P>
+
+<H2><A NAME="components">Alpine Components</A></H2>
+
+If you have picked up the <EM>Alpine</EM> distribution, then you already know that
+<EM>Alpine</EM> comes in a few different pieces. They are: <P>
+
+<DL COMPACT>
+
+<DT> <EM>Alpine</EM>
+
+<DD> The main code from which the <EM>Alpine</EM> program is compiled. <P>
+
+<DT> <EM>Pico</EM>
+
+<DD> <EM>Pico</EM> is the name for the <EM>Alpine</EM> composer.
+The <EM>Pico</EM> code is used in two
+ways: (1) it is compiled on its own to be a stand-alone editor and, (2)
+it is compiled as a library for <EM>Alpine</EM> to support composition
+of messages within <EM>Alpine</EM>.
+<EM>Pico</EM> is <EM>Alpine</EM>'s internal editor invoked when users
+need to fill in header lines or type the text of an email message. <P>
+
+<DT> <EM>Imap</EM>
+
+<DD> An API for IMAP. Includes the C-Client library, which is compiled
+into <EM>Alpine</EM>, and the IMAP server <EM>IMAPd</EM>.
+C-Client implements the IMAP
+protocol and also negotiates all access between <EM>Alpine</EM> and
+the mail folders
+it operates on, even if the folders are local.
+The C-Client routines are used for email folder parsing
+and interpreting MIME messages. <EM>IMAPd</EM> is a separate server that handles
+IMAP connections from any IMAP-compliant email program. When <EM>Alpine</EM>
+accesses a remote mailbox, the <EM>Alpine</EM> program is the IMAP client and the
+<EM>IMAPd</EM> program is the IMAP server. Of course, <EM>Alpine</EM> can
+use any IMAP-compliant IMAP server, not just <EM>IMAPd</EM>. <P>
+
+</DL>
+
+<!-- pnuts -->
+
+</BODY>
+</HTML>
+
diff --git a/doc/tech-notes/low-level.html b/doc/tech-notes/low-level.html
new file mode 100644
index 00000000..ee5264ac
--- /dev/null
+++ b/doc/tech-notes/low-level.html
@@ -0,0 +1,975 @@
+<HTML><HEAD><TITLE>Alpine Technical Notes: Behind the Scenes</TITLE></HEAD><BODY>
+<H1>Behind the Scenes</H1>
+
+Many people ask how certain <EM>Alpine</EM> features are implemented. This section
+outlines some of the details.
+
+<H2><A NAME="addrbook">Address Books</A></H2>
+
+There are two types of address book storage. There
+are <EM>local</EM> address books, which are the address books that are
+stored in a local file; and there
+are <EM>remote</EM> address books, which are stored on an IMAP server.
+
+<H4><A NAME="remote-abooks">Information About Remote Address Books</A></H4>
+
+<BLOCKQUOTE>
+NOTE: The remote address book capability does not allow you to access an
+existing local address book from a remote system! That is, you can't set
+the remote address book to something like <CODE>{remote.host}.addressbook</CODE>
+and expect to access the existing <CODE>.addressbook</CODE> <EM>file</EM>
+on remote.host. Instead, you need to create a new remote address book in
+a new, previously unused remote mail <EM>folder</EM>. Then you can use the
+<EM>Select</EM> and <EM>Apply Save</EM> commands in the address book screen to
+<EM>Save</EM> all of the entries from an existing local address book to
+the new remote address book.
+</BLOCKQUOTE>
+<P>
+
+A remote address book is stored in a mail folder on an
+IMAP server.
+An <EM>Alpine</EM> remote address book is just like an <EM>Alpine</EM> local address book
+in that it is not interoperable with other email clients.
+The folder is a regular folder containing mail messages but
+those messages are special. The first message must be an alpine remote
+address book header message which contains the header <EM>x-pine-addrbook</EM>.
+The last message in the folder contains the address book data.
+In between the first
+and the last message are old versions of the address book data. The address
+book data is simply stored in the message as it would be on disk, with no MIME
+encoding. When it is used the data from the last message in the folder is
+copied to a local file and then that file is used exactly like a local
+address book file is used. When a change is made the modified local file
+is appended to the remote folder in a new message. In other words, the
+local file is just a cache copy of the data in the remote folder. Each client
+which uses the remote address book will have its own cache copy of the data.
+Whenever a copy is done the entire address book is copied, not just the
+entries which have changed.
+<P>
+
+<EM>Alpine</EM> can tell that the remote data has changed
+by one of several methods.
+If the date contained in the Date header of the last message has changed then
+it knows it has changed. If the UID of the last message has changed, or the
+number of messages in the folder has changed, it knows that it has changed.
+When <EM>Alpine</EM> discovers the folder has changed it gets
+a new copy and puts it in the local cache file.
+<P>
+
+There is a configuration file variable for remote address books called
+<A HREF="config.html#remote-abook-metafile"><EM>remote-abook-metafile</EM></A>.
+The variable is the name of a file in which
+information about remote address books is stored. There is one line in the
+metafile for each remote address book. The information stored there is the
+name of the cache file and information to help figure out when the remote
+folder was last changed. If the metafile or any of the cache files is deleted
+then <EM>Alpine</EM> will rebuild them the next time it runs.
+<P>
+
+Remote address books have names that look just like regular
+<A HREF="config-notes.html#remote-folders">remote mail folder</A> names.
+For example:
+
+<BLOCKQUOTE>
+ {host.domain}foldername<BR>
+</BLOCKQUOTE>
+
+<EM>Alpine</EM> decides whether or not an address book is remote simply by
+looking at the first character of the address book name and comparing
+it to '{'.
+
+<H4>Information About All Address Books</H4>
+
+The address book is named, by default, <CODE>.addressbook</CODE> in the
+user's Unix home directory, or in the case of <EM>PC-Alpine</EM>,
+<CODE>ADDRBOOK</CODE>, in the same directory as the <CODE>PINERC</CODE> file.
+There may be more than
+one address book, and the default name can be overridden via an entry in
+any of the <EM>Alpine</EM> configuration files. The two configuration variables
+<A HREF="config.html#pers-abook"><EM>address-book</EM></A>
+and <A HREF="config.html#glob-abook"><EM>global-address-book</EM></A>
+are used to specify the names of the address books.
+Each of these variables is a list
+variable. The total set of address books for a user is the combination of
+all the address books specified in these two lists. Each entry in the
+list is an optional nickname followed by an address book name. The nickname is
+everything up to the last space before the file name. The
+<EM>global-address-book</EM> list will typically be configured in the
+system-wide configuration file, though a user may override it like most
+other variables. Address books which are listed in the
+<EM>global-address-book</EM> variable are forced read-only, and are
+typically shared among multiple users. <P>
+
+Local address books (or local cache files for remote address books) are
+simple text files with lines in the format:
+
+<BLOCKQUOTE>
+ &lt;nickname&gt;TAB&lt;fullname&gt;TAB&lt;address&gt;TAB&lt;fcc&gt;TAB&lt;comments&gt;<BR>
+</BLOCKQUOTE>
+
+The last two fields are optional. A "line" may be made up of multiple
+actual lines in the file by using continuation lines, which are lines
+beginning with SPACE characters. The line breaks may be after TABs or in
+between addresses in a distribution list.
+Each <EM>actual</EM> line in the file must
+be less than 1000 characters in length.
+<P>
+
+Nicknames (the first field) are short names that the user types instead of
+typing in the full address. There are several characters which aren't
+allowed in nicknames in order to avoid ambiguity when parsing the address
+(SPACE, COMMA, @, ", ;, :, (, ), [, ], <, >, \).
+Nicknames aren't required. In fact,
+none of the fields is required. <P>
+
+The <EM>fullname</EM> field is usually stored as Last_name, First_name, in order
+that a sort on the fullname field comes out sorted by Last_name.
+If there is an unquoted comma in the fullname,
+<EM>Alpine</EM> will flip the first and last name around and get rid of
+the comma when using the entry in a composition. It isn't required that
+there be a comma, that's only useful if the user wants the entries to sort
+on last names. <P>
+
+The <EM>address</EM> field takes one
+of two forms, depending on whether the entry
+is a single (simple) address or a distribution list. For a simple entry,
+the address field is an RFC 2822 address. This could be either the
+email-address part of the address, i.e., the part that goes inside the
+brackets (&lt;&gt;), or it could be a full RFC 2822 address. The phrase part
+of the address (the fullname) is used unless there is a fullname present
+in the fullname field of the address book entry. In that case, the fullname
+of the address book entry replaces the fullname of the address. For a
+distribution list, the &lt;address&gt; is in the format:
+
+<BLOCKQUOTE>
+ "(" &lt;address&gt;, &lt;address&gt;, &lt;address&gt;, ... ")"<BR>
+</BLOCKQUOTE>
+<P>
+
+The only purpose for the parentheses around the list of addresses is to
+make it easier for the parsing routines to tell that it is a simple entry
+instead of a list. The two are displayed differently and treated slightly
+differently in some cases, though most of the distinction has disappeared.
+Each of the addresses in a list can be a full RFC 2822 address with
+fullname included, or it may be just the simple email-address part of the
+address. This allows the user to have a list which includes the fullnames
+of all the list members. In both the simple and list cases,
+addresses may also be other nicknames which appear in this address book or
+in one of the other address books. (Those nicknames are searched for by
+looking through the address books in the order they appear in the address
+book screen, with the first match winning.) Lists may be nested. If
+addresses refer to each other in a loop (for example, list A includes
+list B which includes list A again) this is detected and flagged. In that
+case, the address will be changed to "**** address loop ****". <P>
+
+The optional <EM>fcc</EM> field is a folder name, just like the fcc field in the
+composer headers. If the first address in the To field of a composition
+comes from an address book entry with an fcc field, then that fcc is
+placed in the fcc header in the composer. <P>
+
+The <EM>comments</EM> field is just a free
+text field for storing comments about an
+entry. By default, neither the fcc nor the comments field is shown on the
+screen in the address book screen. You may make those fields visible by
+configuring the variable
+<A HREF="config.html#abook-formats"><EM>addressbook-formats</EM></A>.
+They are also searched when you use the <EM>WhereIs</EM>
+command in the address book screen and are visible when you
+<EM>View</EM> or <EM>Update</EM> an entry. <P>
+
+The address book is displayed in the order that it is stored.
+When the user chooses a different sorting criterion, the data is actually
+sorted and stored, as opposed to showing a sorted view of the data. <P>
+
+When the address book is written out, it is first written to a temporary
+file and if that write is successful it is renamed. This guards
+against errors writing the file that might destroy the whole address book.
+The address book is re-written after each change. If the address book
+is a remote address book, the file is then appended to the remote mail
+folder using IMAP. <P>
+
+The end-of-line character(s) in the address book file are those native to
+the system writing it. So it is &lt;LF&gt; on Unix and
+&lt;CR&gt;&lt;LF&gt; on PC's. However, both Unix and PC versions of <EM>Alpine</EM>
+can read either format, so it should be possible to share a read-only
+address book among the two populations (using NFS, for example).
+<P>
+
+<HR>
+
+<H3><A NAME="addrbook-lu">Address Book Lookup File</A></H3>
+
+<EM>Pine</EM> used an additional file for each address book, called
+the LookUp file. It had the same name as the address book file
+with the suffix ".lu" appended.
+<EM>Alpine</EM> no longer uses a lookup file.
+<P>
+
+<H4>Validity Checking of Address Books</H4>
+
+There is no file locking done on <EM>Alpine</EM> address books, however, there
+is considerable validity checking done to make sure that the address book
+hasn't changed unexpectedly. Whenever the address book is about to be changed,
+a check is made to see if the file is newer than when we read it or the
+remote address book folder has changed since we last copied it. If either
+of these is true, the change is aborted.
+<P>
+
+There is an automatic, behind-the-scene check that happens every so often,
+also. For example, if someone else changes one of the address books that
+you have configured, your <EM>Alpine</EM>'s copy of the address book will usually
+be updated automatically without you noticing. This checking happens at the
+same time as new mail checking takes place, unless you are actively using
+the address book, in which case it happens more frequently.
+<P>
+
+<HR>
+
+<H2><A NAME="remote-config">Remote Configuration</A></H2>
+
+Configuration information may be stored remotely.
+Remote configuration information is stored in a folder on an IMAP server.
+This should be a folder which is used only for storing the configuration
+information.
+In other words, it should be a folder which didn't exist before.
+<P>
+Remote configuration folders are very similar to remote address book folders.
+They both consist of a header message, which serves to identify the type
+of folder; the last message, which contains the data; and intermediate
+messages, which contain old versions of the data.
+The first message must contain the header <EM>x-pine-pinerc</EM>.
+<P>
+When a remote configuration is being used, the folder is checked to make
+sure it is a remote configuration folder, then the data contained in the
+last message is copied to a temporary file.
+That file is treated just like any regular local configuration file from
+that point on.
+Whenever a configuration change is made, the entire file is copied back
+to the IMAP server and is appended to the folder as a new message.
+<P>
+Because remote configuration folders are so similar to remote address books,
+the configuration variable
+<A HREF="config.html#remote-abook-metafile"><EM>remote-abook-metafile</EM></A>
+is used by both.
+<P>
+Remote configuration folders have names that look just like regular
+<A HREF="config-notes.html#remote-folders">remote mail folder</A> names.
+For example:
+
+<BLOCKQUOTE>
+ {host.domain}mypinerc<BR>
+</BLOCKQUOTE>
+
+<EM>Alpine</EM> decides whether or not a configuration file is remote simply by
+looking at the first character of the name and comparing
+it to '{'.
+<P>
+
+<HR>
+
+<H2><A NAME="checkpoint">Checkpointing</A></H2>
+
+Periodically <EM>Alpine</EM> will save the whole
+mail folder to disk to prevent loss
+of any mail or mail status in the case that it gets interrupted,
+disconnected, or crashes. The period of time <EM>Alpine</EM> waits to do the
+checkpoint is calculated to be minimally intrusive. The timing can be
+changed (but usually isn't) at compile time. Folder checkpointing happens
+for both local folders and those being accessed with IMAP. The delays are
+divided into three categories: <P>
+
+<BLOCKQUOTE>
+The exact algorithm given below is no longer correct.
+It has gotten more complicated over time.
+However, this gives the general idea <EM>Alpine</EM> uses
+when deciding whether or not to do a checkpoint.
+</BLOCKQUOTE>
+
+<DL COMPACT>
+
+<DT> Good Time:
+
+<DD> This occurs when <EM>Alpine</EM> has been idle for more than 30 seconds. In
+this case <EM>Alpine</EM> will checkpoint if 12 changes
+to the file have been made or
+at least one change has been made and a checkpoint hasn't been done for
+five minutes. <P>
+
+<DT> Bad Time:
+
+<DD> This occurs just after <EM>Alpine</EM> has
+executed some command. <EM>Alpine</EM> will
+checkpoint if there are 36 outstanding changes to the mail file or at
+least one change and no checkpoint for ten minutes. <P>
+
+<DT> Very Bad Time:
+
+<DD> Done when composing a message. In this case, <EM>Alpine</EM> will only
+checkpoint if at least 48 changes have been made or at least one change has been
+made in the last twenty minutes with no checkpoint. <P>
+
+</DL>
+
+<HR>
+
+<H2><A NAME="debug">Debug Files</A></H2>
+
+If UNIX <EM>Alpine</EM> is compiled with the compiler
+<EM>DEBUG</EM> option on (the default),
+then <EM>Alpine</EM> will produce debugging output to a file.
+This can be disabled at compile-time with the --disable-debug configure option,
+or at run-time with the command line flag -d0.
+The file is
+normally <CODE>.pine-debugX</CODE> in the user's home directory where
+<EM>X</EM> goes from 1 to 4. Number 1 is always the most recent session
+and 4 the oldest. Four are saved because often the user has gone in and
+out of <EM>Alpine</EM> a few times after a problem has occurred before the expert
+actually gets to look at it. The amount of output in the debug files
+varies with the debug level set when <EM>Alpine</EM> is
+compiled and/or as a command
+line flag. The default is level 2. This shows very general things and
+records errors. Level 9 produces copious amounts of output for each
+keystroke. <P>
+
+Similarly, <EM>PC-Alpine</EM> creates debug files
+named <CODE>pinedebg.txtX</CODE> in the
+same directory as the <CODE>PINERC</CODE> file. <P>
+
+<HR>
+
+<H2><A NAME="INBOX">INBOX and Special Folders</A></H2>
+
+The <EM>INBOX</EM> folder is treated specially. It is normally kept open
+constantly so that the arrival of new mail can be detected. The name
+<EM>INBOX</EM> refers to wherever new mail is retrieved on the system. If
+the <A HREF="config.html#inbox-path"><EM>inbox-path</EM></A> variable is set,
+then <EM>INBOX</EM> refers to
+that. IMAP servers understand the concept of <EM>INBOX</EM>, so
+specifying the folder <EM>{imap.u.example.edu}INBOX</EM> is meaningful.
+The case of the word <EM>INBOX</EM> is not important,
+but <EM>Alpine</EM> tends to display it in all capital letters. <P>
+
+The folders for <A HREF="config.html#def-fcc">sent mail</A>
+and <A HREF="config.html#def-save">saved messages</A> folders are
+also somewhat special.
+They are automatically created if they are absent and recreated
+if they are deleted. <P>
+
+<HR>
+
+<H2><A NAME="help">Internal Help Files</A></H2>
+
+The file <CODE>pine.hlp</CODE> in the <CODE>alpine</CODE> subdirectory of the
+distribution contains all the help text for <EM>Alpine</EM>.
+It is compiled right into the <EM>Alpine</EM> binary as strings.
+This is done to simplify installation and configuration.
+The <CODE>pine.hlp</CODE> file is in a
+special format that is documented at the beginning of the file.
+It is divided into sections, each with a name that winds up being referenced as
+a global variable. This file is processed during the build process and
+turned into a C file that is compiled into <EM>Alpine</EM>. <P>
+
+<HR>
+
+<H2><A NAME="char-set">International Character Sets</A></H2>
+
+<EM>Alpine</EM> uses Unicode characters internally and
+it is a goal for <EM>Alpine</EM> to handle email in many different languages.
+<EM>Alpine</EM> will properly display only left-to-right character sets
+in a fixed-width font. Specifically, <EM>Alpine</EM> assumes that a fixed-width
+font is in use, in the sense that
+characters are assumed to take up zero, one, or two character cell
+widths from left to right on the screen. This is true even in <EM>PC-Alpine</EM>.
+<P>
+
+<EM>Alpine</EM> recognizes some local character sets which are right-to-left
+(Arabic, Hebrew, and Thai) or not representable in a fixed-width font
+(Arabic) and properly converts texts in these character sets to/from
+Unicode; however, there are known display bugs with these character
+sets.
+<P>
+
+There are three possible configuration character settings and some
+environment variable settings which can affect how <EM>Alpine</EM>
+handles international characters.
+The first two of these are only available in UNIX <EM>Alpine</EM>.
+The three configuration options are
+<EM>display-character-set</EM>,
+<EM>keyboard-character-set</EM>, and
+<EM>posting-character-set</EM>.
+The <EM>keyboard-character-set</EM> defaults to being the same value
+as the <EM>display-character-set</EM>, and that is usually correct, because
+the keyboard almost always produces characters in the same character set
+as the display displays.
+The <EM>display-character-set</EM> is the character set that <EM>Alpine</EM>
+will attempt to use when sending characters to the display.
+<P>
+Besides those variables there is also
+<A HREF="config.html#use-system-translation"><EM>use-system-translation</EM></A>
+which can be used instead of these.
+That usage is only lightly tested and is not recommended.
+<P>
+
+By default, the <EM>display-character-set</EM> variable is not set and UNIX <EM>Alpine</EM>
+will attempt to get this information from the environment.
+In particular, the <CODE>nl_langinfo(CODESET)</CODE> call is used.
+This usually depends on the setting of the environment variables LANG or LC_CTYPE.
+An explicit configuration setting for <EM>display-character-set</EM> will,
+of course, override any default setting.
+<P>
+For <EM>PC-Alpine</EM> the <EM>display-character-set</EM>
+and the <EM>keyboard-character-set</EM>
+are always equivalent to <CODE>UTF-8</CODE> and this is not settable.
+<P>
+
+It is probably best to use UNIX <EM>Alpine</EM> in a terminal emulator
+capable of displaying UTF-8 characters, since that will allow you to
+view just about any received text that is correctly formatted (note,
+however, the above comments about known index display bugs with certain
+character sets). You'll need to have an emulator which uses a UTF-8 font
+and you'll need to set up your environment to use a UTF-8 charmap. For
+example, on a Linux system you might include
+<P>
+<CENTER> <CODE>setenv LANG en_US.UTF-8</CODE> </CENTER>
+<P>
+
+or something similar in your UNIX startup files.
+You'd also have to select a UTF-8 font in your terminal emulator.
+<P>
+
+The types of values that the character set variables may be set to are
+<CODE>UTF-8</CODE>, <CODE>ISO-8859-1</CODE>, or <CODE>EUC-JP</CODE>.
+The <CODE>ISO-2022</CODE> character sets are not supported for input or
+for display, but as a special case, <CODE>ISO-2022-JP</CODE> is supported
+for use only as a <EM>posting-character-set</EM>.
+In the Setup/Config screen you may choose from a list of all the
+character sets <EM>Alpine</EM> knows about by using the &quot;T&quot; ToCharsets command.
+Here is a list of many of the possible character sets:
+
+<P>
+<TABLE>
+<TR> <TD>UTF-8</TD> <TD>Unicode</TD> </TR>
+<TR> <TD>US-ASCII</TD> <TD>7 bit American English characters</TD> </TR>
+<TR> <TD>ISO-8859-1</TD> <TD>8 bit European "Latin 1" character set</TD> </TR>
+<TR> <TD>ISO-8859-2</TD> <TD>8 bit European "Latin 2" character set</TD> </TR>
+<TR> <TD>ISO-8859-3</TD> <TD>8 bit European "Latin 3" character set</TD> </TR>
+<TR> <TD>ISO-8859-4</TD> <TD>8 bit European "Latin 4" character set</TD> </TR>
+<TR> <TD>ISO-8859-5</TD> <TD>8 bit Latin and Cyrillic</TD> </TR>
+<TR> <TD>ISO-8859-6</TD> <TD>8 bit Latin and Arabic</TD> </TR>
+<TR> <TD>ISO-8859-7</TD> <TD>8 bit Latin and Greek</TD> </TR>
+<TR> <TD>ISO-8859-8</TD> <TD>8 bit Latin and Hebrew</TD> </TR>
+<TR> <TD>ISO-8859-9</TD> <TD>8 bit European "Latin 5" character set</TD> </TR>
+<TR> <TD>ISO-8859-10</TD> <TD>8 bit European "Latin 6" character set</TD> </TR>
+<TR> <TD>ISO-8859-11</TD> <TD>Latin and Thai</TD> </TR>
+<TR> <TD>ISO-8859-12</TD> <TD>Reserved</TD> </TR>
+<TR> <TD>ISO-8859-13</TD> <TD>8 bit European "Latin 7" character set</TD> </TR>
+<TR> <TD>ISO-8859-14</TD> <TD>8 bit European "Latin 8" character set</TD> </TR>
+<TR> <TD>ISO-8859-15</TD> <TD>8 bit European "Latin 9" character set</TD> </TR>
+<TR> <TD>ISO-8859-16</TD> <TD>8 bit European "Latin 10" character set</TD> </TR>
+<TR> <TD>KOI8-R</TD> <TD>8 bit Latin and Russian</TD> </TR>
+<TR> <TD>KOI8-U</TD> <TD>8 bit Latin and Ukranian</TD> </TR>
+<TR> <TD>WINDOWS-1251</TD> <TD>8 bit Latin and Russian</TD> </TR>
+<TR> <TD>TIS-620</TD> <TD>8 bit Latin and Thai</TD> </TR>
+<TR> <TD>VISCII</TD> <TD>8 bit Latin and Vietnamese</TD> </TR>
+<TR> <TD>GBK</TD> <TD>Latin and Chinese Simplified</TD> </TR>
+<TR> <TD>GB2312</TD> <TD>Latin and Chinese Simplified</TD> </TR>
+<TR> <TD>CN-GB</TD> <TD>Latin and Chinese Simplified</TD> </TR>
+<TR> <TD>BIG5</TD> <TD>Latin and Chinese Traditional</TD> </TR>
+<TR> <TD>BIG-5</TD> <TD>Latin and Chinese Traditional</TD> </TR>
+<TR> <TD>EUC-JP</TD> <TD>Latin and Japanese</TD> </TR>
+<TR> <TD>SHIFT-JIS</TD> <TD>Latin and Japanese</TD> </TR>
+<TR> <TD>EUC-KR</TD> <TD>Latin and Korean</TD> </TR>
+<TR> <TD>KSC5601</TD> <TD>Latin and Korean</TD> </TR>
+</TABLE>
+<P>
+
+When reading incoming email, <EM>Alpine</EM> understands many different
+character sets and is able to convert the incoming mail into Unicode.
+The Unicode will be converted to the <EM>display-character-set</EM>
+for display on your terminal.
+Characters typed at the keyboard will be converted from the
+<EM>keyboard-character-set</EM> to Unicode for <EM>Alpine</EM>'s internal
+use.
+You may find that you can read some malformed messages that do not
+contain a character set label by setting the option
+<A HREF="config.html#unknown-character-set"><EM>unknown-character-set</EM></A>.
+<P>
+
+The <EM>posting-character-set</EM> is used when sending messages.
+The default behavior obtained by leaving this variable unset is usually
+what is wanted. In that default case, <EM>Alpine</EM> will attempt
+to label the message with the most specific character set from the
+rather arbitrary set
+<P>
+US-ASCII, ISO-8859-15,
+ISO-8859-1, ISO-8859-2, VISCII, KOI8-R, KOI8-U, ISO-8859-7, ISO-8859-6,
+ISO-8859-8, TIS-620, ISO-2022-JP, GB2312, BIG5, EUC-KR, and UTF-8.
+<P>
+
+For example, if the message is made up of only US-ASCII characters, it
+will be labeled US-ASCII. Otherwise, if it is all ISO-8859-15 characters,
+that will be the label. If that doesn't work the same is tried for the
+remaining members of the list.
+
+<P>
+It might make sense to set <EM>posting-character-set</EM> to an
+explicit value instead.
+For example, if you usually send messages in Greek, setting this
+option to ISO-8859-7 will result in messages being labeled as
+US-ASCII if there are no non-ascii characters, ISO-8859-7 if there
+are only Greek characters, or UTF-8 if there are some characters
+which aren't representable in ISO-8859-7.
+Another possibility is to set this option explicitly to UTF-8.
+In that case
+<EM>Alpine</EM> labels only ascii messages as US-ASCII and all other
+messages as UTF-8.
+<P>
+
+<HR>
+
+<H2><A NAME="interrupt">Interrupted and Postponed Messages</A></H2>
+
+If the user is composing mail and is interrupted by being disconnected
+(SIGHUP, SIGTERM or end of file on the standard input), <EM>Alpine</EM> will save the
+interrupted composition and allow the user to continue it when he or she
+resumes <EM>Alpine</EM>. As the next <EM>Alpine</EM> session starts, a message will be given
+that an interrupted message can be continued. To continue the interrupted
+message, simply go into the composer. To get rid of the interrupted
+message, go into the composer and then cancel the message with
+<EM>^C.</EM> <P>
+
+Composition of half-done messages may be postponed to a later time by
+giving the <EM>^O</EM> command. Other messages can be composed while
+postponed messages wait. All of the postponed messages are kept in a
+single folder. Postponing is a good way to quickly reference other
+messages while composing. <P>
+
+<HR>
+
+<H2><A NAME="status">Message Status</A></H2>
+
+The c-client library allows for several flags or status marks to be set
+for each message. <EM>Alpine</EM> uses four of these flags: UNSEEN, DELETED,
+ANSWERED, and FLAGGED. The <CODE>N</CODE> in <EM>Alpine</EM>'s
+FOLDER INDEX means that a
+message is unseen-it has not been read from this folder yet. The <CODE>D</CODE>
+means that a message is marked for deletion.
+Messages marked with <CODE>D</CODE> are
+removed when the user <EM>Expunges</EM> the folder (which usually happens
+when the folder is closed or the user quits <EM>Alpine</EM>).
+The <CODE>A</CODE> in <EM>Alpine</EM>'s
+FOLDER INDEX means that the message has been replied-to. The <CODE>*</CODE> in
+<EM>Alpine</EM>'s FOLDER INDEX means that the message has been ``flagged'' as
+important. That is, the user used the <EM>Flag</EM> command to turn the
+FLAGGED flag on. This flag can mean whatever the user wants it to mean.
+It is just a way to mark some messages as being different from others. It
+will usually probably be used to mark a message as somehow being
+``important''. For Berkeley format folders, the message status is written
+into the email folder itself on the header lines marked <CODE>Status:</CODE>
+and <CODE>X-Status</CODE>.
+<P>
+It is also possible for a user to define their own flags in addition to the
+standard system flags above.
+In <EM>Alpine</EM> these user defined flags are called Keywords.
+<P>
+
+<HR>
+
+<H2><A NAME="MIME-read">MIME: Reading a Message</A></H2>
+
+<EM>Alpine</EM> should be able to handle just about any MIME message. When a MIME
+message is received, <EM>Alpine</EM> will display a list of all the parts, their
+types and sizes. It will display the attachments when possible and
+appropriate and allow users to <EM>Save</EM> all other attachments. <P>
+
+<EM>Alpine</EM> honors the "mailcap"
+configuration system for specifying external programs for handling attachments.
+The mailcap file maps MIME attachment types to the external programs
+loaded on your system which can display and/or print the file.
+A sample mailcap file
+comes bundled with the <EM>Alpine</EM> distribution. It includes comments which
+explain the syntax you need to use for mailcap. With the mailcap file,
+any program (mail readers, newsreaders, WWW clients) can use the same
+configuration for handling MIME-encoded data. <P>
+
+If a <CODE>MAILCAPS</CODE> environment variable is defined,
+<EM>Alpine</EM> will use that to look
+for one or more mailcap files, which are combined. In the absence of
+<CODE>MAILCAPS</CODE>, Unix <EM>Alpine</EM> will look for
+a personal mailcap file in <CODE>~/.mailcap</CODE>
+and combine that with a system-wide file in <CODE>/etc/mailcap</CODE>.
+<EM>PC-Alpine</EM> will look for a file named <CODE>MAILCAP</CODE> in the
+same directory as the <CODE>PINERC</CODE> file,
+and/or the directory containing the <CODE>ALPINE.EXE</CODE> executable. <P>
+
+Messages which include <EM>rich text</EM> or <EM>enriched text</EM> in the
+main body will be displayed in a very limited way (it will show bold and
+underlining). <P>
+
+If <EM>Alpine</EM> sees a MIME message part tagged as type IMAGE,
+and <EM>Alpine</EM>'s
+<A HREF="config.html#image-viewer"><EM>image-viewer</EM></A>
+configuration variable is set, <EM>Alpine</EM> will attempt to
+send that attachment to the named image viewing program. In the case of
+UNIX <EM>Alpine</EM>, the <CODE>DISPLAY</CODE> environment variable is
+checked to see if an X-terminal is being used (which can handle the images).
+If the <EM>image-viewer</EM> variable is not set,
+<EM>Alpine</EM> uses the <EM>mailcap</EM>
+system to determine what to do with IMAGE types, just as it does for any
+other non-TEXT type, e.g. type APPLICATION. For MIME's generic "catch
+all" type, APPLICATION/OCTET-STREAM, the <EM>mailcap</EM> file will
+probably not specify any action, but <EM>Alpine</EM> users may always
+<EM>Save</EM> any MIME attachment to a file. <P>
+
+MIME type "text/plain" is handled a little bit differently than the
+other types. If you are viewing the main body part in the MESSAGE TEXT
+viewing screen, then <EM>Alpine</EM> will use its internal viewer to display it.
+This happens even if there is a mailcap description which matches this
+particular type.
+However, if you view a part of
+type "text/plain" from the ATTACHMENT INDEX screen, then <EM>Alpine</EM> will check
+the mailcap database for a matching entry and use it in preference to
+its internal viewer. <P>
+
+Some text attachments, specifically those which are just other email messages
+forwarded as MIME messages, are displayed as part of the main body of the
+message. This distinction allows easy display when possible (the forward
+as MIME case) and use of an attachment viewer when that is desirable (the
+plain text file attachment case). <P>
+
+If the parts of a multipart message are alternate versions of the same
+thing <EM>Alpine</EM> will select and display the one best suited.
+For parts of type "message/external-body", the parameters showing the
+retrieval method will be displayed, and the retrieval process is automated.
+Messages of type "message/partial" are not supported. <P>
+
+<HR>
+
+<H2><A NAME="MIME-send">MIME: Sending a Message</A></H2>
+
+There are two important factors when trying to include an attachment in a
+message: encoding and labeling.
+<EM>Alpine</EM> has rules for both of these which
+try to assure that the message goes out in a form that is robust and can
+be handled by other MIME mail readers. <P>
+
+MIME has two ways of encoding data-Quoted-Printable and Base64.
+Quoted-Printable leaves the ASCII text alone and only changes 8-bit
+characters to "=" followed by the hex digits. For example, "=09" is a
+tab. It has the advantage that it is mostly readable and that it allows
+for end of line conversions between unlike systems. Base64 encoding is
+similar to <EM>uuencode</EM> or <EM>btoa</EM> and just encodes a raw bit
+stream. This encoding is designed to get text and binary files through
+even the most improperly implemented and configured gateways intact, even
+those that distort uuencoded data. <P>
+
+<STRONG>All</STRONG> attachments are encoded using Base64 encoding. This
+is so that the attachment will arrive at the other end looking exactly
+like it did when it was sent. Since Base64 is completely unreadable
+except by MIME-capable mailers or programs, there is an obvious tradeoff
+being made here. We chose to ensure absolutely reliable transport of
+attachments at the cost of requiring a MIME-capable mailer to read them.
+If the user doesn't want absolute integrity he or she may always
+<EM>include</EM> text (with the <EM>^R</EM> command) in the body of a
+message instead of attaching it. With this policy, the only time
+quoted-printable encoding is used is when the main body of a message
+includes special foreign language characters. <P>
+
+When an attachment is to be sent, <EM>Alpine</EM> sniffs through it to try to set the
+right label (content-type and subtype). An attachment with any lines
+longer than 500 characters in it or more than 10% of the characters are
+8-bit it will be considered binary data. <EM>Alpine</EM> will recognize (and
+correctly label) a few special types including GIF, JPEG, PostScript, and
+some audio formats. Another method which can be more robust and flexible for
+determining the content-type and subtype is to base it on the file extension.
+This method uses a <A HREF="config-notes.html#mime.types">MIME.Types File</A>.
+<P>
+
+If it is not binary data (has only a small proportion of 8-bit characters
+in it,) the attachment is considered 8-bit text. 8-bit text attachments
+are labeled "text/plain" with charset set to the value of the user's
+<EM>keyboard-character-set</EM> variable. If an attachment is ASCII (no 8-bit
+characters) and contains no control characters
+then it is considered plain ASCII text. Such attachments are given the MIME label
+"text/plain; charset=US-ASCII", regardless of the setting of the user's
+<EM>keyboard-character-set</EM> variable. <P>
+
+All other attachments are unrecognized and therefore given the generic
+MIME label "application/octet-stream". <P>
+
+<HR>
+
+<H2><A NAME="new-mail">New Mail Notification</A></H2>
+
+<EM>Alpine</EM> checks for new mail in the <EM>INBOX</EM> and in
+the currently open folder every two and a half minutes by default.
+This default can be changed in the system-wide configuration file or
+at compile-time with the --with-mailcheck-interval=VALUE configuration
+option.
+A user can change it by changing the option
+<A HREF="config.html#mail-check"><EM>mail-check-interval</EM></A>.
+A new mail check can be manually forced by redrawing the screen with a <EM>^L</EM>. <P>
+
+When there is new mail, the message(s) will appear in the index, the
+screen will beep, and a notice showing the sender and subject will be
+displayed. If there has been more than one new message since you last
+issued a command to <EM>Alpine</EM>, the notice will show the count of new messages
+and the sender of the most recent one. <P>
+
+<HR>
+
+<H2><A NAME="NFS">NFS</A></H2>
+
+It is possible to access mail folders on <EM>NFS</EM> mounted volumes with
+<EM>Alpine</EM>, but there are some drawbacks to doing this, especially in the case
+of incoming-message folders that may be concurrently updated by <EM>Alpine</EM> and
+the system's mail delivery agent. One concern is that <EM>Alpine</EM>'s
+user-contention locks don't work because <EM>/tmp</EM> is usually not
+shared, and even if it was, <EM>flock()</EM> doesn't work across
+<EM>NFS.</EM> <P> The implementation of the standard UNIX ".lock" file
+locking has been modified to work with <EM>NFS</EM> as follows. Standard
+hitching post locking is used so first a uniquely named file is created,
+usually something like <EM>xxxx.host.time.pid.</EM> Then a link to it is
+created named <EM>xxxx.lock</EM> where the folder being locked is
+<EM>xxxx.</EM> This file constitutes the lock. This is a standard UNIX
+locking scheme. After the link returns, a <EM>stat(2)</EM> is done on the
+file. If the file has two links, it is concluded that the lock succeeded
+and it is safe to proceed. <P>
+
+In order to minimize the risks of locking failures via <EM>NFS</EM>, we strongly
+recommend using IMAP rather than <EM>NFS</EM> to access remote incoming message
+folders, e.g. your <EM>INBOX</EM>. However, it is generally safe to access
+personal saved-message folders via <EM>NFS</EM> since it is unlikely that
+more than one process will be updating those folders at any given time.
+Still, some problems may occur when two <EM>Alpine</EM> sessions try to access the
+same mail folder from different hosts without using IMAP. Imagine the
+scenario: <EM>Alpine</EM>-A performs a write that changes the folder. <EM>Alpine</EM>-B then
+attempts to perform a write on the same folder. <EM>Alpine</EM>-B will get upset
+that the file has been changed from underneath it and abort operations on
+the folder. <EM>Alpine</EM>-B will continue to display mail from the folder that it
+has in its internal cache, but it will not read or write any further data.
+The only thing that will be lost out of the <EM>Alpine</EM>-B session when this
+happens is the last few status changes. <P>
+
+If other mail readers besides <EM>Alpine</EM> are involved, all bets are off.
+Typically, mailers don't take any precautions against a user opening a
+mailbox more than once and no special precautions are taken to prevent
+<EM>NFS</EM> problems. <P>
+
+<HR>
+
+<H2><A NAME="print">Printers and Printing</A></H2>
+
+UNIX <EM>Alpine</EM> can print to the standard UNIX line printers or to generic
+printers attached to ANSI terminals using the escape sequences to turn the
+printer on and off. The user has a choice of three printers in the
+configuration. <P>
+
+The first setting, <EM>attached-to-ansi</EM>, makes use of escape
+sequences on ANSI/VT100 terminals. It uses "&lt;ESC&gt;[5i" to begin
+directing all output sent to the terminal to the printer and then
+"&lt;ESC&gt;[4i" to return to normal. <EM>Alpine</EM> will send these escape
+sequences if the printer is set to <EM>attached-to-ansi.</EM> This works
+with most ANSI/VT100 emulators on Macs and PCs such as kermit, NCSA
+telnet, VersaTerm Pro, and WinQVT. Various terminal emulators implement
+the print feature differently.
+There is also a closely
+related method called <EM>attached-to-ansi-no-formfeed</EM> which is
+the same except for the lack of formfeed character at the end of the
+print job.
+<P>
+<EM>Attached-to-wyse</EM> and <EM>attached-to-wyse-no-formfeed</EM> are
+very similar to "attached-to-ansi".
+The only difference is in the control characters sent to turn the printer
+on and off.
+The Wyse version uses Ctrl-R for on, and Ctrl-T for off.
+<P>
+
+The second selection is the standard UNIX print command. The default is
+<EM>lpr</EM>, but it can be changed on a system basis to anything so
+desired in <CODE>/usr/local/lib/pine.conf</CODE>. <P>
+
+The third selection is
+the user's personal choice for a UNIX print command. The text to be
+printed is piped into the command. <EM>Enscript</EM> or <EM>lpr</EM> with
+options are popular choices. The actual command is retained even if one
+of the other print selections is used for a while. <P>
+
+Both the second and third sections are actually lists of possible commands
+rather than single commands. <P>
+
+If you have a PostScript printer attached to a PC or Macintosh, then you
+will need to use a utility called <EM>ansiprt</EM> to get printouts on
+your printer. <EM>Ansiprt</EM> source code and details can be found in
+the <CODE>./contrib</CODE> directory of the <EM>Alpine</EM> distribution. <P>
+
+<HR>
+
+<H2><A NAME="save">Save and Export</A></H2>
+
+<EM>Alpine</EM> users get two options for moving
+messages in <EM>Alpine</EM>: <EM>Save</EM> and <EM>Export</EM>.
+<EM>Save</EM> is used when the message should remain ``in the
+<EM>Alpine</EM> realm.'' Saved messages include the complete header
+(including header lines normally hidden by <EM>Alpine</EM>),
+are placed in a <EM>Alpine</EM> folder collection and
+accumulate in a standard folder format which <EM>Alpine</EM> can read.
+In contrast, the <EM>Export</EM> command is used to write the contents
+of a message to a file for use outside of <EM>Alpine</EM>.
+Messages which have been exported are
+placed in the user's home directory (unless the feature
+<A HREF="config.html#use-current-dir"><EM>use-current-dir</EM></A> is
+turned on), not in a <EM>Alpine</EM> folder collection.
+Unless FullHeaderMode is toggled on, all delivery-oriented headers are
+stripped from the message. Even with <EM>Export</EM>, <EM>Alpine</EM> retains
+message separators so that multiple messages can accumulate in a single
+file and subsequently be accessed as a folder. On UNIX systems, the
+<EM>Export</EM> command pays attention to the standard <EM>umask</EM> for
+the setting of the file permissions. <P>
+
+<HR>
+
+<H2><A NAME="sent-mail">Sent Mail</A></H2>
+
+<EM>Alpine</EM>'s default behavior is to keep a copy of each outgoing message in a
+special "sent mail" folder. This folder is also called the fcc for "file
+carbon copy". The existence, location and name of the sent mail folder
+are all configurable. Sent mail archiving can be turned off by setting
+the configuration variable
+<A HREF="config.html#def-fcc"><EM>default-fcc=""</EM></A>. The sent mail folder
+is assumed to be in the default collection for <EM>Save</EM>s,
+which is the first collection named in
+<A HREF="config.html#fold-coll"><EM>folder-collections</EM></A>.
+The name of the folder
+can be chosen by entering a name in <EM>default-fcc</EM>.
+With <EM>PC-Alpine</EM>,
+this can be a bit complicated. If the default collection for <EM>Save</EM>s is
+local (DOS), then the <EM>default-fcc</EM> needs to be <CODE>SENTMAIL</CODE>,
+which is syntax for a DOS file. However, if the default collection
+for <EM>Save</EM>s is
+remote, then the <EM>default-fcc</EM> needs to be <CODE>sent-mail</CODE>
+to match the UNIX syntax. <P>
+
+The configuration variable
+<A HREF="config.html#fcc-name-rule"><EM>fcc-name-rule</EM></A>
+also plays a role in selecting the folder to save sent mail in. <P>
+
+A danger here is that the sent mail could grow without bound. For this
+reason, we thought it useful to encourage the users to periodically prune
+their sent mail folder. The first time <EM>Alpine</EM> is used each month it will
+offer to archive all messages sent from the month before. <EM>Alpine</EM> also
+offers to delete all the sent mail archive folders which are more than 1
+month old. If the user or system has disabled sent mail archiving (by
+setting the configuration variable <EM>default-fcc=""</EM>)
+there will be no pruning question.
+<P>
+
+<HR>
+
+<H2><A NAME="spell">Spell Checker</A></H2>
+
+Both UNIX <EM>Alpine</EM> and <EM>PC-Alpine</EM>
+depend on the system for their spell checking and dictionary. <EM>Pico</EM>, the
+text editor, uses the same spell checking scheme as <EM>Alpine</EM>. <P>
+
+Lines beginning with "&gt;" (usually messages included in replies) are not
+checked. The message text to be checked is on the standard input and the
+incorrect words are expected on the standard output. <P>
+
+The default spell checker is UNIX <EM>spell</EM>. You can replace this
+by setting the <A HREF="config.html#speller"><EM>speller</EM></A> configuration
+variable.
+A common choice for a superior replacement is <EM>ispell</EM>.
+
+<P>
+<EM>PC-Alpine</EM> relies on the aspell library being installed.
+Aspell is independent of Alpine. The Windows version has
+traditionally been available at
+<A HREF="http://aspell.net/win32/">http://aspell.net/win32/</A>. You'll
+need to download and install both Aspell and a precompiled dictionary.
+Aspell is provided in an installer package. Dictionaries, to be
+installed after Aspell, are in '.exe' files to download and run.
+<P>
+
+<HR>
+
+<H2><A NAME="terminal">Terminal Emulation and Key Mapping</A></H2>
+
+UNIX <EM>Alpine</EM> has been designed to require as little as possible from the terminal.
+At the minimum, <EM>Alpine</EM> requires cursor positioning, clear to end of line,
+and inverse video. Unfortunately, there are terminals that are missing
+some of these such as a vt52. <EM>Alpine</EM> makes no assumptions as to whether the
+terminal wraps or doesn't wrap. If the terminal has other capabilities it
+may use some of them. <EM>Alpine</EM> won't run well on older terminals that require
+a space on the screen to change video attributes, such as the Televideo
+925. One can get around this on some terminals by using "protected field"
+mode. The terminal can be made to go into protected mode for reverse
+video, and then reverse video is assigned to protected mode. <P>
+
+<EM>Alpine</EM> handles screens of most any size and resizing on the fly. It catches
+SIGWINCH and does the appropriate thing.
+<P>
+
+On the input side of things, <EM>Alpine</EM> uses all the standard keys, most of the
+control keys and (in function-key mode) the function keys. <EM>Alpine</EM> avoids
+certain control keys, specifically ^S, ^Q, ^H, and <EM>^\</EM>
+because they have other meanings outside of <EM>Alpine</EM> (they control data flow,
+etc.) <EM>^H</EM> is treated the same as the <EM>delete</EM> key, so the
+<EM>backspace</EM> or <EM>delete</EM> keys always work regardless of any
+configuration. There is a feature <EM>compose-maps-delete-key-to-ctrl-d</EM>
+which makes the delete key behave like ^D rather than ^H (deletes current
+character instead of previous character). <P>
+
+Sometimes a communications program or communications server in between you
+and the other end will eat certain control characters. There is a
+work-around when you need it. If you type two escape characters followed
+by a character that will be interpreted as the character with the control
+key depressed. For example, <EM>ESC ESC T</EM> is equivalent to
+<EM>^T</EM>. <P>
+
+When a function key is pressed and <EM>Alpine</EM> is in regular (non-function key)
+mode, <EM>Alpine</EM> traps escape sequences for a number of common function keys so
+users don't get an error message or have an unexpected command executed
+for each character in the function key's escape sequence. <EM>Alpine</EM> expects
+the following escape sequences from terminals defined as VT100: <P>
+
+<BLOCKQUOTE>
+ ANSI/VT100<BR>
+F1: &lt;ESC&gt;OP <BR>
+F2: &lt;ESC&gt;OQ <BR>
+F3: &lt;ESC&gt;OR <BR>
+F4: &lt;ESC&gt;OS <BR>
+F5: &lt;ESC&gt;Op <BR>
+F6: &lt;ESC&gt;Oq <BR>
+F7: &lt;ESC&gt;Or <BR>
+F8: &lt;ESC&gt;Os <BR>
+F9: &lt;ESC&gt;Ot <BR>
+F10: &lt;ESC&gt;Ou <BR>
+F11: &lt;ESC&gt;Ov <BR>
+</BLOCKQUOTE>
+<P>
+
+Arrow keys are a special case. <EM>Alpine</EM> has the escape sequences for a number
+of conventions for arrow keys hard coded and does not use <EM>termcap</EM>
+to discover them. This is because <EM>termcap</EM> is sometimes
+incorrect, and because many users have PC's running terminal emulators
+that don't conform exactly to what they claim to emulate.
+There is a feature called
+<A HREF="config.html#termdef-takes-precedence"><EM>termdef-takes-precedence</EM></A>
+which can be set to cause the <EM>termcap</EM>
+or <EM>terminfo</EM> definitions to be used instead of the built in definitions.
+Some arrow keys on old terminals send single control characters
+like <EM>^K</EM> (one even sends <EM>^\</EM>).
+These arrow keys will not work with <EM>Alpine</EM>.
+The most popular escape sequences for arrow keys are: <P>
+
+<BLOCKQUOTE>
+Up: &lt;ESC&gt;[A &lt;ESC&gt;?x &lt;ESC&gt;A &lt;ESC&gt;OA<BR>
+Down: &lt;ESC&gt;[B &lt;ESC&gt;?r &lt;ESC&gt;B &lt;ESC&gt;OB<BR>
+Right: &lt;ESC&gt;[C &lt;ESC&gt;?v &lt;ESC&gt;C &lt;ESC&gt;OC<BR>
+Left: &lt;ESC&gt;[D &lt;ESC&gt;?t &lt;ESC&gt;D &lt;ESC&gt;OD<BR>
+</BLOCKQUOTE>
+<P>
+
+</BODY>
+</HTML>
diff --git a/doc/tech-notes/pn4tn b/doc/tech-notes/pn4tn
new file mode 100755
index 00000000..aad2b87e
--- /dev/null
+++ b/doc/tech-notes/pn4tn
@@ -0,0 +1,7 @@
+#!/bin/ksh
+echo This script will add the PNUTS generated navigation bar to the bottom
+echo of the PIC Technical Notes HTML files.
+echo '----------------------------------------------------------------------'
+# Written by Stefan Kramer, last modified by skramer on 1995/11/02.
+pnuts.4tech-notes for.pnuts
+# for.pnuts is the file that specifies the sequence of HTML files
diff --git a/doc/tech-notes/pnuts.4tech-notes b/doc/tech-notes/pnuts.4tech-notes
new file mode 100755
index 00000000..33e8d49f
--- /dev/null
+++ b/doc/tech-notes/pnuts.4tech-notes
@@ -0,0 +1,134 @@
+#!/usr/local/bin/perl
+#################################################
+#pnuts version 0.4 part of the WN server package
+#################################################
+# for more info, see http://hopf.math.nwu.edu/docs/utility.html#pnuts
+# Modified by Stefan Kramer for use with Pine Technical Notes
+# Last modified on 1995 Nov. 02
+
+
+require "getopts.pl";
+
+# Edit to specify what should appear as text of navigation bar. Note:
+# If the HTML files processed by PNUTS will be converted to plain-text files,
+# the PNUTS-generated link text will probably be stripped out, so this
+# text should be unique (i.e., not expected to occur elsewhere in text).
+
+# This is the original link appearance, without graphic buttons
+# $prevw="<b>Previous</b>";
+# $nextw="<b>Next</b>";
+# $upw="<b>Up one level</b>";
+# $topw="<b>Table of Contents</b>";
+# $searchw="<b>Search</b>";
+# $indexw="<b>Index</b>";
+
+$prevw='<IMG SRC="../graphics/BPprev.gif" ALT="[Previous]">';
+$nextw='<IMG SRC="../graphics/BPnext.gif" ALT="[Next]">';
+$upw=''; # not needed and don't have a graphic for UP
+$topw='<IMG SRC="../graphics/BPtoc.gif" ALT="[Table of Contents]">';
+$searchw='<IMG SRC="../graphics/BPsearch.gif" ALT="[Search]">';
+$indexw=''; # no index here and no graphic for it
+
+
+
+$VERSION = "0.4";
+
+ &Getopts('s:i:');
+ $search = $opt_s if $opt_s ne "";
+ $index = $opt_i if $opt_i ne "";
+
+ $file = shift;
+ $marker = "<!-- pnuts -->";
+
+
+ open( LIST, "<$file") || die "Can't open file: $!";
+
+
+ $nextfile = <LIST>;
+ print $nextfile;
+ chop( $nextfile);
+ $top = $nextfile;
+
+ while ( &getnextfile() ) {
+ $curcopy = $currentfile."~";
+
+ rename( $currentfile, $curcopy)
+ || die "Can't rename file: $currentfile";
+ open( OLDCURR, "<$curcopy" ) || die "Can't open file: $!";
+ open( NEWCURR, ">$currentfile" ) || die "Can't open file: $!";
+ while ( $line = <OLDCURR>) {
+ if ( $line =~ "^$marker") {
+ &pnutline();
+ }
+ else {
+ print NEWCURR $line;
+ }
+ }
+ close( OLDCURR);
+ close( NEWCURR);
+ }
+
+
+close( LIST);
+exit(0);
+
+sub pnutline {
+ printf( NEWCURR "$marker");
+ printf( NEWCURR "<P><HR>");
+ if ( $previous ) {
+ printf( NEWCURR " <a href=\"%s\">$prevw</a>", $previous);
+ }
+ if ( $nextfile ) {
+ printf( NEWCURR " <a href=\"%s\">$nextw</a>", $nextfile);
+ }
+ if ( $up[$curlevel - 1] ) {
+ printf( NEWCURR " <a href=\"%s\">$upw</a>",
+ $up[$curlevel-1]);
+ }
+ if ( $top && ( $top ne $currentfile) ) {
+ printf( NEWCURR " <a href=\"%s\">$topw</a>", $top);
+ }
+ if ( $search ) {
+ printf( NEWCURR " <a href=\"%s\">$searchw</a>", $search);
+ }
+ if ( $index ) {
+ printf( NEWCURR " <a href=\"%s\">$indexw</a>", $index);
+ }
+ printf( NEWCURR "\n");
+}
+
+sub getnextfile {
+ if ( $nextfile eq "") {
+ return 0;
+ }
+ $previous = $currentfile;
+ $up[$curlevel] = $currentfile;
+
+ $currentfile = $nextfile;
+ while ( 1 ) {
+ ($nextfile = <LIST>) || ($nextfile = "");
+ $nextfile =~ s/(\t*)//;
+ last if $nextfile eq "";
+ print $nextfile;
+ chop( $nextfile);
+ if ( -d $nextfile ) {
+ print "$nextfile is directory, ignoring it\n";
+ next;
+ }
+ last;
+ }
+ $curlevel = $nextlevel;
+ $nextlevel = length( $1);
+ return 1;
+}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/doc/tech-notes/porting.html b/doc/tech-notes/porting.html
new file mode 100644
index 00000000..14025372
--- /dev/null
+++ b/doc/tech-notes/porting.html
@@ -0,0 +1,336 @@
+<HTML><HEAD><TITLE>Pine Technical Notes: Notes for Porting and Modification</TITLE></HEAD><BODY>
+<H1>Notes for Porting and Modification</H1>
+
+<H2><A NAME="new-port">Porting Pine to Other Platforms</A></H2>
+
+Substantial effort has gone into making <EM>Pine</EM>/<EM>Pico</EM> portable.
+There are
+still, of course, a number of machine dependencies. Some of the ports are
+well-tested and some are untested. In particular, the most heavily used
+ports are the Ultrix, AIX, NeXT, Windows, and Dec Unix ports. <P>
+
+Each platform is given a three letter name (see the file
+<CODE>doc/pine-ports</CODE>). Make up a new one for your new port. We've
+attempted to bring all potential platform dependencies into the files:
+<CODE>{pico,pine}/osdep/os-xxx.h</CODE>,
+<CODE>{pico,pine}/osdep/os-xxx.ic</CODE>,
+and <CODE>{pico,pine}/makefile.xxx</CODE>, where <EM>xxx</EM>
+is the three letter name of the port. Thus any new port will hopefully
+just result in new versions of these files and some notes for the
+<EM>pine-ports</EM> file.
+There are separate dependencies in the c-client source, but that
+is handled in separate documentation there.
+Regrettably, the source code is also full of instances of <EM>ifdef DOS</EM>.
+Most of these are due to memory limit problems on <EM>DOS</EM> as
+opposed to actual system dependencies. <P>
+
+The makefiles are kept as simple and straight-forward as possible, because
+many previous attempts at automatically figuring out what to do seem to
+have become complex and ineffective in what they set out to do: which is
+to make compiling and installing the program easy. Each port is for a
+specific hardware/software platform, also because past attempts to
+generalize on versions of Unix or some CPU architecture don't seem to have
+gained much. Thus, there is a separate makefile for each platform that
+calls the appropriate compiler and linker with the appropriate flags.
+Most of these makefiles are pretty similar. The makefile also specifies
+which of the <EM>os-xxx.c</EM> and <EM>os-xxx.h</EM> files to use.
+It is
+the root from which most platform dependencies are selected. In most cases
+the makefile also defines a symbol named after the platform on which there
+can be dependencies in the source code, though we've tried to minimize
+relying on this where reasonable.
+When different "ports" are very similar, it is sometimes possible to use
+the same pine code (for example) with only a small change in the c-client
+or pico code. In those cases, that kind of dependency is reflected in
+the top-level <EM>build</EM> script.
+The <EM>build</EM> script can usually be
+used to invoke the various makes correctly.
+It may set some variables before running make so look to see what <EM>build</EM>
+does before trying a make in one of the subdirectories. This is especially true
+if LDAP is being included. <P>
+
+It is almost always easier to start with an existing port when trying to port
+to a new system. There is a port called <EM>gen</EM> (generic) which may be
+a good starting point. On the other hand, if another port is close to what
+you want, start with it instead.
+<P>
+
+The file <CODE>pico/osdep/os-xxx.h</CODE> contains most of the general
+platform dependent <EM>#include</EM>'s and <EM>#defines</EM>.
+There are a number
+of <EM>Pine</EM> configuration settings that are defined in
+<CODE>pine/osdep/os-xxx.h</CODE>, as well, such as the
+place it looks for certain files, defaults for the printer and folder
+names, the maximum screen size, and so on.
+Start by looking at the generic <CODE>pico/osdep/os-gen.h</CODE> file
+and comparing it to some of the specific <CODE>os-xxx.h</CODE> files there. <P>
+
+The <CODE>osdep/os-xxx.c</CODE> files contain functions that are potentially
+platform dependent. Again, the idea is to gather all the dependencies in
+one place.
+We use a complicated looking method to produce
+the <EM>os-xxx.c</EM> files from a set of included files. Each included
+file usually contains a single implementation method and we've
+found that there are
+usually only two or three different methods in the
+ports we've done so far. Hopefully, coming up with an <CODE>os-xxx.c</CODE>
+for a new port will usually be a matter of including the right set of
+these already written functions. This is done by writing a new
+<CODE>os-xxx.ic</CODE> file in the <CODE>osdep</CODE> subdirectories.
+Starting with the generic <CODE>os-gen.ic</CODE>, as you did with
+the <CODE>os-gen.h</CODE> file above, may be a useful strategy. <P>
+
+We strongly encourage that no changes be made to the general source when
+porting and that all changes be contained in the system
+dependent files if possible. The object is to maintain source code
+integrity and assimilate ports to new platforms rapidly. The more
+conventional way to do this is with a large collection of
+<EM>#ifdefs</EM>. The problem with this is that adding a port for a new
+platform implies changing the source code for all the other platforms and
+thereby risks breaking them. (We readily admit that there are still too
+many <EM>ifdefs</EM> in the code.) <P>
+
+If you do port <EM>Pine</EM> to a new platform we hope that you will send us the
+changes required so that we may attempt to include it in a later release.
+Thanks! <P>
+
+<HR>
+
+<H2><A NAME="checklist">Test Checklist</A></H2>
+
+The following is a checklist of some things to check when testing a new
+port: <P>
+
+<DL COMPACT>
+<DT>
+ ___
+<DD>
+Sending mail, check that headers are correct
+<DT>
+ ___
+<DD>
+Sending mail with attachments
+<DT>
+ ___
+<DD>
+Sending mail with SMTP server
+<DT>
+ ___
+<DD>
+Sending mail without SMTP server
+<DT>
+ ___
+<DD>
+Sending mail with list of two SMTP servers, first one doesn't answer
+<DT>
+ ___
+<DD>
+Replying to and forwarding a message
+<DT>
+ ___
+<DD>
+Postponing messages under composition
+<DT>
+ ___
+<DD>
+Composer operations
+<DT>
+ ___
+<DD>
+Alternate editor, <EM>enable-alternate-editor-implicitly</EM>
+<DT>
+ ___
+<DD>
+Make sure local user names are expanded
+<DT>
+ ___
+<DD>
+Test spelling checker
+<DT>
+ ___
+<DD>
+Catching of SIGHUP while message is being composed
+<DT>
+ ___
+<DD>
+Setting of variables in <CODE>.pinerc</CODE>
+<DT>
+ ___
+<DD>
+New mail notification. Should happen with <EM>Pine</EM> idle to check timeouts
+<DT>
+ ___
+<DD>
+Reading mail (attachments, MIME, MIME with mailcap viewers)
+<DT>
+ ___
+<DD>
+Deleting, undeleting, expunging, sorting
+<DT>
+ ___
+<DD>
+Expunge to empty folder
+<DT>
+ ___
+<DD>
+Make sure that <EM>~</EM> expansion works in config files
+<DT>
+ ___
+<DD>
+Make sure that $VAR expansion works in config files
+<DT>
+ ___
+<DD>
+Save message to folder, check error conditions such as permission denied
+<DT>
+ ___
+<DD>
+Export message with FullHeaderMode on and off
+<DT>
+ ___
+<DD>
+Checkpointing (see the section on checkpointing)
+<DT>
+ ___
+<DD>
+Open IMAP and RIMAP folders
+<DT>
+ ___
+<DD>
+Default-fcc on remote IMAP server
+<DT>
+ ___
+<DD>
+Fcc-name-rule, fcc in addrbook (while composing)
+<DT>
+ ___
+<DD>
+Test opening bogus folders: invalid format, no permission
+<DT>
+ ___
+<DD>
+Open a USENET news group, list in folder-lister, read news, post news
+<DT>
+ ___
+<DD>
+Command line arguments
+<DT>
+ ___
+<DD>
+Change password
+<DT>
+ ___
+<DD>
+Lock keyboard
+<DT>
+ ___
+<DD>
+Address book operations (edit, delete, add, lists, whereis, composeto)
+<DT>
+ ___
+<DD>
+ReadOnly address book
+<DT>
+ ___
+<DD>
+Look at addrbook, change addrbook-sort-rule in Config, go back to
+addrbook screen
+<DT>
+ ___
+<DD>
+No permission to write in same directory as addrbook, should create
+addrbook.lu in a temp directory
+<DT>
+ ___
+<DD>
+Multiple address books
+<DT>
+ ___
+<DD>
+Address book loops from one addrbook to another and back
+<DT>
+ ___
+<DD>
+TakeAddr command with one address, with multiple addresses
+<DT>
+ ___
+<DD>
+TakeAddr command with ReadOnly address books
+<DT>
+ ___
+<DD>
+TakeAddr command with one of two address books ReadOnly
+<DT>
+ ___
+<DD>
+Send mail with empty address book
+<DT>
+ ___
+<DD>
+Config Screen operation, does pinerc get written?
+<DT>
+ ___
+<DD>
+Make sure SIGTSTP, ^Z works
+<DT>
+ ___
+<DD>
+Sent-mail pruning (set back <EM>last-time-prune-questioned</EM> variable)
+<DT>
+ ___
+<DD>
+Printing using all three printer configurations, various screens
+<DT>
+ ___
+<DD>
+View help text and news
+<DT>
+ ___
+<DD>
+Folder list operations (rename, create, delete...)
+<DT>
+ ___
+<DD>
+Saved-msg-name-rule
+<DT>
+ ___
+<DD>
+Screen redrawing in various screens (^L)
+<DT>
+ ___
+<DD>
+Window resizing in various screens
+<DT>
+ ___
+<DD>
+Error messages for incorrect terminal types (try "foo" and "vt52")
+<DT>
+ ___
+<DD>
+Reading of <CODE>/usr/local/lib/pine.conf</CODE>
+<DT>
+ ___
+<DD>
+Fixing variables and features in <CODE>/usr/local/lib/pine.conf.fixed</CODE>
+<DT>
+ ___
+<DD>
+Flag command (check message status changed in mail folder)
+<DT>
+ ___
+<DD>
+Initial-keystroke-list
+<DT>
+ ___
+<DD>
+Aggregate operations (save, delete, export, takeaddr, ...)
+<DT>
+ ___
+<DD>
+Build xxx from scratch, build clean
+</DL>
+
+<!-- pnuts -->
+
+</BODY>
+</HTML>
diff --git a/imap/CONTENTS b/imap/CONTENTS
new file mode 100644
index 00000000..73a54f6e
--- /dev/null
+++ b/imap/CONTENTS
@@ -0,0 +1,75 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+ TOOLKIT DIRECTORY CONTENTS
+
+Documentation:
+ . CONTENTS this file
+ . LICENSE.txt software license
+ . NOTICE copyright notice
+ . README read this file first
+ . SUPPORT where to go to ask questions and/or report bugs
+ . docs/BUILD build and installation instructions
+ . docs/CONFIG detailed configuration notes
+ . docs/FAQ.html frequently asked questions and answers
+ . docs/FAQ.txt text version of FAQ.HTML
+ . docs/RELNOTES release notes
+ . docs/SSLBUILD build and installation instructions using SSL
+ . docs/Y2K information relating to Y2K issues
+ . docs/bugs.txt known bugs and deficiencies in this software
+ . docs/calendar.txt information relating to the calendar
+ . docs/commndmt.txt "ten commandments" on how to write a good IMAP client
+ . docs/draft/ Internet protocol documentation drafts
+ . docs/drivers.txt how various mailbox format drivers interact, and
+ comparable features and performance
+ . docs/formats.txt mailbox formats
+ . docs/internal.txt programming interfaces
+ . docs/locking.txt how file locking works
+ . docs/md5.txt CRAM-MD5 authentication setup instructions
+ . docs/mixfmt.txt specification of new mix format
+ . docs/naming.txt mailbox naming conventions
+ . docs/rfc/ Internet protocol documentation
+
+Sources:
+ . Makefile master makefile for UNIX
+ . makefile.nt master makefile for NT/Win32
+ . makefile.ntk master makefile for NT/Win32 using Kerberos V
+ . makefile.os2 master makefile for OS/2
+ . makefile.w2k master makefile for Windows 2000
+ . makefile.wce master makefile for Windows CE
+ . src/ansilib pre-processed ANSI library routines
+ . src/c-client pre-processed c-client sources
+ . src/charset pre-processed character set conversion tables
+ . src/dmail pre-processed user mail delivery sources
+ . src/ipopd pre-processed POP2/POP3 daemon sources
+ . src/imapd pre-processed IMAP4rev1 daemon sources
+ . src/mailutil pre-processed mailbox utility sources
+ . src/mlock pre-processed mailbox locking sources
+ . src/mtest pre-processed c-client testbed sources
+ . src/osdep/amiga pre-processed Amiga-specific sources
+ . src/osdep/dos pre-processed DOS-specific sources
+ . src/osdep/mac pre-processed Mac-specific sources
+ . src/osdep/nt pre-processed NT-specific sources
+ . src/osdep/os2 pre-processed OS/2-specific sources (incomplete)
+ . src/osdep/tops-20 pre-processed TOPS-20 specific sources
+ . src/osdep/unix pre-processed UNIX-specific sources
+ . src/osdep/vms pre-processed VAX/VMS specific sources
+ . src/osdep/wce pre-processed Windows CE-specific sources (incomplete)
+ . src/tmail pre-processed system mail delivery sources
+ . tools internal tools needed as part of the build process
+
+Directories created at build time on UNIX and NT/Win32:
+ . c-client post-processed c-client sources and binary
+ . ipopd post-processed POP2/POP3 daemon sources and binaries
+ . imapd post-processed IMAP4rev1 daemon sources and binaries
+ . mtest post-processed c-client testbed sources and binaries
diff --git a/imap/LICENSE.txt b/imap/LICENSE.txt
new file mode 100644
index 00000000..349bd53f
--- /dev/null
+++ b/imap/LICENSE.txt
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/imap/Makefile b/imap/Makefile
new file mode 100644
index 00000000..5fcf1dd9
--- /dev/null
+++ b/imap/Makefile
@@ -0,0 +1,737 @@
+# ========================================================================
+# Copyright 1988-2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+# Program: IMAP Toolkit Makefile
+#
+# Author: Mark Crispin
+# UW Technology
+# Seattle, WA 98195
+# Internet: MRC@Washington.EDU
+#
+# Date: 7 December 1989
+# Last Edited: 12 May 2008
+
+
+# Normal command to build IMAP toolkit:
+# make <port> [EXTRAAUTHENTICATORS=xxx] [EXTRADRIVERS=xxx] [EXTRACFLAGS=xxx]
+# [PASSWDTYPE=xxx] [SSLTYPE=xxx] [IP=n]
+
+
+# Port name. These refer to the *standard* compiler on the given system.
+# This means, for example, that the hpx port is for HP's compiler and not for
+# a non-standard compiler such as gcc.
+#
+# If you are using gcc and it is not the standard compiler on your system, try
+# using an ANSI port that is close to what you have. For example, if your
+# system is SVR4ish, try a32 or lnx; if it's more BSDish, try nxt, mct, or bsi.
+#
+# The following ports are bundled:
+# a32 AIX 3.2 for RS/6000
+# a41 AIX 4.1 for RS/6000
+# a52 Attempt at AIX 5.2
+# aix AIX/370 (not RS/6000!!)
+# ami AmigaDOS
+# am2 AmigaDOS with a 68020+
+# ama AmigaDOS using AS225R2
+# amn AmigaDOS with a 680x0 using "new" socket library
+# aos AOS for RT
+# art AIX 2.2.1 for RT
+# asv Altos SVR4
+# aux A/UX
+# bs3 BSD/i386 3.0 and higher
+# bsd generic BSD 4.3 (as in ancient 1980s version)
+# bsf FreeBSD
+# bsi BSD/i386
+# bso OpenBSD (yes, yet another one...)
+# cvx Convex
+# cyg Cygwin
+# d-g Data General DG/UX prior to 5.4 (d41 port no longer exists)
+# d54 Data General DG/UX 5.4
+# do4 Apollo Domain/OS sr10.4
+# dpx Bull DPX/2 B.O.S.
+# drs ICL DRS/NX
+# dyn Dynix
+# epx EP/IX
+# ga4 GCC AIX 4.x for RS/6000
+# gas GCC Altos SVR4
+# gcs GCC Solaris with Blastwave Community Open Source Software
+# gh9 GCC HP-UX 9.x
+# ghp GCC HP-UX 10.x
+# ghs GCC HP-UX 10.x with Trusted Computer Base
+# go5 GCC 2.7.1 (95q4 from Skunkware _not_ 98q2!) SCO Open Server 5.0.x
+# gsc GCC Santa Cruz Operation
+# gsg GCC SGI
+# gso GCC Solaris
+# gsu GCC SUN-OS
+# gul GCC RISC Ultrix (DEC-5000)
+# h11 HP-UX 11i
+# hpp HP-UX 9.x (see gh9)
+# hpx HP-UX 10.x (see ghp, ghs, hxd, and shp)
+# hxd HP-UX 10.x with DCE security (see shp)
+# isc Interactive Systems
+# ldb Debian Linux
+# lfd Fedora Core 4
+# ln8 Linux for Nokia N800
+# lnx Linux with traditional passwords and crypt() in the C library
+# (see lnp, sl4, sl5, and slx)
+# lnp Linux with Pluggable Authentication Modules (PAM)
+# lmd Mandrake Linux
+# lr5 RedHat Enterprise 5 and later (same as lfd)
+# lrh RedHat Linux 7.2 and later
+# lsu SuSE Linux (same as lrh)
+# lyn LynxOS
+# mct MachTen
+# mnt Atari ST Mint (not MacMint)
+# neb NetBSD
+# nec NEC UX
+# nto QNX Neutrine RTP
+# nxt NEXTSTEP
+# nx3 NEXTSTEP 3.x
+# osf OSF/1 (see sos, os4)
+# os4 OSF/1 (Digital UNIX) 4
+# osi Apple iPhone and iPod Touch
+# osx Mac OS X
+# oxp Mac OS X with Pluggable Authentication Modules (PAM)
+# ptx PTX
+# pyr Pyramid
+# qnx QNX 4
+# s40 SUN-OS 4.0 (*not* Solaris)
+# sc5 SCO Open Server 5.0.x (see go5)
+# sco Santa Cruz Operation (see sc5, go5)
+# shp HP-UX with Trusted Computer Base
+# sgi Silicon Graphics IRIX
+# sg6 Silicon Graphics IRIX 6.5
+# sl4 Linux using -lshadow to get the crypt() function
+# sl5 Linux with shadow passwords, no extra libraries
+# slx Linux using -lcrypt to get the crypt() function
+# snx Siemens Nixdorf SININX or Reliant UNIX
+# soc Solaris with /opt/SUNWspro/bin/cc
+# sol Solaris (won't work unless "ucbcc" works -- use gso instead)
+# sos OSF/1 with SecureWare
+# ssn SUN-OS with shadow password security
+# sua Windows Vista (Enterprise or Ultima) Subsystem for Unix Applications
+# sun SUN-OS 4.1 or better (*not* Solaris) (see ssn)
+# sv2 SVR2 on AT&T PC-7300 (incomplete port)
+# sv4 generic SVR4
+# ult RISC Ultrix (DEC-5000)
+# uw2 UnixWare SVR4.2
+# vul VAX Ultrix
+# vu2 VAX Ultrix 2.3 (e.g. for VAXstation-2000 or similar old version)
+
+
+# Extra authenticators (e.g. OTP, Kerberos, etc.). Adds linkage for
+# auth_xxx.c and executes Makefile.xxx, where xxx is the name of the
+# authenticator. Some authenticators are only available from third parties.
+#
+# The following extra authenticators are bundled:
+# gss Kerberos V
+
+EXTRAAUTHENTICATORS=
+
+
+# Additional mailbox drivers. Add linkage for xxxdriver. Some drivers are
+# only available from third parties.
+#
+# The following extra drivers are bundled:
+# mbox if file "mbox" exists on the home directory, automatically moves mail
+# from the spool directory to "mbox" and uses "mbox" as INBOX.
+
+EXTRADRIVERS=mbox
+
+
+# Plaintext password type. Defines how plaintext password authentication is
+# done on this system.
+#
+# The following plaintext login types are bundled:
+# afs AFS authentication database
+# dce DCE authentication database
+# gss Kerberos V
+# nul plaintext authentication never permitted
+# pam PAM authentication (note: for Linux, you should use the "lnp" port
+# instead of setting this...also, you may have to modify PAMLDFLAGS
+# in the imap-[]/src/osdep/unix/Makefile
+# pmb PAM authentication for broken implementations such as Solaris.
+# you may have to modify PAMLDFLAGS
+# std system standard (typically passwd file), determined by port
+# two try alternative (defined by CHECKPWALT), then std
+
+PASSWDTYPE=std
+
+
+# SSL type. Defines whether or not SSL support is on this system
+#
+# The following SSL types are bundled:
+# none no SSL support
+# unix SSL support using OpenSSL
+# nopwd SSL support using OpenSSL, and plaintext authentication permitted only
+# in SSL/TLS sessions
+# sco link SSL before other libraries (for SCO systems)
+# unix.nopwd same as nopwd
+# sco.nopwd same as nopwd, plaintext authentication in SSL/TLS only
+#
+# SSLTYPE=nopwd is now the default as required by RFC 3501
+
+SSLTYPE=nopwd
+
+
+# IP protocol version
+#
+# The following IP protocol versions are defined:
+# o IPv4 support, no DNS (truly ancient systems)
+# 4 (default) IPv4 support only
+# 6 IPv6 and IPv4 support
+
+IP=4
+IP6=6
+
+
+# The following extra compilation flags are defined. None of these flags are
+# recommended. If you use these, include them in the EXTRACFLAGS.
+#
+# -DDISABLE_POP_PROXY
+# By default, the ipop[23]d servers offer POP->IMAP proxy access,
+# which allow a POP client to access mail on an IMAP server by using the
+# POP server as a go-between. Setting this option disables this
+# facility.
+#
+# -DOLDFILESUFFIX=\"xxx\"
+# Change the default suffix appended to the backup .newsrc file from
+# "old".
+#
+# -DSTRICT_RFC822_TIMEZONES
+# Disable recognition of the non-standard UTC (0000), MET (+0100),
+# EET (+0200), JST (+0900), ADT (-0300), AST (-0400), YDT (-0800),
+# YST (-0900), and HST (-1000) symbolic timezones.
+#
+# -DBRITISH_SUMMER_TIME
+# Enables recognition of non-standard symbolic timezone BST as +0100.
+#
+# -DBERING_STANDARD_TIME
+# Enables recognition of non-standard symbolic timezone BST as -1100.
+#
+# -DNEWFOUNDLAND_STANDARD_TIME
+# Enables recognition of non-standard symbolic timezone NST as -0330.
+#
+# -DNOME_STANDARD_TIME
+# Enables recognition of non-standard symbolic timezone NST as -1100.
+#
+# -DSAMOA_STANDARD_TIME
+# Enables recognition of non-standard symbolic timezone SST as -1100.
+#
+# -DY4KBUGFIX
+# Turn on the Y4K bugfix (yes, that's year 4000). It isn't well-known,
+# but century years evenly divisible by 4000 are *not* leap years in the
+# Gregorian calendar. A lot of "Y2K compilant" software does not know
+# about this rule. Remember to turn this on sometime in the next 2000
+# years.
+#
+# -DUSEORTHODOXCALENDAR
+# Use the more accurate Eastern Orthodox calendar instead of the
+# Gregorian calendar. The century years which are leap years happen
+# at alternating 400 and 500 year intervals without shifts every 4000
+# years. The Orthodox and Gregorian calendars diverge by 1 day for
+# gradually-increasing intervals, starting at 2800-2900, and becoming
+# permanent at 48,300.
+#
+# -DUSEJULIANCALENDAR
+# Use the less accurate Julian calendar instead of the Gregorian
+# calendar. Leap years are every 4 years, including century years.
+# My apologies to those in the English-speaking world who object to
+# the reform of September 2, 1752 -> September 14, 1752, since this
+# code still uses January 1 (which Julius Ceasar decreed as the start
+# of the year, which since 153 BCE was the day that Roman consuls
+# took office), rather than the traditional March 25 used by the
+# British. As of 2005, the Julian calendar and the Gregorian calendar
+# diverge by 15 days.
+
+EXTRACFLAGS=
+
+
+# Extra linker flags (additional/alternative libraries, etc.)
+
+EXTRALDFLAGS=
+
+
+# Special make flags (e.g. to override make environment variables)
+
+EXTRASPECIALS=
+SPECIALS=
+
+
+# Normal commands
+
+CAT=cat
+CD=cd
+LN=ln -s
+MAKE=make
+MKDIR=mkdir
+BUILDTYPE=rebuild
+RM=rm -rf
+SH=sh
+SYSTEM=unix
+TOOLS=tools
+TOUCH=touch
+
+
+# Primary build command
+
+BUILD=$(MAKE) build EXTRACFLAGS='$(EXTRACFLAGS)'\
+ EXTRALDFLAGS='$(EXTRALDFLAGS)'\
+ EXTRADRIVERS='$(EXTRADRIVERS)'\
+ EXTRAAUTHENTICATORS='$(EXTRAAUTHENTICATORS)'\
+ PASSWDTYPE=$(PASSWDTYPE) SSLTYPE=$(SSLTYPE) IP=$(IP)\
+ EXTRASPECIALS='$(EXTRASPECIALS)'
+
+
+# Make the IMAP Toolkit
+
+all: c-client SPECIALS rebuild bundled
+
+c-client:
+ @echo Not processed yet. In a first-time build, you must specify
+ @echo the system type so that the sources are properly processed.
+ @false
+
+
+SPECIALS:
+ echo $(SPECIALS) > SPECIALS
+
+# Note on SCO you may have to set LN to "ln".
+
+a32 a41 a52 aix bs3 bsi d-g d54 do4 drs epx ga4 gas gh9 ghp ghs go5 gsc gsg gso gul h11 hpp hpx lnp lyn mct mnt nec nto nxt nx3 osf os4 ptx qnx sc5 sco sgi sg6 shp sl4 sl5 slx snx soc sol sos uw2: an
+ $(BUILD) BUILDTYPE=$@
+
+# If you use sv4, you may find that it works to move it to use the an process.
+# If so, you probably will want to delete the "-Dconst=" from the sv4 CFLAGS in
+# the c-client Makefile.
+
+aos art asv aux bsd cvx dpx dyn isc pyr sv4 ult vul vu2: ua
+ $(BUILD) BUILDTYPE=$@
+
+
+# Knotheads moved Kerberos and SSL locations on these platforms
+
+# Paul Vixie claims that all FreeBSD versions have working IPv6
+
+bsf: an
+ $(TOUCH) ip6
+ $(BUILD) BUILDTYPE=$@ IP=$(IP6) \
+ PASSWDTYPE=pam \
+ SPECIALS="SSLINCLUDE=/usr/include/openssl SSLLIB=/usr/lib SSLCERTS=/etc/ssl/certs SSLKEYS=/etc/ssl/private GSSINCLUDE=/usr/include GSSLIB=/usr/lib PAMLDFLAGS=-lpam"
+
+# I assume that Theo did the right thing for IPv6. OpenBSD does not have PAM.
+
+bso: an
+ $(TOUCH) ip6
+ $(BUILD) BUILDTYPE=$@ IP=$(IP6) \
+ SPECIALS="SSLINCLUDE=/usr/include/openssl SSLLIB=/usr/lib SSLCERTS=/etc/ssl SSLKEYS=/etc/ssl/private GSSINCLUDE=/usr/include GSSLIB=/usr/lib"
+
+# Info from Joel Reicher about NetBSD SSL paths. I assume it has PAM because pam is in NetBSD sources...
+
+neb: an
+ $(TOUCH) ip6
+ $(BUILD) BUILDTYPE=$@ IP=$(IP6) \
+ PASSWDTYPE=pam \
+ SPECIALS="SSLINCLUDE=/usr/include/openssl SSLLIB=/usr/lib SSLCERTS=/etc/openssl/certs SSLKEYS=/etc/openssl/private GSSINCLUDE=/usr/include GSSLIB=/usr/lib PAMLDFLAGS=-lpam"
+
+cyg: an
+ $(BUILD) BUILDTYPE=cyg \
+ SPECIALS="SSLINCLUDE=/usr/include/openssl SSLLIB=/usr/lib SSLCERTS=/usr/ssl/certs SSLKEYS=/usr/ssl/certs"
+
+gcs: an
+ $(BUILD) BUILDTYPE=gso \
+ SPECIALS="SSLINCLUDE=/opt/csw/include/openssl SSLLIB=/opt/csw/lib SSLCERTS=/opt/csw/ssl/certs SSLKEYS=/opt/csw/ssl/certs"
+
+ldb: an
+ $(BUILD) BUILDTYPE=lnp IP=$(IP6) \
+ SPECIALS="SSLINCLUDE=/usr/include/openssl SSLLIB=/usr/lib SSLCERTS=/etc/ssl/certs SSLKEYS=/etc/ssl/private GSSINCLUDE=/usr/include GSSLIB=/usr/lib MAILSPOOL=/var/mail"
+
+lfd: an
+ $(BUILD) BUILDTYPE=lnp IP=$(IP6) \
+ SPECIALS="SSLINCLUDE=/usr/include/openssl SSLLIB=/usr/lib SSLCERTS=/etc/pki/tls/certs SSLKEYS=/etc/pki/tls/private GSSDIR=/usr/kerberos"
+
+ln8: an
+ $(TOUCH) ip6
+ $(BUILD) BUILDTYPE=slx IP=$(IP6) \
+ SPECIALS="SSLINCLUDE=/usr/include/openssl SSLLIB=/usr/lib SSLCERTS=/usr/lib/ssl/certs MAILSPOOL=/var/mail"
+
+
+# RHE5 does not have the IPv6 bug
+
+lr5: an
+ $(TOUCH) ip6
+ $(BUILD) BUILDTYPE=lnp IP=$(IP6) \
+ SPECIALS="SSLINCLUDE=/usr/include/openssl SSLLIB=/usr/lib SSLCERTS=/etc/pki/tls/certs SSLKEYS=/etc/pki/tls/private GSSDIR=/usr/kerberos"
+
+lmd: an
+ $(BUILD) BUILDTYPE=lnp IP=$(IP6) \
+ SPECIALS="SSLINCLUDE=/usr/include/openssl SSLLIB=/usr/lib SSLCERTS=/usr/lib/ssl/certs SSLKEYS=/usr/lib/ssl/private GSSINCLUDE=/usr/include GSSLIB=/usr/lib"
+
+# RHE3 definitely has the IPv6 bug
+
+lrh: lrhok an
+ $(BUILD) BUILDTYPE=lnp IP=$(IP6) \
+ SPECIALS="SSLINCLUDE=/usr/include/openssl SSLLIB=/usr/lib SSLCERTS=/usr/share/ssl/certs SSLKEYS=/usr/share/ssl/private GSSDIR=/usr/kerberos"
+
+lrhok:
+ @$(SH) -c '(test ! -d /etc/pki/tls ) || make lrhwarn'
+ @$(TOUCH) lrhok
+
+lrhwarn:
+ @echo You are building for OLD versions of RedHat Linux. This build
+ @echo is NOT suitable for RedHat Enterprise 5, which stores SSL/TLS
+ @echo certificates and keys in /etc/pki/tls rather than /usr/share/ssl.
+ @echo If you want to build for modern RedHat Linux, you should use
+ @echo make lr5 instead.
+ @echo Do you want to continue this build? Type y or n please:
+ @$(SH) -c 'read x; case "$$x" in y) exit 0;; *) exit 1;; esac'
+ @echo OK, I will remember that you really want to build for old
+ @echo RedHat Linux. You will not see this message again.
+ @echo If you realize that you really wanted to build for modern
+ @echo RedHat Linux, then do the following commands:
+ @echo % rm lrhok
+ @echo % make clean
+ @echo % make lr5
+
+lsu: an
+ $(BUILD) BUILDTYPE=lnp IP=$(IP6) \
+ SPECIALS="SSLINCLUDE=/usr/include/openssl SSLLIB=/usr/lib SSLCERTS=/usr/share/ssl/certs SSLKEYS=/usr/share/ssl/private GSSDIR=/usr/kerberos"
+
+# iToy does not have Kerberos or PAM. It doesn't have a
+# /System/Library/OpenSSL directory either, but the libcrypto shared library
+# has these locations so this is what we will use.
+
+osi: an
+ $(TOUCH) ip6
+ $(BUILD) BUILDTYPE=osx IP=$(IP6) CC=arm-apple-darwin-gcc \
+ SPECIALS="SSLINCLUDE=/usr/include/openssl SSLLIB=/usr/lib SSLCERTS=/System/Library/OpenSSL/certs SSLKEYS=/System/Library/OpenSSL/private"
+
+oxp: an
+ $(TOUCH) ip6
+ $(BUILD) BUILDTYPE=osx IP=$(IP6) EXTRAAUTHENTICATORS="$(EXTRAAUTHENTICATORS) gss" \
+ PASSWDTYPE=pam \
+ EXTRACFLAGS="$(EXTRACFLAGS) -DMAC_OSX_KLUDGE=1" \
+ SPECIALS="SSLINCLUDE=/usr/include/openssl SSLLIB=/usr/lib SSLCERTS=/System/Library/OpenSSL/certs SSLKEYS=/System/Library/OpenSSL/private GSSINCLUDE=/usr/include GSSLIB=/usr/lib PAMDLFLAGS=-lpam"
+
+osx: osxok an
+ $(TOUCH) ip6
+ $(BUILD) BUILDTYPE=$@ IP=$(IP6) EXTRAAUTHENTICATORS="$(EXTRAAUTHENTICATORS) gss" \
+ SPECIALS="SSLINCLUDE=/usr/include/openssl SSLLIB=/usr/lib SSLCERTS=/System/Library/OpenSSL/certs SSLKEYS=/System/Library/OpenSSL/private GSSINCLUDE=/usr/include GSSLIB=/usr/lib"
+
+osxok:
+ @$(SH) -c '(test ! -f /usr/include/pam/pam_appl.h ) || make osxwarn'
+ @$(TOUCH) osxok
+
+osxwarn:
+ @echo You are building for OLD versions of Mac OS X. This build is
+ @echo NOT suitable for modern versions of Mac OS X, such as Tiger,
+ @echo which use PAM-based authentication. If you want to build for
+ @echo modern Mac OS X, you should use make oxp instead.
+ @echo Do you want to continue this build? Type y or n please:
+ @$(SH) -c 'read x; case "$$x" in y) exit 0;; *) exit 1;; esac'
+ @echo OK, I will remember that you really want to build for old
+ @echo Mac OS X. You will not see this message again.
+ @echo If you realize that you really wanted to build for modern
+ @echo Mac OS X, then do the following commands:
+ @echo % rm osxok
+ @echo % make clean
+ @echo % make oxp
+
+
+# Linux shadow password support doesn't build on traditional systems, but most
+# Linux systems are shadow these days.
+
+lnx: lnxnul an
+ $(BUILD) BUILDTYPE=$@
+
+lnxnul:
+ @$(SH) -c '(test $(PASSWDTYPE) = nul) || make lnxok'
+
+lnxok:
+ @echo You are building for traditional Linux. Most modern Linux
+ @echo systems require that you build using make slx.
+ @echo Do you want to continue this build? Type y or n please:
+ @$(SH) -c 'read x; case "$$x" in y) exit 0;; *) exit 1;; esac'
+ @echo OK, I will remember that you really want to build for
+ @echo traditional Linux. You will not see this message again.
+ @echo If you discover that you can not log in to the POP and IMAP
+ @echo servers, then do the following commands:
+ @echo % rm lnxok
+ @echo % make clean
+ @echo % make slx
+ @echo If slx does not work, try sl4 or sl5. Be sure to do a
+ @echo make clean between each try!
+ @$(TOUCH) lnxok
+
+
+# SUN-OS C compiler makes you load libdl by hand...
+
+ssn sun: sunok suntools ua
+ $(BUILD) BUILDTYPE=$@
+
+suntools:
+ $(CD) tools;$(MAKE) LDFLAGS=-ldl
+
+gsu: sunok an
+ $(BUILD) BUILDTYPE=$@
+
+s40: sunok ua
+ $(BUILD) BUILDTYPE=$@
+
+sunok:
+ @echo You are building for the old BSD-based SUN-OS. This is NOT
+ @echo the modern SVR4-based Solaris. If you want to build for
+ @echo Solaris, you should use make gso or make sol or make soc. Do
+ @echo you want to continue this build? Type y or n please:
+ @$(SH) -c 'read x; case "$$x" in y) exit 0;; *) exit 1;; esac'
+ @echo OK, I will remember that you really want to build for the old
+ @echo BSD-based SUN-OS. You will not see this message again.
+ @echo If the build fails and you realize that you really wanted to
+ @echo build for Solaris, then do the following commands:
+ @echo % rm sunok
+ @echo % make clean
+ @echo % make gso
+ @echo If gso does not work, try sol. Be sure to do a make clean
+ @echo between each try!
+ @$(TOUCH) sunok
+
+
+# SVR2 doesn't have symbolic links (at least my SVR2 system doesn't)
+
+sv2:
+ $(MAKE) ua LN=ln
+ $(BUILD) BUILDTYPE=$@ LN=ln
+
+# Hard links don't quite work right in SUA, and there don't seem to be any
+# SSL includes. However, IPv6 works.
+
+sua:
+ $(TOUCH) ip6 sslnone
+ $(MAKE) an LN=cp SSLTYPE=none
+ $(BUILD) BUILDTYPE=$@ LN=cp IP=$(IP6) SSLTYPE=none
+
+
+# Pine port names, not distinguished in c-client
+
+bs2: an
+ $(BUILD) BUILDTYPE=bsi
+
+pt1: an
+ $(BUILD) BUILDTYPE=ptx
+
+
+# Compatibility
+
+hxd:
+ $(BUILD) BUILDTYPE=hpx PASSWDTYPE=dce
+
+# Amiga
+
+ami am2 ama amn:
+ $(MAKE) an LN=cp SYSTEM=amiga
+ $(BUILD) BUILDTYPE=$@ LN=cp
+
+
+# Courtesy entries for Microsoft systems
+
+nt:
+ nmake /nologo /f makefile.nt
+
+ntk:
+ nmake /nologo /f makefile.ntk
+
+w2k:
+ nmake /nologo /f makefile.w2k
+
+wce:
+ nmake /nologo /f makefile.wce
+
+
+# SSL build choices
+
+sslnopwd sslunix.nopwd sslsco.nopwd:
+ @echo +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ @echo + Building in full compliance with RFC 3501 security
+ @echo + requirements:
+ @echo ++ TLS/SSL encryption is supported
+ @echo ++ Unencrypted plaintext passwords are prohibited
+ @echo +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+sslunix sslsco:
+ @echo +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ @echo + Building in PARTIAL compliance with RFC 3501 security
+ @echo + requirements:
+ @echo + Compliant:
+ @echo ++ TLS/SSL encryption is supported
+ @echo + Non-compliant:
+ @echo ++ Unencrypted plaintext passwords are permitted
+ @echo +
+ @echo + In order to rectify this problem, you MUST build with:
+ @echo ++ SSLTYPE=$(SSLTYPE).nopwd
+ @echo +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ @echo
+ @echo Do you want to continue this build anyway? Type y or n please:
+ @$(SH) -c 'read x; case "$$x" in y) exit 0;; *) (make nounenc;exit 1);; esac'
+
+nounenc:
+ @echo +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ @echo + At your request, this build with unencrypted authentication has
+ @echo + been CANCELLED.
+ @echo + You must start over with a new make command.
+ @echo +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+
+sslnone:
+ @echo +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ @echo + Building in NON-COMPLIANCE with RFC 3501 security requirements:
+ @echo + Non-compliant:
+ @echo ++ TLS/SSL encryption is NOT supported
+ @echo ++ Unencrypted plaintext passwords are permitted
+ @echo +
+ @echo + In order to rectify this problem, you MUST build with:
+ @echo ++ SSLTYPE=nopwd
+ @echo + You must also have OpenSSL or equivalent installed.
+ @echo +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ @echo
+ @echo Do you want to continue this build anyway? Type y or n please:
+ @$(SH) -c 'read x; case "$$x" in y) exit 0;; *) (make nonossl;exit 1);; esac'
+
+nonossl:
+ @echo +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ @echo + At your request, this build with no TLS/SSL support has been
+ @echo + CANCELLED.
+ @echo + You must start over with a new make command.
+ @echo +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+
+# IP build choices
+
+ip4:
+ @echo +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ @echo + Building with IPv4 support
+ @echo +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+ip6:
+ @echo +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ @echo + Building with IPv6 support
+ @echo +
+ @echo + NOTE: Some versions of glibc have a bug in the getaddrinfo
+ @echo + call which does DNS name resolution. This bug causes host
+ @echo + names to be canonicalized incorrectly, as well as doing an
+ @echo + unnecessary and performance-sapping reverse DNS call. This
+ @echo + problem does not affect the IPv4 gethostbyname call.
+ @echo +
+ @echo + getaddrinfo works properly on Mac OS X and Windows. However,
+ @echo + the problem has been observed on some Linux systems.
+ @echo +
+ @echo + If you answer n to the following question the build will be
+ @echo + cancelled and you must rebuild. If you did not specify IPv6
+ @echo + yourself, try adding IP6=4 to the make command line.
+ @echo +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ @echo
+ @echo Do you want to build with IPv6 anyway? Type y or n please:
+ @$(SH) -c 'read x; case "$$x" in y) exit 0;; *) (make noip6;exit 1);; esac'
+ @echo OK, I will remember that you really want to build with IPv6.
+ @echo You will not see this message again.
+ @$(TOUCH) ip6
+
+noip6:
+ $(MAKE) clean
+ @echo +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ @echo + At your request, this build with IPv6 has been CANCELLED.
+ @echo + You must start over with a new make command.
+ @echo +
+ @echo + If you wish to rebuild without IPv6 support, do one of the
+ @echo + following:
+ @echo +
+ @echo + 1. If you specified IP=6 on the make command line, omit it.
+ @echo +
+ @echo + 2. Some of the Linux builds automatically select IPv6. If
+ @echo + you choose one of those builds, add IP6=4 to the make command
+ @echo + line. Note that this is IP6=4, not IP=4.
+ @echo +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+# C compiler types
+
+an ua:
+ @$(MAKE) ssl$(SSLTYPE)
+ @echo Applying $@ process to sources...
+ $(TOOLS)/$@ "$(LN)" src/c-client c-client
+ $(TOOLS)/$@ "$(LN)" src/ansilib c-client
+ $(TOOLS)/$@ "$(LN)" src/charset c-client
+ $(TOOLS)/$@ "$(LN)" src/osdep/$(SYSTEM) c-client
+ $(TOOLS)/$@ "$(LN)" src/mtest mtest
+ $(TOOLS)/$@ "$(LN)" src/ipopd ipopd
+ $(TOOLS)/$@ "$(LN)" src/imapd imapd
+ $(TOOLS)/$@ "$(LN)" src/mailutil mailutil
+ $(TOOLS)/$@ "$(LN)" src/mlock mlock
+ $(TOOLS)/$@ "$(LN)" src/dmail dmail
+ $(TOOLS)/$@ "$(LN)" src/tmail tmail
+ $(LN) $(TOOLS)/$@ .
+
+build: OSTYPE rebuild rebuildclean bundled
+
+OSTYPE:
+ @$(MAKE) ip$(IP)
+ @echo Building c-client for $(BUILDTYPE)...
+ @$(TOUCH) SPECIALS
+ echo `$(CAT) SPECIALS` $(EXTRASPECIALS) > c-client/SPECIALS
+ $(CD) c-client;$(MAKE) $(BUILDTYPE) EXTRACFLAGS='$(EXTRACFLAGS)'\
+ EXTRALDFLAGS='$(EXTRALDFLAGS)'\
+ EXTRADRIVERS='$(EXTRADRIVERS)'\
+ EXTRAAUTHENTICATORS='$(EXTRAAUTHENTICATORS)'\
+ PASSWDTYPE=$(PASSWDTYPE) SSLTYPE=$(SSLTYPE) IP=$(IP)\
+ $(SPECIALS) $(EXTRASPECIALS)
+ echo $(BUILDTYPE) > OSTYPE
+ $(TOUCH) rebuild
+
+rebuild:
+ @$(SH) -c '(test $(BUILDTYPE) = rebuild -o $(BUILDTYPE) = `$(CAT) OSTYPE`) || (echo Already built for `$(CAT) OSTYPE` -- you must do \"make clean\" first && exit 1)'
+ @echo Rebuilding c-client for `$(CAT) OSTYPE`...
+ @$(TOUCH) SPECIALS
+ $(CD) c-client;$(MAKE) all CC=`$(CAT) CCTYPE` \
+ CFLAGS="`$(CAT) CFLAGS`" `$(CAT) SPECIALS`
+
+rebuildclean:
+ $(SH) -c '$(RM) rebuild || true'
+
+bundled:
+ @echo Building bundled tools...
+ $(CD) mtest;$(MAKE)
+ $(CD) ipopd;$(MAKE)
+ $(CD) imapd;$(MAKE)
+ $(CD) mailutil;$(MAKE)
+ @$(SH) -c '(test -f /usr/include/sysexits.h ) || make sysexitwarn'
+ $(CD) mlock;$(MAKE) || true
+ $(CD) dmail;$(MAKE) || true
+ $(CD) tmail;$(MAKE) || true
+
+
+sysexitwarn:
+ @echo +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ @echo + Hmm...it does not look like /usr/include/sysexits.h exists.
+ @echo + Either your system is too ancient to have the sysexits.h
+ @echo + include, or your C compiler gets it from some other location
+ @echo + than /usr/include. If your system is too old to have the
+ @echo + sysexits.h include, you will not be able to build the
+ @echo + following programs.
+ @echo +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+clean:
+ @echo Removing old processed sources and binaries...
+ $(SH) -c '$(RM) an ua OSTYPE SPECIALS c-client mtest imapd ipopd mailutil mlock dmail tmail || true'
+ $(CD) tools;$(MAKE) clean
+
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo not war?
diff --git a/imap/NOTICE b/imap/NOTICE
new file mode 100644
index 00000000..a8f5136a
--- /dev/null
+++ b/imap/NOTICE
@@ -0,0 +1,17 @@
+UW IMAP toolkit notices:
+
+This software was developed by the University of Washington
+(http://www.washington.edu/).
+
+The Univerity of Washington IMAP Toolkit (c-client API, dmail, imapd,
+ipop2d, ipop3d, mailutil, mlock, mtest, and tmail software; and its
+included text) is Copyright 1988-2007 by the University of Washington.
+
+The c-client library and mtest software are in part based upon code
+developed by Mark Crispin at Stanford University, and is
+
+ * Copyright 1988 Stanford University and was developed in the
+ * Symbolic Systems Resources Group of the Knowledge Systems Laboratory
+ * at Stanford University in 1987-88, and was funded by the
+ * Biomedical Research Technology Program of the National Institutes of
+ * Health under grant number RR-00785.
diff --git a/imap/README b/imap/README
new file mode 100644
index 00000000..d412d5b6
--- /dev/null
+++ b/imap/README
@@ -0,0 +1,74 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+ IMAP Toolkit Environment
+ 4 April 2007
+ Mark Crispin
+
+
+ UNIX QUICK BUILD NOTES
+
+These quick build notes assume that you have installed OpenSSL before
+attempting to build this software, and that you do not have any non-default
+configuration parameters.
+
+If you need additional information in building this software with OpenSSL,
+please refer to the docs/SSLBUILD file for more information.
+
+If you intend to build this software with a non-default configuration
+(including building a non-compliant server without SSL support), please
+refer to the docs/BUILD file for more information.
+
+1) Look in the top-level Makefile and find your system type code. For example,
+ modern versions of Linux will use either "slx", "lnp", or one of the
+ lnp-variants (such as "lrh").
+
+2) Type "make" followed by the system type, e.g. "make slx".
+
+3) Install the POP2 daemon (ipopd/ipop2d), the POP3 daemon (ipopd/ipop3d), and
+ the IMAP daemon (imapd/imapd) on a system directory of your choosing.
+
+4) Update /etc/services to register the pop2 service on TCP port 109, the
+ pop3 service on TCP port 110, and the imap service on TCP port 143. Also
+ update Yellow Pages/NIS/NetInfo/etc. if appropriate on your system.
+
+5) Update /etc/inetd.conf (or install files on /etc/xinetd.d) to invoke the
+ POP2, POP3, and IMAP daemons on their associated services.
+
+6) If your system uses PAM authentication, be sure to set up /etc/pam.d/imap
+ (*not* /etc/pam.d/imapd) and /etc/pam.d/pop (*not* /etc/pam.d/ipop3d or
+ /etc/pam.d/pop3d or /etc/pam.d/popd or /etc/pam.d/pop3).
+
+7) Unless you built your system without SSL support, you will need to set
+ up SSL server certificates as described in docs/SSLBUILD.
+
+6) That's all!
+
+Read the file docs/BUILD and docs/SSLBUILD if you need more detailed
+information and/or you don't understand these quick build instructions.
+
+ MISCELLANEOUS NOTES
+
+ mtest has been run under UNIX, DOS, Windows, NT, Macintosh, TOPS-20, and
+VMS. It is a very primitive interface, however, and is suited mainly as a
+model of how to write a main program for c-client. You should take a look at
+the source to figure out how to use it. Briefly, it first asks for a mailbox
+name (either a local file path or an IMAP mailbox in the form
+"{hostname}mailbox") and then puts you in a command mode where "?" will give
+you a list of commands.
+
+ Pine is available separately on the FTP.CAC.Washington.EDU archives.
+
+ The focus of development and support is for UNIX and Win32 (including
+Windows 95/98/Millenium, Windows NT, and Windows 2000). The other ports are
+not frequently used or tested, and may be incomplete.
diff --git a/imap/SUPPORT b/imap/SUPPORT
new file mode 100644
index 00000000..562f51e1
--- /dev/null
+++ b/imap/SUPPORT
@@ -0,0 +1,22 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+ BUG REPORTING ADDRESS
+
+ Bug reports, comments, or questions regarding this software may
+be reported to the imap-uw@u.washington.edu mailing list (this replaces
+the old c-client@u.washington.edu mailing list). You can subscribe to
+this list by sending a message to:
+ imap-uw-subscribe@mailman.u.washington.edu
+
+ An alternative is to use the comp.mail.imap newsgroup.
diff --git a/imap/docs/BUILD b/imap/docs/BUILD
new file mode 100644
index 00000000..e094e065
--- /dev/null
+++ b/imap/docs/BUILD
@@ -0,0 +1,491 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+ BUILD AND INSTALLATION NOTES
+ Last Updated: 15 November 2007
+
+Table of Contents:
+1. UNIX Build Notes
+2. UNIX Installation Notes
+3. Win32 Build Notes
+4. Win32 Installation Notes
+5. Inactive Ports (TOPS-20, VMS)
+6. Other ports (Macintosh, DOS/Win16, Windows CE, Amiga, OS/2)
+
+
+ UNIX BUILD NOTES
+
+ The default build on many systems with IPv4 only. To build with IPv6,
+add "IP=6" to the make command line, e.g.
+ make lnp IP=6
+
+ The default build is to build with SSL and disabling plaintext passwords
+unless SSL/TLS encryption is in effect (SSLTYPE=nopwd). This means that
+OpenSSL MUST be installed before building the IMAP toolkit. Please refer to
+the SSLBUILD file for more information.
+
+ To build without SSL, add "SSLTYPE=none" to the make command line.
+Note that doing so will produce an IMAP server which is NON-COMPLIANT with
+RFC 3501.
+
+ You must build through the top-level imap-2007/Makefile, which will run
+a "process" step the first time and create the imap-2007/c-client,
+imap-2007/ipopd, and imap-2007/imapd directories in which building actually
+takes place.
+
+ Before doing a make on UNIX, you should read imap-2007/Makefile and see
+if your system type is known. The various system types are three-letter codes.
+If your system type is known, then use this as the make option. After the
+first time you do a make, this option is remembered in a file called OSTYPE,
+so just typing "make" suffices.
+
+ For example, if you are using a more or less modern Linux system, your
+system type is probably one of the specific distribution types (such as lrh for
+RedHat). For more generic builds, try slx (shadow passwords only) or lnp (PAM).
+To build for RedHat, do:
+ make lrh
+
+ There are other make options, described in imap-2007/src/osdep/Makefile.
+
+ It's probably best to see if an existing port will work on your system
+before inventing a new port. Try:
+ sv4 generic SVR4, non-ANSI compiler
+ a32 modern SVR4
+ bsd basic 4.3 BSD, non-ANSI compiler
+ bsf modern BSD
+
+ If you must invent a new port, you need to create an entry in
+imap-2007/Makefile and imap-2007/src/osdep/Makefile for your new port, as
+well as osdep/os_???.h and osdep/os_???.c files with the appropriate
+OS-dependent support for that system. You also need to determine which setup
+process to use. You should use the ua process unless you are sure that your
+compiler supports *ALL* aspects of ANSI C prototyping. Note that some
+compilers, such as Ultrix, support some aspects of ANSI C but not others;
+c-client really beats on the full prototyping capability of ANSI C so you
+have to use the non-ANSI source tree for such systems.
+
+ If you send a new port back to us, we will make it available for others
+who use your particular system type.
+
+ The mbox driver is now enabled by default. If the file "mbox" exists on
+the user's home directory and is in UNIX mailbox format, then when INBOX is
+opened this file will be selected as INBOX instead of the mail spool file.
+Messages will be automatically transferred from the mail spool file into the
+mbox file. To disable this behavior, delete "mbox" from the EXTRADRIVERS list
+in the top-level Makefile and rebuild.
+
+ WARNING: The SVR2 (sv2) port is *incomplete*. SVR2 does not appear to
+have any way to do ftruncate(), which is needed by the mbox, mbx, mmdf, mtx,
+tenex, and unix drivers.
+
+ UNIX INSTALLATION NOTES
+
+ Binaries from the build are:
+ imap-2007/mtest/mtest c-client testbed program
+ imap-2007/ipopd/ipop2d POP2 daemon
+ imap-2007/ipopd/ipop3d POP3 daemon
+ imap-2007/imapd/imapd IMAP4rev1 daemon
+
+ mtest is normally not used except by c-client developers.
+
+STEP 1: [x]inetd setup
+
+ The ipop2d, ipop3d, and imapd daemons should be installed in a system
+daemon directory and invoked by a listener such as xinetd or inetd. In the
+following examples, /usr/local/etc is used).
+
+STEP 1(A): xinetd-specific setup
+
+ If your system uses xinetd, the daemons are invoked by files in your
+/etc/xinetd.d directory with names corresponding to the service names (that
+is: imap, pop2, pop3). You will need to consult your local xinetd
+documentation to see what should go into these files. Here is a a sample
+/etc/xinetd.d/imap file:
+
+service imap
+{
+ disable = no
+ socket_type = stream
+ wait = no
+ user = root
+ server = /usr/local/etc/imapd
+ groups = yes
+ flags = REUSE IPv6
+}
+
+STEP 1(B): inetd-specific setup
+
+ If your system still uses inetd, the daemons are invoked by your
+/etc/inetd.conf file with lines such as:
+
+pop stream tcp nowait root /usr/local/etc/ipop2d ipop2d
+pop3 stream tcp nowait root /usr/local/etc/ipop3d ipop3d
+imap stream tcp nowait root /usr/local/etc/imapd imapd
+
+ Note that different variants of UNIX have different versions of inetd,
+so you should verify the precise form of these commands (for example, some
+versions of inetd do not require the "nowait").
+
+ IMPORTANT NOTE: inetd has a limit of how many new connections it will
+allow in a certain interval, and when this limit is exceeded, it shuts down
+the server. If you have anything beyond a small-scale server, you are
+probably going to run up against this limit. You'll know when it happens;
+your syslog will give the misleading message "imap/tcp server failing
+(looping), service terminated" and users will complain that IMAP service is
+unavailable for the next 10 minutes. Similarly with "pop3/tcp server
+failing"...
+
+ It must be emphasized that this is *NOT* a bug in the IMAP or POP
+servers, nor is it anything that I can "fix". It is an inetd problem, and
+the only way to fix it is to change inetd's behavior.
+
+ By default, the parameters of this limit are (from inetd.c source code):
+
+#define TOOMANY 40 /* don't start more than TOOMANY */
+#define CNT_INTVL 60 /* servers in CNT_INTVL sec. */
+#define RETRYTIME (60*10) /* retry after bind or server fail */
+
+That is, no more than 40 connections (TOOMANY) in 60 seconds (CNT_INTL), and
+if exceeded, shut down the server for 10 minutes (RETRYTIME). This was a
+good setting in the 1980s ARPAnet, but is much too small today.
+
+ Some versions of inetd allow you to see a higher maximum in the
+/etc/inetd.conf file. Read "man inetd" and see if you see something like
+this in the text:
+
+ The wait/nowait entry is applicable to datagram sockets only [...]
+ [...] The optional ``max'' suffix (separated from
+ ``wait'' or ``nowait'' by a dot) specifies the maximum number of server
+ instances that may be spawned from inetd within an interval of 60 sec-
+ onds. When omitted, ``max'' defaults to 40.
+
+If you see this, then edit the /etc/inetd.conf entry for imapd to be
+something like:
+
+imap stream tcp nowait.100 root /usr/local/etc/imapd imapd
+ (or, if you use TCP wrappers)
+imap stream tcp nowait.100 root /usr/local/etc/tcpd imapd
+
+ Otherwise, you'll need to edit the inetd source code to set TOOMANY to a
+higher value, then rebuild inetd.
+
+
+STEP 2: services setup
+
+ You may also have to edit your /etc/services (or Yellow Pages,
+NetInfo, etc. equivalent) to register these services, such as:
+
+pop 109/tcp
+pop3 110/tcp
+imap 143/tcp
+
+
+STEP 3: PAM setup
+
+ If your system has PAM (Pluggable Authentication Modules -- most
+modern systems do) then you need to set up PAM authenticators for imap and
+pop. The correct file names are
+ /etc/pam.d/imap
+and
+ /etc/pam.d/pop
+
+ It probably works to copy your /etc/pam.d/ftpd file to the above two
+names.
+
+ Many people get these file names wrong, and then spend a lot of time
+trying to figure out why it doesn't work. Common mistakes are:
+ /etc/pam.d/imapd
+ /etc/pam.d/imap4
+ /etc/pam.d/imap4rev1
+ /etc/pam.d/ipop3d
+ /etc/pam.d/pop3d
+ /etc/pam.d/popd
+ /etc/pam.d/pop3
+
+
+STEP 4: optional rimap setup
+
+ If you want to enable the rimap capability, which allows users with a
+suitable client and .rhosts file on the server to access IMAP services
+without transmitting her password in the clear over the network, you need
+to have /etc/rimapd as a link to the real copy of imapd. Assuming you have
+imapd installed on /usr/local/etc as above:
+ % ln -s /usr/local/etc/imapd /etc/rimapd
+
+ Technical note: rimap works by having the client routine tcp_aopen()
+invoke `rsh _host_ exec /etc/rimapd' in an child process, and then returning
+pipes to that process' standard I/O instead of a TCP socket. You can set up
+`e-mail only accounts' by making the shell be something which accepts only
+that string and not ordinary UNIX shell commands.
+
+
+STEP 4: notes on privileges
+
+ Neither user "root", not any other UID 0 account, can log in via IMAP or
+POP. "That's not a bug, it's a feature!"
+
+ This software is designed to run without privileges. The mail spool
+directory must be protected 1777; that is, with world write and the sticky
+bit. Of course, mail *files* should be protected 600!
+
+ An alternative to having the mail spool directory protected 1777, at the
+cost of some performance, is to use the external "mlock" program, available
+as part of the imap-utils package. With mlock installed as /etc/mlock and
+setgid mail, the spool directory can be protected 775 with group mail.
+Please disregard this paragraph if you don't understand it COMPLETELY, and
+know EXACTLY what to do without question.
+
+
+STEP 5: SVR4 specific setup
+
+ There is one "gotcha" on System V Release 4 based systems such as
+Solaris. These systems do not use the standard UNIX mail format, but rather a
+variant of that format that depends upon a bogus "Content-Length:" message
+header. This is widely recognized to have been a terrible mistake. One
+symptom of the problem is that under certain circumstances, a message may get
+broken up into several messages. I'm also aware of security bugs caused by
+programs that foolishly trust "Content-Length:" headers with evil values.
+
+ To fix your system, edit your sendmail.cf to change the Mlocal line to
+have the -E flag. A typical entry will lool like:
+
+Mlocal, P=/usr/lib/mail.local, F=flsSDFMmnPE, S=10, R=20, A=mail.local -d $u
+
+ WIN32 BUILD NOTES
+
+ Visual C++ 6.0 along with the current Microsoft Platform SDK
+(specifically the CORE SDK and the Internet Development SDK) is required
+to build on Windows 9x/Me/NT/2K/XP. If you do not have the Platform SDK
+installed or in your path properly, you'll get errors when building os_nt.c,
+typically in env_nt.c, ssl_nt.c, ssl_w2k.c, or gss_shim.c. You can download
+the Microsoft Platform SDK from Microsoft's web site.
+
+ There is also considerable debate about how new mail is to be snarfed.
+I am currently using something that seems to work with WinSMTP. Look at
+the definition of MAILFILE in imap-2007/src/osdep/nt/mailfile.h and at the
+sysinbox() function in imap-2007/src/osdep/nt/env_nt.c to see what's there
+now, so you have a clue about how to hack it.
+
+ To build under Windows 95/98/NT, connect to the imap-2007 directory
+and do:
+ nmake -f makefile.nt
+The resulting binaries will support SSL if either schannel.dll or
+security.dll is installed in Windows, using the old, undocumented, SSL
+interfaces. You can also use this to build under Me/2000/XP, but it is
+not the preferred build on this platform.
+
+ To build with MIT Kerberos support, connect to the imap-2007 directory
+and do:
+ nmake -f makefile.ntk
+The resulting binaries will support SSL if either schannel.dll or
+security.dll is installed in Windows, using the old, undocumented SSL
+interfaces. They will also support MIT Kerberos. Note, however, that
+these binaries will only run on systems which have the MIT Kerberos DLLs
+installed, and will not run otherwise.
+
+ To build under Windows Me/2000/XP, connect to the imap-2007 directory
+and do:
+ nmake -f makefile.w2k
+The resulting binaries will support SSL and Microsoft Kerberos, using the
+official, documented, Microsoft interfaces. Note, however, that these
+binaries will not run under Windows 95, Windows 98, or Windows NT4.
+
+ WIN32 INSTALLATION NOTES
+
+ The resulting binaries will be:
+ imap-2007\mtest\mtest.exe (testbed client)
+ imap-2007\ipopd\ipop2d.exe POP2 server
+ imap-2007\ipopd\ipop3d.exe POP3 server
+ imap-2007\imapd\imapd.exe IMAP4rev1 server
+
+ These servers are stdio servers. I wrote a simple network listener
+for NT called inetlisn; currently it is available as:
+ ftp://ftp.cac.washington.edu/mail/nt/inetlisn.tar
+To build this, use "nmake" after connecting to the inetlisn directory.
+inetlisn takes two arguments, the first being the port number and the second
+being the binary to run to serve a connection on that port, e.g.
+ c:\bin\inetlisn 143 c:\mail_daemons\imapd
+
+ Note that NT imapd must be started as SYSTEM in order to be recognized as
+being "not logged in"; otherwise it will preauth as whatever user it is
+running as which is probably not what you want. One way to have it run as
+system is to have inetlisn run by an AT command, e.g. if the time now is
+2:05PM, try something like:
+ AT 14:06 "c:\bin\inetlisn 143 c:\mail_daemons\imapd"
+
+ A more advanced network listener called wsinetd is available on:
+ http://wsinetd.sourceforge.net
+It is based on inetlisn, and essentially is a "completed" version of inetlisn.
+
+ Bottom line: this is not plug-and-play. If you're not a hacker and/or
+are unwilling to invest the time to do some programming, you probably want to
+buy a commercial server product.
+
+ INACTIVE PORTS
+
+ The TOPS-20 and VMS ports were developed at one time or another, but are
+no longer actively developed. However, from time to time I test build both
+of these to make sure that they compile without errors and that mtest runs,
+and will continue doing so as long as I have access to systems running these
+operating systems.
+
+
+ TOPS-20 BUILD NOTES
+
+ I have provided a c-client port for TOPS-20 systems, but you're on your
+own in terms of a nice TOPS-20 like main program. Maybe someday some nice
+person will try porting Pine to TOPS-20. This assumes the use of KCC 6, and
+probably will not build with other compilers or older versions of KCC.
+
+ You do not use imap-2007/Makefile under TOPS-20, nor do you build any
+components other than c-client and mtest. Merge the contents of
+imap-2007/src/c-client, imap-2007/src/charset, imap-2007/src/mtest, and
+imap-2007/src/osdep/tops-20 onto a single directory on TOPS-20 and build from
+that. The command:
+ DO BUILD.CTL
+will build the sources. If you don't have MIC, then SUBMIT BUILD.CTL and let
+BATCON execute it.
+
+
+ VMS BUILD NOTES
+
+ The VMS port has been tested with imap-2007, but as I am soon going
+to lose access to a VMS system I will no longer be able able to test and
+this port will be moved to the "other ports" category".
+
+ You do not use imap-2007/Makefile under VMS, nor do you build any
+components other than c-client and mtest. Merge the contents of
+imap-2007/src/c-client, imap-2007/src/charset, imap-2007/src/mtest, and
+imap-2007/src/osdep/vms onto a single directory on VMS and build from that.
+The command to build it is:
+ @BUILD MULTINET
+or @BUILD NETLIB
+If you just do @BUILD it will build with dummy TCP code, and since only TCP
+based drivers are provided here this isn't too useful.
+
+ If you aren't on the Pacific coast of the US or Canada, you probably will
+need to change the wired-in timezone in the BUILD.COM file. Apparently, the
+wonderful VMS system that DEC loves so much doesn't maintain any concept of
+time zone; the VMS C compiler returns a null pointer from gmtime()!
+
+ Otherwise you're pretty much on your own here.
+
+ OTHER PORTS
+
+ The following ports were developed at one time or another, but are no
+longer actively developed or tested. It is not known if they still work or
+not.
+
+ Port Status
+ ---- ------
+Macintosh Obsolete; Mac OS X uses UNIX port
+DOS/Win16 Obsolete; modern PCs use Win32 port
+Windows CE Never completed
+Amiga Unknown
+OS/2 Unknown
+
+ MACINTOSH BUILD NOTES
+
+ This port is for the old Mac OS system, not Mac OS X.
+
+ If you are building a Macintosh client, you will need MacTCP installed on
+your system as well as the MacTCP C includes and libraries.
+
+ You do not use imap-2007/Makefile on the Mac, nor do you build any
+components other than c-client and mtest. Merge the contents of
+imap-2007/src/c-client, imap-2007/src/charset, imap-2007/src/mtest, and
+imap-2007/src/osdep/mac onto a single directory on the Mac and build from
+that. mtext.sit.hqx is a THINK C project file and cute icon for building
+mtest, encoded with Binhex and StuffIt.
+
+ THINK C is a truly wretched product which help make me understand why
+Macintosh has lost most of its market share. Not only does it do cretinous
+things such as barf about a cast in front of an lvalue, it also limits the size
+of code *or* data in a single file to 32K! So much for having large character
+set tables. Symantec says that "MacOS requires it, break up your files into
+smaller pieces" yet somehow gcc under MachTen contrives to compile C programs
+without subjecting the programmer to this idiocy.
+
+ As a result of this, I found myself obliged to comment out the #includes
+of the East Asian character sets in utf8.c in order to get it to build. It's
+also necessary to break up some of the files, at least mail.c and imap4r1.c.
+Maybe you don't have to do this in CodeWarrior or whatever the new compiler is
+called, but I've pretty much given up on Macintosh.
+
+ If you use precompiled headers, you may get some compilation errors since
+some Apple symbols need to be redefined in order to get it to build under all
+versions of MacOS. Try turning off the precompiled headers (so it will
+re-read the .h files) and see if it builds any better.
+
+ If you use a Mac C compiler with 2-byte ints (such as THINK C's normal
+mode) you will need to fix some bugs in the MacTCP C includes and libraries to
+prevent it from generating bad code, since those MacTCP files violate Apple's
+standards of always using explicit shorts or longs, never ints. You could
+avoid this if you set 4-byte ints in THINK C; however, the ANSI and UNIX
+libraries in THINK C use 2-byte ints so you will also need to build 4-byte int
+versions of these. c-client itself is 2-byte int or 4-byte int clean; it can
+be used in either mode.
+
+ The most important bug in the MacTCP files that you need to fix is in the
+file AddressXlation.h, you need to change the definition of the rtnCode member
+of the hostInfo structure to be long instead of int. There are several other
+changes you need to make if you decide to compile dnr.c under THINK C instead
+of using the Apple-supplied object file; see me for details if you decide to
+undertake such an effort. This is fixed in newer versions from Apple.
+
+
+ DOS/WIN16 BUILD NOTES
+
+ If you are building a DOS client, you will need a TCP/IP stack installed
+on your DOS system along with its development environment. The currently
+supported stacks are Beame & Whiteside, PC-NFS, Novell, PC/IP, Waterloo, and
+Winsock. mtest and a version of Pine called PC Pine run under DOS.
+
+ You do not use imap-2007/Makefile under DOS, nor do you build any
+components other than c-client and mtest. Merge the contents of
+imap-2007/src/c-client, imap-2007/src/charset, imap-2007/src/mtest, and
+imap-2007/src/osdep/dos onto a single directory on DOS and build from that.
+The MAKE command on DOS takes an argument identifying the TCP/IP stack in use.
+For example, do:
+ MAKE MAKEFILE OS=WSK (or MAKE -F MAKEFILE OS=WSK)
+to build for Winsock.
+
+ If you write a program for DOS/Win16, you will probably have to write a
+replacement cache manager (look at mm_cache()) and otherwise disable most of
+c-client's caching. Even so, memory limitations will be an ongoing problem,
+particularly with DOS, and you will have some severe performance problems.
+It's a bit better on Win16, but in my opinion you are better off writing a
+32-bit program and telling your Win16 customers to upgrade to Windows 95 or at
+least install Win32s.
+
+
+ WINDOWS CE BUILD NOTES
+
+ I build using Visual C++ 6.0 with the WCE extensions. The current code
+has SH3 wired in for the compiler building.
+
+ To build under NT, connect to the imap-2007 directory and do:
+ nmake -f makefile.wce
+
+ The only binary produced is a cclient.lib file. I haven't gotten as far
+as building mtest on WCE, mainly because I don't have a stdlib library.
+
+
+ AMIGA BUILD AND INSTALLATION NOTES
+
+ The Amiga port was contributed. Maybe the UNIX notes will help.
+
+
+ OS2 BUILD NOTES
+
+ The OS2 port was contributed. Maybe the Win32 Build Notes will help.
diff --git a/imap/docs/CONFIG b/imap/docs/CONFIG
new file mode 100644
index 00000000..01ae71f9
--- /dev/null
+++ b/imap/docs/CONFIG
@@ -0,0 +1,181 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+ UNIX Configuration Notes
+
+ The IMAP and POP3 servers are plug-and-play on standard UNIX
+systems. There is no special configuration needed. Please ignore all
+rumors to the effect that you need to create an IMAP configuration
+file.
+
+ If your system is non-standard, virtually everything that you are
+likely to want to modify can be found in the source file
+ .../src/osdep/unix/env_unix.c
+In particular, special attention should be given to the routines:
+ env_init() initialize c-client environment variables,
+ especially the user name and home directory
+ sysinbox() return the UNIX path of the INBOX in which
+ mail delivery will place mail
+ mailboxdir() translate a mailbox name into the associated
+ UNIX directory for listing
+ mailboxfile() translate a mailbox name into the associated
+ UNIX file for opening
+
+ There are also build options in the top-level makefile which you
+can give on the command line when building the software. The most
+common build options are "SSLTYPE=unix", to build the software with SSL,
+and "SSLTYPE=nopwd", to build the software with SSL and disable plaintext
+authentication unless the session is encrypted.
+
+ You should modify these routines as necessary for local policy.
+The most common modifications are to env_init(), to modify the
+software's idea of the home directory (which is used everywhere as the
+default directory), and to sysinbox(), to modify where the software
+looks for newly-delivered mail.
+
+ Example 1: suppose your mailer delivers mail to file ".mailbox"
+in the user's home directory instead of the default UNIX mail spool
+directory. You will want to change routine sysinbox(), changing the
+line that reads:
+
+ sprintf (tmp,"%s/%s",MAILSPOOL,myusername ());
+to be:
+ sprintf (tmp,"%s/.mailbox",myhomedir ());
+
+ Example 2: suppose you want to change c-client's idea of the
+user's mailbox directory to be the "mail" subdirectory of the user's
+home directory instead of the user's home directory. You will want to
+change variable mailsubdir, changing the line that reads:
+
+static char *mailsubdir = NIL; /* mail subdirectory name */
+ to be:
+static char *mailsubdir = "mail";/* mail subdirectory name */
+
+ Example 3: suppose you want to disable plaintext authentication in
+the IMAP and POP servers. If you want to disable plaintext authentication
+in unencrypted sessions but permit it in encrypted sessions, you should use
+"SSLTYPE=nopwd" in the make command line when building the software. For
+example, to do this on a Linux system with PAM authentication, do:
+ make lnp SSLTYPE=nopwd
+If you want to disable plaintext authentication under all circumstances
+(including SSL or TLS encrypted sessions), use "PASSWDTYPE=nul", e.g.:
+ make lnx EXTRAAUTHENTICATORS=gss PASSWDTYPE=nul
+which will make it impossible to log in except via Kerberos.
+
+ Example 4: suppose you want the IMAP and POP servers to do a chroot()
+to the user's home directory. This is not recommended; there are known
+ways of attacking chroot() based security mechanisms. Furthermore, if you
+do this you can not use a traditional UNIX format INBOX in the mail spool
+directory, since chroot() will prevent access to that directory. If you
+really want to do this, you need to change variable closedBox, changing
+the line which reads:
+
+static short closedBox = NIL; /* is a closed box */
+ to be:
+static short closedBox = T; /* is a closed box */
+
+ Example 5: suppose you want to disable non-namespace access to the
+filesystem root and other users' names, but do not want to go to the
+extreme of chroot() and you want to allow access to a traditional UNIX
+format INBOX in the mail spool directory. You need to change variable
+restrictBox, changing the line which reads:
+
+static short restrictBox = NIL; /* is a restricted box */
+ to be:
+static short restrictBox = -1; /* is a restricted box */
+
+Other values to set in restrictBox can be found in env_unix.h.
+
+ Ignore all references in env_unix.c to a configuration file; that
+code is for UW-internal use only. It is extremely unlikely that that
+facility will work usefully for you; it is extremely likely that you
+will shoot yourself in the foot by using; and it frequently changes in
+an incompatible manner.
+
+ There are two other build-time configuration issues which you may
+need to consider: drivers and authenticators. Both of these are set
+up in the top-level Makefile -- in particular, by the EXTRADRIVERS and
+EXTRAAUTHENTICATORS variables.
+
+ Drivers are code modules that support different mailbox storage
+technologies. By default, all drivers are enabled. There is little
+benefit to be gained by disabling a driver, with one exception. The
+mbox driver implements the behavior of automatically moving new mail
+from the spool directory to the "mbox" file on the user's home
+directory, if and *only* if the "mbox" exists and is in mailbox
+format. The mbox driver is listed under EXTRADRIVERS; if you wish to
+disable it just remove it from that list and rebuild.
+
+ Authenticators are code modules that support authentication
+technology for the server (password file lookup, Kerberos, S/Key,
+etc.). EXTRAAUTHENTICATORS is used to add an authenticator. This
+subject can be complex; find a wizard if you can't figure it out.
+
+ It is also possible to add your own drivers and authenticators.
+This is a topic for wizards, and is beyond the scope of this text.
+
+ NT Configuration Notes
+
+ This software is not plug-and-play on NT. If you're not a hacker
+and/or are unwilling to invest the time to do some programming, you
+probably want to buy a commercial server for NT.
+
+ The primary issue that you need to deal with is the format of
+mail, where the INBOX is located, and where secondary folders are
+located. As distributed, the software supports mail in the default
+format used on UNIX (unix format) as well as mbx, mtx, and tenex
+formats. mbx format is encouraged if at all possible; mtx and tenex
+format are for compatibility with the past. However, it all depends
+upon how and where your SMTP server delivers mail.
+
+ To change the default mailbox format, edit the symbol
+DEFAULTDRIVER in:
+ ../src/osdep/nt/makefile.nt
+or
+ ../src/osdep/nt/makefile.ntk
+To change the default location of INBOX, edit the file:
+ ../src/osdep/nt/mailfile.h
+Virtually everything else having to do with environment that you are
+likely to want to modify can be found in the source file:
+ .../src/osdep/nt/env_nt.c
+In particular, special attention should be given to the routines:
+ env_init() initialize c-client environment variables,
+ especially the user name and home directory
+ sysinbox() return the NT path of the INBOX in which
+ mail delivery will place mail
+ mailboxdir() translate a mailbox name into the associated
+ NT directory for listing
+ mailboxfile() translate a mailbox name into the associated
+ NT file for opening
+
+ You should modify these routines as necessary. The most common
+modifications are to env_init(), to modify the software's idea of the
+home directory (which is used everywhere as the default directory),
+and to sysinbox(), to modify where the software looks for
+newly-delivered mail.
+
+ There are two other build-time configuration issues which you may
+need to consider: drivers and authenticators. Both of these are set
+up in the top-level Makefile -- in particular, by the EXTRADRIVERS and
+EXTRAAUTHENTICATORS variables.
+
+ Drivers are code modules that support different mailbox storage
+technologies. By default, all drivers are enabled. There is little
+benefit to be gained by disabling a driver.
+
+ Authenticators are code modules that support authentication
+technology for the server (password file lookup, Kerberos, S/Key,
+etc.). EXTRAAUTHENTICATORS is used to add an authenticator. This
+subject can be complex; find a wizard if you can't figure it out.
+
+ It is also possible to add your own drivers and authenticators.
diff --git a/imap/docs/FAQ.html b/imap/docs/FAQ.html
new file mode 100644
index 00000000..12a9feac
--- /dev/null
+++ b/imap/docs/FAQ.html
@@ -0,0 +1,4226 @@
+<!--
+ * ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ *
+-->
+
+<!--chtml set title="IMAP Toolkit Frequently Asked Questions"-->
+<!--chtml include "//imap/incs/top.inc"-->
+
+ <h2>Table of Contents</h2>
+
+ <ul>
+ <li>
+ <a href="#general">1. General/Software Feature Questions</a>
+
+ <ul>
+ <li><a href="#1.1">1.1 Can I set up a POP or IMAP server on
+ UNIX/Linux/OSF/etc.?</a></li>
+
+ <li><a href="#1.2">1.2 I am currently using qpopper as my POP3 server
+ on UNIX. Do I need to replace it with ipop3d in order to run
+ imapd?</a></li>
+
+ <li><a href="#1.3">1.3 Can I set up a POP or IMAP server on Windows
+ XP, 2000, NT, Me, 98, or 95?</a></li>
+
+ <li><a href="#1.4">1.4 Can I set up a POP or IMAP server on Windows
+ 3.1 or DOS?</a></li>
+
+ <li><a href="#1.5">1.5 Can I set up a POP or IMAP server on
+ Macintosh?</a></li>
+
+ <li><a href="#1.6">1.6 Can I set up a POP or IMAP server on
+ VAX/VMS?</a></li>
+
+ <li><a href="#1.7">1.7 Can I set up a POP or IMAP server on
+ TOPS-20?</a></li>
+
+ <li><a href="#1.8">1.8 Are hierarchical mailboxes supported?</a></li>
+
+ <li><a href="#1.9">1.9 Are "dual-use" mailboxes supported?</a></li>
+
+ <li><a href="#1.10">1.10 Can I have a mailbox that has both messages
+ and sub-mailboxes?</a></li>
+
+ <li><a href="#1.11">1.11 What is the difference between "mailbox" and
+ "folder"?</a></li>
+
+ <li><a href="#1.12">1.12 What is the status of
+ internationalization?</a></li>
+
+ <li><a href="#1.13">1.13 Can I use SSL?</a></li>
+
+ <li><a href="#1.14">1.14 Can I use TLS and the STARTTLS
+ facility?</a></li>
+
+ <li><a href="#1.15">1.15 Can I use CRAM-MD5 authentication?</a></li>
+
+ <li><a href="#1.16">1.16 Can I use APOP authentication?</a></li>
+
+ <li><a href="#1.17">1.17 Can I use Kerberos V5?</a></li>
+
+ <li><a href="#1.18">1.18 Can I use PAM for plaintext
+ passwords?</a></li>
+
+ <li><a href="#1.19">1.19 Can I use Kerberos 5 for plaintext
+ passwords?</a></li>
+
+ <li><a href="#1.20">1.20 Can I use AFS for plaintext
+ passwords?</a></li>
+
+ <li><a href="#1.21">1.21 Can I use DCE for plaintext
+ passwords?</a></li>
+
+ <li><a href="#1.22">1.22 Can I use the CRAM-MD5 database for
+ plaintext passwords?</a></li>
+
+ <li><a href="#1.23">1.23 Can I disable plaintext passwords?</a></li>
+
+ <li><a href="#1.24">1.24 Can I disable plaintext passwords on
+ unencrypted sessions, but allow them on encrypted sessions?</a></li>
+
+ <li><a href="#1.25">1.25 Can I use virtual hosts?</a></li>
+
+ <li><a href="#1.26">1.26 Can I use RPOP authentication?</a></li>
+
+ <li><a href="#1.27">1.27 Can I use Kerberos V4?</a></li>
+
+ <li><a href="#1.28">1.28 Is there support for S/Key or OTP?</a></li>
+
+ <li><a href="#1.29">1.29 Is there support for NTLM or SPA?</a></li>
+
+ <li><a href="#1.30">1.30 Is there support for mh?</a></li>
+
+ <li><a href="#1.31">1.31 Is there support for qmail and the maildir
+ format?</a></li>
+
+ <li><a href="#1.32">1.32 Is there support for the Cyrus mailbox
+ format?</a></li>
+
+ <li><a href="#1.33">1.33 Is this software Y2K compliant?</a></li>
+ </ul>
+ </li>
+
+ <li>
+ <a href="#requirements">2. What Do I Need to Build This Software?</a>
+
+ <ul>
+ <li><a href="#2.1">2.1 What do I need to build this software with SSL
+ on UNIX?</a></li>
+
+ <li><a href="#2.2">2.2 What do I need to build this software with
+ Kerberos V on UNIX?</a></li>
+
+ <li><a href="#2.3">2.3 What do I need to use a C++ compiler with this
+ software to build my own application?</a></li>
+
+ <li><a href="#2.4">2.4 What do I need to build this software on
+ Windows?</a></li>
+
+ <li><a href="#2.5">2.5 What do I need to build this software on
+ DOS?</a></li>
+
+ <li><a href="#2.6">2.6 Can't I use Borland C to build this software
+ on the PC?</a></li>
+
+ <li><a href="#2.7">2.7 What do I need to build this software on the
+ Mac?</a></li>
+
+ <li><a href="#2.8">2.8 What do I need to build this software on
+ VMS?</a></li>
+
+ <li><a href="#2.9">2.9 What do I need to build this software on
+ TOPS-20?</a></li>
+
+ <li><a href="#2.10">2.10 What do I need to build this software on
+ Amiga or OS/2?</a></li>
+
+ <li><a href="#2.11">2.11 What do I need to build this software on
+ Windows CE?</a></li>
+ </ul>
+ </li>
+
+ <li>
+ <a href="#build">3. Build and Configuration Questions</a>
+
+ <ul>
+ <li><a href="#3.1">3.1 How do I configure the IMAP and POP servers on
+ UNIX?</a></li>
+
+ <li><a href="#3.2">3.2 I built and installed the servers according to
+ the BUILD instructions. It can't be that easy. Don't I need to write
+ a config file?</a></li>
+
+ <li><a href="#3.3">3.3 How do I make the IMAP and POP servers look
+ for INBOX at some place other than the mail spool directory?</a></li>
+
+ <li><a href="#3.4">3.4 How do I make the IMAP server look for
+ secondary folders at some place other than the user's home
+ directory?</a></li>
+
+ <li><a href="#3.5">3.5 How do I configure SSL?</a></li>
+
+ <li><a href="#3.6">3.6 How do I configure TLS and the STARTTLS
+ facility?</a></li>
+
+ <li><a href="#3.7">3.7 How do I build/install OpenSSL and
+ obtain/create certificates for use with SSL?</a></li>
+
+ <li><a href="#3.8">3.8 How do I configure CRAM-MD5
+ authentication?</a></li>
+
+ <li><a href="#3.9">3.9 How do I configure APOP
+ authentication?</a></li>
+
+ <li><a href="#3.10">3.10 How do I configure Kerberos V5?</a></li>
+
+ <li><a href="#3.11">3.11 How do I configure PAM for plaintext
+ passwords?</a></li>
+
+ <li><a href="#3.12">3.12 It looks like all I have to do to make the
+ server use Kerberos is to build with PAM on my Linux system, and set
+ it up in PAM for Kerberos passwords. Right?</a></li>
+
+ <li><a href="#3.13">3.13 How do I configure Kerberos 5 for plaintext
+ passwords?</a></li>
+
+ <li><a href="#3.14">3.14 How do I configure AFS for plaintext
+ passwords?</a></li>
+
+ <li><a href="#3.15">3.15 How do I configure DCE for plaintext
+ passwords?</a></li>
+
+ <li><a href="#3.16">3.16 How do I configure the CRAM-MD5 database for
+ plaintext passwords?</a></li>
+
+ <li><a href="#3.17">3.17 How do I disable plaintext
+ passwords?</a></li>
+
+ <li><a href="#3.18">3.18 How do I disable plaintext passwords on
+ unencrypted sessions, but allow them in SSL or TLS sessions?</a></li>
+
+ <li><a href="#3.19">3.19 How do I configure virtual hosts?</a></li>
+
+ <li>
+ <a href="#3.20">3.20 Why do I get compiler warning messages such
+ as:
+
+ <ul>
+ <li>passing arg 3 of `scandir' from incompatible pointer
+ type</li>
+
+ <li>Pointers are not assignment-compatible.</li>
+
+ <li>Argument #4 is not the correct type.</li>
+ </ul>during the build?</a>
+ </li>
+
+ <li>
+ <a href="#3.21">3.21 Why do I get compiler warning messages such
+ as
+
+ <ul>
+ <li>Operation between types "void(*)(int)" and "void*" is not
+ allowed.</li>
+
+ <li>Function argument assignment between types "void*" and
+ "void(*)(int)" is not allowed.</li>
+
+ <li>Pointers are not assignment-compatible.</li>
+
+ <li>Argument #5 is not the correct type.</li>
+ </ul>during the build?</a>
+ </li>
+
+ <li>
+ <a href="#3.22">3.22 Why do I get linker warning messages such
+ as:
+
+ <ul>
+ <li>mtest.c:515: the `gets' function is dangerous and should not
+ be used.</li>
+ </ul>during the build? Isn't this a security bug?</a>
+ </li>
+
+ <li>
+ <a href="#3.23">3.23 Why do I get linker warning messages such
+ as:</a>
+
+ <ul>
+ <li>auth_ssl.c:92: the `tmpnam' function is dangerous and should
+ not be used.</li>
+ </ul>during the build? Isn't this a security bug?
+ </li>
+
+ <li><a href="#3.24">3.24 OK, suppose I see a warning message about a
+ function being "dangerous and should not be used" for something other
+ than this gets() or tmpnam() call?</a></li>
+ </ul>
+ </li>
+
+ <li>
+ <a href="#operation">4. Operational Questions</a>
+
+ <ul>
+ <li><a href="#4.1">4.1 How can I enable anonymous IMAP
+ logins?</a></li>
+
+ <li><a href="#4.2">4.2 How do I set up an alert message that each
+ IMAP user will see?</a></li>
+
+ <li><a href="#4.3">4.3 How does the c-client library choose which of
+ its several mechanisms to use to establish an IMAP connection to the
+ server? I noticed that it can connect on port 143, port 993, via rsh,
+ and via ssh.</a></li>
+
+ <li><a href="#4.4">4.4 I am using a TLS-capable IMAP server, so I
+ don't need to use /ssl to get encryption. However, I want to be
+ certain that my session is TLS encrypted before I send my password.
+ How to I do this?</a></li>
+
+ <li><a href="#4.5">4.5 How do I use one of the alternative formats
+ described in the formats.txt document? In particular, I hear that mbx
+ format will give me better performance and allow shared
+ access.</a></li>
+
+ <li><a href="#4.6">4.6 How do I set up shared mailboxes?</a></li>
+
+ <li><a href="#4.7">4.7 How can I make the server syslogs go to
+ someplace other than the mail syslog?</a></li>
+ </ul>
+ </li>
+
+ <li>
+ <a href="#security">5. Security Questions</a>
+
+ <ul>
+ <li><a href="#5.1">5.1 I see that the IMAP server allows access to
+ arbitary files on the system, including /etc/passwd! How do I disable
+ this?</a></li>
+
+ <li><a href="#5.2">5.2 I've heard that IMAP servers are insecure. Is
+ this true?</a></li>
+
+ <li><a href="#5.3">5.3 How do I know that I have the most secure
+ version of the server?</a></li>
+
+ <li><a href="#5.4">5.4 I see all these strcpy() and sprintf() calls,
+ those are unsafe, aren't they?</a></li>
+
+ <li><a href="#5.5">5.5 Those /tmp lock files are protected 666, is
+ that really right?</a></li>
+ </ul>
+ </li>
+
+ <li>
+ <a href="#strange">6. <i>Why Did You Do This Strange Thing?</i>
+ Questions</a>
+
+ <ul>
+ <li><a href="#6.1">6.1 Why don't you use GNU autoconfig / automake /
+ autoblurdybloop?</a></li>
+
+ <li><a href="#6.2">6.2 Why do you insist upon a build with -g?
+ Doesn't it waste disk and memory space?</a></li>
+
+ <li><a href="#6.3">6.3 Why don't you make c-client a shared
+ library?</a></li>
+
+ <li><a href="#6.4">6.4 Why don't you use iconv() for
+ internationalization support?</a></li>
+
+ <li><a href="#6.5">6.5 Why is the IMAP server connected to the home
+ directory by default?</a></li>
+
+ <li><a href="#6.6">6.6 I have a Windows system. Why isn't the server
+ plug and play for me?</a></li>
+
+ <li><a href="#6.7">6.7 I looked at the UNIX SSL code and saw that you
+ have the SSL data payload size set to 8192 bytes. SSL allows 16K; why
+ aren't you using the full size?</a></li>
+
+ <li><a href="#6.8">6.8 Why is an mh format INBOX called #mhinbox
+ instead of just INBOX?</a></li>
+
+ <li><a href="#6.9">6.9 Why don't you support the maildir
+ format?</a></li>
+
+ <li><a href="#6.10">6.10 Why don't you support the Cyrus
+ format?</a></li>
+
+ <li><a href="#6.11">6.11 Why is it creating extra forks on my SVR4
+ system?</a></li>
+
+ <li><a href="#6.12">6.12 Why are you so fussy about the date/time
+ format in the internal <code>"From&nbsp;"</code> line in traditional
+ UNIX mailbox files? My other mail program just considers every line
+ that starts with <code>"From&nbsp;"</code> to be the start of the
+ message.</a></li>
+
+ <li><a href="#6.13">6.13 Why is traditional UNIX format the default
+ format?</a></li>
+
+ <li><a href="#6.14">6.14 Why do you write this "DON'T DELETE THIS
+ MESSAGE -- FOLDER INTERNAL DATA" message at the start of traditional
+ UNIX and MMDF format mailboxes?</a></li>
+
+ <li><a href="#6.15">6.15 Why don't you stash the mailbox metadata in
+ the first real message of the mailbox instead of writing this fake
+ FOLDER INTERNAL DATA message?</a></li>
+
+ <li><a href="#6.16">6.16 Why aren't "dual-use" mailboxes the
+ default?</a></li>
+
+ <li><a href="#6.17">6.17 Why do you use ucbcc to build on
+ Solaris?</a></li>
+
+ <li><a href="#6.18">6.18 Why should I care about some old system with
+ BSD libraries? cc is the right thing on my Solaris system!</a></li>
+
+ <li><a href="#6.19">6.19 Why do you insist upon writing .lock files
+ in the spool directory?</a></li>
+
+ <li><a href="#6.20">6.20 Why should I care about compatibility with
+ the past?</a></li>
+ </ul>
+ </li>
+
+ <li>
+ <a href="#problems">7. Problems and Annoyances</a>
+
+ <ul>
+ <li><a href="#7.1">7.1 Help! My INBOX is empty! What happened to my
+ messages?</a></li>
+
+ <li><a href="#7.2">7.2 Help! All my messages in a non-INBOX mailbox
+ have been concatenated into one message which claims to be from me
+ and has a subject of the file name of the mailbox! What's going
+ on?</a></li>
+
+ <li>
+ <a href="#7.3">7.3 Why do I get the message:
+
+ <ul>
+ <li>CREATE failed: Can't create mailbox node xxxxxxxxx: File
+ exists</li>
+ </ul>and how do I fix it?</a>
+ </li>
+
+ <li><a href="#7.4">7.4 Why can't I log in to the server? The user
+ name and password are right!</a></li>
+
+ <li><a href="#7.5">7.5 Help! My load average is soaring and I see
+ hundreds of POP and IMAP servers, many logged in as the same
+ user!</a></li>
+
+ <li><a href="#7.6">7.6 Why does mail disappear even though I set
+ "keep mail on server"?</a></li>
+
+ <li>
+ <a href="#7.7">7.7 Why do I get the message
+
+ <ul>
+ <li>Moved ##### bytes of new mail to /home/user/mbox from
+ /var/spool/mail/user</li>
+ </ul>and why did this happen?</a>
+ </li>
+
+ <li><a href="#7.8">7.8 Why isn't it showing the local host name as a
+ fully-qualified domain name?</a></li>
+
+ <li><a href="#7.9">7.9 Why is the local host name in the
+ From/Sender/Message-ID headers of outgoing mail not coming out as a
+ fully-qualified domain name?</a></li>
+
+ <li>
+ <a href="#7.10">7.10 What does the message:
+
+ <ul>
+ <li>Mailbox vulnerable - directory /var/spool/mail must have 1777
+ protection</li>
+ </ul>mean? How can I fix this?</a>
+ </li>
+
+ <li>
+ <a href="#7.11">7.11 What does the message:
+
+ <ul>
+ <li>Mailbox is open by another process, access is readonly</li>
+ </ul>mean? How do I fix this?</a>
+ </li>
+
+ <li>
+ <a href="#7.12">7.12 What does the message:
+
+ <ul>
+ <li>Can't get write access to mailbox, access is readonly</li>
+ </ul>mean?</a>
+ </li>
+
+ <li><a href="#7.13">7.13 I set my POP3 client to "delete messages
+ from server" but they never get deleted. What is wrong?</a></li>
+
+ <li>
+ <a href="#7.14">7.14 What do messages such as:
+
+ <ul>
+ <li>Message ... UID ... already has UID ...</li>
+
+ <li>Message ... UID ... less than ...</li>
+
+ <li>Message ... UID ... greater than last ...</li>
+
+ <li>Invalid UID ... in message ..., rebuilding UIDs</li>
+ </ul>mean?</a>
+ </li>
+
+ <li>
+ <a href="#7.15">7.15 What do the error messages:
+
+ <ul>
+ <li>Unable to read internal header at ...</li>
+
+ <li>Unable to find CRLF at ...</li>
+
+ <li>Unable to parse internal header at ...</li>
+
+ <li>Unable to parse message date at ...</li>
+
+ <li>Unable to parse message flags at ...</li>
+
+ <li>Unable to parse message UID at ...</li>
+
+ <li>Unable to parse message size at ...</li>
+
+ <li>Last message (at ... ) runs past end of file ...</li>
+ </ul>mean? I am using mbx format.</a>
+ </li>
+
+ <li>
+ <a href="#7.16">7.16 What do the syslog messages:
+
+ <ul>
+ <li>imap/tcp server failing (looping)</li>
+
+ <li>pop3/tcp server failing (looping)</li>
+ </ul>mean? When it happens, the listed service shuts down. How can
+ I fix this?</a>
+ </li>
+
+ <li>
+ <a href="#7.17">7.17 What does the syslog message:
+
+ <ul>
+ <li>Mailbox lock file /tmp/.600.1df3 open failure: Permission
+ denied</li>
+ </ul>mean?</a>
+ </li>
+
+ <li>
+ <a href="#7.18">7.18 What do the syslog messages:
+
+ <ul>
+ <li>Command stream end of file, while reading line user=...
+ host=...</li>
+
+ <li>Command stream end of file, while reading char user=...
+ host=...</li>
+
+ <li>Command stream end of file, while writing text user=...
+ host=...</li>
+ </ul>mean?</a>
+ </li>
+
+ <li>
+ <a href="#7.19">7.19 Why did my POP or IMAP session suddenly
+ disconnect? The syslog has the message:
+
+ <ul>
+ <li>Killed (lost mailbox lock) user=... host=...</li>
+ </ul></a>
+ </li>
+
+ <li><a href="#7.20">7.20 Why does my IMAP client show all the files
+ on the system, recursively from the UNIX root directory?</a></li>
+
+ <li><a href="#7.21">7.21 Why does my IMAP client show all of my
+ files, recursively from my UNIX home directory?</a></li>
+
+ <li><a href="#7.22">7.22 Why does my IMAP client show that I have
+ mailboxes named "#mhinbox", "#mh", "#shared", "#ftp", "#news", and
+ "#public"?</a></li>
+
+ <li><a href="#7.23">7.23 Why does my IMAP client show all my files in
+ my home directory?</a></li>
+
+ <li><a href="#7.24">7.24 Why is there a long delay before I get
+ connected to the IMAP or POP server, no matter what client I
+ use?</a></li>
+
+ <li><a href="#7.25">7.25 Why is there a long delay in Pine or any
+ other c-client based application call before I get connected to the
+ IMAP server? The hang seems to be in the c-client mail_open() call. I
+ don't have this problem with any other IMAP client. There is no delay
+ connecting to a POP3 or NNTP server with mail_open().</a></li>
+
+ <li><a href="#7.26">7.26 Why does a message sometimes get split into
+ two or more messages on my SUN system?</a></li>
+
+ <li>
+ <a href="#7.27">7.27 Why did my POP or IMAP session suddenly
+ disconnect? The syslog has the message:
+
+ <ul>
+ <li>Autologout user=&lt;...my user name...&gt; host=&lt;...my
+ imap server...&gt;</li>
+ </ul></a>
+ </li>
+
+ <li>
+ <a href="#7.28">7.28 What does the UNIX error message:
+
+ <ul>
+ <li>TLS/SSL failure: myserver: SSL negotiation failed</li>
+ </ul>mean?</a>
+ </li>
+
+ <li>
+ <a href="#7.29">7.29 What does the PC error message:
+
+ <ul>
+ <li>TLS/SSL failure: myserver: Unexpected TCP input
+ disconnect</li>
+ </ul>mean?</a>
+ </li>
+
+ <li>
+ <a href="#7.30">7.30 What does the error message:
+
+ <ul>
+ <li>TLS/SSL failure: myserver: Server name does not match
+ certificate</li>
+ </ul>mean?</a>
+ </li>
+
+ <li>
+ <a href="#7.31">7.31 What does the UNIX error message:
+
+ <ul>
+ <li>TLS/SSL failure: myserver: self-signed certificate</li>
+ </ul>mean?</a>
+ </li>
+
+ <li>
+ <a href="#7.32">7.32 What does the PC error message
+
+ <ul>
+ <li>TLS/SSL failure: myserver: Self-signed certificate or
+ untrusted authority</li>
+ </ul>mean?</a>
+ </li>
+
+ <li>
+ <a href="#7.33">7.33 What does the UNIX error message:
+
+ <ul>
+ <li>TLS/SSL failure: myserver: unable to get local issuer
+ certificate</li>
+ </ul>mean?</a>
+ </li>
+
+ <li><a href="#7.34">7.34 Why does reading certain messages hang when
+ using Netscape? It works fine with Pine!</a></li>
+
+ <li><a href="#7.35">7.35 Why does Netscape say that there's a problem
+ with the IMAP server and that I should "Contact your mail server
+ administrator."?</a></li>
+
+ <li><a href="#7.36">7.36 Why is one user creating huge numbers of
+ IMAP or POP server sessions?</a></li>
+
+ <li><a href="#7.37">7.37 Why don't I get any new mail notifications
+ from Outlook Express or Outlook after a while?</a></li>
+
+ <li><a href="#7.38">7.38 Why don't I get any new mail notifications
+ from Entourage?</a></li>
+
+ <li><a href="#7.39">7.39 Why doesn't Entourage work at all?</a></li>
+
+ <li><a href="#7.40">7.40 Why doesn't Netscape Notify (NSNOTIFY.EXE)
+ work at all?</a></li>
+
+ <li><a href="#7.41">7.41 Why can't I connect via SSL to Eudora? It
+ says the connection has been broken, and in the server syslogs I see
+ "Command stream end of file".</a></li>
+
+ <li><a href="#7.42">7.42 Sheesh. Aren't there <i>any</i> good IMAP
+ clients out there?</a></li>
+
+ <li>
+ <a href="#7.43">7.43 But wait! PC Pine (or other PC program build
+ with c-client) crashes with the message
+
+ <ul>
+ <li>incomplete SecBuffer exceeds maximum buffer size</li>
+ </ul>when I use SSL connections. This is a bug in c-client, right?</a>
+ </li>
+
+ <li><a href="#7.44">7.44 My qpopper users keep on getting the DON'T
+ DELETE THIS MESSAGE -- FOLDER INTERNAL DATA if they also use Pine or
+ IMAP. How can I fix this?</a></li>
+
+ <li><a href="#7.45">7.45 Help! I installed the servers but I can't
+ connect to them from my client!</a></li>
+
+ <li>
+ <a href="#7.46">7.46 Why do I get the message
+
+ <ul>
+ <li>Can not authenticate to SMTP server: 421 SMTP connection went
+ away!</li>
+ </ul>and why did this happen? There was also something about
+
+ <ul>
+ <li>SECURITY PROBLEM: insecure server advertised AUTH=PLAIN</li>
+ </ul></a>
+ </li>
+
+ <li>
+ <a href="#7.47">7.47 Why do I get the message
+
+ <ul>
+ <li>SMTP Authentication cancelled</li>
+ </ul>and why did this happen? There was also something about
+
+ <ul>
+ <li>SECURITY PROBLEM: insecure server advertised AUTH=PLAIN</li>
+ </ul></a>
+ </li>
+
+ <li>
+ <a href="#7.48">7.48 Why do I get the message
+
+ <ul>
+ <li>Invalid base64 string</li>
+ </ul>when I try to authenticate to a Cyrus server?
+ </li></a>
+ </ul>
+ </li>
+
+ <li>
+ <a href="#additional">8. Where to Go For Additional Information</a>
+
+ <ul>
+ <li><a href="#8.1">8.1 Where can I go to ask questions?</a></li>
+
+ <li><a href="#8.2">8.2 I have some ideas for enhancements to IMAP.
+ Where should I go?</a></li>
+
+ <li><a href="#8.3">8.3 Where can I read more about IMAP and other
+ email protocols?</a></li>
+
+ <li><a href="#8.4">8.4 Where can I find out more about setting up and
+ administering an IMAP server?</a></li>
+ </ul>
+ </li>
+ </ul><!--=======START BODY-->
+ <hr>
+
+ <h2><a name="general">1. General/Software Feature Questions</a></h2>
+ <hr>
+
+ <p><a name="1.1"><strong>1.1 Can I set up a POP or IMAP server on
+ UNIX/Linux/OSF/etc.?</strong></a></p>
+
+ <dl>
+ <dd>Yes. Refer to the UNIX specific notes in files CONFIG and BUILD.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="1.2"><strong>1.2 I am currently using qpopper as my POP3 server on
+ UNIX. Do I need to replace it with ipop3d in order to run
+ imapd?</strong></a></p>
+
+ <dl>
+ <dd>
+ Not necessarily.
+
+ <p>Although ipop3d interoperates with imapd better than qpopper, imapd
+ and qpopper will work together. The few qpopper/imapd interoperability
+ issues mostly affect users who use both IMAP and POP3 clients; those
+ users would probably be better served if their POP3 server is
+ ipop3d.</p>
+
+ <p>If you are happy with qpopper and just want to add imapd, you should
+ do that, and defer a decision on changing qpopper to ipop3d. That way,
+ you can get comfortable with imapd's performance, without changing
+ anything for your qpopper users.</p>
+
+ <p>Many sites have subsequently decided to change from qpopper to
+ ipop3d in order to get better POP3/IMAP interoperability. If you need
+ to do this, you'll know. There also seems to be a way to make qpopper
+ work better with imapd; see the answer to the <a href="#7.44">My
+ qpopper users keep on getting the DON'T DELETE THIS MESSAGE -- FOLDER
+ INTERNAL DATA if they also use Pine or IMAP. How can I fix this?</a>
+ question.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="1.3"><strong>1.3 Can I set up a POP or IMAP server on Windows XP,
+ 2000, NT, Me, 98, or 95?</strong></a></p>
+
+ <dl>
+ <dd>
+ Yes. Refer to the NT specific notes in files CONFIG and BUILD. Also,
+ for DOS-based versions of Windows (Windows Me, 98, and 95) you *must*
+ set up CRAM-MD5 authentication, as described in md5.txt.
+
+ <p>There is no file access control on Windows 9x or Me, so you probably
+ will have to do modifications to env_unix.c to prevent people from
+ hacking others' mail.</p>
+
+ <p>Note, however, that the server is not plug and play the way it is
+ for UNIX.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="1.4"><strong>1.4 Can I set up a POP or IMAP server on Windows 3.1 or
+ DOS?</strong></a><br>
+ <a name="1.5"><strong>1.5 Can I set up a POP or IMAP server on
+ Macintosh?</strong></a><br>
+ <a name="1.6"><strong>1.6 Can I set up a POP or IMAP server on
+ VAX/VMS?</strong></a></p>
+
+ <dl>
+ <dd>Yes, it's just a small matter of programming.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="1.7"><strong>1.7 Can I set up a POP or IMAP server on
+ TOPS-20?</strong></a></p>
+
+ <dl>
+ <dd>
+ You have a TOPS-20 system? Cool.
+
+ <p>If IMAP2 (RFC 1176) is good enough for you, you can use MAPSER which
+ is about the ultimate gonzo pure TOPS-20 extended addressing assembly
+ language program. Unfortunately, IMAP2 is barely good enough for Pine
+ these days, and most other IMAP clients won't work with IMAP2 at all.
+ Maybe someone will hack MAPSER to do IMAP4rev1 some day.</p>
+
+ <p>We don't know if anyone wrote a POP3 server for TOPS-20. There
+ definitely was a POP2 server once upon a time.</p>
+
+ <p>Or you can port the POP and IMAP server from this IMAP toolkit to
+ it. All that you need for a first stab is to port the MTX driver.
+ That'll probably be just a couple of hours of hacking.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="1.8"><strong>1.8 Are hierarchical mailboxes
+ supported?</strong></a><br>
+ <a name="1.9"><strong>1.9 Are "dual-use" mailboxes
+ supported?</strong></a><br>
+ <a name="1.10"><strong>1.10 Can I have a mailbox that has both messages and
+ sub-mailboxes?</strong></a></p>
+
+ <dl>
+ <dd>
+ Yes. However, there is one important caveat.
+
+ <p>Some mailbox formats, including the default which is the traditional
+ UNIX mailbox format, are stored as a single file containing all the
+ messages. UNIX does not permit a name in the filesystem to be both a
+ file and a directory; consequently you can not have a sub-mailbox
+ within a mailbox that is in one of these formats.</p>
+
+ <p>This is not a limitation of the software; this is a limitation of
+ UNIX. For example, there are mailbox formats in which the name is a
+ directory and each message is a file within that directory; these
+ formats support sub-mailboxes within such mailboxes. However, for
+ technical reasons, the "flat file" formats are generally preferred
+ since they perform better. Read imap-2007/docs/formats.txt for more
+ information on this topic.</p>
+
+ <p>It is always permissible to create a directory that is not a
+ mailbox, and have sub-mailboxes under it. The easiest way to create a
+ directory is to create a new mailbox inside a directory that doesn't
+ already exist. For example, if you create "Mail/testbox" on UNIX, the
+ directory "Mail/" will automatically be created and then the mailbox
+ "testbox" will be created as a sub-mailbox of "Mail/".</p>
+
+ <p>It is also possible to create the name "Mail/" directly. Check the
+ documentation for your client software to see how to do this with that
+ software.</p>
+
+ <p>Of course, on Windows systems you would use "\" instead of "/".</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="1.11"><strong>1.11 What is the difference between "mailbox" and
+ "folder"?</strong></a></p>
+
+ <dl>
+ <dd>
+ The term "mailbox" is IMAP-speak for what a lot of software calls a
+ "folder" or a "mail folder". However, "folder" is often used in other
+ contexts to refer to a directory, for example, in the graphic user
+ interface on both Windows and Macintosh.
+
+ <p>A "mailbox" is specifically defined as a named object that contains
+ messages. It is not required to be capable of containing other types of
+ objects including other mailboxes; although some mailbox formats will
+ permit this.</p>
+
+ <p>In IMAP-speak, a mailbox which can not contain other mailboxes is
+ called a "no-inferiors mailbox". Similarly, a directory which can not
+ contain messages is not a mailbox and is called a "no-select name".</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="1.12"><strong>1.12 What is the status of
+ internationalization?</strong></a></p>
+
+ <dl>
+ <dd>
+ The IMAP toolkit is partially internationalized and multilingualized.
+
+ <p>Searching is supported in the following charsets: US-ASCII, UTF-8,
+ ISO-8859-1, ISO-8859-2, ISO-8859-3, ISO-8859-4, ISO-8859-5, ISO-8859-6,
+ ISO-8859-7, ISO-8859-8, ISO-8859-9, ISO-8859-10, ISO-8859-11,
+ ISO-8859-13, ISO-8859-14, ISO-8859-15, ISO-8859-16, KOI8-R, KOI8-U
+ (alias KOI8-RU), TIS-620, VISCII, ISO-2022-JP, ISO-2022-KR,
+ ISO-2022-CN, ISO-2022-JP-1, ISO-2022-JP-2, GB2312 (alias CN-GB),
+ CN-GB-12345, BIG5 (alias CN-BIG5), EUC-JP, EUC-KR, Shift_JIS,
+ Shift-JIS, KS_C_5601-1987, KS_C_5601-1992, WINDOWS_874, WINDOWS-1250,
+ WINDOWS-1251, WINDOWS-1252, WINDOWS-1253, WINDOWS-1254, WINDOWS-1255,
+ WINDOWS-1256, WINDOWS-1257, WINDOWS-1258.</p>
+
+ <p>All ISO-2022-?? charsets are treated identically, and support ASCII,
+ JIS Roman, hankaku katakana, ISO-8859-[1 - 10], TIS, GB 2312, JIS X
+ 0208, JIS X 0212, KSC 5601, and planes 1 and 2 of CNS 11643.</p>
+
+ <p>EUC-JP includes support for JIS X 0212 and hankaku katakana.</p>
+
+ <p>c-client library support also exists to convert text in any of the
+ above charsets into Unicode, including headers with MIME
+ encoded-words.</p>
+
+ <p>There is no support for localization (e.g. non-English error
+ messages) at the present time, but such support is planned.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="1.13"><strong>1.13 Can I use SSL?</strong></a></p>
+
+ <dl>
+ <dd>Yes. See the answer to the <a href="#3.5">How do I configure SSL?</a>
+ question.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="1.14"><strong>1.14 Can I use TLS and the STARTTLS
+ facility?</strong></a></p>
+
+ <dl>
+ <dd>Yes. See the answer to the <a href="#3.6">How do I configure TLS and
+ the STARTTLS facility?</a> question.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="1.15"><strong>1.15 Can I use CRAM-MD5
+ authentication?</strong></a></p>
+
+ <dl>
+ <dd>Yes. See the answer to the <a href="#3.8">How do I configure CRAM-MD5
+ authentication?</a> question.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="1.16"><strong>1.16 Can I use APOP authentication?</strong></a></p>
+
+ <dl>
+ <dd>
+ Yes. See the <a href="#3.9">How do I configure APOP authentication?</a>
+ question.
+
+ <p>Note that there is no client support for APOP authentication.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="1.17"><strong>1.17 Can I use Kerberos V5?</strong></a></p>
+
+ <dl>
+ <dd>Yes. See the answer to the <a href="#3.10">How do I configure
+ Kerberos V5?</a> question.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="1.18"><strong>1.18 Can I use PAM for plaintext
+ passwords?</strong></a></p>
+
+ <dl>
+ <dd>Yes. See the answer to the <a href="#3.11">How do I configure PAM for
+ plaintext passwords?</a> question.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="1.19"><strong>1.19 Can I use Kerberos 5 for plaintext
+ passwords?</strong></a></p>
+
+ <dl>
+ <dd>Yes. See the answer to the <a href="#3.13">How do I configure
+ Kerberos 5 for plaintext passwords?</a> question.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="1.20"><strong>1.20 Can I use AFS for plaintext
+ passwords?</strong></a></p>
+
+ <dl>
+ <dd>Yes. See the answer to the <a href="#3.14">How do I configure AFS for
+ plaintext passwords?</a> question.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="1.21"><strong>1.21 Can I use DCE for plaintext
+ passwords?</strong></a></p>
+
+ <dl>
+ <dd>Yes. See the answer to the <a href="#3.15">How do I configure DCE for
+ plaintext passwords?</a> question.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="1.22"><strong>1.22 Can I use the CRAM-MD5 database for plaintext
+ passwords?</strong></a></p>
+
+ <dl>
+ <dd>Yes. See the answer to the <a href="#3.16">How do I configure the
+ CRAM-MD5 database for plaintext passwords?</a> question.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="1.23"><strong>1.23 Can I disable plaintext
+ passwords?</strong></a></p>
+
+ <dl>
+ <dd>Yes. See the answer to the <a href="#3.17">How do I disable plaintext
+ passwords?</a> question.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="1.24"><strong>1.24 Can I disable plaintext passwords on unencrypted
+ sessions, but allow them on encrypted sessions?</strong></a></p>
+
+ <dl>
+ <dd>Yes. See the answer to the <a href="#3.18">How do I disable plaintext
+ passwords on unencrypted sessions, but allow them in SSL or TLS
+ sessions?</a> question.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="1.25"><strong>1.25 Can I use virtual hosts?</strong></a></p>
+
+ <dl>
+ <dd>Yes. See the answer to the <a href="#3.19">How do I configure virtual
+ hosts?</a> question.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="1.26"><strong>1.26 Can I use RPOP authentication?</strong></a></p>
+
+ <dl>
+ <dd>There is no support for RPOP authentication.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="1.27"><strong>1.27 Can I use Kerberos V4?</strong></a></p>
+
+ <dl>
+ <dd>
+ Kerberos V4 is not supported. Kerberos V4 client-only contributed code
+ is available in
+ <pre>
+<a href=
+"ftp://ftp.cac.washington.edu/mail/kerberos4-patches.tar.Z">ftp://ftp.cac.washington.edu/mail/kerberos4-patches.tar.Z
+</a>
+</pre>This is a patchkit which must be applied to the IMAP toolkit according
+to the instructions in the patchkit's README. We can not promise that this
+code works.
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="1.28"><strong>1.28 Is there support for S/Key or
+ OTP?</strong></a></p>
+
+ <dl>
+ <dd>There is currently no support for S/Key or OTP. There may be an OTP
+ SASL authenticator available from third parties.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="1.29"><strong>1.29 Is there support for NTLM or
+ SPA?</strong></a></p>
+
+ <dl>
+ <dd>
+ There is currently no support for NTLM or SPA, nor are there any plans
+ to add such support. In general, I avoid vendor-specific mechanisms. I
+ also believe that these mechanisms are being deprecated by their
+ vendor.
+
+ <p>There may be an NTLM SASL authenticator available from third
+ parties.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="1.30"><strong>1.30 Is there support for mh?</strong></a></p>
+
+ <dl>
+ <dd>
+ Yes, but only as a legacy format. Your mh format INBOX is accessed by
+ the name "#mhinbox", and all other mh format mailboxes are accessed by
+ prefixing "#mh/" to the name, e.g. "#mh/foo". The mh support uses the
+ "Path:" entry in your .mh_profile file to identify the root directory
+ of your mh format mailboxes.
+
+ <p>Non-legacy use of mh format is not encouraged. There is no support
+ for permanent flags or unique identifiers; furthermore there are known
+ severe performance problems with the mh format.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="1.31"><strong>1.31 Is there support for qmail and the maildir
+ format?</strong></a></p>
+
+ <dl>
+ <dd>There is no support for qmail or the maildir format in our
+ distribution, nor are there any plans to add such support. Maildir
+ support may be available from third parties.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="1.32"><strong>1.32 Is there support for the Cyrus mailbox
+ format?</strong></a></p>
+
+ <dl>
+ <dd>No.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="1.33"><strong>1.33 Is this software Y2K compliant?</strong></a></p>
+
+ <dl>
+ <dd>Please read the files Y2K and calendar.txt.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><br></p>
+
+ <h2><a name="requirements">2. What Do I Need to Build This Software?</a></h2>
+ <hr>
+
+ <p><a name="2.1"><strong>2.1 What do I need to build this software with SSL on
+ UNIX?</strong></a></p>
+
+ <dl>
+ <dd>You need to build and install OpenSSL first.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="2.2"><strong>2.2 What do I need to build this software with Kerberos
+ V on UNIX?</strong></a></p>
+
+ <dl>
+ <dd>You need to build and install MIT Kerberos first.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="2.3"><strong>2.3 What do I need to use a C++ compiler with this
+ software to build my own application?</strong></a></p>
+
+ <dl>
+ <dd>
+ If you are building an application using the c-client library, use the
+ new c-client.h file instead of including the other include files. It
+ seems that c-client.h should define away all the troublesome names that
+ conflict with C++.
+
+ <p>If you use gcc, you may need to use -fno-operator-names as well.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="2.4"><strong>2.4 What do I need to build this software on
+ Windows?</strong></a></p>
+
+ <dl>
+ <dd>
+ You need Microsoft Visual C++ 6.0, Visual C++ .NET, or Visual C# .NET
+ (which you can buy from any computer store), along with the Microsoft
+ Platform SDK (which you can download from Microsoft's web site).
+
+ <p>You do not need to install the entire Platform SDK; it suffices to
+ install just the Core SDK and the Internet Development SDK.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="2.5"><strong>2.5 What do I need to build this software on
+ DOS?</strong></a></p>
+
+ <dl>
+ <dd>It's been several years since we last attempted to do this. At the
+ time, we used Microsoft C.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="2.6"><strong>2.6 Can't I use Borland C to build this software on the
+ PC?</strong></a></p>
+
+ <dl>
+ <dd>Probably not. If you know otherwise, please let us know.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="2.7"><strong>2.7 What do I need to build this software on the
+ Mac?</strong></a></p>
+
+ <dl>
+ <dd>It has been several years since we last attempted to do this. At the
+ time, we used Symantec THINK C; but today you'll need a C compiler which
+ allows segments to be more than 32K.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="2.8"><strong>2.8 What do I need to build this software on
+ VMS?</strong></a></p>
+
+ <dl>
+ <dd>You need the VMS C compiler, and either the Multinet or Netlib
+ TCP.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="2.9"><strong>2.9 What do I need to build this software on
+ TOPS-20?</strong></a></p>
+
+ <dl>
+ <dd>You need the TOPS-20 KCC compiler.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="2.10"><strong>2.10 What do I need to build this software on Amiga or
+ OS/2?</strong></a></p>
+
+ <dl>
+ <dd>We don't know.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="2.11"><strong>2.11 What do I need to build this software on Windows
+ CE?</strong></a></p>
+
+ <dl>
+ <dd>This port is incomplete. Someone needs to finish it.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><br></p>
+
+ <h2><a name="build">3. Build and Configuration Questions</a></h2>
+ <hr>
+
+ <p><a name="3.1"><strong>3.1 How do I configure the IMAP and POP servers on
+ UNIX?</strong></a><br>
+ <a name="3.2"><strong>3.2 I built and installed the servers according to the
+ BUILD instructions. It can't be that easy. Don't I need to write a
+ config file?</strong></a></p>
+
+ <dl>
+ <dd>
+ For ordinary "vanilla" UNIX systems, this software is plug and play;
+ just build it, install it, and you're done. If you have a modified
+ system, then you may want to do additional work; most of this is to a
+ single source code file (env_unix.c on UNIX systems). Read the file
+ CONFIG for more details.
+
+ <p>Yes, it's that easy. There are some additional options, such as SSL
+ or Kerberos, which require additional steps to build. See the relevant
+ questions below.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="3.3"><strong>3.3 How do I make the IMAP and POP servers look for
+ INBOX at some place other than the mail spool
+ directory?</strong></a><br>
+ <a name="3.4"><strong>3.4 How do I make the IMAP server look for secondary
+ folders at some place other than the user's home
+ directory?</strong></a></p>
+
+ <dl>
+ <dd>Please read the file CONFIG for discussion of this and other
+ issues.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="3.5"><strong>3.5 How do I configure SSL?</strong></a><br>
+ <a name="3.6"><strong>3.6 How do I configure TLS and the STARTTLS
+ facility?</strong></a></p>
+
+ <dl>
+ <dd>
+ imap-2007 supports SSL and TLS client functionality on UNIX and 32-bit
+ Windows for IMAP, POP3, SMTP, and NNTP; and SSL and TLS server
+ functionality on UNIX for IMAP and POP3.
+
+ <p>UNIX SSL build requires that a third-party software package,
+ OpenSSL, be installed on the system first. Read imap-2007/docs/SSLBUILD
+ for more information.</p>
+
+ <p>SSL is supported via undocumented Microsoft interfaces in Windows 9x
+ and NT4; and via standard interfaces in Windows 2000, Windows
+ Millenium, and Windows XP.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="3.7"><strong>3.7 How do I build/install OpenSSL and obtain/create
+ certificates for use with SSL?</strong></a></p>
+
+ <dl>
+ <dd>If you need help in doing this, try the contacts mentioned in the
+ OpenSSL README. We do not offer support for OpenSSL or certificates.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="3.8"><strong>3.8 How do I configure CRAM-MD5
+ authentication?</strong></a><br>
+ <a name="3.9"><strong>3.9 How do I configure APOP
+ authentication?</strong></a></p>
+
+ <dl>
+ <dd>
+ CRAM-MD5 authentication is enabled in the IMAP and POP3 client code on
+ all platforms. Read md5.txt to learn how to set up CRAM-MD5 and APOP
+ authentication on UNIX and NT servers.
+
+ <p>There is no support for APOP client authentication.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="3.10"><strong>3.10 How do I configure Kerberos V5?</strong></a></p>
+
+ <dl>
+ <dd>
+ imap-2007 supports client and server functionality on UNIX and 32-bit
+ Windows.
+
+ <p>Kerberos V5 is supported by default in Windows 2000 builds:</p>
+ <pre>
+ nmake -f makefile.w2k
+</pre>
+
+ <p>Other builds require that a third-party Kerberos package, e.g. MIT
+ Kerberos, be installed on the system first.</p>
+
+ <p>To build with Kerberos V5 on UNIX, include EXTRAAUTHENTICATORS=gss
+ in the make command line, e.g.</p>
+ <pre>
+ make lnp EXTRAAUTHENTICATORS=gss
+</pre>
+
+ <p>To build with Kerberos V5 on Windows 9x, Windows Millenium, and NT4,
+ use the "makefile.ntk" file instead of "makefile.nt":</p>
+ <pre>
+
+ nmake -f makefile.ntk
+</pre>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="3.11"><strong>3.11 How do I configure PAM for plaintext
+ passwords?</strong></a></p>
+
+ <dl>
+ <dd>
+ On Linux systems, use the lnp port, e.g.
+ <pre>
+ make lnp
+
+</pre>On Solaris systems and other systems with defective PAM
+implementations, build with PASSWDTYPE=pmb, e.g.
+ <pre>
+ make sol PASSWDTYPE=pmb
+</pre>On all other systems, build with PASSWDTYPE=pam, e.g
+ <pre>
+ make foo PASSWDTYPE=pam
+</pre>If you build with PASSWDTYPE=pam and authentication does not work, try
+rebuilding (after a "make clean") with PASSWDTYPE=pmb.
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="3.12"><strong>3.12 It looks like all I have to do to make the server
+ use Kerberos is to build with PAM on my Linux system, and set it up in
+ PAM for Kerberos passwords. Right?</strong></a></p>
+
+ <dl>
+ <dd>
+ Yes and no.
+
+ <p>Doing this will make plaintext password authentication use the
+ Kerberos password instead of the /etc/passwd password.</p>
+
+ <p>However, this will NOT give you Kerberos-secure authentication. See
+ the answer to the <a href="#3.10">How do I configure Kerberos V5?</a>
+ question for how to build with Kerberos-secure authentication.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="3.13"><strong>3.13 How do I configure Kerberos 5 for plaintext
+ passwords?</strong></a></p>
+
+ <dl>
+ <dd>
+ Build with PASSWDTYPE=gss, e.g.
+ <pre>
+ make sol PASSWDTYPE=gss
+</pre>However, this will NOT give you Kerberos-secure authentication. See the
+answer to the <a href="#3.10">How do I configure Kerberos V5?</a> question
+for how to build with Kerberos-secure authentication.
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="3.14"><strong>3.14 How do I configure AFS for plaintext
+ passwords?</strong></a></p>
+
+ <dl>
+ <dd>
+ Build with PASSWDTYPE=afs, e.g
+ <pre>
+ make sol PASSWDTYPE=afs
+
+</pre>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="3.15"><strong>3.15 How do I configure DCE for plaintext
+ passwords?</strong></a></p>
+
+ <dl>
+ <dd>
+ Build with PASSWDTYPE=dce, e.g
+ <pre>
+ make sol PASSWDTYPE=dce
+</pre>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="3.16"><strong>3.16 How do I configure the CRAM-MD5 database for
+ plaintext passwords?</strong></a></p>
+
+ <dl>
+ <dd>
+ The CRAM-MD5 password database is automatically used for plaintext
+ password if it exists.
+
+ <p>Note that this is NOT CRAM-MD5-secure authentication. You probably
+ want to consider disabling plaintext passwords for non-SSL/TLS
+ sessions. See the next two questions.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="3.17"><strong>3.17 How do I disable plaintext
+ passwords?</strong></a></p>
+
+ <dl>
+ <dd>
+ Server-level plaintext passwords can be disabled by setting
+ PASSWDTYPE=nul, e.g.
+ <pre>
+ make lnx EXTRAAUTHENTICATORS=gss PASSWDTYPE=nul
+</pre>Note that you must have a CRAM-MD5 database installed or specify at
+least one EXTRAAUTHENTICATOR, otherwise it will not be possible to log in to
+the server.
+
+ <p>When plaintext passwords are disabled, the IMAP server will
+ advertise the LOGINDISABLED capability and the POP3 server will not
+ advertise the USER capability.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+
+ <p><a name="3.18"><strong>3.18 How do I disable plaintext passwords on
+ unencrypted sessions, but allow them in SSL or TLS
+ sessions?</strong></a></p>
+
+ <dl>
+ <dd>
+ <p>Do not set PASSWDTYPE=nul or SSLTYPE=unix. Set SSLTYPE=nopwd
+ instead, e.g.</p>
+ <pre>
+ make lnx SSLTYPE=nopwd
+</pre>
+
+ <p>When plaintext passwords are disabled, the IMAP server will
+ advertise the LOGINDISABLED capability and the POP3 server will not
+ advertise the USER capability.</p>
+
+ <p>Plaintext passwords will always be enabled in SSL sessions; the IMAP
+ server will not advertise the LOGINDISABLED capability and the POP3
+ server will advertise the USER capability.</p>
+
+ <p>If the client does a successful start-TLS in a non-SSL session,
+ plaintext passwords will be enabled, and a new CAPABILITY or CAPA
+ command (which is required after start-TLS) will show the effect as in
+ SSL sessions.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="3.19"><strong>3.19 How do I configure virtual
+ hosts?</strong></a></p>
+
+ <dl>
+ <dd>
+ This is automatic, but with certain restrictions.
+
+ <p>The most important one is that each virtual host must have its own
+ IP address; otherwise the server has no way of knowing which virtual
+ host is desired.</p>
+
+ <p>As distributed, the software uses a global password file; hence user
+ "fred" on one virtual host is "fred" on all virtual hosts. You may want
+ to modify the checkpw() routine to implement some other policy (e.g.
+ separate password files).</p>
+
+ <p>Note that the security model assumes that all users have their own
+ unique UNIX UID number. So if you use separate password files you
+ should make certain that the UID numbers do not overlap between
+ different files.</p>
+
+ <p>More advanced virtual host support may be available as patches from
+ third parties.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="3.20"><strong>3.20 Why do I get compiler warning messages such
+ as:</strong></a></p>
+ <pre>
+ passing arg 3 of `scandir' from incompatible pointer type
+ Pointers are not assignment-compatible.
+ Argument #4 is not the correct type.
+
+</pre>
+
+ <p><strong>during the build?</strong></p>
+
+ <dl>
+ <dd>
+ You can safely ignore these messages.
+
+ <p>Over the years, the prototype for scandir() has changed, and thus is
+ variant across different UNIX platforms. In particular, the definitions
+ of the third argument (type select_t) and fourth argument (type
+ compar_t) have changed over the years, the issue being whether or not
+ the arguments to the functions pointed to by these function pointers
+ are of type const or not.</p>
+
+ <p>The way that c-client calls scandir() will tend to generate these
+ compiler warnings on newer systems such as Linux; however, it will
+ still build. The problem with fixing the call is that then it won't
+ build on older systems.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="3.21"><strong>3.21 Why do I get compiler warning messages such
+ as</strong></a></p>
+ <pre>
+ Operation between types "void(*)(int)" and "void*" is not allowed.
+ Function argument assignment between types "void*" and "void(*)(int)" is not allowed.
+ Pointers are not assignment-compatible.
+ Argument #5 is not the correct type.
+</pre>
+
+ <p><strong>during the build?</strong></p>
+
+ <dl>
+ <dd>
+ You can safely ignore these messages.
+
+ <p>All known systems have no problem with casting a function pointer
+ to/from a void* pointer, certain C compilers issue a compiler
+ diagnostic because this facility is listed as a "Common extension" by
+ the C standard:</p>
+ <pre>
+ K.5.7 Function pointer casts
+ [#1] A pointer to an object or to void may be cast to a pointer
+ to a function, allowing data to be invoked as a function (6.3.4).
+ [#2] A pointer to a function may be cast to a pointer to an
+ object or to void, allowing a function to be inspected or
+ modified (for example, by a debugger) (6.3.4).
+
+</pre>It may be just a "common extension", but this facility is relied upon
+heavily by c-client.
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="3.22"><strong>3.22 Why do I get linker warning messages such
+ as:</strong></a></p>
+ <pre>
+mtest.c:515: the `gets' function is dangerous and should not be used.
+</pre>
+
+ <p><strong>during the build? Isn't this a security bug?</strong></p>
+
+ <dl>
+ <dd>
+ You can safely ignore this message.
+
+ <p>Certain linkers, most notably on Linux, give this warning message.
+ It is indeed true that the traditional gets() function is not a safe
+ one.</p>
+
+ <p>However, the mtest program is only a demonstration program, a model
+ of a very basic application program using c-client. It is not something
+ that you would install, much less run in any security-sensitive
+ context.</p>
+
+ <p>mtest has numerous other shortcuts that you wouldn't want to do in a
+ real application program.</p>
+
+ <p>The only "security bug" with mtest would be if it was run by some
+ script in a security-sensitive context, but mtest isn't particularly
+ useful for such purposes. If you wanted to write a script to automate
+ some email task using c-client, you'd be better off using imapd instead
+ of mtest.</p>
+
+ <p>mtest only has two legitimate uses. It's a useful testbed for me
+ when debugging new versions of c-client, and it's useful as a model for
+ someone writing a simple c-client application to see how the various
+ calls work.</p>
+
+ <p>By the way, if you need a more advanced example of c-client
+ programming than mtest (and you probably will), I recommend that you
+ look at the source code for imapd and Pine.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="3.23"><strong>3.23 Why do I get linker warning messages such
+ as:</strong></a></p>
+ <pre>
+ auth_ssl.c:92: the `tmpnam' function is dangerous and should not be used.
+</pre>
+
+ <p><strong>during the build? Isn't this a security bug?</strong></p>
+
+ <dl>
+ <dd>
+ You can safely ignore this message.
+
+ <p>Certain linkers, most notably on Linux, give this warning message,
+ based upon two known issues with tmpnam():</p>
+
+ <dl>
+ <dd>there can be a buffer overflow if an inadequate buffer is
+ allocated.</dd>
+
+ <dd>there can be a timing race caused by certain incautious usage of
+ the return value.</dd>
+ </dl>
+
+ <p>Neither of these issues applies in the particular use that is made
+ of tmpnam(). More importantly, the tmpnam() call is never executed on
+ Linux systems.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="3.24"><strong>3.24 OK, suppose I see a warning message about a
+ function being "dangerous and should not be used" for something other
+ than this gets() or tmpnam() call?</strong></a></p>
+
+ <dl>
+ <dd>Please forward the details for investigation.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><br></p>
+
+ <h2><a name="operation">4. Operational Questions</a></h2>
+ <hr>
+
+ <p><a name="4.1"><strong>4.1 How can I enable anonymous IMAP
+ logins?</strong></a></p>
+
+ <dl>
+ <dd>Create the file /etc/anonymous.newsgroups. At the present time, this
+ file should be empty. This will permit IMAP logins as anonymous as well
+ as the ANONYMOUS SASL authenticator. Anonymous users have access to
+ mailboxes in the #news., #ftp/, and #public/ namespaces only.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="4.2"><strong>4.2 How do I set up an alert message that each IMAP
+ user will see?</strong></a></p>
+
+ <dl>
+ <dd>Create the file /etc/imapd.alert with the text of the message. This
+ text should be kept to one line if possible. Note that this will cause an
+ alert to every IMAP user every time they initiate an IMAP session, so it
+ should only be used for critical messages.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="4.3"><strong>4.3 How does the c-client library choose which of its
+ several mechanisms to use to establish an IMAP connection to the server?
+ I noticed that it can connect on port 143, port 993, via rsh, and via
+ ssh.</strong></a></p>
+
+ <dl>
+ <dd>
+ c-client chooses how to establish an IMAP connection via the following
+ rules:
+
+ <ul>
+ <li>If /ssl is specified, use an SSL connection. Fail otherwise.</li>
+
+ <li>Else if client is a UNIX system and "ssh server exec /etc/rimapd"
+ works, use that</li>
+
+ <li>Else if /tryssl is specified and an SSL connection works, use
+ that.</li>
+
+ <li>Else if client is a UNIX system and "rsh server exec /etc/rimapd"
+ works, use that.</li>
+
+ <li>Else use a non-SSL connection.</li>
+ </ul>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="4.4"><strong>4.4 I am using a TLS-capable IMAP server, so I don't
+ need to use /ssl to get encryption. However, I want to be certain that
+ my session is TLS encrypted before I send my password. How to I do
+ this?</strong></a></p>
+
+ <dl>
+ <dd>Use the /tls option in the mailbox name. This will cause an error
+ message and the connection to fail if the server does not negotiate
+ STARTTLS.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="4.5"><strong>4.5 How do I use one of the alternative formats
+ described in the formats.txt document? In particular, I hear that mbx
+ format will give me better performance and allow shared
+ access.</strong></a></p>
+
+ <dl>
+ <dd>
+ The rumors about mbx format being preferred are true. It is faster than
+ the traditional UNIX mailbox format and permits shared access.
+
+ <p>However, and this is <em>very important</em>, note that using an
+ alternative mailbox format is an advanced facility, and only expert
+ users should undertake it. If you don't understand any of the following
+ notes, you may not be enough of an expert yet, and are probably better
+ off not going this route until you are more comfortable with your
+ understanding.</p>
+
+ <p>Some of the formats, including mbx, are only supported by the
+ software based on the c-client library, and are not recognized by other
+ mailbox programs. The "vi" editor will corrupt any mbx format mailbox
+ that it encounters.</p>
+
+ <p>Another problem is that the certain formats, including mbx, use
+ advanced file access and locking techniques that do <em>not</em> work
+ reliably with NFS. NFS is not a real filesystem. Use IMAP instead of
+ NFS for distributed access.</p>
+
+ <p>Each of the following steps are in escalating order of involvement.
+ The further you go down this list, the more deeply committed you
+ become:</p>
+
+ <ul>
+ <li>The simplest way to create a mbx-format mailbox is to prefix the
+ name with "#driver.mbx/" when creating a mailbox through c-client.
+ For example, if you create "#driver.mbx/foo", the mailbox "foo" will
+ be created in mbx format. Only use "#driver.mbx/" when creating the
+ mailbox. At all other times, just use the name ("foo" in this
+ example); the software will automatically select the driver for mbx
+ whenever that mailbox is accessed without you doing anything
+ else.</li>
+
+ <li>You can use the "mailutil copy" command to copy an existing
+ mailbox to a new mailbox in mbx format. Read the man page provided
+ with the mailutil program for details.</li>
+
+ <li>If you create an mbx-format INBOX, by creating
+ "#driver.mbx/INBOX" (note that "INBOX" must be all uppercase), then
+ subsequent access to INBOX by any c-client based application will use
+ the mbx-format INBOX. Any mail delivered to the traditional format
+ mailbox in the spool directory (e.g. /var/spool/mail/$USER) will
+ automatically be copied into the mbx-format INBOX and the spool
+ directory copy removed.</li>
+
+ <li>You can cause any newly-created mailboxes to be in mbx-format by
+ default by changing the definition of CREATEPROTO=unixproto to be
+ CREATEPROTO=mbxproto in src/osdep/unix/Makefile, then rebuilding the
+ IMAP toolkit (do a "make clean" first). Do not change EMPTYPROTO,
+ since mbx format mailboxes are never a zero-byte file. If you use
+ Pine or the imap-utils, you should probably also rebuild them with
+ the new IMAP toolkit too.</li>
+
+ <li>You can deliver directly to the mbx-format INBOX by use of the
+ tmail or dmail programs. tmail is for direct invocation from sendmail
+ (or whatever MTA program you use); dmail is for calls from procmail.
+ Both of these programs have man pages which must be read carefully
+ before making this change.</li>
+ </ul>
+
+ <p>Most other servers (e.g. Cyrus) require use of a non-standard
+ format. A full-fledged format conversion is not significantly different
+ from what you have to do with other servers. The difference, which
+ makes format conversion procedures somewhat more complicated with this
+ server, is that there is no "all or nothing" requirement with this
+ server. There are many points in between. A format conversion can be
+ anything from a single mailbox or single user, to systemwide.</p>
+
+ <p>This is good in that you can decide how far to go, or do the steps
+ incrementally as you become more comfortable with the result. On the
+ other hand, there's no "One True Way" which can be boiled down to a
+ simple set of pedagogical instructions.</p>
+
+ <p>A number of sites have done full-fledged format conversions, and are
+ reportedly quite happy with the results. Feel free to ask in the
+ comp.mail.imap newsgroup or the imap-uw mailing list for advice or
+ help.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="4.6"><strong>4.6 How do I set up shared mailboxes?</strong></a></p>
+
+ <dl>
+ <dd>
+ At the simplest level, a shared mailbox is one which has UNIX file and
+ directory protections which permit multiple users to access it. What
+ this means is that your existing skills and tools to create and manage
+ shared files on your UNIX system apply to shared mailboxes; e.g.
+ <pre>
+ chmod 666 mailbox
+</pre>
+
+ <p>You may want to consider the use of a mailbox format which permits
+ multiple simultaneous read/write sessions, such as the mbx format. The
+ traditional UNIX format only allows one read/write session to a
+ mailbox at a time.</p>
+
+ <p>An additional convenience item are three system directories, which
+ can be set up for shared namespaces. These are: #ftp, #shared, and
+ #public, and are defined by creating the associated UNIX users and home
+ directories as described below.</p>
+
+ <p>#ftp/ refers to the anonymous ftp filesystem exported by the ftp
+ server, and is equivalent to the home directory for UNIX user "ftp".
+ For example, #ftp/foo/bar refers to the file /foo/bar in the anonymous
+ FTP filesystem, or ~ftp/foo/bar for normal users. Anonymous FTP files
+ are available to anonymous IMAP logins. By default, newly-created files
+ in #ftp/ are protected 644.</p>
+
+ <p>#public/ refers to an IMAP toolkit convention called "public" files,
+ and is equivalent to the home directory for UNIX user "imappublic". For
+ example, #public/foo/bar refers to the file ~imappublic/foo/bar. Public
+ files are available to anonymous IMAP logins. By default, newly-created
+ files in #public are created with protection 0666.</p>
+
+ <p>#shared/ refers to an IMAP toolkit convention called "shared" files,
+ and is equivalent to the home directory for UNIX user "imapshared". For
+ example, #shared/foo/bar refers to the file ~imapshared/foo/bar. Shared
+ files are <em>not</em> available to anonymous IMAP logins. By default,
+ newly-created files in #shared are created with protection 0660.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="4.7"><strong>4.7 How can I make the server syslogs go to someplace
+ other than the mail syslog?</strong></a></p>
+
+ <dl>
+ <dd>
+ The openlog() call that sets the syslog facility is in
+ <strong>src/osdep/unix/env_unix.c</strong> in routine
+ <strong>server_init()</strong>. You need to edit this file to change
+ the syslog facility from LOG_MAIL to the facility you want, then
+ rebuild. You also need to set up your /etc/syslog.conf properly.
+
+ <p>Refer to the man pages for syslog and syslogd for more information
+ on what the available syslog facilities are and how to configure
+ syslogs. If you still don't understand what to do, find a UNIX system
+ expert.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><br></p>
+
+ <h2><a name="security">5. Security Questions</a></h2>
+ <hr>
+
+ <p><a name="5.1"><strong>5.1 I see that the IMAP server allows access to
+ arbitary files on the system, including /etc/passwd! How do I disable
+ this?</strong></a></p>
+
+ <dl>
+ <dd>
+ You should not worry about this if your IMAP users are allowed shell
+ access. The IMAP server does not permit any access that the user can
+ not have via the shell.
+
+ <p>If, and only if, you deny your IMAP users shell access, you may want
+ to consider one of three choices. Note that these choices reduce IMAP
+ functionality, and may have undesirable side effects. Each of these
+ choices involves an edit to file
+ <strong>src/osdep/unix/env_unix.c</strong></p>
+
+ <p>The first (and recommended) choice is to set
+ <strong>restrictBox</strong> as described in file CONFIG. This will
+ disable access to the filesystem root, to other users' home directory,
+ and to superior directory.</p>
+
+ <p>The second (and strongly NOT recommended) choice is to set
+ <strong>closedBox</strong> as described in file CONFIG. This puts each
+ IMAP session into a so-called "chroot jail", and thus setting this
+ option is <em>extremely</em> dangerous; it can make your system much
+ less secure and open to root compromise attacks. So do not use this
+ option unless you are <em>absolutely certain</em> that you understand
+ all the issues of a "chroot jail."</p>
+
+ <p>The third choice is to rewrite routine
+ <strong>mailboxfile()</strong> to implement whatever mapping from
+ mailbox name to filesystem name (and restrictions) that you wish. This
+ is the most general choice. As a guide, you can see at the start of
+ routine <strong>mailboxfile()</strong> what the
+ <strong>restrictBox</strong> choice does.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="5.2"><strong>5.2 I've heard that IMAP servers are insecure. Is this
+ true?</strong></a></p>
+
+ <dl>
+ <dd>
+ There are no known security problems in this version of the IMAP
+ toolkit, including the IMAP and POP servers. The IMAP and POP servers
+ limit what can be done while not logged in, and as part of the login
+ process discard all privileges except those of the user.
+
+ <p>As with other software packages, there have been buffer overflow
+ vulnerabilities in past versions. All known problems of this nature are
+ fixed in this version.</p>
+
+ <p>There is every reason to believe that the bad guys are engaged in an
+ ongoing effort to find vulnerabilities in the IMAP toolkit. We look for
+ such problems, and when one is found we fix it.</p>
+
+ <p>It's unfortunate that any vulnerabilities existed in past versions,
+ and we're doing my best to keep the IMAP toolkit free of
+ vulnerabilities. No new vulnerabilities have been discovered in quite a
+ while, but efforts will not be relaxed.</p>
+
+ <p>Beware of vendors who claim that their implementations can not have
+ vulnerabilities.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="5.3"><strong>5.3 How do I know that I have the most secure version
+ of the server?</strong></a></p>
+
+ <dl>
+ <dd>
+ The best way is to keep your server software up to date. The bad guys
+ are always looking for ways to crack software, and when they find one,
+ let all their friends know.
+
+ <p>Oldtimers used to refer to a concept of <em>software rot</em>: if
+ your software hasn't been updated in a while, it would "rot" -- tend to
+ acquire problems that it didn't have when it was new.</p>
+
+ <p>The latest release version of the IMAP toolkit is always available
+ at <a href=
+ "ftp://ftp.cac.washington.edu/mail/imap.tar.Z">ftp://ftp.cac.washington.edu/mail/imap.tar.Z</a></p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="5.4"><strong>5.4 I see all these strcpy() and sprintf() calls, those
+ are unsafe, aren't they?</strong></a></p>
+
+ <dl>
+ <dd>
+ Yes and no.
+
+ <p>It can be unsafe to do these calls if you do not know that the
+ string being written will fit in the buffer. However, they are
+ perfectly safe if you do know that.</p>
+
+ <p>Beware of programmers who advocate doing a brute-force change of all
+ instances of</p>
+ <pre>
+ strcpy (s,t);
+</pre>to
+ <pre>
+ strncpy (s,t,n)[n] = '\0';
+</pre>and similar measures in the name of "fixing all possible buffer
+overflows."
+
+ <p>There are examples in which a security bug was introduced because of
+ this type of "fix", due to the programmer using the wrong value for n.
+ In one case, the programmer thought that n was larger than it actually
+ was, causing a NUL to be written out of the buffer; in another, n was
+ too small, and a security credential was truncated.</p>
+
+ <p>What is particularly ironic was that in both cases, the original
+ strcpy() was safe, because the size of the source string was known to
+ be safe.</p>
+
+ <p>With all this in mind, the software has been inspected, and it is
+ believed that all places where buffer overflows can happen have been
+ fixed. The strcpy()s that are still are in the code occur after a size
+ check was done in some other way.</p>
+
+ <p>Note that the common C idiom of</p>
+ <pre>
+ *s++ = c;
+</pre>is just as vulnerable to buffer overflows. You can't cure buffer
+overflows by outlawing certain functions, nor is it desirable to do so;
+sometimes operations like strcpy() translate into fast machine instructions
+for better performance.
+
+ <p>Nothing replaces careful study of code. That's how the bad guys find
+ bugs. Security is not accomplished by means of brute-force
+ shortcuts.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="5.5"><strong>5.5 Those /tmp lock files are protected 666, is that
+ really right?</strong></a></p>
+
+ <dl>
+ <dd>
+ Yes. Shared mailboxes won't work otherwise. Also, you get into
+ accidental denial of service problems with old lock files left lying
+ around; this happens fairly frequently.
+
+ <p>The deliberate mischief that can be caused by fiddling with the lock
+ files is small-scale; harassment level at most. There are many -- and
+ much more effective -- other ways of harassing another user on UNIX.
+ It's usually not difficult to determine the culprit.</p>
+
+ <p>Before worrying about deliberate mischief, worry first about things
+ happening by accident!</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><br></p>
+
+ <h2><a name="strange">6. <em>Why Did You Do This Strange Thing?</em>
+ Questions</a></h2>
+ <hr>
+
+ <p><a name="6.1"><strong>6.1 Why don't you use GNU autoconfig / automake /
+ autoblurdybloop?</strong></a></p>
+
+ <dl>
+ <dd>
+ Autoconfig et al are not available on all the platforms where the IMAP
+ toolkit is supported; and do not work correctly on some of the
+ platforms where they do exist. Furthermore, these programs add another
+ layer of complexity to an already complex process.
+
+ <p>Coaxing software that uses autoconfig to build properly on platforms
+ which were not specifically considered by that software wastes an
+ inordinate amount of time. When (not if) autoconfig fails to do the
+ right thing, the result is an inpenetrable morass to untangle in order
+ to find the problem and fix it.</p>
+
+ <p>The concept behind autoconfig is good, but the execution is flawed.
+ It rarely does the right thing on a platform that wasn't specifically
+ considered. Human life is too short to debug autoconfig problems,
+ especially since the current mechanism is so much easier.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="6.2"><strong>6.2 Why do you insist upon a build with -g? Doesn't it
+ waste disk and memory space?</strong></a></p>
+
+ <dl>
+ <dd>
+ From time to time a submitted port has snuck in without -g. This has
+ <em>always</em> ended up causing problems. There are only two valid
+ excuses for not using -g in a port:
+
+ <ul>
+ <li>The compiler does not support -g</li>
+
+ <li>An alternate form of -g is needed with optimization, e.g.
+ -g3.</li>
+ </ul>
+
+ <p>There will be no new ports added without -g (or a suitable
+ alternative) being set.</p>
+
+ <p>-g has not been arbitrarily added to the ports which do not
+ currently have it because we don't know if doing so would break the
+ build. However, any support issues with one of those port <em>will</em>
+ lead to the correct -g setting being determined and permanently
+ added.</p>
+
+ <p>Processors are fast enough (and disk space is cheap enough) that -g
+ should be automatic in all compilers with no way of turning it off, and
+ /bin/strip should be a symlink to /bin/true. Human life is too short to
+ deal with binaries built without -g. Such binaries should be a bad
+ memory of the days of KIPS processors and disks that costs several
+ dollars per kilobyte.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="6.3"><strong>6.3 Why don't you make c-client a shared
+ library?</strong></a></p>
+
+ <dl>
+ <dd>
+ All too often, shared libraries create far more problems than they
+ solve.
+
+ <p>Remember that you only gain the benefit of a shared library when
+ there are multiple applications which use that shared library. Even
+ without shared libraries, on most modern operating systems (and many
+ ancient ones too!) applications will share their text segments between
+ across multiple processes running the same application. This means that
+ if your system only runs one application (e.g. imapd) that uses the
+ c-client library, then you gain no benefit from making c-client a
+ shared library even if it has 100 imapd processes. You will, however
+ suffer added complexity.</p>
+
+ <p>If you have a server system that just runs imapd and ipop3d, then
+ making c-client a shared library will save just one copy of c-client no
+ matter how many IMAP/POP3 processes are running.</p>
+
+ <p>The problem with shared libraries is that you have to keep around a
+ copy of the library every time something changes in the library that
+ would affect the interface the library presents to the application. So,
+ you end up having many copies of the same shared library.</p>
+
+ <p>If you don't keep multiple copies of the shared library, then one of
+ two things happens. If there was proper versioning, then you'll get a
+ message such as "cannot open shared object file" or "minor versions
+ don't match" and the application won't run. Otherwise, the application
+ will run, but will fail in mysterious ways.</p>
+
+ <p>Several sites and third-party distributors have modified the
+ c-client makefile in order to make c-client be a shared library.
+ <em>When</em> (not <em>if</em>) a c-client based application fails in
+ mysterious ways because of a library compatibility problem, the result
+ is a bug report. A lot of time and effort ends up getting wasted
+ investigating such bug reports.</p>
+
+ <p>Memory is so cheap these days that it's not worth it. Human life is
+ too short to deal with shared library compatibility problems.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="6.4"><strong>6.4 Why don't you use iconv() for internationalization
+ support?</strong></a></p>
+
+ <dl>
+ <dd>iconv() is not ubiquitous enough.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="6.5"><strong>6.5 Why is the IMAP server connected to the home
+ directory by default?</strong></a></p>
+
+ <dl>
+ <dd>
+ The IMAP server has no way of knowing what you might call "mail" as
+ opposed to "some other file"; in fact, you can use IMAP to access any
+ file.
+
+ <p>The IMAP server also doesn't know whether your preferred
+ subdirectory for mailbox files is "mail/", ".mail/", "Mail/",
+ "Mailboxes/", or any of a zillion other possibilities. If one such name
+ were chosen, it would undoubtably anger the partisans of all the other
+ names.</p>
+
+ <p>It is possible to modify the software so that the default connected
+ directory is someplace else. Please read the file CONFIG for discussion
+ of this and other issues.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="6.6"><strong>6.6 I have a Windows system. Why isn't the server plug
+ and play for me?</strong></a></p>
+
+ <dl>
+ <dd>
+ There is no standard for how mail is stored on Windows; nor a single
+ standard SMTP server. The closest to either would be the SMTP server in
+ Microsoft's IIS.
+
+ <p>So there's no default by which to make assumptions. As the software
+ is set up, it assumes that the each user has an Windows login account
+ and private home directory, and that mail is stored on that home
+ directory as files in one of the popular UNIX formats. It also assumes
+ that there is some tool equivalent to inetd on UNIX that does the
+ TCP/IP listening and server startup.</p>
+
+ <p>Basically, unless you're an email software hacker, you probably want
+ to look elsewhere if you want IMAP/POP servers for Windows.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="6.7"><strong>6.7 I looked at the UNIX SSL code and saw that you have
+ the SSL data payload size set to 8192 bytes. SSL allows 16K; why aren't
+ you using the full size?</strong></a></p>
+
+ <dl>
+ <dd>
+ This is to avoid an interoperability problem with:
+
+ <ul>
+ <li>PC IMAP clients that use Microsoft's SChannel.DLL (SSPI) for SSL
+ support</li>
+
+ <li>Microsoft Exchange server (which also uses SChannel).</li>
+ </ul>
+
+ <p>SChannel has a bug that makes it think that the maximum SSL data
+ payload size is 16379 bytes -- 5 bytes too small. Thus, c-client has to
+ make sure that it never transmits full sized SSL packets.</p>
+
+ <p>The reason for using 8K (as opposed to, say, 16379 bytes, or 15K,
+ or...) is that it corresponds with the TCP buffer size that the
+ software uses elsewhere for input; there's a slight performance benefit
+ to having the two sizes correspond or at least be a multiple of each
+ other. Also, it keeps the size as a power of two, which might be
+ significant on some platforms.</p>
+
+ <p>There wasn't a significant difference that we could measure between
+ 8K and 15K.</p>
+
+ <p>Microsoft has developed a hotfix for this bug. Look up MSKB article
+ number 300562. Contrary to the article text which implies that this is
+ a Pine issue, this bug also affects Microsoft Exchange server with
+ <em>any</em> client that transmits full-sized SSL payloads.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="6.8"><strong>6.8 Why is an mh format INBOX called #mhinbox instead
+ of just INBOX?</strong></a></p>
+
+ <dl>
+ <dd>
+ It's a long story. In brief, the mh format driver is less functional
+ than any of the other drivers. It turned out that there were some users
+ (including high-level administrators) who tried mh years ago and no
+ longer use it, but still had an mh profile left behind.
+
+ <p>When the mh driver used INBOX, it would see the mh profile, and
+ proceed to move the user's INBOX into the mh format INBOX. This caused
+ considerable confusion as some things stopped working.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="6.9"><strong>6.9 Why don't you support the maildir
+ format?</strong></a></p>
+
+ <dl>
+ <dd>
+ It is technically difficult to support maildir in IMAP while
+ maintaining acceptable performance, robustness, following the
+ requirements of the IMAP protocol specification, and following the
+ requirements of maildir.
+
+ <p>No one has succeeded in accomplishing all four together. The various
+ maildir drivers offered as patches all have these problems. The problem
+ is exacerbated because this implementation supports multiple formats;
+ consequently this implementation can't make any performance shortcuts
+ by assuming that all the world is maildir.</p>
+
+ <p>We can't do a better job than the maildir fan community has done
+ with their maildir drivers. Similarly, if the maildir fan community
+ provides the maildir driver, they take on the responsibility for
+ answering maildir-specific support questions. This is as it should be,
+ and that is why maildir support is left to the maildir fan
+ community.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="6.10"><strong>6.10 Why don't you support the Cyrus
+ format?</strong></a></p>
+
+ <dl>
+ <dd>
+ There's no point to doing so. An implementation which supports multiple
+ formats will never do as well as one which is optimized to support one
+ single format.
+
+ <p>If you want to use Cyrus mailbox format, you should use the Cyrus
+ server, which is the native implementation of that format and is
+ specifically optimized for that format. That's also why Cyrus doesn't
+ implement any other format.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="6.11"><strong>6.11 Why is it creating extra forks on my SVR4
+ system?</strong></a></p>
+
+ <dl>
+ <dd>
+ This is because your system only has fcntl() style locking and not
+ flock() style locking. fcntl() locking has a design flaw that causes a
+ close() to release any locks made by that process on the file opened on
+ that file descriptor, even if the lock was made on a different file
+ descriptor.
+
+ <p>This design flaw causes unexpected loss of lock, and consequent
+ mailbox corruption. The workaround is to do certain "dangerous
+ operations" in another fork, thus avoiding doing a close() in the
+ vulnerable fork.</p>
+
+ <p>The best way to solve this problem is to upgrade your SVR4 (Solaris,
+ AIX, HP-UX, SGI) or OSF/1 system to a more advanced operating system,
+ such as Linux or BSD. These more advanced operating systems have
+ fcntl() locking for compatibility with SVR4, but also have flock()
+ locking.</p>
+
+ <p>Beware of certain SVR4 systems, such as AIX, which have an "flock()"
+ function in their C library that is just a jacket that does an fcntl()
+ lock. This is not a true flock(), and has the same design flaw as
+ fcntl().</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="6.12"><strong>6.12 Why are you so fussy about the date/time format in
+ the internal <code>"From&nbsp;"</code> line in traditional UNIX mailbox
+ files? My other mail program just considers every line that starts with
+ <code>"From&nbsp;"</code> to be the start of the message.</strong></a></p>
+
+ <dl>
+ <dd>
+ You just answered your own question. If any line that starts with
+ <code>"From&nbsp;"</code> is treated as the start of a message, then
+ every message text line which starts with <code>"From&nbsp;"</code> has
+ to be quoted (typically by prefixing a "&gt;" character). People
+ complain about this -- "why did a &gt; get stuck in my message?"
+
+ <p>So, good mail reading software only considers a line to be a
+ <code>"From&nbsp;"</code> line if it follows the actual specification
+ for a "From&nbsp;" line. This means, among other things, that the day of
+ week is fixed-format: <code>"May&nbsp;14"</code>, but
+ <code>"May&nbsp;&nbsp;7"</code> (note the extra space) as opposed to
+ <code>"May&nbsp;7"</code>. ctime() format for the date is the most
+ common, although POSIX also allows a numeric timezone after the
+ year. For compatibility with ancient software, the seconds are optional,
+ the timezone may appear before the year, the old 3-letter timezones are
+ also permitted, and "remote from xxx" may appear after the whole
+ thing.</p>
+
+ <p>Unfortunately, some software written by novices use other formats.
+ The most common error is to have a variable-width day of month, perhaps
+ in the erroneous belief that RFC 2822 (or RFC 822) defines the format of
+ the date/time in the <code>"From&nbsp;"</code> line (it doesn't; no RFC
+ describes internal formats). I've seen a few other goofs, such as a
+ single-digit second, but these are less common.</p>
+
+ <p>If you are writing your own software that writes mailbox files, and
+ you really aren't all that savvy with all the ins and outs and ancient
+ history, you should seriously consider using the c-client library (e.g.
+ routine mail_append()) instead of doing the file writes yourself. If
+ you must do it yourself, use ctime(), as in:</p>
+ <pre>
+ fprintf (mbx,"From %s@%h %s",user,host,ctime (time (0)));
+</pre>rather than try to figure out a good format yourself. ctime() is the
+most traditional format and nobody will flame you for using it.
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="6.13"><strong>6.13 Why is traditional UNIX format the default
+ format?</strong></a></p>
+
+ <dl>
+ <dd>Compatibility with the past 30 or so years of UNIX history. This
+ server is the only one that completely interoperates with legacy UNIX
+ mail tools.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="6.14"><strong>6.14 Why do you write this "DON'T DELETE THIS MESSAGE
+ -- FOLDER INTERNAL DATA" message at the start of traditional UNIX and
+ MMDF format mailboxes?</strong></a></p>
+
+ <dl>
+ <dd>
+ This pseudo-message serves two purposes.
+
+ <p>First, it establishes the mailbox format even when the mailbox has
+ no messages. Otherwise, a mailbox with no messages is a zero-byte file,
+ which could be one of several formats.</p>
+
+ <p>Second, it holds mailbox metadata used by IMAP: the UID validity,
+ the last assigned UID, and mailbox keywords. Without this metadata,
+ which must be preserved even when the mailbox has no messages, the
+ traditional UNIX format wouldn't be able to support the full
+ capabilities of IMAP.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="6.15"><strong>6.15 Why don't you stash the mailbox metadata in the
+ first real message of the mailbox instead of writing this fake FOLDER
+ INTERNAL DATA message?</strong></a></p>
+
+ <dl>
+ <dd>
+ In fact, that is what is done if the mailbox is non-empty and does not
+ already have a FOLDER INTERNAL DATA message.
+
+ <p>One problem with doing that is that if some external program removes
+ the first message, the metadata is lost and must be recreated, thus
+ losing any prior UID or keyword list status that IMAP clients may
+ depend upon.</p>
+
+ <p>Another problem is that this doesn't help if the last message is
+ deleted. This will result in an empty mailbox, and the necessity to
+ create a FOLDER INTERNAL DATA message.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="6.16"><strong>6.16 Why aren't "dual-use" mailboxes the
+ default?</strong></a></p>
+
+ <dl>
+ <dd>Compatibility with the past 30 or so years of UNIX history, not to
+ mention compatibility with user expectations when using shell tools.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="6.17"><strong>6.17 Why do you use ucbcc to build on
+ Solaris?</strong></a></p>
+
+ <dl>
+ <dd>
+ It is a long, long story about why cc is set to ucbcc. You need to
+ invoke the C compiler so that it links with the SVR4 libraries and not
+ the BSD libraries, otherwise readdir() will return the wrong
+ information.
+
+ <p>Of all the names in the most common path, ucbcc is the only name to
+ be found (on /usr/ccs/bin) that points to a suitable compiler. cc is
+ likely to be /usr/ucb/cc which is absolutely not the compiler that you
+ want. The real SVR4 cc is probably something like /opt/SUNWspro/bin/cc
+ which is rarely in anyone's path by default.</p>
+
+ <p>ucbcc is probably a link to acc, e.g. /opt/SUNWspro/SC4.0/bin/acc,
+ and is the UCB C compiler using the SVR4 libraries.</p>
+
+ <p>If ucbcc isn't on your system, then punt on the SUN C compiler and
+ use gcc instead (the gso port instead of the sol port).</p>
+
+ <p>If, in spite of all the above warnings, you choose to change "ucbcc"
+ to "cc", you will probably find that the -O2 needs to be changed to -O.
+ If you don't get any error messages with -O2, that's a pretty good
+ indicator that you goofed and are running the compiler that will link
+ with the BSD libraries.</p>
+
+ <p>To recap:</p>
+
+ <ul>
+ <li>The sol port is designed to be built using the UCB compiler using
+ the SVR4 libraries. This compiler is "ucbcc", which is lunk to acc.
+ You use -O2 as one of the CFLAGS.</li>
+
+ <li>If you build the sol port with the UCB compiler using the BSD
+ libraries, you will get no error messages but you will get bad
+ binaries (the most obvious symptom is dropping the first two
+ characters return filenames from the imapd LIST command. This
+ compiler also uses -O2, and is very often what the user gets from
+ "cc". <strong>BEWARE</strong></li>
+
+ <li>If you build the sol port with the real SVR4 compiler, which is
+ often hidden away or unavailable on many systems, then you will get
+ errors from -O2 and you need to change that to -O. But you will get a
+ good binary. However, you should try it with -O2 first, to make sure
+ that you got this compiler and not the UCB compiler using BSD
+ libraries.</li>
+ </ul>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="6.18"><strong>6.18 Why should I care about some old system with BSD
+ libraries? cc is the right thing on my Solaris system!</strong></a></p>
+
+ <dl>
+ <dd>
+ Because there still are sites that use such systems. On those systems,
+ the assumption that "cc" does the right thing will lead to corrupt
+ binaries with no error message or other warning that anything is amiss.
+
+ <p>Too many sites have fallen victim to this problem.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="6.19"><strong>6.19 Why do you insist upon writing .lock files in the
+ spool directory?</strong></a></p>
+
+ <dl>
+ <dd>Compatibility with the past 30 years of UNIX software which deals
+ with the spool directory, especially software which delivers mail.
+ Otherwise, it is possible to lose mail.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="6.20"><strong>6.20 Why should I care about compatibility with the
+ past?</strong></a></p>
+
+ <dl>
+ <dd>This is one of those questions in which the answer never convinces
+ those who ask it. Somehow, everybody who ever asks this question ends up
+ answering it for themselves as they get older, with the very answer that
+ they rejected years earlier.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><br></p>
+
+ <h2><a name="problems">7. Problems and Annoyances</a></h2>
+ <hr>
+
+ <p><a name="7.1"><strong>7.1 Help! My INBOX is empty! What happened to my
+ messages?</strong></a></p>
+
+ <dl>
+ <dd>
+ If you are seeing "0 messages" when you open INBOX and you know you
+ have messages there (and perhaps have looked at your mail spool file
+ and see that messages are there), then probably there is something
+ wrong with the very first line of your mail spool file. Make sure that
+ the first five bytes of the file are "From ", followed by an email
+ address and a date/time in ctime() format, e.g.:
+ <pre>
+ From fred@foo.bar Mon May 7 20:54:30 2001
+</pre>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.2"><strong>7.2 Help! All my messages in a non-INBOX mailbox have
+ been concatenated into one message which claims to be from me and has a
+ subject of the file name of the mailbox! What's going
+ on?</strong></a></p>
+
+ <dl>
+ <dd>
+ Something wrong with the very first line of the mailbox. Make sure that
+ the first five bytes of the file are "From ", followed by an email
+ address and a date/time in ctime() format, e.g.:
+ <pre>
+ From fred@foo.bar Mon May 7 20:54:30 2001
+</pre>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.3"><strong>7.3 Why do I get the message:</strong> <tt>CREATE
+ failed: Can't create mailbox node xxxxxxxxx: File exists</tt>
+ <strong>and how do I fix it?</strong></a></p>
+
+ <dl>
+ <dd>See the answer to the <a href="#1.8">Are hierarchical mailboxes
+ supported?</a> question.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.4"><strong>7.4 Why can't I log in to the server? The user name and
+ password are right!</strong></a></p>
+
+ <dl>
+ <dd>
+ There are a myriad number of possible answers to this question. The
+ only way to say for sure what is wrong is run the server under a
+ debugger such as gdb while root (yes, you must be root) with a
+ breakpoint at routines checkpw() and loginpw(), then single-step until
+ you see which test rejected you. The server isn't going to give any
+ error messages other than "login failed" in the name of not giving out
+ any unnecessary information to unauthorized individuals.
+
+ <p>Here are some of the more common reasons why login may fail:</p>
+
+ <ul>
+ <li>You didn't really give the correct user name and/or
+ password.</li>
+
+ <li>Your client doesn't send the LOGIN command correctly; for
+ example, IMAP2 clients won't send a password containing a "*"
+ correctly to an IMAP4 server.</li>
+
+ <li>If you have set up a CRAM-MD5 database, remember that the
+ password used is the one in the CRAM-MD5 database, and furthermore
+ that there must also be an entry in /etc/passwd (but the /etc/passwd
+ password is not used).</li>
+
+ <li>If you are using PAM, have you created a service file for the
+ server in /etc/pam.d?</li>
+
+ <li>If you are using shadow passwords, have you used an appropriate
+ port when building? In particular, note that "lnx" is for Linux
+ systems without shadow passwords; you probably want "slx" or "lnp"
+ instead.</li>
+
+ <li>If your system has account or password expirations, check to see
+ that the expiration date hasn't passed.</li>
+
+ <li>You can't log in as root or any other UID 0 user. This is for
+ your own safety, not to mention the fact that the servers use UID 0
+ as meaning "not logged in".</li>
+ </ul>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.5"><strong>7.5 Help! My load average is soaring and I see hundreds
+ of POP and IMAP servers, many logged in as the same
+ user!</strong></a></p>
+
+ <dl>
+ <dd>
+ Certain inferior losing GUI mail reading programs have a "synchronize
+ all mailboxes at startup" (IMAP) or "check for new mail every second"
+ (POP) feature which causes a rapid and unchecked spawning of servers.
+
+ <p>This is not a problem in the server; the client is really asking for
+ all those server sessions. Unfortunately, there isn't much that the POP
+ and IMAP servers can do about it; they don't spawned themselves.</p>
+
+ <p>Some sites have added code to record the number of server sessions
+ spawned per user per hour, and disable login for a user who has
+ exceeded a predetermined rate. This doesn't stop the servers from being
+ spawned; it just means that a server session will commit suicide a bit
+ faster.</p>
+
+ <p>Another possibility is to detect excessive server spawning activity
+ at the level where the server is spawned, which would be inetd or
+ possibly tcpd. The problem here is that this is a hard time to
+ quantify. 50 sessions in a minute from a multi-user timesharing system
+ may be perfectly alright, whereas 10 sessions a minute from a PC may be
+ too much.</p>
+
+ <p>The real solution is to fix the client configuration, by disabling
+ those evil features. Also tell the vendors of those clients how you
+ feel about distributing denial-of-service attack tools in the guise of
+ mail reading programs.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.6"><strong>7.6 Why does mail disappear even though I set "keep
+ mail on server"?</strong></a><br>
+ <a name="7.7"><strong>7.7 Why do I get the message</strong> <tt>Moved #####
+ bytes of new mail to /home/user/mbox from /var/spool/mail/user</tt>
+ <strong>and why did this happen?</strong></a></p>
+
+ <dl>
+ <dd>
+ This is probably caused by the mbox driver. If the file "mbox" exists
+ on the user's home directory and is in UNIX mailbox format, then when
+ INBOX is opened this file will be selected as INBOX instead of the mail
+ spool file. Messages will be automatically transferred from the mail
+ spool file into the mbox file.
+
+ <p>To disable this behavior, delete "mbox" from the EXTRADRIVERS list
+ in the top-level Makefile and rebuild. Note that if you do this, users
+ won't be able to access the messages that have already been moved to
+ mbox unless they open mbox instead of INBOX.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.8"><strong>7.8 Why isn't it showing the local host name as a
+ fully-qualified domain name?</strong></a><br>
+ <a name="7.9"><strong>7.9 Why is the local host name in the
+ From/Sender/Message-ID headers of outgoing mail not coming out as a
+ fully-qualified domain name?</strong></a></p>
+
+ <dl>
+ <dd>
+ Your UNIX system is misconfigured. The entry for your system in
+ /etc/hosts must have the fully-qualified domain name first, e.g.
+ <pre>
+ 105.69.1.234 myserver.example.com myserver
+</pre>
+
+ <p>A common mistake of novice system administrators is to have the
+ short name first, e.g.</p>
+ <pre>
+ 105.69.1.234 myserver myserver.example.com
+
+</pre>
+
+ <p>or to omit the fully qualified domain name entirely, e.g.</p>
+ <pre>
+ 105.69.1.234 myserver
+</pre>
+
+ <p>The result of this is that when the IMAP toolkit does a
+ gethostbyname() call to get the fully-qualified domain name, it would
+ get "myserver" instead of "myserver.example.com".</p>
+
+ <p>On some systems, a configuration file (typically named
+ /etc/svc.conf, /etc/netsvc.conf, or /etc/nsswitch.conf) can be used to
+ configure the system to use the domain name system (DNS) instead of
+ /etc/hosts, so it doesn't matter if /etc/hosts is misconfigured.</p>
+
+ <p>Check the man pages for gethostbyname, hosts, svc, and/or netsvc for
+ more information.</p>
+
+ <p>Unfortunately, certain vendors, most notably SUN, have failed to
+ make this clear in their documentation. Most of SUN's documentation
+ assumes a corporate network that is not connected to the Internet.</p>
+
+ <p>net.folklore once (late 1980s) held that the proper procedure was to
+ append the results of getdomainname() to the name returned by
+ gethostname(), and some versions of sendmail configuration files were
+ distributed that did this. This was incorrect; the string returned from
+ getdomainname() is the Yellow Pages (a.k.a NIS) domain name, which is a
+ completely different (albeit unfortunately named) entity from an
+ Internet domain. These were often fortuitously the same string, except
+ when they weren't. Frequently, this would result in host names with
+ spuriously doubled domain names, e.g.</p>
+ <pre>
+ myserver.example.com.example.com
+
+</pre>
+
+ <p>This practice has been thoroughly discredited for many years, but
+ folklore dies hard.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.10"><strong>7.10 What does the message:</strong> <tt>Mailbox
+ vulnerable - directory /var/spool/mail must have 1777 protection</tt>
+ <strong>mean? How can I fix this?</strong></a></p>
+
+ <dl>
+ <dd>
+ In order to update a mailbox in the default UNIX format, it is
+ necessary to create a lock file to prevent the mailer from delivering
+ mail while an update is in progress. Some systems use a directory
+ protection of 775, requiring that all mail handling programs be setgid
+ mail; or of 755, requiring that all mail handling programs be setuid
+ root.
+
+ <p>The IMAP toolkit does not run with any special privileges, and I
+ plan to keep it that way. It is antithetical to the concept of a
+ toolkit if users can't write their own programs to use it. Also, I've
+ had enough bad experiences with security bugs while running privileged;
+ the IMAP and POP servers have to be root when not logged in, in order
+ to be able to log themselves in. I don't want to go any deeper down
+ that slippery slope.</p>
+
+ <p>Directory protection 1777 is secure enough on most well-managed
+ systems. If you can't trust your users with a 1777 mail spool (petty
+ harassment is about the limit of the abuse exposure), then you have
+ much worse problems then that.</p>
+
+ <p>If you absolutely insist upon requiring privileges to create a lock
+ file, external file locking can be done via a setgid mail program named
+ /etc/mlock (this is defined by LOCKPGM in the c-client Makefile). If
+ the toolkit is unable to create a &lt;...mailbox...&gt;.lock file in
+ the directory by itself, it will try to call mlock to do it. I do not
+ recommend doing this for performance reasons.</p>
+
+ <p>A sample mlock program is included as part of imap-2007. We have
+ tried to make this sample program secure, but it has not been
+ thoroughly audited.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.11"><strong>7.11 What does the message:</strong> <tt>Mailbox is
+ open by another process, access is readonly</tt> <strong>mean? How do I
+ fix this?</strong></a></p>
+
+ <dl>
+ <dd>
+ A problem occurred in applying a lock to a /tmp lock file. Either some
+ other program has the mailbox open and won't relenquish it, or
+ something is wrong with the protection of /tmp or the lock.
+
+ <p>Make sure that the /tmp directory is protected 1777. Some security
+ scripts incorrectly set the protection of the /tmp directory to 775,
+ which disables /tmp for all non-privileged programs.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.12"><strong>7.12 What does the message:</strong> <tt>Can't get
+ write access to mailbox, access is readonly</tt>
+ <strong>mean?</strong></a></p>
+
+ <dl>
+ <dd>The mailbox file is write-protected against you.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.13"><strong>7.13 I set my POP3 client to "delete messages from
+ server" but they never get deleted. What is wrong?</strong></a></p>
+
+ <dl>
+ <dd>
+ Make sure that your mailbox is not read-only: that the mailbox is owned
+ by you and write enabled (protection 0600), and that the /tmp directory
+ is longer world-writeable. /tmp must be world-writeable because lots of
+ applications use it for scratch space. To fix this, do
+ <pre>
+
+ chmod 1777 /tmp
+</pre>as root.
+
+ <p>Make sure that your POP3 client issues a QUIT command when it
+ finishes. The POP3 protocol specifies that deletions are discarded
+ unless a proper QUIT is done.</p>
+
+ <p>Make sure that you are not opening multiple POP3 sessions to the
+ same mailbox. It is a requirement of the POP3 protocol than only one
+ POP3 session be in effect to a mailbox at a time, however some,
+ poorly-written POP3 clients violate this. Also, some background "check
+ for new mail" tasks also cause a violation. See the answer to the
+ <a href="#7.19">What does the syslog message: Killed (lost mailbox
+ lock) user=... host=... mean?</a> question for more details.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.14"><strong>7.14 What do messages such as:</strong></a></p>
+ <pre>
+ Message ... UID ... already has UID ...
+ Message ... UID ... less than ...
+ Message ... UID ... greater than last ...
+ Invalid UID ... in message ..., rebuilding UIDs
+</pre>
+
+ <p><strong>mean?</strong></p>
+
+ <dl>
+ <dd>
+ Something happened to corrupt the unique identifier regime in the
+ mailbox. In traditional UNIX-format mailboxes, this can happen if the
+ user deleted the "DO NOT DELETE" internal message.
+
+ <p>This problem is relatively harmless; a new valid unique identifier
+ regime will be created. The main effect is that any references to the
+ old UIDs will no longer be useful.</p>
+
+ <p>So, unless it is a chronic problem or you feel like debugging, you
+ can safely ignore these messages.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.15"><strong>7.15 What do the error messages:</strong></a></p>
+ <pre>
+ Unable to read internal header at ...
+ Unable to find CRLF at ...
+ Unable to parse internal header at ...
+ Unable to parse message date at ...
+ Unable to parse message flags at ...
+ Unable to parse message UID at ...
+ Unable to parse message size at ...
+ Last message (at ... ) runs past end of file ...
+</pre>
+
+ <p><strong>mean? I am using mbx format.</strong></p>
+
+ <dl>
+ <dd>
+ The mbx-format mailbox is corrupted and needs to be repaired.
+
+ <p>You should make an effort to find out why the corruption happened.
+ Was there an obvious system problem (crash or disk failure)? Did the
+ user accidentally access the file via NFS? Mailboxes don't get
+ corrupted by themselves; something caused the problem.</p>
+
+ <p>Some people have developed automated scripts, but if you're
+ comfortable using emacs it's pretty easy to fix it manually. Do
+ <em>not</em> use vi or any other editor unless you are certain that
+ editor can handle binary!!!</p>
+
+ <p>If you are not comfortable with emacs, or if the file is too large
+ to read with emacs, see the "step-by-step" technique later on for
+ another way of doing it.</p>
+
+ <p>After the word "at" in the error message is the byte position it got
+ to when it got unhappy with the file, e.g. if you see:</p>
+ <pre>
+ Unable to parse internal header at 43921: ne bombastic blurdybloop
+</pre>The problem occurs at the 43,931 byte in the file. That's the point you
+need to fix. c-client is expecting an internal header at that byte number,
+looking something like:
+ <pre>
+ 6-Jan-1998 17:42:24 -0800,1045;000000100001-00000001
+</pre>The format of this internal line is:
+ <pre>
+ dd-mmm-yyyy hh:mm:ss +zzzz,ssss;ffffffffFFFF-UUUUUUUU
+</pre>The only thing that is variable is the "ssss" field, it can be as many
+digits as needed. All other fields (inluding the "dd") are fixed width. So,
+the easiest thing to do is to look forward in the file for the next internal
+header, and delete everything from the error point to that internal header.
+
+ <p>Here's what to do if you want to be smarter and do a little bit more
+ work. Generally, you're in the middle of a message, and there's nothing
+ wrong with that message. The problem happened in the *previous*
+ message. So, search back to the previous internal header. Now, remember
+ that "ssss" field? That's the size of that message.</p>
+
+ <p>Mark where you are in the file, move the cursor to the line after
+ the internal header, and skip that many bytes ("ssss") forward. If
+ you're at the point of the error in the file, then that message is
+ corrupt. If you're at a different point, then perhaps the previous
+ message is corrupt and has a too long size count that "ate" into this
+ message.</p>
+
+ <p>Basically, what you need to do is make sure that all those size
+ counts are right, and that moving "ssss" bytes from the line after the
+ internal header will land you at another internal header.</p>
+
+ <p>Usually, once you know what you're looking at, it's pretty easy to
+ work out the corruption, and the best remedial action. Repair scripts
+ will make the problem go away but may not always do the smartest/best
+ salvage of the user's data. Manual repair is more flexible and usually
+ preferable.</p>
+
+ <p>Here is a step-by-step technique for fixing corrupt mbx files that's
+ a bit cruder than the procedure outlined above, but works for any size
+ file.</p>
+
+ <p>In this example, suppose that the corrupt file is INBOX, the error
+ message is</p>
+ <pre>
+ Unable to find CRLF at 132551754
+</pre>and the size of the INBOX file is 132867870 bytes.
+
+ <p>The first step is to split the mailbox file at the point of the
+ error:</p>
+
+ <ul>
+ <li>Rename the INBOX file to some other name, such as INBOX.bad.</li>
+
+ <li>Copy the first 132,551,754 bytes of INBOX.bad to another file,
+ such as INBOX.new.</li>
+
+ <li>Extract the trailing 316,116 bytes (132867870-132551754) of
+ INBOX.bad into another file, such as INBOX.tail.</li>
+
+ <li>You no longer need INBOX.bad. Delete it.</li>
+ </ul>In other words, use the number from the "Unable to find CRLF at"
+ as the point to split INBOX into two new files, INBOX.new and
+ INBOX.tail.
+
+ <p>Now, remove the erroneous data:</p>
+
+ <ul>
+ <li>Verify that you can open INBOX.new in IMAP or Pine.</li>
+
+ <li>The last message of INBOX.new is probably corrupted. Copy it to
+ another file, such as badmsg.1, then delete and expunge that last
+ message from INBOX.new</li>
+
+ <li>Locate the first occurance of text in INBOX.tail which looks like
+ an internal header, as described above.</li>
+
+ <li>Remove all the text which occurs prior to that point, and place
+ it into another file, such as badmsg.2. Note that in the case of a
+ single digit date, there is a leading space which must not be removed
+ (e.g. " 6-Nov-2001" not "6-Nov-2001").</li>
+ </ul>
+
+ <p>Reassemble the mailbox:</p>
+
+ <ul>
+ <li>Append INBOX.tail to INBOX.new.</li>
+
+ <li>You no longer need INBOX.tail. Delete it.</li>
+
+ <li>Verify that you can open INBOX.new in IMAP or Pine.</li>
+ </ul>
+
+ <p>Reinstall INBOX.new as INBOX:</p>
+
+ <ul>
+ <li>Check to see if you have received any new messages while
+ repairing INBOX.</li>
+
+ <li>If you haven't received any new messages while repairing INBOX,
+ just rename INBOX.new to INBOX.</li>
+
+ <li>If you have received new messages, be sure to copy the new
+ messages from INBOX to INBOX.new before doing the rename.</li>
+ </ul>
+
+ <p>You now have a working INBOX, as well as two files with corrupted
+ data (badmsg.1 and badmsg.2). There may be some useful data in the two
+ badmsg files that you might want to try salvaging; otherwise you can
+ delete the two badmsg files.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.16"><strong>7.16 What do the syslog messages:</strong></a></p>
+ <pre>
+
+ imap/tcp server failing (looping)
+ pop3/tcp server failing (looping)
+</pre>
+
+ <p><strong>mean? When it happens, the listed service shuts down. How can I
+ fix this?</strong></p>
+
+ <dl>
+ <dd>
+ The error message "server failing (looping), service terminated" is not
+ from either the IMAP or POP servers. Instead, it comes from inetd, the
+ daemon which listens for TCP connections to a number of servers,
+ including the IMAP and POP servers.
+
+ <p>inetd has a limit of 40 new server sessions per minute for any
+ particular service. If more than 40 sessions are initiated in a minute,
+ inetd will issue the "failing (looping), service terminated" message
+ and shut down the service for 10 minutes. inetd does this to prevent
+ system resource consumption by a client which is spawning infinite
+ numbers of servers. It should be noted that this is a denial of
+ service; however for some systems the alternative is a crash which
+ would be a worse denial of service!</p>
+
+ <p>For larger server systems, the limit of 40 is much too low. The
+ limit was established many years ago when a system typically only ran a
+ few dozen servers.</p>
+
+ <p>On some versions of inetd, such as the one distributed with most
+ versions of Linux, you can modify the <strong>/etc/inetd.conf</strong>
+ file to have a larger number of servers by appending a period followed
+ by a number after the <strong>nowait</strong> word for the server
+ entry. For example, if your existing /etc/inetd.conf line reads:</p>
+ <pre>
+ imap stream tcp nowait root /usr/etc/imapd imapd
+</pre>try changing it to be:
+ <pre>
+ imap stream tcp nowait.100 root /usr/etc/imapd imapd
+</pre>Another example (using TCP wrappers):
+ <pre>
+ imap stream tcp nowait root /usr/sbin/tcpd imapd
+</pre>try changing it to be:
+ <pre>
+ imap stream tcp nowait.100 root /usr/sbin/tcpd imapd
+
+</pre>to increase the limit to 100 sessions/minute.
+
+ <p>Before making this change, please read the information in "man
+ inetd" to determine whether or not your inetd has this feature. If it
+ does not, and you make this change, the likely outcome is that you will
+ disable IMAP service entirely.</p>
+
+ <p>Another way to fix this problem is to edit the inetd.c source code
+ (provided by your UNIX system vendor) to set higher limits, rebuild
+ inetd, install the new binary, and reboot your system. This should only
+ be done by a UNIX system expert. In the inetd.c source code, the limits
+ <strong>TOOMANY</strong> (normally 40) is the maximum number of new
+ server sessions permitted per minute, and <strong>RETRYTIME</strong>
+ (normally 600) is the number of seconds inetd will shut down the server
+ after it exceeds TOOMANY.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.17"><strong>7.17 What does the syslog message:</strong>
+ <tt>Mailbox lock file /tmp/.600.1df3 open failure: Permission
+ denied</tt> <strong>mean?</strong></a></p>
+
+ <dl>
+ <dd>
+ This usually means that some "helpful" security script person has
+ protected /tmp so that it is no longer world-writeable. /tmp must be
+ world-writeable because lots of applications use it for scratch space.
+ To fix this, do
+ <pre>
+ chmod 1777 /tmp
+
+</pre>as root.
+
+ <p>If that isn't the answer, check the protection of the named file. If
+ it is something other than 666, then either someone is hacking or some
+ "helpful" person modified the code to have a different default lock
+ file protection.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.18"><strong>7.18 What do the syslog messages:</strong></a></p>
+ <pre>
+ Command stream end of file, while reading line user=... host=...
+ Command stream end of file, while reading char user=... host=...
+ Command stream end of file, while writing text user=... host=...
+</pre>
+
+ <p><strong>mean?</strong></p>
+
+ <dl>
+ <dd>
+ This message occurs when the session is disconnected without a proper
+ LOGOUT (IMAP) or QUIT (POP) command being received by the server first.
+
+ <p>In many cases, this is perfectly normal; many client implementations
+ are impolite and do this. Some programmers think this sort of rudeness
+ is "more efficient".</p>
+
+ <p>The condition could, however, indicate a client or network
+ connectivity problem. The server has no way of knowing whether there's
+ a problem or just a rude client, so it issues this message instead of a
+ Logout.</p>
+
+ <p>Certain inferior losing clients disconnect abruptly after a failed
+ login, and instead of saying that the login failed, just say that they
+ can't access the mailbox. They then complain to the system manager, who
+ looks in the syslog and finds this message. Not very helpful, eh? See
+ the answer to the <a href="#7.4">Why can't I log in to the server? The
+ user name and password are right!</a> question.</p>
+
+ <p>If the user isn't reporting a problem, you can probably ignore this
+ message.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.19"><strong>7.19 Why did my POP or IMAP session suddenly
+ disconnect? The syslog has the message:</strong> <tt>Killed (lost
+ mailbox lock) user=... host=...</tt></a></p>
+
+ <dl>
+ <dd>
+ This message only happens when either the traditional UNIX mailbox
+ format or MMDF format is in use. This format only allows one session to
+ have the mailbox open read/write at a time.
+
+ <p>The servers assume that if a second session attempts to open the
+ mailbox, that means that the first session is probably owned by an
+ abandoned client. The common scenario here is a user who leaves his
+ client running at the office, and then tries to read his mail from
+ home. Through an internal mechanism called <em>kiss of death</em>, the
+ second session requests the first session to kill itself. When the
+ first session receives the "kiss of death", it issues the "Killed (lost
+ mailbox lock)" syslog message and terminates. The second session then
+ seizes read/write access, and becomes the new "first" session.</p>
+
+ <p>Certain poorly-designed clients routinely open multiple sessions to
+ the same mailbox; the users of those clients tend to get this message a
+ lot.</p>
+
+ <p>Another cause of this message is a background "check for new mail"
+ task which does its work by opening a POP session to server every few
+ seconds. They do this because POP doesn't have a way to announce new
+ mail.</p>
+
+ <p>The solution to both situations is to replace the client with a good
+ online IMAP client such as Pine. Life is too short to waste on POP
+ clients and poorly-designed IMAP clients.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.20"><strong>7.20 Why does my IMAP client show all the files on the
+ system, recursively from the UNIX root directory?</strong></a><br>
+ <a name="7.21"><strong>7.21 Why does my IMAP client show all of my files,
+ recursively from my UNIX home directory?</strong></a></p>
+
+ <dl>
+ <dd>
+ A well-written client should only show one level of hierarchy and then
+ stop, awaiting explicit user action before going lower. However, some
+ poorly-designed clients will recursively list all files, which may be a
+ very long list (especially if you have symbolic links to directories
+ that create a loop in the filesystem graph!).
+
+ <p>This behavior has also been observed in some third-party c-client
+ drivers, including maildir drivers. Consequently, this problem has even
+ been observed in Pine. It is important to understand that this is not a
+ problem in Pine or c-client; it is a problem in the third-party driver.
+ A Pine built without that third-party driver will not have this
+ problem.</p>
+
+ <p>See also the answer to <a href="#7.73">Why does my IMAP client show
+ all my files in my home directory?</a></p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.22"><strong>7.22 Why does my IMAP client show that I have
+ mailboxes named "#mhinbox", "#mh", "#shared", "#ftp", "#news", and
+ "#public"?</strong></a></p>
+
+ <dl>
+ <dd>
+ These are IMAP namespace names. They represent other hierarchies in
+ which messages may exist. These hierarchies may not necessarily exist
+ on a server, but the namespace name is still in the namespace list in
+ order to mark it as reserved.
+
+ <p>A few poorly-designed clients display all namespace names as if they
+ were top-level mailboxes in a user's list of mailboxes, whether or not
+ they actually exist. This is a flaw in those clients.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.23"><strong>7.23 Why does my IMAP client show all my files in my
+ home directory?</strong></a></p>
+
+ <dl>
+ <dd>
+ As distributed, the IMAP server is connected to your home directory by
+ default. It has no way of knowing what you might call "mail" as opposed
+ to "some other file"; in fact, you can use IMAP to access any file.
+
+ <p>Most clients have an option to configure your connected directory on
+ the IMAP server. For example, in Pine you can specify this as the
+ "Path" in your folder-collection, e.g.</p>
+ <pre>
+ Nickname : Secondary Folders
+ Server : imap.example.com
+ Path : mail/
+ View :
+</pre>In this example, the user is connected to the "mail" subdirectory of
+his home directory.
+
+ <p>Other servers call this the "folder prefix" or similar term.</p>
+
+ <p>It is possible to modify the IMAP server so that all users are
+ automatically connected to some other directory, e.g. a subdirectory of
+ the user's home directory. Read the file CONFIG for more details.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.24"><strong>7.24 Why is there a long delay before I get connected
+ to the IMAP or POP server, no matter what client I use?</strong></a></p>
+
+ <dl>
+ <dd>
+ There are two common occurances of this problem:
+
+ <ul>
+ <li>You are running a system (e.g. certain versions of Linux) which
+ by default attempts to connect to an "IDENT" protocol (port 113)
+ server on your client. However, a firewall or NAT box is blocking
+ connections to that port, so the connection attempt times out.
+
+ <p>The IDENT protocol is a well-known bad idea that does not
+ deliver any real security but causes incredible problems. The idea
+ is that this will give the server a record of the user name, or at
+ least what some program listening on port 113 says is the user
+ name. So, if somebody coming from port nnnnn on a system does
+ something bad, IDENT may give you the userid of the bad guy.</p>
+
+ <p>The problem is, IDENT is only meaningful on a timesharing system
+ which has an administrator who is privileged and users who are not.
+ It is of no value on a personal system which has no separate
+ concept of "system administrator" vs. "unprivileged user".</p>
+
+ <p>On either type of system, security-minded people either turn
+ IDENT off or replace it with an IDENT server that lies. Among other
+ things, IDENT gives spammers the ability to harvest email addresses
+ from anyone who connects to a web page.</p>
+
+ <p>This problem has been showing up quite frequently on systems
+ which use xinetd instead of inetd. Look for files named
+ /etc/xinetd.conf, /etc/xinetd.d/imapd, /etc/inetd.d/ipop2d, and
+ /etc/xinetd.d/ipop3d. In those files, look for lines containing
+ "USERID", e.g.</p>
+ <pre>
+ log_on_success += USERID
+</pre>Hunt down such lines, and delete them ruthlessly from all files in
+which they occur. Don't be shy about it.
+ </li>
+
+ <li>The DNS is taking a long time to do a reverse DNS (PTR record)
+ lookup of the IP address of your client. This is a problem in your
+ DNS, which either you or you ISP need to resolve. Ideally, the DNS
+ should return the client's name; but if it can't it should at least
+ return an error quickly.</li>
+ </ul>
+
+ <p>As you may have noticed, neither of these are actual problems in the
+ IMAP or POP servers; they are configuration issues with either your
+ system or your network infrastructure. If this is all new to you, run
+ (don't walk) to the nearest technical bookstore and get yourself a good
+ pedagogical text on system administration for the type of system you
+ are running.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.25"><strong>7.25 Why is there a long delay in Pine or any other
+ c-client based application call before I get connected to the IMAP
+ server? The hang seems to be in the c-client mail_open() call. I don't
+ have this problem with any other IMAP client. There is no delay
+ connecting to a POP3 or NNTP server with mail_open().</strong></a></p>
+
+ <dl>
+ <dd>
+ By default, the c-client library attempts to make a connection through
+ rsh (and ssh, if you enable that). If the command:
+ <pre>
+ rsh imapserver exec /etc/rimapd
+
+</pre>(or ssh if that is enabled) returns with a "* PREAUTH" response, it
+will use the resulting rsh session as the IMAP session and not require an
+authentication step on the server.
+
+ <p>Unfortunately, rsh has a design error that treats "TCP connection
+ refused" as "temporary failure, try again"; it expects the "rsh not
+ allowed" case to be implemented as a successful connection followed by
+ an error message and close the connection.</p>
+
+ <p>It must be emphasized that this is a bug in rsh. It is <em>not</em>
+ a bug in the IMAP toolkit.</p>
+
+ <p>The use of rsh can be disabled in any the following ways:</p>
+
+ <ul>
+ <li>You can disable it for this particular session by either:
+
+ <ul>
+ <li>setting an explicit port number in the mailbox name, e.g.
+ <pre>
+ {imapserver.foo.com:143}INBOX
+</pre>
+ </li>
+
+ <li>using SSL (the /ssl switch)</li>
+ </ul>
+ </li>
+
+ <li>You can disable rsh globally by setting the rsh timeout value to
+ 0 with the call:
+ <pre>
+ mail_parameters (NIL,SET_RSHTIMEOUT,0);
+</pre>
+ </li>
+ </ul>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.26"><strong>7.26 Why does a message sometimes get split into two
+ or more messages on my SUN system?</strong></a></p>
+
+ <dl>
+ <dd>
+ This is caused by an interaction of two independent design problems in
+ SUN mail software. The first problem is that the "forward message"
+ option in SUN's <em>mail tool</em> program includes the internal "From
+ " header line in the text that it forwarded. This internal header line
+ is specific to traditional UNIX mailbox files and is not suitable for
+ use in forwarded messages.
+
+ <p>The second problem is that the mail delivery agent assumes that mail
+ reading programs will not use the traditional UNIX mailbox format but
+ instead an incompatible variant that depends upon a
+ <em>Content-Length:</em> message header. Content-Length is widely
+ recognized to have been a terrible mistake, and is no longer
+ recommended for use in mail (it is used in other facilities that use
+ MIME).</p>
+
+ <p>One symptom of the problem is that under certain circumstances, a
+ message may get broken up into several messages. I'm also aware of
+ security bugs caused by programs that foolishly trust "Content-Length:"
+ headers with evil values.</p>
+
+ <p>To fix the mailer on your system, edit your sendmail.cf to change
+ the <strong>Mlocal</strong> line to have the <strong>-E</strong> flag.
+ A typical entry will lool like:</p>
+ <pre>
+ Mlocal, P=/usr/lib/mail.local, F=flsSDFMmnPE, S=10, R=20,
+ A=mail.local -d $u
+</pre>This fix will also work around the problem with mail tool, because it
+will insert a "&gt;" before the internal header line to prevent it from being
+interpreted by mail reading software as an internal header line.
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.27"><strong>7.27 Why did my POP or IMAP session suddenly
+ disconnect? The syslog has the message:</strong></a></p>
+ <pre>
+ Autologout user=&lt;...my user name...&gt; host=&lt;...my client system...&gt;
+
+</pre>
+
+ <dl>
+ <dd>
+ This is a problem in your client.
+
+ <p>In the case of IMAP, it failed to communicate with the IMAP server
+ for over 30 minutes; in the case of POP, it failed to communicate with
+ the POP server for over 10 minutes.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.28"><strong>7.28 What does the UNIX error message:</strong>
+ <tt>TLS/SSL failure: myserver: SSL negotiation failed</tt>
+ <strong>mean?</strong></a><br>
+ <a name="7.29"><strong>7.29 What does the PC error message:</strong>
+ <tt>TLS/SSL failure: myserver: Unexpected TCP input disconnect</tt>
+ <strong>mean?</strong></a></p>
+
+ <dl>
+ <dd>
+ This usually means that an attempt to negotiate TLS encryption via the
+ STARTTLS command failed, because the server advertises STARTTLS
+ functionality, but doesn't actually have it (e.g. because no
+ certificates are installed).
+
+ <p>Use the /notls option in the mailbox name to disable TLS
+ negotiation.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.30"><strong>7.30 What does the error message:</strong> <tt>TLS/SSL
+ failure: myserver: Server name does not match certificate</tt>
+ <strong>mean?</strong></a></p>
+
+ <dl>
+ <dd>
+ An SSL or TLS session encryption failed because the server name in the
+ server's certificate does not match the name that you gave it. This
+ could indicate that the server is not really the system you think that
+ it is, but can be also be called if you gave a nickname for the server
+ or name that was not fully-qualified. You must use the fully-qualified
+ domain name for the server in order to validate its certificate
+
+ <p>Use the /novalidate-cert option in the mailbox name to disable
+ validation of the certificate.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.31"><strong>7.31 What does the UNIX error message:</strong>
+ <tt>TLS/SSL failure: myserver: self-signed certificate</tt>
+ <strong>mean?</strong></a><br>
+ <a name="7.32"><strong>7.32 What does the PC error message:</strong>
+ <tt>TLS/SSL failure: myserver: Self-signed certificate or untrusted
+ authority</tt> <strong>mean?</strong></a></p>
+
+ <dl>
+ <dd>
+ An SSL or TLS session encryption failed because your server's
+ certificate is "self-signed"; that is, it is not signed by any
+ Certificate Authority (CA) and thus can not be validated. A CA-signed
+ certificate costs money, and some smaller sites either don't want to
+ pay for it or haven't gotten one yet. The bad part about this is that
+ this means there is no guarantee that the server is really the system
+ you think that it is.
+
+ <p>Use the /novalidate-cert option in the mailbox name to disable
+ validation of the certificate.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.33"><strong>7.33 What does the UNIX error message:</strong>
+ <tt>TLS/SSL failure: myserver: unable to get local issuer
+ certificate</tt> <strong>mean?</strong></a></p>
+
+ <dl>
+ <dd>
+ An SSL or TLS session encryption failed because your system does not
+ have the Certificate Authority (CA) certificates installed on OpenSSL's
+ certificates directory. On most systems, this directory is
+ /usr/local/ssl/certs). As a result, it is not possible to validate the
+ server's certificate.
+
+ <p>If CA certificates are properly installed, you should see
+ factory.pem and about a dozen other .pem names such as
+ thawteCb.pem.</p>
+
+ <p>As a workaround, you can use the /novalidate-cert option in the
+ mailbox name to disable validation of the certificate; however, note
+ that you are then vulnerable to various security attacks by bad
+ guys.</p>
+
+ <p>The correct fix is to copy all the files from the certs/ directory
+ in the OpenSSL distribution to the /usr/local/ssl/certs (or whatever)
+ directory. Note that you need to do this after building OpenSSL,
+ because the OpenSSL build creates a number of needed symbolic links.
+ For some bizarre reason, the OpenSSL "make install" doesn't do this for
+ you, so you must do it manually.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.34"><strong>7.34 Why does reading certain messages hang when using
+ Netscape? It works fine with Pine!</strong></a></p>
+
+ <dl>
+ <dd>
+ There are two possible causes.
+
+ <p>Check the mail syslog. If you see the message "Killed (lost mailbox
+ lock)" for the impacted user(s), read the FAQ entry regarding that
+ message.</p>
+
+ <p>Check the affected mailbox to see if there are embedded NUL
+ characters in the message. NULs in message texts are a technical
+ violation of both the message format and IMAP specifications. Most
+ clients don't care, but apparently Netscape does.</p>
+
+ <p>You can work around this by rebuilding imapd with the
+ <strong>NETSCAPE_BRAIN_DAMAGE</strong> option set (see
+ src/imapd/Makefile); this will cause imapd to convert all NULs to 0x80
+ characters. A better solution is to enable the feature in your MTA to
+ MIME-convert messages with binary content. See the documentation for
+ your MTA for how to do this.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.35"><strong>7.35 Why does Netscape say that there's a problem with
+ the IMAP server and that I should "Contact your mail server
+ administrator."?</strong></a></p>
+
+ <dl>
+ <dd>
+ Certain versions of Netscape do this when you click the Manage Mail
+ button, which uses an undocumented feature of Netscape's proprietary
+ IMAP server.
+
+ <p>You can work around this by rebuilding imapd with the
+ <strong>NETSCAPE_BRAIN_DAMAGE</strong> option set (see
+ src/imapd/Makefile) to a URL that points either to an alternative IMAP
+ client (e.g. Pine) or perhaps to a homebrew mail account management
+ page.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.36"><strong>7.36 Why is one user creating huge numbers of IMAP or
+ POP server sessions?</strong></a></p>
+
+ <dl>
+ <dd>The user is probably using Outlook Express, Eudora, or a similar
+ program. See the answer to the <a href="#7.5">Help! My load average is
+ soaring and I see hundreds of POP and IMAP servers, many logged in as the
+ same user!</a> question.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.37"><strong>7.37 Why don't I get any new mail notifications from
+ Outlook Express or Outlook after a while?</strong></a></p>
+
+ <dl>
+ <dd>
+ This is a known bug in Outlook Express. Microsoft is aware of the
+ problem and its cause. They have informed us that they do not have any
+ plans to fix it at the present time.
+
+ <p>The problem is also reported in Outlook 2000, but not verified.</p>
+
+ <p>Outlook Express uses the IMAP IDLE command to avoid having to "ping"
+ the server every few minutes for new mail. Unfortunately, Outlook
+ Express overlooks the part in the IDLE specification which requires
+ that a client terminate and restart the IDLE before the IMAP 30 minute
+ inactivity autologout timer triggers.</p>
+
+ <p>When this happens, Outlook Express displays "Not connected" at the
+ bottom of the window. Since it's no longer connected to the IMAP
+ server, it isn't going to notice any new mail.</p>
+
+ <p>As soon as the user does anything that would cause an IMAP
+ operation, Outlook Express will reconnect and new mail will flow again.
+ If the user does something that causes an IMAP operation at least every
+ 29 minutes, the problem won't happen.</p>
+
+ <p>Modern versions of imapd attempt to work around the problem by
+ automatically reporting fake new mail after 29 minutes. This causes
+ Outlook Express to exit the IDLE state; as soon as this happens imapd
+ revokes the fake new mail. As long as this behavior isn't known to
+ cause problems with other clients, this workaround will remain in
+ imapd.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.38"><strong>7.38 Why don't I get any new mail notifications from
+ Entourage?</strong></a></p>
+
+ <dl>
+ <dd>
+ This is a known bug in Entourage.
+
+ <p>You built an older version of imapd with the
+ <strong>MICROSOFT_BRAIN_DAMAGE</strong> option set, in order to disable
+ support for the IDLE command. However, Entourage won't get new mail
+ unless IDLE command support exists.</p>
+
+ <p>Note: the MICROSOFT_BRAIN_DAMAGE option no longer exists in modern
+ versions, as the Outlook Express problem which it attempted to solve
+ has been worked around in another way.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.39"><strong>7.39 Why doesn't Entourage work at
+ all?</strong></a></p>
+
+ <dl>
+ <dd>
+ It's hard to know. Entourage breaks almost every rule in the book for
+ IMAP. It is highly instructive to do a packet trace on Entourage, as an
+ example of how <em>not</em> to use IMAP. It does things like STATUS
+ (MESSAGES) on the currently selected mailbox and re-fetching the same
+ static data over and over again.
+
+ <p>It seems that every time we understand what it is doing wrong in
+ Entourage and come up with a workaround, we learn about something else
+ that's broken.</p>
+
+ <p>Try building imapd with the <strong>ENTOURAGE_BRAIN_DAMAGE</strong>
+ option set, in order to disable the diagnostic that occurs when doing
+ STATUS on the currently selected mailbox.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.40"><strong>7.40 Why doesn't Netscape Notify (NSNOTIFY.EXE) work
+ at all?</strong></a></p>
+
+ <dl>
+ <dd>
+ This is a bug in NSNOTIFY; it doesn't handle unsolicited data from the
+ server correctly.
+
+ <p>Fortunately, there is no reason to use this program with IMAP;
+ NSNOTIFY is a polling program to let you know when new mail has
+ appeared in your maildrop. This is necessary with POP; but since IMAP
+ dynamically announces new mail in the session you're better off (and
+ will actually cause less load on the server!) keeping your mail reading
+ program's IMAP session open and let IMAP do the notifying for you.</p>
+
+ <p>Consequently, the recommended fix for the NSNOTIFY problem is to
+ delete the NSNOTIFY binary.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.41"><strong>7.41 Why can't I connect via SSL to Eudora? It says
+ the connection has been broken, and in the server syslogs I see "Command
+ stream end of file".</strong></a></p>
+
+ <dl>
+ <dd>There is a report that you can fix the problem by going into Eudora's
+ advanced network configuration menu and increasing the network buffer
+ size to 8192.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.42"><strong>7.42 Sheesh. Aren't there <em>any</em> good IMAP
+ clients out there?</strong></a></p>
+
+ <dl>
+ <dd>
+ Yes!
+
+ <p>Pine is a <em>wonderful</em> client. It's fast, it uses IMAP well,
+ and it generates text mail (life is too short to waste on HTML mail).
+ Also, there are some really wonderful things in progress in the Pine
+ world.</p>
+
+ <p>There are some good GUI clients out there, mostly from smaller
+ vendors. Without naming names, look for the vendors who are active in
+ the IMAP protocol development community, and their products.</p>
+
+ <p>Netscape, Eudora, and Outlook <em>can</em> be configured with enough
+ effort to be good citizens and work well for users, <em>but</em> they
+ can also be badly misconfigured, and often the misconfiguration is the
+ default.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.43"><strong>7.43 But wait! PC Pine (or other PC program build with
+ c-client) crashes with the message</strong> <tt>incomplete SecBuffer
+ exceeds maximum buffer size</tt> <strong>when I use SSL connections.
+ This is a bug in c-client, right?</strong></a></p>
+
+ <dl>
+ <dd>
+ It's a bug in the Microsoft SChannel.DLL, which implements SSL.
+ Microsoft admits it (albeit with an unstatement: "it's not fully RFC
+ compliant"). The problem is that SChannel indicates that the maximum
+ SSL packet data size is 5 bytes smaller than the actual maximum. Thus,
+ any IMAP server which transmits a maximum sized SSL packet will not
+ work with PC Pine or any other program which uses SChannel.
+
+ <p>It can take a while for the problem to show up. The client has to do
+ something that causes at least 16K of contiguous data. Many clients do
+ partial fetching, which tends to reduce the number of cases where this
+ can happen. However, <em>all</em> software which uses SChannel to
+ support SSL is affected by this bug.</p>
+
+ <p>This problem does not affect UNIX code, since OpenSSL is used on
+ UNIX.</p>
+
+ <p>This problem most recently showed up with the CommunigatePro IMAP
+ server. They have an update which trims down their maximum contiguous
+ data to less than 16K, in order to work around the problem.</p>
+
+ <p>This problem has also shown up with the Exchange IMAP server with
+ UNIX clients (including Pine built with an older version of c-client)
+ which sends full-sized 16K SSL packets. Modern c-client works around
+ the problem by trimming down its maximum outgoing SSL packet size to
+ 8K.</p>
+
+ <p>Microsoft has developed a hotfix for this bug. Look up MSKB article
+ number 300562. Contrary to the article text which implies that this is
+ a Pine issue, this bug also affect Microsoft Exchange server with *any*
+ UNIX based client that transmits full-sized SSL payloads.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.44"><strong>7.44 My qpopper users keep on getting the DON'T DELETE
+ THIS MESSAGE -- FOLDER INTERNAL DATA if they also use Pine or IMAP. How
+ can I fix this?</strong></a></p>
+
+ <dl>
+ <dd>
+ This is an incompatibility between qpopper and the c-client library
+ used by Pine, imapd, and ipop[23]d.
+
+ <p>Assuming that you want to continue using qpopper, look into
+ qpopper's <strong>--enable-uw-kludge-flag</strong> configuration flag,
+ which is documented as "check for and hide UW 'Folder Internal Data'
+ messages".</p>
+
+ <p>The other alternative is to switch from qpopper to ipop3d.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.45"><strong>7.45 Help! I installed the servers but I can't connect
+ to them from my client!</strong></a></p>
+
+ <dl>
+ <dd>
+ Review the installation instructions carefully. Make sure that you have
+ not skipped any of the steps. Make sure that you have made the correct
+ entries in the configuration files; pay careful attention to the exact
+ spelling of the service names and the path names. Make sure as well
+ that you have properly restarted inetd.
+
+ <p>If you have a system with Yellow Pages/NIS such as Solaris, have you
+ updated the service names there as well as in /etc/services?</p>
+
+ <p>If you have a system with TCP wrappers, have you properly updated
+ the TCP wrapper files (e.g. /etc/hosts.allow and /etc/hosts.deny) for
+ the servers?</p>
+
+ <p>If you have a system which uses xinetd instead of inetd, have you
+ made sure that you have made the correct corresponding xinetd changes
+ for those services?</p>
+
+ <p>Try telneting to the server port (143 for IMAP, 110 for POP3). If
+ you get a "refused" error, that probably means that you don't have the
+ service set up in inetd.conf. If the connection opens and then closes
+ with no message, the service is set up, but either the path name of the
+ server binary in inetd.conf is wrong or your TCP wrappers are
+ configured to deny access.</p>
+
+ <p>If you don't know how to make the corresponding changes to these
+ files, seek the help of a local expert for your system.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.46"><strong>7.46 Why do I get the message</strong> <tt>Can not
+ authenticate to SMTP server: 421 SMTP connection went away!</tt>
+ <strong>and why did this happen? There was also something about</strong>
+ <tt>SECURITY PROBLEM: insecure server advertised AUTH=PLAIN</tt></a></p>
+
+ <dl>
+ <dd>
+ Some versions of qmail, including that running on mail.smtp.yahoo.com,
+ disconnect the SMTP session if you fail to authenticate prior to
+ attempting to transmit mail. An attempt to authenticate was made, but
+ it failed because the server had already disconnected.
+
+ <p>To work around this, you need to specify /user=... in the host name
+ specification.</p>
+
+ <p>The SECURITY PROBLEM came about because the server advertised the
+ AUTH=PLAIN SASL authentication mechanism outside of a TLS-encrypted
+ session, in violation of RFC 4616. This message is just a warning, and
+ in fact occurred after the server had disconnected.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.47"><strong>7.47 Why do I get the message</strong> <tt>SMTP
+ Authentication cancelled</tt> <strong>and why did this happen? There was
+ also something about</strong> <tt>SECURITY PROBLEM: insecure server
+ advertised AUTH=PLAIN</tt></a></p>
+
+ <dl>
+ <dd>
+ This is a bug in the SMTP server.
+
+ <p>Some versions of qmail, including that running on
+ mail.smtp.yahoo.com, have a bug in their implementation of SASL in
+ their SMTP server, which renders it non-compliant with the
+ standard.</p>
+
+ <p>If the client does not provide an initial response in the command
+ line for an authentication mechanism whose profile does not have an
+ initial challenge, qmail issues a bogus response:</p>
+ <pre>
+ 334 ok, go on
+</pre>The problem is the "ok, go on". This violates RFC 4954's requirement
+that the text part in a 334 response be a BASE64 encoded string; in other
+words, it is a protocol syntax error.
+
+ <p>In the case of AUTH=PLAIN, RFC 4422 (page 7) requires that the
+ encoded string have no data. In other words, the appropropiate
+ standards-compliant server response is "334" followed by a SPACE and a
+ CRLF.</p>
+
+ <p>The SECURITY PROBLEM came about because the server advertised the
+ AUTH=PLAIN SASL authentication mechanism outside of a TLS-encrypted
+ session, in violation of RFC 4616. This message is just a warning, and
+ is not related the "Authentication cancelled" problem.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="7.48"><strong>7.48 Why do I get the message</strong> <tt>Invalid
+ base64 string</tt> <strong>when I try to authenticate to a Cyrus
+ server?</strong></a></p>
+
+ <dl>
+ <dd>
+ This slightly misleading message is the way that a Cyrus server
+ indicates that an authentication exchange was cancelled. It is not
+ indicative of a bug or protocol violation.
+
+ <p>The most common reason that this happens is if the Cyrus server
+ offers Kerberos authentication, c-client is built with Kerberos
+ support, but your client system is not within the Kerberos realm. In
+ this case, the client code will try to authenticate via Kerberos, fail
+ to get the Kerberos credentials, cancel the authentication attempt, and
+ try the next available authentication technology.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><br></p>
+
+ <h2><a name="additional">8. Where to Go For Additional Information</a></h2>
+ <hr>
+
+ <p><a name="8.1"><strong>8.1 Where can I go to ask questions?</strong></a><br>
+ <a name="8.2"><strong>8.2 I have some ideas for enhancements to IMAP. Where
+ should I go?</strong></a></p>
+
+ <dl>
+ <dd>
+ If you have questions about the IMAP protocol, or want to participate
+ in discussions of future directions of the IMAP protocol, the
+ appropriate mailing list is imap-protocol@u.washington.edu. You can
+ subscribe to this list via <a href=
+ "mailto:imap-protocol-request@u.washington.edu"><tt>imap-protocol-request@u.washington.edu</tt></a>
+
+ <p>If you have questions about this software, you can send me email
+ directly or use the imap-uw@u.washington.edu mailing list. You can
+ subscribe to this list via <a href=
+ "mailto:imap-uw-request@u.washington.edu"><tt>imap-uw-request@u.washington.edu</tt></a></p>
+
+ <p>If you have general questions about the use of IMAP software
+ (not specific to the UW IMAP toolkit) use the
+ imap-use@u.washington.edu mailing list. You can subscribe to
+ this list via <a href=
+ "mailto:imap-use-request@u.washington.edu"><tt>imap-use-request@u.washington.edu</tt></a></p>
+
+ <p>You must be a subscriber to post to these lists. As an
+ alternative, you can use the
+ <strong>comp.mail.imap</strong> newsgroup.</p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="8.3"><strong>8.3 Where can I read more about IMAP and other email
+ protocols?</strong></a></p>
+
+ <dl>
+ <dd>We recommend <em>Internet Email Protocols: A Developer's Guide</em>,
+ by Kevin Johnson, published by Addison Wesley, ISBN 0-201-43288-9.</dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+ <hr>
+
+ <p><a name="8.4"><strong>8.4 Where can I find out more about setting up and
+ administering an IMAP server?</strong></a></p>
+
+ <dl>
+ <dd>
+ We recommend <em>Managing IMAP</em>, by Dianna Mullet &amp; Kevin
+ Mullet, published by O'Reilly, ISBN 0-596-00012-X.
+
+ <p>This book also has an excellent comparison of the UW and Cyrus IMAP
+ servers.<br></p>
+ </dd>
+ </dl>
+
+ <p><a href="#top">Back to top</a></p>
+
+ <p>Last Updated: 15 November 2007</p>
+
+<!--chtml include "//imap/incs/bottom.inc"-->
+
diff --git a/imap/docs/FAQ.txt b/imap/docs/FAQ.txt
new file mode 100644
index 00000000..797bed09
--- /dev/null
+++ b/imap/docs/FAQ.txt
@@ -0,0 +1,2993 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+ IMAP Toolkit Frequently Asked Questions
+
+Table of Contents
+
+ * 1. General/Software Feature Questions
+ + 1.1 Can I set up a POP or IMAP server on UNIX/Linux/OSF/etc.?
+ + 1.2 I am currently using qpopper as my POP3 server on UNIX.
+ Do I need to replace it with ipop3d in order to run imapd?
+ + 1.3 Can I set up a POP or IMAP server on Windows XP, 2000,
+ NT, Me, 98, or 95?
+ + 1.4 Can I set up a POP or IMAP server on Windows 3.1 or DOS?
+ + 1.5 Can I set up a POP or IMAP server on Macintosh?
+ + 1.6 Can I set up a POP or IMAP server on VAX/VMS?
+ + 1.7 Can I set up a POP or IMAP server on TOPS-20?
+ + 1.8 Are hierarchical mailboxes supported?
+ + 1.9 Are "dual-use" mailboxes supported?
+ + 1.10 Can I have a mailbox that has both messages and
+ sub-mailboxes?
+ + 1.11 What is the difference between "mailbox" and "folder"?
+ + 1.12 What is the status of internationalization?
+ + 1.13 Can I use SSL?
+ + 1.14 Can I use TLS and the STARTTLS facility?
+ + 1.15 Can I use CRAM-MD5 authentication?
+ + 1.16 Can I use APOP authentication?
+ + 1.17 Can I use Kerberos V5?
+ + 1.18 Can I use PAM for plaintext passwords?
+ + 1.19 Can I use Kerberos 5 for plaintext passwords?
+ + 1.20 Can I use AFS for plaintext passwords?
+ + 1.21 Can I use DCE for plaintext passwords?
+ + 1.22 Can I use the CRAM-MD5 database for plaintext passwords?
+ + 1.23 Can I disable plaintext passwords?
+ + 1.24 Can I disable plaintext passwords on unencrypted
+ sessions, but allow them on encrypted sessions?
+ + 1.25 Can I use virtual hosts?
+ + 1.26 Can I use RPOP authentication?
+ + 1.27 Can I use Kerberos V4?
+ + 1.28 Is there support for S/Key or OTP?
+ + 1.29 Is there support for NTLM or SPA?
+ + 1.30 Is there support for mh?
+ + 1.31 Is there support for qmail and the maildir format?
+ + 1.32 Is there support for the Cyrus mailbox format?
+ + 1.33 Is this software Y2K compliant?
+ * 2. What Do I Need to Build This Software?
+ + 2.1 What do I need to build this software with SSL on UNIX?
+ + 2.2 What do I need to build this software with Kerberos V on
+ UNIX?
+ + 2.3 What do I need to use a C++ compiler with this software
+ to build my own application?
+ + 2.4 What do I need to build this software on Windows?
+ + 2.5 What do I need to build this software on DOS?
+ + 2.6 Can't I use Borland C to build this software on the PC?
+ + 2.7 What do I need to build this software on the Mac?
+ + 2.8 What do I need to build this software on VMS?
+ + 2.9 What do I need to build this software on TOPS-20?
+ + 2.10 What do I need to build this software on Amiga or OS/2?
+ + 2.11 What do I need to build this software on Windows CE?
+ * 3. Build and Configuration Questions
+ + 3.1 How do I configure the IMAP and POP servers on UNIX?
+ + 3.2 I built and installed the servers according to the BUILD
+ instructions. It can't be that easy. Don't I need to write a
+ config file?
+ + 3.3 How do I make the IMAP and POP servers look for INBOX at
+ some place other than the mail spool directory?
+ + 3.4 How do I make the IMAP server look for secondary folders
+ at some place other than the user's home directory?
+ + 3.5 How do I configure SSL?
+ + 3.6 How do I configure TLS and the STARTTLS facility?
+ + 3.7 How do I build/install OpenSSL and obtain/create
+ certificates for use with SSL?
+ + 3.8 How do I configure CRAM-MD5 authentication?
+ + 3.9 How do I configure APOP authentication?
+ + 3.10 How do I configure Kerberos V5?
+ + 3.11 How do I configure PAM for plaintext passwords?
+ + 3.12 It looks like all I have to do to make the server use
+ Kerberos is to build with PAM on my Linux system, and set it
+ up in PAM for Kerberos passwords. Right?
+ + 3.13 How do I configure Kerberos 5 for plaintext passwords?
+ + 3.14 How do I configure AFS for plaintext passwords?
+ + 3.15 How do I configure DCE for plaintext passwords?
+ + 3.16 How do I configure the CRAM-MD5 database for plaintext
+ passwords?
+ + 3.17 How do I disable plaintext passwords?
+ + 3.18 How do I disable plaintext passwords on unencrypted
+ sessions, but allow them in SSL or TLS sessions?
+ + 3.19 How do I configure virtual hosts?
+ + 3.20 Why do I get compiler warning messages such as:
+ o passing arg 3 of `scandir' from incompatible pointer
+ type
+ o Pointers are not assignment-compatible.
+ o Argument #4 is not the correct type.
+ during the build?
+ + 3.21 Why do I get compiler warning messages such as
+ o Operation between types "void(*)(int)" and "void*" is
+ not allowed.
+ o Function argument assignment between types "void*" and
+ "void(*)(int)" is not allowed.
+ o Pointers are not assignment-compatible.
+ o Argument #5 is not the correct type.
+ during the build?
+ + 3.22 Why do I get linker warning messages such as:
+ o mtest.c:515: the `gets' function is dangerous and should
+ not be used.
+ during the build? Isn't this a security bug?
+ + 3.23 Why do I get linker warning messages such as:
+ o auth_ssl.c:92: the `tmpnam' function is dangerous and
+ should not be used.
+ during the build? Isn't this a security bug?
+ + 3.24 OK, suppose I see a warning message about a function
+ being "dangerous and should not be used" for something other
+ than this gets() or tmpnam() call?
+ * 4. Operational Questions
+ + 4.1 How can I enable anonymous IMAP logins?
+ + 4.2 How do I set up an alert message that each IMAP user will
+ see?
+ + 4.3 How does the c-client library choose which of its several
+ mechanisms to use to establish an IMAP connection to the
+ server? I noticed that it can connect on port 143, port 993,
+ via rsh, and via ssh.
+ + 4.4 I am using a TLS-capable IMAP server, so I don't need to
+ use /ssl to get encryption. However, I want to be certain
+ that my session is TLS encrypted before I send my password.
+ How to I do this?
+ + 4.5 How do I use one of the alternative formats described in
+ the formats.txt document? In particular, I hear that mbx
+ format will give me better performance and allow shared
+ access.
+ + 4.6 How do I set up shared mailboxes?
+ + 4.7 How can I make the server syslogs go to someplace other
+ than the mail syslog?
+ * 5. Security Questions
+ + 5.1 I see that the IMAP server allows access to arbitary
+ files on the system, including /etc/passwd! How do I disable
+ this?
+ + 5.2 I've heard that IMAP servers are insecure. Is this true?
+ + 5.3 How do I know that I have the most secure version of the
+ server?
+ + 5.4 I see all these strcpy() and sprintf() calls, those are
+ unsafe, aren't they?
+ + 5.5 Those /tmp lock files are protected 666, is that really
+ right?
+ * 6. Why Did You Do This Strange Thing? Questions
+ + 6.1 Why don't you use GNU autoconfig / automake /
+ autoblurdybloop?
+ + 6.2 Why do you insist upon a build with -g? Doesn't it waste
+ disk and memory space?
+ + 6.3 Why don't you make c-client a shared library?
+ + 6.4 Why don't you use iconv() for internationalization
+ support?
+ + 6.5 Why is the IMAP server connected to the home directory by
+ default?
+ + 6.6 I have a Windows system. Why isn't the server plug and
+ play for me?
+ + 6.7 I looked at the UNIX SSL code and saw that you have the
+ SSL data payload size set to 8192 bytes. SSL allows 16K; why
+ aren't you using the full size?
+ + 6.8 Why is an mh format INBOX called #mhinbox instead of just
+ INBOX?
+ + 6.9 Why don't you support the maildir format?
+ + 6.10 Why don't you support the Cyrus format?
+ + 6.11 Why is it creating extra forks on my SVR4 system?
+ + 6.12 Why are you so fussy about the date/time format in the
+ internal "From " line in traditional UNIX mailbox files? My
+ other mail program just considers every line that starts with
+ "From " to be the start of the message.
+ + 6.13 Why is traditional UNIX format the default format?
+ + 6.14 Why do you write this "DON'T DELETE THIS MESSAGE --
+ FOLDER INTERNAL DATA" message at the start of traditional
+ UNIX and MMDF format mailboxes?
+ + 6.15 Why don't you stash the mailbox metadata in the first
+ real message of the mailbox instead of writing this fake
+ FOLDER INTERNAL DATA message?
+ + 6.16 Why aren't "dual-use" mailboxes the default?
+ + 6.17 Why do you use ucbcc to build on Solaris?
+ + 6.18 Why should I care about some old system with BSD
+ libraries? cc is the right thing on my Solaris system!
+ + 6.19 Why do you insist upon writing .lock files in the spool
+ directory?
+ + 6.20 Why should I care about compatibility with the past?
+ * 7. Problems and Annoyances
+ + 7.1 Help! My INBOX is empty! What happened to my messages?
+ + 7.2 Help! All my messages in a non-INBOX mailbox have been
+ concatenated into one message which claims to be from me and
+ has a subject of the file name of the mailbox! What's going
+ on?
+ + 7.3 Why do I get the message:
+ o CREATE failed: Can't create mailbox node xxxxxxxxx: File
+ exists
+ and how do I fix it?
+ + 7.4 Why can't I log in to the server? The user name and
+ password are right!
+ + 7.5 Help! My load average is soaring and I see hundreds of
+ POP and IMAP servers, many logged in as the same user!
+ + 7.6 Why does mail disappear even though I set "keep mail on
+ server"?
+ + 7.7 Why do I get the message
+ o Moved ##### bytes of new mail to /home/user/mbox from
+ /var/spool/mail/user
+ and why did this happen?
+ + 7.8 Why isn't it showing the local host name as a
+ fully-qualified domain name?
+ + 7.9 Why is the local host name in the From/Sender/Message-ID
+ headers of outgoing mail not coming out as a fully-qualified
+ domain name?
+ + 7.10 What does the message:
+ o Mailbox vulnerable - directory /var/spool/mail must have
+ 1777 protection
+ mean? How can I fix this?
+ + 7.11 What does the message:
+ o Mailbox is open by another process, access is readonly
+ mean? How do I fix this?
+ + 7.12 What does the message:
+ o Can't get write access to mailbox, access is readonly
+ mean?
+ + 7.13 I set my POP3 client to "delete messages from server"
+ but they never get deleted. What is wrong?
+ + 7.14 What do messages such as:
+ o Message ... UID ... already has UID ...
+ o Message ... UID ... less than ...
+ o Message ... UID ... greater than last ...
+ o Invalid UID ... in message ..., rebuilding UIDs
+ mean?
+ + 7.15 What do the error messages:
+ o Unable to read internal header at ...
+ o Unable to find CRLF at ...
+ o Unable to parse internal header at ...
+ o Unable to parse message date at ...
+ o Unable to parse message flags at ...
+ o Unable to parse message UID at ...
+ o Unable to parse message size at ...
+ o Last message (at ... ) runs past end of file ...
+ mean? I am using mbx format.
+ + 7.16 What do the syslog messages:
+ o imap/tcp server failing (looping)
+ o pop3/tcp server failing (looping)
+ mean? When it happens, the listed service shuts down. How can
+ I fix this?
+ + 7.17 What does the syslog message:
+ o Mailbox lock file /tmp/.600.1df3 open failure:
+ Permission denied
+ mean?
+ + 7.18 What do the syslog messages:
+ o Command stream end of file, while reading line user=...
+ host=...
+ o Command stream end of file, while reading char user=...
+ host=...
+ o Command stream end of file, while writing text user=...
+ host=...
+ mean?
+ + 7.19 Why did my POP or IMAP session suddenly disconnect? The
+ syslog has the message:
+ o Killed (lost mailbox lock) user=... host=...
+ + 7.20 Why does my IMAP client show all the files on the
+ system, recursively from the UNIX root directory?
+ + 7.21 Why does my IMAP client show all of my files,
+ recursively from my UNIX home directory?
+ + 7.22 Why does my IMAP client show that I have mailboxes named
+ "#mhinbox", "#mh", "#shared", "#ftp", "#news", and "#public"?
+ + 7.23 Why does my IMAP client show all my files in my home
+ directory?
+ + 7.24 Why is there a long delay before I get connected to the
+ IMAP or POP server, no matter what client I use?
+ + 7.25 Why is there a long delay in Pine or any other c-client
+ based application call before I get connected to the IMAP
+ server? The hang seems to be in the c-client mail_open()
+ call. I don't have this problem with any other IMAP client.
+ There is no delay connecting to a POP3 or NNTP server with
+ mail_open().
+ + 7.26 Why does a message sometimes get split into two or more
+ messages on my SUN system?
+ + 7.27 Why did my POP or IMAP session suddenly disconnect? The
+ syslog has the message:
+ o Autologout user=<...my user name...> host=<...my imap
+ server...>
+ + 7.28 What does the UNIX error message:
+ o TLS/SSL failure: myserver: SSL negotiation failed
+ mean?
+ + 7.29 What does the PC error message:
+ o TLS/SSL failure: myserver: Unexpected TCP input
+ disconnect
+ mean?
+ + 7.30 What does the error message:
+ o TLS/SSL failure: myserver: Server name does not match
+ certificate
+ mean?
+ + 7.31 What does the UNIX error message:
+ o TLS/SSL failure: myserver: self-signed certificate
+ mean?
+ + 7.32 What does the PC error message
+ o TLS/SSL failure: myserver: Self-signed certificate or
+ untrusted authority
+ mean?
+ + 7.33 What does the UNIX error message:
+ o TLS/SSL failure: myserver: unable to get local issuer
+ certificate
+ mean?
+ + 7.34 Why does reading certain messages hang when using
+ Netscape? It works fine with Pine!
+ + 7.35 Why does Netscape say that there's a problem with the
+ IMAP server and that I should "Contact your mail server
+ administrator."?
+ + 7.36 Why is one user creating huge numbers of IMAP or POP
+ server sessions?
+ + 7.37 Why don't I get any new mail notifications from Outlook
+ Express or Outlook after a while?
+ + 7.38 Why don't I get any new mail notifications from
+ Entourage?
+ + 7.39 Why doesn't Entourage work at all?
+ + 7.40 Why doesn't Netscape Notify (NSNOTIFY.EXE) work at all?
+ + 7.41 Why can't I connect via SSL to Eudora? It says the
+ connection has been broken, and in the server syslogs I see
+ "Command stream end of file".
+ + 7.42 Sheesh. Aren't there any good IMAP clients out there?
+ + 7.43 But wait! PC Pine (or other PC program build with
+ c-client) crashes with the message
+ o incomplete SecBuffer exceeds maximum buffer size
+ when I use SSL connections. This is a bug in c-client, right?
+ + 7.44 My qpopper users keep on getting the DON'T DELETE THIS
+ MESSAGE -- FOLDER INTERNAL DATA if they also use Pine or
+ IMAP. How can I fix this?
+ + 7.45 Help! I installed the servers but I can't connect to
+ them from my client!
+ + 7.46 Why do I get the message
+ o Can not authenticate to SMTP server: 421 SMTP connection
+ went away!
+ and why did this happen? There was also something about
+ o SECURITY PROBLEM: insecure server advertised AUTH=PLAIN
+ + 7.47 Why do I get the message
+ o SMTP Authentication cancelled
+ and why did this happen? There was also something about
+ o SECURITY PROBLEM: insecure server advertised AUTH=PLAIN
+ + 7.48 Why do I get the message
+ o Invalid base64 string
+ when I try to authenticate to a Cyrus server?
+ * 8. Where to Go For Additional Information
+ + 8.1 Where can I go to ask questions?
+ + 8.2 I have some ideas for enhancements to IMAP. Where should
+ I go?
+ + 8.3 Where can I read more about IMAP and other email
+ protocols?
+ + 8.4 Where can I find out more about setting up and
+ administering an IMAP server?
+ _________________________________________________________________
+
+1. General/Software Feature Questions
+ _________________________________________________________________
+
+ 1.1 Can I set up a POP or IMAP server on UNIX/Linux/OSF/etc.?
+
+ Yes. Refer to the UNIX specific notes in files CONFIG and
+ BUILD.
+ _________________________________________________________________
+
+ 1.2 I am currently using qpopper as my POP3 server on UNIX. Do I need
+ to replace it with ipop3d in order to run imapd?
+
+ Not necessarily.
+
+ Although ipop3d interoperates with imapd better than qpopper,
+ imapd and qpopper will work together. The few qpopper/imapd
+ interoperability issues mostly affect users who use both IMAP
+ and POP3 clients; those users would probably be better served
+ if their POP3 server is ipop3d.
+
+ If you are happy with qpopper and just want to add imapd, you
+ should do that, and defer a decision on changing qpopper to
+ ipop3d. That way, you can get comfortable with imapd's
+ performance, without changing anything for your qpopper users.
+
+ Many sites have subsequently decided to change from qpopper to
+ ipop3d in order to get better POP3/IMAP interoperability. If
+ you need to do this, you'll know. There also seems to be a way
+ to make qpopper work better with imapd; see the answer to the
+ My qpopper users keep on getting the DON'T DELETE THIS MESSAGE
+ -- FOLDER INTERNAL DATA if they also use Pine or IMAP. How can
+ I fix this? question.
+ _________________________________________________________________
+
+ 1.3 Can I set up a POP or IMAP server on Windows XP, 2000, NT, Me, 98,
+ or 95?
+
+ Yes. Refer to the NT specific notes in files CONFIG and BUILD.
+ Also, for DOS-based versions of Windows (Windows Me, 98, and
+ 95) you *must* set up CRAM-MD5 authentication, as described in
+ md5.txt.
+
+ There is no file access control on Windows 9x or Me, so you
+ probably will have to do modifications to env_unix.c to prevent
+ people from hacking others' mail.
+
+ Note, however, that the server is not plug and play the way it
+ is for UNIX.
+ _________________________________________________________________
+
+ 1.4 Can I set up a POP or IMAP server on Windows 3.1 or DOS?
+ 1.5 Can I set up a POP or IMAP server on Macintosh?
+ 1.6 Can I set up a POP or IMAP server on VAX/VMS?
+
+ Yes, it's just a small matter of programming.
+ _________________________________________________________________
+
+ 1.7 Can I set up a POP or IMAP server on TOPS-20?
+
+ You have a TOPS-20 system? Cool.
+
+ If IMAP2 (RFC 1176) is good enough for you, you can use MAPSER
+ which is about the ultimate gonzo pure TOPS-20 extended
+ addressing assembly language program. Unfortunately, IMAP2 is
+ barely good enough for Pine these days, and most other IMAP
+ clients won't work with IMAP2 at all. Maybe someone will hack
+ MAPSER to do IMAP4rev1 some day.
+
+ We don't know if anyone wrote a POP3 server for TOPS-20. There
+ definitely was a POP2 server once upon a time.
+
+ Or you can port the POP and IMAP server from this IMAP toolkit
+ to it. All that you need for a first stab is to port the MTX
+ driver. That'll probably be just a couple of hours of hacking.
+ _________________________________________________________________
+
+ 1.8 Are hierarchical mailboxes supported?
+ 1.9 Are "dual-use" mailboxes supported?
+ 1.10 Can I have a mailbox that has both messages and sub-mailboxes?
+
+ Yes. However, there is one important caveat.
+
+ Some mailbox formats, including the default which is the
+ traditional UNIX mailbox format, are stored as a single file
+ containing all the messages. UNIX does not permit a name in the
+ filesystem to be both a file and a directory; consequently you
+ can not have a sub-mailbox within a mailbox that is in one of
+ these formats.
+
+ This is not a limitation of the software; this is a limitation
+ of UNIX. For example, there are mailbox formats in which the
+ name is a directory and each message is a file within that
+ directory; these formats support sub-mailboxes within such
+ mailboxes. However, for technical reasons, the "flat file"
+ formats are generally preferred since they perform better. Read
+ imap-2007/docs/formats.txt for more information on this topic.
+
+ It is always permissible to create a directory that is not a
+ mailbox, and have sub-mailboxes under it. The easiest way to
+ create a directory is to create a new mailbox inside a
+ directory that doesn't already exist. For example, if you
+ create "Mail/testbox" on UNIX, the directory "Mail/" will
+ automatically be created and then the mailbox "testbox" will be
+ created as a sub-mailbox of "Mail/".
+
+ It is also possible to create the name "Mail/" directly. Check
+ the documentation for your client software to see how to do
+ this with that software.
+
+ Of course, on Windows systems you would use "\" instead of "/".
+ _________________________________________________________________
+
+ 1.11 What is the difference between "mailbox" and "folder"?
+
+ The term "mailbox" is IMAP-speak for what a lot of software
+ calls a "folder" or a "mail folder". However, "folder" is often
+ used in other contexts to refer to a directory, for example, in
+ the graphic user interface on both Windows and Macintosh.
+
+ A "mailbox" is specifically defined as a named object that
+ contains messages. It is not required to be capable of
+ containing other types of objects including other mailboxes;
+ although some mailbox formats will permit this.
+
+ In IMAP-speak, a mailbox which can not contain other mailboxes
+ is called a "no-inferiors mailbox". Similarly, a directory
+ which can not contain messages is not a mailbox and is called a
+ "no-select name".
+ _________________________________________________________________
+
+ 1.12 What is the status of internationalization?
+
+ The IMAP toolkit is partially internationalized and
+ multilingualized.
+
+ Searching is supported in the following charsets: US-ASCII,
+ UTF-8, ISO-8859-1, ISO-8859-2, ISO-8859-3, ISO-8859-4,
+ ISO-8859-5, ISO-8859-6, ISO-8859-7, ISO-8859-8, ISO-8859-9,
+ ISO-8859-10, ISO-8859-11, ISO-8859-13, ISO-8859-14,
+ ISO-8859-15, ISO-8859-16, KOI8-R, KOI8-U (alias KOI8-RU),
+ TIS-620, VISCII, ISO-2022-JP, ISO-2022-KR, ISO-2022-CN,
+ ISO-2022-JP-1, ISO-2022-JP-2, GB2312 (alias CN-GB),
+ CN-GB-12345, BIG5 (alias CN-BIG5), EUC-JP, EUC-KR, Shift_JIS,
+ Shift-JIS, KS_C_5601-1987, KS_C_5601-1992, WINDOWS_874,
+ WINDOWS-1250, WINDOWS-1251, WINDOWS-1252, WINDOWS-1253,
+ WINDOWS-1254, WINDOWS-1255, WINDOWS-1256, WINDOWS-1257,
+ WINDOWS-1258.
+
+ All ISO-2022-?? charsets are treated identically, and support
+ ASCII, JIS Roman, hankaku katakana, ISO-8859-[1 - 10], TIS, GB
+ 2312, JIS X 0208, JIS X 0212, KSC 5601, and planes 1 and 2 of
+ CNS 11643.
+
+ EUC-JP includes support for JIS X 0212 and hankaku katakana.
+
+ c-client library support also exists to convert text in any of
+ the above charsets into Unicode, including headers with MIME
+ encoded-words.
+
+ There is no support for localization (e.g. non-English error
+ messages) at the present time, but such support is planned.
+ _________________________________________________________________
+
+ 1.13 Can I use SSL?
+
+ Yes. See the answer to the How do I configure SSL? question.
+ _________________________________________________________________
+
+ 1.14 Can I use TLS and the STARTTLS facility?
+
+ Yes. See the answer to the How do I configure TLS and the
+ STARTTLS facility? question.
+ _________________________________________________________________
+
+ 1.15 Can I use CRAM-MD5 authentication?
+
+ Yes. See the answer to the How do I configure CRAM-MD5
+ authentication? question.
+ _________________________________________________________________
+
+ 1.16 Can I use APOP authentication?
+
+ Yes. See the How do I configure APOP authentication? question.
+
+ Note that there is no client support for APOP authentication.
+ _________________________________________________________________
+
+ 1.17 Can I use Kerberos V5?
+
+ Yes. See the answer to the How do I configure Kerberos V5?
+ question.
+ _________________________________________________________________
+
+ 1.18 Can I use PAM for plaintext passwords?
+
+ Yes. See the answer to the How do I configure PAM for plaintext
+ passwords? question.
+ _________________________________________________________________
+
+ 1.19 Can I use Kerberos 5 for plaintext passwords?
+
+ Yes. See the answer to the How do I configure Kerberos 5 for
+ plaintext passwords? question.
+ _________________________________________________________________
+
+ 1.20 Can I use AFS for plaintext passwords?
+
+ Yes. See the answer to the How do I configure AFS for plaintext
+ passwords? question.
+ _________________________________________________________________
+
+ 1.21 Can I use DCE for plaintext passwords?
+
+ Yes. See the answer to the How do I configure DCE for plaintext
+ passwords? question.
+ _________________________________________________________________
+
+ 1.22 Can I use the CRAM-MD5 database for plaintext passwords?
+
+ Yes. See the answer to the How do I configure the CRAM-MD5
+ database for plaintext passwords? question.
+ _________________________________________________________________
+
+ 1.23 Can I disable plaintext passwords?
+
+ Yes. See the answer to the How do I disable plaintext
+ passwords? question.
+ _________________________________________________________________
+
+ 1.24 Can I disable plaintext passwords on unencrypted sessions, but
+ allow them on encrypted sessions?
+
+ Yes. See the answer to the How do I disable plaintext passwords
+ on unencrypted sessions, but allow them in SSL or TLS sessions?
+ question.
+ _________________________________________________________________
+
+ 1.25 Can I use virtual hosts?
+
+ Yes. See the answer to the How do I configure virtual hosts?
+ question.
+ _________________________________________________________________
+
+ 1.26 Can I use RPOP authentication?
+
+ There is no support for RPOP authentication.
+ _________________________________________________________________
+
+ 1.27 Can I use Kerberos V4?
+
+ Kerberos V4 is not supported. Kerberos V4 client-only
+ contributed code is available in
+
+ftp://ftp.cac.washington.edu/mail/kerberos4-patches.tar.Z
+
+ This is a patchkit which must be applied to the IMAP toolkit
+ according to the instructions in the patchkit's README. We can
+ not promise that this code works.
+ _________________________________________________________________
+
+ 1.28 Is there support for S/Key or OTP?
+
+ There is currently no support for S/Key or OTP. There may be an
+ OTP SASL authenticator available from third parties.
+ _________________________________________________________________
+
+ 1.29 Is there support for NTLM or SPA?
+
+ There is currently no support for NTLM or SPA, nor are there
+ any plans to add such support. In general, I avoid
+ vendor-specific mechanisms. I also believe that these
+ mechanisms are being deprecated by their vendor.
+
+ There may be an NTLM SASL authenticator available from third
+ parties.
+ _________________________________________________________________
+
+ 1.30 Is there support for mh?
+
+ Yes, but only as a legacy format. Your mh format INBOX is
+ accessed by the name "#mhinbox", and all other mh format
+ mailboxes are accessed by prefixing "#mh/" to the name, e.g.
+ "#mh/foo". The mh support uses the "Path:" entry in your
+ .mh_profile file to identify the root directory of your mh
+ format mailboxes.
+
+ Non-legacy use of mh format is not encouraged. There is no
+ support for permanent flags or unique identifiers; furthermore
+ there are known severe performance problems with the mh format.
+ _________________________________________________________________
+
+ 1.31 Is there support for qmail and the maildir format?
+
+ There is no support for qmail or the maildir format in our
+ distribution, nor are there any plans to add such support.
+ Maildir support may be available from third parties.
+ _________________________________________________________________
+
+ 1.32 Is there support for the Cyrus mailbox format?
+
+ No.
+ _________________________________________________________________
+
+ 1.33 Is this software Y2K compliant?
+
+ Please read the files Y2K and calendar.txt.
+ _________________________________________________________________
+
+2. What Do I Need to Build This Software?
+ _________________________________________________________________
+
+ 2.1 What do I need to build this software with SSL on UNIX?
+
+ You need to build and install OpenSSL first.
+ _________________________________________________________________
+
+ 2.2 What do I need to build this software with Kerberos V on UNIX?
+
+ You need to build and install MIT Kerberos first.
+ _________________________________________________________________
+
+ 2.3 What do I need to use a C++ compiler with this software to build
+ my own application?
+
+ If you are building an application using the c-client library,
+ use the new c-client.h file instead of including the other
+ include files. It seems that c-client.h should define away all
+ the troublesome names that conflict with C++.
+
+ If you use gcc, you may need to use -fno-operator-names as
+ well.
+ _________________________________________________________________
+
+ 2.4 What do I need to build this software on Windows?
+
+ You need Microsoft Visual C++ 6.0, Visual C++ .NET, or Visual
+ C# .NET (which you can buy from any computer store), along with
+ the Microsoft Platform SDK (which you can download from
+ Microsoft's web site).
+
+ You do not need to install the entire Platform SDK; it suffices
+ to install just the Core SDK and the Internet Development SDK.
+ _________________________________________________________________
+
+ 2.5 What do I need to build this software on DOS?
+
+ It's been several years since we last attempted to do this. At
+ the time, we used Microsoft C.
+ _________________________________________________________________
+
+ 2.6 Can't I use Borland C to build this software on the PC?
+
+ Probably not. If you know otherwise, please let us know.
+ _________________________________________________________________
+
+ 2.7 What do I need to build this software on the Mac?
+
+ It has been several years since we last attempted to do this.
+ At the time, we used Symantec THINK C; but today you'll need a
+ C compiler which allows segments to be more than 32K.
+ _________________________________________________________________
+
+ 2.8 What do I need to build this software on VMS?
+
+ You need the VMS C compiler, and either the Multinet or Netlib
+ TCP.
+ _________________________________________________________________
+
+ 2.9 What do I need to build this software on TOPS-20?
+
+ You need the TOPS-20 KCC compiler.
+ _________________________________________________________________
+
+ 2.10 What do I need to build this software on Amiga or OS/2?
+
+ We don't know.
+ _________________________________________________________________
+
+ 2.11 What do I need to build this software on Windows CE?
+
+ This port is incomplete. Someone needs to finish it.
+ _________________________________________________________________
+
+3. Build and Configuration Questions
+ _________________________________________________________________
+
+ 3.1 How do I configure the IMAP and POP servers on UNIX?
+ 3.2 I built and installed the servers according to the BUILD
+ instructions. It can't be that easy. Don't I need to write a config
+ file?
+
+ For ordinary "vanilla" UNIX systems, this software is plug and
+ play; just build it, install it, and you're done. If you have a
+ modified system, then you may want to do additional work; most
+ of this is to a single source code file (env_unix.c on UNIX
+ systems). Read the file CONFIG for more details.
+
+ Yes, it's that easy. There are some additional options, such as
+ SSL or Kerberos, which require additional steps to build. See
+ the relevant questions below.
+ _________________________________________________________________
+
+ 3.3 How do I make the IMAP and POP servers look for INBOX at some
+ place other than the mail spool directory?
+ 3.4 How do I make the IMAP server look for secondary folders at some
+ place other than the user's home directory?
+
+ Please read the file CONFIG for discussion of this and other
+ issues.
+ _________________________________________________________________
+
+ 3.5 How do I configure SSL?
+ 3.6 How do I configure TLS and the STARTTLS facility?
+
+ imap-2007 supports SSL and TLS client functionality on UNIX and
+ 32-bit Windows for IMAP, POP3, SMTP, and NNTP; and SSL and TLS
+ server functionality on UNIX for IMAP and POP3.
+
+ UNIX SSL build requires that a third-party software package,
+ OpenSSL, be installed on the system first. Read
+ imap-2007/docs/SSLBUILD for more information.
+
+ SSL is supported via undocumented Microsoft interfaces in
+ Windows 9x and NT4; and via standard interfaces in Windows
+ 2000, Windows Millenium, and Windows XP.
+ _________________________________________________________________
+
+ 3.7 How do I build/install OpenSSL and obtain/create certificates for
+ use with SSL?
+
+ If you need help in doing this, try the contacts mentioned in
+ the OpenSSL README. We do not offer support for OpenSSL or
+ certificates.
+ _________________________________________________________________
+
+ 3.8 How do I configure CRAM-MD5 authentication?
+ 3.9 How do I configure APOP authentication?
+
+ CRAM-MD5 authentication is enabled in the IMAP and POP3 client
+ code on all platforms. Read md5.txt to learn how to set up
+ CRAM-MD5 and APOP authentication on UNIX and NT servers.
+
+ There is no support for APOP client authentication.
+ _________________________________________________________________
+
+ 3.10 How do I configure Kerberos V5?
+
+ imap-2007 supports client and server functionality on UNIX and
+ 32-bit Windows.
+
+ Kerberos V5 is supported by default in Windows 2000 builds:
+
+ nmake -f makefile.w2k
+
+ Other builds require that a third-party Kerberos package, e.g.
+ MIT Kerberos, be installed on the system first.
+
+ To build with Kerberos V5 on UNIX, include
+ EXTRAAUTHENTICATORS=gss in the make command line, e.g.
+
+ make lnp EXTRAAUTHENTICATORS=gss
+
+ To build with Kerberos V5 on Windows 9x, Windows Millenium, and
+ NT4, use the "makefile.ntk" file instead of "makefile.nt":
+
+
+ nmake -f makefile.ntk
+ _________________________________________________________________
+
+ 3.11 How do I configure PAM for plaintext passwords?
+
+ On Linux systems, use the lnp port, e.g.
+
+ make lnp
+
+ On Solaris systems and other systems with defective PAM
+ implementations, build with PASSWDTYPE=pmb, e.g.
+
+ make sol PASSWDTYPE=pmb
+
+ On all other systems, build with PASSWDTYPE=pam, e.g
+
+ make foo PASSWDTYPE=pam
+
+ If you build with PASSWDTYPE=pam and authentication does not
+ work, try rebuilding (after a "make clean") with
+ PASSWDTYPE=pmb.
+ _________________________________________________________________
+
+ 3.12 It looks like all I have to do to make the server use Kerberos is
+ to build with PAM on my Linux system, and set it up in PAM for
+ Kerberos passwords. Right?
+
+ Yes and no.
+
+ Doing this will make plaintext password authentication use the
+ Kerberos password instead of the /etc/passwd password.
+
+ However, this will NOT give you Kerberos-secure authentication.
+ See the answer to the How do I configure Kerberos V5? question
+ for how to build with Kerberos-secure authentication.
+ _________________________________________________________________
+
+ 3.13 How do I configure Kerberos 5 for plaintext passwords?
+
+ Build with PASSWDTYPE=gss, e.g.
+
+ make sol PASSWDTYPE=gss
+
+ However, this will NOT give you Kerberos-secure authentication.
+ See the answer to the How do I configure Kerberos V5? question
+ for how to build with Kerberos-secure authentication.
+ _________________________________________________________________
+
+ 3.14 How do I configure AFS for plaintext passwords?
+
+ Build with PASSWDTYPE=afs, e.g
+
+ make sol PASSWDTYPE=afs
+ _________________________________________________________________
+
+ 3.15 How do I configure DCE for plaintext passwords?
+
+ Build with PASSWDTYPE=dce, e.g
+
+ make sol PASSWDTYPE=dce
+ _________________________________________________________________
+
+ 3.16 How do I configure the CRAM-MD5 database for plaintext passwords?
+
+ The CRAM-MD5 password database is automatically used for
+ plaintext password if it exists.
+
+ Note that this is NOT CRAM-MD5-secure authentication. You
+ probably want to consider disabling plaintext passwords for
+ non-SSL/TLS sessions. See the next two questions.
+ _________________________________________________________________
+
+ 3.17 How do I disable plaintext passwords?
+
+ Server-level plaintext passwords can be disabled by setting
+ PASSWDTYPE=nul, e.g.
+
+ make lnx EXTRAAUTHENTICATORS=gss PASSWDTYPE=nul
+
+ Note that you must have a CRAM-MD5 database installed or
+ specify at least one EXTRAAUTHENTICATOR, otherwise it will not
+ be possible to log in to the server.
+
+ When plaintext passwords are disabled, the IMAP server will
+ advertise the LOGINDISABLED capability and the POP3 server will
+ not advertise the USER capability.
+
+ 3.18 How do I disable plaintext passwords on unencrypted sessions, but
+ allow them in SSL or TLS sessions?
+
+ Do not set PASSWDTYPE=nul or SSLTYPE=unix. Set SSLTYPE=nopwd
+ instead, e.g.
+
+ make lnx SSLTYPE=nopwd
+
+ When plaintext passwords are disabled, the IMAP server will
+ advertise the LOGINDISABLED capability and the POP3 server will
+ not advertise the USER capability.
+
+ Plaintext passwords will always be enabled in SSL sessions; the
+ IMAP server will not advertise the LOGINDISABLED capability and
+ the POP3 server will advertise the USER capability.
+
+ If the client does a successful start-TLS in a non-SSL session,
+ plaintext passwords will be enabled, and a new CAPABILITY or
+ CAPA command (which is required after start-TLS) will show the
+ effect as in SSL sessions.
+ _________________________________________________________________
+
+ 3.19 How do I configure virtual hosts?
+
+ This is automatic, but with certain restrictions.
+
+ The most important one is that each virtual host must have its
+ own IP address; otherwise the server has no way of knowing
+ which virtual host is desired.
+
+ As distributed, the software uses a global password file; hence
+ user "fred" on one virtual host is "fred" on all virtual hosts.
+ You may want to modify the checkpw() routine to implement some
+ other policy (e.g. separate password files).
+
+ Note that the security model assumes that all users have their
+ own unique UNIX UID number. So if you use separate password
+ files you should make certain that the UID numbers do not
+ overlap between different files.
+
+ More advanced virtual host support may be available as patches
+ from third parties.
+ _________________________________________________________________
+
+ 3.20 Why do I get compiler warning messages such as:
+ passing arg 3 of `scandir' from incompatible pointer type
+ Pointers are not assignment-compatible.
+ Argument #4 is not the correct type.
+
+ during the build?
+
+ You can safely ignore these messages.
+
+ Over the years, the prototype for scandir() has changed, and
+ thus is variant across different UNIX platforms. In particular,
+ the definitions of the third argument (type select_t) and
+ fourth argument (type compar_t) have changed over the years,
+ the issue being whether or not the arguments to the functions
+ pointed to by these function pointers are of type const or not.
+
+ The way that c-client calls scandir() will tend to generate
+ these compiler warnings on newer systems such as Linux;
+ however, it will still build. The problem with fixing the call
+ is that then it won't build on older systems.
+ _________________________________________________________________
+
+ 3.21 Why do I get compiler warning messages such as
+ Operation between types "void(*)(int)" and "void*" is not allowed.
+ Function argument assignment between types "void*" and "void(*)(int)" is not a
+llowed.
+ Pointers are not assignment-compatible.
+ Argument #5 is not the correct type.
+
+ during the build?
+
+ You can safely ignore these messages.
+
+ All known systems have no problem with casting a function
+ pointer to/from a void* pointer, certain C compilers issue a
+ compiler diagnostic because this facility is listed as a
+ "Common extension" by the C standard:
+
+ K.5.7 Function pointer casts
+ [#1] A pointer to an object or to void may be cast to a pointer
+ to a function, allowing data to be invoked as a function (6.3.4).
+ [#2] A pointer to a function may be cast to a pointer to an
+ object or to void, allowing a function to be inspected or
+ modified (for example, by a debugger) (6.3.4).
+
+ It may be just a "common extension", but this facility is
+ relied upon heavily by c-client.
+ _________________________________________________________________
+
+ 3.22 Why do I get linker warning messages such as:
+mtest.c:515: the `gets' function is dangerous and should not be used.
+
+ during the build? Isn't this a security bug?
+
+ You can safely ignore this message.
+
+ Certain linkers, most notably on Linux, give this warning
+ message. It is indeed true that the traditional gets() function
+ is not a safe one.
+
+ However, the mtest program is only a demonstration program, a
+ model of a very basic application program using c-client. It is
+ not something that you would install, much less run in any
+ security-sensitive context.
+
+ mtest has numerous other shortcuts that you wouldn't want to do
+ in a real application program.
+
+ The only "security bug" with mtest would be if it was run by
+ some script in a security-sensitive context, but mtest isn't
+ particularly useful for such purposes. If you wanted to write a
+ script to automate some email task using c-client, you'd be
+ better off using imapd instead of mtest.
+
+ mtest only has two legitimate uses. It's a useful testbed for
+ me when debugging new versions of c-client, and it's useful as
+ a model for someone writing a simple c-client application to
+ see how the various calls work.
+
+ By the way, if you need a more advanced example of c-client
+ programming than mtest (and you probably will), I recommend
+ that you look at the source code for imapd and Pine.
+ _________________________________________________________________
+
+ 3.23 Why do I get linker warning messages such as:
+ auth_ssl.c:92: the `tmpnam' function is dangerous and should not be used.
+
+ during the build? Isn't this a security bug?
+
+ You can safely ignore this message.
+
+ Certain linkers, most notably on Linux, give this warning
+ message, based upon two known issues with tmpnam():
+
+ there can be a buffer overflow if an inadequate buffer is
+ allocated.
+ there can be a timing race caused by certain incautious
+ usage of the return value.
+
+ Neither of these issues applies in the particular use that is
+ made of tmpnam(). More importantly, the tmpnam() call is never
+ executed on Linux systems.
+ _________________________________________________________________
+
+ 3.24 OK, suppose I see a warning message about a function being
+ "dangerous and should not be used" for something other than this
+ gets() or tmpnam() call?
+
+ Please forward the details for investigation.
+ _________________________________________________________________
+
+4. Operational Questions
+ _________________________________________________________________
+
+ 4.1 How can I enable anonymous IMAP logins?
+
+ Create the file /etc/anonymous.newsgroups. At the present time,
+ this file should be empty. This will permit IMAP logins as
+ anonymous as well as the ANONYMOUS SASL authenticator.
+ Anonymous users have access to mailboxes in the #news., #ftp/,
+ and #public/ namespaces only.
+ _________________________________________________________________
+
+ 4.2 How do I set up an alert message that each IMAP user will see?
+
+ Create the file /etc/imapd.alert with the text of the message.
+ This text should be kept to one line if possible. Note that
+ this will cause an alert to every IMAP user every time they
+ initiate an IMAP session, so it should only be used for
+ critical messages.
+ _________________________________________________________________
+
+ 4.3 How does the c-client library choose which of its several
+ mechanisms to use to establish an IMAP connection to the server? I
+ noticed that it can connect on port 143, port 993, via rsh, and via
+ ssh.
+
+ c-client chooses how to establish an IMAP connection via the
+ following rules:
+
+ + If /ssl is specified, use an SSL connection. Fail otherwise.
+ + Else if client is a UNIX system and "ssh server exec
+ /etc/rimapd" works, use that
+ + Else if /tryssl is specified and an SSL connection works, use
+ that.
+ + Else if client is a UNIX system and "rsh server exec
+ /etc/rimapd" works, use that.
+ + Else use a non-SSL connection.
+ _________________________________________________________________
+
+ 4.4 I am using a TLS-capable IMAP server, so I don't need to use /ssl
+ to get encryption. However, I want to be certain that my session is
+ TLS encrypted before I send my password. How to I do this?
+
+ Use the /tls option in the mailbox name. This will cause an
+ error message and the connection to fail if the server does not
+ negotiate STARTTLS.
+ _________________________________________________________________
+
+ 4.5 How do I use one of the alternative formats described in the
+ formats.txt document? In particular, I hear that mbx format will give
+ me better performance and allow shared access.
+
+ The rumors about mbx format being preferred are true. It is
+ faster than the traditional UNIX mailbox format and permits
+ shared access.
+
+ However, and this is very important, note that using an
+ alternative mailbox format is an advanced facility, and only
+ expert users should undertake it. If you don't understand any
+ of the following notes, you may not be enough of an expert yet,
+ and are probably better off not going this route until you are
+ more comfortable with your understanding.
+
+ Some of the formats, including mbx, are only supported by the
+ software based on the c-client library, and are not recognized
+ by other mailbox programs. The "vi" editor will corrupt any mbx
+ format mailbox that it encounters.
+
+ Another problem is that the certain formats, including mbx, use
+ advanced file access and locking techniques that do not work
+ reliably with NFS. NFS is not a real filesystem. Use IMAP
+ instead of NFS for distributed access.
+
+ Each of the following steps are in escalating order of
+ involvement. The further you go down this list, the more deeply
+ committed you become:
+
+ + The simplest way to create a mbx-format mailbox is to prefix
+ the name with "#driver.mbx/" when creating a mailbox through
+ c-client. For example, if you create "#driver.mbx/foo", the
+ mailbox "foo" will be created in mbx format. Only use
+ "#driver.mbx/" when creating the mailbox. At all other times,
+ just use the name ("foo" in this example); the software will
+ automatically select the driver for mbx whenever that mailbox
+ is accessed without you doing anything else.
+ + You can use the "mailutil copy" command to copy an existing
+ mailbox to a new mailbox in mbx format. Read the man page
+ provided with the mailutil program for details.
+ + If you create an mbx-format INBOX, by creating
+ "#driver.mbx/INBOX" (note that "INBOX" must be all
+ uppercase), then subsequent access to INBOX by any c-client
+ based application will use the mbx-format INBOX. Any mail
+ delivered to the traditional format mailbox in the spool
+ directory (e.g. /var/spool/mail/$USER) will automatically be
+ copied into the mbx-format INBOX and the spool directory copy
+ removed.
+ + You can cause any newly-created mailboxes to be in mbx-format
+ by default by changing the definition of
+ CREATEPROTO=unixproto to be CREATEPROTO=mbxproto in
+ src/osdep/unix/Makefile, then rebuilding the IMAP toolkit (do
+ a "make clean" first). Do not change EMPTYPROTO, since mbx
+ format mailboxes are never a zero-byte file. If you use Pine
+ or the imap-utils, you should probably also rebuild them with
+ the new IMAP toolkit too.
+ + You can deliver directly to the mbx-format INBOX by use of
+ the tmail or dmail programs. tmail is for direct invocation
+ from sendmail (or whatever MTA program you use); dmail is for
+ calls from procmail. Both of these programs have man pages
+ which must be read carefully before making this change.
+
+ Most other servers (e.g. Cyrus) require use of a non-standard
+ format. A full-fledged format conversion is not significantly
+ different from what you have to do with other servers. The
+ difference, which makes format conversion procedures somewhat
+ more complicated with this server, is that there is no "all or
+ nothing" requirement with this server. There are many points in
+ between. A format conversion can be anything from a single
+ mailbox or single user, to systemwide.
+
+ This is good in that you can decide how far to go, or do the
+ steps incrementally as you become more comfortable with the
+ result. On the other hand, there's no "One True Way" which can
+ be boiled down to a simple set of pedagogical instructions.
+
+ A number of sites have done full-fledged format conversions,
+ and are reportedly quite happy with the results. Feel free to
+ ask in the comp.mail.imap newsgroup or the imap-uw mailing
+ list for advice or help.
+ _________________________________________________________________
+
+ 4.6 How do I set up shared mailboxes?
+
+ At the simplest level, a shared mailbox is one which has UNIX
+ file and directory protections which permit multiple users to
+ access it. What this means is that your existing skills and
+ tools to create and manage shared files on your UNIX system
+ apply to shared mailboxes; e.g.
+
+ chmod 666 mailbox
+
+ You may want to consider the use of a mailbox format which
+ permits multiple simultaneous read/write sessions, such as the
+ mbx format. The traditional UNIX format only allows one
+ read/write session to a mailbox at a time.
+
+ An additional convenience item are three system directories,
+ which can be set up for shared namespaces. These are: #ftp,
+ #shared, and #public, and are defined by creating the
+ associated UNIX users and home directories as described below.
+
+ #ftp/ refers to the anonymous ftp filesystem exported by the
+ ftp server, and is equivalent to the home directory for UNIX
+ user "ftp". For example, #ftp/foo/bar refers to the file
+ /foo/bar in the anonymous FTP filesystem, or ~ftp/foo/bar for
+ normal users. Anonymous FTP files are available to anonymous
+ IMAP logins. By default, newly-created files in #ftp/ are
+ protected 644.
+
+ #public/ refers to an IMAP toolkit convention called "public"
+ files, and is equivalent to the home directory for UNIX user
+ "imappublic". For example, #public/foo/bar refers to the file
+ ~imappublic/foo/bar. Public files are available to anonymous
+ IMAP logins. By default, newly-created files in #public are
+ created with protection 0666.
+
+ #shared/ refers to an IMAP toolkit convention called "shared"
+ files, and is equivalent to the home directory for UNIX user
+ "imapshared". For example, #shared/foo/bar refers to the file
+ ~imapshared/foo/bar. Shared files are not available to
+ anonymous IMAP logins. By default, newly-created files in
+ #shared are created with protection 0660.
+ _________________________________________________________________
+
+ 4.7 How can I make the server syslogs go to someplace other than the
+ mail syslog?
+
+ The openlog() call that sets the syslog facility is in
+ src/osdep/unix/env_unix.c in routine server_init(). You need to
+ edit this file to change the syslog facility from LOG_MAIL to
+ the facility you want, then rebuild. You also need to set up
+ your /etc/syslog.conf properly.
+
+ Refer to the man pages for syslog and syslogd for more
+ information on what the available syslog facilities are and how
+ to configure syslogs. If you still don't understand what to do,
+ find a UNIX system expert.
+ _________________________________________________________________
+
+5. Security Questions
+ _________________________________________________________________
+
+ 5.1 I see that the IMAP server allows access to arbitary files on the
+ system, including /etc/passwd! How do I disable this?
+
+ You should not worry about this if your IMAP users are allowed
+ shell access. The IMAP server does not permit any access that
+ the user can not have via the shell.
+
+ If, and only if, you deny your IMAP users shell access, you may
+ want to consider one of three choices. Note that these choices
+ reduce IMAP functionality, and may have undesirable side
+ effects. Each of these choices involves an edit to file
+ src/osdep/unix/env_unix.c
+
+ The first (and recommended) choice is to set restrictBox as
+ described in file CONFIG. This will disable access to the
+ filesystem root, to other users' home directory, and to
+ superior directory.
+
+ The second (and strongly NOT recommended) choice is to set
+ closedBox as described in file CONFIG. This puts each IMAP
+ session into a so-called "chroot jail", and thus setting this
+ option is extremely dangerous; it can make your system much
+ less secure and open to root compromise attacks. So do not use
+ this option unless you are absolutely certain that you
+ understand all the issues of a "chroot jail."
+
+ The third choice is to rewrite routine mailboxfile() to
+ implement whatever mapping from mailbox name to filesystem name
+ (and restrictions) that you wish. This is the most general
+ choice. As a guide, you can see at the start of routine
+ mailboxfile() what the restrictBox choice does.
+ _________________________________________________________________
+
+ 5.2 I've heard that IMAP servers are insecure. Is this true?
+
+ There are no known security problems in this version of the
+ IMAP toolkit, including the IMAP and POP servers. The IMAP and
+ POP servers limit what can be done while not logged in, and as
+ part of the login process discard all privileges except those
+ of the user.
+
+ As with other software packages, there have been buffer
+ overflow vulnerabilities in past versions. All known problems
+ of this nature are fixed in this version.
+
+ There is every reason to believe that the bad guys are engaged
+ in an ongoing effort to find vulnerabilities in the IMAP
+ toolkit. We look for such problems, and when one is found we
+ fix it.
+
+ It's unfortunate that any vulnerabilities existed in past
+ versions, and we're doing my best to keep the IMAP toolkit free
+ of vulnerabilities. No new vulnerabilities have been discovered
+ in quite a while, but efforts will not be relaxed.
+
+ Beware of vendors who claim that their implementations can not
+ have vulnerabilities.
+ _________________________________________________________________
+
+ 5.3 How do I know that I have the most secure version of the server?
+
+ The best way is to keep your server software up to date. The
+ bad guys are always looking for ways to crack software, and
+ when they find one, let all their friends know.
+
+ Oldtimers used to refer to a concept of software rot: if your
+ software hasn't been updated in a while, it would "rot" -- tend
+ to acquire problems that it didn't have when it was new.
+
+ The latest release version of the IMAP toolkit is always
+ available at ftp://ftp.cac.washington.edu/mail/imap.tar.Z
+ _________________________________________________________________
+
+ 5.4 I see all these strcpy() and sprintf() calls, those are unsafe,
+ aren't they?
+
+ Yes and no.
+
+ It can be unsafe to do these calls if you do not know that the
+ string being written will fit in the buffer. However, they are
+ perfectly safe if you do know that.
+
+ Beware of programmers who advocate doing a brute-force change
+ of all instances of
+
+ strcpy (s,t);
+
+ to
+
+ strncpy (s,t,n)[n] = '\0';
+
+ and similar measures in the name of "fixing all possible buffer
+ overflows."
+
+ There are examples in which a security bug was introduced
+ because of this type of "fix", due to the programmer using the
+ wrong value for n. In one case, the programmer thought that n
+ was larger than it actually was, causing a NUL to be written
+ out of the buffer; in another, n was too small, and a security
+ credential was truncated.
+
+ What is particularly ironic was that in both cases, the
+ original strcpy() was safe, because the size of the source
+ string was known to be safe.
+
+ With all this in mind, the software has been inspected, and it
+ is believed that all places where buffer overflows can happen
+ have been fixed. The strcpy()s that are still are in the code
+ occur after a size check was done in some other way.
+
+ Note that the common C idiom of
+
+ *s++ = c;
+
+ is just as vulnerable to buffer overflows. You can't cure
+ buffer overflows by outlawing certain functions, nor is it
+ desirable to do so; sometimes operations like strcpy()
+ translate into fast machine instructions for better
+ performance.
+
+ Nothing replaces careful study of code. That's how the bad guys
+ find bugs. Security is not accomplished by means of brute-force
+ shortcuts.
+ _________________________________________________________________
+
+ 5.5 Those /tmp lock files are protected 666, is that really right?
+
+ Yes. Shared mailboxes won't work otherwise. Also, you get into
+ accidental denial of service problems with old lock files left
+ lying around; this happens fairly frequently.
+
+ The deliberate mischief that can be caused by fiddling with the
+ lock files is small-scale; harassment level at most. There are
+ many -- and much more effective -- other ways of harassing
+ another user on UNIX. It's usually not difficult to determine
+ the culprit.
+
+ Before worrying about deliberate mischief, worry first about
+ things happening by accident!
+ _________________________________________________________________
+
+6. Why Did You Do This Strange Thing? Questions
+ _________________________________________________________________
+
+ 6.1 Why don't you use GNU autoconfig / automake / autoblurdybloop?
+
+ Autoconfig et al are not available on all the platforms where
+ the IMAP toolkit is supported; and do not work correctly on
+ some of the platforms where they do exist. Furthermore, these
+ programs add another layer of complexity to an already complex
+ process.
+
+ Coaxing software that uses autoconfig to build properly on
+ platforms which were not specifically considered by that
+ software wastes an inordinate amount of time. When (not if)
+ autoconfig fails to do the right thing, the result is an
+ inpenetrable morass to untangle in order to find the problem
+ and fix it.
+
+ The concept behind autoconfig is good, but the execution is
+ flawed. It rarely does the right thing on a platform that
+ wasn't specifically considered. Human life is too short to
+ debug autoconfig problems, especially since the current
+ mechanism is so much easier.
+ _________________________________________________________________
+
+ 6.2 Why do you insist upon a build with -g? Doesn't it waste disk and
+ memory space?
+
+ From time to time a submitted port has snuck in without -g.
+ This has always ended up causing problems. There are only two
+ valid excuses for not using -g in a port:
+
+ + The compiler does not support -g
+ + An alternate form of -g is needed with optimization, e.g.
+ -g3.
+
+ There will be no new ports added without -g (or a suitable
+ alternative) being set.
+
+ -g has not been arbitrarily added to the ports which do not
+ currently have it because we don't know if doing so would break
+ the build. However, any support issues with one of those port
+ will lead to the correct -g setting being determined and
+ permanently added.
+
+ Processors are fast enough (and disk space is cheap enough)
+ that -g should be automatic in all compilers with no way of
+ turning it off, and /bin/strip should be a symlink to
+ /bin/true. Human life is too short to deal with binaries built
+ without -g. Such binaries should be a bad memory of the days of
+ KIPS processors and disks that costs several dollars per
+ kilobyte.
+ _________________________________________________________________
+
+ 6.3 Why don't you make c-client a shared library?
+
+ All too often, shared libraries create far more problems than
+ they solve.
+
+ Remember that you only gain the benefit of a shared library
+ when there are multiple applications which use that shared
+ library. Even without shared libraries, on most modern
+ operating systems (and many ancient ones too!) applications
+ will share their text segments between across multiple
+ processes running the same application. This means that if your
+ system only runs one application (e.g. imapd) that uses the
+ c-client library, then you gain no benefit from making c-client
+ a shared library even if it has 100 imapd processes. You will,
+ however suffer added complexity.
+
+ If you have a server system that just runs imapd and ipop3d,
+ then making c-client a shared library will save just one copy
+ of c-client no matter how many IMAP/POP3 processes are running.
+
+ The problem with shared libraries is that you have to keep
+ around a copy of the library every time something changes in
+ the library that would affect the interface the library
+ presents to the application. So, you end up having many copies
+ of the same shared library.
+
+ If you don't keep multiple copies of the shared library, then
+ one of two things happens. If there was proper versioning, then
+ you'll get a message such as "cannot open shared object file"
+ or "minor versions don't match" and the application won't run.
+ Otherwise, the application will run, but will fail in
+ mysterious ways.
+
+ Several sites and third-party distributors have modified the
+ c-client makefile in order to make c-client be a shared
+ library. When (not if) a c-client based application fails in
+ mysterious ways because of a library compatibility problem, the
+ result is a bug report. A lot of time and effort ends up
+ getting wasted investigating such bug reports.
+
+ Memory is so cheap these days that it's not worth it. Human
+ life is too short to deal with shared library compatibility
+ problems.
+ _________________________________________________________________
+
+ 6.4 Why don't you use iconv() for internationalization support?
+
+ iconv() is not ubiquitous enough.
+ _________________________________________________________________
+
+ 6.5 Why is the IMAP server connected to the home directory by default?
+
+ The IMAP server has no way of knowing what you might call
+ "mail" as opposed to "some other file"; in fact, you can use
+ IMAP to access any file.
+
+ The IMAP server also doesn't know whether your preferred
+ subdirectory for mailbox files is "mail/", ".mail/", "Mail/",
+ "Mailboxes/", or any of a zillion other possibilities. If one
+ such name were chosen, it would undoubtably anger the partisans
+ of all the other names.
+
+ It is possible to modify the software so that the default
+ connected directory is someplace else. Please read the file
+ CONFIG for discussion of this and other issues.
+ _________________________________________________________________
+
+ 6.6 I have a Windows system. Why isn't the server plug and play for
+ me?
+
+ There is no standard for how mail is stored on Windows; nor a
+ single standard SMTP server. The closest to either would be the
+ SMTP server in Microsoft's IIS.
+
+ So there's no default by which to make assumptions. As the
+ software is set up, it assumes that the each user has an
+ Windows login account and private home directory, and that mail
+ is stored on that home directory as files in one of the popular
+ UNIX formats. It also assumes that there is some tool
+ equivalent to inetd on UNIX that does the TCP/IP listening and
+ server startup.
+
+ Basically, unless you're an email software hacker, you probably
+ want to look elsewhere if you want IMAP/POP servers for
+ Windows.
+ _________________________________________________________________
+
+ 6.7 I looked at the UNIX SSL code and saw that you have the SSL data
+ payload size set to 8192 bytes. SSL allows 16K; why aren't you using
+ the full size?
+
+ This is to avoid an interoperability problem with:
+
+ + PC IMAP clients that use Microsoft's SChannel.DLL (SSPI) for
+ SSL support
+ + Microsoft Exchange server (which also uses SChannel).
+
+ SChannel has a bug that makes it think that the maximum SSL
+ data payload size is 16379 bytes -- 5 bytes too small. Thus,
+ c-client has to make sure that it never transmits full sized
+ SSL packets.
+
+ The reason for using 8K (as opposed to, say, 16379 bytes, or
+ 15K, or...) is that it corresponds with the TCP buffer size
+ that the software uses elsewhere for input; there's a slight
+ performance benefit to having the two sizes correspond or at
+ least be a multiple of each other. Also, it keeps the size as a
+ power of two, which might be significant on some platforms.
+
+ There wasn't a significant difference that we could measure
+ between 8K and 15K.
+
+ Microsoft has developed a hotfix for this bug. Look up MSKB
+ article number 300562. Contrary to the article text which
+ implies that this is a Pine issue, this bug also affects
+ Microsoft Exchange server with any client that transmits
+ full-sized SSL payloads.
+ _________________________________________________________________
+
+ 6.8 Why is an mh format INBOX called #mhinbox instead of just INBOX?
+
+ It's a long story. In brief, the mh format driver is less
+ functional than any of the other drivers. It turned out that
+ there were some users (including high-level administrators) who
+ tried mh years ago and no longer use it, but still had an mh
+ profile left behind.
+
+ When the mh driver used INBOX, it would see the mh profile, and
+ proceed to move the user's INBOX into the mh format INBOX. This
+ caused considerable confusion as some things stopped working.
+ _________________________________________________________________
+
+ 6.9 Why don't you support the maildir format?
+
+ It is technically difficult to support maildir in IMAP while
+ maintaining acceptable performance, robustness, following the
+ requirements of the IMAP protocol specification, and following
+ the requirements of maildir.
+
+ No one has succeeded in accomplishing all four together. The
+ various maildir drivers offered as patches all have these
+ problems. The problem is exacerbated because this
+ implementation supports multiple formats; consequently this
+ implementation can't make any performance shortcuts by assuming
+ that all the world is maildir.
+
+ We can't do a better job than the maildir fan community has
+ done with their maildir drivers. Similarly, if the maildir fan
+ community provides the maildir driver, they take on the
+ responsibility for answering maildir-specific support
+ questions. This is as it should be, and that is why maildir
+ support is left to the maildir fan community.
+ _________________________________________________________________
+
+ 6.10 Why don't you support the Cyrus format?
+
+ There's no point to doing so. An implementation which supports
+ multiple formats will never do as well as one which is
+ optimized to support one single format.
+
+ If you want to use Cyrus mailbox format, you should use the
+ Cyrus server, which is the native implementation of that format
+ and is specifically optimized for that format. That's also why
+ Cyrus doesn't implement any other format.
+ _________________________________________________________________
+
+ 6.11 Why is it creating extra forks on my SVR4 system?
+
+ This is because your system only has fcntl() style locking and
+ not flock() style locking. fcntl() locking has a design flaw
+ that causes a close() to release any locks made by that process
+ on the file opened on that file descriptor, even if the lock
+ was made on a different file descriptor.
+
+ This design flaw causes unexpected loss of lock, and consequent
+ mailbox corruption. The workaround is to do certain "dangerous
+ operations" in another fork, thus avoiding doing a close() in
+ the vulnerable fork.
+
+ The best way to solve this problem is to upgrade your SVR4
+ (Solaris, AIX, HP-UX, SGI) or OSF/1 system to a more advanced
+ operating system, such as Linux or BSD. These more advanced
+ operating systems have fcntl() locking for compatibility with
+ SVR4, but also have flock() locking.
+
+ Beware of certain SVR4 systems, such as AIX, which have an
+ "flock()" function in their C library that is just a jacket
+ that does an fcntl() lock. This is not a true flock(), and has
+ the same design flaw as fcntl().
+ _________________________________________________________________
+
+ 6.12 Why are you so fussy about the date/time format in the internal
+ "From " line in traditional UNIX mailbox files? My other mail program
+ just considers every line that starts with "From " to be the start of
+ the message.
+
+ You just answered your own question. If any line that starts
+ with "From " is treated as the start of a message, then every
+ message text line which starts with "From " has to be quoted
+ (typically by prefixing a ">" character). People complain about
+ this -- "why did a > get stuck in my message?"
+
+ So, good mail reading software only considers a line to be a
+ "From " line if it follows the actual specification for a
+ "From " line. This means, among other things, that the day of
+ week is fixed-format: "May 14", but "May 7" (note the extra
+ space) as opposed to "May 7". ctime() format for the date is
+ the most common, although POSIX also allows a numeric timezone
+ after the year. For compatibility with ancient software, the
+ seconds are optional, the timezone may appear before the year,
+ the old 3-letter timezones are also permitted, and "remote from
+ xxx" may appear after the whole thing.
+
+ Unfortunately, some software written by novices use other
+ formats. The most common error is to have a variable-width day
+ of month, perhaps in the erroneous belief that RFC 2822 (or RFC
+ 822) defines the format of the date/time in the "From " line
+ (it doesn't; no RFC describes internal formats). I've seen a
+ few other goofs, such as a single-digit second, but these are
+ less common.
+
+ If you are writing your own software that writes mailbox files,
+ and you really aren't all that savvy with all the ins and outs
+ and ancient history, you should seriously consider using the
+ c-client library (e.g. routine mail_append()) instead of doing
+ the file writes yourself. If you must do it yourself, use
+ ctime(), as in:
+
+ fprintf (mbx,"From %s@%h %s",user,host,ctime (time (0)));
+
+ rather than try to figure out a good format yourself. ctime()
+ is the most traditional format and nobody will flame you for
+ using it.
+ _________________________________________________________________
+
+ 6.13 Why is traditional UNIX format the default format?
+
+ Compatibility with the past 30 or so years of UNIX history.
+ This server is the only one that completely interoperates with
+ legacy UNIX mail tools.
+ _________________________________________________________________
+
+ 6.14 Why do you write this "DON'T DELETE THIS MESSAGE -- FOLDER
+ INTERNAL DATA" message at the start of traditional UNIX and MMDF
+ format mailboxes?
+
+ This pseudo-message serves two purposes.
+
+ First, it establishes the mailbox format even when the mailbox
+ has no messages. Otherwise, a mailbox with no messages is a
+ zero-byte file, which could be one of several formats.
+
+ Second, it holds mailbox metadata used by IMAP: the UID
+ validity, the last assigned UID, and mailbox keywords. Without
+ this metadata, which must be preserved even when the mailbox
+ has no messages, the traditional UNIX format wouldn't be able
+ to support the full capabilities of IMAP.
+ _________________________________________________________________
+
+ 6.15 Why don't you stash the mailbox metadata in the first real
+ message of the mailbox instead of writing this fake FOLDER INTERNAL
+ DATA message?
+
+ In fact, that is what is done if the mailbox is non-empty and
+ does not already have a FOLDER INTERNAL DATA message.
+
+ One problem with doing that is that if some external program
+ removes the first message, the metadata is lost and must be
+ recreated, thus losing any prior UID or keyword list status
+ that IMAP clients may depend upon.
+
+ Another problem is that this doesn't help if the last message
+ is deleted. This will result in an empty mailbox, and the
+ necessity to create a FOLDER INTERNAL DATA message.
+ _________________________________________________________________
+
+ 6.16 Why aren't "dual-use" mailboxes the default?
+
+ Compatibility with the past 30 or so years of UNIX history, not
+ to mention compatibility with user expectations when using
+ shell tools.
+ _________________________________________________________________
+
+ 6.17 Why do you use ucbcc to build on Solaris?
+
+ It is a long, long story about why cc is set to ucbcc. You need
+ to invoke the C compiler so that it links with the SVR4
+ libraries and not the BSD libraries, otherwise readdir() will
+ return the wrong information.
+
+ Of all the names in the most common path, ucbcc is the only
+ name to be found (on /usr/ccs/bin) that points to a suitable
+ compiler. cc is likely to be /usr/ucb/cc which is absolutely
+ not the compiler that you want. The real SVR4 cc is probably
+ something like /opt/SUNWspro/bin/cc which is rarely in anyone's
+ path by default.
+
+ ucbcc is probably a link to acc, e.g.
+ /opt/SUNWspro/SC4.0/bin/acc, and is the UCB C compiler using
+ the SVR4 libraries.
+
+ If ucbcc isn't on your system, then punt on the SUN C compiler
+ and use gcc instead (the gso port instead of the sol port).
+
+ If, in spite of all the above warnings, you choose to change
+ "ucbcc" to "cc", you will probably find that the -O2 needs to
+ be changed to -O. If you don't get any error messages with -O2,
+ that's a pretty good indicator that you goofed and are running
+ the compiler that will link with the BSD libraries.
+
+ To recap:
+
+ + The sol port is designed to be built using the UCB compiler
+ using the SVR4 libraries. This compiler is "ucbcc", which is
+ lunk to acc. You use -O2 as one of the CFLAGS.
+ + If you build the sol port with the UCB compiler using the BSD
+ libraries, you will get no error messages but you will get
+ bad binaries (the most obvious symptom is dropping the first
+ two characters return filenames from the imapd LIST command.
+ This compiler also uses -O2, and is very often what the user
+ gets from "cc". BEWARE
+ + If you build the sol port with the real SVR4 compiler, which
+ is often hidden away or unavailable on many systems, then you
+ will get errors from -O2 and you need to change that to -O.
+ But you will get a good binary. However, you should try it
+ with -O2 first, to make sure that you got this compiler and
+ not the UCB compiler using BSD libraries.
+ _________________________________________________________________
+
+ 6.18 Why should I care about some old system with BSD libraries? cc is
+ the right thing on my Solaris system!
+
+ Because there still are sites that use such systems. On those
+ systems, the assumption that "cc" does the right thing will
+ lead to corrupt binaries with no error message or other warning
+ that anything is amiss.
+
+ Too many sites have fallen victim to this problem.
+ _________________________________________________________________
+
+ 6.19 Why do you insist upon writing .lock files in the spool
+ directory?
+
+ Compatibility with the past 30 years of UNIX software which
+ deals with the spool directory, especially software which
+ delivers mail. Otherwise, it is possible to lose mail.
+ _________________________________________________________________
+
+ 6.20 Why should I care about compatibility with the past?
+
+ This is one of those questions in which the answer never
+ convinces those who ask it. Somehow, everybody who ever asks
+ this question ends up answering it for themselves as they get
+ older, with the very answer that they rejected years earlier.
+ _________________________________________________________________
+
+7. Problems and Annoyances
+ _________________________________________________________________
+
+ 7.1 Help! My INBOX is empty! What happened to my messages?
+
+ If you are seeing "0 messages" when you open INBOX and you know
+ you have messages there (and perhaps have looked at your mail
+ spool file and see that messages are there), then probably
+ there is something wrong with the very first line of your mail
+ spool file. Make sure that the first five bytes of the file are
+ "From ", followed by an email address and a date/time in
+ ctime() format, e.g.:
+
+ From fred@foo.bar Mon May 7 20:54:30 2001
+ _________________________________________________________________
+
+ 7.2 Help! All my messages in a non-INBOX mailbox have been
+ concatenated into one message which claims to be from me and has a
+ subject of the file name of the mailbox! What's going on?
+
+ Something wrong with the very first line of the mailbox. Make
+ sure that the first five bytes of the file are "From ",
+ followed by an email address and a date/time in ctime() format,
+ e.g.:
+
+ From fred@foo.bar Mon May 7 20:54:30 2001
+ _________________________________________________________________
+
+ 7.3 Why do I get the message: CREATE failed: Can't create mailbox node
+ xxxxxxxxx: File exists and how do I fix it?
+
+ See the answer to the Are hierarchical mailboxes supported?
+ question.
+ _________________________________________________________________
+
+ 7.4 Why can't I log in to the server? The user name and password are
+ right!
+
+ There are a myriad number of possible answers to this question.
+ The only way to say for sure what is wrong is run the server
+ under a debugger such as gdb while root (yes, you must be root)
+ with a breakpoint at routines checkpw() and loginpw(), then
+ single-step until you see which test rejected you. The server
+ isn't going to give any error messages other than "login
+ failed" in the name of not giving out any unnecessary
+ information to unauthorized individuals.
+
+ Here are some of the more common reasons why login may fail:
+
+ + You didn't really give the correct user name and/or password.
+ + Your client doesn't send the LOGIN command correctly; for
+ example, IMAP2 clients won't send a password containing a "*"
+ correctly to an IMAP4 server.
+ + If you have set up a CRAM-MD5 database, remember that the
+ password used is the one in the CRAM-MD5 database, and
+ furthermore that there must also be an entry in /etc/passwd
+ (but the /etc/passwd password is not used).
+ + If you are using PAM, have you created a service file for the
+ server in /etc/pam.d?
+ + If you are using shadow passwords, have you used an
+ appropriate port when building? In particular, note that
+ "lnx" is for Linux systems without shadow passwords; you
+ probably want "slx" or "lnp" instead.
+ + If your system has account or password expirations, check to
+ see that the expiration date hasn't passed.
+ + You can't log in as root or any other UID 0 user. This is for
+ your own safety, not to mention the fact that the servers use
+ UID 0 as meaning "not logged in".
+ _________________________________________________________________
+
+ 7.5 Help! My load average is soaring and I see hundreds of POP and
+ IMAP servers, many logged in as the same user!
+
+ Certain inferior losing GUI mail reading programs have a
+ "synchronize all mailboxes at startup" (IMAP) or "check for new
+ mail every second" (POP) feature which causes a rapid and
+ unchecked spawning of servers.
+
+ This is not a problem in the server; the client is really
+ asking for all those server sessions. Unfortunately, there
+ isn't much that the POP and IMAP servers can do about it; they
+ don't spawned themselves.
+
+ Some sites have added code to record the number of server
+ sessions spawned per user per hour, and disable login for a
+ user who has exceeded a predetermined rate. This doesn't stop
+ the servers from being spawned; it just means that a server
+ session will commit suicide a bit faster.
+
+ Another possibility is to detect excessive server spawning
+ activity at the level where the server is spawned, which would
+ be inetd or possibly tcpd. The problem here is that this is a
+ hard time to quantify. 50 sessions in a minute from a
+ multi-user timesharing system may be perfectly alright, whereas
+ 10 sessions a minute from a PC may be too much.
+
+ The real solution is to fix the client configuration, by
+ disabling those evil features. Also tell the vendors of those
+ clients how you feel about distributing denial-of-service
+ attack tools in the guise of mail reading programs.
+ _________________________________________________________________
+
+ 7.6 Why does mail disappear even though I set "keep mail on server"?
+ 7.7 Why do I get the message Moved ##### bytes of new mail to
+ /home/user/mbox from /var/spool/mail/user and why did this happen?
+
+ This is probably caused by the mbox driver. If the file "mbox"
+ exists on the user's home directory and is in UNIX mailbox
+ format, then when INBOX is opened this file will be selected as
+ INBOX instead of the mail spool file. Messages will be
+ automatically transferred from the mail spool file into the
+ mbox file.
+
+ To disable this behavior, delete "mbox" from the EXTRADRIVERS
+ list in the top-level Makefile and rebuild. Note that if you do
+ this, users won't be able to access the messages that have
+ already been moved to mbox unless they open mbox instead of
+ INBOX.
+ _________________________________________________________________
+
+ 7.8 Why isn't it showing the local host name as a fully-qualified
+ domain name?
+ 7.9 Why is the local host name in the From/Sender/Message-ID headers
+ of outgoing mail not coming out as a fully-qualified domain name?
+
+ Your UNIX system is misconfigured. The entry for your system in
+ /etc/hosts must have the fully-qualified domain name first,
+ e.g.
+
+ 105.69.1.234 myserver.example.com myserver
+
+ A common mistake of novice system administrators is to have the
+ short name first, e.g.
+
+ 105.69.1.234 myserver myserver.example.com
+
+ or to omit the fully qualified domain name entirely, e.g.
+
+ 105.69.1.234 myserver
+
+ The result of this is that when the IMAP toolkit does a
+ gethostbyname() call to get the fully-qualified domain name, it
+ would get "myserver" instead of "myserver.example.com".
+
+ On some systems, a configuration file (typically named
+ /etc/svc.conf, /etc/netsvc.conf, or /etc/nsswitch.conf) can be
+ used to configure the system to use the domain name system
+ (DNS) instead of /etc/hosts, so it doesn't matter if /etc/hosts
+ is misconfigured.
+
+ Check the man pages for gethostbyname, hosts, svc, and/or
+ netsvc for more information.
+
+ Unfortunately, certain vendors, most notably SUN, have failed
+ to make this clear in their documentation. Most of SUN's
+ documentation assumes a corporate network that is not connected
+ to the Internet.
+
+ net.folklore once (late 1980s) held that the proper procedure
+ was to append the results of getdomainname() to the name
+ returned by gethostname(), and some versions of sendmail
+ configuration files were distributed that did this. This was
+ incorrect; the string returned from getdomainname() is the
+ Yellow Pages (a.k.a NIS) domain name, which is a completely
+ different (albeit unfortunately named) entity from an Internet
+ domain. These were often fortuitously the same string, except
+ when they weren't. Frequently, this would result in host names
+ with spuriously doubled domain names, e.g.
+
+ myserver.example.com.example.com
+
+ This practice has been thoroughly discredited for many years,
+ but folklore dies hard.
+ _________________________________________________________________
+
+ 7.10 What does the message: Mailbox vulnerable - directory
+ /var/spool/mail must have 1777 protection mean? How can I fix this?
+
+ In order to update a mailbox in the default UNIX format, it is
+ necessary to create a lock file to prevent the mailer from
+ delivering mail while an update is in progress. Some systems
+ use a directory protection of 775, requiring that all mail
+ handling programs be setgid mail; or of 755, requiring that all
+ mail handling programs be setuid root.
+
+ The IMAP toolkit does not run with any special privileges, and
+ I plan to keep it that way. It is antithetical to the concept
+ of a toolkit if users can't write their own programs to use it.
+ Also, I've had enough bad experiences with security bugs while
+ running privileged; the IMAP and POP servers have to be root
+ when not logged in, in order to be able to log themselves in. I
+ don't want to go any deeper down that slippery slope.
+
+ Directory protection 1777 is secure enough on most well-managed
+ systems. If you can't trust your users with a 1777 mail spool
+ (petty harassment is about the limit of the abuse exposure),
+ then you have much worse problems then that.
+
+ If you absolutely insist upon requiring privileges to create a
+ lock file, external file locking can be done via a setgid mail
+ program named /etc/mlock (this is defined by LOCKPGM in the
+ c-client Makefile). If the toolkit is unable to create a
+ <...mailbox...>.lock file in the directory by itself, it will
+ try to call mlock to do it. I do not recommend doing this for
+ performance reasons.
+
+ A sample mlock program is included as part of imap-2007. We
+ have tried to make this sample program secure, but it has not
+ been thoroughly audited.
+ _________________________________________________________________
+
+ 7.11 What does the message: Mailbox is open by another process, access
+ is readonly mean? How do I fix this?
+
+ A problem occurred in applying a lock to a /tmp lock file.
+ Either some other program has the mailbox open and won't
+ relenquish it, or something is wrong with the protection of
+ /tmp or the lock.
+
+ Make sure that the /tmp directory is protected 1777. Some
+ security scripts incorrectly set the protection of the /tmp
+ directory to 775, which disables /tmp for all non-privileged
+ programs.
+ _________________________________________________________________
+
+ 7.12 What does the message: Can't get write access to mailbox, access
+ is readonly mean?
+
+ The mailbox file is write-protected against you.
+ _________________________________________________________________
+
+ 7.13 I set my POP3 client to "delete messages from server" but they
+ never get deleted. What is wrong?
+
+ Make sure that your mailbox is not read-only: that the mailbox
+ is owned by you and write enabled (protection 0600), and that
+ the /tmp directory is longer world-writeable. /tmp must be
+ world-writeable because lots of applications use it for scratch
+ space. To fix this, do
+
+
+ chmod 1777 /tmp
+
+ as root.
+
+ Make sure that your POP3 client issues a QUIT command when it
+ finishes. The POP3 protocol specifies that deletions are
+ discarded unless a proper QUIT is done.
+
+ Make sure that you are not opening multiple POP3 sessions to
+ the same mailbox. It is a requirement of the POP3 protocol than
+ only one POP3 session be in effect to a mailbox at a time,
+ however some, poorly-written POP3 clients violate this. Also,
+ some background "check for new mail" tasks also cause a
+ violation. See the answer to the What does the syslog message:
+ Killed (lost mailbox lock) user=... host=... mean? question for
+ more details.
+ _________________________________________________________________
+
+ 7.14 What do messages such as:
+ Message ... UID ... already has UID ...
+ Message ... UID ... less than ...
+ Message ... UID ... greater than last ...
+ Invalid UID ... in message ..., rebuilding UIDs
+
+ mean?
+
+ Something happened to corrupt the unique identifier regime in
+ the mailbox. In traditional UNIX-format mailboxes, this can
+ happen if the user deleted the "DO NOT DELETE" internal
+ message.
+
+ This problem is relatively harmless; a new valid unique
+ identifier regime will be created. The main effect is that any
+ references to the old UIDs will no longer be useful.
+
+ So, unless it is a chronic problem or you feel like debugging,
+ you can safely ignore these messages.
+ _________________________________________________________________
+
+ 7.15 What do the error messages:
+ Unable to read internal header at ...
+ Unable to find CRLF at ...
+ Unable to parse internal header at ...
+ Unable to parse message date at ...
+ Unable to parse message flags at ...
+ Unable to parse message UID at ...
+ Unable to parse message size at ...
+ Last message (at ... ) runs past end of file ...
+
+ mean? I am using mbx format.
+
+ The mbx-format mailbox is corrupted and needs to be repaired.
+
+ You should make an effort to find out why the corruption
+ happened. Was there an obvious system problem (crash or disk
+ failure)? Did the user accidentally access the file via NFS?
+ Mailboxes don't get corrupted by themselves; something caused
+ the problem.
+
+ Some people have developed automated scripts, but if you're
+ comfortable using emacs it's pretty easy to fix it manually. Do
+ not use vi or any other editor unless you are certain that
+ editor can handle binary!!!
+
+ If you are not comfortable with emacs, or if the file is too
+ large to read with emacs, see the "step-by-step" technique
+ later on for another way of doing it.
+
+ After the word "at" in the error message is the byte position
+ it got to when it got unhappy with the file, e.g. if you see:
+
+ Unable to parse internal header at 43921: ne bombastic blurdybloop
+
+ The problem occurs at the 43,931 byte in the file. That's the
+ point you need to fix. c-client is expecting an internal header
+ at that byte number, looking something like:
+
+ 6-Jan-1998 17:42:24 -0800,1045;000000100001-00000001
+
+ The format of this internal line is:
+
+ dd-mmm-yyyy hh:mm:ss +zzzz,ssss;ffffffffFFFF-UUUUUUUU
+
+ The only thing that is variable is the "ssss" field, it can be
+ as many digits as needed. All other fields (inluding the "dd")
+ are fixed width. So, the easiest thing to do is to look forward
+ in the file for the next internal header, and delete everything
+ from the error point to that internal header.
+
+ Here's what to do if you want to be smarter and do a little bit
+ more work. Generally, you're in the middle of a message, and
+ there's nothing wrong with that message. The problem happened
+ in the *previous* message. So, search back to the previous
+ internal header. Now, remember that "ssss" field? That's the
+ size of that message.
+
+ Mark where you are in the file, move the cursor to the line
+ after the internal header, and skip that many bytes ("ssss")
+ forward. If you're at the point of the error in the file, then
+ that message is corrupt. If you're at a different point, then
+ perhaps the previous message is corrupt and has a too long size
+ count that "ate" into this message.
+
+ Basically, what you need to do is make sure that all those size
+ counts are right, and that moving "ssss" bytes from the line
+ after the internal header will land you at another internal
+ header.
+
+ Usually, once you know what you're looking at, it's pretty easy
+ to work out the corruption, and the best remedial action.
+ Repair scripts will make the problem go away but may not always
+ do the smartest/best salvage of the user's data. Manual repair
+ is more flexible and usually preferable.
+
+ Here is a step-by-step technique for fixing corrupt mbx files
+ that's a bit cruder than the procedure outlined above, but
+ works for any size file.
+
+ In this example, suppose that the corrupt file is INBOX, the
+ error message is
+
+ Unable to find CRLF at 132551754
+
+ and the size of the INBOX file is 132867870 bytes.
+
+ The first step is to split the mailbox file at the point of the
+ error:
+
+ + Rename the INBOX file to some other name, such as INBOX.bad.
+ + Copy the first 132,551,754 bytes of INBOX.bad to another
+ file, such as INBOX.new.
+ + Extract the trailing 316,116 bytes (132867870-132551754) of
+ INBOX.bad into another file, such as INBOX.tail.
+ + You no longer need INBOX.bad. Delete it.
+
+ In other words, use the number from the "Unable to find CRLF
+ at" as the point to split INBOX into two new files, INBOX.new
+ and INBOX.tail.
+
+ Now, remove the erroneous data:
+
+ + Verify that you can open INBOX.new in IMAP or Pine.
+ + The last message of INBOX.new is probably corrupted. Copy it
+ to another file, such as badmsg.1, then delete and expunge
+ that last message from INBOX.new
+ + Locate the first occurance of text in INBOX.tail which looks
+ like an internal header, as described above.
+ + Remove all the text which occurs prior to that point, and
+ place it into another file, such as badmsg.2. Note that in
+ the case of a single digit date, there is a leading space
+ which must not be removed (e.g. " 6-Nov-2001" not
+ "6-Nov-2001").
+
+ Reassemble the mailbox:
+
+ + Append INBOX.tail to INBOX.new.
+ + You no longer need INBOX.tail. Delete it.
+ + Verify that you can open INBOX.new in IMAP or Pine.
+
+ Reinstall INBOX.new as INBOX:
+
+ + Check to see if you have received any new messages while
+ repairing INBOX.
+ + If you haven't received any new messages while repairing
+ INBOX, just rename INBOX.new to INBOX.
+ + If you have received new messages, be sure to copy the new
+ messages from INBOX to INBOX.new before doing the rename.
+
+ You now have a working INBOX, as well as two files with
+ corrupted data (badmsg.1 and badmsg.2). There may be some
+ useful data in the two badmsg files that you might want to try
+ salvaging; otherwise you can delete the two badmsg files.
+ _________________________________________________________________
+
+ 7.16 What do the syslog messages:
+
+ imap/tcp server failing (looping)
+ pop3/tcp server failing (looping)
+
+ mean? When it happens, the listed service shuts down. How can I fix
+ this?
+
+ The error message "server failing (looping), service
+ terminated" is not from either the IMAP or POP servers.
+ Instead, it comes from inetd, the daemon which listens for TCP
+ connections to a number of servers, including the IMAP and POP
+ servers.
+
+ inetd has a limit of 40 new server sessions per minute for any
+ particular service. If more than 40 sessions are initiated in a
+ minute, inetd will issue the "failing (looping), service
+ terminated" message and shut down the service for 10 minutes.
+ inetd does this to prevent system resource consumption by a
+ client which is spawning infinite numbers of servers. It should
+ be noted that this is a denial of service; however for some
+ systems the alternative is a crash which would be a worse
+ denial of service!
+
+ For larger server systems, the limit of 40 is much too low. The
+ limit was established many years ago when a system typically
+ only ran a few dozen servers.
+
+ On some versions of inetd, such as the one distributed with
+ most versions of Linux, you can modify the /etc/inetd.conf file
+ to have a larger number of servers by appending a period
+ followed by a number after the nowait word for the server
+ entry. For example, if your existing /etc/inetd.conf line
+ reads:
+
+ imap stream tcp nowait root /usr/etc/imapd imapd
+
+ try changing it to be:
+
+ imap stream tcp nowait.100 root /usr/etc/imapd imapd
+
+ Another example (using TCP wrappers):
+
+ imap stream tcp nowait root /usr/sbin/tcpd imapd
+
+ try changing it to be:
+
+ imap stream tcp nowait.100 root /usr/sbin/tcpd imapd
+
+ to increase the limit to 100 sessions/minute.
+
+ Before making this change, please read the information in "man
+ inetd" to determine whether or not your inetd has this feature.
+ If it does not, and you make this change, the likely outcome is
+ that you will disable IMAP service entirely.
+
+ Another way to fix this problem is to edit the inetd.c source
+ code (provided by your UNIX system vendor) to set higher
+ limits, rebuild inetd, install the new binary, and reboot your
+ system. This should only be done by a UNIX system expert. In
+ the inetd.c source code, the limits TOOMANY (normally 40) is
+ the maximum number of new server sessions permitted per minute,
+ and RETRYTIME (normally 600) is the number of seconds inetd
+ will shut down the server after it exceeds TOOMANY.
+ _________________________________________________________________
+
+ 7.17 What does the syslog message: Mailbox lock file /tmp/.600.1df3
+ open failure: Permission denied mean?
+
+ This usually means that some "helpful" security script person
+ has protected /tmp so that it is no longer world-writeable.
+ /tmp must be world-writeable because lots of applications use
+ it for scratch space. To fix this, do
+
+ chmod 1777 /tmp
+
+ as root.
+
+ If that isn't the answer, check the protection of the named
+ file. If it is something other than 666, then either someone is
+ hacking or some "helpful" person modified the code to have a
+ different default lock file protection.
+ _________________________________________________________________
+
+ 7.18 What do the syslog messages:
+ Command stream end of file, while reading line user=... host=...
+ Command stream end of file, while reading char user=... host=...
+ Command stream end of file, while writing text user=... host=...
+
+ mean?
+
+ This message occurs when the session is disconnected without a
+ proper LOGOUT (IMAP) or QUIT (POP) command being received by
+ the server first.
+
+ In many cases, this is perfectly normal; many client
+ implementations are impolite and do this. Some programmers
+ think this sort of rudeness is "more efficient".
+
+ The condition could, however, indicate a client or network
+ connectivity problem. The server has no way of knowing whether
+ there's a problem or just a rude client, so it issues this
+ message instead of a Logout.
+
+ Certain inferior losing clients disconnect abruptly after a
+ failed login, and instead of saying that the login failed, just
+ say that they can't access the mailbox. They then complain to
+ the system manager, who looks in the syslog and finds this
+ message. Not very helpful, eh? See the answer to the Why can't
+ I log in to the server? The user name and password are right!
+ question.
+
+ If the user isn't reporting a problem, you can probably ignore
+ this message.
+ _________________________________________________________________
+
+ 7.19 Why did my POP or IMAP session suddenly disconnect? The syslog
+ has the message: Killed (lost mailbox lock) user=... host=...
+
+ This message only happens when either the traditional UNIX
+ mailbox format or MMDF format is in use. This format only
+ allows one session to have the mailbox open read/write at a
+ time.
+
+ The servers assume that if a second session attempts to open
+ the mailbox, that means that the first session is probably
+ owned by an abandoned client. The common scenario here is a
+ user who leaves his client running at the office, and then
+ tries to read his mail from home. Through an internal mechanism
+ called kiss of death, the second session requests the first
+ session to kill itself. When the first session receives the
+ "kiss of death", it issues the "Killed (lost mailbox lock)"
+ syslog message and terminates. The second session then seizes
+ read/write access, and becomes the new "first" session.
+
+ Certain poorly-designed clients routinely open multiple
+ sessions to the same mailbox; the users of those clients tend
+ to get this message a lot.
+
+ Another cause of this message is a background "check for new
+ mail" task which does its work by opening a POP session to
+ server every few seconds. They do this because POP doesn't have
+ a way to announce new mail.
+
+ The solution to both situations is to replace the client with a
+ good online IMAP client such as Pine. Life is too short to
+ waste on POP clients and poorly-designed IMAP clients.
+ _________________________________________________________________
+
+ 7.20 Why does my IMAP client show all the files on the system,
+ recursively from the UNIX root directory?
+ 7.21 Why does my IMAP client show all of my files, recursively from my
+ UNIX home directory?
+
+ A well-written client should only show one level of hierarchy
+ and then stop, awaiting explicit user action before going
+ lower. However, some poorly-designed clients will recursively
+ list all files, which may be a very long list (especially if
+ you have symbolic links to directories that create a loop in
+ the filesystem graph!).
+
+ This behavior has also been observed in some third-party
+ c-client drivers, including maildir drivers. Consequently, this
+ problem has even been observed in Pine. It is important to
+ understand that this is not a problem in Pine or c-client; it
+ is a problem in the third-party driver. A Pine built without
+ that third-party driver will not have this problem.
+
+ See also the answer to Why does my IMAP client show all my
+ files in my home directory?
+ _________________________________________________________________
+
+ 7.22 Why does my IMAP client show that I have mailboxes named
+ "#mhinbox", "#mh", "#shared", "#ftp", "#news", and "#public"?
+
+ These are IMAP namespace names. They represent other
+ hierarchies in which messages may exist. These hierarchies may
+ not necessarily exist on a server, but the namespace name is
+ still in the namespace list in order to mark it as reserved.
+
+ A few poorly-designed clients display all namespace names as if
+ they were top-level mailboxes in a user's list of mailboxes,
+ whether or not they actually exist. This is a flaw in those
+ clients.
+ _________________________________________________________________
+
+ 7.23 Why does my IMAP client show all my files in my home directory?
+
+ As distributed, the IMAP server is connected to your home
+ directory by default. It has no way of knowing what you might
+ call "mail" as opposed to "some other file"; in fact, you can
+ use IMAP to access any file.
+
+ Most clients have an option to configure your connected
+ directory on the IMAP server. For example, in Pine you can
+ specify this as the "Path" in your folder-collection, e.g.
+
+ Nickname : Secondary Folders
+ Server : imap.example.com
+ Path : mail/
+ View :
+
+ In this example, the user is connected to the "mail"
+ subdirectory of his home directory.
+
+ Other servers call this the "folder prefix" or similar term.
+
+ It is possible to modify the IMAP server so that all users are
+ automatically connected to some other directory, e.g. a
+ subdirectory of the user's home directory. Read the file CONFIG
+ for more details.
+ _________________________________________________________________
+
+ 7.24 Why is there a long delay before I get connected to the IMAP or
+ POP server, no matter what client I use?
+
+ There are two common occurances of this problem:
+
+ + You are running a system (e.g. certain versions of Linux)
+ which by default attempts to connect to an "IDENT" protocol
+ (port 113) server on your client. However, a firewall or NAT
+ box is blocking connections to that port, so the connection
+ attempt times out.
+ The IDENT protocol is a well-known bad idea that does not
+ deliver any real security but causes incredible problems. The
+ idea is that this will give the server a record of the user
+ name, or at least what some program listening on port 113
+ says is the user name. So, if somebody coming from port nnnnn
+ on a system does something bad, IDENT may give you the userid
+ of the bad guy.
+ The problem is, IDENT is only meaningful on a timesharing
+ system which has an administrator who is privileged and users
+ who are not. It is of no value on a personal system which has
+ no separate concept of "system administrator" vs.
+ "unprivileged user".
+ On either type of system, security-minded people either turn
+ IDENT off or replace it with an IDENT server that lies. Among
+ other things, IDENT gives spammers the ability to harvest
+ email addresses from anyone who connects to a web page.
+ This problem has been showing up quite frequently on systems
+ which use xinetd instead of inetd. Look for files named
+ /etc/xinetd.conf, /etc/xinetd.d/imapd, /etc/inetd.d/ipop2d,
+ and /etc/xinetd.d/ipop3d. In those files, look for lines
+ containing "USERID", e.g.
+ log_on_success += USERID
+ Hunt down such lines, and delete them ruthlessly from all
+ files in which they occur. Don't be shy about it.
+ + The DNS is taking a long time to do a reverse DNS (PTR
+ record) lookup of the IP address of your client. This is a
+ problem in your DNS, which either you or you ISP need to
+ resolve. Ideally, the DNS should return the client's name;
+ but if it can't it should at least return an error quickly.
+
+ As you may have noticed, neither of these are actual problems
+ in the IMAP or POP servers; they are configuration issues with
+ either your system or your network infrastructure. If this is
+ all new to you, run (don't walk) to the nearest technical
+ bookstore and get yourself a good pedagogical text on system
+ administration for the type of system you are running.
+ _________________________________________________________________
+
+ 7.25 Why is there a long delay in Pine or any other c-client based
+ application call before I get connected to the IMAP server? The hang
+ seems to be in the c-client mail_open() call. I don't have this
+ problem with any other IMAP client. There is no delay connecting to a
+ POP3 or NNTP server with mail_open().
+
+ By default, the c-client library attempts to make a connection
+ through rsh (and ssh, if you enable that). If the command:
+
+ rsh imapserver exec /etc/rimapd
+
+ (or ssh if that is enabled) returns with a "* PREAUTH"
+ response, it will use the resulting rsh session as the IMAP
+ session and not require an authentication step on the server.
+
+ Unfortunately, rsh has a design error that treats "TCP
+ connection refused" as "temporary failure, try again"; it
+ expects the "rsh not allowed" case to be implemented as a
+ successful connection followed by an error message and close
+ the connection.
+
+ It must be emphasized that this is a bug in rsh. It is not a
+ bug in the IMAP toolkit.
+
+ The use of rsh can be disabled in any the following ways:
+
+ + You can disable it for this particular session by either:
+ o setting an explicit port number in the mailbox name,
+ e.g.
+ {imapserver.foo.com:143}INBOX
+ o using SSL (the /ssl switch)
+ + You can disable rsh globally by setting the rsh timeout value
+ to 0 with the call:
+ mail_parameters (NIL,SET_RSHTIMEOUT,0);
+ _________________________________________________________________
+
+ 7.26 Why does a message sometimes get split into two or more messages
+ on my SUN system?
+
+ This is caused by an interaction of two independent design
+ problems in SUN mail software. The first problem is that the
+ "forward message" option in SUN's mail tool program includes
+ the internal "From " header line in the text that it forwarded.
+ This internal header line is specific to traditional UNIX
+ mailbox files and is not suitable for use in forwarded
+ messages.
+
+ The second problem is that the mail delivery agent assumes that
+ mail reading programs will not use the traditional UNIX mailbox
+ format but instead an incompatible variant that depends upon a
+ Content-Length: message header. Content-Length is widely
+ recognized to have been a terrible mistake, and is no longer
+ recommended for use in mail (it is used in other facilities
+ that use MIME).
+
+ One symptom of the problem is that under certain circumstances,
+ a message may get broken up into several messages. I'm also
+ aware of security bugs caused by programs that foolishly trust
+ "Content-Length:" headers with evil values.
+
+ To fix the mailer on your system, edit your sendmail.cf to
+ change the Mlocal line to have the -E flag. A typical entry
+ will lool like:
+
+ Mlocal, P=/usr/lib/mail.local, F=flsSDFMmnPE, S=10, R=20,
+ A=mail.local -d $u
+
+ This fix will also work around the problem with mail tool,
+ because it will insert a ">" before the internal header line to
+ prevent it from being interpreted by mail reading software as
+ an internal header line.
+ _________________________________________________________________
+
+ 7.27 Why did my POP or IMAP session suddenly disconnect? The syslog
+ has the message:
+ Autologout user=<...my user name...> host=<...my client system...>
+
+ This is a problem in your client.
+
+ In the case of IMAP, it failed to communicate with the IMAP
+ server for over 30 minutes; in the case of POP, it failed to
+ communicate with the POP server for over 10 minutes.
+ _________________________________________________________________
+
+ 7.28 What does the UNIX error message: TLS/SSL failure: myserver: SSL
+ negotiation failed mean?
+ 7.29 What does the PC error message: TLS/SSL failure: myserver:
+ Unexpected TCP input disconnect mean?
+
+ This usually means that an attempt to negotiate TLS encryption
+ via the STARTTLS command failed, because the server advertises
+ STARTTLS functionality, but doesn't actually have it (e.g.
+ because no certificates are installed).
+
+ Use the /notls option in the mailbox name to disable TLS
+ negotiation.
+ _________________________________________________________________
+
+ 7.30 What does the error message: TLS/SSL failure: myserver: Server
+ name does not match certificate mean?
+
+ An SSL or TLS session encryption failed because the server name
+ in the server's certificate does not match the name that you
+ gave it. This could indicate that the server is not really the
+ system you think that it is, but can be also be called if you
+ gave a nickname for the server or name that was not
+ fully-qualified. You must use the fully-qualified domain name
+ for the server in order to validate its certificate
+
+ Use the /novalidate-cert option in the mailbox name to disable
+ validation of the certificate.
+ _________________________________________________________________
+
+ 7.31 What does the UNIX error message: TLS/SSL failure: myserver:
+ self-signed certificate mean?
+ 7.32 What does the PC error message: TLS/SSL failure: myserver:
+ Self-signed certificate or untrusted authority mean?
+
+ An SSL or TLS session encryption failed because your server's
+ certificate is "self-signed"; that is, it is not signed by any
+ Certificate Authority (CA) and thus can not be validated. A
+ CA-signed certificate costs money, and some smaller sites
+ either don't want to pay for it or haven't gotten one yet. The
+ bad part about this is that this means there is no guarantee
+ that the server is really the system you think that it is.
+
+ Use the /novalidate-cert option in the mailbox name to disable
+ validation of the certificate.
+ _________________________________________________________________
+
+ 7.33 What does the UNIX error message: TLS/SSL failure: myserver:
+ unable to get local issuer certificate mean?
+
+ An SSL or TLS session encryption failed because your system
+ does not have the Certificate Authority (CA) certificates
+ installed on OpenSSL's certificates directory. On most systems,
+ this directory is /usr/local/ssl/certs). As a result, it is not
+ possible to validate the server's certificate.
+
+ If CA certificates are properly installed, you should see
+ factory.pem and about a dozen other .pem names such as
+ thawteCb.pem.
+
+ As a workaround, you can use the /novalidate-cert option in the
+ mailbox name to disable validation of the certificate; however,
+ note that you are then vulnerable to various security attacks
+ by bad guys.
+
+ The correct fix is to copy all the files from the certs/
+ directory in the OpenSSL distribution to the
+ /usr/local/ssl/certs (or whatever) directory. Note that you
+ need to do this after building OpenSSL, because the OpenSSL
+ build creates a number of needed symbolic links. For some
+ bizarre reason, the OpenSSL "make install" doesn't do this for
+ you, so you must do it manually.
+ _________________________________________________________________
+
+ 7.34 Why does reading certain messages hang when using Netscape? It
+ works fine with Pine!
+
+ There are two possible causes.
+
+ Check the mail syslog. If you see the message "Killed (lost
+ mailbox lock)" for the impacted user(s), read the FAQ entry
+ regarding that message.
+
+ Check the affected mailbox to see if there are embedded NUL
+ characters in the message. NULs in message texts are a
+ technical violation of both the message format and IMAP
+ specifications. Most clients don't care, but apparently
+ Netscape does.
+
+ You can work around this by rebuilding imapd with the
+ NETSCAPE_BRAIN_DAMAGE option set (see src/imapd/Makefile); this
+ will cause imapd to convert all NULs to 0x80 characters. A
+ better solution is to enable the feature in your MTA to
+ MIME-convert messages with binary content. See the
+ documentation for your MTA for how to do this.
+ _________________________________________________________________
+
+ 7.35 Why does Netscape say that there's a problem with the IMAP server
+ and that I should "Contact your mail server administrator."?
+
+ Certain versions of Netscape do this when you click the Manage
+ Mail button, which uses an undocumented feature of Netscape's
+ proprietary IMAP server.
+
+ You can work around this by rebuilding imapd with the
+ NETSCAPE_BRAIN_DAMAGE option set (see src/imapd/Makefile) to a
+ URL that points either to an alternative IMAP client (e.g.
+ Pine) or perhaps to a homebrew mail account management page.
+ _________________________________________________________________
+
+ 7.36 Why is one user creating huge numbers of IMAP or POP server
+ sessions?
+
+ The user is probably using Outlook Express, Eudora, or a
+ similar program. See the answer to the Help! My load average is
+ soaring and I see hundreds of POP and IMAP servers, many logged
+ in as the same user! question.
+ _________________________________________________________________
+
+ 7.37 Why don't I get any new mail notifications from Outlook Express
+ or Outlook after a while?
+
+ This is a known bug in Outlook Express. Microsoft is aware of
+ the problem and its cause. They have informed us that they do
+ not have any plans to fix it at the present time.
+
+ The problem is also reported in Outlook 2000, but not verified.
+
+ Outlook Express uses the IMAP IDLE command to avoid having to
+ "ping" the server every few minutes for new mail.
+ Unfortunately, Outlook Express overlooks the part in the IDLE
+ specification which requires that a client terminate and
+ restart the IDLE before the IMAP 30 minute inactivity
+ autologout timer triggers.
+
+ When this happens, Outlook Express displays "Not connected" at
+ the bottom of the window. Since it's no longer connected to the
+ IMAP server, it isn't going to notice any new mail.
+
+ As soon as the user does anything that would cause an IMAP
+ operation, Outlook Express will reconnect and new mail will
+ flow again. If the user does something that causes an IMAP
+ operation at least every 29 minutes, the problem won't happen.
+
+ Modern versions of imapd attempt to work around the problem by
+ automatically reporting fake new mail after 29 minutes. This
+ causes Outlook Express to exit the IDLE state; as soon as this
+ happens imapd revokes the fake new mail. As long as this
+ behavior isn't known to cause problems with other clients, this
+ workaround will remain in imapd.
+ _________________________________________________________________
+
+ 7.38 Why don't I get any new mail notifications from Entourage?
+
+ This is a known bug in Entourage.
+
+ You built an older version of imapd with the
+ MICROSOFT_BRAIN_DAMAGE option set, in order to disable support
+ for the IDLE command. However, Entourage won't get new mail
+ unless IDLE command support exists.
+
+ Note: the MICROSOFT_BRAIN_DAMAGE option no longer exists in
+ modern versions, as the Outlook Express problem which it
+ attempted to solve has been worked around in another way.
+ _________________________________________________________________
+
+ 7.39 Why doesn't Entourage work at all?
+
+ It's hard to know. Entourage breaks almost every rule in the
+ book for IMAP. It is highly instructive to do a packet trace on
+ Entourage, as an example of how not to use IMAP. It does things
+ like STATUS (MESSAGES) on the currently selected mailbox and
+ re-fetching the same static data over and over again.
+
+ It seems that every time we understand what it is doing wrong
+ in Entourage and come up with a workaround, we learn about
+ something else that's broken.
+
+ Try building imapd with the ENTOURAGE_BRAIN_DAMAGE option set,
+ in order to disable the diagnostic that occurs when doing
+ STATUS on the currently selected mailbox.
+ _________________________________________________________________
+
+ 7.40 Why doesn't Netscape Notify (NSNOTIFY.EXE) work at all?
+
+ This is a bug in NSNOTIFY; it doesn't handle unsolicited data
+ from the server correctly.
+
+ Fortunately, there is no reason to use this program with IMAP;
+ NSNOTIFY is a polling program to let you know when new mail has
+ appeared in your maildrop. This is necessary with POP; but
+ since IMAP dynamically announces new mail in the session you're
+ better off (and will actually cause less load on the server!)
+ keeping your mail reading program's IMAP session open and let
+ IMAP do the notifying for you.
+
+ Consequently, the recommended fix for the NSNOTIFY problem is
+ to delete the NSNOTIFY binary.
+ _________________________________________________________________
+
+ 7.41 Why can't I connect via SSL to Eudora? It says the connection has
+ been broken, and in the server syslogs I see "Command stream end of
+ file".
+
+ There is a report that you can fix the problem by going into
+ Eudora's advanced network configuration menu and increasing the
+ network buffer size to 8192.
+ _________________________________________________________________
+
+ 7.42 Sheesh. Aren't there any good IMAP clients out there?
+
+ Yes!
+
+ Pine is a wonderful client. It's fast, it uses IMAP well, and
+ it generates text mail (life is too short to waste on HTML
+ mail). Also, there are some really wonderful things in progress
+ in the Pine world.
+
+ There are some good GUI clients out there, mostly from smaller
+ vendors. Without naming names, look for the vendors who are
+ active in the IMAP protocol development community, and their
+ products.
+
+ Netscape, Eudora, and Outlook can be configured with enough
+ effort to be good citizens and work well for users, but they
+ can also be badly misconfigured, and often the misconfiguration
+ is the default.
+ _________________________________________________________________
+
+ 7.43 But wait! PC Pine (or other PC program build with c-client)
+ crashes with the message incomplete SecBuffer exceeds maximum buffer
+ size when I use SSL connections. This is a bug in c-client, right?
+
+ It's a bug in the Microsoft SChannel.DLL, which implements SSL.
+ Microsoft admits it (albeit with an unstatement: "it's not
+ fully RFC compliant"). The problem is that SChannel indicates
+ that the maximum SSL packet data size is 5 bytes smaller than
+ the actual maximum. Thus, any IMAP server which transmits a
+ maximum sized SSL packet will not work with PC Pine or any
+ other program which uses SChannel.
+
+ It can take a while for the problem to show up. The client has
+ to do something that causes at least 16K of contiguous data.
+ Many clients do partial fetching, which tends to reduce the
+ number of cases where this can happen. However, all software
+ which uses SChannel to support SSL is affected by this bug.
+
+ This problem does not affect UNIX code, since OpenSSL is used
+ on UNIX.
+
+ This problem most recently showed up with the CommunigatePro
+ IMAP server. They have an update which trims down their maximum
+ contiguous data to less than 16K, in order to work around the
+ problem.
+
+ This problem has also shown up with the Exchange IMAP server
+ with UNIX clients (including Pine built with an older version
+ of c-client) which sends full-sized 16K SSL packets. Modern
+ c-client works around the problem by trimming down its maximum
+ outgoing SSL packet size to 8K.
+
+ Microsoft has developed a hotfix for this bug. Look up MSKB
+ article number 300562. Contrary to the article text which
+ implies that this is a Pine issue, this bug also affect
+ Microsoft Exchange server with *any* UNIX based client that
+ transmits full-sized SSL payloads.
+ _________________________________________________________________
+
+ 7.44 My qpopper users keep on getting the DON'T DELETE THIS MESSAGE --
+ FOLDER INTERNAL DATA if they also use Pine or IMAP. How can I fix
+ this?
+
+ This is an incompatibility between qpopper and the c-client
+ library used by Pine, imapd, and ipop[23]d.
+
+ Assuming that you want to continue using qpopper, look into
+ qpopper's --enable-uw-kludge-flag configuration flag, which is
+ documented as "check for and hide UW 'Folder Internal Data'
+ messages".
+
+ The other alternative is to switch from qpopper to ipop3d.
+ _________________________________________________________________
+
+ 7.45 Help! I installed the servers but I can't connect to them from my
+ client!
+
+ Review the installation instructions carefully. Make sure that
+ you have not skipped any of the steps. Make sure that you have
+ made the correct entries in the configuration files; pay
+ careful attention to the exact spelling of the service names
+ and the path names. Make sure as well that you have properly
+ restarted inetd.
+
+ If you have a system with Yellow Pages/NIS such as Solaris,
+ have you updated the service names there as well as in
+ /etc/services?
+
+ If you have a system with TCP wrappers, have you properly
+ updated the TCP wrapper files (e.g. /etc/hosts.allow and
+ /etc/hosts.deny) for the servers?
+
+ If you have a system which uses xinetd instead of inetd, have
+ you made sure that you have made the correct corresponding
+ xinetd changes for those services?
+
+ Try telneting to the server port (143 for IMAP, 110 for POP3).
+ If you get a "refused" error, that probably means that you
+ don't have the service set up in inetd.conf. If the connection
+ opens and then closes with no message, the service is set up,
+ but either the path name of the server binary in inetd.conf is
+ wrong or your TCP wrappers are configured to deny access.
+
+ If you don't know how to make the corresponding changes to
+ these files, seek the help of a local expert for your system.
+ _________________________________________________________________
+
+ 7.46 Why do I get the message Can not authenticate to SMTP server: 421
+ SMTP connection went away! and why did this happen? There was also
+ something about SECURITY PROBLEM: insecure server advertised
+ AUTH=PLAIN
+
+ Some versions of qmail, including that running on
+ mail.smtp.yahoo.com, disconnect the SMTP session if you fail to
+ authenticate prior to attempting to transmit mail. An attempt
+ to authenticate was made, but it failed because the server had
+ already disconnected.
+
+ To work around this, you need to specify /user=... in the host
+ name specification.
+
+ The SECURITY PROBLEM came about because the server advertised
+ the AUTH=PLAIN SASL authentication mechanism outside of a
+ TLS-encrypted session, in violation of RFC 4616. This message
+ is just a warning, and in fact occurred after the server had
+ disconnected.
+ _________________________________________________________________
+
+ 7.47 Why do I get the message SMTP Authentication cancelled and why
+ did this happen? There was also something about SECURITY PROBLEM:
+ insecure server advertised AUTH=PLAIN
+
+ This is a bug in the SMTP server.
+
+ Some versions of qmail, including that running on
+ mail.smtp.yahoo.com, have a bug in their implementation of SASL
+ in their SMTP server, which renders it non-compliant with the
+ standard.
+
+ If the client does not provide an initial response in the
+ command line for an authentication mechanism whose profile does
+ not have an initial challenge, qmail issues a bogus response:
+
+ 334 ok, go on
+
+ The problem is the "ok, go on". This violates RFC 4954's
+ requirement that the text part in a 334 response be a BASE64
+ encoded string; in other words, it is a protocol syntax error.
+
+ In the case of AUTH=PLAIN, RFC 4422 (page 7) requires that the
+ encoded string have no data. In other words, the appropropiate
+ standards-compliant server response is "334" followed by a
+ SPACE and a CRLF.
+
+ The SECURITY PROBLEM came about because the server advertised
+ the AUTH=PLAIN SASL authentication mechanism outside of a
+ TLS-encrypted session, in violation of RFC 4616. This message
+ is just a warning, and is not related the "Authentication
+ cancelled" problem.
+ _________________________________________________________________
+
+ 7.48 Why do I get the message Invalid base64 string when I try to
+ authenticate to a Cyrus server?
+
+ This slightly misleading message is the way that a Cyrus server
+ indicates that an authentication exchange was cancelled. It is
+ not indicative of a bug or protocol violation.
+
+ The most common reason that this happens is if the Cyrus server
+ offers Kerberos authentication, c-client is built with Kerberos
+ support, but your client system is not within the Kerberos
+ realm. In this case, the client code will try to authenticate
+ via Kerberos, fail to get the Kerberos credentials, cancel the
+ authentication attempt, and try the next available
+ authentication technology.
+ _________________________________________________________________
+
+8. Where to Go For Additional Information
+ _________________________________________________________________
+
+ 8.1 Where can I go to ask questions?
+ 8.2 I have some ideas for enhancements to IMAP. Where should I go?
+
+ If you have questions about the IMAP protocol, or want to
+ participate in discussions of future directions of the IMAP
+ protocol, the appropriate mailing list is
+ imap-protocol@u.washington.edu. You can subscribe to this
+ list via imap-protocol-request@u.washington.edu
+
+ If you have questions about this software, you can send me
+ email directly or use the imap-uw@u.washington.edu mailing
+ list. You can subscribe to this list via
+ imap-uw-request@u.washington.edu
+
+ If you have general questions about the use of IMAP software
+ (not specific to the UW IMAP toolkit) use the
+ imap-use@u.washington.edu mailing list. You can subscribe to
+ this list via imap-use-request@u.washington.edu
+
+ You must be a subscriber to post to these lists. As an
+ alternative, you can use the comp.mail.imap newsgroup.
+ _________________________________________________________________
+
+ 8.3 Where can I read more about IMAP and other email protocols?
+
+ We recommend Internet Email Protocols: A Developer's Guide, by
+ Kevin Johnson, published by Addison Wesley, ISBN 0-201-43288-9.
+ _________________________________________________________________
+
+ 8.4 Where can I find out more about setting up and administering an
+ IMAP server?
+
+ We recommend Managing IMAP, by Dianna Mullet & Kevin Mullet,
+ published by O'Reilly, ISBN 0-596-00012-X.
+
+ This book also has an excellent comparison of the UW and Cyrus
+ IMAP servers.
+
+ Last Updated: 15 November 2007
diff --git a/imap/docs/IPv6.txt b/imap/docs/IPv6.txt
new file mode 100644
index 00000000..5b1af621
--- /dev/null
+++ b/imap/docs/IPv6.txt
@@ -0,0 +1,131 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+The following information about configuring inetd and xinetd for IPv6 was
+contributed by a user. I have not checked it for accuracy or completeness,
+but have included it as-is in the hope that it may be useful:
+
+---------------------------------------------------------------------------
+One thing you might consider adding to the docs are hints for setting up
+inetd or xinetd to simultaneously listen on BOTH IPv4 and IPv6 for
+different OS.
+
+Some OS want to see separate IPv4-only and IPv6-only listening sockets
+configured in inetd.conf or xinetd.conf. Others will accept IPv4
+connections on lines configured for IPv6 and actually generate errors if
+you have both configured when inetd or xinetd start up. It's not clear to
+me whether this difference is due to how inetd or xinetd are built or
+whether it's due to the kernel's IP stack implementation. I suspect it's
+really the latter. Below are some examples:
+
+Here's a fragment of /usr/local/etc/xinetd.conf for a FreeBSD 4.9 server:
+
+service imap
+{
+ socket_type = stream
+ protocol = tcp
+ wait = no
+ user = root
+ server = /usr/local/libexec/imapd
+}
+service imap
+{
+ flags = IPv6
+ socket_type = stream
+ protocol = tcp
+ wait = no
+ user = root
+ server = /usr/local/libexec/imapd
+}
+service imaps
+{
+ socket_type = stream
+ protocol = tcp
+ wait = no
+ user = root
+ server = /usr/local/libexec/imapd
+}
+service imaps
+{
+ flags = IPv6
+ socket_type = stream
+ protocol = tcp
+ wait = no
+ user = root
+ server = /usr/local/libexec/imapd
+}
+
+Note that you have to specify a nearly identical paragraph for each
+service which differs only by the 'flags = IPv6'. An equivalent
+inetd.conf would look something like:
+
+imap stream tcp nowait root /usr/local/libexec/imapd imapd
+imap stream tcp6 nowait root /usr/local/libexec/imapd imapd
+imaps stream tcp nowait root /usr/local/libexec/imapd imapd
+imaps stream tcp6 nowait root /usr/local/libexec/imapd imapd
+
+The man pages for inetd suggest that if you use a single entry with
+'tcp46' replacing either 'tcp' or 'tcp6' the system will listen on both
+types of addresses. At least for the case of FreeBSD this is actually
+incorrect.
+
+Now if you were to use the above xinetd.conf on Fedora Linux, it would
+complain. What does work under Linux is to create a single service
+paragraph for each service which will accept connections on both IPv4 and
+IPv6:
+
+In /etc/xinetd.d/imap:
+
+service imap
+{
+ flags = IPv6
+ disable = no
+ socket_type = stream
+ wait = no
+ user = root
+ server = /usr/local/sbin/imapd
+}
+
+In /etc/xinetd.d/imaps:
+
+service imaps
+{
+ flags = IPv6
+ disable = no
+ socket_type = stream
+ wait = no
+ user = root
+ server = /usr/local/sbin/imapd
+}
+
+The man page for xinetd says the IPv6 flag means xinetd will listen ONLY
+on IPv6. However the actual behaviour (for Fedora Linux) is to listen on
+both IPv4 and IPv6.
+
+All of the above also applies to ipop3d. Anyway, this might save some
+folks a little bit of head scratching time.
+---------------------------------------------------------------------------
+Addendum from the original submitter:
+---------------------------------------------------------------------------
+I've since learned that the discrepancy really is a function of the OS.
+It seems those systems that force you to create separate IPv4 and IPv6
+listeners in inetd (or xinetd) are the same systems that also disable
+IPv4-mapped IPv6 addresses by default. This is a boot-time configuration
+option. If you enable IPv4-mapped IPv6 addresses, then the 'tcp46' option
+in inetd works and the simplified config would look like:
+
+imap4 stream tcp46 nowait root /usr/local/libexec/imapd imapd
+imaps stream tcp46 nowait root /usr/local/libexec/imapd imapd
+
+In FreeBSD, enabling IPv4-mapped addresses is done by adding
+ipv6_ipv4mapping="YES" to /etc/rc.conf (in addition to ipv6_enable="YES").
diff --git a/imap/docs/RELNOTES b/imap/docs/RELNOTES
new file mode 100644
index 00000000..5cfd9132
--- /dev/null
+++ b/imap/docs/RELNOTES
@@ -0,0 +1,787 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+Updated: 16 December 2008
+
+imap-2007e is a maintenance release, consisting primarily of bugfixes to
+problems discovered in the release that affected a small number of users
+plus a security fix for users of the RFC822BUFFER routines.
+
+
+Updated: 29 October 2008
+
+imap-2007d is a maintenance release, consisting primarily of bugfixes to
+problems discovered in the release that affected a small number of users
+plus a security fix for users of tmail or dmail.
+
+
+Updated: 25 March 2008
+
+imap-2007b is a maintenance release, consisting primarily of bugfixes to
+problems discovered in the release that affected a small number of users.
+
+
+Updated: 2 January 2008
+
+imap-2007a is a maintenance release, consisting primarily of bugfixes to
+problems discovered in the release that affected a small number of users.
+
+
+Updated: 20 December 2007
+
+imap-2007 is a release corresponding with the release of Alpine 1.0.
+The primary focus of the imap-2007 release is bugfixes and reliability
+improvements. This includes:
+ . fixes to problems discovered between the Alpine 0.99999 pre-release
+ and Alpine 1.0
+ . fixes to the mix driver to timing race problems uncovered by Timo
+ Sirainen's imaptest suite. imap-2007 using the mix format is
+ believed to pass imaptest completely.
+
+A new function, utf8_csvalidmap(), has been added for the benefit of
+Alpine to use in examining UTF-8 text and determining efficiently
+whether it can be downgraded to a legacy charset. If you develop an
+MUA, this may be useful for you too, although you'll have to read the
+source code to see how to use it. The purpose of the "not-CJK" bit is
+to prevent messages being downgraded to a CJK charset if all they have
+in that charset are some special punctuation.
+
+
+Updated: 5 September 2007
+
+imap-2006k is a maintenance release, consisting primarily of bugfixes to
+problems discovered in the release that affected a small number of users.
+
+The primary focus of this maintenance release is to correct deadlock
+issues. There were two major causes of the deadlocks:
+ . a change in imap-2006i attempted to resolve a glibc mutex-based
+ deadlock in imapd's signal handler, but ended up worsening the problem.
+ . a bug in the mbx driver, introduced as part of the UIDPLUS work in 2006,
+ applied an mbx-style lock briefly on a traditional UNIX format mailbox.
+ If the traditional UNIX format mailbox was already locked by some other
+ process, the result would be a deadlock of both processes.
+
+imapd's signal handling logic is rewritten to avoid the mutex issue, and
+the mbx driver is fixed so that mbx-style locks are only applied to mbx
+format mailboxes.
+
+imapd now supports the WITHIN extension.
+
+
+Updated: 14 June 2007
+
+imap-2006j is a maintenance release, consisting primarily of bugfixes to
+problems discovered in the release that affected a small number of users.
+
+
+Updated: 5 June 2007
+
+imap-2006i is a maintenance release, consisting primarily of bugfixes to
+problems discovered in the release that affected a small number of users.
+
+imapd now supports the CHILDREN and ESEARCH extensions.
+
+imapd's attempt to return COPYUID/APPENDUID information for a traditional
+UNIX (and MMDF) format mailbox when the mailbox is open by another process
+has been declared to be a failure and is now revoked. It was subject to a
+timing race, loss of which involved an expensive reset of the mailbox's UID
+regime. Any imapd COPY or APPEND to a traditional UNIX or MMDF format that
+is open by some other process will now no longer return COPYUID/APPEND.
+Although this is technically in violation of RFC 4315, there is a loophole
+in that document and the timing race/performance problem is worse.
+
+
+Updated: 4 April 2007
+
+imap-2006h is a maintenance release, consisting primarily of bugfixes to
+problems discovered in the release that affected a small number of users.
+
+
+Updated: 30 March 2007
+
+imap-2006g is a maintenance release, consisting primarily of bugfixes to
+problems discovered in the release that affected a small number of users.
+
+
+Updated: 30 January 2007
+
+imap-2006f is a maintenance release, consisting primarily of bugfixes to
+problems discovered in the release that affected a small number of users.
+
+For the benefit of multi-threaded applications, use of strtok() has been
+abolished in the c-client library. imapd and ipop3d stuff use it though.
+The TOPS-20 and VAX/VMS ports still use strtok() since they don't use UNIX
+threads.
+
+This version has been test-built on Linux, Mac OS X, NeXT, Windows XP,
+TOPS-20, and VAX/VMS. This will probably be the last test-build on VAX/VMS
+since the system I use for that purpose is being shut down. I have no way
+to test-build on DOS, legacy Mac OS (OS 9 and earlier), OS/2, or Windows CE;
+and the builds on those systems are probably broken.
+
+
+Updated: 26 January 2007
+
+imap-2006e is a maintenance release, consisting primarily of bugfixes to
+problems discovered in the release that affected a small number of users.
+
+
+Updated: 6 December 2006
+
+imap-2006d is a maintenance release, consisting primarily of bugfixes to
+problems discovered in the release that affected a small number of users.
+
+The decomposition mapping, title-case mapping, and character widths tables
+have been updated to comply with the Unicode 5.0 standard.
+
+Prototypes for the utf8aux.c functions have been moved to a new utf8aux.h.
+
+The general c-client modules now include c-client.h instead of the individual
+files. Use of c-client.h instead of individual include files insulates
+against future shuffling of include files.
+
+
+Updated: 23 October 2006
+
+imap-2006c is a maintenance release, consisting primarily of bugfixes to
+problems discovered in the release that affected a small number of users.
+
+By popular request, if a user has a mix (or other dual-use) format INBOX,
+it will no longer be listed as \NoInferiors. It's a bad idea to depend
+upon this due to the case ambiguity issue, but it's there.
+
+
+Updated: 26 September 2006
+
+imap-2006b is a maintenance release, consisting entirely of bugfixes to
+problems discovered in the release that affected a small number of users.
+
+
+Updated: 15 September 2006
+
+imap-2006a is a maintenance release, consisting entirely of bugfixes to
+problems discovered in the release that affected a small number of users.
+
+If it is necessary to build IPv4-only on one of the ports that has IPv6
+preconfigured (ldb, lfd, lmd, lrh, lsu, osx, oxp), this can be done by
+using IP6=4. You can't do IP=4 in the build command directly since these
+ports set IP themselves; however, now instead of setting IP=6 they now set
+IP=$(IP6).
+
+
+Updated: 30 August 2006
+
+imap-2006 is a major release. Programs written for imap-2004g should
+build with this version with minor or no modification. imap-2005 was not
+released except as development snapshots.
+
+imap-2006 contains major extensions to its Unicode support. Searching and
+sorting are now done with strings canonicalized to titlecase and decomposed
+form. Among other things, this means that Latin letters with diacriticals
+will now sort with the basic Latin letter, and case-independent searching of
+such letters (e.g., German umlauts) now works. Previously, sorting was done
+strictly by Unicode codepoint, and case-independence only worked with ASCII.
+
+imapd now supports the UIDPLUS extension for mailboxes in unix, mmdf, mbx, mx,
+and mix formats. UID EXPUNGE is fully implemented. Note that UIDPLUS is not
+supported in the little-used drivers (mh, mtx, tenex) in which meaningful
+APPENDUID/COPYUID data can not be returned. Refer to bugs.txt for more
+details.
+
+The new mix format is a dual-use mailbox format designed for performance and
+reliability with large mailboxes. mix is documented in file mixfmt.txt.
+
+SSL/TLS certificate validation on UNIX now checks the alternative names in the
+certificate if the CN does not match.
+
+The new /tls-sslv23 flag in a mailbox name causes a TLS session to use the
+(incorrect) SSLv23 client method instead of the TLSv1 client method. Some
+broken servers use the SSLv23 server method, and this flag works around that
+problem. WARNING: use of this flag will cause TLS negotiation to fail with
+a server which uses the proper TLSv1 server method. Additionally, there are
+known security risks in SSLv2; so users should be suspicious if this switch
+suddenly becomes necesary.
+
+The silly mailbox flag combination /ssl/tls is now rejected as an invalid
+remote specification. Previous versions tried to negotiate TLS over an SSL
+session; even if the server permitted such a thing it couldn't work.
+
+The memory management of several drivers has been redesigned to consume less
+memory and hopefully be faster.
+
+The private.data member of the MESSAGECACHE (elt) has been replaced with
+a union that contains private.spare.data and private.spare.ptr, the latter
+being a pointer.
+
+A new FT_RETURNSTRINGSTRUCT flag has been added for mail_fetch_body() and
+mail_fetch_text() calls. If this flag is set, *and* if the function returns
+NIL, then the requested string data is available on a stringstruct on
+stream->private.string. This is a special hack for the IMAP and POP servers
+and is subject to incompatible change. The result is a major performance
+improvement in the servers with the mbx driver, particularly with large
+messages.
+
+
+Updated: 15 September 2005
+
+imap-2004g is a maintenance release, and consists solely of a bugfix to
+quoted string handling in the mailbox name parsing routine.
+
+
+Updated: 15 August 2005
+
+imap-2004f is a maintenance release, and consists solely of a bugfix to
+the TCP code.
+
+Also included is a new version of the UNIX SSL/TLS routines that allows the
+SSL/TLS certificate validation client code to validate alternative names in
+server certificates. This code has not been thoroughly regression-tested but
+is believed to work. To use this new code instead of the old support:
+ cd imap-2004f/src/osdep/unix
+ mv ssl_unix.c ssl_unix.old
+ mv ssl_unix.new ssl_unix.c
+Then rebuild.
+
+
+Updated: 21 June 2005
+
+imap-2004e is a maintenance release, consisting entirely of bugfixes.
+
+There are no user-visible functional enhancements in this version.
+
+
+Updated: 20 April 2005
+
+imap-2004d is a maintenance release, released concurrently with Pine
+4.63, and consists primarily of bugfixes
+
+There is now a workaround for RedHat breaking flock(). However, since
+RedHat has said that they don't support flock(), there is no guarantee
+that they won't break it in the future. So you may want to consider some
+other Linux distribution or BSD instead. See:
+ https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=123415
+for the gruesome details.
+
+There are no user-visible functional enhancements in this version.
+
+
+Updated: 18 January 2005
+
+imap-2004c is a maintenance release, released concurrently with Pine
+4.62, including fixes to quoted-printable encoding and CRAM-MD5
+authentication.
+
+NNTP proxy in imapd now supports the LIST and LSUB commands.
+
+There are no other user-visible functional enhancements in this version.
+
+
+Updated: 29 November 2004
+
+imap-2004b is a maintenance release, consisting primarily of bugfixes.
+Programs written for imap-2004a will build with this version without
+modifification.
+
+There are new ports for Solaris with Blastwave Community Open Source
+Software (gcs) and Mandrake Linux (lmd).
+
+SET_SNARFINTERVAL now controls how frequently local drivers will move new
+mail from the mail spool as well as from a maildrop. Maildrops are still
+tied to a minimum interval of 1 minute, but there is now no minimum for the
+spool file.
+
+Character set conversions now map non-breaking space to space if the
+destination character set doesn't have nbsp. JIS Roman yen sign is now
+mapped to Unicode yen sign.
+
+There are no user-visible functional enhancements in this version.
+
+
+Updated: 8 July 2004
+
+imap-2004a is a maintenance release, consisting primarily of critical
+bugfixes. Programs written for imap-2004 will build with this version
+without modification.
+
+imapd now has a supported NNTP proxy capability. If the file /etc/imapd.nntp
+exists, the contents of that file are used as the host name of an NNTP
+server which will be used whenever a #news. name is used. For example, if
+/etc/imapd.nntp contains nntp.example.com, and the IMAP client SELECTs or
+EXAMINEs the name #news.comp.mail.imap, what will actually be opened in
+imapd is {nntp.example.com/nntp}comp.mail.imap
+
+The OSF/1 port (Digital UNIX, Tru64) now uses flocksim instead of flcksafe.
+Some cretin decided to delete the winning flock() call and make flock() use
+the losing fcntl() call instead.
+
+The unix[nt] and mmdf drivers now prevent mail_append() from writing Status:,
+X-Status:, X-UID, X-IMAP[base]:, and X-Keywords: header lines to a
+traditional UNIX or MMDF format mailbox. If any such lines are in the
+text supplied to mail_append(), they will be quoted by prefixing with
+"X-Original-" (e.g. Status: will become X-Original-Status:).
+
+There are no user-visible functional enhancements in this version.
+
+
+Updated: 10 May 2004
+
+imap-2004 is a major release. Programs written for imap-2002e should
+build with this version with minor modification. imap-2003 was not
+released except as development snapshots.
+
+mailutil has three new commands: delete, rename, and prune.
+
+IPv6 support now exists for UNIX and W2K. It is the default in W2K builds.
+On UNIX, add "IP=6" to the make command line. Windows IPv6 support is
+only for W2K builds.
+
+The NNTP driver now supports NNTP SASL and TLS.
+
+The ldb (Debian) and lrh (RedHat) ports now look for mlock on
+/usr/sbin/mlock instead of /etc/mlock.
+
+imapd now supports the LITERAL+ and SASL-IR initial-response extensions.
+
+The IMAP driver has some additional checks to reduce the amount of network
+traffic, including executing "silly searches" (searches of sequence numbers
+only) locally.
+
+The IMAP, POP, SMTP, and NNTP drivers now have diagnostic code to provide
+better information about servers which violate SASL's empty challenge
+requirements (e.g. with the PLAIN mechanism).
+
+There is a new mail_fetch_overview_sequence() function which is like
+mail_fetch_overview() but takes a sequence number string as an argument.
+There should have been a flags argument and FT_UID bit as in all the other
+mail_fetch_???() functions but compatibility with the past... :-(
+
+The overview_t callback (from mail_fetch_overview()) now has a fourth
+argument which contains the message sequence number (as opposed to the UID
+which is in the second argument). It turned out that some applications were
+calling mail_msgno() (which can be moderately expensive) to get the sequence
+number, and c-client already knew it.
+
+Many declarations which are completely internal to a driver have been removed
+from the driver .h file, and in those cases where there are no external
+declarations left the .h file has been eliminated entirely. As part of this,
+the mbox driver routines are now incorporated with the unix driver routines
+as opposed to being a separate file. The mbox driver still needs to be lunk
+in order to get the mbox functionality.
+
+
+Updated: 27 August 2003
+
+imap-2002e is a minor release, released concurrently with Pine 4.58, and
+contains primarily bugfixes. Programs written for imap-2002d will build
+with this version without modification.
+
+The NNTP client code now tries to perform better with legacy NNTP servers
+which do not comply with the current NNTP protocol specification draft, most
+notably Netscape Collabra.
+
+Delivery notifications now work reliably with SMTP servers that support it.
+
+The following changes are primarily of concern to developers and power users:
+
+There is a "limited advertise" option in env_unix.c which, if set, will only
+advertise the user's own namespace and the #shared/ namespace.
+
+It is now possible to build the IMAP toolkit with a separate SSL KEY file
+from the certificate file (SSLKEYS vs. SSLCERTS).
+
+A new BODY structure element, sparep, is available for the main program to
+use as a pointer for its own purposes; as well as a SET_FREEBODYSPAREP
+function, similar to SET_FREEENVELOPESPAREP, SET_FREEELTSPAREP, etc.
+
+
+Updated: 28 May 2003
+
+imap-2002d is a minor release, released concurrently with Pine 4.56, and
+contains primarily bugfixes. Programs written for imap-2002 should build
+with this version without modification, with one exception. That exception
+is the ngbogus envelope flag, which stopped being used in imap-2002c and is
+now gone for good.
+
+The NNTP newsgroup listing code now tries to use wildmats on the NNTP server,
+which should result in better performance especially on slow lines. It is
+also once again permitted to log in on NNTP servers when /loser is set.
+
+imapd now supports the UNSELECT command.
+
+A new envelope flag, imapenvonly, indicates that the envelope in a
+MESSAGE/RFC822 BODY structure only has the IMAP envelope components and
+not the additional components from c-client: Newsgroups, Followup-To,
+and References.
+
+
+Updated: 7 April 2003
+
+imap-2002c is a minor release, released concurrently with Pine 4.55, and
+contains primarily bugfixes. Programs written for imap-2002 will build
+with this version without modification.
+
+The POP3 driver will, with new servers that support CAPA, use the LIST
+command to get the elt->rfc822_size and the TOP command to get the message
+header, instead of fetching the entire message. Note that it is a bad idea
+to do this with old servers, since they may misimplement LIST and TOP. The
+result is a substantial performance improvement.
+
+Subject extraction for comparisons in SORT and THREAD are now done in full
+compliance with the rules laid out in the specification. This only makes
+a difference if "re:" was part of a MIME quoted-word.
+
+The new experimental #move namespace allows download-and-delete from a source
+mailbox to a destination mailbox. Immediately following #move is a delimiter
+character which must not appear in the source mailbox name, then the source
+mailbox name, then the delimiter again, then the destination mailbox name.
+For example:
+ #move+{pop3.foo.com/pop3}+INBOX
+will download messages from "pop3.foo.com" into your local INBOX.
+
+The NNTP driver now uses the LIST EXTENSIONS command as described in the
+current NNTP protocol specification draft, and will prefer to use OVER over
+XOVER, HDR over XHDR, etc.
+
+The SET_NNTPRANGE function of mail_parameters() can be used to limit the
+number of articles recognized by the NNTP driver, resulting in a substantial
+performance improvement with NNTP servers that may have hundreds of thousands
+of old articles in the spool. If set non-zero, then only the last n article
+numbers will be considered. If you are on a slow link, you may want to set
+this to 1000 or less.
+
+Besides the normally tested UNIX and 32-bit Microsoft platforms, this release
+has also been tested and will once build under TOPS-20 and VAX/VMS. I also
+fixed a bug which would keep it from building on 16-bit DOS, but I don't know
+if it will build on that platform or not since I no longer have a system with
+the old DOS C compiler. It has not been tested on Macintosh (note however
+that Mac OS X is a type of UNIX and should build), Amiga, or OS/2, and probably
+no longer builds on those platforms.
+
+
+Updated: 7 January 2003
+
+imap-2002b is a maintenace release, released concurrently with Pine 4.52,
+and contains only bugfixes. Programs written for imap-2002 will build with
+this version without modification.
+
+Drivers which do not announce new mail are now indicated by the DR_NONEWMAIL
+driver flag. Driver which do not announce new mail when read-only are now
+indicated by the DR_NONEWMAILRONLY flag.
+
+There are no user-visible functional enhancements in this version.
+
+
+Updated: 10 December 2002
+
+imap-2002a is a maintenance release, consisting entirely of critical
+bugfixes. Programs written for imap-2002 will build with this version
+without modification.
+
+There are no functional enhancements in this version.
+
+
+Updated: 28 October 2002
+
+imap-2002 is a major release. Programs written for imap-2001 will probably
+build with this version without modification, with one exception. That
+exception is if the program uses [GS]ET_DISABLEAUTOMATICSHAREDNAMESPACES,
+which has been renamed to [GS]ET_DISABLEAUTOSHAREDNS in order to placate
+some compilers which don't like very long names.
+
+SSLTYPE=nopwd is now the default, in accordance with current IESG security
+requirements. In order to build the IMAP toolkit without SSL/TLS you must
+now use SSLTYPE=none. At initial build time, you will be told if the SSLTYPE
+setting is in compliance with IESG security requirements, and if it is not
+you will be asked to confirm to continue the build.
+
+ORDEREDSUBJECT threading has been changed in accordance with draft 12 of the
+IMAP threading specification. Previously, each non-root message in an
+ORDEREDSUBJECT thread has been a child of the message immediately preceeding
+it in the thread. Draft 12 changes this so that the second message in the
+thread is the child of the first (root) message, and all subsequent messages
+are siblings of the first message. This is significant in MUAs which display
+the thread structure graphically; the new definition is much saner than the
+old one since it does not nest endlessly due to parent/child relationships
+that may not exist. This also impacts imapd, since imapd's THREAD command
+will return a thread structure.
+
+RFC 1730 server support, which was disabled in imap-2001, is now fully
+removed from imapd. imapd still supports IMAP2bis, specifically the FIND
+command, since there are still a few IMAP2 clients out there.
+
+The IMAP client routines in the c-client library continue to support recognize
+RFC 1730 servers, but do not implement the deprecated features of RFC 1730.
+
+The Frequently Asked Questions file is now in HTML format, although a text
+version (generated from the HTML version with Lynx) is also provided.
+
+A new program, mailutil, is now bundled with the IMAP toolkit. mailutil
+replaces the old chkmail, imapcopy, imapmove, imapxfer, mbxcopy, mbxcreat,
+and mbxcvt programs that were distributed in the imap-utils. In addition,
+the tmail, dmail, and mlock programs from the imap-utils are now also
+bundled with the IMAP toolkit.
+
+In addition to the usual bugfixes, the following c-client functionalities
+are new in imap-2002:
+
+The SET_DISABLE822TZTEXT parameter allows a client to suppress generation of
+the "human friendly" time zone text in RFC822 dates. This placates netnews
+and some broken SMTP servers which think that long timezone names from Windows
+are an attempt at a buffer overflow attack.
+
+The restrictBox option in env_unix.c sets "restricted box" functionality,
+which disables access to the root (leading "/"), access to other user's
+directories (leading "~"), and access to superior directories via "..".
+
+Content-Location is now supported by the "location" member of the BODY
+structure. Note that there is a bug in the IMAP client code in older
+versions of the c-client library that causes it to handle BODYSTRUCTURE
+extension data improperly if that data is a literal. The new functionality
+for Content-Location may trigger this bug. The fix is either to upgrade
+the IMAP client program to the imap-2002 version of c-client or to remove
+the Content-Location support from imapd.
+
+There are now 8 spare bits for application use in both the elts and the
+mail streams.
+
+mail_search() now returns a value (previously it was void). If mail_search()
+returns NIL, then the supplied charset was invalid or the IMAP server
+returned NO (probably because the supplied charset was invalid).
+
+New utf8_charset() routine to look up a charset and return c-client's
+database about that charset if found. Among other things, this will give
+you the scripts supported by that charset and its Unicode conversion table.
+
+New FT_NOLOOKAHEAD flag for mail_fetch_structure() disables fetching of
+any envelopes other than the one specified. Otherwise, it will try to do
+anticipatory fetching (up to IMAPLOOKAHEAD).
+
+New GET_FETCHLOOKAHEAD allows better control of mail_fetch_structure()
+lookahead. Instead of looking IMAPLOOKAHEAD messages forward from the
+specified message, it will use a supplied SEARCHSET to generate message
+sequences and ranges. It will stop at IMAPLOOKAHEAD messages or at the
+completion of a range which exceeds IMAPLOOKAHEAD. The search set only
+applies to the next mail_fetch_structure() on that stream, and is cleared
+once it is used. Call with
+ SEARCHSET **set = (SEARCHSET **)
+ mail_parameters (stream,GET_FETCHLOOKAHEAD,(void *) stream);
+ *set = pointer to desired search set
+
+New mail_shortdate() routine returns an date in the format expected by
+SEARCHPGMs.
+
+
+Updated: 2 November 2001
+
+imap-2001a is a maintenance release, consisting primarily of bugfixes
+including some critical bugfixes to crash and denial of service problems.
+Programs written for imap-2001 will build with this version without
+modification.
+
+The following new facilities have also been added:
+
+The new /norsh switch in mailbox names provides a more intuitive way of
+disabling rsh-IMAP than the existing :143 or setting the rsh-timeout to 0.
+
+Passwords are no longer returned in mm_dlog() callbacks unless the
+application sets the SET_DEBUGSENSITIVE parameter.
+
+The SET_NETFSSTATBUG parameter allows an application to force the
+traditional UNIX mailbox driver to close and reopen the mailbox at ping
+time. This is EXTREMELY inefficient, and should only be used to access
+files stored on AFS and old NFS systems.
+
+The ISO 8859 and Windows conversion tables have been updated to comply
+with Unicode 3.1, and the KOI8-R table has been verified as compliant with
+Unicode 3.1.
+
+The SPECIALS mechanism for passing parameters to the lowest level Makefile
+has been updated to be more general. See the next item for why you might
+care.
+
+New lrh port to build on Red Hat Linux 7.2, with pre-set definitions for
+the places where Red Hat has placed Kerberos and SSL. It's actually just
+the lnp port with SPECIALS defined accordingly. You may want to use it as
+a model if your system needs such definitions. Note that SPECIALS is
+primarily for IMAP toolkit (and Pine) purposes, and that user settings
+should use EXTRASPECIALS instead.
+
+
+Updated: 22 June 2001
+
+imap-2001 is a major release. Programs written for imap-2000 will probably
+build with this version without modification.
+
+The FAQ document has been significantly expanded. Be sure to read it for
+more information.
+
+In addition to the usual bugfixes, the following features are new in
+imap-2001:
+
+SSL is now fully integrated into the IMAP toolkit; the old "alt" kludges to
+be able to produce a "sanitized" version of the IMAP toolkit to comply with
+late unlamented US export regulations are now completely gone.
+
+Full client and server TLS support is also in this release.
+
+The server certificate must be signed by a trusted certificate authority and
+the name in the certificate match the user's entry for the server host name;
+this means that the user must enter a fully-qualified host name.
+
+To build with SSL/TLS on UNIX, you now use "SSLTYPE=unix" instead of the
+former "SPECIALAUTHENTICATORS=ssl". To build with SSL/TLS on UNIX and disable
+the use of plaintext passwords except when under SSL/TLS, use "SSLTYPE=nopwd"
+instead of "SSLTYPE=unix".
+
+RFC 1730 (IMAP4 as opposed to IMAP4rev1) support is turned off by default in
+imapd. No clients should still be using RFC 1730 protocol. Look at the imapd
+Makefile for how to re-enable RFC 1730 support. Note that this code may be
+removed in the future, so if you think you need it you had better let me know.
+
+There are some new options (turned off by default) which attempt to work around
+problems in certain clients. See the FAQ file for more details.
+
+
+Updated: 24 January 2001
+
+imap-2000c is a maintenance release, consisting primarily of bugfixes.
+
+
+Updated: 9 January 2001
+
+imap-2000b is a maintenance release, consisting primarily of bugfixes.
+
+
+Updated: 9 November 2000
+
+imap-2000a is a maintenance release, consisting primarily of bugfixes.
+
+
+Updated: 19 September 2000
+
+imap-2000 is a major release. There are major internal and external changes
+from earlier versions (imap-4.x and imap-3.x series). Programs written for
+imap-4.x will probably build with this version without modification. It is
+extremely unlikely that a program written for imap-3.x or earlier series will
+build with this version without modifications. Drivers written for earlier
+versions will definitely need to be rewritten.
+
+In addition to the usual bugfixes, the following features are new in imap-2000:
+
+SSL support is now available. For UNIX, it is necessary to install some
+version of OpenSSL; see imap-2000/docs/SSLBUILD for more information. SSL
+support is now automatic for the NT, NTK, and W2K ports. SSL use is indicated
+by the /ssl switch in the mailbox name.
+
+With SSL connections, the server certificate is validated by the client code
+on UNIX, and Windows 2000 unless /novalidate-cert is specified. Server
+certificates are currently is not validated on Windows 9x, Windows Millenium,
+or Windows NT 4; this is an artifact of the operating system and not the port
+(e.g. client code using the NT port will validate certificates if running on
+Windows 2000). On UNIX, the server certificate must be signed by a trusted
+certificate authority. On Windows 2000, the certificate must be signed by a
+trusted certificate authority and match the user's entry for the server host
+name; this means that the user must enter a fully-qualified host name.
+
+Calendar reclama for the benefit of old broken non-Y2K compliant software.
+Two digit years from 00 to 69 will be interpreted as 2000 through 2069. In
+addition, three digit years from 100 to 105 will be interpreted as 2000
+through 2005.
+
+Support for REFERENCES threading (in addition to the previously-existing
+ORDEREDSUBJECT threading).
+
+Support for the IMAP MULTIAPPEND extension. This allows much faster uploading
+of multiple messages to an IMAP server.
+
+Support for the LOGINDISABLED IMAP capability. If the IMAP server sends
+LOGINDISABLED as a capability, the client code will never attempt to send an
+IMAP LOGIN command.
+
+Support for SASL authentication identity vs. authorization identity. If the
+authentication method does not support this concept (e.g. AUTH=CRAM-MD5,
+AUTH=LOGIN, LOGIN command), the "*" character in the user name may be used to
+indicate a separate authentication identity; for example, "fred*joe" indicates
+authorization identity "fred", authentication identity "joe".
+
+
+UNIX-specific Changes:
+
+Support for SASL authentication identity vs. authorization identity in the
+IMAP and POP3 servers. If the user indicated by the authentication identity
+is in the "mailadm" group, he may specify any authorization identity and get
+logged in as the authorization identity user.
+
+If the IMAP and POP3 servers are build with PASSWDTYPE=nul, it will send
+LOGINDISABLED as a capability and also disable the AUTH=LOGIN and AUTH=PLAIN
+SASL authenticators.
+
+New MAILSUBDIR build option to change the default mailbox directory from the
+user's home directory to a subdirectory of the user's home directory. See
+imap-2000/Makefile for more information.
+
+New CHROOT_SERVER build option for closed server systems only. If defined, a
+chroot() call to the user's home directory is done as part of the login
+process. See imap-2000/Makefile for more information.
+
+New ADVERTISE_THE_WORLD build option which will add an IMAP namespace that
+points to the root. Not for the faint of heart.
+
+UNIX format mailboxes no longer require the pseudo-message, nor will a
+pseudo-message be added to a mailbox that does not have one. A new
+X-IMAPbase: header will be written in the first message. This is rather less
+efficient and robust than the pseudo-message (which remains the encouraged
+mechanism; UNIX format mailboxes will always be created with it), but perhaps
+will pacify some people who get upset by the pseudo-message.
+
+When building with MIT Kerberos it will try to detect and use libk5crypto.a
+instead of libcrypto.a.
+
+The mbx driver is more aggressive about cleaning up expunged messages that
+couldn't be purged because of shared access to the mailbox at the time of
+expunge. Now, every checkpoint will try to purge such messages; and a
+checkpoint is attempted at close time.
+
+
+Windows-specific Changes:
+
+New W2K port for Windows 2000. In addition to supporting SSL using the
+official SSPI interface (the NT and NTK ports invoke SChannel.DLL directly),
+the W2K port also supports Microsoft Kerberos. Note that the NT and NTK ports
+will work on Windows 2000, but the W2K port will not work on NT4, Windows
+9x, or Windows Millenium.
+
+There is now a #user namespace, equivalent to the "~" namespace on UNIX.
+
+
+
+Changes for Developers:
+
+New c-client.h file which acts as a master include. c-client based
+applications should now include c-client.h instead of the individual c-client
+files (mail.h, misc.h, etc.). It is believed that c-client.h will work in C++
+applications.
+
+New GET_FREEENVELOPESPAREP/SET_FREEENVELOPESPAREP and
+GET_FREEELTSPAREP/SET_FREEELTSPAREP function callbacks to free the "sparep"
+member of the envelope and cache elements, respectively.
+
+New OP_MULNEWSRC flag to mail_open() to use multiple newsrc files, and new
+GET_NEWSRCQUERY/SET_NEWSRCQUERY function callbacks to get the name of the
+newsrc file for news access.
+
+New "secret" nntp_article() function to do the NNTP ARTICLE command; this is
+generally useful only when chasing news URLs.
+
+New GET_HIDEDOTFILES/SET_HIDEDOTFILES feature to suppress file names that
+start with "." in mail_list() results.
diff --git a/imap/docs/SSLBUILD b/imap/docs/SSLBUILD
new file mode 100644
index 00000000..962e8b29
--- /dev/null
+++ b/imap/docs/SSLBUILD
@@ -0,0 +1,267 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+ SSL/TLS BUILD AND INSTALLATION NOTES FOR UNIX
+ Last Updated: 15 November 2007
+
+PREREQUISITES BEFORE STARTING:
+ 1) Review the information in imap-2007/docs/BUILD.
+ 2) Obtain a copy of OpenSSL. OpenSSL is available from third parties. We
+ do not provide OpenSSL.
+ 3) Make sure that you know how to build OpenSSL properly on the standard
+ /usr/local/ssl directory. In particular, /usr/local/ssl/include (and
+ /usr/local/ssl/include/openssl) and /usr/local/ssl/lib must be set up
+ from the OpenSSL build. If you have a non-standard installation, then
+ you must modify the imap-2007/src/osdep/unix/Makefile file to point
+ to the appropriate locations.
+ 4) Make sure that you know how to obtain appropriate certificates on your
+ system.
+
+NOTE: We can NOT provide you with support in building/installing OpenSSL, or
+in obtaining certificates. If you need help in doing this, try the contacts
+mentioned in the OpenSSL README.
+
+
+SSL BUILD:
+
+ By default, the IMAP toolkit builds with SSL and disabling plaintext
+passwords unless SSL/TLS encryption is in effect (SSLTYPE=nopwd). This
+produces an IMAP server which is compliant with RFC 3501 security
+requirements.
+
+ To build with SSL but allow plaintext passwords in insecure sessions,
+add "SSLTYPE=unix" to the make command line. Note that doing so will
+produce an IMAP server which is NON-COMPLIANT with RFC 3501.
+
+ To build without SSL, add "SSLTYPE=none" to the make command line.
+Note that doing so will produce an IMAP server which is NON-COMPLIANT
+with RFC 3501.
+
+ There are other make options relevant to SSL, described in
+ imap-2007/src/osdep/unix/Makefile
+The most important of these are SSLDIR, SSLCRYPTO, and SSLRSA.
+
+ SSLDIR is set to /usr/local/ssl by default. This is the normal
+installation directory for OpenSSL. If your system uses a different directory
+you will need to change this.
+
+ SSLCRYPTO is set to -lcrypto by default. Older versions of MIT Kerberos
+also have a libcrypto and will cause a library name conflict. If you are
+using an older version of Kerberos, you may need to change SSLCRYPTO to
+$(SSLLIB)/libcrypto.a
+
+ SSLRSA is set empty by default. It can be set to specify the RSAREF
+libraries, which you once had to use with OpenSSL to use RSA algorithms
+legally if you are in the USA, due to patent issues. Since RSA Security Inc.
+released the RSA algorithm into the public domain on September 6, 2000, there
+is no longer any reason to do this.
+
+
+SSL INSTALLATION:
+
+ Binaries from the build are:
+ imap-2007/mtest/mtest c-client testbed program
+ imap-2007/ipopd/ipop2d POP2 daemon
+ imap-2007/ipopd/ipop3d POP3 daemon
+ imap-2007/imapd/imapd IMAP4rev1 daemon
+
+ mtest is normally not used except by c-client developers.
+
+STEP 1: inetd setup
+
+
+ The ipop2d, ipop3d, and imapd daemons should be installed in a system
+daemon directory and invoked by a listener such as xinetd or inetd. In the
+following examples, /usr/local/etc is used).
+
+STEP 1(A): xinetd-specific setup
+
+ If your system uses xinetd, the daemons are invoked by files in your
+/etc/xinetd.d directory with names corresponding to the service names (that
+is: imap, imaps, pop2, pop3, pop3s). You will need to consult your local
+xinetd documentation to see what should go into these files. Here is a a
+sample /etc/xinetd.d/imaps file:
+
+service imaps
+{
+ disable = no
+ socket_type = stream
+ wait = no
+ user = root
+ server = /usr/local/etc/imapd
+ groups = yes
+ flags = REUSE IPv6
+}
+
+STEP 1(B): inetd-specific setup
+
+ If your system still uses inetd, the daemons are invoked by your
+/etc/inetd.conf file with lines such as:
+
+pop stream tcp nowait root /usr/local/etc/ipop2d ipop2d
+pop3 stream tcp nowait root /usr/local/etc/ipop3d ipop3d
+imap stream tcp nowait root /usr/local/etc/imapd imapd
+pop3s stream tcp nowait root /usr/local/etc/ipop3d ipop3d
+imaps stream tcp nowait root /usr/local/etc/imapd imapd
+
+ Please refer to imap-2007/docs/BUILD for an important note about inetd's
+limit on the number of new connections. If that note applies to you, and you
+can configure the number of connection in /etc/inetd.conf as described in
+imap-2007/docs/build, here is the sample /etc/inetd.conf entry with SSL:
+
+pop3 stream tcp nowait.100 root /usr/local/etc/ipop3d ipop3d
+pop3s stream tcp nowait.100 root /usr/local/etc/ipop3d ipop3d
+imap stream tcp nowait.100 root /usr/local/etc/imapd imapd
+imaps stream tcp nowait.100 root /usr/local/etc/imapd imapd
+ (or, if you use TCP wrappers)
+pop3 stream tcp nowait.100 root /usr/local/etc/tcpd ipop3d
+imap stream tcp nowait.100 root /usr/local/etc/tcpd imapd
+pop3s stream tcp nowait.100 root /usr/local/etc/ipop3d ipop3d
+imaps stream tcp nowait.100 root /usr/local/etc/imapd imapd
+
+NOTE: do *NOT* use TCP wrappers (tcpd) for the imaps and pop3s services! I
+don't know why, but it doesn't work with TCP wrappers.
+
+
+STEP 2: services setup
+
+ You may also have to edit your /etc/services (or Yellow Pages,
+NetInfo, etc. equivalent) to register these services, such as:
+
+pop 109/tcp
+pop3 110/tcp
+imap 143/tcp
+imaps 993/tcp
+pop3s 995/tcp
+
+NOTE: The SSL IMAP service *MUST* be called "imaps", and the SSL POP3 service
+*MUST* be called "pop3s".
+
+
+STEP 3: PAM setup
+
+ If your system has PAM (Pluggable Authentication Modules -- most
+modern systems do) then you need to set up PAM authenticators for imap and
+pop. The correct file names are
+ /etc/pam.d/imap
+and
+ /etc/pam.d/pop
+
+ It probably works to copy your /etc/pam.d/ftpd file to the above two
+names.
+
+ Many people get these file names wrong, and then spend a lot of time
+trying to figure out why it doesn't work. Common mistakes are:
+ /etc/pam.d/imapd
+ /etc/pam.d/imap4
+ /etc/pam.d/imap4rev1
+ /etc/pam.d/imaps
+ /etc/pam.d/ipop3d
+ /etc/pam.d/pop3d
+ /etc/pam.d/popd
+ /etc/pam.d/pop3
+ /etc/pam.d/pop3s
+
+
+STEP 4: certificates setup
+
+NOTE: We can NOT provide you with support in obtaining certificates. If you
+need help in doing this, try the contacts mentioned in the OpenSSL README.
+
+WARNING: Do NOT install servers built with SSL support unless you also plan to
+install proper certificates! It is NOT supported to run SSL-enabled servers
+on a system without the proper certificates.
+
+ You must set up certificates on /usr/local/ssl/certs (this may be
+different if you have a non-standard installation of OpenSSL; for example,
+FreeBSD has modified OpenSSL to use /usr/local/certs). You should install
+both the certificate authority certificates from the SSL distribution after
+building OpenSSL, plus your own certificates. The latter should have been
+purchased from a certificate authority, although self-signed certificates are
+permissible. A sample certificate file is at the end of this document.
+
+ Install the resulting certificate file on /usr/local/ssl/certs, with a
+file name consisting of the server name and a suffix of ".pem". For example,
+install the imapd certificate on /usr/local/ssl/certs/imapd.pem and the ipop3d
+certificate on /usr/local/ssl/certs/ipop3d.pem. These files should be
+protected against random people accessing them. It is permissible for
+imapd.pem and ipop3d.pem to be links to the same file.
+
+ The imapd.pem and ipop3d.pem must contain a private key and a
+certificate. The private key must not be encrypted.
+
+ The following command to openssl can be used to create a self-signed
+certificate with a 10-year expiration:
+ req -new -x509 -nodes -out imapd.pem -keyout imapd.pem -days 3650
+
+ *** IMPORTANT ***
+ We DO NOT recommend, encourage, or sanction the use of self-signed
+certificates. Nor will we be responsible for any problems (including security
+problems!) which result from your use of a self-signed certificate. Use of
+self-signed certificates should be limited to testing only. Buy a real
+certificate from a certificate authority!
+
+ *** IMPORTANT ***
+
+ If you have a multihomed system with multiple domain names (and hence
+separate certificates for each domain name), you can append the IP address
+to the service name. For example, the IMAP certificate for [12.34.56.78]
+would be /usr/local/ssl/certs/imapd-12.34.56.78.pem and so on. You only need
+to use this feature if you need to use multiple certificates (because different
+DNS names are used).
+
+
+SAMPLE CERTIFICATE FILE
+
+ Here is a sample certificate file. Do *NOT* use this on your own
+machine; it is simply an example of what one would look like.
+
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDHkqs4YDbakYxRkYXIpY7xLXDQwULR5LW7xWVzuWmmZJOtzwlP
+7mN87g+aaiQzwXUVndaCw3Zm6cOG4mytf20jPZq0tvWnjEB3763sorpfpOe/4Vsn
+VBFjyQY6YdqYXNmjmzff5gTAecEXOcJ8CrPsaK+nkhw7bHUHX2X+97oMNQIDAQAB
+AoGBAMd3YkZAc9LUsig8iDhYsJuAzUb4Qi7Cppj73EBjyqKR18BaM3Z+T1VoIpQ1
+DeXkr39heCrN7aNCdTh1SiXGPG6+fkGj9HVw7LmjwXclp4UZwWp3fVbSAWfe3VRe
+LM/6p65qogEYuBRMhbSmsn9rBgz3tYVU0lDMZvWxQmUWWg7BAkEA6EbMJeCVdAYu
+nQsjwf4vhsHJTChKv/He6kT93Yr/rvq5ihIAPQK/hwcmWf05P9F6bdrA6JTOm3xu
+TvJsT/rIvQJBANv0yczI5pUQszw4s+LTzH+kZSb6asWp316BAMDedX+7ID4HaeKk
+e4JnBK//xHKVP7xmHuioKYtRlsnuHpWVtNkCQQDPru2+OE6pTRXEqT8xp3sLPJ4m
+ECi18yfjxAhRXIU9CUV4ZJv98UUbEJOEBtx3aW/UZbHyw4rwj5N511xtLsjpAkA9
+p1XRYxbO/clfvf0ePYP621fHHzZChaUo1jwh07lXvloBSQ6zCqvcF4hG1Qh5ncAp
+zO4pBMnwVURRAb/s6fOxAkADv2Tilu1asafmqVzpnRsdfBZx2Xt4oPtquR9IN0Q1
+ewRxOC13KZwoAWtkS7l0mY19WD27onF6iAaF7beuK/Va
+-----END RSA PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIIECTCCA3KgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBujELMAkGA1UEBhMCVVMx
+EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1NlYXR0bGUxHzAdBgNVBAoT
+FkJsdXJkeWJsb29wIEluZHVzdHJpZXMxFjAUBgNVBAsTDUlTIERlcGFydG1lbnQx
+ITAfBgNVBAMTGEJvbWJhc3RpYyBULiBCbHVyZHlibG9vcDEoMCYGCSqGSIb3DQEJ
+ARYZYm9tYmFzdGljQGJsdXJkeWJsb29wLmNvbTAeFw0wMDA2MDYwMDUxMTRaFw0x
+MDA2MDQwMDUxMTRaMIG6MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv
+bjEQMA4GA1UEBxMHU2VhdHRsZTEfMB0GA1UEChMWQmx1cmR5Ymxvb3AgSW5kdXN0
+cmllczEWMBQGA1UECxMNSVMgRGVwYXJ0bWVudDEhMB8GA1UEAxMYQm9tYmFzdGlj
+IFQuIEJsdXJkeWJsb29wMSgwJgYJKoZIhvcNAQkBFhlib21iYXN0aWNAYmx1cmR5
+Ymxvb3AuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDHkqs4YDbakYxR
+kYXIpY7xLXDQwULR5LW7xWVzuWmmZJOtzwlP7mN87g+aaiQzwXUVndaCw3Zm6cOG
+4mytf20jPZq0tvWnjEB3763sorpfpOe/4VsnVBFjyQY6YdqYXNmjmzff5gTAecEX
+OcJ8CrPsaK+nkhw7bHUHX2X+97oMNQIDAQABo4IBGzCCARcwHQYDVR0OBBYEFD+g
+lcPrnpsSvIdkm/eol4sYYg09MIHnBgNVHSMEgd8wgdyAFD+glcPrnpsSvIdkm/eo
+l4sYYg09oYHApIG9MIG6MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv
+bjEQMA4GA1UEBxMHU2VhdHRsZTEfMB0GA1UEChMWQmx1cmR5Ymxvb3AgSW5kdXN0
+cmllczEWMBQGA1UECxMNSVMgRGVwYXJ0bWVudDEhMB8GA1UEAxMYQm9tYmFzdGlj
+IFQuIEJsdXJkeWJsb29wMSgwJgYJKoZIhvcNAQkBFhlib21iYXN0aWNAYmx1cmR5
+Ymxvb3AuY29tggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAwEEk
+JXpVXVaFTuG2VJGIzPOxQ+X3V1Cl86y4gM1bDbqlilOUdByUEG4YfSb8ILIn+eXk
+WzMAw63Ww5t0/jkO5JRs6i1SUt0Oy80DryNRJYLBVBi499WEduro8GCVD8HuSkDC
+yL1Rdq8qlNhWPsggcbhuhvpbEz4pAfzPkrWMBn4=
+-----END CERTIFICATE-----
diff --git a/imap/docs/Y2K b/imap/docs/Y2K
new file mode 100644
index 00000000..12b84284
--- /dev/null
+++ b/imap/docs/Y2K
@@ -0,0 +1,145 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+QUESTION: Is c-client Y2K compliant?
+
+ANSWER:
+
+ There are no known Y2K issues in c-client; nor have there ever
+been any known Y2K issues in c-client from its inception.
+
+ Some older versions of c-client don't like the two-digit year
+"00", although the only impact of this is that messages with that year
+will sort before any other messages. Nobody should be using two-digit
+years in email messages any more (use "2000" instead of "00").
+
+ You may wish to read the document calendar.txt for more
+information about the Y3.3K/Y4K, Y20K, and Y4)K issues. Assuming that
+c-client is still around in 2000-40,000 years, someone will have to
+deal with these.
+
+ Within the plausible lifetimes of people today, there are three
+known date-related issues in c-client which will have to be addressed
+in the future. If I am still alive when the first problem hits, I
+will be nearly 82 years old, and won't be maintaining c-client any
+more.
+
+
+Y2038:
+
+ c-client, like most UNIX software, has Y2038 issues. On Tuesday,
+January 19, 2038 at 03:14:08 Coordinated Universal Time (also known as
+UTC, UT, or historically GMT), the clock on 32-bit UNIX systems will
+wrap around to a negative number; that is, from 0x7fffffff to
+0x80000000.
+
+ c-client uses an unsigned long for its 32-bit time; however the C
+library on most UNIX systems uses a signed long and will interpret
+that time as being Friday, December 13, 1901 at 20:45:52 UTC.
+
+ Fixing this problem will require changing the C library to use
+either unsigned longs or a wider (e.g. 64-bit) value for time. Lots
+of work will need to be done on 32-bit UNIX systems as 2038
+approaches. History suggests that most of the work will be done in
+the autumn of 2037... ;-) It's not known if anything is necessary to
+do to c-client other than just rebuild it with the new C library.
+
+ Going to 32-bit unsigned longs means that there will be a Y2106
+bug that someone will have to fix. Hopefully nobody will even think
+of using 32-bit systems by then.
+
+
+Y2070:
+
+ c-client assumes that 2-digit years with values of 70 or greater
+are in the 20th century, and that 2-digit years with values of 69 or
+less are in the 21st century. Time for UNIX began on January 1, 1970
+and email on ARPAnet happened between the first TENEX systems shortly
+after that; consequently there is no ambiguity with email data with
+2-digit years prior to the year 2070. This is used only when parsing
+a 2-digit year. c-client never generates one.
+
+ Fixing this problem requires convincing people not to use 2-digit
+years. This is a lesson that people should have figured out 70 years
+earlier with Y2K. Consequently, this may be a "non-problem."
+Otherwise, look in mail_parse_date() for the comment "two digit year"
+and change the statement as desired. [Note: do not change the
+definition of BASEYEAR since the UNIX port assumes that this matches
+when time began in the operating system.]
+
+
+Y2098:
+
+ On January 1, 2098, the year in per-message internal dates will
+expire, since a 7-bit field is allocated for the year. c-client will
+mistakenly think that the day is January 1, 1970.
+
+ Fortunately, it is easy to fix this problem. Just increase the
+width of "year" in MESSAGECACHE in mail.h. If you make it 8 bits,
+it'll be good until January 1, 2216; 9 bits makes it good until 2482.
+10 bits will push it back that you'd worry about the Y2800 question
+before having to increase it again. If you ignore Y2800, 11 bits will
+push it it back to having to worry about Y4K first.
+
+
+Y2800:
+
+ At this year, you will need to decide whether to keep the Gregorian
+calendar, which is one day slow every 20,000 years, or go to the more
+accurate Eastern Orthodox calendar which is one day slow every 45,000
+years. The Gregorian and Eastern Orthodox calendars diverge at this
+year.
+
+ There hasn't been any statement about how the international
+community will deal with the situation of the Orthodox calendar being
+one day ahead of the Gregorian calendar between 2800 and 2900. This
+will happen again between 3200 and 3300, and at gradually increasing
+intervals until 48,300 when the shift becomes permanent (assuming no
+Y20K or Y40K fixes).
+
+ If you wish to make the transition to the Eastern Orthodox calendar,
+rebuild c-client with -DUSEORTHODOXCALENDAR=1. You can then ignore Y4K
+and Y20K!
+
+
+Y3.3K/Y4K:
+
+ Some time around the year 3300, the calendar has gotten one day
+behind. To remedy this, a little-known rule in the Gregorian calendar
+is that years that are evenly divisible by 4000 are not leap years.
+Unlike the other rules, this rule hasn't had effect yet, and won't for
+another 2000 years.
+
+ To fix the Y4K problem, just rebuild c-client with -DY4KBUGFIX=1.
+
+
+Y20K:
+
+ Those of you who stuck with the Gregorian calendar have a
+problem; the calendar is now one day slow. The Pope has not made any
+statement about how this problem will be fixed. Maybe they'll declare
+that 20,004 is also not a leap year or something.
+
+ There is no fix for this problem in c-client.
+
+
+Y40K:
+
+ Greeks, Serbs, Russians, and other Eastern Orthodox have spent
+the past 38,000 years laughing at westerners' increasingly futile
+efforts to keep the Gregorian calendar in order. The day of reckoning
+has come; the Orthodox calendar is now one day slow. The Patriarch of
+Istanbul (nee Constantinople) has not made any statement about how this
+will be fixed.
+
+ There is no fix for this problem in c-client.
diff --git a/imap/docs/bugs.txt b/imap/docs/bugs.txt
new file mode 100644
index 00000000..5f87b3ef
--- /dev/null
+++ b/imap/docs/bugs.txt
@@ -0,0 +1,234 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+ KNOWN BUGS/MISFEATURES/DEFICIENCIES IN THE IMAP TOOLKIT
+ Last Updated: 15 November 2007
+
+The following are known problems/deficiencies in the imap-2007 toolkit:
+
+ . Possible problems for some installations:
+ . In some versions of Redhat Linux, SVR4-style timezone name lookup
+ doesn't work properly due to a bug in glibc. The workaround is to
+ edit os_lnx.c to include tz_bsd.c instead of tz_sv4.c. Note that
+ other versions of Linux don't support BSD-style timezone name
+ lookup, so don't make this change unless it's needed on your system.
+ . In some systems, the OpenSSL distribution is installed other than at
+ the standard /usr/local/ssl location. If this is the case on your
+ system and you want to build with SSL support, you will need to set
+ the SSLDIR variable, either by including a setting of EXTRASPECIALS
+ in the make command line, e.g.
+ build lnp SPECIALAUTHENTICATORS=ssl EXTRASPECIALS="SSLDIR=/usr/ssl"
+ or by editing .../src/osdep/unix/Makefile
+ . /tmp, /usr/tmp or /var/tmp (if present), and the mail spool directory
+ must be protected 1777 (world write with sticky bit); otherwise
+ mailbox locking and updates won't work. An alternative to 1777 on
+ the mail spool directory is to install the mlock program that is
+ bundled with the IMAP toolkit.
+ . Multiple access protection locking does not work if the mailbox or
+ /tmp are NFS mounted.
+ . Shared access mailbox formats (mbx, mtx, mx, and tenex) do not work
+ well with NFS and such usage is not supported. mmdf and unix formats
+ are supported for use over NFS; however there won't be any multiple
+ access locking protection.
+ . Server startup delays may occur if a reverse DNS (IP address to name)
+ lookup on the client's IP address does not complete in an expeditious
+ fashion. This is actually a DNS problem and should be fixed in the
+ DNS and/or the server's host table. A workaround exists (see the
+ top-level Makefile for details) but is not recommended and can not
+ be used at all with Kerberos.
+ . At the insistance of the security gurus, SSL certification validation
+ is now on by default. This means that you must now use the new
+ /novalidate-cert switch if establishing an SSL connection to a server
+ with a self-signed certificate; i.e. if "imap.example.com" has a
+ self-signed certificate, you must use a mailbox name such as
+ {imap.example.com/ssl/novalidate-cert}INBOX
+ to get an SSL session instead of just
+ {imap.example.com/ssl}INBOX
+ . GCC 8.x and above on SGI systems does not correctly pass/return
+ structures which are smaller than 16 bytes and are not 8 bytes. The
+ problem is that structures are padded at the wrong end; e.g. a 4 byte
+ structure is loaded into the lower 4 bytes of the register when it
+ should be loaded into the upper 4 bytes of the register. This affects
+ IRIX 6 the most because it is a 64-bit system and 4 byte structures are
+ common. This compiler bug impacts the use of inet_ntoa() in c-client
+ and causes syslog messages to show IP addresses as 255.255.255.255
+ instead of the correct values. The fix is either to use SGI's C compiler
+ instead of GCC or link with an implementation of inet_ntoa() that was
+ built with GCC instead of the standard SGI C library version.
+ . By default, the UNIX SSL build assumes that RSAREF is not needed, because
+ RSA Security Inc. released the RSA public key encryption algorithm into
+ the public domain on September 6, 2000. There is no longer any need to
+ use RSAREF, and since RSAREF is slower than OpenSSL's RSA routines
+ there's good reason not to. If for some reason you still want to use
+ RSAREF, you will need to edit .../src/osdep/unix/Makefile to
+ change SSLRSA to load libRSAglue and librsaref.
+ . By default, the UNIX SSL build assumes that no name conflict exists
+ between OpenSSL and Kerberos 5. If you are using an older version
+ of Kerberos, you may need to edit .../src/osdep/unix/Makefile
+ to change SSLCRYPTO so that it loads the OpenSSL libcrypto library
+ explicitly as libcrypto.a.
+ . By default, host names are canonicalized via gethostbyname() and
+ gethostbyaddr() for everything except for SSL certificate validation.
+ This can represent a security bug due to DNS spoofing, but is more
+ likely to deliver results that users expect and also may be necessary
+ to get Kerberos to work. Set variable "trustdns" in mail.c to NIL if
+ you want to disable this.
+
+ . Bugs:
+ . It doesn't work to have a "}" character as a user name in /user= in a
+ mailbox name, even if the user name is quoted. In other words,
+ {example.com/user="foo}bar"}zap
+ won't work; foo will be interpreted as an unterminated quoted string
+ and the remote mailbox name will be
+ bar"}zap.
+ . The experimental mx driver has performance problems and shouldn't be used
+ . docs/internal.txt is out of date (again)
+
+ . UIDPLUS bugs/limitations:
+ . Not supported in all local file formats (see below).
+ . There are two known issues with UIDPLUS in the mmdf and unix formats:
+ (a) If the destination mailbox is currently selected (whether in this
+ or another session), no COPYUID or APPENDUID is returned. The other
+ choice was to assign a UID based upon the uid_last value and hope
+ that the session selecting the mailbox would pick it up and update
+ uid_last. The problem was a timing race if another message was
+ copied/appended to that mailbox before the selecting session updated
+ the mailbox. If the timing race is lost, then all UID in the mailbox
+ would be reassigned by the selecting session, thus making the
+ returned APPENDUID/COPYUID data useless and causing a performance
+ problem.
+ Earlier versions did the "hope for the best" method. This was
+ revoked in favor of not returning COPYUID/APPENDUID.
+ Although this violates RFC 4315, there is a loophole which, although
+ for other purposes, permits this behavior.
+ (b) There is a known failure if the destination mailbox is currently
+ selected by legacy software (e.g. older versions of the IMAP
+ server, Pine, etc.). In this case, all UIDs end up being
+ reassigned by the legacy software.
+
+ . Annoyances:
+ . Friendly host names (e.g. "server" instead of "server.foo.com") can't be
+ used in a mailbox name with SSL certificate validation; you have to enter
+ the fully-qualified domain name. This is a requirement established by
+ the security gurus.
+
+ . IMAP client limitations:
+ . No SASL protection mechanisms (SASL authentication mechanisms are
+ supported)
+
+ . NNTP client limitations:
+ . Non-standard IMAP SCAN extension not supported
+
+ . POP client limitations:
+ . No SASL protection mechanisms (SASL authentication mechanisms are
+ supported)
+ . No POP3 UID support
+ . Non-standard IMAP SCAN extension not supported
+
+ . SMTP client limitations:
+ . No SASL protection mechanisms (SASL authentication mechanisms are
+ supported)
+ . No support for use of TURN, ETRN, and pipelining.
+ . No support for enhanced status codes
+
+ . UNIX limitations:
+ . IPv6 is supported but is not the default on most platforms; you have to
+ use IP=6 in the make command
+ . Supported local file formats: mbx, mh, mmdf, mix, mtx, mx, news, phile,
+ tenex, unix
+ . Supported SASL mechanisms: CRAM-MD5, PLAIN, LOGIN, ANONYMOUS, GSSAPI
+ . Sticky UIDs are not supported in the mh, mtx, and tenex drivers
+ . Creation of keywords is not supported in the mh, mtx, and tenex drivers
+ . Copy and append of keywords only works in the mbx driver.
+ . Flat file formats (mbx, mmdf, mtx, phile, tenex, unix) do not permit
+ mailboxes to have inferior names
+ . SSL temporary key should be seeded better than it is.
+ . UIDPLUS support is limited to the unix, mmdf, mbx, mx, and mix formats.
+ . Non-standard IMAP SCAN extension not support for mh and news formats.
+
+ . Amiga limitations:
+ . Supported local file formats: mbx, mh, mmdf, mix, mtx, mx, news, phile,
+ tenex, unix
+ . Supported SASL mechanisms: CRAM-MD5, PLAIN, LOGIN, ANONYMOUS
+ . Sticky UIDs are not supported in the mh, mtx, and tenex drivers
+ . Creation of keywords is not supported in the mh, mtx, and tenex drivers
+ . Copy and append of keywords only works in the mbx driver.
+ . Flat file formats (mbx, mmdf, mtx, phile, tenex, unix) do not permit
+ mailboxes to have inferior names
+ . UIDPLUS support is limited to the unix, mmdf, mbx, mx, and mix formats.
+ . Non-standard IMAP SCAN extension not supported for mh and news formats.
+
+ . Win32 (Win9x/NT/Windows 2000) limitations:
+ . IPv6 is supported in W2K builds but is not the default; you have to use
+ IP=6 in the nmake command
+ . Supported local file formats: mbx, mtx, tenex, unix
+ . Supported SASL mechanisms: CRAM-MD5, PLAIN, LOGIN, ANONYMOUS, GSSAPI
+ . No server SSL or TLS support.
+ . No server authentication for GSSAPI
+ . No server authentication for CRAM-MD5 on NT-based Windows (NT/2K/XP);
+ it does work on DOS-based Windows (9x/Me).
+ . Sticky UIDs are not supported in the mtxnt and tenexnt drivers
+ . Creation of keywords is not supported in the mtxnt and tenexnt drivers
+ . Copy and append of keywords only works in the mbxnt driver.
+ . No support for TCP open timeouts
+ . Flat file formats (mbx, mtx, tenex, unix) do not permit mailboxes to have
+ inferior names
+ . UIDPLUS support is limited to the unix and mbx formats.
+
+ . Win16 (Win3.1)/DOS limitations:
+ . IPv6 not supported
+ . Supported local file formats: bezerk, mtx
+ . Supported SASL mechanisms: CRAM-MD5, LOGIN, ANONYMOUS
+ . Supported TCPs: B&W, Novell, PC-NFs, PC/TCP, Waterloo, Winsock
+ . Sticky UIDs are not supported on local files
+ . Creation of keywords are not supported on local files
+ . Bezerk driver is read-only and does not handle LF-only newlines well
+ . No support for any TCP timeouts on Waterloo DOS
+ . No support for TCP open timeouts on Winsock and generic DOS
+ . Flat file formats (bezerk, mtx) do not permit mailboxes to have inferior
+ names
+ . Does not work well unless a mailgets routine is armed when fetching
+ texts.
+
+ . Mac limitations:
+ . IPv6 not supported
+ . No local file drivers
+ . Supported SASL mechanisms: CRAM-MD5, LOGIN, ANONYMOUS
+ . Does not output human-friendly time zone string
+
+ . TOPS-20 limitations:
+ . IPv6 not supported
+ . No local file drivers
+ . Supported SASL mechanisms: CRAM-MD5, LOGIN, ANONYMOUS
+ . No support for any TCP timeouts
+
+ . VMS limitations:
+ . IPv6 not supported
+ . No local file drivers
+ . Supported SASL mechanisms: CRAM-MD5, LOGIN, ANONYMOUS
+ . Supported TCPs: Multinet, Netlib
+ . No support for any TCP timeouts on VMS Netlib
+ . No support for TCP open timeouts on VMS Multinet
+ . Time zone must be configured at build time
+ . Does not output human-friendly time zone string
+
+ . Windows CE limitations:
+ . IPv6 not yet supported
+ . No local file drivers
+ . Supported SASL mechanisms: CRAM-MD5, LOGIN, ANONYMOUS
+ . No support for TCP open timeouts
+ . Not finished, only builds c-client library
+
+ . OS/2 limitations:
+ . IPv6 not supported
+ . Not finished, does not build
diff --git a/imap/docs/calendar.txt b/imap/docs/calendar.txt
new file mode 100644
index 00000000..c1009078
--- /dev/null
+++ b/imap/docs/calendar.txt
@@ -0,0 +1,332 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+ ALL ABOUT CALENDARS
+
+ Although one can never be sure of what will happen at some future
+time, there is strong historical precedent for presuming that the
+present Gregorian calendar will still be in effect within the useful
+lifetime of the IMAP toolkit. We have therefore chosen to adhere to
+these precedents.
+
+ The purpose of a calendar is to reckon time in advance, to show
+how many days have to elapse until a certain event takes place in the
+future, such as the harvest or the release of a new version of Pine.
+The earliest calendars, naturally, were crude and tended to be based
+upon the seasons or the lunar cycle.
+
+
+ANCIENT CALENDARS
+
+ The calendar of the Assyrians, for example, was based upon the
+phases of the moon. They knew that a lunation (the time from one full
+moon to the next) was 29 1/2 days long, so their lunar year had a
+duration of 354 days. This fell short of the solar year by about 11
+days. (The exact time for the solar year is approximately 365 days, 5
+hours, 48 minutes, and 46 seconds.) After 3 years, such a lunar
+calendar would be off by a whole month, so the Assyrians added an extra
+month from time to time to keep their calendar in synchronization with
+the seasons.
+
+ The best approximation that was possible in antiquity was a 19-year
+period, with 7 of these 19 years having 13 months (leap months). This
+scheme was adopted as the basis for the lunar calendar used by the
+Hebrews. The Arabs also used this calendar until Mohammed forbade
+shifting from 12 months to 13 months; this causes the Muslim holy month
+of Ramadan to move backwards through the seasons, completing a cycle
+every 32 1/2 years.
+
+ When Rome emerged as a world power, the difficulties of making a
+calendar were well known, but the Romans complicated their lives because
+of their superstition that even numbers were unlucky. Hence their
+months were 29 or 31 days long, with the exception of February, which
+had 28 days. Every second year, the Roman calendar included an extra
+month called Mercedonius of 22 or 23 days to keep up with the solar
+year.
+
+
+JULIAN CALENDAR
+
+ Even this algorithm was very poor, so that in 45 BCE, Caesar,
+advised by the astronomer Sosigenes, ordered a sweeping reform. By
+imperial decree, the year 46 BCE was made 445 days long to bring the
+calendar back in step with the seasons. The new calendar, similar to
+the one we now use was called the Julian calendar (named after Julius
+Caesar).
+
+ Months in the Julian calendar were 30 or 31 days in length and
+every fourth year was made a leap year (having 366 days) by adding a day
+to the end of the year. This leap year rule was not consistantly
+applied until 8 CE. The year-ending month of February, never a popular
+month, was presently shortened so that Julius Caesar and Emperor
+Augustus could each have long months named after them.
+
+ Caesar also decreed that the year would start with the first of
+January, which since 153 BCE was the day that Roman consuls took office,
+and not the vernal equinox in late March. Not everyone accepted that
+part of his reform, as we shall see.
+
+
+GREGORIAN CALENDAR
+
+ Caesar's year was 11 1/2 minutes short of the calculations
+recommended by Sosigenes and eventually the date of the vernal equinox
+began to drift. Roger Bacon became alarmed and sent a note to Pope
+Clement IV, who apparently was not impressed. Pope Sixtus IV later
+became convinced that another reform was needed and called the German
+astronomer, Regiomontanus, to Rome to advise him. Unfortunately,
+Regiomontanus died of the plague shortly thereafter and the plans died
+as well.
+
+ In 1545, the Council of Trent authorized Pope Gregory XIII to
+reform the calendar once more. Most of the mathematical work was done
+by Father Christopher Clavius, S.J. The immediate correction that was
+adopted was that Thursday, October 4, 1582 was to be the last day of the
+Julian calendar. The next day was Friday, with the date of October 15.
+For long range accuracy, a formula suggested by the Vatican librarian
+Aloysius Giglio was adopted. It said that every fourth year is a leap
+year except for century years that are not divisible by 400. Thus 1700,
+1800 and 1900 would not be leap years, but 2000 would be a leap year
+since 2000 is divisible by 400. This rule eliminates 3 leap years every
+4 centuries, making the calendar sufficiently correct for most ordinary
+purposes. This calendar is known as the Gregorian calendar and is the
+one that we now use today.
+
+ It is interesting to note that in 1582, all the Protestant princes
+ignored the papal decree and so many countries continued to use the
+Julian calendar until either 1698 or 1752. Britain and its American
+colonies went from Wednesday, September 2, 1752 to Thursday, September
+14. Prior to the changeover, the British used March 25 as the start of
+the new year.
+
+ In Russia, it needed the revolution to introduce the Gregorian
+calendar in 1918. Turkey didn't adopt it until 1927.
+
+
+NUMBERING OF YEARS
+
+ The numbering of the year is generally done according to an "era",
+such as the year of a ruler's reign.
+
+ In about 525, a monk named Dionysius Exiguus suggested that the
+calculated year of Jesus' birth be designated as year 1 in the Julian
+calendar. This suggestion was adopted over the next 500 years and
+subsequently followed in the Gregorian calendar.
+
+ For the benefit of those who seek religious significance to the
+calendar millenium, note that year 1 is too late by at least 4 years.
+Herod the Great, named in the Christian Bible as having all children in
+Bethlehem put to death in an attempt to kill the infant Jesus, died in 4
+BCE.
+
+ Nothing particularly significant of an historic or religious nature
+happened in Gregorian year 1; however it has become a worldwide standard
+as the "common era." In modern times, the terms "CE" (common era) and
+"BCE" (before common era) are preferred over the earlier (and, as we
+have seen, less accurate) "AD" (anno Domini, "the year of the Lord") and
+"BC" (before Christ).
+
+ The Hebrew lunar calendar begins at 3760 BCE, the year of creation
+in Jewish tradition. The Muslim lunar calendar begins on July 16, 622,
+when Mohammed fled from Mecca to Medina.
+
+ The Japanese, Taiwanese, and North Koreans use the Gregorian
+calendar, but number the year by political era. In Japan, an era
+begins when an emperor succeeds to the throne; year 1 of the Heisei
+era was 1989 when Emperor Akihito ascended to the throne (the first
+few days of 1989 was year 64 of the Shouwa era). In Taiwan, year 1 is
+the first full year after the founding of the Republic of China in 1911.
+In North Korea, year 1 is the year of the Juche (self-reliance) ideal,
+corresponding to the birth year of founder Kim Il-Sung (1912). Thus,
+year 2000 is Heisei 12 (Japan), 89th year of the Republic (Taiwan),
+and Juche 89 (North Korea).
+
+
+FURTHER MODIFICATIONS TO THE GREGORIAN CALENDAR
+
+ Despite the great accuracy of the Gregorian calendar, it still
+falls behind very slightly every few years. The most serious problem
+is that the earth's rotation is slowing gradually. If you are very
+concerned about this problem, we suggest that you tune in short wave
+radio station WWV or the Global Positioning System, which broadcasts
+official time signals for use in the United States. About once every
+3 years, they declare a leap second at which time you should be
+careful to adjust your system clock. If you have trouble picking up
+their signals, we suggest you purchase an atomic clock (not part of
+the IMAP toolkit).
+
+ Another problem is that the Gregorian calendar represents a year
+of 365.2425 days, whereas the actual time taken for the earth to
+rotate around the Sun is 365.2421991 days. Thus, the Gregorian calendar
+is actually 26 seconds slow each year, resulting in the calendar
+being one day behind every 3,300 or so years (a Y3.3K problem).
+
+ Consequently, the Gregorian calendar has been modified with a
+further rule, which is that years evenly divisible by 4000 are not
+leap years. Thus, the year 4000 will not be a leap year. Or, at
+least we assume that's what will happen assuming that the calendar
+remains unchanged for the next 2000 years.
+
+ The modified Gregorian calendar represents a year of 365.24225
+days. Thus, the modified Gregorian calendar is actually 4 seconds
+slow each year, resulting in the calendar being one day slow every
+20,000 or so years. So there will be a Y20K problem.
+
+ There is some dispute whether the modified Gregorian calendar was
+officially adopted, or if it's just a proposal. Other options (see
+below) exist; fortunately no decision needs to be made for several
+centuries yet.
+
+ There is code in c-client to support the modified Gregorian
+calendar, although it is currently disabled. Sometime in the next
+2000 years, someone will need to enable this code so that c-client is
+Y4K compiliant. Then, 18,000 years from now, someone will have to
+tear into c-client's code to fix the Y20K bug.
+
+
+EASTERN ORTHODOX MODIFICATION OF THE GREGORIAN CALENDAR
+
+ The Eastern Orthodox church in 1923 established its own rules to
+correct the Julian calendar. In their calendar, century years modulo
+900 must result in value of 200 or 600 to be considered a leap year.
+Both the Orthodox and Gregorian calendar agree that the years 2000 and
+2400 will be leap years, and the years 1900, 2100, 2200, 2300, 2500,
+2600, 2700 are not. However, the year 2800 will be a leap year in the
+Gregorian calendar but not in the Orthodox calendar; similarly, the
+year 2900 will be a leap year in the Orthodox calendar but not in the
+Gregorian calendar. Both calendars will agree that 3000 and
+3100 are leap years, but will disagree again in 3200 and 3300.
+
+ There is code in c-client to support the Orthodox calendar. It
+can be enabled by adding -DUSEORTHODOXCALENDAR=1 to the c-client
+CFLAGS, e.g.
+ make xxx EXTRACFLAGS="-DUSEORTHODOXCALENDAR=1"
+
+ The Orthodox calendar represents a year of 365.24222222... days.
+Thus, the Orthodox calendar is actually 2 seconds slow each year,
+resulting in the calendar being one day slow every 40,000 or so years.
+The Eastern Orthodox church has not yet made any statements on how the
+Y40K bug will be fixed.
+
+
+OTHER ISSUES AFFECTING THE CALENDAR IN THE FUTURE
+
+ The effect of leap seconds also needs to be considered when
+looking at the Y3.3K/Y4K, Y20K, and Y40K problems. Leap seconds put
+the clock back in line with the Earth's rotation, whereas leap years
+put the calendar back in line with the Earth's revolution. Since leap
+seconds slow down the clock (and hence the calendar), they actually
+bring the day of reckoning for the Gregorian and Orthodox calendars
+sooner.
+
+ Another factor is that the next ice age (technically, the end of
+the current interglacial period; we are in the middle of an ice age
+now!) is due around Y25K. It is not known what perturbations this will
+cause on the Earth's rotation and revolution, nor what calendar
+adjustments will be necessary at that time.
+
+ Hence my use of "or so" in predicting the years that the calendar
+will fall behind. The actual point may be anywhere from decades (in the
+case of Y3.3K) to millenia (in the case of Y40K) off from these predictions.
+
+
+MEANINGS OF DAY NAMES
+
+ The names of days of the week from a combination of Roman and
+Germanic names for celestial bodies:
+. Sunday Latin "dies solis" => "Sun's day"
+. Monday Latin "dies lunae" => "Moon's day"
+. Tuesday Germanic "Tiw's day" => "Mars' day"
+. Wednesday Germanic "Woden's day" => "Mercury's day"
+. Thursday Germanic "Thor's day" => "Jupiter's day"
+. Friday Germanic "Frigg's day" => "Venus' day"
+. Saturday Latin "dies Saturni" => "Saturn's day"
+
+
+MEANINGS OF MONTH NAMES
+
+ The names of the months are from the Roman calendar:
+. January Janus, protector of doorways
+. February Februalia, a time for sacrifice to atone for sins
+. March Mars, god of war
+. April Latin "aperire" => "to open" buds
+. May Maia, goddess of plant growth
+. June Latin "juvenis" => "youth"
+. July Julius Caesar
+. August Augustus Caesar
+. September Latin "septem" => "seven"
+. October Latin "octo" => "eight"
+. November Latin "novem" => "nine"
+. December Latin "decem" => "ten"
+
+ As you'll notice, the last four months are numbered 7 to 10, which
+is an artifact of the time when the new year started in March.
+
+
+INTERESTING FORMULAE
+
+ There's another reason why the historical starting of the new year
+is significant. Starting with March, the length of months follows a
+mathematical series:
+ 31 30 31 30 31 31 30 31 30 31 31 28
+
+ This means that you can calculate the day of week for any
+arbitrary day/month/year of the Gregorian calendar with the following
+formula (note all divisions are integral):
+ _ _
+ | 7 + 31*(m - 1) y y y |
+ dow = | d + -------------- + y + - - --- + --- | MOD 7
+ |_ 12 4 100 400_|
+where
+ d := day of month (1..31)
+ m := month in old style (March = 1..February = 12)
+ y := year in old style
+ dow := day of week (Tuesday = 0..Monday = 6)
+
+ To convert from new style month/year to old style:
+ if (m > 2) m -= 2; /* Mar-Dec: subtract 2 from month */
+ else m += 10,y--; /* Jan-Feb: months 11 & 12 of previous year */
+
+ Here's another fun formula. To find the number of days between two
+days, calculate a pair of calendar days with the formula (again, all
+divisions are integral), using new style month/year this time:
+ m
+ m + -
+ 8 y y y
+ d + 30 * (m - 1) + ----- + y * 365 + - - --- + --- - ld
+ 2 4 100 400
+
+where:
+ d := day of month (1..31)
+ m := month in new style (January = 1..December = 12)
+ y := year in new style
+ ld := leap day correction factor:
+ 0 for January and February in non-leap years
+ 1 for January and February in leap years
+ 2 for all other months in all years
+
+ In C code, the leap day correction factor is calculated as:
+ (m < 3) ? !(y % 4) && ((y % 100) || !(y % 400)) : 2
+
+ It's up to you to figure out how to adapt these formulas for the
+Y4K bugfix and the Orthodox calendar. If you're really clever, try to
+use these formulae to implement the C library ctime(), gmtime(), and
+mktime() functions. Most C library implementations use a table of the
+number of days in a month. You don't need it.
+
+
+ACKNOWLEDGEMENT:
+
+The original version is from an old Digital Equipment Corporation SPR
+answer for VMS. Modifications for c-client, and additional information
+added by Mark Crispin.
diff --git a/imap/docs/commndmt.txt b/imap/docs/commndmt.txt
new file mode 100644
index 00000000..7fd9707b
--- /dev/null
+++ b/imap/docs/commndmt.txt
@@ -0,0 +1,101 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+[I wrote this tongue-in-cheek, but there's a lot here that people who
+ build IMAP clients should take careful note. Most existing clients
+ violate at least one, generally several, of these commandments.
+ These are based on known user-visible problems that occur with various
+ commonly used clients. Put another way, behind each commandment is a
+ plethora of user (and server administrator) complaints caused by a
+ violator.]
+
+ Ten Commandments of How to Write an IMAP client
+ Mark Crispin
+
+1. Thou shalt not assume that it is alright to open multiple IMAP
+sessions selected on the same mailbox simultaneously, lest thou face
+the righteous wrath of mail stores that doth not permit such access.
+Instead, thou shalt labor mightily, even unto having to use thy brain
+to thinketh the matter through, such that thy client use existing
+sessions that are already open.
+
+2. Thou shalt not abuse the STATUS command by using it to check for
+new mail on a mailbox that you already have selected in an IMAP
+session; for that session hath already told thou about new mail
+without thy having to ask.
+
+3. Thou shalt remember the 30 minute inactivity timeout, and remember
+to speak to the IMAP server before that timeout expires. If thou
+useth the IDLE command, thou shalt send DONE from the IDLE before 29
+minutes hath passed, and issue a new IDLE. If thou maketh no use of
+IDLE, then thou shalt send NOOP every few minutes, and the server
+shalt tell you about new mail, and there will be much rejoicing in the
+land.
+
+4. Thou shalt not assume that all names are both the name of a mailbox
+and the name of a upper level of hierarchy that contains mailboxes;
+lest thou face the righteous wrath of mail stores in which a mailbox
+is a file and a level of hierarchy is a directory. Thou shalt pay
+diligent attention to the \NoSelect and \NoInferiors flags, so that
+your users may praise you with great praise.
+
+5. Thou shalt learn and understand the unique features of IMAP, such
+as the unsolicited data model, the strict ascending rule of UIDs, how
+UIDs map to sequence numbers, the ENVELOPE and BODYSTRUCTURE
+structures; so that thou may use the IMAP protocol effectively. For a
+POP client hacked to babble IMAP protocol is still no more than a POP
+client.
+
+6. Thou shalt remember untagged data sent by the server, and when thou
+needest data thou shalt consult your memory before asking the server.
+For those who must analyze thy protocol transactions are weak of
+stomach, and are likely to lose their recent meal should they see thou
+repeatedly re-fetch static data.
+
+7. Thou shalt labor with great effort to work within the IMAP
+deleted/expunge model, even if thy own model is that of a trashcan;
+for interoperability is paramount and a trashcan model can be done
+entirely in the user interface.
+
+8. Thou shalt not fear to open multiple IMAP sessions to the server;
+but thou shalt use this technique with wisdom. For verily it is true;
+if thou doth desire to monitor continuously five mailboxes for new
+mail, it is better to have five IMAP sessions continuously open on the
+mailboxes. It is generally not good to do a succession of five SELECT
+or STATUS commands on a periodic basis; and it is truly wretched to
+open and close five sessions to do a STATUS or SELECT on a periodic
+basis. The cost of opening and closing a session is great, especially
+if that session is SSL/TLS protected; and the cost of a STATUS or
+SELECT can also be great. By comparison, the cost of an open session
+doing an IDLE or getting a NOOP every few minutes is small. Great
+praise shall be given to thy wisdom in doing what is less costly
+instead of "common sense."
+
+9. Thou shalt not abuse subscriptions, for verily the LIST command is
+the proper way to discover mailboxes on the server. Thou shalt not
+subscribe names to the user's subscription list without explicit
+instructions from the user; nor shalt thou assume that only subscribed
+names are valid. Rather, thou shalt treat subscribed names as akin to
+a bookmarks, or perhaps akin to how Windows shows the "My Documents"
+folder -- a set of names that are separate from the hierarchy, for
+they are such.
+
+10. Thou shalt use the LIST "*" wildcard only with great care. If
+thou doth not fully comprehend the danger of "*", thou shalt use only
+"%" and forget about the existance of "*".
+
+Honor these commandments, and keep them holy in thy heart, so that thy
+users shalt maximize their pleasure, and the server administrators
+shalt sing thy praises and recommend thy work as a model for others to
+emulate.
+
diff --git a/imap/docs/draft/README b/imap/docs/draft/README
new file mode 100644
index 00000000..9aec4719
--- /dev/null
+++ b/imap/docs/draft/README
@@ -0,0 +1,19 @@
+Last Updated: 6 March 2008
+
+This directory contains Internet Drafts which, at the time of release of
+this software, were not yet been published as RFCs. These documents are
+expected to be released as RFCs in the near future.
+
+This software adheres to the specification in these documents, which
+are included for informational purposes. Note, however, that these
+documents must be considered preliminary in nature and will be superceded
+by the successor RFC.
+
+File Name I-D Name
+--------- --------
+sort.txt draft-ietf-imapext-sort-20.txt
+ ;; SORT and THREAD commands
+ ;; Status: approved, blocked waiting for i18n
+
+i18n.txt draft-ietf-imapext-i18n-15.txt
+ ;; internationalization in IMAP
diff --git a/imap/docs/draft/i18n.txt b/imap/docs/draft/i18n.txt
new file mode 100644
index 00000000..f47c6cc7
--- /dev/null
+++ b/imap/docs/draft/i18n.txt
@@ -0,0 +1,1140 @@
+
+
+
+
+
+
+Network Working Group Chris Newman
+Internet-Draft Sun Microsystems
+Intended Status: Proposed Standard Arnt Gulbrandsen
+ Oryx Mail Systems GmhH
+ Alexey Melnikov
+ Isode Limited
+ February 1, 2008
+
+ Internet Message Access Protocol Internationalization
+ draft-ietf-imapext-i18n-15.txt
+
+
+Status of this Memo
+ By submitting this Internet-Draft, each author represents that any
+ applicable patent or other IPR claims of which he or she is aware
+ have been or will be disclosed, and any of which he or she becomes
+ aware will be disclosed, in accordance with Section 6 of BCP 79.
+
+ Internet-Drafts are working documents of the Internet Engineering
+ Task Force (IETF), its areas, and its working groups. Note that
+ other groups may also distribute working documents as Internet-
+ Drafts.
+
+ Internet-Drafts are draft documents valid for a maximum of six
+ months and may be updated, replaced, or obsoleted by other documents
+ at any time. It is inappropriate to use Internet-Drafts as
+ reference material or to cite them other than as "work in progress".
+
+ The list of current Internet-Drafts can be accessed at
+ http://www.ietf.org/ietf/1id-abstracts.txt. The list of Internet-
+ Draft Shadow Directories can be accessed at
+ http://www.ietf.org/shadow.html.
+
+ This Internet-Draft expires in August 2008.
+
+
+Copyright Notice
+
+ Copyright (C) The IETF Trust (2008).
+
+
+Abstract
+
+ Internet Message Access Protocol (IMAP) version 4rev1 has basic
+ support for non-ASCII characters in mailbox names and search
+ substrings. It also supports non-ASCII message headers and content
+ encoded as specified by Multipurpose Internet Mail Extensions
+ (MIME). This specification defines a collection of IMAP extensions
+
+
+
+Newman & Co Expires August 2008 FF[Page 1]
+
+
+
+
+
+Internet-draft February 2008
+
+
+ which improve international support including comparator negotiation
+ for search, sort and thread, language negotiation for international
+ error text, and translations for namespace prefixes.
+
+
+Table of Contents
+
+ 1. Conventions Used in this Document . . . . . . . . . . . . . . 2
+ 2. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 3
+ 3. LANGUAGE Extension . . . . . . . . . . . . . . . . . . . . . 3
+ 3.1 LANGUAGE Extension Requirements . . . . . . . . . . . . . . . 3
+ 3.2 LANGUAGE Command . . . . . . . . . . . . . . . . . . . . . . 4
+ 3.3 LANGUAGE Response . . . . . . . . . . . . . . . . . . . . . . 6
+ 3.4 TRANSLATION Extension to the NAMESPACE Response . . . . . . . 6
+ 3.5 Formal Syntax . . . . . . . . . . . . . . . . . . . . . . . . 6
+ 4. I18NLEVEL=1 and I18NLEVEL=2 Extensions . . . . . . . . . . . 7
+ 4.1 Introduction and Overview . . . . . . . . . . . . . . . . . . 8
+ 4.2 Requirements common to both I18NLEVEL=1 and I18NLEVEL=2 . . .
+ 4.3 I18NLEVEL=1 Extension Requirements . . . . . . . . . . . . . 8
+ 4.4 I18NLEVEL=2 Extension Requirements . . . . . . . . . . . . . 8
+ 4.5 Compatibility Notes
+ 4.6 Comparators and Charsets . . . . . . . . . . . . . . . . . . 9
+ 4.7 COMPARATOR Command . . . . . . . . . . . . . . . . . . . . . 9
+ 4.8 COMPARATOR Response . . . . . . . . . . . . . . . . . . . . . 10
+ 4.9 BADCOMPARATOR Response Code . . . . . . . . . . . . . . . . .
+ 4.10 Formal Syntax . . . . . . . . . . . . . . . . . . . . . . . 10
+ 5. Other IMAP Internationalization Issues . . . . . . . . . . . 11
+ 5.1 UTF-8 Userids and Passwords . . . . . . . . . . . . . . . . . 11
+ 5.2 UTF-8 Mailbox Names . . . . . . . . . . . . . . . . . . . . . 11
+ 5.3 UTF-8 Domains, Addresses and Mail Headers . . . . . . . . . . 11
+ 6. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 12
+ 7. Security Considerations . . . . . . . . . . . . . . . . . . . 12
+ 8. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . 12
+ 9. Relevant Standards for i18n IMAP Implementations . . . . . . 13
+ Normative References . . . . . . . . . . . . . . . . . . . . 13
+ Informative References . . . . . . . . . . . . . . . . . . . 14
+ Authors' Addresses . . . . . . . . . . . . . . . . . . . . . 15
+ Intellectual Property and Copyright Statements . . . . . . . 16
+
+
+Conventions Used in This Document
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in [RFC2119].
+
+ The formal syntax use the Augmented Backus-Naur Form (ABNF)
+ [RFC4234] notation including the core rules defined in Appendix A.
+
+
+
+Newman & Co Expires August 2008 FF[Page 2]
+
+
+
+
+
+Internet-draft February 2008
+
+
+ The UTF8-related productions are defined in [RFC3629].
+
+ In examples, "C:" and "S:" indicate lines sent by the client and
+ server respectively. If a single "C:" or "S:" label applies to
+ multiple lines, then the line breaks between those lines are for
+ editorial clarity only and are not part of the actual protocol
+ exchange.
+
+
+2. Introduction
+
+ This specification defines two IMAP4rev1 [RFC3501] extensions to
+ enhance international support. These extensions can be advertised
+ and implemented separately.
+
+ The LANGUAGE extension allows the client to request a suitable
+ language for protocol error messages and in combination with the
+ NAMESPACE extension [RFC2342] enables namespace translations.
+
+ The I18NLEVEL=2 extension allows the client to request a suitable
+ collation which will modify the behavior of the base specification's
+ SEARCH command as well as the SORT and THREAD extensions [SORT].
+ This leverages the collation registry [RFC4790].
+
+
+3. LANGUAGE Extension
+
+ IMAP allows server responses to include human-readable text that in
+ many cases needs to be presented to the user. But that text is
+ limited to US-ASCII by the IMAP specification [RFC3501] in order to
+ preserve backwards compatibility with deployed IMAP implementations.
+ This section specifies a way for an IMAP client to negotiate which
+ language the server should use when sending human-readable text.
+
+ The LANGUAGE extension only provides a mechanism for altering fixed
+ server strings such as response text and NAMESPACE folder names.
+ Assigning localized language aliases to shared mailboxes would be
+ done with a separate mechanism such as the proposed METADATA
+ extension (see [METADATA]).
+
+
+3.1 LANGUAGE Extension Requirements
+
+ IMAP servers that support this extension MUST list the keyword
+ LANGUAGE in their CAPABILITY response as well as in the greeting
+ CAPABILITY data.
+
+ A server that advertises this extension MUST use the language "i-
+
+
+
+Newman & Co Expires August 2008 FF[Page 3]
+
+
+
+
+
+Internet-draft February 2008
+
+
+ default" as described in [RFC2277] as its default language until
+ another supported language is negotiated by the client. A server
+ MUST include "i-default" as one of its supported languages.
+
+ Clients and servers that support this extension MUST also support
+ the NAMESPACE extension [RFC2342].
+
+ The LANGUAGE command is valid in all states. Clients are urged to
+ issue LANGUAGE before authentication, since some servers send
+ valuable user information as part of authentication (e.g. "password
+ is correct, but expired"). If a security layer (such as SASL or
+ TLS) is subsequently negotiated by the client, it MUST re-issue the
+ LANGUAGE command in order to make sure that no previous active
+ attack (if any) on LANGUAGE negotiation has effect on subsequent
+ error messages. (See Section 7 for a more detailed explanation of
+ the attack.)
+
+
+
+3.2 LANGUAGE Command
+
+ Arguments: Optional language range arguments.
+
+ Response: A possible LANGUAGE response (see section 3.3).
+ A possible NAMESPACE response (see section 3.4).
+
+ Result: OK - Command completed
+ NO - Could not complete command
+ BAD - arguments invalid
+
+ The LANGUAGE command requests that human-readable text emitted by
+ the server be localized to a language matching one of the language
+ range argument as described by section 2 of [RFC4647].
+
+ If the command succeeds, the server will return human-readable
+ responses in the first supported language specified. These
+ responses will be in UTF-8 [RFC3629]. The server MUST send a
+ LANGUAGE response specifying the language used, and the change takes
+ effect immediately after the LANGUAGE response.
+
+ If the command fails, the server continues to return human-readable
+ responses in the language it was previously using.
+
+ The special "default" language range argument indicates a request to
+ use a language designated as preferred by the server administrator.
+ The preferred language MAY vary based on the currently active user.
+
+ If a language range does not match a known language tag exactly but
+
+
+
+Newman & Co Expires August 2008 FF[Page 4]
+
+
+
+
+
+Internet-draft February 2008
+
+
+ does match a language by the rules of [RFC4647], the server MUST
+ send an untagged LANGUAGE response indicating the language selected.
+
+ If there aren't any arguments, the server SHOULD send an untagged
+ LANGUAGE response listing the languages it supports. If the server
+ is unable to enumerate the list of languages it supports it MAY
+ return a tagged NO response to the enumeration request.
+
+ < The server defaults to using English i-default responses until
+ the user explicitly changes the language. >
+
+ C: A001 LOGIN KAREN PASSWORD
+ S: A001 OK LOGIN completed
+
+ < Client requested MUL language, which no server supports. >
+
+ C: A002 LANGUAGE MUL
+ S: A002 NO Unsupported language MUL
+
+ < A LANGUAGE command with no arguments is a request to enumerate
+ the list of languages the server supports. >
+
+ C: A003 LANGUAGE
+ S: * LANGUAGE (EN DE IT i-default)
+ S: A003 OK Supported languages have been enumerated
+
+ C: B001 LANGUAGE
+ S: B001 NO Server is unable to enumerate supported languages
+
+ < Once the client changes the language, all responses will be in
+ that language starting after the LANGUAGE response. Note that
+ this includes the NAMESPACE response. Because RFCs are in US-
+ ASCII, this document uses an ASCII transcription rather than
+ UTF-8 text, e.g. ue in the word "ausgefuehrt" >
+
+ C: C001 LANGUAGE DE
+ S: * LANGUAGE (DE)
+ S: * NAMESPACE (("" "/")) (("Other Users/" "/" "TRANSLATION"
+ ("Andere Ben&APw-tzer/"))) (("Public Folders/" "/"
+ "TRANSLATION" ("Gemeinsame Postf&AM8-cher/")))
+ S: C001 OK Sprachwechsel durch LANGUAGE-Befehl ausgefuehrt
+
+ < If a server does not support the requested primary language,
+ responses will continue to be returned in the current language
+ the server is using. >
+
+ C: D001 LANGUAGE FR
+ S: D001 NO Diese Sprache ist nicht unterstuetzt
+
+
+
+Newman & Co Expires August 2008 FF[Page 5]
+
+
+
+
+
+Internet-draft February 2008
+
+
+ C: D002 LANGUAGE DE-IT
+ S: * LANGUAGE (DE-IT)
+ S: * NAMESPACE (("" "/"))(("Other Users/" "/" "TRANSLATION"
+ ("Andere Ben&APw-tzer/"))) (("Public Folders/" "/"
+ "TRANSLATION" ("Gemeinsame Postf&AM8-cher/")))
+ S: D002 OK Sprachwechsel durch LANGUAGE-Befehl ausgefuehrt
+ C: D003 LANGUAGE "default"
+ S: * LANGUAGE (DE)
+ S: D003 OK Sprachwechsel durch LANGUAGE-Befehl ausgefuehrt
+
+ < Server does not speak French, but does speak English. User
+ speaks Canadian French and Canadian English. >
+
+ C: E001 LANGUAGE FR-CA EN-CA
+ S: * LANGUAGE (EN)
+ S: E001 OK Now speaking English
+
+
+
+3.3 LANGUAGE Response
+
+ Contents: A list of one or more language tags.
+
+ The LANGUAGE response occurs as a result of a LANGUAGE command. A
+ LANGUAGE response with a list containing a single language tag
+ indicates that the server is now using that language. A LANGUAGE
+ response with a list containing multiple language tags indicates the
+ server is communicating a list of available languages to the client,
+ and no change in the active language has been made.
+
+
+3.4 TRANSLATION Extension to the NAMESPACE Response
+
+ If localized representations of the namespace prefixes are available
+ in the selected language, the server SHOULD include these in the
+ TRANSLATION extension to the NAMESPACE response.
+
+ The TRANSLATION extension to the NAMESPACE response returns a single
+ string, containing the modified UTF-7 [RFC3501] encoded translation
+ of the namespace prefix. It is the responsibility of the client to
+ convert between the namespace prefix and the translation of the
+ namespace prefix when presenting mailbox names to the user.
+
+ In this example a server supports the IMAP4 NAMESPACE command. It
+ uses no prefix to the user's Personal Namespace, a prefix of "Other
+ Users" to its Other Users' Namespace and a prefix of "Public
+ Folders" to its only Shared Namespace. Since a client will often
+ display these prefixes to the user, the server includes a
+
+
+
+Newman & Co Expires August 2008 FF[Page 6]
+
+
+
+
+
+Internet-draft February 2008
+
+
+ translation of them that can be presented to the user.
+
+ C: A001 LANGUAGE DE-IT
+ S: * NAMESPACE (("" "/")) (("Other Users/" "/" "TRANSLATION"
+ ("Andere Ben&APw-tzer/"))) (("Public Folders/" "/"
+ "TRANSLATION" ("Gemeinsame Postf&AM8-cher/")))
+ S: A001 OK LANGUAGE-Befehl ausgefuehrt
+
+
+3.5 Formal Syntax
+
+ The following syntax specification inherits ABNF [RFC4234] rules
+ from IMAP4rev1 [RFC3501], IMAP4 Namespace [RFC2342], Tags for the
+ Identifying Languages [RFC4646], UTF-8 [RFC3629] and Collected
+ Extensions to IMAP4 ABNF [RFC4466].
+
+ command-any =/ language-cmd
+ ; LANGUAGE command is valid in all states
+
+ language-cmd = "LANGUAGE" *(SP lang-range-quoted)
+
+ response-payload =/ language-data
+
+ language-data = "LANGUAGE" SP "(" lang-tag-quoted *(SP
+ lang-tag-quoted) ")"
+
+ namespace-trans = SP DQUOTE "TRANSLATION" DQUOTE SP "(" string ")"
+ ; the string is encoded in Modified UTF-7.
+ ; this is a subset of the syntax permitted by
+ ; the Namespace-Response-Extension rule in [RFC4466]
+
+ lang-range-quoted = astring
+ ; Once any literal wrapper or quoting is removed, this
+ ; follows the language-range rule in [RFC4647]
+
+ lang-tag-quoted = astring
+ ; Once any literal wrapper or quoting is removed, this follows
+ ; the Language-Tag rule in [RFC4646]
+
+ resp-text = ["[" resp-text-code "]" SP ] UTF8-TEXT-CHAR
+ *(UTF8-TEXT-CHAR / "[")
+ ; After the server is changed to a language other than
+ ; i-default, this resp-text rule replaces the resp-text
+ ; rule from [RFC3501].
+
+ UTF8-TEXT-CHAR = %x20-5A / %x5C-7E / UTF8-2 / UTF8-3 / UTF8-4
+ ; UTF-8 excluding 7-bit control characters and "["
+
+
+
+
+Newman & Co Expires August 2008 FF[Page 7]
+
+
+
+
+
+Internet-draft February 2008
+
+
+4. I18NLEVEL=1 and I18NLEVEL=2 Extensions
+
+
+4.1 Introduction and Overview
+
+ IMAP4rev1 [RFC3501] includes the SEARCH command which can be used to
+ locate messages matching criteria including human-readable text.
+ The SORT extension [SORT] to IMAP allows the client to ask the
+ server to determine the order of messages based on criteria
+ including human-readable text. These mechanisms require the ability
+ to support non-English search and sort functions.
+
+ Section 4 defines two IMAP extensions for internationalizing IMAP
+ SEARCH, SORT and THREAD [SORT] using the comparator framework
+ [RFC4790].
+
+ The I18NLEVEL=1 extension updates SEARCH/SORT/THREAD to use
+ i;unicode-casemap comparator, as defined in [UCM]. See Sections 4.2
+ and 4.3 for more details.
+
+ The I18NLEVEL=2 extension is a superset of the I18NLEVEL=1
+ extension. It adds to I18NLEVEL=1 extension the ability to determine
+ the active comparator (see definition below) and negotiate use of
+ comparators using the COMPARATOR command. It also adds the
+ COMPARATOR response that indicates the active comparator and
+ possibly other available comparators. See Sections 4.2 and 4.4 for
+ more details.
+
+
+4.2 Requirements common to both I18NLEVEL=1 and I18NLEVEL=2
+
+ The term "default comparator" refers to the comparator which is used
+ by SEARCH and SORT absent any negotiation using the COMPARATOR (see
+ Section 4.7) command. The term "active comparator" refers to the
+ comparator which will be used within a session e.g. by SEARCH and
+ SORT. The COMPARATOR command is used to change the active
+ comparator.
+
+ The active comparator applies to the following SEARCH keys: "BCC",
+ "BODY", "CC", "FROM", "SUBJECT", "TEXT", "TO" and "HEADER". If the
+ server also advertises the "SORT" extension, then the active
+ comparator applies to the following SORT keys: "CC", "FROM",
+ "SUBJECT" and "TO". If the server advertises THREAD=ORDEREDSUBJECT,
+ then the active comparator applies to the ORDEREDSUBJECT threading
+ algorithm. If the server advertises THREAD=REFERENCES, then the
+ active comparator applies to the subject field comparisons done by
+ REFERENCES threading algorithm. Future extensions may choose to
+ apply the active comparator to their SEARCH keys.
+
+
+
+Newman & Co Expires August 2008 FF[Page 8]
+
+
+
+
+
+Internet-draft February 2008
+
+
+ For SORT and THREAD, the pre-processing necessary to extract the
+ base subject text from a Subject header occurs prior to the
+ application of a comparator.
+
+ A server that advertises I18NLEVEL=1 or I18NLEVEL=2 extension MUST
+ implement the i;unicode-casemap comparator, as defined in [UCM].
+
+ A server that advertises I18NLEVEL=1 or I18NLEVEL=2 extension MUST
+ support UTF-8 as a SEARCH charset.
+
+
+4.3 I18NLEVEL=1 Extension Requirements
+
+ An IMAP server that satisfies all requirements specified in sections
+ 4.2 and 4.6 (and doesn't support/advertise any other I18NLEVEL=<n>
+ extension, where n > 1) MUST list the keyword I18NLEVEL=1 in its
+ CAPABILITY data once IMAP enters the authenticated state, and MAY
+ list that keyword in other states.
+
+
+
+4.4 I18NLEVEL=2 Extension Requirements
+
+ IMAP server that satisfies all requirements specified in sections
+ 4.2, 4.4, 4.6-4.10 (and doesn't support/advertise any other
+ I18NLEVEL=<n> extension, where n > 2) MUST list the keyword
+ I18NLEVEL=2 in its CAPABILITY data once IMAP enters the
+ authenticated state, and MAY list that keyword in other states.
+
+ A server that advertises this extension MUST implement the
+ i;unicode-casemap comparator, as defined in [UCM]. It MAY implement
+ other comparators from the IANA registry established by [RFC4790].
+ See also section 4.5 of this document.
+
+ A server that advertises this extension SHOULD use i;unicode-casemap
+ as the default comparator. (Note that i;unicode-casemap is the
+ default comparator for I18NLEVEL=1, but not necessarily the default
+ for I18NLEVEL=2.) The selection of the default comparator MAY be
+ adjustable by the server administrator, and MAY be sensitive to the
+ current user. Once the IMAP connection enters authenticated state,
+ the default comparator MUST remain static for the remainder of that
+ connection.
+
+ Note that since SEARCH uses the substring operation, IMAP servers
+ can only implement collations that offer the substring operation
+ (see [RFC4790 section 4.2.2). Since SORT uses ordering operation
+ (and by implication equality), IMAP servers which advertise the SORT
+ extension can only implement collations that offer all three
+
+
+
+Newman & Co Expires August 2008 FF[Page 9]
+
+
+
+
+
+Internet-draft February 2008
+
+
+ operations (see [RFC4790] sections 4.2.2-4).
+
+ If the active collation does not provide the operations needed by an
+ IMAP command, the server MUST respond with a tagged BAD.
+
+
+4.5 Compatibility Notes
+
+ Several server implementations deployed prior to the publication of
+ this specification comply with I18NLEVEL=1 (see section 4.3), but do
+ not advertise that. Other legacy servers use the i;ascii-casemap
+ (see [RFC4790]) comparator.
+
+ There is no good way for a client to know which comparator that a
+ legacy server uses. If the client has to assume the worst, it may
+ end up doing expensive local operations to obtain i;unicode-casemap
+ comparisons even though the server implements it.
+
+ Legacy server implementations which comply with I18NLEVEL=1 should
+ be updated to advertise I18NLEVEL=1. All server implementations
+ should eventually be updated to comply with the I18NLEVEL=2
+ extension.
+
+
+4.6 Comparators and Character Encodings
+
+ RFC 3501, section 6.4.4 says:
+
+ In all search keys that use strings, a message matches
+ the key if the string is a substring of the field. The
+ matching is case-insensitive.
+
+ When performing the SEARCH operation, the active comparator is
+ applied instead of the case-insensitive matching specified above.
+
+ An IMAP server which performs collation operations (e.g., as part of
+ commands such as SEARCH, SORT, THREAD) does so according to the
+ following procedure:
+
+ (a) MIME encoding (for example see [RFC2047] for headers and
+ [RFC2045] for body parts) MUST be removed in the texts being
+ collated.
+
+ If MIME encoding removal fails for a message (e.g., a body part
+ of the message has an unsupported Content-Transfer-Encoding,
+ uses characters not allowed by the Content-Transfer-Encoding,
+ etc.), the collation of this message is undefined by this
+ specification, and is handled in an implementation-dependent
+
+
+
+Newman & Co Expires August 2008 FF[Page 10]
+
+
+
+
+
+Internet-draft February 2008
+
+
+ manner.
+
+ (b) The decoded text from (a) MUST be converted to the charset
+ expected by the active comparator.
+
+ (c) For the substring operation:
+ If step (b) failed (e.g., the text is in an unknown charset,
+ contains a sequence which is not valid according in that
+ charset, etc.), the original decoded text from (a) (i.e.,
+ before the charset conversion attempt) is collated using the
+ i;octet comparator (see [RFC4790]).
+
+ If step (b) was successful, the converted text from (b) is
+ collated according to the active comparator.
+
+
+ For the ordering operation:
+
+ All strings that were successfully converted by step (b) are
+ separated from all strings that failed step (b). Strings in
+ each group are collated independently. All strings successfully
+ converted by step (b) are then validated by the active
+ comparator. Strings that pass validation are collated using the
+ active comparator. All strings that either fail step (b) or fail
+ the active collation's validity operation are collated (after
+ applying step (a)) using the i;octet comparator (see [RFC4790]).
+ The resulting sorted list is produced by appending all collated
+ "failed" strings after all strings collated using the active
+ comparator.
+
+
+ Example: The following example demonstrates ordering of 4
+ different strings using i;unicode-casemap [UCM] comparator.
+ Strings are represented using hexadecimal notation used by
+ ABNF [RFC4234].
+
+ (1) %xD0 %xC0 %xD0 %xBD %xD0 %xB4 %xD1 %x80 %xD0 %xB5
+ %xD0 %xB9 (labeled with charset=UTF-8)
+ (2) %xD1 %x81 %xD0 %x95 %xD0 %xA0 %xD0 %x93 %xD0 %x95
+ %xD0 %x99 (labeled with charset=UTF-8)
+ (3) %xD0 %x92 %xD0 %xB0 %xD1 %x81 %xD0 %xB8 %xD0 %xBB
+ %xD0 %xB8 %xFF %xB9 (labeled with charset=UTF-8)
+ (4) %xE1 %xCC %xC5 %xCB %xD3 %xC5 %xCA (labeled with
+ charset=KOI8-R)
+
+ Step (b) will convert string # 4 to the following
+ sequence of octets (in UTF-8):
+
+
+
+
+Newman & Co Expires August 2008 FF[Page 11]
+
+
+
+
+
+Internet-draft February 2008
+
+
+ %xD0 %x90 %xD0 %xBB %xD0 %xB5 %xD0 %xBA %xD1 %x81 %xD0
+ %xB5 %xD0 %xB9
+
+ and will reject strings (1) and (3), as they contain
+ octets not allowed in charset=UTF-8.
+ After that, using the i;unicode-casemap collation,
+ string (4) will collate before string (2). Using the
+ i;octet collation on the original strings, string (3)
+ will collate before string (1). So the final ordering
+ is as follows: (4) (2) (3) (1).
+
+ If the substring operation (e.g., IMAP SEARCH) of the active
+ comparator returns the "undefined" result (see section 4.2.3 of
+ [RFC4790]) for either the text specified in the SEARCH command or
+ the message text, then the operation is repeated on the result of
+ step (a) using the i;octet comparator.
+
+ The ordering operation (e.g., IMAP SORT and THREAD) SHOULD collate
+ the following together: strings encoded using unknown or invalid
+ character encodings, strings in unrecognized charsets, and invalid
+ input (as defined by the active collation).
+
+
+
+4.7 COMPARATOR Command
+
+ Arguments: Optional comparator order arguments.
+
+ Response: A possible COMPARATOR response (see Section 4.8).
+
+ Result: OK - Command completed
+ NO - No matching comparator found
+ BAD - arguments invalid
+
+ The COMPARATOR command is valid in authenticated and selected
+ states.
+
+ The COMPARATOR command is used to determine or change the active
+ comparator. When issued with no arguments, it results in a
+ COMPARATOR response indicating the currently active comparator.
+
+ When issued with one or more comparator argument, it changes the
+ active comparator as directed. (If more than one installed
+ comparator is matched by an argument, the first argument wins.) The
+ COMPARATOR response lists all matching comparators if more than one
+ matches the specified patterns.
+
+ The argument "default" refers to the server's default comparator.
+
+
+
+Newman & Co Expires August 2008 FF[Page 12]
+
+
+
+
+
+Internet-draft February 2008
+
+
+ Otherwise each argument is an collation specification as defined in
+ the Internet Application Protocol Comparator Registry [RFC4790].
+
+ < The client requests activating a Czech comparator if possible,
+ or else a generic international comparator which it considers
+ suitable for Czech. The server picks the first supported
+ comparator. >
+
+ C: A001 COMPARATOR "cz;*" i;basic
+ S: * COMPARATOR i;basic
+ S: A001 OK Will use i;basic for collation
+
+
+4.8 COMPARATOR Response
+
+ Contents: The active comparator.
+ An optional list of available matching comparators
+
+ The COMPARATOR response occurs as a result of a COMPARATOR command.
+ The first argument in the comparator response is the name of the
+ active comparator. The second argument is a list of comparators
+ which matched any of the arguments to the COMPARATOR command and is
+ present only if more than one match is found.
+
+
+4.9 BADCOMPARATOR response code
+
+ This response code SHOULD be returned as a result of server failing
+ an IMAP command (returning NO), when the server knows that none of
+ the specified comparators match the requested comparator(s).
+
+
+4.10 Formal Syntax
+
+ The following syntax specification inherits ABNF [RFC4234] rules
+ from IMAP4rev1 [RFC3501], and Internet Application Protocol
+ Comparator Registry [RFC4790].
+
+ command-auth =/ comparator-cmd
+
+ resp-text-code =/ "BADCOMPARATOR"
+
+ comparator-cmd = "COMPARATOR" *(SP comp-order-quoted)
+
+ response-payload =/ comparator-data
+
+ comparator-data = "COMPARATOR" SP comp-sel-quoted [SP "("
+ comp-id-quoted *(SP comp-id-quoted) ")"]
+
+
+
+Newman & Co Expires August 2008 FF[Page 13]
+
+
+
+
+
+Internet-draft February 2008
+
+
+ comp-id-quoted = astring
+ ; Once any literal wrapper or quoting is removed, this
+ ; follows the collation-id rule from [RFC4790]
+
+ comp-order-quoted = astring
+ ; Once any literal wrapper or quoting is removed, this
+ ; follows the collation-order rule from [RFC4790]
+
+ comp-sel-quoted = astring
+ ; Once any literal wrapper or quoting is removed, this
+ ; follows the collation-selected rule from [RFC4790]
+
+
+5. Other IMAP Internationalization Issues
+
+ The following sections provide an overview of various other IMAP
+ internationalization issues. These issues are not resolved by this
+ specification, but could be resolved by other standards work, such
+ as that being done by the EAI group (see [IMAP-EAI]).
+
+
+5.1 Unicode Userids and Passwords
+
+ IMAP4rev1 currently restricts the userid and password fields of the
+ LOGIN command to US-ASCII. The "userid" and "password" fields of the
+ IMAP LOGIN command are restricted to US-ASCII only until a future
+ standards track RFC states otherwise. Servers are encouraged to
+ validate both fields to make sure they conform to the formal syntax
+ of UTF-8 and to reject the LOGIN command if that syntax is violated.
+ Servers MAY reject the use of any 8-bit in the "userid" or
+ "password" field.
+
+ When AUTHENTICATE is used, some servers may support userids and
+ passwords in Unicode [RFC3490] since SASL (see [RFC4422]) allows
+ that. However, such userids cannot be used as part of email
+ addresses.
+
+
+5.2 UTF-8 Mailbox Names
+
+ The modified UTF-7 mailbox naming convention described in section
+ 5.1.3 of RFC 3501 is best viewed as an transition from the status
+ quo in 1996 when modified UTF-7 was first specified. At that time,
+ there was widespread unofficial use of local character sets such as
+ ISO-8859-1 and Shift-JIS for non-ASCII mailbox names, with resultant
+ non-interoperability.
+
+ The requirements in section 5.1 of RFC 3501 are very important if
+
+
+
+Newman & Co Expires August 2008 FF[Page 14]
+
+
+
+
+
+Internet-draft February 2008
+
+
+ we're ever going to be able to deploy UTF-8 mailbox names. Servers
+ are encouraged to enforce them.
+
+
+5.3 UTF-8 Domains, Addresses and Mail Headers
+
+ There is now an IETF standard for Internationalizing Domain Names in
+ Applications [RFC3490]. While IMAP clients are free to support this
+ standard, an argument can be made that it would be helpful to simple
+ clients if the IMAP server could perform this conversion (the same
+ argument would apply to MIME header encoding [RFC2047]). However,
+ it would be unwise to move forward with such work until the work in
+ progress to define the format of international email addresses is
+ complete.
+
+
+6. IANA Considerations
+
+ The IANA is requested to add LANGUAGE, I18NLEVEL=1 and I18NLEVEL=2
+ to the IMAP4 Capabilities Registry. [Note to IANA:
+ http://www.iana.org/assignments/imap4-capabilities]
+
+
+7. Security Considerations
+
+ The LANGUAGE extension makes a new command available in "Not
+ Authenticated" state in IMAP. Some IMAP implementations run with
+ root privilege when the server is in "Not Authenticated" state and
+ do not revoke that privilege until after authentication is complete.
+ Such implementations are particularly vulnerable to buffer overflow
+ security errors at this stage and need to implement parsing of this
+ command with extra care.
+
+ A LANGUAGE command issued prior to activation of a security layer is
+ subject to an active attack which suppresses or modifies the
+ negotiation and thus makes STARTTLS or authentication error messages
+ more difficult to interpret. This is not a new attack as the error
+ messages themselves are subject to active attack. Clients MUST re-
+ issue the LANGUAGE command once a security layer is active, so this
+ does not impact subsequent protocol operations.
+
+ LANGUAGE, I18NLEVEL=1 and I18NLEVEL=2 extensions use the UTF-8
+ charset, thus the security considerations for UTF-8 [RFC3629] are
+ relevent. However, neither uses UTF-8 for identifiers so the most
+ serious concerns do not apply.
+
+
+8. Acknowledgements
+
+
+
+Newman & Co Expires August 2008 FF[Page 15]
+
+
+
+
+
+Internet-draft February 2008
+
+
+ The LANGUAGE extension is based on a previous Internet draft by Mike
+ Gahrns, a substantial portion of the text in that section was
+ written by him. Many people have participated in discussions about
+ an IMAP Language extension in the various fora of the IETF and
+ Internet working groups, so any list of contributors is bound to be
+ incomplete. However, the authors would like to thank Andrew McCown
+ for early work on the original proposal, John Myers for suggestions
+ regarding the namespace issue, along with Jutta Degener, Mark
+ Crispin, Mark Pustilnik, Larry Osterman, Cyrus Daboo, Martin Duerst,
+ Timo Sirainen, Ben Campbell and Magnus Nystrom for their many
+ suggestions that have been incorporated into this document.
+
+ Initial discussion of the I18NLEVEL=2 extension involved input from
+ Mark Crispin and other participants of the IMAP Extensions WG.
+
+
+9. Relevant Standards for i18n IMAP Implementations
+
+ This is a non-normative list of standards to consider when
+ implementing i18n aware IMAP software.
+
+ o The LANGUAGE and I18NLEVEL=2 extensions to IMAP (this
+ specification).
+ o The 8-bit rules for mailbox naming in section 5.1 of RFC 3501.
+ o The Mailbox International Naming Convention in section 5.1.3 of
+ RFC 3501.
+ o MIME [RFC2045] for message bodies.
+ o MIME header encoding [RFC2047] for message headers.
+ o The IETF EAI working group.
+ o MIME Parameter Value and Encoded Word Extensions [RFC2231] for
+ filenames. Quality IMAP server implementations will
+ automatically combine multipart parameters when generating the
+ BODYSTRUCTURE. There is also some deployed non-standard use of
+ MIME header encoding inside double-quotes for filenames.
+ o IDNA [RFC3490] and punycode [RFC3492] for domain names
+ (currently only relevant to IMAP clients).
+ o The UTF-8 charset [RFC3629].
+ o The IETF policy on Character Sets and Languages [RFC2277].
+
+
+Normative References
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC2277] Alvestrand, "IETF Policy on Character Sets and
+ Languages", BCP 18, RFC 2277, January 1998.
+
+
+
+
+Newman & Co Expires August 2008 FF[Page 16]
+
+
+
+
+
+Internet-draft February 2008
+
+
+ [RFC2342] Gahrns, Newman, "IMAP4 Namespace", RFC 2342, May 1998.
+
+ [RFC3501] Crispin, "INTERNET MESSAGE ACCESS PROTOCOL - VERSION
+ 4rev1", RFC 3501, March 2003.
+
+ [RFC3629] Yergeau, "UTF-8, a transformation format of ISO 10646",
+ STD 63, RFC 3629, November 2003.
+
+ [RFC4234] Crocker, Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", RFC 4234, Brandenburg
+ Internetworking, Demon Internet Ltd, October 2005.
+
+ [RFC4422] Melnikov, Zeilenga, "Simple Authentication and Security
+ Layer (SASL)", RFC 4422, June 2006.
+
+ [RFC4466] Melnikov, Daboo, "Collected Extensions to IMAP4 ABNF",
+ RFC 4466, Isode Ltd., April 2006.
+
+ [RFC4646] Philips, Davis, "Tags for Identifying Languages", BCP 47,
+ RFC 4646, September 2006.
+
+ [RFC4647] Philips, Davis, "Matching of Language Tags", BCP 47, RFC
+ 4647, September 2006.
+
+ [RFC4790] Newman, Duerst, Gulbrandsen, "Internet Application
+ Protocol Comparator Registry", RFC 4790, February 2007.
+
+ [SORT] Crispin, M. and K. Murchison, "INTERNET MESSAGE ACCESS
+ PROTOCOL - SORT AND THREAD EXTENSION", draft-ietf-
+ imapext-sort-19 (work in progress), November 2006.
+
+ [UCM] Crispin, "i;unicode-casemap - Simple Unicode Collation
+ Algorithm", RFC 5051, October 2007.
+
+ [RFC2045] Freed, Borenstein, "Multipurpose Internet Mail Extensions
+ (MIME) Part One: Format of Internet Message Bodies", RFC
+ 2045, November 1996.
+
+ [RFC2047] Moore, "MIME (Multipurpose Internet Mail Extensions) Part
+ Three: Message Header Extensions for Non-ASCII Text", RFC
+ 2047, November 1996.
+
+
+Informative References
+
+
+ [RFC2231] Freed, Moore, "MIME Parameter Value and Encoded Word
+ Extensions: Character Sets, Languages, and
+
+
+
+Newman & Co Expires August 2008 FF[Page 17]
+
+
+
+
+
+Internet-draft February 2008
+
+
+ Continuations", RFC 2231, November 1997.
+
+ [RFC3490] Faltstrom, Hoffman, Costello, "Internationalizing Domain
+ Names in Applications (IDNA)", RFC 3490, March 2003.
+
+ [RFC3492] Costello, "Punycode: A Bootstring encoding of Unicode for
+ Internationalized Domain Names in Applications (IDNA)",
+ RFC 3492, March 2003.
+
+ [METADATA] Daboo, C., "IMAP METADATA Extension", draft-daboo-imap-
+ annotatemore-12 (work in progress), December 2007.
+
+ [IMAP-EAI] Resnick, Newman, "IMAP Support for UTF-8", draft-ietf-
+ eai-imap-utf8 (work in progress), May 2006.
+
+
+
+Authors' Addresses
+
+ Chris Newman
+ Sun Microsystems
+ 3401 Centrelake Dr., Suite 410
+ Ontario, CA 91761
+ US
+
+ Email: chris.newman@sun.com
+
+
+ Arnt Gulbrandsen
+ Oryx Mail Systems GmbH
+ Schweppermannstr. 8
+ D-81671 Muenchen
+ Germany
+
+ Email: arnt@oryx.com
+
+ Fax: +49 89 4502 9758
+
+
+ Alexey Melnikov
+ Isode Limited
+ 5 Castle Business Village, 36 Station Road,
+ Hampton, Middlesex, TW12 2BX, UK
+
+ Email: Alexey.Melnikov@isode.com
+
+
+
+
+
+
+Newman & Co Expires August 2008 FF[Page 18]
+
+
+
+
+
+Internet-draft February 2008
+
+
+Intellectual Property Statement
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be found
+ in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this specification
+ can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+
+Full Copyright Statement
+
+ Copyright (C) The IETF Trust (2008). This document is subject to
+ the rights, licenses and restrictions contained in BCP 78, and
+ except as set forth therein, the authors retain all their rights.
+
+ This document and the information contained herein are provided on
+ an "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE
+ REPRESENTS OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY, THE
+ IETF TRUST AND THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL
+ WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY
+ WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE
+ ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS
+ FOR A PARTICULAR PURPOSE.
+
+
+Acknowledgment
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+Newman & Co Expires August 2008 FF[Page 19]
+
+
diff --git a/imap/docs/draft/sort.txt b/imap/docs/draft/sort.txt
new file mode 100644
index 00000000..4453bb4d
--- /dev/null
+++ b/imap/docs/draft/sort.txt
@@ -0,0 +1,885 @@
+IMAP Extensions Working Group M. Crispin
+Internet-Draft K. Murchison
+Intended status: Proposed Standard March 10, 2008
+Expires: September 10, 2008
+Document: internet-drafts/draft-ietf-imapext-sort-20.txt
+
+ INTERNET MESSAGE ACCESS PROTOCOL - SORT AND THREAD EXTENSIONS
+
+Status of this Memo
+
+ By submitting this Internet-Draft, each author represents that
+ any applicable patent or other IPR claims of which he or she is
+ aware have been or will be disclosed, and any of which he or she
+ becomes aware will be disclosed, in accordance with Section 6 of
+ BCP 79.
+
+ Internet-Drafts are working documents of the Internet Engineering
+ Task Force (IETF), its areas, and its working groups. Note that
+ other groups may also distribute working documents as
+ Internet-Drafts.
+
+ Internet-Drafts are draft documents valid for a maximum of six months
+ and may be updated, replaced, or obsoleted by other documents at any
+ time. It is inappropriate to use Internet-Drafts as reference
+ material or to cite them other than as "work in progress."
+
+ The list of current Internet-Drafts can be accessed at
+ http://www.ietf.org/ietf/1id-abstracts.txt
+
+ The list of Internet-Draft Shadow Directories can be accessed at
+ http://www.ietf.org/shadow.html.
+
+ A revised version of this draft document will be submitted to the RFC
+ editor as a Proposed Standard for the Internet Community. Discussion
+ and suggestions for improvement are requested, and should be sent to
+ ietf-imapext@IMC.ORG.
+
+ Distribution of this memo is unlimited.
+
+Abstract
+
+ This document describes the base-level server-based sorting and
+ threading extensions to the [IMAP] protocol. These extensions
+ provide substantial performance improvements for IMAP clients which
+ offer sorted and threaded views.
+
+1. Introduction
+
+ The SORT and THREAD extensions to the [IMAP] protocol provide a means
+ of server-based sorting and threading of messages, without requiring
+ that the client download the necessary data to do so itself. This is
+ particularly useful for online clients as described in [IMAP-MODELS].
+
+ A server which supports the base-level SORT extension indicates this
+ with a capability name which starts with "SORT". Future,
+ upwards-compatible extensions to the SORT extension will all start
+ with "SORT", indicating support for this base level.
+
+ A server which supports the THREAD extension indicates this with one
+ or more capability names consisting of "THREAD=" followed by a
+ supported threading algorithm name as described in this document.
+ This provides for future upwards-compatible extensions.
+
+ A server which implements the SORT and/or THREAD extensions MUST
+ collate strings in accordance with the requirements of I18NLEVEL=1,
+ as described in [IMAP-I18N], and SHOULD implement and advertise the
+ I18NLEVEL=1 extension. Alternatively, a server MAY implement
+ I18NLEVEL=2 (or higher) and comply with the rules of that level.
+
+ Discussion: the SORT and THREAD extensions predate [IMAP-I18N] by
+ several years. At the time of this writing, all known server
+ implementations of SORT and THREAD comply with the rules of
+ I18NLEVEL=1, but do not necessarily advertise it. As discussed
+ in [IMAP-I18N] section 4.5, all server implementations should
+ eventually be updated to comply with the I18NLEVEL=2 extension.
+
+ Historical note: the REFERENCES threading algorithm is based on the
+ [THREADING] algorithm written used in "Netscape Mail and News"
+ versions 2.0 through 3.0.
+
+2. Terminology
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in [KEYWORDS].
+
+ The word "can" (not "may") is used to refer to a possible
+ circumstance or situation, as opposed to an optional facility of the
+ protocol.
+
+ "User" is used to refer to a human user, whereas "client" refers to
+ the software being run by the user.
+
+ In examples, "C:" and "S:" indicate lines sent by the client and
+ server respectively.
+
+2.1 Base Subject
+
+ Subject sorting and threading use the "base subject," which has
+ specific subject artifacts removed. Due to the complexity of these
+ artifacts, the formal syntax for the subject extraction rules is
+ ambiguous. The following procedure is followed to determine the
+ "base subject", using the [ABNF] formal syntax rules described in
+ section 5:
+
+ (1) Convert any RFC 2047 encoded-words in the subject to
+ UTF-8 as described in "internationalization
+ considerations." Convert all tabs and continuations to
+ space. Convert all multiple spaces to a single space.
+
+ (2) Remove all trailing text of the subject that matches
+ the subj-trailer ABNF, repeat until no more matches are
+ possible.
+
+ (3) Remove all prefix text of the subject that matches the
+ subj-leader ABNF.
+
+ (4) If there is prefix text of the subject that matches the
+ subj-blob ABNF, and removing that prefix leaves a non-empty
+ subj-base, then remove the prefix text.
+
+ (5) Repeat (3) and (4) until no matches remain.
+
+ Note: it is possible to defer step (2) until step (6), but this
+ requires checking for subj-trailer in step (4).
+
+ (6) If the resulting text begins with the subj-fwd-hdr ABNF
+ and ends with the subj-fwd-trl ABNF, remove the
+ subj-fwd-hdr and subj-fwd-trl and repeat from step (2).
+
+ (7) The resulting text is the "base subject" used in the
+ SORT.
+
+ All servers and disconnected (as described in [IMAP-MODELS]) clients
+ MUST use exactly this algorithm to determine the "base subject".
+ Otherwise there is potential for a user to get inconsistent results
+ based on whether they are running in connected or disconnected mode.
+
+2.2 Sent Date
+
+ As used in this document, the term "sent date" refers to the date and
+ time from the Date: header, adjusted by time zone to normalize to
+ UTC. For example, "31 Dec 2000 16:01:33 -0800" is equivalent to the
+ UTC date and time of "1 Jan 2001 00:01:33 +0000".
+
+ If the time zone is invalid, the date and time SHOULD be treated as
+ UTC. If the time is also invalid, the time SHOULD be treated as
+ 00:00:00. If there is no valid date or time, the date and time
+ SHOULD be treated as 00:00:00 on the earliest possible date.
+
+ This differs from the date-related criteria in the SEARCH command
+ (described in [IMAP] section 6.4.4), which use just the date and not
+ the time, and are not adjusted by time zone.
+
+ If the sent date can not be determined (a Date: header is missing or
+ can not be parsed), the INTERNALDATE for that message is used as the
+ sent date.
+
+ When comparing two sent dates that match exactly, the order in which
+ the two messages appear in the mailbox (that is, by sequence number)
+ is used as a tie-breaker to determine the order.
+
+3. Additional Commands
+
+ These commands are extension to the [IMAP] base protocol.
+
+ The section headings are intended to correspond with where they would
+ be located in the main document if they were part of the base
+ specification.
+
+BASE.6.4.SORT. SORT Command
+
+ Arguments: sort program
+ charset specification
+ searching criteria (one or more)
+
+ Data: untagged responses: SORT
+
+ Result: OK - sort completed
+ NO - sort error: can't sort that charset or
+ criteria
+ BAD - command unknown or arguments invalid
+
+ The SORT command is a variant of SEARCH with sorting semantics for
+ the results. Sort has two arguments before the searching criteria
+ argument; a parenthesized list of sort criteria, and the searching
+ charset.
+
+ The charset argument is mandatory (unlike SEARCH) and indicates
+ the [CHARSET] of the strings that appear in the searching
+ criteria. The US-ASCII and UTF-8 charsets MUST be implemented.
+ All other charsets are optional.
+
+ There is also a UID SORT command which returns unique identifiers
+ instead of message sequence numbers. Note that there are separate
+ searching criteria for message sequence numbers and UIDs; thus the
+ arguments to UID SORT are interpreted the same as in SORT. This
+ is analogous to the behavior of UID SEARCH, as opposed to UID
+ COPY, UID FETCH, or UID STORE.
+
+ The SORT command first searches the mailbox for messages that
+ match the given searching criteria using the charset argument for
+ the interpretation of strings in the searching criteria. It then
+ returns the matching messages in an untagged SORT response, sorted
+ according to one or more sort criteria.
+
+ Sorting is in ascending order. Earlier dates sort before later
+ dates; smaller sizes sort before larger sizes; and strings are
+ sorted according to ascending values established by their
+ collation algorithm (see under "Internationalization
+ Considerations").
+
+ If two or more messages exactly match according to the sorting
+ criteria, these messages are sorted according to the order in
+ which they appear in the mailbox. In other words, there is an
+ implicit sort criterion of "sequence number".
+
+ When multiple sort criteria are specified, the result is sorted in
+ the priority order that the criteria appear. For example,
+ (SUBJECT DATE) will sort messages in order by their base subject
+ text; and for messages with the same base subject text will sort
+ by their sent date.
+
+ Untagged EXPUNGE responses are not permitted while the server is
+ responding to a SORT command, but are permitted during a UID SORT
+ command.
+
+ The defined sort criteria are as follows. Refer to the Formal
+ Syntax section for the precise syntactic definitions of the
+ arguments. If the associated RFC-822 header for a particular
+ criterion is absent, it is treated as the empty string. The empty
+ string always collates before non-empty strings.
+
+ ARRIVAL
+ Internal date and time of the message. This differs from the
+ ON criteria in SEARCH, which uses just the internal date.
+
+ CC
+ [IMAP] addr-mailbox of the first "cc" address.
+
+ DATE
+ Sent date and time, as described in section 2.2.
+
+ FROM
+ [IMAP] addr-mailbox of the first "From" address.
+
+ REVERSE
+ Followed by another sort criterion, has the effect of that
+ criterion but in reverse (descending) order.
+ Note: REVERSE only reverses a single criterion, and does not
+ affect the implicit "sequence number" sort criterion if all
+ other criteria are identicial. Consequently, a sort of
+ REVERSE SUBJECT is not the same as a reverse ordering of a
+ SUBJECT sort. This can be avoided by use of additional
+ criteria, e.g. SUBJECT DATE vs. REVERSE SUBJECT REVERSE
+ DATE. In general, however, it's better (and faster, if the
+ client has a "reverse current ordering" command) to reverse
+ the results in the client instead of issuing a new SORT.
+
+ SIZE
+ Size of the message in octets.
+
+ SUBJECT
+ Base subject text.
+
+ TO
+ [IMAP] addr-mailbox of the first "To" address.
+
+ Example: C: A282 SORT (SUBJECT) UTF-8 SINCE 1-Feb-1994
+ S: * SORT 2 84 882
+ S: A282 OK SORT completed
+ C: A283 SORT (SUBJECT REVERSE DATE) UTF-8 ALL
+ S: * SORT 5 3 4 1 2
+ S: A283 OK SORT completed
+ C: A284 SORT (SUBJECT) US-ASCII TEXT "not in mailbox"
+ S: * SORT
+ S: A284 OK SORT completed
+
+BASE.6.4.THREAD. THREAD Command
+
+Arguments: threading algorithm
+ charset specification
+ searching criteria (one or more)
+
+Data: untagged responses: THREAD
+
+Result: OK - thread completed
+ NO - thread error: can't thread that charset or
+ criteria
+ BAD - command unknown or arguments invalid
+
+ The THREAD command is a variant of SEARCH with threading semantics
+ for the results. Thread has two arguments before the searching
+ criteria argument; a threading algorithm, and the searching
+ charset.
+
+ The charset argument is mandatory (unlike SEARCH) and indicates
+ the [CHARSET] of the strings that appear in the searching
+ criteria. The US-ASCII and UTF-8 charsets MUST be implemented.
+ All other charsets are optional.
+
+ There is also a UID THREAD command which returns unique
+ identifiers instead of message sequence numbers. Note that there
+ are separate searching criteria for message sequence numbers and
+ UIDs; thus the arguments to UID THREAD are interpreted the same as
+ in THREAD. This is analogous to the behavior of UID SEARCH, as
+ opposed to UID COPY, UID FETCH, or UID STORE.
+
+ The THREAD command first searches the mailbox for messages that
+ match the given searching criteria using the charset argument for
+ the interpretation of strings in the searching criteria. It then
+ returns the matching messages in an untagged THREAD response,
+ threaded according to the specified threading algorithm.
+
+ All collation is in ascending order. Earlier dates collate before
+ later dates and strings are collated according to ascending values
+ established by their collation algorithm (see under
+ "Internationalization Considerations").
+
+ Untagged EXPUNGE responses are not permitted while the server is
+ responding to a THREAD command, but are permitted during a UID
+ THREAD command.
+
+ The defined threading algorithms are as follows:
+
+ ORDEREDSUBJECT
+
+ The ORDEREDSUBJECT threading algorithm is also referred to as
+ "poor man's threading." The searched messages are sorted by
+ base subject and then by the sent date. The messages are then
+ split into separate threads, with each thread containing
+ messages with the same base subject text. Finally, the threads
+ are sorted by the sent date of the first message in the thread.
+
+ The first message of each thread are siblings of each other
+ (the "root"). The second message of a thread is the child of
+ the first message, and subsequent messages of the thread are
+ siblings of the second message and hence children of the
+ message at the root. Hence, there are no grandchildren in
+ ORDEREDSUBJECT threading.
+
+ Children in ORDEREDSUBJECT threading do not have descendents.
+ Client implementations SHOULD treat descendents of a child in
+ a server response as being siblings of that child.
+
+ REFERENCES
+
+ The REFERENCES threading algorithm threads the searched
+ messages by grouping them together in parent/child
+ relationships based on which messages are replies to others.
+ The parent/child relationships are built using two methods:
+ reconstructing a message's ancestry using the references
+ contained within it; and checking the original (not base)
+ subject of a message to see if it is a reply to (or forward of)
+ another message.
+
+ Note: "Message ID" in the following description refers to a
+ normalized form of the msg-id in [RFC-2822]. The actual
+ text in an RFC 2822 may use quoting, resulting in multiple
+ ways of expressing the same Message ID. Implementations of
+ the REFERENCES threading algorithm MUST normalize any msg-id
+ in order to avoid false non-matches due to differences in
+ quoting.
+
+ For example, the msg-id
+ <"01KF8JCEOCBS0045PS"@xxx.yyy.com>
+ and the msg-id
+ <01KF8JCEOCBS0045PS@xxx.yyy.com>
+ MUST be interpreted as being the same Message ID.
+
+ The references used for reconstructing a message's ancestry are
+ found using the following rules:
+
+ If a message contains a References header line, then use the
+ Message IDs in the References header line as the references.
+
+ If a message does not contain a References header line, or
+ the References header line does not contain any valid
+ Message IDs, then use the first (if any) valid Message ID
+ found in the In-Reply-To header line as the only reference
+ (parent) for this message.
+
+ Note: Although [RFC-2822] permits multiple Message IDs in
+ the In-Reply-To header, in actual practice this
+ discipline has not been followed. For example,
+ In-Reply-To headers have been observed with message
+ addresses after the Message ID, and there are no good
+ heuristics for software to determine the difference.
+ This is not a problem with the References header however.
+
+ If a message does not contain an In-Reply-To header line, or
+ the In-Reply-To header line does not contain a valid Message
+ ID, then the message does not have any references (NIL).
+
+ A message is considered to be a reply or forward if the base
+ subject extraction rules, applied to the original subject,
+ remove any of the following: a subj-refwd, a "(fwd)"
+ subj-trailer, or a subj-fwd-hdr and subj-fwd-trl.
+
+ The REFERENCES algorithm is significantly more complex than
+ ORDEREDSUBJECT and consists of six main steps. These steps are
+ outlined in detail below.
+
+ (1) For each searched message:
+
+ (A) Using the Message IDs in the message's references, link
+ the corresponding messages (those whose Message-ID header
+ line contains the given reference Message ID) together as
+ parent/child. Make the first reference the parent of the
+ second (and the second a child of the first), the second the
+ parent of the third (and the third a child of the second),
+ etc. The following rules govern the creation of these
+ links:
+
+ If a message does not contain a Message-ID header line,
+ or the Message-ID header line does not contain a valid
+ Message ID, then assign a unique Message ID to this
+ message.
+
+ If two or more messages have the same Message ID, then
+ only use that Message ID in the first (lowest sequence
+ number) message, and assign a unique Message ID to each
+ of the subsequent messages with a duplicate of that
+ Message ID.
+
+ If no message can be found with a given Message ID,
+ create a dummy message with this ID. Use this dummy
+ message for all subsequent references to this ID.
+
+ If a message already has a parent, don't change the
+ existing link. This is done because the References
+ header line may have been truncated by a MUA. As a
+ result, there is no guarantee that the messages
+ corresponding to adjacent Message IDs in the References
+ header line are parent and child.
+
+ Do not create a parent/child link if creating that link
+ would introduce a loop. For example, before making
+ message A the parent of B, make sure that A is not a
+ descendent of B.
+
+ Note: Message ID comparisons are case-sensitive.
+
+ (B) Create a parent/child link between the last reference
+ (or NIL if there are no references) and the current message.
+ If the current message already has a parent, it is probably
+ the result of a truncated References header line, so break
+ the current parent/child link before creating the new
+ correct one. As in step 1.A, do not create the parent/child
+ link if creating that link would introduce a loop. Note
+ that if this message has no references, that it will now
+ have no parent.
+
+ Note: The parent/child links created in steps 1.A and 1.B
+ MUST be kept consistent with one another at ALL times.
+
+ (2) Gather together all of the messages that have no parents
+ and make them all children (siblings of one another) of a dummy
+ parent (the "root"). These messages constitute the first
+ (head) message of the threads created thus far.
+
+ (3) Prune dummy messages from the thread tree. Traverse each
+ thread under the root, and for each message:
+
+ If it is a dummy message with NO children, delete it.
+
+ If it is a dummy message with children, delete it, but
+ promote its children to the current level. In other words,
+ splice them in with the dummy's siblings.
+
+ Do not promote the children if doing so would make them
+ children of the root, unless there is only one child.
+
+ (4) Sort the messages under the root (top-level siblings only)
+ by sent date as described in section 2.2. In the case of a
+ dummy message, sort its children by sent date and then use the
+ first child for the top-level sort.
+
+ (5) Gather together messages under the root that have the same
+ base subject text.
+
+ (A) Create a table for associating base subjects with
+ messages, called the subject table.
+
+ (B) Populate the subject table with one message per each
+ base subject. For each child of the root:
+
+ (i) Find the subject of this thread, by using the base
+ subject from either the current message or its first
+ child if the current message is a dummy. This is the
+ thread subject.
+
+ (ii) If the thread subject is empty, skip this message.
+
+ (iii) Look up the message associated with the thread
+ subject in the subject table.
+
+ (iv) If there is no message in the subject table with the
+ thread subject, add the current message and the thread
+ subject to the subject table.
+
+ Otherwise, if the message in the subject table is not a
+ dummy, AND either of the following criteria are true:
+
+ The current message is a dummy, OR
+
+ The message in the subject table is a reply or forward
+ and the current message is not.
+
+ then replace the message in the subject table with the
+ current message.
+
+ (C) Merge threads with the same thread subject. For each
+ child of the root:
+
+ (i) Find the message's thread subject as in step 5.B.i
+ above.
+
+ (ii) If the thread subject is empty, skip this message.
+
+ (iii) Lookup the message associated with this thread
+ subject in the subject table.
+
+ (iv) If the message in the subject table is the current
+ message, skip this message.
+
+ Otherwise, merge the current message with the one in the
+ subject table using the following rules:
+
+ If both messages are dummies, append the current
+ message's children to the children of the message in
+ the subject table (the children of both messages
+ become siblings), and then delete the current message.
+
+ If the message in the subject table is a dummy and the
+ current message is not, make the current message a
+ child of the message in the subject table (a sibling
+ of its children).
+
+ If the current message is a reply or forward and the
+ message in the subject table is not, make the current
+ message a child of the message in the subject table (a
+ sibling of its children).
+
+ Otherwise, create a new dummy message and make both
+ the current message and the message in the subject
+ table children of the dummy. Then replace the message
+ in the subject table with the dummy message.
+
+ Note: Subject comparisons are case-insensitive, as
+ described under "Internationalization
+ Considerations."
+
+ (6) Traverse the messages under the root and sort each set of
+ siblings by sent date as described in section 2.2. Traverse
+ the messages in such a way that the "youngest" set of siblings
+ are sorted first, and the "oldest" set of siblings are sorted
+ last (grandchildren are sorted before children, etc). In the
+ case of a dummy message (which can only occur with top-level
+ siblings), use its first child for sorting.
+
+ Example: C: A283 THREAD ORDEREDSUBJECT UTF-8 SINCE 5-MAR-2000
+ S: * THREAD (166)(167)(168)(169)(172)(170)(171)
+ (173)(174 (175)(176)(178)(181)(180))(179)(177
+ (183)(182)(188)(184)(185)(186)(187)(189))(190)
+ (191)(192)(193)(194 195)(196 (197)(198))(199)
+ (200 202)(201)(203)(204)(205)(206 207)(208)
+ S: A283 OK THREAD completed
+ C: A284 THREAD ORDEREDSUBJECT US-ASCII TEXT "gewp"
+ S: * THREAD
+ S: A284 OK THREAD completed
+ C: A285 THREAD REFERENCES UTF-8 SINCE 5-MAR-2000
+ S: * THREAD (166)(167)(168)(169)(172)((170)(179))
+ (171)(173)((174)(175)(176)(178)(181)(180))
+ ((177)(183)(182)(188 (184)(189))(185 186)(187))
+ (190)(191)(192)(193)((194)(195 196))(197 198)
+ (199)(200 202)(201)(203)(204)(205 206 207)(208)
+ S: A285 OK THREAD completed
+
+ Note: The line breaks in the first and third server
+ responses are for editorial clarity and do not appear in
+ real THREAD responses.
+
+4. Additional Responses
+
+ These responses are extensions to the [IMAP] base protocol.
+
+ The section headings of these responses are intended to correspond
+ with where they would be located in the main document.
+
+BASE.7.2.SORT. SORT Response
+
+ Data: zero or more numbers
+
+ The SORT response occurs as a result of a SORT or UID SORT
+ command. The number(s) refer to those messages that match the
+ search criteria. For SORT, these are message sequence numbers;
+ for UID SORT, these are unique identifiers. Each number is
+ delimited by a space.
+
+ Example: S: * SORT 2 3 6
+
+BASE.7.2.THREAD. THREAD Response
+
+ Data: zero or more threads
+
+ The THREAD response occurs as a result of a THREAD or UID THREAD
+ command. It contains zero or more threads. A thread consists of
+ a parenthesized list of thread members.
+
+ Thread members consist of zero or more message numbers, delimited
+ by spaces, indicating successive parent and child. This continues
+ until the thread splits into multiple sub-threads, at which point
+ the thread nests into multiple sub-threads with the first member
+ of each subthread being siblings at this level. There is no limit
+ to the nesting of threads.
+
+ The messages numbers refer to those messages that match the search
+ criteria. For THREAD, these are message sequence numbers; for UID
+ THREAD, these are unique identifiers.
+
+ Example: S: * THREAD (2)(3 6 (4 23)(44 7 96))
+
+ The first thread consists only of message 2. The second thread
+ consists of the messages 3 (parent) and 6 (child), after which it
+ splits into two subthreads; the first of which contains messages 4
+ (child of 6, sibling of 44) and 23 (child of 4), and the second of
+ which contains messages 44 (child of 6, sibling of 4), 7 (child of
+ 44), and 96 (child of 7). Since some later messages are parents
+ of earlier messages, the messages were probably moved from some
+ other mailbox at different times.
+
+ -- 2
+
+ -- 3
+ \-- 6
+ |-- 4
+ | \-- 23
+ |
+ \-- 44
+ \-- 7
+ \-- 96
+
+ Example: S: * THREAD ((3)(5))
+
+ In this example, 3 and 5 are siblings of a parent which does not
+ match the search criteria (and/or does not exist in the mailbox);
+ however they are members of the same thread.
+
+5. Formal Syntax of SORT and THREAD Commands and Responses
+
+ The following syntax specification uses the Augmented Backus-Naur
+ Form (ABNF) notation as specified in [ABNF]. It also uses [ABNF]
+ rules defined in [IMAP].
+
+sort = ["UID" SP] "SORT" SP sort-criteria SP search-criteria
+
+sort-criteria = "(" sort-criterion *(SP sort-criterion) ")"
+
+sort-criterion = ["REVERSE" SP] sort-key
+
+sort-key = "ARRIVAL" / "CC" / "DATE" / "FROM" / "SIZE" /
+ "SUBJECT" / "TO"
+
+thread = ["UID" SP] "THREAD" SP thread-alg SP search-criteria
+
+thread-alg = "ORDEREDSUBJECT" / "REFERENCES" / thread-alg-ext
+
+thread-alg-ext = atom
+ ; New algorithms MUST be registered with IANA
+
+search-criteria = charset 1*(SP search-key)
+
+charset = atom / quoted
+ ; CHARSET values MUST be registered with IANA
+
+sort-data = "SORT" *(SP nz-number)
+
+thread-data = "THREAD" [SP 1*thread-list]
+
+thread-list = "(" (thread-members / thread-nested) ")"
+
+thread-members = nz-number *(SP nz-number) [SP thread-nested]
+
+thread-nested = 2*thread-list
+
+ The following syntax describes base subject extraction rules (2)-(6):
+
+subject = *subj-leader [subj-middle] *subj-trailer
+
+subj-refwd = ("re" / ("fw" ["d"])) *WSP [subj-blob] ":"
+
+subj-blob = "[" *BLOBCHAR "]" *WSP
+
+subj-fwd = subj-fwd-hdr subject subj-fwd-trl
+
+subj-fwd-hdr = "[fwd:"
+
+subj-fwd-trl = "]"
+
+subj-leader = (*subj-blob subj-refwd) / WSP
+
+subj-middle = *subj-blob (subj-base / subj-fwd)
+ ; last subj-blob is subj-base if subj-base would
+ ; otherwise be empty
+
+subj-trailer = "(fwd)" / WSP
+
+subj-base = NONWSP *(*WSP NONWSP)
+ ; can be a subj-blob
+
+BLOBCHAR = %x01-5a / %x5c / %x5e-ff
+ ; any CHAR8 except '[' and ']'
+
+NONWSP = %x01-08 / %x0a-1f / %x21-ff
+ ; any CHAR8 other than WSP
+
+6. Security Considerations
+
+ The SORT and THREAD extensions do not raise any security
+ considerations that are not present in the base [IMAP] protocol, and
+ these issues are discussed in [IMAP]. Nevertheless, it is important
+ to remember that [IMAP] protocol transactions, including message
+ data, are sent in the clear over the network unless protection from
+ snooping is negotiated, either by the use of STARTTLS, privacy
+ protection is negotiated in the AUTHENTICATE command, or some other
+ protection mechanism.
+
+ Although not a security consideration, it is important to recognize
+ that sorting by REFERENCES can lead to misleading threading trees.
+ For example, a message with false References: header data will cause
+ a thread to be incorporated into another thread.
+
+ The process of extracting the base subject may lead to incorrect
+ collation if the extracted data was significant text as opposed to
+ a subject artifact.
+
+7. Internationalization Considerations
+
+ As stated in the introduction, the rules of I18NLEVEL=1 as described
+ in [IMAP-I18N] MUST be followed; that is, the SORT and THREAD
+ extensions MUST collate strings according to the i;unicode-casemap
+ collation described in [UNICASEMAP]. Servers SHOULD also advertise
+ the I18NLEVEL=1 extension. Alternatively, a server MAY implement
+ I18NLEVEL=2 (or higher) and comply with the rules of that level.
+
+ As discussed in [IMAP-I18N] section 4.5, all server implementations
+ should eventually be updated to support the [IMAP-I18N] I18NLEVEL=2
+ extension.
+
+ Translations of the "re" or "fw"/"fwd" tokens are not specified for
+ removal in the base subject extraction process. An attempt to add
+ such translated tokens would result in a geometrically complex, and
+ ultimately unimplementable, task.
+
+ Instead, note that [RFC-2822] section 3.6.5 recommends that "re:"
+ (from the Latin "res", in the matter of) be used to identify a reply.
+ Although it is evident that, from the multiple forms of token to
+ identify a forwarded message, there is considerable variation found
+ in the wild, the variations are (still) manageable. Consequently, it
+ is suggested that "re:" and one of the variations of the tokens for
+ forward supported by the base subject extraction rules be adopted for
+ Internet mail messages, since doing so makes it a simple display time
+ task to localize the token language for the user.
+
+8. IANA Considerations
+
+ [IMAP] capabilities are registered by publishing a standards track or
+ IESG approved experimental RFC. This document constitutes
+ registration of the SORT and THREAD capabilities in the [IMAP]
+ capabilities registry.
+
+ This document creates a new [IMAP] threading algorithms registry,
+ which registers threading algorithms by publishing a standards track
+ or IESG approved experimental RFC. This document constitutes
+ registration of the ORDEREDSUBJECT and REFERENCES algorithms in that
+ registry.
+
+9. Normative References
+
+ The following documents are normative to this document:
+
+ [ABNF] Crocker, D. and Overell, P. "Augmented BNF
+ for Syntax Specifications: ABNF", RFC 5234
+ January 2008
+
+ [CHARSET] Freed, N. and Postel, J. "IANA Character Set
+ Registration Procedures", RFC 2978, October
+ 2000.
+
+ [IMAP] Crispin, M. "Internet Message Access Protocol -
+ Version 4rev1", RFC 3501, March 2003.
+
+ [IMAP-I18N] Newman, C. and Gulbrandsen, A. "Internet
+ Message Access Protocol Internationalization",
+ Work in Progress.
+
+ [KEYWORDS] Bradner, S. "Key words for use in RFCs to
+ Indicate Requirement Levels", BCP 14, RFC 2119,
+ March 1997.
+
+ [RFC-2822] Resnick, P. "Internet Message Format", RFC
+ 2822, April 2001.
+
+ [UNICASEMAP] Crispin, M. "i;unicode-casemap - Simple Unicode
+ Collation Algorithm", RFC 5051.
+
+10. Informative References
+
+ The following documents are informative to this document:
+
+ [IMAP-MODELS] Crispin, M. "Distributed Electronic Mail Models
+ in IMAP4", RFC 1733, December 1994.
+
+ [THREADING] Zawinski, J. "Message Threading",
+ http://www.jwz.org/doc/threading.html,
+ 1997-2002.
+
+Appendices
+
+Author's Address
+
+ Mark R. Crispin
+ Networks and Distributed Computing
+ University of Washington
+ 4545 15th Avenue NE
+ Seattle, WA 98105-4527
+
+ Phone: +1 (206) 543-5762
+
+ EMail: MRC@CAC.Washington.EDU
+
+ Kenneth Murchison
+ Carnegie Mellon University
+ 5000 Forbes Avenue
+ Cyert Hall 285
+ Pittsburgh, PA 15213
+
+ Phone: +1 (412) 268-2638
+ Email: murch@andrew.cmu.edu
+
+Full Copyright Statement
+
+ Copyright (C) The IETF Trust (2008).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY, THE IETF TRUST AND
+ THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF
+ THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at ietf-
+ ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
diff --git a/imap/docs/drivers.txt b/imap/docs/drivers.txt
new file mode 100644
index 00000000..de91aa53
--- /dev/null
+++ b/imap/docs/drivers.txt
@@ -0,0 +1,189 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+ c-client Driver Characteristics
+ Mark Crispin
+ 11 December 2006
+
+
+ Drivers are code modules that support different mailbox storage
+technologies. A mailbox storage technology may be implemented by
+ 1) files and directories on the local system
+ 2) a database
+ 3) a network protocol.
+
+ In the case of files and directories on the local system, a
+driver supports a particular mailbox format. Mailbox formats are
+discussed in more detail in the file formats.txt.
+
+ As of the date this document was written, there was no bundled
+support for any databases in c-client. However, it should not be
+particularly difficult to write a driver that communicates with a
+database.
+
+ Network protocols supported by c-client drivers are the Internet
+Mail Access Protocol (all versions: IMAP4rev1, IMAP4, IMAP2bis, and
+IMAP2); the Post Office Protocol (version 3); and the Network News
+Transport Protocol (NNTP). In addition, c-client also supports NNTP
+and the Simple Mail Transport Protocol (SMTP) for mailbox transport.
+
+ By default, all drivers are enabled. There is little benefit to
+be gained by disabling a driver, with one exception. The mbox driver
+implements the behavior of automatically moving new mail from the
+spool directory to the "mbox" file on the user's home directory, if
+and *only* if the "mbox" exists and is in mailbox format. The mbox
+driver is listed under EXTRADRIVERS; if you wish to disable it just
+remove it from that list and rebuild.
+
+I. Special name "INBOX"
+
+The following rules to select INBOX and its format apply in
+the order given if "black box mode" is not in effect:
+ 1) mbox format is selected if file ~/mbox exists, and is in unix
+ format or is zero-length.
+ 2) mx format is selected if file ~/INBOX/.mxindex exists.
+ 3) mbx format is selected if file ~/INBOX exists and is in mbx format.
+ 4) tenex format is selected if:
+ a) file ~/mail.txt exists, and is in tenex format or is zero-length.
+ b) file ~/INBOX exists and is in tenex format.
+ 5) mtx format is selected if:
+ a) file ~/INBOX.MTX exists, and is in mtx format or is zero-length.
+ b) file ~/INBOX exists and is in mtx format.
+ 6) mmdf format is selected if the spool directory file exists and is
+ in mmdf format.
+ 7) unix format is selected if the spool directory file exists and is
+ in unix format.
+ 8) the dummy driver is selected if the spool directory file does not
+ exist, or exists and is empty.
+
+If "black box mode" is not in effect, messages are automatically
+transferred ("snarfed") from the spool directory to an INBOX in mbox,
+mx, mbx, tenex, and mtx formats.
+
+The following rules to select INBOX and its format apply in the order
+given if "black box mode" is in effect:
+ 1) mx format is selected if file ~/INBOX/.mxindex exists.
+ 2) mbx format is selected if file ~/INBOX exists and is in mbx format.
+ 3) tenex format is selected if file ~/INBOX exists and is in tenex format.
+ 4) mtx format is selected if file ~/INBOX exists and is in mtx format.
+ 5) mmdf format is selected if file ~/INBOX exists and is in mmdf format.
+ 6) unix format is selected if file ~/INBOX exists and is in unix format.
+ 7) the dummy driver is selected if ~/INBOX does not exist, or exists
+ and is empty.
+
+II. Special Name #mhinbox
+
+#mhinbox always refers to the directory "inbox" in the MH path, which
+is declared in the ~/.mh_profile file. Messages are automatically
+transferred from the spool directory to #mhinbox mailbox.
+
+
+III. Special Prefix "#mh/"
+
+Any name prefixed with "#mh/" always refers to a directory in the MH
+path, which is declared in the ~/.mh_profile file. For example, the name
+"#mh/foo" refers to directory "foo" in the MH path.
+
+
+IV. Special prefix "#news."
+
+Any name prefixed with "#news" always refers to a newsgroup. For
+example, the name "#news.comp.mail.misc" refers to newsgroup
+"comp.mail.misc".
+
+
+V. All Other Names
+
+The driver is selected by generating a file name from the mailbox
+name, and then examining the data of the object with the resulting
+name. The formats are checked in order: mx, mbx, tenex, mtx, mmdf,
+unix, and phile. The dummy driver is selected if the file is empty.
+
+The file name is generated according to certain rules, based upon the
+prefix of the mailbox name. On UNIX, the following rules apply:
+
+Prefix Interpretation of Suffix
+------ ------------------------
+/ [black box] preceeds a user name; "/foo/bar" means
+ "black box user foo's mailbox bar"
+ [not black box] preceeds an absolute path name.
+~ [not black box] preceeds a user name; "~foo/bar" means
+ "UNIX user foo's mailbox bar"
+#ftp/ preceeds UNIX user ftp's mailbox name
+#public/ preceeds UNIX user imappublic's mailbox name
+#shared/ preceeds UNIX user imapshared's mailbox name
+
+All other names are interpreted in the context of the UNIX user's home
+directory (not black box), the black box user's black box directory
+(black box), or UNIX user ftp's home directory (anonymous).
+
+The strings "..", "//", and /~ are forbidden in names in:
+ black box mode
+ #ftp, #public, or #shared names
+ anonymous users
+
+Anonymous users may only access:
+ INBOX (belonging to UNIX user ftp)
+ files in or below UNIX user ftp's home directory
+ #ftp, #news, and #public namespace
+
+VI. Driver Comparison
+
+The following information about the local file drivers is an
+elaboration of a table compiled by Osma Ahvenlampi.
+
+Driver CA CE UID Kwd Sub NFS Performance Layout
+------ -- -- --- --- --- --- ----------- ------
+unix no no yes yes no limited fair file
+ ;;; traditional UNIX format
+mbox no no yes yes no limited fair file
+ ;;; traditional UNIX format, INBOX only, using ~/mbox with automatic
+ ;;; moving from the mail spool directory.
+mmdf no no yes yes no limited fair file
+ ;;; default on SCO systems
+mbx yes yes yes yes no no very good prefile
+ ;;; best performing local file driver; preferred format at UW
+tenex yes no no limited no no good prefile
+ ;;; compatible with UNIX MM
+mtx yes no no limited no no very good prefile
+ ;;; PC Pine standard format; compatible with TOPS-20; identical to tenex
+ ;;; but instead CRLF newlines instead of LF
+mx yes buggy yes yes yes no poor ixdir
+ ;;; fullest function; *not* recommended due to performance problems and bugs;
+ ;;; to be redesigned/rewritten
+mh yes no no no yes yes very poor dir
+ ;;; compatible with mh; #mhinbox for INBOX, #mh/ prefix for all other names
+news yes no yes no yes yes very poor ixdir
+ ;;; local news spool access; #news. prefix for all names
+phile no no no no no yes good file
+ ;;; reads arbitrary file as a single readonly message
+
+IMPORTANT: the "performance" ratings are relative to other drivers,
+and not necessarily to other software which implements those formats.
+They relate to the driver's performance in typical operations such as
+an IMAP "FETCH ALL".
+
+Key to headings:
+ CA: concurrent read/write access
+ CE: expunge permitted in concurrent read/write access
+ UID: sticky UIDs
+ Kwd: keyword flags
+ Sub: subfolders
+ NFS: usable over network filesystems (NFS, AFS, etc.)
+ Layout: file - single file
+ prefile - file with preallocated space for state
+ dir - directory, messages are files
+ ixdir - directory, messages are files, with helper index
+
+In addition, drivers imap, nntp, and pop3 support IMAP4rev1, NNTP, and
+POP3 protocols respectively.
diff --git a/imap/docs/formats.txt b/imap/docs/formats.txt
new file mode 100644
index 00000000..8dfb9dae
--- /dev/null
+++ b/imap/docs/formats.txt
@@ -0,0 +1,217 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+ Mailbox Format Characteristics
+ Mark Crispin
+ 11 December 2006
+
+
+ When a mailbox storage technology uses local files and
+directories directly, the file(s) and directories are layed out in a
+mailbox format.
+
+I. Flat-File Formats
+
+ In these formats, a mailbox and all the messages inside are a
+single file on the filesystem. The mailbox name is the name of the
+file in the filesystem, relative to the user's "mail home directory."
+
+ A flat-file format mailbox is always a file, never a directory.
+This means that it is impossible to have a flat-file format mailbox
+that has inferior mailbox names under it (so-called "dual-usage"
+mailboxes). For some inexplicable reason, some people want this.
+
+ The mail home directory is usually the same as the user login
+home directory if that concept is meaningful; otherwise, it is some
+other default directory (e.g. "C:\My Documents" on Windows 98). This
+can be redefined by modifying the c-client source code or in an
+application via the SET_HOMEDIR mail_parameters() call.
+
+ For example, a mailbox named "project" is likely to be found in
+the file "project" in the user's home directory. Similarly, a mailbox
+named "test/trial1" (assuming a UNIX system) is likely to be found in
+the file "trial1" in the subdirectory "test" in the user's home
+directory.
+
+ Note that the name "INBOX" has special semantics and rules, as
+described in the file naming.txt.
+
+ The following flat-file formats are supported by c-client as of
+the time of this writing:
+
+. unix This is the traditional UNIX mailbox format, in use for nearly
+ 30 years. It uses a line starting with "From " to indicate
+ start of message, and stores the message status inside the
+ RFC822 message header.
+
+ unix is not particularly efficient; the entire mailbox file
+ must be read when the mailbox is open, and when reading message
+ texts it is necessary to convert the newline convention to
+ Internet standard CR LF form. unix preserves UIDs, and allows
+ the creation of keywords.
+
+ Only one process may have a unix-format mailbox open
+ read/write at a time.
+
+. mmdf This is the format used by the MMDF mailer. It uses a line
+ consisting of 4 <CTRL/A> (0x01) characters to indicate start
+ and end of message. Optionally, there may also be a unix
+ format "From " line. It otherwise has the same
+ characteristics as unix format.
+
+. mbx This is the current preferred mailbox format. It can be
+ handled quite efficiently by c-client, without the problems
+ that exist with unix and mmdf formats. Messages are stored
+ in Internet standard CR LF format.
+
+ mbx permits shared access, including shared expunge. It
+ preserves UIDs, and allows the creation of keywords.
+
+. mtx This is supported for compatibility with the past. This is
+ the old Tenex/TOPS-20 mail.txt format. It can be handled
+ quite efficiently by c-client, and has most of the
+ characteristics of mbx format.
+
+ mtx is deficient in that it does not support shared expunge;
+ it has no means to store UIDs; and it has no way to define
+ keywords except through an external configuration file.
+
+. tenex This is supported for compatibility with the past. This is
+ the old Columbia MM format. This is similar to mtx format,
+ only it uses UNIX-style bare-LF newlines instead of CR LF
+ newlines, thus incurring a performance penalty for newline
+ conversion.
+
+. phile This is not strictly a format. Any file which is not in a
+ recognized format is in phile format, which treats the entire
+ contents of the file as a single message.
+
+
+II. File/Message Formats
+
+ In these formats, a mailbox is a directory, and each the messages
+inside are separate files inside the directory. The file names of
+these files are generally the text form of a number, which also
+matches the UID of the message.
+
+ In the case of mx, the mailbox name is the name of the directory
+in the filesystem, relative to the user's "mail home directory." In
+the case of news and mh, the mailbox name is in a separate namespace
+as described in the file naming.txt.
+
+ A file/message format mailbox is always a directory. This means
+that it is possible to have a file/message format mailbox that has
+inferior mailbox names under it (so-called "dual-usage" mailboxes).
+For some inexplicable reason, some people want this.
+
+ Note that the name "INBOX" has special semantics and rules, as
+described in the file naming.txt.
+
+ The following file/message formats are supported by c-client as of
+the time of this writing:
+
+. mx This is an experimental format, and may be removed in a future
+ release. An mx format mailbox has a .mxindex file which holds
+ the message status and unique identifiers. Messages are
+ stored in Internet standard CF LF form, so the file size of
+ the message file equals the size of the message.
+
+ mx is somewhat inefficient; the entire directory must be read
+ and each file stat()'d. We found it intolerable for a
+ moderate sized mailbox (2000 messages) and have more or less
+ abandoned it.
+
+. mh This is supported for compatibility with the past. This is
+ the format used by the old mh program.
+
+ mh is very inefficient; the entire directory must be read
+ and each file stat()'d, and in order to determine the size
+ of a message, the entire file must be read and newline
+ conversion performed.
+
+ mh is deficient in that it does not support any permanent
+ flags or keywords; and has no means to store UIDs (because
+ the mh "compress" command renames all the files, that's
+ why).
+
+. news This is an export of the local filesystem's news spool, e.g.
+ /var/spool/news. Access to mailboxes in news format is read
+ only; however, message "deleted" status is preserved in a
+ .newsrc file in the user's home directory. There is no other
+ status or keywords.
+
+ news is very inefficient; the entire directory must be
+ read and each file stat()'d, and in order to determine the
+ size of a message, the entire file must be read and newline
+ conversion performed.
+
+ news is deficient in that it does not support permanent flags
+ other than deleted; does not support keywords; and has no
+ expunge.
+
+
+Soapbox on File/Message Formats
+
+ If it sounds from the above descriptions that we're not putting
+too much effort into file/message formats, you are correct.
+
+ There's a general reason why file/message formats are a bad idea.
+Just about every filesystem in existance serializes file creation and
+deletions because these manipulate the free space map. This turns out
+to be an enormous problem when you start creating/deleting more than a
+few messages per second; you spend all your time thrashing in the
+filesystem.
+
+ It is also extremely slow to do a text search through a
+file/message format mailbox. All of those open()s and close()s really
+add up to major filesystem thrashing.
+
+
+What about Cyrus and Maildir?
+
+ Both formats are vulnerable to the filesystem thrashing outlined
+above.
+
+ The Cyrus format used by CMU's Cyrus server (and Esys' server)
+has a special associated flat file in each directory that contains
+extensive data (including pre-parsed ENVELOPEs and BODYSTRUCTUREs)
+about the messages. Put another way, it's a (considerably) more
+featureful form of mx. It also uses certain operating system
+facilities (e.g. file/memory mapping) which are not available on older
+systems, at a cost of much more limited portability than c-client.
+These considerably ameliorate the fundamental problems with
+file/message formats; in fact, Cyrus is halfway to being a database.
+Rather than support Cyrus format in c-client, you should run Cyrus or
+Esys if you want that format.
+
+ The Maildir format used by qmail has all of the performance
+disadvantages of mh noted above, with the additional problem that the
+files are renamed in order to change their status so you end up having
+to rescan the directory frequently to locate the current names
+(particularly in a shared mailbox scenario). It doesn't scale, and it
+represents a support nightmare; it is therefore not supported in the
+official distribution. Maildir support code for c-client is available
+from third parties; but, if you use it, it is entirely at your own
+risk (read: don't complain about how poorly it performs or bugs).
+
+
+So what does this all mean?
+
+ A database (such as used by Exchange) is really a much better
+approach if you want to move away from flat files. mx and especially
+Cyrus take a tenative step in that direction; mx failed mostly because
+it didn't go anywhere near far enough. Cyrus goes much further, and
+scores remarkable benefits from doing so.
+
+ However, a well-designed pure database without the overhead of
+separate files would do even better.
diff --git a/imap/docs/imaprc.txt b/imap/docs/imaprc.txt
new file mode 100644
index 00000000..cda153a6
--- /dev/null
+++ b/imap/docs/imaprc.txt
@@ -0,0 +1,613 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+ .imaprc secrets revealed!
+ Mark Crispin, June 17, 2002
+
+The following information describes the format of the /etc/c-client.cf
+and ~/.imaprc file. The Columbia MM ~/.mminit file is also read by
+c-client; however, the only command that ~/.mminit has in common is
+set keywords.
+
+**********************************************************************
+* DANGER! BEWARE! TAKE CARE! *
+**********************************************************************
+* *
+* These files, and this documentation, are for internal UW usage *
+* only. This capability is for UW experimental tinkering, and most *
+* emphatically *not* for sorcerer's apprentices at other sites who *
+* feel that if a config file capability exists, they must write a *
+* config file whether or not there is any need for one. *
+* *
+* This information is subject to change without notice. Commands *
+* may be added, removed, or altered. The behavior of comamnds may *
+* change. Do not use any of this information without consulting me *
+* first. c-client's defaults have been carefully chosen to be right *
+* for general-purpose and most special-purpose configurations. If *
+* you tinker with these defaults, all hell may break loose. *
+* *
+* This is not an idle threat. There have been several instances of *
+* people who ignored these warnings and have gotten burned. *
+* *
+* Don't even trust this file to work. Many of the things which can *
+* be changed by this file can also be changed by the application, *
+* and it is totally unpredictable which will take precedence. It *
+* all depends upon how the application is coded. Not only that, you *
+* may cause the application to crash. *
+* *
+* In other words, keep your cotton-pickin' hands off my defaults. *
+* If it crashes and erases your mail, I don't want to hear about it. *
+* Consider 'em ``mandatory defaults''. Got a nice ring, eh? :-) If *
+* you must tinker with defaults, play with the .pinerc and pine.conf *
+* files in Pine. It's got options galore, all supported for you to *
+* have fun. They're also documented; so well documented, it takes *
+* two strong men to carry around all the documentation. ;-) ;-) *
+* *
+* Joking aside, you really shouldn't be fooling around with this *
+* capability. It's dangerous, and you can shoot yourself in the *
+* foot easily. If you need custom changes, you are better off with *
+* local source code modifications. Seriously. *
+* *
+* One last warning: don't believe anything that you read in this *
+* document. Every effort has been made to ensure that this document *
+* is incomplete and inaccurate, and I take no responsibility for any *
+* glimmers of correct information that may, by some fluke, be here. *
+* *
+**********************************************************************
+
+The files are read in order: /etc/c-client.cf, ~/.mminit, ~/.imaprc,
+and an entry in a later file overrides the setting of an earlier file
+except as noted below. This ordering and overriding behavior may
+change without notice.
+
+Almost all of these facilities can also be set via the mail_parameters()
+call in the program. Whether the file overrides mail_parameters(), or
+mail_parameters() overrides the file, is indeterminate. It will vary
+from program to program, and it may be one way in one version and the
+other way in the next version. It's completely unpredictable, and so
+anything you do with these files has to be in complete knowledge of what
+the version of each program you're running is going to do. This is
+because the files do something for testing, but the real capability for
+configurability is put in the program instead. Are you getting the
+feeling that you shouldn't be messing with these files yet?
+
+The very first line of the file MUST start with the exact string "I
+accept the risk". This ensures that you have checked the file for
+correctness against this version of the IMAP toolkit. This enable
+string may change without notice in future versions, and the new
+string may or may not be accurately described in an updated version of
+this file. So any time you install software that uses the IMAP
+toolkit, you need to check the new version against these files (if you
+have insisted upon creating them in spite of all warnings). If two
+pieces of software use different versions of the IMAP toolkit with
+incompatible requirements, one of them won't work. Re-read the
+warning above about why you should not use these files.
+
+Subsequent lines are read from the file one at a time. Case does not
+matter. Unrecognized commands are ignored.
+
+1) set new-folder-format
+ sets what format new mailboxes are created in. This also controls
+ default delivery via tmail and dmail.
+
+ a) set new-folder-format same-as-inbox
+ Folder is created using the same mailbox format as INBOX. If
+ INBOX is empty, it defaults to system standard.
+
+ b) set new-folder-format system-standard
+ This is the default. Folder is created using the wired-in system
+ standard format, which on most UNIX systems is ordinary UNIX
+ /bin/mail format. On SCO systems, this is MMDF.
+
+ c) set new-folder-format <driver name>
+ Folder is created using the given driver name, e.g. mbx, unix,
+ mmdf, etc.
+
+ There is no protection against setting this to a silly value (e.g.
+ news, nntp, dummy) and doing so is a great way to screw things up.
+ Setting this to mh does not do what you think it does. Setting this
+ to tenex or mtx isn't particularly useful.
+
+2) set empty-folder-format
+ sets what format data is written into an empty mailbox file using
+ mail_copy() or mail_append(). This also controls default delivery
+ via tmail.
+
+ a) set empty-folder-format same-as-inbox
+ Data is written using the same mailbox format as INBOX. If
+ INBOX is empty, it defaults to system standard.
+
+ b) set empty-folder-format system-standard
+ This is the default. Data is written using the wired-in system
+ standard format, which on most UNIX systems is ordinary UNIX
+ /bin/mail format. On SCO systems, this is MMDF.
+
+ c) set-empty-folder-format <driver name>
+ Data is written using the given driver name, e.g. tenex, unix,
+ mmdf, etc.
+
+ There is no protection against setting this to a silly value (e.g.
+ news, nntp, dummy) and doing so is a great way to screw things up.
+ Setting this to mh, mbx, or mx does not work.
+
+3) set keywords <word1>, <word2>, ... <wordn>
+ Sets the list of keyword flags (supported by tenex and mtx) to the
+ given list. Up to 30 flags may be given. Since these names
+ correspond to numeric bits, the order of the keywords can not be
+ changed, nor can keywords be removed or inserted (you can append
+ new keywords, up to the limit of 30).
+
+ Set keywords is a deprecated command. It may not appear in
+ future versions, or it may appear in a changed form. It exists
+ only for compatibility with MM, and should only appear in ~/.mminit
+ and not in the other files. It is likely to disappear entirely in
+ IMAP4.
+
+ There is no protection against setting these to silly values, and
+ doing so is a great way to cause a crash.
+
+4) set from-widget header-only
+ Sets smart insertion of the > character in front of lines that
+ begin with ``From ''. Only such lines that are also in UNIX mbox
+ header file format will have a > character inserted. The default
+ is to insert the > character in front of all lines which begin with
+ ``From '', for the benefit of legacy tools that get confused
+ otherwise.
+
+5) set black-box-directory <directory name>
+ Sets the directory in which the user's data can be found. A user's
+ folders can be found in a subdirectory of the black box directory
+ named with the user's username. For example, if the blackbox
+ directory is /usr/spool/folders/, user jones' data can be found
+ in /usr/spool/folders/jones/. The user's black-box directory is
+ the location of folders, .mminit, .imaprc, .newsrc, and all other
+ files used by c-client; internally, it sets c-client's idea of the
+ user's ``home directory'', overriding /etc/passwd.
+
+ This command may not appear in ~/.mminit or ~/.imaprc
+
+ In black-box mode, it is not permitted to access any folders
+ outside of the user's personal blackbox directory. The breakouts
+ ``/'', ``~'', and ``..'' are not permitted.
+
+ In order to make this work without crashing, you must set another
+ option which is not listed in this document.
+
+ There is no protection against setting this to a silly value, and
+ doing so is a great way to cause a crash.
+
+6) set local-host <host name>
+ Sets c-client's idea of the local host name.
+
+ There is no protection against setting this to a silly value, and
+ doing so is a great way to cause a crash.
+
+7) set news-active-file <file name>
+ Sets the location of the news active file, if it is not in the
+ standard place.
+
+ It is recommended to use a courtesy symbolic link instead.
+
+ There is no protection against setting this to a silly value, and
+ doing so is a great way to cause a crash.
+
+8) set news-spool-directory <directory name>
+ Sets the location of the news spool, if it is not in the standard
+ place.
+
+ It is recommended to use a courtesy symbolic link instead.
+
+ There is no protection against setting this to a silly value, and
+ doing so is a great way to cause a crash.
+
+9) set news-state-file <file name>
+ Sets the location of the news state file (normally $(USER)/.newsrc).
+
+ This is not very useful in /etc/c-client.cf because it is a file name.
+ Setting this in /etc/c-client.cf would set all users to the same file
+ as their newsrc, which is probably not what you want.
+
+ There is no protection against setting this to a silly value, and
+ doing so is a great way to cause a crash.
+
+10) set system-inbox <file name>
+ Sets the location of the "system inbox", if it is not in the standard
+ place. This is the default location of INBOX, or the mail drop point
+ from which mail is snarfed (e.g. in tenex, mtx, mbox, mh formats).
+
+ This is not very useful in /etc/c-client.cf because it is a file name.
+ Setting this in /etc/c-client.cf would set all users to the same file
+ as their system inbox, which is probably not what you want.
+
+ There is no protection against setting this to a silly value, and
+ doing so is a great way to cause a crash.
+
+11) set tcp-open-timeout <number>
+ Sets the number of seconds that the TCP routines will block on opening
+ a TCP connection before timing out. If a timeout occurs, the connection
+ attempt is aborted.
+
+ The default is zero, meaning use the operating system default (75
+ seconds on most UNIX systems).
+
+ There is no protection against setting this to an excessively small
+ value, such as 1, and doing so is a great way to cause users extreme
+ grief.
+
+12) set tcp-read-timeout <number>
+ Sets the number of seconds that the TCP routines will block on reading
+ data before calling the timeout routine. If no timeout routine is set
+ by the program, the connection will be aborted on a timeout.
+
+ The default is zero, meaning infinite.
+
+ There is no protection against setting this to an excessively small
+ value, such as 1, and doing so is a great way to cause users extreme
+ grief.
+
+13) set tcp-write-timeout <number>
+ Sets the number of seconds that the TCP routines will block on sending
+ data before calling the timeout routine. If no timeout routine is set
+ by the program, the connection will be aborted on a timeout.
+
+ The default is zero, meaning infinite.
+
+ There is no protection against setting this to an excessively small
+ value, such as 1, and doing so is a great way to cause users extreme
+ grief.
+
+14) set rsh-timeout <number>
+ Sets the number of seconds that the rsh routines will block on opening
+ an rimapd connection before timing out. If a timeout occurs, the
+ rsh connection attempt is aborted. A zero timeout will disable rsh.
+
+ The default is 15 seconds.
+
+ There is no protection against setting this to an excessively small
+ value, such as 1, and doing so is a great way to cause users extreme
+ grief.
+
+15) set maximum-login-trials <number>
+ Sets the number of iterations of asking the user, via mm_login(), for
+ a user name and password, before cancelling the attempt.
+
+ The default is 3.
+
+ There is no protection against setting this to zero, and doing so is
+ a great way to cause users extreme grief.
+
+16) set lookahead <number>
+ Sets the number of envelopes that are looked ahead in IMAP, in
+ mail_fetchstructure(). This is based on the guess that in such
+ operations as drawing browser lines, if you get data for message n
+ you are likely to want it for message n+1, n+2,... in short order.
+ Lookahead preloads the c-client cache and saves unnecessary RTTs.
+
+ The default is 20, a good number for a browser on a 24x80 screen, and
+ small enough to usually have no significant real-time difference from
+ a single message fetch.
+
+ Setting it to 0 turns off lookahead.
+
+ There is no protection against setting this ridiculously high and
+ incurring performance penalties as a result.
+
+17) set prefetch <number>
+ Sets the number of envelops which are automatically fetched for the
+ messages which match in a search. This is based on the guess that
+ in a browser that is "zoomed" on the results of a search, you are
+ likely to want the envelope data for each of those messages in
+ short order. Prefetching reloads the c-client cache, saves
+ unnecessary RTTs, and avoids loading undesired envelopes due to
+ lookahead (see above).
+
+ The default is 20.
+
+ Setting it to 0 turns off prefetch.
+
+ There is no protection against setting this ridiculously high and
+ incurring performance penalties as a result.
+
+18) set close-on-error <number>
+ If non-zero, IMAP connections are closed if an EXAMINE or SELECT
+ command fails. Otherwise, they are left half-open, and can be used
+ again to select some other mailbox. The mailbox name in the stream
+ is set to {serverhost}<no_mailbox>
+
+ The default is zero (do not close on error).
+
+19) set imap-port <number>
+ Set the TCP/IP contact port to use for IMAP. This overrides the
+ wired-in setting and the setting from /etc/services, and can in
+ turn be overridden by an explicit user specification in the mailbox
+ name, e.g. {serverhost:143}foo
+
+ The default is zero (use setting from /etc/services or the wired-in
+ setting (143).
+
+ There is no protection against setting this to a silly value, and
+ doing so is a great way to cause users extreme grief.
+
+20) set pop3-port <number>
+ Set the TCP/IP contact port to use for POP3. This overrides the
+ wired-in setting and the setting from /etc/services, and can in
+ turn be overridden by an explicit user specification in the mailbox
+ name, e.g. {serverhost:110/pop3}
+
+ The default is zero (use setting from /etc/services or the wired-in
+ setting (110).
+
+ There is no protection against setting this to a silly value, and
+ doing so is a great way to cause users extreme grief.
+
+21) set uid-lookahead <number>
+ Sets the number of UIDs that are looked ahead in IMAP in mail_uid().
+ Lookahead preloads the c-client cache and saves unnecessary RTTs.
+
+ The default is 1000, small enough to usually have no significant
+ real-time difference from a single message UID fetch.
+
+ Setting it to 0 turns off lookahead.
+
+ There is no protection against setting this ridiculously high and
+ incurring performance penalties as a result.
+
+22) set mailbox-protection <number>
+ Set the default protection for newly-created mailbox files.
+
+ The default is 384.
+
+ There is no protection against setting this to a silly value, and
+ doing so is a great way to screw things up massively.
+
+23) set directory-protection <number>
+ Set the default protection for newly-created directories.
+
+ The default is 448.
+
+ There is no protection against setting this to a silly value, and
+ doing so is a great way to screw things up massively.
+
+24) set lock-protection <number>
+ Set the default protection for lock files
+
+ The default is 438, which is necessary if locks are to be respected
+ by processes running as other UIDs.
+
+ There is no protection against setting this to a silly value, and
+ contrary to what you may think just about any value other than 438
+ turns out to be a silly value.
+
+25) set disable-fcntl-locking <number>
+ This only applies to SVR4 systems.
+
+ If non-zero, fnctl() locking is not attempted. In the past, this
+ was used to avoid locking NFS files. If NFS is involved, the evil
+ lockd/statd daemons get invoked. These daemons supposedly work over
+ NFS, but really don't.
+
+ You probably don't really want to do this, though, because now the
+ flock() emulator (which calls fcntl()) now checks to see if the file
+ is accessed via NFS and no-ops the lock. This is compatible with
+ BSD.
+
+ Disabling fcntl() locking loses a great deal of locking protection
+ on local files as well as NFS files (which now never have locking
+ protection).
+
+ The default is zero (fcntl() locking is enabled).
+
+26) set lock-EACCES-error <number>
+ If non-zero, a warning message is given if an attempt to create a
+ lock file fails. Otherwise, EACCES is treated as a "silent failure",
+ and it proceeds without trying to use the lock file. This is for
+ the benefit of users on systems with paranoid /usr/spool/mail
+ protections which don't let users create /usr/spool/mail/$(USER).lock
+ files; these unfortunate users would be harassed with a flood of
+ error messages otherwise. The problem is that on SVR4, if EACCES
+ remains disabled and fcntl() locking is also disabled, then there is
+ no locking at all which is doubleplus-ungood.
+
+ If the site is paranoid on /usr/spool/mail protections AND if there
+ is no fcntl() locking (SVR4) or usable flock() locking (e.g. NFS),
+ then there is no way to win. Find a different system to use.
+
+ The default is non-zero (report EACCESS as an error).
+
+27) set list-maximum-level <number>
+ Sets the maximum depth of recursion that a * wildcard list will go
+ down the directory tree. 0 means that no recursion is permitted,
+ and * becomes like %.
+
+ The default is 20.
+
+ There is no protection against setting this to a ridiculously high
+ value. Since LIST will follow symbolic links, it can effectively
+ recurse infinitely, until the name strings get large enough that
+ some name limit is exceeded.
+
+28) set anonymous-home-directory <directory name>
+ Sets the location of the anonymous home directory, if it is not in
+ the standard place.
+
+ It is recommended to use a courtesy symbolic link instead.
+
+ There is no protection against setting this to a silly value, and
+ doing so is a great way to cause a crash.
+
+29) set chroot-server <number>
+ This option is for closed server systems only. If defined, a chroot()
+ call to the user's home directory is done as part of the login
+ process. This has the effect of preventing access to any files
+ outside of the user's home directory (including shared mailboxes).
+
+ Shared mailboxes with other users can't possibly work with this
+ option, because there is no way to export lock information to other
+ users.
+
+ This should be done ONLY on systems which do not permit users to
+ have shell access
+
+ This option should NEVER(!!) be set if users are allowed shell access.
+ Doing so actually makes the system *less* secure, since the user could
+ create an etc subdirectory which would be treated as real /etc by such
+ programs as /bin/su.
+
+ The default is zero (don't do chroot).
+
+ This option is strongly *NOT* recommended.
+
+30) set disable-automatic-shared-namespaces <number>
+ Never look up the "ftp", "imappublic", and "imapshared" users as
+ posssible home directories for the #ftp, #public, and #shared
+ namespaces. On some systems (reportedly including AIX 4.3.3)
+ getpwnam() of an unknown user name is horrendously slow.
+
+ Note that this does not remove the #ftp, #public, and #shared
+ namespaces, and they can still be set up by other means.
+
+ The default is zero (shared namespaces are automatic).
+
+31) set advertise-the-world <number>
+ Include the UNIX root as a shared namespace. This is generally a bad
+ idea, since certain IMAP clients (names withheld to protect the guilty)
+ will take this as license to download the entire filesystem tree.
+
+ The default is zero (don't advertise the world).
+
+32) set mail-subdirectory <subdirectory name>
+ Change the default connected directory from the user's home directory
+ to the named subdirectory of the user's home directory. For example,
+ setting MAILSUBDIR="mail" will cause the POP2 and IMAP servers to
+ connect to the user's ~/mail subdirectory. This is equivalent to
+ the env_unix.c edit described in Example 2 of the CONFIG file.
+
+ Note that if the subdirectory does not exist, the result is undefined.
+ It is probably an extremely bad idea to set this unless you can
+ guarantee that the subdirectory exists for all users. If you can not
+ guarantee this, then you should leave the default as the user's home
+ directory and allow them to configure a personal default in their IMAP
+ client.
+
+ The default is not to use any subdirectory.
+
+33) set allow-user-config <number>
+ Allow users to use ~/.imaprc and ~/.mminit files.
+
+ The default is zero (don't allow user config files).
+
+34) set allow-reverse-dns <number>
+ By default, the servers (ipop[23]d and imapd) will do gethostbyaddr()
+ on the local and remote sockets so that imapd can identify itself
+ properly (this is important when the same CPU hosts multiple virtual
+ hosts on different IP addresss) and also includes the client's name
+ when it writes to the syslog. There are also client gethostbyaddr()
+ calls, used primarily by authentication mechanisms.
+
+ Setting this option to zero disables all gethostbyaddr() calls. The
+ returned "host name" string for the socket is just the bracketed
+ [12.34.56.78] form, as if the reverse DNS lookup failed.
+
+ WARNING: Some authentication mechanisms, e.g. Kerberos V, depend upon
+ the host names being right, and if you set this option, it won't work.
+
+ You should only do this if you are encountering server performance
+ problems due to a misconfigured DNS, e.g. long startup delays or
+ client timeouts.
+
+ The default is non-zero (allow reverse DNS).
+
+35) set disable-plaintext <number>
+ Disable plaintext password authentication (LOGIN command, AUTH=LOGIN,
+ and AUTH=PLAIN).
+
+ The default is zero (allow plaintext authentication).
+
+36) set trust-dns <number>
+ By default, host names are canonicalized via gethostbyname() for
+ everything except for SSL certificate validation.
+
+ This can represent a security bug due to DNS spoofing, but is more
+ likely to deliver results that users expect. It also may be necessary
+ for SASL authentication to work right (e.g. generating a correct name
+ for a Kerberos service principal) if the name entered by the user is a
+ CNAME or not a fully-qualified domain name.
+
+ If trust-dns is set to zero, no host name canonicalization is done.
+ The user's actual entered name is used for SASL authentication and
+ will appear in the mailbox name of the open stream.
+
+ The default is non-zero (do DNS canonicalization).
+
+37) set sasl-uses-ptr-name <number>
+ By default, if trust-dns is set, the host names used in authentication
+ (e.g. to generate a Kerberos service principal) are canonicalized via
+ gethostbyaddr() instead of by gethostbyname(). If gethostbyaddr()
+ fails the gethostbyname() canonicalization is used.
+
+ This represents an additional security bug due to DNS spoofing, over and
+ above trust-dns. It also adds an additional DNS query to starting a
+ session.
+
+ It is necessary for sites which implement a server cluster with multiple
+ A records for a cluster name (instead of a CNAME) but each cluster
+ member has a unique PTR record which it expects for a Kerberos service
+ principal.
+
+ If sasl-uses-ptr-name is set to zero and trust-dns is set non-zero, the
+ gethostbyname() canonicalized name is used for SASL authentication.
+
+ The setting of sasl-uses-ptr-name is irrelevant if trust-dns is set to
+ zero.
+
+ The default is non-zero (use name from PTR record for SASL).
+
+38) set network-filesystem-stat-bug <number>
+ By default, traditional UNIX mailbox files are only closed and reopened
+ at checkpoint and expunge time. This ensures that, prior to rewriting
+ the file, that any cached stat() data from a network filesystem is
+ updated with current data.
+
+ Very old versions of NFS, and reputedly also AFS, can get into a state
+ in which the cached stat() data stays out-of-date, even across a
+ close and reopen of the file.
+
+ If network-filesystem-stat-bug is set non-zero, then the mailbox file
+ is closed and reopened at ping time as a workaround for this bug in
+ these network filesystems. This means that in imapd, the mailbox
+ file is closed and reopened for every IMAP command. This is obviously
+ something that should be avoided unless absolutely necessary.
+
+ NFS and AFS are terrible ways to distribute mail. You use use IMAP
+ servers with a local disk instead.
+
+ The default is zero (only close/reopen at checkpoint and expunge time).
+
+ Setting this option is a great way to ruin your system's performance.
+
+39) set restrict-mailbox-access <option> <option> ... <option>
+ This option is for closed server systems only. It is less extreme
+ than chroot-server, and allows selective restriction of what mailbox
+ named users can use. The existing options are:
+ root access not permitted to names starting with "/"
+ otherusers access not permitted to other users' names; this should
+ normally be used in conjunction with "root", otherwise
+ another user's names can be accessed via a root name.
+ all all of the above
+ Setting any combination of options also disables access to superior
+ directories via "..".
+
+ This should be done ONLY on systems which do not permit users to
+ have shell access
+
+ The default is no restrictions.
diff --git a/imap/docs/internal.txt b/imap/docs/internal.txt
new file mode 100644
index 00000000..203688e8
--- /dev/null
+++ b/imap/docs/internal.txt
@@ -0,0 +1,2988 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+ Documentation of c-client Functions and Interfaces
+
+REVISED: 19 August 1996
+
+ Credits
+
+ The original version of this document was written by Mark Crispin at
+the University of Washington, and described the version of c-client that
+supported the IMAP2 (RFC 1176) and IMAP2bis (unpublished) protocols.
+
+ This version is a substantial rewrite of that document, and was
+written by Mark Crispin with funding from Sun Microsystems, Incorporated.
+Sun's generous support of this work is gratefully acknowledged.
+
+
+ Road Map
+
+ This document is organized into the following sections. Except as
+noted, an implementor of an application that uses c-client needs to be
+familiar with all of these sections. Someone who plans to write a new
+mailbox driver for c-client (or otherwise modify it) needs to be familiar
+with all sections, no exception.
+
+History
+ History of how c-client came about.
+
+Overview
+ Read this before designing an application that uses c-client.
+
+c-client Structures
+ Documentation of several important c-client structs which are
+ used in, and returned by, c-client calls.
+
+String Structures
+ Documentation of the concept of a "string structure", which
+ provides random access to strings without requiring that the
+ string be in memory.
+
+c-client Support Functions
+ Documentation of support functions for c-client; these deal
+ with c-client functionality.
+
+ Only mail_parameters() is of interest to most application
+ developers. Advanced application developers, particularly
+ for limited memory systems, may also need to know about the
+ readfn_t, mailgets_t, mailcache_t, and tcptimeout_t function
+ pointer types, and possibly also the mail_valid_net_parse()
+ function.
+
+Mailbox Access Functions
+ Documentation of functions which deal with mailboxes;
+ listing, subscribing, creating, deleting, renaming, status
+ inquiries, opening, and closing mailboxes.
+
+Handle Functions
+ Documentation of mail stream handles, which provide protection
+ for an advanced application which may have multiple pointers to
+ a single mail stream. If a stream has a handle on it, closing
+ the stream does not release its memory, so pointers to it in
+ the application remain valid. Freeing the last handle will free
+ the entire stream.
+
+ This is only of interest for advanced application developers.
+
+Message Data Fetching Functions
+ Documentation on message data fetching in an open mailbox,
+ including parsed representations of RFC-822 and MIME headers
+ and message text. Also how to fetch message attributes (flags,
+ internal date, sizes).
+
+Message Status Manipulation Functions
+ Documentation on altering message flags in an open mailbox.
+
+Mailbox Searching
+ Documentation on searching an open mailbox for messages which
+ match certain criteria (e.g. "messages sent July 4 from Jones
+ with text `Paris'").
+
+Miscellaneous Mailbox and Message Functions
+ Documentation on other operations that would be used by an
+ application but that don't fit into any of the above categories.
+
+Date/Time Handling Functions
+ Documentation on functions that deal with date/time strings.
+
+ This is only of interest for advanced application developers
+ and for implementors of new c-client drivers.
+
+Utility Functions
+ Documentation on internal utility functions.
+
+ This is primarily of interest for implementors of new c-client
+ drivers, but advanced application developers may also use some
+ of these functions.
+
+Data Structure Instantiation/Destruction functions
+ Documentation on creating and destroy c-client structures.
+
+ This is primarily of interest for implementors of new c-client
+ drivers. However, application developers will need some of
+ these functions to create and destroy structures which are used
+ as arguments to various application functions.
+
+Authentication Functions
+ Documentation on support for network protocol authentication
+ functions.
+
+ This is only of interest for implementors of new c-client
+ drivers which deal with authentication mechanisms.
+
+Network Access Functions
+ Documentation on creating and destroy c-client structures.
+
+ This is primarily of interest for implementors of new c-client
+ drivers which deal with a network. However, advanced
+ application developers may need to use this information if they
+ wish to insert their own layer into a network session.
+
+Subscription Management Functions
+ Documentation on managing the local (client-based) subscription
+ database file.
+
+ This is primarily of interest to advanced application developers.
+
+Miscellaneous Utility Functions
+ Documentation on various useful utility functions, such as "make
+ a copy of this string."
+
+SMTP Functions
+ Documentation on posting email messages via SMTP protocol.
+
+NNTP Functions
+ Documentation on posting netnews messages via NNTP protocol.
+
+RFC 822 Support Functions
+ Documentation on public RFC-822/MIME functions.
+
+ This is primarily of interest for implementors of new c-client
+ drivers and advanced application developers.
+
+Operating System-Dependent Public Interface
+ Documentation on OS-dependent functions. With the exception of
+ fs_get(), fs_give(), and fs_resize(), which should be called
+ instead of malloc(), free(), and realloc(), these functions are
+ primarily of interest for implementors of new c-client drivers.
+
+Main Program Callbacks
+ Documentation of functions which the main program must provide
+ as callbacks from c-client.
+
+Driver Interface
+ Documentation of the driver dispatch vector and the functions
+ which a driver must supply.
+
+ This is primarily of interest for implementors of new c-client
+ drivers.
+
+Driver Support Functions
+ Documentation of support functions which are called by drivers.
+
+ This is primarily of interest for implementors of new c-client
+ drivers.
+ History
+
+ The c-client API was originally written by Mark Crispin at Stanford
+University as a set of routines to support IMAP and SMTP from a main
+program which would handle the user interface. In its original form, it
+was written as the low-level routines that were to be used as part of a
+Macintosh client.
+
+ The first IMAP client, MM-D (for "MM on Xerox D machines" -- MM was a
+popular DEC-20 mail program) was written in Interlisp for Xerox Lisp
+machines. At that time, there was no name for the embryonic Mac client,
+but since it was the first one to be written in C instead of Lisp, it was
+given a development name of "C client". This name became "c-client"
+because that is the name of the subdirectory on UNIX where the source files
+were stored.
+
+ To exercise the routines, a minimal main program which uses c-client,
+mtest, was written. mtest has subsequently been extended so that it runs
+on every platform that c-client is ported.
+
+ The real Mac client, was eventually written by Frank Gilmurrary and
+Bill Yeager at Stanford using the autumn 1988 version of c-client and named
+"MacMS". In the winter of 1988-89, Mark Crispin, who had changed jobs to
+the University of Washington, developed MS as an MM-like text-based program
+for UNIX and MailManager as a GUI-based program for NeXT machines.
+
+ The realization sunk in that this API needed its own name. As early
+as spring 1989, there were at least four programs (mtest, MS, MailManager,
+and MacMS) that used it. The name c-client thus became permanent.
+
+ In its history, c-client has undergone two major redesigns, both by
+Mark Crispin who is now on the staff at the University of Washington.
+
+ The first major redesign added the following:
+ 1) ANSI C calling conventions throughout to assist in function
+ argument type checking.
+ 2) Vectoring mail access calls through "driver" methods; thus
+ providing transparent access to multiple types of mail
+ stores with the same call.
+ 3) MIME support.
+
+ The second major redesign was part of the IMAP4 project. Many
+c-client functions were extended with additional arguments and options.
+The driver interface was also made simpler, with more work done by
+driver-independent code.
+
+ Overview
+
+ The most important file for the author of an application using the
+c-client is mail.h. mail.h defines several important structures of
+data which are passed between the main program and the c-client.
+Although some functions (e.g. mail_fetchtext_body()) return the data
+fetched, for certain other data items (e.g. flags) you need to get the
+data as a structure reference. mail.h also defines a large number of
+useful constants and structures.
+
+ When a function in mail.h exists to reference data, it MUST be
+used instead of referencing the structures directly. This is because
+in some cases the data is not actually fetched until a reference (via
+the function call) is made. For example, although the MESSAGECACHE
+element for a message can be obtained by indexing the proper cache
+element in the stream, there is no guarantee that the item in fact
+exists unless mail_fetchstructure_full() is called for that message.
+Less costly functions. also exist to create and load a MESSAGECACHE
+element.
+
+ The main program will probably also need to include smtp.h,
+misc.h, and osdep.h, but this usage should be solely to receive
+function prototypes. Any other definitions in those files should be
+considered private to that module.
+
+ Two important predefined symbols are NIL and T. NIL is any sort
+of "false"; T is any sort of "true". NIL is also used to null-specify
+certain optional arguments.
+
+ * * * IMPORTANT * * *
+
+ Any multi-threaded application should test stream->lock prior to
+calling any c-client stream functions. Any attempt to call a
+mail_xxx() function while one is already in progress on the same
+stream will cause the application to fail in unpredictable ways.
+
+ Note that this check is insufficient in a preemptive-scheduling
+multi-tasking application due to the possibility of a timing race.
+Such applications must be written so that only one process accesses
+the stream, or to have a higher level lock.
+
+ Since MAIL operations will not finish until they are completed, a
+single-tasking application does not have to worry about this problem,
+except in the callback invoked from MAIL (e.g. mm_exists(), etc.) in which
+case the stream is *always* locked.
+
+ c-client Structures
+
+ c-client has a large number of structures which are used for
+multiple functions. The most important of these are described here.
+
+ The MAILSTREAM structure is used to reference open mailboxes.
+Applications may reference the following:
+
+char *mailbox; mailbox name
+unsigned short use; stream use count, this is incremented
+unsigned short sequence; stream sequence, this is incremented
+ each time a stream is reused (i.e.
+ mail_open() is called to open a
+ different mailbox on this stream)
+unsigned int rdonly : 1; stream is open read-only
+unsigned int anonymous : 1; stream is open with anonymous access
+unsigned int halfopen : 1; stream is half-open; it can be
+ reopened or used for functions that
+ don't need a open mailbox such as
+ mail_create() but no message data
+ can be fetched
+unsigned int perm_seen : 1; Seen flag can be set permanently
+unsigned int perm_deleted : 1; Deleted flag can be set permanently
+unsigned int perm_flagged : 1; Flagged flag can be set permanently
+unsigned int perm_answered :1; Answered flag can be set permanently
+unsigned int perm_draft : 1; Draft flag can be set permanently
+unsigned int kwd_create : 1; new user flags can be created by
+ referencing then in mail_setflag() or
+ mail_clearflag(). Note: this can
+ change during a session (e.g. if
+ there is a limit on the number of
+ keywords), so check after creating a
+ new flag to see if any more can be
+ created before letting the user try
+ to do so
+unsigned long perm_user_flags; corresponding user flags can be set
+ permanently. This is a bit mask
+ which matches the entries in
+ stream->user_flags[]
+unsigned long gensym; generated unique value. Always
+ referenced with stream->gensys++
+unsigned long nmsgs; number of messages in current mailbox
+unsigned long recent; number of recent messages in current
+ mailbox
+unsigned long uid_validity; UID validity value; this is used to
+ verify that recorded UIDs match the
+ UIDs that the stream has. If the
+ mailbox does not have matching UIDs
+ (e.g. the UIDs were lost or not
+ recorded) then the UID validity value
+ will be different
+unsigned long uid_last; highest currently assigned UID in the
+ current mailbox; a new UID will be
+ assigned with ++stream->uid_last
+char *user_flags[NUSERFLAGS]; pointers to user flag names in bit
+ order from stream->perm_user_flags or
+ elt->user_flags
+
+ The following MAILSTREAM values are only used internally:
+
+DRIVER *dtb; dispatch table for this driver
+void *local; pointer to driver local data
+unsigned int lock : 1; stream lock flag (an operation is in
+ progress; used as a bug trap to
+ detect recursion back to c-client
+ from callback routines).
+unsigned int debug : 1; debugging information should be logged
+ via mm_dlog().
+unsigned int silent : 1; don't do main program callbacks on
+ this stream (used when a stream is
+ opened internally)
+unsigned int scache : 1; short caching; don't cache information
+ in memory
+
+ The following MAILSTREAM values are only used by the cache
+manager routine (see the documentation about mailcache_t above):
+
+unsigned long cachesize; size of c-client message cache
+union {
+ void **c; to get at the cache in general
+ MESSAGECACHE **s; message cache array
+ LONGCACHE **l; long cache array
+} cache;
+
+ The following MAILSTREAM values are for the convenience of
+drivers that use short caching and want to be able to garbage collect
+any values that they returned:
+
+unsigned long msgno; message number of `current' message
+ENVELOPE *env; pointer to `current' message envelope
+BODY *body; pointer to `current' message body
+char *text; pointer to `current' text
+
+
+ The MESSAGECACHE structure (commonly called an "elt" as a
+nickname for "cache ELemenT") contains information about messages.
+Applications may use the following:
+
+unsigned long msgno; message number. If the elt is locked
+ (by elt->lockcount++), then the elt
+ pointer can be stored (e.g. with the
+ data for a window which draws this
+ message) and elt->msgno will change
+ automatically whenever expunges are
+ done so the window will always view
+ the correct message. If elt->msgno
+ becomes 0, then the message has been
+ expunged, but the elt won't be freed
+ until the elt lock count is
+ decremented (by mail_free_elt()).
+unsigned long uid; message unique ID
+unsigned int hours: 5; internal date hours (0-23)
+unsigned int minutes: 6; internal date minutes (0-59)
+unsigned int seconds: 6; internal date seconds (0-59)
+unsigned int zoccident : 1; non-zero if internal date time zone is
+ west of UTC
+unsigned int zhours : 4; internal date time zone hours from UTC
+ (0-12)
+unsigned int zminutes: 6; internal date time zone minutes (0-59)
+unsigned int seen : 1; message Seen flag
+unsigned int deleted : 1; message Deleted flag
+unsigned int flagged : 1; message Flagged flag
+unsigned int answered : 1; message Answered glag
+unsigned int draft : 1; message Draft flag
+unsigned int valid : 1; flags are valid in this elt; an elt
+ that was newly created but never
+ loaded with flags won't have this set.
+unsigned int recent : 1; message recent flag
+unsigned int searched : 1; message matches search criteria in
+ most recent mail_search_full() call
+unsigned int spare : 1; reserved for application use
+unsigned int spare2 : 1; reserved for application use
+unsigned int spare3 : 1; reserved for application use
+unsigned int lockcount : 8; non-zero if multiple references to
+ this elt. Refer to the msgno member
+ for more information.
+unsigned int day : 5; internal date day of month (1-31)
+unsigned int month : 4; internal date month of year (1-12)
+unsigned int year : 7; internal date year since BASEYEAR
+ (currently 1970; was 1969 in older
+ versions so use BASEYEAR instead of
+ having the base year wired in)
+unsigned long user_flags; message user flags; this is a bit mask
+ which matches the entries in
+ stream->user_flags[]
+unsigned long rfc822_size; size of message in octets
+
+ The following MESSAGECACHE values are only used internally by
+drivers:
+
+unsigned int sequence : 1; message is in sequence from either
+ mail_sequence() or mail_uid_sequence()
+unsigned long data1; first data item
+unsigned long data2; second data item
+unsigned long data3; third data item
+unsigned long data4; fourth data item
+
+
+ The ADDRESS structure is a parsed form of a linked list of RFC 822
+addresses. It contains the following information:
+
+char *personal; personal name phrase
+char *adl; at-domain-list (also called "source
+ route")
+char *mailbox; mailbox name
+char *host; domain name of mailbox's host
+char *error; error in address from smtp_mail(); if
+ an error is returned from smtp_mail()
+ for one of the recipient addresses
+ the SMTP server's error text for that
+ recipient can be found here. If it
+ is null then there was no error (or
+ an error was found with a prior
+ recipient
+ADDRESS *next; pointer to next address in list
+
+
+ The ENVELOPE structure is a parsed form of the RFC 822 header.
+Its member names correspond to the RFC 822 field names. It contains
+the following information:
+
+char *remail; remail header if any
+ADDRESS *return_path; error return address
+char *date; message composition date string
+ADDRESS *from; from address list
+ADDRESS *sender; sender address list
+ADDRESS *reply_to; reply address list
+char *subject; message subject string
+ADDRESS *to; primary recipient list
+ADDRESS *cc; secondary recipient list
+ADDRESS *bcc; blind secondary recipient list
+char *in_reply_to; replied message ID
+char *message_id; message ID
+char *newsgroups; USENET newsgroups
+char *followup_to; USENET reply newsgroups
+char *references; USENET references
+
+
+ The BODY structure is a parsed form of a linked list of the MIME
+structure of a message. It contains the following information.
+
+unsigned short type; body primary type code. This is an
+ index into the body_types vector of
+ body type names. The following body
+ types are pre-defined:
+ TYPETEXT unformatted text
+ TYPEMULTIPART multiple part
+ TYPEMESSAGE encapsulated message
+ TYPEAPPLICATION application data
+ TYPEAUDIO audio
+ TYPEIMAGE static image (GIF, JPEG, etc.)
+ TYPEVIDEO video
+ TYPEOTHER unknown
+ Additional types up to TYPEMAX are
+ dynamically defined if they are
+ encountered by c-client.
+unsigned short encoding; body transfer encoding. This is an
+ index into the body_encodings vector
+ of body encoding names. The
+ following body encodings are
+ pre-defined:
+ ENC7BIT 7 bit SMTP semantic data
+ ENC8BIT 8 bit SMTP semantic data
+ ENCBINARY 8 bit binary data
+ ENCBASE64 base-64 encoded data
+ ENCQUOTEDPRINTABLE human-readable 8-as-7 bit data
+ ENCOTHER unknown
+ Additional encodings up to ENCMAX are
+ dynamically defined if they are
+ encountered by c-client.
+char *subtype; body subtype string
+PARAMETER *parameter; parameter list
+char *id; body content identifier
+char *description; body content description
+unsigned char *contents.text; when composing a message that is NOT
+ of TYPEMULTIPART, non-binary text of
+ the content is stored here. Note that
+ this happens even when the text is
+ of TYPEMESSAGE. Text of encoding
+ ENC8BIT may be converted to
+ ENCQUOTEDPRINTABLE when it is sent.
+ This should not be referenced for any
+ other reason; in particular, this is
+ NOT the way for an application to
+ access content data (use
+ mail_fetchbody_full() instead).
+BINARY *contents.binary; when composing a message that is NOT
+ of TYPEMULTIPART, binary content (of
+ encoding ENCBINARY) is stored here.
+ It will be converted to ENCBASE64 when
+ it is sent.
+ This should not be referenced for any
+ other reason; in particular, this is
+ NOT the way for an application to
+ access content data (use
+ mail_fetchbody_full() instead).
+PART *contents.part; for body parts of TYPEMULTIPART, this
+ contains the list of body parts in
+ this multipart
+MESSAGE contents.msg; for body parts of TYPEMESSAGE with
+ subtype "RFC822", this contains the
+ encapsulated message
+unsigned long size.lines; size in lines
+unsigned long size.bytes; size in octets. This MUST be set when
+ composing a message if the encoding is
+ ENC8BIT or ENCBINARY.
+char *md5; body content MD5 checksum
+
+ The following BODY information is used only by c-client
+internally. The use of this data is driver-specific and it can not be
+relied-upon by applications.
+
+unsigned char *contents.text; drivers can store a pointer to the
+ body contents as text here.
+unsigned long size.ibytes; internal size of the body content (prior
+ to newline conversion, etc.) in octets
+
+
+ The MESSAGE structure is a parsed form of a MESSAGE/RFC822 MIME
+body part. It contains the following information:
+
+ENVELOPE *env; encapsulated message RFC 822 header
+BODY *body; encapsulated message MIME structure
+
+ The following MESSAGE information is used only by c-client
+internally. The use of this data is driver-specific and it can not be
+relied-upon by applications.
+
+char *hdr; encapsulated message header
+unsigned long hdrsize; message header size
+char *text; message in RFC 822 form
+unsigned long offset; offset of text from header
+
+
+ The PARAMETER structure is a parsed form of a linked list of
+attribute/value pairs. It contains the following information:
+
+char *attribute; attribute name
+char *value; value
+PARAMETER *next; next parameter in list
+
+
+ The PART structure is a parsed form of a linked list of MIME body
+parts. It contains the following information:
+
+BODY body; body information for this part
+PART *next; next body part
+
+ The following PART information is used only by c-client
+internally. The use of this data is driver-specific and it can not be
+relied-upon by applications.
+
+unsigned long offset; offset from body origin
+
+
+ The NETMBX structure is a parsed form of a network mailbox name:
+
+char host[NETMAXHOST]; remote host name
+char user[NETMAXUSER]; remote user name if specified
+char mailbox[NETMAXMBX]; remote mailbox name
+char service[NETMAXSRV]; remote service name (IMAP4, NNTP, etc.)
+unsigned long port; TCP/IP port number if specified
+unsigned int anoflag : 1; anonymous access requested
+unsigned int dbgflag : 1; protocol debugging telemetry, via
+ mm_dlog(), requested
+
+
+ The STRINGLIST structure is a list of strings (which may have
+embedded NULs) and their lengths:
+
+char *text; string text
+unsigned long size; string length
+STRINGLIST *next; next string in list
+
+ String Structures
+
+ A string structure is analogous to a char*, and is used in some
+functions as an input argument. It represents a string of data in a
+way that does not necessarily require the entire string to be in
+memory at once. This is essential for small machines with
+highly-restricted memory limits (e.g. DOS).
+
+ String Structure Access
+
+ To use a string structure, the caller needs to know a string
+driver and needs to know the driver-dependent data used by that string
+structure. A simple string driver is mail_string, a string driver
+that takes an in-memory char* string as the driver-dependent data.
+The DOS port uses string drivers that take a struct holding a file
+descriptor and a file offset. Often the user of a string driver is
+the same module that defined it, so usually the programmer knows about
+its conventions.
+
+ The following calls are used to access a string structure:
+
+void INIT (STRING *s,STRINGDRIVER *d,void *data,unsigned long size);
+ s pointer to the string structure to be initialized
+ d pointer to the string driver
+ data pointer to driver-dependent data, from which the
+ driver can determine string data
+ size size of the string
+ This call initializes the string stucture.
+
+
+unsigned long SIZE (STRING *s);
+ s pointer to the string structure
+ This call returns the number of characters remaining in the string
+after the current string character pointer.
+
+
+char CHR (STRING *s);
+ s pointer to the string structure
+ This call returns the character at the current string character
+pointer.
+
+
+char SNX (STRING *s);
+ s pointer to the string structure
+ This call returns the character at the current string character
+pointer, and increments the string character pointer.
+
+
+unsigned long GETPOS (STRING *s);
+ s pointer to the string structure
+ This returns the value of the current string character pointer.
+
+
+void SETPOS (STRING *s,unsigned long i);
+ s pointer to the string structure
+ i new string pointer value
+ This method sets the string character pointer to the given value.
+
+
+ String Structure Internals
+
+ A string structure holds the following data:
+
+void *data; used by the string driver as it likes
+unsigned long data1; used by the string driver as it likes
+unsigned long size; static, holds the total length of the string
+ from the INIT call
+char *chunk; current chunk of in-memory data; this is used
+ for buffering to avoid unnecessary calls to
+ the string driver's next method.
+unsigned long chunksize; size of an in-memory data chunk
+unsigned long offset; position of first character of the chunk in
+ the overall string
+char *curpos; current position; this is what CHR() will
+ access
+unsigned long cursize; number of characters remaining in the current
+ string
+STRINGDRIVER *dtb; the string driver for this string structure
+
+
+ A string structure is manipulated by a string driver, which has
+the following access methods:
+
+void (*init) (STRING *s,void *data,unsigned long size);
+ s pointer to the string structure to be initialized
+ data pointer to driver-dependent data, from which the
+ driver can determine string data
+ size size of the string
+ This method initializes the string stucture. It can use the data,
+data1, and chunksize values as it likes. The remaining values must be
+set up as follows:
+ size static, copied from the size argument
+ chunk pointer to a buffer loaded with initial data
+ chunksize size of the buffer
+ offset 0
+ curpos copied from chunk
+ cursize copied from chunksize
+ dtb STRINGDRIVER identity pointer
+
+
+char (*next) (STRING *s);
+ s pointer to the string structure
+ This method returns the character at the current string character
+pointer, and increments the string character pointer. This method
+is likely to call the setpos method if the desired character is not in
+the current chunk.
+
+
+void (*setpos) (STRING *s,unsigned long i);
+ s pointer to the string structure
+ i new string pointer value
+ This method sets the string character pointer to the given value. If
+the pointer is not in the current chunk, then a new chunk is loaded
+and the associated values (chunk, offset, curpos, cursize) are
+adjusted accordingly.
+
+ c-client Support Functions
+
+
+void mail_string_init (STRING *s,void *data,unsigned long size);
+char mail_string_next (STRING *s);
+void mail_string_setpos (STRING *s,unsigned long i);
+
+ These three functions are the init, next, and setpos string
+structure access methods for the build-in mail_string string driver.
+mail_string is a basic string driver for a char* string. See the
+documentation below on "String Structures" for more information.
+
+
+void mail_link (DRIVER *driver);
+ driver pointer to the driver to be added
+
+ This function adds the specified driver to the list of mailbox
+drivers. Initially there are no drivers lunk, so all programs which
+intend to use c-client need to have at least one call to this function.
+
+ A function which uses IMAP4 would have a statement such as:
+ mail_link (&imapdriver); /* link in IMAP driver */
+early in the program's initialization. Normally, this is done by the
+statement
+ #include "linkage.c"
+which will include the "system standard driver linkage" defined when
+c-client was built. By using linkage.c instead of explicit mail_link()
+calls, you are guaranteed that you will have a consistant linkage among
+all software built on this system.
+
+
+void auth_link (AUTHENTICATOR *auth);
+ auth pointer to the authenticator to be added
+
+ This function adds the specified authenticator to the list of
+authenticators. Initially there are no authenticators lunk. Normally,
+this is done by linkage.c so you don't need to call this routine
+explicitly.
+
+
+void *mail_parameters (MAILSTREAM *stream,long function,void *value);
+ stream stream to poll or NIL
+ function function code
+ value new value for function codes that change a parameter
+
+ This function fetches or changes the settings of various c-client
+operational parameters depending upon the function. If the stream is
+specified, only the action for the underlying driver for that stream is
+taken; however, the scope of the operational parameters is global so
+there is generally no reason for the stream argument ever to be
+non-NIL.
+
+ The function codes ENABLE_DRIVER and DISABLE_DRIVER take a driver
+pointer as a value. These functions enable and disable mailbox
+processing by that driver. By default, all drivers are enabled.
+
+ The remaining function codes are in a pair named GET_xxx to
+fetch an operational parameter and SET_xxx to set the parameter:
+
+ GET_DRIVERS / SET_DRIVERS
+ The list of currently lunk drivers.
+
+ GET_GETS / SET_GETS
+ If non-NIL, points to a function for reading message text.
+ Defaults to NIL.
+ This function is called with three arguments; a function
+ pointer to a "reading function", a stream for the reading
+ function, and a size in octets. The reading function is
+ in turn called with the stream, a size in octets, and a
+ pointer to a readin buffer.
+ This function returns with a char* string, which will be
+ returned by the mail_fetchheader(), mail_fetchtext(), or
+ mail_fetchbody() function which triggered the message text
+ reading.
+ The purpose is to permit reading of large strings, without
+ requiring an in-memory buffer for the entire string. The idea
+ is that this function can store the data in some form other
+ than a char* (e.g. a temporary file) and the main program will
+ recognize that it should get the text from there instead of
+ from the results from mail_fetch....().
+ This is only supported on DOS and Win16; on other platforms it
+ is inconsistent whether or not it works.
+
+ GET_CACHE / SET_CACHE
+ Points to the c-client cache manager function. Defaults to
+ mm_cache().
+
+ GET_SMTPVERBOSE / SET_SMTPVERBOSE
+ If non-NIL, points to a function that accepts a char* string.
+ This function is called any time the SMTP routines receive a
+ response code less than 100. The argument is the text of the
+ response code
+
+ GET_RFC822OUTPUT / SET_RFC822OUTPUT
+ If non-NIL, points to an alternate rfc822_output() function.
+ rfc822_output() will call this function and return instead of
+ doing its normal action. See the description of
+ rfc822_output() for more information.
+
+ GET_USERNAME / SET_USERNAME
+ The logged-in user name.
+
+ GET_HOMEDIR / SET_HOMEDIR
+ The home directory path name.
+
+ GET_LOCALHOST / SET_LOCALHOST
+ The local host name.
+
+ GET_SYSINBOX / SET_SYSINBOX
+ The "system INBOX" (where mail is delivered) path name.
+
+ GET_OPENTIMEOUT / SET_OPENTIMEOUT
+ TCP/IP open timeout in seconds. Defaults to 0 (system
+ default timeout, usually 75 seconds on Unix).
+
+ GET_READTIMEOUT / SET_READTIMEOUT
+ TCP/IP read timeout in seconds. Defaults to 0 (no timeout).
+
+ GET_WRITETIMEOUT / SET_WRITETIMEOUT
+ TCP/IP write timeout in seconds. Defaults to 0 (no timeout).
+
+ GET_CLOSETIMEOUT / SET_CLOSETIMEOUT
+ TCP/IP close timeout in seconds. Defaults to 0 (no timeout).
+
+ GET_TIMEOUT / SET_TIMEOUT
+ If non-NIL, points to the function called when a TCP/IP
+ timeout occurs. This function is called with the number of
+ seconds since the start of the TCP operation. If it returns
+ non-zero, the TCP/IP operation is continued; if it returns
+ non-zero, the TCP/IP connection is aborted.
+
+ GET_RSHTIMEOUT / SET_RSHTIMEOUT
+ rsh connection timeout in seconds. Defaults to 15 seconds.
+
+ GET_MAXLOGINTRIALS / SET_MAXLOGINTRIALS
+ The maximum number of login attempts permitted in an IMAP or
+ POP connection. Defaults to 3.
+
+ GET_LOOKAHEAD / SET_LOOKAHEAD
+ The number of subsequent envelopes prefetched in IMAP when an
+ envelope is fetched. Defaults to 20.
+
+ GET_IMAPPORT / SET_IMAPPORT
+ The IMAP port number. Defaults to 143.
+
+ GET_PREFETCH / SET_PREFETCH
+ The number of envelopes prefetched in IMAP from the results
+ of a SEARCH. Defaults to 20.
+
+ GET_CLOSEONERROR / SET_CLOSEONERROR
+ If non-NIL, close an opening IMAP connection if the SELECT
+ command fails instead of returning a half-open stream.
+ Defaults to NIL.
+
+ GET_POP3PORT / SET_POP3PORT
+ The POP3 port number. Defaults to 110.
+
+ GET_UIDLOOKAHEAD / SET_UIDLOOKAHEAD
+ The number of UIDs premapped when a message number is
+ translated to a UID. Defaults to 1000.
+
+ GET_MBXPROTECTION / SET_MBXPROTECTION
+ Default file protection for newly created mailboxes.
+ Defaults to 0600.
+
+ GET_DIRPROTECTION / SET_DIRPROTECTION
+ Default file protection for newly created directories.
+ Defaults to 0700.
+
+ GET_LOCKPROTECTION / SET_LOCKPROTECTION
+ Default file protection for locks. Defaults to 0666.
+ WARNING: don't blithely change this. If other processes
+ can't get access to a lock then they will have trouble in
+ locking properly.
+
+ GET_FROMWIDGET / SET_FROMWIDGET
+ If non-NIL, APPEND in the Unix mbox format will insert a
+ ">" character in front of all lines which begin with the
+ string "From ". If NIL, it will only do so if the entire
+ line looks like a message delimiter (that is, the date is
+ also in correct format). Defaults to T.
+
+ GET_NEWSACTIVE / SET_NEWSACTIVE
+ Netnews active file path name.
+
+ GET_NEWSSPOOL / SET_NEWSSPOOL
+ Netnews spool directory path name.
+
+ GET_NEWSRC / SET_NEWSRC
+ Netnews newsgroup reading status file (.newsrc) path name.
+
+ GET_EXTENSION / SET_EXTENSION
+ If non-NIL, points to a string holding the extension for all
+ mailbox files. This is only supported on DOS and Win16.
+
+ GET_DISABLEFCNTLLOCK / SET_DISABLEFCNTLLOCK
+ If non-NIL, disables fcntl() locking on SVR4. This is done
+ if fcntl() tends to hang for no good reason. Now that the
+ fcntl() code checks for NFS files and no-ops the locking,
+ this problem usually doesn't happen much any more. Defaults
+ to NIL.
+
+ GET_LOCKEACCESERROR / SET_LOCKEACCESERROR
+ If non-NIL, give a warning if an attempt to create a .lock
+ file gets an EACCES ("Permission denied") error. This usually
+ means that somebody protected the system inbox directory (e.g.
+ /var/mail) instead of making it public-write with the sticky
+ bit. Defaults to non-NIL, since this is usually bad news.
+
+ GET_LISTMAXLEVEL / SET_LISTMAXLEVEL
+ The maximum depth of recusion that LIST will go on a *
+ wildcard. Defaults to 20.
+
+ GET_ANONYMOUSHOME / SET_ANONYMOUSHOME
+ The anonymous use home directory name.
+
+
+typedef long (*readfn_t) (void *stream,unsigned long size,char *buffer);
+ stream a designator suitable
+ size a number of octets to read
+ buffer a buffer of at least size octets for readin
+
+ This function reads the given number of octets into the buffer,
+using the given stream. What sort of object the stream is depends upon
+the function and its caller, so you must make sure that the readfn is
+suitable for the caller's purpose. Common uses include support of the
+mailgets function (see below) and of reading from local files on systems
+with limited address space.
+
+
+typedef char *(*mailgets_t) (readfn_t f,void *stream,unsigned long size);
+ f the readfn to use
+ stream stream argument for the readfn
+ size total number of octets to read
+
+ This is the argument to the SET_GETS mail_parameter() call. This
+function must read size octets from the stream, using the readfn f. It
+may call f multiple times to accomplish this; this will read the data in
+a serial fashion. So, for example, if size is a megabyte and there is
+only 4K of available buffer space, it can call f 256 times to satisfy
+the request. There is no way to back up in the reading, so any
+processing or saving of the data must be done when it is read.
+
+ The function mm_gets() in mail.c is a sample mailgets function; it
+reads the first MAXMESSAGESIZE of data into memory and discards the
+rest.
+
+
+typedef void *(*mailcache_t) (MAILSTREAM *stream,unsigned long msgno,long op);
+ stream stream to cache manage
+ msgno message to cache manage in the stream
+ op cache management operation
+
+ This function manages the c-client cache. Normally, a program will
+use the default c-client cache manager routine mm_cache(). However, a
+main program may want to supply its own cache manager, e.g. it may want
+to store the data on a disk file instead of in memory on DOS and Win16
+where memory is tight.
+
+ If you write your own cache manager, you need to examine the
+default mm_cache() manager closely, as well as paying close attention to
+what goes into an elt (a MESSAGECACHE element). It is highly likely
+that if you roll elts out to disk, you will want to set stream->scache
+and *NOT* use long elts (because long elts have ENVELOPE and BODY
+pointers that you would have to know how to write to disk and read back).
+
+ The cache management functions are one of the following:
+
+ CH_INIT Initialize the entire cache for the stream. This is
+ called only when creating a new stream or when freeing
+ it. The msgno argument is ignored.
+
+ CH_SIZE Make sure that the cache is at least large enough to
+ support msgno. This is a request to grow the cache if
+ necessary, not shrink it.
+
+ CH_MAKELELT Return a long elt for msgno, creating it if necessary.
+ This is the underlying support function for mail_lelt().
+
+ CH_LELT Return the long elt for msgno, or NIL if it does not
+ already exist.
+
+ CH_MAKEELT Return an elt for msgno, creating it if necessary.
+ This is the underlying support function for mail_elt().
+
+ CH_ELT Return the elt for msgno, or NIL if it does not already
+ exist.
+
+ CH_FREE Free the [l]elt for msgno.
+
+ CH_EXPUNGE Free the [l]elt for msgno, and reclaim its position.
+ All subsequent elts are renumbered with their elt->msgno
+ decremented by 1. [Hence msgno+1 becomes msgno, etc.]
+ This supports message expunging from the cache.
+
+
+typedef long (*tcptimeout_t) (long time);
+ time total time spent since TCP operation started
+
+ This function is called when a TCP operation times out. It is set
+by the SET_TIMEOUT mail_parameter(). The function can return non-zero
+to continue the TCP operation (e.g. after outputting a "do you still
+want to wait" prompt) or zero if it wants the TCP operation to abort and
+close. If the TCP operation aborts, it will likely cause the upper
+level IMAP, SMTP, etc. stream to abort and close as well.
+
+
+DRIVER *mail_valid (MAILSTREAM *stream,char *mailbox,char *purpose);
+ stream if non-NIL, stream to use for validation
+ mailbox mailbox name to validate
+ purpose filled in as xxx in "Can't xxx" in error messages
+
+ This function validates the given mailbox name. It successful, it
+returns the driver that can open that name if successful, otherwise it
+returns NIL. If stream is non-NIL, the mailbox name must be valid for
+the type of mailbox associated with that stream (e.g. an NNTP name can
+not be used with an IMAP stream). If purpose is non-NIL, an error
+message is passed via mm_log() when an error occurs.
+
+
+DRIVER *mail_valid_net (char *name,DRIVER *drv,char *host,char *mailbox);
+ name mailbox name to validate
+ drv driver name to validate against
+ host buffer to return host name if non-NIL
+ mailbox buffer to return remote mailbox name if non-NIL
+
+ This function is an alternative to mail_valid_net_parse(). It
+validates the given mailbox name as a network name and makes sure that
+its service name is the same as the driver in drv. If successful, it
+returns drv, and copies the host and mailbox strings as needed.
+Otherwise it returns NIL.
+
+
+long mail_valid_net_parse (char *name,NETMBX *mb);
+ name mailbox name to parse
+ mb pointer to NETMBX structure to return
+
+ This function parses a network mailbox name. If the name is a
+network mailbox name, it returns non-NIL, with the NETMBX structure
+loaded with the results form the parse.
+
+ Mailbox Access Functions
+
+void mail_list (MAILSTREAM *stream,char *ref,char *pat);
+void mail_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+ stream if non-NIL, stream to use
+ ref mailbox reference string
+ pat mailbox pattern string
+ contents contents to search
+
+ This function returns a list of mailboxes via the mm_list()
+callback. The reference is applied to the pattern in an implementation
+dependent fashion, and the resulting string is used to search for
+matching mailbox names. "*" is a wildcard which matches zero or more
+characters; "%" is a variant which does not descend a hierarchy level.
+Read the IMAP specification for more information.
+
+ mail_scan() is a variant which takes a string to search for in the
+text of the mailbox. The string is a free-text string, without regard
+for message boundaries, and thus the choice of strings must be made
+with care.
+
+
+void mail_lsub (MAILSTREAM *stream,char *ref,char *pat);
+ stream if non-NIL, stream to use
+ ref mailbox reference string
+ pat mailbox pattern string
+
+ This function returns a list of subscribed mailboxes via the
+mm_lsub() callback. The reference is applied to the pattern in an
+implementation dependent fashion, and the resulting string is used to
+search for matching mailbox names in the subscription list. "*" is a
+wildcard which matches zero or more characters; "%" is a variant which
+does not descend a hierarchy level. Read the IMAP specification for
+more information.
+
+
+long mail_subscribe (MAILSTREAM *stream,char *mailbox);
+ stream if non-NIL, stream to use
+ mailbox mailbox name
+
+ This function adds the given name to the subscription list. It
+returns T if successful, NIL if unsuccessful. If unsuccessful, an
+error message is returned via the mm_log() callback.
+
+
+long mail_unsubscribe (MAILSTREAM *stream,char *mailbox);
+ stream if non-NIL, stream to use
+ mailbox mailbox name
+
+ This function removes the given name from the subscription list.
+It returns T if successful, NIL if unsuccessful. If unsuccessful, an
+error message is returned via the mm_log() callback.
+
+
+long mail_create (MAILSTREAM *stream,char *mailbox);
+ stream if non-NIL, stream to use
+ mailbox mailbox name
+
+ This function creates a mailbox with the given name. It returns T
+if successful, NIL if unsuccessful. If unsuccessful, an error message
+is returned via the mm_log() callback.
+
+ It is an error to create INBOX or a mailbox name which already
+exists.
+
+
+long mail_delete (MAILSTREAM *stream,char *mailbox);
+ stream if non-NIL, stream to use
+ mailbox mailbox name
+
+ This function deletes the named mailbox. It returns T if
+successful, NIL if unsuccessful. If unsuccessful, an error message is
+returned via the mm_log() callback.
+
+ It is an error to delete INBOX or a mailbox name which does not
+already exist.
+
+
+long mail_rename (MAILSTREAM *stream,char *old,char *newname);
+ stream if non-NIL, stream to use
+ old existing mailbox name
+ newname new (not yet existing) mailbox name
+
+ This function renames the old mailbox to the new mailbox name.
+It returns T if successful, NIL if unsuccessful. If unsuccessful, an
+error message is returned via the mm_log() callback.
+
+ It is an error to reanme a mailbox that does not exist, or rename
+a mailbox to a name that already exists. It is permitted to rename
+INBOX; a new empty INBOX is created in its place.
+
+
+long mail_status (MAILSTREAM *stream,char *mbx,long flags);
+ stream if non-NIL, stream to use
+ mbx mailbox name
+ flags option flags
+
+ This function returns the status of the given mailbox name via the
+mm_status() callback. It returns T if successful, NIL if unsuccessful.
+If unsuccessful, an error message is returned via the mm_log()
+callback.
+
+ The options are a bit mask with one or more of the following,
+indicating the data which should be returned.
+ SA_MESSAGES number of messages in the mailbox
+ SA_RECENT number of recent messages in the mailbox
+ SA_UNSEEN number of unseen messages in the mailbox
+ SA_UIDNEXT next UID value to be assigned
+ SA_UIDVALIDITY UID validity value
+
+ Note that, depending upon implementation, some of these values may
+be more costly to get than others. For example, calculating the
+number of unseen messages may require opening the mailbox and scanning
+all of the message flags. A mail_status() call should thus be used
+with option flags specifying only the data that is actually needed.
+
+
+MAILSTREAM *mail_open (MAILSTREAM *oldstream,char *name,long options);
+ oldstream if non-NIL, stream to recycle
+ name mailbox name to open
+ options option flags.
+
+ This function opens the mailbox and if successful returns a stream
+suitable for use by the other MAIL functions.
+
+ If oldstream is non-NIL, an attempt is made to reuse oldstream as
+the stream for this mailbox; this is useful when you want to open
+another mailbox to the same IMAP or NNTP server without having to open
+a new connection. Doing this will close the previously open mailbox.
+
+ The options are a bit mask with one or more of the following:
+ OP_DEBUG Log IMAP protocol telemetry through mm_debug()
+ OP_READONLY Open mailbox read-only.
+ OP_ANONYMOUS Don't use or update a .newsrc file for news.
+ OP_SHORTCACHE Don't cache envelopes or body structures
+ OP_SILENT Don't pass mailbox events (internal use only)
+ OP_PROTOTYPE Return the "prototype stream" for the driver
+ associated with this mailbox instead of
+ opening the stream
+ OP_HALFOPEN For IMAP and NNTP names, open a connection
+ to the server but don't open a mailbox.
+ OP_EXPUNGE Silently expunge the oldstream before recycling
+
+ NIL is returned if this function fails for any reason.
+
+
+MAILSTREAM *mail_close (MAILSTREAM *stream);
+MAILSTREAM *mail_close_full (MAILSTREAM *stream,long options);
+ stream stream to close
+ options option flags
+ This function closes the MAIL stream and frees all resources
+associated with it that it may have created (subject to any handles
+existing).
+
+ The options for mail_close_full() are a bit mask with one or more
+of the following:
+ CL_EXPUNGE Silently expunge before closing
+
+ This function always returns NIL, so it can be used as:
+ stream = mail_close (stream);
+
+ Handle Functions
+
+ Handles are used when an entity that wishes to access the stream
+may survive the stream without knowing that it outlived it. For
+example, an object reading a message may have a handle to a stream,
+but the message selection object that spawned it (and which owns the
+stream) may have gone away. A stream can be closed or recycled while
+handles are pointing at it, but it is not completely freed until all
+handles are gone. A stream may have an arbitrary number of handles.
+
+
+MAILHANDLE *mail_makehandle (MAILSTREAM *stream);
+ stream stream to make handle to
+
+ This function creates and returns a handle to the stream.
+
+
+void mail_free_handle (MAILHANDLE **handle);
+ handle pointer to handle to release
+
+ This function frees the handle and notifies the stream that it has
+one fewer handle. If this is the last handle on the stream and the
+stream has been closed, then the stream is freed.
+
+
+MAILSTREAM *mail_stream (MAILHANDLE *handle);
+ handle handle to look up
+
+ This function returns the stream associated with the handle if and
+only if the stream still represents the same MAIL connection associated
+with the handle. Otherwise, NIL is returned (meaning that there is no
+active stream associated with this handle).
+
+ Message Data Fetching Functions
+
+[Note!! There is an important difference between a "sequence" and a
+ "msgno". A sequence is a string representing one or more messages in
+ IMAP4-style sequence format ("n", "n:m", or combination of these
+ delimited by commas), whereas a msgno is an int representing a single
+ message.]
+
+void mail_fetchfast (MAILSTREAM *stream,char *sequence);
+void mail_fetchfast_full (MAILSTREAM *stream,char *sequence,long flags);
+ stream stream to fetch on
+ sequence IMAP-format set of message sequence numbers
+ flags option flags
+
+ This function causes a cache load of all the "fast" information
+(internal date, RFC 822 size, and flags) for the given sequence. Since
+all this information is also fetched by mail_fetchstructure(), this
+function is generally not used unless the OP_SHORTCACHE option in the
+mail_open() call is used.
+
+ The options for mail_fetchfast_full() are a bit mask with one or
+more of the following:
+ FT_UID The sequence argument contains UIDs instead of
+ sequence numbers
+
+
+void mail_fetchflags (MAILSTREAM *stream,char *sequence);
+void mail_fetchflags_full (MAILSTREAM *stream,char *sequence,long flags);
+
+ This function causes a fetch of the flags for the given sequence.
+This main reason for using this function is to update the flags in the
+local cache in case some other process changed the flags (multiple
+simultaneous write access is allowed to the flags) as part of a "check
+entire mailbox" (as opposed to "check for new messages") operation.
+
+ The options for mail_fetchflags_full() are a bit mask with one or more
+of the following:
+ FT_UID The sequence argument contains UIDs instead of
+ sequence numbers
+
+
+ENVELOPE *mail_fetchenvelope (MAILSTREAM *stream,unsigned long msgno);
+ENVELOPE *mail_fetchstructure (MAILSTREAM *stream,unsigned long msgno,
+ BODY **body);
+ENVELOPE *mail_fetchstructure_full (MAILSTREAM *stream,unsigned long msgno,
+ BODY **body,long flags);
+ stream stream to fetch on
+ msgno message sequence number
+ body pointer to where to return BODY structure if non-NIL
+ flags option flags
+ This function causes a fetch of all the structured information
+(envelope, internal date, RFC 822 size, flags, and body structure) for
+the given msgno and, in the case of IMAP, up to MAPLOOKAHEAD (a
+parameter in IMAP2.H) subsequent messages which are not yet in the
+cache. No fetch is done if the envelope for the given msgno is already
+in the cache. The ENVELOPE and the BODY for this msgno is returned.
+It is possible for the BODY to be NIL, in which case no information is
+available about the structure of the message body.
+
+ The options for mail_fetchstructure_full() are a bit mask with one
+or more of the following:
+ FT_UID The msgno argument is a UID
+
+ This is the primary function for fetching non-text information
+about messages, and should be called before any attempt to reference
+cache information about this message via mail_elt().
+
+
+char *mail_fetchheader (MAILSTREAM *stream,unsigned long msgno);
+char *mail_fetchheader_full (MAILSTREAM *stream,unsigned long msgno,
+ STRINGLIST *lines,unsigned long *len,long flags);
+ stream stream to fetch on
+ msgno message sequence number
+ lines list of header lines to fetch
+ len returned length in octets
+ flags option flags
+
+ This function causes a fetch of the complete, unfiltered RFC 822
+format header of the specified message as a text string and returns
+that text string.
+
+ If the lines argument is non-NIL, it contains a list of header
+field names to use in subsetting the header text. Only those lines
+which have that header field name are returned, unless FT_NOT is set in
+which case only those lines which do not have that header field name
+are returned.
+
+ If the len argument is non-NIL, it holds a pointer in which the
+length of the string in octets is returned. This is useful in cases
+where there may be an embedded null in the string.
+
+ This function always returns a valid string pointer; if no header
+exists or if it can not be fetched (e.g. by a deceased IMAP stream) an
+empty string is returned.
+
+ The options for mail_fetchheader_full() are a bit mask with one or
+more of the following:
+ FT_UID The msgno argument is a UID
+ FT_NOT The returned header lines are those that are
+ not in the lines argument
+ FT_INTERNAL The return string is in "internal" format,
+ without any attempt to canonicalize to CRLF
+ newlines
+ FT_PREFETCHTEXT The RFC822.TEXT should be pre-fetched at the
+ same time. This avoids an extra RTT on an
+ IMAP connection if a full message text is
+ desired (e.g. in a "save to local file"
+ operation)
+
+
+char *mail_fetchtext (MAILSTREAM *stream,unsigned long msgno);
+char *mail_fetchtext_full (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *len,long flags);
+ stream stream to fetch on
+ msgno message sequence number
+ len returned length in octets
+ flags option flags
+
+ This function causes a fetch of the non-header text of the
+specified message as a text string and returns that text string. No
+attempt is made to segregate individual body parts.
+
+ If the len argument is non-NIL, it holds a pointer in which the
+length of the string in octets is returned. This is useful in cases
+where there may be an embedded null in the string.
+
+ This function always returns a valid string pointer; if no header
+exists or if it can not be fetched (e.g. by a deceased IMAP stream) an
+empty string is returned.
+
+ The options for mail_fetchtext_full() are a bit mask with one or
+more of the following:
+ FT_UID The msgno argument is a UID
+ FT_PEEK Do not set the \Seen flag if it not already set
+ FT_INTERNAL The return string is in "internal" format,
+ without any attempt to canonicalize to CRLF
+ newlines
+
+
+char *mail_fetchbody (MAILSTREAM *stream,unsigned long msgno,char *sec,
+ unsigned long *len);
+char *mail_fetchbody_full (MAILSTREAM *stream,unsigned long msgno,char *sec,
+ unsigned long *len,long flags);
+ stream stream to fetch on
+ msgno message sequence number
+ sec section specifier
+ len returned length in octets
+ flags option flags
+
+ This function causes a fetch of the particular section of the
+body of the specified message as a text string and returns that text
+string. The section specification is a string of integers delimited by
+period which index into a body part list as per the IMAP4
+specification. Body parts are not decoded by this function; see
+rfc822_base64() and rfc822_quotedprintable().
+
+ If the len argument is non-NIL, it holds a pointer in which the
+length of the string in octets is returned. This is useful in cases
+where there may be an embedded null in the string.
+
+ This function may return NIL on error.
+
+ The options for mail_fetchbody_full() are a bit mask with one or
+more of the following:
+ FT_UID The msgno argument is a UID
+ FT_PEEK Do not set the \Seen flag if it not already set
+ FT_INTERNAL The return string is in "internal" format,
+ without any attempt to canonicalize to CRLF
+ newlines
+
+
+unsigned long mail_uid (MAILSTREAM *stream,unsigned long msgno);
+ stream stream to fetch on
+ msgno message sequence number
+
+ This function returns the UID for the given message sequence
+number.
+
+
+void mail_fetchfrom (char *s,MAILSTREAM *stream,unsigned long msgno,
+ long length);
+ s destination string
+ stream stream to fetch on
+ msgno message sequence number
+ length maximum field length
+
+ This function writes a "from" string of the specified length for
+the specified message, suitable for display to the user in a menu line,
+into the string pointed to by s.
+
+ If the personal name of the first address in the envelope's from
+item is non-NIL, it is used; otherwise a string is created by appending
+the mailbox of the first address, an "@", and the host of the first
+address. The string is trimmed or padded with trailing spaces as
+necessary to make its length match the length argument.
+
+
+void mail_fetchsubject (char *s,MAILSTREAM *stream,unsigned long msgno,
+ long length);
+ s destination string
+ stream stream to fetch on
+ msgno message sequence number
+ length maximum field length
+
+ This function returns a "subject" string of the specified length
+for the specified message, suitable for display to the user in a menu
+line.
+
+ The envelope's subject item is copied and trimmed as necessary
+to make its length be no more what the caller requested. Unlike
+mail_fetchfrom(), this function can return a string of shorter length
+than what the caller requested.
+
+
+LONGCACHE *mail_lelt (MAILSTREAM *stream,unsigned long msgno);
+MESSAGECACHE *mail_elt (MAILSTREAM *stream,unsigned long msgno);
+ stream stream to access
+ msgno message sequence number
+
+ This function returns the cache entry for the specified message.
+Although it will create a cache entry if it does not already exist,
+that functionality is for internal use only. This function should
+never be called without having first called mail_fetchfast() or
+mail_fetchstructure() on the message first.
+
+ A cache entry holds the internal date/time, flags, and RFC 822
+size of a message. It holds other data as well, but that is for
+internal use only.
+
+ mail_lelt() is a variant that returns a `long' cache entry, which
+consists of an cache entry (as a structure, not a pointer), an envelope
+pointer, and a body pointer. This is used in conjunction with the elt
+lock count functionality, to allow an application to associate the
+cached envelope and body of a message with an open window even if the
+message is subsequently expunged or if the stream is closed.
+
+ Unless your application wants to look at cached envelopes and
+bodies even after the message is expunged or the stream is closed, it
+should not use mail_lelt(). Instead, it should use a returned elt from
+mail_elt() and use the elt->msgsno as the argument to
+mail_fetchstructure().
+
+ BEWARE: the behavior of mail_lelt() is undefined if the
+ stream is open with OP_SHORTCACHE. mail_lelt() is extremely
+ special purpose, and should only be used in sophisticated
+ special purpose applications after discussing its use with
+ the c-client author. If you think you need this function,
+ you are probably mistaken. In almost all cases, you should
+ use mail_elt() and mail_fetchstructure() instead.
+
+ Message Status Manipulation Functions
+
+void mail_setflag (MAILSTREAM *stream,char *sequence,char *flag);
+void mail_setflag_full (MAILSTREAM *stream,char *sequence,char *flag,
+ long flags);
+ stream stream to use
+ sequence IMAP-format set of message sequence numbers
+ flag IMAP-format flag string
+ flags option flags
+
+ This function causes a store to add the specified flag to the flags
+set for the messages in the specified sequence. If there is any
+problem in setting flags, a message will be passed to the application
+via the mm_log() facility.
+
+ The options for mail_setflag_full() are a bit mask with one or
+more of the following:
+ ST_UID The sequence argument contains UIDs instead of
+ sequence numbers
+ ST_SILENT Do not update the local cache with the new
+ value of the flags. This is useful to save
+ network bandwidth, at the cost of invalidating
+ the cache.
+
+
+void mail_clearflag (MAILSTREAM *stream,char *sequence,char *flag);
+void mail_clearflag_full (MAILSTREAM *stream,char *sequence,char *flag,
+ long flags);
+ stream stream to use
+ sequence IMAP-format set of message sequence numbers
+ flag IMAP-format flag string
+ flags option flags
+
+ This function causes a store to delete the specified flag from the
+flags set for the messages in the specified sequence. If there is any
+problem in clearing flags, a message will be passed to the application
+via the mm_log() facility.
+
+ The options for mail_setflag_full() are a bit mask with one or
+more of the following:
+ ST_UID The sequence argument contains UIDs instead of
+ sequence numbers
+ ST_SILENT Do not update the local cache with the new
+ value of the flags. This is useful to save
+ network bandwidth, at the cost of invalidating
+ the cache.
+
+ Mailbox Searching
+
+void mail_search (MAILSTREAM *stream,char *criteria);
+void mail_search_full (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,
+ long flags);
+ stream stream to search
+ charset MIME character set to use when searching strings
+ pgm search program
+ flags option flags
+
+ This function causes a mailbox search, using the given MIME
+charset (NIL means the default, US-ASCII) and the given search program.
+A search program is a structure that holds the following data:
+
+SEARCHSET *msgno; a set of message sequence numbers
+SEARCHSET *uid; a set of unique identifiers
+SEARCHOR *or; OR result of two search programs
+SEARCHPGMLIST *not; AND result of list of NOT'ed search programs
+SEARCHHEADER *header; message headers
+STRINGLIST *bcc; string(s) appear in bcc list
+STRINGLIST *body; string(s) appear in message body text
+STRINGLIST *cc; string(s) appear in cc list
+STRINGLIST *from; string(s) appear in from
+STRINGLIST *keyword; user flag string(s) set
+STRINGLIST *unkeyword; user flag strings() not set
+STRINGLIST *subject; string(s) appear in subject
+STRINGLIST *text; string(s) appear in message header or body
+STRINGLIST *to; string(s) appear in to list
+unsigned long larger; larger than this many octets
+unsigned long smaller; smaller than this many octes
+ The following dates are in form:
+ ((year - BASEYEAR) << 9) + (month << 5) + day
+unsigned short sentbefore;
+ sent before this date
+unsigned short senton; sent on this date
+unsigned short sentsince;
+ sent since this date
+unsigned short before; received before this date
+unsigned short on; received on this date
+unsigned short since; received since this date
+unsigned int answered : 1;
+ message answered
+unsigned int unanswered : 1;
+ message not answered
+unsigned int deleted : 1;
+ message deleted
+unsigned int undeleted : 1;
+ message not deleted
+unsigned int draft : 1; message is a draft
+unsigned int undraft : 1;
+ message is not a draft
+unsigned int flagged : 1;
+ message flagged as urgent
+unsigned int unflagged : 1;
+ message not flagged as urgent
+unsigned int recent : 1;
+ message recent since last parse of mailbox
+unsigned int old : 1; message not recent since last parse of mailbox
+unsigned int seen : 1; message read
+unsigned int unseen : 1;
+ message not read
+
+ The following auxillary structures are used by search programs:
+ SEARCHHEADER: header line searching
+char *line; header line field name
+char *text; text header line
+SEARCHHEADER *next; next SEARCHHEADER in list (AND'ed)
+
+ SEARCHSET: message number set
+unsigned long first; first number in set
+unsigned long last; if non-zero, last number in set
+SEARCHSET *next; next SEARCHSET in list (AND'ed)
+
+ SEARCHOR: two search programs, OR'ed together
+SEARCHPGM *first; first program
+SEARCHPGM *second; second program
+SEARCHOR *next; next SEARCHOR in list
+
+ SEARCHPGMLIST: list of search programs
+SEARCHPGM *pgm; search program (AND'd with others in list)
+SEARCHPGMLIST *next; next SEARCHPGM in list
+
+ mail_search(), the older interface, accepts a search criteria
+argument as a character string in IMAP2 (RFC-1176) format. Do not try
+to use any IMAP4 search criteria with this interface.
+
+ The application's mm_searched() function is called for each
+message that matches the search criteria. In addition, after the
+search is completed, the "fast" information (see mail_fetchfast_full()
+and envelopes of the searched messages are fetched (this is called
+pre-fetching).
+
+ If there is any problem in searching, a message will be passed to
+the application via the mm_log() facility.
+
+ The flags for mail_search_full() are a bit mask with one or more
+of the following:
+ SE_UID Return UIDs instead of sequence numbers
+ SE_FREE Return the search program to free storage after
+ finishing
+ SE_NOPREFETCH Don't prefetch searched messages.
+
+
+unsigned long *mail_sort (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
+ SORTPGM *pgm,long flags);
+ stream stream to sort
+ charset MIME character set to use when sorting strings
+ spg search program
+ pgm sort program
+ flags option flags
+
+
+ This function is a variant of mail_search_full(). It accepts an
+additional argument, a sort program, which specifies one or more sort
+rules to be applied to the result. If the searching and sorting are
+successful, it returns a 0-terminated vector of message sequence
+numbers (or UIDs if SE_UID is set). This vector is created out of
+free storage, and must be freed with fs_give() when finished with it.
+
+ A sort program is a structure that holds the following data:
+unsigned int reverse : 1;
+ reverse sorting of this key
+short function; sort rule, one of the following:
+ SORTDATE message Date
+ SORTARRIVAL arrival date
+ SORTFROM mailbox in first From address
+ SORTSUBJECT message Subject
+ SORTTO mailbox in first To address
+ SORTCC mailbox in first cc address
+ SORTSIZE size of message in octets
+SORTPGM *next; next sort program to be applied if two or more
+ messages collate identically with this rule
+
+ The flags for mail_search_full() are a bit mask with one or more
+of the following:
+ SE_UID Return UIDs instead of sequence numbers
+ SE_FREE Return the search program to free storage after
+ finishing
+ SE_NOPREFETCH Don't prefetch searched messages.
+ SO_FREE Return the sort program to free storage after
+ finishing
+
+ Miscellaneous Mailbox and Message Functions
+
+long mail_ping (MAILSTREAM *stream);
+ stream string to ping
+
+ The function pings the stream to see if it is still active. It may
+discover new mail; this is the preferred method for a periodic "new mail
+check" as well as a "keep alive" for servers which have an inactivity
+timeout. It returns T if the stream is still alive, NIL otherwise.
+
+ If new mail is found, the application's mm_exists() function is
+called with the newly-determined number of messages in the mailbox.
+
+
+void mail_check (MAILSTREAM *stream);
+ stream stream to checkpoint
+
+ This function causes a mailstore-defined checkpoint of the
+mailbox. This may include such things as a writeback to disk, a check
+for flag changes in a shared mailbox, etc. It is not a "check for new
+mail"; mail_ping() performs this function (as potentially does any other
+function). The status of the check is passed to the application via the
+mm_log() facility.
+
+
+void mail_expunge (MAILSTREAM *stream);
+ stream string to expunge
+
+ This function causes an expunge (permanent removal of messages
+which are marked as deleted) of the mailbox. The application's
+mm_expunged() function is called for each message that has been
+expunged. The application's mm_exists() function is called at the start
+and end of the expunge to ensure synchronization. The status of the
+expunge is passed to the application via the mm_log() facility.
+
+ Note that the decrementing of msgno's for subsequent messages
+happens immediately; for example, if three consequtive messages starting
+at msgno 5 are expunged, mm_expunged() will be called with a msgno of 5
+three times.
+
+
+long mail_copy (MAILSTREAM *stream,char *sequence,char *mailbox);
+long mail_move (MAILSTREAM *stream,char *sequence,char *mailbox);
+long mail_copy_full (MAILSTREAM *stream,char *sequence,char *mailbox,
+ long options);
+ stream stream to copy
+ sequence IMAP-format set of message numbers
+ mailbox destination mailbox name
+ options option flags
+
+ This function causes the messages in the specified sequence to be
+copied to the specified mailbox. T is returned if the copy is
+successful. mail_move() is equivalent to setting CP_MOVE in the options.
+
+ If there is any problem in copying, a message will be passed to
+the application via the mm_log() facility and the function returns NIL.
+No copying is actually done in this case.
+
+ Note that the mailbox must be on the same host as the stream and
+is a mailbox of the type of the source mailbox only.
+
+ The flags for mail_search_full() are a bit mask with one or more
+of the following:
+ CP_UID The sequence argument contains UIDs instead of
+ sequence numbers
+ CP_MOVE Delete the messages from the current mailbox
+ after copying to the destination.
+
+
+long mail_append (MAILSTREAM *stream,char *mailbox,STRING *message);
+long mail_append_full (MAILSTREAM *stream,char *mailbox,char *flags,char *date,
+ STRING *message);
+ stream stream to use if non-NIL (in the IMAP case)
+ mailbox destination mailbox name
+ flags flags to set on message if non-NIL
+ date internal date (received date) to set on message if non-NIL
+ message string structure of message to write
+
+ This function writes the message in the string structure to the
+destination mailbox, along with the flags and date if specified. This
+is useful in those cases where you can't use mail_copy(), e.g. when
+copying from one server to another; you can always fetch the message
+and then mail_append() it to the destination. It may also be useful
+for maintaining an outbox of your outgoing mail.
+
+
+void mail_gc (MAILSTREAM *stream,long gcflags);
+ stream stream to GC if non-NIL (else GC's all streams)
+ flags option flags
+
+ This function garbage collects (purges) the cache of entries of
+a specific type. Some drivers do not allow purging of particular
+cache types, and an attempt to do so is ignored.
+
+ The flags for mail_gc() are a bit mask with one or more of the
+following:
+ GC_ELT message cache elements
+ GC_ENV ENVELOPEs and BODYs
+ GC_TEXTS cached texts
+
+ Date/Time Handling Functions
+
+
+char *mail_date (char *string,MESSAGECACHE *elt);
+ string destination string
+ elt message cache element containing date
+
+ This function accepts a message cache element that contains date
+information, and writes an IMAP-4 date string, that is, one in form:
+ dd-mmm-yyyy hh:mm:ss +zzzz
+based upon the data in the elt. The destination string must be large
+enough to hold this string.
+
+
+char *mail_cdate (char *string,MESSAGECACHE *elt);
+ string destination string
+ elt message cache element containing date
+
+ This function accepts a message cache element that contains date
+information, and writes a ctime() format date string, that is, one in
+form:
+ www mmm dd hh:mm:ss yyyy\n
+based upon the data in the elt. The destination string must be large
+enough to hold this string.
+
+
+long mail_parse_date (MESSAGECACHE *elt,char *string);
+ elt message cache element to store parsed date
+ string source date string
+
+ This function parses the date/time stored in the given string,
+in format:
+ [www,] date [[hh:mm[:ss][-zzz| +zzzz]
+where the date can be any of:
+ mm/dd/yy, mm/dd/yyyy, dd-mmm-yy, dd-mmm-yyyy, dd mmm yy, dd mmm yyyy
+and stores the result of the parse in the elt. If the parse is
+successful, T is returned, else NIL.
+
+
+unsigned long mail_longdate (MESSAGECACHE *elt);
+ elt message cache element containing date.
+
+ This function accepts a message cache element that contains date
+information, and returns the number of days since the base time of the
+imap-4 toolkit. At present, this is the same as the Unix time() value
+for that date/time, and hence can be used for functions such as utime().
+
+ Utility Functions
+
+void mail_debug (MAILSTREAM *stream);
+ stream stream to debug
+
+ This function enables telemetry logging for this stream. All
+telemetry is passed to the application via the mm_dlog() facility.
+
+
+void mail_nodebug (MAILSTREAM *stream);
+ stream stream to disable debugging
+
+ This function disables telemetry logging for this stream.
+
+
+long mail_sequence (MAILSTREAM *stream,char *sequence);
+ stream stream to set the sequence bits
+ sequence IMAP-format message set string
+
+ This function parses the given sequence string for message
+numbers, sets the sequence bit in the stream's message cache element
+of all messages in the sequence (and turns it off in all other message
+cache elements). If the parse is successful, T is returned, else NIL.
+
+
+long mail_uid_sequence (MAILSTREAM *stream,char *sequence);
+ stream stream to set the sequence bits
+ sequence IMAP-format message set string
+
+ This function parses the given sequence string for unique
+identifiers, sets the sequence bit in the stream's message cache
+element of all messages in the sequence (and turns it off in all other
+message cache elements). If the parse is successful, T is returned,
+else NIL.
+
+
+long mail_parse_flags (MAILSTREAM *stream,char *flag,unsigned long *uf);
+ stream stream (used to get user flags)
+ flag IMAP-format flag string to parse
+ uf returned location of user flags
+
+ The function parses the given flag string, and returns the system
+flags as its return value and the user flags in the location pointed
+to by the uf argument. If there is an error in parse, a log message
+is issued via mm_log() and this function returns NIL.
+
+
+unsigned long mail_filter (char *text,unsigned long len,STRINGLIST *lines,
+ long flags);
+ text RFC 822 text to filter
+ len length in octets in the text argument
+ lines string list of header file names to filter
+ flags option flags
+
+ This function supports the header lines filtering function of
+mail_fetchheader_full(). The lines argument contains a list of header
+field names to use in subsetting the header text. Only those lines
+which have that header field name are returned, unless FT_NOT is set
+in which case only those lines which do not have that header field
+name are returned.
+
+ The options for mail_filter() are a bit mask with one or more of
+the following:
+ FT_NOT The returned header lines are those that are
+ not in the lines argument
+
+
+long mail_search_msg (MAILSTREAM *stream,unsigned long msgno,char *charset,
+ SEARCHPGM *pgm);
+ stream stream to search
+ msgno message number of message to inspect
+ charset character set of search strings
+ pgm search program to test
+
+ This function implements mail_search_full() locally in cases when
+it is not done by a server (e.g. local mail files, NNTP/POP). It
+inspects the given message on that stream to see if it matches the
+criteria or not. If it matches, T is returned, else NIL.
+
+
+SEARCHPGM *mail_criteria (char *criteria);
+ criteria IMAP2-format search criteria string
+
+ This function accepts an IMAP2-format search criteria string and
+parses it. If the parse is successful, it returns a search program
+suitable for use in mail_search_full().
+ WARNING: This function does not accept IMAP4 search criteria.
+ The source string must be writeable (this restriction was also
+ in the old IMAP2 c-client).
+
+ Data Structure Instantiation/Destruction functions
+
+ These functions are used to obtain structures from free storage and
+to release them.
+
+ENVELOPE *mail_newenvelope (void);
+ADDRESS *mail_newaddr (void);
+BODY *mail_newbody (void);
+BODY *mail_initbody (BODY *body);
+PARAMETER *mail_newbody_parameter (void);
+PART *mail_newbody_part (void);
+STRINGLIST *mail_newstringlist (void);
+SEARCHPGM *mail_newsearchpgm (void);
+SEARCHHEADER *mail_newsearchheader (char *line);
+SEARCHSET *mail_newsearchset (void);
+SEARCHOR *mail_newsearchor (void);
+SEARCHPGMLIST *mail_newsearchpgmlist (void);
+SORTPGM *mail_newsortpgm (void);
+
+ These functions, all named mail_new...(), create a new structure of
+the given type and initialize all of its elements to zero or empty.
+
+void mail_free_body (BODY **body);
+void mail_free_body_parameter (PARAMETER **parameter);
+void mail_free_body_part (PART **part);
+void mail_free_cache (MAILSTREAM *stream);
+void mail_free_elt (MESSAGECACHE **elt);
+void mail_free_lelt (LONGCACHE **lelt);
+void mail_free_envelope (ENVELOPE **env);
+void mail_free_address (ADDRESS **address);
+void mail_free_stringlist (STRINGLIST **string);
+void mail_free_searchpgm (SEARCHPGM **pgm);
+void mail_free_searchheader (SEARCHHEADER **hdr);
+void mail_free_searchset (SEARCHSET **set);
+void mail_free_searchor (SEARCHOR **orl);
+void mail_free_searchpgmlist (SEARCHPGMLIST **pgl);
+void mail_free_sortpgm (SORTPGM **pgm);
+
+ These functions, all named mail_free_...(), take a pointer to a
+structure pointer, free all contained strings and structures within the
+structure, and finally free the structure itself and set its pointer to
+NIL. For example, mail_free_envelope() frees all the ADDRESS structures
+contained in the envelope.
+
+ Normally, mail_free_elt() and mail_free_lelt() are used only if the
+main program has a private pointer to cache elements. If so, it is
+expected to increment the cache element's lockcount when it makes a
+private pointer, and to call this function when it is finished with it.
+
+ Authentication Functions
+
+char *mail_auth (char *mechanism,authresponse_t resp,int argc,char *argv[]);
+ mechanism authentication mechanism name
+ resp callback function for providing responses
+ argc main() function argc value
+ argv main() function argv value
+
+ This server function searches the list of authenticators that was
+established by auth_link() for an authenticator with the given name. If
+an authenticator is found, authentication is initialized. The function
+pointed to by resp is called as the authenticator requires responses.
+
+
+AUTHENTICATOR *mail_lookup_auth (unsigned int i);
+ i position in authenticator list
+
+ This function returns the nth authenticator in the list, where n is
+the value of it.
+
+
+unsigned int mail_lookup_auth_name (char *mechanism);
+ mechanism authentication mechanism name
+
+ This function searches the list of authenticators for an
+authenticator with the given name, and returns its position in the
+authenticator list.
+
+
+ The functions below are provided by c-client client drivers or by
+servers to support the protocol-dependent parts of authentication.
+
+typedef void *(*authchallenge_t) (void *stream,unsigned long *len);
+ stream stream to read challenge
+ len pointer to returned length in octets
+
+ This driver function is called by an authenticator to read a
+challenge from the given protocol stream in a protocol-dependent way.
+It returns that challenge in binary and its length in octets to the
+authenticator.
+
+
+typedef long (*authrespond_t) (void *stream,char *s,unsigned long size);
+ stream stream to send response
+ s response string
+ size length of response string in octets
+
+ This driver function is called by an authenticator to send a
+challenge response to the given stream in a protocol-dependent way.
+It returns T if successful, NIL if failure.
+
+
+typedef char *(*authresponse_t) (void *challenge,unsigned long clen,
+ unsigned long *rlen);
+ challenge challenge string
+ clen length of challenge string in octets
+ rlen pointer to returned length of response string
+
+ This server function is called with a challenge string of clen
+octets. It sends, according to whatever protocol (IMAP, POP, etc.) it
+uses, and returns the received response and response length in octets.
+
+
+typedef long (*authclient_t) (authchallenge_t challenger,
+ authrespond_t responder,NETMBX *mb,void *s,
+ unsigned long trial);
+ challenger pointer to protocol-dependent challenge reader function
+ responder pointer to protocol-dependent response sender function
+ mb NETMBX struct of the mailbox desired to open
+ s stream for protocol-dependent routines to use
+ trial number of authentication attempts remaining
+
+ This client authenticator function negotiates reading challenges
+and sending responses for a particular authenticator (Kerberos, etc.)
+over the protocol, and returns T if authenticated or NIL if failed.
+
+
+typedef char *(*authserver_t) (authresponse_t responder,int argc,char *argv[]);
+ responder pointer to protocol-dependent responder function
+ argc main() function argc value
+ argv main() function argv value
+
+ This server authenticator function negotiates sending challenges and
+reading responses for a particular authenticator (Kerberos, etc.), and
+returns either the authenticated user name or NIL if authentication
+failed.
+
+ Network Access Functions
+
+ These functions provide a layer of indirection between the TCP
+routines and upper level routines. This makes it possible to insert
+additional code (e.g. privacy or checksum handling).
+
+NETSTREAM *net_open (char *host,char *service,unsigned long port);
+ host host name
+ service contact service name
+ port contact port number
+
+ This function opens a TCP connection to the given host and service
+or port.
+
+
+NETSTREAM *net_aopen (NETMBX *mb,char *service,char *usrbuf);
+ NETMBX parsed mailbox specification
+ service stream to open (at present, only /etc/rimapd is used)
+ usrbuf buffer to return login user name
+
+ This function attempts to open a preauthenticated connection to the
+given mailbox and service. It will return the login user name of the
+preauthenticated connection, as well as an open network stream, if
+successful.
+
+
+char *net_getline (NETSTREAM *stream);
+ stream network stream to read
+
+ This routine reads a text line from the stream. It calls
+stream->dtb->getline, which normally points to tcp_getline() but can be
+set to some other function.
+
+
+long net_getbuffer (void *stream,unsigned long size,char *buffer);
+ stream network stream to read
+ size length of data in octets
+ buffer buffer of at least size octets
+
+ This routine reads data from the stream. It calls
+stream->dtb->getbuffer, which normally points to tcp_getbuffer() but can
+be set to some other function.
+
+
+long net_soutr (NETSTREAM *stream,char *string);
+ stream network stream to write
+ string null-terminated string to output
+
+ This routine writes a null-terminated string to the stream. It
+calls stream->dtb->soutr, which normally points to tcp_soutr() but can
+be set to some other function.
+
+
+long net_sout (NETSTREAM *stream,char *string,unsigned long size);
+ stream network stream to write
+ string string to output
+ size length of string in octets
+
+ This routine writes a string of length size to the stream. It
+calls stream->dtb->sout, which normally points to tcp_sout() but can be
+set to some other function.
+
+
+void net_close (NETSTREAM *stream);
+ stream stream to close
+
+ This routine closes the stream. It calls stream->dtb->close, which
+normally points to tcp_close() but can point to some other function.
+
+
+char *net_host (NETSTREAM *stream);
+ stream stream to inspect
+
+ This routine returns the remote host name of the stream. It calls
+stream->dtb->host, which normally points to tcp_host() but can point
+to some other function.
+
+
+unsigned long net_port (NETSTREAM *stream);
+ stream stream to inspect
+
+ This routine returns the remote port number of the stream. It calls
+stream->dtb->port, which normally points to tcp_port() but can point
+to some other function.
+
+
+char *net_localhost (NETSTREAM *stream);
+ stream stream to inspect
+
+ This routine returns the local host name of the stream. It calls
+stream->dtb->localhost, which normally points to tcp_localhost() but can
+point to some other function.
+
+ Subscription Management Functions
+
+long sm_subscribe (char *mailbox);
+ mailbox mailbox name to subscribe
+
+ This function adds the given mailbox name to the local subscription
+list, and returns T if successful, NIL if failure.
+
+
+long sm_unsubscribe (char *mailbox);
+ mailbox mailbox name to unsubscribe
+
+ This function removes the given mailbox name from the local
+subscription list, and returns T if successful, NIL if failure.
+
+char *sm_read (void **sdb);
+ sdb data to use in subsequent calls, or NIL if first call
+
+ This function returns the local subscription list as null
+terminated strings. Each call returns the next element in the list.
+The first call should be with sdb pointing to a NIL pointer; this will
+be filled in for subsequent calls. At the last call, NIL will be
+returned.
+
+ Miscellaneous Utility Functions
+
+char *ucase (char *string);
+ string string to convert
+
+ This function converts each lowercase character of the specified
+string to uppercase and returns the string.
+
+
+char *lcase (char *string);
+ string string to convert
+
+ This function converts each uppercase character of the specified
+string to lowercase and returns the string.
+
+
+char *cpystr (char *string);
+ string string to copy
+
+ This function makes a copy of the string from free storage and returns
+the copy.
+
+
+long find_rightmost_bit (long *valptr);
+ valptr pointer to value to search
+
+ This function returns -1 if the 32-bit value pointed to by valptr
+is non-zero, otherwise it returns the bit number (0 = LSB, 31 = MSB) of
+the right-most bit in that value. This is used to convert from the bits
+in the cache's userflags item to an index into the stream's userFlags
+array of flag texts.
+
+
+long min (long i,long j);
+ i first argument
+ j second argument
+
+ This function returns the minimum of the two integers.
+
+long max (long i,long j);
+ i first argument
+ j second argument
+
+ This function returns the maximum of the two integers.
+
+long search (char *s,long c,char *pat,long patc);
+ s string to search
+ c size of string
+ pat pattern to search in string
+ patc size of pattern
+
+ This function does a fast case-independent search for the given
+pattern in pat (length patc) in base string s, and returns T if the
+pattern is found in the string.
+
+
+long pmatch (char *s,char *pat,delim);
+long pmatch_full (char *s,char *pat,delim);
+ s string to match
+ pat wildcard (* and %) to match in pattern
+ delim hierarchy delimiter
+
+ This function returns T if the given wildcard pattern matches the
+string in s with hierarchy delimiter delim. Otherwise NIL is returned.
+
+
+long dmatch (char *s,char *pat,char delim);
+ s string to match
+ pat wildcard (* and %) to match in pattern
+ delim hierarchy delimiter
+
+ This function returns T if the given wildcard pattern matches the
+directory. If not, then none of the elements in the directory are
+considered for recursive checking with pmatch_full().
+
+ SMTP Functions
+
+SMTPSTREAM *smtp_open (char **hostlist,long debug);
+ hostlist vector of SMTP server host names to try
+ debug non-zero if want protocol telemetry debugging
+
+ This function opens an SMTP connection to a one of the hosts in the
+host list and if successful returns a stream suitable for use by the
+other SMTP functions. The hosts are tried in order until a connection is
+successfully opened. If debug is non-NIL, protocol telemetry is logged
+via mm_dlog(). NIL is returned if this function fails to open a
+connection to any of the hosts in the list.
+
+void smtp_close (SMTPSTREAM *stream);
+ stream stream to close
+
+ This function closes the SMTP stream and frees all resources
+associated with it that it may have created.
+
+long smtp_mail (SMTPSTREAM *stream,char *type,ENVELOPE *msg,BODY *body);
+ stream stream to transmit mail
+ type mail type (MAIL, SEND, SAML, SOML)
+ msg message envelope
+ body message body
+
+ This function negotiates an SMTP transaction of the specified type
+(one of "MAIL", "SEND", "SAML", or "SOML") to deliver the specified
+message. This function returns T if success or NIL if there is any
+failure. The text reason for the failure is in stream->reply item; if
+it is associated with a recipient it is also in that address'
+address->error item.
+
+
+void smtp_debug (SMTPSTREAM *stream);
+ stream stream to enable debugging telemetry
+
+ This function enables SMTP protocol telemetry logging for this
+stream. All SMTP protocol operations are passed to the application via
+the mm_dlog() facility.
+
+
+void smtp_nodebug (SMTPSTREAM *stream);
+ stream stream to disable debugging telemetry
+
+ This function disables SMTP protocol telemetry logging for this
+stream.
+
+
+typedef void (*smtpverbose_t) (char *buffer);
+ buffer pointer to verbose reply buffer
+
+ This is the argument to the SET_SMTPVERBOSE mail_parmameter() call.
+If this function pointer is non-NIL, then if a verbose SMTP response
+(with SMTP code less than 100) is received, this function is called with
+that response text as its argument.
+
+ NNTP Functions
+
+NNTPSTREAM *nntp_open (char **hostlist,long debug);
+ hostlist vector of NNTP server host names to try
+ debug non-zero if want protocol telemetry debugging
+
+ This function opens an NNTP connection to a one of the hosts in the
+host list and if successful returns a stream suitable for use by the
+other MTP functions. The hosts are tried in order until a connection is
+successfully opened. If debug is non-NIL, protocol telemetry is logged
+via mm_dlog(). NIL is returned if this function fails to open a
+connection to any of the hosts in the list.
+
+
+void nntp_close (NNTPSTREAM *stream);
+ stream stream to close
+
+ This function closes the NNTP stream and frees all resources
+associated with it that it may have created.
+
+
+long nntp_mail (NNTPSTREAM *stream,ENVELOPE *msg,BODY *body);
+ stream stream to transmit mail
+ msg message envelope
+ body message body
+
+ This function negotiates an NNTP posting transaction to deliver
+the specified news message. This function returns T if success or NIL
+if there is any failure. The text reason for the failure is in
+stream->reply item; if it is associated with a recipient it is also in
+that address' address->error item.
+
+ RFC 822 Support Functions
+
+ Although rfc822.c contains several additional functions besides
+these, only the functions documented here should be used by
+applications. The other functions are for internal use only.
+
+
+void rfc822_header (char *header,ENVELOPE *env,BODY *body);
+ header buffer to write RFC 822 header
+ env message ENVELOPE (used to obtain RFC 822 information)
+ body message BODY (used to obtain MIME information)
+
+ This function writes an RFC 822 format header into header based
+on the information in the envelope and body. The header buffer must
+be large enough to contain the full text of the resulting header.
+
+
+void rfc822_write_address (char *dest,ADDRESS *adr);
+ dest buffer to write address list
+ adr RFC 822 ADDRESS list
+
+ This function writes an RFC 822 format address list into dest
+based on the information in adr. The dest buffer must be large enough
+to contain the full text of the resulting address list.
+
+void rfc822_parse_msg (ENVELOPE **en,BODY **bdy,char *s,unsigned long i,
+ STRING *b,char *host,char *tmp);
+ en destination pointer where message ENVELOPE will be stored
+ bdy destination pointer where message BODY will be stored
+ s RFC 822 header to parse (character string)
+ i length of RFC 822 header
+ b stringstruct of message body
+ host default host name if an address lacks an @host.
+ temp scratch buffer, must be long enough to hold unwound
+ header lines (a buffer that is i octets long is OK)
+
+ This function parses the RFC 822 header pointed to by s with body
+pointed to by string structure b into the specified destination
+envelope and body pointers, using host as the default host name and
+tmp as a scratch buffer. New ENVELOPE and BODY structures are
+created; when finished with them the application must free them with
+mail_free_envelope() and mail_free_body(). Any parsing errors are
+noted via the mm_log() mechanism using log type PARSE.
+
+
+void rfc822_parse_adrlist (ADDRESS **lst,char *string,char *host);
+ lst destination pointer where ADDRESS will be stored
+ string string of addresses to parse
+ host default host name if an address lacks an @host.
+
+ This function parses the address list in the given string into an
+address list in lst. Any addresses missing a host name are have the
+host name defaulted from the host argument. If the destination list
+is non-empty it appends the new addresses to the list. Any parsing
+errors are noted via the mm_log() mechanism using log type PARSE.
+
+long rfc822_output (char *t,ENVELOPE *env,BODY *body,soutr_t f,void *s,
+ long ok8bit);
+ t scratch buffer, large enough to hold message header
+ env message ENVELOPE
+ body message BODY
+ f I/O function to write to
+ s stream for I/O function f
+ ok8bit non-zero if OK to output 8-bit data
+
+ This function writes the message described with the given
+envelope and body. Any body part contents of type ENCBINARY is
+converted to ENCBASE64 before sending. If ok8bit is NIL, any message
+data of type ENC8BIT is converted to ENCQUOTEDPRINTABLE before
+sending; if ok8bit is non-NIL then ENC8BIT data is sent as-is. T is
+returned if the function succeeds, else NIL is returned.
+
+ The function f is typically net_soutr(), but it can be any
+function which matches
+ typedef long (*soutr_t) (void *stream,char *string);
+where stream holds sufficient information to enable the output routine
+to know where to output to, and the string is a null-terminated string
+to output. This function returns either T or NIL, and that value is
+passed up to rfc822_output() for its return.
+
+
+void *rfc822_base64 (char *src,unsigned long srcl,unsigned long *len);
+ src source string
+ srcl size of source string in octets
+ len pointer to where destination string length in octets
+ will be returned
+
+ This function decodes a BASE64 body part given a source string
+and its length. The decoded body part as a sequence of binary octets
+is returned, and its length is returned in len.
+
+
+char *rfc822_qprint (char *src,unsigned long srcl,unsigned long *len);
+ src source string
+ srcl size of source string in octets
+ len pointer to where destination string length in octets
+ will be returned
+
+ This function decodes a QUOTED-PRINTABLE body part given a source
+string and its length. The decoded body part as an 8-bit character
+string is returned, and its length is returned in len.
+
+ Operating System-Dependent Public Interface
+
+ These functions are in OS-dependent code, and are rewritten each
+time c-client is ported to a new operating system.
+
+
+void rfc822_date (char *date);
+ date buffer to write the date, must be large enough
+
+ This function is called to get the current date and time in an
+RFC 822 format string into the given buffer.
+
+
+void *fs_get (size_t size);
+ size number of octets requested
+
+ This function allocates and returns a block of free storage of
+the specified size. Unlike malloc(), there is no failure return; this
+function must return with the requested storage.
+
+
+void fs_resize (void **block,size_t size);
+ block pointer to pointer to block to be resized
+ size new size in octets
+
+ This function resizes the free storage block, updating the
+pointer if necessary. Unlike realloc(), there is no failure return;
+this function must return with the requested storage.
+
+
+void fs_give (void **block);
+ block pointer to pointer to block to free
+
+ This function releases a block of free storage allocated by
+fs_get(). It also erases the block pointer, so it isn't necessary to
+do this in the application.
+
+
+void fatal (char *string);
+ string message string
+
+ This function is called when an "impossible" error is detected
+and the client wishes to crash. The string should contain a reason.
+
+
+char *strcrlfcpy (char **dst,long *dstl,char *src,long srcl);
+ dst pointer to destination string pointer
+ dstl pointer to destination string size
+ src source strin
+ srcl source string size
+
+ This function is called to copy into a destination string dst of
+size dstl (resized if necessary), a CRLF newline form string from
+local format string src of size srcl.
+
+
+TCPSTREAM *tcp_open (char *host,long port);
+TCPSTREAM *tcp_aopen (char *host,char *service);
+char *tcp_getline (TCPSTREAM *stream);
+long tcp_getbuffer (TCPSTREAM *stream,long size,char *buffer);
+long tcp_soutr (TCPSTREAM *stream,char *string);
+void tcp_close (TCPSTREAM *stream);
+char *tcp_host (TCPSTREAM *stream);
+unsigned long tcp_port (TCPSTREAM *stream);
+char *tcp_localhost (TCPSTREAM *stream);
+
+ These functions are TCP-specific versions of the more general
+net_xxx() functions. These should not be called directly by
+applications.
+
+
+char *tcp_clienthost (char *dst);
+ dst destination string buffer
+
+ This function should be called only by a server called by inetd
+or similar mechanism which maps standard input to a network socket.
+It returns the host name of the other end (e.g. the client of a
+server) using the given string buffer, or NIL if it can't get this
+information.
+
+ Main Program Callbacks
+
+ All applications which use the c-client must have the following
+callbacks to handle events from c-client. Note that in any callback
+which involves a mail stream, the stream is locked and you can not
+recursively call c-client from the callback. This may also be true in
+callbacks which do not have a stream; in general, the rule is "do not
+call c-client, especially any mail_xxx() function, from a c-client
+callback".
+
+
+void mm_flags (MAILSTREAM *stream,unsigned long number);
+ stream stream where event happened
+ number message number
+
+ This function is called when c-client manipulates the flags for
+the given message number. This alerts the application that it may
+need to inspect that message's flags to see if there are any
+interesting changes.
+
+
+void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status);
+ stream stream where event happened
+ mailbox mailbox name for this status
+ status MAILSTATUS structure with message status
+
+ This function is called when c-client reports status of a mailbox
+(generally as the result of a mail_status() function call). The
+returned MAILSTATUS structure has the following members:
+
+long flags; validity flags. These are the same as
+ the SA_xxx option flags in the
+ mail_status() call, and they indicate
+ which of the other members of the
+ MAILSTATUS structure have usable data
+ (i.e. if SA_MESSAGES is not set, do
+ not believe status->messages!!).
+unsigned long messages; number of messages if SA_MESSAGES
+unsigned long recent; number of recent messages if SA_RECENT
+unsigned long unseen; number of unseen messages if SA_UNSEEN
+unsigned long uidnext; next UID to be assigned if SA_UIDNEXT
+unsigned long uidvalidity; UID validity value if SA_UIDVALIDITY
+
+
+void mm_searched (MAILSTREAM *stream,unsigned long number);
+ stream stream where event happened
+ number message number
+
+ This function is called to notify the main program that this
+message number matches a search (generally as the result of a
+mail_search_full() function call).
+
+
+void mm_exists (MAILSTREAM *stream,unsigned long number);
+ stream stream where event happened
+ number message number
+
+ This function is called to notify the main program that there are
+this many messages in the mailbox. It is also used to notify the main
+program of new mail, by announcing a higher number than the main
+program was previously aware.
+
+
+void mm_expunged (MAILSTREAM *stream,unsigned long number);
+ stream stream where event happened
+ number message number
+
+ This function is called to notify the main program that this
+message number has been expunged from the mail file and that all
+subsequent messages are now referenced by a message number one less
+than before. This implicitly decrements the number of messages in the
+mailbox.
+
+
+void mm_list (MAILSTREAM *stream,char delim,char *name,long attrib);
+ stream stream where event happened
+ delim hierarchy delimiter
+ name mailbox name
+ attrib mailbox attributes
+
+ This function is called to notify the main program that this
+mailbox name matches a mailbox listing request (generally as the
+result of a mail_list() function call). The hierarchy delimiter is a
+character that separates out levels of hierarchy in mailbox names.
+The attributes are a bit mask with one of the following:
+ LATT_NOINFERIORS
+ it is not possible for there to be any
+ hierarchy inferiors to this name (that is,
+ this name followed by the hierarchy delimiter
+ and additional name characters).
+ LATT_NOSELECT this is not a mailbox name, just a hierarchy
+ level, and it may not be opened by mail_open()
+ LATT_MARKED this mailbox may have recent messages
+ LATT_UNMARKED this mailbox does not have any recent messages
+
+
+void mm_lsub (MAILSTREAM *stream,char delim,char *name,long attrib);
+ stream stream where event happened
+ delim hierarchy delimiter
+ name mailbox name
+ attrib mailbox attributes
+
+
+ This function is called to notify the main program that this
+mailbox name matches a subscribed mailbox listing request (generally
+as the result of a mail_lsub() function call). The hierarchy
+delimiter is a character that separates out levels of hierarchy in
+mailbox names. The attributes are a bit mask with one of the
+following:
+ LATT_NOINFERIORS
+ it is not possible for there to be any
+ hierarchy inferiors to this name (that is,
+ this name followed by the hierarchy delimiter
+ and additional name characters).
+ LATT_NOSELECT this is not a mailbox name, just a hierarchy
+ level, and it may not be opened by mail_open()
+ LATT_MARKED this mailbox may have recent messages
+ LATT_UNMARKED this mailbox does not have any recent messages
+
+
+void mm_notify (MAILSTREAM *stream,char *string,long errflg);
+ stream stream where event happened
+ string message string
+ errflg message error level
+
+ This function is called to deliver a stream-oriented message
+event. This is the mechanism by which any IMAP response codes for any
+application (e.g. TRYCREATE) are delivered to the application.
+No newline is included in the string, so this function has to output
+its own.
+
+ The message error level is one of the following:
+
+ NIL normal operation. The text is `babble' that may be
+ interesting to the user, e.g. the greeting message
+ from a server.
+
+ WARN A warning event. This event should be displayed to
+ the user. Examples: a mailbox rewrite failed because
+ of disk full, but the previous mailbox contents were
+ recovered.
+
+ ERROR An error event. This event should be displayed to
+ the user, or at least logged someplace. This type of
+ error shouldn't happen, and so should be called to the
+ attention of support staff. Whatever happened has
+ probably disrupted the user's work. Examples: an
+ untagged BAD from an IMAP server.
+
+
+void mm_log (char *string,long errflg);
+ string message string
+ errflg message error level
+
+ This function is called to deliver a log message. No newline is
+included in the string, so this function has to output its own. In
+general, it is intended that these messages are logged someplace, and
+possibly shown to the user.
+
+ The message error level is one of the following:
+
+ NIL normal operation. The text is `babble' that may be
+ interesting to the user, e.g. "Expunged 3 messages".
+
+ PARSE An RFC 822 parsing error. Since bogus headers are
+ all-too-common in the real world, these can often be
+ ignored on the "garbage in, garbage out" princple.
+ However, since surprising results can be yielded when
+ trying to parse garbage, this message should be logged
+ somewhere so it can be figured out what happened.
+
+ WARN A warning event. This event should be displayed to
+ the user. It occurs when an error condition has
+ happened, but c-client knows what to do to recover.
+ Examples: "Can't open read-write, so opening
+ read-only", "Empty mailbox", "Login failed, try
+ again", "Waiting for mailbox to become unlocked",
+ "IMAP protocol error". Although a user should be
+ told about a warning, it's generally not necessary
+ to interrupt the flow of her work (e.g. it's alright
+ to display the warning in a scrolling window, but
+ not necessary to require the user to do anything).
+
+ ERROR An error event. This event should be displayed to
+ the user, or at least logged someplace. This is a
+ serious error condition occured that aborted the
+ requested operation and possibly also aborted the mail
+ stream. This ranges from normal error conditions such
+ as "Can't open mailbox", "too many login failures, go
+ away" to bizarre conditions such as "Apparent new mail
+ appeared in the mailbox that doesn't look like mail,
+ program aborting". Errors must be called to the
+ user's attention, and probably should require some
+ sort of acknowledgement (e.g. answering a modal panel)
+ before the application proceeds.
+
+
+void mm_dlog (char *string);
+ string message string
+
+ This function is called to deliver a debugging telemetry
+message. No newline is included in the string, so this function has
+to output its own. This is called only when debugging is enabled.
+
+
+void mm_login (NETMBX *mb,char *user,char *pwd,long trial);
+ mb parsed mailbox specification
+ user pointer to where to return user name
+ pwd pointer to where to return password
+ trial number of prior login attempts
+
+ This function is called to get a user name and password for the
+given network mailbox. It stores the user name and password in the
+strings pointed to by the appropriate arguments. The trial argument
+is the number of attempts to perform the login and is initially zero
+(e.g. for a default username and password login functionality). It is
+incremented for each subsequent trial until the maximum number of
+trials are made.
+
+
+void mm_critical (MAILSTREAM *stream);
+ stream stream where event happened
+
+ This function is called to alert the application that c-client
+is about to run some critical code on that stream that may result in a
+clobbered mail file if it is interrupted. It may be desirable to
+disable CTRL/C, etc. during this time.
+
+
+void mm_nocritical (MAILSTREAM *stream);
+ stream stream where event happened
+
+ This function is called to alert the application that c-client
+is no longer running critical code on that stream that may result in a
+clobbered mail file if it is interrupted.
+
+
+long mm_diskerror (MAILSTREAM *stream,long errcode,long serious);
+ stream stream where event happened
+ errcode OS error code for disk error
+ serious non-zero if c-client can not undo the operation (and
+ thus must retry to avoid mail file damage)
+
+ This function is called to alert the application that the
+c-client has encountered an unrecoverable write error when trying to
+update the mail file. errcode contains the system error code. If
+serious is non-zero, then it is probable that the disk copy of the
+mailbox has been damaged.
+
+ The return value from this function is the abort flag; if serious
+is zero and the abort flag is non-zero, the operation is aborted. If
+the abort flag is zero or if serious was non-zero, a return from this
+function will retry the failing operation.
+
+
+void mm_fatal (char *string);
+ string message string
+
+ This function is called from the fatal() routine in the
+operating system code to notify the main program that it is about to
+crash. The string contains a reason. At the very minimum, the main
+program should do something like
+ mm_log (string,ERROR);
+and then return. No newline is included in the string, so this
+function has to output its own.
+
+ Driver interface
+
+ When writing a new driver for the c-client, you must provide a
+DRIVER stucture giving a dispatch vector between MAIL and the driver.
+The DRIVER dispatch vector is described in mail.h.
+
+char *name;
+ Name by which the driver is known to c-client.
+
+unsigned long flags;
+ Attribute flags for this driver:
+ DR_DISABLE This driver is currently disabled.
+ DR_LOCAL This driver deals with local mailboxes; if
+ this is off it deals with mailboxes over a
+ network.
+ DR_MAIL This driver supports e-mail messages.
+ DR_NEWS This driver supports netnews messages
+ DR_READONLY This driver only allows read-only access;
+ mail_setflag(), mail_expunge(), etc. are
+ no-ops.
+ DR_NOFAST This driver does not implement mail_fetchfast()
+ in a fast way (e.g. it may have to fetch the
+ entire message text over a network to
+ calculate sizes).
+ DR_NAMESPACE This driver accepts and uses namespace format
+ names.
+ DR_LOWMEM This driver is designed for systems with very
+ limited amounts of memory (e.g. DOS) and
+ support routines called by this driver should
+ try not to use much memory.
+
+DRIVER *next;
+ Pointer to the next driver which this application supports (or NIL if
+this is the last driver). Drivers are lunk together via the mail_link()
+function.
+
+DRIVER *driver_valid (char *mailbox);
+ This function returns a pointer to the driver's DRIVER dispatch
+vector iff this driver accepts the given name as a valid mailbox for this
+driver. Otherwise, it returns the value of the next driver's
+driver_valid() or NIL if there is no next driver. In other words, calling
+driver_valid() for the first driver will return the driver dispatch vector
+for the driver which supports this type of mailbox.
+
+void *driver_parameters (long function,void *value);
+ This function implements mail_parameters() for this driver.
+
+void driver_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+ This function implements mail_scan() for this driver.
+
+void driver_list (MAILSTREAM *stream,char *ref,char *pat);
+ This function implements mail_list() for this driver.
+
+void driver_lsub (MAILSTREAM *stream,char *ref,char *pat);
+ This function implements mail_lsub() for this driver.
+
+long driver_subscribe (MAILSTREAM *stream,char *mailbox);
+ This function implements mail_subscribe() for this driver.
+
+long driver_unsubscribe (MAILSTREAM *stream,char *mailbox);
+ This function implements mail_unsubscribe() for this driver.
+
+long driver_create (MAILSTREAM *stream,char *mailbox);
+ This function implements mail_create() for this driver.
+
+long driver_delete (MAILSTREAM *stream,char *mailbox);
+ This function implements mail_delete() for this driver.
+
+long driver_rename (MAILSTREAM *stream,char *old,char *new);
+ This function implements mail_rename() for this driver.
+
+long driver_status (MAILSTREAM *stream,char *mailbox,long flags);
+ This function implements mail_status() for this driver.
+
+MAILSTREAM *driver_open (MAILSTREAM *stream);
+ This function opens the mailbox identified by the given stream. It
+may use the data on the stream and create additional data on stream->local
+as necessary. It should return the given stream unless it failed to open
+the mailbox, in which case it should return NIL.
+
+void driver_close (MAILSTREAM *stream,long options);
+ This function implements mail_close() for this driver.
+
+void driver_fetchfast (MAILSTREAM *stream,char *sequence,long flags);
+ This function implements mail_fetchfast() for this driver.
+
+void driver_fetchflags (MAILSTREAM *stream,char *sequence,long flags);
+ This function implements mail_fetchflags() for this driver.
+
+ENVELOPE *driver_fetchstructure (MAILSTREAM *stream,unsigned long msgno,
+ BODY **body,long flags);
+ This function implements mail_fetchstructure() for this driver.
+
+char *driver_fetchheader (MAILSTREAM *stream,unsigned long msgno,
+ STRINGLIST *lines,unsigned long *len,long flags);
+ This function implements mail_fetchheader() for this driver.
+
+char *driver_fetchtext (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *len,long flags);
+ This function implements mail_fetchtext() for this driver.
+
+char *driver_fetchbody (MAILSTREAM *stream,unsigned long msgno,char *section,
+ unsigned long *len,long flags);
+ This function implements mail_fetchbody() for this driver.
+
+void driver_setflag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+ This function implements mail_setflag() for this driver.
+
+void driver_clearflag (MAILSTREAM *stream,char *sequence,char *flag,
+ long flags);
+ This function implements mail_clearflag() for this driver.
+
+void driver_search (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,
+ long flags);
+ This function implements mail_search() for this driver.
+
+unsigned long *driver_sort (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
+ SORTPGM *pgm,long flags);
+ This function implements mail_sort() for this driver.
+
+void *driver_thread (MAILSTREAM *stream,char *seq,long function,long flag);
+ This dispatch is reserved for a future threading capability.
+
+long driver_ping (MAILSTREAM *stream);
+ This function implements mail_ping() for this driver.
+
+void driver_check (MAILSTREAM *stream);
+ This function implements mail_check() for this driver.
+
+void driver_expunge (MAILSTREAM *stream);
+ This function implements mail_expunge() for this driver.
+
+long driver_copy (MAILSTREAM *stream,char *sequence,char *mailbox,
+ long options);
+ This function implements mail_copy() for this driver.
+
+long driver_append (MAILSTREAM *stream,char *mailbox,char *flags,char *date,
+ STRING *message);
+ This function implements mail_append() for this driver.
+
+void driver_gc (MAILSTREAM *stream,long gcflags);
+ This function implements mail_gc() for this driver.
+
+ Driver Support Functions
+
+void mail_searched (MAILSTREAM *stream,unsigned long msgno);
+ stream stream where event happened
+ msgno message number
+
+ This function is called by the driver to notify c-client that this
+message number matches a search. It invokes the main program's
+mm_searched() function.
+
+void mail_exists (MAILSTREAM *stream,unsigned long nmsgs);
+ stream stream where event happened
+ nmsgs number of messages
+
+ This function is called by the driver to notify c-client that this
+message number exists (i.e. there are this many messages in the mailbox).
+It invokes the main program's mm_exists() function.
+
+void mail_recent (MAILSTREAM *stream,unsigned long recent);
+ stream stream where event happened
+ recent number of messages
+
+ This function is called by the driver to notify c-client that this
+many messages are "recent" (i.e. arrived in the mailbox since the previous
+time the mailbox was opened).
+
+void mail_expunged (MAILSTREAM *stream,unsigned long msgno);
+ stream stream where event happened
+ msgno number of messages
+
+ This function is called by the driver to notify MAIL that this
+message number has been expunged from the mail file and that all subsequent
+messages are now referenced by a message number one less than before. It
+invokes the main program's mm_expunged() function.
+
+void mail_lock (MAILSTREAM *stream);
+ stream stream where event happened
+ This function sets the stream lock. It is an error to set the stream
+lock if the stream is already locked.
+
+ This is mainly used to catch errors due to a callback function
+(e.g. mm_exists) inadvertantly recursing back to the MAIL routines and
+establishing an infinite recursion. Normally, drivers will set the lock
+prior to calling one of the callback functions above or, more likely, in
+the beginning of the driver's non-reentrant "do operation" section. In the
+IMAP4 driver, the stream lock is set when entering imap_send() and cleared
+on exit.
+
+void mail_unlock (MAILSTREAM *stream);
+ stream stream where event happened
+
+ This function releases the stream lock. It is an error to release the
+stream lock if the stream is not locked.
diff --git a/imap/docs/locking.txt b/imap/docs/locking.txt
new file mode 100644
index 00000000..32ff6c66
--- /dev/null
+++ b/imap/docs/locking.txt
@@ -0,0 +1,417 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+ UNIX Advisory File Locking Implications on c-client
+ Mark Crispin, 28 November 1995
+
+
+ THIS DOCUMENT HAS BEEN UPDATED TO REFLECT THE FACT THAT
+ LINUX SUPPORTS BOTH flock() AND fcntl() AND THAT OSF/1
+ HAS BEEN BROKEN SO THAT IT ONLY SUPPORTS fcntl().
+ -- JUNE 15, 2004
+
+ THIS DOCUMENT HAS BEEN UPDATED TO REFLECT THE CODE IN THE
+ IMAP-4 TOOLKIT AS OF NOVEMBER 28, 1995. SOME STATEMENTS
+ IN THIS DOCUMENT DO NOT APPLY TO EARLIER VERSIONS OF THE
+ IMAP TOOLKIT.
+
+INTRODUCTION
+
+ Advisory locking is a mechanism by which cooperating processes
+can signal to each other their usage of a resource and whether or not
+that usage is critical. It is not a mechanism to protect against
+processes which do not cooperate in the locking.
+
+ The most basic form of locking involves a counter. This counter
+is -1 when the resource is available. If a process wants the lock, it
+executes an atomic increment-and-test-if-zero. If the value is zero,
+the process has the lock and can execute the critical code that needs
+exclusive usage of a resource. When it is finished, it sets the lock
+back to -1. In C terms:
+
+ while (++lock) /* try to get lock */
+ invoke_other_threads (); /* failed, try again */
+ .
+ . /* critical code here */
+ .
+ lock = -1; /* release lock */
+
+ This particular form of locking appears most commonly in
+multi-threaded applications such as operating system kernels. It
+makes several presumptions:
+ (1) it is alright to keep testing the lock (no overflow)
+ (2) the critical resource is single-access only
+ (3) there is shared writeable memory between the two threads
+ (4) the threads can be trusted to release the lock when finished
+
+ In applications programming on multi-user systems, most commonly
+the other threads are in an entirely different process, which may even
+be logged in as a different user. Few operating systems offer shared
+writeable memory between such processes.
+
+ A means of communicating this is by use of a file with a mutually
+agreed upon name. A binary semaphore can be passed by means of the
+existance or non-existance of that file, provided that there is an
+atomic means to create a file if and only if that file does not exist.
+In C terms:
+
+ /* try to get lock */
+ while ((fd = open ("lockfile",O_WRONLY|O_CREAT|O_EXCL,0666)) < 0)
+ sleep (1); /* failed, try again */
+ close (fd); /* got the lock */
+ .
+ . /* critical code here */
+ .
+ unlink ("lockfile"); /* release lock */
+
+ This form of locking makes fewer presumptions, but it still is
+guilty of presumptions (2) and (4) above. Presumption (2) limits the
+ability to have processes sharing a resource in a non-conflicting
+fashion (e.g. reading from a file). Presumption (4) leads to
+deadlocks should the process crash while it has a resource locked.
+
+ Most modern operating systems provide a resource locking system
+call that has none of these presumptions. In particular, a mechanism
+is provided for identifying shared locks as opposed to exclusive
+locks. A shared lock permits other processes to obtain a shared lock,
+but denies exclusive locks. In other words:
+
+ current state want shared want exclusive
+ ------------- ----------- --------------
+ unlocked YES YES
+ locked shared YES NO
+ locked exclusive NO NO
+
+ Furthermore, the operating system automatically relinquishes all
+locks held by that process when it terminates.
+
+ A useful operation is the ability to upgrade a shared lock to
+exclusive (provided there are no other shared users of the lock) and
+to downgrade an exclusive lock to shared. It is important that at no
+time is the lock ever removed; a process upgrading to exclusive must
+not relenquish its shared lock.
+
+ Most commonly, the resources being locked are files. Shared
+locks are particularly important with files; multiple simultaneous
+processes can read from a file, but only one can safely write at a
+time. Some writes may be safer than others; an append to the end of
+the file is safer than changing existing file data. In turn, changing
+a file record in place is safer than rewriting the file with an
+entirely different structure.
+
+
+FILE LOCKING ON UNIX
+
+ In the oldest versions of UNIX, the use of a semaphore lockfile
+was the only available form of locking. Advisory locking system calls
+were not added to UNIX until after the BSD vs. System V split. Both
+of these system calls deal with file resources only.
+
+ Most systems only have one or the other form of locking. AIX
+and newer versions of OSF/1 emulate the BSD form of locking as a jacket
+into the System V form. Ultrix and Linux implement both forms.
+
+BSD
+
+ BSD added the flock() system call. It offers capabilities to
+acquire shared lock, acquire exclusive lock, and unlock. Optionally,
+the process can request an immediate error return instead of blocking
+when the lock is unavailable.
+
+
+FLOCK() BUGS
+
+ flock() advertises that it permits upgrading of shared locks to
+exclusive and downgrading of exclusive locks to shared, but it does so
+by releasing the former lock and then trying to acquire the new lock.
+This creates a window of vulnerability in which another process can
+grab the exclusive lock. Therefore, this capability is not useful,
+although many programmers have been deluded by incautious reading of
+the flock() man page to believe otherwise. This problem can be
+programmed around, once the programmer is aware of it.
+
+ flock() always returns as if it succeeded on NFS files, when in
+fact it is a no-op. There is no way around this.
+
+ Leaving aside these two problems, flock() works remarkably well,
+and has shown itself to be robust and trustworthy.
+
+SYSTEM V/POSIX
+
+ System V added new functions to the fnctl() system call, and a
+simple interface through the lockf() subroutine. This was
+subsequently included in POSIX. Both offer the facility to apply the
+lock to a particular region of the file instead of to the entire file.
+lockf() only supports exclusive locks, and calls fcntl() internally;
+hence it won't be discussed further.
+
+ Functionally, fcntl() locking is a superset of flock(); it is
+possible to implement a flock() emulator using fcntl(), with one minor
+exception: it is not possible to acquire an exclusive lock if the file
+is not open for write.
+
+ The fcntl() locking functions are: query lock station of a file
+region, lock/unlock a region, and lock/unlock a region and block until
+have the lock. The locks may be shared or exclusive. By means of the
+statd and lockd daemons, fcntl() locking is available on NFS files.
+
+ When statd is started at system boot, it reads its /etc/state
+file (which contains the number of times it has been invoked) and
+/etc/sm directory (which contains a list of all remote sites which are
+client or server locking with this site), and notifies the statd on
+each of these systems that it has been restarted. Each statd then
+notifies the local lockd of the restart of that system.
+
+ lockd receives fcntl() requests for NFS files. It communicates
+with the lockd at the server and requests it to apply the lock, and
+with the statd to request it for notification when the server goes
+down. It blocks until all these requests are completed.
+
+ There is quite a mythos about fcntl() locking.
+
+ One religion holds that fcntl() locking is the best thing since
+sliced bread, and that programs which use flock() should be converted
+to fcntl() so that NFS locking will work. However, as noted above,
+very few systems support both calls, so such an exercise is pointless
+except on Ultrix and Linux.
+
+ Another religion, which I adhere to, has the opposite viewpoint.
+
+
+FCNTL() BUGS
+
+ For all of the hairy code to do individual section locking of a
+file, it's clear that the designers of fcntl() locking never
+considered some very basic locking operations. It's as if all they
+knew about locking they got out of some CS textbook with not
+investigation of real-world needs.
+
+ It is not possible to acquire an exclusive lock unless the file
+is open for write. You could have append with shared read, and thus
+you could have a case in which a read-only access may need to go
+exclusive. This problem can be programmed around once the programmer
+is aware of it.
+
+ If the file is opened on another file designator in the same
+process, the file is unlocked even if no attempt is made to do any
+form of locking on the second designator. This is a very bad bug. It
+means that an application must keep track of all the files that it has
+opened and locked.
+
+ If there is no statd/lockd on the NFS server, fcntl() will hang
+forever waiting for them to appear. This is a bad bug. It means that
+any attempt to lock on a server that doesn't run these daemons will
+hang. There is no way for an application to request flock() style
+``try to lock, but no-op if the mechanism ain't there''.
+
+ There is a rumor to the effect that fcntl() will hang forever on
+local files too if there is no local statd/lockd. These daemons are
+running on mailer.u, although they appear not to have much CPU time.
+A useful experiment would be to kill them and see if imapd is affected
+in any way, but I decline to do so without an OK from UCS! ;-) If
+killing statd/lockd can be done without breaking fcntl() on local
+files, this would become one of the primary means of dealing with this
+problem.
+
+ The statd and lockd daemons have quite a reputation for extreme
+fragility. There have been numerous reports about the locking
+mechanism being wedged on a systemwide or even clusterwide basis,
+requiring a reboot to clear. It is rumored that this wedge, once it
+happens, also blocks local locking. Presumably killing and restarting
+statd would suffice to clear the wedge, but I haven't verified this.
+
+ There appears to be a limit to how many locks may be in use at a
+time on the system, although the documentation only mentions it in
+passing. On some of their systems, UCS has increased lockd's ``size
+of the socket buffer'', whatever that means.
+
+C-CLIENT USAGE
+
+ c-client uses flock(). On System V systems, flock() is simulated
+by an emulator that calls fcntl().
+
+
+BEZERK AND MMDF
+
+ Locking in the traditional UNIX formats was largely dictated by
+the status quo in other applications; however, additional protection
+is added against inadvertantly running multiple instances of a
+c-client application on the same mail file.
+
+ (1) c-client attempts to create a .lock file (mail file name with
+``.lock'' appended) whenever it reads from, or writes to, the mail
+file. This is an exclusive lock, and is held only for short periods
+of time while c-client is actually doing the I/O. There is a 5-minute
+timeout for this lock, after which it is broken on the presumption
+that it is a stale lock. If it can not create the .lock file due to
+an EACCES (protection failure) error, it once silently proceeded
+without this lock; this was for systems which protect /usr/spool/mail
+from unprivileged processes creating files. Today, c-client reports
+an error unless it is built otherwise. The purpose of this lock is to
+prevent against unfavorable interactions with mail delivery.
+
+ (2) c-client applies a shared flock() to the mail file whenever
+it reads from the mail file, and an exclusive flock() whenever it
+writes to the mail file. This lock is freed as soon as it finishes
+reading. The purpose of this lock is to prevent against unfavorable
+interactions with mail delivery.
+
+ (3) c-client applies an exclusive flock() to a file on /tmp
+(whose name represents the device and inode number of the file) when
+it opens the mail file. This lock is maintained throughout the
+session, although c-client has a feature (called ``kiss of death'')
+which permits c-client to forcibly and irreversibly seize the lock
+from a cooperating c-client application that surrenders the lock on
+demand. The purpose of this lock is to prevent against unfavorable
+interactions with other instances of c-client (rewriting the mail
+file).
+
+ Mail delivery daemons use lock (1), (2), or both. Lock (1) works
+over NFS; lock (2) is the only one that works on sites that protect
+/usr/spool/mail against unprivileged file creation. Prudent mail
+delivery daemons use both forms of locking, and of course so does
+c-client.
+
+ If only lock (2) is used, then multiple processes can read from
+the mail file simultaneously, although in real life this doesn't
+really change things. The normal state of locks (1) and (2) is
+unlocked except for very brief periods.
+
+
+TENEX AND MTX
+
+ The design of the locking mechanism of these formats was
+motivated by a design to enable multiple simultaneous read/write
+access. It is almost the reverse of how locking works with
+bezerk/mmdf.
+
+ (1) c-client applies a shared flock() to the mail file when it
+opens the mail file. It upgrades this lock to exclusive whenever it
+tries to expunge the mail file. Because of the flock() bug that
+upgrading a lock actually releases it, it will not do so until it has
+acquired an exclusive lock (2) first. The purpose of this lock is to
+prevent against expunge taking place while some other c-client has the
+mail file open (and thus knows where all the messages are).
+
+ (2) c-client applies a shared flock() to a file on /tmp (whose
+name represents the device and inode number of the file) when it
+parses the mail file. It applies an exclusive flock() to this file
+when it appends new mail to the mail file, as well as before it
+attempts to upgrade lock (1) to exclusive. The purpose of this lock
+is to prevent against data being appended while some other c-client is
+parsing mail in the file (to prevent reading of incomplete messages).
+It also protects against the lock-releasing timing race on lock (1).
+
+OBSERVATIONS
+
+ In a perfect world, locking works. You are protected against
+unfavorable interactions with the mailer and against your own mistake
+by running more than one instance of your mail reader. In tenex/mtx
+formats, you have the additional benefit that multiple simultaneous
+read/write access works, with the sole restriction being that you
+can't expunge if there are any sharers of the mail file.
+
+ If the mail file is NFS-mounted, then flock() locking is a silent
+no-op. This is the way BSD implements flock(), and c-client's
+emulation of flock() through fcntl() tests for NFS files and
+duplicates this functionality. There is no locking protection for
+tenex/mtx mail files at all, and only protection against the mailer
+for bezerk/mmdf mail files. This has been the accepted state of
+affairs on UNIX for many sad years.
+
+ If you can not create .lock files, it should not affect locking,
+since the flock() locks suffice for all protection. This is, however,
+not true if the mailer does not check for flock() locking, or if the
+the mail file is NFS-mounted.
+
+ What this means is that there is *no* locking protection at all
+in the case of a client using an NFS-mounted /usr/spool/mail that does
+not permit file creation by unprivileged programs. It is impossible,
+under these circumstances, for an unprivileged program to do anything
+about it. Worse, if EACCES errors on .lock file creation are no-op'ed
+, the user won't even know about it. This is arguably a site
+configuration error.
+
+ The problem with not being able to create .lock files exists on
+System V as well, but the failure modes for flock() -- which is
+implemented via fcntl() -- are different.
+
+ On System V, if the mail file is NFS-mounted and either the
+client or the server lacks a functioning statd/lockd pair, then the
+lock attempt would have hung forever if it weren't for the fact that
+c-client tests for NFS and no-ops the flock() emulator in this case.
+Systemwide or clusterwide failures of statd/lockd have been known to
+occur which cause all locks in all processes to hang (including
+local?). Without the special NFS test made by c-client, there would
+be no way to request BSD-style no-op behavior, nor is there any way to
+determine that this is happening other than the system being hung.
+
+ The additional locking introduced by c-client was shown to cause
+much more stress on the System V locking mechanism than has
+traditionally been placed upon it. If it was stressed too far, all
+hell broke loose. Fortunately, this is now past history.
+
+TRADEOFFS
+
+ c-client based applications have a reasonable chance of winning
+as long as you don't use NFS for remote access to mail files. That's
+what IMAP is for, after all. It is, however, very important to
+realize that you can *not* use the lock-upgrade feature by itself
+because it releases the lock as an interim step -- you need to have
+lock-upgrading guarded by another lock.
+
+ If you have the misfortune of using System V, you are likely to
+run into problems sooner or later having to do with statd/lockd. You
+basically end up with one of three unsatisfactory choices:
+ 1) Grit your teeth and live with it.
+ 2) Try to make it work:
+ a) avoid NFS access so as not to stress statd/lockd.
+ b) try to understand the code in statd/lockd and hack it
+ to be more robust.
+ c) hunt out the system limit of locks, if there is one,
+ and increase it. Figure on at least two locks per
+ simultaneous imapd process and four locks per Pine
+ process. Better yet, make the limit be 10 times the
+ maximum number of processes.
+ d) increase the socket buffer (-S switch to lockd) if
+ it is offered. I don't know what this actually does,
+ but giving lockd more resources to do its work can't
+ hurt. Maybe.
+ 3) Decide that it can't possibly work, and turn off the
+ fcntl() calls in your program.
+ 4) If nuking statd/lockd can be done without breaking local
+ locking, then do so. This would make SVR4 have the same
+ limitations as BSD locking, with a couple of additional
+ bugs.
+ 5) Check for NFS, and don't do the fcntl() in the NFS case.
+ This is what c-client does.
+
+ Note that if you are going to use NFS to access files on a server
+which does not have statd/lockd running, your only choice is (3), (4),
+or (5). Here again, IMAP can bail you out.
+
+ These problems aren't unique to c-client applications; they have
+also been reported with Elm, Mediamail, and other email tools.
+
+ Of the other two SVR4 locking bugs:
+
+ Programmer awareness is necessary to deal with the bug that you
+can not get an exclusive lock unless the file is open for write. I
+believe that c-client has fixed all of these cases.
+
+ The problem about opening a second designator smashing any
+current locks on the file has not been addressed satisfactorily yet.
+This is not an easy problem to deal with, especially in c-client which
+really doesn't know what other files/streams may be open by Pine.
+
+ Aren't you so happy that you bought an System V system?
diff --git a/imap/docs/md5.txt b/imap/docs/md5.txt
new file mode 100644
index 00000000..c43f1023
--- /dev/null
+++ b/imap/docs/md5.txt
@@ -0,0 +1,91 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+ MD5 Based Authentication
+ Mark Crispin
+ 1 November 1999
+
+
+ The IMAP toolkit makes available two MD5 based authentication
+mechanisms, CRAM-MD5 and APOP. CRAM-MD5 is described in RFC 2195, and
+is a SASL (RFC 2222) authentication mechanism. APOP is described in
+RFC 1939, the standard document for the POP3 protocol.
+
+ These mechanisms use the same general idea. The server issues a
+challenge; the client responds with an MD5 checksum of the challenge
+plus the password; the server in compares the client's response with
+its own calculated value of the checksum. If the client's response
+matches the server's calulated value, the client is authenticated.
+
+ Unlike plaintext passwords, this form of authentication is
+believed to be secure against the session being monitored; "sniffing"
+the session will not disclose the password nor will it provide usable
+information to authenticate in another session without knowing the
+password.
+
+ The key disadvantage with this form of authentication is that the
+server must know a plaintext form of the password. In traditional
+UNIX authentication, the server only knows an encrypted form of the
+password. Consequently, the authentication database for this form of
+authentication must be kept strictly confidential; a bad guy who
+acquires access to this database can access any account in the
+database.
+
+ CRAM-MD5 client support is implemented unconditionally; any
+client application built with the IMAP toolkit will use CRAM-MD5 with
+any server which advertises CRAM-MD5 SASL support.
+
+ CRAM-MD5 and APOP server support is implemented if, and only if,
+the CRAM-MD5 authentication database exists. By default, the CRAM-MD5
+authentication database is in a UNIX file called
+ /etc/cram-md5.pwd
+It is recommended that this file be protected 0400.
+
+ NOTE: FAILURE TO PROTECT THIS FILE AGAINST UNAUTHORIZED
+ ACCESS WILL COMPROMSE CRAM-MD5 AND APOP AUTHENTICATION
+ FOR ALL USERS LISTED IN THIS DATABASE.
+
+ If the CRAM-MD5 authentication database exists, then plaintext
+password authentication (e.g. the LOGIN command) will also use the
+CRAM-MD5 passwords instead of UNIX passwords. Alternatively, it is
+possible to build the IMAP toolkit so that plaintext password
+authentication is disabled entirely, by using PASSWDTYPE=nul, e.g.
+ make aix PASSWDTYPE=nul
+
+
+ The CRAM-MD5 authentication database file consists of a series of
+text lines, consisting of a UNIX user name, a single tab, and the
+password. A line starting with a "#" character is ignored, as are any
+lines which are not in valid format. For example:
+
+------------------------------Sample------------------------------
+# CRAM-MD5 authentication database
+# Entries are in form <user><tab><password>
+# Lines starting with "#" are comments
+
+bill hubba-hubba
+hillary nysenator
+monica beret
+tripp wired
+kenstarr inquisitor
+reno waco
+jessie thebody
+billgates ruleworld
+------------------------------Sample------------------------------
+
+ Every entry in the CRAM-MD5 authentication database must have a
+corresponding entry in the /etc/passwd file. It is STRONGLY
+RECOMMENDED that the CRAM-MD5 password NOT be the same as the
+/etc/passwd password. It is permitted for the /etc/passwd password to
+be disabled; /etc/passwd is just used to get the UID, GID, and home
+directory information.
diff --git a/imap/docs/mixfmt.txt b/imap/docs/mixfmt.txt
new file mode 100644
index 00000000..afe3f940
--- /dev/null
+++ b/imap/docs/mixfmt.txt
@@ -0,0 +1,363 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+Last update: 18 December 2006
+
+INTRODUCTION
+
+This file is the descendant of a design document used to specify the
+mix format. An attempt is being made to keep this document more or
+less current with the way the mix format actually works.
+
+
+1. Mix mailbox naming
+
+Mailbox names correspond to directory names; thus mix format mailboxes
+are "dual-use" (lack both \NoInferiors and \NoSelect). This will
+satisfy some long-standing requests.
+
+
+2. Mailbox files
+
+A mix format mailbox is a directory with regular files with filenames
+of:
+ .mixmeta mailbox metadata file
+ .mixindex message index file (message static data)
+ .mixstatus message status file (message dynamic data)
+ .mix######## (where ######### is a <hex8>) secondary message
+ data files.
+ .mix primary message data file (used in experimental
+ versions, supported for compatibility only)
+
+2.1 Metadata, index, and status files
+
+The mailbox metadata, index, and status files contain a sequence of
+CRLF-terminated lines. These files have an update sequence, which is
+a strictly-ascending sequence value. Any time the file is changed,
+the update sequence is increased; this allows easy detection of
+whether the file has been changed by another process. For now, this
+update sequence is a modseq (see below).
+
+2.1.1 Metadata file
+
+The mailbox metadata file is called ".mixmeta". It contains a series
+of CRLF-terminated lines. The first character of the line is a key that
+identifies the payload of the line, and the remainder of the line is the
+payload.
+ Key Payload
+ --- -------
+ S <hex8> ;; update sequence
+ V <hex8> ;; UIDVALIDITY
+ L <hex8> ;; UIDLAST
+ N <hex8> ;; current new message file
+ K [atom 0*(SP atom)] ;; keyword list
+
+All other keys are reserved for future assignment and must be ignored
+(and may be discarded) by software which does not recognize them. The
+mailbox metadata file is rewritten as part of new mail delivery (so
+APPENDUID/COPYUID can work) and when new keywords are added.
+
+2.1.2 Message static index file
+
+The mailbox message static index file is called ".mixindex". It contains
+a series of CRLF-terminated lines. The first character of the line is a
+key that identifies the payload of the line, and the remainder of the line
+is the payload.
+ Key Payload
+ --- -------
+ S <hex8> ;; update sequence
+ : <uid>:<date>:<size>:<file>:<pos>:<isiz>:<hsiz>
+ ;; per-message record
+
+The per-message records contain the following data:
+ <uid> = <hex8> ;; message UID
+ <date> = <yyyymmddhhmmss+zzzz> ;; internal date
+ <size> = <hex8> ;; rfc822.size
+ <file> = <hex8> ;; message data file (0 = .mix file)
+ <pos> = <hex8> ;; message position in file
+ <isiz> = <hex8> ;; message internal data size
+ <hsiz> = <hex8> ;; header size (offset to body)
+
+All other keys, and subsequent fields in per-message records, are
+reserved for future assignment and must be ignored (and may be
+discarded) by software which does not recognize them. The mailbox
+metadata file is appended by new mail delivery and rewritten by
+expunge "burping", and otherwise is not altered.
+
+2.1.3 Message dynamic status file
+
+The mailbox message dynamic status file is called ".mixstatus". It contains
+a series of CRLF-terminated lines. The first character of the line is a
+key that identifies the payload of the line, and the remainder of the line
+is the payload.
+ Key Payload
+ --- -------
+ S <hex8> ;; update sequence
+ : <uid>:<uf>:<sf>:<mod>: ;; per-message record
+
+The per-message records contain the following data:
+ <uid> = <hex8> ;; message UID
+ <keys> = <hex8> ;; keyword flags
+ <flag> = <hex4> ;; system flags
+ <mod> = <hex8> ;; date/time last modified (modseq)
+
+All other keys, and subsequent fields in per-message records, are
+reserved for future assignment and must be ignored (and may be
+discarded) by software which does not recognize them. The mailbox
+dynamic idex file is rewritten by flag changes (or any future change
+that alters dynamic data) and is re-read when a session sees that the
+mtime has changed (atime and ctime are not used).
+
+The modseq is an unsigned 32-bit date/time, along with a guarantee
+that this value can not go backwards. It currently corresponds to the
+time from time(); however, since it is unsigned, it won't run out until
+the year 2106. In the future, this may be used as a basic for implementing
+the IMAP CONDSTORE extension.
+
+2.2 Message data files
+
+A mix message file is a regular file with filename starting with
+".mix" followed by a <hex8> suffix which indicates the file number. It
+contains a series of CRLF-terminated lines. By special dispensation, the
+filename ".mix" is used for file number 0, which was used in experimental
+versions of mix as a "primary" file (this concept no longer exists).
+
+A file number is set to the current modseq when it is created. If a copy
+or append causes the file to exceed the compiled-in file size limit, a new
+file is started and the metadata is updated accordingly.
+
+Preceeding each message is per-message record with the following format:
+ Key Payload
+ --- -------
+ ;; per-message record
+ : :<code>:<uid>:<date>:<size>:
+
+The per-message records contain the following data:
+ <code> = "msg" ;; fixed code
+ <uid> = <hex8> ;; message UID
+ <date> = <yyyymmddhhmmss+zzzz> ;; internal date
+ <size> = <hex8> ;; rfc822.size
+The message data begins on the next line
+
+Subsequent fields are reserved for future assignment and must be ignored.
+
+
+3. New mail delivery
+
+To deliver a new message, it is necessary to share lock the destination
+metadata file, then get an exclusive lock on the destination index and
+status files. Once this is done, the new message data is appended to the
+new message file. The metadata (UIDLAST value), index, and status
+files are all updated to add the new message.
+
+Then all the destination mailbox files are closed.
+
+
+4. Mailbox pinging
+
+The index and status files are share locked. Initially, sequences are
+remembered as zero, so at open time they are always "altered".
+
+The sequence from the index file is checked; if it is altered the index
+file is read and processed as follows:
+ . If expunge is permitted, then any messages that are not in the index
+ are reported as having been expunged via mm_expunged().
+ . new messages are announced via mm_exists()/mm_recent().
+
+Next, the sequence from the status file is checked. If it is altered,
+the status file is read and the status updated for any message which is
+new or has an altered modseq in the status file. Altered modseq messages
+are announced via mm_flags().
+
+Then the index and status files are closed.
+
+
+4. Flag alteration
+
+The status file is exclusive locked.
+
+The sequence from the status file is checked. If it is altered, the
+status file is read and the status updated for any message which is
+new or has an altered modseq in the status file. Altered modseq
+messages are announced via mm_flags().
+
+The alterations are then applied for all requested messages, updating
+the modseq for each requestedmessage which changes flags as a result
+of the alteration (alterations which do not result in a change do not
+alter the modseq). Then the status file is rewritten with a new
+sequence, but only if flags of at least one message was changed.
+
+Then the status file is closed.
+
+
+5. Checkpoint and expunge
+
+Checkpoint is identical to expunge, however it skips the step of expunging
+deleted messages.
+
+The index and status files are locked exclusive. If expunging, all
+deleted messages are expunged from the index and announced via
+mm_expunged(). The message data is notremoved at this time.
+
+If a checkpoint was requested, or if any messages were expunged, or if
+it remembered that a "burp" was needed, then:
+ . the metadata file is locked exclusive. If this fails, remember that
+ a burp is needed. Otherwise perform a burp:
+ . calculate the file byte ranges occupied by expunged messages
+ . for each file needing "burping", open and slide down subsequent file
+ data on top of the expunged messages
+ . update the index and status files
+
+Then the index and status files are closed.
+
+5.1 More details on expunging and "burping"
+
+Shared expunge presents a problem due to the requirements of the IMAP
+protocol. You can't "burp" away a message until you are certain that
+no sharers have a pointer to any longer. Consequently, for the nonce
+"burping" out expunged data be defered to an exclusive expunge as in
+mbx format.
+
+If shared burping is ever implemented, then care will be needed not to
+burp data that a session still relies upon. It's easy enough to burp
+the index files; just create new index files, deleting the old, and
+require that you look for a new one appearing at mailbox ping time
+(when it's safe). The data files are a problem, since we
+intentionally don't want to keep them open and do want to avoid quota
+problems by overwriting in place. Also, when you burp you have to
+change the pointers in the index file.
+
+Bottom line: shared burping is too hairy right now, so the first
+version will do exclusive-only burping and not worry about it. If
+shared burping is really needed, then that routine will need to be
+rewritten.
+
+Shared burping has been a problem for every other IMAP server. Most
+get it wrong, and cause terrible confusion to clients (including
+client crashes).
+
+
+6. Message data file file roll out strategy
+
+The current new message file is finalized, and a new one started, when
+an append or copy is done that would cause the file to grow to larger
+than a preconfigured size (MIXDATAROLL). A multi-message copy or
+append is written into its entirety to a single new message file. In
+the case of multi-copy, the new message file is switched when the sum
+of the sizes of all messages to be copied would cause the current new
+message file to exceed MIXDATAROLL. In the case of multi-append, only
+the first message is considered; this is due to technical limitations.
+
+7. Error detection
+
+Mix detects bad data in the metadata, index, and status files; and
+declares the stream dead. It does not unilaterally reassign
+UIDVALIDITY the way that the flat file formats do.
+
+When mix reads a header from the message file, it also reads the
+per-message record and verifies that there is a per-message record there.
+This is a simple test for message file corruption. It doesn't declare
+the stream dead; it simply issues an error message and returns a
+zero-length string for the message header. This makes it possible for
+the user to fix the mailbox simply by deleting and expunging any messages
+that are in this state.
+
+
+8. Reconstruct tool
+
+[None of this is implemented yet.]
+
+The layout of these files is designed to make the reconstruct tool be
+as simple as possible. Much of the need for the reconstruct tool is
+eliminated since the mix format has a much more limited scope of
+writing than the flat file formats; thus there is "less collateral
+damage."
+
+If the metadata file is lost or corrupted, then all keywords are lost;
+if the mailbox has any keywords used in the .mixstatus file, it'll be
+necessary to create some placeholder names. Otherwise, a new
+UIDVALIDITY can be assigned, and a good UIDLAST value calculated by
+the reconstruct tool. Since this file is very small, it's not likely
+to be damaged.
+
+If the index file is lost or corrupted, it is possible to reconstruct
+it with no loss by reading all the data files. However, this could
+cause expunged but not yet burped messages to reappear.
+
+If the status file is lost or corrupted, then flags are lost and
+will revert to a default state of no flags set. Just deleting the
+corrupted file is good enough.
+
+The reconstruct tool can use the per-message record in the message
+file to locate messages if the recorded sizes and/or messages are
+corrupt. If that happens, it will need to rebuild the index file
+(with associated changes to the metadata file to change the
+UIDVALIDITY). That should probably be a manual operation and not be
+part of the default operation or auto-reconstruct.
+
+
+9. Locking strategy
+
+The mix format does not use the traditional c-client /tmp file locking.
+
+The metadata file is open and locked whenever the mailbox is open.
+Normally this is a shared lock, but it will be upgraded to exclusive
+if the mailbox is expunged. As a guard (since there is no true
+lock-upgrade/downgrade on UNIX), the index exclusive lock must be
+acquired first before upgrading to exclusive.
+
+The index file is shared locked when reading the index, and exclusive
+locked (and read) when appending new messages to the index or when
+expunging (note that expunging also requires an exclusive lock on
+metadata). Normally, the index file is not open or locked.
+
+The status file is shared locked when reading status, and exclusive
+locked (and read) when updating status. Normally, the status file is
+not open or locked.
+
+It isn't necessary to lock any of the data files as long as we only
+have exclusive burping.
+
+
+10. Memory usage
+
+The mix format returns a file stringstruct, which is the modern
+c-client behavior. This prevents imapd from growing to enormous sizes
+due to a godzillagram (how it affects other programs depends upon what
+they do with the returned stringstruct).
+
+
+11. Future extensions
+
+Cached ENVELOPE, BODYSTRUCTURE. Cyrus does, and this will eliminate
+most of the reason to access the data files. Possibly cached overviews,
+ala NNTP, instead?
+
+
+Support for ANNOTATION.
+
+
+12. RENAME issues
+
+Mix currently makes no attempt to address the IMAP RENAME problem.
+This occurs when a mailbox is deleted, and another mailbox is renamed
+with that name in place, no attempt is made to reassign UIDVALIDITY
+for this mailbox and all the inferior mailboxes. This potentially can
+cause problems for a disconnected-use client that has cached status
+for the old mailbox which had that name.
+
+The RENAME problem is a well known flaw in the IMAP protocol. Few
+servers correctly handle it (among other things, not only do all the
+UIDVALIDITY values have to be changed but this has to be done
+atomically!). It was a mistake to add RENAME into IMAP, but it's much
+too late to remove it now.
diff --git a/imap/docs/naming.txt b/imap/docs/naming.txt
new file mode 100644
index 00000000..b0b484ae
--- /dev/null
+++ b/imap/docs/naming.txt
@@ -0,0 +1,143 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+ Mailbox Name Conventions
+ Mark Crispin
+ 5 October 2005
+
+
+Please refer to the file drivers.txt for related information.
+
+
+I. Special names
+
+Special names appear by themselves.
+
+I.a. INBOX
+
+The name INBOX is special and refers to primary incoming message
+mailbox on the local system.
+
+
+I.b. #mhinbox (UNIX only)
+
+The name #mhinbox is special and refers to the primary incoming mh
+format mailbox on the local system. Don't worry about this if you
+don't know what mh format is.
+
+
+II. Special prefixes
+
+All names which start with a "#" have a "special prefix" which
+identifies an alternative namespace. Special prefixes appear in front
+of some additional text which constitutes a suffix.
+
+II.a. #mh/ (UNIX only)
+
+The prefix #mh/ is special and refers to the mh format mailbox named
+with the suffix. For example, #mh/foo refers to the mh format mailbox
+named foo. Don't worry about this if you don't know what mh format is.
+
+
+II.b. #news. (UNIX only)
+
+The prefix #news. is special and refers to the newsgroup named with
+the suffix. For example, #news.comp.mail.misc refers to the newsgroup
+named comp.mail.misc.
+
+
+II.c. #ftp/ (UNIX only)
+
+The prefix #ftp/ is special and refers to the anonymous ftp filesystem
+named with the suffix. For example, #ftp/foo/bar refers to the file
+/foo/bar in the anonymous FTP filesystem. Anonymous FTP files are
+available to anonymous IMAP logins.
+
+
+II.d. #public/ (UNIX only)
+
+The prefix #public/ is special and refers to the public files
+filesystem named with the suffix. For example, #public/foo/bar refers
+to the file /foo/bar in the public filesystem. Public files are
+available to anonymous IMAP logins.
+
+
+II.e. #shared/ (UNIX only)
+
+The prefix #shared/ is special and refers to the shared files
+filesystem named with the suffix. For example, #shared/foo/bar
+frefers to the file /foo/bar in the shared filesystem.
+
+
+III. Remote names
+
+All names which start with "{" are remote names, and are in the form
+ "{" remote_system_name [":" port] [flags] "}" [mailbox_name]
+where:
+ remote_system_name Internet domain name or bracketed IP address
+ of server.
+ port optional TCP port number, default is the
+ default port for that service
+ flags optional flags, one of the following:
+ "/service=" service mailbox access service, default is "imap"
+ "/user=" user remote user name for login on the server
+ "/authuser=" user remote authentication user; if specified this
+ is the user name whose password is used (e.g.
+ administrator)
+ "/anonymous" remote access as anonymous user
+ "/debug" record protocol telemetry in application's
+ debug log
+ "/secure" do not transmit a plaintext password over
+ the network
+ "/imap", "/imap2", "/imap2bis", "/imap4", "/imap4rev1"
+ equivalent to /service=imap
+ "/pop3" equivalent to /service=pop3
+ "/nntp" equivalent to /service=nntp
+ "/norsh" do not use rsh or ssh to establish a preauthenticated
+ IMAP session
+ "/ssl" use the Secure Socket Layer to encrypt the session
+ "/validate-cert" validate certificates from TLS/SSL server (this is the
+ default behavior)
+ "/novalidate-cert" do not validate certificates from TLS/SSL server,
+ needed if server uses self-signed certificates
+ "/tls" force use of start-TLS to encrypt the session, and
+ reject connection to servers that do not support it
+ "/tls-sslv23" use the depreciated SSLv23 client when negotiating
+ TLS to the server. This is necessary with some
+ broken servers which (incorrectly) think that TLS
+ is just another way of doing SSL.
+ "/notls" do not do start-TLS to encrypt the session, even
+ with servers that support it
+ "/readonly" request read-only mailbox open (IMAP only; ignored
+ on NNTP, and an error with SMTP and POP3)
+ "/loser" disable various protocol features and perform various
+ client-side workarounds; for example, it disables
+ the SEARCH command in IMAP and does client-side
+ searching instead. The precise measures taken by
+ /loser depend upon the protocol and are subject to
+ change over time. /loser is intended for use with
+ defective servers which do not implement the
+ protocol specification correctly. It should be used
+ only as a last resort since it will seriously
+ degrade performance.
+ mailbox_name remote mailbox name, default is INBOX
+
+For example:
+ {imap.foo.com}INBOX
+opens an IMAP connection to system imap.foo.com and selects INBOX.
+
+
+IV. All other names
+
+All other names are treated as local file names, relative to the
+user's home directory. Read drivers.txt for more details.
diff --git a/imap/docs/rfc/README b/imap/docs/rfc/README
new file mode 100644
index 00000000..550d8d20
--- /dev/null
+++ b/imap/docs/rfc/README
@@ -0,0 +1,70 @@
+The following documents are necessary to understand the syntax rules
+most of the remaining documents. Note that some documents refer to
+RFC 2234 which has been replaced by RFC 5234:
+ rfc5234.txt Augmented BNF for Syntax Specifications - ABNF
+ rfc4466.txt Collected Extensions to IMAP4 ABNF
+
+
+The following documents specify the IMAP protocol:
+ rfc3501.txt Internet Message Access Protocol - Version 4rev1
+
+
+The following documents provide additional information which is useful
+in understanding the IMAP protocol:
+ rfc1733.txt Distributed Electronic Mail Models in IMAP4
+ rfc2180.txt IMAP4 Multi-Accessed Mailbox Practice
+ rfc2683.txt IMAP4 Implementation Recommendations
+ rfc4549.txt Synchronization Operations for Disconnected IMAP4 Clients
+
+
+The following documents describe extensions to the IMAP protocol.
+Items marked with "*" are supported in this distribution:
+ rfc4314.txt ACL
+ * rfc3516.txt BINARY
+ rfc4469.txt CATENATE
+ * rfc3348.txt CHILDREN
+ rfc4978.txt COMPRESS
+ rfc4551.txt CONDSTORE
+ rfc5161.txt ENABLE
+ * rfc4731.txt ESEARCH
+ rfc2971.txt ID
+ * rfc2177.txt IDLE
+ * rfc2088.txt LITERAL+
+ * rfc2221.txt LOGIN-REFERRALS
+ * rfc2193.txt MAILBOX-REFERRALS
+ * rfc3502.txt MULTIAPPEND
+ * rfc2342.txt NAMESPACE
+ rfc5162.txt QRESYNC
+ rfc2087.txt QUOTA
+ * rfc4959.txt SASL-IR
+ * rfc4315.txt UIDPLUS
+ * rfc3691.txt UNSELECT
+ rfc4467.txt URLAUTH
+ * rfc5032.txt WITHIN
+
+
+The following documents describe SASL:
+ rfc4422.txt Simple Authentication and Security Layer (SASL)
+and the SASL mechanisms supported in this distribution:
+ rfc4505.txt ANONYMOUS
+ rfc2195.txt CRAM-MD5
+ rfc4752.txt GSSAPI
+ rfc4616.txt PLAIN
+
+
+The following documents relate to internationalization issues:
+ rfc4790.txt Internet Application Protocol Collation Registry
+ rfc5051.txt i;unicode-casemap - Simple Unicode Collation Algorithm
+
+
+The following documents are primarily of historic interest:
+ rfc1732.txt IMAP4 Compatibility with IMAP2 and IMAP2bis
+ rfc2061.txt IMAP4 Compatibility with IMAP2bis
+ rfc2062.txt Internet Message Access Protocol - Obsolete Syntax
+
+
+The following documents discuss matters which are related to IMAP:
+ rfc3503.txt MDN Profile for IMAP
+ rfc3656.txt MUPDATE Distributed Mailbox Database Protocol
+ rfc4468.txt Message Submission BURL Extension
+ rfc5092.txt IMAP URL Scheme
diff --git a/imap/docs/rfc/rfc1732.txt b/imap/docs/rfc/rfc1732.txt
new file mode 100644
index 00000000..cfae89c0
--- /dev/null
+++ b/imap/docs/rfc/rfc1732.txt
@@ -0,0 +1,283 @@
+
+
+
+
+
+
+Network Working Group M. Crispin
+Request for Comments: 1732 University of Washington
+Category: Informational December 1994
+
+
+ IMAP4 COMPATIBILITY WITH IMAP2 AND IMAP2BIS
+
+
+Status of this Memo
+
+ This memo provides information for the Internet community. This memo
+ does not specify an Internet standard of any kind. Distribution of
+ this memo is unlimited.
+
+Introduction
+
+ This is a summary of hints and recommendations to enable an IMAP4
+ implementation to interoperate with implementations that conform to
+ earlier specifications. None of these hints and recommendations are
+ required by the IMAP4 specification; implementors must decide for
+ themselves whether they want their implementation to fail if it
+ encounters old software.
+
+ IMAP4 has been designed to be upwards compatible with earlier
+ specifications. For the most part, IMAP4 facilities that were not in
+ earlier specifications should be invisible to clients unless the
+ client asks for the facility.
+
+ In some cases, older servers may support some of the capabilities
+ listed as being "new in IMAP4" as experimental extensions to the
+ IMAP2 protocol described in RFC 1176.
+
+ This information may not be complete; it reflects current knowledge
+ of server and client implementations as well as "folklore" acquired
+ in the evolution of the protocol.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Crispin [Page 1]
+
+RFC 1732 IMAP4 - Compatibility December 1994
+
+
+IMAP4 client interoperability with old servers
+
+ In general, a client should be able to discover whether an IMAP2
+ server supports a facility by trial-and-error; if an attempt to use a
+ facility generates a BAD response, the client can assume that the
+ server does not support the facility.
+
+ A quick way to check whether a server implementation supports the
+ IMAP4 specification is to try the CAPABILITY command. An OK response
+ that includes the IMAP4 capability value indicates a server that
+ supports IMAP4; a BAD response or one without the IMAP4 capability
+ value indicates an older server.
+
+ The following is a list of facilities that are only in IMAP4, and
+ suggestions for how new clients might interoperate with old servers:
+
+ CAPABILITY command
+ A BAD response to this command indicates that the server
+ implements IMAP2 (or IMAP2bis) and not IMAP4.
+
+ AUTHENTICATE command.
+ Use the LOGIN command.
+
+ LSUB and LIST commands
+ Try the RFC 1176 FIND command.
+
+ * in a sequence
+ Use the number of messages in the mailbox from the EXISTS
+ unsolicited response.
+
+ SEARCH extensions (character set, additional criteria)
+ Reformulate the search request using only the searching
+ options listed in search_old in the IMAP4 grammar. This may
+ entail doing multiple searches to achieve the desired
+ results.
+
+ BODYSTRUCTURE fetch data item
+ Try to fetch the non-extensible BODY data item.
+
+ body section number 0
+ Fetch the entire message and extract the header.
+
+ RFC822.HEADER.LINES and RFC822.HEADER.LINES.NOT fetch data items
+ Use RFC822.HEADER and remove the unwanted information.
+
+ BODY.PEEK[section], RFC822.PEEK, and RFC822.TEXT.PEEK fetch data
+ items Use the corresponding non-PEEK versions and manually
+ clear the \Seen flag as necessary.
+
+
+
+Crispin [Page 2]
+
+RFC 1732 IMAP4 - Compatibility December 1994
+
+
+ UID fetch data item and the UID commands
+ No equivalent capabilitity exists in older servers.
+
+ FLAGS.SILENT, +FLAGS.SILENT, and -FLAGS.SILENT store data items
+ Use the corresponding non-SILENT versions and ignore the
+ untagged FETCH responses which com eback.
+
+
+ The following IMAP4 facilities were introduced in the experimental
+ IMAP2bis revisions to RFC-1176, and may be present in a server that
+ does not support the CAPABILITY command:
+
+ CREATE, DELETE, and RENAME commands
+ To test whether these commands are present, try a CREATE
+ INBOX command. If the response is NO, these commands are
+ supported by the server. If the response is BAD, they are
+ not. Older servers without the CREATE capability may sup-
+ port implicit creation of a mailbox by a COPY command with a
+ non-existant name as the destination.
+
+ APPEND command
+ To test whether this command is present, try to append a
+ zero-length stream to a mailbox name that is known not to
+ exist (or at least, highly unlikely to exist) on the remote
+ system.
+
+ SUBSCRIBE and UNSUBSCRIBE commands
+ Try the form of these commands with the optional MAILBOX
+ keyword.
+
+ EXAMINE command
+ Use the SELECT command instead.
+
+ flags and internal date argument to APPEND command
+ Try the APPEND without any flag list and internal date argu-
+ ments.
+
+ BODY, BODY[section], and FULL fetch data items
+ Use RFC822.TEXT and ALL instead. Server does not support
+ MIME.
+
+ PARTIAL command
+ Use the appropriate FETCH command and ignore the unwanted
+ data.
+
+
+ IMAP4 client implementations must accept all responses and data for-
+ mats documented in the IMAP4 specification, including those labeled
+
+
+
+Crispin [Page 3]
+
+RFC 1732 IMAP4 - Compatibility December 1994
+
+
+ as obsolete. This includes the COPY and STORE unsolicited responses
+ and the old format of dates and times. In particular, client imple-
+ mentations must not treat a date/time as a fixed format string; nor
+ may they assume that the time begins at a particular octet.
+
+ IMAP4 client implementations must not depend upon the presence of any
+ server extensions that are not in the base IMAP4 specification.
+
+ The experimental IMAP2bis version specified that the TRYCREATE spe-
+ cial information token is sent as a separate unsolicited OK response
+ instead of inside the NO response.
+
+ The FIND BBOARDS, FIND ALL.BBOARDS, and BBOARD commands of RFC 1176
+ are removed from IMAP4. There is no equivalent to the bboard com-
+ mands, which provided a separate namespace with implicit restrictions
+ on what may be done in that namespace.
+
+ Older server implementations may automatically create the destination
+ mailbox on COPY if that mailbox does not already exist. This was how
+ a new mailbox was created in older specifications. If the server
+ does not support the CREATE command (see above for how to test for
+ this), it will probably create a mailbox on COPY.
+
+ Older server implementations may not preserve flags or internal dates
+ on COPY. Some server implementations may not permit the preservation
+ of certain flags on COPY or their setting with APPEND as site policy.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Crispin [Page 4]
+
+RFC 1732 IMAP4 - Compatibility December 1994
+
+
+IMAP4 server interoperability with old clients
+
+ In general, there should be no interoperation problem between a
+ server conforming to the IMAP4 specification and a well-written
+ client that conforms to an earlier specification. Known problems are
+ noted below:
+
+ Poor wording in the description of the CHECK command in earlier
+ specifications implied that a CHECK command is the way to get the
+ current number of messages in the mailbox. This is incorrect. A
+ CHECK command does not necessarily result in an EXISTS response.
+ Clients must remember the most recent EXISTS value sent from the
+ server, and should not generate unnecessary CHECK commands.
+
+ An incompatibility exists with COPY in IMAP4. COPY in IMAP4
+ servers does not automatically create the destination mailbox if
+ that mailbox does not already exist. This may cause problems with
+ old clients that expect automatic mailbox creation in COPY.
+
+ The PREAUTH unsolicited response is new in IMAP4. It is highly
+ unlikely that an old client would ever see this response.
+
+ The format of dates and times has changed due to the impending end
+ of the century. Clients that fail to accept a four-digit year or
+ a signed four-digit timezone value will not work properly with
+ IMAP4.
+
+ An incompatibility exists with the use of "\" in quoted strings.
+ This is best avoided by using literals instead of quoted strings
+ if "\" or <"> is embedded in the string.
+
+Security Considerations
+
+ Security issues are not discussed in this memo.
+
+Author's Address:
+
+ Mark R. Crispin
+ Networks and Distributed Computing, JE-30
+ University of Washington
+ Seattle, WA 98195
+
+ Phone: (206) 543-5762
+
+ EMail: MRC@CAC.Washington.EDU
+
+
+
+
+
+
+Crispin [Page 5]
+
diff --git a/imap/docs/rfc/rfc1733.txt b/imap/docs/rfc/rfc1733.txt
new file mode 100644
index 00000000..2ec385f0
--- /dev/null
+++ b/imap/docs/rfc/rfc1733.txt
@@ -0,0 +1,171 @@
+
+
+
+
+
+
+Network Working Group M. Crispin
+Request for Comments: 1733 University of Washington
+Category: Informational December 1994
+
+
+ DISTRIBUTED ELECTRONIC MAIL MODELS IN IMAP4
+
+
+Status of this Memo
+
+ This memo provides information for the Internet community. This memo
+ does not specify an Internet standard of any kind. Distribution of
+ this memo is unlimited.
+
+
+Distributed Electronic Mail Models
+
+ There are three fundamental models of client/server email: offline,
+ online, and disconnected use. IMAP4 can be used in any one of these
+ three models.
+
+ The offline model is the most familiar form of client/server email
+ today, and is used by protocols such as POP-3 (RFC 1225) and UUCP.
+ In this model, a client application periodically connects to a
+ server. It downloads all the pending messages to the client machine
+ and deletes these from the server. Thereafter, all mail processing
+ is local to the client. This model is store-and-forward; it moves
+ mail on demand from an intermediate server (maildrop) to a single
+ destination machine.
+
+ The online model is most commonly used with remote filesystem
+ protocols such as NFS. In this model, a client application
+ manipulates mailbox data on a server machine. A connection to the
+ server is maintained throughout the session. No mailbox data are
+ kept on the client; the client retrieves data from the server as is
+ needed. IMAP4 introduces a form of the online model that requires
+ considerably less network bandwidth than a remote filesystem
+ protocol, and provides the opportunity for using the server for CPU
+ or I/O intensive functions such as parsing and searching.
+
+ The disconnected use model is a hybrid of the offline and online
+ models, and is used by protocols such as PCMAIL (RFC 1056). In this
+ model, a client user downloads some set of messages from the server,
+ manipulates them offline, then at some later time uploads the
+ changes. The server remains the authoritative repository of the
+ messages. The problems of synchronization (particularly when
+ multiple clients are involved) are handled through the means of
+ unique identifiers for each message.
+
+
+
+Crispin [Page 1]
+
+RFC 1733 IMAP4 - Model December 1994
+
+
+ Each of these models have their own strengths and weaknesses:
+
+ Feature Offline Online Disc
+ ------- ------- ------ ----
+ Can use multiple clients NO YES YES
+ Minimum use of server connect time YES NO YES
+ Minimum use of server resources YES NO NO
+ Minimum use of client disk resources NO YES NO
+ Multiple remote mailboxes NO YES YES
+ Fast startup NO YES NO
+ Mail processing when not online YES NO YES
+
+ Although IMAP4 has its origins as a protocol designed to accommodate
+ the online model, it can support the other two models as well. This
+ makes possible the creation of clients that can be used in any of the
+ three models. For example, a user may wish to switch between the
+ online and disconnected models on a regular basis (e.g. owing to
+ travel).
+
+ IMAP4 is designed to transmit message data on demand, and to provide
+ the facilities necessary for a client to decide what data it needs at
+ any particular time. There is generally no need to do a wholesale
+ transfer of an entire mailbox or even of the complete text of a
+ message. This makes a difference in situations where the mailbox is
+ large, or when the link to the server is slow.
+
+ More specifically, IMAP4 supports server-based RFC 822 and MIME
+ processing. With this information, it is possible for a client to
+ determine in advance whether it wishes to retrieve a particular
+ message or part of a message. For example, a user connected to an
+ IMAP4 server via a dialup link can determine that a message has a
+ 2000 byte text segment and a 40 megabyte video segment, and elect to
+ fetch only the text segment.
+
+ In IMAP4, the client/server relationship lasts only for the duration
+ of the TCP connection. There is no registration of clients. Except
+ for any unique identifiers used in disconnected use operation, the
+ client initially has no knowledge of mailbox state and learns it from
+ the IMAP4 server when a mailbox is selected. This initial transfer
+ is minimal; the client requests additional state data as it needs.
+
+ As noted above, the choice for the location of mailbox data depends
+ upon the model chosen. The location of message state (e.g. whether
+ or not a message has been read or answered) is also determined by the
+ model, and is not necessarily the same as the location of the mailbox
+ data. For example, in the online model message state can be co-
+ located with mailbox data; it can also be located elsewhere (on the
+ client or on a third agent) using unique identifiers to achieve
+
+
+
+Crispin [Page 2]
+
+RFC 1733 IMAP4 - Model December 1994
+
+
+ common reference across sessions. The latter is particularly useful
+ with a server that exports public data such as netnews and does not
+ maintain per-user state.
+
+ The IMAP4 protocol provides the generality to implement these
+ different models. This is done by means of server and (especially)
+ client configuration, and not by requiring changes to the protocol or
+ the implementation of the protocol.
+
+
+Security Considerations
+
+ Security issues are not discussed in this memo.
+
+
+Author's Address:
+
+ Mark R. Crispin
+ Networks and Distributed Computing, JE-30
+ University of Washington
+ Seattle, WA 98195
+
+ Phone: (206) 543-5762
+
+ EMail: MRC@CAC.Washington.EDU
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Crispin [Page 3]
+
diff --git a/imap/docs/rfc/rfc2061.txt b/imap/docs/rfc/rfc2061.txt
new file mode 100644
index 00000000..7cb02bb2
--- /dev/null
+++ b/imap/docs/rfc/rfc2061.txt
@@ -0,0 +1,171 @@
+
+
+
+
+
+
+Network Working Group M. Crispin
+Request for Comments: 2061 University of Washington
+Category: Informational December 1996
+
+
+ IMAP4 COMPATIBILITY WITH IMAP2BIS
+
+Status of this Memo
+
+ This memo provides information for the Internet community. This memo
+ does not specify an Internet standard of any kind. Distribution of
+ this memo is unlimited.
+
+Introduction
+
+ The Internet Message Access Protocol (IMAP) has been through several
+ revisions and variants in its 10-year history. Many of these are
+ either extinct or extremely rare; in particular, several undocumented
+ variants and the variants described in RFC 1064, RFC 1176, and RFC
+ 1203 fall into this category.
+
+ One variant, IMAP2bis, is at the time of this writing very common and
+ has been widely distributed with the Pine mailer. Unfortunately,
+ there is no definite document describing IMAP2bis. This document is
+ intended to be read along with RFC 1176 and the most recent IMAP4
+ specification (RFC 2060) to assist implementors in creating an IMAP4
+ implementation to interoperate with implementations that conform to
+ earlier specifications. Nothing in this document is required by the
+ IMAP4 specification; implementors must decide for themselves whether
+ they want their implementation to fail if it encounters old software.
+
+ At the time of this writing, IMAP4 has been updated from the version
+ described in RFC 1730. An implementor who wishes to interoperate
+ with both RFC 1730 and RFC 2060 should refer to both documents.
+
+ This information is not complete; it reflects current knowledge of
+ server and client implementations as well as "folklore" acquired in
+ the evolution of the protocol. It is NOT a description of how to
+ interoperate with all variants of IMAP, but rather with the old
+ variant that is most likely to be encountered. For detailed
+ information on interoperating with other old variants, refer to RFC
+ 1732.
+
+IMAP4 client interoperability with IMAP2bis servers
+
+ A quick way to check whether a server implementation supports the
+ IMAP4 specification is to try the CAPABILITY command. An OK response
+ will indicate which variant(s) of IMAP4 are supported by the server.
+
+
+
+Crispin Informational [Page 1]
+
+RFC 2061 IMAP4 Compatibility December 1996
+
+
+ If the client does not find any of its known variant in the response,
+ it should treat the server as IMAP2bis. A BAD response indicates an
+ IMAP2bis or older server.
+
+ Most IMAP4 facilities are in IMAP2bis. The following exceptions
+ exist:
+
+ CAPABILITY command
+ The absense of this command indicates IMAP2bis (or older).
+
+ AUTHENTICATE command.
+ Use the LOGIN command.
+
+ LSUB, SUBSCRIBE, and UNSUBSCRIBE commands
+ No direct functional equivalent. IMAP2bis had a concept
+ called "bboards" which is not in IMAP4. RFC 1176 supported
+ these with the BBOARD and FIND BBOARDS commands. IMAP2bis
+ augmented these with the FIND ALL.BBOARDS, SUBSCRIBE BBOARD,
+ and UNSUBSCRIBE BBOARD commands. It is recommended that
+ none of these commands be implemented in new software,
+ including servers that support old clients.
+
+ LIST command
+ Use the command FIND ALL.MAILBOXES, which has a similar syn-
+ tax and response to the FIND MAILBOXES command described in
+ RFC 1176. The FIND MAILBOXES command is unlikely to produce
+ useful information.
+
+ * in a sequence
+ Use the number of messages in the mailbox from the EXISTS
+ unsolicited response.
+
+ SEARCH extensions (character set, additional criteria)
+ Reformulate the search request using only the RFC 1176 syn-
+ tax. This may entail doing multiple searches to achieve the
+ desired results.
+
+ BODYSTRUCTURE fetch data item
+ Use the non-extensible BODY data item.
+
+ body sections HEADER, TEXT, MIME, HEADER.FIELDS, HEADER.FIELDS.NOT
+ Use body section numbers only.
+
+ BODY.PEEK[section]
+ Use BODY[section] and manually clear the \Seen flag as
+ necessary.
+
+
+
+
+
+Crispin Informational [Page 2]
+
+RFC 2061 IMAP4 Compatibility December 1996
+
+
+ FLAGS.SILENT, +FLAGS.SILENT, and -FLAGS.SILENT store data items
+ Use the corresponding non-SILENT versions and ignore the
+ untagged FETCH responses which come back.
+
+ UID fetch data item and the UID commands
+ No functional equivalent.
+
+ CLOSE command
+ No functional equivalent.
+
+
+ In IMAP2bis, the TRYCREATE special information token is sent as a
+ separate unsolicited OK response instead of inside the NO response.
+
+ IMAP2bis is ambiguous about whether or not flags or internal dates
+ are preserved on COPY. It is impossible to know what behavior is
+ supported by the server.
+
+IMAP4 server interoperability with IMAP2bis clients
+
+ The only interoperability problem between an IMAP4 server and a
+ well-written IMAP2bis client is an incompatibility with the use of
+ "\" in quoted strings. This is best avoided by using literals
+ instead of quoted strings if "\" or <"> is embedded in the string.
+
+Security Considerations
+
+ Security issues are not discussed in this memo.
+
+Author's Address
+
+ Mark R. Crispin
+ Networks and Distributed Computing
+ University of Washington
+ 4545 15th Aveneue NE
+ Seattle, WA 98105-4527
+
+ Phone: (206) 543-5762
+ EMail: MRC@CAC.Washington.EDU
+
+
+
+
+
+
+
+
+
+
+
+
+Crispin Informational [Page 3]
+
diff --git a/imap/docs/rfc/rfc2062.txt b/imap/docs/rfc/rfc2062.txt
new file mode 100644
index 00000000..865d1dad
--- /dev/null
+++ b/imap/docs/rfc/rfc2062.txt
@@ -0,0 +1,451 @@
+
+
+
+
+
+
+Network Working Group M. Crispin
+Request for Comments: 2062 University of Washington
+Category: Informational December 1996
+
+
+ Internet Message Access Protocol - Obsolete Syntax
+
+Status of this Memo
+
+ This memo provides information for the Internet community. This memo
+ does not specify an Internet standard of any kind. Distribution of
+ this memo is unlimited.
+
+Abstract
+
+ This document describes obsolete syntax which may be encountered by
+ IMAP4 implementations which deal with older versions of the Internet
+ Mail Access Protocol. IMAP4 implementations MAY implement this
+ syntax in order to maximize interoperability with older
+ implementations.
+
+ This document repeats information from earlier documents, most
+ notably RFC 1176 and RFC 1730.
+
+Obsolete Commands and Fetch Data Items
+
+ The following commands are OBSOLETE. It is NOT required to support
+ any of these commands or fetch data items in new server
+ implementations. These commands are documented here for the benefit
+ of implementors who may wish to support them for compatibility with
+ old client implementations.
+
+ The section headings of these commands are intended to correspond
+ with where they would be located in the main document if they were
+ not obsoleted.
+
+6.3.OBS.1. FIND ALL.MAILBOXES Command
+
+ Arguments: mailbox name with possible wildcards
+
+ Data: untagged responses: MAILBOX
+
+ Result: OK - find completed
+ NO - find failure: can't list that name
+ BAD - command unknown or arguments invalid
+
+
+
+
+
+
+Crispin Informational [Page 1]
+
+RFC 2062 IMAP4 Obsolete December 1996
+
+
+ The FIND ALL.MAILBOXES command returns a subset of names from the
+ complete set of all names available to the user. It returns zero
+ or more untagged MAILBOX replies. The mailbox argument to FIND
+ ALL.MAILBOXES is similar to that for LIST with an empty reference,
+ except that the characters "%" and "?" match a single character.
+
+ Example: C: A002 FIND ALL.MAILBOXES *
+ S: * MAILBOX blurdybloop
+ S: * MAILBOX INBOX
+ S: A002 OK FIND ALL.MAILBOXES completed
+
+6.3.OBS.2. FIND MAILBOXES Command
+
+ Arguments: mailbox name with possible wildcards
+
+ Data: untagged responses: MAILBOX
+
+ Result: OK - find completed
+ NO - find failure: can't list that name
+ BAD - command unknown or arguments invalid
+
+ The FIND MAILBOXES command returns a subset of names from the set
+ of names that the user has declared as being "active" or
+ "subscribed". It returns zero or more untagged MAILBOX replies.
+ The mailbox argument to FIND MAILBOXES is similar to that for LSUB
+ with an empty reference, except that the characters "%" and "?"
+ match a single character.
+
+ Example: C: A002 FIND MAILBOXES *
+ S: * MAILBOX blurdybloop
+ S: * MAILBOX INBOX
+ S: A002 OK FIND MAILBOXES completed
+
+6.3.OBS.3. SUBSCRIBE MAILBOX Command
+
+ Arguments: mailbox name
+
+ Data: no specific data for this command
+
+ Result: OK - subscribe completed
+ NO - subscribe failure: can't subscribe to that name
+ BAD - command unknown or arguments invalid
+
+ The SUBSCRIBE MAILBOX command is identical in effect to the
+ SUBSCRIBE command. A server which implements this command must be
+ able to distinguish between a SUBSCRIBE MAILBOX command and a
+ SUBSCRIBE command with a mailbox name argument of "MAILBOX".
+
+
+
+
+Crispin Informational [Page 2]
+
+RFC 2062 IMAP4 Obsolete December 1996
+
+
+ Example: C: A002 SUBSCRIBE MAILBOX #news.comp.mail.mime
+ S: A002 OK SUBSCRIBE MAILBOX to #news.comp.mail.mime
+ completed
+ C: A003 SUBSCRIBE MAILBOX
+ S: A003 OK SUBSCRIBE to MAILBOX completed
+
+
+6.3.OBS.4. UNSUBSCRIBE MAILBOX Command
+
+ Arguments: mailbox name
+
+ Data: no specific data for this command
+
+ Result: OK - unsubscribe completed
+ NO - unsubscribe failure: can't unsubscribe that name
+ BAD - command unknown or arguments invalid
+
+ The UNSUBSCRIBE MAILBOX command is identical in effect to the
+ UNSUBSCRIBE command. A server which implements this command must
+ be able to distinguish between a UNSUBSCRIBE MAILBOX command and
+ an UNSUBSCRIBE command with a mailbox name argument of "MAILBOX".
+
+ Example: C: A002 UNSUBSCRIBE MAILBOX #news.comp.mail.mime
+ S: A002 OK UNSUBSCRIBE MAILBOX from #news.comp.mail.mime
+ completed
+ C: A003 UNSUBSCRIBE MAILBOX
+ S: A003 OK UNSUBSCRIBE from MAILBOX completed
+
+6.4.OBS.1 PARTIAL Command
+
+ Arguments: message sequence number
+ message data item name
+ position of first octet
+ number of octets
+
+ Data: untagged responses: FETCH
+
+ Result: OK - partial completed
+ NO - partial error: can't fetch that data
+ BAD - command unknown or arguments invalid
+
+ The PARTIAL command is equivalent to the associated FETCH command,
+ with the added functionality that only the specified number of
+ octets, beginning at the specified starting octet, are returned.
+ Only a single message can be fetched at a time. The first octet
+ of a message, and hence the minimum for the starting octet, is
+ octet 1.
+
+
+
+
+Crispin Informational [Page 3]
+
+RFC 2062 IMAP4 Obsolete December 1996
+
+
+ The following FETCH items are valid data for PARTIAL: RFC822,
+ RFC822.HEADER, RFC822.TEXT, BODY[<section>], as well as any .PEEK
+ forms of these.
+
+ Any partial fetch that attempts to read beyond the end of the text
+ is truncated as appropriate. If the starting octet is beyond the
+ end of the text, an empty string is returned.
+
+ The data are returned with the FETCH response. There is no
+ indication of the range of the partial data in this response. It
+ is not possible to stream multiple PARTIAL commands of the same
+ data item without processing and synchronizing at each step, since
+ streamed commands may be executed out of order.
+
+ There is no requirement that partial fetches follow any sequence.
+ For example, if a partial fetch of octets 1 through 10000 breaks
+ in an awkward place for BASE64 decoding, it is permitted to
+ continue with a partial fetch of 9987 through 19987, etc.
+
+ The handling of the \Seen flag is the same as in the associated
+ FETCH command.
+
+ Example: C: A005 PARTIAL 4 RFC822 1 1024
+ S: * 1 FETCH (RFC822 {1024}
+ S: Return-Path: <gray@cac.washington.edu>
+ S: ...
+ S: ......... FLAGS (\Seen))
+ S: A005 OK PARTIAL completed
+
+6.4.5.OBS.1 Obsolete FETCH Data Items
+
+ The following FETCH data items are obsolete:
+
+ BODY[<...>0] A body part number of 0 is the [RFC-822] header of
+ the message. BODY[0] is functionally equivalent to
+ BODY[HEADER], differing in the syntax of the
+ resulting untagged FETCH data (BODY[0] is
+ returned).
+
+ RFC822.HEADER.LINES <header_list>
+ Functionally equivalent to BODY.PEEK[HEADER.LINES
+ <header_list>], differing in the syntax of the
+ resulting untagged FETCH data (RFC822.HEADER is
+ returned).
+
+
+
+
+
+
+
+Crispin Informational [Page 4]
+
+RFC 2062 IMAP4 Obsolete December 1996
+
+
+ RFC822.HEADER.LINES.NOT <header_list>
+ Functionally equivalent to
+ BODY.PEEK[HEADER.LINES.NOT <header_list>],
+ differing in the syntax of the resulting untagged
+ FETCH data (RFC822.HEADER is returned).
+
+ RFC822.PEEK Functionally equivalent to BODY.PEEK[], except for
+ the syntax of the resulting untagged FETCH data
+ (RFC822 is returned).
+
+ RFC822.TEXT.PEEK
+ Functionally equivalent to BODY.PEEK[TEXT], except
+ for the syntax of the resulting untagged FETCH data
+ (RFC822.TEXT is returned).
+
+Obsolete Responses
+
+ The following responses are OBSOLETE. Except as noted below, these
+ responses MUST NOT be transmitted by new server implementations.
+ Client implementations SHOULD accept these responses.
+
+ The section headings of these responses are intended to correspond
+ with where they would be located in the main document if they were
+ not obsoleted.
+
+7.2.OBS.1. MAILBOX Response
+
+ Data: name
+
+ The MAILBOX response MUST NOT be transmitted by server
+ implementations except in response to the obsolete FIND MAILBOXES
+ and FIND ALL.MAILBOXES commands. Client implementations that do
+ not use these commands MAY ignore this response. It is documented
+ here for the benefit of implementors who may wish to support it
+ for compatibility with old client implementations.
+
+ This response occurs as a result of the FIND MAILBOXES and FIND
+ ALL.MAILBOXES commands. It returns a single name that matches the
+ FIND specification. There are no attributes or hierarchy
+ delimiter.
+
+ Example: S: * MAILBOX blurdybloop
+
+
+
+
+
+
+
+
+
+Crispin Informational [Page 5]
+
+RFC 2062 IMAP4 Obsolete December 1996
+
+
+7.3.OBS.1. COPY Response
+
+ Data: none
+
+ The COPY response MUST NOT be transmitted by new server
+ implementations. Client implementations MUST ignore the COPY
+ response. It is documented here for the benefit of client
+ implementors who may encounter this response from old server
+ implementations.
+
+ In some experimental versions of this protocol, this response was
+ returned in response to a COPY command to indicate on a
+ per-message basis that the message was copied successfully.
+
+ Example: S: * 44 COPY
+
+7.3.OBS.2. STORE Response
+
+ Data: message data
+
+ The STORE response MUST NOT be transmitted by new server
+ implementations. Client implementations MUST treat the STORE
+ response as equivalent to the FETCH response. It is documented
+ here for the benefit of client implementors who may encounter this
+ response from old server implementations.
+
+ In some experimental versions of this protocol, this response was
+ returned instead of FETCH in response to a STORE command to report
+ the new value of the flags.
+
+ Example: S: * 69 STORE (FLAGS (\Deleted))
+
+Formal Syntax of Obsolete Commands and Responses
+
+ Each obsolete syntax rule that is suffixed with "_old" is added to
+ the corresponding name in the formal syntax. For example,
+ command_auth_old adds the FIND command to command_auth.
+
+ command_auth_old ::= find
+
+ command_select_old
+ ::= partial
+
+ date_year_old ::= 2digit
+ ;; (year - 1900)
+
+ date_time_old ::= <"> date_day_fixed "-" date_month "-" date_year
+ SPACE time "-" zone_name <">
+
+
+
+Crispin Informational [Page 6]
+
+RFC 2062 IMAP4 Obsolete December 1996
+
+
+ find ::= "FIND" SPACE ["ALL."] "MAILBOXES" SPACE
+ list_mailbox
+
+ fetch_att_old ::= "RFC822.HEADER.LINES" [".NOT"] SPACE header_list /
+ fetch_text_old
+
+ fetch_text_old ::= "BODY" [".PEEK"] section_old /
+ "RFC822" [".HEADER" / ".TEXT" [".PEEK"]]
+
+ msg_data_old ::= "COPY" / ("STORE" SPACE msg_att)
+
+ partial ::= "PARTIAL" SPACE nz_number SPACE fetch_text_old SPACE
+ number SPACE number
+
+ section_old ::= "[" (number ["." number]) "]"
+
+ subscribe_old ::= "SUBSCRIBE" SPACE "MAILBOX" SPACE mailbox
+
+ unsubscribe_old ::= "UNSUBSCRIBE" SPACE "MAILBOX" SPACE mailbox
+
+ zone_name ::= "UT" / "GMT" / "Z" / ;; +0000
+ "AST" / "EDT" / ;; -0400
+ "EST" / "CDT" / ;; -0500
+ "CST" / "MDT" / ;; -0600
+ "MST" / "PDT" / ;; -0700
+ "PST" / "YDT" / ;; -0800
+ "YST" / "HDT" / ;; -0900
+ "HST" / "BDT" / ;; -1000
+ "BST" / ;; -1100
+ "A" / "B" / "C" / "D" / "E" / "F" / ;; +1 to +6
+ "G" / "H" / "I" / "K" / "L" / "M" / ;; +7 to +12
+ "N" / "O" / "P" / "Q" / "R" / "S" / ;; -1 to -6
+ "T" / "U" / "V" / "W" / "X" / "Y" ;; -7 to -12
+
+Security Considerations
+
+ Security issues are not discussed in this memo.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Crispin Informational [Page 7]
+
+RFC 2062 IMAP4 Obsolete December 1996
+
+
+Author's Address
+
+ Mark R. Crispin
+ Networks and Distributed Computing
+ University of Washington
+ 4545 15th Aveneue NE
+ Seattle, WA 98105-4527
+
+ Phone: (206) 543-5762
+ EMail: MRC@CAC.Washington.EDU
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Crispin Informational [Page 8]
+
diff --git a/imap/docs/rfc/rfc2087.txt b/imap/docs/rfc/rfc2087.txt
new file mode 100644
index 00000000..1db5b57b
--- /dev/null
+++ b/imap/docs/rfc/rfc2087.txt
@@ -0,0 +1,283 @@
+
+
+
+
+
+
+Network Working Group J. Myers
+Request for Comments: 2087 Carnegie Mellon
+Category: Standards Track January 1997
+
+
+ IMAP4 QUOTA extension
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+1. Abstract
+
+ The QUOTA extension of the Internet Message Access Protocol [IMAP4]
+ permits administrative limits on resource usage (quotas) to be
+ manipulated through the IMAP protocol.
+
+Table of Contents
+
+ 1. Abstract........................................... 1
+ 2. Conventions Used in this Document.................. 1
+ 3. Introduction and Overview.......................... 2
+ 4. Commands........................................... 2
+ 4.1. SETQUOTA Command................................... 2
+ 4.2. GETQUOTA Command................................... 2
+ 4.3. GETQUOTAROOT Command............................... 3
+ 5. Responses.......................................... 3
+ 5.1. QUOTA Response..................................... 3
+ 5.2. QUOTAROOT Response................................. 4
+ 6. Formal syntax...................................... 4
+ 7. References......................................... 5
+ 8. Security Considerations............................ 5
+ 9. Author's Address................................... 5
+
+
+2. Conventions Used in this Document
+
+ In examples, "C:" and "S:" indicate lines sent by the client and
+ server respectively.
+
+
+
+
+
+
+
+
+Myers Standards Track [Page 1]
+
+RFC 2087 QUOTA January 1997
+
+
+3. Introduction and Overview
+
+ The QUOTA extension is present in any IMAP4 implementation which
+ returns "QUOTA" as one of the supported capabilities to the
+ CAPABILITY command.
+
+ An IMAP4 server which supports the QUOTA capability may support
+ limits on any number of resources. Each resource has an atom name
+ and an implementation-defined interpretation which evaluates to an
+ integer. Examples of such resources are:
+
+ Name Interpretation
+
+ STORAGE Sum of messages' RFC822.SIZE, in units of 1024 octets
+ MESSAGE Number of messages
+
+
+ Each mailbox has zero or more implementation-defined named "quota
+ roots". Each quota root has zero or more resource limits. All
+ mailboxes that share the same named quota root share the resource
+ limits of the quota root.
+
+ Quota root names do not necessarily have to match the names of
+ existing mailboxes.
+
+4. Commands
+
+4.1. SETQUOTA Command
+
+ Arguments: quota root
+ list of resource limits
+
+ Data: untagged responses: QUOTA
+
+ Result: OK - setquota completed
+ NO - setquota error: can't set that data
+ BAD - command unknown or arguments invalid
+
+ The SETQUOTA command takes the name of a mailbox quota root and a
+ list of resource limits. The resource limits for the named quota root
+ are changed to be the specified limits. Any previous resource limits
+ for the named quota root are discarded.
+
+ If the named quota root did not previously exist, an implementation
+ may optionally create it and change the quota roots for any number of
+ existing mailboxes in an implementation-defined manner.
+
+
+
+
+
+Myers Standards Track [Page 2]
+
+RFC 2087 QUOTA January 1997
+
+
+ Example: C: A001 SETQUOTA "" (STORAGE 512)
+ S: * QUOTA "" (STORAGE 10 512)
+ S: A001 OK Setquota completed
+
+4.2. GETQUOTA Command
+
+ Arguments: quota root
+
+ Data: untagged responses: QUOTA
+
+ Result: OK - getquota completed
+ NO - getquota error: no such quota root, permission
+ denied
+ BAD - command unknown or arguments invalid
+
+ The GETQUOTA command takes the name of a quota root and returns the
+ quota root's resource usage and limits in an untagged QUOTA response.
+
+ Example: C: A003 GETQUOTA ""
+ S: * QUOTA "" (STORAGE 10 512)
+ S: A003 OK Getquota completed
+
+4.3. GETQUOTAROOT Command
+
+ Arguments: mailbox name
+
+ Data: untagged responses: QUOTAROOT, QUOTA
+
+ Result: OK - getquota completed
+ NO - getquota error: no such mailbox, permission denied
+ BAD - command unknown or arguments invalid
+
+ The GETQUOTAROOT command takes the name of a mailbox and returns the
+ list of quota roots for the mailbox in an untagged QUOTAROOT
+ response. For each listed quota root, it also returns the quota
+ root's resource usage and limits in an untagged QUOTA response.
+
+ Example: C: A003 GETQUOTAROOT INBOX
+ S: * QUOTAROOT INBOX ""
+ S: * QUOTA "" (STORAGE 10 512)
+ S: A003 OK Getquota completed
+
+
+
+
+
+
+
+
+
+
+Myers Standards Track [Page 3]
+
+RFC 2087 QUOTA January 1997
+
+
+5. Responses
+
+5.1. QUOTA Response
+
+ Data: quota root name
+ list of resource names, usages, and limits
+
+ This response occurs as a result of a GETQUOTA or GETQUOTAROOT
+ command. The first string is the name of the quota root for which
+ this quota applies.
+
+ The name is followed by a S-expression format list of the resource
+ usage and limits of the quota root. The list contains zero or
+ more triplets. Each triplet conatins a resource name, the current
+ usage of the resource, and the resource limit.
+
+ Resources not named in the list are not limited in the quota root.
+ Thus, an empty list means there are no administrative resource
+ limits in the quota root.
+
+ Example: S: * QUOTA "" (STORAGE 10 512)
+
+5.2. QUOTAROOT Response
+
+ Data: mailbox name
+ zero or more quota root names
+
+ This response occurs as a result of a GETQUOTAROOT command. The
+ first string is the mailbox and the remaining strings are the
+ names of the quota roots for the mailbox.
+
+ Example: S: * QUOTAROOT INBOX ""
+ S: * QUOTAROOT comp.mail.mime
+
+6. Formal syntax
+
+ The following syntax specification uses the augmented Backus-Naur
+ Form (BNF) notation as specified in RFC 822 with one exception; the
+ delimiter used with the "#" construct is a single space (SP) and not
+ one or more commas.
+
+ Except as noted otherwise, all alphabetic characters are case-
+ insensitive. The use of upper or lower case characters to define
+ token strings is for editorial clarity only. Implementations MUST
+ accept these strings in a case-insensitive fashion.
+
+
+
+
+
+
+Myers Standards Track [Page 4]
+
+RFC 2087 QUOTA January 1997
+
+
+ getquota ::= "GETQUOTA" SP astring
+
+ getquotaroot ::= "GETQUOTAROOT" SP astring
+
+ quota_list ::= "(" #quota_resource ")"
+
+ quota_resource ::= atom SP number SP number
+
+ quota_response ::= "QUOTA" SP astring SP quota_list
+
+ quotaroot_response
+ ::= "QUOTAROOT" SP astring *(SP astring)
+
+ setquota ::= "SETQUOTA" SP astring SP setquota_list
+
+ setquota_list ::= "(" 0#setquota_resource ")"
+
+ setquota_resource ::= atom SP number
+
+7. References
+
+ [IMAP4] Crispin, M., "Internet Message Access Protocol - Version 4",
+ RFC 1730, University of Washington, December 1994.
+
+ [RFC-822] Crocker, D., "Standard for the Format of ARPA Internet
+ Text Messages", STD 11, RFC 822.
+
+8. Security Considerations
+
+ Implementors should be careful to make sure the implementation of
+ these commands does not violate the site's security policy. The
+ resource usage of other users is likely to be considered confidential
+ information and should not be divulged to unauthorized persons.
+
+9. Author's Address
+
+ John G. Myers
+ Carnegie-Mellon University
+ 5000 Forbes Ave.
+ Pittsburgh PA, 15213-3890
+
+ EMail: jgm+@cmu.edu
+
+
+
+
+
+
+
+
+
+Myers Standards Track [Page 5]
+
diff --git a/imap/docs/rfc/rfc2088.txt b/imap/docs/rfc/rfc2088.txt
new file mode 100644
index 00000000..f36cc764
--- /dev/null
+++ b/imap/docs/rfc/rfc2088.txt
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+Network Working Group J. Myers
+Request for Comments: 2088 Carnegie Mellon
+Cateogry: Standards Track January 1997
+
+
+ IMAP4 non-synchronizing literals
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+1. Abstract
+
+ The Internet Message Access Protocol [IMAP4] contains the "literal"
+ syntactic construct for communicating strings. When sending a
+ literal from client to server, IMAP4 requires the client to wait for
+ the server to send a command continuation request between sending the
+ octet count and the string data. This document specifies an
+ alternate form of literal which does not require this network round
+ trip.
+
+2. Conventions Used in this Document
+
+ In examples, "C:" and "S:" indicate lines sent by the client and
+ server respectively.
+
+3. Specification
+
+ The non-synchronizing literal is added an alternate form of literal,
+ and may appear in communication from client to server instead of the
+ IMAP4 form of literal. The IMAP4 form of literal, used in
+ communication from client to server, is referred to as a
+ synchronizing literal.
+
+ Non-synchronizing literals may be used with any IMAP4 server
+ implementation which returns "LITERAL+" as one of the supported
+ capabilities to the CAPABILITY command. If the server does not
+ advertise the LITERAL+ capability, the client must use synchronizing
+ literals instead.
+
+ The non-synchronizing literal is distinguished from the original
+ synchronizing literal by having a plus ('+') between the octet count
+ and the closing brace ('}'). The server does not generate a command
+ continuation request in response to a non-synchronizing literal, and
+
+
+
+Myers Standards Track [Page 1]
+
+RFC 2088 LITERAL January 1997
+
+
+ clients are not required to wait before sending the octets of a non-
+ synchronizing literal.
+
+ The protocol receiver of an IMAP4 server must check the end of every
+ received line for an open brace ('{') followed by an octet count, a
+ plus ('+'), and a close brace ('}') immediately preceeding the CRLF.
+ If it finds this sequence, it is the octet count of a non-
+ synchronizing literal and the server MUST treat the specified number
+ of following octets and the following line as part of the same
+ command. A server MAY still process commands and reject errors on a
+ line-by-line basis, as long as it checks for non-synchronizing
+ literals at the end of each line.
+
+ Example: C: A001 LOGIN {11+}
+ C: FRED FOOBAR {7+}
+ C: fat man
+ S: A001 OK LOGIN completed
+
+4. Formal Syntax
+
+ The following syntax specification uses the augmented Backus-Naur
+ Form (BNF) notation as specified in [RFC-822] as modified by [IMAP4].
+ Non-terminals referenced but not defined below are as defined by
+ [IMAP4].
+
+ literal ::= "{" number ["+"] "}" CRLF *CHAR8
+ ;; Number represents the number of CHAR8 octets
+
+6. References
+
+ [IMAP4] Crispin, M., "Internet Message Access Protocol - Version 4",
+ draft-crispin-imap-base-XX.txt, University of Washington, April 1996.
+
+ [RFC-822] Crocker, D., "Standard for the Format of ARPA Internet Text
+ Messages", STD 11, RFC 822.
+
+7. Security Considerations
+
+ There are no known security issues with this extension.
+
+8. Author's Address
+
+ John G. Myers
+ Carnegie-Mellon University
+ 5000 Forbes Ave.
+ Pittsburgh PA, 15213-3890
+
+ Email: jgm+@cmu.edu
+
+
+
+Myers Standards Track [Page 2]
+
diff --git a/imap/docs/rfc/rfc2177.txt b/imap/docs/rfc/rfc2177.txt
new file mode 100644
index 00000000..c11c7654
--- /dev/null
+++ b/imap/docs/rfc/rfc2177.txt
@@ -0,0 +1,227 @@
+
+
+
+
+
+
+Network Working Group B. Leiba
+Request for Comments: 2177 IBM T.J. Watson Research Center
+Category: Standards Track June 1997
+
+
+ IMAP4 IDLE command
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+1. Abstract
+
+ The Internet Message Access Protocol [IMAP4] requires a client to
+ poll the server for changes to the selected mailbox (new mail,
+ deletions). It's often more desirable to have the server transmit
+ updates to the client in real time. This allows a user to see new
+ mail immediately. It also helps some real-time applications based on
+ IMAP, which might otherwise need to poll extremely often (such as
+ every few seconds). (While the spec actually does allow a server to
+ push EXISTS responses aysynchronously, a client can't expect this
+ behaviour and must poll.)
+
+ This document specifies the syntax of an IDLE command, which will
+ allow a client to tell the server that it's ready to accept such
+ real-time updates.
+
+2. Conventions Used in this Document
+
+ In examples, "C:" and "S:" indicate lines sent by the client and
+ server respectively.
+
+ The key words "MUST", "MUST NOT", "SHOULD", "SHOULD NOT", and "MAY"
+ in this document are to be interpreted as described in RFC 2060
+ [IMAP4].
+
+3. Specification
+
+ IDLE Command
+
+ Arguments: none
+
+ Responses: continuation data will be requested; the client sends
+ the continuation data "DONE" to end the command
+
+
+
+Leiba Standards Track [Page 1]
+
+RFC 2177 IMAP4 IDLE command June 1997
+
+
+
+ Result: OK - IDLE completed after client sent "DONE"
+ NO - failure: the server will not allow the IDLE
+ command at this time
+ BAD - command unknown or arguments invalid
+
+ The IDLE command may be used with any IMAP4 server implementation
+ that returns "IDLE" as one of the supported capabilities to the
+ CAPABILITY command. If the server does not advertise the IDLE
+ capability, the client MUST NOT use the IDLE command and must poll
+ for mailbox updates. In particular, the client MUST continue to be
+ able to accept unsolicited untagged responses to ANY command, as
+ specified in the base IMAP specification.
+
+ The IDLE command is sent from the client to the server when the
+ client is ready to accept unsolicited mailbox update messages. The
+ server requests a response to the IDLE command using the continuation
+ ("+") response. The IDLE command remains active until the client
+ responds to the continuation, and as long as an IDLE command is
+ active, the server is now free to send untagged EXISTS, EXPUNGE, and
+ other messages at any time.
+
+ The IDLE command is terminated by the receipt of a "DONE"
+ continuation from the client; such response satisfies the server's
+ continuation request. At that point, the server MAY send any
+ remaining queued untagged responses and then MUST immediately send
+ the tagged response to the IDLE command and prepare to process other
+ commands. As in the base specification, the processing of any new
+ command may cause the sending of unsolicited untagged responses,
+ subject to the ambiguity limitations. The client MUST NOT send a
+ command while the server is waiting for the DONE, since the server
+ will not be able to distinguish a command from a continuation.
+
+ The server MAY consider a client inactive if it has an IDLE command
+ running, and if such a server has an inactivity timeout it MAY log
+ the client off implicitly at the end of its timeout period. Because
+ of that, clients using IDLE are advised to terminate the IDLE and
+ re-issue it at least every 29 minutes to avoid being logged off.
+ This still allows a client to receive immediate mailbox updates even
+ though it need only "poll" at half hour intervals.
+
+
+
+
+
+
+
+
+
+
+
+Leiba Standards Track [Page 2]
+
+RFC 2177 IMAP4 IDLE command June 1997
+
+
+ Example: C: A001 SELECT INBOX
+ S: * FLAGS (Deleted Seen)
+ S: * 3 EXISTS
+ S: * 0 RECENT
+ S: * OK [UIDVALIDITY 1]
+ S: A001 OK SELECT completed
+ C: A002 IDLE
+ S: + idling
+ ...time passes; new mail arrives...
+ S: * 4 EXISTS
+ C: DONE
+ S: A002 OK IDLE terminated
+ ...another client expunges message 2 now...
+ C: A003 FETCH 4 ALL
+ S: * 4 FETCH (...)
+ S: A003 OK FETCH completed
+ C: A004 IDLE
+ S: * 2 EXPUNGE
+ S: * 3 EXISTS
+ S: + idling
+ ...time passes; another client expunges message 3...
+ S: * 3 EXPUNGE
+ S: * 2 EXISTS
+ ...time passes; new mail arrives...
+ S: * 3 EXISTS
+ C: DONE
+ S: A004 OK IDLE terminated
+ C: A005 FETCH 3 ALL
+ S: * 3 FETCH (...)
+ S: A005 OK FETCH completed
+ C: A006 IDLE
+
+4. Formal Syntax
+
+ The following syntax specification uses the augmented Backus-Naur
+ Form (BNF) notation as specified in [RFC-822] as modified by [IMAP4].
+ Non-terminals referenced but not defined below are as defined by
+ [IMAP4].
+
+ command_auth ::= append / create / delete / examine / list / lsub /
+ rename / select / status / subscribe / unsubscribe
+ / idle
+ ;; Valid only in Authenticated or Selected state
+
+ idle ::= "IDLE" CRLF "DONE"
+
+
+
+
+
+
+Leiba Standards Track [Page 3]
+
+RFC 2177 IMAP4 IDLE command June 1997
+
+
+5. References
+
+ [IMAP4] Crispin, M., "Internet Message Access Protocol - Version
+ 4rev1", RFC 2060, December 1996.
+
+6. Security Considerations
+
+ There are no known security issues with this extension.
+
+7. Author's Address
+
+ Barry Leiba
+ IBM T.J. Watson Research Center
+ 30 Saw Mill River Road
+ Hawthorne, NY 10532
+
+ Email: leiba@watson.ibm.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Leiba Standards Track [Page 4]
+
diff --git a/imap/docs/rfc/rfc2180.txt b/imap/docs/rfc/rfc2180.txt
new file mode 100644
index 00000000..57607002
--- /dev/null
+++ b/imap/docs/rfc/rfc2180.txt
@@ -0,0 +1,787 @@
+
+
+
+
+
+
+Network Working Group M. Gahrns
+Request for Comments: 2180 Microsoft
+Category: Informational July 1997
+
+
+ IMAP4 Multi-Accessed Mailbox Practice
+
+Status of this Memo
+
+ This memo provides information for the Internet community. This memo
+ does not specify an Internet standard of any kind. Distribution of
+ this memo is unlimited.
+
+1. Abstract
+
+ IMAP4[RFC-2060] is rich client/server protocol that allows a client
+ to access and manipulate electronic mail messages on a server.
+ Within the protocol framework, it is possible to have differing
+ results for particular client/server interactions. If a protocol does
+ not allow for this, it is often unduly restrictive.
+
+ For example, when multiple clients are accessing a mailbox and one
+ attempts to delete the mailbox, an IMAP4 server may choose to
+ implement a solution based upon server architectural constraints or
+ individual preference.
+
+ With this flexibility comes greater client responsibility. It is not
+ sufficient for a client to be written based upon the behavior of a
+ particular IMAP server. Rather the client must be based upon the
+ behavior allowed by the protocol.
+
+ By documenting common IMAP4 server practice for the case of
+ simultaneous client access to a mailbox, we hope to ensure the widest
+ amount of inter-operation between IMAP4 clients and servers.
+
+ The behavior described in this document reflects the practice of some
+ existing servers or behavior that the consensus of the IMAP mailing
+ list has deemed to be reasonable. The behavior described within this
+ document is believed to be [RFC-2060] compliant. However, this
+ document is not meant to define IMAP4 compliance, nor is it an
+ exhaustive list of valid IMAP4 behavior. [RFC-2060] must always be
+ consulted to determine IMAP4 compliance, especially for server
+ behavior not described within this document.
+
+
+
+
+
+
+
+
+Gahrns Informational [Page 1]
+
+RFC 2180 IMAP4 Multi-Accessed Mailbox Practice July 1997
+
+
+2. Conventions used in this document
+
+ In examples,"C1:", "C2:" and "C3:" indicate lines sent by 3 different
+ clients (client #1, client #2 and client #3) that are connected to a
+ server. "S1:", "S2:" and "S3:" indicated lines sent by the server to
+ client #1, client #2 and client #3 respectively.
+
+ A shared mailbox, is a mailbox that can be used by multiple users.
+
+ A multi-accessed mailbox, is a mailbox that has multiple clients
+ simultaneously accessing it.
+
+ A client is said to have accessed a mailbox after a successful SELECT
+ or EXAMINE command.
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in [RFC-2119].
+
+
+3. Deletion/Renaming of a multi-accessed mailbox
+
+ If an external agent or multiple clients are accessing a mailbox,
+ care must be taken when handling the deletion or renaming of the
+ mailbox. Following are some strategies an IMAP server may choose to
+ use when dealing with this situation.
+
+
+3.1. The server MAY fail the DELETE/RENAME command of a multi-accessed
+ mailbox
+
+ In some cases, this behavior may not be practical. For example, if a
+ large number of clients are accessing a shared mailbox, the window in
+ which no clients have the mailbox accessed may be small or non-
+ existent, effectively rendering the mailbox undeletable or
+ unrenamable.
+
+ Example:
+
+ <Client #1 and Client #2 have mailbox FOO accessed. Client #1 tries
+ to DELETE the mailbox and is refused>
+
+ C1: A001 DELETE FOO
+ S1: A001 NO Mailbox FOO is in use by another user.
+
+
+
+
+
+
+
+Gahrns Informational [Page 2]
+
+RFC 2180 IMAP4 Multi-Accessed Mailbox Practice July 1997
+
+
+3.2. The server MAY allow the DELETE command of a multi-accessed
+ mailbox, but keep the information in the mailbox available for
+ those clients that currently have access to the mailbox.
+
+ When all clients have finished accessing the mailbox, it is
+ permanently removed. For clients that do not already have access to
+ the mailbox, the 'ghosted' mailbox would not be available. For
+ example, it would not be returned to these clients in a subsequent
+ LIST or LSUB command and would not be a valid mailbox argument to any
+ other IMAP command until the reference count of clients accessing the
+ mailbox reached 0.
+
+ In some cases, this behavior may not be desirable. For example if
+ someone created a mailbox with offensive or sensitive information,
+ one might prefer to have the mailbox deleted and all access to the
+ information contained within removed immediately, rather than
+ continuing to allow access until the client closes the mailbox.
+
+ Furthermore, this behavior, may prevent 'recycling' of the same
+ mailbox name until all clients have finished accessing the original
+ mailbox.
+
+ Example:
+
+ <Client #1 and Client #2 have mailbox FOO selected. Client #1 DELETEs
+ mailbox FOO>
+
+ C1: A001 DELETE FOO
+ S1: A001 OK Mailbox FOO is deleted.
+
+ <Client #2 is still able to operate on the deleted mailbox>
+
+ C2: B001 STORE 1 +FLAGS (\Seen)
+ S2: * 1 FETCH FLAGS (\Seen)
+ S2: B001 OK STORE completed
+
+ <Client #3 which did not have access to the mailbox prior to the
+ deletion by client #1 does not have access to the mailbox>
+
+ C3: C001 STATUS FOO (MESSAGES)
+ S3: C001 NO Mailbox does not exist
+
+ <Nor is client #3 able to create a mailbox with the name FOO, while
+ the reference count is non zero>
+
+ C3: C002 CREATE FOO
+ S3: C002 NO Mailbox FOO is still in use. Try again later.
+
+
+
+
+Gahrns Informational [Page 3]
+
+RFC 2180 IMAP4 Multi-Accessed Mailbox Practice July 1997
+
+
+ <Client #2 closes its access to the mailbox, no other clients have
+ access to the mailbox FOO and reference count becomes 0>
+
+ C2: B002 CLOSE
+ S2: B002 OK CLOSE Completed
+
+ <Now that the reference count on FOO has reached 0, the mailbox name
+ can be recycled>
+
+ C3: C003 CREATE FOO
+ S3: C003 OK CREATE Completed
+
+
+3.3. The server MAY allow the DELETE/RENAME of a multi-accessed
+ mailbox, but disconnect all other clients who have the mailbox
+ accessed by sending a untagged BYE response.
+
+ A server may often choose to disconnect clients in the DELETE case,
+ but may choose to implement a "friendlier" method for the RENAME
+ case.
+
+ Example:
+
+ <Client #1 and Client #2 have mailbox FOO accessed. Client #1 DELETEs
+ the mailbox FOO>
+
+ C1: A002 DELETE FOO
+ S1: A002 OK DELETE completed.
+
+ <Server disconnects all other users of the mailbox>
+ S2: * BYE Mailbox FOO has been deleted.
+
+
+3.4. The server MAY allow the RENAME of a multi-accessed mailbox by
+ simply changing the name attribute on the mailbox.
+
+ Other clients that have access to the mailbox can continue issuing
+ commands such as FETCH that do not reference the mailbox name.
+ Clients would discover the renaming the next time they referred to
+ the old mailbox name. Some servers MAY choose to include the
+ [NEWNAME] response code in their tagged NO response to a command that
+ contained the old mailbox name, as a hint to the client that the
+ operation can succeed if the command is issued with the new mailbox
+ name.
+
+
+
+
+
+
+
+Gahrns Informational [Page 4]
+
+RFC 2180 IMAP4 Multi-Accessed Mailbox Practice July 1997
+
+
+ Example:
+
+ <Client #1 and Client #2 have mailbox FOO accessed. Client #1 RENAMEs
+ the mailbox.>
+
+ C1: A001 RENAME FOO BAR
+ S1: A001 OK RENAME completed.
+
+ <Client #2 is still able to do operations that do not reference the
+ mailbox name>
+
+ C2: B001 FETCH 2:4 (FLAGS)
+ S2: * 2 FETCH . . .
+ S2: * 3 FETCH . . .
+ S2: * 4 FETCH . . .
+ S2: B001 OK FETCH completed
+
+ <Client #2 is not able to do operations that reference the mailbox
+ name>
+
+ C2: B002 APPEND FOO {300} C2: Date: Mon, 7 Feb 1994
+ 21:52:25 0800 (PST) C2: . . . S2: B002 NO [NEWNAME FOO
+ BAR] Mailbox has been renamed
+
+
+4. Expunging of messages on a multi-accessed mailbox
+
+ If an external agent or multiple clients are accessing a mailbox,
+ care must be taken when handling the EXPUNGE of messages. Other
+ clients accessing the mailbox may be in the midst of issuing a
+ command that depends upon message sequence numbers. Because an
+ EXPUNGE response can not be sent while responding to a FETCH, STORE
+ or SEARCH command, it is not possible to immediately notify the
+ client of the EXPUNGE. This can result in ambiguity if the client
+ issues a FETCH, STORE or SEARCH operation on a message that has been
+ EXPUNGED.
+
+
+4.1. Fetching of expunged messages
+
+ Following are some strategies an IMAP server may choose to use when
+ dealing with a FETCH command on expunged messages.
+
+
+
+
+
+
+
+
+
+Gahrns Informational [Page 5]
+
+RFC 2180 IMAP4 Multi-Accessed Mailbox Practice July 1997
+
+
+ Consider the following scenario:
+
+ - Client #1 and Client #2 have mailbox FOO selected.
+ - There are 7 messages in the mailbox.
+ - Messages 4:7 are marked for deletion.
+ - Client #1 issues an EXPUNGE, to expunge messages 4:7
+
+
+4.1.1. The server MAY allow the EXPUNGE of a multi-accessed mailbox but
+ keep the messages available to satisfy subsequent FETCH commands
+ until it is able to send an EXPUNGE response to each client.
+
+ In some cases, the behavior of keeping "ghosted" messages may not be
+ desirable. For example if a message contained offensive or sensitive
+ information, one might prefer to instantaneously remove all access to
+ the information, regardless of whether another client is in the midst
+ of accessing it.
+
+ Example: (Building upon the scenario outlined in 4.1.)
+
+ <Client #2 is still able to access the expunged messages because the
+ server has kept a 'ghosted' copy of the messages until it is able to
+ notify client #2 of the EXPUNGE>
+
+ C2: B001 FETCH 4:7 RFC822
+ S2: * 4 FETCH RFC822 . . . (RFC822 info returned)
+ S2: * 5 FETCH RFC822 . . . (RFC822 info returned)
+ S2: * 6 FETCH RFC822 . . . (RFC822 info returned)
+ S2: * 7 FETCH RFC822 . . . (RFC822 info returned)
+ S2: B001 OK FETCH Completed
+
+ <Client #2 issues a command where it can get notified of the EXPUNGE>
+
+ C2: B002 NOOP
+ S2: * 4 EXPUNGE
+ S2: * 4 EXPUNGE
+ S2: * 4 EXPUNGE
+ S2: * 4 EXPUNGE
+ S2: * 3 EXISTS
+ S2: B002 OK NOOP Complete
+
+ <Client #2 no longer has access to the expunged messages>
+
+ C2: B003 FETCH 4:7 RFC822
+ S2: B003 NO Messages 4:7 are no longer available.
+
+
+
+
+
+
+Gahrns Informational [Page 6]
+
+RFC 2180 IMAP4 Multi-Accessed Mailbox Practice July 1997
+
+
+4.1.2 The server MAY allow the EXPUNGE of a multi-accessed mailbox,
+ and on subsequent FETCH commands return FETCH responses only for
+ non-expunged messages and a tagged NO.
+
+ After receiving a tagged NO FETCH response, the client SHOULD issue a
+ NOOP command so that it will be informed of any pending EXPUNGE
+ responses. The client may then either reissue the failed FETCH
+ command, or by examining the EXPUNGE response from the NOOP and the
+ FETCH response from the FETCH, determine that the FETCH failed
+ because of pending expunges.
+
+ Example: (Building upon the scenario outlined in 4.1.)
+
+ <Client #2 attempts to FETCH a mix of expunged and non-expunged
+ messages. A FETCH response is returned only for then non-expunged
+ messages along with a tagged NO>
+
+ C2: B001 FETCH 3:5 ENVELOPE
+ S2: * 3 FETCH ENVELOPE . . . (ENVELOPE info returned)
+ S2: B001 NO Some of the requested messages no longer exist
+
+ <Upon receiving a tagged NO FETCH response, Client #2 issues a NOOP
+ to be informed of any pending EXPUNGE responses>
+
+ C2: B002 NOOP
+ S2: * 4 EXPUNGE
+ S2: * 4 EXPUNGE
+ S2: * 4 EXPUNGE
+ S2: * 4 EXPUNGE
+ S2: * 3 EXISTS
+ S2: B002 OK NOOP Completed.
+
+ <By receiving a FETCH response for message 3, and an EXPUNGE response
+ that indicates messages 4:7 have been expunged, the client does not
+ need to re-issue the FETCH>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Gahrns Informational [Page 7]
+
+RFC 2180 IMAP4 Multi-Accessed Mailbox Practice July 1997
+
+
+4.1.3 The server MAY allow the EXPUNGE of a multi-accessed mailbox, and
+ on subsequent FETCH commands return the usual FETCH responses for
+ non-expunged messages, "NIL FETCH Responses" for expunged
+ messages, and a tagged OK response.
+
+ If all of the messages in the subsequent FETCH command have been
+ expunged, the server SHOULD return only a tagged NO. In this case,
+ the client SHOULD issue a NOOP command so that it will be informed of
+ any pending EXPUNGE responses. The client may then either reissue
+ the failed FETCH command, or by examining the EXPUNGE response from
+ the NOOP, determine that the FETCH failed because of pending
+ expunges.
+
+ "NIL FETCH responses" are a representation of empty data as
+ appropriate for the FETCH argument specified.
+
+ Example:
+
+ * 1 FETCH (ENVELOPE (NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL))
+ * 1 FETCH (FLAGS ())
+ * 1 FETCH (INTERNALDATE "00-Jan-0000 00:00:00 +0000")
+ * 1 FETCH (RFC822 "")
+ * 1 FETCH (RFC822.HEADER "")
+ * 1 FETCH (RFC822.TEXT "")
+ * 1 FETCH (RFC822.SIZE 0)
+ * 1 FETCH (BODY ("TEXT" "PLAIN" NIL NIL NIL "7BIT" 0 0)
+ * 1 FETCH (BODYSTRUCTURE ("TEXT" "PLAIN" NIL NIL NIL "7BIT" 0 0)
+ * 1 FETCH (BODY[<section>] "")
+ * 1 FETCH (BODY[<section>]<partial> "")
+
+ In some cases, a client may not be able to distinguish between "NIL
+ FETCH responses" received because a message was expunged and those
+ received because the data actually was NIL. For example, a * 5
+ FETCH (FLAGS ()) response could be received if no flags were set on
+ message 5, or because message 5 was expunged. In a case of potential
+ ambiguity, the client SHOULD issue a command such as NOOP to force
+ the sending of the EXPUNGE responses to resolve any ambiguity.
+
+ Example: (Building upon the scenario outlined in 4.1.)
+
+ <Client #2 attempts to access a mix of expunged and non-expunged
+ messages. Normal data is returned for non-expunged message, "NIL
+ FETCH responses" are returned for expunged messages>
+
+
+
+
+
+
+
+
+Gahrns Informational [Page 8]
+
+RFC 2180 IMAP4 Multi-Accessed Mailbox Practice July 1997
+
+
+ C2: B002 FETCH 3:5 ENVELOPE
+ S2: * 3 FETCH ENVELOPE . . . (ENVELOPE info returned)
+ S2: * 4 FETCH ENVELOPE (NIL NIL NIL NIL NIL NIL NIL NIL
+ NIL NIL)
+ S2: * 5 FETCH ENVELOPE (NIL NIL NIL NIL NIL NIL NIL NIL
+ NIL NIL)
+ S2: B002 OK FETCH Completed
+
+ <Client #2 attempts to FETCH only expunged messages and receives a
+ tagged NO response>
+
+ C2: B002 FETCH 4:7 ENVELOPE
+ S2: B002 NO Messages 4:7 have been expunged.
+
+
+4.1.4 To avoid the situation altogether, the server MAY fail the
+ EXPUNGE of a multi-accessed mailbox
+
+ In some cases, this behavior may not be practical. For example, if a
+ large number of clients are accessing a shared mailbox, the window in
+ which no clients have the mailbox accessed may be small or non-
+ existent, effectively rendering the message unexpungeable.
+
+
+4.2. Storing of expunged messages
+
+ Following are some strategies an IMAP server may choose to use when
+ dealing with a STORE command on expunged messages.
+
+
+4.2.1 If the ".SILENT" suffix is used, and the STORE completed
+ successfully for all the non-expunged messages, the server SHOULD
+ return a tagged OK.
+
+ Example: (Building upon the scenario outlined in 4.1.)
+
+ <Client #2 tries to silently STORE flags on expunged and non-
+ expunged messages. The server sets the flags on the non-expunged
+ messages and returns OK>
+
+ C2: B001 STORE 1:7 +FLAGS.SILENT (\SEEN)
+ S2: B001 OK
+
+
+
+
+
+
+
+
+
+Gahrns Informational [Page 9]
+
+RFC 2180 IMAP4 Multi-Accessed Mailbox Practice July 1997
+
+
+4.2.2. If the ".SILENT" suffix is not used, and only expunged messages
+ are referenced, the server SHOULD return only a tagged NO.
+
+ Example: (Building upon the scenario outlined in 4.1.)
+
+ <Client #2 tries to STORE flags only on expunged messages>
+
+ C2: B001 STORE 5:7 +FLAGS (\SEEN)
+ S2: B001 NO Messages have been expunged
+
+
+4.2.3. If the ".SILENT" suffix is not used, and a mixture of expunged
+ and non-expunged messages are referenced, the server MAY set the
+ flags and return a FETCH response for the non-expunged messages
+ along with a tagged NO.
+
+ After receiving a tagged NO STORE response, the client SHOULD issue a
+ NOOP command so that it will be informed of any pending EXPUNGE
+ responses. The client may then either reissue the failed STORE
+ command, or by examining the EXPUNGE responses from the NOOP and
+ FETCH responses from the STORE, determine that the STORE failed
+ because of pending expunges.
+
+ Example: (Building upon the scenario outlined in 4.1.)
+
+ <Client #2 tries to STORE flags on a mixture of expunged and non-
+ expunged messages>
+
+ C2: B001 STORE 1:7 +FLAGS (\SEEN)
+ S2: * FETCH 1 FLAGS (\SEEN)
+ S2: * FETCH 2 FLAGS (\SEEN)
+ S2: * FETCH 3 FLAGS (\SEEN)
+ S2: B001 NO Some of the messages no longer exist.
+
+ C2: B002 NOOP
+ S2: * 4 EXPUNGE
+ S2: * 4 EXPUNGE
+ S2: * 4 EXPUNGE
+ S2: * 4 EXPUNGE
+ S2: * 3 EXISTS
+ S2: B002 OK NOOP Completed.
+
+ <By receiving FETCH responses for messages 1:3, and an EXPUNGE
+ response that indicates messages 4:7 have been expunged, the client
+ does not need to re-issue the STORE>
+
+
+
+
+
+
+Gahrns Informational [Page 10]
+
+RFC 2180 IMAP4 Multi-Accessed Mailbox Practice July 1997
+
+
+4.2.4. If the ".SILENT" suffix is not used, and a mixture of expunged
+ and non-expunged messages are referenced, the server MAY return
+ an untagged NO and not set any flags.
+
+ After receiving a tagged NO STORE response, the client SHOULD issue a
+ NOOP command so that it will be informed of any pending EXPUNGE
+ responses. The client would then re-issue the STORE command after
+ updating its message list per any EXPUNGE response.
+
+ If a large number of clients are accessing a shared mailbox, the
+ window in which there are no pending expunges may be small or non-
+ existent, effectively disallowing a client from setting the flags on
+ all messages at once.
+
+ Example: (Building upon the scenario outlined in 4.1.)
+
+ <Client #2 tries to STORE flags on a mixture of expunged and non-
+ expunged messages>
+
+ C2: B001 STORE 1:7 +FLAGS (\SEEN)
+ S2: B001 NO Some of the messages no longer exist.
+
+ <Client #2 issues a NOOP to be informed of the EXPUNGED messages>
+
+ C2: B002 NOOP
+ S2: * 4 EXPUNGE
+ S2: * 4 EXPUNGE
+ S2: * 4 EXPUNGE
+ S2: * 4 EXPUNGE
+ S2: * 3 EXISTS
+ S2: B002 OK NOOP Completed.
+
+ <Client #2 updates its message list and re-issues the STORE on only
+ those messages that have not been expunged>
+
+ C2: B003 STORE 1:3 +FLAGS (\SEEN) S2: * FETCH 1 FLAGS
+ (\SEEN) S2: * FETCH 2 FLAGS (\SEEN) S2: * FETCH 3 FLAGS
+ (\SEEN) S2: B003 OK STORE Completed
+
+
+4.3. Searching of expunged messages
+
+ A server MAY simply not return a search response for messages that
+ have been expunged and it has not been able to inform the client
+ about. If a client was expecting a particular message to be returned
+ in a search result, and it was not, the client SHOULD issue a NOOP
+ command to see if the message was expunged by another client.
+
+
+
+
+Gahrns Informational [Page 11]
+
+RFC 2180 IMAP4 Multi-Accessed Mailbox Practice July 1997
+
+
+4.4 Copying of expunged messages
+
+ COPY is the only IMAP4 sequence number command that is safe to allow
+ an EXPUNGE response on. This is because a client is not permitted to
+ cascade several COPY commands together. A client is required to wait
+ and confirm that the copy worked before issuing another one.
+
+4.4.1 The server MAY disallow the COPY of messages in a multi-access
+ mailbox that contains expunged messages.
+
+ Pending EXPUNGE response(s) MUST be returned to the COPY command.
+
+ Example:
+
+ C: A001 COPY 2,4,6,8 FRED
+ S: * 4 EXPUNGE
+ S: A001 NO COPY rejected, because some of the requested
+ messages were expunged
+
+ Note: Non of the above messages are copied because if a COPY command
+ is unsuccessful, the server MUST restore the destination mailbox to
+ its state before the COPY attempt.
+
+
+4.4.2 The server MAY allow the COPY of messages in a multi-access
+ mailbox that contains expunged messages.
+
+ Pending EXPUNGE response(s) MUST be returned to the COPY command.
+ Messages that are copied are messages corresponding to sequence
+ numbers before any EXPUNGE response.
+
+ Example:
+
+ C: A001 COPY 2,4,6,8 FRED
+ S: * 3 EXPUNGE
+ S: A001 OK COPY completed
+
+ In the above example, the messages that are copied to FRED are
+ messages 2,4,6,8 at the start of the COPY command. These are
+ equivalent to messages 2,3,5,7 at the end of the COPY command. The
+ EXPUNGE response can't take place until after the messages from the
+ COPY command are identified (because of the "no expunge while no
+ commands in progress" rule).
+
+
+
+
+
+
+
+
+Gahrns Informational [Page 12]
+
+RFC 2180 IMAP4 Multi-Accessed Mailbox Practice July 1997
+
+
+ Example:
+
+ C: A001 COPY 2,4,6,8 FRED
+ S: * 4 EXPUNGE
+ S: A001 OK COPY completed
+
+ In the above example, message 4 was copied before it was expunged,
+ and MUST appear in the destination mailbox FRED.
+
+
+5. Security Considerations
+
+ This document describes behavior of servers that use the IMAP4
+ protocol, and as such, has the same security considerations as
+ described in [RFC-2060].
+
+ In particular, some described server behavior does not allow for the
+ immediate deletion of information when a mailbox is accessed by
+ multiple clients. This may be a consideration when dealing with
+ sensitive information where immediate deletion would be preferred.
+
+
+6. References
+
+ [RFC-2060], Crispin, M., "Internet Message Access Protocol - Version
+ 4rev1", RFC 2060, University of Washington, December 1996.
+
+ [RFC-2119], Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", RFC 2119, Harvard University, March 1997.
+
+
+7. Acknowledgments
+
+ This document is the result of discussions on the IMAP4 mailing list
+ and is meant to reflect consensus of this group. In particular,
+ Raymond Cheng, Mark Crispin, Jim Evans, Erik Forsberg, Steve Hole,
+ Mark Keasling, Barry Leiba, Syd Logan, John Mani, Pat Moran, Larry
+ Osterman, Chris Newman, Bart Schaefer, Vladimir Vulovic, and Jack De
+ Winter were active participants in this discussion or made
+ suggestions to this document.
+
+
+
+
+
+
+
+
+
+
+
+Gahrns Informational [Page 13]
+
+RFC 2180 IMAP4 Multi-Accessed Mailbox Practice July 1997
+
+
+8. Author's Address
+
+ Mike Gahrns
+ Microsoft
+ One Microsoft Way
+ Redmond, WA, 98072
+
+ Phone: (206) 936-9833
+ EMail: mikega@microsoft.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Gahrns Informational [Page 14]
+
diff --git a/imap/docs/rfc/rfc2193.txt b/imap/docs/rfc/rfc2193.txt
new file mode 100644
index 00000000..2fec58d7
--- /dev/null
+++ b/imap/docs/rfc/rfc2193.txt
@@ -0,0 +1,507 @@
+
+
+
+
+
+
+Network Working Group M. Gahrns
+Request for Comments: 2193 Microsoft
+Category: Standards Track September 1997
+
+
+ IMAP4 Mailbox Referrals
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+1. Abstract
+
+ When dealing with large amounts of users, messages and geographically
+ dispersed IMAP4 [RFC-2060] servers, it is often desirable to
+ distribute messages amongst different servers within an organization.
+ For example an administrator may choose to store user's personal
+ mailboxes on a local IMAP4 server, while storing shared mailboxes
+ remotely on another server. This type of configuration is common
+ when it is uneconomical to store all data centrally due to limited
+ bandwidth or disk resources.
+
+ Mailbox referrals allow clients to seamlessly access mailboxes that
+ are distributed across several IMAP4 servers.
+
+ A referral mechanism can provide efficiencies over the alternative
+ "proxy method", in which the local IMAP4 server contacts the remote
+ server on behalf of the client, and then transfers the data from the
+ remote server to itself, and then on to the client. The referral
+ mechanism's direct client connection to the remote server is often a
+ more efficient use of bandwidth, and does not require the local
+ server to impersonate the client when authenticating to the remote
+ server.
+
+2. Conventions used in this document
+
+ In examples, "C:" and "S:" indicate lines sent by the client and
+ server respectively.
+
+ A home server, is an IMAP4 server that contains the user's inbox.
+
+ A remote mailbox is a mailbox that is not hosted on the user's home
+ server.
+
+
+
+
+Gahrns Standards Track [Page 1]
+
+RFC 2193 IMAP4 Mailbox Referrals September 1997
+
+
+ A remote server is a server that contains remote mailboxes.
+
+ A shared mailbox, is a mailbox that multiple users have access to.
+
+ An IMAP mailbox referral is when the server directs the client to
+ another IMAP mailbox.
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in [RFC-2119].
+
+3. Introduction and Overview
+
+ IMAP4 servers that support this extension MUST list the keyword
+ MAILBOX-REFERRALS in their CAPABILITY response. No client action is
+ needed to invoke the MAILBOX-REFERRALS capability in a server.
+
+ A MAILBOX-REFERRALS capable IMAP4 server MUST NOT return referrals
+ that result in a referrals loop.
+
+ A referral response consists of a tagged NO response and a REFERRAL
+ response code. The REFERRAL response code MUST contain as an
+ argument a one or more valid URLs separated by a space as defined in
+ [RFC-1738]. If a server replies with multiple URLs for a particular
+ object, they MUST all be of the same type. In this case, the URL MUST
+ be an IMAP URL as defined in [RFC-2192]. A client that supports the
+ REFERRALS extension MUST be prepared for a URL of any type, but it
+ need only be able to process IMAP URLs.
+
+ A server MAY respond with multiple IMAP mailbox referrals if there is
+ more than one replica of the mailbox. This allows the implementation
+ of a load balancing or failover scheme. How a server keeps multiple
+ replicas of a mailbox in sync is not addressed by this document.
+
+ If the server has a preferred order in which the client should
+ attempt to access the URLs, the preferred URL SHOULD be listed in the
+ first, with the remaining URLs presented in descending order of
+ preference. If multiple referrals are given for a mailbox, a server
+ should be aware that there are synchronization issues for a client if
+ the UIDVALIDITY of the referred mailboxes are different.
+
+ An IMAP mailbox referral may be given in response to an IMAP command
+ that specifies a mailbox as an argument.
+
+
+
+
+
+
+
+
+Gahrns Standards Track [Page 2]
+
+RFC 2193 IMAP4 Mailbox Referrals September 1997
+
+
+ Example:
+
+ A001 NO [REFERRAL IMAP://user;AUTH=*@SERVER2/REMOTE]Remote Mailbox
+
+ NOTE: user;AUTH=* is specified as required by [RFC-2192] to avoid a
+ client falling back to anonymous login.
+
+ Remote mailboxes and their inferiors, that are accessible only via
+ referrals SHOULD NOT appear in LIST and LSUB responses issued against
+ the user's home server. They MUST appear in RLIST and RLSUB
+ responses issued against the user's home server. Hierarchy referrals,
+ in which a client would be required to connect to the remote server
+ to issue a LIST to discover the inferiors of a mailbox are not
+ addressed in this document.
+
+ For example, if shared mailboxes were only accessible via referrals
+ on a remote server, a RLIST "" "#SHARED/%" command would return the
+ same response if issued against the user's home server or the remote
+ server.
+
+ Note: Mailboxes that are available on the user's home server do not
+ need to be available on the remote server. In addition, there may be
+ additional mailboxes available on the remote server, but they will
+ not accessible to the client via referrals unless they appear in the
+ LIST response to the RLIST command against the user's home server.
+
+ A MAILBOX-REFERRALS capable client will issue the RLIST and RLSUB
+ commands in lieu of LIST and LSUB. The RLIST and RLSUB commands
+ behave identically to their LIST and LSUB counterparts, except remote
+ mailboxes are returned in addition to local mailboxes in the LIST and
+ LSUB responses. This avoids displaying to a non MAILBOX-REFERRALS
+ enabled client inaccessible remote mailboxes.
+
+4.1. SELECT, EXAMINE, DELETE, SUBSCRIBE, UNSUBSCRIBE, STATUS and APPEND
+ Referrals
+
+ An IMAP4 server MAY respond to the SELECT, EXAMINE, DELETE,
+ SUBSCRIBE, UNSUBSCRIBE, STATUS or APPEND command with one or more
+ IMAP mailbox referrals to indicate to the client that the mailbox is
+ hosted on a remote server.
+
+ When a client processes an IMAP mailbox referral, it will open a new
+ connection or use an existing connection to the remote server so that
+ it is able to issue the commands necessary to process the remote
+ mailbox.
+
+
+
+
+
+
+Gahrns Standards Track [Page 3]
+
+RFC 2193 IMAP4 Mailbox Referrals September 1997
+
+
+ Example: <IMAP4 connection to home server>
+
+ C: A001 DELETE "SHARED/FOO"
+ S: A001 NO [REFERRAL IMAP://user;AUTH=*@SERVER2/SHARED/FOO]
+ Remote mailbox. Try SERVER2.
+
+ <Client established a second connection to SERVER2 and
+ issues the DELETE command on the referred mailbox>
+
+ S: * OK IMAP4rev1 server ready
+ C: B001 AUTHENTICATE KERBEROS_V4
+ <authentication exchange>
+ S: B001 OK user is authenticated
+
+ C: B002 DELETE "SHARED/FOO"
+ S: B002 OK DELETE completed
+
+ Example: <IMAP4 connection to home server>
+
+ C: A001 SELECT REMOTE
+ S: A001 NO [REFERRAL IMAP://user;AUTH=*@SERVER2/REMOTE
+ IMAP://user;AUTH=*@SERVER3/REMOTE] Remote mailbox.
+ Try SERVER2 or SERVER3.
+
+ <Client opens second connection to remote server, and
+ issues the commands needed to process the items in the
+ remote mailbox>
+
+ S: * OK IMAP4rev1 server ready
+ C: B001 AUTHENTICATE KERBEROS_V4
+ <authentication exchange>
+ S: B001 OK user is authenticated
+
+ C: B002 SELECT REMOTE
+ S: * 12 EXISTS
+ S: * 1 RECENT
+ S: * OK [UNSEEN 10] Message 10 is first unseen
+ S: * OK [UIDVALIDITY 123456789]
+ S: * FLAGS (Answered Flagged Deleted Seen Draft)
+ S: * OK [PERMANENTFLAGS (Answered Deleted Seen ]
+ S: B002 OK [READ-WRITE] Selected completed
+
+ C: B003 FETCH 10:12 RFC822
+ S: * 10 FETCH . . .
+ S: * 11 FETCH . . .
+ S: * 12 FETCH . . .
+ S: B003 OK FETCH Completed
+
+
+
+
+Gahrns Standards Track [Page 4]
+
+RFC 2193 IMAP4 Mailbox Referrals September 1997
+
+
+ <Client is finished processing the REMOTE mailbox and
+ wants to process a mailbox on its home server>
+
+ C: B004 LOGOUT
+ S: * BYE IMAP4rev1 server logging out
+ S: B004 OK LOGOUT Completed
+
+ <Client continues with first connection>
+
+ C: A002 SELECT INBOX
+ S: * 16 EXISTS
+ S: * 2 RECENT
+ S: * OK [UNSEEN 10] Message 10 is first unseen
+ S: * OK [UIDVALIDITY 123456789]
+ S: * FLAGS (Answered Flagged Deleted Seen Draft)
+ S: * OK [PERMANENTFLAGS (Answered Deleted Seen ]
+ S: A002 OK [READ-WRITE] Selected completed
+
+4.2. CREATE Referrals
+
+ An IMAP4 server MAY respond to the CREATE command with one or more
+ IMAP mailbox referrals, if it wishes to direct the client to issue
+ the CREATE against another server. The server can employ any means,
+ such as examining the hierarchy of the specified mailbox name, in
+ determining which server the mailbox should be created on.
+
+ Example:
+
+ C: A001 CREATE "SHARED/FOO"
+ S: A001 NO [REFERRAL IMAP://user;AUTH=*@SERVER2/SHARED/FOO]
+ Mailbox should be created on remote server
+
+ Alternatively, because a home server is required to maintain a
+ listing of referred remote mailboxes, a server MAY allow the creation
+ of a mailbox that will ultimately reside on a remote server against
+ the home server, and provide referrals on subsequent commands that
+ manipulate the mailbox.
+
+ Example:
+
+ C: A001 CREATE "SHARED/FOO"
+ S: A001 OK CREATE succeeded
+
+ C: A002 SELECT "SHARED/FOO"
+ S: A002 NO [REFERRAL IMAP://user;AUTH=*@SERVER2/SHARED/FOO]
+ Remote mailbox. Try SERVER2
+
+
+
+
+
+Gahrns Standards Track [Page 5]
+
+RFC 2193 IMAP4 Mailbox Referrals September 1997
+
+
+4.3. RENAME Referrals
+
+ An IMAP4 server MAY respond to the RENAME command with one or more
+ pairs of IMAP mailbox referrals. In each pair of IMAP mailbox
+ referrals, the first one is an URL to the existing mailbox name and
+ the second is an URL to the requested new mailbox name.
+
+ If within an IMAP mailbox referral pair, the existing and new mailbox
+ URLs are on different servers, the remote servers are unable to
+ perform the RENAME operation. To achieve the same behavior of
+ server RENAME, the client MAY issue the constituent CREATE, FETCH,
+ APPEND, and DELETE commands against both servers.
+
+ If within an IMAP mailbox referral pair, the existing and new mailbox
+ URLs are on the same server it is an indication that the currently
+ connected server is unable to perform the operation. The client can
+ simply re-issue the RENAME command on the remote server.
+
+ Example:
+
+ C: A001 RENAME FOO BAR
+ S: A001 NO [REFERRAL IMAP://user;AUTH=*@SERVER1/FOO
+ IMAP://user;AUTH=*@SERVER2/BAR] Unable to rename mailbox
+ across servers
+
+ Since the existing and new mailbox names are on different servers,
+ the client would be required to make a connection to both servers and
+ issue the constituent commands require to achieve the RENAME.
+
+ Example:
+
+ C: A001 RENAME FOO BAR
+ S: A001 NO [REFERRAL IMAP://user;AUTH=*@SERVER2/FOO
+ IMAP://user;AUTH=*@SERVER2/BAR] Unable to rename mailbox
+ located on SERVER2
+
+ Since both the existing and new mailbox are on the same remote
+ server, the client can simply make a connection to the remote server
+ and re-issue the RENAME command.
+
+4.4. COPY Referrals
+
+ An IMAP4 server MAY respond to the COPY command with one or more IMAP
+ mailbox referrals. This indicates that the destination mailbox is on
+ a remote server. To achieve the same behavior of a server COPY, the
+ client MAY issue the constituent FETCH and APPEND commands against
+ both servers.
+
+
+
+
+Gahrns Standards Track [Page 6]
+
+RFC 2193 IMAP4 Mailbox Referrals September 1997
+
+
+ Example:
+
+ C: A001 COPY 1 "SHARED/STUFF"
+ S: A001 NO [REFERRAL IMAP://user;AUTH=*@SERVER2/SHARED/STUFF]
+ Unable to copy message(s) to SERVER2.
+
+5.1 RLIST command
+
+ Arguments: reference name
+ mailbox name with possible wildcards
+
+ Responses: untagged responses: LIST
+
+ Result: OK - RLIST Completed
+ NO - RLIST Failure
+ BAD - command unknown or arguments invalid
+
+ The RLIST command behaves identically to its LIST counterpart, except
+ remote mailboxes are returned in addition to local mailboxes in the
+ LIST responses.
+
+5.2 RLSUB Command
+
+ Arguments: reference name
+ mailbox name with possible wildcards
+
+ Responses: untagged responses: LSUB
+
+ Result: OK - RLSUB Completed
+ NO - RLSUB Failure
+ BAD - command unknown or arguments invalid
+
+ The RLSUB command behaves identically to its LSUB counterpart, except
+ remote mailboxes are returned in addition to local mailboxes in the
+ LSUB responses.
+
+6. Formal Syntax
+
+ The following syntax specification uses the augmented Backus-Naur
+ Form (BNF) as described in [ABNF].
+
+ list_mailbox = <list_mailbox> as defined in [RFC-2060]
+
+ mailbox = <mailbox> as defined in [RFC-2060]
+
+ mailbox_referral = <tag> SPACE "NO" SPACE
+ <referral_response_code> (text / text_mime2)
+ ; See [RFC-2060] for <tag>, text and text_mime2 definition
+
+
+
+Gahrns Standards Track [Page 7]
+
+RFC 2193 IMAP4 Mailbox Referrals September 1997
+
+
+ referral_response_code = "[" "REFERRAL" 1*(SPACE <url>) "]"
+ ; See [RFC-1738] for <url> definition
+
+ rlist = "RLIST" SPACE mailbox SPACE list_mailbox
+
+ rlsub = "RLSUB" SPACE mailbox SPACE list_mailbox
+
+6. Security Considerations
+
+ The IMAP4 referral mechanism makes use of IMAP URLs, and as such,
+ have the same security considerations as general internet URLs [RFC-
+ 1738], and in particular IMAP URLs [RFC-2192].
+
+ With the MAILBOX-REFERRALS capability, it is potentially easier to
+ write a rogue server that injects a bogus referral response that
+ directs a user to an incorrect mailbox. Although referrals reduce
+ the effort to write such a server, the referral response makes
+ detection of the intrusion easier.
+
+7. References
+
+ [RFC-2060], Crispin, M., "Internet Message Access Protocol - Version
+ 4rev1", RFC 2060, University of Washington, December 1996.
+
+ [RFC-2192], Newman, C., "IMAP URL Scheme", RFC 2192, Innosoft,
+ September 1997.
+
+ [RFC-1738], Berners-Lee, T., Masinter, L., and M. McCahill, "Uniform
+ Resource Locators (URL)", RFC 1738, CERN, Xerox Corporation,
+ University of Minnesota, December 1994.
+
+ [RFC-2119], Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", RFC 2119, Harvard University, March 1997.
+
+ [ABNF], DRUMS working group, Dave Crocker Editor, "Augmented BNF for
+ Syntax Specifications: ABNF", Work in Progress, Internet Mail
+ Consortium, April 1997.
+
+8. Acknowledgments
+
+ Many valuable suggestions were received from private discussions and
+ the IMAP4 mailing list. In particular, Raymond Cheng, Mark Crispin,
+ Mark Keasling, Chris Newman and Larry Osterman made significant
+ contributions to this document.
+
+
+
+
+
+
+
+Gahrns Standards Track [Page 8]
+
+RFC 2193 IMAP4 Mailbox Referrals September 1997
+
+
+9. Author's Address
+
+ Mike Gahrns
+ Microsoft
+ One Microsoft Way
+ Redmond, WA, 98072
+
+ Phone: (206) 936-9833
+ EMail: mikega@microsoft.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Gahrns Standards Track [Page 9]
+
diff --git a/imap/docs/rfc/rfc2195.txt b/imap/docs/rfc/rfc2195.txt
new file mode 100644
index 00000000..4a2725bf
--- /dev/null
+++ b/imap/docs/rfc/rfc2195.txt
@@ -0,0 +1,283 @@
+
+
+
+
+
+
+Network Working Group J. Klensin
+Request for Comments: 2195 R. Catoe
+Category: Standards Track P. Krumviede
+Obsoletes: 2095 MCI
+ September 1997
+
+
+ IMAP/POP AUTHorize Extension for Simple Challenge/Response
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Abstract
+
+ While IMAP4 supports a number of strong authentication mechanisms as
+ described in RFC 1731, it lacks any mechanism that neither passes
+ cleartext, reusable passwords across the network nor requires either
+ a significant security infrastructure or that the mail server update
+ a mail-system-wide user authentication file on each mail access.
+ This specification provides a simple challenge-response
+ authentication protocol that is suitable for use with IMAP4. Since
+ it utilizes Keyed-MD5 digests and does not require that the secret be
+ stored in the clear on the server, it may also constitute an
+ improvement on APOP for POP3 use as specified in RFC 1734.
+
+1. Introduction
+
+ Existing Proposed Standards specify an AUTHENTICATE mechanism for the
+ IMAP4 protocol [IMAP, IMAP-AUTH] and a parallel AUTH mechanism for
+ the POP3 protocol [POP3-AUTH]. The AUTHENTICATE mechanism is
+ intended to be extensible; the four methods specified in [IMAP-AUTH]
+ are all fairly powerful and require some security infrastructure to
+ support. The base POP3 specification [POP3] also contains a
+ lightweight challenge-response mechanism called APOP. APOP is
+ associated with most of the risks associated with such protocols: in
+ particular, it requires that both the client and server machines have
+ access to the shared secret in cleartext form. CRAM offers a method
+ for avoiding such cleartext storage while retaining the algorithmic
+ simplicity of APOP in using only MD5, though in a "keyed" method.
+
+
+
+
+
+
+
+Klensin, Catoe & Krumviede Standards Track [Page 1]
+
+RFC 2195 IMAP/POP AUTHorize Extension September 1997
+
+
+ At present, IMAP [IMAP] lacks any facility corresponding to APOP.
+ The only alternative to the strong mechanisms identified in [IMAP-
+ AUTH] is a presumably cleartext username and password, supported
+ through the LOGIN command in [IMAP]. This document describes a
+ simple challenge-response mechanism, similar to APOP and PPP CHAP
+ [PPP], that can be used with IMAP (and, in principle, with POP3).
+
+ This mechanism also has the advantage over some possible alternatives
+ of not requiring that the server maintain information about email
+ "logins" on a per-login basis. While mechanisms that do require such
+ per-login history records may offer enhanced security, protocols such
+ as IMAP, which may have several connections between a given client
+ and server open more or less simultaneous, may make their
+ implementation particularly challenging.
+
+2. Challenge-Response Authentication Mechanism (CRAM)
+
+ The authentication type associated with CRAM is "CRAM-MD5".
+
+ The data encoded in the first ready response contains an
+ presumptively arbitrary string of random digits, a timestamp, and the
+ fully-qualified primary host name of the server. The syntax of the
+ unencoded form must correspond to that of an RFC 822 'msg-id'
+ [RFC822] as described in [POP3].
+
+ The client makes note of the data and then responds with a string
+ consisting of the user name, a space, and a 'digest'. The latter is
+ computed by applying the keyed MD5 algorithm from [KEYED-MD5] where
+ the key is a shared secret and the digested text is the timestamp
+ (including angle-brackets).
+
+ This shared secret is a string known only to the client and server.
+ The `digest' parameter itself is a 16-octet value which is sent in
+ hexadecimal format, using lower-case ASCII characters.
+
+ When the server receives this client response, it verifies the digest
+ provided. If the digest is correct, the server should consider the
+ client authenticated and respond appropriately.
+
+ Keyed MD5 is chosen for this application because of the greater
+ security imparted to authentication of short messages. In addition,
+ the use of the techniques described in [KEYED-MD5] for precomputation
+ of intermediate results make it possible to avoid explicit cleartext
+ storage of the shared secret on the server system by instead storing
+ the intermediate results which are known as "contexts".
+
+
+
+
+
+
+Klensin, Catoe & Krumviede Standards Track [Page 2]
+
+RFC 2195 IMAP/POP AUTHorize Extension September 1997
+
+
+ CRAM does not support a protection mechanism.
+
+ Example:
+
+ The examples in this document show the use of the CRAM mechanism with
+ the IMAP4 AUTHENTICATE command [IMAP-AUTH]. The base64 encoding of
+ the challenges and responses is part of the IMAP4 AUTHENTICATE
+ command, not part of the CRAM specification itself.
+
+ S: * OK IMAP4 Server
+ C: A0001 AUTHENTICATE CRAM-MD5
+ S: + PDE4OTYuNjk3MTcwOTUyQHBvc3RvZmZpY2UucmVzdG9uLm1jaS5uZXQ+
+ C: dGltIGI5MTNhNjAyYzdlZGE3YTQ5NWI0ZTZlNzMzNGQzODkw
+ S: A0001 OK CRAM authentication successful
+
+ In this example, the shared secret is the string
+ 'tanstaaftanstaaf'. Hence, the Keyed MD5 digest is produced by
+ calculating
+
+ MD5((tanstaaftanstaaf XOR opad),
+ MD5((tanstaaftanstaaf XOR ipad),
+ <1896.697170952@postoffice.reston.mci.net>))
+
+ where ipad and opad are as defined in the keyed-MD5 Work in
+ Progress [KEYED-MD5] and the string shown in the challenge is the
+ base64 encoding of <1896.697170952@postoffice.reston.mci.net>. The
+ shared secret is null-padded to a length of 64 bytes. If the
+ shared secret is longer than 64 bytes, the MD5 digest of the
+ shared secret is used as a 16 byte input to the keyed MD5
+ calculation.
+
+ This produces a digest value (in hexadecimal) of
+
+ b913a602c7eda7a495b4e6e7334d3890
+
+ The user name is then prepended to it, forming
+
+ tim b913a602c7eda7a495b4e6e7334d3890
+
+ Which is then base64 encoded to meet the requirements of the IMAP4
+ AUTHENTICATE command (or the similar POP3 AUTH command), yielding
+
+ dGltIGI5MTNhNjAyYzdlZGE3YTQ5NWI0ZTZlNzMzNGQzODkw
+
+
+
+
+
+
+
+
+Klensin, Catoe & Krumviede Standards Track [Page 3]
+
+RFC 2195 IMAP/POP AUTHorize Extension September 1997
+
+
+3. References
+
+ [CHAP] Lloyd, B., and W. Simpson, "PPP Authentication Protocols",
+ RFC 1334, October 1992.
+
+ [IMAP] Crispin, M., "Internet Message Access Protocol - Version
+ 4rev1", RFC 2060, University of Washington, December 1996.
+
+ [IMAP-AUTH] Myers, J., "IMAP4 Authentication Mechanisms",
+ RFC 1731, Carnegie Mellon, December 1994.
+
+ [KEYED-MD5] Krawczyk, Bellare, Canetti, "HMAC: Keyed-Hashing for
+ Message Authentication", RFC 2104, February 1997.
+
+ [MD5] Rivest, R., "The MD5 Message Digest Algorithm",
+ RFC 1321, MIT Laboratory for Computer Science, April 1992.
+
+ [POP3] Myers, J., and M. Rose, "Post Office Protocol - Version 3",
+ STD 53, RFC 1939, Carnegie Mellon, May 1996.
+
+ [POP3-AUTH] Myers, J., "POP3 AUTHentication command", RFC 1734,
+ Carnegie Mellon, December, 1994.
+
+4. Security Considerations
+
+ It is conjectured that use of the CRAM authentication mechanism
+ provides origin identification and replay protection for a session.
+ Accordingly, a server that implements both a cleartext password
+ command and this authentication type should not allow both methods of
+ access for a given user.
+
+ While the saving, on the server, of "contexts" (see section 2) is
+ marginally better than saving the shared secrets in cleartext as is
+ required by CHAP [CHAP] and APOP [POP3], it is not sufficient to
+ protect the secrets if the server itself is compromised.
+ Consequently, servers that store the secrets or contexts must both be
+ protected to a level appropriate to the potential information value
+ in user mailboxes and identities.
+
+ As the length of the shared secret increases, so does the difficulty
+ of deriving it.
+
+ While there are now suggestions in the literature that the use of MD5
+ and keyed MD5 in authentication procedures probably has a limited
+ effective lifetime, the technique is now widely deployed and widely
+ understood. It is believed that this general understanding may
+ assist with the rapid replacement, by CRAM-MD5, of the current uses
+ of permanent cleartext passwords in IMAP. This document has been
+
+
+
+Klensin, Catoe & Krumviede Standards Track [Page 4]
+
+RFC 2195 IMAP/POP AUTHorize Extension September 1997
+
+
+ deliberately written to permit easy upgrading to use SHA (or whatever
+ alternatives emerge) when they are considered to be widely available
+ and adequately safe.
+
+ Even with the use of CRAM, users are still vulnerable to active
+ attacks. An example of an increasingly common active attack is 'TCP
+ Session Hijacking' as described in CERT Advisory CA-95:01 [CERT95].
+
+ See section 1 above for additional discussion.
+
+5. Acknowledgements
+
+ This memo borrows ideas and some text liberally from [POP3] and
+ [RFC-1731] and thanks are due the authors of those documents. Ran
+ Atkinson made a number of valuable technical and editorial
+ contributions to the document.
+
+6. Authors' Addresses
+
+ John C. Klensin
+ MCI Telecommunications
+ 800 Boylston St, 7th floor
+ Boston, MA 02199
+ USA
+
+ EMail: klensin@mci.net
+ Phone: +1 617 960 1011
+
+ Randy Catoe
+ MCI Telecommunications
+ 2100 Reston Parkway
+ Reston, VA 22091
+ USA
+
+ EMail: randy@mci.net
+ Phone: +1 703 715 7366
+
+ Paul Krumviede
+ MCI Telecommunications
+ 2100 Reston Parkway
+ Reston, VA 22091
+ USA
+
+ EMail: paul@mci.net
+ Phone: +1 703 715 7251
+
+
+
+
+
+
+Klensin, Catoe & Krumviede Standards Track [Page 5]
+
diff --git a/imap/docs/rfc/rfc2221.txt b/imap/docs/rfc/rfc2221.txt
new file mode 100644
index 00000000..81d00620
--- /dev/null
+++ b/imap/docs/rfc/rfc2221.txt
@@ -0,0 +1,283 @@
+
+
+
+
+
+
+Network Working Group M. Gahrns
+Request for Comments: 2221 Microsoft
+Category: Standards Track October 1997
+
+
+ IMAP4 Login Referrals
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (1997). All Rights Reserved.
+
+1. Abstract
+
+ When dealing with large amounts of users and many IMAP4 [RFC-2060]
+ servers, it is often necessary to move users from one IMAP4 server to
+ another. For example, hardware failures or organizational changes
+ may dictate such a move.
+
+ Login referrals allow clients to transparently connect to an
+ alternate IMAP4 server, if their home IMAP4 server has changed.
+
+ A referral mechanism can provide efficiencies over the alternative
+ 'proxy method', in which the local IMAP4 server contacts the remote
+ server on behalf of the client, and then transfers the data from the
+ remote server to itself, and then on to the client. The referral
+ mechanism's direct client connection to the remote server is often a
+ more efficient use of bandwidth, and does not require the local
+ server to impersonate the client when authenticating to the remote
+ server.
+
+2. Conventions used in this document
+
+ In examples, "C:" and "S:" indicate lines sent by the client and
+ server respectively.
+
+ A home server, is an IMAP4 server that contains the user's inbox.
+
+ A remote server is a server that contains remote mailboxes.
+
+
+
+
+
+Gahrns Standards Track [Page 1]
+
+RFC 2221 IMAP4 Login Referrals October 1997
+
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in [RFC-2119].
+
+3. Introduction and Overview
+
+ IMAP4 servers that support this extension MUST list the keyword
+ LOGIN-REFERRALS in their CAPABILITY response. No client action is
+ needed to invoke the LOGIN-REFERRALS capability in a server.
+
+ A LOGIN-REFERRALS capable IMAP4 server SHOULD NOT return a referral
+ to a server that will return a referral. A client MUST NOT follow
+ more than 10 levels of referral without consulting the user.
+
+ A LOGIN-REFERRALS response code MUST contain as an argument a valid
+ IMAP server URL as defined in [IMAP-URL].
+
+ A home server referral consists of either a tagged NO or OK, or an
+ untagged BYE response that contains a LOGIN-REFERRALS response code.
+
+ Example: A001 NO [REFERRAL IMAP://user;AUTH=*@SERVER2/] Remote Server
+
+ NOTE: user;AUTH=* is specified as required by [IMAP-URL] to avoid a
+ client falling back to anonymous login.
+
+4. Home Server Referrals
+
+ A home server referral may be returned in response to an AUTHENTICATE
+ or LOGIN command, or it may appear in the connection startup banner.
+ If a server returns a home server referral in a tagged NO response,
+ that server does not contain any mailboxes that are accessible to the
+ user. If a server returns a home server referral in a tagged OK
+ response, it indicates that the user's personal mailboxes are
+ elsewhere, but the server contains public mailboxes which are
+ readable by the user. After receiving a home server referral, the
+ client can not make any assumptions as to whether this was a
+ permanent or temporary move of the user.
+
+4.1. LOGIN and AUTHENTICATE Referrals
+
+ An IMAP4 server MAY respond to a LOGIN or AUTHENTICATE command with a
+ home server referral if it wishes to direct the user to another IMAP4
+ server.
+
+ Example: C: A001 LOGIN MIKE PASSWORD
+ S: A001 NO [REFERRAL IMAP://MIKE@SERVER2/] Specified user
+ is invalid on this server. Try SERVER2.
+
+
+
+
+Gahrns Standards Track [Page 2]
+
+RFC 2221 IMAP4 Login Referrals October 1997
+
+
+ Example: C: A001 LOGIN MATTHEW PASSWORD
+ S: A001 OK [REFERRAL IMAP://MATTHEW@SERVER2/] Specified
+ user's personal mailboxes located on Server2, but
+ public mailboxes are available.
+
+ Example: C: A001 AUTHENTICATE GSSAPI
+ <authentication exchange>
+ S: A001 NO [REFERRAL IMAP://user;AUTH=GSSAPI@SERVER2/]
+ Specified user is invalid on this server. Try
+ SERVER2.
+
+4.2. BYE at connection startup referral
+
+ An IMAP4 server MAY respond with an untagged BYE and a REFERRAL
+ response code that contains an IMAP URL to a home server if it is not
+ willing to accept connections and wishes to direct the client to
+ another IMAP4 server.
+
+ Example: S: * BYE [REFERRAL IMAP://user;AUTH=*@SERVER2/] Server not
+ accepting connections. Try SERVER2
+
+5. Formal Syntax
+
+ The following syntax specification uses the augmented Backus-Naur
+ Form (BNF) as described in [ABNF].
+
+ This amends the "resp_text_code" element of the IMAP4 grammar
+ described in [RFC-2060]
+
+ resp_text_code =/ "REFERRAL" SPACE <imapurl>
+ ; See [IMAP-URL] for definition of <imapurl>
+ ; See [RFC-2060] for base definition of resp_text_code
+
+6. Security Considerations
+
+ The IMAP4 login referral mechanism makes use of IMAP URLs, and as
+ such, have the same security considerations as general internet URLs
+ [RFC-1738], and in particular IMAP URLs [IMAP-URL].
+
+ A server MUST NOT give a login referral if authentication for that
+ user fails. This is to avoid revealing information about the user's
+ account to an unauthorized user.
+
+ With the LOGIN-REFERRALS capability, it is potentially easier to
+ write a rogue 'password catching' server that collects login data and
+ then refers the client to their actual IMAP4 server. Although
+ referrals reduce the effort to write such a server, the referral
+ response makes detection of the intrusion easier.
+
+
+
+Gahrns Standards Track [Page 3]
+
+RFC 2221 IMAP4 Login Referrals October 1997
+
+
+7. References
+
+ [RFC-2060], Crispin, M., "Internet Message Access Protocol - Version
+ 4rev1", RFC 2060, December 1996.
+
+ [IMAP-URL], Newman, C., "IMAP URL Scheme", RFC 2192, Innosoft,
+ September 1997.
+
+ [RFC-1738], Berners-Lee, T., Masinter, L. and M. McCahill, "Uniform
+ Resource Locators (URL)", RFC 1738, December 1994.
+
+ [RFC-2119], Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", RFC 2119, March 1997.
+
+ [ABNF], DRUMS working group, Dave Crocker Editor, "Augmented BNF for
+ Syntax Specifications: ABNF", Work in Progress.
+
+8. Acknowledgments
+
+ Many valuable suggestions were received from private discussions and
+ the IMAP4 mailing list. In particular, Raymond Cheng, Mark Crispin,
+ Mark Keasling Chris Newman and Larry Osterman made significant
+ contributions to this document.
+
+9. Author's Address
+
+ Mike Gahrns
+ Microsoft
+ One Microsoft Way
+ Redmond, WA, 98072
+
+ Phone: (206) 936-9833
+ EMail: mikega@microsoft.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Gahrns Standards Track [Page 4]
+
+RFC 2221 IMAP4 Login Referrals October 1997
+
+
+10. Full Copyright Statement
+
+ Copyright (C) The Internet Society (1997). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implmentation may be prepared, copied, published
+ andand distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE."
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Gahrns Standards Track [Page 5]
+
diff --git a/imap/docs/rfc/rfc2342.txt b/imap/docs/rfc/rfc2342.txt
new file mode 100644
index 00000000..0926646d
--- /dev/null
+++ b/imap/docs/rfc/rfc2342.txt
@@ -0,0 +1,563 @@
+
+
+
+
+
+
+Network Working Group M. Gahrns
+Request for Comments: 2342 Microsoft
+Category: Standards Track C. Newman
+ Innosoft
+ May 1998
+
+
+ IMAP4 Namespace
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (1998). All Rights Reserved.
+
+1. Abstract
+
+ IMAP4 [RFC-2060] does not define a default server namespace. As a
+ result, two common namespace models have evolved:
+
+ The "Personal Mailbox" model, in which the default namespace that is
+ presented consists of only the user's personal mailboxes. To access
+ shared mailboxes, the user must use an escape mechanism to reach
+ another namespace.
+
+ The "Complete Hierarchy" model, in which the default namespace that
+ is presented includes the user's personal mailboxes along with any
+ other mailboxes they have access to.
+
+ These two models, create difficulties for certain client operations.
+ This document defines a NAMESPACE command that allows a client to
+ discover the prefixes of namespaces used by a server for personal
+ mailboxes, other users' mailboxes, and shared mailboxes. This allows
+ a client to avoid much of the manual user configuration that is now
+ necessary when mixing and matching IMAP4 clients and servers.
+
+2. Conventions used in this document
+
+ In examples, "C:" and "S:" indicate lines sent by the client and
+ server respectively. If such lines are wrapped without a new "C:" or
+ "S:" label, then the wrapping is for editorial clarity and is not
+ part of the command.
+
+
+
+Gahrns & Newman Standards Track [Page 1]
+
+RFC 2342 IMAP4 Namespace May 1998
+
+
+ Personal Namespace: A namespace that the server considers within the
+ personal scope of the authenticated user on a particular connection.
+ Typically, only the authenticated user has access to mailboxes in
+ their Personal Namespace. It is the part of the namespace that
+ belongs to the user that is allocated for mailboxes. If an INBOX
+ exists for a user, it MUST appear within the user's personal
+ namespace. In the typical case, there SHOULD be only one Personal
+ Namespace on a server.
+
+ Other Users' Namespace: A namespace that consists of mailboxes from
+ the Personal Namespaces of other users. To access mailboxes in the
+ Other Users' Namespace, the currently authenticated user MUST be
+ explicitly granted access rights. For example, it is common for a
+ manager to grant to their secretary access rights to their mailbox.
+ In the typical case, there SHOULD be only one Other Users' Namespace
+ on a server.
+
+ Shared Namespace: A namespace that consists of mailboxes that are
+ intended to be shared amongst users and do not exist within a user's
+ Personal Namespace.
+
+ The namespaces a server uses MAY differ on a per-user basis.
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in [RFC-2119].
+
+3. Introduction and Overview
+
+ Clients often attempt to create mailboxes for such purposes as
+ maintaining a record of sent messages (e.g. "Sent Mail") or
+ temporarily saving messages being composed (e.g. "Drafts"). For
+ these clients to inter-operate correctly with the variety of IMAP4
+ servers available, the user must enter the prefix of the Personal
+ Namespace used by the server. Using the NAMESPACE command, a client
+ is able to automatically discover this prefix without manual user
+ configuration.
+
+ In addition, users are often required to manually enter the prefixes
+ of various namespaces in order to view the mailboxes located there.
+ For example, they might be required to enter the prefix of #shared to
+ view the shared mailboxes namespace. The NAMESPACE command allows a
+ client to automatically discover the namespaces that are available on
+ a server. This allows a client to present the available namespaces to
+ the user in what ever manner it deems appropriate. For example, a
+
+
+
+
+
+
+Gahrns & Newman Standards Track [Page 2]
+
+RFC 2342 IMAP4 Namespace May 1998
+
+
+ client could choose to initially display only personal mailboxes, or
+ it may choose to display the complete list of mailboxes available,
+ and initially position the user at the root of their Personal
+ Namespace.
+
+ A server MAY choose to make available to the NAMESPACE command only a
+ subset of the complete set of namespaces the server supports. To
+ provide the ability to access these namespaces, a client SHOULD allow
+ the user the ability to manually enter a namespace prefix.
+
+4. Requirements
+
+ IMAP4 servers that support this extension MUST list the keyword
+ NAMESPACE in their CAPABILITY response.
+
+ The NAMESPACE command is valid in the Authenticated and Selected
+ state.
+
+5. NAMESPACE Command
+
+ Arguments: none
+
+ Response: an untagged NAMESPACE response that contains the prefix
+ and hierarchy delimiter to the server's Personal
+ Namespace(s), Other Users' Namespace(s), and Shared
+ Namespace(s) that the server wishes to expose. The
+ response will contain a NIL for any namespace class
+ that is not available. Namespace_Response_Extensions
+ MAY be included in the response.
+ Namespace_Response_Extensions which are not on the IETF
+ standards track, MUST be prefixed with an "X-".
+
+ Result: OK - Command completed
+ NO - Error: Can't complete command
+ BAD - argument invalid
+
+ Example 5.1:
+ ===========
+
+ < A server that supports a single personal namespace. No leading
+ prefix is used on personal mailboxes and "/" is the hierarchy
+ delimiter.>
+
+ C: A001 NAMESPACE
+ S: * NAMESPACE (("" "/")) NIL NIL
+ S: A001 OK NAMESPACE command completed
+
+
+
+
+
+Gahrns & Newman Standards Track [Page 3]
+
+RFC 2342 IMAP4 Namespace May 1998
+
+
+ Example 5.2:
+ ===========
+
+ < A user logged on anonymously to a server. No personal mailboxes
+ are associated with the anonymous user and the user does not have
+ access to the Other Users' Namespace. No prefix is required to
+ access shared mailboxes and the hierarchy delimiter is "." >
+
+ C: A001 NAMESPACE
+ S: * NAMESPACE NIL NIL (("" "."))
+ S: A001 OK NAMESPACE command completed
+
+ Example 5.3:
+ ===========
+
+ < A server that contains a Personal Namespace and a single Shared
+ Namespace. >
+
+ C: A001 NAMESPACE
+ S: * NAMESPACE (("" "/")) NIL (("Public Folders/" "/"))
+ S: A001 OK NAMESPACE command completed
+
+ Example 5.4:
+ ===========
+
+ < A server that contains a Personal Namespace, Other Users'
+ Namespace and multiple Shared Namespaces. Note that the hierarchy
+ delimiter used within each namespace can be different. >
+
+ C: A001 NAMESPACE
+ S: * NAMESPACE (("" "/")) (("~" "/")) (("#shared/" "/")
+ ("#public/" "/")("#ftp/" "/")("#news." "."))
+ S: A001 OK NAMESPACE command completed
+
+ The prefix string allows a client to do things such as automatically
+ creating personal mailboxes or LISTing all available mailboxes within
+ a namespace.
+
+ Example 5.5:
+ ===========
+
+ < A server that supports only the Personal Namespace, with a
+ leading prefix of INBOX to personal mailboxes and a hierarchy
+ delimiter of ".">
+
+ C: A001 NAMESPACE
+ S: * NAMESPACE (("INBOX." ".")) NIL NIL
+ S: A001 OK NAMESPACE command completed
+
+
+
+Gahrns & Newman Standards Track [Page 4]
+
+RFC 2342 IMAP4 Namespace May 1998
+
+
+ < Automatically create a mailbox to store sent items.>
+
+ C: A002 CREATE "INBOX.Sent Mail"
+ S: A002 OK CREATE command completed
+
+ Although typically a server will support only a single Personal
+ Namespace, and a single Other User's Namespace, circumstances exist
+ where there MAY be multiples of these, and a client MUST be prepared
+ for them. If a client is configured such that it is required to
+ create a certain mailbox, there can be circumstances where it is
+ unclear which Personal Namespaces it should create the mailbox in.
+ In these situations a client SHOULD let the user select which
+ namespaces to create the mailbox in.
+
+ Example 5.6:
+ ===========
+
+ < In this example, a server supports 2 Personal Namespaces. In
+ addition to the regular Personal Namespace, the user has an
+ additional personal namespace to allow access to mailboxes in an
+ MH format mailstore. >
+
+ < The client is configured to save a copy of all mail sent by the
+ user into a mailbox called 'Sent Mail'. Furthermore, after a
+ message is deleted from a mailbox, the client is configured to
+ move that message to a mailbox called 'Deleted Items'.>
+
+ < Note that this example demonstrates how some extension flags can
+ be passed to further describe the #mh namespace. >
+
+ C: A001 NAMESPACE
+ S: * NAMESPACE (("" "/")("#mh/" "/" "X-PARAM" ("FLAG1" "FLAG2")))
+ NIL NIL
+ S: A001 OK NAMESPACE command completed
+
+ < It is desired to keep only one copy of sent mail. It is unclear
+ which Personal Namespace the client should use to create the 'Sent
+ Mail' mailbox. The user is prompted to select a namespace and
+ only one 'Sent Mail' mailbox is created. >
+
+ C: A002 CREATE "Sent Mail"
+ S: A002 OK CREATE command completed
+
+ < The client is designed so that it keeps two 'Deleted Items'
+ mailboxes, one for each namespace. >
+
+ C: A003 CREATE "Delete Items"
+ S: A003 OK CREATE command completed
+
+
+
+Gahrns & Newman Standards Track [Page 5]
+
+RFC 2342 IMAP4 Namespace May 1998
+
+
+ C: A004 CREATE "#mh/Deleted Items"
+ S: A004 OK CREATE command completed
+
+ The next level of hierarchy following the Other Users' Namespace
+ prefix SHOULD consist of <username>, where <username> is a user name
+ as per the IMAP4 LOGIN or AUTHENTICATE command.
+
+ A client can construct a LIST command by appending a "%" to the Other
+ Users' Namespace prefix to discover the Personal Namespaces of other
+ users that are available to the currently authenticated user.
+
+ In response to such a LIST command, a server SHOULD NOT return user
+ names that have not granted access to their personal mailboxes to the
+ user in question.
+
+ A server MAY return a LIST response containing only the names of
+ users that have explicitly granted access to the user in question.
+
+ Alternatively, a server MAY return NO to such a LIST command,
+ requiring that a user name be included with the Other Users'
+ Namespace prefix before listing any other user's mailboxes.
+
+ Example 5.7:
+ ===========
+
+ < A server that supports providing a list of other user's
+ mailboxes that are accessible to the currently logged on user. >
+
+ C: A001 NAMESPACE
+ S: * NAMESPACE (("" "/")) (("Other Users/" "/")) NIL
+ S: A001 OK NAMESPACE command completed
+
+ C: A002 LIST "" "Other Users/%"
+ S: * LIST () "/" "Other Users/Mike"
+ S: * LIST () "/" "Other Users/Karen"
+ S: * LIST () "/" "Other Users/Matthew"
+ S: * LIST () "/" "Other Users/Tesa"
+ S: A002 OK LIST command completed
+
+ Example 5.8:
+ ===========
+
+ < A server that does not support providing a list of other user's
+ mailboxes that are accessible to the currently logged on user.
+ The mailboxes are listable if the client includes the name of the
+ other user with the Other Users' Namespace prefix. >
+
+
+
+
+
+Gahrns & Newman Standards Track [Page 6]
+
+RFC 2342 IMAP4 Namespace May 1998
+
+
+ C: A001 NAMESPACE
+ S: * NAMESPACE (("" "/")) (("#Users/" "/")) NIL
+ S: A001 OK NAMESPACE command completed
+
+ < In this example, the currently logged on user has access to the
+ Personal Namespace of user Mike, but the server chose to suppress
+ this information in the LIST response. However, by appending the
+ user name Mike (received through user input) to the Other Users'
+ Namespace prefix, the client is able to get a listing of the
+ personal mailboxes of user Mike. >
+
+ C: A002 LIST "" "#Users/%"
+ S: A002 NO The requested item could not be found.
+
+ C: A003 LIST "" "#Users/Mike/%"
+ S: * LIST () "/" "#Users/Mike/INBOX"
+ S: * LIST () "/" "#Users/Mike/Foo"
+ S: A003 OK LIST command completed.
+
+ A prefix string might not contain a hierarchy delimiter, because
+ in some cases it is not needed as part of the prefix.
+
+ Example 5.9:
+ ===========
+
+ < A server that allows access to the Other Users' Namespace by
+ prefixing the others' mailboxes with a '~' followed by <username>,
+ where <username> is a user name as per the IMAP4 LOGIN or
+ AUTHENTICATE command.>
+
+ C: A001 NAMESPACE
+ S: * NAMESPACE (("" "/")) (("~" "/")) NIL
+ S: A001 OK NAMESPACE command completed
+
+ < List the mailboxes for user mark >
+
+ C: A002 LIST "" "~mark/%"
+ S: * LIST () "/" "~mark/INBOX"
+ S: * LIST () "/" "~mark/foo"
+ S: A002 OK LIST command completed
+
+ Historical convention has been to start all namespaces with the "#"
+ character. Namespaces that include the "#" character are not IMAP
+ URL [IMAP-URL] friendly requiring the "#" character to be represented
+ as %23 when within URLs. As such, server implementers MAY instead
+ consider using namespace prefixes that do not contain the "#"
+ character.
+
+
+
+
+Gahrns & Newman Standards Track [Page 7]
+
+RFC 2342 IMAP4 Namespace May 1998
+
+
+6. Formal Syntax
+
+ The following syntax specification uses the augmented Backus-Naur
+ Form (BNF) as described in [ABNF].
+
+ atom = <atom>
+ ; <atom> as defined in [RFC-2060]
+
+ Namespace = nil / "(" 1*( "(" string SP (<"> QUOTED_CHAR <"> /
+ nil) *(Namespace_Response_Extension) ")" ) ")"
+
+ Namespace_Command = "NAMESPACE"
+
+ Namespace_Response_Extension = SP string SP "(" string *(SP string)
+ ")"
+
+ Namespace_Response = "*" SP "NAMESPACE" SP Namespace SP Namespace SP
+ Namespace
+
+ ; The first Namespace is the Personal Namespace(s)
+ ; The second Namespace is the Other Users' Namespace(s)
+ ; The third Namespace is the Shared Namespace(s)
+
+ nil = <nil>
+ ; <nil> as defined in [RFC-2060]
+
+ QUOTED_CHAR = <QUOTED_CHAR>
+ ; <QUOTED_CHAR> as defined in [RFC-2060]
+
+ string = <string>
+ ; <string> as defined in [RFC-2060]
+ ; Note that the namespace prefix is to a mailbox and following
+ ; IMAP4 convention, any international string in the NAMESPACE
+ ; response MUST be of modified UTF-7 format as described in
+ ; [RFC-2060].
+
+7. Security Considerations
+
+ In response to a LIST command containing an argument of the Other
+ Users' Namespace prefix, a server SHOULD NOT list users that have not
+ granted list access to their personal mailboxes to the currently
+ authenticated user. Providing such a list, could compromise security
+ by potentially disclosing confidential information of who is located
+ on the server, or providing a starting point of a list of user
+ accounts to attack.
+
+
+
+
+
+
+Gahrns & Newman Standards Track [Page 8]
+
+RFC 2342 IMAP4 Namespace May 1998
+
+
+8. References
+
+ [RFC-2060], Crispin, M., "Internet Message Access Protocol Version
+ 4rev1", RFC 2060, December 1996.
+
+ [RFC-2119], Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [ABNF] Crocker, D., Editor, and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", RFC 2234, November 1997.
+
+ [IMAP-URL], Newman, C., "IMAP URL Scheme", RFC 2192, September 1997.
+
+9. Acknowledgments
+
+ Many people have participated in the discussion of IMAP namespaces on
+ the IMAP mailing list. In particular, the authors would like to
+ thank Mark Crispin for many of the concepts relating to the Personal
+ Namespace and accessing the Personal Namespace of other users, Steve
+ Hole for summarizing the two namespace models, John Myers and Jack De
+ Winter for their work in a preceding effort trying to define a
+ standardized personal namespace, and Larry Osterman for his review
+ and collaboration on this document.
+
+11. Authors' Addresses
+
+ Mike Gahrns
+ Microsoft
+ One Microsoft Way
+ Redmond, WA, 98072, USA
+
+ Phone: (425) 936-9833
+ EMail: mikega@microsoft.com
+
+
+ Chris Newman
+ Innosoft International, Inc.
+ 1050 East Garvey Ave. South
+ West Covina, CA, 91790, USA
+
+ EMail: chris.newman@innosoft.com
+
+
+
+
+
+
+
+
+
+
+Gahrns & Newman Standards Track [Page 9]
+
+RFC 2342 IMAP4 Namespace May 1998
+
+
+12. Full Copyright Statement
+
+ Copyright (C) The Internet Society (1998). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Gahrns & Newman Standards Track [Page 10]
+
diff --git a/imap/docs/rfc/rfc2683.txt b/imap/docs/rfc/rfc2683.txt
new file mode 100644
index 00000000..d92e3405
--- /dev/null
+++ b/imap/docs/rfc/rfc2683.txt
@@ -0,0 +1,1291 @@
+
+
+
+
+
+
+Network Working Group B. Leiba
+Request for Comments: 2683 IBM T.J. Watson Research Center
+Category: Informational September 1999
+
+
+ IMAP4 Implementation Recommendations
+
+Status of this Memo
+
+ This memo provides information for the Internet community. It does
+ not specify an Internet standard of any kind. Distribution of this
+ memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (1999). All Rights Reserved.
+
+1. Abstract
+
+ The IMAP4 specification [RFC-2060] describes a rich protocol for use
+ in building clients and servers for storage, retrieval, and
+ manipulation of electronic mail. Because the protocol is so rich and
+ has so many implementation choices, there are often trade-offs that
+ must be made and issues that must be considered when designing such
+ clients and servers. This document attempts to outline these issues
+ and to make recommendations in order to make the end products as
+ interoperable as possible.
+
+2. Conventions used in this document
+
+ In examples, "C:" indicates lines sent by a client that is connected
+ to a server. "S:" indicates lines sent by the server to the client.
+
+ The words "must", "must not", "should", "should not", and "may" are
+ used with specific meaning in this document; since their meaning is
+ somewhat different from that specified in RFC 2119, we do not put
+ them in all caps here. Their meaning is as follows:
+
+ must -- This word means that the action described is necessary
+ to ensure interoperability. The recommendation should
+ not be ignored.
+ must not -- This phrase means that the action described will be
+ almost certain to hurt interoperability. The
+ recommendation should not be ignored.
+
+
+
+
+
+
+
+Leiba Informational [Page 1]
+
+RFC 2683 IMAP4 Implementation Recommendations September 1999
+
+
+ should -- This word means that the action described is strongly
+ recommended and will enhance interoperability or
+ usability. The recommendation should not be ignored
+ without careful consideration.
+ should not -- This phrase means that the action described is strongly
+ recommended against, and might hurt interoperability or
+ usability. The recommendation should not be ignored
+ without careful consideration.
+ may -- This word means that the action described is an
+ acceptable implementation choice. No specific
+ recommendation is implied; this word is used to point
+ out a choice that might not be obvious, or to let
+ implementors know what choices have been made by
+ existing implementations.
+
+3. Interoperability Issues and Recommendations
+
+3.1. Accessibility
+
+ This section describes the issues related to access to servers and
+ server resources. Concerns here include data sharing and maintenance
+ of client/server connections.
+
+3.1.1. Multiple Accesses of the Same Mailbox
+
+ One strong point of IMAP4 is that, unlike POP3, it allows for
+ multiple simultaneous access to a single mailbox. A user can, thus,
+ read mail from a client at home while the client in the office is
+ still connected; or the help desk staff can all work out of the same
+ inbox, all seeing the same pool of questions. An important point
+ about this capability, though is that NO SERVER IS GUARANTEED TO
+ SUPPORT THIS. If you are selecting an IMAP server and this facility
+ is important to you, be sure that the server you choose to install,
+ in the configuration you choose to use, supports it.
+
+ If you are designing a client, you must not assume that you can
+ access the same mailbox more than once at a time. That means
+
+ 1. you must handle gracefully the failure of a SELECT command if the
+ server refuses the second SELECT,
+ 2. you must handle reasonably the severing of your connection (see
+ "Severed Connections", below) if the server chooses to allow the
+ second SELECT by forcing the first off,
+ 3. you must avoid making multiple connections to the same mailbox in
+ your own client (for load balancing or other such reasons), and
+ 4. you must avoid using the STATUS command on a mailbox that you have
+ selected (with some server implementations the STATUS command has
+ the same problems with multiple access as do the SELECT and
+
+
+
+Leiba Informational [Page 2]
+
+RFC 2683 IMAP4 Implementation Recommendations September 1999
+
+
+ EXAMINE commands).
+
+ A further note about STATUS: The STATUS command is sometimes used to
+ check a non-selected mailbox for new mail. This mechanism must not
+ be used to check for new mail in the selected mailbox; section 5.2 of
+ [RFC-2060] specifically forbids this in its last paragraph. Further,
+ since STATUS takes a mailbox name it is an independent operation, not
+ operating on the selected mailbox. Because of this, the information
+ it returns is not necessarily in synchronization with the selected
+ mailbox state.
+
+3.1.2. Severed Connections
+
+ The client/server connection may be severed for one of three reasons:
+ the client severs the connection, the server severs the connection,
+ or the connection is severed by outside forces beyond the control of
+ the client and the server (a telephone line drops, for example).
+ Clients and servers must both deal with these situations.
+
+ When the client wants to sever a connection, it's usually because it
+ has finished the work it needed to do on that connection. The client
+ should send a LOGOUT command, wait for the tagged response, and then
+ close the socket. But note that, while this is what's intended in
+ the protocol design, there isn't universal agreement here. Some
+ contend that sending the LOGOUT and waiting for the two responses
+ (untagged BYE and tagged OK) is wasteful and unnecessary, and that
+ the client can simply close the socket. The server should interpret
+ the closed socket as a log out by the client. The counterargument is
+ that it's useful from the standpoint of cleanup, problem
+ determination, and the like, to have an explicit client log out,
+ because otherwise there is no way for the server to tell the
+ difference between "closed socket because of log out" and "closed
+ socket because communication was disrupted". If there is a
+ client/server interaction problem, a client which routinely
+ terminates a session by breaking the connection without a LOGOUT will
+ make it much more difficult to determine the problem.
+
+ Because of this disagreement, server designers must be aware that
+ some clients might close the socket without sending a LOGOUT. In any
+ case, whether or not a LOGOUT was sent, the server should not
+ implicitly expunge any messages from the selected mailbox. If a
+ client wants the server to do so, it must send a CLOSE or EXPUNGE
+ command explicitly.
+
+ When the server wants to sever a connection it's usually due to an
+ inactivity timeout or is because a situation has arisen that has
+ changed the state of the mail store in a way that the server can not
+ communicate to the client. The server should send an untagged BYE
+
+
+
+Leiba Informational [Page 3]
+
+RFC 2683 IMAP4 Implementation Recommendations September 1999
+
+
+ response to the client and then close the socket. Sending an
+ untagged BYE response before severing allows the server to send a
+ human-readable explanation of the problem to the client, which the
+ client may then log, display to the user, or both (see section 7.1.5
+ of [RFC-2060]).
+
+ Regarding inactivity timeouts, there is some controversy. Unlike
+ POP, for which the design is for a client to connect, retrieve mail,
+ and log out, IMAP's design encourages long-lived (and mostly
+ inactive) client/server sessions. As the number of users grows, this
+ can use up a lot of server resources, especially with clients that
+ are designed to maintain sessions for mailboxes that the user has
+ finished accessing. To alleviate this, a server may implement an
+ inactivity timeout, unilaterally closing a session (after first
+ sending an untagged BYE, as noted above). Some server operators have
+ reported dramatic improvements in server performance after doing
+ this. As specified in [RFC-2060], if such a timeout is done it must
+ not be until at least 30 minutes of inactivity. The reason for this
+ specification is to prevent clients from sending commands (such as
+ NOOP) to the server at frequent intervals simply to avert a too-early
+ timeout. If the client knows that the server may not time out the
+ session for at least 30 minutes, then the client need not poll at
+ intervals more frequent than, say, 25 minutes.
+
+3.2. Scaling
+
+ IMAP4 has many features that allow for scalability, as mail stores
+ become larger and more numerous. Large numbers of users, mailboxes,
+ and messages, and very large messages require thought to handle
+ efficiently. This document will not address the administrative
+ issues involved in large numbers of users, but we will look at the
+ other items.
+
+3.2.1. Flood Control
+
+ There are three situations when a client can make a request that will
+ result in a very large response - too large for the client reasonably
+ to deal with: there are a great many mailboxes available, there are a
+ great many messages in the selected mailbox, or there is a very large
+ message part. The danger here is that the end user will be stuck
+ waiting while the server sends (and the client processes) an enormous
+ response. In all of these cases there are things a client can do to
+ reduce that danger.
+
+ There is also the case where a client can flood a server, by sending
+ an arbitratily long command. We'll discuss that issue, too, in this
+ section.
+
+
+
+
+Leiba Informational [Page 4]
+
+RFC 2683 IMAP4 Implementation Recommendations September 1999
+
+
+3.2.1.1. Listing Mailboxes
+
+ Some servers present Usenet newsgroups to IMAP users. Newsgroups,
+ and other such hierarchical mailbox structures, can be very numerous
+ but may have only a few entries at the top level of hierarchy. Also,
+ some servers are built against mail stores that can, unbeknownst to
+ the server, have circular hierarchies - that is, it's possible for
+ "a/b/c/d" to resolve to the same file structure as "a", which would
+ then mean that "a/b/c/d/b" is the same as "a/b", and the hierarchy
+ will never end. The LIST response in this case will be unlimited.
+
+ Clients that will have trouble with this are those that use
+
+ C: 001 LIST "" *
+
+ to determine the mailbox list. Because of this, clients should not
+ use an unqualified "*" that way in the LIST command. A safer
+ approach is to list each level of hierarchy individually, allowing
+ the user to traverse the tree one limb at a time, thus:
+
+ C: 001 LIST "" %
+ S: * LIST () "/" Banana
+ S: * LIST ...etc...
+ S: 001 OK done
+
+ and then
+
+ C: 002 LIST "" Banana/%
+ S: * LIST () "/" Banana/Apple
+ S: * LIST ...etc...
+ S: 002 OK done
+
+ Using this technique the client's user interface can give the user
+ full flexibility without choking on the voluminous reply to "LIST *".
+
+ Of course, it is still possible that the reply to
+
+ C: 005 LIST "" alt.fan.celebrity.%
+
+ may be thousands of entries long, and there is, unfortunately,
+ nothing the client can do to protect itself from that. This has not
+ yet been a notable problem.
+
+ Servers that may export circular hierarchies (any server that
+ directly presents a UNIX file system, for instance) should limit the
+ hierarchy depth to prevent unlimited LIST responses. A suggested
+ depth limit is 20 hierarchy levels.
+
+
+
+
+Leiba Informational [Page 5]
+
+RFC 2683 IMAP4 Implementation Recommendations September 1999
+
+
+3.2.1.2. Fetching the List of Messages
+
+ When a client selects a mailbox, it is given a count, in the untagged
+ EXISTS response, of the messages in the mailbox. This number can be
+ very large. In such a case it might be unwise to use
+
+ C: 004 FETCH 1:* ALL
+
+ to populate the user's view of the mailbox. One good method to avoid
+ problems with this is to batch the requests, thus:
+
+ C: 004 FETCH 1:50 ALL
+ S: * 1 FETCH ...etc...
+ S: 004 OK done
+ C: 005 FETCH 51:100 ALL
+ S: * 51 FETCH ...etc...
+ S: 005 OK done
+ C: 006 FETCH 101:150 ALL
+ ...etc...
+
+ Using this method, another command, such as "FETCH 6 BODY[1]" can be
+ inserted as necessary, and the client will not have its access to the
+ server blocked by a storm of FETCH replies. (Such a method could be
+ reversed to fetch the LAST 50 messages first, then the 50 prior to
+ that, and so on.)
+
+ As a smart extension of this, a well designed client, prepared for
+ very large mailboxes, will not automatically fetch data for all
+ messages AT ALL. Rather, the client will populate the user's view
+ only as the user sees it, possibly pre-fetching selected information,
+ and only fetching other information as the user scrolls to it. For
+ example, to select only those messages beginning with the first
+ unseen one:
+
+ C: 003 SELECT INBOX
+ S: * 10000 EXISTS
+ S: * 80 RECENT
+ S: * FLAGS (\Answered \Flagged \Deleted \Draft \Seen)
+ S: * OK [UIDVALIDITY 824708485] UID validity status
+ S: * OK [UNSEEN 9921] First unseen message
+ S: 003 OK [READ-WRITE] SELECT completed
+ C: 004 FETCH 9921:* ALL
+ ... etc...
+
+ If the server does not return an OK [UNSEEN] response, the client may
+ use SEARCH UNSEEN to obtain that value.
+
+
+
+
+
+Leiba Informational [Page 6]
+
+RFC 2683 IMAP4 Implementation Recommendations September 1999
+
+
+ This mechanism is good as a default presentation method, but only
+ works well if the default message order is acceptable. A client may
+ want to present various sort orders to the user (by subject, by date
+ sent, by sender, and so on) and in that case (lacking a SORT
+ extension on the server side) the client WILL have to retrieve all
+ message descriptors. A client that provides this service should not
+ do it by default and should inform the user of the costs of choosing
+ this option for large mailboxes.
+
+3.2.1.3. Fetching a Large Body Part
+
+ The issue here is similar to the one for a list of messages. In the
+ BODYSTRUCTURE response the client knows the size, in bytes, of the
+ body part it plans to fetch. Suppose this is a 70 MB video clip. The
+ client can use partial fetches to retrieve the body part in pieces,
+ avoiding the problem of an uninterruptible 70 MB literal coming back
+ from the server:
+
+ C: 022 FETCH 3 BODY[1]<0.20000>
+ S: * 3 FETCH (FLAGS(\Seen) BODY[1]<0> {20000}
+ S: ...data...)
+ S: 022 OK done
+ C: 023 FETCH 3 BODY[1]<20001.20000>
+ S: * 3 FETCH (BODY[1]<20001> {20000}
+ S: ...data...)
+ S: 023 OK done
+ C: 024 FETCH 3 BODY[1]<40001.20000>
+ ...etc...
+
+3.2.1.4. BODYSTRUCTURE vs. Entire Messages
+
+ Because FETCH BODYSTRUCTURE is necessary in order to determine the
+ number of body parts, and, thus, whether a message has "attachments",
+ clients often use FETCH FULL as their normal method of populating the
+ user's view of a mailbox. The benefit is that the client can display
+ a paperclip icon or some such indication along with the normal
+ message summary. However, this comes at a significant cost with some
+ server configurations. The parsing needed to generate the FETCH
+ BODYSTRUCTURE response may be time-consuming compared with that
+ needed for FETCH ENVELOPE. The client developer should consider this
+ issue when deciding whether the ability to add a paperclip icon is
+ worth the tradeoff in performance, especially with large mailboxes.
+
+ Some clients, rather than using FETCH BODYSTRUCTURE, use FETCH BODY[]
+ (or the equivalent FETCH RFC822) to retrieve the entire message.
+ They then do the MIME parsing in the client. This may give the
+ client slightly more flexibility in some areas (access, for instance,
+ to header fields that aren't returned in the BODYSTRUCTURE and
+
+
+
+Leiba Informational [Page 7]
+
+RFC 2683 IMAP4 Implementation Recommendations September 1999
+
+
+ ENVELOPE responses), but it can cause severe performance problems by
+ forcing the transfer of all body parts when the user might only want
+ to see some of them - a user logged on by modem and reading a small
+ text message with a large ZIP file attached may prefer to read the
+ text only and save the ZIP file for later. Therefore, a client
+ should not normally retrieve entire messages and should retrieve
+ message body parts selectively.
+
+3.2.1.5. Long Command Lines
+
+ A client can wind up building a very long command line in an effort to
+ try to be efficient about requesting information from a server. This
+ can typically happen when a client builds a message set from selected
+ messages and doesn't recognise that contiguous blocks of messages may
+ be group in a range. Suppose a user selects all 10,000 messages in a
+ large mailbox and then unselects message 287. The client could build
+ that message set as "1:286,288:10000", but a client that doesn't
+ handle that might try to enumerate each message individually and build
+ "1,2,3,4, [and so on] ,9999,10000". Adding that to the fetch command
+ results in a command line that's almost 49,000 octets long, and,
+ clearly, one can construct a command line that's even longer.
+
+ A client should limit the length of the command lines it generates to
+ approximately 1000 octets (including all quoted strings but not
+ including literals). If the client is unable to group things into
+ ranges so that the command line is within that length, it should
+ split the request into multiple commands. The client should use
+ literals instead of long quoted strings, in order to keep the command
+ length down.
+
+ For its part, a server should allow for a command line of at least
+ 8000 octets. This provides plenty of leeway for accepting reasonable
+ length commands from clients. The server should send a BAD response
+ to a command that does not end within the server's maximum accepted
+ command length.
+
+3.2.2. Subscriptions
+
+ The client isn't the only entity that can get flooded: the end user,
+ too, may need some flood control. The IMAP4 protocol provides such
+ control in the form of subscriptions. Most servers support the
+ SUBSCRIBE, UNSUBSCRIBE, and LSUB commands, and many users choose to
+ narrow down a large list of available mailboxes by subscribing to the
+ ones that they usually want to see. Clients, with this in mind,
+ should give the user a way to see only subscribed mailboxes. A
+ client that never uses the LSUB command takes a significant usability
+ feature away from the user. Of course, the client would not want to
+ hide the LIST command completely; the user needs to have a way to
+
+
+
+Leiba Informational [Page 8]
+
+RFC 2683 IMAP4 Implementation Recommendations September 1999
+
+
+ choose between LIST and LSUB. The usual way to do this is to provide
+ a setting like "show which mailboxes?: [] all [] subscribed only".
+
+3.2.3. Searching
+
+ IMAP SEARCH commands can become particularly troublesome (that is,
+ slow) on mailboxes containing a large number of messages. So let's
+ put a few things in perspective in that regard.
+
+ The flag searches should be fast. The flag searches (ALL, [UN]SEEN,
+ [UN]ANSWERED, [UN]DELETED, [UN]DRAFT, [UN]FLAGGED, NEW, OLD, RECENT)
+ are known to be used by clients for the client's own use (for
+ instance, some clients use "SEARCH UNSEEN" to find unseen mail and
+ "SEARCH DELETED" to warn the user before expunging messages).
+
+ Other searches, particularly the text searches (HEADER, TEXT, BODY)
+ are initiated by the user, rather than by the client itself, and
+ somewhat slower performance can be tolerated, since the user is aware
+ that the search is being done (and is probably aware that it might be
+ time-consuming). A smart server might use dynamic indexing to speed
+ commonly used text searches.
+
+ The client may allow other commands to be sent to the server while a
+ SEARCH is in progress, but at the time of this writing there is
+ little or no server support for parallel processing of multiple
+ commands in the same session (and see "Multiple Accesses of the Same
+ Mailbox" above for a description of the dangers of trying to work
+ around this by doing your SEARCH in another session).
+
+ Another word about text searches: some servers, built on database
+ back-ends with indexed search capabilities, may return search results
+ that do not match the IMAP spec's "case-insensitive substring"
+ requirements. While these servers are in violation of the protocol,
+ there is little harm in the violation as long as the search results
+ are used only in response to a user's request. Still, developers of
+ such servers should be aware that they ARE violating the protocol,
+ should think carefully about that behaviour, and must be certain that
+ their servers respond accurately to the flag searches for the reasons
+ outlined above.
+
+ In addition, servers should support CHARSET UTF-8 [UTF-8] in
+ searches.
+
+
+
+
+
+
+
+
+
+Leiba Informational [Page 9]
+
+RFC 2683 IMAP4 Implementation Recommendations September 1999
+
+
+3.3 Avoiding Invalid Requests
+
+ IMAP4 provides ways for a server to tell a client in advance what is
+ and isn't permitted in some circumstances. Clients should use these
+ features to avoid sending requests that a well designed client would
+ know to be invalid. This section explains this in more detail.
+
+3.3.1. The CAPABILITY Command
+
+ All IMAP4 clients should use the CAPABILITY command to determine what
+ version of IMAP and what optional features a server supports. The
+ client should not send IMAP4rev1 commands and arguments to a server
+ that does not advertize IMAP4rev1 in its CAPABILITY response.
+ Similarly, the client should not send IMAP4 commands that no longer
+ exist in IMAP4rev1 to a server that does not advertize IMAP4 in its
+ CAPABILITY response. An IMAP4rev1 server is NOT required to support
+ obsolete IMAP4 or IMAP2bis commands (though some do; do not let this
+ fact lull you into thinking that it's valid to send such commands to
+ an IMAP4rev1 server).
+
+ A client should not send commands to probe for the existance of
+ certain extensions. All standard and standards-track extensions
+ include CAPABILITY tokens indicating their presense. All private and
+ experimental extensions should do the same, and clients that take
+ advantage of them should use the CAPABILITY response to determine
+ whether they may be used or not.
+
+3.3.2. Don't Do What the Server Says You Can't
+
+ In many cases, the server, in response to a command, will tell the
+ client something about what can and can't be done with a particular
+ mailbox. The client should pay attention to this information and
+ should not try to do things that it's been told it can't do.
+
+ Examples:
+
+ * Do not try to SELECT a mailbox that has the \Noselect flag set.
+ * Do not try to CREATE a sub-mailbox in a mailbox that has the
+ \Noinferiors flag set.
+ * Do not respond to a failing COPY or APPEND command by trying to
+ CREATE the target mailbox if the server does not respond with a
+ [TRYCREATE] response code.
+ * Do not try to expunge a mailbox that has been selected with the
+ [READ-ONLY] response code.
+
+
+
+
+
+
+
+Leiba Informational [Page 10]
+
+RFC 2683 IMAP4 Implementation Recommendations September 1999
+
+
+3.4. Miscellaneous Protocol Considerations
+
+ We describe here a number of important protocol-related issues, the
+ misunderstanding of which has caused significant interoperability
+ problems in IMAP4 implementations. One general item is that every
+ implementer should be certain to take note of and to understand
+ section 2.2.2 and the preamble to section 7 of the IMAP4rev1 spec
+ [RFC-2060].
+
+3.4.1. Well Formed Protocol
+
+ We cannot stress enough the importance of adhering strictly to the
+ protocol grammar. The specification of the protocol is quite rigid;
+ do not assume that you can insert blank space for "readability" if
+ none is called for. Keep in mind that there are parsers out there
+ that will crash if there are protocol errors. There are clients that
+ will report every parser burp to the user. And in any case,
+ information that cannot be parsed is information that is lost. Be
+ careful in your protocol generation. And see "A Word About Testing",
+ below.
+
+ In particular, note that the string in the INTERNALDATE response is
+ NOT an RFC-822 date string - that is, it is not in the same format as
+ the first string in the ENVELOPE response. Since most clients will,
+ in fact, accept an RFC-822 date string in the INTERNALDATE response,
+ it's easy to miss this in your interoperability testing. But it will
+ cause a problem with some client, so be sure to generate the correct
+ string for this field.
+
+3.4.2. Special Characters
+
+ Certain characters, currently the double-quote and the backslash, may
+ not be sent as-is inside a quoted string. These characters must be
+ preceded by the escape character if they are in a quoted string, or
+ else the string must be sent as a literal. Both clients and servers
+ must handle this, both on output (they must send these characters
+ properly) and on input (they must be able to receive escaped
+ characters in quoted strings). Example:
+
+ C: 001 LIST "" %
+ S: * LIST () "" INBOX
+ S: * LIST () "\\" TEST
+ S: * LIST () "\\" {12}
+ S: "My" mailbox
+ S: 001 OK done
+ C: 002 LIST "" "\"My\" mailbox\\%"
+ S: * LIST () "\\" {17}
+ S: "My" mailbox\Junk
+
+
+
+Leiba Informational [Page 11]
+
+RFC 2683 IMAP4 Implementation Recommendations September 1999
+
+
+ S: 002 OK done
+
+ Note that in the example the server sent the hierarchy delimiter as
+ an escaped character in the quoted string and sent the mailbox name
+ containing imbedded double-quotes as a literal. The client used only
+ quoted strings, escaping both the backslash and the double-quote
+ characters.
+
+ The CR and LF characters may be sent ONLY in literals; they are not
+ allowed, even if escaped, inside quoted strings.
+
+ And while we're talking about special characters: the IMAP spec, in
+ the section titled "Mailbox International Naming Convention",
+ describes how to encode mailbox names in modified UTF-7 [UTF-7 and
+ RFC-2060]. Implementations must adhere to this in order to be
+ interoperable in the international market, and servers should
+ validate mailbox names sent by client and reject names that do not
+ conform.
+
+ As to special characters in userids and passwords: clients must not
+ restrict what a user may type in for a userid or a password. The
+ formal grammar specifies that these are "astrings", and an astring
+ can be a literal. A literal, in turn can contain any 8-bit
+ character, and clients must allow users to enter all 8-bit characters
+ here, and must pass them, unchanged, to the server (being careful to
+ send them as literals when necessary). In particular, some server
+ configurations use "@" in user names, and some clients do not allow
+ that character to be entered; this creates a severe interoperability
+ problem.
+
+3.4.3. UIDs and UIDVALIDITY
+
+ Servers that support existing back-end mail stores often have no good
+ place to save UIDs for messages. Often the existing mail store will
+ not have the concept of UIDs in the sense that IMAP has: strictly
+ increasing, never re-issued, 32-bit integers. Some servers solve
+ this by storing the UIDs in a place that's accessible to end users,
+ allowing for the possibility that the users will delete them. Others
+ solve it by re-assigning UIDs every time a mailbox is selected.
+
+ The server should maintain UIDs permanently for all messages if it
+ can. If that's not possible, the server must change the UIDVALIDITY
+ value for the mailbox whenever any of the UIDs may have become
+ invalid. Clients must recognize that the UIDVALIDITY has changed and
+ must respond to that condition by throwing away any information that
+ they have saved about UIDs in that mailbox. There have been many
+ problems in this area when clients have failed to do this; in the
+ worst case it will result in loss of mail when a client deletes the
+
+
+
+Leiba Informational [Page 12]
+
+RFC 2683 IMAP4 Implementation Recommendations September 1999
+
+
+ wrong piece of mail by using a stale UID.
+
+ It seems to be a common misunderstanding that "the UIDVALIDITY and
+ the UID, taken together, form a 64-bit identifier that uniquely
+ identifies a message on a server". This is absolutely NOT TRUE.
+ There is no assurance that the UIDVALIDITY values of two mailboxes be
+ different, so the UIDVALIDITY in no way identifies a mailbox. The
+ ONLY purpose of UIDVALIDITY is, as its name indicates, to give the
+ client a way to check the validity of the UIDs it has cached. While
+ it is a valid implementation choice to put these values together to
+ make a 64-bit identifier for the message, the important concept here
+ is that UIDs are not unique between mailboxes; they are only unique
+ WITHIN a given mailbox.
+
+ Some server implementations have attempted to make UIDs unique across
+ the entire server. This is inadvisable, in that it limits the life
+ of UIDs unnecessarily. The UID is a 32-bit number and will run out
+ in reasonably finite time if it's global across the server. If you
+ assign UIDs sequentially in one mailbox, you will not have to start
+ re-using them until you have had, at one time or another, 2**32
+ different messages in that mailbox. In the global case, you will
+ have to reuse them once you have had, at one time or another, 2**32
+ different messages in the entire mail store. Suppose your server has
+ around 8000 users registered (2**13). That gives an average of 2**19
+ UIDs per user. Suppose each user gets 32 messages (2**5) per day.
+ That gives you 2**14 days (16000+ days = about 45 years) before you
+ run out. That may seem like enough, but multiply the usage just a
+ little (a lot of spam, a lot of mailing list subscriptions, more
+ users) and you limit yourself too much.
+
+ What's worse is that if you have to wrap the UIDs, and, thus, you
+ have to change UIDVALIDITY and invalidate the UIDs in the mailbox,
+ you have to do it for EVERY mailbox in the system, since they all
+ share the same UID pool. If you assign UIDs per mailbox and you have
+ a problem, you only have to kill the UIDs for that one mailbox.
+
+ Under extreme circumstances (and this is extreme, indeed), the server
+ may have to invalidate UIDs while a mailbox is in use by a client -
+ that is, the UIDs that the client knows about in its active mailbox
+ are no longer valid. In that case, the server must immediately
+ change the UIDVALIDITY and must communicate this to the client. The
+ server may do this by sending an unsolicited UIDVALIDITY message, in
+ the same form as in response to the SELECT command. Clients must be
+ prepared to handle such a message and the possibly coincident failure
+ of the command in process. For example:
+
+
+
+
+
+
+Leiba Informational [Page 13]
+
+RFC 2683 IMAP4 Implementation Recommendations September 1999
+
+
+ C: 032 UID STORE 382 +Flags.silent \Deleted
+ S: * OK [UIDVALIDITY 12345] New UIDVALIDITY value!
+ S: 032 NO UID command rejected because UIDVALIDITY changed!
+ C: ...invalidates local information and re-fetches...
+ C: 033 FETCH 1:* UID
+ ...etc...
+
+ At the time of the writing of this document, the only server known to
+ do this does so only under the following condition: the client
+ selects INBOX, but there is not yet a physical INBOX file created.
+ Nonetheless, the SELECT succeeds, exporting an empty INBOX with a
+ temporary UIDVALIDITY of 1. While the INBOX remains selected, mail
+ is delivered to the user, which creates the real INBOX file and
+ assigns a permanent UIDVALIDITY (that is likely not to be 1). The
+ server reports the change of UIDVALIDITY, but as there were no
+ messages before, so no UIDs have actually changed, all the client
+ must do is accept the change in UIDVALIDITY.
+
+ Alternatively, a server may force the client to re-select the
+ mailbox, at which time it will obtain a new UIDVALIDITY value. To do
+ this, the server closes this client session (see "Severed
+ Connections" above) and the client then reconnects and gets back in
+ synch. Clients must be prepared for either of these behaviours.
+
+ We do not know of, nor do we anticipate the future existance of, a
+ server that changes UIDVALIDITY while there are existing messages,
+ but clients must be prepared to handle this eventuality.
+
+3.4.4. FETCH Responses
+
+ When a client asks for certain information in a FETCH command, the
+ server may return the requested information in any order, not
+ necessarily in the order that it was requested. Further, the server
+ may return the information in separate FETCH responses and may also
+ return information that was not explicitly requested (to reflect to
+ the client changes in the state of the subject message). Some
+ examples:
+
+ C: 001 FETCH 1 UID FLAGS INTERNALDATE
+ S: * 5 FETCH (FLAGS (\Deleted))
+ S: * 1 FETCH (FLAGS (\Seen) INTERNALDATE "..." UID 345)
+ S: 001 OK done
+
+ (In this case, the responses are in a different order. Also, the
+ server returned a flag update for message 5, which wasn't part of the
+ client's request.)
+
+
+
+
+
+Leiba Informational [Page 14]
+
+RFC 2683 IMAP4 Implementation Recommendations September 1999
+
+
+ C: 002 FETCH 2 UID FLAGS INTERNALDATE
+ S: * 2 FETCH (INTERNALDATE "...")
+ S: * 2 FETCH (UID 399)
+ S: * 2 FETCH (FLAGS ())
+ S: 002 OK done
+
+ (In this case, the responses are in a different order and were
+ returned in separate responses.)
+
+ C: 003 FETCH 2 BODY[1]
+ S: * 2 FETCH (FLAGS (\Seen) BODY[1] {14}
+ S: Hello world!
+ S: )
+ S: 003 OK done
+
+ (In this case, the FLAGS response was added by the server, since
+ fetching the body part caused the server to set the \Seen flag.)
+
+ Because of this characteristic a client must be ready to receive any
+ FETCH response at any time and should use that information to update
+ its local information about the message to which the FETCH response
+ refers. A client must not assume that any FETCH responses will come
+ in any particular order, or even that any will come at all. If after
+ receiving the tagged response for a FETCH command the client finds
+ that it did not get all of the information requested, the client
+ should send a NOOP command to the server to ensure that the server
+ has an opportunity to send any pending EXPUNGE responses to the
+ client (see [RFC-2180]).
+
+3.4.5. RFC822.SIZE
+
+ Some back-end mail stores keep the mail in a canonical form, rather
+ than retaining the original MIME format of the messages. This means
+ that the server must reassemble the message to produce a MIME stream
+ when a client does a fetch such as RFC822 or BODY[], requesting the
+ entire message. It also may mean that the server has no convenient
+ way to know the RFC822.SIZE of the message. Often, such a server
+ will actually have to build the MIME stream to compute the size, only
+ to throw the stream away and report the size to the client.
+
+ When this is the case, some servers have chosen to estimate the size,
+ rather than to compute it precisely. Such an estimate allows the
+ client to display an approximate size to the user and to use the
+ estimate in flood control considerations (q.v.), but requires that
+ the client not use the size for things such as allocation of buffers,
+ because those buffers might then be too small to hold the actual MIME
+ stream. Instead, a client should use the size that's returned in the
+ literal when you fetch the data.
+
+
+
+Leiba Informational [Page 15]
+
+RFC 2683 IMAP4 Implementation Recommendations September 1999
+
+
+ The protocol requires that the RFC822.SIZE value returned by the
+ server be EXACT. Estimating the size is a protocol violation, and
+ server designers must be aware that, despite the performance savings
+ they might realize in using an estimate, this practice will cause
+ some clients to fail in various ways. If possible, the server should
+ compute the RFC822.SIZE for a particular message once, and then save
+ it for later retrieval. If that's not possible, the server must
+ compute the value exactly every time. Incorrect estimates do cause
+ severe interoperability problems with some clients.
+
+3.4.6. Expunged Messages
+
+ If the server allows multiple connections to the same mailbox, it is
+ often possible for messages to be expunged in one client unbeknownst
+ to another client. Since the server is not allowed to tell the
+ client about these expunged messages in response to a FETCH command,
+ the server may have to deal with the issue of how to return
+ information about an expunged message. There was extensive
+ discussion about this issue, and the results of that discussion are
+ summarized in [RFC-2180]. See that reference for a detailed
+ explanation and for recommendations.
+
+3.4.7. The Namespace Issue
+
+ Namespaces are a very muddy area in IMAP4 implementation right now
+ (see [NAMESPACE] for a proposal to clear the water a bit). Until the
+ issue is resolved, the important thing for client developers to
+ understand is that some servers provide access through IMAP to more
+ than just the user's personal mailboxes, and, in fact, the user's
+ personal mailboxes may be "hidden" somewhere in the user's default
+ hierarchy. The client, therefore, should provide a setting wherein
+ the user can specify a prefix to be used when accessing mailboxes. If
+ the user's mailboxes are all in "~/mail/", for instance, then the
+ user can put that string in the prefix. The client would then put
+ the prefix in front of any name pattern in the LIST and LSUB
+ commands:
+
+ C: 001 LIST "" ~/mail/%
+
+ (See also "Reference Names in the LIST Command" below.)
+
+3.4.8. Creating Special-Use Mailboxes
+
+ It may seem at first that this is part of the namespace issue; it is
+ not, and is only indirectly related to it. A number of clients like
+ to create special-use mailboxes with particular names. Most
+ commonly, clients with a "trash folder" model of message deletion
+ want to create a mailbox with the name "Trash" or "Deleted". Some
+
+
+
+Leiba Informational [Page 16]
+
+RFC 2683 IMAP4 Implementation Recommendations September 1999
+
+
+ clients want to create a "Drafts" mailbox, an "Outbox" mailbox, or a
+ "Sent Mail" mailbox. And so on. There are two major
+ interoperability problems with this practice:
+
+ 1. different clients may use different names for mailboxes with
+ similar functions (such as "Trash" and "Deleted"), or may manage
+ the same mailboxes in different ways, causing problems if a user
+ switches between clients and
+ 2. there is no guarantee that the server will allow the creation of
+ the desired mailbox.
+
+ The client developer is, therefore, well advised to consider
+ carefully the creation of any special-use mailboxes on the server,
+ and, further, the client must not require such mailbox creation -
+ that is, if you do decide to do this, you must handle gracefully the
+ failure of the CREATE command and behave reasonably when your
+ special-use mailboxes do not exist and can not be created.
+
+ In addition, the client developer should provide a convenient way for
+ the user to select the names for any special-use mailboxes, allowing
+ the user to make these names the same in all clients used and to put
+ them where the user wants them.
+
+3.4.9. Reference Names in the LIST Command
+
+ Many implementers of both clients and servers are confused by the
+ "reference name" on the LIST command. The reference name is intended
+ to be used in much the way a "cd" (change directory) command is used
+ on Unix, PC DOS, Windows, and OS/2 systems. That is, the mailbox
+ name is interpreted in much the same way as a file of that name would
+ be found if one had done a "cd" command into the directory specified
+ by the reference name. For example, in Unix we have the following:
+
+ > cd /u/jones/junk
+ > vi banana [file is "/u/jones/junk/banana"]
+ > vi stuff/banana [file is "/u/jones/junk/stuff/banana"]
+ > vi /etc/hosts [file is "/etc/hosts"]
+
+ In the past, there have been several interoperability problems with
+ this. First, while some IMAP servers are built on Unix or PC file
+ systems, many others are not, and the file system semantics do not
+ make sense in those configurations. Second, while some IMAP servers
+ expose the underlying file system to the clients, others allow access
+ only to the user's personal mailboxes, or to some other limited set
+ of files, making such file-system-like semantics less meaningful.
+ Third, because the IMAP spec leaves the interpretation of the
+ reference name as "implementation-dependent", in the past the various
+ server implementations handled it in vastly differing ways.
+
+
+
+Leiba Informational [Page 17]
+
+RFC 2683 IMAP4 Implementation Recommendations September 1999
+
+
+ The following recommendations are the result of significant
+ operational experience, and are intended to maximize
+ interoperability.
+
+ Server implementations must implement the reference argument in a way
+ that matches the intended "change directory" operation as closely as
+ possible. As a minimum implementation, the reference argument may be
+ prepended to the mailbox name (while suppressing double delimiters;
+ see the next paragraph). Even servers that do not provide a way to
+ break out of the current hierarchy (see "breakout facility" below)
+ must provide a reasonable implementation of the reference argument,
+ as described here, so that they will interoperate with clients that
+ use it.
+
+ Server implementations that prepend the reference argument to the
+ mailbox name should insert a hierarchy delimiter between them, and
+ must not insert a second if one is already present:
+
+ C: A001 LIST ABC DEF
+ S: * LIST () "/" ABC/DEF <=== should do this
+ S: A001 OK done
+
+ C: A002 LIST ABC/ /DEF
+ S: * LIST () "/" ABC//DEF <=== must not do this
+ S: A002 OK done
+
+ On clients, the reference argument is chiefly used to implement a
+ "breakout facility", wherein the user may directly access a mailbox
+ outside the "current directory" hierarchy. Client implementations
+ should have an operational mode that does not use the reference
+ argument. This is to interoperate with older servers that did not
+ implement the reference argument properly. While it's a good idea to
+ give the user access to a breakout facility, clients that do not
+ intend to do so should not use the reference argument at all.
+
+ Client implementations should always place a trailing hierarchy
+ delimiter on the reference argument. This is because some servers
+ prepend the reference argument to the mailbox name without inserting
+ a hierarchy delimiter, while others do insert a hierarchy delimiter
+ if one is not already present. A client that puts the delimiter in
+ will work with both varieties of server.
+
+ Client implementations that implement a breakout facility should
+ allow the user to choose whether or not to use a leading hierarchy
+ delimiter on the mailbox argument. This is because the handling of a
+ leading mailbox hierarchy delimiter also varies from server to
+ server, and even between different mailstores on the same server. In
+ some cases, a leading hierarchy delimiter means "discard the
+
+
+
+Leiba Informational [Page 18]
+
+RFC 2683 IMAP4 Implementation Recommendations September 1999
+
+
+ reference argument" (implementing the intended breakout facility),
+ thus:
+
+ C: A001 LIST ABC/ /DEF
+ S: * LIST () "/" /DEF
+ S: A001 OK done
+
+ In other cases, however, the two are catenated and the extra
+ hierarchy delimiter is discarded, thus:
+
+ C: A001 LIST ABC/ /DEF
+ S: * LIST () "/" ABC/DEF
+ S: A001 OK done
+
+ Client implementations must not assume that the server supports a
+ breakout facility, but may provide a way for the user to use one if
+ it is available. Any breakout facility should be exported to the
+ user interface. Note that there may be other "breakout" characters
+ besides the hierarchy delimiter (for instance, UNIX filesystem
+ servers are likely to use a leading "~" as well), and that their
+ interpretation is server-dependent.
+
+3.4.10. Mailbox Hierarchy Delimiters
+
+ The server's selection of what to use as a mailbox hierarchy
+ delimiter is a difficult one, involving several issues: What
+ characters do users expect to see? What characters can they enter
+ for a hierarchy delimiter if it is desired (or required) that the
+ user enter it? What character can be used for the hierarchy
+ delimiter, noting that the chosen character can not otherwise be used
+ in the mailbox name?
+
+ Because some interfaces show users the hierarchy delimiters or allow
+ users to enter qualified mailbox names containing them, server
+ implementations should use delimiter characters that users generally
+ expect to see as name separators. The most common characters used
+ for this are "/" (as in Unix file names), "\" (as in OS/2 and Windows
+ file names), and "." (as in news groups). There is little to choose
+ among these apart from what users may expect or what is dictated by
+ the underlying file system, if any. One consideration about using
+ "\" is that it's also a special character in the IMAP protocol. While
+ the use of other hierarchy delimiter characters is permissible, A
+ DESIGNER IS WELL ADVISED TO STAY WITH ONE FROM THIS SET unless the
+ server is intended for special purposes only. Implementers might be
+ thinking about using characters such as "-", "_", ";", "&", "#", "@",
+ and "!", but they should be aware of the surprise to the user as well
+ as of the effect on URLs and other external specifications (since
+ some of these characters have special meanings there). Also, a
+
+
+
+Leiba Informational [Page 19]
+
+RFC 2683 IMAP4 Implementation Recommendations September 1999
+
+
+ server that uses "\" (and clients of such a server) must remember to
+ escape that character in quoted strings or to send literals instead.
+ Literals are recommended over escaped characters in quoted strings in
+ order to maintain compatibility with older IMAP versions that did not
+ allow escaped characters in quoted strings (but check the grammar to
+ see where literals are allowed):
+
+ C: 001 LIST "" {13}
+ S: + send literal
+ C: this\%\%\%\h*
+ S: * LIST () "\\" {27}
+ S: this\is\a\mailbox\hierarchy
+ S: 001 OK LIST complete
+
+ In any case, a server should not use normal alpha-numeric characters
+ (such as "X" or "0") as delimiters; a user would be very surprised to
+ find that "EXPENDITURES" actually represented a two-level hierarchy.
+ And a server should not use characters that are non-printable or
+ difficult or impossible to enter on a standard US keyboard. Control
+ characters, box-drawing characters, and characters from non-US
+ alphabets fit into this category. Their use presents
+ interoperability problems that are best avoided.
+
+ The UTF-7 encoding of mailbox names also raises questions about what
+ to do with the hierarchy delimiters in encoded names: do we encode
+ each hierarchy level and separate them with delimiters, or do we
+ encode the fully qualified name, delimiters and all? The answer for
+ IMAP is the former: encode each hierarchy level separately, and
+ insert delimiters between. This makes it particularly important not
+ to use as a hierarchy delimiter a character that might cause
+ confusion with IMAP's modified UTF-7 [UTF-7 and RFC-2060] encoding.
+
+ To repeat: a server should use "/", "\", or "." as its hierarchy
+ delimiter. The use of any other character is likely to cause
+ problems and is STRONGLY DISCOURAGED.
+
+3.4.11. ALERT Response Codes
+
+ The protocol spec is very clear on the matter of what to do with
+ ALERT response codes, and yet there are many clients that violate it
+ so it needs to be said anyway: "The human-readable text contains a
+ special alert that must be presented to the user in a fashion that
+ calls the user's attention to the message." That should be clear
+ enough, but I'll repeat it here: Clients must present ALERT text
+ clearly to the user.
+
+
+
+
+
+
+Leiba Informational [Page 20]
+
+RFC 2683 IMAP4 Implementation Recommendations September 1999
+
+
+3.4.12. Deleting Mailboxes
+
+ The protocol does not guarantee that a client may delete a mailbox
+ that is not empty, though on some servers it is permissible and is,
+ in fact, much faster than the alternative or deleting all the
+ messages from the client. If the client chooses to try to take
+ advantage of this possibility it must be prepared to use the other
+ method in the even that the more convenient one fails. Further, a
+ client should not try to delete the mailbox that it has selected, but
+ should first close that mailbox; some servers do not permit the
+ deletion of the selected mailbox.
+
+ That said, a server should permit the deletion of a non-empty
+ mailbox; there's little reason to pass this work on to the client.
+ Moreover, forbidding this prevents the deletion of a mailbox that for
+ some reason can not be opened or expunged, leading to possible
+ denial-of-service problems.
+
+ Example:
+
+ [User tells the client to delete mailbox BANANA, which is
+ currently selected...]
+ C: 008 CLOSE
+ S: 008 OK done
+ C: 009 DELETE BANANA
+ S: 009 NO Delete failed; mailbox is not empty.
+ C: 010 SELECT BANANA
+ S: * ... untagged SELECT responses
+ S: 010 OK done
+ C: 011 STORE 1:* +FLAGS.SILENT \DELETED
+ S: 011 OK done
+ C: 012 CLOSE
+ S: 012 OK done
+ C: 013 DELETE BANANA
+ S: 013 OK done
+
+3.5. A Word About Testing
+
+ Since the whole point of IMAP is interoperability, and since
+ interoperability can not be tested in a vacuum, the final
+ recommendation of this treatise is, "Test against EVERYTHING." Test
+ your client against every server you can get an account on. Test
+ your server with every client you can get your hands on. Many
+ clients make limited test versions available on the Web for the
+ downloading. Many server owners will give serious client developers
+ guest accounts for testing. Contact them and ask. NEVER assume that
+ because your client works with one or two servers, or because your
+ server does fine with one or two clients, you will interoperate well
+
+
+
+Leiba Informational [Page 21]
+
+RFC 2683 IMAP4 Implementation Recommendations September 1999
+
+
+ in general.
+
+ In particular, in addition to everything else, be sure to test
+ against the reference implementations: the PINE client, the
+ University of Washington server, and the Cyrus server.
+
+ See the following URLs on the web for more information here:
+
+ IMAP Products and Sources: http://www.imap.org/products.html
+ IMC MailConnect: http://www.imc.org/imc-mailconnect
+
+4. Security Considerations
+
+ This document describes behaviour of clients and servers that use the
+ IMAP4 protocol, and as such, has the same security considerations as
+ described in [RFC-2060].
+
+5. References
+
+ [RFC-2060] Crispin, M., "Internet Message Access Protocol - Version
+ 4rev1", RFC 2060, December 1996.
+
+ [RFC-2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC-2180] Gahrns, M., "IMAP4 Multi-Accessed Mailbox Practice", RFC
+ 2180, July 1997.
+
+ [UTF-8] Yergeau, F., " UTF-8, a transformation format of Unicode
+ and ISO 10646", RFC 2044, October 1996.
+
+ [UTF-7] Goldsmith, D. and M. Davis, "UTF-7, a Mail-Safe
+ Transformation Format of Unicode", RFC 2152, May 1997.
+
+ [NAMESPACE] Gahrns, M. and C. Newman, "IMAP4 Namespace", Work in
+ Progress.
+
+6. Author's Address
+
+ Barry Leiba
+ IBM T.J. Watson Research Center
+ 30 Saw Mill River Road
+ Hawthorne, NY 10532
+
+ Phone: 1-914-784-7941
+ EMail: leiba@watson.ibm.com
+
+
+
+
+
+Leiba Informational [Page 22]
+
+RFC 2683 IMAP4 Implementation Recommendations September 1999
+
+
+7. Full Copyright Statement
+
+ Copyright (C) The Internet Society (1999). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Leiba Informational [Page 23]
+
diff --git a/imap/docs/rfc/rfc2971.txt b/imap/docs/rfc/rfc2971.txt
new file mode 100644
index 00000000..9e7264dc
--- /dev/null
+++ b/imap/docs/rfc/rfc2971.txt
@@ -0,0 +1,451 @@
+
+
+
+
+
+
+Network Working Group T. Showalter
+Request for Comments: 2971 Mirapoint, Inc.
+Category: Standards Track October 2000
+
+
+ IMAP4 ID extension
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2000). All Rights Reserved.
+
+Abstract
+
+ The ID extension to the Internet Message Access Protocol - Version
+ 4rev1 (IMAP4rev1) protocol allows the server and client to exchange
+ identification information on their implementation in order to make
+ bug reports and usage statistics more complete.
+
+1. Introduction
+
+ The IMAP4rev1 protocol described in [IMAP4rev1] provides a method for
+ accessing remote mail stores, but it provides no facility to
+ advertise what program a client or server uses to provide service.
+ This makes it difficult for implementors to get complete bug reports
+ from users, as it is frequently difficult to know what client or
+ server is in use.
+
+ Additionally, some sites may wish to assemble usage statistics based
+ on what clients are used, but in an an environment where users are
+ permitted to obtain and maintain their own clients this is difficult
+ to accomplish.
+
+ The ID command provides a facility to advertise information on what
+ programs are being used along with contact information (should bugs
+ ever occur).
+
+
+
+
+
+
+
+
+Showalter Standards Track [Page 1]
+
+RFC 2971 IMAP4 ID extension October 2000
+
+
+2. Conventions Used in this Document
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in [KEYWORDS].
+
+ The conventions used in this document are the same as specified in
+ [IMAP4rev1]. In examples, "C:" and "S:" indicate lines sent by the
+ client and server respectively. Line breaks have been inserted for
+ readability.
+
+3. Specification
+
+ The sole purpose of the ID extension is to enable clients and servers
+ to exchange information on their implementations for the purposes of
+ statistical analysis and problem determination.
+
+ This information is be submitted to a server by any client wishing to
+ provide information for statistical purposes, provided the server
+ advertises its willingness to take the information with the atom "ID"
+ included in the list of capabilities returned by the CAPABILITY
+ command.
+
+ Implementations MUST NOT make operational changes based on the data
+ sent as part of the ID command or response. The ID command is for
+ human consumption only, and is not to be used in improving the
+ performance of clients or servers.
+
+ This includes, but is not limited to, the following:
+
+ Servers MUST NOT attempt to work around client bugs by using
+ information from the ID command. Clients MUST NOT attempt to work
+ around server bugs based on the ID response.
+
+ Servers MUST NOT provide features to a client or otherwise
+ optimize for a particular client by using information from the ID
+ command. Clients MUST NOT provide features to a server or
+ otherwise optimize for a particular server based on the ID
+ response.
+
+ Servers MUST NOT deny access to or refuse service for a client
+ based on information from the ID command. Clients MUST NOT refuse
+ to operate or limit their operation with a server based on the ID
+ response.
+
+
+
+
+
+
+
+Showalter Standards Track [Page 2]
+
+RFC 2971 IMAP4 ID extension October 2000
+
+
+ Rationale: It is imperative that this extension not supplant IMAP's
+ CAPABILITY mechanism with a ad-hoc approach where implementations
+ guess each other's features based on who they claim to be.
+
+ Implementations MUST NOT send false information in an ID command.
+
+ Implementations MAY send less information than they have available or
+ no information at all. Such behavior may be useful to preserve user
+ privacy. See Security Considerations, section 7.
+
+3.1. ID Command
+
+ Arguments: client parameter list or NIL
+
+ Responses: OPTIONAL untagged response: ID
+
+ Result: OK identification information accepted
+ BAD command unknown or arguments invalid
+
+ Implementation identification information is sent by the client with
+ the ID command.
+
+ This command is valid in any state.
+
+ The information sent is in the form of a list of field/value pairs.
+ Fields are permitted to be any IMAP4 string, and values are permitted
+ to be any IMAP4 string or NIL. A value of NIL indicates that the
+ client can not or will not specify this information. The client may
+ also send NIL instead of the list, indicating that it wants to send
+ no information, but would still accept a server response.
+
+ The available fields are defined in section 3.3.
+
+ Example: C: a023 ID ("name" "sodr" "version" "19.34" "vendor"
+ "Pink Floyd Music Limited")
+ S: * ID NIL
+ S: a023 OK ID completed
+
+3.2. ID Response
+
+ Contents: server parameter list
+
+ In response to an ID command issued by the client, the server replies
+ with a tagged response containing information on its implementation.
+ The format is the same as the client list.
+
+
+
+
+
+
+Showalter Standards Track [Page 3]
+
+RFC 2971 IMAP4 ID extension October 2000
+
+
+ Example: C: a042 ID NIL
+ S: * ID ("name" "Cyrus" "version" "1.5" "os" "sunos"
+ "os-version" "5.5" "support-url"
+ "mailto:cyrus-bugs+@andrew.cmu.edu")
+ S: a042 OK ID command completed
+
+ A server MUST send a tagged ID response to an ID command. However, a
+ server MAY send NIL in place of the list.
+
+3.3. Defined Field Values
+
+ Any string may be sent as a field, but the following are defined to
+ describe certain values that might be sent. Implementations are free
+ to send none, any, or all of these. Strings are not case-sensitive.
+ Field strings MUST NOT be longer than 30 octets. Value strings MUST
+ NOT be longer than 1024 octets. Implementations MUST NOT send more
+ than 30 field-value pairs.
+
+ name Name of the program
+ version Version number of the program
+ os Name of the operating system
+ os-version Version of the operating system
+ vendor Vendor of the client/server
+ support-url URL to contact for support
+ address Postal address of contact/vendor
+ date Date program was released, specified as a date-time
+ in IMAP4rev1
+ command Command used to start the program
+ arguments Arguments supplied on the command line, if any
+ if any
+ environment Description of environment, i.e., UNIX environment
+ variables or Windows registry settings
+
+ Implementations MUST NOT use contact information to submit automatic
+ bug reports. Implementations may include information from an ID
+ response in a report automatically prepared, but are prohibited from
+ sending the report without user authorization.
+
+ It is preferable to find the name and version of the underlying
+ operating system at runtime in cases where this is possible.
+
+ Information sent via an ID response may violate user privacy. See
+ Security Considerations, section 7.
+
+ Implementations MUST NOT send the same field name more than once.
+
+
+
+
+
+
+Showalter Standards Track [Page 4]
+
+RFC 2971 IMAP4 ID extension October 2000
+
+
+4. Formal Syntax
+
+ This syntax is intended to augment the grammar specified in
+ [IMAP4rev1] in order to provide for the ID command. This
+ specification uses the augmented Backus-Naur Form (BNF) notation as
+ used in [IMAP4rev1].
+
+ command_any ::= "CAPABILITY" / "LOGOUT" / "NOOP" / x_command / id
+ ;; adds id command to command_any in [IMAP4rev1]
+
+ id ::= "ID" SPACE id_params_list
+
+ id_response ::= "ID" SPACE id_params_list
+
+ id_params_list ::= "(" #(string SPACE nstring) ")" / nil
+ ;; list of field value pairs
+
+ response_data ::= "*" SPACE (resp_cond_state / resp_cond_bye /
+ mailbox_data / message_data / capability_data / id_response)
+
+5. Use of the ID extension with Firewalls and Other Intermediaries
+
+ There exist proxies, firewalls, and other intermediary systems that
+ can intercept an IMAP session and make changes to the data exchanged
+ in the session. Such intermediaries are not anticipated by the IMAP4
+ protocol design and are not within the scope of the IMAP4 standard.
+ However, in order for the ID command to be useful in the presence of
+ such intermediaries, those intermediaries need to take special note
+ of the ID command and response. In particular, if an intermediary
+ changes any part of the IMAP session it must also change the ID
+ command to advertise its presence.
+
+ A firewall MAY act to block transmission of specific information
+ fields in the ID command and response that it believes reveal
+ information that could expose a security vulnerability. However, a
+ firewall SHOULD NOT disable the extension, when present, entirely,
+ and SHOULD NOT unconditionally remove either the client or server
+ list.
+
+ Finally, it should be noted that a firewall, when handling a
+ CAPABILITY response, MUST NOT allow the names of extensions to be
+ returned to the client that the firewall has no knowledge of.
+
+
+
+
+
+
+
+
+
+Showalter Standards Track [Page 5]
+
+RFC 2971 IMAP4 ID extension October 2000
+
+
+6. References
+
+ [KEYWORDS] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", RFC 2119, March 1997.
+
+ [IMAP4rev1] Crispin, M., "Internet Message Access Protocol - Version
+ 4rev1", RFC 2060, October 1996.
+
+ [RFC-822] Crocker, D., "Standard for the Format of ARPA Internet
+ Text Messages", STD 11, RFC 822, August 1982.
+
+7. Security Considerations
+
+ This extension has the danger of violating the privacy of users if
+ misused. Clients and servers should notify users that they implement
+ and enable the ID command.
+
+ It is highly desirable that implementations provide a method of
+ disabling ID support, perhaps by not sending ID at all, or by sending
+ NIL as the argument to the ID command or response.
+
+ Implementors must exercise extreme care in adding fields sent as part
+ of an ID command or response. Some fields, including a processor ID
+ number, Ethernet address, or other unique (or mostly unique)
+ identifier allow tracking of users in ways that violate user privacy
+ expectations.
+
+ Having implementation information of a given client or server may
+ make it easier for an attacker to gain unauthorized access due to
+ security holes.
+
+ Since this command includes arbitrary data and does not require the
+ user to authenticate, server implementations are cautioned to guard
+ against an attacker sending arbitrary garbage data in order to fill
+ up the ID log. In particular, if a server naively logs each ID
+ command to disk without inspecting it, an attacker can simply fire up
+ thousands of connections and send a few kilobytes of random data.
+ Servers have to guard against this. Methods include truncating
+ abnormally large responses; collating responses by storing only a
+ single copy, then keeping a counter of the number of times that
+ response has been seen; keeping only particularly interesting parts
+ of responses; and only logging responses of users who actually log
+ in.
+
+ Security is affected by firewalls which modify the IMAP protocol
+ stream; see section 5, Use of the ID Extension with Firewalls and
+ Other Intermediaries, for more information.
+
+
+
+
+Showalter Standards Track [Page 6]
+
+RFC 2971 IMAP4 ID extension October 2000
+
+
+8. Author's Address
+
+ Tim Showalter
+ Mirapoint, Inc.
+ 909 Hermosa Ct.
+ Sunnyvale, CA 94095
+
+ EMail: tjs@mirapoint.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Showalter Standards Track [Page 7]
+
+RFC 2971 IMAP4 ID extension October 2000
+
+
+9. Full Copyright Statement
+
+ Copyright (C) The Internet Society (2000). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Showalter Standards Track [Page 8]
+
diff --git a/imap/docs/rfc/rfc3348.txt b/imap/docs/rfc/rfc3348.txt
new file mode 100644
index 00000000..500871cc
--- /dev/null
+++ b/imap/docs/rfc/rfc3348.txt
@@ -0,0 +1,339 @@
+
+
+
+
+
+
+Network Working Group M. Gahrns
+Request for Comments: 3348 R. Cheng
+Category: Informational Microsoft
+ July 2002
+
+
+ The Internet Message Action Protocol (IMAP4)
+ Child Mailbox Extension
+
+Status of this Memo
+
+ This memo provides information for the Internet community. It does
+ not specify an Internet standard of any kind. Distribution of this
+ memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2002). All Rights Reserved.
+
+Abstract
+
+ The Internet Message Action Protocol (IMAP4) CHILDREN extension
+ provides a mechanism for a client to efficiently determine if a
+ particular mailbox has children, without issuing a LIST "" * or a
+ LIST "" % for each mailbox.
+
+1. Conventions used in this document
+
+ In examples, "C:" and "S:" indicate lines sent by the client and
+ server respectively. If such lines are wrapped without a new "C:" or
+ "S:" label, then the wrapping is for editorial clarity and is not
+ part of the command.
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in [RFC-2119].
+
+2. Introduction and Overview
+
+ Many IMAP4 [RFC-2060] clients present to the user a hierarchical view
+ of the mailboxes that a user has access to. Rather than initially
+ presenting to the user the entire mailbox hierarchy, it is often
+ preferable to show to the user a collapsed outline list of the
+ mailbox hierarchy (particularly if there is a large number of
+ mailboxes). The user can then expand the collapsed outline hierarchy
+ as needed. It is common to include within the collapsed hierarchy a
+
+
+
+
+
+Gahrns, et al. Informational [Page 1]
+
+RFC 3348 IMAP4 Child Mailbox Extension July 2002
+
+
+ visual clue (such as a "+") to indicate that there are child
+ mailboxes under a particular mailbox. When the visual clue is
+ clicked the hierarchy list is expanded to show the child mailboxes.
+
+ Several IMAP vendors implemented this proposal, and it is proposed to
+ document this behavior and functionality as an Informational RFC.
+
+ There is interest in addressing the general extensibility of the IMAP
+ LIST command through an IMAP LIST Extension draft. Similar
+ functionality to the \HasChildren and \HasNoChildren flags could be
+ incorporated into this new LIST Extension. It is proposed that the
+ more general LIST Extension draft proceed on the standards track with
+ this proposal being relegated to informational status only.
+
+ If the functionality of the \HasChildren and \HasNoChildren flags
+ were incorporated into a more general LIST extension, this would have
+ the advantage that a client could then have the opportunity to
+ request whether or not the server should return this information.
+ This would be an advantage over the current draft for servers where
+ this information is expensive to compute, since the server would only
+ need to compute the information when it knew that the client
+ requesting the information was able to consume it.
+
+3. Requirements
+
+ IMAP4 servers that support this extension MUST list the keyword
+ CHILDREN in their CAPABILITY response.
+
+ The CHILDREN extension defines two new attributes that MAY be
+ returned within a LIST response.
+
+ \HasChildren - The presence of this attribute indicates that the
+ mailbox has child mailboxes.
+
+ Servers SHOULD NOT return \HasChildren if child mailboxes exist, but
+ none will be displayed to the current user in a LIST response (as
+ should be the case where child mailboxes exist, but a client does not
+ have permissions to access them.) In this case, \HasNoChildren
+ SHOULD be used.
+
+ In many cases, however, a server may not be able to efficiently
+ compute whether a user has access to all child mailboxes, or multiple
+ users may be accessing the same account and simultaneously changing
+ the mailbox hierarchy. As such a client MUST be prepared to accept
+ the \HasChildren attribute as a hint. That is, a mailbox MAY be
+ flagged with the \HasChildren attribute, but no child mailboxes will
+ appear in a subsequent LIST response.
+
+
+
+
+Gahrns, et al. Informational [Page 2]
+
+RFC 3348 IMAP4 Child Mailbox Extension July 2002
+
+
+ Example 3.1:
+ ============
+
+ /*** Consider a server that has the following mailbox hierarchy:
+
+ INBOX
+ ITEM_1
+ ITEM_1A
+ ITEM_2
+ TOP_SECRET
+
+ Where INBOX, ITEM_1 and ITEM_2 are top level mailboxes. ITEM_1A is a
+ child mailbox of ITEM_1 and TOP_SECRET is a child mailbox of ITEM_2
+ that the currently logged on user does NOT have access to.
+
+ Note that in this case, the server is not able to efficiently compute
+ access rights to child mailboxes and responds with a \HasChildren
+ attribute for mailbox ITEM_2, even though ITEM_2/TOP_SECRET does not
+ appear in the list response. ***/
+
+ C: A001 LIST "" *
+ S: * LIST (\HasNoChildren) "/" INBOX
+ S: * LIST (\HasChildren) "/" ITEM_1
+ S: * LIST (\HasNoChildren) "/" ITEM_1/ITEM_1A
+ S: * LIST (\HasChildren) "/" ITEM_2
+ S: A001 OK LIST Completed
+
+ \HasNoChildren - The presence of this attribute indicates that the
+ mailbox has NO child mailboxes that are accessible to the currently
+ authenticated user. If a mailbox has the \Noinferiors attribute, the
+ \HasNoChildren attribute is redundant and SHOULD be omitted in the
+ LIST response.
+
+ In some instances a server that supports the CHILDREN extension MAY
+ NOT be able to determine whether a mailbox has children. For example
+ it may have difficulty determining whether there are child mailboxes
+ when LISTing mailboxes while operating in a particular namespace.
+
+ In these cases, a server MAY exclude both the \HasChildren and
+ \HasNoChildren attributes in the LIST response. As such, a client
+ can not make any assumptions about whether a mailbox has children
+ based upon the absence of a single attribute.
+
+ It is an error for the server to return both a \HasChildren and a
+ \HasNoChildren attribute in a LIST response.
+
+
+
+
+
+
+Gahrns, et al. Informational [Page 3]
+
+RFC 3348 IMAP4 Child Mailbox Extension July 2002
+
+
+ It is an error for the server to return both a \HasChildren and a
+ \NoInferiors attribute in a LIST response.
+
+ Note: the \HasNoChildren attribute should not be confused with the
+ IMAP4 [RFC-2060] defined attribute \Noinferiors which indicates that
+ no child mailboxes exist now and none can be created in the future.
+
+ The \HasChildren and \HasNoChildren attributes might not be returned
+ in response to a LSUB response. Many servers maintain a simple
+ mailbox subscription list that is not updated when the underlying
+ mailbox structure is changed. A client MUST NOT assume that
+ hierarchy information will be maintained in the subscription list.
+
+ RLIST is a command defined in [RFC-2193] that includes in a LIST
+ response mailboxes that are accessible only via referral. That is, a
+ client must explicitly issue an RLIST command to see a list of these
+ mailboxes. Thus in the case where a mailbox has child mailboxes that
+ are available only via referral, the mailboxes would appear as
+ \HasNoChildren in response to the LIST command, and \HasChildren in
+ response to the RLIST command.
+
+5. Formal Syntax
+
+ The following syntax specification uses the augmented Backus-Naur
+ Form (BNF) as described in [ABNF].
+
+ Two new mailbox attributes are defined as flag_extensions to the
+ IMAP4 mailbox_list response:
+
+ HasChildren = "\HasChildren"
+
+ HasNoChildren = "\HasNoChildren"
+
+6. Security Considerations
+
+ This extension provides a client a more efficient means of
+ determining whether a particular mailbox has children. If a mailbox
+ has children, but the currently authenticated user does not have
+ access to any of them, the server SHOULD respond with a
+ \HasNoChildren attribute. In many cases, however, a server may not
+ be able to efficiently compute whether a user has access to all child
+ mailboxes. If such a server responds with a \HasChildren attribute,
+ when in fact the currently authenticated user does not have access to
+ any child mailboxes, potentially more information is conveyed about
+ the mailbox than intended. A server designed with such levels of
+ security in mind SHOULD NOT attach the \HasChildren attribute to a
+ mailbox unless the server is certain that the user has access to at
+ least one of the child mailboxes.
+
+
+
+Gahrns, et al. Informational [Page 4]
+
+RFC 3348 IMAP4 Child Mailbox Extension July 2002
+
+
+7. References
+
+ [RFC-2060] Crispin, M., "Internet Message Access Protocol - Version
+ 4rev1", RFC 2060, December 1996.
+
+ [RFC-2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC-2234] Crocker, D. and P. Overell, Editors, "Augmented BNF for
+ Syntax Specifications: ABNF", RFC 2234, November 1997.
+
+ [RFC-2193] Gahrns, M., "IMAP4 Mailbox Referrals", RFC 2193, September
+ 1997.
+
+8. Acknowledgments
+
+ The authors would like to thank the participants of several IMC Mail
+ Connect events for their input when this idea was originally
+ presented and refined.
+
+9. Author's Address
+
+ Mike Gahrns
+ Microsoft
+ One Microsoft Way
+ Redmond, WA, 98052
+ Phone: (425) 936-9833
+ EMail: mikega@microsoft.com
+
+ Raymond Cheng
+ Microsoft
+ One Microsoft Way
+ Redmond, WA, 98052
+ Phone: (425) 703-4913
+ EMail: raych@microsoft.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Gahrns, et al. Informational [Page 5]
+
+RFC 3348 IMAP4 Child Mailbox Extension July 2002
+
+
+10. Full Copyright Statement
+
+ Copyright (C) The Internet Society (2002). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Gahrns, et al. Informational [Page 6]
+
diff --git a/imap/docs/rfc/rfc3501.txt b/imap/docs/rfc/rfc3501.txt
new file mode 100644
index 00000000..6f470dd1
--- /dev/null
+++ b/imap/docs/rfc/rfc3501.txt
@@ -0,0 +1,6052 @@
+
+
+
+
+
+
+Network Working Group M. Crispin
+Request for Comments: 3501 University of Washington
+Obsoletes: 2060 March 2003
+Category: Standards Track
+
+
+ INTERNET MESSAGE ACCESS PROTOCOL - VERSION 4rev1
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2003). All Rights Reserved.
+
+Abstract
+
+ The Internet Message Access Protocol, Version 4rev1 (IMAP4rev1)
+ allows a client to access and manipulate electronic mail messages on
+ a server. IMAP4rev1 permits manipulation of mailboxes (remote
+ message folders) in a way that is functionally equivalent to local
+ folders. IMAP4rev1 also provides the capability for an offline
+ client to resynchronize with the server.
+
+ IMAP4rev1 includes operations for creating, deleting, and renaming
+ mailboxes, checking for new messages, permanently removing messages,
+ setting and clearing flags, RFC 2822 and RFC 2045 parsing, searching,
+ and selective fetching of message attributes, texts, and portions
+ thereof. Messages in IMAP4rev1 are accessed by the use of numbers.
+ These numbers are either message sequence numbers or unique
+ identifiers.
+
+ IMAP4rev1 supports a single server. A mechanism for accessing
+ configuration information to support multiple IMAP4rev1 servers is
+ discussed in RFC 2244.
+
+ IMAP4rev1 does not specify a means of posting mail; this function is
+ handled by a mail transfer protocol such as RFC 2821.
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 1]
+
+RFC 3501 IMAPv4 March 2003
+
+
+Table of Contents
+
+ IMAP4rev1 Protocol Specification ................................ 4
+ 1. How to Read This Document ............................... 4
+ 1.1. Organization of This Document ........................... 4
+ 1.2. Conventions Used in This Document ....................... 4
+ 1.3. Special Notes to Implementors ........................... 5
+ 2. Protocol Overview ....................................... 6
+ 2.1. Link Level .............................................. 6
+ 2.2. Commands and Responses .................................. 6
+ 2.2.1. Client Protocol Sender and Server Protocol Receiver ..... 6
+ 2.2.2. Server Protocol Sender and Client Protocol Receiver ..... 7
+ 2.3. Message Attributes ...................................... 8
+ 2.3.1. Message Numbers ......................................... 8
+ 2.3.1.1. Unique Identifier (UID) Message Attribute ....... 8
+ 2.3.1.2. Message Sequence Number Message Attribute ....... 10
+ 2.3.2. Flags Message Attribute ................................. 11
+ 2.3.3. Internal Date Message Attribute ......................... 12
+ 2.3.4. [RFC-2822] Size Message Attribute ....................... 12
+ 2.3.5. Envelope Structure Message Attribute .................... 12
+ 2.3.6. Body Structure Message Attribute ........................ 12
+ 2.4. Message Texts ........................................... 13
+ 3. State and Flow Diagram .................................. 13
+ 3.1. Not Authenticated State ................................. 13
+ 3.2. Authenticated State ..................................... 13
+ 3.3. Selected State .......................................... 13
+ 3.4. Logout State ............................................ 14
+ 4. Data Formats ............................................ 16
+ 4.1. Atom .................................................... 16
+ 4.2. Number .................................................. 16
+ 4.3. String .................................................. 16
+ 4.3.1. 8-bit and Binary Strings ................................ 17
+ 4.4. Parenthesized List ...................................... 17
+ 4.5. NIL ..................................................... 17
+ 5. Operational Considerations .............................. 18
+ 5.1. Mailbox Naming .......................................... 18
+ 5.1.1. Mailbox Hierarchy Naming ................................ 19
+ 5.1.2. Mailbox Namespace Naming Convention ..................... 19
+ 5.1.3. Mailbox International Naming Convention ................. 19
+ 5.2. Mailbox Size and Message Status Updates ................. 21
+ 5.3. Response when no Command in Progress .................... 21
+ 5.4. Autologout Timer ........................................ 22
+ 5.5. Multiple Commands in Progress ........................... 22
+ 6. Client Commands ........................................ 23
+ 6.1. Client Commands - Any State ............................ 24
+ 6.1.1. CAPABILITY Command ..................................... 24
+ 6.1.2. NOOP Command ........................................... 25
+ 6.1.3. LOGOUT Command ......................................... 26
+
+
+
+Crispin Standards Track [Page 2]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ 6.2. Client Commands - Not Authenticated State .............. 26
+ 6.2.1. STARTTLS Command ....................................... 27
+ 6.2.2. AUTHENTICATE Command ................................... 28
+ 6.2.3. LOGIN Command .......................................... 30
+ 6.3. Client Commands - Authenticated State .................. 31
+ 6.3.1. SELECT Command ......................................... 32
+ 6.3.2. EXAMINE Command ........................................ 34
+ 6.3.3. CREATE Command ......................................... 34
+ 6.3.4. DELETE Command ......................................... 35
+ 6.3.5. RENAME Command ......................................... 37
+ 6.3.6. SUBSCRIBE Command ...................................... 39
+ 6.3.7. UNSUBSCRIBE Command .................................... 39
+ 6.3.8. LIST Command ........................................... 40
+ 6.3.9. LSUB Command ........................................... 43
+ 6.3.10. STATUS Command ......................................... 44
+ 6.3.11. APPEND Command ......................................... 46
+ 6.4. Client Commands - Selected State ....................... 47
+ 6.4.1. CHECK Command .......................................... 47
+ 6.4.2. CLOSE Command .......................................... 48
+ 6.4.3. EXPUNGE Command ........................................ 49
+ 6.4.4. SEARCH Command ......................................... 49
+ 6.4.5. FETCH Command .......................................... 54
+ 6.4.6. STORE Command .......................................... 58
+ 6.4.7. COPY Command ........................................... 59
+ 6.4.8. UID Command ............................................ 60
+ 6.5. Client Commands - Experimental/Expansion ............... 62
+ 6.5.1. X<atom> Command ........................................ 62
+ 7. Server Responses ....................................... 62
+ 7.1. Server Responses - Status Responses .................... 63
+ 7.1.1. OK Response ............................................ 65
+ 7.1.2. NO Response ............................................ 66
+ 7.1.3. BAD Response ........................................... 66
+ 7.1.4. PREAUTH Response ....................................... 67
+ 7.1.5. BYE Response ........................................... 67
+ 7.2. Server Responses - Server and Mailbox Status ........... 68
+ 7.2.1. CAPABILITY Response .................................... 68
+ 7.2.2. LIST Response .......................................... 69
+ 7.2.3. LSUB Response .......................................... 70
+ 7.2.4 STATUS Response ........................................ 70
+ 7.2.5. SEARCH Response ........................................ 71
+ 7.2.6. FLAGS Response ......................................... 71
+ 7.3. Server Responses - Mailbox Size ........................ 71
+ 7.3.1. EXISTS Response ........................................ 71
+ 7.3.2. RECENT Response ........................................ 72
+ 7.4. Server Responses - Message Status ...................... 72
+ 7.4.1. EXPUNGE Response ....................................... 72
+ 7.4.2. FETCH Response ......................................... 73
+ 7.5. Server Responses - Command Continuation Request ........ 79
+
+
+
+Crispin Standards Track [Page 3]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ 8. Sample IMAP4rev1 connection ............................ 80
+ 9. Formal Syntax .......................................... 81
+ 10. Author's Note .......................................... 92
+ 11. Security Considerations ................................ 92
+ 11.1. STARTTLS Security Considerations ....................... 92
+ 11.2. Other Security Considerations .......................... 93
+ 12. IANA Considerations .................................... 94
+ Appendices ..................................................... 95
+ A. References ............................................. 95
+ B. Changes from RFC 2060 .................................. 97
+ C. Key Word Index ......................................... 103
+ Author's Address ............................................... 107
+ Full Copyright Statement ....................................... 108
+
+IMAP4rev1 Protocol Specification
+
+1. How to Read This Document
+
+1.1. Organization of This Document
+
+ This document is written from the point of view of the implementor of
+ an IMAP4rev1 client or server. Beyond the protocol overview in
+ section 2, it is not optimized for someone trying to understand the
+ operation of the protocol. The material in sections 3 through 5
+ provides the general context and definitions with which IMAP4rev1
+ operates.
+
+ Sections 6, 7, and 9 describe the IMAP commands, responses, and
+ syntax, respectively. The relationships among these are such that it
+ is almost impossible to understand any of them separately. In
+ particular, do not attempt to deduce command syntax from the command
+ section alone; instead refer to the Formal Syntax section.
+
+1.2. Conventions Used in This Document
+
+ "Conventions" are basic principles or procedures. Document
+ conventions are noted in this section.
+
+ In examples, "C:" and "S:" indicate lines sent by the client and
+ server respectively.
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "MAY", and "OPTIONAL" in this document are to
+ be interpreted as described in [KEYWORDS].
+
+ The word "can" (not "may") is used to refer to a possible
+ circumstance or situation, as opposed to an optional facility of the
+ protocol.
+
+
+
+Crispin Standards Track [Page 4]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ "User" is used to refer to a human user, whereas "client" refers to
+ the software being run by the user.
+
+ "Connection" refers to the entire sequence of client/server
+ interaction from the initial establishment of the network connection
+ until its termination.
+
+ "Session" refers to the sequence of client/server interaction from
+ the time that a mailbox is selected (SELECT or EXAMINE command) until
+ the time that selection ends (SELECT or EXAMINE of another mailbox,
+ CLOSE command, or connection termination).
+
+ Characters are 7-bit US-ASCII unless otherwise specified. Other
+ character sets are indicated using a "CHARSET", as described in
+ [MIME-IMT] and defined in [CHARSET]. CHARSETs have important
+ additional semantics in addition to defining character set; refer to
+ these documents for more detail.
+
+ There are several protocol conventions in IMAP. These refer to
+ aspects of the specification which are not strictly part of the IMAP
+ protocol, but reflect generally-accepted practice. Implementations
+ need to be aware of these conventions, and avoid conflicts whether or
+ not they implement the convention. For example, "&" may not be used
+ as a hierarchy delimiter since it conflicts with the Mailbox
+ International Naming Convention, and other uses of "&" in mailbox
+ names are impacted as well.
+
+1.3. Special Notes to Implementors
+
+ Implementors of the IMAP protocol are strongly encouraged to read the
+ IMAP implementation recommendations document [IMAP-IMPLEMENTATION] in
+ conjunction with this document, to help understand the intricacies of
+ this protocol and how best to build an interoperable product.
+
+ IMAP4rev1 is designed to be upwards compatible from the [IMAP2] and
+ unpublished IMAP2bis protocols. IMAP4rev1 is largely compatible with
+ the IMAP4 protocol described in RFC 1730; the exception being in
+ certain facilities added in RFC 1730 that proved problematic and were
+ subsequently removed. In the course of the evolution of IMAP4rev1,
+ some aspects in the earlier protocols have become obsolete. Obsolete
+ commands, responses, and data formats which an IMAP4rev1
+ implementation can encounter when used with an earlier implementation
+ are described in [IMAP-OBSOLETE].
+
+ Other compatibility issues with IMAP2bis, the most common variant of
+ the earlier protocol, are discussed in [IMAP-COMPAT]. A full
+ discussion of compatibility issues with rare (and presumed extinct)
+
+
+
+
+Crispin Standards Track [Page 5]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ variants of [IMAP2] is in [IMAP-HISTORICAL]; this document is
+ primarily of historical interest.
+
+ IMAP was originally developed for the older [RFC-822] standard, and
+ as a consequence several fetch items in IMAP incorporate "RFC822" in
+ their name. With the exception of RFC822.SIZE, there are more modern
+ replacements; for example, the modern version of RFC822.HEADER is
+ BODY.PEEK[HEADER]. In all cases, "RFC822" should be interpreted as a
+ reference to the updated [RFC-2822] standard.
+
+2. Protocol Overview
+
+2.1. Link Level
+
+ The IMAP4rev1 protocol assumes a reliable data stream such as that
+ provided by TCP. When TCP is used, an IMAP4rev1 server listens on
+ port 143.
+
+2.2. Commands and Responses
+
+ An IMAP4rev1 connection consists of the establishment of a
+ client/server network connection, an initial greeting from the
+ server, and client/server interactions. These client/server
+ interactions consist of a client command, server data, and a server
+ completion result response.
+
+ All interactions transmitted by client and server are in the form of
+ lines, that is, strings that end with a CRLF. The protocol receiver
+ of an IMAP4rev1 client or server is either reading a line, or is
+ reading a sequence of octets with a known count followed by a line.
+
+2.2.1. Client Protocol Sender and Server Protocol Receiver
+
+ The client command begins an operation. Each client command is
+ prefixed with an identifier (typically a short alphanumeric string,
+ e.g., A0001, A0002, etc.) called a "tag". A different tag is
+ generated by the client for each command.
+
+ Clients MUST follow the syntax outlined in this specification
+ strictly. It is a syntax error to send a command with missing or
+ extraneous spaces or arguments.
+
+ There are two cases in which a line from the client does not
+ represent a complete command. In one case, a command argument is
+ quoted with an octet count (see the description of literal in String
+ under Data Formats); in the other case, the command arguments require
+ server feedback (see the AUTHENTICATE command). In either case, the
+
+
+
+
+Crispin Standards Track [Page 6]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ server sends a command continuation request response if it is ready
+ for the octets (if appropriate) and the remainder of the command.
+ This response is prefixed with the token "+".
+
+ Note: If instead, the server detected an error in the
+ command, it sends a BAD completion response with a tag
+ matching the command (as described below) to reject the
+ command and prevent the client from sending any more of the
+ command.
+
+ It is also possible for the server to send a completion
+ response for some other command (if multiple commands are
+ in progress), or untagged data. In either case, the
+ command continuation request is still pending; the client
+ takes the appropriate action for the response, and reads
+ another response from the server. In all cases, the client
+ MUST send a complete command (including receiving all
+ command continuation request responses and command
+ continuations for the command) before initiating a new
+ command.
+
+ The protocol receiver of an IMAP4rev1 server reads a command line
+ from the client, parses the command and its arguments, and transmits
+ server data and a server command completion result response.
+
+2.2.2. Server Protocol Sender and Client Protocol Receiver
+
+ Data transmitted by the server to the client and status responses
+ that do not indicate command completion are prefixed with the token
+ "*", and are called untagged responses.
+
+ Server data MAY be sent as a result of a client command, or MAY be
+ sent unilaterally by the server. There is no syntactic difference
+ between server data that resulted from a specific command and server
+ data that were sent unilaterally.
+
+ The server completion result response indicates the success or
+ failure of the operation. It is tagged with the same tag as the
+ client command which began the operation. Thus, if more than one
+ command is in progress, the tag in a server completion response
+ identifies the command to which the response applies. There are
+ three possible server completion responses: OK (indicating success),
+ NO (indicating failure), or BAD (indicating a protocol error such as
+ unrecognized command or command syntax error).
+
+ Servers SHOULD enforce the syntax outlined in this specification
+ strictly. Any client command with a protocol syntax error, including
+ (but not limited to) missing or extraneous spaces or arguments,
+
+
+
+Crispin Standards Track [Page 7]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ SHOULD be rejected, and the client given a BAD server completion
+ response.
+
+ The protocol receiver of an IMAP4rev1 client reads a response line
+ from the server. It then takes action on the response based upon the
+ first token of the response, which can be a tag, a "*", or a "+".
+
+ A client MUST be prepared to accept any server response at all times.
+ This includes server data that was not requested. Server data SHOULD
+ be recorded, so that the client can reference its recorded copy
+ rather than sending a command to the server to request the data. In
+ the case of certain server data, the data MUST be recorded.
+
+ This topic is discussed in greater detail in the Server Responses
+ section.
+
+2.3. Message Attributes
+
+ In addition to message text, each message has several attributes
+ associated with it. These attributes can be retrieved individually
+ or in conjunction with other attributes or message texts.
+
+2.3.1. Message Numbers
+
+ Messages in IMAP4rev1 are accessed by one of two numbers; the unique
+ identifier or the message sequence number.
+
+
+2.3.1.1. Unique Identifier (UID) Message Attribute
+
+ A 32-bit value assigned to each message, which when used with the
+ unique identifier validity value (see below) forms a 64-bit value
+ that MUST NOT refer to any other message in the mailbox or any
+ subsequent mailbox with the same name forever. Unique identifiers
+ are assigned in a strictly ascending fashion in the mailbox; as each
+ message is added to the mailbox it is assigned a higher UID than the
+ message(s) which were added previously. Unlike message sequence
+ numbers, unique identifiers are not necessarily contiguous.
+
+ The unique identifier of a message MUST NOT change during the
+ session, and SHOULD NOT change between sessions. Any change of
+ unique identifiers between sessions MUST be detectable using the
+ UIDVALIDITY mechanism discussed below. Persistent unique identifiers
+ are required for a client to resynchronize its state from a previous
+ session with the server (e.g., disconnected or offline access
+ clients); this is discussed further in [IMAP-DISC].
+
+
+
+
+
+Crispin Standards Track [Page 8]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ Associated with every mailbox are two values which aid in unique
+ identifier handling: the next unique identifier value and the unique
+ identifier validity value.
+
+ The next unique identifier value is the predicted value that will be
+ assigned to a new message in the mailbox. Unless the unique
+ identifier validity also changes (see below), the next unique
+ identifier value MUST have the following two characteristics. First,
+ the next unique identifier value MUST NOT change unless new messages
+ are added to the mailbox; and second, the next unique identifier
+ value MUST change whenever new messages are added to the mailbox,
+ even if those new messages are subsequently expunged.
+
+ Note: The next unique identifier value is intended to
+ provide a means for a client to determine whether any
+ messages have been delivered to the mailbox since the
+ previous time it checked this value. It is not intended to
+ provide any guarantee that any message will have this
+ unique identifier. A client can only assume, at the time
+ that it obtains the next unique identifier value, that
+ messages arriving after that time will have a UID greater
+ than or equal to that value.
+
+ The unique identifier validity value is sent in a UIDVALIDITY
+ response code in an OK untagged response at mailbox selection time.
+ If unique identifiers from an earlier session fail to persist in this
+ session, the unique identifier validity value MUST be greater than
+ the one used in the earlier session.
+
+ Note: Ideally, unique identifiers SHOULD persist at all
+ times. Although this specification recognizes that failure
+ to persist can be unavoidable in certain server
+ environments, it STRONGLY ENCOURAGES message store
+ implementation techniques that avoid this problem. For
+ example:
+
+ 1) Unique identifiers MUST be strictly ascending in the
+ mailbox at all times. If the physical message store is
+ re-ordered by a non-IMAP agent, this requires that the
+ unique identifiers in the mailbox be regenerated, since
+ the former unique identifiers are no longer strictly
+ ascending as a result of the re-ordering.
+
+ 2) If the message store has no mechanism to store unique
+ identifiers, it must regenerate unique identifiers at
+ each session, and each session must have a unique
+ UIDVALIDITY value.
+
+
+
+
+Crispin Standards Track [Page 9]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ 3) If the mailbox is deleted and a new mailbox with the
+ same name is created at a later date, the server must
+ either keep track of unique identifiers from the
+ previous instance of the mailbox, or it must assign a
+ new UIDVALIDITY value to the new instance of the
+ mailbox. A good UIDVALIDITY value to use in this case
+ is a 32-bit representation of the creation date/time of
+ the mailbox. It is alright to use a constant such as
+ 1, but only if it guaranteed that unique identifiers
+ will never be reused, even in the case of a mailbox
+ being deleted (or renamed) and a new mailbox by the
+ same name created at some future time.
+
+ 4) The combination of mailbox name, UIDVALIDITY, and UID
+ must refer to a single immutable message on that server
+ forever. In particular, the internal date, [RFC-2822]
+ size, envelope, body structure, and message texts
+ (RFC822, RFC822.HEADER, RFC822.TEXT, and all BODY[...]
+ fetch data items) must never change. This does not
+ include message numbers, nor does it include attributes
+ that can be set by a STORE command (e.g., FLAGS).
+
+
+2.3.1.2. Message Sequence Number Message Attribute
+
+ A relative position from 1 to the number of messages in the mailbox.
+ This position MUST be ordered by ascending unique identifier. As
+ each new message is added, it is assigned a message sequence number
+ that is 1 higher than the number of messages in the mailbox before
+ that new message was added.
+
+ Message sequence numbers can be reassigned during the session. For
+ example, when a message is permanently removed (expunged) from the
+ mailbox, the message sequence number for all subsequent messages is
+ decremented. The number of messages in the mailbox is also
+ decremented. Similarly, a new message can be assigned a message
+ sequence number that was once held by some other message prior to an
+ expunge.
+
+ In addition to accessing messages by relative position in the
+ mailbox, message sequence numbers can be used in mathematical
+ calculations. For example, if an untagged "11 EXISTS" is received,
+ and previously an untagged "8 EXISTS" was received, three new
+ messages have arrived with message sequence numbers of 9, 10, and 11.
+ Another example, if message 287 in a 523 message mailbox has UID
+ 12345, there are exactly 286 messages which have lesser UIDs and 236
+ messages which have greater UIDs.
+
+
+
+
+Crispin Standards Track [Page 10]
+
+RFC 3501 IMAPv4 March 2003
+
+
+2.3.2. Flags Message Attribute
+
+ A list of zero or more named tokens associated with the message. A
+ flag is set by its addition to this list, and is cleared by its
+ removal. There are two types of flags in IMAP4rev1. A flag of
+ either type can be permanent or session-only.
+
+ A system flag is a flag name that is pre-defined in this
+ specification. All system flags begin with "\". Certain system
+ flags (\Deleted and \Seen) have special semantics described
+ elsewhere. The currently-defined system flags are:
+
+ \Seen
+ Message has been read
+
+ \Answered
+ Message has been answered
+
+ \Flagged
+ Message is "flagged" for urgent/special attention
+
+ \Deleted
+ Message is "deleted" for removal by later EXPUNGE
+
+ \Draft
+ Message has not completed composition (marked as a draft).
+
+ \Recent
+ Message is "recently" arrived in this mailbox. This session
+ is the first session to have been notified about this
+ message; if the session is read-write, subsequent sessions
+ will not see \Recent set for this message. This flag can not
+ be altered by the client.
+
+ If it is not possible to determine whether or not this
+ session is the first session to be notified about a message,
+ then that message SHOULD be considered recent.
+
+ If multiple connections have the same mailbox selected
+ simultaneously, it is undefined which of these connections
+ will see newly-arrived messages with \Recent set and which
+ will see it without \Recent set.
+
+ A keyword is defined by the server implementation. Keywords do not
+ begin with "\". Servers MAY permit the client to define new keywords
+ in the mailbox (see the description of the PERMANENTFLAGS response
+ code for more information).
+
+
+
+
+Crispin Standards Track [Page 11]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ A flag can be permanent or session-only on a per-flag basis.
+ Permanent flags are those which the client can add or remove from the
+ message flags permanently; that is, concurrent and subsequent
+ sessions will see any change in permanent flags. Changes to session
+ flags are valid only in that session.
+
+ Note: The \Recent system flag is a special case of a
+ session flag. \Recent can not be used as an argument in a
+ STORE or APPEND command, and thus can not be changed at
+ all.
+
+2.3.3. Internal Date Message Attribute
+
+ The internal date and time of the message on the server. This
+ is not the date and time in the [RFC-2822] header, but rather a
+ date and time which reflects when the message was received. In
+ the case of messages delivered via [SMTP], this SHOULD be the
+ date and time of final delivery of the message as defined by
+ [SMTP]. In the case of messages delivered by the IMAP4rev1 COPY
+ command, this SHOULD be the internal date and time of the source
+ message. In the case of messages delivered by the IMAP4rev1
+ APPEND command, this SHOULD be the date and time as specified in
+ the APPEND command description. All other cases are
+ implementation defined.
+
+2.3.4. [RFC-2822] Size Message Attribute
+
+ The number of octets in the message, as expressed in [RFC-2822]
+ format.
+
+2.3.5. Envelope Structure Message Attribute
+
+ A parsed representation of the [RFC-2822] header of the message.
+ Note that the IMAP Envelope structure is not the same as an
+ [SMTP] envelope.
+
+2.3.6. Body Structure Message Attribute
+
+ A parsed representation of the [MIME-IMB] body structure
+ information of the message.
+
+
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 12]
+
+RFC 3501 IMAPv4 March 2003
+
+
+2.4. Message Texts
+
+ In addition to being able to fetch the full [RFC-2822] text of a
+ message, IMAP4rev1 permits the fetching of portions of the full
+ message text. Specifically, it is possible to fetch the
+ [RFC-2822] message header, [RFC-2822] message body, a [MIME-IMB]
+ body part, or a [MIME-IMB] header.
+
+3. State and Flow Diagram
+
+ Once the connection between client and server is established, an
+ IMAP4rev1 connection is in one of four states. The initial
+ state is identified in the server greeting. Most commands are
+ only valid in certain states. It is a protocol error for the
+ client to attempt a command while the connection is in an
+ inappropriate state, and the server will respond with a BAD or
+ NO (depending upon server implementation) command completion
+ result.
+
+3.1. Not Authenticated State
+
+ In the not authenticated state, the client MUST supply
+ authentication credentials before most commands will be
+ permitted. This state is entered when a connection starts
+ unless the connection has been pre-authenticated.
+
+3.2. Authenticated State
+
+ In the authenticated state, the client is authenticated and MUST
+ select a mailbox to access before commands that affect messages
+ will be permitted. This state is entered when a
+ pre-authenticated connection starts, when acceptable
+ authentication credentials have been provided, after an error in
+ selecting a mailbox, or after a successful CLOSE command.
+
+3.3. Selected State
+
+ In a selected state, a mailbox has been selected to access.
+ This state is entered when a mailbox has been successfully
+ selected.
+
+
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 13]
+
+RFC 3501 IMAPv4 March 2003
+
+
+3.4. Logout State
+
+ In the logout state, the connection is being terminated. This
+ state can be entered as a result of a client request (via the
+ LOGOUT command) or by unilateral action on the part of either
+ the client or server.
+
+ If the client requests the logout state, the server MUST send an
+ untagged BYE response and a tagged OK response to the LOGOUT
+ command before the server closes the connection; and the client
+ MUST read the tagged OK response to the LOGOUT command before
+ the client closes the connection.
+
+ A server MUST NOT unilaterally close the connection without
+ sending an untagged BYE response that contains the reason for
+ having done so. A client SHOULD NOT unilaterally close the
+ connection, and instead SHOULD issue a LOGOUT command. If the
+ server detects that the client has unilaterally closed the
+ connection, the server MAY omit the untagged BYE response and
+ simply close its connection.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 14]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ +----------------------+
+ |connection established|
+ +----------------------+
+ ||
+ \/
+ +--------------------------------------+
+ | server greeting |
+ +--------------------------------------+
+ || (1) || (2) || (3)
+ \/ || ||
+ +-----------------+ || ||
+ |Not Authenticated| || ||
+ +-----------------+ || ||
+ || (7) || (4) || ||
+ || \/ \/ ||
+ || +----------------+ ||
+ || | Authenticated |<=++ ||
+ || +----------------+ || ||
+ || || (7) || (5) || (6) ||
+ || || \/ || ||
+ || || +--------+ || ||
+ || || |Selected|==++ ||
+ || || +--------+ ||
+ || || || (7) ||
+ \/ \/ \/ \/
+ +--------------------------------------+
+ | Logout |
+ +--------------------------------------+
+ ||
+ \/
+ +-------------------------------+
+ |both sides close the connection|
+ +-------------------------------+
+
+ (1) connection without pre-authentication (OK greeting)
+ (2) pre-authenticated connection (PREAUTH greeting)
+ (3) rejected connection (BYE greeting)
+ (4) successful LOGIN or AUTHENTICATE command
+ (5) successful SELECT or EXAMINE command
+ (6) CLOSE command, or failed SELECT or EXAMINE command
+ (7) LOGOUT command, server shutdown, or connection closed
+
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 15]
+
+RFC 3501 IMAPv4 March 2003
+
+
+4. Data Formats
+
+ IMAP4rev1 uses textual commands and responses. Data in
+ IMAP4rev1 can be in one of several forms: atom, number, string,
+ parenthesized list, or NIL. Note that a particular data item
+ may take more than one form; for example, a data item defined as
+ using "astring" syntax may be either an atom or a string.
+
+4.1. Atom
+
+ An atom consists of one or more non-special characters.
+
+4.2. Number
+
+ A number consists of one or more digit characters, and
+ represents a numeric value.
+
+4.3. String
+
+ A string is in one of two forms: either literal or quoted
+ string. The literal form is the general form of string. The
+ quoted string form is an alternative that avoids the overhead of
+ processing a literal at the cost of limitations of characters
+ which may be used.
+
+ A literal is a sequence of zero or more octets (including CR and
+ LF), prefix-quoted with an octet count in the form of an open
+ brace ("{"), the number of octets, close brace ("}"), and CRLF.
+ In the case of literals transmitted from server to client, the
+ CRLF is immediately followed by the octet data. In the case of
+ literals transmitted from client to server, the client MUST wait
+ to receive a command continuation request (described later in
+ this document) before sending the octet data (and the remainder
+ of the command).
+
+ A quoted string is a sequence of zero or more 7-bit characters,
+ excluding CR and LF, with double quote (<">) characters at each
+ end.
+
+ The empty string is represented as either "" (a quoted string
+ with zero characters between double quotes) or as {0} followed
+ by CRLF (a literal with an octet count of 0).
+
+ Note: Even if the octet count is 0, a client transmitting a
+ literal MUST wait to receive a command continuation request.
+
+
+
+
+
+
+Crispin Standards Track [Page 16]
+
+RFC 3501 IMAPv4 March 2003
+
+
+4.3.1. 8-bit and Binary Strings
+
+ 8-bit textual and binary mail is supported through the use of a
+ [MIME-IMB] content transfer encoding. IMAP4rev1 implementations MAY
+ transmit 8-bit or multi-octet characters in literals, but SHOULD do
+ so only when the [CHARSET] is identified.
+
+ Although a BINARY body encoding is defined, unencoded binary strings
+ are not permitted. A "binary string" is any string with NUL
+ characters. Implementations MUST encode binary data into a textual
+ form, such as BASE64, before transmitting the data. A string with an
+ excessive amount of CTL characters MAY also be considered to be
+ binary.
+
+4.4. Parenthesized List
+
+ Data structures are represented as a "parenthesized list"; a sequence
+ of data items, delimited by space, and bounded at each end by
+ parentheses. A parenthesized list can contain other parenthesized
+ lists, using multiple levels of parentheses to indicate nesting.
+
+ The empty list is represented as () -- a parenthesized list with no
+ members.
+
+4.5. NIL
+
+ The special form "NIL" represents the non-existence of a particular
+ data item that is represented as a string or parenthesized list, as
+ distinct from the empty string "" or the empty parenthesized list ().
+
+ Note: NIL is never used for any data item which takes the
+ form of an atom. For example, a mailbox name of "NIL" is a
+ mailbox named NIL as opposed to a non-existent mailbox
+ name. This is because mailbox uses "astring" syntax which
+ is an atom or a string. Conversely, an addr-name of NIL is
+ a non-existent personal name, because addr-name uses
+ "nstring" syntax which is NIL or a string, but never an
+ atom.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 17]
+
+RFC 3501 IMAPv4 March 2003
+
+
+5. Operational Considerations
+
+ The following rules are listed here to ensure that all IMAP4rev1
+ implementations interoperate properly.
+
+5.1. Mailbox Naming
+
+ Mailbox names are 7-bit. Client implementations MUST NOT attempt to
+ create 8-bit mailbox names, and SHOULD interpret any 8-bit mailbox
+ names returned by LIST or LSUB as UTF-8. Server implementations
+ SHOULD prohibit the creation of 8-bit mailbox names, and SHOULD NOT
+ return 8-bit mailbox names in LIST or LSUB. See section 5.1.3 for
+ more information on how to represent non-ASCII mailbox names.
+
+ Note: 8-bit mailbox names were undefined in earlier
+ versions of this protocol. Some sites used a local 8-bit
+ character set to represent non-ASCII mailbox names. Such
+ usage is not interoperable, and is now formally deprecated.
+
+ The case-insensitive mailbox name INBOX is a special name reserved to
+ mean "the primary mailbox for this user on this server". The
+ interpretation of all other names is implementation-dependent.
+
+ In particular, this specification takes no position on case
+ sensitivity in non-INBOX mailbox names. Some server implementations
+ are fully case-sensitive; others preserve case of a newly-created
+ name but otherwise are case-insensitive; and yet others coerce names
+ to a particular case. Client implementations MUST interact with any
+ of these. If a server implementation interprets non-INBOX mailbox
+ names as case-insensitive, it MUST treat names using the
+ international naming convention specially as described in section
+ 5.1.3.
+
+ There are certain client considerations when creating a new mailbox
+ name:
+
+ 1) Any character which is one of the atom-specials (see the Formal
+ Syntax) will require that the mailbox name be represented as a
+ quoted string or literal.
+
+ 2) CTL and other non-graphic characters are difficult to represent
+ in a user interface and are best avoided.
+
+ 3) Although the list-wildcard characters ("%" and "*") are valid
+ in a mailbox name, it is difficult to use such mailbox names
+ with the LIST and LSUB commands due to the conflict with
+ wildcard interpretation.
+
+
+
+
+Crispin Standards Track [Page 18]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ 4) Usually, a character (determined by the server implementation)
+ is reserved to delimit levels of hierarchy.
+
+ 5) Two characters, "#" and "&", have meanings by convention, and
+ should be avoided except when used in that convention.
+
+5.1.1. Mailbox Hierarchy Naming
+
+ If it is desired to export hierarchical mailbox names, mailbox names
+ MUST be left-to-right hierarchical using a single character to
+ separate levels of hierarchy. The same hierarchy separator character
+ is used for all levels of hierarchy within a single name.
+
+5.1.2. Mailbox Namespace Naming Convention
+
+ By convention, the first hierarchical element of any mailbox name
+ which begins with "#" identifies the "namespace" of the remainder of
+ the name. This makes it possible to disambiguate between different
+ types of mailbox stores, each of which have their own namespaces.
+
+ For example, implementations which offer access to USENET
+ newsgroups MAY use the "#news" namespace to partition the
+ USENET newsgroup namespace from that of other mailboxes.
+ Thus, the comp.mail.misc newsgroup would have a mailbox
+ name of "#news.comp.mail.misc", and the name
+ "comp.mail.misc" can refer to a different object (e.g., a
+ user's private mailbox).
+
+5.1.3. Mailbox International Naming Convention
+
+ By convention, international mailbox names in IMAP4rev1 are specified
+ using a modified version of the UTF-7 encoding described in [UTF-7].
+ Modified UTF-7 may also be usable in servers that implement an
+ earlier version of this protocol.
+
+ In modified UTF-7, printable US-ASCII characters, except for "&",
+ represent themselves; that is, characters with octet values 0x20-0x25
+ and 0x27-0x7e. The character "&" (0x26) is represented by the
+ two-octet sequence "&-".
+
+ All other characters (octet values 0x00-0x1f and 0x7f-0xff) are
+ represented in modified BASE64, with a further modification from
+ [UTF-7] that "," is used instead of "/". Modified BASE64 MUST NOT be
+ used to represent any printing US-ASCII character which can represent
+ itself.
+
+
+
+
+
+
+Crispin Standards Track [Page 19]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ "&" is used to shift to modified BASE64 and "-" to shift back to
+ US-ASCII. There is no implicit shift from BASE64 to US-ASCII, and
+ null shifts ("-&" while in BASE64; note that "&-" while in US-ASCII
+ means "&") are not permitted. However, all names start in US-ASCII,
+ and MUST end in US-ASCII; that is, a name that ends with a non-ASCII
+ ISO-10646 character MUST end with a "-").
+
+ The purpose of these modifications is to correct the following
+ problems with UTF-7:
+
+ 1) UTF-7 uses the "+" character for shifting; this conflicts with
+ the common use of "+" in mailbox names, in particular USENET
+ newsgroup names.
+
+ 2) UTF-7's encoding is BASE64 which uses the "/" character; this
+ conflicts with the use of "/" as a popular hierarchy delimiter.
+
+ 3) UTF-7 prohibits the unencoded usage of "\"; this conflicts with
+ the use of "\" as a popular hierarchy delimiter.
+
+ 4) UTF-7 prohibits the unencoded usage of "~"; this conflicts with
+ the use of "~" in some servers as a home directory indicator.
+
+ 5) UTF-7 permits multiple alternate forms to represent the same
+ string; in particular, printable US-ASCII characters can be
+ represented in encoded form.
+
+ Although modified UTF-7 is a convention, it establishes certain
+ requirements on server handling of any mailbox name with an
+ embedded "&" character. In particular, server implementations
+ MUST preserve the exact form of the modified BASE64 portion of a
+ modified UTF-7 name and treat that text as case-sensitive, even if
+ names are otherwise case-insensitive or case-folded.
+
+ Server implementations SHOULD verify that any mailbox name with an
+ embedded "&" character, used as an argument to CREATE, is: in the
+ correctly modified UTF-7 syntax, has no superfluous shifts, and
+ has no encoding in modified BASE64 of any printing US-ASCII
+ character which can represent itself. However, client
+ implementations MUST NOT depend upon the server doing this, and
+ SHOULD NOT attempt to create a mailbox name with an embedded "&"
+ character unless it complies with the modified UTF-7 syntax.
+
+ Server implementations which export a mail store that does not
+ follow the modified UTF-7 convention MUST convert to modified
+ UTF-7 any mailbox name that contains either non-ASCII characters
+ or the "&" character.
+
+
+
+
+Crispin Standards Track [Page 20]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ For example, here is a mailbox name which mixes English,
+ Chinese, and Japanese text:
+ ~peter/mail/&U,BTFw-/&ZeVnLIqe-
+
+ For example, the string "&Jjo!" is not a valid mailbox
+ name because it does not contain a shift to US-ASCII
+ before the "!". The correct form is "&Jjo-!". The
+ string "&U,BTFw-&ZeVnLIqe-" is not permitted because it
+ contains a superfluous shift. The correct form is
+ "&U,BTF2XlZyyKng-".
+
+5.2. Mailbox Size and Message Status Updates
+
+ At any time, a server can send data that the client did not request.
+ Sometimes, such behavior is REQUIRED. For example, agents other than
+ the server MAY add messages to the mailbox (e.g., new message
+ delivery), change the flags of the messages in the mailbox (e.g.,
+ simultaneous access to the same mailbox by multiple agents), or even
+ remove messages from the mailbox. A server MUST send mailbox size
+ updates automatically if a mailbox size change is observed during the
+ processing of a command. A server SHOULD send message flag updates
+ automatically, without requiring the client to request such updates
+ explicitly.
+
+ Special rules exist for server notification of a client about the
+ removal of messages to prevent synchronization errors; see the
+ description of the EXPUNGE response for more detail. In particular,
+ it is NOT permitted to send an EXISTS response that would reduce the
+ number of messages in the mailbox; only the EXPUNGE response can do
+ this.
+
+ Regardless of what implementation decisions a client makes on
+ remembering data from the server, a client implementation MUST record
+ mailbox size updates. It MUST NOT assume that any command after the
+ initial mailbox selection will return the size of the mailbox.
+
+5.3. Response when no Command in Progress
+
+ Server implementations are permitted to send an untagged response
+ (except for EXPUNGE) while there is no command in progress. Server
+ implementations that send such responses MUST deal with flow control
+ considerations. Specifically, they MUST either (1) verify that the
+ size of the data does not exceed the underlying transport's available
+ window size, or (2) use non-blocking writes.
+
+
+
+
+
+
+
+Crispin Standards Track [Page 21]
+
+RFC 3501 IMAPv4 March 2003
+
+
+5.4. Autologout Timer
+
+ If a server has an inactivity autologout timer, the duration of that
+ timer MUST be at least 30 minutes. The receipt of ANY command from
+ the client during that interval SHOULD suffice to reset the
+ autologout timer.
+
+5.5. Multiple Commands in Progress
+
+ The client MAY send another command without waiting for the
+ completion result response of a command, subject to ambiguity rules
+ (see below) and flow control constraints on the underlying data
+ stream. Similarly, a server MAY begin processing another command
+ before processing the current command to completion, subject to
+ ambiguity rules. However, any command continuation request responses
+ and command continuations MUST be negotiated before any subsequent
+ command is initiated.
+
+ The exception is if an ambiguity would result because of a command
+ that would affect the results of other commands. Clients MUST NOT
+ send multiple commands without waiting if an ambiguity would result.
+ If the server detects a possible ambiguity, it MUST execute commands
+ to completion in the order given by the client.
+
+ The most obvious example of ambiguity is when a command would affect
+ the results of another command, e.g., a FETCH of a message's flags
+ and a STORE of that same message's flags.
+
+ A non-obvious ambiguity occurs with commands that permit an untagged
+ EXPUNGE response (commands other than FETCH, STORE, and SEARCH),
+ since an untagged EXPUNGE response can invalidate sequence numbers in
+ a subsequent command. This is not a problem for FETCH, STORE, or
+ SEARCH commands because servers are prohibited from sending EXPUNGE
+ responses while any of those commands are in progress. Therefore, if
+ the client sends any command other than FETCH, STORE, or SEARCH, it
+ MUST wait for the completion result response before sending a command
+ with message sequence numbers.
+
+ Note: UID FETCH, UID STORE, and UID SEARCH are different
+ commands from FETCH, STORE, and SEARCH. If the client
+ sends a UID command, it must wait for a completion result
+ response before sending a command with message sequence
+ numbers.
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 22]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ For example, the following non-waiting command sequences are invalid:
+
+ FETCH + NOOP + STORE
+ STORE + COPY + FETCH
+ COPY + COPY
+ CHECK + FETCH
+
+ The following are examples of valid non-waiting command sequences:
+
+ FETCH + STORE + SEARCH + CHECK
+ STORE + COPY + EXPUNGE
+
+ UID SEARCH + UID SEARCH may be valid or invalid as a non-waiting
+ command sequence, depending upon whether or not the second UID
+ SEARCH contains message sequence numbers.
+
+6. Client Commands
+
+ IMAP4rev1 commands are described in this section. Commands are
+ organized by the state in which the command is permitted. Commands
+ which are permitted in multiple states are listed in the minimum
+ permitted state (for example, commands valid in authenticated and
+ selected state are listed in the authenticated state commands).
+
+ Command arguments, identified by "Arguments:" in the command
+ descriptions below, are described by function, not by syntax. The
+ precise syntax of command arguments is described in the Formal Syntax
+ section.
+
+ Some commands cause specific server responses to be returned; these
+ are identified by "Responses:" in the command descriptions below.
+ See the response descriptions in the Responses section for
+ information on these responses, and the Formal Syntax section for the
+ precise syntax of these responses. It is possible for server data to
+ be transmitted as a result of any command. Thus, commands that do
+ not specifically require server data specify "no specific responses
+ for this command" instead of "none".
+
+ The "Result:" in the command description refers to the possible
+ tagged status responses to a command, and any special interpretation
+ of these status responses.
+
+ The state of a connection is only changed by successful commands
+ which are documented as changing state. A rejected command (BAD
+ response) never changes the state of the connection or of the
+ selected mailbox. A failed command (NO response) generally does not
+ change the state of the connection or of the selected mailbox; the
+ exception being the SELECT and EXAMINE commands.
+
+
+
+Crispin Standards Track [Page 23]
+
+RFC 3501 IMAPv4 March 2003
+
+
+6.1. Client Commands - Any State
+
+ The following commands are valid in any state: CAPABILITY, NOOP, and
+ LOGOUT.
+
+6.1.1. CAPABILITY Command
+
+ Arguments: none
+
+ Responses: REQUIRED untagged response: CAPABILITY
+
+ Result: OK - capability completed
+ BAD - command unknown or arguments invalid
+
+ The CAPABILITY command requests a listing of capabilities that the
+ server supports. The server MUST send a single untagged
+ CAPABILITY response with "IMAP4rev1" as one of the listed
+ capabilities before the (tagged) OK response.
+
+ A capability name which begins with "AUTH=" indicates that the
+ server supports that particular authentication mechanism. All
+ such names are, by definition, part of this specification. For
+ example, the authorization capability for an experimental
+ "blurdybloop" authenticator would be "AUTH=XBLURDYBLOOP" and not
+ "XAUTH=BLURDYBLOOP" or "XAUTH=XBLURDYBLOOP".
+
+ Other capability names refer to extensions, revisions, or
+ amendments to this specification. See the documentation of the
+ CAPABILITY response for additional information. No capabilities,
+ beyond the base IMAP4rev1 set defined in this specification, are
+ enabled without explicit client action to invoke the capability.
+
+ Client and server implementations MUST implement the STARTTLS,
+ LOGINDISABLED, and AUTH=PLAIN (described in [IMAP-TLS])
+ capabilities. See the Security Considerations section for
+ important information.
+
+ See the section entitled "Client Commands -
+ Experimental/Expansion" for information about the form of site or
+ implementation-specific capabilities.
+
+
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 24]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ Example: C: abcd CAPABILITY
+ S: * CAPABILITY IMAP4rev1 STARTTLS AUTH=GSSAPI
+ LOGINDISABLED
+ S: abcd OK CAPABILITY completed
+ C: efgh STARTTLS
+ S: efgh OK STARTLS completed
+ <TLS negotiation, further commands are under [TLS] layer>
+ C: ijkl CAPABILITY
+ S: * CAPABILITY IMAP4rev1 AUTH=GSSAPI AUTH=PLAIN
+ S: ijkl OK CAPABILITY completed
+
+
+6.1.2. NOOP Command
+
+ Arguments: none
+
+ Responses: no specific responses for this command (but see below)
+
+ Result: OK - noop completed
+ BAD - command unknown or arguments invalid
+
+ The NOOP command always succeeds. It does nothing.
+
+ Since any command can return a status update as untagged data, the
+ NOOP command can be used as a periodic poll for new messages or
+ message status updates during a period of inactivity (this is the
+ preferred method to do this). The NOOP command can also be used
+ to reset any inactivity autologout timer on the server.
+
+ Example: C: a002 NOOP
+ S: a002 OK NOOP completed
+ . . .
+ C: a047 NOOP
+ S: * 22 EXPUNGE
+ S: * 23 EXISTS
+ S: * 3 RECENT
+ S: * 14 FETCH (FLAGS (\Seen \Deleted))
+ S: a047 OK NOOP completed
+
+
+
+
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 25]
+
+RFC 3501 IMAPv4 March 2003
+
+
+6.1.3. LOGOUT Command
+
+ Arguments: none
+
+ Responses: REQUIRED untagged response: BYE
+
+ Result: OK - logout completed
+ BAD - command unknown or arguments invalid
+
+ The LOGOUT command informs the server that the client is done with
+ the connection. The server MUST send a BYE untagged response
+ before the (tagged) OK response, and then close the network
+ connection.
+
+ Example: C: A023 LOGOUT
+ S: * BYE IMAP4rev1 Server logging out
+ S: A023 OK LOGOUT completed
+ (Server and client then close the connection)
+
+6.2. Client Commands - Not Authenticated State
+
+ In the not authenticated state, the AUTHENTICATE or LOGIN command
+ establishes authentication and enters the authenticated state. The
+ AUTHENTICATE command provides a general mechanism for a variety of
+ authentication techniques, privacy protection, and integrity
+ checking; whereas the LOGIN command uses a traditional user name and
+ plaintext password pair and has no means of establishing privacy
+ protection or integrity checking.
+
+ The STARTTLS command is an alternate form of establishing session
+ privacy protection and integrity checking, but does not establish
+ authentication or enter the authenticated state.
+
+ Server implementations MAY allow access to certain mailboxes without
+ establishing authentication. This can be done by means of the
+ ANONYMOUS [SASL] authenticator described in [ANONYMOUS]. An older
+ convention is a LOGIN command using the userid "anonymous"; in this
+ case, a password is required although the server may choose to accept
+ any password. The restrictions placed on anonymous users are
+ implementation-dependent.
+
+ Once authenticated (including as anonymous), it is not possible to
+ re-enter not authenticated state.
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 26]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ In addition to the universal commands (CAPABILITY, NOOP, and LOGOUT),
+ the following commands are valid in the not authenticated state:
+ STARTTLS, AUTHENTICATE and LOGIN. See the Security Considerations
+ section for important information about these commands.
+
+6.2.1. STARTTLS Command
+
+ Arguments: none
+
+ Responses: no specific response for this command
+
+ Result: OK - starttls completed, begin TLS negotiation
+ BAD - command unknown or arguments invalid
+
+ A [TLS] negotiation begins immediately after the CRLF at the end
+ of the tagged OK response from the server. Once a client issues a
+ STARTTLS command, it MUST NOT issue further commands until a
+ server response is seen and the [TLS] negotiation is complete.
+
+ The server remains in the non-authenticated state, even if client
+ credentials are supplied during the [TLS] negotiation. This does
+ not preclude an authentication mechanism such as EXTERNAL (defined
+ in [SASL]) from using client identity determined by the [TLS]
+ negotiation.
+
+ Once [TLS] has been started, the client MUST discard cached
+ information about server capabilities and SHOULD re-issue the
+ CAPABILITY command. This is necessary to protect against man-in-
+ the-middle attacks which alter the capabilities list prior to
+ STARTTLS. The server MAY advertise different capabilities after
+ STARTTLS.
+
+ Example: C: a001 CAPABILITY
+ S: * CAPABILITY IMAP4rev1 STARTTLS LOGINDISABLED
+ S: a001 OK CAPABILITY completed
+ C: a002 STARTTLS
+ S: a002 OK Begin TLS negotiation now
+ <TLS negotiation, further commands are under [TLS] layer>
+ C: a003 CAPABILITY
+ S: * CAPABILITY IMAP4rev1 AUTH=PLAIN
+ S: a003 OK CAPABILITY completed
+ C: a004 LOGIN joe password
+ S: a004 OK LOGIN completed
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 27]
+
+RFC 3501 IMAPv4 March 2003
+
+
+6.2.2. AUTHENTICATE Command
+
+ Arguments: authentication mechanism name
+
+ Responses: continuation data can be requested
+
+ Result: OK - authenticate completed, now in authenticated state
+ NO - authenticate failure: unsupported authentication
+ mechanism, credentials rejected
+ BAD - command unknown or arguments invalid,
+ authentication exchange cancelled
+
+ The AUTHENTICATE command indicates a [SASL] authentication
+ mechanism to the server. If the server supports the requested
+ authentication mechanism, it performs an authentication protocol
+ exchange to authenticate and identify the client. It MAY also
+ negotiate an OPTIONAL security layer for subsequent protocol
+ interactions. If the requested authentication mechanism is not
+ supported, the server SHOULD reject the AUTHENTICATE command by
+ sending a tagged NO response.
+
+ The AUTHENTICATE command does not support the optional "initial
+ response" feature of [SASL]. Section 5.1 of [SASL] specifies how
+ to handle an authentication mechanism which uses an initial
+ response.
+
+ The service name specified by this protocol's profile of [SASL] is
+ "imap".
+
+ The authentication protocol exchange consists of a series of
+ server challenges and client responses that are specific to the
+ authentication mechanism. A server challenge consists of a
+ command continuation request response with the "+" token followed
+ by a BASE64 encoded string. The client response consists of a
+ single line consisting of a BASE64 encoded string. If the client
+ wishes to cancel an authentication exchange, it issues a line
+ consisting of a single "*". If the server receives such a
+ response, it MUST reject the AUTHENTICATE command by sending a
+ tagged BAD response.
+
+ If a security layer is negotiated through the [SASL]
+ authentication exchange, it takes effect immediately following the
+ CRLF that concludes the authentication exchange for the client,
+ and the CRLF of the tagged OK response for the server.
+
+ While client and server implementations MUST implement the
+ AUTHENTICATE command itself, it is not required to implement any
+ authentication mechanisms other than the PLAIN mechanism described
+
+
+
+Crispin Standards Track [Page 28]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ in [IMAP-TLS]. Also, an authentication mechanism is not required
+ to support any security layers.
+
+ Note: a server implementation MUST implement a
+ configuration in which it does NOT permit any plaintext
+ password mechanisms, unless either the STARTTLS command
+ has been negotiated or some other mechanism that
+ protects the session from password snooping has been
+ provided. Server sites SHOULD NOT use any configuration
+ which permits a plaintext password mechanism without
+ such a protection mechanism against password snooping.
+ Client and server implementations SHOULD implement
+ additional [SASL] mechanisms that do not use plaintext
+ passwords, such the GSSAPI mechanism described in [SASL]
+ and/or the [DIGEST-MD5] mechanism.
+
+ Servers and clients can support multiple authentication
+ mechanisms. The server SHOULD list its supported authentication
+ mechanisms in the response to the CAPABILITY command so that the
+ client knows which authentication mechanisms to use.
+
+ A server MAY include a CAPABILITY response code in the tagged OK
+ response of a successful AUTHENTICATE command in order to send
+ capabilities automatically. It is unnecessary for a client to
+ send a separate CAPABILITY command if it recognizes these
+ automatic capabilities. This should only be done if a security
+ layer was not negotiated by the AUTHENTICATE command, because the
+ tagged OK response as part of an AUTHENTICATE command is not
+ protected by encryption/integrity checking. [SASL] requires the
+ client to re-issue a CAPABILITY command in this case.
+
+ If an AUTHENTICATE command fails with a NO response, the client
+ MAY try another authentication mechanism by issuing another
+ AUTHENTICATE command. It MAY also attempt to authenticate by
+ using the LOGIN command (see section 6.2.3 for more detail). In
+ other words, the client MAY request authentication types in
+ decreasing order of preference, with the LOGIN command as a last
+ resort.
+
+ The authorization identity passed from the client to the server
+ during the authentication exchange is interpreted by the server as
+ the user name whose privileges the client is requesting.
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 29]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ Example: S: * OK IMAP4rev1 Server
+ C: A001 AUTHENTICATE GSSAPI
+ S: +
+ C: YIIB+wYJKoZIhvcSAQICAQBuggHqMIIB5qADAgEFoQMCAQ6iBw
+ MFACAAAACjggEmYYIBIjCCAR6gAwIBBaESGxB1Lndhc2hpbmd0
+ b24uZWR1oi0wK6ADAgEDoSQwIhsEaW1hcBsac2hpdmFtcy5jYW
+ Mud2FzaGluZ3Rvbi5lZHWjgdMwgdCgAwIBAaEDAgEDooHDBIHA
+ cS1GSa5b+fXnPZNmXB9SjL8Ollj2SKyb+3S0iXMljen/jNkpJX
+ AleKTz6BQPzj8duz8EtoOuNfKgweViyn/9B9bccy1uuAE2HI0y
+ C/PHXNNU9ZrBziJ8Lm0tTNc98kUpjXnHZhsMcz5Mx2GR6dGknb
+ I0iaGcRerMUsWOuBmKKKRmVMMdR9T3EZdpqsBd7jZCNMWotjhi
+ vd5zovQlFqQ2Wjc2+y46vKP/iXxWIuQJuDiisyXF0Y8+5GTpAL
+ pHDc1/pIGmMIGjoAMCAQGigZsEgZg2on5mSuxoDHEA1w9bcW9n
+ FdFxDKpdrQhVGVRDIzcCMCTzvUboqb5KjY1NJKJsfjRQiBYBdE
+ NKfzK+g5DlV8nrw81uOcP8NOQCLR5XkoMHC0Dr/80ziQzbNqhx
+ O6652Npft0LQwJvenwDI13YxpwOdMXzkWZN/XrEqOWp6GCgXTB
+ vCyLWLlWnbaUkZdEYbKHBPjd8t/1x5Yg==
+ S: + YGgGCSqGSIb3EgECAgIAb1kwV6ADAgEFoQMCAQ+iSzBJoAMC
+ AQGiQgRAtHTEuOP2BXb9sBYFR4SJlDZxmg39IxmRBOhXRKdDA0
+ uHTCOT9Bq3OsUTXUlk0CsFLoa8j+gvGDlgHuqzWHPSQg==
+ C:
+ S: + YDMGCSqGSIb3EgECAgIBAAD/////6jcyG4GE3KkTzBeBiVHe
+ ceP2CWY0SR0fAQAgAAQEBAQ=
+ C: YDMGCSqGSIb3EgECAgIBAAD/////3LQBHXTpFfZgrejpLlLImP
+ wkhbfa2QteAQAgAG1yYwE=
+ S: A001 OK GSSAPI authentication successful
+
+ Note: The line breaks within server challenges and client
+ responses are for editorial clarity and are not in real
+ authenticators.
+
+
+6.2.3. LOGIN Command
+
+ Arguments: user name
+ password
+
+ Responses: no specific responses for this command
+
+ Result: OK - login completed, now in authenticated state
+ NO - login failure: user name or password rejected
+ BAD - command unknown or arguments invalid
+
+ The LOGIN command identifies the client to the server and carries
+ the plaintext password authenticating this user.
+
+
+
+
+
+
+Crispin Standards Track [Page 30]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ A server MAY include a CAPABILITY response code in the tagged OK
+ response to a successful LOGIN command in order to send
+ capabilities automatically. It is unnecessary for a client to
+ send a separate CAPABILITY command if it recognizes these
+ automatic capabilities.
+
+ Example: C: a001 LOGIN SMITH SESAME
+ S: a001 OK LOGIN completed
+
+ Note: Use of the LOGIN command over an insecure network
+ (such as the Internet) is a security risk, because anyone
+ monitoring network traffic can obtain plaintext passwords.
+ The LOGIN command SHOULD NOT be used except as a last
+ resort, and it is recommended that client implementations
+ have a means to disable any automatic use of the LOGIN
+ command.
+
+ Unless either the STARTTLS command has been negotiated or
+ some other mechanism that protects the session from
+ password snooping has been provided, a server
+ implementation MUST implement a configuration in which it
+ advertises the LOGINDISABLED capability and does NOT permit
+ the LOGIN command. Server sites SHOULD NOT use any
+ configuration which permits the LOGIN command without such
+ a protection mechanism against password snooping. A client
+ implementation MUST NOT send a LOGIN command if the
+ LOGINDISABLED capability is advertised.
+
+6.3. Client Commands - Authenticated State
+
+ In the authenticated state, commands that manipulate mailboxes as
+ atomic entities are permitted. Of these commands, the SELECT and
+ EXAMINE commands will select a mailbox for access and enter the
+ selected state.
+
+ In addition to the universal commands (CAPABILITY, NOOP, and LOGOUT),
+ the following commands are valid in the authenticated state: SELECT,
+ EXAMINE, CREATE, DELETE, RENAME, SUBSCRIBE, UNSUBSCRIBE, LIST, LSUB,
+ STATUS, and APPEND.
+
+
+
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 31]
+
+RFC 3501 IMAPv4 March 2003
+
+
+6.3.1. SELECT Command
+
+ Arguments: mailbox name
+
+ Responses: REQUIRED untagged responses: FLAGS, EXISTS, RECENT
+ REQUIRED OK untagged responses: UNSEEN, PERMANENTFLAGS,
+ UIDNEXT, UIDVALIDITY
+
+ Result: OK - select completed, now in selected state
+ NO - select failure, now in authenticated state: no
+ such mailbox, can't access mailbox
+ BAD - command unknown or arguments invalid
+
+ The SELECT command selects a mailbox so that messages in the
+ mailbox can be accessed. Before returning an OK to the client,
+ the server MUST send the following untagged data to the client.
+ Note that earlier versions of this protocol only required the
+ FLAGS, EXISTS, and RECENT untagged data; consequently, client
+ implementations SHOULD implement default behavior for missing data
+ as discussed with the individual item.
+
+ FLAGS Defined flags in the mailbox. See the description
+ of the FLAGS response for more detail.
+
+ <n> EXISTS The number of messages in the mailbox. See the
+ description of the EXISTS response for more detail.
+
+ <n> RECENT The number of messages with the \Recent flag set.
+ See the description of the RECENT response for more
+ detail.
+
+ OK [UNSEEN <n>]
+ The message sequence number of the first unseen
+ message in the mailbox. If this is missing, the
+ client can not make any assumptions about the first
+ unseen message in the mailbox, and needs to issue a
+ SEARCH command if it wants to find it.
+
+ OK [PERMANENTFLAGS (<list of flags>)]
+ A list of message flags that the client can change
+ permanently. If this is missing, the client should
+ assume that all flags can be changed permanently.
+
+ OK [UIDNEXT <n>]
+ The next unique identifier value. Refer to section
+ 2.3.1.1 for more information. If this is missing,
+ the client can not make any assumptions about the
+ next unique identifier value.
+
+
+
+Crispin Standards Track [Page 32]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ OK [UIDVALIDITY <n>]
+ The unique identifier validity value. Refer to
+ section 2.3.1.1 for more information. If this is
+ missing, the server does not support unique
+ identifiers.
+
+ Only one mailbox can be selected at a time in a connection;
+ simultaneous access to multiple mailboxes requires multiple
+ connections. The SELECT command automatically deselects any
+ currently selected mailbox before attempting the new selection.
+ Consequently, if a mailbox is selected and a SELECT command that
+ fails is attempted, no mailbox is selected.
+
+ If the client is permitted to modify the mailbox, the server
+ SHOULD prefix the text of the tagged OK response with the
+ "[READ-WRITE]" response code.
+
+ If the client is not permitted to modify the mailbox but is
+ permitted read access, the mailbox is selected as read-only, and
+ the server MUST prefix the text of the tagged OK response to
+ SELECT with the "[READ-ONLY]" response code. Read-only access
+ through SELECT differs from the EXAMINE command in that certain
+ read-only mailboxes MAY permit the change of permanent state on a
+ per-user (as opposed to global) basis. Netnews messages marked in
+ a server-based .newsrc file are an example of such per-user
+ permanent state that can be modified with read-only mailboxes.
+
+ Example: C: A142 SELECT INBOX
+ S: * 172 EXISTS
+ S: * 1 RECENT
+ S: * OK [UNSEEN 12] Message 12 is first unseen
+ S: * OK [UIDVALIDITY 3857529045] UIDs valid
+ S: * OK [UIDNEXT 4392] Predicted next UID
+ S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
+ S: * OK [PERMANENTFLAGS (\Deleted \Seen \*)] Limited
+ S: A142 OK [READ-WRITE] SELECT completed
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 33]
+
+RFC 3501 IMAPv4 March 2003
+
+
+6.3.2. EXAMINE Command
+
+ Arguments: mailbox name
+
+ Responses: REQUIRED untagged responses: FLAGS, EXISTS, RECENT
+ REQUIRED OK untagged responses: UNSEEN, PERMANENTFLAGS,
+ UIDNEXT, UIDVALIDITY
+
+ Result: OK - examine completed, now in selected state
+ NO - examine failure, now in authenticated state: no
+ such mailbox, can't access mailbox
+ BAD - command unknown or arguments invalid
+
+ The EXAMINE command is identical to SELECT and returns the same
+ output; however, the selected mailbox is identified as read-only.
+ No changes to the permanent state of the mailbox, including
+ per-user state, are permitted; in particular, EXAMINE MUST NOT
+ cause messages to lose the \Recent flag.
+
+ The text of the tagged OK response to the EXAMINE command MUST
+ begin with the "[READ-ONLY]" response code.
+
+ Example: C: A932 EXAMINE blurdybloop
+ S: * 17 EXISTS
+ S: * 2 RECENT
+ S: * OK [UNSEEN 8] Message 8 is first unseen
+ S: * OK [UIDVALIDITY 3857529045] UIDs valid
+ S: * OK [UIDNEXT 4392] Predicted next UID
+ S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
+ S: * OK [PERMANENTFLAGS ()] No permanent flags permitted
+ S: A932 OK [READ-ONLY] EXAMINE completed
+
+
+6.3.3. CREATE Command
+
+ Arguments: mailbox name
+
+ Responses: no specific responses for this command
+
+ Result: OK - create completed
+ NO - create failure: can't create mailbox with that name
+ BAD - command unknown or arguments invalid
+
+ The CREATE command creates a mailbox with the given name. An OK
+ response is returned only if a new mailbox with that name has been
+ created. It is an error to attempt to create INBOX or a mailbox
+ with a name that refers to an extant mailbox. Any error in
+ creation will return a tagged NO response.
+
+
+
+Crispin Standards Track [Page 34]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ If the mailbox name is suffixed with the server's hierarchy
+ separator character (as returned from the server by a LIST
+ command), this is a declaration that the client intends to create
+ mailbox names under this name in the hierarchy. Server
+ implementations that do not require this declaration MUST ignore
+ the declaration. In any case, the name created is without the
+ trailing hierarchy delimiter.
+
+ If the server's hierarchy separator character appears elsewhere in
+ the name, the server SHOULD create any superior hierarchical names
+ that are needed for the CREATE command to be successfully
+ completed. In other words, an attempt to create "foo/bar/zap" on
+ a server in which "/" is the hierarchy separator character SHOULD
+ create foo/ and foo/bar/ if they do not already exist.
+
+ If a new mailbox is created with the same name as a mailbox which
+ was deleted, its unique identifiers MUST be greater than any
+ unique identifiers used in the previous incarnation of the mailbox
+ UNLESS the new incarnation has a different unique identifier
+ validity value. See the description of the UID command for more
+ detail.
+
+ Example: C: A003 CREATE owatagusiam/
+ S: A003 OK CREATE completed
+ C: A004 CREATE owatagusiam/blurdybloop
+ S: A004 OK CREATE completed
+
+ Note: The interpretation of this example depends on whether
+ "/" was returned as the hierarchy separator from LIST. If
+ "/" is the hierarchy separator, a new level of hierarchy
+ named "owatagusiam" with a member called "blurdybloop" is
+ created. Otherwise, two mailboxes at the same hierarchy
+ level are created.
+
+
+6.3.4. DELETE Command
+
+ Arguments: mailbox name
+
+ Responses: no specific responses for this command
+
+ Result: OK - delete completed
+ NO - delete failure: can't delete mailbox with that name
+ BAD - command unknown or arguments invalid
+
+
+
+
+
+
+
+Crispin Standards Track [Page 35]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ The DELETE command permanently removes the mailbox with the given
+ name. A tagged OK response is returned only if the mailbox has
+ been deleted. It is an error to attempt to delete INBOX or a
+ mailbox name that does not exist.
+
+ The DELETE command MUST NOT remove inferior hierarchical names.
+ For example, if a mailbox "foo" has an inferior "foo.bar"
+ (assuming "." is the hierarchy delimiter character), removing
+ "foo" MUST NOT remove "foo.bar". It is an error to attempt to
+ delete a name that has inferior hierarchical names and also has
+ the \Noselect mailbox name attribute (see the description of the
+ LIST response for more details).
+
+ It is permitted to delete a name that has inferior hierarchical
+ names and does not have the \Noselect mailbox name attribute. In
+ this case, all messages in that mailbox are removed, and the name
+ will acquire the \Noselect mailbox name attribute.
+
+ The value of the highest-used unique identifier of the deleted
+ mailbox MUST be preserved so that a new mailbox created with the
+ same name will not reuse the identifiers of the former
+ incarnation, UNLESS the new incarnation has a different unique
+ identifier validity value. See the description of the UID command
+ for more detail.
+
+ Examples: C: A682 LIST "" *
+ S: * LIST () "/" blurdybloop
+ S: * LIST (\Noselect) "/" foo
+ S: * LIST () "/" foo/bar
+ S: A682 OK LIST completed
+ C: A683 DELETE blurdybloop
+ S: A683 OK DELETE completed
+ C: A684 DELETE foo
+ S: A684 NO Name "foo" has inferior hierarchical names
+ C: A685 DELETE foo/bar
+ S: A685 OK DELETE Completed
+ C: A686 LIST "" *
+ S: * LIST (\Noselect) "/" foo
+ S: A686 OK LIST completed
+ C: A687 DELETE foo
+ S: A687 OK DELETE Completed
+
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 36]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ C: A82 LIST "" *
+ S: * LIST () "." blurdybloop
+ S: * LIST () "." foo
+ S: * LIST () "." foo.bar
+ S: A82 OK LIST completed
+ C: A83 DELETE blurdybloop
+ S: A83 OK DELETE completed
+ C: A84 DELETE foo
+ S: A84 OK DELETE Completed
+ C: A85 LIST "" *
+ S: * LIST () "." foo.bar
+ S: A85 OK LIST completed
+ C: A86 LIST "" %
+ S: * LIST (\Noselect) "." foo
+ S: A86 OK LIST completed
+
+
+6.3.5. RENAME Command
+
+ Arguments: existing mailbox name
+ new mailbox name
+
+ Responses: no specific responses for this command
+
+ Result: OK - rename completed
+ NO - rename failure: can't rename mailbox with that name,
+ can't rename to mailbox with that name
+ BAD - command unknown or arguments invalid
+
+ The RENAME command changes the name of a mailbox. A tagged OK
+ response is returned only if the mailbox has been renamed. It is
+ an error to attempt to rename from a mailbox name that does not
+ exist or to a mailbox name that already exists. Any error in
+ renaming will return a tagged NO response.
+
+ If the name has inferior hierarchical names, then the inferior
+ hierarchical names MUST also be renamed. For example, a rename of
+ "foo" to "zap" will rename "foo/bar" (assuming "/" is the
+ hierarchy delimiter character) to "zap/bar".
+
+ If the server's hierarchy separator character appears in the name,
+ the server SHOULD create any superior hierarchical names that are
+ needed for the RENAME command to complete successfully. In other
+ words, an attempt to rename "foo/bar/zap" to baz/rag/zowie on a
+ server in which "/" is the hierarchy separator character SHOULD
+ create baz/ and baz/rag/ if they do not already exist.
+
+
+
+
+
+Crispin Standards Track [Page 37]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ The value of the highest-used unique identifier of the old mailbox
+ name MUST be preserved so that a new mailbox created with the same
+ name will not reuse the identifiers of the former incarnation,
+ UNLESS the new incarnation has a different unique identifier
+ validity value. See the description of the UID command for more
+ detail.
+
+ Renaming INBOX is permitted, and has special behavior. It moves
+ all messages in INBOX to a new mailbox with the given name,
+ leaving INBOX empty. If the server implementation supports
+ inferior hierarchical names of INBOX, these are unaffected by a
+ rename of INBOX.
+
+ Examples: C: A682 LIST "" *
+ S: * LIST () "/" blurdybloop
+ S: * LIST (\Noselect) "/" foo
+ S: * LIST () "/" foo/bar
+ S: A682 OK LIST completed
+ C: A683 RENAME blurdybloop sarasoop
+ S: A683 OK RENAME completed
+ C: A684 RENAME foo zowie
+ S: A684 OK RENAME Completed
+ C: A685 LIST "" *
+ S: * LIST () "/" sarasoop
+ S: * LIST (\Noselect) "/" zowie
+ S: * LIST () "/" zowie/bar
+ S: A685 OK LIST completed
+
+ C: Z432 LIST "" *
+ S: * LIST () "." INBOX
+ S: * LIST () "." INBOX.bar
+ S: Z432 OK LIST completed
+ C: Z433 RENAME INBOX old-mail
+ S: Z433 OK RENAME completed
+ C: Z434 LIST "" *
+ S: * LIST () "." INBOX
+ S: * LIST () "." INBOX.bar
+ S: * LIST () "." old-mail
+ S: Z434 OK LIST completed
+
+
+
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 38]
+
+RFC 3501 IMAPv4 March 2003
+
+
+6.3.6. SUBSCRIBE Command
+
+ Arguments: mailbox
+
+ Responses: no specific responses for this command
+
+ Result: OK - subscribe completed
+ NO - subscribe failure: can't subscribe to that name
+ BAD - command unknown or arguments invalid
+
+ The SUBSCRIBE command adds the specified mailbox name to the
+ server's set of "active" or "subscribed" mailboxes as returned by
+ the LSUB command. This command returns a tagged OK response only
+ if the subscription is successful.
+
+ A server MAY validate the mailbox argument to SUBSCRIBE to verify
+ that it exists. However, it MUST NOT unilaterally remove an
+ existing mailbox name from the subscription list even if a mailbox
+ by that name no longer exists.
+
+ Note: This requirement is because a server site can
+ choose to routinely remove a mailbox with a well-known
+ name (e.g., "system-alerts") after its contents expire,
+ with the intention of recreating it when new contents
+ are appropriate.
+
+
+ Example: C: A002 SUBSCRIBE #news.comp.mail.mime
+ S: A002 OK SUBSCRIBE completed
+
+
+6.3.7. UNSUBSCRIBE Command
+
+ Arguments: mailbox name
+
+ Responses: no specific responses for this command
+
+ Result: OK - unsubscribe completed
+ NO - unsubscribe failure: can't unsubscribe that name
+ BAD - command unknown or arguments invalid
+
+ The UNSUBSCRIBE command removes the specified mailbox name from
+ the server's set of "active" or "subscribed" mailboxes as returned
+ by the LSUB command. This command returns a tagged OK response
+ only if the unsubscription is successful.
+
+ Example: C: A002 UNSUBSCRIBE #news.comp.mail.mime
+ S: A002 OK UNSUBSCRIBE completed
+
+
+
+Crispin Standards Track [Page 39]
+
+RFC 3501 IMAPv4 March 2003
+
+
+6.3.8. LIST Command
+
+ Arguments: reference name
+ mailbox name with possible wildcards
+
+ Responses: untagged responses: LIST
+
+ Result: OK - list completed
+ NO - list failure: can't list that reference or name
+ BAD - command unknown or arguments invalid
+
+ The LIST command returns a subset of names from the complete set
+ of all names available to the client. Zero or more untagged LIST
+ replies are returned, containing the name attributes, hierarchy
+ delimiter, and name; see the description of the LIST reply for
+ more detail.
+
+ The LIST command SHOULD return its data quickly, without undue
+ delay. For example, it SHOULD NOT go to excess trouble to
+ calculate the \Marked or \Unmarked status or perform other
+ processing; if each name requires 1 second of processing, then a
+ list of 1200 names would take 20 minutes!
+
+ An empty ("" string) reference name argument indicates that the
+ mailbox name is interpreted as by SELECT. The returned mailbox
+ names MUST match the supplied mailbox name pattern. A non-empty
+ reference name argument is the name of a mailbox or a level of
+ mailbox hierarchy, and indicates the context in which the mailbox
+ name is interpreted.
+
+ An empty ("" string) mailbox name argument is a special request to
+ return the hierarchy delimiter and the root name of the name given
+ in the reference. The value returned as the root MAY be the empty
+ string if the reference is non-rooted or is an empty string. In
+ all cases, a hierarchy delimiter (or NIL if there is no hierarchy)
+ is returned. This permits a client to get the hierarchy delimiter
+ (or find out that the mailbox names are flat) even when no
+ mailboxes by that name currently exist.
+
+ The reference and mailbox name arguments are interpreted into a
+ canonical form that represents an unambiguous left-to-right
+ hierarchy. The returned mailbox names will be in the interpreted
+ form.
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 40]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ Note: The interpretation of the reference argument is
+ implementation-defined. It depends upon whether the
+ server implementation has a concept of the "current
+ working directory" and leading "break out characters",
+ which override the current working directory.
+
+ For example, on a server which exports a UNIX or NT
+ filesystem, the reference argument contains the current
+ working directory, and the mailbox name argument would
+ contain the name as interpreted in the current working
+ directory.
+
+ If a server implementation has no concept of break out
+ characters, the canonical form is normally the reference
+ name appended with the mailbox name. Note that if the
+ server implements the namespace convention (section
+ 5.1.2), "#" is a break out character and must be treated
+ as such.
+
+ If the reference argument is not a level of mailbox
+ hierarchy (that is, it is a \NoInferiors name), and/or
+ the reference argument does not end with the hierarchy
+ delimiter, it is implementation-dependent how this is
+ interpreted. For example, a reference of "foo/bar" and
+ mailbox name of "rag/baz" could be interpreted as
+ "foo/bar/rag/baz", "foo/barrag/baz", or "foo/rag/baz".
+ A client SHOULD NOT use such a reference argument except
+ at the explicit request of the user. A hierarchical
+ browser MUST NOT make any assumptions about server
+ interpretation of the reference unless the reference is
+ a level of mailbox hierarchy AND ends with the hierarchy
+ delimiter.
+
+ Any part of the reference argument that is included in the
+ interpreted form SHOULD prefix the interpreted form. It SHOULD
+ also be in the same form as the reference name argument. This
+ rule permits the client to determine if the returned mailbox name
+ is in the context of the reference argument, or if something about
+ the mailbox argument overrode the reference argument. Without
+ this rule, the client would have to have knowledge of the server's
+ naming semantics including what characters are "breakouts" that
+ override a naming context.
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 41]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ For example, here are some examples of how references
+ and mailbox names might be interpreted on a UNIX-based
+ server:
+
+ Reference Mailbox Name Interpretation
+ ------------ ------------ --------------
+ ~smith/Mail/ foo.* ~smith/Mail/foo.*
+ archive/ % archive/%
+ #news. comp.mail.* #news.comp.mail.*
+ ~smith/Mail/ /usr/doc/foo /usr/doc/foo
+ archive/ ~fred/Mail/* ~fred/Mail/*
+
+ The first three examples demonstrate interpretations in
+ the context of the reference argument. Note that
+ "~smith/Mail" SHOULD NOT be transformed into something
+ like "/u2/users/smith/Mail", or it would be impossible
+ for the client to determine that the interpretation was
+ in the context of the reference.
+
+ The character "*" is a wildcard, and matches zero or more
+ characters at this position. The character "%" is similar to "*",
+ but it does not match a hierarchy delimiter. If the "%" wildcard
+ is the last character of a mailbox name argument, matching levels
+ of hierarchy are also returned. If these levels of hierarchy are
+ not also selectable mailboxes, they are returned with the
+ \Noselect mailbox name attribute (see the description of the LIST
+ response for more details).
+
+ Server implementations are permitted to "hide" otherwise
+ accessible mailboxes from the wildcard characters, by preventing
+ certain characters or names from matching a wildcard in certain
+ situations. For example, a UNIX-based server might restrict the
+ interpretation of "*" so that an initial "/" character does not
+ match.
+
+ The special name INBOX is included in the output from LIST, if
+ INBOX is supported by this server for this user and if the
+ uppercase string "INBOX" matches the interpreted reference and
+ mailbox name arguments with wildcards as described above. The
+ criteria for omitting INBOX is whether SELECT INBOX will return
+ failure; it is not relevant whether the user's real INBOX resides
+ on this or some other server.
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 42]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ Example: C: A101 LIST "" ""
+ S: * LIST (\Noselect) "/" ""
+ S: A101 OK LIST Completed
+ C: A102 LIST #news.comp.mail.misc ""
+ S: * LIST (\Noselect) "." #news.
+ S: A102 OK LIST Completed
+ C: A103 LIST /usr/staff/jones ""
+ S: * LIST (\Noselect) "/" /
+ S: A103 OK LIST Completed
+ C: A202 LIST ~/Mail/ %
+ S: * LIST (\Noselect) "/" ~/Mail/foo
+ S: * LIST () "/" ~/Mail/meetings
+ S: A202 OK LIST completed
+
+
+6.3.9. LSUB Command
+
+ Arguments: reference name
+ mailbox name with possible wildcards
+
+ Responses: untagged responses: LSUB
+
+ Result: OK - lsub completed
+ NO - lsub failure: can't list that reference or name
+ BAD - command unknown or arguments invalid
+
+ The LSUB command returns a subset of names from the set of names
+ that the user has declared as being "active" or "subscribed".
+ Zero or more untagged LSUB replies are returned. The arguments to
+ LSUB are in the same form as those for LIST.
+
+ The returned untagged LSUB response MAY contain different mailbox
+ flags from a LIST untagged response. If this should happen, the
+ flags in the untagged LIST are considered more authoritative.
+
+ A special situation occurs when using LSUB with the % wildcard.
+ Consider what happens if "foo/bar" (with a hierarchy delimiter of
+ "/") is subscribed but "foo" is not. A "%" wildcard to LSUB must
+ return foo, not foo/bar, in the LSUB response, and it MUST be
+ flagged with the \Noselect attribute.
+
+ The server MUST NOT unilaterally remove an existing mailbox name
+ from the subscription list even if a mailbox by that name no
+ longer exists.
+
+
+
+
+
+
+
+Crispin Standards Track [Page 43]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ Example: C: A002 LSUB "#news." "comp.mail.*"
+ S: * LSUB () "." #news.comp.mail.mime
+ S: * LSUB () "." #news.comp.mail.misc
+ S: A002 OK LSUB completed
+ C: A003 LSUB "#news." "comp.%"
+ S: * LSUB (\NoSelect) "." #news.comp.mail
+ S: A003 OK LSUB completed
+
+
+6.3.10. STATUS Command
+
+ Arguments: mailbox name
+ status data item names
+
+ Responses: untagged responses: STATUS
+
+ Result: OK - status completed
+ NO - status failure: no status for that name
+ BAD - command unknown or arguments invalid
+
+ The STATUS command requests the status of the indicated mailbox.
+ It does not change the currently selected mailbox, nor does it
+ affect the state of any messages in the queried mailbox (in
+ particular, STATUS MUST NOT cause messages to lose the \Recent
+ flag).
+
+ The STATUS command provides an alternative to opening a second
+ IMAP4rev1 connection and doing an EXAMINE command on a mailbox to
+ query that mailbox's status without deselecting the current
+ mailbox in the first IMAP4rev1 connection.
+
+ Unlike the LIST command, the STATUS command is not guaranteed to
+ be fast in its response. Under certain circumstances, it can be
+ quite slow. In some implementations, the server is obliged to
+ open the mailbox read-only internally to obtain certain status
+ information. Also unlike the LIST command, the STATUS command
+ does not accept wildcards.
+
+ Note: The STATUS command is intended to access the
+ status of mailboxes other than the currently selected
+ mailbox. Because the STATUS command can cause the
+ mailbox to be opened internally, and because this
+ information is available by other means on the selected
+ mailbox, the STATUS command SHOULD NOT be used on the
+ currently selected mailbox.
+
+
+
+
+
+
+Crispin Standards Track [Page 44]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ The STATUS command MUST NOT be used as a "check for new
+ messages in the selected mailbox" operation (refer to
+ sections 7, 7.3.1, and 7.3.2 for more information about
+ the proper method for new message checking).
+
+ Because the STATUS command is not guaranteed to be fast
+ in its results, clients SHOULD NOT expect to be able to
+ issue many consecutive STATUS commands and obtain
+ reasonable performance.
+
+ The currently defined status data items that can be requested are:
+
+ MESSAGES
+ The number of messages in the mailbox.
+
+ RECENT
+ The number of messages with the \Recent flag set.
+
+ UIDNEXT
+ The next unique identifier value of the mailbox. Refer to
+ section 2.3.1.1 for more information.
+
+ UIDVALIDITY
+ The unique identifier validity value of the mailbox. Refer to
+ section 2.3.1.1 for more information.
+
+ UNSEEN
+ The number of messages which do not have the \Seen flag set.
+
+
+ Example: C: A042 STATUS blurdybloop (UIDNEXT MESSAGES)
+ S: * STATUS blurdybloop (MESSAGES 231 UIDNEXT 44292)
+ S: A042 OK STATUS completed
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 45]
+
+RFC 3501 IMAPv4 March 2003
+
+
+6.3.11. APPEND Command
+
+ Arguments: mailbox name
+ OPTIONAL flag parenthesized list
+ OPTIONAL date/time string
+ message literal
+
+ Responses: no specific responses for this command
+
+ Result: OK - append completed
+ NO - append error: can't append to that mailbox, error
+ in flags or date/time or message text
+ BAD - command unknown or arguments invalid
+
+ The APPEND command appends the literal argument as a new message
+ to the end of the specified destination mailbox. This argument
+ SHOULD be in the format of an [RFC-2822] message. 8-bit
+ characters are permitted in the message. A server implementation
+ that is unable to preserve 8-bit data properly MUST be able to
+ reversibly convert 8-bit APPEND data to 7-bit using a [MIME-IMB]
+ content transfer encoding.
+
+ Note: There MAY be exceptions, e.g., draft messages, in
+ which required [RFC-2822] header lines are omitted in
+ the message literal argument to APPEND. The full
+ implications of doing so MUST be understood and
+ carefully weighed.
+
+ If a flag parenthesized list is specified, the flags SHOULD be set
+ in the resulting message; otherwise, the flag list of the
+ resulting message is set to empty by default. In either case, the
+ Recent flag is also set.
+
+ If a date-time is specified, the internal date SHOULD be set in
+ the resulting message; otherwise, the internal date of the
+ resulting message is set to the current date and time by default.
+
+ If the append is unsuccessful for any reason, the mailbox MUST be
+ restored to its state before the APPEND attempt; no partial
+ appending is permitted.
+
+ If the destination mailbox does not exist, a server MUST return an
+ error, and MUST NOT automatically create the mailbox. Unless it
+ is certain that the destination mailbox can not be created, the
+ server MUST send the response code "[TRYCREATE]" as the prefix of
+ the text of the tagged NO response. This gives a hint to the
+ client that it can attempt a CREATE command and retry the APPEND
+ if the CREATE is successful.
+
+
+
+Crispin Standards Track [Page 46]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ If the mailbox is currently selected, the normal new message
+ actions SHOULD occur. Specifically, the server SHOULD notify the
+ client immediately via an untagged EXISTS response. If the server
+ does not do so, the client MAY issue a NOOP command (or failing
+ that, a CHECK command) after one or more APPEND commands.
+
+ Example: C: A003 APPEND saved-messages (\Seen) {310}
+ S: + Ready for literal data
+ C: Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
+ C: From: Fred Foobar <foobar@Blurdybloop.COM>
+ C: Subject: afternoon meeting
+ C: To: mooch@owatagu.siam.edu
+ C: Message-Id: <B27397-0100000@Blurdybloop.COM>
+ C: MIME-Version: 1.0
+ C: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
+ C:
+ C: Hello Joe, do you think we can meet at 3:30 tomorrow?
+ C:
+ S: A003 OK APPEND completed
+
+ Note: The APPEND command is not used for message delivery,
+ because it does not provide a mechanism to transfer [SMTP]
+ envelope information.
+
+6.4. Client Commands - Selected State
+
+ In the selected state, commands that manipulate messages in a mailbox
+ are permitted.
+
+ In addition to the universal commands (CAPABILITY, NOOP, and LOGOUT),
+ and the authenticated state commands (SELECT, EXAMINE, CREATE,
+ DELETE, RENAME, SUBSCRIBE, UNSUBSCRIBE, LIST, LSUB, STATUS, and
+ APPEND), the following commands are valid in the selected state:
+ CHECK, CLOSE, EXPUNGE, SEARCH, FETCH, STORE, COPY, and UID.
+
+6.4.1. CHECK Command
+
+ Arguments: none
+
+ Responses: no specific responses for this command
+
+ Result: OK - check completed
+ BAD - command unknown or arguments invalid
+
+ The CHECK command requests a checkpoint of the currently selected
+ mailbox. A checkpoint refers to any implementation-dependent
+ housekeeping associated with the mailbox (e.g., resolving the
+ server's in-memory state of the mailbox with the state on its
+
+
+
+Crispin Standards Track [Page 47]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ disk) that is not normally executed as part of each command. A
+ checkpoint MAY take a non-instantaneous amount of real time to
+ complete. If a server implementation has no such housekeeping
+ considerations, CHECK is equivalent to NOOP.
+
+ There is no guarantee that an EXISTS untagged response will happen
+ as a result of CHECK. NOOP, not CHECK, SHOULD be used for new
+ message polling.
+
+ Example: C: FXXZ CHECK
+ S: FXXZ OK CHECK Completed
+
+
+6.4.2. CLOSE Command
+
+ Arguments: none
+
+ Responses: no specific responses for this command
+
+ Result: OK - close completed, now in authenticated state
+ BAD - command unknown or arguments invalid
+
+ The CLOSE command permanently removes all messages that have the
+ \Deleted flag set from the currently selected mailbox, and returns
+ to the authenticated state from the selected state. No untagged
+ EXPUNGE responses are sent.
+
+ No messages are removed, and no error is given, if the mailbox is
+ selected by an EXAMINE command or is otherwise selected read-only.
+
+ Even if a mailbox is selected, a SELECT, EXAMINE, or LOGOUT
+ command MAY be issued without previously issuing a CLOSE command.
+ The SELECT, EXAMINE, and LOGOUT commands implicitly close the
+ currently selected mailbox without doing an expunge. However,
+ when many messages are deleted, a CLOSE-LOGOUT or CLOSE-SELECT
+ sequence is considerably faster than an EXPUNGE-LOGOUT or
+ EXPUNGE-SELECT because no untagged EXPUNGE responses (which the
+ client would probably ignore) are sent.
+
+ Example: C: A341 CLOSE
+ S: A341 OK CLOSE completed
+
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 48]
+
+RFC 3501 IMAPv4 March 2003
+
+
+6.4.3. EXPUNGE Command
+
+ Arguments: none
+
+ Responses: untagged responses: EXPUNGE
+
+ Result: OK - expunge completed
+ NO - expunge failure: can't expunge (e.g., permission
+ denied)
+ BAD - command unknown or arguments invalid
+
+ The EXPUNGE command permanently removes all messages that have the
+ \Deleted flag set from the currently selected mailbox. Before
+ returning an OK to the client, an untagged EXPUNGE response is
+ sent for each message that is removed.
+
+ Example: C: A202 EXPUNGE
+ S: * 3 EXPUNGE
+ S: * 3 EXPUNGE
+ S: * 5 EXPUNGE
+ S: * 8 EXPUNGE
+ S: A202 OK EXPUNGE completed
+
+ Note: In this example, messages 3, 4, 7, and 11 had the
+ \Deleted flag set. See the description of the EXPUNGE
+ response for further explanation.
+
+
+6.4.4. SEARCH Command
+
+ Arguments: OPTIONAL [CHARSET] specification
+ searching criteria (one or more)
+
+ Responses: REQUIRED untagged response: SEARCH
+
+ Result: OK - search completed
+ NO - search error: can't search that [CHARSET] or
+ criteria
+ BAD - command unknown or arguments invalid
+
+ The SEARCH command searches the mailbox for messages that match
+ the given searching criteria. Searching criteria consist of one
+ or more search keys. The untagged SEARCH response from the server
+ contains a listing of message sequence numbers corresponding to
+ those messages that match the searching criteria.
+
+
+
+
+
+
+Crispin Standards Track [Page 49]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ When multiple keys are specified, the result is the intersection
+ (AND function) of all the messages that match those keys. For
+ example, the criteria DELETED FROM "SMITH" SINCE 1-Feb-1994 refers
+ to all deleted messages from Smith that were placed in the mailbox
+ since February 1, 1994. A search key can also be a parenthesized
+ list of one or more search keys (e.g., for use with the OR and NOT
+ keys).
+
+ Server implementations MAY exclude [MIME-IMB] body parts with
+ terminal content media types other than TEXT and MESSAGE from
+ consideration in SEARCH matching.
+
+ The OPTIONAL [CHARSET] specification consists of the word
+ "CHARSET" followed by a registered [CHARSET]. It indicates the
+ [CHARSET] of the strings that appear in the search criteria.
+ [MIME-IMB] content transfer encodings, and [MIME-HDRS] strings in
+ [RFC-2822]/[MIME-IMB] headers, MUST be decoded before comparing
+ text in a [CHARSET] other than US-ASCII. US-ASCII MUST be
+ supported; other [CHARSET]s MAY be supported.
+
+ If the server does not support the specified [CHARSET], it MUST
+ return a tagged NO response (not a BAD). This response SHOULD
+ contain the BADCHARSET response code, which MAY list the
+ [CHARSET]s supported by the server.
+
+ In all search keys that use strings, a message matches the key if
+ the string is a substring of the field. The matching is
+ case-insensitive.
+
+ The defined search keys are as follows. Refer to the Formal
+ Syntax section for the precise syntactic definitions of the
+ arguments.
+
+ <sequence set>
+ Messages with message sequence numbers corresponding to the
+ specified message sequence number set.
+
+ ALL
+ All messages in the mailbox; the default initial key for
+ ANDing.
+
+ ANSWERED
+ Messages with the \Answered flag set.
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 50]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ BCC <string>
+ Messages that contain the specified string in the envelope
+ structure's BCC field.
+
+ BEFORE <date>
+ Messages whose internal date (disregarding time and timezone)
+ is earlier than the specified date.
+
+ BODY <string>
+ Messages that contain the specified string in the body of the
+ message.
+
+ CC <string>
+ Messages that contain the specified string in the envelope
+ structure's CC field.
+
+ DELETED
+ Messages with the \Deleted flag set.
+
+ DRAFT
+ Messages with the \Draft flag set.
+
+ FLAGGED
+ Messages with the \Flagged flag set.
+
+ FROM <string>
+ Messages that contain the specified string in the envelope
+ structure's FROM field.
+
+ HEADER <field-name> <string>
+ Messages that have a header with the specified field-name (as
+ defined in [RFC-2822]) and that contains the specified string
+ in the text of the header (what comes after the colon). If the
+ string to search is zero-length, this matches all messages that
+ have a header line with the specified field-name regardless of
+ the contents.
+
+ KEYWORD <flag>
+ Messages with the specified keyword flag set.
+
+ LARGER <n>
+ Messages with an [RFC-2822] size larger than the specified
+ number of octets.
+
+ NEW
+ Messages that have the \Recent flag set but not the \Seen flag.
+ This is functionally equivalent to "(RECENT UNSEEN)".
+
+
+
+
+Crispin Standards Track [Page 51]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ NOT <search-key>
+ Messages that do not match the specified search key.
+
+ OLD
+ Messages that do not have the \Recent flag set. This is
+ functionally equivalent to "NOT RECENT" (as opposed to "NOT
+ NEW").
+
+ ON <date>
+ Messages whose internal date (disregarding time and timezone)
+ is within the specified date.
+
+ OR <search-key1> <search-key2>
+ Messages that match either search key.
+
+ RECENT
+ Messages that have the \Recent flag set.
+
+ SEEN
+ Messages that have the \Seen flag set.
+
+ SENTBEFORE <date>
+ Messages whose [RFC-2822] Date: header (disregarding time and
+ timezone) is earlier than the specified date.
+
+ SENTON <date>
+ Messages whose [RFC-2822] Date: header (disregarding time and
+ timezone) is within the specified date.
+
+ SENTSINCE <date>
+ Messages whose [RFC-2822] Date: header (disregarding time and
+ timezone) is within or later than the specified date.
+
+ SINCE <date>
+ Messages whose internal date (disregarding time and timezone)
+ is within or later than the specified date.
+
+ SMALLER <n>
+ Messages with an [RFC-2822] size smaller than the specified
+ number of octets.
+
+
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 52]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ SUBJECT <string>
+ Messages that contain the specified string in the envelope
+ structure's SUBJECT field.
+
+ TEXT <string>
+ Messages that contain the specified string in the header or
+ body of the message.
+
+ TO <string>
+ Messages that contain the specified string in the envelope
+ structure's TO field.
+
+ UID <sequence set>
+ Messages with unique identifiers corresponding to the specified
+ unique identifier set. Sequence set ranges are permitted.
+
+ UNANSWERED
+ Messages that do not have the \Answered flag set.
+
+ UNDELETED
+ Messages that do not have the \Deleted flag set.
+
+ UNDRAFT
+ Messages that do not have the \Draft flag set.
+
+ UNFLAGGED
+ Messages that do not have the \Flagged flag set.
+
+ UNKEYWORD <flag>
+ Messages that do not have the specified keyword flag set.
+
+ UNSEEN
+ Messages that do not have the \Seen flag set.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 53]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ Example: C: A282 SEARCH FLAGGED SINCE 1-Feb-1994 NOT FROM "Smith"
+ S: * SEARCH 2 84 882
+ S: A282 OK SEARCH completed
+ C: A283 SEARCH TEXT "string not in mailbox"
+ S: * SEARCH
+ S: A283 OK SEARCH completed
+ C: A284 SEARCH CHARSET UTF-8 TEXT {6}
+ C: XXXXXX
+ S: * SEARCH 43
+ S: A284 OK SEARCH completed
+
+ Note: Since this document is restricted to 7-bit ASCII
+ text, it is not possible to show actual UTF-8 data. The
+ "XXXXXX" is a placeholder for what would be 6 octets of
+ 8-bit data in an actual transaction.
+
+
+6.4.5. FETCH Command
+
+ Arguments: sequence set
+ message data item names or macro
+
+ Responses: untagged responses: FETCH
+
+ Result: OK - fetch completed
+ NO - fetch error: can't fetch that data
+ BAD - command unknown or arguments invalid
+
+ The FETCH command retrieves data associated with a message in the
+ mailbox. The data items to be fetched can be either a single atom
+ or a parenthesized list.
+
+ Most data items, identified in the formal syntax under the
+ msg-att-static rule, are static and MUST NOT change for any
+ particular message. Other data items, identified in the formal
+ syntax under the msg-att-dynamic rule, MAY change, either as a
+ result of a STORE command or due to external events.
+
+ For example, if a client receives an ENVELOPE for a
+ message when it already knows the envelope, it can
+ safely ignore the newly transmitted envelope.
+
+ There are three macros which specify commonly-used sets of data
+ items, and can be used instead of data items. A macro must be
+ used by itself, and not in conjunction with other macros or data
+ items.
+
+
+
+
+
+Crispin Standards Track [Page 54]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ ALL
+ Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE)
+
+ FAST
+ Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE)
+
+ FULL
+ Macro equivalent to: (FLAGS INTERNALDATE RFC822.SIZE ENVELOPE
+ BODY)
+
+ The currently defined data items that can be fetched are:
+
+ BODY
+ Non-extensible form of BODYSTRUCTURE.
+
+ BODY[<section>]<<partial>>
+ The text of a particular body section. The section
+ specification is a set of zero or more part specifiers
+ delimited by periods. A part specifier is either a part number
+ or one of the following: HEADER, HEADER.FIELDS,
+ HEADER.FIELDS.NOT, MIME, and TEXT. An empty section
+ specification refers to the entire message, including the
+ header.
+
+ Every message has at least one part number. Non-[MIME-IMB]
+ messages, and non-multipart [MIME-IMB] messages with no
+ encapsulated message, only have a part 1.
+
+ Multipart messages are assigned consecutive part numbers, as
+ they occur in the message. If a particular part is of type
+ message or multipart, its parts MUST be indicated by a period
+ followed by the part number within that nested multipart part.
+
+ A part of type MESSAGE/RFC822 also has nested part numbers,
+ referring to parts of the MESSAGE part's body.
+
+ The HEADER, HEADER.FIELDS, HEADER.FIELDS.NOT, and TEXT part
+ specifiers can be the sole part specifier or can be prefixed by
+ one or more numeric part specifiers, provided that the numeric
+ part specifier refers to a part of type MESSAGE/RFC822. The
+ MIME part specifier MUST be prefixed by one or more numeric
+ part specifiers.
+
+ The HEADER, HEADER.FIELDS, and HEADER.FIELDS.NOT part
+ specifiers refer to the [RFC-2822] header of the message or of
+ an encapsulated [MIME-IMT] MESSAGE/RFC822 message.
+ HEADER.FIELDS and HEADER.FIELDS.NOT are followed by a list of
+ field-name (as defined in [RFC-2822]) names, and return a
+
+
+
+Crispin Standards Track [Page 55]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ subset of the header. The subset returned by HEADER.FIELDS
+ contains only those header fields with a field-name that
+ matches one of the names in the list; similarly, the subset
+ returned by HEADER.FIELDS.NOT contains only the header fields
+ with a non-matching field-name. The field-matching is
+ case-insensitive but otherwise exact. Subsetting does not
+ exclude the [RFC-2822] delimiting blank line between the header
+ and the body; the blank line is included in all header fetches,
+ except in the case of a message which has no body and no blank
+ line.
+
+ The MIME part specifier refers to the [MIME-IMB] header for
+ this part.
+
+ The TEXT part specifier refers to the text body of the message,
+ omitting the [RFC-2822] header.
+
+ Here is an example of a complex message with some of its
+ part specifiers:
+
+ HEADER ([RFC-2822] header of the message)
+ TEXT ([RFC-2822] text body of the message) MULTIPART/MIXED
+ 1 TEXT/PLAIN
+ 2 APPLICATION/OCTET-STREAM
+ 3 MESSAGE/RFC822
+ 3.HEADER ([RFC-2822] header of the message)
+ 3.TEXT ([RFC-2822] text body of the message) MULTIPART/MIXED
+ 3.1 TEXT/PLAIN
+ 3.2 APPLICATION/OCTET-STREAM
+ 4 MULTIPART/MIXED
+ 4.1 IMAGE/GIF
+ 4.1.MIME ([MIME-IMB] header for the IMAGE/GIF)
+ 4.2 MESSAGE/RFC822
+ 4.2.HEADER ([RFC-2822] header of the message)
+ 4.2.TEXT ([RFC-2822] text body of the message) MULTIPART/MIXED
+ 4.2.1 TEXT/PLAIN
+ 4.2.2 MULTIPART/ALTERNATIVE
+ 4.2.2.1 TEXT/PLAIN
+ 4.2.2.2 TEXT/RICHTEXT
+
+
+ It is possible to fetch a substring of the designated text.
+ This is done by appending an open angle bracket ("<"), the
+ octet position of the first desired octet, a period, the
+ maximum number of octets desired, and a close angle bracket
+ (">") to the part specifier. If the starting octet is beyond
+ the end of the text, an empty string is returned.
+
+
+
+
+Crispin Standards Track [Page 56]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ Any partial fetch that attempts to read beyond the end of the
+ text is truncated as appropriate. A partial fetch that starts
+ at octet 0 is returned as a partial fetch, even if this
+ truncation happened.
+
+ Note: This means that BODY[]<0.2048> of a 1500-octet message
+ will return BODY[]<0> with a literal of size 1500, not
+ BODY[].
+
+ Note: A substring fetch of a HEADER.FIELDS or
+ HEADER.FIELDS.NOT part specifier is calculated after
+ subsetting the header.
+
+ The \Seen flag is implicitly set; if this causes the flags to
+ change, they SHOULD be included as part of the FETCH responses.
+
+ BODY.PEEK[<section>]<<partial>>
+ An alternate form of BODY[<section>] that does not implicitly
+ set the \Seen flag.
+
+ BODYSTRUCTURE
+ The [MIME-IMB] body structure of the message. This is computed
+ by the server by parsing the [MIME-IMB] header fields in the
+ [RFC-2822] header and [MIME-IMB] headers.
+
+ ENVELOPE
+ The envelope structure of the message. This is computed by the
+ server by parsing the [RFC-2822] header into the component
+ parts, defaulting various fields as necessary.
+
+ FLAGS
+ The flags that are set for this message.
+
+ INTERNALDATE
+ The internal date of the message.
+
+ RFC822
+ Functionally equivalent to BODY[], differing in the syntax of
+ the resulting untagged FETCH data (RFC822 is returned).
+
+ RFC822.HEADER
+ Functionally equivalent to BODY.PEEK[HEADER], differing in the
+ syntax of the resulting untagged FETCH data (RFC822.HEADER is
+ returned).
+
+ RFC822.SIZE
+ The [RFC-2822] size of the message.
+
+
+
+
+Crispin Standards Track [Page 57]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ RFC822.TEXT
+ Functionally equivalent to BODY[TEXT], differing in the syntax
+ of the resulting untagged FETCH data (RFC822.TEXT is returned).
+
+ UID
+ The unique identifier for the message.
+
+
+ Example: C: A654 FETCH 2:4 (FLAGS BODY[HEADER.FIELDS (DATE FROM)])
+ S: * 2 FETCH ....
+ S: * 3 FETCH ....
+ S: * 4 FETCH ....
+ S: A654 OK FETCH completed
+
+
+6.4.6. STORE Command
+
+ Arguments: sequence set
+ message data item name
+ value for message data item
+
+ Responses: untagged responses: FETCH
+
+ Result: OK - store completed
+ NO - store error: can't store that data
+ BAD - command unknown or arguments invalid
+
+ The STORE command alters data associated with a message in the
+ mailbox. Normally, STORE will return the updated value of the
+ data with an untagged FETCH response. A suffix of ".SILENT" in
+ the data item name prevents the untagged FETCH, and the server
+ SHOULD assume that the client has determined the updated value
+ itself or does not care about the updated value.
+
+ Note: Regardless of whether or not the ".SILENT" suffix
+ was used, the server SHOULD send an untagged FETCH
+ response if a change to a message's flags from an
+ external source is observed. The intent is that the
+ status of the flags is determinate without a race
+ condition.
+
+
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 58]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ The currently defined data items that can be stored are:
+
+ FLAGS <flag list>
+ Replace the flags for the message (other than \Recent) with the
+ argument. The new value of the flags is returned as if a FETCH
+ of those flags was done.
+
+ FLAGS.SILENT <flag list>
+ Equivalent to FLAGS, but without returning a new value.
+
+ +FLAGS <flag list>
+ Add the argument to the flags for the message. The new value
+ of the flags is returned as if a FETCH of those flags was done.
+
+ +FLAGS.SILENT <flag list>
+ Equivalent to +FLAGS, but without returning a new value.
+
+ -FLAGS <flag list>
+ Remove the argument from the flags for the message. The new
+ value of the flags is returned as if a FETCH of those flags was
+ done.
+
+ -FLAGS.SILENT <flag list>
+ Equivalent to -FLAGS, but without returning a new value.
+
+
+ Example: C: A003 STORE 2:4 +FLAGS (\Deleted)
+ S: * 2 FETCH (FLAGS (\Deleted \Seen))
+ S: * 3 FETCH (FLAGS (\Deleted))
+ S: * 4 FETCH (FLAGS (\Deleted \Flagged \Seen))
+ S: A003 OK STORE completed
+
+
+6.4.7. COPY Command
+
+ Arguments: sequence set
+ mailbox name
+
+ Responses: no specific responses for this command
+
+ Result: OK - copy completed
+ NO - copy error: can't copy those messages or to that
+ name
+ BAD - command unknown or arguments invalid
+
+
+
+
+
+
+
+Crispin Standards Track [Page 59]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ The COPY command copies the specified message(s) to the end of the
+ specified destination mailbox. The flags and internal date of the
+ message(s) SHOULD be preserved, and the Recent flag SHOULD be set,
+ in the copy.
+
+ If the destination mailbox does not exist, a server SHOULD return
+ an error. It SHOULD NOT automatically create the mailbox. Unless
+ it is certain that the destination mailbox can not be created, the
+ server MUST send the response code "[TRYCREATE]" as the prefix of
+ the text of the tagged NO response. This gives a hint to the
+ client that it can attempt a CREATE command and retry the COPY if
+ the CREATE is successful.
+
+ If the COPY command is unsuccessful for any reason, server
+ implementations MUST restore the destination mailbox to its state
+ before the COPY attempt.
+
+ Example: C: A003 COPY 2:4 MEETING
+ S: A003 OK COPY completed
+
+
+6.4.8. UID Command
+
+ Arguments: command name
+ command arguments
+
+ Responses: untagged responses: FETCH, SEARCH
+
+ Result: OK - UID command completed
+ NO - UID command error
+ BAD - command unknown or arguments invalid
+
+ The UID command has two forms. In the first form, it takes as its
+ arguments a COPY, FETCH, or STORE command with arguments
+ appropriate for the associated command. However, the numbers in
+ the sequence set argument are unique identifiers instead of
+ message sequence numbers. Sequence set ranges are permitted, but
+ there is no guarantee that unique identifiers will be contiguous.
+
+ A non-existent unique identifier is ignored without any error
+ message generated. Thus, it is possible for a UID FETCH command
+ to return an OK without any data or a UID COPY or UID STORE to
+ return an OK without performing any operations.
+
+ In the second form, the UID command takes a SEARCH command with
+ SEARCH command arguments. The interpretation of the arguments is
+ the same as with SEARCH; however, the numbers returned in a SEARCH
+ response for a UID SEARCH command are unique identifiers instead
+
+
+
+Crispin Standards Track [Page 60]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ of message sequence numbers. For example, the command UID SEARCH
+ 1:100 UID 443:557 returns the unique identifiers corresponding to
+ the intersection of two sequence sets, the message sequence number
+ range 1:100 and the UID range 443:557.
+
+ Note: in the above example, the UID range 443:557
+ appears. The same comment about a non-existent unique
+ identifier being ignored without any error message also
+ applies here. Hence, even if neither UID 443 or 557
+ exist, this range is valid and would include an existing
+ UID 495.
+
+ Also note that a UID range of 559:* always includes the
+ UID of the last message in the mailbox, even if 559 is
+ higher than any assigned UID value. This is because the
+ contents of a range are independent of the order of the
+ range endpoints. Thus, any UID range with * as one of
+ the endpoints indicates at least one message (the
+ message with the highest numbered UID), unless the
+ mailbox is empty.
+
+ The number after the "*" in an untagged FETCH response is always a
+ message sequence number, not a unique identifier, even for a UID
+ command response. However, server implementations MUST implicitly
+ include the UID message data item as part of any FETCH response
+ caused by a UID command, regardless of whether a UID was specified
+ as a message data item to the FETCH.
+
+
+ Note: The rule about including the UID message data item as part
+ of a FETCH response primarily applies to the UID FETCH and UID
+ STORE commands, including a UID FETCH command that does not
+ include UID as a message data item. Although it is unlikely that
+ the other UID commands will cause an untagged FETCH, this rule
+ applies to these commands as well.
+
+ Example: C: A999 UID FETCH 4827313:4828442 FLAGS
+ S: * 23 FETCH (FLAGS (\Seen) UID 4827313)
+ S: * 24 FETCH (FLAGS (\Seen) UID 4827943)
+ S: * 25 FETCH (FLAGS (\Seen) UID 4828442)
+ S: A999 OK UID FETCH completed
+
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 61]
+
+RFC 3501 IMAPv4 March 2003
+
+
+6.5. Client Commands - Experimental/Expansion
+
+
+6.5.1. X<atom> Command
+
+ Arguments: implementation defined
+
+ Responses: implementation defined
+
+ Result: OK - command completed
+ NO - failure
+ BAD - command unknown or arguments invalid
+
+ Any command prefixed with an X is an experimental command.
+ Commands which are not part of this specification, a standard or
+ standards-track revision of this specification, or an
+ IESG-approved experimental protocol, MUST use the X prefix.
+
+ Any added untagged responses issued by an experimental command
+ MUST also be prefixed with an X. Server implementations MUST NOT
+ send any such untagged responses, unless the client requested it
+ by issuing the associated experimental command.
+
+ Example: C: a441 CAPABILITY
+ S: * CAPABILITY IMAP4rev1 XPIG-LATIN
+ S: a441 OK CAPABILITY completed
+ C: A442 XPIG-LATIN
+ S: * XPIG-LATIN ow-nay eaking-spay ig-pay atin-lay
+ S: A442 OK XPIG-LATIN ompleted-cay
+
+7. Server Responses
+
+ Server responses are in three forms: status responses, server data,
+ and command continuation request. The information contained in a
+ server response, identified by "Contents:" in the response
+ descriptions below, is described by function, not by syntax. The
+ precise syntax of server responses is described in the Formal Syntax
+ section.
+
+ The client MUST be prepared to accept any response at all times.
+
+ Status responses can be tagged or untagged. Tagged status responses
+ indicate the completion result (OK, NO, or BAD status) of a client
+ command, and have a tag matching the command.
+
+ Some status responses, and all server data, are untagged. An
+ untagged response is indicated by the token "*" instead of a tag.
+ Untagged status responses indicate server greeting, or server status
+
+
+
+Crispin Standards Track [Page 62]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ that does not indicate the completion of a command (for example, an
+ impending system shutdown alert). For historical reasons, untagged
+ server data responses are also called "unsolicited data", although
+ strictly speaking, only unilateral server data is truly
+ "unsolicited".
+
+ Certain server data MUST be recorded by the client when it is
+ received; this is noted in the description of that data. Such data
+ conveys critical information which affects the interpretation of all
+ subsequent commands and responses (e.g., updates reflecting the
+ creation or destruction of messages).
+
+ Other server data SHOULD be recorded for later reference; if the
+ client does not need to record the data, or if recording the data has
+ no obvious purpose (e.g., a SEARCH response when no SEARCH command is
+ in progress), the data SHOULD be ignored.
+
+ An example of unilateral untagged server data occurs when the IMAP
+ connection is in the selected state. In the selected state, the
+ server checks the mailbox for new messages as part of command
+ execution. Normally, this is part of the execution of every command;
+ hence, a NOOP command suffices to check for new messages. If new
+ messages are found, the server sends untagged EXISTS and RECENT
+ responses reflecting the new size of the mailbox. Server
+ implementations that offer multiple simultaneous access to the same
+ mailbox SHOULD also send appropriate unilateral untagged FETCH and
+ EXPUNGE responses if another agent changes the state of any message
+ flags or expunges any messages.
+
+ Command continuation request responses use the token "+" instead of a
+ tag. These responses are sent by the server to indicate acceptance
+ of an incomplete client command and readiness for the remainder of
+ the command.
+
+7.1. Server Responses - Status Responses
+
+ Status responses are OK, NO, BAD, PREAUTH and BYE. OK, NO, and BAD
+ can be tagged or untagged. PREAUTH and BYE are always untagged.
+
+ Status responses MAY include an OPTIONAL "response code". A response
+ code consists of data inside square brackets in the form of an atom,
+ possibly followed by a space and arguments. The response code
+ contains additional information or status codes for client software
+ beyond the OK/NO/BAD condition, and are defined when there is a
+ specific action that a client can take based upon the additional
+ information.
+
+
+
+
+
+Crispin Standards Track [Page 63]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ The currently defined response codes are:
+
+ ALERT
+
+ The human-readable text contains a special alert that MUST be
+ presented to the user in a fashion that calls the user's
+ attention to the message.
+
+ BADCHARSET
+
+ Optionally followed by a parenthesized list of charsets. A
+ SEARCH failed because the given charset is not supported by
+ this implementation. If the optional list of charsets is
+ given, this lists the charsets that are supported by this
+ implementation.
+
+ CAPABILITY
+
+ Followed by a list of capabilities. This can appear in the
+ initial OK or PREAUTH response to transmit an initial
+ capabilities list. This makes it unnecessary for a client to
+ send a separate CAPABILITY command if it recognizes this
+ response.
+
+ PARSE
+
+ The human-readable text represents an error in parsing the
+ [RFC-2822] header or [MIME-IMB] headers of a message in the
+ mailbox.
+
+ PERMANENTFLAGS
+
+ Followed by a parenthesized list of flags, indicates which of
+ the known flags the client can change permanently. Any flags
+ that are in the FLAGS untagged response, but not the
+ PERMANENTFLAGS list, can not be set permanently. If the client
+ attempts to STORE a flag that is not in the PERMANENTFLAGS
+ list, the server will either ignore the change or store the
+ state change for the remainder of the current session only.
+ The PERMANENTFLAGS list can also include the special flag \*,
+ which indicates that it is possible to create new keywords by
+ attempting to store those flags in the mailbox.
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 64]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ READ-ONLY
+
+ The mailbox is selected read-only, or its access while selected
+ has changed from read-write to read-only.
+
+ READ-WRITE
+
+ The mailbox is selected read-write, or its access while
+ selected has changed from read-only to read-write.
+
+ TRYCREATE
+
+ An APPEND or COPY attempt is failing because the target mailbox
+ does not exist (as opposed to some other reason). This is a
+ hint to the client that the operation can succeed if the
+ mailbox is first created by the CREATE command.
+
+ UIDNEXT
+
+ Followed by a decimal number, indicates the next unique
+ identifier value. Refer to section 2.3.1.1 for more
+ information.
+
+ UIDVALIDITY
+
+ Followed by a decimal number, indicates the unique identifier
+ validity value. Refer to section 2.3.1.1 for more information.
+
+ UNSEEN
+
+ Followed by a decimal number, indicates the number of the first
+ message without the \Seen flag set.
+
+ Additional response codes defined by particular client or server
+ implementations SHOULD be prefixed with an "X" until they are
+ added to a revision of this protocol. Client implementations
+ SHOULD ignore response codes that they do not recognize.
+
+7.1.1. OK Response
+
+ Contents: OPTIONAL response code
+ human-readable text
+
+ The OK response indicates an information message from the server.
+ When tagged, it indicates successful completion of the associated
+ command. The human-readable text MAY be presented to the user as
+ an information message. The untagged form indicates an
+
+
+
+
+Crispin Standards Track [Page 65]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ information-only message; the nature of the information MAY be
+ indicated by a response code.
+
+ The untagged form is also used as one of three possible greetings
+ at connection startup. It indicates that the connection is not
+ yet authenticated and that a LOGIN command is needed.
+
+ Example: S: * OK IMAP4rev1 server ready
+ C: A001 LOGIN fred blurdybloop
+ S: * OK [ALERT] System shutdown in 10 minutes
+ S: A001 OK LOGIN Completed
+
+
+7.1.2. NO Response
+
+ Contents: OPTIONAL response code
+ human-readable text
+
+ The NO response indicates an operational error message from the
+ server. When tagged, it indicates unsuccessful completion of the
+ associated command. The untagged form indicates a warning; the
+ command can still complete successfully. The human-readable text
+ describes the condition.
+
+ Example: C: A222 COPY 1:2 owatagusiam
+ S: * NO Disk is 98% full, please delete unnecessary data
+ S: A222 OK COPY completed
+ C: A223 COPY 3:200 blurdybloop
+ S: * NO Disk is 98% full, please delete unnecessary data
+ S: * NO Disk is 99% full, please delete unnecessary data
+ S: A223 NO COPY failed: disk is full
+
+
+7.1.3. BAD Response
+
+ Contents: OPTIONAL response code
+ human-readable text
+
+ The BAD response indicates an error message from the server. When
+ tagged, it reports a protocol-level error in the client's command;
+ the tag indicates the command that caused the error. The untagged
+ form indicates a protocol-level error for which the associated
+ command can not be determined; it can also indicate an internal
+ server failure. The human-readable text describes the condition.
+
+
+
+
+
+
+
+Crispin Standards Track [Page 66]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ Example: C: ...very long command line...
+ S: * BAD Command line too long
+ C: ...empty line...
+ S: * BAD Empty command line
+ C: A443 EXPUNGE
+ S: * BAD Disk crash, attempting salvage to a new disk!
+ S: * OK Salvage successful, no data lost
+ S: A443 OK Expunge completed
+
+
+7.1.4. PREAUTH Response
+
+ Contents: OPTIONAL response code
+ human-readable text
+
+ The PREAUTH response is always untagged, and is one of three
+ possible greetings at connection startup. It indicates that the
+ connection has already been authenticated by external means; thus
+ no LOGIN command is needed.
+
+ Example: S: * PREAUTH IMAP4rev1 server logged in as Smith
+
+
+7.1.5. BYE Response
+
+ Contents: OPTIONAL response code
+ human-readable text
+
+ The BYE response is always untagged, and indicates that the server
+ is about to close the connection. The human-readable text MAY be
+ displayed to the user in a status report by the client. The BYE
+ response is sent under one of four conditions:
+
+ 1) as part of a normal logout sequence. The server will close
+ the connection after sending the tagged OK response to the
+ LOGOUT command.
+
+ 2) as a panic shutdown announcement. The server closes the
+ connection immediately.
+
+ 3) as an announcement of an inactivity autologout. The server
+ closes the connection immediately.
+
+ 4) as one of three possible greetings at connection startup,
+ indicating that the server is not willing to accept a
+ connection from this client. The server closes the
+ connection immediately.
+
+
+
+
+Crispin Standards Track [Page 67]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ The difference between a BYE that occurs as part of a normal
+ LOGOUT sequence (the first case) and a BYE that occurs because of
+ a failure (the other three cases) is that the connection closes
+ immediately in the failure case. In all cases the client SHOULD
+ continue to read response data from the server until the
+ connection is closed; this will ensure that any pending untagged
+ or completion responses are read and processed.
+
+ Example: S: * BYE Autologout; idle for too long
+
+7.2. Server Responses - Server and Mailbox Status
+
+ These responses are always untagged. This is how server and mailbox
+ status data are transmitted from the server to the client. Many of
+ these responses typically result from a command with the same name.
+
+7.2.1. CAPABILITY Response
+
+ Contents: capability listing
+
+ The CAPABILITY response occurs as a result of a CAPABILITY
+ command. The capability listing contains a space-separated
+ listing of capability names that the server supports. The
+ capability listing MUST include the atom "IMAP4rev1".
+
+ In addition, client and server implementations MUST implement the
+ STARTTLS, LOGINDISABLED, and AUTH=PLAIN (described in [IMAP-TLS])
+ capabilities. See the Security Considerations section for
+ important information.
+
+ A capability name which begins with "AUTH=" indicates that the
+ server supports that particular authentication mechanism.
+
+ The LOGINDISABLED capability indicates that the LOGIN command is
+ disabled, and that the server will respond with a tagged NO
+ response to any attempt to use the LOGIN command even if the user
+ name and password are valid. An IMAP client MUST NOT issue the
+ LOGIN command if the server advertises the LOGINDISABLED
+ capability.
+
+ Other capability names indicate that the server supports an
+ extension, revision, or amendment to the IMAP4rev1 protocol.
+ Server responses MUST conform to this document until the client
+ issues a command that uses the associated capability.
+
+ Capability names MUST either begin with "X" or be standard or
+ standards-track IMAP4rev1 extensions, revisions, or amendments
+ registered with IANA. A server MUST NOT offer unregistered or
+
+
+
+Crispin Standards Track [Page 68]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ non-standard capability names, unless such names are prefixed with
+ an "X".
+
+ Client implementations SHOULD NOT require any capability name
+ other than "IMAP4rev1", and MUST ignore any unknown capability
+ names.
+
+ A server MAY send capabilities automatically, by using the
+ CAPABILITY response code in the initial PREAUTH or OK responses,
+ and by sending an updated CAPABILITY response code in the tagged
+ OK response as part of a successful authentication. It is
+ unnecessary for a client to send a separate CAPABILITY command if
+ it recognizes these automatic capabilities.
+
+ Example: S: * CAPABILITY IMAP4rev1 STARTTLS AUTH=GSSAPI XPIG-LATIN
+
+
+7.2.2. LIST Response
+
+ Contents: name attributes
+ hierarchy delimiter
+ name
+
+ The LIST response occurs as a result of a LIST command. It
+ returns a single name that matches the LIST specification. There
+ can be multiple LIST responses for a single LIST command.
+
+ Four name attributes are defined:
+
+ \Noinferiors
+ It is not possible for any child levels of hierarchy to exist
+ under this name; no child levels exist now and none can be
+ created in the future.
+
+ \Noselect
+ It is not possible to use this name as a selectable mailbox.
+
+ \Marked
+ The mailbox has been marked "interesting" by the server; the
+ mailbox probably contains messages that have been added since
+ the last time the mailbox was selected.
+
+ \Unmarked
+ The mailbox does not contain any additional messages since the
+ last time the mailbox was selected.
+
+
+
+
+
+
+Crispin Standards Track [Page 69]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ If it is not feasible for the server to determine whether or not
+ the mailbox is "interesting", or if the name is a \Noselect name,
+ the server SHOULD NOT send either \Marked or \Unmarked.
+
+ The hierarchy delimiter is a character used to delimit levels of
+ hierarchy in a mailbox name. A client can use it to create child
+ mailboxes, and to search higher or lower levels of naming
+ hierarchy. All children of a top-level hierarchy node MUST use
+ the same separator character. A NIL hierarchy delimiter means
+ that no hierarchy exists; the name is a "flat" name.
+
+ The name represents an unambiguous left-to-right hierarchy, and
+ MUST be valid for use as a reference in LIST and LSUB commands.
+ Unless \Noselect is indicated, the name MUST also be valid as an
+ argument for commands, such as SELECT, that accept mailbox names.
+
+ Example: S: * LIST (\Noselect) "/" ~/Mail/foo
+
+
+7.2.3. LSUB Response
+
+ Contents: name attributes
+ hierarchy delimiter
+ name
+
+ The LSUB response occurs as a result of an LSUB command. It
+ returns a single name that matches the LSUB specification. There
+ can be multiple LSUB responses for a single LSUB command. The
+ data is identical in format to the LIST response.
+
+ Example: S: * LSUB () "." #news.comp.mail.misc
+
+
+7.2.4 STATUS Response
+
+ Contents: name
+ status parenthesized list
+
+ The STATUS response occurs as a result of an STATUS command. It
+ returns the mailbox name that matches the STATUS specification and
+ the requested mailbox status information.
+
+ Example: S: * STATUS blurdybloop (MESSAGES 231 UIDNEXT 44292)
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 70]
+
+RFC 3501 IMAPv4 March 2003
+
+
+7.2.5. SEARCH Response
+
+ Contents: zero or more numbers
+
+ The SEARCH response occurs as a result of a SEARCH or UID SEARCH
+ command. The number(s) refer to those messages that match the
+ search criteria. For SEARCH, these are message sequence numbers;
+ for UID SEARCH, these are unique identifiers. Each number is
+ delimited by a space.
+
+ Example: S: * SEARCH 2 3 6
+
+
+7.2.6. FLAGS Response
+
+ Contents: flag parenthesized list
+
+ The FLAGS response occurs as a result of a SELECT or EXAMINE
+ command. The flag parenthesized list identifies the flags (at a
+ minimum, the system-defined flags) that are applicable for this
+ mailbox. Flags other than the system flags can also exist,
+ depending on server implementation.
+
+ The update from the FLAGS response MUST be recorded by the client.
+
+ Example: S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
+
+
+7.3. Server Responses - Mailbox Size
+
+ These responses are always untagged. This is how changes in the size
+ of the mailbox are transmitted from the server to the client.
+ Immediately following the "*" token is a number that represents a
+ message count.
+
+7.3.1. EXISTS Response
+
+ Contents: none
+
+ The EXISTS response reports the number of messages in the mailbox.
+ This response occurs as a result of a SELECT or EXAMINE command,
+ and if the size of the mailbox changes (e.g., new messages).
+
+ The update from the EXISTS response MUST be recorded by the
+ client.
+
+ Example: S: * 23 EXISTS
+
+
+
+
+Crispin Standards Track [Page 71]
+
+RFC 3501 IMAPv4 March 2003
+
+
+7.3.2. RECENT Response
+
+ Contents: none
+
+ The RECENT response reports the number of messages with the
+ \Recent flag set. This response occurs as a result of a SELECT or
+ EXAMINE command, and if the size of the mailbox changes (e.g., new
+ messages).
+
+ Note: It is not guaranteed that the message sequence
+ numbers of recent messages will be a contiguous range of
+ the highest n messages in the mailbox (where n is the
+ value reported by the RECENT response). Examples of
+ situations in which this is not the case are: multiple
+ clients having the same mailbox open (the first session
+ to be notified will see it as recent, others will
+ probably see it as non-recent), and when the mailbox is
+ re-ordered by a non-IMAP agent.
+
+ The only reliable way to identify recent messages is to
+ look at message flags to see which have the \Recent flag
+ set, or to do a SEARCH RECENT.
+
+ The update from the RECENT response MUST be recorded by the
+ client.
+
+ Example: S: * 5 RECENT
+
+
+7.4. Server Responses - Message Status
+
+ These responses are always untagged. This is how message data are
+ transmitted from the server to the client, often as a result of a
+ command with the same name. Immediately following the "*" token is a
+ number that represents a message sequence number.
+
+7.4.1. EXPUNGE Response
+
+ Contents: none
+
+ The EXPUNGE response reports that the specified message sequence
+ number has been permanently removed from the mailbox. The message
+ sequence number for each successive message in the mailbox is
+ immediately decremented by 1, and this decrement is reflected in
+ message sequence numbers in subsequent responses (including other
+ untagged EXPUNGE responses).
+
+
+
+
+
+Crispin Standards Track [Page 72]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ The EXPUNGE response also decrements the number of messages in the
+ mailbox; it is not necessary to send an EXISTS response with the
+ new value.
+
+ As a result of the immediate decrement rule, message sequence
+ numbers that appear in a set of successive EXPUNGE responses
+ depend upon whether the messages are removed starting from lower
+ numbers to higher numbers, or from higher numbers to lower
+ numbers. For example, if the last 5 messages in a 9-message
+ mailbox are expunged, a "lower to higher" server will send five
+ untagged EXPUNGE responses for message sequence number 5, whereas
+ a "higher to lower server" will send successive untagged EXPUNGE
+ responses for message sequence numbers 9, 8, 7, 6, and 5.
+
+ An EXPUNGE response MUST NOT be sent when no command is in
+ progress, nor while responding to a FETCH, STORE, or SEARCH
+ command. This rule is necessary to prevent a loss of
+ synchronization of message sequence numbers between client and
+ server. A command is not "in progress" until the complete command
+ has been received; in particular, a command is not "in progress"
+ during the negotiation of command continuation.
+
+ Note: UID FETCH, UID STORE, and UID SEARCH are different
+ commands from FETCH, STORE, and SEARCH. An EXPUNGE
+ response MAY be sent during a UID command.
+
+ The update from the EXPUNGE response MUST be recorded by the
+ client.
+
+ Example: S: * 44 EXPUNGE
+
+
+7.4.2. FETCH Response
+
+ Contents: message data
+
+ The FETCH response returns data about a message to the client.
+ The data are pairs of data item names and their values in
+ parentheses. This response occurs as the result of a FETCH or
+ STORE command, as well as by unilateral server decision (e.g.,
+ flag updates).
+
+ The current data items are:
+
+ BODY
+ A form of BODYSTRUCTURE without extension data.
+
+
+
+
+
+Crispin Standards Track [Page 73]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ BODY[<section>]<<origin octet>>
+ A string expressing the body contents of the specified section.
+ The string SHOULD be interpreted by the client according to the
+ content transfer encoding, body type, and subtype.
+
+ If the origin octet is specified, this string is a substring of
+ the entire body contents, starting at that origin octet. This
+ means that BODY[]<0> MAY be truncated, but BODY[] is NEVER
+ truncated.
+
+ Note: The origin octet facility MUST NOT be used by a server
+ in a FETCH response unless the client specifically requested
+ it by means of a FETCH of a BODY[<section>]<<partial>> data
+ item.
+
+ 8-bit textual data is permitted if a [CHARSET] identifier is
+ part of the body parameter parenthesized list for this section.
+ Note that headers (part specifiers HEADER or MIME, or the
+ header portion of a MESSAGE/RFC822 part), MUST be 7-bit; 8-bit
+ characters are not permitted in headers. Note also that the
+ [RFC-2822] delimiting blank line between the header and the
+ body is not affected by header line subsetting; the blank line
+ is always included as part of header data, except in the case
+ of a message which has no body and no blank line.
+
+ Non-textual data such as binary data MUST be transfer encoded
+ into a textual form, such as BASE64, prior to being sent to the
+ client. To derive the original binary data, the client MUST
+ decode the transfer encoded string.
+
+ BODYSTRUCTURE
+ A parenthesized list that describes the [MIME-IMB] body
+ structure of a message. This is computed by the server by
+ parsing the [MIME-IMB] header fields, defaulting various fields
+ as necessary.
+
+ For example, a simple text message of 48 lines and 2279 octets
+ can have a body structure of: ("TEXT" "PLAIN" ("CHARSET"
+ "US-ASCII") NIL NIL "7BIT" 2279 48)
+
+ Multiple parts are indicated by parenthesis nesting. Instead
+ of a body type as the first element of the parenthesized list,
+ there is a sequence of one or more nested body structures. The
+ second element of the parenthesized list is the multipart
+ subtype (mixed, digest, parallel, alternative, etc.).
+
+
+
+
+
+
+Crispin Standards Track [Page 74]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ For example, a two part message consisting of a text and a
+ BASE64-encoded text attachment can have a body structure of:
+ (("TEXT" "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 1152
+ 23)("TEXT" "PLAIN" ("CHARSET" "US-ASCII" "NAME" "cc.diff")
+ "<960723163407.20117h@cac.washington.edu>" "Compiler diff"
+ "BASE64" 4554 73) "MIXED")
+
+ Extension data follows the multipart subtype. Extension data
+ is never returned with the BODY fetch, but can be returned with
+ a BODYSTRUCTURE fetch. Extension data, if present, MUST be in
+ the defined order. The extension data of a multipart body part
+ are in the following order:
+
+ body parameter parenthesized list
+ A parenthesized list of attribute/value pairs [e.g., ("foo"
+ "bar" "baz" "rag") where "bar" is the value of "foo", and
+ "rag" is the value of "baz"] as defined in [MIME-IMB].
+
+ body disposition
+ A parenthesized list, consisting of a disposition type
+ string, followed by a parenthesized list of disposition
+ attribute/value pairs as defined in [DISPOSITION].
+
+ body language
+ A string or parenthesized list giving the body language
+ value as defined in [LANGUAGE-TAGS].
+
+ body location
+ A string list giving the body content URI as defined in
+ [LOCATION].
+
+ Any following extension data are not yet defined in this
+ version of the protocol. Such extension data can consist of
+ zero or more NILs, strings, numbers, or potentially nested
+ parenthesized lists of such data. Client implementations that
+ do a BODYSTRUCTURE fetch MUST be prepared to accept such
+ extension data. Server implementations MUST NOT send such
+ extension data until it has been defined by a revision of this
+ protocol.
+
+ The basic fields of a non-multipart body part are in the
+ following order:
+
+ body type
+ A string giving the content media type name as defined in
+ [MIME-IMB].
+
+
+
+
+
+Crispin Standards Track [Page 75]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ body subtype
+ A string giving the content subtype name as defined in
+ [MIME-IMB].
+
+ body parameter parenthesized list
+ A parenthesized list of attribute/value pairs [e.g., ("foo"
+ "bar" "baz" "rag") where "bar" is the value of "foo" and
+ "rag" is the value of "baz"] as defined in [MIME-IMB].
+
+ body id
+ A string giving the content id as defined in [MIME-IMB].
+
+ body description
+ A string giving the content description as defined in
+ [MIME-IMB].
+
+ body encoding
+ A string giving the content transfer encoding as defined in
+ [MIME-IMB].
+
+ body size
+ A number giving the size of the body in octets. Note that
+ this size is the size in its transfer encoding and not the
+ resulting size after any decoding.
+
+ A body type of type MESSAGE and subtype RFC822 contains,
+ immediately after the basic fields, the envelope structure,
+ body structure, and size in text lines of the encapsulated
+ message.
+
+ A body type of type TEXT contains, immediately after the basic
+ fields, the size of the body in text lines. Note that this
+ size is the size in its content transfer encoding and not the
+ resulting size after any decoding.
+
+ Extension data follows the basic fields and the type-specific
+ fields listed above. Extension data is never returned with the
+ BODY fetch, but can be returned with a BODYSTRUCTURE fetch.
+ Extension data, if present, MUST be in the defined order.
+
+ The extension data of a non-multipart body part are in the
+ following order:
+
+ body MD5
+ A string giving the body MD5 value as defined in [MD5].
+
+
+
+
+
+
+Crispin Standards Track [Page 76]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ body disposition
+ A parenthesized list with the same content and function as
+ the body disposition for a multipart body part.
+
+ body language
+ A string or parenthesized list giving the body language
+ value as defined in [LANGUAGE-TAGS].
+
+ body location
+ A string list giving the body content URI as defined in
+ [LOCATION].
+
+ Any following extension data are not yet defined in this
+ version of the protocol, and would be as described above under
+ multipart extension data.
+
+ ENVELOPE
+ A parenthesized list that describes the envelope structure of a
+ message. This is computed by the server by parsing the
+ [RFC-2822] header into the component parts, defaulting various
+ fields as necessary.
+
+ The fields of the envelope structure are in the following
+ order: date, subject, from, sender, reply-to, to, cc, bcc,
+ in-reply-to, and message-id. The date, subject, in-reply-to,
+ and message-id fields are strings. The from, sender, reply-to,
+ to, cc, and bcc fields are parenthesized lists of address
+ structures.
+
+ An address structure is a parenthesized list that describes an
+ electronic mail address. The fields of an address structure
+ are in the following order: personal name, [SMTP]
+ at-domain-list (source route), mailbox name, and host name.
+
+ [RFC-2822] group syntax is indicated by a special form of
+ address structure in which the host name field is NIL. If the
+ mailbox name field is also NIL, this is an end of group marker
+ (semi-colon in RFC 822 syntax). If the mailbox name field is
+ non-NIL, this is a start of group marker, and the mailbox name
+ field holds the group name phrase.
+
+ If the Date, Subject, In-Reply-To, and Message-ID header lines
+ are absent in the [RFC-2822] header, the corresponding member
+ of the envelope is NIL; if these header lines are present but
+ empty the corresponding member of the envelope is the empty
+ string.
+
+
+
+
+
+Crispin Standards Track [Page 77]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ Note: some servers may return a NIL envelope member in the
+ "present but empty" case. Clients SHOULD treat NIL and
+ empty string as identical.
+
+ Note: [RFC-2822] requires that all messages have a valid
+ Date header. Therefore, the date member in the envelope can
+ not be NIL or the empty string.
+
+ Note: [RFC-2822] requires that the In-Reply-To and
+ Message-ID headers, if present, have non-empty content.
+ Therefore, the in-reply-to and message-id members in the
+ envelope can not be the empty string.
+
+ If the From, To, cc, and bcc header lines are absent in the
+ [RFC-2822] header, or are present but empty, the corresponding
+ member of the envelope is NIL.
+
+ If the Sender or Reply-To lines are absent in the [RFC-2822]
+ header, or are present but empty, the server sets the
+ corresponding member of the envelope to be the same value as
+ the from member (the client is not expected to know to do
+ this).
+
+ Note: [RFC-2822] requires that all messages have a valid
+ From header. Therefore, the from, sender, and reply-to
+ members in the envelope can not be NIL.
+
+ FLAGS
+ A parenthesized list of flags that are set for this message.
+
+ INTERNALDATE
+ A string representing the internal date of the message.
+
+ RFC822
+ Equivalent to BODY[].
+
+ RFC822.HEADER
+ Equivalent to BODY[HEADER]. Note that this did not result in
+ \Seen being set, because RFC822.HEADER response data occurs as
+ a result of a FETCH of RFC822.HEADER. BODY[HEADER] response
+ data occurs as a result of a FETCH of BODY[HEADER] (which sets
+ \Seen) or BODY.PEEK[HEADER] (which does not set \Seen).
+
+ RFC822.SIZE
+ A number expressing the [RFC-2822] size of the message.
+
+
+
+
+
+
+Crispin Standards Track [Page 78]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ RFC822.TEXT
+ Equivalent to BODY[TEXT].
+
+ UID
+ A number expressing the unique identifier of the message.
+
+
+ Example: S: * 23 FETCH (FLAGS (\Seen) RFC822.SIZE 44827)
+
+
+7.5. Server Responses - Command Continuation Request
+
+ The command continuation request response is indicated by a "+" token
+ instead of a tag. This form of response indicates that the server is
+ ready to accept the continuation of a command from the client. The
+ remainder of this response is a line of text.
+
+ This response is used in the AUTHENTICATE command to transmit server
+ data to the client, and request additional client data. This
+ response is also used if an argument to any command is a literal.
+
+ The client is not permitted to send the octets of the literal unless
+ the server indicates that it is expected. This permits the server to
+ process commands and reject errors on a line-by-line basis. The
+ remainder of the command, including the CRLF that terminates a
+ command, follows the octets of the literal. If there are any
+ additional command arguments, the literal octets are followed by a
+ space and those arguments.
+
+ Example: C: A001 LOGIN {11}
+ S: + Ready for additional command text
+ C: FRED FOOBAR {7}
+ S: + Ready for additional command text
+ C: fat man
+ S: A001 OK LOGIN completed
+ C: A044 BLURDYBLOOP {102856}
+ S: A044 BAD No such command as "BLURDYBLOOP"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 79]
+
+RFC 3501 IMAPv4 March 2003
+
+
+8. Sample IMAP4rev1 connection
+
+ The following is a transcript of an IMAP4rev1 connection. A long
+ line in this sample is broken for editorial clarity.
+
+S: * OK IMAP4rev1 Service Ready
+C: a001 login mrc secret
+S: a001 OK LOGIN completed
+C: a002 select inbox
+S: * 18 EXISTS
+S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
+S: * 2 RECENT
+S: * OK [UNSEEN 17] Message 17 is the first unseen message
+S: * OK [UIDVALIDITY 3857529045] UIDs valid
+S: a002 OK [READ-WRITE] SELECT completed
+C: a003 fetch 12 full
+S: * 12 FETCH (FLAGS (\Seen) INTERNALDATE "17-Jul-1996 02:44:25 -0700"
+ RFC822.SIZE 4286 ENVELOPE ("Wed, 17 Jul 1996 02:23:25 -0700 (PDT)"
+ "IMAP4rev1 WG mtg summary and minutes"
+ (("Terry Gray" NIL "gray" "cac.washington.edu"))
+ (("Terry Gray" NIL "gray" "cac.washington.edu"))
+ (("Terry Gray" NIL "gray" "cac.washington.edu"))
+ ((NIL NIL "imap" "cac.washington.edu"))
+ ((NIL NIL "minutes" "CNRI.Reston.VA.US")
+ ("John Klensin" NIL "KLENSIN" "MIT.EDU")) NIL NIL
+ "<B27397-0100000@cac.washington.edu>")
+ BODY ("TEXT" "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 3028
+ 92))
+S: a003 OK FETCH completed
+C: a004 fetch 12 body[header]
+S: * 12 FETCH (BODY[HEADER] {342}
+S: Date: Wed, 17 Jul 1996 02:23:25 -0700 (PDT)
+S: From: Terry Gray <gray@cac.washington.edu>
+S: Subject: IMAP4rev1 WG mtg summary and minutes
+S: To: imap@cac.washington.edu
+S: cc: minutes@CNRI.Reston.VA.US, John Klensin <KLENSIN@MIT.EDU>
+S: Message-Id: <B27397-0100000@cac.washington.edu>
+S: MIME-Version: 1.0
+S: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
+S:
+S: )
+S: a004 OK FETCH completed
+C: a005 store 12 +flags \deleted
+S: * 12 FETCH (FLAGS (\Seen \Deleted))
+S: a005 OK +FLAGS completed
+C: a006 logout
+S: * BYE IMAP4rev1 server terminating connection
+S: a006 OK LOGOUT completed
+
+
+
+Crispin Standards Track [Page 80]
+
+RFC 3501 IMAPv4 March 2003
+
+
+9. Formal Syntax
+
+ The following syntax specification uses the Augmented Backus-Naur
+ Form (ABNF) notation as specified in [ABNF].
+
+ In the case of alternative or optional rules in which a later rule
+ overlaps an earlier rule, the rule which is listed earlier MUST take
+ priority. For example, "\Seen" when parsed as a flag is the \Seen
+ flag name and not a flag-extension, even though "\Seen" can be parsed
+ as a flag-extension. Some, but not all, instances of this rule are
+ noted below.
+
+ Note: [ABNF] rules MUST be followed strictly; in
+ particular:
+
+ (1) Except as noted otherwise, all alphabetic characters
+ are case-insensitive. The use of upper or lower case
+ characters to define token strings is for editorial clarity
+ only. Implementations MUST accept these strings in a
+ case-insensitive fashion.
+
+ (2) In all cases, SP refers to exactly one space. It is
+ NOT permitted to substitute TAB, insert additional spaces,
+ or otherwise treat SP as being equivalent to LWSP.
+
+ (3) The ASCII NUL character, %x00, MUST NOT be used at any
+ time.
+
+address = "(" addr-name SP addr-adl SP addr-mailbox SP
+ addr-host ")"
+
+addr-adl = nstring
+ ; Holds route from [RFC-2822] route-addr if
+ ; non-NIL
+
+addr-host = nstring
+ ; NIL indicates [RFC-2822] group syntax.
+ ; Otherwise, holds [RFC-2822] domain name
+
+addr-mailbox = nstring
+ ; NIL indicates end of [RFC-2822] group; if
+ ; non-NIL and addr-host is NIL, holds
+ ; [RFC-2822] group name.
+ ; Otherwise, holds [RFC-2822] local-part
+ ; after removing [RFC-2822] quoting
+
+
+
+
+
+
+Crispin Standards Track [Page 81]
+
+RFC 3501 IMAPv4 March 2003
+
+
+addr-name = nstring
+ ; If non-NIL, holds phrase from [RFC-2822]
+ ; mailbox after removing [RFC-2822] quoting
+
+append = "APPEND" SP mailbox [SP flag-list] [SP date-time] SP
+ literal
+
+astring = 1*ASTRING-CHAR / string
+
+ASTRING-CHAR = ATOM-CHAR / resp-specials
+
+atom = 1*ATOM-CHAR
+
+ATOM-CHAR = <any CHAR except atom-specials>
+
+atom-specials = "(" / ")" / "{" / SP / CTL / list-wildcards /
+ quoted-specials / resp-specials
+
+authenticate = "AUTHENTICATE" SP auth-type *(CRLF base64)
+
+auth-type = atom
+ ; Defined by [SASL]
+
+base64 = *(4base64-char) [base64-terminal]
+
+base64-char = ALPHA / DIGIT / "+" / "/"
+ ; Case-sensitive
+
+base64-terminal = (2base64-char "==") / (3base64-char "=")
+
+body = "(" (body-type-1part / body-type-mpart) ")"
+
+body-extension = nstring / number /
+ "(" body-extension *(SP body-extension) ")"
+ ; Future expansion. Client implementations
+ ; MUST accept body-extension fields. Server
+ ; implementations MUST NOT generate
+ ; body-extension fields except as defined by
+ ; future standard or standards-track
+ ; revisions of this specification.
+
+body-ext-1part = body-fld-md5 [SP body-fld-dsp [SP body-fld-lang
+ [SP body-fld-loc *(SP body-extension)]]]
+ ; MUST NOT be returned on non-extensible
+ ; "BODY" fetch
+
+
+
+
+
+
+Crispin Standards Track [Page 82]
+
+RFC 3501 IMAPv4 March 2003
+
+
+body-ext-mpart = body-fld-param [SP body-fld-dsp [SP body-fld-lang
+ [SP body-fld-loc *(SP body-extension)]]]
+ ; MUST NOT be returned on non-extensible
+ ; "BODY" fetch
+
+body-fields = body-fld-param SP body-fld-id SP body-fld-desc SP
+ body-fld-enc SP body-fld-octets
+
+body-fld-desc = nstring
+
+body-fld-dsp = "(" string SP body-fld-param ")" / nil
+
+body-fld-enc = (DQUOTE ("7BIT" / "8BIT" / "BINARY" / "BASE64"/
+ "QUOTED-PRINTABLE") DQUOTE) / string
+
+body-fld-id = nstring
+
+body-fld-lang = nstring / "(" string *(SP string) ")"
+
+body-fld-loc = nstring
+
+body-fld-lines = number
+
+body-fld-md5 = nstring
+
+body-fld-octets = number
+
+body-fld-param = "(" string SP string *(SP string SP string) ")" / nil
+
+body-type-1part = (body-type-basic / body-type-msg / body-type-text)
+ [SP body-ext-1part]
+
+body-type-basic = media-basic SP body-fields
+ ; MESSAGE subtype MUST NOT be "RFC822"
+
+body-type-mpart = 1*body SP media-subtype
+ [SP body-ext-mpart]
+
+body-type-msg = media-message SP body-fields SP envelope
+ SP body SP body-fld-lines
+
+body-type-text = media-text SP body-fields SP body-fld-lines
+
+capability = ("AUTH=" auth-type) / atom
+ ; New capabilities MUST begin with "X" or be
+ ; registered with IANA as standard or
+ ; standards-track
+
+
+
+
+Crispin Standards Track [Page 83]
+
+RFC 3501 IMAPv4 March 2003
+
+
+capability-data = "CAPABILITY" *(SP capability) SP "IMAP4rev1"
+ *(SP capability)
+ ; Servers MUST implement the STARTTLS, AUTH=PLAIN,
+ ; and LOGINDISABLED capabilities
+ ; Servers which offer RFC 1730 compatibility MUST
+ ; list "IMAP4" as the first capability.
+
+CHAR8 = %x01-ff
+ ; any OCTET except NUL, %x00
+
+command = tag SP (command-any / command-auth / command-nonauth /
+ command-select) CRLF
+ ; Modal based on state
+
+command-any = "CAPABILITY" / "LOGOUT" / "NOOP" / x-command
+ ; Valid in all states
+
+command-auth = append / create / delete / examine / list / lsub /
+ rename / select / status / subscribe / unsubscribe
+ ; Valid only in Authenticated or Selected state
+
+command-nonauth = login / authenticate / "STARTTLS"
+ ; Valid only when in Not Authenticated state
+
+command-select = "CHECK" / "CLOSE" / "EXPUNGE" / copy / fetch / store /
+ uid / search
+ ; Valid only when in Selected state
+
+continue-req = "+" SP (resp-text / base64) CRLF
+
+copy = "COPY" SP sequence-set SP mailbox
+
+create = "CREATE" SP mailbox
+ ; Use of INBOX gives a NO error
+
+date = date-text / DQUOTE date-text DQUOTE
+
+date-day = 1*2DIGIT
+ ; Day of month
+
+date-day-fixed = (SP DIGIT) / 2DIGIT
+ ; Fixed-format version of date-day
+
+date-month = "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" /
+ "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec"
+
+date-text = date-day "-" date-month "-" date-year
+
+
+
+
+Crispin Standards Track [Page 84]
+
+RFC 3501 IMAPv4 March 2003
+
+
+date-year = 4DIGIT
+
+date-time = DQUOTE date-day-fixed "-" date-month "-" date-year
+ SP time SP zone DQUOTE
+
+delete = "DELETE" SP mailbox
+ ; Use of INBOX gives a NO error
+
+digit-nz = %x31-39
+ ; 1-9
+
+envelope = "(" env-date SP env-subject SP env-from SP
+ env-sender SP env-reply-to SP env-to SP env-cc SP
+ env-bcc SP env-in-reply-to SP env-message-id ")"
+
+env-bcc = "(" 1*address ")" / nil
+
+env-cc = "(" 1*address ")" / nil
+
+env-date = nstring
+
+env-from = "(" 1*address ")" / nil
+
+env-in-reply-to = nstring
+
+env-message-id = nstring
+
+env-reply-to = "(" 1*address ")" / nil
+
+env-sender = "(" 1*address ")" / nil
+
+env-subject = nstring
+
+env-to = "(" 1*address ")" / nil
+
+examine = "EXAMINE" SP mailbox
+
+fetch = "FETCH" SP sequence-set SP ("ALL" / "FULL" / "FAST" /
+ fetch-att / "(" fetch-att *(SP fetch-att) ")")
+
+fetch-att = "ENVELOPE" / "FLAGS" / "INTERNALDATE" /
+ "RFC822" [".HEADER" / ".SIZE" / ".TEXT"] /
+ "BODY" ["STRUCTURE"] / "UID" /
+ "BODY" section ["<" number "." nz-number ">"] /
+ "BODY.PEEK" section ["<" number "." nz-number ">"]
+
+
+
+
+
+
+Crispin Standards Track [Page 85]
+
+RFC 3501 IMAPv4 March 2003
+
+
+flag = "\Answered" / "\Flagged" / "\Deleted" /
+ "\Seen" / "\Draft" / flag-keyword / flag-extension
+ ; Does not include "\Recent"
+
+flag-extension = "\" atom
+ ; Future expansion. Client implementations
+ ; MUST accept flag-extension flags. Server
+ ; implementations MUST NOT generate
+ ; flag-extension flags except as defined by
+ ; future standard or standards-track
+ ; revisions of this specification.
+
+flag-fetch = flag / "\Recent"
+
+flag-keyword = atom
+
+flag-list = "(" [flag *(SP flag)] ")"
+
+flag-perm = flag / "\*"
+
+greeting = "*" SP (resp-cond-auth / resp-cond-bye) CRLF
+
+header-fld-name = astring
+
+header-list = "(" header-fld-name *(SP header-fld-name) ")"
+
+list = "LIST" SP mailbox SP list-mailbox
+
+list-mailbox = 1*list-char / string
+
+list-char = ATOM-CHAR / list-wildcards / resp-specials
+
+list-wildcards = "%" / "*"
+
+literal = "{" number "}" CRLF *CHAR8
+ ; Number represents the number of CHAR8s
+
+login = "LOGIN" SP userid SP password
+
+lsub = "LSUB" SP mailbox SP list-mailbox
+
+
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 86]
+
+RFC 3501 IMAPv4 March 2003
+
+
+mailbox = "INBOX" / astring
+ ; INBOX is case-insensitive. All case variants of
+ ; INBOX (e.g., "iNbOx") MUST be interpreted as INBOX
+ ; not as an astring. An astring which consists of
+ ; the case-insensitive sequence "I" "N" "B" "O" "X"
+ ; is considered to be INBOX and not an astring.
+ ; Refer to section 5.1 for further
+ ; semantic details of mailbox names.
+
+mailbox-data = "FLAGS" SP flag-list / "LIST" SP mailbox-list /
+ "LSUB" SP mailbox-list / "SEARCH" *(SP nz-number) /
+ "STATUS" SP mailbox SP "(" [status-att-list] ")" /
+ number SP "EXISTS" / number SP "RECENT"
+
+mailbox-list = "(" [mbx-list-flags] ")" SP
+ (DQUOTE QUOTED-CHAR DQUOTE / nil) SP mailbox
+
+mbx-list-flags = *(mbx-list-oflag SP) mbx-list-sflag
+ *(SP mbx-list-oflag) /
+ mbx-list-oflag *(SP mbx-list-oflag)
+
+mbx-list-oflag = "\Noinferiors" / flag-extension
+ ; Other flags; multiple possible per LIST response
+
+mbx-list-sflag = "\Noselect" / "\Marked" / "\Unmarked"
+ ; Selectability flags; only one per LIST response
+
+media-basic = ((DQUOTE ("APPLICATION" / "AUDIO" / "IMAGE" /
+ "MESSAGE" / "VIDEO") DQUOTE) / string) SP
+ media-subtype
+ ; Defined in [MIME-IMT]
+
+media-message = DQUOTE "MESSAGE" DQUOTE SP DQUOTE "RFC822" DQUOTE
+ ; Defined in [MIME-IMT]
+
+media-subtype = string
+ ; Defined in [MIME-IMT]
+
+media-text = DQUOTE "TEXT" DQUOTE SP media-subtype
+ ; Defined in [MIME-IMT]
+
+message-data = nz-number SP ("EXPUNGE" / ("FETCH" SP msg-att))
+
+msg-att = "(" (msg-att-dynamic / msg-att-static)
+ *(SP (msg-att-dynamic / msg-att-static)) ")"
+
+msg-att-dynamic = "FLAGS" SP "(" [flag-fetch *(SP flag-fetch)] ")"
+ ; MAY change for a message
+
+
+
+Crispin Standards Track [Page 87]
+
+RFC 3501 IMAPv4 March 2003
+
+
+msg-att-static = "ENVELOPE" SP envelope / "INTERNALDATE" SP date-time /
+ "RFC822" [".HEADER" / ".TEXT"] SP nstring /
+ "RFC822.SIZE" SP number /
+ "BODY" ["STRUCTURE"] SP body /
+ "BODY" section ["<" number ">"] SP nstring /
+ "UID" SP uniqueid
+ ; MUST NOT change for a message
+
+nil = "NIL"
+
+nstring = string / nil
+
+number = 1*DIGIT
+ ; Unsigned 32-bit integer
+ ; (0 <= n < 4,294,967,296)
+
+nz-number = digit-nz *DIGIT
+ ; Non-zero unsigned 32-bit integer
+ ; (0 < n < 4,294,967,296)
+
+password = astring
+
+quoted = DQUOTE *QUOTED-CHAR DQUOTE
+
+QUOTED-CHAR = <any TEXT-CHAR except quoted-specials> /
+ "\" quoted-specials
+
+quoted-specials = DQUOTE / "\"
+
+rename = "RENAME" SP mailbox SP mailbox
+ ; Use of INBOX as a destination gives a NO error
+
+response = *(continue-req / response-data) response-done
+
+response-data = "*" SP (resp-cond-state / resp-cond-bye /
+ mailbox-data / message-data / capability-data) CRLF
+
+response-done = response-tagged / response-fatal
+
+response-fatal = "*" SP resp-cond-bye CRLF
+ ; Server closes connection immediately
+
+response-tagged = tag SP resp-cond-state CRLF
+
+resp-cond-auth = ("OK" / "PREAUTH") SP resp-text
+ ; Authentication condition
+
+
+
+
+
+Crispin Standards Track [Page 88]
+
+RFC 3501 IMAPv4 March 2003
+
+
+resp-cond-bye = "BYE" SP resp-text
+
+resp-cond-state = ("OK" / "NO" / "BAD") SP resp-text
+ ; Status condition
+
+resp-specials = "]"
+
+resp-text = ["[" resp-text-code "]" SP] text
+
+resp-text-code = "ALERT" /
+ "BADCHARSET" [SP "(" astring *(SP astring) ")" ] /
+ capability-data / "PARSE" /
+ "PERMANENTFLAGS" SP "("
+ [flag-perm *(SP flag-perm)] ")" /
+ "READ-ONLY" / "READ-WRITE" / "TRYCREATE" /
+ "UIDNEXT" SP nz-number / "UIDVALIDITY" SP nz-number /
+ "UNSEEN" SP nz-number /
+ atom [SP 1*<any TEXT-CHAR except "]">]
+
+search = "SEARCH" [SP "CHARSET" SP astring] 1*(SP search-key)
+ ; CHARSET argument to MUST be registered with IANA
+
+search-key = "ALL" / "ANSWERED" / "BCC" SP astring /
+ "BEFORE" SP date / "BODY" SP astring /
+ "CC" SP astring / "DELETED" / "FLAGGED" /
+ "FROM" SP astring / "KEYWORD" SP flag-keyword /
+ "NEW" / "OLD" / "ON" SP date / "RECENT" / "SEEN" /
+ "SINCE" SP date / "SUBJECT" SP astring /
+ "TEXT" SP astring / "TO" SP astring /
+ "UNANSWERED" / "UNDELETED" / "UNFLAGGED" /
+ "UNKEYWORD" SP flag-keyword / "UNSEEN" /
+ ; Above this line were in [IMAP2]
+ "DRAFT" / "HEADER" SP header-fld-name SP astring /
+ "LARGER" SP number / "NOT" SP search-key /
+ "OR" SP search-key SP search-key /
+ "SENTBEFORE" SP date / "SENTON" SP date /
+ "SENTSINCE" SP date / "SMALLER" SP number /
+ "UID" SP sequence-set / "UNDRAFT" / sequence-set /
+ "(" search-key *(SP search-key) ")"
+
+section = "[" [section-spec] "]"
+
+section-msgtext = "HEADER" / "HEADER.FIELDS" [".NOT"] SP header-list /
+ "TEXT"
+ ; top-level or MESSAGE/RFC822 part
+
+section-part = nz-number *("." nz-number)
+ ; body part nesting
+
+
+
+Crispin Standards Track [Page 89]
+
+RFC 3501 IMAPv4 March 2003
+
+
+section-spec = section-msgtext / (section-part ["." section-text])
+
+section-text = section-msgtext / "MIME"
+ ; text other than actual body part (headers, etc.)
+
+select = "SELECT" SP mailbox
+
+seq-number = nz-number / "*"
+ ; message sequence number (COPY, FETCH, STORE
+ ; commands) or unique identifier (UID COPY,
+ ; UID FETCH, UID STORE commands).
+ ; * represents the largest number in use. In
+ ; the case of message sequence numbers, it is
+ ; the number of messages in a non-empty mailbox.
+ ; In the case of unique identifiers, it is the
+ ; unique identifier of the last message in the
+ ; mailbox or, if the mailbox is empty, the
+ ; mailbox's current UIDNEXT value.
+ ; The server should respond with a tagged BAD
+ ; response to a command that uses a message
+ ; sequence number greater than the number of
+ ; messages in the selected mailbox. This
+ ; includes "*" if the selected mailbox is empty.
+
+seq-range = seq-number ":" seq-number
+ ; two seq-number values and all values between
+ ; these two regardless of order.
+ ; Example: 2:4 and 4:2 are equivalent and indicate
+ ; values 2, 3, and 4.
+ ; Example: a unique identifer sequence range of
+ ; 3291:* includes the UID of the last message in
+ ; the mailbox, even if that value is less than 3291.
+
+sequence-set = (seq-number / seq-range) *("," sequence-set)
+ ; set of seq-number values, regardless of order.
+ ; Servers MAY coalesce overlaps and/or execute the
+ ; sequence in any order.
+ ; Example: a message sequence number set of
+ ; 2,4:7,9,12:* for a mailbox with 15 messages is
+ ; equivalent to 2,4,5,6,7,9,12,13,14,15
+ ; Example: a message sequence number set of *:4,5:7
+ ; for a mailbox with 10 messages is equivalent to
+ ; 10,9,8,7,6,5,4,5,6,7 and MAY be reordered and
+ ; overlap coalesced to be 4,5,6,7,8,9,10.
+
+status = "STATUS" SP mailbox SP
+ "(" status-att *(SP status-att) ")"
+
+
+
+
+Crispin Standards Track [Page 90]
+
+RFC 3501 IMAPv4 March 2003
+
+
+status-att = "MESSAGES" / "RECENT" / "UIDNEXT" / "UIDVALIDITY" /
+ "UNSEEN"
+
+status-att-list = status-att SP number *(SP status-att SP number)
+
+store = "STORE" SP sequence-set SP store-att-flags
+
+store-att-flags = (["+" / "-"] "FLAGS" [".SILENT"]) SP
+ (flag-list / (flag *(SP flag)))
+
+string = quoted / literal
+
+subscribe = "SUBSCRIBE" SP mailbox
+
+tag = 1*<any ASTRING-CHAR except "+">
+
+text = 1*TEXT-CHAR
+
+TEXT-CHAR = <any CHAR except CR and LF>
+
+time = 2DIGIT ":" 2DIGIT ":" 2DIGIT
+ ; Hours minutes seconds
+
+uid = "UID" SP (copy / fetch / search / store)
+ ; Unique identifiers used instead of message
+ ; sequence numbers
+
+uniqueid = nz-number
+ ; Strictly ascending
+
+unsubscribe = "UNSUBSCRIBE" SP mailbox
+
+userid = astring
+
+x-command = "X" atom <experimental command arguments>
+
+zone = ("+" / "-") 4DIGIT
+ ; Signed four-digit value of hhmm representing
+ ; hours and minutes east of Greenwich (that is,
+ ; the amount that the given time differs from
+ ; Universal Time). Subtracting the timezone
+ ; from the given time will give the UT form.
+ ; The Universal Time zone is "+0000".
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 91]
+
+RFC 3501 IMAPv4 March 2003
+
+
+10. Author's Note
+
+ This document is a revision or rewrite of earlier documents, and
+ supercedes the protocol specification in those documents: RFC 2060,
+ RFC 1730, unpublished IMAP2bis.TXT document, RFC 1176, and RFC 1064.
+
+11. Security Considerations
+
+ IMAP4rev1 protocol transactions, including electronic mail data, are
+ sent in the clear over the network unless protection from snooping is
+ negotiated. This can be accomplished either by the use of STARTTLS,
+ negotiated privacy protection in the AUTHENTICATE command, or some
+ other protection mechanism.
+
+11.1. STARTTLS Security Considerations
+
+ The specification of the STARTTLS command and LOGINDISABLED
+ capability in this document replaces that in [IMAP-TLS]. [IMAP-TLS]
+ remains normative for the PLAIN [SASL] authenticator.
+
+ IMAP client and server implementations MUST implement the
+ TLS_RSA_WITH_RC4_128_MD5 [TLS] cipher suite, and SHOULD implement the
+ TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA [TLS] cipher suite. This is
+ important as it assures that any two compliant implementations can be
+ configured to interoperate. All other cipher suites are OPTIONAL.
+ Note that this is a change from section 2.1 of [IMAP-TLS].
+
+ During the [TLS] negotiation, the client MUST check its understanding
+ of the server hostname against the server's identity as presented in
+ the server Certificate message, in order to prevent man-in-the-middle
+ attacks. If the match fails, the client SHOULD either ask for
+ explicit user confirmation, or terminate the connection and indicate
+ that the server's identity is suspect. Matching is performed
+ according to these rules:
+
+ The client MUST use the server hostname it used to open the
+ connection as the value to compare against the server name
+ as expressed in the server certificate. The client MUST
+ NOT use any form of the server hostname derived from an
+ insecure remote source (e.g., insecure DNS lookup). CNAME
+ canonicalization is not done.
+
+ If a subjectAltName extension of type dNSName is present in
+ the certificate, it SHOULD be used as the source of the
+ server's identity.
+
+ Matching is case-insensitive.
+
+
+
+
+Crispin Standards Track [Page 92]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ A "*" wildcard character MAY be used as the left-most name
+ component in the certificate. For example, *.example.com
+ would match a.example.com, foo.example.com, etc. but would
+ not match example.com.
+
+ If the certificate contains multiple names (e.g., more than
+ one dNSName field), then a match with any one of the fields
+ is considered acceptable.
+
+ Both the client and server MUST check the result of the STARTTLS
+ command and subsequent [TLS] negotiation to see whether acceptable
+ authentication or privacy was achieved.
+
+11.2. Other Security Considerations
+
+ A server error message for an AUTHENTICATE command which fails due to
+ invalid credentials SHOULD NOT detail why the credentials are
+ invalid.
+
+ Use of the LOGIN command sends passwords in the clear. This can be
+ avoided by using the AUTHENTICATE command with a [SASL] mechanism
+ that does not use plaintext passwords, by first negotiating
+ encryption via STARTTLS or some other protection mechanism.
+
+ A server implementation MUST implement a configuration that, at the
+ time of authentication, requires:
+ (1) The STARTTLS command has been negotiated.
+ OR
+ (2) Some other mechanism that protects the session from password
+ snooping has been provided.
+ OR
+ (3) The following measures are in place:
+ (a) The LOGINDISABLED capability is advertised, and [SASL]
+ mechanisms (such as PLAIN) using plaintext passwords are NOT
+ advertised in the CAPABILITY list.
+ AND
+ (b) The LOGIN command returns an error even if the password is
+ correct.
+ AND
+ (c) The AUTHENTICATE command returns an error with all [SASL]
+ mechanisms that use plaintext passwords, even if the password
+ is correct.
+
+ A server error message for a failing LOGIN command SHOULD NOT specify
+ that the user name, as opposed to the password, is invalid.
+
+ A server SHOULD have mechanisms in place to limit or delay failed
+ AUTHENTICATE/LOGIN attempts.
+
+
+
+Crispin Standards Track [Page 93]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ Additional security considerations are discussed in the section
+ discussing the AUTHENTICATE and LOGIN commands.
+
+12. IANA Considerations
+
+ IMAP4 capabilities are registered by publishing a standards track or
+ IESG approved experimental RFC. The registry is currently located
+ at:
+
+ http://www.iana.org/assignments/imap4-capabilities
+
+ As this specification revises the STARTTLS and LOGINDISABLED
+ extensions previously defined in [IMAP-TLS], the registry will be
+ updated accordingly.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 94]
+
+RFC 3501 IMAPv4 March 2003
+
+
+Appendices
+
+A. Normative References
+
+ The following documents contain definitions or specifications that
+ are necessary to understand this document properly:
+ [ABNF] Crocker, D. and P. Overell, "Augmented BNF for
+ Syntax Specifications: ABNF", RFC 2234,
+ November 1997.
+
+ [ANONYMOUS] Newman, C., "Anonymous SASL Mechanism", RFC
+ 2245, November 1997.
+
+ [CHARSET] Freed, N. and J. Postel, "IANA Character Set
+ Registration Procedures", RFC 2978, October
+ 2000.
+
+ [DIGEST-MD5] Leach, P. and C. Newman, "Using Digest
+ Authentication as a SASL Mechanism", RFC 2831,
+ May 2000.
+
+ [DISPOSITION] Troost, R., Dorner, S. and K. Moore,
+ "Communicating Presentation Information in
+ Internet Messages: The Content-Disposition
+ Header", RFC 2183, August 1997.
+
+ [IMAP-TLS] Newman, C., "Using TLS with IMAP, POP3 and
+ ACAP", RFC 2595, June 1999.
+
+ [KEYWORDS] Bradner, S., "Key words for use in RFCs to
+ Indicate Requirement Levels", BCP 14, RFC 2119,
+ March 1997.
+
+ [LANGUAGE-TAGS] Alvestrand, H., "Tags for the Identification of
+ Languages", BCP 47, RFC 3066, January 2001.
+
+ [LOCATION] Palme, J., Hopmann, A. and N. Shelness, "MIME
+ Encapsulation of Aggregate Documents, such as
+ HTML (MHTML)", RFC 2557, March 1999.
+
+ [MD5] Myers, J. and M. Rose, "The Content-MD5 Header
+ Field", RFC 1864, October 1995.
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 95]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ [MIME-HDRS] Moore, K., "MIME (Multipurpose Internet Mail
+ Extensions) Part Three: Message Header
+ Extensions for Non-ASCII Text", RFC 2047,
+ November 1996.
+
+ [MIME-IMB] Freed, N. and N. Borenstein, "MIME
+ (Multipurpose Internet Mail Extensions) Part
+ One: Format of Internet Message Bodies", RFC
+ 2045, November 1996.
+
+ [MIME-IMT] Freed, N. and N. Borenstein, "MIME
+ (Multipurpose Internet Mail Extensions) Part
+ Two: Media Types", RFC 2046, November 1996.
+
+ [RFC-2822] Resnick, P., "Internet Message Format", RFC
+ 2822, April 2001.
+
+ [SASL] Myers, J., "Simple Authentication and Security
+ Layer (SASL)", RFC 2222, October 1997.
+
+ [TLS] Dierks, T. and C. Allen, "The TLS Protocol
+ Version 1.0", RFC 2246, January 1999.
+
+ [UTF-7] Goldsmith, D. and M. Davis, "UTF-7: A Mail-Safe
+ Transformation Format of Unicode", RFC 2152,
+ May 1997.
+
+ The following documents describe quality-of-implementation issues
+ that should be carefully considered when implementing this protocol:
+
+ [IMAP-IMPLEMENTATION] Leiba, B., "IMAP Implementation
+ Recommendations", RFC 2683, September 1999.
+
+ [IMAP-MULTIACCESS] Gahrns, M., "IMAP4 Multi-Accessed Mailbox
+ Practice", RFC 2180, July 1997.
+
+A.1 Informative References
+
+ The following documents describe related protocols:
+
+ [IMAP-DISC] Austein, R., "Synchronization Operations for
+ Disconnected IMAP4 Clients", Work in Progress.
+
+ [IMAP-MODEL] Crispin, M., "Distributed Electronic Mail
+ Models in IMAP4", RFC 1733, December 1994.
+
+
+
+
+
+
+Crispin Standards Track [Page 96]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ [ACAP] Newman, C. and J. Myers, "ACAP -- Application
+ Configuration Access Protocol", RFC 2244,
+ November 1997.
+
+ [SMTP] Klensin, J., "Simple Mail Transfer Protocol",
+ STD 10, RFC 2821, April 2001.
+
+ The following documents are historical or describe historical aspects
+ of this protocol:
+
+ [IMAP-COMPAT] Crispin, M., "IMAP4 Compatibility with
+ IMAP2bis", RFC 2061, December 1996.
+
+ [IMAP-HISTORICAL] Crispin, M., "IMAP4 Compatibility with IMAP2
+ and IMAP2bis", RFC 1732, December 1994.
+
+ [IMAP-OBSOLETE] Crispin, M., "Internet Message Access Protocol
+ - Obsolete Syntax", RFC 2062, December 1996.
+
+ [IMAP2] Crispin, M., "Interactive Mail Access Protocol
+ - Version 2", RFC 1176, August 1990.
+
+ [RFC-822] Crocker, D., "Standard for the Format of ARPA
+ Internet Text Messages", STD 11, RFC 822,
+ August 1982.
+
+ [RFC-821] Postel, J., "Simple Mail Transfer Protocol",
+ STD 10, RFC 821, August 1982.
+
+B. Changes from RFC 2060
+
+ 1) Clarify description of unique identifiers and their semantics.
+
+ 2) Fix the SELECT description to clarify that UIDVALIDITY is required
+ in the SELECT and EXAMINE responses.
+
+ 3) Added an example of a failing search.
+
+ 4) Correct store-att-flags: "#flag" should be "1#flag".
+
+ 5) Made search and section rules clearer.
+
+ 6) Correct the STORE example.
+
+ 7) Correct "BASE645" misspelling.
+
+ 8) Remove extraneous close parenthesis in example of two-part message
+ with text and BASE64 attachment.
+
+
+
+Crispin Standards Track [Page 97]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ 9) Remove obsolete "MAILBOX" response from mailbox-data.
+
+ 10) A spurious "<" in the rule for mailbox-data was removed.
+
+ 11) Add CRLF to continue-req.
+
+ 12) Specifically exclude "]" from the atom in resp-text-code.
+
+ 13) Clarify that clients and servers should adhere strictly to the
+ protocol syntax.
+
+ 14) Emphasize in 5.2 that EXISTS can not be used to shrink a mailbox.
+
+ 15) Add NEWNAME to resp-text-code.
+
+ 16) Clarify that the empty string, not NIL, is used as arguments to
+ LIST.
+
+ 17) Clarify that NIL can be returned as a hierarchy delimiter for the
+ empty string mailbox name argument if the mailbox namespace is flat.
+
+ 18) Clarify that addr-mailbox and addr-name have RFC-2822 quoting
+ removed.
+
+ 19) Update UTF-7 reference.
+
+ 20) Fix example in 6.3.11.
+
+ 21) Clarify that non-existent UIDs are ignored.
+
+ 22) Update DISPOSITION reference.
+
+ 23) Expand state diagram.
+
+ 24) Clarify that partial fetch responses are only returned in
+ response to a partial fetch command.
+
+ 25) Add UIDNEXT response code. Correct UIDVALIDITY definition
+ reference.
+
+ 26) Further clarification of "can" vs. "MAY".
+
+ 27) Reference RFC-2119.
+
+ 28) Clarify that superfluous shifts are not permitted in modified
+ UTF-7.
+
+ 29) Clarify that there are no implicit shifts in modified UTF-7.
+
+
+
+Crispin Standards Track [Page 98]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ 30) Clarify that "INBOX" in a mailbox name is always INBOX, even if
+ it is given as a string.
+
+ 31) Add missing open parenthesis in media-basic grammar rule.
+
+ 32) Correct attribute syntax in mailbox-data.
+
+ 33) Add UIDNEXT to EXAMINE responses.
+
+ 34) Clarify UNSEEN, PERMANENTFLAGS, UIDVALIDITY, and UIDNEXT
+ responses in SELECT and EXAMINE. They are required now, but weren't
+ in older versions.
+
+ 35) Update references with RFC numbers.
+
+ 36) Flush text-mime2.
+
+ 37) Clarify that modified UTF-7 names must be case-sensitive and that
+ violating the convention should be avoided.
+
+ 38) Correct UID FETCH example.
+
+ 39) Clarify UID FETCH, UID STORE, and UID SEARCH vs. untagged EXPUNGE
+ responses.
+
+ 40) Clarify the use of the word "convention".
+
+ 41) Clarify that a command is not "in progress" until it has been
+ fully received (specifically, that a command is not "in progress"
+ during command continuation negotiation).
+
+ 42) Clarify envelope defaulting.
+
+ 43) Clarify that SP means one and only one space character.
+
+ 44) Forbid silly states in LIST response.
+
+ 45) Clarify that the ENVELOPE, INTERNALDATE, RFC822*, BODY*, and UID
+ for a message is static.
+
+ 46) Add BADCHARSET response code.
+
+ 47) Update formal syntax to [ABNF] conventions.
+
+ 48) Clarify trailing hierarchy delimiter in CREATE semantics.
+
+ 49) Clarify that the "blank line" is the [RFC-2822] delimiting blank
+ line.
+
+
+
+Crispin Standards Track [Page 99]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ 50) Clarify that RENAME should also create hierarchy as needed for
+ the command to complete.
+
+ 51) Fix body-ext-mpart to not require language if disposition
+ present.
+
+ 52) Clarify the RFC822.HEADER response.
+
+ 53) Correct missing space after charset astring in search.
+
+ 54) Correct missing quote for BADCHARSET in resp-text-code.
+
+ 55) Clarify that ALL, FAST, and FULL preclude any other data items
+ appearing.
+
+ 56) Clarify semantics of reference argument in LIST.
+
+ 57) Clarify that a null string for SEARCH HEADER X-FOO means any
+ message with a header line with a field-name of X-FOO regardless of
+ the text of the header.
+
+ 58) Specifically reserve 8-bit mailbox names for future use as UTF-8.
+
+ 59) It is not an error for the client to store a flag that is not in
+ the PERMANENTFLAGS list; however, the server will either ignore the
+ change or make the change in the session only.
+
+ 60) Correct/clarify the text regarding superfluous shifts.
+
+ 61) Correct typographic errors in the "Changes" section.
+
+ 62) Clarify that STATUS must not be used to check for new messages in
+ the selected mailbox
+
+ 63) Clarify LSUB behavior with "%" wildcard.
+
+ 64) Change AUTHORIZATION to AUTHENTICATE in section 7.5.
+
+ 65) Clarify description of multipart body type.
+
+ 66) Clarify that STORE FLAGS does not affect \Recent.
+
+ 67) Change "west" to "east" in description of timezone.
+
+ 68) Clarify that commands which break command pipelining must wait
+ for a completion result response.
+
+ 69) Clarify that EXAMINE does not affect \Recent.
+
+
+
+Crispin Standards Track [Page 100]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ 70) Make description of MIME structure consistent.
+
+ 71) Clarify that date searches disregard the time and timezone of the
+ INTERNALDATE or Date: header. In other words, "ON 13-APR-2000" means
+ messages with an INTERNALDATE text which starts with "13-APR-2000",
+ even if timezone differential from the local timezone is sufficient
+ to move that INTERNALDATE into the previous or next day.
+
+ 72) Clarify that the header fetches don't add a blank line if one
+ isn't in the [RFC-2822] message.
+
+ 73) Clarify (in discussion of UIDs) that messages are immutable.
+
+ 74) Add an example of CHARSET searching.
+
+ 75) Clarify in SEARCH that keywords are a type of flag.
+
+ 76) Clarify the mandatory nature of the SELECT data responses.
+
+ 77) Add optional CAPABILITY response code in the initial OK or
+ PREAUTH.
+
+ 78) Add note that server can send an untagged CAPABILITY command as
+ part of the responses to AUTHENTICATE and LOGIN.
+
+ 79) Remove statement about it being unnecessary to issue a CAPABILITY
+ command more than once in a connection. That statement is no longer
+ true.
+
+ 80) Clarify that untagged EXPUNGE decrements the number of messages
+ in the mailbox.
+
+ 81) Fix definition of "body" (concatenation has tighter binding than
+ alternation).
+
+ 82) Add a new "Special Notes to Implementors" section with reference
+ to [IMAP-IMPLEMENTATION].
+
+ 83) Clarify that an untagged CAPABILITY response to an AUTHENTICATE
+ command should only be done if a security layer was not negotiated.
+
+ 84) Change the definition of atom to exclude "]". Update astring to
+ include "]" for compatiblity with the past. Remove resp-text-atom.
+
+ 85) Remove NEWNAME. It can't work because mailbox names can be
+ literals and can include "]". Functionality can be addressed via
+ referrals.
+
+
+
+
+Crispin Standards Track [Page 101]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ 86) Move modified UTF-7 rationale in order to have more logical
+ paragraph flow.
+
+ 87) Clarify UID uniqueness guarantees with the use of MUST.
+
+ 88) Note that clients should read response data until the connection
+ is closed instead of immediately closing on a BYE.
+
+ 89) Change RFC-822 references to RFC-2822.
+
+ 90) Clarify that RFC-2822 should be followed instead of RFC-822.
+
+ 91) Change recommendation of optional automatic capabilities in LOGIN
+ and AUTHENTICATE to use the CAPABILITY response code in the tagged
+ OK. This is more interoperable than an unsolicited untagged
+ CAPABILITY response.
+
+ 92) STARTTLS and AUTH=PLAIN are mandatory to implement; add
+ recommendations for other [SASL] mechanisms.
+
+ 93) Clarify that a "connection" (as opposed to "server" or "command")
+ is in one of the four states.
+
+ 94) Clarify that a failed or rejected command does not change state.
+
+ 95) Split references between normative and informative.
+
+ 96) Discuss authentication failure issues in security section.
+
+ 97) Clarify that a data item is not necessarily of only one data
+ type.
+
+ 98) Clarify that sequence ranges are independent of order.
+
+ 99) Change an example to clarify that superfluous shifts in
+ Modified-UTF7 can not be fixed just by omitting the shift. The
+ entire string must be recalculated.
+
+ 100) Change Envelope Structure definition since [RFC-2822] uses
+ "envelope" to refer to the [SMTP] envelope and not the envelope data
+ that appears in the [RFC-2822] header.
+
+ 101) Expand on RFC822.HEADER response data vs. BODY[HEADER].
+
+ 102) Clarify Logout state semantics, change ASCII art.
+
+ 103) Security changes to comply with IESG requirements.
+
+
+
+
+Crispin Standards Track [Page 102]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ 104) Add definition for body URI.
+
+ 105) Break sequence range definition into three rules, with rewritten
+ descriptions for each.
+
+ 106) Move STARTTLS and LOGINDISABLED here from [IMAP-TLS].
+
+ 107) Add IANA Considerations section.
+
+ 108) Clarify valid client assumptions for new message UIDs vs.
+ UIDNEXT.
+
+ 109) Clarify that changes to permanentflags affect concurrent
+ sessions as well as subsequent sessions.
+
+ 110) Clarify that authenticated state can be entered by the CLOSE
+ command.
+
+ 111) Emphasize that SELECT and EXAMINE are the exceptions to the rule
+ that a failing command does not change state.
+
+ 112) Clarify that newly-appended messages have the Recent flag set.
+
+ 113) Clarify that newly-copied messages SHOULD have the Recent flag
+ set.
+
+ 114) Clarify that UID commands always return the UID in FETCH
+ responses.
+
+C. Key Word Index
+
+ +FLAGS <flag list> (store command data item) ............... 59
+ +FLAGS.SILENT <flag list> (store command data item) ........ 59
+ -FLAGS <flag list> (store command data item) ............... 59
+ -FLAGS.SILENT <flag list> (store command data item) ........ 59
+ ALERT (response code) ...................................... 64
+ ALL (fetch item) ........................................... 55
+ ALL (search key) ........................................... 50
+ ANSWERED (search key) ...................................... 50
+ APPEND (command) ........................................... 45
+ AUTHENTICATE (command) ..................................... 27
+ BAD (response) ............................................. 66
+ BADCHARSET (response code) ................................. 64
+ BCC <string> (search key) .................................. 51
+ BEFORE <date> (search key) ................................. 51
+ BODY (fetch item) .......................................... 55
+ BODY (fetch result) ........................................ 73
+ BODY <string> (search key) ................................. 51
+
+
+
+Crispin Standards Track [Page 103]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ BODY.PEEK[<section>]<<partial>> (fetch item) ............... 57
+ BODYSTRUCTURE (fetch item) ................................. 57
+ BODYSTRUCTURE (fetch result) ............................... 74
+ BODY[<section>]<<origin octet>> (fetch result) ............. 74
+ BODY[<section>]<<partial>> (fetch item) .................... 55
+ BYE (response) ............................................. 67
+ Body Structure (message attribute) ......................... 12
+ CAPABILITY (command) ....................................... 24
+ CAPABILITY (response code) ................................. 64
+ CAPABILITY (response) ...................................... 68
+ CC <string> (search key) ................................... 51
+ CHECK (command) ............................................ 47
+ CLOSE (command) ............................................ 48
+ COPY (command) ............................................. 59
+ CREATE (command) ........................................... 34
+ DELETE (command) ........................................... 35
+ DELETED (search key) ....................................... 51
+ DRAFT (search key) ......................................... 51
+ ENVELOPE (fetch item) ...................................... 57
+ ENVELOPE (fetch result) .................................... 77
+ EXAMINE (command) .......................................... 33
+ EXISTS (response) .......................................... 71
+ EXPUNGE (command) .......................................... 48
+ EXPUNGE (response) ......................................... 72
+ Envelope Structure (message attribute) ..................... 12
+ FAST (fetch item) .......................................... 55
+ FETCH (command) ............................................ 54
+ FETCH (response) ........................................... 73
+ FLAGGED (search key) ....................................... 51
+ FLAGS (fetch item) ......................................... 57
+ FLAGS (fetch result) ....................................... 78
+ FLAGS (response) ........................................... 71
+ FLAGS <flag list> (store command data item) ................ 59
+ FLAGS.SILENT <flag list> (store command data item) ......... 59
+ FROM <string> (search key) ................................. 51
+ FULL (fetch item) .......................................... 55
+ Flags (message attribute) .................................. 11
+ HEADER (part specifier) .................................... 55
+ HEADER <field-name> <string> (search key) .................. 51
+ HEADER.FIELDS <header-list> (part specifier) ............... 55
+ HEADER.FIELDS.NOT <header-list> (part specifier) ........... 55
+ INTERNALDATE (fetch item) .................................. 57
+ INTERNALDATE (fetch result) ................................ 78
+ Internal Date (message attribute) .......................... 12
+ KEYWORD <flag> (search key) ................................ 51
+ Keyword (type of flag) ..................................... 11
+ LARGER <n> (search key) .................................... 51
+ LIST (command) ............................................. 40
+
+
+
+Crispin Standards Track [Page 104]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ LIST (response) ............................................ 69
+ LOGIN (command) ............................................ 30
+ LOGOUT (command) ........................................... 25
+ LSUB (command) ............................................. 43
+ LSUB (response) ............................................ 70
+ MAY (specification requirement term) ....................... 4
+ MESSAGES (status item) ..................................... 45
+ MIME (part specifier) ...................................... 56
+ MUST (specification requirement term) ...................... 4
+ MUST NOT (specification requirement term) .................. 4
+ Message Sequence Number (message attribute) ................ 10
+ NEW (search key) ........................................... 51
+ NO (response) .............................................. 66
+ NOOP (command) ............................................. 25
+ NOT <search-key> (search key) .............................. 52
+ OK (response) .............................................. 65
+ OLD (search key) ........................................... 52
+ ON <date> (search key) ..................................... 52
+ OPTIONAL (specification requirement term) .................. 4
+ OR <search-key1> <search-key2> (search key) ................ 52
+ PARSE (response code) ...................................... 64
+ PERMANENTFLAGS (response code) ............................. 64
+ PREAUTH (response) ......................................... 67
+ Permanent Flag (class of flag) ............................. 12
+ READ-ONLY (response code) .................................. 65
+ READ-WRITE (response code) ................................. 65
+ RECENT (response) .......................................... 72
+ RECENT (search key) ........................................ 52
+ RECENT (status item) ....................................... 45
+ RENAME (command) ........................................... 37
+ REQUIRED (specification requirement term) .................. 4
+ RFC822 (fetch item) ........................................ 57
+ RFC822 (fetch result) ...................................... 78
+ RFC822.HEADER (fetch item) ................................. 57
+ RFC822.HEADER (fetch result) ............................... 78
+ RFC822.SIZE (fetch item) ................................... 57
+ RFC822.SIZE (fetch result) ................................. 78
+ RFC822.TEXT (fetch item) ................................... 58
+ RFC822.TEXT (fetch result) ................................. 79
+ SEARCH (command) ........................................... 49
+ SEARCH (response) .......................................... 71
+ SEEN (search key) .......................................... 52
+ SELECT (command) ........................................... 31
+ SENTBEFORE <date> (search key) ............................. 52
+ SENTON <date> (search key) ................................. 52
+ SENTSINCE <date> (search key) .............................. 52
+ SHOULD (specification requirement term) .................... 4
+ SHOULD NOT (specification requirement term) ................ 4
+
+
+
+Crispin Standards Track [Page 105]
+
+RFC 3501 IMAPv4 March 2003
+
+
+ SINCE <date> (search key) .................................. 52
+ SMALLER <n> (search key) ................................... 52
+ STARTTLS (command) ......................................... 27
+ STATUS (command) ........................................... 44
+ STATUS (response) .......................................... 70
+ STORE (command) ............................................ 58
+ SUBJECT <string> (search key) .............................. 53
+ SUBSCRIBE (command) ........................................ 38
+ Session Flag (class of flag) ............................... 12
+ System Flag (type of flag) ................................. 11
+ TEXT (part specifier) ...................................... 56
+ TEXT <string> (search key) ................................. 53
+ TO <string> (search key) ................................... 53
+ TRYCREATE (response code) .................................. 65
+ UID (command) .............................................. 60
+ UID (fetch item) ........................................... 58
+ UID (fetch result) ......................................... 79
+ UID <sequence set> (search key) ............................ 53
+ UIDNEXT (response code) .................................... 65
+ UIDNEXT (status item) ...................................... 45
+ UIDVALIDITY (response code) ................................ 65
+ UIDVALIDITY (status item) .................................. 45
+ UNANSWERED (search key) .................................... 53
+ UNDELETED (search key) ..................................... 53
+ UNDRAFT (search key) ....................................... 53
+ UNFLAGGED (search key) ..................................... 53
+ UNKEYWORD <flag> (search key) .............................. 53
+ UNSEEN (response code) ..................................... 65
+ UNSEEN (search key) ........................................ 53
+ UNSEEN (status item) ....................................... 45
+ UNSUBSCRIBE (command) ...................................... 39
+ Unique Identifier (UID) (message attribute) ................ 8
+ X<atom> (command) .......................................... 62
+ [RFC-2822] Size (message attribute) ........................ 12
+ \Answered (system flag) .................................... 11
+ \Deleted (system flag) ..................................... 11
+ \Draft (system flag) ....................................... 11
+ \Flagged (system flag) ..................................... 11
+ \Marked (mailbox name attribute) ........................... 69
+ \Noinferiors (mailbox name attribute) ...................... 69
+ \Noselect (mailbox name attribute) ......................... 69
+ \Recent (system flag) ...................................... 11
+ \Seen (system flag) ........................................ 11
+ \Unmarked (mailbox name attribute) ......................... 69
+
+
+
+
+
+
+
+Crispin Standards Track [Page 106]
+
+RFC 3501 IMAPv4 March 2003
+
+
+Author's Address
+
+ Mark R. Crispin
+ Networks and Distributed Computing
+ University of Washington
+ 4545 15th Avenue NE
+ Seattle, WA 98105-4527
+
+ Phone: (206) 543-5762
+
+ EMail: MRC@CAC.Washington.EDU
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 107]
+
+RFC 3501 IMAPv4 March 2003
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2003). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns. v This
+ document and the information contained herein is provided on an "AS
+ IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK
+ FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT
+ LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL
+ NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY
+ OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 108]
+
+
diff --git a/imap/docs/rfc/rfc3502.txt b/imap/docs/rfc/rfc3502.txt
new file mode 100644
index 00000000..f6b61a44
--- /dev/null
+++ b/imap/docs/rfc/rfc3502.txt
@@ -0,0 +1,395 @@
+
+
+
+
+
+
+Network Working Group M. Crispin
+Request for Comments: 3502 University of Washington
+Category: Standards Track March 2003
+
+
+ Internet Message Access Protocol (IMAP) - MULTIAPPEND Extension
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2003). All Rights Reserved.
+
+Abstract
+
+ This document describes the multiappending extension to the Internet
+ Message Access Protocol (IMAP) (RFC 3501). This extension provides
+ substantial performance improvements for IMAP clients which upload
+ multiple messages at a time to a mailbox on the server.
+
+ A server which supports this extension indicates this with a
+ capability name of "MULTIAPPEND".
+
+Terminology
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "MAY", and "OPTIONAL" in this document are to
+ be interpreted as described in [KEYWORDS].
+
+Introduction
+
+ The MULTIAPPEND extension permits uploading of multiple messages with
+ a single command. When used in conjunction with the [LITERAL+]
+ extension, the entire upload is accomplished in a single
+ command/response round trip.
+
+ A MULTIAPPEND APPEND operation is atomic; either all messages are
+ successfully appended, or no messages are appended.
+
+ In the base IMAP specification, each message must be appended in a
+ separate command, and there is no mechanism to "unappend" messages if
+ an error occurs while appending. Also, some mail stores may require
+
+
+
+Crispin Standards Track [Page 1]
+
+RFC 3502 IMAP MULTIAPPEND March 2003
+
+
+ an expensive "open/lock + sync/unlock/close" operation as part of
+ appending; this can be quite expensive if it must be done on a
+ per-message basis.
+
+ If the server supports both LITERAL+ and pipelining but not
+ MULTIAPPEND, it may be possible to get some of the performance
+ advantages of MULTIAPPEND by doing a pipelined "batch" append.
+ However, it will not work as well as MULTIAPPEND for the following
+ reasons:
+
+ 1) Multiple APPEND commands, even as part of a pipelined batch,
+ are non-atomic by definition. There is no way to revert the
+ mailbox to the state before the batch append in the event of an
+ error.
+
+ 2) It may not be feasible for the server to coalesce pipelined
+ APPEND operations so as to avoid the "open/lock +
+ sync/unlock/close" overhead described above. In any case, such
+ coalescing would be timing dependent and thus potentially
+ unreliable. In particular, with traditional UNIX mailbox files,
+ it is assumed that a lock is held only for a single atomic
+ operation, and many applications disregard any lock that is
+ older than 5 minutes.
+
+ 3) If an error occurs, depending upon the nature of the error,
+ it is possible for additional messages to be appended after the
+ error. For example, the user wants to append 5 messages, but a
+ disk quota error occurs with the third message because of its
+ size. However, the fourth and fifth messages have already been
+ sent in the pipeline, so the mailbox ends up with the first,
+ second, fourth, and fifth messages of the batch appended.
+
+6.3.11. APPEND Command
+
+ Arguments: mailbox name
+ one or more messages to upload, specified as:
+ OPTIONAL flag parenthesized list
+ OPTIONAL date/time string
+ message literal
+
+ Data: no specific responses for this command
+
+ Result: OK - append completed
+ NO - append error: can't append to that mailbox, error
+ in flags or date/time or message text,
+ append cancelled
+ BAD - command unknown or arguments invalid
+
+
+
+
+Crispin Standards Track [Page 2]
+
+RFC 3502 IMAP MULTIAPPEND March 2003
+
+
+ The APPEND command appends the literal arguments as new messages
+ to the end of the specified destination mailbox. This argument
+ SHOULD be in the format of an [RFC-2822] message. 8-bit
+ characters are permitted in the message. A server implementation
+ that is unable to preserve 8-bit data properly MUST be able to
+ reversibly convert 8-bit APPEND data to 7-bit using a [MIME-IMB]
+ content transfer encoding.
+
+ Note: There MAY be exceptions, e.g., draft messages, in
+ which required [RFC-2822] header lines are omitted in the
+ message literal argument to APPEND. The full implications
+ of doing so MUST be understood and carefully weighed.
+
+ If a flag parenthesized list is specified, the flags SHOULD be set
+ in the resulting message; otherwise, the flag list of the
+ resulting message is set empty by default.
+
+ If a date-time is specified, the internal date SHOULD be set in
+ the resulting message; otherwise, the internal date of the
+ resulting message is set to the current date and time by default.
+
+ A zero-length message literal argument is an error, and MUST
+ return a NO. This can be used to cancel the append.
+
+ If the append is unsuccessful for any reason (including being
+ cancelled), the mailbox MUST be restored to its state before the
+ APPEND attempt; no partial appending is permitted. The server MAY
+ return an error before processing all the message arguments.
+
+ If the destination mailbox does not exist, a server MUST return an
+ error, and MUST NOT automatically create the mailbox. Unless it
+ is certain that the destination mailbox can not be created, the
+ server MUST send the response code "[TRYCREATE]" as the prefix of
+ the text of the tagged NO response. This gives a hint to the
+ client that it can attempt a CREATE command and retry the APPEND
+ if the CREATE is successful.
+
+ If the mailbox is currently selected, the normal new message
+ actions SHOULD occur. Specifically, the server SHOULD notify the
+ client immediately via an untagged EXISTS response. If the server
+ does not do so, the client MAY issue a NOOP command (or failing
+ that, a CHECK command) after one or more APPEND commands.
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 3]
+
+RFC 3502 IMAP MULTIAPPEND March 2003
+
+
+ Example: C: A003 APPEND saved-messages (\Seen) {329}
+ S: + Ready for literal data
+ C: Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
+ C: From: Fred Foobar <foobar@Blurdybloop.example.COM>
+ C: Subject: afternoon meeting
+ C: To: mooch@owatagu.example.net
+ C: Message-Id: <B27397-0100000@Blurdybloop.example.COM>
+ C: MIME-Version: 1.0
+ C: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
+ C:
+ C: Hello Joe, do you think we can meet at 3:30 tomorrow?
+ C: (\Seen) " 7-Feb-1994 22:43:04 -0800" {295}
+ S: + Ready for literal data
+ C: Date: Mon, 7 Feb 1994 22:43:04 -0800 (PST)
+ C: From: Joe Mooch <mooch@OWaTaGu.example.net>
+ C: Subject: Re: afternoon meeting
+ C: To: foobar@blurdybloop.example.com
+ C: Message-Id: <a0434793874930@OWaTaGu.example.net>
+ C: MIME-Version: 1.0
+ C: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
+ C:
+ C: 3:30 is fine with me.
+ C:
+ S: A003 OK APPEND completed
+ C: A004 APPEND bogusname (\Flagged) {1023}
+ S: A004 NO [TRYCREATE] No such mailbox as bogusname
+ C: A005 APPEND test (\Flagged) {99}
+ S: + Ready for literal data
+ C: Date: Mon, 7 Feb 2000 22:43:04 -0800 (PST)
+ C: From: Fred Foobar <fred@example.com>
+ C: Subject: hmm...
+ C: {35403}
+ S: A005 NO APPEND failed: Disk quota exceeded
+
+ Note: The APPEND command is not used for message delivery,
+ because it does not provide a mechanism to transfer [SMTP]
+ envelope information.
+
+Modification to IMAP4rev1 Base Protocol Formal Syntax
+
+ The following syntax specification uses the Augmented Backus-Naur
+ Form (ABNF) notation as specified in [ABNF].
+
+ append = "APPEND" SP mailbox 1*append-message
+
+ append-message = [SP flag-list] [SP date-time] SP literal
+
+
+
+
+
+Crispin Standards Track [Page 4]
+
+RFC 3502 IMAP MULTIAPPEND March 2003
+
+
+MULTIAPPEND Interaction with UIDPLUS Extension
+
+ Servers which support both MULTIAPPEND and [UIDPLUS] will have the
+ "resp-code-apnd" rule modified as follows:
+
+ resp-code-apnd = "APPENDUID" SP nz-number SP set
+
+ That is, the APPENDUID response code returns as many UIDs as there
+ were messages appended in the multiple append. The UIDs returned
+ should be in the order the articles where appended. The message set
+ may not contain extraneous UIDs or the symbol "*".
+
+Security Considerations
+
+ The MULTIAPPEND extension does not raise any security considerations
+ that are not present in the base [IMAP] protocol, and these issues
+ are discussed in [IMAP]. Nevertheless, it is important to remember
+ that IMAP4rev1 protocol transactions, including electronic mail data,
+ are sent in the clear over the network unless protection from
+ snooping is negotiated, either by the use of STARTTLS, privacy
+ protection is negotiated in the AUTHENTICATE command, or some other
+ protection mechanism is in effect.
+
+Normative References
+
+ [ABNF] Crocker, D. and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", RFC 2234, November 1997.
+
+ [IMAP] Crispin, M., "Internet Message Access Protocol - Version
+ 4rev1", RFC 3501, March 2003.
+
+ [KEYWORDS] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [MIME-IMB] Freed, N. and N. Borenstein, "MIME (Multipurpose Internet
+ Mail Extensions) Part One: Format of Internet Message
+ Bodies", RFC 2045, November 1996.
+
+ [RFC-2822] Resnick, P., "Internet Message Format", RFC 2822, April
+ 2001.
+
+
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 5]
+
+RFC 3502 IMAP MULTIAPPEND March 2003
+
+
+Informative References
+
+ [LITERAL+] Myers, J., "IMAP4 non-synchronizing literals", RFC 2088,
+ January 1997.
+
+ [UIDPLUS] Myers, J., "IMAP4 UIDPLUS extension", RFC 2359, June 1988.
+
+ [SMTP] Klensin, J., Editor, "Simple Mail Transfer Protocol", RFC
+ 2821, April 2001.
+
+Author's Address
+
+ Mark R. Crispin
+ Networks and Distributed Computing
+ University of Washington
+ 4545 15th Avenue NE
+ Seattle, WA 98105-4527
+
+ Phone: (206) 543-5762
+ EMail: MRC@CAC.Washington.EDU
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 6]
+
+RFC 3502 IMAP MULTIAPPEND March 2003
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2003). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 7]
+
diff --git a/imap/docs/rfc/rfc3503.txt b/imap/docs/rfc/rfc3503.txt
new file mode 100644
index 00000000..5b82fb08
--- /dev/null
+++ b/imap/docs/rfc/rfc3503.txt
@@ -0,0 +1,507 @@
+
+
+
+
+
+
+Network Working Group A. Melnikov
+Request for Comments: 3503 ACI Worldwide/MessagingDirect
+Category: Standards Track March 2003
+
+
+ Message Disposition Notification (MDN) profile for
+ Internet Message Access Protocol (IMAP)
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2003). All Rights Reserved.
+
+Abstract
+
+ The Message Disposition Notification (MDN) facility defined in RFC
+ 2298 provides a means by which a message can request that message
+ processing by the recipient be acknowledged as well as a format to be
+ used for such acknowledgements. However, it doesn't describe how
+ multiple Mail User Agents (MUAs) should handle the generation of MDNs
+ in an Internet Message Access Protocol (IMAP4) environment.
+
+ This document describes how to handle MDNs in such an environment and
+ provides guidelines for implementers of IMAP4 that want to add MDN
+ support to their products.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Melnikov Standards Track [Page 1]
+
+RFC 3503 MDN profile for IMAP March 2003
+
+
+Table of Contents
+
+ 1. Conventions Used in this Document............................. 2
+ 2. Introduction and Overview..................................... 2
+ 3. Client behavior............................................... 3
+ 3.1. Client behavior when receiving a message................. 5
+ 3.2. Client behavior when copying a message................... 5
+ 3.3. Client behavior when sending a message................... 5
+ 3.4. Client behavior when saving a temporary message.......... 5
+ 4. Server behavior............................................... 5
+ 4.1. Server that supports arbitrary keywords.................. 5
+ 4.2. Server that supports only $MDNSent keyword............... 5
+ 4.3. Interaction with IMAP ACL extension...................... 6
+ 5. Examples...................................................... 6
+ 6. Security Considerations....................................... 7
+ 7. Formal Syntax................................................. 7
+ 8. Acknowledgments............................................... 7
+ 9. Normative References.......................................... 8
+ 10. Author's Address.............................................. 8
+ 11. Full Copyright Statement...................................... 9
+
+1. Conventions Used in this Document
+
+ "C:" and "S:" in examples show lines sent by the client and server
+ respectively.
+
+ The keywords "MUST", "MUST NOT", "SHOULD", "SHOULD NOT", and "MAY" in
+ this document when typed in uppercase are to be interpreted as
+ defined in "Key words for use in RFCs to Indicate Requirement Levels"
+ [KEYWORDS].
+
+2. Introduction and Overview
+
+ This memo defines an additional [IMAP4] mailbox keyword that allows
+ multiple Mail User Agents (MUAs) to know if a requested receipt
+ notification was sent.
+
+ Message Disposition Notification [MDN] does not require any special
+ support of IMAP in the case where a user has access to the mailstore
+ from only one computer and is using a single MUA. In this case, the
+ MUA behaves as described in [MDN], i.e., the MUA performs automatic
+ processing and generates corresponding MDNs, it performs requested
+ action and, with the user's permission, sends appropriate MDNs. The
+ MUA will not send MDN twice because the MUA keeps track of sent
+ notifications in a local configuration. However, that does not work
+ when IMAP is used to access the same mailstore from different
+ locations or is using different MUAs.
+
+
+
+
+Melnikov Standards Track [Page 2]
+
+RFC 3503 MDN profile for IMAP March 2003
+
+
+ This document defines a new special purpose mailbox keyword $MDNSent
+ that must be used by MUAs. It does not define any new command or
+ response for IMAP, but describes a technique that MUAs should use to
+ achieve interoperability.
+
+ When a client opens a mailbox for the first time, it verifies that
+ the server is capable of storing the $MDNSent keyword by examining
+ the PERMANENTFLAGS response code. In order to support MDN in IMAP, a
+ server MUST support either the $MDNSent keyword, or arbitrary message
+ keywords.
+
+3. Client behavior
+
+ The use of IMAP requires few additional steps in mail processing on
+ the client side. The following timeline modifies the timeline found
+ in Section 4 of [MDN].
+
+ -- User composes message.
+
+ -- User tells MUA to send message.
+
+ -- MUA passes message to MSA (original recipient information passed
+ along). MUA [optionally] saves message to a folder for sent mail
+ with $MDNSent flag set.
+
+ -- MSA sends message to MTA.
+
+ -- Final MTA receives message.
+
+ -- Final MTA delivers message to MUA (possibly generating DSN).
+
+ -- MUA logs into IMAP server, opens mailbox, verifies if mailbox can
+ store $MDNSent keyword by examining PERMANENTFLAGS response.
+
+ -- MUA performs automatic processing and generates corresponding MDNs
+ ("dispatched", "processed", "deleted", "denied" or "failed"
+ disposition type with "automatic-action" and "MDN-sent-
+ automatically" disposition modes) for messages that do not have
+ $MDNSent keyword, or \Draft flag set. (*)
+
+ -- MUA sets the $MDNSent keyword for every message that required an
+ automatic MDN to be sent, whether or not the MDN was sent.
+
+ -- MUA displays a list of messages to user.
+
+ -- User selects a message and requests that some action be performed
+ on it.
+
+
+
+
+Melnikov Standards Track [Page 3]
+
+RFC 3503 MDN profile for IMAP March 2003
+
+
+ -- MUA performs requested action and, with user's permission, sends
+ appropriate MDN ("displayed", "dispatched", "processed",
+ "deleted", "denied" or "failed" disposition type with "manual-
+ action" and "MDN-sent-manually" or "MDN-sent-automatically"
+ disposition mode). If the generated MDN is saved to a mailbox
+ with the APPEND command, the client MUST specify the $MDNSent
+ keyword in the APPEND.
+
+ -- MUA sets the $MDNSent keyword for all messages for which the user
+ confirmed the dispatching of disposition (or was explicitly
+ prohibited to do so).
+
+ -- User possibly performs other actions on message, but no further
+ MDNs are generated.
+
+ (*) Note: MUA MUST NOT use \Recent flag as an indicator that it
+ should send MDN, because according to [IMAP4], "If multiple
+ connections have the same mailbox selected simultaneously, it is
+ undefined which of these connections will see newly-arrived
+ messages with \Recent set and which will see it without \Recent
+ set". Thus, using \Recent as an indicator will cause
+ unpredictable client behavior with different IMAP4 servers.
+ However, the client MAY use \Seen flag as one of the indicators
+ that MDN must not be sent. The client MUST NOT use any other
+ standard flags, like \Draft or \Answered, to indicate that MDN
+ was previously sent, because they have different well known
+ meaning. In any case, in the presence of the $MDNSent keyword,
+ the client MUST ignore all other flags or keywords for the
+ purpose of generating an MDN and MUST NOT send the MDN.
+
+ When the client opens a mailbox for the first time, it must verify
+ that the server supports the $MDNSent keyword, or arbitrary message
+ keywords by examining PERMANENTFLAGS response code.
+
+ The client MUST NOT try to set the $MDNSent keyword if the server is
+ incapable of storing it permanently.
+
+ The client MUST be prepared to receive NO from the server as the
+ result of STORE $MDNSent when the server advertises the support of
+ storing arbitrary keywords, because the server may limit the number
+ of message keywords it can store in a particular mailbox. A client
+ SHOULD NOT send MDN if it fails to store the $MDNSent keyword.
+
+ Once the $MDNSent keyword is set, it MUST NOT be unset by a client.
+ The client MAY set the $MDNSent keyword when a user denies sending
+ the notification. This prohibits all other MUAs from sending MDN for
+ this message.
+
+
+
+
+Melnikov Standards Track [Page 4]
+
+RFC 3503 MDN profile for IMAP March 2003
+
+
+3.1. Client behavior when receiving a message
+
+ The client MUST NOT send MDN if a message has the $MDNSent keyword
+ set. It also MUST NOT send MDN if a message has \Draft flag, because
+ some clients use this flag to mark a message as incomplete.
+
+ See the timeline in section 3 for details on client behavior when
+ receiving a message.
+
+3.2. Client behavior when copying a message
+
+ The client SHOULD verify that $MDNSent is preserved on a COPY
+ operation. Furthermore, when a message is copied between servers
+ with the APPEND command, the client MUST set the $MDNSent keyword
+ correctly.
+
+3.3. Client behavior when sending a message
+
+ When saving a sent message to any folder, the client MUST set the
+ $MDNSent keyword to prevent another client from sending MDN for the
+ message.
+
+3.4. Client behavior when saving a temporary message
+
+ When saving an unfinished message to any folder client MUST set
+ $MDNSent keyword to prevent another client from sending MDN for the
+ message.
+
+4. Server behavior
+
+ Server implementors that want to follow this specification must
+ insure that their server complies with either section 4.1 or section
+ 4.2. If the server also supports the IMAP [ACL] extension, it MUST
+ also comply with the section 4.3.
+
+4.1. Server that supports arbitrary keywords
+
+ No changes are required from the server to make it compatible with
+ the extension described in this document if it supports arbitrary
+ keywords.
+
+4.2. Server that supports only $MDNSent keyword
+
+ Servers that support only the $MDNSent keyword MUST preserve it on
+ the COPY operation. It is also expected that a server that supports
+ SEARCH <flag> will also support the SEARCH KEYWORD $MDNSent.
+
+
+
+
+
+Melnikov Standards Track [Page 5]
+
+RFC 3503 MDN profile for IMAP March 2003
+
+
+4.3. Interaction with IMAP ACL extension
+
+ Any server that conforms to either 4.1 or 4.2 and also supports the
+ IMAP [ACL] extension, SHOULD preserve the $MDNSent keyword on COPY
+ even if the client does not have 'w' right. This will prevent the
+ generation of a duplicated MDN for the same message. Note that the
+ server MUST still check if the client has rights to perform the COPY
+ operation on a message according to [ACL].
+
+5. Examples
+
+ 1) MUA opens mailbox for the first time.
+
+ a) The server supports storing of arbitrary keywords
+
+ C: a100 select INBOX
+ S: * FLAGS (\Flagged \Draft \Deleted \Seen)
+ S: * OK [PERMANENTFLAGS (\Flagged \Draft \Deleted \Seen \*)]
+ S: * 5 EXISTS
+ S: * 3 RECENT
+ S: * OK [UIDVALIDITY 894294713]
+ S: a100 OK [READ-WRITE] Completed
+
+ b) The server supports storing of the $MDNSent keyword
+
+ C: a100 select INBOX
+ S: * FLAGS (\Flagged \Draft \Deleted \Seen $MDNSent)
+ S: * OK [PERMANENTFLAGS (\Flagged \Draft \Deleted \Seen $MDNSent)]
+ S: * 5 EXISTS
+ S: * 3 RECENT
+ S: * OK [UIDVALIDITY 894294713]
+ S: a100 OK [READ-WRITE] Completed
+
+ 2) The MUA successfully sets the $MDNSent keyword
+
+ C: a200 STORE 4 +FLAGS ($MDNSent)
+ S: * 4 FETCH (FLAGS (\Flagged \Seen $MDNSent))
+ S: * FLAGS ($MDNSent \Flagged \Deleted \Draft \Seen)
+ S: * OK [PERMANENTFLAGS ($MDNSent \Flagged \Deleted \Draft \Seen \*)]
+ S: a200 OK STORE completed
+
+ 3) The server refuses to store the $MDNSent keyword
+
+ C: a200 STORE 4 +FLAGS ($MDNSent)
+ S: a200 NO STORE failed : no space left to store $MDNSent keyword
+
+
+
+
+
+
+Melnikov Standards Track [Page 6]
+
+RFC 3503 MDN profile for IMAP March 2003
+
+
+ 4) All clients and servers MUST treat the $MDNSent keyword as case
+ insensitive in all operations, as stated in [IMAP].
+
+ C: a300 FETCH 1:* FLAGS
+ S: * 1 FETCH (FLAGS (\Seen))
+ S: * 2 FETCH (FLAGS (\Answered \Seen $MdnSENt))
+ S: * 3 FETCH (FLAGS ())
+ S: * 4 FETCH (FLAGS (\Flagged \Seen $MdnSENT))
+ S: * 5 FETCH (FLAGS ($MDNSent))
+ S: * 6 FETCH (FLAGS (\Recent))
+ S: a300 OK FETCH completed
+ C: a400 SEARCH KEYWORDS $mdnsent
+ S: * SEARCH 2 4 5
+ S: a400 OK SEARCH completed
+
+6. Security Considerations
+
+ There are no known security issues with this extension, not found in
+ [MDN] and/or [IMAP4].
+
+ Section 4.3 changes ACL checking requirements on an IMAP server that
+ implements IMAP [ACL] extension.
+
+7. Formal Syntax
+
+ The following syntax specification uses the augmented Backus-Naur
+ Form (BNF) notation as specified in [RFC-822], as modified by
+ [IMAP4]. Non-terminals referenced, but not defined below, are as
+ defined by [IMAP4].
+
+ Except as noted otherwise, all alphabetic characters are case-
+ insensitive. The use of upper or lower case characters to define
+ token strings is for editorial clarity only. Implementations MUST
+ accept these strings in a case-insensitive fashion.
+
+ flag_keyword ::= "$MDNSent" / other_keywords
+
+ other_keywords ::= atom
+
+8. Acknowledgments
+
+ This document is the product of discussions that took place on the
+ IMAP mailing list. Special gratitude to Cyrus Daboo and Randall
+ Gellens for reviewing the document.
+
+ Thank you to my father who as he has helped to make me what I am. I
+ miss you terribly.
+
+
+
+
+Melnikov Standards Track [Page 7]
+
+RFC 3503 MDN profile for IMAP March 2003
+
+
+9. Normative References
+
+ [KEYWORDS] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [MDN] Fajman, R., "An Extensible Message Format for Message
+ Disposition Notifications", RFC 2298, March 1998.
+
+ [IMAP4] Crispin, M., "Internet Message Access Protocol - Version
+ 4rev1", RFC 3501, March 2003.
+
+ [ACL] Myers, J., "IMAP4 ACL extension", RFC 2086, January 1997.
+
+10. Author's Address
+
+ Alexey Melnikov
+ ACI Worldwide/MessagingDirect
+ 59 Clarendon Road
+ Watford, Hertfordshire
+ United Kingdom, WD17 1FQ
+
+ Phone: +44 1923 81 2877
+ EMail: mel@messagingdirect.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Melnikov Standards Track [Page 8]
+
+RFC 3503 MDN profile for IMAP March 2003
+
+
+11. Full Copyright Statement
+
+ Copyright (C) The Internet Society (2003). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Melnikov Standards Track [Page 9]
+
diff --git a/imap/docs/rfc/rfc3516.txt b/imap/docs/rfc/rfc3516.txt
new file mode 100644
index 00000000..4d021975
--- /dev/null
+++ b/imap/docs/rfc/rfc3516.txt
@@ -0,0 +1,451 @@
+
+
+
+
+
+
+Network Working Group L. Nerenberg
+Request for Comments: 3516 Orthanc Systems
+Category: Standards Track April 2003
+
+
+ IMAP4 Binary Content Extension
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2003). All Rights Reserved.
+
+Abstract
+
+ This memo defines the Binary extension to the Internet Message Access
+ Protocol (IMAP4). It provides a mechanism for IMAP4 clients and
+ servers to exchange message body data without using a MIME content-
+ transfer-encoding.
+
+1. Conventions Used in this Document
+
+ The key words "MUST", "MUST NOT", "SHOULD", "SHOULD NOT", and "MAY"
+ in this document are to be interpreted as described in [KEYWORD].
+
+ The abbreviation "CTE" means content-transfer-encoding.
+
+2. Introduction
+
+ The MIME extensions to Internet messaging allow for the transmission
+ of non-textual (binary) message content [MIME-IMB]. Since the
+ traditional transports for messaging are not always capable of
+ passing binary data transparently, MIME provides encoding schemes
+ that allow binary content to be transmitted over transports that are
+ not otherwise able to do so.
+
+ The overhead of MIME-encoding this content can be considerable in
+ some contexts (e.g., slow radio links, streaming multimedia).
+ Reducing the overhead associated with CTE schemes such as base64
+
+
+
+
+
+
+Nerenberg Standards Track [Page 1]
+
+RFC 3516 IMAP4 Binary Content Extension April 2003
+
+
+ can give a noticeable reduction in resource consumption. The Binary
+ extension lets the server perform CTE decoding prior to transmitting
+ message data to the client.
+
+3. Content-Transfer-Encoding Considerations
+
+ Every IMAP4 body section has a MIME content-transfer-encoding.
+ (Those without an explicit Content-Transfer-Encoding header are
+ implicitly labeled as "7bit" content.) In the terminology of [MIME-
+ IMB], the CTE specifies both a decoding algorithm and the domain of
+ the decoded data. In this memo, "decoding" refers to the CTE
+ decoding step described in [MIME-IMB].
+
+ Certain CTEs use an identity encoding transformation. For these CTEs
+ there is no decoding required, however the domain of the underlying
+ data may not be expressible in the IMAP4 protocol (e.g., MIME
+ "binary" content containing NUL octets). To accommodate these cases
+ the Binary extension introduces a new type of literal protocol
+ element that is fully eight bit transparent.
+
+ Thus, server processing of the FETCH BINARY command involves two
+ logical steps:
+
+ 1) perform any CTE-related decoding
+
+ 2) determine the domain of the decoded data
+
+ Step 2 is necessary to determine which protocol element should be
+ used to transmit the decoded data. (See FETCH Response Extensions
+ for further details.)
+
+4. Framework for the IMAP4 Binary Extension
+
+ This memo defines the following extensions to [IMAP4rev1].
+
+4.1. CAPABILITY Identification
+
+ IMAP4 servers that support this extension MUST include "BINARY" in
+ the response list to the CAPABILITY command.
+
+4.2. FETCH Command Extensions
+
+ This extension defines three new FETCH command data items.
+
+ BINARY<section-binary>[<partial>]
+
+ Requests that the specified section be transmitted after
+ performing CTE-related decoding.
+
+
+
+Nerenberg Standards Track [Page 2]
+
+RFC 3516 IMAP4 Binary Content Extension April 2003
+
+
+ The <partial> argument, if present, requests that a subset of
+ the data be returned. The semantics of a partial FETCH BINARY
+ command are the same as for a partial FETCH BODY command, with
+ the exception that the <partial> arguments refer to the DECODED
+ section data.
+
+ BINARY.PEEK<section-binary>[<partial>]
+
+ An alternate form of FETCH BINARY that does not implicitly set
+ the \Seen flag.
+
+ BINARY.SIZE<section-binary>
+
+ Requests the decoded size of the section (i.e., the size to
+ expect in response to the corresponding FETCH BINARY request).
+
+ Note: client authors are cautioned that this might be an
+ expensive operation for some server implementations.
+ Needlessly issuing this request could result in degraded
+ performance due to servers having to calculate the value every
+ time the request is issued.
+
+4.3. FETCH Response Extensions
+
+ This extension defines two new FETCH response data items.
+
+ BINARY<section-binary>[<<number>>]
+
+ An <nstring> or <literal8> expressing the content of the
+ specified section after removing any CTE-related encoding. If
+ <number> is present it refers to the offset within the DECODED
+ section data.
+
+ If the domain of the decoded data is "8bit" and the data does
+ not contain the NUL octet, the server SHOULD return the data in
+ a <string> instead of a <literal8>; this allows the client to
+ determine if the "8bit" data contains the NUL octet without
+ having to explicitly scan the data stream for for NULs.
+
+ If the server does not know how to decode the section's CTE, it
+ MUST fail the request and issue a "NO" response that contains
+ the "UNKNOWN-CTE" extended response code.
+
+
+
+
+
+
+
+
+
+Nerenberg Standards Track [Page 3]
+
+RFC 3516 IMAP4 Binary Content Extension April 2003
+
+
+ BINARY.SIZE<section-binary>
+
+ The size of the section after removing any CTE-related
+ encoding. The value returned MUST match the size of the
+ <nstring> or <literal8> that will be returned by the
+ corresponding FETCH BINARY request.
+
+ If the server does not know how to decode the section's CTE, it
+ MUST fail the request and issue a "NO" response that contains
+ the "UNKNOWN-CTE" extended response code.
+
+4.4. APPEND Command Extensions
+
+ The APPEND command is extended to allow the client to append data
+ containing NULs by using the <literal8> syntax. The server MAY
+ modify the CTE of the appended data, however any such transformation
+ MUST NOT result in a loss of data.
+
+ If the destination mailbox does not support the storage of binary
+ content, the server MUST fail the request and issue a "NO" response
+ that contains the "UNKNOWN-CTE" extended response code.
+
+5. MIME Encoded Headers
+
+ [MIME-MHE] defines an encoding that allows for non-US-ASCII text in
+ message headers. This encoding is not the same as the content-
+ transfer-encoding applied to message bodies, and the decoding
+ transformations described in this memo do not apply to [MIME-MHE]
+ encoded header text. A server MUST NOT perform any conversion of
+ [MIME-MHE] encoded header text in response to any binary FETCH or
+ APPEND request.
+
+6. Implementation Considerations
+
+ Messaging clients and servers have been notoriously lax in their
+ adherence to the Internet CRLF convention for terminating lines of
+ textual data in Internet protocols. When sending data using the
+ Binary extension, servers MUST ensure that textual line-oriented
+ sections are always transmitted using the IMAP4 CRLF line termination
+ syntax, regardless of the underlying storage representation of the
+ data on the server.
+
+ A server may choose to store message body binary content in a non-
+ encoded format. Regardless of the internal storage representation
+ used, the server MUST issue BODYSTRUCTURE responses that describe the
+ message as though the binary-encoded sections are encoded in a CTE
+
+
+
+
+
+Nerenberg Standards Track [Page 4]
+
+RFC 3516 IMAP4 Binary Content Extension April 2003
+
+
+ acceptable to the IMAP4 base specification. Furthermore, the results
+ of a FETCH BODY MUST return the message body content in the format
+ described by the corresponding FETCH BODYSTRUCTURE response.
+
+ While the server is allowed to modify the CTE of APPENDed <literal8>
+ data, this should only be done when it is absolutely necessary.
+ Gratuitous encoding changes will render useless most cryptographic
+ operations that have been performed on the message.
+
+ This extension provides an optimization that is useful in certain
+ specific situations. It does not absolve clients from providing
+ basic functionality (content transfer decoding) that should be
+ available in all messaging clients. Clients supporting this
+ extension SHOULD be prepared to perform their own CTE decoding
+ operations.
+
+7. Formal Protocol Syntax
+
+ The following syntax specification uses the augmented Backus-Naur
+ Form (ABNF) notation as used in [ABNF], and incorporates by reference
+ the Core Rules defined in that document.
+
+ This syntax augments the grammar specified in [IMAP4rev1].
+
+ append =/ "APPEND" SP mailbox [SP flag-list]
+ [SP date-time] SP literal8
+
+ fetch-att =/ "BINARY" [".PEEK"] section-binary [partial]
+ / "BINARY.SIZE" section-binary
+
+ literal8 = "~{" number "}" CRLF *OCTET
+ ; <number> represents the number of OCTETs
+ ; in the response string.
+
+ msg-att-static =/ "BINARY" section-binary SP (nstring / literal8)
+ / "BINARY.SIZE" section-binary SP number
+
+ partial = "<" number "." nz-number ">"
+
+ resp-text-code =/ "UNKNOWN-CTE"
+
+ section-binary = "[" [section-part] "]"
+
+
+
+
+
+
+
+
+
+Nerenberg Standards Track [Page 5]
+
+RFC 3516 IMAP4 Binary Content Extension April 2003
+
+
+8. Normative References
+
+ [ABNF] Crocker, D., Editor, and P. Overell, "Augmented BNF for
+ Syntax Specifications: ABNF", RFC 2234, November 1997.
+
+ [IMAP4rev1] Crispin, M., "Internet Message Access Protocol Version
+ 4rev1", RFC 3501, March 2003.
+
+ [KEYWORD] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [MIME-IMB] Freed, N. and N. Borenstein, "Multipurpose Internet Mail
+ Extensions (MIME) Part One: Format of Internet Message
+ Bodies", RFC 2045, November 1996.
+
+ [MIME-MHE] Moore, K., "MIME (Multipurpose Internet Mail Extensions)
+ Part Three: Message Header Extensions for Non-ASCII
+ Text", RFC 2047, November 1996.
+
+9. Security Considerations
+
+ There are no known additional security issues with this extension
+ beyond those described in the base protocol described in [IMAP4rev1].
+
+10. Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ intellectual property or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; neither does it represent that it
+ has made any effort to identify any such rights. Information on the
+ IETF's procedures with respect to rights in standards-track and
+ standards-related documentation can be found in BCP-11. Copies of
+ claims of rights made available for publication and any assurances of
+ licenses to be made available, or the result of an attempt made to
+ obtain a general license or permission for the use of such
+ proprietary rights by implementors or users of this specification can
+ be obtained from the IETF Secretariat.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights which may cover technology that may be required to practice
+ this standard. Please address the information to the IETF Executive
+ Director.
+
+
+
+
+
+
+Nerenberg Standards Track [Page 6]
+
+RFC 3516 IMAP4 Binary Content Extension April 2003
+
+
+11. Author's Address
+
+ Lyndon Nerenberg
+ Orthanc Systems
+ 1606 - 10770 Winterburn Road
+ Edmonton, Alberta
+ Canada T5S 1T6
+
+ EMail: lyndon@orthanc.ab.ca
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Nerenberg Standards Track [Page 7]
+
+RFC 3516 IMAP4 Binary Content Extension April 2003
+
+
+12. Full Copyright Statement
+
+ Copyright (C) The Internet Society (2003). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Nerenberg Standards Track [Page 8]
+
diff --git a/imap/docs/rfc/rfc3656.txt b/imap/docs/rfc/rfc3656.txt
new file mode 100644
index 00000000..6c0ab5b1
--- /dev/null
+++ b/imap/docs/rfc/rfc3656.txt
@@ -0,0 +1,1067 @@
+
+
+
+
+
+
+Network Working Group R. Siemborski
+Request for Comments: 3656 Carnegie Mellon University
+Category: Experimental December 2003
+
+
+ The Mailbox Update (MUPDATE)
+ Distributed Mailbox Database Protocol
+
+Status of this Memo
+
+ This memo defines an Experimental Protocol for the Internet
+ community. It does not specify an Internet standard of any kind.
+ Discussion and suggestions for improvement are requested.
+ Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2003). All Rights Reserved.
+
+Abstract
+
+ As the demand for high-performance mail delivery agents increases, it
+ becomes apparent that single-machine solutions are inadequate to the
+ task, both because of capacity limits and that the failure of the
+ single machine means a loss of mail delivery for all users. It is
+ preferable to allow many machines to share the responsibility of mail
+ delivery.
+
+ The Mailbox Update (MUPDATE) protocol allows a group of Internet
+ Message Access Protocol (IMAP) or Post Office Protocol - Version 3
+ (POP3) servers to function with a unified mailbox namespace. This
+ document is intended to serve as a reference guide to that protocol.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Siemborski Experimental [Page 1]
+
+RFC 3656 MUPDATE Distributed Mailbox Database Protocol December 2003
+
+
+Table of Contents
+
+ 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 3
+ 2. Protocol Overview . . . . . . . . . . . . . . . . . . . . . . 3
+ 2.1. Atoms . . . . . . . . . . . . . . . . . . . . . . . . . 4
+ 2.2. Strings . . . . . . . . . . . . . . . . . . . . . . . . 4
+ 3. Server Responses . . . . . . . . . . . . . . . . . . . . . . 4
+ 3.1. Response: OK . . . . . . . . . . . . . . . . . . . . . 5
+ 3.2. Response: NO . . . . . . . . . . . . . . . . . . . . . 5
+ 3.3. Response: BAD . . . . . . . . . . . . . . . . . . . . . 5
+ 3.4. Response: BYE . . . . . . . . . . . . . . . . . . . . . 6
+ 3.5. Response: RESERVE . . . . . . . . . . . . . . . . . . . 6
+ 3.6. Response: MAILBOX . . . . . . . . . . . . . . . . . . . 6
+ 3.7. Response: DELETE . . . . . . . . . . . . . . . . . . . 7
+ 3.8. Server Capability Response. . . . . . . . . . . . . . . 7
+ 4. Client Commands . . . . . . . . . . . . . . . . . . . . . . . 8
+ 4.1. Command: ACTIVATE . . . . . . . . . . . . . . . . . . . 8
+ 4.2. Command: AUTHENTICATE . . . . . . . . . . . . . . . . . 8
+ 4.3. Command: DEACTIVATE . . . . . . . . . . . . . . . . . . 9
+ 4.4. Command: DELETE . . . . . . . . . . . . . . . . . . . . 9
+ 4.5. Command: FIND . . . . . . . . . . . . . . . . . . . . . 9
+ 4.6. Command: LIST . . . . . . . . . . . . . . . . . . . . . 10
+ 4.7. Command: LOGOUT . . . . . . . . . . . . . . . . . . . . 10
+ 4.8. Command: NOOP . . . . . . . . . . . . . . . . . . . . . 10
+ 4.9. Command: RESERVE. . . . . . . . . . . . . . . . . . . . 10
+ 4.10. Command: STARTTLS . . . . . . . . . . . . . . . . . . . 11
+ 4.11. Command: UPDATE . . . . . . . . . . . . . . . . . . . . 12
+ 5. MUPDATE Formal Syntax . . . . . . . . . . . . . . . . . . . . 12
+ 6. MUPDATE URL Scheme. . . . . . . . . . . . . . . . . . . . . . 14
+ 6.1. MUPDATE URL Scheme Registration Form. . . . . . . . . . 14
+ 7. Security Considerations . . . . . . . . . . . . . . . . . . . 15
+ 8. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 16
+ 9. Intellectual Property Rights. . . . . . . . . . . . . . . . . 16
+ 10. References. . . . . . . . . . . . . . . . . . . . . . . . . . 17
+ 10.1. Normative References. . . . . . . . . . . . . . . . . . 17
+ 10.2. Informative References. . . . . . . . . . . . . . . . . 17
+ 11. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 18
+ 12. Author's Address. . . . . . . . . . . . . . . . . . . . . . . 18
+ 13. Full Copyright Statement. . . . . . . . . . . . . . . . . . . 19
+
+
+
+
+
+
+
+
+
+
+
+
+Siemborski Experimental [Page 2]
+
+RFC 3656 MUPDATE Distributed Mailbox Database Protocol December 2003
+
+
+1. Introduction
+
+ In order to support an architecture where there are multiple [IMAP,
+ POP3] servers sharing a common mailbox database, it is necessary to
+ be able to provide atomic mailbox operations, as well as offer
+ sufficient guarantees about database consistency.
+
+ The primary goal of the MUPDATE protocol is to be simple to implement
+ yet allow for database consistency between participants.
+
+ The key words "MUST, "MUST NOT", "REQUIRED", "SHOULD", "SHOULD NOT",
+ "RECOMMENDED", and "MAY" in this document are to be interpreted as
+ defined in BCP 14, RFC 2119 [KEYWORDS].
+
+ In examples, "C:" and "S:" indicate lines sent by the client and
+ server respectively.
+
+2. Protocol Overview
+
+ The MUPDATE protocol assumes a reliable data stream such as a TCP
+ network connection. IANA has registered port 3905 with a short name
+ of "mupdate" for this purpose.
+
+ In the current implementation of the MUPDATE protocol there are three
+ types of participants: a single master server, slave (or replica)
+ servers, and clients. The master server maintains an authoritative
+ copy of the mailbox database. Slave servers connect to the MUPDATE
+ master server as clients, and function as replicas from the point of
+ view of end clients. End clients may connect to either the master or
+ any slave and perform searches against the database, however
+ operations that change the database can only be performed against the
+ master. For the purposes of protocol discussion we will consider a
+ slave's connection to the master identical to that of any other
+ client.
+
+ After connection, all commands from a client to server must have an
+ associated unique tag which is an alphanumeric string. Commands MAY
+ be pipelined from the client to the server (that is, the client need
+ not wait for the response before sending the next command). The
+ server MUST execute the commands in the order they were received,
+ however.
+
+ If the server supports an inactivity login timeout, it MUST be at
+ least 15 minutes.
+
+
+
+
+
+
+
+Siemborski Experimental [Page 3]
+
+RFC 3656 MUPDATE Distributed Mailbox Database Protocol December 2003
+
+
+ MUPDATE uses data formats similar to those used in [ACAP]. That is,
+ atoms and strings. All commands and tags in the protocol are
+ transmitted as atoms. All other data is considered to a string, and
+ must be quoted or transmitted as a literal.
+
+ Outside of a literal, both clients and servers MUST support line
+ lengths of at least 1024 octets (including the trailing CR and LF
+ characters). If a line of a longer length must be transmitted,
+ implementations MUST make use of literals to do so.
+
+2.1. Atoms
+
+ An atom consists of one or more alphanumeric characters. Atoms MUST
+ be less than 15 octets in length.
+
+2.2. Strings
+
+ As in [ACAP], a string may be either literal or a quoted string. A
+ literal is a sequence of zero or more octets (including CR and LF),
+ prefix-quoted with an octet count in the form of an open brace ("{"),
+ the number of octets, an optional plus sign to indicate that the data
+ follows immediately (a non-synchronized literal), a close brace
+ ("}"), and a CRLF sequence. If the plus sign is omitted (a
+ synchronized literal), then the receiving side MUST send a "+ go
+ ahead" response, and the sending side MUST wait for this response.
+ Servers MUST support literals of atleast 4096 octets.
+
+ Strings that are sent from server to client SHOULD NOT be in the
+ synchronized literal format.
+
+ A quoted string is a sequence of zero or more 7-bit characters,
+ excluding CR, LF, and the double quote (<">), with double quote
+ characters at each end.
+
+ The empty string is represented as either "" (a quoted string with
+ zero characters between double quotes) or as {0} followed by CRLF (a
+ literal with an octet count of 0).
+
+3. Server Responses
+
+ Every client command in the MUPDATE protocol may receive one or more
+ tagged responses from the server. Each response is preceded by the
+ same tag as the command that elicited the response from the server.
+
+
+
+
+
+
+
+
+Siemborski Experimental [Page 4]
+
+RFC 3656 MUPDATE Distributed Mailbox Database Protocol December 2003
+
+
+3.1. Response: OK
+
+ A tagged OK response indicates that the operation completed
+ successfully. There is a mandatory implementation-defined string
+ after the OK response. This response also indicates the beginning of
+ the streaming update mode when given in response to an UPDATE
+ command.
+
+ Example:
+
+C: N01 NOOP
+S: N01 OK "NOOP Complete"
+
+3.2. Response: NO
+
+ A tagged NO response indicates that the operation was explicitly
+ denied by the server or otherwise failed. There is a mandatory
+ implementation-defined string after the NO response that SHOULD
+ explain the reason for denial.
+
+ Example:
+
+C: A01 AUTHENTICATE "PLAIN"
+S: A01 NO "PLAIN is not a supported SASL mechanism"
+
+3.3. Response: BAD
+
+ A tagged BAD response indicates that the command from the client
+ could not be parsed or understood. There is a mandatory
+ implementation-defined string after the BAD response to provide
+ additional information about the error. Note that untagged BAD
+ responses are allowed if it is unclear what the tag for a given
+ command is (for example, if a blank line is received by the mupdate
+ server, it can generate an untagged BAD response). In the case of an
+ untagged response, the tag should be replaced with a "*".
+
+ Example:
+
+C: C01 SELECT "INBOX"
+S: C01 BAD "This is not an IMAP server"
+C:
+S: * BAD "Need Command"
+
+
+
+
+
+
+
+
+
+Siemborski Experimental [Page 5]
+
+RFC 3656 MUPDATE Distributed Mailbox Database Protocol December 2003
+
+
+3.4. Response: BYE
+
+ A tagged BYE response indicates that the server has decided to close
+ the connection. There is a mandatory implementation-defined string
+ after the BYE response that SHOULD explain the reason for closing the
+ connection. The server MUST close the connection immediately after
+ transmitting the BYE response.
+
+ Example:
+
+C: L01 LOGOUT
+S: L01 BYE "User Logged Out"
+
+3.5. Response: RESERVE
+
+ A tagged RESERVE response may only be given in response to a FIND,
+ LIST, or UPDATE command. It includes two parameters: the name of the
+ mailbox that is being reserved (in mUTF-7 encoding, as specified in
+ [IMAP]) and a location string whose contents is defined by the
+ clients that are using the database, though it is RECOMMENDED that
+ the format of this string be the hostname of the server which is
+ storing the mailbox.
+
+ This response indicates that the given name is no longer available in
+ the namespace, though it does not indicate that the given mailbox is
+ available to clients at the current time.
+
+ Example:
+
+S: U01 RESERVE "internet.bugtraq" "mail2.example.org"
+
+3.6. Response: MAILBOX
+
+ A tagged MAILBOX response may only be given in response to a FIND,
+ LIST, or UPDATE command. It includes three parameters: the name of
+ the mailbox, a location string (as with RESERVE), and a client-
+ defined string that specifies the IMAP ACL [IMAP-ACL] of the mailbox.
+ This message indicates that the given mailbox is ready to be accessed
+ by clients.
+
+ Example:
+
+S: U01 MAILBOX "internet.bugtraq" "mail2.example.org" "anyone rls"
+
+
+
+
+
+
+
+
+Siemborski Experimental [Page 6]
+
+RFC 3656 MUPDATE Distributed Mailbox Database Protocol December 2003
+
+
+3.7. Response: DELETE
+
+ A tagged DELETE response may only be given in response to an UPDATE
+ command, and MUST NOT be given before the OK response to the UPDATE
+ command is given. It contains a single parameter, that of the
+ mailbox that should be deleted from the slave's database. This
+ response indicates that the given mailbox no longer exists in the
+ namespace of the database, and may be given for any mailbox name,
+ active, reserved, or nonexistent. (Though implementations SHOULD NOT
+ issue DELETE responses for nonexistent mailboxes).
+
+ Example:
+
+S: U01 DELETE "user.rjs3.sent-mail-jan-2002"
+
+3.8. Server Capability Response
+
+ Upon connection of the client to the server, and directly following a
+ successful STARTTLS command, the server MUST issue a capabilities
+ banner, of the following format:
+
+ The banner MUST contain a line that begins with "* AUTH" and contain
+ a space-separated list of SASL mechanisms that the server will accept
+ for authentication. The mechanism names are transmitted as atoms.
+ Servers MAY advertise no available mechanisms (to indicate that
+ STARTTLS must be completed before authentication may occur). If
+ STARTTLS is not supported by the server, then the line MUST contain
+ at least one mechanism.
+
+ If the banner is being issued without a TLS layer, and the server
+ supports the STARTTLS command, the banner MUST contain the line "*
+ STARTTLS". If the banner is being issued under a TLS layer (or the
+ server does not support STARTTLS), the banner MUST NOT contain this
+ line.
+
+ The last line of the banner MUST start with "* OK MUPDATE" and be
+ followed by four strings: the server's hostname, an implementation-
+ defined string giving the name of the implementation, an
+ implementation-defined string giving the version of the
+ implementation, and a string that indicates if the server is a master
+ or a slave. The master/slave indication MUST be either "(master)" or
+ an MUPDATE URL that defines where the master can be contacted.
+
+ Any unrecognized responses before the "* OK MUPDATE" response MUST be
+ ignored by the client.
+
+
+
+
+
+
+Siemborski Experimental [Page 7]
+
+RFC 3656 MUPDATE Distributed Mailbox Database Protocol December 2003
+
+
+ Example:
+
+S: * AUTH KERBEROS_V4 GSSAPI
+S: * STARTTLS
+S: * OK MUPDATE "mupdate.example.org" "Cyrus" "v2.1.2" "(master)"
+
+4. Client Commands
+
+ The following are valid commands that a client may send to the
+ MUPDATE server: AUTHENTICATE, ACTIVATE, DEACTIVATE, DELETE, FIND,
+ LIST, LOGOUT, NOOP, RESERVE, STARTTLS, and UPDATE.
+
+ Before a successful AUTHENTICATE command has occurred, the server
+ MUST NOT accept any commands except for AUTHENTICATE, STARTTLS, and
+ LOGOUT (and SHOULD reply with a NO response for all other commands).
+
+4.1. Command: ACTIVATE
+
+ The ACTIVATE command has 3 parameters: the mailbox name, its
+ location, and its ACL. This command MUST NOT not be issued to a
+ slave server.
+
+ This command can also be used to update the ACL or location
+ information of a mailbox. Note that it is not a requirement for a
+ mailbox to be reserved (or even exist in the database) for an
+ ACTIVATE command to succeed, implementations MUST allow this behavior
+ as it facilitates synchronization of the database with the current
+ state of the mailboxes.
+
+4.2. Command: AUTHENTICATE
+
+ The AUTHENTICATE command initiates a [SASL] negotiation session
+ between the client and the server. It has two parameters. The first
+ parameter is mandatory, and is a string indicating the desired [SASL]
+ mechanism. The second is a string containing an optional BASE64
+ encoded (as defined in section 6.8 of [MIME]) client first send.
+
+ All of the remaining SASL blobs that are sent MUST be sent across the
+ wire must be in BASE64 encoded format, and followed by a CR and LF
+ combination. They MUST NOT be encoded as strings.
+
+ Clients may cancel authentication by sending a * followed by a CR and
+ LF.
+
+ The [SASL] service name for the MUPDATE protocol is "mupdate".
+ Implementations are REQUIRED to implement the GSSAPI [SASL]
+ mechanism, though they SHOULD implement as many mechanisms as
+ possible.
+
+
+
+Siemborski Experimental [Page 8]
+
+RFC 3656 MUPDATE Distributed Mailbox Database Protocol December 2003
+
+
+ If a security layer is negotiated, it should be used directly
+ following the CR and LF combination at the end of the server's OK
+ response (i.e., beginning with the client's next command) Only one
+ successful AUTHENTICATE command may be issued per session.
+
+4.3. Command: DEACTIVATE
+
+ The DEACTIVATE command takes two parameters, the mailbox name and
+ location data. The mailbox MUST already exist and be activated on
+ the MUPDATE server. If the server responds OK, then the mailbox name
+ has been moved to the RESERVE state. If the server responds NO, then
+ the mailbox name has not been moved (for example, the mailbox was not
+ already active). Any ACL information that is known about the mailbox
+ MAY be lost when a DEACTIVATE succeeds. This command MUST NOT be
+ issued to a slave.
+
+ Example:
+
+C: A01 DEACTIVATE "user.rjs3.new" "mail3.example.org!u4"
+S: A01 OK "Mailbox Reserved."
+
+4.4. Command: DELETE
+
+ The DELETE command takes only a single parameter, the mailbox name to
+ be removed from the database's namespace. The server SHOULD give a
+ NO response if the mailbox does not exist. This command MUST NOT be
+ issued to a slave server.
+
+4.5. Command: FIND
+
+ The FIND command takes a single parameter, a mailbox name. The
+ server then responds with the current record for the given mailbox,
+ if any, and an OK response.
+
+ Example (mailbox does not exist):
+
+C: F01 FIND "user.rjs3.xyzzy"
+S: F01 OK "Search Complete"
+
+ Example (mailbox is reserved):
+
+C: F01 FIND "user.rjs3"
+S: F01 RESERVE "user.rjs3" "mail4.example.org"
+S: F01 OK "Search Complete"
+
+
+
+
+
+
+
+Siemborski Experimental [Page 9]
+
+RFC 3656 MUPDATE Distributed Mailbox Database Protocol December 2003
+
+
+4.6. Command: LIST
+
+ The LIST command is similar to running FIND across the entire
+ database. The LIST command takes a single optional parameter, which
+ is a prefix to try to match against the location field of the
+ records. Without the parameter, LIST returns every record in the
+ database.
+
+ For each mailbox that matches, either a MAILBOX or a RESERVE response
+ (as applicable) is sent to the client. When all responses are
+ complete, an OK response is issued.
+
+ Example:
+
+C: L01 LIST
+S: L01 RESERVE "user.rjs3" "mail4.example.org!u2"
+S: L01 MAILBOX "user.leg" "mail2.example.org!u1" "leg lrswipcda"
+S: L01 OK "List Complete"
+C: L02 LIST "mail4.example.org!"
+S: L02 RESERVE "user.rjs3" "mail4.example.org!u2"
+S: L02 OK "List Complete"
+
+4.7. Command: LOGOUT
+
+ The LOGOUT command tells the server to close the connection. Its
+ only valid response is the BYE response. The LOGOUT command takes no
+ parameters.
+
+4.8. Command: NOOP
+
+ The NOOP command takes no parameters. Provided the client is
+ authenticated, its only acceptable response is an OK. Any idle
+ timeouts that the server may have on the connection SHOULD be reset
+ upon receipt of this command.
+
+ If this command is issued after an UPDATE command has been issued,
+ then the OK response also indicates that all pending database updates
+ have been sent to the client. That is, the slave can guarantee that
+ its local database is up to date as of a certain time by issuing a
+ NOOP and waiting for the OK. The OK MUST NOT return until all
+ updates that were pending at the time of the NOOP have been sent.
+
+4.9. Command: RESERVE
+
+ The RESERVE command takes two parameters (just like the RESERVE
+ response), the mailbox name to reserve and location data. If the
+ server responds OK, then the mailbox name has been reserved. If the
+ server responds NO, then the mailbox name has not been reserved (for
+
+
+
+Siemborski Experimental [Page 10]
+
+RFC 3656 MUPDATE Distributed Mailbox Database Protocol December 2003
+
+
+ example, another server has reserved it already). This command MUST
+ NOT be issued to a slave.
+
+ The typical sequence for mailbox creation is:
+
+C: R01 RESERVE "user.rjs3.new" "mail3.example.org!u4"
+S: R01 OK "Mailbox Reserved."
+<client does local mailbox create operations>
+C: A01 ACTIVATE "user.rjs3.new" "mail3.example.org!u4" "rjs3 lrswipcda"
+S: A01 OK "Mailbox Activated."
+
+4.10. Command: STARTTLS
+
+ The STARTTLS command requests the commencement of a [TLS]
+ negotiation. The negotiation begins immediately after the CRLF in
+ the OK response. After a client issues a STARTTLS command, it MUST
+ NOT issue further commands until a server response is seen and the
+ [TLS] negotiation is complete.
+
+ The STARTTLS command is only valid in non-authenticated state. The
+ server remains in non-authenticated state, even if client credentials
+ are supplied during the [TLS] negotiation. The [SASL] EXTERNAL
+ mechanism MAY be used to authenticate once [TLS] client credentials
+ are successfully exchanged. Note that servers are not required to
+ support the EXTERNAL mechanism.
+
+ After the [TLS] layer is established, the server MUST re-issue the
+ initial response banner (see Section 3.8). This is necessary to
+ protect against man-in-the-middle attacks which alter the
+ capabilities list prior to STARTTLS, as well as to advertise any new
+ SASL mechanisms (or other capabilities) that may be available under
+ the layer. The client MUST discard cached capability information and
+ replace it with the new information.
+
+ After the a successful STARTTLS command, the server SHOULD return a
+ NO response to additional STARTTLS commands.
+
+ Servers MAY choose to not implement STARTTLS. In this case, they
+ MUST NOT advertise STARTTLS in their capabilities banner, and SHOULD
+ return a BAD response to the STARTTLS command, if it is issued.
+
+ Example:
+
+C: S01 STARTTLS
+S: S01 OK "Begin TLS negotiation now"
+<TLS negotiation, further commands are under TLS layer>
+S: * AUTH KERBEROS_V4 GSSAPI PLAIN
+S: * OK MUPDATE "mupdate.example.org" "Cyrus" "v2.1.2" "(master)"
+
+
+
+Siemborski Experimental [Page 11]
+
+RFC 3656 MUPDATE Distributed Mailbox Database Protocol December 2003
+
+
+4.11. Command: UPDATE
+
+ The UPDATE command is how a slave initializes an update stream from
+ the master (though it is also valid to issue this command to a
+ slave). In response to the command, the server returns a list of all
+ mailboxes in its database (the same results as a parameterless LIST
+ command) followed by an OK response. From this point forward,
+ whenever an update occurs to the master database, it MUST stream the
+ update to the slave within 30 seconds. That is, it will send
+ RESERVE, MAILBOX, or DELETE responses as they are applicable.
+
+ After a client has issued an UPDATE command, it may only issue NOOP
+ and LOGOUT commands for the remainder of the session.
+
+ Example:
+
+C: U01 UPDATE
+S: U01 MAILBOX "user.leg" "mail2.example.org!u1" "leg lrswipcda"
+S: U01 MAILBOX "user.rjs3" "mail3.example.org!u4" "rjs3 lrswipcda"
+S: U01 RESERVE "internet.bugtraq" "mail1.example.org!u5" "anyone lrs"
+S: U01 OK "Streaming Begins"
+<some time goes by, and another client creates a new mailbox>
+S: U01 RESERVE "user.leg.new" "mail2.example.org!u1"
+<some more time passes, and the create succeeds>
+S: U01 MAILBOX "user.leg.new" "mail2.example.org!u1" "leg lrswipcda"
+<much more time passes, and the slave decides to send a NOOP to reset
+its inactivity timer>
+C: N01 NOOP
+S: U01 DELETE "user.leg.new"
+S: N01 OK "NOOP Complete"
+
+5. MUPDATE Formal Syntax
+
+ The following syntax specification uses the Augmented Backus-Naur
+ Form (ABNF) notation as specified in [ABNF]. This uses the ABNF core
+ rules as specified in Appendix A of [ABNF].
+
+ Except as noted otherwise, all alphabetic characters are case-
+ insensitive. The use of upper or lower case characters to define
+ token strings is for editorial clarity only. Implementations MUST
+ accept these strings in a case-insensitive fashion.
+
+ Note that this specification also uses some terminals from section 8
+ of [ACAP].
+
+ cmd-activate = "ACTIVATE" SP string SP string SP string
+
+ cmd-authenticate = "AUTHENTICATE" SP sasl-mech [ SP string ]
+
+
+
+Siemborski Experimental [Page 12]
+
+RFC 3656 MUPDATE Distributed Mailbox Database Protocol December 2003
+
+
+ cmd-delete = "DELETE" SP string
+
+ cmd-find = "FIND" SP string
+
+ cmd-list = "LIST" [ SP string ]
+
+ cmd-logout = "LOGOUT"
+
+ cmd-noop = "NOOP"
+
+ cmd-reserve = "RESERVE" SP string SP string
+
+ cmd-starttls = "STARTTLS"
+
+ cmd-update = "UPDATE"
+
+ command = tag SP command-type CRLF
+
+ command-type = cmd-activate / cmd-authenticate / cmd-delete /
+ cmd-find / cmd-list / cmd-logout / cmd-noop /
+ cmd-reserve / cmd-starttls / cmd-update
+
+ response = tag SP response-type CRLF
+
+ response-type = rsp-ok / rsp-no / rsp-bad / rsp-bye / rsp-mailbox /
+ rsp-reserve / rsp-delete
+
+ rsp-bad = "BAD" SP string
+
+ rsp-bye = "BYE" SP string
+
+ rsp-mailbox = "MAILBOX" SP string SP string SP string
+
+ rsp-no = "NO" SP string
+
+ rsp-ok = "OK" SP string
+
+ rsp-reserve = "RESERVE" SP string SP string
+
+ rsp-delete = "DELETE" SP string
+
+ sasl-mech = 1*ATOM-CHAR
+ ; ATOM-CHAR is defined in [ACAP]
+
+ string = quoted / literal
+ ; quoted and literal are defined in [ACAP]
+
+
+
+
+
+Siemborski Experimental [Page 13]
+
+RFC 3656 MUPDATE Distributed Mailbox Database Protocol December 2003
+
+
+ tag = 1*ATOM-CHAR
+ ; ATOM-CHAR is defined in [ACAP]
+
+6. MUPDATE URL Scheme
+
+ This document defines the a URL scheme for the purposes of
+ referencing MUPDATE resources, according to the requirements in
+ [RFC2717]. This includes both MUPDATE servers as a whole, along with
+ individual mailbox entries on a given MUPDATE server.
+
+ There is no MIME type associated with these resources. It is
+ intended that a URL consumer would either retrieve the MUPDATE record
+ in question, or simply connect to the MUPDATE server running on the
+ specified host. Note that the consumer will need to have
+ authentication credentials for the specified host.
+
+ The MUPDATE URL scheme is similar to the IMAP URL scheme [IMAP-URL].
+ However, it only takes one of two possible forms:
+
+ mupdate://<iserver>/
+ mupdate://<iserver>/<mailbox>
+
+ The first form refers to a MUPDATE server as a whole, the second form
+ indicates both the server and a mailbox to run a FIND against once
+ authenticated to the server. Note that part of <iserver> may include
+ username and authentication information along with a hostname and
+ port.
+
+6.1. MUPDATE URL Scheme Registration Form
+
+ URL scheme name: "mupdate"
+
+ URL scheme syntax:
+
+ This defines the MUPDATE URL Scheme in [ABNF]. Terminals from the
+ BNF of IMAP URLs [IMAP-URL] are also used.
+
+ mupdateurl = "mupdate://" iserver "/" [ enc_mailbox ]
+ ; iserver and enc_mailbox are as defined in [IMAP-URL]
+
+ Character encoding considerations:
+
+ Identical to those described in [IMAP-URL] for the appropriate
+ terminals.
+
+
+
+
+
+
+
+Siemborski Experimental [Page 14]
+
+RFC 3656 MUPDATE Distributed Mailbox Database Protocol December 2003
+
+
+ Intended Usage:
+
+ The form of the URL without an associated mailbox is intended to
+ designate a MUPDATE server only. If a mailbox name is included in
+ the URL, then the consumer is expected to execute a FIND command
+ for that mailbox on the specified server.
+
+ Applications and/or protocols which use this URL scheme name:
+
+ The protocol described in this document.
+
+ Interoperability Considerations:
+
+ None.
+
+ Security Considerations:
+
+ Users of the MUPDATE URL Scheme should review the security
+ considerations that are discussed in [IMAP-URL]. In particular,
+ the consequences of including authentication mechanism information
+ in a URL should be reviewed.
+
+ Relevant Publications:
+
+ This document and [IMAP-URL].
+
+ Author, Change Controller, and Contact for Further Information:
+
+ Author of this document.
+
+7. Security Considerations
+
+ While no unauthenticated users may make modifications or even perform
+ searches on the database, it is important to note that this
+ specification assumes no protections of any type for authenticated
+ users.
+
+ All authenticated users have complete access to the database. For
+ this reason it is important to ensure that accounts that are making
+ use of the database are well secured.
+
+ A more secure deployment might have all read only access go through a
+ slave, and only have accounts which need write access use the master.
+ This has the disadvantage of a marginally longer time for updates to
+ reach the clients.
+
+
+
+
+
+
+Siemborski Experimental [Page 15]
+
+RFC 3656 MUPDATE Distributed Mailbox Database Protocol December 2003
+
+
+ The protocol assumes that all authenticated users are cooperating to
+ maintain atomic operations. Therefore, all new mailboxes SHOULD be
+ RESERVEd before they are ACTIVATEd, despite the fact that the
+ protocol does not require this, and it is therefore possible for a
+ set of participants which do not obey the provided locking to create
+ an inconsistent database. RESERVEing the mailbox first is not
+ required to perform an activate because this behavior simplifies
+ synchronization with the actual location of the mailboxes.
+
+8. IANA Considerations
+
+ The IANA has assigned TCP port number 3905 to "mupdate".
+
+ The IANA has registered a URL scheme for the MUPDATE protocol, as
+ defined in section 6.1 of this document.
+
+ IANA has registered a GSSAPI service name of "mupdate" for the
+ MUPDATE protocol in the registry maintained at:
+
+ http://www.iana.org/assignments/gssapi-service-names
+
+9. Intellectual Property Rights
+
+ The IETF takes no position regarding the validity or scope of any
+ intellectual property or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; neither does it represent that it
+ has made any effort to identify any such rights. Information on the
+ IETF's procedures with respect to rights in standards-track and
+ standards-related documentation can be found in BCP-11. Copies of
+ claims of rights made available for publication and any assurances of
+ licenses to be made available, or the result of an attempt made to
+ obtain a general license or permission for the use of such
+ proprietary rights by implementors or users of this specification can
+ be obtained from the IETF Secretariat.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights which may cover technology that may be required to practice
+ this standard. Please address the information to the IETF Executive
+ Director.
+
+
+
+
+
+
+
+
+
+Siemborski Experimental [Page 16]
+
+RFC 3656 MUPDATE Distributed Mailbox Database Protocol December 2003
+
+
+10. References
+
+10.1. Normative References
+
+ [KEYWORDS] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [IMAP] Crispin, M., "Internet Message Access Protocol - Version
+ 4", RFC 3501, March 2003.
+
+ [ABNF] Crocker, D., Ed. and P. Overell, "Augmented BNF for
+ Syntax Specifications: ABNF", RFC 2234, November 1997.
+
+ [MIME] Freed, N. and N. Bornstein, "Multipurpose Internet Mail
+ Extensions (MIME) Part One: Format of Internet Message
+ Bodies", RFC 2045, November 1996.
+
+ [IMAP-ACL] Myers, J., "IMAP4 ACL extension", RFC 2086, January 1997.
+
+ [SASL] Myers, J., "Simple Authentication and Security Layer
+ (SASL)", RFC 2222, October 1997.
+
+ [IMAP-URL] Newman, C., "IMAP URL Scheme", RFC 2192, September 1997.
+
+ [ACAP] Newman, C. and J. Myers, "ACAP -- Application
+ Configuration Access Protocol", RFC 2244, November 1997.
+
+ [TLS] Dierks, T. and C. Allen, "The TLS Protocol Version 1.0",
+ RFC 2246, January 1999.
+
+10.2. Informative References
+
+ [POP3] Myers, J. and M. Rose, "Post Office Protocol - Version
+ 3", STD 53, RFC 1939, May 1996.
+
+ [RFC2717] Petke, R. and I. King, "Registration Procedures for URL
+ Scheme Names", BCP 35, RFC 2717, November 1999.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Siemborski Experimental [Page 17]
+
+RFC 3656 MUPDATE Distributed Mailbox Database Protocol December 2003
+
+
+11. Acknowledgments
+
+ Lawrence Greenfield and Ken Murchison, for a great deal of input on
+ both the protocol and the text of the documents.
+
+12. Author's Address
+
+ Robert Siemborski
+ Carnegie Mellon, Andrew Systems Group
+ Cyert Hall 207
+ 5000 Forbes Avenue
+ Pittsburgh, PA 15213
+
+ Phone: (412) 268-7456
+ EMail: rjs3+@andrew.cmu.edu
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Siemborski Experimental [Page 18]
+
+RFC 3656 MUPDATE Distributed Mailbox Database Protocol December 2003
+
+
+13. Full Copyright Statement
+
+ Copyright (C) The Internet Society (2003). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assignees.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Siemborski Experimental [Page 19]
+
diff --git a/imap/docs/rfc/rfc3691.txt b/imap/docs/rfc/rfc3691.txt
new file mode 100644
index 00000000..2f4e9b44
--- /dev/null
+++ b/imap/docs/rfc/rfc3691.txt
@@ -0,0 +1,283 @@
+
+
+
+
+
+
+Network Working Group A. Melnikov
+Request for Comments: 3691 Isode Ltd.
+Category: Standards Track February 2004
+
+
+ Internet Message Access Protocol (IMAP) UNSELECT command
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2004). All Rights Reserved.
+
+Abstract
+
+ This document defines an UNSELECT command that can be used to close
+ the current mailbox in an Internet Message Access Protocol - version
+ 4 (IMAP4) session without expunging it. Certain types of IMAP
+ clients need to release resources associated with the selected
+ mailbox without selecting a different mailbox. While IMAP4 provides
+ this functionality (via a SELECT command with a nonexistent mailbox
+ name or reselecting the same mailbox with EXAMINE command), a more
+ clean solution is desirable.
+
+Table of Contents
+
+ 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 2
+ 2. UNSELECT command . . . . . . . . . . . . . . . . . . . . . . . 2
+ 3. Security Considerations. . . . . . . . . . . . . . . . . . . . 3
+ 4. Formal Syntax. . . . . . . . . . . . . . . . . . . . . . . . . 3
+ 5. IANA Considerations. . . . . . . . . . . . . . . . . . . . . . 3
+ 6. Acknowledgments. . . . . . . . . . . . . . . . . . . . . . . . 3
+ 7. Normative References . . . . . . . . . . . . . . . . . . . . . 4
+ 8. Author's Address . . . . . . . . . . . . . . . . . . . . . . . 4
+ 9. Full Copyright Statement . . . . . . . . . . . . . . . . . . . 5
+
+
+
+
+
+
+
+
+
+
+Melnikov Standards Track [Page 1]
+
+RFC 3691 IMAP UNSELECT command February 2004
+
+
+1. Introduction
+
+ Certain types of IMAP clients need to release resources associated
+ with the selected mailbox without selecting a different mailbox.
+ While [IMAP4] provides this functionality (via a SELECT command with
+ a nonexistent mailbox name or reselecting the same mailbox with
+ EXAMINE command), a more clean solution is desirable.
+
+ [IMAP4] defines the CLOSE command that closes the selected mailbox as
+ well as permanently removes all messages with the \Deleted flag set.
+
+ However [IMAP4] lacks a command that simply closes the mailbox
+ without expunging it. This document defines the UNSELECT command for
+ this purpose.
+
+ A server which supports this extension indicates this with a
+ capability name of "UNSELECT".
+
+ "C:" and "S:" in examples show lines sent by the client and server
+ respectively.
+
+ The keywords "MUST", "MUST NOT", "SHOULD", "SHOULD NOT", and "MAY" in
+ this document when typed in uppercase are to be interpreted as
+ defined in "Key words for use in RFCs to Indicate Requirement Levels"
+ [KEYWORDS].
+
+2. UNSELECT Command
+
+ Arguments: none
+
+ Responses: no specific responses for this command
+
+ Result: OK - unselect completed, now in authenticated state
+ BAD - no mailbox selected, or argument supplied but
+ none permitted
+
+ The UNSELECT command frees server's resources associated with the
+ selected mailbox and returns the server to the authenticated
+ state. This command performs the same actions as CLOSE, except
+ that no messages are permanently removed from the currently
+ selected mailbox.
+
+ Example: C: A341 UNSELECT
+ S: A341 OK Unselect completed
+
+
+
+
+
+
+
+Melnikov Standards Track [Page 2]
+
+RFC 3691 IMAP UNSELECT command February 2004
+
+
+3. Security Considerations
+
+ It is believed that this extension doesn't raise any additional
+ security concerns not already discussed in [IMAP4].
+
+4. Formal Syntax
+
+ The following syntax specification uses the Augmented Backus-Naur
+ Form (ABNF) notation as specified in [ABNF]. Non-terminals
+ referenced but not defined below are as defined by [IMAP4].
+
+ Except as noted otherwise, all alphabetic characters are case-
+ insensitive. The use of upper or lower case characters to define
+ token strings is for editorial clarity only. Implementations MUST
+ accept these strings in a case-insensitive fashion.
+
+ command-select /= "UNSELECT"
+
+5. IANA Considerations
+
+ IMAP4 capabilities are registered by publishing a standards track or
+ IESG approved experimental RFC. The registry is currently located
+ at:
+
+ http://www.iana.org/assignments/imap4-capabilities
+
+ This document defines the UNSELECT IMAP capabilities. IANA has added
+ this capability to the registry.
+
+6. Acknowledgments
+
+ UNSELECT command was originally implemented by Tim Showalter in Cyrus
+ IMAP server.
+
+ Also, the author of the document would like to thank Vladimir Butenko
+ and Mark Crispin for reminding that UNSELECT has to be documented.
+ Also thanks to Simon Josefsson for pointing out that there are
+ multiple ways to implement UNSELECT.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Melnikov Standards Track [Page 3]
+
+RFC 3691 IMAP UNSELECT command February 2004
+
+
+7. Normative References
+
+ [KEYWORDS] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [IMAP4] Crispin, M., "Internet Message Access Protocol - Version
+ 4rev1", RFC 3501, March 2003.
+
+ [ABNF] Crocker, D., Ed. and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", RFC 2234, November 1997.
+
+8. Author's Address
+
+ Alexey Melnikov
+ Isode Limited
+ 5 Castle Business Village
+ Hampton, Middlesex TW12 2BX
+
+ EMail: Alexey.Melnikov@isode.com
+ URI: http://www.melnikov.ca/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Melnikov Standards Track [Page 4]
+
+RFC 3691 IMAP UNSELECT command February 2004
+
+
+9. Full Copyright Statement
+
+ Copyright (C) The Internet Society (2004). This document is subject
+ to the rights, licenses and restrictions contained in BCP 78 and
+ except as set forth therein, the authors retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE
+ REPRESENTS OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE
+ INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF
+ THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed
+ to pertain to the implementation or use of the technology
+ described in this document or the extent to which any license
+ under such rights might or might not be available; nor does it
+ represent that it has made any independent effort to identify any
+ such rights. Information on the procedures with respect to
+ rights in RFC documents can be found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use
+ of such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository
+ at http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention
+ any copyrights, patents or patent applications, or other
+ proprietary rights that may cover technology that may be required
+ to implement this standard. Please address the information to the
+ IETF at ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+
+
+Melnikov Standards Track [Page 5]
+
diff --git a/imap/docs/rfc/rfc4314.txt b/imap/docs/rfc/rfc4314.txt
new file mode 100644
index 00000000..e73a56f2
--- /dev/null
+++ b/imap/docs/rfc/rfc4314.txt
@@ -0,0 +1,1515 @@
+
+
+
+
+
+
+Network Working Group A. Melnikov
+Request for Comments: 4314 Isode Ltd.
+Obsoletes: 2086 December 2005
+Category: Standards Track
+
+
+ IMAP4 Access Control List (ACL) Extension
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2005).
+
+Abstract
+
+ The Access Control List (ACL) extension (RFC 2086) of the Internet
+ Message Access Protocol (IMAP) permits mailbox access control lists
+ to be retrieved and manipulated through the IMAP protocol.
+
+ This document is a revision of RFC 2086. It defines several new
+ access control rights and clarifies which rights are required for
+ different IMAP commands.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Melnikov Standards Track [Page 1]
+
+RFC 4314 IMAP ACL December 2005
+
+
+Table of Contents
+
+ 1. Introduction and Overview .......................................3
+ 1.1. Conventions Used in This Document ..........................3
+ 2. Access Control ..................................................3
+ 2.1. Standard Rights ............................................5
+ 2.1.1. Obsolete Rights .....................................5
+ 2.2. Rights Defined in RFC 2086 .................................8
+ 3. Access control management commands and responses ................8
+ 3.1. SETACL Command .............................................8
+ 3.2. DELETEACL Command ..........................................9
+ 3.3. GETACL Command ............................................10
+ 3.4. LISTRIGHTS Command ........................................10
+ 3.5. MYRIGHTS Command ..........................................11
+ 3.6. ACL Response ..............................................11
+ 3.7. LISTRIGHTS Response .......................................12
+ 3.8. MYRIGHTS Response .........................................12
+ 4. Rights Required to Perform Different IMAP4rev1 Commands ........12
+ 5. Other Considerations ...........................................17
+ 5.1. Additional Requirements and Implementation Notes ..........17
+ 5.1.1. Servers ............................................17
+ 5.1.2. Clients ............................................18
+ 5.2. Mapping of ACL Rights to READ-WRITE and READ-ONLY
+ Response Codes ............................................19
+ 6. Security Considerations ........................................20
+ 7. Formal Syntax ..................................................21
+ 8. IANA Considerations ............................................22
+ 9. Internationalization Considerations ............................22
+ Appendix A. Changes since RFC 2086 ................................23
+ Appendix B. Compatibility with RFC 2086 ...........................24
+ Appendix C. Known Deficiencies ....................................24
+ Appendix D. Acknowledgements ......................................25
+ Normative References ..............................................25
+ Informative References ............................................25
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Melnikov Standards Track [Page 2]
+
+RFC 4314 IMAP ACL December 2005
+
+
+1. Introduction and Overview
+
+ The ACL (Access Control List) extension of the Internet Message
+ Access Protocol [IMAP4] permits mailbox access control lists to be
+ retrieved and manipulated through the IMAP protocol.
+
+ This document is a revision of RFC 2086 [RFC2086]. It tries to
+ clarify different ambiguities in RFC 2086, in particular, the use of
+ UTF-8 [UTF-8] in access identifiers, which rights are required for
+ different IMAP4 commands, and how READ-WRITE/READ-ONLY response codes
+ are related to ACL.
+
+1.1. Conventions Used in This Document
+
+ In examples, "C:" and "S:" indicate lines sent by the client and
+ server respectively.
+
+ In all examples "/" character is used as hierarchy separator.
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in RFC 2119 [KEYWORDS].
+
+ The phrase "ACL server" is just a shortcut for saying "IMAP server
+ that supports ACL extension as defined in this document".
+
+2. Access Control
+
+ The ACL extension is present in any IMAP4 implementation that returns
+ "ACL" as one of the supported capabilities to the CAPABILITY command.
+
+ A server implementation conformant to this document MUST also return
+ rights (see below) not defined in Section 2.2 in the "RIGHTS="
+ capability.
+
+ An access control list is a set of <access identifier,rights> pairs.
+ An ACL applies to a mailbox name.
+
+ Access identifier (or just "identifier") is a UTF-8 [UTF-8] string.
+ The identifier "anyone" is reserved to refer to the universal
+ identity (all authentications, including anonymous). All user name
+ strings accepted by the LOGIN or AUTHENTICATE commands to
+ authenticate to the IMAP server are reserved as identifiers for the
+ corresponding users. Identifiers starting with a dash ("-") are
+ reserved for "negative rights", described below. All other
+ identifier strings are interpreted in an implementation-defined
+ manner.
+
+
+
+
+Melnikov Standards Track [Page 3]
+
+RFC 4314 IMAP ACL December 2005
+
+
+ Rights is a string listing a (possibly empty) set of alphanumeric
+ characters, each character listing a set of operations that is being
+ controlled. Lowercase letters are reserved for "standard" rights,
+ listed in Section 2.1. (Note that for compatibility with deployed
+ clients and servers uppercase rights are not allowed.) The set of
+ standard rights can only be extended by a standards-track document.
+ Digits are reserved for implementation- or site-defined rights.
+
+ An implementation MAY tie rights together or MAY force rights to
+ always or never be granted to particular identifiers. For example,
+ in an implementation that uses UNIX mode bits, the rights "swite" are
+ tied, the "a" right is always granted to the owner of a mailbox and
+ is never granted to another user. If rights are tied in an
+ implementation, the implementation must be conservative in granting
+ rights in response to SETACL commands--unless all rights in a tied
+ set are specified, none of that set should be included in the ACL
+ entry for that identifier. A client can discover the set of rights
+ that may be granted to a given identifier in the ACL for a given
+ mailbox name by using the LISTRIGHTS command.
+
+ It is possible for multiple identifiers in an access control list to
+ apply to a given user. For example, an ACL may include rights to be
+ granted to the identifier matching the user, one or more
+ implementation-defined identifiers matching groups that include the
+ user, and/or the identifier "anyone". How these rights are combined
+ to determine the user's access is implementation defined. An
+ implementation may choose, for example, to use the union of the
+ rights granted to the applicable identifiers. An implementation may
+ instead choose, for example, to use only those rights granted to the
+ most specific identifier present in the ACL. A client can determine
+ the set of rights granted to the logged-in user for a given mailbox
+ name by using the MYRIGHTS command.
+
+ When an identifier in an ACL starts with a dash ("-"), that indicates
+ that associated rights are to be removed from the identifier prefixed
+ by the dash. This is referred to as a "negative right". This
+ differs from DELETEACL in that a negative right is added to the ACL
+ and is a part of the calculation of the rights.
+
+ Let's assume that an identifier "fred" refers to a user with login
+ "fred". If the identifier "-fred" is granted the "w" right, that
+ indicates that the "w" right is to be removed from users matching the
+ identifier "fred", even though the user "fred" might have the "w"
+ right as a consequence of some other identifier in the ACL. A
+ DELETEACL of "fred" simply deletes the identifier "fred" from the
+ ACL; it does not affect any rights that the user "fred" may get from
+ another entry in the ACL, in particular it doesn't affect rights
+ granted to the identifier "-fred".
+
+
+
+Melnikov Standards Track [Page 4]
+
+RFC 4314 IMAP ACL December 2005
+
+
+ Server implementations are not required to support "negative right"
+ identifiers.
+
+2.1. Standard Rights
+
+ The currently defined standard rights are (note that the list below
+ doesn't list all commands that use a particular right):
+
+ l - lookup (mailbox is visible to LIST/LSUB commands, SUBSCRIBE
+ mailbox)
+ r - read (SELECT the mailbox, perform STATUS)
+ s - keep seen/unseen information across sessions (set or clear
+ \SEEN flag via STORE, also set \SEEN during APPEND/COPY/
+ FETCH BODY[...])
+ w - write (set or clear flags other than \SEEN and \DELETED via
+ STORE, also set them during APPEND/COPY)
+ i - insert (perform APPEND, COPY into mailbox)
+ p - post (send mail to submission address for mailbox,
+ not enforced by IMAP4 itself)
+ k - create mailboxes (CREATE new sub-mailboxes in any
+ implementation-defined hierarchy, parent mailbox for the new
+ mailbox name in RENAME)
+ x - delete mailbox (DELETE mailbox, old mailbox name in RENAME)
+ t - delete messages (set or clear \DELETED flag via STORE, set
+ \DELETED flag during APPEND/COPY)
+ e - perform EXPUNGE and expunge as a part of CLOSE
+ a - administer (perform SETACL/DELETEACL/GETACL/LISTRIGHTS)
+
+2.1.1. Obsolete Rights
+
+ Due to ambiguity in RFC 2086, some existing RFC 2086 server
+ implementations use the "c" right to control the DELETE command.
+ Others chose to use the "d" right to control the DELETE command. For
+ the former group, let's define the "create" right as union of the "k"
+ and "x" rights, and the "delete" right as union of the "e" and "t"
+ rights. For the latter group, let's define the "create" rights as a
+ synonym to the "k" right, and the "delete" right as union of the "e",
+ "t", and "x" rights.
+
+ For compatibility with RFC 2086, this section defines two virtual
+ rights "d" and "c".
+
+ If a client includes the "d" right in a rights list, then it MUST be
+ treated as if the client had included every member of the "delete"
+ right. (It is not an error for a client to specify both the "d"
+ right and one or more members of the "delete" right, but the effect
+ is no different than if just the "d" right or all members of the
+ "delete" right had been specified.)
+
+
+
+Melnikov Standards Track [Page 5]
+
+RFC 4314 IMAP ACL December 2005
+
+
+ When any of the "delete" member rights is set in a list of rights,
+ the server MUST also include the "d" right when returning the list in
+ a MYRIGHTS or ACL response. This is to enable older clients
+ conforming to RFC 2086 to work with newer servers. (*)
+
+ Example: C: A001 SeTacl INBOX/Drafts David lrswida
+ S: A001 OK Setacl complete
+
+ The client has specified the "d" right in the SETACL command above
+ and it expands to "et" on the server:
+
+ C: A002 getacl INBOX/Drafts
+ S: * ACL INBOX Fred rwipslxcetda David lrswideta
+ S: A002 OK Getacl complete
+
+ If the identifier specified in the LISTRIGHTS command can be granted
+ any of the "delete" member rights on a mailbox, then the server MUST
+ include the "d" right in the corresponding LISTRIGHTS response. (*)
+ If the member rights aren't tied to non-member rights, then the "d"
+ right is returned by itself in the LISTRIGHTS response. If any of
+ the member rights needs to be tied to one (or more) non-member right,
+ then the "d" right and all of the member rights need to be tied to
+ the same non-member right(s) (**).
+
+ If a client includes the "c" right in a rights list, then it MUST be
+ treated as if the client had included every member of the "create"
+ right. (It is not an error for a client to specify both the "c"
+ right and one or more members of the "create" right, but the effect
+ is no different than if just the "c" right or all members of the
+ "create" right had been specified.)
+
+ When any of the "create" member rights is set in a list of rights,
+ the server MUST also include the "c" right when returning the list in
+ a MYRIGHTS or ACL response. This is to enable older clients
+ conforming to RFC 2086 to work with newer servers. (*)
+
+ Example: C: A003 Setacl INBOX/Drafts Byron lrswikda
+ S: A001 OK Setacl complete
+ C: A002 getAcl INBOX/Drafts
+ S: * ACL INBOX Fred rwipslxcetda Byron lrswikcdeta
+ S: A002 OK Getacl complete
+
+ The client has specified the "d" right in the SETACL command above
+ and it expands to "et" on the server: As the client has specified the
+ "k" right (which is a member of the "c" right), the server also
+ returns the "c" right.
+
+
+
+
+
+Melnikov Standards Track [Page 6]
+
+RFC 4314 IMAP ACL December 2005
+
+
+ If the identifier specified in the LISTRIGHTS command can be granted
+ any of the "create" member rights on a mailbox, then the server MUST
+ include the "c" right in the corresponding LISTRIGHTS response. (*)
+ If the member rights aren't tied to non-member rights, then the "c"
+ right is returned by itself in the LISTRIGHTS response. If any of
+ the member rights needs to be tied to one (or more) non-member right,
+ then the "c" right and all of the member rights need to be tied to
+ the same non-member right(s) (**).
+
+ Example: The server that ties the rights as follows:
+
+ lr s w i p k x t
+
+ and c=k
+
+ will return:
+
+ S: * LISTRIGHTS archive/imap anyone ""
+ lr s w i p k x t c d
+
+ Example: The server that ties the rights as follows:
+
+ lr s w i p k xte
+
+ and c=k
+
+ will return:
+
+ S: * LISTRIGHTS archive/imap anyone ""
+ lr s w i p k xte c d
+
+ Example: The server that ties the rights as follows:
+
+ lr s w i p k x te
+
+ and c=k
+
+ will return:
+
+ S: * LISTRIGHTS archive/imap anyone ""
+ lr s w i p k c x te d
+
+ Example: The server that ties the rights as follows:
+
+ lr swte i p k x
+
+ and c=kx
+
+
+
+
+Melnikov Standards Track [Page 7]
+
+RFC 4314 IMAP ACL December 2005
+
+
+ will return:
+
+ S: * LISTRIGHTS archive/imap anyone ""
+ lr swted i p k x c
+
+ (*) Clients conforming to this document MUST ignore the virtual "d"
+ and "c" rights in MYRIGHTS, ACL, and LISTRIGHTS responses.
+
+ (**) The IMAPEXT Working Group has debated this issue in great length
+ and after reviewing existing ACL implementations concluded that
+ this is a reasonable restriction.
+
+2.2. Rights Defined in RFC 2086
+
+ The "RIGHTS=" capability MUST NOT include any of the rights defined
+ in RFC 2086: "l", "r", "s", "w", "i", "p", "a", "c", "d", and the
+ digits ("0" .. "9").
+
+3. Access control management commands and responses
+
+ Servers, when processing a command that has an identifier as a
+ parameter (i.e., any of SETACL, DELETEACL, and LISTRIGHTS commands),
+ SHOULD first prepare the received identifier using "SASLprep" profile
+ [SASLprep] of the "stringprep" algorithm [Stringprep]. If the
+ preparation of the identifier fails or results in an empty string,
+ the server MUST refuse to perform the command with a BAD response.
+ Note that Section 6 recommends additional identifier's verification
+ steps.
+
+3.1. SETACL Command
+
+ Arguments: mailbox name
+ identifier
+ access right modification
+
+ Data: no specific data for this command
+
+ Result: OK - setacl completed
+ NO - setacl failure: can't set acl
+ BAD - arguments invalid
+
+ The SETACL command changes the access control list on the specified
+ mailbox so that the specified identifier is granted permissions as
+ specified in the third argument.
+
+ The third argument is a string containing an optional plus ("+") or
+ minus ("-") prefix, followed by zero or more rights characters. If
+ the string starts with a plus, the following rights are added to any
+
+
+
+Melnikov Standards Track [Page 8]
+
+RFC 4314 IMAP ACL December 2005
+
+
+ existing rights for the identifier. If the string starts with a
+ minus, the following rights are removed from any existing rights for
+ the identifier. If the string does not start with a plus or minus,
+ the rights replace any existing rights for the identifier.
+
+ Note that an unrecognized right MUST cause the command to return the
+ BAD response. In particular, the server MUST NOT silently ignore
+ unrecognized rights.
+
+ Example: C: A001 GETACL INBOX/Drafts
+ S: * ACL INBOX/Drafts Fred rwipslxetad Chris lrswi
+ S: A001 OK Getacl complete
+ C: A002 SETACL INBOX/Drafts Chris +cda
+ S: A002 OK Setacl complete
+ C: A003 GETACL INBOX/Drafts
+ S: * ACL INBOX/Drafts Fred rwipslxetad Chris lrswicdakxet
+ S: A003 OK Getacl complete
+
+
+ C: A035 SETACL INBOX/Drafts John lrQswicda
+ S: A035 BAD Uppercase rights are not allowed
+
+
+ C: A036 SETACL INBOX/Drafts John lrqswicda
+ S: A036 BAD The q right is not supported
+
+3.2. DELETEACL Command
+
+ Arguments: mailbox name
+ identifier
+
+ Data: no specific data for this command
+
+ Result: OK - deleteacl completed
+ NO - deleteacl failure: can't delete acl
+ BAD - arguments invalid
+
+ The DELETEACL command removes any <identifier,rights> pair for the
+ specified identifier from the access control list for the specified
+ mailbox.
+
+ Example: C: B001 getacl INBOX
+ S: * ACL INBOX Fred rwipslxetad -Fred wetd $team w
+ S: B001 OK Getacl complete
+ C: B002 DeleteAcl INBOX Fred
+ S: B002 OK Deleteacl complete
+
+
+
+
+
+Melnikov Standards Track [Page 9]
+
+RFC 4314 IMAP ACL December 2005
+
+
+ C: B003 GETACL INBOX
+ S: * ACL INBOX -Fred wetd $team w
+ S: B003 OK Getacl complete
+
+3.3. GETACL Command
+
+ Arguments: mailbox name
+
+ Data: untagged responses: ACL
+
+ Result: OK - getacl completed
+ NO - getacl failure: can't get acl
+ BAD - arguments invalid
+
+ The GETACL command returns the access control list for mailbox in an
+ untagged ACL response.
+
+ Some implementations MAY permit multiple forms of an identifier to
+ reference the same IMAP account. Usually, such implementations will
+ have a canonical form that is stored internally. An ACL response
+ caused by a GETACL command MAY include a canonicalized form of the
+ identifier that might be different from the one used in the
+ corresponding SETACL command.
+
+ Example: C: A002 GETACL INBOX
+ S: * ACL INBOX Fred rwipsldexta
+ S: A002 OK Getacl complete
+
+3.4. LISTRIGHTS Command
+
+ Arguments: mailbox name
+ identifier
+
+ Data: untagged responses: LISTRIGHTS
+
+ Result: OK - listrights completed
+ NO - listrights failure: can't get rights list
+ BAD - arguments invalid
+
+ The LISTRIGHTS command takes a mailbox name and an identifier and
+ returns information about what rights can be granted to the
+ identifier in the ACL for the mailbox.
+
+ Some implementations MAY permit multiple forms of an identifier to
+ reference the same IMAP account. Usually, such implementations will
+ have a canonical form that is stored internally. A LISTRIGHTS
+
+
+
+
+
+Melnikov Standards Track [Page 10]
+
+RFC 4314 IMAP ACL December 2005
+
+
+ response caused by a LISTRIGHTS command MUST always return the same
+ form of an identifier as specified by the client. This is to allow
+ the client to correlate the response with the command.
+
+ Example: C: a001 LISTRIGHTS ~/Mail/saved smith
+ S: * LISTRIGHTS ~/Mail/saved smith la r swicdkxte
+ S: a001 OK Listrights completed
+
+ Example: C: a005 listrights archive/imap anyone
+ S: * LISTRIGHTS archive.imap anyone ""
+ l r s w i p k x t e c d a 0 1 2 3 4 5 6 7 8 9
+ S: a005 Listrights successful
+
+3.5. MYRIGHTS Command
+
+ Arguments: mailbox name
+
+ Data: untagged responses: MYRIGHTS
+
+ Result: OK - myrights completed
+ NO - myrights failure: can't get rights
+ BAD - arguments invalid
+
+ The MYRIGHTS command returns the set of rights that the user has to
+ mailbox in an untagged MYRIGHTS reply.
+
+ Example: C: A003 MYRIGHTS INBOX
+ S: * MYRIGHTS INBOX rwiptsldaex
+ S: A003 OK Myrights complete
+
+3.6. ACL Response
+
+ Data: mailbox name
+ zero or more identifier rights pairs
+
+ The ACL response occurs as a result of a GETACL command. The first
+ string is the mailbox name for which this ACL applies. This is
+ followed by zero or more pairs of strings; each pair contains the
+ identifier for which the entry applies followed by the set of rights
+ that the identifier has.
+
+ Section 2.1.1 details additional server requirements related to
+ handling of the virtual "d" and "c" rights.
+
+
+
+
+
+
+
+
+Melnikov Standards Track [Page 11]
+
+RFC 4314 IMAP ACL December 2005
+
+
+3.7. LISTRIGHTS Response
+
+ Data: mailbox name
+ identifier
+ required rights
+ list of optional rights
+
+ The LISTRIGHTS response occurs as a result of a LISTRIGHTS command.
+ The first two strings are the mailbox name and identifier for which
+ this rights list applies. Following the identifier is a string
+ containing the (possibly empty) set of rights the identifier will
+ always be granted in the mailbox.
+
+ Following this are zero or more strings each containing a set of
+ rights the identifier can be granted in the mailbox. Rights
+ mentioned in the same string are tied together. The server MUST
+ either grant all tied rights to the identifier in the mailbox or
+ grant none. Section 2.1.1 details additional server requirements
+ related to handling of the virtual "d" and "c" rights.
+
+ The same right MUST NOT be listed more than once in the LISTRIGHTS
+ command.
+
+3.8. MYRIGHTS Response
+
+ Data: mailbox name
+ rights
+
+ The MYRIGHTS response occurs as a result of a MYRIGHTS command. The
+ first string is the mailbox name for which these rights apply. The
+ second string is the set of rights that the client has.
+
+ Section 2.1.1 details additional server requirements related to
+ handling of the virtual "d" and "c" rights.
+
+4. Rights Required to Perform Different IMAP4rev1 Commands
+
+ Before executing a command, an ACL-compliant server MUST check which
+ rights are required to perform it. This section groups command by
+ functions they perform and list the rights required. It also gives
+ the detailed description of any special processing required.
+
+ For the purpose of this section the UID counterpart of a command is
+ considered to be the same command, e.g., both UID COPY and COPY
+ commands require the same set of rights.
+
+
+
+
+
+
+Melnikov Standards Track [Page 12]
+
+RFC 4314 IMAP ACL December 2005
+
+
+ The table below summarizes different rights or their combinations
+ that are required in order to perform different IMAP operations. As
+ it is not always possible to express complex right checking and
+ interactions, the description after the table should be used as the
+ primary reference.
+
+ +-------------------+---+---+---+---+---+---+---+---+---+---+---+---+
+ |Operations\Rights | l | r | s | w | i | k | x | t | e | a |Any|Non|
+ +-------------------+---+---+---+---+---+---+---+---+---+---+---+---+
+ | commands in authenticated state |
+ +-------------------------------------------------------------------+
+ | LIST | + | | | | | | | | | | | |
+ | SUBSCRIBE | * | | | | | | | | | | | * |
+ | UNSUBSCRIBE | | | | | | | | | | | | + |
+ | LSUB | * | | | | | | | | | | | * |
+ |CREATE (for parent)| | | | | | + | | | | | | |
+ | DELETE | | ? | | | | | + | ? | ? | | | |
+ | RENAME | | | | | | + | + | | | | | |
+ | SELECT/EXAMINE | | + | | | | | | | | | | |
+ | STATUS | | + | | | | | | | | | | |
+ | SETACL/DELETEACL | | | | | | | | | | + | | |
+ | GETACL/LISTRIGHTS | | | | | | | | | | + | | |
+ | MYRIGHTS | | | | | | | | | | | + | |
+ | APPEND | | | ? | ? | + | | | ? | | | | |
+ +-------------------------------------------------------------------+
+ | commands in selected state |
+ +-------------------------------------------------------------------+
+ | COPY | | | ? | ? | + | | | ? | | | | |
+ | EXPUNGE | | | | | | | | | + | | | |
+ | CLOSE | | | | | | | | | ? | | | |
+ | FETCH | | | ? | | | | | | | | | |
+ | STORE flags | | | ? | ? | | | | ? | | | | |
+ +-------------------+---+---+---+---+---+---+---+---+---+---+---+---+
+
+ Note: for all commands in the selected state, the "r" is implied,
+ because it is required to SELECT/EXAMINE a mailbox. Servers are not
+ required to check presence of the "r" right once a mailbox is
+ successfully selected.
+
+ Legend:
+ + - The right is required
+ * - Only one of the rights marked with * is required
+ (see description below)
+ ? - The right is OPTIONAL (see description below)
+ "Any" - at least one of the "l", "r", "i", "k", "x", "a" rights is
+ required
+ "Non" - No rights required to perform the command
+
+
+
+
+Melnikov Standards Track [Page 13]
+
+RFC 4314 IMAP ACL December 2005
+
+
+ Listing and subscribing/unsubscribing mailboxes:
+ LIST - "l" right is required. However, unlike other commands
+ (e.g., SELECT) the server MUST NOT return a NO response if it
+ can't list a mailbox.
+ Note that if the user has "l" right to a mailbox "A/B", but not to
+ its parent mailbox "A", the LIST command should behave as if the
+ mailbox "A" doesn't exist, for example:
+
+ C: A777 LIST "" *
+ S: * LIST (\NoInferiors) "/" "A/B"
+ S: * LIST () "/" "C"
+ S: * LIST (\NoInferiors) "/" "C/D"
+ S: A777 OK LIST completed
+
+
+ SUBSCRIBE - "l" right is required only if the server checks for
+ mailbox existence when performing SUBSCRIBE.
+
+ UNSUBSCRIBE - no rights required to perform this operation.
+
+ LSUB - "l" right is required only if the server checks for mailbox
+ existence when performing SUBSCRIBE. However, unlike other
+ commands (e.g., SELECT) the server MUST NOT return a NO response
+ if it can't list a subscribed mailbox.
+
+ Mailbox management:
+ CREATE - "k" right on a nearest existing parent mailbox. When a
+ new mailbox is created, it SHOULD inherit the ACL from the parent
+ mailbox (if one exists) in the defined hierarchy.
+
+ DELETE - "x" right on the mailbox. Note that some servers don't
+ allow to delete a non-empty mailbox. If this is the case, the
+ user would also need "r", "e", and "t" rights, in order to open
+ the mailbox and empty it.
+
+ The DELETE command MUST delete the ACL associated with the deleted
+ mailbox.
+
+ RENAME - Moving a mailbox from one parent to another requires the
+ "x" right on the mailbox itself and the "k" right for the new
+ parent. For example, if the user wants to rename the mailbox
+ named "A/B/C" to "D/E", the user must have the "x" right for the
+ mailbox "A/B/C" and the "k" right for the mailbox "D".
+ The RENAME command SHOULD NOT change the ACLs on the renamed
+ mailbox and submailboxes.
+
+
+
+
+
+
+Melnikov Standards Track [Page 14]
+
+RFC 4314 IMAP ACL December 2005
+
+
+ Copying or appending messages:
+ Before performing a COPY/APPEND command, the server MUST check if
+ the user has "i" right for the target mailbox. If the user
+ doesn't have "i" right, the operation fails. Otherwise for each
+ copied/appended message the server MUST check if the user has
+ "t" right - when the message has \Deleted flag set
+ "s" right - when the message has \Seen flag set
+ "w" right - for all other message flags.
+ Only when the user has a particular right are the corresponding
+ flags stored for the newly created message. The server MUST NOT
+ fail a COPY/APPEND if the user has no rights to set a particular
+ flag.
+
+ Example: C: A003 MYRIGHTS TargetMailbox
+ S: * MYRIGHTS TargetMailbox rwis
+ S: A003 OK Myrights complete
+
+ C: A004 FETCH 1:3 (FLAGS)
+ S: * 1 FETCH (FLAGS (\Draft \Deleted)
+ S: * 2 FETCH (FLAGS (\Answered)
+ S: * 3 FETCH (FLAGS ($Forwarded \Seen)
+ S: A004 OK Fetch Completed
+
+ C: A005 COPY 1:3 TargetMailbox
+ S: A005 OK Copy completed
+
+ C: A006 SELECT TargetMailbox
+ ...
+ S: A006 Select Completed
+
+ Let's assume that the copied messages received message numbers
+ 77:79.
+
+ C: A007 FETCH 77:79 (FLAGS)
+ S: * 77 FETCH (FLAGS (\Draft))
+ S: * 78 FETCH (FLAGS (\Answered))
+ S: * 79 FETCH (FLAGS ($Forwarded \Seen))
+ S: A007 OK Fetch Completed
+
+ \Deleted flag was lost on COPY, as the user has no "t" right in
+ the target mailbox.
+ If the MYRIGHTS command with the tag A003 would have returned:
+
+ S: * MYRIGHTS TargetMailbox rsti
+
+ the response from the FETCH with the tag A007 would have been:
+
+ C: A007 FETCH 77:79 (FLAGS)
+
+
+
+Melnikov Standards Track [Page 15]
+
+RFC 4314 IMAP ACL December 2005
+
+
+ S: * 77 FETCH (FLAGS (\Deleted))
+ S: * 78 FETCH (FLAGS ())
+ S: * 79 FETCH (FLAGS (\Seen))
+ S: A007 OK Fetch Completed
+
+ In the latter case, \Answered, $Forwarded, and \Draft flags were
+ lost on COPY, as the user has no "w" right in the target mailbox.
+
+ Expunging the selected mailbox:
+ EXPUNGE - "e" right on the selected mailbox.
+
+ CLOSE - "e" right on the selected mailbox. If the server is
+ unable to expunge the mailbox because the user doesn't have the
+ "e" right, the server MUST ignore the expunge request, close the
+ mailbox, and return the tagged OK response.
+
+ Fetch information about a mailbox and its messages:
+ SELECT/EXAMINE/STATUS - "r" right on the mailbox.
+
+ FETCH - A FETCH request that implies setting \Seen flag MUST NOT
+ set it, if the current user doesn't have "s" right.
+
+ Changing flags:
+ STORE - the server MUST check if the user has
+ "t" right - when the user modifies \Deleted flag
+ "s" right - when the user modifies \Seen flag
+ "w" right - for all other message flags.
+ STORE operation SHOULD NOT fail if the user has rights to modify
+ at least one flag specified in the STORE, as the tagged NO
+ response to a STORE command is not handled very well by deployed
+ clients.
+
+ Changing ACLs:
+ SETACL/DELETEACL - "a" right on the mailbox.
+
+ Reading ACLs:
+ GETACL - "a" right on the mailbox.
+
+ MYRIGHTS - any of the following rights is required to perform the
+ operation: "l", "r", "i", "k", "x", "a".
+
+ LISTRIGHTS - "a" right on the mailbox.
+
+
+
+
+
+
+
+
+
+Melnikov Standards Track [Page 16]
+
+RFC 4314 IMAP ACL December 2005
+
+
+5. Other Considerations
+
+5.1. Additional Requirements and Implementation Notes
+
+5.1.1. Servers
+
+ This document defines an additional capability that is used to
+ announce the list of extra rights (excluding the ones defined in RFC
+ 2086) supported by the server. The set of rights MUST include "t",
+ "e", "x", and "k". Note that the extra rights can appear in any
+ order.
+
+ Example: C: 1 capability
+ S: * CAPABILITY IMAP4REV1 STARTTLS LITERAL+
+ ACL RIGHTS=texk
+ S: 1 OK completed
+
+ Any server implementing an ACL extension MUST accurately reflect the
+ current user's rights in FLAGS and PERMANENTFLAGS responses.
+
+ Example: C: A142 SELECT INBOX
+ S: * 172 EXISTS
+ S: * 1 RECENT
+ S: * OK [UNSEEN 12] Message 12 is first unseen
+ S: * OK [UIDVALIDITY 3857529045] UIDs valid
+ S: * OK [UIDNEXT 4392] Predicted next UID
+ S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
+ S: * OK [PERMANENTFLAGS (\Seen \Answered \Flagged \*)] L
+ S: A142 OK [READ-WRITE] SELECT completed
+ C: A143 MYRIGHTS INBOX
+ S: * MYRIGHTS INBOX lrwis
+ S: A143 OK completed
+
+ Note that in order to get better performance the client MAY pipeline
+ SELECT and MYRIGHTS commands:
+
+ C: A142 SELECT INBOX
+ C: A143 MYRIGHTS INBOX
+ S: * 172 EXISTS
+ S: * 1 RECENT
+ S: * OK [UNSEEN 12] Message 12 is first unseen
+ S: * OK [UIDVALIDITY 3857529045] UIDs valid
+ S: * OK [UIDNEXT 4392] Predicted next UID
+ S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
+ S: * OK [PERMANENTFLAGS (\Seen \Answered \Flagged \*)] L
+ S: A142 OK [READ-WRITE] SELECT completed
+ S: * MYRIGHTS INBOX lrwis
+ S: A143 OK completed
+
+
+
+Melnikov Standards Track [Page 17]
+
+RFC 4314 IMAP ACL December 2005
+
+
+ Servers MAY cache the rights a user has on a mailbox when the mailbox
+ is selected, so that if a client's rights on a mailbox are changed
+ with SETACL or DELETEACL, commands specific to the selected state
+ (e.g., STORE, EXPUNGE) might not reflect the changed rights until the
+ mailbox is re-selected. If the server checks the rights on each
+ command, then it SHOULD send FLAGS and PERMANENTFLAGS responses if
+ they have changed. If such server detects that the user no longer
+ has read access to the mailbox, it MAY send an untagged BYE response
+ and close connection. It MAY also refuse to execute all commands
+ specific to the selected state until the mailbox is closed; however,
+ server implementors should note that most clients don't handle NO
+ responses very well.
+
+ An ACL server MAY modify one or more ACLs for one or more identifiers
+ as a side effect of modifying the ACL specified in a
+ SETACL/DELETEACL. If the server does that, it MUST send untagged ACL
+ response(s) to notify the client about the changes made.
+
+ An ACL server implementation MUST treat received ACL modification
+ commands as a possible ambiguity with respect to subsequent commands
+ affected by the ACL, as described in Section 5.5 of [IMAP4]. Hence a
+ pipeline SETACL + MYRIGHTS is an ambiguity with respect to the
+ server, meaning that the server must execute the SETACL command to
+ completion before the MYRIGHTS. However, clients are permitted to
+ send such a pipeline.
+
+5.1.2. Clients
+
+ The following requirement is put on clients in order to allow for
+ future extensibility. A client implementation that allows a user to
+ read and update ACLs MUST preserve unrecognized rights that it
+ doesn't allow the user to change. That is, if the client
+
+ 1) can read ACLs
+ and
+ 2) can update ACLs
+ but
+ 3) doesn't allow the user to change the rights the client doesn't
+ recognize, then it MUST preserve unrecognized rights.
+
+ Otherwise the client could risk unintentionally removing permissions
+ it doesn't understand.
+
+
+
+
+
+
+
+
+
+Melnikov Standards Track [Page 18]
+
+RFC 4314 IMAP ACL December 2005
+
+
+5.2. Mapping of ACL Rights to READ-WRITE and READ-ONLY Response Codes
+
+ A particular ACL server implementation MAY allow "shared multiuser
+ access" to some mailboxes. "Shared multiuser access" to a mailbox
+ means that multiple different users are able to access the same
+ mailbox, if they have proper access rights. "Shared multiuser
+ access" to the mailbox doesn't mean that the ACL for the mailbox is
+ currently set to allow access by multiple users. Let's denote a
+ "shared multiuser write access" as a "shared multiuser access" when a
+ user can be granted flag modification rights (any of "w", "s", or
+ "t").
+
+ Section 4 describes which rights are required for modifying different
+ flags.
+
+ If the ACL server implements some flags as shared for a mailbox
+ (i.e., the ACL for the mailbox MAY be set up so that changes to those
+ flags are visible to another user), let's call the set of rights
+ associated with these flags (as described in Section 4) for that
+ mailbox collectively as "shared flag rights". Note that the "shared
+ flag rights" set MAY be different for different mailboxes.
+
+ If the server doesn't support "shared multiuser write access" to a
+ mailbox or doesn't implement shared flags on the mailbox, "shared
+ flag rights" for the mailbox is defined to be the empty set.
+
+ Example 1: Mailbox "banan" allows "shared multiuser write access" and
+ implements flags \Deleted, \Answered, and $MDNSent as
+ shared flags. "Shared flag rights" for the mailbox "banan"
+ is a set containing flags "t" (because system flag
+ \Deleted requires "t" right) and "w" (because both
+ \Answered and $MDNSent require "w" right).
+
+ Example 2: Mailbox "apple" allows "shared multiuser write access" and
+ implements \Seen system flag as shared flag. "Shared flag
+ rights" for the mailbox "apple" contains "s" right
+ because system flag \Seen requires "s" right.
+
+ Example 3: Mailbox "pear" allows "shared multiuser write access" and
+ implements flags \Seen, \Draft as shared flags. "Shared
+ flag rights" for the mailbox "apple" is a set containing
+ flags "s" (because system flag \Seen requires "s" right)
+ and "w" (because system flag \Draft requires "w" right).
+
+ The server MUST include a READ-ONLY response code in the tagged OK
+ response to a SELECT command if none of the following rights is
+ granted to the current user:
+
+
+
+
+Melnikov Standards Track [Page 19]
+
+RFC 4314 IMAP ACL December 2005
+
+
+ "i", "e", and "shared flag rights"(***).
+
+ The server SHOULD include a READ-WRITE response code in the tagged OK
+ response if at least one of the "i", "e", or "shared flag
+ rights"(***) is granted to the current user.
+
+ (***) Note that a future extension to this document can extend the
+ list of rights that causes the server to return the READ-WRITE
+ response code.
+
+ Example 1 (continued): The user that has "lrs" rights for the mailbox
+ "banan". The server returns READ-ONLY
+ response code on SELECT, as none of "iewt"
+ rights is granted to the user.
+
+ Example 2 (continued): The user that has "rit" rights for the mailbox
+ "apple". The server returns READ-WRITE
+ response code on SELECT, as the user has "i"
+ right.
+
+ Example 3 (continued): The user that has "rset" rights for the
+ mailbox "pear". The server returns READ-WRITE
+ response code on SELECT, as the user has "e"
+ and "s" rights.
+
+6. Security Considerations
+
+ An implementation MUST make sure the ACL commands themselves do not
+ give information about mailboxes with appropriately restricted ACLs.
+ For example, when a user agent executes a GETACL command on a mailbox
+ that the user has no permission to LIST, the server would respond to
+ that request with the same error that would be used if the mailbox
+ did not exist, thus revealing no existence information, much less the
+ mailbox's ACL.
+
+ IMAP clients implementing ACL that are able to modify ACLs SHOULD
+ warn a user that wants to give full access (or even just the "a"
+ right) to the special identifier "anyone".
+
+ This document relies on [SASLprep] to describe steps required to
+ perform identifier canonicalization (preparation). The preparation
+ algorithm in SASLprep was specifically designed such that its output
+ is canonical, and it is well-formed. However, due to an anomaly
+ [PR29] in the specification of Unicode normalization, canonical
+ equivalence is not guaranteed for a select few character sequences.
+ Identifiers prepared with SASLprep can be stored and returned by an
+ ACL server. The anomaly affects ACL manipulation and evaluation of
+ identifiers containing the selected character sequences. These
+
+
+
+Melnikov Standards Track [Page 20]
+
+RFC 4314 IMAP ACL December 2005
+
+
+ sequences, however, do not appear in well-formed text. In order to
+ address this problem, an ACL server MAY reject identifiers containing
+ sequences described in [PR29] by sending the tagged BAD response.
+ This is in addition to the requirement to reject identifiers that
+ fail SASLprep preparation as described in Section 3.
+
+ Other security considerations described in [IMAP4] are relevant to
+ this document. In particular, ACL information is sent in the clear
+ over the network unless confidentiality protection is negotiated.
+
+ This can be accomplished either by the use of STARTTLS, negotiated
+ privacy protection in the AUTHENTICATE command, or some other
+ protection mechanism.
+
+7. Formal Syntax
+
+ Formal syntax is defined using ABNF [ABNF], extending the ABNF rules
+ in Section 9 of [IMAP4]. Elements not defined here can be found in
+ [ABNF] and [IMAP4].
+
+ Except as noted otherwise, all alphabetic characters are case
+ insensitive. The use of uppercase or lowercase characters to define
+ token strings is for editorial clarity only. Implementations MUST
+ accept these strings in a case-insensitive fashion.
+
+ LOWER-ALPHA = %x61-7A ;; a-z
+
+ acl-data = "ACL" SP mailbox *(SP identifier SP
+ rights)
+
+ capability =/ rights-capa
+ ;;capability is defined in [IMAP4]
+
+ command-auth =/ setacl / deleteacl / getacl /
+ listrights / myrights
+ ;;command-auth is defined in [IMAP4]
+
+ deleteacl = "DELETEACL" SP mailbox SP identifier
+
+ getacl = "GETACL" SP mailbox
+
+ identifier = astring
+
+ listrights = "LISTRIGHTS" SP mailbox SP identifier
+
+ listrights-data = "LISTRIGHTS" SP mailbox SP identifier
+ SP rights *(SP rights)
+
+
+
+
+Melnikov Standards Track [Page 21]
+
+RFC 4314 IMAP ACL December 2005
+
+
+ mailbox-data =/ acl-data / listrights-data / myrights-data
+ ;;mailbox-data is defined in [IMAP4]
+
+ mod-rights = astring
+ ;; +rights to add, -rights to remove
+ ;; rights to replace
+
+ myrights = "MYRIGHTS" SP mailbox
+
+ myrights-data = "MYRIGHTS" SP mailbox SP rights
+
+ new-rights = 1*LOWER-ALPHA
+ ;; MUST include "t", "e", "x", and "k".
+ ;; MUST NOT include standard rights listed
+ ;; in section 2.2
+
+ rights = astring
+ ;; only lowercase ASCII letters and digits
+ ;; are allowed.
+
+ rights-capa = "RIGHTS=" new-rights
+ ;; RIGHTS=... capability
+
+ setacl = "SETACL" SP mailbox SP identifier
+ SP mod-rights
+
+8. IANA Considerations
+
+ IMAP4 capabilities are registered by publishing a standards-track or
+ IESG-approved experimental RFC. The registry is currently located
+ at:
+
+ http://www.iana.org/assignments/imap4-capabilities
+
+ This document defines the RIGHTS= IMAP capability. IANA has added
+ this capability to the registry.
+
+9. Internationalization Considerations
+
+ Section 3 states requirements on servers regarding
+ internationalization of identifiers.
+
+
+
+
+
+
+
+
+
+
+Melnikov Standards Track [Page 22]
+
+RFC 4314 IMAP ACL December 2005
+
+
+Appendix A. Changes since RFC 2086
+
+ 1. Changed the charset of "identifier" from US-ASCII to UTF-8.
+ 2. Specified that mailbox deletion is controlled by the "x" right
+ and EXPUNGE is controlled by the "e" right.
+ 3. Added the "t" right that controls STORE \Deleted. Redefined the
+ "d" right to be a macro for "e", "t", and possibly "x".
+ 4. Added the "k" right that controls CREATE. Redefined the "c"
+ right to be a macro for "k" and possibly "x".
+ 5. Specified that the "a" right also controls DELETEACL.
+ 6. Specified that the "r" right also controls STATUS.
+ 7. Removed the requirement to check the "r" right for CHECK, SEARCH
+ and FETCH, as this is required for SELECT/EXAMINE to be
+ successful.
+ 8. LISTRIGHTS requires the "a" right on the mailbox (same as
+ SETACL).
+ 9. Deleted "PARTIAL", this is a deprecated feature of RFC 1730.
+ 10. Specified that the "w" right controls setting flags other than
+ \Seen and \Deleted on APPEND. Also specified that the "s" right
+ controls the \Seen flag and that the "t" right controls the
+ \Deleted flag.
+ 11. Specified that SUBSCRIBE is NOT allowed with the "r" right.
+ 12. Specified that the "l" right controls SUBSCRIBE.
+ 13. GETACL is NOT allowed with the "r" right, even though there are
+ several implementations that allows that. If a user only has
+ "r" right, GETACL can disclose information about identifiers
+ existing on the mail system.
+ 14. Clarified that RENAME requires the "k" right for the new parent
+ and the "x" right for the old name.
+ 15. Added new section that describes which rights are required
+ and/or checked when performing various IMAP commands.
+ 16. Added mail client security considerations when dealing with
+ special identifier "anyone".
+ 17. Clarified that negative rights are not the same as DELETEACL.
+ 18. Added "Compatibility with RFC 2086" section.
+ 19. Added section about mapping of ACL rights to READ-WRITE and
+ READ-ONLY response codes.
+ 20. Changed BNF to ABNF.
+ 21. Added "Implementation Notes" section.
+ 22. Updated "References" section.
+ 23. Added more examples.
+ 24. Clarified when the virtual "c" and "d" rights are returned in
+ ACL, MYRIGHTS, and LISTRIGHTS responses.
+
+
+
+
+
+
+
+
+Melnikov Standards Track [Page 23]
+
+RFC 4314 IMAP ACL December 2005
+
+
+Appendix B. Compatibility with RFC 2086
+
+ This non-normative section gives guidelines as to how an existing RFC
+ 2086 server implementation may be updated to comply with this
+ document.
+
+ This document splits the "d" right into several new different rights:
+ "t", "e", and possibly "x" (see Section 2.1.1 for more details). The
+ "d" right remains for backward-compatibility, but it is a virtual
+ right. There are two approaches for RFC 2086 server implementors to
+ handle the "d" right and the new rights that have replaced it:
+
+ a. Tie "t", "e" (and possibly "x) together - almost no changes.
+ b. Implement separate "x", "t" and "e". Return the "d" right in a
+ MYRIGHTS response or an ACL response containing ACL information
+ when any of the "t", "e" (and "x") is granted.
+
+ In a similar manner this document splits the "c" right into several
+ new different rights: "k" and possibly "x" (see Section 2.1.1 for
+ more details). The "c" right remains for backwards-compatibility but
+ it is a virtual right. Again, RFC 2086 server implementors can
+ choose to tie rights or to implement separate rights, as described
+ above.
+
+ Also check Sections 5.1.1 and 5.1.2, as well as Appendix A, to see
+ other changes required. Server implementors should check which
+ rights are required to invoke different IMAP4 commands as described
+ in Section 4.
+
+Appendix C. Known Deficiencies
+
+ This specification has some known deficiencies including:
+
+ 1. This is inadequate to provide complete read-write access to
+ mailboxes protected by Unix-style rights bits because there is no
+ equivalent to "chown" and "chgrp" commands nor is there a good
+ way to discover such limitations are present.
+ 2. Because this extension leaves the specific semantics of how
+ rights are combined by the server as implementation defined, the
+ ability to build a user-friendly interface is limited.
+ 3. Users, groups, and special identifiers (e.g., anyone) exist in
+ the same namespace.
+
+ The work-in-progress "ACL2" extension is intended to redesign this
+ extension to address these deficiencies without the constraint of
+ backward-compatibility and may eventually supercede this facility.
+
+
+
+
+
+Melnikov Standards Track [Page 24]
+
+RFC 4314 IMAP ACL December 2005
+
+
+ However, RFC 2086 is deployed in multiple implementations so this
+ intermediate step, which fixes the straightforward deficiencies in a
+ backward-compatible fashion, is considered worthwhile.
+
+Appendix D. Acknowledgements
+
+ This document is a revision of RFC 2086 written by John G. Myers.
+
+ Editor appreciates comments received from Mark Crispin, Chris Newman,
+ Cyrus Daboo, John G. Myers, Dave Cridland, Ken Murchison, Steve Hole,
+ Vladimir Butenko, Larry Greenfield, Robert Siemborski, Harrie
+ Hazewinkel, Philip Guenther, Brian Candler, Curtis King, Lyndon
+ Nerenberg, Lisa Dusseault, Arnt Gulbrandsen, and other participants
+ of the IMAPEXT working group.
+
+Normative References
+
+ [KEYWORDS] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [ABNF] Crocker, D. and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", RFC 4234, October 2005.
+
+ [IMAP4] Crispin, M., "INTERNET MESSAGE ACCESS PROTOCOL - VERSION
+ 4rev1", RFC 3501, March 2003.
+
+ [UTF-8] Yergeau, F., "UTF-8, a transformation format of ISO
+ 10646", STD 63, RFC 3629, November 2003.
+
+ [Stringprep] Hoffman, P. and M. Blanchet, "Preparation of
+ Internationalized Strings ("stringprep")", RFC 3454,
+ December 2002.
+
+ [SASLprep] Zeilenga, K., "SASLprep: Stringprep Profile for User
+ Names and Passwords", RFC 4013, February 2005.
+
+Informative References
+
+ [RFC2086] Myers, J., "IMAP4 ACL extension", RFC 2086,
+ January 1997.
+
+ [PR29] "Public Review Issue #29: Normalization Issue",
+ February 2004,
+ <http://www.unicode.org/review/pr-29.html>.
+
+
+
+
+
+
+
+Melnikov Standards Track [Page 25]
+
+RFC 4314 IMAP ACL December 2005
+
+
+Author's Address
+
+ Alexey Melnikov
+ Isode Ltd.
+ 5 Castle Business Village
+ 36 Station Road
+ Hampton, Middlesex TW12 2BX
+ GB
+
+ EMail: alexey.melnikov@isode.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Melnikov Standards Track [Page 26]
+
+RFC 4314 IMAP ACL December 2005
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2005).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at ietf-
+ ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+Melnikov Standards Track [Page 27]
+
diff --git a/imap/docs/rfc/rfc4315.txt b/imap/docs/rfc/rfc4315.txt
new file mode 100644
index 00000000..c026f422
--- /dev/null
+++ b/imap/docs/rfc/rfc4315.txt
@@ -0,0 +1,451 @@
+
+
+
+
+
+
+Network Working Group M. Crispin
+Request for Comments: 4315 December 2005
+Obsoletes: 2359
+Category: Standards Track
+
+
+ Internet Message Access Protocol (IMAP) - UIDPLUS extension
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2005).
+
+Abstract
+
+ The UIDPLUS extension of the Internet Message Access Protocol (IMAP)
+ provides a set of features intended to reduce the amount of time and
+ resources used by some client operations. The features in UIDPLUS
+ are primarily intended for disconnected-use clients.
+
+1. Introduction and Overview
+
+ The UIDPLUS extension is present in any IMAP server implementation
+ that returns "UIDPLUS" as one of the supported capabilities to the
+ CAPABILITY command.
+
+ The UIDPLUS extension defines an additional command. In addition,
+ this document recommends new status response codes in IMAP that
+ SHOULD be returned by all server implementations, regardless of
+ whether or not the UIDPLUS extension is implemented.
+
+ The added facilities of the features in UIDPLUS are optimizations;
+ clients can provide equivalent functionality, albeit less
+ efficiently, by using facilities in the base protocol.
+
+1.1. Conventions Used in This Document
+
+ In examples, "C:" and "S:" indicate lines sent by the client and
+ server, respectively.
+
+
+
+
+
+Crispin Standards Track [Page 1]
+
+RFC 4315 IMAP - UIDPLUS Extension December 2005
+
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "MAY", and "OPTIONAL" in this document are to
+ be interpreted as described in [KEYWORDS].
+
+ A "UID set" is similar to the [IMAP] sequence set; however, the "*"
+ value for a sequence number is not permitted.
+
+2. Additional Commands
+
+ The following command definition is an extension to [IMAP] section
+ 6.4.
+
+2.1. UID EXPUNGE Command
+
+ Arguments: sequence set
+
+ Data: untagged responses: EXPUNGE
+
+ Result: OK - expunge completed
+ NO - expunge failure (e.g., permission denied)
+ BAD - command unknown or arguments invalid
+
+ The UID EXPUNGE command permanently removes all messages that both
+ have the \Deleted flag set and have a UID that is included in the
+ specified sequence set from the currently selected mailbox. If a
+ message either does not have the \Deleted flag set or has a UID
+ that is not included in the specified sequence set, it is not
+ affected.
+
+ This command is particularly useful for disconnected use clients.
+ By using UID EXPUNGE instead of EXPUNGE when resynchronizing with
+ the server, the client can ensure that it does not inadvertantly
+ remove any messages that have been marked as \Deleted by other
+ clients between the time that the client was last connected and
+ the time the client resynchronizes.
+
+ If the server does not support the UIDPLUS capability, the client
+ should fall back to using the STORE command to temporarily remove
+ the \Deleted flag from messages it does not want to remove, then
+ issuing the EXPUNGE command. Finally, the client should use the
+ STORE command to restore the \Deleted flag on the messages in
+ which it was temporarily removed.
+
+ Alternatively, the client may fall back to using just the EXPUNGE
+ command, risking the unintended removal of some messages.
+
+
+
+
+
+
+Crispin Standards Track [Page 2]
+
+RFC 4315 IMAP - UIDPLUS Extension December 2005
+
+
+ Example: C: A003 UID EXPUNGE 3000:3002
+ S: * 3 EXPUNGE
+ S: * 3 EXPUNGE
+ S: * 3 EXPUNGE
+ S: A003 OK UID EXPUNGE completed
+
+3. Additional Response Codes
+
+ The following response codes are extensions to the response codes
+ defined in [IMAP] section 7.1. With limited exceptions, discussed
+ below, server implementations that advertise the UIDPLUS extension
+ SHOULD return these response codes.
+
+ In the case of a mailbox that has permissions set so that the client
+ can COPY or APPEND to the mailbox, but not SELECT or EXAMINE it, the
+ server SHOULD NOT send an APPENDUID or COPYUID response code as it
+ would disclose information about the mailbox.
+
+ In the case of a mailbox that has UIDNOTSTICKY status (as defined
+ below), the server MAY omit the APPENDUID or COPYUID response code as
+ it is not meaningful.
+
+ If the server does not return the APPENDUID or COPYUID response
+ codes, the client can discover this information by selecting the
+ destination mailbox. The location of messages placed in the
+ destination mailbox by COPY or APPEND can be determined by using
+ FETCH and/or SEARCH commands (e.g., for Message-ID or some unique
+ marker placed in the message in an APPEND).
+
+ APPENDUID
+
+ Followed by the UIDVALIDITY of the destination mailbox and the UID
+ assigned to the appended message in the destination mailbox,
+ indicates that the message has been appended to the destination
+ mailbox with that UID.
+
+ If the server also supports the [MULTIAPPEND] extension, and if
+ multiple messages were appended in the APPEND command, then the
+ second value is a UID set containing the UIDs assigned to the
+ appended messages, in the order they were transmitted in the
+ APPEND command. This UID set may not contain extraneous UIDs or
+ the symbol "*".
+
+ Note: the UID set form of the APPENDUID response code MUST NOT
+ be used if only a single message was appended. In particular,
+ a server MUST NOT send a range such as 123:123. This is
+ because a client that does not support [MULTIAPPEND] expects
+ only a single UID and not a UID set.
+
+
+
+Crispin Standards Track [Page 3]
+
+RFC 4315 IMAP - UIDPLUS Extension December 2005
+
+
+ UIDs are assigned in strictly ascending order in the mailbox
+ (refer to [IMAP], section 2.3.1.1) and UID ranges are as in
+ [IMAP]; in particular, note that a range of 12:10 is exactly
+ equivalent to 10:12 and refers to the sequence 10,11,12.
+
+ This response code is returned in a tagged OK response to the
+ APPEND command.
+
+ COPYUID
+
+ Followed by the UIDVALIDITY of the destination mailbox, a UID set
+ containing the UIDs of the message(s) in the source mailbox that
+ were copied to the destination mailbox and containing the UIDs
+ assigned to the copied message(s) in the destination mailbox,
+ indicates that the message(s) have been copied to the destination
+ mailbox with the stated UID(s).
+
+ The source UID set is in the order the message(s) were copied; the
+ destination UID set corresponds to the source UID set and is in
+ the same order. Neither of the UID sets may contain extraneous
+ UIDs or the symbol "*".
+
+ UIDs are assigned in strictly ascending order in the mailbox
+ (refer to [IMAP], section 2.3.1.1) and UID ranges are as in
+ [IMAP]; in particular, note that a range of 12:10 is exactly
+ equivalent to 10:12 and refers to the sequence 10,11,12.
+
+ This response code is returned in a tagged OK response to the COPY
+ command.
+
+ UIDNOTSTICKY
+
+ The selected mailbox is supported by a mail store that does not
+ support persistent UIDs; that is, UIDVALIDITY will be different
+ each time the mailbox is selected. Consequently, APPEND or COPY
+ to this mailbox will not return an APPENDUID or COPYUID response
+ code.
+
+ This response code is returned in an untagged NO response to the
+ SELECT command.
+
+ Note: servers SHOULD NOT have any UIDNOTSTICKY mail stores.
+ This facility exists to support legacy mail stores in which it
+ is technically infeasible to support persistent UIDs. This
+ should be avoided when designing new mail stores.
+
+
+
+
+
+
+Crispin Standards Track [Page 4]
+
+RFC 4315 IMAP - UIDPLUS Extension December 2005
+
+
+ Example: C: A003 APPEND saved-messages (\Seen) {297}
+ C: Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
+ C: From: Fred Foobar <foobar@example.com>
+ C: Subject: afternoon meeting
+ C: To: mooch@example.com
+ C: Message-Id: <B27397-0100000@example.com>
+ C: MIME-Version: 1.0
+ C: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
+ C:
+ C: Hello Joe, do you think we can meet at 3:30 tomorrow?
+ C:
+ S: A003 OK [APPENDUID 38505 3955] APPEND completed
+ C: A004 COPY 2:4 meeting
+ S: A004 OK [COPYUID 38505 304,319:320 3956:3958] Done
+ C: A005 UID COPY 305:310 meeting
+ S: A005 OK No matching messages, so nothing copied
+ C: A006 COPY 2 funny
+ S: A006 OK Done
+ C: A007 SELECT funny
+ S: * 1 EXISTS
+ S: * 1 RECENT
+ S: * OK [UNSEEN 1] Message 1 is first unseen
+ S: * OK [UIDVALIDITY 3857529045] Validity session-only
+ S: * OK [UIDNEXT 2] Predicted next UID
+ S: * NO [UIDNOTSTICKY] Non-persistent UIDs
+ S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
+ S: * OK [PERMANENTFLAGS (\Deleted \Seen)] Limited
+ S: A007 OK [READ-WRITE] SELECT completed
+
+ In this example, A003 and A004 demonstrate successful appending and
+ copying to a mailbox that returns the UIDs assigned to the messages.
+ A005 is an example in which no messages were copied; this is because
+ in A003, we see that message 2 had UID 304, and message 3 had UID
+ 319; therefore, UIDs 305 through 310 do not exist (refer to section
+ 2.3.1.1 of [IMAP] for further explanation). A006 is an example of a
+ message being copied that did not return a COPYUID; and, as expected,
+ A007 shows that the mail store containing that mailbox does not
+ support persistent UIDs.
+
+4. Formal Syntax
+
+ Formal syntax is defined using ABNF [ABNF], which extends the ABNF
+ rules defined in [IMAP]. The IMAP4 ABNF should be imported before
+ attempting to validate these rules.
+
+ append-uid = uniqueid
+
+ capability =/ "UIDPLUS"
+
+
+
+Crispin Standards Track [Page 5]
+
+RFC 4315 IMAP - UIDPLUS Extension December 2005
+
+
+ command-select =/ uid-expunge
+
+ resp-code-apnd = "APPENDUID" SP nz-number SP append-uid
+
+ resp-code-copy = "COPYUID" SP nz-number SP uid-set SP uid-set
+
+ resp-text-code =/ resp-code-apnd / resp-code-copy / "UIDNOTSTICKY"
+ ; incorporated before the expansion rule of
+ ; atom [SP 1*<any TEXT-CHAR except "]">]
+ ; that appears in [IMAP]
+
+ uid-expunge = "UID" SP "EXPUNGE" SP sequence-set
+
+ uid-set = (uniqueid / uid-range) *("," uid-set)
+
+ uid-range = (uniqueid ":" uniqueid)
+ ; two uniqueid values and all values
+ ; between these two regards of order.
+ ; Example: 2:4 and 4:2 are equivalent.
+
+ Servers that support [MULTIAPPEND] will have the following extension
+ to the above rules:
+
+ append-uid =/ uid-set
+ ; only permitted if client uses [MULTIAPPEND]
+ ; to append multiple messages.
+
+5. Security Considerations
+
+ The COPYUID and APPENDUID response codes return information about the
+ mailbox, which may be considered sensitive if the mailbox has
+ permissions set that permit the client to COPY or APPEND to the
+ mailbox, but not SELECT or EXAMINE it.
+
+ Consequently, these response codes SHOULD NOT be issued if the client
+ does not have access to SELECT or EXAMINE the mailbox.
+
+6. IANA Considerations
+
+ This document constitutes registration of the UIDPLUS capability in
+ the imap4-capabilities registry, replacing [RFC2359].
+
+7. Normative References
+
+ [ABNF] Crocker, D. and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", RFC 4234, October 2005.
+
+
+
+
+
+Crispin Standards Track [Page 6]
+
+RFC 4315 IMAP - UIDPLUS Extension December 2005
+
+
+ [IMAP] Crispin, M., "INTERNET MESSAGE ACCESS PROTOCOL -
+ VERSION 4rev1", RFC 3501, March 2003.
+
+ [KEYWORDS] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [MULTIAPPEND] Crispin, M., "Internet Message Access Protocol (IMAP) -
+ MULTIAPPEND Extension", RFC 3502, March 2003.
+
+8. Informative References
+
+ [RFC2359] Myers, J., "IMAP4 UIDPLUS extension", RFC 2359, June
+ 1998.
+
+9. Changes from RFC 2359
+
+ This document obsoletes [RFC2359]. However, it is based upon that
+ document, and takes substantial text from it (albeit with numerous
+ clarifications in wording).
+
+ [RFC2359] implied that a server must always return COPYUID/APPENDUID
+ data; thus suggesting that in such cases the server should return
+ arbitrary data if the destination mailbox did not support persistent
+ UIDs. This document adds the UIDNOTSTICKY response code to indicate
+ that a mailbox does not support persistent UIDs, and stipulates that
+ a UIDPLUS server does not return COPYUID/APPENDUID data when the COPY
+ (or APPEND) destination mailbox has UIDNOTSTICKY status.
+
+Author's Address
+
+ Mark R. Crispin
+ Networks and Distributed Computing
+ University of Washington
+ 4545 15th Avenue NE
+ Seattle, WA 98105-4527
+
+ Phone: (206) 543-5762
+ EMail: MRC@CAC.Washington.EDU
+
+
+
+
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 7]
+
+RFC 4315 IMAP - UIDPLUS Extension December 2005
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2005).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at ietf-
+ ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+Crispin Standards Track [Page 8]
+
diff --git a/imap/docs/rfc/rfc4422.txt b/imap/docs/rfc/rfc4422.txt
new file mode 100644
index 00000000..049fa8c5
--- /dev/null
+++ b/imap/docs/rfc/rfc4422.txt
@@ -0,0 +1,1851 @@
+
+
+
+
+
+
+Network Working Group A. Melnikov, Ed.
+Request for Comments: 4422 Isode Limited
+Obsoletes: 2222 K. Zeilenga, Ed.
+Category: Standards Track OpenLDAP Foundation
+ June 2006
+
+
+ Simple Authentication and Security Layer (SASL)
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+Abstract
+
+ The Simple Authentication and Security Layer (SASL) is a framework
+ for providing authentication and data security services in
+ connection-oriented protocols via replaceable mechanisms. It
+ provides a structured interface between protocols and mechanisms.
+ The resulting framework allows new protocols to reuse existing
+ mechanisms and allows old protocols to make use of new mechanisms.
+ The framework also provides a protocol for securing subsequent
+ protocol exchanges within a data security layer.
+
+ This document describes how a SASL mechanism is structured, describes
+ how protocols include support for SASL, and defines the protocol for
+ carrying a data security layer over a connection. In addition, this
+ document defines one SASL mechanism, the EXTERNAL mechanism.
+
+ This document obsoletes RFC 2222.
+
+
+
+
+
+
+
+
+
+
+
+
+
+Melnikov & Zeilenga Standards Track [Page 1]
+
+RFC 4422 SASL June 2006
+
+
+Table of Contents
+
+ 1. Introduction ....................................................3
+ 1.1. Document Audiences .........................................4
+ 1.2. Relationship to Other Documents ............................4
+ 1.3. Conventions ................................................5
+ 2. Identity Concepts ...............................................5
+ 3. The Authentication Exchange .....................................6
+ 3.1. Mechanism Naming ...........................................8
+ 3.2. Mechanism Negotiation ......................................9
+ 3.3. Request Authentication Exchange ............................9
+ 3.4. Challenges and Responses ...................................9
+ 3.4.1. Authorization Identity String ......................10
+ 3.5. Aborting Authentication Exchanges .........................10
+ 3.6. Authentication Outcome ....................................11
+ 3.7. Security Layers ...........................................12
+ 3.8. Multiple Authentications ..................................12
+ 4. Protocol Requirements ..........................................13
+ 5. Mechanism Requirements .........................................16
+ 6. Security Considerations ........................................18
+ 6.1. Active Attacks ............................................19
+ 6.1.1. Hijack Attacks .....................................19
+ 6.1.2. Downgrade Attacks ..................................19
+ 6.1.3. Replay Attacks .....................................20
+ 6.1.4. Truncation Attacks .................................20
+ 6.1.5. Other Active Attacks ...............................20
+ 6.2. Passive Attacks ...........................................20
+ 6.3. Re-keying .................................................21
+ 6.4. Other Considerations ......................................21
+ 7. IANA Considerations ............................................22
+ 7.1. SASL Mechanism Registry ...................................22
+ 7.2. Registration Changes ......................................26
+ 8. References .....................................................26
+ 8.1. Normative References ......................................26
+ 8.2. Informative References ....................................27
+ 9. Acknowledgements ...............................................28
+ Appendix A. The SASL EXTERNAL Mechanism ..........................29
+ A.1. EXTERNAL Technical Specification ..........................29
+ A.2. SASL EXTERNAL Examples ....................................30
+ A.3. Security Considerations ...................................31
+ Appendix B. Changes since RFC 2222 ...............................31
+
+
+
+
+
+
+
+
+
+
+Melnikov & Zeilenga Standards Track [Page 2]
+
+RFC 4422 SASL June 2006
+
+
+1. Introduction
+
+ The Simple Authentication and Security Layer (SASL) is a framework
+ for providing authentication and data security services in
+ connection-oriented protocols via replaceable mechanisms. SASL
+ provides a structured interface between protocols and mechanisms.
+ SASL also provides a protocol for securing subsequent protocol
+ exchanges within a data security layer. The data security layer can
+ provide data integrity, data confidentiality, and other services.
+
+ SASL's design is intended to allow new protocols to reuse existing
+ mechanisms without requiring redesign of the mechanisms and allows
+ existing protocols to make use of new mechanisms without redesign of
+ protocols.
+
+ SASL is conceptually a framework that provides an abstraction layer
+ between protocols and mechanisms as illustrated in the following
+ diagram.
+
+ SMTP LDAP XMPP Other protocols ...
+ \ | | /
+ \ | | /
+ SASL abstraction layer
+ / | | \
+ / | | \
+ EXTERNAL GSSAPI PLAIN Other mechanisms ...
+
+ It is through the interfaces of this abstraction layer that the
+ framework allows any protocol to utilize any mechanism. While this
+ layer does generally hide the particulars of protocols from
+ mechanisms and the particulars of mechanisms from protocols, this
+ layer does not generally hide the particulars of mechanisms from
+ protocol implementations. For example, different mechanisms require
+ different information to operate, some of them use password-based
+ authentication, some of then require realm information, others make
+ use of Kerberos tickets, certificates, etc. Also, in order to
+ perform authorization, server implementations generally have to
+ implement identity mapping between authentication identities, whose
+ form is mechanism specific, and authorization identities, whose form
+ is application protocol specific. Section 2 discusses identity
+ concepts.
+
+ It is possible to design and implement this framework in ways that do
+ abstract away particulars of similar mechanisms. Such a framework
+ implementation, as well as mechanisms implementations, could be
+ designed not only to be shared by multiple implementations of a
+ particular protocol but to be shared by implementations of multiple
+ protocols.
+
+
+
+Melnikov & Zeilenga Standards Track [Page 3]
+
+RFC 4422 SASL June 2006
+
+
+ The framework incorporates interfaces with both protocols and
+ mechanisms in which authentication exchanges are carried out.
+ Section 3 discusses SASL authentication exchanges.
+
+ To use SASL, each protocol (amongst other items) provides a method
+ for identifying which mechanism is to be used, a method for exchange
+ of mechanism-specific server-challenges and client-responses, and a
+ method for communicating the outcome of the authentication exchange.
+ Section 4 discusses SASL protocol requirements.
+
+ Each SASL mechanism defines (amongst other items) a series of
+ server-challenges and client-responses that provide authentication
+ services and negotiate data security services. Section 5 discusses
+ SASL mechanism requirements.
+
+ Section 6 discusses security considerations. Section 7 discusses
+ IANA considerations. Appendix A defines the SASL EXTERNAL mechanism.
+
+1.1. Document Audiences
+
+ This document is written to serve several different audiences:
+
+ - protocol designers using this specification to support
+ authentication in their protocol,
+
+ - mechanism designers that define new SASL mechanisms, and
+
+ - implementors of clients or servers for those protocols that
+ support SASL.
+
+ While the document organization is intended to allow readers to focus
+ on details relevant to their engineering, readers are encouraged to
+ read and understand all aspects of this document.
+
+1.2. Relationship to Other Documents
+
+ This document obsoletes RFC 2222. It replaces all portions of RFC
+ 2222 excepting sections 7.1 (the KERBEROS_IV mechanism), 7.2 (the
+ GSSAPI mechanism), 7.3 (the SKEY mechanism). The KERBEROS_IV and
+ SKEY mechanisms are now viewed as obsolete and their specifications
+ provided in RFC 2222 are Historic. The GSSAPI mechanism is now
+ separately specified [SASL-GSSAPI].
+
+ Appendix B provides a summary of changes since RFC 2222.
+
+
+
+
+
+
+
+Melnikov & Zeilenga Standards Track [Page 4]
+
+RFC 4422 SASL June 2006
+
+
+1.3. Conventions
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in BCP 14 [RFC2119].
+
+ Character names in this document use the notation for code points and
+ names from the Unicode Standard [Unicode]. For example, the letter
+ "a" may be represented as either <U+0061> or <LATIN SMALL LETTER A>.
+
+ Note: a glossary of terms used in Unicode can be found in [Glossary].
+ Information on the Unicode character encoding model can be found in
+ [CharModel].
+
+ In examples, "C:" and "S:" indicate lines of data to be sent by the
+ client and server, respectively. Lines have been wrapped for
+ improved readability.
+
+2. Identity Concepts
+
+ In practice, authentication and authorization may involve multiple
+ identities, possibly in different forms (simple username, Kerberos
+ principal, X.500 Distinguished Name, etc.), possibly with different
+ representations (e.g., ABNF-described UTF-8 encoded Unicode character
+ string, BER-encoded Distinguished Name). While technical
+ specifications often prescribe both the identity form and
+ representation used on the network, different identity forms and/or
+ representations may be (and often are) used within implementations.
+ How identities of different forms relate to each other is, generally,
+ a local matter. In addition, the forms and representations used
+ within an implementation are a local matter.
+
+ However, conceptually, the SASL framework involves two identities:
+
+ 1) an identity associated with the authentication credentials
+ (termed the authentication identity), and
+
+ 2) an identity to act as (termed the authorization identity).
+
+ SASL mechanism specifications describe the credential form(s) (e.g.,
+ X.509 certificates, Kerberos tickets, simple username/password) used
+ to authenticate the client, including (where appropriate) the syntax
+ and semantics of authentication identities carried in the
+ credentials. SASL protocol specifications describe the identity
+ form(s) used in authorization and, in particular, prescribe the
+ syntax and semantics of the authorization identity character string
+ to be transferred by mechanisms.
+
+
+
+
+Melnikov & Zeilenga Standards Track [Page 5]
+
+RFC 4422 SASL June 2006
+
+
+ The client provides its credentials (which include or imply an
+ authentication identity) and, optionally, a character string
+ representing the requested authorization identity as part of the SASL
+ exchange. When this character string is omitted or empty, the client
+ is requesting to act as the identity associated with the credentials
+ (e.g., the user is requesting to act as the authentication identity).
+
+ The server is responsible for verifying the client's credentials and
+ verifying that the identity it associates with the client's
+ credentials (e.g., the authentication identity) is allowed to act as
+ the authorization identity. A SASL exchange fails if either (or
+ both) of these verifications fails. (The SASL exchange may fail for
+ other reasons, such as service authorization failure.)
+
+ However, the precise form(s) of the authentication identities (used
+ within the server in its verifications, or otherwise) and the precise
+ form(s) of the authorization identities (used in making authorization
+ decisions, or otherwise) are beyond the scope of SASL and this
+ specification. In some circumstances, the precise identity forms
+ used in some context outside of the SASL exchange may be dictated by
+ other specifications. For instance, an identity assumption
+ authorization (proxy authorization) policy specification may dictate
+ how authentication and authorization identities are represented in
+ policy statements.
+
+3. The Authentication Exchange
+
+ Each authentication exchange consists of a message from the client to
+ the server requesting authentication via a particular mechanism,
+ followed by one or more pairs of challenges from the server and
+ responses from the client, followed by a message from the server
+ indicating the outcome of the authentication exchange. (Note:
+ exchanges may also be aborted as discussed in Section 3.5.)
+
+ The following illustration provides a high-level overview of an
+ authentication exchange.
+
+ C: Request authentication exchange
+ S: Initial challenge
+ C: Initial response
+ <additional challenge/response messages>
+ S: Outcome of authentication exchange
+
+ If the outcome is successful and a security layer was negotiated,
+ this layer is then installed (see Section 3.7). This also applies to
+ the following illustrations.
+
+
+
+
+
+Melnikov & Zeilenga Standards Track [Page 6]
+
+RFC 4422 SASL June 2006
+
+
+ Some mechanisms specify that the first data sent in the
+ authentication exchange is from the client to the server. Protocols
+ may provide an optional initial response field in the request message
+ to carry this data. Where the mechanism specifies that the first
+ data sent in the exchange is from the client to the server, the
+ protocol provides an optional initial response field, and the client
+ uses this field, the exchange is shortened by one round-trip:
+
+ C: Request authentication exchange + Initial response
+ <additional challenge/response messages>
+ S: Outcome of authentication exchange
+
+ Where the mechanism specifies that the first data sent in the
+ exchange is from the client to the server and this field is
+ unavailable or unused, the client request is followed by an empty
+ challenge.
+
+ C: Request authentication exchange
+ S: Empty Challenge
+ C: Initial Response
+ <additional challenge/response messages>
+ S: Outcome of authentication exchange
+
+ Should a client include an initial response in its request where the
+ mechanism does not allow the client to send data first, the
+ authentication exchange fails.
+
+ Some mechanisms specify that the server is to send additional data to
+ the client when indicating a successful outcome. Protocols may
+ provide an optional additional data field in the outcome message to
+ carry this data. Where the mechanism specifies that the server is to
+ return additional data with the successful outcome, the protocol
+ provides an optional additional data field in the outcome message,
+ and the server uses this field, the exchange is shortened by one
+ round-trip:
+
+ C: Request authentication exchange
+ S: Initial challenge
+ C: Initial response
+ <additional challenge/response messages>
+ S: Outcome of authentication exchange with
+ additional data with success
+
+ Where the mechanism specifies that the server is to return additional
+ data to the client with a successful outcome and this field is
+ unavailable or unused, the additional data is sent as a challenge
+ whose response is empty. After receiving this response, the server
+ then indicates the successful outcome.
+
+
+
+Melnikov & Zeilenga Standards Track [Page 7]
+
+RFC 4422 SASL June 2006
+
+
+ C: Request authentication exchange
+ S: Initial challenge
+ C: Initial response
+ <additional challenge/response messages>
+ S: Additional data challenge
+ C: Empty Response
+ S: Outcome of authentication exchange
+
+ Where mechanisms specify that the first data sent in the exchange is
+ from the client to the server and additional data is sent to the
+ client along with indicating a successful outcome, and the protocol
+ provides fields supporting both, then the exchange takes two fewer
+ round-trips:
+
+ C: Request authentication exchange + Initial response
+ <additional challenge/response messages>
+ S: Outcome of authentication exchange
+ with additional data with success
+
+ instead of:
+
+ C: Request authentication exchange
+ S: Empty Challenge
+ C: Initial Response
+ <additional challenge/response messages>
+ S: Additional data challenge
+ C: Empty Response
+ S: Outcome of authentication exchange
+
+3.1. Mechanism Naming
+
+ SASL mechanisms are named by character strings, from 1 to 20
+ characters in length, consisting of ASCII [ASCII] uppercase letters,
+ digits, hyphens, and/or underscores. In the following Augmented
+ Backus-Naur Form (ABNF) [RFC4234] grammar, the <sasl-mech> production
+ defines the syntax of a SASL mechanism name.
+
+ sasl-mech = 1*20mech-char
+ mech-char = UPPER-ALPHA / DIGIT / HYPHEN / UNDERSCORE
+ ; mech-char is restricted to A-Z (uppercase only), 0-9, -, and _
+ ; from ASCII character set.
+
+ UPPER-ALPHA = %x41-5A ; A-Z (uppercase only)
+ DIGIT = %x30-39 ; 0-9
+ HYPHEN = %x2D ; hyphen (-)
+ UNDERSCORE = %x5F ; underscore (_)
+
+ SASL mechanism names are registered as discussed in Section 7.1.
+
+
+
+Melnikov & Zeilenga Standards Track [Page 8]
+
+RFC 4422 SASL June 2006
+
+
+3.2. Mechanism Negotiation
+
+ Mechanism negotiation is protocol specific.
+
+ Commonly, a protocol will specify that the server advertises
+ supported and available mechanisms to the client via some facility
+ provided by the protocol, and the client will then select the "best"
+ mechanism from this list that it supports and finds suitable.
+
+ Note that the mechanism negotiation is not protected by the
+ subsequent authentication exchange and hence is subject to downgrade
+ attacks if not protected by other means.
+
+ To detect downgrade attacks, a protocol can allow the client to
+ discover available mechanisms subsequent to the authentication
+ exchange and installation of data security layers with at least data
+ integrity protection. This allows the client to detect changes to
+ the list of mechanisms supported by the server.
+
+3.3. Request Authentication Exchange
+
+ The authentication exchange is initiated by the client by requesting
+ authentication via a mechanism it specifies. The client sends a
+ message that contains the name of the mechanism to the server. The
+ particulars of the message are protocol specific.
+
+ Note that the name of the mechanism is not protected by the
+ mechanism, and hence is subject to alteration by an attacker if not
+ integrity protected by other means.
+
+ Where the mechanism is defined to allow the client to send data
+ first, and the protocol's request message includes an optional
+ initial response field, the client may include the response to the
+ initial challenge in the authentication request message.
+
+3.4. Challenges and Responses
+
+ The authentication exchange involves one or more pairs of server-
+ challenges and client-responses, the particulars of which are
+ mechanism specific. These challenges and responses are enclosed in
+ protocol messages, the particulars of which are protocol specific.
+
+ Through these challenges and responses, the mechanism may:
+
+ - authenticate the client to the server,
+
+ - authenticate the server to the client,
+
+
+
+
+Melnikov & Zeilenga Standards Track [Page 9]
+
+RFC 4422 SASL June 2006
+
+
+ - transfer an authorization identity string,
+
+ - negotiate a security layer, and
+
+ - provide other services.
+
+ The negotiation of the security layer may involve negotiation of the
+ security services to be provided in the layer, how these services
+ will be provided, and negotiation of a maximum cipher-text buffer
+ size each side is able to receive in the layer (see Section 3.6).
+
+ After receiving an authentication request or any client response, the
+ server may issue a challenge, abort the exchange, or indicate the
+ outcome of an exchange. After receiving a challenge, a client
+ mechanism may issue a response or abort the exchange.
+
+3.4.1. Authorization Identity String
+
+ The authorization identity string is a sequence of zero or more
+ Unicode [Unicode] characters, excluding the NUL (U+0000) character,
+ representing the identity to act as.
+
+ If the authorization identity string is absent, the client is
+ requesting to act as the identity the server associates with the
+ client's credentials. An empty string is equivalent to an absent
+ authorization identity.
+
+ A non-empty authorization identity string indicates that the client
+ wishes to act as the identity represented by the string. In this
+ case, the form of identity represented by the string, as well as the
+ precise syntax and semantics of the string, is protocol specific.
+
+ While the character encoding schema used to transfer the
+ authorization identity string in the authentication exchange is
+ mechanism specific, mechanisms are expected to be capable of carrying
+ the entire Unicode repertoire (with the exception of the NUL
+ character).
+
+3.5. Aborting Authentication Exchanges
+
+ A client or server may desire to abort an authentication exchange if
+ it is unwilling or unable to continue (or enter into).
+
+ A client may abort the authentication exchange by sending a message,
+ the particulars of which are protocol specific, to the server,
+ indicating that the exchange is aborted. The server may be required
+ by the protocol to return a message in response to the client's abort
+ message.
+
+
+
+Melnikov & Zeilenga Standards Track [Page 10]
+
+RFC 4422 SASL June 2006
+
+
+ Likewise, a server may abort the authentication exchange by sending a
+ message, the particulars of which are protocol specific, to the
+ client, indicating that the exchange is aborted.
+
+3.6. Authentication Outcome
+
+ At the conclusion of the authentication exchange, the server sends a
+ message, the particulars of which are protocol specific, to the
+ client indicating the outcome of the exchange.
+
+ The outcome is not successful if
+
+ - the authentication exchange failed for any reason,
+
+ - the client's credentials could not be verified,
+
+ - the server cannot associate an identity with the client's
+ credentials,
+
+ - the client-provided authorization identity string is malformed,
+
+ - the identity associated with the client's credentials is not
+ authorized to act as the requested authorization identity,
+
+ - the negotiated security layer (or lack thereof) is not
+ suitable, or
+
+ - the server is not willing to provide service to the client for
+ any reason.
+
+ The protocol may include an optional additional data field in this
+ outcome message. This field can only include additional data when
+ the outcome is successful.
+
+ If the outcome is successful and a security layer was negotiated,
+ this layer is then installed. If the outcome is unsuccessful, or a
+ security layer was not negotiated, any existing security is left in
+ place.
+
+ The outcome message provided by the server can provide a way for the
+ client to distinguish between errors that are best dealt with by re-
+ prompting the user for her credentials, errors that are best dealt
+ with by telling the user to try again later, and errors where the
+ user must contact a system administrator for resolution (see the SYS
+ and AUTH POP Response Codes [RFC3206] specification for an example).
+ This distinction is particularly useful during scheduled server
+ maintenance periods as it reduces support costs. It is also
+ important that the server can be configured such that the outcome
+
+
+
+Melnikov & Zeilenga Standards Track [Page 11]
+
+RFC 4422 SASL June 2006
+
+
+ message will not distinguish between a valid user with invalid
+ credentials and an invalid user.
+
+3.7. Security Layers
+
+ SASL mechanisms may offer a wide range of services in security
+ layers. Typical services include data integrity and data
+ confidentiality. SASL mechanisms that do not provide a security
+ layer are treated as negotiating no security layer.
+
+ If use of a security layer is negotiated in the authentication
+ protocol exchange, the layer is installed by the server after
+ indicating the outcome of the authentication exchange and installed
+ by the client upon receipt of the outcome indication. In both cases,
+ the layer is installed before transfer of further protocol data. The
+ precise position upon which the layer takes effect in the protocol
+ data stream is protocol specific.
+
+ Once the security layer is in effect in the protocol data stream, it
+ remains in effect until either a subsequently negotiated security
+ layer is installed or the underlying transport connection is closed.
+
+ When in effect, the security layer processes protocol data into
+ buffers of protected data. If at any time the security layer is
+ unable or unwilling to continue producing buffers protecting protocol
+ data, the underlying transport connection MUST be closed. If the
+ security layer is not able to decode a received buffer, the
+ underlying connection MUST be closed. In both cases, the underlying
+ transport connection SHOULD be closed gracefully.
+
+ Each buffer of protected data is transferred over the underlying
+ transport connection as a sequence of octets prepended with a four-
+ octet field in network byte order that represents the length of the
+ buffer. The length of the protected data buffer MUST be no larger
+ than the maximum size that the other side expects. Upon the receipt
+ of a length field whose value is greater than the maximum size, the
+ receiver SHOULD close the connection, as this might be a sign of an
+ attack.
+
+ The maximum size that each side expects is fixed by the mechanism,
+ either through negotiation or by its specification.
+
+3.8. Multiple Authentications
+
+ Unless explicitly permitted in the protocol (as stated in the
+ protocol's technical specification), only one successful SASL
+ authentication exchange may occur in a protocol session. In this
+
+
+
+
+Melnikov & Zeilenga Standards Track [Page 12]
+
+RFC 4422 SASL June 2006
+
+
+ case, once an authentication exchange has successfully completed,
+ further attempts to initiate an authentication exchange fail.
+
+ Where multiple successful SASL authentication exchanges are permitted
+ in the protocol, then in no case may multiple SASL security layers be
+ simultaneously in effect. If a security layer is in effect and a
+ subsequent SASL negotiation selects a second security layer, then the
+ second security layer replaces the first. If a security layer is in
+ effect and a subsequent SASL negotiation selects no security layer,
+ the original security layer remains in effect.
+
+ Where multiple successful SASL negotiations are permitted in the
+ protocol, the effect of a failed SASL authentication exchange upon
+ the previously established authentication and authorization state is
+ protocol specific. The protocol's technical specification should be
+ consulted to determine whether the previous authentication and
+ authorization state remains in force, or changed to an anonymous
+ state, or otherwise was affected. Regardless of the protocol-
+ specific effect upon previously established authentication and
+ authorization state, the previously negotiated security layer remains
+ in effect.
+
+4. Protocol Requirements
+
+ In order for a protocol to offer SASL services, its specification
+ MUST supply the following information:
+
+ 1) A service name, to be selected from registry of "service" elements
+ for the Generic Security Service Application Program Interface
+ (GSSAPI) host-based service name form, as described in Section 4.1
+ of [RFC2743]. Note that this registry is shared by all GSSAPI and
+ SASL mechanisms.
+
+ 2) Detail any mechanism negotiation facility that the protocol
+ provides (see Section 3.2).
+
+ A protocol SHOULD specify a facility through which the client may
+ discover, both before initiation of the SASL exchange and after
+ installing security layers negotiated by the exchange, the names
+ of the SASL mechanisms that the server makes available to the
+ client. The latter is important to allow the client to detect
+ downgrade attacks. This facility is typically provided through
+ the protocol's extensions or capabilities discovery facility.
+
+ 3) Definition of the messages necessary for authentication exchange,
+ including the following:
+
+
+
+
+
+Melnikov & Zeilenga Standards Track [Page 13]
+
+RFC 4422 SASL June 2006
+
+
+ a) A message to initiate the authentication exchange (see Section
+ 3.3).
+
+ This message MUST contain a field for carrying the name of the
+ mechanism selected by the client.
+
+ This message SHOULD contain an optional field for carrying an
+ initial response. If the message is defined with this field,
+ the specification MUST describe how messages with an empty
+ initial response are distinguished from messages with no
+ initial response. This field MUST be capable of carrying
+ arbitrary sequences of octets (including zero-length sequences
+ and sequences containing zero-valued octets).
+
+ b) Messages to transfer server challenges and client responses
+ (see Section 3.4).
+
+ Each of these messages MUST be capable of carrying arbitrary
+ sequences of octets (including zero-length sequences and
+ sequences containing zero-valued octets).
+
+ c) A message to indicate the outcome of the authentication
+ exchange (see Section 3.6).
+
+ This message SHOULD contain an optional field for carrying
+ additional data with a successful outcome. If the message is
+ defined with this field, the specification MUST describe how
+ messages with an empty additional data are distinguished from
+ messages with no additional data. This field MUST be capable
+ of carrying arbitrary sequences of octets (including zero-
+ length sequences and sequences containing zero-valued octets).
+
+ 4) Prescribe the syntax and semantics of non-empty authorization
+ identity strings (see Section 3.4.1).
+
+ In order to avoid interoperability problems due to differing
+ normalizations, the protocol specification MUST detail precisely
+ how and where (client or server) non-empty authorization identity
+ strings are prepared, including all normalizations, for comparison
+ and other applicable functions to ensure proper function.
+
+ Specifications are encouraged to prescribe use of existing
+ authorization identity forms as well as existing string
+ representations, such as simple user names [RFC4013].
+
+ Where the specification does not precisely prescribe how
+ identities in SASL relate to identities used elsewhere in the
+ protocol, for instance, in access control policy statements, it
+
+
+
+Melnikov & Zeilenga Standards Track [Page 14]
+
+RFC 4422 SASL June 2006
+
+
+ may be appropriate for the protocol to provide a facility by which
+ the client can discover information (such as the representation of
+ the identity used in making access control decisions) about
+ established identities for these uses.
+
+ 5) Detail any facility the protocol provides that allows the client
+ and/or server to abort authentication exchange (see Section 3.5).
+
+ Protocols that support multiple authentications typically allow a
+ client to abort an ongoing authentication exchange by initiating a
+ new authentication exchange. Protocols that do not support
+ multiple authentications may require the client to close the
+ connection and start over to abort an ongoing authentication
+ exchange.
+
+ Protocols typically allow the server to abort ongoing
+ authentication exchanges by returning a non-successful outcome
+ message.
+
+ 6) Identify precisely where newly negotiated security layers start to
+ take effect, in both directions (see Section 3.7).
+
+ Typically, specifications require security layers to start taking
+ effect on the first octet following the outcome message in data
+ being sent by the server and on the first octet sent after receipt
+ of the outcome message in data being sent by the client.
+
+ 7) If the protocol supports other layered security services, such as
+ Transport Layer Security (TLS) [RFC4346], the specification MUST
+ prescribe the order in which security layers are applied to
+ protocol data.
+
+ For instance, where a protocol supports both TLS and SASL security
+ layers, the specification could prescribe any of the following:
+
+ a) SASL security layer is always applied first to data being sent
+ and, hence, applied last to received data,
+
+ b) SASL security layer is always applied last to data being sent
+ and, hence, applied first to received data,
+
+ c) Layers are applied in the order in which they were installed,
+
+ d) Layers are applied in the reverse order in which they were
+ installed, or
+
+ e) Both TLS and SASL security layers cannot be installed.
+
+
+
+
+Melnikov & Zeilenga Standards Track [Page 15]
+
+RFC 4422 SASL June 2006
+
+
+ 8) Indicate whether the protocol supports multiple authentications
+ (see Section 3.8). If so, the protocol MUST detail the effect a
+ failed SASL authentication exchange will have upon a previously
+ established authentication and authorization state.
+
+ Protocol specifications SHOULD avoid stating implementation
+ requirements that would hinder replacement of applicable mechanisms.
+ In general, protocol specifications SHOULD be mechanism neutral.
+ There are a number of reasonable exceptions to this recommendation,
+ including
+
+ - detailing how credentials (which are mechanism specific) are
+ managed in the protocol,
+
+ - detailing how authentication identities (which are mechanism
+ specific) and authorization identities (which are protocol
+ specific) relate to each other, and
+
+ - detailing which mechanisms are applicable to the protocol.
+
+5. Mechanism Requirements
+
+ SASL mechanism specifications MUST supply the following information:
+
+ 1) The name of the mechanism (see Section 3.1). This name MUST be
+ registered as discussed in Section 7.1.
+
+ 2) A definition of the server-challenges and client-responses of the
+ authentication exchange, as well as the following:
+
+ a) An indication of whether the mechanism is client-first,
+ variable, or server-first. If a SASL mechanism is defined as
+ client-first and the client does not send an initial response
+ in the authentication request, then the first server challenge
+ MUST be empty (the EXTERNAL mechanism is an example of this
+ case). If a SASL mechanism is defined as variable, then the
+ specification needs to state how the server behaves when the
+ initial client response in the authentication request is
+ omitted (the DIGEST-MD5 mechanism [DIGEST-MD5] is an example of
+ this case). If a SASL mechanism is defined as server-first,
+ then the client MUST NOT send an initial client response in the
+ authentication request (the CRAM-MD5 mechanism [CRAM-MD5] is an
+ example of this case).
+
+ b) An indication of whether the server is expected to provide
+ additional data when indicating a successful outcome. If so,
+ if the server sends the additional data as a challenge, the
+
+
+
+
+Melnikov & Zeilenga Standards Track [Page 16]
+
+RFC 4422 SASL June 2006
+
+
+ specification MUST indicate that the response to this challenge
+ is an empty response.
+
+ SASL mechanisms SHOULD be designed to minimize the number of
+ challenges and responses necessary to complete the exchange.
+
+ 3) An indication of whether the mechanism is capable of transferring
+ authorization identity strings (see Section 3.4.1). While some
+ legacy mechanisms are incapable of transmitting an authorization
+ identity (which means that for these mechanisms, the authorization
+ identity is always the empty string), newly defined mechanisms
+ SHOULD be capable of transferring authorization identity strings.
+ The mechanism SHOULD NOT be capable of transferring both no
+ authorization identity string and an empty authorization identity.
+
+ Mechanisms that are capable of transferring an authorization
+ identity string MUST be capable of transferring arbitrary non-
+ empty sequences of Unicode characters, excluding those that
+ contain the NUL (U+0000) character. Mechanisms SHOULD use the
+ UTF-8 [RFC3629] transformation format. The specification MUST
+ detail how any Unicode code points special to the mechanism that
+ might appear in the authorization identity string are escaped to
+ avoid ambiguity during decoding of the authorization identity
+ string. Typically, mechanisms that have special characters
+ require these special characters to be escaped or encoded in the
+ character string (after encoding it in a particular Unicode
+ transformation format) using a data encoding scheme such as Base64
+ [RFC3548].
+
+ 4) The specification MUST detail whether the mechanism offers a
+ security layer. If the mechanism does, the specification MUST
+ detail the security and other services offered in the layer as
+ well as how these services are to be implemented.
+
+ 5) If the underlying cryptographic technology used by a mechanism
+ supports data integrity, then the mechanism specification MUST
+ integrity protect the transmission of an authorization identity
+ and the negotiation of the security layer.
+
+ SASL mechanisms SHOULD be protocol neutral.
+
+ SASL mechanisms SHOULD reuse existing credential and identity forms,
+ as well as associated syntaxes and semantics.
+
+ SASL mechanisms SHOULD use the UTF-8 transformation format [RFC3629]
+ for encoding Unicode [Unicode] code points for transfer.
+
+
+
+
+
+Melnikov & Zeilenga Standards Track [Page 17]
+
+RFC 4422 SASL June 2006
+
+
+ In order to avoid interoperability problems due to differing
+ normalizations, when a mechanism calls for character data (other than
+ the authorization identity string) to be used as input to a
+ cryptographic and/or comparison function, the specification MUST
+ detail precisely how and where (client or server) the character data
+ is to be prepared, including all normalizations, for input into the
+ function to ensure proper operation.
+
+ For simple user names and/or passwords in authentication credentials,
+ SASLprep [RFC4013] (a profile of the StringPrep [RFC3454] preparation
+ algorithm), SHOULD be specified as the preparation algorithm.
+
+ The mechanism SHOULD NOT use the authorization identity string in
+ generation of any long-term cryptographic keys or hashes as there is
+ no requirement that the authorization identity string be canonical.
+ Long-term, here, means a term longer than the duration of the
+ authentication exchange in which they were generated. That is, as
+ different clients (of the same or different protocol) may provide
+ different authorization identity strings that are semantically
+ equivalent, use of authorization identity strings in generation of
+ cryptographic keys and hashes will likely lead to interoperability
+ and other problems.
+
+6. Security Considerations
+
+ Security issues are discussed throughout this memo.
+
+ Many existing SASL mechanisms do not provide adequate protection
+ against passive attacks, let alone active attacks, in the
+ authentication exchange. Many existing SASL mechanisms do not offer
+ security layers. It is hoped that future SASL mechanisms will
+ provide strong protection against passive and active attacks in the
+ authentication exchange, as well as security layers with strong basic
+ data security features (e.g., data integrity and data
+ confidentiality) services. It is also hoped that future mechanisms
+ will provide more advanced data security services like re-keying (see
+ Section 6.3).
+
+ Regardless, the SASL framework is susceptible to downgrade attacks.
+ Section 6.1.2 offers a variety of approaches for preventing or
+ detecting these attacks. In some cases, it is appropriate to use
+ data integrity protective services external to SASL (e.g., TLS) to
+ protect against downgrade attacks in SASL. Use of external
+ protective security services is also important when the mechanisms
+ available do not themselves offer adequate integrity and/or
+ confidentiality protection of the authentication exchange and/or
+ protocol data.
+
+
+
+
+Melnikov & Zeilenga Standards Track [Page 18]
+
+RFC 4422 SASL June 2006
+
+
+6.1. Active Attacks
+
+6.1.1. Hijack Attacks
+
+ When the client selects a SASL security layer with at least integrity
+ protection, this protection serves as a counter-measure against an
+ active attacker hijacking the connection and modifying protocol data
+ sent after establishment of the security layer. Implementations
+ SHOULD close the connection when the security services in a SASL
+ security layer report protocol data report lack of data integrity.
+
+6.1.2. Downgrade Attacks
+
+ It is important that any security-sensitive protocol negotiations be
+ performed after installation of a security layer with data integrity
+ protection. Protocols should be designed such that negotiations
+ performed prior to this installation should be revalidated after
+ installation is complete. Negotiation of the SASL mechanism is
+ security sensitive.
+
+ When a client negotiates the authentication mechanism with the server
+ and/or other security features, it is possible for an active attacker
+ to cause a party to use the least secure security services available.
+ For instance, an attacker can modify the server-advertised mechanism
+ list or can modify the client-advertised security feature list within
+ a mechanism response. To protect against this sort of attack,
+ implementations SHOULD NOT advertise mechanisms and/or features that
+ cannot meet their minimum security requirements, SHOULD NOT enter
+ into or continue authentication exchanges that cannot meet their
+ minimum security requirements, and SHOULD verify that completed
+ authentication exchanges result in security services that meet their
+ minimum security requirements. Note that each endpoint needs to
+ independently verify that its security requirements are met.
+
+ In order to detect downgrade attacks to the least (or less) secure
+ mechanism supported, the client can discover the SASL mechanisms that
+ the server makes available both before the SASL authentication
+ exchange and after the negotiated SASL security layer (with at least
+ data integrity protection) has been installed through the protocol's
+ mechanism discovery facility. If the client finds that the
+ integrity-protected list (the list obtained after the security layer
+ was installed) contains a stronger mechanism than those in the
+ previously obtained list, the client should assume that the
+ previously obtained list was modified by an attacker and SHOULD close
+ the underlying transport connection.
+
+ The client's initiation of the SASL exchange, including the selection
+ of a SASL mechanism, is done in the clear and may be modified by an
+
+
+
+Melnikov & Zeilenga Standards Track [Page 19]
+
+RFC 4422 SASL June 2006
+
+
+ active attacker. It is important for any new SASL mechanisms to be
+ designed such that an active attacker cannot obtain an authentication
+ with weaker security properties by modifying the SASL mechanism name
+ and/or the challenges and responses.
+
+ Multi-level negotiation of security features is prone to downgrade
+ attack. Protocol designers should avoid offering higher-level
+ negotiation of security features in protocols (e.g., above SASL
+ mechanism negotiation) and mechanism designers should avoid lower-
+ level negotiation of security features in mechanisms (e.g., below
+ SASL mechanism negotiation).
+
+6.1.3. Replay Attacks
+
+ Some mechanisms may be subject to replay attacks unless protected by
+ external data security services (e.g., TLS).
+
+6.1.4. Truncation Attacks
+
+ Most existing SASL security layers do not themselves offer protection
+ against truncation attack. In a truncation attack, the active
+ attacker causes the protocol session to be closed, causing a
+ truncation of the possibly integrity-protected data stream that leads
+ to behavior of one or both the protocol peers that inappropriately
+ benefits the attacker. Truncation attacks are fairly easy to defend
+ against in connection-oriented application-level protocols. A
+ protocol can defend against these attacks by ensuring that each
+ information exchange has a clear final result and that each protocol
+ session has a graceful closure mechanism, and that these are
+ integrity protected.
+
+6.1.5. Other Active Attacks
+
+ When use of a security layer is negotiated by the authentication
+ protocol exchange, the receiver SHOULD handle gracefully any
+ protected data buffer larger than the defined/negotiated maximal
+ size. In particular, it MUST NOT blindly allocate the amount of
+ memory specified in the buffer size field, as this might cause the
+ "out of memory" condition. If the receiver detects a large block, it
+ SHOULD close the connection.
+
+6.2. Passive Attacks
+
+ Many mechanisms are subject to various passive attacks, including
+ simple eavesdropping of unprotected credential information as well as
+ online and offline dictionary attacks of protected credential
+ information.
+
+
+
+
+Melnikov & Zeilenga Standards Track [Page 20]
+
+RFC 4422 SASL June 2006
+
+
+6.3. Re-keying
+
+ The secure or administratively permitted lifetimes of SASL
+ mechanisms' security layers are finite. Cryptographic keys weaken as
+ they are used and as time passes; the more time and/or cipher-text
+ that a cryptanalyst has after the first use of the a key, the easier
+ it is for the cryptanalyst to mount attacks on the key.
+
+ Administrative limits on a security layer's lifetime may take the
+ form of time limits expressed in X.509 certificates, in Kerberos V
+ tickets, or in directories, and are often desired. In practice, one
+ likely effect of administrative lifetime limits is that applications
+ may find that security layers stop working in the middle of
+ application protocol operation, such as, perhaps, during large data
+ transfers. As the result of this, the connection will be closed (see
+ Section 3.7), which will result in an unpleasant user experience.
+
+ Re-keying (key renegotiation process) is a way of addressing the
+ weakening of cryptographic keys. The SASL framework does not itself
+ provide for re-keying; SASL mechanisms may. Designers of future SASL
+ mechanisms should consider providing re-keying services.
+
+ Implementations that wish to re-key SASL security layers where the
+ mechanism does not provide for re-keying SHOULD reauthenticate the
+ same IDs and replace the expired or soon-to-expire security layers.
+ This approach requires support for reauthentication in the
+ application protocols (see Section 3.8).
+
+6.4. Other Considerations
+
+ Protocol designers and implementors should understand the security
+ considerations of mechanisms so they may select mechanisms that are
+ applicable to their needs.
+
+ Distributed server implementations need to be careful in how they
+ trust other parties. In particular, authentication secrets should
+ only be disclosed to other parties that are trusted to manage and use
+ those secrets in a manner acceptable to the disclosing party.
+ Applications using SASL assume that SASL security layers providing
+ data confidentiality are secure even when an attacker chooses the
+ text to be protected by the security layer. Similarly, applications
+ assume that the SASL security layer is secure even if the attacker
+ can manipulate the cipher-text output of the security layer. New
+ SASL mechanisms are expected to meet these assumptions.
+
+
+
+
+
+
+
+Melnikov & Zeilenga Standards Track [Page 21]
+
+RFC 4422 SASL June 2006
+
+
+ Unicode security considerations [UTR36] apply to authorization
+ identity strings, as well as UTF-8 [RFC3629] security considerations
+ where UTF-8 is used. SASLprep [RFC4013] and StringPrep [RFC3454]
+ security considerations also apply where used.
+
+7. IANA Considerations
+
+7.1. SASL Mechanism Registry
+
+ The SASL mechanism registry is maintained by IANA. The registry is
+ currently available at <http://www.iana.org/assignments/sasl-
+ mechanisms>.
+
+ The purpose of this registry is not only to ensure uniqueness of
+ values used to name SASL mechanisms, but also to provide a definitive
+ reference to technical specifications detailing each SASL mechanism
+ available for use on the Internet.
+
+ There is no naming convention for SASL mechanisms; any name that
+ conforms to the syntax of a SASL mechanism name can be registered.
+
+ The procedure detailed in Section 7.1.1 is to be used for
+ registration of a value naming a specific individual mechanism.
+
+ The procedure detailed in Section 7.1.2 is to be used for
+ registration of a value naming a family of related mechanisms.
+
+ Comments may be included in the registry as discussed in Section
+ 7.1.3 and may be changed as discussed in Section 7.1.4.
+
+ The SASL mechanism registry has been updated to reflect that this
+ document provides the definitive technical specification for SASL and
+ that this section provides the registration procedures for this
+ registry.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Melnikov & Zeilenga Standards Track [Page 22]
+
+RFC 4422 SASL June 2006
+
+
+7.1.1. Mechanism Name Registration Procedure
+
+ IANA will register new SASL mechanism names on a First Come First
+ Served basis, as defined in BCP 26 [RFC2434]. IANA has the right to
+ reject obviously bogus registration requests, but will perform no
+ review of claims made in the registration form.
+
+ Registration of a SASL mechanism is requested by filling in the
+ following template:
+
+ Subject: Registration of SASL mechanism X
+
+ SASL mechanism name (or prefix for the family):
+
+ Security considerations:
+
+ Published specification (recommended):
+
+ Person & email address to contact for further information:
+
+ Intended usage: (One of COMMON, LIMITED USE, or OBSOLETE)
+
+ Owner/Change controller:
+
+ Note: (Any other information that the author deems relevant may be
+ added here.)
+
+ and sending it via electronic mail to IANA at <iana@iana.org>.
+
+ While this registration procedure does not require expert review,
+ authors of SASL mechanisms are encouraged to seek community review
+ and comment whenever that is feasible. Authors may seek community
+ review by posting a specification of their proposed mechanism as an
+ Internet-Draft. SASL mechanisms intended for widespread use should
+ be standardized through the normal IETF process, when appropriate.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Melnikov & Zeilenga Standards Track [Page 23]
+
+RFC 4422 SASL June 2006
+
+
+7.1.2. Family Name Registration Procedure
+
+ As noted above, there is no general naming convention for SASL
+ mechanisms. However, specifications may reserve a portion of the
+ SASL mechanism namespace for a set of related SASL mechanisms, a
+ "family" of SASL mechanisms. Each family of SASL mechanisms is
+ identified by a unique prefix, such as X-. Registration of new SASL
+ mechanism family names requires expert review as defined in BCP 26
+ [RFC2434].
+
+ Registration of a SASL family name is requested by filling in the
+ following template:
+
+ Subject: Registration of SASL mechanism family X
+
+ SASL family name (or prefix for the family):
+
+ Security considerations:
+
+ Published specification (recommended):
+
+ Person & email address to contact for further information:
+
+ Intended usage: (One of COMMON, LIMITED USE, or OBSOLETE)
+
+ Owner/Change controller:
+
+ Note: (Any other information that the author deems relevant may be
+ added here.)
+
+ and sending it via electronic mail to the IETF SASL mailing list at
+ <ietf-sasl@imc.org> and carbon copying IANA at <iana@iana.org>.
+ After allowing two weeks for community input on the IETF SASL mailing
+ list, the expert will determine the appropriateness of the
+ registration request and either approve or disapprove the request
+ with notice to the requestor, the mailing list, and IANA.
+
+ The review should focus on the appropriateness of the requested
+ family name for the proposed use and the appropriateness of the
+ proposed naming and registration plan for existing and future
+ mechanism names in the family. The scope of this request review may
+ entail consideration of relevant aspects of any provided technical
+ specification, such as their IANA Considerations section. However,
+ this review is narrowly focused on the appropriateness of the
+ requested registration and not on the overall soundness of any
+ provided technical specification.
+
+
+
+
+
+Melnikov & Zeilenga Standards Track [Page 24]
+
+RFC 4422 SASL June 2006
+
+
+ Authors are encouraged to pursue community review by posting the
+ technical specification as an Internet-Draft and soliciting comment
+ by posting to appropriate IETF mailing lists.
+
+7.1.3. Comments on SASL Mechanism Registrations
+
+ Comments on a registered SASL mechanism/family should first be sent
+ to the "owner" of the mechanism/family and/or to the <ietf-
+ sasl@imc.org> mailing list.
+
+ Submitters of comments may, after a reasonable attempt to contact the
+ owner, request IANA to attach their comment to the SASL mechanism
+ registration itself by sending mail to <iana@iana.org>. At IANA's
+ sole discretion, IANA may attach the comment to the SASL mechanism's
+ registration.
+
+7.1.4. Change Control
+
+ Once a SASL mechanism registration has been published by IANA, the
+ author may request a change to its definition. The change request
+ follows the same procedure as the registration request.
+
+ The owner of a SASL mechanism may pass responsibility for the SASL
+ mechanism to another person or agency by informing IANA; this can be
+ done without discussion or review.
+
+ The IESG may reassign responsibility for a SASL mechanism. The most
+ common case of this will be to enable changes to be made to
+ mechanisms where the author of the registration has died, has moved
+ out of contact, or is otherwise unable to make changes that are
+ important to the community.
+
+ SASL mechanism registrations may not be deleted; mechanisms that are
+ no longer believed appropriate for use can be declared OBSOLETE by a
+ change to their "intended usage" field; such SASL mechanisms will be
+ clearly marked in the lists published by IANA.
+
+ The IESG is considered to be the owner of all SASL mechanisms that
+ are on the IETF standards track.
+
+
+
+
+
+
+
+
+
+
+
+
+Melnikov & Zeilenga Standards Track [Page 25]
+
+RFC 4422 SASL June 2006
+
+
+7.2. Registration Changes
+
+ The IANA has updated the SASL mechanisms registry as follows:
+
+ 1) Changed the "Intended usage" of the KERBEROS_V4 and SKEY mechanism
+ registrations to OBSOLETE.
+
+ 2) Changed the "Published specification" of the EXTERNAL mechanism to
+ this document as indicated below:
+
+ Subject: Updated Registration of SASL mechanism EXTERNAL
+ Family of SASL mechanisms: NO
+ SASL mechanism name: EXTERNAL
+ Security considerations: See A.3 of RFC 4422
+ Published specification (optional, recommended): RFC 4422
+ Person & email address to contact for further information:
+ Alexey Melnikov <Alexey.Melnikov@isode.com>
+ Intended usage: COMMON
+ Owner/Change controller: IESG <iesg@ietf.org>
+ Note: Updates existing entry for EXTERNAL
+
+8. References
+
+8.1. Normative References
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC2244] Newman, C. and J. G. Myers, "ACAP -- Application
+ Configuration Access Protocol", RFC 2244, November
+ 1997.
+
+ [RFC2434] Narten, T. and H. Alvestrand, "Guidelines for Writing
+ an IANA Considerations Section in RFCs", BCP 26, RFC
+ 2434, October 1998.
+
+ [RFC2743] Linn, J., "Generic Security Service Application Program
+ Interface Version 2, Update 1", RFC 2743, January 2000.
+
+ [RFC3454] Hoffman, P. and M. Blanchet, "Preparation of
+ Internationalized Strings ("stringprep")", RFC 3454,
+ December 2002.
+
+ [RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO
+ 10646", STD 63, RFC 3629, November 2003.
+
+ [RFC4013] Zeilenga, K., "SASLprep: Stringprep Profile for User
+ Names and Passwords", RFC 4013, February 2005.
+
+
+
+Melnikov & Zeilenga Standards Track [Page 26]
+
+RFC 4422 SASL June 2006
+
+
+ [RFC4234] Crocker, D. and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", RFC 4234, October 2005.
+
+ [ASCII] Coded Character Set--7-bit American Standard Code for
+ Information Interchange, ANSI X3.4-1986.
+
+ [Unicode] The Unicode Consortium, "The Unicode Standard, Version
+ 3.2.0" is defined by "The Unicode Standard, Version
+ 3.0" (Reading, MA, Addison-Wesley, 2000. ISBN 0-201-
+ 61633-5), as amended by the "Unicode Standard Annex
+ #27: Unicode 3.1"
+ (http://www.unicode.org/reports/tr27/) and by the
+ "Unicode Standard Annex #28: Unicode 3.2"
+ (http://www.unicode.org/reports/tr28/).
+
+ [CharModel] Whistler, K. and M. Davis, "Unicode Technical Report
+ #17, Character Encoding Model", UTR17,
+ <http://www.unicode.org/unicode/reports/tr17/>, August
+ 2000.
+
+ [Glossary] The Unicode Consortium, "Unicode Glossary",
+ <http://www.unicode.org/glossary/>.
+
+8.2. Informative References
+
+ [RFC3206] Gellens, R., "The SYS and AUTH POP Response Codes", RFC
+ 3206, February 2002.
+
+ [RFC3548] Josefsson, S., "The Base16, Base32, and Base64 Data
+ Encodings", RFC 3548, July 2003.
+
+ [RFC4301] Kent, S. and K. Seo, "Security Architecture for the
+ Internet Protocol", RFC 4301, December 2005.
+
+ [RFC4346] Dierks, T. and E. Rescorla, "The Transport Layer
+ Security (TLS) Protocol Version 1.1", RFC 4346, April
+ 2006.
+
+ [SASL-GSSAPI] Melnikov, A. (Editor), "The Kerberos V5 ("GSSAPI") SASL
+ Mechanism", Work in Progress, May 2006.
+
+ [UTR36] Davis, M., "(Draft) Unicode Technical Report #36,
+ Character Encoding Model", UTR17,
+ <http://www.unicode.org/unicode/reports/tr36/>,
+ February 2005.
+
+ [CRAM-MD5] Nerenberg, L., "The CRAM-MD5 SASL Mechanism", Work in
+ Progress.
+
+
+
+Melnikov & Zeilenga Standards Track [Page 27]
+
+RFC 4422 SASL June 2006
+
+
+ [DIGEST-MD5] Leach, P., C. Newman, and A. Melnikov, "Using Digest
+ Authentication as a SASL Mechanism", Work in Progress,
+ March 2006.
+
+9. Acknowledgements
+
+ This document is a revision of RFC 2222 written by John Myers.
+
+ This revision is a product of the IETF Simple Authentication and
+ Security Layer (SASL) Working Group.
+
+ The following individuals contributed significantly to this revision:
+ Abhijit Menon-Sen, Hallvard Furuseth, Jeffrey Hutzelman, John Myers,
+ Luke Howard, Magnus Nystrom, Nicolas Williams, Peter Saint-Andre, RL
+ 'Bob' Morgan, Rob Siemborski, Sam Hartman, Simon Josefsson, Tim
+ Alsop, and Tony Hansen.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Melnikov & Zeilenga Standards Track [Page 28]
+
+RFC 4422 SASL June 2006
+
+
+Appendix A. The SASL EXTERNAL Mechanism
+
+ This appendix is normative.
+
+ The EXTERNAL mechanism allows a client to request the server to use
+ credentials established by means external to the mechanism to
+ authenticate the client. The external means may be, for instance, IP
+ Security [RFC4301] or TLS [RFC4346] services. In absence of some a
+ priori agreement between the client and the server, the client cannot
+ make any assumption as to what external means the server has used to
+ obtain the client's credentials, nor make an assumption as to the
+ form of credentials. For example, the client cannot assume that the
+ server will use the credentials the client has established via TLS.
+
+A.1. EXTERNAL Technical Specification
+
+ The name of this mechanism is "EXTERNAL".
+
+ The mechanism does not provide a security layer.
+
+ The mechanism is capable of transferring an authorization identity
+ string. If empty, the client is requesting to act as the identity
+ the server has associated with the client's credentials. If non-
+ empty, the client is requesting to act as the identity represented by
+ the string.
+
+ The client is expected to send data first in the authentication
+ exchange. Where the client does not provide an initial response data
+ in its request to initiate the authentication exchange, the server is
+ to respond to the request with an empty initial challenge and then
+ the client is to provide its initial response.
+
+ The client sends the initial response containing the UTF-8 [RFC3629]
+ encoding of the requested authorization identity string. This
+ response is non-empty when the client is requesting to act as the
+ identity represented by the (non-empty) string. This response is
+ empty when the client is requesting to act as the identity the server
+ associated with its authentication credentials.
+
+ The syntax of the initial response is specified as a value of the
+ <extern-initial-resp> production detailed below using the Augmented
+ Backus-Naur Form (ABNF) [RFC4234] notation.
+
+ external-initial-resp = authz-id-string
+ authz-id-string = *( UTF8-char-no-nul )
+ UTF8-char-no-nul = UTF8-1-no-nul / UTF8-2 / UTF8-3 / UTF8-4
+ UTF8-1-no-nul = %x01-7F
+
+
+
+
+Melnikov & Zeilenga Standards Track [Page 29]
+
+RFC 4422 SASL June 2006
+
+
+ where the <UTF8-2>, <UTF8-3>, and <UTF8-4> productions are as defined
+ in [RFC3629].
+
+ There are no additional challenges and responses.
+
+ Hence, the server is to return the outcome of the authentication
+ exchange.
+
+ The exchange fails if
+
+ - the client has not established its credentials via external means,
+
+ - the client's credentials are inadequate,
+
+ - the client provided an empty authorization identity string and the
+ server is unwilling or unable to associate an authorization
+ identity with the client's credentials,
+
+ - the client provided a non-empty authorization identity string that
+ is invalid per the syntax requirements of the applicable
+ application protocol specification,
+
+ - the client provided a non-empty authorization identity string
+ representing an identity that the client is not allowed to act as,
+ or
+
+ - the server is unwilling or unable to provide service to the client
+ for any other reason.
+
+ Otherwise the exchange is successful. When indicating a successful
+ outcome, additional data is not provided.
+
+A.2. SASL EXTERNAL Examples
+
+ This section provides examples of EXTERNAL authentication exchanges.
+ The examples are intended to help the readers understand the above
+ text. The examples are not definitive. The Application
+ Configuration Access Protocol (ACAP) [RFC2244] is used in the
+ examples.
+
+ The first example shows use of EXTERNAL with an empty authorization
+ identity. In this example, the initial response is not sent in the
+ client's request to initiate the authentication exchange.
+
+ S: * ACAP (SASL "DIGEST-MD5")
+ C: a001 STARTTLS
+ S: a001 OK "Begin TLS negotiation now"
+ <TLS negotiation, further commands are under TLS layer>
+
+
+
+Melnikov & Zeilenga Standards Track [Page 30]
+
+RFC 4422 SASL June 2006
+
+
+ S: * ACAP (SASL "DIGEST-MD5" "EXTERNAL")
+ C: a002 AUTHENTICATE "EXTERNAL"
+ S: + ""
+ C: + ""
+ S: a002 OK "Authenticated"
+
+ The second example shows use of EXTERNAL with an authorization
+ identity of "fred@example.com". In this example, the initial
+ response is sent with the client's request to initiate the
+ authentication exchange. This saves a round-trip.
+
+ S: * ACAP (SASL "DIGEST-MD5")
+ C: a001 STARTTLS
+ S: a001 OK "Begin TLS negotiation now"
+ <TLS negotiation, further commands are under TLS layer>
+ S: * ACAP (SASL "DIGEST-MD5" "EXTERNAL")
+ C: a002 AUTHENTICATE "EXTERNAL" {16+}
+ C: fred@example.com
+ S: a002 NO "Cannot assume requested authorization identity"
+
+A.3. Security Considerations
+
+ The EXTERNAL mechanism provides no security protection; it is
+ vulnerable to spoofing by either client or server, active attack, and
+ eavesdropping. It should only be used when adequate security
+ services have been established.
+
+Appendix B. Changes since RFC 2222
+
+ This appendix is non-normative.
+
+ The material in RFC 2222 was significantly rewritten in the
+ production of this document.
+
+ RFC 2222, by not stating that the authorization identity string was a
+ string of Unicode characters, let alone character data, implied that
+ the authorization identity string was a string of octets.
+
+ - The authorization identity string is now defined as a string of
+ Unicode characters. The NUL (U+0000) character is prohibited.
+ While protocol specifications are responsible for defining the
+ authorization identity form, as well as the Unicode string syntax
+ and related semantics, mechanism specifications are responsible
+ for defining how the Unicode string is carried in the
+ authentication exchange.
+
+ - Deleted "If so, when the client does not send data first, the
+ initial challenge MUST be specified as being an empty challenge."
+
+
+
+Melnikov & Zeilenga Standards Track [Page 31]
+
+RFC 4422 SASL June 2006
+
+
+ The following technical change was made to the EXTERNAL mechanism:
+
+ - The authorization identity string is to be UTF-8 encoded.
+
+ Note that protocol and mechanism specification requirements have
+ been significantly tightened. Existing protocol and mechanism
+ specifications will need to be updated to meet these requirements.
+
+Editors' Addresses
+
+ Alexey Melnikov
+ Isode Limited
+ 5 Castle Business Village
+ 36 Station Road
+ Hampton, Middlesex,
+ TW12 2BX, United Kingdom
+
+ EMail: Alexey.Melnikov@isode.com
+ URI: http://www.melnikov.ca/
+
+
+ Kurt D. Zeilenga
+ OpenLDAP Foundation
+
+ EMail: Kurt@OpenLDAP.org
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Melnikov & Zeilenga Standards Track [Page 32]
+
+RFC 4422 SASL June 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is provided by the IETF
+ Administrative Support Activity (IASA).
+
+
+
+
+
+
+
+Melnikov & Zeilenga Standards Track [Page 33]
+
diff --git a/imap/docs/rfc/rfc4466.txt b/imap/docs/rfc/rfc4466.txt
new file mode 100644
index 00000000..dfde1685
--- /dev/null
+++ b/imap/docs/rfc/rfc4466.txt
@@ -0,0 +1,955 @@
+
+
+
+
+
+
+Network Working Group A. Melnikov
+Request for Comments: 4466 Isode Ltd.
+Updates: 2088, 2342, 3501, 3502, 3516 C. Daboo
+Category: Standards Track April 2006
+
+
+ Collected Extensions to IMAP4 ABNF
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+Abstract
+
+ Over the years, many documents from IMAPEXT and LEMONADE working
+ groups, as well as many individual documents, have added syntactic
+ extensions to many base IMAP commands described in RFC 3501. For
+ ease of reference, this document collects most of such ABNF changes
+ in one place.
+
+ This document also suggests a set of standard patterns for adding
+ options and extensions to several existing IMAP commands defined in
+ RFC 3501. The patterns provide for compatibility between existing
+ and future extensions.
+
+ This document updates ABNF in RFCs 2088, 2342, 3501, 3502, and 3516.
+ It also includes part of the errata to RFC 3501. This document
+ doesn't specify any semantic changes to the listed RFCs.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Melnikov & Daboo Standards Track [Page 1]
+
+RFC 4466 Collected Extensions to IMAP4 ABNF April 2006
+
+
+Table of Contents
+
+ 1. Introduction ....................................................2
+ 1.1. Purpose of This Document ...................................2
+ 1.2. Conventions Used in This Document ..........................3
+ 2. IMAP ABNF Extensions ............................................3
+ 2.1. Optional Parameters with the SELECT/EXAMINE Commands .......3
+ 2.2. Extended CREATE Command ....................................4
+ 2.3. Extended RENAME Command ....................................5
+ 2.4. Extensions to FETCH and UID FETCH Commands .................6
+ 2.5. Extensions to STORE and UID STORE Commands .................6
+ 2.6. Extensions to SEARCH Command ...............................7
+ 2.6.1. Extended SEARCH Command .............................7
+ 2.6.2. ESEARCH untagged response ...........................8
+ 2.7. Extensions to APPEND Command ...............................8
+ 3. Formal Syntax ...................................................9
+ 4. Security Considerations ........................................14
+ 5. Normative References ...........................................15
+ 6. Acknowledgements ...............................................15
+
+1. Introduction
+
+1.1. Purpose of This Document
+
+ This document serves several purposes:
+
+ 1. rationalize and generalize ABNF for some existing IMAP
+ extensions;
+ 2. collect the ABNF in one place in order to minimize cross
+ references between documents;
+ 3. define building blocks for future extensions so that they can
+ be used together in a compatible way.
+
+ It is expected that a future revision of this document will be
+ incorporated into a revision of RFC 3501.
+
+ This document updates ABNF in RFCs 2088, 2342, 3501, 3502, and 3516.
+ It also includes part of the errata to RFC 3501. This document
+ doesn't specify any semantic changes to the listed RFCs.
+
+ The ABNF in section 6 of RFC 2342 got rewritten to conform to the
+ ABNF syntax as defined in RFC 4234 and to reference new non-terminals
+ from RFC 3501. It was also restructured to allow for better
+ readability. There were no changes "on the wire".
+
+ Section 2 extends ABNF for SELECT, EXAMINE, CREATE, RENAME, FETCH/UID
+ FETCH, STORE/UID STORE, SEARCH, and APPEND commands in a consistent
+ manner. Extensions to all the commands but APPEND have the same
+
+
+
+Melnikov & Daboo Standards Track [Page 2]
+
+RFC 4466 Collected Extensions to IMAP4 ABNF April 2006
+
+
+ structure. Extensibility for the APPEND command was done slightly
+ differently in order to preserve backward compatibility with existing
+ extensions.
+
+ Section 2 also defines a new ESEARCH response, whose purpose is to
+ define a better version of the SEARCH response defined in RFC 3501.
+
+ Section 3 defines the collected ABNF that replaces pieces of ABNF in
+ the aforementioned RFCs. The collected ABNF got generalized to allow
+ for easier future extensibility.
+
+1.2. Conventions Used in This Document
+
+ In examples, "C:" and "S:" indicate lines sent by the client and
+ server, respectively.
+
+ The key words "MUST", "MUST NOT", "SHOULD", "SHOULD NOT", and "MAY"
+ in this document are to be interpreted as defined in "Key words for
+ use in RFCs to Indicate Requirement Levels" [KEYWORDS].
+
+2. IMAP ABNF Extensions
+
+ This section is not normative. It provides some background on the
+ intended use of different extensions and it gives some guidance about
+ how future extensions should extend the described commands.
+
+2.1. Optional Parameters with the SELECT/EXAMINE Commands
+
+ This document adds the ability to include one or more parameters with
+ the IMAP SELECT (section 6.3.1 of [IMAP4]) or EXAMINE (section 6.3.2
+ of [IMAP4]) commands, to turn on or off certain standard behaviors,
+ or to add new optional behaviors required for a particular extension.
+
+ There are two possible modes of operation:
+
+ o A global state change where a single use of the optional parameter
+ will affect the session state from that time on, irrespective of
+ subsequent SELECT/EXAMINE commands.
+
+ o A per-mailbox state change that will affect the session only for
+ the duration of the new selected state. A subsequent
+ SELECT/EXAMINE without the optional parameter will cancel its
+ effect for the newly selected mailbox.
+
+ Optional parameters to the SELECT or EXAMINE commands are added as a
+ parenthesized list of attribute/value pairs, and appear after the
+ mailbox name in the standard SELECT or EXAMINE command. The order of
+ individual parameters is arbitrary. A parameter value is optional
+
+
+
+Melnikov & Daboo Standards Track [Page 3]
+
+RFC 4466 Collected Extensions to IMAP4 ABNF April 2006
+
+
+ and may consist of atoms, strings, or lists in a specific order. If
+ the parameter value is present, it always appears in parentheses (*).
+ Any parameter not defined by extensions that the server supports must
+ be rejected with a BAD response.
+
+ Example:
+
+ C: a SELECT INBOX (ANNOTATE)
+ S: ...
+ S: a OK SELECT complete
+
+ In the above example, a single parameter is used with the SELECT
+ command.
+
+ Example:
+
+ C: a EXAMINE INBOX (ANNOTATE RESPONSES ("UID Responses")
+ CONDSTORE)
+ S: ...
+ S: a OK EXAMINE complete
+
+ In the above example, three parameters are used with the EXAMINE
+ command. The second parameter consists of two items: an atom
+ "RESPONSES" followed by a quoted string.
+
+ Example:
+
+ C: a SELECT INBOX (BLURDYBLOOP)
+ S: a BAD Unknown parameter in SELECT command
+
+ In the above example, a parameter not supported by the server is
+ used. This results in the BAD response from the server.
+
+ (*) - if a parameter has a mandatory value, which can always be
+ represented as a number or a sequence-set, the parameter value does
+ not need the enclosing (). See ABNF for more details.
+
+2.2. Extended CREATE Command
+
+ Arguments: mailbox name
+ OPTIONAL list of CREATE parameters
+
+ Responses: no specific responses for this command
+
+ Result: OK - create completed
+ NO - create failure: cannot create mailbox with
+ that name
+ BAD - argument(s) invalid
+
+
+
+Melnikov & Daboo Standards Track [Page 4]
+
+RFC 4466 Collected Extensions to IMAP4 ABNF April 2006
+
+
+ This document adds the ability to include one or more parameters with
+ the IMAP CREATE command (see section 6.3.3 of [IMAP4]), to turn on or
+ off certain standard behaviors, or to add new optional behaviors
+ required for a particular extension. No CREATE parameters are
+ defined in this document.
+
+ Optional parameters to the CREATE command are added as a
+ parenthesized list of attribute/value pairs after the mailbox name.
+ The order of individual parameters is arbitrary. A parameter value
+ is optional and may consist of atoms, strings, or lists in a specific
+ order. If the parameter value is present, it always appears in
+ parentheses (*). Any parameter not defined by extensions that the
+ server supports must be rejected with a BAD response.
+
+ (*) - if a parameter has a mandatory value, which can always be
+ represented as a number or a sequence-set, the parameter value does
+ not need the enclosing (). See ABNF for more details.
+
+2.3. Extended RENAME Command
+
+ Arguments: existing mailbox name
+ new mailbox name
+ OPTIONAL list of RENAME parameters
+
+ Responses: no specific responses for this command
+
+ Result: OK - rename completed
+ NO - rename failure: cannot rename mailbox with
+ that name, cannot rename to mailbox with
+ that name, etc.
+ BAD - argument(s) invalid
+
+ This document adds the ability to include one or more parameters with
+ the IMAP RENAME command (see section 6.3.5 of [IMAP4]), to turn on or
+ off certain standard behaviors, or to add new optional behaviors
+ required for a particular extension. No RENAME parameters are
+ defined in this document.
+
+ Optional parameters to the RENAME command are added as a
+ parenthesized list of attribute/value pairs after the new mailbox
+ name. The order of individual parameters is arbitrary. A parameter
+ value is optional and may consist of atoms, strings, or lists in a
+ specific order. If the parameter value is present, it always appears
+ in parentheses (*). Any parameter not defined by extensions that the
+ server supports must be rejected with a BAD response.
+
+
+
+
+
+
+Melnikov & Daboo Standards Track [Page 5]
+
+RFC 4466 Collected Extensions to IMAP4 ABNF April 2006
+
+
+ (*) - if a parameter has a mandatory value, which can always be
+ represented as a number or a sequence-set, the parameter value does
+ not need the enclosing (). See ABNF for more details.
+
+2.4. Extensions to FETCH and UID FETCH Commands
+
+ Arguments: sequence set
+ message data item names or macro
+ OPTIONAL fetch modifiers
+
+ Responses: untagged responses: FETCH
+
+ Result: OK - fetch completed
+ NO - fetch error: cannot fetch that data
+ BAD - command unknown or arguments invalid
+
+ This document extends the syntax of the FETCH and UID FETCH commands
+ (see section 6.4.5 of [IMAP4]) to include optional FETCH modifiers.
+ No fetch modifiers are defined in this document.
+
+ The order of individual modifiers is arbitrary. Each modifier is an
+ attribute/value pair. A modifier value is optional and may consist
+ of atoms and/or strings and/or lists in a specific order. If the
+ modifier value is present, it always appears in parentheses (*). Any
+ modifiers not defined by extensions that the server supports must be
+ rejected with a BAD response.
+
+ (*) - if a modifier has a mandatory value, which can always be
+ represented as a number or a sequence-set, the modifier value does
+ not need the enclosing (). See ABNF for more details.
+
+2.5. Extensions to STORE and UID STORE Commands
+
+ Arguments: message set
+ OPTIONAL store modifiers
+ message data item name
+ value for message data item
+
+ Responses: untagged responses: FETCH
+
+ Result: OK - store completed
+ NO - store error: cannot store that data
+ BAD - command unknown or arguments invalid
+
+ This document extends the syntax of the STORE and UID STORE commands
+ (see section 6.4.6 of [IMAP4]) to include optional STORE modifiers.
+ No store modifiers are defined in this document.
+
+
+
+
+Melnikov & Daboo Standards Track [Page 6]
+
+RFC 4466 Collected Extensions to IMAP4 ABNF April 2006
+
+
+ The order of individual modifiers is arbitrary. Each modifier is an
+ attribute/value pair. A modifier value is optional and may consist
+ of atoms and/or strings and/or lists in a specific order. If the
+ modifier value is present, it always appears in parentheses (*). Any
+ modifiers not defined by extensions that the server supports must be
+ rejected with a BAD response.
+
+ (*) - if a modifier has a mandatory value, which can always be
+ represented as a number or a sequence-set, the modifier value does
+ not need the enclosing (). See ABNF for more details.
+
+2.6. Extensions to SEARCH Command
+
+2.6.1. Extended SEARCH Command
+
+ Arguments: OPTIONAL result specifier
+ OPTIONAL [CHARSET] specification
+ searching criteria (one or more)
+
+ Responses: REQUIRED untagged response: SEARCH (*)
+
+ Result: OK - search completed
+ NO - search error: cannot search that [CHARSET] or
+ criteria
+ BAD - command unknown or arguments invalid
+
+ This section updates definition of the SEARCH command described in
+ section 6.4.4 of [IMAP4].
+
+ The SEARCH command is extended to allow for result options. This
+ document does not define any result options.
+
+ The order of individual options is arbitrary. Individual options may
+ contain parameters enclosed in parentheses (**). If an option has
+ parameters, they consist of atoms and/or strings and/or lists in a
+ specific order. Any options not defined by extensions that the
+ server supports must be rejected with a BAD response.
+
+ (*) - An extension to the SEARCH command may require another untagged
+ response, or no untagged response to be returned. Section 2.6.2
+ defines a new ESEARCH untagged response that replaces the SEARCH
+ untagged response. Note that for a given extended SEARCH command the
+ SEARCH and ESEARCH responses SHOULD be mutually exclusive, i.e., only
+ one of them should be returned.
+
+ (**) - if an option has a mandatory parameter, which can always be
+ represented as a number or a sequence-set, the option parameter does
+ not need the enclosing (). See ABNF for more details.
+
+
+
+Melnikov & Daboo Standards Track [Page 7]
+
+RFC 4466 Collected Extensions to IMAP4 ABNF April 2006
+
+
+2.6.2. ESEARCH untagged response
+
+ Contents: one or more search-return-data pairs
+
+ The ESEARCH response SHOULD be sent as a result of an extended SEARCH
+ or UID SEARCH command specified in section 2.6.1.
+
+ The ESEARCH response starts with an optional search correlator. If
+ it is missing, then the response was not caused by a particular IMAP
+ command, whereas if it is present, it contains the tag of the command
+ that caused the response to be returned.
+
+ The search correlator is followed by an optional UID indicator. If
+ this indicator is present, all data in the ESEARCH response refers to
+ UIDs, otherwise all returned data refers to message numbers.
+
+ The rest of the ESEARCH response contains one or more search data
+ pairs. Each pair starts with unique return item name, followed by a
+ space and the corresponding data. Search data pairs may be returned
+ in any order. Unless specified otherwise by an extension, any return
+ item name SHOULD appear only once in an ESEARCH response.
+
+ Example: S: * ESEARCH UID COUNT 5 ALL 4:19,21,28
+
+ Example: S: * ESEARCH (TAG "a567") UID COUNT 5 ALL 4:19,21,28
+
+ Example: S: * ESEARCH COUNT 5 ALL 1:17,21
+
+2.7. Extensions to APPEND Command
+
+ The IMAP BINARY extension [BINARY] extends the APPEND command to
+ allow a client to append data containing NULs by using the <literal8>
+ syntax. The ABNF was rewritten to allow for easier extensibility by
+ IMAP extensions. This document hasn't specified any semantical
+ changes to the [BINARY] extension.
+
+ In addition, the non-terminal "literal8" defined in [BINARY] got
+ extended to allow for non-synchronizing literals if both [BINARY] and
+ [LITERAL+] extensions are supported by the server.
+
+ The IMAP MULTIAPPEND extension [MULTIAPPEND] extends the APPEND
+ command to allow a client to append multiple messages atomically.
+ This document defines a common syntax for the APPEND command that
+ takes into consideration syntactic extensions defined by both
+ [BINARY] and [MULTIAPPEND] extensions.
+
+
+
+
+
+
+Melnikov & Daboo Standards Track [Page 8]
+
+RFC 4466 Collected Extensions to IMAP4 ABNF April 2006
+
+
+3. Formal Syntax
+
+ The following syntax specification uses the Augmented Backus-Naur
+ Form (ABNF) notation as specified in [ABNF].
+
+ Non-terminals referenced but not defined below are as defined by
+ [IMAP4].
+
+ Except as noted otherwise, all alphabetic characters are case-
+ insensitive. The use of uppercase or lowercase characters to define
+ token strings is for editorial clarity only. Implementations MUST
+ accept these strings in a case-insensitive fashion.
+
+ append = "APPEND" SP mailbox 1*append-message
+ ;; only a single append-message may appear
+ ;; if MULTIAPPEND [MULTIAPPEND] capability
+ ;; is not present
+
+ append-message = append-opts SP append-data
+
+ append-ext = append-ext-name SP append-ext-value
+ ;; This non-terminal define extensions to
+ ;; to message metadata.
+
+ append-ext-name = tagged-ext-label
+
+ append-ext-value= tagged-ext-val
+ ;; This non-terminal shows recommended syntax
+ ;; for future extensions.
+
+
+ append-data = literal / literal8 / append-data-ext
+
+ append-data-ext = tagged-ext
+ ;; This non-terminal shows recommended syntax
+ ;; for future extensions,
+ ;; i.e., a mandatory label followed
+ ;; by parameters.
+
+ append-opts = [SP flag-list] [SP date-time] *(SP append-ext)
+ ;; message metadata
+
+ charset = atom / quoted
+ ;; Exact syntax is defined in [CHARSET].
+
+ create = "CREATE" SP mailbox
+ [create-params]
+ ;; Use of INBOX gives a NO error.
+
+
+
+Melnikov & Daboo Standards Track [Page 9]
+
+RFC 4466 Collected Extensions to IMAP4 ABNF April 2006
+
+
+ create-params = SP "(" create-param *( SP create-param) ")"
+
+ create-param-name = tagged-ext-label
+
+ create-param = create-param-name [SP create-param-value]
+
+ create-param-value= tagged-ext-val
+ ;; This non-terminal shows recommended syntax
+ ;; for future extensions.
+
+
+ esearch-response = "ESEARCH" [search-correlator] [SP "UID"]
+ *(SP search-return-data)
+ ;; Note that SEARCH and ESEARCH responses
+ ;; SHOULD be mutually exclusive,
+ ;; i.e., only one of the response types
+ ;; should be
+ ;; returned as a result of a command.
+
+
+ examine = "EXAMINE" SP mailbox [select-params]
+ ;; modifies the original IMAP EXAMINE command
+ ;; to accept optional parameters
+
+ fetch = "FETCH" SP sequence-set SP ("ALL" / "FULL" /
+ "FAST" / fetch-att /
+ "(" fetch-att *(SP fetch-att) ")")
+ [fetch-modifiers]
+ ;; modifies the original IMAP4 FETCH command to
+ ;; accept optional modifiers
+
+ fetch-modifiers = SP "(" fetch-modifier *(SP fetch-modifier) ")"
+
+ fetch-modifier = fetch-modifier-name [ SP fetch-modif-params ]
+
+ fetch-modif-params = tagged-ext-val
+ ;; This non-terminal shows recommended syntax
+ ;; for future extensions.
+
+ fetch-modifier-name = tagged-ext-label
+
+ literal8 = "~{" number ["+"] "}" CRLF *OCTET
+ ;; A string that might contain NULs.
+ ;; <number> represents the number of OCTETs
+ ;; in the response string.
+ ;; The "+" is only allowed when both LITERAL+ and
+ ;; BINARY extensions are supported by the server.
+
+
+
+
+Melnikov & Daboo Standards Track [Page 10]
+
+RFC 4466 Collected Extensions to IMAP4 ABNF April 2006
+
+
+ mailbox-data =/ Namespace-Response /
+ esearch-response
+
+ Namespace = nil / "(" 1*Namespace-Descr ")"
+
+ Namespace-Command = "NAMESPACE"
+
+ Namespace-Descr = "(" string SP
+ (DQUOTE QUOTED-CHAR DQUOTE / nil)
+ *(Namespace-Response-Extension) ")"
+
+ Namespace-Response-Extension = SP string SP
+ "(" string *(SP string) ")"
+
+ Namespace-Response = "NAMESPACE" SP Namespace
+ SP Namespace SP Namespace
+ ;; This response is currently only allowed
+ ;; if the IMAP server supports [NAMESPACE].
+ ;; The first Namespace is the Personal Namespace(s)
+ ;; The second Namespace is the Other Users' Namespace(s)
+ ;; The third Namespace is the Shared Namespace(s)
+
+ rename = "RENAME" SP mailbox SP mailbox
+ [rename-params]
+ ;; Use of INBOX as a destination gives
+ ;; a NO error, unless rename-params
+ ;; is not empty.
+
+ rename-params = SP "(" rename-param *( SP rename-param) ")"
+
+ rename-param = rename-param-name [SP rename-param-value]
+
+ rename-param-name = tagged-ext-label
+
+ rename-param-value= tagged-ext-val
+ ;; This non-terminal shows recommended syntax
+ ;; for future extensions.
+
+
+ response-data = "*" SP response-payload CRLF
+
+ response-payload= resp-cond-state / resp-cond-bye /
+ mailbox-data / message-data / capability-data
+
+ search = "SEARCH" [search-return-opts]
+ SP search-program
+
+ search-correlator = SP "(" "TAG" SP tag-string ")"
+
+
+
+Melnikov & Daboo Standards Track [Page 11]
+
+RFC 4466 Collected Extensions to IMAP4 ABNF April 2006
+
+
+ search-program = ["CHARSET" SP charset SP]
+ search-key *(SP search-key)
+ ;; CHARSET argument to SEARCH MUST be
+ ;; registered with IANA.
+
+ search-return-data = search-modifier-name SP search-return-value
+ ;; Note that not every SEARCH return option
+ ;; is required to have the corresponding
+ ;; ESEARCH return data.
+
+ search-return-opts = SP "RETURN" SP "(" [search-return-opt
+ *(SP search-return-opt)] ")"
+
+ search-return-opt = search-modifier-name [SP search-mod-params]
+
+ search-return-value = tagged-ext-val
+ ;; Data for the returned search option.
+ ;; A single "nz-number"/"number" value
+ ;; can be returned as an atom (i.e., without
+ ;; quoting). A sequence-set can be returned
+ ;; as an atom as well.
+
+ search-modifier-name = tagged-ext-label
+
+ search-mod-params = tagged-ext-val
+ ;; This non-terminal shows recommended syntax
+ ;; for future extensions.
+
+
+ select = "SELECT" SP mailbox [select-params]
+ ;; modifies the original IMAP SELECT command to
+ ;; accept optional parameters
+
+ select-params = SP "(" select-param *(SP select-param) ")"
+
+ select-param = select-param-name [SP select-param-value]
+ ;; a parameter to SELECT may contain one or
+ ;; more atoms and/or strings and/or lists.
+
+ select-param-name= tagged-ext-label
+
+ select-param-value= tagged-ext-val
+ ;; This non-terminal shows recommended syntax
+ ;; for future extensions.
+
+
+ status-att-list = status-att-val *(SP status-att-val)
+ ;; Redefines status-att-list from RFC 3501.
+
+
+
+Melnikov & Daboo Standards Track [Page 12]
+
+RFC 4466 Collected Extensions to IMAP4 ABNF April 2006
+
+
+ ;; status-att-val is defined in RFC 3501 errata
+
+ status-att-val = ("MESSAGES" SP number) /
+ ("RECENT" SP number) /
+ ("UIDNEXT" SP nz-number) /
+ ("UIDVALIDITY" SP nz-number) /
+ ("UNSEEN" SP number)
+ ;; Extensions to the STATUS responses
+ ;; should extend this production.
+ ;; Extensions should use the generic
+ ;; syntax defined by tagged-ext.
+
+ store = "STORE" SP sequence-set [store-modifiers]
+ SP store-att-flags
+ ;; extend [IMAP4] STORE command syntax
+ ;; to allow for optional store-modifiers
+
+ store-modifiers = SP "(" store-modifier *(SP store-modifier)
+ ")"
+
+ store-modifier = store-modifier-name [SP store-modif-params]
+
+ store-modif-params = tagged-ext-val
+ ;; This non-terminal shows recommended syntax
+ ;; for future extensions.
+
+ store-modifier-name = tagged-ext-label
+
+ tag-string = string
+ ;; tag of the command that caused
+ ;; the ESEARCH response, sent as
+ ;; a string.
+
+ tagged-ext = tagged-ext-label SP tagged-ext-val
+ ;; recommended overarching syntax for
+ ;; extensions
+
+ tagged-ext-label = tagged-label-fchar *tagged-label-char
+ ;; Is a valid RFC 3501 "atom".
+
+ tagged-label-fchar = ALPHA / "-" / "_" / "."
+
+ tagged-label-char = tagged-label-fchar / DIGIT / ":"
+
+
+
+
+
+
+
+
+Melnikov & Daboo Standards Track [Page 13]
+
+RFC 4466 Collected Extensions to IMAP4 ABNF April 2006
+
+
+ tagged-ext-comp = astring /
+ tagged-ext-comp *(SP tagged-ext-comp) /
+ "(" tagged-ext-comp ")"
+ ;; Extensions that follow this general
+ ;; syntax should use nstring instead of
+ ;; astring when appropriate in the context
+ ;; of the extension.
+ ;; Note that a message set or a "number"
+ ;; can always be represented as an "atom".
+ ;; An URL should be represented as
+ ;; a "quoted" string.
+
+ tagged-ext-simple = sequence-set / number
+
+ tagged-ext-val = tagged-ext-simple /
+ "(" [tagged-ext-comp] ")"
+
+4. Security Considerations
+
+ This document updates ABNF in RFCs 2088, 2342, 3501, 3502, and 3516.
+ The updated documents must be consulted for security considerations
+ for the extensions that they define.
+
+ As a protocol gets more complex, parser bugs become more common
+ including buffer overflow, denial of service, and other common
+ security coding errors. To the extent that this document makes the
+ parser more complex, it makes this situation worse. To the extent
+ that this document makes the parser more consistent and thus simpler,
+ the situation is improved. The impact will depend on how many
+ deployed IMAP extensions are consistent with this document.
+ Implementers are encouraged to take care of these issues when
+ extending existing implementations. Future IMAP extensions should
+ strive for consistency and simplicity to the greatest extent
+ possible.
+
+ Extensions to IMAP commands that are permitted in NOT AUTHENTICATED
+ state are more sensitive to these security issues due to the larger
+ possible attacker community prior to authentication, and the fact
+ that some IMAP servers run with elevated privileges in that state.
+ This document does not extend any commands permitted in NOT
+ AUTHENTICATED state. Future IMAP extensions to commands permitted in
+ NOT AUTHENTICATED state should favor simplicity over consistency or
+ extensibility.
+
+
+
+
+
+
+
+
+Melnikov & Daboo Standards Track [Page 14]
+
+RFC 4466 Collected Extensions to IMAP4 ABNF April 2006
+
+
+5. Normative References
+
+ [KEYWORDS] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [IMAP4] Crispin, M., "INTERNET MESSAGE ACCESS PROTOCOL -
+ VERSION 4rev1", RFC 3501, March 2003.
+
+ [ABNF] Crocker, D., Ed., and P. Overell, "Augmented BNF for
+ Syntax Specifications: ABNF", RFC 4234, October 2005.
+
+ [CHARSET] Freed, N. and J. Postel, "IANA Charset Registration
+ Procedures", BCP 19, RFC 2978, October 2000.
+
+ [MULTIAPPEND] Crispin, M., "Internet Message Access Protocol (IMAP) -
+ MULTIAPPEND Extension", RFC 3502, March 2003.
+
+ [NAMESPACE] Gahrns, M. and C. Newman, "IMAP4 Namespace", RFC 2342,
+ May 1998.
+
+ [LITERAL+] Myers, J., "IMAP4 non-synchronizing literals", RFC
+ 2088, January 1997.
+
+ [BINARY] Nerenberg, L., "IMAP4 Binary Content Extension", RFC
+ 3516, April 2003.
+
+6. Acknowledgements
+
+ This documents is based on ideas proposed by Pete Resnick, Mark
+ Crispin, Ken Murchison, Philip Guenther, Randall Gellens, and Lyndon
+ Nerenberg.
+
+ However, all errors and omissions must be attributed to the authors
+ of the document.
+
+ Thanks to Philip Guenther, Dave Cridland, Mark Crispin, Chris Newman,
+ Elwyn Davies, and Barry Leiba for comments and corrections.
+
+ literal8 syntax was taken from RFC 3516.
+
+
+
+
+
+
+
+
+
+
+
+
+Melnikov & Daboo Standards Track [Page 15]
+
+RFC 4466 Collected Extensions to IMAP4 ABNF April 2006
+
+
+Authors' Addresses
+
+ Alexey Melnikov
+ Isode Limited
+ 5 Castle Business Village
+ 36 Station Road
+ Hampton, Middlesex, TW12 2BX
+ UK
+
+ EMail: Alexey.Melnikov@isode.com
+
+
+ Cyrus Daboo
+
+ EMail: cyrus@daboo.name
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Melnikov & Daboo Standards Track [Page 16]
+
+RFC 4466 Collected Extensions to IMAP4 ABNF April 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is provided by the IETF
+ Administrative Support Activity (IASA).
+
+
+
+
+
+
+
+Melnikov & Daboo Standards Track [Page 17]
+
diff --git a/imap/docs/rfc/rfc4467.txt b/imap/docs/rfc/rfc4467.txt
new file mode 100644
index 00000000..83b6516a
--- /dev/null
+++ b/imap/docs/rfc/rfc4467.txt
@@ -0,0 +1,1011 @@
+
+
+
+
+
+
+Network Working Group M. Crispin
+Request for Comments: 4467 University of Washington
+Updates: 3501 May 2006
+Category: Standards Track
+
+
+ Internet Message Access Protocol (IMAP) - URLAUTH Extension
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+Abstract
+
+ This document describes the URLAUTH extension to the Internet Message
+ Access Protocol (IMAP) (RFC 3501) and the IMAP URL Scheme (IMAPURL)
+ (RFC 2192). This extension provides a means by which an IMAP client
+ can use URLs carrying authorization to access limited message data on
+ the IMAP server.
+
+ An IMAP server that supports this extension indicates this with a
+ capability name of "URLAUTH".
+
+1. Introduction
+
+ In [IMAPURL], a URL of the form imap://fred@example.com/INBOX/;uid=20
+ requires authorization as userid "fred". However, [IMAPURL] implies
+ that it only supports authentication and confuses the concepts of
+ authentication and authorization.
+
+ The URLAUTH extension defines an authorization mechanism for IMAP
+ URLs to replace [IMAPURL]'s authentication-only mechanism. URLAUTH
+ conveys authorization in the URL string itself and reuses a portion
+ of the syntax of the [IMAPURL] authentication mechanism to convey the
+ authorization identity (which also defines the default namespace in
+ [IMAP]).
+
+ The URLAUTH extension provides a means by which an authorized user of
+ an IMAP server can create URLAUTH-authorized IMAP URLs. A URLAUTH-
+ authorized URL conveys authorization (not authentication) to the data
+
+
+
+Crispin Standards Track [Page 1]
+
+RFC 4467 IMAP - URLAUTH Extension May 2006
+
+
+ addressed by that URL. This URL can be used in another IMAP session
+ to access specific content on the IMAP server, without otherwise
+ providing authorization to any other data (such as other data in the
+ mailbox specified in the URL) owned by the authorizing user.
+
+ Conceptually, a URLAUTH-authorized URL can be thought of as a "pawn
+ ticket" that carries no authentication information and can be
+ redeemed by whomever presents it. However, unlike a pawn ticket,
+ URLAUTH has optional mechanisms to restrict the usage of a URLAUTH-
+ authorized URL. Using these mechanisms, URLAUTH-authorized URLs can
+ be usable by:
+
+ . anonymous (the "pawn ticket" model)
+ . authenticated users only
+ . a specific authenticated user only
+ . message submission acting on behalf of a specific user only
+
+ There is also a mechanism for expiration.
+
+ A URLAUTH-authorized URL can be used in the argument to the BURL
+ command in message composition, as described in [BURL], for such
+ purposes as allowing a client (with limited memory or other
+ resources) to submit a message forward or to resend from an IMAP
+ mailbox without requiring the client to fetch that message data.
+
+ The URLAUTH is generated using an authorization mechanism name and an
+ authorization token, which is generated using a secret mailbox access
+ key. An IMAP client can request that the server generate and assign
+ a new mailbox access key (thus effectively revoking all current URLs
+ using URLAUTH with the old mailbox access key) but cannot set the
+ mailbox access key to a key of its own choosing.
+
+1.1. Conventions Used in this Document
+
+ The key words "MUST", "MUST NOT", "SHOULD", "SHOULD NOT", and "MAY"
+ in this document are to be interpreted as defined in [KEYWORDS].
+
+ The formal syntax uses the Augmented Backus-Naur Form (ABNF) notation
+ including the core rules defined in Appendix A of [ABNF].
+
+ In examples, "C:" and "S:" indicate lines sent by the client and
+ server, respectively. If a single "C:" or "S:" label applies to
+ multiple lines, then the line breaks between those lines are for
+ editorial clarity only and are not part of the actual protocol
+ exchange.
+
+
+
+
+
+
+Crispin Standards Track [Page 2]
+
+RFC 4467 IMAP - URLAUTH Extension May 2006
+
+
+2. Concepts
+
+2.1. URLAUTH
+
+ The URLAUTH is a component, appended at the end of a URL, that
+ conveys authorization to access the data addressed by that URL. It
+ contains an authorized access identifier, an authorization mechanism
+ name, and an authorization token. The authorization token is
+ generated from the URL, the authorized access identifier, the
+ authorization mechanism name, and a mailbox access key.
+
+2.2. Mailbox Access Key
+
+ The mailbox access key is a random string with at least 128 bits of
+ entropy. It is generated by software (not by the human user) and
+ MUST be unpredictable.
+
+ Each user has a table of mailboxes and an associated mailbox access
+ key for each mailbox. Consequently, the mailbox access key is per-
+ user and per-mailbox. In other words, two users sharing the same
+ mailbox each have a different mailbox access key for that mailbox,
+ and each mailbox accessed by a single user also has a different
+ mailbox access key.
+
+2.3. Authorized Access Identifier
+
+ The authorized access identifier restricts use of the URLAUTH
+ authorized URL to certain users authorized on the server, as
+ described in section 3.
+
+2.4. Authorization Mechanism
+
+ The authorization mechanism is the algorithm by which the URLAUTH is
+ generated and subsequently verified, using the mailbox access key.
+
+2.4.1. INTERNAL Authorization Mechanism
+
+ This specification defines the INTERNAL mechanism, which uses a token
+ generation algorithm of the server's choosing and does not involve
+ disclosure of the mailbox access key to the client.
+
+ Note: The token generation algorithm chosen by the server
+ implementation should be modern and reasonably secure. At the
+ time of the writing of this document, an [HMAC] such as HMAC-SHA1
+ is recommended.
+
+
+
+
+
+
+Crispin Standards Track [Page 3]
+
+RFC 4467 IMAP - URLAUTH Extension May 2006
+
+
+ If it becomes necessary to change the token generation algorithm
+ of the INTERNAL mechanism (e.g., because an attack against the
+ current algorithm has been discovered), all currently existing
+ URLAUTH-authorized URLs are invalidated by the change in
+ algorithm. Since this would be an unpleasant surprise to
+ applications that depend upon the validity of a URLAUTH-authorized
+ URL, and there is no good way to do a bulk update of existing
+ deployed URLs, it is best to avoid this situation by using a
+ secure algorithm as opposed to one that is "good enough".
+
+ Server implementations SHOULD consider the possibility of changing
+ the algorithm. In some cases, it may be desirable to implement
+ the change of algorithm in a way that newly-generated tokens use
+ the new algorithm, but that for a limited period of time tokens
+ using either the new or old algorithm can be validated.
+ Consequently, the server SHOULD incorporate some means of
+ identifying the token generation algorithm within the token.
+
+ Although this specification is extensible for other mechanisms, none
+ are defined in this document. In addition to the mechanism name
+ itself, other mechanisms may have mechanism-specific data, which is
+ to be interpreted according to the definition of that mechanism.
+
+2.5. Authorization Token
+
+ The authorization token is a deterministic string of at least 128
+ bits that an entity with knowledge of the secret mailbox access key
+ and URL authorization mechanism can use to verify the URL.
+
+3. IMAP URL Extensions
+
+ [IMAPURL] is extended by allowing the addition of
+ ";EXPIRE=<datetime>" and ";URLAUTH=<access>:<mech>:<token>" to IMAP
+ URLs that refer to a specific message or message parts.
+
+ The URLAUTH is comprised of ";URLAUTH=<access>:<mech>:<token>" and
+ MUST be at the end of the URL.
+
+ URLAUTH does not apply to, and MUST NOT be used with, any IMAP URL
+ that refers to an entire IMAP server, a list of mailboxes, an entire
+ IMAP mailbox, or IMAP search results.
+
+ When ";EXPIRE=<datetime>" is used, this indicates the latest date and
+ time that the URL is valid. After that date and time, the URL has
+ expired, and server implementations MUST reject the URL. If
+ ";EXPIRE=<datetime>" is not used, the URL has no expiration, but
+ still can be revoked as discussed below.
+
+
+
+
+Crispin Standards Track [Page 4]
+
+RFC 4467 IMAP - URLAUTH Extension May 2006
+
+
+ The URLAUTH takes the form ";URLAUTH=<access>:<mech>:<token>". It is
+ composed of three parts. The <access> portion provides the
+ authorized access identifiers, which may constrain the operations and
+ users that are permitted to use this URL. The <mech> portion
+ provides the authorization mechanism used by the IMAP server to
+ generate the authorization token that follows. The <token> portion
+ provides the authorization token.
+
+ The "submit+" access identifier prefix, followed by a userid,
+ indicates that only a userid authorized as a message submission
+ entity on behalf of the specified userid is permitted to use this
+ URL. The IMAP server does not validate the specified userid but does
+ validate that the IMAP session has an authorization identity that is
+ authorized as a message submission entity. The authorized message
+ submission entity MUST validate the userid prior to contacting the
+ IMAP server.
+
+ The "user+" access identifier prefix, followed by a userid, indicates
+ that use of this URL is limited to IMAP sessions that are logged in
+ as the specified userid (that is, have authorization identity as that
+ userid).
+
+ Note: If a SASL mechanism that provides both authorization and
+ authentication identifiers is used to authenticate to the IMAP
+ server, the "user+" access identifier MUST match the authorization
+ identifier.
+
+ The "authuser" access identifier indicates that use of this URL is
+ limited to IMAP sessions that are logged in as an authorized user
+ (that is, have authorization identity as an authorized user) of that
+ IMAP server. Use of this URL is prohibited to anonymous IMAP
+ sessions.
+
+ The "anonymous" access identifier indicates that use of this URL is
+ not restricted by session authorization identity; that is, any IMAP
+ session in authenticated or selected state (as defined in [IMAP]),
+ including anonymous sessions, may issue a URLFETCH using this URL.
+
+ The authorization token is represented as an ASCII-encoded
+ hexadecimal string, which is used to authorize the URL. The length
+ and the calculation of the authorization token depends upon the
+ mechanism used; but, in all cases, the authorization token is at
+ least 128 bits (and therefore at least 32 hexadecimal digits).
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 5]
+
+RFC 4467 IMAP - URLAUTH Extension May 2006
+
+
+4. Discussion of URLAUTH Authorization Issues
+
+ In [IMAPURL], the userid before the "@" in the URL has two purposes:
+
+ 1) It provides context for user-specific mailbox paths such as
+ "INBOX".
+
+ 2) It specifies that resolution of the URL requires logging in as
+ that user and limits use of that URL to only that user.
+
+ An obvious limitation of using the same field for both purposes is
+ that the URL can only be resolved by the mailbox owner.
+
+ URLAUTH overrides the second purpose of the userid in the IMAP URL
+ and by default permits the URL to be resolved by any user permitted
+ by the access identifier.
+
+ The "user+<userid>" access identifier limits resolution of that URL
+ to a particular userid, whereas the "submit+<userid>" access
+ identifier is more general and simply requires that the session be
+ authorized by a user that has been granted a "submit" role within the
+ authentication system. Use of either of these access identifiers
+ makes it impossible for an attacker, spying on the session, to use
+ the same URL, either directly or by submission to a message
+ submission entity.
+
+ The "authuser" and "anonymous" access identifiers do not have this
+ level of protection and should be used with caution. These access
+ identifiers are primarily useful for public export of data from an
+ IMAP server, without requiring that it be copied to a web or
+ anonymous FTP server. Refer to the Security Considerations for more
+ details.
+
+5. Generation of URLAUTH-Authorized URLs
+
+ A URLAUTH-authorized URL is generated from an initial URL as follows:
+
+ An initial URL is built, ending with ";URLAUTH=<access>" but without
+ the ":<mech>:<token>" components. An authorization mechanism is
+ selected and used to calculate the authorization token, with the
+ initial URL as the data and a secret known to the IMAP server as the
+ key. The URLAUTH-authorized URL is generated by taking the initial
+ URL and appending ":", the URL authorization mechanism name, ":", and
+ the ASCII-encoded hexadecimal representation of the authorization
+ token.
+
+
+
+
+
+
+Crispin Standards Track [Page 6]
+
+RFC 4467 IMAP - URLAUTH Extension May 2006
+
+
+ Note: ASCII-encoded hexadecimal is used instead of BASE64 because
+ a BASE64 representation may have "=" padding characters, which
+ would be problematic in a URL.
+
+ In the INTERNAL mechanism, the mailbox access key for that mailbox is
+ the secret known to the IMAP server, and a server-selected algorithm
+ is used as described in section 2.4.1.
+
+6. Validation of URLAUTH-authorized URLs
+
+ A URLAUTH-authorized URL is validated as follows:
+
+ The URL is split at the ":" that separates "<access>" from
+ "<mech>:<token>" in the ";URLAUTH=<access>:<mech>:<token>" portion of
+ the URL. The "<mech>:<token>" portion is first parsed and saved as
+ the authorization mechanism and the authorization token. The URL is
+ truncated, discarding the ":" described above, to create a "rump URL"
+ (the URL minus the ":" and the "<mech>:<token>" portion). The rump
+ URL is then analyzed to identify the mailbox.
+
+ If the mailbox cannot be identified, an authorization token is
+ calculated on the rump URL, using random "plausible" keys (selected
+ by the server) as needed, before returning a validation failure.
+ This prevents timing attacks aimed at identifying mailbox names.
+
+ If the mailbox can be identified, the authorization token is
+ calculated on the rump URL and a secret known to the IMAP server
+ using the given URL authorization mechanism. Validation is
+ successful if, and only if, the calculated authorization token for
+ that mechanism matches the authorization token supplied in
+ ";URLAUTH=<access>:<mech>:<token>".
+
+ Removal of the ":<mech>:<token>" portion of the URL MUST be the only
+ operation applied to the URLAUTH-authorized URL to get the rump URL.
+ In particular, URL percent escape decoding and case-folding
+ (including to the domain part of the URL) MUST NOT occur.
+
+ In the INTERNAL mechanism, the mailbox access key for that mailbox is
+ used as the secret known to the IMAP server, and the same server-
+ selected algorithm used for generating URLs is used to calculate the
+ authorization token for verification.
+
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 7]
+
+RFC 4467 IMAP - URLAUTH Extension May 2006
+
+
+7. Additional Commands
+
+ These commands are extensions to the [IMAP] base protocol.
+
+ The section headings of these commands are intended to correspond
+ with where they would be located in the base protocol document if
+ they were part of that document.
+
+BASE.6.3.RESETKEY. RESETKEY Command
+
+ Arguments: optional mailbox name
+ optional mechanism name(s)
+
+ Responses: none other than in result
+
+ Result: OK - RESETKEY completed, URLMECH containing new data
+ NO - RESETKEY error: can't change key of that mailbox
+ BAD - command unknown or arguments invalid
+
+ The RESETKEY command has two forms.
+
+ The first form accepts a mailbox name as an argument and generates a
+ new mailbox access key for the given mailbox in the user's mailbox
+ access key table, replacing any previous mailbox access key (and
+ revoking any URLs that were authorized with a URLAUTH using that key)
+ in that table. By default, the mailbox access key is generated for
+ the INTERNAL mechanism; other mechanisms can be specified with the
+ optional mechanism argument.
+
+ The second form, with no arguments, removes all mailbox access keys
+ in the user's mailbox access key table, revoking all URLs currently
+ authorized using URLAUTH by the user.
+
+ Any current IMAP session logged in as the user that has the mailbox
+ selected will receive an untagged OK response with the URLMECH status
+ response code (see section BASE.7.1.URLMECH for more details about
+ the URLMECH status response code).
+
+ Example:
+
+ C: a31 RESETKEY
+ S: a31 OK All keys removed
+ C: a32 RESETKEY INBOX
+ S: a32 OK [URLMECH INTERNAL] mechs
+ C: a33 RESETKEY INBOX XSAMPLE
+ S: a33 OK [URLMECH INTERNAL XSAMPLE=P34OKhO7VEkCbsiYY8rGEg==] done
+
+
+
+
+
+Crispin Standards Track [Page 8]
+
+RFC 4467 IMAP - URLAUTH Extension May 2006
+
+
+BASE.6.3.GENURLAUTH. GENURLAUTH Command
+
+ Argument: one or more URL/mechanism pairs
+
+ Response: untagged response: GENURLAUTH
+
+ Result: OK - GENURLAUTH completed
+ NO - GENURLAUTH error: can't generate a URLAUTH
+ BAD - command unknown or arguments invalid
+
+ The GENURLAUTH command requests that the server generate a URLAUTH-
+ authorized URL for each of the given URLs using the given URL
+ authorization mechanism.
+
+ The server MUST validate each supplied URL as follows:
+
+ (1) The mailbox component of the URL MUST refer to an existing
+ mailbox.
+
+ (2) The server component of the URL MUST contain a valid userid
+ that identifies the owner of the mailbox access key table that
+ will be used to generate the URLAUTH-authorized URL. As a
+ consequence, the iserver rule of [IMAPURL] is modified so that
+ iuserauth is mandatory.
+
+ Note: the server component of the URL is generally the
+ logged in userid and server. If not, then the logged in
+ userid and server MUST have owner-type access to the
+ mailbox access key table owned by the userid and server
+ indicated by the server component of the URL.
+
+ (3) There is a valid access identifier that, in the case of
+ "submit+" and "user+", will contain a valid userid. This
+ userid is not necessarily the same as the owner userid
+ described in (2).
+
+ (4) The server MAY also verify that the iuid and/or isection
+ components (if present) are valid.
+
+ If any of the above checks fail, the server MUST return a tagged BAD
+ response with the following exception. If an invalid userid is
+ supplied as the mailbox access key owner and/or as part of the access
+ identifier, the server MAY issue a tagged OK response with a
+ generated mailbox key that always fails validation when used with a
+ URLFETCH command. This exception prevents an attacker from
+ validating userids.
+
+
+
+
+
+Crispin Standards Track [Page 9]
+
+RFC 4467 IMAP - URLAUTH Extension May 2006
+
+
+ If there is currently no mailbox access key for the given mailbox in
+ the owner's mailbox access key table, one is automatically generated.
+ That is, it is not necessary to use RESETKEY prior to first-time use
+ of GENURLAUTH.
+
+ If the command is successful, a GENURLAUTH response code is returned
+ listing the requested URLs as URLAUTH-authorized URLs.
+
+ Examples:
+
+ C: a775 GENURLAUTH "imap://joe@example.com/INBOX/;uid=20/
+ ;section=1.2" INTERNAL
+ S: a775 BAD missing access identifier in supplied URL
+ C: a776 GENURLAUTH "imap://example.com/Shared/;uid=20/
+ ;section=1.2;urlauth=submit+fred" INTERNAL
+ S: a776 BAD missing owner username in supplied URL
+ C: a777 GENURLAUTH "imap://joe@example.com/INBOX/;uid=20/
+ ;section=1.2;urlauth=submit+fred" INTERNAL
+ S: * GENURLAUTH "imap://joe@example.com/INBOX/;uid=20/;section=1.2
+ ;urlauth=submit+fred:internal:91354a473744909de610943775f92038"
+ S: a777 OK GENURLAUTH completed
+
+BASE.6.3.URLFETCH. URLFETCH Command
+
+ Argument: one or more URLs
+
+ Response: untagged response: URLFETCH
+
+ Result: OK - urlfetch completed
+ NO - urlfetch failed due to server internal error
+ BAD - command unknown or arguments invalid
+
+ The URLFETCH command requests that the server return the text data
+ associated with the specified IMAP URLs, as described in [IMAPURL]
+ and extended by this document. The data is returned for all
+ validated URLs, regardless of whether or not the session would
+ otherwise be able to access the mailbox containing that data via
+ SELECT or EXAMINE.
+
+ Note: This command does not require that the URL refer to the
+ selected mailbox; nor does it require that any mailbox be
+ selected. It also does not in any way interfere with any selected
+ mailbox.
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 10]
+
+RFC 4467 IMAP - URLAUTH Extension May 2006
+
+
+ The URLFETCH command effectively executes with the access of the
+ userid in the server component of the URL (which is generally the
+ userid that issued the GENURLAUTH). By itself, the URLAUTH does NOT
+ grant access to the data; once validated, it grants whatever access
+ to the data is held by the userid in the server component of the URL.
+ That access may have changed since the GENURLAUTH was done.
+
+ The URLFETCH command MUST return an untagged URLFETCH response and a
+ tagged OK response to any URLFETCH command that is syntactically
+ valid. A NO response indicates a server internal failure that may be
+ resolved on later retry.
+
+ Note: The possibility of a NO response is to accommodate
+ implementations that would otherwise have to issue an untagged BYE
+ with a fatal error due to an inability to respond to a valid
+ request. In an ideal world, a server SHOULD NOT issue a NO
+ response.
+
+ The server MUST return NIL for any IMAP URL that references an entire
+ IMAP server, a list of mailboxes, an entire IMAP mailbox, or IMAP
+ search results.
+
+ Example:
+
+ Note: For clarity, this example uses the LOGIN command, which
+ SHOULD NOT be used over a non-encrypted communication path.
+
+ This example is of a submit server, obtaining a message segment
+ for a message that it has already validated was submitted by
+ "fred".
+
+ S: * OK [CAPABILITY IMAP4REV1 URLAUTH] example.com IMAP server
+ C: a001 LOGIN submitserver secret
+ S: a001 OK submitserver logged in
+ C: a002 URLFETCH "imap://joe@example.com/INBOX/;uid=20/
+ ;section=1.2;urlauth=submit+fred:internal
+ :91354a473744909de610943775f92038"
+ S: * URLFETCH "imap://joe@example.com/INBOX/;uid=20/;section=1.2
+ ;urlauth=submit+fred:internal
+ :91354a473744909de610943775f92038" {28}
+ S: Si vis pacem, para bellum.
+ S:
+ S: a002 OK URLFETCH completed
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 11]
+
+RFC 4467 IMAP - URLAUTH Extension May 2006
+
+
+8. Additional Responses
+
+ These responses are extensions to the [IMAP] base protocol.
+
+ The section headings of these responses are intended to correspond
+ with where they would be located in the base protocol document if
+ they were part of that document.
+
+BASE.7.1.URLMECH. URLMECH Status Response Code
+
+ The URLMECH status response code is followed by a list of URL
+ authorization mechanism names. Mechanism names other than INTERNAL
+ may be appended with an "=" and BASE64-encoded form of mechanism-
+ specific data.
+
+ This status response code is returned in an untagged OK response in
+ response to a RESETKEY, SELECT, or EXAMINE command. In the case of
+ the RESETKEY command, this status response code can be sent in the
+ tagged OK response instead of requiring a separate untagged OK
+ response.
+
+ Example:
+
+ C: a33 RESETKEY INBOX XSAMPLE
+ S: a33 OK [URLMECH INTERNAL XSAMPLE=P34OKhO7VEkCbsiYY8rGEg==] done
+
+ In this example, the server supports the INTERNAL mechanism and an
+ experimental mechanism called XSAMPLE, which also holds some
+ mechanism-specific data (the name "XSAMPLE" is for illustrative
+ purposes only).
+
+BASE.7.4.GENURLAUTH. GENURLAUTH Response
+
+ Contents: One or more URLs
+
+ The GENURLAUTH response returns the URLAUTH-authorized URL(s)
+ requested by a GENURLAUTH command.
+
+ Example:
+
+ C: a777 GENURLAUTH "imap://joe@example.com/INBOX/;uid=20/
+ ;section=1.2;urlauth=submit+fred" INTERNAL
+ S: * GENURLAUTH "imap://joe@example.com/INBOX/;uid=20/;section=1.2
+ ;urlauth=submit+fred:internal:91354a473744909de610943775f92038"
+ S: a777 OK GENURLAUTH completed
+
+
+
+
+
+
+Crispin Standards Track [Page 12]
+
+RFC 4467 IMAP - URLAUTH Extension May 2006
+
+
+BASE.7.4.URLFETCH. URLFETCH Response
+
+ Contents: One or more URL/nstring pairs
+
+ The URLFETCH response returns the message text data associated with
+ one or more IMAP URLs, as described in [IMAPURL] and extended by this
+ document. This response occurs as the result of a URLFETCH command.
+
+ The returned data string is NIL if the URL is invalid for any reason
+ (including validation failure). If the URL is valid, but the IMAP
+ fetch of the body part returned NIL (this should not happen), the
+ returned data string should be the empty string ("") and not NIL.
+
+ Note: This command does not require that the URL refer to the
+ selected mailbox; nor does it require that any mailbox be
+ selected. It also does not in any way interfere with any selected
+ mailbox.
+
+ Example:
+
+ C: a002 URLFETCH "imap://joe@example.com/INBOX/;uid=20/
+ ;section=1.2;urlauth=submit+fred:internal
+ :91354a473744909de610943775f92038"
+ S: * URLFETCH "imap://joe@example.com/INBOX/;uid=20/;section=1.2
+ ;urlauth=submit+fred:internal
+ :91354a473744909de610943775f92038" {28}
+ S: Si vis pacem, para bellum.
+ S:
+ S: a002 OK URLFETCH completed
+
+9. Formal Syntax
+
+ The following syntax specification uses the Augmented Backus-Naur
+ Form (ABNF) notation as specified in [ABNF].
+
+ The following modifications are made to the Formal Syntax in [IMAP]:
+
+resetkey = "RESETKEY" [SP mailbox *(SP mechanism)]
+
+capability =/ "URLAUTH"
+
+command-auth =/ resetkey / genurlauth / urlfetch
+
+resp-text-code =/ "URLMECH" SP "INTERNAL" *(SP mechanism ["=" base64])
+
+genurlauth = "GENURLAUTH" 1*(SP url-rump SP mechanism)
+
+genurlauth-data = "*" SP "GENURLAUTH" 1*(SP url-full)
+
+
+
+Crispin Standards Track [Page 13]
+
+RFC 4467 IMAP - URLAUTH Extension May 2006
+
+
+url-full = astring
+ ; contains authimapurlfull as defined below
+
+url-rump = astring
+ ; contains authimapurlrump as defined below
+
+urlfetch = "URLFETCH" 1*(SP url-full)
+
+urlfetch-data = "*" SP "URLFETCH" 1*(SP url-full SP nstring)
+
+ The following extensions are made to the Formal Syntax in [IMAPURL]:
+
+authimapurl = "imap://" enc-user [iauth] "@" hostport "/"
+ imessagepart
+ ; replaces "imapurl" and "iserver" rules for
+ ; URLAUTH authorized URLs
+
+authimapurlfull = authimapurl iurlauth
+
+authimapurlrump = authimapurl iurlauth-rump
+
+enc-urlauth = 32*HEXDIG
+
+enc-user = 1*achar
+ ; same as "enc_user" in RFC 2192
+
+iurlauth = iurlauth-rump ":" mechanism ":" enc-urlauth
+
+iurlauth-rump = [expire] ";URLAUTH=" access
+
+access = ("submit+" enc-user) / ("user+" enc-user) /
+ "authuser" / "anonymous"
+
+expire = ";EXPIRE=" date-time
+ ; date-time defined in [DATETIME]
+
+mechanism = "INTERNAL" / 1*(ALPHA / DIGIT / "-" / ".")
+ ; case-insensitive
+ ; new mechanisms MUST be registered with IANA
+
+
+
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 14]
+
+RFC 4467 IMAP - URLAUTH Extension May 2006
+
+
+10. Security Considerations
+
+ Security considerations are discussed throughout this memo.
+
+ The mailbox access key SHOULD have at least 128 bits of entropy
+ (refer to [RANDOM] for more details) and MUST be unpredictable.
+
+ The server implementation of the INTERNAL mechanism SHOULD consider
+ the possibility of needing to change the token generation algorithm,
+ and SHOULD incorporate some means of identifying the token generation
+ algorithm within the token.
+
+ The URLMECH status response code may expose sensitive data in the
+ mechanism-specific data for mechanisms other than INTERNAL. A server
+ implementation MUST implement a configuration that will not return a
+ URLMECH status response code unless some mechanism is provided that
+ protects the session from snooping, such as a TLS or SASL security
+ layer that provides confidentiality protection.
+
+ The calculation of an authorization token with a "plausible" key if
+ the mailbox can not be identified is necessary to avoid attacks in
+ which the server is probed to see if a particular mailbox exists on
+ the server by measuring the amount of time taken to reject a known
+ bad name versus some other name.
+
+ To protect against a computational denial-of-service attack, a server
+ MAY impose progressively longer delays on multiple URL requests that
+ fail validation.
+
+ The decision to use the "authuser" access identifier should be made
+ with caution. An "authuser" access identifier can be used by any
+ authorized user of the IMAP server; therefore, use of this access
+ identifier should be limited to content that may be disclosed to any
+ authorized user of the IMAP server.
+
+ The decision to use the "anonymous" access identifier should be made
+ with extreme caution. An "anonymous" access identifier can be used
+ by anyone; therefore, use of this access identifier should be limited
+ to content that may be disclosed to anyone. Many IMAP servers do not
+ permit anonymous access; in this case, the "anonymous" access
+ identifier is equivalent to "authuser", but this MUST NOT be relied
+ upon.
+
+ Although this specification does not prohibit the theoretical
+ capability to generate a URL with a server component other than the
+ logged in userid and server, this capability should only be provided
+
+
+
+
+
+Crispin Standards Track [Page 15]
+
+RFC 4467 IMAP - URLAUTH Extension May 2006
+
+
+ when the logged in userid/server has been authorized as equivalent to
+ the server component userid/server, or otherwise has access to that
+ userid/server mailbox access key table.
+
+11. IANA Considerations
+
+ This document constitutes registration of the URLAUTH capability in
+ the imap4-capabilities registry.
+
+ URLAUTH authorization mechanisms are registered by publishing a
+ standards track or IESG-approved experimental RFC. The registry is
+ currently located at:
+
+http://www.iana.org/assignments/urlauth-authorization-mechanism-registry
+
+ This registry is case-insensitive.
+
+ This document constitutes registration of the INTERNAL URLAUTH
+ authorization mechanism.
+
+ IMAP URLAUTH Authorization Mechanism Registry
+
+ Mechanism Name Reference
+ -------------- ---------
+ INTERNAL [RFC4467]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 16]
+
+RFC 4467 IMAP - URLAUTH Extension May 2006
+
+
+12. Normative References
+
+ [ABNF] Crocker, D. and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", RFC 4234, October 2005.
+
+ [BURL] Newman, C., "Message Submission BURL Extension", RFC 4468,
+ May 2006.
+
+ [DATETIME] Klyne, G. and C. Newman, "Date and Time on the Internet:
+ Timestamps", RFC 3339, July 2002.
+
+ [IMAP] Crispin, M., "Internet Message Access Protocol - Version
+ 4rev1", RFC 3501, March 2003.
+
+ [IMAPURL] Newman, C., "IMAP URL Scheme", RFC 2192, September 1997.
+
+ [KEYWORDS] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+13. Informative References
+
+ [HMAC] Krawczyk, H., Bellare, M., and R. Canetti, "HMAC: Keyed-
+ Hashing for Message Authentication", RFC 2104, February
+ 1997.
+
+ [RANDOM] Eastlake, D., 3rd, Schiller, J., and S. Crocker,
+ "Randomness Requirements for Security", BCP 106, RFC 4086,
+ June 2005.
+
+Author's Address
+
+ Mark R. Crispin
+ Networks and Distributed Computing
+ University of Washington
+ 4545 15th Avenue NE
+ Seattle, WA 98105-4527
+
+ Phone: (206) 543-5762
+ EMail: MRC@CAC.Washington.EDU
+
+
+
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 17]
+
+RFC 4467 IMAP - URLAUTH Extension May 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is provided by the IETF
+ Administrative Support Activity (IASA).
+
+
+
+
+
+
+
+Crispin Standards Track [Page 18]
+
diff --git a/imap/docs/rfc/rfc4468.txt b/imap/docs/rfc/rfc4468.txt
new file mode 100644
index 00000000..b16dcb4e
--- /dev/null
+++ b/imap/docs/rfc/rfc4468.txt
@@ -0,0 +1,787 @@
+
+
+
+
+
+
+Network Working Group C. Newman
+Request for Comments: 4468 Sun Microsystems
+Updates: 3463 May 2006
+Category: Standards Track
+
+
+ Message Submission BURL Extension
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+Abstract
+
+ The submission profile of Simple Mail Transfer Protocol (SMTP)
+ provides a standard way for an email client to submit a complete
+ message for delivery. This specification extends the submission
+ profile by adding a new BURL command that can be used to fetch
+ submission data from an Internet Message Access Protocol (IMAP)
+ server. This permits a mail client to inject content from an IMAP
+ server into the SMTP infrastructure without downloading it to the
+ client and uploading it back to the server.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Newman Standards Track [Page 1]
+
+RFC 4468 Message Submission BURL Extension May 2006
+
+
+Table of Contents
+
+ 1. Introduction ....................................................2
+ 2. Conventions Used in This Document ...............................2
+ 3. BURL Submission Extension .......................................3
+ 3.1. SMTP Submission Extension Registration .....................3
+ 3.2. BURL Transaction ...........................................3
+ 3.3. The BURL IMAP Options ......................................4
+ 3.4. Examples ...................................................5
+ 3.5. Formal Syntax ..............................................6
+ 4. 8-Bit and Binary ................................................7
+ 5. Updates to RFC 3463 .............................................7
+ 6. Response Codes ..................................................7
+ 7. IANA Considerations .............................................9
+ 8. Security Considerations .........................................9
+ 9. References .....................................................11
+ 9.1. Normative References ......................................11
+ 9.2. Informative References ....................................12
+ Appendix A. Acknowledgements .....................................13
+
+1. Introduction
+
+ This specification defines an extension to the standard Message
+ Submission [RFC4409] protocol to permit data to be fetched from an
+ IMAP server at message submission time. This MAY be used in
+ conjunction with the CHUNKING [RFC3030] mechanism so that chunks of
+ the message can come from an external IMAP server. This provides the
+ ability to forward an email message without first downloading it to
+ the client.
+
+2. Conventions Used in This Document
+
+ The key words "MUST", "MUST NOT", "SHOULD", "SHOULD NOT", and "MAY"
+ in this document are to be interpreted as defined in "Key words for
+ use in RFCs to Indicate Requirement Levels" [RFC2119].
+
+ The formal syntax uses the Augmented Backus-Naur Form (ABNF)
+ [RFC4234] notation including the core rules defined in Appendix B of
+ RFC 4234.
+
+
+
+
+
+
+
+
+
+
+
+
+Newman Standards Track [Page 2]
+
+RFC 4468 Message Submission BURL Extension May 2006
+
+
+3. BURL Submission Extension
+
+ This section defines the BURL submission extension.
+
+3.1. SMTP Submission Extension Registration
+
+ 1. The name of this submission extension is "BURL". This extends
+ the Message Submission protocol on port 587 and MUST NOT be
+ advertised by a regular SMTP [RFC2821] server on port 25 that
+ acts as a relay for incoming mail from other SMTP relays.
+
+ 2. The EHLO keyword value associated with the extension is "BURL".
+
+ 3. The BURL EHLO keyword will have zero or more arguments. The only
+ argument defined at this time is the "imap" argument, which MUST
+ be present in order to use IMAP URLs with BURL. Clients MUST
+ ignore other arguments after the BURL EHLO keyword unless they
+ are defined by a subsequent IETF standards track specification.
+ The arguments that appear after the BURL EHLO keyword may change
+ subsequent to the use of SMTP AUTH [RFC2554], so a server that
+ advertises BURL with no arguments prior to authentication
+ indicates that BURL is supported but authentication is required
+ to use it.
+
+ 4. This extension adds the BURL SMTP verb. This verb is used as a
+ replacement for the DATA command and is only permitted during a
+ mail transaction after at least one successful RCPT TO.
+
+3.2. BURL Transaction
+
+ A simple BURL transaction will consist of MAIL FROM, one or more RCPT
+ TO headers, and a BURL command with the "LAST" tag. The BURL command
+ will include an IMAP URL pointing to a fully formed message ready for
+ injection into the SMTP infrastructure. If PIPELINING [RFC2920] is
+ advertised, the client MAY send the entire transaction in one round
+ trip. If no valid RCPT TO address is supplied, the BURL command will
+ simply fail, and no resolution of the BURL URL argument will be
+ performed. If at least one valid RCPT TO address is supplied, then
+ the BURL URL argument will be resolved before the server responds to
+ the command.
+
+ A more sophisticated BURL transaction MAY occur when the server also
+ advertises CHUNKING [RFC3030]. In this case, the BURL and BDAT
+ commands may be interleaved until one of them terminates the
+ transaction with the "LAST" argument. If PIPELINING [RFC2920] is
+ also advertised, then the client may pipeline the entire transaction
+ in one round-trip. However, it MUST wait for the results of the
+ "LAST" BDAT or BURL command prior to initiating a new transaction.
+
+
+
+Newman Standards Track [Page 3]
+
+RFC 4468 Message Submission BURL Extension May 2006
+
+
+ The BURL command directs the server to fetch the data object to which
+ the URL refers and include it in the message. If the URL fetch
+ fails, the server will fail the entire transaction.
+
+3.3. The BURL IMAP Options
+
+ When "imap" is present in the space-separated list of arguments
+ following the BURL EHLO keyword, it indicates that the BURL command
+ supports the URLAUTH [RFC4467] extended form of IMAP URLs [RFC2192]
+ and that the submit server is configured with the necessary
+ credentials to resolve "urlauth=submit+" IMAP URLs for the submit
+ server's domain.
+
+ Subsequent to a successful SMTP AUTH command, the submission server
+ MAY indicate a prearranged trust relationship with a specific IMAP
+ server by including a BURL EHLO keyword argument of the form
+ "imap://imap.example.com". In this case, the submission server will
+ permit a regular IMAP URL referring to messages or parts of messages
+ on imap.example.com that the user who authenticated to the submit
+ server can access. Note that this form does not imply that the
+ submit server supports URLAUTH URLs; the submit server must advertise
+ both "imap" and "imap://imap.example.com" to indicate support for
+ both extended and non-extended URL forms.
+
+ When the submit server connects to the IMAP server, it acts as an
+ IMAP client and thus is subject to both the mandatory-to-implement
+ IMAP capabilities in Section 6.1.1 of RFC 3501, and the security
+ considerations in Section 11 of RFC 3501. Specifically, this
+ requires that the submit server implement a configuration that uses
+ STARTTLS followed by SASL PLAIN [SASL-PLAIN] to authenticate to the
+ IMAP server.
+
+ When the submit server resolves a URLAUTH IMAP URL, it uses submit
+ server credentials when authenticating to the IMAP server. The
+ authentication identity and password used for submit credentials MUST
+ be configurable. The string "submit" is suggested as a default value
+ for the authentication identity, with no default for the password.
+ Typically, the authorization identity is empty in this case; thus the
+ IMAP server will derive the authorization identity from the
+ authentication identity. If the IMAP URL uses the "submit+" access
+ identifier prefix, the submit server MUST refuse the BURL command
+ unless the userid in the URL's <access> token matches the submit
+ client's authorization identity.
+
+ When the submit server resolves a regular IMAP URL, it uses the
+ submit client's authorization identity when authenticating to the
+ IMAP server. If both the submit client and the submit server's
+ embedded IMAP client use SASL PLAIN (or the equivalent), the submit
+
+
+
+Newman Standards Track [Page 4]
+
+RFC 4468 Message Submission BURL Extension May 2006
+
+
+ server SHOULD forward the client's credentials if and only if the
+ submit server knows that the IMAP server is in the same
+ administrative domain. If the submit server supports SASL mechanisms
+ other than PLAIN, it MUST implement a configuration in which the
+ submit server's embedded IMAP client uses STARTTLS and SASL PLAIN
+ with the submit server's authentication identity and password (for
+ the respective IMAP server) and the submit client's authorization
+ identity.
+
+3.4. Examples
+
+ In examples, "C:" and "S:" indicate lines sent by the client and
+ server, respectively. If a single "C:" or "S:" label applies to
+ multiple lines, then the line breaks between those lines are for
+ editorial clarity only and are not part of the actual protocol
+ exchange.
+
+ Two successful submissions (without and with pipelining) follow:
+
+ <SSL/TLS encryption layer negotiated>
+ C: EHLO potter.example.com
+ S: 250-owlry.example.com
+ S: 250-8BITMIME
+ S: 250-BURL imap
+ S: 250-AUTH PLAIN
+ S: 250-DSN
+ S: 250 ENHANCEDSTATUSCODES
+ C: AUTH PLAIN aGFycnkAaGFycnkAYWNjaW8=
+ S: 235 2.7.0 PLAIN authentication successful.
+ C: MAIL FROM:<harry@gryffindor.example.com>
+ S: 250 2.5.0 Address Ok.
+ C: RCPT TO:<ron@gryffindor.example.com>
+ S: 250 2.1.5 ron@gryffindor.example.com OK.
+ C: BURL imap://harry@gryffindor.example.com/outbox
+ ;uidvalidity=1078863300/;uid=25;urlauth=submit+harry
+ :internal:91354a473744909de610943775f92038 LAST
+ S: 250 2.5.0 Ok.
+
+ <SSL/TLS encryption layer negotiated>
+ C: EHLO potter.example.com
+ S: 250-owlry.example.com
+ S: 250-8BITMIME
+ S: 250-PIPELINING
+ S: 250-BURL imap
+ S: 250-AUTH PLAIN
+ S: 250-DSN
+ S: 250 ENHANCEDSTATUSCODES
+ C: AUTH PLAIN aGFycnkAaGFycnkAYWNjaW8=
+
+
+
+Newman Standards Track [Page 5]
+
+RFC 4468 Message Submission BURL Extension May 2006
+
+
+ C: MAIL FROM:<harry@gryffindor.example.com>
+ C: RCPT TO:<ron@gryffindor.example.com>
+ C: BURL imap://harry@gryffindor.example.com/outbox
+ ;uidvalidity=1078863300/;uid=25;urlauth=submit+harry
+ :internal:91354a473744909de610943775f92038 LAST
+ S: 235 2.7.0 PLAIN authentication successful.
+ S: 250 2.5.0 Address Ok.
+ S: 250 2.1.5 ron@gryffindor.example.com OK.
+ S: 250 2.5.0 Ok.
+
+ Note that PIPELINING of the AUTH command is only permitted if the
+ selected mechanism can be completed in one round trip, a client
+ initial response is provided, and no SASL security layer is
+ negotiated. This is possible for PLAIN and EXTERNAL, but not for
+ most other SASL mechanisms.
+
+ Some examples of failure cases:
+
+ C: MAIL FROM:<harry@gryffindor.example.com>
+ C: RCPT TO:<malfoy@slitherin.example.com>
+ C: BURL imap://harry@gryffindor.example.com/outbox
+ ;uidvalidity=1078863300/;uid=25;urlauth=submit+harry
+ :internal:91354a473744909de610943775f92038 LAST
+ S: 250 2.5.0 Address Ok.
+ S: 550 5.7.1 Relaying not allowed: malfoy@slitherin.example.com
+ S: 554 5.5.0 No recipients have been specified.
+
+ C: MAIL FROM:<harry@gryffindor.example.com>
+ C: RCPT TO:<ron@gryffindor.example.com>
+ C: BURL imap://harry@gryffindor.example.com/outbox
+ ;uidvalidity=1078863300/;uid=25;urlauth=submit+harry
+ :internal:71354a473744909de610943775f92038 LAST
+ S: 250 2.5.0 Address Ok.
+ S: 250 2.1.5 ron@gryffindor.example.com OK.
+ S: 554 5.7.0 IMAP URL authorization failed
+
+3.5. Formal Syntax
+
+ The following syntax specification inherits ABNF [RFC4234] and
+ Uniform Resource Identifiers [RFC3986].
+
+ burl-param = "imap" / ("imap://" authority)
+ ; parameter to BURL EHLO keyword
+
+ burl-cmd = "BURL" SP absolute-URI [SP end-marker] CRLF
+
+ end-marker = "LAST"
+
+
+
+
+Newman Standards Track [Page 6]
+
+RFC 4468 Message Submission BURL Extension May 2006
+
+
+4. 8-Bit and Binary
+
+ A submit server that advertises BURL MUST also advertise 8BITMIME
+ [RFC1652] and perform the down conversion described in that
+ specification on the resulting complete message if 8-bit data is
+ received with the BURL command and passed to a 7-bit server. If the
+ URL argument to BURL refers to binary data, then the submit server
+ MAY refuse the command or down convert as described in Binary SMTP
+ [RFC3030].
+
+ The Submit server MAY refuse to accept a BURL command or combination
+ of BURL and BDAT commands that result in un-encoded 8-bit data in
+ mail or MIME [RFC2045] headers. Alternatively, the server MAY accept
+ such data and down convert to MIME header encoding [RFC2047].
+
+5. Updates to RFC 3463
+
+ SMTP or Submit servers that advertise ENHANCEDSTATUSCODES [RFC2034]
+ use enhanced status codes defined in RFC 3463 [RFC3463]. The BURL
+ extension introduces new error cases that that RFC did not consider.
+ The following additional enhanced status codes are defined by this
+ specification:
+
+ X.6.6 Message content not available
+
+ The message content could not be fetched from a remote system.
+ This may be useful as a permanent or persistent temporary
+ notification.
+
+ X.7.8 Trust relationship required
+
+ The submission server requires a configured trust relationship
+ with a third-party server in order to access the message content.
+
+6. Response Codes
+
+ This section includes example response codes to the BURL command.
+ Other text may be used with the same response codes. This list is
+ not exhaustive, and BURL clients MUST tolerate any valid SMTP
+ response code. Most of these examples include the appropriate
+ enhanced status code [RFC3463].
+
+ 554 5.5.0 No recipients have been specified
+
+ This response code occurs when BURL is used (for example, with
+ PIPELINING) and all RCPT TOs failed.
+
+
+
+
+
+Newman Standards Track [Page 7]
+
+RFC 4468 Message Submission BURL Extension May 2006
+
+
+ 503 5.5.0 Valid RCPT TO required before BURL
+
+ This response code is an alternative to the previous one when BURL
+ is used (for example, with PIPELINING) and all RCPT TOs failed.
+
+ 554 5.6.3 Conversion required but not supported
+
+ This response code occurs when the URL points to binary data and
+ the implementation does not support down conversion to base64.
+ This can also be used if the URL points to message data with 8-bit
+ content in headers and the server does not down convert such
+ content.
+
+ 554 5.3.4 Message too big for system
+
+ The message (subsequent to URL resolution) is larger than the
+ per-message size limit for this server.
+
+ 554 5.7.8 URL resolution requires trust relationship
+
+ The submit server does not have a trust relationship with the IMAP
+ server specified in the URL argument to BURL.
+
+ 552 5.2.2 Mailbox full
+
+ The recipient is local, the submit server supports direct
+ delivery, and the recipient has exceeded his quota and any grace
+ period for delivery attempts.
+
+ 554 5.6.6 IMAP URL resolution failed
+
+ The IMAP URLFETCH command returned an error or no data.
+
+ 250 2.5.0 Waiting for additional BURL or BDAT commands
+
+ A BURL command without the "LAST" modifier was sent. The URL for
+ this BURL command was successfully resolved, but the content will
+ not necessarily be committed to persistent storage until the rest
+ of the message content is collected. For example, a Unix server
+ may have written the content to a queue file buffer, but may not
+ yet have performed an fsync() operation. If the server loses
+ power, the content can still be lost.
+
+ 451 4.4.1 IMAP server unavailable
+
+ The connection to the IMAP server to resolve the URL failed.
+
+
+
+
+
+Newman Standards Track [Page 8]
+
+RFC 4468 Message Submission BURL Extension May 2006
+
+
+ 250 2.5.0 Ok.
+
+ The URL was successfully resolved, and the complete message data
+ has been committed to persistent storage.
+
+ 250 2.6.4 MIME header conversion with loss performed
+
+ The URL pointed to message data that included mail or MIME headers
+ with 8-bit data. This data was converted to MIME header encoding
+ [RFC2047], but the submit server may not have correctly guessed
+ the unlabeled character set.
+
+7. IANA Considerations
+
+ The "BURL" SMTP extension as described in Section 3 has been
+ registered. This registration has been marked for use by message
+ submission [RFC4409] only in the registry.
+
+8. Security Considerations
+
+ Modern SMTP submission servers often include content-based security
+ and denial-of-service defense mechanisms such as virus filtering,
+ size limits, server-generated signatures, spam filtering, etc.
+ Implementations of BURL should fetch the URL content prior to
+ application of such content-based mechanisms in order to preserve
+ their function.
+
+ Clients that generate unsolicited bulk email or email with viruses
+ could use this mechanism to compensate for a slow link between the
+ client and submit server. In particular, this mechanism would make
+ it feasible for a programmable cell phone or other device on a slow
+ link to become a significant source of unsolicited bulk email and/or
+ viruses. This makes it more important for submit server vendors
+ implementing BURL to have auditing and/or defenses against such
+ denial-of-service attacks including mandatory authentication, logging
+ that associates unique client identifiers with mail transactions,
+ limits on reuse of the same IMAP URL, rate limits, recipient count
+ limits, and content filters.
+
+ Transfer of the URLAUTH [RFC4467] form of IMAP URLs in the clear can
+ expose the authorization token to network eavesdroppers.
+ Implementations that support such URLs can address this issue by
+ using a strong confidentiality protection mechanism. For example,
+ the SMTP STARTTLS [RFC3207] and the IMAP STARTTLS [RFC3501]
+ extensions, in combination with a configuration setting that requires
+ their use with such IMAP URLs, would address this concern.
+
+
+
+
+
+Newman Standards Track [Page 9]
+
+RFC 4468 Message Submission BURL Extension May 2006
+
+
+ Use of a prearranged trust relationship between a submit server and a
+ specific IMAP server introduces security considerations. A
+ compromise of the submit server should not automatically compromise
+ all accounts on the IMAP server, so trust relationships involving
+ super-user proxy credentials are strongly discouraged. A system that
+ requires the submit server to authenticate to the IMAP server with
+ submit credentials and subsequently requires a URLAUTH URL to fetch
+ any content addresses this concern. A trusted third party model for
+ proxy credentials (such as that provided by Kerberos 5 [RFC4120])
+ would also suffice.
+
+ When a client uses SMTP STARTTLS to send a BURL command that
+ references non-public information, there is a user expectation that
+ the entire message content will be treated confidentially. To
+ address this expectation, the message submission server SHOULD use
+ STARTTLS or a mechanism providing equivalent data confidentiality
+ when fetching the content referenced by that URL.
+
+ A legitimate user of a submit server may try to compromise other
+ accounts on the server by providing an IMAP URLAUTH URL that points
+ to a server under that user's control that is designed to undermine
+ the security of the submit server. For this reason, the IMAP client
+ code that the submit server uses must be robust with respect to
+ arbitrary input sizes (including large IMAP literals) and arbitrary
+ delays from the IMAP server. Requiring a prearranged trust
+ relationship between a submit server and the IMAP server also
+ addresses this concern.
+
+ An authorized user of the submit server could set up a fraudulent
+ IMAP server and pass a URL for that server to the submit server. The
+ submit server might then contact the fraudulent IMAP server to
+ authenticate with submit credentials and fetch content. There are
+ several ways to mitigate this potential attack. A submit server that
+ only uses submit credentials with a fixed set of trusted IMAP servers
+ will not be vulnerable to exposure of those credentials. A submit
+ server can treat the IMAP server as untrusted and include defenses
+ for buffer overflows, denial-of-service slowdowns, and other
+ potential attacks. Finally, because authentication is required to
+ use BURL, it is possible to keep a secure audit trail and use that to
+ detect and punish the offending party.
+
+
+
+
+
+
+
+
+
+
+
+Newman Standards Track [Page 10]
+
+RFC 4468 Message Submission BURL Extension May 2006
+
+
+9. References
+
+9.1. Normative References
+
+ [RFC1652] Klensin, J., Freed, N., Rose, M., Stefferud, E., and D.
+ Crocker, "SMTP Service Extension for
+ 8bit-MIMEtransport", RFC 1652, July 1994.
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC2192] Newman, C., "IMAP URL Scheme", RFC 2192,
+ September 1997.
+
+ [RFC2554] Myers, J., "SMTP Service Extension for Authentication",
+ RFC 2554, March 1999.
+
+ [RFC2821] Klensin, J., "Simple Mail Transfer Protocol", RFC 2821,
+ April 2001.
+
+ [RFC3207] Hoffman, P., "SMTP Service Extension for Secure SMTP
+ over Transport Layer Security", RFC 3207,
+ February 2002.
+
+ [RFC3501] Crispin, M., "INTERNET MESSAGE ACCESS PROTOCOL -
+ VERSION 4rev1", RFC 3501, March 2003.
+
+ [RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter,
+ "Uniform Resource Identifier (URI): Generic Syntax",
+ STD 66, RFC 3986, January 2005.
+
+ [RFC4234] Crocker, D. and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", RFC 4234, October 2005.
+
+ [RFC4409] Gellens, R. and J. Klensin, "Message Submission for
+ Mail", RFC 4409, April 2006.
+
+ [RFC4467] Crispin, M., "Internet Message Access Protocol (IMAP) -
+ URLAUTH Extension", RFC 4467, May 2006.
+
+
+
+
+
+
+
+
+
+
+
+
+Newman Standards Track [Page 11]
+
+RFC 4468 Message Submission BURL Extension May 2006
+
+
+9.2. Informative References
+
+ [RFC2034] Freed, N., "SMTP Service Extension for Returning
+ Enhanced Error Codes", RFC 2034, October 1996.
+
+ [RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet
+ Mail Extensions (MIME) Part One: Format of Internet
+ Message Bodies", RFC 2045, November 1996.
+
+ [RFC2047] Moore, K., "MIME (Multipurpose Internet Mail
+ Extensions) Part Three: Message Header Extensions for
+ Non-ASCII Text", RFC 2047, November 1996.
+
+ [RFC2920] Freed, N., "SMTP Service Extension for Command
+ Pipelining", STD 60, RFC 2920, September 2000.
+
+ [RFC3030] Vaudreuil, G., "SMTP Service Extensions for
+ Transmission of Large and Binary MIME Messages",
+ RFC 3030, December 2000.
+
+ [RFC3463] Vaudreuil, G., "Enhanced Mail System Status Codes",
+ RFC 3463, January 2003.
+
+ [RFC4120] Neuman, C., Yu, T., Hartman, S., and K. Raeburn, "The
+ Kerberos Network Authentication Service (V5)", RFC
+ 4120, July 2005.
+
+ [SASL-PLAIN] Zeilenga, K., "The Plain SASL Mechanism", Work in
+ Progress, March 2005.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Newman Standards Track [Page 12]
+
+RFC 4468 Message Submission BURL Extension May 2006
+
+
+Appendix A. Acknowledgements
+
+ This document is a product of the lemonade WG. Many thanks are due
+ to all the participants of that working group for their input. Mark
+ Crispin was instrumental in the conception of this mechanism. Thanks
+ to Randall Gellens, Alexey Melnikov, Sam Hartman, Ned Freed, Dave
+ Cridland, Peter Coates, and Mark Crispin for review comments on the
+ document. Thanks to the RFC Editor for correcting the author's
+ grammar mistakes. Thanks to Ted Hardie, Randall Gellens, Mark
+ Crispin, Pete Resnick, and Greg Vaudreuil for extremely interesting
+ debates comparing this proposal and alternatives. Thanks to the
+ lemonade WG chairs Eric Burger and Glenn Parsons for concluding the
+ debate at the correct time and making sure this document got
+ completed.
+
+Author's Address
+
+ Chris Newman
+ Sun Microsystems
+ 3401 Centrelake Dr., Suite 410
+ Ontario, CA 91761
+ US
+
+ EMail: chris.newman@sun.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Newman Standards Track [Page 13]
+
+RFC 4468 Message Submission BURL Extension May 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is provided by the IETF
+ Administrative Support Activity (IASA).
+
+
+
+
+
+
+
+Newman Standards Track [Page 14]
+
diff --git a/imap/docs/rfc/rfc4469.txt b/imap/docs/rfc/rfc4469.txt
new file mode 100644
index 00000000..da365514
--- /dev/null
+++ b/imap/docs/rfc/rfc4469.txt
@@ -0,0 +1,731 @@
+
+
+
+
+
+
+Network Working Group P. Resnick
+Request for Comments: 4469 QUALCOMM Incorporated
+Updates: 3501, 3502 April 2006
+Category: Standards Track
+
+
+ Internet Message Access Protocol (IMAP) CATENATE Extension
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+Abstract
+
+ The CATENATE extension to the Internet Message Access Protocol (IMAP)
+ extends the APPEND command to allow clients to create messages on the
+ IMAP server that may contain a combination of new data along with
+ parts of (or entire) messages already on the server. Using this
+ extension, the client can catenate parts of an already existing
+ message onto a new message without having to first download the data
+ and then upload it back to the server.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Resnick Standards Track [Page 1]
+
+RFC 4469 IMAP CATENATE Extension April 2006
+
+
+1. Introduction
+
+ The CATENATE extension to the Internet Message Access Protocol (IMAP)
+ [1] allows the client to create a message on the server that can
+ include the text of messages (or parts of messages) that already
+ exist on the server without having to FETCH them and APPEND them back
+ to the server. The CATENATE extension extends the APPEND command so
+ that, instead of a single message literal, the command can take as
+ arguments any combination of message literals (as described in IMAP
+ [1]) and message URLs (as described in the IMAP URL Scheme [2]
+ specification). The server takes all the pieces and catenates them
+ into the output message. The CATENATE extension can also coexist
+ with the MULTIAPPEND extension [3] to APPEND multiple messages in a
+ single command.
+
+ There are some obvious uses for the CATENATE extension. The
+ motivating use case was to provide a way for a resource-constrained
+ client to compose a message for subsequent submission that contains
+ data that already exists in that client's IMAP store. Because the
+ client does not have to download and re-upload potentially large
+ message parts, bandwidth and processing limitations do not have as
+ much impact. In addition, since the client can create a message in
+ its own IMAP store, the command also addresses the desire of the
+ client to archive a copy of a sent message without having to upload
+ the message twice. (Mechanisms for sending the message are outside
+ the scope of this document.)
+
+ The extended APPEND command can also be used to copy parts of a
+ message to another mailbox for archival purposes while getting rid of
+ undesired parts. In environments where server storage is limited, a
+ client could get rid of large message parts by copying over only the
+ necessary parts and then deleting the original message. The
+ mechanism could also be used to add data to a message (such as
+ prepending message header fields) or to include other data by making
+ a copy of the original and catenating the new data.
+
+2. The CATENATE Capability
+
+ A server that supports this extension returns "CATENATE" as one of
+ the responses to the CAPABILITY command.
+
+
+
+
+
+
+
+
+
+
+
+Resnick Standards Track [Page 2]
+
+RFC 4469 IMAP CATENATE Extension April 2006
+
+
+3. The APPEND Command
+
+ Arguments: mailbox name
+ (The following can be repeated in the presence of the
+ MULTIAPPEND extension [3])
+ OPTIONAL flag parenthesized list
+ OPTIONAL date/time string
+ a single message literal or one or more message parts to
+ catenate, specified as:
+ message literal
+ or
+ message (or message part) URL
+
+ Responses: OPTIONAL NO responses: BADURL, TOOBIG
+
+ Result: OK - append completed
+ NO - append error: can't append to that mailbox, error
+ in flags or date/time or message text, or can't
+ fetch that data
+ BAD - command unknown or arguments invalid
+
+ The APPEND command concatenates all the message parts and appends
+ them as a new message to the end of the specified mailbox. The
+ parenthesized flag list and date/time string set the flags and the
+ internal date, respectively, as described in IMAP [1]. The
+ subsequent command parameters specify the message parts that are
+ appended sequentially to the output message.
+
+ If the original form of APPEND is used, a message literal follows the
+ optional flag list and date/time string, which is appended as
+ described in IMAP [1]. If the extended form is used, "CATENATE" and
+ a parenthesized list of message literals and message URLs follows,
+ each of which is appended to the new message. If a message literal
+ is specified (indicated by "TEXT"), the octets following the count
+ are appended. If a message URL is specified (indicated by "URL"),
+ the octets of the body part pointed to by that URL are appended, as
+ if the literal returned in a FETCH BODY response were put in place of
+ the message part specifier. The APPEND command does not cause the
+ \Seen flag to be set for any catenated body part. The APPEND command
+ does not change the selected mailbox.
+
+ In the extended APPEND command, the string following "URL" is an IMAP
+ URL [2] and is interpreted according to the rules of [2]. The
+ present document only describes the behavior of the command using
+ IMAP URLs that refer to specific messages or message parts on the
+ current IMAP server from the current authenticated IMAP session.
+ Because of that, only relative IMAP message or message part URLs
+ (i.e., those having no scheme or <iserver>) are used. The base URL
+
+
+
+Resnick Standards Track [Page 3]
+
+RFC 4469 IMAP CATENATE Extension April 2006
+
+
+ for evaluating the relative URL is considered "imap://user@server/",
+ where "user" is the user name of the currently authenticated user and
+ "server" is the domain name of the current server. When in the
+ selected state, the base URL is considered
+ "imap://user@server/mailbox", where "mailbox" is the encoded name of
+ the currently selected mailbox. Additionally, since the APPEND
+ command is valid in the authenticated state of an IMAP session, no
+ further LOGIN or AUTHENTICATE command is performed for URLs specified
+ in the extended APPEND command.
+
+ Note: Use of an absolute IMAP URL or any URL that refers to
+ anything other than a message or message part from the current
+ authenticated IMAP session is outside the scope of this document
+ and would require an extension to this specification, and a server
+ implementing only this specification would return NO to such a
+ request.
+
+ The client is responsible for making sure that the catenated message
+ is in the format of an Internet Message Format (RFC 2822) [4] or
+ Multipurpose Internet Mail Extension (MIME) [5] message. In
+ particular, when a URL is catenated, the server copies octets,
+ unchanged, from the indicated message or message part to the
+ catenated message. It does no data conversion (e.g., MIME transfer
+ encodings) nor any verification that the data is appropriate for the
+ MIME part of the message into which it is inserted. The client is
+ also responsible for inserting appropriate MIME boundaries between
+ body parts, and writing MIME Content-Type and Content-Transfer-
+ Encoding lines as needed in the appropriate places.
+
+ Responses behave just as the original APPEND command described in
+ IMAP [1]. If the server implements the IMAP UIDPLUS extension [6],
+ it will also return an APPENDUID response code in the tagged OK
+ response. Two response codes are provided in Section 4 that can be
+ used in the tagged NO response if the APPEND command fails.
+
+4. Response Codes
+
+ When a APPEND command fails, it may return a response code that
+ describes a reason for the failure.
+
+4.1. BADURL Response
+
+ The BADURL response code is returned if the APPEND fails to process
+ one of the specified URLs. Possible reasons for this are bad URL
+ syntax, unrecognized URL schema, invalid message UID, or invalid body
+ part. The BADURL response code contains the first URL specified as a
+ parameter to the APPEND command that has caused the operation to
+ fail.
+
+
+
+Resnick Standards Track [Page 4]
+
+RFC 4469 IMAP CATENATE Extension April 2006
+
+
+4.2. TOOBIG Response
+
+ The TOOBIG response code is returned if the resulting message will
+ exceed the 4-GB IMAP message limit. This might happen, for example,
+ if the client specifies 3 URLs for 2-GB messages. Note that even if
+ the server doesn't return TOOBIG, it still has to be defensive
+ against misbehaving or malicious clients that try to construct a
+ message over the 4-GB limit. The server may also wish to return the
+ TOOBIG response code if the resulting message exceeds a server-
+ specific message size limit.
+
+5. Formal Syntax
+
+ The following syntax specification uses the Augmented Backus-Naur
+ Form (ABNF) [7] notation. Elements not defined here can be found in
+ the formal syntax of the ABNF [7], IMAP [1], and IMAP ABNF extensions
+ [8] specifications. Note that capability and resp-text-code are
+ extended from the IMAP [1] specification and append-data is extended
+ from the IMAP ABNF extensions [8] specification.
+
+ append-data =/ "CATENATE" SP "(" cat-part *(SP cat-part) ")"
+
+ cat-part = text-literal / url
+
+ text-literal = "TEXT" SP literal
+
+ url = "URL" SP astring
+
+ resp-text-code =/ toobig-response-code / badurl-response-code
+
+ toobig-response-code = "TOOBIG"
+
+ badurl-response-code = "BADURL" SP url-resp-text
+
+ url-resp-text = 1*(%x01-09 /
+ %x0B-0C /
+ %x0E-5B /
+ %x5D-FE) ; Any TEXT-CHAR except "]"
+
+ capability =/ "CATENATE"
+
+ The astring in the definition of url and the url-resp-text in the
+ definition of badurl-response-code each contain an imapurl as defined
+ by [2].
+
+
+
+
+
+
+
+Resnick Standards Track [Page 5]
+
+RFC 4469 IMAP CATENATE Extension April 2006
+
+
+6. Acknowledgements
+
+ Thanks to the members of the LEMONADE working group for their input.
+ Special thanks to Alexey Melnikov for the examples.
+
+7. Security Considerations
+
+ The CATENATE extension does not raise any security considerations
+ that are not present for the base protocol or in the use of IMAP
+ URLs, and these issues are discussed in the IMAP [1] and IMAP URL [2]
+ documents.
+
+8. IANA Considerations
+
+ IMAP4 capabilities are registered by publishing a standards track or
+ IESG approved experimental RFC. The registry is currently located at
+ <http://www.iana.org/assignments/imap4-capabilities>. This document
+ defines the CATENATE IMAP capability. The IANA has added this
+ capability to the registry.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Resnick Standards Track [Page 6]
+
+RFC 4469 IMAP CATENATE Extension April 2006
+
+
+Appendix A. Examples
+
+ Lines not starting with "C: " or "S: " are continuations of the
+ previous lines.
+
+ The original message in examples 1 and 2 below (UID = 20) has the
+ following structure:
+
+
+ multipart/mixed MIME message with two body parts:
+
+ 1. text/plain
+
+ 2. application/x-zip-compressed
+
+ Example 1: The following example demonstrates how a CATENATE client
+ can replace an attachment in a draft message, without the need to
+ download it to the client and upload it back.
+
+ C: A003 APPEND Drafts (\Seen \Draft $MDNSent) CATENATE
+ (URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;section=HEADER"
+ TEXT {42}
+ S: + Ready for literal data
+ C:
+ C: --------------030308070208000400050907
+ C: URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;section=1.MIME"
+ URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;section=1" TEXT {42}
+ S: + Ready for literal data
+ C:
+ C: --------------030308070208000400050907
+ C: URL "/Drafts;UIDVALIDITY=385759045/;UID=30" TEXT {44}
+ S: + Ready for literal data
+ C:
+ C: --------------030308070208000400050907--
+ C: )
+ S: A003 OK catenate append completed
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Resnick Standards Track [Page 7]
+
+RFC 4469 IMAP CATENATE Extension April 2006
+
+
+ Example 2: The following example demonstrates how the CATENATE
+ extension can be used to replace edited text in a draft message, as
+ well as header fields for the top level message part (e.g., Subject
+ has changed). The previous version of the draft is marked as
+ \Deleted. Note that the server also supports the UIDPLUS extension,
+ so the APPENDUID response code is returned in the successful OK
+ response to the APPEND command.
+
+ C: A003 APPEND Drafts (\Seen \Draft $MDNSent) CATENATE (TEXT {738}
+ S: + Ready for literal data
+ C: Return-Path: <bar@example.org>
+ C: Received: from [127.0.0.2]
+ C: by rufus.example.org via TCP (internal) with ESMTPA;
+ C: Thu, 11 Nov 2004 16:57:07 +0000
+ C: Message-ID: <419399E1.6000505@example.org>
+ C: Date: Thu, 12 Nov 2004 16:57:05 +0000
+ C: From: Bob Ar <bar@example.org>
+ C: X-Accept-Language: en-us, en
+ C: MIME-Version: 1.0
+ C: To: foo@example.net
+ C: Subject: About our holiday trip
+ C: Content-Type: multipart/mixed;
+ C: boundary="------------030308070208000400050907"
+ C:
+ C: --------------030308070208000400050907
+ C: Content-Type: text/plain; charset=us-ascii; format=flowed
+ C: Content-Transfer-Encoding: 7bit
+ C:
+ C: Our travel agent has sent the updated schedule.
+ C:
+ C: Cheers,
+ C: Bob
+ C: --------------030308070208000400050907
+ C: URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;Section=2.MIME"
+ URL "/Drafts;UIDVALIDITY=385759045/;UID=20/;Section=2" TEXT {44}
+ S: + Ready for literal data
+ C:
+ C: --------------030308070208000400050907--
+ C: )
+ S: A003 OK [APPENDUID 385759045 45] append Completed
+ C: A004 UID STORE 20 +FLAGS.SILENT (\Deleted)
+ S: A004 OK STORE completed
+
+
+
+
+
+
+
+
+
+Resnick Standards Track [Page 8]
+
+RFC 4469 IMAP CATENATE Extension April 2006
+
+
+ Example 3: The following example demonstrates how the CATENATE
+ extension can be used to strip attachments. Below, a PowerPoint
+ attachment was replaced by a small text part explaining that the
+ attachment was stripped.
+
+ C: A003 APPEND Drafts (\Seen \Draft $MDNSent) CATENATE
+ (URL "/Drafts;UIDVALIDITY=385759045/;UID=21/;section=HEADER"
+ TEXT {42}
+ S: + Ready for literal data
+ C:
+ C: --------------030308070208000400050903
+ C: URL "/Drafts;UIDVALIDITY=385759045/;UID=21/;section=1.MIME"
+ URL "/Drafts;UIDVALIDITY=385759045/;UID=21/;section=1" TEXT {255}
+ S: + Ready for literal data
+ C:
+ C: --------------030308070208000400050903
+ C: Content-type: text/plain; charset="us-ascii"
+ C: Content-transfer-encoding: 7bit
+ C:
+ C: This body part contained a Power Point presentation that was
+ C: deleted upon your request.
+ C: --------------030308070208000400050903--
+ C: )
+ S: A003 OK append Completed
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Resnick Standards Track [Page 9]
+
+RFC 4469 IMAP CATENATE Extension April 2006
+
+
+ Example 4: The following example demonstrates a failed APPEND
+ command. The server returns the BADURL response code to indicate
+ that one of the provided URLs is invalid. This example also
+ demonstrates how the CATENATE extension can be used to construct a
+ digest of several messages.
+
+ C: A003 APPEND Sent (\Seen $MDNSent) CATENATE (TEXT {541}
+ S: + Ready for literal data
+ C: Return-Path: <foo@example.org>
+ C: Received: from [127.0.0.2]
+ C: by rufus.example.org via TCP (internal) with ESMTPA;
+ C: Thu, 11 Nov 2004 16:57:07 +0000
+ C: Message-ID: <419399E1.6000505@example.org>
+ C: Date: Thu, 21 Nov 2004 16:57:05 +0000
+ C: From: Farren Oo <foo@example.org>
+ C: X-Accept-Language: en-us, en
+ C: MIME-Version: 1.0
+ C: To: bar@example.org
+ C: Subject: Digest of the mailing list for today
+ C: Content-Type: multipart/digest;
+ C: boundary="------------030308070208000400050904"
+ C:
+ C: --------------030308070208000400050904
+ C: URL "/INBOX;UIDVALIDITY=785799047/;UID=11467" TEXT {42}
+ S: + Ready for literal data
+ C:
+ C: --------------030308070208000400050904
+ C: URL "/INBOX;UIDVALIDITY=785799047/;UID=113330/;section=1.5.9"
+ TEXT {42}
+ S: + Ready for literal data
+ C:
+ C: --------------030308070208000400050904
+ C: URL "/INBOX;UIDVALIDITY=785799047/;UID=11916" TEXT {44}
+ S: + Ready for literal data
+ C:
+ C: --------------030308070208000400050904--
+ C: )
+ S: A003 NO [BADURL "/INBOX;UIDVALIDITY=785799047/;UID=113330;
+ section=1.5.9"] CATENATE append has failed, one message expunged
+
+ Note that the server could have validated the URLs as they were
+ received and therefore could have returned the tagged NO response
+ with BADURL response-code in place of any continuation request after
+ the URL was received.
+
+
+
+
+
+
+
+Resnick Standards Track [Page 10]
+
+RFC 4469 IMAP CATENATE Extension April 2006
+
+
+9. Normative References
+
+ [1] Crispin, M., "INTERNET MESSAGE ACCESS PROTOCOL - VERSION 4rev1",
+ RFC 3501, March 2003.
+
+ [2] Newman, C., "IMAP URL Scheme", RFC 2192, September 1997.
+
+ [3] Crispin, M., "Internet Message Access Protocol (IMAP) -
+ MULTIAPPEND Extension", RFC 3502, March 2003.
+
+ [4] Resnick, P., "Internet Message Format", RFC 2822, April 2001.
+
+ [5] Freed, N. and N. Borenstein, "Multipurpose Internet Mail
+ Extensions (MIME) Part One: Format of Internet Message Bodies",
+ RFC 2045, November 1996.
+
+ [6] Crispin, M., "Internet Message Access Protocol (IMAP) - UIDPLUS
+ extension", RFC 4315, December 2005.
+
+ [7] Crocker, D. and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", RFC 4234, October 2005.
+
+ [8] Melnikov, A. and C. Daboo, "Collected Extensions to IMAP4 ABNF",
+ RFC 4466, April 2006.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Resnick Standards Track [Page 11]
+
+RFC 4469 IMAP CATENATE Extension April 2006
+
+
+Author's Address
+
+ Peter W. Resnick
+ QUALCOMM Incorporated
+ 5775 Morehouse Drive
+ San Diego, CA 92121-1714
+ US
+
+ Phone: +1 858 651 4478
+ EMail: presnick@qualcomm.com
+ URI: http://www.qualcomm.com/~presnick/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Resnick Standards Track [Page 12]
+
+RFC 4469 IMAP CATENATE Extension April 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is provided by the IETF
+ Administrative Support Activity (IASA).
+
+
+
+
+
+
+
+Resnick Standards Track [Page 13]
+
diff --git a/imap/docs/rfc/rfc4505.txt b/imap/docs/rfc/rfc4505.txt
new file mode 100644
index 00000000..6b8a4a11
--- /dev/null
+++ b/imap/docs/rfc/rfc4505.txt
@@ -0,0 +1,507 @@
+
+
+
+
+
+
+Network Working Group K. Zeilenga, Ed.
+Request for Comments: 4505 OpenLDAP Foundation
+Obsoletes: 2245 June 2006
+Category: Standards Track
+
+
+ Anonymous Simple Authentication and Security Layer (SASL) Mechanism
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+Abstract
+
+ On the Internet, it is common practice to permit anonymous access to
+ various services. Traditionally, this has been done with a plain-
+ text password mechanism using "anonymous" as the user name and using
+ optional trace information, such as an email address, as the
+ password. As plain-text login commands are not permitted in new IETF
+ protocols, a new way to provide anonymous login is needed within the
+ context of the Simple Authentication and Security Layer (SASL)
+ framework.
+
+1. Introduction
+
+ This document defines an anonymous mechanism for the Simple
+ Authentication and Security Layer ([SASL]) framework. The name
+ associated with this mechanism is "ANONYMOUS".
+
+ Unlike many other SASL mechanisms, whose purpose is to authenticate
+ and identify the user to a server, the purpose of this SASL mechanism
+ is to allow the user to gain access to services or resources without
+ requiring the user to establish or otherwise disclose their identity
+ to the server. That is, this mechanism provides an anonymous login
+ method.
+
+ This mechanism does not provide a security layer.
+
+ This document replaces RFC 2245. Changes since RFC 2245 are detailed
+ in Appendix A.
+
+
+
+Zeilenga Standards Track [Page 1]
+
+RFC 4505 Anonymous SASL Mechanism June 2006
+
+
+2. The Anonymous Mechanism
+
+ The mechanism consists of a single message from the client to the
+ server. The client may include in this message trace information in
+ the form of a string of [UTF-8]-encoded [Unicode] characters prepared
+ in accordance with [StringPrep] and the "trace" stringprep profile
+ defined in Section 3 of this document. The trace information, which
+ has no semantical value, should take one of two forms: an Internet
+ email address, or an opaque string that does not contain the '@'
+ (U+0040) character and that can be interpreted by the system
+ administrator of the client's domain. For privacy reasons, an
+ Internet email address or other information identifying the user
+ should only be used with permission from the user.
+
+ A server that permits anonymous access will announce support for the
+ ANONYMOUS mechanism and allow anyone to log in using that mechanism,
+ usually with restricted access.
+
+ A formal grammar for the client message using Augmented BNF [ABNF] is
+ provided below as a tool for understanding this technical
+ specification.
+
+ message = [ email / token ]
+ ;; to be prepared in accordance with Section 3
+
+ UTF1 = %x00-3F / %x41-7F ;; less '@' (U+0040)
+ UTF2 = %xC2-DF UTF0
+ UTF3 = %xE0 %xA0-BF UTF0 / %xE1-EC 2(UTF0) /
+ %xED %x80-9F UTF0 / %xEE-EF 2(UTF0)
+ UTF4 = %xF0 %x90-BF 2(UTF0) / %xF1-F3 3(UTF0) /
+ %xF4 %x80-8F 2(UTF0)
+ UTF0 = %x80-BF
+
+ TCHAR = UTF1 / UTF2 / UTF3 / UTF4
+ ;; any UTF-8 encoded Unicode character
+ ;; except '@' (U+0040)
+
+ email = addr-spec
+ ;; as defined in [IMAIL]
+
+ token = 1*255TCHAR
+
+ Note to implementors:
+ The <token> production is restricted to 255 UTF-8-encoded Unicode
+ characters. As the encoding of a characters uses a sequence of 1
+ to 4 octets, a token may be as long as 1020 octets.
+
+
+
+
+
+Zeilenga Standards Track [Page 2]
+
+RFC 4505 Anonymous SASL Mechanism June 2006
+
+
+3. The "trace" Profile of "Stringprep"
+
+ This section defines the "trace" profile of [StringPrep]. This
+ profile is designed for use with the SASL ANONYMOUS Mechanism.
+ Specifically, the client is to prepare the <message> production in
+ accordance with this profile.
+
+ The character repertoire of this profile is Unicode 3.2 [Unicode].
+
+ No mapping is required by this profile.
+
+ No Unicode normalization is required by this profile.
+
+ The list of unassigned code points for this profile is that provided
+ in Appendix A of [StringPrep]. Unassigned code points are not
+ prohibited.
+
+ Characters from the following tables of [StringPrep] are prohibited:
+
+ - C.2.1 (ASCII control characters)
+ - C.2.2 (Non-ASCII control characters)
+ - C.3 (Private use characters)
+ - C.4 (Non-character code points)
+ - C.5 (Surrogate codes)
+ - C.6 (Inappropriate for plain text)
+ - C.8 (Change display properties are deprecated)
+ - C.9 (Tagging characters)
+
+ No additional characters are prohibited.
+
+ This profile requires bidirectional character checking per Section 6
+ of [StringPrep].
+
+4. Example
+
+ Here is a sample ANONYMOUS login between an IMAP client and server.
+ In this example, "C:" and "S:" indicate lines sent by the client and
+ server, respectively. If such lines are wrapped without a new "C:"
+ or "S:" label, then the wrapping is for editorial clarity and is not
+ part of the command.
+
+ Note that this example uses the IMAP profile [IMAP4] of SASL. The
+ base64 encoding of challenges and responses as well as the "+ "
+ preceding the responses are part of the IMAP4 profile, not part of
+ SASL itself. Additionally, protocols with SASL profiles permitting
+ an initial client response will be able to avoid the extra round trip
+ below (the server response with an empty "+ ").
+
+
+
+
+Zeilenga Standards Track [Page 3]
+
+RFC 4505 Anonymous SASL Mechanism June 2006
+
+
+ In this example, the trace information is "sirhc".
+
+ S: * OK IMAP4 server ready
+ C: A001 CAPABILITY
+ S: * CAPABILITY IMAP4 IMAP4rev1 AUTH=DIGEST-MD5 AUTH=ANONYMOUS
+ S: A001 OK done
+ C: A002 AUTHENTICATE ANONYMOUS
+ S: +
+ C: c2lyaGM=
+ S: A003 OK Welcome, trace information has been logged.
+
+5. Security Considerations
+
+ The ANONYMOUS mechanism grants access to services and/or resources by
+ anyone. For this reason, it should be disabled by default so that
+ the administrator can make an explicit decision to enable it.
+
+ If the anonymous user has any write privileges, a denial-of-service
+ attack is possible by filling up all available space. This can be
+ prevented by disabling all write access by anonymous users.
+
+ If anonymous users have read and write access to the same area, the
+ server can be used as a communication mechanism to exchange
+ information anonymously. Servers that accept anonymous submissions
+ should implement the common "drop box" model, which forbids anonymous
+ read access to the area where anonymous submissions are accepted.
+
+ If the anonymous user can run many expensive operations (e.g., an
+ IMAP SEARCH BODY command), this could enable a denial-of-service
+ attack. Servers are encouraged to reduce the priority of anonymous
+ users or limit their resource usage.
+
+ While servers may impose a limit on the number of anonymous users,
+ note that such limits enable denial-of-service attacks and should be
+ used with caution.
+
+ The trace information is not authenticated, so it can be falsified.
+ This can be used as an attempt to get someone else in trouble for
+ access to questionable information. Administrators investigating
+ abuse need to realize that this trace information may be falsified.
+
+ A client that uses the user's correct email address as trace
+ information without explicit permission may violate that user's
+ privacy. Anyone who accesses an anonymous archive on a sensitive
+ subject (e.g., sexual abuse) likely has strong privacy needs.
+ Clients should not send the email address without the explicit
+ permission of the user and should offer the option of supplying no
+ trace information, thus only exposing the source IP address and time.
+
+
+
+Zeilenga Standards Track [Page 4]
+
+RFC 4505 Anonymous SASL Mechanism June 2006
+
+
+ Anonymous proxy servers could enhance this privacy but would have to
+ consider the resulting potential denial-of-service attacks.
+
+ Anonymous connections are susceptible to man-in-the-middle attacks
+ that view or alter the data transferred. Clients and servers are
+ encouraged to support external data security services.
+
+ Protocols that fail to require an explicit anonymous login are more
+ susceptible to break-ins given certain common implementation
+ techniques. Specifically, Unix servers that offer user login may
+ initially start up as root and switch to the appropriate user id
+ after an explicit login command. Normally, such servers refuse all
+ data access commands prior to explicit login and may enter a
+ restricted security environment (e.g., the Unix chroot(2) function)
+ for anonymous users. If anonymous access is not explicitly
+ requested, the entire data access machinery is exposed to external
+ security attacks without the chance for explicit protective measures.
+ Protocols that offer restricted data access should not allow
+ anonymous data access without an explicit login step.
+
+ General [SASL] security considerations apply to this mechanism.
+
+ [StringPrep] security considerations and [Unicode] security
+ considerations discussed in [StringPrep] apply to this mechanism.
+ [UTF-8] security considerations also apply.
+
+6. IANA Considerations
+
+ The SASL Mechanism registry [IANA-SASL] entry for the ANONYMOUS
+ mechanism has been updated by the IANA to reflect that this document
+ now provides its technical specification.
+
+ To: iana@iana.org
+ Subject: Updated Registration of SASL mechanism ANONYMOUS
+
+ SASL mechanism name: ANONYMOUS
+ Security considerations: See RFC 4505.
+ Published specification (optional, recommended): RFC 4505
+ Person & email address to contact for further information:
+ Kurt Zeilenga <Kurt@OpenLDAP.org>
+ Chris Newman <Chris.Newman@sun.com>
+ Intended usage: COMMON
+ Author/Change controller: IESG <iesg@ietf.org>
+ Note: Updates existing entry for ANONYMOUS
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 5]
+
+RFC 4505 Anonymous SASL Mechanism June 2006
+
+
+ The [StringPrep] profile "trace", first defined in this RFC, has been
+ registered:
+
+ To: iana@iana.org
+ Subject: Initial Registration of Stringprep "trace" profile
+
+ Stringprep profile: trace
+ Published specification: RFC 4505
+ Person & email address to contact for further information:
+ Kurt Zeilenga <kurt@openldap.org>
+
+7. Acknowledgement
+
+ This document is a revision of RFC 2245 by Chris Newman. Portions of
+ the grammar defined in Section 1 were borrowed from RFC 3629 by
+ Francois Yergeau.
+
+ This document is a product of the IETF SASL WG.
+
+8. Normative References
+
+ [ABNF] Crocker, D. and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", RFC 4234, October 2005.
+
+ [IMAIL] Resnick, P., "Internet Message Format", RFC 2822, April
+ 2001.
+
+ [SASL] Melnikov, A., Ed. and K. Zeilenga, Ed., "Simple
+ Authentication and Security Layer (SASL)", RFC 4422,
+ June 2006.
+
+ [StringPrep] Hoffman, P. and M. Blanchet, "Preparation of
+ Internationalized Strings ('stringprep')", RFC 3454,
+ December 2002.
+
+ [Unicode] The Unicode Consortium, "The Unicode Standard, Version
+ 3.2.0" is defined by "The Unicode Standard, Version 3.0"
+ (Reading, MA, Addison-Wesley, 2000. ISBN 0-201-61633-5),
+ as amended by the "Unicode Standard Annex #27: Unicode
+ 3.1" (http://www.unicode.org/reports/tr27/) and by the
+ "Unicode Standard Annex #28: Unicode 3.2"
+ (http://www.unicode.org/reports/tr28/).
+
+ [UTF-8] Yergeau, F., "UTF-8, a transformation format of ISO
+ 10646", RFC 3629 (also STD 63), November 2003.
+
+
+
+
+
+
+Zeilenga Standards Track [Page 6]
+
+RFC 4505 Anonymous SASL Mechanism June 2006
+
+
+9. Informative References
+
+ [IMAP4] Crispin, M., "INTERNET MESSAGE ACCESS PROTOCOL - VERSION
+ 4rev1", RFC 3501, March 2003.
+
+ [IANA-SASL] IANA, "SIMPLE AUTHENTICATION AND SECURITY LAYER (SASL)
+ MECHANISMS", <http://www.iana.org/assignments/sasl-
+ mechanisms>.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 7]
+
+RFC 4505 Anonymous SASL Mechanism June 2006
+
+
+Appendix A. Changes since RFC 2245
+
+ This appendix is non-normative.
+
+ RFC 2245 allows the client to include optional trace information in
+ the form of a human readable string. RFC 2245 restricted this string
+ to US-ASCII. As the Internet is international, this document uses a
+ string restricted to UTF-8 encoded Unicode characters. A
+ "stringprep" profile is defined to precisely define which Unicode
+ characters are allowed in this string. While the string remains
+ restricted to 255 characters, the encoded length of each character
+ may now range from 1 to 4 octets.
+
+ Additionally, a number of editorial changes were made.
+
+Editor's Address
+
+ Kurt D. Zeilenga
+ OpenLDAP Foundation
+
+ EMail: Kurt@OpenLDAP.org
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 8]
+
+RFC 4505 Anonymous SASL Mechanism June 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is provided by the IETF
+ Administrative Support Activity (IASA).
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 9]
+
diff --git a/imap/docs/rfc/rfc4549.txt b/imap/docs/rfc/rfc4549.txt
new file mode 100644
index 00000000..8430ee10
--- /dev/null
+++ b/imap/docs/rfc/rfc4549.txt
@@ -0,0 +1,1963 @@
+
+
+
+
+
+
+Network Working Group A. Melnikov, Ed.
+Request for Comments: 4549 Isode Ltd.
+Category: Informational June 2006
+
+
+ Synchronization Operations for Disconnected IMAP4 Clients
+
+Status of This Memo
+
+ This memo provides information for the Internet community. It does
+ not specify an Internet standard of any kind. Distribution of this
+ memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+Abstract
+
+ This document attempts to address some of the issues involved in
+ building a disconnected IMAP4 client. In particular, it deals with
+ the issues of what might be called the "driver" portion of the
+ synchronization tool: the portion of the code responsible for issuing
+ the correct set of IMAP4 commands to synchronize the disconnected
+ client in the way that is most likely to make the human who uses the
+ disconnected client happy.
+
+ This note describes different strategies that can be used by
+ disconnected clients and shows how to use IMAP protocol in order to
+ minimize the time of the synchronization process.
+
+ This note also lists IMAP extensions that a server should implement
+ in order to provide better synchronization facilities to disconnected
+ clients.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Melnikov Informational [Page 1]
+
+RFC 4549 Synch Ops for Disconnected IMAP4 Clients June 2006
+
+
+Table of Contents
+
+ 1. Introduction ....................................................3
+ 1.1. Conventions Used in This Document ..........................3
+ 2. Design Principles ...............................................3
+ 3. Overall Picture of Synchronization ..............................4
+ 4. Mailbox Synchronization Steps and Strategies ....................7
+ 4.1. Checking UID Validity ......................................7
+ 4.2. Synchronizing Local Changes with the Server ................8
+ 4.2.1. Uploading Messages to the Mailbox ...................8
+ 4.2.2. Optimizing "move" and "copy" Operations .............9
+ 4.2.3. Replaying Local Flag Changes .......................14
+ 4.2.4. Processing Mailbox Compression (EXPUNGE) Requests ..15
+ 4.2.5. Closing a Mailbox ..................................17
+ 4.3. Details of "Normal" Synchronization of a Single Mailbox ...18
+ 4.3.1. Discovering New Messages and Changes to Old
+ Messages ...........................................18
+ 4.3.2. Searching for "Interesting" Messages. ..............20
+ 4.3.3. Populating Cache with "Interesting" Messages. ......21
+ 4.3.4. User-Initiated Synchronization .....................22
+ 4.4. Special Case: Descriptor-Only Synchronization .............22
+ 4.5. Special Case: Fast New-Only Synchronization ...............23
+ 4.6. Special Case: Blind FETCH .................................23
+ 5. Implementation Considerations ..................................24
+ 5.1. Error Recovery during Playback ............................26
+ 5.2. Quality of Implementation Issues ..........................28
+ 5.3. Optimizations .............................................28
+ 6. IMAP Extensions That May Help ..................................30
+ 6.1. CONDSTORE Extension .......................................30
+ 7. Security Considerations ........................................33
+ 8. References .....................................................33
+ 8.1. Normative References ......................................33
+ 8.2. Informative References ....................................34
+ 9. Acknowledgements ...............................................34
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Melnikov Informational [Page 2]
+
+RFC 4549 Synch Ops for Disconnected IMAP4 Clients June 2006
+
+
+1. Introduction
+
+ Several recommendations presented in this document are generally
+ applicable to all types of IMAP clients. However, this document
+ tries to concentrate on disconnected mail clients [IMAP-MODEL]. It
+ also suggests some IMAP extensions* that should be implemented by
+ IMAP servers in order to make the life of disconnected clients
+ easier. In particular, the [UIDPLUS] extension was specifically
+ designed to streamline certain disconnected operations, like
+ expunging, uploading, and copying messages (see Sections 4.2.1,
+ 4.2.2.1, and 4.2.4).
+
+ Readers of this document are also strongly advised to read RFC 2683
+ [RFC2683].
+
+ * Note that the functionality provided by the base IMAP protocol
+ [IMAP4] is sufficient to perform basic synchronization.
+
+1.1. Conventions Used in This Document
+
+ In examples, "C:" and "S:" indicate lines sent by the client and
+ server, respectively. Long lines in examples are broken for
+ editorial clarity.
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in RFC 2119 [KEYWORDS].
+
+ Let's call an IMAP command idempotent if the result of executing the
+ command twice sequentially is the same as the result of executing the
+ command just once.
+
+2. Design Principles
+
+ All mailbox state or content information stored on the disconnected
+ client should be viewed strictly as a cache of the state of the
+ server. The "master" state remains on the server, just as it would
+ with an interactive IMAP4 client. The one exception to this rule is
+ that information about the state of the disconnected client's cache
+ (the state includes flag changes while offline and during scheduled
+ message uploads) remains on the disconnected client: that is, the
+ IMAP4 server is not responsible for remembering the state of the
+ disconnected IMAP4 client.
+
+ We assume that a disconnected client is a client that, for whatever
+ reason, wants to minimize the length of time that it is "on the
+ phone" to the IMAP4 server. Often this will be because the client is
+ using a dialup connection, possibly with very low bandwidth, but
+
+
+
+Melnikov Informational [Page 3]
+
+RFC 4549 Synch Ops for Disconnected IMAP4 Clients June 2006
+
+
+ sometimes it might just be that the human is in a hurry to catch an
+ airplane, or some other event beyond our control. Whatever the
+ reason, we assume that we must make efficient use of the network
+ connection, both in the usual sense (not generating spurious traffic)
+ and in the sense that we would prefer not to have the connection
+ sitting idle while the client and/or the server is performing
+ strictly local computation or I/O. Another, perhaps simpler way of
+ stating this is that we assume that network connections are
+ "expensive".
+
+ Practical experience with disconnected mail systems has shown that
+ there is no single synchronization strategy that is appropriate for
+ all cases. Different humans have different preferences, and the same
+ human's preference will vary depending both on external circumstance
+ (how much of a hurry the human is in today) and on the value that the
+ human places on the messages being transferred. The point here is
+ that there is no way that the synchronization program can guess
+ exactly what the human wants to do, so the human will have to provide
+ some guidance.
+
+ Taken together, the preceding two principles lead to the conclusion
+ that the synchronization program must make its decisions based on
+ some kind of guidance provided by the human, by selecting the
+ appropriate options in the user interface or through some sort of
+ configuration file. Almost certainly, it should not pause for I/O
+ with the human in the middle of the synchronization process. The
+ human will almost certainly have several different configurations for
+ the synchronization program, for different circumstances.
+
+ Since a disconnected client has no way of knowing what changes might
+ have occurred to the mailbox while it was disconnected, message
+ numbers are not useful to a disconnected client. All disconnected
+ client operations should be performed using UIDs, so that the client
+ can be sure that it and the server are talking about the same
+ messages during the synchronization process.
+
+3. Overall Picture of Synchronization
+
+ The basic strategy for synchronization is outlined below. Note that
+ the real strategy may vary from one application to another or may
+ depend on a synchronization mode.
+
+ a) Process any "actions" that were pending on the client that were
+ not associated with any mailbox. (In particular sending messages
+ composed offline with SMTP. This is not part of IMAP
+ synchronization, but it is mentioned here for completeness.)
+
+
+
+
+
+Melnikov Informational [Page 4]
+
+RFC 4549 Synch Ops for Disconnected IMAP4 Clients June 2006
+
+
+ b) Fetch the current list of "interesting" mailboxes. (The
+ disconnected client should allow the user to skip this step
+ completely.)
+
+ c) "Client-to-server synchronization": for each IMAP "action" that
+ was pending on the client, do the following:
+
+ 1) If the action implies opening a new mailbox (any operation that
+ operates on messages), open the mailbox. Check its UID
+ validity value (see Section 4.1 for more details) returned in
+ the UIDVALIDITY response code. If the UIDVALIDITY value
+ returned by the server differs, the client MUST empty the local
+ cache of the mailbox and remove any pending "actions" that
+ refer to UIDs in that mailbox (and consider them failed). Note
+ that this doesn't affect actions performed on client-generated
+ fake UIDs (see Section 5).
+
+ 2) Perform the action. If the action is to delete a mailbox
+ (DELETE), make sure that the mailbox is closed first (see also
+ Section 3.4.12 of [RFC2683]).
+
+ d) "Server-to-client synchronization": for each mailbox that requires
+ synchronization, do the following:
+
+ 1) Check the mailbox UIDVALIDITY (see Section 4.1 for more
+ details) with SELECT/EXAMINE/STATUS.
+
+ If UIDVALIDITY value returned by the server differs, the client
+ MUST
+
+ * empty the local cache of that mailbox;
+ * remove any pending "actions" that refer to UIDs in that
+ mailbox and consider them failed; and
+ * skip step 2-II.
+
+ 2) Fetch the current "descriptors";
+
+ I) Discover new messages.
+
+ II) Discover changes to old messages.
+
+ 3) Fetch the bodies of any "interesting" messages that the client
+ doesn't already have.
+
+ e) Close all open mailboxes not required for further operations (if
+ staying online) or disconnect all open connections (if going
+ offline).
+
+
+
+
+Melnikov Informational [Page 5]
+
+RFC 4549 Synch Ops for Disconnected IMAP4 Clients June 2006
+
+
+ Terms used:
+
+ "Actions" are queued requests that were made by the human to the
+ client's Mail User Agent (MUA) software while the client was
+ disconnected.
+
+ We define "descriptors" as a set of IMAP4 FETCH data items.
+ Conceptually, a message's descriptor is that set of information that
+ allows the synchronization program to decide what protocol actions
+ are necessary to bring the local cache to the desired state for this
+ message; since this decision is really up to the human, this
+ information probably includes at least a few header fields intended
+ for human consumption. Exactly what will constitute a descriptor
+ depends on the client implementation. At a minimum, the descriptor
+ contains the message's UID and FLAGS. Other likely candidates are
+ the RFC822.SIZE, RFC822.HEADER, BODYSTRUCTURE, or ENVELOPE data
+ items.
+
+ Comments:
+
+ 1) The list of actions should be ordered. For example, if the human
+ deletes message A1 in mailbox A, then expunges mailbox A, and then
+ deletes message A2 in mailbox A, the human will expect that
+ message A1 is gone and that message A2 is still present but is now
+ deleted.
+
+ By processing all the actions before proceeding with
+ synchronization, we avoid having to compensate for the local MUA's
+ changes to the server's state. That is, once we have processed
+ all the pending actions, the steps that the client must take to
+ synchronize itself will be the same no matter where the changes to
+ the server's state originated.
+
+ 2) Steps a and b can be performed in parallel. Alternatively, step a
+ can be performed after d.
+
+ 3) On step b, the set of "interesting" mailboxes pretty much has to
+ be determined by the human. What mailboxes belong to this set may
+ vary between different IMAP4 sessions with the same server,
+ client, and human. An interesting mailbox can be a mailbox
+ returned by LSUB command (see Section 6.3.9 of [IMAP4]). The
+ special mailbox "INBOX" SHOULD be in the default set of mailboxes
+ that the client considers interesting. However, providing the
+ ability to ignore INBOX for a particular session or client may be
+ valuable for some mail filtering strategies.
+
+
+
+
+
+
+Melnikov Informational [Page 6]
+
+RFC 4549 Synch Ops for Disconnected IMAP4 Clients June 2006
+
+
+ 4) On step d-2-II, the client also finds out about changes to the
+ flags of messages that the client already has in its local cache,
+ and about messages in the local cache that no longer exist on the
+ server (i.e., messages that have been expunged).
+
+ 5) "Interesting" messages are those messages that the synchronization
+ program thinks the human wants to have cached locally, based on
+ the configuration and the data retrieved in step b.
+
+ 6) A disconnected IMAP client is a special case of an IMAP client, so
+ it MUST be able to handle any "unexpected" unsolicited responses,
+ like EXISTS and EXPUNGE, at any time. The disconnected client MAY
+ ignore EXPUNGE response during "client-to-server" synchronization
+ phase (step c).
+
+ The rest of this discussion will focus primarily on the
+ synchronization issues for a single mailbox.
+
+4. Mailbox Synchronization Steps and Strategies
+
+4.1. Checking UID Validity
+
+ The "UID validity" of a mailbox is a number returned in an
+ UIDVALIDITY response code in an OK untagged response at mailbox
+ selection time. The UID validity value changes between sessions when
+ UIDs fail to persist between sessions.
+
+ Whenever the client selects a mailbox, the client must compare the
+ returned UID validity value with the value stored in the local cache.
+ If the UID validity values differ, the UIDs in the client's cache are
+ no longer valid. The client MUST then empty the local cache of that
+ mailbox and remove any pending "actions" that refer to UIDs in that
+ mailbox. The client MAY also issue a warning to the human. The
+ client MUST NOT cancel any scheduled uploads (i.e., APPENDs) for the
+ mailbox.
+
+ Note that UIDVALIDITY is not only returned on a mailbox selection.
+ The COPYUID and APPENDUID response codes defined in the [UIDPLUS]
+ extension (see also 4.2.2) and the UIDVALIDITY STATUS response data
+ item also contain a UIDVALIDITY value for some other mailbox. The
+ client SHOULD behave as described in the previous paragraph (but it
+ should act on the other mailbox's cache), no matter how it obtained
+ the UIDVALIDITY value.
+
+
+
+
+
+
+
+
+Melnikov Informational [Page 7]
+
+RFC 4549 Synch Ops for Disconnected IMAP4 Clients June 2006
+
+
+4.2. Synchronizing Local Changes with the Server
+
+4.2.1. Uploading Messages to the Mailbox
+
+ Two of the most common examples of operations resulting in message
+ uploads are:
+
+ 1) Saving a draft message
+
+ 2) Copying a message between remote mailboxes on two different IMAP
+ servers or a local mailbox and a remote mailbox.
+
+ Message upload is performed with the APPEND command. A message
+ scheduled to be uploaded has no UID associated with it, as all UIDs
+ are assigned by the server. The APPEND command will effectively
+ associate a UID with the uploaded message that can be stored in the
+ local cache for future reference. However, [IMAP4] doesn't describe
+ a simple mechanism to discover the message UID by just performing the
+ APPEND command. In order to discover the UID, the client can do one
+ of the following:
+
+ 1) Remove the uploaded message from cache. Then, use the mechanism
+ described in 4.3 to fetch the information about the uploaded
+ message as if it had been uploaded by some other client.
+
+ 2) Try to fetch header information as described in 4.2.2 in order to
+ find a message that corresponds to the uploaded message. One
+ strategy for doing this is described in 4.2.2.
+
+ Case 1 describes a not particularly smart client.
+
+ C: A003 APPEND Drafts (\Seen $MDNSent) {310}
+ S: + Ready for literal data
+ C: Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
+ C: From: Fred Foobar <foobar@blt.example.COM>
+ C: Subject: afternoon meeting
+ C: To: mooch@owatagu.siam.edu
+ C: Message-Id: <B27397-0100000@blt.example.COM>
+ C: MIME-Version: 1.0
+ C: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
+ C:
+ C: Hello Joe, do you think we can meet at 3:30 tomorrow?
+ C:
+ S: A003 OK APPEND Completed
+
+ Fortunately, there is a simpler way to discover the message UID in
+ the presence of the [UIDPLUS] extension:
+
+
+
+
+Melnikov Informational [Page 8]
+
+RFC 4549 Synch Ops for Disconnected IMAP4 Clients June 2006
+
+
+ C: A003 APPEND Drafts (\Seen $MDNSent) {310}
+ S: + Ready for literal data
+ C: Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
+ C: From: Fred Foobar <foobar@blt.example.COM>
+ C: Subject: afternoon meeting
+ C: To: mooch@owatagu.siam.edu
+ C: Message-Id: <B27397-0100000@blt.example.COM>
+ C: MIME-Version: 1.0
+ C: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
+ C:
+ C: Hello Joe, do you think we can meet at 3:30 tomorrow?
+ C:
+ S: A003 OK [APPENDUID 1022843275 77712] APPEND completed
+
+ The UID of the appended message is the second parameter of APPENDUID
+ response code.
+
+4.2.2. Optimizing "move" and "copy" Operations
+
+ Practical experience with IMAP and other mailbox access protocols
+ that support multiple mailboxes suggests that moving a message from
+ one mailbox to another is an extremely common operation.
+
+4.2.2.1. Moving a Message between Two Mailboxes on the Same Server
+
+ In IMAP4, a "move" operation between two mailboxes on the same server
+ is really a combination of a COPY operation and a STORE +FLAGS
+ (\Deleted) operation. This makes good protocol sense for IMAP, but
+ it leaves a simple-minded disconnected client in the silly position
+ of deleting and possibly expunging its cached copy of a message, then
+ fetching an identical copy via the network.
+
+ However, the presence of the UIDPLUS extension in the server can
+ help:
+
+ C: A001 UID COPY 567,414 "Interesting Messages"
+ S: A001 OK [COPYUID 1022843275 414,567 5:6] Completed
+
+ This tells the client that the message with UID 414 in the current
+ mailbox was successfully copied to the mailbox "Interesting Messages"
+ and was given the UID 5, and that the message with UID 567 was given
+ the UID 6.
+
+ In the absence of UIDPLUS extension support in the server, the
+ following trick can be used. By including the Message-ID: header and
+ the INTERNALDATE data item as part of the descriptor, the client can
+ check the descriptor of a "new" message against messages that are
+ already in its cache and avoid fetching the extra copy. Of course,
+
+
+
+Melnikov Informational [Page 9]
+
+RFC 4549 Synch Ops for Disconnected IMAP4 Clients June 2006
+
+
+ it's possible that the cost of checking to see if the message is
+ already in the local cache may exceed the cost of just fetching it,
+ so this technique should not be used blindly. If the MUA implements
+ a "move" command, it makes special provisions to use this technique
+ when it knows that a copy/delete sequence is the result of a "move"
+ command.
+
+ Note that servers are not required (although they are strongly
+ encouraged with "SHOULD language") to preserve INTERNALDATE when
+ copying messages.
+
+ Also note that since it's theoretically possible for this algorithm
+ to find the wrong message (given sufficiently malignant Message-ID
+ headers), implementers should provide a way to disable this
+ optimization, both permanently and on a message-by-message basis.
+
+ Example 1: Copying a message in the absence of UIDPLUS extension.
+
+ At some point in time the client has fetched the source message and
+ some information was cached:
+
+ C: C021 UID FETCH <uids> (BODY.PEEK[] INTERNALDATE FLAGS)
+ ...
+ S: * 27 FETCH (UID 123 INTERNALDATE "31-May-2002 05:26:59 -0600"
+ FLAGS (\Draft $MDNSent) BODY[] {1036}
+ S: ...
+ S: Message-Id: <20040903110856.22a127cd@chardonnay>
+ S: ...
+ S: ...message body...
+ S: )
+ ...
+ S: C021 OK fetch completed
+
+ Later on, the client decides to copy the message:
+
+ C: C035 UID COPY 123 "Interesting Messages"
+ S: C035 OK Completed
+
+ As the server hasn't provided the COPYUID response code, the client
+ tries the optimization described above:
+
+ C: C036 SELECT "Interesting Messages"
+ ...
+ C: C037 UID SEARCH ON 31-May-2002 HEADER
+ "Message-Id" "20040903110856.22a127cd@chardonnay"
+ S: SEARCH 12368
+ S: C037 OK completed
+
+
+
+
+Melnikov Informational [Page 10]
+
+RFC 4549 Synch Ops for Disconnected IMAP4 Clients June 2006
+
+
+ Note that if the server has returned multiple UIDs in the SEARCH
+ response, the client MUST NOT use any of the returned UID.
+
+4.2.2.2. Moving a Message from a Remote Mailbox to a Local
+
+ Moving a message from a remote mailbox to a local is done with FETCH
+ (that includes FLAGS and INTERNALDATE) followed by UID STORE <uid>
+ +FLAGS.SILENT (\Deleted):
+
+ C: A003 UID FETCH 123 (BODY.PEEK[] INTERNALDATE FLAGS)
+ S: * 27 FETCH (UID 123 INTERNALDATE "31-May-2002 05:26:59 -0600"
+ FLAGS (\Seen $MDNSent) BODY[]
+ S: ...message body...
+ S: )
+ S: A003 OK UID FETCH completed
+ C: A004 UID STORE <uid> +FLAGS.SILENT (\Deleted)
+ S: A004 STORE completed
+
+ Note that there is no reason to fetch the message during
+ synchronization if it's already in the client's cache. Also, the
+ client SHOULD preserve delivery date in the local cache.
+
+4.2.2.3. Moving a Message from a Local Mailbox to a Remote
+
+ Moving a message from a local mailbox to a remote is done with
+ APPEND:
+
+ C: A003 APPEND Drafts (\Seen $MDNSent) "31-May-2002 05:26:59 -0600"
+ {310}
+ S: + Ready for literal data
+ C: Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
+ C: From: Fred Foobar <foobar@blt.example.COM>
+ C: Subject: afternoon meeting
+ C: To: mooch@owatagu.siam.edu
+ C: Message-Id: <B27397-0100000@blt.example.COM>
+ C: MIME-Version: 1.0
+ C: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
+ C:
+ C: Hello Joe, do you think we can meet at 3:30 tomorrow?
+ C:
+ S: A003 OK [APPENDUID 1022843275 77712] completed
+
+ The client SHOULD specify the delivery date from the local cache in
+ the APPEND.
+
+ If the [LITERAL+] extension is available, the client can save a
+ round-trip*:
+
+
+
+
+Melnikov Informational [Page 11]
+
+RFC 4549 Synch Ops for Disconnected IMAP4 Clients June 2006
+
+
+ C: A003 APPEND Drafts (\Seen $MDNSent) "31-May-2002 05:26:59 -0600"
+ {310+}
+ C: Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
+ C: From: Fred Foobar <foobar@blt.example.COM>
+ C: Subject: afternoon meeting
+ C: To: mooch@owatagu.siam.edu
+ C: Message-Id: <B27397-0100000@blt.example.COM>
+ C: MIME-Version: 1.0
+ C: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
+ C:
+ C: Hello Joe, do you think we can meet at 3:30 tomorrow?
+ C:
+ S: A003 OK [APPENDUID 1022843275 77712] completed
+
+ * Note that there is a risk that the server will reject the message
+ due to its size. If this happens, the client will waste bandwidth
+ transferring the whole message. If the client wouldn't have used
+ the LITERAL+, this could have been avoided:
+
+ C: A003 APPEND Drafts (\Seen $MDNSent) "31-May-2004 05:26:59 -0600"
+ {16777215}
+ S: A003 NO Sorry, message is too big
+
+4.2.2.4. Moving a Message between Two Mailboxes on Different Servers
+
+ Moving a message between two mailbox on two different servers is a
+ combination of the operations described in 4.2.2.2 followed by the
+ operations described in 4.2.2.3.
+
+4.2.2.5. Uploading Multiple Messages to a Remote Mailbox with
+ MULTIAPPEND
+
+ When there is a need to upload multiple messages to a remote mailbox
+ (e.g., as per 4.2.2.3), the presence of certain IMAP extensions may
+ significantly improve performance. One of them is [MULTIAPPEND].
+
+ For some mail stores, opening a mailbox for appending might be
+ expensive. [MULTIAPPEND] tells the server to open the mailbox once
+ (instead of opening and closing it "n" times per "n" messages to be
+ uploaded) and to keep it open while a group of messages is being
+ uploaded to the server.
+
+ Also, if the server supports both [MULTIAPPEND] and [LITERAL+]
+ extensions, the entire upload is accomplished in a single
+ command/response round-trip.
+
+
+
+
+
+
+Melnikov Informational [Page 12]
+
+RFC 4549 Synch Ops for Disconnected IMAP4 Clients June 2006
+
+
+ Note: Client implementers should be aware that [MULTIAPPEND] performs
+ append of multiple messages atomically. This means, for example, if
+ there is not enough space to save "n"-th message (or the message has
+ invalid structure and is rejected by the server) after successful
+ upload of "n-1" messages, the whole upload operation fails, and no
+ message will be saved in the mailbox. Although this behavior might
+ be desirable in certain situations, it might not be what you want.
+ Otherwise, the client should use the regular APPEND command (Section
+ 4.2.2.3), possibly utilizing the [LITERAL+] extension. See also
+ Section 5.1 for discussions about error recovery.
+
+ Note: MULTIAPPEND can be used together with the UIDPLUS extension in
+ a way similar to what was described in Section 4.2.1. [MULTIAPPEND]
+ extends the syntax of the APPENDUID response code to allow for
+ multiple message UIDs in the second parameter.
+
+ Example 2:
+
+ This example demonstrates the use of MULTIAPPEND together with
+ UIDPLUS (synchronization points where the client waits for
+ confirmations from the server are marked with "<--->"):
+
+ C: A003 APPEND Jan-2002 (\Seen $MDNSent) "31-May-2002 05:26:59 -0600"
+ {310}
+ <--->
+ S: + Ready for literal data
+ C: Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
+ C: From: Fred Foobar <foobar@blt.example.COM>
+ C: Subject: afternoon meeting
+ C: To: mooch@owatagu.siam.edu
+ C: Message-Id: <B27397-0100000@blt.example.COM>
+ C: MIME-Version: 1.0
+ C: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
+ C:
+ C: Hello Joe, do you think we can meet at 3:30 tomorrow?
+ C: (\Seen) " 1-Jun-2002 22:43:04 -0800" {286}
+ <--->
+ S: + Ready for literal data
+ C: Date: Mon, 7 Feb 1994 22:43:04 -0800 (PST)
+ C: From: Joe Mooch <mooch@OWaTaGu.siam.EDU>
+ C: Subject: Re: afternoon meeting
+ C: To: foobar@blt.example.com
+ C: Message-Id: <a0434793874930@OWaTaGu.siam.EDU>
+ C: MIME-Version: 1.0
+ C: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
+ C:
+ C: 3:30 is fine with me.
+ C:
+
+
+
+Melnikov Informational [Page 13]
+
+RFC 4549 Synch Ops for Disconnected IMAP4 Clients June 2006
+
+
+ S: A003 OK [APPENDUID 1022843275 77712,77713] completed
+
+ The upload takes 3 round-trips.
+
+ Example 3:
+
+ In this example, Example 2 was modified for the case when the server
+ supports MULTIAPPEND, LITERAL+, and UIDPLUS. The upload takes only 1
+ round-trip.
+
+ C: A003 APPEND Jan-2002 (\Seen $MDNSent) "31-May-2002 05:26:59 -0600"
+ {310+}
+ C: Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
+ C: From: Fred Foobar <foobar@blt.example.COM>
+ C: Subject: afternoon meeting
+ C: To: mooch@owatagu.siam.edu
+ C: Message-Id: <B27397-0100000@blt.example.COM>
+ C: MIME-Version: 1.0
+ C: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
+ C:
+ C: Hello Joe, do you think we can meet at 3:30 tomorrow?
+ C: (\Seen) " 1-Jun-2002 22:43:04 -0800" {286+}
+ C: Date: Mon, 7 Feb 1994 22:43:04 -0800 (PST)
+ C: From: Joe Mooch <mooch@OWaTaGu.siam.EDU>
+ C: Subject: Re: afternoon meeting
+ C: To: foobar@blt.example.com
+ C: Message-Id: <a0434793874930@OWaTaGu.siam.EDU>
+ C: MIME-Version: 1.0
+ C: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
+ C:
+ C: 3:30 is fine with me.
+ C:
+ S: A003 OK [APPENDUID 1022843275 77712,77713] completed
+
+4.2.3. Replaying Local Flag Changes
+
+ The disconnected client uses the STORE command to synchronize local
+ flag state with the server. The disconnected client SHOULD use
+ +FLAGS.SILENT or -FLAGS.SILENT in order to set or unset flags
+ modified by the user while offline. The FLAGS form MUST NOT be used,
+ as there is a risk that this will overwrite flags on the server that
+ have been changed by some other client.
+
+ Example 4:
+
+ For the message with UID 15, the disconnected client stores the
+ following flags \Seen and $Highest. The flags were modified on the
+ server by some other client: \Seen, \Answered, and $Highest. While
+
+
+
+Melnikov Informational [Page 14]
+
+RFC 4549 Synch Ops for Disconnected IMAP4 Clients June 2006
+
+
+ offline, the user requested that the $Highest flags be removed and
+ that the \Deleted flag be added. The flag synchronization sequence
+ for the message should look like:
+
+ C: A001 UID STORE 15 +FLAGS.SILENT (\Deleted)
+ S: A001 STORE completed
+ C: A002 UID STORE 15 -FLAGS.SILENT ($Highest)
+ S: A002 STORE completed
+
+ If the disconnected client is able to store an additional binary
+ state information (or a piece of information that can take a value
+ from a predefined set of values) in the local cache of an IMAP
+ mailbox or in a local mailbox (e.g., message priority), and if the
+ server supports storing of arbitrary keywords, the client MUST use
+ keywords to store this state on the server.
+
+ Example 5:
+
+ Imagine a speculative mail client that can mark a message as one of
+ work-related ($Work), personal ($Personal), or spam ($Spam). In
+ order to mark a message as personal, the client issues:
+
+ C: A001 UID STORE 15 +FLAGS.SILENT ($Personal)
+ S: A001 STORE completed
+ C: A002 UID STORE 15 -FLAGS.SILENT ($Work $Spam)
+ S: A002 STORE completed
+
+ In order to mark the message as not work, not personal and not spam,
+ the client issues:
+
+ C: A003 UID STORE 15 -FLAGS.SILENT ($Personal $Work $Spam)
+ S: A003 STORE completed
+
+4.2.4. Processing Mailbox Compression (EXPUNGE) Requests
+
+ A naive disconnected client implementation that supports compressing
+ a mailbox while offline may decide to issue an EXPUNGE command to the
+ server in order to expunge messages marked \Deleted. The problem
+ with this command during synchronization is that it permanently
+ erases all messages with the \Deleted flag set, i.e., even those
+ messages that were marked as \Deleted on the server while the user
+ was offline. Doing this might result in an unpleasant surprise for
+ the user.
+
+ Fortunately the [UIDPLUS] extension can help in this case as well.
+ The extension introduces UID EXPUNGE command, that, unlike EXPUNGE,
+ takes a UID set parameter, that lists UIDs of all messages that can
+ be expunged. When processing this command the server erases only
+
+
+
+Melnikov Informational [Page 15]
+
+RFC 4549 Synch Ops for Disconnected IMAP4 Clients June 2006
+
+
+ messages with \Deleted flag listed in the UID list. Thus, messages
+ not listed in the UID set will not be expunged even if they have the
+ \Deleted flag set.
+
+ Example 6:
+
+ While the user was offline, 3 messages with UIDs 7, 27, and 65 were
+ marked \Deleted when the user requested to compress the open mailbox.
+ Another client marked a message \Deleted on the server (UID 34).
+ During synchronization, the disconnected client issues:
+
+ C: A001 UID EXPUNGE 7,27,65
+ S: * ... EXPUNGE
+ S: * ... EXPUNGE
+ S: * ... EXPUNGE
+ S: A001 UID EXPUNGE completed
+
+ If another client issues UID SEARCH DELETED command (to find all
+ messages with the \Deleted flag) before and after the UID EXPUNGE, it
+ will get:
+
+ Before:
+
+ C: B001 UID SEARCH DELETED
+ S: * SEARCH 65 34 27 7
+ S: B001 UID SEARCH completed
+
+ After:
+
+ C: B002 UID SEARCH DELETED
+ S: * SEARCH 34
+ S: B002 UID SEARCH completed
+
+ In the absence of the [UIDPLUS] extension, the following sequence of
+ commands can be used as an approximation. Note: It's possible for
+ another client to mark additional messages as deleted while this
+ sequence is being performed. In this case, these additional messages
+ will be expunged as well.
+
+ 1) Find all messages marked \Deleted on the server.
+
+ C: A001 UID SEARCH DELETED
+ S: * SEARCH 65 34 27 7
+ S: A001 UID SEARCH completed
+
+ 2) Find all messages that must not be erased (for the previous
+ example the list will consist of the message with UID 34).
+
+
+
+
+Melnikov Informational [Page 16]
+
+RFC 4549 Synch Ops for Disconnected IMAP4 Clients June 2006
+
+
+ 3) Temporarily remove \Deleted flag on all messages found in step 2.
+
+ C: A002 UID STORE 34 -FLAGS.SILENT (\Deleted)
+ S: A002 UID STORE completed
+
+ 4) Expunge the mailbox.
+
+ C: A003 EXPUNGE
+ S: * 20 EXPUNGE
+ S: * 7 EXPUNGE
+ S: * 1 EXPUNGE
+ S: A003 EXPUNGE completed
+
+ Here, the message with UID 7 has message number 1, with UID 27 has
+ message number 7, and with UID 65 has message number 20.
+
+ 5) Restore \Deleted flag on all messages found when performing step
+ 2.
+
+ C: A004 UID STORE 34 +FLAGS.SILENT (\Deleted)
+ S: A004 UID STORE completed
+
+4.2.5. Closing a Mailbox
+
+ When the disconnected client has to close a mailbox, it should not
+ use the CLOSE command, because CLOSE does a silent EXPUNGE. (Section
+ 4.2.4 explains why EXPUNGE should not be used by a disconnected
+ client.) It is safe to use CLOSE only if the mailbox was opened with
+ EXAMINE.
+
+ If the mailbox was opened with SELECT, the client can use one of the
+ following commands to implicitly close the mailbox and prevent the
+ silent expunge:
+
+ 1) UNSELECT - This is a command described in [UNSELECT] that works as
+ CLOSE, but doesn't cause the silent EXPUNGE. This command is
+ supported by the server if it reports UNSELECT in its CAPABILITY
+ list.
+
+ 2) SELECT <another_mailbox> - SELECT causes implicit CLOSE without
+ EXPUNGE.
+
+ 3) If the client intends to issue LOGOUT after closing the mailbox,
+ it may just issue LOGOUT, because LOGOUT causes implicit CLOSE
+ without EXPUNGE as well.
+
+ 4) SELECT <non_existing_mailbox> - If the client knows a mailbox that
+ doesn't exist or can't be selected, it MAY SELECT it.
+
+
+
+Melnikov Informational [Page 17]
+
+RFC 4549 Synch Ops for Disconnected IMAP4 Clients June 2006
+
+
+ If the client opened the mailbox with SELECT and just wants to avoid
+ implicit EXPUNGE without closing the mailbox, it may also use the
+ following:
+
+ 5) EXAMINE <mailbox> - Reselect the same mailbox in read-only mode.
+
+4.3. Details of "Normal" Synchronization of a Single Mailbox
+
+ The most common form of synchronization is where the human trusts the
+ integrity of the client's copy of the state of a particular mailbox
+ and simply wants to bring the client's cache up to date so that it
+ accurately reflects the mailbox's current state on the server.
+
+4.3.1. Discovering New Messages and Changes to Old Messages
+
+ Let <lastseenuid> represent the highest UID that the client knows
+ about in this mailbox. Since UIDs are allocated in strictly
+ ascending order, this is simply the UID of the last message in the
+ mailbox that the client knows about. Let <lastseenuid+1> represent
+ <lastseenuid>'s UID plus one. Let <descriptors> represent a list
+ consisting of all the FETCH data item items that the implementation
+ considers part of the descriptor; at a minimum this is just the FLAGS
+ data item, but it usually also includes BODYSTRUCTURE and
+ RFC822.SIZE. At this step, <descriptors> SHOULD NOT include RFC822.
+
+ With no further information, the client can issue the following two
+ commands:
+
+ tag1 UID FETCH <lastseenuid+1>:* <descriptors>
+ tag2 UID FETCH 1:<lastseenuid> FLAGS
+
+ The first command will request some information about "new" messages
+ (i.e., messages received by the server since the last
+ synchronization). It will also allow the client to build a message
+ number to UID map (only for new messages). The second command allows
+ the client to
+
+ 1) update cached flags for old messages;
+
+ 2) find out which old messages got expunged; and
+
+ 3) build a mapping between message numbers and UIDs (for old
+ messages).
+
+ The order here is significant. We want the server to start returning
+ the list of new message descriptors as fast as it can, so that the
+ client can start issuing more FETCH commands, so we start out by
+ asking for the descriptors of all the messages we know the client
+
+
+
+Melnikov Informational [Page 18]
+
+RFC 4549 Synch Ops for Disconnected IMAP4 Clients June 2006
+
+
+ cannot possibly have cached yet. The second command fetches the
+ information we need to determine what changes may have occurred to
+ messages that the client already has cached. Note that the former
+ command should only be issued if the UIDNEXT value cached by the
+ client differs from the one returned by the server. Once the client
+ has issued these two commands, there's nothing more the client can do
+ with this mailbox until the responses to the first command start
+ arriving. A clever synchronization program might use this time to
+ fetch its local cache state from disk or to start the process of
+ synchronizing another mailbox.
+
+ The following is an example of the first FETCH:
+
+ C: A011 UID fetch 131:* (FLAGS BODYSTRUCTURE INTERNALDATE
+ RFC822.SIZE)
+
+ Note 1: The first FETCH may result in the server's sending a huge
+ volume of data. A smart disconnected client should use message
+ ranges (see also Section 3.2.1.2 of [RFC2683]), so that the user is
+ able to execute a different operation between fetching information
+ for a group of new messages.
+
+ Example 7:
+
+ Knowing the new UIDNEXT returned by the server on SELECT or EXAMINE
+ (<uidnext>), the client can split the UID range
+ <lastseenuid+1>:<uidnext> into groups, e.g., 100 messages. After
+ that, the client can issue:
+
+ C: A011 UID fetch <lastseenuid+1>:<lastseenuid+100>
+ (FLAGS BODYSTRUCTURE INTERNALDATE RFC822.SIZE)
+ ...
+ C: A012 UID fetch <lastseenuid+101>:<lastseenuid+200>
+ (FLAGS BODYSTRUCTURE INTERNALDATE RFC822.SIZE)
+ ...
+ ...
+ C: A0FF UID fetch <lastseenuid+901>:<uidnext>
+ (FLAGS BODYSTRUCTURE INTERNALDATE RFC822.SIZE)
+
+ Note that unless a SEARCH command is issued, it is impossible to
+ determine how many messages will fall into a subrange, as UIDs are
+ not necessarily contiguous.
+
+ Note 2: The client SHOULD ignore any unsolicited EXPUNGE responses
+ received during the first FETCH command. EXPUNGE responses contain
+ message numbers that are useless to a client that doesn't have the
+ message-number-to-UID translation table.
+
+
+
+
+Melnikov Informational [Page 19]
+
+RFC 4549 Synch Ops for Disconnected IMAP4 Clients June 2006
+
+
+ The second FETCH command will result in zero or more untagged fetch
+ responses. Each response will have a corresponding UID FETCH data
+ item. All messages that didn't have a matching untagged FETCH
+ response MUST be removed from the local cache.
+
+ For example, if the <lastseenuid> had a value 15000 and the local
+ cache contained 3 messages with the UIDs 12, 777, and 14999,
+ respectively, then after receiving the following responses from the
+ server, the client must remove the message with UID 14999 from its
+ local cache.
+
+ S: * 1 FETCH (UID 12 FLAGS (\Seen))
+ S: * 2 FETCH (UID 777 FLAGS (\Answered \Deleted))
+
+ Note 3: If the client is not interested in flag changes (i.e., the
+ client only wants to know which old messages are still on the
+ server), the second FETCH command can be substituted with:
+
+ tag2 UID SEARCH UID 1:<lastseenuid>
+
+ This command will generate less traffic. However, an implementor
+ should be aware that in order to build the mapping table from message
+ numbers to UIDs, the output of the SEARCH command MUST be sorted
+ first, because there is no requirement for a server to return UIDs in
+ SEARCH response in any particular order.
+
+4.3.2. Searching for "Interesting" Messages.
+
+ This step is performed entirely on the client (from the information
+ received in the step described in 4.3.1), entirely on the server, or
+ on some combination of both. The decision on what is an
+ "interesting" message is up to the client software and the human.
+ One easy criterion that should probably be implemented in any client
+ is whether the message is "too big" for automatic retrieval, where
+ "too big" is a parameter defined in the client's configuration.
+
+ Another commonly used criterion is the age of a message. For
+ example, the client may choose to download only messages received in
+ the last week (in this case, <date> would be today's date minus 7
+ days):
+
+ tag3 UID SEARCH UID <uidset> SINCE <date>
+
+ Keep in mind that a date search disregards time and time zone. The
+ client can avoid doing this search if it specified INTERNALDATE in
+ <descriptors> on the step described in 4.3.1. If the client did, it
+ can perform the local search on its message cache.
+
+
+
+
+Melnikov Informational [Page 20]
+
+RFC 4549 Synch Ops for Disconnected IMAP4 Clients June 2006
+
+
+ At this step, the client also decides what kind of information about
+ a particular message to fetch from the server. In particular, even
+ for a message that is considered "too big", the client MAY choose to
+ fetch some part(s) of it. For example, if the message is a
+ multipart/mixed containing a text part and a MPEG attachment, there
+ is no reason for the client not to fetch the text part. The decision
+ of which part should or should not be fetched can be based on the
+ information received in the BODYSTRUCTURE FETCH response data item
+ (i.e., if BODYSTRUCTURE was included in <descriptors> on the step
+ described in 4.3.1).
+
+4.3.3. Populating Cache with "Interesting" Messages.
+
+ Once the client has found out which messages are "interesting", it
+ can start issuing appropriate FETCH commands for "interesting"
+ messages or parts thereof.
+
+ Note that fetching a message into the disconnected client's local
+ cache does NOT imply that the human has (or even will) read the
+ message. Thus, the synchronization program for a disconnected client
+ should always be careful to use the .PEEK variants of the FETCH data
+ items that implicitly set the \Seen flag.
+
+ Once the last descriptor has arrived and the last FETCH command has
+ been issued, the client simply needs to process the incoming fetch
+ items and use them to update the local message cache.
+
+ In order to avoid deadlock problems, the client must give processing
+ of received messages priority over issuing new FETCH commands during
+ this synchronization process. This may necessitate temporary local
+ queuing of FETCH requests that cannot be issued without causing a
+ deadlock. In order to achieve the best use of the "expensive"
+ network connection, the client will almost certainly need to pay
+ careful attention to any flow-control information that it can obtain
+ from the underlying transport connection (usually a TCP connection).
+
+ Note: The requirement stated in the previous paragraph might result
+ in an unpleasant user experience, if followed blindly. For example,
+ the user might be unwilling to wait for the client to finish
+ synchronization before starting to process the user's requests. A
+ smart disconnected client should allow the user to perform requested
+ operations in between IMAP commands that are part of the
+ synchronization process. See also Note 1 in Section 4.3.1.
+
+
+
+
+
+
+
+
+Melnikov Informational [Page 21]
+
+RFC 4549 Synch Ops for Disconnected IMAP4 Clients June 2006
+
+
+ Example 8:
+
+ After fetching a message BODYSTRUCTURE, the client discovers a
+ complex MIME message. Then, it decides to fetch MIME headers of the
+ nested MIME messages and some body parts.
+
+ C: A011 UID fetch 11 (BODYSTRUCTURE)
+ S: ...
+ C: A012 UID fetch 11 (BODY[HEADER] BODY[1.MIME] BODY[1.1.MIME]
+ BODY[1.2.MIME] BODY[2.MIME] BODY[3.MIME] BODY[4.MIME]
+ BODY[5.MIME] BODY[6.MIME] BODY[7.MIME] BODY[8.MIME] BODY[9.MIME]
+ BODY[10.MIME] BODY[11.MIME] BODY[12.MIME] BODY[13.MIME]
+ BODY[14.MIME] BODY[15.MIME] BODY[16.MIME] BODY[17.MIME]
+ BODY[18.MIME] BODY[19.MIME] BODY[20.MIME] BODY[21.MIME])
+ S: ...
+ C: A013 UID fetch 11 (BODY[1.1] BODY[1.2])
+ S: ...
+ C: A014 UID fetch 11 (BODY[3] BODY[4] BODY[5] BODY[6] BODY[7] BODY[8]
+ BODY[9] BODY[10] BODY[11] BODY[13] BODY[14] BODY[15] BODY[16]
+ BODY[21])
+ S: ...
+
+4.3.4. User-Initiated Synchronization
+
+ After the client has finished the main synchronization process as
+ described in Sections 4.3.1-4.3.3, the user may optionally request
+ additional synchronization steps while the client is still online.
+ This is not any different from the process described in Sections
+ 4.3.2 and 4.3.3.
+
+ Typical examples are:
+
+ 1) fetch all messages selected in UI.
+ 2) fetch all messages marked as \Flagged on the server.
+
+4.4. Special Case: Descriptor-Only Synchronization
+
+ For some mailboxes, fetching the descriptors might be the entire
+ synchronization step. Practical experience with IMAP has shown that
+ a certain class of mailboxes (e.g., "archival" mailboxes) are used
+ primarily for long-term storage of important messages that the human
+ wants to have instantly available on demand but does not want
+ cluttering up the disconnected client's cache at any other time.
+ Messages in this kind of mailbox would be fetched exclusively by
+ explicit actions queued by the local MUA. Thus, the only
+ synchronization desirable on this kind of mailbox is fetching enough
+ descriptor information for the user to be able to identify messages
+ for subsequent download.
+
+
+
+Melnikov Informational [Page 22]
+
+RFC 4549 Synch Ops for Disconnected IMAP4 Clients June 2006
+
+
+ Special mailboxes that receive messages from a high volume, low
+ priority mailing list might also be in this category, at least when
+ the human is in a hurry.
+
+4.5. Special Case: Fast New-Only Synchronization
+
+ In some cases, the human might be in such a hurry that he or she
+ doesn't care about changes to old messages, just about new messages.
+ In this case, the client can skip the UID FETCH command that obtains
+ the flags and UIDs for old messages (1:<lastseenuid>).
+
+4.6. Special Case: Blind FETCH
+
+ In some cases, the human may know (for whatever reason) that he or
+ she always wants to fetch any new messages in a particular mailbox,
+ unconditionally. In this case, the client can just fetch the
+ messages themselves, rather than just the descriptors, by using a
+ command like:
+
+ tag1 UID FETCH <lastseenuid+1>:* (FLAGS BODY.PEEK[])
+
+ Note that this example ignores the fact that the messages can be
+ arbitrary long. The disconnected client MUST always check for
+ message size before downloading, unless explicitly told otherwise. A
+ well-behaved client should instead use something like the following:
+
+ 1) Issue "tag1 UID FETCH <lastseenuid+1>:* (FLAGS RFC822.SIZE)".
+
+ 2) From the message sizes returned in step 1, construct UID set
+ <required_messages>.
+
+ 3) Issue "tag2 UID FETCH <required_messages> (BODY.PEEK[])".
+
+ or
+
+ 1) Issue "tag1 UID FETCH <lastseenuid+1>:* (FLAGS)".
+
+ 2) Construct UID set <old_uids> from the responses of step 1.
+
+ 3) Issue "tag2 SEARCH UID <old_uids> SMALLER <message_limit>".
+ Construct UID set <required_messages> from the result of the
+ SEARCH command.
+
+ 4) Issue "tag3 UID FETCH <required_messages> (BODY.PEEK[])".
+
+
+
+
+
+
+
+Melnikov Informational [Page 23]
+
+RFC 4549 Synch Ops for Disconnected IMAP4 Clients June 2006
+
+
+ or
+
+ 1) Issue "tag1 UID FETCH <lastseenuid+1>:* (FLAGS
+ BODY.PEEK[]<0.<length>>)", where <length> should be replaced with
+ the maximal message size the client is willing to download.
+
+ Note: In response to such a command, the server will only return
+ partial data if the message is longer than <length>. It will
+ return the full message data for any message whose size is smaller
+ than or equal to <length>. In the former case, the client will
+ not be able to extract the full MIME structure of the message from
+ the truncated data, so the client should include BODYSTRUCTURE in
+ the UID FETCH command as well.
+
+5. Implementation Considerations
+
+ Below are listed some common implementation pitfalls that should be
+ considered when implementing a disconnected client.
+
+ 1) Implementing fake UIDs on the client.
+
+ A message scheduled to be uploaded has no UID, as UIDs are
+ selected by the server. The client may implement fake UIDs
+ internally in order to reference not-yet-uploaded messages in
+ further operations. (For example, a message could be scheduled to
+ be uploaded, but subsequently marked as deleted or copied to
+ another mailbox). Here, the client MUST NOT under any
+ circumstances send these fake UIDs to the server. Also, client
+ implementers should be reminded that according to [IMAP4] a UID is
+ a 32-bit unsigned integer excluding 0. So, both 4294967295 and
+ 2147483648 are valid UIDs, and 0 and -1 are both invalid. Some
+ disconnected mail clients have been known to send negative numbers
+ (e.g., "-1") as message UIDs to servers during synchronization.
+
+ Situation 1: The user starts composing a new message, edits it,
+ saves it, continues to edit it, and saves it again.
+
+ A disconnected client may record in its replay log (log of
+ operations to be replayed on the server during synchronization)
+ the sequence of operations as shown below. For the purpose of
+ this situation, we assume that all draft messages are stored in
+ the mailbox called Drafts on an IMAP server. We will also use the
+ following conventions: <old_uid> is the UID of the intermediate
+ version of the draft when it was saved for the first time. This
+ is a fake UID generated on the client. <new_uid> is the UID of
+ the final version of the draft. This is another fake UID
+ generated on the client.
+
+
+
+
+Melnikov Informational [Page 24]
+
+RFC 4549 Synch Ops for Disconnected IMAP4 Clients June 2006
+
+
+ 1) APPEND Drafts (\Seen $MDNSent \Drafts) {<nnn>}
+ ...first version of the message follows...
+
+ 2) APPEND Drafts (\Seen $MDNSent \Drafts) {<mmm>}
+ ...final version of the message follows...
+
+ 3) STORE <old_uid> +FLAGS (\Deleted)
+
+ Step 1 corresponds to the first attempt to save the draft message,
+ step 2 corresponds to the second attempt to save the draft
+ message, and step 3 deletes the first version of the draft message
+ saved in step 1.
+
+ A naive disconnected client may send the command in step 3 without
+ replacing the fake client generated <old_uid> with the value
+ returned by the server in step 1. A server will probably reject
+ this command, which will make the client believe that the
+ synchronization sequence has failed.
+
+ 2) Section 5.1 discusses common implementation errors related to
+ error recovery during playback.
+
+ 3) Don't assume that the disconnected client is the only client used
+ by the user.
+
+ Situation 2: Some clients may use the \Deleted flag as an
+ indicator that the message should not appear in the user's view.
+ Usage of the \Deleted flag for this purpose is not safe, as other
+ clients (e.g., online clients) might EXPUNGE the mailbox at any
+ time.
+
+ 4) Beware of data dependencies between synchronization operations.
+
+ It might be very tempting for a client writer to perform some
+ optimizations on the playback log. Such optimizations might
+ include removing redundant operations (for example, see
+ optimization 2 in Section 5.3), or their reordering.
+
+ It is not always safe to reorder or remove redundant operations
+ during synchronization because some operations may have
+ dependencies (as Situation 3 demonstrates). So, if in doubt,
+ don't do this.
+
+ Situation 3: The user copies a message out of a mailbox and then
+ deletes the mailbox.
+
+
+
+
+
+
+Melnikov Informational [Page 25]
+
+RFC 4549 Synch Ops for Disconnected IMAP4 Clients June 2006
+
+
+ C: A001 SELECT Old-Mail
+ S: ...
+ C: A002 UID COPY 111 ToDo
+ S: A002 OK [COPYUID 1022843345 111 94] Copy completed
+ ...
+ C: A015 CLOSE
+ S: A015 OK Completed
+ C: A016 DELETE Old-Mail
+ S: A016 OK Mailbox deletion completed successfully
+
+ If the client performs DELETE (tag A016) first and COPY (tag A002)
+ second, then the COPY fails. Also, the message that the user so
+ carefully copied into another mailbox has been lost.
+
+5.1. Error Recovery during Playback
+
+ Error recovery during synchronization is one of the trickiest parts
+ to get right. Below, we will discuss certain error conditions and
+ suggest possible choices for handling them.
+
+ 1) Lost connection to the server.
+
+ The client MUST remember the current position in the playback
+ (replay) log and replay it starting from the interrupted operation
+ (the last command issued by the client, but not acknowledged by
+ the server) the next time it successfully connects to the same
+ server. If the connection was lost while executing a non-
+ idempotent IMAP command (see the definition in Section 1), then
+ when the client is reconnected, it MUST make sure that the
+ interrupted command was indeed not executed. If it wasn't
+ executed, the client must restart playback from the interrupted
+ command, otherwise from the following command.
+
+ Upon reconnect, care must be taken in order to properly reapply
+ logical operations that are represented by multiple IMAP commands,
+ e.g., UID EXPUNGE emulation when UID EXPUNGE is not supported by
+ the server (see Section 4.2.4).
+
+ Once the client detects that the connection to the server was
+ lost, it MUST stop replaying its log. There are existing
+ disconnected clients that, to the great annoyance of users, pop up
+ an error dialog for each and every playback operation that fails.
+
+ 2) Copying/appending messages to a mailbox that doesn't exist. (The
+ server advertises this condition by sending the TRYCREATE response
+ code in the tagged NO response to the APPEND or COPY command.)
+
+
+
+
+
+Melnikov Informational [Page 26]
+
+RFC 4549 Synch Ops for Disconnected IMAP4 Clients June 2006
+
+
+ The user should be advised about the situation and be given one of
+ the following choices:
+
+ a) Try to recreate a mailbox.
+ b) Copy/upload messages to another mailbox.
+ c) Skip copy/upload.
+ d) Abort replay.
+
+ 3) Copying messages from a mailbox that doesn't exist, or renaming or
+ getting/changing ACLs [ACL] on a mailbox that doesn't exist:
+
+ a) Skip operation.
+ b) Abort replay.
+
+ 4) Deleting mailboxes or deleting/expunging messages that no longer
+ exist.
+
+ This is actually is not an error and should be ignored by the
+ client.
+
+ 5) Performing operations on messages that no longer exist.
+
+ a) Skip operation.
+ b) Abort replay.
+
+ In the case of changing flags on an expunged message, the client
+ should silently ignore the error.
+
+ Note 1: Several synchronization operations map to multiple IMAP
+ commands (for example, "move" described in 4.2.2). The client must
+ guarantee atomicity of each such multistep operation. For example,
+ when performing a "move" between two mailboxes on the same server, if
+ the server is unable to copy messages, the client MUST NOT attempt to
+ set the \Deleted flag on the messages being copied, let alone expunge
+ them. However, the client MAY consider that move operation to have
+ succeeded even if the server was unable to set the \Deleted flag on
+ copied messages.
+
+ Note 2: Many synchronization operations have data dependencies. A
+ failed operation must cause all dependent operations to fail as well.
+ The client should check this and MUST NOT try to perform all
+ dependent operations blindly (unless the user corrected the original
+ problem). For example, a message may be scheduled to be appended to
+ a mailbox on the server and later on the appended message may be
+ copied to another mailbox. If the APPEND operation fails, the client
+ must not attempt to COPY the failed message later on. (See also
+ Section 5, Situation 3).
+
+
+
+
+Melnikov Informational [Page 27]
+
+RFC 4549 Synch Ops for Disconnected IMAP4 Clients June 2006
+
+
+5.2. Quality of Implementation Issues
+
+ Below, some quality of implementation issues are listed for
+ disconnected clients. They will help to write a disconnected client
+ that works correctly, performs synchronization as quickly as possible
+ (and thus can make the user happier as well as save her some money),
+ and minimizes the server load:
+
+ 1) Don't lose information.
+
+ No matter how smart your client is in other areas, if it loses
+ information, users will get very upset.
+
+ 2) Don't do work unless explicitly asked. Be flexible. Ask all
+ questions BEFORE starting synchronization, if possible.
+
+ 3) Minimize traffic.
+
+ The client MUST NOT issue a command if the client already received
+ the required information from the server.
+
+ The client MUST make use of UIDPLUS extension if it is supported
+ by the server.
+
+ See also optimization 1 in Section 5.3.
+
+ 4) Minimize the number of round-trips.
+
+ Round-trips kill performance, especially on links with high
+ latency. Sections 4.2.2.5 and 5.2 give some advice on how to
+ minimize the number of round-trips.
+
+ See also optimization 1 in Section 5.3.
+
+5.3. Optimizations
+
+ Some useful optimizations are described in this section. A
+ disconnected client that supports the recommendations listed below
+ will give the user a more pleasant experience.
+
+ 1) The initial OK or PREAUTH responses may contain the CAPABILITY
+ response code as described in Section 7.1 of [IMAP4]. This
+ response code gives the same information as returned by the
+ CAPABILITY command*. A disconnected client that pays attention to
+ this response code can avoid sending CAPABILITY command and will
+ save a round-trip.
+
+
+
+
+
+Melnikov Informational [Page 28]
+
+RFC 4549 Synch Ops for Disconnected IMAP4 Clients June 2006
+
+
+ * Note: Some servers report in the CAPABILITY response code
+ extensions that are only relevant in unauthenticated state or in
+ all states. Such servers usually send another CAPABILITY
+ response code upon successful authentication using LOGIN or
+ AUTHENTICATE command (that negotiates no security layer; see
+ Section 6.2.2 of [IMAP4]). The CAPABILITY response code sent
+ upon successful LOGIN/AUTHENTICATE might be different from the
+ CAPABILITY response code in the initial OK response, as
+ extensions only relevant for unauthenticated state will not be
+ advertised, and some additional extensions available only in
+ authenticated and/or selected state will be.
+
+ Example 9:
+
+ S: * OK [CAPABILITY IMAP4REV1 LOGIN-REFERRALS STARTTLS
+ AUTH=DIGEST-MD5 AUTH=SRP] imap.example.com ready
+ C: 2 authenticate DIGEST-MD5
+ S: 2 OK [CAPABILITY IMAP4REV1 IDLE NAMESPACE MAILBOX-REFERRALS SCAN
+ SORT THREAD=REFERENCES THREAD=ORDEREDSUBJECT MULTIAPPEND]
+ User authenticated (no layer)
+
+ 2) An advanced disconnected client may choose to optimize its replay
+ log. For example, there might be some operations that are
+ redundant (the list is not complete):
+
+ a) an EXPUNGE followed by another EXPUNGE or CLOSE;
+ b) changing flags (other than the \Deleted flag) on a message that
+ gets immediately expunged;
+ c) opening and closing the same mailbox.
+
+ When optimizing, be careful about data dependencies between commands.
+ For example, if the client is wishing to optimize (see case b, above)
+
+ tag1 UID STORE <uid1> +FLAGS (\Deleted)
+ ...
+ tag2 UID STORE <uid1> +FLAGS (\Flagged)
+ ...
+ tag3 UID COPY <uid1> "Backup"
+ ...
+ tag4 UID EXPUNGE <uid1>
+
+ it can't remove the second UID STORE command because the message is
+ being copied before it gets expunged.
+
+ In general, it might be a good idea to keep mailboxes open during
+ synchronization (see case c above), if possible. This can be more
+ easily achieved in conjunction with optimization 3 described below.
+
+
+
+
+Melnikov Informational [Page 29]
+
+RFC 4549 Synch Ops for Disconnected IMAP4 Clients June 2006
+
+
+ 3) Perform some synchronization steps in parallel, if possible.
+
+ Several synchronization steps don't depend on each other and thus
+ can be performed in parallel. Because the server machine is
+ usually more powerful than the client machine and can perform some
+ operations in parallel, this may speed up the total time of
+ synchronization.
+
+ In order to achieve such parallelization, the client will have to
+ open more than one connection to the same server. Client writers
+ should not forget about non-trivial cost associated with
+ establishing a TCP connection and performing an authentication.
+ The disconnected client MUST NOT use one connection per mailbox.
+ In most cases, it is sufficient to have two connections. The
+ disconnected client SHOULD avoid selecting the same mailbox in
+ more than one connection; see Section 3.1.1 of [RFC2683] for more
+ details.
+
+ Any mailbox synchronization MUST start with checking the
+ UIDVALIDITY as described in Section 4.1 of this document. The
+ client MAY use STATUS command to check UID Validity of a non-
+ selected mailbox. This is preferable to opening many connections
+ to the same server to perform synchronization of multiple
+ mailboxes simultaneously. As described in Section 5.3.10 of
+ [IMAP4], this SHOULD NOT be used on the selected mailbox.
+
+6. IMAP Extensions That May Help
+
+ The following extensions can save traffic and/or the number of
+ round-trips:
+
+ 1) The use of [UIDPLUS] is discussed in Sections 4.1, 4.2.1, 4.2.2.1
+ and 4.2.4.
+
+ 2) The use of the MULTIAPPEND and LITERAL+ extensions for uploading
+ messages is discussed in Section 4.2.2.5.
+
+ 3) Use the CONDSTORE extension (see Section 6.1) for quick flag
+ resynchronization.
+
+6.1. CONDSTORE Extension
+
+ An advanced disconnected mail client should use the [CONDSTORE]
+ extension when it is supported by the server. The client must cache
+ the value from HIGHESTMODSEQ OK response code received on mailbox
+ opening and update it whenever the server sends MODSEQ FETCH data
+ items.
+
+
+
+
+Melnikov Informational [Page 30]
+
+RFC 4549 Synch Ops for Disconnected IMAP4 Clients June 2006
+
+
+ If the client receives NOMODSEQ OK untagged response instead of
+ HIGHESTMODSEQ, it MUST remove the last known HIGHESTMODSEQ value from
+ its cache and follow the more general instructions in Section 3.
+
+ When the client opens the mailbox for synchronization, it first
+ compares UIDVALIDITY as described in step d-1 in Section 3. If the
+ cached UIDVALIDITY value matches the one returned by the server, the
+ client MUST compare the cached value of HIGHESTMODSEQ with the one
+ returned by the server. If the cached HIGHESTMODSEQ value also
+ matches the one returned by the server, then the client MUST NOT
+ fetch flags for cached messages, as they hasn't changed. If the
+ value on the server is higher than the cached one, the client MAY use
+ "SEARCH MODSEQ <cached-value>" to find all messages with flags
+ changed since the last time the client was online and had the mailbox
+ opened. Alternatively, the client MAY use "FETCH 1:* (FLAGS)
+ (CHANGEDSINCE <cached-value>)". The latter operation combines
+ searching for changed messages and fetching new information.
+
+ In all cases, the client still needs to fetch information about new
+ messages (if requested by the user) as well as discover which
+ messages have been expunged.
+
+ Step d ("Server-to-client synchronization") in Section 4 in the
+ presence of the CONDSTORE extension is amended as follows:
+
+ d) "Server-to-client synchronization" - For each mailbox that
+ requires synchronization, do the following:
+
+ 1a) Check the mailbox UIDVALIDITY (see section 4.1 for more
+ details) with SELECT/EXAMINE/STATUS.
+
+ If the UIDVALIDITY value returned by the server differs, the
+ client MUST
+
+ * empty the local cache of that mailbox;
+ * "forget" the cached HIGHESTMODSEQ value for the mailbox;
+ * remove any pending "actions" that refer to UIDs in that
+ mailbox (note that this doesn't affect actions performed on
+ client-generated fake UIDs; see Section 5); and
+ * skip steps 1b and 2-II;
+
+ 1b) Check the mailbox HIGHESTMODSEQ. If the cached value is the
+ same as the one returned by the server, skip fetching message
+ flags on step 2-II, i.e., the client only has to find out
+ which messages got expunged.
+
+
+
+
+
+
+Melnikov Informational [Page 31]
+
+RFC 4549 Synch Ops for Disconnected IMAP4 Clients June 2006
+
+
+ 2) Fetch the current "descriptors".
+
+ I) Discover new messages.
+
+ II) Discover changes to old messages and flags for new messages
+ using
+ "FETCH 1:* (FLAGS) (CHANGEDSINCE <cached-value>)" or
+ "SEARCH MODSEQ <cached-value>".
+
+ Discover expunged messages; for example, using
+ "UID SEARCH 1:<lastseenuid>". (All messages not returned
+ in this command are expunged.)
+
+ 3) Fetch the bodies of any "interesting" messages that the client
+ doesn't already have.
+
+ Example 10:
+
+ The UIDVALIDITY value is the same, but the HIGHESTMODSEQ value
+ has changed on the server while the client was offline.
+
+ C: A142 SELECT INBOX
+ S: * 172 EXISTS
+ S: * 1 RECENT
+ S: * OK [UNSEEN 12] Message 12 is first unseen
+ S: * OK [UIDVALIDITY 3857529045] UIDs valid
+ S: * OK [UIDNEXT 201] Predicted next UID
+ S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
+ S: * OK [PERMANENTFLAGS (\Deleted \Seen \*)] Limited
+ S: * OK [HIGHESTMODSEQ 20010715194045007]
+ S: A142 OK [READ-WRITE] SELECT completed
+
+ After that, either:
+
+ C: A143 UID FETCH 1:* (FLAGS) (CHANGEDSINCE 20010715194032001)
+ S: * 2 FETCH (UID 6 MODSEQ (20010715205008000) FLAGS (\Deleted))
+ S: * 5 FETCH (UID 9 MODSEQ (20010715195517000) FLAGS ($NoJunk
+ $AutoJunk $MDNSent))
+ ...
+ S: A143 OK FETCH completed
+
+ or:
+
+
+
+
+
+
+
+
+
+Melnikov Informational [Page 32]
+
+RFC 4549 Synch Ops for Disconnected IMAP4 Clients June 2006
+
+
+ C: A143 UID SEARCH MODSEQ 20010715194032001 UID 1:20
+ S: * SEARCH 6 9 11 12 18 19 20 23 (MODSEQ 20010917162500)
+ S: A143 OK Search complete
+ C: A144 UID SEARCH 1:20
+ S: * SEARCH 6 9 ...
+ S: A144 OK FETCH completed
+
+7. Security Considerations
+
+ It is believed that this document does not raise any new security
+ concerns that are not already present in the base [IMAP4] protocol,
+ and these issues are discussed in [IMAP4]. Additional security
+ considerations may be found in different extensions mentioned in this
+ document; in particular, in [UIDPLUS], [LITERAL+], [CONDSTORE],
+ [MULTIAPPEND], and [UNSELECT].
+
+ Implementers are also reminded about the importance of thorough
+ testing.
+
+8. References
+
+8.1. Normative References
+
+ [KEYWORDS] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [IMAP4] Crispin, M., "INTERNET MESSAGE ACCESS PROTOCOL -
+ VERSION 4rev1", RFC 3501, March 2003.
+
+ [UIDPLUS] Crispin, M., "Internet Message Access Protocol (IMAP) -
+ UIDPLUS extension", RFC 4315, December 2005.
+
+ [LITERAL+] Myers, J., "IMAP4 non-synchronizing literals", RFC
+ 2088, January 1997.
+
+ [CONDSTORE] Melnikov, A. and S. Hole, "IMAP Extension for
+ Conditional STORE Operation or Quick Flag Changes
+ Resynchronization", RFC 4551, June 2006.
+
+ [MULTIAPPEND] Crispin, M., "Internet Message Access Protocol (IMAP) -
+ MULTIAPPEND Extension", RFC 3502, March 2003.
+
+ [UNSELECT] Melnikov, A., "Internet Message Access Protocol (IMAP)
+ UNSELECT command", RFC 3691, February 2004.
+
+ [RFC2683] Leiba, B., "IMAP4 Implementation Recommendations", RFC
+ 2683, September 1999.
+
+
+
+
+Melnikov Informational [Page 33]
+
+RFC 4549 Synch Ops for Disconnected IMAP4 Clients June 2006
+
+
+8.2. Informative References
+
+ [ACL] Melnikov, A., "IMAP4 Access Control List (ACL)
+ Extension", RFC 4314, December 2005.
+
+ [IMAP-MODEL] Crispin, M., "Distributed Electronic Mail Models in
+ IMAP4", RFC 1733, December 1994.
+
+9. Acknowledgements
+
+ This document is based on version 01 of the text written by Rob
+ Austein in November 1994.
+
+ The editor appreciates comments posted by Mark Crispin to the IMAP
+ mailing list and the comments/corrections/ideas received from Grant
+ Baillie, Cyrus Daboo, John G. Myers, Chris Newman, and Timo Sirainen.
+
+ The editor would also like to thank the developers of Netscape
+ Messenger and Mozilla mail clients for providing examples of
+ disconnected mail clients that served as a base for many
+ recommendations in this document.
+
+Editor's Address
+
+ Alexey Melnikov
+ Isode Limited
+ 5 Castle Business Village
+ 36 Station Road
+ Hampton, Middlesex
+ TW12 2BX
+ United Kingdom
+
+ Phone: +44 77 53759732
+ EMail: alexey.melnikov@isode.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Melnikov Informational [Page 34]
+
+RFC 4549 Synch Ops for Disconnected IMAP4 Clients June 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is provided by the IETF
+ Administrative Support Activity (IASA).
+
+
+
+
+
+
+
+Melnikov Informational [Page 35]
+
diff --git a/imap/docs/rfc/rfc4551.txt b/imap/docs/rfc/rfc4551.txt
new file mode 100644
index 00000000..894b5109
--- /dev/null
+++ b/imap/docs/rfc/rfc4551.txt
@@ -0,0 +1,1403 @@
+
+
+
+
+
+
+Network Working Group A. Melnikov
+Request for Comments: 4551 Isode Ltd.
+Updates: 3501 S. Hole
+Category: Standards Track ACI WorldWide/MessagingDirect
+ June 2006
+
+
+ IMAP Extension for Conditional STORE Operation
+ or Quick Flag Changes Resynchronization
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+Abstract
+
+ Often, multiple IMAP (RFC 3501) clients need to coordinate changes to
+ a common IMAP mailbox. Examples include different clients working on
+ behalf of the same user, and multiple users accessing shared
+ mailboxes. These clients need a mechanism to synchronize state
+ changes for messages within the mailbox. They must be able to
+ guarantee that only one client can change message state (e.g.,
+ message flags) at any time. An example of such an application is use
+ of an IMAP mailbox as a message queue with multiple dequeueing
+ clients.
+
+ The Conditional Store facility provides a protected update mechanism
+ for message state information that can detect and resolve conflicts
+ between multiple writing mail clients.
+
+ The Conditional Store facility also allows a client to quickly
+ resynchronize mailbox flag changes.
+
+ This document defines an extension to IMAP (RFC 3501).
+
+
+
+
+
+
+
+
+
+Melnikov & Hole Standards Track [Page 1]
+
+RFC 4551 IMAP Extension for Conditional STORE June 2006
+
+
+Table of Contents
+
+ 1. Introduction and Overview ................................. 3
+ 2. Conventions Used in This Document ......................... 5
+ 3. IMAP Protocol Changes ..................................... 6
+ 3.1. New OK untagged responses for SELECT and EXAMINE ......... 6
+ 3.1.1. HIGHESTMODSEQ response code ............................ 6
+ 3.1.2. NOMODSEQ response code ................................. 7
+ 3.2. STORE and UID STORE Commands ............................. 7
+ 3.3 FETCH and UID FETCH Commands ..............................13
+ 3.3.1. CHANGEDSINCE FETCH modifier ............................13
+ 3.3.2. MODSEQ message data item in FETCH Command ..............14
+ 3.4. MODSEQ search criterion in SEARCH ........................16
+ 3.5. Modified SEARCH untagged response ........................17
+ 3.6. HIGHESTMODSEQ status data items ..........................17
+ 3.7. CONDSTORE parameter to SELECT and EXAMINE ................18
+ 3.8. Additional quality of implementation issues ..............18
+ 4. Formal Syntax .............................................19
+ 5. Server implementation considerations ......................21
+ 6. Security Considerations ...................................22
+ 7. IANA Considerations .......................................22
+ 8. References ................................................23
+ 8.1. Normative References .....................................23
+ 8.2. Informative References ...................................23
+ 9. Acknowledgements ..........................................23
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Melnikov & Hole Standards Track [Page 2]
+
+RFC 4551 IMAP Extension for Conditional STORE June 2006
+
+
+1. Introduction and Overview
+
+ The Conditional STORE extension is present in any IMAP4
+ implementation that returns "CONDSTORE" as one of the supported
+ capabilities in the CAPABILITY command response.
+
+ An IMAP server that supports this extension MUST associate a positive
+ unsigned 64-bit value called a modification sequence (mod-sequence)
+ with every IMAP message. This is an opaque value updated by the
+ server whenever a metadata item is modified. The server MUST
+ guarantee that each STORE command performed on the same mailbox
+ (including simultaneous stores to different metadata items from
+ different connections) will get a different mod-sequence value.
+ Also, for any two successful STORE operations performed in the same
+ session on the same mailbox, the mod-sequence of the second completed
+ operation MUST be greater than the mod-sequence of the first
+ completed. Note that the latter rule disallows the use of the system
+ clock as a mod-sequence, because if system time changes (e.g., an NTP
+ [NTP] client adjusting the time), the next generated value might be
+ less than the previous one.
+
+ Mod-sequences allow a client that supports the CONDSTORE extension to
+ determine if a message metadata has changed since some known moment.
+ Whenever the state of a flag changes (i.e., the flag is added where
+ previously it wasn't set, or the flag is removed and before it was
+ set) the value of the modification sequence for the message MUST be
+ updated. Adding the flag when it is already present or removing when
+ it is not present SHOULD NOT change the mod-sequence.
+
+ When a message is appended to a mailbox (via the IMAP APPEND command,
+ COPY to the mailbox, or using an external mechanism) the server
+ generates a new modification sequence that is higher than the highest
+ modification sequence of all messages in the mailbox and assigns it
+ to the appended message.
+
+ The server MAY store separate (per-message) modification sequence
+ values for different metadata items. If the server does so, per-
+ message mod-sequence is the highest mod-sequence of all metadata
+ items for the specified message.
+
+ The server that supports this extension is not required to be able to
+ store mod-sequences for every available mailbox. Section 3.1.2
+ describes how the server may act if a particular mailbox doesn't
+ support the persistent storage of mod-sequences.
+
+
+
+
+
+
+
+Melnikov & Hole Standards Track [Page 3]
+
+RFC 4551 IMAP Extension for Conditional STORE June 2006
+
+
+ This extension makes the following changes to the IMAP4 protocol:
+
+ a) adds UNCHANGEDSINCE STORE modifier.
+
+ b) adds the MODIFIED response code which should be used with an OK
+ response to the STORE command. (It can also be used in a NO
+ response.)
+
+ c) adds a new MODSEQ message data item for use with the FETCH
+ command.
+
+ d) adds CHANGEDSINCE FETCH modifier.
+
+ e) adds a new MODSEQ search criterion.
+
+ f) extends the syntax of untagged SEARCH responses to include
+ mod-sequence.
+
+ g) adds new OK untagged responses for the SELECT and EXAMINE
+ commands.
+
+ h) defines an additional parameter to SELECT/EXAMINE commands.
+
+ i) adds the HIGHESTMODSEQ status data item to the STATUS command.
+
+ A client supporting CONDSTORE extension indicates its willingness to
+ receive mod-sequence updates in all untagged FETCH responses by
+ issuing:
+
+ - a SELECT or EXAMINE command with the CONDSTORE parameter,
+ - a STATUS (HIGHESTMODSEQ) command,
+ - a FETCH or SEARCH command that includes the MODSEQ message data
+ item,
+ - a FETCH command with the CHANGEDSINCE modifier, or
+ - a STORE command with the UNCHANGEDSINCE modifier.
+
+ The server MUST include mod-sequence data in all subsequent untagged
+ FETCH responses (until the connection is closed), whether they were
+ caused by a regular STORE, a STORE with UNCHANGEDSINCE modifier, or
+ an external agent.
+
+ This document uses the term "CONDSTORE-aware client" to refer to a
+ client that announces its willingness to receive mod-sequence updates
+ as described above. The term "CONDSTORE enabling command" will refer
+ any of the commands listed above. A future extension to this
+ document may extend the list of CONDSTORE enabling commands. A first
+ CONDSTORE enabling command executed in the session MUST cause the
+
+
+
+
+Melnikov & Hole Standards Track [Page 4]
+
+RFC 4551 IMAP Extension for Conditional STORE June 2006
+
+
+ server to return HIGHESTMODSEQ (Section 3.1.1) unless the server has
+ sent NOMODSEQ (Section 3.1.2) response code when the currently
+ selected mailbox was selected.
+
+ The rest of this document describes the protocol changes more
+ rigorously.
+
+2. Conventions Used in This Document
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in RFC 2119 [KEYWORDS].
+
+ In examples, lines beginning with "S:" are sent by the IMAP server,
+ and lines beginning with "C:" are sent by the client. Line breaks
+ may appear in example commands solely for editorial clarity; when
+ present in the actual message, they are represented by "CRLF".
+
+ Formal syntax is defined using ABNF [ABNF].
+
+ The term "metadata" or "metadata item" is used throughout this
+ document. It refers to any system or user-defined keyword. Future
+ documents may extend "metadata" to include other dynamic message
+ data.
+
+ Some IMAP mailboxes are private, accessible only to the owning user.
+ Other mailboxes are not, either because the owner has set an Access
+ Control List [ACL] that permits access by other users, or because it
+ is a shared mailbox. Let's call a metadata item "shared" for the
+ mailbox if any changes to the metadata items are persistent and
+ visible to all other users accessing the mailbox. Otherwise, the
+ metadata item is called "private". Note that private metadata items
+ are still visible to all sessions accessing the mailbox as the same
+ user. Also note that different mailboxes may have different metadata
+ items as shared.
+
+ See Section 1 for the definition of a "CONDSTORE-aware client" and a
+ "CONDSTORE enabling command".
+
+
+
+
+
+
+
+
+
+
+
+
+
+Melnikov & Hole Standards Track [Page 5]
+
+RFC 4551 IMAP Extension for Conditional STORE June 2006
+
+
+3. IMAP Protocol Changes
+
+3.1. New OK Untagged Responses for SELECT and EXAMINE
+
+ This document adds two new response codes, HIGHESTMODSEQ and
+ NOMODSEQ. One of those response codes MUST be returned in the OK
+ untagged response for a successful SELECT/EXAMINE command.
+
+ When opening a mailbox, the server must check if the mailbox supports
+ the persistent storage of mod-sequences. If the mailbox supports the
+ persistent storage of mod-sequences and the mailbox open operation
+ succeeds, the server MUST send the OK untagged response including
+ HIGHESTMODSEQ response code. If the persistent storage for the
+ mailbox is not supported, the server MUST send the OK untagged
+ response including NOMODSEQ response code instead.
+
+3.1.1. HIGHESTMODSEQ Response Code
+
+ This document adds a new response code that is returned in the OK
+ untagged response for the SELECT and EXAMINE commands. A server
+ supporting the persistent storage of mod-sequences for the mailbox
+ MUST send the OK untagged response including HIGHESTMODSEQ response
+ code with every successful SELECT or EXAMINE command:
+
+ OK [HIGHESTMODSEQ <mod-sequence-value>]
+
+ where <mod-sequence-value> is the highest mod-sequence value of
+ all messages in the mailbox. When the server changes UIDVALIDITY
+ for a mailbox, it doesn't have to keep the same HIGHESTMODSEQ for
+ the mailbox.
+
+ A disconnected client can use the value of HIGHESTMODSEQ to check if
+ it has to refetch metadata from the server. If the UIDVALIDITY value
+ has changed for the selected mailbox, the client MUST delete the
+ cached value of HIGHESTMODSEQ. If UIDVALIDITY for the mailbox is the
+ same, and if the HIGHESTMODSEQ value stored in the client's cache is
+ less than the value returned by the server, then some metadata items
+ on the server have changed since the last synchronization, and the
+ client needs to update its cache. The client MAY use SEARCH MODSEQ
+ (Section 3.4) to find out exactly which metadata items have changed.
+ Alternatively, the client MAY issue FETCH with the CHANGEDSINCE
+ modifier (Section 3.3.1) in order to fetch data for all messages that
+ have metadata items changed since some known modification sequence.
+
+ Example 1:
+
+ C: A142 SELECT INBOX
+ S: * 172 EXISTS
+
+
+
+Melnikov & Hole Standards Track [Page 6]
+
+RFC 4551 IMAP Extension for Conditional STORE June 2006
+
+
+ S: * 1 RECENT
+ S: * OK [UNSEEN 12] Message 12 is first unseen
+ S: * OK [UIDVALIDITY 3857529045] UIDs valid
+ S: * OK [UIDNEXT 4392] Predicted next UID
+ S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
+ S: * OK [PERMANENTFLAGS (\Deleted \Seen \*)] Limited
+ S: * OK [HIGHESTMODSEQ 715194045007]
+ S: A142 OK [READ-WRITE] SELECT completed
+
+3.1.2. NOMODSEQ Response Code
+
+ A server that doesn't support the persistent storage of mod-sequences
+ for the mailbox MUST send the OK untagged response including NOMODSEQ
+ response code with every successful SELECT or EXAMINE command. A
+ server that returned NOMODSEQ response code for a mailbox, which
+ subsequently receives one of the following commands while the mailbox
+ is selected:
+
+ - a FETCH command with the CHANGEDSINCE modifier,
+ - a FETCH or SEARCH command that includes the MODSEQ message data
+ item, or
+ - a STORE command with the UNCHANGEDSINCE modifier
+
+ MUST reject any such command with the tagged BAD response.
+
+ Example 2:
+
+ C: A142 SELECT INBOX
+ S: * 172 EXISTS
+ S: * 1 RECENT
+ S: * OK [UNSEEN 12] Message 12 is first unseen
+ S: * OK [UIDVALIDITY 3857529045] UIDs valid
+ S: * OK [UIDNEXT 4392] Predicted next UID
+ S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
+ S: * OK [PERMANENTFLAGS (\Deleted \Seen \*)] Limited
+ S: * OK [NOMODSEQ] Sorry, this mailbox format doesn't support
+ modsequences
+ S: A142 OK [READ-WRITE] SELECT completed
+
+3.2. STORE and UID STORE Commands
+
+ This document defines the following STORE modifier (see Section 2.5
+ of [IMAPABNF]):
+
+ UNCHANGEDSINCE <mod-sequence>
+
+ For each message specified in the message set, the server performs
+ the following. If the mod-sequence of any metadata item of the
+
+
+
+Melnikov & Hole Standards Track [Page 7]
+
+RFC 4551 IMAP Extension for Conditional STORE June 2006
+
+
+ message is equal or less than the specified UNCHANGEDSINCE value,
+ then the requested operation (as described by the message data
+ item) is performed. If the operation is successful, the server
+ MUST update the mod-sequence attribute of the message. An
+ untagged FETCH response MUST be sent, even if the .SILENT suffix
+ is specified, and the response MUST include the MODSEQ message
+ data item. This is required to update the client's cache with the
+ correct mod-sequence values. See Section 3.3.2 for more details.
+
+ However, if the mod-sequence of any metadata item of the message
+ is greater than the specified UNCHANGEDSINCE value, then the
+ requested operation MUST NOT be performed. In this case, the
+ mod-sequence attribute of the message is not updated, and the
+ message number (or unique identifier in the case of the UID STORE
+ command) is added to the list of messages that failed the
+ UNCHANGESINCE test.
+
+ When the server finished performing the operation on all the
+ messages in the message set, it checks for a non-empty list of
+ messages that failed the UNCHANGESINCE test. If this list is
+ non-empty, the server MUST return in the tagged response a
+ MODIFIED response code. The MODIFIED response code includes the
+ message set (for STORE) or set of UIDs (for UID STORE) of all
+ messages that failed the UNCHANGESINCE test.
+
+ Example 3:
+
+ All messages pass the UNCHANGESINCE test.
+
+ C: a103 UID STORE 6,4,8 (UNCHANGEDSINCE 12121230045)
+ +FLAGS.SILENT (\Deleted)
+ S: * 1 FETCH (UID 4 MODSEQ (12121231000))
+ S: * 2 FETCH (UID 6 MODSEQ (12121230852))
+ S: * 4 FETCH (UID 8 MODSEQ (12121130956))
+ S: a103 OK Conditional Store completed
+
+ Example 4:
+
+ C: a104 STORE * (UNCHANGEDSINCE 12121230045) +FLAGS.SILENT
+ (\Deleted $Processed)
+ S: * 50 FETCH (MODSEQ (12111230047))
+ S: a104 OK Store (conditional) completed
+
+ Example 5:
+
+ C: c101 STORE 1 (UNCHANGEDSINCE 12121230045) -FLAGS.SILENT
+ (\Deleted)
+ S: * OK [HIGHESTMODSEQ 12111230047]
+
+
+
+Melnikov & Hole Standards Track [Page 8]
+
+RFC 4551 IMAP Extension for Conditional STORE June 2006
+
+
+ S: * 50 FETCH (MODSEQ (12111230048))
+ S: c101 OK Store (conditional) completed
+
+ HIGHESTMODSEQ response code was sent by the server presumably
+ because this was the first CONDSTORE enabling command.
+
+ Example 6:
+
+ In spite of the failure of the conditional STORE operation for
+ message 7, the server continues to process the conditional STORE
+ in order to find all messages that fail the test.
+
+ C: d105 STORE 7,5,9 (UNCHANGEDSINCE 320162338)
+ +FLAGS.SILENT (\Deleted)
+ S: * 5 FETCH (MODSEQ (320162350))
+ S: d105 OK [MODIFIED 7,9] Conditional STORE failed
+
+ Example 7:
+
+ Same as above, but the server follows the SHOULD recommendation in
+ Section 6.4.6 of [IMAP4].
+
+ C: d105 STORE 7,5,9 (UNCHANGEDSINCE 320162338)
+ +FLAGS.SILENT (\Deleted)
+ S: * 7 FETCH (MODSEQ (320162342) FLAGS (\Seen \Deleted))
+ S: * 5 FETCH (MODSEQ (320162350))
+ S: * 9 FETCH (MODSEQ (320162349) FLAGS (\Answered))
+ S: d105 OK [MODIFIED 7,9] Conditional STORE failed
+
+ Use of UNCHANGEDSINCE with a modification sequence of 0 always
+ fails if the metadata item exists. A system flag MUST always be
+ considered existent, whether it was set or not.
+
+ Example 8:
+
+ C: a102 STORE 12 (UNCHANGEDSINCE 0)
+ +FLAGS.SILENT ($MDNSent)
+ S: a102 OK [MODIFIED 12] Conditional STORE failed
+
+ The client has tested the presence of the $MDNSent user-defined
+ keyword.
+
+ Note: A client trying to make an atomic change to the state of a
+ particular metadata item (or a set of metadata items) should be
+ prepared to deal with the case when the server returns the MODIFIED
+ response code if the state of the metadata item being watched hasn't
+ changed (but the state of some other metadata item has). This is
+ necessary, because some servers don't store separate mod-sequences
+
+
+
+Melnikov & Hole Standards Track [Page 9]
+
+RFC 4551 IMAP Extension for Conditional STORE June 2006
+
+
+ for different metadata items. However, a server implementation
+ SHOULD avoid generating spurious MODIFIED responses for +FLAGS/-FLAGS
+ STORE operations, even when the server stores a single mod-sequence
+ per message. Section 5 describes how this can be achieved.
+
+ Unless the server has included an unsolicited FETCH to update
+ client's knowledge about messages that have failed the UNCHANGEDSINCE
+ test, upon receipt of the MODIFIED response code, the client SHOULD
+ try to figure out if the required metadata items have indeed changed
+ by issuing FETCH or NOOP command. It is RECOMMENDED that the server
+ avoids the need for the client to do that by sending an unsolicited
+ FETCH response (Examples 9 and 10).
+
+ If the required metadata items haven't changed, the client SHOULD
+ retry the command with the new mod-sequence. The client SHOULD allow
+ for a configurable but reasonable number of retries (at least 2).
+
+ Example 9:
+
+ In the example below, the server returns the MODIFIED response
+ code without sending information describing why the STORE
+ UNCHANGEDSINCE operation has failed.
+
+ C: a106 STORE 100:150 (UNCHANGEDSINCE 212030000000)
+ +FLAGS.SILENT ($Processed)
+ S: * 100 FETCH (MODSEQ (303181230852))
+ S: * 102 FETCH (MODSEQ (303181230852))
+ ...
+ S: * 150 FETCH (MODSEQ (303181230852))
+ S: a106 OK [MODIFIED 101] Conditional STORE failed
+
+ The flag $Processed was set on the message 101...
+
+ C: a107 NOOP
+ S: * 101 FETCH (MODSEQ (303011130956) FLAGS ($Processed))
+ S: a107 OK
+
+ Or the flag hasn't changed, but another has (note that this server
+ behaviour is discouraged. Server implementers should also see
+ Section 5)...
+
+ C: b107 NOOP
+ S: * 101 FETCH (MODSEQ (303011130956) FLAGS (\Deleted \Answered))
+ S: b107 OK
+
+ ...and the client retries the operation for the message 101 with
+ the updated UNCHANGEDSINCE value
+
+
+
+
+Melnikov & Hole Standards Track [Page 10]
+
+RFC 4551 IMAP Extension for Conditional STORE June 2006
+
+
+ C: b108 STORE 101 (UNCHANGEDSINCE 303011130956)
+ +FLAGS.SILENT ($Processed)
+ S: * 101 FETCH (MODSEQ (303181230852))
+ S: b108 OK Conditional Store completed
+
+ Example 10:
+
+ Same as above, but the server avoids the need for the client to
+ poll for changes.
+
+ The flag $Processed was set on the message 101 by another
+ client...
+
+ C: a106 STORE 100:150 (UNCHANGEDSINCE 212030000000)
+ +FLAGS.SILENT ($Processed)
+ S: * 100 FETCH (MODSEQ (303181230852))
+ S: * 101 FETCH (MODSEQ (303011130956) FLAGS ($Processed))
+ S: * 102 FETCH (MODSEQ (303181230852))
+ ...
+ S: * 150 FETCH (MODSEQ (303181230852))
+ S: a106 OK [MODIFIED 101] Conditional STORE failed
+
+ Or the flag hasn't changed, but another has (note that this server
+ behaviour is discouraged. Server implementers should also see
+ Section 5)...
+
+ C: a106 STORE 100:150 (UNCHANGEDSINCE 212030000000)
+ +FLAGS.SILENT ($Processed)
+ S: * 100 FETCH (MODSEQ (303181230852))
+ S: * 101 FETCH (MODSEQ (303011130956) FLAGS (\Deleted \Answered))
+ S: * 102 FETCH (MODSEQ (303181230852))
+ ...
+ S: * 150 FETCH (MODSEQ (303181230852))
+ S: a106 OK [MODIFIED 101] Conditional STORE failed
+
+ ...and the client retries the operation for the message 101 with
+ the updated UNCHANGEDSINCE value
+
+ C: b108 STORE 101 (UNCHANGEDSINCE 303011130956)
+ +FLAGS.SILENT ($Processed)
+ S: * 101 FETCH (MODSEQ (303181230852))
+ S: b108 OK Conditional Store completed
+
+ Or the flag hasn't changed, but another has (nice server
+ behaviour. Server implementers should also see Section 5)...
+
+ C: a106 STORE 100:150 (UNCHANGEDSINCE 212030000000)
+ +FLAGS.SILENT ($Processed)
+
+
+
+Melnikov & Hole Standards Track [Page 11]
+
+RFC 4551 IMAP Extension for Conditional STORE June 2006
+
+
+ S: * 100 FETCH (MODSEQ (303181230852))
+ S: * 101 FETCH (MODSEQ (303011130956) FLAGS ($Processed \Deleted
+ \Answered))
+ S: * 102 FETCH (MODSEQ (303181230852))
+ ...
+ S: * 150 FETCH (MODSEQ (303181230852))
+ S: a106 OK Conditional STORE completed
+
+ Example 11:
+
+ The following example is based on the example from the Section
+ 4.2.3 of [RFC-2180] and demonstrates that the MODIFIED response
+ code may be also returned in the tagged NO response.
+
+ Client tries to conditionally STORE flags on a mixture of expunged
+ and non-expunged messages; one message fails the UNCHANGEDSINCE
+ test.
+
+ C: B001 STORE 1:7 (UNCHANGEDSINCE 320172338) +FLAGS (\SEEN)
+ S: * 1 FETCH (MODSEQ (320172342) FLAGS (\SEEN))
+ S: * 3 FETCH (MODSEQ (320172342) FLAGS (\SEEN))
+ S: B001 NO [MODIFIED 2] Some of the messages no longer exist.
+
+ C: B002 NOOP
+ S: * 4 EXPUNGE
+ S: * 4 EXPUNGE
+ S: * 4 EXPUNGE
+ S: * 4 EXPUNGE
+ S: * 2 FETCH (MODSEQ (320172340) FLAGS (\Deleted \Answered))
+ S: B002 OK NOOP Completed.
+
+ By receiving FETCH responses for messages 1 and 3, and EXPUNGE
+ responses that indicate that messages 4 through 7 have been
+ expunged, the client retries the operation only for the message 2.
+ The updated UNCHANGEDSINCE value is used.
+
+ C: b003 STORE 2 (UNCHANGEDSINCE 320172340) +FLAGS (\Seen)
+ S: * 2 FETCH (MODSEQ (320180050))
+ S: b003 OK Conditional Store completed
+
+ Note: If a message is specified multiple times in the message set,
+ and the server doesn't internally eliminate duplicates from the
+ message set, it MUST NOT fail the conditional STORE operation for the
+ second (or subsequent) occurrence of the message if the operation
+ completed successfully for the first occurrence. For example, if the
+ client specifies:
+
+
+
+
+
+Melnikov & Hole Standards Track [Page 12]
+
+RFC 4551 IMAP Extension for Conditional STORE June 2006
+
+
+ e105 STORE 7,3:9 (UNCHANGEDSINCE 12121230045)
+ +FLAGS.SILENT (\Deleted)
+
+ the server must not fail the operation for message 7 as part of
+ processing "3:9" if it succeeded when message 7 was processed the
+ first time.
+
+ Once the client specified the UNCHANGEDSINCE modifier in a STORE
+ command, the server MUST include the MODSEQ fetch response data items
+ in all subsequent unsolicited FETCH responses.
+
+ This document also changes the behaviour of the server when it has
+ performed a STORE or UID STORE command and the UNCHANGEDSINCE
+ modifier is not specified. If the operation is successful for a
+ message, the server MUST update the mod-sequence attribute of the
+ message. The server is REQUIRED to include the mod-sequence value
+ whenever it decides to send the unsolicited FETCH response to all
+ CONDSTORE-aware clients that have opened the mailbox containing the
+ message.
+
+ Server implementers should also see Section 3.8 for additional
+ quality of implementation issues related to the STORE command.
+
+3.3. FETCH and UID FETCH Commands
+
+3.3.1. CHANGEDSINCE FETCH Modifier
+
+ This document defines the following FETCH modifier (see Section 2.4
+ of [IMAPABNF]):
+
+ CHANGEDSINCE <mod-sequence>
+
+ CHANGEDSINCE FETCH modifier allows to create a further subset of
+ the list of messages described by sequence set. The information
+ described by message data items is only returned for messages that
+ have mod-sequence bigger than <mod-sequence>.
+
+ When CHANGEDSINCE FETCH modifier is specified, it implicitly adds
+ MODSEQ FETCH message data item (Section 3.3.2).
+
+ Example 12:
+
+ C: s100 UID FETCH 1:* (FLAGS) (CHANGEDSINCE 12345)
+ S: * 1 FETCH (UID 4 MODSEQ (65402) FLAGS (\Seen))
+ S: * 2 FETCH (UID 6 MODSEQ (75403) FLAGS (\Deleted))
+ S: * 4 FETCH (UID 8 MODSEQ (29738) FLAGS ($NoJunk $AutoJunk
+ $MDNSent))
+ S: s100 OK FETCH completed
+
+
+
+Melnikov & Hole Standards Track [Page 13]
+
+RFC 4551 IMAP Extension for Conditional STORE June 2006
+
+
+3.3.2. MODSEQ Message Data Item in FETCH Command
+
+ This extension adds a MODSEQ message data item to the FETCH command.
+ The MODSEQ message data item allows clients to retrieve mod-sequence
+ values for a range of messages in the currently selected mailbox.
+
+ Once the client specified the MODSEQ message data item in a FETCH
+ request, the server MUST include the MODSEQ fetch response data items
+ in all subsequent unsolicited FETCH responses.
+
+ Syntax: MODSEQ
+
+ The MODSEQ message data item causes the server to return MODSEQ
+ fetch response data items.
+
+ Syntax: MODSEQ ( <permsg-modsequence> )
+
+ MODSEQ response data items contain per-message mod-sequences.
+
+ The MODSEQ response data item is returned if the client issued
+ FETCH with MODSEQ message data item. It also allows the server to
+ notify the client about mod-sequence changes caused by conditional
+ STOREs (Section 3.2) and/or changes caused by external sources.
+
+ Example 13:
+
+ C: a FETCH 1:3 (MODSEQ)
+ S: * 1 FETCH (MODSEQ (624140003))
+ S: * 2 FETCH (MODSEQ (624140007))
+ S: * 3 FETCH (MODSEQ (624140005))
+ S: a OK Fetch complete
+
+ In this example, the client requests per-message mod-sequences for
+ a set of messages.
+
+ When a flag for a message is modified in a different session, the
+ server sends an unsolicited FETCH response containing the mod-
+ sequence for the message.
+
+ Example 14:
+
+ (Session 1, authenticated as a user "alex"). The user adds a
+ shared flag \Deleted:
+
+ C: A142 SELECT INBOX
+ ...
+ S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
+ S: * OK [PERMANENTFLAGS (\Answered \Deleted \Seen \*)] Limited
+
+
+
+Melnikov & Hole Standards Track [Page 14]
+
+RFC 4551 IMAP Extension for Conditional STORE June 2006
+
+
+ ...
+
+ C: A160 STORE 7 +FLAGS.SILENT (\Deleted)
+ S: * 7 FETCH (MODSEQ (2121231000))
+ S: A160 OK Store completed
+
+ (Session 2, also authenticated as the user "alex"). Any changes
+ to flags are always reported to all sessions authenticated as the
+ same user as in the session 1.
+
+ C: C180 NOOP
+ S: * 7 FETCH (FLAGS (\Deleted \Answered) MODSEQ (12121231000))
+ S: C180 OK Noop completed
+
+ (Session 3, authenticated as a user "andrew"). As \Deleted is a
+ shared flag, changes in session 1 are also reported in session 3:
+
+ C: D210 NOOP
+ S: * 7 FETCH (FLAGS (\Deleted \Answered) MODSEQ (12121231000))
+ S: D210 OK Noop completed
+
+ The user modifies a private flag \Seen in session 1...
+
+ C: A240 STORE 7 +FLAGS.SILENT (\Seen)
+ S: * 7 FETCH (MODSEQ (12121231777))
+ S: A240 OK Store completed
+
+ ...which is only reported in session 2...
+
+ C: C270 NOOP
+ S: * 7 FETCH (FLAGS (\Deleted \Answered \Seen) MODSEQ
+ (12121231777))
+ S: C270 OK Noop completed
+
+ ...but not in session 3.
+
+ C: D300 NOOP
+ S: D300 OK Noop completed
+
+ And finally, the user removes flags \Answered (shared) and \Seen
+ (private) in session 1.
+
+ C: A330 STORE 7 -FLAGS.SILENT (\Answered \Seen)
+ S: * 7 FETCH (MODSEQ (12121245160))
+ S: A330 OK Store completed
+
+
+
+
+
+
+Melnikov & Hole Standards Track [Page 15]
+
+RFC 4551 IMAP Extension for Conditional STORE June 2006
+
+
+ Both changes are reported in the session 2...
+
+ C: C360 NOOP
+ S: * 7 FETCH (FLAGS (\Deleted) MODSEQ (12121245160))
+ S: C360 OK Noop completed
+
+ ...and only changes to shared flags are reported in session 3.
+
+ C: D390 NOOP
+ S: * 7 FETCH (FLAGS (\Deleted) MODSEQ (12121245160))
+ S: D390 OK Noop completed
+
+ Server implementers should also see Section 3.8 for additional
+ quality of implementation issues related to the FETCH command.
+
+3.4. MODSEQ Search Criterion in SEARCH
+
+ The MODSEQ criterion for the SEARCH command allows a client to search
+ for the metadata items that were modified since a specified moment.
+
+ Syntax: MODSEQ [<entry-name> <entry-type-req>] <mod-sequence-valzer>
+
+ Messages that have modification values that are equal to or
+ greater than <mod-sequence-valzer>. This allows a client, for
+ example, to find out which messages contain metadata items that
+ have changed since the last time it updated its disconnected
+ cache. The client may also specify <entry-name> (name of metadata
+ item) and <entry-type-req> (type of metadata item) before
+ <mod-sequence-valzer>. <entry-type-req> can be one of "shared",
+ "priv" (private), or "all". The latter means that the server
+ should use the biggest value among "priv" and "shared" mod-
+ sequences for the metadata item. If the server doesn't store
+ internally separate mod-sequences for different metadata items, it
+ MUST ignore <entry-name> and <entry-type-req>. Otherwise, the
+ server should use them to narrow down the search.
+
+ For a flag <flagname>, the corresponding <entry-name> has a form
+ "/flags/<flagname>" as defined in [IMAPABNF]. Note that the
+ leading "\" character that denotes a system flag has to be escaped
+ as per Section 4.3 of [IMAP4], as the <entry-name> uses syntax for
+ quoted strings.
+
+ If client specifies a MODSEQ criterion in a SEARCH command and the
+ server returns a non-empty SEARCH result, the server MUST also append
+ (to the end of the untagged SEARCH response) the highest mod-sequence
+ for all messages being returned. See also Section 3.5.
+
+
+
+
+
+Melnikov & Hole Standards Track [Page 16]
+
+RFC 4551 IMAP Extension for Conditional STORE June 2006
+
+
+ Example 15:
+
+ C: a SEARCH MODSEQ "/flags/\\draft" all 620162338
+ S: * SEARCH 2 5 6 7 11 12 18 19 20 23 (MODSEQ 917162500)
+ S: a OK Search complete
+
+ In the above example, the message numbers of any messages
+ containing the string "IMAP4" in the "value" attribute of the
+ "/comment" entry and having a mod-sequence equal to or greater
+ than 620162338 for the "\Draft" flag are returned in the search
+ results.
+
+ Example 16:
+
+ C: t SEARCH OR NOT MODSEQ 720162338 LARGER 50000
+ S: * SEARCH
+ S: t OK Search complete, nothing found
+
+3.5. Modified SEARCH Untagged Response
+
+ Data: zero or more numbers
+ mod-sequence value (omitted if no match)
+
+ This document extends syntax of the untagged SEARCH response to
+ include the highest mod-sequence for all messages being returned.
+
+ If a client specifies a MODSEQ criterion in a SEARCH (or UID SEARCH)
+ command and the server returns a non-empty SEARCH result, the server
+ MUST also append (to the end of the untagged SEARCH response) the
+ highest mod-sequence for all messages being returned. See Section
+ 3.4 for examples.
+
+3.6. HIGHESTMODSEQ Status Data Items
+
+ This document defines a new status data item:
+
+ HIGHESTMODSEQ
+
+ The highest mod-sequence value of all messages in the mailbox.
+ This is the same value that is returned by the server in the
+ HIGHESTMODSEQ response code in an OK untagged response (see
+ Section 3.1.1). If the server doesn't support the persistent
+ storage of mod-sequences for the mailbox (see Section 3.1.2), the
+ server MUST return 0 as the value of HIGHESTMODSEQ status data
+ item.
+
+
+
+
+
+
+Melnikov & Hole Standards Track [Page 17]
+
+RFC 4551 IMAP Extension for Conditional STORE June 2006
+
+
+ Example 17:
+
+ C: A042 STATUS blurdybloop (UIDNEXT MESSAGES HIGHESTMODSEQ)
+ S: * STATUS blurdybloop (MESSAGES 231 UIDNEXT 44292
+ HIGHESTMODSEQ 7011231777)
+ S: A042 OK STATUS completed
+
+3.7. CONDSTORE Parameter to SELECT and EXAMINE
+
+ The CONDSTORE extension defines a single optional select parameter,
+ "CONDSTORE", which tells the server that it MUST include the MODSEQ
+ fetch response data items in all subsequent unsolicited FETCH
+ responses.
+
+ The CONDSTORE parameter to SELECT/EXAMINE helps avoid a race
+ condition that might arise when one or more metadata items are
+ modified in another session after the server has sent the
+ HIGHESTMODSEQ response code and before the client was able to issue a
+ CONDSTORE enabling command.
+
+ Example 18:
+
+ C: A142 SELECT INBOX (CONDSTORE)
+ S: * 172 EXISTS
+ S: * 1 RECENT
+ S: * OK [UNSEEN 12] Message 12 is first unseen
+ S: * OK [UIDVALIDITY 3857529045] UIDs valid
+ S: * OK [UIDNEXT 4392] Predicted next UID
+ S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
+ S: * OK [PERMANENTFLAGS (\Deleted \Seen \*)] Limited
+ S: * OK [HIGHESTMODSEQ 715194045007]
+ S: A142 OK [READ-WRITE] SELECT completed, CONDSTORE is now enabled
+
+3.8. Additional Quality-of-Implementation Issues
+
+ Server implementations should follow the following rule, which
+ applies to any successfully completed STORE/UID STORE (with and
+ without UNCHANGEDSINCE modifier), as well as to a FETCH command that
+ implicitly sets \Seen flag:
+
+ Adding the flag when it is already present or removing when it is
+ not present SHOULD NOT change the mod-sequence.
+
+ This will prevent spurious client synchronization requests.
+
+
+
+
+
+
+
+Melnikov & Hole Standards Track [Page 18]
+
+RFC 4551 IMAP Extension for Conditional STORE June 2006
+
+
+ However, note that client implementers MUST NOT rely on this server
+ behavior. A client can't distinguish between the case when a server
+ has violated the SHOULD mentioned above, and that when one or more
+ clients set and unset (or unset and set) the flag in another session.
+
+4. Formal Syntax
+
+ The following syntax specification uses the Augmented Backus-Naur
+ Form (ABNF) [ABNF] notation. Elements not defined here can be found
+ in the formal syntax of the ABNF [ABNF], IMAP [IMAP4], and IMAP ABNF
+ extensions [IMAPABNF] specifications.
+
+ Except as noted otherwise, all alphabetic characters are case-
+ insensitive. The use of upper- or lowercase characters to define
+ token strings is for editorial clarity only. Implementations MUST
+ accept these strings in a case-insensitive fashion.
+
+ capability =/ "CONDSTORE"
+
+ status-att =/ "HIGHESTMODSEQ"
+ ;; extends non-terminal defined in RFC 3501.
+
+ status-att-val =/ "HIGHESTMODSEQ" SP mod-sequence-valzer
+ ;; extends non-terminal defined in [IMAPABNF].
+ ;; Value 0 denotes that the mailbox doesn't
+ ;; support persistent mod-sequences
+ ;; as described in Section 3.1.2
+
+ store-modifier =/ "UNCHANGEDSINCE" SP mod-sequence-valzer
+ ;; Only a single "UNCHANGEDSINCE" may be
+ ;; specified in a STORE operation
+
+ fetch-modifier =/ chgsince-fetch-mod
+ ;; conforms to the generic "fetch-modifier"
+ ;; syntax defined in [IMAPABNF].
+
+ chgsince-fetch-mod = "CHANGEDSINCE" SP mod-sequence-value
+ ;; CHANGEDSINCE FETCH modifier conforms to
+ ;; the fetch-modifier syntax
+
+ fetch-att =/ fetch-mod-sequence
+ ;; modifies original IMAP4 fetch-att
+
+ fetch-mod-sequence = "MODSEQ"
+
+ fetch-mod-resp = "MODSEQ" SP "(" permsg-modsequence ")"
+
+ msg-att-dynamic =/ fetch-mod-resp
+
+
+
+Melnikov & Hole Standards Track [Page 19]
+
+RFC 4551 IMAP Extension for Conditional STORE June 2006
+
+
+ search-key =/ search-modsequence
+ ;; modifies original IMAP4 search-key
+ ;;
+ ;; This change applies to all commands
+ ;; referencing this non-terminal, in
+ ;; particular SEARCH.
+
+ search-modsequence = "MODSEQ" [search-modseq-ext] SP
+ mod-sequence-valzer
+
+ search-modseq-ext = SP entry-name SP entry-type-req
+
+ resp-text-code =/ "HIGHESTMODSEQ" SP mod-sequence-value /
+ "NOMODSEQ" /
+ "MODIFIED" SP set
+
+ entry-name = entry-flag-name
+
+ entry-flag-name = DQUOTE "/flags/" attr-flag DQUOTE
+ ;; each system or user defined flag <flag>
+ ;; is mapped to "/flags/<flag>".
+ ;;
+ ;; <entry-flag-name> follows the escape rules
+ ;; used by "quoted" string as described in
+ ;; Section 4.3 of [IMAP4], e.g., for the flag
+ ;; \Seen the corresponding <entry-name> is
+ ;; "/flags/\\seen", and for the flag
+ ;; $MDNSent, the corresponding <entry-name>
+ ;; is "/flags/$mdnsent".
+
+ entry-type-resp = "priv" / "shared"
+ ;; metadata item type
+
+ entry-type-req = entry-type-resp / "all"
+ ;; perform SEARCH operation on private
+ ;; metadata item, shared metadata item or both
+
+ permsg-modsequence = mod-sequence-value
+ ;; per message mod-sequence
+
+ mod-sequence-value = 1*DIGIT
+ ;; Positive unsigned 64-bit integer
+ ;; (mod-sequence)
+ ;; (1 <= n < 18,446,744,073,709,551,615)
+
+ mod-sequence-valzer = "0" / mod-sequence-value
+
+ search-sort-mod-seq = "(" "MODSEQ" SP mod-sequence-value ")"
+
+
+
+Melnikov & Hole Standards Track [Page 20]
+
+RFC 4551 IMAP Extension for Conditional STORE June 2006
+
+
+ select-param =/ condstore-param
+ ;; conforms to the generic "select-param"
+ ;; non-terminal syntax defined in [IMAPABNF].
+
+ condstore-param = "CONDSTORE"
+
+ mailbox-data =/ "SEARCH" [1*(SP nz-number) SP
+ search-sort-mod-seq]
+
+ attr-flag = "\\Answered" / "\\Flagged" / "\\Deleted" /
+ "\\Seen" / "\\Draft" / attr-flag-keyword /
+ attr-flag-extension
+ ;; Does not include "\\Recent"
+
+ attr-flag-extension = "\\" atom
+ ;; Future expansion. Client implementations
+ ;; MUST accept flag-extension flags. Server
+ ;; implementations MUST NOT generate
+ ;; flag-extension flags except as defined by
+ ;; future standard or standards-track
+ ;; revisions of [IMAP4].
+
+ attr-flag-keyword = atom
+
+5. Server Implementation Considerations
+
+ This section describes how a server implementation that doesn't store
+ separate per-metadata mod-sequences for different metadata items can
+ avoid sending the MODIFIED response to any of the following
+ conditional STORE operations:
+
+ +FLAGS
+ -FLAGS
+ +FLAGS.SILENT
+ -FLAGS.SILENT
+
+ Note that the optimization described in this section can't be
+ performed in case of a conditional STORE FLAGS operation.
+
+ Let's use the following example. The client has issued
+
+ C: a106 STORE 100:150 (UNCHANGEDSINCE 212030000000)
+ +FLAGS.SILENT ($Processed)
+
+ When the server receives the command and parses it successfully, it
+ iterates through the message set and tries to execute the conditional
+ STORE command for each message.
+
+
+
+
+Melnikov & Hole Standards Track [Page 21]
+
+RFC 4551 IMAP Extension for Conditional STORE June 2006
+
+
+ Each server internally works as a client, i.e., it has to cache the
+ current state of all IMAP flags as it is known to the client. In
+ order to report flag changes to the client, the server compares the
+ cached values with the values in its database for IMAP flags.
+
+ Imagine that another client has changed the state of a flag \Deleted
+ on the message 101 and that the change updated the mod-sequence for
+ the message. The server knows that the mod-sequence for the mailbox
+ has changed; however, it also knows that:
+
+ a) the client is not interested in \Deleted flag, as it hasn't
+ included it in +FLAGS.SILENT operation; and
+
+ b) the state of the flag $Processed hasn't changed (the server can
+ determine this by comparing cached flag state with the state of
+ the flag in the database).
+
+ Therefore, the server doesn't have to report MODIFIED to the client.
+ Instead, the server may set $Processed flag, update the mod-sequence
+ for the message 101 once again and send an untagged FETCH response
+ with new mod-sequence and flags:
+
+ S: * 101 FETCH (MODSEQ (303011130956)
+ FLAGS ($Processed \Deleted \Answered))
+
+ See also Section 3.8 for additional quality-of-implementation issues.
+
+6. Security Considerations
+
+ It is believed that the Conditional STORE extension doesn't raise any
+ new security concerns that are not already discussed in [IMAP4].
+ However, the availability of this extension may make it possible for
+ IMAP4 to be used in critical applications it could not be used for
+ previously, making correct IMAP server implementation and operation
+ even more important.
+
+7. IANA Considerations
+
+ IMAP4 capabilities are registered by publishing a standards track or
+ IESG approved experimental RFC. The registry is currently located
+ at:
+
+ http://www.iana.org/assignments/imap4-capabilities
+
+ This document defines the CONDSTORE IMAP capability. IANA has added
+ it to the registry accordingly.
+
+
+
+
+
+Melnikov & Hole Standards Track [Page 22]
+
+RFC 4551 IMAP Extension for Conditional STORE June 2006
+
+
+8. References
+
+8.1. Normative References
+
+ [KEYWORDS] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [ABNF] Crocker, D. and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", RFC 4234, October 2005.
+
+ [IMAP4] Crispin, M., "INTERNET MESSAGE ACCESS PROTOCOL - VERSION
+ 4rev1", RFC 3501, March 2003.
+
+ [IMAPABNF] Melnikov, A. and C. Daboo, "Collected Extensions to IMAP4
+ ABNF", RFC 4466, April 2006.
+
+8.2. Informative References
+
+ [ACAP] Newman, C. and J. Myers, "ACAP -- Application
+ Configuration Access Protocol", RFC 2244, November 1997.
+
+ [ACL] Melnikov, A., "IMAP4 Access Control List (ACL) Extension",
+ RFC 4314, December 2005.
+
+ [ANN] Daboo, C. and R. Gellens, "IMAP ANNOTATE Extension", Work
+ in Progress, March 2006.
+
+ [NTP] Mills, D., "Network Time Protocol (Version 3)
+ Specification, Implementation and Analysis", RFC 1305,
+ March 1992.
+
+ [RFC-2180] Gahrns, M., "IMAP4 Multi-Accessed Mailbox Practice", RFC
+ 2180, July 1997.
+
+9. Acknowledgements
+
+ Some text was borrowed from "IMAP ANNOTATE Extension" [ANN] by
+ Randall Gellens and Cyrus Daboo and from "ACAP -- Application
+ Configuration Access Protocol" [ACAP] by Chris Newman and John Myers.
+
+ Many thanks to Randall Gellens for his thorough review of the
+ document.
+
+ The authors also acknowledge the feedback provided by Cyrus Daboo,
+ Larry Greenfield, Chris Newman, Harrie Hazewinkel, Arnt Gulbrandsen,
+ Timo Sirainen, Mark Crispin, Ned Freed, Ken Murchison, and Dave
+ Cridland.
+
+
+
+
+Melnikov & Hole Standards Track [Page 23]
+
+RFC 4551 IMAP Extension for Conditional STORE June 2006
+
+
+Authors' Addresses
+
+ Alexey Melnikov
+ Isode Limited
+ 5 Castle Business Village
+ 36 Station Road
+ Hampton, Middlesex
+ TW12 2BX,
+ United Kingdom
+
+ EMail: Alexey.Melnikov@isode.com
+
+
+ Steve Hole
+ ACI WorldWide/MessagingDirect
+ #1807, 10088 102 Ave
+ Edmonton, AB
+ T5J 2Z1
+ Canada
+
+ EMail: Steve.Hole@messagingdirect.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Melnikov & Hole Standards Track [Page 24]
+
+RFC 4551 IMAP Extension for Conditional STORE June 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is provided by the IETF
+ Administrative Support Activity (IASA).
+
+
+
+
+
+
+
+Melnikov & Hole Standards Track [Page 25]
+
diff --git a/imap/docs/rfc/rfc4616.txt b/imap/docs/rfc/rfc4616.txt
new file mode 100644
index 00000000..991189d5
--- /dev/null
+++ b/imap/docs/rfc/rfc4616.txt
@@ -0,0 +1,619 @@
+
+
+
+
+
+
+Network Working Group K. Zeilenga, Ed.
+Request for Comments: 4616 OpenLDAP Foundation
+Updates: 2595 August 2006
+Category: Standards Track
+
+
+ The PLAIN Simple Authentication and Security Layer (SASL) Mechanism
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2006).
+
+Abstract
+
+ This document defines a simple clear-text user/password Simple
+ Authentication and Security Layer (SASL) mechanism called the PLAIN
+ mechanism. The PLAIN mechanism is intended to be used, in
+ combination with data confidentiality services provided by a lower
+ layer, in protocols that lack a simple password authentication
+ command.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 1]
+
+RFC 4616 The PLAIN SASL Mechanism August 2006
+
+
+1. Introduction
+
+ Clear-text, multiple-use passwords are simple, interoperate with
+ almost all existing operating system authentication databases, and
+ are useful for a smooth transition to a more secure password-based
+ authentication mechanism. The drawback is that they are unacceptable
+ for use over network connections where data confidentiality is not
+ ensured.
+
+ This document defines the PLAIN Simple Authentication and Security
+ Layer ([SASL]) mechanism for use in protocols with no clear-text
+ login command (e.g., [ACAP] or [SMTP-AUTH]). This document updates
+ RFC 2595, replacing Section 6. Changes since RFC 2595 are detailed
+ in Appendix A.
+
+ The name associated with this mechanism is "PLAIN".
+
+ The PLAIN SASL mechanism does not provide a security layer.
+
+ The PLAIN mechanism should not be used without adequate data security
+ protection as this mechanism affords no integrity or confidentiality
+ protections itself. The mechanism is intended to be used with data
+ security protections provided by application-layer protocol,
+ generally through its use of Transport Layer Security ([TLS])
+ services.
+
+ By default, implementations SHOULD advertise and make use of the
+ PLAIN mechanism only when adequate data security services are in
+ place. Specifications for IETF protocols that indicate that this
+ mechanism is an applicable authentication mechanism MUST mandate that
+ implementations support an strong data security service, such as TLS.
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in [Keywords].
+
+2. PLAIN SASL Mechanism
+
+ The mechanism consists of a single message, a string of [UTF-8]
+ encoded [Unicode] characters, from the client to the server. The
+ client presents the authorization identity (identity to act as),
+ followed by a NUL (U+0000) character, followed by the authentication
+ identity (identity whose password will be used), followed by a NUL
+ (U+0000) character, followed by the clear-text password. As with
+ other SASL mechanisms, the client does not provide an authorization
+ identity when it wishes the server to derive an identity from the
+ credentials and use that as the authorization identity.
+
+
+
+
+Zeilenga Standards Track [Page 2]
+
+RFC 4616 The PLAIN SASL Mechanism August 2006
+
+
+ The formal grammar for the client message using Augmented BNF [ABNF]
+ follows.
+
+ message = [authzid] UTF8NUL authcid UTF8NUL passwd
+ authcid = 1*SAFE ; MUST accept up to 255 octets
+ authzid = 1*SAFE ; MUST accept up to 255 octets
+ passwd = 1*SAFE ; MUST accept up to 255 octets
+ UTF8NUL = %x00 ; UTF-8 encoded NUL character
+
+ SAFE = UTF1 / UTF2 / UTF3 / UTF4
+ ;; any UTF-8 encoded Unicode character except NUL
+
+ UTF1 = %x01-7F ;; except NUL
+ UTF2 = %xC2-DF UTF0
+ UTF3 = %xE0 %xA0-BF UTF0 / %xE1-EC 2(UTF0) /
+ %xED %x80-9F UTF0 / %xEE-EF 2(UTF0)
+ UTF4 = %xF0 %x90-BF 2(UTF0) / %xF1-F3 3(UTF0) /
+ %xF4 %x80-8F 2(UTF0)
+ UTF0 = %x80-BF
+
+ The authorization identity (authzid), authentication identity
+ (authcid), password (passwd), and NUL character deliminators SHALL be
+ transferred as [UTF-8] encoded strings of [Unicode] characters. As
+ the NUL (U+0000) character is used as a deliminator, the NUL (U+0000)
+ character MUST NOT appear in authzid, authcid, or passwd productions.
+
+ The form of the authzid production is specific to the application-
+ level protocol's SASL profile [SASL]. The authcid and passwd
+ productions are form-free. Use of non-visible characters or
+ characters that a user may be unable to enter on some keyboards is
+ discouraged.
+
+ Servers MUST be capable of accepting authzid, authcid, and passwd
+ productions up to and including 255 octets. It is noted that the
+ UTF-8 encoding of a Unicode character may be as long as 4 octets.
+
+ Upon receipt of the message, the server will verify the presented (in
+ the message) authentication identity (authcid) and password (passwd)
+ with the system authentication database, and it will verify that the
+ authentication credentials permit the client to act as the (presented
+ or derived) authorization identity (authzid). If both steps succeed,
+ the user is authenticated.
+
+ The presented authentication identity and password strings, as well
+ as the database authentication identity and password strings, are to
+ be prepared before being used in the verification process. The
+ [SASLPrep] profile of the [StringPrep] algorithm is the RECOMMENDED
+ preparation algorithm. The SASLprep preparation algorithm is
+
+
+
+Zeilenga Standards Track [Page 3]
+
+RFC 4616 The PLAIN SASL Mechanism August 2006
+
+
+ recommended to improve the likelihood that comparisons behave in an
+ expected manner. The SASLprep preparation algorithm is not mandatory
+ so as to allow the server to employ other preparation algorithms
+ (including none) when appropriate. For instance, use of a different
+ preparation algorithm may be necessary for the server to interoperate
+ with an external system.
+
+ When preparing the presented strings using [SASLPrep], the presented
+ strings are to be treated as "query" strings (Section 7 of
+ [StringPrep]) and hence unassigned code points are allowed to appear
+ in their prepared output. When preparing the database strings using
+ [SASLPrep], the database strings are to be treated as "stored"
+ strings (Section 7 of [StringPrep]) and hence unassigned code points
+ are prohibited from appearing in their prepared output.
+
+ Regardless of the preparation algorithm used, if the output of a
+ non-invertible function (e.g., hash) of the expected string is
+ stored, the string MUST be prepared before input to that function.
+
+ Regardless of the preparation algorithm used, if preparation fails or
+ results in an empty string, verification SHALL fail.
+
+ When no authorization identity is provided, the server derives an
+ authorization identity from the prepared representation of the
+ provided authentication identity string. This ensures that the
+ derivation of different representations of the authentication
+ identity produces the same authorization identity.
+
+ The server MAY use the credentials to initialize any new
+ authentication database, such as one suitable for [CRAM-MD5] or
+ [DIGEST-MD5].
+
+3. Pseudo-Code
+
+ This section provides pseudo-code illustrating the verification
+ process (using hashed passwords and the SASLprep preparation
+ function) discussed above. This section is not definitive.
+
+ boolean Verify(string authzid, string authcid, string passwd) {
+ string pAuthcid = SASLprep(authcid, true); # prepare authcid
+ string pPasswd = SASLprep(passwd, true); # prepare passwd
+ if (pAuthcid == NULL || pPasswd == NULL) {
+ return false; # preparation failed
+ }
+ if (pAuthcid == "" || pPasswd == "") {
+ return false; # empty prepared string
+ }
+
+
+
+
+Zeilenga Standards Track [Page 4]
+
+RFC 4616 The PLAIN SASL Mechanism August 2006
+
+
+ storedHash = FetchPasswordHash(pAuthcid);
+ if (storedHash == NULL || storedHash == "") {
+ return false; # error or unknown authcid
+ }
+
+ if (!Compare(storedHash, Hash(pPasswd))) {
+ return false; # incorrect password
+ }
+
+ if (authzid == NULL ) {
+ authzid = DeriveAuthzid(pAuthcid);
+ if (authzid == NULL || authzid == "") {
+ return false; # could not derive authzid
+ }
+ }
+
+ if (!Authorize(pAuthcid, authzid)) {
+ return false; # not authorized
+ }
+
+ return true;
+ }
+
+ The second parameter of the SASLprep function, when true, indicates
+ that unassigned code points are allowed in the input. When the
+ SASLprep function is called to prepare the password prior to
+ computing the stored hash, the second parameter would be false.
+
+ The second parameter provided to the Authorize function is not
+ prepared by this code. The application-level SASL profile should be
+ consulted to determine what, if any, preparation is necessary.
+
+ Note that the DeriveAuthzid and Authorize functions (whether
+ implemented as one function or two, whether designed in a manner in
+ which these functions or whether the mechanism implementation can be
+ reused elsewhere) require knowledge and understanding of mechanism
+ and the application-level protocol specification and/or
+ implementation details to implement.
+
+ Note that the Authorize function outcome is clearly dependent on
+ details of the local authorization model and policy. Both functions
+ may be dependent on other factors as well.
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 5]
+
+RFC 4616 The PLAIN SASL Mechanism August 2006
+
+
+4. Examples
+
+ This section provides examples of PLAIN authentication exchanges.
+ The examples are intended to help the readers understand the above
+ text. The examples are not definitive.
+
+ "C:" and "S:" indicate lines sent by the client and server,
+ respectively. "<NUL>" represents a single NUL (U+0000) character.
+ The Application Configuration Access Protocol ([ACAP]) is used in the
+ examples.
+
+ The first example shows how the PLAIN mechanism might be used for
+ user authentication.
+
+ S: * ACAP (SASL "CRAM-MD5") (STARTTLS)
+ C: a001 STARTTLS
+ S: a001 OK "Begin TLS negotiation now"
+ <TLS negotiation, further commands are under TLS layer>
+ S: * ACAP (SASL "CRAM-MD5" "PLAIN")
+ C: a002 AUTHENTICATE "PLAIN"
+ S: + ""
+ C: {21}
+ C: <NUL>tim<NUL>tanstaaftanstaaf
+ S: a002 OK "Authenticated"
+
+ The second example shows how the PLAIN mechanism might be used to
+ attempt to assume the identity of another user. In this example, the
+ server rejects the request. Also, this example makes use of the
+ protocol optional initial response capability to eliminate a round-
+ trip.
+
+ S: * ACAP (SASL "CRAM-MD5") (STARTTLS)
+ C: a001 STARTTLS
+ S: a001 OK "Begin TLS negotiation now"
+ <TLS negotiation, further commands are under TLS layer>
+ S: * ACAP (SASL "CRAM-MD5" "PLAIN")
+ C: a002 AUTHENTICATE "PLAIN" {20+}
+ C: Ursel<NUL>Kurt<NUL>xipj3plmq
+ S: a002 NO "Not authorized to requested authorization identity"
+
+5. Security Considerations
+
+ As the PLAIN mechanism itself provided no integrity or
+ confidentiality protections, it should not be used without adequate
+ external data security protection, such as TLS services provided by
+ many application-layer protocols. By default, implementations SHOULD
+ NOT advertise and SHOULD NOT make use of the PLAIN mechanism unless
+ adequate data security services are in place.
+
+
+
+Zeilenga Standards Track [Page 6]
+
+RFC 4616 The PLAIN SASL Mechanism August 2006
+
+
+ When the PLAIN mechanism is used, the server gains the ability to
+ impersonate the user to all services with the same password
+ regardless of any encryption provided by TLS or other confidentiality
+ protection mechanisms. Whereas many other authentication mechanisms
+ have similar weaknesses, stronger SASL mechanisms address this issue.
+ Clients are encouraged to have an operational mode where all
+ mechanisms that are likely to reveal the user's password to the
+ server are disabled.
+
+ General [SASL] security considerations apply to this mechanism.
+
+ Unicode, [UTF-8], and [StringPrep] security considerations also
+ apply.
+
+6. IANA Considerations
+
+ The SASL Mechanism registry [IANA-SASL] entry for the PLAIN mechanism
+ has been updated by the IANA to reflect that this document now
+ provides its technical specification.
+
+ To: iana@iana.org
+ Subject: Updated Registration of SASL mechanism PLAIN
+
+ SASL mechanism name: PLAIN
+ Security considerations: See RFC 4616.
+ Published specification (optional, recommended): RFC 4616
+ Person & email address to contact for further information:
+ Kurt Zeilenga <kurt@openldap.org>
+ IETF SASL WG <ietf-sasl@imc.org>
+ Intended usage: COMMON
+ Author/Change controller: IESG <iesg@ietf.org>
+ Note: Updates existing entry for PLAIN
+
+7. Acknowledgements
+
+ This document is a revision of RFC 2595 by Chris Newman. Portions of
+ the grammar defined in Section 2 were borrowed from [UTF-8] by
+ Francois Yergeau.
+
+ This document is a product of the IETF Simple Authentication and
+ Security Layer (SASL) Working Group.
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 7]
+
+RFC 4616 The PLAIN SASL Mechanism August 2006
+
+
+8. Normative References
+
+ [ABNF] Crocker, D., Ed. and P. Overell, "Augmented BNF for
+ Syntax Specifications: ABNF", RFC 4234, October 2005.
+
+ [Keywords] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [SASL] Melnikov, A., Ed., and K. Zeilenga, Ed., "Simple
+ Authentication and Security Layer (SASL)", RFC 4422,
+ June 2006.
+
+ [SASLPrep] Zeilenga, K., "SASLprep: Stringprep Profile for User
+ Names and Passwords", RFC 4013, February 2005.
+
+ [StringPrep] Hoffman, P. and M. Blanchet, "Preparation of
+ Internationalized Strings ("stringprep")", RFC 3454,
+ December 2002.
+
+ [Unicode] The Unicode Consortium, "The Unicode Standard, Version
+ 3.2.0" is defined by "The Unicode Standard, Version
+ 3.0" (Reading, MA, Addison-Wesley, 2000. ISBN 0-201-
+ 61633-5), as amended by the "Unicode Standard Annex
+ #27: Unicode 3.1"
+ (http://www.unicode.org/reports/tr27/) and by the
+ "Unicode Standard Annex #28: Unicode 3.2"
+ (http://www.unicode.org/reports/tr28/).
+
+ [UTF-8] Yergeau, F., "UTF-8, a transformation format of ISO
+ 10646", STD 63, RFC 3629, November 2003.
+
+ [TLS] Dierks, T. and E. Rescorla, "The Transport Layer
+ Security (TLS) Protocol Version 1.1", RFC 4346, April
+ 2006.
+
+9. Informative References
+
+ [ACAP] Newman, C. and J. Myers, "ACAP -- Application
+ Configuration Access Protocol", RFC 2244, November
+ 1997.
+
+ [CRAM-MD5] Nerenberg, L., Ed., "The CRAM-MD5 SASL Mechanism", Work
+ in Progress, June 2006.
+
+ [DIGEST-MD5] Melnikov, A., Ed., "Using Digest Authentication as a
+ SASL Mechanism", Work in Progress, June 2006.
+
+
+
+
+
+Zeilenga Standards Track [Page 8]
+
+RFC 4616 The PLAIN SASL Mechanism August 2006
+
+
+ [IANA-SASL] IANA, "SIMPLE AUTHENTICATION AND SECURITY LAYER (SASL)
+ MECHANISMS",
+ <http://www.iana.org/assignments/sasl-mechanisms>.
+
+ [SMTP-AUTH] Myers, J., "SMTP Service Extension for Authentication",
+ RFC 2554, March 1999.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 9]
+
+RFC 4616 The PLAIN SASL Mechanism August 2006
+
+
+Appendix A. Changes since RFC 2595
+
+ This appendix is non-normative.
+
+ This document replaces Section 6 of RFC 2595.
+
+ The specification details how the server is to compare client-
+ provided character strings with stored character strings.
+
+ The ABNF grammar was updated. In particular, the grammar now allows
+ LINE FEED (U+000A) and CARRIAGE RETURN (U+000D) characters in the
+ authzid, authcid, passwd productions. However, whether these control
+ characters may be used depends on the string preparation rules
+ applicable to the production. For passwd and authcid productions,
+ control characters are prohibited. For authzid, one must consult the
+ application-level SASL profile. This change allows PLAIN to carry
+ all possible authorization identity strings allowed in SASL.
+
+ Pseudo-code was added.
+
+ The example section was expanded to illustrate more features of the
+ PLAIN mechanism.
+
+Editor's Address
+
+ Kurt D. Zeilenga
+ OpenLDAP Foundation
+
+ EMail: Kurt@OpenLDAP.org
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 10]
+
+RFC 4616 The PLAIN SASL Mechanism August 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The Internet Society (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+ ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+ INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+ INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is provided by the IETF
+ Administrative Support Activity (IASA).
+
+
+
+
+
+
+
+Zeilenga Standards Track [Page 11]
+
diff --git a/imap/docs/rfc/rfc4731.txt b/imap/docs/rfc/rfc4731.txt
new file mode 100644
index 00000000..8c4869aa
--- /dev/null
+++ b/imap/docs/rfc/rfc4731.txt
@@ -0,0 +1,451 @@
+
+
+
+
+
+
+Network Working Group A. Melnikov
+Request for Comments: 4731 Isode Ltd
+Category: Standards Track D. Cridland
+ Inventure Systems Ltd
+ November 2006
+
+
+ IMAP4 Extension to SEARCH Command for Controlling
+ What Kind of Information Is Returned
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The IETF Trust (2006).
+
+Abstract
+
+ This document extends IMAP (RFC 3501) SEARCH and UID SEARCH commands
+ with several result options, which can control what kind of
+ information is returned. The following result options are defined:
+ minimal value, maximal value, all found messages, and number of found
+ messages.
+
+Table of Contents
+
+ 1. Introduction ....................................................2
+ 2. Conventions Used in This Document ...............................2
+ 3. IMAP Protocol Changes ...........................................2
+ 3.1. New SEARCH/UID SEARCH Result Options .......................2
+ 3.2. Interaction with CONDSTORE extension .......................4
+ 4. Formal Syntax ...................................................5
+ 5. Security Considerations .........................................6
+ 6. IANA Considerations .............................................6
+ 7. Normative References ............................................6
+ 8. Acknowledgments .................................................6
+
+
+
+
+
+
+
+
+
+Melnikov & Cridland Standards Track [Page 1]
+
+RFC 4731 IMAP4 Extension to SEARCH November 2006
+
+
+1. Introduction
+
+ [IMAPABNF] extended SEARCH and UID SEARCH commands with result
+ specifiers (also known as result options), which can control what
+ kind of information is returned.
+
+ A server advertising the ESEARCH capability supports the following
+ result options: minimal value, maximal value, all found messages,
+ and number of found messages. These result options allow clients to
+ get SEARCH results in more convenient forms, while also saving
+ bandwidth required to transport the results, for example, by finding
+ the first unseen message or returning the number of unseen or deleted
+ messages. Also, when a single MIN or a single MAX result option is
+ specified, servers can optimize execution of SEARCHes.
+
+2. Conventions Used in This Document
+
+ In examples, "C:" and "S:" indicate lines sent by the client and
+ server, respectively.
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in RFC 2119 [KEYWORDS].
+
+3. IMAP Protocol Changes
+
+3.1. New SEARCH/UID SEARCH Result Options
+
+ The SEARCH/UID SEARCH commands are extended to allow for the
+ following result options:
+
+ MIN
+ Return the lowest message number/UID that satisfies the SEARCH
+ criteria.
+
+ If the SEARCH results in no matches, the server MUST NOT
+ include the MIN result option in the ESEARCH response; however,
+ it still MUST send the ESEARCH response.
+
+ MAX
+ Return the highest message number/UID that satisfies the SEARCH
+ criteria.
+
+ If the SEARCH results in no matches, the server MUST NOT
+ include the MAX result option in the ESEARCH response; however,
+ it still MUST send the ESEARCH response.
+
+
+
+
+
+Melnikov & Cridland Standards Track [Page 2]
+
+RFC 4731 IMAP4 Extension to SEARCH November 2006
+
+
+ ALL
+ Return all message numbers/UIDs that satisfy the SEARCH
+ criteria. Unlike regular (unextended) SEARCH, the messages are
+ always returned using the sequence-set syntax. A sequence-set
+ representation may be more compact and can be used as is in a
+ subsequent command that accepts sequence-set. Note, the client
+ MUST NOT assume that messages/UIDs will be listed in any
+ particular order.
+
+ If the SEARCH results in no matches, the server MUST NOT
+ include the ALL result option in the ESEARCH response; however,
+ it still MUST send the ESEARCH response.
+
+ COUNT
+ Return number of the messages that satisfy the SEARCH criteria.
+ This result option MUST always be included in the ESEARCH
+ response.
+
+ If one or more result options described above are specified, the
+ extended SEARCH command MUST return a single ESEARCH response
+ [IMAPABNF], instead of the SEARCH response.
+
+ An extended UID SEARCH command MUST cause an ESEARCH response with
+ the UID indicator present.
+
+ Note that future extensions to this document can allow servers to
+ return multiple ESEARCH responses for a single extended SEARCH
+ command. These extensions will have to describe how results from
+ multiple ESEARCH responses are to be amalgamated.
+
+ If the list of result options is empty, that requests the server to
+ return an ESEARCH response instead of the SEARCH response. This is
+ equivalent to "(ALL)".
+
+ Example: C: A282 SEARCH RETURN (MIN COUNT) FLAGGED
+ SINCE 1-Feb-1994 NOT FROM "Smith"
+ S: * ESEARCH (TAG "A282") MIN 2 COUNT 3
+ S: A282 OK SEARCH completed
+
+ Example: C: A283 SEARCH RETURN () FLAGGED
+ SINCE 1-Feb-1994 NOT FROM "Smith"
+ S: * ESEARCH (TAG "A283") ALL 2,10:11
+ S: A283 OK SEARCH completed
+
+ The following example demonstrates finding the first unseen message
+ as returned in the UNSEEN response code on a successful SELECT
+ command:
+
+
+
+
+Melnikov & Cridland Standards Track [Page 3]
+
+RFC 4731 IMAP4 Extension to SEARCH November 2006
+
+
+ Example: C: A284 SEARCH RETURN (MIN) UNSEEN
+ S: * ESEARCH (TAG "A284") MIN 4
+ S: A284 OK SEARCH completed
+
+ The following example demonstrates that if the ESEARCH UID indicator
+ is present, all data in the ESEARCH response is referring to UIDs;
+ for example, the MIN result specifier will be followed by a UID.
+
+ Example: C: A285 UID SEARCH RETURN (MIN MAX) 1:5000
+ S: * ESEARCH (TAG "A285") UID MIN 7 MAX 3800
+ S: A285 OK SEARCH completed
+
+ The following example demonstrates returning the number of deleted
+ messages:
+
+ Example: C: A286 SEARCH RETURN (COUNT) DELETED
+ S: * ESEARCH (TAG "A286") COUNT 15
+ S: A286 OK SEARCH completed
+
+3.2. Interaction with CONDSTORE extension
+
+ When the server supports both the ESEARCH and the CONDSTORE
+ [CONDSTORE] extension, and the client requests one or more result
+ option described in section 3.1 together with the MODSEQ search
+ criterion in the same SEARCH/UID SEARCH command, then the server MUST
+ return the ESEARCH response containing the MODSEQ result option
+ (described in the following paragraph) instead of the extended SEARCH
+ response described in section 3.5 of [CONDSTORE].
+
+ If the SEARCH/UID SEARCH command contained a single MIN or MAX result
+ option, the MODSEQ result option contains the mod-sequence for the
+ found message. If the SEARCH/UID SEARCH command contained both MIN
+ and MAX result options and no ALL/COUNT option, the MODSEQ result
+ option contains the highest mod-sequence for the two returned
+ messages. Otherwise the MODSEQ result option contains the highest
+ mod-sequence for all messages being returned.
+
+ Example: The following example demonstrates how Example 15 from
+ [CONDSTORE] would look in the presence of one or more result option:
+
+ C: a1 SEARCH RETURN (MIN) MODSEQ "/flags/\\draft"
+ all 620162338
+ S: * ESEARCH (TAG "a1") MIN 2 MODSEQ 917162488
+ S: a1 OK Search complete
+
+ C: a2 SEARCH RETURN (MAX) MODSEQ "/flags/\\draft"
+ all 620162338
+ S: * ESEARCH (TAG "a2") MAX 23 MODSEQ 907162321
+
+
+
+Melnikov & Cridland Standards Track [Page 4]
+
+RFC 4731 IMAP4 Extension to SEARCH November 2006
+
+
+ S: a2 OK Search complete
+
+ C: a3 SEARCH RETURN (MIN MAX) MODSEQ "/flags/\\draft"
+ all 620162338
+ S: * ESEARCH (TAG "a3") MIN 2 MAX 23 MODSEQ 917162488
+ S: a3 OK Search complete
+
+ C: a4 SEARCH RETURN (MIN COUNT) MODSEQ "/flags/\\draft"
+ all 620162338
+ S: * ESEARCH (TAG "a4") MIN 2 COUNT 10 MODSEQ 917162500
+ S: a4 OK Search complete
+
+4. Formal Syntax
+
+ The following syntax specification uses the Augmented Backus-Naur
+ Form (ABNF) notation as specified in [ABNF].
+
+ Non-terminals referenced but not defined below are as defined by
+ [IMAP4], [CONDSTORE], or [IMAPABNF].
+
+ Except as noted otherwise, all alphabetic characters are case-
+ insensitive. The use of upper or lowercase characters to define
+ token strings is for editorial clarity only. Implementations MUST
+ accept these strings in a case-insensitive fashion.
+
+ capability =/ "ESEARCH"
+
+ search-return-data = "MIN" SP nz-number /
+ "MAX" SP nz-number /
+ "ALL" SP sequence-set /
+ "COUNT" SP number
+ ;; conforms to the generic
+ ;; search-return-data syntax defined
+ ;; in [IMAPABNF]
+
+ search-return-opt = "MIN" / "MAX" / "ALL" / "COUNT"
+ ;; conforms to generic search-return-opt
+ ;; syntax defined in [IMAPABNF]
+
+ When the CONDSTORE [CONDSTORE] IMAP extension is also supported,
+ the ABNF is updated as follows:
+
+ search-return-data =/ "MODSEQ" SP mod-sequence-value
+ ;; mod-sequence-value is defined
+ ;; in [CONDSTORE]
+
+
+
+
+
+
+Melnikov & Cridland Standards Track [Page 5]
+
+RFC 4731 IMAP4 Extension to SEARCH November 2006
+
+
+5. Security Considerations
+
+ In the general case, the IMAP SEARCH/UID SEARCH commands can be CPU
+ and/or IO intensive, and are seen by some as a potential attack point
+ for denial of service attacks, so some sites/implementations even
+ disable them entirely. This is quite unfortunate, as SEARCH command
+ is one of the best examples demonstrating IMAP advantage over POP3.
+
+ The ALL and COUNT return options don't change how SEARCH is working
+ internally; they only change how information about found messages is
+ returned. MIN and MAX SEARCH result options described in this
+ document can lighten the load on IMAP servers that choose to optimize
+ SEARCHes containing only one or both of them.
+
+ It is believed that this extension doesn't raise any additional
+ security concerns not already discussed in [IMAP4].
+
+6. IANA Considerations
+
+ IMAP4 capabilities are registered by publishing a standards track RFC
+ or an IESG-approved experimental RFC. The registry is currently
+ located at <http://www.iana.org/assignments/imap4-capabilities>.
+
+ This document defines the ESEARCH IMAP capability, which IANA added
+ to the registry.
+
+7. Normative References
+
+ [KEYWORDS] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [IMAP4] Crispin, M., "INTERNET MESSAGE ACCESS PROTOCOL - VERSION
+ 4rev1", RFC 3501, March 2003.
+
+ [ABNF] Crocker, D. (Ed.) and P. Overell , "Augmented BNF for
+ Syntax Specifications: ABNF", RFC 4234, October 2005.
+
+ [IMAPABNF] Melnikov, A. and C. Daboo, "Collected Extensions to IMAP4
+ ABNF", RFC 4466, April 2006..
+
+ [CONDSTORE] Melnikov, A. and S. Hole, "IMAP Extension for Conditional
+ STORE", RFC 4551, June 2006.
+
+8. Acknowledgments
+
+ Thanks to Michael Wener, Arnt Gulbrandsen, Cyrus Daboo, Mark Crispin,
+ and Pete Maclean for comments and corrections.
+
+
+
+
+Melnikov & Cridland Standards Track [Page 6]
+
+RFC 4731 IMAP4 Extension to SEARCH November 2006
+
+
+Authors' Addresses
+
+ Alexey Melnikov
+ Isode Limited
+ 5 Castle Business Village
+ 36 Station Road
+ Hampton, Middlesex, TW12 2BX
+ UK
+
+ EMail: Alexey.Melnikov@isode.com
+
+
+ Dave A. Cridland
+ Inventure Systems Limited
+
+ EMail: dave.cridland@inventuresystems.co.uk
+ URL: http://invsys.co.uk/dave/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Melnikov & Cridland Standards Track [Page 7]
+
+RFC 4731 IMAP4 Extension to SEARCH November 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The IETF Trust (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY, THE IETF TRUST,
+ AND THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT
+ THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY
+ IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR
+ PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+Melnikov & Cridland Standards Track [Page 8]
+
diff --git a/imap/docs/rfc/rfc4752.txt b/imap/docs/rfc/rfc4752.txt
new file mode 100644
index 00000000..bfd8e30b
--- /dev/null
+++ b/imap/docs/rfc/rfc4752.txt
@@ -0,0 +1,563 @@
+
+
+
+
+
+
+Network Working Group A. Melnikov, Ed.
+Request for Comments: 4752 Isode
+Obsoletes: 2222 November 2006
+Category: Standards Track
+
+
+ The Kerberos V5 ("GSSAPI")
+ Simple Authentication and Security Layer (SASL) Mechanism
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The IETF Trust (2006).
+
+Abstract
+
+ The Simple Authentication and Security Layer (SASL) is a framework
+ for adding authentication support to connection-based protocols.
+ This document describes the method for using the Generic Security
+ Service Application Program Interface (GSS-API) Kerberos V5 in the
+ SASL.
+
+ This document replaces Section 7.2 of RFC 2222, the definition of the
+ "GSSAPI" SASL mechanism. This document, together with RFC 4422,
+ obsoletes RFC 2222.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Melnikov Standards Track [Page 1]
+
+RFC 4752 SASL GSSAPI Mechanism November 2006
+
+
+Table of Contents
+
+ 1. Introduction ....................................................2
+ 1.1. Relationship to Other Documents ............................2
+ 2. Conventions Used in This Document ...............................2
+ 3. Kerberos V5 GSS-API Mechanism ...................................2
+ 3.1. Client Side of Authentication Protocol Exchange ............3
+ 3.2. Server Side of Authentication Protocol Exchange ............4
+ 3.3. Security Layer .............................................6
+ 4. IANA Considerations .............................................7
+ 5. Security Considerations .........................................7
+ 6. Acknowledgements ................................................8
+ 7. Changes since RFC 2222 ..........................................8
+ 8. References ......................................................8
+ 8.1. Normative References .......................................8
+ 8.2. Informative References .....................................9
+
+1. Introduction
+
+ This specification documents currently deployed Simple Authentication
+ and Security Layer (SASL [SASL]) mechanism supporting the Kerberos V5
+ [KERBEROS] Generic Security Service Application Program Interface
+ ([GSS-API]) mechanism [RFC4121]. The authentication sequence is
+ described in Section 3. Note that the described authentication
+ sequence has known limitations, in particular, it lacks channel
+ bindings and the number of round-trips required to complete
+ authentication exchange is not minimal. SASL WG is working on a
+ separate document that should address these limitations.
+
+1.1. Relationship to Other Documents
+
+ This document, together with RFC 4422, obsoletes RFC 2222 in its
+ entirety. This document replaces Section 7.2 of RFC 2222. The
+ remainder is obsoleted as detailed in Section 1.2 of RFC 4422.
+
+2. Conventions Used in This Document
+
+ The key words "MUST", "MUST NOT", "SHOULD", "SHOULD NOT", and "MAY"
+ in this document are to be interpreted as defined in "Key words for
+ use in RFCs to Indicate Requirement Levels" [KEYWORDS].
+
+3. Kerberos V5 GSS-API Mechanism
+
+ The SASL mechanism name for the Kerberos V5 GSS-API mechanism
+ [RFC4121] is "GSSAPI". Though known as the SASL GSSAPI mechanism,
+ the mechanism is specifically tied to Kerberos V5 and GSS-API's
+ Kerberos V5 mechanism.
+
+
+
+
+Melnikov Standards Track [Page 2]
+
+RFC 4752 SASL GSSAPI Mechanism November 2006
+
+
+ The GSSAPI SASL mechanism is a "client goes first" SASL mechanism;
+ i.e., it starts with the client sending a "response" created as
+ described in the following section.
+
+ The implementation MAY set any GSS-API flags or arguments not
+ mentioned in this specification as is necessary for the
+ implementation to enforce its security policy.
+
+ Note that major status codes returned by GSS_Init_sec_context() or
+ GSS_Accept_sec_context() other than GSS_S_COMPLETE or
+ GSS_S_CONTINUE_NEEDED cause authentication failure. Major status
+ codes returned by GSS_Unwrap() other than GSS_S_COMPLETE (without any
+ additional supplementary status codes) cause authentication and/or
+ security layer failure.
+
+3.1. Client Side of Authentication Protocol Exchange
+
+ The client calls GSS_Init_sec_context, passing in
+ input_context_handle of 0 (initially), mech_type of the Kerberos V5
+ GSS-API mechanism [KRB5GSS], chan_binding of NULL, and targ_name
+ equal to output_name from GSS_Import_Name called with input_name_type
+ of GSS_C_NT_HOSTBASED_SERVICE (*) and input_name_string of
+ "service@hostname" where "service" is the service name specified in
+ the protocol's profile, and "hostname" is the fully qualified host
+ name of the server. When calling the GSS_Init_sec_context, the
+ client MUST pass the integ_req_flag of TRUE (**). If the client will
+ be requesting a security layer, it MUST also supply to the
+ GSS_Init_sec_context a mutual_req_flag of TRUE, and a
+ sequence_req_flag of TRUE. If the client will be requesting a
+ security layer providing confidentiality protection, it MUST also
+ supply to the GSS_Init_sec_context a conf_req_flag of TRUE. The
+ client then responds with the resulting output_token. If
+ GSS_Init_sec_context returns GSS_S_CONTINUE_NEEDED, then the client
+ should expect the server to issue a token in a subsequent challenge.
+ The client must pass the token to another call to
+ GSS_Init_sec_context, repeating the actions in this paragraph.
+
+ (*) Clients MAY use name types other than GSS_C_NT_HOSTBASED_SERVICE
+ to import servers' acceptor names, but only when they have a priori
+ knowledge that the servers support alternate name types. Otherwise
+ clients MUST use GSS_C_NT_HOSTBASED_SERVICE for importing acceptor
+ names.
+
+ (**) Note that RFC 2222 [RFC2222] implementations will not work with
+ GSS-API implementations that require integ_req_flag to be true. No
+ implementations of RFC 1964 [KRB5GSS] or RFC 4121 [RFC4121] that
+ require integ_req_flag to be true are believed to exist and it is
+ expected that any future update to [RFC4121] will require that
+
+
+
+Melnikov Standards Track [Page 3]
+
+RFC 4752 SASL GSSAPI Mechanism November 2006
+
+
+ integrity be available even in not explicitly requested by the
+ application.
+
+ When GSS_Init_sec_context returns GSS_S_COMPLETE, the client examines
+ the context to ensure that it provides a level of protection
+ permitted by the client's security policy. In particular, if the
+ integ_avail flag is not set in the context, then no security layer
+ can be offered or accepted.
+
+ If the conf_avail flag is not set in the context, then no security
+ layer with confidentiality can be offered or accepted. If the
+ context is acceptable, the client takes the following actions: If the
+ last call to GSS_Init_sec_context returned an output_token, then the
+ client responds with the output_token, otherwise the client responds
+ with no data. The client should then expect the server to issue a
+ token in a subsequent challenge. The client passes this token to
+ GSS_Unwrap and interprets the first octet of resulting cleartext as a
+ bit-mask specifying the security layers supported by the server and
+ the second through fourth octets as the maximum size output_message
+ the server is able to receive (in network byte order). If the
+ resulting cleartext is not 4 octets long, the client fails the
+ negotiation. The client verifies that the server maximum buffer is 0
+ if the server does not advertise support for any security layer.
+
+ The client then constructs data, with the first octet containing the
+ bit-mask specifying the selected security layer, the second through
+ fourth octets containing in network byte order the maximum size
+ output_message the client is able to receive (which MUST be 0 if the
+ client does not support any security layer), and the remaining octets
+ containing the UTF-8 [UTF8] encoded authorization identity.
+ (Implementation note: The authorization identity is not terminated
+ with the zero-valued (%x00) octet (e.g., the UTF-8 encoding of the
+ NUL (U+0000) character)). The client passes the data to GSS_Wrap
+ with conf_flag set to FALSE and responds with the generated
+ output_message. The client can then consider the server
+ authenticated.
+
+3.2. Server Side of Authentication Protocol Exchange
+
+ A server MUST NOT advertise support for the "GSSAPI" SASL mechanism
+ described in this document unless it has acceptor credential for the
+ Kerberos V GSS-API mechanism [KRB5GSS].
+
+ The server passes the initial client response to
+ GSS_Accept_sec_context as input_token, setting input_context_handle
+ to 0 (initially), chan_binding of NULL, and a suitable
+ acceptor_cred_handle (see below). If GSS_Accept_sec_context returns
+ GSS_S_CONTINUE_NEEDED, the server returns the generated output_token
+
+
+
+Melnikov Standards Track [Page 4]
+
+RFC 4752 SASL GSSAPI Mechanism November 2006
+
+
+ to the client in challenge and passes the resulting response to
+ another call to GSS_Accept_sec_context, repeating the actions in this
+ paragraph.
+
+ Servers SHOULD use a credential obtained by calling GSS_Acquire_cred
+ or GSS_Add_cred for the GSS_C_NO_NAME desired_name and the Object
+ Identifier (OID) of the Kerberos V5 GSS-API mechanism [KRB5GSS](*).
+ Servers MAY use GSS_C_NO_CREDENTIAL as an acceptor credential handle.
+ Servers MAY use a credential obtained by calling GSS_Acquire_cred or
+ GSS_Add_cred for the server's principal name(s) (**) and the Kerberos
+ V5 GSS-API mechanism [KRB5GSS].
+
+ (*) Unlike GSS_Add_cred the GSS_Acquire_cred uses an OID set of GSS-
+ API mechanism as an input parameter. The OID set can be created by
+ using GSS_Create_empty_OID_set and GSS_Add_OID_set_member. It can be
+ freed by calling the GSS_Release_oid_set.
+
+
+ (**) Use of server's principal names having
+ GSS_C_NT_HOSTBASED_SERVICE name type and "service@hostname" format,
+ where "service" is the service name specified in the protocol's
+ profile, and "hostname" is the fully qualified host name of the
+ server, is RECOMMENDED. The server name is generated by calling
+ GSS_Import_name with input_name_type of GSS_C_NT_HOSTBASED_SERVICE
+ and input_name_string of "service@hostname".
+
+ Upon successful establishment of the security context (i.e.,
+ GSS_Accept_sec_context returns GSS_S_COMPLETE), the server SHOULD
+ verify that the negotiated GSS-API mechanism is indeed Kerberos V5
+ [KRB5GSS]. This is done by examining the value of the mech_type
+ parameter returned from the GSS_Accept_sec_context call. If the
+ value differs, SASL authentication MUST be aborted.
+
+ Upon successful establishment of the security context and if the
+ server used GSS_C_NO_NAME/GSS_C_NO_CREDENTIAL to create acceptor
+ credential handle, the server SHOULD also check using the
+ GSS_Inquire_context that the target_name used by the client matches
+ either
+
+ - the GSS_C_NT_HOSTBASED_SERVICE "service@hostname" name syntax,
+ where "service" is the service name specified in the application
+ protocol's profile,
+
+ or
+
+ - the GSS_KRB5_NT_PRINCIPAL_NAME [KRB5GSS] name syntax for a two-
+ component principal where the first component matches the service
+ name specified in the application protocol's profile.
+
+
+
+Melnikov Standards Track [Page 5]
+
+RFC 4752 SASL GSSAPI Mechanism November 2006
+
+
+ When GSS_Accept_sec_context returns GSS_S_COMPLETE, the server
+ examines the context to ensure that it provides a level of protection
+ permitted by the server's security policy. In particular, if the
+ integ_avail flag is not set in the context, then no security layer
+ can be offered or accepted. If the conf_avail flag is not set in the
+ context, then no security layer with confidentiality can be offered
+ or accepted.
+
+ If the context is acceptable, the server takes the following actions:
+ If the last call to GSS_Accept_sec_context returned an output_token,
+ the server returns it to the client in a challenge and expects a
+ reply from the client with no data. Whether or not an output_token
+ was returned (and after receipt of any response from the client to
+ such an output_token), the server then constructs 4 octets of data,
+ with the first octet containing a bit-mask specifying the security
+ layers supported by the server and the second through fourth octets
+ containing in network byte order the maximum size output_token the
+ server is able to receive (which MUST be 0 if the server does not
+ support any security layer). The server must then pass the plaintext
+ to GSS_Wrap with conf_flag set to FALSE and issue the generated
+ output_message to the client in a challenge.
+
+ The server must then pass the resulting response to GSS_Unwrap and
+ interpret the first octet of resulting cleartext as the bit-mask for
+ the selected security layer, the second through fourth octets as the
+ maximum size output_message the client is able to receive (in network
+ byte order), and the remaining octets as the authorization identity.
+ The server verifies that the client has selected a security layer
+ that was offered and that the client maximum buffer is 0 if no
+ security layer was chosen. The server must verify that the src_name
+ is authorized to act as the authorization identity. After these
+ verifications, the authentication process is complete. The server is
+ not expected to return any additional data with the success
+ indicator.
+
+3.3. Security Layer
+
+ The security layers and their corresponding bit-masks are as follows:
+
+ 1 No security layer
+ 2 Integrity protection.
+ Sender calls GSS_Wrap with conf_flag set to FALSE
+ 4 Confidentiality protection.
+ Sender calls GSS_Wrap with conf_flag set to TRUE
+
+ Other bit-masks may be defined in the future; bits that are not
+ understood must be negotiated off.
+
+
+
+
+Melnikov Standards Track [Page 6]
+
+RFC 4752 SASL GSSAPI Mechanism November 2006
+
+
+ When decoding any received data with GSS_Unwrap, the major_status
+ other than the GSS_S_COMPLETE MUST be treated as a fatal error.
+
+ Note that SASL negotiates the maximum size of the output_message to
+ send. Implementations can use the GSS_Wrap_size_limit call to
+ determine the corresponding maximum size input_message.
+
+4. IANA Considerations
+
+ IANA modified the existing registration for "GSSAPI" as follows:
+
+ Family of SASL mechanisms: NO
+
+ SASL mechanism name: GSSAPI
+
+ Security considerations: See Section 5 of RFC 4752
+
+ Published specification: RFC 4752
+
+ Person & email address to contact for further information:
+ Alexey Melnikov <Alexey.Melnikov@isode.com>
+
+ Intended usage: COMMON
+
+ Owner/Change controller: iesg@ietf.org
+
+ Additional information: This mechanism is for the Kerberos V5
+ mechanism of GSS-API.
+
+5. Security Considerations
+
+ Security issues are discussed throughout this memo.
+
+ When constructing the input_name_string, the client SHOULD NOT
+ canonicalize the server's fully qualified domain name using an
+ insecure or untrusted directory service.
+
+ For compatibility with deployed software, this document requires that
+ the chan_binding (channel bindings) parameter to GSS_Init_sec_context
+ and GSS_Accept_sec_context be NULL, hence disallowing use of GSS-API
+ support for channel bindings. GSS-API channel bindings in SASL is
+ expected to be supported via a new GSS-API family of SASL mechanisms
+ (to be introduced in a future document).
+
+ Additional security considerations are in the [SASL] and [GSS-API]
+ specifications. Additional security considerations for the GSS-API
+ mechanism can be found in [KRB5GSS] and [KERBEROS].
+
+
+
+
+Melnikov Standards Track [Page 7]
+
+RFC 4752 SASL GSSAPI Mechanism November 2006
+
+
+6. Acknowledgements
+
+ This document replaces Section 7.2 of RFC 2222 [RFC2222] by John G.
+ Myers. He also contributed significantly to this revision.
+
+ Lawrence Greenfield converted text of this document to the XML
+ format.
+
+ Contributions of many members of the SASL mailing list are gratefully
+ acknowledged, in particular comments from Chris Newman, Nicolas
+ Williams, Jeffrey Hutzelman, Sam Hartman, Mark Crispin, and Martin
+ Rex.
+
+7. Changes since RFC 2222
+
+ RFC 2078 [RFC2078] specifies the version of GSS-API used by RFC 2222
+ [RFC2222], which provided the original version of this specification.
+ That version of GSS-API did not provide the integ_integ_avail flag as
+ an input to GSS_Init_sec_context. Instead, integrity was always
+ requested. RFC 4422 [SASL] requires that when possible, the security
+ layer negotiation be integrity protected. To meet this requirement
+ and as part of moving from RFC 2078 [RFC2078] to RFC 2743 [GSS-API],
+ this specification requires that clients request integrity from
+ GSS_Init_sec_context so they can use GSS_Wrap to protect the security
+ layer negotiation. This specification does not require that the
+ mechanism offer the integrity security layer, simply that the
+ security layer negotiation be wrapped.
+
+8. References
+
+8.1. Normative References
+
+ [GSS-API] Linn, J., "Generic Security Service Application Program
+ Interface Version 2, Update 1", RFC 2743, January 2000.
+
+ [KERBEROS] Neuman, C., Yu, T., Hartman, S., and K. Raeburn, "The
+ Kerberos Network Authentication Service (V5)", RFC 4120,
+ July 2005.
+
+ [KEYWORDS] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [KRB5GSS] Linn, J., "The Kerberos Version 5 GSS-API Mechanism", RFC
+ 1964, June 1996.
+
+
+
+
+
+
+
+Melnikov Standards Track [Page 8]
+
+RFC 4752 SASL GSSAPI Mechanism November 2006
+
+
+ [RFC4121] Zhu, L., Jaganathan, K., and S. Hartman, "The Kerberos
+ Version 5 Generic Security Service Application Program
+ Interface (GSS-API) Mechanism: Version 2", RFC 4121, July
+ 2005.
+
+ [SASL] Melnikov, A. and K. Zeilenga, "Simple Authentication and
+ Security Layer (SASL)", RFC 4422, June 2006.
+
+ [UTF8] Yergeau, F., "UTF-8, a transformation format of ISO
+ 10646", STD 63, RFC 3629, November 2003.
+
+8.2. Informative References
+
+ [RFC2078] Linn, J., "Generic Security Service Application Program
+ Interface, Version 2", RFC 2078, January 1997.
+
+ [RFC2222] Myers, J., "Simple Authentication and Security Layer
+ (SASL)", RFC 2222, October 1997.
+
+Editor's Address
+
+ Alexey Melnikov
+ Isode Limited
+ 5 Castle Business Village
+ 36 Station Road
+ Hampton, Middlesex TW12 2BX
+ UK
+
+ EMail: Alexey.Melnikov@isode.com
+ URI: http://www.melnikov.ca/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Melnikov Standards Track [Page 9]
+
+RFC 4752 SASL GSSAPI Mechanism November 2006
+
+
+Full Copyright Statement
+
+ Copyright (C) The IETF Trust (2006).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY, THE IETF TRUST,
+ AND THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT
+ THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY
+ IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR
+ PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+Melnikov Standards Track [Page 10]
+
diff --git a/imap/docs/rfc/rfc4790.txt b/imap/docs/rfc/rfc4790.txt
new file mode 100644
index 00000000..d58191c0
--- /dev/null
+++ b/imap/docs/rfc/rfc4790.txt
@@ -0,0 +1,1459 @@
+
+
+
+
+
+
+Network Working Group C. Newman
+Request for Comments: 4790 Sun Microsystems
+Category: Standards Track M. Duerst
+ Aoyama Gakuin University
+ A. Gulbrandsen
+ Oryx
+ March 2007
+
+
+ Internet Application Protocol Collation Registry
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The IETF Trust (2007).
+
+Abstract
+
+ Many Internet application protocols include string-based lookup,
+ searching, or sorting operations. However, the problem space for
+ searching and sorting international strings is large, not fully
+ explored, and is outside the area of expertise for the Internet
+ Engineering Task Force (IETF). Rather than attempt to solve such a
+ large problem, this specification creates an abstraction framework so
+ that application protocols can precisely identify a comparison
+ function, and the repertoire of comparison functions can be extended
+ in the future.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Newman, et al. Standards Track [Page 1]
+
+RFC 4790 Collation Registry March 2007
+
+
+Table of Contents
+
+ 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 4
+ 1.1. Conventions Used in This Document . . . . . . . . . . . . 4
+ 2. Collation Definition and Purpose . . . . . . . . . . . . . . . 4
+ 2.1. Definition . . . . . . . . . . . . . . . . . . . . . . . . 4
+ 2.2. Purpose . . . . . . . . . . . . . . . . . . . . . . . . . 4
+ 2.3. Some Other Terms Used in this Document . . . . . . . . . . 5
+ 2.4. Sort Keys . . . . . . . . . . . . . . . . . . . . . . . . 5
+ 3. Collation Identifier Syntax . . . . . . . . . . . . . . . . . 6
+ 3.1. Basic Syntax . . . . . . . . . . . . . . . . . . . . . . . 6
+ 3.2. Wildcards . . . . . . . . . . . . . . . . . . . . . . . . 6
+ 3.3. Ordering Direction . . . . . . . . . . . . . . . . . . . . 7
+ 3.4. URIs . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
+ 3.5. Naming Guidelines . . . . . . . . . . . . . . . . . . . . 7
+ 4. Collation Specification Requirements . . . . . . . . . . . . . 8
+ 4.1. Collation/Server Interface . . . . . . . . . . . . . . . . 8
+ 4.2. Operations Supported . . . . . . . . . . . . . . . . . . . 8
+ 4.2.1. Validity . . . . . . . . . . . . . . . . . . . . . . . 9
+ 4.2.2. Equality . . . . . . . . . . . . . . . . . . . . . . . 9
+ 4.2.3. Substring . . . . . . . . . . . . . . . . . . . . . . 9
+ 4.2.4. Ordering . . . . . . . . . . . . . . . . . . . . . . . 10
+ 4.3. Sort Keys . . . . . . . . . . . . . . . . . . . . . . . . 10
+ 4.4. Use of Lookup Tables . . . . . . . . . . . . . . . . . . . 11
+ 5. Application Protocol Requirements . . . . . . . . . . . . . . 11
+ 5.1. Character Encoding . . . . . . . . . . . . . . . . . . . . 11
+ 5.2. Operations . . . . . . . . . . . . . . . . . . . . . . . . 11
+ 5.3. Wildcards . . . . . . . . . . . . . . . . . . . . . . . . 12
+ 5.4. String Comparison . . . . . . . . . . . . . . . . . . . . 12
+ 5.5. Disconnected Clients . . . . . . . . . . . . . . . . . . . 12
+ 5.6. Error Codes . . . . . . . . . . . . . . . . . . . . . . . 13
+ 5.7. Octet Collation . . . . . . . . . . . . . . . . . . . . . 13
+ 6. Use by Existing Protocols . . . . . . . . . . . . . . . . . . 13
+ 7. Collation Registration . . . . . . . . . . . . . . . . . . . . 14
+ 7.1. Collation Registration Procedure . . . . . . . . . . . . . 14
+ 7.2. Collation Registration Format . . . . . . . . . . . . . . 15
+ 7.2.1. Registration Template . . . . . . . . . . . . . . . . 15
+ 7.2.2. The Collation Element . . . . . . . . . . . . . . . . 15
+ 7.2.3. The Identifier Element . . . . . . . . . . . . . . . . 16
+ 7.2.4. The Title Element . . . . . . . . . . . . . . . . . . 16
+ 7.2.5. The Operations Element . . . . . . . . . . . . . . . . 16
+ 7.2.6. The Specification Element . . . . . . . . . . . . . . 16
+ 7.2.7. The Submitter Element . . . . . . . . . . . . . . . . 16
+ 7.2.8. The Owner Element . . . . . . . . . . . . . . . . . . 16
+ 7.2.9. The Version Element . . . . . . . . . . . . . . . . . 17
+ 7.2.10. The Variable Element . . . . . . . . . . . . . . . . . 17
+ 7.3. Structure of Collation Registry . . . . . . . . . . . . . 17
+ 7.4. Example Initial Registry Summary . . . . . . . . . . . . . 18
+
+
+
+Newman, et al. Standards Track [Page 2]
+
+RFC 4790 Collation Registry March 2007
+
+
+ 8. Guidelines for Expert Reviewer . . . . . . . . . . . . . . . . 18
+ 9. Initial Collations . . . . . . . . . . . . . . . . . . . . . . 19
+ 9.1. ASCII Numeric Collation . . . . . . . . . . . . . . . . . 20
+ 9.1.1. ASCII Numeric Collation Description . . . . . . . . . 20
+ 9.1.2. ASCII Numeric Collation Registration . . . . . . . . . 20
+ 9.2. ASCII Casemap Collation . . . . . . . . . . . . . . . . . 21
+ 9.2.1. ASCII Casemap Collation Description . . . . . . . . . 21
+ 9.2.2. ASCII Casemap Collation Registration . . . . . . . . . 22
+ 9.3. Octet Collation . . . . . . . . . . . . . . . . . . . . . 22
+ 9.3.1. Octet Collation Description . . . . . . . . . . . . . 22
+ 9.3.2. Octet Collation Registration . . . . . . . . . . . . . 23
+ 10. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 23
+ 11. Security Considerations . . . . . . . . . . . . . . . . . . . 23
+ 12. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . 23
+ 13. References . . . . . . . . . . . . . . . . . . . . . . . . . . 24
+ 13.1. Normative References . . . . . . . . . . . . . . . . . . . 24
+ 13.2. Informative References . . . . . . . . . . . . . . . . . . 24
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Newman, et al. Standards Track [Page 3]
+
+RFC 4790 Collation Registry March 2007
+
+
+1. Introduction
+
+ The Application Configuration Access Protocol ACAP [11] specification
+ introduced the concept of a comparator (which we call collation in
+ this document), but failed to create an IANA registry. With the
+ introduction of stringprep [6] and the Unicode Collation Algorithm
+ [7], it is now time to create that registry and populate it with some
+ initial values appropriate for an international community. This
+ specification replaces and generalizes the definition of a comparator
+ in ACAP, and creates a collation registry.
+
+1.1. Conventions Used in This Document
+
+ The key words "MUST", "MUST NOT", "SHOULD", "SHOULD NOT", and "MAY"
+ in this document are to be interpreted as defined in "Key words for
+ use in RFCs to Indicate Requirement Levels" [1].
+
+ The attribute syntax specifications use the Augmented Backus-Naur
+ Form (ABNF) [2] notation, including the core rules defined in
+ Appendix A. The ABNF production "Language-tag" is imported from
+ Language Tags [5] and "reg-name" from URI: Generic Syntax [4].
+
+2. Collation Definition and Purpose
+
+2.1. Definition
+
+ A collation is a named function which takes two arbitrary length
+ strings as input and can be used to perform one or more of three
+ basic comparison operations: equality test, substring match, and
+ ordering test.
+
+2.2. Purpose
+
+ Collations are an abstraction for comparison functions so that these
+ comparison functions can be used in multiple protocols. The details
+ of a particular comparison operation can be specified by someone with
+ appropriate expertise, independent of the application protocols that
+ use that collation. This is similar to the way a charset [13]
+ separates the details of octet to character mapping from a protocol
+ specification, such as MIME [9], or the way SASL [10] separates the
+ details of an authentication mechanism from a protocol specification,
+ such as ACAP [11].
+
+
+
+
+
+
+
+
+
+Newman, et al. Standards Track [Page 4]
+
+RFC 4790 Collation Registry March 2007
+
+
+ Here is a small diagram to help illustrate the value of this
+ abstraction:
+
+ +-------------------+ +-----------------+
+ | IMAP i18n SEARCH |--+ | Basic |
+ +-------------------+ | +--| Collation Spec |
+ | | +-----------------+
+ +-------------------+ | +-------------+ | +-----------------+
+ | ACAP i18n SEARCH |--+--| Collation |--+--| A stringprep |
+ +-------------------+ | | Registry | | | Collation Spec |
+ | +-------------+ | +-----------------+
+ +-------------------+ | | +-----------------+
+ | ...other protocol |--+ | | locale-specific |
+ +-------------------+ +--| Collation Spec |
+ +-----------------+
+
+ Thus IMAP, ACAP, and future application protocols with international
+ search capability simply specify how to interface to the collation
+ registry instead of each protocol specification having to specify all
+ the collations it supports.
+
+2.3. Some Other Terms Used in this Document
+
+ The terms client, server, and protocol are used in somewhat unusual
+ senses.
+
+ Client means a user, or a program acting directly on behalf of a
+ user. This may be a mail reader acting as an IMAP client, or it may
+ be an interactive shell, where the user can type protocol commands/
+ requests directly, or it may be a script or program written by the
+ user.
+
+ Server means a program that performs services requested by the
+ client. This may be a traditional server such as an HTTP server, or
+ it may be a Sieve [14] interpreter running a Sieve script written by
+ a user. A server needs to use the operations provided by collations
+ in order to fulfill the client's requests.
+
+ The protocol describes how the client tells the server what it wants
+ done, and (if applicable) how the server tells the client about the
+ results. IMAP is a protocol by this definition, and so is the Sieve
+ language.
+
+2.4. Sort Keys
+
+ One component of a collation is a transformation, which turns a
+ string into a sort key, which is then used while sorting.
+
+
+
+
+Newman, et al. Standards Track [Page 5]
+
+RFC 4790 Collation Registry March 2007
+
+
+ The transformation can range from an identity mapping (e.g., the
+ i;octet collation Section 9.3) to a mapping that makes the string
+ unreadable to a human.
+
+ This is an implementation detail of collations or servers. A
+ protocol SHOULD NOT expose it to clients, since some collations leave
+ the sort key's format up to the implementation, and current
+ conformant implementations are known to use different formats.
+
+3. Collation Identifier Syntax
+
+3.1. Basic Syntax
+
+ The collation identifier itself is a single US-ASCII string. The
+ identifier MUST NOT be longer than 254 characters, and obeys the
+ following grammar:
+
+ collation-char = ALPHA / DIGIT / "-" / ";" / "=" / "."
+
+ collation-id = collation-prefix ";" collation-core-name
+ *collation-arg
+
+ collation-scope = Language-tag / "vnd-" reg-name
+
+ collation-core-name = ALPHA *( ALPHA / DIGIT / "-" )
+
+ collation-arg = ";" ALPHA *( ALPHA / DIGIT ) "="
+ 1*( ALPHA / DIGIT / "." )
+
+
+ Note: the ABNF production "Language-tag" is imported from Language
+ Tags [5] and "reg-name" from URI: Generic Syntax [4].
+
+ There is a special identifier called "default". For protocols that
+ have a default collation, "default" refers to that collation. For
+ other protocols, the identifier "default" MUST match no collations,
+ and servers SHOULD treat it in the same way as they treat nonexistent
+ collations.
+
+3.2. Wildcards
+
+ The string a client uses to select a collation MAY contain one or
+ more wildcard ("*") characters that match zero or more collation-
+ chars. Wildcard characters MUST NOT be adjacent. If the wildcard
+ string matches multiple collations, the server SHOULD attempt to
+ select a widely useful collation in preference to a narrowly useful
+ one.
+
+
+
+
+Newman, et al. Standards Track [Page 6]
+
+RFC 4790 Collation Registry March 2007
+
+
+ collation-wild = ("*" / (ALPHA ["*"])) *(collation-char ["*"])
+ ; MUST NOT exceed 254 characters total
+
+3.3. Ordering Direction
+
+ When used as a protocol element for ordering, the collation
+ identifier MAY be prefixed by either "+" or "-" to explicitly specify
+ an ordering direction. "+" has no effect on the ordering operation,
+ while "-" inverts the result of the ordering operation. In general,
+ collation-order is used when a client requests a collation, and
+ collation-selected is used when the server informs the client of the
+ selected collation.
+
+ collation-selected = ["+" / "-"] collation-id
+
+ collation-order = ["+" / "-"] collation-wild
+
+3.4. URIs
+
+ Some protocols are designed to use URIs [4] to refer to collations
+ rather than simple tokens. A special section of the IANA URL space
+ is reserved for such usage. The "collation-uri" form is used to
+ refer to a specific named collation (the collation registration may
+ not actually be present). The "collation-auri" form is an abstract
+ name for an ordering, a collation pattern or a vendor private
+ collator.
+
+ collation-uri = "http://www.iana.org/assignments/collation/"
+ collation-id ".xml"
+
+ collation-auri = ( "http://www.iana.org/assignments/collation/"
+ collation-order ".xml" ) / other-uri
+
+ other-uri = <absoluteURI>
+ ; excluding the IANA collation namespace.
+
+3.5. Naming Guidelines
+
+ While this specification makes no absolute requirements on the
+ structure of collation identifiers, naming consistency is important,
+ so the following initial guidelines are provided.
+
+ Collation identifiers with an international audience typically begin
+ with "i;". Collation identifiers intended for a particular language
+ or locale typically begin with a language tag [5] followed by a ";".
+ After the first ";" is normally the name of the general collation
+ algorithm, followed by a series of algorithm modifications separated
+ by the ";" delimiter. Parameterized modifications will use "=" to
+
+
+
+Newman, et al. Standards Track [Page 7]
+
+RFC 4790 Collation Registry March 2007
+
+
+ delimit the parameter from the value. The version numbers of any
+ lookup tables used by the algorithm SHOULD be present as
+ parameterized modifications.
+
+ Collation identifiers of the form *;vnd-hostname;* are reserved for
+ vendor-specific collations created by the owner of the hostname
+ following the "vnd-" prefix (e.g., vnd-example.com for the vendor
+ example.com). Registration of such collations (or the name space as
+ a whole), with intended use of the "Vendor", is encouraged when a
+ public specification or open-source implementation is available, but
+ is not required.
+
+4. Collation Specification Requirements
+
+4.1. Collation/Server Interface
+
+ The collation itself defines what it operates on. Most collations
+ are expected to operate on character strings. The i;octet
+ (Section 9.3) collation operates on octet strings. The i;ascii-
+ numeric (Section 9.1) operation operates on numbers.
+
+ This specification defines the collation interface in terms of octet
+ strings. However, implementations may choose to use character
+ strings instead. Such implementations may not be able to implement
+ e.g., i;octet. Since i;octet is not currently mandatory to implement
+ for any protocol, this should not be a problem.
+
+4.2. Operations Supported
+
+ A collation specification MUST state which of the three basic
+ operations are supported (equality, substring, ordering) and how to
+ perform each of the supported operations on any two input character
+ strings, including empty strings. Collations must be deterministic,
+ i.e., given a collation with a specific identifier, and any two fixed
+ input strings, the result MUST be the same for the same operation.
+
+ In general, collation operations should behave as their names
+ suggest. While a collation may be new, the operations are not, so
+ the new collation's operations should be similar to those of older
+ collations. For example, a date/time collation should not provide a
+ "substring" operation that would morph IMAP substring SEARCH into
+ e.g., a date-range search.
+
+ A non-obvious consequence of the rules for each collation operation
+ is that, for any single collation, either none or all of the
+ operations can return "undefined". For example, it is not possible
+ to have an equality operation that never returns "undefined", and a
+ substring operation that occasionally does.
+
+
+
+Newman, et al. Standards Track [Page 8]
+
+RFC 4790 Collation Registry March 2007
+
+
+4.2.1. Validity
+
+ The validity test takes one string as argument. It returns valid if
+ its input string is a valid input to the collation's other
+ operations, and invalid if not. (In other words, a string is valid
+ if it is equal to itself according to the collation's equality
+ operation.)
+
+ The validity test is provided by all collations. It MUST NOT be
+ listed separately in the collation registration.
+
+4.2.2. Equality
+
+ The equality test always returns "match" or "no-match" when it is
+ supplied valid input, and MAY return "undefined" if one or both input
+ strings are not valid.
+
+ The equality test MUST be reflexive and symmetric. For valid input,
+ it MUST be transitive.
+
+ If a collation provides either a substring or an ordering test, it
+ MUST also provide an equality test. The substring and/or ordering
+ tests MUST be consistent with the equality test.
+
+ The return values of the equality test are called "match", "no-match"
+ and "undefined" in this document.
+
+4.2.3. Substring
+
+ The substring matching operation determines if the first string is a
+ substring of the second string, i.e., if one or more substrings of
+ the second string is equal to the first, as defined by the
+ collation's equality operation.
+
+ A collation that supports substring matching will automatically
+ support two special cases of substring matching: prefix and suffix
+ matching, if those special cases are supported by the application
+ protocol. It returns "match" or "no-match" when it is supplied valid
+ input and returns "undefined" when supplied invalid input.
+
+ Application protocols MAY return position information for substring
+ matches. If this is done, the position information SHOULD include
+ both the starting offset and the ending offset for each match. This
+ is important because more sophisticated collations can match strings
+ of unequal length (for example, a pre-composed accented character can
+ match a decomposed accented character). In general, overlapping
+ matches SHOULD be reported (as when "ana" occurs twice within
+ "banana"), although there are cases where a collation may decide not
+
+
+
+Newman, et al. Standards Track [Page 9]
+
+RFC 4790 Collation Registry March 2007
+
+
+ to. For example, in a collation which treats all whitespace
+ sequences as identical, the substring operation could be defined such
+ that " 1 " (SP "1" SP) is reported just once within " 1 " (SP SP
+ "1" SP SP), not four times (SP SP "1" SP, SP "1" SP, SP "1" SP SP and
+ SP SP "1" SP SP), since the four matches are, in a sense, the same
+ match.
+
+ A string is a substring of itself. The empty string is a substring
+ of all strings.
+
+ Note that the substring operation of some collations can match
+ strings of unequal length. For example, a pre-composed accented
+ character can match a decomposed accented character. The Unicode
+ Collation Algorithm [7] discusses this in more detail.
+
+ The return values of the substring operation are called "match", "no-
+ match", and "undefined" in this document.
+
+4.2.4. Ordering
+
+ The ordering operation determines how two strings are ordered. It
+ MUST be reflexive. For valid input, it MUST be transitive and
+ trichotomous.
+
+ Ordering returns "less" if the first string is listed before the
+ second string, according to the collation; "greater", if the second
+ string is listed before the first string; and "equal", if the two
+ strings are equal, as defined by the collation's equality operation.
+ If one or both strings are invalid, the result of ordering is
+ "undefined".
+
+ When the collation is used with a "+" prefix, the behavior is the
+ same as when used with no prefix. When the collation is used with a
+ "-" prefix, the result of the ordering operation of the collation
+ MUST be reversed.
+
+ The return values of the ordering operation are called "less",
+ "equal", "greater", and "undefined" in this document.
+
+4.3. Sort Keys
+
+ A collation specification SHOULD describe the internal transformation
+ algorithm to generate sort keys. This algorithm can be applied to
+ individual strings, and the result can be stored to potentially
+ optimize future comparison operations. A collation MAY specify that
+ the sort key is generated by the identity function. The sort key may
+ have no meaning to a human. The sort key may not be valid input to
+ the collation.
+
+
+
+Newman, et al. Standards Track [Page 10]
+
+RFC 4790 Collation Registry March 2007
+
+
+4.4. Use of Lookup Tables
+
+ Some collations use customizable lookup tables, e.g., because the
+ tables depend on locale, and may be modified after shipping the
+ software. Collations that use more than one customizable lookup
+ table in a documented format MUST assign numbers to the tables they
+ use. This permits an application protocol command to access the
+ tables used by a server collation, so that clients and servers use
+ the same tables.
+
+5. Application Protocol Requirements
+
+ This section describes the requirements and issues that an
+ application protocol needs to consider if it offers searching,
+ substring matching and/or sorting, and permits the use of characters
+ outside the US-ASCII charset.
+
+5.1. Character Encoding
+
+ The protocol specification has to make sure that it is clear on which
+ characters (rather than just octets) the collations are used. This
+ can be done by specifying the protocol itself in terms of characters
+ (e.g., in the case of a query language), by specifying a single
+ character encoding for the protocol (e.g., UTF-8 [3]), or by
+ carefully describing the relevant issues of character encoding
+ labeling and conversion. In the later case, details to consider
+ include how to handle unknown charsets, any charsets that are
+ mandatory-to-implement, any issues with byte-order that might apply,
+ and any transfer encodings that need to be supported.
+
+5.2. Operations
+
+ The protocol must specify which of the operations defined in this
+ specification (equality matching, substring matching, and ordering)
+ can be invoked in the protocol, and how they are invoked. There may
+ be more than one way to invoke an operation.
+
+ The protocol MUST provide a mechanism for the client to select the
+ collation to use with equality matching, substring matching, and
+ ordering.
+
+ If a protocol needs a total ordering and the collation chosen does
+ not provide it because the ordering operation returns "undefined" at
+ least once, the recommended fallback is to sort all invalid strings
+ after the valid ones, and use i;octet to order the invalid strings.
+
+ Although the collation's substring function provides a list of
+ matches, a protocol need not provide all that to the client. It may
+
+
+
+Newman, et al. Standards Track [Page 11]
+
+RFC 4790 Collation Registry March 2007
+
+
+ provide only the first matching substring, or even just the
+ information that the substring search matched. In this way,
+ collations can be used with protocols that are defined such that "x
+ is a substring of y" returns true-false.
+
+ If the protocol provides positional information for the results of a
+ substring match, that positional information SHOULD fully specify the
+ substring(s) in the result that matches, independent of the length of
+ the search string. For example, returning both the starting and
+ ending offset of the match would suffice, as would the starting
+ offset and a length. Returning just the starting offset is not
+ acceptable. This rule is necessary because advanced collations can
+ treat strings of different lengths as equal (for example, pre-
+ composed and decomposed accented characters).
+
+5.3. Wildcards
+
+ The protocol MUST specify whether it allows the use of wildcards in
+ collation identifiers. If the protocol allows wildcards, then:
+ The protocol MUST specify how comparisons behave in the absence of
+ explicit collation negotiation, or when a collation of "default"
+ is requested. The protocol MAY specify that the default collation
+ used in such circumstances is sensitive to server configuration.
+
+ The protocol SHOULD provide a way to list available collations
+ matching a given wildcard pattern, or patterns.
+
+5.4. String Comparison
+
+ If a protocol compares strings in any nontrivial way, using a
+ collation may be appropriate. As an example, many protocols use
+ case-independent strings. In many cases, a simple ASCII mapping to
+ upper/lower case works well. In other cases, it may be better to use
+ a specifiable collation; for example, so that a server can treat "i"
+ and "I" as equivalent in Italy, and different in Turkey (Turkish also
+ has a dotted upper-case" I" and a dotless lower-case "i").
+
+ Protocol designers should consider, in each case, whether to use a
+ specifiable collation. Keywords often have other needs than user
+ variables, and search arguments may be different again.
+
+5.5. Disconnected Clients
+
+ If the protocol supports disconnected clients, and a collation is
+ used that can use configurable tables (e.g., to support
+ locale-specific extensions), then the client may not be able to
+ reproduce the server's collation operations while offline.
+
+
+
+
+Newman, et al. Standards Track [Page 12]
+
+RFC 4790 Collation Registry March 2007
+
+
+ A mechanism to download such tables has been discussed. Such a
+ mechanism is not included in the present specification, since the
+ problem is not yet well understood.
+
+5.6. Error Codes
+
+ The protocol specification should consider assigning protocol error
+ codes for the following circumstances:
+
+ o The client requests the use of a collation by identifier or
+ pattern, but no implemented collation matches that pattern.
+
+ o The client attempts to use a collation for an operation that is
+ not supported by that collation -- for example, attempting to use
+ the "i;ascii-numeric" collation for substring matching.
+
+ o The client uses an equality or substring matching collation, and
+ the result is an error. It may be appropriate to distinguish
+ between the two input strings, particularly when one is supplied
+ by the client and the other is stored by the server. It might
+ also be appropriate to distinguish the specific case of an invalid
+ UTF-8 string.
+
+5.7. Octet Collation
+
+ The i;octet (Section 9.3) collation is only usable with protocols
+ based on octet-strings. Clients and servers MUST NOT use i;octet
+ with other protocols.
+
+ If the protocol permits the use of collations with data structures
+ other than strings, the protocol MUST describe the default behavior
+ for a collation with those data structures.
+
+6. Use by Existing Protocols
+
+ This section is informative.
+
+ Both ACAP [11] and Sieve [14] are standards track specifications that
+ used collations prior to the creation of this specification and
+ registry. Those standards do not meet all the application protocol
+ requirements described in Section 5.
+
+ These protocols allow the use of the i;octet (Section 9.3) collation
+ working directly on UTF-8 data, as used in these protocols.
+
+
+
+
+
+
+
+Newman, et al. Standards Track [Page 13]
+
+RFC 4790 Collation Registry March 2007
+
+
+ In Sieve, all matches are either true or false. Accordingly, Sieve
+ servers must treat "undefined" and "no-match" results of the equality
+ and substring operations as false, and only "match" as true.
+
+ In ACAP and Sieve, there are no invalid strings. In this document's
+ terms, invalid strings sort after valid strings.
+
+ IMAP [15] also collates, although that is explicit only when the
+ COMPARATOR [17] extension is used. The built-in IMAP substring
+ operation and the ordering provided by the SORT [16] extension may
+ not meet the requirements made in this document.
+
+ Other protocols may be in a similar position.
+
+ In IMAP, the default collation is i;ascii-casemap, because its
+ operations are understood to match IMAP's built-in operations.
+
+7. Collation Registration
+
+7.1. Collation Registration Procedure
+
+ The IETF will create a mailing list, collation@ietf.org, which can be
+ used for public discussion of collation proposals prior to
+ registration. Use of the mailing list is strongly encouraged. The
+ IESG will appoint a designated expert who will monitor the
+ collation@ietf.org mailing list and review registrations.
+
+ The registration procedure begins when a completed registration
+ template is sent to iana@iana.org and collation@ietf.org. The
+ designated expert is expected to tell IANA and the submitter of the
+ registration within two weeks whether the registration is approved,
+ approved with minor changes, or rejected with cause. When a
+ registration is rejected with cause, it can be re-submitted if the
+ concerns listed in the cause are addressed. Decisions made by the
+ designated expert can be appealed to the IESG Applications Area
+ Director, then to the IESG. They follow the normal appeals procedure
+ for IESG decisions.
+
+ Collation registrations in a standards track, BCP, or IESG-approved
+ experimental RFC are owned by the IETF, and changes to the
+ registration follow normal procedures for updating such documents.
+ Collation registrations in other RFCs are owned by the RFC author(s).
+ Other collation registrations are owned by the individual(s) listed
+ in the contact field of the registration, and IANA will preserve this
+ information.
+
+ If the registration is a change of an existing collation, it MUST be
+ approved by the owner. In the event the owner cannot be contacted
+
+
+
+Newman, et al. Standards Track [Page 14]
+
+RFC 4790 Collation Registry March 2007
+
+
+ for a period of one month, and the designated expert deems the change
+ necessary, the IESG MAY re-assign ownership to an appropriate party.
+
+7.2. Collation Registration Format
+
+ Registration of a collation is done by sending a well-formed XML
+ document to collation@ietf.org and iana@iana.org.
+
+7.2.1. Registration Template
+
+ Here is a template for the registration:
+
+ <?xml version='1.0'?>
+ <!DOCTYPE collation SYSTEM 'collationreg.dtd'>
+ <collation rfc="YYYY" scope="global" intendedUse="common">
+ <identifier>collation identifier</identifier>
+ <title>technical title for collation</title>
+ <operations>equality order substring</operations>
+ <specification>specification reference</specification>
+ <owner>email address of owner or IETF</owner>
+ <submitter>email address of submitter</submitter>
+ <version>1</version>
+ </collation>
+
+7.2.2. The Collation Element
+
+ The root of the registration document MUST be a <collation> element.
+ The collation element contains the other elements in the
+ registration, which are described in the following sub-subsections,
+ in the order given here.
+
+ The <collation> element MAY include an "rfc=" attribute if the
+ specification is in an RFC. The "rfc=" attribute gives only the
+ number of the RFC, without any prefix, such as "RFC", or suffix, such
+ as ".txt".
+
+ The <collation> element MUST include a "scope=" attribute, which MUST
+ have one of the values "global", "local", or "other".
+
+ The <collation> element MUST include an "intendedUse=" attribute,
+ which must have one of the values "common", "limited", "vendor", or
+ "deprecated". Collation specifications intended for "common" use are
+ expected to reference standards from standards bodies with
+ significant experience dealing with the details of international
+ character sets.
+
+ Be aware that future revisions of this specification may add
+ additional function types, as well as additional XML attributes,
+
+
+
+Newman, et al. Standards Track [Page 15]
+
+RFC 4790 Collation Registry March 2007
+
+
+ values, and elements. Any system that automatically parses these XML
+ documents MUST take this into account to preserve future
+ compatibility.
+
+7.2.3. The Identifier Element
+
+ The <identifier> element gives the precise identifier of the
+ collation, e.g., i;ascii-casemap. The <identifier> element is
+ mandatory.
+
+7.2.4. The Title Element
+
+ The <title> element gives the title of the collation. The <title>
+ element is mandatory.
+
+7.2.5. The Operations Element
+
+ The <operations> element lists which of the three operations
+ ("equality", "order" or "substring") the collation provides,
+ separated by single spaces. The <operations> element is mandatory.
+
+7.2.6. The Specification Element
+
+ The <specification> element describes where to find the
+ specification. The <specification> element is mandatory. It MAY
+ have a URI attribute. There may be more than one <specification>
+ element, in which case, they together form the specification.
+
+ If it is discovered that parts of a collation specification conflict,
+ a new revision of the collation is necessary, and the
+ collation@ietf.org mailing list should be notified.
+
+7.2.7. The Submitter Element
+
+ The <submitter> element provides an RFC 2822 [12] email address for
+ the person who submitted the registration. It is optional if the
+ <owner> element contains an email address.
+
+ There may be more than one <submitter> element.
+
+7.2.8. The Owner Element
+
+ The <owner> element contains either the four letters "IETF" or an
+ email address of the owner of the registration. The <owner> element
+ is mandatory. There may be more than one <owner> element. If so,
+ all owners are equal. Each owner can speak for all.
+
+
+
+
+
+Newman, et al. Standards Track [Page 16]
+
+RFC 4790 Collation Registry March 2007
+
+
+7.2.9. The Version Element
+
+ The <version> element MUST be included when the registration is
+ likely to be revised, or has been revised in such a way that the
+ results change for one or more input strings. The <version> element
+ is optional.
+
+7.2.10. The Variable Element
+
+ The <variable> element specifies an optional variable to control the
+ collation's behaviour, for example whether it is case sensitive. The
+ <variable> element is optional. When <variable> is used, it must
+ contain <name> and <default> elements, and it may contain one or more
+ <value> elements.
+
+7.2.10.1. The Name Element
+
+ The <name> element specifies the name value of a variable. The
+ <name> element is mandatory.
+
+7.2.10.2. The Default Element
+
+ The <default> element specifies the default value of a variable. The
+ <default> element is mandatory.
+
+7.2.10.3. The Value Element
+
+ The <value> element specifies a legal value of a variable. The
+ <value> element is optional. If one or more <value> elements are
+ present, only those values are legal. If none are, then the
+ variable's legal values do not form an enumerated set, and the rules
+ MUST be specified in an RFC accompanying the registration.
+
+7.3. Structure of Collation Registry
+
+ Once the registration is approved, IANA will store each XML
+ registration document in a URL of the form
+ http://www.iana.org/assignments/collation/collation-id.xml, where
+ collation-id is the content of the identifier element in the
+ registration. Both the submitter and the designated expert are
+ responsible for verifying that the XML is well-formed. The
+ registration document should avoid using new elements. If any are
+ necessary, it is important to be consistent with other registrations.
+
+ IANA will also maintain a text summary of the registry under the name
+ http://www.iana.org/assignments/collation/collation-index.html. This
+ summary is divided into four sections. The first section is for
+ collations intended for common use. This section is intended for
+
+
+
+Newman, et al. Standards Track [Page 17]
+
+RFC 4790 Collation Registry March 2007
+
+
+ collation registrations published in IESG-approved RFCs, or for
+ locally scoped collations from the primary standards body for that
+ locale. The designated expert is encouraged to reject collation
+ registrations with an intended use of "common" if the expert believes
+ it should be "limited", as it is desirable to keep the number of
+ "common" registrations small and of high quality. The second section
+ is reserved for limited-use collations. The third section is
+ reserved for registered vendor-specific collations. The final
+ section is reserved for deprecated collations.
+
+7.4. Example Initial Registry Summary
+
+ The following is an example of how IANA might structure the initial
+ registry summary.html file:
+
+ Collation Functions Scope Reference
+ --------- --------- ----- ---------
+ Common Use Collations:
+ i;ascii-casemap e, o, s Local [RFC 4790]
+
+ Limited Use Collations:
+ i;octet e, o, s Other [RFC 4790]
+ i;ascii-numeric e, o Other [RFC 4790]
+
+ Vendor Collations:
+
+ Deprecated Collations:
+
+
+ References
+ ----------
+ [RFC 4790] Newman, C., Duerst, M., Gulbrandsen, A., "Internet
+ Application Protocol Collation Registry", RFC 4790,
+ Sun Microsystems, March 2007.
+
+8. Guidelines for Expert Reviewer
+
+ The expert reviewer appointed by the IESG has fairly broad latitude
+ for this registry. While a number of collations are expected
+ (particularly customizations of the UCA for localized use), an
+ explosion of collations (particularly common-use collations) is not
+ desirable for widespread interoperability. However, it is important
+ for the expert reviewer to provide cause when rejecting a
+ registration, and, when possible, to describe corrective action to
+
+
+
+
+
+
+
+Newman, et al. Standards Track [Page 18]
+
+RFC 4790 Collation Registry March 2007
+
+
+ permit the registration to proceed. The following table includes
+ some example reasons to reject a registration with cause:
+
+ o The registration is not a well-formed XML document.
+
+ o The registration has an intended use of "common", but there is no
+ evidence the collation will be widely deployed, so it should be
+ listed as "limited".
+
+ o The registration has an intended use of "common", but it is
+ redundant with the functionality of a previously registered
+ "common" collation.
+
+ o The registration has an intended use of "common", but the
+ specification is not detailed enough to allow interoperable
+ implementations by others.
+
+ o The collation identifier fails to precisely identify the version
+ numbers of relevant tables to use.
+
+ o The registration fails to meet one of the "MUST" requirements in
+ Section 4.
+
+ o The collation identifier fails to meet the syntax in Section 3.
+
+ o The collation specification referenced in the registration is
+ vague or has optional features without a clear behavior specified.
+
+ o The referenced specification does not adequately address security
+ considerations specific to that collation.
+
+ o The registration's operations are needlessly different from those
+ of traditional operations.
+
+ o The registration's XML is needlessly different from that of
+ already registered collations.
+
+9. Initial Collations
+
+ This section registers the three collations that were originally
+ defined in [11], and are implemented in most [14] engines. Some of
+ the behavior of these collations is perhaps not ideal, such as
+ i;ascii-casemap accepting non-ASCII input. Compatibility with widely
+ deployed code was judged more important than fixing the collations.
+ Some of the aspects of these collations are necessary to maintain
+ compatibility with widely deployed code.
+
+
+
+
+
+Newman, et al. Standards Track [Page 19]
+
+RFC 4790 Collation Registry March 2007
+
+
+9.1. ASCII Numeric Collation
+
+9.1.1. ASCII Numeric Collation Description
+
+ The "i;ascii-numeric" collation is a simple collation intended for
+ use with arbitrarily-sized, unsigned decimal integer numbers stored
+ as octet strings. US-ASCII digits (0x30 to 0x39) represent digits of
+ the numbers. Before converting from string to integer, the input
+ string is truncated at the first non-digit character. All input is
+ valid; strings that do not start with a digit represent positive
+ infinity.
+
+ The collation supports equality and ordering, but does not support
+ the substring operation.
+
+ The equality operation returns "match" if the two strings represent
+ the same number (i.e., leading zeroes and trailing non-digits are
+ disregarded), and "no-match" if the two strings represent different
+ numbers.
+
+ The ordering operation returns "less" if the first string represents
+ a smaller number than the second, "equal" if they represent the same
+ number, and "greater" if the first string represents a larger number
+ than the second.
+
+ Some examples: "0" is less than "1", and "1" is less than
+ "4294967298". "4294967298", "04294967298", and "4294967298b" are all
+ equal. "04294967298" is less than "". "", "x", and "y" are equal.
+
+9.1.2. ASCII Numeric Collation Registration
+
+ <?xml version='1.0'?>
+ <!DOCTYPE collation SYSTEM 'collationreg.dtd'>
+ <collation rfc="4790" scope="other" intendedUse="limited">
+ <identifier>i;ascii-numeric</identifier>
+ <title>ASCII Numeric</title>
+ <operations>equality order</operations>
+ <specification>RFC 4790</specification>
+ <owner>IETF</owner>
+ <submitter>chris.newman@sun.com</submitter>
+ </collation>
+
+
+
+
+
+
+
+
+
+
+Newman, et al. Standards Track [Page 20]
+
+RFC 4790 Collation Registry March 2007
+
+
+9.2. ASCII Casemap Collation
+
+9.2.1. ASCII Casemap Collation Description
+
+ The "i;ascii-casemap" collation is a simple collation that operates
+ on octet strings and treats US-ASCII letters case-insensitively. It
+ provides equality, substring, and ordering operations. All input is
+ valid. Note that letters outside ASCII are not treated case-
+ insensitively.
+
+ Its equality, ordering, and substring operations are as for i;octet,
+ except that at first, the lower-case letters (octet values 97-122) in
+ each input string are changed to upper case (octet values 65-90).
+
+ Care should be taken when using OS-supplied functions to implement
+ this collation, as it is not locale sensitive. Functions, such as
+ strcasecmp and toupper, are sometimes locale sensitive, and may
+ inappropriately map lower-case letters other than a-z to upper case.
+
+ The i;ascii-casemap collation is well-suited for use with many
+ Internet protocols and computer languages. Use with natural language
+ is often inappropriate; even though the collation apparently supports
+ languages such as Swahili and English, in real-world use, it tends to
+ mis-sort a number of types of string:
+
+ o people and place names containing non-ASCII,
+
+ o words such as "naive" (if spelled with an accent, the accented
+ character could push the word to the wrong spot in a sorted list),
+
+ o names such as "Lloyd" (which, in Welsh, sorts after "Lyon", unlike
+ in English),
+
+ o strings containing euro and pound sterling symbols, quotation
+ marks other than '"', dashes/hyphens, etc.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Newman, et al. Standards Track [Page 21]
+
+RFC 4790 Collation Registry March 2007
+
+
+9.2.2. ASCII Casemap Collation Registration
+
+ <?xml version='1.0'?>
+ <!DOCTYPE collation SYSTEM 'collationreg.dtd'>
+ <collation rfc="4790" scope="local" intendedUse="common">
+ <identifier>i;ascii-casemap</identifier>
+ <title>ASCII Casemap</title>
+ <operations>equality order substring</operations>
+ <specification>RFC 4790</specification>
+ <owner>IETF</owner>
+ <submitter>chris.newman@sun.com</submitter>
+ </collation>
+
+9.3. Octet Collation
+
+9.3.1. Octet Collation Description
+
+ The "i;octet" collation is a simple and fast collation intended for
+ use on binary octet strings rather than on character data. Protocols
+ that want to make this collation available have to do so by
+ explicitly allowing it. If not explicitly allowed, it MUST NOT be
+ used. It never returns an "undefined" result. It provides equality,
+ substring, and ordering operations.
+
+ The ordering algorithm is as follows:
+
+ 1. If both strings are the empty string, return the result "equal".
+
+ 2. If the first string is empty and the second is not, return the
+ result "less".
+
+ 3. If the second string is empty and the first is not, return the
+ result "greater".
+
+ 4. If both strings begin with the same octet value, remove the first
+ octet from both strings and repeat this algorithm from step 1.
+
+ 5. If the unsigned value (0 to 255) of the first octet of the first
+ string is less than the unsigned value of the first octet of the
+ second string, then return "less".
+
+ 6. If this step is reached, return "greater".
+
+ This algorithm is roughly equivalent to the C library function
+ memcmp, with appropriate length checks added.
+
+
+
+
+
+
+Newman, et al. Standards Track [Page 22]
+
+RFC 4790 Collation Registry March 2007
+
+
+ The matching operation returns "match" if the sorting algorithm would
+ return "equal". Otherwise, the matching operation returns "no-
+ match".
+
+ The substring operation returns "match" if the first string is the
+ empty string, or if there exists a substring of the second string of
+ length equal to the length of the first string, which would result in
+ a "match" result from the equality function. Otherwise, the
+ substring operation returns "no-match".
+
+9.3.2. Octet Collation Registration
+
+ This collation is defined with intendedUse="limited" because it can
+ only be used by protocols that explicitly allow it.
+
+ <?xml version='1.0'?>
+ <!DOCTYPE collation SYSTEM 'collationreg.dtd'>
+ <collation rfc="4790" scope="global" intendedUse="limited">
+ <identifier>i;octet</identifier>
+ <title>Octet</title>
+ <operations>equality order substring</operations>
+ <specification>RFC 4790</specification>
+ <owner>IETF</owner>
+ <submitter>chris.newman@sun.com</submitter>
+ </collation>
+
+10. IANA Considerations
+
+ Section 7 defines how to register collations with IANA. Section 9
+ defines a list of predefined collations that have been registered
+ with IANA.
+
+11. Security Considerations
+
+ Collations will normally be used with UTF-8 strings. Thus, the
+ security considerations for UTF-8 [3], stringprep [6], and Unicode
+ TR-36 [8] also apply, and are normative to this specification.
+
+12. Acknowledgements
+
+ The authors want to thank all who have contributed to this document,
+ including Brian Carpenter, John Cowan, Dave Cridland, Mark Davis,
+ Spencer Dawkins, Lisa Dusseault, Lars Eggert, Frank Ellermann, Philip
+ Guenther, Tony Hansen, Ted Hardie, Sam Hartman, Kjetil Torgrim Homme,
+ Michael Kay, John Klensin, Alexey Melnikov, Jim Melton, and Abhijit
+ Menon-Sen.
+
+
+
+
+
+Newman, et al. Standards Track [Page 23]
+
+RFC 4790 Collation Registry March 2007
+
+
+13. References
+
+13.1. Normative References
+
+ [1] Bradner, S., "Key words for use in RFCs to Indicate Requirement
+ Levels", BCP 14, RFC 2119, March 1997.
+
+ [2] Crocker, D. and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", RFC 4234, October 2005.
+
+ [3] Yergeau, F., "UTF-8, a transformation format of ISO 10646",
+ STD 63, RFC 3629, November 2003.
+
+ [4] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform
+ Resource Identifier (URI): Generic Syntax", RFC 3986,
+ January 2005.
+
+ [5] Phillips, A. and M. Davis, "Tags for Identifying Languages",
+ BCP 47, RFC 4646, September 2006.
+
+ [6] Hoffman, P. and M. Blanchet, "Preparation of Internationalized
+ Strings ("stringprep")", RFC 3454, December 2002.
+
+ [7] Davis, M. and K. Whistler, "Unicode Collation Algorithm version
+ 14", May 2005,
+ <http://www.unicode.org/reports/tr10/tr10-14.html>.
+
+ [8] Davis, M. and M. Suignard, "Unicode Security Considerations",
+ February 2006, <http://www.unicode.org/reports/tr36/>.
+
+13.2. Informative References
+
+ [9] Freed, N. and N. Borenstein, "Multipurpose Internet Mail
+ Extensions (MIME) Part One: Format of Internet Message Bodies",
+ RFC 2045, November 1996.
+
+ [10] Melnikov, A., "Simple Authentication and Security Layer
+ (SASL)", RFC 4422, June 2006.
+
+ [11] Newman, C. and J. Myers, "ACAP -- Application Configuration
+ Access Protocol", RFC 2244, November 1997.
+
+ [12] Resnick, P., "Internet Message Format", RFC 2822, April 2001.
+
+ [13] Freed, N. and J. Postel, "IANA Charset Registration
+ Procedures", BCP 19, RFC 2978, October 2000.
+
+
+
+
+
+Newman, et al. Standards Track [Page 24]
+
+RFC 4790 Collation Registry March 2007
+
+
+ [14] Showalter, T., "Sieve: A Mail Filtering Language", RFC 3028,
+ January 2001.
+
+ [15] Crispin, M., "Internet Message Access Protocol - Version
+ 4rev1", RFC 3501, March 2003.
+
+ [16] Crispin, M. and K. Murchison, "Internet Message Access Protocol
+ - Sort and Thread Extensions", Work in Progress, May 2004.
+
+ [17] Newman, C. and A. Gulbrandsen, "Internet Message Access
+ Protocol Internationalization", Work in Progress, January 2006.
+
+Authors' Addresses
+
+ Chris Newman
+ Sun Microsystems
+ 1050 Lakes Drive
+ West Covina, CA 91790
+ USA
+
+ EMail: chris.newman@sun.com
+
+
+ Martin Duerst
+ Aoyama Gakuin University
+ 5-10-1 Fuchinobe
+ Sagamihara, Kanagawa 229-8558
+ Japan
+
+ Phone: +81 42 759 6329
+ Fax: +81 42 759 6495
+ EMail: duerst@it.aoyama.ac.jp
+ URI: http://www.sw.it.aoyama.ac.jp/D%C3%BCrst/
+
+ Note: Please write "Duerst" with u-umlaut wherever possible, for
+ example as "D&#252;rst" in XML and HTML.
+
+
+ Arnt Gulbrandsen
+ Oryx Mail Systems GmbH
+ Schweppermannstr. 8
+ 81671 Munich
+ Germany
+
+ Fax: +49 89 4502 9758
+ EMail: arnt@oryx.com
+ URI: http://www.oryx.com/arnt/
+
+
+
+
+Newman, et al. Standards Track [Page 25]
+
+RFC 4790 Collation Registry March 2007
+
+
+Full Copyright Statement
+
+ Copyright (C) The IETF Trust (2007).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY, THE IETF TRUST AND
+ THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF
+ THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+Newman, et al. Standards Track [Page 26]
+
diff --git a/imap/docs/rfc/rfc4959.txt b/imap/docs/rfc/rfc4959.txt
new file mode 100644
index 00000000..3df18354
--- /dev/null
+++ b/imap/docs/rfc/rfc4959.txt
@@ -0,0 +1,395 @@
+
+
+
+
+
+
+Network Working Group R. Siemborski
+Request for Comments: 4959 Google, Inc.
+Category: Standards Track A. Gulbrandsen
+ Oryx Mail Systems GmbH
+ September 2007
+
+
+ IMAP Extension for Simple Authentication and Security Layer (SASL)
+ Initial Client Response
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Abstract
+
+ To date, the Internet Message Access Protocol (IMAP) has used a
+ Simple Authentication and Security Layer (SASL) profile which always
+ required at least one complete round trip for an authentication, as
+ it did not support an initial client response argument. This
+ additional round trip at the beginning of the session is undesirable,
+ especially when round-trip costs are high.
+
+ This document defines an extension to IMAP which allows clients and
+ servers to avoid this round trip by allowing an initial client
+ response argument to the IMAP AUTHENTICATE command.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Siemborski & Gulbrandsen Standards Track [Page 1]
+
+RFC 4959 IMAP Ext for SASL Initial Client Response September 2007
+
+
+1. Introduction
+
+ The SASL initial client response extension is present in any IMAP
+ [RFC3501] server implementation which returns "SASL-IR" as one of the
+ supported capabilities in its CAPABILITY response.
+
+ Servers which support this extension will accept an optional initial
+ client response with the AUTHENTICATE command for any SASL [RFC4422]
+ mechanisms which support it.
+
+2. Conventions Used in This Document
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in [RFC2119].
+
+ In examples, "C:" and "S:" indicate lines sent by the client and
+ server, respectively.
+
+ Formal syntax is defined by [RFC4234] as extended by [RFC3501].
+
+3. IMAP Changes to the IMAP AUTHENTICATE Command
+
+ This extension adds an optional second argument to the AUTHENTICATE
+ command that is defined in Section 6.2.2 of [RFC3501]. If this
+ second argument is present, it represents the contents of the
+ "initial client response" defined in Section 5.1 of [RFC4422].
+
+ As with any other client response, this initial client response MUST
+ be encoded as defined in Section 4 of [RFC4648]. It also MUST be
+ transmitted outside of a quoted string or literal. To send a zero-
+ length initial response, the client MUST send a single pad character
+ ("="). This indicates that the response is present, but is a zero-
+ length string.
+
+ When decoding the BASE64 [RFC4648] data in the initial client
+ response, decoding errors MUST be treated as IMAP [RFC3501] would
+ handle them in any normal SASL client response. In particular, the
+ server should check for any characters not explicitly allowed by the
+ BASE64 alphabet, as well as any sequence of BASE64 characters that
+ contains the pad character ('=') anywhere other than the end of the
+ string (e.g., "=AAA" and "AAA=BBB" are not allowed).
+
+ If the client uses an initial response with a SASL mechanism that
+ does not support an initial response, the server MUST reject the
+ command with a tagged BAD response.
+
+
+
+
+
+Siemborski & Gulbrandsen Standards Track [Page 2]
+
+RFC 4959 IMAP Ext for SASL Initial Client Response September 2007
+
+
+ Note: support and use of the initial client response is optional for
+ both clients and servers. Servers that implement this extension MUST
+ support clients that omit the initial client response, and clients
+ that implement this extension MUST NOT send an initial client
+ response to servers that do not advertise the SASL-IR capability. In
+ such a situation, clients MUST fall back to an IMAP [RFC3501]
+ compatible mode.
+
+ If either the client or the server do not support the SASL-IR
+ capability, a mechanism which uses an initial client response is
+ negotiated using the challenge/response exchange described in
+ [RFC3501], with an initial zero-length server challenge.
+
+4. Examples
+
+ The following is an example authentication using the PLAIN (see
+ [RFC4616]) SASL mechanism (under a TLS protection layer, see
+ [RFC4346]) and an initial client response:
+
+ ... client connects to server and negotiates a TLS
+ protection layer ...
+ C: C01 CAPABILITY
+ S: * CAPABILITY IMAP4rev1 SASL-IR AUTH=PLAIN
+ S: C01 OK Completed
+ C: A01 AUTHENTICATE PLAIN dGVzdAB0ZXN0AHRlc3Q=
+ S: A01 OK Success (tls protection)
+
+ Note that even when a server supports this extension, the following
+ negotiation (which does not use the initial response) is still valid
+ and MUST be supported by the server:
+
+ ... client connects to server and negotiates a TLS
+ protection layer ...
+ C: C01 CAPABILITY
+ S: * CAPABILITY IMAP4rev1 SASL-IR AUTH=PLAIN
+ S: C01 OK Completed
+ C: A01 AUTHENTICATE PLAIN
+ (note that there is a space following the "+" in the
+ following line)
+ S: +
+ C: dGVzdAB0ZXN0AHRlc3Q=
+ S: A01 OK Success (tls protection)
+
+ The following is an example authentication using the SASL EXTERNAL
+ mechanism (defined in [RFC4422]) under a TLS protection layer (see
+ [RFC4346]) and an empty initial client response:
+
+
+
+
+
+Siemborski & Gulbrandsen Standards Track [Page 3]
+
+RFC 4959 IMAP Ext for SASL Initial Client Response September 2007
+
+
+ ... client connects to server and negotiates a TLS
+ protection layer ...
+ C: C01 CAPABILITY
+ S: * CAPABILITY IMAP4rev1 SASL-IR AUTH=PLAIN AUTH=EXTERNAL
+ S: C01 OK Completed
+ C: A01 AUTHENTICATE EXTERNAL =
+ S: A01 OK Success (tls protection)
+
+ This is in contrast with the handling of such a situation when an
+ initial response is omitted:
+
+ ... client connects to server and negotiates a TLS protection
+ layer ...
+ C: C01 CAPABILITY
+ S: * CAPABILITY IMAP4rev1 SASL-IR AUTH=PLAIN AUTH=EXTERNAL
+ S: C01 OK Completed
+ C: A01 AUTHENTICATE EXTERNAL
+ (note that there is a space following the "+" in the
+ following line)
+ S: +
+ C:
+ S: A01 OK Success (tls protection)
+
+5. IANA Considerations
+
+ The IANA has added SASL-IR to the IMAP4 Capabilities Registry.
+
+6. Security Considerations
+
+ The extension defined in this document is subject to many of the
+ Security Considerations defined in [RFC3501] and [RFC4422].
+
+ Server implementations MUST treat the omission of an initial client
+ response from the AUTHENTICATE command as defined by [RFC3501] (as if
+ this extension did not exist).
+
+ Although [RFC3501] has no express line length limitations, some
+ implementations choose to enforce them anyway. Such implementations
+ MUST be aware that the addition of the initial response parameter to
+ AUTHENTICATE may increase the maximum line length that IMAP parsers
+ may expect to support. Server implementations MUST be able to
+ receive the largest possible initial client response that their
+ supported mechanisms might receive.
+
+
+
+
+
+
+
+
+Siemborski & Gulbrandsen Standards Track [Page 4]
+
+RFC 4959 IMAP Ext for SASL Initial Client Response September 2007
+
+
+7. Formal Syntax
+
+ The following syntax specification uses the Augmented Backus-Naur
+ Form [RFC4234] notation. [RFC3501] defines the non-terminals
+ capability, auth-type, and base64.
+
+ capability =/ "SASL-IR"
+
+ authenticate = "AUTHENTICATE" SP auth-type [SP (base64 / "=")]
+ *(CRLF base64)
+ ;;redefine AUTHENTICATE from [RFC3501]
+
+8. Acknowledgments
+
+ The authors would like to acknowledge the contributions of Ken
+ Murchison and Mark Crispin, along with the rest of the IMAPEXT
+ Working Group for their assistance in reviewing this document.
+
+ Alexey Melnikov and Cyrus Daboo also had some early discussions about
+ this extension.
+
+9. References
+
+9.1. Normative References
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC3501] Crispin, M., "INTERNET MESSAGE ACCESS PROTOCOL - VERSION
+ 4rev1", RFC 3501, March 2003.
+
+ [RFC4234] Crocker, D. and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", RFC 4234, October 2005.
+
+ [RFC4422] Melnikov, A. and K. Zeilenga, "Simple Authentication and
+ Security Layer (SASL)", RFC 4422, June 2006.
+
+ [RFC4648] Josefsson, S., "The Base16, Base32, and Base64 Data
+ Encodings", RFC 4648, October 2006.
+
+9.2. Informative References
+
+ [RFC4616] Zeilenga, K., "The PLAIN Simple Authentication and
+ Security Layer (SASL) Mechanism", RFC 4616, August 2006.
+
+ [RFC4346] Dierks, T. and E. Rescorla, "The Transport Layer Security
+ (TLS) Protocol Version 1.1", RFC 4346, April 2006.
+
+
+
+
+Siemborski & Gulbrandsen Standards Track [Page 5]
+
+RFC 4959 IMAP Ext for SASL Initial Client Response September 2007
+
+
+Authors' Addresses
+
+ Robert Siemborski
+ Google, Inc.
+ 1600 Ampitheatre Parkway
+ Mountain View, CA 94043
+
+ Phone: +1 650 623 6925
+ EMail: robsiemb@google.com
+
+
+ Arnt Gulbrandsen
+ Oryx Mail Systems GmbH
+ Schweppermannstr. 8
+ D-81671 Muenchen
+ Germany
+
+ EMail: arnt@oryx.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Siemborski & Gulbrandsen Standards Track [Page 6]
+
+RFC 4959 IMAP Ext for SASL Initial Client Response September 2007
+
+
+Full Copyright Statement
+
+ Copyright (C) The IETF Trust (2007).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY, THE IETF TRUST AND
+ THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF
+ THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+
+
+
+
+
+
+
+
+
+
+
+Siemborski & Gulbrandsen Standards Track [Page 7]
+
diff --git a/imap/docs/rfc/rfc4978.txt b/imap/docs/rfc/rfc4978.txt
new file mode 100644
index 00000000..14b56b6e
--- /dev/null
+++ b/imap/docs/rfc/rfc4978.txt
@@ -0,0 +1,507 @@
+
+
+
+
+
+
+Network Working Group A. Gulbrandsen
+Request for Comments: 4978 Oryx Mail Systems GmbH
+Category: Standards Track August 2007
+
+
+ The IMAP COMPRESS Extension
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Abstract
+
+ The COMPRESS extension allows an IMAP connection to be effectively
+ and efficiently compressed.
+
+ Table of Contents
+
+ 1. Introduction and Overview .......................................2
+ 2. Conventions Used in This Document ...............................2
+ 3. The COMPRESS Command ............................................3
+ 4. Compression Efficiency ..........................................4
+ 5. Formal Syntax ...................................................6
+ 6. Security Considerations .........................................6
+ 7. IANA Considerations .............................................6
+ 8. Acknowledgements ................................................7
+ 9. References ......................................................7
+ 9.1. Normative References .......................................7
+ 9.2. Informative References .....................................7
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Gulbrandsen Standards Track [Page 1]
+
+RFC 4978 The IMAP COMPRESS Extension August 2007
+
+
+1. Introduction and Overview
+
+ A server which supports the COMPRESS extension indicates this with
+ one or more capability names consisting of "COMPRESS=" followed by a
+ supported compression algorithm name as described in this document.
+
+ The goal of COMPRESS is to reduce the bandwidth usage of IMAP.
+
+ Compared to PPP compression (see [RFC1962]) and modem-based
+ compression (see [MNP] and [V42BIS]), COMPRESS offers much better
+ compression efficiency. COMPRESS can be used together with Transport
+ Security Layer (TLS) [RFC4346], Simple Authentication and Security
+ layer (SASL) encryption, Virtual Private Networks (VPNs), etc.
+ Compared to TLS compression [RFC3749], COMPRESS has the following
+ (dis)advantages:
+
+ - COMPRESS can be implemented easily both by IMAP servers and
+ clients.
+
+ - IMAP COMPRESS benefits from an intimate knowledge of the IMAP
+ protocol's state machine, allowing for dynamic and aggressive
+ optimization of the underlying compression algorithm's parameters.
+
+ - When the TLS layer implements compression, any protocol using that
+ layer can transparently benefit from that compression (e.g., SMTP
+ and IMAP). COMPRESS is specific to IMAP.
+
+ In order to increase interoperation, it is desirable to have as few
+ different compression algorithms as possible, so this document
+ specifies only one. The DEFLATE algorithm (defined in [RFC1951]) is
+ standard, widely available and fairly efficient, so it is the only
+ algorithm defined by this document.
+
+ In order to increase interoperation, IMAP servers that advertise this
+ extension SHOULD also advertise the TLS DEFLATE compression mechanism
+ as defined in [RFC3749]. IMAP clients MAY use either COMPRESS or TLS
+ compression, however, if the client and server support both, it is
+ RECOMMENDED that the client choose TLS compression.
+
+ The extension adds one new command (COMPRESS) and no new responses.
+
+2. Conventions Used in This Document
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in [RFC2119].
+
+ Formal syntax is defined by [RFC4234] as modified by [RFC3501].
+
+
+
+Gulbrandsen Standards Track [Page 2]
+
+RFC 4978 The IMAP COMPRESS Extension August 2007
+
+
+ In the examples, "C:" and "S:" indicate lines sent by the client and
+ server respectively. "[...]" denotes elision.
+
+3. The COMPRESS Command
+
+ Arguments: Name of compression mechanism: "DEFLATE".
+
+ Responses: None
+
+ Result: OK The server will compress its responses and expects the
+ client to compress its commands.
+ NO Compression is already active via another layer.
+ BAD Command unknown, invalid or unknown argument, or COMPRESS
+ already active.
+
+ The COMPRESS command instructs the server to use the named
+ compression mechanism ("DEFLATE" is the only one defined) for all
+ commands and/or responses after COMPRESS.
+
+ The client MUST NOT send any further commands until it has seen the
+ result of COMPRESS. If the response was OK, the client MUST compress
+ starting with the first command after COMPRESS. If the server
+ response was BAD or NO, the client MUST NOT turn on compression.
+
+ If the server responds NO because it knows that the same mechanism is
+ active already (e.g., because TLS has negotiated the same mechanism),
+ it MUST send COMPRESSIONACTIVE as resp-text-code (see [RFC3501],
+ Section 7.1), and the resp-text SHOULD say which layer compresses.
+
+ If the server issues an OK response, the server MUST compress
+ starting immediately after the CRLF which ends the tagged OK
+ response. (Responses issued by the server before the OK response
+ will, of course, still be uncompressed.) If the server issues a BAD
+ or NO response, the server MUST NOT turn on compression.
+
+ For DEFLATE (as for many other compression mechanisms), the
+ compressor can trade speed against quality. When decompressing there
+ isn't much of a tradeoff. Consequently, the client and server are
+ both free to pick the best reasonable rate of compression for the
+ data they send.
+
+ When COMPRESS is combined with TLS (see [RFC4346]) or SASL (see
+ [RFC4422]) security layers, the sending order of the three extensions
+ MUST be first COMPRESS, then SASL, and finally TLS. That is, before
+ data is transmitted it is first compressed. Second, if a SASL
+ security layer has been negotiated, the compressed data is then
+ signed and/or encrypted accordingly. Third, if a TLS security layer
+ has been negotiated, the data from the previous step is signed and/or
+
+
+
+Gulbrandsen Standards Track [Page 3]
+
+RFC 4978 The IMAP COMPRESS Extension August 2007
+
+
+ encrypted accordingly. When receiving data, the processing order
+ MUST be reversed. This ensures that before sending, data is
+ compressed before it is encrypted, independent of the order in which
+ the client issues COMPRESS, AUTHENTICATE, and STARTTLS.
+
+ The following example illustrates how commands and responses are
+ compressed during a simple login sequence:
+
+ S: * OK [CAPABILITY IMAP4REV1 STARTTLS COMPRESS=DEFLATE]
+ C: a starttls
+ S: a OK TLS active
+
+ From this point on, everything is encrypted.
+
+ C: b login arnt tnra
+ S: b OK Logged in as arnt
+ C: c compress deflate
+ S: d OK DEFLATE active
+
+ From this point on, everything is compressed before being
+ encrypted.
+
+ The following example demonstrates how a server may refuse to
+ compress twice:
+
+ S: * OK [CAPABILITY IMAP4REV1 STARTTLS COMPRESS=DEFLATE]
+ [...]
+ C: c compress deflate
+ S: c NO [COMPRESSIONACTIVE] DEFLATE active via TLS
+
+4. Compression Efficiency
+
+ This section is informative, not normative.
+
+ IMAP poses some unusual problems for a compression layer.
+
+ Upstream is fairly simple. Most IMAP clients send the same few
+ commands again and again, so any compression algorithm that can
+ exploit repetition works efficiently. The APPEND command is an
+ exception; clients that send many APPEND commands may want to
+ surround large literals with flushes in the same way as is
+ recommended for servers later in this section.
+
+ Downstream has the unusual property that several kinds of data are
+ sent, confusing all dictionary-based compression algorithms.
+
+
+
+
+
+
+Gulbrandsen Standards Track [Page 4]
+
+RFC 4978 The IMAP COMPRESS Extension August 2007
+
+
+ One type is IMAP responses. These are highly compressible; zlib
+ using its least CPU-intensive setting compresses typical responses to
+ 25-40% of their original size.
+
+ Another type is email headers. These are equally compressible, and
+ benefit from using the same dictionary as the IMAP responses.
+
+ A third type is email body text. Text is usually fairly short and
+ includes much ASCII, so the same compression dictionary will do a
+ good job here, too. When multiple messages in the same thread are
+ read at the same time, quoted lines etc. can often be compressed
+ almost to zero.
+
+ Finally, attachments (non-text email bodies) are transmitted, either
+ in binary form or encoded with base-64.
+
+ When attachments are retrieved in binary form, DEFLATE may be able to
+ compress them, but the format of the attachment is usually not IMAP-
+ like, so the dictionary built while compressing IMAP does not help.
+ The compressor has to adapt its dictionary from IMAP to the
+ attachment's format, and then back. A few file formats aren't
+ compressible at all using deflate, e.g., .gz, .zip, and .jpg files.
+
+ When attachments are retrieved in base-64 form, the same problems
+ apply, but the base-64 encoding adds another problem. 8-bit
+ compression algorithms such as deflate work well on 8-bit file
+ formats, however base-64 turns a file into something resembling 6-bit
+ bytes, hiding most of the 8-bit file format from the compressor.
+
+ When using the zlib library (see [RFC1951]), the functions
+ deflateInit2(), deflate(), inflateInit2(), and inflate() suffice to
+ implement this extension. The windowBits value must be in the range
+ -8 to -15, or else deflateInit2() uses the wrong format.
+ deflateParams() can be used to improve compression rate and resource
+ use. The Z_FULL_FLUSH argument to deflate() can be used to clear the
+ dictionary (the receiving peer does not need to do anything).
+
+ A client can improve downstream compression by implementing BINARY
+ (defined in [RFC3516]) and using FETCH BINARY instead of FETCH BODY.
+ In the author's experience, the improvement ranges from 5% to 40%
+ depending on the attachment being downloaded.
+
+ A server can improve downstream compression if it hints to the
+ compressor that the data type is about to change strongly, e.g., by
+ sending a Z_FULL_FLUSH at the start and end of large non-text
+ literals (before and after '*CHAR8' in the definition of literal in
+ RFC 3501, page 86). Small literals are best left alone. A possible
+ boundary is 5k.
+
+
+
+Gulbrandsen Standards Track [Page 5]
+
+RFC 4978 The IMAP COMPRESS Extension August 2007
+
+
+ A server can improve the CPU efficiency both of the server and the
+ client if it adjusts the compression level (e.g., using the
+ deflateParams() function in zlib) at these points, to avoid trying to
+ compress incompressible attachments. A very simple strategy is to
+ change the level to 0 at the start of a literal provided the first
+ two bytes are either 0x1F 0x8B (as in deflate-compressed files) or
+ 0xFF 0xD8 (JPEG), and to keep it at 1-5 the rest of the time. More
+ complex strategies are possible.
+
+5. Formal Syntax
+
+ The following syntax specification uses the Augmented Backus-Naur
+ Form (ABNF) notation as specified in [RFC4234]. This syntax augments
+ the grammar specified in [RFC3501]. [RFC4234] defines SP and
+ [RFC3501] defines command-auth, capability, and resp-text-code.
+
+ Except as noted otherwise, all alphabetic characters are case-
+ insensitive. The use of upper or lower case characters to define
+ token strings is for editorial clarity only. Implementations MUST
+ accept these strings in a case-insensitive fashion.
+
+ command-auth =/ compress
+
+ compress = "COMPRESS" SP algorithm
+
+ capability =/ "COMPRESS=" algorithm
+ ;; multiple COMPRESS capabilities allowed
+
+ algorithm = "DEFLATE"
+
+ resp-text-code =/ "COMPRESSIONACTIVE"
+
+ Note that due the syntax of capability names, future algorithm names
+ must be atoms.
+
+6. Security Considerations
+
+ As for TLS compression [RFC3749].
+
+7. IANA Considerations
+
+ The IANA has added COMPRESS=DEFLATE to the list of IMAP capabilities.
+
+
+
+
+
+
+
+
+
+Gulbrandsen Standards Track [Page 6]
+
+RFC 4978 The IMAP COMPRESS Extension August 2007
+
+
+8. Acknowledgements
+
+ Eric Burger, Dave Cridland, Tony Finch, Ned Freed, Philip Guenther,
+ Randall Gellens, Tony Hansen, Cullen Jennings, Stephane Maes, Alexey
+ Melnikov, Lyndon Nerenberg, and Zoltan Ordogh have all helped with
+ this document.
+
+ The author would also like to thank various people in the rooms at
+ meetings, whose help is real, but not reflected in the author's
+ mailbox.
+
+9. References
+
+9.1. Normative References
+
+ [RFC1951] Deutsch, P., "DEFLATE Compressed Data Format Specification
+ version 1.3", RFC 1951, May 1996.
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC3501] Crispin, M., "INTERNET MESSAGE ACCESS PROTOCOL - VERSION
+ 4rev1", RFC 3501, March 2003.
+
+ [RFC4234] Crocker, D. and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", RFC 4234, October 2005.
+
+9.2. Informative References
+
+ [RFC1962] Rand, D., "The PPP Compression Control Protocol (CCP)",
+ RFC 1962, June 1996.
+
+ [RFC3516] Nerenberg, L., "IMAP4 Binary Content Extension", RFC 3516,
+ April 2003.
+
+ [RFC3749] Hollenbeck, S., "Transport Layer Security Protocol
+ Compression Methods", RFC 3749, May 2004.
+
+ [RFC4346] Dierks, T. and E. Rescorla, "The Transport Layer Security
+ (TLS) Protocol Version 1.1", RFC 4346, April 2006.
+
+ [RFC4422] Melnikov, A. and K. Zeilenga, "Simple Authentication and
+ Security Layer (SASL)", RFC 4422, June 2006.
+
+ [V42BIS] ITU, "V.42bis: Data compression procedures for data
+ circuit-terminating equipment (DCE) using error correction
+ procedures", http://www.itu.int/rec/T-REC-V.42bis, January
+ 1990.
+
+
+
+Gulbrandsen Standards Track [Page 7]
+
+RFC 4978 The IMAP COMPRESS Extension August 2007
+
+
+ [MNP] Gilbert Held, "The Complete Modem Reference", Second
+ Edition, Wiley Professional Computing, ISBN 0-471-00852-4,
+ May 1994.
+
+Author's Address
+
+ Arnt Gulbrandsen
+ Oryx Mail Systems GmbH
+ Schweppermannstr. 8
+ D-81671 Muenchen
+ Germany
+
+ Fax: +49 89 4502 9758
+ EMail: arnt@oryx.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Gulbrandsen Standards Track [Page 8]
+
+RFC 4978 The IMAP COMPRESS Extension August 2007
+
+
+Full Copyright Statement
+
+ Copyright (C) The IETF Trust (2007).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY, THE IETF TRUST AND
+ THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF
+ THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+
+
+
+
+
+
+
+
+
+
+
+Gulbrandsen Standards Track [Page 9]
+
diff --git a/imap/docs/rfc/rfc5032.txt b/imap/docs/rfc/rfc5032.txt
new file mode 100644
index 00000000..f8e48953
--- /dev/null
+++ b/imap/docs/rfc/rfc5032.txt
@@ -0,0 +1,283 @@
+
+
+
+
+
+
+Network Working Group E. Burger, Ed.
+Request for Comments: 5032 BEA Systems, Inc.
+Updates: 3501 September 2007
+Category: Standards Track
+
+
+ WITHIN Search Extension to the IMAP Protocol
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Abstract
+
+ This document describes the WITHIN extension to IMAP SEARCH. IMAP
+ SEARCH returns messages whose internal date is within or outside a
+ specified interval. The mechanism described here, OLDER and YOUNGER,
+ differs from BEFORE and SINCE in that the client specifies an
+ interval, rather than a date. WITHIN is useful for persistent
+ searches where either the device does not have the capacity to
+ perform the search at regular intervals or the network is of limited
+ bandwidth and thus there is a desire to reduce network traffic from
+ sending repeated requests and redundant responses.
+
+1. Introduction
+
+ This extension exposes two new search keys, OLDER and YOUNGER, each
+ of which takes a non-zero integer argument corresponding to a time
+ interval in seconds. The server calculates the time of interest by
+ subtracting the time interval the client presents from the current
+ date and time of the server. The server then either returns messages
+ older or younger than the resultant time and date, depending on the
+ search key used.
+
+1.1. Conventions Used in This Document
+
+ In examples, "C:" and "S:" indicate lines sent by the client and
+ server, respectively.
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in RFC 2119 [RFC2119].
+
+
+
+
+
+Burger Standards Track [Page 1]
+
+RFC 5032 Search Within September 2007
+
+
+ When describing the general syntax, we omit some definitions, as RFC
+ 3501 [RFC3501] defines them.
+
+2. Protocol Operation
+
+ An IMAP4 server that supports the capability described here MUST
+ return "WITHIN" as one of the server supported capabilities in the
+ CAPABILITY command.
+
+ For both the OLDER and YOUNGER search keys, the server calculates a
+ target date and time by subtracting the interval, specified in
+ seconds, from the current date and time of the server. The server
+ then compares the target time with the INTERNALDATE of the message,
+ as specified in IMAP [RFC3501]. For OLDER, messages match if the
+ INTERNALDATE is less recent than or equal to the target time. For
+ YOUNGER, messages match if the INTERNALDATE is more recent than or
+ equal to the target time.
+
+ Both OLDER and YOUNGER searches always result in exact matching, to
+ the resolution of a second. However, if one is doing a dynamic
+ evaluation, for example, in a context [CONTEXT], one needs to be
+ aware that the server might perform the evaluation periodically.
+ Thus, the server may delay the updates. Clients MUST be aware that
+ dynamic search results may not reflect the current state of the
+ mailbox. If the client needs a search result that reflects the
+ current state of the mailbox, we RECOMMEND that the client issue a
+ new search.
+
+3. Formal Syntax
+
+ The following syntax specification uses the Augmented Backus-Naur
+ Form (ABNF) notation. Elements not defined here can be found in the
+ formal syntax of ABNF [RFC4234] and IMAP [RFC3501].
+
+ This document extends RFC 3501 [RFC3501] with two new search keys:
+ OLDER <interval> and YOUNGER <interval>.
+
+ search-key =/ ( "OLDER" / "YOUNGER" ) SP nz-number
+ ; search-key defined in RFC 3501
+
+4. Example
+
+ C: a1 SEARCH UNSEEN YOUNGER 259200
+ S: a1 * SEARCH 4 8 15 16 23 42
+
+ Search for all unseen messages within the past 3 days, or 259200
+ seconds, according to the server's current time.
+
+
+
+
+Burger Standards Track [Page 2]
+
+RFC 5032 Search Within September 2007
+
+
+5. Security Considerations
+
+ The WITHIN extension does not raise any security considerations that
+ are not present in the base protocol. Considerations are the same as
+ for IMAP [RFC3501].
+
+6. IANA Considerations
+
+ Per the IMAP RFC [RFC3501], registration of a new IMAP capability in
+ the IMAP Capability registry requires the publication of a standards-
+ track RFC or an IESG approved experimental RFC. The registry is
+ currently located at
+ <http://www.iana.org/assignments/imap4-capabilities>. This
+ standards-track document defines the WITHIN IMAP capability. IANA
+ has added this extension to the IANA IMAP Capability registry.
+
+7. References
+
+7.1. Normative References
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", RFC 2119, BCP 14, March 1997.
+
+ [RFC3501] Crispin, M., "Internet Message Access Protocol - Version
+ 4rev1", RFC 3501, March 2003.
+
+ [RFC4234] Crocker, D., Ed. and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", RFC 4234, October 2005.
+
+7.2. Informative References
+
+ [CONTEXT] Melnikov, D. and C. King, "Contexts for IMAP4", Work
+ in Progress, May 2006.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Burger Standards Track [Page 3]
+
+RFC 5032 Search Within September 2007
+
+
+Appendix A. Contributors
+
+ Stephane Maes and Ray Cromwell wrote the original version of this
+ document as part of P-IMAP, as well as the first versions for the
+ IETF. From an attribution perspective, they are clearly authors.
+
+Appendix B. Acknowledgements
+
+ The authors want to thank all who have contributed key insight and
+ who have extensively reviewed and discussed the concepts of LPSEARCH.
+ They also thank the authors of its early introduction in P-IMAP.
+
+ We also want to give a special thanks to Arnt Gilbrandsen, Ken
+ Murchison, Zoltan Ordogh, and most especially Dave Cridland for their
+ review and suggestions. A special thank you goes to Alexey Melnikov
+ for his choice submission of text.
+
+Author's Address
+
+ Eric W. Burger (editor)
+ BEA Systems, Inc.
+ USA
+
+ EMail: eric.burger@bea.com
+ URI: http://www.standardstrack.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Burger Standards Track [Page 4]
+
+RFC 5032 Search Within September 2007
+
+
+Full Copyright Statement
+
+ Copyright (C) The IETF Trust (2007).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY, THE IETF TRUST AND
+ THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF
+ THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+
+
+
+
+
+
+
+
+
+
+
+Burger Standards Track [Page 5]
+
diff --git a/imap/docs/rfc/rfc5051.txt b/imap/docs/rfc/rfc5051.txt
new file mode 100644
index 00000000..0a4479ca
--- /dev/null
+++ b/imap/docs/rfc/rfc5051.txt
@@ -0,0 +1,395 @@
+
+
+
+
+
+
+Network Working Group M. Crispin
+Request for Comments: 5051 University of Washington
+Category: Standards Track October 2007
+
+
+ i;unicode-casemap - Simple Unicode Collation Algorithm
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Abstract
+
+ This document describes "i;unicode-casemap", a simple case-
+ insensitive collation for Unicode strings. It provides equality,
+ substring, and ordering operations.
+
+1. Introduction
+
+ The "i;ascii-casemap" collation described in [COMPARATOR] is quite
+ simple to implement and provides case-independent comparisons for the
+ 26 Latin alphabetics. It is specified as the default and/or baseline
+ comparator in some application protocols, e.g., [IMAP-SORT].
+
+ However, the "i;ascii-casemap" collation does not produce
+ satisfactory results with non-ASCII characters. It is possible, with
+ a modest extension, to provide a more sophisticated collation with
+ greater multilingual applicability than "i;ascii-casemap". This
+ extension provides case-independent comparisons for a much greater
+ number of characters. It also collates characters with diacriticals
+ with the non-diacritical character forms.
+
+ This collation, "i;unicode-casemap", is intended to be an alternative
+ to, and preferred over, "i;ascii-casemap". It does not replace the
+ "i;basic" collation described in [BASIC].
+
+2. Unicode Casemap Collation Description
+
+ The "i;unicode-casemap" collation is a simple collation which is
+ case-insensitive in its treatment of characters. It provides
+ equality, substring, and ordering operations. The validity test
+ operation returns "valid" for any input.
+
+
+
+
+
+Crispin Standards Track [Page 1]
+
+RFC 5051 i;unicode-casemap October 2007
+
+
+ This collation allows strings in arbitrary (and mixed) character
+ sets, as long as the character set for each string is identified and
+ it is possible to convert the string to Unicode. Strings which have
+ an unidentified character set and/or cannot be converted to Unicode
+ are not rejected, but are treated as binary.
+
+ Each input string is prepared by converting it to a "titlecased
+ canonicalized UTF-8" string according to the following steps, using
+ UnicodeData.txt ([UNICODE-DATA]):
+
+ (1) A Unicode codepoint is obtained from the input string.
+
+ (a) If the input string is in a known charset that can be
+ converted to Unicode, a sequence in the string's charset
+ is read and checked for validity according to the rules of
+ that charset. If the sequence is valid, it is converted
+ to a Unicode codepoint. Note that for input strings in
+ UTF-8, the UTF-8 sequence must be valid according to the
+ rules of [UTF-8]; e.g., overlong UTF-8 sequences are
+ invalid.
+
+ (b) If the input string is in an unknown charset, or an
+ invalid sequence occurs in step (1)(a), conversion ceases.
+ No further preparation is performed, and any partial
+ preparation results are discarded. The original string is
+ used unchanged with the i;octet comparator.
+
+ (2) The following steps, using UnicodeData.txt ([UNICODE-DATA]),
+ are performed on the resulting codepoint from step (1)(a).
+
+ (a) If the codepoint has a titlecase property in
+ UnicodeData.txt (this is normally the same as the
+ uppercase property), the codepoint is converted to the
+ codepoints in the titlecase property.
+
+ (b) If the resulting codepoint from (2)(a) has a decomposition
+ property of any type in UnicodeData.txt, the codepoint is
+ converted to the codepoints in the decomposition property.
+ This step is recursively applied to each of the resulting
+ codepoints until no more decomposition is possible
+ (effectively Normalization Form KD).
+
+ Example: codepoint U+01C4 (LATIN CAPITAL LETTER DZ WITH CARON)
+ has a titlecase property of U+01C5 (LATIN CAPITAL LETTER D
+ WITH SMALL LETTER Z WITH CARON). Codepoint U+01C5 has a
+ decomposition property of U+0044 (LATIN CAPITAL LETTER D)
+ U+017E (LATIN SMALL LETTER Z WITH CARON). U+017E has a
+ decomposition property of U+007A (LATIN SMALL LETTER Z) U+030c
+
+
+
+Crispin Standards Track [Page 2]
+
+RFC 5051 i;unicode-casemap October 2007
+
+
+ (COMBINING CARON). Neither U+0044, U+007A, nor U+030C have
+ any decomposition properties. Therefore, U+01C4 is converted
+ to U+0044 U+007A U+030C by this step.
+
+ (3) The resulting codepoint(s) from step (2) is/are appended, in
+ UTF-8 format, to the "titlecased canonicalized UTF-8" string.
+
+ (4) Repeat from step (1) until there is no more data in the input
+ string.
+
+ Following the above preparation process on each string, the equality,
+ ordering, and substring operations are as for i;octet.
+
+ It is permitted to use an alternative implementation of the above
+ preparation process if it produces the same results. For example, it
+ may be more convenient for an implementation to convert all input
+ strings to a sequence of UTF-16 or UTF-32 values prior to performing
+ any of the step (2) actions. Similarly, if all input strings are (or
+ are convertible to) Unicode, it may be possible to use UTF-32 as an
+ alternative to UTF-8 in step (3).
+
+ Note: UTF-16 is unsuitable as an alternative to UTF-8 in step (3),
+ because UTF-16 surrogates will cause i;octet to collate codepoints
+ U+E0000 through U+FFFF after non-BMP codepoints.
+
+ This collation is not locale sensitive. Consequently, care should be
+ taken when using OS-supplied functions to implement this collation.
+ Functions such as strcasecmp and toupper are sometimes locale
+ sensitive and may inconsistently casemap letters.
+
+ The i;unicode-casemap collation is well suited to use with many
+ Internet protocols and computer languages. Use with natural language
+ is often inappropriate; even though the collation apparently supports
+ languages such as Swahili and English, in real-world use it tends to
+ mis-sort a number of types of string:
+
+ o people and place names containing scripts that are not collated
+ according to "alphabetical order".
+ o words with characters that have diacriticals. However,
+ i;unicode-casemap generally does a better job than i;ascii-casemap
+ for most (but not all) languages. For example, German umlaut
+ letters will sort correctly, but some Scandinavian letters will
+ not.
+ o names such as "Lloyd" (which in Welsh sorts after "Lyon", unlike
+ in English),
+ o strings containing other non-letter symbols; e.g., euro and pound
+ sterling symbols, quotation marks other than '"', dashes/hyphens,
+ etc.
+
+
+
+Crispin Standards Track [Page 3]
+
+RFC 5051 i;unicode-casemap October 2007
+
+
+3. Unicode Casemap Collation Registration
+
+ <?xml version='1.0'?>
+ <!DOCTYPE collation SYSTEM 'collationreg.dtd'>
+ <collation rfc="5051" scope="global" intendedUse="common">
+ <identifier>i;unicode-casemap</identifier>
+ <title>Unicode Casemap</title>
+ <operations>equality order substring</operations>
+ <specification>RFC 5051</specification>
+ <owner>IETF</owner>
+ <submitter>mrc@cac.washington.edu</submitter>
+ </collation>
+
+4. Security Considerations
+
+ The security considerations for [UTF-8], [STRINGPREP], and [UNICODE-
+ SECURITY] apply and are normative to this specification.
+
+ The results from this comparator will vary depending upon the
+ implementation for several reasons. Implementations MUST consider
+ whether these possibilities are a problem for their use case:
+
+ 1) New characters added in Unicode may have decomposition or
+ titlecase properties that will not be known to an implementation
+ based upon an older revision of Unicode. This impacts step (2).
+
+ 2) Step (2)(b) defines a subset of Normalization Form KD (NFKD) that
+ does not require normalization of out-of-order diacriticals.
+ However, an implementation MAY use an NFKD library routine that
+ does such normalization. This impacts step (2)(b) and possibly
+ also step (1)(a), and is an issue only with ill-formed UTF-8
+ input.
+
+ 3) The set of charsets handled in step (1)(a) is open-ended. UTF-8
+ (and, by extension, US-ASCII) are the only mandatory-to-implement
+ charsets. This impacts step (1)(a).
+
+ Implementations SHOULD, as far as feasible, support all the
+ charsets they are likely to encounter in the input data, in order
+ to avoid poor collation caused by the fall through to the (1)(b)
+ rule.
+
+ 4) Other charsets may have revisions which add new characters that
+ are not known to an implementation based upon an older revision.
+ This impacts step (1)(a) and possibly also step (1)(b).
+
+
+
+
+
+
+Crispin Standards Track [Page 4]
+
+RFC 5051 i;unicode-casemap October 2007
+
+
+ An attacker may create input that is ill-formed or in an unknown
+ charset, with the intention of impacting the results of this
+ comparator or exploiting other parts of the system which process this
+ input in different ways. Note, however, that even well-formed data
+ in a known charset can impact the result of this comparator in
+ unexpected ways. For example, an attacker can substitute U+0041
+ (LATIN CAPITAL LETTER A) with U+0391 (GREEK CAPITAL LETTER ALPHA) or
+ U+0410 (CYRILLIC CAPITAL LETTER A) in the intention of causing a
+ non-match of strings which visually appear the same and/or causing
+ the string to appear elsewhere in a sort.
+
+5. IANA Considerations
+
+ The i;unicode-casemap collation defined in section 2 has been added
+ to the registry of collations defined in [COMPARATOR].
+
+6. Normative References
+
+ [COMPARATOR] Newman, C., Duerst, M., and A. Gulbrandsen,
+ "Internet Application Protocol Collation
+ Registry", RFC 4790, February 2007.
+
+ [STRINGPREP] Hoffman, P. and M. Blanchet, "Preparation of
+ Internationalized Strings ("stringprep")", RFC
+ 3454, December 2002.
+
+ [UTF-8] Yergeau, F., "UTF-8, a transformation format of
+ ISO 10646", STD 63, RFC 3629, November 2003.
+
+ [UNICODE-DATA] <http://www.unicode.org/Public/UNIDATA/
+ UnicodeData.txt>
+
+ Although the UnicodeData.txt file referenced
+ here is part of the Unicode standard, it is
+ subject to change as new characters are added
+ to Unicode and errors are corrected in Unicode
+ revisions. As a result, it may be less stable
+ than might otherwise be implied by the
+ standards status of this specification.
+
+ [UNICODE-SECURITY] Davis, M. and M. Suignard, "Unicode Security
+ Considerations", February 2006,
+ <http://www.unicode.org/reports/tr36/>.
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 5]
+
+RFC 5051 i;unicode-casemap October 2007
+
+
+7. Informative References
+
+ [BASIC] Newman, C., Duerst, M., and A. Gulbrandsen,
+ "i;basic - the Unicode Collation Algorithm",
+ Work in Progress, March 2007.
+
+ [IMAP-SORT] Crispin, M. and K. Murchison, "Internet Message
+ Access Protocol - SORT and THREAD Extensions",
+ Work in Progress, September 2007.
+
+Author's Address
+
+ Mark R. Crispin
+ Networks and Distributed Computing
+ University of Washington
+ 4545 15th Avenue NE
+ Seattle, WA 98105-4527
+
+ Phone: +1 (206) 543-5762
+ EMail: MRC@CAC.Washington.EDU
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 6]
+
+RFC 5051 i;unicode-casemap October 2007
+
+
+Full Copyright Statement
+
+ Copyright (C) The IETF Trust (2007).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY, THE IETF TRUST AND
+ THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF
+ THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+
+
+
+
+
+
+
+
+
+
+
+Crispin Standards Track [Page 7]
+
diff --git a/imap/docs/rfc/rfc5092.txt b/imap/docs/rfc/rfc5092.txt
new file mode 100644
index 00000000..ab87f350
--- /dev/null
+++ b/imap/docs/rfc/rfc5092.txt
@@ -0,0 +1,1795 @@
+
+
+
+
+
+
+Network Working Group A. Melnikov, Ed.
+Request for Comments: 5092 Isode Ltd.
+Obsoletes: 2192 C. Newman
+Updates: 4467 Sun Microsystems
+Category: Standards Track November 2007
+
+
+ IMAP URL Scheme
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Abstract
+
+ IMAP (RFC 3501) is a rich protocol for accessing remote message
+ stores. It provides an ideal mechanism for accessing public mailing
+ list archives as well as private and shared message stores. This
+ document defines a URL scheme for referencing objects on an IMAP
+ server.
+
+ This document obsoletes RFC 2192. It also updates RFC 4467.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Melnikov & Newman Standards Track [Page 1]
+
+RFC 5092 IMAP URL Scheme November 2007
+
+
+Table of Contents
+
+ 1. Introduction ....................................................2
+ 2. Conventions Used in This Document ...............................3
+ 3. IMAP userinfo Component (iuserinfo) .............................4
+ 3.1. IMAP Mailbox Naming Scope ..................................4
+ 3.2. IMAP User Name and Authentication Mechanism ................4
+ 3.3. Limitations of enc-user ....................................6
+ 4. IMAP Server .....................................................7
+ 5. Lists of Messages ...............................................7
+ 6. A Specific Message or Message Part ..............................8
+ 6.1. URLAUTH Authorized URL .....................................9
+ 6.1.1. Concepts ............................................9
+ 6.1.1.1. URLAUTH ....................................9
+ 6.1.1.2. Mailbox Access Key .........................9
+ 6.1.1.3. Authorized Access Identifier ...............9
+ 6.1.1.4. Authorization Mechanism ...................10
+ 6.1.1.5. Authorization Token .......................10
+ 6.1.2. URLAUTH Extensions to IMAP URL .....................10
+ 7. Relative IMAP URLs .............................................11
+ 7.1. absolute-path References ..................................12
+ 7.2. relative-path References ..................................12
+ 8. Internationalization Considerations ............................13
+ 9. Examples .......................................................13
+ 9.1. Examples of Relative URLs .................................16
+ 10. Security Considerations .......................................16
+ 10.1. Security Considerations Specific to URLAUTH Authorized
+ URL ......................................................17
+ 11. ABNF for IMAP URL Scheme ......................................17
+ 12. IANA Considerations ...........................................21
+ 12.1. IANA Registration of imap: URI Scheme ....................21
+ 13. References ....................................................22
+ 13.1. Normative References .....................................22
+ 13.2. Informative References ...................................23
+ Appendix A. Sample Code............................................24
+ Appendix B. List of Changes since RFC 2192.........................30
+ Appendix C. List of Changes since RFC 4467.........................31
+ Appendix D. Acknowledgments........................................31
+
+1. Introduction
+
+ The IMAP URL scheme is used to designate IMAP servers, mailboxes,
+ messages, MIME bodies [MIME], and search programs on Internet hosts
+ accessible using the IMAP protocol over TCP.
+
+ The IMAP URL follows the common Internet scheme syntax as defined in
+ [URI-GEN]. If :<port> is omitted, the port defaults to 143 (as
+ defined in Section 2.1 of [IMAP4]).
+
+
+
+Melnikov & Newman Standards Track [Page 2]
+
+RFC 5092 IMAP URL Scheme November 2007
+
+
+ An absolute IMAP URL takes one of the following forms:
+
+ imap://<iserver>[/]
+
+ imap://<iserver>/<enc-mailbox>[<uidvalidity>][?<enc-search>]
+
+ imap://<iserver>/<enc-mailbox>[<uidvalidity>]<iuid>
+ [<isection>][<ipartial>][<iurlauth>]
+
+ The first form is used to refer to an IMAP server (see Section 4),
+ the second form refers to the contents of a mailbox or a set of
+ messages resulting from a search (see Section 5), and the final form
+ refers to a specific message or message part, and possibly a byte
+ range in that part (see Section 6). If [URLAUTH] extension is
+ supported, then the final form can have the <iurlauth> component (see
+ Section 6.1 for more details).
+
+ The <iserver> component common to all types of absolute IMAP URLs has
+ the following syntax expressed in ABNF [ABNF]:
+
+ [iuserinfo "@"] host [ ":" port ]
+
+ The <iserver> component is the same as "authority" defined in
+ [URI-GEN]. The syntax and uses of the <iuserinfo> ("IMAP userinfo
+ component") are described in detail in Section 3. The syntax of
+ <host> and <port> is described in [URI-GEN].
+
+2. Conventions Used in This Document
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in RFC 2119 [KEYWORDS].
+
+ This document references many productions from [URI-GEN]. When the
+ document needs to emphasize IMAP URI-specific differences from [URI-
+ GEN] (i.e., for parts of IMAP URIs that have more restricted syntax
+ than generic URIs), it uses a non-terminal i<foo> to define an IMAP-
+ specific version of the non-terminal <foo> from [URI-GEN].
+
+ Note that the ABNF syntax shown in Section 11 is normative. Sections
+ 2-6 may use a less formal syntax that does not necessarily match the
+ normative ABNF shown in Section 11. If there are any differences
+ between the syntax shown in Sections 2-6 and Section 11, then the
+ syntax shown in Section 11 must be treated as authoritative. Non-
+ syntax requirements included in Sections 2-6 are, of course,
+ normative.
+
+
+
+
+
+Melnikov & Newman Standards Track [Page 3]
+
+RFC 5092 IMAP URL Scheme November 2007
+
+
+3. IMAP userinfo Component (iuserinfo)
+
+ The <iuserinfo> component conforms to the generic syntax of
+ <userinfo> defined in [URI-GEN]. It has the following syntax
+ expressed in ABNF [ABNF]:
+
+ enc-user [iauth] / [enc-user] iauth
+
+ The meaning of the different parts is described in subsections of
+ this section.
+
+3.1. IMAP Mailbox Naming Scope
+
+ The "enc-user" part of the "iuserinfo" component, if present, denotes
+ mailbox naming scope. If it is absent, the IMAP URL can only
+ reference mailboxes with globally unique names, i.e., mailboxes with
+ names that don't change depending on the user the client
+ authenticated as to the IMAP server. Note that not all IMAP
+ implementations support globally unique names.
+
+ For example, a personal mailbox described by the following URL
+ <imap://michael@example.org/INBOX> is most likely different from a
+ personal mailbox described by <imap://bester@example.org/INBOX>, even
+ though both URLs use the same mailbox name.
+
+3.2. IMAP User Name and Authentication Mechanism
+
+ The userinfo component (see [URI-GEN]) of an IMAP URI may contain an
+ IMAP user name (a.k.a. authorization identity [SASL], "enc-user")
+ and/or an authentication mechanism. (Note that the "enc-user" also
+ defines a mailbox naming scope as described in Section 3.1). The
+ IMAP user name and the authentication mechanism are used in the
+ "LOGIN" or "AUTHENTICATE" commands after making the connection to the
+ IMAP server.
+
+ If no user name and no authentication mechanism are supplied, the
+ client MUST authenticate as anonymous to the server. If the server
+ advertises AUTH=ANONYMOUS IMAP capability, the client MUST use the
+ AUTHENTICATE command with ANONYMOUS [ANONYMOUS] SASL mechanism. If
+ SASL ANONYMOUS is not available, the (case-insensitive) user name
+ "anonymous" is used with the "LOGIN" command and the Internet email
+ address of the end user accessing the resource is supplied as the
+ password. The latter option is given in order to provide for
+ interoperability with deployed servers.
+
+ Note that, as described in RFC 3501, the "LOGIN" command MUST NOT be
+ used when the IMAP server advertises the LOGINDISABLED capability.
+
+
+
+
+Melnikov & Newman Standards Track [Page 4]
+
+RFC 5092 IMAP URL Scheme November 2007
+
+
+ An authentication mechanism (as used by the IMAP AUTHENTICATE
+ command) can be expressed by adding ";AUTH=<enc-auth-type>" to the
+ end of the user name in an IMAP URL. When such an <enc-auth-type> is
+ indicated, the client SHOULD request appropriate credentials from
+ that mechanism and use the "AUTHENTICATE" command instead of the
+ "LOGIN" command. If no user name is specified, one MUST be obtained
+ from the mechanism or requested from the user/configuration as
+ appropriate.
+
+ The string ";AUTH=*" indicates that the client SHOULD select an
+ appropriate authentication mechanism. (Though the '*' character in
+ this usage is not strictly a delimiter, it is being treated like a
+ sub-delim [URI-GEN] in this instance. It MUST NOT be percent-encoded
+ in this usage, as ";AUTH=%2A" will not match this production.) It
+ MAY use any mechanism listed in the response to the CAPABILITY
+ command (or CAPABILITY response code) or use an out-of-band security
+ service resulting in a PREAUTH connection. If no user name is
+ specified and no appropriate authentication mechanisms are available,
+ the client SHOULD fall back to anonymous login as described above.
+ The behavior prescribed in this section allows a URL that grants
+ read-write access to authorized users and read-only anonymous access
+ to other users.
+
+ If a user name is included with no authentication mechanism, then
+ ";AUTH=*" is assumed.
+
+ Clients must take care when resolving a URL that requires or requests
+ any sort of authentication, since URLs can easily come from untrusted
+ sources. Supplying authentication credentials to the wrong server
+ may compromise the security of the user's account; therefore, the
+ program resolving the URL should meet at least one of the following
+ criteria in this case:
+
+ 1) The URL comes from a trusted source, such as a referral server
+ that the client has validated and trusts according to site policy.
+ Note that user entry of the URL may or may not count as a trusted
+ source, depending on the experience level of the user and site
+ policy.
+
+ 2) Explicit local site policy permits the client to connect to the
+ server in the URL. For example, a company example.com may have a
+ site policy to trust all IMAP server names ending in example.com,
+ whereas such a policy would be unwise for example.edu where random
+ students can set up IMAP servers.
+
+ 3) The user confirms that connecting to that domain name with the
+ specified credentials and/or mechanism is permitted. For example,
+ when using "LOGIN" or SASL PLAIN with Transport Layer Security
+
+
+
+Melnikov & Newman Standards Track [Page 5]
+
+RFC 5092 IMAP URL Scheme November 2007
+
+
+ (TLS), the IMAP URL client presents a dialog box "Is it OK to send
+ your password to server "example.com"? Please be aware the owners
+ of example.com will be able to reuse your password to connect to
+ other servers on your behalf".
+
+ 4) A mechanism is used that validates the server before passing
+ potentially compromising client credentials. For example, a site
+ has a designated TLS certificate used to certify site-trusted IMAP
+ server certificates, and this has been configured explicitly into
+ the IMAP URL client. Another example is use of a Simple
+ Authentication and Security Layer (SASL) mechanism such as
+ DIGEST-MD5 [DIGEST-MD5], which supports mutual authentication.
+
+ 5) An authentication mechanism is used that will not reveal any
+ information to the server that could be used to compromise future
+ connections. Examples are SASL ANONYMOUS [ANONYMOUS] or GSSAPI
+ [GSSAPI].
+
+ URLs that do not include a user name but include an authentication
+ mechanism (";AUTH=<mech>") must be treated with extra care, since for
+ some <mech>s they are more likely to compromise the user's primary
+ account. A URL containing ";AUTH=*" must also be treated with extra
+ care since it might fall back on a weaker security mechanism.
+ Finally, clients are discouraged from using a plaintext password as a
+ fallback with ";AUTH=*" unless the connection has strong encryption.
+
+ A program interpreting IMAP URLs MAY cache open connections to an
+ IMAP server for later reuse. If a URL contains a user name, only
+ connections authenticated as that user may be reused. If a URL does
+ not contain a user name or authentication mechanism, then only an
+ anonymous connection may be reused.
+
+ Note that if unsafe or reserved characters such as " " (space) or ";"
+ are present in the user name or authentication mechanism, they MUST
+ be percent-encoded as described in [URI-GEN].
+
+3.3. Limitations of enc-user
+
+ As per Sections 3.1 and 3.2 of this document, the IMAP URI enc-user
+ has two purposes:
+
+ 1) It provides context for user-specific mailbox paths such as
+ "INBOX" (Section 3.1).
+
+ 2) It specifies that resolution of the URL requires logging in as
+ that user and limits use of that URL to only that user (Section
+ 3.2).
+
+
+
+
+Melnikov & Newman Standards Track [Page 6]
+
+RFC 5092 IMAP URL Scheme November 2007
+
+
+ An obvious limitation of using the same field for both purposes is
+ that the URL can be resolved only by the mailbox owner. In order to
+ avoid this restriction, implementations should use globally unique
+ mailbox names (see Section 3.1) whenever possible.
+
+ Note: There is currently no general way in IMAP of learning a
+ globally unique name for a mailbox. However, by looking at the
+ NAMESPACE [NAMESPACE] command result, it is possible to determine
+ whether or not a mailbox name is globally unique.
+
+ The URLAUTH component overrides the second purpose of the enc-user in
+ the IMAP URI and by default permits the URI to be resolved by any
+ user permitted by the <access> identifier. URLAUTH and <access>
+ identifier are described in Section 6.1.
+
+4. IMAP Server
+
+ An IMAP URL referring to an IMAP server has the following form:
+
+ imap://<iserver>[/]
+
+ This URL type is frequently used to describe a location of an IMAP
+ server, both in referrals and in configuration. It may optionally
+ contain the <iuserinfo> component (see Sections 3 and 11). A program
+ interpreting this URL would issue the standard set of commands it
+ uses to present a view of the content of the IMAP server, as visible
+ to the user described by the "enc-user" part of the <iuserinfo>
+ component, if the "enc-user" part is specified.
+
+5. Lists of Messages
+
+ An IMAP URL referring to a list of messages has the following form:
+
+ imap://<iserver>/<enc-mailbox>[<uidvalidity>][?<enc-search>]
+
+ The <enc-mailbox> field is used as the argument to the IMAP4 "SELECT"
+ or "EXAMINE" command. Note that if unsafe or reserved characters
+ such as " " (space), ";", or "?" are present in <enc-mailbox>, they
+ MUST be percent-encoded as described in [URI-GEN].
+
+ The <uidvalidity> field is optional. If it is present, it MUST be
+ the same as the value of IMAP4 UIDVALIDITY response code at the time
+ the URL was created. This MUST be used by the program interpreting
+ the IMAP URL to determine if the URL is stale. If the IMAP URL is
+ stale, then the program should behave as if the corresponding mailbox
+ doesn't exist.
+
+
+
+
+
+Melnikov & Newman Standards Track [Page 7]
+
+RFC 5092 IMAP URL Scheme November 2007
+
+
+ Note that the <uidvalidity> field is a modifier to the <enc-mailbox>,
+ i.e., it is considered a part of the last "component" (as used in
+ [URI-GEN]) of the <enc-mailbox>. This is significant during relative
+ URI resolution.
+
+ The "?<enc-search>" field is optional. If it is not present, the
+ program interpreting the URL will present the entire content of the
+ mailbox.
+
+ If the "?<enc-search>" field is present, the program interpreting the
+ URL should use the contents of this field as arguments following an
+ IMAP4 SEARCH command. These arguments are likely to contain unsafe
+ characters such as " " (space) (which are likely to be present in the
+ <enc-search>). If unsafe characters are present, they MUST be
+ percent-encoded as described in [URI-GEN].
+
+ Note that quoted strings and non-synchronizing literals [LITERAL+]
+ are allowed in the <enc-search> content; however, synchronizing
+ literals are not allowed, as their presence would effectively mean
+ that the agent interpreting IMAP URLs needs to parse an <enc-search>
+ content, find all synchronizing literals, and perform proper command
+ continuation request handling (see Sections 4.3 and 7 of [IMAP4]).
+
+6. A Specific Message or Message Part
+
+ An IMAP URL referring to a specific message or message part has the
+ following form:
+
+ imap://<iserver>/<enc-mailbox>[<uidvalidity>]<iuid>
+ [<isection>][<ipartial>][<iurlauth>]
+
+ The <enc-mailbox> and [uidvalidity] are as defined in Section 5
+ above.
+
+ If <uidvalidity> is present in this form, it SHOULD be used by the
+ program interpreting the URL to determine if the URL is stale.
+
+ The <iuid> refers to an IMAP4 message Unique Identifier (UID), and it
+ SHOULD be used as the <set> argument to the IMAP4 "UID FETCH"
+ command.
+
+ The <isection> field is optional. If not present, the URL refers to
+ the entire Internet message as returned by the IMAP command "UID
+ FETCH <uid> BODY.PEEK[]". If present, the URL refers to the object
+ returned by a "UID FETCH <uid> BODY.PEEK[<section>]" command. The
+ type of the object may be determined by using a "UID FETCH <uid>
+ BODYSTRUCTURE" command and locating the appropriate part in the
+
+
+
+
+Melnikov & Newman Standards Track [Page 8]
+
+RFC 5092 IMAP URL Scheme November 2007
+
+
+ resulting BODYSTRUCTURE. Note that unsafe characters in [isection]
+ MUST be percent-encoded as described in [URI-GEN].
+
+ The <ipartial> field is optional. If present, it effectively appends
+ "<<partial-range>>" to the end of the UID FETCH BODY.PEEK[<section>]
+ command constructed as described in the previous paragraph. In other
+ words, it allows the client to request a byte range of the
+ message/message part.
+
+ The <iurlauth> field is described in detail in Section 6.1.
+
+6.1. URLAUTH Authorized URL
+
+ URLAUTH authorized URLs are only supported by an IMAP server
+ advertising the URLAUTH IMAP capability [URLAUTH].
+
+6.1.1. Concepts
+
+6.1.1.1. URLAUTH
+
+ URLAUTH is a component, appended at the end of a URL, that conveys
+ authorization to access the data addressed by that URL. It contains
+ an authorized access identifier, an authorization mechanism name, and
+ an authorization token. The authorization token is generated from
+ the URL, the authorized access identifier, authorization mechanism
+ name, and a mailbox access key.
+
+ Note: This specification only allows for the URLAUTH component in
+ IMAP URLs describing a message or its part.
+
+6.1.1.2. Mailbox Access Key
+
+ The mailbox access key is an unpredictable, random string. To ensure
+ unpredictability, the random string with at least 128 bits of entropy
+ is generated by software or hardware (not by the human user).
+
+ Each user has a table of mailboxes and an associated mailbox access
+ key for each mailbox. Consequently, the mailbox access key is per-
+ user and per-mailbox. In other words, two users sharing the same
+ mailbox each have a different mailbox access key for that mailbox,
+ and each mailbox accessed by a single user also has a different
+ mailbox access key.
+
+6.1.1.3. Authorized Access Identifier
+
+ The authorized <access> identifier restricts use of the URLAUTH
+ authorized URL to certain users authorized on the server, as
+ described in Section 6.1.2.
+
+
+
+Melnikov & Newman Standards Track [Page 9]
+
+RFC 5092 IMAP URL Scheme November 2007
+
+
+6.1.1.4. Authorization Mechanism
+
+ The authorization mechanism is the algorithm by which the URLAUTH is
+ generated and subsequently verified, using the mailbox access key.
+
+6.1.1.5. Authorization Token
+
+ The authorization token is a deterministic string of at least 128
+ bits that an entity with knowledge of the secret mailbox access key
+ and URL authorization mechanism can use to verify the URL.
+
+6.1.2. URLAUTH Extensions to IMAP URL
+
+ A specific message or message part IMAP URL can optionally contain
+ ";EXPIRE=<datetime>" and/or ";URLAUTH=<access>:<mech>:<token>".
+
+ When ";EXPIRE=<datetime>" is used, this indicates the latest date and
+ time that the URL is valid. After that date and time, the URL has
+ expired and server implementations MUST reject the URL. If
+ ";EXPIRE=<datetime>" is not used, the URL has no expiration, but can
+ still be revoked using the RESETKEY command [URLAUTH].
+
+ The URLAUTH takes the form ";URLAUTH=<access>:<mech>:<token>", and it
+ MUST be at the end of the URL. It is composed of three parts. The
+ <access> portion provides the authorized access identifiers that may
+ constrain the operations and users that are permitted to use this
+ URL. The <mech> portion provides the authorization mechanism used by
+ the IMAP server to generate the authorization token that follows.
+ The <token> portion provides the authorization token, which can be
+ generated using the GENURLAUTH command [URLAUTH].
+
+ The "submit+" <access> identifier prefix, followed by a userid,
+ indicates that only a userid authorized as a message submission
+ entity on behalf of the specified userid is permitted to use this
+ URL. The IMAP server does not validate the specified userid but does
+ validate that the IMAP session has an authorization identity that is
+ authorized as a message submission entity. The authorized message
+ submission entity MUST validate the userid prior to contacting the
+ IMAP server.
+
+ The "user+" <access> identifier prefix, followed by a userid,
+ indicates that use of this URL is limited to IMAP sessions that are
+ logged in as the specified userid (that is, have authorization
+ identity as that userid).
+
+ Note: If a SASL mechanism that provides both authorization and
+ authentication identifiers is used to authenticate to the IMAP
+ server, the "user+" <access> identifier MUST match the
+
+
+
+Melnikov & Newman Standards Track [Page 10]
+
+RFC 5092 IMAP URL Scheme November 2007
+
+
+ authorization identifier. If the SASL mechanism can't transport
+ the authorization identifier, the "user+" <access> identifier MUST
+ match the authorization identifier derived from the authentication
+ identifier (see [SASL]).
+
+ The "authuser" <access> identifier indicates that use of this URL is
+ limited to authenticated IMAP sessions that are logged in as any
+ non-anonymous user (that is, have authorization identity as a non-
+ anonymous user) of that IMAP server. To restate this: use of this
+ type of URL is prohibited to anonymous IMAP sessions, i.e., any
+ URLFETCH command containing this type of URL issued in an anonymous
+ session MUST return NIL in the URLFETCH response.
+
+ The "anonymous" <access> identifier indicates that use of this URL is
+ not restricted by session authorization identity; that is, any IMAP
+ session in authenticated or selected state (as defined in [IMAP4]),
+ including anonymous sessions, may issue a URLFETCH [URLAUTH] using
+ this URL.
+
+ The authorization token is represented as an ASCII-encoded
+ hexadecimal string, which is used to authorize the URL. The length
+ and the calculation of the authorization token depend upon the
+ mechanism used, but in all cases, the authorization token is at least
+ 128 bits (and therefore at least 32 hexadecimal digits).
+
+ Example:
+
+ <imap://joe@example.com/INBOX/;uid=20/;section=1.2;urlauth=
+ submit+fred:internal:91354a473744909de610943775f92038>
+
+7. Relative IMAP URLs
+
+ Relative IMAP URLs are permitted and are resolved according to the
+ rules defined in [URI-GEN]. In particular, in IMAP URLs parameters
+ (such as ";uid=" or ";section=") are treated as part of the normal
+ path with respect to relative URL resolution.
+
+ [URI-GEN] defines four forms of relative URLs: <inetwork-path>,
+ <iabsolute-path>, <irelative-path>, and <ipath-empty>. Their syntax
+ is defined in Section 11.
+
+ A relative reference that begins with two slash characters is termed
+ a network-path reference (<inetwork-path>); such references are
+ rarely used, because in most cases they can be replaced with an
+ equivalent absolute URL. A relative reference that begins with a
+ single slash character is termed an absolute-path reference
+ (<iabsolute-path>; see also Section 7.1). A relative reference that
+ does not begin with a slash character is termed a relative-path
+
+
+
+Melnikov & Newman Standards Track [Page 11]
+
+RFC 5092 IMAP URL Scheme November 2007
+
+
+ reference (<irelative-path>; see also Section 7.2). The final form
+ is <ipath-empty>, which is "same-document reference" (see Section 4.4
+ of [URI-GEN]).
+
+ The following observations about relative URLs are important:
+
+ The <iauth> grammar element (which is a part of <iuserinfo>, which
+ is, in turn, a part of <iserver>; see Section 3) is considered part
+ of the user name for purposes of resolving relative IMAP URLs. This
+ means that unless a new user name/server specification is included in
+ the relative URL, the authentication mechanism is inherited from the
+ base IMAP URL.
+
+ URLs always use "/" as the hierarchy delimiter for the purpose of
+ resolving paths in relative URLs. IMAP4 permits the use of any
+ hierarchy delimiter in mailbox names. For this reason, relative
+ mailbox paths will only work if the mailbox uses "/" as the hierarchy
+ delimiter. Relative URLs may be used on mailboxes that use other
+ delimiters, but in that case, the entire mailbox name MUST be
+ specified in the relative URL or inherited as a whole from the base
+ URL.
+
+ If an IMAP server allows for mailbox names starting with "./" or
+ "../", ending with "/." or "/..", or containing sequences "/../" or
+ "/./", then such mailbox names MUST be percent-encoded as described
+ in [URI-GEN]. Otherwise, they would be misinterpreted as dot-
+ segments (see Section 3.3 of [URI-GEN]), which are processed
+ specially during the relative path resolution process.
+
+7.1. absolute-path References
+
+ A relative reference that begins with a single slash character is
+ termed an absolute-path reference (see Section 4.2 of [URI-GEN]). If
+ an IMAP server permits mailbox names with a leading "/", then the
+ leading "/" MUST be percent-encoded as described in [URI-GEN].
+ Otherwise, the produced absolute-path reference URI will be
+ misinterpreted as a network-path reference [URI-GEN] described by the
+ <inetwork-path> non-terminal.
+
+7.2. relative-path References
+
+ A relative reference that does not begin with a slash character is
+ termed a relative-path reference [URI-GEN]. Implementations MUST NOT
+ generate or accept relative-path IMAP references.
+
+ See also Section 4.2 of [URI-GEN] for restrictions on relative-path
+ references.
+
+
+
+
+Melnikov & Newman Standards Track [Page 12]
+
+RFC 5092 IMAP URL Scheme November 2007
+
+
+8. Internationalization Considerations
+
+ IMAP4, Section 5.1.3 [IMAP4] includes a convention for encoding non-
+ US-ASCII characters in IMAP mailbox names. Because this convention
+ is private to IMAP, it is necessary to convert IMAP's encoding to one
+ that can be more easily interpreted by a URL display program. For
+ this reason, IMAP's modified UTF-7 encoding for mailboxes MUST be
+ converted to UTF-8 [UTF-8]. Since 8-bit octets are not permitted in
+ URLs, the UTF-8 octets are percent-encoded as required by the URL
+ specification [URI-GEN], Section 2.1. Sample code is included in
+ Appendix A to demonstrate this conversion.
+
+ IMAP user names are UTF-8 strings and MUST be percent-encoded as
+ required by the URL specification [URI-GEN], Section 2.1.
+
+ Also note that IMAP SEARCH criteria can contain non-US-ASCII
+ characters. 8-bit octets in those strings MUST be percent-encoded as
+ required by the URL specification [URI-GEN], Section 2.1.
+
+9. Examples
+
+ The following examples demonstrate how an IMAP4 client program might
+ translate various IMAP4 URLs into a series of IMAP4 commands.
+ Commands sent from the client to the server are prefixed with "C:",
+ and responses sent from the server to the client are prefixed with
+ "S:".
+
+ The URL:
+
+ <imap://minbari.example.org/gray-council;UIDVALIDITY=385759045/;
+ UID=20/;PARTIAL=0.1024>
+
+ may result in the following client commands and server responses:
+
+ <connect to minbari.example.org, port 143>
+ S: * OK [CAPABILITY IMAP4rev1 STARTTLS AUTH=ANONYMOUS] Welcome
+ C: A001 AUTHENTICATE ANONYMOUS
+ S: +
+ C: c2hlcmlkYW5AYmFieWxvbjUuZXhhbXBsZS5vcmc=
+ S: A001 OK Welcome sheridan@babylon5.example.org
+ C: A002 SELECT gray-council
+ <client verifies the UIDVALIDITY matches>
+ C: A003 UID FETCH 20 BODY.PEEK[]<0.1024>
+
+ The URL:
+
+ <imap://psicorp.example.org/~peter/%E6%97%A5%E6%9C%AC%E8%AA%9E/
+ %E5%8F%B0%E5%8C%97>
+
+
+
+Melnikov & Newman Standards Track [Page 13]
+
+RFC 5092 IMAP URL Scheme November 2007
+
+
+ may result in the following client commands:
+
+ <connect to psicorp.example.org, port 143>
+ S: * OK [CAPABILITY IMAP4rev1 STARTTLS AUTH=CRAM-MD5] Welcome
+ C: A001 LOGIN ANONYMOUS bester@psycop.psicorp.example.org
+ C: A002 SELECT ~peter/&ZeVnLIqe-/&U,BTFw-
+ <commands the client uses for viewing the contents of
+ the mailbox>
+
+ The URL:
+
+ <imap://;AUTH=GSSAPI@minbari.example.org/gray-council/;uid=20/
+ ;section=1.2>
+
+ may result in the following client commands:
+
+ <connect to minbari.example.org, port 143>
+ S: * OK Greetings
+ C: A000 CAPABILITY
+ S: * CAPABILITY IMAP4rev1 STARTTLS AUTH=GSSAPI
+ S: A000 OK
+ C: A001 AUTHENTICATE GSSAPI
+ <authentication exchange>
+ C: A002 SELECT gray-council
+ C: A003 UID FETCH 20 BODY.PEEK[1.2]
+
+ If the following relative URL is located in that body part:
+
+ <;section=1.4>
+
+ this could result in the following client commands:
+
+ C: A004 UID FETCH 20 (BODY.PEEK[1.2.MIME]
+ BODY.PEEK[1.MIME]
+ BODY.PEEK[HEADER.FIELDS (Content-Location)])
+ <Client looks for Content-Location headers in
+ result. If no such headers, then it does the following>
+ C: A005 UID FETCH 20 BODY.PEEK[1.4]
+
+ The URL:
+
+ <imap://;AUTH=*@minbari.example.org/gray%20council?
+ SUBJECT%20shadows>
+
+
+
+
+
+
+
+
+Melnikov & Newman Standards Track [Page 14]
+
+RFC 5092 IMAP URL Scheme November 2007
+
+
+ could result in the following:
+
+ <connect to minbari.example.org, port 143>
+ S: * OK Welcome
+ C: A001 CAPABILITY
+ S: * CAPABILITY IMAP4rev1 AUTH=DIGEST-MD5
+ S: A001 OK
+ C: A002 AUTHENTICATE DIGEST-MD5
+ <authentication exchange>
+ S: A002 OK user lennier authenticated
+ C: A003 SELECT "gray council"
+ ...
+ C: A004 SEARCH SUBJECT shadows
+ S: * SEARCH 8 10 13 14 15 16
+ S: A004 OK SEARCH completed
+ C: A005 FETCH 8,10,13:16 ALL
+ ...
+
+ In the example above, the client has implementation-dependent
+ choices. The authentication mechanism could be anything, including
+ PREAUTH. The final FETCH command could fetch more or less
+ information about the messages, depending on what it wishes to
+ display to the user.
+
+ The URL:
+
+ <imap://john;AUTH=*@minbari.example.org/babylon5/personel?
+ charset%20UTF-8%20SUBJECT%20%7B14+%7D%0D%0A%D0%98%D0%B2%
+ D0%B0%D0%BD%D0%BE%D0%B2%D0%B0>
+
+ shows that 8-bit data can be sent using non-synchronizing literals
+ [LITERAL+]. This could result in the following:
+
+ <connect to minbari.example.org, port 143>
+ S: * OK Hi there
+ C: A001 CAPABILITY
+ S: * CAPABILITY IMAP4rev1 LITERAL+ AUTH=DIGEST-MD5
+ S: A001 OK
+ C: A002 AUTHENTICATE DIGEST-MD5
+ <authentication exchange>
+ S: A002 OK user john authenticated
+ C: A003 SELECT babylon5/personel
+ ...
+ C: A004 SEARCH CHARSET UTF-8 SUBJECT {14+}
+ C: XXXXXXXXXXXXXX
+ S: * SEARCH 7 10 12
+ S: A004 OK SEARCH completed
+ C: A005 FETCH 7,10,12 ALL
+
+
+
+Melnikov & Newman Standards Track [Page 15]
+
+RFC 5092 IMAP URL Scheme November 2007
+
+
+ ...
+
+ where XXXXXXXXXXXXXX is 14 bytes of UTF-8 encoded data as specified
+ in the URL above.
+
+9.1. Examples of Relative URLs
+
+ The following absolute-path reference
+
+ </foo/;UID=20/..>
+
+ is the same as
+
+ </foo>
+
+ That is, both of them reference the mailbox "foo" located on the IMAP
+ server described by the corresponding Base URI.
+
+ The following relative-path reference
+
+ <;UID=20>
+
+ references a message with UID in the mailbox specified by the Base
+ URI.
+
+ The following edge case example demonstrates that the ;UIDVALIDITY=
+ modifier is a part of the mailbox name as far as relative URI
+ resolution is concerned:
+
+ <..;UIDVALIDITY=385759045/;UID=20>
+
+ In this example, ".." is not a dot-segment [URI-GEN].
+
+10. Security Considerations
+
+ Security considerations discussed in the IMAP specification [IMAP4]
+ and the URI specification [URI-GEN] are relevant. Security
+ considerations related to authenticated URLs are discussed in Section
+ 3.2 of this document.
+
+ Many email clients store the plaintext password for later use after
+ logging into an IMAP server. Such clients MUST NOT use a stored
+ password in response to an IMAP URL without explicit permission from
+ the user to supply that password to the specified host name.
+
+ Clients resolving IMAP URLs that wish to achieve data confidentiality
+ and/or integrity SHOULD use the STARTTLS command (if supported by the
+
+
+
+
+Melnikov & Newman Standards Track [Page 16]
+
+RFC 5092 IMAP URL Scheme November 2007
+
+
+ server) before starting authentication, or use a SASL mechanism, such
+ as GSSAPI, that provides a confidentiality security layer.
+
+10.1. Security Consideration Specific to URLAUTH Authorized URL
+
+ The "user+<userid>" <access> identifier limits resolution of that URL
+ to a particular userid, whereas the "submit+<userid>" <access>
+ identifier is more general and simply requires that the session be
+ authorized by a user that has been granted a "submit" role within the
+ authentication system. Use of either of these mechanisms limits the
+ scope of the URL. An attacker who cannot authenticate using the
+ appropriate credentials cannot make use of the URL.
+
+ The "authuser" and "anonymous" <access> identifiers do not have this
+ level of protection. These access identifiers are primarily useful
+ for public export of data from an IMAP server, without requiring that
+ it be copied to a web or anonymous FTP server.
+
+ The decision to use the "authuser" <access> identifier should be made
+ with caution. An "authuser" <access> identifier can be used by any
+ authorized user of the IMAP server; therefore, use of this access
+ identifier should be limited to content that may be disclosed to any
+ authorized user of the IMAP server.
+
+ The decision to use the "anonymous" <access> identifier should be
+ made with extreme caution. An "anonymous" <access> identifier can be
+ used by anyone; therefore, use of this access identifier should be
+ limited to content that may be disclosed to anyone.
+
+11. ABNF for IMAP URL Scheme
+
+ Formal syntax is defined using ABNF [ABNF], extending the ABNF rules
+ in Section 9 of [IMAP4]. Elements not defined here can be found in
+ [ABNF], [IMAP4], [IMAPABNF], or [URI-GEN]. Strings are not case
+ sensitive, and free insertion of linear white space is not permitted.
+
+ sub-delims-sh = "!" / "$" / "'" / "(" / ")" /
+ "*" / "+" / ","
+ ;; Same as [URI-GEN] sub-delims,
+ ;; but without ";", "&" and "=".
+
+ uchar = unreserved / sub-delims-sh / pct-encoded
+
+ achar = uchar / "&" / "="
+ ;; Same as [URI-GEN] 'unreserved / sub-delims /
+ ;; pct-encoded', but ";" is disallowed.
+
+ bchar = achar / ":" / "@" / "/"
+
+
+
+Melnikov & Newman Standards Track [Page 17]
+
+RFC 5092 IMAP URL Scheme November 2007
+
+
+ enc-auth-type = 1*achar
+ ; %-encoded version of [IMAP4] "auth-type"
+
+ enc-mailbox = 1*bchar
+ ; %-encoded version of [IMAP4] "mailbox"
+
+ enc-search = 1*bchar
+ ; %-encoded version of [IMAPABNF]
+ ; "search-program". Note that IMAP4
+ ; literals may not be used in
+ ; a "search-program", i.e., only
+ ; quoted or non-synchronizing
+ ; literals (if the server supports
+ ; LITERAL+ [LITERAL+]) are allowed.
+
+ enc-section = 1*bchar
+ ; %-encoded version of [IMAP4] "section-spec"
+
+ enc-user = 1*achar
+ ; %-encoded version of [IMAP4] authorization
+ ; identity or "userid".
+
+ imapurl = "imap://" iserver ipath-query
+ ; Defines an absolute IMAP URL
+
+ ipath-query = ["/" [ icommand ]]
+ ; Corresponds to "path-abempty [ "?" query ]"
+ ; in [URI-GEN]
+
+ Generic syntax for relative URLs is defined in Section 4.2 of
+ [URI-GEN]. For ease of implementation, the relative IMAP URL syntax
+ is defined below:
+
+ imapurl-rel = inetwork-path
+
+ / iabsolute-path
+ / irelative-path
+ / ipath-empty
+
+ inetwork-path = "//" iserver ipath-query
+ ; Corresponds to '"//" authority path-abempty
+ ; [ "?" query ]' in [URI-GEN]
+
+ iabsolute-path = "/" [ icommand ]
+ ; icommand, if present, MUST NOT start with '/'.
+ ;
+ ; Corresponds to 'path-absolute [ "?" query ]'
+ ; in [URI-GEN]
+
+
+
+Melnikov & Newman Standards Track [Page 18]
+
+RFC 5092 IMAP URL Scheme November 2007
+
+
+ irelative-path = imessagelist /
+ imsg-or-part
+ ; Corresponds to 'path-noscheme [ "?" query ]'
+ ; in [URI-GEN]
+
+ imsg-or-part = ( imailbox-ref "/" iuid-only ["/" isection-only]
+ ["/" ipartial-only] ) /
+ ( iuid-only ["/" isection-only]
+ ["/" ipartial-only] ) /
+ ( isection-only ["/" ipartial-only] ) /
+ ipartial-only
+
+ ipath-empty = 0<pchar>
+ ; Zero characters.
+ ; The same-document reference.
+
+ The following three rules are only used in the presence of the IMAP
+ [URLAUTH] extension:
+
+ authimapurl = "imap://" iserver "/" imessagepart
+ ; Same as "imapurl" when "[icommand]" is
+ ; "imessagepart"
+
+ authimapurlfull = authimapurl iurlauth
+ ; Same as "imapurl" when "[icommand]" is
+ ; "imessagepart iurlauth"
+
+ authimapurlrump = authimapurl iurlauth-rump
+
+
+ enc-urlauth = 32*HEXDIG
+
+ iurlauth = iurlauth-rump iua-verifier
+
+ iua-verifier = ":" uauth-mechanism ":" enc-urlauth
+
+ iurlauth-rump = [expire] ";URLAUTH=" access
+
+ access = ("submit+" enc-user) / ("user+" enc-user) /
+ "authuser" / "anonymous"
+
+ expire = ";EXPIRE=" date-time
+ ; date-time is defined in [DATETIME]
+
+ uauth-mechanism = "INTERNAL" / 1*(ALPHA / DIGIT / "-" / ".")
+ ; Case-insensitive.
+ ; New mechanisms MUST be registered with IANA.
+
+
+
+
+Melnikov & Newman Standards Track [Page 19]
+
+RFC 5092 IMAP URL Scheme November 2007
+
+
+ iauth = ";AUTH=" ( "*" / enc-auth-type )
+
+ icommand = imessagelist /
+ imessagepart [iurlauth]
+
+ imailbox-ref = enc-mailbox [uidvalidity]
+
+ imessagelist = imailbox-ref [ "?" enc-search ]
+ ; "enc-search" is [URI-GEN] "query".
+
+ imessagepart = imailbox-ref iuid [isection] [ipartial]
+
+ ipartial = "/" ipartial-only
+
+ ipartial-only = ";PARTIAL=" partial-range
+
+ isection = "/" isection-only
+
+ isection-only = ";SECTION=" enc-section
+
+ iserver = [iuserinfo "@"] host [ ":" port ]
+ ; This is the same as "authority" defined
+ ; in [URI-GEN]. See [URI-GEN] for "host"
+ ; and "port" definitions.
+
+ iuid = "/" iuid-only
+
+ iuid-only = ";UID=" nz-number
+ ; See [IMAP4] for "nz-number" definition
+
+ iuserinfo = enc-user [iauth] / [enc-user] iauth
+ ; conforms to the generic syntax of
+ ; "userinfo" as defined in [URI-GEN].
+
+ partial-range = number ["." nz-number]
+ ; partial FETCH. The first number is
+ ; the offset of the first byte,
+ ; the second number is the length of
+ ; the fragment.
+
+ uidvalidity = ";UIDVALIDITY=" nz-number
+ ; See [IMAP4] for "nz-number" definition
+
+
+
+
+
+
+
+
+
+Melnikov & Newman Standards Track [Page 20]
+
+RFC 5092 IMAP URL Scheme November 2007
+
+
+12. IANA Considerations
+
+ IANA has updated the "imap" definition in the "Uniform Resource
+ Identifier scheme registry" to point to this document.
+
+ The registration template (as per [URI-REG]) is specified in Section
+ 12.1 of this document.
+
+12.1. IANA Registration of imap: URI Scheme
+
+ This section provides the information required to register the imap:
+ URI scheme.
+
+ URI scheme name: imap
+
+ Status: permanent
+
+ URI scheme syntax:
+
+ See Section 11 of [RFC5092].
+
+ URI scheme semantics:
+
+ The imap: URI scheme is used to designate IMAP servers, mailboxes,
+ messages, MIME bodies [MIME] and their parts, and search programs
+ on Internet hosts accessible using the IMAP protocol.
+
+ There is no MIME type associated with this URI.
+
+ Encoding considerations:
+
+ See Section 8 of [RFC5092].
+
+ Applications/protocols that use this URI scheme name:
+
+ The imap: URI is intended to be used by applications that might
+ need access to an IMAP mailstore. Such applications may include
+ (but are not limited to) IMAP-capable web browsers; IMAP clients
+ that wish to access a mailbox, message, or edit a message on the
+ server using [CATENATE]; [SUBMIT] clients and servers that are
+ requested to assemble a complete message on submission using
+ [BURL].
+
+ Interoperability considerations:
+
+ A widely deployed IMAP client Netscape Mail (and possibly
+ Mozilla/Thunderbird/Seamonkey) uses a different imap: scheme
+ internally.
+
+
+
+Melnikov & Newman Standards Track [Page 21]
+
+RFC 5092 IMAP URL Scheme November 2007
+
+
+ Security considerations:
+
+ See Security Considerations (Section 10) of [RFC5092].
+
+ Contact:
+
+ Alexey Melnikov <alexey.melnikov@isode.com>
+
+ Author/Change controller:
+
+ IESG
+
+ References:
+
+ [RFC5092] and [IMAP4].
+
+13. References
+
+13.1. Normative References
+
+ [KEYWORDS] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [IMAP4] Crispin, M., "INTERNET MESSAGE ACCESS PROTOCOL - VERSION
+ 4rev1", RFC 3501, March 2003.
+
+ [IMAPABNF] Melnikov, A. and C. Daboo, "Collected Extensions to
+ IMAP4 ABNF", RFC 4466, April 2006.
+
+ [ABNF] Crocker, D., Ed., and P. Overell, "Augmented BNF for
+ Syntax Specifications: ABNF", RFC 4234, October 2005.
+
+ [MIME] Freed, N. and N. Borenstein, "Multipurpose Internet Mail
+ Extensions (MIME) Part One: Format of Internet Message
+ Bodies", RFC 2045, November 1996.
+
+ [URI-GEN] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform
+ Resource Identifier (URI): Generic Syntax", STD 66, RFC
+ 3986, January 2005.
+
+ [UTF-8] Yergeau, F., "UTF-8, a transformation format of ISO
+ 10646", STD 63, RFC 3629, November 2003.
+
+ [NAMESPACE] Gahrns, M. and C. Newman, "IMAP4 Namespace", RFC 2342,
+ May 1998.
+
+ [LITERAL+] Myers, J., "IMAP4 non-synchronizing literals", RFC 2088,
+ January 1997.
+
+
+
+Melnikov & Newman Standards Track [Page 22]
+
+RFC 5092 IMAP URL Scheme November 2007
+
+
+ [ANONYMOUS] Zeilenga, K., "Anonymous Simple Authentication and
+ Security Layer (SASL) Mechanism", RFC 4505, June 2006.
+
+ [DATETIME] Klyne, G. and C. Newman, "Date and Time on the Internet:
+ Timestamps", RFC 3339, July 2002.
+
+ [URLAUTH] Crispin, M., "Internet Message Access Protocol (IMAP) -
+ URLAUTH Extension", RFC 4467, May 2006.
+
+13.2. Informative References
+
+ [SUBMIT] Gellens, R. and J. Klensin, "Message Submission for
+ Mail", RFC 4409, April 2006.
+
+ [BURL] Newman, C., "Message Submission BURL Extension", RFC
+ 4468, May 2006.
+
+ [CATENATE] Resnick, P., "Internet Message Access Protocol (IMAP)
+ CATENATE Extension", RFC 4469, April 2006.
+
+ [SASL] Melnikov, A., Ed., and K. Zeilenga, Ed., "Simple
+ Authentication and Security Layer (SASL)", RFC 4422,
+ June 2006.
+
+ [GSSAPI] Melnikov, A., Ed., "The Kerberos V5 ("GSSAPI") Simple
+ Authentication and Security Layer (SASL) Mechanism", RFC
+ 4752, November 2006.
+
+ [DIGEST-MD5] Leach, P. and C. Newman, "Using Digest Authentication as
+ a SASL Mechanism", RFC 2831, May 2000.
+
+ [URI-REG] Hansen, T., Hardie, T., and L. Masinter, "Guidelines and
+ Registration Procedures for New URI Schemes", BCP 115,
+ RFC 4395, February 2006.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Melnikov & Newman Standards Track [Page 23]
+
+RFC 5092 IMAP URL Scheme November 2007
+
+
+Appendix A. Sample Code
+
+ Here is sample C source code to convert between URL paths and IMAP
+ mailbox names, taking into account mapping between IMAP's modified
+ UTF-7 [IMAP4] and hex-encoded UTF-8, which is more appropriate for
+ URLs. This code has not been rigorously tested nor does it
+ necessarily behave reasonably with invalid input, but it should serve
+ as a useful example. This code just converts the mailbox portion of
+ the URL and does not deal with parameters, query, or server
+ components of the URL.
+
+/* Copyright (C) The IETF Trust (2007). This version of
+ sample C code is part of RFC XXXX; see the RFC itself
+ for full legal notices.
+
+ Regarding this sample C code (or any portion of it), the authors
+ make no guarantees and are not responsible for any damage
+ resulting from its use. The authors grant irrevocable permission
+ to anyone to use, modify, and distribute it in any way that does
+ not diminish the rights of anyone else to use, modify, and
+ distribute it, provided that redistributed derivative works do
+ not contain misleading author or version information.
+
+ Derivative works need not be licensed under similar terms.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+/* hexadecimal lookup table */
+static const char hex[] = "0123456789ABCDEF";
+
+#define XX 127
+/*
+ * Table for decoding hexadecimal in %encoding
+ */
+static const char index_hex[256] = {
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,XX,XX, XX,XX,XX,XX,
+ XX,10,11,12, 13,14,15,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,10,11,12, 13,14,15,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+
+
+
+Melnikov & Newman Standards Track [Page 24]
+
+RFC 5092 IMAP URL Scheme November 2007
+
+
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+ XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
+};
+#define HEXCHAR(c) (index_hex[(unsigned char)(c)])
+
+/* "gen-delims" excluding "/" but including "%" */
+#define GENERAL_DELIMS_NO_SLASH ":?#[]@" "%"
+
+/* "gen-delims" (excluding "/", but including "%")
+ plus subset of "sub-delims" */
+#define GENERAL_UNSAFE_NO_SLASH GENERAL_DELIMS_NO_SLASH ";&=+"
+#define OTHER_UNSAFE " \"<>\\^`{|}"
+
+/* URL unsafe printable characters */
+static const char mailbox_url_unsafe[] = GENERAL_UNSAFE_NO_SLASH
+ OTHER_UNSAFE;
+
+/* UTF7 modified base64 alphabet */
+static const char base64chars[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
+
+#define UNDEFINED 64
+
+/* UTF16 definitions */
+#define UTF16MASK 0x03FFUL
+#define UTF16SHIFT 10
+#define UTF16BASE 0x10000UL
+#define UTF16HIGHSTART 0xD800UL
+#define UTF16HIGHEND 0xDBFFUL
+#define UTF16LOSTART 0xDC00UL
+#define UTF16LOEND 0xDFFFUL
+
+/* Convert an IMAP mailbox to a URL path
+ * dst needs to have roughly 4 times the storage space of src
+ * Hex encoding can triple the size of the input
+ * UTF-7 can be slightly denser than UTF-8
+ * (worst case: 8 octets UTF-7 becomes 9 octets UTF-8)
+ */
+void MailboxToURL(char *dst, char *src)
+{
+ unsigned char c, i, bitcount;
+ unsigned long ucs4, utf16, bitbuf;
+ unsigned char base64[256], utf8[6];
+
+ /* initialize modified base64 decoding table */
+
+
+
+Melnikov & Newman Standards Track [Page 25]
+
+RFC 5092 IMAP URL Scheme November 2007
+
+
+ memset(base64, UNDEFINED, sizeof (base64));
+ for (i = 0; i < sizeof (base64chars); ++i) {
+ base64[(int) base64chars[i]] = i;
+ }
+
+ /* loop until end of string */
+ while (*src != '\0') {
+ c = *src++;
+ /* deal with literal characters and &- */
+ if (c != '&' || *src == '-') {
+ /* NB: There are no "URL safe" characters after the '~' */
+ if (c < ' ' || c > '~' ||
+ strchr(mailbox_url_unsafe, c) != NULL) {
+ /* hex encode if necessary */
+ dst[0] = '%';
+ dst[1] = hex[c >> 4];
+ dst[2] = hex[c & 0x0f];
+ dst += 3;
+ } else {
+ /* encode literally */
+ *dst++ = c;
+ }
+ /* skip over the '-' if this is an &- sequence */
+ if (c == '&') ++src;
+
+ } else {
+ /* convert modified UTF-7 -> UTF-16 -> UCS-4 -> UTF-8 -> HEX */
+ bitbuf = 0;
+ bitcount = 0;
+ ucs4 = 0;
+ while ((c = base64[(unsigned char) *src]) != UNDEFINED) {
+ ++src;
+ bitbuf = (bitbuf << 6) | c;
+ bitcount += 6;
+ /* enough bits for a UTF-16 character? */
+ if (bitcount >= 16) {
+ bitcount -= 16;
+ utf16 = (bitcount ? bitbuf >> bitcount
+ : bitbuf) & 0xffff;
+ /* convert UTF16 to UCS4 */
+ if
+ (utf16 >= UTF16HIGHSTART && utf16 <= UTF16HIGHEND) {
+ ucs4 = (utf16 - UTF16HIGHSTART) << UTF16SHIFT;
+ continue;
+ } else if
+ (utf16 >= UTF16LOSTART && utf16 <= UTF16LOEND) {
+ ucs4 += utf16 - UTF16LOSTART + UTF16BASE;
+ } else {
+
+
+
+Melnikov & Newman Standards Track [Page 26]
+
+RFC 5092 IMAP URL Scheme November 2007
+
+
+ ucs4 = utf16;
+ }
+ /* convert UTF-16 range of UCS4 to UTF-8 */
+ if (ucs4 <= 0x7fUL) {
+ utf8[0] = (unsigned char) ucs4;
+ i = 1;
+ } else if (ucs4 <= 0x7ffUL) {
+ utf8[0] = 0xc0 | (unsigned char) (ucs4 >> 6);
+ utf8[1] = 0x80 | (unsigned char) (ucs4 & 0x3f);
+ i = 2;
+ } else if (ucs4 <= 0xffffUL) {
+ utf8[0] = 0xe0 | (unsigned char) (ucs4 >> 12);
+ utf8[1] = 0x80 | (unsigned char) ((ucs4 >> 6) & 0x3f);
+ utf8[2] = 0x80 | (unsigned char) (ucs4 & 0x3f);
+ i = 3;
+ } else {
+ utf8[0] = 0xf0 | (unsigned char) (ucs4 >> 18);
+ utf8[1] = 0x80 | (unsigned char) ((ucs4 >> 12) & 0x3f);
+ utf8[2] = 0x80 | (unsigned char) ((ucs4 >> 6) & 0x3f);
+ utf8[3] = 0x80 | (unsigned char) (ucs4 & 0x3f);
+ i = 4;
+ }
+ /* convert utf8 to hex */
+ for (c = 0; c < i; ++c) {
+ dst[0] = '%';
+ dst[1] = hex[utf8[c] >> 4];
+ dst[2] = hex[utf8[c] & 0x0f];
+ dst += 3;
+ }
+ }
+ }
+ /* skip over trailing '-' in modified UTF-7 encoding */
+ if (*src == '-') ++src;
+ }
+ }
+ /* terminate destination string */
+ *dst = '\0';
+}
+
+/* Convert hex coded UTF-8 URL path to modified UTF-7 IMAP mailbox
+ * dst should be about twice the length of src to deal with non-hex
+ * coded URLs
+ */
+int URLtoMailbox(char *dst, char *src)
+{
+ unsigned int utf8pos = 0;
+ unsigned int utf8total, i, c, utf7mode, bitstogo, utf16flag;
+ unsigned long ucs4 = 0, bitbuf = 0;
+
+
+
+Melnikov & Newman Standards Track [Page 27]
+
+RFC 5092 IMAP URL Scheme November 2007
+
+
+ utf7mode = 0; /* is the output UTF7 currently in base64 mode? */
+ utf8total = 0; /* how many octets is the current input UTF-8 char;
+ 0 == between characters */
+ bitstogo = 0; /* bits that need to be encoded into base64; if
+ bitstogo != 0 then utf7mode == 1 */
+ while ((c = (unsigned char)*src) != '\0') {
+ ++src;
+ /* undo hex-encoding */
+ if (c == '%' && src[0] != '\0' && src[1] != '\0') {
+ c = HEXCHAR(src[0]);
+ i = HEXCHAR(src[1]);
+ if (c == XX || i == XX) {
+ return 0;
+ } else {
+ c = (char)((c << 4) | i);
+ }
+ src += 2;
+ }
+ /* normal character? */
+ if (c >= ' ' && c <= '~') {
+ /* switch out of UTF-7 mode */
+ if (utf7mode) {
+ if (bitstogo) {
+ *dst++ = base64chars[(bitbuf << (6 - bitstogo)) & 0x3F];
+ }
+ *dst++ = '-';
+ utf7mode = 0;
+ bitstogo = bitbuf = 0;
+ }
+ *dst++ = c;
+ /* encode '&' as '&-' */
+ if (c == '&') {
+ *dst++ = '-';
+ }
+ continue;
+ }
+ /* switch to UTF-7 mode */
+ if (!utf7mode) {
+ *dst++ = '&';
+ utf7mode = 1;
+ }
+ /* Encode US-ASCII characters as themselves */
+ if (c < 0x80) {
+ ucs4 = c;
+ utf8total = 1;
+ } else if (utf8total) {
+ /* this is a subsequent octet of a multi-octet character */
+ /* save UTF8 bits into UCS4 */
+
+
+
+Melnikov & Newman Standards Track [Page 28]
+
+RFC 5092 IMAP URL Scheme November 2007
+
+
+ ucs4 = (ucs4 << 6) | (c & 0x3FUL);
+ if (++utf8pos < utf8total) {
+ continue;
+ }
+ } else {
+ /* this is the first octet of a multi-octet character */
+ utf8pos = 1;
+ if (c < 0xE0) {
+ utf8total = 2;
+ ucs4 = c & 0x1F;
+ } else if (c < 0xF0) {
+ utf8total = 3;
+ ucs4 = c & 0x0F;
+ } else {
+ /* NOTE: can't convert UTF8 sequences longer than 4 */
+ utf8total = 4;
+ ucs4 = c & 0x03;
+ }
+ continue;
+ }
+ /* Finished with UTF-8 character. Make sure it isn't an
+ overlong sequence. If it is, return failure. */
+ if ((ucs4 < 0x80 && utf8total > 1) ||
+ (ucs4 < 0x0800 && utf8total > 2) ||
+ (ucs4 < 0x00010000 && utf8total > 3) ||
+ (ucs4 < 0x00200000 && utf8total > 4) ||
+ (ucs4 < 0x04000000 && utf8total > 5) ||
+ (ucs4 < 0x80000000 && utf8total > 6)) {
+ return 0;
+ }
+ /* loop to split ucs4 into two utf16 chars if necessary */
+ utf8total = 0;
+ do {
+ if (ucs4 >= UTF16BASE) {
+ ucs4 -= UTF16BASE;
+ bitbuf = (bitbuf << 16) | ((ucs4 >> UTF16SHIFT)
+ + UTF16HIGHSTART);
+ ucs4 = (ucs4 & UTF16MASK) + UTF16LOSTART;
+ utf16flag = 1;
+ } else {
+ bitbuf = (bitbuf << 16) | ucs4;
+ utf16flag = 0;
+ }
+ bitstogo += 16;
+ /* spew out base64 */
+ while (bitstogo >= 6) {
+ bitstogo -= 6;
+ *dst++ = base64chars[(bitstogo ? (bitbuf >> bitstogo)
+
+
+
+Melnikov & Newman Standards Track [Page 29]
+
+RFC 5092 IMAP URL Scheme November 2007
+
+
+ : bitbuf)
+ & 0x3F];
+ }
+ } while (utf16flag);
+ }
+ /* if in UTF-7 mode, finish in ASCII */
+ if (utf7mode) {
+ if (bitstogo) {
+ *dst++ = base64chars[(bitbuf << (6 - bitstogo)) & 0x3F];
+ }
+ *dst++ = '-';
+ }
+ /* tie off string */
+ *dst = '\0';
+ return 1;
+}
+
+Appendix B. List of Changes since RFC 2192
+
+ Updated boilerplate, list of editor's, etc.
+ Updated references.
+ Updated ABNF not to use _, to use SP instead of SPACE, etc.
+ Updated example domains to use example.org.
+ Fixed ABNF error in "imessagelist" non-terminal.
+ Updated ABNF, due to changes in RFC 3501, RFC 4466, and RFC 3986.
+ Renamed "iuserauth" non-terminal to <iuserinfo>.
+ Clarified that the userinfo component describes both authorization
+ identity and mailbox naming scope.
+ Allow for non-synchronizing literals in "enc-search".
+ Added "ipartial" specifier that denotes a partial FETCH.
+ Moved URLAUTH text from RFC 4467 to this document.
+ Updated ABNF for the whole server to allow missing trailing "/"
+ (e.g., "imap://imap.example.com" is now valid and is the same as
+ "imap://imap.example.com/").
+ Clarified how relative-path references are constructed.
+ Added more examples demonstrating relative-path references.
+ Added rules for relative URLs and restructured ABNF as the result.
+ Removed text on use of relative URLs in MHTML.
+ Added examples demonstrating security considerations when resolving
+ URLs.
+ Recommend usage of STARTTLS/SASL security layer to protect
+ confidential data.
+ Removed some advices about connection reuse that were incorrect.
+ Removed URLs referencing a list of mailboxes, as this feature
+ hasn't seen any deployments.
+ Clarified that user name "anonymous" is case-insensitive.
+
+
+
+
+
+Melnikov & Newman Standards Track [Page 30]
+
+RFC 5092 IMAP URL Scheme November 2007
+
+
+Appendix C. List of Changes since RFC 4467
+
+ Renamed <mechanism> to <uauth-mechanism>. Restructured ABNF.
+
+Appendix D. Acknowledgments
+
+ Text describing URLAUTH was lifted from [URLAUTH] by Mark Crispin.
+
+ Stephane H. Maes contributed some ideas to this document; he also
+ co-edited early versions of this document.
+
+ The editors would like to thank Mark Crispin, Ken Murchison, Ted
+ Hardie, Zoltan Ordogh, Dave Cridland, Kjetil Torgrim Homme, Lisa
+ Dusseault, Spencer Dawkins, Filip Navara, Shawn M. Emery, Sam
+ Hartman, Russ Housley, and Lars Eggert for the time they devoted to
+ reviewing this document and/or for the comments received.
+
+Authors' Addresses
+
+ Chris Newman (Author/Editor)
+ Sun Microsystems
+ 3401 Centrelake Dr., Suite 410
+ Ontario, CA 91761
+ EMail: chris.newman@sun.com
+
+ Alexey Melnikov (Editor)
+ Isode Limited
+ 5 Castle Business Village
+ 36 Station Road
+ Hampton, Middlesex
+ TW12 2BX, UK
+ EMail: Alexey.Melnikov@isode.com
+ URI: http://www.melnikov.ca/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Melnikov & Newman Standards Track [Page 31]
+
+RFC 5092 IMAP URL Scheme November 2007
+
+
+Full Copyright Statement
+
+ Copyright (C) The IETF Trust (2007).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY, THE IETF TRUST AND
+ THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF
+ THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+
+
+
+
+
+
+
+
+
+
+
+Melnikov & Newman Standards Track [Page 32]
+
diff --git a/imap/docs/rfc/rfc5161.txt b/imap/docs/rfc/rfc5161.txt
new file mode 100644
index 00000000..13bbbf74
--- /dev/null
+++ b/imap/docs/rfc/rfc5161.txt
@@ -0,0 +1,395 @@
+
+
+
+
+
+
+Network Working Group A. Gulbrandsen, Ed.
+Request for Comments: 5161 Oryx Mail Systems GmbH
+Category: Standards Track A. Melnikov, Ed.
+ Isode Limited
+ March 2008
+
+
+ The IMAP ENABLE Extension
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Abstract
+
+ Most IMAP extensions are used by the client when it wants to and the
+ server supports it. However, a few extensions require the server to
+ know whether a client supports that extension. The ENABLE extension
+ allows an IMAP client to say which extensions it supports.
+
+1. Overview
+
+ Several IMAP extensions allow the server to return unsolicited
+ responses specific to these extensions in certain circumstances.
+ However, servers cannot send those unsolicited responses until they
+ know that the clients support such extensions and thus won't choke on
+ the extension response data.
+
+ Up until now, extensions have typically stated that a server cannot
+ send the unsolicited responses until after the client has used a
+ command with the extension data (i.e., at that point the server knows
+ the client is aware of the extension). CONDSTORE ([RFC4551]),
+ ANNOTATE ([ANNOTATE]), and some extensions under consideration at the
+ moment use various commands to enable server extensions. For
+ example, CONDSTORE uses a SELECT or FETCH parameter, and ANNOTATE
+ uses a side effect of FETCH.
+
+ The ENABLE extension provides an explicit indication from the client
+ that it supports particular extensions. This is done using a new
+ ENABLE command.
+
+ An IMAP server that supports ENABLE advertises this by including the
+ word ENABLE in its capability list.
+
+
+
+
+Gulbrandsen & Melnikov Standards Track [Page 1]
+
+RFC 5161 The IMAP ENABLE Extension March 2008
+
+
+ Most IMAP extensions do not require the client to enable the
+ extension in any way.
+
+2. Conventions Used in This Document
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in [RFC2119].
+
+ Formal syntax is defined by [RFC5234] and [RFC3501].
+
+ Example lines prefaced by "C:" are sent by the client and ones
+ prefaced by "S:" by the server. The five characters [...] means that
+ something has been elided.
+
+3. Protocol Changes
+
+3.1. The ENABLE Command
+
+ Arguments: capability names
+
+ Result: OK: Relevant capabilities enabled
+ BAD: No arguments, or syntax error in an argument
+
+ The ENABLE command takes a list of capability names, and requests the
+ server to enable the named extensions. Once enabled using ENABLE,
+ each extension remains active until the IMAP connection is closed.
+ For each argument, the server does the following:
+
+ - If the argument is not an extension known to the server, the server
+ MUST ignore the argument.
+
+ - If the argument is an extension known to the server, and it is not
+ specifically permitted to be enabled using ENABLE, the server MUST
+ ignore the argument. (Note that knowing about an extension doesn't
+ necessarily imply supporting that extension.)
+
+ - If the argument is an extension that is supported by the server and
+ that needs to be enabled, the server MUST enable the extension for
+ the duration of the connection. At present, this applies only to
+ CONDSTORE ([RFC4551]). Note that once an extension is enabled,
+ there is no way to disable it.
+
+ If the ENABLE command is successful, the server MUST send an untagged
+ ENABLED response (see Section 3.2).
+
+
+
+
+
+
+Gulbrandsen & Melnikov Standards Track [Page 2]
+
+RFC 5161 The IMAP ENABLE Extension March 2008
+
+
+ Clients SHOULD only include extensions that need to be enabled by the
+ server. At the time of publication, CONDSTORE is the only such
+ extension (i.e., ENABLE CONDSTORE is an additional "CONDSTORE
+ enabling command" as defined in [RFC4551]). Future RFCs may add to
+ this list.
+
+ The ENABLE command is only valid in the authenticated state (see
+ [RFC3501]), before any mailbox is selected. Clients MUST NOT issue
+ ENABLE once they SELECT/EXAMINE a mailbox; however, server
+ implementations don't have to check that no mailbox is selected or
+ was previously selected during the duration of a connection.
+
+ The ENABLE command can be issued multiple times in a session. It is
+ additive; i.e., "ENABLE a b", followed by "ENABLE c" is the same as a
+ single command "ENABLE a b c". When multiple ENABLE commands are
+ issued, each corresponding ENABLED response SHOULD only contain
+ extensions enabled by the corresponding ENABLE command.
+
+ There are no limitations on pipelining ENABLE. For example, it is
+ possible to send ENABLE and then immediately SELECT, or a LOGIN
+ immediately followed by ENABLE.
+
+ The server MUST NOT change the CAPABILITY list as a result of
+ executing ENABLE; i.e., a CAPABILITY command issued right after an
+ ENABLE command MUST list the same capabilities as a CAPABILITY
+ command issued before the ENABLE command. This is demonstrated in
+ the following example:
+
+ C: t1 CAPABILITY
+ S: * CAPABILITY IMAP4rev1 ID LITERAL+ ENABLE X-GOOD-IDEA
+ S: t1 OK foo
+ C: t2 ENABLE CONDSTORE X-GOOD-IDEA
+ S: * ENABLED X-GOOD-IDEA
+ S: t2 OK foo
+ C: t3 CAPABILITY
+ S: * CAPABILITY IMAP4rev1 ID LITERAL+ ENABLE X-GOOD-IDEA
+ S: t3 OK foo again
+
+ In the following example, the client enables CONDSTORE:
+
+ C: a1 ENABLE CONDSTORE
+ S: * ENABLED CONDSTORE
+ S: a1 OK Conditional Store enabled
+
+
+
+
+
+
+
+
+Gulbrandsen & Melnikov Standards Track [Page 3]
+
+RFC 5161 The IMAP ENABLE Extension March 2008
+
+
+3.2. The ENABLED Response
+
+ Contents: capability listing
+
+ The ENABLED response occurs as a result of an ENABLE command. The
+ capability listing contains a space-separated listing of capability
+ names that the server supports and that were successfully enabled.
+ The ENABLED response may contain no capabilities, which means that no
+ extensions listed by the client were successfully enabled.
+
+3.3. Note to Designers of Extensions That May Use the ENABLE Command
+
+ Designers of IMAP extensions are discouraged from creating extensions
+ that require ENABLE unless there is no good alternative design.
+ Specifically, extensions that cause potentially incompatible behavior
+ changes to deployed server responses (and thus benefit from ENABLE)
+ have a higher complexity cost than extensions that do not.
+
+4. Formal Syntax
+
+ The following syntax specification uses the Augmented Backus-Naur
+ Form (ABNF) notation as specified in [RFC5234] including the core
+ rules in Appendix B.1. [RFC3501] defines the non-terminals
+ "capability" and "command-any".
+
+ Except as noted otherwise, all alphabetic characters are
+ case-insensitive. The use of upper or lower case characters to
+ define token strings is for editorial clarity only. Implementations
+ MUST accept these strings in a case-insensitive fashion.
+
+ capability =/ "ENABLE"
+
+ command-any =/ "ENABLE" 1*(SP capability)
+
+ response-data =/ "*" SP enable-data CRLF
+
+ enable-data = "ENABLED" *(SP capability)
+
+5. Security Considerations
+
+ It is believed that this extension doesn't add any security
+ considerations that are not already present in the base IMAP protocol
+ [RFC3501].
+
+6. IANA Considerations
+
+ The IANA has added ENABLE to the IMAP4 Capabilities Registry.
+
+
+
+
+Gulbrandsen & Melnikov Standards Track [Page 4]
+
+RFC 5161 The IMAP ENABLE Extension March 2008
+
+
+7. Acknowledgments
+
+ The editors would like to thank Randy Gellens, Chris Newman, Peter
+ Coates, Dave Cridland, Mark Crispin, Ned Freed, Dan Karp, Cyrus
+ Daboo, Ken Murchison, and Eric Burger for comments and corrections.
+ However, this doesn't necessarily mean that they endorse this
+ extension, agree with all details, or are responsible for errors
+ introduced by the editors.
+
+8. Normative References
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC3501] Crispin, M., "INTERNET MESSAGE ACCESS PROTOCOL - VERSION
+ 4rev1", RFC 3501, March 2003.
+
+ [RFC5234] Crocker, D., Ed., and P. Overell, "Augmented BNF for
+ Syntax Specifications: ABNF", STD 68, RFC 5234, January
+ 2008.
+
+ [RFC4551] Melnikov, A. and S. Hole, "IMAP Extension for Conditional
+ STORE Operation or Quick Flag Changes Resynchronization",
+ RFC 4551, June 2006.
+
+9. Informative References
+
+ [ANNOTATE] Daboo, C. and R. Gellens, "IMAP ANNOTATE Extension", Work
+ in Progress, August 2006.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Gulbrandsen & Melnikov Standards Track [Page 5]
+
+RFC 5161 The IMAP ENABLE Extension March 2008
+
+
+Editors' Addresses
+
+ Arnt Gulbrandsen
+ Oryx Mail Systems GmbH
+ Schweppermannstr. 8
+ D-81671 Muenchen
+ Germany
+
+ Fax: +49 89 4502 9758
+ EMail: arnt@oryx.com
+
+
+ Alexey Melnikov
+ Isode Ltd
+ 5 Castle Business Village
+ 36 Station Road
+ Hampton, Middlesex TW12 2BX
+ UK
+
+ EMail: Alexey.Melnikov@isode.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Gulbrandsen & Melnikov Standards Track [Page 6]
+
+RFC 5161 The IMAP ENABLE Extension March 2008
+
+
+Full Copyright Statement
+
+ Copyright (C) The IETF Trust (2008).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY, THE IETF TRUST AND
+ THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF
+ THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+
+
+
+
+
+
+
+
+
+
+
+Gulbrandsen & Melnikov Standards Track [Page 7]
+
diff --git a/imap/docs/rfc/rfc5162.txt b/imap/docs/rfc/rfc5162.txt
new file mode 100644
index 00000000..305c54fb
--- /dev/null
+++ b/imap/docs/rfc/rfc5162.txt
@@ -0,0 +1,1291 @@
+
+
+
+
+
+
+Network Working Group A. Melnikov
+Request for Comments: 5162 D. Cridland
+Category: Standards Track Isode Ltd
+ C. Wilson
+ Nokia
+ March 2008
+
+
+ IMAP4 Extensions for Quick Mailbox Resynchronization
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Abstract
+
+ This document defines an IMAP4 extension, which gives an IMAP client
+ the ability to quickly resynchronize any previously opened mailbox as
+ part of the SELECT command, without the need for server-side state or
+ additional client round-trips. This extension also introduces a new
+ response that allows for a more compact representation of a list of
+ expunged messages (and always includes the Unique Identifiers (UIDs)
+ expunged).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Melnikov, et al. Standards Track [Page 1]
+
+RFC 5162 IMAP Quick Mailbox Resync March 2008
+
+
+Table of Contents
+
+ 1. Introduction and Overview . . . . . . . . . . . . . . . . . . 2
+ 2. Requirements Notation . . . . . . . . . . . . . . . . . . . . 4
+ 3. IMAP Protocol Changes . . . . . . . . . . . . . . . . . . . . 4
+ 3.1. QRESYNC Parameter to SELECT/EXAMINE . . . . . . . . . . . 4
+ 3.2. VANISHED UID FETCH Modifier . . . . . . . . . . . . . . . 8
+ 3.3. EXPUNGE Command . . . . . . . . . . . . . . . . . . . . . 10
+ 3.4. CLOSE Command . . . . . . . . . . . . . . . . . . . . . . 11
+ 3.5. UID EXPUNGE Command . . . . . . . . . . . . . . . . . . . 11
+ 3.6. VANISHED Response . . . . . . . . . . . . . . . . . . . . 12
+ 3.7. CLOSED Response Code . . . . . . . . . . . . . . . . . . . 15
+ 4. Server Implementation Considerations . . . . . . . . . . . . . 15
+ 4.1. Server Implementations That Don't Store Extra State . . . 15
+ 4.2. Server Implementations Storing Minimal State . . . . . . . 16
+ 4.3. Additional State Required on the Server . . . . . . . . . 16
+ 5. Updated Synchronization Sequence . . . . . . . . . . . . . . . 17
+ 6. Formal Syntax . . . . . . . . . . . . . . . . . . . . . . . . 19
+ 7. Security Considerations . . . . . . . . . . . . . . . . . . . 20
+ 8. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 21
+ 9. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 21
+ 10. References . . . . . . . . . . . . . . . . . . . . . . . . . . 21
+ 10.1. Normative References . . . . . . . . . . . . . . . . . . . 21
+ 10.2. Informative References . . . . . . . . . . . . . . . . . . 22
+
+1. Introduction and Overview
+
+ The [CONDSTORE] extension gives a disconnected client the ability to
+ quickly resynchronize IMAP flag changes for previously seen messages.
+ This can be done using the CHANGEDSINCE FETCH modifier once a mailbox
+ is opened. In order for the client to discover which messages have
+ been expunged, the client still has to issue a UID FETCH or a UID
+ SEARCH command. This document defines an extension to [CONDSTORE]
+ that allows a reconnecting client to perform full resynchronization,
+ including discovery of expunged messages, in a single round-trip.
+ This extension also introduces a new response, VANISHED, that allows
+ for a more compact representation of a list of expunged messages.
+
+ This extension can be useful for mobile clients that can experience
+ frequent disconnects caused by environmental factors (battery life,
+ signal strength, etc.). Such clients need a way to quickly reconnect
+ to the IMAP server, while minimizing delay experienced by the user as
+ well as the amount of traffic (and hence the expense) generated by
+ resynchronization.
+
+
+
+
+
+
+
+Melnikov, et al. Standards Track [Page 2]
+
+RFC 5162 IMAP Quick Mailbox Resync March 2008
+
+
+ By extending the SELECT command to perform the additional
+ resynchronization, this also allows clients to reduce concurrent
+ connections to the IMAP server held purely for the sake of avoiding
+ the resynchronization.
+
+ The quick resync IMAP extension is present if an IMAP4 server returns
+ "QRESYNC" as one of the supported capabilities to the CAPABILITY
+ command.
+
+ Servers supporting this extension MUST implement and advertise
+ support for the [ENABLE] IMAP extension. Also, the presence of the
+ "QRESYNC" capability implies support for the [CONDSTORE] IMAP
+ extension even if the CONDSTORE capability isn't advertised. A
+ server compliant with this specification is REQUIREd to support
+ "ENABLE QRESYNC" and "ENABLE QRESYNC CONDSTORE" (which are "CONDSTORE
+ enabling commands", as defined in [CONDSTORE], and have identical
+ results), but there is no requirement for a compliant server to
+ support "ENABLE CONDSTORE" by itself. The "ENABLE QRESYNC"/"ENABLE
+ QRESYNC CONDSTORE" command also tells the server that it SHOULD start
+ sending VANISHED responses (see Section 3.6) instead of EXPUNGE
+ responses. This change remains in effect until the connection is
+ closed.
+
+ For compatibility with clients that only support the [CONDSTORE] IMAP
+ extension, servers SHOULD advertise CONDSTORE in the CAPABILITY
+ response as well.
+
+ A client making use of this extension MUST issue "ENABLE QRESYNC"
+ once it is authenticated. A server MUST respond with a tagged BAD
+ response if the QRESYNC parameter to the SELECT/EXAMINE command or
+ the VANISHED UID FETCH modifier is specified and the client hasn't
+ issued "ENABLE QRESYNC" in the current connection.
+
+ This document puts additional requirements on a server implementing
+ the [CONDSTORE] extension. Each mailbox that supports persistent
+ storage of mod-sequences, i.e., for which the server has sent a
+ HIGHESTMODSEQ untagged OK response code on a successful SELECT/
+ EXAMINE, MUST increment the per-mailbox mod-sequence when one or more
+ messages are expunged due to EXPUNGE, UID EXPUNGE or CLOSE; the
+ server MUST associate the incremented mod-sequence with the UIDs of
+ the expunged messages.
+
+ A client that supports CONDSTORE but not this extension might
+ resynchronize a mailbox and discover that its HIGHESTMODSEQ has
+ increased from the value cached by the client. If the increase is
+ only due to messages having been expunged since the client last
+ synchronized, the client is likely to send a FETCH ... CHANGEDSINCE
+ command that returns no data. Thus, a client that supports CONDSTORE
+
+
+
+Melnikov, et al. Standards Track [Page 3]
+
+RFC 5162 IMAP Quick Mailbox Resync March 2008
+
+
+ but not this extension might incur a penalty of an unneeded round-
+ trip when resynchronizing some mailboxes (those that have had
+ messages expunged but no flag changes since the last
+ synchronization).
+
+ This extra round-trip is only incurred by clients that support
+ CONDSTORE but not this extension, and only when a mailbox has had
+ messages expunged but no flag changes to non-expunged messages.
+ Since CONDSTORE is a relatively new extension, it is thought likely
+ that clients that support it will also support this extension.
+
+2. Requirements Notation
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in [RFC2119].
+
+ In examples, "C:" and "S:" indicate lines sent by the client and
+ server respectively. If a single "C:" or "S:" label applies to
+ multiple lines, then the line breaks between those lines are for
+ editorial clarity only and are not part of the actual protocol
+ exchange. The five characters [...] means that something has been
+ elided.
+
+ Understanding of the IMAP message sequence numbers and UIDs and the
+ EXPUNGE response [RFC3501] is essential when reading this document.
+
+3. IMAP Protocol Changes
+
+3.1. QRESYNC Parameter to SELECT/EXAMINE
+
+ The Quick Resynchronization parameter to SELECT/EXAMINE commands has
+ four arguments:
+
+ o the last known UIDVALIDITY,
+
+ o the last known modification sequence,
+
+ o the optional set of known UIDs, and
+
+ o an optional parenthesized list of known sequence ranges and their
+ corresponding UIDs.
+
+ A server MUST respond with a tagged BAD response if the Quick
+ Resynchronization parameter to SELECT/EXAMINE command is specified
+ and the client hasn't issued "ENABLE QRESYNC" in the current
+ connection.
+
+
+
+
+Melnikov, et al. Standards Track [Page 4]
+
+RFC 5162 IMAP Quick Mailbox Resync March 2008
+
+
+ Before opening the specified mailbox, the server verifies all
+ arguments for syntactic validity. If any parameter is not
+ syntactically valid, the server returns the tagged BAD response, and
+ the mailbox remains unselected. Once the check is done, the server
+ opens the mailbox as if no SELECT/EXAMINE parameters are specified
+ (this is subject to processing of other parameters as defined in
+ other extensions). In particular this means that the server MUST
+ send all untagged responses as specified in Sections 6.3.1 and 6.3.2
+ of [RFC3501].
+
+ After that, the server checks the UIDVALIDITY value provided by the
+ client. If the provided UIDVALIDITY doesn't match the UIDVALIDITY
+ for the mailbox being opened, then the server MUST ignore the
+ remaining parameters and behave as if no dynamic message data
+ changed. The client can discover this situation by comparing the
+ UIDVALIDITY value returned by the server. This behavior allows the
+ client not to synchronize the mailbox or decide on the best
+ synchronization strategy.
+
+ Example: Attempting to resynchronize INBOX, but the provided
+ UIDVALIDITY parameter doesn't match the current UIDVALIDITY
+ value.
+
+ C: A02 SELECT INBOX (QRESYNC (67890007 20050715194045000
+ 41,43:211,214:541))
+ S: * 464 EXISTS
+ S: * 3 RECENT
+ S: * OK [UIDVALIDITY 3857529045] UIDVALIDITY
+ S: * OK [UIDNEXT 550] Predicted next UID
+ S: * OK [HIGHESTMODSEQ 90060128194045007]
+ S: * OK [UNSEEN 12] Message 12 is first unseen
+ S: * FLAGS (\Answered \Flagged \Draft \Deleted \Seen)
+ S: * OK [PERMANENTFLAGS (\Answered \Flagged \Draft
+ \Deleted \Seen \*)] Permanent flags
+ S: A02 OK [READ-WRITE] Sorry, UIDVALIDITY mismatch
+
+ Modification Sequence and UID Parameters:
+
+ A server that doesn't support the persistent storage of mod-sequences
+ for the mailbox MUST send the OK untagged response including the
+ NOMODSEQ response code with every successful SELECT or EXAMINE
+ command, as described in [CONDSTORE]. Such a server doesn't need to
+ remember mod-sequences for expunged messages in the mailbox. It MUST
+ ignore the remaining parameters and behave as if no dynamic message
+ data changed.
+
+ If the provided UIDVALIDITY matches that of the selected mailbox, the
+ server then checks the last known modification sequence.
+
+
+
+Melnikov, et al. Standards Track [Page 5]
+
+RFC 5162 IMAP Quick Mailbox Resync March 2008
+
+
+ The server sends the client any pending flag changes (using FETCH
+ responses that MUST contain UIDs) and expunges those that have
+ occurred in this mailbox since the provided modification sequence.
+
+ If the list of known UIDs was also provided, the server should only
+ report flag changes and expunges for the specified messages. If the
+ client did not provide the list of UIDs, the server acts as if the
+ client has specified "1:<maxuid>", where <maxuid> is the mailbox's
+ UIDNEXT value minus 1. If the mailbox is empty and never had any
+ messages in it, then lack of the list of UIDs is interpreted as an
+ empty set of UIDs.
+
+ Thus, the client can process just these pending events and need not
+ perform a full resynchronization. Without the message sequence
+ number matching information, the result of this step is semantically
+ equivalent to the client issuing:
+ tag1 UID FETCH "known-uids" (FLAGS) (CHANGEDSINCE
+ "mod-sequence-value" VANISHED)
+
+ Example:
+ C: A03 SELECT INBOX (QRESYNC (67890007
+ 90060115194045000 41,43:211,214:541))
+ S: * OK [CLOSED]
+ S: * 314 EXISTS
+ S: * 15 RECENT
+ S: * OK [UIDVALIDITY 67890007] UIDVALIDITY
+ S: * OK [UIDNEXT 567] Predicted next UID
+ S: * OK [HIGHESTMODSEQ 90060115205545359]
+ S: * OK [UNSEEN 7] There are some unseen messages in the mailbox
+ S: * FLAGS (\Answered \Flagged \Draft \Deleted \Seen)
+ S: * OK [PERMANENTFLAGS (\Answered \Flagged \Draft
+ \Deleted \Seen \*)] Permanent flags
+ S: * VANISHED (EARLIER) 41,43:116,118,120:211,214:540
+ S: * 49 FETCH (UID 117 FLAGS (\Seen \Answered) MODSEQ
+ (90060115194045001))
+ S: * 50 FETCH (UID 119 FLAGS (\Draft $MDNSent) MODSEQ
+ (90060115194045308))
+ S: ...
+ S: * 100 FETCH (UID 541 FLAGS (\Seen $Forwarded) MODSEQ
+ (90060115194045001))
+ S: A03 OK [READ-WRITE] mailbox selected
+
+ Message sequence match data:
+
+ A client MAY provide a parenthesized list of a message sequence set
+ and the corresponding UID sets. Both MUST be provided in ascending
+ order. The server uses this data to restrict the range for which it
+ provides expunged message information.
+
+
+
+Melnikov, et al. Standards Track [Page 6]
+
+RFC 5162 IMAP Quick Mailbox Resync March 2008
+
+
+ Conceptually, the client provides a small sample of sequence numbers
+ for which it knows the corresponding UIDs. The server then compares
+ each sequence number and UID pair the client provides with the
+ current state of the mailbox. If a pair matches, then the client
+ knows of any expunges up to, and including, the message, and thus
+ will not include that range in the VANISHED response, even if the
+ "mod-sequence-value" provided by the client is too old for the server
+ to have data of when those messages were expunged.
+
+ Thus, if the Nth message number in the first set in the list is 4,
+ and the Nth UID in the second set in the list is 8, and the mailbox's
+ fourth message has UID 8, then no UIDs equal to or less than 8 are
+ present in the VANISHED response. If the (N+1)th message number is
+ 12, and the (N+1)th UID is 24, and the (N+1)th message in the mailbox
+ has UID 25, then the lowest UID included in the VANISHED response
+ would be 9.
+
+ In the following two examples, the server is unable to remember
+ expunges at all, and only UIDs with messages divisible by three are
+ present in the mailbox. In the first example, the client does not
+ use the fourth parameter; in the second, it provides it. This
+ example is somewhat extreme, but shows that judicious usage of the
+ sequence match data can save a substantial amount of bandwidth.
+
+ Example:
+ C: A04 SELECT INBOX (QRESYNC (67890007
+ 90060115194045000 1:29997))
+ S: * 10003 EXISTS
+ S: * 5 RECENT
+ S: * OK [UIDVALIDITY 67890007] UIDVALIDITY
+ S: * OK [UIDNEXT 30013] Predicted next UID
+ S: * OK [HIGHESTMODSEQ 90060115205545359]
+ S: * OK [UNSEEN 7] There are some unseen messages in the mailbox
+ S: * FLAGS (\Answered \Flagged \Draft \Deleted \Seen)
+ S: * OK [PERMANENTFLAGS (\Answered \Flagged \Draft
+ \Deleted \Seen \*)] Permanent flags
+ S: * VANISHED (EARLIER) 1:2,4:5,7:8,10:11,13:14 [...]
+ 29998:29999,30001:30002,30004:30005,30007:30008
+ S: * 9889 FETCH (UID 29667 FLAGS (\Seen \Answered) MODSEQ
+ (90060115194045027))
+ S: * 9890 FETCH (UID 29670 FLAGS (\Draft $MDNSent) MODSEQ
+ (90060115194045028))
+ S: ...
+ S: * 9999 FETCH (UID 29997 FLAGS (\Seen $Forwarded) MODSEQ
+ (90060115194045031))
+ S: A04 OK [READ-WRITE] mailbox selected
+
+
+
+
+
+Melnikov, et al. Standards Track [Page 7]
+
+RFC 5162 IMAP Quick Mailbox Resync March 2008
+
+
+ Example:
+ C: B04 SELECT INBOX (QRESYNC (67890007
+ 90060115194045000 1:29997 (5000,7500,9000,9990:9999 15000,
+ 22500,27000,29970,29973,29976,29979,29982,29985,29988,29991,
+ 29994,29997)))
+ S: * 10003 EXISTS
+ S: * 5 RECENT
+ S: * OK [UIDVALIDITY 67890007] UIDVALIDITY
+ S: * OK [UIDNEXT 30013] Predicted next UID
+ S: * OK [HIGHESTMODSEQ 90060115205545359]
+ S: * OK [UNSEEN 7] There are some unseen messages in the mailbox
+ S: * FLAGS (\Answered \Flagged \Draft \Deleted \Seen)
+ S: * OK [PERMANENTFLAGS (\Answered \Flagged \Draft
+ \Deleted \Seen \*)] Permanent flags
+ S: * VANISHED (EARLIER) 29998:29999,30001:30002,30004:30005,30007:
+ 30008
+ S: * 9889 FETCH (UID 29667 FLAGS (\Seen \Answered) MODSEQ
+ (90060115194045027))
+ S: * 9890 FETCH (UID 29670 FLAGS (\Draft $MDNSent) MODSEQ
+ (90060115194045028))
+ S: ...
+ S: * 9999 FETCH (UID 29997 FLAGS (\Seen $Forwarded) MODSEQ
+ (90060115194045031))
+ S: B04 OK [READ-WRITE] mailbox selected
+
+3.2. VANISHED UID FETCH Modifier
+
+ [IMAPABNF] has extended the syntax of the FETCH and UID FETCH
+ commands to include an optional FETCH modifier. This document
+ defines a new UID FETCH modifier: VANISHED.
+
+ Note, that the VANISHED UID FETCH modifier is NOT allowed with a
+ FETCH command. The server MUST return a tagged BAD response if this
+ response is specified as a modifier to the FETCH command.
+
+ A server MUST respond with a tagged BAD response if the VANISHED UID
+ FETCH modifier is specified and the client hasn't issued "ENABLE
+ QRESYNC" in the current connection.
+
+ The VANISHED UID FETCH modifier MUST only be specified together with
+ the CHANGEDSINCE UID FETCH modifier.
+
+ The VANISHED UID FETCH modifier instructs the server to report those
+ messages from the UID set parameter that have been expunged and whose
+ associated mod-sequence is larger than the specified mod-sequence.
+ That is, the client requests to be informed of messages from the
+ specified set that were expunged since the specified mod-sequence.
+ Note that the mod-sequence(s) associated with these messages were
+
+
+
+Melnikov, et al. Standards Track [Page 8]
+
+RFC 5162 IMAP Quick Mailbox Resync March 2008
+
+
+ updated when the messages were expunged (as described above). The
+ expunged messages are reported using the VANISHED response as
+ described in Section 3.6, which MUST contain the EARLIER tag. Any
+ VANISHED (EARLIER) responses MUST be returned before any FETCH
+ responses, as otherwise the client might get confused about how
+ message numbers map to UIDs.
+
+ Note: A server that receives a mod-sequence smaller than <minmodseq>,
+ where <minmodseq> is the value of the smallest expunged mod-sequence
+ it remembers minus one, MUST behave as if it was requested to report
+ all expunged messages from the provided UID set parameter.
+
+ Example 1: Without the VANISHED UID FETCH modifier, a CONDSTORE-aware
+ client [CONDSTORE] needs to issue separate commands to learn of flag
+ changes and expunged messages since the last synchronization:
+
+ C: s100 UID FETCH 300:500 (FLAGS) (CHANGEDSINCE 12345)
+ S: * 1 FETCH (UID 404 MODSEQ (65402) FLAGS (\Seen))
+ S: * 2 FETCH (UID 406 MODSEQ (75403) FLAGS (\Deleted))
+ S: * 4 FETCH (UID 408 MODSEQ (29738) FLAGS ($NoJunk
+ $AutoJunk $MDNSent))
+ S: s100 OK FETCH completed
+ C: s101 UID SEARCH 300:500
+ S: * SEARCH 404 406 407 408 410 412
+ S: s101 OK search completed
+
+ Where 300 and 500 are the lowest and highest UIDs from client's
+ cache. The second SEARCH response tells the client that the messages
+ with UIDs 407, 410, and 412 are still present, but their flags
+ haven't changed since the specified modification sequence.
+
+ Using the VANISHED UID FETCH modifier, it is sufficient to issue only
+ a single command:
+
+ C: s100 UID FETCH 300:500 (FLAGS) (CHANGEDSINCE 12345
+ VANISHED)
+ S: * VANISHED (EARLIER) 300:310,405,411
+ S: * 1 FETCH (UID 404 MODSEQ (65402) FLAGS (\Seen))
+ S: * 2 FETCH (UID 406 MODSEQ (75403) FLAGS (\Deleted))
+ S: * 4 FETCH (UID 408 MODSEQ (29738) FLAGS ($NoJunk
+ $AutoJunk $MDNSent))
+ S: s100 OK FETCH completed
+
+
+
+
+
+
+
+
+
+Melnikov, et al. Standards Track [Page 9]
+
+RFC 5162 IMAP Quick Mailbox Resync March 2008
+
+
+3.3. EXPUNGE Command
+
+ Arguments: none
+
+ Responses: untagged responses: EXPUNGE or VANISHED
+
+ Result: OK - expunge completed
+ NO - expunge failure: can't expunge (e.g., permission denied)
+ BAD - command unknown or arguments invalid
+
+ This section updates the definition of the EXPUNGE command described
+ in Section 6.4.3 of [RFC3501].
+
+ The EXPUNGE command permanently removes all messages that have the
+ \Deleted flag set from the currently selected mailbox. Before
+ returning an OK to the client, those messages that are removed are
+ reported using a VANISHED response or EXPUNGE responses.
+
+ If the server is capable of storing modification sequences for the
+ selected mailbox, it MUST increment the per-mailbox mod-sequence if
+ at least one message was permanently removed due to the execution of
+ the EXPUNGE command. For each permanently removed message, the
+ server MUST remember the incremented mod-sequence and corresponding
+ UID. If at least one message got expunged, the server MUST send the
+ updated per-mailbox modification sequence using the HIGHESTMODSEQ
+ response code (defined in [CONDSTORE]) in the tagged OK response.
+
+ Example: C: A202 EXPUNGE
+ S: * 3 EXPUNGE
+ S: * 3 EXPUNGE
+ S: * 5 EXPUNGE
+ S: * 8 EXPUNGE
+ S: A202 OK [HIGHESTMODSEQ 20010715194045319] expunged
+
+ Note: In this example, messages 3, 4, 7, and 11 had the \Deleted flag
+ set. The first "* 3 EXPUNGE" reports message # 3 as expunged. The
+ second "* 3 EXPUNGE" reports message # 4 as expunged (the message
+ number got decremented due to the previous EXPUNGE response). See
+ the description of the EXPUNGE response in [RFC3501] for further
+ explanation.
+
+ Note that if the server chooses to always send VANISHED responses
+ instead of EXPUNGE responses, the previous example might look like
+ this:
+
+ Example: C: B202 EXPUNGE
+ S: * VANISHED 405,407,410,425
+ S: B202 OK [HIGHESTMODSEQ 20010715194045319] expunged
+
+
+
+Melnikov, et al. Standards Track [Page 10]
+
+RFC 5162 IMAP Quick Mailbox Resync March 2008
+
+
+ Here messages with message numbers 3, 4, 7, and 11 have respective
+ UIDs 405, 407, 410, and 425.
+
+3.4. CLOSE Command
+
+ Arguments: none
+
+ Responses: no specific responses for this command
+
+ Result: OK - close completed, now in authenticated state
+ BAD - command unknown or arguments invalid
+
+ This section updates the definition of the CLOSE command described in
+ Section 6.4.2 of [RFC3501].
+
+ The CLOSE command permanently removes all messages that have the
+ \Deleted flag set from the currently selected mailbox, and returns to
+ the authenticated state from the selected state. No untagged EXPUNGE
+ (or VANISHED) responses are sent.
+
+ If the server is capable of storing modification sequences for the
+ selected mailbox, it MUST increment the per-mailbox mod-sequence if
+ at least one message was permanently removed due to the execution of
+ the CLOSE command. For each permanently removed message, the server
+ MUST remember the incremented mod-sequence and corresponding UID. If
+ at least one message got expunged, the server MUST send the updated
+ per-mailbox modification sequence using the HIGHESTMODSEQ response
+ code (defined in [CONDSTORE]) in the tagged OK response.
+
+ Example: C: A202 CLOSE
+ S: A202 OK [HIGHESTMODSEQ 20010715194045319] done
+
+3.5. UID EXPUNGE Command
+
+ Arguments: message set
+
+ Responses: untagged responses: EXPUNGE or VANISHED
+
+ Result: OK - expunge completed
+ NO - expunge failure: can't expunge (e.g., permission denied)
+ BAD - command unknown or arguments invalid
+
+ This section updates the definition of the UID EXPUNGE command
+ described in Section 2.1 of [UIDPLUS]. Servers that implement both
+ [UIDPLUS] and QRESYNC extensions must implement UID EXPUNGE as
+ described in this section.
+
+
+
+
+
+Melnikov, et al. Standards Track [Page 11]
+
+RFC 5162 IMAP Quick Mailbox Resync March 2008
+
+
+ The UID EXPUNGE command permanently removes from the currently
+ selected mailbox all messages that both have the \Deleted flag set
+ and have a UID that is included in the specified message set. If a
+ message either does not have the \Deleted flag set or has a UID that
+ is not included in the specified message set, it is not affected.
+
+ This command is particularly useful for disconnected mode clients.
+ By using UID EXPUNGE instead of EXPUNGE when resynchronizing with the
+ server, the client can avoid inadvertently removing any messages that
+ have been marked as \Deleted by other clients between the time that
+ the client was last connected and the time the client resynchronizes.
+
+ Before returning an OK to the client, those messages that are removed
+ are reported using a VANISHED response or EXPUNGE responses.
+
+ If the server is capable of storing modification sequences for the
+ selected mailbox, it MUST increment the per-mailbox mod-sequence if
+ at least one message was permanently removed due to the execution of
+ the UID EXPUNGE command. For each permanently removed message, the
+ server MUST remember the incremented mod-sequence and corresponding
+ UID. If at least one message got expunged, the server MUST send the
+ updated per-mailbox modification sequence using the HIGHESTMODSEQ
+ response code (defined in [CONDSTORE]) in the tagged OK response.
+
+ Example: C: . UID EXPUNGE 3000:3002
+ S: * 3 EXPUNGE
+ S: * 3 EXPUNGE
+ S: * 3 EXPUNGE
+ S: . OK [HIGHESTMODSEQ 20010715194045319] Ok
+
+ Note: In this example, at least messages with message numbers 3, 4,
+ and 5 (UIDs 3000 to 3002) had the \Deleted flag set. The first "* 3
+ EXPUNGE" reports message # 3 as expunged. The second "* 3 EXPUNGE"
+ reports message # 4 as expunged (the message number got decremented
+ due to the previous EXPUNGE response). See the description of the
+ EXPUNGE response in [RFC3501] for further explanation.
+
+3.6. VANISHED Response
+
+ Contents: an optional EARLIER tag
+
+ list of UIDs
+
+ The VANISHED response reports that the specified UIDs have been
+ permanently removed from the mailbox. This response is similar to
+ the EXPUNGE response [RFC3501]; however, it can return information
+ about multiple messages, and it returns UIDs instead of message
+
+
+
+
+Melnikov, et al. Standards Track [Page 12]
+
+RFC 5162 IMAP Quick Mailbox Resync March 2008
+
+
+ numbers. The first benefit saves bandwidth, while the second is more
+ convenient for clients that only use UIDs to access the IMAP server.
+
+ The VANISHED response has the same restrictions on when it can be
+ sent as does the EXPUNGE response (see below).
+
+ The VANISHED response has two forms. The first form contains the
+ EARLIER tag, which signifies that the response was caused by a UID
+ FETCH (VANISHED) or a SELECT/EXAMINE (QRESYNC) command. This
+ response is sent if the UID set parameter to the UID FETCH (VANISHED)
+ command includes UIDs of messages that are no longer in the mailbox.
+ When the client sees a VANISHED EARLIER response, it MUST NOT
+ decrement message sequence numbers for each successive message in the
+ mailbox.
+
+ The second form doesn't contain the EARLIER tag and is described
+ below. Once a client has issued "ENABLE QRESYNC", the server SHOULD
+ use the VANISHED response without the EARLIER tag instead of the
+ EXPUNGE response. The server SHOULD continue using VANISHED in lieu
+ of EXPUNGE for the duration of the connection. In particular, this
+ affects the EXPUNGE [RFC3501] and UID EXPUNGE [UIDPLUS] commands, as
+ well as messages expunged in other connections. Such a VANISHED
+ response MUST NOT contain the EARLIER tag.
+
+ A VANISHED response sent because of an EXPUNGE or UID EXPUNGE command
+ or because messages were expunged in other connections (i.e., the
+ VANISHED response without the EARLIER tag) also decrements the number
+ of messages in the mailbox; it is not necessary for the server to
+ send an EXISTS response with the new value. It also decrements
+ message sequence numbers for each successive message in the mailbox
+ (see the example at the end of this section). Note that a VANISHED
+ response caused by EXPUNGE, UID EXPUNGE, or messages expunged in
+ other connections SHOULD only contain UIDs for messages expunged
+ since the last VANISHED/EXPUNGE response sent for the currently
+ opened mailbox or since the mailbox was opened. That is, servers
+ SHOULD NOT send UIDs for previously expunged messages, unless
+ explicitly requested to do so by the UID FETCH (VANISHED) command.
+
+ Note that client implementors must take care to properly decrement
+ the number of messages in the mailbox even if a server violates this
+ last SHOULD or repeats the same UID multiple times in the returned
+ UID set. In general, this means that a client using this extension
+ should either avoid using message numbers entirely, or have a
+ complete mapping of UIDs to message sequence numbers for the selected
+ mailbox.
+
+
+
+
+
+
+Melnikov, et al. Standards Track [Page 13]
+
+RFC 5162 IMAP Quick Mailbox Resync March 2008
+
+
+ Because clients handle the two different forms of the VANISHED
+ response differently, servers MUST NOT report UIDs resulting from a
+ UID FETCH (VANISHED) or a SELECT/EXAMINE (QRESYNC) in the same
+ VANISHED response as UIDs of messages expunged now (i.e., messages
+ expunged in other connections). Instead, the server MUST send
+ separate VANISHED responses: one with the EARLIER tag and one
+ without.
+
+ A VANISHED response MUST NOT be sent when no command is in progress,
+ nor while responding to a FETCH, STORE, or SEARCH command. This rule
+ is necessary to prevent a loss of synchronization of message sequence
+ numbers between client and server. A command is not "in progress"
+ until the complete command has been received; in particular, a
+ command is not "in progress" during the negotiation of command
+ continuation.
+
+ Note: UID FETCH, UID STORE, and UID SEARCH are different commands
+ from FETCH, STORE, and SEARCH. A VANISHED response MAY be sent
+ during a UID command. However, the VANISHED response MUST NOT be
+ sent during a UID SEARCH command that contains message numbers in the
+ search criteria.
+
+ The update from the VANISHED response MUST be recorded by the client.
+
+ Example: Let's assume that there is the following mapping between
+ message numbers and UIDs in the currently selected mailbox (here "X"
+ marks messages with the \Deleted flag set, and "x" represents UIDs
+ which are not relevant for the example):
+
+ Message numbers: 1 2 3 4 5 6 7 8 9 10 11
+ UIDs: x 504 505 507 508 x 510 x x x 625
+ \Deleted messages: X X X X
+
+ In the presence of the extension defined in this document:
+
+ C: A202 EXPUNGE
+ S: * VANISHED 505,507,510,625
+ S: A202 OK EXPUNGE completed
+
+ Without the QRESYNC extension, the same example might look like:
+
+ C: A202 EXPUNGE
+ S: * 3 EXPUNGE
+ S: * 3 EXPUNGE
+ S: * 5 EXPUNGE
+ S: * 8 EXPUNGE
+ S: A202 OK EXPUNGE completed
+
+
+
+
+Melnikov, et al. Standards Track [Page 14]
+
+RFC 5162 IMAP Quick Mailbox Resync March 2008
+
+
+ (Continuing previous example) If subsequently messages with UIDs 504
+ and 508 got marked as \Deleted:
+
+ C: A210 EXPUNGE
+ S: * VANISHED 504,508
+ S: A210 OK EXPUNGE completed
+
+ i.e., the last VANISHED response only contains UIDs of messages
+ expunged since the previous VANISHED response.
+
+3.7. CLOSED Response Code
+
+ The CLOSED response code has no parameters. A server implementing
+ the extension defined in this document MUST return the CLOSED
+ response code when the currently selected mailbox is closed
+ implicitly using the SELECT/EXAMINE command on another mailbox. The
+ CLOSED response code serves as a boundary between responses for the
+ previously opened mailbox (which was closed) and the newly selected
+ mailbox: all responses before the CLOSED response code relate to the
+ mailbox that was closed, and all subsequent responses relate to the
+ newly opened mailbox.
+
+ There is no need to return the CLOSED response code on completion of
+ the CLOSE or the UNSELECT [UNSELECT] command (or similar) whose
+ purpose is to close the currently selected mailbox without opening a
+ new one.
+
+4. Server Implementation Considerations
+
+ This section describes a minimalist implementation, a moderate
+ implementation, and an example of a full implementation.
+
+4.1. Server Implementations That Don't Store Extra State
+
+ Strictly speaking, a server implementation that doesn't remember mod-
+ sequences associated with expunged messages can be considered
+ compliant with this specification. Such implementations return all
+ expunged messages specified in the UID set of the UID FETCH
+ (VANISHED) command every time, without paying attention to the
+ specified CHANGEDSINCE mod-sequence. Such implementations are
+ discouraged, as they can end up returning VANISHED responses that are
+ bigger than the result of a UID SEARCH command for the same UID set.
+
+ Clients that use the message sequence match data can reduce the scope
+ of this VANISHED response substantially in the typical case where
+ expunges have not happened, or happen only toward the end of the
+ mailbox.
+
+
+
+
+Melnikov, et al. Standards Track [Page 15]
+
+RFC 5162 IMAP Quick Mailbox Resync March 2008
+
+
+4.2. Server Implementations Storing Minimal State
+
+ A server that stores the HIGHESTMODSEQ value at the time of the last
+ EXPUNGE can omit the VANISHED response when a client provides a
+ MODSEQ value that is equal to, or higher than, the current value of
+ this datum, that is, when there have been no EXPUNGEs.
+
+ A client providing message sequence match data can reduce the scope
+ as above. In the case where there have been no expunges, the server
+ can ignore this data.
+
+4.3. Additional State Required on the Server
+
+ When compared to the [CONDSTORE] extension, this extension requires
+ servers to store additional state associated with expunged messages.
+ Note that implementations are not required to store this state in
+ persistent storage; however, use of persistent storage is advisable.
+
+ One possible way to correctly implement the extension described in
+ this document is to store a queue of <UID set, mod-sequence> pairs.
+ <UID set> can be represented as a sequence of <min UID, max UID>
+ pairs.
+
+ When messages are expunged, one or more entries are added to the
+ queue tail.
+
+ When the server receives a request to return messages expunged since
+ a given mod-sequence, it will search the queue from the tail (i.e.,
+ going from the highest expunged mod-sequence to the lowest) until it
+ sees the first record with a mod-sequence less than or equal to the
+ given mod-sequence or it reaches the head of the queue.
+
+ Note that indefinitely storing information about expunged messages
+ can cause storage and related problems for an implementation. In the
+ worst case, this could result in almost 64Gb of storage for each IMAP
+ mailbox. For example, consider an implementation that stores <min
+ UID, max UID, mod-sequence> triples for each range of messages
+ expunged at the same time. Each triple requires 16 octets: 4 octets
+ for each of the two UIDs, and 8 octets for the mod-sequence. Assume
+ that there is a mailbox containing a single message with a UID of
+ 2**32-1 (the maximum possible UID value), where messages had
+ previously existed with UIDs starting at 1, and have been expunged
+ one at a time. For this mailbox alone, storage is required for the
+ triples <1, 1, modseq1>, <2, 2, modseq2>, ..., <2**32-2, 2**32-2,
+ modseq4294967294>.
+
+
+
+
+
+
+Melnikov, et al. Standards Track [Page 16]
+
+RFC 5162 IMAP Quick Mailbox Resync March 2008
+
+
+ Hence, implementations are encouraged to adopt strategies to protect
+ against such storage problems, such as limiting the size of the queue
+ used to store mod-sequences for expunged messages and "expiring"
+ older records when this limit is reached. When the selected
+ implementation-specific queue limit is reached, the oldest record(s)
+ are deleted from the queue (note that such records are located at the
+ queue head). For all such "expired" records, the server needs to
+ store a single mod-sequence, which is the highest mod-sequence for
+ all "expired" expunged messages.
+
+ Note that if the client provides the message sequence match data,
+ this can heavily reduce the data cost of sending a complete set of
+ missing UIDs; thus, reducing the problems for clients if a server is
+ unable to persist much of this queue. If the queue contains data
+ back to the requested mod-sequence, this data can be ignored.
+
+ Also, note that if the UIDVALIDITY of the mailbox changes or if the
+ mailbox is deleted, then any state associated with expunged messages
+ doesn't need to be preserved and SHOULD be deleted.
+
+5. Updated Synchronization Sequence
+
+ This section updates the description of optimized synchronization in
+ Section 6.1 of the [IMAP-DISC].
+
+ An advanced disconnected mail client should use the QRESYNC and
+ [CONDSTORE] extensions when they are supported by the server. The
+ client uses the value from the HIGHESTMODSEQ OK response code
+ received on mailbox opening to determine if it needs to
+ resynchronize. Once the synchronization is complete, it MUST cache
+ the received value (unless the mailbox UIDVALIDITY value has changed;
+ see below). The client MUST update its copy of the HIGHESTMODSEQ
+ value whenever the server sends a subsequent HIGHESTMODSEQ OK
+ response code.
+
+ After completing a full synchronization, the client MUST also take
+ note of any unsolicited MODSEQ FETCH data items received from the
+ server. Whenever the client receives a tagged response to a command,
+ it calculates the highest value among all MODSEQ FETCH data items
+ received since the last tagged response. If this value is bigger
+ than the client's copy of the HIGHESTMODSEQ value, then the client
+ MUST use this value as its new HIGHESTMODSEQ value.
+
+ Note: It is not safe to update the client's copy of the HIGHESTMODSEQ
+ value with a MODSEQ FETCH data item value as soon as it is received
+ because servers are not required to send MODSEQ FETCH data items in
+ increasing modseqence order. This can lead to the client missing
+ some changes in case of connectivity loss.
+
+
+
+Melnikov, et al. Standards Track [Page 17]
+
+RFC 5162 IMAP Quick Mailbox Resync March 2008
+
+
+ When opening the mailbox for synchronization, the client uses the
+ QRESYNC parameter to the SELECT/EXAMINE command. The QRESYNC
+ parameter is followed by the UIDVALIDITY and mailbox HIGHESTMODSEQ
+ values, as known to the client. It can be optionally followed by the
+ set of UIDs, for example, if the client is only interested in partial
+ synchronization of the mailbox. The client may also transmit a list
+ containing its knowledge of message numbers.
+
+ If the SELECT/EXAMINE command is successful, the client compares
+ UIDVALIDITY as described in step d)1) in Section 3 of the
+ [IMAP-DISC]. If the cached UIDVALIDITY value matches the one
+ returned by the server and the server also returns the HIGHESTMODSEQ
+ response code, then the server reports expunged messages and returns
+ flag changes for all messages specified by the client in the UID set
+ parameter (or for all messages in the mailbox, if the client omitted
+ the UID set parameter). At this point, the client is synchronized,
+ except for maybe the new messages.
+
+ If upon a successful SELECT/EXAMINE (QRESYNC) command the client
+ receives a NOMODSEQ OK untagged response (instead of the
+ HIGHESTMODSEQ response code), it MUST remove the last known
+ HIGHESTMODSEQ value from its cache and follow the more general
+ instructions in Section 3 of the [IMAP-DISC].
+
+ At this point, the client is in sync with the server regarding old
+ messages. This client can now fetch information about new messages
+ (if requested by the user).
+
+ Step d) ("Server-to-client synchronization") in Section 4 of the
+ [IMAP-DISC] in the presence of the QRESYNC & CONDSTORE extensions is
+ amended as follows:
+
+ d) "Server-to-client synchronization" -- for each mailbox that
+ requires synchronization, do the following:
+
+ 1a) Check the mailbox UIDVALIDITY (see Section 4.1 of the [IMAP-DISC]
+ for more details) after issuing SELECT/EXAMINE (QRESYNC) command.
+
+ If the UIDVALIDITY value returned by the server differs, the
+ client MUST
+
+ * empty the local cache of that mailbox;
+
+ * "forget" the cached HIGHESTMODSEQ value for the mailbox;
+
+
+
+
+
+
+
+Melnikov, et al. Standards Track [Page 18]
+
+RFC 5162 IMAP Quick Mailbox Resync March 2008
+
+
+ * remove any pending "actions" which refer to UIDs in that
+ mailbox. Note, this doesn't affect actions performed on
+ client generated fake UIDs (see Section 5 of the
+ [IMAP-DISC]);
+
+ 2) Fetch the current "descriptors";
+
+ I) Discover new messages.
+
+ 3) Fetch the bodies of any "interesting" messages that the client
+ doesn't already have.
+
+ Example: The UIDVALIDITY value is the same, but the HIGHESTMODSEQ
+ value has changed on the server while the client was
+ offline:
+
+ C: A142 SELECT INBOX (QRESYNC (3857529045 20010715194032001 1:198))
+ S: * 172 EXISTS
+ S: * 1 RECENT
+ S: * OK [UNSEEN 12] Message 12 is first unseen
+ S: * OK [UIDVALIDITY 3857529045] UIDs valid
+ S: * OK [UIDNEXT 201] Predicted next UID
+ S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
+ S: * OK [PERMANENTFLAGS (\Deleted \Seen \*)] Limited
+ S: * OK [HIGHESTMODSEQ 20010715194045007]
+ S: * VANISHED (EARLIER) 1:5,7:8,10:15
+ S: * 2 FETCH (UID 6 MODSEQ (20010715205008000)
+ FLAGS (\Deleted))
+ S: * 5 FETCH (UID 9 MODSEQ (20010715195517000)
+ FLAGS ($NoJunk $AutoJunk $MDNSent))
+ ...
+ S: A142 OK [READ-WRITE] SELECT completed
+
+6. Formal Syntax
+
+ The following syntax specification uses the Augmented Backus-Naur
+ Form (ABNF) notation as specified in [ABNF].
+
+ Non-terminals referenced but not defined below are as defined by
+ [RFC3501], [CONDSTORE], or [IMAPABNF].
+
+ Except as noted otherwise, all alphabetic characters are case-
+ insensitive. The use of upper or lower case characters to define
+ token strings is for editorial clarity only. Implementations MUST
+ accept these strings in a case-insensitive fashion.
+
+
+
+
+
+
+Melnikov, et al. Standards Track [Page 19]
+
+RFC 5162 IMAP Quick Mailbox Resync March 2008
+
+
+ capability =/ "QRESYNC"
+
+ select-param = "QRESYNC" SP "(" uidvalidity SP
+ mod-sequence-value [SP known-uids]
+ [SP seq-match-data] ")"
+ ;; conforms to the generic select-param
+ ;; syntax defined in [IMAPABNF]
+
+ seq-match-data = "(" known-sequence-set SP known-uid-set ")"
+
+ uidvalidity = nz-number
+
+ known-uids = sequence-set
+ ;; sequence of UIDs, "*" is not allowed
+
+ known-sequence-set = sequence-set
+ ;; set of message numbers corresponding to
+ ;; the UIDs in known-uid-set, in ascending order.
+ ;; * is not allowed.
+
+ known-uid-set = sequence-set
+ ;; set of UIDs corresponding to the messages in
+ ;; known-sequence-set, in ascending order.
+ ;; * is not allowed.
+
+ message-data =/ expunged-resp
+
+ expunged-resp = "VANISHED" [SP "(EARLIER)"] SP known-uids
+
+ rexpunges-fetch-mod = "VANISHED"
+ ;; VANISHED UID FETCH modifier conforms
+ ;; to the fetch-modifier syntax
+ ;; defined in [IMAPABNF]. It is only
+ ;; allowed in the UID FETCH command.
+
+ resp-text-code =/ "CLOSED"
+
+7. Security Considerations
+
+ As always, it is important to thoroughly test clients and servers
+ implementing this extension, as it changes how the server reports
+ expunged messages to the client.
+
+ Security considerations relevant to [CONDSTORE] are relevant to this
+ extension.
+
+ This document doesn't raise any new security concerns not already
+ raised by [CONDSTORE] or [RFC3501].
+
+
+
+Melnikov, et al. Standards Track [Page 20]
+
+RFC 5162 IMAP Quick Mailbox Resync March 2008
+
+
+8. IANA Considerations
+
+ IMAP4 capabilities are registered by publishing a standards track or
+ IESG approved experimental RFC. The registry is currently located
+ at:
+
+ http://www.iana.org/assignments/imap4-capabilities
+
+ This document defines the QRESYNC IMAP capability. IANA has added
+ this capability to the registry.
+
+9. Acknowledgments
+
+ Thanks to Steve Hole, Cyrus Daboo, and Michael Wener for encouraging
+ creation of this document.
+
+ Valuable comments, both in agreement and in dissent, were received
+ from Timo Sirainen, Michael Wener, Randall Gellens, Arnt Gulbrandsen,
+ Chris Newman, Peter Coates, Mark Crispin, Elwyn Davies, Dan Karp,
+ Eric Rescorla, and Mike Zraly.
+
+ This document takes substantial text from [RFC3501] by Mark Crispin.
+
+10. References
+
+10.1. Normative References
+
+ [ABNF] Crocker, D. and P. Overell, "Augmented BNF for Syntax
+ Specifications: ABNF", STD 68, RFC 5234, January 2008.
+
+ [CONDSTORE] Melnikov, A. and S. Hole, "IMAP Extension for
+ Conditional STORE Operation or Quick Flag Changes
+ Resynchronization", RFC 4551, June 2006.
+
+ [ENABLE] Gulbrandsen, A., Ed. and A. Melnikov, Ed., "The IMAP
+ ENABLE Extension", RFC 5161, March 2008.
+
+ [IMAPABNF] Melnikov, A. and C. Daboo, "Collected Extensions to
+ IMAP4 ABNF", RFC 4466, April 2006.
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC3501] Crispin, M., "INTERNET MESSAGE ACCESS PROTOCOL - VERSION
+ 4rev1", RFC 3501, March 2003.
+
+ [UIDPLUS] Crispin, M., "Internet Message Access Protocol (IMAP) -
+ UIDPLUS extension", RFC 4315, December 2005.
+
+
+
+Melnikov, et al. Standards Track [Page 21]
+
+RFC 5162 IMAP Quick Mailbox Resync March 2008
+
+
+10.2. Informative References
+
+ [IMAP-DISC] Melnikov, A., Ed., "Synchronization Operations For
+ Disconnected Imap4 Clients", RFC 4549, June 2006.
+
+ [UNSELECT] Melnikov, A., "Internet Message Access Protocol (IMAP)
+ UNSELECT command", RFC 3691, February 2004.
+
+Authors' Addresses
+
+ Alexey Melnikov
+ Isode Ltd
+ 5 Castle Business Village
+ 36 Station Road
+ Hampton, Middlesex TW12 2BX
+ UK
+
+ EMail: Alexey.Melnikov@isode.com
+
+
+ Dave Cridland
+ Isode Ltd
+ 5 Castle Business Village
+ 36 Station Road
+ Hampton, Middlesex TW12 2BX
+ UK
+
+ EMail: dave.cridland@isode.com
+
+
+ Corby Wilson
+ Nokia
+ 5 Wayside Rd.
+ Burlington, MA 01803
+ USA
+
+ EMail: corby@computer.org
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Melnikov, et al. Standards Track [Page 22]
+
+RFC 5162 IMAP Quick Mailbox Resync March 2008
+
+
+Full Copyright Statement
+
+ Copyright (C) The IETF Trust (2008).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY, THE IETF TRUST AND
+ THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF
+ THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+
+
+
+
+
+
+
+
+
+
+
+Melnikov, et al. Standards Track [Page 23]
+
diff --git a/imap/docs/rfc/rfc5234.txt b/imap/docs/rfc/rfc5234.txt
new file mode 100644
index 00000000..42bb44ca
--- /dev/null
+++ b/imap/docs/rfc/rfc5234.txt
@@ -0,0 +1,899 @@
+
+
+
+
+
+
+Network Working Group D. Crocker, Ed.
+Request for Comments: 5234 Brandenburg InternetWorking
+STD: 68 P. Overell
+Obsoletes: 4234 THUS plc.
+Category: Standards Track January 2008
+
+
+ Augmented BNF for Syntax Specifications: ABNF
+
+Status of This Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Abstract
+
+ Internet technical specifications often need to define a formal
+ syntax. Over the years, a modified version of Backus-Naur Form
+ (BNF), called Augmented BNF (ABNF), has been popular among many
+ Internet specifications. The current specification documents ABNF.
+ It balances compactness and simplicity with reasonable
+ representational power. The differences between standard BNF and
+ ABNF involve naming rules, repetition, alternatives, order-
+ independence, and value ranges. This specification also supplies
+ additional rule definitions and encoding for a core lexical analyzer
+ of the type common to several Internet specifications.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Crocker & Overell Standards Track [Page 1]
+
+RFC 5234 ABNF January 2008
+
+
+Table of Contents
+
+ 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 3
+ 2. Rule Definition . . . . . . . . . . . . . . . . . . . . . . . 3
+ 2.1. Rule Naming . . . . . . . . . . . . . . . . . . . . . . . 3
+ 2.2. Rule Form . . . . . . . . . . . . . . . . . . . . . . . . 4
+ 2.3. Terminal Values . . . . . . . . . . . . . . . . . . . . . 4
+ 2.4. External Encodings . . . . . . . . . . . . . . . . . . . . 6
+ 3. Operators . . . . . . . . . . . . . . . . . . . . . . . . . . 6
+ 3.1. Concatenation: Rule1 Rule2 . . . . . . . . . . . . . . . 6
+ 3.2. Alternatives: Rule1 / Rule2 . . . . . . . . . . . . . . . 7
+ 3.3. Incremental Alternatives: Rule1 =/ Rule2 . . . . . . . . . 7
+ 3.4. Value Range Alternatives: %c##-## . . . . . . . . . . . . 8
+ 3.5. Sequence Group: (Rule1 Rule2) . . . . . . . . . . . . . . 8
+ 3.6. Variable Repetition: *Rule . . . . . . . . . . . . . . . 9
+ 3.7. Specific Repetition: nRule . . . . . . . . . . . . . . . 9
+ 3.8. Optional Sequence: [RULE] . . . . . . . . . . . . . . . . 9
+ 3.9. Comment: ; Comment . . . . . . . . . . . . . . . . . . . 9
+ 3.10. Operator Precedence . . . . . . . . . . . . . . . . . . . 10
+ 4. ABNF Definition of ABNF . . . . . . . . . . . . . . . . . . . 10
+ 5. Security Considerations . . . . . . . . . . . . . . . . . . . 12
+ 6. References . . . . . . . . . . . . . . . . . . . . . . . . . . 12
+ 6.1. Normative References . . . . . . . . . . . . . . . . . . . 12
+ 6.2. Informative References . . . . . . . . . . . . . . . . . . 12
+ Appendix A. Acknowledgements . . . . . . . . . . . . . . . . . . 13
+ Appendix B. Core ABNF of ABNF . . . . . . . . . . . . . . . . . . 13
+ B.1. Core Rules . . . . . . . . . . . . . . . . . . . . . . . . 13
+ B.2. Common Encoding . . . . . . . . . . . . . . . . . . . . . 15
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Crocker & Overell Standards Track [Page 2]
+
+RFC 5234 ABNF January 2008
+
+
+1. Introduction
+
+ Internet technical specifications often need to define a formal
+ syntax and are free to employ whatever notation their authors deem
+ useful. Over the years, a modified version of Backus-Naur Form
+ (BNF), called Augmented BNF (ABNF), has been popular among many
+ Internet specifications. It balances compactness and simplicity with
+ reasonable representational power. In the early days of the Arpanet,
+ each specification contained its own definition of ABNF. This
+ included the email specifications, [RFC733] and then [RFC822], which
+ came to be the common citations for defining ABNF. The current
+ document separates those definitions to permit selective reference.
+ Predictably, it also provides some modifications and enhancements.
+
+ The differences between standard BNF and ABNF involve naming rules,
+ repetition, alternatives, order-independence, and value ranges.
+ Appendix B supplies rule definitions and encoding for a core lexical
+ analyzer of the type common to several Internet specifications. It
+ is provided as a convenience and is otherwise separate from the meta
+ language defined in the body of this document, and separate from its
+ formal status.
+
+2. Rule Definition
+
+2.1. Rule Naming
+
+ The name of a rule is simply the name itself, that is, a sequence of
+ characters, beginning with an alphabetic character, and followed by a
+ combination of alphabetics, digits, and hyphens (dashes).
+
+ NOTE:
+
+ Rule names are case insensitive.
+
+ The names <rulename>, <Rulename>, <RULENAME>, and <rUlENamE> all
+ refer to the same rule.
+
+ Unlike original BNF, angle brackets ("<", ">") are not required.
+ However, angle brackets may be used around a rule name whenever their
+ presence facilitates in discerning the use of a rule name. This is
+ typically restricted to rule name references in free-form prose, or
+ to distinguish partial rules that combine into a string not separated
+ by white space, such as shown in the discussion about repetition,
+ below.
+
+
+
+
+
+
+
+Crocker & Overell Standards Track [Page 3]
+
+RFC 5234 ABNF January 2008
+
+
+2.2. Rule Form
+
+ A rule is defined by the following sequence:
+
+ name = elements crlf
+
+ where <name> is the name of the rule, <elements> is one or more rule
+ names or terminal specifications, and <crlf> is the end-of-line
+ indicator (carriage return followed by line feed). The equal sign
+ separates the name from the definition of the rule. The elements
+ form a sequence of one or more rule names and/or value definitions,
+ combined according to the various operators defined in this document,
+ such as alternative and repetition.
+
+ For visual ease, rule definitions are left aligned. When a rule
+ requires multiple lines, the continuation lines are indented. The
+ left alignment and indentation are relative to the first lines of the
+ ABNF rules and need not match the left margin of the document.
+
+2.3. Terminal Values
+
+ Rules resolve into a string of terminal values, sometimes called
+ characters. In ABNF, a character is merely a non-negative integer.
+ In certain contexts, a specific mapping (encoding) of values into a
+ character set (such as ASCII) will be specified.
+
+ Terminals are specified by one or more numeric characters, with the
+ base interpretation of those characters indicated explicitly. The
+ following bases are currently defined:
+
+ b = binary
+
+ d = decimal
+
+ x = hexadecimal
+
+ Hence:
+
+ CR = %d13
+
+ CR = %x0D
+
+ respectively specify the decimal and hexadecimal representation of
+ [US-ASCII] for carriage return.
+
+
+
+
+
+
+
+Crocker & Overell Standards Track [Page 4]
+
+RFC 5234 ABNF January 2008
+
+
+ A concatenated string of such values is specified compactly, using a
+ period (".") to indicate a separation of characters within that
+ value. Hence:
+
+ CRLF = %d13.10
+
+ ABNF permits the specification of literal text strings directly,
+ enclosed in quotation marks. Hence:
+
+ command = "command string"
+
+ Literal text strings are interpreted as a concatenated set of
+ printable characters.
+
+ NOTE:
+
+ ABNF strings are case insensitive and the character set for these
+ strings is US-ASCII.
+
+ Hence:
+
+ rulename = "abc"
+
+ and:
+
+ rulename = "aBc"
+
+ will match "abc", "Abc", "aBc", "abC", "ABc", "aBC", "AbC", and
+ "ABC".
+
+ To specify a rule that is case sensitive, specify the characters
+ individually.
+
+ For example:
+
+ rulename = %d97 %d98 %d99
+
+ or
+
+ rulename = %d97.98.99
+
+ will match only the string that comprises only the lowercase
+ characters, abc.
+
+
+
+
+
+
+
+
+Crocker & Overell Standards Track [Page 5]
+
+RFC 5234 ABNF January 2008
+
+
+2.4. External Encodings
+
+ External representations of terminal value characters will vary
+ according to constraints in the storage or transmission environment.
+ Hence, the same ABNF-based grammar may have multiple external
+ encodings, such as one for a 7-bit US-ASCII environment, another for
+ a binary octet environment, and still a different one when 16-bit
+ Unicode is used. Encoding details are beyond the scope of ABNF,
+ although Appendix B provides definitions for a 7-bit US-ASCII
+ environment as has been common to much of the Internet.
+
+ By separating external encoding from the syntax, it is intended that
+ alternate encoding environments can be used for the same syntax.
+
+3. Operators
+
+3.1. Concatenation: Rule1 Rule2
+
+ A rule can define a simple, ordered string of values (i.e., a
+ concatenation of contiguous characters) by listing a sequence of rule
+ names. For example:
+
+ foo = %x61 ; a
+
+ bar = %x62 ; b
+
+ mumble = foo bar foo
+
+ So that the rule <mumble> matches the lowercase string "aba".
+
+ Linear white space: Concatenation is at the core of the ABNF parsing
+ model. A string of contiguous characters (values) is parsed
+ according to the rules defined in ABNF. For Internet specifications,
+ there is some history of permitting linear white space (space and
+ horizontal tab) to be freely and implicitly interspersed around major
+ constructs, such as delimiting special characters or atomic strings.
+
+ NOTE:
+
+ This specification for ABNF does not provide for implicit
+ specification of linear white space.
+
+ Any grammar that wishes to permit linear white space around
+ delimiters or string segments must specify it explicitly. It is
+ often useful to provide for such white space in "core" rules that are
+ then used variously among higher-level rules. The "core" rules might
+ be formed into a lexical analyzer or simply be part of the main
+ ruleset.
+
+
+
+Crocker & Overell Standards Track [Page 6]
+
+RFC 5234 ABNF January 2008
+
+
+3.2. Alternatives: Rule1 / Rule2
+
+ Elements separated by a forward slash ("/") are alternatives.
+ Therefore,
+
+ foo / bar
+
+ will accept <foo> or <bar>.
+
+ NOTE:
+
+ A quoted string containing alphabetic characters is a special form
+ for specifying alternative characters and is interpreted as a non-
+ terminal representing the set of combinatorial strings with the
+ contained characters, in the specified order but with any mixture
+ of upper- and lowercase.
+
+3.3. Incremental Alternatives: Rule1 =/ Rule2
+
+ It is sometimes convenient to specify a list of alternatives in
+ fragments. That is, an initial rule may match one or more
+ alternatives, with later rule definitions adding to the set of
+ alternatives. This is particularly useful for otherwise independent
+ specifications that derive from the same parent ruleset, such as
+ often occurs with parameter lists. ABNF permits this incremental
+ definition through the construct:
+
+ oldrule =/ additional-alternatives
+
+ So that the ruleset
+
+ ruleset = alt1 / alt2
+
+ ruleset =/ alt3
+
+ ruleset =/ alt4 / alt5
+
+ is the same as specifying
+
+ ruleset = alt1 / alt2 / alt3 / alt4 / alt5
+
+
+
+
+
+
+
+
+
+
+
+Crocker & Overell Standards Track [Page 7]
+
+RFC 5234 ABNF January 2008
+
+
+3.4. Value Range Alternatives: %c##-##
+
+ A range of alternative numeric values can be specified compactly,
+ using a dash ("-") to indicate the range of alternative values.
+ Hence:
+
+ DIGIT = %x30-39
+
+ is equivalent to:
+
+ DIGIT = "0" / "1" / "2" / "3" / "4" / "5" / "6" /
+
+ "7" / "8" / "9"
+
+ Concatenated numeric values and numeric value ranges cannot be
+ specified in the same string. A numeric value may use the dotted
+ notation for concatenation or it may use the dash notation to specify
+ one value range. Hence, to specify one printable character between
+ end-of-line sequences, the specification could be:
+
+ char-line = %x0D.0A %x20-7E %x0D.0A
+
+3.5. Sequence Group: (Rule1 Rule2)
+
+ Elements enclosed in parentheses are treated as a single element,
+ whose contents are strictly ordered. Thus,
+
+ elem (foo / bar) blat
+
+ matches (elem foo blat) or (elem bar blat), and
+
+ elem foo / bar blat
+
+ matches (elem foo) or (bar blat).
+
+ NOTE:
+
+ It is strongly advised that grouping notation be used, rather than
+ relying on the proper reading of "bare" alternations, when
+ alternatives consist of multiple rule names or literals.
+
+ Hence, it is recommended that the following form be used:
+
+ (elem foo) / (bar blat)
+
+ It will avoid misinterpretation by casual readers.
+
+
+
+
+
+Crocker & Overell Standards Track [Page 8]
+
+RFC 5234 ABNF January 2008
+
+
+ The sequence group notation is also used within free text to set off
+ an element sequence from the prose.
+
+3.6. Variable Repetition: *Rule
+
+ The operator "*" preceding an element indicates repetition. The full
+ form is:
+
+ <a>*<b>element
+
+ where <a> and <b> are optional decimal values, indicating at least
+ <a> and at most <b> occurrences of the element.
+
+ Default values are 0 and infinity so that *<element> allows any
+ number, including zero; 1*<element> requires at least one;
+ 3*3<element> allows exactly 3; and 1*2<element> allows one or two.
+
+3.7. Specific Repetition: nRule
+
+ A rule of the form:
+
+ <n>element
+
+ is equivalent to
+
+ <n>*<n>element
+
+ That is, exactly <n> occurrences of <element>. Thus, 2DIGIT is a
+ 2-digit number, and 3ALPHA is a string of three alphabetic
+ characters.
+
+3.8. Optional Sequence: [RULE]
+
+ Square brackets enclose an optional element sequence:
+
+ [foo bar]
+
+ is equivalent to
+
+ *1(foo bar).
+
+3.9. Comment: ; Comment
+
+ A semicolon starts a comment that continues to the end of line. This
+ is a simple way of including useful notes in parallel with the
+ specifications.
+
+
+
+
+
+Crocker & Overell Standards Track [Page 9]
+
+RFC 5234 ABNF January 2008
+
+
+3.10. Operator Precedence
+
+ The various mechanisms described above have the following precedence,
+ from highest (binding tightest) at the top, to lowest (loosest) at
+ the bottom:
+
+ Rule name, prose-val, Terminal value
+
+ Comment
+
+ Value range
+
+ Repetition
+
+ Grouping, Optional
+
+ Concatenation
+
+ Alternative
+
+ Use of the alternative operator, freely mixed with concatenations,
+ can be confusing.
+
+ Again, it is recommended that the grouping operator be used to
+ make explicit concatenation groups.
+
+4. ABNF Definition of ABNF
+
+ NOTES:
+
+ 1. This syntax requires a formatting of rules that is relatively
+ strict. Hence, the version of a ruleset included in a
+ specification might need preprocessing to ensure that it can
+ be interpreted by an ABNF parser.
+
+ 2. This syntax uses the rules provided in Appendix B.
+
+
+ rulelist = 1*( rule / (*c-wsp c-nl) )
+
+ rule = rulename defined-as elements c-nl
+ ; continues if next line starts
+ ; with white space
+
+ rulename = ALPHA *(ALPHA / DIGIT / "-")
+
+
+
+
+
+
+Crocker & Overell Standards Track [Page 10]
+
+RFC 5234 ABNF January 2008
+
+
+ defined-as = *c-wsp ("=" / "=/") *c-wsp
+ ; basic rules definition and
+ ; incremental alternatives
+
+ elements = alternation *c-wsp
+
+ c-wsp = WSP / (c-nl WSP)
+
+ c-nl = comment / CRLF
+ ; comment or newline
+
+ comment = ";" *(WSP / VCHAR) CRLF
+
+ alternation = concatenation
+ *(*c-wsp "/" *c-wsp concatenation)
+
+ concatenation = repetition *(1*c-wsp repetition)
+
+ repetition = [repeat] element
+
+ repeat = 1*DIGIT / (*DIGIT "*" *DIGIT)
+
+ element = rulename / group / option /
+ char-val / num-val / prose-val
+
+ group = "(" *c-wsp alternation *c-wsp ")"
+
+ option = "[" *c-wsp alternation *c-wsp "]"
+
+ char-val = DQUOTE *(%x20-21 / %x23-7E) DQUOTE
+ ; quoted string of SP and VCHAR
+ ; without DQUOTE
+
+ num-val = "%" (bin-val / dec-val / hex-val)
+
+ bin-val = "b" 1*BIT
+ [ 1*("." 1*BIT) / ("-" 1*BIT) ]
+ ; series of concatenated bit values
+ ; or single ONEOF range
+
+ dec-val = "d" 1*DIGIT
+ [ 1*("." 1*DIGIT) / ("-" 1*DIGIT) ]
+
+ hex-val = "x" 1*HEXDIG
+ [ 1*("." 1*HEXDIG) / ("-" 1*HEXDIG) ]
+
+
+
+
+
+
+Crocker & Overell Standards Track [Page 11]
+
+RFC 5234 ABNF January 2008
+
+
+ prose-val = "<" *(%x20-3D / %x3F-7E) ">"
+ ; bracketed string of SP and VCHAR
+ ; without angles
+ ; prose description, to be used as
+ ; last resort
+
+5. Security Considerations
+
+ Security is truly believed to be irrelevant to this document.
+
+6. References
+
+6.1. Normative References
+
+ [US-ASCII] American National Standards Institute, "Coded Character
+ Set -- 7-bit American Standard Code for Information
+ Interchange", ANSI X3.4, 1986.
+
+6.2. Informative References
+
+ [RFC733] Crocker, D., Vittal, J., Pogran, K., and D. Henderson,
+ "Standard for the format of ARPA network text messages",
+ RFC 733, November 1977.
+
+ [RFC822] Crocker, D., "Standard for the format of ARPA Internet
+ text messages", STD 11, RFC 822, August 1982.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Crocker & Overell Standards Track [Page 12]
+
+RFC 5234 ABNF January 2008
+
+
+Appendix A. Acknowledgements
+
+ The syntax for ABNF was originally specified in RFC 733. Ken L.
+ Harrenstien, of SRI International, was responsible for re-coding the
+ BNF into an Augmented BNF that makes the representation smaller and
+ easier to understand.
+
+ This recent project began as a simple effort to cull out the portion
+ of RFC 822 that has been repeatedly cited by non-email specification
+ writers, namely the description of Augmented BNF. Rather than simply
+ and blindly converting the existing text into a separate document,
+ the working group chose to give careful consideration to the
+ deficiencies, as well as benefits, of the existing specification and
+ related specifications made available over the last 15 years, and
+ therefore to pursue enhancement. This turned the project into
+ something rather more ambitious than was first intended.
+ Interestingly, the result is not massively different from that
+ original, although decisions, such as removing the list notation,
+ came as a surprise.
+
+ This "separated" version of the specification was part of the DRUMS
+ working group, with significant contributions from Jerome Abela,
+ Harald Alvestrand, Robert Elz, Roger Fajman, Aviva Garrett, Tom
+ Harsch, Dan Kohn, Bill McQuillan, Keith Moore, Chris Newman, Pete
+ Resnick, and Henning Schulzrinne.
+
+ Julian Reschke warrants a special thanks for converting the Draft
+ Standard version to XML source form.
+
+Appendix B. Core ABNF of ABNF
+
+ This appendix contains some basic rules that are in common use.
+ Basic rules are in uppercase. Note that these rules are only valid
+ for ABNF encoded in 7-bit ASCII or in characters sets that are a
+ superset of 7-bit ASCII.
+
+B.1. Core Rules
+
+ Certain basic rules are in uppercase, such as SP, HTAB, CRLF, DIGIT,
+ ALPHA, etc.
+
+ ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
+
+ BIT = "0" / "1"
+
+ CHAR = %x01-7F
+ ; any 7-bit US-ASCII character,
+ ; excluding NUL
+
+
+
+Crocker & Overell Standards Track [Page 13]
+
+RFC 5234 ABNF January 2008
+
+
+ CR = %x0D
+ ; carriage return
+
+ CRLF = CR LF
+ ; Internet standard newline
+
+ CTL = %x00-1F / %x7F
+ ; controls
+
+ DIGIT = %x30-39
+ ; 0-9
+
+ DQUOTE = %x22
+ ; " (Double Quote)
+
+ HEXDIG = DIGIT / "A" / "B" / "C" / "D" / "E" / "F"
+
+ HTAB = %x09
+ ; horizontal tab
+
+ LF = %x0A
+ ; linefeed
+
+ LWSP = *(WSP / CRLF WSP)
+ ; Use of this linear-white-space rule
+ ; permits lines containing only white
+ ; space that are no longer legal in
+ ; mail headers and have caused
+ ; interoperability problems in other
+ ; contexts.
+ ; Do not use when defining mail
+ ; headers and use with caution in
+ ; other contexts.
+
+ OCTET = %x00-FF
+ ; 8 bits of data
+
+ SP = %x20
+
+ VCHAR = %x21-7E
+ ; visible (printing) characters
+
+ WSP = SP / HTAB
+ ; white space
+
+
+
+
+
+
+
+Crocker & Overell Standards Track [Page 14]
+
+RFC 5234 ABNF January 2008
+
+
+B.2. Common Encoding
+
+ Externally, data are represented as "network virtual ASCII" (namely,
+ 7-bit US-ASCII in an 8-bit field), with the high (8th) bit set to
+ zero. A string of values is in "network byte order", in which the
+ higher-valued bytes are represented on the left-hand side and are
+ sent over the network first.
+
+Authors' Addresses
+
+ Dave Crocker (editor)
+ Brandenburg InternetWorking
+ 675 Spruce Dr.
+ Sunnyvale, CA 94086
+ US
+
+ Phone: +1.408.246.8253
+ EMail: dcrocker@bbiw.net
+
+
+ Paul Overell
+ THUS plc.
+ 1/2 Berkeley Square,
+ 99 Berkeley Street
+ Glasgow G3 7HR
+ UK
+
+ EMail: paul.overell@thus.net
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Crocker & Overell Standards Track [Page 15]
+
+RFC 5234 ABNF January 2008
+
+
+Full Copyright Statement
+
+ Copyright (C) The IETF Trust (2008).
+
+ This document is subject to the rights, licenses and restrictions
+ contained in BCP 78, and except as set forth therein, the authors
+ retain all their rights.
+
+ This document and the information contained herein are provided on an
+ "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+ OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY, THE IETF TRUST AND
+ THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF
+ THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Intellectual Property
+
+ The IETF takes no position regarding the validity or scope of any
+ Intellectual Property Rights or other rights that might be claimed to
+ pertain to the implementation or use of the technology described in
+ this document or the extent to which any license under such rights
+ might or might not be available; nor does it represent that it has
+ made any independent effort to identify any such rights. Information
+ on the procedures with respect to rights in RFC documents can be
+ found in BCP 78 and BCP 79.
+
+ Copies of IPR disclosures made to the IETF Secretariat and any
+ assurances of licenses to be made available, or the result of an
+ attempt made to obtain a general license or permission for the use of
+ such proprietary rights by implementers or users of this
+ specification can be obtained from the IETF on-line IPR repository at
+ http://www.ietf.org/ipr.
+
+ The IETF invites any interested party to bring to its attention any
+ copyrights, patents or patent applications, or other proprietary
+ rights that may cover technology that may be required to implement
+ this standard. Please address the information to the IETF at
+ ietf-ipr@ietf.org.
+
+
+
+
+
+
+
+
+
+
+
+
+Crocker & Overell Standards Track [Page 16]
+
diff --git a/imap/makefile.nt b/imap/makefile.nt
new file mode 100644
index 00000000..4c2643d6
--- /dev/null
+++ b/imap/makefile.nt
@@ -0,0 +1,76 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+# Program: IMAP Toolkit Makefile for Windows 9x and Windows NT
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 7 December 1989
+# Last Edited: 30 August 2006
+
+
+COPY=copy
+CD=cd
+MAKE=nmake /nologo /f makefile.nt
+MKDIR=mkdir
+RD=rmdir /s /q
+
+
+# Make the IMAP Toolkit
+
+build: c-client mtest mailutil imapd ipopd
+ $(CD) c-client
+ $(MAKE)
+ $(CD) ..\mtest
+ $(MAKE)
+ $(CD) ..\mailutil
+ $(MAKE)
+ $(CD) ..\ipopd
+ $(MAKE)
+ $(CD) ..\imapd
+ $(MAKE)
+ $(CD) ..
+
+c-client:
+ $(MKDIR) c-client
+ $(COPY) src\c-client\*.* c-client
+ $(COPY) src\charset\*.* c-client
+ $(COPY) src\osdep\nt\*.* c-client
+
+mtest:
+ $(MKDIR) mtest
+ $(COPY) src\mtest\*.* mtest
+
+mailutil:
+ $(MKDIR) mailutil
+ $(COPY) src\mailutil\*.* mailutil
+
+ipopd:
+ $(MKDIR) ipopd
+ $(COPY) src\ipopd\*.* ipopd
+
+imapd:
+ $(MKDIR) imapd
+ $(COPY) src\imapd\*.* imapd
+
+clean:
+ $(RD) c-client mtest mailutil ipopd imapd
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo 'not war?'
diff --git a/imap/makefile.ntk b/imap/makefile.ntk
new file mode 100644
index 00000000..858bf38f
--- /dev/null
+++ b/imap/makefile.ntk
@@ -0,0 +1,76 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+# Program: IMAP Toolkit Makefile for Windows 9x and Windows NT + Kerberos
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 7 December 1989
+# Last Edited: 30 August 2006
+
+
+COPY=copy
+CD=cd
+MAKE=nmake /nologo /f makefile.ntk
+MKDIR=mkdir
+RD=rmdir /s /q
+
+
+# Make the IMAP Toolkit
+
+build: c-client mtest mailutil imapd ipopd
+ $(CD) c-client
+ $(MAKE)
+ $(CD) ..\mtest
+ $(MAKE)
+ $(CD) ..\mailutil
+ $(MAKE)
+ $(CD) ..\ipopd
+ $(MAKE)
+ $(CD) ..\imapd
+ $(MAKE)
+ $(CD) ..
+
+c-client:
+ $(MKDIR) c-client
+ $(COPY) src\c-client\*.* c-client
+ $(COPY) src\charset\*.* c-client
+ $(COPY) src\osdep\nt\*.* c-client
+
+mtest:
+ $(MKDIR) mtest
+ $(COPY) src\mtest\*.* mtest
+
+mailutil:
+ $(MKDIR) mailutil
+ $(COPY) src\mailutil\*.* mailutil
+
+ipopd:
+ $(MKDIR) ipopd
+ $(COPY) src\ipopd\*.* ipopd
+
+imapd:
+ $(MKDIR) imapd
+ $(COPY) src\imapd\*.* imapd
+
+clean:
+ $(RD) c-client mtest mailutil ipopd imapd
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo 'not war?'
diff --git a/imap/makefile.os2 b/imap/makefile.os2
new file mode 100644
index 00000000..273d04b6
--- /dev/null
+++ b/imap/makefile.os2
@@ -0,0 +1,59 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+# Program: IMAP Toolkit Makefile
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 7 December 1989
+# Last Edited: 30 August 2006
+
+
+COPY=copy
+MAKE=make -f makefile.os2
+MKDIR=md
+RD=rm -rf
+
+
+# Make the IMAP Toolkit
+
+build: c-client mtest mailutil
+ (cd c-client & $(MAKE))
+ (cd mtest & $(MAKE))
+ (cd mailutil & $(MAKE))
+
+c-client:
+ $(MKDIR) c-client
+ $(COPY) src\c-client\\* c-client
+ $(COPY) src\charset\\* c-client
+ $(COPY) src\osdep\os2\\* c-client
+
+mtest:
+ $(MKDIR) mtest
+ $(COPY) src\mtest\\* mtest
+
+mailutil:
+ $(MKDIR) mailutil
+ $(COPY) src\mailutil\\* mailutil
+
+clean:
+ $(RD) c-client mtest mailutil
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo 'not war?'
diff --git a/imap/makefile.w2k b/imap/makefile.w2k
new file mode 100644
index 00000000..b29f81c9
--- /dev/null
+++ b/imap/makefile.w2k
@@ -0,0 +1,77 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+# Program: IMAP Toolkit Makefile for Windows 2000/XP
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 7 December 1989
+# Last Edited: 20 August 2006
+
+
+IP=6
+COPY=copy
+CD=cd
+MAKE=nmake /nologo /f makefile.w2k IP=$(IP)
+MKDIR=mkdir
+RD=rmdir /s /q
+
+
+# Make the IMAP Toolkit
+
+build: c-client mtest mailutil imapd ipopd
+ $(CD) c-client
+ $(MAKE)
+ $(CD) ..\mtest
+ $(MAKE)
+ $(CD) ..\mailutil
+ $(MAKE)
+ $(CD) ..\ipopd
+ $(MAKE)
+ $(CD) ..\imapd
+ $(MAKE)
+ $(CD) ..
+
+c-client:
+ $(MKDIR) c-client
+ $(COPY) src\c-client\*.* c-client
+ $(COPY) src\charset\*.* c-client
+ $(COPY) src\osdep\nt\*.* c-client
+
+mtest:
+ $(MKDIR) mtest
+ $(COPY) src\mtest\*.* mtest
+
+mailutil:
+ $(MKDIR) mailutil
+ $(COPY) src\mailutil\*.* mailutil
+
+ipopd:
+ $(MKDIR) ipopd
+ $(COPY) src\ipopd\*.* ipopd
+
+imapd:
+ $(MKDIR) imapd
+ $(COPY) src\imapd\*.* imapd
+
+clean:
+ $(RD) c-client mtest mailutil ipopd imapd
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo 'not war?'
diff --git a/imap/makefile.wce b/imap/makefile.wce
new file mode 100644
index 00000000..62cd10f5
--- /dev/null
+++ b/imap/makefile.wce
@@ -0,0 +1,64 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+# Program: IMAP Toolkit Makefile
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 7 December 1989
+# Last Edited: 30 August 2006
+
+
+COPY=copy
+CD=cd
+MAKE=nmake /nologo /f makefile.wce
+MKDIR=mkdir
+RD=rmdir /s /q
+
+
+# Make the IMAP Toolkit
+
+build: c-client mtest mailutil
+ $(CD) c-client
+ $(MAKE)
+# $(CD) ..\mtest
+# $(MAKE)
+# $(CD) ..\mailutil
+# $(MAKE)
+ $(CD) ..
+
+c-client:
+ $(MKDIR) c-client
+ $(COPY) src\c-client\*.* c-client
+ $(COPY) src\charset\*.* c-client
+ $(COPY) src\osdep\wce\*.* c-client
+
+mtest:
+ $(MKDIR) mtest
+ $(COPY) src\mtest\*.* mtest
+
+mailutil:
+ $(MKDIR) mailutil
+ $(COPY) src\mailutil\*.* mailutil
+
+clean:
+ $(RD) c-client mtest
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo 'not war?'
diff --git a/imap/src/ansilib/memmove.c b/imap/src/ansilib/memmove.c
new file mode 100644
index 00000000..85453bd0
--- /dev/null
+++ b/imap/src/ansilib/memmove.c
@@ -0,0 +1,40 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Memory move
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Copy memory block
+ * Accepts: destination pointer
+ * source pointer
+ * length
+ * Returns: destination pointer
+ */
+
+void *memmove (void *s,void *ct,size_t n)
+{
+ bcopy (ct,s,n); /* they should have this one */
+ return s;
+}
diff --git a/imap/src/ansilib/memmove2.c b/imap/src/ansilib/memmove2.c
new file mode 100644
index 00000000..52532e21
--- /dev/null
+++ b/imap/src/ansilib/memmove2.c
@@ -0,0 +1,49 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Memory move when no bcopy()
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+/* Copy memory block
+ * Accepts: destination pointer
+ * source pointer
+ * length
+ * Returns: destination pointer
+ */
+
+void *memmove (void *s,void *ct,size_t n)
+{
+ char *dp,*sp;
+ int i;
+ unsigned long dest = (unsigned long) s;
+ unsigned long src = (unsigned long) ct;
+ if (((dest < src) && ((dest + n) < src)) ||
+ ((dest > src) && ((src + n) < dest))) return (void *) memcpy (s,ct,n);
+ dp = (char *) s;
+ sp = (char *) ct;
+ if (dest < src) for (i = 0; i < n; ++i) dp[i] = sp[i];
+ else if (dest > src) for (i = n - 1; i >= 0; --i) dp[i] = sp[i];
+ return s;
+}
diff --git a/imap/src/ansilib/memset.c b/imap/src/ansilib/memset.c
new file mode 100644
index 00000000..fce66806
--- /dev/null
+++ b/imap/src/ansilib/memset.c
@@ -0,0 +1,40 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Set memory
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+/* Set a block of memory
+ * Accepts: destination pointer
+ * value to set
+ * length
+ * Returns: destination pointer
+ */
+
+void *memset (void *s,int c,size_t n)
+{
+ if (c) while (n) s[--n] = c; /* this way if non-zero */
+ else bzero (s,n); /* they should have this one */
+ return s;
+}
diff --git a/imap/src/ansilib/strpbrk.c b/imap/src/ansilib/strpbrk.c
new file mode 100644
index 00000000..d63f7627
--- /dev/null
+++ b/imap/src/ansilib/strpbrk.c
@@ -0,0 +1,40 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: String search for break character
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+/* Return pointer to first occurance in string of any delimiter
+ * Accepts: source pointer
+ * vector of delimiters pointer
+ * Returns: pointer to delimiter or NIL if not found
+ */
+
+char *strpbrk (char *cs,char *ct)
+{
+ char *s;
+ /* search for delimiter until end of string */
+ for (; *cs; cs++) for (s = ct; *s; s++) if (*s == *cs) return cs;
+ return NIL; /* not found */
+}
diff --git a/imap/src/ansilib/strstr.c b/imap/src/ansilib/strstr.c
new file mode 100644
index 00000000..5c02569a
--- /dev/null
+++ b/imap/src/ansilib/strstr.c
@@ -0,0 +1,45 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Substring search
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+/* Return pointer to first occurance in string of a substring
+ * Accepts: source pointer
+ * substring pointer
+ * Returns: pointer to substring in source or NIL if not found
+ */
+
+char *strstr (char *cs,char *ct)
+{
+ char *s;
+ char *t;
+ while (cs = strchr (cs,*ct)) {/* for each occurance of the first character */
+ /* see if remainder of string matches */
+ for (s = cs + 1, t = ct + 1; *t && *s == *t; s++, t++);
+ if (!*t) return cs; /* if ran out of substring then have match */
+ cs++; /* try from next character */
+ }
+ return NIL; /* not found */
+}
diff --git a/imap/src/ansilib/strtok.c b/imap/src/ansilib/strtok.c
new file mode 100644
index 00000000..973ba4e7
--- /dev/null
+++ b/imap/src/ansilib/strtok.c
@@ -0,0 +1,67 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: String return successive tokens
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 January 2007
+ */
+
+/* Find token in string
+ * Accepts: source pointer or NIL to use previous source
+ * vector of token delimiters pointer
+ * Returns: pointer to next token
+ */
+
+static char *state = NIL; /* string to locate tokens */
+
+char *strtok (char *s,char *ct)
+{
+ return strtok_r (s,ct,&state);/* jacket into reentrant routine */
+}
+
+
+/* Find token in string (reentrant)
+ * Accepts: source pointer or NIL to use previous source
+ * vector of token delimiters pointer
+ * returned state pointer
+ * Returns: pointer to next token
+ */
+
+char *strtok_r (char *s,char *ct,char **r)
+{
+ char *t,*ts;
+ if (!s) s = *r; /* use previous token if none specified */
+ *r = NIL; /* default to no returned state */
+ if (!(s && *s)) return NIL; /* no tokens left */
+ /* find any leading delimiters */
+ do for (t = ct, ts = NIL; *t; t++) if (*t == *s) {
+ if (*(ts = ++s)) break; /* yes, restart search if more in string */
+ return NIL; /* else no more tokens */
+ } while (ts); /* continue until no more leading delimiters */
+ /* can we find a new delimiter? */
+ for (ts = s; *ts; ts++) for (t = ct; *t; t++) if (*t == *ts) {
+ *ts++ = '\0'; /* yes, tie off token at that point */
+ *r = ts; /* save subsequent tokens for future call */
+ return s; /* return our token */
+ }
+ return s; /* return final token */
+}
diff --git a/imap/src/ansilib/strtoul.c b/imap/src/ansilib/strtoul.c
new file mode 100644
index 00000000..0646b637
--- /dev/null
+++ b/imap/src/ansilib/strtoul.c
@@ -0,0 +1,73 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: String to unsigned long
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ *
+ * Date: 14 February 1995
+ * Last Edited: 30 August 2006
+ */
+
+/*
+ * Turn a string unsigned long into the real thing
+ * Accepts: source string
+ * pointer to place to return end pointer
+ * base
+ * Returns: parsed unsigned long integer, end pointer is updated
+ */
+
+unsigned long strtoul (char *t,char **endp,int base)
+{
+ unsigned long value = 0; /* the accumulated value */
+ int negative = 0; /* this a negative number? */
+ unsigned char c,*s = t;
+ if (base && (base < 2 || base > 36)) {
+ errno = EINVAL; /* insist upon valid base */
+ return value;
+ }
+ while (isspace (*s)) s++; /* skip leading whitespace */
+ switch (*s) { /* check for leading sign char */
+ case '-':
+ negative = 1; /* yes, negative #. fall into '+' */
+ case '+':
+ s++; /* skip the sign character */
+ }
+ if (!base) { /* base not specified? */
+ if (*s != '0') base = 10; /* must be decimal if doesn't start with 0 */
+ /* starts with 0x? */
+ else if (tolower (*++s) == 'x') {
+ base = 16; /* yes, is hex */
+ s++; /* skip the x */
+ }
+ else base = 8; /* ...or octal */
+ }
+ do { /* convert to numeric form if digit */
+ if (isdigit (*s)) c = *s - '0';
+ /* alphabetic conversion */
+ else if (isalpha (*s)) c = *s - (isupper (*s) ? 'A' : 'a') + 10;
+ else break; /* else no way it's valid */
+ if (c >= base) break; /* digit out of range for base? */
+ value = value * base + c; /* accumulate the digit */
+ } while (*++s); /* loop until non-numeric character */
+ if (tolower (*s) == 'l') s++; /* ignore 'l' or 'L' marker */
+ if (endp) *endp = s; /* save users endp to after number */
+ /* negate number if needed */
+ return negative ? -value : value;
+}
diff --git a/imap/src/c-client/auth_ext.c b/imap/src/c-client/auth_ext.c
new file mode 100644
index 00000000..61dfc1b8
--- /dev/null
+++ b/imap/src/c-client/auth_ext.c
@@ -0,0 +1,96 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: EXTERNAL authenticator
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 6 April 2005
+ * Last Edited: 30 August 2006
+ */
+
+long auth_external_client (authchallenge_t challenger,authrespond_t responder,
+ char *service,NETMBX *mb,void *stream,
+ unsigned long *trial,char *user);
+char *auth_external_server (authresponse_t responder,int argc,char *argv[]);
+
+AUTHENTICATOR auth_ext = { /* secure, has full auth, hidden */
+ AU_SECURE | AU_AUTHUSER | AU_HIDE,
+ "EXTERNAL", /* authenticator name */
+ NIL, /* always valid */
+ auth_external_client, /* client method */
+ auth_external_server, /* server method */
+ NIL /* next authenticator */
+};
+
+/* Client authenticator
+ * Accepts: challenger function
+ * responder function
+ * SASL service name
+ * parsed network mailbox structure
+ * stream argument for functions
+ * pointer to current trial count
+ * returned user name
+ * Returns: T if success, NIL otherwise, number of trials incremented if retry
+ */
+
+long auth_external_client (authchallenge_t challenger,authrespond_t responder,
+ char *service,NETMBX *mb,void *stream,
+ unsigned long *trial,char *user)
+{
+ void *challenge;
+ unsigned long clen;
+ long ret = NIL;
+ *trial = 65535; /* never retry */
+ if (challenge = (*challenger) (stream,&clen)) {
+ fs_give ((void **) &challenge);
+ /* send authorization id (empty string OK) */
+ if ((*responder) (stream,strcpy (user,mb->user),strlen (mb->user))) {
+ if (challenge = (*challenger) (stream,&clen))
+ fs_give ((void **) &challenge);
+ else ret = LONGT; /* check the authentication */
+ }
+ }
+ return ret;
+}
+
+
+/* Server authenticator
+ * Accepts: responder function
+ * argument count
+ * argument vector
+ * Returns: authenticated user name or NIL
+ */
+
+char *auth_external_server (authresponse_t responder,int argc,char *argv[])
+{
+ unsigned long len;
+ char *authid;
+ char *authenid = (char *) mail_parameters (NIL,GET_EXTERNALAUTHID,NIL);
+ char *ret = NIL;
+ /* get authorization identity */
+ if (authenid && (authid = (*responder) ("",0,&len))) {
+ /* note: responders null-terminate */
+ if (*authid ? authserver_login (authid,authenid,argc,argv) :
+ authserver_login (authenid,NIL,argc,argv)) ret = myusername ();
+ fs_give ((void **) &authid);
+ }
+ return ret;
+}
diff --git a/imap/src/c-client/auth_gss.c b/imap/src/c-client/auth_gss.c
new file mode 100644
index 00000000..26bf9e50
--- /dev/null
+++ b/imap/src/c-client/auth_gss.c
@@ -0,0 +1,423 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: GSSAPI authenticator
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 12 January 1998
+ * Last Edited: 30 August 2006
+ */
+
+
+long auth_gssapi_valid (void);
+long auth_gssapi_client (authchallenge_t challenger,authrespond_t responder,
+ char *service,NETMBX *mb,void *stream,
+ unsigned long *trial,char *user);
+long auth_gssapi_client_work (authchallenge_t challenger,gss_buffer_desc chal,
+ authrespond_t responder,char *service,NETMBX *mb,
+ void *stream,char *user,kinit_t ki);
+char *auth_gssapi_server (authresponse_t responder,int argc,char *argv[]);
+
+
+AUTHENTICATOR auth_gss = {
+ AU_SECURE | AU_AUTHUSER, /* secure authenticator */
+ "GSSAPI", /* authenticator name */
+ auth_gssapi_valid, /* check if valid */
+ auth_gssapi_client, /* client method */
+ auth_gssapi_server, /* server method */
+ NIL /* next authenticator */
+};
+
+#define AUTH_GSSAPI_P_NONE 1
+#define AUTH_GSSAPI_P_INTEGRITY 2
+#define AUTH_GSSAPI_P_PRIVACY 4
+
+#define AUTH_GSSAPI_C_MAXSIZE 8192
+
+#define SERVER_LOG(x,y) syslog (LOG_ALERT,x,y)
+
+/* Check if GSSAPI valid on this system
+ * Returns: T if valid, NIL otherwise
+ */
+
+long auth_gssapi_valid (void)
+{
+ char tmp[MAILTMPLEN];
+ OM_uint32 smn;
+ gss_buffer_desc buf;
+ gss_name_t name;
+ /* make service name */
+ sprintf (tmp,"%s@%s",(char *) mail_parameters (NIL,GET_SERVICENAME,NIL),
+ mylocalhost ());
+ buf.length = strlen (buf.value = tmp);
+ /* see if can build a name */
+ if (gss_import_name (&smn,&buf,GSS_C_NT_HOSTBASED_SERVICE,&name) !=
+ GSS_S_COMPLETE) return NIL;
+ /* remove server method if no keytab */
+ if (!kerberos_server_valid ()) auth_gss.server = NIL;
+ gss_release_name (&smn,&name);/* finished with name */
+ return LONGT;
+}
+
+/* Client authenticator
+ * Accepts: challenger function
+ * responder function
+ * SASL service name
+ * parsed network mailbox structure
+ * stream argument for functions
+ * pointer to current trial count
+ * returned user name
+ * Returns: T if success, NIL otherwise, number of trials incremented if retry
+ */
+
+long auth_gssapi_client (authchallenge_t challenger,authrespond_t responder,
+ char *service,NETMBX *mb,void *stream,
+ unsigned long *trial,char *user)
+{
+ gss_buffer_desc chal;
+ kinit_t ki = (kinit_t) mail_parameters (NIL,GET_KINIT,NIL);
+ long ret = NIL;
+ *trial = 65535; /* never retry */
+ /* get initial (empty) challenge */
+ if (chal.value = (*challenger) (stream,(unsigned long *) &chal.length)) {
+ if (chal.length) { /* abort if challenge non-empty */
+ mm_log ("Server bug: non-empty initial GSSAPI challenge",WARN);
+ (*responder) (stream,NIL,0);
+ ret = LONGT; /* will get a BAD response back */
+ }
+ else if (mb->authuser[0] && strcmp (mb->authuser,myusername ())) {
+ mm_log ("Can't use Kerberos: invalid /authuser",WARN);
+ (*responder) (stream,NIL,0);
+ ret = LONGT; /* will get a BAD response back */
+ }
+ else ret = auth_gssapi_client_work (challenger,chal,responder,service,mb,
+ stream,user,ki);
+ }
+ return ret;
+}
+
+/* Client authenticator worker function
+ * Accepts: challenger function
+ * responder function
+ * SASL service name
+ * parsed network mailbox structure
+ * stream argument for functions
+ * returned user name
+ * kinit function pointer if should retry with kinit
+ * Returns: T if success, NIL otherwise
+ */
+
+long auth_gssapi_client_work (authchallenge_t challenger,gss_buffer_desc chal,
+ authrespond_t responder,char *service,NETMBX *mb,
+ void *stream,char *user,kinit_t ki)
+{
+ char tmp[MAILTMPLEN];
+ OM_uint32 smj,smn,dsmj,dsmn;
+ OM_uint32 mctx = 0;
+ gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
+ gss_buffer_desc resp,buf;
+ long i;
+ int conf;
+ gss_qop_t qop;
+ gss_name_t crname = NIL;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ void *data;
+ long ret = NIL;
+ sprintf (tmp,"%s@%s",service,mb->host);
+ buf.length = strlen (buf.value = tmp);
+ /* get service name */
+ if (gss_import_name (&smn,&buf,GSS_C_NT_HOSTBASED_SERVICE,&crname) !=
+ GSS_S_COMPLETE) {
+ mm_log ("Can't import Kerberos service name",WARN);
+ (*responder) (stream,NIL,0);
+ }
+ else {
+ data = (*bn) (BLOCK_SENSITIVE,NIL);
+ /* negotiate with KDC */
+ smj = gss_init_sec_context (&smn,GSS_C_NO_CREDENTIAL,&ctx,crname,NIL,
+ GSS_C_INTEG_FLAG | GSS_C_MUTUAL_FLAG |
+ GSS_C_REPLAY_FLAG,0,GSS_C_NO_CHANNEL_BINDINGS,
+ GSS_C_NO_BUFFER,NIL,&resp,NIL,NIL);
+ (*bn) (BLOCK_NONSENSITIVE,data);
+
+ /* while continuation needed */
+ while (smj == GSS_S_CONTINUE_NEEDED) {
+ if (chal.value) fs_give ((void **) &chal.value);
+ /* send response, get next challenge */
+ i = (*responder) (stream,resp.value,resp.length) &&
+ (chal.value = (*challenger) (stream,(unsigned long *) &chal.length));
+ gss_release_buffer (&smn,&resp);
+ if (i) { /* negotiate continuation with KDC */
+ data = (*bn) (BLOCK_SENSITIVE,NIL);
+ switch (smj = /* make sure continuation going OK */
+ gss_init_sec_context (&smn,GSS_C_NO_CREDENTIAL,&ctx,
+ crname,GSS_C_NO_OID,GSS_C_INTEG_FLAG |
+ GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,0,
+ GSS_C_NO_CHANNEL_BINDINGS,&chal,NIL,
+ &resp,NIL,NIL)) {
+ case GSS_S_CONTINUE_NEEDED:
+ case GSS_S_COMPLETE:
+ break;
+ default: /* error, don't need context any more */
+ gss_delete_sec_context (&smn,&ctx,NIL);
+ }
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ }
+ else { /* error in continuation */
+ mm_log ("Error in negotiating Kerberos continuation",WARN);
+ (*responder) (stream,NIL,0);
+ /* don't need context any more */
+ gss_delete_sec_context (&smn,&ctx,NIL);
+ break;
+ }
+ }
+
+ switch (smj) { /* done - deal with final condition */
+ case GSS_S_COMPLETE:
+ if (chal.value) fs_give ((void **) &chal.value);
+ /* get prot mechanisms and max size */
+ if ((*responder) (stream,resp.value ? resp.value : "",resp.length) &&
+ (chal.value = (*challenger) (stream,(unsigned long *)&chal.length))&&
+ (gss_unwrap (&smn,ctx,&chal,&resp,&conf,&qop) == GSS_S_COMPLETE) &&
+ (resp.length >= 4) && (*((char *) resp.value) & AUTH_GSSAPI_P_NONE)){
+ /* make copy of flags and length */
+ memcpy (tmp,resp.value,4);
+ gss_release_buffer (&smn,&resp);
+ /* no session protection */
+ tmp[0] = AUTH_GSSAPI_P_NONE;
+ /* install user name */
+ strcpy (tmp+4,strcpy (user,mb->user[0] ? mb->user : myusername ()));
+ buf.value = tmp; buf.length = strlen (user) + 4;
+ /* successful negotiation */
+ switch (smj = gss_wrap (&smn,ctx,NIL,qop,&buf,&conf,&resp)) {
+ case GSS_S_COMPLETE:
+ if ((*responder) (stream,resp.value,resp.length)) ret = T;
+ gss_release_buffer (&smn,&resp);
+ break;
+ default:
+ do switch (dsmj = gss_display_status (&dsmn,smj,GSS_C_GSS_CODE,
+ GSS_C_NO_OID,&mctx,&resp)) {
+ case GSS_S_COMPLETE:
+ mctx = 0;
+ case GSS_S_CONTINUE_NEEDED:
+ sprintf (tmp,"Unknown gss_wrap failure: %s",(char *) resp.value);
+ mm_log (tmp,WARN);
+ gss_release_buffer (&dsmn,&resp);
+ }
+ while (dsmj == GSS_S_CONTINUE_NEEDED);
+ do switch (dsmj = gss_display_status (&dsmn,smn,GSS_C_MECH_CODE,
+ GSS_C_NO_OID,&mctx,&resp)) {
+ case GSS_S_COMPLETE:
+ case GSS_S_CONTINUE_NEEDED:
+ sprintf (tmp,"GSSAPI mechanism status: %s",(char *) resp.value);
+ mm_log (tmp,WARN);
+ gss_release_buffer (&dsmn,&resp);
+ }
+ while (dsmj == GSS_S_CONTINUE_NEEDED);
+ (*responder) (stream,NIL,0);
+ }
+ }
+ /* flush final challenge */
+ if (chal.value) fs_give ((void **) &chal.value);
+ /* don't need context any more */
+ gss_delete_sec_context (&smn,&ctx,NIL);
+ break;
+
+ case GSS_S_CREDENTIALS_EXPIRED:
+ if (chal.value) fs_give ((void **) &chal.value);
+ /* retry if application kinits */
+ if (ki && (*ki) (mb->host,"Kerberos credentials expired"))
+ ret = auth_gssapi_client_work (challenger,chal,responder,service,mb,
+ stream,user,NIL);
+ else { /* application can't kinit */
+ sprintf (tmp,"Kerberos credentials expired (try running kinit) for %s",
+ mb->host);
+ mm_log (tmp,WARN);
+ (*responder) (stream,NIL,0);
+ }
+ break;
+ case GSS_S_FAILURE:
+ if (chal.value) fs_give ((void **) &chal.value);
+ do switch (dsmj = gss_display_status (&dsmn,smn,GSS_C_MECH_CODE,
+ GSS_C_NO_OID,&mctx,&resp)) {
+ case GSS_S_COMPLETE: /* end of message, can kinit? */
+ if (ki && kerberos_try_kinit (smn) &&
+ (*ki) (mb->host,(char *) resp.value)) {
+ gss_release_buffer (&dsmn,&resp);
+ ret = auth_gssapi_client_work (challenger,chal,responder,service,mb,
+ stream,user,NIL);
+ break; /* done */
+ }
+ else (*responder) (stream,NIL,0);
+ case GSS_S_CONTINUE_NEEDED:
+ sprintf (tmp,kerberos_try_kinit (smn) ?
+ "Kerberos error: %.80s (try running kinit) for %.80s" :
+ "GSSAPI failure: %s for %.80s",(char *) resp.value,mb->host);
+ mm_log (tmp,WARN);
+ gss_release_buffer (&dsmn,&resp);
+ } while (dsmj == GSS_S_CONTINUE_NEEDED);
+ break;
+
+ default: /* miscellaneous errors */
+ if (chal.value) fs_give ((void **) &chal.value);
+ do switch (dsmj = gss_display_status (&dsmn,smj,GSS_C_GSS_CODE,
+ GSS_C_NO_OID,&mctx,&resp)) {
+ case GSS_S_COMPLETE:
+ mctx = 0;
+ case GSS_S_CONTINUE_NEEDED:
+ sprintf (tmp,"Unknown GSSAPI failure: %s",(char *) resp.value);
+ mm_log (tmp,WARN);
+ gss_release_buffer (&dsmn,&resp);
+ }
+ while (dsmj == GSS_S_CONTINUE_NEEDED);
+ do switch (dsmj = gss_display_status (&dsmn,smn,GSS_C_MECH_CODE,
+ GSS_C_NO_OID,&mctx,&resp)) {
+ case GSS_S_COMPLETE:
+ case GSS_S_CONTINUE_NEEDED:
+ sprintf (tmp,"GSSAPI mechanism status: %s",(char *) resp.value);
+ mm_log (tmp,WARN);
+ gss_release_buffer (&dsmn,&resp);
+ }
+ while (dsmj == GSS_S_CONTINUE_NEEDED);
+ (*responder) (stream,NIL,0);
+ break;
+ }
+ /* finished with credentials name */
+ if (crname) gss_release_name (&smn,&crname);
+ }
+ return ret; /* return status */
+}
+
+/* Server authenticator
+ * Accepts: responder function
+ * argument count
+ * argument vector
+ * Returns: authenticated user name or NIL
+ */
+
+char *auth_gssapi_server (authresponse_t responder,int argc,char *argv[])
+{
+ char *ret = NIL;
+ char tmp[MAILTMPLEN];
+ unsigned long maxsize = htonl (AUTH_GSSAPI_C_MAXSIZE);
+ int conf;
+ OM_uint32 smj,smn,dsmj,dsmn,flags;
+ OM_uint32 mctx = 0;
+ gss_name_t crname,name;
+ gss_OID mech;
+ gss_buffer_desc chal,resp,buf;
+ gss_cred_id_t crd;
+ gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
+ gss_qop_t qop = GSS_C_QOP_DEFAULT;
+ /* make service name */
+ sprintf (tmp,"%s@%s",(char *) mail_parameters (NIL,GET_SERVICENAME,NIL),
+ tcp_serverhost ());
+ buf.length = strlen (buf.value = tmp);
+ /* acquire credentials */
+ if ((gss_import_name (&smn,&buf,GSS_C_NT_HOSTBASED_SERVICE,&crname)) ==
+ GSS_S_COMPLETE) {
+ if ((smj = gss_acquire_cred (&smn,crname,0,NIL,GSS_C_ACCEPT,&crd,NIL,NIL))
+ == GSS_S_COMPLETE) {
+ if (resp.value = (*responder) ("",0,(unsigned long *) &resp.length)) {
+ do { /* negotiate authentication */
+ smj = gss_accept_sec_context (&smn,&ctx,crd,&resp,
+ GSS_C_NO_CHANNEL_BINDINGS,&name,&mech,
+ &chal,&flags,NIL,NIL);
+ /* don't need response any more */
+ fs_give ((void **) &resp.value);
+ switch (smj) { /* how did it go? */
+ case GSS_S_COMPLETE: /* successful */
+ case GSS_S_CONTINUE_NEEDED:
+ if (chal.value) { /* send challenge, get next response */
+ resp.value = (*responder) (chal.value,chal.length,
+ (unsigned long *) &resp.length);
+ gss_release_buffer (&smn,&chal);
+ }
+ break;
+ }
+ }
+ while (resp.value && resp.length && (smj == GSS_S_CONTINUE_NEEDED));
+
+ /* successful exchange? */
+ if ((smj == GSS_S_COMPLETE) &&
+ (gss_display_name (&smn,name,&buf,&mech) == GSS_S_COMPLETE)) {
+ /* send security and size */
+ memcpy (resp.value = tmp,(void *) &maxsize,resp.length = 4);
+ tmp[0] = AUTH_GSSAPI_P_NONE;
+ if (gss_wrap (&smn,ctx,NIL,qop,&resp,&conf,&chal) == GSS_S_COMPLETE){
+ resp.value = (*responder) (chal.value,chal.length,
+ (unsigned long *) &resp.length);
+ gss_release_buffer (&smn,&chal);
+ if (gss_unwrap (&smn,ctx,&resp,&chal,&conf,&qop)==GSS_S_COMPLETE) {
+ /* client request valid */
+ if (chal.value && (chal.length > 4) &&
+ (chal.length < (MAILTMPLEN - 1)) &&
+ memcpy (tmp,chal.value,chal.length) &&
+ (tmp[0] & AUTH_GSSAPI_P_NONE)) {
+ /* tie off authorization ID */
+ tmp[chal.length] = '\0';
+ ret = kerberos_login (tmp+4,buf.value,argc,argv);
+ }
+ /* done with user name */
+ gss_release_buffer (&smn,&chal);
+ }
+ /* finished with response */
+ fs_give ((void **) &resp.value);
+ }
+ /* don't need name buffer any more */
+ gss_release_buffer (&smn,&buf);
+ }
+ /* don't need client name any more */
+ gss_release_name (&smn,&name);
+ /* don't need context any more */
+ if (ctx != GSS_C_NO_CONTEXT) gss_delete_sec_context (&smn,&ctx,NIL);
+ }
+ /* finished with credentials */
+ gss_release_cred (&smn,&crd);
+ }
+
+ else { /* can't acquire credentials! */
+ if (gss_display_name (&dsmn,crname,&buf,&mech) == GSS_S_COMPLETE)
+ SERVER_LOG ("Failed to acquire credentials for %s",buf.value);
+ if (smj != GSS_S_FAILURE) do
+ switch (dsmj = gss_display_status (&dsmn,smj,GSS_C_GSS_CODE,
+ GSS_C_NO_OID,&mctx,&resp)) {
+ case GSS_S_COMPLETE:
+ mctx = 0;
+ case GSS_S_CONTINUE_NEEDED:
+ SERVER_LOG ("Unknown GSSAPI failure: %s",resp.value);
+ gss_release_buffer (&dsmn,&resp);
+ }
+ while (dsmj == GSS_S_CONTINUE_NEEDED);
+ do switch (dsmj = gss_display_status (&dsmn,smn,GSS_C_MECH_CODE,
+ GSS_C_NO_OID,&mctx,&resp)) {
+ case GSS_S_COMPLETE:
+ case GSS_S_CONTINUE_NEEDED:
+ SERVER_LOG ("GSSAPI mechanism status: %s",resp.value);
+ gss_release_buffer (&dsmn,&resp);
+ }
+ while (dsmj == GSS_S_CONTINUE_NEEDED);
+ }
+ /* finished with credentials name */
+ gss_release_name (&smn,&crname);
+ }
+ return ret; /* return status */
+}
diff --git a/imap/src/c-client/auth_log.c b/imap/src/c-client/auth_log.c
new file mode 100644
index 00000000..1e1b1b5c
--- /dev/null
+++ b/imap/src/c-client/auth_log.c
@@ -0,0 +1,117 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Login authenticator
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 5 December 1995
+ * Last Edited: 30 August 2006
+ */
+
+long auth_login_client (authchallenge_t challenger,authrespond_t responder,
+ char *service,NETMBX *mb,void *stream,
+ unsigned long *trial,char *user);
+char *auth_login_server (authresponse_t responder,int argc,char *argv[]);
+
+AUTHENTICATOR auth_log = {
+ AU_HIDE, /* hidden */
+ "LOGIN", /* authenticator name */
+ NIL, /* always valid */
+ auth_login_client, /* client method */
+ auth_login_server, /* server method */
+ NIL /* next authenticator */
+};
+
+#define PWD_USER "User Name"
+#define PWD_PWD "Password"
+
+/* Client authenticator
+ * Accepts: challenger function
+ * responder function
+ * SASL service name
+ * parsed network mailbox structure
+ * stream argument for functions
+ * pointer to current trial count
+ * returned user name
+ * Returns: T if success, NIL otherwise, number of trials incremented if retry
+ */
+
+long auth_login_client (authchallenge_t challenger,authrespond_t responder,
+ char *service,NETMBX *mb,void *stream,
+ unsigned long *trial,char *user)
+{
+ char pwd[MAILTMPLEN];
+ void *challenge;
+ unsigned long clen;
+ long ret = NIL;
+ /* get user name prompt */
+ if (challenge = (*challenger) (stream,&clen)) {
+ fs_give ((void **) &challenge);
+ pwd[0] = NIL; /* prompt user */
+ mm_login (mb,user,pwd,*trial);
+ if (!pwd[0]) { /* user requested abort */
+ (*responder) (stream,NIL,0);
+ *trial = 0; /* cancel subsequent attempts */
+ ret = LONGT; /* will get a BAD response back */
+ }
+ /* send user name */
+ else if ((*responder) (stream,user,strlen (user)) &&
+ (challenge = (*challenger) (stream,&clen))) {
+ fs_give ((void **) &challenge);
+ /* send password */
+ if ((*responder) (stream,pwd,strlen (pwd))) {
+ if (challenge = (*challenger) (stream,&clen))
+ fs_give ((void **) &challenge);
+ else {
+ ++*trial; /* can try again if necessary */
+ ret = LONGT; /* check the authentication */
+ }
+ }
+ }
+ }
+ memset (pwd,0,MAILTMPLEN); /* erase password */
+ if (!ret) *trial = 65535; /* don't retry if bad protocol */
+ return ret;
+}
+
+
+/* Server authenticator
+ * Accepts: responder function
+ * argument count
+ * argument vector
+ * Returns: authenticated user name or NIL
+ */
+
+char *auth_login_server (authresponse_t responder,int argc,char *argv[])
+{
+ char *ret = NIL;
+ char *user,*pass,*authuser;
+ if (user = (*responder) (PWD_USER,sizeof (PWD_USER),NIL)) {
+ if (pass = (*responder) (PWD_PWD,sizeof (PWD_PWD),NIL)) {
+ /* delimit user from possible admin */
+ if (authuser = strchr (user,'*')) *authuser++ = '\0';
+ if (server_login (user,pass,authuser,argc,argv)) ret = myusername ();
+ fs_give ((void **) &pass);
+ }
+ fs_give ((void **) &user);
+ }
+ return ret;
+}
diff --git a/imap/src/c-client/auth_md5.c b/imap/src/c-client/auth_md5.c
new file mode 100644
index 00000000..29ab947d
--- /dev/null
+++ b/imap/src/c-client/auth_md5.c
@@ -0,0 +1,495 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: CRAM-MD5 authenticator
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 21 October 1998
+ * Last Edited: 30 January 2007
+ */
+
+/* MD5 context */
+
+#define MD5BLKLEN 64 /* MD5 block length */
+#define MD5DIGLEN 16 /* MD5 digest length */
+
+typedef struct {
+ unsigned long chigh; /* high 32bits of byte count */
+ unsigned long clow; /* low 32bits of byte count */
+ unsigned long state[4]; /* state (ABCD) */
+ unsigned char buf[MD5BLKLEN]; /* input buffer */
+ unsigned char *ptr; /* buffer position */
+} MD5CONTEXT;
+
+
+/* Prototypes */
+
+long auth_md5_valid (void);
+long auth_md5_client (authchallenge_t challenger,authrespond_t responder,
+ char *service,NETMBX *mb,void *stream,
+ unsigned long *trial,char *user);
+char *auth_md5_server (authresponse_t responder,int argc,char *argv[]);
+char *auth_md5_pwd (char *user);
+char *apop_login (char *chal,char *user,char *md5,int argc,char *argv[]);
+char *hmac_md5 (char *text,unsigned long tl,char *key,unsigned long kl);
+void md5_init (MD5CONTEXT *ctx);
+void md5_update (MD5CONTEXT *ctx,unsigned char *data,unsigned long len);
+void md5_final (unsigned char *digest,MD5CONTEXT *ctx);
+static void md5_transform (unsigned long *state,unsigned char *block);
+static void md5_encode (unsigned char *dst,unsigned long *src,int len);
+static void md5_decode (unsigned long *dst,unsigned char *src,int len);
+
+
+/* Authenticator linkage */
+
+AUTHENTICATOR auth_md5 = {
+ AU_SECURE, /* secure authenticator */
+ "CRAM-MD5", /* authenticator name */
+ auth_md5_valid, /* check if valid */
+ auth_md5_client, /* client method */
+ auth_md5_server, /* server method */
+ NIL /* next authenticator */
+};
+
+/* Check if CRAM-MD5 valid on this system
+ * Returns: T, always
+ */
+
+long auth_md5_valid (void)
+{
+ struct stat sbuf;
+ /* server forbids MD5 if no MD5 enable file */
+ if (stat (MD5ENABLE,&sbuf)) auth_md5.server = NIL;
+ return T; /* MD5 is otherwise valid */
+}
+
+
+/* Client authenticator
+ * Accepts: challenger function
+ * responder function
+ * SASL service name
+ * parsed network mailbox structure
+ * stream argument for functions
+ * pointer to current trial count
+ * returned user name
+ * Returns: T if success, NIL otherwise, number of trials incremented if retry
+ */
+
+long auth_md5_client (authchallenge_t challenger,authrespond_t responder,
+ char *service,NETMBX *mb,void *stream,
+ unsigned long *trial,char *user)
+{
+ char pwd[MAILTMPLEN];
+ void *challenge;
+ unsigned long clen;
+ long ret = NIL;
+ /* get challenge */
+ if (challenge = (*challenger) (stream,&clen)) {
+ pwd[0] = NIL; /* prompt user */
+ mm_login (mb,user,pwd,*trial);
+ if (!pwd[0]) { /* user requested abort */
+ fs_give ((void **) &challenge);
+ (*responder) (stream,NIL,0);
+ *trial = 0; /* cancel subsequent attempts */
+ ret = LONGT; /* will get a BAD response back */
+ }
+ else { /* got password, build response */
+ sprintf (pwd,"%.65s %.33s",user,hmac_md5 (challenge,clen,
+ pwd,strlen (pwd)));
+ fs_give ((void **) &challenge);
+ /* send credentials, allow retry if OK */
+ if ((*responder) (stream,pwd,strlen (pwd))) {
+ if (challenge = (*challenger) (stream,&clen))
+ fs_give ((void **) &challenge);
+ else {
+ ++*trial; /* can try again if necessary */
+ ret = LONGT; /* check the authentication */
+ }
+ }
+ }
+ }
+ memset (pwd,0,MAILTMPLEN); /* erase password in case not overwritten */
+ if (!ret) *trial = 65535; /* don't retry if bad protocol */
+ return ret;
+}
+
+/* Server authenticator
+ * Accepts: responder function
+ * argument count
+ * argument vector
+ * Returns: authenticated user name or NIL
+ *
+ * This is much hairier than it needs to be due to the necessary of zapping
+ * the password data.
+ */
+
+static int md5try = MAXLOGINTRIALS;
+
+char *auth_md5_server (authresponse_t responder,int argc,char *argv[])
+{
+ char *ret = NIL;
+ char *p,*u,*user,*authuser,*hash,chal[MAILTMPLEN];
+ unsigned long cl,pl;
+ /* generate challenge */
+ sprintf (chal,"<%lu.%lu@%s>",(unsigned long) getpid (),
+ (unsigned long) time (0),mylocalhost ());
+ /* send challenge, get user and hash */
+ if (user = (*responder) (chal,cl = strlen (chal),NIL)) {
+ /* got user, locate hash */
+ if (hash = strrchr (user,' ')) {
+ *hash++ = '\0'; /* tie off user */
+ /* see if authentication user */
+ if (authuser = strchr (user,'*')) *authuser++ = '\0';
+ /* get password */
+ if (p = auth_md5_pwd ((authuser && *authuser) ? authuser : user)) {
+ pl = strlen (p);
+ u = (md5try && !strcmp (hash,hmac_md5 (chal,cl,p,pl))) ? user : NIL;
+ memset (p,0,pl); /* erase sensitive information */
+ fs_give ((void **) &p); /* flush erased password */
+ /* now log in for real */
+ if (u && authserver_login (u,authuser,argc,argv)) ret = myusername ();
+ else if (md5try) --md5try;
+ }
+ }
+ fs_give ((void **) &user);
+ }
+ if (!ret) sleep (3); /* slow down possible cracker */
+ return ret;
+}
+
+/* Return MD5 password for user
+ * Accepts: user name
+ * Returns: plaintext password if success, else NIL
+ *
+ * This is much hairier than it needs to be due to the necessary of zapping
+ * the password data. That's why we don't use stdio here.
+ */
+
+char *auth_md5_pwd (char *user)
+{
+ struct stat sbuf;
+ int fd = open (MD5ENABLE,O_RDONLY,NIL);
+ unsigned char *s,*t,*buf,*lusr,*lret;
+ char *r;
+ char *ret = NIL;
+ if (fd >= 0) { /* found the file? */
+ fstat (fd,&sbuf); /* yes, slurp it into memory */
+ read (fd,buf = (char *) fs_get (sbuf.st_size + 1),sbuf.st_size);
+ /* see if any uppercase characters in user */
+ for (s = user; *s && ((*s < 'A') || (*s > 'Z')); s++);
+ /* yes, make lowercase copy */
+ lusr = *s ? lcase (cpystr (user)) : NIL;
+ for (s = strtok_r ((char *) buf,"\015\012",&r),lret = NIL; s;
+ s = ret ? NIL : strtok_r (NIL,"\015\012",&r))
+ /* must be valid entry line */
+ if (*s && (*s != '#') && (t = strchr (s,'\t')) && t[1]) {
+ *t++ = '\0'; /* found tab, tie off user, point to pwd */
+ if (!strcmp (s,user)) ret = cpystr (t);
+ else if (lusr && !lret) if (!strcmp (s,lusr)) lret = t;
+ }
+ /* accept case-independent name */
+ if (!ret && lret) ret = cpystr (lret);
+ /* don't need lowercase copy any more */
+ if (lusr) fs_give ((void **) &lusr);
+ /* erase sensitive information from buffer */
+ memset (buf,0,sbuf.st_size + 1);
+ fs_give ((void **) &buf); /* flush the buffer */
+ close (fd); /* don't need file any longer */
+ }
+ return ret; /* return password */
+}
+
+/* APOP server login
+ * Accepts: challenge
+ * desired user name
+ * purported MD5
+ * argument count
+ * argument vector
+ * Returns: authenticated user name or NIL
+ */
+
+char *apop_login (char *chal,char *user,char *md5,int argc,char *argv[])
+{
+ int i,j;
+ char *ret = NIL;
+ char *s,*authuser,tmp[MAILTMPLEN];
+ unsigned char digest[MD5DIGLEN];
+ MD5CONTEXT ctx;
+ char *hex = "0123456789abcdef";
+ /* see if authentication user */
+ if (authuser = strchr (user,'*')) *authuser++ = '\0';
+ /* get password */
+ if (s = auth_md5_pwd ((authuser && *authuser) ? authuser : user)) {
+ md5_init (&ctx); /* initialize MD5 context */
+ /* build string to get MD5 digest */
+ sprintf (tmp,"%.128s%.128s",chal,s);
+ memset (s,0,strlen (s)); /* erase sensitive information */
+ fs_give ((void **) &s); /* flush erased password */
+ md5_update (&ctx,(unsigned char *) tmp,strlen (tmp));
+ memset (tmp,0,MAILTMPLEN); /* erase sensitive information */
+ md5_final (digest,&ctx);
+ /* convert to printable hex */
+ for (i = 0, s = tmp; i < MD5DIGLEN; i++) {
+ *s++ = hex[(j = digest[i]) >> 4];
+ *s++ = hex[j & 0xf];
+ }
+ *s = '\0'; /* tie off hash text */
+ memset (digest,0,MD5DIGLEN);/* erase sensitive information */
+ if (md5try && !strcmp (md5,tmp) &&
+ authserver_login (user,authuser,argc,argv))
+ ret = cpystr (myusername ());
+ else if (md5try) --md5try;
+ memset (tmp,0,MAILTMPLEN); /* erase sensitive information */
+ }
+ if (!ret) sleep (3); /* slow down possible cracker */
+ return ret;
+}
+
+/*
+ * RFC 2104 HMAC hashing
+ * Accepts: text to hash
+ * text length
+ * key
+ * key length
+ * Returns: hash as text, always
+ */
+
+char *hmac_md5 (char *text,unsigned long tl,char *key,unsigned long kl)
+{
+ int i,j;
+ static char hshbuf[2*MD5DIGLEN + 1];
+ char *s;
+ MD5CONTEXT ctx;
+ char *hex = "0123456789abcdef";
+ unsigned char digest[MD5DIGLEN],k_ipad[MD5BLKLEN+1],k_opad[MD5BLKLEN+1];
+ if (kl > MD5BLKLEN) { /* key longer than pad length? */
+ md5_init (&ctx); /* yes, set key as MD5(key) */
+ md5_update (&ctx,(unsigned char *) key,kl);
+ md5_final (digest,&ctx);
+ key = (char *) digest;
+ kl = MD5DIGLEN;
+ }
+ memcpy (k_ipad,key,kl); /* store key in pads */
+ memset (k_ipad+kl,0,(MD5BLKLEN+1)-kl);
+ memcpy (k_opad,k_ipad,MD5BLKLEN+1);
+ /* XOR key with ipad and opad values */
+ for (i = 0; i < MD5BLKLEN; i++) {
+ k_ipad[i] ^= 0x36;
+ k_opad[i] ^= 0x5c;
+ }
+ md5_init (&ctx); /* inner MD5: hash ipad and text */
+ md5_update (&ctx,k_ipad,MD5BLKLEN);
+ md5_update (&ctx,(unsigned char *) text,tl);
+ md5_final (digest,&ctx);
+ md5_init (&ctx); /* outer MD5: hash opad and inner results */
+ md5_update (&ctx,k_opad,MD5BLKLEN);
+ md5_update (&ctx,digest,MD5DIGLEN);
+ md5_final (digest,&ctx);
+ /* convert to printable hex */
+ for (i = 0, s = hshbuf; i < MD5DIGLEN; i++) {
+ *s++ = hex[(j = digest[i]) >> 4];
+ *s++ = hex[j & 0xf];
+ }
+ *s = '\0'; /* tie off hash text */
+ return hshbuf;
+}
+
+/* Everything after this point is derived from the RSA Data Security, Inc.
+ * MD5 Message-Digest Algorithm
+ */
+
+/* You may wonder why these strange "a &= 0xffffffff;" statements are here.
+ * This is to ensure correct results on machines with a unsigned long size of
+ * larger than 32 bits.
+ */
+
+#define RND1(a,b,c,d,x,s,ac) \
+ a += ((b & c) | (d & ~b)) + x + (unsigned long) ac; \
+ a &= 0xffffffff; \
+ a = b + ((a << s) | (a >> (32 - s)));
+
+#define RND2(a,b,c,d,x,s,ac) \
+ a += ((b & d) | (c & ~d)) + x + (unsigned long) ac; \
+ a &= 0xffffffff; \
+ a = b + ((a << s) | (a >> (32 - s)));
+
+#define RND3(a,b,c,d,x,s,ac) \
+ a += (b ^ c ^ d) + x + (unsigned long) ac; \
+ a &= 0xffffffff; \
+ a = b + ((a << s) | (a >> (32 - s)));
+
+#define RND4(a,b,c,d,x,s,ac) \
+ a += (c ^ (b | ~d)) + x + (unsigned long) ac; \
+ a &= 0xffffffff; \
+ a = b + ((a << s) | (a >> (32 - s)));
+
+/* Initialize MD5 context
+ * Accepts: context to initialize
+ */
+
+void md5_init (MD5CONTEXT *ctx)
+{
+ ctx->clow = ctx->chigh = 0; /* initialize byte count to zero */
+ /* initialization constants */
+ ctx->state[0] = 0x67452301; ctx->state[1] = 0xefcdab89;
+ ctx->state[2] = 0x98badcfe; ctx->state[3] = 0x10325476;
+ ctx->ptr = ctx->buf; /* reset buffer pointer */
+}
+
+
+/* MD5 add data to context
+ * Accepts: context
+ * input data
+ * length of data
+ */
+
+void md5_update (MD5CONTEXT *ctx,unsigned char *data,unsigned long len)
+{
+ unsigned long i = (ctx->buf + MD5BLKLEN) - ctx->ptr;
+ /* update double precision number of bytes */
+ if ((ctx->clow += len) < len) ctx->chigh++;
+ while (i <= len) { /* copy/transform data, 64 bytes at a time */
+ memcpy (ctx->ptr,data,i); /* fill up 64 byte chunk */
+ md5_transform (ctx->state,ctx->ptr = ctx->buf);
+ data += i,len -= i,i = MD5BLKLEN;
+ }
+ memcpy (ctx->ptr,data,len); /* copy final bit of data in buffer */
+ ctx->ptr += len; /* update buffer pointer */
+}
+
+/* MD5 Finalization
+ * Accepts: destination digest
+ * context
+ */
+
+void md5_final (unsigned char *digest,MD5CONTEXT *ctx)
+{
+ unsigned long i,bits[2];
+ bits[0] = ctx->clow << 3; /* calculate length in bits (before padding) */
+ bits[1] = (ctx->chigh << 3) + (ctx->clow >> 29);
+ *ctx->ptr++ = 0x80; /* padding byte */
+ if ((i = (ctx->buf + MD5BLKLEN) - ctx->ptr) < 8) {
+ memset (ctx->ptr,0,i); /* pad out buffer with zeros */
+ md5_transform (ctx->state,ctx->buf);
+ /* pad out with zeros, leaving 8 bytes */
+ memset (ctx->buf,0,MD5BLKLEN - 8);
+ ctx->ptr = ctx->buf + MD5BLKLEN - 8;
+ }
+ else if (i -= 8) { /* need to pad this buffer? */
+ memset (ctx->ptr,0,i); /* yes, pad out with zeros, leaving 8 bytes */
+ ctx->ptr += i;
+ }
+ md5_encode (ctx->ptr,bits,2); /* make LSB-first length */
+ md5_transform (ctx->state,ctx->buf);
+ /* store state in digest */
+ md5_encode (digest,ctx->state,4);
+ /* erase context */
+ memset (ctx,0,sizeof (MD5CONTEXT));
+}
+
+/* MD5 basic transformation
+ * Accepts: state vector
+ * current 64-byte block
+ */
+
+static void md5_transform (unsigned long *state,unsigned char *block)
+{
+ unsigned long a = state[0],b = state[1],c = state[2],d = state[3],x[16];
+ md5_decode (x,block,16); /* decode into 16 longs */
+ /* round 1 */
+ RND1 (a,b,c,d,x[ 0], 7,0xd76aa478); RND1 (d,a,b,c,x[ 1],12,0xe8c7b756);
+ RND1 (c,d,a,b,x[ 2],17,0x242070db); RND1 (b,c,d,a,x[ 3],22,0xc1bdceee);
+ RND1 (a,b,c,d,x[ 4], 7,0xf57c0faf); RND1 (d,a,b,c,x[ 5],12,0x4787c62a);
+ RND1 (c,d,a,b,x[ 6],17,0xa8304613); RND1 (b,c,d,a,x[ 7],22,0xfd469501);
+ RND1 (a,b,c,d,x[ 8], 7,0x698098d8); RND1 (d,a,b,c,x[ 9],12,0x8b44f7af);
+ RND1 (c,d,a,b,x[10],17,0xffff5bb1); RND1 (b,c,d,a,x[11],22,0x895cd7be);
+ RND1 (a,b,c,d,x[12], 7,0x6b901122); RND1 (d,a,b,c,x[13],12,0xfd987193);
+ RND1 (c,d,a,b,x[14],17,0xa679438e); RND1 (b,c,d,a,x[15],22,0x49b40821);
+ /* round 2 */
+ RND2 (a,b,c,d,x[ 1], 5,0xf61e2562); RND2 (d,a,b,c,x[ 6], 9,0xc040b340);
+ RND2 (c,d,a,b,x[11],14,0x265e5a51); RND2 (b,c,d,a,x[ 0],20,0xe9b6c7aa);
+ RND2 (a,b,c,d,x[ 5], 5,0xd62f105d); RND2 (d,a,b,c,x[10], 9, 0x2441453);
+ RND2 (c,d,a,b,x[15],14,0xd8a1e681); RND2 (b,c,d,a,x[ 4],20,0xe7d3fbc8);
+ RND2 (a,b,c,d,x[ 9], 5,0x21e1cde6); RND2 (d,a,b,c,x[14], 9,0xc33707d6);
+ RND2 (c,d,a,b,x[ 3],14,0xf4d50d87); RND2 (b,c,d,a,x[ 8],20,0x455a14ed);
+ RND2 (a,b,c,d,x[13], 5,0xa9e3e905); RND2 (d,a,b,c,x[ 2], 9,0xfcefa3f8);
+ RND2 (c,d,a,b,x[ 7],14,0x676f02d9); RND2 (b,c,d,a,x[12],20,0x8d2a4c8a);
+ /* round 3 */
+ RND3 (a,b,c,d,x[ 5], 4,0xfffa3942); RND3 (d,a,b,c,x[ 8],11,0x8771f681);
+ RND3 (c,d,a,b,x[11],16,0x6d9d6122); RND3 (b,c,d,a,x[14],23,0xfde5380c);
+ RND3 (a,b,c,d,x[ 1], 4,0xa4beea44); RND3 (d,a,b,c,x[ 4],11,0x4bdecfa9);
+ RND3 (c,d,a,b,x[ 7],16,0xf6bb4b60); RND3 (b,c,d,a,x[10],23,0xbebfbc70);
+ RND3 (a,b,c,d,x[13], 4,0x289b7ec6); RND3 (d,a,b,c,x[ 0],11,0xeaa127fa);
+ RND3 (c,d,a,b,x[ 3],16,0xd4ef3085); RND3 (b,c,d,a,x[ 6],23, 0x4881d05);
+ RND3 (a,b,c,d,x[ 9], 4,0xd9d4d039); RND3 (d,a,b,c,x[12],11,0xe6db99e5);
+ RND3 (c,d,a,b,x[15],16,0x1fa27cf8); RND3 (b,c,d,a,x[ 2],23,0xc4ac5665);
+ /* round 4 */
+ RND4 (a,b,c,d,x[ 0], 6,0xf4292244); RND4 (d,a,b,c,x[ 7],10,0x432aff97);
+ RND4 (c,d,a,b,x[14],15,0xab9423a7); RND4 (b,c,d,a,x[ 5],21,0xfc93a039);
+ RND4 (a,b,c,d,x[12], 6,0x655b59c3); RND4 (d,a,b,c,x[ 3],10,0x8f0ccc92);
+ RND4 (c,d,a,b,x[10],15,0xffeff47d); RND4 (b,c,d,a,x[ 1],21,0x85845dd1);
+ RND4 (a,b,c,d,x[ 8], 6,0x6fa87e4f); RND4 (d,a,b,c,x[15],10,0xfe2ce6e0);
+ RND4 (c,d,a,b,x[ 6],15,0xa3014314); RND4 (b,c,d,a,x[13],21,0x4e0811a1);
+ RND4 (a,b,c,d,x[ 4], 6,0xf7537e82); RND4 (d,a,b,c,x[11],10,0xbd3af235);
+ RND4 (c,d,a,b,x[ 2],15,0x2ad7d2bb); RND4 (b,c,d,a,x[ 9],21,0xeb86d391);
+ /* update state */
+ state[0] += a; state[1] += b; state[2] += c; state[3] += d;
+ memset (x,0,sizeof (x)); /* erase sensitive data */
+}
+
+/* You may wonder why these strange "& 0xff" maskings are here. This is to
+ * ensure correct results on machines with a char size of larger than 8 bits.
+ * For example, the KCC compiler on the PDP-10 uses 9-bit chars.
+ */
+
+/* MD5 encode unsigned long into LSB-first bytes
+ * Accepts: destination pointer
+ * source
+ * length of source
+ */
+
+static void md5_encode (unsigned char *dst,unsigned long *src,int len)
+{
+ int i;
+ for (i = 0; i < len; i++) {
+ *dst++ = (unsigned char) (src[i] & 0xff);
+ *dst++ = (unsigned char) ((src[i] >> 8) & 0xff);
+ *dst++ = (unsigned char) ((src[i] >> 16) & 0xff);
+ *dst++ = (unsigned char) ((src[i] >> 24) & 0xff);
+ }
+}
+
+
+/* MD5 decode LSB-first bytes into unsigned long
+ * Accepts: destination pointer
+ * source
+ * length of destination
+ */
+
+static void md5_decode (unsigned long *dst,unsigned char *src,int len)
+{
+ int i, j;
+ for (i = 0, j = 0; i < len; i++, j += 4)
+ dst[i] = ((unsigned long) (src[j] & 0xff)) |
+ (((unsigned long) (src[j+1] & 0xff)) << 8) |
+ (((unsigned long) (src[j+2] & 0xff)) << 16) |
+ (((unsigned long) (src[j+3] & 0xff)) << 24);
+}
diff --git a/imap/src/c-client/auth_pla.c b/imap/src/c-client/auth_pla.c
new file mode 100644
index 00000000..73ca2259
--- /dev/null
+++ b/imap/src/c-client/auth_pla.c
@@ -0,0 +1,133 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Plain authenticator
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 22 September 1998
+ * Last Edited: 30 August 2006
+ */
+
+long auth_plain_client (authchallenge_t challenger,authrespond_t responder,
+ char *service,NETMBX *mb,void *stream,
+ unsigned long *trial,char *user);
+char *auth_plain_server (authresponse_t responder,int argc,char *argv[]);
+
+AUTHENTICATOR auth_pla = {
+ AU_AUTHUSER | AU_HIDE, /* allow authuser, hidden */
+ "PLAIN", /* authenticator name */
+ NIL, /* always valid */
+ auth_plain_client, /* client method */
+ auth_plain_server, /* server method */
+ NIL /* next authenticator */
+};
+
+/* Client authenticator
+ * Accepts: challenger function
+ * responder function
+ * SASL service name
+ * parsed network mailbox structure
+ * stream argument for functions
+ * pointer to current trial count
+ * returned user name
+ * Returns: T if success, NIL otherwise, number of trials incremented if retry
+ */
+
+long auth_plain_client (authchallenge_t challenger,authrespond_t responder,
+ char *service,NETMBX *mb,void *stream,
+ unsigned long *trial,char *user)
+{
+ char *u,pwd[MAILTMPLEN];
+ void *challenge;
+ unsigned long clen;
+ long ret = NIL;
+ /* snarl if not SSL/TLS session */
+ if (!mb->sslflag && !mb->tlsflag)
+ mm_log ("SECURITY PROBLEM: insecure server advertised AUTH=PLAIN",WARN);
+ /* get initial (empty) challenge */
+ if (challenge = (*challenger) (stream,&clen)) {
+ fs_give ((void **) &challenge);
+ if (clen) { /* abort if challenge non-empty */
+ mm_log ("Server bug: non-empty initial PLAIN challenge",WARN);
+ (*responder) (stream,NIL,0);
+ ret = LONGT; /* will get a BAD response back */
+ }
+ pwd[0] = NIL; /* prompt user if empty challenge */
+ mm_login (mb,user,pwd,*trial);
+ if (!pwd[0]) { /* empty challenge or user requested abort */
+ (*responder) (stream,NIL,0);
+ *trial = 0; /* cancel subsequent attempts */
+ ret = LONGT; /* will get a BAD response back */
+ }
+ else {
+ unsigned long rlen =
+ strlen (mb->authuser) + strlen (user) + strlen (pwd) + 2;
+ char *response = (char *) fs_get (rlen);
+ char *t = response; /* copy authorization id */
+ if (mb->authuser[0]) for (u = user; *u; *t++ = *u++);
+ *t++ = '\0'; /* delimiting NUL */
+ /* copy authentication id */
+ for (u = mb->authuser[0] ? mb->authuser : user; *u; *t++ = *u++);
+ *t++ = '\0'; /* delimiting NUL */
+ /* copy password */
+ for (u = pwd; *u; *t++ = *u++);
+ /* send credentials */
+ if ((*responder) (stream,response,rlen)) {
+ if (challenge = (*challenger) (stream,&clen))
+ fs_give ((void **) &challenge);
+ else {
+ ++*trial; /* can try again if necessary */
+ ret = LONGT; /* check the authentication */
+ }
+ }
+ memset (response,0,rlen); /* erase credentials */
+ fs_give ((void **) &response);
+ }
+ }
+ memset (pwd,0,MAILTMPLEN); /* erase password */
+ if (!ret) *trial = 65535; /* don't retry if bad protocol */
+ return ret;
+}
+
+/* Server authenticator
+ * Accepts: responder function
+ * argument count
+ * argument vector
+ * Returns: authenticated user name or NIL
+ */
+
+char *auth_plain_server (authresponse_t responder,int argc,char *argv[])
+{
+ char *ret = NIL;
+ char *user,*aid,*pass;
+ unsigned long len;
+ /* get user name */
+ if (aid = (*responder) ("",0,&len)) {
+ /* note: responders null-terminate */
+ if ((((unsigned long) ((user = aid + strlen (aid) + 1) - aid)) < len) &&
+ (((unsigned long) ((pass = user + strlen (user) + 1) - aid)) < len) &&
+ (((unsigned long) ((pass + strlen (pass)) - aid)) == len) &&
+ (*aid ? server_login (aid,pass,user,argc,argv) :
+ server_login (user,pass,NIL,argc,argv))) ret = myusername ();
+ fs_give ((void **) &aid);
+ }
+ return ret;
+}
diff --git a/imap/src/c-client/c-client.h b/imap/src/c-client/c-client.h
new file mode 100644
index 00000000..7bf3710a
--- /dev/null
+++ b/imap/src/c-client/c-client.h
@@ -0,0 +1,55 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: c-client master include for application programs
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 19 May 2000
+ * Last Edited: 6 December 2006
+ */
+
+#ifndef __CCLIENT_H /* nobody should include this twice... */
+#define __CCLIENT_H
+
+#ifdef __cplusplus /* help out people who use C++ compilers */
+extern "C" {
+ /* If you use gcc, you may also have to use -fno-operator-names */
+#define private cclientPrivate /* private to c-client */
+#define and cclientAnd /* C99 doesn't realize that ISO 646 is dead */
+#define or cclientOr
+#define not cclientNot
+#endif
+
+#include "mail.h" /* primary interfaces */
+#include "osdep.h" /* OS-dependent routines */
+#include "rfc822.h" /* RFC822 and MIME routines */
+#include "smtp.h" /* SMTP sending routines */
+#include "nntp.h" /* NNTP sending routines */
+#include "utf8.h" /* Unicode and charset routines */
+#include "utf8aux.h" /* Unicode auxillary routines */
+#include "misc.h" /* miscellaneous utility routines */
+
+#ifdef __cplusplus /* undo the C++ mischief */
+#undef private
+}
+#endif
+
+#endif
diff --git a/imap/src/c-client/env.h b/imap/src/c-client/env.h
new file mode 100644
index 00000000..d03e7e63
--- /dev/null
+++ b/imap/src/c-client/env.h
@@ -0,0 +1,45 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Environment routines
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 13 February 2008
+ */
+
+/* Function prototypes */
+
+long pmatch_full (unsigned char *s,unsigned char *pat,unsigned char delim);
+long dmatch (unsigned char *s,unsigned char *pat,unsigned char delim);
+void *env_parameters (long function,void *value);
+void rfc822_date (char *date);
+void rfc822_timezone (char *s,void *t);
+void internal_date (char *date);
+long server_input_wait (long seconds);
+void server_init (char *server,char *service,char *sasl,
+ void *clkint,void *kodint,void *hupint,void *trmint,
+ void *staint);
+long server_login (char *user,char *pass,char *authuser,int argc,char *argv[]);
+long authserver_login (char *user,char *authuser,int argc,char *argv[]);
+long anonymous_login (int argc,char *argv[]);
+char *mylocalhost (void);
+char *myhomedir (void);
+char *mailboxfile (char *dst,char *name);
+MAILSTREAM *default_proto (long type);
diff --git a/imap/src/c-client/flstring.c b/imap/src/c-client/flstring.c
new file mode 100644
index 00000000..bbdbe3c8
--- /dev/null
+++ b/imap/src/c-client/flstring.c
@@ -0,0 +1,91 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: File string routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 April 1997
+ * Last Edited: 6 December 2006
+ */
+
+
+#include <stdio.h>
+#include "mail.h"
+#include "flstring.h"
+
+/* String driver for stdio file strings */
+
+static void file_string_init (STRING *s,void *data,unsigned long size);
+static char file_string_next (STRING *s);
+static void file_string_setpos (STRING *s,unsigned long i);
+
+STRINGDRIVER file_string = {
+ file_string_init, /* initialize string structure */
+ file_string_next, /* get next byte in string structure */
+ file_string_setpos /* set position in string structure */
+};
+
+
+/* Initialize mail string structure for file
+ * Accepts: string structure
+ * pointer to string
+ * size of string
+ */
+
+static void file_string_init (STRING *s,void *data,unsigned long size)
+{
+ s->data = data; /* note file descriptor */
+ /* big enough for one byte */
+ s->chunk = s->curpos = (char *) &s->data1;
+ s->size = size; /* data size */
+ s->cursize = s->chunksize = 1;/* always call stdio */
+ file_string_setpos (s,0); /* initial offset is 0 */
+}
+
+
+/* Get next character from string
+ * Accepts: string structure
+ * Returns: character, string structure chunk refreshed
+ */
+
+static char file_string_next (STRING *s)
+{
+ char ret = *s->curpos;
+ s->offset++; /* advance position */
+ s->cursize = 1; /* reset size */
+ *s->curpos = (char) getc ((FILE *) s->data);
+ return ret;
+}
+
+
+/* Set string pointer position
+ * Accepts: string structure
+ * new position
+ */
+
+static void file_string_setpos (STRING *s,unsigned long i)
+{
+ s->offset = i; /* note new offset */
+ fseek ((FILE *) s->data,i,SEEK_SET);
+ /* in case using returnstringstruct hack */
+ s->chunk = s->curpos = (char *) &s->data1;
+ *s->curpos = (char) getc ((FILE *) s->data);
+}
diff --git a/imap/src/c-client/flstring.h b/imap/src/c-client/flstring.h
new file mode 100644
index 00000000..be6a831c
--- /dev/null
+++ b/imap/src/c-client/flstring.h
@@ -0,0 +1,30 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: File string routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 April 1997
+ * Last Edited: 30 August 2006
+ */
+
+
+extern STRINGDRIVER file_string;
diff --git a/imap/src/c-client/fs.h b/imap/src/c-client/fs.h
new file mode 100644
index 00000000..7bf32136
--- /dev/null
+++ b/imap/src/c-client/fs.h
@@ -0,0 +1,34 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Free storage management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Function prototypes */
+
+void *fs_get (size_t size);
+void fs_resize (void **block,size_t size);
+void fs_give (void **block);
diff --git a/imap/src/c-client/ftl.h b/imap/src/c-client/ftl.h
new file mode 100644
index 00000000..59348ad5
--- /dev/null
+++ b/imap/src/c-client/ftl.h
@@ -0,0 +1,32 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Crash management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Function prototypes */
+
+void fatal (char *string);
diff --git a/imap/src/c-client/imap4r1.c b/imap/src/c-client/imap4r1.c
new file mode 100644
index 00000000..1409b37d
--- /dev/null
+++ b/imap/src/c-client/imap4r1.c
@@ -0,0 +1,5672 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Interactive Message Access Protocol 4rev1 (IMAP4R1) routines
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 June 1988
+ * Last Edited: 8 May 2008
+ *
+ * This original version of this file is
+ * Copyright 1988 Stanford University
+ * and was developed in the Symbolic Systems Resources Group of the Knowledge
+ * Systems Laboratory at Stanford University in 1987-88, and was funded by the
+ * Biomedical Research Technology Program of the National Institutes of Health
+ * under grant number RR-00785.
+ */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <time.h>
+#include "c-client.h"
+#include "imap4r1.h"
+
+/* Parameters */
+
+#define IMAPLOOKAHEAD 20 /* envelope lookahead */
+#define IMAPUIDLOOKAHEAD 1000 /* UID lookahead */
+#define IMAPTCPPORT (long) 143 /* assigned TCP contact port */
+#define IMAPSSLPORT (long) 993 /* assigned SSL TCP contact port */
+#define MAXCOMMAND 1000 /* RFC 2683 guideline for cmd line length */
+#define IDLETIMEOUT (long) 30 /* defined in RFC 3501 */
+#define MAXSERVERLIT 0x7ffffffe /* maximum server literal size
+ * must be smaller than 4294967295
+ */
+
+
+/* Parsed reply message from imap_reply */
+
+typedef struct imap_parsed_reply {
+ unsigned char *line; /* original reply string pointer */
+ unsigned char *tag; /* command tag this reply is for */
+ unsigned char *key; /* reply keyword */
+ unsigned char *text; /* subsequent text */
+} IMAPPARSEDREPLY;
+
+
+#define IMAPTMPLEN 16*MAILTMPLEN
+
+
+/* IMAP4 I/O stream local data */
+
+typedef struct imap_local {
+ NETSTREAM *netstream; /* TCP I/O stream */
+ IMAPPARSEDREPLY reply; /* last parsed reply */
+ MAILSTATUS *stat; /* status to fill in */
+ IMAPCAP cap; /* server capabilities */
+ char *appendmailbox; /* mailbox being appended to */
+ unsigned int uidsearch : 1; /* UID searching */
+ unsigned int byeseen : 1; /* saw a BYE response */
+ /* got implicit capabilities */
+ unsigned int gotcapability : 1;
+ unsigned int sensitive : 1; /* sensitive data in progress */
+ unsigned int tlsflag : 1; /* TLS session */
+ unsigned int tlssslv23 : 1; /* TLS using SSLv23 client method */
+ unsigned int notlsflag : 1; /* TLS not used in session */
+ unsigned int sslflag : 1; /* SSL session */
+ unsigned int novalidate : 1; /* certificate not validated */
+ unsigned int filter : 1; /* filter SEARCH/SORT/THREAD results */
+ unsigned int loser : 1; /* server is a loser */
+ unsigned int saslcancel : 1; /* SASL cancelled by protocol */
+ long authflags; /* required flags for authenticators */
+ unsigned long sortsize; /* sort return data size */
+ unsigned long *sortdata; /* sort return data */
+ struct {
+ unsigned long uid; /* last UID returned */
+ unsigned long msgno; /* last msgno returned */
+ } lastuid;
+ NAMESPACE **namespace; /* namespace return data */
+ THREADNODE *threaddata; /* thread return data */
+ char *referral; /* last referral */
+ char *prefix; /* find prefix */
+ char *user; /* logged-in user */
+ char *reform; /* reformed sequence */
+ char tmp[IMAPTMPLEN]; /* temporary buffer */
+ SEARCHSET *lookahead; /* fetch lookahead */
+} IMAPLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((IMAPLOCAL *) stream->local)
+
+/* Arguments to imap_send() */
+
+typedef struct imap_argument {
+ int type; /* argument type */
+ void *text; /* argument text */
+} IMAPARG;
+
+
+/* imap_send() argument types */
+
+#define ATOM 0
+#define NUMBER 1
+#define FLAGS 2
+#define ASTRING 3
+#define LITERAL 4
+#define LIST 5
+#define SEARCHPROGRAM 6
+#define SORTPROGRAM 7
+#define BODYTEXT 8
+#define BODYPEEK 9
+#define BODYCLOSE 10
+#define SEQUENCE 11
+#define LISTMAILBOX 12
+#define MULTIAPPEND 13
+#define SNLIST 14
+#define MULTIAPPENDREDO 15
+
+
+/* Append data */
+
+typedef struct append_data {
+ append_t af;
+ void *data;
+ char *flags;
+ char *date;
+ STRING *message;
+} APPENDDATA;
+
+/* Function prototypes */
+
+DRIVER *imap_valid (char *name);
+void *imap_parameters (long function,void *value);
+void imap_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void imap_list (MAILSTREAM *stream,char *ref,char *pat);
+void imap_lsub (MAILSTREAM *stream,char *ref,char *pat);
+void imap_list_work (MAILSTREAM *stream,char *cmd,char *ref,char *pat,
+ char *contents);
+long imap_subscribe (MAILSTREAM *stream,char *mailbox);
+long imap_unsubscribe (MAILSTREAM *stream,char *mailbox);
+long imap_create (MAILSTREAM *stream,char *mailbox);
+long imap_delete (MAILSTREAM *stream,char *mailbox);
+long imap_rename (MAILSTREAM *stream,char *old,char *newname);
+long imap_manage (MAILSTREAM *stream,char *mailbox,char *command,char *arg2);
+long imap_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *imap_open (MAILSTREAM *stream);
+IMAPPARSEDREPLY *imap_rimap (MAILSTREAM *stream,char *service,NETMBX *mb,
+ char *usr,char *tmp);
+long imap_anon (MAILSTREAM *stream,char *tmp);
+long imap_auth (MAILSTREAM *stream,NETMBX *mb,char *tmp,char *usr);
+long imap_login (MAILSTREAM *stream,NETMBX *mb,char *pwd,char *usr);
+void *imap_challenge (void *stream,unsigned long *len);
+long imap_response (void *stream,char *s,unsigned long size);
+void imap_close (MAILSTREAM *stream,long options);
+void imap_fast (MAILSTREAM *stream,char *sequence,long flags);
+void imap_flags (MAILSTREAM *stream,char *sequence,long flags);
+long imap_overview (MAILSTREAM *stream,overview_t ofn);
+ENVELOPE *imap_structure (MAILSTREAM *stream,unsigned long msgno,BODY **body,
+ long flags);
+long imap_msgdata (MAILSTREAM *stream,unsigned long msgno,char *section,
+ unsigned long first,unsigned long last,STRINGLIST *lines,
+ long flags);
+unsigned long imap_uid (MAILSTREAM *stream,unsigned long msgno);
+unsigned long imap_msgno (MAILSTREAM *stream,unsigned long uid);
+void imap_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+long imap_search (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,long flags);
+unsigned long *imap_sort (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
+ SORTPGM *pgm,long flags);
+THREADNODE *imap_thread (MAILSTREAM *stream,char *type,char *charset,
+ SEARCHPGM *spg,long flags);
+THREADNODE *imap_thread_work (MAILSTREAM *stream,char *type,char *charset,
+ SEARCHPGM *spg,long flags);
+long imap_ping (MAILSTREAM *stream);
+void imap_check (MAILSTREAM *stream);
+long imap_expunge (MAILSTREAM *stream,char *sequence,long options);
+long imap_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long imap_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+long imap_append_referral (char *mailbox,char *tmp,append_t af,void *data,
+ char *flags,char *date,STRING *message,
+ APPENDDATA *map,long options);
+IMAPPARSEDREPLY *imap_append_single (MAILSTREAM *stream,char *mailbox,
+ char *flags,char *date,STRING *message);
+
+void imap_gc (MAILSTREAM *stream,long gcflags);
+void imap_gc_body (BODY *body);
+void imap_capability (MAILSTREAM *stream);
+long imap_acl_work (MAILSTREAM *stream,char *command,IMAPARG *args[]);
+
+IMAPPARSEDREPLY *imap_send (MAILSTREAM *stream,char *cmd,IMAPARG *args[]);
+IMAPPARSEDREPLY *imap_sout (MAILSTREAM *stream,char *tag,char *base,char **s);
+long imap_soutr (MAILSTREAM *stream,char *string);
+IMAPPARSEDREPLY *imap_send_astring (MAILSTREAM *stream,char *tag,char **s,
+ SIZEDTEXT *as,long wildok,char *limit);
+IMAPPARSEDREPLY *imap_send_literal (MAILSTREAM *stream,char *tag,char **s,
+ STRING *st);
+IMAPPARSEDREPLY *imap_send_spgm (MAILSTREAM *stream,char *tag,char *base,
+ char **s,SEARCHPGM *pgm,char *limit);
+char *imap_send_spgm_trim (char *base,char *s,char *text);
+IMAPPARSEDREPLY *imap_send_sset (MAILSTREAM *stream,char *tag,char *base,
+ char **s,SEARCHSET *set,char *prefix,
+ char *limit);
+IMAPPARSEDREPLY *imap_send_slist (MAILSTREAM *stream,char *tag,char *base,
+ char **s,char *name,STRINGLIST *list,
+ char *limit);
+void imap_send_sdate (char **s,char *name,unsigned short date);
+IMAPPARSEDREPLY *imap_reply (MAILSTREAM *stream,char *tag);
+IMAPPARSEDREPLY *imap_parse_reply (MAILSTREAM *stream,char *text);
+IMAPPARSEDREPLY *imap_fake (MAILSTREAM *stream,char *tag,char *text);
+long imap_OK (MAILSTREAM *stream,IMAPPARSEDREPLY *reply);
+void imap_parse_unsolicited (MAILSTREAM *stream,IMAPPARSEDREPLY *reply);
+void imap_parse_response (MAILSTREAM *stream,char *text,long errflg,long ntfy);
+NAMESPACE *imap_parse_namespace (MAILSTREAM *stream,unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply);
+THREADNODE *imap_parse_thread (MAILSTREAM *stream,unsigned char **txtptr);
+void imap_parse_header (MAILSTREAM *stream,ENVELOPE **env,SIZEDTEXT *hdr,
+ STRINGLIST *stl);
+void imap_parse_envelope (MAILSTREAM *stream,ENVELOPE **env,
+ unsigned char **txtptr,IMAPPARSEDREPLY *reply);
+ADDRESS *imap_parse_adrlist (MAILSTREAM *stream,unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply);
+ADDRESS *imap_parse_address (MAILSTREAM *stream,unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply);
+void imap_parse_flags (MAILSTREAM *stream,MESSAGECACHE *elt,
+ unsigned char **txtptr);
+unsigned long imap_parse_user_flag (MAILSTREAM *stream,char *flag);
+unsigned char *imap_parse_astring (MAILSTREAM *stream,unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply,unsigned long *len);
+unsigned char *imap_parse_string (MAILSTREAM *stream,unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply,GETS_DATA *md,
+ unsigned long *len,long flags);
+void imap_parse_body (GETS_DATA *md,char *seg,unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply);
+void imap_parse_body_structure (MAILSTREAM *stream,BODY *body,
+ unsigned char **txtptr,IMAPPARSEDREPLY *reply);
+PARAMETER *imap_parse_body_parameter (MAILSTREAM *stream,
+ unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply);
+void imap_parse_disposition (MAILSTREAM *stream,BODY *body,
+ unsigned char **txtptr,IMAPPARSEDREPLY *reply);
+STRINGLIST *imap_parse_language (MAILSTREAM *stream,unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply);
+STRINGLIST *imap_parse_stringlist (MAILSTREAM *stream,unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply);
+void imap_parse_extension (MAILSTREAM *stream,unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply);
+void imap_parse_capabilities (MAILSTREAM *stream,char *t);
+IMAPPARSEDREPLY *imap_fetch (MAILSTREAM *stream,char *sequence,long flags);
+char *imap_reform_sequence (MAILSTREAM *stream,char *sequence,long flags);
+
+/* Driver dispatch used by MAIL */
+
+DRIVER imapdriver = {
+ "imap", /* driver name */
+ /* driver flags */
+ DR_MAIL|DR_NEWS|DR_NAMESPACE|DR_CRLF|DR_RECYCLE|DR_HALFOPEN,
+ (DRIVER *) NIL, /* next driver */
+ imap_valid, /* mailbox is valid for us */
+ imap_parameters, /* manipulate parameters */
+ imap_scan, /* scan mailboxes */
+ imap_list, /* find mailboxes */
+ imap_lsub, /* find subscribed mailboxes */
+ imap_subscribe, /* subscribe to mailbox */
+ imap_unsubscribe, /* unsubscribe from mailbox */
+ imap_create, /* create mailbox */
+ imap_delete, /* delete mailbox */
+ imap_rename, /* rename mailbox */
+ imap_status, /* status of mailbox */
+ imap_open, /* open mailbox */
+ imap_close, /* close mailbox */
+ imap_fast, /* fetch message "fast" attributes */
+ imap_flags, /* fetch message flags */
+ imap_overview, /* fetch overview */
+ imap_structure, /* fetch message envelopes */
+ NIL, /* fetch message header */
+ NIL, /* fetch message body */
+ imap_msgdata, /* fetch partial message */
+ imap_uid, /* unique identifier */
+ imap_msgno, /* message number */
+ imap_flag, /* modify flags */
+ NIL, /* per-message modify flags */
+ imap_search, /* search for message based on criteria */
+ imap_sort, /* sort messages */
+ imap_thread, /* thread messages */
+ imap_ping, /* ping mailbox to see if still alive */
+ imap_check, /* check for new messages */
+ imap_expunge, /* expunge deleted messages */
+ imap_copy, /* copy messages to another mailbox */
+ imap_append, /* append string message to mailbox */
+ imap_gc /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM imapproto = {&imapdriver};
+
+ /* driver parameters */
+static unsigned long imap_maxlogintrials = MAXLOGINTRIALS;
+static long imap_lookahead = IMAPLOOKAHEAD;
+static long imap_uidlookahead = IMAPUIDLOOKAHEAD;
+static long imap_fetchlookaheadlimit = IMAPLOOKAHEAD;
+static long imap_defaultport = 0;
+static long imap_sslport = 0;
+static long imap_tryssl = NIL;
+static long imap_prefetch = IMAPLOOKAHEAD;
+static long imap_closeonerror = NIL;
+static imapenvelope_t imap_envelope = NIL;
+static imapreferral_t imap_referral = NIL;
+static char *imap_extrahdrs = NIL;
+
+ /* constants */
+static char *hdrheader[] = {
+ "BODY.PEEK[HEADER.FIELDS (Newsgroups Content-MD5 Content-Disposition Content-Language Content-Location",
+ "BODY.PEEK[HEADER.FIELDS (Newsgroups Content-Disposition Content-Language Content-Location",
+ "BODY.PEEK[HEADER.FIELDS (Newsgroups Content-Language Content-Location",
+ "BODY.PEEK[HEADER.FIELDS (Newsgroups Content-Location",
+ "BODY.PEEK[HEADER.FIELDS (Newsgroups"
+};
+static char *hdrtrailer ="Followup-To References)]";
+
+/* IMAP validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *imap_valid (char *name)
+{
+ return mail_valid_net (name,&imapdriver,NIL,NIL);
+}
+
+
+/* IMAP manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *imap_parameters (long function,void *value)
+{
+ switch ((int) function) {
+ case GET_NAMESPACE:
+ if (((IMAPLOCAL *) ((MAILSTREAM *) value)->local)->cap.namespace &&
+ !((IMAPLOCAL *) ((MAILSTREAM *) value)->local)->namespace)
+ imap_send (((MAILSTREAM *) value),"NAMESPACE",NIL);
+ value = (void *) &((IMAPLOCAL *) ((MAILSTREAM *) value)->local)->namespace;
+ break;
+ case GET_THREADERS:
+ value = (void *)
+ ((IMAPLOCAL *) ((MAILSTREAM *) value)->local)->cap.threader;
+ break;
+ case SET_FETCHLOOKAHEAD: /* must use pointer from GET_FETCHLOOKAHEAD */
+ fatal ("SET_FETCHLOOKAHEAD not permitted");
+ case GET_FETCHLOOKAHEAD:
+ value = (void *) &((IMAPLOCAL *) ((MAILSTREAM *) value)->local)->lookahead;
+ break;
+ case SET_MAXLOGINTRIALS:
+ imap_maxlogintrials = (long) value;
+ break;
+ case GET_MAXLOGINTRIALS:
+ value = (void *) imap_maxlogintrials;
+ break;
+ case SET_LOOKAHEAD:
+ imap_lookahead = (long) value;
+ break;
+ case GET_LOOKAHEAD:
+ value = (void *) imap_lookahead;
+ break;
+ case SET_UIDLOOKAHEAD:
+ imap_uidlookahead = (long) value;
+ break;
+ case GET_UIDLOOKAHEAD:
+ value = (void *) imap_uidlookahead;
+ break;
+
+ case SET_IMAPPORT:
+ imap_defaultport = (long) value;
+ break;
+ case GET_IMAPPORT:
+ value = (void *) imap_defaultport;
+ break;
+ case SET_SSLIMAPPORT:
+ imap_sslport = (long) value;
+ break;
+ case GET_SSLIMAPPORT:
+ value = (void *) imap_sslport;
+ break;
+ case SET_PREFETCH:
+ imap_prefetch = (long) value;
+ break;
+ case GET_PREFETCH:
+ value = (void *) imap_prefetch;
+ break;
+ case SET_CLOSEONERROR:
+ imap_closeonerror = (long) value;
+ break;
+ case GET_CLOSEONERROR:
+ value = (void *) imap_closeonerror;
+ break;
+ case SET_IMAPENVELOPE:
+ imap_envelope = (imapenvelope_t) value;
+ break;
+ case GET_IMAPENVELOPE:
+ value = (void *) imap_envelope;
+ break;
+ case SET_IMAPREFERRAL:
+ imap_referral = (imapreferral_t) value;
+ break;
+ case GET_IMAPREFERRAL:
+ value = (void *) imap_referral;
+ break;
+ case SET_IMAPEXTRAHEADERS:
+ imap_extrahdrs = (char *) value;
+ break;
+ case GET_IMAPEXTRAHEADERS:
+ value = (void *) imap_extrahdrs;
+ break;
+ case SET_IMAPTRYSSL:
+ imap_tryssl = (long) value;
+ break;
+ case GET_IMAPTRYSSL:
+ value = (void *) imap_tryssl;
+ break;
+ case SET_FETCHLOOKAHEADLIMIT:
+ imap_fetchlookaheadlimit = (long) value;
+ break;
+ case GET_FETCHLOOKAHEADLIMIT:
+ value = (void *) imap_fetchlookaheadlimit;
+ break;
+
+ case SET_IDLETIMEOUT:
+ fatal ("SET_IDLETIMEOUT not permitted");
+ case GET_IDLETIMEOUT:
+ value = (void *) IDLETIMEOUT;
+ break;
+ default:
+ value = NIL; /* error case */
+ break;
+ }
+ return value;
+}
+
+/* IMAP scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void imap_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ imap_list_work (stream,"SCAN",ref,pat,contents);
+}
+
+
+/* IMAP list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void imap_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ imap_list_work (stream,"LIST",ref,pat,NIL);
+}
+
+
+/* IMAP list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void imap_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ void *sdb = NIL;
+ char *s,mbx[MAILTMPLEN];
+ /* do it on the server */
+ imap_list_work (stream,"LSUB",ref,pat,NIL);
+ if (*pat == '{') { /* if remote pattern, must be IMAP */
+ if (!imap_valid (pat)) return;
+ ref = NIL; /* good IMAP pattern, punt reference */
+ }
+ /* if remote reference, must be valid IMAP */
+ if (ref && (*ref == '{') && !imap_valid (ref)) return;
+ /* kludgy application of reference */
+ if (ref && *ref) sprintf (mbx,"%s%s",ref,pat);
+ else strcpy (mbx,pat);
+
+ if (s = sm_read (&sdb)) do if (imap_valid (s) && pmatch (s,mbx))
+ mm_lsub (stream,NIL,s,NIL);
+ while (s = sm_read (&sdb)); /* until no more subscriptions */
+}
+
+/* IMAP find list of mailboxes
+ * Accepts: mail stream
+ * list command
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void imap_list_work (MAILSTREAM *stream,char *cmd,char *ref,char *pat,
+ char *contents)
+{
+ MAILSTREAM *st = stream;
+ int pl;
+ char *s,prefix[MAILTMPLEN],mbx[MAILTMPLEN];
+ IMAPARG *args[4],aref,apat,acont;
+ if (ref && *ref) { /* have a reference? */
+ if (!(imap_valid (ref) && /* make sure valid IMAP name and open stream */
+ ((stream && LOCAL && LOCAL->netstream) ||
+ (stream = mail_open (NIL,ref,OP_HALFOPEN|OP_SILENT))))) return;
+ /* calculate prefix length */
+ pl = strchr (ref,'}') + 1 - ref;
+ strncpy (prefix,ref,pl); /* build prefix */
+ prefix[pl] = '\0'; /* tie off prefix */
+ ref += pl; /* update reference */
+ }
+ else {
+ if (!(imap_valid (pat) && /* make sure valid IMAP name and open stream */
+ ((stream && LOCAL && LOCAL->netstream) ||
+ (stream = mail_open (NIL,pat,OP_HALFOPEN|OP_SILENT))))) return;
+ /* calculate prefix length */
+ pl = strchr (pat,'}') + 1 - pat;
+ strncpy (prefix,pat,pl); /* build prefix */
+ prefix[pl] = '\0'; /* tie off prefix */
+ pat += pl; /* update reference */
+ }
+ LOCAL->prefix = prefix; /* note prefix */
+ if (contents) { /* want to do a scan? */
+ if (LEVELSCAN (stream)) { /* make sure permitted */
+ args[0] = &aref; args[1] = &apat; args[2] = &acont; args[3] = NIL;
+ aref.type = ASTRING; aref.text = (void *) (ref ? ref : "");
+ apat.type = LISTMAILBOX; apat.text = (void *) pat;
+ acont.type = ASTRING; acont.text = (void *) contents;
+ imap_send (stream,cmd,args);
+ }
+ else mm_log ("Scan not valid on this IMAP server",ERROR);
+ }
+
+ else if (LEVELIMAP4 (stream)){/* easy if IMAP4 */
+ args[0] = &aref; args[1] = &apat; args[2] = NIL;
+ aref.type = ASTRING; aref.text = (void *) (ref ? ref : "");
+ apat.type = LISTMAILBOX; apat.text = (void *) pat;
+ /* referrals armed? */
+ if (LOCAL->cap.mbx_ref && mail_parameters (stream,GET_IMAPREFERRAL,NIL)) {
+ /* yes, convert LIST -> RLIST */
+ if (!compare_cstring (cmd,"LIST")) cmd = "RLIST";
+ /* and convert LSUB -> RLSUB */
+ else if (!compare_cstring (cmd,"LSUB")) cmd = "RLSUB";
+ }
+ imap_send (stream,cmd,args);
+ }
+ else if (LEVEL1176 (stream)) {/* convert to IMAP2 format wildcard */
+ /* kludgy application of reference */
+ if (ref && *ref) sprintf (mbx,"%s%s",ref,pat);
+ else strcpy (mbx,pat);
+ for (s = mbx; *s; s++) if (*s == '%') *s = '*';
+ args[0] = &apat; args[1] = NIL;
+ apat.type = LISTMAILBOX; apat.text = (void *) mbx;
+ if (!(strstr (cmd,"LIST") &&/* if list, try IMAP2bis, then RFC-1176 */
+ strcmp (imap_send (stream,"FIND ALL.MAILBOXES",args)->key,"BAD")) &&
+ !strcmp (imap_send (stream,"FIND MAILBOXES",args)->key,"BAD"))
+ LOCAL->cap.rfc1176 = NIL; /* must be RFC-1064 */
+ }
+ LOCAL->prefix = NIL; /* no more prefix */
+ /* close temporary stream if we made one */
+ if (stream != st) mail_close (stream);
+}
+
+/* IMAP subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long imap_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ MAILSTREAM *st = stream;
+ long ret = ((stream && LOCAL && LOCAL->netstream) ||
+ (stream = mail_open (NIL,mailbox,OP_HALFOPEN|OP_SILENT))) ?
+ imap_manage (stream,mailbox,LEVELIMAP4 (stream) ?
+ "Subscribe" : "Subscribe Mailbox",NIL) : NIL;
+ /* toss out temporary stream */
+ if (st != stream) mail_close (stream);
+ return ret;
+}
+
+
+/* IMAP unsubscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to delete from manage list
+ * Returns: T on success, NIL on failure
+ */
+
+long imap_unsubscribe (MAILSTREAM *stream,char *mailbox)
+{
+ MAILSTREAM *st = stream;
+ long ret = ((stream && LOCAL && LOCAL->netstream) ||
+ (stream = mail_open (NIL,mailbox,OP_HALFOPEN|OP_SILENT))) ?
+ imap_manage (stream,mailbox,LEVELIMAP4 (stream) ?
+ "Unsubscribe" : "Unsubscribe Mailbox",NIL) : NIL;
+ /* toss out temporary stream */
+ if (st != stream) mail_close (stream);
+ return ret;
+}
+
+/* IMAP create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long imap_create (MAILSTREAM *stream,char *mailbox)
+{
+ return imap_manage (stream,mailbox,"Create",NIL);
+}
+
+
+/* IMAP delete mailbox
+ * Accepts: mail stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long imap_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return imap_manage (stream,mailbox,"Delete",NIL);
+}
+
+
+/* IMAP rename mailbox
+ * Accepts: mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long imap_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ return imap_manage (stream,old,"Rename",newname);
+}
+
+/* IMAP manage a mailbox
+ * Accepts: mail stream
+ * mailbox to manipulate
+ * command to execute
+ * optional second argument
+ * Returns: T on success, NIL on failure
+ */
+
+long imap_manage (MAILSTREAM *stream,char *mailbox,char *command,char *arg2)
+{
+ MAILSTREAM *st = stream;
+ IMAPPARSEDREPLY *reply;
+ long ret = NIL;
+ char mbx[MAILTMPLEN],mbx2[MAILTMPLEN];
+ IMAPARG *args[3],ambx,amb2;
+ imapreferral_t ir =
+ (imapreferral_t) mail_parameters (stream,GET_IMAPREFERRAL,NIL);
+ ambx.type = amb2.type = ASTRING; ambx.text = (void *) mbx;
+ amb2.text = (void *) mbx2;
+ args[0] = &ambx; args[1] = args[2] = NIL;
+ /* require valid names and open stream */
+ if (mail_valid_net (mailbox,&imapdriver,NIL,mbx) &&
+ (arg2 ? mail_valid_net (arg2,&imapdriver,NIL,mbx2) : &imapdriver) &&
+ ((stream && LOCAL && LOCAL->netstream) ||
+ (stream = mail_open (NIL,mailbox,OP_HALFOPEN|OP_SILENT)))) {
+ if (arg2) args[1] = &amb2; /* second arg present? */
+ if (!(ret = (imap_OK (stream,reply = imap_send (stream,command,args)))) &&
+ ir && LOCAL->referral) {
+ long code = -1;
+ switch (*command) { /* which command was it? */
+ case 'S': code = REFSUBSCRIBE; break;
+ case 'U': code = REFUNSUBSCRIBE; break;
+ case 'C': code = REFCREATE; break;
+ case 'D': code = REFDELETE; break;
+ case 'R': code = REFRENAME; break;
+ default:
+ fatal ("impossible referral command");
+ }
+ if ((code >= 0) && (mailbox = (*ir) (stream,LOCAL->referral,code)))
+ ret = imap_manage (NIL,mailbox,command,(*command == 'R') ?
+ (mailbox + strlen (mailbox) + 1) : NIL);
+ }
+ mm_log (reply->text,ret ? NIL : ERROR);
+ /* toss out temporary stream */
+ if (st != stream) mail_close (stream);
+ }
+ return ret;
+}
+
+/* IMAP status
+ * Accepts: mail stream
+ * mailbox name
+ * status flags
+ * Returns: T on success, NIL on failure
+ */
+
+long imap_status (MAILSTREAM *stream,char *mbx,long flags)
+{
+ IMAPARG *args[3],ambx,aflg;
+ char tmp[MAILTMPLEN];
+ NETMBX mb;
+ unsigned long i;
+ long ret = NIL;
+ MAILSTREAM *tstream = NIL;
+ /* use given stream if (rev1 or halfopen) and
+ right host */
+ if (!((stream && (LEVELIMAP4rev1 (stream) || stream->halfopen) &&
+ mail_usable_network_stream (stream,mbx)) ||
+ (stream = tstream = mail_open (NIL,mbx,OP_HALFOPEN|OP_SILENT))))
+ return NIL;
+ /* parse mailbox name */
+ mail_valid_net_parse (mbx,&mb);
+ args[0] = &ambx;args[1] = NIL;/* set up first argument as mailbox */
+ ambx.type = ASTRING; ambx.text = (void *) mb.mailbox;
+ if (LEVELIMAP4rev1 (stream)) {/* have STATUS command? */
+ imapreferral_t ir;
+ aflg.type = FLAGS; aflg.text = (void *) tmp;
+ args[1] = &aflg; args[2] = NIL;
+ tmp[0] = tmp[1] = '\0'; /* build flag list */
+ if (flags & SA_MESSAGES) strcat (tmp," MESSAGES");
+ if (flags & SA_RECENT) strcat (tmp," RECENT");
+ if (flags & SA_UNSEEN) strcat (tmp," UNSEEN");
+ if (flags & SA_UIDNEXT) strcat (tmp," UIDNEXT");
+ if (flags & SA_UIDVALIDITY) strcat (tmp," UIDVALIDITY");
+ tmp[0] = '(';
+ strcat (tmp,")");
+ /* send "STATUS mailbox flag" */
+ if (imap_OK (stream,imap_send (stream,"STATUS",args))) ret = T;
+ else if ((ir = (imapreferral_t)
+ mail_parameters (stream,GET_IMAPREFERRAL,NIL)) &&
+ LOCAL->referral &&
+ (mbx = (*ir) (stream,LOCAL->referral,REFSTATUS)))
+ ret = imap_status (NIL,mbx,flags | (stream->debug ? SA_DEBUG : NIL));
+ }
+
+ /* IMAP2 way */
+ else if (imap_OK (stream,imap_send (stream,"EXAMINE",args))) {
+ MAILSTATUS status;
+ status.flags = flags & ~ (SA_UIDNEXT | SA_UIDVALIDITY);
+ status.messages = stream->nmsgs;
+ status.recent = stream->recent;
+ status.unseen = 0;
+ if (flags & SA_UNSEEN) { /* must search to get unseen messages */
+ /* clear search vector */
+ for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched = NIL;
+ if (imap_OK (stream,imap_send (stream,"SEARCH UNSEEN",NIL)))
+ for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++)
+ if (mail_elt (stream,i)->searched) status.unseen++;
+ }
+ strcpy (strchr (strcpy (tmp,stream->mailbox),'}') + 1,mb.mailbox);
+ /* pass status to main program */
+ mm_status (stream,tmp,&status);
+ ret = T; /* note success */
+ }
+ if (tstream) mail_close (tstream);
+ return ret; /* success */
+}
+
+/* IMAP open
+ * Accepts: stream to open
+ * Returns: stream to use on success, NIL on failure
+ */
+
+MAILSTREAM *imap_open (MAILSTREAM *stream)
+{
+ unsigned long i,j;
+ char *s,tmp[MAILTMPLEN],usr[MAILTMPLEN];
+ NETMBX mb;
+ IMAPPARSEDREPLY *reply = NIL;
+ imapreferral_t ir =
+ (imapreferral_t) mail_parameters (stream,GET_IMAPREFERRAL,NIL);
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &imapproto;
+ mail_valid_net_parse (stream->mailbox,&mb);
+ usr[0] = '\0'; /* initially no user name */
+ if (LOCAL) { /* if stream opened earlier by us */
+ /* recycle if still alive */
+ if (LOCAL->netstream && (!stream->halfopen || LOCAL->cap.unselect)) {
+ i = stream->silent; /* temporarily mark silent */
+ stream->silent = T; /* don't give mm_exists() events */
+ j = imap_ping (stream); /* learn if stream still alive */
+ stream->silent = i; /* restore prior state */
+ if (j) { /* was stream still alive? */
+ sprintf (tmp,"Reusing connection to %s",net_host (LOCAL->netstream));
+ if (LOCAL->user) sprintf (tmp + strlen (tmp),"/user=\"%s\"",
+ LOCAL->user);
+ if (!stream->silent) mm_log (tmp,(long) NIL);
+ /* unselect if now want halfopen */
+ if (stream->halfopen) imap_send (stream,"UNSELECT",NIL);
+ }
+ else imap_close (stream,NIL);
+ }
+ else imap_close (stream,NIL);
+ }
+ /* copy flags from name */
+ if (mb.dbgflag) stream->debug = T;
+ if (mb.readonlyflag) stream->rdonly = T;
+ if (mb.anoflag) stream->anonymous = T;
+ if (mb.secflag) stream->secure = T;
+ if (mb.trysslflag || imap_tryssl) stream->tryssl = T;
+
+ if (!LOCAL) { /* open new connection if no recycle */
+ NETDRIVER *ssld = (NETDRIVER *) mail_parameters (NIL,GET_SSLDRIVER,NIL);
+ unsigned long defprt = imap_defaultport ? imap_defaultport : IMAPTCPPORT;
+ unsigned long sslport = imap_sslport ? imap_sslport : IMAPSSLPORT;
+ stream->local = /* instantiate localdata */
+ (void *) memset (fs_get (sizeof (IMAPLOCAL)),0,sizeof (IMAPLOCAL));
+ /* assume IMAP2bis server */
+ LOCAL->cap.imap2bis = LOCAL->cap.rfc1176 = T;
+ /* in case server is a loser */
+ if (mb.loser) LOCAL->loser = T;
+ /* desirable authenticators */
+ LOCAL->authflags = (stream->secure ? AU_SECURE : NIL) |
+ (mb.authuser[0] ? AU_AUTHUSER : NIL);
+ /* IMAP connection open logic is more complex than net_open() normally
+ * deals with, because of the simap and rimap hacks.
+ * If the session is anonymous, a specific port is given, or if /ssl or
+ * /tls is set, do net_open() since those conditions override everything
+ * else.
+ */
+ if (stream->anonymous || mb.port || mb.sslflag || mb.tlsflag)
+ reply = (LOCAL->netstream = net_open (&mb,NIL,defprt,ssld,"*imaps",
+ sslport)) ?
+ imap_reply (stream,NIL) : NIL;
+ /*
+ * No overriding conditions, so get the best connection that we can. In
+ * order, attempt to open via simap, tryssl, rimap, and finally TCP.
+ */
+ /* try simap */
+ else if (reply = imap_rimap (stream,"*imap",&mb,usr,tmp));
+ else if (ssld && /* try tryssl if enabled */
+ (stream->tryssl || mail_parameters (NIL,GET_TRYSSLFIRST,NIL)) &&
+ (LOCAL->netstream =
+ net_open_work (ssld,mb.host,"*imaps",sslport,mb.port,
+ (mb.novalidate ? NET_NOVALIDATECERT : 0) |
+ NET_SILENT | NET_TRYSSL))) {
+ if (net_sout (LOCAL->netstream,"",0)) {
+ mb.sslflag = T;
+ reply = imap_reply (stream,NIL);
+ }
+ else { /* flush fake SSL stream */
+ net_close (LOCAL->netstream);
+ LOCAL->netstream = NIL;
+ }
+ }
+ /* try rimap first, then TCP */
+ else if (!(reply = imap_rimap (stream,"imap",&mb,usr,tmp)) &&
+ (LOCAL->netstream = net_open (&mb,NIL,defprt,NIL,NIL,NIL)))
+ reply = imap_reply (stream,NIL);
+ /* make sure greeting is good */
+ if (!reply || strcmp (reply->tag,"*") ||
+ (strcmp (reply->key,"OK") && strcmp (reply->key,"PREAUTH"))) {
+ if (reply) mm_log (reply->text,ERROR);
+ return NIL; /* lost during greeting */
+ }
+
+ /* if connected and not preauthenticated */
+ if (LOCAL->netstream && strcmp (reply->key,"PREAUTH")) {
+ sslstart_t stls = (sslstart_t) mail_parameters (NIL,GET_SSLSTART,NIL);
+ /* get server capabilities */
+ if (!LOCAL->gotcapability) imap_capability (stream);
+ if (LOCAL->netstream && /* does server support STARTTLS? */
+ stls && LOCAL->cap.starttls && !mb.sslflag && !mb.notlsflag &&
+ imap_OK (stream,imap_send (stream,"STARTTLS",NIL))) {
+ mb.tlsflag = T; /* TLS OK, get into TLS at this end */
+ LOCAL->netstream->dtb = ssld;
+ if (!(LOCAL->netstream->stream =
+ (*stls) (LOCAL->netstream->stream,mb.host,
+ (mb.tlssslv23 ? NIL : NET_TLSCLIENT) |
+ (mb.novalidate ? NET_NOVALIDATECERT : NIL)))) {
+ /* drat, drop this connection */
+ if (LOCAL->netstream) net_close (LOCAL->netstream);
+ LOCAL->netstream = NIL;
+ }
+ /* get capabilities now that TLS in effect */
+ if (LOCAL->netstream) imap_capability (stream);
+ }
+ else if (mb.tlsflag) { /* user specified /tls but can't do it */
+ mm_log ("Unable to negotiate TLS with this server",ERROR);
+ return NIL;
+ }
+ if (LOCAL->netstream) { /* still in the land of the living? */
+ if ((long) mail_parameters (NIL,GET_TRUSTDNS,NIL)) {
+ /* remote name for authentication */
+ strncpy (mb.host,(long) mail_parameters(NIL,GET_SASLUSESPTRNAME,NIL)?
+ net_remotehost (LOCAL->netstream) :
+ net_host (LOCAL->netstream),NETMAXHOST-1);
+ mb.host[NETMAXHOST-1] = '\0';
+ }
+ /* need new capabilities after login */
+ LOCAL->gotcapability = NIL;
+ if (!(stream->anonymous ? imap_anon (stream,tmp) :
+ (LOCAL->cap.auth ? imap_auth (stream,&mb,tmp,usr) :
+ imap_login (stream,&mb,tmp,usr)))) {
+ /* failed, is there a referral? */
+ if (ir && LOCAL->referral &&
+ (s = (*ir) (stream,LOCAL->referral,REFAUTHFAILED))) {
+ imap_close (stream,NIL);
+ fs_give ((void **) &stream->mailbox);
+ /* set as new mailbox name to open */
+ stream->mailbox = s;
+ return imap_open (stream);
+ }
+ return NIL; /* authentication failed */
+ }
+ else if (ir && LOCAL->referral &&
+ (s = (*ir) (stream,LOCAL->referral,REFAUTH))) {
+ imap_close (stream,NIL);
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = s; /* set as new mailbox name to open */
+ /* recurse to log in on real site */
+ return imap_open (stream);
+ }
+ }
+ }
+ /* get server capabilities again */
+ if (LOCAL->netstream && !LOCAL->gotcapability) imap_capability (stream);
+ /* save state for future recycling */
+ if (mb.tlsflag) LOCAL->tlsflag = T;
+ if (mb.tlssslv23) LOCAL->tlssslv23 = T;
+ if (mb.notlsflag) LOCAL->notlsflag = T;
+ if (mb.sslflag) LOCAL->sslflag = T;
+ if (mb.novalidate) LOCAL->novalidate = T;
+ if (mb.loser) LOCAL->loser = T;
+ }
+
+ if (LOCAL->netstream) { /* still have a connection? */
+ stream->perm_seen = stream->perm_deleted = stream->perm_answered =
+ stream->perm_draft = LEVELIMAP4 (stream) ? NIL : T;
+ stream->perm_user_flags = LEVELIMAP4 (stream) ? NIL : 0xffffffff;
+ stream->sequence++; /* bump sequence number */
+ sprintf (tmp,"{%s",(long) mail_parameters (NIL,GET_TRUSTDNS,NIL) ?
+ net_host (LOCAL->netstream) : mb.host);
+ if (!((i = net_port (LOCAL->netstream)) & 0xffff0000))
+ sprintf (tmp + strlen (tmp),":%lu",i);
+ strcat (tmp,"/imap");
+ if (LOCAL->tlsflag) strcat (tmp,"/tls");
+ if (LOCAL->tlssslv23) strcat (tmp,"/tls-sslv23");
+ if (LOCAL->notlsflag) strcat (tmp,"/notls");
+ if (LOCAL->sslflag) strcat (tmp,"/ssl");
+ if (LOCAL->novalidate) strcat (tmp,"/novalidate-cert");
+ if (LOCAL->loser) strcat (tmp,"/loser");
+ if (stream->secure) strcat (tmp,"/secure");
+ if (stream->rdonly) strcat (tmp,"/readonly");
+ if (stream->anonymous) strcat (tmp,"/anonymous");
+ else { /* record user name */
+ if (!LOCAL->user && usr[0]) LOCAL->user = cpystr (usr);
+ if (LOCAL->user) sprintf (tmp + strlen (tmp),"/user=\"%s\"",
+ LOCAL->user);
+ }
+ strcat (tmp,"}");
+
+ if (!stream->halfopen) { /* wants to open a mailbox? */
+ IMAPARG *args[2];
+ IMAPARG ambx;
+ ambx.type = ASTRING;
+ ambx.text = (void *) mb.mailbox;
+ args[0] = &ambx; args[1] = NIL;
+ stream->nmsgs = 0;
+ if (imap_OK (stream,reply = imap_send (stream,stream->rdonly ?
+ "EXAMINE": "SELECT",args))) {
+ strcat (tmp,mb.mailbox);/* mailbox name */
+ if (!stream->nmsgs && !stream->silent)
+ mm_log ("Mailbox is empty",(long) NIL);
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (mb.mailbox,"INBOX");
+ }
+ else if (ir && LOCAL->referral &&
+ (s = (*ir) (stream,LOCAL->referral,REFSELECT))) {
+ imap_close (stream,NIL);
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = s; /* set as new mailbox name to open */
+ return imap_open (stream);
+ }
+ else {
+ mm_log (reply->text,ERROR);
+ if (imap_closeonerror) return NIL;
+ stream->halfopen = T; /* let him keep it half-open */
+ }
+ }
+ if (stream->halfopen) { /* half-open connection? */
+ strcat (tmp,"<no_mailbox>");
+ /* make sure dummy message counts */
+ mail_exists (stream,(long) 0);
+ mail_recent (stream,(long) 0);
+ }
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ }
+ /* success if stream open */
+ return LOCAL->netstream ? stream : NIL;
+}
+
+/* IMAP rimap connect
+ * Accepts: MAIL stream
+ * NETMBX specification
+ * service to use
+ * user name
+ * scratch buffer
+ * Returns: parsed reply if success, else NIL
+ */
+
+IMAPPARSEDREPLY *imap_rimap (MAILSTREAM *stream,char *service,NETMBX *mb,
+ char *usr,char *tmp)
+{
+ unsigned long i;
+ char c[2];
+ NETSTREAM *tstream;
+ IMAPPARSEDREPLY *reply = NIL;
+ /* try rimap open */
+ if (!mb->norsh && (tstream = net_aopen (NIL,mb,service,usr))) {
+ /* if success, see if reasonable banner */
+ if (net_getbuffer (tstream,(long) 1,c) && (*c == '*')) {
+ i = 0; /* copy to buffer */
+ do tmp[i++] = *c;
+ while (net_getbuffer (tstream,(long) 1,c) && (*c != '\015') &&
+ (*c != '\012') && (i < (MAILTMPLEN-1)));
+ tmp[i] = '\0'; /* tie off */
+ /* snarfed a valid greeting? */
+ if ((*c == '\015') && net_getbuffer (tstream,(long) 1,c) &&
+ (*c == '\012') &&
+ !strcmp ((reply = imap_parse_reply (stream,cpystr (tmp)))->tag,"*")){
+ /* parse line as IMAP */
+ imap_parse_unsolicited (stream,reply);
+ /* make sure greeting is good */
+ if (!strcmp (reply->key,"OK") || !strcmp (reply->key,"PREAUTH")) {
+ LOCAL->netstream = tstream;
+ return reply; /* return success */
+ }
+ }
+ }
+ net_close (tstream); /* failed, punt the temporary netstream */
+ }
+ return NIL;
+}
+
+/* IMAP log in as anonymous
+ * Accepts: stream to authenticate
+ * scratch buffer
+ * Returns: T on success, NIL on failure
+ */
+
+long imap_anon (MAILSTREAM *stream,char *tmp)
+{
+ IMAPPARSEDREPLY *reply;
+ char *s = net_localhost (LOCAL->netstream);
+ if (LOCAL->cap.authanon) {
+ char tag[16];
+ unsigned long i;
+ char *broken = "[CLOSED] IMAP connection broken (anonymous auth)";
+ sprintf (tag,"%08lx",0xffffffff & (stream->gensym++));
+ /* build command */
+ sprintf (tmp,"%s AUTHENTICATE ANONYMOUS",tag);
+ if (!imap_soutr (stream,tmp)) {
+ mm_log (broken,ERROR);
+ return NIL;
+ }
+ if (imap_challenge (stream,&i)) imap_response (stream,s,strlen (s));
+ /* get response */
+ if (!(reply = &LOCAL->reply)->tag) reply = imap_fake (stream,tag,broken);
+ /* what we wanted? */
+ if (compare_cstring (reply->tag,tag)) {
+ /* abort if don't have tagged response */
+ while (compare_cstring ((reply = imap_reply (stream,tag))->tag,tag))
+ imap_soutr (stream,"*");
+ }
+ }
+ else {
+ IMAPARG *args[2];
+ IMAPARG ausr;
+ ausr.type = ASTRING;
+ ausr.text = (void *) s;
+ args[0] = &ausr; args[1] = NIL;
+ /* send "LOGIN anonymous <host>" */
+ reply = imap_send (stream,"LOGIN ANONYMOUS",args);
+ }
+ /* success if reply OK */
+ if (imap_OK (stream,reply)) return T;
+ mm_log (reply->text,ERROR);
+ return NIL;
+}
+
+/* IMAP authenticate
+ * Accepts: stream to authenticate
+ * parsed network mailbox structure
+ * scratch buffer
+ * place to return user name
+ * Returns: T on success, NIL on failure
+ */
+
+long imap_auth (MAILSTREAM *stream,NETMBX *mb,char *tmp,char *usr)
+{
+ unsigned long trial,ua;
+ int ok;
+ char tag[16];
+ char *lsterr = NIL;
+ AUTHENTICATOR *at;
+ IMAPPARSEDREPLY *reply;
+ for (ua = LOCAL->cap.auth, LOCAL->saslcancel = NIL; LOCAL->netstream && ua &&
+ (at = mail_lookup_auth (find_rightmost_bit (&ua) + 1));) {
+ if (lsterr) { /* previous authenticator failed? */
+ sprintf (tmp,"Retrying using %s authentication after %.80s",
+ at->name,lsterr);
+ mm_log (tmp,NIL);
+ fs_give ((void **) &lsterr);
+ }
+ trial = 0; /* initial trial count */
+ tmp[0] = '\0'; /* no error */
+ do { /* gensym a new tag */
+ if (lsterr) { /* previous attempt with this one failed? */
+ sprintf (tmp,"Retrying %s authentication after %.80s",at->name,lsterr);
+ mm_log (tmp,WARN);
+ fs_give ((void **) &lsterr);
+ }
+ LOCAL->saslcancel = NIL;
+ sprintf (tag,"%08lx",0xffffffff & (stream->gensym++));
+ /* build command */
+ sprintf (tmp,"%s AUTHENTICATE %s",tag,at->name);
+ if (imap_soutr (stream,tmp)) {
+ /* hide client authentication responses */
+ if (!(at->flags & AU_SECURE)) LOCAL->sensitive = T;
+ ok = (*at->client) (imap_challenge,imap_response,"imap",mb,stream,
+ &trial,usr);
+ LOCAL->sensitive = NIL; /* unhide */
+ /* make sure have a response */
+ if (!(reply = &LOCAL->reply)->tag)
+ reply = imap_fake (stream,tag,
+ "[CLOSED] IMAP connection broken (authenticate)");
+ else if (compare_cstring (reply->tag,tag))
+ while (compare_cstring ((reply = imap_reply (stream,tag))->tag,tag))
+ imap_soutr (stream,"*");
+ /* good if SASL ok and success response */
+ if (ok && imap_OK (stream,reply)) return T;
+ if (!trial) { /* if main program requested cancellation */
+ mm_log ("IMAP Authentication cancelled",ERROR);
+ return NIL;
+ }
+ /* no error if protocol-initiated cancel */
+ lsterr = cpystr (reply->text);
+ }
+ }
+ while (LOCAL->netstream && !LOCAL->byeseen && trial &&
+ (trial < imap_maxlogintrials));
+ }
+ if (lsterr) { /* previous authenticator failed? */
+ if (!LOCAL->saslcancel) { /* don't do this if a cancel */
+ sprintf (tmp,"Can not authenticate to IMAP server: %.80s",lsterr);
+ mm_log (tmp,ERROR);
+ }
+ fs_give ((void **) &lsterr);
+ }
+ return NIL; /* ran out of authenticators */
+}
+
+/* IMAP login
+ * Accepts: stream to login
+ * parsed network mailbox structure
+ * scratch buffer of length MAILTMPLEN
+ * place to return user name
+ * Returns: T on success, NIL on failure
+ */
+
+long imap_login (MAILSTREAM *stream,NETMBX *mb,char *pwd,char *usr)
+{
+ unsigned long trial = 0;
+ IMAPPARSEDREPLY *reply;
+ IMAPARG *args[3];
+ IMAPARG ausr,apwd;
+ long ret = NIL;
+ if (stream->secure) /* never do LOGIN if want security */
+ mm_log ("Can't do secure authentication with this server",ERROR);
+ /* never do LOGIN if server disabled it */
+ else if (LOCAL->cap.logindisabled)
+ mm_log ("Server disables LOGIN, no recognized SASL authenticator",ERROR);
+ else if (mb->authuser[0]) /* never do LOGIN with /authuser */
+ mm_log ("Can't do /authuser with this server",ERROR);
+ else { /* OK to try login */
+ ausr.type = apwd.type = ASTRING;
+ ausr.text = (void *) usr;
+ apwd.text = (void *) pwd;
+ args[0] = &ausr; args[1] = &apwd; args[2] = NIL;
+ do {
+ pwd[0] = 0; /* prompt user for password */
+ mm_login (mb,usr,pwd,trial++);
+ if (pwd[0]) { /* send login command if have password */
+ LOCAL->sensitive = T; /* hide this command */
+ /* send "LOGIN usr pwd" */
+ if (imap_OK (stream,reply = imap_send (stream,"LOGIN",args)))
+ ret = LONGT; /* success */
+ else {
+ mm_log (reply->text,WARN);
+ if (!LOCAL->referral && (trial == imap_maxlogintrials))
+ mm_log ("Too many login failures",ERROR);
+ }
+ LOCAL->sensitive = NIL; /* unhide */
+ }
+ /* user refused to give password */
+ else mm_log ("Login aborted",ERROR);
+ } while (!ret && pwd[0] && (trial < imap_maxlogintrials) &&
+ LOCAL->netstream && !LOCAL->byeseen && !LOCAL->referral);
+ }
+ memset (pwd,0,MAILTMPLEN); /* erase password */
+ return ret;
+}
+
+/* Get challenge to authenticator in binary
+ * Accepts: stream
+ * pointer to returned size
+ * Returns: challenge or NIL if not challenge
+ */
+
+void *imap_challenge (void *s,unsigned long *len)
+{
+ char tmp[MAILTMPLEN];
+ void *ret = NIL;
+ MAILSTREAM *stream = (MAILSTREAM *) s;
+ IMAPPARSEDREPLY *reply = NIL;
+ /* get tagged response or challenge */
+ while (stream && LOCAL->netstream &&
+ (reply = imap_parse_reply (stream,net_getline (LOCAL->netstream))) &&
+ !strcmp (reply->tag,"*")) imap_parse_unsolicited (stream,reply);
+ /* parse challenge if have one */
+ if (stream && LOCAL->netstream && reply && reply->tag &&
+ (*reply->tag == '+') && !reply->tag[1] && reply->text &&
+ !(ret = rfc822_base64 ((unsigned char *) reply->text,
+ strlen (reply->text),len))) {
+ sprintf (tmp,"IMAP SERVER BUG (invalid challenge): %.80s",
+ (char *) reply->text);
+ mm_log (tmp,ERROR);
+ }
+ return ret;
+}
+
+
+/* Send authenticator response in BASE64
+ * Accepts: MAIL stream
+ * string to send
+ * length of string
+ * Returns: T if successful, else NIL
+ */
+
+long imap_response (void *s,char *response,unsigned long size)
+{
+ MAILSTREAM *stream = (MAILSTREAM *) s;
+ unsigned long i,j,ret;
+ char *t,*u;
+ if (response) { /* make CRLFless BASE64 string */
+ if (size) {
+ for (t = (char *) rfc822_binary ((void *) response,size,&i),u = t,j = 0;
+ j < i; j++) if (t[j] > ' ') *u++ = t[j];
+ *u = '\0'; /* tie off string for mm_dlog() */
+ if (stream->debug) mail_dlog (t,LOCAL->sensitive);
+ /* append CRLF */
+ *u++ = '\015'; *u++ = '\012';
+ ret = net_sout (LOCAL->netstream,t,u - t);
+ fs_give ((void **) &t);
+ }
+ else ret = imap_soutr (stream,"");
+ }
+ else { /* abort requested */
+ ret = imap_soutr (stream,"*");
+ LOCAL->saslcancel = T; /* mark protocol-requested SASL cancel */
+ }
+ return ret;
+}
+
+/* IMAP close
+ * Accepts: MAIL stream
+ * option flags
+ */
+
+void imap_close (MAILSTREAM *stream,long options)
+{
+ THREADER *thr,*t;
+ IMAPPARSEDREPLY *reply;
+ if (stream && LOCAL) { /* send "LOGOUT" */
+ if (!LOCAL->byeseen) { /* don't even think of doing it if saw a BYE */
+ /* expunge silently if requested */
+ if (options & CL_EXPUNGE)
+ imap_send (stream,LEVELIMAP4 (stream) ? "CLOSE" : "EXPUNGE",NIL);
+ if (LOCAL->netstream &&
+ !imap_OK (stream,reply = imap_send (stream,"LOGOUT",NIL)))
+ mm_log (reply->text,WARN);
+ }
+ /* close NET connection if still open */
+ if (LOCAL->netstream) net_close (LOCAL->netstream);
+ LOCAL->netstream = NIL;
+ /* free up memory */
+ if (LOCAL->sortdata) fs_give ((void **) &LOCAL->sortdata);
+ if (LOCAL->namespace) {
+ mail_free_namespace (&LOCAL->namespace[0]);
+ mail_free_namespace (&LOCAL->namespace[1]);
+ mail_free_namespace (&LOCAL->namespace[2]);
+ fs_give ((void **) &LOCAL->namespace);
+ }
+ if (LOCAL->threaddata) mail_free_threadnode (&LOCAL->threaddata);
+ /* flush threaders */
+ if (thr = LOCAL->cap.threader) while (t = thr) {
+ fs_give ((void **) &t->name);
+ thr = t->next;
+ fs_give ((void **) &t);
+ }
+ if (LOCAL->referral) fs_give ((void **) &LOCAL->referral);
+ if (LOCAL->user) fs_give ((void **) &LOCAL->user);
+ if (LOCAL->reply.line) fs_give ((void **) &LOCAL->reply.line);
+ if (LOCAL->reform) fs_give ((void **) &LOCAL->reform);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ }
+}
+
+/* IMAP fetch fast information
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ *
+ * Generally, imap_structure is preferred
+ */
+
+void imap_fast (MAILSTREAM *stream,char *sequence,long flags)
+{
+ IMAPPARSEDREPLY *reply = imap_fetch (stream,sequence,flags & FT_UID);
+ if (!imap_OK (stream,reply)) mm_log (reply->text,ERROR);
+}
+
+
+/* IMAP fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ */
+
+void imap_flags (MAILSTREAM *stream,char *sequence,long flags)
+{ /* send "FETCH sequence FLAGS" */
+ char *cmd = (LEVELIMAP4 (stream) && (flags & FT_UID)) ? "UID FETCH":"FETCH";
+ IMAPPARSEDREPLY *reply;
+ IMAPARG *args[3],aseq,aatt;
+ if (LOCAL->loser) sequence = imap_reform_sequence (stream,sequence,
+ flags & FT_UID);
+ aseq.type = SEQUENCE; aseq.text = (void *) sequence;
+ aatt.type = ATOM; aatt.text = (void *) "FLAGS";
+ args[0] = &aseq; args[1] = &aatt; args[2] = NIL;
+ if (!imap_OK (stream,reply = imap_send (stream,cmd,args)))
+ mm_log (reply->text,ERROR);
+}
+
+/* IMAP fetch overview
+ * Accepts: MAIL stream, sequence bits set
+ * pointer to overview return function
+ * Returns: T if successful, NIL otherwise
+ */
+
+long imap_overview (MAILSTREAM *stream,overview_t ofn)
+{
+ MESSAGECACHE *elt;
+ ENVELOPE *env;
+ OVERVIEW ov;
+ char *s,*t;
+ unsigned long i,start,last,len,slen;
+ if (!LOCAL->netstream) return NIL;
+ /* build overview sequence */
+ for (i = 1,len = start = last = 0,s = t = NIL; i <= stream->nmsgs; ++i)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ if (!elt->private.msg.env) {
+ if (s) { /* continuing a sequence */
+ if (i == last + 1) last = i;
+ else { /* end of range */
+ if (last != start) sprintf (t,":%lu,%lu",last,i);
+ else sprintf (t,",%lu",i);
+ if ((len - (slen = (t += strlen (t)) - s)) < 20) {
+ fs_resize ((void **) &s,len += MAILTMPLEN);
+ t = s + slen; /* relocate current pointer */
+ }
+ start = last = i; /* begin a new range */
+ }
+ }
+ else { /* first time, start new buffer */
+ s = (char *) fs_get (len = MAILTMPLEN);
+ sprintf (s,"%lu",start = last = i);
+ t = s + strlen (s); /* end of buffer */
+ }
+ }
+ }
+ /* last sequence */
+ if (last != start) sprintf (t,":%lu",last);
+ if (s) { /* prefetch as needed */
+ imap_fetch (stream,s,FT_NEEDENV);
+ fs_give ((void **) &s);
+ }
+ ov.optional.lines = 0; /* now overview each message */
+ ov.optional.xref = NIL;
+ if (ofn) for (i = 1; i <= stream->nmsgs; i++)
+ if (((elt = mail_elt (stream,i))->sequence) &&
+ (env = mail_fetch_structure (stream,i,NIL,NIL)) && ofn) {
+ ov.subject = env->subject;
+ ov.from = env->from;
+ ov.date = env->date;
+ ov.message_id = env->message_id;
+ ov.references = env->references;
+ ov.optional.octets = elt->rfc822_size;
+ (*ofn) (stream,mail_uid (stream,i),&ov,i);
+ }
+ return LONGT;
+}
+
+/* IMAP fetch structure
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to return body
+ * option flags
+ * Returns: envelope of this message, body returned in body value
+ *
+ * Fetches the "fast" information as well
+ */
+
+ENVELOPE *imap_structure (MAILSTREAM *stream,unsigned long msgno,BODY **body,
+ long flags)
+{
+ unsigned long i,j,k,x;
+ char *s,seq[MAILTMPLEN],tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ ENVELOPE **env;
+ BODY **b;
+ IMAPPARSEDREPLY *reply = NIL;
+ IMAPARG *args[3],aseq,aatt;
+ SEARCHSET *set = LOCAL->lookahead;
+ LOCAL->lookahead = NIL;
+ args[0] = &aseq; args[1] = &aatt; args[2] = NIL;
+ aseq.type = SEQUENCE; aseq.text = (void *) seq;
+ aatt.type = ATOM; aatt.text = NIL;
+ if (flags & FT_UID) /* see if can find msgno from UID */
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->private.uid == msgno) {
+ msgno = i; /* found msgno, use it from now on */
+ flags &= ~FT_UID; /* no longer a UID fetch */
+ }
+ sprintf (s = seq,"%lu",msgno);/* initial sequence */
+ if (LEVELIMAP4 (stream) && (flags & FT_UID)) {
+ /* UID fetching is requested and we can't map the UID to a message sequence
+ * number. Assume that the message isn't cached at all.
+ */
+ if (!imap_OK (stream,reply = imap_fetch (stream,seq,FT_NEEDENV +
+ (body ? FT_NEEDBODY : NIL) +
+ (flags & (FT_UID + FT_NOHDRS)))))
+ mm_log (reply->text,ERROR);
+ /* now hunt for this UID */
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->private.uid == msgno) {
+ if (body) *body = elt->private.msg.body;
+ return elt->private.msg.env;
+ }
+ if (body) *body = NIL; /* can't find the UID */
+ return NIL;
+ }
+ elt = mail_elt (stream,msgno);/* get cache pointer */
+ if (stream->scache) { /* short caching? */
+ env = &stream->env; /* use temporaries on the stream */
+ b = &stream->body;
+ if (msgno != stream->msgno){/* flush old poop if a different message */
+ mail_free_envelope (env);
+ mail_free_body (b);
+ stream->msgno = msgno; /* this is now the current short cache msg */
+ }
+ }
+
+ else { /* normal cache */
+ env = &elt->private.msg.env;/* get envelope and body pointers */
+ b = &elt->private.msg.body;
+ /* prefetch if don't have envelope */
+ if (!(flags & FT_NOLOOKAHEAD) &&
+ ((!*env || (*env)->incomplete) ||
+ (body && !*b && LEVELIMAP2bis (stream)))) {
+ if (set) { /* have a lookahead list? */
+ MESSAGE *msg;
+ for (k = imap_fetchlookaheadlimit;
+ k && set && (((s += strlen (s)) - seq) < (MAXCOMMAND - 30));
+ set = set->next) {
+ i = (set->first == 0xffffffff) ? stream->nmsgs :
+ min (set->first,stream->nmsgs);
+ if (j = (set->last == 0xffffffff) ? stream->nmsgs :
+ min (set->last,stream->nmsgs)) {
+ if (i > j) { /* swap the range if backwards */
+ x = i; i = j; j = x;
+ }
+ /* find first message not msgno or in cache */
+ while (((i == msgno) ||
+ ((msg = &(mail_elt (stream,i)->private.msg))->env &&
+ (!body || msg->body))) && (i++ < j));
+ /* until range or lookahead finished */
+ while (k && (i <= j)) {
+ /* find first cached message in range */
+ for (x = i + 1; (x <= j) &&
+ !((msg = &(mail_elt (stream,x)->private.msg))->env &&
+ (!body || msg->body)); x++);
+ if (i == --x) { /* only one message? */
+ sprintf (s += strlen (s),",%lu",i++);
+ k--; /* prefetching one message */
+ }
+ else { /* a range to prefetch */
+ sprintf (s += strlen (s),",%lu:%lu",i,x);
+ i = 1 + x - i; /* number of messages in this range */
+ /* still can look ahead some more? */
+ if (k = (k > i) ? k - i : 0)
+ /* yes, scan further in this range */
+ for (i = x + 2; (i <= j) &&
+ ((i == msgno) ||
+ ((msg = &(mail_elt (stream,i)->private.msg))->env &&
+ (!body || msg->body)));
+ i++);
+ }
+ }
+ }
+ else if ((i != msgno) && !mail_elt (stream,i)->private.msg.env) {
+ sprintf (s += strlen (s),",%lu",i);
+ k--; /* prefetching one message */
+ }
+ }
+ }
+ /* build message number list */
+ else for (i = msgno+1,k = imap_lookahead; k && (i <= stream->nmsgs); i++)
+ if (!mail_elt (stream,i)->private.msg.env) {
+ s += strlen (s); /* find string end, see if nearing end */
+ if ((s - seq) > (MAILTMPLEN - 20)) break;
+ sprintf (s,",%lu",i); /* append message */
+ for (j = i + 1, k--; /* hunt for last message without an envelope */
+ k && (j <= stream->nmsgs) &&
+ !mail_elt (stream,j)->private.msg.env; j++, k--);
+ /* if different, make a range */
+ if (i != --j) sprintf (s + strlen (s),":%lu",i = j);
+ }
+ }
+ }
+
+ if (!stream->lock) { /* no-op if stream locked */
+ /* Build the fetch attributes. Unlike imap_fetch(), this tries not to
+ * fetch data that is already cached. However, since it is based on the
+ * message requested and not on any of the prefetched messages, it can
+ * goof, either by fetching data already cached or not prefetching data
+ * that isn't cached (but was cached in the message requested).
+ * Fortunately, no great harm is done. If it doesn't prefetch the data,
+ * it will get it when the affected message(s) are requested.
+ */
+ if (!elt->private.uid && LEVELIMAP4 (stream)) strcpy (tmp," UID");
+ else tmp[0] = '\0'; /* initialize command */
+ /* need envelope? */
+ if (!*env || (*env)->incomplete) {
+ strcat (tmp," ENVELOPE"); /* yes, get it and possible extra poop */
+ if (!(flags & FT_NOHDRS) && LEVELIMAP4rev1 (stream)) {
+ if (imap_extrahdrs) sprintf (tmp + strlen (tmp)," %s %s %s",
+ hdrheader[LOCAL->cap.extlevel],
+ imap_extrahdrs,hdrtrailer);
+ else sprintf (tmp + strlen (tmp)," %s %s",
+ hdrheader[LOCAL->cap.extlevel],hdrtrailer);
+ }
+ }
+ /* need body? */
+ if (body && !*b && LEVELIMAP2bis (stream))
+ strcat (tmp,LEVELIMAP4 (stream) ? " BODYSTRUCTURE" : " BODY");
+ if (!elt->day) strcat (tmp," INTERNALDATE");
+ if (!elt->rfc822_size) strcat (tmp," RFC822.SIZE");
+ if (tmp[0]) { /* anything to do? */
+ tmp[0] = '('; /* make into a list */
+ strcat (tmp," FLAGS)"); /* always get current flags */
+ aatt.text = (void *) tmp; /* do the built command */
+ if (!imap_OK (stream,reply = imap_send (stream,"FETCH",args))) {
+ /* failed, probably RFC-1176 server */
+ if (!LEVELIMAP4 (stream) && LEVELIMAP2bis (stream) && body && !*b){
+ aatt.text = (void *) "ALL";
+ if (imap_OK (stream,reply = imap_send (stream,"FETCH",args)))
+ /* doesn't have body capabilities */
+ LOCAL->cap.imap2bis = NIL;
+ else mm_log (reply->text,ERROR);
+ }
+ else mm_log (reply->text,ERROR);
+ }
+ }
+ }
+ if (body) { /* wants to return body */
+ if (!*b && !LEVELIMAP2bis (stream)) {
+ /* simulate body structure fetch for IMAP2 */
+ *b = mail_initbody (mail_newbody ());
+ (*b)->subtype = cpystr (rfc822_default_subtype ((*b)->type));
+ ((*b)->parameter = mail_newbody_parameter ())->attribute =
+ cpystr ("CHARSET");
+ (*b)->parameter->value = cpystr ("US-ASCII");
+ s = mail_fetch_text (stream,msgno,NIL,&i,flags);
+ (*b)->size.bytes = i;
+ while (i--) if (*s++ == '\n') (*b)->size.lines++;
+ }
+ *body = *b; /* return the body */
+ }
+ return *env; /* return the envelope */
+}
+
+/* IMAP fetch message data
+ * Accepts: MAIL stream
+ * message number
+ * section specifier
+ * offset of first designated byte or 0 to start at beginning
+ * maximum number of bytes or 0 for all bytes
+ * lines to fetch if header
+ * flags
+ * Returns: T on success, NIL on failure
+ */
+
+long imap_msgdata (MAILSTREAM *stream,unsigned long msgno,char *section,
+ unsigned long first,unsigned long last,STRINGLIST *lines,
+ long flags)
+{
+ int i;
+ char *t,tmp[MAILTMPLEN],partial[40],seq[40];
+ char *noextend,*nopartial,*nolines,*nopeek,*nononpeek;
+ char *cmd = (LEVELIMAP4 (stream) && (flags & FT_UID)) ? "UID FETCH":"FETCH";
+ IMAPPARSEDREPLY *reply;
+ IMAPARG *args[5],*auxargs[3],aseq,aatt,alns,acls,aflg;
+ noextend = nopartial = nolines = nopeek = nononpeek = NIL;
+ /* does searching desire a lookahead? */
+ if ((flags & FT_SEARCHLOOKAHEAD) && (msgno < stream->nmsgs) &&
+ !stream->scache) {
+ sprintf (seq,"%lu:%lu",msgno,
+ (unsigned long) min (msgno + IMAPLOOKAHEAD,stream->nmsgs));
+ aseq.type = SEQUENCE;
+ aseq.text = (void *) seq;
+ }
+ else { /* no, do it the easy way */
+ aseq.type = NUMBER;
+ aseq.text = (void *) msgno;
+ }
+ aatt.type = ATOM; /* assume atomic attribute */
+ alns.type = LIST; alns.text = (void *) lines;
+ acls.type = BODYCLOSE; acls.text = (void *) partial;
+ aflg.type = ATOM; aflg.text = (void *) "FLAGS";
+ args[0] = &aseq; args[1] = &aatt; args[2] = args[3] = args[4] = NIL;
+ auxargs[0] = &aseq; auxargs[1] = &aflg; auxargs[2] = NIL;
+ partial[0] = '\0'; /* initially no partial specifier */
+ if (LEVELIMAP4rev1 (stream)) {/* easy if IMAP4rev1 server */
+ /* HEADER fetching with special handling? */
+ if (!strcmp (section,"HEADER") && (lines || (flags & FT_PREFETCHTEXT))) {
+ if (lines) { /* want specific header lines? */
+ aatt.type = (flags & FT_PEEK) ? BODYPEEK : BODYTEXT;
+ aatt.text = (void *) ((flags & FT_NOT) ?
+ "HEADER.FIELDS.NOT" : "HEADER.FIELDS");
+ args[2] = &alns; args[3] = &acls;
+ }
+ /* must be prefetching */
+ else aatt.text = (void *) ((flags & FT_PEEK) ?
+ "(BODY.PEEK[HEADER] BODY.PEEK[TEXT])" :
+ "(BODY[HEADER] BODY[TEXT])");
+ }
+ else { /* simple case */
+ aatt.type = (flags & FT_PEEK) ? BODYPEEK : BODYTEXT;
+ aatt.text = (void *) section;
+ args[2] = &acls;
+ }
+ if (first || last) sprintf (partial,"<%lu.%lu>",first,last ? last:-1);
+ }
+
+ /* IMAP4 did not have:
+ * . HEADER body part (can simulate with BODY[0] or BODY.PEEK[0])
+ * . TEXT body part (can simulate top-level with RFC822.TEXT or
+ * RFC822.TEXT.PEEK)
+ * . MIME body part
+ * . (usable) partial fetching
+ * . (usable) selective header line fetching
+ */
+ else if (LEVEL1730 (stream)) {/* IMAP4 (RFC 1730) compatibility */
+ /* BODY[HEADER] becomes BODY.PEEK[0] */
+ if (!strcmp (section,"HEADER"))
+ aatt.text = (void *)
+ ((flags & FT_PREFETCHTEXT) ?
+ ((flags & FT_PEEK) ? "(BODY.PEEK[0] RFC822.TEXT.PEEK)" :
+ "(BODY[0] RFC822.TEXT)") :
+ ((flags & FT_PEEK) ? "BODY.PEEK[0]" : "BODY[0]"));
+ /* BODY[TEXT] becomes RFC822.TEXT */
+ else if (!strcmp (section,"TEXT"))
+ aatt.text = (void *) ((flags & FT_PEEK) ? "RFC822.TEXT.PEEK" :
+ "RFC822.TEXT");
+ else if (!section[0]) /* BODY[] becomes RFC822 */
+ aatt.text = (void *) ((flags & FT_PEEK) ? "RFC822.PEEK" : "RFC822");
+ /* nested header */
+ else if (t = strstr (section,".HEADER")) {
+ aatt.type = (flags & FT_PEEK) ? BODYPEEK : BODYTEXT;
+ args[2] = &acls; /* will need to close section */
+ aatt.text = (void *) tmp; /* convert .HEADER to .0 */
+ strncpy (tmp,section,t-section);
+ strcpy (tmp+(t-section),".0");
+ }
+ else { /* IMAP4 body part */
+ aatt.type = (flags & FT_PEEK) ? BODYPEEK : BODYTEXT;
+ args[2] = &acls; /* will need to close section */
+ aatt.text = (void *) section;
+ }
+ if (strstr (section,".MIME") || strstr (section,".TEXT")) noextend = "4";
+ if (first || last) nopartial = "4";
+ if (lines) nolines = "4";
+ }
+
+ /* IMAP2bis did not have:
+ * . HEADER body part (can simulate peeking top-level with RFC822.HEADER)
+ * . TEXT body part (can simulate non-peeking top-level with RFC822.TEXT)
+ * . MIME body part
+ * . partial fetching
+ * . selective header line fetching
+ * . non-peeking header fetching
+ * . peeking body fetching
+ */
+ /* IMAP2bis compatibility */
+ else if (LEVELIMAP2bis (stream)) {
+ /* BODY[HEADER] becomes RFC822.HEADER */
+ if (!strcmp (section,"HEADER")) {
+ aatt.text = (void *)
+ ((flags & FT_PREFETCHTEXT) ?
+ "(RFC822.HEADER RFC822.TEXT)" : "RFC822.HEADER");
+ if (flags & FT_PEEK) flags &= ~FT_PEEK;
+ else nononpeek = "2bis";
+ }
+ /* BODY[TEXT] becomes RFC822.TEXT */
+ else if (!strcmp (section,"TEXT")) aatt.text = (void *) "RFC822.TEXT";
+ /* BODY[] becomes RFC822 */
+ else if (!section[0]) aatt.text = (void *) "RFC822";
+ else { /* IMAP2bis body part */
+ aatt.type = BODYTEXT;
+ args[2] = &acls; /* will need to close section */
+ aatt.text = (void *) section;
+ }
+ if (strstr (section,".HEADER") || strstr (section,".MIME") ||
+ strstr (section,".TEXT")) noextend = "2bis";
+ if (first || last) nopartial = "2bis";
+ if (lines) nolines = "2bis";
+ if (flags & FT_PEEK) nopeek = "2bis";
+ }
+
+ /* IMAP2 did not have:
+ * . HEADER body part (can simulate peeking top-level with RFC822.HEADER)
+ * . TEXT body part (can simulate non-peeking top-level with RFC822.TEXT)
+ * . MIME body part
+ * . multiple body parts (can simulate BODY[1] with RFC822.TEXT)
+ * . partial fetching
+ * . selective header line fetching
+ * . non-peeking header fetching
+ * . peeking body fetching
+ */
+ else { /* IMAP2 (RFC 1176/1064) compatibility */
+ /* BODY[HEADER] */
+ if (!strcmp (section,"HEADER")) {
+ aatt.text = (void *) ((flags & FT_PREFETCHTEXT) ?
+ "(RFC822.HEADER RFC822.TEXT)" : "RFC822.HEADER");
+ if (flags & FT_PEEK) flags &= ~FT_PEEK;
+ nononpeek = "2";
+ }
+ /* BODY[TEXT] becomes RFC822.TEXT */
+ else if (!strcmp (section,"TEXT")) aatt.text = (void *) "RFC822.TEXT";
+ /* BODY[1] treated like RFC822.TEXT */
+ else if (!strcmp (section,"1")) {
+ SIZEDTEXT text;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ /* have a cached RFC822.TEXT? */
+ if (elt->private.msg.text.text.data) {
+ text.size = elt->private.msg.text.text.size;
+ /* should move instead of copy */
+ text.data = memcpy (fs_get (text.size+1),
+ elt->private.msg.text.text.data,text.size);
+ (t = (char *) text.data)[text.size] = '\0';
+ imap_cache (stream,msgno,"1",NIL,&text);
+ return LONGT; /* don't have to do any fetches */
+ }
+ /* otherwise do RFC822.TEXT */
+ aatt.text = (void *) "RFC822.TEXT";
+ }
+ /* BODY[] becomes RFC822 */
+ else if (!section[0]) aatt.text = (void *) "RFC822";
+ else noextend = "2"; /* how did we get here? */
+ if (flags & FT_PEEK) nopeek = "2";
+ if (first || last) nopartial = "2";
+ if (lines) nolines = "2";
+ }
+
+ /* Report unavailable functionalities. The application can use the helpful
+ * LEVELIMAPREV1, LEVELIMAP4, and LEVELIMAP2bis operations provided in
+ * imap4r1.h to avoid triggering these errors. There aren't any workarounds
+ * for these restrictions.
+ */
+ if (noextend) {
+ sprintf (tmp,"[NOTIMAP4REV1] IMAP%s server can't do extended body fetch",
+ noextend);
+ mm_log (tmp,ERROR);
+ return NIL; /* can't do anything close either */
+ }
+ if (nopartial) {
+ sprintf (tmp,"[NOTIMAP4REV1] IMAP%s server can't do partial fetch",
+ nopartial);
+ mm_notify (stream,tmp,WARN);
+ }
+ if (nolines) {
+ sprintf(tmp,"[NOTIMAP4REV1] IMAP%s server can't do selective header fetch",
+ nolines);
+ mm_notify (stream,tmp,WARN);
+ }
+
+ /* trying to do unsupported peek behavior? */
+ if ((t = nopeek) || (t = nononpeek)) {
+ /* get most recent \Seen setting */
+ if (!imap_OK (stream,reply = imap_send (stream,cmd,auxargs)))
+ mm_log (reply->text,WARN);
+ /* note current setting of \Seen flag */
+ if (!(i = mail_elt (stream,msgno)->seen)) {
+ sprintf (tmp,nopeek ? /* only babble if \Seen not set */
+ "[NOTIMAP4] Simulating peeking fetch in IMAP%s" :
+ "[NOTIMAP4] Simulating non-peeking header fetch in IMAP%s",t);
+ mm_notify (stream,tmp,NIL);
+ }
+ /* send the fetch command */
+ if (!imap_OK (stream,reply = imap_send (stream,cmd,args))) {
+ mm_log (reply->text,ERROR);
+ return NIL; /* failure */
+ }
+ /* send command if need to reset \Seen */
+ if (((nopeek && !i && mail_elt (stream,msgno)->seen &&
+ (aflg.text = "-FLAGS \\Seen")) ||
+ ((nononpeek && !mail_elt (stream,msgno)->seen) &&
+ (aflg.text = "+FLAGS \\Seen"))) &&
+ !imap_OK (stream,reply = imap_send (stream,"STORE",auxargs)))
+ mm_log (reply->text,WARN);
+ }
+ /* simple case if traditional behavior */
+ else if (!imap_OK (stream,reply = imap_send (stream,cmd,args))) {
+ mm_log (reply->text,ERROR);
+ return NIL; /* failure */
+ }
+ /* simulate BODY[1] return for RFC 1064/1176 */
+ if (!LEVELIMAP2bis (stream) && !strcmp (section,"1")) {
+ SIZEDTEXT text;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ text.size = elt->private.msg.text.text.size;
+ /* should move instead of copy */
+ text.data = memcpy (fs_get (text.size+1),elt->private.msg.text.text.data,
+ text.size);
+ (t = (char *) text.data)[text.size] = '\0';
+ imap_cache (stream,msgno,"1",NIL,&text);
+ }
+ return LONGT;
+}
+
+/* IMAP fetch UID
+ * Accepts: MAIL stream
+ * message number
+ * Returns: UID
+ */
+
+unsigned long imap_uid (MAILSTREAM *stream,unsigned long msgno)
+{
+ MESSAGECACHE *elt;
+ IMAPPARSEDREPLY *reply;
+ IMAPARG *args[3],aseq,aatt;
+ char *s,seq[MAILTMPLEN];
+ unsigned long i,j,k;
+ /* IMAP2 didn't have UIDs */
+ if (!LEVELIMAP4 (stream)) return msgno;
+ /* do we know its UID yet? */
+ if (!(elt = mail_elt (stream,msgno))->private.uid) {
+ aseq.type = SEQUENCE; aseq.text = (void *) seq;
+ aatt.type = ATOM; aatt.text = (void *) "UID";
+ args[0] = &aseq; args[1] = &aatt; args[2] = NIL;
+ sprintf (seq,"%lu",msgno);
+ if (k = imap_uidlookahead) {/* build UID list */
+ for (i = msgno + 1, s = seq; k && (i <= stream->nmsgs); i++)
+ if (!mail_elt (stream,i)->private.uid) {
+ s += strlen (s); /* find string end, see if nearing end */
+ if ((s - seq) > (MAILTMPLEN - 20)) break;
+ sprintf (s,",%lu",i); /* append message */
+ for (j = i + 1, k--; /* hunt for last message without a UID */
+ k && (j <= stream->nmsgs) && !mail_elt (stream,j)->private.uid;
+ j++, k--);
+ /* if different, make a range */
+ if (i != --j) sprintf (s + strlen (s),":%lu",i = j);
+ }
+ }
+ /* send "FETCH msgno UID" */
+ if (!imap_OK (stream,reply = imap_send (stream,"FETCH",args)))
+ mm_log (reply->text,ERROR);
+ }
+ return elt->private.uid; /* return our UID now */
+}
+
+/* IMAP fetch message number from UID
+ * Accepts: MAIL stream
+ * UID
+ * Returns: message number
+ */
+
+unsigned long imap_msgno (MAILSTREAM *stream,unsigned long uid)
+{
+ IMAPPARSEDREPLY *reply;
+ IMAPARG *args[3],aseq,aatt;
+ char seq[MAILTMPLEN];
+ int holes = 0;
+ unsigned long i,msgno;
+ /* IMAP2 didn't have UIDs */
+ if (!LEVELIMAP4 (stream)) return uid;
+ /* This really should be a binary search, but since there are likely to be
+ * holes in the msgno->UID map it's hard to do.
+ */
+ for (msgno = 1; msgno <= stream->nmsgs; msgno++) {
+ if (!(i = mail_elt (stream,msgno)->private.uid)) holes = T;
+ else if (i == uid) return msgno;
+ }
+ if (holes) { /* have holes in cache? */
+ /* yes, have server hunt for UID */
+ LOCAL->lastuid.uid = LOCAL->lastuid.msgno = 0;
+ aseq.type = SEQUENCE; aseq.text = (void *) seq;
+ aatt.type = ATOM; aatt.text = (void *) "UID";
+ args[0] = &aseq; args[1] = &aatt; args[2] = NIL;
+ sprintf (seq,"%lu",uid);
+ /* send "UID FETCH uid UID" */
+ if (!imap_OK (stream,reply = imap_send (stream,"UID FETCH",args)))
+ mm_log (reply->text,ERROR);
+ if (LOCAL->lastuid.uid) { /* got any results from FETCH? */
+ if ((LOCAL->lastuid.uid == uid) &&
+ /* what, me paranoid? */
+ (LOCAL->lastuid.msgno <= stream->nmsgs) &&
+ (mail_elt (stream,LOCAL->lastuid.msgno)->private.uid == uid))
+ /* got it the easy way */
+ return LOCAL->lastuid.msgno;
+ /* sigh, do another linear search... */
+ for (msgno = 1; msgno <= stream->nmsgs; msgno++)
+ if (mail_elt (stream,msgno)->private.uid == uid) return msgno;
+ }
+ }
+ return 0; /* didn't find the UID anywhere */
+}
+
+/* IMAP modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ */
+
+void imap_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ char *cmd = (LEVELIMAP4 (stream) && (flags & ST_UID)) ? "UID STORE":"STORE";
+ IMAPPARSEDREPLY *reply;
+ IMAPARG *args[4],aseq,ascm,aflg;
+ if (LOCAL->loser) sequence = imap_reform_sequence (stream,sequence,
+ flags & ST_UID);
+ aseq.type = SEQUENCE; aseq.text = (void *) sequence;
+ ascm.type = ATOM; ascm.text = (void *)
+ ((flags & ST_SET) ?
+ ((LEVELIMAP4 (stream) && (flags & ST_SILENT)) ?
+ "+Flags.silent" : "+Flags") :
+ ((LEVELIMAP4 (stream) && (flags & ST_SILENT)) ?
+ "-Flags.silent" : "-Flags"));
+ aflg.type = FLAGS; aflg.text = (void *) flag;
+ args[0] = &aseq; args[1] = &ascm; args[2] = &aflg; args[3] = NIL;
+ /* send "STORE sequence +Flags flag" */
+ if (!imap_OK (stream,reply = imap_send (stream,cmd,args)))
+ mm_log (reply->text,ERROR);
+}
+
+/* IMAP search for messages
+ * Accepts: MAIL stream
+ * character set
+ * search program
+ * option flags
+ * Returns: T on success, NIL on failure
+ */
+
+long imap_search (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,long flags)
+{
+ unsigned long i,j,k;
+ char *s;
+ IMAPPARSEDREPLY *reply;
+ MESSAGECACHE *elt;
+ if ((flags & SE_NOSERVER) || /* if want to do local search */
+ LOCAL->loser || /* or loser */
+ (!LEVELIMAP4 (stream) && /* or old server but new functions... */
+ (charset || (flags & SE_UID) || pgm->msgno || pgm->uid || pgm->or ||
+ pgm->not || pgm->header || pgm->larger || pgm->smaller ||
+ pgm->sentbefore || pgm->senton || pgm->sentsince || pgm->draft ||
+ pgm->undraft || pgm->return_path || pgm->sender || pgm->reply_to ||
+ pgm->message_id || pgm->in_reply_to || pgm->newsgroups ||
+ pgm->followup_to || pgm->references)) ||
+ (!LEVELWITHIN (stream) && (pgm->older || pgm->younger))) {
+ if ((flags & SE_NOLOCAL) ||
+ !mail_search_default (stream,charset,pgm,flags | SE_NOSERVER))
+ return NIL;
+ }
+ /* do silly ALL or seq-only search locally */
+ else if (!(flags & (SE_NOLOCAL|SE_SILLYOK)) &&
+ !(pgm->uid || pgm->or || pgm->not ||
+ pgm->header || pgm->from || pgm->to || pgm->cc || pgm->bcc ||
+ pgm->subject || pgm->body || pgm->text ||
+ pgm->larger || pgm->smaller ||
+ pgm->sentbefore || pgm->senton || pgm->sentsince ||
+ pgm->before || pgm->on || pgm->since ||
+ pgm->answered || pgm->unanswered ||
+ pgm->deleted || pgm->undeleted || pgm->draft || pgm->undraft ||
+ pgm->flagged || pgm->unflagged || pgm->recent || pgm->old ||
+ pgm->seen || pgm->unseen ||
+ pgm->keyword || pgm->unkeyword ||
+ pgm->return_path || pgm->sender ||
+ pgm->reply_to || pgm->in_reply_to || pgm->message_id ||
+ pgm->newsgroups || pgm->followup_to || pgm->references)) {
+ if (!mail_search_default (stream,NIL,pgm,flags | SE_NOSERVER))
+ fatal ("impossible mail_search_default() failure");
+ }
+
+ else { /* do server-based SEARCH */
+ char *cmd = (flags & SE_UID) ? "UID SEARCH" : "SEARCH";
+ IMAPARG *args[4],apgm,aatt,achs;
+ SEARCHSET *ss,*set;
+ args[1] = args[2] = args[3] = NIL;
+ apgm.type = SEARCHPROGRAM; apgm.text = (void *) pgm;
+ if (charset) { /* optional charset argument requested */
+ args[0] = &aatt; args[1] = &achs; args[2] = &apgm;
+ aatt.type = ATOM; aatt.text = (void *) "CHARSET";
+ achs.type = ASTRING; achs.text = (void *) charset;
+ }
+ else args[0] = &apgm; /* no charset argument */
+ /* tell receiver that these will be UIDs */
+ LOCAL->uidsearch = (flags & SE_UID) ? T : NIL;
+ reply = imap_send (stream,cmd,args);
+ /* did server barf with that searchpgm? */
+ if (!(flags & SE_UID) && pgm && (ss = pgm->msgno) &&
+ !strcmp (reply->key,"BAD")) {
+ LOCAL->filter = T; /* retry, filtering SEARCH results */
+ for (i = 1; i <= stream->nmsgs; i++)
+ mail_elt (stream,i)->private.filter = NIL;
+ for (set = ss; set; set = set->next) if (i = set->first) {
+ /* single message becomes one-message range */
+ if (!(j = set->last)) j = i;
+ else if (j < i) { /* swap reversed range */
+ i = set->last; j = set->first;
+ }
+ while (i <= j) mail_elt (stream,i++)->private.filter = T;
+ }
+ pgm->msgno = NIL; /* and without the searchset */
+ reply = imap_send (stream,cmd,args);
+ pgm->msgno = ss; /* restore searchset */
+ LOCAL->filter = NIL; /* turn off filtering */
+ }
+ LOCAL->uidsearch = NIL;
+ /* do locally if server won't grok */
+ if (!strcmp (reply->key,"BAD")) {
+ if ((flags & SE_NOLOCAL) ||
+ !mail_search_default (stream,charset,pgm,flags | SE_NOSERVER))
+ return NIL;
+ }
+ else if (!imap_OK (stream,reply)) {
+ mm_log (reply->text,ERROR);
+ return NIL;
+ }
+ }
+
+ /* can never pre-fetch with a short cache */
+ if ((k = imap_prefetch) && !(flags & (SE_NOPREFETCH | SE_UID)) &&
+ !stream->scache) { /* only if prefetching permitted */
+ s = LOCAL->tmp; /* build sequence in temporary buffer */
+ *s = '\0'; /* initially nothing */
+ /* search through mailbox */
+ for (i = 1; k && (i <= stream->nmsgs); ++i)
+ /* for searched messages with no envelope */
+ if ((elt = mail_elt (stream,i)) && elt->searched &&
+ !mail_elt (stream,i)->private.msg.env) {
+ /* prepend with comma if not first time */
+ if (LOCAL->tmp[0]) *s++ = ',';
+ sprintf (s,"%lu",j = i);/* output message number */
+ s += strlen (s); /* point at end of string */
+ k--; /* count one up */
+ /* search for possible end of range */
+ while (k && (i < stream->nmsgs) &&
+ (elt = mail_elt (stream,i+1))->searched &&
+ !elt->private.msg.env) i++,k--;
+ if (i != j) { /* if a range */
+ sprintf (s,":%lu",i); /* output delimiter and end of range */
+ s += strlen (s); /* point at end of string */
+ }
+ if ((s - LOCAL->tmp) > (IMAPTMPLEN - 50)) break;
+ }
+ if (LOCAL->tmp[0]) { /* anything to pre-fetch? */
+ /* pre-fetch envelopes for the first imap_prefetch number of messages */
+ if (!imap_OK (stream,reply =
+ imap_fetch (stream,s = cpystr (LOCAL->tmp),FT_NEEDENV +
+ ((flags & SE_NOHDRS) ? FT_NOHDRS : NIL) +
+ ((flags & SE_NEEDBODY) ? FT_NEEDBODY : NIL))))
+ mm_log (reply->text,ERROR);
+ fs_give ((void **) &s); /* flush copy of sequence */
+ }
+ }
+ return LONGT;
+}
+
+/* IMAP sort messages
+ * Accepts: mail stream
+ * character set
+ * search program
+ * sort program
+ * option flags
+ * Returns: vector of sorted message sequences or NIL if error
+ */
+
+unsigned long *imap_sort (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
+ SORTPGM *pgm,long flags)
+{
+ unsigned long i,j,start,last;
+ unsigned long *ret = NIL;
+ pgm->nmsgs = 0; /* start off with no messages */
+ /* can use server-based sort? */
+ if (LEVELSORT (stream) && !(flags & SE_NOSERVER) &&
+ (!spg || (LEVELWITHIN (stream) || !(spg->older || spg->younger)))) {
+ char *cmd = (flags & SE_UID) ? "UID SORT" : "SORT";
+ IMAPARG *args[4],apgm,achs,aspg;
+ IMAPPARSEDREPLY *reply;
+ SEARCHSET *ss = NIL;
+ SEARCHPGM *tsp = NIL;
+ apgm.type = SORTPROGRAM; apgm.text = (void *) pgm;
+ achs.type = ASTRING; achs.text = (void *) (charset ? charset : "US-ASCII");
+ aspg.type = SEARCHPROGRAM;
+ /* did he provide a searchpgm? */
+ if (!(aspg.text = (void *) spg)) {
+ for (i = 1,start = last = 0; i <= stream->nmsgs; ++i)
+ if (mail_elt (stream,i)->searched) {
+ if (ss) { /* continuing a sequence */
+ if (i == last + 1) last = i;
+ else { /* end of range */
+ if (last != start) ss->last = last;
+ (ss = ss->next = mail_newsearchset ())->first = i;
+ start = last = i; /* begin a new range */
+ }
+ }
+ else { /* first time, start new searchpgm */
+ (tsp = mail_newsearchpgm ())->msgno = ss = mail_newsearchset ();
+ ss->first = start = last = i;
+ }
+ }
+ /* nothing to sort if no messages */
+ if (!(aspg.text = (void *) tsp)) return NIL;
+ /* else install last sequence */
+ if (last != start) ss->last = last;
+ }
+
+ args[0] = &apgm; args[1] = &achs; args[2] = &aspg; args[3] = NIL;
+ /* ask server to do it */
+ reply = imap_send (stream,cmd,args);
+ if (tsp) { /* was there a temporary searchpgm? */
+ aspg.text = NIL; /* yes, flush it */
+ mail_free_searchpgm (&tsp);
+ /* did server barf with that searchpgm? */
+ if (!(flags & SE_UID) && !strcmp (reply->key,"BAD")) {
+ LOCAL->filter = T; /* retry, filtering SORT/THREAD results */
+ reply = imap_send (stream,cmd,args);
+ LOCAL->filter = NIL; /* turn off filtering */
+ }
+ }
+ /* do locally if server barfs */
+ if (!strcmp (reply->key,"BAD"))
+ return (flags & SE_NOLOCAL) ? NIL :
+ imap_sort (stream,charset,spg,pgm,flags | SE_NOSERVER);
+ /* server sorted OK? */
+ else if (imap_OK (stream,reply)) {
+ pgm->nmsgs = LOCAL->sortsize;
+ ret = LOCAL->sortdata;
+ LOCAL->sortdata = NIL; /* mail program is responsible for flushing */
+ }
+ else mm_log (reply->text,ERROR);
+ }
+
+ /* not much can do if short caching */
+ else if (stream->scache) ret = mail_sort_msgs (stream,charset,spg,pgm,flags);
+ else { /* try to be a bit more clever */
+ char *s,*t;
+ unsigned long len;
+ MESSAGECACHE *elt;
+ SORTCACHE **sc;
+ SORTPGM *sp;
+ long ftflags = 0;
+ /* see if need envelopes */
+ for (sp = pgm; sp && !ftflags; sp = sp->next) switch (sp->function) {
+ case SORTDATE: case SORTFROM: case SORTSUBJECT: case SORTTO: case SORTCC:
+ ftflags = FT_NEEDENV + ((flags & SE_NOHDRS) ? FT_NOHDRS : NIL);
+ }
+ if (spg) { /* only if a search needs to be done */
+ int silent = stream->silent;
+ stream->silent = T; /* don't pass up mm_searched() events */
+ /* search for messages */
+ mail_search_full (stream,charset,spg,flags & SE_NOSERVER);
+ stream->silent = silent; /* restore silence state */
+ }
+ /* initialize progress counters */
+ pgm->nmsgs = pgm->progress.cached = 0;
+ /* pass 1: count messages to sort */
+ for (i = 1,len = start = last = 0,s = t = NIL; i <= stream->nmsgs; ++i)
+ if ((elt = mail_elt (stream,i))->searched) {
+ pgm->nmsgs++;
+ if (ftflags ? !elt->private.msg.env : !elt->day) {
+ if (s) { /* continuing a sequence */
+ if (i == last + 1) last = i;
+ else { /* end of range */
+ if (last != start) sprintf (t,":%lu,%lu",last,i);
+ else sprintf (t,",%lu",i);
+ start = last = i; /* begin a new range */
+ if ((len - (j = ((t += strlen (t)) - s)) < 20)) {
+ fs_resize ((void **) &s,len += MAILTMPLEN);
+ t = s + j; /* relocate current pointer */
+ }
+ }
+ }
+ else { /* first time, start new buffer */
+ s = (char *) fs_get (len = MAILTMPLEN);
+ sprintf (s,"%lu",start = last = i);
+ t = s + strlen (s); /* end of buffer */
+ }
+ }
+ }
+ /* last sequence */
+ if (last != start) sprintf (t,":%lu",last);
+ if (s) { /* load cache for all messages being sorted */
+ imap_fetch (stream,s,ftflags);
+ fs_give ((void **) &s);
+ }
+ if (pgm->nmsgs) { /* pass 2: sort cache */
+ sortresults_t sr = (sortresults_t)
+ mail_parameters (NIL,GET_SORTRESULTS,NIL);
+ sc = mail_sort_loadcache (stream,pgm);
+ /* pass 3: sort messages */
+ if (!pgm->abort) ret = mail_sort_cache (stream,pgm,sc,flags);
+ fs_give ((void **) &sc); /* don't need sort vector any more */
+ /* also return via callback if requested */
+ if (sr) (*sr) (stream,ret,pgm->nmsgs);
+ }
+ }
+ return ret;
+}
+
+/* IMAP thread messages
+ * Accepts: mail stream
+ * thread type
+ * character set
+ * search program
+ * option flags
+ * Returns: thread node tree or NIL if error
+ */
+
+THREADNODE *imap_thread (MAILSTREAM *stream,char *type,char *charset,
+ SEARCHPGM *spg,long flags)
+{
+ THREADER *thr;
+ if (!(flags & SE_NOSERVER) &&
+ (!spg || (LEVELWITHIN (stream) || !(spg->older || spg->younger))))
+ /* does server have this threader type? */
+ for (thr = LOCAL->cap.threader; thr; thr = thr->next)
+ if (!compare_cstring (thr->name,type))
+ return imap_thread_work (stream,type,charset,spg,flags);
+ /* server doesn't support it, do locally */
+ return (flags & SE_NOLOCAL) ? NIL:
+ mail_thread_msgs (stream,type,charset,spg,flags | SE_NOSERVER,imap_sort);
+}
+
+/* IMAP thread messages worker routine
+ * Accepts: mail stream
+ * thread type
+ * character set
+ * search program
+ * option flags
+ * Returns: thread node tree
+ */
+
+THREADNODE *imap_thread_work (MAILSTREAM *stream,char *type,char *charset,
+ SEARCHPGM *spg,long flags)
+{
+ unsigned long i,start,last;
+ char *cmd = (flags & SE_UID) ? "UID THREAD" : "THREAD";
+ IMAPARG *args[4],apgm,achs,aspg;
+ IMAPPARSEDREPLY *reply;
+ THREADNODE *ret = NIL;
+ SEARCHSET *ss = NIL;
+ SEARCHPGM *tsp = NIL;
+ apgm.type = ATOM; apgm.text = (void *) type;
+ achs.type = ASTRING;
+ achs.text = (void *) (charset ? charset : "US-ASCII");
+ aspg.type = SEARCHPROGRAM;
+ /* did he provide a searchpgm? */
+ if (!(aspg.text = (void *) spg)) {
+ for (i = 1,start = last = 0; i <= stream->nmsgs; ++i)
+ if (mail_elt (stream,i)->searched) {
+ if (ss) { /* continuing a sequence */
+ if (i == last + 1) last = i;
+ else { /* end of range */
+ if (last != start) ss->last = last;
+ (ss = ss->next = mail_newsearchset ())->first = i;
+ start = last =i; /* begin a new range */
+ }
+ }
+ else { /* first time, start new searchpgm */
+ (tsp = mail_newsearchpgm ())->msgno = ss = mail_newsearchset ();
+ ss->first = start = last = i;
+ }
+ }
+ /* nothing to sort if no messages */
+ if (!(aspg.text = (void *) tsp)) return NIL;
+ /* else install last sequence */
+ if (last != start) ss->last = last;
+ }
+
+ args[0] = &apgm; args[1] = &achs; args[2] = &aspg; args[3] = NIL;
+ /* ask server to do it */
+ reply = imap_send (stream,cmd,args);
+ if (tsp) { /* was there a temporary searchpgm? */
+ aspg.text = NIL; /* yes, flush it */
+ mail_free_searchpgm (&tsp);
+ /* did server barf with that searchpgm? */
+ if (!(flags & SE_UID) && !strcmp (reply->key,"BAD")) {
+ LOCAL->filter = T; /* retry, filtering SORT/THREAD results */
+ reply = imap_send (stream,cmd,args);
+ LOCAL->filter = NIL; /* turn off filtering */
+ }
+ }
+ /* do locally if server barfs */
+ if (!strcmp (reply->key,"BAD"))
+ ret = (flags & SE_NOLOCAL) ? NIL:
+ mail_thread_msgs (stream,type,charset,spg,flags | SE_NOSERVER,imap_sort);
+ /* server threaded OK? */
+ else if (imap_OK (stream,reply)) {
+ ret = LOCAL->threaddata;
+ LOCAL->threaddata = NIL; /* mail program is responsible for flushing */
+ }
+ else mm_log (reply->text,ERROR);
+ return ret;
+}
+
+/* IMAP ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream still alive, else NIL
+ */
+
+long imap_ping (MAILSTREAM *stream)
+{
+ return (LOCAL->netstream && /* send "NOOP" */
+ imap_OK (stream,imap_send (stream,"NOOP",NIL))) ? T : NIL;
+}
+
+
+/* IMAP check mailbox
+ * Accepts: MAIL stream
+ */
+
+void imap_check (MAILSTREAM *stream)
+{
+ /* send "CHECK" */
+ IMAPPARSEDREPLY *reply = imap_send (stream,"CHECK",NIL);
+ mm_log (reply->text,imap_OK (stream,reply) ? (long) NIL : ERROR);
+}
+
+/* IMAP expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T if success, NIL if failure
+ */
+
+long imap_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret = NIL;
+ IMAPPARSEDREPLY *reply = NIL;
+ if (sequence) { /* wants selective expunging? */
+ if (options & EX_UID) { /* UID EXPUNGE form? */
+ if (LEVELUIDPLUS (stream)) {/* server support UIDPLUS? */
+ IMAPARG *args[2],aseq;
+ aseq.type = SEQUENCE; aseq.text = (void *) sequence;
+ args[0] = &aseq; args[1] = NIL;
+ ret = imap_OK (stream,reply = imap_send (stream,"UID EXPUNGE",args));
+ }
+ else mm_log ("[NOTUIDPLUS] Can't do UID EXPUNGE with this server",ERROR);
+ }
+ /* otherwise try to make into UID EXPUNGE */
+ else if (mail_sequence (stream,sequence)) {
+ unsigned long i,j;
+ char *t = (char *) fs_get (IMAPTMPLEN);
+ char *s = t;
+ /* search through mailbox */
+ for (*s = '\0', i = 1; i <= stream->nmsgs; ++i)
+ if (mail_elt (stream,i)->sequence) {
+ if (t[0]) *s++ = ','; /* prepend with comma if not first time */
+ sprintf (s,"%lu",mail_uid (stream,j = i));
+ s += strlen (s); /* point at end of string */
+ /* search for possible end of range */
+ while ((i < stream->nmsgs) && mail_elt (stream,i+1)->sequence) i++;
+ if (i != j) { /* output end of range */
+ sprintf (s,":%lu",mail_uid (stream,i));
+ s += strlen (s); /* point at end of string */
+ }
+ if ((s - t) > (IMAPTMPLEN - 50)) {
+ mm_log ("Excessively complex sequence",ERROR);
+ return NIL;
+ }
+ }
+ /* now do as UID EXPUNGE */
+ ret = imap_expunge (stream,t,EX_UID);
+ fs_give ((void **) &t);
+ }
+ }
+ /* ordinary EXPUNGE */
+ else ret = imap_OK (stream,reply = imap_send (stream,"EXPUNGE",NIL));
+ if (reply) mm_log (reply->text,ret ? (long) NIL : ERROR);
+ return ret;
+}
+
+/* IMAP copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * option flags
+ * Returns: T if successful else NIL
+ */
+
+long imap_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long flags)
+{
+ char *cmd = (LEVELIMAP4 (stream) && (flags & CP_UID)) ? "UID COPY" : "COPY";
+ char *s;
+ long ret = NIL;
+ IMAPPARSEDREPLY *reply;
+ IMAPARG *args[3],aseq,ambx;
+ imapreferral_t ir =
+ (imapreferral_t) mail_parameters (stream,GET_IMAPREFERRAL,NIL);
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ if (LOCAL->loser) sequence = imap_reform_sequence (stream,sequence,
+ flags & CP_UID);
+ aseq.type = SEQUENCE; aseq.text = (void *) sequence;
+ ambx.type = ASTRING; ambx.text = (void *) mailbox;
+ args[0] = &aseq; args[1] = &ambx; args[2] = NIL;
+ /* note mailbox in case APPENDUID */
+ LOCAL->appendmailbox = mailbox;
+ /* send "COPY sequence mailbox" */
+ ret = imap_OK (stream,reply = imap_send (stream,cmd,args));
+ LOCAL->appendmailbox = NIL; /* no longer appending */
+ if (ret) { /* success, delete messages if move */
+ if (flags & CP_MOVE) imap_flag (stream,sequence,"\\Deleted",
+ ST_SET + ((flags&CP_UID) ? ST_UID : NIL));
+ }
+ /* failed, do referral action if any */
+ else if (ir && pc && LOCAL->referral && mail_sequence (stream,sequence) &&
+ (s = (*ir) (stream,LOCAL->referral,REFCOPY)))
+ ret = (*pc) (stream,sequence,s,flags | (stream->debug ? CP_DEBUG : NIL));
+ /* otherwise issue error message */
+ else mm_log (reply->text,ERROR);
+ return ret;
+}
+
+/* IMAP mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long imap_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ MAILSTREAM *st = stream;
+ IMAPARG *args[3],ambx,amap;
+ IMAPPARSEDREPLY *reply = NIL;
+ APPENDDATA map;
+ char tmp[MAILTMPLEN];
+ long debug = stream ? stream->debug : NIL;
+ long ret = NIL;
+ imapreferral_t ir =
+ (imapreferral_t) mail_parameters (stream,GET_IMAPREFERRAL,NIL);
+ /* mailbox must be good */
+ if (mail_valid_net (mailbox,&imapdriver,NIL,tmp)) {
+ /* create a stream if given one no good */
+ if ((stream && LOCAL && LOCAL->netstream) ||
+ (stream = mail_open (NIL,mailbox,OP_HALFOPEN|OP_SILENT |
+ (debug ? OP_DEBUG : NIL)))) {
+ /* note mailbox in case APPENDUID */
+ LOCAL->appendmailbox = mailbox;
+ /* use multi-append? */
+ if (LEVELMULTIAPPEND (stream)) {
+ ambx.type = ASTRING; ambx.text = (void *) tmp;
+ amap.type = MULTIAPPEND; amap.text = (void *) &map;
+ map.af = af; map.data = data;
+ args[0] = &ambx; args[1] = &amap; args[2] = NIL;
+ /* success if OK */
+ ret = imap_OK (stream,reply = imap_send (stream,"APPEND",args));
+ LOCAL->appendmailbox = NIL;
+ }
+ /* do succession of single appends */
+ else while ((*af) (stream,data,&map.flags,&map.date,&map.message) &&
+ map.message &&
+ (ret = imap_OK (stream,reply =
+ imap_append_single (stream,tmp,map.flags,
+ map.date,map.message))));
+ LOCAL->appendmailbox = NIL;
+ /* don't do referrals if success or no reply */
+ if (ret || !reply) mailbox = NIL;
+ /* otherwise generate referral */
+ else if (!(mailbox = (ir && LOCAL->referral) ?
+ (*ir) (stream,LOCAL->referral,REFAPPEND) : NIL))
+ mm_log (reply->text,ERROR);
+ /* close temporary stream */
+ if (st != stream) stream = mail_close (stream);
+ if (mailbox) /* chase referral if any */
+ ret = imap_append_referral (mailbox,tmp,af,data,map.flags,map.date,
+ map.message,&map,debug);
+ }
+ else mm_log ("Can't access server for append",ERROR);
+ }
+ return ret; /* return */
+}
+
+/* IMAP mail append message referral retry
+ * Accepts: destination mailbox
+ * temporary buffer
+ * append callback
+ * data for callback
+ * flags from previous attempt
+ * date from previous attempt
+ * message stringstruct from previous attempt
+ * options (currently non-zero to set OP_DEBUG)
+ * Returns: T if append successful, else NIL
+ */
+
+long imap_append_referral (char *mailbox,char *tmp,append_t af,void *data,
+ char *flags,char *date,STRING *message,
+ APPENDDATA *map,long options)
+{
+ MAILSTREAM *stream;
+ IMAPARG *args[3],ambx,amap;
+ IMAPPARSEDREPLY *reply;
+ imapreferral_t ir =
+ (imapreferral_t) mail_parameters (NIL,GET_IMAPREFERRAL,NIL);
+ /* barf if bad mailbox */
+ while (mailbox && mail_valid_net (mailbox,&imapdriver,NIL,tmp)) {
+ /* create a stream if given one no good */
+ if (!(stream = mail_open (NIL,mailbox,OP_HALFOPEN|OP_SILENT |
+ (options ? OP_DEBUG : NIL)))) {
+ sprintf (tmp,"Can't access referral server: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* got referral server, use multi-append? */
+ if (LEVELMULTIAPPEND (stream)) {
+ ambx.type = ASTRING; ambx.text = (void *) tmp;
+ amap.type = MULTIAPPENDREDO; amap.text = (void *) map;
+ args[0] = &ambx; args[1] = &amap; args[2] = NIL;
+ /* do multiappend on referral site */
+ if (imap_OK (stream,reply = imap_send (stream,"APPEND",args))) {
+ mail_close (stream); /* multiappend OK, close stream */
+ return LONGT; /* all done */
+ }
+ }
+ /* do multiple single appends */
+ else while (imap_OK (stream,reply =
+ imap_append_single (stream,tmp,flags,date,message)))
+ if (!((*af) (stream,data,&flags,&date,&message) && message)) {
+ mail_close (stream); /* last message, close stream */
+ return LONGT; /* all done */
+ }
+ /* generate error if no nested referral */
+ if (!(mailbox = (ir && LOCAL->referral) ?
+ (*ir) (stream,LOCAL->referral,REFAPPEND) : NIL))
+ mm_log (reply->text,ERROR);
+ mail_close (stream); /* close previous referral stream */
+ }
+ return NIL; /* bogus mailbox */
+}
+
+/* IMAP append single message
+ * Accepts: mail stream
+ * destination mailbox
+ * initial flags
+ * internal date
+ * stringstruct of message to append
+ * Returns: reply from append
+ */
+
+IMAPPARSEDREPLY *imap_append_single (MAILSTREAM *stream,char *mailbox,
+ char *flags,char *date,STRING *message)
+{
+ MESSAGECACHE elt;
+ IMAPARG *args[5],ambx,aflg,adat,amsg;
+ IMAPPARSEDREPLY *reply;
+ char tmp[MAILTMPLEN];
+ int i;
+ ambx.type = ASTRING; ambx.text = (void *) mailbox;
+ args[i = 0] = &ambx;
+ if (flags) {
+ aflg.type = FLAGS; aflg.text = (void *) flags;
+ args[++i] = &aflg;
+ }
+ if (date) { /* ensure date in INTERNALDATE format */
+ if (!mail_parse_date (&elt,date)) {
+ /* flush previous reply */
+ if (LOCAL->reply.line) fs_give ((void **) &LOCAL->reply.line);
+ /* build new fake reply */
+ LOCAL->reply.tag = LOCAL->reply.line = cpystr ("*");
+ LOCAL->reply.key = "BAD";
+ LOCAL->reply.text = "Bad date in append";
+ return &LOCAL->reply;
+ }
+ adat.type = ASTRING;
+ adat.text = (void *) (date = mail_date (tmp,&elt));
+ args[++i] = &adat;
+ }
+ amsg.type = LITERAL; amsg.text = (void *) message;
+ args[++i] = &amsg;
+ args[++i] = NIL;
+ /* easy if IMAP4[rev1] */
+ if (LEVELIMAP4 (stream)) reply = imap_send (stream,"APPEND",args);
+ else { /* try the IMAP2bis way */
+ args[1] = &amsg; args[2] = NIL;
+ reply = imap_send (stream,"APPEND",args);
+ }
+ return reply;
+}
+
+/* IMAP garbage collect stream
+ * Accepts: Mail stream
+ * garbage collection flags
+ */
+
+void imap_gc (MAILSTREAM *stream,long gcflags)
+{
+ unsigned long i;
+ MESSAGECACHE *elt;
+ mailcache_t mc = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL);
+ /* make sure the cache is large enough */
+ (*mc) (stream,stream->nmsgs,CH_SIZE);
+ if (gcflags & GC_TEXTS) { /* garbage collect texts? */
+ if (!stream->scache) for (i = 1; i <= stream->nmsgs; ++i)
+ if (elt = (MESSAGECACHE *) (*mc) (stream,i,CH_ELT))
+ imap_gc_body (elt->private.msg.body);
+ imap_gc_body (stream->body);
+ }
+ /* gc cache if requested and unlocked */
+ if (gcflags & GC_ELT) for (i = 1; i <= stream->nmsgs; ++i)
+ if ((elt = (MESSAGECACHE *) (*mc) (stream,i,CH_ELT)) &&
+ (elt->lockcount == 1)) (*mc) (stream,i,CH_FREE);
+}
+
+/* IMAP garbage collect body texts
+ * Accepts: body to GC
+ */
+
+void imap_gc_body (BODY *body)
+{
+ PART *part;
+ if (body) { /* have a body? */
+ if (body->mime.text.data) /* flush MIME data */
+ fs_give ((void **) &body->mime.text.data);
+ /* flush text contents */
+ if (body->contents.text.data)
+ fs_give ((void **) &body->contents.text.data);
+ body->mime.text.size = body->contents.text.size = 0;
+ /* multipart? */
+ if (body->type == TYPEMULTIPART)
+ for (part = body->nested.part; part; part = part->next)
+ imap_gc_body (&part->body);
+ /* MESSAGE/RFC822? */
+ else if ((body->type == TYPEMESSAGE) && !strcmp (body->subtype,"RFC822")) {
+ imap_gc_body (body->nested.msg->body);
+ if (body->nested.msg->full.text.data)
+ fs_give ((void **) &body->nested.msg->full.text.data);
+ if (body->nested.msg->header.text.data)
+ fs_give ((void **) &body->nested.msg->header.text.data);
+ if (body->nested.msg->text.text.data)
+ fs_give ((void **) &body->nested.msg->text.text.data);
+ body->nested.msg->full.text.size = body->nested.msg->header.text.size =
+ body->nested.msg->text.text.size = 0;
+ }
+ }
+}
+
+/* IMAP get capabilities
+ * Accepts: mail stream
+ */
+
+void imap_capability (MAILSTREAM *stream)
+{
+ THREADER *thr,*t;
+ LOCAL->gotcapability = NIL; /* flush any previous capabilities */
+ /* request new capabilities */
+ imap_send (stream,"CAPABILITY",NIL);
+ if (!LOCAL->gotcapability) { /* did server get any? */
+ /* no, flush threaders just in case */
+ if (thr = LOCAL->cap.threader) while (t = thr) {
+ fs_give ((void **) &t->name);
+ thr = t->next;
+ fs_give ((void **) &t);
+ }
+ /* zap most capabilities */
+ memset (&LOCAL->cap,0,sizeof (LOCAL->cap));
+ /* assume IMAP2bis server if failure */
+ LOCAL->cap.imap2bis = LOCAL->cap.rfc1176 = T;
+ }
+}
+
+/* IMAP set ACL
+ * Accepts: mail stream
+ * mailbox name
+ * authentication identifer
+ * new access rights
+ * Returns: T on success, NIL on failure
+ */
+
+long imap_setacl (MAILSTREAM *stream,char *mailbox,char *id,char *rights)
+{
+ IMAPARG *args[4],ambx,aid,art;
+ ambx.type = aid.type = art.type = ASTRING;
+ ambx.text = (void *) mailbox; aid.text = (void *) id;
+ art.text = (void *) rights;
+ args[0] = &ambx; args[1] = &aid; args[2] = &art; args[3] = NIL;
+ return imap_acl_work (stream,"SETACL",args);
+}
+
+
+/* IMAP delete ACL
+ * Accepts: mail stream
+ * mailbox name
+ * authentication identifer
+ * Returns: T on success, NIL on failure
+ */
+
+long imap_deleteacl (MAILSTREAM *stream,char *mailbox,char *id)
+{
+ IMAPARG *args[3],ambx,aid;
+ ambx.type = aid.type = ASTRING;
+ ambx.text = (void *) mailbox; aid.text = (void *) id;
+ args[0] = &ambx; args[1] = &aid; args[2] = NIL;
+ return imap_acl_work (stream,"DELETEACL",args);
+}
+
+
+/* IMAP get ACL
+ * Accepts: mail stream
+ * mailbox name
+ * Returns: T on success with data returned via callback, NIL on failure
+ */
+
+long imap_getacl (MAILSTREAM *stream,char *mailbox)
+{
+ IMAPARG *args[2],ambx;
+ ambx.type = ASTRING; ambx.text = (void *) mailbox;
+ args[0] = &ambx; args[1] = NIL;
+ return imap_acl_work (stream,"GETACL",args);
+}
+
+/* IMAP list rights
+ * Accepts: mail stream
+ * mailbox name
+ * authentication identifer
+ * Returns: T on success with data returned via callback, NIL on failure
+ */
+
+long imap_listrights (MAILSTREAM *stream,char *mailbox,char *id)
+{
+ IMAPARG *args[3],ambx,aid;
+ ambx.type = aid.type = ASTRING;
+ ambx.text = (void *) mailbox; aid.text = (void *) id;
+ args[0] = &ambx; args[1] = &aid; args[2] = NIL;
+ return imap_acl_work (stream,"LISTRIGHTS",args);
+}
+
+
+/* IMAP my rights
+ * Accepts: mail stream
+ * mailbox name
+ * Returns: T on success with data returned via callback, NIL on failure
+ */
+
+long imap_myrights (MAILSTREAM *stream,char *mailbox)
+{
+ IMAPARG *args[2],ambx;
+ ambx.type = ASTRING; ambx.text = (void *) mailbox;
+ args[0] = &ambx; args[1] = NIL;
+ return imap_acl_work (stream,"MYRIGHTS",args);
+}
+
+
+/* IMAP ACL worker routine
+ * Accepts: mail stream
+ * command
+ * command arguments
+ * Returns: T on success, NIL on failure
+ */
+
+long imap_acl_work (MAILSTREAM *stream,char *command,IMAPARG *args[])
+{
+ long ret = NIL;
+ if (LEVELACL (stream)) { /* send command */
+ IMAPPARSEDREPLY *reply;
+ if (imap_OK (stream,reply = imap_send (stream,command,args)))
+ ret = LONGT;
+ else mm_log (reply->text,ERROR);
+ }
+ else mm_log ("ACL not available on this IMAP server",ERROR);
+ return ret;
+}
+
+/* IMAP set quota
+ * Accepts: mail stream
+ * quota root name
+ * resource limit list as a stringlist
+ * Returns: T on success with data returned via callback, NIL on failure
+ */
+
+long imap_setquota (MAILSTREAM *stream,char *qroot,STRINGLIST *limits)
+{
+ long ret = NIL;
+ if (LEVELQUOTA (stream)) { /* send "SETQUOTA" */
+ IMAPPARSEDREPLY *reply;
+ IMAPARG *args[3],aqrt,alim;
+ aqrt.type = ASTRING; aqrt.text = (void *) qroot;
+ alim.type = SNLIST; alim.text = (void *) limits;
+ args[0] = &aqrt; args[1] = &alim; args[2] = NIL;
+ if (imap_OK (stream,reply = imap_send (stream,"SETQUOTA",args)))
+ ret = LONGT;
+ else mm_log (reply->text,ERROR);
+ }
+ else mm_log ("Quota not available on this IMAP server",ERROR);
+ return ret;
+}
+
+/* IMAP get quota
+ * Accepts: mail stream
+ * quota root name
+ * Returns: T on success with data returned via callback, NIL on failure
+ */
+
+long imap_getquota (MAILSTREAM *stream,char *qroot)
+{
+ long ret = NIL;
+ if (LEVELQUOTA (stream)) { /* send "GETQUOTA" */
+ IMAPPARSEDREPLY *reply;
+ IMAPARG *args[2],aqrt;
+ aqrt.type = ASTRING; aqrt.text = (void *) qroot;
+ args[0] = &aqrt; args[1] = NIL;
+ if (imap_OK (stream,reply = imap_send (stream,"GETQUOTA",args)))
+ ret = LONGT;
+ else mm_log (reply->text,ERROR);
+ }
+ else mm_log ("Quota not available on this IMAP server",ERROR);
+ return ret;
+}
+
+
+/* IMAP get quota root
+ * Accepts: mail stream
+ * mailbox name
+ * Returns: T on success with data returned via callback, NIL on failure
+ */
+
+long imap_getquotaroot (MAILSTREAM *stream,char *mailbox)
+{
+ long ret = NIL;
+ if (LEVELQUOTA (stream)) { /* send "GETQUOTAROOT" */
+ IMAPPARSEDREPLY *reply;
+ IMAPARG *args[2],ambx;
+ ambx.type = ASTRING; ambx.text = (void *) mailbox;
+ args[0] = &ambx; args[1] = NIL;
+ if (imap_OK (stream,reply = imap_send (stream,"GETQUOTAROOT",args)))
+ ret = LONGT;
+ else mm_log (reply->text,ERROR);
+ }
+ else mm_log ("Quota not available on this IMAP server",ERROR);
+ return ret;
+}
+
+/* Internal routines */
+
+
+/* IMAP send command
+ * Accepts: MAIL stream
+ * command
+ * argument list
+ * Returns: parsed reply
+ */
+
+#define CMDBASE LOCAL->tmp /* command base */
+
+IMAPPARSEDREPLY *imap_send (MAILSTREAM *stream,char *cmd,IMAPARG *args[])
+{
+ IMAPPARSEDREPLY *reply;
+ IMAPARG *arg,**arglst;
+ SORTPGM *spg;
+ STRINGLIST *list;
+ SIZEDTEXT st;
+ APPENDDATA *map;
+ sendcommand_t sc = (sendcommand_t) mail_parameters (NIL,GET_SENDCOMMAND,NIL);
+ size_t i;
+ void *a;
+ char c,*s,*t,tag[10];
+ stream->unhealthy = NIL; /* make stream healthy again */
+ /* gensym a new tag */
+ sprintf (tag,"%08lx",0xffffffff & (stream->gensym++));
+ if (!LOCAL->netstream) /* make sure have a session */
+ return imap_fake (stream,tag,"[CLOSED] IMAP connection lost");
+ mail_lock (stream); /* lock up the stream */
+ if (sc) /* tell client sending a command */
+ (*sc) (stream,cmd,((compare_cstring (cmd,"FETCH") &&
+ compare_cstring (cmd,"STORE") &&
+ compare_cstring (cmd,"SEARCH")) ?
+ NIL : SC_EXPUNGEDEFERRED));
+ /* ignore referral from previous command */
+ if (LOCAL->referral) fs_give ((void **) &LOCAL->referral);
+ sprintf (CMDBASE,"%s %s",tag,cmd);
+ s = CMDBASE + strlen (CMDBASE);
+ if (arglst = args) while (arg = *arglst++) {
+ *s++ = ' '; /* delimit argument with space */
+ switch (arg->type) {
+ case ATOM: /* atom */
+ for (t = (char *) arg->text; *t; *s++ = *t++);
+ break;
+ case NUMBER: /* number */
+ sprintf (s,"%lu",(unsigned long) arg->text);
+ s += strlen (s);
+ break;
+ case FLAGS: /* flag list as a single string */
+ if (*(t = (char *) arg->text) != '(') {
+ *s++ = '('; /* wrap parens around string */
+ while (*t) *s++ = *t++;
+ *s++ = ')'; /* wrap parens around string */
+ }
+ else while (*t) *s++ = *t++;
+ break;
+ case ASTRING: /* atom or string, must be literal? */
+ st.size = strlen ((char *) (st.data = (unsigned char *) arg->text));
+ if (reply = imap_send_astring (stream,tag,&s,&st,NIL,CMDBASE+MAXCOMMAND))
+ return reply;
+ break;
+ case LITERAL: /* literal, as a stringstruct */
+ if (reply = imap_send_literal (stream,tag,&s,arg->text)) return reply;
+ break;
+
+ case LIST: /* list of strings */
+ list = (STRINGLIST *) arg->text;
+ c = '('; /* open paren */
+ do { /* for each list item */
+ *s++ = c; /* write prefix character */
+ if (reply = imap_send_astring (stream,tag,&s,&list->text,NIL,
+ CMDBASE+MAXCOMMAND)) return reply;
+ c = ' '; /* prefix character for subsequent strings */
+ }
+ while (list = list->next);
+ *s++ = ')'; /* close list */
+ break;
+ case SEARCHPROGRAM: /* search program */
+ if (reply = imap_send_spgm (stream,tag,CMDBASE,&s,arg->text,
+ CMDBASE+MAXCOMMAND))
+ return reply;
+ break;
+ case SORTPROGRAM: /* search program */
+ c = '('; /* open paren */
+ for (spg = (SORTPGM *) arg->text; spg; spg = spg->next) {
+ *s++ = c; /* write prefix */
+ if (spg->reverse) for (t = "REVERSE "; *t; *s++ = *t++);
+ switch (spg->function) {
+ case SORTDATE:
+ for (t = "DATE"; *t; *s++ = *t++);
+ break;
+ case SORTARRIVAL:
+ for (t = "ARRIVAL"; *t; *s++ = *t++);
+ break;
+ case SORTFROM:
+ for (t = "FROM"; *t; *s++ = *t++);
+ break;
+ case SORTSUBJECT:
+ for (t = "SUBJECT"; *t; *s++ = *t++);
+ break;
+ case SORTTO:
+ for (t = "TO"; *t; *s++ = *t++);
+ break;
+ case SORTCC:
+ for (t = "CC"; *t; *s++ = *t++);
+ break;
+ case SORTSIZE:
+ for (t = "SIZE"; *t; *s++ = *t++);
+ break;
+ default:
+ fatal ("Unknown sort program function in imap_send()!");
+ }
+ c = ' '; /* prefix character for subsequent items */
+ }
+ *s++ = ')'; /* close list */
+ break;
+
+ case BODYTEXT: /* body section */
+ for (t = "BODY["; *t; *s++ = *t++);
+ for (t = (char *) arg->text; *t; *s++ = *t++);
+ break;
+ case BODYPEEK: /* body section */
+ for (t = "BODY.PEEK["; *t; *s++ = *t++);
+ for (t = (char *) arg->text; *t; *s++ = *t++);
+ break;
+ case BODYCLOSE: /* close bracket and possible length */
+ s[-1] = ']'; /* no leading space */
+ for (t = (char *) arg->text; *t; *s++ = *t++);
+ break;
+ case SEQUENCE: /* sequence */
+ if ((i = strlen (t = (char *) arg->text)) <= (size_t) MAXCOMMAND)
+ while (*t) *s++ = *t++; /* easy case */
+ else {
+ mail_unlock (stream); /* unlock stream */
+ a = arg->text; /* save original sequence pointer */
+ arg->type = ATOM; /* make recursive call be faster */
+ do { /* break up into multiple commands */
+ if (i <= MAXCOMMAND) {/* final part? */
+ reply = imap_send (stream,cmd,args);
+ i = 0; /* and mark as done */
+ }
+ else { /* still needs to be split further */
+ if (!(t = strchr (t + MAXCOMMAND - 30,',')) ||
+ ((t - (char *) arg->text) > MAXCOMMAND))
+ fatal ("impossible over-long sequence");
+ *t = '\0'; /* tie off sequence at point of split*/
+ /* recurse to do this part */
+ reply = imap_send (stream,cmd,args);
+ *t++ = ','; /* restore the comma in case something cares */
+ /* punt if error */
+ if (!imap_OK (stream,reply)) break;
+ /* calculate size of remaining sequence */
+ i -= (t - (char *) arg->text);
+ /* point to new remaining sequence */
+ arg->text = (void *) t;
+ }
+ } while (i);
+ arg->type = SEQUENCE; /* restore in case something cares */
+ arg->text = a;
+ return reply; /* return result */
+ }
+ break;
+ case LISTMAILBOX: /* astring with wildcards */
+ st.size = strlen ((char *) (st.data = (unsigned char *) arg->text));
+ if (reply = imap_send_astring (stream,tag,&s,&st,T,CMDBASE+MAXCOMMAND))
+ return reply;
+ break;
+
+ case MULTIAPPEND: /* append multiple messages */
+ /* get package pointer */
+ map = (APPENDDATA *) arg->text;
+ if (!(*map->af) (stream,map->data,&map->flags,&map->date,&map->message)||
+ !map->message) {
+ STRING es;
+ INIT (&es,mail_string,"",0);
+ return (reply = imap_send_literal (stream,tag,&s,&es)) ?
+ reply : imap_fake (stream,tag,"Server zero-length literal error");
+ }
+ case MULTIAPPENDREDO: /* redo multiappend */
+ /* get package pointer */
+ map = (APPENDDATA *) arg->text;
+ do { /* make sure date valid if given */
+ char datetmp[MAILTMPLEN];
+ MESSAGECACHE elt;
+ STRING es;
+ if (!map->date || mail_parse_date (&elt,map->date)) {
+ if (t = map->flags) { /* flags given? */
+ if (*t != '(') {
+ *s++ = '('; /* wrap parens around string */
+ while (*t) *s++ = *t++;
+ *s++ = ')'; /* wrap parens around string */
+ }
+ else while (*t) *s++ = *t++;
+ *s++ = ' '; /* delimit with space */
+ }
+ if (map->date) { /* date given? */
+ st.size = strlen ((char *) (st.data = (unsigned char *)
+ mail_date (datetmp,&elt)));
+ if (reply = imap_send_astring (stream,tag,&s,&st,NIL,
+ CMDBASE+MAXCOMMAND)) return reply;
+ *s++ = ' '; /* delimit with space */
+ }
+ if (reply = imap_send_literal (stream,tag,&s,map->message))
+ return reply;
+ /* get next message */
+ if ((*map->af) (stream,map->data,&map->flags,&map->date,
+ &map->message)) {
+ /* have a message, delete next in command */
+ if (map->message) *s++ = ' ';
+ continue; /* loop back for next message */
+ }
+ }
+ /* bad date or need to abort */
+ INIT (&es,mail_string,"",0);
+ return (reply = imap_send_literal (stream,tag,&s,&es)) ?
+ reply : imap_fake (stream,tag,"Server zero-length literal error");
+ break; /* exit the loop */
+ } while (map->message);
+ break;
+
+ case SNLIST: /* list of string/number pairs */
+ list = (STRINGLIST *) arg->text;
+ c = '('; /* open paren */
+ do { /* for each list item */
+ *s++ = c; /* write prefix character */
+ if (list) { /* sigh, QUOTA has bizarre syntax! */
+ for (t = (char *) list->text.data; *t; *s++ = *t++);
+ sprintf (s," %lu",list->text.size);
+ s += strlen (s);
+ c = ' '; /* prefix character for subsequent strings */
+ }
+ }
+ while (list = list->next);
+ *s++ = ')'; /* close list */
+ break;
+ default:
+ fatal ("Unknown argument type in imap_send()!");
+ }
+ }
+ /* send the command */
+ reply = imap_sout (stream,tag,CMDBASE,&s);
+ mail_unlock (stream); /* unlock stream */
+ return reply;
+}
+
+/* IMAP send atom-string
+ * Accepts: MAIL stream
+ * reply tag
+ * pointer to current position pointer of output bigbuf
+ * atom-string to output
+ * flag if list_wildcards allowed
+ * maximum to write as atom or qstring
+ * Returns: error reply or NIL if success
+ */
+
+IMAPPARSEDREPLY *imap_send_astring (MAILSTREAM *stream,char *tag,char **s,
+ SIZEDTEXT *as,long wildok,char *limit)
+{
+ unsigned long j;
+ char c;
+ STRING st;
+ /* default to atom unless empty or loser */
+ int qflag = (as->size && !LOCAL->loser) ? NIL : T;
+ /* in case needed */
+ INIT (&st,mail_string,(void *) as->data,as->size);
+ /* always write literal if no space */
+ if ((*s + as->size) > limit) return imap_send_literal (stream,tag,s,&st);
+ for (j = 0; j < as->size; j++) switch (c = as->data[j]) {
+ default: /* all other characters */
+ if (!(c & 0x80)) { /* must not be 8bit */
+ if (c <= ' ') qflag = T; /* must quote if a CTL */
+ break;
+ }
+ case '\0': /* not a CHAR */
+ case '\012': case '\015': /* not a TEXT-CHAR */
+ case '"': case '\\': /* quoted-specials (IMAP2 required this) */
+ return imap_send_literal (stream,tag,s,&st);
+ case '*': case '%': /* list_wildcards */
+ if (wildok) break; /* allowed if doing the wild thing */
+ /* atom_specials */
+ case '(': case ')': case '{': case ' ': case 0x7f:
+#if 0
+ case '"': case '\\': /* quoted-specials (could work in IMAP4) */
+#endif
+ qflag = T; /* must use quoted string format */
+ break;
+ }
+ if (qflag) *(*s)++ = '"'; /* write open quote */
+ for (j = 0; j < as->size; j++) *(*s)++ = as->data[j];
+ if (qflag) *(*s)++ = '"'; /* write close quote */
+ return NIL;
+}
+
+/* IMAP send literal
+ * Accepts: MAIL stream
+ * reply tag
+ * pointer to current position pointer of output bigbuf
+ * literal to output as stringstruct
+ * Returns: error reply or NIL if success
+ */
+
+IMAPPARSEDREPLY *imap_send_literal (MAILSTREAM *stream,char *tag,char **s,
+ STRING *st)
+{
+ IMAPPARSEDREPLY *reply;
+ unsigned long i = SIZE (st);
+ unsigned long j;
+ sprintf (*s,"{%lu}",i); /* write literal count */
+ *s += strlen (*s); /* size of literal count */
+ /* send the command */
+ reply = imap_sout (stream,tag,CMDBASE,s);
+ if (strcmp (reply->tag,"+")) {/* prompt for more data? */
+ mail_unlock (stream); /* no, give up */
+ return reply;
+ }
+ while (i) { /* dump the text */
+ if (st->cursize) { /* if text to do in this chunk */
+ /* RFC 3501 technically forbids NULs in literals. Normally, the
+ * delivering MTA would take care of MIME converting the message text
+ * so that it is NUL-free. If it doesn't, then we have the choice of
+ * either violating IMAP by sending NULs, corrupting the data, or going
+ * to lots of work to do MIME conversion in the IMAP server.
+ *
+ * No current stringstruct driver objects to having its buffer patched.
+ * If this ever changes, it will be necessary to change this kludge.
+ */
+ /* patch NULs to C1 control */
+ for (j = 0; j < st->cursize; ++j)
+ if (!st->curpos[j]) st->curpos[j] = 0x80;
+ if (!net_sout (LOCAL->netstream,st->curpos,st->cursize)) {
+ mail_unlock (stream);
+ return imap_fake (stream,tag,"[CLOSED] IMAP connection broken (data)");
+ }
+ i -= st->cursize; /* note that we wrote out this much */
+ st->curpos += (st->cursize - 1);
+ st->cursize = 0;
+ }
+ (*st->dtb->next) (st); /* advance to next buffer's worth */
+ }
+ return NIL; /* success */
+}
+
+/* IMAP send search program
+ * Accepts: MAIL stream
+ * reply tag
+ * base pointer if trimming needed
+ * pointer to current position pointer of output bigbuf
+ * search program to output
+ * pointer to limit guideline
+ * Returns: error reply or NIL if success
+ */
+
+
+IMAPPARSEDREPLY *imap_send_spgm (MAILSTREAM *stream,char *tag,char *base,
+ char **s,SEARCHPGM *pgm,char *limit)
+{
+ IMAPPARSEDREPLY *reply;
+ SEARCHHEADER *hdr;
+ SEARCHOR *pgo;
+ SEARCHPGMLIST *pgl;
+ char *t;
+ /* trim if called recursively */
+ if (base) *s = imap_send_spgm_trim (base,*s,NIL);
+ base = *s; /* this is the new base */
+ /* default searchpgm */
+ for (t = "ALL"; *t; *(*s)++ = *t++);
+ if (!pgm) return NIL; /* done if NIL searchpgm */
+ if ((pgm->msgno && /* message sequences */
+ (pgm->msgno->next || /* trim away first:last */
+ (pgm->msgno->first != 1) || (pgm->msgno->last != stream->nmsgs)) &&
+ (reply = imap_send_sset (stream,tag,base,s,pgm->msgno," ",limit))) ||
+ (pgm->uid &&
+ (reply = imap_send_sset (stream,tag,base,s,pgm->uid," UID ",limit))))
+ return reply;
+ /* message sizes */
+ if (pgm->larger) {
+ sprintf (*s," LARGER %lu",pgm->larger);
+ *s += strlen (*s);
+ }
+ if (pgm->smaller) {
+ sprintf (*s," SMALLER %lu",pgm->smaller);
+ *s += strlen (*s);
+ }
+
+ /* message flags */
+ if (pgm->answered) for (t = " ANSWERED"; *t; *(*s)++ = *t++);
+ if (pgm->unanswered) for (t =" UNANSWERED"; *t; *(*s)++ = *t++);
+ if (pgm->deleted) for (t =" DELETED"; *t; *(*s)++ = *t++);
+ if (pgm->undeleted) for (t =" UNDELETED"; *t; *(*s)++ = *t++);
+ if (pgm->draft) for (t =" DRAFT"; *t; *(*s)++ = *t++);
+ if (pgm->undraft) for (t =" UNDRAFT"; *t; *(*s)++ = *t++);
+ if (pgm->flagged) for (t =" FLAGGED"; *t; *(*s)++ = *t++);
+ if (pgm->unflagged) for (t =" UNFLAGGED"; *t; *(*s)++ = *t++);
+ if (pgm->recent) for (t =" RECENT"; *t; *(*s)++ = *t++);
+ if (pgm->old) for (t =" OLD"; *t; *(*s)++ = *t++);
+ if (pgm->seen) for (t =" SEEN"; *t; *(*s)++ = *t++);
+ if (pgm->unseen) for (t =" UNSEEN"; *t; *(*s)++ = *t++);
+ if ((pgm->keyword && /* keywords */
+ (reply = imap_send_slist (stream,tag,base,s," KEYWORD ",pgm->keyword,
+ limit))) ||
+ (pgm->unkeyword &&
+ (reply = imap_send_slist (stream,tag,base,s," UNKEYWORD ",
+ pgm->unkeyword,limit))))
+ return reply;
+ /* sent date ranges */
+ if (pgm->sentbefore) imap_send_sdate (s,"SENTBEFORE",pgm->sentbefore);
+ if (pgm->senton) imap_send_sdate (s,"SENTON",pgm->senton);
+ if (pgm->sentsince) imap_send_sdate (s,"SENTSINCE",pgm->sentsince);
+ /* internal date ranges */
+ if (pgm->before) imap_send_sdate (s,"BEFORE",pgm->before);
+ if (pgm->on) imap_send_sdate (s,"ON",pgm->on);
+ if (pgm->since) imap_send_sdate (s,"SINCE",pgm->since);
+ if (pgm->older) {
+ sprintf (*s," OLDER %lu",pgm->older);
+ *s += strlen (*s);
+ }
+ if (pgm->younger) {
+ sprintf (*s," YOUNGER %lu",pgm->younger);
+ *s += strlen (*s);
+ }
+ /* search texts */
+ if ((pgm->bcc && (reply = imap_send_slist (stream,tag,base,s," BCC ",
+ pgm->bcc,limit))) ||
+ (pgm->cc && (reply = imap_send_slist (stream,tag,base,s," CC ",pgm->cc,
+ limit))) ||
+ (pgm->from && (reply = imap_send_slist (stream,tag,base,s," FROM ",
+ pgm->from,limit))) ||
+ (pgm->to && (reply = imap_send_slist (stream,tag,base,s," TO ",pgm->to,
+ limit))))
+ return reply;
+ if ((pgm->subject && (reply = imap_send_slist (stream,tag,base,s," SUBJECT ",
+ pgm->subject,limit))) ||
+ (pgm->body && (reply = imap_send_slist (stream,tag,base,s," BODY ",
+ pgm->body,limit))) ||
+ (pgm->text && (reply = imap_send_slist (stream,tag,base,s," TEXT ",
+ pgm->text,limit))))
+ return reply;
+
+ /* Note that these criteria are not supported by IMAP and have to be
+ emulated */
+ if ((pgm->return_path &&
+ (reply = imap_send_slist (stream,tag,base,s," HEADER Return-Path ",
+ pgm->return_path,limit))) ||
+ (pgm->sender &&
+ (reply = imap_send_slist (stream,tag,base,s," HEADER Sender ",
+ pgm->sender,limit))) ||
+ (pgm->reply_to &&
+ (reply = imap_send_slist (stream,tag,base,s," HEADER Reply-To ",
+ pgm->reply_to,limit))) ||
+ (pgm->in_reply_to &&
+ (reply = imap_send_slist (stream,tag,base,s," HEADER In-Reply-To ",
+ pgm->in_reply_to,limit))) ||
+ (pgm->message_id &&
+ (reply = imap_send_slist (stream,tag,base,s," HEADER Message-ID ",
+ pgm->message_id,limit))) ||
+ (pgm->newsgroups &&
+ (reply = imap_send_slist (stream,tag,base,s," HEADER Newsgroups ",
+ pgm->newsgroups,limit))) ||
+ (pgm->followup_to &&
+ (reply = imap_send_slist (stream,tag,base,s," HEADER Followup-To ",
+ pgm->followup_to,limit))) ||
+ (pgm->references &&
+ (reply = imap_send_slist (stream,tag,base,s," HEADER References ",
+ pgm->references,limit)))) return reply;
+
+ /* all other headers */
+ if (hdr = pgm->header) do {
+ *s = imap_send_spgm_trim (base,*s," HEADER ");
+ if (reply = imap_send_astring (stream,tag,s,&hdr->line,NIL,limit))
+ return reply;
+ *(*s)++ = ' ';
+ if (reply = imap_send_astring (stream,tag,s,&hdr->text,NIL,limit))
+ return reply;
+ } while (hdr = hdr->next);
+ for (pgo = pgm->or; pgo; pgo = pgo->next) {
+ *s = imap_send_spgm_trim (base,*s," OR (");
+ if (reply = imap_send_spgm (stream,tag,base,s,pgo->first,limit))
+ return reply;
+ for (t = ") ("; *t; *(*s)++ = *t++);
+ if (reply = imap_send_spgm (stream,tag,base,s,pgo->second,limit))
+ return reply;
+ *(*s)++ = ')';
+ }
+ for (pgl = pgm->not; pgl; pgl = pgl->next) {
+ *s = imap_send_spgm_trim (base,*s," NOT (");
+ if (reply = imap_send_spgm (stream,tag,base,s,pgl->pgm,limit))
+ return reply;
+ *(*s)++ = ')';
+ }
+ /* trim if needed */
+ *s = imap_send_spgm_trim (base,*s,NIL);
+ return NIL; /* search program written OK */
+}
+
+
+/* Write new text and trim extraneous "ALL" from searchpgm
+ * Accepts: pointer to start of searchpgm or NIL
+ * current end pointer
+ * new text to write or NIL
+ * Returns: new end pointer, trimmed if needed
+ */
+
+char *imap_send_spgm_trim (char *base,char *s,char *text)
+{
+ char *t;
+ /* write new text */
+ if (text) for (t = text; *t; *s++ = *t++);
+ /* need to trim? */
+ if (base && (s > (t = (base + 4))) && (*base == 'A') && (base[1] == 'L') &&
+ (base[2] == 'L') && (base[3] == ' ')) {
+ memmove (base,t,s - t); /* yes, blat down remaining text */
+ s -= 4; /* and reduce current pointer */
+ }
+ return s; /* return new end pointer */
+}
+
+/* IMAP send search set
+ * Accepts: MAIL stream
+ * current command tag
+ * base pointer if trimming needed
+ * pointer to current position pointer of output bigbuf
+ * search set to output
+ * message prefix
+ * maximum output pointer
+ * Returns: NIL if success, error reply if error
+ */
+
+IMAPPARSEDREPLY *imap_send_sset (MAILSTREAM *stream,char *tag,char *base,
+ char **s,SEARCHSET *set,char *prefix,
+ char *limit)
+{
+ IMAPPARSEDREPLY *reply;
+ STRING st;
+ char c,*t;
+ char *start = *s;
+ /* trim and write prefix */
+ *s = imap_send_spgm_trim (base,*s,prefix);
+ /* run down search list */
+ for (c = NIL; set && (*s < limit); set = set->next, c = ',') {
+ if (c) *(*s)++ = c; /* write delimiter and first value */
+ if (set->first == 0xffffffff) *(*s)++ = '*';
+ else {
+ sprintf (*s,"%lu",set->first);
+ *s += strlen (*s);
+ }
+ /* have a second value? */
+ if (set->last && (set->first != set->last)) {
+ *(*s)++ = ':'; /* write delimiter and second value */
+ if (set->last == 0xffffffff) *(*s)++ = '*';
+ else {
+ sprintf (*s,"%lu",set->last);
+ *s += strlen (*s);
+ }
+ }
+ }
+ if (set) { /* insert "OR" in front of incomplete set */
+ memmove (start + 3,start,*s - start);
+ memcpy (start," OR",3);
+ *s += 3; /* point to end of buffer */
+ /* write glue that is equivalent to ALL */
+ for (t =" ((OR BCC FOO NOT BCC "; *t; *(*s)++ = *t++);
+ /* but broken by a literal */
+ INIT (&st,mail_string,(void *) "FOO",3);
+ if (reply = imap_send_literal (stream,tag,s,&st)) return reply;
+ *(*s)++ = ')'; /* close glue */
+ if (reply = imap_send_sset (stream,tag,NIL,s,set,prefix,limit))
+ return reply;
+ *(*s)++ = ')'; /* close second OR argument */
+ }
+ return NIL;
+}
+
+/* IMAP send search list
+ * Accepts: MAIL stream
+ * reply tag
+ * base pointer if trimming needed
+ * pointer to current position pointer of output bigbuf
+ * name of search list
+ * search list to output
+ * maximum output pointer
+ * Returns: NIL if success, error reply if error
+ */
+
+IMAPPARSEDREPLY *imap_send_slist (MAILSTREAM *stream,char *tag,char *base,
+ char **s,char *name,STRINGLIST *list,
+ char *limit)
+{
+ IMAPPARSEDREPLY *reply;
+ do {
+ *s = imap_send_spgm_trim (base,*s,name);
+ base = NIL; /* no longer need trimming */
+ reply = imap_send_astring (stream,tag,s,&list->text,NIL,limit);
+ }
+ while (!reply && (list = list->next));
+ return reply;
+}
+
+
+/* IMAP send search date
+ * Accepts: pointer to current position pointer of output bigbuf
+ * field name
+ * search date to output
+ */
+
+void imap_send_sdate (char **s,char *name,unsigned short date)
+{
+ sprintf (*s," %s %d-%s-%d",name,date & 0x1f,
+ months[((date >> 5) & 0xf) - 1],BASEYEAR + (date >> 9));
+ *s += strlen (*s);
+}
+
+/* IMAP send buffered command to sender
+ * Accepts: MAIL stream
+ * reply tag
+ * string
+ * pointer to string tail pointer
+ * Returns: reply
+ */
+
+IMAPPARSEDREPLY *imap_sout (MAILSTREAM *stream,char *tag,char *base,char **s)
+{
+ IMAPPARSEDREPLY *reply;
+ if (stream->debug) { /* output debugging telemetry */
+ **s = '\0';
+ mail_dlog (base,LOCAL->sensitive);
+ }
+ *(*s)++ = '\015'; /* append CRLF */
+ *(*s)++ = '\012';
+ **s = '\0';
+ reply = net_sout (LOCAL->netstream,base,*s - base) ?
+ imap_reply (stream,tag) :
+ imap_fake (stream,tag,"[CLOSED] IMAP connection broken (command)");
+ *s = base; /* restart buffer */
+ return reply;
+}
+
+
+/* IMAP send null-terminated string to sender
+ * Accepts: MAIL stream
+ * string
+ * Returns: T if success, else NIL
+ */
+
+long imap_soutr (MAILSTREAM *stream,char *string)
+{
+ long ret;
+ unsigned long i;
+ char *s;
+ if (stream->debug) mm_dlog (string);
+ sprintf (s = (char *) fs_get ((i = strlen (string) + 2) + 1),
+ "%s\015\012",string);
+ ret = net_sout (LOCAL->netstream,s,i);
+ fs_give ((void **) &s);
+ return ret;
+}
+
+/* IMAP get reply
+ * Accepts: MAIL stream
+ * tag to search or NIL if want a greeting
+ * Returns: parsed reply, never NIL
+ */
+
+IMAPPARSEDREPLY *imap_reply (MAILSTREAM *stream,char *tag)
+{
+ IMAPPARSEDREPLY *reply;
+ while (LOCAL->netstream) { /* parse reply from server */
+ if (reply = imap_parse_reply (stream,net_getline (LOCAL->netstream))) {
+ /* continuation ready? */
+ if (!strcmp (reply->tag,"+")) return reply;
+ /* untagged data? */
+ else if (!strcmp (reply->tag,"*")) {
+ imap_parse_unsolicited (stream,reply);
+ if (!tag) return reply; /* return if just wanted greeting */
+ }
+ else { /* tagged data */
+ if (tag && !compare_cstring (tag,reply->tag)) return reply;
+ /* report bogon */
+ sprintf (LOCAL->tmp,"Unexpected tagged response: %.80s %.80s %.80s",
+ (char *) reply->tag,(char *) reply->key,(char *) reply->text);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ }
+ }
+ return imap_fake (stream,tag,
+ "[CLOSED] IMAP connection broken (server response)");
+}
+
+/* IMAP parse reply
+ * Accepts: MAIL stream
+ * text of reply
+ * Returns: parsed reply, or NIL if can't parse at least a tag and key
+ */
+
+
+IMAPPARSEDREPLY *imap_parse_reply (MAILSTREAM *stream,char *text)
+{
+ char *r;
+ if (LOCAL->reply.line) fs_give ((void **) &LOCAL->reply.line);
+ /* init fields in case error */
+ LOCAL->reply.key = LOCAL->reply.text = LOCAL->reply.tag = NIL;
+ if (!(LOCAL->reply.line = text)) {
+ /* NIL text means the stream died */
+ if (LOCAL->netstream) net_close (LOCAL->netstream);
+ LOCAL->netstream = NIL;
+ return NIL;
+ }
+ if (stream->debug) mm_dlog (LOCAL->reply.line);
+ if (!(LOCAL->reply.tag = strtok_r (LOCAL->reply.line," ",&r))) {
+ mm_notify (stream,"IMAP server sent a blank line",WARN);
+ stream->unhealthy = T;
+ return NIL;
+ }
+ /* non-continuation replies */
+ if (strcmp (LOCAL->reply.tag,"+")) {
+ /* parse key */
+ if (!(LOCAL->reply.key = strtok_r (NIL," ",&r))) {
+ /* determine what is missing */
+ sprintf (LOCAL->tmp,"Missing IMAP reply key: %.80s",
+ (char *) LOCAL->reply.tag);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ return NIL; /* can't parse this text */
+ }
+ ucase (LOCAL->reply.key); /* canonicalize key to upper */
+ /* get text as well, allow empty text */
+ if (!(LOCAL->reply.text = strtok_r (NIL,"\n",&r)))
+ LOCAL->reply.text = LOCAL->reply.key + strlen (LOCAL->reply.key);
+ }
+ else { /* special handling of continuation */
+ LOCAL->reply.key = "BAD"; /* so it barfs if not expecting continuation */
+ if (!(LOCAL->reply.text = strtok_r (NIL,"\n",&r)))
+ LOCAL->reply.text = "";
+ }
+ return &LOCAL->reply; /* return parsed reply */
+}
+
+/* IMAP fake reply when stream determined to be dead
+ * Accepts: MAIL stream
+ * tag
+ * text of fake reply (must start with "[CLOSED]")
+ * Returns: parsed reply
+ */
+
+IMAPPARSEDREPLY *imap_fake (MAILSTREAM *stream,char *tag,char *text)
+{
+ mm_notify (stream,text,BYE); /* send bye alert */
+ if (LOCAL->netstream) net_close (LOCAL->netstream);
+ LOCAL->netstream = NIL; /* farewell, dear NET stream... */
+ /* flush previous reply */
+ if (LOCAL->reply.line) fs_give ((void **) &LOCAL->reply.line);
+ /* build new fake reply */
+ LOCAL->reply.tag = LOCAL->reply.line = cpystr (tag ? tag : "*");
+ LOCAL->reply.key = "NO";
+ LOCAL->reply.text = text;
+ return &LOCAL->reply; /* return parsed reply */
+}
+
+
+/* IMAP check for OK response in tagged reply
+ * Accepts: MAIL stream
+ * parsed reply
+ * Returns: T if OK else NIL
+ */
+
+long imap_OK (MAILSTREAM *stream,IMAPPARSEDREPLY *reply)
+{
+ long ret = NIL;
+ /* OK - operation succeeded */
+ if (!strcmp (reply->key,"OK")) {
+ imap_parse_response (stream,reply->text,NIL,NIL);
+ ret = T;
+ }
+ /* NO - operation failed */
+ else if (!strcmp (reply->key,"NO"))
+ imap_parse_response (stream,reply->text,WARN,NIL);
+ else { /* BAD - operation rejected */
+ if (!strcmp (reply->key,"BAD")) {
+ imap_parse_response (stream,reply->text,ERROR,NIL);
+ sprintf (LOCAL->tmp,"IMAP protocol error: %.80s",(char *) reply->text);
+ }
+ /* bad protocol received */
+ else sprintf (LOCAL->tmp,"Unexpected IMAP response: %.80s %.80s",
+ (char *) reply->key,(char *) reply->text);
+ mm_log (LOCAL->tmp,ERROR); /* either way, this is not good */
+ }
+ return ret;
+}
+
+/* IMAP parse and act upon unsolicited reply
+ * Accepts: MAIL stream
+ * parsed reply
+ */
+
+void imap_parse_unsolicited (MAILSTREAM *stream,IMAPPARSEDREPLY *reply)
+{
+ unsigned long i = 0;
+ unsigned long j,msgno;
+ unsigned char *s,*t;
+ char *r;
+ /* see if key is a number */
+ if (isdigit (*reply->key)) {
+ msgno = strtoul (reply->key,(char **) &s,10);
+ if (*s) { /* better be nothing after number */
+ sprintf (LOCAL->tmp,"Unexpected untagged message: %.80s",
+ (char *) reply->key);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ return;
+ }
+ if (!reply->text) { /* better be some data */
+ mm_notify (stream,"Missing message data",WARN);
+ stream->unhealthy = T;
+ return;
+ }
+ /* get message data type, canonicalize upper */
+ s = ucase (strtok_r (reply->text," ",&r));
+ /* and locate the text after it */
+ t = strtok_r (NIL,"\n",&r);
+ /* now take the action */
+ /* change in size of mailbox */
+ if (!strcmp (s,"EXISTS") && (msgno >= stream->nmsgs))
+ mail_exists (stream,msgno);
+ else if (!strcmp (s,"RECENT") && (msgno <= stream->nmsgs))
+ mail_recent (stream,msgno);
+ else if (!strcmp (s,"EXPUNGE") && msgno && (msgno <= stream->nmsgs)) {
+ mailcache_t mc = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL);
+ MESSAGECACHE *elt = (MESSAGECACHE *) (*mc) (stream,msgno,CH_ELT);
+ if (elt) imap_gc_body (elt->private.msg.body);
+ /* notify upper level */
+ mail_expunged (stream,msgno);
+ }
+
+ else if ((!strcmp (s,"FETCH") || !strcmp (s,"STORE")) &&
+ msgno && (msgno <= stream->nmsgs)) {
+ char *prop;
+ GETS_DATA md;
+ ENVELOPE **e;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ ENVELOPE *env = NIL;
+ imapenvelope_t ie =
+ (imapenvelope_t) mail_parameters (stream,GET_IMAPENVELOPE,NIL);
+ ++t; /* skip past open parenthesis */
+ /* parse Lisp-form property list */
+ while (prop = (strtok_r (t," )",&r))) {
+ t = strtok_r (NIL,"\n",&r);
+ INIT_GETS (md,stream,elt->msgno,NIL,0,0);
+ e = NIL; /* not pointing at any envelope yet */
+ /* canonicalize property, parse it */
+ if (!strcmp (ucase (prop),"FLAGS")) imap_parse_flags (stream,elt,&t);
+ else if (!strcmp (prop,"INTERNALDATE") &&
+ (s = imap_parse_string (stream,&t,reply,NIL,NIL,LONGT))) {
+ if (!mail_parse_date (elt,s)) {
+ sprintf (LOCAL->tmp,"Bogus date: %.80s",(char *) s);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ /* slam in default so we don't try again */
+ mail_parse_date (elt,"01-Jan-1970 00:00:00 +0000");
+ }
+ fs_give ((void **) &s);
+ }
+ /* unique identifier */
+ else if (!strcmp (prop,"UID")) {
+ LOCAL->lastuid.uid = elt->private.uid = strtoul (t,(char **) &t,10);
+ LOCAL->lastuid.msgno = elt->msgno;
+ }
+ else if (!strcmp (prop,"ENVELOPE")) {
+ if (stream->scache) { /* short cache, flush old stuff */
+ mail_free_body (&stream->body);
+ stream->msgno = elt->msgno;
+ e = &stream->env; /* get pointer to envelope */
+ }
+ else e = &elt->private.msg.env;
+ imap_parse_envelope (stream,e,&t,reply);
+ }
+ else if (!strncmp (prop,"BODY",4)) {
+ if (!prop[4] || !strcmp (prop+4,"STRUCTURE")) {
+ BODY **body;
+ if (stream->scache){/* short cache, flush old stuff */
+ if (stream->msgno != msgno) {
+ mail_free_envelope (&stream->env);
+ sprintf (LOCAL->tmp,"Body received for %lu but current is %lu",
+ msgno,stream->msgno);
+ stream->msgno = msgno;
+ }
+ /* get pointer to body */
+ body = &stream->body;
+ }
+ else body = &elt->private.msg.body;
+ /* flush any prior body */
+ mail_free_body (body);
+ /* instantiate and parse a new body */
+ imap_parse_body_structure (stream,*body = mail_newbody(),&t,reply);
+ }
+
+ else if (prop[4] == '[') {
+ STRINGLIST *stl = NIL;
+ SIZEDTEXT text;
+ /* will want to return envelope data */
+ if (!strcmp (md.what = cpystr (prop + 5),"HEADER]") ||
+ !strcmp (md.what,"0]"))
+ e = stream->scache ? &stream->env : &elt->private.msg.env;
+ LOCAL->tmp[0] ='\0';/* no errors yet */
+ /* found end of section? */
+ if (!(s = strchr (md.what,']'))) {
+ /* skip leading nesting */
+ for (s = md.what; *s && (isdigit (*s) || (*s == '.')); s++);
+ /* better be one of these */
+ if (strncmp (s,"HEADER.FIELDS",13) &&
+ (!s[13] || strcmp (s+13,".NOT")))
+ sprintf (LOCAL->tmp,"Unterminated section: %.80s",md.what);
+ /* get list of headers */
+ else if (!(stl = imap_parse_stringlist (stream,&t,reply)))
+ sprintf (LOCAL->tmp,"Bogus header field list: %.80s",
+ (char *) t);
+ else if (*t != ']')
+ sprintf (LOCAL->tmp,"Unterminated header section: %.80s",
+ (char *) t);
+ /* point after the text */
+ else if (t = strchr (s = t,' ')) *t++ = '\0';
+ }
+ if (s && !LOCAL->tmp[0]) {
+ *s++ = '\0'; /* tie off section specifier */
+ if (*s == '<') { /* partial specifier? */
+ md.first = strtoul (s+1,(char **) &s,10) + 1;
+ if (*s++ != '>') /* make sure properly terminated */
+ sprintf (LOCAL->tmp,"Unterminated partial data: %.80s",
+ (char *) s-1);
+ }
+ if (!LOCAL->tmp[0] && *s)
+ sprintf (LOCAL->tmp,"Junk after section: %.80s",(char *) s);
+ }
+ if (LOCAL->tmp[0]) { /* got any errors? */
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ mail_free_stringlist (&stl);
+ }
+ else { /* parse text from server */
+ text.data = (unsigned char *)
+ imap_parse_string (stream,&t,reply,
+ ((md.what[0] && (md.what[0] != 'H')) ||
+ md.first || md.last) ? &md : NIL,
+ &text.size,NIL);
+ /* all done if partial */
+ if (md.first || md.last) mail_free_stringlist (&stl);
+ /* otherwise register it in the cache */
+ else imap_cache (stream,msgno,md.what,stl,&text);
+ }
+ fs_give ((void **) &md.what);
+ }
+ else {
+ sprintf (LOCAL->tmp,"Unknown body message property: %.80s",prop);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ }
+
+ /* one of the RFC822 props? */
+ else if (!strncmp (prop,"RFC822",6) && (!prop[6] || (prop[6] == '.'))){
+ SIZEDTEXT text;
+ if (!prop[6]) { /* cache full message */
+ md.what = "";
+ text.data = (unsigned char *)
+ imap_parse_string (stream,&t,reply,&md,&text.size,NIL);
+ imap_cache (stream,msgno,md.what,NIL,&text);
+ }
+ else if (!strcmp (prop+7,"SIZE"))
+ elt->rfc822_size = strtoul (t,(char **) &t,10);
+ /* legacy properties */
+ else if (!strcmp (prop+7,"HEADER")) {
+ text.data = (unsigned char *)
+ imap_parse_string (stream,&t,reply,NIL,&text.size,NIL);
+ imap_cache (stream,msgno,"HEADER",NIL,&text);
+ e = stream->scache ? &stream->env : &elt->private.msg.env;
+ }
+ else if (!strcmp (prop+7,"TEXT")) {
+ md.what = "TEXT";
+ text.data = (unsigned char *)
+ imap_parse_string (stream,&t,reply,&md,&text.size,NIL);
+ imap_cache (stream,msgno,md.what,NIL,&text);
+ }
+ else {
+ sprintf (LOCAL->tmp,"Unknown RFC822 message property: %.80s",prop);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ }
+ else {
+ sprintf (LOCAL->tmp,"Unknown message property: %.80s",prop);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ if (e && *e) env = *e; /* note envelope if we got one */
+ }
+ /* do callback if requested */
+ if (ie && env) (*ie) (stream,msgno,env);
+ }
+ /* obsolete response to COPY */
+ else if (strcmp (s,"COPY")) {
+ sprintf (LOCAL->tmp,"Unknown message data: %lu %.80s",msgno,(char *) s);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ }
+
+ else if (!strcmp (reply->key,"FLAGS") && reply->text &&
+ (*reply->text == '(') &&
+ (s = strtok_r (reply->text+1," )",&r)))
+ do if (*s != '\\') {
+ for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i] &&
+ compare_cstring (s,stream->user_flags[i]); i++);
+ if (i > NUSERFLAGS) {
+ sprintf (LOCAL->tmp,"Too many server flags, discarding: %.80s",
+ (char *) s);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ }
+ else if (!stream->user_flags[i]) stream->user_flags[i++] = cpystr (s);
+ }
+ while (s = strtok_r (NIL," )",&r));
+ else if (!strcmp (reply->key,"SEARCH")) {
+ /* only do something if have text */
+ if (reply->text && (t = strtok_r (reply->text," ",&r))) do
+ if (i = strtoul (t,NIL,10)) {
+ /* UIDs always passed to main program */
+ if (LOCAL->uidsearch) mm_searched (stream,i);
+ /* should be a msgno then */
+ else if ((i <= stream->nmsgs) &&
+ (!LOCAL->filter || mail_elt (stream,i)->private.filter)) {
+ mail_elt (stream,i)->searched = T;
+ if (!stream->silent) mm_searched (stream,i);
+ }
+ } while (t = strtok_r (NIL," ",&r));
+ }
+ else if (!strcmp (reply->key,"SORT")) {
+ sortresults_t sr = (sortresults_t)
+ mail_parameters (NIL,GET_SORTRESULTS,NIL);
+ LOCAL->sortsize = 0; /* initialize sort data */
+ if (LOCAL->sortdata) fs_give ((void **) &LOCAL->sortdata);
+ LOCAL->sortdata = (unsigned long *)
+ fs_get ((stream->nmsgs + 1) * sizeof (unsigned long));
+ /* only do something if have text */
+ if (reply->text && (t = strtok_r (reply->text," ",&r))) {
+ do if ((i = atol (t)) && (LOCAL->filter ?
+ mail_elt (stream,i)->searched : T))
+ LOCAL->sortdata[LOCAL->sortsize++] = i;
+ while ((t = strtok_r (NIL," ",&r)) && (LOCAL->sortsize < stream->nmsgs));
+ }
+ LOCAL->sortdata[LOCAL->sortsize] = 0;
+ /* also return via callback if requested */
+ if (sr) (*sr) (stream,LOCAL->sortdata,LOCAL->sortsize);
+ }
+ else if (!strcmp (reply->key,"THREAD")) {
+ threadresults_t tr = (threadresults_t)
+ mail_parameters (NIL,GET_THREADRESULTS,NIL);
+ if (LOCAL->threaddata) mail_free_threadnode (&LOCAL->threaddata);
+ if (s = reply->text) {
+ LOCAL->threaddata = imap_parse_thread (stream,&s);
+ if (tr) (*tr) (stream,LOCAL->threaddata);
+ if (s && *s) {
+ sprintf (LOCAL->tmp,"Junk at end of thread: %.80s",(char *) s);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ }
+ }
+
+ else if (!strcmp (reply->key,"STATUS") && reply->text) {
+ MAILSTATUS status;
+ unsigned char *txt = reply->text;
+ if ((t = imap_parse_astring (stream,&txt,reply,&j)) && txt &&
+ (*txt++ == ' ') && (*txt++ == '(') && (s = strchr (txt,')')) &&
+ (s - txt) && !s[1]) {
+ *s = '\0'; /* tie off status data */
+ /* initialize data block */
+ status.flags = status.messages = status.recent = status.unseen =
+ status.uidnext = status.uidvalidity = 0;
+ while (*txt && (s = strchr (txt,' '))) {
+ *s++ = '\0'; /* tie off status attribute name */
+ /* get attribute value */
+ i = strtoul (s,(char **) &s,10);
+ if (!compare_cstring (txt,"MESSAGES")) {
+ status.flags |= SA_MESSAGES;
+ status.messages = i;
+ }
+ else if (!compare_cstring (txt,"RECENT")) {
+ status.flags |= SA_RECENT;
+ status.recent = i;
+ }
+ else if (!compare_cstring (txt,"UNSEEN")) {
+ status.flags |= SA_UNSEEN;
+ status.unseen = i;
+ }
+ else if (!compare_cstring (txt,"UIDNEXT")) {
+ status.flags |= SA_UIDNEXT;
+ status.uidnext = i;
+ }
+ else if (!compare_cstring (txt,"UIDVALIDITY")) {
+ status.flags |= SA_UIDVALIDITY;
+ status.uidvalidity = i;
+ }
+ /* next attribute */
+ txt = (*s == ' ') ? s + 1 : s;
+ }
+ if (((i = 1 + strchr (stream->mailbox,'}') - stream->mailbox) + j) <
+ IMAPTMPLEN) {
+ strcpy (strncpy (LOCAL->tmp,stream->mailbox,i) + i,t);
+ /* pass status to main program */
+ mm_status (stream,LOCAL->tmp,&status);
+ }
+ }
+ if (t) fs_give ((void **) &t);
+ }
+
+ else if ((!strcmp (reply->key,"LIST") || !strcmp (reply->key,"LSUB")) &&
+ reply->text && (*reply->text == '(') &&
+ (s = strchr (reply->text,')')) && (s[1] == ' ')) {
+ char delimiter = '\0';
+ *s++ = '\0'; /* tie off attribute list */
+ /* parse attribute list */
+ if (t = strtok_r (reply->text+1," ",&r)) do {
+ if (!compare_cstring (t,"\\NoInferiors")) i |= LATT_NOINFERIORS;
+ else if (!compare_cstring (t,"\\NoSelect")) i |= LATT_NOSELECT;
+ else if (!compare_cstring (t,"\\Marked")) i |= LATT_MARKED;
+ else if (!compare_cstring (t,"\\Unmarked")) i |= LATT_UNMARKED;
+ else if (!compare_cstring (t,"\\HasChildren")) i |= LATT_HASCHILDREN;
+ else if (!compare_cstring (t,"\\HasNoChildren")) i |= LATT_HASNOCHILDREN;
+ /* ignore extension flags */
+ }
+ while (t = strtok_r (NIL," ",&r));
+ switch (*++s) { /* process delimiter */
+ case 'N': /* NIL */
+ case 'n':
+ s += 4; /* skip over NIL<space> */
+ break;
+ case '"': /* have a delimiter */
+ delimiter = (*++s == '\\') ? *++s : *s;
+ s += 3; /* skip over <delimiter><quote><space> */
+ }
+ /* parse the mailbox name */
+ if (t = imap_parse_astring (stream,&s,reply,&j)) {
+ /* prepend prefix if requested */
+ if (LOCAL->prefix && ((strlen (LOCAL->prefix) + j) < IMAPTMPLEN))
+ sprintf (s = LOCAL->tmp,"%s%s",LOCAL->prefix,(char *) t);
+ else s = t; /* otherwise just mailbox name */
+ /* pass data to main program */
+ if (reply->key[1] == 'S') mm_lsub (stream,delimiter,s,i);
+ else mm_list (stream,delimiter,s,i);
+ fs_give ((void **) &t); /* flush mailbox name */
+ }
+ }
+ else if (!strcmp (reply->key,"NAMESPACE")) {
+ if (LOCAL->namespace) {
+ mail_free_namespace (&LOCAL->namespace[0]);
+ mail_free_namespace (&LOCAL->namespace[1]);
+ mail_free_namespace (&LOCAL->namespace[2]);
+ }
+ else LOCAL->namespace = (NAMESPACE **) fs_get (3 * sizeof (NAMESPACE *));
+ if (s = reply->text) { /* parse namespace results */
+ LOCAL->namespace[0] = imap_parse_namespace (stream,&s,reply);
+ LOCAL->namespace[1] = imap_parse_namespace (stream,&s,reply);
+ LOCAL->namespace[2] = imap_parse_namespace (stream,&s,reply);
+ if (s && *s) {
+ sprintf (LOCAL->tmp,"Junk after namespace list: %.80s",(char *) s);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ }
+ else {
+ mm_notify (stream,"Missing namespace list",WARN);
+ stream->unhealthy = T;
+ }
+ }
+
+ else if (!strcmp (reply->key,"ACL") && (s = reply->text) &&
+ (t = imap_parse_astring (stream,&s,reply,NIL))) {
+ getacl_t ar = (getacl_t) mail_parameters (NIL,GET_ACL,NIL);
+ if (s && (*s++ == ' ')) {
+ ACLLIST *al = mail_newacllist ();
+ ACLLIST *ac = al;
+ do if ((ac->identifier = imap_parse_astring (stream,&s,reply,NIL)) &&
+ s && (*s++ == ' '))
+ ac->rights = imap_parse_astring (stream,&s,reply,NIL);
+ while (ac->rights && s && (*s == ' ') && s++ &&
+ (ac = ac->next = mail_newacllist ()));
+ if (!ac->rights || (s && *s)) {
+ sprintf (LOCAL->tmp,"Invalid ACL identifer/rights for %.80s",
+ (char *) t);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ else if (ar) (*ar) (stream,t,al);
+ mail_free_acllist (&al); /* clean up */
+ }
+ /* no optional rights */
+ else if (ar) (*ar) (stream,t,NIL);
+ fs_give ((void **) &t); /* free mailbox name */
+ }
+
+ else if (!strcmp (reply->key,"LISTRIGHTS") && (s = reply->text) &&
+ (t = imap_parse_astring (stream,&s,reply,NIL))) {
+ listrights_t lr = (listrights_t) mail_parameters (NIL,GET_LISTRIGHTS,NIL);
+ char *id,*r;
+ if (s && (*s++ == ' ') && (id = imap_parse_astring (stream,&s,reply,NIL))){
+ if (s && (*s++ == ' ') &&
+ (r = imap_parse_astring (stream,&s,reply,NIL))) {
+ if (s && (*s++ == ' ')) {
+ STRINGLIST *rl = mail_newstringlist ();
+ STRINGLIST *rc = rl;
+ do rc->text.data = (unsigned char *)
+ imap_parse_astring (stream,&s,reply,&rc->text.size);
+ while (rc->text.data && s && (*s == ' ') && s++ &&
+ (rc = rc->next = mail_newstringlist ()));
+ if (!rc->text.data || (s && *s)) {
+ sprintf (LOCAL->tmp,"Invalid optional LISTRIGHTS for %.80s",
+ (char *) t);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ else if (lr) (*lr) (stream,t,id,r,rl);
+ /* clean up */
+ mail_free_stringlist (&rl);
+ }
+ /* no optional rights */
+ else if (lr) (*lr) (stream,t,id,r,NIL);
+ fs_give ((void **) &r); /* free rights */
+ }
+ else {
+ sprintf (LOCAL->tmp,"Missing LISTRIGHTS rights for %.80s",(char *) t);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ fs_give ((void **) &id); /* free identifier */
+ }
+ else {
+ sprintf (LOCAL->tmp,"Missing LISTRIGHTS identifer for %.80s",(char *) t);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ fs_give ((void **) &t); /* free mailbox name */
+ }
+
+ else if (!strcmp (reply->key,"MYRIGHTS") && (s = reply->text) &&
+ (t = imap_parse_astring (stream,&s,reply,NIL))) {
+ myrights_t mr = (myrights_t) mail_parameters (NIL,GET_MYRIGHTS,NIL);
+ char *r;
+ if (s && (*s++ == ' ') && (r = imap_parse_astring (stream,&s,reply,NIL))) {
+ if (s && *s) {
+ sprintf (LOCAL->tmp,"Junk after MYRIGHTS for %.80s",(char *) t);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ else if (mr) (*mr) (stream,t,r);
+ fs_give ((void **) &r); /* free rights */
+ }
+ else {
+ sprintf (LOCAL->tmp,"Missing MYRIGHTS for %.80s",(char *) t);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ fs_give ((void **) &t); /* free mailbox name */
+ }
+
+ /* this response has a bizarre syntax! */
+ else if (!strcmp (reply->key,"QUOTA") && (s = reply->text) &&
+ (t = imap_parse_astring (stream,&s,reply,NIL))) {
+ /* in case error */
+ sprintf (LOCAL->tmp,"Bad quota resource list for %.80s",(char *) t);
+ if (s && (*s++ == ' ') && (*s++ == '(') && *s && ((*s != ')') || !s[1])) {
+ quota_t qt = (quota_t) mail_parameters (NIL,GET_QUOTA,NIL);
+ QUOTALIST *ql = NIL;
+ QUOTALIST *qc;
+ /* parse non-empty quota resource list */
+ if (*s != ')') for (ql = qc = mail_newquotalist (); T;
+ qc = qc->next = mail_newquotalist ()) {
+ if ((qc->name = imap_parse_astring (stream,&s,reply,NIL)) && s &&
+ (*s++ == ' ') && (isdigit (*s) || (LOCAL->loser && (*s == '-')))) {
+ if (isdigit (*s)) qc->usage = strtoul (s,(char **) &s,10);
+ else if (t = strchr (s,' ')) t = s;
+ if ((*s++ == ' ') && (isdigit (*s) || (LOCAL->loser &&(*s == '-')))){
+ if (isdigit (*s)) qc->limit = strtoul (s,(char **) &s,10);
+ else if (t = strpbrk (s," )")) t = s;
+ /* another resource follows? */
+ if (*s == ' ') continue;
+ /* end of resource list? */
+ if ((*s == ')') && !s[1]) {
+ if (qt) (*qt) (stream,t,ql);
+ break; /* all done */
+ }
+ }
+ }
+ /* something bad happened */
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ break; /* parse failed */
+ }
+ /* all done with quota resource list now */
+ if (ql) mail_free_quotalist (&ql);
+ }
+ else {
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ fs_give ((void **) &t); /* free root name */
+ }
+ else if (!strcmp (reply->key,"QUOTAROOT") && (s = reply->text) &&
+ (t = imap_parse_astring (stream,&s,reply,NIL))) {
+ sprintf (LOCAL->tmp,"Bad quota root list for %.80s",(char *) t);
+ if (s && (*s++ == ' ')) {
+ quotaroot_t qr = (quotaroot_t) mail_parameters (NIL,GET_QUOTAROOT,NIL);
+ STRINGLIST *rl = mail_newstringlist ();
+ STRINGLIST *rc = rl;
+ do rc->text.data = (unsigned char *)
+ imap_parse_astring (stream,&s,reply,&rc->text.size);
+ while (rc->text.data && *s && (*s++ == ' ') &&
+ (rc = rc->next = mail_newstringlist ()));
+ if (!rc->text.data || (s && *s)) {
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ else if (qr) (*qr) (stream,t,rl);
+ /* clean up */
+ mail_free_stringlist (&rl);
+ }
+ else {
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ fs_give ((void **) &t);
+ }
+
+ else if (!strcmp (reply->key,"OK") || !strcmp (reply->key,"PREAUTH"))
+ imap_parse_response (stream,reply->text,NIL,T);
+ else if (!strcmp (reply->key,"NO"))
+ imap_parse_response (stream,reply->text,WARN,T);
+ else if (!strcmp (reply->key,"BAD"))
+ imap_parse_response (stream,reply->text,ERROR,T);
+ else if (!strcmp (reply->key,"BYE")) {
+ LOCAL->byeseen = T; /* note that a BYE seen */
+ imap_parse_response (stream,reply->text,BYE,T);
+ }
+ else if (!strcmp (reply->key,"CAPABILITY") && reply->text)
+ imap_parse_capabilities (stream,reply->text);
+ else if (!strcmp (reply->key,"MAILBOX") && reply->text) {
+ if (LOCAL->prefix &&
+ ((strlen (LOCAL->prefix) + strlen (reply->text)) < IMAPTMPLEN))
+ sprintf (t = LOCAL->tmp,"%s%s",LOCAL->prefix,(char *) reply->text);
+ else t = reply->text;
+ mm_list (stream,NIL,t,NIL);
+ }
+ else {
+ sprintf (LOCAL->tmp,"Unexpected untagged message: %.80s",
+ (char *) reply->key);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+}
+
+/* Parse human-readable response text
+ * Accepts: mail stream
+ * text
+ * error level for mm_notify()
+ * non-NIL if want mm_notify() event even if no response code
+ */
+
+void imap_parse_response (MAILSTREAM *stream,char *text,long errflg,long ntfy)
+{
+ char *s,*t,*r;
+ size_t i;
+ unsigned long j;
+ MESSAGECACHE *elt;
+ copyuid_t cu;
+ appenduid_t au;
+ SEARCHSET *source = NIL;
+ SEARCHSET *dest = NIL;
+ if (text && (*text == '[') && (t = strchr (s = text + 1,']')) &&
+ ((i = t - s) < IMAPTMPLEN)) {
+ LOCAL->tmp[i] = '\0'; /* make mungable copy of text code */
+ if (s = strchr (strncpy (t = LOCAL->tmp,s,i),' ')) *s++ = '\0';
+ if (s) { /* have argument? */
+ ntfy = NIL; /* suppress mm_notify if normal SELECT data */
+ if (!compare_cstring (t,"UIDVALIDITY") &&
+ ((j = strtoul (s,NIL,10)) != stream->uid_validity)) {
+ mailcache_t mc = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL);
+ stream->uid_validity = j;
+ /* purge any UIDs in cache */
+ for (j = 1; j <= stream->nmsgs; j++)
+ if (elt = (MESSAGECACHE *) (*mc) (stream,j,CH_ELT))
+ elt->private.uid = 0;
+ }
+ else if (!compare_cstring (t,"UIDNEXT"))
+ stream->uid_last = strtoul (s,NIL,10) - 1;
+ else if (!compare_cstring (t,"PERMANENTFLAGS") && (*s == '(') &&
+ (t[i-1] == ')')) {
+ t[i-1] = '\0'; /* tie off flags */
+ stream->perm_seen = stream->perm_deleted = stream->perm_answered =
+ stream->perm_draft = stream->kwd_create = NIL;
+ stream->perm_user_flags = NIL;
+ if (s = strtok_r (s+1," ",&r)) do {
+ if (*s == '\\') { /* system flags */
+ if (!compare_cstring (s,"\\Seen")) stream->perm_seen = T;
+ else if (!compare_cstring (s,"\\Deleted"))
+ stream->perm_deleted = T;
+ else if (!compare_cstring (s,"\\Flagged"))
+ stream->perm_flagged = T;
+ else if (!compare_cstring (s,"\\Answered"))
+ stream->perm_answered = T;
+ else if (!compare_cstring (s,"\\Draft")) stream->perm_draft = T;
+ else if (!strcmp (s,"\\*")) stream->kwd_create = T;
+ }
+ else stream->perm_user_flags |= imap_parse_user_flag (stream,s);
+ }
+ while (s = strtok_r (NIL," ",&r));
+ }
+
+ else if (!compare_cstring (t,"CAPABILITY"))
+ imap_parse_capabilities (stream,s);
+ else if ((j = LEVELUIDPLUS (stream) && LOCAL->appendmailbox) &&
+ !compare_cstring (t,"COPYUID") &&
+ (cu = (copyuid_t) mail_parameters (NIL,GET_COPYUID,NIL)) &&
+ isdigit (*s) && (j = strtoul (s,&s,10)) && (*s++ == ' ') &&
+ (source = mail_parse_set (s,&s)) && (*s++ == ' ') &&
+ (dest = mail_parse_set (s,&s)) && !*s)
+ (*cu) (stream,LOCAL->appendmailbox,j,source,dest);
+ else if (j && !compare_cstring (t,"APPENDUID") &&
+ (au = (appenduid_t) mail_parameters (NIL,GET_APPENDUID,NIL)) &&
+ isdigit (*s) && (j = strtoul (s,&s,10)) && (*s++ == ' ') &&
+ (dest = mail_parse_set (s,&s)) && !*s)
+ (*au) (LOCAL->appendmailbox,j,dest);
+ else { /* all other response code events */
+ ntfy = T; /* must mm_notify() */
+ if (!compare_cstring (t,"REFERRAL"))
+ LOCAL->referral = cpystr (t + 9);
+ }
+ mail_free_searchset (&source);
+ mail_free_searchset (&dest);
+ }
+ else { /* no arguments */
+ if (!compare_cstring (t,"UIDNOTSTICKY")) {
+ ntfy = NIL;
+ stream->uid_nosticky = T;
+ }
+ else if (!compare_cstring (t,"READ-ONLY")) stream->rdonly = T;
+ else if (!compare_cstring (t,"READ-WRITE"))
+ stream->rdonly = NIL;
+ else if (!compare_cstring (t,"PARSE") && !errflg)
+ errflg = PARSE;
+ }
+ }
+ /* give event to main program */
+ if (ntfy && !stream->silent) mm_notify (stream,text ? text : "",errflg);
+}
+
+/* Parse a namespace
+ * Accepts: mail stream
+ * current text pointer
+ * parsed reply
+ * Returns: namespace list, text pointer updated
+ */
+
+NAMESPACE *imap_parse_namespace (MAILSTREAM *stream,unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply)
+{
+ NAMESPACE *ret = NIL;
+ NAMESPACE *nam = NIL;
+ NAMESPACE *prev = NIL;
+ PARAMETER *par = NIL;
+ if (*txtptr) { /* only if argument given */
+ /* ignore leading space */
+ while (**txtptr == ' ') ++*txtptr;
+ switch (**txtptr) {
+ case 'N': /* if NIL */
+ case 'n':
+ ++*txtptr; /* bump past "N" */
+ ++*txtptr; /* bump past "I" */
+ ++*txtptr; /* bump past "L" */
+ break;
+ case '(':
+ ++*txtptr; /* skip past open paren */
+ while (**txtptr == '(') {
+ ++*txtptr; /* skip past open paren */
+ prev = nam; /* note previous if any */
+ nam = (NAMESPACE *) memset (fs_get (sizeof (NAMESPACE)),0,
+ sizeof (NAMESPACE));
+ if (!ret) ret = nam; /* if first time note first namespace */
+ /* if previous link new block to it */
+ if (prev) prev->next = nam;
+ nam->name = imap_parse_string (stream,txtptr,reply,NIL,NIL,NIL);
+ /* ignore whitespace */
+ while (**txtptr == ' ') ++*txtptr;
+ switch (**txtptr) { /* parse delimiter */
+ case 'N':
+ case 'n':
+ *txtptr += 3; /* bump past "NIL" */
+ break;
+ case '"':
+ if (*++*txtptr == '\\') nam->delimiter = *++*txtptr;
+ else nam->delimiter = **txtptr;
+ *txtptr += 2; /* bump past character and closing quote */
+ break;
+ default:
+ sprintf (LOCAL->tmp,"Missing delimiter in namespace: %.80s",
+ (char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ *txtptr = NIL; /* stop parse */
+ return ret;
+ }
+
+ while (**txtptr == ' '){/* append new parameter to tail */
+ if (nam->param) par = par->next = mail_newbody_parameter ();
+ else nam->param = par = mail_newbody_parameter ();
+ if (!(par->attribute = imap_parse_string (stream,txtptr,reply,NIL,
+ NIL,NIL))) {
+ mm_notify (stream,"Missing namespace extension attribute",WARN);
+ stream->unhealthy = T;
+ par->attribute = cpystr ("UNKNOWN");
+ }
+ /* skip space */
+ while (**txtptr == ' ') ++*txtptr;
+ if (**txtptr == '(') {/* have value list? */
+ char *att = par->attribute;
+ ++*txtptr; /* yes */
+ do { /* parse each value */
+ if (!(par->value = imap_parse_string (stream,txtptr,reply,NIL,
+ NIL,LONGT))) {
+ sprintf (LOCAL->tmp,
+ "Missing value for namespace attribute %.80s",att);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ par->value = cpystr ("UNKNOWN");
+ }
+ /* is there another value? */
+ if (**txtptr == ' ') par = par->next = mail_newbody_parameter ();
+ } while (!par->value);
+ }
+ else {
+ sprintf (LOCAL->tmp,"Missing values for namespace attribute %.80s",
+ par->attribute);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ par->value = cpystr ("UNKNOWN");
+ }
+ }
+ if (**txtptr == ')') ++*txtptr;
+ else { /* missing trailing paren */
+ sprintf (LOCAL->tmp,"Junk at end of namespace: %.80s",
+ (char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ return ret;
+ }
+ }
+ if (**txtptr == ')') { /* expected trailing paren? */
+ ++*txtptr; /* got it! */
+ break;
+ }
+ default:
+ sprintf (LOCAL->tmp,"Not a namespace: %.80s",(char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ *txtptr = NIL; /* stop parse now */
+ break;
+ }
+ }
+ return ret;
+}
+
+/* Parse a thread node list
+ * Accepts: mail stream
+ * current text pointer
+ * Returns: thread node list, text pointer updated
+ */
+
+THREADNODE *imap_parse_thread (MAILSTREAM *stream,unsigned char **txtptr)
+{
+ char *s;
+ THREADNODE *ret = NIL; /* returned tree */
+ THREADNODE *last = NIL; /* last branch in this tree */
+ THREADNODE *parent = NIL; /* parent of current node */
+ THREADNODE *cur; /* current node */
+ while (**txtptr == '(') { /* see a thread? */
+ ++*txtptr; /* skip past open paren */
+ while (**txtptr != ')') { /* parse thread */
+ if (**txtptr == '(') { /* thread branch */
+ cur = imap_parse_thread (stream,txtptr);
+ /* add to parent */
+ if (parent) parent = parent->next = cur;
+ else { /* no parent, create dummy */
+ if (last) last = last->branch = mail_newthreadnode (NIL);
+ /* new tree */
+ else ret = last = mail_newthreadnode (NIL);
+ /* add to dummy parent */
+ last->next = parent = cur;
+ }
+ }
+ /* threaded message number */
+ else if (isdigit (*(s = *txtptr)) &&
+ ((cur = mail_newthreadnode (NIL))->num =
+ strtoul (*txtptr,(char **) txtptr,10))) {
+ if (LOCAL->filter && !mail_elt (stream,cur->num)->searched)
+ cur->num = NIL; /* make dummy if filtering and not searched */
+ /* add to parent */
+ if (parent) parent = parent->next = cur;
+ /* no parent, start new thread */
+ else if (last) last = last->branch = parent = cur;
+ /* create new tree */
+ else ret = last = parent = cur;
+ }
+ else { /* anything else is a bogon */
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Bogus thread member: %.80s",s);
+ mm_notify (stream,tmp,WARN);
+ stream->unhealthy = T;
+ return ret;
+ }
+ /* skip past any space */
+ if (**txtptr == ' ') ++*txtptr;
+ }
+ ++*txtptr; /* skip pase end of thread */
+ parent = NIL; /* close this thread */
+ }
+ return ret; /* return parsed thread */
+}
+
+/* Parse RFC822 message header
+ * Accepts: MAIL stream
+ * envelope to parse into
+ * header as sized text
+ * stringlist if partial header
+ */
+
+void imap_parse_header (MAILSTREAM *stream,ENVELOPE **env,SIZEDTEXT *hdr,
+ STRINGLIST *stl)
+{
+ ENVELOPE *nenv;
+ /* parse what we can from this header */
+ rfc822_parse_msg (&nenv,NIL,(char *) hdr->data,hdr->size,NIL,
+ net_host (LOCAL->netstream),stream->dtb->flags);
+ if (*env) { /* need to merge this header into envelope? */
+ if (!(*env)->newsgroups) { /* need Newsgroups? */
+ (*env)->newsgroups = nenv->newsgroups;
+ (*env)->ngpathexists = nenv->ngpathexists;
+ nenv->newsgroups = NIL;
+ }
+ if (!(*env)->followup_to) { /* need Followup-To? */
+ (*env)->followup_to = nenv->followup_to;
+ nenv->followup_to = NIL;
+ }
+ if (!(*env)->references) { /* need References? */
+ (*env)->references = nenv->references;
+ nenv->references = NIL;
+ }
+ if (!(*env)->sparep) { /* need spare pointer? */
+ (*env)->sparep = nenv->sparep;
+ nenv->sparep = NIL;
+ }
+ mail_free_envelope (&nenv);
+ (*env)->imapenvonly = NIL; /* have complete envelope now */
+ }
+ /* otherwise set it to this envelope */
+ else (*env = nenv)->incomplete = stl ? T : NIL;
+}
+
+/* IMAP parse envelope
+ * Accepts: MAIL stream
+ * pointer to envelope pointer
+ * current text pointer
+ * parsed reply
+ *
+ * Updates text pointer
+ */
+
+void imap_parse_envelope (MAILSTREAM *stream,ENVELOPE **env,
+ unsigned char **txtptr,IMAPPARSEDREPLY *reply)
+{
+ ENVELOPE *oenv = *env;
+ char c = *((*txtptr)++); /* grab first character */
+ /* ignore leading spaces */
+ while (c == ' ') c = *((*txtptr)++);
+ switch (c) { /* dispatch on first character */
+ case '(': /* if envelope S-expression */
+ *env = mail_newenvelope (); /* parse the new envelope */
+ (*env)->date = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT);
+ (*env)->subject = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT);
+ (*env)->from = imap_parse_adrlist (stream,txtptr,reply);
+ (*env)->sender = imap_parse_adrlist (stream,txtptr,reply);
+ (*env)->reply_to = imap_parse_adrlist (stream,txtptr,reply);
+ (*env)->to = imap_parse_adrlist (stream,txtptr,reply);
+ (*env)->cc = imap_parse_adrlist (stream,txtptr,reply);
+ (*env)->bcc = imap_parse_adrlist (stream,txtptr,reply);
+ (*env)->in_reply_to = imap_parse_string (stream,txtptr,reply,NIL,NIL,
+ LONGT);
+ (*env)->message_id = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT);
+ if (oenv) { /* need to merge old envelope? */
+ (*env)->newsgroups = oenv->newsgroups;
+ oenv->newsgroups = NIL;
+ (*env)->ngpathexists = oenv->ngpathexists;
+ (*env)->followup_to = oenv->followup_to;
+ oenv->followup_to = NIL;
+ (*env)->references = oenv->references;
+ oenv->references = NIL;
+ mail_free_envelope(&oenv);/* free old envelope */
+ }
+ /* have IMAP envelope components only */
+ else (*env)->imapenvonly = T;
+ if (**txtptr != ')') {
+ sprintf (LOCAL->tmp,"Junk at end of envelope: %.80s",(char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ else ++*txtptr; /* skip past delimiter */
+ break;
+ case 'N': /* if NIL */
+ case 'n':
+ ++*txtptr; /* bump past "I" */
+ ++*txtptr; /* bump past "L" */
+ break;
+ default:
+ sprintf (LOCAL->tmp,"Not an envelope: %.80s",(char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ break;
+ }
+}
+
+/* IMAP parse address list
+ * Accepts: MAIL stream
+ * current text pointer
+ * parsed reply
+ * Returns: address list, NIL on failure
+ *
+ * Updates text pointer
+ */
+
+ADDRESS *imap_parse_adrlist (MAILSTREAM *stream,unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply)
+{
+ ADDRESS *adr = NIL;
+ char c = **txtptr; /* sniff at first character */
+ /* ignore leading spaces */
+ while (c == ' ') c = *++*txtptr;
+ ++*txtptr; /* skip past open paren */
+ switch (c) {
+ case '(': /* if envelope S-expression */
+ adr = imap_parse_address (stream,txtptr,reply);
+ if (**txtptr != ')') {
+ sprintf (LOCAL->tmp,"Junk at end of address list: %.80s",
+ (char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ else ++*txtptr; /* skip past delimiter */
+ break;
+ case 'N': /* if NIL */
+ case 'n':
+ ++*txtptr; /* bump past "I" */
+ ++*txtptr; /* bump past "L" */
+ break;
+ default:
+ sprintf (LOCAL->tmp,"Not an address: %.80s",(char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ break;
+ }
+ return adr;
+}
+
+/* IMAP parse address
+ * Accepts: MAIL stream
+ * current text pointer
+ * parsed reply
+ * Returns: address, NIL on failure
+ *
+ * Updates text pointer
+ */
+
+ADDRESS *imap_parse_address (MAILSTREAM *stream,unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply)
+{
+ long ingroup = 0;
+ ADDRESS *adr = NIL;
+ ADDRESS *ret = NIL;
+ ADDRESS *prev = NIL;
+ char c = **txtptr; /* sniff at first address character */
+ switch (c) {
+ case '(': /* if envelope S-expression */
+ while (c == '(') { /* recursion dies on small stack machines */
+ ++*txtptr; /* skip past open paren */
+ if (adr) prev = adr; /* note previous if any */
+ adr = mail_newaddr (); /* instantiate address and parse its fields */
+ adr->personal = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT);
+ adr->adl = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT);
+ adr->mailbox = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT);
+ adr->host = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT);
+ if (**txtptr != ')') { /* handle trailing paren */
+ sprintf (LOCAL->tmp,"Junk at end of address: %.80s",(char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ else ++*txtptr; /* skip past close paren */
+ c = **txtptr; /* set up for while test */
+ /* ignore leading spaces in front of next */
+ while (c == ' ') c = *++*txtptr;
+
+ if (!adr->mailbox) { /* end of group? */
+ /* decrement group if all looks well */
+ if (ingroup && !(adr->personal || adr->adl || adr->host)) --ingroup;
+ else {
+ if (ingroup) { /* in a group? */
+ sprintf (LOCAL->tmp,/* yes, must be bad syntax */
+ "Junk in end of group: pn=%.80s al=%.80s dn=%.80s",
+ adr->personal ? adr->personal : "",
+ adr->adl ? adr->adl : "",
+ adr->host ? adr->host : "");
+ mm_notify (stream,LOCAL->tmp,WARN);
+ }
+ else mm_notify (stream,"End of group encountered when not in group",
+ WARN);
+ stream->unhealthy = T;
+ mail_free_address (&adr);
+ adr = prev;
+ prev = NIL;
+ }
+ }
+ else if (!adr->host) { /* start of group? */
+ if (adr->personal || adr->adl) {
+ sprintf (LOCAL->tmp,"Junk in start of group: pn=%.80s al=%.80s",
+ adr->personal ? adr->personal : "",
+ adr->adl ? adr->adl : "");
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ mail_free_address (&adr);
+ adr = prev;
+ prev = NIL;
+ }
+ else ++ingroup; /* in a group now */
+ }
+ if (adr) { /* good address */
+ if (!ret) ret = adr; /* if first time note first adr */
+ /* if previous link new block to it */
+ if (prev) prev->next = adr;
+ /* flush bogus personal name */
+ if (LOCAL->loser && adr->personal && strchr (adr->personal,'@'))
+ fs_give ((void **) &adr->personal);
+ }
+ }
+ break;
+ case 'N': /* if NIL */
+ case 'n':
+ *txtptr += 3; /* bump past NIL */
+ break;
+ default:
+ sprintf (LOCAL->tmp,"Not an address: %.80s",(char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ break;
+ }
+ return ret;
+}
+
+/* IMAP parse flags
+ * Accepts: current message cache
+ * current text pointer
+ *
+ * Updates text pointer
+ */
+
+void imap_parse_flags (MAILSTREAM *stream,MESSAGECACHE *elt,
+ unsigned char **txtptr)
+{
+ char *flag;
+ char c = '\0';
+ struct { /* old flags */
+ unsigned int valid : 1;
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.valid = elt->valid; old.seen = elt->seen; old.deleted = elt->deleted;
+ old.flagged = elt->flagged; old.answered = elt->answered;
+ old.draft = elt->draft; old.user_flags = elt->user_flags;
+ elt->valid = T; /* mark have valid flags now */
+ elt->user_flags = NIL; /* zap old flag values */
+ elt->seen = elt->deleted = elt->flagged = elt->answered = elt->draft =
+ elt->recent = NIL;
+ while (c != ')') { /* parse list of flags */
+ /* point at a flag */
+ while (*(flag = ++*txtptr) == ' ');
+ /* scan for end of flag */
+ while (**txtptr != ' ' && **txtptr != ')') ++*txtptr;
+ c = **txtptr; /* save delimiter */
+ **txtptr = '\0'; /* tie off flag */
+ if (!*flag) break; /* null flag */
+ /* if starts with \ must be sys flag */
+ else if (*flag == '\\') {
+ if (!compare_cstring (flag,"\\Seen")) elt->seen = T;
+ else if (!compare_cstring (flag,"\\Deleted")) elt->deleted = T;
+ else if (!compare_cstring (flag,"\\Flagged")) elt->flagged = T;
+ else if (!compare_cstring (flag,"\\Answered")) elt->answered = T;
+ else if (!compare_cstring (flag,"\\Recent")) elt->recent = T;
+ else if (!compare_cstring (flag,"\\Draft")) elt->draft = T;
+ }
+ /* otherwise user flag */
+ else elt->user_flags |= imap_parse_user_flag (stream,flag);
+ }
+ ++*txtptr; /* bump past delimiter */
+ if (!old.valid || (old.seen != elt->seen) ||
+ (old.deleted != elt->deleted) || (old.flagged != elt->flagged) ||
+ (old.answered != elt->answered) || (old.draft != elt->draft) ||
+ (old.user_flags != elt->user_flags)) mm_flags (stream,elt->msgno);
+}
+
+
+/* IMAP parse user flag
+ * Accepts: MAIL stream
+ * flag name
+ * Returns: flag bit position
+ */
+
+unsigned long imap_parse_user_flag (MAILSTREAM *stream,char *flag)
+{
+ long i;
+ /* sniff through all user flags */
+ for (i = 0; i < NUSERFLAGS; ++i) if (stream->user_flags[i])
+ if (!compare_cstring (flag,stream->user_flags[i])) return (1 << i);
+ return (unsigned long) 0; /* not found */
+}
+
+/* IMAP parse atom-string
+ * Accepts: MAIL stream
+ * current text pointer
+ * parsed reply
+ * returned string length
+ * Returns: string
+ *
+ * Updates text pointer
+ */
+
+unsigned char *imap_parse_astring (MAILSTREAM *stream,unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply,unsigned long *len)
+{
+ unsigned long i;
+ unsigned char c,*s,*ret;
+ /* ignore leading spaces */
+ for (c = **txtptr; c == ' '; c = *++*txtptr);
+ switch (c) {
+ case '"': /* quoted string? */
+ case '{': /* literal? */
+ ret = imap_parse_string (stream,txtptr,reply,NIL,len,NIL);
+ break;
+ default: /* must be atom */
+ for (c = *(s = *txtptr); /* find end of atom */
+ c && (c > ' ') && (c != '(') && (c != ')') && (c != '{') &&
+ (c != '%') && (c != '*') && (c != '"') && (c != '\\') && (c < 0x80);
+ c = *++*txtptr);
+ if (i = *txtptr - s) { /* atom ends at atom_special */
+ if (len) *len = i; /* return length of atom */
+ ret = strncpy ((char *) fs_get (i + 1),s,i);
+ ret[i] = '\0'; /* tie off string */
+ }
+ else { /* no atom found */
+ sprintf (LOCAL->tmp,"Not an atom: %.80s",(char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ if (len) *len = 0;
+ ret = NIL;
+ }
+ break;
+ }
+ return ret;
+}
+
+/* IMAP parse string
+ * Accepts: MAIL stream
+ * current text pointer
+ * parsed reply
+ * mailgets data
+ * returned string length
+ * filter newline flag
+ * Returns: string
+ *
+ * Updates text pointer
+ */
+
+unsigned char *imap_parse_string (MAILSTREAM *stream,unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply,GETS_DATA *md,
+ unsigned long *len,long flags)
+{
+ char *st;
+ char *string = NIL;
+ unsigned long i,j,k;
+ int bogon = NIL;
+ unsigned char c = **txtptr; /* sniff at first character */
+ mailgets_t mg = (mailgets_t) mail_parameters (NIL,GET_GETS,NIL);
+ readprogress_t rp =
+ (readprogress_t) mail_parameters (NIL,GET_READPROGRESS,NIL);
+ /* ignore leading spaces */
+ while (c == ' ') c = *++*txtptr;
+ st = ++*txtptr; /* remember start of string */
+ switch (c) {
+ case '"': /* if quoted string */
+ i = 0; /* initial byte count */
+ /* search for end of string */
+ for (c = **txtptr; c != '"'; ++i,c = *++*txtptr) {
+ /* backslash quotes next character */
+ if (c == '\\') c = *++*txtptr;
+ /* CHAR8 not permitted in quoted string */
+ if (!bogon && (bogon = (c & 0x80))) {
+ sprintf (LOCAL->tmp,"Invalid CHAR in quoted string: %x",
+ (unsigned int) c);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ else if (!c) { /* NUL not permitted either */
+ mm_notify (stream,"Unterminated quoted string",WARN);
+ stream->unhealthy = T;
+ if (len) *len = 0; /* punt, since may be at end of string */
+ return NIL;
+ }
+ }
+ ++*txtptr; /* bump past delimiter */
+ string = (char *) fs_get ((size_t) i + 1);
+ for (j = 0; j < i; j++) { /* copy the string */
+ if (*st == '\\') ++st; /* quoted character */
+ string[j] = *st++;
+ }
+ string[j] = '\0'; /* tie off string */
+ if (len) *len = i; /* set return value too */
+ if (md && mg) { /* have special routine to slurp string? */
+ STRING bs;
+ if (md->first) { /* partial fetch? */
+ md->first--; /* restore origin octet */
+ md->last = i; /* number of octets that we got */
+ }
+ INIT (&bs,mail_string,string,i);
+ (*mg) (mail_read,&bs,i,md);
+ }
+ break;
+
+ case 'N': /* if NIL */
+ case 'n':
+ ++*txtptr; /* bump past "I" */
+ ++*txtptr; /* bump past "L" */
+ if (len) *len = 0;
+ break;
+ case '{': /* if literal string */
+ /* get size of string */
+ if ((i = strtoul (*txtptr,(char **) txtptr,10)) > MAXSERVERLIT) {
+ sprintf (LOCAL->tmp,"Absurd server literal length %lu",i);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T; /* read and discard */
+ do net_getbuffer (LOCAL->netstream,j = min (i,(long) IMAPTMPLEN - 1),
+ LOCAL->tmp);
+ while (i -= j);
+ }
+ if (len) *len = i; /* set return value */
+ if (md && mg) { /* have special routine to slurp string? */
+ if (md->first) { /* partial fetch? */
+ md->first--; /* restore origin octet */
+ md->last = i; /* number of octets that we got */
+ }
+ else md->flags |= MG_COPY;/* otherwise flag need to copy */
+ string = (*mg) (net_getbuffer,LOCAL->netstream,i,md);
+ }
+ else { /* must slurp into free storage */
+ string = (char *) fs_get ((size_t) i + 1);
+ *string = '\0'; /* init in case getbuffer fails */
+ /* get the literal */
+ if (rp) for (k = 0; j = min ((long) MAILTMPLEN,(long) i); i -= j) {
+ net_getbuffer (LOCAL->netstream,j,string + k);
+ (*rp) (md,k += j);
+ }
+ else net_getbuffer (LOCAL->netstream,i,string);
+ }
+ fs_give ((void **) &reply->line);
+ if (flags && string) /* need to filter newlines? */
+ for (st = string; st = strpbrk (st,"\015\012\011"); *st++ = ' ');
+ /* get new reply text line */
+ if (!(reply->line = net_getline (LOCAL->netstream)))
+ reply->line = cpystr ("");
+ if (stream->debug) mm_dlog (reply->line);
+ *txtptr = reply->line; /* set text pointer to point at it */
+ break;
+ default:
+ sprintf (LOCAL->tmp,"Not a string: %c%.80s",c,(char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ if (len) *len = 0;
+ break;
+ }
+ return (unsigned char *) string;
+}
+
+/* Register text in IMAP cache
+ * Accepts: MAIL stream
+ * message number
+ * IMAP segment specifier
+ * header string list (if a HEADER section specifier)
+ * sized text to register
+ * Returns: non-zero if cache non-empty
+ */
+
+long imap_cache (MAILSTREAM *stream,unsigned long msgno,char *seg,
+ STRINGLIST *stl,SIZEDTEXT *text)
+{
+ char *t,tmp[MAILTMPLEN];
+ unsigned long i;
+ BODY *b;
+ SIZEDTEXT *ret;
+ STRINGLIST *stc;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ /* top-level header never does mailgets */
+ if (!strcmp (seg,"HEADER") || !strcmp (seg,"0") ||
+ !strcmp (seg,"HEADER.FIELDS") || !strcmp (seg,"HEADER.FIELDS.NOT")) {
+ ret = &elt->private.msg.header.text;
+ if (text) { /* don't do this if no text */
+ if (ret->data) fs_give ((void **) &ret->data);
+ mail_free_stringlist (&elt->private.msg.lines);
+ elt->private.msg.lines = stl;
+ /* prevent cache reuse of .NOT */
+ if ((seg[0] == 'H') && (seg[6] == '.') && (seg[13] == '.'))
+ for (stc = stl; stc; stc = stc->next) stc->text.size = 0;
+ if (stream->scache) { /* short caching puts it in the stream */
+ if (stream->msgno != msgno) {
+ /* flush old stuff */
+ mail_free_envelope (&stream->env);
+ mail_free_body (&stream->body);
+ stream->msgno = msgno;
+ }
+ imap_parse_header (stream,&stream->env,text,stl);
+ }
+ /* regular caching */
+ else imap_parse_header (stream,&elt->private.msg.env,text,stl);
+ }
+ }
+ /* top level text */
+ else if (!strcmp (seg,"TEXT")) {
+ ret = &elt->private.msg.text.text;
+ if (text && ret->data) fs_give ((void **) &ret->data);
+ }
+ else if (!*seg) { /* full message */
+ ret = &elt->private.msg.full.text;
+ if (text && ret->data) fs_give ((void **) &ret->data);
+ }
+
+ else { /* nested, find non-contents specifier */
+ for (t = seg; *t && !((*t == '.') && (isalpha(t[1]) || !atol (t+1))); t++);
+ if (*t) *t++ = '\0'; /* tie off section from data specifier */
+ if (!(b = mail_body (stream,msgno,seg))) {
+ sprintf (tmp,"Unknown section number: %.80s",seg);
+ mm_notify (stream,tmp,WARN);
+ stream->unhealthy = T;
+ return NIL;
+ }
+ if (*t) { /* if a non-numberic subpart */
+ if ((i = (b->type == TYPEMESSAGE) && (!strcmp (b->subtype,"RFC822"))) &&
+ (!strcmp (t,"HEADER") || !strcmp (t,"0") ||
+ !strcmp (t,"HEADER.FIELDS") || !strcmp (t,"HEADER.FIELDS.NOT"))) {
+ ret = &b->nested.msg->header.text;
+ if (text) {
+ if (ret->data) fs_give ((void **) &ret->data);
+ mail_free_stringlist (&b->nested.msg->lines);
+ b->nested.msg->lines = stl;
+ /* prevent cache reuse of .NOT */
+ if ((t[0] == 'H') && (t[6] == '.') && (t[13] == '.'))
+ for (stc = stl; stc; stc = stc->next) stc->text.size = 0;
+ imap_parse_header (stream,&b->nested.msg->env,text,stl);
+ }
+ }
+ else if (i && !strcmp (t,"TEXT")) {
+ ret = &b->nested.msg->text.text;
+ if (text && ret->data) fs_give ((void **) &ret->data);
+ }
+ /* otherwise it must be MIME */
+ else if (!strcmp (t,"MIME")) {
+ ret = &b->mime.text;
+ if (text && ret->data) fs_give ((void **) &ret->data);
+ }
+ else {
+ sprintf (tmp,"Unknown section specifier: %.80s.%.80s",seg,t);
+ mm_notify (stream,tmp,WARN);
+ stream->unhealthy = T;
+ return NIL;
+ }
+ }
+ else { /* ordinary contents */
+ ret = &b->contents.text;
+ if (text && ret->data) fs_give ((void **) &ret->data);
+ }
+ }
+ if (text) { /* update cache if requested */
+ ret->data = text->data;
+ ret->size = text->size;
+ }
+ return ret->data ? LONGT : NIL;
+}
+
+/* IMAP parse body structure
+ * Accepts: MAIL stream
+ * body structure to write into
+ * current text pointer
+ * parsed reply
+ *
+ * Updates text pointer
+ */
+
+void imap_parse_body_structure (MAILSTREAM *stream,BODY *body,
+ unsigned char **txtptr,IMAPPARSEDREPLY *reply)
+{
+ int i;
+ char *s;
+ PART *part = NIL;
+ char c = *((*txtptr)++); /* grab first character */
+ /* ignore leading spaces */
+ while (c == ' ') c = *((*txtptr)++);
+ switch (c) { /* dispatch on first character */
+ case '(': /* body structure list */
+ if (**txtptr == '(') { /* multipart body? */
+ body->type= TYPEMULTIPART;/* yes, set its type */
+ do { /* instantiate new body part */
+ if (part) part = part->next = mail_newbody_part ();
+ else body->nested.part = part = mail_newbody_part ();
+ /* parse it */
+ imap_parse_body_structure (stream,&part->body,txtptr,reply);
+ } while (**txtptr == '(');/* for each body part */
+ if (body->subtype = imap_parse_string(stream,txtptr,reply,NIL,NIL,LONGT))
+ ucase (body->subtype);
+ else {
+ mm_notify (stream,"Missing multipart subtype",WARN);
+ stream->unhealthy = T;
+ body->subtype = cpystr (rfc822_default_subtype (body->type));
+ }
+ if (**txtptr == ' ') /* multipart parameters */
+ body->parameter = imap_parse_body_parameter (stream,txtptr,reply);
+ if (**txtptr == ' ') { /* disposition */
+ imap_parse_disposition (stream,body,txtptr,reply);
+ if (LOCAL->cap.extlevel < BODYEXTDSP) LOCAL->cap.extlevel = BODYEXTDSP;
+ }
+ if (**txtptr == ' ') { /* language */
+ body->language = imap_parse_language (stream,txtptr,reply);
+ if (LOCAL->cap.extlevel < BODYEXTLANG)
+ LOCAL->cap.extlevel = BODYEXTLANG;
+ }
+ if (**txtptr == ' ') { /* location */
+ body->location = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT);
+ if (LOCAL->cap.extlevel < BODYEXTLOC) LOCAL->cap.extlevel = BODYEXTLOC;
+ }
+ while (**txtptr == ' ') imap_parse_extension (stream,txtptr,reply);
+ if (**txtptr != ')') { /* validate ending */
+ sprintf (LOCAL->tmp,"Junk at end of multipart body: %.80s",
+ (char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ else ++*txtptr; /* skip past delimiter */
+ }
+
+ else { /* not multipart, parse type name */
+ if (**txtptr == ')') { /* empty body? */
+ ++*txtptr; /* bump past it */
+ break; /* and punt */
+ }
+ body->type = TYPEOTHER; /* assume unknown type */
+ body->encoding = ENCOTHER;/* and unknown encoding */
+ /* parse type */
+ if (s = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT)) {
+ ucase (s); /* application always gets uppercase form */
+ for (i = 0; /* look in existing table */
+ (i <= TYPEMAX) && body_types[i] && strcmp (s,body_types[i]); i++);
+ if (i <= TYPEMAX) { /* only if found a slot */
+ body->type = i; /* set body type */
+ if (body_types[i]) fs_give ((void **) &s);
+ else body_types[i]=s; /* assign empty slot */
+ }
+ }
+ if (body->subtype = imap_parse_string(stream,txtptr,reply,NIL,NIL,LONGT))
+ ucase (body->subtype); /* parse subtype */
+ else {
+ mm_notify (stream,"Missing body subtype",WARN);
+ stream->unhealthy = T;
+ body->subtype = cpystr (rfc822_default_subtype (body->type));
+ }
+ body->parameter = imap_parse_body_parameter (stream,txtptr,reply);
+ body->id = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT);
+ body->description = imap_parse_string (stream,txtptr,reply,NIL,NIL,
+ LONGT);
+ if (s = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT)) {
+ ucase (s); /* application always gets uppercase form */
+ for (i = 0; /* search for body encoding */
+ (i <= ENCMAX) && body_encodings[i] && strcmp(s,body_encodings[i]);
+ i++);
+ if (i > ENCMAX) body->encoding = ENCOTHER;
+ else { /* only if found a slot */
+ body->encoding = i; /* set body encoding */
+ if (body_encodings[i]) fs_give ((void **) &s);
+ /* assign empty slot */
+ else body_encodings[i] = s;
+ }
+ }
+ /* parse size of contents in bytes */
+ body->size.bytes = strtoul (*txtptr,(char **) txtptr,10);
+ switch (body->type) { /* possible extra stuff */
+ case TYPEMESSAGE: /* message envelope and body */
+ /* non MESSAGE/RFC822 is basic type */
+ if (strcmp (body->subtype,"RFC822")) break;
+ { /* make certain server sends an envelope */
+ ENVELOPE *env = NIL;
+ imap_parse_envelope (stream,&env,txtptr,reply);
+ if (!env) {
+ mm_notify (stream,"Missing body message envelope",WARN);
+ stream->unhealthy = T;
+ body->subtype = cpystr ("RFC822_MISSING_ENVELOPE");
+ break;
+ }
+ (body->nested.msg = mail_newmsg ())->env = env;
+ }
+ body->nested.msg->body = mail_newbody ();
+ imap_parse_body_structure (stream,body->nested.msg->body,txtptr,reply);
+ /* drop into text case */
+ case TYPETEXT: /* size in lines */
+ body->size.lines = strtoul (*txtptr,(char **) txtptr,10);
+ break;
+ default: /* otherwise nothing special */
+ break;
+ }
+
+ if (**txtptr == ' ') { /* extension data - md5 */
+ body->md5 = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT);
+ if (LOCAL->cap.extlevel < BODYEXTMD5) LOCAL->cap.extlevel = BODYEXTMD5;
+ }
+ if (**txtptr == ' ') { /* disposition */
+ imap_parse_disposition (stream,body,txtptr,reply);
+ if (LOCAL->cap.extlevel < BODYEXTDSP) LOCAL->cap.extlevel = BODYEXTDSP;
+ }
+ if (**txtptr == ' ') { /* language */
+ body->language = imap_parse_language (stream,txtptr,reply);
+ if (LOCAL->cap.extlevel < BODYEXTLANG)
+ LOCAL->cap.extlevel = BODYEXTLANG;
+ }
+ if (**txtptr == ' ') { /* location */
+ body->location = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT);
+ if (LOCAL->cap.extlevel < BODYEXTLOC) LOCAL->cap.extlevel = BODYEXTLOC;
+ }
+ while (**txtptr == ' ') imap_parse_extension (stream,txtptr,reply);
+ if (**txtptr != ')') { /* validate ending */
+ sprintf (LOCAL->tmp,"Junk at end of body part: %.80s",
+ (char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ else ++*txtptr; /* skip past delimiter */
+ }
+ break;
+ case 'N': /* if NIL */
+ case 'n':
+ ++*txtptr; /* bump past "I" */
+ ++*txtptr; /* bump past "L" */
+ break;
+ default: /* otherwise quite bogus */
+ sprintf (LOCAL->tmp,"Bogus body structure: %.80s",(char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ break;
+ }
+}
+
+/* IMAP parse body parameter
+ * Accepts: MAIL stream
+ * current text pointer
+ * parsed reply
+ * Returns: body parameter
+ * Updates text pointer
+ */
+
+PARAMETER *imap_parse_body_parameter (MAILSTREAM *stream,
+ unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply)
+{
+ PARAMETER *ret = NIL;
+ PARAMETER *par = NIL;
+ char c,*s;
+ /* ignore leading spaces */
+ while ((c = *(*txtptr)++) == ' ');
+ /* parse parameter list */
+ if (c == '(') while (c != ')') {
+ /* append new parameter to tail */
+ if (ret) par = par->next = mail_newbody_parameter ();
+ else ret = par = mail_newbody_parameter ();
+ if(!(par->attribute=imap_parse_string (stream,txtptr,reply,NIL,NIL,
+ LONGT))) {
+ mm_notify (stream,"Missing parameter attribute",WARN);
+ stream->unhealthy = T;
+ par->attribute = cpystr ("UNKNOWN");
+ }
+ if (!(par->value = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT))){
+ sprintf (LOCAL->tmp,"Missing value for parameter %.80s",par->attribute);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ par->value = cpystr ("UNKNOWN");
+ }
+ switch (c = **txtptr) { /* see what comes after */
+ case ' ': /* flush whitespace */
+ while ((c = *++*txtptr) == ' ');
+ break;
+ case ')': /* end of attribute/value pairs */
+ ++*txtptr; /* skip past closing paren */
+ break;
+ default:
+ sprintf (LOCAL->tmp,"Junk at end of parameter: %.80s",(char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ break;
+ }
+ }
+ /* empty parameter, must be NIL */
+ else if (((c == 'N') || (c == 'n')) &&
+ ((*(s = *txtptr) == 'I') || (*s == 'i')) &&
+ ((s[1] == 'L') || (s[1] == 'l'))) *txtptr += 2;
+ else {
+ sprintf (LOCAL->tmp,"Bogus body parameter: %c%.80s",c,
+ (char *) (*txtptr) - 1);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ return ret;
+}
+
+/* IMAP parse body disposition
+ * Accepts: MAIL stream
+ * body structure to write into
+ * current text pointer
+ * parsed reply
+ */
+
+void imap_parse_disposition (MAILSTREAM *stream,BODY *body,
+ unsigned char **txtptr,IMAPPARSEDREPLY *reply)
+{
+ switch (*++*txtptr) {
+ case '(':
+ ++*txtptr; /* skip open paren */
+ body->disposition.type = imap_parse_string (stream,txtptr,reply,NIL,NIL,
+ LONGT);
+ body->disposition.parameter =
+ imap_parse_body_parameter (stream,txtptr,reply);
+ if (**txtptr != ')') { /* validate ending */
+ sprintf (LOCAL->tmp,"Junk at end of disposition: %.80s",
+ (char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ }
+ else ++*txtptr; /* skip past delimiter */
+ break;
+ case 'N': /* if NIL */
+ case 'n':
+ ++*txtptr; /* bump past "N" */
+ ++*txtptr; /* bump past "I" */
+ ++*txtptr; /* bump past "L" */
+ break;
+ default:
+ sprintf (LOCAL->tmp,"Unknown body disposition: %.80s",(char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ /* try to skip to next space */
+ while ((*++*txtptr != ' ') && (**txtptr != ')') && **txtptr);
+ break;
+ }
+}
+
+/* IMAP parse body language
+ * Accepts: MAIL stream
+ * current text pointer
+ * parsed reply
+ * Returns: string list or NIL if empty or error
+ */
+
+STRINGLIST *imap_parse_language (MAILSTREAM *stream,unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply)
+{
+ unsigned long i;
+ char *s;
+ STRINGLIST *ret = NIL;
+ /* language is a list */
+ if (*++*txtptr == '(') ret = imap_parse_stringlist (stream,txtptr,reply);
+ else if (s = imap_parse_string (stream,txtptr,reply,NIL,&i,LONGT)) {
+ (ret = mail_newstringlist ())->text.data = (unsigned char *) s;
+ ret->text.size = i;
+ }
+ return ret;
+}
+
+/* IMAP parse string list
+ * Accepts: MAIL stream
+ * current text pointer
+ * parsed reply
+ * Returns: string list or NIL if empty or error
+ */
+
+STRINGLIST *imap_parse_stringlist (MAILSTREAM *stream,unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply)
+{
+ STRINGLIST *stl = NIL;
+ STRINGLIST *stc = NIL;
+ unsigned char *t = *txtptr;
+ /* parse the list */
+ if (*t++ == '(') while (*t != ')') {
+ if (stl) stc = stc->next = mail_newstringlist ();
+ else stc = stl = mail_newstringlist ();
+ /* parse astring */
+ if (!(stc->text.data =
+ imap_parse_astring (stream,&t,reply,&stc->text.size))) {
+ sprintf (LOCAL->tmp,"Bogus string list member: %.80s",(char *) t);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ mail_free_stringlist (&stl);
+ break;
+ }
+ else if (*t == ' ') ++t; /* another token follows */
+ }
+ if (stl) *txtptr = ++t; /* update return string */
+ return stl;
+}
+
+/* IMAP parse unknown body extension data
+ * Accepts: MAIL stream
+ * current text pointer
+ * parsed reply
+ *
+ * Updates text pointer
+ */
+
+void imap_parse_extension (MAILSTREAM *stream,unsigned char **txtptr,
+ IMAPPARSEDREPLY *reply)
+{
+ unsigned long i,j;
+ switch (*++*txtptr) { /* action depends upon first character */
+ case '(':
+ while (**txtptr != ')') imap_parse_extension (stream,txtptr,reply);
+ ++*txtptr; /* bump past closing parenthesis */
+ break;
+ case '"': /* if quoted string */
+ while (*++*txtptr != '"') if (**txtptr == '\\') ++*txtptr;
+ ++*txtptr; /* bump past closing quote */
+ break;
+ case 'N': /* if NIL */
+ case 'n':
+ ++*txtptr; /* bump past "N" */
+ ++*txtptr; /* bump past "I" */
+ ++*txtptr; /* bump past "L" */
+ break;
+ case '{': /* get size of literal */
+ ++*txtptr; /* bump past open squiggle */
+ if (i = strtoul (*txtptr,(char **) txtptr,10)) do
+ net_getbuffer (LOCAL->netstream,j = min (i,(long) IMAPTMPLEN - 1),
+ LOCAL->tmp);
+ while (i -= j);
+ /* get new reply text line */
+ if (!(reply->line = net_getline (LOCAL->netstream)))
+ reply->line = cpystr ("");
+ if (stream->debug) mm_dlog (reply->line);
+ *txtptr = reply->line; /* set text pointer to point at it */
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ strtoul (*txtptr,(char **) txtptr,10);
+ break;
+ default:
+ sprintf (LOCAL->tmp,"Unknown extension token: %.80s",(char *) *txtptr);
+ mm_notify (stream,LOCAL->tmp,WARN);
+ stream->unhealthy = T;
+ /* try to skip to next space */
+ while ((*++*txtptr != ' ') && (**txtptr != ')') && **txtptr);
+ break;
+ }
+}
+
+/* IMAP parse capabilities
+ * Accepts: MAIL stream
+ * capability list
+ */
+
+void imap_parse_capabilities (MAILSTREAM *stream,char *t)
+{
+ char *s,*r;
+ unsigned long i;
+ THREADER *thr,*th;
+ if (!LOCAL->gotcapability) { /* need to save previous capabilities? */
+ /* no, flush threaders */
+ if (thr = LOCAL->cap.threader) while (th = thr) {
+ fs_give ((void **) &th->name);
+ thr = th->next;
+ fs_give ((void **) &th);
+ }
+ /* zap capabilities */
+ memset (&LOCAL->cap,0,sizeof (LOCAL->cap));
+ LOCAL->gotcapability = T; /* flag that capabilities arrived */
+ }
+ for (t = strtok_r (t," ",&r); t; t = strtok_r (NIL," ",&r)) {
+ if (!compare_cstring (t,"IMAP4"))
+ LOCAL->cap.imap4 = LOCAL->cap.imap2bis = LOCAL->cap.rfc1176 = T;
+ else if (!compare_cstring (t,"IMAP4rev1"))
+ LOCAL->cap.imap4rev1 = LOCAL->cap.imap2bis = LOCAL->cap.rfc1176 = T;
+ else if (!compare_cstring (t,"IMAP2")) LOCAL->cap.rfc1176 = T;
+ else if (!compare_cstring (t,"IMAP2bis"))
+ LOCAL->cap.imap2bis = LOCAL->cap.rfc1176 = T;
+ else if (!compare_cstring (t,"ACL")) LOCAL->cap.acl = T;
+ else if (!compare_cstring (t,"QUOTA")) LOCAL->cap.quota = T;
+ else if (!compare_cstring (t,"LITERAL+")) LOCAL->cap.litplus = T;
+ else if (!compare_cstring (t,"IDLE")) LOCAL->cap.idle = T;
+ else if (!compare_cstring (t,"MAILBOX-REFERRALS")) LOCAL->cap.mbx_ref = T;
+ else if (!compare_cstring (t,"LOGIN-REFERRALS")) LOCAL->cap.log_ref = T;
+ else if (!compare_cstring (t,"NAMESPACE")) LOCAL->cap.namespace = T;
+ else if (!compare_cstring (t,"UIDPLUS")) LOCAL->cap.uidplus = T;
+ else if (!compare_cstring (t,"STARTTLS")) LOCAL->cap.starttls = T;
+ else if (!compare_cstring (t,"LOGINDISABLED"))LOCAL->cap.logindisabled = T;
+ else if (!compare_cstring (t,"ID")) LOCAL->cap.id = T;
+ else if (!compare_cstring (t,"CHILDREN")) LOCAL->cap.children = T;
+ else if (!compare_cstring (t,"MULTIAPPEND")) LOCAL->cap.multiappend = T;
+ else if (!compare_cstring (t,"BINARY")) LOCAL->cap.binary = T;
+ else if (!compare_cstring (t,"UNSELECT")) LOCAL->cap.unselect = T;
+ else if (!compare_cstring (t,"SASL-IR")) LOCAL->cap.sasl_ir = T;
+ else if (!compare_cstring (t,"SCAN")) LOCAL->cap.scan = T;
+ else if (!compare_cstring (t,"URLAUTH")) LOCAL->cap.urlauth = T;
+ else if (!compare_cstring (t,"CATENATE")) LOCAL->cap.catenate = T;
+ else if (!compare_cstring (t,"CONDSTORE")) LOCAL->cap.condstore = T;
+ else if (!compare_cstring (t,"ESEARCH")) LOCAL->cap.esearch = T;
+ else if (((t[0] == 'S') || (t[0] == 's')) &&
+ ((t[1] == 'O') || (t[1] == 'o')) &&
+ ((t[2] == 'R') || (t[2] == 'r')) &&
+ ((t[3] == 'T') || (t[3] == 't'))) LOCAL->cap.sort = T;
+ /* capability with value? */
+ else if (s = strchr (t,'=')) {
+ *s++ = '\0'; /* separate token from value */
+ if (!compare_cstring (t,"THREAD") && !LOCAL->loser) {
+ THREADER *thread = (THREADER *) fs_get (sizeof (THREADER));
+ thread->name = cpystr (s);
+ thread->dispatch = NIL;
+ thread->next = LOCAL->cap.threader;
+ LOCAL->cap.threader = thread;
+ }
+ else if (!compare_cstring (t,"AUTH")) {
+ if ((i = mail_lookup_auth_name (s,LOCAL->authflags)) &&
+ (--i < MAXAUTHENTICATORS)) LOCAL->cap.auth |= (1 << i);
+ else if (!compare_cstring (s,"ANONYMOUS")) LOCAL->cap.authanon = T;
+ }
+ }
+ /* ignore other capabilities */
+ }
+ /* disable LOGIN if PLAIN also advertised */
+ if ((i = mail_lookup_auth_name ("PLAIN",NIL)) && (--i < MAXAUTHENTICATORS) &&
+ (LOCAL->cap.auth & (1 << i)) &&
+ (i = mail_lookup_auth_name ("LOGIN",NIL)) && (--i < MAXAUTHENTICATORS))
+ LOCAL->cap.auth &= ~(1 << i);
+}
+
+/* IMAP load cache
+ * Accepts: MAIL stream
+ * sequence
+ * flags
+ * Returns: parsed reply from fetch
+ */
+
+IMAPPARSEDREPLY *imap_fetch (MAILSTREAM *stream,char *sequence,long flags)
+{
+ int i = 2;
+ char *cmd = (LEVELIMAP4 (stream) && (flags & FT_UID)) ?
+ "UID FETCH" : "FETCH";
+ IMAPARG *args[9],aseq,aarg,aenv,ahhr,axtr,ahtr,abdy,atrl;
+ if (LOCAL->loser) sequence = imap_reform_sequence (stream,sequence,
+ flags & FT_UID);
+ args[0] = &aseq; aseq.type = SEQUENCE; aseq.text = (void *) sequence;
+ args[1] = &aarg; aarg.type = ATOM;
+ aenv.type = ATOM; aenv.text = (void *) "ENVELOPE";
+ ahhr.type = ATOM; ahhr.text = (void *) hdrheader[LOCAL->cap.extlevel];
+ axtr.type = ATOM; axtr.text = (void *) imap_extrahdrs;
+ ahtr.type = ATOM; ahtr.text = (void *) hdrtrailer;
+ abdy.type = ATOM; abdy.text = (void *) "BODYSTRUCTURE";
+ atrl.type = ATOM; atrl.text = (void *) "INTERNALDATE RFC822.SIZE FLAGS)";
+ if (LEVELIMAP4 (stream)) { /* include UID if IMAP4 or IMAP4rev1 */
+ aarg.text = (void *) "(UID";
+ if (flags & FT_NEEDENV) { /* if need envelopes */
+ args[i++] = &aenv; /* include envelope */
+ /* extra header poop if IMAP4rev1 */
+ if (!(flags & FT_NOHDRS) && LEVELIMAP4rev1 (stream)) {
+ args[i++] = &ahhr; /* header header */
+ if (axtr.text) args[i++] = &axtr;
+ args[i++] = &ahtr; /* header trailer */
+ }
+ /* fetch body if requested */
+ if (flags & FT_NEEDBODY) args[i++] = &abdy;
+ }
+ args[i++] = &atrl; /* fetch trailer */
+ }
+ /* easy if IMAP2 */
+ else aarg.text = (void *) (flags & FT_NEEDENV) ?
+ ((flags & FT_NEEDBODY) ?
+ "(RFC822.HEADER BODY INTERNALDATE RFC822.SIZE FLAGS)" :
+ "(RFC822.HEADER INTERNALDATE RFC822.SIZE FLAGS)") : "FAST";
+ args[i] = NIL; /* tie off command */
+ return imap_send (stream,cmd,args);
+}
+
+/* Reform sequence for losing server that doesn't handle ranges right
+ * Accepts: MAIL stream
+ * sequence
+ * non-zero if UID
+ * Returns: sequence
+ */
+
+char *imap_reform_sequence (MAILSTREAM *stream,char *sequence,long flags)
+{
+ unsigned long i,j,star;
+ char *s,*t,*tl,*rs;
+ /* can't win if empty */
+ if (!stream->nmsgs) return sequence;
+ /* get highest possible range value */
+ star = flags ? mail_uid (stream,stream->nmsgs) : stream->nmsgs;
+ /* flush old reformed sequence */
+ if (LOCAL->reform) fs_give ((void **) &LOCAL->reform);
+ rs = LOCAL->reform = (char *) fs_get (1+ strlen (sequence));
+ for (s = sequence; t = strpbrk (s,",:"); ) switch (*t++) {
+ case ',': /* single message */
+ strncpy (rs,s,i = t - s); /* copy string up to that point */
+ rs += i; /* advance destination pointer */
+ s += i; /* and source */
+ break;
+ case ':': /* message range */
+ i = (*s == '*') ? star : strtoul (s,NIL,10);
+ if (*t == '*') { /* range ends with star */
+ j = star;
+ tl = t+1;
+ }
+ else { /* numeric range end */
+ j = strtoul (t,(char **) &tl,10);
+ if (!tl) tl = t + strlen (t);
+ }
+ if (i <= j) { /* if first less than second */
+ if (*tl) tl++; /* skip past end of range if present */
+ strncpy (rs,s,i = tl - s);/* copy string up to that point */
+ rs += i; /* advance destination and source pointers */
+ s += i;
+ }
+ else { /* here's the workaround for losing servers */
+ strncpy (rs,t,i = tl - t);/* swap the order */
+ rs[i] = ':'; /* delimit */
+ strncpy (rs+i+1,s,j = (t-1) - s);
+ rs += i + 1 + j; /* advance destination pointer */
+ if (*tl) *rs++ = *tl++; /* write trailing delimiter if present */
+ s = tl; /* advance source pointer */
+ }
+ }
+ if (*s) strcpy (rs,s); /* write remainder of sequence */
+ else *rs = '\0'; /* tie off string */
+ return LOCAL->reform;
+}
+
+/* IMAP return host name
+ * Accepts: MAIL stream
+ * Returns: host name
+ */
+
+char *imap_host (MAILSTREAM *stream)
+{
+ if (stream->dtb != &imapdriver)
+ fatal ("imap_host called on non-IMAP stream!");
+ /* return host name on stream if open */
+ return (LOCAL && LOCAL->netstream) ? net_host (LOCAL->netstream) :
+ ".NO-IMAP-CONNECTION.";
+}
+
+
+/* IMAP return IMAP capability structure
+ * Accepts: MAIL stream
+ * Returns: IMAP capability structure
+ */
+
+IMAPCAP *imap_cap (MAILSTREAM *stream)
+{
+ if (stream->dtb != &imapdriver)
+ fatal ("imap_cap called on non-IMAP stream!");
+ return &LOCAL->cap; /* return capability structure */
+}
diff --git a/imap/src/c-client/imap4r1.h b/imap/src/c-client/imap4r1.h
new file mode 100644
index 00000000..8ee8a186
--- /dev/null
+++ b/imap/src/c-client/imap4r1.h
@@ -0,0 +1,281 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Interactive Mail Access Protocol 4rev1 (IMAP4R1) routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 14 October 1988
+ * Last Edited: 5 September 2007
+ */
+
+
+/* This include file is provided for applications which need to look under
+ * the covers at the IMAP driver and in particular want to do different
+ * operations depending upon the IMAP server's protocol level and
+ * capabilities. It is NOT included in the normal c-client.h application
+ * export, and most applications do NOT need the definitions in this file.
+ *
+ * As of October 15, 2003, it is believed that:
+ *
+ * Version RFC Status Known Implementations
+ * ------- --- ------ ---------------------
+ * IMAP1 none extinct experimental TOPS-20 server
+ * IMAP2 1064 extinct old TOPS-20, SUMEX servers
+ * IMAP2 1176 rare TOPS-20, old UW servers
+ * IMAP2bis expired I-D uncommon old UW, Cyrus servers
+ * IMAP3 1203 extinct none (never implemented)
+ * IMAP4 1730 rare old UW, Cyrus, Netscape servers
+ * IMAP4rev1 2060, 3501 ubiquitous UW, Cyrus, and many others
+ *
+ * Most client implementations will only interoperate with an IMAP4rev1
+ * server. c-client based client implementations can interoperate with IMAP2,
+ * IMAP2bis, IMAP4, and IMAP4rev1 servers, but only if they are very careful.
+ *
+ * The LEVELxxx() macros in this file enable the client to determine the
+ * server protocol level and capabilities. This file also contains a few
+ * backdoor calls into the IMAP driver.
+ */
+
+/* Server protocol level and capabilities */
+
+typedef struct imap_cap {
+ unsigned int rfc1176 : 1; /* server is RFC-1176 IMAP2 */
+ unsigned int imap2bis : 1; /* server is IMAP2bis */
+ unsigned int imap4 : 1; /* server is IMAP4 (RFC 1730) */
+ unsigned int imap4rev1 : 1; /* server is IMAP4rev1 */
+ unsigned int acl : 1; /* server has ACL (RFC 2086) */
+ unsigned int quota : 1; /* server has QUOTA (RFC 2087) */
+ unsigned int litplus : 1; /* server has LITERAL+ (RFC 2088) */
+ unsigned int idle : 1; /* server has IDLE (RFC 2177) */
+ unsigned int mbx_ref : 1; /* server has mailbox referrals (RFC 2193) */
+ unsigned int log_ref : 1; /* server has login referrals (RFC 2221) */
+ unsigned int authanon : 1; /* server has anonymous SASL (RFC 2245) */
+ unsigned int namespace :1; /* server has NAMESPACE (RFC 2342) */
+ unsigned int uidplus : 1; /* server has UIDPLUS (RFC 2359) */
+ unsigned int starttls : 1; /* server has STARTTLS (RFC 2595) */
+ /* server disallows LOGIN command (RFC 2595) */
+ unsigned int logindisabled : 1;
+ unsigned int id : 1; /* server has ID (RFC 2971) */
+ unsigned int children : 1; /* server has CHILDREN (RFC 3348) */
+ unsigned int multiappend : 1; /* server has multi-APPEND (RFC 3502) ;*/
+ unsigned int binary : 1; /* server has BINARY (RFC 3516) */
+ unsigned int unselect : 1; /* server has UNSELECT */
+ unsigned int sasl_ir : 1; /* server has SASL-IR initial response */
+ unsigned int sort : 1; /* server has SORT */
+ unsigned int scan : 1; /* server has SCAN */
+ unsigned int urlauth : 1; /* server has URLAUTH (RFC 4467) */
+ unsigned int catenate : 1; /* server has CATENATE (RFC 4469) */
+ unsigned int condstore : 1; /* server has CONDSTORE (RFC 4551) */
+ unsigned int esearch : 1; /* server has ESEARCH (RFC 4731) */
+ unsigned int within : 1; /* server has WITHIN (RFC 5032) */
+ unsigned int extlevel; /* extension data level supported by server */
+ /* supported authenticators */
+ unsigned int auth : MAXAUTHENTICATORS;
+ THREADER *threader; /* list of threaders */
+} IMAPCAP;
+
+/* IMAP4rev1 level or better */
+
+#define LEVELIMAP4rev1(stream) imap_cap (stream)->imap4rev1
+
+#define LEVELSTATUS LEVELIMAP4rev1
+
+
+/* IMAP4 level or better (not including RFC 1730 design mistakes) */
+
+#define LEVELIMAP4(stream) (imap_cap (stream)->imap4rev1 || \
+ imap_cap (stream)->imap4)
+
+
+/* IMAP4 RFC-1730 level */
+
+#define LEVEL1730(stream) imap_cap (stream)->imap4
+
+
+/* IMAP2bis level or better */
+
+#define LEVELIMAP2bis(stream) imap_cap (stream)->imap2bis
+
+
+/* IMAP2 RFC-1176 level or better */
+
+#define LEVEL1176(stream) imap_cap (stream)->rfc1176
+
+
+/* IMAP2 RFC-1064 or better */
+
+#define LEVEL1064(stream) 1
+
+/* Has ACL extension */
+
+#define LEVELACL(stream) imap_cap (stream)->acl
+
+
+/* Has QUOTA extension */
+
+#define LEVELQUOTA(stream) imap_cap (stream)->quota
+
+
+/* Has LITERALPLUS extension */
+
+#define LEVELLITERALPLUS(stream) imap_cap (stream)->litplus
+
+
+/* Has IDLE extension */
+
+#define LEVELIDLE(stream) imap_cap (stream)->idle
+
+
+/* Has mailbox referrals */
+
+#define LEVELMBX_REF(stream) imap_cap (stream)->mbx_ref
+
+
+/* Has login referrals */
+
+#define LEVELLOG_REF(stream) imap_cap (stream)->log_ref
+
+
+/* Has AUTH=ANONYMOUS extension */
+
+#define LEVELANONYMOUS(stream) imap_cap (stream)->authanon
+
+
+/* Has NAMESPACE extension */
+
+#define LEVELNAMESPACE(stream) imap_cap (stream)->namespace
+
+
+/* Has UIDPLUS extension */
+
+#define LEVELUIDPLUS(stream) imap_cap (stream)->uidplus
+
+
+/* Has STARTTLS extension */
+
+#define LEVELSTARTTLS(stream) imap_cap (stream)->starttls
+
+
+/* Has LOGINDISABLED extension */
+
+#define LEVELLOGINDISABLED(stream) imap_cap (stream)->logindisabled
+
+/* Has ID extension */
+
+#define LEVELID(stream) imap_cap (stream)->id
+
+
+/* Has CHILDREN extension */
+
+#define LEVELCHILDREN(stream) imap_cap (stream)->children
+
+
+/* Has MULTIAPPEND extension */
+
+#define LEVELMULTIAPPEND(stream) imap_cap (stream)->multiappend
+
+
+/* Has BINARY extension */
+
+#define LEVELBINARY(stream) imap_cap (stream)->binary
+
+
+/* Has UNSELECT extension */
+
+#define LEVELUNSELECT(stream) imap_cap (stream)->unselect
+
+
+/* Has SASL initial response extension */
+
+#define LEVELSASLIR(stream) imap_cap (stream)->sasl_ir
+
+
+/* Has SORT extension */
+
+#define LEVELSORT(stream) imap_cap (stream)->sort
+
+
+/* Has at least one THREAD extension */
+
+#define LEVELTHREAD(stream) ((imap_cap (stream)->threader) ? T : NIL)
+
+
+/* Has SCAN extension */
+
+#define LEVELSCAN(stream) imap_cap (stream)->scan
+
+
+/* Has URLAUTH extension */
+
+#define LEVELURLAUTH(stream) imap_cap (stream)->urlauth
+
+
+/* Has CATENATE extension */
+
+#define LEVELCATENATE(stream) imap_cap (stream)->catenate
+
+
+/* Has CONDSTORE extension */
+
+#define LEVELCONDSTORE(stream) imap_cap (stream)->condstore
+
+
+/* Has ESEARCH extension */
+
+#define LEVELESEARCH(stream) imap_cap (stream)->esearch
+
+
+/* Has WITHIN extension */
+
+#define LEVELWITHIN(stream) imap_cap (stream)->within
+
+/* Body structure extension levels */
+
+/* These are in BODYSTRUCTURE order. Note that multipart bodies do not have
+ * body-fld-md5. This is alright, since all subsequent body structure
+ * extensions are in both singlepart and multipart bodies. If that ever
+ * changes, this will have to be split.
+ */
+
+#define BODYEXTMD5 1 /* body-fld-md5 */
+#define BODYEXTDSP 2 /* body-fld-dsp */
+#define BODYEXTLANG 3 /* body-fld-lang */
+#define BODYEXTLOC 4 /* body-fld-loc */
+
+
+/* Function prototypes */
+
+IMAPCAP *imap_cap (MAILSTREAM *stream);
+char *imap_host (MAILSTREAM *stream);
+long imap_cache (MAILSTREAM *stream,unsigned long msgno,char *seg,
+ STRINGLIST *stl,SIZEDTEXT *text);
+
+
+/* Temporary */
+
+long imap_setacl (MAILSTREAM *stream,char *mailbox,char *id,char *rights);
+long imap_deleteacl (MAILSTREAM *stream,char *mailbox,char *id);
+long imap_getacl (MAILSTREAM *stream,char *mailbox);
+long imap_listrights (MAILSTREAM *stream,char *mailbox,char *id);
+long imap_myrights (MAILSTREAM *stream,char *mailbox);
+long imap_setquota (MAILSTREAM *stream,char *qroot,STRINGLIST *limits);
+long imap_getquota (MAILSTREAM *stream,char *qroot);
+long imap_getquotaroot (MAILSTREAM *stream,char *mailbox);
diff --git a/imap/src/c-client/mail.c b/imap/src/c-client/mail.c
new file mode 100644
index 00000000..d80a01f6
--- /dev/null
+++ b/imap/src/c-client/mail.c
@@ -0,0 +1,6337 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Mailbox Access routines
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@Washington.EDU
+ *
+ * Date: 22 November 1989
+ * Last Edited: 15 April 2008
+ */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <time.h>
+#include "c-client.h"
+
+char *UW_copyright = "Copyright 1988-2007 University of Washington\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n";
+
+/* c-client global data */
+
+ /* version of this library */
+static char *mailcclientversion = CCLIENTVERSION;
+ /* list of mail drivers */
+static DRIVER *maildrivers = NIL;
+ /* list of authenticators */
+static AUTHENTICATOR *mailauthenticators = NIL;
+ /* SSL driver pointer */
+static NETDRIVER *mailssldriver = NIL;
+ /* pointer to alternate gets function */
+static mailgets_t mailgets = NIL;
+ /* pointer to read progress function */
+static readprogress_t mailreadprogress = NIL;
+ /* mail cache manipulation function */
+static mailcache_t mailcache = mm_cache;
+ /* RFC-822 output generator */
+static rfc822out_t mail822out = NIL;
+ /* RFC-822 output generator (new style) */
+static rfc822outfull_t mail822outfull = NIL;
+ /* SMTP verbose callback */
+static smtpverbose_t mailsmtpverbose = mm_dlog;
+ /* proxy copy routine */
+static mailproxycopy_t mailproxycopy = NIL;
+ /* RFC-822 external line parse */
+static parseline_t mailparseline = NIL;
+ /* RFC-822 external phrase parser */
+static parsephrase_t mailparsephrase = NIL;
+static kinit_t mailkinit = NIL; /* application kinit callback */
+ /* note network sent command */
+static sendcommand_t mailsendcommand = NIL;
+ /* newsrc file name decision function */
+static newsrcquery_t mailnewsrcquery = NIL;
+ /* ACL results callback */
+static getacl_t mailaclresults = NIL;
+ /* list rights results callback */
+static listrights_t maillistrightsresults = NIL;
+ /* my rights results callback */
+static myrights_t mailmyrightsresults = NIL;
+ /* quota results callback */
+static quota_t mailquotaresults = NIL;
+ /* quota root results callback */
+static quotaroot_t mailquotarootresults = NIL;
+ /* sorted results callback */
+static sortresults_t mailsortresults = NIL;
+ /* threaded results callback */
+static threadresults_t mailthreadresults = NIL;
+ /* COPY UID results */
+static copyuid_t mailcopyuid = NIL;
+ /* APPEND UID results */
+static appenduid_t mailappenduid = NIL;
+ /* free elt extra stuff callback */
+static freeeltsparep_t mailfreeeltsparep = NIL;
+ /* free envelope extra stuff callback */
+static freeenvelopesparep_t mailfreeenvelopesparep = NIL;
+ /* free body extra stuff callback */
+static freebodysparep_t mailfreebodysparep = NIL;
+ /* free stream extra stuff callback */
+static freestreamsparep_t mailfreestreamsparep = NIL;
+ /* SSL start routine */
+static sslstart_t mailsslstart = NIL;
+ /* SSL certificate query */
+static sslcertificatequery_t mailsslcertificatequery = NIL;
+ /* SSL client certificate */
+static sslclientcert_t mailsslclientcert = NIL;
+ /* SSL client private key */
+static sslclientkey_t mailsslclientkey = NIL;
+ /* SSL failure notify */
+static sslfailure_t mailsslfailure = NIL;
+ /* snarf interval */
+static long mailsnarfinterval = 60;
+ /* snarf preservation */
+static long mailsnarfpreserve = NIL;
+ /* newsrc name uses canonical host */
+static long mailnewsrccanon = LONGT;
+
+ /* supported threaders */
+static THREADER mailthreadordsub = {
+ "ORDEREDSUBJECT",mail_thread_orderedsubject,NIL
+};
+static THREADER mailthreadlist = {
+ "REFERENCES",mail_thread_references,&mailthreadordsub
+};
+
+ /* server name */
+static char *servicename = "unknown";
+ /* server externally-set authentication ID */
+static char *externalauthid = NIL;
+static int expungeatping = T; /* mail_ping() may call mm_expunged() */
+static int trysslfirst = NIL; /* always try SSL first */
+static int notimezones = NIL; /* write timezones in "From " header */
+static int trustdns = T; /* do DNS canonicalization */
+static int saslusesptrname = T; /* SASL uses name from DNS PTR lookup */
+ /* trustdns also must be set */
+static int debugsensitive = NIL;/* debug telemetry includes sensitive data */
+
+/* Default mail cache handler
+ * Accepts: pointer to cache handle
+ * message number
+ * caching function
+ * Returns: cache data
+ */
+
+void *mm_cache (MAILSTREAM *stream,unsigned long msgno,long op)
+{
+ size_t n;
+ void *ret = NIL;
+ unsigned long i;
+ switch ((int) op) { /* what function? */
+ case CH_INIT: /* initialize cache */
+ if (stream->cache) { /* flush old cache contents */
+ while (stream->cachesize) {
+ mm_cache (stream,stream->cachesize,CH_FREE);
+ mm_cache (stream,stream->cachesize--,CH_FREESORTCACHE);
+ }
+ fs_give ((void **) &stream->cache);
+ fs_give ((void **) &stream->sc);
+ stream->nmsgs = 0; /* can't have any messages now */
+ }
+ break;
+ case CH_SIZE: /* (re-)size the cache */
+ if (!stream->cache) { /* have a cache already? */
+ /* no, create new cache */
+ n = (stream->cachesize = msgno + CACHEINCREMENT) * sizeof (void *);
+ stream->cache = (MESSAGECACHE **) memset (fs_get (n),0,n);
+ stream->sc = (SORTCACHE **) memset (fs_get (n),0,n);
+ }
+ /* is existing cache size large neough */
+ else if (msgno > stream->cachesize) {
+ i = stream->cachesize; /* remember old size */
+ n = (stream->cachesize = msgno + CACHEINCREMENT) * sizeof (void *);
+ fs_resize ((void **) &stream->cache,n);
+ fs_resize ((void **) &stream->sc,n);
+ while (i < stream->cachesize) {
+ stream->cache[i] = NIL;
+ stream->sc[i++] = NIL;
+ }
+ }
+ break;
+
+ case CH_MAKEELT: /* return elt, make if necessary */
+ if (!stream->cache[msgno - 1])
+ stream->cache[msgno - 1] = mail_new_cache_elt (msgno);
+ /* falls through */
+ case CH_ELT: /* return elt */
+ ret = (void *) stream->cache[msgno - 1];
+ break;
+ case CH_SORTCACHE: /* return sortcache entry, make if needed */
+ if (!stream->sc[msgno - 1]) stream->sc[msgno - 1] =
+ (SORTCACHE *) memset (fs_get (sizeof (SORTCACHE)),0,sizeof (SORTCACHE));
+ ret = (void *) stream->sc[msgno - 1];
+ break;
+ case CH_FREE: /* free elt */
+ mail_free_elt (&stream->cache[msgno - 1]);
+ break;
+ case CH_FREESORTCACHE:
+ if (stream->sc[msgno - 1]) {
+ if (stream->sc[msgno - 1]->from)
+ fs_give ((void **) &stream->sc[msgno - 1]->from);
+ if (stream->sc[msgno - 1]->to)
+ fs_give ((void **) &stream->sc[msgno - 1]->to);
+ if (stream->sc[msgno - 1]->cc)
+ fs_give ((void **) &stream->sc[msgno - 1]->cc);
+ if (stream->sc[msgno - 1]->subject)
+ fs_give ((void **) &stream->sc[msgno - 1]->subject);
+ if (stream->sc[msgno - 1]->unique &&
+ (stream->sc[msgno - 1]->unique != stream->sc[msgno - 1]->message_id))
+ fs_give ((void **) &stream->sc[msgno - 1]->unique);
+ if (stream->sc[msgno - 1]->message_id)
+ fs_give ((void **) &stream->sc[msgno - 1]->message_id);
+ if (stream->sc[msgno - 1]->references)
+ mail_free_stringlist (&stream->sc[msgno - 1]->references);
+ fs_give ((void **) &stream->sc[msgno - 1]);
+ }
+ break;
+ case CH_EXPUNGE: /* expunge cache slot */
+ for (i = msgno - 1; msgno < stream->nmsgs; i++,msgno++) {
+ if (stream->cache[i] = stream->cache[msgno])
+ stream->cache[i]->msgno = msgno;
+ stream->sc[i] = stream->sc[msgno];
+ }
+ stream->cache[i] = NIL; /* top of cache goes away */
+ stream->sc[i] = NIL;
+ break;
+ default:
+ fatal ("Bad mm_cache op");
+ break;
+ }
+ return ret;
+}
+
+/* Dummy string driver for complete in-memory strings */
+
+static void mail_string_init (STRING *s,void *data,unsigned long size);
+static char mail_string_next (STRING *s);
+static void mail_string_setpos (STRING *s,unsigned long i);
+
+STRINGDRIVER mail_string = {
+ mail_string_init, /* initialize string structure */
+ mail_string_next, /* get next byte in string structure */
+ mail_string_setpos /* set position in string structure */
+};
+
+
+/* Initialize mail string structure for in-memory string
+ * Accepts: string structure
+ * pointer to string
+ * size of string
+ */
+
+static void mail_string_init (STRING *s,void *data,unsigned long size)
+{
+ /* set initial string pointers */
+ s->chunk = s->curpos = (char *) (s->data = data);
+ /* and sizes */
+ s->size = s->chunksize = s->cursize = size;
+ s->data1 = s->offset = 0; /* never any offset */
+}
+
+
+/* Get next character from string
+ * Accepts: string structure
+ * Returns: character, string structure chunk refreshed
+ */
+
+static char mail_string_next (STRING *s)
+{
+ return *s->curpos++; /* return the last byte */
+}
+
+
+/* Set string pointer position
+ * Accepts: string structure
+ * new position
+ */
+
+static void mail_string_setpos (STRING *s,unsigned long i)
+{
+ s->curpos = s->chunk + i; /* set new position */
+ s->cursize = s->chunksize - i;/* and new size */
+}
+
+/* Mail routines
+ *
+ * mail_xxx routines are the interface between this module and the outside
+ * world. Only these routines should be referenced by external callers.
+ *
+ * Note that there is an important difference between a "sequence" and a
+ * "message #" (msgno). A sequence is a string representing a sequence in
+ * {"n", "n:m", or combination separated by commas} format, whereas a msgno
+ * is a single integer.
+ *
+ */
+
+/* Mail version check
+ * Accepts: version
+ */
+
+void mail_versioncheck (char *version)
+{
+ /* attempt to protect again wrong .h */
+ if (strcmp (version,mailcclientversion)) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"c-client library version skew, app=%.100s library=%.100s",
+ version,mailcclientversion);
+ fatal (tmp);
+ }
+}
+
+
+/* Mail link driver
+ * Accepts: driver to add to list
+ */
+
+void mail_link (DRIVER *driver)
+{
+ DRIVER **d = &maildrivers;
+ while (*d) d = &(*d)->next; /* find end of list of drivers */
+ *d = driver; /* put driver at the end */
+ driver->next = NIL; /* this driver is the end of the list */
+}
+
+/* Mail manipulate driver parameters
+ * Accepts: mail stream
+ * function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mail_parameters (MAILSTREAM *stream,long function,void *value)
+{
+ void *r,*ret = NIL;
+ DRIVER *d;
+ AUTHENTICATOR *a;
+ switch ((int) function) {
+ case SET_INBOXPATH:
+ fatal ("SET_INBOXPATH not permitted");
+ case GET_INBOXPATH:
+ if ((stream || (stream = mail_open (NIL,"INBOX",OP_PROTOTYPE))) &&
+ stream->dtb) ret = (*stream->dtb->parameters) (function,value);
+ break;
+ case SET_THREADERS:
+ fatal ("SET_THREADERS not permitted");
+ case GET_THREADERS: /* use stream dtb instead of global */
+ ret = (stream && stream->dtb) ?
+ /* KLUDGE ALERT: note stream passed as value */
+ (*stream->dtb->parameters) (function,stream) : (void *) &mailthreadlist;
+ break;
+ case SET_NAMESPACE:
+ fatal ("SET_NAMESPACE not permitted");
+ break;
+ case SET_NEWSRC: /* too late on open stream */
+ if (stream && stream->dtb && (stream != ((*stream->dtb->open) (NIL))))
+ fatal ("SET_NEWSRC not permitted");
+ else ret = env_parameters (function,value);
+ break;
+ case GET_NAMESPACE:
+ case GET_NEWSRC: /* use stream dtb instead of environment */
+ ret = (stream && stream->dtb) ?
+ /* KLUDGE ALERT: note stream passed as value */
+ (*stream->dtb->parameters) (function,stream) :
+ env_parameters (function,value);
+ break;
+ case ENABLE_DEBUG:
+ fatal ("ENABLE_DEBUG not permitted");
+ case DISABLE_DEBUG:
+ fatal ("DISABLE_DEBUG not permitted");
+ case SET_DIRFMTTEST:
+ fatal ("SET_DIRFMTTEST not permitted");
+ case GET_DIRFMTTEST:
+ if (!(stream && stream->dtb &&
+ (ret = (*stream->dtb->parameters) (function,NIL))))
+ fatal ("GET_DIRFMTTEST not permitted");
+ break;
+
+ case SET_DRIVERS:
+ fatal ("SET_DRIVERS not permitted");
+ case GET_DRIVERS: /* always return global */
+ ret = (void *) maildrivers;
+ break;
+ case SET_DRIVER:
+ fatal ("SET_DRIVER not permitted");
+ case GET_DRIVER:
+ for (d = maildrivers; d && compare_cstring (d->name,(char *) value);
+ d = d->next);
+ ret = (void *) d;
+ break;
+ case ENABLE_DRIVER:
+ for (d = maildrivers; d && compare_cstring (d->name,(char *) value);
+ d = d->next);
+ if (ret = (void *) d) d->flags &= ~DR_DISABLE;
+ break;
+ case DISABLE_DRIVER:
+ for (d = maildrivers; d && compare_cstring (d->name,(char *) value);
+ d = d->next);
+ if (ret = (void *) d) d->flags |= DR_DISABLE;
+ break;
+ case ENABLE_AUTHENTICATOR:
+ for (a = mailauthenticators;/* scan authenticators */
+ a && compare_cstring (a->name,(char *) value); a = a->next);
+ if (ret = (void *) a) a->flags &= ~AU_DISABLE;
+ break;
+ case DISABLE_AUTHENTICATOR:
+ for (a = mailauthenticators;/* scan authenticators */
+ a && compare_cstring (a->name,(char *) value); a = a->next);
+ if (ret = (void *) a) a->flags |= AU_DISABLE;
+ break;
+ case UNHIDE_AUTHENTICATOR:
+ for (a = mailauthenticators;/* scan authenticators */
+ a && compare_cstring (a->name,(char *) value); a = a->next);
+ if (ret = (void *) a) a->flags &= ~AU_HIDE;
+ break;
+ case HIDE_AUTHENTICATOR:
+ for (a = mailauthenticators;/* scan authenticators */
+ a && compare_cstring (a->name,(char *) value); a = a->next);
+ if (ret = (void *) a) a->flags |= AU_HIDE;
+ break;
+ case SET_EXTERNALAUTHID:
+ if (value) { /* setting external authentication ID */
+ externalauthid = cpystr ((char *) value);
+ mail_parameters (NIL,UNHIDE_AUTHENTICATOR,"EXTERNAL");
+ }
+ else { /* clearing external authentication ID */
+ if (externalauthid) fs_give ((void **) &externalauthid);
+ mail_parameters (NIL,HIDE_AUTHENTICATOR,"EXTERNAL");
+ }
+ case GET_EXTERNALAUTHID:
+ ret = (void *) externalauthid;
+ break;
+
+ case SET_GETS:
+ mailgets = (mailgets_t) value;
+ case GET_GETS:
+ ret = (void *) mailgets;
+ break;
+ case SET_READPROGRESS:
+ mailreadprogress = (readprogress_t) value;
+ case GET_READPROGRESS:
+ ret = (void *) mailreadprogress;
+ break;
+ case SET_CACHE:
+ mailcache = (mailcache_t) value;
+ case GET_CACHE:
+ ret = (void *) mailcache;
+ break;
+ case SET_RFC822OUTPUT:
+ mail822out = (rfc822out_t) value;
+ case GET_RFC822OUTPUT:
+ ret = (void *) mail822out;
+ break;
+ case SET_RFC822OUTPUTFULL:
+ mail822outfull = (rfc822outfull_t) value;
+ case GET_RFC822OUTPUTFULL:
+ ret = (void *) mail822outfull;
+ break;
+ case SET_SMTPVERBOSE:
+ mailsmtpverbose = (smtpverbose_t) value;
+ case GET_SMTPVERBOSE:
+ ret = (void *) mailsmtpverbose;
+ break;
+ case SET_MAILPROXYCOPY:
+ mailproxycopy = (mailproxycopy_t) value;
+ case GET_MAILPROXYCOPY:
+ ret = (void *) mailproxycopy;
+ break;
+ case SET_PARSELINE:
+ mailparseline = (parseline_t) value;
+ case GET_PARSELINE:
+ ret = (void *) mailparseline;
+ break;
+ case SET_PARSEPHRASE:
+ mailparsephrase = (parsephrase_t) value;
+ case GET_PARSEPHRASE:
+ ret = (void *) mailparsephrase;
+ break;
+ case SET_NEWSRCQUERY:
+ mailnewsrcquery = (newsrcquery_t) value;
+ case GET_NEWSRCQUERY:
+ ret = (void *) mailnewsrcquery;
+ break;
+ case SET_NEWSRCCANONHOST:
+ mailnewsrccanon = (long) value;
+ case GET_NEWSRCCANONHOST:
+ ret = (void *) mailnewsrccanon;
+ break;
+
+ case SET_COPYUID:
+ mailcopyuid = (copyuid_t) value;
+ case GET_COPYUID:
+ ret = (void *) mailcopyuid;
+ break;
+ case SET_APPENDUID:
+ mailappenduid = (appenduid_t) value;
+ case GET_APPENDUID:
+ ret = (void *) mailappenduid;
+ break;
+ case SET_FREEENVELOPESPAREP:
+ mailfreeenvelopesparep = (freeenvelopesparep_t) value;
+ case GET_FREEENVELOPESPAREP:
+ ret = (void *) mailfreeenvelopesparep;
+ break;
+ case SET_FREEELTSPAREP:
+ mailfreeeltsparep = (freeeltsparep_t) value;
+ case GET_FREEELTSPAREP:
+ ret = (void *) mailfreeeltsparep;
+ break;
+ case SET_FREESTREAMSPAREP:
+ mailfreestreamsparep = (freestreamsparep_t) value;
+ case GET_FREESTREAMSPAREP:
+ ret = (void *) mailfreestreamsparep;
+ break;
+ case SET_FREEBODYSPAREP:
+ mailfreebodysparep = (freebodysparep_t) value;
+ case GET_FREEBODYSPAREP:
+ ret = (void *) mailfreebodysparep;
+ break;
+
+ case SET_SSLSTART:
+ mailsslstart = (sslstart_t) value;
+ case GET_SSLSTART:
+ ret = (void *) mailsslstart;
+ break;
+ case SET_SSLCERTIFICATEQUERY:
+ mailsslcertificatequery = (sslcertificatequery_t) value;
+ case GET_SSLCERTIFICATEQUERY:
+ ret = (void *) mailsslcertificatequery;
+ break;
+ case SET_SSLCLIENTCERT:
+ mailsslclientcert = (sslclientcert_t) value;
+ case GET_SSLCLIENTCERT:
+ ret = (void *) mailsslclientcert;
+ break;
+ case SET_SSLCLIENTKEY:
+ mailsslclientkey = (sslclientkey_t) value;
+ case GET_SSLCLIENTKEY:
+ ret = (void *) mailsslclientkey;
+ break;
+ case SET_SSLFAILURE:
+ mailsslfailure = (sslfailure_t) value;
+ case GET_SSLFAILURE:
+ ret = (void *) mailsslfailure;
+ break;
+ case SET_KINIT:
+ mailkinit = (kinit_t) value;
+ case GET_KINIT:
+ ret = (void *) mailkinit;
+ break;
+ case SET_SENDCOMMAND:
+ mailsendcommand = (sendcommand_t) value;
+ case GET_SENDCOMMAND:
+ ret = (void *) mailsendcommand;
+ break;
+
+ case SET_SERVICENAME:
+ servicename = (char *) value;
+ case GET_SERVICENAME:
+ ret = (void *) servicename;
+ break;
+ case SET_EXPUNGEATPING:
+ expungeatping = (value ? T : NIL);
+ case GET_EXPUNGEATPING:
+ ret = (void *) (expungeatping ? VOIDT : NIL);
+ break;
+ case SET_SORTRESULTS:
+ mailsortresults = (sortresults_t) value;
+ case GET_SORTRESULTS:
+ ret = (void *) mailsortresults;
+ break;
+ case SET_THREADRESULTS:
+ mailthreadresults = (threadresults_t) value;
+ case GET_THREADRESULTS:
+ ret = (void *) mailthreadresults;
+ break;
+ case SET_SSLDRIVER:
+ mailssldriver = (NETDRIVER *) value;
+ case GET_SSLDRIVER:
+ ret = (void *) mailssldriver;
+ break;
+ case SET_TRYSSLFIRST:
+ trysslfirst = (value ? T : NIL);
+ case GET_TRYSSLFIRST:
+ ret = (void *) (trysslfirst ? VOIDT : NIL);
+ break;
+ case SET_NOTIMEZONES:
+ notimezones = (value ? T : NIL);
+ case GET_NOTIMEZONES:
+ ret = (void *) (notimezones ? VOIDT : NIL);
+ break;
+ case SET_TRUSTDNS:
+ trustdns = (value ? T : NIL);
+ case GET_TRUSTDNS:
+ ret = (void *) (trustdns ? VOIDT : NIL);
+ break;
+ case SET_SASLUSESPTRNAME:
+ saslusesptrname = (value ? T : NIL);
+ case GET_SASLUSESPTRNAME:
+ ret = (void *) (saslusesptrname ? VOIDT : NIL);
+ break;
+ case SET_DEBUGSENSITIVE:
+ debugsensitive = (value ? T : NIL);
+ case GET_DEBUGSENSITIVE:
+ ret = (void *) (debugsensitive ? VOIDT : NIL);
+ break;
+
+ case SET_ACL:
+ mailaclresults = (getacl_t) value;
+ case GET_ACL:
+ ret = (void *) mailaclresults;
+ break;
+ case SET_LISTRIGHTS:
+ maillistrightsresults = (listrights_t) value;
+ case GET_LISTRIGHTS:
+ ret = (void *) maillistrightsresults;
+ break;
+ case SET_MYRIGHTS:
+ mailmyrightsresults = (myrights_t) value;
+ case GET_MYRIGHTS:
+ ret = (void *) mailmyrightsresults;
+ break;
+ case SET_QUOTA:
+ mailquotaresults = (quota_t) value;
+ case GET_QUOTA:
+ ret = (void *) mailquotaresults;
+ break;
+ case SET_QUOTAROOT:
+ mailquotarootresults = (quotaroot_t) value;
+ case GET_QUOTAROOT:
+ ret = (void *) mailquotarootresults;
+ break;
+ case SET_SNARFINTERVAL:
+ mailsnarfinterval = (long) value;
+ case GET_SNARFINTERVAL:
+ ret = (void *) mailsnarfinterval;
+ break;
+ case SET_SNARFPRESERVE:
+ mailsnarfpreserve = (long) value;
+ case GET_SNARFPRESERVE:
+ ret = (void *) mailsnarfpreserve;
+ break;
+ case SET_SNARFMAILBOXNAME:
+ if (stream) { /* have a stream? */
+ if (stream->snarf.name) fs_give ((void **) &stream->snarf.name);
+ stream->snarf.name = cpystr ((char *) value);
+ }
+ else fatal ("SET_SNARFMAILBOXNAME with no stream");
+ case GET_SNARFMAILBOXNAME:
+ if (stream) ret = (void *) stream->snarf.name;
+ break;
+ default:
+ if (r = smtp_parameters (function,value)) ret = r;
+ if (r = env_parameters (function,value)) ret = r;
+ if (r = tcp_parameters (function,value)) ret = r;
+ if (stream && stream->dtb) {/* if have stream, do for its driver only */
+ if (r = (*stream->dtb->parameters) (function,value)) ret = r;
+ }
+ /* else do all drivers */
+ else for (d = maildrivers; d; d = d->next)
+ if (r = (d->parameters) (function,value)) ret = r;
+ break;
+ }
+ return ret;
+}
+
+/* Mail validate mailbox name
+ * Accepts: MAIL stream
+ * mailbox name
+ * purpose string for error message
+ * Return: driver factory on success, NIL on failure
+ */
+
+DRIVER *mail_valid (MAILSTREAM *stream,char *mailbox,char *purpose)
+{
+ char tmp[MAILTMPLEN];
+ DRIVER *factory = NIL;
+ /* never allow names with newlines */
+ if (strpbrk (mailbox,"\015\012")) {
+ if (purpose) { /* if want an error message */
+ sprintf (tmp,"Can't %s with such a name",purpose);
+ MM_LOG (tmp,ERROR);
+ }
+ return NIL;
+ }
+ /* validate name, find driver factory */
+ if (strlen (mailbox) < (NETMAXHOST+(NETMAXUSER*2)+NETMAXMBX+NETMAXSRV+50))
+ for (factory = maildrivers; factory &&
+ ((factory->flags & DR_DISABLE) ||
+ ((factory->flags & DR_LOCAL) && (*mailbox == '{')) ||
+ !(*factory->valid) (mailbox));
+ factory = factory->next);
+ /* validate factory against non-dummy stream */
+ if (factory && stream && stream->dtb && (stream->dtb != factory) &&
+ strcmp (stream->dtb->name,"dummy"))
+ /* factory invalid; if dummy, use stream */
+ factory = strcmp (factory->name,"dummy") ? NIL : stream->dtb;
+ if (!factory && purpose) { /* if want an error message */
+ sprintf (tmp,"Can't %s %.80s: %s",purpose,mailbox,(*mailbox == '{') ?
+ "invalid remote specification" : "no such mailbox");
+ MM_LOG (tmp,ERROR);
+ }
+ return factory; /* return driver factory */
+}
+
+/* Mail validate network mailbox name
+ * Accepts: mailbox name
+ * mailbox driver to validate against
+ * pointer to where to return host name if non-NIL
+ * pointer to where to return mailbox name if non-NIL
+ * Returns: driver on success, NIL on failure
+ */
+
+DRIVER *mail_valid_net (char *name,DRIVER *drv,char *host,char *mailbox)
+{
+ NETMBX mb;
+ if (!mail_valid_net_parse (name,&mb) || strcmp (mb.service,drv->name))
+ return NIL;
+ if (host) strcpy (host,mb.host);
+ if (mailbox) strcpy (mailbox,mb.mailbox);
+ return drv;
+}
+
+
+/* Mail validate network mailbox name
+ * Accepts: mailbox name
+ * NETMBX structure to return values
+ * Returns: T on success, NIL on failure
+ */
+
+long mail_valid_net_parse (char *name,NETMBX *mb)
+{
+ return mail_valid_net_parse_work (name,mb,"imap");
+}
+
+/* Mail validate network mailbox name worker routine
+ * Accepts: mailbox name
+ * NETMBX structure to return values
+ * default service
+ * Returns: T on success, NIL on failure
+ */
+
+long mail_valid_net_parse_work (char *name,NETMBX *mb,char *service)
+{
+ int i,j;
+ char c,*s,*t,*v,tmp[MAILTMPLEN],arg[MAILTMPLEN];
+ /* initialize structure */
+ memset (mb,'\0',sizeof (NETMBX));
+ /* must have host specification */
+ if (*name++ != '{') return NIL;
+ if (*name == '[') { /* if domain literal, find its ending */
+ if (!((v = strpbrk (name,"]}")) && (*v++ == ']'))) return NIL;
+ }
+ /* find end of host name */
+ else if (!(v = strpbrk (name,"/:}"))) return NIL;
+ /* validate length, find mailbox part */
+ if (!((i = v - name) && (i < NETMAXHOST) && (t = strchr (v,'}')) &&
+ ((j = t - v) < MAILTMPLEN) && (strlen (t+1) < (size_t) NETMAXMBX)))
+ return NIL; /* invalid mailbox */
+ strncpy (mb->host,name,i); /* set host name */
+ strncpy (mb->orighost,name,i);
+ mb->host[i] = mb->orighost[i] = '\0';
+ strcpy (mb->mailbox,t+1); /* set mailbox name */
+ if (t - v) { /* any switches or port specification? */
+ strncpy (t = tmp,v,j); /* copy it */
+ tmp[j] = '\0'; /* tie it off */
+ c = *t++; /* get first delimiter */
+ do switch (c) { /* act based upon the character */
+ case ':': /* port specification */
+ if (mb->port || !(mb->port = strtoul (t,&t,10))) return NIL;
+ c = t ? *t++ : '\0'; /* get delimiter, advance pointer */
+ break;
+ case '/': /* switch */
+ /* find delimiter */
+ if (t = strpbrk (s = t,"/:=")) {
+ c = *t; /* remember delimiter for later */
+ *t++ = '\0'; /* tie off switch name */
+ }
+ else c = '\0'; /* no delimiter */
+ if (c == '=') { /* parse switches which take arguments */
+ if (*t == '"') { /* quoted string? */
+ for (v = arg,i = 0,++t; (c = *t++) != '"';) {
+ if (!c) return NIL; /* unterminated string */
+ /* quote next character */
+ if (c == '\\') c = *t++;
+ if (!c) return NIL; /* can't quote NUL either */
+ arg[i++] = c;
+ }
+ c = *t++; /* remember delimiter for later */
+ arg[i] = '\0'; /* tie off argument */
+ }
+ else { /* non-quoted argument */
+ if (t = strpbrk (v = t,"/:")) {
+ c = *t; /* remember delimiter for later */
+ *t++ = '\0'; /* tie off switch name */
+ }
+ else c = '\0'; /* no delimiter */
+ i = strlen (v); /* length of argument */
+ }
+ if (!compare_cstring (s,"service") && (i < NETMAXSRV) && !*mb->service)
+ lcase (strcpy (mb->service,v));
+ else if (!compare_cstring (s,"user") && (i < NETMAXUSER) && !*mb->user)
+ strcpy (mb->user,v);
+ else if (!compare_cstring (s,"authuser") && (i < NETMAXUSER) &&
+ !*mb->authuser) strcpy (mb->authuser,v);
+ else return NIL;
+ }
+
+ else { /* non-argument switch */
+ if (!compare_cstring (s,"anonymous")) mb->anoflag = T;
+ else if (!compare_cstring (s,"debug")) mb->dbgflag = T;
+ else if (!compare_cstring (s,"readonly")) mb->readonlyflag = T;
+ else if (!compare_cstring (s,"secure")) mb->secflag = T;
+ else if (!compare_cstring (s,"norsh")) mb->norsh = T;
+ else if (!compare_cstring (s,"loser")) mb->loser = T;
+ else if (!compare_cstring (s,"tls") && !mb->notlsflag)
+ mb->tlsflag = T;
+ else if (!compare_cstring (s,"tls-sslv23") && !mb->notlsflag)
+ mb->tlssslv23 = mb->tlsflag = T;
+ else if (!compare_cstring (s,"notls") && !mb->tlsflag)
+ mb->notlsflag = T;
+ else if (!compare_cstring (s,"tryssl"))
+ mb->trysslflag = mailssldriver? T : NIL;
+ else if (mailssldriver && !compare_cstring (s,"ssl") && !mb->tlsflag)
+ mb->sslflag = mb->notlsflag = T;
+ else if (mailssldriver && !compare_cstring (s,"novalidate-cert"))
+ mb->novalidate = T;
+ /* hack for compatibility with the past */
+ else if (mailssldriver && !compare_cstring (s,"validate-cert"));
+ /* service switches below here */
+ else if (*mb->service) return NIL;
+ else if (!compare_cstring (s,"imap") ||
+ !compare_cstring (s,"nntp") ||
+ !compare_cstring (s,"pop3") ||
+ !compare_cstring (s,"smtp") ||
+ !compare_cstring (s,"submit"))
+ lcase (strcpy (mb->service,s));
+ else if (!compare_cstring (s,"imap2") ||
+ !compare_cstring (s,"imap2bis") ||
+ !compare_cstring (s,"imap4") ||
+ !compare_cstring (s,"imap4rev1"))
+ strcpy (mb->service,"imap");
+ else if (!compare_cstring (s,"pop"))
+ strcpy (mb->service,"pop3");
+ else return NIL; /* invalid non-argument switch */
+ }
+ break;
+ default: /* anything else is bogus */
+ return NIL;
+ } while (c); /* see if anything more to parse */
+ }
+ /* default mailbox name */
+ if (!*mb->mailbox) strcpy (mb->mailbox,"INBOX");
+ /* default service name */
+ if (!*mb->service) strcpy (mb->service,service);
+ /* /norsh only valid if imap */
+ if (mb->norsh && strcmp (mb->service,"imap")) return NIL;
+ return T;
+}
+
+/* Mail scan mailboxes for string
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * contents to search
+ */
+
+void mail_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ int remote = ((*pat == '{') || (ref && *ref == '{'));
+ DRIVER *d;
+ if (ref && (strlen (ref) > NETMAXMBX)) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Invalid LIST reference specification: %.80s",ref);
+ MM_LOG (tmp,ERROR);
+ return;
+ }
+ if (strlen (pat) > NETMAXMBX) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Invalid LIST pattern specification: %.80s",pat);
+ MM_LOG (tmp,ERROR);
+ return;
+ }
+ if (*pat == '{') ref = NIL; /* ignore reference if pattern is remote */
+ if (stream) { /* if have a stream, do it for that stream */
+ if ((d = stream->dtb) && d->scan &&
+ !(((d->flags & DR_LOCAL) && remote)))
+ (*d->scan) (stream,ref,pat,contents);
+ }
+ /* otherwise do for all DTB's */
+ else for (d = maildrivers; d; d = d->next)
+ if (d->scan && !((d->flags & DR_DISABLE) ||
+ ((d->flags & DR_LOCAL) && remote)))
+ (d->scan) (NIL,ref,pat,contents);
+}
+
+/* Mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mail_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ int remote = ((*pat == '{') || (ref && *ref == '{'));
+ DRIVER *d = maildrivers;
+ if (ref && (strlen (ref) > NETMAXMBX)) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Invalid LIST reference specification: %.80s",ref);
+ MM_LOG (tmp,ERROR);
+ return;
+ }
+ if (strlen (pat) > NETMAXMBX) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Invalid LIST pattern specification: %.80s",pat);
+ MM_LOG (tmp,ERROR);
+ return;
+ }
+ if (*pat == '{') ref = NIL; /* ignore reference if pattern is remote */
+ if (stream && stream->dtb) { /* if have a stream, do it for that stream */
+ if (!(((d = stream->dtb)->flags & DR_LOCAL) && remote))
+ (*d->list) (stream,ref,pat);
+ }
+ /* otherwise do for all DTB's */
+ else do if (!((d->flags & DR_DISABLE) ||
+ ((d->flags & DR_LOCAL) && remote)))
+ (d->list) (NIL,ref,pat);
+ while (d = d->next); /* until at the end */
+}
+
+/* Mail list subscribed mailboxes
+ * Accepts: mail stream
+ * pattern to search
+ */
+
+void mail_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ int remote = ((*pat == '{') || (ref && *ref == '{'));
+ DRIVER *d = maildrivers;
+ if (ref && (strlen (ref) > NETMAXMBX)) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Invalid LSUB reference specification: %.80s",ref);
+ MM_LOG (tmp,ERROR);
+ return;
+ }
+ if (strlen (pat) > NETMAXMBX) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Invalid LSUB pattern specification: %.80s",pat);
+ MM_LOG (tmp,ERROR);
+ return;
+ }
+ if (*pat == '{') ref = NIL; /* ignore reference if pattern is remote */
+ if (stream && stream->dtb) { /* if have a stream, do it for that stream */
+ if (!(((d = stream->dtb)->flags & DR_LOCAL) && remote))
+ (*d->lsub) (stream,ref,pat);
+ }
+ /* otherwise do for all DTB's */
+ else do if (!((d->flags & DR_DISABLE) ||
+ ((d->flags & DR_LOCAL) && remote)))
+ (d->lsub) (NIL,ref,pat);
+ while (d = d->next); /* until at the end */
+}
+
+/* Mail subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long mail_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ DRIVER *factory = mail_valid (stream,mailbox,"subscribe to mailbox");
+ return factory ?
+ (factory->subscribe ?
+ (*factory->subscribe) (stream,mailbox) : sm_subscribe (mailbox)) : NIL;
+}
+
+
+/* Mail unsubscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to delete from subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long mail_unsubscribe (MAILSTREAM *stream,char *mailbox)
+{
+ DRIVER *factory = mail_valid (stream,mailbox,NIL);
+ return (factory && factory->unsubscribe) ?
+ (*factory->unsubscribe) (stream,mailbox) : sm_unsubscribe (mailbox);
+}
+
+/* Mail create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mail_create (MAILSTREAM *stream,char *mailbox)
+{
+ MAILSTREAM *ts;
+ char *s,*t,tmp[MAILTMPLEN];
+ size_t i;
+ DRIVER *d;
+ /* never allow names with newlines */
+ if (s = strpbrk (mailbox,"\015\012")) {
+ MM_LOG ("Can't create mailbox with such a name",ERROR);
+ return NIL;
+ }
+ if (strlen (mailbox) >= (NETMAXHOST+(NETMAXUSER*2)+NETMAXMBX+NETMAXSRV+50)) {
+ sprintf (tmp,"Can't create %.80s: %s",mailbox,(*mailbox == '{') ?
+ "invalid remote specification" : "no such mailbox");
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* create of INBOX invalid */
+ if (!compare_cstring (mailbox,"INBOX")) {
+ MM_LOG ("Can't create INBOX",ERROR);
+ return NIL;
+ }
+ /* validate name */
+ if (s = mail_utf7_valid (mailbox)) {
+ sprintf (tmp,"Can't create %s: %.80s",s,mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+
+ /* see if special driver hack */
+ if ((mailbox[0] == '#') && ((mailbox[1] == 'd') || (mailbox[1] == 'D')) &&
+ ((mailbox[2] == 'r') || (mailbox[2] == 'R')) &&
+ ((mailbox[3] == 'i') || (mailbox[3] == 'I')) &&
+ ((mailbox[4] == 'v') || (mailbox[4] == 'V')) &&
+ ((mailbox[5] == 'e') || (mailbox[5] == 'E')) &&
+ ((mailbox[6] == 'r') || (mailbox[6] == 'R')) && (mailbox[7] == '.')) {
+ /* copy driver until likely delimiter */
+ if ((s = strpbrk (t = mailbox+8,"/\\:")) && (i = s - t)) {
+ strncpy (tmp,t,i);
+ tmp[i] = '\0';
+ }
+ else {
+ sprintf (tmp,"Can't create mailbox %.80s: bad driver syntax",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ for (d = maildrivers; d && strcmp (d->name,tmp); d = d->next);
+ if (d) mailbox = ++s; /* skip past driver specification */
+ else {
+ sprintf (tmp,"Can't create mailbox %.80s: unknown driver",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ }
+ /* use stream if one given or deterministic */
+ else if ((stream && stream->dtb) ||
+ (((*mailbox == '{') || (*mailbox == '#')) &&
+ (stream = mail_open (NIL,mailbox,OP_PROTOTYPE | OP_SILENT))))
+ d = stream->dtb;
+ else if ((*mailbox != '{') && (ts = default_proto (NIL))) d = ts->dtb;
+ else { /* failed utterly */
+ sprintf (tmp,"Can't create mailbox %.80s: indeterminate format",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ return (*d->create) (stream,mailbox);
+}
+
+/* Mail delete mailbox
+ * Accepts: mail stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mail_delete (MAILSTREAM *stream,char *mailbox)
+{
+ DRIVER *dtb = mail_valid (stream,mailbox,"delete mailbox");
+ if (!dtb) return NIL;
+ if (((mailbox[0] == 'I') || (mailbox[0] == 'i')) &&
+ ((mailbox[1] == 'N') || (mailbox[1] == 'n')) &&
+ ((mailbox[2] == 'B') || (mailbox[2] == 'b')) &&
+ ((mailbox[3] == 'O') || (mailbox[3] == 'o')) &&
+ ((mailbox[4] == 'X') || (mailbox[4] == 'x')) && !mailbox[5]) {
+ MM_LOG ("Can't delete INBOX",ERROR);
+ return NIL;
+ }
+ return SAFE_DELETE (dtb,stream,mailbox);
+}
+
+
+/* Mail rename mailbox
+ * Accepts: mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long mail_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ char *s,tmp[MAILTMPLEN];
+ DRIVER *dtb = mail_valid (stream,old,"rename mailbox");
+ if (!dtb) return NIL;
+ /* validate name */
+ if (s = mail_utf7_valid (newname)) {
+ sprintf (tmp,"Can't rename to %s: %.80s",s,newname);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ if ((*old != '{') && (*old != '#') && mail_valid (NIL,newname,NIL)) {
+ sprintf (tmp,"Can't rename %.80s: mailbox %.80s already exists",
+ old,newname);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ return SAFE_RENAME (dtb,stream,old,newname);
+}
+
+/* Validate mailbox as Modified UTF-7
+ * Accepts: candidate mailbox name
+ * Returns: error string if error, NIL if valid
+ */
+
+char *mail_utf7_valid (char *mailbox)
+{
+ char *s;
+ for (s = mailbox; *s; s++) { /* make sure valid name */
+ /* reserved for future use with UTF-8 */
+ if (*s & 0x80) return "mailbox name with 8-bit octet";
+ /* validate modified UTF-7 */
+ else if (*s == '&') while (*++s != '-') switch (*s) {
+ case '\0':
+ return "unterminated modified UTF-7 name";
+ case '+': /* valid modified BASE64 */
+ case ',':
+ break; /* all OK so far */
+ default: /* must be alphanumeric */
+ if (!isalnum (*s)) return "invalid modified UTF-7 name";
+ break;
+ }
+ }
+ return NIL; /* all OK */
+}
+
+/* Mail status of mailbox
+ * Accepts: mail stream if open on this mailbox
+ * mailbox name
+ * status flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mail_status (MAILSTREAM *stream,char *mbx,long flags)
+{
+ DRIVER *dtb = mail_valid (stream,mbx,"get status of mailbox");
+ if (!dtb) return NIL; /* only if valid */
+ if (stream && ((dtb != stream->dtb) ||
+ ((dtb->flags & DR_LOCAL) && strcmp (mbx,stream->mailbox) &&
+ strcmp (mbx,stream->original_mailbox))))
+ stream = NIL; /* stream not suitable */
+ return SAFE_STATUS (dtb,stream,mbx,flags);
+}
+
+
+/* Mail status of mailbox default handler
+ * Accepts: mail stream
+ * mailbox name
+ * status flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mail_status_default (MAILSTREAM *stream,char *mbx,long flags)
+{
+ MAILSTATUS status;
+ unsigned long i;
+ MAILSTREAM *tstream = NIL;
+ /* make temporary stream (unless this mbx) */
+ if (!stream && !(stream = tstream =
+ mail_open (NIL,mbx,OP_READONLY|OP_SILENT))) return NIL;
+ status.flags = flags; /* return status values */
+ status.messages = stream->nmsgs;
+ status.recent = stream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++)
+ if (!mail_elt (stream,i)->seen) status.unseen++;
+ status.uidnext = stream->uid_last + 1;
+ status.uidvalidity = stream->uid_validity;
+ MM_STATUS(stream,mbx,&status);/* pass status to main program */
+ if (tstream) mail_close (tstream);
+ return T; /* success */
+}
+
+/* Mail open
+ * Accepts: candidate stream for recycling
+ * mailbox name
+ * open options
+ * Returns: stream to use on success, NIL on failure
+ */
+
+MAILSTREAM *mail_open (MAILSTREAM *stream,char *name,long options)
+{
+ int i;
+ char c,*s,tmp[MAILTMPLEN];
+ NETMBX mb;
+ DRIVER *d;
+ switch (name[0]) { /* see if special handling */
+ case '#': /* possible special hacks */
+ if (((name[1] == 'M') || (name[1] == 'm')) &&
+ ((name[2] == 'O') || (name[2] == 'o')) &&
+ ((name[3] == 'V') || (name[3] == 'v')) &&
+ ((name[4] == 'E') || (name[4] == 'e')) && (c = name[5]) &&
+ (s = strchr (name+6,c)) && (i = s - (name + 6)) && (i < MAILTMPLEN)) {
+ if (stream = mail_open (stream,s+1,options)) {
+ strncpy (tmp,name+6,i); /* copy snarf mailbox name */
+ tmp[i] = '\0'; /* tie off name */
+ mail_parameters (stream,SET_SNARFMAILBOXNAME,(void *) tmp);
+ stream->snarf.options = options;
+ mail_ping (stream); /* do initial snarf */
+ /* punt if can't do initial snarf */
+ if (!stream->snarf.time) stream = mail_close (stream);
+ }
+ return stream;
+ }
+ /* special POP hack */
+ else if (((name[1] == 'P') || (name[1] == 'p')) &&
+ ((name[2] == 'O') || (name[2] == 'o')) &&
+ ((name[3] == 'P') || (name[3] == 'p')) &&
+ mail_valid_net_parse_work (name+4,&mb,"pop3") &&
+ !strcmp (mb.service,"pop3") && !mb.anoflag && !mb.readonlyflag) {
+ if (stream = mail_open (stream,mb.mailbox,options)) {
+ sprintf (tmp,"{%.255s",mb.host);
+ if (mb.port) sprintf (tmp + strlen (tmp),":%lu",mb.port);
+ if (mb.user[0]) sprintf (tmp + strlen (tmp),"/user=%.64s",mb.user);
+ if (mb.dbgflag) strcat (tmp,"/debug");
+ if (mb.secflag) strcat (tmp,"/secure");
+ if (mb.tlsflag) strcat (tmp,"/tls");
+ if (mb.notlsflag) strcat (tmp,"/notls");
+ if (mb.sslflag) strcat (tmp,"/ssl");
+ if (mb.trysslflag) strcat (tmp,"/tryssl");
+ if (mb.novalidate) strcat (tmp,"/novalidate-cert");
+ strcat (tmp,"/pop3/loser}");
+ mail_parameters (stream,SET_SNARFMAILBOXNAME,(void *) tmp);
+ mail_ping (stream); /* do initial snarf */
+ }
+ return stream; /* return local mailbox stream */
+ }
+
+ else if ((options & OP_PROTOTYPE) &&
+ ((name[1] == 'D') || (name[1] == 'd')) &&
+ ((name[2] == 'R') || (name[2] == 'r')) &&
+ ((name[3] == 'I') || (name[3] == 'i')) &&
+ ((name[4] == 'V') || (name[4] == 'v')) &&
+ ((name[5] == 'E') || (name[5] == 'e')) &&
+ ((name[6] == 'R') || (name[6] == 'r')) && (name[7] == '.')) {
+ sprintf (tmp,"%.80s",name+8);
+ /* tie off name at likely delimiter */
+ if (s = strpbrk (tmp,"/\\:")) *s++ = '\0';
+ else {
+ sprintf (tmp,"Can't resolve mailbox %.80s: bad driver syntax",name);
+ MM_LOG (tmp,ERROR);
+ return mail_close (stream);
+ }
+ for (d = maildrivers; d && compare_cstring (d->name,tmp); d = d->next);
+ if (d) return (*d->open) (NIL);
+ sprintf (tmp,"Can't resolve mailbox %.80s: unknown driver",name);
+ MM_LOG (tmp,ERROR);
+ return mail_close (stream);
+ }
+ /* fall through to default case */
+ default: /* not special hack (but could be # name */
+ d = mail_valid (NIL,name,(options & OP_SILENT) ?
+ (char *) NIL : "open mailbox");
+ }
+ return d ? mail_open_work (d,stream,name,options) : stream;
+}
+
+/* Mail open worker routine
+ * Accepts: factory
+ * candidate stream for recycling
+ * mailbox name
+ * open options
+ * Returns: stream to use on success, NIL on failure
+ */
+
+MAILSTREAM *mail_open_work (DRIVER *d,MAILSTREAM *stream,char *name,
+ long options)
+{
+ int i;
+ char tmp[MAILTMPLEN];
+ NETMBX mb;
+ if (options & OP_PROTOTYPE) return (*d->open) (NIL);
+ /* name is copied here in case the caller does a re-open using
+ * stream->mailbox or stream->original_mailbox as the argument.
+ */
+ name = cpystr (name); /* make copy of name */
+ if (stream) { /* recycling requested? */
+ if ((stream->dtb == d) && (d->flags & DR_RECYCLE) &&
+ ((d->flags & DR_HALFOPEN) || !(options & OP_HALFOPEN)) &&
+ mail_usable_network_stream (stream,name)) {
+ /* yes, checkpoint if needed */
+ if (d->flags & DR_XPOINT) mail_check (stream);
+ mail_free_cache (stream); /* clean up stream */
+ if (stream->mailbox) fs_give ((void **) &stream->mailbox);
+ if (stream->original_mailbox)
+ fs_give ((void **) &stream->original_mailbox);
+ /* flush user flags */
+ for (i = 0; i < NUSERFLAGS; i++)
+ if (stream->user_flags[i]) fs_give ((void **) &stream->user_flags[i]);
+ }
+ else { /* stream not recycleable, babble if net */
+ if (!stream->silent && stream->dtb && !(stream->dtb->flags&DR_LOCAL) &&
+ mail_valid_net_parse (stream->mailbox,&mb)) {
+ sprintf (tmp,"Closing connection to %.80s",mb.host);
+ MM_LOG (tmp,(long) NIL);
+ }
+ /* flush the old stream */
+ stream = mail_close (stream);
+ }
+ }
+ /* check if driver does not support halfopen */
+ else if ((options & OP_HALFOPEN) && !(d->flags & DR_HALFOPEN)) {
+ fs_give ((void **) &name);
+ return NIL;
+ }
+
+ /* instantiate new stream if not recycling */
+ if (!stream) (*mailcache) (stream = (MAILSTREAM *)
+ memset (fs_get (sizeof (MAILSTREAM)),0,
+ sizeof (MAILSTREAM)),(long) 0,CH_INIT);
+ stream->dtb = d; /* set dispatch */
+ /* set mailbox name */
+ stream->mailbox = cpystr (stream->original_mailbox = name);
+ /* initialize stream flags */
+ stream->inbox = stream->lock = NIL;
+ stream->debug = (options & OP_DEBUG) ? T : NIL;
+ stream->rdonly = (options & OP_READONLY) ? T : NIL;
+ stream->anonymous = (options & OP_ANONYMOUS) ? T : NIL;
+ stream->scache = (options & OP_SHORTCACHE) ? T : NIL;
+ stream->silent = (options & OP_SILENT) ? T : NIL;
+ stream->halfopen = (options & OP_HALFOPEN) ? T : NIL;
+ stream->secure = (options & OP_SECURE) ? T : NIL;
+ stream->tryssl = (options & OP_TRYSSL) ? T : NIL;
+ stream->mulnewsrc = (options & OP_MULNEWSRC) ? T : NIL;
+ stream->nokod = (options & OP_NOKOD) ? T : NIL;
+ stream->sniff = (options & OP_SNIFF) ? T : NIL;
+ stream->perm_seen = stream->perm_deleted = stream->perm_flagged =
+ stream->perm_answered = stream->perm_draft = stream->kwd_create = NIL;
+ stream->uid_nosticky = (d->flags & DR_NOSTICKY) ? T : NIL;
+ stream->uid_last = 0; /* default UID validity */
+ stream->uid_validity = (unsigned long) time (0);
+ /* have driver open, flush if failed */
+ return ((*d->open) (stream)) ? stream : mail_close (stream);
+}
+
+/* Mail close
+ * Accepts: mail stream
+ * close options
+ * Returns: NIL, always
+ */
+
+MAILSTREAM *mail_close_full (MAILSTREAM *stream,long options)
+{
+ int i;
+ if (stream) { /* make sure argument given */
+ /* do the driver's close action */
+ if (stream->dtb) (*stream->dtb->close) (stream,options);
+ stream->dtb = NIL; /* resign driver */
+ if (stream->mailbox) fs_give ((void **) &stream->mailbox);
+ if (stream->original_mailbox)
+ fs_give ((void **) &stream->original_mailbox);
+ if (stream->snarf.name) fs_give ((void **) &stream->snarf.name);
+ stream->sequence++; /* invalidate sequence */
+ /* flush user flags */
+ for (i = 0; i < NUSERFLAGS; i++)
+ if (stream->user_flags[i]) fs_give ((void **) &stream->user_flags[i]);
+ mail_free_cache (stream); /* finally free the stream's storage */
+ if (mailfreestreamsparep && stream->sparep)
+ (*mailfreestreamsparep) (&stream->sparep);
+ if (!stream->use) fs_give ((void **) &stream);
+ }
+ return NIL;
+}
+
+/* Mail make handle
+ * Accepts: mail stream
+ * Returns: handle
+ *
+ * Handles provide a way to have multiple pointers to a stream yet allow the
+ * stream's owner to nuke it or recycle it.
+ */
+
+MAILHANDLE *mail_makehandle (MAILSTREAM *stream)
+{
+ MAILHANDLE *handle = (MAILHANDLE *) fs_get (sizeof (MAILHANDLE));
+ handle->stream = stream; /* copy stream */
+ /* and its sequence */
+ handle->sequence = stream->sequence;
+ stream->use++; /* let stream know another handle exists */
+ return handle;
+}
+
+
+/* Mail release handle
+ * Accepts: Mail handle
+ */
+
+void mail_free_handle (MAILHANDLE **handle)
+{
+ MAILSTREAM *s;
+ if (*handle) { /* only free if exists */
+ /* resign stream, flush unreferenced zombies */
+ if ((!--(s = (*handle)->stream)->use) && !s->dtb) fs_give ((void **) &s);
+ fs_give ((void **) handle); /* now flush the handle */
+ }
+}
+
+
+/* Mail get stream handle
+ * Accepts: Mail handle
+ * Returns: mail stream or NIL if stream gone
+ */
+
+MAILSTREAM *mail_stream (MAILHANDLE *handle)
+{
+ MAILSTREAM *s = handle->stream;
+ return (s->dtb && (handle->sequence == s->sequence)) ? s : NIL;
+}
+
+/* Mail fetch cache element
+ * Accepts: mail stream
+ * message # to fetch
+ * Returns: cache element of this message
+ * Can also be used to create cache elements for new messages.
+ */
+
+MESSAGECACHE *mail_elt (MAILSTREAM *stream,unsigned long msgno)
+{
+ if (msgno < 1 || msgno > stream->nmsgs) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Bad msgno %lu in mail_elt, nmsgs = %lu, mbx=%.80s",
+ msgno,stream->nmsgs,stream->mailbox ? stream->mailbox : "???");
+ fatal (tmp);
+ }
+ return (MESSAGECACHE *) (*mailcache) (stream,msgno,CH_MAKEELT);
+}
+
+
+/* Mail fetch fast information
+ * Accepts: mail stream
+ * sequence
+ * option flags
+ *
+ * Generally, mail_fetch_structure is preferred
+ */
+
+void mail_fetch_fast (MAILSTREAM *stream,char *sequence,long flags)
+{
+ /* do the driver's action */
+ if (stream->dtb && stream->dtb->fast)
+ (*stream->dtb->fast) (stream,sequence,flags);
+}
+
+
+/* Mail fetch flags
+ * Accepts: mail stream
+ * sequence
+ * option flags
+ */
+
+void mail_fetch_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ /* do the driver's action */
+ if (stream->dtb && stream->dtb->msgflags)
+ (*stream->dtb->msgflags) (stream,sequence,flags);
+}
+
+/* Mail fetch message overview
+ * Accepts: mail stream
+ * UID sequence to fetch
+ * pointer to overview return function
+ */
+
+void mail_fetch_overview (MAILSTREAM *stream,char *sequence,overview_t ofn)
+{
+ if (stream->dtb && mail_uid_sequence (stream,sequence) &&
+ !(stream->dtb->overview && (*stream->dtb->overview) (stream,ofn)) &&
+ mail_ping (stream))
+ mail_fetch_overview_default (stream,ofn);
+}
+
+
+/* Mail fetch message overview using sequence numbers instead of UIDs
+ * Accepts: mail stream
+ * sequence to fetch
+ * pointer to overview return function
+ */
+
+void mail_fetch_overview_sequence (MAILSTREAM *stream,char *sequence,
+ overview_t ofn)
+{
+ if (stream->dtb && mail_sequence (stream,sequence) &&
+ !(stream->dtb->overview && (*stream->dtb->overview) (stream,ofn)) &&
+ mail_ping (stream))
+ mail_fetch_overview_default (stream,ofn);
+}
+
+
+/* Mail fetch message overview default handler
+ * Accepts: mail stream with sequence bits lit
+ * pointer to overview return function
+ */
+
+void mail_fetch_overview_default (MAILSTREAM *stream,overview_t ofn)
+{
+ MESSAGECACHE *elt;
+ ENVELOPE *env;
+ OVERVIEW ov;
+ unsigned long i;
+ ov.optional.lines = 0;
+ ov.optional.xref = NIL;
+ for (i = 1; i <= stream->nmsgs; i++)
+ if (((elt = mail_elt (stream,i))->sequence) &&
+ (env = mail_fetch_structure (stream,i,NIL,NIL)) && ofn) {
+ ov.subject = env->subject;
+ ov.from = env->from;
+ ov.date = env->date;
+ ov.message_id = env->message_id;
+ ov.references = env->references;
+ ov.optional.octets = elt->rfc822_size;
+ (*ofn) (stream,mail_uid (stream,i),&ov,i);
+ }
+}
+
+/* Mail fetch message structure
+ * Accepts: mail stream
+ * message # to fetch
+ * pointer to return body
+ * option flags
+ * Returns: envelope of this message, body returned in body value
+ *
+ * Fetches the "fast" information as well
+ */
+
+ENVELOPE *mail_fetch_structure (MAILSTREAM *stream,unsigned long msgno,
+ BODY **body,long flags)
+{
+ ENVELOPE **env;
+ BODY **b;
+ MESSAGECACHE *elt;
+ char c,*s,*hdr;
+ unsigned long hdrsize;
+ STRING bs;
+ /* do the driver's action if specified */
+ if (stream->dtb && stream->dtb->structure)
+ return (*stream->dtb->structure) (stream,msgno,body,flags);
+ if (flags & FT_UID) { /* UID form of call */
+ if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID;
+ else return NIL; /* must get UID/msgno map first */
+ }
+ elt = mail_elt (stream,msgno);/* get elt for real message number */
+ if (stream->scache) { /* short caching */
+ if (msgno != stream->msgno){/* garbage collect if not same message */
+ mail_gc (stream,GC_ENV | GC_TEXTS);
+ stream->msgno = msgno; /* this is the current message now */
+ }
+ env = &stream->env; /* get pointers to envelope and body */
+ b = &stream->body;
+ }
+ else { /* get pointers to elt envelope and body */
+ env = &elt->private.msg.env;
+ b = &elt->private.msg.body;
+ }
+
+ if (stream->dtb && ((body && !*b) || !*env || (*env)->incomplete)) {
+ mail_free_envelope (env); /* flush old envelope and body */
+ mail_free_body (b);
+ /* see if need to fetch the whole thing */
+ if (body || !elt->rfc822_size) {
+ s = (*stream->dtb->header) (stream,msgno,&hdrsize,flags & ~FT_INTERNAL);
+ /* make copy in case body fetch smashes it */
+ hdr = (char *) memcpy (fs_get ((size_t) hdrsize+1),s,(size_t) hdrsize);
+ hdr[hdrsize] = '\0'; /* tie off header */
+ (*stream->dtb->text) (stream,msgno,&bs,(flags & ~FT_INTERNAL) | FT_PEEK);
+ if (!elt->rfc822_size) elt->rfc822_size = hdrsize + SIZE (&bs);
+ if (body) /* only parse body if requested */
+ rfc822_parse_msg (env,b,hdr,hdrsize,&bs,BADHOST,stream->dtb->flags);
+ else
+ rfc822_parse_msg (env,NIL,hdr,hdrsize,NIL,BADHOST,stream->dtb->flags);
+ fs_give ((void **) &hdr); /* flush header */
+ }
+ else { /* can save memory doing it this way */
+ hdr = (*stream->dtb->header) (stream,msgno,&hdrsize,flags | FT_INTERNAL);
+ if (hdrsize) { /* in case null header */
+ c = hdr[hdrsize]; /* preserve what's there */
+ hdr[hdrsize] = '\0'; /* tie off header */
+ rfc822_parse_msg (env,NIL,hdr,hdrsize,NIL,BADHOST,stream->dtb->flags);
+ hdr[hdrsize] = c; /* restore in case cached data */
+ }
+ else *env = mail_newenvelope ();
+ }
+ }
+ /* if need date, have date in envelope? */
+ if (!elt->day && *env && (*env)->date) mail_parse_date (elt,(*env)->date);
+ /* sigh, fill in bogus default */
+ if (!elt->day) elt->day = elt->month = 1;
+ if (body) *body = *b; /* return the body */
+ return *env; /* return the envelope */
+}
+
+/* Mail mark single message (internal use only)
+ * Accepts: mail stream
+ * elt to mark
+ * fetch flags
+ */
+
+static void markseen (MAILSTREAM *stream,MESSAGECACHE *elt,long flags)
+{
+ unsigned long i;
+ char sequence[20];
+ MESSAGECACHE *e;
+ /* non-peeking and needs to set \Seen? */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ if (stream->dtb->flagmsg){ /* driver wants per-message call? */
+ elt->valid = NIL; /* do pre-alteration driver call */
+ (*stream->dtb->flagmsg) (stream,elt);
+ /* set seen, do post-alteration driver call */
+ elt->seen = elt->valid = T;
+ (*stream->dtb->flagmsg) (stream,elt);
+ }
+ if (stream->dtb->flag) { /* driver wants one-time call? */
+ /* better safe than sorry, save seq bits */
+ for (i = 1; i <= stream->nmsgs; i++) {
+ e = mail_elt (stream,i);
+ e->private.sequence = e->sequence;
+ }
+ /* call driver to set the message */
+ sprintf (sequence,"%lu",elt->msgno);
+ (*stream->dtb->flag) (stream,sequence,"\\Seen",ST_SET);
+ /* restore sequence bits */
+ for (i = 1; i <= stream->nmsgs; i++) {
+ e = mail_elt (stream,i);
+ e->sequence = e->private.sequence;
+ }
+ }
+ /* notify mail program of flag change */
+ MM_FLAGS (stream,elt->msgno);
+ }
+}
+
+/* Mail fetch message
+ * Accepts: mail stream
+ * message # to fetch
+ * pointer to returned length
+ * flags
+ * Returns: message text
+ */
+
+char *mail_fetch_message (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *len,long flags)
+{
+ GETS_DATA md;
+ SIZEDTEXT *t;
+ STRING bs;
+ MESSAGECACHE *elt;
+ char *s,*u;
+ unsigned long i,j;
+ if (len) *len = 0; /* default return size */
+ if (flags & FT_UID) { /* UID form of call */
+ if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID;
+ else return ""; /* must get UID/msgno map first */
+ }
+ /* initialize message data identifier */
+ INIT_GETS (md,stream,msgno,"",0,0);
+ /* is data already cached? */
+ if ((t = &(elt = mail_elt (stream,msgno))->private.msg.full.text)->data) {
+ markseen (stream,elt,flags);/* mark message seen */
+ return mail_fetch_text_return (&md,t,len);
+ }
+ if (!stream->dtb) return ""; /* not in cache, must have live driver */
+ if (stream->dtb->msgdata) return
+ ((*stream->dtb->msgdata) (stream,msgno,"",0,0,NIL,flags) && t->data) ?
+ mail_fetch_text_return (&md,t,len) : "";
+ /* ugh, have to do this the crufty way */
+ u = mail_fetch_header (stream,msgno,NIL,NIL,&i,flags);
+ /* copy in case text method stomps on it */
+ s = (char *) memcpy (fs_get ((size_t) i),u,(size_t) i);
+ if ((*stream->dtb->text) (stream,msgno,&bs,flags)) {
+ t = &stream->text; /* build combined copy */
+ if (t->data) fs_give ((void **) &t->data);
+ t->data = (unsigned char *) fs_get ((t->size = i + SIZE (&bs)) + 1);
+ if (!elt->rfc822_size) elt->rfc822_size = t->size;
+ else if (elt->rfc822_size != t->size) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Calculated RFC822.SIZE (%lu) != reported size (%lu)",
+ t->size,elt->rfc822_size);
+ mm_log (tmp,WARN); /* bug trap */
+ }
+ memcpy (t->data,s,(size_t) i);
+ for (u = (char *) t->data + i, j = SIZE (&bs); j;) {
+ memcpy (u,bs.curpos,bs.cursize);
+ u += bs.cursize; /* update text */
+ j -= bs.cursize;
+ bs.curpos += (bs.cursize -1);
+ bs.cursize = 0;
+ (*bs.dtb->next) (&bs); /* advance to next buffer's worth */
+ }
+ *u = '\0'; /* tie off data */
+ u = mail_fetch_text_return (&md,t,len);
+ }
+ else u = "";
+ fs_give ((void **) &s); /* finished with copy of header */
+ return u;
+}
+
+/* Mail fetch message header
+ * Accepts: mail stream
+ * message # to fetch
+ * MIME section specifier (#.#.#...#)
+ * list of lines to fetch
+ * pointer to returned length
+ * flags
+ * Returns: message header in RFC822 format
+ *
+ * Note: never calls a mailgets routine
+ */
+
+char *mail_fetch_header (MAILSTREAM *stream,unsigned long msgno,char *section,
+ STRINGLIST *lines,unsigned long *len,long flags)
+{
+ STRING bs;
+ BODY *b = NIL;
+ SIZEDTEXT *t = NIL,rt;
+ MESSAGE *m = NIL;
+ MESSAGECACHE *elt;
+ char tmp[MAILTMPLEN];
+ if (len) *len = 0; /* default return size */
+ if (section && (strlen (section) > (MAILTMPLEN - 20))) return "";
+ if (flags & FT_UID) { /* UID form of call */
+ if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID;
+ else return ""; /* must get UID/msgno map first */
+ }
+ elt = mail_elt (stream,msgno);/* get cache data */
+ if (section && *section) { /* nested body header wanted? */
+ if (!((b = mail_body (stream,msgno,section)) &&
+ (b->type == TYPEMESSAGE) && !strcmp (b->subtype,"RFC822")))
+ return ""; /* lose if no body or not MESSAGE/RFC822 */
+ m = b->nested.msg; /* point to nested message */
+ }
+ /* else top-level message header wanted */
+ else m = &elt->private.msg;
+ if (m->header.text.data && mail_match_lines (lines,m->lines,flags)) {
+ if (lines) textcpy (t = &stream->text,&m->header.text);
+ else t = &m->header.text; /* in cache, and cache is valid */
+ markseen (stream,elt,flags);/* mark message seen */
+ }
+
+ else if (stream->dtb) { /* not in cache, has live driver? */
+ if (stream->dtb->msgdata) { /* has driver section fetch? */
+ /* build driver section specifier */
+ if (section && *section) sprintf (tmp,"%s.HEADER",section);
+ else strcpy (tmp,"HEADER");
+ if ((*stream->dtb->msgdata) (stream,msgno,tmp,0,0,lines,flags)) {
+ t = &m->header.text; /* fetch data */
+ /* don't need to postprocess lines */
+ if (m->lines) lines = NIL;
+ else if (lines) textcpy (t = &stream->text,&m->header.text);
+ }
+ }
+ else if (b) { /* nested body wanted? */
+ if (stream->private.search.text) {
+ rt.data = (unsigned char *) stream->private.search.text +
+ b->nested.msg->header.offset;
+ rt.size = b->nested.msg->header.text.size;
+ t = &rt;
+ }
+ else if ((*stream->dtb->text) (stream,msgno,&bs,flags & ~FT_INTERNAL)) {
+ if ((bs.dtb->next == mail_string_next) && !lines) {
+ rt.data = (unsigned char *) bs.curpos + b->nested.msg->header.offset;
+ rt.size = b->nested.msg->header.text.size;
+ if (stream->private.search.string)
+ stream->private.search.text = bs.curpos;
+ t = &rt; /* special hack to avoid extra copy */
+ }
+ else textcpyoffstring (t = &stream->text,&bs,
+ b->nested.msg->header.offset,
+ b->nested.msg->header.text.size);
+ }
+ }
+ else { /* top-level header fetch */
+ /* mark message seen */
+ markseen (stream,elt,flags);
+ if (rt.data = (unsigned char *)
+ (*stream->dtb->header) (stream,msgno,&rt.size,flags)) {
+ /* make a safe copy if need to filter */
+ if (lines) textcpy (t = &stream->text,&rt);
+ else t = &rt; /* top level header */
+ }
+ }
+ }
+ if (!t || !t->data) return "";/* error if no string */
+ /* filter headers if requested */
+ if (lines) t->size = mail_filter ((char *) t->data,t->size,lines,flags);
+ if (len) *len = t->size; /* return size if requested */
+ return (char *) t->data; /* and text */
+}
+
+/* Mail fetch message text
+ * Accepts: mail stream
+ * message # to fetch
+ * MIME section specifier (#.#.#...#)
+ * pointer to returned length
+ * flags
+ * Returns: message text
+ */
+
+char *mail_fetch_text (MAILSTREAM *stream,unsigned long msgno,char *section,
+ unsigned long *len,long flags)
+{
+ GETS_DATA md;
+ PARTTEXT *p;
+ STRING bs;
+ MESSAGECACHE *elt;
+ BODY *b = NIL;
+ char tmp[MAILTMPLEN];
+ unsigned long i;
+ if (len) *len = 0; /* default return size */
+ memset (&stream->private.string,NIL,sizeof (STRING));
+ if (section && (strlen (section) > (MAILTMPLEN - 20))) return "";
+ if (flags & FT_UID) { /* UID form of call */
+ if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID;
+ else return ""; /* must get UID/msgno map first */
+ }
+ elt = mail_elt (stream,msgno);/* get cache data */
+ if (section && *section) { /* nested body text wanted? */
+ if (!((b = mail_body (stream,msgno,section)) &&
+ (b->type == TYPEMESSAGE) && !strcmp (b->subtype,"RFC822")))
+ return ""; /* lose if no body or not MESSAGE/RFC822 */
+ p = &b->nested.msg->text; /* point at nested message */
+ /* build IMAP-format section specifier */
+ sprintf (tmp,"%s.TEXT",section);
+ flags &= ~FT_INTERNAL; /* can't win with this set */
+ }
+ else { /* top-level message text wanted */
+ p = &elt->private.msg.text;
+ strcpy (tmp,"TEXT");
+ }
+ /* initialize message data identifier */
+ INIT_GETS (md,stream,msgno,section,0,0);
+ if (p->text.data) { /* is data already cached? */
+ markseen (stream,elt,flags);/* mark message seen */
+ return mail_fetch_text_return (&md,&p->text,len);
+ }
+ if (!stream->dtb) return ""; /* not in cache, must have live driver */
+ if (stream->dtb->msgdata) return
+ ((*stream->dtb->msgdata) (stream,msgno,tmp,0,0,NIL,flags) && p->text.data)?
+ mail_fetch_text_return (&md,&p->text,len) : "";
+ if (!(*stream->dtb->text) (stream,msgno,&bs,flags)) return "";
+ if (section && *section) { /* nested is more complex */
+ SETPOS (&bs,p->offset);
+ i = p->text.size; /* just want this much */
+ }
+ else i = SIZE (&bs); /* want entire text */
+ return mail_fetch_string_return (&md,&bs,i,len,flags);
+}
+
+/* Mail fetch message body part MIME headers
+ * Accepts: mail stream
+ * message # to fetch
+ * MIME section specifier (#.#.#...#)
+ * pointer to returned length
+ * flags
+ * Returns: message text
+ */
+
+char *mail_fetch_mime (MAILSTREAM *stream,unsigned long msgno,char *section,
+ unsigned long *len,long flags)
+{
+ PARTTEXT *p;
+ STRING bs;
+ BODY *b;
+ char tmp[MAILTMPLEN];
+ if (len) *len = 0; /* default return size */
+ if (section && (strlen (section) > (MAILTMPLEN - 20))) return "";
+ if (flags & FT_UID) { /* UID form of call */
+ if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID;
+ else return ""; /* must get UID/msgno map first */
+ }
+ flags &= ~FT_INTERNAL; /* can't win with this set */
+ if (!(section && *section && (b = mail_body (stream,msgno,section))))
+ return ""; /* not valid section */
+ /* in cache? */
+ if ((p = &b->mime)->text.data) {
+ /* mark message seen */
+ markseen (stream,mail_elt (stream,msgno),flags);
+ if (len) *len = p->text.size;
+ return (char *) p->text.data;
+ }
+ if (!stream->dtb) return ""; /* not in cache, must have live driver */
+ if (stream->dtb->msgdata) { /* has driver fetch? */
+ /* build driver section specifier */
+ sprintf (tmp,"%s.MIME",section);
+ if ((*stream->dtb->msgdata) (stream,msgno,tmp,0,0,NIL,flags) &&
+ p->text.data) {
+ if (len) *len = p->text.size;
+ return (char *) p->text.data;
+ }
+ else return "";
+ }
+ if (len) *len = b->mime.text.size;
+ if (!b->mime.text.size) { /* empty MIME header -- mark seen anyway */
+ markseen (stream,mail_elt (stream,msgno),flags);
+ return "";
+ }
+ /* have to get it from offset */
+ if (stream->private.search.text)
+ return stream->private.search.text + b->mime.offset;
+ if (!(*stream->dtb->text) (stream,msgno,&bs,flags)) {
+ if (len) *len = 0;
+ return "";
+ }
+ if (bs.dtb->next == mail_string_next) {
+ if (stream->private.search.string) stream->private.search.text = bs.curpos;
+ return bs.curpos + b->mime.offset;
+ }
+ return textcpyoffstring (&stream->text,&bs,b->mime.offset,b->mime.text.size);
+}
+
+/* Mail fetch message body part
+ * Accepts: mail stream
+ * message # to fetch
+ * MIME section specifier (#.#.#...#)
+ * pointer to returned length
+ * flags
+ * Returns: message body
+ */
+
+char *mail_fetch_body (MAILSTREAM *stream,unsigned long msgno,char *section,
+ unsigned long *len,long flags)
+{
+ GETS_DATA md;
+ PARTTEXT *p;
+ STRING bs;
+ BODY *b;
+ SIZEDTEXT *t;
+ char *s,tmp[MAILTMPLEN];
+ memset (&stream->private.string,NIL,sizeof (STRING));
+ if (!(section && *section)) /* top-level text wanted? */
+ return mail_fetch_message (stream,msgno,len,flags);
+ else if (strlen (section) > (MAILTMPLEN - 20)) return "";
+ flags &= ~FT_INTERNAL; /* can't win with this set */
+ /* initialize message data identifier */
+ INIT_GETS (md,stream,msgno,section,0,0);
+ /* kludge for old section 0 header */
+ if (!strcmp (s = strcpy (tmp,section),"0") ||
+ ((s = strstr (tmp,".0")) && !s[2])) {
+ SIZEDTEXT ht;
+ *s = '\0'; /* tie off section */
+ /* this silly way so it does mailgets */
+ ht.data = (unsigned char *) mail_fetch_header (stream,msgno,
+ tmp[0] ? tmp : NIL,NIL,
+ &ht.size,flags);
+ /* may have UIDs here */
+ md.flags = (flags & FT_UID) ? MG_UID : NIL;
+ return mail_fetch_text_return (&md,&ht,len);
+ }
+ if (len) *len = 0; /* default return size */
+ if (flags & FT_UID) { /* UID form of call */
+ if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID;
+ else return ""; /* must get UID/msgno map first */
+ }
+ /* must have body */
+ if (!(b = mail_body (stream,msgno,section))) return "";
+ /* have cached text? */
+ if ((t = &(p = &b->contents)->text)->data) {
+ /* mark message seen */
+ markseen (stream,mail_elt (stream,msgno),flags);
+ return mail_fetch_text_return (&md,t,len);
+ }
+ if (!stream->dtb) return ""; /* not in cache, must have live driver */
+ if (stream->dtb->msgdata) return
+ ((*stream->dtb->msgdata)(stream,msgno,section,0,0,NIL,flags) && t->data) ?
+ mail_fetch_text_return (&md,t,len) : "";
+ if (len) *len = t->size;
+ if (!t->size) { /* empty body part -- mark seen anyway */
+ markseen (stream,mail_elt (stream,msgno),flags);
+ return "";
+ }
+ /* copy body from stringstruct offset */
+ if (stream->private.search.text)
+ return stream->private.search.text + p->offset;
+ if (!(*stream->dtb->text) (stream,msgno,&bs,flags)) {
+ if (len) *len = 0;
+ return "";
+ }
+ if (bs.dtb->next == mail_string_next) {
+ if (stream->private.search.string) stream->private.search.text = bs.curpos;
+ return bs.curpos + p->offset;
+ }
+ SETPOS (&bs,p->offset);
+ return mail_fetch_string_return (&md,&bs,t->size,len,flags);
+}
+
+/* Mail fetch partial message text
+ * Accepts: mail stream
+ * message # to fetch
+ * MIME section specifier (#.#.#...#)
+ * offset of first designed byte or 0 to start at beginning
+ * maximum number of bytes or 0 for all bytes
+ * flags
+ * Returns: T if successful, else NIL
+ */
+
+long mail_partial_text (MAILSTREAM *stream,unsigned long msgno,char *section,
+ unsigned long first,unsigned long last,long flags)
+{
+ GETS_DATA md;
+ PARTTEXT *p = NIL;
+ MESSAGECACHE *elt;
+ STRING bs;
+ BODY *b;
+ char tmp[MAILTMPLEN];
+ unsigned long i;
+ if (!mailgets) fatal ("mail_partial_text() called without a mailgets!");
+ if (section && (strlen (section) > (MAILTMPLEN - 20))) return NIL;
+ if (flags & FT_UID) { /* UID form of call */
+ if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID;
+ else return NIL; /* must get UID/msgno map first */
+ }
+ elt = mail_elt (stream,msgno);/* get cache data */
+ flags &= ~FT_INTERNAL; /* bogus if this is set */
+ if (section && *section) { /* nested body text wanted? */
+ if (!((b = mail_body (stream,msgno,section)) &&
+ (b->type == TYPEMESSAGE) && !strcmp (b->subtype,"RFC822")))
+ return NIL; /* lose if no body or not MESSAGE/RFC822 */
+ p = &b->nested.msg->text; /* point at nested message */
+ /* build IMAP-format section specifier */
+ sprintf (tmp,"%s.TEXT",section);
+ }
+ else { /* else top-level message text wanted */
+ p = &elt->private.msg.text;
+ strcpy (tmp,"TEXT");
+ }
+
+ /* initialize message data identifier */
+ INIT_GETS (md,stream,msgno,tmp,first,last);
+ if (p->text.data) { /* is data already cached? */
+ INIT (&bs,mail_string,p->text.data,i = p->text.size);
+ markseen (stream,elt,flags);/* mark message seen */
+ }
+ else { /* else get data from driver */
+ if (!stream->dtb) return NIL;
+ if (stream->dtb->msgdata) /* driver will handle this */
+ return (*stream->dtb->msgdata) (stream,msgno,tmp,first,last,NIL,flags);
+ if (!(*stream->dtb->text) (stream,msgno,&bs,flags)) return NIL;
+ if (section && *section) { /* nexted if more complex */
+ SETPOS (&bs,p->offset); /* offset stringstruct to data */
+ i = p->text.size; /* maximum size of data */
+ }
+ else i = SIZE (&bs); /* just want this much */
+ }
+ if (i <= first) i = first = 0;/* first byte is beyond end of text */
+ /* truncate as needed */
+ else { /* offset and truncate */
+ SETPOS (&bs,first + GETPOS (&bs));
+ i -= first; /* reduced size */
+ if (last && (i > last)) i = last;
+ }
+ /* do the mailgets thing */
+ (*mailgets) (mail_read,&bs,i,&md);
+ return T; /* success */
+}
+
+/* Mail fetch partial message body part
+ * Accepts: mail stream
+ * message # to fetch
+ * MIME section specifier (#.#.#...#)
+ * offset of first designed byte or 0 to start at beginning
+ * maximum number of bytes or 0 for all bytes
+ * flags
+ * Returns: T if successful, else NIL
+ */
+
+long mail_partial_body (MAILSTREAM *stream,unsigned long msgno,char *section,
+ unsigned long first,unsigned long last,long flags)
+{
+ GETS_DATA md;
+ PARTTEXT *p;
+ STRING bs;
+ BODY *b;
+ SIZEDTEXT *t;
+ unsigned long i;
+ if (!(section && *section)) /* top-level text wanted? */
+ return mail_partial_text (stream,msgno,NIL,first,last,flags);
+ if (!mailgets) fatal ("mail_partial_body() called without a mailgets!");
+ if (flags & FT_UID) { /* UID form of call */
+ if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID;
+ else return NIL; /* must get UID/msgno map first */
+ }
+ /* must have body */
+ if (!(b = mail_body (stream,msgno,section))) return NIL;
+ flags &= ~FT_INTERNAL; /* bogus if this is set */
+
+ /* initialize message data identifier */
+ INIT_GETS (md,stream,msgno,section,first,last);
+ /* have cached text? */
+ if ((t = &(p = &b->contents)->text)->data) {
+ /* mark message seen */
+ markseen (stream,mail_elt (stream,msgno),flags);
+ INIT (&bs,mail_string,t->data,i = t->size);
+ }
+ else { /* else get data from driver */
+ if (!stream->dtb) return NIL;
+ if (stream->dtb->msgdata) /* driver will handle this */
+ return (*stream->dtb->msgdata) (stream,msgno,section,first,last,NIL,
+ flags);
+ if (!(*stream->dtb->text) (stream,msgno,&bs,flags)) return NIL;
+ if (section && *section) { /* nexted if more complex */
+ SETPOS (&bs,p->offset); /* offset stringstruct to data */
+ i = t->size; /* maximum size of data */
+ }
+ else i = SIZE (&bs); /* just want this much */
+ }
+ if (i <= first) i = first = 0;/* first byte is beyond end of text */
+ else { /* offset and truncate */
+ SETPOS (&bs,first + GETPOS (&bs));
+ i -= first; /* reduced size */
+ if (last && (i > last)) i = last;
+ }
+ /* do the mailgets thing */
+ (*mailgets) (mail_read,&bs,i,&md);
+ return T; /* success */
+}
+
+/* Mail return message text
+ * Accepts: identifier data
+ * sized text
+ * pointer to returned length
+ * Returns: text
+ */
+
+char *mail_fetch_text_return (GETS_DATA *md,SIZEDTEXT *t,unsigned long *len)
+{
+ STRING bs;
+ if (len) *len = t->size; /* return size */
+ if (t->size && mailgets) { /* have to do the mailgets thing? */
+ /* silly but do it anyway for consistency */
+ INIT (&bs,mail_string,t->data,t->size);
+ return (*mailgets) (mail_read,&bs,t->size,md);
+ }
+ return t->size ? (char *) t->data : "";
+}
+
+
+/* Mail return message string
+ * Accepts: identifier data
+ * stringstruct
+ * text length
+ * pointer to returned length
+ * flags
+ * Returns: text, or NIL if stringstruct returned
+ */
+
+char *mail_fetch_string_return (GETS_DATA *md,STRING *bs,unsigned long i,
+ unsigned long *len,long flags)
+{
+ char *ret = NIL;
+ if (len) *len = i; /* return size */
+ /* return stringstruct hack */
+ if (flags & FT_RETURNSTRINGSTRUCT) {
+ memcpy (&md->stream->private.string,bs,sizeof (STRING));
+ SETPOS (&md->stream->private.string,GETPOS (&md->stream->private.string));
+ }
+ /* have to do the mailgets thing? */
+ else if (mailgets) ret = (*mailgets) (mail_read,bs,i,md);
+ /* special hack to avoid extra copy */
+ else if (bs->dtb->next == mail_string_next) ret = bs->curpos;
+ /* make string copy in memory */
+ else ret = textcpyoffstring (&md->stream->text,bs,GETPOS (bs),i);
+ return ret;
+}
+
+/* Read data from stringstruct
+ * Accepts: stringstruct
+ * size of data to read
+ * buffer to read into
+ * Returns: T, always, stringstruct updated
+ */
+
+long mail_read (void *stream,unsigned long size,char *buffer)
+{
+ unsigned long i;
+ STRING *s = (STRING *) stream;
+ while (size) { /* until satisfied */
+ memcpy (buffer,s->curpos,i = min (s->cursize,size));
+ buffer += i; /* update buffer */
+ size -= i; /* note that we read this much */
+ s->curpos += --i; /* advance that many spaces minus 1 */
+ s->cursize -= i;
+ SNX (s); /* now use SNX to advance the last byte */
+ }
+ return T;
+}
+
+/* Mail fetch UID
+ * Accepts: mail stream
+ * message number
+ * Returns: UID or zero if dead stream
+ */
+
+unsigned long mail_uid (MAILSTREAM *stream,unsigned long msgno)
+{
+ unsigned long uid = mail_elt (stream,msgno)->private.uid;
+ return uid ? uid :
+ (stream->dtb && stream->dtb->uid) ? (*stream->dtb->uid) (stream,msgno) : 0;
+}
+
+
+/* Mail fetch msgno from UID
+ * Accepts: mail stream
+ * UID
+ * Returns: msgno or zero if failed
+ */
+
+unsigned long mail_msgno (MAILSTREAM *stream,unsigned long uid)
+{
+ unsigned long msgno,delta,first,firstuid,last,lastuid,middle,miduid;
+ if (stream->dtb) { /* active stream? */
+ if (stream->dtb->msgno) /* direct way */
+ return (*stream->dtb->msgno) (stream,uid);
+ else if (stream->dtb->uid) {/* indirect way */
+ /* Placeholder for now, since currently there are no drivers which
+ * have a uid method but not a msgno method
+ */
+ for (msgno = 1; msgno <= stream->nmsgs; msgno++)
+ if ((*stream->dtb->uid) (stream,msgno) == uid) return msgno;
+ }
+ /* binary search since have full map */
+ else for (first = 1,last = stream->nmsgs, delta = (first <= last) ? 1 : 0;
+ delta &&
+ (uid >= (firstuid = mail_elt (stream,first)->private.uid)) &&
+ (uid <= (lastuid = mail_elt (stream,last)->private.uid));) {
+ /* done if match at an endpoint */
+ if (uid == firstuid) return first;
+ if (uid == lastuid) return last;
+ /* have anything between endpoints? */
+ if (delta = ((last - first) / 2)) {
+ if ((miduid = mail_elt (stream,middle = first + delta)->private.uid)
+ == uid)
+ return middle; /* found match in middle */
+ else if (uid < miduid) last = middle - 1;
+ else first = middle + 1;
+ }
+ }
+ }
+ else { /* dead stream, do linear search for UID */
+ for (msgno = 1; msgno <= stream->nmsgs; msgno++)
+ if (mail_elt (stream,msgno)->private.uid == uid) return msgno;
+ }
+ return 0; /* didn't find the UID anywhere */
+}
+
+/* Mail fetch From string for menu
+ * Accepts: destination string
+ * mail stream
+ * message # to fetch
+ * desired string length
+ * Returns: string of requested length
+ */
+
+void mail_fetchfrom (char *s,MAILSTREAM *stream,unsigned long msgno,
+ long length)
+{
+ char *t;
+ char tmp[MAILTMPLEN];
+ ENVELOPE *env = mail_fetchenvelope (stream,msgno);
+ ADDRESS *adr = env ? env->from : NIL;
+ memset (s,' ',(size_t)length);/* fill it with spaces */
+ s[length] = '\0'; /* tie off with null */
+ /* get first from address from envelope */
+ while (adr && !adr->host) adr = adr->next;
+ if (adr) { /* if a personal name exists use it */
+ if (!(t = adr->personal))
+ sprintf (t = tmp,"%.256s@%.256s",adr->mailbox,adr->host);
+ memcpy (s,t,(size_t) min (length,(long) strlen (t)));
+ }
+}
+
+
+/* Mail fetch Subject string for menu
+ * Accepts: destination string
+ * mail stream
+ * message # to fetch
+ * desired string length
+ * Returns: string of no more than requested length
+ */
+
+void mail_fetchsubject (char *s,MAILSTREAM *stream,unsigned long msgno,
+ long length)
+{
+ ENVELOPE *env = mail_fetchenvelope (stream,msgno);
+ memset (s,'\0',(size_t) length+1);
+ /* copy subject from envelope */
+ if (env && env->subject) strncpy (s,env->subject,(size_t) length);
+ else *s = ' '; /* if no subject then just a space */
+}
+
+/* Mail modify flags
+ * Accepts: mail stream
+ * sequence
+ * flag(s)
+ * option flags
+ */
+
+void mail_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned long i,uf;
+ long f;
+ short nf;
+ if (!stream->dtb) return; /* no-op if no stream */
+ if ((stream->dtb->flagmsg || !stream->dtb->flag) &&
+ ((flags & ST_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) &&
+ ((f = mail_parse_flags (stream,flag,&uf)) || uf))
+ for (i = 1,nf = (flags & ST_SET) ? T : NIL; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ struct { /* old flags */
+ unsigned int valid : 1;
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.valid = elt->valid; old.seen = elt->seen;
+ old.deleted = elt->deleted; old.flagged = elt->flagged;
+ old.answered = elt->answered; old.draft = elt->draft;
+ old.user_flags = elt->user_flags;
+ elt->valid = NIL; /* prepare for flag alteration */
+ if (stream->dtb->flagmsg) (*stream->dtb->flagmsg) (stream,elt);
+ if (f&fSEEN) elt->seen = nf;
+ if (f&fDELETED) elt->deleted = nf;
+ if (f&fFLAGGED) elt->flagged = nf;
+ if (f&fANSWERED) elt->answered = nf;
+ if (f&fDRAFT) elt->draft = nf;
+ /* user flags */
+ if (flags & ST_SET) elt->user_flags |= uf;
+ else elt->user_flags &= ~uf;
+ elt->valid = T; /* flags now altered */
+ if ((old.valid != elt->valid) || (old.seen != elt->seen) ||
+ (old.deleted != elt->deleted) || (old.flagged != elt->flagged) ||
+ (old.answered != elt->answered) || (old.draft != elt->draft) ||
+ (old.user_flags != elt->user_flags))
+ MM_FLAGS (stream,elt->msgno);
+ if (stream->dtb->flagmsg) (*stream->dtb->flagmsg) (stream,elt);
+ }
+ /* call driver once */
+ if (stream->dtb->flag) (*stream->dtb->flag) (stream,sequence,flag,flags);
+}
+
+/* Mail search for messages
+ * Accepts: mail stream
+ * character set
+ * search program
+ * option flags
+ * Returns: T if successful, NIL if dead stream, NIL searchpgm or bad charset
+ */
+
+long mail_search_full (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,
+ long flags)
+{
+ unsigned long i;
+ long ret = NIL;
+ if (!(flags & SE_RETAIN)) /* clear search vector unless retaining */
+ for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched = NIL;
+ if (pgm && stream->dtb) /* must have a search program and driver */
+ ret = (*(stream->dtb->search ? stream->dtb->search : mail_search_default))
+ (stream,charset,pgm,flags);
+ /* flush search program if requested */
+ if (flags & SE_FREE) mail_free_searchpgm (&pgm);
+ return ret;
+}
+
+
+/* Mail search for messages default handler
+ * Accepts: mail stream
+ * character set
+ * search program
+ * option flags
+ * Returns: T if successful, NIL if bad charset
+ */
+
+long mail_search_default (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,
+ long flags)
+{
+ unsigned long i;
+ char *msg;
+ /* make sure that charset is good */
+ if (msg = utf8_badcharset (charset)) {
+ MM_LOG (msg,ERROR); /* output error */
+ fs_give ((void **) &msg);
+ return NIL;
+ }
+ utf8_searchpgm (pgm,charset);
+ for (i = 1; i <= stream->nmsgs; ++i)
+ if (mail_search_msg (stream,i,NIL,pgm)) {
+ if (flags & SE_UID) mm_searched (stream,mail_uid (stream,i));
+ else { /* mark as searched, notify mail program */
+ mail_elt (stream,i)->searched = T;
+ if (!stream->silent) mm_searched (stream,i);
+ }
+ }
+ return LONGT; /* search completed */
+}
+
+/* Mail ping mailbox
+ * Accepts: mail stream
+ * Returns: stream if still open else NIL
+ */
+
+long mail_ping (MAILSTREAM *stream)
+{
+ unsigned long i,n,uf,len;
+ char *s,*f,tmp[MAILTMPLEN],flags[MAILTMPLEN];
+ MAILSTREAM *snarf;
+ MESSAGECACHE *elt;
+ STRING bs;
+ long ret;
+ /* do driver action */
+ if ((ret = ((stream && stream->dtb) ? (stream->dtb->ping) (stream) : NIL)) &&
+ stream->snarf.name && /* time to snarf? */
+ /* prohibit faster than once/min */
+ (time (0) > (time_t) (stream->snarf.time + min(60,mailsnarfinterval))) &&
+ (snarf = mail_open (NIL,stream->snarf.name,
+ stream->snarf.options | OP_SILENT))) {
+ if ((n = snarf->nmsgs) && /* yes, have messages to snarf? */
+ mail_search_full (snarf,NIL,mail_criteria ("UNDELETED"),SE_FREE)) {
+ for (i = 1; ret && (i <= n); i++) /* for each message */
+ if ((elt = mail_elt (snarf,i))->searched &&
+ (s = mail_fetch_message (snarf,i,&len,FT_PEEK)) && len) {
+ INIT (&bs,mail_string,s,len);
+ if (mailsnarfpreserve) {
+ /* yes, make sure have fast data */
+ if (!elt->valid || !elt->day) {
+ sprintf (tmp,"%lu",n);
+ mail_fetch_fast (snarf,tmp,NIL);
+ }
+ /* initialize flag string */
+ memset (flags,0,MAILTMPLEN);
+ /* output system flags except \Deleted */
+ if (elt->seen) strcat (flags," \\Seen");
+ if (elt->flagged) strcat (flags," \\Flagged");
+ if (elt->answered) strcat (flags," \\Answered");
+ if (elt->draft) strcat (flags," \\Draft");
+ /* any user flags? */
+ for (uf = elt->user_flags,s = flags + strlen (flags);
+ uf && (f = stream->user_flags[find_rightmost_bit (&uf)]) &&
+ ((MAILTMPLEN - (s - tmp)) > (long) (2 + strlen (f)));
+ s += strlen (s)) sprintf (s," %s",f);
+ ret = mail_append_full (stream,stream->mailbox,flags + 1,
+ mail_date (tmp,elt),&bs);
+ }
+ else ret = mail_append (stream,stream->mailbox,&bs);
+
+ if (ret) { /* did snarf succeed? */
+ /* driver has per-message (or no) flag call */
+ if (snarf->dtb->flagmsg || !snarf->dtb->flag) {
+ elt->valid = NIL; /* prepare for flag alteration */
+ if (snarf->dtb->flagmsg) (*snarf->dtb->flagmsg) (snarf,elt);
+ /* flags now altered */
+ elt->deleted = elt->seen = elt->valid = T;
+ if (snarf->dtb->flagmsg) (*snarf->dtb->flagmsg) (snarf,elt);
+ }
+ /* driver has one-time flag call */
+ if (snarf->dtb->flag) {
+ sprintf (tmp,"%lu",i);
+ (*snarf->dtb->flag) (snarf,tmp,"\\Deleted \\Seen",ST_SET);
+ }
+ }
+ else { /* copy failed */
+ sprintf (tmp,"Unable to move message %lu from %s mailbox",
+ i,snarf->dtb->name);
+ mm_log (tmp,WARN);
+ }
+ }
+ }
+ /* expunge the messages */
+ mail_close_full (snarf,n ? CL_EXPUNGE : NIL);
+ stream->snarf.time = (unsigned long) time (0);
+ /* Even if the snarf failed, we don't want to return NIL if the stream
+ * is still alive. Or at least that's what we currently think.
+ */
+ /* redo the driver's action */
+ ret = stream->dtb ? (*stream->dtb->ping) (stream) : NIL;
+ }
+ return ret;
+}
+
+/* Mail check mailbox
+ * Accepts: mail stream
+ */
+
+void mail_check (MAILSTREAM *stream)
+{
+ /* do the driver's action */
+ if (stream->dtb) (*stream->dtb->check) (stream);
+}
+
+
+/* Mail expunge mailbox
+ * Accepts: mail stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T on success, NIL on failure
+ */
+
+long mail_expunge_full (MAILSTREAM *stream,char *sequence,long options)
+{
+ /* do the driver's action */
+ return stream->dtb ? (*stream->dtb->expunge) (stream,sequence,options) : NIL;
+}
+
+
+/* Mail copy message(s)
+ * Accepts: mail stream
+ * sequence
+ * destination mailbox
+ * flags
+ */
+
+long mail_copy_full (MAILSTREAM *stream,char *sequence,char *mailbox,
+ long options)
+{
+ return stream->dtb ?
+ SAFE_COPY (stream->dtb,stream,sequence,mailbox,options) : NIL;
+}
+
+/* Append data package to use for old single-message mail_append() interface */
+
+typedef struct mail_append_package {
+ char *flags; /* initial flags */
+ char *date; /* message internal date */
+ STRING *message; /* stringstruct of message */
+} APPENDPACKAGE;
+
+
+/* Single append message string
+ * Accepts: mail stream
+ * package pointer (cast as a void *)
+ * pointer to return initial flags
+ * pointer to return message internal date
+ * pointer to return stringstruct of message to append
+ * Returns: T, always
+ */
+
+static long mail_append_single (MAILSTREAM *stream,void *data,char **flags,
+ char **date,STRING **message)
+{
+ APPENDPACKAGE *ap = (APPENDPACKAGE *) data;
+ *flags = ap->flags; /* get desired data from the package */
+ *date = ap->date;
+ *message = ap->message;
+ ap->message = NIL; /* so next callback puts a stop to it */
+ return LONGT; /* always return success */
+}
+
+
+/* Mail append message string
+ * Accepts: mail stream
+ * destination mailbox
+ * initial flags
+ * message internal date
+ * stringstruct of message to append
+ * Returns: T on success, NIL on failure
+ */
+
+long mail_append_full (MAILSTREAM *stream,char *mailbox,char *flags,char *date,
+ STRING *message)
+{
+ APPENDPACKAGE ap;
+ ap.flags = flags; /* load append package */
+ ap.date = date;
+ ap.message = message;
+ return mail_append_multiple (stream,mailbox,mail_append_single,(void *) &ap);
+}
+
+/* Mail append message(s)
+ * Accepts: mail stream
+ * destination mailbox
+ * append data callback
+ * arbitrary data for callback use
+ * Returns: T on success, NIL on failure
+ */
+
+long mail_append_multiple (MAILSTREAM *stream,char *mailbox,append_t af,
+ void *data)
+{
+ char *s,tmp[MAILTMPLEN];
+ DRIVER *d = NIL;
+ long ret = NIL;
+ /* never allow names with newlines */
+ if (strpbrk (mailbox,"\015\012"))
+ MM_LOG ("Can't append to mailbox with such a name",ERROR);
+ else if (strlen (mailbox) >=
+ (NETMAXHOST+(NETMAXUSER*2)+NETMAXMBX+NETMAXSRV+50)) {
+ sprintf (tmp,"Can't append %.80s: %s",mailbox,(*mailbox == '{') ?
+ "invalid remote specification" : "no such mailbox");
+ MM_LOG (tmp,ERROR);
+ }
+ /* special driver hack? */
+ else if (!strncmp (lcase (strcpy (tmp,mailbox)),"#driver.",8)) {
+ /* yes, tie off name at likely delimiter */
+ if (!(s = strpbrk (tmp+8,"/\\:"))) {
+ sprintf (tmp,"Can't append to mailbox %.80s: bad driver syntax",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ *s++ = '\0'; /* tie off at delimiter */
+ if (!(d = (DRIVER *) mail_parameters (NIL,GET_DRIVER,tmp+8))) {
+ sprintf (tmp,"Can't append to mailbox %.80s: unknown driver",mailbox);
+ MM_LOG (tmp,ERROR);
+ }
+ else ret = SAFE_APPEND (d,stream,mailbox + (s - tmp),af,data);
+ }
+ else if (d = mail_valid (stream,mailbox,NIL))
+ ret = SAFE_APPEND (d,stream,mailbox,af,data);
+ /* No driver, try for TRYCREATE if no stream. Note that we use the
+ * createProto here, not the appendProto, since the dummy driver already
+ * took care of the appendProto case. Otherwise, if appendProto is set to
+ * NIL, we won't get a TRYCREATE.
+ */
+ else if (!stream && (stream = default_proto (NIL)) && stream->dtb &&
+ SAFE_APPEND (stream->dtb,stream,mailbox,af,data))
+ /* timing race? */
+ MM_NOTIFY (stream,"Append validity confusion",WARN);
+ /* generate error message */
+ else mail_valid (stream,mailbox,"append to mailbox");
+ return ret;
+}
+
+/* Mail garbage collect stream
+ * Accepts: mail stream
+ * garbage collection flags
+ */
+
+void mail_gc (MAILSTREAM *stream,long gcflags)
+{
+ MESSAGECACHE *elt;
+ unsigned long i;
+ /* do the driver's action first */
+ if (stream->dtb && stream->dtb->gc) (*stream->dtb->gc) (stream,gcflags);
+ stream->msgno = 0; /* nothing cached now */
+ if (gcflags & GC_ENV) { /* garbage collect envelopes? */
+ if (stream->env) mail_free_envelope (&stream->env);
+ if (stream->body) mail_free_body (&stream->body);
+ }
+ if (gcflags & GC_TEXTS) { /* free texts */
+ if (stream->text.data) fs_give ((void **) &stream->text.data);
+ stream->text.size = 0;
+ }
+ /* garbage collect per-message stuff */
+ for (i = 1; i <= stream->nmsgs; i++)
+ if (elt = (MESSAGECACHE *) (*mailcache) (stream,i,CH_ELT))
+ mail_gc_msg (&elt->private.msg,gcflags);
+}
+
+
+/* Mail garbage collect message
+ * Accepts: message structure
+ * garbage collection flags
+ */
+
+void mail_gc_msg (MESSAGE *msg,long gcflags)
+{
+ if (gcflags & GC_ENV) { /* garbage collect envelopes? */
+ mail_free_envelope (&msg->env);
+ mail_free_body (&msg->body);
+ }
+ if (gcflags & GC_TEXTS) { /* garbage collect texts */
+ if (msg->full.text.data) fs_give ((void **) &msg->full.text.data);
+ if (msg->header.text.data) {
+ mail_free_stringlist (&msg->lines);
+ fs_give ((void **) &msg->header.text.data);
+ }
+ if (msg->text.text.data) fs_give ((void **) &msg->text.text.data);
+ /* now GC all body components */
+ if (msg->body) mail_gc_body (msg->body);
+ }
+}
+
+/* Mail garbage collect texts in BODY structure
+ * Accepts: BODY structure
+ */
+
+void mail_gc_body (BODY *body)
+{
+ PART *part;
+ switch (body->type) { /* free contents */
+ case TYPEMULTIPART: /* multiple part */
+ for (part = body->nested.part; part; part = part->next)
+ mail_gc_body (&part->body);
+ break;
+ case TYPEMESSAGE: /* encapsulated message */
+ if (body->subtype && !strcmp (body->subtype,"RFC822")) {
+ mail_free_stringlist (&body->nested.msg->lines);
+ mail_gc_msg (body->nested.msg,GC_TEXTS);
+ }
+ break;
+ default:
+ break;
+ }
+ if (body->mime.text.data) fs_give ((void **) &body->mime.text.data);
+ if (body->contents.text.data) fs_give ((void **) &body->contents.text.data);
+}
+
+/* Mail get body part
+ * Accepts: mail stream
+ * message number
+ * section specifier
+ * Returns: pointer to body
+ */
+
+BODY *mail_body (MAILSTREAM *stream,unsigned long msgno,unsigned char *section)
+{
+ BODY *b = NIL;
+ PART *pt;
+ unsigned long i;
+ /* make sure have a body */
+ if (section && *section && mail_fetchstructure (stream,msgno,&b) && b)
+ while (*section) { /* find desired section */
+ if (isdigit (*section)) { /* get section specifier */
+ /* make sure what follows is valid */
+ if (!(i = strtoul (section,(char **) &section,10)) ||
+ (*section && ((*section++ != '.') || !*section))) return NIL;
+ /* multipart content? */
+ if (b->type == TYPEMULTIPART) {
+ /* yes, find desired part */
+ if (pt = b->nested.part) while (--i && (pt = pt->next));
+ if (!pt) return NIL; /* bad specifier */
+ b = &pt->body; /* note new body */
+ }
+ /* otherwise must be section 1 */
+ else if (i != 1) return NIL;
+ /* need to go down further? */
+ if (*section) switch (b->type) {
+ case TYPEMULTIPART: /* multipart */
+ break;
+ case TYPEMESSAGE: /* embedded message */
+ if (!strcmp (b->subtype,"RFC822")) {
+ b = b->nested.msg->body;
+ break;
+ }
+ default: /* bogus subpart specification */
+ return NIL;
+ }
+ }
+ else return NIL; /* unknown section specifier */
+ }
+ return b;
+}
+
+/* Mail output date from elt fields
+ * Accepts: character string to write into
+ * elt to get data data from
+ * Returns: the character string
+ */
+
+const char *days[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+
+const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+
+char *mail_date (char *string,MESSAGECACHE *elt)
+{
+ sprintf (string,"%2d-%s-%d %02d:%02d:%02d %c%02d%02d",
+ elt->day ? elt->day : 1,
+ months[elt->month ? (elt->month - 1) : 0],
+ elt->year + BASEYEAR,elt->hours,elt->minutes,elt->seconds,
+ elt->zoccident ? '-' : '+',elt->zhours,elt->zminutes);
+ return string;
+}
+
+
+/* Mail output extended-ctime format date from elt fields
+ * Accepts: character string to write into
+ * elt to get data data from
+ * Returns: the character string
+ */
+
+char *mail_cdate (char *string,MESSAGECACHE *elt)
+{
+ char *fmt = "%s %s %2d %02d:%02d:%02d %4d %s%02d%02d\n";
+ int d = elt->day ? elt->day : 1;
+ int m = elt->month ? (elt->month - 1) : 0;
+ int y = elt->year + BASEYEAR;
+ const char *s = months[m];
+ if (m < 2) { /* if before March, */
+ m += 10; /* January = month 10 of previous year */
+ y--;
+ }
+ else m -= 2; /* March is month 0 */
+ sprintf (string,fmt,days[(int) (d + 2 + ((7 + 31 * m) / 12)
+#ifndef USEJULIANCALENDAR
+#ifndef USEORTHODOXCALENDAR /* Gregorian calendar */
+ + (y / 400)
+#ifdef Y4KBUGFIX
+ - (y / 4000)
+#endif
+#else /* Orthodox calendar */
+ + (2 * (y / 900)) + ((y % 900) >= 200)
+ + ((y % 900) >= 600)
+#endif
+ - (y / 100)
+#endif
+ + y + (y / 4)) % 7],
+ s,d,elt->hours,elt->minutes,elt->seconds,elt->year + BASEYEAR,
+ elt->zoccident ? "-" : "+",elt->zhours,elt->zminutes);
+ return string;
+}
+
+/* Mail parse date into elt fields
+ * Accepts: elt to write into
+ * date string to parse
+ * Returns: T if parse successful, else NIL
+ * This routine parses dates as follows:
+ * . leading three alphas followed by comma and space are ignored
+ * . date accepted in format: mm/dd/yy, mm/dd/yyyy, dd-mmm-yy, dd-mmm-yyyy,
+ * dd mmm yy, dd mmm yyyy, yyyy-mm-dd, yyyymmdd
+ * . two and three digit years interpreted according to RFC 2822 rules
+ * . mandatory end of string if yyyy-mm-dd or yyyymmdd; otherwise optional
+ * space followed by time:
+ * . time accepted in format hh:mm:ss or hh:mm
+ * . end of string accepted
+ * . timezone accepted: hyphen followed by symbolic timezone, or space
+ * followed by signed numeric timezone or symbolic timezone
+ * Examples of normal input:
+ * . IMAP date-only (SEARCH):
+ * dd-mmm-yyyy
+ * . IMAP date-time (INTERNALDATE):
+ * dd-mmm-yyyy hh:mm:ss +zzzz
+ * . RFC-822:
+ * www, dd mmm yy hh:mm:ss zzz
+ * . RFC-2822:
+ * www, dd mmm yyyy hh:mm:ss +zzzz
+ */
+
+long mail_parse_date (MESSAGECACHE *elt,unsigned char *s)
+{
+ unsigned long d,m,y;
+ int mi,ms;
+ struct tm *t;
+ time_t tn;
+ char tmp[MAILTMPLEN];
+ static unsigned long maxyear = 0;
+ if (!maxyear) { /* know the end of time yet? */
+ MESSAGECACHE tmpelt;
+ memset (&tmpelt,0xff,sizeof (MESSAGECACHE));
+ maxyear = BASEYEAR + tmpelt.year;
+ }
+ /* clear elt */
+ elt->zoccident = elt->zhours = elt->zminutes =
+ elt->hours = elt->minutes = elt->seconds =
+ elt->day = elt->month = elt->year = 0;
+ /* make a writeable uppercase copy */
+ if (s && *s && (strlen (s) < (size_t)MAILTMPLEN)) s = ucase (strcpy (tmp,s));
+ else return NIL;
+ /* skip over possible day of week */
+ if (isalpha (*s) && isalpha (s[1]) && isalpha (s[2]) && (s[3] == ',') &&
+ (s[4] == ' ')) s += 5;
+ while (*s == ' ') s++; /* parse first number (probable month) */
+ if (!(m = strtoul (s,(char **) &s,10))) return NIL;
+
+ switch (*s) { /* different parse based on delimiter */
+ case '/': /* mm/dd/yy format */
+ if (isdigit (*++s) && (d = strtoul (s,(char **) &s,10)) &&
+ (*s == '/') && isdigit (*++s)) {
+ y = strtoul (s,(char **) &s,10);
+ if (*s == '\0') break; /* must end here */
+ }
+ return NIL; /* bogon */
+ case ' ': /* dd mmm yy format */
+ while (s[1] == ' ') s++; /* slurp extra whitespace */
+ case '-':
+ if (isdigit (s[1])) { /* possible ISO 8601 date format? */
+ y = m; /* yes, first number is year */
+ /* get month and day */
+ if ((m = strtoul (s+1,(char **) &s,10)) && (*s++ == '-') &&
+ (d = strtoul (s,(char **) &s,10)) && !*s) break;
+ return NIL; /* syntax error or time present */
+ }
+ d = m; /* dd-mmm-yy[yy], so first number is a day */
+ /* make sure string long enough! */
+ if (strlen (s) < (size_t) 5) return NIL;
+ /* Some compilers don't allow `<<' and/or longs in case statements. */
+ /* slurp up the month string */
+ ms = ((s[1] - 'A') * 1024) + ((s[2] - 'A') * 32) + (s[3] - 'A');
+ switch (ms) { /* determine the month */
+ case (('J'-'A') * 1024) + (('A'-'A') * 32) + ('N'-'A'): m = 1; break;
+ case (('F'-'A') * 1024) + (('E'-'A') * 32) + ('B'-'A'): m = 2; break;
+ case (('M'-'A') * 1024) + (('A'-'A') * 32) + ('R'-'A'): m = 3; break;
+ case (('A'-'A') * 1024) + (('P'-'A') * 32) + ('R'-'A'): m = 4; break;
+ case (('M'-'A') * 1024) + (('A'-'A') * 32) + ('Y'-'A'): m = 5; break;
+ case (('J'-'A') * 1024) + (('U'-'A') * 32) + ('N'-'A'): m = 6; break;
+ case (('J'-'A') * 1024) + (('U'-'A') * 32) + ('L'-'A'): m = 7; break;
+ case (('A'-'A') * 1024) + (('U'-'A') * 32) + ('G'-'A'): m = 8; break;
+ case (('S'-'A') * 1024) + (('E'-'A') * 32) + ('P'-'A'): m = 9; break;
+ case (('O'-'A') * 1024) + (('C'-'A') * 32) + ('T'-'A'): m = 10; break;
+ case (('N'-'A') * 1024) + (('O'-'A') * 32) + ('V'-'A'): m = 11; break;
+ case (('D'-'A') * 1024) + (('E'-'A') * 32) + ('C'-'A'): m = 12; break;
+ default: return NIL; /* unknown month */
+ }
+ if (s[4] == *s) s += 5; /* advance to year */
+ else { /* first three were OK, possibly full name */
+ mi = *s; /* note delimiter, skip alphas */
+ for (s += 4; isalpha (*s); s++);
+ /* error if delimiter not here */
+ if (mi != *s++) return NIL;
+ }
+ while (*s == ' ') s++; /* parse year */
+ if (isdigit (*s)) { /* must be a digit here */
+ y = strtoul (s,(char **) &s,10);
+ if (*s == '\0' || *s == ' ') break;
+ }
+ case '\0': /* ISO 8601 compact date */
+ if (m < (BASEYEAR * 10000)) return NIL;
+ y = m / 10000; /* get year */
+ d = (m %= 10000) % 100; /* get day */
+ m /= 100; /* and month */
+ break;
+ default:
+ return NIL; /* unknown date format */
+ }
+
+ /* minimal validity check of date */
+ if ((d > 31) || (m > 12)) return NIL;
+ if (y < 49) y += 2000; /* RFC 2282 rules for two digit years 00-49 */
+ else if (y < 999) y += 1900; /* 2-digit years 50-99 and 3-digit years */
+ /* reject prehistoric and far future years */
+ if ((y < BASEYEAR) || (y > maxyear)) return NIL;
+ /* set values in elt */
+ elt->day = d; elt->month = m; elt->year = y - BASEYEAR;
+ ms = '\0'; /* initially no time zone string */
+ if (*s) { /* time specification present? */
+ /* parse time */
+ d = strtoul (s+1,(char **) &s,10);
+ if (*s != ':') return NIL;
+ m = strtoul (++s,(char **) &s,10);
+ y = (*s == ':') ? strtoul (++s,(char **) &s,10) : 0;
+ /* validity check time */
+ if ((d > 23) || (m > 59) || (y > 60)) return NIL;
+ /* set values in elt */
+ elt->hours = d; elt->minutes = m; elt->seconds = y;
+ switch (*s) { /* time zone specifier? */
+ case ' ': /* numeric time zone */
+ while (s[1] == ' ') s++; /* slurp extra whitespace */
+ if (!isalpha (s[1])) { /* treat as '-' case if alphabetic */
+ /* test for sign character */
+ if ((elt->zoccident = (*++s == '-')) || (*s == '+')) s++;
+ /* validate proper timezone */
+ if (isdigit(*s) && isdigit(s[1]) && isdigit(s[2]) && (s[2] < '6') &&
+ isdigit(s[3])) {
+ elt->zhours = (*s - '0') * 10 + (s[1] - '0');
+ elt->zminutes = (s[2] - '0') * 10 + (s[3] - '0');
+ }
+ return T; /* all done! */
+ }
+ /* falls through */
+ case '-': /* symbolic time zone */
+ if (!(ms = *++s)) ms = 'Z';
+ else if (*++s) { /* multi-character? */
+ ms -= 'A'; ms *= 1024; /* yes, make compressed three-byte form */
+ ms += ((*s++ - 'A') * 32);
+ if (*s) ms += *s++ - 'A';
+ if (*s) ms = '\0'; /* more than three characters */
+ }
+ default: /* ignore anything else */
+ break;
+ }
+ }
+
+ /* This is not intended to be a comprehensive list of all possible
+ * timezone strings. Such a list would be impractical. Rather, this
+ * listing is intended to incorporate all military, North American, and
+ * a few special cases such as Japan and the major European zone names,
+ * such as what might be expected to be found in a Tenex format mailbox
+ * and spewed from an IMAP server. The trend is to migrate to numeric
+ * timezones which lack the flavor but also the ambiguity of the names.
+ *
+ * RFC-822 only recognizes UT, GMT, 1-letter military timezones, and the
+ * 4 CONUS timezones and their summer time variants. [Sorry, Canadian
+ * Atlantic Provinces, Alaska, and Hawaii.]
+ */
+ switch (ms) { /* determine the timezone */
+ /* Universal */
+ case (('U'-'A')*1024)+(('T'-'A')*32):
+#ifndef STRICT_RFC822_TIMEZONES
+ case (('U'-'A')*1024)+(('T'-'A')*32)+'C'-'A':
+#endif
+ /* Greenwich */
+ case (('G'-'A')*1024)+(('M'-'A')*32)+'T'-'A':
+ case 'Z': elt->zhours = 0; break;
+
+ /* oriental (from Greenwich) timezones */
+#ifndef STRICT_RFC822_TIMEZONES
+ /* Middle Europe */
+ case (('M'-'A')*1024)+(('E'-'A')*32)+'T'-'A':
+#endif
+#ifdef BRITISH_SUMMER_TIME
+ /* British Summer */
+ case (('B'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
+#endif
+ case 'A': elt->zhours = 1; break;
+#ifndef STRICT_RFC822_TIMEZONES
+ /* Eastern Europe */
+ case (('E'-'A')*1024)+(('E'-'A')*32)+'T'-'A':
+#endif
+ case 'B': elt->zhours = 2; break;
+ case 'C': elt->zhours = 3; break;
+ case 'D': elt->zhours = 4; break;
+ case 'E': elt->zhours = 5; break;
+ case 'F': elt->zhours = 6; break;
+ case 'G': elt->zhours = 7; break;
+ case 'H': elt->zhours = 8; break;
+#ifndef STRICT_RFC822_TIMEZONES
+ /* Japan */
+ case (('J'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
+#endif
+ case 'I': elt->zhours = 9; break;
+ case 'K': elt->zhours = 10; break;
+ case 'L': elt->zhours = 11; break;
+ case 'M': elt->zhours = 12; break;
+
+ /* occidental (from Greenwich) timezones */
+ case 'N': elt->zoccident = 1; elt->zhours = 1; break;
+ case 'O': elt->zoccident = 1; elt->zhours = 2; break;
+#ifndef STRICT_RFC822_TIMEZONES
+ case (('A'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
+#endif
+ case 'P': elt->zoccident = 1; elt->zhours = 3; break;
+#ifdef NEWFOUNDLAND_STANDARD_TIME
+ /* Newfoundland */
+ case (('N'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
+ elt->zoccident = 1; elt->zhours = 3; elt->zminutes = 30; break;
+#endif
+#ifndef STRICT_RFC822_TIMEZONES
+ /* Atlantic */
+ case (('A'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
+#endif
+ /* CONUS */
+ case (('E'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
+ case 'Q': elt->zoccident = 1; elt->zhours = 4; break;
+ /* Eastern */
+ case (('E'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
+ case (('C'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
+ case 'R': elt->zoccident = 1; elt->zhours = 5; break;
+ /* Central */
+ case (('C'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
+ case (('M'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
+ case 'S': elt->zoccident = 1; elt->zhours = 6; break;
+ /* Mountain */
+ case (('M'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
+ case (('P'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
+ case 'T': elt->zoccident = 1; elt->zhours = 7; break;
+ /* Pacific */
+ case (('P'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
+#ifndef STRICT_RFC822_TIMEZONES
+ case (('Y'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
+#endif
+ case 'U': elt->zoccident = 1; elt->zhours = 8; break;
+#ifndef STRICT_RFC822_TIMEZONES
+ /* Yukon */
+ case (('Y'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
+#endif
+ case 'V': elt->zoccident = 1; elt->zhours = 9; break;
+#ifndef STRICT_RFC822_TIMEZONES
+ /* Hawaii */
+ case (('H'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
+#endif
+ case 'W': elt->zoccident = 1; elt->zhours = 10; break;
+ /* Nome/Bering/Samoa */
+#ifdef NOME_STANDARD_TIME
+ case (('N'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
+#endif
+#ifdef BERING_STANDARD_TIME
+ case (('B'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
+#endif
+#ifdef SAMOA_STANDARD_TIME
+ case (('S'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
+#endif
+ case 'X': elt->zoccident = 1; elt->zhours = 11; break;
+ case 'Y': elt->zoccident = 1; elt->zhours = 12; break;
+
+ default: /* unknown time zones treated as local */
+ tn = time (0); /* time now... */
+ t = localtime (&tn); /* get local minutes since midnight */
+ mi = t->tm_hour * 60 + t->tm_min;
+ ms = t->tm_yday; /* note Julian day */
+ if (t = gmtime (&tn)) { /* minus UTC minutes since midnight */
+ mi -= t->tm_hour * 60 + t->tm_min;
+ /* ms can be one of:
+ * 36x local time is December 31, UTC is January 1, offset -24 hours
+ * 1 local time is 1 day ahead of UTC, offset +24 hours
+ * 0 local time is same day as UTC, no offset
+ * -1 local time is 1 day behind UTC, offset -24 hours
+ * -36x local time is January 1, UTC is December 31, offset +24 hours
+ */
+ if (ms -= t->tm_yday) /* correct offset if different Julian day */
+ mi += ((ms < 0) == (abs (ms) == 1)) ? -24*60 : 24*60;
+ if (mi < 0) { /* occidental? */
+ mi = abs (mi); /* yup, make positive number */
+ elt->zoccident = 1; /* and note west of UTC */
+ }
+ elt->zhours = mi / 60; /* now break into hours and minutes */
+ elt->zminutes = mi % 60;
+ }
+ break;
+ }
+ return T;
+}
+
+/* Mail n messages exist
+ * Accepts: mail stream
+ * number of messages
+ */
+
+void mail_exists (MAILSTREAM *stream,unsigned long nmsgs)
+{
+ char tmp[MAILTMPLEN];
+ if (nmsgs > MAXMESSAGES) {
+ sprintf (tmp,"Mailbox has more messages (%lu) exist than maximum (%lu)",
+ nmsgs,MAXMESSAGES);
+ mm_log (tmp,ERROR);
+ nmsgs = MAXMESSAGES; /* cap to maximum */
+ /* probably will crash in mail_elt() soon enough... */
+ }
+ /* make sure cache is large enough */
+ (*mailcache) (stream,nmsgs,CH_SIZE);
+ stream->nmsgs = nmsgs; /* update stream status */
+ /* notify main program of change */
+ if (!stream->silent) MM_EXISTS (stream,nmsgs);
+}
+
+
+/* Mail n messages are recent
+ * Accepts: mail stream
+ * number of recent messages
+ */
+
+void mail_recent (MAILSTREAM *stream,unsigned long recent)
+{
+ char tmp[MAILTMPLEN];
+ if (recent <= stream->nmsgs) stream->recent = recent;
+ else {
+ sprintf (tmp,"Non-existent recent message(s) %lu, nmsgs=%lu",
+ recent,stream->nmsgs);
+ mm_log (tmp,ERROR);
+ }
+}
+
+
+/* Mail message n is expunged
+ * Accepts: mail stream
+ * message #
+ */
+
+void mail_expunged (MAILSTREAM *stream,unsigned long msgno)
+{
+ char tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ if (msgno > stream->nmsgs) {
+ sprintf (tmp,"Expunge of non-existent message %lu, nmsgs=%lu",
+ msgno,stream->nmsgs);
+ mm_log (tmp,ERROR);
+ }
+ else {
+ elt = (MESSAGECACHE *) (*mailcache) (stream,msgno,CH_ELT);
+ /* notify main program of change */
+ if (!stream->silent) MM_EXPUNGED (stream,msgno);
+ if (elt) { /* if an element is there */
+ elt->msgno = 0; /* invalidate its message number and free */
+ (*mailcache) (stream,msgno,CH_FREE);
+ (*mailcache) (stream,msgno,CH_FREESORTCACHE);
+ }
+ /* expunge the slot */
+ (*mailcache) (stream,msgno,CH_EXPUNGE);
+ --stream->nmsgs; /* update stream status */
+ if (stream->msgno) { /* have stream pointers? */
+ /* make sure the short cache is nuked */
+ if (stream->scache) mail_gc (stream,GC_ENV | GC_TEXTS);
+ else stream->msgno = 0; /* make sure invalidated in any case */
+ }
+ }
+}
+
+/* Mail stream status routines */
+
+
+/* Mail lock stream
+ * Accepts: mail stream
+ */
+
+void mail_lock (MAILSTREAM *stream)
+{
+ if (stream->lock) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Lock when already locked, mbx=%.80s",
+ stream->mailbox ? stream->mailbox : "???");
+ fatal (tmp);
+ }
+ else stream->lock = T; /* lock stream */
+}
+
+
+/* Mail unlock stream
+ * Accepts: mail stream
+ */
+
+void mail_unlock (MAILSTREAM *stream)
+{
+ if (!stream->lock) fatal ("Unlock when not locked");
+ else stream->lock = NIL; /* unlock stream */
+}
+
+
+/* Mail turn on debugging telemetry
+ * Accepts: mail stream
+ */
+
+void mail_debug (MAILSTREAM *stream)
+{
+ stream->debug = T; /* turn on debugging telemetry */
+ if (stream->dtb) (*stream->dtb->parameters) (ENABLE_DEBUG,stream);
+}
+
+
+/* Mail turn off debugging telemetry
+ * Accepts: mail stream
+ */
+
+void mail_nodebug (MAILSTREAM *stream)
+{
+ stream->debug = NIL; /* turn off debugging telemetry */
+ if (stream->dtb) (*stream->dtb->parameters) (DISABLE_DEBUG,stream);
+}
+
+
+/* Mail log to debugging telemetry
+ * Accepts: message
+ * flag that data is "sensitive"
+ */
+
+void mail_dlog (char *string,long flag)
+{
+ mm_dlog ((debugsensitive || !flag) ? string : "<suppressed>");
+}
+
+/* Mail parse UID sequence
+ * Accepts: mail stream
+ * sequence to parse
+ * Returns: T if parse successful, else NIL
+ */
+
+long mail_uid_sequence (MAILSTREAM *stream,unsigned char *sequence)
+{
+ unsigned long i,j,k,x,y;
+ for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->sequence = NIL;
+ while (sequence && *sequence){/* while there is something to parse */
+ if (*sequence == '*') { /* maximum message */
+ i = stream->nmsgs ? mail_uid (stream,stream->nmsgs) : stream->uid_last;
+ sequence++; /* skip past * */
+ }
+ /* parse and validate message number */
+ /* parse and validate message number */
+ else if (!isdigit (*sequence)) {
+ MM_LOG ("Syntax error in sequence",ERROR);
+ return NIL;
+ }
+ else if (!(i = strtoul (sequence,(char **) &sequence,10))) {
+ MM_LOG ("UID may not be zero",ERROR);
+ return NIL;
+ }
+ switch (*sequence) { /* see what the delimiter is */
+ case ':': /* sequence range */
+ if (*++sequence == '*') { /* maximum message */
+ j = stream->nmsgs ? mail_uid (stream,stream->nmsgs) : stream->uid_last;
+ sequence++; /* skip past * */
+ }
+ /* parse end of range */
+ else if (!(j = strtoul (sequence,(char **) &sequence,10))) {
+ MM_LOG ("UID sequence range invalid",ERROR);
+ return NIL;
+ }
+ if (*sequence && *sequence++ != ',') {
+ MM_LOG ("UID sequence range syntax error",ERROR);
+ return NIL;
+ }
+ if (i > j) { /* swap the range if backwards */
+ x = i; i = j; j = x;
+ }
+ x = mail_msgno (stream,i);/* get msgnos */
+ y = mail_msgno (stream,j);/* for both UIDS (don't && it) */
+ /* easy if both UIDs valid */
+ if (x && y) while (x <= y) mail_elt (stream,x++)->sequence = T;
+ /* start UID valid, end is not */
+ else if (x) while ((x <= stream->nmsgs) && (mail_uid (stream,x) <= j))
+ mail_elt (stream,x++)->sequence = T;
+ /* end UID valid, start is not */
+ else if (y) for (x = 1; x <= y; x++) {
+ if (mail_uid (stream,x) >= i) mail_elt (stream,x)->sequence = T;
+ }
+ /* neither is valid, ugh */
+ else for (x = 1; x <= stream->nmsgs; x++)
+ if (((k = mail_uid (stream,x)) >= i) && (k <= j))
+ mail_elt (stream,x)->sequence = T;
+ break;
+ case ',': /* single message */
+ ++sequence; /* skip the delimiter, fall into end case */
+ case '\0': /* end of sequence, mark this message */
+ if (x = mail_msgno (stream,i)) mail_elt (stream,x)->sequence = T;
+ break;
+ default: /* anything else is a syntax error! */
+ MM_LOG ("UID sequence syntax error",ERROR);
+ return NIL;
+ }
+ }
+ return T; /* successfully parsed sequence */
+}
+
+/* Mail see if line list matches that in cache
+ * Accepts: candidate line list
+ * cached line list
+ * matching flags
+ * Returns: T if match, NIL if no match
+ */
+
+long mail_match_lines (STRINGLIST *lines,STRINGLIST *msglines,long flags)
+{
+ unsigned long i;
+ unsigned char *s,*t;
+ STRINGLIST *m;
+ if (!msglines) return T; /* full header is in cache */
+ /* need full header but filtered in cache */
+ if ((flags & FT_NOT) || !lines) return NIL;
+ do { /* make sure all present & accounted for */
+ for (m = msglines; m; m = m->next) if (lines->text.size == m->text.size) {
+ for (s = lines->text.data,t = m->text.data,i = lines->text.size;
+ i && !compare_uchar (*s,*t); s++,t++,i--);
+ if (!i) break; /* this line matches */
+ }
+ if (!m) return NIL; /* didn't find in the list */
+ }
+ while (lines = lines->next);
+ return T; /* all lines found */
+}
+
+/* Mail filter text by header lines
+ * Accepts: text to filter, with trailing null
+ * length of text
+ * list of lines
+ * fetch flags
+ * Returns: new text size, text overwritten
+ */
+
+unsigned long mail_filter (char *text,unsigned long len,STRINGLIST *lines,
+ long flags)
+{
+ STRINGLIST *hdrs;
+ int notfound;
+ unsigned long i;
+ char c,*s,*e,*t,tmp[MAILTMPLEN];
+ char *src = text;
+ char *dst = src;
+ char *end = text + len;
+ text[len] = '\012'; /* guard against running off buffer */
+ while (src < end) { /* process header */
+ /* slurp header line name */
+ for (s = src,e = s + MAILTMPLEN - 1,e = (e < end ? e : end),t = tmp;
+ (s < e) && ((c = (*s ? *s : (*s = ' '))) != ':') &&
+ ((c > ' ') ||
+ ((c != ' ') && (c != '\t') && (c != '\015') && (c != '\012')));
+ *t++ = *s++);
+ *t = '\0'; /* tie off */
+ notfound = T; /* not found yet */
+ if (i = t - tmp) /* see if found in header */
+ for (hdrs = lines; hdrs && notfound; hdrs = hdrs->next)
+ if ((hdrs->text.size == i) && !compare_csizedtext (tmp,&hdrs->text))
+ notfound = NIL;
+ /* skip header line if not wanted */
+ if (i && ((flags & FT_NOT) ? !notfound : notfound))
+ while (((*src++ != '\012') && (*src++ != '\012') && (*src++ != '\012') &&
+ (*src++ != '\012') && (*src++ != '\012') && (*src++ != '\012') &&
+ (*src++ != '\012') && (*src++ != '\012') && (*src++ != '\012') &&
+ (*src++ != '\012')) || ((*src == ' ') || (*src == '\t')));
+ else if (src == dst) { /* copy to self */
+ while (((*src++ != '\012') && (*src++ != '\012') && (*src++ != '\012') &&
+ (*src++ != '\012') && (*src++ != '\012') && (*src++ != '\012') &&
+ (*src++ != '\012') && (*src++ != '\012') && (*src++ != '\012') &&
+ (*src++ != '\012')) || ((*src == ' ') || (*src == '\t')));
+ dst = src; /* update destination */
+ }
+ else { /* copy line and any continuation line */
+ while ((((*dst++ = *src++) != '\012') && ((*dst++ = *src++) != '\012') &&
+ ((*dst++ = *src++) != '\012') && ((*dst++ = *src++) != '\012') &&
+ ((*dst++ = *src++) != '\012') && ((*dst++ = *src++) != '\012') &&
+ ((*dst++ = *src++) != '\012') && ((*dst++ = *src++) != '\012') &&
+ ((*dst++ = *src++) != '\012') && ((*dst++ = *src++) != '\012'))||
+ ((*src == ' ') || (*src == '\t')));
+ /* in case hit the guard LF */
+ if (src > end) dst -= (src - end);
+ }
+ }
+ *dst = '\0'; /* tie off destination */
+ return dst - text;
+}
+
+/* Local mail search message
+ * Accepts: MAIL stream
+ * message number
+ * optional section specification
+ * search program
+ * Returns: T if found, NIL otherwise
+ */
+
+long mail_search_msg (MAILSTREAM *stream,unsigned long msgno,char *section,
+ SEARCHPGM *pgm)
+{
+ unsigned short d;
+ char tmp[MAILTMPLEN];
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ SEARCHHEADER *hdr;
+ SEARCHOR *or;
+ SEARCHPGMLIST *not;
+ unsigned long now = (unsigned long) time (0);
+ if (pgm->msgno || pgm->uid) { /* message set searches */
+ SEARCHSET *set;
+ /* message sequences */
+ if (pgm->msgno) { /* inside this message sequence set */
+ for (set = pgm->msgno; set; set = set->next)
+ if (set->last ? ((set->first <= set->last) ?
+ ((msgno >= set->first) && (msgno <= set->last)) :
+ ((msgno >= set->last) && (msgno <= set->first))) :
+ msgno == set->first) break;
+ if (!set) return NIL; /* not found within sequence */
+ }
+ if (pgm->uid) { /* inside this unique identifier set */
+ unsigned long uid = mail_uid (stream,msgno);
+ for (set = pgm->uid; set; set = set->next)
+ if (set->last ? ((set->first <= set->last) ?
+ ((uid >= set->first) && (uid <= set->last)) :
+ ((uid >= set->last) && (uid <= set->first))) :
+ uid == set->first) break;
+ if (!set) return NIL; /* not found within sequence */
+ }
+ }
+
+ /* Fast data searches */
+ /* need to fetch fast data? */
+ if ((!elt->rfc822_size && (pgm->larger || pgm->smaller)) ||
+ (!elt->year && (pgm->before || pgm->on || pgm->since ||
+ pgm->older || pgm->younger)) ||
+ (!elt->valid && (pgm->answered || pgm->unanswered ||
+ pgm->deleted || pgm->undeleted ||
+ pgm->draft || pgm->undraft ||
+ pgm->flagged || pgm->unflagged ||
+ pgm->recent || pgm->old ||
+ pgm->seen || pgm->unseen ||
+ pgm->keyword || pgm->unkeyword))) {
+ unsigned long i;
+ MESSAGECACHE *ielt;
+ for (i = elt->msgno; /* find last unloaded message in range */
+ (i < stream->nmsgs) && (ielt = mail_elt (stream,i+1)) &&
+ ((!ielt->rfc822_size && (pgm->larger || pgm->smaller)) ||
+ (!ielt->year && (pgm->before || pgm->on || pgm->since ||
+ pgm->older || pgm->younger)) ||
+ (!ielt->valid && (pgm->answered || pgm->unanswered ||
+ pgm->deleted || pgm->undeleted ||
+ pgm->draft || pgm->undraft ||
+ pgm->flagged || pgm->unflagged ||
+ pgm->recent || pgm->old ||
+ pgm->seen || pgm->unseen ||
+ pgm->keyword || pgm->unkeyword))); ++i);
+ if (i == elt->msgno) sprintf (tmp,"%lu",elt->msgno);
+ else sprintf (tmp,"%lu:%lu",elt->msgno,i);
+ mail_fetch_fast (stream,tmp,NIL);
+ }
+ /* size ranges */
+ if ((pgm->larger && (elt->rfc822_size <= pgm->larger)) ||
+ (pgm->smaller && (elt->rfc822_size >= pgm->smaller))) return NIL;
+ /* message flags */
+ if ((pgm->answered && !elt->answered) ||
+ (pgm->unanswered && elt->answered) ||
+ (pgm->deleted && !elt->deleted) ||
+ (pgm->undeleted && elt->deleted) ||
+ (pgm->draft && !elt->draft) ||
+ (pgm->undraft && elt->draft) ||
+ (pgm->flagged && !elt->flagged) ||
+ (pgm->unflagged && elt->flagged) ||
+ (pgm->recent && !elt->recent) ||
+ (pgm->old && elt->recent) ||
+ (pgm->seen && !elt->seen) ||
+ (pgm->unseen && elt->seen)) return NIL;
+ /* keywords */
+ if ((pgm->keyword && !mail_search_keyword (stream,elt,pgm->keyword,LONGT)) ||
+ (pgm->unkeyword && !mail_search_keyword (stream,elt,pgm->unkeyword,NIL)))
+ return NIL;
+ /* internal date ranges */
+ if (pgm->before || pgm->on || pgm->since) {
+ d = mail_shortdate (elt->year,elt->month,elt->day);
+ if (pgm->before && (d >= pgm->before)) return NIL;
+ if (pgm->on && (d != pgm->on)) return NIL;
+ if (pgm->since && (d < pgm->since)) return NIL;
+ }
+ if (pgm->older || pgm->younger) {
+ unsigned long msgd = mail_longdate (elt);
+ if (pgm->older && msgd > (now - pgm->older)) return NIL;
+ if (pgm->younger && msgd < (now - pgm->younger)) return NIL;
+ }
+
+ /* envelope searches */
+ if (pgm->sentbefore || pgm->senton || pgm->sentsince ||
+ pgm->bcc || pgm->cc || pgm->from || pgm->to || pgm->subject ||
+ pgm->return_path || pgm->sender || pgm->reply_to || pgm->in_reply_to ||
+ pgm->message_id || pgm->newsgroups || pgm->followup_to ||
+ pgm->references) {
+ ENVELOPE *env;
+ MESSAGECACHE delt;
+ if (section) { /* use body part envelope */
+ BODY *body = mail_body (stream,msgno,section);
+ env = (body && (body->type == TYPEMESSAGE) && body->subtype &&
+ !strcmp (body->subtype,"RFC822")) ? body->nested.msg->env : NIL;
+ }
+ else { /* use top level envelope if no section */
+ if (pgm->header && !stream->scache && !(stream->dtb->flags & DR_LOCAL))
+ mail_fetch_header(stream,msgno,NIL,NIL,NIL,FT_PEEK|FT_SEARCHLOOKAHEAD);
+ env = mail_fetchenvelope (stream,msgno);
+ }
+ if (!env) return NIL; /* no envelope obtained */
+ /* sent date ranges */
+ if ((pgm->sentbefore || pgm->senton || pgm->sentsince) &&
+ (!mail_parse_date (&delt,env->date) ||
+ !(d = mail_shortdate (delt.year,delt.month,delt.day)) ||
+ (pgm->sentbefore && (d >= pgm->sentbefore)) ||
+ (pgm->senton && (d != pgm->senton)) ||
+ (pgm->sentsince && (d < pgm->sentsince)))) return NIL;
+ /* search headers */
+ if ((pgm->bcc && !mail_search_addr (env->bcc,pgm->bcc)) ||
+ (pgm->cc && !mail_search_addr (env->cc,pgm->cc)) ||
+ (pgm->from && !mail_search_addr (env->from,pgm->from)) ||
+ (pgm->to && !mail_search_addr (env->to,pgm->to)) ||
+ (pgm->subject && !mail_search_header_text (env->subject,pgm->subject)))
+ return NIL;
+ /* These criteria are not supported by IMAP and have to be emulated */
+ if ((pgm->return_path &&
+ !mail_search_addr (env->return_path,pgm->return_path)) ||
+ (pgm->sender && !mail_search_addr (env->sender,pgm->sender)) ||
+ (pgm->reply_to && !mail_search_addr (env->reply_to,pgm->reply_to)) ||
+ (pgm->in_reply_to &&
+ !mail_search_header_text (env->in_reply_to,pgm->in_reply_to)) ||
+ (pgm->message_id &&
+ !mail_search_header_text (env->message_id,pgm->message_id)) ||
+ (pgm->newsgroups &&
+ !mail_search_header_text (env->newsgroups,pgm->newsgroups)) ||
+ (pgm->followup_to &&
+ !mail_search_header_text (env->followup_to,pgm->followup_to)) ||
+ (pgm->references &&
+ !mail_search_header_text (env->references,pgm->references)))
+ return NIL;
+ }
+
+ /* search header lines */
+ for (hdr = pgm->header; hdr; hdr = hdr->next) {
+ char *t,*e,*v;
+ SIZEDTEXT s;
+ STRINGLIST sth,stc;
+ sth.next = stc.next = NIL; /* only one at a time */
+ sth.text.data = hdr->line.data;
+ sth.text.size = hdr->line.size;
+ /* get the header text */
+ if ((t = mail_fetch_header (stream,msgno,NIL,&sth,&s.size,
+ FT_INTERNAL | FT_PEEK |
+ (section ? NIL : FT_SEARCHLOOKAHEAD))) &&
+ strchr (t,':')) {
+ if (hdr->text.size) { /* anything matches empty search string */
+ /* non-empty, copy field data */
+ s.data = (unsigned char *) fs_get (s.size + 1);
+ /* for each line */
+ for (v = (char *) s.data, e = t + s.size; t < e;) switch (*t) {
+ default: /* non-continuation, skip leading field name */
+ while ((t < e) && (*t++ != ':'));
+ if ((t < e) && (*t == ':')) t++;
+ case '\t': case ' ': /* copy field data */
+ while ((t < e) && (*t != '\015') && (*t != '\012')) *v++ = *t++;
+ *v++ = '\n'; /* tie off line */
+ while (((*t == '\015') || (*t == '\012')) && (t < e)) t++;
+ }
+ /* calculate true size */
+ s.size = v - (char *) s.data;
+ *v = '\0'; /* tie off results */
+ stc.text.data = hdr->text.data;
+ stc.text.size = hdr->text.size;
+ /* search header */
+ if (mail_search_header (&s,&stc)) fs_give ((void **) &s.data);
+ else { /* search failed */
+ fs_give ((void **) &s.data);
+ return NIL;
+ }
+ }
+ }
+ else return NIL; /* no matching header text */
+ }
+ /* search strings */
+ if ((pgm->text && !mail_search_text (stream,msgno,section,pgm->text,LONGT))||
+ (pgm->body && !mail_search_text (stream,msgno,section,pgm->body,NIL)))
+ return NIL;
+ /* logical conditions */
+ for (or = pgm->or; or; or = or->next)
+ if (!(mail_search_msg (stream,msgno,section,or->first) ||
+ mail_search_msg (stream,msgno,section,or->second))) return NIL;
+ for (not = pgm->not; not; not = not->next)
+ if (mail_search_msg (stream,msgno,section,not->pgm)) return NIL;
+ return T;
+}
+
+/* Mail search message header null-terminated text
+ * Accepts: header text
+ * strings to search
+ * Returns: T if search found a match
+ */
+
+long mail_search_header_text (char *s,STRINGLIST *st)
+{
+ SIZEDTEXT h;
+ /* have any text? */
+ if (h.data = (unsigned char *) s) {
+ h.size = strlen (s); /* yes, get its size */
+ return mail_search_header (&h,st);
+ }
+ return NIL;
+}
+
+
+/* Mail search message header
+ * Accepts: header as sized text
+ * strings to search
+ * Returns: T if search found a match
+ */
+
+long mail_search_header (SIZEDTEXT *hdr,STRINGLIST *st)
+{
+ SIZEDTEXT h;
+ long ret = LONGT;
+ /* make UTF-8 version of header */
+ utf8_mime2text (hdr,&h,U8T_CANONICAL);
+ while (h.size && ((h.data[h.size-1]=='\015') || (h.data[h.size-1]=='\012')))
+ --h.size; /* slice off trailing newlines */
+ do if (h.size ? /* search non-empty string */
+ !ssearch (h.data,h.size,st->text.data,st->text.size) : st->text.size)
+ ret = NIL;
+ while (ret && (st = st->next));
+ if (h.data != hdr->data) fs_give ((void **) &h.data);
+ return ret;
+}
+
+/* Mail search message body
+ * Accepts: MAIL stream
+ * message number
+ * optional section specification
+ * string list
+ * flags
+ * Returns: T if search found a match
+ */
+
+long mail_search_text (MAILSTREAM *stream,unsigned long msgno,char *section,
+ STRINGLIST *st,long flags)
+{
+ BODY *body;
+ long ret = NIL;
+ STRINGLIST *s = mail_newstringlist ();
+ mailgets_t omg = mailgets;
+ if (stream->dtb->flags & DR_LOWMEM) mailgets = mail_search_gets;
+ /* strings to search */
+ for (stream->private.search.string = s; st;) {
+ s->text.data = st->text.data;
+ s->text.size = st->text.size;
+ if (st = st->next) s = s->next = mail_newstringlist ();
+ }
+ stream->private.search.text = NIL;
+ if (flags) { /* want header? */
+ SIZEDTEXT s,t;
+ s.data = (unsigned char *)
+ mail_fetch_header (stream,msgno,section,NIL,&s.size,FT_INTERNAL|FT_PEEK);
+ utf8_mime2text (&s,&t,U8T_CANONICAL);
+ ret = mail_search_string_work (&t,&stream->private.search.string);
+ if (t.data != s.data) fs_give ((void **) &t.data);
+ }
+ if (!ret) { /* still looking for match? */
+ /* no section, get top-level body */
+ if (!section) mail_fetchstructure (stream,msgno,&body);
+ /* get body of nested message */
+ else if ((body = mail_body (stream,msgno,section)) &&
+ (body->type == TYPEMULTIPART) && body->subtype &&
+ !strcmp (body->subtype,"RFC822")) body = body->nested.msg->body;
+ if (body) ret = mail_search_body (stream,msgno,body,NIL,1,flags);
+ }
+ mailgets = omg; /* restore former gets routine */
+ /* clear searching */
+ for (s = stream->private.search.string; s; s = s->next) s->text.data = NIL;
+ mail_free_stringlist (&stream->private.search.string);
+ stream->private.search.text = NIL;
+ return ret;
+}
+
+/* Mail search message body text parts
+ * Accepts: MAIL stream
+ * message number
+ * current body pointer
+ * hierarchical level prefix
+ * position at current hierarchical level
+ * string list
+ * flags
+ * Returns: T if search found a match
+ */
+
+long mail_search_body (MAILSTREAM *stream,unsigned long msgno,BODY *body,
+ char *prefix,unsigned long section,long flags)
+{
+ long ret = NIL;
+ unsigned long i;
+ char *s,*t,sect[MAILTMPLEN];
+ SIZEDTEXT st,h;
+ PART *part;
+ PARAMETER *param;
+ if (prefix && (strlen (prefix) > (MAILTMPLEN - 20))) return NIL;
+ sprintf (sect,"%s%lu",prefix ? prefix : "",section++);
+ if (flags && prefix) { /* want to search MIME header too? */
+ st.data = (unsigned char *) mail_fetch_mime (stream,msgno,sect,&st.size,
+ FT_INTERNAL | FT_PEEK);
+ if (stream->dtb->flags & DR_LOWMEM) ret = stream->private.search.result;
+ else {
+ /* make UTF-8 version of header */
+ utf8_mime2text (&st,&h,U8T_CANONICAL);
+ ret = mail_search_string_work (&h,&stream->private.search.string);
+ if (h.data != st.data) fs_give ((void **) &h.data);
+ }
+ }
+ if (!ret) switch (body->type) {
+ case TYPEMULTIPART:
+ /* extend prefix if not first time */
+ s = prefix ? strcat (sect,".") : "";
+ for (i = 1,part = body->nested.part; part && !ret; i++,part = part->next)
+ ret = mail_search_body (stream,msgno,&part->body,s,i,flags);
+ break;
+ case TYPEMESSAGE:
+ if (!strcmp (body->subtype,"RFC822")) {
+ if (flags) { /* want to search nested message header? */
+ st.data = (unsigned char *)
+ mail_fetch_header (stream,msgno,sect,NIL,&st.size,
+ FT_INTERNAL | FT_PEEK);
+ if (stream->dtb->flags & DR_LOWMEM) ret =stream->private.search.result;
+ else {
+ /* make UTF-8 version of header */
+ utf8_mime2text (&st,&h,U8T_CANONICAL);
+ ret = mail_search_string_work (&h,&stream->private.search.string);
+ if (h.data != st.data) fs_give ((void **) &h.data);
+ }
+ }
+ if (body = body->nested.msg->body)
+ ret = (body->type == TYPEMULTIPART) ?
+ mail_search_body (stream,msgno,body,(prefix ? prefix : ""),
+ section - 1,flags) :
+ mail_search_body (stream,msgno,body,strcat (sect,"."),1,flags);
+ break;
+ }
+ /* non-MESSAGE/RFC822 falls into text case */
+
+ case TYPETEXT:
+ s = mail_fetch_body (stream,msgno,sect,&i,FT_INTERNAL | FT_PEEK);
+ if (stream->dtb->flags & DR_LOWMEM) ret = stream->private.search.result;
+ else {
+ for (t = NIL,param = body->parameter; param && !t; param = param->next)
+ if (!strcmp (param->attribute,"CHARSET")) t = param->value;
+ switch (body->encoding) { /* what encoding? */
+ case ENCBASE64:
+ if (st.data = (unsigned char *)
+ rfc822_base64 ((unsigned char *) s,i,&st.size)) {
+ ret = mail_search_string (&st,t,&stream->private.search.string);
+ fs_give ((void **) &st.data);
+ }
+ break;
+ case ENCQUOTEDPRINTABLE:
+ if (st.data = rfc822_qprint ((unsigned char *) s,i,&st.size)) {
+ ret = mail_search_string (&st,t,&stream->private.search.string);
+ fs_give ((void **) &st.data);
+ }
+ break;
+ default:
+ st.data = (unsigned char *) s;
+ st.size = i;
+ ret = mail_search_string (&st,t,&stream->private.search.string);
+ break;
+ }
+ }
+ break;
+ }
+ return ret;
+}
+
+/* Mail search text
+ * Accepts: sized text to search
+ * character set of sized text
+ * string list of search keys
+ * Returns: T if search found a match
+ */
+
+long mail_search_string (SIZEDTEXT *s,char *charset,STRINGLIST **st)
+{
+ SIZEDTEXT u;
+ long ret;
+ STRINGLIST **sc = st;
+ /* convert to UTF-8 as best we can */
+ if (!utf8_text (s,charset,&u,U8T_CANONICAL))
+ utf8_text (s,NIL,&u,U8T_CANONICAL);
+ ret = mail_search_string_work (&u,st);
+ if (u.data != s->data) fs_give ((void **) &u.data);
+ return ret;
+}
+
+
+/* Mail search text worker routine
+ * Accepts: sized text to search
+ * string list of search keys
+ * Returns: T if search found a match
+ */
+
+long mail_search_string_work (SIZEDTEXT *s,STRINGLIST **st)
+{
+ void *t;
+ STRINGLIST **sc = st;
+ while (*sc) { /* run down criteria list */
+ if (ssearch (s->data,s->size,(*sc)->text.data,(*sc)->text.size)) {
+ t = (void *) (*sc); /* found one, need to flush this */
+ *sc = (*sc)->next; /* remove it from the list */
+ fs_give (&t); /* flush the buffer */
+ }
+ else sc = &(*sc)->next; /* move to next in list */
+ }
+ return *st ? NIL : LONGT;
+}
+
+
+/* Mail search keyword
+ * Accepts: MAIL stream
+ * elt to get flags from
+ * keyword list
+ * T for keyword search, NIL for unkeyword search
+ * Returns: T if search found a match
+ */
+
+long mail_search_keyword (MAILSTREAM *stream,MESSAGECACHE *elt,STRINGLIST *st,
+ long flag)
+{
+ int i,j;
+ unsigned long f = 0;
+ unsigned long tf;
+ do {
+ for (i = 0; (j = (i < NUSERFLAGS) && stream->user_flags[i]); ++i)
+ if (!compare_csizedtext (stream->user_flags[i],&st->text)) {
+ f |= (1 << i);
+ break;
+ }
+ if (flag && !j) return NIL;
+ } while (st = st->next);
+ tf = elt->user_flags & f; /* get set flags which match */
+ return flag ? (f == tf) : !tf;
+}
+
+/* Mail search an address list
+ * Accepts: address list
+ * string list
+ * Returns: T if search found a match
+ */
+
+#define SEARCHBUFLEN (size_t) 2000
+#define SEARCHBUFSLOP (size_t) 5
+
+long mail_search_addr (ADDRESS *adr,STRINGLIST *st)
+{
+ ADDRESS *a,tadr;
+ SIZEDTEXT txt;
+ char tmp[SENDBUFLEN + 1];
+ size_t i = SEARCHBUFLEN;
+ size_t k;
+ long ret = NIL;
+ if (adr) {
+ txt.data = (unsigned char *) fs_get (i + SEARCHBUFSLOP);
+ /* never an error or next */
+ tadr.error = NIL,tadr.next = NIL;
+ /* write address list */
+ for (txt.size = 0,a = adr; a; a = a->next) {
+ k = (tadr.mailbox = a->mailbox) ? 4 + 2*strlen (a->mailbox) : 3;
+ if (tadr.personal = a->personal) k += 3 + 2*strlen (a->personal);
+ if (tadr.adl = a->adl) k += 3 + 2*strlen (a->adl);
+ if (tadr.host = a->host) k += 3 + 2*strlen (a->host);
+ if (tadr.personal || tadr.adl) k += 2;
+ if (k < (SENDBUFLEN-10)) {/* ignore ridiculous addresses */
+ tmp[0] = '\0';
+ rfc822_write_address (tmp,&tadr);
+ /* resize buffer if necessary */
+ if (((k = strlen (tmp)) + txt.size) > i)
+ fs_resize ((void **) &txt.data,SEARCHBUFSLOP + (i += SEARCHBUFLEN));
+ /* add new address */
+ memcpy (txt.data + txt.size,tmp,k);
+ txt.size += k;
+ /* another address follows */
+ if (a->next) txt.data[txt.size++] = ',';
+ }
+ }
+ txt.data[txt.size] = '\0'; /* tie off string */
+ ret = mail_search_header (&txt,st);
+ fs_give ((void **) &txt.data);
+ }
+ return ret;
+}
+
+/* Get string for low-memory searching
+ * Accepts: readin function pointer
+ * stream to use
+ * number of bytes
+ * gets data packet
+
+ * mail stream
+ * message number
+ * descriptor string
+ * option flags
+ * Returns: NIL, always
+ */
+
+#define SEARCHSLOP 128
+
+char *mail_search_gets (readfn_t f,void *stream,unsigned long size,
+ GETS_DATA *md)
+{
+ unsigned long i;
+ char tmp[MAILTMPLEN+SEARCHSLOP+1];
+ SIZEDTEXT st;
+ /* better not be called unless searching */
+ if (!md->stream->private.search.string) {
+ sprintf (tmp,"Search botch, mbx = %.80s, %s = %lu[%.80s]",
+ md->stream->mailbox,
+ (md->flags & FT_UID) ? "UID" : "msg",md->msgno,md->what);
+ fatal (tmp);
+ }
+ /* initially no match for search */
+ md->stream->private.search.result = NIL;
+ /* make sure buffer clear */
+ memset (st.data = (unsigned char *) tmp,'\0',
+ (size_t) MAILTMPLEN+SEARCHSLOP+1);
+ /* read first buffer */
+ (*f) (stream,st.size = i = min (size,(long) MAILTMPLEN),tmp);
+ /* search for text */
+ if (mail_search_string (&st,NIL,&md->stream->private.search.string))
+ md->stream->private.search.result = T;
+ else if (size -= i) { /* more to do, blat slop down */
+ memmove (tmp,tmp+MAILTMPLEN-SEARCHSLOP,(size_t) SEARCHSLOP);
+ do { /* read subsequent buffers one at a time */
+ (*f) (stream,i = min (size,(long) MAILTMPLEN),tmp+SEARCHSLOP);
+ st.size = i + SEARCHSLOP;
+ if (mail_search_string (&st,NIL,&md->stream->private.search.string))
+ md->stream->private.search.result = T;
+ else memmove (tmp,tmp+MAILTMPLEN,(size_t) SEARCHSLOP);
+ }
+ while ((size -= i) && !md->stream->private.search.result);
+ }
+ if (size) { /* toss out everything after that */
+ do (*f) (stream,i = min (size,(long) MAILTMPLEN),tmp);
+ while (size -= i);
+ }
+ return NIL;
+}
+
+/* Mail parse search criteria
+ * Accepts: criteria
+ * Returns: search program if parse successful, else NIL
+ */
+
+SEARCHPGM *mail_criteria (char *criteria)
+{
+ SEARCHPGM *pgm = NIL;
+ char *criterion,*r,tmp[MAILTMPLEN];
+ int f;
+ if (criteria) { /* only if criteria defined */
+ /* make writeable copy of criteria */
+ criteria = cpystr (criteria);
+ /* for each criterion */
+ for (pgm = mail_newsearchpgm (), criterion = strtok_r (criteria," ",&r);
+ criterion; (criterion = strtok_r (NIL," ",&r))) {
+ f = NIL; /* init then scan the criterion */
+ switch (*ucase (criterion)) {
+ case 'A': /* possible ALL, ANSWERED */
+ if (!strcmp (criterion+1,"LL")) f = T;
+ else if (!strcmp (criterion+1,"NSWERED")) f = pgm->answered = T;
+ break;
+ case 'B': /* possible BCC, BEFORE, BODY */
+ if (!strcmp (criterion+1,"CC"))
+ f = mail_criteria_string (&pgm->bcc,&r);
+ else if (!strcmp (criterion+1,"EFORE"))
+ f = mail_criteria_date (&pgm->before,&r);
+ else if (!strcmp (criterion+1,"ODY"))
+ f = mail_criteria_string (&pgm->body,&r);
+ break;
+ case 'C': /* possible CC */
+ if (!strcmp (criterion+1,"C")) f = mail_criteria_string (&pgm->cc,&r);
+ break;
+ case 'D': /* possible DELETED */
+ if (!strcmp (criterion+1,"ELETED")) f = pgm->deleted = T;
+ break;
+ case 'F': /* possible FLAGGED, FROM */
+ if (!strcmp (criterion+1,"LAGGED")) f = pgm->flagged = T;
+ else if (!strcmp (criterion+1,"ROM"))
+ f = mail_criteria_string (&pgm->from,&r);
+ break;
+ case 'K': /* possible KEYWORD */
+ if (!strcmp (criterion+1,"EYWORD"))
+ f = mail_criteria_string (&pgm->keyword,&r);
+ break;
+
+ case 'N': /* possible NEW */
+ if (!strcmp (criterion+1,"EW")) f = pgm->recent = pgm->unseen = T;
+ break;
+ case 'O': /* possible OLD, ON */
+ if (!strcmp (criterion+1,"LD")) f = pgm->old = T;
+ else if (!strcmp (criterion+1,"N"))
+ f = mail_criteria_date (&pgm->on,&r);
+ break;
+ case 'R': /* possible RECENT */
+ if (!strcmp (criterion+1,"ECENT")) f = pgm->recent = T;
+ break;
+ case 'S': /* possible SEEN, SINCE, SUBJECT */
+ if (!strcmp (criterion+1,"EEN")) f = pgm->seen = T;
+ else if (!strcmp (criterion+1,"INCE"))
+ f = mail_criteria_date (&pgm->since,&r);
+ else if (!strcmp (criterion+1,"UBJECT"))
+ f = mail_criteria_string (&pgm->subject,&r);
+ break;
+ case 'T': /* possible TEXT, TO */
+ if (!strcmp (criterion+1,"EXT"))
+ f = mail_criteria_string (&pgm->text,&r);
+ else if (!strcmp (criterion+1,"O"))
+ f = mail_criteria_string (&pgm->to,&r);
+ break;
+ case 'U': /* possible UN* */
+ if (criterion[1] == 'N') {
+ if (!strcmp (criterion+2,"ANSWERED")) f = pgm->unanswered = T;
+ else if (!strcmp (criterion+2,"DELETED")) f = pgm->undeleted = T;
+ else if (!strcmp (criterion+2,"FLAGGED")) f = pgm->unflagged = T;
+ else if (!strcmp (criterion+2,"KEYWORD"))
+ f = mail_criteria_string (&pgm->unkeyword,&r);
+ else if (!strcmp (criterion+2,"SEEN")) f = pgm->unseen = T;
+ }
+ break;
+ default: /* we will barf below */
+ break;
+ }
+ if (!f) { /* if can't identify criterion */
+ sprintf (tmp,"Unknown search criterion: %.30s",criterion);
+ MM_LOG (tmp,ERROR);
+ mail_free_searchpgm (&pgm);
+ break;
+ }
+ }
+ /* no longer need copy of criteria */
+ fs_give ((void **) &criteria);
+ }
+ return pgm;
+}
+
+/* Parse a date
+ * Accepts: pointer to date integer to return
+ * pointer to strtok state
+ * Returns: T if successful, else NIL
+ */
+
+int mail_criteria_date (unsigned short *date,char **r)
+{
+ STRINGLIST *s = NIL;
+ MESSAGECACHE elt;
+ /* parse the date and return fn if OK */
+ int ret = (mail_criteria_string (&s,r) &&
+ mail_parse_date (&elt,(char *) s->text.data) &&
+ (*date = mail_shortdate (elt.year,elt.month,elt.day))) ?
+ T : NIL;
+ if (s) mail_free_stringlist (&s);
+ return ret;
+}
+
+/* Calculate shortdate from elt values
+ * Accepts: year (0 = BASEYEAR)
+ * month (1 = January)
+ * day
+ * Returns: shortdate
+ */
+
+unsigned short mail_shortdate (unsigned int year,unsigned int month,
+ unsigned int day)
+{
+ return (year << 9) + (month << 5) + day;
+}
+
+/* Parse a string
+ * Accepts: pointer to stringlist
+ * pointer to strtok state
+ * Returns: T if successful, else NIL
+ */
+
+int mail_criteria_string (STRINGLIST **s,char **r)
+{
+ unsigned long n;
+ char e,*d,*end = " ",*c = strtok_r (NIL,"",r);
+ if (!c) return NIL; /* missing argument */
+ switch (*c) { /* see what the argument is */
+ case '{': /* literal string */
+ n = strtoul (c+1,&d,10); /* get its length */
+ if ((*d++ == '}') && (*d++ == '\015') && (*d++ == '\012') &&
+ (!(*(c = d + n)) || (*c == ' '))) {
+ e = *--c; /* store old delimiter */
+ *c = '\377'; /* make sure not a space */
+ strtok_r (c," ",r); /* reset the strtok mechanism */
+ *c = e; /* put character back */
+ break;
+ }
+ case '\0': /* catch bogons */
+ case ' ':
+ return NIL;
+ case '"': /* quoted string */
+ if (strchr (c+1,'"')) end = "\"";
+ else return NIL; /* falls through */
+ default: /* atomic string */
+ if (d = strtok_r (c,end,r)) n = strlen (d);
+ else return NIL;
+ break;
+ }
+ while (*s) s = &(*s)->next; /* find tail of list */
+ *s = mail_newstringlist (); /* make new entry */
+ /* return the data */
+ (*s)->text.data = (unsigned char *) cpystr (d);
+ (*s)->text.size = n;
+ return T;
+}
+
+/* Mail parse set from string
+ * Accepts: string to parse
+ * pointer to updated string pointer for return
+ * Returns: set with pointer updated, or NIL if error
+ */
+
+SEARCHSET *mail_parse_set (char *s,char **ret)
+{
+ SEARCHSET *cur;
+ SEARCHSET *set = NIL;
+ while (isdigit (*s)) {
+ if (!set) cur = set = mail_newsearchset ();
+ else cur = cur->next = mail_newsearchset ();
+ /* parse value */
+ if (!(cur->first = strtoul (s,&s,10)) ||
+ ((*s == ':') && !(isdigit (*++s) && (cur->last = strtoul (s,&s,10)))))
+ break; /* bad value or range */
+ if (*s == ',') ++s; /* point to next value if more */
+ else { /* end of set */
+ *ret = s; /* set return pointer */
+ return set; /* return set */
+ }
+ }
+ mail_free_searchset (&set); /* failure, punt partial set */
+ return NIL;
+}
+
+
+/* Mail append to set
+ * Accepts: head of search set or NIL to do nothing
+ * message to add
+ * Returns: tail of search set or NIL if did nothing
+ */
+
+SEARCHSET *mail_append_set (SEARCHSET *set,unsigned long msgno)
+{
+ if (set) { /* find tail */
+ while (set->next) set = set->next;
+ /* start of set if no first member */
+ if (!set->first) set->first = msgno;
+ else if (msgno == (set->last ? set->last : set->first) + 1)
+ set->last = msgno; /* extend range if 1 past current */
+ else (set = set->next = mail_newsearchset ())->first = msgno;
+ }
+ return set;
+}
+
+/* Mail sort messages
+ * Accepts: mail stream
+ * character set
+ * search program
+ * sort program
+ * option flags
+ * Returns: vector of sorted message sequences or NIL if error
+ */
+
+unsigned long *mail_sort (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
+ SORTPGM *pgm,long flags)
+{
+ unsigned long *ret = NIL;
+ if (stream->dtb) /* do the driver's action */
+ ret = (*(stream->dtb->sort ? stream->dtb->sort : mail_sort_msgs))
+ (stream,charset,spg,pgm,flags);
+ /* flush search/sort programs if requested */
+ if (spg && (flags & SE_FREE)) mail_free_searchpgm (&spg);
+ if (flags & SO_FREE) mail_free_sortpgm (&pgm);
+ return ret;
+}
+
+/* Mail sort messages work routine
+ * Accepts: mail stream
+ * character set
+ * search program
+ * sort program
+ * option flags
+ * Returns: vector of sorted message sequences or NIL if error
+ */
+
+unsigned long *mail_sort_msgs (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
+ SORTPGM *pgm,long flags)
+{
+ unsigned long i;
+ SORTCACHE **sc;
+ unsigned long *ret = NIL;
+ if (spg) { /* only if a search needs to be done */
+ int silent = stream->silent;
+ stream->silent = T; /* don't pass up mm_searched() events */
+ /* search for messages */
+ mail_search_full (stream,charset,spg,NIL);
+ stream->silent = silent; /* restore silence state */
+ }
+ /* initialize progress counters */
+ pgm->nmsgs = pgm->progress.cached = 0;
+ /* pass 1: count messages to sort */
+ for (i = 1; i <= stream->nmsgs; ++i)
+ if (mail_elt (stream,i)->searched) pgm->nmsgs++;
+ if (pgm->nmsgs) { /* pass 2: sort cache */
+ sc = mail_sort_loadcache (stream,pgm);
+ /* pass 3: sort messages */
+ if (!pgm->abort) ret = mail_sort_cache (stream,pgm,sc,flags);
+ fs_give ((void **) &sc); /* don't need sort vector any more */
+ }
+ /* empty sort results */
+ else ret = (unsigned long *) memset (fs_get (sizeof (unsigned long)),0,
+ sizeof (unsigned long));
+ /* also return via callback if requested */
+ if (mailsortresults) (*mailsortresults) (stream,ret,pgm->nmsgs);
+ return ret; /* return sort results */
+}
+
+/* Mail sort sortcache vector
+ * Accepts: mail stream
+ * sort program
+ * sortcache vector
+ * option flags
+ * Returns: vector of sorted message sequences or NIL if error
+ */
+
+unsigned long *mail_sort_cache (MAILSTREAM *stream,SORTPGM *pgm,SORTCACHE **sc,
+ long flags)
+{
+ unsigned long i,*ret;
+ /* pass 3: sort messages */
+ qsort ((void *) sc,pgm->nmsgs,sizeof (SORTCACHE *),mail_sort_compare);
+ /* optional post sorting */
+ if (pgm->postsort) (*pgm->postsort) ((void *) sc);
+ /* pass 4: return results */
+ ret = (unsigned long *) fs_get ((pgm->nmsgs+1) * sizeof (unsigned long));
+ if (flags & SE_UID) /* UID or msgno? */
+ for (i = 0; i < pgm->nmsgs; i++) ret[i] = mail_uid (stream,sc[i]->num);
+ else for (i = 0; i < pgm->nmsgs; i++) ret[i] = sc[i]->num;
+ ret[pgm->nmsgs] = 0; /* tie off message list */
+ return ret;
+}
+
+/* Mail load sortcache
+ * Accepts: mail stream, already searched
+ * sort program
+ * Returns: vector of sortcache pointers matching search
+ */
+
+static STRINGLIST maildateline = {{(unsigned char *) "date",4},NIL};
+static STRINGLIST mailrnfromline = {{(unsigned char *) ">from",5},NIL};
+static STRINGLIST mailfromline = {{(unsigned char *) "from",4},
+ &mailrnfromline};
+static STRINGLIST mailtonline = {{(unsigned char *) "to",2},NIL};
+static STRINGLIST mailccline = {{(unsigned char *) "cc",2},NIL};
+static STRINGLIST mailsubline = {{(unsigned char *) "subject",7},NIL};
+
+SORTCACHE **mail_sort_loadcache (MAILSTREAM *stream,SORTPGM *pgm)
+{
+ char *t,*v,*x,tmp[MAILTMPLEN];
+ SORTPGM *pg;
+ SORTCACHE *s,**sc;
+ MESSAGECACHE *elt,telt;
+ ENVELOPE *env;
+ ADDRESS *adr = NIL;
+ unsigned long i = (pgm->nmsgs) * sizeof (SORTCACHE *);
+ sc = (SORTCACHE **) memset (fs_get ((size_t) i),0,(size_t) i);
+ /* see what needs to be loaded */
+ for (i = 1; !pgm->abort && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->searched) {
+ sc[pgm->progress.cached++] =
+ s = (SORTCACHE *) (*mailcache) (stream,i,CH_SORTCACHE);
+ s->pgm = pgm; /* note sort program */
+ s->num = i;
+ /* get envelope if cached */
+ if (stream->scache) env = (i == stream->msgno) ? stream->env : NIL;
+ else env = elt->private.msg.env;
+ for (pg = pgm; pg; pg = pg->next) switch (pg->function) {
+ case SORTARRIVAL: /* sort by arrival date */
+ if (!s->arrival) {
+ /* internal date unknown but can get? */
+ if (!elt->day && !(stream->dtb->flags & DR_NOINTDATE)) {
+ sprintf (tmp,"%lu",i);
+ mail_fetch_fast (stream,tmp,NIL);
+ }
+ /* wrong thing before 3-Jan-1970 */
+ s->arrival = elt->day ? mail_longdate (elt) : 1;
+ s->dirty = T;
+ }
+ break;
+ case SORTSIZE: /* sort by message size */
+ if (!s->size) {
+ if (!elt->rfc822_size) {
+ sprintf (tmp,"%lu",i);
+ mail_fetch_fast (stream,tmp,NIL);
+ }
+ s->size = elt->rfc822_size ? elt->rfc822_size : 1;
+ s->dirty = T;
+ }
+ break;
+
+ case SORTDATE: /* sort by date */
+ if (!s->date) {
+ if (env) t = env->date;
+ else if ((t = mail_fetch_header (stream,i,NIL,&maildateline,NIL,
+ FT_INTERNAL | FT_PEEK)) &&
+ (t = strchr (t,':')))
+ for (x = ++t; x = strpbrk (x,"\012\015"); x++)
+ switch (*(v = ((*x == '\015') && (x[1] == '\012')) ? x+2 : x+1)){
+ case ' ': /* erase continuation newlines */
+ case '\t':
+ memmove (x,v,strlen (v));
+ break;
+ default: /* tie off extraneous text */
+ *x = x[1] = '\0';
+ }
+ /* skip leading whitespace */
+ if (t) while ((*t == ' ') || (*t == '\t')) t++;
+ /* parse date from Date: header */
+ if (!(t && mail_parse_date (&telt,t) &&
+ (s->date = mail_longdate (&telt)))) {
+ /* failed, use internal date */
+ if (!(s->date = s->arrival)) {
+ /* internal date unknown but can get? */
+ if (!elt->day && !(stream->dtb->flags & DR_NOINTDATE)) {
+ sprintf (tmp,"%lu",i);
+ mail_fetch_fast (stream,tmp,NIL);
+ }
+ /* wrong thing before 3-Jan-1970 */
+ s->date = (s->arrival = elt->day ? mail_longdate (elt) : 1);
+ }
+ }
+ s->dirty = T;
+ }
+ break;
+
+ case SORTFROM: /* sort by first from */
+ if (!s->from) {
+ if (env) s->from = env->from && env->from->mailbox ?
+ cpystr (env->from->mailbox) : NIL;
+ else if ((t = mail_fetch_header (stream,i,NIL,&mailfromline,NIL,
+ FT_INTERNAL | FT_PEEK)) &&
+ (t = strchr (t,':'))) {
+ for (x = ++t; x = strpbrk (x,"\012\015"); x++)
+ switch (*(v = ((*x == '\015') && (x[1] == '\012')) ? x+2 : x+1)){
+ case ' ': /* erase continuation newlines */
+ case '\t':
+ memmove (x,v,strlen (v));
+ break;
+ case 'f': /* continuation but with extra "From:" */
+ case 'F':
+ if (v = strchr (v,':')) {
+ memmove (x,v+1,strlen (v+1));
+ break;
+ }
+ default: /* tie off extraneous text */
+ *x = x[1] = '\0';
+ }
+ if (adr = rfc822_parse_address (&adr,adr,&t,BADHOST,0)) {
+ s->from = adr->mailbox;
+ adr->mailbox = NIL;
+ mail_free_address (&adr);
+ }
+ }
+ if (!s->from) s->from = cpystr ("");
+ s->dirty = T;
+ }
+ break;
+
+ case SORTTO: /* sort by first to */
+ if (!s->to) {
+ if (env) s->to = env->to && env->to->mailbox ?
+ cpystr (env->to->mailbox) : NIL;
+ else if ((t = mail_fetch_header (stream,i,NIL,&mailtonline,NIL,
+ FT_INTERNAL | FT_PEEK)) &&
+ (t = strchr (t,':'))) {
+ for (x = ++t; x = strpbrk (x,"\012\015"); x++)
+ switch (*(v = ((*x == '\015') && (x[1] == '\012')) ? x+2 : x+1)){
+ case ' ': /* erase continuation newlines */
+ case '\t':
+ memmove (x,v,strlen (v));
+ break;
+ case 't': /* continuation but with extra "To:" */
+ case 'T':
+ if (v = strchr (v,':')) {
+ memmove (x,v+1,strlen (v+1));
+ break;
+ }
+ default: /* tie off extraneous text */
+ *x = x[1] = '\0';
+ }
+ if (adr = rfc822_parse_address (&adr,adr,&t,BADHOST,0)) {
+ s->to = adr->mailbox;
+ adr->mailbox = NIL;
+ mail_free_address (&adr);
+ }
+ }
+ if (!s->to) s->to = cpystr ("");
+ s->dirty = T;
+ }
+ break;
+
+ case SORTCC: /* sort by first cc */
+ if (!s->cc) {
+ if (env) s->cc = env->cc && env->cc->mailbox ?
+ cpystr (env->cc->mailbox) : NIL;
+ else if ((t = mail_fetch_header (stream,i,NIL,&mailccline,NIL,
+ FT_INTERNAL | FT_PEEK)) &&
+ (t = strchr (t,':'))) {
+ for (x = ++t; x = strpbrk (x,"\012\015"); x++)
+ switch (*(v = ((*x == '\015') && (x[1] == '\012')) ? x+2 : x+1)){
+ case ' ': /* erase continuation newlines */
+ case '\t':
+ memmove (x,v,strlen (v));
+ break;
+ case 't': /* continuation but with extra "To:" */
+ case 'T':
+ if (v = strchr (v,':')) {
+ memmove (x,v+1,strlen (v+1));
+ break;
+ }
+ default: /* tie off extraneous text */
+ *x = x[1] = '\0';
+ }
+ if (adr = rfc822_parse_address (&adr,adr,&t,BADHOST,0)) {
+ s->cc = adr->mailbox;
+ adr->mailbox = NIL;
+ mail_free_address (&adr);
+ }
+ }
+ if (!s->cc) s->cc = cpystr ("");
+ s->dirty = T;
+ }
+ break;
+
+ case SORTSUBJECT: /* sort by subject */
+ if (!s->subject) {
+ /* get subject from envelope if have one */
+ if (env) t = env->subject ? env->subject : "";
+ /* otherwise snarf from header text */
+ else if ((t = mail_fetch_header (stream,i,NIL,&mailsubline,
+ NIL,FT_INTERNAL | FT_PEEK)) &&
+ (t = strchr (t,':')))
+ for (x = ++t; x = strpbrk (x,"\012\015"); x++)
+ switch (*(v = ((*x == '\015') && (x[1] == '\012')) ? x+2 : x+1)){
+ case ' ': /* erase continuation newlines */
+ case '\t':
+ memmove (x,v,strlen (v));
+ break;
+ default: /* tie off extraneous text */
+ *x = x[1] = '\0';
+ }
+ else t = ""; /* empty subject */
+ /* strip and cache subject */
+ s->refwd = mail_strip_subject (t,&s->subject);
+ s->dirty = T;
+ }
+ break;
+ default:
+ fatal ("Unknown sort function");
+ }
+ }
+ return sc;
+}
+
+/* Strip subjects of extra spaces and leading and trailing cruft for sorting
+ * Accepts: unstripped subject
+ * pointer to return stripped subject, in cpystr form
+ * Returns: T if subject had a re/fwd, NIL otherwise
+ */
+
+unsigned int mail_strip_subject (char *t,char **ret)
+{
+ SIZEDTEXT src,dst;
+ unsigned long i,slen;
+ char c,*s,*x;
+ unsigned int refwd = NIL;
+ if (src.size = strlen (t)) { /* have non-empty subject? */
+ src.data = (unsigned char *) t;
+ /* Step 1 */
+ /* make copy, convert MIME2 if needed */
+ *ret = s = (utf8_mime2text (&src,&dst,U8T_CANONICAL) &&
+ (src.data != dst.data)) ? (char *) dst.data : cpystr (t);
+ /* convert spaces to tab, strip extra spaces */
+ for (x = t = s, c = 'x'; *t; t++) {
+ if (c != ' ') c = *x++ = ((*t == '\t') ? ' ' : *t);
+ else if ((*t != '\t') && (*t != ' ')) c = *x++ = *t;
+ }
+ *x = '\0'; /* tie off string */
+ /* Step 2 */
+ for (slen = dst.size; s; slen = strlen (s)) {
+ for (t = s + slen; t > s; ) switch (t[-1]) {
+ case ' ': case '\t': /* WSP */
+ *--t = '\0'; /* just remove it */
+ break;
+ case ')': /* possible "(fwd)" */
+ if ((t >= (s + 5)) && (t[-5] == '(') &&
+ ((t[-4] == 'F') || (t[-4] == 'f')) &&
+ ((t[-3] == 'W') || (t[-3] == 'w')) &&
+ ((t[-2] == 'D') || (t[-2] == 'd'))) {
+ *(t -= 5) = '\0'; /* remove "(fwd)" */
+ refwd = T; /* note a re/fwd */
+ break;
+ }
+ default: /* not a subj-trailer */
+ t = s;
+ break;
+ }
+ /* Steps 3-5 */
+ for (t = s; t; ) switch (*s) {
+ case ' ': case '\t': /* WSP */
+ s = t = mail_strip_subject_wsp (s + 1);
+ break;
+ case 'r': case 'R': /* possible "re" */
+ if (((s[1] == 'E') || (s[1] == 'e')) &&
+ (t = mail_strip_subject_wsp (s + 2)) &&
+ (t = mail_strip_subject_blob (t)) && (*t == ':')) {
+ s = ++t; /* found "re" */
+ refwd = T; /* definitely a re/fwd at this point */
+ }
+ else t = NIL; /* found subj-middle */
+ break;
+ case 'f': case 'F': /* possible "fw" or "fwd" */
+ if (((s[1] == 'w') || (s[1] == 'W')) &&
+ (((s[2] == 'd') || (s[2] == 'D')) ?
+ (t = mail_strip_subject_wsp (s + 3)) :
+ (t = mail_strip_subject_wsp (s + 2))) &&
+ (t = mail_strip_subject_blob (t)) && (*t == ':')) {
+ s = ++t; /* found "fwd" */
+ refwd = T; /* definitely a re/fwd at this point */
+ }
+ else t = NIL; /* found subj-middle */
+ break;
+ case '[': /* possible subj-blob */
+ if ((t = mail_strip_subject_blob (s)) && *t) s = t;
+ else t = NIL; /* found subj-middle */
+ break;
+ default:
+ t = NIL; /* found subj-middle */
+ break;
+ }
+ /* Step 6 */
+ /* Netscape-style "[Fwd: ...]"? */
+ if ((*s == '[') && ((s[1] == 'F') || (s[1] == 'f')) &&
+ ((s[2] == 'W') || (s[2] == 'w')) &&
+ ((s[3] == 'D') || (s[3] == 'd')) && (s[4] == ':') &&
+ (s[i = strlen (s) - 1] == ']')) {
+ s[i] = '\0'; /* flush closing "]" */
+ s += 5; /* and leading "[Fwd:" */
+ refwd = T; /* definitely a re/fwd at this point */
+ }
+ else break; /* don't need to loop back to step 2 */
+ }
+ if (s != (t = *ret)) { /* removed leading text? */
+ s = *ret = cpystr (s); /* yes, make a fresh return copy */
+ fs_give ((void **) &t); /* flush old copy */
+ }
+ }
+ else *ret = cpystr (""); /* empty subject */
+ return refwd; /* return re/fwd state */
+}
+
+/* Strip subject wsp helper routine
+ * Accepts: text
+ * Returns: pointer to text after blob
+ */
+
+char *mail_strip_subject_wsp (char *s)
+{
+ while ((*s == ' ') || (*s == '\t')) s++;
+ return s;
+}
+
+
+/* Strip subject blob helper routine
+ * Accepts: text
+ * Returns: pointer to text after any blob, NIL if blob-like but not blob
+ */
+
+char *mail_strip_subject_blob (char *s)
+{
+ if (*s != '[') return s; /* not a blob, ignore */
+ /* search for end of blob */
+ while (*++s != ']') if ((*s == '[') || !*s) return NIL;
+ return mail_strip_subject_wsp (s + 1);
+}
+
+/* Sort compare messages
+ * Accept: first message sort cache element
+ * second message sort cache element
+ * Returns: -1 if a1 < a2, 0 if a1 == a2, 1 if a1 > a2
+ */
+
+int mail_sort_compare (const void *a1,const void *a2)
+{
+ int i = 0;
+ SORTCACHE *s1 = *(SORTCACHE **) a1;
+ SORTCACHE *s2 = *(SORTCACHE **) a2;
+ SORTPGM *pgm = s1->pgm;
+ if (!s1->sorted) { /* this one sorted yet? */
+ s1->sorted = T;
+ pgm->progress.sorted++; /* another sorted message */
+ }
+ if (!s2->sorted) { /* this one sorted yet? */
+ s2->sorted = T;
+ pgm->progress.sorted++; /* another sorted message */
+ }
+ do {
+ switch (pgm->function) { /* execute search program */
+ case SORTDATE: /* sort by date */
+ i = compare_ulong (s1->date,s2->date);
+ break;
+ case SORTARRIVAL: /* sort by arrival date */
+ i = compare_ulong (s1->arrival,s2->arrival);
+ break;
+ case SORTSIZE: /* sort by message size */
+ i = compare_ulong (s1->size,s2->size);
+ break;
+ case SORTFROM: /* sort by first from */
+ i = compare_cstring (s1->from,s2->from);
+ break;
+ case SORTTO: /* sort by first to */
+ i = compare_cstring (s1->to,s2->to);
+ break;
+ case SORTCC: /* sort by first cc */
+ i = compare_cstring (s1->cc,s2->cc);
+ break;
+ case SORTSUBJECT: /* sort by subject */
+ i = compare_cstring (s1->subject,s2->subject);
+ break;
+ }
+ if (pgm->reverse) i = -i; /* flip results if necessary */
+ }
+ while (pgm = i ? NIL : pgm->next);
+ /* return result, avoid 0 if at all possible */
+ return i ? i : compare_ulong (s1->num,s2->num);
+}
+
+/* Return message date as an unsigned long seconds since time began
+ * Accepts: message cache pointer
+ * Returns: unsigned long of date
+ *
+ * This routine, like most UNIX systems, is clueless about leap seconds.
+ * Thus, it treats 23:59:60 as equivalent to 00:00:00 the next day.
+ *
+ * This routine forces any early hours on 1-Jan-1970 in oriental timezones
+ * to be 1-Jan-1970 00:00:00 UTC, so as to avoid negative longdates.
+ */
+
+unsigned long mail_longdate (MESSAGECACHE *elt)
+{
+ unsigned long m = elt->month ? elt->month : 1;
+ unsigned long yr = elt->year + BASEYEAR;
+ /* number of days since time began */
+ unsigned long ret = (elt->day ? (elt->day - 1) : 0)
+ + 30 * (m - 1) + ((m + (m > 8)) / 2)
+#ifndef USEJULIANCALENDAR
+#ifndef USEORTHODOXCALENDAR /* Gregorian calendar */
+ + ((yr / 400) - (BASEYEAR / 400)) - ((yr / 100) - (BASEYEAR / 100))
+#ifdef Y4KBUGFIX
+ - ((yr / 4000) - (BASEYEAR / 4000))
+#endif
+ - ((m < 3) ?
+ !(yr % 4) && ((yr % 100) || (!(yr % 400)
+#ifdef Y4KBUGFIX
+ && (yr % 4000)
+#endif
+ )) : 2)
+#else /* Orthodox calendar */
+ + ((2*(yr / 900)) - (2*(BASEYEAR / 900)))
+ + (((yr % 900) >= 200) - ((BASEYEAR % 900) >= 200))
+ + (((yr % 900) >= 600) - ((BASEYEAR % 900) >= 600))
+ - ((yr / 100) - (BASEYEAR / 100))
+ - ((m < 3) ?
+ !(yr % 4) && ((yr % 100) || ((yr % 900) == 200) || ((yr % 900) == 600))
+ : 2)
+#endif
+#endif
+ + elt->year * 365 + (((unsigned long) (elt->year + (BASEYEAR % 4))) / 4);
+ ret *= 24; ret += elt->hours; /* date value in hours */
+ ret *= 60; ret +=elt->minutes;/* date value in minutes */
+ yr = (elt->zhours * 60) + elt->zminutes;
+ if (elt->zoccident) ret += yr;/* occidental timezone, make UTC */
+ else if (ret < yr) return 0; /* still 31-Dec-1969 in UTC */
+ else ret -= yr; /* oriental timezone, make UTC */
+ ret *= 60; ret += elt->seconds;
+ return ret;
+}
+
+/* Mail thread messages
+ * Accepts: mail stream
+ * thread type
+ * character set
+ * search program
+ * option flags
+ * Returns: thread node tree or NIL if error
+ */
+
+THREADNODE *mail_thread (MAILSTREAM *stream,char *type,char *charset,
+ SEARCHPGM *spg,long flags)
+{
+ THREADNODE *ret = NIL;
+ if (stream->dtb) /* must have a live driver */
+ ret = stream->dtb->thread ? /* do driver's action if available */
+ (*stream->dtb->thread) (stream,type,charset,spg,flags) :
+ mail_thread_msgs (stream,type,charset,spg,flags,mail_sort_msgs);
+ /* flush search/sort programs if requested */
+ if (spg && (flags & SE_FREE)) mail_free_searchpgm (&spg);
+ return ret;
+}
+
+
+/* Mail thread messages
+ * Accepts: mail stream
+ * thread type
+ * character set
+ * search program
+ * option flags
+ * sorter routine
+ * Returns: thread node tree or NIL if error
+ */
+
+THREADNODE *mail_thread_msgs (MAILSTREAM *stream,char *type,char *charset,
+ SEARCHPGM *spg,long flags,sorter_t sorter)
+{
+ THREADER *t;
+ for (t = &mailthreadlist; t; t = t->next)
+ if (!compare_cstring (type,t->name)) {
+ THREADNODE *ret = (*t->dispatch) (stream,charset,spg,flags,sorter);
+ if (mailthreadresults) (*mailthreadresults) (stream,ret);
+ return ret;
+ }
+ MM_LOG ("No such thread type",ERROR);
+ return NIL;
+}
+
+/* Mail thread ordered subject
+ * Accepts: mail stream
+ * character set
+ * search program
+ * option flags
+ * sorter routine
+ * Returns: thread node tree
+ */
+
+THREADNODE *mail_thread_orderedsubject (MAILSTREAM *stream,char *charset,
+ SEARCHPGM *spg,long flags,
+ sorter_t sorter)
+{
+ THREADNODE *thr = NIL;
+ THREADNODE *cur,*top,**tc;
+ SORTPGM pgm,pgm2;
+ SORTCACHE *s;
+ unsigned long i,j,*lst,*ls;
+ /* sort by subject+date */
+ memset (&pgm,0,sizeof (SORTPGM));
+ memset (&pgm2,0,sizeof (SORTPGM));
+ pgm.function = SORTSUBJECT;
+ pgm.next = &pgm2;
+ pgm2.function = SORTDATE;
+ if (lst = (*sorter) (stream,charset,spg,&pgm,flags & ~(SE_FREE | SE_UID))){
+ if (*(ls = lst)) { /* create thread */
+ /* note first subject */
+ cur = top = thr = mail_newthreadnode
+ ((SORTCACHE *) (*mailcache) (stream,*ls++,CH_SORTCACHE));
+ /* note its number */
+ cur->num = (flags & SE_UID) ? mail_uid (stream,*lst) : *lst;
+ i = 1; /* number of threads */
+ while (*ls) { /* build tree */
+ /* subjects match? */
+ s = (SORTCACHE *) (*mailcache) (stream,*ls++,CH_SORTCACHE);
+ if (compare_cstring (top->sc->subject,s->subject)) {
+ i++; /* have a new thread */
+ top = top->branch = cur = mail_newthreadnode (s);
+ }
+ /* start a child of the top */
+ else if (cur == top) cur = cur->next = mail_newthreadnode (s);
+ /* sibling of child */
+ else cur = cur->branch = mail_newthreadnode (s);
+ /* set to msgno or UID as needed */
+ cur->num = (flags & SE_UID) ? mail_uid (stream,s->num) : s->num;
+ }
+ /* make threadnode cache */
+ tc = (THREADNODE **) fs_get (i * sizeof (THREADNODE *));
+ /* load threadnode cache */
+ for (j = 0, cur = thr; cur; cur = cur->branch) tc[j++] = cur;
+ if (i != j) fatal ("Threadnode cache confusion");
+ qsort ((void *) tc,i,sizeof (THREADNODE *),mail_thread_compare_date);
+ for (j = 0, --i; j < i; j++) tc[j]->branch = tc[j+1];
+ tc[j]->branch = NIL; /* end of root */
+ thr = tc[0]; /* head of data */
+ fs_give ((void **) &tc);
+ }
+ fs_give ((void **) &lst);
+ }
+ return thr;
+}
+
+/* Mail thread references
+ * Accepts: mail stream
+ * character set
+ * search program
+ * option flags
+ * sorter routine
+ * Returns: thread node tree
+ */
+
+#define REFHASHSIZE 1009 /* arbitrary prime for hash table size */
+
+/* Reference threading container, as described in Jamie Zawinski's web page
+ * (http://www.jwz.org/doc/threading.html) for this algorithm. These are
+ * stored as extended data in the hash table (called "id_table" in JWZ's
+ * document) and are maintained by the hash table routines. The hash table
+ * routines implement extended data as additional void* words at the end of
+ * each bucket, hence these strange macros instead of a struct which would
+ * have been more straightforward.
+ */
+
+#define THREADLINKS 3 /* number of thread links */
+
+#define CACHE(data) ((SORTCACHE *) (data)[0])
+#define PARENT(data) ((container_t) (data)[1])
+#define SETPARENT(data,value) ((container_t) (data[1] = value))
+#define SIBLING(data) ((container_t) (data)[2])
+#define SETSIBLING(data,value) ((container_t) (data[2] = value))
+#define CHILD(data) ((container_t) (data)[3])
+#define SETCHILD(data,value) ((container_t) (data[3] = value))
+
+THREADNODE *mail_thread_references (MAILSTREAM *stream,char *charset,
+ SEARCHPGM *spg,long flags,sorter_t sorter)
+{
+ MESSAGECACHE *elt,telt;
+ ENVELOPE *env;
+ SORTCACHE *s;
+ STRINGLIST *st;
+ HASHENT *he;
+ THREADNODE **tc,*cur,*lst,*nxt,*sis,*msg;
+ container_t con,nxc,prc,sib;
+ void **sub;
+ char *t,tmp[MAILTMPLEN];
+ unsigned long j,nmsgs;
+ unsigned long i = stream->nmsgs * sizeof (SORTCACHE *);
+ SORTCACHE **sc = (SORTCACHE **) memset (fs_get ((size_t) i),0,(size_t) i);
+ HASHTAB *ht = hash_create (REFHASHSIZE);
+ THREADNODE *root = NIL;
+ if (spg) { /* only if a search needs to be done */
+ int silent = stream->silent;
+ stream->silent = T; /* don't pass up mm_searched() events */
+ /* search for messages */
+ mail_search_full (stream,charset,spg,NIL);
+ stream->silent = silent; /* restore silence state */
+ }
+
+ /* create SORTCACHE vector of requested msgs */
+ for (i = 1, nmsgs = 0; i <= stream->nmsgs; ++i)
+ if (mail_elt (stream,i)->searched)
+ (sc[nmsgs++] = (SORTCACHE *)(*mailcache)(stream,i,CH_SORTCACHE))->num =i;
+ /* separate pass so can do overview fetch lookahead */
+ for (i = 0; i < nmsgs; ++i) { /* for each requested message */
+ /* is anything missing in its SORTCACHE? */
+ if (!((s = sc[i])->date && s->subject && s->message_id && s->references)) {
+ /* driver has an overview mechanism? */
+ if (stream->dtb && stream->dtb->overview) {
+ /* yes, find following unloaded entries */
+ for (j = i + 1; (j < nmsgs) && !sc[j]->references; ++j);
+ sprintf (tmp,"%lu",mail_uid (stream,s->num));
+ if (i != --j) /* end of range different? */
+ sprintf (tmp + strlen (tmp),":%lu",mail_uid (stream,sc[j]->num));
+ /* load via overview mechanism */
+ mail_fetch_overview (stream,tmp,mail_thread_loadcache);
+ }
+ /* still missing data? */
+ if (!s->date || !s->subject || !s->message_id || !s->references) {
+ /* try to load data from envelope */
+ if (env = mail_fetch_structure (stream,s->num,NIL,NIL)) {
+ if (!s->date && env->date && mail_parse_date (&telt,env->date))
+ s->date = mail_longdate (&telt);
+ if (!s->subject && env->subject)
+ s->refwd =
+ mail_strip_subject (env->subject,&s->subject);
+ if (!s->message_id && env->message_id && *env->message_id)
+ s->message_id = mail_thread_parse_msgid (env->message_id,NIL);
+ if (!s->references && /* use References: or In-Reply-To: */
+ !(s->references =
+ mail_thread_parse_references (env->references,T)))
+ s->references = mail_thread_parse_references(env->in_reply_to,NIL);
+ }
+ /* last resort */
+ if (!s->date && !(s->date = s->arrival)) {
+ /* internal date unknown but can get? */
+ if (!(elt = mail_elt (stream,s->num))->day &&
+ !(stream->dtb->flags & DR_NOINTDATE)) {
+ sprintf (tmp,"%lu",s->num);
+ mail_fetch_fast (stream,tmp,NIL);
+ }
+ /* wrong thing before 3-Jan-1970 */
+ s->date = (s->arrival = elt->day ? mail_longdate (elt) : 1);
+ }
+ if (!s->subject) s->subject = cpystr ("");
+ if (!s->references) s->references = mail_newstringlist ();
+ s->dirty = T;
+ }
+ }
+
+ /* Step 1 (preliminary) */
+ /* generate unique string */
+ sprintf (tmp,"%s.%lx.%lx@%s",stream->mailbox,stream->uid_validity,
+ mail_uid (stream,s->num),mylocalhost ());
+ /* flush old unique string if not message-id */
+ if (s->unique && (s->unique != s->message_id))
+ fs_give ((void **) &s->unique);
+ s->unique = s->message_id ? /* don't permit Message ID duplicates */
+ (hash_lookup (ht,s->message_id) ? cpystr (tmp) : s->message_id) :
+ (s->message_id = cpystr (tmp));
+ /* add unique string to hash table */
+ hash_add (ht,s->unique,s,THREADLINKS);
+ }
+ /* Step 1 */
+ for (i = 0; i < nmsgs; ++i) { /* for each message in sortcache */
+ /* Step 1A */
+ if ((st = (s = sc[i])->references) && st->text.data)
+ for (con = hash_lookup_and_add (ht,(char *) st->text.data,NIL,
+ THREADLINKS); st = st->next; con = nxc) {
+ nxc = hash_lookup_and_add (ht,(char *) st->text.data,NIL,THREADLINKS);
+ /* only if no parent & won't introduce loop */
+ if (!PARENT (nxc) && !mail_thread_check_child (con,nxc)) {
+ SETPARENT (nxc,con); /* establish parent/child link */
+ /* other children become sibling of this one */
+ SETSIBLING (nxc,CHILD (con));
+ SETCHILD (con,nxc); /* set as child of parent */
+ }
+ }
+ else con = NIL; /* else message has no ancestors */
+ /* Step 1B */
+ if ((prc = PARENT ((nxc = hash_lookup (ht,s->unique)))) &&
+ (prc != con)) { /* break links if have a different parent */
+ SETPARENT (nxc,NIL); /* easy if direct child */
+ if (nxc == CHILD (prc)) SETCHILD (prc,SIBLING (nxc));
+ else { /* otherwise hunt through sisters */
+ for (sib = CHILD (prc); nxc != SIBLING (sib); sib = SIBLING (sib));
+ SETSIBLING (sib,SIBLING (nxc));
+ }
+ SETSIBLING (nxc,NIL); /* no more little sisters either */
+ prc = NIL; /* no more parent set */
+ }
+ /* need to set parent, and parent is good? */
+ if (!prc && !mail_thread_check_child (con,nxc)) {
+ SETPARENT (nxc,con); /* establish parent/child link */
+ if (con) { /* if non-root parent, set parent's child */
+ if (CHILD (con)) { /* have a child already */
+ /* find youngest daughter */
+ for (con = CHILD (con); SIBLING (con); con = SIBLING (con));
+ SETSIBLING (con,nxc); /* add new baby sister */
+ }
+ else SETCHILD (con,nxc);/* set as only child */
+ }
+ }
+ }
+ fs_give ((void **) &sc); /* finished with sortcache vector */
+
+ /* Step 2 */
+ /* search hash table for parentless messages */
+ for (i = 0, prc = con = NIL; i < ht->size; i++)
+ for (he = ht->table[i]; he; he = he->next)
+ if (!PARENT ((nxc = he->data))) {
+ /* sibling of previous parentless message */
+ if (con) con = SETSIBLING (con,nxc);
+ else prc = con = nxc; /* first parentless message */
+ }
+ /* Once the dummy containers are pruned, we no longer need the parent
+ * information, so we can convert the containers to THREADNODEs. Since
+ * we don't need the id_table any more either, we can reset the hash table
+ * and reuse it as a subject_table. Resetting the hash table will also
+ * destroy the containers.
+ */
+ /* Step 3 */
+ /* prune dummies, convert to threadnode */
+ root = mail_thread_c2node (stream,mail_thread_prune_dummy (prc,NIL),flags);
+ /* Step 4 */
+ /* make buffer for sorting */
+ tc = (THREADNODE **) fs_get (nmsgs * sizeof (THREADNODE *));
+ /* load threadcache and count nodes to sort */
+ for (i = 0, cur = root; cur ; cur = cur->branch) tc[i++] = cur;
+ if (i > 1) { /* only if need to sort */
+ qsort ((void *) tc,i,sizeof (THREADNODE *),mail_thread_compare_date);
+ /* relink siblings */
+ for (j = 0, --i; j < i; j++) tc[j]->branch = tc[j+1];
+ tc[j]->branch = NIL; /* end of root */
+ root = tc[0]; /* establish new root */
+ }
+ /* Step 5A */
+ hash_reset (ht); /* discard containers, reset ht */
+ /* Step 5B */
+ for (cur = root; cur; cur = cur->branch)
+ if ((t = (nxt = (cur->sc ? cur : cur->next))->sc->subject) && *t) {
+ /* add new subject to hash table */
+ if (!(sub = hash_lookup (ht,t))) hash_add (ht,t,cur,0);
+ /* if one in table not dummy and */
+ else if ((s = (lst = (THREADNODE *) sub[0])->sc) &&
+ /* current dummy, or not re/fwd and table is */
+ (!cur->sc || (!nxt->sc->refwd && s->refwd)))
+ sub[0] = (void *) cur; /* replace with this message */
+ }
+
+ /* Step 5C */
+ for (cur = root, sis = NIL; cur; cur = msg) {
+ /* do nothing if current message or no sub */
+ if (!(t = (cur->sc ? cur : cur->next)->sc->subject) || !*t ||
+ ((lst = (THREADNODE *) (sub = hash_lookup (ht,t))[0]) == cur))
+ msg = (sis = cur)->branch;
+ else if (!lst->sc) { /* is message in the table a dummy? */
+ /* find youngest daughter of msg in table */
+ for (msg = lst->next; msg->branch; msg = msg->branch);
+ if (!cur->sc) { /* current message a dummy? */
+ msg->branch = cur->next;/* current's daughter now dummy's youngest */
+ msg = cur->branch; /* continue scan at younger sister */
+ /* now delete this node */
+ cur->branch = cur->next = NIL;
+ mail_free_threadnode (&cur);
+ }
+ else { /* current message not a dummy */
+ msg->branch = cur; /* append as youngest daughter */
+ msg = cur->branch; /* continue scan at younger sister */
+ cur->branch = NIL; /* lose our younger sisters */
+ }
+ }
+ else { /* no dummies, is current re/fwd, table not? */
+ if (cur->sc->refwd && !lst->sc->refwd) {
+ if (lst->next) { /* find youngest daughter of msg in table */
+ for (msg = lst->next; msg->branch; msg = msg->branch);
+ msg->branch = cur; /* append as youngest daughter */
+ }
+ else lst->next = cur; /* no children, so make the eldest daughter */
+ }
+
+ else { /* no re/fwd, create a new dummy */
+ msg = mail_newthreadnode (NIL);
+ if (lst == root) { /* msg in table is root? */
+ root = lst->branch; /* younger sister becomes new root */
+ /* no longer older sister either */
+ if (lst == sis) sis = NIL;
+ }
+ else { /* find older sister of msg in table */
+ for (nxt = root; lst != nxt->branch; nxt = nxt->branch);
+ /* remove from older sister */
+ nxt->branch = lst->branch;
+ }
+ msg->next = lst; /* msg in table becomes child */
+ lst->branch = cur; /* current now little sister of msg in table */
+ if (sis) { /* have an elder sister? */
+ if (sis == lst) /* rescan if lost her */
+ for (sis = root; cur != sis->branch; sis = sis->branch);
+ sis->branch = msg; /* make dummy younger sister of big sister */
+ }
+ else root = msg; /* otherwise this is the new root */
+ sub[0] = sis = msg; /* set new msg in table and new big sister */
+ }
+ msg = cur->branch; /* continue scan at younger sister */
+ cur->branch = NIL; /* lose our younger sisters */
+ }
+ if (sis) sis->branch = msg; /* older sister gets this as younger sister */
+ else root = msg; /* otherwise this is the new root */
+ }
+ hash_destroy (&ht); /* finished with hash table */
+ /* Step 6 */
+ /* sort threads */
+ root = mail_thread_sort (root,tc);
+ fs_give ((void **) &tc); /* finished with sort buffer */
+ return root; /* return sorted list */
+}
+
+/* Fetch overview callback to load sortcache for threading
+ * Accepts: MAIL stream
+ * UID of this message
+ * overview of this message
+ * msgno of this message
+ */
+
+void mail_thread_loadcache (MAILSTREAM *stream,unsigned long uid,OVERVIEW *ov,
+ unsigned long msgno)
+{
+ if (msgno && ov) { /* just in case */
+ MESSAGECACHE telt, *elt;
+ ENVELOPE *env;
+ SORTCACHE *s = (SORTCACHE *) (*mailcache) (stream,msgno,CH_SORTCACHE);
+ if (!s->subject && ov->subject) {
+ s->refwd = mail_strip_subject (ov->subject,&s->subject);
+ s->dirty = T;
+ }
+ if (!s->from && ov->from && ov->from->mailbox) {
+ s->from = cpystr (ov->from->mailbox);
+ s->dirty = T;
+ }
+ if (!s->date && ov->date && mail_parse_date (&telt,ov->date)) {
+ s->date = mail_longdate (&telt);
+ s->dirty = T;
+ }
+ if (!s->message_id && ov->message_id) {
+ s->message_id = mail_thread_parse_msgid (ov->message_id,NIL);
+ s->dirty = T;
+ }
+ if (!s->references &&
+ !(s->references = mail_thread_parse_references (ov->references,T))
+ && stream->dtb && !strcmp(stream->dtb->name, "imap")
+ && (elt = mail_elt (stream, msgno)) != NULL
+ && (env = elt->private.msg.env) != NULL
+ && env->in_reply_to
+ && !(s->references = mail_thread_parse_references(env->in_reply_to, NIL))) {
+ /* don't do In-Reply-To with NNTP mailboxes */
+ s->references = mail_newstringlist ();
+ s->dirty = T;
+ }
+ if (!s->size && ov->optional.octets) {
+ s->size = ov->optional.octets;
+ s->dirty = T;
+ }
+ }
+}
+
+/* Thread parse Message ID
+ * Accepts: pointer to purported Message ID
+ * pointer to return pointer
+ * Returns: Message ID or NIL, return pointer updated
+ */
+
+char *mail_thread_parse_msgid (char *s,char **ss)
+{
+ char *ret = NIL;
+ char *t = NIL;
+ ADDRESS *adr;
+ if (s) { /* only for non-NIL strings */
+ rfc822_skipws (&s); /* skip whitespace */
+ /* ignore phrases */
+ if (((*s == '<') || (s = rfc822_parse_phrase (s))) &&
+ (adr = rfc822_parse_routeaddr (s,&t,BADHOST))) {
+ /* make return msgid */
+ if (adr->mailbox && adr->host)
+ sprintf (ret = (char *) fs_get (strlen (adr->mailbox) +
+ strlen (adr->host) + 2),"%s@%s",
+ adr->mailbox,adr->host);
+ mail_free_address (&adr); /* don't need temporary address */
+ }
+ }
+ if (ss) *ss = t; /* update return pointer */
+ return ret;
+}
+
+
+/* Thread parse references
+ * Accepts: pointer to purported references
+ * parse multiple references flag
+ * Returns: references or NIL
+ */
+
+STRINGLIST *mail_thread_parse_references (char *s,long flag)
+{
+ char *t;
+ STRINGLIST *ret = NIL;
+ STRINGLIST *cur;
+ /* found first reference? */
+ if (t = mail_thread_parse_msgid (s,&s)) {
+ (ret = mail_newstringlist ())->text.data = (unsigned char *) t;
+ ret->text.size = strlen (t);
+ if (flag) /* parse subsequent references */
+ for (cur = ret; t = mail_thread_parse_msgid (s,&s); cur = cur->next) {
+ (cur->next = mail_newstringlist ())->text.data = (unsigned char *) t;
+ cur->next->text.size = strlen (t);
+ }
+ }
+ return ret;
+}
+
+/* Prune dummy messages
+ * Accepts: candidate container to prune
+ * older sibling of container, if any
+ * Returns: container in this position, possibly pruned
+ * All children and younger siblings are also pruned
+ */
+
+container_t mail_thread_prune_dummy (container_t msg,container_t ane)
+{
+ /* prune container and children */
+ container_t ret = msg ? mail_thread_prune_dummy_work (msg,ane) : NIL;
+ /* prune all younger sisters */
+ if (ret) for (ane = ret; ane && (msg = SIBLING (ane)); ane = msg)
+ msg = mail_thread_prune_dummy_work (msg,ane);
+ return ret;
+}
+
+
+/* Prune dummy messages worker routine
+ * Accepts: candidate container to prune
+ * older sibling of container, if any
+ * Returns: container in this position, possibly pruned
+ * All children are also pruned
+ */
+
+container_t mail_thread_prune_dummy_work (container_t msg,container_t ane)
+{
+ container_t cur;
+ /* get children, if any */
+ container_t nxt = mail_thread_prune_dummy (CHILD (msg),NIL);
+ /* just update children if container has msg */
+ if (CACHE (msg)) SETCHILD (msg,nxt);
+ else if (!nxt) { /* delete dummy with no children */
+ nxt = SIBLING (msg); /* get younger sister */
+ if (ane) SETSIBLING (ane,nxt);
+ /* prune younger sister if exists */
+ msg = nxt ? mail_thread_prune_dummy_work (nxt,ane) : NIL;
+ }
+ /* not if parent root & multiple children */
+ else if ((cur = PARENT (msg)) || !SIBLING (nxt)) {
+ /* OK to promote, try younger sister of aunt */
+ if (ane) SETSIBLING (ane,nxt);
+ /* otherwise promote to child of grandmother */
+ else if (cur) SETCHILD (cur,nxt);
+ SETPARENT (nxt,cur); /* set parent as well */
+ /* look for end of siblings in new container */
+ for (cur = nxt; SIBLING (cur); cur = SIBLING (cur));
+ /* reattach deleted container's siblings */
+ SETSIBLING (cur,SIBLING (msg));
+ /* prune and return new container */
+ msg = mail_thread_prune_dummy_work (nxt,ane);
+ }
+ else SETCHILD (msg,nxt); /* in case child pruned */
+ return msg; /* return this message */
+}
+
+/* Test that purported mother is not a child of purported daughter
+ * Accepts: mother
+ * purported daugher
+ * Returns: T if circular parentage exists, else NIL
+ */
+
+long mail_thread_check_child (container_t mother,container_t daughter)
+{
+ if (mother) { /* only if mother non-NIL */
+ if (mother == daughter) return T;
+ for (daughter = CHILD (daughter); daughter; daughter = SIBLING (daughter))
+ if (mail_thread_check_child (mother,daughter)) return T;
+ }
+ return NIL;
+}
+
+
+/* Generate threadnodes from containers
+ * Accepts: Mail stream
+ * container
+ * flags
+ * Return: threadnode list
+ */
+
+THREADNODE *mail_thread_c2node (MAILSTREAM *stream,container_t con,long flags)
+{
+ THREADNODE *ret,*cur;
+ SORTCACHE *s;
+ container_t nxt;
+ /* for each container */
+ for (ret = cur = NIL; con; con = SIBLING (con)) {
+ s = CACHE (con); /* yes, get its sortcache */
+ /* create node for it */
+ if (ret) cur = cur->branch = mail_newthreadnode (s);
+ else ret = cur = mail_newthreadnode (s);
+ /* attach sequence or UID for non-dummy */
+ if (s) cur->num = (flags & SE_UID) ? mail_uid (stream,s->num) : s->num;
+ /* attach the children */
+ if (nxt = CHILD (con)) cur->next = mail_thread_c2node (stream,nxt,flags);
+ }
+ return ret;
+}
+
+/* Sort thread tree by date
+ * Accepts: thread tree to sort
+ * qsort vector to sort
+ * Returns: sorted thread tree
+ */
+
+THREADNODE *mail_thread_sort (THREADNODE *thr,THREADNODE **tc)
+{
+ unsigned long i,j;
+ THREADNODE *cur;
+ /* sort children of each thread */
+ for (cur = thr; cur; cur = cur->branch)
+ if (cur->next) cur->next = mail_thread_sort (cur->next,tc);
+ /* Must do this in a separate pass since recursive call will clobber tc */
+ /* load threadcache and count nodes to sort */
+ for (i = 0, cur = thr; cur; cur = cur->branch) tc[i++] = cur;
+ if (i > 1) { /* only if need to sort */
+ qsort ((void *) tc,i,sizeof (THREADNODE *),mail_thread_compare_date);
+ /* relink root siblings */
+ for (j = 0, --i; j < i; j++) tc[j]->branch = tc[j+1];
+ tc[j]->branch = NIL; /* end of root */
+ }
+ return i ? tc[0] : NIL; /* return new head of list */
+}
+
+
+/* Thread compare date
+ * Accept: first message sort cache element
+ * second message sort cache element
+ * Returns: -1 if a1 < a2, 1 if a1 > a2
+ *
+ * This assumes that a sort cache element is either a message (with a
+ * sortcache entry) or a dummy with a message (with sortcache entry) child.
+ * This is true of both the ORDEREDSUBJECT (no dummies) and REFERENCES
+ * (dummies only at top-level, and with non-dummy children).
+ *
+ * If a new algorithm allows a dummy parent to have a dummy child, this
+ * routine must be changed if it is to be used by that algorithm.
+ *
+ * Messages with bogus dates are always sorted at the top.
+ */
+
+int mail_thread_compare_date (const void *a1,const void *a2)
+{
+ THREADNODE *t1 = *(THREADNODE **) a1;
+ THREADNODE *t2 = *(THREADNODE **) a2;
+ SORTCACHE *s1 = t1->sc ? t1->sc : t1->next->sc;
+ SORTCACHE *s2 = t2->sc ? t2->sc : t2->next->sc;
+ int ret = compare_ulong (s1->date,s2->date);
+ /* use number as final tie-breaker */
+ return ret ? ret : compare_ulong (s1->num,s2->num);
+}
+
+/* Mail parse sequence
+ * Accepts: mail stream
+ * sequence to parse
+ * Returns: T if parse successful, else NIL
+ */
+
+long mail_sequence (MAILSTREAM *stream,unsigned char *sequence)
+{
+ unsigned long i,j,x;
+ for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->sequence = NIL;
+ while (sequence && *sequence){/* while there is something to parse */
+ if (*sequence == '*') { /* maximum message */
+ if (stream->nmsgs) i = stream->nmsgs;
+ else {
+ MM_LOG ("No messages, so no maximum message number",ERROR);
+ return NIL;
+ }
+ sequence++; /* skip past * */
+ }
+ /* parse and validate message number */
+ else if (!isdigit (*sequence)) {
+ MM_LOG ("Syntax error in sequence",ERROR);
+ return NIL;
+ }
+ else if (!(i = strtoul (sequence,(char **) &sequence,10)) ||
+ (i > stream->nmsgs)) {
+ MM_LOG ("Sequence out of range",ERROR);
+ return NIL;
+ }
+ switch (*sequence) { /* see what the delimiter is */
+ case ':': /* sequence range */
+ if (*++sequence == '*') { /* maximum message */
+ if (stream->nmsgs) j = stream->nmsgs;
+ else {
+ MM_LOG ("No messages, so no maximum message number",ERROR);
+ return NIL;
+ }
+ sequence++; /* skip past * */
+ }
+ /* parse end of range */
+ else if (!(j = strtoul (sequence,(char **) &sequence,10)) ||
+ (j > stream->nmsgs)) {
+ MM_LOG ("Sequence range invalid",ERROR);
+ return NIL;
+ }
+ if (*sequence && *sequence++ != ',') {
+ MM_LOG ("Sequence range syntax error",ERROR);
+ return NIL;
+ }
+ if (i > j) { /* swap the range if backwards */
+ x = i; i = j; j = x;
+ }
+ /* mark each item in the sequence */
+ while (i <= j) mail_elt (stream,j--)->sequence = T;
+ break;
+ case ',': /* single message */
+ ++sequence; /* skip the delimiter, fall into end case */
+ case '\0': /* end of sequence, mark this message */
+ mail_elt (stream,i)->sequence = T;
+ break;
+ default: /* anything else is a syntax error! */
+ MM_LOG ("Sequence syntax error",ERROR);
+ return NIL;
+ }
+ }
+ return T; /* successfully parsed sequence */
+}
+
+/* Parse flag list
+ * Accepts: MAIL stream
+ * flag list as a character string
+ * pointer to user flags to return
+ * Returns: system flags
+ */
+
+long mail_parse_flags (MAILSTREAM *stream,char *flag,unsigned long *uf)
+{
+ char *t,*n,*s,tmp[MAILTMPLEN],msg[MAILTMPLEN];
+ short f = 0;
+ long i,j;
+ *uf = 0; /* initially no user flags */
+ if (flag && *flag) { /* no-op if no flag string */
+ /* check if a list and make sure valid */
+ if (((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) ||
+ (strlen (flag) >= MAILTMPLEN)) {
+ MM_LOG ("Bad flag list",ERROR);
+ return NIL;
+ }
+ /* copy the flag string w/o list construct */
+ strncpy (n = tmp,flag+i,(j = strlen (flag) - (2*i)));
+ tmp[j] = '\0';
+ while ((t = n) && *t) { /* parse the flags */
+ /* find end of flag */
+ if (n = strchr (t,' ')) *n++ = '\0';
+ if (*t == '\\') { /* system flag? */
+ if (!compare_cstring (t+1,"SEEN")) f |= fSEEN;
+ else if (!compare_cstring (t+1,"DELETED")) f |= fDELETED;
+ else if (!compare_cstring (t+1,"FLAGGED")) f |= fFLAGGED;
+ else if (!compare_cstring (t+1,"ANSWERED")) f |= fANSWERED;
+ else if (!compare_cstring (t+1,"DRAFT")) f |= fDRAFT;
+ else {
+ sprintf (msg,"Unsupported system flag: %.80s",t);
+ MM_LOG (msg,WARN);
+ }
+ }
+
+ else { /* keyword flag */
+ for (i = j = 0; /* user flag, search through table */
+ !i && (j < NUSERFLAGS) && (s = stream->user_flags[j]); ++j)
+ if (!compare_cstring (t,s)) *uf |= i = 1 << j;
+ if (!i) { /* flag not found, can it be created? */
+ if (stream->kwd_create && (j < NUSERFLAGS) && *t &&
+ (strlen (t) <= MAXUSERFLAG)) {
+ for (s = t; t && *s; s++) switch (*s) {
+ default: /* all other characters */
+ /* SPACE, CTL, or not CHAR */
+ if ((*s > ' ') && (*s < 0x7f)) break;
+ case '*': case '%': /* list_wildcards */
+ case '"': case '\\':/* quoted-specials */
+ /* atom_specials */
+ case '(': case ')': case '{':
+ case ']': /* resp-specials */
+ sprintf (msg,"Invalid flag: %.80s",t);
+ MM_LOG (msg,WARN);
+ t = NIL;
+ }
+ if (t) { /* only if valid */
+ *uf |= 1 << j; /* set the bit */
+ stream->user_flags[j] = cpystr (t);
+ /* if out of user flags */
+ if (j == NUSERFLAGS - 1) stream->kwd_create = NIL;
+ }
+ }
+ else {
+ if (*t) sprintf (msg,"Unknown flag: %.80s",t);
+ else strcpy (msg,"Empty flag invalid");
+ MM_LOG (msg,WARN);
+ }
+ }
+ }
+ }
+ }
+ return f;
+}
+
+/* Mail check network stream for usability with new name
+ * Accepts: MAIL stream
+ * candidate new name
+ * Returns: T if stream can be used, NIL otherwise
+ */
+
+long mail_usable_network_stream (MAILSTREAM *stream,char *name)
+{
+ NETMBX smb,nmb,omb;
+ return (stream && stream->dtb && !(stream->dtb->flags & DR_LOCAL) &&
+ mail_valid_net_parse (name,&nmb) &&
+ mail_valid_net_parse (stream->mailbox,&smb) &&
+ mail_valid_net_parse (stream->original_mailbox,&omb) &&
+ ((!compare_cstring (smb.host,
+ trustdns ? tcp_canonical (nmb.host) : nmb.host)&&
+ !strcmp (smb.service,nmb.service) &&
+ (!nmb.port || (smb.port == nmb.port)) &&
+ (nmb.anoflag == stream->anonymous) &&
+ (!nmb.user[0] || !strcmp (smb.user,nmb.user))) ||
+ (!compare_cstring (omb.host,nmb.host) &&
+ !strcmp (omb.service,nmb.service) &&
+ (!nmb.port || (omb.port == nmb.port)) &&
+ (nmb.anoflag == stream->anonymous) &&
+ (!nmb.user[0] || !strcmp (omb.user,nmb.user))))) ? LONGT : NIL;
+}
+
+/* Mail data structure instantiation routines */
+
+
+/* Mail instantiate cache elt
+ * Accepts: initial message number
+ * Returns: new cache elt
+ */
+
+MESSAGECACHE *mail_new_cache_elt (unsigned long msgno)
+{
+ MESSAGECACHE *elt = (MESSAGECACHE *) memset (fs_get (sizeof (MESSAGECACHE)),
+ 0,sizeof (MESSAGECACHE));
+ elt->lockcount = 1; /* initially only cache references it */
+ elt->msgno = msgno; /* message number */
+ return elt;
+}
+
+
+/* Mail instantiate envelope
+ * Returns: new envelope
+ */
+
+ENVELOPE *mail_newenvelope (void)
+{
+ return (ENVELOPE *) memset (fs_get (sizeof (ENVELOPE)),0,sizeof (ENVELOPE));
+}
+
+
+/* Mail instantiate address
+ * Returns: new address
+ */
+
+ADDRESS *mail_newaddr (void)
+{
+ return (ADDRESS *) memset (fs_get (sizeof (ADDRESS)),0,sizeof (ADDRESS));
+}
+
+/* Mail instantiate body
+ * Returns: new body
+ */
+
+BODY *mail_newbody (void)
+{
+ return mail_initbody ((BODY *) fs_get (sizeof (BODY)));
+}
+
+
+/* Mail initialize body
+ * Accepts: body
+ * Returns: body
+ */
+
+BODY *mail_initbody (BODY *body)
+{
+ memset ((void *) body,0,sizeof (BODY));
+ body->type = TYPETEXT; /* content type */
+ body->encoding = ENC7BIT; /* content encoding */
+ return body;
+}
+
+
+/* Mail instantiate body parameter
+ * Returns: new body part
+ */
+
+PARAMETER *mail_newbody_parameter (void)
+{
+ return (PARAMETER *) memset (fs_get (sizeof(PARAMETER)),0,sizeof(PARAMETER));
+}
+
+
+/* Mail instantiate body part
+ * Returns: new body part
+ */
+
+PART *mail_newbody_part (void)
+{
+ PART *part = (PART *) memset (fs_get (sizeof (PART)),0,sizeof (PART));
+ mail_initbody (&part->body); /* initialize the body */
+ return part;
+}
+
+
+/* Mail instantiate body message part
+ * Returns: new body message part
+ */
+
+MESSAGE *mail_newmsg (void)
+{
+ return (MESSAGE *) memset (fs_get (sizeof (MESSAGE)),0,sizeof (MESSAGE));
+}
+
+/* Mail instantiate string list
+ * Returns: new string list
+ */
+
+STRINGLIST *mail_newstringlist (void)
+{
+ return (STRINGLIST *) memset (fs_get (sizeof (STRINGLIST)),0,
+ sizeof (STRINGLIST));
+}
+
+
+/* Mail instantiate new search program
+ * Returns: new search program
+ */
+
+SEARCHPGM *mail_newsearchpgm (void)
+{
+ return (SEARCHPGM *) memset (fs_get (sizeof(SEARCHPGM)),0,sizeof(SEARCHPGM));
+}
+
+
+/* Mail instantiate new search program
+ * Accepts: header line name
+ * Returns: new search program
+ */
+
+SEARCHHEADER *mail_newsearchheader (char *line,char *text)
+{
+ SEARCHHEADER *hdr = (SEARCHHEADER *) memset (fs_get (sizeof (SEARCHHEADER)),
+ 0,sizeof (SEARCHHEADER));
+ hdr->line.size = strlen ((char *) (hdr->line.data =
+ (unsigned char *) cpystr (line)));
+ hdr->text.size = strlen ((char *) (hdr->text.data =
+ (unsigned char *) cpystr (text)));
+ return hdr;
+}
+
+
+/* Mail instantiate new search set
+ * Returns: new search set
+ */
+
+SEARCHSET *mail_newsearchset (void)
+{
+ return (SEARCHSET *) memset (fs_get (sizeof(SEARCHSET)),0,sizeof(SEARCHSET));
+}
+
+
+/* Mail instantiate new search or
+ * Returns: new search or
+ */
+
+SEARCHOR *mail_newsearchor (void)
+{
+ SEARCHOR *or = (SEARCHOR *) memset (fs_get (sizeof (SEARCHOR)),0,
+ sizeof (SEARCHOR));
+ or->first = mail_newsearchpgm ();
+ or->second = mail_newsearchpgm ();
+ return or;
+}
+
+/* Mail instantiate new searchpgmlist
+ * Returns: new searchpgmlist
+ */
+
+SEARCHPGMLIST *mail_newsearchpgmlist (void)
+{
+ SEARCHPGMLIST *pgl = (SEARCHPGMLIST *)
+ memset (fs_get (sizeof (SEARCHPGMLIST)),0,sizeof (SEARCHPGMLIST));
+ pgl->pgm = mail_newsearchpgm ();
+ return pgl;
+}
+
+
+/* Mail instantiate new sortpgm
+ * Returns: new sortpgm
+ */
+
+SORTPGM *mail_newsortpgm (void)
+{
+ return (SORTPGM *) memset (fs_get (sizeof (SORTPGM)),0,sizeof (SORTPGM));
+}
+
+
+/* Mail instantiate new threadnode
+ * Accepts: sort cache for thread node
+ * Returns: new threadnode
+ */
+
+THREADNODE *mail_newthreadnode (SORTCACHE *sc)
+{
+ THREADNODE *thr = (THREADNODE *) memset (fs_get (sizeof (THREADNODE)),0,
+ sizeof (THREADNODE));
+ if (sc) thr->sc = sc; /* initialize sortcache */
+ return thr;
+}
+
+
+/* Mail instantiate new acllist
+ * Returns: new acllist
+ */
+
+ACLLIST *mail_newacllist (void)
+{
+ return (ACLLIST *) memset (fs_get (sizeof (ACLLIST)),0,sizeof (ACLLIST));
+}
+
+
+/* Mail instantiate new quotalist
+ * Returns: new quotalist
+ */
+
+QUOTALIST *mail_newquotalist (void)
+{
+ return (QUOTALIST *) memset (fs_get (sizeof (QUOTALIST)),0,
+ sizeof (QUOTALIST));
+}
+
+/* Mail garbage collection routines */
+
+
+/* Mail garbage collect body
+ * Accepts: pointer to body pointer
+ */
+
+void mail_free_body (BODY **body)
+{
+ if (*body) { /* only free if exists */
+ mail_free_body_data (*body);/* free its data */
+ fs_give ((void **) body); /* return body to free storage */
+ }
+}
+
+
+/* Mail garbage collect body data
+ * Accepts: body pointer
+ */
+
+void mail_free_body_data (BODY *body)
+{
+ switch (body->type) { /* free contents */
+ case TYPEMULTIPART: /* multiple part */
+ mail_free_body_part (&body->nested.part);
+ break;
+ case TYPEMESSAGE: /* encapsulated message */
+ if (body->subtype && !strcmp (body->subtype,"RFC822")) {
+ mail_free_stringlist (&body->nested.msg->lines);
+ mail_gc_msg (body->nested.msg,GC_ENV | GC_TEXTS);
+ }
+ if (body->nested.msg) fs_give ((void **) &body->nested.msg);
+ break;
+ default:
+ break;
+ }
+ if (body->subtype) fs_give ((void **) &body->subtype);
+ mail_free_body_parameter (&body->parameter);
+ if (body->id) fs_give ((void **) &body->id);
+ if (body->description) fs_give ((void **) &body->description);
+ if (body->disposition.type) fs_give ((void **) &body->disposition.type);
+ if (body->disposition.parameter)
+ mail_free_body_parameter (&body->disposition.parameter);
+ if (body->language) mail_free_stringlist (&body->language);
+ if (body->location) fs_give ((void **) &body->location);
+ if (body->mime.text.data) fs_give ((void **) &body->mime.text.data);
+ if (body->contents.text.data) fs_give ((void **) &body->contents.text.data);
+ if (body->md5) fs_give ((void **) &body->md5);
+ if (mailfreebodysparep && body->sparep)
+ (*mailfreebodysparep) (&body->sparep);
+}
+
+/* Mail garbage collect body parameter
+ * Accepts: pointer to body parameter pointer
+ */
+
+void mail_free_body_parameter (PARAMETER **parameter)
+{
+ if (*parameter) { /* only free if exists */
+ if ((*parameter)->attribute) fs_give ((void **) &(*parameter)->attribute);
+ if ((*parameter)->value) fs_give ((void **) &(*parameter)->value);
+ /* run down the list as necessary */
+ mail_free_body_parameter (&(*parameter)->next);
+ /* return body part to free storage */
+ fs_give ((void **) parameter);
+ }
+}
+
+
+/* Mail garbage collect body part
+ * Accepts: pointer to body part pointer
+ */
+
+void mail_free_body_part (PART **part)
+{
+ if (*part) { /* only free if exists */
+ mail_free_body_data (&(*part)->body);
+ /* run down the list as necessary */
+ mail_free_body_part (&(*part)->next);
+ fs_give ((void **) part); /* return body part to free storage */
+ }
+}
+
+/* Mail garbage collect message cache
+ * Accepts: mail stream
+ *
+ * The message cache is set to NIL when this function finishes.
+ */
+
+void mail_free_cache (MAILSTREAM *stream)
+{
+ /* do driver specific stuff first */
+ mail_gc (stream,GC_ELT | GC_ENV | GC_TEXTS);
+ /* flush the cache */
+ (*mailcache) (stream,(long) 0,CH_INIT);
+}
+
+
+/* Mail garbage collect cache element
+ * Accepts: pointer to cache element pointer
+ */
+
+void mail_free_elt (MESSAGECACHE **elt)
+{
+ /* only free if exists and no sharers */
+ if (*elt && !--(*elt)->lockcount) {
+ mail_gc_msg (&(*elt)->private.msg,GC_ENV | GC_TEXTS);
+ if (mailfreeeltsparep && (*elt)->sparep)
+ (*mailfreeeltsparep) (&(*elt)->sparep);
+ fs_give ((void **) elt);
+ }
+ else *elt = NIL; /* else simply drop pointer */
+}
+
+/* Mail garbage collect envelope
+ * Accepts: pointer to envelope pointer
+ */
+
+void mail_free_envelope (ENVELOPE **env)
+{
+ if (*env) { /* only free if exists */
+ if ((*env)->remail) fs_give ((void **) &(*env)->remail);
+ mail_free_address (&(*env)->return_path);
+ if ((*env)->date) fs_give ((void **) &(*env)->date);
+ mail_free_address (&(*env)->from);
+ mail_free_address (&(*env)->sender);
+ mail_free_address (&(*env)->reply_to);
+ if ((*env)->subject) fs_give ((void **) &(*env)->subject);
+ mail_free_address (&(*env)->to);
+ mail_free_address (&(*env)->cc);
+ mail_free_address (&(*env)->bcc);
+ if ((*env)->in_reply_to) fs_give ((void **) &(*env)->in_reply_to);
+ if ((*env)->message_id) fs_give ((void **) &(*env)->message_id);
+ if ((*env)->newsgroups) fs_give ((void **) &(*env)->newsgroups);
+ if ((*env)->followup_to) fs_give ((void **) &(*env)->followup_to);
+ if ((*env)->references) fs_give ((void **) &(*env)->references);
+ if (mailfreeenvelopesparep && (*env)->sparep)
+ (*mailfreeenvelopesparep) (&(*env)->sparep);
+ fs_give ((void **) env); /* return envelope to free storage */
+ }
+}
+
+
+/* Mail garbage collect address
+ * Accepts: pointer to address pointer
+ */
+
+void mail_free_address (ADDRESS **address)
+{
+ if (*address) { /* only free if exists */
+ if ((*address)->personal) fs_give ((void **) &(*address)->personal);
+ if ((*address)->adl) fs_give ((void **) &(*address)->adl);
+ if ((*address)->mailbox) fs_give ((void **) &(*address)->mailbox);
+ if ((*address)->host) fs_give ((void **) &(*address)->host);
+ if ((*address)->error) fs_give ((void **) &(*address)->error);
+ if ((*address)->orcpt.type) fs_give ((void **) &(*address)->orcpt.type);
+ if ((*address)->orcpt.addr) fs_give ((void **) &(*address)->orcpt.addr);
+ mail_free_address (&(*address)->next);
+ fs_give ((void **) address);/* return address to free storage */
+ }
+}
+
+
+/* Mail garbage collect stringlist
+ * Accepts: pointer to stringlist pointer
+ */
+
+void mail_free_stringlist (STRINGLIST **string)
+{
+ if (*string) { /* only free if exists */
+ if ((*string)->text.data) fs_give ((void **) &(*string)->text.data);
+ mail_free_stringlist (&(*string)->next);
+ fs_give ((void **) string); /* return string to free storage */
+ }
+}
+
+/* Mail garbage collect searchpgm
+ * Accepts: pointer to searchpgm pointer
+ */
+
+void mail_free_searchpgm (SEARCHPGM **pgm)
+{
+ if (*pgm) { /* only free if exists */
+ mail_free_searchset (&(*pgm)->msgno);
+ mail_free_searchset (&(*pgm)->uid);
+ mail_free_searchor (&(*pgm)->or);
+ mail_free_searchpgmlist (&(*pgm)->not);
+ mail_free_searchheader (&(*pgm)->header);
+ mail_free_stringlist (&(*pgm)->bcc);
+ mail_free_stringlist (&(*pgm)->body);
+ mail_free_stringlist (&(*pgm)->cc);
+ mail_free_stringlist (&(*pgm)->from);
+ mail_free_stringlist (&(*pgm)->keyword);
+ mail_free_stringlist (&(*pgm)->subject);
+ mail_free_stringlist (&(*pgm)->text);
+ mail_free_stringlist (&(*pgm)->to);
+ fs_give ((void **) pgm); /* return program to free storage */
+ }
+}
+
+
+/* Mail garbage collect searchheader
+ * Accepts: pointer to searchheader pointer
+ */
+
+void mail_free_searchheader (SEARCHHEADER **hdr)
+{
+ if (*hdr) { /* only free if exists */
+ if ((*hdr)->line.data) fs_give ((void **) &(*hdr)->line.data);
+ if ((*hdr)->text.data) fs_give ((void **) &(*hdr)->text.data);
+ mail_free_searchheader (&(*hdr)->next);
+ fs_give ((void **) hdr); /* return header to free storage */
+ }
+}
+
+
+/* Mail garbage collect searchset
+ * Accepts: pointer to searchset pointer
+ */
+
+void mail_free_searchset (SEARCHSET **set)
+{
+ if (*set) { /* only free if exists */
+ mail_free_searchset (&(*set)->next);
+ fs_give ((void **) set); /* return set to free storage */
+ }
+}
+
+/* Mail garbage collect searchor
+ * Accepts: pointer to searchor pointer
+ */
+
+void mail_free_searchor (SEARCHOR **orl)
+{
+ if (*orl) { /* only free if exists */
+ mail_free_searchpgm (&(*orl)->first);
+ mail_free_searchpgm (&(*orl)->second);
+ mail_free_searchor (&(*orl)->next);
+ fs_give ((void **) orl); /* return searchor to free storage */
+ }
+}
+
+
+/* Mail garbage collect search program list
+ * Accepts: pointer to searchpgmlist pointer
+ */
+
+void mail_free_searchpgmlist (SEARCHPGMLIST **pgl)
+{
+ if (*pgl) { /* only free if exists */
+ mail_free_searchpgm (&(*pgl)->pgm);
+ mail_free_searchpgmlist (&(*pgl)->next);
+ fs_give ((void **) pgl); /* return searchpgmlist to free storage */
+ }
+}
+
+
+/* Mail garbage collect namespace
+ * Accepts: poiner to namespace
+ */
+
+void mail_free_namespace (NAMESPACE **n)
+{
+ if (*n) {
+ fs_give ((void **) &(*n)->name);
+ mail_free_namespace (&(*n)->next);
+ mail_free_body_parameter (&(*n)->param);
+ fs_give ((void **) n); /* return namespace to free storage */
+ }
+}
+
+/* Mail garbage collect sort program
+ * Accepts: pointer to sortpgm pointer
+ */
+
+void mail_free_sortpgm (SORTPGM **pgm)
+{
+ if (*pgm) { /* only free if exists */
+ mail_free_sortpgm (&(*pgm)->next);
+ fs_give ((void **) pgm); /* return sortpgm to free storage */
+ }
+}
+
+
+/* Mail garbage collect thread node
+ * Accepts: pointer to threadnode pointer
+ */
+
+void mail_free_threadnode (THREADNODE **thr)
+{
+ if (*thr) { /* only free if exists */
+ mail_free_threadnode (&(*thr)->branch);
+ mail_free_threadnode (&(*thr)->next);
+ fs_give ((void **) thr); /* return threadnode to free storage */
+ }
+}
+
+
+/* Mail garbage collect acllist
+ * Accepts: pointer to acllist pointer
+ */
+
+void mail_free_acllist (ACLLIST **al)
+{
+ if (*al) { /* only free if exists */
+ if ((*al)->identifier) fs_give ((void **) &(*al)->identifier);
+ if ((*al)->rights) fs_give ((void **) &(*al)->rights);
+ mail_free_acllist (&(*al)->next);
+ fs_give ((void **) al); /* return acllist to free storage */
+ }
+}
+
+
+/* Mail garbage collect quotalist
+ * Accepts: pointer to quotalist pointer
+ */
+
+void mail_free_quotalist (QUOTALIST **ql)
+{
+ if (*ql) { /* only free if exists */
+ if ((*ql)->name) fs_give ((void **) &(*ql)->name);
+ mail_free_quotalist (&(*ql)->next);
+ fs_give ((void **) ql); /* return quotalist to free storage */
+ }
+}
+
+/* Link authenicator
+ * Accepts: authenticator to add to list
+ */
+
+void auth_link (AUTHENTICATOR *auth)
+{
+ if (!auth->valid || (*auth->valid) ()) {
+ AUTHENTICATOR **a = &mailauthenticators;
+ while (*a) a = &(*a)->next; /* find end of list of authenticators */
+ *a = auth; /* put authenticator at the end */
+ auth->next = NIL; /* this authenticator is the end of the list */
+ }
+}
+
+
+/* Authenticate access
+ * Accepts: mechanism name
+ * responder function
+ * argument count
+ * argument vector
+ * Returns: authenticated user name or NIL
+ */
+
+char *mail_auth (char *mechanism,authresponse_t resp,int argc,char *argv[])
+{
+ AUTHENTICATOR *auth;
+ for (auth = mailauthenticators; auth; auth = auth->next)
+ if (auth->server && !compare_cstring (auth->name,mechanism))
+ return (!(auth->flags & AU_DISABLE) &&
+ ((auth->flags & AU_SECURE) ||
+ !mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL))) ?
+ (*auth->server) (resp,argc,argv) : NIL;
+ return NIL; /* no authenticator found */
+}
+
+/* Lookup authenticator index
+ * Accepts: authenticator index
+ * Returns: authenticator, or 0 if not found
+ */
+
+AUTHENTICATOR *mail_lookup_auth (unsigned long i)
+{
+ AUTHENTICATOR *auth = mailauthenticators;
+ while (auth && --i) auth = auth->next;
+ return auth;
+}
+
+
+/* Lookup authenticator name
+ * Accepts: authenticator name
+ * required authenticator flags
+ * Returns: index in authenticator chain, or 0 if not found
+ */
+
+unsigned int mail_lookup_auth_name (char *mechanism,long flags)
+{
+ int i;
+ AUTHENTICATOR *auth;
+ for (i = 1, auth = mailauthenticators; auth; i++, auth = auth->next)
+ if (auth->client && !(flags & ~auth->flags) &&
+ !(auth->flags & AU_DISABLE) && !compare_cstring (auth->name,mechanism))
+ return i;
+ return 0;
+}
+
+/* Standard TCP/IP network driver */
+
+static NETDRIVER tcpdriver = {
+ tcp_open, /* open connection */
+ tcp_aopen, /* open preauthenticated connection */
+ tcp_getline, /* get a line */
+ tcp_getbuffer, /* get a buffer */
+ tcp_soutr, /* output pushed data */
+ tcp_sout, /* output string */
+ tcp_close, /* close connection */
+ tcp_host, /* return host name */
+ tcp_remotehost, /* return remote host name */
+ tcp_port, /* return port number */
+ tcp_localhost /* return local host name */
+};
+
+
+/* Network open
+ * Accepts: NETMBX specifier to open
+ * default network driver
+ * default port
+ * SSL driver
+ * SSL service name
+ * SSL driver port
+ * Returns: Network stream if success, else NIL
+ */
+
+NETSTREAM *net_open (NETMBX *mb,NETDRIVER *dv,unsigned long port,
+ NETDRIVER *ssld,char *ssls,unsigned long sslp)
+{
+ NETSTREAM *stream = NIL;
+ char tmp[MAILTMPLEN];
+ unsigned long flags = mb->novalidate ? NET_NOVALIDATECERT : 0;
+ if (strlen (mb->host) >= NETMAXHOST) {
+ sprintf (tmp,"Invalid host name: %.80s",mb->host);
+ MM_LOG (tmp,ERROR);
+ }
+ /* use designated driver if given */
+ else if (dv) stream = net_open_work (dv,mb->host,mb->service,port,mb->port,
+ flags);
+ else if (mb->sslflag && ssld) /* use ssl if sslflag lit */
+ stream = net_open_work (ssld,mb->host,ssls,sslp,mb->port,flags);
+ /* if trysslfirst and can open ssl... */
+ else if ((mb->trysslflag || trysslfirst) && ssld &&
+ (stream = net_open_work (ssld,mb->host,ssls,sslp,mb->port,
+ flags | NET_SILENT | NET_TRYSSL))) {
+ if (net_sout (stream,"",0)) mb->sslflag = T;
+ else {
+ net_close (stream); /* flush fake SSL stream */
+ stream = NIL;
+ }
+ }
+ /* default to TCP driver */
+ else stream = net_open_work (&tcpdriver,mb->host,mb->service,port,mb->port,
+ flags);
+ return stream;
+}
+
+/* Network open worker routine
+ * Accepts: network driver
+ * host name
+ * service name to look up port
+ * port number if service name not found
+ * port number to override service name
+ * flags (passed on top of port)
+ * Returns: Network stream if success, else NIL
+ */
+
+NETSTREAM *net_open_work (NETDRIVER *dv,char *host,char *service,
+ unsigned long port,unsigned long portoverride,
+ unsigned long flags)
+{
+ NETSTREAM *stream = NIL;
+ void *tstream;
+ if (service && (*service == '*')) {
+ flags |= NET_NOOPENTIMEOUT; /* mark that no timeout is desired */
+ ++service; /* no longer need the no timeout indicator */
+ }
+ if (portoverride) { /* explicit port number? */
+ service = NIL; /* yes, override service name */
+ port = portoverride; /* use that instead of default port */
+ }
+ if (tstream = (*dv->open) (host,service,port | flags)) {
+ stream = (NETSTREAM *) fs_get (sizeof (NETSTREAM));
+ stream->stream = tstream;
+ stream->dtb = dv;
+ }
+ return stream;
+}
+
+
+/* Network authenticated open
+ * Accepts: network driver
+ * NETMBX specifier
+ * service specifier
+ * return user name buffer
+ * Returns: Network stream if success else NIL
+ */
+
+NETSTREAM *net_aopen (NETDRIVER *dv,NETMBX *mb,char *service,char *user)
+{
+ NETSTREAM *stream = NIL;
+ void *tstream;
+ if (!dv) dv = &tcpdriver; /* default to TCP driver */
+ if (tstream = (*dv->aopen) (mb,service,user)) {
+ stream = (NETSTREAM *) fs_get (sizeof (NETSTREAM));
+ stream->stream = tstream;
+ stream->dtb = dv;
+ }
+ return stream;
+}
+
+/* Network receive line
+ * Accepts: Network stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *net_getline (NETSTREAM *stream)
+{
+ return (*stream->dtb->getline) (stream->stream);
+}
+
+
+/* Network receive buffer
+ * Accepts: Network stream (must be void * for use as readfn_t)
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long net_getbuffer (void *st,unsigned long size,char *buffer)
+{
+ NETSTREAM *stream = (NETSTREAM *) st;
+ return (*stream->dtb->getbuffer) (stream->stream,size,buffer);
+}
+
+
+/* Network send null-terminated string
+ * Accepts: Network stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long net_soutr (NETSTREAM *stream,char *string)
+{
+ return (*stream->dtb->soutr) (stream->stream,string);
+}
+
+
+/* Network send string
+ * Accepts: Network stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long net_sout (NETSTREAM *stream,char *string,unsigned long size)
+{
+ return (*stream->dtb->sout) (stream->stream,string,size);
+}
+
+/* Network close
+ * Accepts: Network stream
+ */
+
+void net_close (NETSTREAM *stream)
+{
+ if (stream->stream) (*stream->dtb->close) (stream->stream);
+ fs_give ((void **) &stream);
+}
+
+
+/* Network get host name
+ * Accepts: Network stream
+ * Returns: host name for this stream
+ */
+
+char *net_host (NETSTREAM *stream)
+{
+ return (*stream->dtb->host) (stream->stream);
+}
+
+
+/* Network get remote host name
+ * Accepts: Network stream
+ * Returns: host name for this stream
+ */
+
+char *net_remotehost (NETSTREAM *stream)
+{
+ return (*stream->dtb->remotehost) (stream->stream);
+}
+
+/* Network return port for this stream
+ * Accepts: Network stream
+ * Returns: port number for this stream
+ */
+
+unsigned long net_port (NETSTREAM *stream)
+{
+ return (*stream->dtb->port) (stream->stream);
+}
+
+
+/* Network get local host name
+ * Accepts: Network stream
+ * Returns: local host name
+ */
+
+char *net_localhost (NETSTREAM *stream)
+{
+ return (*stream->dtb->localhost) (stream->stream);
+}
diff --git a/imap/src/c-client/mail.h b/imap/src/c-client/mail.h
new file mode 100644
index 00000000..174993e1
--- /dev/null
+++ b/imap/src/c-client/mail.h
@@ -0,0 +1,1838 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Mailbox Access routines
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@Washington.EDU
+ *
+ * Date: 22 November 1989
+ * Last Edited: 16 December 2008
+ */
+
+/* The Version */
+
+#define CCLIENTVERSION "2007e"
+
+/* Build parameters */
+
+#define CACHEINCREMENT 250 /* cache growth increments */
+#define MAILTMPLEN 1024 /* size of a temporary buffer */
+#define SENDBUFLEN 16385 /* size of temporary sending buffer, also
+ * used for SMTP commands and NETMBX generation
+ * buffer so shouldn't be made smaller than
+ * MAILTMPLEN. Note that there's a guard byte,
+ * so this is actually len+1. */
+#define MAXAUTHENTICATORS 8 /* maximum number of SASL authenticators */
+ /* maximum number of messages */
+#define MAXMESSAGES (unsigned long) 1000000
+#define MAXLOGINTRIALS 3 /* maximum number of client login attempts */
+#define MAXWILDCARDS 10 /* maximum wildcards allowed in LIST/LSUB */
+
+
+/* These can't be changed without changing code */
+
+#define NUSERFLAGS 30 /* maximum number of user flags */
+#define MAXUSERFLAG 50 /* maximum length of a user flag */
+#define BASEYEAR 1970 /* the year time began on Unix DON'T CHANGE */
+ /* default for unqualified addresses */
+#define BADHOST ".MISSING-HOST-NAME."
+ /* default for syntax errors in addresses */
+#define ERRHOST ".SYNTAX-ERROR."
+
+
+/* Coddle certain compilers' 6-character symbol limitation */
+
+#ifdef __COMPILER_KCC__
+#include "shortsym.h"
+#endif
+
+
+/* Function status code */
+
+#define NIL 0 /* convenient name */
+#define T 1 /* opposite of NIL */
+#define LONGT (long) 1 /* long T to pacify some compilers */
+#define VOIDT (void *) "" /* void T ditto */
+
+/* Global and Driver Parameters */
+
+ /* 0xx: driver and authenticator flags */
+#define ENABLE_DRIVER (long) 1
+#define DISABLE_DRIVER (long) 2
+#define ENABLE_AUTHENTICATOR (long) 3
+#define DISABLE_AUTHENTICATOR (long) 4
+#define ENABLE_DEBUG (long) 5
+#define DISABLE_DEBUG (long) 6
+#define HIDE_AUTHENTICATOR (long) 7
+#define UNHIDE_AUTHENTICATOR (long) 8
+ /* 1xx: c-client globals */
+#define GET_DRIVERS (long) 101
+#define SET_DRIVERS (long) 102
+#define GET_GETS (long) 103
+#define SET_GETS (long) 104
+#define GET_CACHE (long) 105
+#define SET_CACHE (long) 106
+#define GET_SMTPVERBOSE (long) 107
+#define SET_SMTPVERBOSE (long) 108
+#define GET_RFC822OUTPUT (long) 109
+#define SET_RFC822OUTPUT (long) 110
+#define GET_READPROGRESS (long) 111
+#define SET_READPROGRESS (long) 112
+#define GET_THREADERS (long) 113
+#define SET_THREADERS (long) 114
+#define GET_NAMESPACE (long) 115
+#define SET_NAMESPACE (long) 116
+#define GET_MAILPROXYCOPY (long) 117
+#define SET_MAILPROXYCOPY (long) 118
+#define GET_SERVICENAME (long) 119
+#define SET_SERVICENAME (long) 120
+#define GET_DRIVER (long) 121
+#define SET_DRIVER (long) 122
+#define GET_EXPUNGEATPING (long) 123
+#define SET_EXPUNGEATPING (long) 124
+#define GET_PARSEPHRASE (long) 125
+#define SET_PARSEPHRASE (long) 126
+#define GET_SSLDRIVER (long) 127
+#define SET_SSLDRIVER (long) 128
+#define GET_TRYSSLFIRST (long) 129
+#define SET_TRYSSLFIRST (long) 130
+#define GET_BLOCKNOTIFY (long) 131
+#define SET_BLOCKNOTIFY (long) 132
+#define GET_SORTRESULTS (long) 133
+#define SET_SORTRESULTS (long) 134
+#define GET_THREADRESULTS (long) 135
+#define SET_THREADRESULTS (long) 136
+#define GET_PARSELINE (long) 137
+#define SET_PARSELINE (long) 138
+#define GET_NEWSRCQUERY (long) 139
+#define SET_NEWSRCQUERY (long) 140
+#define GET_FREEENVELOPESPAREP (long) 141
+#define SET_FREEENVELOPESPAREP (long) 142
+#define GET_FREEELTSPAREP (long) 143
+#define SET_FREEELTSPAREP (long) 144
+#define GET_SSLSTART (long) 145
+#define SET_SSLSTART (long) 146
+#define GET_DEBUGSENSITIVE (long) 147
+#define SET_DEBUGSENSITIVE (long) 148
+#define GET_TCPDEBUG (long) 149
+#define SET_TCPDEBUG (long) 150
+#define GET_FREESTREAMSPAREP (long) 151
+#define SET_FREESTREAMSPAREP (long) 152
+#define GET_FREEBODYSPAREP (long) 153
+#define SET_FREEBODYSPAREP (long) 154
+#define GET_COPYUID (long) 155
+#define SET_COPYUID (long) 156
+#define GET_APPENDUID (long) 157
+#define SET_APPENDUID (long) 158
+#define GET_RFC822OUTPUTFULL (long) 159
+#define SET_RFC822OUTPUTFULL (long) 160
+#define GET_BLOCKENVINIT (long) 161
+#define SET_BLOCKENVINIT (long) 162
+
+ /* 2xx: environment */
+#define GET_USERNAME (long) 201
+#define SET_USERNAME (long) 202
+#define GET_HOMEDIR (long) 203
+#define SET_HOMEDIR (long) 204
+#define GET_LOCALHOST (long) 205
+#define SET_LOCALHOST (long) 206
+#define GET_SYSINBOX (long) 207
+#define SET_SYSINBOX (long) 208
+#define GET_USERPROMPT (long) 209
+#define SET_USERPROMPT (long) 210
+#define GET_DISABLEPLAINTEXT (long) 211
+#define SET_DISABLEPLAINTEXT (long) 212
+#define GET_CHROOTSERVER (long) 213
+#define SET_CHROOTSERVER (long) 214
+#define GET_ADVERTISETHEWORLD (long) 215
+#define SET_ADVERTISETHEWORLD (long) 216
+#define GET_DISABLEAUTOSHAREDNS (long) 217
+#define SET_DISABLEAUTOSHAREDNS (long) 218
+#define GET_MAILSUBDIR 219
+#define SET_MAILSUBDIR 220
+#define GET_DISABLE822TZTEXT 221
+#define SET_DISABLE822TZTEXT 222
+#define GET_LIMITEDADVERTISE (long) 223
+#define SET_LIMITEDADVERTISE (long) 224
+#define GET_LOGOUTHOOK (long) 225
+#define SET_LOGOUTHOOK (long) 226
+#define GET_LOGOUTDATA (long) 227
+#define SET_LOGOUTDATA (long) 228
+#define GET_EXTERNALAUTHID (long) 229
+#define SET_EXTERNALAUTHID (long) 230
+#define GET_SSLCAPATH (long) 231
+#define SET_SSLCAPATH (long) 232
+
+ /* 3xx: TCP/IP */
+#define GET_OPENTIMEOUT (long) 300
+#define SET_OPENTIMEOUT (long) 301
+#define GET_READTIMEOUT (long) 302
+#define SET_READTIMEOUT (long) 303
+#define GET_WRITETIMEOUT (long) 304
+#define SET_WRITETIMEOUT (long) 305
+#define GET_CLOSETIMEOUT (long) 306
+#define SET_CLOSETIMEOUT (long) 307
+#define GET_TIMEOUT (long) 308
+#define SET_TIMEOUT (long) 309
+#define GET_RSHTIMEOUT (long) 310
+#define SET_RSHTIMEOUT (long) 311
+#define GET_ALLOWREVERSEDNS (long) 312
+#define SET_ALLOWREVERSEDNS (long) 313
+#define GET_RSHCOMMAND (long) 314
+#define SET_RSHCOMMAND (long) 315
+#define GET_RSHPATH (long) 316
+#define SET_RSHPATH (long) 317
+#define GET_SSHTIMEOUT (long) 318
+#define SET_SSHTIMEOUT (long) 319
+#define GET_SSHCOMMAND (long) 320
+#define SET_SSHCOMMAND (long) 321
+#define GET_SSHPATH (long) 322
+#define SET_SSHPATH (long) 323
+#define GET_SSLCERTIFICATEQUERY (long) 324
+#define SET_SSLCERTIFICATEQUERY (long) 325
+#define GET_SSLFAILURE (long) 326
+#define SET_SSLFAILURE (long) 327
+#define GET_NEWSRCCANONHOST (long) 328
+#define SET_NEWSRCCANONHOST (long) 329
+#define GET_KINIT (long) 330
+#define SET_KINIT (long) 331
+#define GET_SSLCLIENTCERT (long) 332
+#define SET_SSLCLIENTCERT (long) 333
+#define GET_SSLCLIENTKEY (long) 334
+#define SET_SSLCLIENTKEY (long) 335
+#define GET_KERBEROS_CP_SVR_NAME (long) 336
+#define SET_KERBEROS_CP_SVR_NAME (long) 337
+
+ /* 4xx: network drivers */
+#define GET_MAXLOGINTRIALS (long) 400
+#define SET_MAXLOGINTRIALS (long) 401
+#define GET_LOOKAHEAD (long) 402
+#define SET_LOOKAHEAD (long) 403
+#define GET_IMAPPORT (long) 404
+#define SET_IMAPPORT (long) 405
+#define GET_PREFETCH (long) 406
+#define SET_PREFETCH (long) 407
+#define GET_CLOSEONERROR (long) 408
+#define SET_CLOSEONERROR (long) 409
+#define GET_POP3PORT (long) 410
+#define SET_POP3PORT (long) 411
+#define GET_UIDLOOKAHEAD (long) 412
+#define SET_UIDLOOKAHEAD (long) 413
+#define GET_NNTPPORT (long) 414
+#define SET_NNTPPORT (long) 415
+#define GET_IMAPENVELOPE (long) 416
+#define SET_IMAPENVELOPE (long) 417
+#define GET_IMAPREFERRAL (long) 418
+#define SET_IMAPREFERRAL (long) 419
+#define GET_SSLIMAPPORT (long) 420
+#define SET_SSLIMAPPORT (long) 421
+#define GET_SSLPOPPORT (long) 422
+#define SET_SSLPOPPORT (long) 423
+#define GET_SSLNNTPPORT (long) 424
+#define SET_SSLNNTPPORT (long) 425
+#define GET_SSLSMTPPORT (long) 426
+#define SET_SSLSMTPPORT (long) 427
+#define GET_SMTPPORT (long) 428
+#define SET_SMTPPORT (long) 429
+#define GET_IMAPEXTRAHEADERS (long) 430
+#define SET_IMAPEXTRAHEADERS (long) 431
+#define GET_ACL (long) 432
+#define SET_ACL (long) 433
+#define GET_LISTRIGHTS (long) 434
+#define SET_LISTRIGHTS (long) 435
+#define GET_MYRIGHTS (long) 436
+#define SET_MYRIGHTS (long) 437
+#define GET_QUOTA (long) 438
+#define SET_QUOTA (long) 439
+#define GET_QUOTAROOT (long) 440
+#define SET_QUOTAROOT (long) 441
+#define GET_IMAPTRYSSL (long) 442
+#define SET_IMAPTRYSSL (long) 443
+#define GET_FETCHLOOKAHEAD (long) 444
+#define SET_FETCHLOOKAHEAD (long) 445
+#define GET_NNTPRANGE (long) 446
+#define SET_NNTPRANGE (long) 447
+#define GET_NNTPHIDEPATH (long) 448
+#define SET_NNTPHIDEPATH (long) 449
+#define GET_SENDCOMMAND (long) 450
+#define SET_SENDCOMMAND (long) 451
+#define GET_IDLETIMEOUT (long) 452
+#define SET_IDLETIMEOUT (long) 453
+#define GET_FETCHLOOKAHEADLIMIT (long) 454
+#define SET_FETCHLOOKAHEADLIMIT (long) 455
+
+ /* 5xx: local file drivers */
+#define GET_MBXPROTECTION (long) 500
+#define SET_MBXPROTECTION (long) 501
+#define GET_DIRPROTECTION (long) 502
+#define SET_DIRPROTECTION (long) 503
+#define GET_LOCKPROTECTION (long) 504
+#define SET_LOCKPROTECTION (long) 505
+#define GET_FROMWIDGET (long) 506
+#define SET_FROMWIDGET (long) 507
+#define GET_NEWSACTIVE (long) 508
+#define SET_NEWSACTIVE (long) 509
+#define GET_NEWSSPOOL (long) 510
+#define SET_NEWSSPOOL (long) 511
+#define GET_NEWSRC (long) 512
+#define SET_NEWSRC (long) 513
+#define GET_EXTENSION (long) 514
+#define SET_EXTENSION (long) 515
+#define GET_DISABLEFCNTLLOCK (long) 516
+#define SET_DISABLEFCNTLLOCK (long) 517
+#define GET_LOCKEACCESERROR (long) 518
+#define SET_LOCKEACCESERROR (long) 519
+#define GET_LISTMAXLEVEL (long) 520
+#define SET_LISTMAXLEVEL (long) 521
+#define GET_ANONYMOUSHOME (long) 522
+#define SET_ANONYMOUSHOME (long) 523
+#define GET_FTPHOME (long) 524
+#define SET_FTPHOME (long) 525
+#define GET_PUBLICHOME (long) 526
+#define SET_PUBLICHOME (long) 527
+#define GET_SHAREDHOME (long) 528
+#define SET_SHAREDHOME (long) 529
+#define GET_MHPROFILE (long) 530
+#define SET_MHPROFILE (long) 531
+#define GET_MHPATH (long) 532
+#define SET_MHPATH (long) 533
+#define GET_ONETIMEEXPUNGEATPING (long) 534
+#define SET_ONETIMEEXPUNGEATPING (long) 535
+#define GET_USERHASNOLIFE (long) 536
+#define SET_USERHASNOLIFE (long) 537
+#define GET_FTPPROTECTION (long) 538
+#define SET_FTPPROTECTION (long) 539
+#define GET_PUBLICPROTECTION (long) 540
+#define SET_PUBLICPROTECTION (long) 541
+#define GET_SHAREDPROTECTION (long) 542
+#define SET_SHAREDPROTECTION (long) 543
+#define GET_LOCKTIMEOUT (long) 544
+#define SET_LOCKTIMEOUT (long) 545
+#define GET_NOTIMEZONES (long) 546
+#define SET_NOTIMEZONES (long) 547
+#define GET_HIDEDOTFILES (long) 548
+#define SET_HIDEDOTFILES (long) 549
+#define GET_FTPDIRPROTECTION (long) 550
+#define SET_FTPDIRPROTECTION (long) 551
+#define GET_PUBLICDIRPROTECTION (long) 552
+#define SET_PUBLICDIRPROTECTION (long) 553
+#define GET_SHAREDDIRPROTECTION (long) 554
+#define SET_SHAREDDIRPROTECTION (long) 555
+#define GET_TRUSTDNS (long) 556
+#define SET_TRUSTDNS (long) 557
+#define GET_SASLUSESPTRNAME (long) 558
+#define SET_SASLUSESPTRNAME (long) 559
+#define GET_NETFSSTATBUG (long) 560
+#define SET_NETFSSTATBUG (long) 561
+#define GET_SNARFMAILBOXNAME (long) 562
+#define SET_SNARFMAILBOXNAME (long) 563
+#define GET_SNARFINTERVAL (long) 564
+#define SET_SNARFINTERVAL (long) 565
+#define GET_SNARFPRESERVE (long) 566
+#define SET_SNARFPRESERVE (long) 567
+#define GET_INBOXPATH (long) 568
+#define SET_INBOXPATH (long) 569
+#define GET_DIRFMTTEST (long) 570
+#define SET_DIRFMTTEST (long) 571
+#define GET_SCANCONTENTS (long) 572
+#define SET_SCANCONTENTS (long) 573
+#define GET_MHALLOWINBOX (long) 574
+#define SET_MHALLOWINBOX (long) 575
+
+/* Driver flags */
+
+#define DR_DISABLE (long) 0x1 /* driver is disabled */
+#define DR_LOCAL (long) 0x2 /* local file driver */
+#define DR_MAIL (long) 0x4 /* supports mail */
+#define DR_NEWS (long) 0x8 /* supports news */
+#define DR_READONLY (long) 0x10 /* driver only allows readonly access */
+#define DR_NOFAST (long) 0x20 /* "fast" data is slow (whole msg fetch) */
+#define DR_NAMESPACE (long) 0x40/* driver has a special namespace */
+#define DR_LOWMEM (long) 0x80 /* low amounts of memory available */
+#define DR_LOCKING (long) 0x100 /* driver does locking */
+#define DR_CRLF (long) 0x200 /* driver internal form uses CRLF newlines */
+#define DR_NOSTICKY (long) 0x400/* driver does not support sticky UIDs */
+#define DR_RECYCLE (long) 0x800 /* driver does stream recycling */
+#define DR_XPOINT (long) 0x1000 /* needs to be checkpointed */
+ /* driver has no real internal date */
+#define DR_NOINTDATE (long) 0x2000
+ /* driver does not announce new mail */
+#define DR_NONEWMAIL (long) 0x4000
+ /* driver does not announce new mail when RO */
+#define DR_NONEWMAILRONLY (long) 0x8000
+ /* driver can be halfopen */
+#define DR_HALFOPEN (long) 0x10000
+#define DR_DIRFMT (long) 0x20000/* driver is a directory-format */
+#define DR_MODSEQ (long) 0x40000/* driver supports modseqs */
+
+
+/* Cache management function codes */
+
+#define CH_INIT (long) 10 /* initialize cache */
+#define CH_SIZE (long) 11 /* (re-)size the cache */
+#define CH_MAKEELT (long) 30 /* return elt, make if needed */
+#define CH_ELT (long) 31 /* return elt if exists */
+#define CH_SORTCACHE (long) 35 /* return sortcache entry, make if needed */
+#define CH_FREE (long) 40 /* free space used by elt */
+ /* free space used by sortcache */
+#define CH_FREESORTCACHE (long) 43
+#define CH_EXPUNGE (long) 45 /* delete elt pointer from list */
+
+
+/* Mailbox open options
+ * For compatibility with the past, OP_DEBUG must always be 1.
+ */
+
+#define OP_DEBUG (long) 0x1 /* debug protocol negotiations */
+#define OP_READONLY (long) 0x2 /* read-only open */
+#define OP_ANONYMOUS (long) 0x4 /* anonymous open of newsgroup */
+#define OP_SHORTCACHE (long) 0x8/* short (elt-only) caching */
+#define OP_SILENT (long) 0x10 /* don't pass up events (internal use) */
+#define OP_PROTOTYPE (long) 0x20/* return driver prototype */
+#define OP_HALFOPEN (long) 0x40 /* half-open (IMAP connect but no select) */
+#define OP_EXPUNGE (long) 0x80 /* silently expunge recycle stream */
+#define OP_SECURE (long) 0x100 /* don't do non-secure authentication */
+#define OP_TRYSSL (long) 0x200 /* try SSL first */
+ /* use multiple newsrc files */
+#define OP_MULNEWSRC (long) 0x400
+#define OP_NOKOD (long) 0x800 /* suppress kiss-of-death */
+#define OP_SNIFF (long) 0x1000 /* metadata only open */
+ /* reserved for application use */
+#define OP_RESERVED (unsigned long) 0xff000000
+
+
+/* Net open options */
+
+ /* no error messages */
+#define NET_SILENT ((unsigned long) 0x80000000)
+ /* no validation of SSL certificates */
+#define NET_NOVALIDATECERT ((unsigned long) 0x40000000)
+ /* no open timeout */
+#define NET_NOOPENTIMEOUT ((unsigned long) 0x20000000)
+ /* TLS not SSL */
+#define NET_TLSCLIENT ((unsigned long) 0x10000000)
+ /* try SSL mode */
+#define NET_TRYSSL ((unsigned long) 0x8000000)
+
+/* Close options */
+
+#define CL_EXPUNGE (long) 1 /* expunge silently */
+
+
+/* Fetch options */
+
+#define FT_UID (long) 0x1 /* argument is a UID */
+#define FT_PEEK (long) 0x2 /* peek at data */
+#define FT_NOT (long) 0x4 /* NOT flag for header lines fetch */
+#define FT_INTERNAL (long) 0x8 /* text can be internal strings */
+ /* IMAP prefetch text when fetching header */
+#define FT_PREFETCHTEXT (long) 0x20
+#define FT_NOHDRS (long) 0x40 /* suppress fetching extra headers (note that
+ this breaks news handling) */
+#define FT_NEEDENV (long) 0x80 /* (internal use) include envelope */
+#define FT_NEEDBODY (long) 0x100/* (internal use) include body structure */
+ /* no fetch lookahead */
+#define FT_NOLOOKAHEAD (long) 0x200
+ /* (internal use) lookahead in hdr searching */
+#define FT_SEARCHLOOKAHEAD (long) 0x400
+ /* stringstruct return hack */
+#define FT_RETURNSTRINGSTRUCT (long) 0x800
+
+
+/* Flagging options */
+
+#define ST_UID (long) 0x1 /* argument is a UID sequence */
+#define ST_SILENT (long) 0x2 /* don't return results */
+#define ST_SET (long) 0x4 /* set vs. clear */
+
+
+/* Expunge options */
+
+#define EX_UID (long) 0x1 /* argument is a UID sequence */
+
+
+/* Copy options */
+
+#define CP_UID (long) 0x1 /* argument is a UID sequence */
+#define CP_MOVE (long) 0x2 /* delete from source after copying */
+ /* set debug in any created stream */
+#define CP_DEBUG (long) 0x20000000
+
+/* Search/sort/thread options */
+
+#define SE_UID (long) 0x1 /* return UID */
+#define SE_FREE (long) 0x2 /* free search program after finished */
+#define SE_NOPREFETCH (long) 0x4/* no search prefetching */
+#define SO_FREE (long) 0x8 /* free sort program after finished */
+#define SE_NOSERVER (long) 0x10 /* don't do server-based search/sort/thread */
+#define SE_RETAIN (long) 0x20 /* retain previous search results */
+#define SO_OVERVIEW (long) 0x40 /* use overviews in searching (NNTP only) */
+#define SE_NEEDBODY (long) 0x80 /* include body structure in prefetch */
+#define SE_NOHDRS (long) 0x100 /* suppress prefetching extra headers (note
+ that this breaks news handling) */
+#define SE_NOLOCAL (long) 0x200 /* no local retry (IMAP only) */
+
+#define SO_NOSERVER SE_NOSERVER /* compatibility name */
+#define SE_SILLYOK (long) 0x400 /* allow silly searches */
+
+
+/* Status options */
+
+#define SA_MESSAGES (long) 0x1 /* number of messages */
+#define SA_RECENT (long) 0x2 /* number of recent messages */
+#define SA_UNSEEN (long) 0x4 /* number of unseen messages */
+#define SA_UIDNEXT (long) 0x8 /* next UID to be assigned */
+ /* UID validity value */
+#define SA_UIDVALIDITY (long) 0x10
+ /* set OP_DEBUG on any created stream */
+#define SA_DEBUG (long) 0x10000000
+ /* use multiple newsrcs */
+#define SA_MULNEWSRC (long) 0x20000000
+
+/* Mailgets flags */
+
+#define MG_UID (long) 0x1 /* message number is a UID */
+#define MG_COPY (long) 0x2 /* must return copy of argument */
+
+/* SASL authenticator categories */
+
+#define AU_SECURE (long) 0x1 /* /secure allowed */
+#define AU_AUTHUSER (long) 0x2 /* /authuser=xxx allowed */
+ /* authenticator hidden */
+#define AU_HIDE (long) 0x10000000
+ /* authenticator disabled */
+#define AU_DISABLE (long) 0x20000000
+
+
+/* Garbage collection flags */
+
+#define GC_ELT (long) 0x1 /* message cache elements */
+#define GC_ENV (long) 0x2 /* envelopes and bodies */
+#define GC_TEXTS (long) 0x4 /* cached texts */
+
+
+/* mm_log()/mm_notify() condition codes */
+
+#define WARN (long) 1 /* mm_log warning type */
+#define ERROR (long) 2 /* mm_log error type */
+#define PARSE (long) 3 /* mm_log parse error type */
+#define BYE (long) 4 /* mm_notify stream dying */
+#define TCPDEBUG (long) 5 /* mm_log TCP debug babble */
+
+
+/* Bits from mail_parse_flags(). Don't change these, since the header format
+ * used by tenex, mtx, and mbx corresponds to these bits.
+ */
+
+#define fSEEN 0x1
+#define fDELETED 0x2
+#define fFLAGGED 0x4
+#define fANSWERED 0x8
+#define fOLD 0x10
+#define fDRAFT 0x20
+
+#define fEXPUNGED 0x8000 /* internal flag */
+
+/* Bits for mm_list() and mm_lsub() */
+
+/* Note that (LATT_NOINFERIORS LATT_HASCHILDREN LATT_HASNOCHILDREN) and
+ * (LATT_NOSELECT LATT_MARKED LATT_UNMARKED) each have eight possible states,
+ * but only four of these are valid. The other four are silly states which
+ * while invalid can unfortunately be expressed in the IMAP protocol.
+ */
+
+ /* terminal node in hierarchy */
+#define LATT_NOINFERIORS (long) 0x1
+ /* name can not be selected */
+#define LATT_NOSELECT (long) 0x2
+ /* changed since last accessed */
+#define LATT_MARKED (long) 0x4
+ /* accessed since last changed */
+#define LATT_UNMARKED (long) 0x8
+ /* name has referral to remote mailbox */
+#define LATT_REFERRAL (long) 0x10
+ /* has selectable inferiors */
+#define LATT_HASCHILDREN (long) 0x20
+ /* has no selectable inferiors */
+#define LATT_HASNOCHILDREN (long) 0x40
+
+
+/* Sort functions */
+
+#define SORTDATE 0 /* date */
+#define SORTARRIVAL 1 /* arrival date */
+#define SORTFROM 2 /* from */
+#define SORTSUBJECT 3 /* subject */
+#define SORTTO 4 /* to */
+#define SORTCC 5 /* cc */
+#define SORTSIZE 6 /* size */
+
+
+/* imapreferral_t codes */
+
+#define REFAUTHFAILED (long) 0 /* authentication referral -- not logged in */
+#define REFAUTH (long) 1 /* authentication referral -- logged in */
+#define REFSELECT (long) 2 /* select referral */
+#define REFCREATE (long) 3
+#define REFDELETE (long) 4
+#define REFRENAME (long) 5
+#define REFSUBSCRIBE (long) 6
+#define REFUNSUBSCRIBE (long) 7
+#define REFSTATUS (long) 8
+#define REFCOPY (long) 9
+#define REFAPPEND (long) 10
+
+
+/* sendcommand_t codes */
+
+ /* expunge response deferred */
+#define SC_EXPUNGEDEFERRED (long) 1
+
+/* Block notification codes */
+
+#define BLOCK_NONE 0 /* not blocked */
+#define BLOCK_SENSITIVE 1 /* sensitive code, disallow alarms */
+#define BLOCK_NONSENSITIVE 2 /* non-sensitive code, allow alarms */
+#define BLOCK_DNSLOOKUP 10 /* blocked on DNS lookup */
+#define BLOCK_TCPOPEN 11 /* blocked on TCP open */
+#define BLOCK_TCPREAD 12 /* blocked on TCP read */
+#define BLOCK_TCPWRITE 13 /* blocked on TCP write */
+#define BLOCK_TCPCLOSE 14 /* blocked on TCP close */
+#define BLOCK_FILELOCK 20 /* blocked on file locking */
+
+
+/* In-memory sized-text */
+
+#define SIZEDTEXT struct mail_sizedtext
+
+SIZEDTEXT {
+ unsigned char *data; /* text */
+ unsigned long size; /* size of text in octets */
+};
+
+
+/* String list */
+
+#define STRINGLIST struct string_list
+
+STRINGLIST {
+ SIZEDTEXT text; /* string text */
+ STRINGLIST *next;
+};
+
+
+/* Parse results from mail_valid_net_parse */
+
+#define NETMAXHOST 256
+#define NETMAXUSER 65
+#define NETMAXMBX (MAILTMPLEN/4)
+#define NETMAXSRV 21
+typedef struct net_mailbox {
+ char host[NETMAXHOST]; /* host name (may be canonicalized) */
+ char orighost[NETMAXHOST]; /* host name before canonicalization */
+ char user[NETMAXUSER]; /* user name */
+ char authuser[NETMAXUSER]; /* authentication user name */
+ char mailbox[NETMAXMBX]; /* mailbox name */
+ char service[NETMAXSRV]; /* service name */
+ unsigned long port; /* TCP port number */
+ unsigned int anoflag : 1; /* anonymous */
+ unsigned int dbgflag : 1; /* debug flag */
+ unsigned int secflag : 1; /* secure flag */
+ unsigned int sslflag : 1; /* SSL driver flag */
+ unsigned int trysslflag : 1; /* try SSL driver first flag */
+ unsigned int novalidate : 1; /* don't validate certificates */
+ unsigned int tlsflag : 1; /* TLS flag */
+ unsigned int notlsflag : 1; /* do not do TLS flag */
+ unsigned int readonlyflag : 1;/* want readonly */
+ unsigned int norsh : 1; /* don't use rsh/ssh */
+ unsigned int loser : 1; /* server is a loser */
+ unsigned int tlssslv23 : 1; /* force SSLv23 client method over TLS */
+} NETMBX;
+
+/* Item in an address list */
+
+#define ADDRESS struct mail_address
+
+ADDRESS {
+ char *personal; /* personal name phrase */
+ char *adl; /* at-domain-list source route */
+ char *mailbox; /* mailbox name */
+ char *host; /* domain name of mailbox's host */
+ char *error; /* error in address from SMTP module */
+ struct {
+ char *type; /* address type (default "rfc822") */
+ char *addr; /* address as xtext */
+ } orcpt;
+ ADDRESS *next; /* pointer to next address in list */
+};
+
+
+/* Message envelope */
+
+typedef struct mail_envelope {
+ unsigned int ngpathexists : 1; /* newsgroups may be bogus */
+ unsigned int incomplete : 1; /* envelope may be incomplete */
+ unsigned int imapenvonly : 1; /* envelope only has IMAP envelope */
+ char *remail; /* remail header if any */
+ ADDRESS *return_path; /* error return address */
+ unsigned char *date; /* message composition date string */
+ ADDRESS *from; /* originator address list */
+ ADDRESS *sender; /* sender address list */
+ ADDRESS *reply_to; /* reply address list */
+ char *subject; /* message subject string */
+ ADDRESS *to; /* primary recipient list */
+ ADDRESS *cc; /* secondary recipient list */
+ ADDRESS *bcc; /* blind secondary recipient list */
+ char *in_reply_to; /* replied message ID */
+ char *message_id; /* message ID */
+ char *newsgroups; /* USENET newsgroups */
+ char *followup_to; /* USENET reply newsgroups */
+ char *references; /* USENET references */
+ void *sparep; /* spare pointer reserved for main program */
+} ENVELOPE;
+
+/* Primary body types */
+/* If you change any of these you must also change body_types in rfc822.c */
+
+#define TYPETEXT 0 /* unformatted text */
+#define TYPEMULTIPART 1 /* multiple part */
+#define TYPEMESSAGE 2 /* encapsulated message */
+#define TYPEAPPLICATION 3 /* application data */
+#define TYPEAUDIO 4 /* audio */
+#define TYPEIMAGE 5 /* static image */
+#define TYPEVIDEO 6 /* video */
+#define TYPEMODEL 7 /* model */
+#define TYPEOTHER 8 /* unknown */
+#define TYPEMAX 15 /* maximum type code */
+
+
+/* Body encodings */
+/* If you change any of these you must also change body_encodings in rfc822.c
+ */
+
+#define ENC7BIT 0 /* 7 bit SMTP semantic data */
+#define ENC8BIT 1 /* 8 bit SMTP semantic data */
+#define ENCBINARY 2 /* 8 bit binary data */
+#define ENCBASE64 3 /* base-64 encoded data */
+#define ENCQUOTEDPRINTABLE 4 /* human-readable 8-as-7 bit data */
+#define ENCOTHER 5 /* unknown */
+#define ENCMAX 10 /* maximum encoding code */
+
+
+/* Body contents */
+
+#define BODY struct mail_bodystruct
+#define MESSAGE struct mail_body_message
+#define PARAMETER struct mail_body_parameter
+#define PART struct mail_body_part
+#define PARTTEXT struct mail_body_text
+
+/* Message body text */
+
+PARTTEXT {
+ unsigned long offset; /* offset from body origin */
+ SIZEDTEXT text; /* text */
+};
+
+
+/* Message body structure */
+
+BODY {
+ unsigned short type; /* body primary type */
+ unsigned short encoding; /* body transfer encoding */
+ char *subtype; /* subtype string */
+ PARAMETER *parameter; /* parameter list */
+ char *id; /* body identifier */
+ char *description; /* body description */
+ struct { /* body disposition */
+ char *type; /* disposition type */
+ PARAMETER *parameter; /* disposition parameters */
+ } disposition;
+ STRINGLIST *language; /* body language */
+ char *location; /* body content URI */
+ PARTTEXT mime; /* MIME header */
+ PARTTEXT contents; /* body part contents */
+ union { /* different ways of accessing contents */
+ PART *part; /* body part list */
+ MESSAGE *msg; /* body encapsulated message */
+ } nested;
+ struct {
+ unsigned long lines; /* size of text in lines */
+ unsigned long bytes; /* size of text in octets */
+ } size;
+ char *md5; /* MD5 checksum */
+ void *sparep; /* spare pointer reserved for main program */
+};
+
+
+/* Parameter list */
+
+PARAMETER {
+ char *attribute; /* parameter attribute name */
+ char *value; /* parameter value */
+ PARAMETER *next; /* next parameter in list */
+};
+
+
+/* Multipart content list */
+
+PART {
+ BODY body; /* body information for this part */
+ PART *next; /* next body part */
+};
+
+
+/* RFC-822 Message */
+
+MESSAGE {
+ ENVELOPE *env; /* message envelope */
+ BODY *body; /* message body */
+ PARTTEXT full; /* full message */
+ STRINGLIST *lines; /* lines used to filter header */
+ PARTTEXT header; /* header text */
+ PARTTEXT text; /* body text */
+};
+
+/* Entry in the message cache array */
+
+typedef struct message_cache {
+ unsigned long msgno; /* message number */
+ unsigned int lockcount : 8; /* non-zero if multiple references */
+ unsigned long rfc822_size; /* # of bytes of message as raw RFC822 */
+ struct { /* c-client internal use only */
+ unsigned long uid; /* message unique ID */
+ unsigned long mod; /* modseq */
+ PARTTEXT special; /* special text pointers */
+ MESSAGE msg; /* internal message pointers */
+ union { /* driver internal use */
+ unsigned long data;
+ void *ptr;
+ } spare;
+ unsigned int sequence : 1; /* saved sequence bit */
+ unsigned int dirty : 1; /* driver internal use */
+ unsigned int filter : 1; /* driver internal use */
+ unsigned int ghost : 1; /* driver internal use */
+ } private;
+ /* internal date */
+ unsigned int day : 5; /* day of month (1-31) */
+ unsigned int month : 4; /* month of year (1-12) */
+ unsigned int year : 7; /* year since BASEYEAR (expires in 127 yrs) */
+ unsigned int hours: 5; /* hours (0-23) */
+ unsigned int minutes: 6; /* minutes (0-59) */
+ unsigned int seconds: 6; /* seconds (0-59) */
+ unsigned int zoccident : 1; /* non-zero if west of UTC */
+ unsigned int zhours : 4; /* hours from UTC (0-12) */
+ unsigned int zminutes: 6; /* minutes (0-59) */
+ /* system flags */
+ unsigned int seen : 1; /* system Seen flag */
+ unsigned int deleted : 1; /* system Deleted flag */
+ unsigned int flagged : 1; /* system Flagged flag */
+ unsigned int answered : 1; /* system Answered flag */
+ unsigned int draft : 1; /* system Draft flag */
+ unsigned int recent : 1; /* system Recent flag */
+ /* message status */
+ unsigned int valid : 1; /* elt has valid flags */
+ unsigned int searched : 1; /* message was searched */
+ unsigned int sequence : 1; /* message is in sequence */
+ /* reserved for use by main program */
+ unsigned int spare : 1; /* first spare bit */
+ unsigned int spare2 : 1; /* second spare bit */
+ unsigned int spare3 : 1; /* third spare bit */
+ unsigned int spare4 : 1; /* fourth spare bit */
+ unsigned int spare5 : 1; /* fifth spare bit */
+ unsigned int spare6 : 1; /* sixth spare bit */
+ unsigned int spare7 : 1; /* seventh spare bit */
+ unsigned int spare8 : 1; /* eighth spare bit */
+ void *sparep; /* spare pointer */
+ unsigned long user_flags; /* user-assignable flags */
+} MESSAGECACHE;
+
+/* String structure */
+
+#define STRINGDRIVER struct string_driver
+
+typedef struct mailstring {
+ void *data; /* driver-dependent data */
+ unsigned long data1; /* driver-dependent data */
+ unsigned long size; /* total length of string */
+ char *chunk; /* base address of chunk */
+ unsigned long chunksize; /* size of chunk */
+ unsigned long offset; /* offset of this chunk in base */
+ char *curpos; /* current position in chunk */
+ unsigned long cursize; /* number of bytes remaining in chunk */
+ STRINGDRIVER *dtb; /* driver that handles this type of string */
+} STRING;
+
+
+/* Dispatch table for string driver */
+
+STRINGDRIVER {
+ /* initialize string driver */
+ void (*init) (STRING *s,void *data,unsigned long size);
+ /* get next character in string */
+ char (*next) (STRING *s);
+ /* set position in string */
+ void (*setpos) (STRING *s,unsigned long i);
+};
+
+
+/* Stringstruct access routines */
+
+#define INIT(s,d,data,size) ((*((s)->dtb = &d)->init) (s,data,size))
+#define SIZE(s) ((s)->size - GETPOS (s))
+#define CHR(s) (*(s)->curpos)
+#define SNX(s) (--(s)->cursize ? *(s)->curpos++ : (*(s)->dtb->next) (s))
+#define GETPOS(s) ((s)->offset + ((s)->curpos - (s)->chunk))
+#define SETPOS(s,i) (*(s)->dtb->setpos) (s,i)
+
+/* Search program */
+
+#define SEARCHPGM struct search_program
+#define SEARCHHEADER struct search_header
+#define SEARCHSET struct search_set
+#define SEARCHOR struct search_or
+#define SEARCHPGMLIST struct search_pgm_list
+
+
+SEARCHHEADER { /* header search */
+ SIZEDTEXT line; /* header line */
+ SIZEDTEXT text; /* text in header */
+ SEARCHHEADER *next; /* next in list */
+};
+
+
+SEARCHSET { /* message set */
+ unsigned long first; /* sequence number */
+ unsigned long last; /* last value, if a range */
+ SEARCHSET *next; /* next in list */
+};
+
+
+SEARCHOR {
+ SEARCHPGM *first; /* first program */
+ SEARCHPGM *second; /* second program */
+ SEARCHOR *next; /* next in list */
+};
+
+
+SEARCHPGMLIST {
+ SEARCHPGM *pgm; /* search program */
+ SEARCHPGMLIST *next; /* next in list */
+};
+
+SEARCHPGM { /* search program */
+ SEARCHSET *msgno; /* message numbers */
+ SEARCHSET *uid; /* unique identifiers */
+ SEARCHOR *or; /* or'ed in programs */
+ SEARCHPGMLIST *not; /* and'ed not program */
+ SEARCHHEADER *header; /* list of headers */
+ STRINGLIST *bcc; /* bcc recipients */
+ STRINGLIST *body; /* text in message body */
+ STRINGLIST *cc; /* cc recipients */
+ STRINGLIST *from; /* originator */
+ STRINGLIST *keyword; /* keywords */
+ STRINGLIST *unkeyword; /* unkeywords */
+ STRINGLIST *subject; /* text in subject */
+ STRINGLIST *text; /* text in headers and body */
+ STRINGLIST *to; /* to recipients */
+ unsigned long larger; /* larger than this size */
+ unsigned long smaller; /* smaller than this size */
+ unsigned long older; /* older than this interval */
+ unsigned long younger; /* younger than this interval */
+ unsigned short sentbefore; /* sent before this date */
+ unsigned short senton; /* sent on this date */
+ unsigned short sentsince; /* sent since this date */
+ unsigned short before; /* before this date */
+ unsigned short on; /* on this date */
+ unsigned short since; /* since this date */
+ unsigned int answered : 1; /* answered messages */
+ unsigned int unanswered : 1; /* unanswered messages */
+ unsigned int deleted : 1; /* deleted messages */
+ unsigned int undeleted : 1; /* undeleted messages */
+ unsigned int draft : 1; /* message draft */
+ unsigned int undraft : 1; /* message undraft */
+ unsigned int flagged : 1; /* flagged messages */
+ unsigned int unflagged : 1; /* unflagged messages */
+ unsigned int recent : 1; /* recent messages */
+ unsigned int old : 1; /* old messages */
+ unsigned int seen : 1; /* seen messages */
+ unsigned int unseen : 1; /* unseen messages */
+ /* These must be simulated in IMAP */
+ STRINGLIST *return_path; /* error return address */
+ STRINGLIST *sender; /* sender address list */
+ STRINGLIST *reply_to; /* reply address list */
+ STRINGLIST *in_reply_to; /* replied message ID */
+ STRINGLIST *message_id; /* message ID */
+ STRINGLIST *newsgroups; /* USENET newsgroups */
+ STRINGLIST *followup_to; /* USENET reply newsgroups */
+ STRINGLIST *references; /* USENET references */
+};
+
+
+/* Mailbox status */
+
+typedef struct mbx_status {
+ long flags; /* validity flags */
+ unsigned long messages; /* number of messages */
+ unsigned long recent; /* number of recent messages */
+ unsigned long unseen; /* number of unseen messages */
+ unsigned long uidnext; /* next UID to be assigned */
+ unsigned long uidvalidity; /* UID validity value */
+} MAILSTATUS;
+
+/* Sort program */
+
+typedef void (*postsort_t) (void *sc);
+
+#define SORTPGM struct sort_program
+
+SORTPGM {
+ unsigned int reverse : 1; /* sort function is to be reversed */
+ unsigned int abort : 1; /* abort sorting */
+ short function; /* sort function */
+ unsigned long nmsgs; /* number of messages being sorted */
+ struct {
+ unsigned long cached; /* number of messages cached so far */
+ unsigned long sorted; /* number of messages sorted so far */
+ unsigned long postsorted; /* number of postsorted messages so far */
+ } progress;
+ postsort_t postsort; /* post sorter */
+ SORTPGM *next; /* next function */
+};
+
+
+/* Sort cache */
+
+#define SORTCACHE struct sort_cache
+
+SORTCACHE {
+ unsigned int sorted : 1; /* message has been sorted */
+ unsigned int postsorted : 1; /* message has been postsorted */
+ unsigned int refwd : 1; /* subject is a re or fwd */
+ unsigned int dirty : 1; /* has data not written to backup */
+ SORTPGM *pgm; /* sort program */
+ unsigned long num; /* message number (sequence or UID) */
+ unsigned long date; /* sent date */
+ unsigned long arrival; /* arrival date */
+ unsigned long size; /* message size */
+ char *from; /* from string */
+ char *to; /* to string */
+ char *cc; /* cc string */
+ char *subject; /* extracted subject string */
+ char *message_id; /* message-id string */
+ char *unique; /* unique string, normally message-id */
+ STRINGLIST *references; /* references string */
+};
+
+/* ACL list */
+
+#define ACLLIST struct acl_list
+
+ACLLIST {
+ char *identifier; /* authentication identifier */
+ char *rights; /* access rights */
+ ACLLIST *next;
+};
+
+/* Quota resource list */
+
+#define QUOTALIST struct quota_list
+
+QUOTALIST {
+ char *name; /* resource name */
+ unsigned long usage; /* resource usage */
+ unsigned long limit; /* resource limit */
+ QUOTALIST *next; /* next resource */
+};
+
+/* Mail Access I/O stream */
+
+
+/* Structure for mail driver dispatch */
+
+#define DRIVER struct driver
+
+
+/* Mail I/O stream */
+
+typedef struct mail_stream {
+ DRIVER *dtb; /* dispatch table for this driver */
+ void *local; /* pointer to driver local data */
+ char *mailbox; /* mailbox name (canonicalized) */
+ char *original_mailbox; /* mailbox name (non-canonicalized) */
+ unsigned short use; /* stream use count */
+ unsigned short sequence; /* stream sequence */
+ unsigned int inbox : 1; /* stream open on an INBOX */
+ unsigned int lock : 1; /* stream lock flag */
+ unsigned int debug : 1; /* stream debug flag */
+ unsigned int silent : 1; /* don't pass events to main program */
+ unsigned int rdonly : 1; /* stream read-only flag */
+ unsigned int anonymous : 1; /* stream anonymous access flag */
+ unsigned int scache : 1; /* stream short cache flag */
+ unsigned int halfopen : 1; /* stream half-open flag */
+ unsigned int secure : 1; /* stream secure flag */
+ unsigned int tryssl : 1; /* stream tryssl flag */
+ unsigned int mulnewsrc : 1; /* stream use multiple newsrc files */
+ unsigned int perm_seen : 1; /* permanent Seen flag */
+ unsigned int perm_deleted : 1;/* permanent Deleted flag */
+ unsigned int perm_flagged : 1;/* permanent Flagged flag */
+ unsigned int perm_answered :1;/* permanent Answered flag */
+ unsigned int perm_draft : 1; /* permanent Draft flag */
+ unsigned int kwd_create : 1; /* can create new keywords */
+ unsigned int uid_nosticky : 1;/* UIDs are not preserved */
+ unsigned int unhealthy : 1; /* unhealthy protocol negotiations */
+ unsigned int nokod : 1; /* suppress kiss-of-death */
+ unsigned int sniff : 1; /* metadata only */
+ unsigned long perm_user_flags;/* mask of permanent user flags */
+ unsigned long gensym; /* generated tag */
+ unsigned long nmsgs; /* # of associated msgs */
+ unsigned long recent; /* # of recent msgs */
+ unsigned long uid_validity; /* UID validity sequence */
+ unsigned long uid_last; /* last assigned UID */
+ char *user_flags[NUSERFLAGS]; /* pointers to user flags in bit order */
+ unsigned long cachesize; /* size of message cache */
+ MESSAGECACHE **cache; /* message cache array */
+ SORTCACHE **sc; /* sort cache array */
+ unsigned long msgno; /* message number of `current' message */
+ ENVELOPE *env; /* scratch buffer for envelope */
+ BODY *body; /* scratch buffer for body */
+ SIZEDTEXT text; /* scratch buffer for text */
+ struct {
+ char *name; /* mailbox name to snarf from */
+ unsigned long time; /* last snarf time */
+ long options; /* snarf open options */
+ } snarf;
+ struct { /* internal use only */
+ struct { /* search temporaries */
+ STRINGLIST *string; /* string(s) to search */
+ long result; /* search result */
+ char *text; /* cache of fetched text */
+ } search;
+ STRING string; /* stringstruct return hack */
+ } private;
+ /* reserved for use by main program */
+ void *sparep; /* spare pointer */
+ unsigned int spare : 1; /* first spare bit */
+ unsigned int spare2 : 1; /* second spare bit */
+ unsigned int spare3 : 1; /* third spare bit */
+ unsigned int spare4 : 1; /* fourth spare bit */
+ unsigned int spare5 : 1; /* fifth spare bit */
+ unsigned int spare6 : 1; /* sixth spare bit */
+ unsigned int spare7 : 1; /* seventh spare bit */
+ unsigned int spare8 : 1; /* eighth spare bit */
+} MAILSTREAM;
+
+/* Mail I/O stream handle */
+
+typedef struct mail_stream_handle {
+ MAILSTREAM *stream; /* pointer to mail stream */
+ unsigned short sequence; /* sequence of what we expect stream to be */
+} MAILHANDLE;
+
+
+/* Message overview */
+
+typedef struct mail_overview {
+ char *subject; /* message subject string */
+ ADDRESS *from; /* originator address list */
+ char *date; /* message composition date string */
+ char *message_id; /* message ID */
+ char *references; /* USENET references */
+ struct { /* may be 0 or NUL if unknown/undefined */
+ unsigned long octets; /* message octets (probably LF-newline form) */
+ unsigned long lines; /* message lines */
+ char *xref; /* cross references */
+ } optional;
+} OVERVIEW;
+
+/* Network access I/O stream */
+
+
+/* Structure for network driver dispatch */
+
+#define NETDRIVER struct net_driver
+
+
+/* Network transport I/O stream */
+
+typedef struct net_stream {
+ void *stream; /* driver's I/O stream */
+ NETDRIVER *dtb; /* network driver */
+} NETSTREAM;
+
+
+/* Network transport driver dispatch */
+
+NETDRIVER {
+ void *(*open) (char *host,char *service,unsigned long port);
+ void *(*aopen) (NETMBX *mb,char *service,char *usrbuf);
+ char *(*getline) (void *stream);
+ long (*getbuffer) (void *stream,unsigned long size,char *buffer);
+ long (*soutr) (void *stream,char *string);
+ long (*sout) (void *stream,char *string,unsigned long size);
+ void (*close) (void *stream);
+ char *(*host) (void *stream);
+ char *(*remotehost) (void *stream);
+ unsigned long (*port) (void *stream);
+ char *(*localhost) (void *stream);
+};
+
+
+/* Mailgets data identifier */
+
+typedef struct getsdata {
+ MAILSTREAM *stream;
+ unsigned long msgno;
+ char *what;
+ STRINGLIST *stl;
+ unsigned long first;
+ unsigned long last;
+ long flags;
+} GETS_DATA;
+
+
+#define INIT_GETS(md,s,m,w,f,l) \
+ md.stream = s, md.msgno = m, md.what = w, md.first = f, md.last = l, \
+ md.stl = NIL, md.flags = NIL;
+
+/* Mail delivery I/O stream */
+
+typedef struct send_stream {
+ NETSTREAM *netstream; /* network I/O stream */
+ char *host; /* SMTP service host */
+ char *reply; /* last reply string */
+ long replycode; /* last reply code */
+ unsigned int debug : 1; /* stream debug flag */
+ unsigned int sensitive : 1; /* sensitive data in progress */
+ unsigned int loser : 1; /* server is a loser */
+ unsigned int saslcancel : 1; /* SASL cancelled by protocol */
+ union { /* protocol specific */
+ struct { /* SMTP specific */
+ unsigned int ok : 1; /* supports ESMTP */
+ struct { /* service extensions */
+ unsigned int send : 1; /* supports SEND */
+ unsigned int soml : 1; /* supports SOML */
+ unsigned int saml : 1; /* supports SAML */
+ unsigned int expn : 1; /* supports EXPN */
+ unsigned int help : 1; /* supports HELP */
+ unsigned int turn : 1; /* supports TURN */
+ unsigned int etrn : 1; /* supports ETRN */
+ unsigned int starttls:1;/* supports STARTTLS */
+ unsigned int relay : 1; /* supports relaying */
+ unsigned int pipe : 1; /* supports pipelining */
+ unsigned int ensc : 1; /* supports enhanced status codes */
+ unsigned int bmime : 1; /* supports BINARYMIME */
+ unsigned int chunk : 1; /* supports CHUNKING */
+ } service;
+ struct { /* 8-bit MIME transport */
+ unsigned int ok : 1; /* supports 8-bit MIME */
+ unsigned int want : 1; /* want 8-bit MIME */
+ } eightbit;
+ struct { /* delivery status notification */
+ unsigned int ok : 1; /* supports DSN */
+ unsigned int want : 1; /* want DSN */
+ struct { /* notification options */
+ /* notify on failure */
+ unsigned int failure : 1;
+ /* notify on delay */
+ unsigned int delay : 1;
+ /* notify on success */
+ unsigned int success : 1;
+ } notify;
+ unsigned int full : 1; /* return full headers */
+ char *envid; /* envelope identifier as xtext */
+ } dsn;
+ struct { /* size declaration */
+ unsigned int ok : 1; /* supports SIZE */
+ unsigned long limit; /* maximum size supported */
+ } size;
+ struct { /* deliverby declaration */
+ unsigned int ok : 1; /* supports DELIVERBY */
+ unsigned long minby; /* minimum by-time */
+ } deliverby;
+ struct { /* authenticated turn */
+ unsigned int ok : 1; /* supports ATRN */
+ char *domains; /* domains */
+ } atrn;
+ /* supported SASL authenticators */
+ unsigned int auth : MAXAUTHENTICATORS;
+ } esmtp;
+ struct { /* NNTP specific */
+ unsigned int post : 1; /* supports POST */
+ struct { /* NNTP extensions */
+ unsigned int ok : 1; /* supports extensions */
+ /* supports LISTGROUP */
+ unsigned int listgroup : 1;
+ unsigned int over : 1; /* supports OVER */
+ unsigned int hdr : 1; /* supports HDR */
+ unsigned int pat : 1; /* supports PAT */
+ /* supports STARTTLS */
+ unsigned int starttls : 1;
+ /* server has MULTIDOMAIN */
+ unsigned int multidomain : 1;
+ /* supports AUTHINFO USER */
+ unsigned int authuser : 1;
+ /* supported authenticators */
+ unsigned int sasl : MAXAUTHENTICATORS;
+ } ext;
+ } nntp;
+ } protocol;
+} SENDSTREAM;
+
+/* Jacket into external interfaces */
+
+typedef long (*readfn_t) (void *stream,unsigned long size,char *buffer);
+typedef char *(*mailgets_t) (readfn_t f,void *stream,unsigned long size,
+ GETS_DATA *md);
+typedef char *(*readprogress_t) (GETS_DATA *md,unsigned long octets);
+typedef void *(*mailcache_t) (MAILSTREAM *stream,unsigned long msgno,long op);
+typedef long (*mailproxycopy_t) (MAILSTREAM *stream,char *sequence,
+ char *mailbox,long options);
+typedef long (*tcptimeout_t) (long overall,long last, char *host);
+typedef void *(*authchallenge_t) (void *stream,unsigned long *len);
+typedef long (*authrespond_t) (void *stream,char *s,unsigned long size);
+typedef long (*authcheck_t) (void);
+typedef long (*authclient_t) (authchallenge_t challenger,
+ authrespond_t responder,char *service,NETMBX *mb,
+ void *s,unsigned long *trial,char *user);
+typedef char *(*authresponse_t) (void *challenge,unsigned long clen,
+ unsigned long *rlen);
+typedef char *(*authserver_t) (authresponse_t responder,int argc,char *argv[]);
+typedef void (*smtpverbose_t) (char *buffer);
+typedef void (*imapenvelope_t) (MAILSTREAM *stream,unsigned long msgno,
+ ENVELOPE *env);
+typedef char *(*imapreferral_t) (MAILSTREAM *stream,char *url,long code);
+typedef void (*overview_t) (MAILSTREAM *stream,unsigned long uid,OVERVIEW *ov,
+ unsigned long msgno);
+typedef unsigned long *(*sorter_t) (MAILSTREAM *stream,char *charset,
+ SEARCHPGM *spg,SORTPGM *pgm,long flags);
+typedef void (*parseline_t) (ENVELOPE *env,char *hdr,char *data,char *host);
+typedef ADDRESS *(*parsephrase_t) (char *phrase,char *end,char *host);
+typedef void *(*blocknotify_t) (int reason,void *data);
+typedef long (*kinit_t) (char *host,char *reason);
+typedef void (*sendcommand_t) (MAILSTREAM *stream,char *cmd,long flags);
+typedef char *(*newsrcquery_t) (MAILSTREAM *stream,char *mulname,char *name);
+typedef void (*getacl_t) (MAILSTREAM *stream,char *mailbox,ACLLIST *acl);
+typedef void (*listrights_t) (MAILSTREAM *stream,char *mailbox,char *id,
+ char *alwaysrights,STRINGLIST *possiblerights);
+typedef void (*myrights_t) (MAILSTREAM *stream,char *mailbox,char *rights);
+typedef void (*quota_t) (MAILSTREAM *stream,char *qroot,QUOTALIST *qlist);
+typedef void (*quotaroot_t) (MAILSTREAM *stream,char *mbx,STRINGLIST *qroot);
+typedef void (*sortresults_t) (MAILSTREAM *stream,unsigned long *list,
+ unsigned long size);
+typedef char *(*userprompt_t) (void);
+typedef long (*append_t) (MAILSTREAM *stream,void *data,char **flags,
+ char **date,STRING **message);
+typedef void (*copyuid_t) (MAILSTREAM *stream,char *mailbox,
+ unsigned long uidvalidity,SEARCHSET *sourceset,
+ SEARCHSET *destset);
+typedef void (*appenduid_t) (char *mailbox,unsigned long uidvalidity,
+ SEARCHSET *set);
+typedef long (*dirfmttest_t) (char *name);
+typedef long (*scancontents_t) (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz);
+
+typedef void (*freeeltsparep_t) (void **sparep);
+typedef void (*freeenvelopesparep_t) (void **sparep);
+typedef void (*freebodysparep_t) (void **sparep);
+typedef void (*freestreamsparep_t) (void **sparep);
+typedef void *(*sslstart_t) (void *stream,char *host,unsigned long flags);
+typedef long (*sslcertificatequery_t) (char *reason,char *host,char *cert);
+typedef void (*sslfailure_t) (char *host,char *reason,unsigned long flags);
+typedef void (*logouthook_t) (void *data);
+typedef char *(*sslclientcert_t) (void);
+typedef char *(*sslclientkey_t) (void);
+
+/* Globals */
+
+extern char *body_types[]; /* defined body type strings */
+extern char *body_encodings[]; /* defined body encoding strings */
+extern const char *days[]; /* day name strings */
+extern const char *months[]; /* month name strings */
+
+/* Threading */
+
+/* Thread node */
+
+#define THREADNODE struct thread_node
+
+THREADNODE {
+ unsigned long num; /* message number */
+ SORTCACHE *sc; /* (internal use) sortcache entry */
+ THREADNODE *branch; /* branch at this point in tree */
+ THREADNODE *next; /* next node */
+};
+
+typedef void (*threadresults_t) (MAILSTREAM *stream,THREADNODE *tree);
+
+
+/* Thread dispatch */
+
+#define THREADER struct threader_list
+
+THREADER {
+ char *name; /* name of threader */
+ THREADNODE *(*dispatch) (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
+ long flags,sorter_t sorter);
+ THREADER *next;
+};
+
+
+/* Container for references threading */
+
+typedef void ** container_t;
+
+/* Namespaces */
+
+#define NAMESPACE struct mail_namespace
+
+NAMESPACE {
+ char *name; /* name of this namespace */
+ int delimiter; /* hierarchy delimiter */
+ PARAMETER *param; /* namespace parameters */
+ NAMESPACE *next; /* next namespace */
+};
+
+
+/* Authentication */
+
+#define AUTHENTICATOR struct mail_authenticator
+
+AUTHENTICATOR {
+ long flags; /* authenticator flags */
+ char *name; /* name of this authenticator */
+ authcheck_t valid; /* authenticator valid on this system */
+ authclient_t client; /* client function that supports it */
+ authserver_t server; /* server function that supports it */
+ AUTHENTICATOR *next; /* next authenticator */
+};
+
+/* Mail driver dispatch */
+
+DRIVER {
+ char *name; /* driver name */
+ unsigned long flags; /* driver flags */
+ DRIVER *next; /* next driver */
+ /* mailbox is valid for us */
+ DRIVER *(*valid) (char *mailbox);
+ /* manipulate driver parameters */
+ void *(*parameters) (long function,void *value);
+ /* scan mailboxes */
+ void (*scan) (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+ /* list mailboxes */
+ void (*list) (MAILSTREAM *stream,char *ref,char *pat);
+ /* list subscribed mailboxes */
+ void (*lsub) (MAILSTREAM *stream,char *ref,char *pat);
+ /* subscribe to mailbox */
+ long (*subscribe) (MAILSTREAM *stream,char *mailbox);
+ /* unsubscribe from mailbox */
+ long (*unsubscribe) (MAILSTREAM *stream,char *mailbox);
+ /* create mailbox */
+ long (*create) (MAILSTREAM *stream,char *mailbox);
+ /* delete mailbox */
+ long (*mbxdel) (MAILSTREAM *stream,char *mailbox);
+ /* rename mailbox */
+ long (*mbxren) (MAILSTREAM *stream,char *old,char *newname);
+ /* status of mailbox */
+ long (*status) (MAILSTREAM *stream,char *mbx,long flags);
+
+ /* open mailbox */
+ MAILSTREAM *(*open) (MAILSTREAM *stream);
+ /* close mailbox */
+ void (*close) (MAILSTREAM *stream,long options);
+ /* fetch message "fast" attributes */
+ void (*fast) (MAILSTREAM *stream,char *sequence,long flags);
+ /* fetch message flags */
+ void (*msgflags) (MAILSTREAM *stream,char *sequence,long flags);
+ /* fetch message overview */
+ long (*overview) (MAILSTREAM *stream,overview_t ofn);
+ /* fetch message envelopes */
+ ENVELOPE *(*structure) (MAILSTREAM *stream,unsigned long msgno,BODY **body,
+ long flags);
+ /* return RFC-822 header */
+ char *(*header) (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+ /* return RFC-822 text */
+ long (*text) (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+ /* load cache */
+ long (*msgdata) (MAILSTREAM *stream,unsigned long msgno,char *section,
+ unsigned long first,unsigned long last,STRINGLIST *lines,
+ long flags);
+ /* return UID for message */
+ unsigned long (*uid) (MAILSTREAM *stream,unsigned long msgno);
+ /* return message number from UID */
+ unsigned long (*msgno) (MAILSTREAM *stream,unsigned long uid);
+ /* modify flags */
+ void (*flag) (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+ /* per-message modify flags */
+ void (*flagmsg) (MAILSTREAM *stream,MESSAGECACHE *elt);
+ /* search for message based on criteria */
+ long (*search) (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,long flags);
+ /* sort messages */
+ unsigned long *(*sort) (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
+ SORTPGM *pgm,long flags);
+ /* thread messages */
+ THREADNODE *(*thread) (MAILSTREAM *stream,char *type,char *charset,
+ SEARCHPGM *spg,long flag);
+ /* ping mailbox to see if still alive */
+ long (*ping) (MAILSTREAM *stream);
+ /* check for new messages */
+ void (*check) (MAILSTREAM *stream);
+ /* expunge deleted messages */
+ long (*expunge) (MAILSTREAM *stream,char *sequence,long options);
+ /* copy messages to another mailbox */
+ long (*copy) (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+ /* append string message to mailbox */
+ long (*append) (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+ /* garbage collect stream */
+ void (*gc) (MAILSTREAM *stream,long gcflags);
+};
+
+
+#include "linkage.h"
+
+/* Compatibility support names for old interfaces */
+
+#define GET_TRYALTFIRST GET_TRYSSLFIRST
+#define SET_TRYALTFIRST SET_TRYSSLFIRST
+#define GET_IMAPTRYALT GET_IMAPTRYSSL
+#define SET_IMAPTRYALT SET_IMAPTRYSSL
+#define OP_TRYALT OP_TRYSSL
+#define altflag sslflag
+
+#define mail_close(stream) \
+ mail_close_full (stream,NIL)
+#define mail_fetchfast(stream,sequence) \
+ mail_fetch_fast (stream,sequence,NIL)
+#define mail_fetchfast_full mail_fetch_fast
+#define mail_fetchflags(stream,sequence) \
+ mail_fetch_flags (stream,sequence,NIL)
+#define mail_fetchflags_full mail_fetch_flags
+#define mail_fetchenvelope(stream,msgno) \
+ mail_fetch_structure (stream,msgno,NIL,NIL)
+#define mail_fetchstructure(stream,msgno,body) \
+ mail_fetch_structure (stream,msgno,body,NIL)
+#define mail_fetchstructure_full mail_fetch_structure
+#define mail_fetchheader(stream,msgno) \
+ mail_fetch_header (stream,msgno,NIL,NIL,NIL,FT_PEEK)
+#define mail_fetchheader_full(stream,msgno,lines,len,flags) \
+ mail_fetch_header (stream,msgno,NIL,lines,len,FT_PEEK | (flags))
+#define mail_fetchtext(stream,msgno) \
+ mail_fetch_text (stream,msgno,NIL,NIL,NIL)
+#define mail_fetchtext_full(stream,msgno,length,flags) \
+ mail_fetch_text (stream,msgno,NIL,length,flags)
+#define mail_fetchbody(stream,msgno,section,length) \
+ mail_fetch_body (stream,msgno,section,length,NIL)
+#define mail_fetchbody_full mail_fetch_body
+#define mail_setflag(stream,sequence,flag) \
+ mail_flag (stream,sequence,flag,ST_SET)
+#define mail_setflag_full(stream,sequence,flag,flags) \
+ mail_flag (stream,sequence,flag,ST_SET | (flags))
+#define mail_clearflag(stream,sequence,flag) \
+ mail_flag (stream,sequence,flag,NIL)
+#define mail_clearflag_full mail_flag
+#define mail_search(stream,criteria) \
+ mail_search_full (stream,NIL,mail_criteria (criteria),SE_FREE);
+#define mail_expunge(stream) \
+ mail_expunge_full (stream,NIL,NIL)
+#define mail_copy(stream,sequence,mailbox) \
+ mail_copy_full (stream,sequence,mailbox,NIL)
+#define mail_move(stream,sequence,mailbox) \
+ mail_copy_full (stream,sequence,mailbox,CP_MOVE)
+#define mail_append(stream,mailbox,message) \
+ mail_append_full (stream,mailbox,NIL,NIL,message)
+
+/* Interfaces for SVR4 locking brain-damage workaround */
+
+/* Driver dispatching */
+
+#define SAFE_DELETE(dtb,stream,mailbox) (*dtb->mbxdel) (stream,mailbox)
+#define SAFE_RENAME(dtb,stream,old,newname) (*dtb->mbxren) (stream,old,newname)
+#define SAFE_STATUS(dtb,stream,mbx,flags) (*dtb->status) (stream,mbx,flags)
+#define SAFE_COPY(dtb,stream,sequence,mailbox,options) \
+ (*dtb->copy) (stream,sequence,mailbox,options)
+#define SAFE_APPEND(dtb,stream,mailbox,af,data) \
+ (*dtb->append) (stream,mailbox,af,data)
+#define SAFE_SCAN_CONTENTS(dtb,name,contents,csiz,fsiz) \
+ scan_contents (dtb,name,contents,csiz,fsiz)
+
+
+/* Driver callbacks */
+
+#define MM_EXISTS mm_exists
+#define MM_EXPUNGED mm_expunged
+#define MM_FLAGS mm_flags
+#define MM_NOTIFY mm_notify
+#define MM_STATUS mm_status
+#define MM_LOG mm_log
+#define MM_CRITICAL mm_critical
+#define MM_NOCRITICAL mm_nocritical
+#define MM_DISKERROR mm_diskerror
+#define MM_FATAL mm_fatal
+#define MM_APPEND(af) (*af)
+
+/* Function prototypes */
+
+void mm_searched (MAILSTREAM *stream,unsigned long number);
+void mm_exists (MAILSTREAM *stream,unsigned long number);
+void mm_expunged (MAILSTREAM *stream,unsigned long number);
+void mm_flags (MAILSTREAM *stream,unsigned long number);
+void mm_notify (MAILSTREAM *stream,char *string,long errflg);
+void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes);
+void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes);
+void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status);
+void mm_log (char *string,long errflg);
+void mm_dlog (char *string);
+void mm_login (NETMBX *mb,char *user,char *pwd,long trial);
+void mm_critical (MAILSTREAM *stream);
+void mm_nocritical (MAILSTREAM *stream);
+long mm_diskerror (MAILSTREAM *stream,long errcode,long serious);
+void mm_fatal (char *string);
+void *mm_cache (MAILSTREAM *stream,unsigned long msgno,long op);
+
+extern STRINGDRIVER mail_string;
+void mail_versioncheck (char *version);
+void mail_link (DRIVER *driver);
+void *mail_parameters (MAILSTREAM *stream,long function,void *value);
+DRIVER *mail_valid (MAILSTREAM *stream,char *mailbox,char *purpose);
+DRIVER *mail_valid_net (char *name,DRIVER *drv,char *host,char *mailbox);
+long mail_valid_net_parse (char *name,NETMBX *mb);
+long mail_valid_net_parse_work (char *name,NETMBX *mb,char *service);
+void mail_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void mail_list (MAILSTREAM *stream,char *ref,char *pat);
+void mail_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long mail_subscribe (MAILSTREAM *stream,char *mailbox);
+long mail_unsubscribe (MAILSTREAM *stream,char *mailbox);
+long mail_create (MAILSTREAM *stream,char *mailbox);
+long mail_delete (MAILSTREAM *stream,char *mailbox);
+long mail_rename (MAILSTREAM *stream,char *old,char *newname);
+char *mail_utf7_valid (char *mailbox);
+long mail_status (MAILSTREAM *stream,char *mbx,long flags);
+long mail_status_default (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *mail_open (MAILSTREAM *stream,char *name,long options);
+MAILSTREAM *mail_open_work (DRIVER *d,MAILSTREAM *stream,char *name,
+ long options);
+MAILSTREAM *mail_close_full (MAILSTREAM *stream,long options);
+MAILHANDLE *mail_makehandle (MAILSTREAM *stream);
+void mail_free_handle (MAILHANDLE **handle);
+MAILSTREAM *mail_stream (MAILHANDLE *handle);
+
+void mail_fetch_fast (MAILSTREAM *stream,char *sequence,long flags);
+void mail_fetch_flags (MAILSTREAM *stream,char *sequence,long flags);
+void mail_fetch_overview (MAILSTREAM *stream,char *sequence,overview_t ofn);
+void mail_fetch_overview_sequence (MAILSTREAM *stream,char *sequence,
+ overview_t ofn);
+void mail_fetch_overview_default (MAILSTREAM *stream,overview_t ofn);
+ENVELOPE *mail_fetch_structure (MAILSTREAM *stream,unsigned long msgno,
+ BODY **body,long flags);
+char *mail_fetch_message (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *len,long flags);
+char *mail_fetch_header (MAILSTREAM *stream,unsigned long msgno,char *section,
+ STRINGLIST *lines,unsigned long *len,long flags);
+char *mail_fetch_text (MAILSTREAM *stream,unsigned long msgno,char *section,
+ unsigned long *len,long flags);
+char *mail_fetch_mime (MAILSTREAM *stream,unsigned long msgno,char *section,
+ unsigned long *len,long flags);
+char *mail_fetch_body (MAILSTREAM *stream,unsigned long msgno,char *section,
+ unsigned long *len,long flags);
+long mail_partial_text (MAILSTREAM *stream,unsigned long msgno,char *section,
+ unsigned long first,unsigned long last,long flags);
+long mail_partial_body (MAILSTREAM *stream,unsigned long msgno,char *section,
+ unsigned long first,unsigned long last,long flags);
+char *mail_fetch_text_return (GETS_DATA *md,SIZEDTEXT *t,unsigned long *len);
+char *mail_fetch_string_return (GETS_DATA *md,STRING *bs,unsigned long i,
+ unsigned long *len,long flags);
+long mail_read (void *stream,unsigned long size,char *buffer);
+unsigned long mail_uid (MAILSTREAM *stream,unsigned long msgno);
+unsigned long mail_msgno (MAILSTREAM *stream,unsigned long uid);
+void mail_fetchfrom (char *s,MAILSTREAM *stream,unsigned long msgno,
+ long length);
+void mail_fetchsubject (char *s,MAILSTREAM *stream,unsigned long msgno,
+ long length);
+MESSAGECACHE *mail_elt (MAILSTREAM *stream,unsigned long msgno);
+void mail_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+long mail_search_full (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,
+ long flags);
+long mail_search_default (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,
+ long flags);
+long mail_ping (MAILSTREAM *stream);
+void mail_check (MAILSTREAM *stream);
+long mail_expunge_full (MAILSTREAM *stream,char *sequence,long options);
+long mail_copy_full (MAILSTREAM *stream,char *sequence,char *mailbox,
+ long options);
+long mail_append_full (MAILSTREAM *stream,char *mailbox,char *flags,char *date,
+ STRING *message);
+long mail_append_multiple (MAILSTREAM *stream,char *mailbox,append_t af,
+ void *data);
+void mail_gc (MAILSTREAM *stream,long gcflags);
+void mail_gc_msg (MESSAGE *msg,long gcflags);
+void mail_gc_body (BODY *body);
+
+BODY *mail_body (MAILSTREAM *stream,unsigned long msgno,
+ unsigned char *section);
+char *mail_date (char *string,MESSAGECACHE *elt);
+char *mail_cdate (char *string,MESSAGECACHE *elt);
+long mail_parse_date (MESSAGECACHE *elt,unsigned char *string);
+void mail_exists (MAILSTREAM *stream,unsigned long nmsgs);
+void mail_recent (MAILSTREAM *stream,unsigned long recent);
+void mail_expunged (MAILSTREAM *stream,unsigned long msgno);
+void mail_lock (MAILSTREAM *stream);
+void mail_unlock (MAILSTREAM *stream);
+void mail_debug (MAILSTREAM *stream);
+void mail_nodebug (MAILSTREAM *stream);
+void mail_dlog (char *string,long flag);
+long mail_match_lines (STRINGLIST *lines,STRINGLIST *msglines,long flags);
+unsigned long mail_filter (char *text,unsigned long len,STRINGLIST *lines,
+ long flags);
+long mail_search_msg (MAILSTREAM *stream,unsigned long msgno,char *section,
+ SEARCHPGM *pgm);
+long mail_search_header_text (char *s,STRINGLIST *st);
+long mail_search_header (SIZEDTEXT *hdr,STRINGLIST *st);
+long mail_search_text (MAILSTREAM *stream,unsigned long msgno,char *section,
+ STRINGLIST *st,long flags);
+long mail_search_body (MAILSTREAM *stream,unsigned long msgno,BODY *body,
+ char *prefix,unsigned long section,long flags);
+long mail_search_string (SIZEDTEXT *s,char *charset,STRINGLIST **st);
+long mail_search_string_work (SIZEDTEXT *s,STRINGLIST **st);
+long mail_search_keyword (MAILSTREAM *stream,MESSAGECACHE *elt,STRINGLIST *st,
+ long flag);
+long mail_search_addr (ADDRESS *adr,STRINGLIST *st);
+char *mail_search_gets (readfn_t f,void *stream,unsigned long size,
+ GETS_DATA *md);
+SEARCHPGM *mail_criteria (char *criteria);
+int mail_criteria_date (unsigned short *date,char **r);
+int mail_criteria_string (STRINGLIST **s,char **r);
+unsigned short mail_shortdate (unsigned int year,unsigned int month,
+ unsigned int day);
+SEARCHSET *mail_parse_set (char *s,char **ret);
+SEARCHSET *mail_append_set (SEARCHSET *set,unsigned long msgno);
+unsigned long *mail_sort (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
+ SORTPGM *pgm,long flags);
+unsigned long *mail_sort_cache (MAILSTREAM *stream,SORTPGM *pgm,SORTCACHE **sc,
+ long flags);
+unsigned long *mail_sort_msgs (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
+ SORTPGM *pgm,long flags);
+SORTCACHE **mail_sort_loadcache (MAILSTREAM *stream,SORTPGM *pgm);
+unsigned int mail_strip_subject (char *t,char **ret);
+char *mail_strip_subject_wsp (char *s);
+char *mail_strip_subject_blob (char *s);
+int mail_sort_compare (const void *a1,const void *a2);
+unsigned long mail_longdate (MESSAGECACHE *elt);
+THREADNODE *mail_thread (MAILSTREAM *stream,char *type,char *charset,
+ SEARCHPGM *spg,long flags);
+THREADNODE *mail_thread_msgs (MAILSTREAM *stream,char *type,char *charset,
+ SEARCHPGM *spg,long flags,sorter_t sorter);
+THREADNODE *mail_thread_orderedsubject (MAILSTREAM *stream,char *charset,
+ SEARCHPGM *spg,long flags,
+ sorter_t sorter);
+THREADNODE *mail_thread_references (MAILSTREAM *stream,char *charset,
+ SEARCHPGM *spg,long flags,
+ sorter_t sorter);
+void mail_thread_loadcache (MAILSTREAM *stream,unsigned long uid,OVERVIEW *ov,
+ unsigned long msgno);
+char *mail_thread_parse_msgid (char *s,char **ss);
+STRINGLIST *mail_thread_parse_references (char *s,long flag);
+long mail_thread_check_child (container_t mother,container_t daughter);
+container_t mail_thread_prune_dummy (container_t msg,container_t ane);
+container_t mail_thread_prune_dummy_work (container_t msg,container_t ane);
+THREADNODE *mail_thread_c2node (MAILSTREAM *stream,container_t con,long flags);
+THREADNODE *mail_thread_sort (THREADNODE *thr,THREADNODE **tc);
+int mail_thread_compare_date (const void *a1,const void *a2);
+long mail_sequence (MAILSTREAM *stream,unsigned char *sequence);
+long mail_uid_sequence (MAILSTREAM *stream,unsigned char *sequence);
+long mail_parse_flags (MAILSTREAM *stream,char *flag,unsigned long *uf);
+long mail_usable_network_stream (MAILSTREAM *stream,char *name);
+
+MESSAGECACHE *mail_new_cache_elt (unsigned long msgno);
+ENVELOPE *mail_newenvelope (void);
+ADDRESS *mail_newaddr (void);
+BODY *mail_newbody (void);
+BODY *mail_initbody (BODY *body);
+PARAMETER *mail_newbody_parameter (void);
+PART *mail_newbody_part (void);
+MESSAGE *mail_newmsg (void);
+STRINGLIST *mail_newstringlist (void);
+SEARCHPGM *mail_newsearchpgm (void);
+SEARCHHEADER *mail_newsearchheader (char *line,char *text);
+SEARCHSET *mail_newsearchset (void);
+SEARCHOR *mail_newsearchor (void);
+SEARCHPGMLIST *mail_newsearchpgmlist (void);
+SORTPGM *mail_newsortpgm (void);
+THREADNODE *mail_newthreadnode (SORTCACHE *sc);
+ACLLIST *mail_newacllist (void);
+QUOTALIST *mail_newquotalist (void);
+void mail_free_body (BODY **body);
+void mail_free_body_data (BODY *body);
+void mail_free_body_parameter (PARAMETER **parameter);
+void mail_free_body_part (PART **part);
+void mail_free_cache (MAILSTREAM *stream);
+void mail_free_elt (MESSAGECACHE **elt);
+void mail_free_envelope (ENVELOPE **env);
+void mail_free_address (ADDRESS **address);
+void mail_free_stringlist (STRINGLIST **string);
+void mail_free_searchpgm (SEARCHPGM **pgm);
+void mail_free_searchheader (SEARCHHEADER **hdr);
+void mail_free_searchset (SEARCHSET **set);
+void mail_free_searchor (SEARCHOR **orl);
+void mail_free_searchpgmlist (SEARCHPGMLIST **pgl);
+void mail_free_namespace (NAMESPACE **n);
+void mail_free_sortpgm (SORTPGM **pgm);
+void mail_free_threadnode (THREADNODE **thr);
+void mail_free_acllist (ACLLIST **al);
+void mail_free_quotalist (QUOTALIST **ql);
+void auth_link (AUTHENTICATOR *auth);
+char *mail_auth (char *mechanism,authresponse_t resp,int argc,char *argv[]);
+AUTHENTICATOR *mail_lookup_auth (unsigned long i);
+unsigned int mail_lookup_auth_name (char *mechanism,long flags);
+
+NETSTREAM *net_open (NETMBX *mb,NETDRIVER *dv,unsigned long port,
+ NETDRIVER *ssld,char *ssls,unsigned long sslp);
+NETSTREAM *net_open_work (NETDRIVER *dv,char *host,char *service,
+ unsigned long port,unsigned long portoverride,
+ unsigned long flags);
+NETSTREAM *net_aopen (NETDRIVER *dv,NETMBX *mb,char *service,char *usrbuf);
+char *net_getline (NETSTREAM *stream);
+ /* stream must be void* for use as readfn_t */
+long net_getbuffer (void *stream,unsigned long size,char *buffer);
+long net_soutr (NETSTREAM *stream,char *string);
+long net_sout (NETSTREAM *stream,char *string,unsigned long size);
+void net_close (NETSTREAM *stream);
+char *net_host (NETSTREAM *stream);
+char *net_remotehost (NETSTREAM *stream);
+unsigned long net_port (NETSTREAM *stream);
+char *net_localhost (NETSTREAM *stream);
+
+long sm_subscribe (char *mailbox);
+long sm_unsubscribe (char *mailbox);
+char *sm_read (void **sdb);
+
+void ssl_onceonlyinit (void);
+char *ssl_start_tls (char *s);
+void ssl_server_init (char *server);
+
+
+/* Server I/O functions */
+
+int PBIN (void);
+char *PSIN (char *s,int n);
+long PSINR (char *s,unsigned long n);
+int PBOUT (int c);
+long INWAIT (long seconds);
+int PSOUT (char *s);
+int PSOUTR (SIZEDTEXT *s);
+int PFLUSH (void);
diff --git a/imap/src/c-client/misc.c b/imap/src/c-client/misc.c
new file mode 100644
index 00000000..4b789af8
--- /dev/null
+++ b/imap/src/c-client/misc.c
@@ -0,0 +1,475 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Miscellaneous utility routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 5 July 1988
+ * Last Edited: 6 December 2006
+ *
+ * This original version of this file is
+ * Copyright 1988 Stanford University
+ * and was developed in the Symbolic Systems Resources Group of the Knowledge
+ * Systems Laboratory at Stanford University in 1987-88, and was funded by the
+ * Biomedical Research Technology Program of the NationalInstitutes of Health
+ * under grant number RR-00785.
+ */
+
+
+#include <ctype.h>
+#include "c-client.h"
+
+/* Convert ASCII string to all uppercase
+ * Accepts: string pointer
+ * Returns: string pointer
+ *
+ * Don't use islower/toupper since this function must be ASCII only.
+ */
+
+unsigned char *ucase (unsigned char *s)
+{
+ unsigned char *t;
+ /* if lowercase covert to upper */
+ for (t = s; *t; t++) if ((*t >= 'a') && (*t <= 'z')) *t -= ('a' - 'A');
+ return s; /* return string */
+}
+
+
+/* Convert string to all lowercase
+ * Accepts: string pointer
+ * Returns: string pointer
+ *
+ * Don't use isupper/tolower since this function must be ASCII only.
+ */
+
+unsigned char *lcase (unsigned char *s)
+{
+ unsigned char *t;
+ /* if uppercase covert to lower */
+ for (t = s; *t; t++) if ((*t >= 'A') && (*t <= 'Z')) *t += ('a' - 'A');
+ return s; /* return string */
+}
+
+/* Copy string to free storage
+ * Accepts: source string
+ * Returns: free storage copy of string
+ */
+
+char *cpystr (const char *string)
+{
+ return string ? strcpy ((char *) fs_get (1 + strlen (string)),string) : NIL;
+}
+
+
+/* Copy text/size to free storage as sized text
+ * Accepts: destination sized text
+ * pointer to source text
+ * size of source text
+ * Returns: text as a char *
+ */
+
+char *cpytxt (SIZEDTEXT *dst,char *text,unsigned long size)
+{
+ /* flush old space */
+ if (dst->data) fs_give ((void **) &dst->data);
+ /* copy data in sized text */
+ memcpy (dst->data = (unsigned char *)
+ fs_get ((size_t) (dst->size = size) + 1),text,(size_t) size);
+ dst->data[size] = '\0'; /* tie off text */
+ return (char *) dst->data; /* convenience return */
+}
+
+/* Copy sized text to free storage as sized text
+ * Accepts: destination sized text
+ * source sized text
+ * Returns: text as a char *
+ */
+
+char *textcpy (SIZEDTEXT *dst,SIZEDTEXT *src)
+{
+ /* flush old space */
+ if (dst->data) fs_give ((void **) &dst->data);
+ /* copy data in sized text */
+ memcpy (dst->data = (unsigned char *)
+ fs_get ((size_t) (dst->size = src->size) + 1),
+ src->data,(size_t) src->size);
+ dst->data[dst->size] = '\0'; /* tie off text */
+ return (char *) dst->data; /* convenience return */
+}
+
+
+/* Copy stringstruct to free storage as sized text
+ * Accepts: destination sized text
+ * source stringstruct
+ * Returns: text as a char *
+ */
+
+char *textcpystring (SIZEDTEXT *text,STRING *bs)
+{
+ unsigned long i = 0;
+ /* clear old space */
+ if (text->data) fs_give ((void **) &text->data);
+ /* make free storage space in sized text */
+ text->data = (unsigned char *) fs_get ((size_t) (text->size = SIZE (bs)) +1);
+ while (i < text->size) text->data[i++] = SNX (bs);
+ text->data[i] = '\0'; /* tie off text */
+ return (char *) text->data; /* convenience return */
+}
+
+
+/* Copy stringstruct from offset to free storage as sized text
+ * Accepts: destination sized text
+ * source stringstruct
+ * offset into stringstruct
+ * size of source text
+ * Returns: text as a char *
+ */
+
+char *textcpyoffstring (SIZEDTEXT *text,STRING *bs,unsigned long offset,
+ unsigned long size)
+{
+ unsigned long i = 0;
+ /* clear old space */
+ if (text->data) fs_give ((void **) &text->data);
+ SETPOS (bs,offset); /* offset the string */
+ /* make free storage space in sized text */
+ text->data = (unsigned char *) fs_get ((size_t) (text->size = size) + 1);
+ while (i < size) text->data[i++] = SNX (bs);
+ text->data[i] = '\0'; /* tie off text */
+ return (char *) text->data; /* convenience return */
+}
+
+/* Returns index of rightmost bit in word
+ * Accepts: pointer to a 32 bit value
+ * Returns: -1 if word is 0, else index of rightmost bit
+ *
+ * Bit is cleared in the word
+ */
+
+unsigned long find_rightmost_bit (unsigned long *valptr)
+{
+ unsigned long value = *valptr;
+ unsigned long bit = 0;
+ if (!(value & 0xffffffff)) return 0xffffffff;
+ /* binary search for rightmost bit */
+ if (!(value & 0xffff)) value >>= 16, bit += 16;
+ if (!(value & 0xff)) value >>= 8, bit += 8;
+ if (!(value & 0xf)) value >>= 4, bit += 4;
+ if (!(value & 0x3)) value >>= 2, bit += 2;
+ if (!(value & 0x1)) value >>= 1, bit += 1;
+ *valptr ^= (1 << bit); /* clear specified bit */
+ return bit;
+}
+
+
+/* Return minimum of two integers
+ * Accepts: integer 1
+ * integer 2
+ * Returns: minimum
+ */
+
+long min (long i,long j)
+{
+ return ((i < j) ? i : j);
+}
+
+
+/* Return maximum of two integers
+ * Accepts: integer 1
+ * integer 2
+ * Returns: maximum
+ */
+
+long max (long i,long j)
+{
+ return ((i > j) ? i : j);
+}
+
+/* Search, case-insensitive for ASCII characters
+ * Accepts: base string
+ * length of base string
+ * pattern string
+ * length of pattern string
+ * Returns: T if pattern exists inside base, else NIL
+ */
+
+long search (unsigned char *base,long basec,unsigned char *pat,long patc)
+{
+ long i,j,k;
+ int c;
+ unsigned char mask[256];
+ static unsigned char alphatab[256] = {
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,223,223,223,223,223,223,223,223,223,223,223,223,223,223,223,
+ 223,223,223,223,223,223,223,223,223,223,223,255,255,255,255,255,
+ 255,223,223,223,223,223,223,223,223,223,223,223,223,223,223,223,
+ 223,223,223,223,223,223,223,223,223,223,223,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
+ };
+ /* validate arguments */
+ if (base && (basec > 0) && pat && (basec >= patc)) {
+ if (patc <= 0) return T; /* empty pattern always succeeds */
+ memset (mask,0,256); /* initialize search validity mask */
+ for (i = 0; i < patc; i++) if (!mask[c = pat[i]]) {
+ /* mark single character if non-alphabetic */
+ if (alphatab[c] & 0x20) mask[c] = T;
+ /* else mark both cases */
+ else mask[c & 0xdf] = mask[c | 0x20] = T;
+ }
+ /* Boyer-Moore type search */
+ for (i = --patc; i < basec; i += (mask[c] ? 1 : (j + 1)))
+ for (j = patc,c = base[k = i]; !((c ^ pat[j]) & alphatab[c]);
+ j--,c = base[--k])
+ if (!j) return T; /* found a match! */
+ }
+ return NIL; /* pattern not found */
+}
+
+/* Boyer-Moore string search
+ * Accepts: base string
+ * length of base string
+ * pattern string
+ * length of pattern string
+ * Returns: T if pattern exists inside base, else NIL
+ */
+
+long ssearch (unsigned char *base,long basec,unsigned char *pat,long patc)
+{
+ long i,j,k;
+ int c;
+ unsigned char mask[256];
+ /* validate arguments */
+ if (base && (basec > 0) && pat && (basec >= patc)) {
+ if (patc <= 0) return T; /* empty pattern always succeeds */
+ memset (mask,0,256); /* initialize search validity mask */
+ for (i = 0; i < patc; i++) mask[pat[i]] = T;
+ /* Boyer-Moore type search */
+ for (i = --patc, c = pat[i]; i < basec; i += (mask[c] ? 1 : (j + 1)))
+ for (j = patc,c = base[k = i]; (c == pat[j]); j--,c = base[--k])
+ if (!j) return T; /* found a match! */
+ }
+ return NIL; /* pattern not found */
+}
+
+/* Create a hash table
+ * Accepts: size of new table (note: should be a prime)
+ * Returns: hash table
+ */
+
+HASHTAB *hash_create (size_t size)
+{
+ size_t i = sizeof (size_t) + size * sizeof (HASHENT *);
+ HASHTAB *ret = (HASHTAB *) memset (fs_get (i),0,i);
+ ret->size = size;
+ return ret;
+}
+
+
+/* Destroy hash table
+ * Accepts: hash table
+ */
+
+void hash_destroy (HASHTAB **hashtab)
+{
+ if (*hashtab) {
+ hash_reset (*hashtab); /* reset hash table */
+ fs_give ((void **) hashtab);
+ }
+}
+
+
+/* Reset hash table
+ * Accepts: hash table
+ */
+
+void hash_reset (HASHTAB *hashtab)
+{
+ size_t i;
+ HASHENT *ent,*nxt;
+ /* free each hash entry */
+ for (i = 0; i < hashtab->size; i++) if (ent = hashtab->table[i])
+ for (hashtab->table[i] = NIL; ent; ent = nxt) {
+ nxt = ent->next; /* get successor */
+ fs_give ((void **) &ent); /* flush this entry */
+ }
+}
+
+/* Calculate index into hash table
+ * Accepts: hash table
+ * entry name
+ * Returns: index
+ */
+
+unsigned long hash_index (HASHTAB *hashtab,char *key)
+{
+ unsigned long i,ret;
+ /* polynomial of letters of the word */
+ for (ret = 0; i = (unsigned int) *key++; ret += i) ret *= HASHMULT;
+ return ret % (unsigned long) hashtab->size;
+}
+
+
+/* Look up name in hash table
+ * Accepts: hash table
+ * key
+ * Returns: associated data
+ */
+
+void **hash_lookup (HASHTAB *hashtab,char *key)
+{
+ HASHENT *ret;
+ for (ret = hashtab->table[hash_index (hashtab,key)]; ret; ret = ret->next)
+ if (!strcmp (key,ret->name)) return ret->data;
+ return NIL;
+}
+
+/* Add entry to hash table
+ * Accepts: hash table
+ * key
+ * associated data
+ * number of extra data slots
+ * Returns: hash entry
+ * Caller is responsible for ensuring that entry isn't already in table
+ */
+
+HASHENT *hash_add (HASHTAB *hashtab,char *key,void *data,long extra)
+{
+ unsigned long i = hash_index (hashtab,key);
+ size_t j = sizeof (HASHENT) + (extra * sizeof (void *));
+ HASHENT *ret = (HASHENT *) memset (fs_get (j),0,j);
+ ret->next = hashtab->table[i];/* insert as new head in this index */
+ ret->name = key; /* set up hash key */
+ ret->data[0] = data; /* and first data */
+ return hashtab->table[i] = ret;
+}
+
+
+/* Look up name in hash table
+ * Accepts: hash table
+ * key
+ * associated data
+ * number of extra data slots
+ * Returns: associated data
+ */
+
+void **hash_lookup_and_add (HASHTAB *hashtab,char *key,void *data,long extra)
+{
+ HASHENT *ret;
+ unsigned long i = hash_index (hashtab,key);
+ size_t j = sizeof (HASHENT) + (extra * sizeof (void *));
+ for (ret = hashtab->table[i]; ret; ret = ret->next)
+ if (!strcmp (key,ret->name)) return ret->data;
+ ret = (HASHENT *) memset (fs_get (j),0,j);
+ ret->next = hashtab->table[i];/* insert as new head in this index */
+ ret->name = key; /* set up hash key */
+ ret->data[0] = data; /* and first data */
+ return (hashtab->table[i] = ret)->data;
+}
+
+/* Convert two hex characters into byte
+ * Accepts: char for high nybble
+ * char for low nybble
+ * Returns: byte
+ *
+ * Arguments must be isxdigit validated
+ */
+
+unsigned char hex2byte (unsigned char c1,unsigned char c2)
+{
+ /* merge the two nybbles */
+ return ((c1 -= (isdigit (c1) ? '0' : ((c1 <= 'Z') ? 'A' : 'a') - 10)) << 4) +
+ (c2 - (isdigit (c2) ? '0' : ((c2 <= 'Z') ? 'A' : 'a') - 10));
+}
+
+
+/* Compare two unsigned longs
+ * Accepts: first value
+ * second value
+ * Returns: -1 if l1 < l2, 0 if l1 == l2, 1 if l1 > l2
+ */
+
+int compare_ulong (unsigned long l1,unsigned long l2)
+{
+ if (l1 < l2) return -1;
+ if (l1 > l2) return 1;
+ return 0;
+}
+
+
+/* Compare two unsigned chars, case-independent
+ * Accepts: first value
+ * second value
+ * Returns: -1 if c1 < c2, 0 if c1 == c2, 1 if c1 > c2
+ *
+ * Don't use isupper/tolower since this function must be ASCII only.
+ */
+
+int compare_uchar (unsigned char c1,unsigned char c2)
+{
+ return compare_ulong (((c1 >= 'A') && (c1 <= 'Z')) ? c1 + ('a' - 'A') : c1,
+ ((c2 >= 'A') && (c2 <= 'Z')) ? c2 + ('a' - 'A') : c2);
+}
+
+/* Compare two case-independent ASCII strings
+ * Accepts: first string
+ * second string
+ * Returns: -1 if s1 < s2, 0 if s1 == s2, 1 if s1 > s2
+ */
+
+int compare_cstring (unsigned char *s1,unsigned char *s2)
+{
+ int i;
+ if (!s1) return s2 ? -1 : 0; /* empty string cases */
+ else if (!s2) return 1;
+ for (; *s1 && *s2; s1++,s2++) if (i = (compare_uchar (*s1,*s2))) return i;
+ if (*s1) return 1; /* first string is longer */
+ return *s2 ? -1 : 0; /* second string longer : strings identical */
+}
+
+
+/* Compare case-independent string with sized text
+ * Accepts: first string
+ * sized text
+ * Returns: -1 if s1 < s2, 0 if s1 == s2, 1 if s1 > s2
+ */
+
+int compare_csizedtext (unsigned char *s1,SIZEDTEXT *s2)
+{
+ int i;
+ unsigned char *s;
+ unsigned long j;
+ if (!s1) return s2 ? -1 : 0; /* null string cases */
+ else if (!s2) return 1;
+ for (s = (char *) s2->data,j = s2->size; *s1 && j; ++s1,++s,--j)
+ if (i = (compare_uchar (*s1,*s))) return i;
+ if (*s1) return 1; /* first string is longer */
+ return j ? -1 : 0; /* second string longer : strings identical */
+}
diff --git a/imap/src/c-client/misc.h b/imap/src/c-client/misc.h
new file mode 100644
index 00000000..1ac79485
--- /dev/null
+++ b/imap/src/c-client/misc.h
@@ -0,0 +1,110 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Miscellaneous utility routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 5 July 1988
+ * Last Edited: 30 August 2006
+ *
+ * This original version of this file is
+ * Copyright 1988 Stanford University
+ * and was developed in the Symbolic Systems Resources Group of the Knowledge
+ * Systems Laboratory at Stanford University in 1987-88, and was funded by the
+ * Biomedical Research Technology Program of the NationalInstitutes of Health
+ * under grant number RR-00785.
+ */
+
+/* Hash table operations */
+
+#define HASHMULT 29 /* hash polynomial multiplier */
+
+#define HASHENT struct hash_entry
+
+HASHENT {
+ HASHENT *next; /* next entry with same hash code */
+ char *name; /* name of this hash entry */
+ void *data[1]; /* data of this hash entry */
+};
+
+
+#define HASHTAB struct hash_table
+
+HASHTAB {
+ size_t size; /* size of this table */
+ HASHENT *table[1]; /* table */
+};
+
+
+/* KLUDGE ALERT!!!
+ *
+ * Yes, write() is overridden here instead of in osdep. This
+ * is because misc.h is one of the last files that most things #include, so
+ * this should avoid problems with some system #include file.
+ */
+
+#define write safe_write
+
+
+/* Some C compilers have these as macros */
+
+#undef min
+#undef max
+
+
+/* And some C libraries have these as int functions */
+
+#define min Min
+#define max Max
+
+
+/* Compatibility definitions */
+
+#define pmatch(s,pat) \
+ pmatch_full (s,pat,NIL)
+
+/* Function prototypes */
+
+unsigned char *ucase (unsigned char *string);
+unsigned char *lcase (unsigned char *string);
+char *cpystr (const char *string);
+char *cpytxt (SIZEDTEXT *dst,char *text,unsigned long size);
+char *textcpy (SIZEDTEXT *dst,SIZEDTEXT *src);
+char *textcpystring (SIZEDTEXT *text,STRING *bs);
+char *textcpyoffstring (SIZEDTEXT *text,STRING *bs,unsigned long offset,
+ unsigned long size);
+unsigned long find_rightmost_bit (unsigned long *valptr);
+long min (long i,long j);
+long max (long i,long j);
+long search (unsigned char *base,long basec,unsigned char *pat,long patc);
+long ssearch (unsigned char *base,long basec,unsigned char *pat,long patc);
+HASHTAB *hash_create (size_t size);
+void hash_destroy (HASHTAB **hashtab);
+void hash_reset (HASHTAB *hashtab);
+unsigned long hash_index (HASHTAB *hashtab,char *key);
+void **hash_lookup (HASHTAB *hashtab,char *key);
+HASHENT *hash_add (HASHTAB *hashtab,char *key,void *data,long extra);
+void **hash_lookup_and_add (HASHTAB *hashtab,char *key,void *data,long extra);
+unsigned char hex2byte (unsigned char c1,unsigned char c2);
+int compare_ulong (unsigned long l1,unsigned long l2);
+int compare_uchar (unsigned char c1,unsigned char c2);
+int compare_cstring (unsigned char *s1,unsigned char *s2);
+int compare_csizedtext (unsigned char *s1,SIZEDTEXT *s2);
diff --git a/imap/src/c-client/netmsg.c b/imap/src/c-client/netmsg.c
new file mode 100644
index 00000000..187e4ebb
--- /dev/null
+++ b/imap/src/c-client/netmsg.c
@@ -0,0 +1,104 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Network message (SMTP/NNTP/POP2/POP3) routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 8 June 1995
+ * Last Edited: 6 December 2006
+ */
+
+
+#include <stdio.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "c-client.h"
+#include "netmsg.h"
+#include "flstring.h"
+
+/* Network message read
+ * Accepts: file
+ * number of bytes to read
+ * buffer address
+ * Returns: T if success, NIL otherwise
+ */
+
+long netmsg_read (void *stream,unsigned long count,char *buffer)
+{
+ return (fread (buffer,(size_t) 1,(size_t) count,(FILE *) stream) == count) ?
+ T : NIL;
+}
+
+/* Slurp dot-terminated text from NET
+ * Accepts: NET stream
+ * place to return size
+ * place to return header size
+ * Returns: file descriptor
+ */
+
+FILE *netmsg_slurp (NETSTREAM *stream,unsigned long *size,unsigned long *hsiz)
+{
+ unsigned long i;
+ char *s,*t,tmp[MAILTMPLEN];
+ FILE *f = tmpfile ();
+ if (!f) {
+ sprintf (tmp,".%lx.%lx",(unsigned long) time (0),(unsigned long)getpid ());
+ if (f = fopen (tmp,"wb+")) unlink (tmp);
+ else {
+ sprintf (tmp,"Unable to create scratch file: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ }
+ *size = 0; /* initially emtpy */
+ if (hsiz) *hsiz = 0;
+ while (s = net_getline (stream)) {
+ if (*s == '.') { /* possible end of text? */
+ if (s[1]) t = s + 1; /* pointer to true start of line */
+ else {
+ fs_give ((void **) &s); /* free the line */
+ break; /* end of data */
+ }
+ }
+ else t = s; /* want the entire line */
+ if (f) { /* copy it to the file */
+ i = strlen (t); /* size of line */
+ if ((fwrite (t,(size_t) 1,(size_t) i,f) == i) &&
+ (fwrite ("\015\012",(size_t) 1,(size_t) 2,f) == 2)) {
+ *size += i + 2; /* tally up size of data */
+ /* note header position */
+ if (!i && hsiz && !*hsiz) *hsiz = *size;
+ }
+ else {
+ sprintf (tmp,"Error writing scratch file at byte %lu",*size);
+ MM_LOG (tmp,ERROR);
+ fclose (f); /* forget it */
+ f = NIL; /* failure now */
+ }
+ }
+ fs_give ((void **) &s); /* free the line */
+ }
+ /* if making a file, rewind to start of file */
+ if (f) fseek (f,(unsigned long) 0,SEEK_SET);
+ /* header consumes entire message */
+ if (hsiz && !*hsiz) *hsiz = *size;
+ return f; /* return the file descriptor */
+}
diff --git a/imap/src/c-client/netmsg.h b/imap/src/c-client/netmsg.h
new file mode 100644
index 00000000..bacff576
--- /dev/null
+++ b/imap/src/c-client/netmsg.h
@@ -0,0 +1,32 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Network message (SMTP/NNTP/POP2/POP3) routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 8 June 1995
+ * Last Edited: 30 August 2006
+ */
+
+
+ /* stream must be void* for use as readfn_t */
+long netmsg_read (void *stream,unsigned long count,char *buffer);
+FILE *netmsg_slurp (NETSTREAM *stream,unsigned long *size,unsigned long *hsiz);
diff --git a/imap/src/c-client/newsrc.c b/imap/src/c-client/newsrc.c
new file mode 100644
index 00000000..41f1fd24
--- /dev/null
+++ b/imap/src/c-client/newsrc.c
@@ -0,0 +1,510 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Newsrc manipulation routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 12 September 1994
+ * Last Edited: 30 August 2006
+ */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include "c-client.h"
+#include "newsrc.h"
+
+#ifndef OLDFILESUFFIX
+#define OLDFILESUFFIX ".old"
+#endif
+
+/* Error message
+ * Accepts: message format
+ * additional message string
+ * message level
+ * Returns: NIL, always
+ */
+
+long newsrc_error (char *fmt,char *text,long errflg)
+{
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,fmt,text);
+ MM_LOG (tmp,errflg);
+ return NIL;
+}
+
+
+/* Write error message
+ * Accepts: newsrc name
+ * file designator
+ * file designator
+ * Returns: NIL, always
+ */
+
+long newsrc_write_error (char *name,FILE *f1,FILE *f2)
+{
+ if (f1) fclose (f1); /* close file designators */
+ if (f2) fclose (f2);
+ return newsrc_error ("Error writing to %.80s",name,ERROR);
+}
+
+
+/* Create newsrc file in local form
+ * Accepts: MAIL stream
+ * notification flag
+ * Returns: file designator of newsrc
+ */
+
+FILE *newsrc_create (MAILSTREAM *stream,int notify)
+{
+ char *newsrc = (char *) mail_parameters (stream,GET_NEWSRC,stream);
+ FILE *f = fopen (newsrc,"wb");
+ if (!f) newsrc_error ("Unable to create news state %.80s",newsrc,ERROR);
+ else if (notify) newsrc_error ("Creating news state %.80s",newsrc,WARN);
+ return f;
+}
+
+/* Write new state in newsrc
+ * Accepts: file designator of newsrc
+ * group
+ * new subscription status character
+ * newline convention
+ * Returns: T if successful, NIL otherwise
+ */
+
+long newsrc_newstate (FILE *f,char *group,char state,char *nl)
+{
+ long ret = (f && (fputs (group,f) != EOF) && ((putc (state,f)) != EOF) &&
+ ((putc (' ',f)) != EOF) && (fputs (nl,f) != EOF)) ? LONGT : NIL;
+ if (fclose (f) == EOF) ret = NIL;
+ return ret;
+}
+
+
+/* Write messages in newsrc
+ * Accepts: file designator of newsrc
+ * MAIL stream
+ * message number/newsgroup message map
+ * newline convention
+ * Returns: T if successful, NIL otherwise
+ */
+
+long newsrc_newmessages (FILE *f,MAILSTREAM *stream,char *nl)
+{
+ unsigned long i,j,k;
+ char tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ int c = ' ';
+ if (stream->nmsgs) { /* have any messages? */
+ for (i = 1,j = k = (mail_elt (stream,i)->private.uid > 1) ? 1 : 0;
+ i <= stream->nmsgs; ++i) {
+ /* deleted message? */
+ if ((elt = mail_elt (stream,i))->deleted) {
+ k = elt->private.uid; /* this is the top of the current range */
+ if (!j) j = k; /* if no range in progress, start one */
+ }
+ else if (j) { /* unread message, ending a range */
+ /* calculate end of range */
+ if (k = elt->private.uid - 1) {
+ /* dump range */
+ sprintf (tmp,(j == k) ? "%c%ld" : "%c%ld-%ld",c,j,k);
+ if (fputs (tmp,f) == EOF) return NIL;
+ c = ','; /* need a comma after the first time */
+ }
+ j = 0; /* no more range in progress */
+ }
+ }
+ if (j) { /* dump trailing range */
+ sprintf (tmp,(j == k) ? "%c%ld" : "%c%ld-%ld",c,j,k);
+ if (fputs (tmp,f) == EOF) return NIL;
+ }
+ }
+ /* write trailing newline, return */
+ return (fputs (nl,f) == EOF) ? NIL : LONGT;
+}
+
+/* List subscribed newsgroups
+ * Accepts: MAIL stream
+ * prefix to append name
+ * pattern to search
+ */
+
+void newsrc_lsub (MAILSTREAM *stream,char *pattern)
+{
+ char *s,*t,*lcl,name[MAILTMPLEN];
+ int c = ' ';
+ int showuppers = pattern[strlen (pattern) - 1] == '%';
+ FILE *f = fopen ((char *) mail_parameters (stream,GET_NEWSRC,stream),"rb");
+ if (f) { /* got file? */
+ /* remote name? */
+ if (*(lcl = strcpy (name,pattern)) == '{') lcl = strchr (lcl,'}') + 1;
+ if (*lcl == '#') lcl += 6; /* namespace format name? */
+ while (c != EOF) { /* yes, read newsrc */
+ for (s = lcl; (s < (name + MAILTMPLEN - 1)) && ((c = getc (f)) != EOF) &&
+ (c != ':') && (c != '!') && (c != '\015') && (c != '\012');
+ *s++ = c);
+ if (c == ':') { /* found a subscribed newsgroup? */
+ *s = '\0'; /* yes, tie off name */
+ /* report if match */
+ if (pmatch_full (name,pattern,'.')) mm_lsub (stream,'.',name,NIL);
+ else while (showuppers && (t = strrchr (lcl,'.'))) {
+ *t = '\0'; /* tie off the name */
+ if (pmatch_full (name,pattern,'.'))
+ mm_lsub (stream,'.',name,LATT_NOSELECT);
+ }
+ }
+ while ((c != '\015') && (c != '\012') && (c != EOF)) c = getc (f);
+ }
+ fclose (f);
+ }
+}
+
+/* Update subscription status of newsrc
+ * Accepts: MAIL stream
+ * group
+ * new subscription status character
+ * Returns: T if successful, NIL otherwise
+ */
+
+long newsrc_update (MAILSTREAM *stream,char *group,char state)
+{
+ char tmp[MAILTMPLEN];
+ char *newsrc = (char *) mail_parameters (stream,GET_NEWSRC,stream);
+ long ret = NIL;
+ FILE *f = fopen (newsrc,"r+b");
+ if (f) { /* update existing file */
+ int c = 0;
+ char *s,nl[3];
+ long pos = 0;
+ nl[0] = nl[1] = nl[2]='\0'; /* no newline known yet */
+ do { /* read newsrc */
+ for (s = tmp; (s < (tmp + MAILTMPLEN - 1)) && ((c = getc (f)) != EOF) &&
+ (c != ':') && (c != '!') && (c != '\015') && (c != '\012');
+ *s++ = c) pos = ftell (f);
+ *s = '\0'; /* tie off name */
+ /* found the newsgroup? */
+ if (((c == ':') || (c == '!')) && !strcmp (tmp,group)) {
+ if (c == state) { /* already at that state? */
+ if (c == ':') newsrc_error("Already subscribed to %.80s",group,WARN);
+ ret = LONGT; /* noop the update */
+ }
+ /* write the character */
+ else if (!fseek (f,pos,0)) ret = ((putc (state,f)) == EOF) ? NIL:LONGT;
+ if (fclose (f) == EOF) ret = NIL;
+ f = NIL; /* done with file */
+ break;
+ }
+ /* gobble remainder of this line */
+ while ((c != '\015') && (c != '\012') && (c != EOF)) c = getc (f);
+ /* need to know about newlines and found it? */
+ if (!nl[0] && ((c == '\015') || (c == '\012')) && ((nl[0]=c) == '\015')){
+ /* sniff and see if an LF */
+ if ((c = getc (f)) == '\012') nl[1] = c;
+ else ungetc (c,f); /* nope, push it back */
+ }
+ } while (c != EOF);
+
+ if (f) { /* still haven't written it yet? */
+ if (nl[0]) { /* know its newline convention? */
+ fseek (f,0L,2); /* yes, seek to end of file */
+ ret = newsrc_newstate (f,group,state,nl);
+ }
+ else { /* can't find a newline convention */
+ fclose (f); /* punt the file */
+ /* can't win if read something */
+ if (pos) newsrc_error ("Unknown newline convention in %.80s",
+ newsrc,ERROR);
+ /* file must have been empty, rewrite it */
+ else ret = newsrc_newstate(newsrc_create(stream,NIL),group,state,"\n");
+ }
+ }
+ }
+ /* create new file */
+ else ret = newsrc_newstate (newsrc_create (stream,T),group,state,"\n");
+ return ret; /* return with update status */
+}
+
+/* Update newsgroup status in stream
+ * Accepts: newsgroup name
+ * MAIL stream
+ * Returns: number of recent messages
+ */
+
+long newsrc_read (char *group,MAILSTREAM *stream)
+{
+ int c = 0;
+ char *s,tmp[MAILTMPLEN];
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ unsigned long m = 1,recent = 0,unseen = 0;
+ FILE *f = fopen ((char *) mail_parameters (stream,GET_NEWSRC,stream),"rb");
+ if (f) do { /* read newsrc */
+ for (s = tmp; (s < (tmp + MAILTMPLEN - 1)) && ((c = getc (f)) != EOF) &&
+ (c != ':') && (c != '!') && (c != '\015') && (c != '\012'); *s++ = c);
+ *s = '\0'; /* tie off name */
+ if ((c==':') || (c=='!')) { /* found newsgroup? */
+ if (strcmp (tmp,group)) /* group name match? */
+ while ((c != '\015') && (c != '\012') && (c != EOF)) c = getc (f);
+ else { /* yes, skip leading whitespace */
+ while ((c = getc (f)) == ' ');
+ /* only if unprocessed messages */
+ if (stream->nmsgs) while (f && (m <= stream->nmsgs)) {
+ /* collect a number */
+ if (isdigit (c)) { /* better have a number */
+ for (i = 0,j = 0; isdigit (c); c = getc (f)) i = i*10 + (c-'0');
+ if (c == '-') for (c = getc (f); isdigit (c); c = getc (f))
+ j = j*10 +(c-'0');/* collect second value if range */
+ if (!unseen && (mail_elt (stream,m)->private.uid < i)) unseen = m;
+ /* skip messages before first value */
+ while ((m <= stream->nmsgs) &&
+ ((elt = mail_elt (stream,m))->private.uid < i) && m++)
+ elt->valid = T;
+ /* do all messages in range */
+ while ((m <= stream->nmsgs) && (elt = mail_elt (stream,m)) &&
+ (j ? ((elt->private.uid >= i) && (elt->private.uid <= j)) :
+ (elt->private.uid == i)) && m++)
+ elt->valid = elt->deleted = T;
+ }
+
+ switch (c) { /* what is the delimiter? */
+ case ',': /* more to come */
+ c = getc (f); /* get first character of number */
+ break;
+ default: /* bogus character */
+ sprintf (tmp,"Bogus character 0x%x in news state",(unsigned int)c);
+ MM_LOG (tmp,ERROR);
+ case EOF: case '\015': case '\012':
+ fclose (f); /* all done - close the file */
+ f = NIL;
+ break;
+ }
+ }
+ else { /* empty newsgroup */
+ while ((c != '\015') && (c != '\012') && (c != EOF)) c = getc (f);
+ fclose (f); /* all done - close the file */
+ f = NIL;
+ }
+ }
+ }
+ } while (f && (c != EOF)); /* until file closed or EOF */
+ if (f) { /* still have file open? */
+ sprintf (tmp,"No state for newsgroup %.80s found, reading as new",group);
+ MM_LOG (tmp,WARN);
+ fclose (f); /* close the file */
+ }
+ if (m <= stream->nmsgs) { /* any messages beyond newsrc range? */
+ if (!unseen) unseen = m; /* then this must be the first unseen one */
+ do {
+ elt = mail_elt (stream,m++);
+ elt->valid = elt->recent = T;
+ ++recent; /* count another recent message */
+ }
+ while (m <= stream->nmsgs);
+ }
+ if (unseen) { /* report first unseen message */
+ sprintf (tmp,"[UNSEEN] %lu is first unseen message in %.80s",unseen,group);
+ MM_NOTIFY (stream,tmp,(long) NIL);
+ }
+ return recent;
+}
+
+/* Update newsgroup entry in newsrc
+ * Accepts: newsgroup name
+ * MAIL stream
+ * Returns: T if successful, NIL otherwise
+ */
+
+long newsrc_write (char *group,MAILSTREAM *stream)
+{
+ long ret = NIL;
+ int c = 0,d = EOF;
+ char *newsrc = (char *) mail_parameters (stream,GET_NEWSRC,stream);
+ char *s,tmp[MAILTMPLEN],backup[MAILTMPLEN],nl[3];
+ FILE *f,*bf;
+ nl[0] = nl[1] = nl[2] = '\0'; /* no newline known yet */
+ if (f = fopen (newsrc,"rb")) {/* have existing newsrc file? */
+ if (!(bf = fopen ((strcat (strcpy (backup,newsrc),OLDFILESUFFIX)),"wb"))) {
+ fclose (f); /* punt input file */
+ return newsrc_error("Can't create backup news state %.80s",backup,ERROR);
+ }
+ /* copy to backup file */
+ while ((c = getc (f)) != EOF) {
+ /* need to know about newlines and found it? */
+ if (!nl[0] && ((c == '\015') || (c == '\012')) && ((nl[0]=c) == '\015')){
+ /* sniff and see if an LF */
+ if ((c = getc (f)) == '\012') nl[1] = c;
+ ungetc (c,f); /* push it back */
+ }
+ /* write to backup file */
+ if ((d = putc (c,bf)) == EOF) {
+ fclose (f); /* punt input file */
+ return newsrc_error ("Error writing backup news state %.80s",
+ newsrc,ERROR);
+ }
+ }
+ fclose (f); /* close existing file */
+ if (fclose (bf) == EOF) /* and backup file */
+ return newsrc_error ("Error closing backup news state %.80s",
+ newsrc,ERROR);
+ if (d == EOF) { /* open for write if empty file */
+ if (f = newsrc_create (stream,NIL)) bf = NIL;
+ else return NIL;
+ }
+ else if (!nl[0]) /* make sure newlines valid */
+ return newsrc_error ("Unknown newline convention in %.80s",newsrc,ERROR);
+ /* now read backup file */
+ else if (!(bf = fopen (backup,"rb")))
+ return newsrc_error ("Error reading backup news state %.80s",
+ backup,ERROR);
+ /* open newsrc for writing */
+ else if (!(f = fopen (newsrc,"wb"))) {
+ fclose (bf); /* punt backup */
+ return newsrc_error ("Can't rewrite news state %.80s",newsrc,ERROR);
+ }
+ }
+ else { /* create new newsrc file */
+ if (f = newsrc_create (stream,T)) bf = NIL;
+ else return NIL; /* can't create newsrc */
+ }
+
+ while (bf) { /* read newsrc */
+ for (s = tmp; (s < (tmp + MAILTMPLEN - 1)) && ((c = getc (bf)) != EOF) &&
+ (c != ':') && (c != '!') && (c != '\015') && (c != '\012'); *s++ = c);
+ *s = '\0'; /* tie off name */
+ /* saw correct end of group delimiter? */
+ if (tmp[0] && ((c == ':') || (c == '!'))) {
+ /* yes, write newsgroup name and delimiter */
+ if ((tmp[0] && (fputs (tmp,f) == EOF)) || ((putc (c,f)) == EOF))
+ return newsrc_write_error (newsrc,bf,f);
+ if (!strcmp (tmp,group)) {/* found correct group? */
+ /* yes, write new status */
+ if (!newsrc_newmessages (f,stream,nl[0] ? nl : "\n"))
+ return newsrc_write_error (newsrc,bf,f);
+ /* skip past old data */
+ while (((c = getc (bf)) != EOF) && (c != '\015') && (c != '\012'));
+ /* skip past newline */
+ while ((c == '\015') || (c == '\012')) c = getc (bf);
+ while (c != EOF) { /* copy remainder of file */
+ if (putc (c,f) == EOF) return newsrc_write_error (newsrc,bf,f);
+ c = getc (bf); /* get next character */
+ }
+ /* done with file */
+ if (fclose (f) == EOF) return newsrc_write_error (newsrc,bf,NIL);
+ f = NIL;
+ }
+ /* copy remainder of line */
+ else while (((c = getc (bf)) != EOF) && (c != '\015') && (c != '\012'))
+ if (putc (c,f) == EOF) return newsrc_write_error (newsrc,bf,f);
+ if (c == '\015') { /* write CR if seen */
+ if (putc (c,f) == EOF) return newsrc_write_error (newsrc,bf,f);
+ /* sniff to see if LF */
+ if (((c = getc (bf)) != EOF) && (c != '\012')) ungetc (c,bf);
+ }
+ /* write LF if seen */
+ if ((c == '\012') && (putc (c,f) == EOF))
+ return newsrc_write_error (newsrc,bf,f);
+ }
+ if (c == EOF) { /* hit end of file? */
+ fclose (bf); /* yup, close the file */
+ bf = NIL;
+ }
+ }
+ if (f) { /* still have newsrc file open? */
+ ret = ((fputs (group,f) != EOF) && ((putc (':',f)) != EOF) &&
+ newsrc_newmessages (f,stream,nl[0] ? nl : "\n")) ? LONGT : NIL;
+ if (fclose (f) == EOF) ret = newsrc_write_error (newsrc,NIL,NIL);
+ }
+ else ret = LONGT;
+ return ret;
+}
+
+/* Get newsgroup state as text stream
+ * Accepts: MAIL stream
+ * newsgroup name
+ * Returns: string containing newsgroup state, or NIL if not found
+ */
+
+char *newsrc_state (MAILSTREAM *stream,char *group)
+{
+ int c = 0;
+ char *s,tmp[MAILTMPLEN];
+ long pos;
+ size_t size;
+ FILE *f = fopen ((char *) mail_parameters (stream,GET_NEWSRC,stream),"rb");
+ if (f) do { /* read newsrc */
+ for (s = tmp; (s < (tmp + MAILTMPLEN - 1)) && ((c = getc (f)) != EOF) &&
+ (c != ':') && (c != '!') && (c != '\015') && (c != '\012'); *s++ = c);
+ *s = '\0'; /* tie off name */
+ if ((c==':') || (c=='!')) { /* found newsgroup? */
+ if (strcmp (tmp,group)) /* group name match? */
+ while ((c != '\015') && (c != '\012') && (c != EOF)) c = getc (f);
+ else { /* yes, skip leading whitespace */
+ do pos = ftell (f);
+ while ((c = getc (f)) == ' ');
+ /* count characters in state */
+ for (size = 0; (c != '\015') && (c != '\012') && (c != EOF); size++)
+ c = getc (f);
+ /* now copy it */
+ s = (char *) fs_get (size + 1);
+ fseek (f,pos,SEEK_SET);
+ fread (s,(size_t) 1,size,f);
+ s[size] = '\0'; /* tie off string */
+ fclose (f); /* all done - close the file */
+ return s;
+ }
+ }
+ } while (f && (c != EOF)); /* until file closed or EOF */
+ sprintf (tmp,"No state for newsgroup %.80s found",group);
+ MM_LOG (tmp,WARN);
+ if (f) fclose (f); /* close the file */
+ return NIL; /* not found return */
+}
+
+/* Check UID in newsgroup state
+ * Accepts: newsgroup state string
+ * uid
+ * returned recent count
+ * returned unseen count
+ */
+
+void newsrc_check_uid (unsigned char *state,unsigned long uid,
+ unsigned long *recent,unsigned long *unseen)
+{
+ unsigned long i,j;
+ while (*state) { /* until run out of state string */
+ /* collect a number */
+ for (i = 0; isdigit (*state); i = i*10 + (*state++ - '0'));
+ if (*state != '-') j = i; /* coerce single mesage into range */
+ else { /* have a range */
+ for (j = 0; isdigit (*++state); j = j*10 + (*state - '0'));
+ if (!j) j = i; /* guard against -0 */
+ if (j < i) return; /* bogon if end less than start */
+ }
+ if (*state == ',') state++; /* skip past comma */
+ else if (*state) return; /* otherwise it's a bogon */
+ if (uid <= j) { /* covered by upper bound? */
+ if (uid < i) ++*unseen; /* unseen if not covered by lower bound */
+ return; /* don't need to look further */
+ }
+ }
+ ++*unseen; /* not found in any range, message is unseen */
+ ++*recent; /* and recent */
+}
diff --git a/imap/src/c-client/newsrc.h b/imap/src/c-client/newsrc.h
new file mode 100644
index 00000000..578f656d
--- /dev/null
+++ b/imap/src/c-client/newsrc.h
@@ -0,0 +1,43 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Newsrc manipulation routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 12 September 1994
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Function prototypes */
+
+long newsrc_error (char *fmt,char *text,long errflg);
+long newsrc_write_error (char *name,FILE *f1,FILE *f2);
+FILE *newsrc_create (MAILSTREAM *stream,int notify);
+long newsrc_newstate (FILE *f,char *group,char state,char *nl);
+long newsrc_newmessages (FILE *f,MAILSTREAM *stream,char *nl);
+void newsrc_lsub (MAILSTREAM *stream,char *pattern);
+long newsrc_update (MAILSTREAM *stream,char *group,char state);
+long newsrc_read (char *group,MAILSTREAM *stream);
+long newsrc_write (char *group,MAILSTREAM *stream);
+char *newsrc_state (MAILSTREAM *stream,char *group);
+void newsrc_check_uid (unsigned char *state,unsigned long uid,
+ unsigned long *recent,unsigned long *unseen);
diff --git a/imap/src/c-client/nl.h b/imap/src/c-client/nl.h
new file mode 100644
index 00000000..520e9f7e
--- /dev/null
+++ b/imap/src/c-client/nl.h
@@ -0,0 +1,34 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Newline routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Function prototypes */
+
+unsigned long strcrlfcpy (unsigned char **dst,unsigned long *dstl,
+ unsigned char *src,unsigned long srcl);
+unsigned long strcrlflen (STRING *s);
diff --git a/imap/src/c-client/nntp.c b/imap/src/c-client/nntp.c
new file mode 100644
index 00000000..fe90edba
--- /dev/null
+++ b/imap/src/c-client/nntp.c
@@ -0,0 +1,2224 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Network News Transfer Protocol (NNTP) routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 February 1992
+ * Last Edited: 6 September 2007
+ */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include "c-client.h"
+#include "newsrc.h"
+#include "netmsg.h"
+#include "flstring.h"
+
+/* Constants */
+
+#define NNTPSSLPORT (long) 563 /* assigned SSL TCP contact port */
+#define NNTPGREET (long) 200 /* NNTP successful greeting */
+ /* NNTP successful greeting w/o posting priv */
+#define NNTPGREETNOPOST (long) 201
+#define NNTPEXTOK (long) 202 /* NNTP extensions OK */
+#define NNTPGOK (long) 211 /* NNTP group selection OK */
+#define NNTPGLIST (long) 215 /* NNTP group list being returned */
+#define NNTPARTICLE (long) 220 /* NNTP article file */
+#define NNTPHEAD (long) 221 /* NNTP header text */
+#define NNTPBODY (long) 222 /* NNTP body text */
+#define NNTPOVER (long) 224 /* NNTP overview text */
+#define NNTPOK (long) 240 /* NNTP OK code */
+#define NNTPAUTHED (long) 281 /* NNTP successful authentication */
+ /* NNTP successful authentication with data */
+#define NNTPAUTHEDDATA (long) 282
+#define NNTPREADY (long) 340 /* NNTP ready for data */
+#define NNTPWANTAUTH2 (long) 380/* NNTP authentication needed (old) */
+#define NNTPWANTPASS (long) 381 /* NNTP password needed */
+#define NNTPTLSSTART (long) 382 /* NNTP continue with TLS negotiation */
+#define NNTPCHALLENGE (long) 383/* NNTP challenge, want response */
+#define NNTPSOFTFATAL (long) 400/* NNTP soft fatal code */
+#define NNTPWANTAUTH (long) 480 /* NNTP authentication needed */
+#define NNTPBADCMD (long) 500 /* NNTP unrecognized command */
+#define IDLETIMEOUT (long) 3 /* defined in NNTPEXT WG base draft */
+
+
+/* NNTP I/O stream local data */
+
+typedef struct nntp_local {
+ SENDSTREAM *nntpstream; /* NNTP stream for I/O */
+ unsigned int dirty : 1; /* disk copy of .newsrc needs updating */
+ unsigned int tlsflag : 1; /* TLS session */
+ unsigned int tlssslv23 : 1; /* TLS using SSLv23 client method */
+ unsigned int notlsflag : 1; /* TLS not used in session */
+ unsigned int sslflag : 1; /* SSL session */
+ unsigned int novalidate : 1; /* certificate not validated */
+ unsigned int xover : 1; /* supports XOVER */
+ unsigned int xhdr : 1; /* supports XHDR */
+ char *name; /* remote newsgroup name */
+ char *user; /* mailbox user */
+ char *newsrc; /* newsrc file */
+ char *over_fmt; /* overview format */
+ unsigned long msgno; /* current text message number */
+ FILE *txt; /* current text */
+ unsigned long txtsize; /* current text size */
+} NNTPLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((NNTPLOCAL *) stream->local)
+
+
+/* Convenient access to protocol-specific data */
+
+#define NNTP stream->protocol.nntp
+
+
+/* Convenient access to extensions */
+
+#define EXTENSION LOCAL->nntpstream->protocol.nntp.ext
+
+/* Function prototypes */
+
+DRIVER *nntp_valid (char *name);
+DRIVER *nntp_isvalid (char *name,char *mbx);
+void *nntp_parameters (long function,void *value);
+void nntp_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void nntp_list (MAILSTREAM *stream,char *ref,char *pat);
+void nntp_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long nntp_canonicalize (char *ref,char *pat,char *pattern,char *wildmat);
+long nntp_subscribe (MAILSTREAM *stream,char *mailbox);
+long nntp_unsubscribe (MAILSTREAM *stream,char *mailbox);
+long nntp_create (MAILSTREAM *stream,char *mailbox);
+long nntp_delete (MAILSTREAM *stream,char *mailbox);
+long nntp_rename (MAILSTREAM *stream,char *old,char *newname);
+long nntp_status (MAILSTREAM *stream,char *mbx,long flags);
+long nntp_getmap (MAILSTREAM *stream,char *name,
+ unsigned long first,unsigned long last,
+ unsigned long rnmsgs,unsigned long nmsgs,char *tmp);
+MAILSTREAM *nntp_mopen (MAILSTREAM *stream);
+void nntp_mclose (MAILSTREAM *stream,long options);
+void nntp_fetchfast (MAILSTREAM *stream,char *sequence,long flags);
+void nntp_flags (MAILSTREAM *stream,char *sequence,long flags);
+long nntp_overview (MAILSTREAM *stream,overview_t ofn);
+long nntp_parse_overview (OVERVIEW *ov,char *text,MESSAGECACHE *elt);
+long nntp_over (MAILSTREAM *stream,char *sequence);
+char *nntp_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *size,
+ long flags);
+long nntp_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+FILE *nntp_article (MAILSTREAM *stream,char *msgid,unsigned long *size,
+ unsigned long *hsiz);
+void nntp_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long nntp_search (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,long flags);
+long nntp_search_msg (MAILSTREAM *stream,unsigned long msgno,SEARCHPGM *pgm,
+ OVERVIEW *ov);
+unsigned long *nntp_sort (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
+ SORTPGM *pgm,long flags);
+SORTCACHE **nntp_sort_loadcache (MAILSTREAM *stream,SORTPGM *pgm,
+ unsigned long start,unsigned long last,
+ long flags);
+THREADNODE *nntp_thread (MAILSTREAM *stream,char *type,char *charset,
+ SEARCHPGM *spg,long flags);
+long nntp_ping (MAILSTREAM *stream);
+void nntp_check (MAILSTREAM *stream);
+long nntp_expunge (MAILSTREAM *stream,char *sequence,long options);
+long nntp_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long nntp_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+long nntp_extensions (SENDSTREAM *stream,long flags);
+long nntp_send (SENDSTREAM *stream,char *command,char *args);
+long nntp_send_work (SENDSTREAM *stream,char *command,char *args);
+long nntp_send_auth (SENDSTREAM *stream,long flags);
+long nntp_send_auth_work (SENDSTREAM *stream,NETMBX *mb,char *pwd,long flags);
+void *nntp_challenge (void *s,unsigned long *len);
+long nntp_response (void *s,char *response,unsigned long size);
+long nntp_reply (SENDSTREAM *stream);
+long nntp_fake (SENDSTREAM *stream,char *text);
+long nntp_soutr (void *stream,char *s);
+
+/* Driver dispatch used by MAIL */
+
+DRIVER nntpdriver = {
+ "nntp", /* driver name */
+ /* driver flags */
+#ifdef INADEQUATE_MEMORY
+ DR_LOWMEM |
+#endif
+ DR_NEWS|DR_READONLY|DR_NOFAST|DR_NAMESPACE|DR_CRLF|DR_RECYCLE|DR_XPOINT |
+ DR_NOINTDATE|DR_NONEWMAIL|DR_HALFOPEN,
+ (DRIVER *) NIL, /* next driver */
+ nntp_valid, /* mailbox is valid for us */
+ nntp_parameters, /* manipulate parameters */
+ nntp_scan, /* scan mailboxes */
+ nntp_list, /* find mailboxes */
+ nntp_lsub, /* find subscribed mailboxes */
+ nntp_subscribe, /* subscribe to mailbox */
+ nntp_unsubscribe, /* unsubscribe from mailbox */
+ nntp_create, /* create mailbox */
+ nntp_delete, /* delete mailbox */
+ nntp_rename, /* rename mailbox */
+ nntp_status, /* status of mailbox */
+ nntp_mopen, /* open mailbox */
+ nntp_mclose, /* close mailbox */
+ nntp_fetchfast, /* fetch message "fast" attributes */
+ nntp_flags, /* fetch message flags */
+ nntp_overview, /* fetch overview */
+ NIL, /* fetch message structure */
+ nntp_header, /* fetch message header */
+ nntp_text, /* fetch message text */
+ NIL, /* fetch message */
+ NIL, /* unique identifier */
+ NIL, /* message number from UID */
+ NIL, /* modify flags */
+ nntp_flagmsg, /* per-message modify flags */
+ nntp_search, /* search for message based on criteria */
+ nntp_sort, /* sort messages */
+ nntp_thread, /* thread messages */
+ nntp_ping, /* ping mailbox to see if still alive */
+ nntp_check, /* check for new messages */
+ nntp_expunge, /* expunge deleted messages */
+ nntp_copy, /* copy messages to another mailbox */
+ nntp_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM nntpproto = {&nntpdriver};
+
+
+ /* driver parameters */
+static unsigned long nntp_maxlogintrials = MAXLOGINTRIALS;
+static long nntp_port = 0;
+static long nntp_sslport = 0;
+static unsigned long nntp_range = 0;
+static long nntp_hidepath = 0;
+
+/* NNTP validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *nntp_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return nntp_isvalid (name,tmp);
+}
+
+
+/* NNTP validate mailbox work routine
+ * Accepts: mailbox name
+ * buffer for returned mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *nntp_isvalid (char *name,char *mbx)
+{
+ NETMBX mb;
+ if (!mail_valid_net_parse (name,&mb) || strcmp (mb.service,nntpdriver.name)||
+ mb.anoflag) return NIL;
+ if (mb.mailbox[0] != '#') strcpy (mbx,mb.mailbox);
+ /* namespace format name */
+ else if ((mb.mailbox[1] == 'n') && (mb.mailbox[2] == 'e') &&
+ (mb.mailbox[3] == 'w') && (mb.mailbox[4] == 's') &&
+ (mb.mailbox[5] == '.')) strcpy (mbx,mb.mailbox+6);
+ else return NIL; /* bogus name */
+ return &nntpdriver;
+}
+
+/* News manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *nntp_parameters (long function,void *value)
+{
+ switch ((int) function) {
+ case SET_MAXLOGINTRIALS:
+ nntp_maxlogintrials = (unsigned long) value;
+ break;
+ case GET_MAXLOGINTRIALS:
+ value = (void *) nntp_maxlogintrials;
+ break;
+ case SET_NNTPPORT:
+ nntp_port = (long) value;
+ break;
+ case GET_NNTPPORT:
+ value = (void *) nntp_port;
+ break;
+ case SET_SSLNNTPPORT:
+ nntp_sslport = (long) value;
+ break;
+ case GET_SSLNNTPPORT:
+ value = (void *) nntp_sslport;
+ break;
+ case SET_NNTPRANGE:
+ nntp_range = (unsigned long) value;
+ break;
+ case GET_NNTPRANGE:
+ value = (void *) nntp_range;
+ break;
+ case SET_NNTPHIDEPATH:
+ nntp_hidepath = (long) value;
+ break;
+ case GET_NNTPHIDEPATH:
+ value = (void *) nntp_hidepath;
+ break;
+ case GET_NEWSRC:
+ if (value)
+ value = (void *) ((NNTPLOCAL *) ((MAILSTREAM *) value)->local)->newsrc;
+ break;
+ case GET_IDLETIMEOUT:
+ value = (void *) IDLETIMEOUT;
+ break;
+ case ENABLE_DEBUG:
+ if (value)
+ ((NNTPLOCAL *) ((MAILSTREAM *) value)->local)->nntpstream->debug = T;
+ break;
+ case DISABLE_DEBUG:
+ if (value)
+ ((NNTPLOCAL *) ((MAILSTREAM *) value)->local)->nntpstream->debug = NIL;
+ break;
+ default:
+ value = NIL; /* error case */
+ break;
+ }
+ return value;
+}
+
+/* NNTP mail scan mailboxes for string
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void nntp_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ char tmp[MAILTMPLEN];
+ if (nntp_canonicalize (ref,pat,tmp,NIL))
+ mm_log ("Scan not valid for NNTP mailboxes",ERROR);
+}
+
+
+/* NNTP list newsgroups
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void nntp_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ MAILSTREAM *st = stream;
+ char *s,*t,*lcl,pattern[MAILTMPLEN],name[MAILTMPLEN],wildmat[MAILTMPLEN];
+ int showuppers = pat[strlen (pat) - 1] == '%';
+ if (!*pat) {
+ if (nntp_canonicalize (ref,"*",pattern,NIL)) {
+ /* tie off name at root */
+ if ((s = strchr (pattern,'}')) && (s = strchr (s+1,'.'))) *++s = '\0';
+ else pattern[0] = '\0';
+ mm_list (stream,'.',pattern,NIL);
+ }
+ }
+ /* ask server for open newsgroups */
+ else if (nntp_canonicalize (ref,pat,pattern,wildmat) &&
+ ((stream && LOCAL && LOCAL->nntpstream) ||
+ (stream = mail_open (NIL,pattern,OP_HALFOPEN|OP_SILENT))) &&
+ ((nntp_send (LOCAL->nntpstream,"LIST ACTIVE",
+ wildmat[0] ? wildmat : NIL) == NNTPGLIST) ||
+ (nntp_send (LOCAL->nntpstream,"LIST",NIL) == NNTPGLIST))) {
+ /* namespace format name? */
+ if (*(lcl = strchr (strcpy (name,pattern),'}') + 1) == '#') lcl += 6;
+ /* process data until we see final dot */
+ while (s = net_getline (LOCAL->nntpstream->netstream)) {
+ if ((*s == '.') && !s[1]){/* end of text */
+ fs_give ((void **) &s);
+ break;
+ }
+ if (t = strchr (s,' ')) { /* tie off after newsgroup name */
+ *t = '\0';
+ strcpy (lcl,s); /* make full form of name */
+ /* report if match */
+ if (pmatch_full (name,pattern,'.')) mm_list (stream,'.',name,NIL);
+ else while (showuppers && (t = strrchr (lcl,'.'))) {
+ *t = '\0'; /* tie off the name */
+ if (pmatch_full (name,pattern,'.'))
+ mm_list (stream,'.',name,LATT_NOSELECT);
+ }
+ }
+ fs_give ((void **) &s); /* clean up */
+ }
+ if (stream != st) mail_close (stream);
+ }
+}
+
+/* NNTP list subscribed newsgroups
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void nntp_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ void *sdb = NIL;
+ char *s,mbx[MAILTMPLEN];
+ /* return data from newsrc */
+ if (nntp_canonicalize (ref,pat,mbx,NIL)) newsrc_lsub (stream,mbx);
+ if (*pat == '{') { /* if remote pattern, must be NNTP */
+ if (!nntp_valid (pat)) return;
+ ref = NIL; /* good NNTP pattern, punt reference */
+ }
+ /* if remote reference, must be valid NNTP */
+ if (ref && (*ref == '{') && !nntp_valid (ref)) return;
+ /* kludgy application of reference */
+ if (ref && *ref) sprintf (mbx,"%s%s",ref,pat);
+ else strcpy (mbx,pat);
+
+ if (s = sm_read (&sdb)) do if (nntp_valid (s) && pmatch (s,mbx))
+ mm_lsub (stream,NIL,s,NIL);
+ while (s = sm_read (&sdb)); /* until no more subscriptions */
+}
+
+/* NNTP canonicalize newsgroup name
+ * Accepts: reference
+ * pattern
+ * returned single pattern
+ * returned wildmat pattern
+ * Returns: T on success, NIL on failure
+ */
+
+long nntp_canonicalize (char *ref,char *pat,char *pattern,char *wildmat)
+{
+ char *s;
+ DRIVER *ret;
+ if (ref && *ref) { /* have a reference */
+ if (!nntp_valid (ref)) return NIL;
+ strcpy (pattern,ref); /* copy reference to pattern */
+ /* # overrides mailbox field in reference */
+ if (*pat == '#') strcpy (strchr (pattern,'}') + 1,pat);
+ /* pattern starts, reference ends, with . */
+ else if ((*pat == '.') && (pattern[strlen (pattern) - 1] == '.'))
+ strcat (pattern,pat + 1); /* append, omitting one of the period */
+ else strcat (pattern,pat); /* anything else is just appended */
+ }
+ else strcpy (pattern,pat); /* just have basic name */
+ if ((ret = wildmat ? /* if valid and wildmat */
+ nntp_isvalid (pattern,wildmat) : nntp_valid (pattern)) && wildmat) {
+ /* don't return wildmat if specials present */
+ if (strpbrk (wildmat,",?![\\]")) wildmat[0] = '\0';
+ /* replace all % with * */
+ for (s = wildmat; s = strchr (s,'%'); *s = '*');
+ }
+ return ret ? LONGT : NIL;
+}
+
+/* NNTP subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long nntp_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ char mbx[MAILTMPLEN];
+ return nntp_isvalid (mailbox,mbx) ? newsrc_update (stream,mbx,':') : NIL;
+}
+
+
+/* NNTP unsubscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to delete from subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long nntp_unsubscribe (MAILSTREAM *stream,char *mailbox)
+{
+ char mbx[MAILTMPLEN];
+ return nntp_isvalid (mailbox,mbx) ? newsrc_update (stream,mbx,'!') : NIL;
+}
+
+/* NNTP create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long nntp_create (MAILSTREAM *stream,char *mailbox)
+{
+ return NIL; /* never valid for NNTP */
+}
+
+
+/* NNTP delete mailbox
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long nntp_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return NIL; /* never valid for NNTP */
+}
+
+
+/* NNTP rename mailbox
+ * Accepts: mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long nntp_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ return NIL; /* never valid for NNTP */
+}
+
+/* NNTP status
+ * Accepts: mail stream
+ * mailbox name
+ * status flags
+ * Returns: T on success, NIL on failure
+ */
+
+long nntp_status (MAILSTREAM *stream,char *mbx,long flags)
+{
+ MAILSTATUS status;
+ NETMBX mb;
+ unsigned long i,j,k,rnmsgs;
+ long ret = NIL;
+ char *s,*name,*state,tmp[MAILTMPLEN];
+ char *old = (stream && !stream->halfopen) ? LOCAL->name : NIL;
+ MAILSTREAM *tstream = NIL;
+ if (!(mail_valid_net_parse (mbx,&mb) && !strcmp (mb.service,"nntp") &&
+ *mb.mailbox &&
+ ((mb.mailbox[0] != '#') ||
+ ((mb.mailbox[1] == 'n') && (mb.mailbox[2] == 'e') &&
+ (mb.mailbox[3] == 'w') && (mb.mailbox[4] == 's') &&
+ (mb.mailbox[5] == '.'))))) {
+ sprintf (tmp,"Invalid NNTP name %s",mbx);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* note mailbox name */
+ name = (*mb.mailbox == '#') ? mb.mailbox+6 : mb.mailbox;
+ /* stream to reuse? */
+ if (!(stream && LOCAL->nntpstream &&
+ mail_usable_network_stream (stream,mbx)) &&
+ !(tstream = stream =
+ mail_open (NIL,mbx,OP_HALFOPEN|OP_SILENT|
+ ((flags & SA_MULNEWSRC) ? OP_MULNEWSRC : NIL))))
+ return NIL; /* can't reuse or make a new one */
+
+ if (nntp_send (LOCAL->nntpstream,"GROUP",name) == NNTPGOK) {
+ status.flags = flags; /* status validity flags */
+ k = strtoul (LOCAL->nntpstream->reply + 4,&s,10);
+ i = strtoul (s,&s,10); /* first assigned UID */
+ /* next UID to be assigned */
+ status.uidnext = (j = strtoul (s,NIL,10)) + 1;
+ /* maximum number of messages */
+ rnmsgs = status.messages = (i | j) ? status.uidnext - i : 0;
+ if (k > status.messages) { /* check for absurdity */
+ sprintf (tmp,"NNTP SERVER BUG (impossible message count): %lu > %lu",
+ k,status.messages);
+ mm_log (tmp,WARN);
+ }
+ /* restrict article range if needed */
+ if (nntp_range && (status.messages > nntp_range)) {
+ i = status.uidnext - (status.messages = nntp_range);
+ if (k > nntp_range) k = nntp_range;
+ }
+ /* initially zero */
+ status.recent = status.unseen = 0;
+ if (!status.messages); /* empty case */
+ /* use server guesstimate in simple case */
+ else if (!(flags & (SA_RECENT | SA_UNSEEN))) status.messages = k;
+
+ /* have newsrc state? */
+ else if (state = newsrc_state (stream,name)) {
+ /* yes, get the UID/sequence map */
+ if (nntp_getmap (stream,name,i,status.uidnext - 1,rnmsgs,
+ status.messages,tmp)) {
+ /* calculate true count */
+ for (status.messages = 0;
+ (s = net_getline (LOCAL->nntpstream->netstream)) &&
+ strcmp (s,"."); ) {
+ /* only count if in range */
+ if (((k = atol (s)) >= i) && (k < status.uidnext)) {
+ newsrc_check_uid (state,k,&status.recent,&status.unseen);
+ status.messages++;
+ }
+ fs_give ((void **) &s);
+ }
+ if (s) fs_give ((void **) &s);
+ }
+ /* assume c-client/NNTP map is entire range */
+ else while (i < status.uidnext)
+ newsrc_check_uid (state,i++,&status.recent,&status.unseen);
+ fs_give ((void **) &state);
+ }
+ /* no .newsrc state, all messages new */
+ else status.recent = status.unseen = status.messages;
+ /* UID validity is a constant */
+ status.uidvalidity = stream->uid_validity;
+ /* pass status to main program */
+ mm_status (stream,mbx,&status);
+ ret = T; /* succes */
+ }
+ /* flush temporary stream */
+ if (tstream) mail_close (tstream);
+ /* else reopen old newsgroup */
+ else if (old && nntp_send (LOCAL->nntpstream,"GROUP",old) != NNTPGOK) {
+ mm_log (LOCAL->nntpstream->reply,ERROR);
+ stream->halfopen = T; /* go halfopen */
+ }
+ return ret; /* success */
+}
+
+/* NNTP get map
+ * Accepts: stream
+ * newsgroup name
+ * first UID in map range
+ * last UID in map range
+ * reported total number of messages in newsgroup
+ * calculated number of messages in range
+ * temporary buffer
+ * Returns: T on success, NIL on failure
+ */
+
+long nntp_getmap (MAILSTREAM *stream,char *name,
+ unsigned long first,unsigned long last,
+ unsigned long rnmsgs,unsigned long nmsgs,char *tmp)
+{
+ short trylistgroup = NIL;
+ if (rnmsgs > (nmsgs * 8)) /* small subrange? */
+ trylistgroup = T; /* yes, can try LISTGROUP if [X]HDR fails */
+ else switch ((int) nntp_send (LOCAL->nntpstream,"LISTGROUP",name)) {
+ case NNTPGOK: /* got data */
+ return LONGT;
+ default: /* else give up if server claims LISTGROUP */
+ if (EXTENSION.listgroup) return NIL;
+ }
+ /* build range */
+ sprintf (tmp,"%lu-%lu",first,last);
+ if (EXTENSION.hdr) /* have HDR extension? */
+ return (nntp_send (LOCAL->nntpstream,"HDR Date",tmp) == NNTPHEAD) ?
+ LONGT : NIL;
+ if (LOCAL->xhdr) /* try the experimental extension then */
+ switch ((int) nntp_send (LOCAL->nntpstream,"XHDR Date",tmp)) {
+ case NNTPHEAD: /* got an overview? */
+ return LONGT;
+ case NNTPBADCMD: /* unknown command? */
+ LOCAL->xhdr = NIL; /* disable future XHDR attempts */
+ }
+ if (trylistgroup && /* no [X]HDR, maybe do LISTGROUP after all */
+ (nntp_send (LOCAL->nntpstream,"LISTGROUP",name) == NNTPGOK))
+ return LONGT;
+ return NIL;
+}
+
+/* NNTP open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *nntp_mopen (MAILSTREAM *stream)
+{
+ unsigned long i,j,k,nmsgs,rnmsgs;
+ char *s,*mbx,tmp[MAILTMPLEN];
+ FILE *f;
+ NETMBX mb;
+ char *newsrc = (char *) mail_parameters (NIL,GET_NEWSRC,NIL);
+ newsrcquery_t nq = (newsrcquery_t) mail_parameters (NIL,GET_NEWSRCQUERY,NIL);
+ SENDSTREAM *nstream = NIL;
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &nntpproto;
+ mail_valid_net_parse (stream->mailbox,&mb);
+ /* note mailbox anme */
+ mbx = (*mb.mailbox == '#') ? mb.mailbox+6 : mb.mailbox;
+ if (LOCAL) { /* recycle stream */
+ nstream = LOCAL->nntpstream;/* remember NNTP protocol stream */
+ sprintf (tmp,"Reusing connection to %s",net_host (nstream->netstream));
+ if (!stream->silent) mm_log (tmp,(long) NIL);
+ if (stream->rdonly) mb.readonlyflag = T;
+ if (LOCAL->tlsflag) mb.tlsflag = T;
+ if (LOCAL->tlssslv23) mb.tlssslv23 = T;
+ if (LOCAL->notlsflag) mb.notlsflag = T;
+ if (LOCAL->sslflag) mb.sslflag = T;
+ if (LOCAL->novalidate) mb.novalidate = T;
+ if (LOCAL->nntpstream->loser) mb.loser = T;
+ if (stream->secure) mb.secflag = T;
+ LOCAL->nntpstream = NIL; /* keep nntp_mclose() from punting it */
+ nntp_mclose (stream,NIL); /* do close action */
+ stream->dtb = &nntpdriver; /* reattach this driver */
+ }
+ /* copy flags */
+ if (mb.dbgflag) stream->debug = T;
+ if (mb.readonlyflag) stream->rdonly = T;
+ if (mb.secflag) stream->secure = T;
+ mb.trysslflag = stream->tryssl = (mb.trysslflag || stream->tryssl) ? T : NIL;
+ if (!nstream) { /* open NNTP now if not already open */
+ char *hostlist[2];
+ hostlist[0] = strcpy (tmp,mb.host);
+ if (mb.port || nntp_port)
+ sprintf (tmp + strlen (tmp),":%lu",mb.port ? mb.port : nntp_port);
+ if (mb.tlsflag) strcat (tmp,"/tls");
+ if (mb.tlssslv23) strcat (tmp,"/tls-sslv23");
+ if (mb.notlsflag) strcat (tmp,"/notls");
+ if (mb.sslflag) strcat (tmp,"/ssl");
+ if (mb.novalidate) strcat (tmp,"/novalidate-cert");
+ if (mb.loser) strcat (tmp,"/loser");
+ if (mb.secflag) strcat (tmp,"/secure");
+ if (mb.user[0]) sprintf (tmp + strlen (tmp),"/user=\"%s\"",mb.user);
+ hostlist[1] = NIL;
+ if (!(nstream = nntp_open (hostlist,NOP_READONLY |
+ (stream->debug ? NOP_DEBUG : NIL)))) return NIL;
+ }
+
+ /* always zero messages if halfopen */
+ if (stream->halfopen) i = j = k = rnmsgs = nmsgs = 0;
+ /* otherwise open the newsgroup */
+ else if (nntp_send (nstream,"GROUP",mbx) == NNTPGOK) {
+ k = strtoul (nstream->reply + 4,&s,10);
+ i = strtoul (s,&s,10);
+ stream->uid_last = j = strtoul (s,&s,10);
+ rnmsgs = nmsgs = (i | j) ? 1 + j - i : 0;
+ if (k > nmsgs) { /* check for absurdity */
+ sprintf (tmp,"NNTP SERVER BUG (impossible message count): %lu > %lu",
+ k,nmsgs);
+ mm_log (tmp,WARN);
+ }
+ /* restrict article range if needed */
+ if (nntp_range && (nmsgs > nntp_range)) i = 1 + j - (nmsgs = nntp_range);
+ }
+ else { /* no such newsgroup */
+ mm_log (nstream->reply,ERROR);
+ nntp_close (nstream); /* punt stream */
+ return NIL;
+ }
+ /* instantiate local data */
+ stream->local = memset (fs_get (sizeof (NNTPLOCAL)),0,sizeof (NNTPLOCAL));
+ LOCAL->nntpstream = nstream;
+ /* save state for future recycling */
+ if (mb.tlsflag) LOCAL->tlsflag = T;
+ if (mb.tlssslv23) LOCAL->tlssslv23 = T;
+ if (mb.notlsflag) LOCAL->notlsflag = T;
+ if (mb.sslflag) LOCAL->sslflag = T;
+ if (mb.novalidate) LOCAL->novalidate = T;
+ if (mb.loser) LOCAL->nntpstream->loser = T;
+ /* assume present until proven otherwise */
+ LOCAL->xhdr = LOCAL->xover = T;
+ LOCAL->name = cpystr (mbx); /* copy newsgroup name */
+ if (stream->mulnewsrc) { /* want to use multiple .newsrc files? */
+ strcpy (tmp,newsrc);
+ s = tmp + strlen (tmp); /* end of string */
+ *s++ = '-'; /* hyphen delimiter and host */
+ lcase (strcpy (s,(long) mail_parameters (NIL,GET_NEWSRCCANONHOST,NIL) ?
+ net_host (nstream->netstream) : mb.host));
+ LOCAL->newsrc = cpystr (nq ? (*nq) (stream,tmp,newsrc) : tmp);
+ }
+ else LOCAL->newsrc = cpystr (newsrc);
+ if (mb.user[0]) LOCAL->user = cpystr (mb.user);
+ stream->sequence++; /* bump sequence number */
+ stream->rdonly = stream->perm_deleted = T;
+ /* UIDs are always valid */
+ stream->uid_validity = 0xbeefface;
+ sprintf (tmp,"{%s:%lu/nntp",(long) mail_parameters (NIL,GET_TRUSTDNS,NIL) ?
+ net_host (nstream->netstream) : mb.host,
+ net_port (nstream->netstream));
+ if (LOCAL->tlsflag) strcat (tmp,"/tls");
+ if (LOCAL->tlssslv23) strcat (tmp,"/tls-sslv23");
+ if (LOCAL->notlsflag) strcat (tmp,"/notls");
+ if (LOCAL->sslflag) strcat (tmp,"/ssl");
+ if (LOCAL->novalidate) strcat (tmp,"/novalidate-cert");
+ if (LOCAL->nntpstream->loser) strcat (tmp,"/loser");
+ if (stream->secure) strcat (tmp,"/secure");
+ if (stream->rdonly) strcat (tmp,"/readonly");
+ if (LOCAL->user) sprintf (tmp + strlen (tmp),"/user=\"%s\"",LOCAL->user);
+ if (stream->halfopen) strcat (tmp,"}<no_mailbox>");
+ else sprintf (tmp + strlen (tmp),"}#news.%s",mbx);
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+
+ if (EXTENSION.over && /* get overview format if have OVER */
+ (nntp_send (LOCAL->nntpstream,"LIST","OVERVIEW.FMT") == NNTPGLIST) &&
+ (f = netmsg_slurp (LOCAL->nntpstream->netstream,&k,NIL))) {
+ fread (LOCAL->over_fmt = (char *) fs_get ((size_t) k + 3),
+ (size_t) 1,(size_t) k,f);
+ LOCAL->over_fmt[k] = '\0';
+ fclose (f); /* flush temp file */
+ }
+ if (nmsgs) { /* if any messages exist */
+ short silent = stream->silent;
+ stream->silent = T; /* don't notify main program yet */
+ mail_exists (stream,nmsgs); /* silently set the cache to the guesstimate */
+ /* get UID/sequence map, nuke holes */
+ if (nntp_getmap (stream,mbx,i,j,rnmsgs,nmsgs,tmp)) {
+ for (nmsgs = 0; /* calculate true count */
+ (s = net_getline (nstream->netstream)) && strcmp (s,"."); ) {
+ if ((k = atol (s)) > j){/* discard too high article numbers */
+ sprintf (tmp,"NNTP SERVER BUG (out of range article ID): %lu > %lu",
+ k,j);
+ mm_notify (stream,tmp,NIL);
+ stream->unhealthy = T;
+ }
+ else if (k >= i) { /* silently ignore too-low article numbers */
+ /* guard against server returning extra msgs */
+ if (nmsgs == stream->nmsgs) mail_exists (stream,nmsgs+1);
+ /* create elt for this message, set UID */
+ mail_elt (stream,++nmsgs)->private.uid = k;
+ }
+ fs_give ((void **) &s);
+ }
+ if (s) fs_give ((void **) &s);
+ }
+ /* assume c-client/NNTP map is entire range */
+ else for (k = 1; k <= nmsgs; k++) mail_elt (stream,k)->private.uid = i++;
+ stream->unhealthy = NIL; /* set healthy */
+ stream->nmsgs = 0; /* whack it back down */
+ stream->silent = silent; /* restore old silent setting */
+ mail_exists (stream,nmsgs); /* notify upper level that messages exist */
+ /* read .newsrc entries */
+ mail_recent (stream,newsrc_read (mbx,stream));
+ }
+ else { /* empty newsgroup or halfopen */
+ if (!(stream->silent || stream->halfopen)) {
+ sprintf (tmp,"Newsgroup %s is empty",mbx);
+ mm_log (tmp,WARN);
+ }
+ mail_exists (stream,(long) 0);
+ mail_recent (stream,(long) 0);
+ }
+ return stream; /* return stream to caller */
+}
+
+/* NNTP close
+ * Accepts: MAIL stream
+ * option flags
+ */
+
+void nntp_mclose (MAILSTREAM *stream,long options)
+{
+ unsigned long i;
+ MESSAGECACHE *elt;
+ if (LOCAL) { /* only if a file is open */
+ nntp_check (stream); /* dump final checkpoint */
+ if (LOCAL->over_fmt) fs_give ((void **) &LOCAL->over_fmt);
+ if (LOCAL->name) fs_give ((void **) &LOCAL->name);
+ if (LOCAL->user) fs_give ((void **) &LOCAL->user);
+ if (LOCAL->newsrc) fs_give ((void **) &LOCAL->newsrc);
+ if (LOCAL->txt) fclose (LOCAL->txt);
+ /* close NNTP connection */
+ if (LOCAL->nntpstream) nntp_close (LOCAL->nntpstream);
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->private.spare.ptr)
+ fs_give ((void **) &elt->private.spare.ptr);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* NNTP fetch fast information
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ * This is ugly and slow
+ */
+
+void nntp_fetchfast (MAILSTREAM *stream,char *sequence,long flags)
+{
+ unsigned long i;
+ MESSAGECACHE *elt;
+ /* get sequence */
+ if (stream && LOCAL && ((flags & FT_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++) {
+ if ((elt = mail_elt (stream,i))->sequence && (elt->valid = T) &&
+ !(elt->day && elt->rfc822_size)) {
+ ENVELOPE **env = NIL;
+ ENVELOPE *e = NIL;
+ if (!stream->scache) env = &elt->private.msg.env;
+ else if (stream->msgno == i) env = &stream->env;
+ else env = &e;
+ if (!*env || !elt->rfc822_size) {
+ STRING bs;
+ unsigned long hs;
+ char *ht = (*stream->dtb->header) (stream,i,&hs,NIL);
+ /* need to make an envelope? */
+ if (!*env) rfc822_parse_msg (env,NIL,ht,hs,NIL,BADHOST,
+ stream->dtb->flags);
+ /* need message size too, ugh */
+ if (!elt->rfc822_size) {
+ (*stream->dtb->text) (stream,i,&bs,FT_PEEK);
+ elt->rfc822_size = hs + SIZE (&bs) - GETPOS (&bs);
+ }
+ }
+ /* if need date, have date in envelope? */
+ if (!elt->day && *env && (*env)->date)
+ mail_parse_date (elt,(*env)->date);
+ /* sigh, fill in bogus default */
+ if (!elt->day) elt->day = elt->month = 1;
+ mail_free_envelope (&e);
+ }
+ }
+}
+
+/* NNTP fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ */
+
+void nntp_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ unsigned long i;
+ if ((flags & FT_UID) ? /* validate all elts */
+ mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))
+ for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->valid = T;
+}
+
+/* NNTP fetch overview
+ * Accepts: MAIL stream, sequence bits set
+ * overview return function
+ * Returns: T if successful, NIL otherwise
+ */
+
+long nntp_overview (MAILSTREAM *stream,overview_t ofn)
+{
+ unsigned long i,j,k,uid;
+ char c,*s,*t,*v,tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ OVERVIEW ov;
+ if (!LOCAL->nntpstream->netstream) return NIL;
+ /* scan sequence to load cache */
+ for (i = 1; i <= stream->nmsgs; i++)
+ /* have cached overview yet? */
+ if ((elt = mail_elt (stream,i))->sequence && !elt->private.spare.ptr) {
+ for (j = i + 1; /* no, find end of cache gap range */
+ (j <= stream->nmsgs) && (elt = mail_elt (stream,j))->sequence &&
+ !elt->private.spare.ptr; j++);
+ /* make NNTP range */
+ sprintf (tmp,(i == (j - 1)) ? "%lu" : "%lu-%lu",mail_uid (stream,i),
+ mail_uid (stream,j - 1));
+ i = j; /* advance beyond gap */
+ /* ask server for overview data to cache */
+ if (nntp_over (stream,tmp)) {
+ while ((s = net_getline (LOCAL->nntpstream->netstream)) &&
+ strcmp (s,".")) {
+ /* death to embedded newlines */
+ for (t = v = s; c = *v++;)
+ if ((c != '\012') && (c != '\015')) *t++ = c;
+ *t++ = '\0'; /* tie off string in case it was shortened */
+ /* cache the overview if found its sequence */
+ if ((uid = atol (s)) && (k = mail_msgno (stream,uid)) &&
+ (t = strchr (s,'\t'))) {
+ if ((elt = mail_elt (stream,k))->private.spare.ptr)
+ fs_give ((void **) &elt->private.spare.ptr);
+ elt->private.spare.ptr = cpystr (t + 1);
+ }
+ else { /* shouldn't happen, snarl if it does */
+ sprintf (tmp,"Server returned data for unknown UID %lu",uid);
+ mm_notify (stream,tmp,WARN);
+ stream->unhealthy = T;
+ }
+ /* flush the overview */
+ fs_give ((void **) &s);
+ }
+ stream->unhealthy = NIL;/* set healthy */
+ /* flush the terminating dot */
+ if (s) fs_give ((void **) &s);
+ }
+ else i = stream->nmsgs; /* OVER failed, punt cache load */
+ }
+
+ /* now scan sequence to return overviews */
+ if (ofn) for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ uid = mail_uid (stream,i);/* UID for this message */
+ /* parse cached overview */
+ if (nntp_parse_overview (&ov,s = (char *) elt->private.spare.ptr,elt))
+ (*ofn) (stream,uid,&ov,i);
+ else { /* parse failed */
+ (*ofn) (stream,uid,NIL,i);
+ if (s && *s) { /* unusable cached entry? */
+ sprintf (tmp,"Unable to parse overview for UID %lu: %.500s",uid,s);
+ mm_notify (stream,tmp,WARN);
+ stream->unhealthy = T;
+ /* erase it from the cache */
+ fs_give ((void **) &s);
+ }
+ stream->unhealthy = NIL;/* set healthy */
+ /* insert empty cached text as necessary */
+ if (!s) elt->private.spare.ptr = cpystr ("");
+ }
+ /* clean up overview data */
+ if (ov.from) mail_free_address (&ov.from);
+ if (ov.subject) fs_give ((void **) &ov.subject);
+ }
+ return T;
+}
+
+/* Send OVER to NNTP server
+ * Accepts: mail stream
+ * sequence to send
+ * Returns: T if success and overviews will follow, else NIL
+ */
+
+long nntp_over (MAILSTREAM *stream,char *sequence)
+{
+ unsigned char *s;
+ /* test for Netscape Collabra server */
+ if (EXTENSION.over && LOCAL->xover &&
+ nntp_send (LOCAL->nntpstream,"OVER","0") == NNTPOVER) {
+ /* "Netscape-Collabra/3.52 03615 NNTP" responds to the OVER command with
+ * a bogus "Subject:From:Date:Bytes:Lines" response followed by overviews
+ * which lack the Message-ID and References:. This violates the draft
+ * NNTP specification (draft-ietf-nntpext-base-18.txt as of this writing).
+ * XOVER works fine.
+ */
+ while ((s = net_getline (LOCAL->nntpstream->netstream)) && strcmp (s,".")){
+ if (!isdigit (*s)) { /* is it that fetid piece of reptile dung? */
+ EXTENSION.over = NIL; /* sure smells like it */
+ mm_log ("Working around Netscape Collabra bug",WARN);
+ }
+ fs_give ((void **) &s); /* flush the overview */
+ }
+ if (s) fs_give ((void **) &s);
+ /* don't do this test again */
+ if (EXTENSION.over) LOCAL->xover = NIL;
+ }
+ if (EXTENSION.over) /* have OVER extension? */
+ return (nntp_send (LOCAL->nntpstream,"OVER",sequence) == NNTPOVER) ?
+ LONGT : NIL;
+ if (LOCAL->xover) /* try the experiment extension then */
+ switch ((int) nntp_send (LOCAL->nntpstream,"XOVER",sequence)) {
+ case NNTPOVER: /* got an overview? */
+ return LONGT;
+ case NNTPBADCMD: /* unknown command? */
+ LOCAL->xover = NIL; /* disable future XOVER attempts */
+ }
+ return NIL;
+}
+
+/* Parse OVERVIEW struct from cached NNTP OVER response
+ * Accepts: struct to load
+ * cached OVER response
+ * internaldate
+ * Returns: T if success, NIL if fail
+ */
+
+long nntp_parse_overview (OVERVIEW *ov,char *text,MESSAGECACHE *elt)
+{
+ char *t;
+ /* nothing in overview yet */
+ memset ((void *) ov,0,sizeof (OVERVIEW));
+ /* no cached data */
+ if (!(text && *text)) return NIL;
+ ov->subject = cpystr (text); /* make hackable copy of overview */
+ /* find end of Subject */
+ if (t = strchr (ov->subject,'\t')) {
+ *t++ = '\0'; /* tie off Subject, point to From */
+ /* find end of From */
+ if (ov->date = strchr (t,'\t')) {
+ *ov->date++ = '\0'; /* tie off From, point to Date */
+ /* load internaldate too */
+ if (!elt->day) mail_parse_date (elt,ov->date);
+ /* parse From */
+ rfc822_parse_adrlist (&ov->from,t,BADHOST);
+ /* find end of Date */
+ if (ov->message_id = strchr (ov->date,'\t')) {
+ /* tie off Date, point to Message-ID */
+ *ov->message_id++ = '\0';
+ /* find end of Message-ID */
+ if (ov->references = strchr (ov->message_id,'\t')) {
+ /* tie off Message-ID, point to References */
+ *ov->references++ = '\0';
+ /* fine end of References */
+ if (t = strchr (ov->references,'\t')) {
+ *t++ = '\0'; /* tie off References, point to octet size */
+ /* parse size of message in octets */
+ ov->optional.octets = atol (t);
+ /* find end of size */
+ if (t = strchr (t,'\t')) {
+ /* parse size of message in lines */
+ ov->optional.lines = atol (++t);
+ /* find Xref */
+ if (ov->optional.xref = strchr (t,'\t'))
+ *ov->optional.xref++ = '\0';
+ }
+ }
+ }
+ }
+ }
+ }
+ return ov->references ? T : NIL;
+}
+
+/* NNTP fetch header as text
+ * Accepts: mail stream
+ * message number
+ * pointer to return size
+ * flags
+ * Returns: header text
+ */
+
+char *nntp_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *size,
+ long flags)
+{
+ char tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ FILE *f;
+ *size = 0;
+ if ((flags & FT_UID) && !(msgno = mail_msgno (stream,msgno))) return "";
+ /* have header text? */
+ if (!(elt = mail_elt (stream,msgno))->private.msg.header.text.data) {
+ sprintf (tmp,"%lu",mail_uid (stream,msgno));
+ /* get header text */
+ switch (nntp_send (LOCAL->nntpstream,"HEAD",tmp)) {
+ case NNTPHEAD:
+ if (f = netmsg_slurp (LOCAL->nntpstream->netstream,size,NIL)) {
+ fread (elt->private.msg.header.text.data =
+ (unsigned char *) fs_get ((size_t) *size + 3),
+ (size_t) 1,(size_t) *size,f);
+ fclose (f); /* flush temp file */
+ /* tie off header with extra CRLF and NUL */
+ elt->private.msg.header.text.data[*size] = '\015';
+ elt->private.msg.header.text.data[++*size] = '\012';
+ elt->private.msg.header.text.data[++*size] = '\0';
+ elt->private.msg.header.text.size = *size;
+ elt->valid = T; /* make elt valid now */
+ break;
+ }
+ /* fall into default case */
+ default: /* failed, mark as deleted and empty */
+ elt->valid = elt->deleted = T;
+ case NNTPSOFTFATAL: /* don't mark deleted if stream dead */
+ *size = elt->private.msg.header.text.size = 0;
+ break;
+ }
+ }
+ /* just return size of text */
+ else *size = elt->private.msg.header.text.size;
+ return elt->private.msg.header.text.data ?
+ (char *) elt->private.msg.header.text.data : "";
+}
+
+/* NNTP fetch body
+ * Accepts: mail stream
+ * message number
+ * pointer to stringstruct to initialize
+ * flags
+ * Returns: T if successful, else NIL
+ */
+
+long nntp_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ char tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ INIT (bs,mail_string,(void *) "",0);
+ if ((flags & FT_UID) && !(msgno = mail_msgno (stream,msgno))) return NIL;
+ elt = mail_elt (stream,msgno);
+ /* different message, flush cache */
+ if (LOCAL->txt && (LOCAL->msgno != msgno)) {
+ fclose (LOCAL->txt);
+ LOCAL->txt = NIL;
+ }
+ LOCAL->msgno = msgno; /* note cached message */
+ if (!LOCAL->txt) { /* have file for this message? */
+ sprintf (tmp,"%lu",elt->private.uid);
+ switch (nntp_send (LOCAL->nntpstream,"BODY",tmp)) {
+ case NNTPBODY:
+ if (LOCAL->txt = netmsg_slurp (LOCAL->nntpstream->netstream,
+ &LOCAL->txtsize,NIL)) break;
+ /* fall into default case */
+ default: /* failed, mark as deleted */
+ elt->deleted = T;
+ case NNTPSOFTFATAL: /* don't mark deleted if stream dead */
+ return NIL;
+ }
+ }
+ if (!(flags & FT_PEEK)) { /* mark seen if needed */
+ elt->seen = T;
+ mm_flags (stream,elt->msgno);
+ }
+ INIT (bs,file_string,(void *) LOCAL->txt,LOCAL->txtsize);
+ return T;
+}
+
+/* NNTP fetch article from message ID (for news: URL support)
+ * Accepts: mail stream
+ * message ID
+ * pointer to return total message size
+ * pointer to return file size
+ * Returns: FILE * to message if successful, else NIL
+ */
+
+FILE *nntp_article (MAILSTREAM *stream,char *msgid,unsigned long *size,
+ unsigned long *hsiz)
+{
+ return (nntp_send (LOCAL->nntpstream,"ARTICLE",msgid) == NNTPARTICLE) ?
+ netmsg_slurp (LOCAL->nntpstream->netstream,size,hsiz) : NIL;
+}
+
+
+/* NNTP per-message modify flag
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void nntp_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ if (!LOCAL->dirty) { /* only bother checking if not dirty yet */
+ if (elt->valid) { /* if done, see if deleted changed */
+ if (elt->sequence != elt->deleted) LOCAL->dirty = T;
+ elt->sequence = T; /* leave the sequence set */
+ }
+ /* note current setting of deleted flag */
+ else elt->sequence = elt->deleted;
+ }
+}
+
+/* NNTP search messages
+ * Accepts: mail stream
+ * character set
+ * search program
+ * option flags
+ * Returns: T on success, NIL on failure
+ */
+
+long nntp_search (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,long flags)
+{
+ unsigned long i;
+ MESSAGECACHE *elt;
+ OVERVIEW ov;
+ char *msg;
+ /* make sure that charset is good */
+ if (msg = utf8_badcharset (charset)) {
+ MM_LOG (msg,ERROR); /* output error */
+ fs_give ((void **) &msg);
+ return NIL;
+ }
+ utf8_searchpgm (pgm,charset);
+ if (flags & SO_OVERVIEW) { /* only if specified to use overview */
+ /* identify messages that will be searched */
+ for (i = 1; i <= stream->nmsgs; ++i)
+ mail_elt (stream,i)->sequence = nntp_search_msg (stream,i,pgm,NIL);
+ nntp_overview (stream,NIL); /* load the overview cache */
+ }
+ /* init in case no overview at cleanup */
+ memset ((void *) &ov,0,sizeof (OVERVIEW));
+ /* otherwise do default search */
+ for (i = 1; i <= stream->nmsgs; ++i) {
+ if (((flags & SO_OVERVIEW) && ((elt = mail_elt (stream,i))->sequence) &&
+ nntp_parse_overview (&ov,(char *) elt->private.spare.ptr,elt)) ?
+ nntp_search_msg (stream,i,pgm,&ov) :
+ mail_search_msg (stream,i,NIL,pgm)) {
+ if (flags & SE_UID) mm_searched (stream,mail_uid (stream,i));
+ else { /* mark as searched, notify mail program */
+ mail_elt (stream,i)->searched = T;
+ if (!stream->silent) mm_searched (stream,i);
+ }
+ }
+ /* clean up overview data */
+ if (ov.from) mail_free_address (&ov.from);
+ if (ov.subject) fs_give ((void **) &ov.subject);
+ }
+ return LONGT;
+}
+
+/* NNTP search message
+ * Accepts: MAIL stream
+ * message number
+ * search program
+ * overview to search (NIL means preliminary pass)
+ * Returns: T if found, NIL otherwise
+ */
+
+long nntp_search_msg (MAILSTREAM *stream,unsigned long msgno,SEARCHPGM *pgm,
+ OVERVIEW *ov)
+{
+ unsigned short d;
+ unsigned long now = (unsigned long) time (0);
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ SEARCHHEADER *hdr;
+ SEARCHOR *or;
+ SEARCHPGMLIST *not;
+ if (pgm->msgno || pgm->uid) { /* message set searches */
+ SEARCHSET *set;
+ /* message sequences */
+ if (set = pgm->msgno) { /* must be inside this sequence */
+ while (set) { /* run down until find matching range */
+ if (set->last ? ((msgno < set->first) || (msgno > set->last)) :
+ msgno != set->first) set = set->next;
+ else break;
+ }
+ if (!set) return NIL; /* not found within sequence */
+ }
+ if (set = pgm->uid) { /* must be inside this sequence */
+ unsigned long uid = mail_uid (stream,msgno);
+ while (set) { /* run down until find matching range */
+ if (set->last ? ((uid < set->first) || (uid > set->last)) :
+ uid != set->first) set = set->next;
+ else break;
+ }
+ if (!set) return NIL; /* not found within sequence */
+ }
+ }
+
+ /* Fast data searches */
+ /* message flags */
+ if ((pgm->answered && !elt->answered) ||
+ (pgm->unanswered && elt->answered) ||
+ (pgm->deleted && !elt->deleted) ||
+ (pgm->undeleted && elt->deleted) ||
+ (pgm->draft && !elt->draft) ||
+ (pgm->undraft && elt->draft) ||
+ (pgm->flagged && !elt->flagged) ||
+ (pgm->unflagged && elt->flagged) ||
+ (pgm->recent && !elt->recent) ||
+ (pgm->old && elt->recent) ||
+ (pgm->seen && !elt->seen) ||
+ (pgm->unseen && elt->seen)) return NIL;
+ /* keywords */
+ if ((pgm->keyword && !mail_search_keyword (stream,elt,pgm->keyword,LONGT)) ||
+ (pgm->unkeyword && mail_search_keyword (stream,elt,pgm->unkeyword,NIL)))
+ return NIL;
+ if (ov) { /* only do this if real searching */
+ MESSAGECACHE delt;
+ /* size ranges */
+ if ((pgm->larger && (ov->optional.octets <= pgm->larger)) ||
+ (pgm->smaller && (ov->optional.octets >= pgm->smaller))) return NIL;
+ /* date ranges */
+ if ((pgm->sentbefore || pgm->senton || pgm->sentsince ||
+ pgm->before || pgm->on || pgm->since) &&
+ (!mail_parse_date (&delt,ov->date) ||
+ !(d = mail_shortdate (delt.year,delt.month,delt.day)) ||
+ (pgm->sentbefore && (d >= pgm->sentbefore)) ||
+ (pgm->senton && (d != pgm->senton)) ||
+ (pgm->sentsince && (d < pgm->sentsince)) ||
+ (pgm->before && (d >= pgm->before)) ||
+ (pgm->on && (d != pgm->on)) ||
+ (pgm->since && (d < pgm->since)))) return NIL;
+ if (pgm->older || pgm->younger) {
+ unsigned long msgd = mail_longdate (elt);
+ if (pgm->older && msgd > (now - pgm->older)) return NIL;
+ if (pgm->younger && msgd < (now - pgm->younger)) return NIL;
+ }
+ if ((pgm->from && !mail_search_addr (ov->from,pgm->from)) ||
+ (pgm->subject && !mail_search_header_text (ov->subject,pgm->subject))||
+ (pgm->message_id &&
+ !mail_search_header_text (ov->message_id,pgm->message_id)) ||
+ (pgm->references &&
+ !mail_search_header_text (ov->references,pgm->references)))
+ return NIL;
+
+
+ /* envelope searches */
+ if (pgm->bcc || pgm->cc || pgm->to || pgm->return_path || pgm->sender ||
+ pgm->reply_to || pgm->in_reply_to || pgm->newsgroups ||
+ pgm->followup_to) {
+ ENVELOPE *env = mail_fetchenvelope (stream,msgno);
+ if (!env) return NIL; /* no envelope obtained */
+ /* search headers */
+ if ((pgm->bcc && !mail_search_addr (env->bcc,pgm->bcc)) ||
+ (pgm->cc && !mail_search_addr (env->cc,pgm->cc)) ||
+ (pgm->to && !mail_search_addr (env->to,pgm->to)))
+ return NIL;
+ /* These criteria are not supported by IMAP and have to be emulated */
+ if ((pgm->return_path &&
+ !mail_search_addr (env->return_path,pgm->return_path)) ||
+ (pgm->sender && !mail_search_addr (env->sender,pgm->sender)) ||
+ (pgm->reply_to && !mail_search_addr (env->reply_to,pgm->reply_to)) ||
+ (pgm->in_reply_to &&
+ !mail_search_header_text (env->in_reply_to,pgm->in_reply_to)) ||
+ (pgm->newsgroups &&
+ !mail_search_header_text (env->newsgroups,pgm->newsgroups)) ||
+ (pgm->followup_to &&
+ !mail_search_header_text (env->followup_to,pgm->followup_to)))
+ return NIL;
+ }
+
+ /* search header lines */
+ for (hdr = pgm->header; hdr; hdr = hdr->next) {
+ char *t,*e,*v;
+ SIZEDTEXT s;
+ STRINGLIST sth,stc;
+ sth.next = stc.next = NIL;/* only one at a time */
+ sth.text.data = hdr->line.data;
+ sth.text.size = hdr->line.size;
+ /* get the header text */
+ if ((t = mail_fetch_header (stream,msgno,NIL,&sth,&s.size,
+ FT_INTERNAL | FT_PEEK)) && strchr (t,':')) {
+ if (hdr->text.size) { /* anything matches empty search string */
+ /* non-empty, copy field data */
+ s.data = (unsigned char *) fs_get (s.size + 1);
+ /* for each line */
+ for (v = (char *) s.data, e = t + s.size; t < e;) switch (*t) {
+ default: /* non-continuation, skip leading field name */
+ while ((t < e) && (*t++ != ':'));
+ if ((t < e) && (*t == ':')) t++;
+ case '\t': case ' ': /* copy field data */
+ while ((t < e) && (*t != '\015') && (*t != '\012')) *v++ = *t++;
+ *v++ = '\n'; /* tie off line */
+ while (((*t == '\015') || (*t == '\012')) && (t < e)) t++;
+ }
+ /* calculate true size */
+ s.size = v - (char *) s.data;
+ *v = '\0'; /* tie off results */
+ stc.text.data = hdr->text.data;
+ stc.text.size = hdr->text.size;
+ /* search header */
+ if (mail_search_header (&s,&stc)) fs_give ((void **) &s.data);
+ else { /* search failed */
+ fs_give ((void **) &s.data);
+ return NIL;
+ }
+ }
+ }
+ else return NIL; /* no matching header text */
+ }
+ /* search strings */
+ if ((pgm->text &&
+ !mail_search_text (stream,msgno,NIL,pgm->text,LONGT))||
+ (pgm->body && !mail_search_text (stream,msgno,NIL,pgm->body,NIL)))
+ return NIL;
+ }
+ /* logical conditions */
+ for (or = pgm->or; or; or = or->next)
+ if (!(nntp_search_msg (stream,msgno,or->first,ov) ||
+ nntp_search_msg (stream,msgno,or->second,ov))) return NIL;
+ for (not = pgm->not; not; not = not->next)
+ if (nntp_search_msg (stream,msgno,not->pgm,ov)) return NIL;
+ return T;
+}
+
+/* NNTP sort messages
+ * Accepts: mail stream
+ * character set
+ * search program
+ * sort program
+ * option flags
+ * Returns: vector of sorted message sequences or NIL if error
+ */
+
+unsigned long *nntp_sort (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
+ SORTPGM *pgm,long flags)
+{
+ unsigned long i,start,last;
+ SORTCACHE **sc;
+ mailcache_t mailcache = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL);
+ unsigned long *ret = NIL;
+ sortresults_t sr = (sortresults_t) mail_parameters (NIL,GET_SORTRESULTS,NIL);
+ if (spg) { /* only if a search needs to be done */
+ int silent = stream->silent;
+ stream->silent = T; /* don't pass up mm_searched() events */
+ /* search for messages */
+ mail_search_full (stream,charset,spg,NIL);
+ stream->silent = silent; /* restore silence state */
+ }
+ /* initialize progress counters */
+ pgm->nmsgs = pgm->progress.cached = 0;
+ /* pass 1: count messages to sort */
+ for (i = 1,start = last = 0; i <= stream->nmsgs; ++i)
+ if (mail_elt (stream,i)->searched) {
+ pgm->nmsgs++;
+ /* have this in the sortcache already? */
+ if (!((SORTCACHE *) (*mailcache) (stream,i,CH_SORTCACHE))->date) {
+ /* no, record as last message */
+ last = mail_uid (stream,i);
+ /* and as first too if needed */
+ if (!start) start = last;
+ }
+ }
+ if (pgm->nmsgs) { /* pass 2: load sort cache */
+ sc = nntp_sort_loadcache (stream,pgm,start,last,flags);
+ /* pass 3: sort messages */
+ if (!pgm->abort) ret = mail_sort_cache (stream,pgm,sc,flags);
+ fs_give ((void **) &sc); /* don't need sort vector any more */
+ }
+ /* empty sort results */
+ else ret = (unsigned long *) memset (fs_get (sizeof (unsigned long)),0,
+ sizeof (unsigned long));
+ /* also return via callback if requested */
+ if (sr) (*sr) (stream,ret,pgm->nmsgs);
+ return ret;
+}
+
+/* Mail load sortcache
+ * Accepts: mail stream, already searched
+ * sort program
+ * first UID to OVER
+ * last UID to OVER
+ * option flags
+ * Returns: vector of sortcache pointers matching search
+ */
+
+SORTCACHE **nntp_sort_loadcache (MAILSTREAM *stream,SORTPGM *pgm,
+ unsigned long start,unsigned long last,
+ long flags)
+{
+ unsigned long i;
+ char c,*s,*t,*v,tmp[MAILTMPLEN];
+ SORTPGM *pg;
+ SORTCACHE **sc,*r;
+ MESSAGECACHE telt;
+ ADDRESS *adr = NIL;
+ mailcache_t mailcache = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL);
+ /* verify that the sortpgm is OK */
+ for (pg = pgm; pg; pg = pg->next) switch (pg->function) {
+ case SORTARRIVAL: /* sort by arrival date */
+ case SORTSIZE: /* sort by message size */
+ case SORTDATE: /* sort by date */
+ case SORTFROM: /* sort by first from */
+ case SORTSUBJECT: /* sort by subject */
+ break;
+ case SORTTO: /* sort by first to */
+ mm_notify (stream,"[NNTPSORT] Can't do To-field sorting in NNTP",WARN);
+ break;
+ case SORTCC: /* sort by first cc */
+ mm_notify (stream,"[NNTPSORT] Can't do cc-field sorting in NNTP",WARN);
+ break;
+ default:
+ fatal ("Unknown sort function");
+ }
+
+ if (start) { /* messages need to be loaded in sortcache? */
+ /* yes, build range */
+ if (start != last) sprintf (tmp,"%lu-%lu",start,last);
+ else sprintf (tmp,"%lu",start);
+ /* get it from the NNTP server */
+ if (!nntp_over (stream,tmp)) return mail_sort_loadcache (stream,pgm);
+ while ((s = net_getline (LOCAL->nntpstream->netstream)) && strcmp (s,".")){
+ /* death to embedded newlines */
+ for (t = v = s; c = *v++;) if ((c != '\012') && (c != '\015')) *t++ = c;
+ *t++ = '\0'; /* tie off resulting string */
+ /* parse OVER response */
+ if ((i = mail_msgno (stream,atol (s))) &&
+ (t = strchr (s,'\t')) && (v = strchr (++t,'\t'))) {
+ *v++ = '\0'; /* tie off subject */
+ /* put stripped subject in sortcache */
+ r = (SORTCACHE *) (*mailcache) (stream,i,CH_SORTCACHE);
+ r->refwd = mail_strip_subject (t,&r->subject);
+ if (t = strchr (v,'\t')) {
+ *t++ = '\0'; /* tie off from */
+ if (adr = rfc822_parse_address (&adr,adr,&v,BADHOST,0)) {
+ r->from = adr->mailbox;
+ adr->mailbox = NIL;
+ mail_free_address (&adr);
+ }
+ if (v = strchr (t,'\t')) {
+ *v++ = '\0'; /* tie off date */
+ if (mail_parse_date (&telt,t)) r->date = mail_longdate (&telt);
+ if ((v = strchr (v,'\t')) && (v = strchr (++v,'\t')))
+ r->size = atol (++v);
+ }
+ }
+ }
+ fs_give ((void **) &s);
+ }
+ if (s) fs_give ((void **) &s);
+ }
+
+ /* calculate size of sortcache index */
+ i = pgm->nmsgs * sizeof (SORTCACHE *);
+ /* instantiate the index */
+ sc = (SORTCACHE **) memset (fs_get ((size_t) i),0,(size_t) i);
+ /* see what needs to be loaded */
+ for (i = 1; !pgm->abort && (i <= stream->nmsgs); i++)
+ if ((mail_elt (stream,i))->searched) {
+ sc[pgm->progress.cached++] =
+ r = (SORTCACHE *) (*mailcache) (stream,i,CH_SORTCACHE);
+ r->pgm = pgm; /* note sort program */
+ r->num = (flags & SE_UID) ? mail_uid (stream,i) : i;
+ if (!r->date) r->date = r->num;
+ if (!r->arrival) r->arrival = mail_uid (stream,i);
+ if (!r->size) r->size = 1;
+ if (!r->from) r->from = cpystr ("");
+ if (!r->to) r->to = cpystr ("");
+ if (!r->cc) r->cc = cpystr ("");
+ if (!r->subject) r->subject = cpystr ("");
+ }
+ return sc;
+}
+
+
+/* NNTP thread messages
+ * Accepts: mail stream
+ * thread type
+ * character set
+ * search program
+ * option flags
+ * Returns: thread node tree
+ */
+
+THREADNODE *nntp_thread (MAILSTREAM *stream,char *type,char *charset,
+ SEARCHPGM *spg,long flags)
+{
+ return mail_thread_msgs (stream,type,charset,spg,flags,nntp_sort);
+}
+
+/* NNTP ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long nntp_ping (MAILSTREAM *stream)
+{
+ return (nntp_send (LOCAL->nntpstream,"STAT",NIL) != NNTPSOFTFATAL);
+}
+
+
+/* NNTP check mailbox
+ * Accepts: MAIL stream
+ */
+
+void nntp_check (MAILSTREAM *stream)
+{
+ /* never do if no updates */
+ if (LOCAL->dirty) newsrc_write (LOCAL->name,stream);
+ LOCAL->dirty = NIL;
+}
+
+
+/* NNTP expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T if success, NIL if failure
+ */
+
+long nntp_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ if (!stream->silent) mm_log ("Expunge ignored on readonly mailbox",NIL);
+ return LONGT;
+}
+
+/* NNTP copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * option flags
+ * Returns: T if copy successful, else NIL
+ */
+
+long nntp_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ mm_log ("Copy not valid for NNTP",ERROR);
+ return NIL;
+}
+
+
+/* NNTP append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long nntp_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ mm_log ("Append not valid for NNTP",ERROR);
+ return NIL;
+}
+
+/* NNTP open connection
+ * Accepts: network driver
+ * service host list
+ * port number
+ * service name
+ * NNTP open options
+ * Returns: SEND stream on success, NIL on failure
+ */
+
+SENDSTREAM *nntp_open_full (NETDRIVER *dv,char **hostlist,char *service,
+ unsigned long port,long options)
+{
+ SENDSTREAM *stream = NIL;
+ NETSTREAM *netstream = NIL;
+ NETMBX mb;
+ char tmp[MAILTMPLEN];
+ long extok = LONGT;
+ NETDRIVER *ssld = (NETDRIVER *) mail_parameters (NIL,GET_SSLDRIVER,NIL);
+ sslstart_t stls = (sslstart_t) mail_parameters (NIL,GET_SSLSTART,NIL);
+ if (!(hostlist && *hostlist)) mm_log ("Missing NNTP service host",ERROR);
+ else do { /* try to open connection */
+ sprintf (tmp,"{%.200s/%.20s}",*hostlist,service ? service : "nntp");
+ if (!mail_valid_net_parse (tmp,&mb) || mb.anoflag) {
+ sprintf (tmp,"Invalid host specifier: %.80s",*hostlist);
+ mm_log (tmp,ERROR);
+ }
+ else { /* light tryssl flag if requested */
+ mb.trysslflag = (options & NOP_TRYSSL) ? T : NIL;
+ /* default port */
+ if (mb.port) port = mb.port;
+ else if (!port) port = nntp_port ? nntp_port : NNTPTCPPORT;
+ if (netstream = /* try to open ordinary connection */
+ net_open (&mb,dv,port,
+ (NETDRIVER *) mail_parameters (NIL,GET_SSLDRIVER,NIL),
+ "*nntps",nntp_sslport ? nntp_sslport : NNTPSSLPORT)) {
+ stream = (SENDSTREAM *) fs_get (sizeof (SENDSTREAM));
+ /* initialize stream */
+ memset ((void *) stream,0,sizeof (SENDSTREAM));
+ stream->netstream = netstream;
+ stream->host = cpystr ((long) mail_parameters (NIL,GET_TRUSTDNS,NIL) ?
+ net_host (netstream) : mb.host);
+ stream->debug = (mb.dbgflag || (options & NOP_DEBUG)) ? T : NIL;
+ if (mb.loser) stream->loser = T;
+ /* process greeting */
+ switch ((int) nntp_reply (stream)) {
+ case NNTPGREET: /* allow posting */
+ NNTP.post = T;
+ mm_notify (NIL,stream->reply + 4,(long) NIL);
+ break;
+ case NNTPGREETNOPOST: /* posting not allowed, must be readonly */
+ NNTP.post = NIL;
+ break;
+ default:
+ mm_log (stream->reply,ERROR);
+ stream = nntp_close (stream);
+ break;
+ }
+ }
+ }
+ } while (!stream && *++hostlist);
+
+ /* get extensions */
+ if (stream && extok)
+ extok = nntp_extensions (stream,(mb.secflag ? AU_SECURE : NIL) |
+ (mb.authuser[0] ? AU_AUTHUSER : NIL));
+ if (stream && !dv && stls && NNTP.ext.starttls &&
+ !mb.sslflag && !mb.notlsflag &&
+ (nntp_send_work (stream,"STARTTLS",NNTP.ext.multidomain ? mb.host : NIL)
+ == NNTPTLSSTART)) {
+ mb.tlsflag = T; /* TLS OK, get into TLS at this end */
+ stream->netstream->dtb = ssld;
+ /* negotiate TLS */
+ if (stream->netstream->stream =
+ (*stls) (stream->netstream->stream,mb.host,
+ (mb.tlssslv23 ? NIL : NET_TLSCLIENT) |
+ (mb.novalidate ? NET_NOVALIDATECERT:NIL)))
+ extok = nntp_extensions (stream,(mb.secflag ? AU_SECURE : NIL) |
+ (mb.authuser[0] ? AU_AUTHUSER : NIL));
+ else {
+ sprintf (tmp,"Unable to negotiate TLS with this server: %.80s",mb.host);
+ mm_log (tmp,ERROR);
+ /* close without doing QUIT */
+ if (stream->netstream) net_close (stream->netstream);
+ stream->netstream = NIL;
+ stream = nntp_close (stream);
+ }
+ }
+ else if (mb.tlsflag) { /* user specified /tls but can't do it */
+ mm_log ("Unable to negotiate TLS with this server",ERROR);
+ return NIL;
+ }
+ if (stream) { /* have a session? */
+ if (mb.user[0]) { /* yes, have user name? */
+ if ((long) mail_parameters (NIL,GET_TRUSTDNS,NIL)) {
+ /* remote name for authentication */
+ strncpy (mb.host,(long) mail_parameters (NIL,GET_SASLUSESPTRNAME,NIL) ?
+ net_remotehost (netstream) : net_host (netstream),
+ NETMAXHOST-1);
+ mb.host[NETMAXHOST-1] = '\0';
+ }
+ if (!nntp_send_auth_work (stream,&mb,tmp,NIL))
+ stream = nntp_close (stream);
+ }
+ /* authenticate if no-post and not readonly */
+ else if (!(NNTP.post || (options & NOP_READONLY) ||
+ nntp_send_auth (stream,NIL))) stream = nntp_close (stream);
+ }
+
+ /* in case server demands MODE READER */
+ if (stream) switch ((int) nntp_send_work (stream,"MODE","READER")) {
+ case NNTPGREET:
+ NNTP.post = T;
+ break;
+ case NNTPGREETNOPOST:
+ NNTP.post = NIL;
+ break;
+ case NNTPWANTAUTH: /* server wants auth first, do so and retry */
+ case NNTPWANTAUTH2: /* remote name for authentication */
+ if ((long) mail_parameters (NIL,GET_TRUSTDNS,NIL)) {
+ strncpy (mb.host,(long) mail_parameters (NIL,GET_SASLUSESPTRNAME,NIL) ?
+ net_remotehost (netstream) : net_host (netstream),NETMAXHOST-1);
+ mb.host[NETMAXHOST-1] = '\0';
+ }
+ if (nntp_send_auth_work (stream,&mb,tmp,NIL))
+ switch ((int) nntp_send (stream,"MODE","READER")) {
+ case NNTPGREET:
+ NNTP.post = T;
+ break;
+ case NNTPGREETNOPOST:
+ NNTP.post = NIL;
+ break;
+ }
+ else stream = nntp_close (stream);
+ break;
+ }
+ if (stream) { /* looks like we have a stream? */
+ /* yes, make sure can post if not readonly */
+ if (!(NNTP.post || (options & NOP_READONLY))) stream = nntp_close (stream);
+ else if (extok) nntp_extensions (stream,(mb.secflag ? AU_SECURE : NIL) |
+ (mb.authuser[0] ? AU_AUTHUSER : NIL));
+ }
+ return stream;
+}
+
+/* NNTP extensions
+ * Accepts: stream
+ * authenticator flags
+ * Returns: T on success, NIL on failure
+ */
+
+long nntp_extensions (SENDSTREAM *stream,long flags)
+{
+ unsigned long i;
+ char *t,*r,*args;
+ /* zap all old extensions */
+ memset (&NNTP.ext,0,sizeof (NNTP.ext));
+ if (stream->loser) return NIL;/* nothing at all for losers */
+ /* get server extensions */
+ switch ((int) nntp_send_work (stream,"LIST","EXTENSIONS")) {
+ case NNTPEXTOK: /* what NNTP base spec says */
+ case NNTPGLIST: /* some servers do this instead */
+ break;
+ default: /* no LIST EXTENSIONS on this server */
+ return NIL;
+ }
+ NNTP.ext.ok = T; /* server offers extensions */
+ while ((t = net_getline (stream->netstream)) && (t[1] || (*t != '.'))) {
+ if (stream->debug) mm_dlog (t);
+ /* get optional capability arguments */
+ if (args = strchr (t,' ')) *args++ = '\0';
+ if (!compare_cstring (t,"LISTGROUP")) NNTP.ext.listgroup = T;
+ else if (!compare_cstring (t,"OVER")) NNTP.ext.over = T;
+ else if (!compare_cstring (t,"HDR")) NNTP.ext.hdr = T;
+ else if (!compare_cstring (t,"PAT")) NNTP.ext.pat = T;
+ else if (!compare_cstring (t,"STARTTLS")) NNTP.ext.starttls = T;
+ else if (!compare_cstring (t,"MULTIDOMAIN")) NNTP.ext.multidomain = T;
+
+ else if (!compare_cstring (t,"AUTHINFO") && args) {
+ char *sasl = NIL;
+ for (args = strtok_r (args," ",&r); args; args = strtok_r (NIL," ",&r)) {
+ if (!compare_cstring (args,"USER")) NNTP.ext.authuser = T;
+ else if (((args[0] == 'S') || (args[0] == 's')) &&
+ ((args[1] == 'A') || (args[1] == 'a')) &&
+ ((args[2] == 'S') || (args[2] == 's')) &&
+ ((args[3] == 'L') || (args[3] == 'l')) && (args[4] == ':'))
+ sasl = args + 5;
+ }
+ if (sasl) { /* if SASL, look up authenticators */
+ for (sasl = strtok_r (sasl,",",&r); sasl; sasl = strtok_r (NIL,",",&r))
+ if ((i = mail_lookup_auth_name (sasl,flags)) &&
+ (--i < MAXAUTHENTICATORS))
+ NNTP.ext.sasl |= (1 << i);
+ /* disable LOGIN if PLAIN also advertised */
+ if ((i = mail_lookup_auth_name ("PLAIN",NIL)) &&
+ (--i < MAXAUTHENTICATORS) && (NNTP.ext.sasl & (1 << i)) &&
+ (i = mail_lookup_auth_name ("LOGIN",NIL)) &&
+ (--i < MAXAUTHENTICATORS)) NNTP.ext.sasl &= ~(1 << i);
+ }
+ }
+ fs_give ((void **) &t);
+ }
+ if (t) { /* flush end of text indicator */
+ if (stream->debug) mm_dlog (t);
+ fs_give ((void **) &t);
+ }
+ return LONGT;
+}
+
+/* NNTP close connection
+ * Accepts: SEND stream
+ * Returns: NIL always
+ */
+
+SENDSTREAM *nntp_close (SENDSTREAM *stream)
+{
+ if (stream) { /* send "QUIT" */
+ if (stream->netstream) nntp_send (stream,"QUIT",NIL);
+ /* do close actions */
+ if (stream->netstream) net_close (stream->netstream);
+ if (stream->host) fs_give ((void **) &stream->host);
+ if (stream->reply) fs_give ((void **) &stream->reply);
+ fs_give ((void **) &stream);/* flush the stream */
+ }
+ return NIL;
+}
+
+/* NNTP deliver news
+ * Accepts: SEND stream
+ * message envelope
+ * message body
+ * Returns: T on success, NIL on failure
+ */
+
+long nntp_mail (SENDSTREAM *stream,ENVELOPE *env,BODY *body)
+{
+ long ret;
+ RFC822BUFFER buf;
+ char *s,path[MAILTMPLEN],tmp[SENDBUFLEN+1];
+ long error = NIL;
+ long retry = NIL;
+ buf.f = nntp_soutr; /* initialize buffer */
+ buf.s = stream->netstream;
+ buf.end = (buf.beg = buf.cur = tmp) + SENDBUFLEN;
+ tmp[SENDBUFLEN] = '\0'; /* must have additional null guard byte */
+ /* Gabba gabba hey, we need some brain damage to send netnews!!!
+ *
+ * First, we give ourselves a frontal lobotomy, and put in some UUCP
+ * syntax. It doesn't matter that it's completely bogus UUCP, and
+ * that UUCP has nothing to do with anything we're doing. It's been
+ * alleged that "Path: not-for-mail" is also acceptable, but we won't
+ * make assumptions unless the user says so.
+ *
+ * Second, we bop ourselves on the head with a ball-peen hammer. How
+ * dare we be so presumptious as to insert a *comment* in a Date:
+ * header line. Why, we were actually trying to be nice to a human
+ * by giving a symbolic timezone (such as PST) in addition to a
+ * numeric timezone (such as -0800). But the gods of news transport
+ * will have none of this. Unix weenies, tried and true, rule!!!
+ *
+ * Third, Netscape Collabra server doesn't give the NNTPWANTAUTH error
+ * until after requesting and receiving the entire message. So we can't
+ * call rely upon nntp_send() to do the auth retry.
+ */
+ /* RFC-1036 requires this cretinism */
+ sprintf (path,"Path: %s!%s\015\012",net_localhost (stream->netstream),
+ env->sender ? env->sender->mailbox :
+ (env->from ? env->from->mailbox : "not-for-mail"));
+ /* here's another cretinism */
+ if (s = strstr (env->date," (")) *s = NIL;
+ do if ((ret = nntp_send_work (stream,"POST",NIL)) == NNTPREADY)
+ /* output data, return success status */
+ ret = (net_soutr (stream->netstream,
+ nntp_hidepath ? "Path: not-for-mail\015\012" : path) &&
+ rfc822_output_full (&buf,env,body,T)) ?
+ nntp_send_work (stream,".",NIL) :
+ nntp_fake (stream,"NNTP connection broken (message text)");
+ while (((ret == NNTPWANTAUTH) || (ret == NNTPWANTAUTH2)) &&
+ nntp_send_auth (stream,LONGT));
+ if (s) *s = ' '; /* put the comment in the date back */
+ if (ret == NNTPOK) return LONGT;
+ else if (ret < 400) { /* if not an error reply */
+ sprintf (tmp,"Unexpected NNTP posting reply code %ld",ret);
+ mm_log (tmp,WARN); /* so someone looks at this eventually */
+ if ((ret >= 200) && (ret < 300)) return LONGT;
+ }
+ return NIL;
+}
+
+/* NNTP send command
+ * Accepts: SEND stream
+ * text
+ * Returns: reply code
+ */
+
+long nntp_send (SENDSTREAM *stream,char *command,char *args)
+{
+ long ret;
+ switch ((int) (ret = nntp_send_work (stream,command,args))) {
+ case NNTPWANTAUTH: /* authenticate and retry */
+ case NNTPWANTAUTH2:
+ if (nntp_send_auth (stream,LONGT))
+ ret = nntp_send_work (stream,command,args);
+ else { /* we're probably hosed, nuke the session */
+ nntp_send (stream,"QUIT",NIL);
+ /* close net connection */
+ if (stream->netstream) net_close (stream->netstream);
+ stream->netstream = NIL;
+ }
+ default: /* all others just return */
+ break;
+ }
+ return ret;
+}
+
+
+/* NNTP send command worker routine
+ * Accepts: SEND stream
+ * text
+ * Returns: reply code
+ */
+
+long nntp_send_work (SENDSTREAM *stream,char *command,char *args)
+{
+ long ret;
+ char *s = (char *) fs_get (strlen (command) + (args ? strlen (args) + 1 : 0)
+ + 3);
+ if (!stream->netstream) ret = nntp_fake (stream,"NNTP connection lost");
+ else { /* build the complete command */
+ if (args) sprintf (s,"%s %s",command,args);
+ else strcpy (s,command);
+ if (stream->debug) mail_dlog (s,stream->sensitive);
+ strcat (s,"\015\012");
+ /* send the command */
+ ret = net_soutr (stream->netstream,s) ? nntp_reply (stream) :
+ nntp_fake (stream,"NNTP connection broken (command)");
+ }
+ fs_give ((void **) &s);
+ return ret;
+}
+
+/* NNTP send authentication if needed
+ * Accepts: SEND stream
+ * flags (non-NIL to get new extensions)
+ * Returns: T if need to redo command, NIL otherwise
+ */
+
+long nntp_send_auth (SENDSTREAM *stream,long flags)
+{
+ NETMBX mb;
+ char tmp[MAILTMPLEN];
+ /* remote name for authentication */
+ sprintf (tmp,"{%.200s/nntp",(long) mail_parameters (NIL,GET_TRUSTDNS,NIL) ?
+ ((long) mail_parameters (NIL,GET_SASLUSESPTRNAME,NIL) ?
+ net_remotehost (stream->netstream) : net_host (stream->netstream)):
+ stream->host);
+ if (stream->netstream->dtb ==
+ (NETDRIVER *) mail_parameters (NIL,GET_SSLDRIVER,NIL))
+ strcat (tmp,"/ssl");
+ strcat (tmp,"}<none>");
+ mail_valid_net_parse (tmp,&mb);
+ return nntp_send_auth_work (stream,&mb,tmp,flags);
+}
+
+/* NNTP send authentication worker routine
+ * Accepts: SEND stream
+ * NETMBX structure
+ * scratch buffer of length MAILTMPLEN
+ * flags (non-NIL to get new extensions)
+ * Returns: T if authenticated, NIL otherwise
+ */
+
+long nntp_send_auth_work (SENDSTREAM *stream,NETMBX *mb,char *pwd,long flags)
+{
+ unsigned long trial,auths;
+ char tmp[MAILTMPLEN],usr[MAILTMPLEN];
+ AUTHENTICATOR *at;
+ char *lsterr = NIL;
+ long ret = NIL;
+ /* try SASL first */
+ for (auths = NNTP.ext.sasl, stream->saslcancel = NIL;
+ !ret && stream->netstream && auths &&
+ (at = mail_lookup_auth (find_rightmost_bit (&auths) + 1)); ) {
+ if (lsterr) { /* previous authenticator failed? */
+ sprintf (tmp,"Retrying using %s authentication after %.80s",
+ at->name,lsterr);
+ mm_log (tmp,NIL);
+ fs_give ((void **) &lsterr);
+ }
+ trial = 0; /* initial trial count */
+ tmp[0] = '\0'; /* empty buffer */
+ if (stream->netstream) do {
+ if (lsterr) {
+ sprintf (tmp,"Retrying %s authentication after %.80s",at->name,lsterr);
+ mm_log (tmp,WARN);
+ fs_give ((void **) &lsterr);
+ }
+ stream->saslcancel = NIL;
+ if (nntp_send (stream,"AUTHINFO SASL",at->name) == NNTPCHALLENGE) {
+ /* hide client authentication responses */
+ if (!(at->flags & AU_SECURE)) stream->sensitive = T;
+ if ((*at->client) (nntp_challenge,nntp_response,"nntp",mb,stream,
+ &trial,usr)) {
+ if (stream->replycode == NNTPAUTHED) ret = LONGT;
+ /* if main program requested cancellation */
+ else if (!trial) mm_log ("NNTP Authentication cancelled",ERROR);
+ }
+ stream->sensitive = NIL;/* unhide */
+ }
+ /* remember response if error and no cancel */
+ if (!ret && trial) lsterr = cpystr (stream->reply);
+ } while (!ret && stream->netstream && trial &&
+ (trial < nntp_maxlogintrials));
+ }
+
+ if (lsterr) { /* SAIL failed? */
+ if (!stream->saslcancel) { /* don't do this if a cancel */
+ sprintf (tmp,"Can not authenticate to NNTP server: %.80s",lsterr);
+ mm_log (tmp,ERROR);
+ }
+ fs_give ((void **) &lsterr);
+ }
+ else if (mb->secflag) /* no SASL, can't do /secure */
+ mm_log ("Can't do secure authentication with this server",ERROR);
+ else if (mb->authuser[0]) /* or /authuser */
+ mm_log ("Can't do /authuser with this server",ERROR);
+ /* Always try AUTHINFO USER, even if NNTP.ext.authuser isn't set. There
+ * are servers that require it but don't return it as an extension.
+ */
+ else for (trial = 0, pwd[0] = 'x';
+ !ret && pwd[0] && (trial < nntp_maxlogintrials) &&
+ stream->netstream; ) {
+ pwd[0] = NIL; /* get user name and password */
+ mm_login (mb,usr,pwd,trial++);
+ /* do the authentication */
+ if (pwd[0]) switch ((int) nntp_send_work (stream,"AUTHINFO USER",usr)) {
+ case NNTPBADCMD: /* give up if unrecognized command */
+ mm_log (NNTP.ext.authuser ? stream->reply :
+ "Can't do AUTHINFO USER to this server",ERROR);
+ trial = nntp_maxlogintrials;
+ break;
+ case NNTPAUTHED: /* successful authentication */
+ ret = LONGT; /* guess no password was needed */
+ break;
+ case NNTPWANTPASS: /* wants password */
+ stream->sensitive = T; /* hide this command */
+ if (nntp_send_work (stream,"AUTHINFO PASS",pwd) == NNTPAUTHED)
+ ret = LONGT; /* password OK */
+ stream->sensitive = NIL; /* unhide */
+ if (ret) break; /* OK if successful */
+ default: /* authentication failed */
+ mm_log (stream->reply,WARN);
+ if (trial == nntp_maxlogintrials)
+ mm_log ("Too many NNTP authentication failures",ERROR);
+ }
+ /* user refused to give a password */
+ else mm_log ("Login aborted",ERROR);
+ }
+ memset (pwd,0,MAILTMPLEN); /* erase password */
+ /* get new extensions if needed */
+ if (ret && flags) nntp_extensions (stream,(mb->secflag ? AU_SECURE : NIL) |
+ (mb->authuser[0] ? AU_AUTHUSER : NIL));
+ return ret;
+}
+
+/* Get challenge to authenticator in binary
+ * Accepts: stream
+ * pointer to returned size
+ * Returns: challenge or NIL if not challenge
+ */
+
+void *nntp_challenge (void *s,unsigned long *len)
+{
+ char tmp[MAILTMPLEN];
+ void *ret = NIL;
+ SENDSTREAM *stream = (SENDSTREAM *) s;
+ if ((stream->replycode == NNTPCHALLENGE) &&
+ !(ret = rfc822_base64 ((unsigned char *) stream->reply + 4,
+ strlen (stream->reply + 4),len))) {
+ sprintf (tmp,"NNTP SERVER BUG (invalid challenge): %.80s",stream->reply+4);
+ mm_log (tmp,ERROR);
+ }
+ return ret;
+}
+
+
+/* Send authenticator response in BASE64
+ * Accepts: MAIL stream
+ * string to send
+ * length of string
+ * Returns: T, always
+ */
+
+long nntp_response (void *s,char *response,unsigned long size)
+{
+ SENDSTREAM *stream = (SENDSTREAM *) s;
+ unsigned long i,j;
+ char *t,*u;
+ if (response) { /* make CRLFless BASE64 string */
+ if (size) {
+ for (t = (char *) rfc822_binary ((void *) response,size,&i),u = t,j = 0;
+ j < i; j++) if (t[j] > ' ') *u++ = t[j];
+ *u = '\0'; /* tie off string */
+ i = nntp_send_work (stream,t,NIL);
+ fs_give ((void **) &t);
+ }
+ else i = nntp_send_work (stream,"",NIL);
+ }
+ else { /* abort requested */
+ i = nntp_send_work (stream,"*",NIL);
+ stream->saslcancel = T; /* mark protocol-requested SASL cancel */
+ }
+ return LONGT;
+}
+
+/* NNTP get reply
+ * Accepts: SEND stream
+ * Returns: reply code
+ */
+
+long nntp_reply (SENDSTREAM *stream)
+{
+ /* flush old reply */
+ if (stream->reply) fs_give ((void **) &stream->reply);
+ /* get reply */
+ if (!(stream->reply = net_getline (stream->netstream)))
+ return nntp_fake (stream,"NNTP connection broken (response)");
+ if (stream->debug) mm_dlog (stream->reply);
+ /* handle continuation by recursion */
+ if (stream->reply[3] == '-') return nntp_reply (stream);
+ /* return response code */
+ return stream->replycode = atol (stream->reply);
+}
+
+
+/* NNTP set fake error
+ * Accepts: SEND stream
+ * error text
+ * Returns: error code
+ */
+
+long nntp_fake (SENDSTREAM *stream,char *text)
+{
+ if (stream->netstream) { /* close net connection if still open */
+ net_close (stream->netstream);
+ stream->netstream = NIL;
+ }
+ /* flush any old reply */
+ if (stream->reply) fs_give ((void **) &stream->reply);
+ /* set up pseudo-reply string */
+ stream->reply = (char *) fs_get (20+strlen (text));
+ sprintf (stream->reply,"%ld %s",NNTPSOFTFATAL,text);
+ return NNTPSOFTFATAL; /* return error code */
+}
+
+/* NNTP filter mail
+ * Accepts: stream
+ * string
+ * Returns: T on success, NIL on failure
+ */
+
+long nntp_soutr (void *stream,char *s)
+{
+ char c,*t;
+ /* "." on first line */
+ if (s[0] == '.') net_soutr (stream,".");
+ /* find lines beginning with a "." */
+ while (t = strstr (s,"\015\012.")) {
+ c = *(t += 3); /* remember next character after "." */
+ *t = '\0'; /* tie off string */
+ /* output prefix */
+ if (!net_soutr (stream,s)) return NIL;
+ *t = c; /* restore delimiter */
+ s = t - 1; /* push pointer up to the "." */
+ }
+ /* output remainder of text */
+ return *s ? net_soutr (stream,s) : T;
+}
diff --git a/imap/src/c-client/nntp.h b/imap/src/c-client/nntp.h
new file mode 100644
index 00000000..445ae4d7
--- /dev/null
+++ b/imap/src/c-client/nntp.h
@@ -0,0 +1,56 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Network News Transfer Protocol (NNTP) routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 February 1992
+ * Last Edited: 30 August 2006
+ */
+
+/* Constants (should be in nntp.c) */
+
+#define NNTPTCPPORT (long) 119 /* assigned TCP contact port */
+
+
+/* NNTP open options
+ * For compatibility with the past, NOP_DEBUG must always be 1.
+ */
+
+#define NOP_DEBUG (long) 0x1 /* debug protocol negotiations */
+#define NOP_READONLY (long) 0x2 /* read-only open */
+#define NOP_TRYSSL (long) 0x4 /* try SSL first */
+ /* reserved for application use */
+#define NOP_RESERVED (unsigned long) 0xff000000
+
+
+/* Compatibility support names */
+
+#define nntp_open(hostlist,options) \
+ nntp_open_full (NIL,hostlist,"nntp",NIL,options)
+
+
+/* Function prototypes */
+
+SENDSTREAM *nntp_open_full (NETDRIVER *dv,char **hostlist,char *service,
+ unsigned long port,long options);
+SENDSTREAM *nntp_close (SENDSTREAM *stream);
+long nntp_mail (SENDSTREAM *stream,ENVELOPE *msg,BODY *body);
diff --git a/imap/src/c-client/pop3.c b/imap/src/c-client/pop3.c
new file mode 100644
index 00000000..58a9ceb6
--- /dev/null
+++ b/imap/src/c-client/pop3.c
@@ -0,0 +1,1091 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Post Office Protocol 3 (POP3) client routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 6 June 1994
+ * Last Edited: 4 April 2007
+ */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <time.h>
+#include "c-client.h"
+#include "flstring.h"
+#include "netmsg.h"
+
+/* Parameters */
+
+#define POP3TCPPORT (long) 110 /* assigned TCP contact port */
+#define POP3SSLPORT (long) 995 /* assigned SSL TCP contact port */
+#define IDLETIMEOUT (long) 10 /* defined in RFC 1939 */
+
+
+/* POP3 I/O stream local data */
+
+typedef struct pop3_local {
+ NETSTREAM *netstream; /* TCP I/O stream */
+ char *response; /* last server reply */
+ char *reply; /* text of last server reply */
+ unsigned long cached; /* current cached message uid */
+ unsigned long hdrsize; /* current cached header size */
+ FILE *txt; /* current cached file descriptor */
+ struct {
+ unsigned int capa : 1; /* server has CAPA, definitely new */
+ unsigned int expire : 1; /* server has EXPIRE */
+ unsigned int logindelay : 1;/* server has LOGIN-DELAY */
+ unsigned int stls : 1; /* server has STLS */
+ unsigned int pipelining : 1;/* server has PIPELINING */
+ unsigned int respcodes : 1; /* server has RESP-CODES */
+ unsigned int top : 1; /* server has TOP */
+ unsigned int uidl : 1; /* server has UIDL */
+ unsigned int user : 1; /* server has USER */
+ char *implementation; /* server implementation string */
+ long delaysecs; /* minimum time between login (neg variable) */
+ long expiredays; /* server-guaranteed minimum retention days */
+ /* supported authenticators */
+ unsigned int sasl : MAXAUTHENTICATORS;
+ } cap;
+ unsigned int sensitive : 1; /* sensitive data in progress */
+ unsigned int loser : 1; /* server is a loser */
+ unsigned int saslcancel : 1; /* SASL cancelled by protocol */
+} POP3LOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((POP3LOCAL *) stream->local)
+
+/* Function prototypes */
+
+DRIVER *pop3_valid (char *name);
+void *pop3_parameters (long function,void *value);
+void pop3_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void pop3_list (MAILSTREAM *stream,char *ref,char *pat);
+void pop3_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long pop3_subscribe (MAILSTREAM *stream,char *mailbox);
+long pop3_unsubscribe (MAILSTREAM *stream,char *mailbox);
+long pop3_create (MAILSTREAM *stream,char *mailbox);
+long pop3_delete (MAILSTREAM *stream,char *mailbox);
+long pop3_rename (MAILSTREAM *stream,char *old,char *newname);
+long pop3_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *pop3_open (MAILSTREAM *stream);
+long pop3_capa (MAILSTREAM *stream,long flags);
+long pop3_auth (MAILSTREAM *stream,NETMBX *mb,char *pwd,char *usr);
+void *pop3_challenge (void *stream,unsigned long *len);
+long pop3_response (void *stream,char *s,unsigned long size);
+void pop3_close (MAILSTREAM *stream,long options);
+void pop3_fetchfast (MAILSTREAM *stream,char *sequence,long flags);
+char *pop3_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *size,
+ long flags);
+long pop3_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+unsigned long pop3_cache (MAILSTREAM *stream,MESSAGECACHE *elt);
+long pop3_ping (MAILSTREAM *stream);
+void pop3_check (MAILSTREAM *stream);
+long pop3_expunge (MAILSTREAM *stream,char *sequence,long options);
+long pop3_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long pop3_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+long pop3_send_num (MAILSTREAM *stream,char *command,unsigned long n);
+long pop3_send (MAILSTREAM *stream,char *command,char *args);
+long pop3_reply (MAILSTREAM *stream);
+long pop3_fake (MAILSTREAM *stream,char *text);
+
+/* POP3 mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER pop3driver = {
+ "pop3", /* driver name */
+ /* driver flags */
+#ifdef INADEQUATE_MEMORY
+ DR_LOWMEM |
+#endif
+ DR_MAIL|DR_NOFAST|DR_CRLF|DR_NOSTICKY|DR_NOINTDATE|DR_NONEWMAIL,
+ (DRIVER *) NIL, /* next driver */
+ pop3_valid, /* mailbox is valid for us */
+ pop3_parameters, /* manipulate parameters */
+ pop3_scan, /* scan mailboxes */
+ pop3_list, /* find mailboxes */
+ pop3_lsub, /* find subscribed mailboxes */
+ pop3_subscribe, /* subscribe to mailbox */
+ pop3_unsubscribe, /* unsubscribe from mailbox */
+ pop3_create, /* create mailbox */
+ pop3_delete, /* delete mailbox */
+ pop3_rename, /* rename mailbox */
+ pop3_status, /* status of mailbox */
+ pop3_open, /* open mailbox */
+ pop3_close, /* close mailbox */
+ pop3_fetchfast, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message structure */
+ pop3_header, /* fetch message header */
+ pop3_text, /* fetch message text */
+ NIL, /* fetch message */
+ NIL, /* unique identifier */
+ NIL, /* message number from UID */
+ NIL, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ pop3_ping, /* ping mailbox to see if still alive */
+ pop3_check, /* check for new messages */
+ pop3_expunge, /* expunge deleted messages */
+ pop3_copy, /* copy messages to another mailbox */
+ pop3_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM pop3proto = {&pop3driver};
+
+ /* driver parameters */
+static unsigned long pop3_maxlogintrials = MAXLOGINTRIALS;
+static long pop3_port = 0;
+static long pop3_sslport = 0;
+
+/* POP3 mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *pop3_valid (char *name)
+{
+ NETMBX mb;
+ return (mail_valid_net_parse (name,&mb) &&
+ !strcmp (mb.service,pop3driver.name) && !mb.authuser[0] &&
+ !compare_cstring (mb.mailbox,"INBOX")) ? &pop3driver : NIL;
+}
+
+
+/* News manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *pop3_parameters (long function,void *value)
+{
+ switch ((int) function) {
+ case SET_MAXLOGINTRIALS:
+ pop3_maxlogintrials = (unsigned long) value;
+ break;
+ case GET_MAXLOGINTRIALS:
+ value = (void *) pop3_maxlogintrials;
+ break;
+ case SET_POP3PORT:
+ pop3_port = (long) value;
+ break;
+ case GET_POP3PORT:
+ value = (void *) pop3_port;
+ break;
+ case SET_SSLPOPPORT:
+ pop3_sslport = (long) value;
+ break;
+ case GET_SSLPOPPORT:
+ value = (void *) pop3_sslport;
+ break;
+ case GET_IDLETIMEOUT:
+ value = (void *) IDLETIMEOUT;
+ break;
+ default:
+ value = NIL; /* error case */
+ break;
+ }
+ return value;
+}
+
+/* POP3 mail scan mailboxes for string
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void pop3_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ char tmp[MAILTMPLEN];
+ if ((ref && *ref) ? /* have a reference */
+ (pop3_valid (ref) && pmatch ("INBOX",pat)) :
+ (mail_valid_net (pat,&pop3driver,NIL,tmp) && pmatch ("INBOX",tmp)))
+ mm_log ("Scan not valid for POP3 mailboxes",ERROR);
+}
+
+
+/* POP3 mail find list of all mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void pop3_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ char tmp[MAILTMPLEN];
+ if (ref && *ref) { /* have a reference */
+ if (pop3_valid (ref) && pmatch ("INBOX",pat)) {
+ strcpy (strchr (strcpy (tmp,ref),'}')+1,"INBOX");
+ mm_list (stream,NIL,tmp,LATT_NOINFERIORS);
+ }
+ }
+ else if (mail_valid_net (pat,&pop3driver,NIL,tmp) && pmatch ("INBOX",tmp)) {
+ strcpy (strchr (strcpy (tmp,pat),'}')+1,"INBOX");
+ mm_list (stream,NIL,tmp,LATT_NOINFERIORS);
+ }
+}
+
+/* POP3 mail find list of subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void pop3_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ void *sdb = NIL;
+ char *s,mbx[MAILTMPLEN];
+ if (*pat == '{') { /* if remote pattern, must be POP3 */
+ if (!pop3_valid (pat)) return;
+ ref = NIL; /* good POP3 pattern, punt reference */
+ }
+ /* if remote reference, must be valid POP3 */
+ if (ref && (*ref == '{') && !pop3_valid (ref)) return;
+ /* kludgy application of reference */
+ if (ref && *ref) sprintf (mbx,"%s%s",ref,pat);
+ else strcpy (mbx,pat);
+
+ if (s = sm_read (&sdb)) do if (pop3_valid (s) && pmatch (s,mbx))
+ mm_lsub (stream,NIL,s,NIL);
+ while (s = sm_read (&sdb)); /* until no more subscriptions */
+}
+
+
+/* POP3 mail subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long pop3_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return sm_subscribe (mailbox);
+}
+
+
+/* POP3 mail unsubscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to delete from subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long pop3_unsubscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return sm_unsubscribe (mailbox);
+}
+
+/* POP3 mail create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long pop3_create (MAILSTREAM *stream,char *mailbox)
+{
+ return NIL; /* never valid for POP3 */
+}
+
+
+/* POP3 mail delete mailbox
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long pop3_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return NIL; /* never valid for POP3 */
+}
+
+
+/* POP3 mail rename mailbox
+ * Accepts: mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long pop3_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ return NIL; /* never valid for POP3 */
+}
+
+/* POP3 status
+ * Accepts: mail stream
+ * mailbox name
+ * status flags
+ * Returns: T on success, NIL on failure
+ */
+
+long pop3_status (MAILSTREAM *stream,char *mbx,long flags)
+{
+ MAILSTATUS status;
+ unsigned long i;
+ long ret = NIL;
+ MAILSTREAM *tstream =
+ (stream && LOCAL->netstream && mail_usable_network_stream (stream,mbx)) ?
+ stream : mail_open (NIL,mbx,OP_SILENT);
+ if (tstream) { /* have a usable stream? */
+ status.flags = flags; /* return status values */
+ status.messages = tstream->nmsgs;
+ status.recent = tstream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1,status.unseen = 0; i <= tstream->nmsgs; i++)
+ if (!mail_elt (tstream,i)->seen) status.unseen++;
+ status.uidnext = tstream->uid_last + 1;
+ status.uidvalidity = tstream->uid_validity;
+ /* pass status to main program */
+ mm_status (tstream,mbx,&status);
+ if (stream != tstream) mail_close (tstream);
+ ret = LONGT;
+ }
+ return ret; /* success */
+}
+
+/* POP3 mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *pop3_open (MAILSTREAM *stream)
+{
+ unsigned long i,j;
+ char *s,*t,tmp[MAILTMPLEN],usr[MAILTMPLEN];
+ NETMBX mb;
+ MESSAGECACHE *elt;
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &pop3proto;
+ mail_valid_net_parse (stream->mailbox,&mb);
+ usr[0] = '\0'; /* initially no user name */
+ if (stream->local) fatal ("pop3 recycle stream");
+ /* /anonymous not supported */
+ if (mb.anoflag || stream->anonymous) {
+ mm_log ("Anonymous POP3 login not available",ERROR);
+ return NIL;
+ }
+ /* /readonly not supported either */
+ if (mb.readonlyflag || stream->rdonly) {
+ mm_log ("Read-only POP3 access not available",ERROR);
+ return NIL;
+ }
+ /* copy other switches */
+ if (mb.dbgflag) stream->debug = T;
+ if (mb.secflag) stream->secure = T;
+ mb.trysslflag = stream->tryssl = (mb.trysslflag || stream->tryssl) ? T : NIL;
+ stream->local = /* instantiate localdata */
+ (void *) memset (fs_get (sizeof (POP3LOCAL)),0,sizeof (POP3LOCAL));
+ stream->sequence++; /* bump sequence number */
+ stream->perm_deleted = T; /* deleted is only valid flag */
+
+ if ((LOCAL->netstream = /* try to open connection */
+ net_open (&mb,NIL,pop3_port ? pop3_port : POP3TCPPORT,
+ (NETDRIVER *) mail_parameters (NIL,GET_SSLDRIVER,NIL),
+ "*pop3s",pop3_sslport ? pop3_sslport : POP3SSLPORT)) &&
+ pop3_reply (stream)) {
+ mm_log (LOCAL->reply,NIL); /* give greeting */
+ if (!pop3_auth (stream,&mb,tmp,usr)) pop3_close (stream,NIL);
+ else if (pop3_send (stream,"STAT",NIL)) {
+ int silent = stream->silent;
+ stream->silent = T;
+ sprintf (tmp,"{%.200s:%lu/pop3",
+ (long) mail_parameters (NIL,GET_TRUSTDNS,NIL) ?
+ net_host (LOCAL->netstream) : mb.host,
+ net_port (LOCAL->netstream));
+ if (mb.tlsflag) strcat (tmp,"/tls");
+ if (mb.tlssslv23) strcat (tmp,"/tls-sslv23");
+ if (mb.notlsflag) strcat (tmp,"/notls");
+ if (mb.sslflag) strcat (tmp,"/ssl");
+ if (mb.novalidate) strcat (tmp,"/novalidate-cert");
+ if (LOCAL->loser = mb.loser) strcat (tmp,"/loser");
+ if (stream->secure) strcat (tmp,"/secure");
+ sprintf (tmp + strlen (tmp),"/user=\"%s\"}%s",usr,mb.mailbox);
+ stream->inbox = T; /* always INBOX */
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ /* notify upper level */
+ mail_exists (stream,stream->uid_last = strtoul (LOCAL->reply,NIL,10));
+ mail_recent (stream,stream->nmsgs);
+ /* instantiate elt */
+ for (i = 0; i < stream->nmsgs;) {
+ elt = mail_elt (stream,++i);
+ elt->valid = elt->recent = T;
+ elt->private.uid = i;
+ }
+
+ /* trust LIST output if new server */
+ if (!LOCAL->loser && LOCAL->cap.capa && pop3_send (stream,"LIST",NIL)) {
+ while ((s = net_getline (LOCAL->netstream)) && (*s != '.')) {
+ if ((i = strtoul (s,&t,10)) && (i <= stream->nmsgs) &&
+ (j = strtoul (t,NIL,10))) mail_elt (stream,i)->rfc822_size = j;
+ fs_give ((void **) &s);
+ }
+ /* flush final dot */
+ if (s) fs_give ((void **) &s);
+ else { /* lost connection */
+ mm_log ("POP3 connection broken while itemizing messages",ERROR);
+ pop3_close (stream,NIL);
+ return NIL;
+ }
+ }
+ stream->silent = silent; /* notify main program */
+ mail_exists (stream,stream->nmsgs);
+ /* notify if empty */
+ if (!(stream->nmsgs || stream->silent)) mm_log ("Mailbox is empty",WARN);
+ }
+ else { /* error in STAT */
+ mm_log (LOCAL->reply,ERROR);
+ pop3_close (stream,NIL); /* too bad */
+ }
+ }
+ else { /* connection failed */
+ if (LOCAL->reply) mm_log (LOCAL->reply,ERROR);
+ pop3_close (stream,NIL); /* failed, clean up */
+ }
+ return LOCAL ? stream : NIL; /* if stream is alive, return to caller */
+}
+
+/* POP3 capabilities
+ * Accepts: stream
+ * authenticator flags
+ * Returns: T on success, NIL on failure
+ */
+
+long pop3_capa (MAILSTREAM *stream,long flags)
+{
+ unsigned long i;
+ char *s,*t,*r,*args;
+ if (LOCAL->cap.implementation)/* zap all old capabilities */
+ fs_give ((void **) &LOCAL->cap.implementation);
+ memset (&LOCAL->cap,0,sizeof (LOCAL->cap));
+ /* get server capabilities */
+ if (pop3_send (stream,"CAPA",NIL)) LOCAL->cap.capa = T;
+ else {
+ LOCAL->cap.user = T; /* guess worst-case old server */
+ return NIL; /* no CAPA on this server */
+ }
+ while ((t = net_getline (LOCAL->netstream)) && (t[1] || (*t != '.'))) {
+ if (stream->debug) mm_dlog (t);
+ /* get optional capability arguments */
+ if (args = strchr (t,' ')) *args++ = '\0';
+ if (!compare_cstring (t,"STLS")) LOCAL->cap.stls = T;
+ else if (!compare_cstring (t,"PIPELINING")) LOCAL->cap.pipelining = T;
+ else if (!compare_cstring (t,"RESP-CODES")) LOCAL->cap.respcodes = T;
+ else if (!compare_cstring (t,"TOP")) LOCAL->cap.top = T;
+ else if (!compare_cstring (t,"UIDL")) LOCAL->cap.uidl = T;
+ else if (!compare_cstring (t,"USER")) LOCAL->cap.user = T;
+ else if (!compare_cstring (t,"IMPLEMENTATION") && args)
+ LOCAL->cap.implementation = cpystr (args);
+ else if (!compare_cstring (t,"EXPIRE") && args) {
+ LOCAL->cap.expire = T; /* note that it is present */
+ if (s = strchr(args,' ')){/* separate time from possible USER */
+ *s++ = '\0';
+ /* in case they add something after USER */
+ if ((strlen (s) > 4) && (s[4] == ' ')) s[4] = '\0';
+ }
+ LOCAL->cap.expire = /* get expiration time */
+ (!compare_cstring (args,"NEVER")) ? 65535 :
+ ((s && !compare_cstring (s,"USER")) ? -atoi (args) : atoi (args));
+ }
+ else if (!compare_cstring (t,"LOGIN-DELAY") && args) {
+ LOCAL->cap.logindelay = T;/* note that it is present */
+ if (s = strchr(args,' ')){/* separate time from possible USER */
+ *s++ = '\0';
+ /* in case they add something after USER */
+ if ((strlen (s) > 4) && (s[4] == ' ')) s[4] = '\0';
+ }
+ /* get delay time */
+ LOCAL->cap.delaysecs = (s && !compare_cstring (s,"USER")) ?
+ -atoi (args) : atoi (args);
+ }
+ else if (!compare_cstring (t,"SASL") && args)
+ for (args = strtok_r (args," ",&r); args; args = strtok_r (NIL," ",&r))
+ if ((i = mail_lookup_auth_name (args,flags)) &&
+ (--i < MAXAUTHENTICATORS))
+ LOCAL->cap.sasl |= (1 << i);
+ fs_give ((void **) &t);
+ }
+ if (t) { /* flush end of text indicator */
+ if (stream->debug) mm_dlog (t);
+ fs_give ((void **) &t);
+ }
+ return LONGT;
+}
+
+/* POP3 authenticate
+ * Accepts: stream to login
+ * parsed network mailbox structure
+ * scratch buffer of length MAILTMPLEN
+ * place to return user name
+ * Returns: T on success, NIL on failure
+ */
+
+long pop3_auth (MAILSTREAM *stream,NETMBX *mb,char *pwd,char *usr)
+{
+ unsigned long i,trial,auths = 0;
+ char *t;
+ AUTHENTICATOR *at;
+ long ret = NIL;
+ long flags = (stream->secure ? AU_SECURE : NIL) |
+ (mb->authuser[0] ? AU_AUTHUSER : NIL);
+ long capaok = pop3_capa (stream,flags);
+ NETDRIVER *ssld = (NETDRIVER *) mail_parameters (NIL,GET_SSLDRIVER,NIL);
+ sslstart_t stls = (sslstart_t) mail_parameters (NIL,GET_SSLSTART,NIL);
+ /* server has TLS? */
+ if (stls && LOCAL->cap.stls && !mb->sslflag && !mb->notlsflag &&
+ pop3_send (stream,"STLS",NIL)) {
+ mb->tlsflag = T; /* TLS OK, get into TLS at this end */
+ LOCAL->netstream->dtb = ssld;
+ if (!(LOCAL->netstream->stream =
+ (*stls) (LOCAL->netstream->stream,mb->host,
+ (mb->tlssslv23 ? NIL : NET_TLSCLIENT) |
+ (mb->novalidate ? NET_NOVALIDATECERT : NIL)))) {
+ /* drat, drop this connection */
+ if (LOCAL->netstream) net_close (LOCAL->netstream);
+ LOCAL->netstream= NIL;
+ return NIL; /* TLS negotiation failed */
+ }
+ pop3_capa (stream,flags); /* get capabilities now that TLS in effect */
+ }
+ else if (mb->tlsflag) { /* user specified /tls but can't do it */
+ mm_log ("Unable to negotiate TLS with this server",ERROR);
+ return NIL;
+ }
+ /* get authenticators from capabilities */
+ if (capaok) auths = LOCAL->cap.sasl;
+ /* get list of authenticators */
+ else if (pop3_send (stream,"AUTH",NIL)) {
+ while ((t = net_getline (LOCAL->netstream)) && (t[1] || (*t != '.'))) {
+ if (stream->debug) mm_dlog (t);
+ if ((i = mail_lookup_auth_name (t,flags)) && (--i < MAXAUTHENTICATORS))
+ auths |= (1 << i);
+ fs_give ((void **) &t);
+ }
+ if (t) { /* flush end of text indicator */
+ if (stream->debug) mm_dlog (t);
+ fs_give ((void **) &t);
+ }
+ }
+ /* disable LOGIN if PLAIN also advertised */
+ if ((i = mail_lookup_auth_name ("PLAIN",NIL)) && (--i < MAXAUTHENTICATORS) &&
+ (auths & (1 << i)) &&
+ (i = mail_lookup_auth_name ("LOGIN",NIL)) && (--i < MAXAUTHENTICATORS))
+ auths &= ~(1 << i);
+
+ if (auths) { /* got any authenticators? */
+ if ((long) mail_parameters (NIL,GET_TRUSTDNS,NIL)) {
+ /* remote name for authentication */
+ strncpy (mb->host,(long) mail_parameters (NIL,GET_SASLUSESPTRNAME,NIL) ?
+ net_remotehost (LOCAL->netstream) : net_host (LOCAL->netstream),
+ NETMAXHOST-1);
+ mb->host[NETMAXHOST-1] = '\0';
+ }
+ for (t = NIL, LOCAL->saslcancel = NIL; !ret && LOCAL->netstream && auths &&
+ (at = mail_lookup_auth (find_rightmost_bit (&auths)+1)); ) {
+ if (t) { /* previous authenticator failed? */
+ sprintf (pwd,"Retrying using %.80s authentication after %.80s",
+ at->name,t);
+ mm_log (pwd,NIL);
+ fs_give ((void **) &t);
+ }
+ trial = 0; /* initial trial count */
+ do {
+ if (t) {
+ sprintf (pwd,"Retrying %s authentication after %.80s",at->name,t);
+ mm_log (pwd,WARN);
+ fs_give ((void **) &t);
+ }
+ LOCAL->saslcancel = NIL;
+ if (pop3_send (stream,"AUTH",at->name)) {
+ /* hide client authentication responses */
+ if (!(at->flags & AU_SECURE)) LOCAL->sensitive = T;
+ if ((*at->client) (pop3_challenge,pop3_response,"pop",mb,stream,
+ &trial,usr) && LOCAL->response) {
+ if (*LOCAL->response == '+') ret = LONGT;
+ /* if main program requested cancellation */
+ else if (!trial) mm_log ("POP3 Authentication cancelled",ERROR);
+ }
+ LOCAL->sensitive=NIL; /* unhide */
+ }
+ /* remember response if error and no cancel */
+ if (!ret && trial) t = cpystr (LOCAL->reply);
+ } while (!ret && trial && (trial < pop3_maxlogintrials) &&
+ LOCAL->netstream);
+ }
+ if (t) { /* previous authenticator failed? */
+ if (!LOCAL->saslcancel) { /* don't do this if a cancel */
+ sprintf (pwd,"Can not authenticate to POP3 server: %.80s",t);
+ mm_log (pwd,ERROR);
+ }
+ fs_give ((void **) &t);
+ }
+ }
+
+ else if (stream->secure)
+ mm_log ("Can't do secure authentication with this server",ERROR);
+ else if (mb->authuser[0])
+ mm_log ("Can't do /authuser with this server",ERROR);
+ else if (!LOCAL->cap.user) mm_log ("Can't login to this server",ERROR);
+ else { /* traditional login */
+ trial = 0; /* initial trial count */
+ do {
+ pwd[0] = 0; /* prompt user for password */
+ mm_login (mb,usr,pwd,trial++);
+ if (pwd[0]) { /* send login sequence if have password */
+ if (pop3_send (stream,"USER",usr)) {
+ LOCAL->sensitive = T; /* hide this command */
+ if (pop3_send (stream,"PASS",pwd)) ret = LONGT;
+ LOCAL->sensitive=NIL; /* unhide */
+ }
+ if (!ret) { /* failure */
+ mm_log (LOCAL->reply,WARN);
+ if (trial == pop3_maxlogintrials)
+ mm_log ("Too many login failures",ERROR);
+ }
+ }
+ /* user refused to give password */
+ else mm_log ("Login aborted",ERROR);
+ } while (!ret && pwd[0] && (trial < pop3_maxlogintrials) &&
+ LOCAL->netstream);
+ }
+ memset (pwd,0,MAILTMPLEN); /* erase password */
+ /* get capabilities if logged in */
+ if (ret && capaok) pop3_capa (stream,flags);
+ return ret;
+}
+
+/* Get challenge to authenticator in binary
+ * Accepts: stream
+ * pointer to returned size
+ * Returns: challenge or NIL if not challenge
+ */
+
+void *pop3_challenge (void *s,unsigned long *len)
+{
+ char tmp[MAILTMPLEN];
+ void *ret = NIL;
+ MAILSTREAM *stream = (MAILSTREAM *) s;
+ if (stream && LOCAL->response &&
+ (*LOCAL->response == '+') && (LOCAL->response[1] == ' ') &&
+ !(ret = rfc822_base64 ((unsigned char *) LOCAL->reply,
+ strlen (LOCAL->reply),len))) {
+ sprintf (tmp,"POP3 SERVER BUG (invalid challenge): %.80s",LOCAL->reply);
+ mm_log (tmp,ERROR);
+ }
+ return ret;
+}
+
+
+/* Send authenticator response in BASE64
+ * Accepts: MAIL stream
+ * string to send
+ * length of string
+ * Returns: T if successful, else NIL
+ */
+
+long pop3_response (void *s,char *response,unsigned long size)
+{
+ MAILSTREAM *stream = (MAILSTREAM *) s;
+ unsigned long i,j,ret;
+ char *t,*u;
+ if (response) { /* make CRLFless BASE64 string */
+ if (size) {
+ for (t = (char *) rfc822_binary ((void *) response,size,&i),u = t,j = 0;
+ j < i; j++) if (t[j] > ' ') *u++ = t[j];
+ *u = '\0'; /* tie off string for mm_dlog() */
+ if (stream->debug) mail_dlog (t,LOCAL->sensitive);
+ /* append CRLF */
+ *u++ = '\015'; *u++ = '\012'; *u = '\0';
+ ret = net_sout (LOCAL->netstream,t,u - t);
+ fs_give ((void **) &t);
+ }
+ else ret = net_sout (LOCAL->netstream,"\015\012",2);
+ }
+ else { /* abort requested */
+ ret = net_sout (LOCAL->netstream,"*\015\012",3);
+ LOCAL->saslcancel = T; /* mark protocol-requested SASL cancel */
+ }
+ pop3_reply (stream); /* get response */
+ return ret;
+}
+
+/* POP3 mail close
+ * Accepts: MAIL stream
+ * option flags
+ */
+
+void pop3_close (MAILSTREAM *stream,long options)
+{
+ int silent = stream->silent;
+ if (LOCAL) { /* only if a file is open */
+ if (LOCAL->netstream) { /* close POP3 connection */
+ stream->silent = T;
+ if (options & CL_EXPUNGE) pop3_expunge (stream,NIL,NIL);
+ stream->silent = silent;
+ pop3_send (stream,"QUIT",NIL);
+ mm_notify (stream,LOCAL->reply,BYE);
+ }
+ /* close POP3 connection */
+ if (LOCAL->netstream) net_close (LOCAL->netstream);
+ /* clean up */
+ if (LOCAL->cap.implementation)
+ fs_give ((void **) &LOCAL->cap.implementation);
+ if (LOCAL->txt) fclose (LOCAL->txt);
+ LOCAL->txt = NIL;
+ if (LOCAL->response) fs_give ((void **) &LOCAL->response);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* POP3 mail fetch fast information
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ * This is ugly and slow
+ */
+
+void pop3_fetchfast (MAILSTREAM *stream,char *sequence,long flags)
+{
+ unsigned long i;
+ MESSAGECACHE *elt;
+ /* get sequence */
+ if (stream && LOCAL && ((flags & FT_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence &&
+ !(elt->day && elt->rfc822_size)) {
+ ENVELOPE **env = NIL;
+ ENVELOPE *e = NIL;
+ if (!stream->scache) env = &elt->private.msg.env;
+ else if (stream->msgno == i) env = &stream->env;
+ else env = &e;
+ if (!*env || !elt->rfc822_size) {
+ STRING bs;
+ unsigned long hs;
+ char *ht = (*stream->dtb->header) (stream,i,&hs,NIL);
+ /* need to make an envelope? */
+ if (!*env) rfc822_parse_msg (env,NIL,ht,hs,NIL,BADHOST,
+ stream->dtb->flags);
+ /* need message size too, ugh */
+ if (!elt->rfc822_size) {
+ (*stream->dtb->text) (stream,i,&bs,FT_PEEK);
+ elt->rfc822_size = hs + SIZE (&bs) - GETPOS (&bs);
+ }
+ }
+ /* if need date, have date in envelope? */
+ if (!elt->day && *env && (*env)->date)
+ mail_parse_date (elt,(*env)->date);
+ /* sigh, fill in bogus default */
+ if (!elt->day) elt->day = elt->month = 1;
+ mail_free_envelope (&e);
+ }
+}
+
+/* POP3 fetch header as text
+ * Accepts: mail stream
+ * message number
+ * pointer to return size
+ * flags
+ * Returns: header text
+ */
+
+char *pop3_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *size,
+ long flags)
+{
+ unsigned long i;
+ char tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ FILE *f = NIL;
+ *size = 0; /* initially no header size */
+ if ((flags & FT_UID) && !(msgno = mail_msgno (stream,msgno))) return "";
+ /* have header text already? */
+ if (!(elt = mail_elt (stream,msgno))->private.msg.header.text.data) {
+ /* if have CAPA and TOP, assume good TOP */
+ if (!LOCAL->loser && LOCAL->cap.top) {
+ sprintf (tmp,"TOP %lu 0",mail_uid (stream,msgno));
+ if (pop3_send (stream,tmp,NIL))
+ f = netmsg_slurp (LOCAL->netstream,&i,
+ &elt->private.msg.header.text.size);
+ }
+ /* otherwise load the cache with the message */
+ else if (elt->private.msg.header.text.size = pop3_cache (stream,elt))
+ f = LOCAL->txt;
+ if (f) { /* got it, make sure at start of file */
+ fseek (f,(unsigned long) 0,SEEK_SET);
+ /* read header from the cache */
+ fread (elt->private.msg.header.text.data = (unsigned char *)
+ fs_get ((size_t) elt->private.msg.header.text.size + 1),
+ (size_t) 1,(size_t) elt->private.msg.header.text.size,f);
+ /* tie off header text */
+ elt->private.msg.header.text.data[elt->private.msg.header.text.size] =
+ '\0';
+ /* close if not the cache */
+ if (f != LOCAL->txt) fclose (f);
+ }
+ }
+ /* return size of text */
+ if (size) *size = elt->private.msg.header.text.size;
+ return elt->private.msg.header.text.data ?
+ (char *) elt->private.msg.header.text.data : "";
+}
+
+/* POP3 fetch body
+ * Accepts: mail stream
+ * message number
+ * pointer to stringstruct to initialize
+ * flags
+ * Returns: T if successful, else NIL
+ */
+
+long pop3_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ MESSAGECACHE *elt;
+ INIT (bs,mail_string,(void *) "",0);
+ if ((flags & FT_UID) && !(msgno = mail_msgno (stream,msgno))) return NIL;
+ elt = mail_elt (stream,msgno);
+ pop3_cache (stream,elt); /* make sure cache loaded */
+ if (!LOCAL->txt) return NIL; /* error if don't have a file */
+ if (!(flags & FT_PEEK)) { /* mark seen if needed */
+ elt->seen = T;
+ mm_flags (stream,elt->msgno);
+ }
+ INIT (bs,file_string,(void *) LOCAL->txt,elt->rfc822_size);
+ SETPOS (bs,LOCAL->hdrsize); /* skip past header */
+ return T;
+}
+
+/* POP3 cache message
+ * Accepts: mail stream
+ * message number
+ * Returns: header size
+ */
+
+unsigned long pop3_cache (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ /* already cached? */
+ if (LOCAL->cached != mail_uid (stream,elt->msgno)) {
+ /* no, close current file */
+ if (LOCAL->txt) fclose (LOCAL->txt);
+ LOCAL->txt = NIL;
+ LOCAL->cached = LOCAL->hdrsize = 0;
+ if (pop3_send_num (stream,"RETR",elt->msgno) &&
+ (LOCAL->txt = netmsg_slurp (LOCAL->netstream,&elt->rfc822_size,
+ &LOCAL->hdrsize)))
+ /* set as current message number */
+ LOCAL->cached = mail_uid (stream,elt->msgno);
+ else elt->deleted = T;
+ }
+ return LOCAL->hdrsize;
+}
+
+/* POP3 mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long pop3_ping (MAILSTREAM *stream)
+{
+ return pop3_send (stream,"NOOP",NIL);
+}
+
+
+/* POP3 mail check mailbox
+ * Accepts: MAIL stream
+ */
+
+void pop3_check (MAILSTREAM *stream)
+{
+ if (pop3_ping (stream)) mm_log ("Check completed",NIL);
+}
+
+
+/* POP3 mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T if success, NIL if failure
+ */
+
+long pop3_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ char tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ unsigned long i = 1,n = 0;
+ long ret;
+ if (ret = sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) :
+ LONGT) { /* build selected sequence if needed */
+ while (i <= stream->nmsgs) {
+ elt = mail_elt (stream,i);
+ if (elt->deleted && (sequence ? elt->sequence : T) &&
+ pop3_send_num (stream,"DELE",i)) {
+ /* expunging currently cached message? */
+ if (LOCAL->cached == mail_uid (stream,i)) {
+ /* yes, close current file */
+ if (LOCAL->txt) fclose (LOCAL->txt);
+ LOCAL->txt = NIL;
+ LOCAL->cached = LOCAL->hdrsize = 0;
+ }
+ mail_expunged (stream,i);
+ n++;
+ }
+ else i++; /* try next message */
+ }
+ if (!stream->silent) { /* only if not silent */
+ if (n) { /* did we expunge anything? */
+ sprintf (tmp,"Expunged %lu messages",n);
+ mm_log (tmp,(long) NIL);
+ }
+ else mm_log ("No messages deleted, so no update needed",(long) NIL);
+ }
+ }
+ return ret;
+}
+
+/* POP3 mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * option flags
+ * Returns: T if copy successful, else NIL
+ */
+
+long pop3_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ mm_log ("Copy not valid for POP3",ERROR);
+ return NIL;
+}
+
+
+/* POP3 mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long pop3_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ mm_log ("Append not valid for POP3",ERROR);
+ return NIL;
+}
+
+/* Internal routines */
+
+
+/* Post Office Protocol 3 send command with number argument
+ * Accepts: MAIL stream
+ * command
+ * number
+ * Returns: T if successful, NIL if failure
+ */
+
+long pop3_send_num (MAILSTREAM *stream,char *command,unsigned long n)
+{
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"%lu",mail_uid (stream,n));
+ return pop3_send (stream,command,tmp);
+}
+
+
+/* Post Office Protocol 3 send command
+ * Accepts: MAIL stream
+ * command
+ * command argument
+ * Returns: T if successful, NIL if failure
+ */
+
+long pop3_send (MAILSTREAM *stream,char *command,char *args)
+{
+ long ret;
+ char *s = (char *) fs_get (strlen (command) + (args ? strlen (args) + 1: 0)
+ + 3);
+ mail_lock (stream); /* lock up the stream */
+ if (!LOCAL->netstream) ret = pop3_fake (stream,"POP3 connection lost");
+ else { /* build the complete command */
+ if (args) sprintf (s,"%s %s",command,args);
+ else strcpy (s,command);
+ if (stream->debug) mail_dlog (s,LOCAL->sensitive);
+ strcat (s,"\015\012");
+ /* send the command */
+ ret = net_soutr (LOCAL->netstream,s) ? pop3_reply (stream) :
+ pop3_fake (stream,"POP3 connection broken in command");
+ }
+ fs_give ((void **) &s);
+ mail_unlock (stream); /* unlock stream */
+ return ret;
+}
+
+/* Post Office Protocol 3 get reply
+ * Accepts: MAIL stream
+ * Returns: T if success reply, NIL if error reply
+ */
+
+long pop3_reply (MAILSTREAM *stream)
+{
+ char *s;
+ /* flush old reply */
+ if (LOCAL->response) fs_give ((void **) &LOCAL->response);
+ /* get reply */
+ if (!(LOCAL->response = net_getline (LOCAL->netstream)))
+ return pop3_fake (stream,"POP3 connection broken in response");
+ if (stream->debug) mm_dlog (LOCAL->response);
+ LOCAL->reply = (s = strchr (LOCAL->response,' ')) ? s + 1 : LOCAL->response;
+ /* return success or failure */
+ return (*LOCAL->response =='+') ? T : NIL;
+}
+
+
+/* Post Office Protocol 3 set fake error
+ * Accepts: MAIL stream
+ * error text
+ * Returns: NIL, always
+ */
+
+long pop3_fake (MAILSTREAM *stream,char *text)
+{
+ mm_notify (stream,text,BYE); /* send bye alert */
+ if (LOCAL->netstream) net_close (LOCAL->netstream);
+ LOCAL->netstream = NIL; /* farewell, dear TCP stream */
+ /* flush any old reply */
+ if (LOCAL->response) fs_give ((void **) &LOCAL->response);
+ LOCAL->reply = text; /* set up pseudo-reply string */
+ return NIL; /* return error code */
+}
diff --git a/imap/src/c-client/rfc822.c b/imap/src/c-client/rfc822.c
new file mode 100644
index 00000000..d9239646
--- /dev/null
+++ b/imap/src/c-client/rfc822.c
@@ -0,0 +1,2373 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: RFC 2822 and MIME routines
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@Washington.EDU
+ *
+ * Date: 27 July 1988
+ * Last Edited: 14 May 2008
+ *
+ * This original version of this file is
+ * Copyright 1988 Stanford University
+ * and was developed in the Symbolic Systems Resources Group of the Knowledge
+ * Systems Laboratory at Stanford University in 1987-88, and was funded by the
+ * Biomedical Research Technology Program of the NationalInstitutes of Health
+ * under grant number RR-00785.
+ */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <time.h>
+#include "c-client.h"
+
+
+/* Support for deprecated features in earlier specifications. Note that this
+ * module follows RFC 2822, and all use of "rfc822" in function names is
+ * for compatibility. Only the code identified by the conditionals below
+ * follows the earlier documents.
+ */
+
+#define RFC733 1 /* parse "at" */
+#define RFC822 0 /* generate A-D-L (MUST be 0 for 2822) */
+
+/* RFC-822 static data */
+
+#define RFC822CONT " " /* RFC 2822 continuation */
+
+ /* should have been "Remailed-" */
+#define RESENTPREFIX "ReSent-"
+static char *resentprefix = RESENTPREFIX;
+ /* syntax error host string */
+static const char *errhst = ERRHOST;
+
+
+/* Body formats constant strings, must match definitions in mail.h */
+
+char *body_types[TYPEMAX+1] = {
+ "TEXT", "MULTIPART", "MESSAGE", "APPLICATION", "AUDIO", "IMAGE", "VIDEO",
+ "MODEL", "X-UNKNOWN"
+};
+
+
+char *body_encodings[ENCMAX+1] = {
+ "7BIT", "8BIT", "BINARY", "BASE64", "QUOTED-PRINTABLE", "X-UNKNOWN"
+};
+
+
+/* Token delimiting special characters */
+
+ /* RFC 2822 specials */
+const char *specials = " ()<>@,;:\\\"[].\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\177";
+ /* RFC 2822 phrase specials (no space) */
+const char *rspecials = "()<>@,;:\\\"[].\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\177";
+ /* RFC 2822 dot-atom specials (no dot) */
+const char *wspecials = " ()<>@,;:\\\"[]\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\177";
+ /* RFC 2045 MIME body token specials */
+const char *tspecials = " ()<>@,;:\\\"[]/?=\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\177";
+
+/* Subtype defaulting (a no-no, but regretably necessary...)
+ * Accepts: type code
+ * Returns: default subtype name
+ */
+
+char *rfc822_default_subtype (unsigned short type)
+{
+ switch (type) {
+ case TYPETEXT: /* default is TEXT/PLAIN */
+ return "PLAIN";
+ case TYPEMULTIPART: /* default is MULTIPART/MIXED */
+ return "MIXED";
+ case TYPEMESSAGE: /* default is MESSAGE/RFC822 */
+ return "RFC822";
+ case TYPEAPPLICATION: /* default is APPLICATION/OCTET-STREAM */
+ return "OCTET-STREAM";
+ case TYPEAUDIO: /* default is AUDIO/BASIC */
+ return "BASIC";
+ default: /* others have no default subtype */
+ return "UNKNOWN";
+ }
+}
+
+/* RFC 2822 parsing routines */
+
+
+/* Parse an RFC 2822 message
+ * Accepts: pointer to return envelope
+ * pointer to return body
+ * pointer to header
+ * header byte count
+ * pointer to body stringstruct
+ * pointer to local host name
+ * recursion depth
+ * source driver flags
+ */
+
+void rfc822_parse_msg_full (ENVELOPE **en,BODY **bdy,char *s,unsigned long i,
+ STRING *bs,char *host,unsigned long depth,
+ unsigned long flags)
+{
+ char c,*t,*d;
+ char *tmp = (char *) fs_get ((size_t) i + 100);
+ ENVELOPE *env = (*en = mail_newenvelope ());
+ BODY *body = bdy ? (*bdy = mail_newbody ()) : NIL;
+ long MIMEp = -1; /* flag that MIME semantics are in effect */
+ long PathP = NIL; /* flag that a Path: was seen */
+ parseline_t pl = (parseline_t) mail_parameters (NIL,GET_PARSELINE,NIL);
+ if (!host) host = BADHOST; /* make sure that host is non-null */
+ while (i && *s != '\n') { /* until end of header */
+ t = tmp; /* initialize buffer pointer */
+ c = ' '; /* and previous character */
+ while (i && c) { /* collect text until logical end of line */
+ switch (c = *s++) { /* slurp a character */
+ case '\015': /* return, possible end of logical line */
+ if (*s == '\n') break; /* ignore if LF follows */
+ case '\012': /* LF, possible end of logical line */
+ /* tie off unless next line starts with WS */
+ if (*s != ' ' && *s != '\t') *t++ = c = '\0';
+ break;
+ case '\t': /* tab */
+ *t++ = ' '; /* coerce to space */
+ break;
+ default: /* all other characters */
+ *t++ = c; /* insert the character into the line */
+ break;
+ }
+ if (!--i) *t++ = '\0'; /* see if end of header */
+ }
+
+ /* find header item type */
+ if (t = d = strchr (tmp,':')) {
+ *d++ = '\0'; /* tie off header item, point at its data */
+ while (*d == ' ') d++; /* flush whitespace */
+ while ((tmp < t--) && (*t == ' ')) *t = '\0';
+ ucase (tmp); /* coerce to uppercase */
+ /* external callback */
+ if (pl) (*pl) (env,tmp,d,host);
+ switch (*tmp) { /* dispatch based on first character */
+ case '>': /* possible >From: */
+ if (!strcmp (tmp+1,"FROM")) rfc822_parse_adrlist (&env->from,d,host);
+ break;
+ case 'B': /* possible bcc: */
+ if (!strcmp (tmp+1,"CC")) rfc822_parse_adrlist (&env->bcc,d,host);
+ break;
+ case 'C': /* possible cc: or Content-<mumble>*/
+ if (!strcmp (tmp+1,"C")) rfc822_parse_adrlist (&env->cc,d,host);
+ else if ((tmp[1] == 'O') && (tmp[2] == 'N') && (tmp[3] == 'T') &&
+ (tmp[4] == 'E') && (tmp[5] == 'N') && (tmp[6] == 'T') &&
+ (tmp[7] == '-') && body)
+ switch (MIMEp) {
+ case -1: /* unknown if MIME or not */
+ if (!(MIMEp = /* see if MIME-Version header exists */
+ search ((unsigned char *) s-1,i,
+ (unsigned char *)"\012MIME-Version",(long) 13))) {
+#if 1
+ /* This is a disgusting kludge, and most of the messages which
+ * benefit from it are spam.
+ */
+ if (!strcmp (tmp+8,"TRANSFER-ENCODING") ||
+ (!strcmp (tmp+8,"TYPE") && strchr (d,'/'))) {
+ MM_LOG ("Warning: MIME header encountered in non-MIME message",
+ PARSE);
+ MIMEp = 1; /* declare MIME now */
+ }
+ else
+#endif
+ break; /* non-MIME message */
+ }
+ case T: /* definitely MIME */
+ rfc822_parse_content_header (body,tmp+8,d);
+ }
+ break;
+ case 'D': /* possible Date: */
+ if (!env->date && !strcmp (tmp+1,"ATE")) env->date = cpystr (d);
+ break;
+ case 'F': /* possible From: */
+ if (!strcmp (tmp+1,"ROM")) rfc822_parse_adrlist (&env->from,d,host);
+ else if (!strcmp (tmp+1,"OLLOWUP-TO")) {
+ t = env->followup_to = (char *) fs_get (1 + strlen (d));
+ while (c = *d++) if (c != ' ') *t++ = c;
+ *t++ = '\0';
+ }
+ break;
+ case 'I': /* possible In-Reply-To: */
+ if (!env->in_reply_to && !strcmp (tmp+1,"N-REPLY-TO"))
+ env->in_reply_to = cpystr (d);
+ break;
+
+ case 'M': /* possible Message-ID: or MIME-Version: */
+ if (!env->message_id && !strcmp (tmp+1,"ESSAGE-ID"))
+ env->message_id = cpystr (d);
+ else if (!strcmp (tmp+1,"IME-VERSION")) {
+ /* tie off at end of phrase */
+ if (t = rfc822_parse_phrase (d)) *t = '\0';
+ rfc822_skipws (&d); /* skip whitespace */
+ /* known version? */
+ if (strcmp (d,"1.0") && strcmp (d,"RFC-XXXX"))
+ MM_LOG ("Warning: message has unknown MIME version",PARSE);
+ MIMEp = T; /* note that we are MIME */
+ }
+ break;
+ case 'N': /* possible Newsgroups: */
+ if (!env->newsgroups && !strcmp (tmp+1,"EWSGROUPS")) {
+ t = env->newsgroups = (char *) fs_get (1 + strlen (d));
+ while (c = *d++) if (c != ' ') *t++ = c;
+ *t++ = '\0';
+ }
+ break;
+ case 'P': /* possible Path: */
+ if (!strcmp (tmp+1,"ATH")) env->ngpathexists = T;
+ break;
+ case 'R': /* possible Reply-To: */
+ if (!strcmp (tmp+1,"EPLY-TO"))
+ rfc822_parse_adrlist (&env->reply_to,d,host);
+ else if (!env->references && !strcmp (tmp+1,"EFERENCES"))
+ env->references = cpystr (d);
+ break;
+ case 'S': /* possible Subject: or Sender: */
+ if (!env->subject && !strcmp (tmp+1,"UBJECT"))
+ env->subject = cpystr (d);
+ else if (!strcmp (tmp+1,"ENDER"))
+ rfc822_parse_adrlist (&env->sender,d,host);
+ break;
+ case 'T': /* possible To: */
+ if (!strcmp (tmp+1,"O")) rfc822_parse_adrlist (&env->to,d,host);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ fs_give ((void **) &tmp); /* done with scratch buffer */
+ /* default Sender: and Reply-To: to From: */
+ if (!env->sender) env->sender = rfc822_cpy_adr (env->from);
+ if (!env->reply_to) env->reply_to = rfc822_cpy_adr (env->from);
+ /* now parse the body */
+ if (body) rfc822_parse_content (body,bs,host,depth,flags);
+}
+
+/* Parse a message body content
+ * Accepts: pointer to body structure
+ * body string
+ * pointer to local host name
+ * recursion depth
+ * source driver flags
+ */
+
+void rfc822_parse_content (BODY *body,STRING *bs,char *h,unsigned long depth,
+ unsigned long flags)
+{
+ char c,c1,*s,*s1;
+ int f;
+ unsigned long i,j,k,m;
+ PARAMETER *param;
+ PART *part = NIL;
+ if (depth > MAXMIMEDEPTH) { /* excessively deep recursion? */
+ body->type = TYPETEXT; /* yes, probably a malicious MIMEgram */
+ MM_LOG ("Ignoring excessively deep MIME recursion",PARSE);
+ }
+ if (!body->subtype) /* default subtype if still unknown */
+ body->subtype = cpystr (rfc822_default_subtype (body->type));
+ /* note offset and sizes */
+ body->contents.offset = GETPOS (bs);
+ /* note internal body size in all cases */
+ body->size.bytes = body->contents.text.size = i = SIZE (bs);
+ if (!(flags & DR_CRLF)) body->size.bytes = strcrlflen (bs);
+ switch (body->type) { /* see if anything else special to do */
+ case TYPETEXT: /* text content */
+ if (!body->parameter) { /* no parameters set */
+ body->parameter = mail_newbody_parameter ();
+ body->parameter->attribute = cpystr ("CHARSET");
+ while (i--) { /* count lines and guess charset */
+ c = SNX (bs); /* get current character */
+ /* charset still unknown? */
+ if (!body->parameter->value) {
+ if ((c == I2C_ESC) && (i && i--) && ((c = SNX (bs)) == I2C_MULTI) &&
+ (i && i--) && (((c = SNX (bs)) == I2CS_94x94_JIS_NEW) ||
+ (c == I2CS_94x94_JIS_OLD)))
+ body->parameter->value = cpystr ("ISO-2022-JP");
+ else if (c & 0x80) body->parameter->value = cpystr ("X-UNKNOWN");
+ }
+ if (c == '\n') body->size.lines++;
+ }
+ /* 7-bit content */
+ if (!body->parameter->value) switch (body->encoding) {
+ case ENC7BIT: /* unadorned 7-bit */
+ case ENC8BIT: /* unadorned 8-bit (but 7-bit content) */
+ case ENCBINARY: /* binary (but 7-bit content( */
+ body->parameter->value = cpystr ("US-ASCII");
+ break;
+ default: /* QUOTED-PRINTABLE, BASE64, etc. */
+ body->parameter->value = cpystr ("X-UNKNOWN");
+ break;
+ }
+ }
+ /* just count lines */
+ else while (i--) if ((SNX (bs)) == '\n') body->size.lines++;
+ break;
+
+ case TYPEMESSAGE: /* encapsulated message */
+ /* encapsulated RFC-822 message? */
+ if (!strcmp (body->subtype,"RFC822")) {
+ body->nested.msg = mail_newmsg ();
+ switch (body->encoding) { /* make sure valid encoding */
+ case ENC7BIT: /* these are valid nested encodings */
+ case ENC8BIT:
+ case ENCBINARY:
+ break;
+ default:
+ MM_LOG ("Ignoring nested encoding of message contents",PARSE);
+ }
+ /* hunt for blank line */
+ for (c = '\012',j = 0; (i > j) && ((c != '\012') || (CHR(bs) != '\012'));
+ j++) if ((c1 = SNX (bs)) != '\015') c = c1;
+ if (i > j) { /* unless no more text */
+ c1 = SNX (bs); /* body starts here */
+ j++; /* advance count */
+ }
+ /* note body text offset and header size */
+ body->nested.msg->header.text.size = j;
+ body->nested.msg->text.text.size = body->contents.text.size - j;
+ body->nested.msg->text.offset = GETPOS (bs);
+ body->nested.msg->full.offset = body->nested.msg->header.offset =
+ body->contents.offset;
+ body->nested.msg->full.text.size = body->contents.text.size;
+ /* copy header string */
+ SETPOS (bs,body->contents.offset);
+ s = (char *) fs_get ((size_t) j + 1);
+ for (s1 = s,k = j; k--; *s1++ = SNX (bs));
+ s[j] = '\0'; /* tie off string (not really necessary) */
+ /* now parse the body */
+ rfc822_parse_msg_full (&body->nested.msg->env,&body->nested.msg->body,s,
+ j,bs,h,depth+1,flags);
+ fs_give ((void **) &s); /* free header string */
+ /* restore position */
+ SETPOS (bs,body->contents.offset);
+ }
+ /* count number of lines */
+ while (i--) if (SNX (bs) == '\n') body->size.lines++;
+ break;
+ case TYPEMULTIPART: /* multiple parts */
+ switch (body->encoding) { /* make sure valid encoding */
+ case ENC7BIT: /* these are valid nested encodings */
+ case ENC8BIT:
+ case ENCBINARY:
+ break;
+ default:
+ MM_LOG ("Ignoring nested encoding of multipart contents",PARSE);
+ }
+ /* remember if digest */
+ f = !strcmp (body->subtype,"DIGEST");
+ /* find cookie */
+ for (s1 = NIL,param = body->parameter; param && !s1; param = param->next)
+ if (!strcmp (param->attribute,"BOUNDARY")) s1 = param->value;
+ if (!s1) s1 = "-"; /* yucky default */
+ j = strlen (s1) + 2; /* length of cookie and header */
+ c = '\012'; /* initially at beginning of line */
+
+ while (i > j) { /* examine data */
+ if (m = GETPOS (bs)) m--; /* get position in front of character */
+ switch (c) { /* examine each line */
+ case '\015': /* handle CRLF form */
+ if (CHR (bs) == '\012'){/* following LF? */
+ c = SNX (bs); i--; /* yes, slurp it */
+ }
+ case '\012': /* at start of a line, start with -- ? */
+ if (!(i && i-- && ((c = SNX (bs)) == '-') && i-- &&
+ ((c = SNX (bs)) == '-'))) break;
+ /* see if cookie matches */
+ if (k = j - 2) for (s = s1; i-- && *s++ == (c = SNX (bs)) && --k;);
+ if (k) break; /* strings didn't match if non-zero */
+ /* terminating delimiter? */
+ if ((c = ((i && i--) ? (SNX (bs)) : '\012')) == '-') {
+ if ((i && i--) && ((c = SNX (bs)) == '-') &&
+ ((i && i--) ? (((c = SNX (bs)) == '\015') || (c=='\012')):T)) {
+ /* if have a final part calculate its size */
+ if (part) part->body.mime.text.size =
+ (m > part->body.mime.offset) ? (m - part->body.mime.offset) :0;
+ part = NIL; i = 1; /* terminate scan */
+ }
+ break;
+ }
+ /* swallow trailing whitespace */
+ while ((c == ' ') || (c == '\t'))
+ c = ((i && i--) ? (SNX (bs)) : '\012');
+ switch (c) { /* need newline after all of it */
+ case '\015': /* handle CRLF form */
+ if (i && CHR (bs) == '\012') {
+ c = SNX (bs); i--;/* yes, slurp it */
+ }
+ case '\012': /* new line */
+ if (part) { /* calculate size of previous */
+ part->body.mime.text.size =
+ (m > part->body.mime.offset) ? (m-part->body.mime.offset) : 0;
+ /* instantiate next */
+ part = part->next = mail_newbody_part ();
+ } /* otherwise start new list */
+ else part = body->nested.part = mail_newbody_part ();
+ /* digest has a different default */
+ if (f) part->body.type = TYPEMESSAGE;
+ /* note offset from main body */
+ part->body.mime.offset = GETPOS (bs);
+ break;
+ default: /* whatever it was it wasn't valid */
+ break;
+ }
+ break;
+ default: /* not at a line */
+ c = SNX (bs); i--; /* get next character */
+ break;
+ }
+ }
+
+ /* calculate size of any final part */
+ if (part) part->body.mime.text.size = i +
+ ((GETPOS(bs) > part->body.mime.offset) ?
+ (GETPOS(bs) - part->body.mime.offset) : 0);
+ /* make a scratch buffer */
+ s1 = (char *) fs_get ((size_t) (k = MAILTMPLEN));
+ /* in case empty multipart */
+ if (!body->nested.part) body->nested.part = mail_newbody_part ();
+ /* parse non-empty body parts */
+ for (part = body->nested.part; part; part = part->next) {
+ /* part non-empty (header and/or content)? */
+ if (i = part->body.mime.text.size) {
+ /* move to that part of the body */
+ SETPOS (bs,part->body.mime.offset);
+ /* until end of header */
+ while (i && ((c = CHR (bs)) != '\015') && (c != '\012')) {
+ /* collect text until logical end of line */
+ for (j = 0,c = ' '; c; ) {
+ /* make sure buffer big enough */
+ if (j > (k - 10)) fs_resize ((void **) &s1,k += MAILTMPLEN);
+ switch (c1 = SNX (bs)) {
+ case '\015': /* return */
+ if (i && (CHR (bs) == '\012')) {
+ c1 = SNX (bs); /* eat any LF following */
+ i--;
+ }
+ case '\012': /* newline, possible end of logical line */
+ /* tie off unless continuation */
+ if (!i || ((CHR (bs) != ' ') && (CHR (bs) != '\t')))
+ s1[j] = c = '\0';
+ break;
+ case '\t': /* tab */
+ case ' ': /* insert whitespace if not already there */
+ if (c != ' ') s1[j++] = c = ' ';
+ break;
+ default: /* all other characters */
+ s1[j++] = c = c1; /* insert the character into the line */
+ break;
+ }
+ /* end of data ties off the header */
+ if (!i || !--i) s1[j++] = c = '\0';
+ }
+
+ /* find header item type */
+ if (((s1[0] == 'C') || (s1[0] == 'c')) &&
+ ((s1[1] == 'O') || (s1[1] == 'o')) &&
+ ((s1[2] == 'N') || (s1[2] == 'n')) &&
+ ((s1[3] == 'T') || (s1[3] == 't')) &&
+ ((s1[4] == 'E') || (s1[4] == 'e')) &&
+ ((s1[5] == 'N') || (s1[5] == 'n')) &&
+ ((s1[6] == 'T') || (s1[6] == 't')) &&
+ (s1[7] == '-') && (s = strchr (s1+8,':'))) {
+ /* tie off and flush whitespace */
+ for (*s++ = '\0'; *s == ' '; s++);
+ /* parse the header */
+ rfc822_parse_content_header (&part->body,ucase (s1+8),s);
+ }
+ }
+ /* skip header trailing (CR)LF */
+ if (i && (CHR (bs) =='\015')) {i--; c1 = SNX (bs);}
+ if (i && (CHR (bs) =='\012')) {i--; c1 = SNX (bs);}
+ j = bs->size; /* save upper level size */
+ /* set offset for next level, fake size to i */
+ bs->size = GETPOS (bs) + i;
+ part->body.mime.text.size -= i;
+ /* now parse it */
+ rfc822_parse_content (&part->body,bs,h,depth+1,flags);
+ bs->size = j; /* restore current level size */
+ }
+ else { /* zero-length part, use default subtype */
+ part->body.subtype = cpystr (rfc822_default_subtype (part->body.type));
+ /* see if anything else special to do */
+ switch (part->body.type) {
+ case TYPETEXT: /* text content */
+ /* default parameters */
+ if (!part->body.parameter) {
+ part->body.parameter = mail_newbody_parameter ();
+ part->body.parameter->attribute = cpystr ("CHARSET");
+ /* only assume US-ASCII if 7BIT */
+ part->body.parameter->value =
+ cpystr ((part->body.encoding == ENC7BIT) ?
+ "US-ASCII" : "X-UNKNOWN");
+ }
+ break;
+ case TYPEMESSAGE: /* encapsulated message in digest */
+ part->body.nested.msg = mail_newmsg ();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ fs_give ((void **) &s1); /* finished with scratch buffer */
+ break;
+ default: /* nothing special to do in any other case */
+ break;
+ }
+}
+
+/* Parse RFC 2822 body content header
+ * Accepts: body to write to
+ * possible content name
+ * remainder of header
+ */
+
+void rfc822_parse_content_header (BODY *body,char *name,char *s)
+{
+ char c,*t,tmp[MAILTMPLEN];
+ long i;
+ STRINGLIST *stl;
+ rfc822_skipws (&s); /* skip leading comments */
+ /* flush whitespace */
+ if (t = strchr (name,' ')) *t = '\0';
+ switch (*name) { /* see what kind of content */
+ case 'I': /* possible Content-ID */
+ if (!(strcmp (name+1,"D") || body->id)) body->id = cpystr (s);
+ break;
+ case 'D': /* possible Content-Description */
+ if (!(strcmp (name+1,"ESCRIPTION") || body->description))
+ body->description = cpystr (s);
+ if (!(strcmp (name+1,"ISPOSITION") || body->disposition.type)) {
+ /* get type word */
+ if (!(name = rfc822_parse_word (s,tspecials))) break;
+ c = *name; /* remember delimiter */
+ *name = '\0'; /* tie off type */
+ body->disposition.type = ucase (cpystr (s));
+ *name = c; /* restore delimiter */
+ rfc822_skipws (&name); /* skip whitespace */
+ rfc822_parse_parameter (&body->disposition.parameter,name);
+ }
+ break;
+ case 'L': /* possible Content-Language */
+ if (!(strcmp (name+1,"ANGUAGE") || body->language)) {
+ stl = NIL; /* process languages */
+ while (s && (name = rfc822_parse_word (s,tspecials))) {
+ c = *name; /* save delimiter */
+ *name = '\0'; /* tie off subtype */
+ if (stl) stl = stl->next = mail_newstringlist ();
+ else stl = body->language = mail_newstringlist ();
+ stl->text.data = (unsigned char *) ucase (cpystr (s));
+ stl->text.size = strlen ((char *) stl->text.data);
+ *name = c; /* restore delimiter */
+ rfc822_skipws (&name); /* skip whitespace */
+ if (*name == ',') { /* any more languages? */
+ s = ++name; /* advance to it them */
+ rfc822_skipws (&s);
+ }
+ else s = NIL; /* bogus or end of list */
+ }
+ }
+ else if (!(strcmp (name+1,"OCATION") || body->location))
+ body->location = cpystr (s);
+ break;
+ case 'M': /* possible Content-MD5 */
+ if (!(strcmp (name+1,"D5") || body->md5)) body->md5 = cpystr (s);
+ break;
+
+ case 'T': /* possible Content-Type/Transfer-Encoding */
+ if (!(strcmp (name+1,"YPE") || body->subtype || body->parameter)) {
+ /* get type word */
+ if (!(name = rfc822_parse_word (s,tspecials))) break;
+ c = *name; /* remember delimiter */
+ *name = '\0'; /* tie off type */
+ /* search for body type */
+ for (i = 0,s = rfc822_cpy (s);
+ (i <= TYPEMAX) && body_types[i] &&
+ compare_cstring (s,body_types[i]); i++);
+ if (i > TYPEMAX) { /* fell off end of loop? */
+ body->type = TYPEOTHER; /* coerce to X-UNKNOWN */
+ sprintf (tmp,"MIME type table overflow: %.100s",s);
+ MM_LOG (tmp,PARSE);
+ }
+ else { /* record body type index */
+ body->type = (unsigned short) i;
+ /* and name if new type */
+ if (body_types[body->type]) fs_give ((void **) &s);
+ else { /* major MIME body type unknown to us */
+ body_types[body->type] = ucase (s);
+ sprintf (tmp,"Unknown MIME type: %.100s",s);
+ MM_LOG (tmp,PARSE);
+ }
+ }
+ *name = c; /* restore delimiter */
+ rfc822_skipws (&name); /* skip whitespace */
+ if ((*name == '/') && /* subtype? */
+ (name = rfc822_parse_word ((s = ++name),tspecials))) {
+ c = *name; /* save delimiter */
+ *name = '\0'; /* tie off subtype */
+ rfc822_skipws (&s); /* copy subtype */
+ if (s) body->subtype = ucase (rfc822_cpy (s));
+ *name = c; /* restore delimiter */
+ rfc822_skipws (&name); /* skip whitespace */
+ }
+ else if (!name) { /* no subtype, was a subtype delimiter? */
+ name = s; /* barf, restore pointer */
+ rfc822_skipws (&name); /* skip leading whitespace */
+ }
+ rfc822_parse_parameter (&body->parameter,name);
+ }
+
+ else if (!strcmp (name+1,"RANSFER-ENCODING")) {
+ if (!(name = rfc822_parse_word (s,tspecials))) break;
+ c = *name; /* remember delimiter */
+ *name = '\0'; /* tie off encoding */
+ /* search for body encoding */
+ for (i = 0,s = rfc822_cpy (s);
+ (i <= ENCMAX) && body_encodings[i] &&
+ compare_cstring (s,body_encodings[i]); i++);
+ if (i > ENCMAX) { /* fell off end of loop? */
+ body->encoding = ENCOTHER;
+ sprintf (tmp,"MIME encoding table overflow: %.100s",s);
+ MM_LOG (tmp,PARSE);
+ }
+ else { /* record body encoding index */
+ body->encoding = (unsigned short) i;
+ /* and name if new encoding */
+ if (body_encodings[body->encoding]) fs_give ((void **) &s);
+ else {
+ body_encodings[body->encoding] = ucase (s);
+ sprintf (tmp,"Unknown MIME transfer encoding: %.100s",s);
+ MM_LOG (tmp,PARSE);
+ }
+ }
+ *name = c; /* restore delimiter */
+ /* ??check for cruft here?? */
+ }
+ break;
+ default: /* otherwise unknown */
+ break;
+ }
+}
+
+/* Parse RFC 2822 body parameter list
+ * Accepts: parameter list to write to
+ * text of list
+ */
+
+void rfc822_parse_parameter (PARAMETER **par,char *text)
+{
+ char c,*s,tmp[MAILTMPLEN];
+ PARAMETER *param = NIL;
+ /* parameter list? */
+ while (text && (*text == ';') &&
+ (text = rfc822_parse_word ((s = ++text),tspecials))) {
+ c = *text; /* remember delimiter */
+ *text = '\0'; /* tie off attribute name */
+ rfc822_skipws (&s); /* skip leading attribute whitespace */
+ if (!*s) *text = c; /* must have an attribute name */
+ else { /* instantiate a new parameter */
+ if (*par) param = param->next = mail_newbody_parameter ();
+ else param = *par = mail_newbody_parameter ();
+ param->attribute = ucase (cpystr (s));
+ *text = c; /* restore delimiter */
+ rfc822_skipws (&text); /* skip whitespace before equal sign */
+ if ((*text == '=') && /* make sure have value */
+ (text = rfc822_parse_word ((s = ++text),tspecials))) {
+ c = *text; /* remember delimiter */
+ *text = '\0'; /* tie off value */
+ rfc822_skipws (&s); /* skip leading value whitespace */
+ if (*s) param->value = rfc822_cpy (s);
+ *text = c; /* restore delimiter */
+ rfc822_skipws (&text);
+ }
+ if (!param->value) { /* value not found? */
+ param->value = cpystr ("MISSING_PARAMETER_VALUE");
+ sprintf (tmp,"Missing parameter value: %.80s",param->attribute);
+ MM_LOG (tmp,PARSE);
+ }
+ }
+ }
+ /* string not present */
+ if (!text) MM_LOG ("Missing parameter",PARSE);
+ else if (*text) { /* must be end of poop */
+ sprintf (tmp,"Unexpected characters at end of parameters: %.80s",text);
+ MM_LOG (tmp,PARSE);
+ }
+}
+
+/* Parse RFC 2822 address list
+ * Accepts: address list to write to
+ * input string
+ * default host name
+ */
+
+void rfc822_parse_adrlist (ADDRESS **lst,char *string,char *host)
+{
+ int c;
+ char *s,tmp[MAILTMPLEN];
+ ADDRESS *last = *lst;
+ ADDRESS *adr;
+ if (!string) return; /* no string */
+ rfc822_skipws (&string); /* skip leading WS */
+ if (!*string) return; /* empty string */
+ /* run to tail of list */
+ if (last) while (last->next) last = last->next;
+ while (string) { /* loop until string exhausted */
+ while (*string == ',') { /* RFC 822 allowed null addresses!! */
+ ++string; /* skip the comma */
+ rfc822_skipws (&string); /* and any leading WS */
+ }
+ if (!*string) string = NIL; /* punt if ran out of string */
+ /* got an address? */
+ else if (adr = rfc822_parse_address (lst,last,&string,host,0)) {
+ last = adr; /* new tail address */
+ if (string) { /* analyze what follows */
+ rfc822_skipws (&string);
+ switch (c = *(unsigned char *) string) {
+ case ',': /* comma? */
+ ++string; /* then another address follows */
+ break;
+ default:
+ s = isalnum (c) ? "Must use comma to separate addresses: %.80s" :
+ "Unexpected characters at end of address: %.80s";
+ sprintf (tmp,s,string);
+ MM_LOG (tmp,PARSE);
+ last = last->next = mail_newaddr ();
+ last->mailbox = cpystr ("UNEXPECTED_DATA_AFTER_ADDRESS");
+ last->host = cpystr (errhst);
+ /* falls through */
+ case '\0': /* null-specified address? */
+ string = NIL; /* punt remainder of parse */
+ break;
+ }
+ }
+ }
+ else if (string) { /* bad mailbox */
+ rfc822_skipws (&string); /* skip WS */
+ if (!*string) strcpy (tmp,"Missing address after comma");
+ else sprintf (tmp,"Invalid mailbox list: %.80s",string);
+ MM_LOG (tmp,PARSE);
+ string = NIL;
+ (adr = mail_newaddr ())->mailbox = cpystr ("INVALID_ADDRESS");
+ adr->host = cpystr (errhst);
+ if (last) last = last->next = adr;
+ else *lst = last = adr;
+ break;
+ }
+ }
+}
+
+/* Parse RFC 2822 address
+ * Accepts: address list to write to
+ * tail of address list
+ * pointer to input string
+ * default host name
+ * group nesting depth
+ * Returns: new list tail
+ */
+
+ADDRESS *rfc822_parse_address (ADDRESS **lst,ADDRESS *last,char **string,
+ char *defaulthost,unsigned long depth)
+{
+ ADDRESS *adr;
+ if (!*string) return NIL; /* no string */
+ rfc822_skipws (string); /* skip leading WS */
+ if (!**string) return NIL; /* empty string */
+ if (adr = rfc822_parse_group (lst,last,string,defaulthost,depth)) last = adr;
+ /* got an address? */
+ else if (adr = rfc822_parse_mailbox (string,defaulthost)) {
+ if (!*lst) *lst = adr; /* yes, first time through? */
+ else last->next = adr; /* no, append to the list */
+ /* set for subsequent linking */
+ for (last = adr; last->next; last = last->next);
+ }
+ else if (*string) return NIL;
+ return last;
+}
+
+/* Parse RFC 2822 group
+ * Accepts: address list to write to
+ * pointer to tail of address list
+ * pointer to input string
+ * default host name
+ * group nesting depth
+ */
+
+ADDRESS *rfc822_parse_group (ADDRESS **lst,ADDRESS *last,char **string,
+ char *defaulthost,unsigned long depth)
+{
+ char tmp[MAILTMPLEN];
+ char *p,*s;
+ ADDRESS *adr;
+ if (depth > MAXGROUPDEPTH) { /* excessively deep recursion? */
+ MM_LOG ("Ignoring excessively deep group recursion",PARSE);
+ return NIL; /* probably abusive */
+ }
+ if (!*string) return NIL; /* no string */
+ rfc822_skipws (string); /* skip leading WS */
+ if (!**string || /* trailing whitespace or not group */
+ ((*(p = *string) != ':') && !(p = rfc822_parse_phrase (*string))))
+ return NIL;
+ s = p; /* end of candidate phrase */
+ rfc822_skipws (&s); /* find delimiter */
+ if (*s != ':') return NIL; /* not really a group */
+ *p = '\0'; /* tie off group name */
+ p = ++s; /* continue after the delimiter */
+ rfc822_skipws (&p); /* skip subsequent whitespace */
+ /* write as address */
+ (adr = mail_newaddr ())->mailbox = rfc822_cpy (*string);
+ if (!*lst) *lst = adr; /* first time through? */
+ else last->next = adr; /* no, append to the list */
+ last = adr; /* set for subsequent linking */
+ *string = p; /* continue after this point */
+ while (*string && **string && (**string != ';')) {
+ if (adr = rfc822_parse_address (lst,last,string,defaulthost,depth+1)) {
+ last = adr; /* new tail address */
+ if (*string) { /* anything more? */
+ rfc822_skipws (string); /* skip whitespace */
+ switch (**string) { /* see what follows */
+ case ',': /* another address? */
+ ++*string; /* yes, skip past the comma */
+ case ';': /* end of group? */
+ case '\0': /* end of string */
+ break;
+ default:
+ sprintf (tmp,"Unexpected characters after address in group: %.80s",
+ *string);
+ MM_LOG (tmp,PARSE);
+ *string = NIL; /* cancel remainder of parse */
+ last = last->next = mail_newaddr ();
+ last->mailbox = cpystr ("UNEXPECTED_DATA_AFTER_ADDRESS_IN_GROUP");
+ last->host = cpystr (errhst);
+ }
+ }
+ }
+ else { /* bogon */
+ sprintf (tmp,"Invalid group mailbox list: %.80s",*string);
+ MM_LOG (tmp,PARSE);
+ *string = NIL; /* cancel remainder of parse */
+ (adr = mail_newaddr ())->mailbox = cpystr ("INVALID_ADDRESS_IN_GROUP");
+ adr->host = cpystr (errhst);
+ last = last->next = adr;
+ }
+ }
+ if (*string) { /* skip close delimiter */
+ if (**string == ';') ++*string;
+ rfc822_skipws (string);
+ }
+ /* append end of address mark to the list */
+ last->next = (adr = mail_newaddr ());
+ last = adr; /* set for subsequent linking */
+ return last; /* return the tail */
+}
+
+/* Parse RFC 2822 mailbox
+ * Accepts: pointer to string pointer
+ * default host
+ * Returns: address list
+ *
+ * Updates string pointer
+ */
+
+ADDRESS *rfc822_parse_mailbox (char **string,char *defaulthost)
+{
+ ADDRESS *adr = NIL;
+ char *s,*end;
+ parsephrase_t pp = (parsephrase_t) mail_parameters (NIL,GET_PARSEPHRASE,NIL);
+ if (!*string) return NIL; /* no string */
+ rfc822_skipws (string); /* flush leading whitespace */
+ if (!**string) return NIL; /* empty string */
+ if (*(s = *string) == '<') /* note start, handle case of phraseless RA */
+ adr = rfc822_parse_routeaddr (s,string,defaulthost);
+ /* otherwise, expect at least one word */
+ else if (end = rfc822_parse_phrase (s)) {
+ if ((adr = rfc822_parse_routeaddr (end,string,defaulthost))) {
+ /* phrase is a personal name */
+ if (adr->personal) fs_give ((void **) &adr->personal);
+ *end = '\0'; /* tie off phrase */
+ adr->personal = rfc822_cpy (s);
+ }
+ /* call external phraseparser if phrase only */
+ else if (pp && rfc822_phraseonly (end) &&
+ (adr = (*pp) (s,end,defaulthost))) {
+ *string = end; /* update parse pointer */
+ rfc822_skipws (string); /* skip WS in the normal way */
+ }
+ else adr = rfc822_parse_addrspec (s,string,defaulthost);
+ }
+ return adr; /* return the address */
+}
+
+
+/* Check if address is a phrase only
+ * Accepts: pointer to end of phrase
+ * Returns: T if phrase only, else NIL;
+ */
+
+long rfc822_phraseonly (char *end)
+{
+ while (*end == ' ') ++end; /* call rfc822_skipws() instead?? */
+ switch (*end) {
+ case '\0': case ',': case ';':
+ return LONGT; /* is a phrase only */
+ }
+ return NIL; /* something other than phase is here */
+}
+
+/* Parse RFC 2822 route-address
+ * Accepts: string pointer
+ * pointer to string pointer to update
+ * Returns: address
+ *
+ * Updates string pointer
+ */
+
+ADDRESS *rfc822_parse_routeaddr (char *string,char **ret,char *defaulthost)
+{
+ char tmp[MAILTMPLEN];
+ ADDRESS *adr;
+ char *s,*t,*adl;
+ size_t adllen,i;
+ if (!string) return NIL;
+ rfc822_skipws (&string); /* flush leading whitespace */
+ /* must start with open broket */
+ if (*string != '<') return NIL;
+ t = ++string; /* see if A-D-L there */
+ rfc822_skipws (&t); /* flush leading whitespace */
+ for (adl = NIL,adllen = 0; /* parse possible A-D-L */
+ (*t == '@') && (s = rfc822_parse_domain (t+1,&t));) {
+ i = strlen (s) + 2; /* @ plus domain plus delimiter or NUL */
+ if (adl) { /* have existing A-D-L? */
+ fs_resize ((void **) &adl,adllen + i);
+ sprintf (adl + adllen - 1,",@%s",s);
+ }
+ /* write initial A-D-L */
+ else sprintf (adl = (char *) fs_get (i),"@%s",s);
+ adllen += i; /* new A-D-L length */
+ fs_give ((void **) &s); /* don't need domain any more */
+ rfc822_skipws (&t); /* skip WS */
+ if (*t != ',') break; /* put if not comma */
+ t++; /* skip the comma */
+ rfc822_skipws (&t); /* skip WS */
+ }
+ if (adl) { /* got an A-D-L? */
+ if (*t != ':') { /* make sure syntax good */
+ sprintf (tmp,"Unterminated at-domain-list: %.80s%.80s",adl,t);
+ MM_LOG (tmp,PARSE);
+ }
+ else string = ++t; /* continue parse from this point */
+ }
+
+ /* parse address spec */
+ if (!(adr = rfc822_parse_addrspec (string,ret,defaulthost))) {
+ if (adl) fs_give ((void **) &adl);
+ return NIL;
+ }
+ if (adl) adr->adl = adl; /* have an A-D-L? */
+ if (*ret) if (**ret == '>') { /* make sure terminated OK */
+ ++*ret; /* skip past the broket */
+ rfc822_skipws (ret); /* flush trailing WS */
+ if (!**ret) *ret = NIL; /* wipe pointer if at end of string */
+ return adr; /* return the address */
+ }
+ sprintf (tmp,"Unterminated mailbox: %.80s@%.80s",adr->mailbox,
+ *adr->host == '@' ? "<null>" : adr->host);
+ MM_LOG (tmp,PARSE);
+ adr->next = mail_newaddr ();
+ adr->next->mailbox = cpystr ("MISSING_MAILBOX_TERMINATOR");
+ adr->next->host = cpystr (errhst);
+ return adr; /* return the address */
+}
+
+/* Parse RFC 2822 address-spec
+ * Accepts: string pointer
+ * pointer to string pointer to update
+ * default host
+ * Returns: address
+ *
+ * Updates string pointer
+ */
+
+ADDRESS *rfc822_parse_addrspec (char *string,char **ret,char *defaulthost)
+{
+ ADDRESS *adr;
+ char c,*s,*t,*v,*end;
+ if (!string) return NIL; /* no string */
+ rfc822_skipws (&string); /* flush leading whitespace */
+ if (!*string) return NIL; /* empty string */
+ /* find end of mailbox */
+ if (!(t = rfc822_parse_word (string,wspecials))) return NIL;
+ adr = mail_newaddr (); /* create address block */
+ c = *t; /* remember delimiter */
+ *t = '\0'; /* tie off mailbox */
+ /* copy mailbox */
+ adr->mailbox = rfc822_cpy (string);
+ *t = c; /* restore delimiter */
+ end = t; /* remember end of mailbox */
+ rfc822_skipws (&t); /* skip whitespace */
+ while (*t == '.') { /* some cretin taking RFC 822 too seriously? */
+ string = ++t; /* skip past the dot and any WS */
+ rfc822_skipws (&string);
+ /* get next word of mailbox */
+ if (t = rfc822_parse_word (string,wspecials)) {
+ end = t; /* remember new end of mailbox */
+ c = *t; /* remember delimiter */
+ *t = '\0'; /* tie off word */
+ s = rfc822_cpy (string); /* copy successor part */
+ *t = c; /* restore delimiter */
+ /* build new mailbox */
+ sprintf (v = (char *) fs_get (strlen (adr->mailbox) + strlen (s) + 2),
+ "%s.%s",adr->mailbox,s);
+ fs_give ((void **) &adr->mailbox);
+ adr->mailbox = v; /* new host name */
+ rfc822_skipws (&t); /* skip WS after mailbox */
+ }
+ else { /* barf */
+ MM_LOG ("Invalid mailbox part after .",PARSE);
+ break;
+ }
+ }
+ t = end; /* remember delimiter in case no host */
+
+ rfc822_skipws (&end); /* sniff ahead at what follows */
+#if RFC733 /* RFC 733 used "at" instead of "@" */
+ if (((*end == 'a') || (*end == 'A')) &&
+ ((end[1] == 't') || (end[1] == 'T')) &&
+ ((end[2] == ' ') || (end[2] == '\t') || (end[2] == '\015') ||
+ (end[2] == '\012') || (end[2] == '(')))
+ *++end = '@';
+#endif
+ if (*end != '@') end = t; /* host name missing */
+ /* otherwise parse host name */
+ else if (!(adr->host = rfc822_parse_domain (++end,&end)))
+ adr->host = cpystr (errhst);
+ /* default host if missing */
+ if (!adr->host) adr->host = cpystr (defaulthost);
+ /* try person name in comments if missing */
+ if (end && !(adr->personal && *adr->personal)) {
+ while (*end == ' ') ++end; /* see if we can find a person name here */
+ if ((*end == '(') && (s = rfc822_skip_comment (&end,LONGT)) && strlen (s))
+ adr->personal = rfc822_cpy (s);
+ rfc822_skipws (&end); /* skip any other WS in the normal way */
+ }
+ /* set return to end pointer */
+ *ret = (end && *end) ? end : NIL;
+ return adr; /* return the address we got */
+}
+
+/* Parse RFC 2822 domain
+ * Accepts: string pointer
+ * pointer to return end of domain
+ * Returns: domain name or NIL if failure
+ */
+
+char *rfc822_parse_domain (char *string,char **end)
+{
+ char *ret = NIL;
+ char c,*s,*t,*v;
+ rfc822_skipws (&string); /* skip whitespace */
+ if (*string == '[') { /* domain literal? */
+ if (!(*end = rfc822_parse_word (string + 1,"]\\")))
+ MM_LOG ("Empty domain literal",PARSE);
+ else if (**end != ']') MM_LOG ("Unterminated domain literal",PARSE);
+ else {
+ size_t len = ++*end - string;
+ strncpy (ret = (char *) fs_get (len + 1),string,len);
+ ret[len] = '\0'; /* tie off literal */
+ }
+ }
+ /* search for end of host */
+ else if (t = rfc822_parse_word (string,wspecials)) {
+ c = *t; /* remember delimiter */
+ *t = '\0'; /* tie off host */
+ ret = rfc822_cpy (string); /* copy host */
+ *t = c; /* restore delimiter */
+ *end = t; /* remember end of domain */
+ rfc822_skipws (&t); /* skip WS after host */
+ while (*t == '.') { /* some cretin taking RFC 822 too seriously? */
+ string = ++t; /* skip past the dot and any WS */
+ rfc822_skipws (&string);
+ if (string = rfc822_parse_domain (string,&t)) {
+ *end = t; /* remember new end of domain */
+ c = *t; /* remember delimiter */
+ *t = '\0'; /* tie off host */
+ s = rfc822_cpy (string);/* copy successor part */
+ *t = c; /* restore delimiter */
+ /* build new domain */
+ sprintf (v = (char *) fs_get (strlen (ret) + strlen (s) + 2),
+ "%s.%s",ret,s);
+ fs_give ((void **) &ret);
+ ret = v; /* new host name */
+ rfc822_skipws (&t); /* skip WS after domain */
+ }
+ else { /* barf */
+ MM_LOG ("Invalid domain part after .",PARSE);
+ break;
+ }
+ }
+ }
+ else MM_LOG ("Missing or invalid host name after @",PARSE);
+ return ret;
+}
+
+/* Parse RFC 2822 phrase
+ * Accepts: string pointer
+ * Returns: pointer to end of phrase
+ */
+
+char *rfc822_parse_phrase (char *s)
+{
+ char *curpos;
+ if (!s) return NIL; /* no-op if no string */
+ /* find first word of phrase */
+ curpos = rfc822_parse_word (s,NIL);
+ if (!curpos) return NIL; /* no words means no phrase */
+ if (!*curpos) return curpos; /* check if string ends with word */
+ s = curpos; /* sniff past the end of this word and WS */
+ rfc822_skipws (&s); /* skip whitespace */
+ /* recurse to see if any more */
+ return (s = rfc822_parse_phrase (s)) ? s : curpos;
+}
+
+/* Parse RFC 2822 word
+ * Accepts: string pointer
+ * delimiter (or NIL for phrase word parsing)
+ * Returns: pointer to end of word
+ */
+
+char *rfc822_parse_word (char *s,const char *delimiters)
+{
+ char *st,*str;
+ if (!s) return NIL; /* no string */
+ rfc822_skipws (&s); /* flush leading whitespace */
+ if (!*s) return NIL; /* empty string */
+ str = s; /* hunt pointer for strpbrk */
+ while (T) { /* look for delimiter, return if none */
+ if (!(st = strpbrk (str,delimiters ? delimiters : wspecials)))
+ return str + strlen (str);
+ /* ESC in phrase */
+ if (!delimiters && (*st == I2C_ESC)) {
+ str = ++st; /* always skip past ESC */
+ switch (*st) { /* special hack for RFC 1468 (ISO-2022-JP) */
+ case I2C_MULTI: /* multi byte sequence */
+ switch (*++st) {
+ case I2CS_94x94_JIS_OLD:/* old JIS (1978) */
+ case I2CS_94x94_JIS_NEW:/* new JIS (1983) */
+ str = ++st; /* skip past the shift to JIS */
+ while (st = strchr (st,I2C_ESC))
+ if ((*++st == I2C_G0_94) && ((st[1] == I2CS_94_ASCII) ||
+ (st[1] == I2CS_94_JIS_ROMAN) ||
+ (st[1] == I2CS_94_JIS_BUGROM))) {
+ str = st += 2; /* skip past the shift back to ASCII */
+ break;
+ }
+ /* eats entire text if no shift back */
+ if (!st || !*st) return str + strlen (str);
+ }
+ break;
+ case I2C_G0_94: /* single byte sequence */
+ switch (st[1]) {
+ case I2CS_94_ASCII: /* shift to ASCII */
+ case I2CS_94_JIS_ROMAN: /* shift to JIS-Roman */
+ case I2CS_94_JIS_BUGROM:/* old buggy definition of JIS-Roman */
+ str = st + 2; /* skip past the shift */
+ break;
+ }
+ }
+ }
+
+ else switch (*st) { /* dispatch based on delimiter */
+ case '"': /* quoted string */
+ /* look for close quote */
+ while (*++st != '"') switch (*st) {
+ case '\0': /* unbalanced quoted string */
+ return NIL; /* sick sick sick */
+ case '\\': /* quoted character */
+ if (!*++st) return NIL; /* skip the next character */
+ default: /* ordinary character */
+ break; /* no special action */
+ }
+ str = ++st; /* continue parse */
+ break;
+ case '\\': /* quoted character */
+ /* This is wrong; a quoted-pair can not be part of a word. However,
+ * domain-literal is parsed as a word and quoted-pairs can be used
+ * *there*. Either way, it's pretty pathological.
+ */
+ if (st[1]) { /* not on NUL though... */
+ str = st + 2; /* skip quoted character and go on */
+ break;
+ }
+ default: /* found a word delimiter */
+ return (st == s) ? NIL : st;
+ }
+ }
+}
+
+/* Copy an RFC 2822 format string
+ * Accepts: string
+ * Returns: copy of string
+ */
+
+char *rfc822_cpy (char *src)
+{
+ /* copy and unquote */
+ return rfc822_quote (cpystr (src));
+}
+
+
+/* Unquote an RFC 2822 format string
+ * Accepts: string
+ * Returns: string
+ */
+
+char *rfc822_quote (char *src)
+{
+ char *ret = src;
+ if (strpbrk (src,"\\\"")) { /* any quoting in string? */
+ char *dst = ret;
+ while (*src) { /* copy string */
+ if (*src == '\"') src++; /* skip double quote entirely */
+ else {
+ if (*src == '\\') src++;/* skip over single quote, copy next always */
+ *dst++ = *src++; /* copy character */
+ }
+ }
+ *dst = '\0'; /* tie off string */
+ }
+ return ret; /* return our string */
+}
+
+
+/* Copy address list
+ * Accepts: address list
+ * Returns: address list
+ */
+
+ADDRESS *rfc822_cpy_adr (ADDRESS *adr)
+{
+ ADDRESS *dadr;
+ ADDRESS *ret = NIL;
+ ADDRESS *prev = NIL;
+ while (adr) { /* loop while there's still an MAP adr */
+ dadr = mail_newaddr (); /* instantiate a new address */
+ if (!ret) ret = dadr; /* note return */
+ if (prev) prev->next = dadr;/* tie on to the end of any previous */
+ dadr->personal = cpystr (adr->personal);
+ dadr->adl = cpystr (adr->adl);
+ dadr->mailbox = cpystr (adr->mailbox);
+ dadr->host = cpystr (adr->host);
+ prev = dadr; /* this is now the previous */
+ adr = adr->next; /* go to next address in list */
+ }
+ return (ret); /* return the MTP address list */
+}
+
+/* Skips RFC 2822 whitespace
+ * Accepts: pointer to string pointer
+ */
+
+void rfc822_skipws (char **s)
+{
+ while (T) switch (**s) {
+ case ' ': case '\t': case '\015': case '\012':
+ ++*s; /* skip all forms of LWSP */
+ break;
+ case '(': /* start of comment */
+ if (rfc822_skip_comment (s,(long) NIL)) break;
+ default:
+ return; /* end of whitespace */
+ }
+}
+
+
+/* Skips RFC 2822 comment
+ * Accepts: pointer to string pointer
+ * trim flag
+ * Returns: pointer to first non-blank character of comment
+ */
+
+char *rfc822_skip_comment (char **s,long trim)
+{
+ char *ret,tmp[MAILTMPLEN];
+ char *s1 = *s;
+ char *t = NIL;
+ /* skip past whitespace */
+ for (ret = ++s1; *ret == ' '; ret++);
+ do switch (*s1) { /* get character of comment */
+ case '(': /* nested comment? */
+ if (!rfc822_skip_comment (&s1,(long) NIL)) return NIL;
+ t = --s1; /* last significant char at end of comment */
+ break;
+ case ')': /* end of comment? */
+ *s = ++s1; /* skip past end of comment */
+ if (trim) { /* if level 0, must trim */
+ if (t) t[1] = '\0'; /* tie off comment string */
+ else *ret = '\0'; /* empty comment */
+ }
+ return ret;
+ case '\\': /* quote next character? */
+ if (*++s1) { /* next character non-null? */
+ t = s1; /* update last significant character pointer */
+ break; /* all OK */
+ }
+ case '\0': /* end of string */
+ sprintf (tmp,"Unterminated comment: %.80s",*s);
+ MM_LOG (tmp,PARSE);
+ **s = '\0'; /* nuke duplicate messages in case reparse */
+ return NIL; /* this is wierd if it happens */
+ case ' ': /* whitespace isn't significant */
+ break;
+ default: /* random character */
+ t = s1; /* update last significant character pointer */
+ break;
+ } while (s1++);
+ return NIL; /* impossible, but pacify lint et al */
+}
+
+/* Buffered output routines */
+
+
+/* Output character to buffer
+ * Accepts: buffer
+ * character to write
+ * Returns: T if success, NIL if error
+ */
+
+static long rfc822_output_char (RFC822BUFFER *buf,int c)
+{
+ if ((buf->cur == buf->end) && !rfc822_output_flush (buf)) return NIL;
+ *buf->cur++ = c; /* add character, soutr buffer if full */
+ return (buf->cur == buf->end) ? rfc822_output_flush (buf) : LONGT;
+}
+
+
+/* Output data to buffer
+ * Accepts: buffer
+ * data to write
+ * size of data
+ * Returns: T if success, NIL if error
+ */
+
+static long rfc822_output_data (RFC822BUFFER *buf,char *string,long len)
+{
+ while (len) { /* until request satified */
+ long i;
+ if (i = min (len,buf->end - buf->cur)) {
+ memcpy (buf->cur,string,i);
+ buf->cur += i; /* blat data */
+ string += i;
+ len -= i;
+ }
+ /* soutr buffer now if full */
+ if ((len || (buf->cur == buf->end)) && !rfc822_output_flush (buf))
+ return NIL;
+ }
+ return LONGT;
+}
+
+/* Output string to buffer
+ * Accepts: buffer
+ * string to write
+ * Returns: T if success, NIL if error
+ */
+
+static long rfc822_output_string (RFC822BUFFER *buf,char *string)
+{
+ return rfc822_output_data (buf,string,strlen (string));
+}
+
+
+/* Flush buffer
+ * Accepts: buffer
+ * I/O routine
+ * stream for I/O routine
+ * Returns: T if success, NIL if error
+ */
+
+long rfc822_output_flush (RFC822BUFFER *buf)
+{
+ *buf->cur = '\0'; /* tie off buffer at this point */
+ return (*buf->f) (buf->s,buf->cur = buf->beg);
+}
+
+/* Message writing routines */
+
+
+/* Output RFC 822 message
+ * Accepts: temporary buffer as a SIZEDTEXT
+ * envelope
+ * body
+ * I/O routine
+ * stream for I/O routine
+ * non-zero if 8-bit output desired
+ * Returns: T if successful, NIL if failure
+ *
+ * This routine always uses standard specials for phrases and does not write
+ * bcc entries, since it is called from the SMTP and NNTP routines. If you
+ * need to do something different you need to arm an rfc822outfull_t and/or
+ * rfc822out_t function.
+ */
+
+long rfc822_output_full (RFC822BUFFER *buf,ENVELOPE *env,BODY *body,long ok8)
+{
+ rfc822outfull_t r822of =
+ (rfc822outfull_t) mail_parameters (NIL,GET_RFC822OUTPUTFULL,NIL);
+ rfc822out_t r822o = (rfc822out_t) mail_parameters (NIL,GET_RFC822OUTPUT,NIL);
+ /* call external RFC 2822 output generator */
+ if (r822of) return (*r822of) (buf,env,body,ok8);
+ else if (r822o) return (*r822o) (buf->cur,env,body,buf->f,buf->s,ok8);
+ /* encode body as necessary */
+ if (ok8) rfc822_encode_body_8bit (env,body);
+ else rfc822_encode_body_7bit (env,body);
+ /* output header and body */
+ return rfc822_output_header (buf,env,body,NIL,NIL) &&
+ rfc822_output_text (buf,body) && rfc822_output_flush (buf);
+}
+
+/* Output RFC 822 header
+ * Accepts: buffer
+ * envelope
+ * body
+ * non-standard specials to be used for phrases if non-NIL
+ * flags (non-zero to include bcc
+ * Returns: T if success, NIL if failure
+ */
+
+long rfc822_output_header (RFC822BUFFER *buf,ENVELOPE *env,BODY *body,
+ const char *specials,long flags)
+{
+ long i = env->remail ? strlen (env->remail) : 0;
+ return /* write header */
+ (!i || /* snip extra CRLF from remail header */
+ rfc822_output_data (buf,env->remail,
+ ((i > 4) && (env->remail[i-4] == '\015')) ?
+ i - 2 : i)) &&
+ rfc822_output_header_line (buf,"Newsgroups",i,env->newsgroups) &&
+ rfc822_output_header_line (buf,"Date",i,env->date) &&
+ rfc822_output_address_line (buf,"From",i,env->from,specials) &&
+ rfc822_output_address_line (buf,"Sender",i,env->sender,specials) &&
+ rfc822_output_address_line (buf,"Reply-To",i,env->reply_to,specials) &&
+ rfc822_output_header_line (buf,"Subject",i,env->subject) &&
+ ((env->bcc && !(env->to || env->cc)) ?
+ rfc822_output_string (buf,"To: undisclosed recipients: ;\015\012") :
+ LONGT) &&
+ rfc822_output_address_line (buf,"To",i,env->to,specials) &&
+ rfc822_output_address_line (buf,"cc",i,env->cc,specials) &&
+ (flags ? rfc822_output_address_line (buf,"bcc",i,env->bcc,specials) : T) &&
+ rfc822_output_header_line (buf,"In-Reply-To",i,env->in_reply_to) &&
+ rfc822_output_header_line (buf,"Message-ID",i,env->message_id) &&
+ rfc822_output_header_line (buf,"Followup-to",i,env->followup_to) &&
+ rfc822_output_header_line (buf,"References",i,env->references) &&
+ (env->remail || !body ||
+ (rfc822_output_string (buf,"MIME-Version: 1.0\015\012") &&
+ rfc822_output_body_header (buf,body))) &&
+ /* write terminating blank line */
+ rfc822_output_string (buf,"\015\012");
+}
+
+/* Output RFC 2822 header text line
+ * Accepts: buffer
+ * pointer to header type
+ * non-NIL if resending
+ * pointer to text
+ * Returns: T if success, NIL if failure
+ */
+
+long rfc822_output_header_line (RFC822BUFFER *buf,char *type,long resent,
+ char *text)
+{
+ return !text ||
+ ((resent ? rfc822_output_string (buf,resentprefix) : LONGT) &&
+ rfc822_output_string (buf,type) && rfc822_output_string (buf,": ") &&
+ rfc822_output_string (buf,text) && rfc822_output_string (buf,"\015\012"));
+}
+
+
+/* Output RFC 2822 header address line
+ * Accepts: buffer
+ * pointer to header type
+ * non-NIL if resending
+ * address(s) to interpret
+ * non-standard specials to be used for phrases if non-NIL
+ * Returns: T if success, NIL if failure
+ */
+
+long rfc822_output_address_line (RFC822BUFFER *buf,char *type,long resent,
+ ADDRESS *adr,const char *specials)
+{
+ long pretty = strlen (type);
+ return !adr ||
+ ((resent ? rfc822_output_string (buf,resentprefix) : LONGT) &&
+ rfc822_output_data (buf,type,pretty) && rfc822_output_string (buf,": ") &&
+ rfc822_output_address_list (buf,adr,
+ resent ? pretty + sizeof (RESENTPREFIX) - 1 :
+ pretty,specials) &&
+ rfc822_output_string (buf,"\015\012"));
+}
+
+/* Output RFC 2822 address list
+ * Accepts: buffer
+ * pointer to address list
+ * non-zero if pretty-printing
+ * non-standard specials to be used for phrases if non-NIL
+ * Returns: T if success, NIL if failure
+ */
+
+long rfc822_output_address_list (RFC822BUFFER *buf,ADDRESS *adr,long pretty,
+ const char *specials)
+{
+ long n;
+ /* default to rspecials */
+ if (!specials) specials = rspecials;
+ for (n = 0; adr; adr = adr->next) {
+ char *base = buf->cur;
+ if (adr->host) { /* ordinary address? */
+ if (!(pretty && n)) { /* suppress if pretty and in group */
+ if ( /* use phrase <route-addr> if phrase */
+#if RFC822
+ adr->adl || /* or A-D-L */
+#endif
+ (adr->personal && *adr->personal)) {
+ if (!((adr->personal ? rfc822_output_cat (buf,adr->personal,
+ rspecials) : LONGT) &&
+ rfc822_output_string (buf," <") &&
+ rfc822_output_address (buf,adr) &&
+ rfc822_output_string (buf,">"))) return NIL;
+ }
+ else if (!rfc822_output_address (buf,adr)) return NIL;
+ if (adr->next && adr->next->mailbox &&
+ !rfc822_output_string (buf,", ")) return NIL;
+ }
+ }
+ else if (adr->mailbox) { /* start of group? */
+ /* yes, write group */
+ if (!(rfc822_output_cat (buf,adr->mailbox,rspecials) &&
+ rfc822_output_string (buf,": "))) return NIL;
+ ++n; /* in a group now */
+ }
+ else if (n) { /* must be end of group (but be paranoid) */
+ if (!rfc822_output_char (buf,';') ||
+ ((!--n && adr->next && adr->next->mailbox) &&
+ !rfc822_output_string (buf,", "))) return NIL;
+ }
+ if (pretty && adr->next && /* pretty printing? */
+ ((pretty += ((buf->cur > base) ? buf->cur - base :
+ (buf->end - base) + (buf->cur - buf->beg))) >= 78)) {
+ if (!(rfc822_output_string (buf,"\015\012") &&
+ rfc822_output_string (buf,RFC822CONT))) return NIL;
+ base = buf->cur; /* update base for pretty printing */
+ pretty = sizeof (RFC822CONT) - 1;
+ }
+ }
+ return LONGT;
+}
+
+/* Write RFC 2822 route-address to string
+ * Accepts: buffer
+ * pointer to single address
+ * Returns: T if success, NIL if failure
+ */
+
+long rfc822_output_address (RFC822BUFFER *buf,ADDRESS *adr)
+{
+ return !adr || !adr->host ||
+ (
+#if RFC822 /* old code with A-D-L support */
+ (!adr->adl || (rfc822_output_string (buf,adr->adl) &&
+ rfc822_output_char (buf,':'))) &&
+#endif
+ rfc822_output_cat (buf,adr->mailbox,NIL) &&
+ ((*adr->host == '@') || /* unless null host (HIGHLY discouraged!) */
+ (rfc822_output_char (buf,'@') &&
+ rfc822_output_cat (buf,adr->host,NIL))));
+}
+
+
+/* Output RFC 2822 string with concatenation
+ * Accepts: buffer
+ * string to concatenate
+ * list of special characters or NIL for dot-atom format
+ * Returns: T if success, NIL if failure
+ */
+
+long rfc822_output_cat (RFC822BUFFER *buf,char *src,const char *specials)
+{
+ char *s;
+ if (!*src || /* empty string or any specials present? */
+ (specials ? (T && strpbrk (src,specials)) :
+ (strpbrk (src,wspecials) || (*src == '.') || strstr (src,"..") ||
+ (src[strlen (src) - 1] == '.')))) {
+ /* yes, write as quoted string*/
+ if (!rfc822_output_char (buf,'"')) return NIL;
+ /* embedded quote characters? */
+ for (; s = strpbrk (src,"\\\""); src = s + 1) {
+ /* yes, insert quoting */
+ if (!(rfc822_output_data (buf,src,s-src) &&
+ rfc822_output_char (buf,'\\') &&
+ rfc822_output_char (buf,*s))) return NIL;
+ }
+ /* return string and trailing quote*/
+ return rfc822_output_string (buf,src) && rfc822_output_char (buf,'"');
+ }
+ /* easy case */
+ return rfc822_output_string (buf,src);
+}
+
+/* Output MIME parameter list
+ * Accepts: buffer
+ * parameter list
+ * Returns: T if success, NIL if failure
+ */
+
+long rfc822_output_parameter (RFC822BUFFER *buf,PARAMETER *param)
+{
+ while (param) {
+ if (rfc822_output_string (buf,"; ") &&
+ rfc822_output_string (buf,param->attribute) &&
+ rfc822_output_char (buf,'=') &&
+ rfc822_output_cat (buf,param->value,tspecials)) param = param->next;
+ else return NIL;
+ }
+ return LONGT;
+}
+
+
+/* Output RFC 2822 stringlist
+ * Accepts: buffer
+ * stringlist
+ * Returns: T if success, NIL if failure
+ */
+
+long rfc822_output_stringlist (RFC822BUFFER *buf,STRINGLIST *stl)
+{
+ while (stl)
+ if (!rfc822_output_cat (buf,(char *) stl->text.data,tspecials) ||
+ ((stl = stl->next) && !rfc822_output_string (buf,", ")))
+ return NIL;
+ return LONGT;
+}
+
+/* Output body content header
+ * Accepts: buffer
+ * body to interpret
+ * Returns: T if success, NIL if failure
+ */
+
+long rfc822_output_body_header (RFC822BUFFER *buf,BODY *body)
+{
+ return /* type and subtype*/
+ rfc822_output_string (buf,"Content-Type: ") &&
+ rfc822_output_string (buf,body_types[body->type]) &&
+ rfc822_output_char (buf,'/') &&
+ rfc822_output_string (buf,body->subtype ? body->subtype :
+ rfc822_default_subtype (body->type)) &&
+ /* parameters (w/ US-ASCII default */
+ (body->parameter ? rfc822_output_parameter (buf,body->parameter) :
+ ((body->type != TYPETEXT) ||
+ (rfc822_output_string (buf,"; CHARSET=") &&
+ rfc822_output_string (buf,(body->encoding == ENC7BIT) ?
+ "US-ASCII" : "X-UNKNOWN")))) &&
+ (!body->encoding || /* note: 7BIT never output as encoding! */
+ (rfc822_output_string (buf,"\015\012Content-Transfer-Encoding: ") &&
+ rfc822_output_string (buf,body_encodings[body->encoding]))) &&
+ (!body->id || /* identification */
+ (rfc822_output_string (buf,"\015\012Content-ID: ") &&
+ rfc822_output_string (buf,body->id))) &&
+ (!body->description || /* description */
+ (rfc822_output_string (buf,"\015\012Content-Description: ") &&
+ rfc822_output_string (buf,body->description))) &&
+ (!body->md5 || /* MD5 checksum */
+ (rfc822_output_string (buf,"\015\012Content-MD5: ") &&
+ rfc822_output_string (buf,body->md5))) &&
+ (!body->language || /* language */
+ (rfc822_output_string (buf,"\015\012Content-Language: ") &&
+ rfc822_output_stringlist (buf,body->language))) &&
+ (!body->location || /* location */
+ (rfc822_output_string (buf,"\015\012Content-Location: ") &&
+ rfc822_output_string (buf,body->location))) &&
+ (!body->disposition.type || /* disposition */
+ (rfc822_output_string (buf,"\015\012Content-Disposition: ") &&
+ rfc822_output_string (buf,body->disposition.type) &&
+ rfc822_output_parameter (buf,body->disposition.parameter))) &&
+ rfc822_output_string (buf,"\015\012");
+}
+
+/* Encode a body for 7BIT transmittal
+ * Accepts: envelope
+ * body
+ */
+
+void rfc822_encode_body_7bit (ENVELOPE *env,BODY *body)
+{
+ void *f;
+ PART *part;
+ PARAMETER **param;
+ if (body) switch (body->type) {
+ case TYPEMULTIPART: /* multi-part */
+ for (param = &body->parameter;
+ *param && strcmp ((*param)->attribute,"BOUNDARY");
+ param = &(*param)->next);
+ if (!*param) { /* cookie not set up yet? */
+ char tmp[MAILTMPLEN]; /* make cookie not in BASE64 or QUOTEPRINT*/
+ sprintf (tmp,"%lu-%lu-%lu=:%lu",(unsigned long) gethostid (),
+ (unsigned long) random (),(unsigned long) time (0),
+ (unsigned long) getpid ());
+ (*param) = mail_newbody_parameter ();
+ (*param)->attribute = cpystr ("BOUNDARY");
+ (*param)->value = cpystr (tmp);
+ }
+ part = body->nested.part; /* encode body parts */
+ do rfc822_encode_body_7bit (env,&part->body);
+ while (part = part->next); /* until done */
+ break;
+ case TYPEMESSAGE: /* encapsulated message */
+ switch (body->encoding) {
+ case ENC7BIT:
+ break;
+ case ENC8BIT:
+ MM_LOG ("8-bit included message in 7-bit message body",PARSE);
+ break;
+ case ENCBINARY:
+ MM_LOG ("Binary included message in 7-bit message body",PARSE);
+ break;
+ default:
+ fatal ("Invalid rfc822_encode_body_7bit message encoding");
+ }
+ break; /* can't change encoding */
+ default: /* all else has some encoding */
+ switch (body->encoding) {
+ case ENC8BIT: /* encode 8BIT into QUOTED-PRINTABLE */
+ /* remember old 8-bit contents */
+ f = (void *) body->contents.text.data;
+ body->contents.text.data =
+ rfc822_8bit (body->contents.text.data,
+ body->contents.text.size,&body->contents.text.size);
+ body->encoding = ENCQUOTEDPRINTABLE;
+ fs_give (&f); /* flush old binary contents */
+ break;
+ case ENCBINARY: /* encode binary into BASE64 */
+ /* remember old binary contents */
+ f = (void *) body->contents.text.data;
+ body->contents.text.data =
+ rfc822_binary ((void *) body->contents.text.data,
+ body->contents.text.size,&body->contents.text.size);
+ body->encoding = ENCBASE64;
+ fs_give (&f); /* flush old binary contents */
+ default: /* otherwise OK */
+ break;
+ }
+ break;
+ }
+}
+
+/* Encode a body for 8BIT transmittal
+ * Accepts: envelope
+ * body
+ */
+
+void rfc822_encode_body_8bit (ENVELOPE *env,BODY *body)
+{
+ void *f;
+ PART *part;
+ PARAMETER **param;
+ if (body) switch (body->type) {
+ case TYPEMULTIPART: /* multi-part */
+ for (param = &body->parameter;
+ *param && strcmp ((*param)->attribute,"BOUNDARY");
+ param = &(*param)->next);
+ if (!*param) { /* cookie not set up yet? */
+ char tmp[MAILTMPLEN]; /* make cookie not in BASE64 or QUOTEPRINT*/
+ sprintf (tmp,"%lu-%lu-%lu=:%lu",(unsigned long) gethostid (),
+ (unsigned long) random (),(unsigned long) time (0),
+ (unsigned long) getpid ());
+ (*param) = mail_newbody_parameter ();
+ (*param)->attribute = cpystr ("BOUNDARY");
+ (*param)->value = cpystr (tmp);
+ }
+ part = body->nested.part; /* encode body parts */
+ do rfc822_encode_body_8bit (env,&part->body);
+ while (part = part->next); /* until done */
+ break;
+ case TYPEMESSAGE: /* encapsulated message */
+ switch (body->encoding) {
+ case ENC7BIT:
+ case ENC8BIT:
+ break;
+ case ENCBINARY:
+ MM_LOG ("Binary included message in 8-bit message body",PARSE);
+ break;
+ default:
+ fatal ("Invalid rfc822_encode_body_7bit message encoding");
+ }
+ break; /* can't change encoding */
+ default: /* other type, encode binary into BASE64 */
+ if (body->encoding == ENCBINARY) {
+ /* remember old binary contents */
+ f = (void *) body->contents.text.data;
+ body->contents.text.data =
+ rfc822_binary ((void *) body->contents.text.data,
+ body->contents.text.size,&body->contents.text.size);
+ body->encoding = ENCBASE64;
+ fs_give (&f); /* flush old binary contents */
+ }
+ break;
+ }
+}
+
+/* Output RFC 822 text
+ * Accepts: buffer
+ * body
+ * Returns: T if successful, NIL if failure
+ */
+
+long rfc822_output_text (RFC822BUFFER *buf,BODY *body)
+{
+ /* MULTIPART gets special handling */
+ if (body->type == TYPEMULTIPART) {
+ char *cookie,tmp[MAILTMPLEN];
+ PARAMETER *param;
+ PART *part;
+ /* find cookie */
+ for (param = body->parameter; param && strcmp (param->attribute,"BOUNDARY");
+ param = param->next);
+ if (param) cookie = param->value;
+ else { /* make cookie not in BASE64 or QUOTEPRINT*/
+ sprintf (cookie = tmp,"%lu-%lu-%lu=:%lu",(unsigned long) gethostid (),
+ (unsigned long) random (),(unsigned long) time (0),
+ (unsigned long) getpid ());
+ (param = mail_newbody_parameter ())->attribute = cpystr ("BOUNDARY");
+ param->value = cpystr (tmp);
+ param->next = body->parameter;
+ body->parameter = param;
+ }
+ /* output each part */
+ for (part = body->nested.part; part; part = part->next)
+ if (!(rfc822_output_string (buf,"--") &&
+ rfc822_output_string (buf,cookie) &&
+ rfc822_output_string (buf,"\015\012") &&
+ rfc822_output_body_header (buf,&part->body) &&
+ rfc822_output_string (buf,"\015\012") &&
+ rfc822_output_text (buf,&part->body))) return NIL;
+ /* output trailing cookie */
+ return rfc822_output_string (buf,"--") &&
+ rfc822_output_string (buf,cookie) &&
+ rfc822_output_string (buf,"--\015\012");
+ }
+ /* output segment and trailing CRLF */
+ return (!body->contents.text.data ||
+ rfc822_output_string (buf,(char *) body->contents.text.data)) &&
+ rfc822_output_string (buf,"\015\012");
+}
+
+/* Body contents encoding/decoding routines */
+
+
+/* Convert BASE64 contents to binary
+ * Accepts: source
+ * length of source
+ * pointer to return destination length
+ * Returns: destination as binary or NIL if error
+ */
+
+#define WSP 0176 /* NUL, TAB, LF, FF, CR, SPC */
+#define JNK 0177
+#define PAD 0100
+
+void *rfc822_base64 (unsigned char *src,unsigned long srcl,unsigned long *len)
+{
+ char c,*s,tmp[MAILTMPLEN];
+ void *ret = fs_get ((size_t) ((*len = 4 + ((srcl * 3) / 4))) + 1);
+ char *d = (char *) ret;
+ int e;
+ static char decode[256] = {
+ WSP,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,WSP,WSP,JNK,WSP,WSP,JNK,JNK,
+ JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,
+ WSP,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,076,JNK,JNK,JNK,077,
+ 064,065,066,067,070,071,072,073,074,075,JNK,JNK,JNK,PAD,JNK,JNK,
+ JNK,000,001,002,003,004,005,006,007,010,011,012,013,014,015,016,
+ 017,020,021,022,023,024,025,026,027,030,031,JNK,JNK,JNK,JNK,JNK,
+ JNK,032,033,034,035,036,037,040,041,042,043,044,045,046,047,050,
+ 051,052,053,054,055,056,057,060,061,062,063,JNK,JNK,JNK,JNK,JNK,
+ JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,
+ JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,
+ JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,
+ JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,
+ JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,
+ JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,
+ JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,
+ JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK,JNK
+ };
+ /* initialize block */
+ memset (ret,0,((size_t) *len) + 1);
+ *len = 0; /* in case we return an error */
+
+ /* simple-minded decode */
+ for (e = 0; srcl--; ) switch (c = decode[*src++]) {
+ default: /* valid BASE64 data character */
+ switch (e++) { /* install based on quantum position */
+ case 0:
+ *d = c << 2; /* byte 1: high 6 bits */
+ break;
+ case 1:
+ *d++ |= c >> 4; /* byte 1: low 2 bits */
+ *d = c << 4; /* byte 2: high 4 bits */
+ break;
+ case 2:
+ *d++ |= c >> 2; /* byte 2: low 4 bits */
+ *d = c << 6; /* byte 3: high 2 bits */
+ break;
+ case 3:
+ *d++ |= c; /* byte 3: low 6 bits */
+ e = 0; /* reinitialize mechanism */
+ break;
+ }
+ break;
+ case WSP: /* whitespace */
+ break;
+ case PAD: /* padding */
+ switch (e++) { /* check quantum position */
+ case 3: /* one = is good enough in quantum 3 */
+ /* make sure no data characters in remainder */
+ for (; srcl; --srcl) switch (decode[*src++]) {
+ /* ignore space, junk and extraneous padding */
+ case WSP: case JNK: case PAD:
+ break;
+ default: /* valid BASE64 data character */
+ /* This indicates bad MIME. One way that it can be caused is if
+ a single-section message was BASE64 encoded and then something
+ (e.g. a mailing list processor) appended text. The problem is
+ that in 1 out of 3 cases, there is no padding and hence no way
+ to detect the end of the data. Consequently, prudent software
+ will always encapsulate a BASE64 segment inside a MULTIPART.
+ */
+ sprintf (tmp,"Possible data truncation in rfc822_base64(): %.80s",
+ (char *) src - 1);
+ if (s = strpbrk (tmp,"\015\012")) *s = NIL;
+ mm_log (tmp,PARSE);
+ srcl = 1; /* don't issue any more messages */
+ break;
+ }
+ break;
+ case 2: /* expect a second = in quantum 2 */
+ if (srcl && (*src == '=')) break;
+ default: /* impossible quantum position */
+ fs_give (&ret);
+ return NIL;
+ }
+ break;
+ case JNK: /* junk character */
+ fs_give (&ret);
+ return NIL;
+ }
+ *len = d - (char *) ret; /* calculate data length */
+ *d = '\0'; /* NUL terminate just in case */
+ return ret; /* return the string */
+}
+
+/* Convert binary contents to BASE64
+ * Accepts: source
+ * length of source
+ * pointer to return destination length
+ * Returns: destination as BASE64
+ */
+
+unsigned char *rfc822_binary (void *src,unsigned long srcl,unsigned long *len)
+{
+ unsigned char *ret,*d;
+ unsigned char *s = (unsigned char *) src;
+ char *v = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ unsigned long i = ((srcl + 2) / 3) * 4;
+ *len = i += 2 * ((i / 60) + 1);
+ d = ret = (unsigned char *) fs_get ((size_t) ++i);
+ /* process tuplets */
+ for (i = 0; srcl >= 3; s += 3, srcl -= 3) {
+ *d++ = v[s[0] >> 2]; /* byte 1: high 6 bits (1) */
+ /* byte 2: low 2 bits (1), high 4 bits (2) */
+ *d++ = v[((s[0] << 4) + (s[1] >> 4)) & 0x3f];
+ /* byte 3: low 4 bits (2), high 2 bits (3) */
+ *d++ = v[((s[1] << 2) + (s[2] >> 6)) & 0x3f];
+ *d++ = v[s[2] & 0x3f]; /* byte 4: low 6 bits (3) */
+ if ((++i) == 15) { /* output 60 characters? */
+ i = 0; /* restart line break count, insert CRLF */
+ *d++ = '\015'; *d++ = '\012';
+ }
+ }
+ if (srcl) {
+ *d++ = v[s[0] >> 2]; /* byte 1: high 6 bits (1) */
+ /* byte 2: low 2 bits (1), high 4 bits (2) */
+ *d++ = v[((s[0] << 4) + (--srcl ? (s[1] >> 4) : 0)) & 0x3f];
+ /* byte 3: low 4 bits (2), high 2 bits (3) */
+ *d++ = srcl ? v[((s[1] << 2) + (--srcl ? (s[2] >> 6) : 0)) & 0x3f] : '=';
+ /* byte 4: low 6 bits (3) */
+ *d++ = srcl ? v[s[2] & 0x3f] : '=';
+ if (srcl) srcl--; /* count third character if processed */
+ if ((++i) == 15) { /* output 60 characters? */
+ i = 0; /* restart line break count, insert CRLF */
+ *d++ = '\015'; *d++ = '\012';
+ }
+ }
+ *d++ = '\015'; *d++ = '\012'; /* insert final CRLF */
+ *d = '\0'; /* tie off string */
+ if (((unsigned long) (d - ret)) != *len) fatal ("rfc822_binary logic flaw");
+ return ret; /* return the resulting string */
+}
+
+/* Convert QUOTED-PRINTABLE contents to 8BIT
+ * Accepts: source
+ * length of source
+ * pointer to return destination length
+ * Returns: destination as 8-bit text or NIL if error
+ */
+
+unsigned char *rfc822_qprint (unsigned char *src,unsigned long srcl,
+ unsigned long *len)
+{
+ char tmp[MAILTMPLEN];
+ unsigned int bogon = NIL;
+ unsigned char *ret = (unsigned char *) fs_get ((size_t) srcl + 1);
+ unsigned char *d = ret;
+ unsigned char *t = d;
+ unsigned char *s = src;
+ unsigned char c,e;
+ *len = 0; /* in case we return an error */
+ /* until run out of characters */
+ while (((unsigned long) (s - src)) < srcl) {
+ switch (c = *s++) { /* what type of character is it? */
+ case '=': /* quoting character */
+ if (((unsigned long) (s - src)) < srcl) switch (c = *s++) {
+ case '\0': /* end of data */
+ s--; /* back up pointer */
+ break;
+ case '\015': /* non-significant line break */
+ if ((((unsigned long) (s - src)) < srcl) && (*s == '\012')) s++;
+ case '\012': /* bare LF */
+ t = d; /* accept any leading spaces */
+ break;
+ default: /* two hex digits then */
+ if (!(isxdigit (c) && (((unsigned long) (s - src)) < srcl) &&
+ (e = *s++) && isxdigit (e))) {
+ /* This indicates bad MIME. One way that it can be caused is if
+ a single-section message was QUOTED-PRINTABLE encoded and then
+ something (e.g. a mailing list processor) appended text. The
+ problem is that there is no way to determine where the encoded
+ data ended and the appended crud began. Consequently, prudent
+ software will always encapsulate a QUOTED-PRINTABLE segment
+ inside a MULTIPART.
+ */
+ if (!bogon++) { /* only do this once */
+ sprintf (tmp,"Invalid quoted-printable sequence: =%.80s",
+ (char *) s - 1);
+ mm_log (tmp,PARSE);
+ }
+ *d++ = '='; /* treat = as ordinary character */
+ *d++ = c; /* and the character following */
+ t = d; /* note point of non-space */
+ break;
+ }
+ *d++ = hex2byte (c,e); /* merge the two hex digits */
+ t = d; /* note point of non-space */
+ break;
+ }
+ break;
+ case ' ': /* space, possibly bogus */
+ *d++ = c; /* stash the space but don't update s */
+ break;
+ case '\015': /* end of line */
+ case '\012': /* bare LF */
+ d = t; /* slide back to last non-space, drop in */
+ default:
+ *d++ = c; /* stash the character */
+ t = d; /* note point of non-space */
+ }
+ }
+ *d = '\0'; /* tie off results */
+ *len = d - ret; /* calculate length */
+ return ret; /* return the string */
+}
+
+/* Convert 8BIT contents to QUOTED-PRINTABLE
+ * Accepts: source
+ * length of source
+ * pointer to return destination length
+ * Returns: destination as quoted-printable text
+ */
+
+#define MAXL (size_t) 75 /* 76th position only used by continuation = */
+
+unsigned char *rfc822_8bit (unsigned char *src,unsigned long srcl,
+ unsigned long *len)
+{
+ unsigned long lp = 0;
+ unsigned char *ret = (unsigned char *)
+ fs_get ((size_t) (3*srcl + 3*(((3*srcl)/MAXL) + 1)));
+ unsigned char *d = ret;
+ char *hex = "0123456789ABCDEF";
+ unsigned char c;
+ while (srcl--) { /* for each character */
+ /* true line break? */
+ if (((c = *src++) == '\015') && (*src == '\012') && srcl) {
+ *d++ = '\015'; *d++ = *src++; srcl--;
+ lp = 0; /* reset line count */
+ }
+ else { /* not a line break */
+ /* quoting required? */
+ if (iscntrl (c) || (c == 0x7f) || (c & 0x80) || (c == '=') ||
+ ((c == ' ') && (*src == '\015'))) {
+ if ((lp += 3) > MAXL) { /* yes, would line overflow? */
+ *d++ = '='; *d++ = '\015'; *d++ = '\012';
+ lp = 3; /* set line count */
+ }
+ *d++ = '='; /* quote character */
+ *d++ = hex[c >> 4]; /* high order 4 bits */
+ *d++ = hex[c & 0xf]; /* low order 4 bits */
+ }
+ else { /* ordinary character */
+ if ((++lp) > MAXL) { /* would line overflow? */
+ *d++ = '='; *d++ = '\015'; *d++ = '\012';
+ lp = 1; /* set line count */
+ }
+ *d++ = c; /* ordinary character */
+ }
+ }
+ }
+ *d = '\0'; /* tie off destination */
+ *len = d - ret; /* calculate true size */
+ /* try to give some space back */
+ fs_resize ((void **) &ret,(size_t) *len + 1);
+ return ret;
+}
+
+/* Legacy Routines */
+
+/*
+ * WARNING: These routines are for compatibility with old software only.
+ *
+ * Their use in new software is to be avoided.
+ *
+ * These interfaces do not provide satisfactory buffer checking. In
+ * versions of c-client prior to imap-2005, they did not provide any
+ * buffer checking at all.
+ *
+ * As a half-hearted attempt, these new compatability functions for the
+ * legacy interfaces limit what they write to size SENDBUFLEN and will
+ * fatal() if more than that is written. However, that isn't good enough
+ * since several of these functions *append* to the buffer, and return an
+ * updated pointer. Consequently, there is no way of knowing what the
+ * actual available space is in the buffer, yet the function will still
+ * write up to SENDBUFLEN bytes even if there is much less space actually
+ * available. The result is a buffer overflow.
+ *
+ * You won't get a buffer overflow if you never attempt to append using
+ * these interfaces, but you can get the fatal() if it tries to write
+ * more than SENDBUFLEN bytes.
+ *
+ * To avoid this problem, use the corresponding rfc822_output_???()
+ * functions instead, e.g., rfc822_output_address() instead of
+ * rfc822_address().
+ *
+ */
+
+/* Flush routine, only called if overflow
+ * Accepts: stream
+ * string to output
+ * Returns: never
+ */
+
+static long rfc822_legacy_soutr (void *stream,char *string)
+{
+ fatal ("rfc822.c legacy routine buffer overflow");
+ return NIL;
+}
+
+/* Legacy write RFC 2822 header from message structure
+ * Accepts: scratch buffer to write into
+ * message envelope
+ * message body
+ */
+
+void rfc822_header (char *header,ENVELOPE *env,BODY *body)
+{
+ RFC822BUFFER buf;
+ /* write at start of buffer */
+ buf.end = (buf.beg = buf.cur = header) + SENDBUFLEN - 1;
+ buf.f = rfc822_legacy_soutr;
+ buf.s = NIL;
+ rfc822_output_header (&buf,env,body,NIL,NIL);
+ *buf.cur = '\0'; /* tie off buffer */
+}
+
+
+/* Legacy write RFC 2822 text from header line
+ * Accepts: pointer to destination string pointer
+ * pointer to header type
+ * message to interpret
+ * pointer to text
+ */
+
+void rfc822_header_line (char **header,char *type,ENVELOPE *env,char *text)
+{
+ RFC822BUFFER buf;
+ /* append to buffer */
+ buf.end = (buf.beg = buf.cur = *header + strlen (*header)) + SENDBUFLEN - 1;
+ buf.f = rfc822_legacy_soutr;
+ buf.s = NIL;
+ rfc822_output_header_line (&buf,type,env->remail ? LONGT : NIL,text);
+ *(*header = buf.cur) = '\0'; /* tie off buffer */
+}
+
+/* Legacy write RFC 2822 address from header line
+ * Accepts: pointer to destination string pointer
+ * pointer to header type
+ * message to interpret
+ * address to interpret
+ */
+
+void rfc822_address_line (char **header,char *type,ENVELOPE *env,ADDRESS *adr)
+{
+ RFC822BUFFER buf;
+ /* append to buffer */
+ buf.end = (buf.beg = buf.cur = *header + strlen (*header)) + SENDBUFLEN - 1;
+ buf.f = rfc822_legacy_soutr;
+ buf.s = NIL;
+ rfc822_output_address_line (&buf,type,env->remail ? LONGT : NIL,adr,NIL);
+ *(*header = buf.cur) = '\0'; /* tie off buffer */
+}
+
+
+/* Legacy write RFC 2822 address list
+ * Accepts: pointer to destination string
+ * address to interpret
+ * header base if pretty-printing
+ * Returns: end of destination string
+ */
+
+char *rfc822_write_address_full (char *dest,ADDRESS *adr,char *base)
+{
+ RFC822BUFFER buf;
+ /* append to buffer */
+ buf.end = (buf.beg = buf.cur = dest + strlen (dest)) + SENDBUFLEN - 1;
+ buf.f = rfc822_legacy_soutr;
+ buf.s = NIL;
+ rfc822_output_address_list (&buf,adr,base ? dest - base : 0,NIL);
+ *buf.cur = '\0'; /* tie off buffer */
+ return buf.cur;
+}
+
+
+/* Legacy write RFC 2822 route-address to string
+ * Accepts: pointer to destination string
+ * address to interpret
+ */
+
+void rfc822_address (char *dest,ADDRESS *adr)
+{
+ RFC822BUFFER buf;
+ /* append to buffer */
+ buf.end = (buf.beg = buf.cur = dest + strlen (dest)) + SENDBUFLEN - 1;
+ buf.f = rfc822_legacy_soutr;
+ buf.s = NIL;
+ rfc822_output_address (&buf,adr);
+ *buf.cur = '\0'; /* tie off buffer */
+}
+
+/* Concatenate RFC 2822 string
+ * Accepts: pointer to destination string
+ * pointer to string to concatenate
+ * list of special characters or NIL for dot-atom format
+ */
+
+void rfc822_cat (char *dest,char *src,const char *specials)
+{
+ RFC822BUFFER buf;
+ /* append to buffer */
+ buf.end = (buf.beg = buf.cur = dest + strlen (dest)) + SENDBUFLEN - 1;
+ buf.f = rfc822_legacy_soutr;
+ buf.s = NIL;
+ rfc822_output_cat (&buf,src,specials);
+ *buf.cur = '\0'; /* tie off buffer */
+}
+
+
+/* Legacy write body content header
+ * Accepts: pointer to destination string pointer
+ * pointer to body to interpret
+ */
+
+void rfc822_write_body_header (char **dst,BODY *body)
+{
+ RFC822BUFFER buf;
+ /* append to buffer */
+ buf.end = (buf.beg = buf.cur = *dst + strlen (*dst)) + SENDBUFLEN - 1;
+ buf.f = rfc822_legacy_soutr;
+ buf.s = NIL;
+ rfc822_output_body_header (&buf,body);
+ *(*dst = buf.cur) = '\0'; /* tie off buffer */
+}
+
+/* Legacy output RFC 822 message
+ * Accepts: temporary buffer
+ * envelope
+ * body
+ * I/O routine
+ * stream for I/O routine
+ * non-zero if 8-bit output desired
+ * Returns: T if successful, NIL if failure
+ */
+
+long rfc822_output (char *t,ENVELOPE *env,BODY *body,soutr_t f,void *s,
+ long ok8bit)
+{
+ long ret;
+ rfc822out_t r822o = (rfc822out_t) mail_parameters (NIL,GET_RFC822OUTPUT,NIL);
+ /* call external RFC 2822 output generator */
+ if (r822o) ret = (*r822o) (t,env,body,f,s,ok8bit);
+ else { /* output generator not armed */
+ RFC822BUFFER buf; /* use our own buffer rather than trust */
+ char tmp[SENDBUFLEN+1]; /* client to give us a big enough one */
+ buf.f = f;
+ buf.s = s;
+ buf.end = (buf.beg = buf.cur = t) + SENDBUFLEN - 1;
+ tmp[SENDBUFLEN] = '\0'; /* must have additional guard byte */
+ ret = rfc822_output_full (&buf,env,body,ok8bit);
+ }
+ return ret;
+}
+
+
+/* Legacy output RFC 822 body
+ * Accepts: body
+ * I/O routine
+ * stream for I/O routine
+ * Returns: T if successful, NIL if failure
+ */
+
+long rfc822_output_body (BODY *body,soutr_t f,void *s)
+{
+ RFC822BUFFER buf;
+ char tmp[SENDBUFLEN+1];
+ buf.f = f;
+ buf.s = s;
+ buf.end = (buf.beg = buf.cur = tmp) + SENDBUFLEN;
+ tmp[SENDBUFLEN] = '\0'; /* must have additional guard byte */
+ return rfc822_output_text (&buf,body) && rfc822_output_flush (&buf);
+}
diff --git a/imap/src/c-client/rfc822.h b/imap/src/c-client/rfc822.h
new file mode 100644
index 00000000..0621732a
--- /dev/null
+++ b/imap/src/c-client/rfc822.h
@@ -0,0 +1,127 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: RFC 2822 and MIME routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 27 July 1988
+ * Last Edited: 30 August 2006
+ *
+ * This original version of this file is
+ * Copyright 1988 Stanford University
+ * and was developed in the Symbolic Systems Resources Group of the Knowledge
+ * Systems Laboratory at Stanford University in 1987-88, and was funded by the
+ * Biomedical Research Technology Program of the NationalInstitutes of Health
+ * under grant number RR-00785.
+ */
+
+#define MAXGROUPDEPTH 50 /* RFC [2]822 doesn't allow any group nesting */
+#define MAXMIMEDEPTH 50 /* more than any sane MIMEgram */
+
+/* Output buffering for RFC [2]822 */
+
+typedef long (*soutr_t) (void *stream,char *string);
+typedef long (*rfc822out_t) (char *tmp,ENVELOPE *env,BODY *body,soutr_t f,
+ void *s,long ok8bit);
+
+typedef struct rfc822buffer {
+ soutr_t f; /* I/O flush routine */
+ void *s; /* stream for I/O routine */
+ char *beg; /* start of buffer */
+ char *cur; /* current buffer pointer */
+ char *end; /* end of buffer */
+} RFC822BUFFER;
+
+typedef long (*rfc822outfull_t) (RFC822BUFFER *buf,ENVELOPE *env,BODY *body,
+ long ok8bit);
+
+/* Function prototypes */
+
+char *rfc822_default_subtype (unsigned short type);
+void rfc822_parse_msg_full (ENVELOPE **en,BODY **bdy,char *s,unsigned long i,
+ STRING *bs,char *host,unsigned long depth,
+ unsigned long flags);
+void rfc822_parse_content (BODY *body,STRING *bs,char *h,unsigned long depth,
+ unsigned long flags);
+void rfc822_parse_content_header (BODY *body,char *name,char *s);
+void rfc822_parse_parameter (PARAMETER **par,char *text);
+void rfc822_parse_adrlist (ADDRESS **lst,char *string,char *host);
+ADDRESS *rfc822_parse_address (ADDRESS **lst,ADDRESS *last,char **string,
+ char *defaulthost,unsigned long depth);
+ADDRESS *rfc822_parse_group (ADDRESS **lst,ADDRESS *last,char **string,
+ char *defaulthost,unsigned long depth);
+ADDRESS *rfc822_parse_mailbox (char **string,char *defaulthost);
+long rfc822_phraseonly (char *end);
+ADDRESS *rfc822_parse_routeaddr (char *string,char **ret,char *defaulthost);
+ADDRESS *rfc822_parse_addrspec (char *string,char **ret,char *defaulthost);
+char *rfc822_parse_domain (char *string,char **end);
+char *rfc822_parse_phrase (char *string);
+char *rfc822_parse_word (char *string,const char *delimiters);
+char *rfc822_cpy (char *src);
+char *rfc822_quote (char *src);
+ADDRESS *rfc822_cpy_adr (ADDRESS *adr);
+void rfc822_skipws (char **s);
+char *rfc822_skip_comment (char **s,long trim);
+
+long rfc822_output_full (RFC822BUFFER *buf,ENVELOPE *env,BODY *body,long ok8);
+long rfc822_output_flush (RFC822BUFFER *buf);
+long rfc822_output_header (RFC822BUFFER *buf,ENVELOPE *env,BODY *body,
+ const char *specials,long flags);
+long rfc822_output_header_line (RFC822BUFFER *buf,char *type,long resent,
+ char *text);
+long rfc822_output_address_line (RFC822BUFFER *buf,char *type,long resent,
+ ADDRESS *adr,const char *specials);
+long rfc822_output_address_list (RFC822BUFFER *buf,ADDRESS *adr,long pretty,
+ const char *specials);
+long rfc822_output_address (RFC822BUFFER *buf,ADDRESS *adr);
+long rfc822_output_cat (RFC822BUFFER *buf,char *src,const char *specials);
+long rfc822_output_parameter (RFC822BUFFER *buf,PARAMETER *param);
+long rfc822_output_stringlist (RFC822BUFFER *buf,STRINGLIST *stl);
+long rfc822_output_body_header (RFC822BUFFER *buf,BODY *body);
+void rfc822_encode_body_7bit (ENVELOPE *env,BODY *body);
+void rfc822_encode_body_8bit (ENVELOPE *env,BODY *body);
+long rfc822_output_text (RFC822BUFFER *buf,BODY *body);
+void *rfc822_base64 (unsigned char *src,unsigned long srcl,unsigned long *len);
+unsigned char *rfc822_binary (void *src,unsigned long srcl,unsigned long *len);
+unsigned char *rfc822_qprint (unsigned char *src,unsigned long srcl,
+ unsigned long *len);
+unsigned char *rfc822_8bit (unsigned char *src,unsigned long srcl,
+ unsigned long *len);
+
+/* Legacy routines for compatibility with the past */
+
+void rfc822_header (char *header,ENVELOPE *env,BODY *body);
+void rfc822_header_line (char **header,char *type,ENVELOPE *env,char *text);
+void rfc822_address_line (char **header,char *type,ENVELOPE *env,ADDRESS *adr);
+char *rfc822_write_address_full (char *dest,ADDRESS *adr,char *base);
+void rfc822_address (char *dest,ADDRESS *adr);
+void rfc822_cat (char *dest,char *src,const char *specials);
+void rfc822_write_body_header (char **header,BODY *body);
+long rfc822_output (char *t,ENVELOPE *env,BODY *body,soutr_t f,void *s,
+ long ok8bit);
+long rfc822_output_body (BODY *body,soutr_t f,void *s);
+
+
+#define rfc822_write_address(dest,adr) \
+ rfc822_write_address_full (dest,adr,NIL)
+
+#define rfc822_parse_msg(en,bdy,s,i,bs,host,flags) \
+ rfc822_parse_msg_full (en,bdy,s,i,bs,host,0,flags)
diff --git a/imap/src/c-client/smanager.c b/imap/src/c-client/smanager.c
new file mode 100644
index 00000000..cee659ff
--- /dev/null
+++ b/imap/src/c-client/smanager.c
@@ -0,0 +1,129 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Subscription Manager
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 December 1992
+ * Last Edited: 6 December 2006
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include "c-client.h"
+
+/* Subscribe to mailbox
+ * Accepts: mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long sm_subscribe (char *mailbox)
+{
+ FILE *f;
+ char *s,db[MAILTMPLEN],tmp[MAILTMPLEN];
+ /* canonicalize INBOX */
+ if (!compare_cstring (mailbox,"INBOX")) mailbox = "INBOX";
+ SUBSCRIPTIONFILE (db); /* open subscription database */
+ if (f = fopen (db,"r")) { /* make sure not already there */
+ while (fgets (tmp,MAILTMPLEN,f)) {
+ if (s = strchr (tmp,'\n')) *s = '\0';
+ if (!strcmp (tmp,mailbox)) {/* already subscribed? */
+ sprintf (tmp,"Already subscribed to mailbox %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ fclose (f);
+ return NIL;
+ }
+ }
+ fclose (f);
+ }
+ if (!(f = fopen (db,"a"))) { /* append new entry */
+ MM_LOG ("Can't append to subscription database",ERROR);
+ return NIL;
+ }
+ fprintf (f,"%s\n",mailbox);
+ return (fclose (f) == EOF) ? NIL : T;
+}
+
+/* Unsubscribe from mailbox
+ * Accepts: mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long sm_unsubscribe (char *mailbox)
+{
+ FILE *f,*tf;
+ char *s,tmp[MAILTMPLEN],old[MAILTMPLEN],newname[MAILTMPLEN];
+ int found = NIL;
+ /* canonicalize INBOX */
+ if (!compare_cstring (mailbox,"INBOX")) mailbox = "INBOX";
+ SUBSCRIPTIONFILE (old); /* make file names */
+ SUBSCRIPTIONTEMP (newname);
+ if (!(f = fopen (old,"r"))) /* open subscription database */
+ MM_LOG ("No subscriptions",ERROR);
+ else if (!(tf = fopen (newname,"w"))) {
+ MM_LOG ("Can't create subscription temporary file",ERROR);
+ fclose (f);
+ }
+ else {
+ while (fgets (tmp,MAILTMPLEN,f)) {
+ if (s = strchr (tmp,'\n')) *s = '\0';
+ if (strcmp (tmp,mailbox)) fprintf (tf,"%s\n",tmp);
+ else found = T; /* found the name */
+ }
+ fclose (f);
+ if (fclose (tf) == EOF)
+ MM_LOG ("Can't write subscription temporary file",ERROR);
+ else if (!found) {
+ sprintf (tmp,"Not subscribed to mailbox %.80s",mailbox);
+ MM_LOG (tmp,ERROR); /* error if at end */
+ }
+ else if (!unlink (old) && !rename (newname,old)) return LONGT;
+ else MM_LOG ("Can't update subscription database",ERROR);
+ }
+ return NIL;
+}
+
+/* Read subscription database
+ * Accepts: pointer to subscription database handle (handle NIL if first time)
+ * Returns: character string for subscription database or NIL if done
+ */
+
+static char sbname[MAILTMPLEN];
+
+char *sm_read (void **sdb)
+{
+ FILE *f = (FILE *) *sdb;
+ char *s;
+ if (!f) { /* first time through? */
+ SUBSCRIPTIONFILE (sbname); /* open subscription database */
+ /* make sure not already there */
+ if (f = fopen (sbname,"r")) *sdb = (void *) f;
+ else return NIL;
+ }
+ if (fgets (sbname,MAILTMPLEN,f)) {
+ if (s = strchr (sbname,'\n')) *s = '\0';
+ return sbname;
+ }
+ fclose (f); /* all done */
+ *sdb = NIL; /* zap sdb */
+ return NIL;
+}
diff --git a/imap/src/c-client/smtp.c b/imap/src/c-client/smtp.c
new file mode 100644
index 00000000..0a3cf489
--- /dev/null
+++ b/imap/src/c-client/smtp.c
@@ -0,0 +1,793 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Simple Mail Transfer Protocol (SMTP) routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 27 July 1988
+ * Last Edited: 28 January 2008
+ *
+ * This original version of this file is
+ * Copyright 1988 Stanford University
+ * and was developed in the Symbolic Systems Resources Group of the Knowledge
+ * Systems Laboratory at Stanford University in 1987-88, and was funded by the
+ * Biomedical Research Technology Program of the National Institutes of Health
+ * under grant number RR-00785.
+ */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include "c-client.h"
+
+/* Constants */
+
+#define SMTPSSLPORT (long) 465 /* former assigned SSL TCP contact port */
+#define SMTPGREET (long) 220 /* SMTP successful greeting */
+#define SMTPAUTHED (long) 235 /* SMTP successful authentication */
+#define SMTPOK (long) 250 /* SMTP OK code */
+#define SMTPAUTHREADY (long) 334/* SMTP ready for authentication */
+#define SMTPREADY (long) 354 /* SMTP ready for data */
+#define SMTPSOFTFATAL (long) 421/* SMTP soft fatal code */
+#define SMTPWANTAUTH (long) 505 /* SMTP authentication needed */
+#define SMTPWANTAUTH2 (long) 530/* SMTP authentication needed */
+#define SMTPUNAVAIL (long) 550 /* SMTP mailbox unavailable */
+#define SMTPHARDERROR (long) 554/* SMTP miscellaneous hard failure */
+
+
+/* Convenient access to protocol-specific data */
+
+#define ESMTP stream->protocol.esmtp
+
+
+/* Function prototypes */
+
+void *smtp_challenge (void *s,unsigned long *len);
+long smtp_response (void *s,char *response,unsigned long size);
+long smtp_auth (SENDSTREAM *stream,NETMBX *mb,char *tmp);
+long smtp_rcpt (SENDSTREAM *stream,ADDRESS *adr,long *error);
+long smtp_send (SENDSTREAM *stream,char *command,char *args);
+long smtp_reply (SENDSTREAM *stream);
+long smtp_ehlo (SENDSTREAM *stream,char *host,NETMBX *mb);
+long smtp_fake (SENDSTREAM *stream,char *text);
+static long smtp_seterror (SENDSTREAM *stream,long code,char *text);
+long smtp_soutr (void *stream,char *s);
+
+/* Mailer parameters */
+
+static unsigned long smtp_maxlogintrials = MAXLOGINTRIALS;
+static long smtp_port = 0; /* default port override */
+static long smtp_sslport = 0;
+
+
+#ifndef RFC2821
+#define RFC2821 /* RFC 2821 compliance */
+#endif
+
+/* SMTP limits, current as of RFC 2821 */
+
+#define SMTPMAXLOCALPART 64
+#define SMTPMAXDOMAIN 255
+#define SMTPMAXPATH 256
+
+
+/* I have seen local parts of more than 64 octets, in spite of the SMTP
+ * limits. So, we'll have a more generous limit that's still guaranteed
+ * not to pop the buffer, and let the server worry about it. As of this
+ * writing, it comes out to 240. Anyone with a mailbox name larger than
+ * that is in serious need of a life or at least a new ISP! 23 June 1998
+ */
+
+#define MAXLOCALPART ((MAILTMPLEN - (SMTPMAXDOMAIN + SMTPMAXPATH + 32)) / 2)
+
+/* Mail Transfer Protocol manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *smtp_parameters (long function,void *value)
+{
+ switch ((int) function) {
+ case SET_MAXLOGINTRIALS:
+ smtp_maxlogintrials = (unsigned long) value;
+ break;
+ case GET_MAXLOGINTRIALS:
+ value = (void *) smtp_maxlogintrials;
+ break;
+ case SET_SMTPPORT:
+ smtp_port = (long) value;
+ break;
+ case GET_SMTPPORT:
+ value = (void *) smtp_port;
+ break;
+ case SET_SSLSMTPPORT:
+ smtp_sslport = (long) value;
+ break;
+ case GET_SSLSMTPPORT:
+ value = (void *) smtp_sslport;
+ break;
+ default:
+ value = NIL; /* error case */
+ break;
+ }
+ return value;
+}
+
+/* Mail Transfer Protocol open connection
+ * Accepts: network driver
+ * service host list
+ * port number
+ * service name
+ * SMTP open options
+ * Returns: SEND stream on success, NIL on failure
+ */
+
+SENDSTREAM *smtp_open_full (NETDRIVER *dv,char **hostlist,char *service,
+ unsigned long port,long options)
+{
+ SENDSTREAM *stream = NIL;
+ long reply;
+ char *s,tmp[MAILTMPLEN];
+ NETSTREAM *netstream;
+ NETMBX mb;
+ if (!(hostlist && *hostlist)) mm_log ("Missing SMTP service host",ERROR);
+ /* maximum domain name is 64 characters */
+ else do if (strlen (*hostlist) < SMTPMAXDOMAIN) {
+ sprintf (tmp,"{%.1000s}",*hostlist);
+ if (!mail_valid_net_parse_work (tmp,&mb,service ? service : "smtp") ||
+ mb.anoflag || mb.readonlyflag) {
+ sprintf (tmp,"Invalid host specifier: %.80s",*hostlist);
+ mm_log (tmp,ERROR);
+ }
+ else { /* light tryssl flag if requested */
+ mb.trysslflag = (options & SOP_TRYSSL) ? T : NIL;
+ /* explicit port overrides all */
+ if (mb.port) port = mb.port;
+ /* else /submit overrides port argument */
+ else if (!compare_cstring (mb.service,"submit")) {
+ port = SUBMITTCPPORT; /* override port, use IANA name */
+ strcpy (mb.service,"submission");
+ }
+ /* else port argument overrides SMTP port */
+ else if (!port) port = smtp_port ? smtp_port : SMTPTCPPORT;
+ if (netstream = /* try to open ordinary connection */
+ net_open (&mb,dv,port,
+ (NETDRIVER *) mail_parameters (NIL,GET_SSLDRIVER,NIL),
+ "*smtps",smtp_sslport ? smtp_sslport : SMTPSSLPORT)) {
+ stream = (SENDSTREAM *) memset (fs_get (sizeof (SENDSTREAM)),0,
+ sizeof (SENDSTREAM));
+ stream->netstream = netstream;
+ stream->host = cpystr ((long) mail_parameters (NIL,GET_TRUSTDNS,NIL) ?
+ net_host (netstream) : mb.host);
+ stream->debug = (mb.dbgflag || (options & OP_DEBUG)) ? T : NIL;
+ if (options & SOP_SECURE) mb.secflag = T;
+ /* get name of local host to use */
+ s = compare_cstring ("localhost",mb.host) ?
+ net_localhost (netstream) : "localhost";
+
+ do reply = smtp_reply (stream);
+ while ((reply < 100) || (stream->reply[3] == '-'));
+ if (reply != SMTPGREET){/* get SMTP greeting */
+ sprintf (tmp,"SMTP greeting failure: %.80s",stream->reply);
+ mm_log (tmp,ERROR);
+ stream = smtp_close (stream);
+ }
+ /* try EHLO first, then HELO */
+ else if (((reply = smtp_ehlo (stream,s,&mb)) != SMTPOK) &&
+ ((reply = smtp_send (stream,"HELO",s)) != SMTPOK)) {
+ sprintf (tmp,"SMTP hello failure: %.80s",stream->reply);
+ mm_log (tmp,ERROR);
+ stream = smtp_close (stream);
+ }
+ else {
+ NETDRIVER *ssld =(NETDRIVER *)mail_parameters(NIL,GET_SSLDRIVER,NIL);
+ sslstart_t stls = (sslstart_t) mail_parameters(NIL,GET_SSLSTART,NIL);
+ ESMTP.ok = T; /* ESMTP server, start TLS if present */
+ if (!dv && stls && ESMTP.service.starttls &&
+ !mb.sslflag && !mb.notlsflag &&
+ (smtp_send (stream,"STARTTLS",NIL) == SMTPGREET)) {
+ mb.tlsflag = T; /* TLS OK, get into TLS at this end */
+ stream->netstream->dtb = ssld;
+ /* TLS started, negotiate it */
+ if (!(stream->netstream->stream = (*stls)
+ (stream->netstream->stream,mb.host,
+ (mb.tlssslv23 ? NIL : NET_TLSCLIENT) |
+ (mb.novalidate ? NET_NOVALIDATECERT:NIL)))){
+ /* TLS negotiation failed after STARTTLS */
+ sprintf (tmp,"Unable to negotiate TLS with this server: %.80s",
+ mb.host);
+ mm_log (tmp,ERROR);
+ /* close without doing QUIT */
+ if (stream->netstream) net_close (stream->netstream);
+ stream->netstream = NIL;
+ stream = smtp_close (stream);
+ }
+ /* TLS OK, re-negotiate EHLO */
+ else if ((reply = smtp_ehlo (stream,s,&mb)) != SMTPOK) {
+ sprintf (tmp,"SMTP EHLO failure after STARTTLS: %.80s",
+ stream->reply);
+ mm_log (tmp,ERROR);
+ stream = smtp_close (stream);
+ }
+ else ESMTP.ok = T; /* TLS OK and EHLO successful */
+ }
+ else if (mb.tlsflag) {/* user specified /tls but can't do it */
+ sprintf (tmp,"TLS unavailable with this server: %.80s",mb.host);
+ mm_log (tmp,ERROR);
+ stream = smtp_close (stream);
+ }
+
+ /* remote name for authentication */
+ if (stream && ((mb.secflag || mb.user[0]))) {
+ if (ESMTP.auth) { /* use authenticator? */
+ if ((long) mail_parameters (NIL,GET_TRUSTDNS,NIL)) {
+ /* remote name for authentication */
+ strncpy (mb.host,
+ (long) mail_parameters (NIL,GET_SASLUSESPTRNAME,NIL) ?
+ net_remotehost (netstream) : net_host (netstream),
+ NETMAXHOST-1);
+ mb.host[NETMAXHOST-1] = '\0';
+ }
+ if (!smtp_auth (stream,&mb,tmp)) stream = smtp_close (stream);
+ }
+ else { /* no available authenticators? */
+ sprintf (tmp,"%sSMTP authentication not available: %.80s",
+ mb.secflag ? "Secure " : "",mb.host);
+ mm_log (tmp,ERROR);
+ stream = smtp_close (stream);
+ }
+ }
+ }
+ }
+ }
+ } while (!stream && *++hostlist);
+ if (stream) { /* set stream options if have a stream */
+ if (options &(SOP_DSN | SOP_DSN_NOTIFY_FAILURE | SOP_DSN_NOTIFY_DELAY |
+ SOP_DSN_NOTIFY_SUCCESS | SOP_DSN_RETURN_FULL)) {
+ ESMTP.dsn.want = T;
+ if (options & SOP_DSN_NOTIFY_FAILURE) ESMTP.dsn.notify.failure = T;
+ if (options & SOP_DSN_NOTIFY_DELAY) ESMTP.dsn.notify.delay = T;
+ if (options & SOP_DSN_NOTIFY_SUCCESS) ESMTP.dsn.notify.success = T;
+ if (options & SOP_DSN_RETURN_FULL) ESMTP.dsn.full = T;
+ }
+ if (options & SOP_8BITMIME) ESMTP.eightbit.want = T;
+ }
+ return stream;
+}
+
+/* SMTP authenticate
+ * Accepts: stream to login
+ * parsed network mailbox structure
+ * scratch buffer
+ * place to return user name
+ * Returns: T on success, NIL on failure
+ */
+
+long smtp_auth (SENDSTREAM *stream,NETMBX *mb,char *tmp)
+{
+ unsigned long trial,auths;
+ char *lsterr = NIL;
+ char usr[MAILTMPLEN];
+ AUTHENTICATOR *at;
+ long ret = NIL;
+ for (auths = ESMTP.auth, stream->saslcancel = NIL;
+ !ret && stream->netstream && auths &&
+ (at = mail_lookup_auth (find_rightmost_bit (&auths) + 1)); ) {
+ if (lsterr) { /* previous authenticator failed? */
+ sprintf (tmp,"Retrying using %s authentication after %.80s",
+ at->name,lsterr);
+ mm_log (tmp,NIL);
+ fs_give ((void **) &lsterr);
+ }
+ trial = 0; /* initial trial count */
+ tmp[0] = '\0'; /* empty buffer */
+ if (stream->netstream) do {
+ if (lsterr) {
+ sprintf (tmp,"Retrying %s authentication after %.80s",at->name,lsterr);
+ mm_log (tmp,WARN);
+ fs_give ((void **) &lsterr);
+ }
+ stream->saslcancel = NIL;
+ if (smtp_send (stream,"AUTH",at->name) == SMTPAUTHREADY) {
+ /* hide client authentication responses */
+ if (!(at->flags & AU_SECURE)) stream->sensitive = T;
+ if ((*at->client) (smtp_challenge,smtp_response,"smtp",mb,stream,
+ &trial,usr)) {
+ if (stream->replycode == SMTPAUTHED) {
+ ESMTP.auth = NIL; /* disable authenticators */
+ ret = LONGT;
+ }
+ /* if main program requested cancellation */
+ else if (!trial) mm_log ("SMTP Authentication cancelled",ERROR);
+ }
+ stream->sensitive = NIL;/* unhide */
+ }
+ /* remember response if error and no cancel */
+ if (!ret && trial) lsterr = cpystr (stream->reply);
+ } while (!ret && stream->netstream && trial &&
+ (trial < smtp_maxlogintrials));
+ }
+ if (lsterr) { /* previous authenticator failed? */
+ if (!stream->saslcancel) { /* don't do this if a cancel */
+ sprintf (tmp,"Can not authenticate to SMTP server: %.80s",lsterr);
+ mm_log (tmp,ERROR);
+ }
+ fs_give ((void **) &lsterr);
+ }
+ return ret; /* authentication failed */
+}
+
+/* Get challenge to authenticator in binary
+ * Accepts: stream
+ * pointer to returned size
+ * Returns: challenge or NIL if not challenge
+ */
+
+void *smtp_challenge (void *s,unsigned long *len)
+{
+ char tmp[MAILTMPLEN];
+ void *ret = NIL;
+ SENDSTREAM *stream = (SENDSTREAM *) s;
+ if ((stream->replycode == SMTPAUTHREADY) &&
+ !(ret = rfc822_base64 ((unsigned char *) stream->reply + 4,
+ strlen (stream->reply + 4),len))) {
+ sprintf (tmp,"SMTP SERVER BUG (invalid challenge): %.80s",stream->reply+4);
+ mm_log (tmp,ERROR);
+ }
+ return ret;
+}
+
+
+/* Send authenticator response in BASE64
+ * Accepts: MAIL stream
+ * string to send
+ * length of string
+ * Returns: T, always
+ */
+
+long smtp_response (void *s,char *response,unsigned long size)
+{
+ SENDSTREAM *stream = (SENDSTREAM *) s;
+ unsigned long i,j;
+ char *t,*u;
+ if (response) { /* make CRLFless BASE64 string */
+ if (size) {
+ for (t = (char *) rfc822_binary ((void *) response,size,&i),u = t,j = 0;
+ j < i; j++) if (t[j] > ' ') *u++ = t[j];
+ *u = '\0'; /* tie off string */
+ i = smtp_send (stream,t,NIL);
+ fs_give ((void **) &t);
+ }
+ else i = smtp_send (stream,"",NIL);
+ }
+ else { /* abort requested */
+ i = smtp_send (stream,"*",NIL);
+ stream->saslcancel = T; /* mark protocol-requested SASL cancel */
+ }
+ return LONGT;
+}
+
+/* Mail Transfer Protocol close connection
+ * Accepts: SEND stream
+ * Returns: NIL always
+ */
+
+SENDSTREAM *smtp_close (SENDSTREAM *stream)
+{
+ if (stream) { /* send "QUIT" */
+ if (stream->netstream) { /* do close actions if have netstream */
+ smtp_send (stream,"QUIT",NIL);
+ if (stream->netstream) /* could have been closed during "QUIT" */
+ net_close (stream->netstream);
+ }
+ /* clean up */
+ if (stream->host) fs_give ((void **) &stream->host);
+ if (stream->reply) fs_give ((void **) &stream->reply);
+ if (ESMTP.dsn.envid) fs_give ((void **) &ESMTP.dsn.envid);
+ if (ESMTP.atrn.domains) fs_give ((void **) &ESMTP.atrn.domains);
+ fs_give ((void **) &stream);/* flush the stream */
+ }
+ return NIL;
+}
+
+/* Mail Transfer Protocol deliver mail
+ * Accepts: SEND stream
+ * delivery option (MAIL, SEND, SAML, SOML)
+ * message envelope
+ * message body
+ * Returns: T on success, NIL on failure
+ */
+
+long smtp_mail (SENDSTREAM *stream,char *type,ENVELOPE *env,BODY *body)
+{
+ RFC822BUFFER buf;
+ char tmp[SENDBUFLEN+1];
+ long error = NIL;
+ long retry = NIL;
+ buf.f = smtp_soutr; /* initialize buffer */
+ buf.s = stream->netstream;
+ buf.end = (buf.beg = buf.cur = tmp) + SENDBUFLEN;
+ tmp[SENDBUFLEN] = '\0'; /* must have additional null guard byte */
+ if (!(env->to || env->cc || env->bcc)) {
+ /* no recipients in request */
+ smtp_seterror (stream,SMTPHARDERROR,"No recipients specified");
+ return NIL;
+ }
+ do { /* make sure stream is in good shape */
+ smtp_send (stream,"RSET",NIL);
+ if (retry) { /* need to retry with authentication? */
+ NETMBX mb;
+ /* yes, build remote name for authentication */
+ sprintf (tmp,"{%.200s/smtp%s}<none>",
+ (long) mail_parameters (NIL,GET_TRUSTDNS,NIL) ?
+ ((long) mail_parameters (NIL,GET_SASLUSESPTRNAME,NIL) ?
+ net_remotehost (stream->netstream) :
+ net_host (stream->netstream)) :
+ stream->host,
+ (stream->netstream->dtb ==
+ (NETDRIVER *) mail_parameters (NIL,GET_SSLDRIVER,NIL)) ?
+ "/ssl" : "");
+ mail_valid_net_parse (tmp,&mb);
+ if (!smtp_auth (stream,&mb,tmp)) return NIL;
+ retry = NIL; /* no retry at this point */
+ }
+
+ strcpy (tmp,"FROM:<"); /* compose "MAIL FROM:<return-path>" */
+#ifdef RFC2821
+ if (env->return_path && env->return_path->host &&
+ !((strlen (env->return_path->mailbox) > SMTPMAXLOCALPART) ||
+ (strlen (env->return_path->host) > SMTPMAXDOMAIN))) {
+ rfc822_cat (tmp,env->return_path->mailbox,NIL);
+ sprintf (tmp + strlen (tmp),"@%s",env->return_path->host);
+ }
+#else /* old code with A-D-L support */
+ if (env->return_path && env->return_path->host &&
+ !((env->return_path->adl &&
+ (strlen (env->return_path->adl) > SMTPMAXPATH)) ||
+ (strlen (env->return_path->mailbox) > SMTPMAXLOCALPART) ||
+ (strlen (env->return_path->host) > SMTPMAXDOMAIN)))
+ rfc822_address (tmp,env->return_path);
+#endif
+ strcat (tmp,">");
+ if (ESMTP.ok) {
+ if (ESMTP.eightbit.ok && ESMTP.eightbit.want)
+ strcat (tmp," BODY=8BITMIME");
+ if (ESMTP.dsn.ok && ESMTP.dsn.want) {
+ strcat (tmp,ESMTP.dsn.full ? " RET=FULL" : " RET=HDRS");
+ if (ESMTP.dsn.envid)
+ sprintf (tmp + strlen (tmp)," ENVID=%.100s",ESMTP.dsn.envid);
+ }
+ }
+ /* send "MAIL FROM" command */
+ switch (smtp_send (stream,type,tmp)) {
+ case SMTPUNAVAIL: /* mailbox unavailable? */
+ case SMTPWANTAUTH: /* wants authentication? */
+ case SMTPWANTAUTH2:
+ if (ESMTP.auth) retry = T;/* yes, retry with authentication */
+ case SMTPOK: /* looks good */
+ break;
+ default: /* other failure */
+ return NIL;
+ }
+ /* negotiate the recipients */
+ if (!retry && env->to) retry = smtp_rcpt (stream,env->to,&error);
+ if (!retry && env->cc) retry = smtp_rcpt (stream,env->cc,&error);
+ if (!retry && env->bcc) retry = smtp_rcpt (stream,env->bcc,&error);
+ if (!retry && error) { /* any recipients failed? */
+ smtp_send (stream,"RSET",NIL);
+ smtp_seterror (stream,SMTPHARDERROR,"One or more recipients failed");
+ return NIL;
+ }
+ } while (retry);
+ /* negotiate data command */
+ if (!(smtp_send (stream,"DATA",NIL) == SMTPREADY)) return NIL;
+ /* send message data */
+ if (!rfc822_output_full (&buf,env,body,
+ ESMTP.eightbit.ok && ESMTP.eightbit.want)) {
+ smtp_fake (stream,"SMTP connection broken (message data)");
+ return NIL; /* can't do much else here */
+ }
+ /* send trailing dot */
+ return (smtp_send (stream,".",NIL) == SMTPOK) ? LONGT : NIL;
+}
+
+/* Simple Mail Transfer Protocol send VERBose
+ * Accepts: SMTP stream
+ * Returns: T if successful, else NIL
+ *
+ * Descriptive text formerly in [al]pine sources:
+ * At worst, this command may cause the SMTP connection to get nuked. Modern
+ * sendmail's recognize it, and possibly other SMTP implementations (the "ON"
+ * arg is for PMDF). What's more, if it works, the reply code and accompanying
+ * text may vary from server to server.
+ */
+
+long smtp_verbose (SENDSTREAM *stream)
+{
+ /* accept any 2xx reply code */
+ return ((smtp_send (stream,"VERB","ON") / (long) 100) == 2) ? LONGT : NIL;
+}
+
+/* Internal routines */
+
+
+/* Simple Mail Transfer Protocol send recipient
+ * Accepts: SMTP stream
+ * address list
+ * pointer to error flag
+ * Returns: T if should retry, else NIL
+ */
+
+long smtp_rcpt (SENDSTREAM *stream,ADDRESS *adr,long *error)
+{
+ char *s,tmp[2*MAILTMPLEN],orcpt[MAILTMPLEN];
+ while (adr) { /* for each address on the list */
+ /* clear any former error */
+ if (adr->error) fs_give ((void **) &adr->error);
+ if (adr->host) { /* ignore group syntax */
+ /* enforce SMTP limits to protect the buffer */
+ if (strlen (adr->mailbox) > MAXLOCALPART) {
+ adr->error = cpystr ("501 Recipient name too long");
+ *error = T;
+ }
+ else if ((strlen (adr->host) > SMTPMAXDOMAIN)) {
+ adr->error = cpystr ("501 Recipient domain too long");
+ *error = T;
+ }
+#ifndef RFC2821 /* old code with A-D-L support */
+ else if (adr->adl && (strlen (adr->adl) > SMTPMAXPATH)) {
+ adr->error = cpystr ("501 Path too long");
+ *error = T;
+ }
+#endif
+
+ else {
+ strcpy (tmp,"TO:<"); /* compose "RCPT TO:<return-path>" */
+#ifdef RFC2821
+ rfc822_cat (tmp,adr->mailbox,NIL);
+ sprintf (tmp + strlen (tmp),"@%s>",adr->host);
+#else /* old code with A-D-L support */
+ rfc822_address (tmp,adr);
+ strcat (tmp,">");
+#endif
+ /* want notifications */
+ if (ESMTP.ok && ESMTP.dsn.ok && ESMTP.dsn.want) {
+ /* yes, start with prefix */
+ strcat (tmp," NOTIFY=");
+ s = tmp + strlen (tmp);
+ if (ESMTP.dsn.notify.failure) strcat (s,"FAILURE,");
+ if (ESMTP.dsn.notify.delay) strcat (s,"DELAY,");
+ if (ESMTP.dsn.notify.success) strcat (s,"SUCCESS,");
+ /* tie off last comma */
+ if (*s) s[strlen (s) - 1] = '\0';
+ else strcat (tmp,"NEVER");
+ if (adr->orcpt.addr) {
+ sprintf (orcpt,"%.498s;%.498s",
+ adr->orcpt.type ? adr->orcpt.type : "rfc822",
+ adr->orcpt.addr);
+ sprintf (tmp + strlen (tmp)," ORCPT=%.500s",orcpt);
+ }
+ }
+ switch (smtp_send (stream,"RCPT",tmp)) {
+ case SMTPOK: /* looks good */
+ break;
+ case SMTPUNAVAIL: /* mailbox unavailable? */
+ case SMTPWANTAUTH: /* wants authentication? */
+ case SMTPWANTAUTH2:
+ if (ESMTP.auth) return T;
+ default: /* other failure */
+ *error = T; /* note that an error occurred */
+ adr->error = cpystr (stream->reply);
+ }
+ }
+ }
+ adr = adr->next; /* do any subsequent recipients */
+ }
+ return NIL; /* no retry called for */
+}
+
+/* Simple Mail Transfer Protocol send command
+ * Accepts: SEND stream
+ * text
+ * Returns: reply code
+ */
+
+long smtp_send (SENDSTREAM *stream,char *command,char *args)
+{
+ long ret;
+ char *s = (char *) fs_get (strlen (command) + (args ? strlen (args) + 1 : 0)
+ + 3);
+ /* build the complete command */
+ if (args) sprintf (s,"%s %s",command,args);
+ else strcpy (s,command);
+ if (stream->debug) mail_dlog (s,stream->sensitive);
+ strcat (s,"\015\012");
+ /* send the command */
+ if (stream->netstream && net_soutr (stream->netstream,s)) {
+ do stream->replycode = smtp_reply (stream);
+ while ((stream->replycode < 100) || (stream->reply[3] == '-'));
+ ret = stream->replycode;
+ }
+ else ret = smtp_fake (stream,"SMTP connection broken (command)");
+ fs_give ((void **) &s);
+ return ret;
+}
+
+
+/* Simple Mail Transfer Protocol get reply
+ * Accepts: SMTP stream
+ * Returns: reply code
+ */
+
+long smtp_reply (SENDSTREAM *stream)
+{
+ smtpverbose_t pv = (smtpverbose_t) mail_parameters (NIL,GET_SMTPVERBOSE,NIL);
+ long reply;
+ /* flush old reply */
+ if (stream->reply) fs_give ((void **) &stream->reply);
+ /* get reply */
+ if (stream->netstream && (stream->reply = net_getline (stream->netstream))) {
+ if (stream->debug) mm_dlog (stream->reply);
+ /* return response code */
+ reply = atol (stream->reply);
+ if (pv && (reply < 100)) (*pv) (stream->reply);
+ }
+ else reply = smtp_fake (stream,"SMTP connection broken (reply)");
+ return reply;
+}
+
+/* Simple Mail Transfer Protocol send EHLO
+ * Accepts: SMTP stream
+ * host name to use in EHLO
+ * NETMBX structure
+ * Returns: reply code
+ */
+
+long smtp_ehlo (SENDSTREAM *stream,char *host,NETMBX *mb)
+{
+ unsigned long i,j;
+ long flags = (mb->secflag ? AU_SECURE : NIL) |
+ (mb->authuser[0] ? AU_AUTHUSER : NIL);
+ char *s,*t,*r,tmp[MAILTMPLEN];
+ /* clear ESMTP data */
+ memset (&ESMTP,0,sizeof (ESMTP));
+ if (mb->loser) return 500; /* never do EHLO if a loser */
+ sprintf (tmp,"EHLO %s",host); /* build the complete command */
+ if (stream->debug) mm_dlog (tmp);
+ strcat (tmp,"\015\012");
+ /* send the command */
+ if (!net_soutr (stream->netstream,tmp))
+ return smtp_fake (stream,"SMTP connection broken (EHLO)");
+ /* got an OK reply? */
+ do if ((i = smtp_reply (stream)) == SMTPOK) {
+ /* hack for AUTH= */
+ if (stream->reply[4] && stream->reply[5] && stream->reply[6] &&
+ stream->reply[7] && (stream->reply[8] == '=')) stream->reply[8] = ' ';
+ /* get option code */
+ if (!(s = strtok_r (stream->reply+4," ",&r)));
+ /* have option, does it have a value */
+ else if ((t = strtok_r (NIL," ",&r)) && *t) {
+ /* EHLO options which take arguments */
+ if (!compare_cstring (s,"SIZE")) {
+ if (isdigit (*t)) ESMTP.size.limit = strtoul (t,&t,10);
+ ESMTP.size.ok = T;
+ }
+ else if (!compare_cstring (s,"DELIVERBY")) {
+ if (isdigit (*t)) ESMTP.deliverby.minby = strtoul (t,&t,10);
+ ESMTP.deliverby.ok = T;
+ }
+ else if (!compare_cstring (s,"ATRN")) {
+ ESMTP.atrn.domains = cpystr (t);
+ ESMTP.atrn.ok = T;
+ }
+ else if (!compare_cstring (s,"AUTH"))
+ do if ((j = mail_lookup_auth_name (t,flags)) &&
+ (--j < MAXAUTHENTICATORS)) ESMTP.auth |= (1 << j);
+ while ((t = strtok_r (NIL," ",&r)) && *t);
+ }
+ /* EHLO options which do not take arguments */
+ else if (!compare_cstring (s,"SIZE")) ESMTP.size.ok = T;
+ else if (!compare_cstring (s,"8BITMIME")) ESMTP.eightbit.ok = T;
+ else if (!compare_cstring (s,"DSN")) ESMTP.dsn.ok = T;
+ else if (!compare_cstring (s,"ATRN")) ESMTP.atrn.ok = T;
+ else if (!compare_cstring (s,"SEND")) ESMTP.service.send = T;
+ else if (!compare_cstring (s,"SOML")) ESMTP.service.soml = T;
+ else if (!compare_cstring (s,"SAML")) ESMTP.service.saml = T;
+ else if (!compare_cstring (s,"EXPN")) ESMTP.service.expn = T;
+ else if (!compare_cstring (s,"HELP")) ESMTP.service.help = T;
+ else if (!compare_cstring (s,"TURN")) ESMTP.service.turn = T;
+ else if (!compare_cstring (s,"ETRN")) ESMTP.service.etrn = T;
+ else if (!compare_cstring (s,"STARTTLS")) ESMTP.service.starttls = T;
+ else if (!compare_cstring (s,"RELAY")) ESMTP.service.relay = T;
+ else if (!compare_cstring (s,"PIPELINING")) ESMTP.service.pipe = T;
+ else if (!compare_cstring (s,"ENHANCEDSTATUSCODES"))
+ ESMTP.service.ensc = T;
+ else if (!compare_cstring (s,"BINARYMIME")) ESMTP.service.bmime = T;
+ else if (!compare_cstring (s,"CHUNKING")) ESMTP.service.chunk = T;
+ }
+ while ((i < 100) || (stream->reply[3] == '-'));
+ /* disable LOGIN if PLAIN also advertised */
+ if ((j = mail_lookup_auth_name ("PLAIN",NIL)) && (--j < MAXAUTHENTICATORS) &&
+ (ESMTP.auth & (1 << j)) &&
+ (j = mail_lookup_auth_name ("LOGIN",NIL)) && (--j < MAXAUTHENTICATORS))
+ ESMTP.auth &= ~(1 << j);
+ return i; /* return the response code */
+}
+
+/* Simple Mail Transfer Protocol set fake error and abort
+ * Accepts: SMTP stream
+ * error text
+ * Returns: SMTPSOFTFATAL, always
+ */
+
+long smtp_fake (SENDSTREAM *stream,char *text)
+{
+ if (stream->netstream) { /* close net connection if still open */
+ net_close (stream->netstream);
+ stream->netstream = NIL;
+ }
+ /* set last error */
+ return smtp_seterror (stream,SMTPSOFTFATAL,text);
+}
+
+
+/* Simple Mail Transfer Protocol set error
+ * Accepts: SMTP stream
+ * SMTP error code
+ * error text
+ * Returns: error code
+ */
+
+static long smtp_seterror (SENDSTREAM *stream,long code,char *text)
+{
+ /* flush any old reply */
+ if (stream->reply ) fs_give ((void **) &stream->reply);
+ /* set up pseudo-reply string */
+ stream->reply = (char *) fs_get (20+strlen (text));
+ sprintf (stream->reply,"%ld %s",code,text);
+ return code; /* return error code */
+}
+
+
+/* Simple Mail Transfer Protocol filter mail
+ * Accepts: stream
+ * string
+ * Returns: T on success, NIL on failure
+ */
+
+long smtp_soutr (void *stream,char *s)
+{
+ char c,*t;
+ /* "." on first line */
+ if (s[0] == '.') net_sout (stream,".",1);
+ /* find lines beginning with a "." */
+ while (t = strstr (s,"\015\012.")) {
+ c = *(t += 3); /* remember next character after "." */
+ *t = '\0'; /* tie off string */
+ /* output prefix */
+ if (!net_sout (stream,s,t-s)) return NIL;
+ *t = c; /* restore delimiter */
+ s = t - 1; /* push pointer up to the "." */
+ }
+ /* output remainder of text */
+ return *s ? net_soutr (stream,s) : T;
+}
diff --git a/imap/src/c-client/smtp.h b/imap/src/c-client/smtp.h
new file mode 100644
index 00000000..4be82db4
--- /dev/null
+++ b/imap/src/c-client/smtp.h
@@ -0,0 +1,76 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Simple Mail Transfer Protocol (SMTP) routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 27 July 1988
+ * Last Edited: 15 August 2007
+ *
+ * This original version of this file is
+ * Copyright 1988 Stanford University
+ * and was developed in the Symbolic Systems Resources Group of the Knowledge
+ * Systems Laboratory at Stanford University in 1987-88, and was funded by the
+ * Biomedical Research Technology Program of the NationalInstitutes of Health
+ * under grant number RR-00785.
+ */
+
+/* Constants (should be in smtp.c) */
+
+#define SMTPTCPPORT (long) 25 /* assigned TCP contact port */
+#define SUBMITTCPPORT (long) 587/* assigned TCP contact port */
+
+
+/* SMTP open options
+ * For compatibility with the past, SOP_DEBUG must always be 1.
+ */
+
+#define SOP_DEBUG (long) 1 /* debug protocol negotiations */
+#define SOP_DSN (long) 2 /* DSN requested */
+ /* DSN notification, none set mean NEVER */
+#define SOP_DSN_NOTIFY_FAILURE (long) 4
+#define SOP_DSN_NOTIFY_DELAY (long) 8
+#define SOP_DSN_NOTIFY_SUCCESS (long) 16
+ /* DSN return full msg vs. header */
+
+#define SOP_DSN_RETURN_FULL (long) 32
+#define SOP_8BITMIME (long) 64 /* 8-bit MIME requested */
+#define SOP_SECURE (long) 256 /* don't do non-secure authentication */
+#define SOP_TRYSSL (long) 512 /* try SSL first */
+#define SOP_TRYALT SOP_TRYSSL /* old name */
+ /* reserved for application use */
+#define SOP_RESERVED (unsigned long) 0xff000000
+
+
+/* Compatibility support names */
+
+#define smtp_open(hostlist,options) \
+ smtp_open_full (NIL,hostlist,"smtp",NIL,options)
+
+
+/* Function prototypes */
+
+void *smtp_parameters (long function,void *value);
+SENDSTREAM *smtp_open_full (NETDRIVER *dv,char **hostlist,char *service,
+ unsigned long port,long options);
+SENDSTREAM *smtp_close (SENDSTREAM *stream);
+long smtp_mail (SENDSTREAM *stream,char *type,ENVELOPE *msg,BODY *body);
+long smtp_verbose (SENDSTREAM *stream);
diff --git a/imap/src/c-client/sslio.h b/imap/src/c-client/sslio.h
new file mode 100644
index 00000000..8063f3fc
--- /dev/null
+++ b/imap/src/c-client/sslio.h
@@ -0,0 +1,70 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: SSL routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 7 February 2001
+ * Last Edited: 30 August 2006
+ */
+
+/* SSL driver */
+
+struct ssl_driver { /* must parallel NETDRIVER in mail.h */
+ SSLSTREAM *(*open) (char *host,char *service,unsigned long port);
+ SSLSTREAM *(*aopen) (NETMBX *mb,char *service,char *usrbuf);
+ char *(*getline) (SSLSTREAM *stream);
+ long (*getbuffer) (SSLSTREAM *stream,unsigned long size,char *buffer);
+ long (*soutr) (SSLSTREAM *stream,char *string);
+ long (*sout) (SSLSTREAM *stream,char *string,unsigned long size);
+ void (*close) (SSLSTREAM *stream);
+ char *(*host) (SSLSTREAM *stream);
+ char *(*remotehost) (SSLSTREAM *stream);
+ unsigned long (*port) (SSLSTREAM *stream);
+ char *(*localhost) (SSLSTREAM *stream);
+};
+
+
+/* SSL stdio stream */
+
+typedef struct ssl_stdiostream {
+ SSLSTREAM *sslstream; /* SSL stream */
+ int octr; /* output counter */
+ char *optr; /* output pointer */
+ char obuf[SSLBUFLEN]; /* output buffer */
+} SSLSTDIOSTREAM;
+
+
+/* Function prototypes */
+
+SSLSTREAM *ssl_open (char *host,char *service,unsigned long port);
+SSLSTREAM *ssl_aopen (NETMBX *mb,char *service,char *usrbuf);
+char *ssl_getline (SSLSTREAM *stream);
+long ssl_getbuffer (SSLSTREAM *stream,unsigned long size,char *buffer);
+long ssl_getdata (SSLSTREAM *stream);
+long ssl_soutr (SSLSTREAM *stream,char *string);
+long ssl_sout (SSLSTREAM *stream,char *string,unsigned long size);
+void ssl_close (SSLSTREAM *stream);
+char *ssl_host (SSLSTREAM *stream);
+char *ssl_remotehost (SSLSTREAM *stream);
+unsigned long ssl_port (SSLSTREAM *stream);
+char *ssl_localhost (SSLSTREAM *stream);
+long ssl_server_input_wait (long seconds);
diff --git a/imap/src/c-client/tcp.h b/imap/src/c-client/tcp.h
new file mode 100644
index 00000000..b9f095a8
--- /dev/null
+++ b/imap/src/c-client/tcp.h
@@ -0,0 +1,59 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: TCP/IP routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 31 January 2007
+ */
+
+
+/* Dummy definition overridden by TCP routines */
+
+#ifndef TCPSTREAM
+#define TCPSTREAM void
+#endif
+
+
+/* Function prototypes */
+
+void *tcp_parameters (long function,void *value);
+TCPSTREAM *tcp_open (char *host,char *service,unsigned long port);
+TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf);
+char *tcp_getline (TCPSTREAM *stream);
+long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer);
+long tcp_getdata (TCPSTREAM *stream);
+long tcp_soutr (TCPSTREAM *stream,char *string);
+long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size);
+void tcp_close (TCPSTREAM *stream);
+char *tcp_host (TCPSTREAM *stream);
+char *tcp_remotehost (TCPSTREAM *stream);
+unsigned long tcp_port (TCPSTREAM *stream);
+char *tcp_localhost (TCPSTREAM *stream);
+char *tcp_clientaddr (void);
+char *tcp_clienthost (void);
+long tcp_clientport (void);
+char *tcp_serveraddr (void);
+char *tcp_serverhost (void);
+long tcp_serverport (void);
+char *tcp_canonical (char *name);
+long tcp_isclienthost (char *host);
diff --git a/imap/src/c-client/utf8.c b/imap/src/c-client/utf8.c
new file mode 100644
index 00000000..8fda7ffd
--- /dev/null
+++ b/imap/src/c-client/utf8.c
@@ -0,0 +1,2554 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UTF-8 routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 June 1997
+ * Last Edited: 17 January 2008
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include "c-client.h"
+
+/* *** IMPORTANT ***
+ *
+ * There is a very important difference between "character set" and "charset",
+ * and the comments in this file reflect these differences. A "character set"
+ * (also known as "coded character set") is a mapping between codepoints and
+ * characters. A "charset" is as defined in MIME, and incorporates one or more
+ * coded character sets in a character encoding scheme. See RFC 2130 for more
+ * details.
+ */
+
+
+/* Character set conversion tables */
+
+#include "iso_8859.c" /* 8-bit single-byte coded graphic */
+#include "koi8_r.c" /* Cyrillic - Russia */
+#include "koi8_u.c" /* Cyrillic - Ukraine */
+#include "tis_620.c" /* Thai */
+#include "viscii.c" /* Vietnamese */
+#include "windows.c" /* Windows */
+#include "ibm.c" /* IBM */
+#include "gb_2312.c" /* Chinese (PRC) - simplified */
+#include "gb_12345.c" /* Chinese (PRC) - traditional */
+#include "jis_0208.c" /* Japanese - basic */
+#include "jis_0212.c" /* Japanese - supplementary */
+#include "ksc_5601.c" /* Korean */
+#include "big5.c" /* Taiwanese (ROC) - industrial standard */
+#include "cns11643.c" /* Taiwanese (ROC) - national standard */
+
+
+#include "widths.c" /* Unicode character widths */
+#include "tmap.c" /* Unicode titlecase mapping */
+#include "decomtab.c" /* Unicode decomposions */
+
+/* EUC parameters */
+
+#ifdef GBTOUNICODE /* PRC simplified Chinese */
+static const struct utf8_eucparam gb_param = {
+ BASE_GB2312_KU,BASE_GB2312_TEN,MAX_GB2312_KU,MAX_GB2312_TEN,
+ (void *) gb2312tab};
+#endif
+
+
+#ifdef GB12345TOUNICODE /* PRC traditional Chinese */
+static const struct utf8_eucparam gbt_param = {
+ BASE_GB12345_KU,BASE_GB12345_TEN,MAX_GB12345_KU,MAX_GB12345_TEN,
+ (void *) gb12345tab};
+#endif
+
+
+#ifdef BIG5TOUNICODE /* ROC traditional Chinese */
+static const struct utf8_eucparam big5_param[] = {
+ {BASE_BIG5_KU,BASE_BIG5_TEN_0,MAX_BIG5_KU,MAX_BIG5_TEN_0,(void *) big5tab},
+ {BASE_BIG5_KU,BASE_BIG5_TEN_1,MAX_BIG5_KU,MAX_BIG5_TEN_1,NIL}
+};
+#endif
+
+
+#ifdef JISTOUNICODE /* Japanese */
+static const struct utf8_eucparam jis_param[] = {
+ {BASE_JIS0208_KU,BASE_JIS0208_TEN,MAX_JIS0208_KU,MAX_JIS0208_TEN,
+ (void *) jis0208tab},
+ {MIN_KANA_8,0,MAX_KANA_8,0,(void *) KANA_8},
+#ifdef JIS0212TOUNICODE /* Japanese extended */
+ {BASE_JIS0212_KU,BASE_JIS0212_TEN,MAX_JIS0212_KU,MAX_JIS0212_TEN,
+ (void *) jis0212tab}
+#else
+ {0,0,0,0,NIL}
+#endif
+};
+#endif
+
+
+#ifdef KSCTOUNICODE /* Korean */
+static const struct utf8_eucparam ksc_param = {
+ BASE_KSC5601_KU,BASE_KSC5601_TEN,MAX_KSC5601_KU,MAX_KSC5601_TEN,
+ (void *) ksc5601tab};
+#endif
+
+/* List of supported charsets */
+
+static const CHARSET utf8_csvalid[] = {
+ {"US-ASCII",CT_ASCII,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ NIL,NIL,NIL},
+ {"UTF-8",CT_UTF8,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ NIL,SC_UNICODE,NIL},
+ {"UTF-7",CT_UTF7,CF_PRIMARY | CF_POSTING | CF_UNSUPRT,
+ NIL,SC_UNICODE,"UTF-8"},
+ {"ISO-8859-1",CT_1BYTE0,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ NIL,SC_LATIN_1,NIL},
+ {"ISO-8859-2",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) iso8859_2tab,SC_LATIN_2,NIL},
+ {"ISO-8859-3",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) iso8859_3tab,SC_LATIN_3,NIL},
+ {"ISO-8859-4",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) iso8859_4tab,SC_LATIN_4,NIL},
+ {"ISO-8859-5",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) iso8859_5tab,SC_CYRILLIC,"KOI8-R"},
+ {"ISO-8859-6",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) iso8859_6tab,SC_ARABIC,NIL},
+ {"ISO-8859-7",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) iso8859_7tab,SC_GREEK,NIL},
+ {"ISO-8859-8",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) iso8859_8tab,SC_HEBREW,NIL},
+ {"ISO-8859-9",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) iso8859_9tab,SC_LATIN_5,NIL},
+ {"ISO-8859-10",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) iso8859_10tab,SC_LATIN_6,NIL},
+ {"ISO-8859-11",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) iso8859_11tab,SC_THAI,NIL},
+#if 0 /* ISO 8859-12 reserved for ISCII(?) */
+ {"ISO-8859-12",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) iso8859_12tab,NIL,NIL},
+#endif
+ {"ISO-8859-13",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) iso8859_13tab,SC_LATIN_7,NIL},
+ {"ISO-8859-14",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) iso8859_14tab,SC_LATIN_8,NIL},
+ {"ISO-8859-15",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) iso8859_15tab,SC_LATIN_9,NIL},
+ {"ISO-8859-16",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) iso8859_16tab,SC_LATIN_10,NIL},
+ {"KOI8-R",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) koi8rtab,SC_CYRILLIC,NIL},
+ {"KOI8-U",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) koi8utab,SC_CYRILLIC | SC_UKRANIAN,NIL},
+ {"KOI8-RU",CT_1BYTE,CF_DISPLAY,
+ (void *) koi8utab,SC_CYRILLIC | SC_UKRANIAN,"KOI8-U"},
+ {"TIS-620",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) tis620tab,SC_THAI,"ISO-8859-11"},
+ {"VISCII",CT_1BYTE8,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) visciitab,SC_VIETNAMESE,NIL},
+
+#ifdef GBTOUNICODE
+ {"GBK",CT_DBYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) &gb_param,SC_CHINESE_SIMPLIFIED,NIL},
+ {"GB2312",CT_DBYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) &gb_param,SC_CHINESE_SIMPLIFIED,"GBK"},
+ {"CN-GB",CT_DBYTE,CF_DISPLAY,
+ (void *) &gb_param,SC_CHINESE_SIMPLIFIED,"GBK"},
+#ifdef CNS1TOUNICODE
+ {"ISO-2022-CN",CT_2022,CF_PRIMARY | CF_UNSUPRT,
+ NIL,SC_CHINESE_SIMPLIFIED | SC_CHINESE_TRADITIONAL,
+ NIL},
+#endif
+#endif
+#ifdef GB12345TOUNICODE
+ {"CN-GB-12345",CT_DBYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) &gbt_param,SC_CHINESE_TRADITIONAL,"BIG5"},
+#endif
+#ifdef BIG5TOUNICODE
+ {"BIG5",CT_DBYTE2,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) big5_param,SC_CHINESE_TRADITIONAL,NIL},
+ {"CN-BIG5",CT_DBYTE2,CF_DISPLAY,
+ (void *) big5_param,SC_CHINESE_TRADITIONAL,"BIG5"},
+ {"BIG-5",CT_DBYTE2,CF_DISPLAY,
+ (void *) big5_param,SC_CHINESE_TRADITIONAL,"BIG5"},
+#endif
+#ifdef JISTOUNICODE
+ {"ISO-2022-JP",CT_2022,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ NIL,SC_JAPANESE,NIL},
+ {"EUC-JP",CT_EUC,CF_PRIMARY | CF_DISPLAY,
+ (void *) jis_param,SC_JAPANESE,"ISO-2022-JP"},
+ {"SHIFT_JIS",CT_SJIS,CF_PRIMARY | CF_DISPLAY,
+ NIL,SC_JAPANESE,"ISO-2022-JP"},
+ {"SHIFT-JIS",CT_SJIS,CF_PRIMARY | CF_DISPLAY,
+ NIL,SC_JAPANESE,"ISO-2022-JP"},
+#ifdef JIS0212TOUNICODE
+ {"ISO-2022-JP-1",CT_2022,CF_UNSUPRT,
+ NIL,SC_JAPANESE,"ISO-2022-JP"},
+#ifdef GBTOUNICODE
+#ifdef KSCTOUNICODE
+ {"ISO-2022-JP-2",CT_2022,CF_UNSUPRT,
+ NIL,
+ SC_LATIN_1 | SC_LATIN_2 | SC_LATIN_3 | SC_LATIN_4 | SC_LATIN_5 |
+ SC_LATIN_6 | SC_LATIN_7 | SC_LATIN_8 | SC_LATIN_9 | SC_LATIN_10 |
+ SC_ARABIC | SC_CYRILLIC | SC_GREEK | SC_HEBREW | SC_THAI |
+ SC_VIETNAMESE | SC_CHINESE_SIMPLIFIED | SC_JAPANESE | SC_KOREAN
+#ifdef CNS1TOUNICODE
+ | SC_CHINESE_TRADITIONAL
+#endif
+ ,"UTF-8"},
+#endif
+#endif
+#endif
+#endif
+
+#ifdef KSCTOUNICODE
+ {"ISO-2022-KR",CT_2022,CF_PRIMARY | CF_DISPLAY | CF_UNSUPRT,
+ NIL,SC_KOREAN,"EUC-KR"},
+ {"EUC-KR",CT_DBYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) &ksc_param,SC_KOREAN,NIL},
+ {"KSC5601",CT_DBYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) &ksc_param,SC_KOREAN,"EUC-KR"},
+ {"KSC_5601",CT_DBYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) &ksc_param,SC_KOREAN,"EUC-KR"},
+ {"KS_C_5601-1987",CT_DBYTE,CF_DISPLAY,
+ (void *) &ksc_param,SC_KOREAN,"EUC-KR"},
+ {"KS_C_5601-1989",CT_DBYTE,CF_DISPLAY,
+ (void *) &ksc_param,SC_KOREAN,"EUC-KR"},
+ {"KS_C_5601-1992",CT_DBYTE,CF_DISPLAY,
+ (void *) &ksc_param,SC_KOREAN,"EUC-KR"},
+ {"KS_C_5601-1997",CT_DBYTE,CF_DISPLAY,
+ (void *) &ksc_param,SC_KOREAN,"EUC-KR"},
+#endif
+
+ /* deep sigh */
+ {"WINDOWS-874",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) windows_874tab,SC_THAI,"ISO-8859-11"},
+ {"CP874",CT_1BYTE,CF_DISPLAY,
+ (void *) windows_874tab,SC_THAI,"ISO-8859-11"},
+#ifdef GBTOUNICODE
+ {"WINDOWS-936",CT_DBYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) &gb_param,SC_CHINESE_SIMPLIFIED,"GBK"},
+ {"CP936",CT_DBYTE,CF_DISPLAY,
+ (void *) &gb_param,SC_CHINESE_SIMPLIFIED,"GBK"},
+#endif
+#ifdef KSCTOUNICODE
+ {"WINDOWS-949",CT_DBYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) &ksc_param,SC_KOREAN,"EUC-KR"},
+ {"CP949",CT_DBYTE,CF_DISPLAY,
+ (void *) &ksc_param,SC_KOREAN,"EUC-KR"},
+ {"X-WINDOWS-949",CT_DBYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) &ksc_param,SC_KOREAN,"EUC-KR"},
+#endif
+ {"WINDOWS-1250",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) windows_1250tab,SC_LATIN_2,"ISO-8859-2"},
+ {"CP1250",CT_1BYTE,CF_DISPLAY,
+ (void *) windows_1250tab,SC_LATIN_2,"ISO-8859-2"},
+ {"WINDOWS-1251",CT_1BYTE,CF_PRIMARY | CF_DISPLAY | CF_POSTING,
+ (void *) windows_1251tab,SC_CYRILLIC,"KOI8-R"},
+ {"CP1251",CT_1BYTE,CF_DISPLAY,
+ (void *) windows_1251tab,SC_CYRILLIC,"KOI8-R"},
+ {"WINDOWS-1252",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) windows_1252tab,SC_LATIN_1,"ISO-8859-1"},
+ {"CP1252",CT_1BYTE,CF_DISPLAY,
+ (void *) windows_1252tab,SC_LATIN_1,"ISO-8859-1"},
+ {"WINDOWS-1253",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) windows_1253tab,SC_GREEK,"ISO-8859-7"},
+ {"CP1253",CT_1BYTE,CF_DISPLAY,
+ (void *) windows_1253tab,SC_GREEK,"ISO-8859-7"},
+ {"WINDOWS-1254",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) windows_1254tab,SC_LATIN_5,"ISO-8859-9"},
+ {"CP1254",CT_1BYTE,CF_DISPLAY,
+ (void *) windows_1254tab,SC_LATIN_5,"ISO-8859-9"},
+ {"WINDOWS-1255",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) windows_1255tab,SC_HEBREW,"ISO-8859-8"},
+ {"CP1255",CT_1BYTE,CF_DISPLAY,
+ (void *) windows_1255tab,SC_HEBREW,"ISO-8859-8"},
+ {"WINDOWS-1256",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) windows_1256tab,SC_ARABIC,"ISO-8859-6"},
+ {"CP1256",CT_1BYTE,CF_DISPLAY,
+ (void *) windows_1256tab,SC_ARABIC,"ISO-8859-6"},
+ {"WINDOWS-1257",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) windows_1257tab,SC_LATIN_7,"ISO-8859-13"},
+ {"CP1257",CT_1BYTE,CF_DISPLAY,
+ (void *) windows_1257tab,SC_LATIN_7,"ISO-8859-13"},
+ {"WINDOWS-1258",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) windows_1258tab,SC_VIETNAMESE,"VISCII"},
+ {"CP1258",CT_1BYTE,CF_DISPLAY,
+ (void *) windows_1258tab,SC_VIETNAMESE,"VISCII"},
+
+ /* deeper sigh */
+ {"IBM367",CT_ASCII,CF_PRIMARY | CF_DISPLAY,
+ NIL,NIL,"US-ASCII"},
+ {"IBM437",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) ibm_437tab,SC_LATIN_1,"ISO-8859-1"},
+ {"IBM737",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) ibm_737tab,SC_GREEK,"ISO-8859-7"},
+ {"IBM775",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) ibm_775tab,SC_LATIN_7,"ISO-8859-13"},
+ {"IBM850",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) ibm_850tab,SC_LATIN_1,"ISO-8859-1"},
+ {"IBM852",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) ibm_852tab,SC_LATIN_2,"ISO-8859-2"},
+ {"IBM855",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) ibm_855tab,SC_CYRILLIC,"ISO-8859-5"},
+ {"IBM857",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) ibm_857tab,SC_LATIN_5,"ISO-8859-9"},
+ {"IBM860",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) ibm_860tab,SC_LATIN_1,"ISO-8859-1"},
+ {"IBM861",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) ibm_861tab,SC_LATIN_6,"ISO-8859-10"},
+ {"IBM862",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) ibm_862tab,SC_HEBREW,"ISO-8859-8"},
+ {"IBM863",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) ibm_863tab,SC_LATIN_1,"ISO-8859-1"},
+ {"IBM864",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) ibm_864tab,SC_ARABIC,"ISO-8859-6"},
+ {"IBM865",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) ibm_865tab,SC_LATIN_6,"ISO-8859-10"},
+ {"IBM866",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) ibm_866tab,SC_CYRILLIC,"KOI8-R"},
+ {"IBM869",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) ibm_869tab,SC_GREEK,"ISO-8859-7"},
+ {"IBM874",CT_1BYTE,CF_PRIMARY | CF_DISPLAY,
+ (void *) ibm_874tab,SC_THAI,"ISO-8859-11"},
+ /* deepest sigh */
+ {"ANSI_X3.4-1968",CT_ASCII,CF_DISPLAY,
+ NIL,NIL,"US-ASCII"},
+ {"UNICODE-1-1-UTF-7",CT_UTF7,CF_UNSUPRT,
+ NIL,SC_UNICODE,"UTF-8"},
+ /* these should never appear in email */
+ {"UCS-2",CT_UCS2,CF_PRIMARY | CF_DISPLAY | CF_NOEMAIL,
+ NIL,SC_UNICODE,"UTF-8"},
+ {"UCS-4",CT_UCS4,CF_PRIMARY | CF_DISPLAY | CF_NOEMAIL,
+ NIL,SC_UNICODE,"UTF-8"},
+ {"UTF-16",CT_UTF16,CF_PRIMARY | CF_DISPLAY | CF_NOEMAIL,
+ NIL,SC_UNICODE,"UTF-8"},
+ NIL
+};
+
+/* Non-Unicode Script table */
+
+static const SCRIPT utf8_scvalid[] = {
+ {"Arabic",NIL,SC_ARABIC},
+ {"Chinese Simplified","China, Singapore",SC_CHINESE_SIMPLIFIED},
+ {"Chinese Traditional","Taiwan, Hong Kong, Macao",SC_CHINESE_TRADITIONAL},
+ {"Cyrillic",NIL,SC_CYRILLIC},
+ {"Cyrillic Ukranian",NIL,SC_UKRANIAN},
+ {"Greek",NIL,SC_GREEK},
+ {"Hebrew",NIL,SC_HEBREW},
+ {"Japanese",NIL,SC_JAPANESE},
+ {"Korean",NIL,SC_KOREAN},
+ {"Latin-1","Western Europe",SC_LATIN_1},
+ {"Latin-2","Eastern Europe",SC_LATIN_2},
+ {"Latin-3","Southern Europe",SC_LATIN_3},
+ {"Latin-4","Northern Europe",SC_LATIN_4},
+ {"Latin-5","Turkish",SC_LATIN_5},
+ {"Latin-6","Nordic",SC_LATIN_6},
+ {"Latin-7","Baltic",SC_LATIN_7},
+ {"Latin-8","Celtic",SC_LATIN_8},
+ {"Latin-9","Euro",SC_LATIN_9},
+ {"Latin-10","Balkan",SC_LATIN_10},
+ {"Thai",NIL,SC_THAI},
+ {"Vietnamese",NIL,SC_VIETNAMESE},
+ NIL
+};
+
+/* Look up script name or return entire table
+ * Accepts: script name or NIL
+ * Returns: pointer to script table entry or NIL if unknown
+ */
+
+SCRIPT *utf8_script (char *script)
+{
+ unsigned long i;
+ if (!script) return (SCRIPT *) &utf8_scvalid[0];
+ else if (*script && (strlen (script) < 128))
+ for (i = 0; utf8_scvalid[i].name; i++)
+ if (!compare_cstring (script,utf8_scvalid[i].name))
+ return (SCRIPT *) &utf8_scvalid[i];
+ return NIL; /* failed */
+}
+
+
+/* Look up charset name or return entire table
+ * Accepts: charset name or NIL
+ * Returns: charset table entry or NIL if unknown
+ */
+
+const CHARSET *utf8_charset (char *charset)
+{
+ unsigned long i;
+ if (!charset) return (CHARSET *) &utf8_csvalid[0];
+ else if (*charset && (strlen (charset) < 128))
+ for (i = 0; utf8_csvalid[i].name; i++)
+ if (!compare_cstring (charset,utf8_csvalid[i].name))
+ return (CHARSET *) &utf8_csvalid[i];
+ return NIL; /* failed */
+}
+
+/* Validate charset and generate error message if invalid
+ * Accepts: bad character set
+ * Returns: NIL if good charset, else error message string
+ */
+
+#define BADCSS "[BADCHARSET ("
+#define BADCSE ")] Unknown charset: "
+
+char *utf8_badcharset (char *charset)
+{
+ char *msg = NIL;
+ if (!utf8_charset (charset)) {
+ char *s,*t;
+ unsigned long i,j;
+ /* calculate size of header, trailer, and bad
+ * charset plus charset names */
+ for (i = 0, j = sizeof (BADCSS) + sizeof (BADCSE) + strlen (charset) - 2;
+ utf8_csvalid[i].name; i++)
+ j += strlen (utf8_csvalid[i].name) + 1;
+ /* not built right */
+ if (!i) fatal ("No valid charsets!");
+ /* header */
+ for (s = msg = (char *) fs_get (j), t = BADCSS; *t; *s++ = *t++);
+ /* each charset */
+ for (i = 0; utf8_csvalid[i].name; *s++ = ' ', i++)
+ for (t = utf8_csvalid[i].name; *t; *s++ = *t++);
+ /* back over last space, trailer */
+ for (t = BADCSE, --s; *t; *s++ = *t++);
+ /* finally bogus charset */
+ for (t = charset; *t; *s++ = *t++);
+ *s++ = '\0'; /* finally tie off string */
+ if (s != (msg + j)) fatal ("charset msg botch");
+ }
+ return msg;
+}
+
+/* Convert charset labelled sized text to UTF-8
+ * Accepts: source sized text
+ * charset
+ * pointer to returned sized text if non-NIL
+ * flags
+ * Returns: T if successful, NIL if failure
+ */
+
+long utf8_text (SIZEDTEXT *text,char *charset,SIZEDTEXT *ret,long flags)
+{
+ ucs4cn_t cv = (flags & U8T_CASECANON) ? ucs4_titlecase : NIL;
+ ucs4de_t de = (flags & U8T_DECOMPOSE) ? ucs4_decompose_recursive : NIL;
+ const CHARSET *cs = (charset && *charset) ?
+ utf8_charset (charset) : utf8_infercharset (text);
+ if (cs) return (text && ret) ? utf8_text_cs (text,cs,ret,cv,de) : LONGT;
+ if (ret) { /* no conversion possible */
+ ret->data = text->data; /* so return source */
+ ret->size = text->size;
+ }
+ return NIL; /* failure */
+}
+
+
+/* Operations used in converting data */
+
+#define UTF8_COUNT_BMP(count,c,cv,de) { \
+ void *more = NIL; \
+ if (cv) c = (*cv) (c); \
+ if (de) c = (*de) (c,&more); \
+ do count += UTF8_SIZE_BMP(c); \
+ while (more && (c = (*de) (U8G_ERROR,&more)));\
+}
+
+#define UTF8_WRITE_BMP(b,c,cv,de) { \
+ void *more = NIL; \
+ if (cv) c = (*cv) (c); \
+ if (de) c = (*de) (c,&more); \
+ do UTF8_PUT_BMP (b,c) \
+ while (more && (c = (*de) (U8G_ERROR,&more)));\
+}
+
+#define UTF8_COUNT(count,c,cv,de) { \
+ void *more = NIL; \
+ if (cv) c = (*cv) (c); \
+ if (de) c = (*de) (c,&more); \
+ do count += utf8_size (c); \
+ while (more && (c = (*de) (U8G_ERROR,&more)));\
+}
+
+#define UTF8_WRITE(b,c,cv,de) { \
+ void *more = NIL; \
+ if (cv) c = (*cv) (c); \
+ if (de) c = (*de) (c,&more); \
+ do b = utf8_put (b,c); \
+ while (more && (c = (*de) (U8G_ERROR,&more)));\
+}
+
+/* Convert sized text to UTF-8 given CHARSET block
+ * Accepts: source sized text
+ * CHARSET block
+ * pointer to returned sized text
+ * canonicalization function
+ * decomposition function
+ * Returns: T if successful, NIL if failure
+ */
+
+long utf8_text_cs (SIZEDTEXT *text,const CHARSET *cs,SIZEDTEXT *ret,
+ ucs4cn_t cv,ucs4de_t de)
+{
+ ret->data = text->data; /* default to source */
+ ret->size = text->size;
+ switch (cs->type) { /* convert if type known */
+ case CT_ASCII: /* 7-bit ASCII no table */
+ case CT_UTF8: /* variable UTF-8 encoded Unicode no table */
+ if (cv || de) utf8_text_utf8 (text,ret,cv,de);
+ break;
+ case CT_1BYTE0: /* 1 byte no table */
+ utf8_text_1byte0 (text,ret,cv,de);
+ break;
+ case CT_1BYTE: /* 1 byte ASCII + table 0x80-0xff */
+ utf8_text_1byte (text,ret,cs->tab,cv,de);
+ break;
+ case CT_1BYTE8: /* 1 byte table 0x00 - 0xff */
+ utf8_text_1byte8 (text,ret,cs->tab,cv,de);
+ break;
+ case CT_EUC: /* 2 byte ASCII + utf8_eucparam base/CS2/CS3 */
+ utf8_text_euc (text,ret,cs->tab,cv,de);
+ break;
+ case CT_DBYTE: /* 2 byte ASCII + utf8_eucparam */
+ utf8_text_dbyte (text,ret,cs->tab,cv,de);
+ break;
+ case CT_DBYTE2: /* 2 byte ASCII + utf8_eucparam plane1/2 */
+ utf8_text_dbyte2 (text,ret,cs->tab,cv,de);
+ break;
+ case CT_UTF7: /* variable UTF-7 encoded Unicode no table */
+ utf8_text_utf7 (text,ret,cv,de);
+ break;
+ case CT_UCS2: /* 2 byte 16-bit Unicode no table */
+ utf8_text_ucs2 (text,ret,cv,de);
+ break;
+ case CT_UCS4: /* 4 byte 32-bit Unicode no table */
+ utf8_text_ucs4 (text,ret,cv,de);
+ break;
+ case CT_UTF16: /* variable UTF-16 encoded Unicode no table */
+ utf8_text_utf16 (text,ret,cv,de);
+ break;
+ case CT_2022: /* variable ISO-2022 encoded no table*/
+ utf8_text_2022 (text,ret,cv,de);
+ break;
+ case CT_SJIS: /* 2 byte Shift-JIS encoded JIS no table */
+ utf8_text_sjis (text,ret,cv,de);
+ break;
+ default: /* unknown character set type */
+ return NIL;
+ }
+ return LONGT; /* return success */
+}
+
+/* Reverse mapping routines
+ *
+ * These routines only support character sets, not all possible charsets. In
+ * particular, they do not support any Unicode encodings or ISO 2022.
+ *
+ * As a special dispensation, utf8_cstext() and utf8_cstocstext() support
+ * support ISO-2022-JP if EUC-JP can be reverse mapped; and utf8_rmaptext()
+ * will generated ISO-2022-JP using an EUC-JP rmap if flagged to do so.
+ *
+ * No attempt is made to map "equivalent" Unicode characters or Unicode
+ * characters that have the same glyph; nor is there any attempt to handle
+ * combining characters or otherwise do any stringprep. Maybe later.
+ */
+
+
+/* Convert UTF-8 sized text to charset
+ * Accepts: source sized text
+ * destination charset
+ * pointer to returned sized text
+ * substitute character if not in cs, else NIL to return failure
+ * Returns: T if successful, NIL if failure
+ */
+
+
+long utf8_cstext (SIZEDTEXT *text,char *charset,SIZEDTEXT *ret,
+ unsigned long errch)
+{
+ short iso2022jp = !compare_cstring (charset,"ISO-2022-JP");
+ unsigned short *rmap = utf8_rmap (iso2022jp ? "EUC-JP" : charset);
+ return rmap ? utf8_rmaptext (text,rmap,ret,errch,iso2022jp) : NIL;
+}
+
+/* Convert charset labelled sized text to another charset
+ * Accepts: source sized text
+ * source charset
+ * pointer to returned sized text
+ * destination charset
+ * substitute character if not in dest cs, else NIL to return failure
+ * Returns: T if successful, NIL if failure
+ *
+ * This routine has the same restricts as utf8_cstext().
+ */
+
+long utf8_cstocstext (SIZEDTEXT *src,char *sc,SIZEDTEXT *dst,char *dc,
+ unsigned long errch)
+{
+ SIZEDTEXT utf8;
+ const CHARSET *scs,*dcs;
+ unsigned short *rmap;
+ long ret = NIL;
+ long iso2022jp;
+ /* lookup charsets and reverse map */
+ if ((dc && (dcs = utf8_charset (dc))) &&
+ (rmap = (iso2022jp = ((dcs->type == CT_2022) &&
+ !compare_cstring (dcs->name,"ISO-2022-JP"))) ?
+ utf8_rmap ("EUC-JP") : utf8_rmap_cs (dcs)) &&
+ (scs = (sc && *sc) ? utf8_charset (sc) : utf8_infercharset (src))) {
+ /* init temporary buffer */
+ memset (&utf8,NIL,sizeof (SIZEDTEXT));
+ /* source cs equivalent to dest cs? */
+ if ((scs->type == dcs->type) && (scs->tab == dcs->tab)) {
+ dst->data = src->data; /* yes, just copy pointers */
+ dst->size = src->size;
+ ret = LONGT;
+ }
+ /* otherwise do the conversion */
+ else ret = (utf8_text_cs (src,scs,&utf8,NIL,NIL) &&
+ utf8_rmaptext (&utf8,rmap,dst,errch,iso2022jp));
+ /* flush temporary buffer */
+ if (utf8.data && (utf8.data != src->data) && (utf8.data != dst->data))
+ fs_give ((void **) &utf8.data);
+ }
+ return ret;
+}
+
+/* Cached rmap */
+
+static const CHARSET *currmapcs = NIL;
+static unsigned short *currmap = NIL;
+
+
+/* Cache and return map for UTF-8 -> character set
+ * Accepts: character set name
+ * Returns: cached map if character set found, else NIL
+ */
+
+unsigned short *utf8_rmap (char *charset)
+{
+ return (currmapcs && !compare_cstring (charset,currmapcs->name)) ? currmap :
+ utf8_rmap_cs (utf8_charset (charset));
+}
+
+
+/* Cache and return map for UTF-8 -> character set given CHARSET block
+ * Accepts: CHARSET block
+ * Returns: cached map if character set found, else NIL
+ */
+
+unsigned short *utf8_rmap_cs (const CHARSET *cs)
+{
+ unsigned short *ret = NIL;
+ if (!cs); /* have charset? */
+ else if (cs == currmapcs) ret = currmap;
+ else if (ret = utf8_rmap_gen (cs,currmap)) {
+ currmapcs = cs;
+ currmap = ret;
+ }
+ return ret;
+}
+
+/* Return map for UTF-8 -> character set given CHARSET block
+ * Accepts: CHARSET block
+ * old map to recycle
+ * Returns: map if character set found, else NIL
+ */
+
+unsigned short *utf8_rmap_gen (const CHARSET *cs,unsigned short *oldmap)
+{
+ unsigned short u,*tab,*rmap;
+ unsigned int i,m,ku,ten;
+ struct utf8_eucparam *param,*p2;
+ switch (cs->type) { /* is a character set? */
+ case CT_ASCII: /* 7-bit ASCII no table */
+ case CT_1BYTE0: /* 1 byte no table */
+ case CT_1BYTE: /* 1 byte ASCII + table 0x80-0xff */
+ case CT_1BYTE8: /* 1 byte table 0x00 - 0xff */
+ case CT_EUC: /* 2 byte ASCII + utf8_eucparam base/CS2/CS3 */
+ case CT_DBYTE: /* 2 byte ASCII + utf8_eucparam */
+ case CT_DBYTE2: /* 2 byte ASCII + utf8_eucparam plane1/2 */
+ case CT_SJIS: /* 2 byte Shift-JIS */
+ rmap = oldmap ? oldmap : /* recycle old map if supplied else make new */
+ (unsigned short *) fs_get (65536 * sizeof (unsigned short));
+ /* initialize table for ASCII */
+ for (i = 0; i < 128; i++) rmap[i] = (unsigned short) i;
+ /* populate remainder of table with NOCHAR */
+#define NOCHARBYTE (NOCHAR & 0xff)
+#if NOCHAR - ((NOCHARBYTE << 8) | NOCHARBYTE)
+ while (i < 65536) rmap[i++] = NOCHAR;
+#else
+ memset (rmap + 128,NOCHARBYTE,(65536 - 128) * sizeof (unsigned short));
+#endif
+ break;
+ default: /* unsupported charset type */
+ rmap = NIL; /* no map possible */
+ }
+ if (rmap) { /* have a map? */
+ switch (cs->type) { /* additional reverse map actions */
+ case CT_1BYTE0: /* 1 byte no table */
+ for (i = 128; i < 256; i++) rmap[i] = (unsigned short) i;
+ break;
+ case CT_1BYTE: /* 1 byte ASCII + table 0x80-0xff */
+ for (tab = (unsigned short *) cs->tab,i = 128; i < 256; i++)
+ if (tab[i & BITS7] != UBOGON) rmap[tab[i & BITS7]] = (unsigned short)i;
+ break;
+ case CT_1BYTE8: /* 1 byte table 0x00 - 0xff */
+ for (tab = (unsigned short *) cs->tab,i = 0; i < 256; i++)
+ if (tab[i] != UBOGON) rmap[tab[i]] = (unsigned short) i;
+ break;
+ case CT_EUC: /* 2 byte ASCII + utf8_eucparam base/CS2/CS3 */
+ for (param = (struct utf8_eucparam *) cs->tab,
+ tab = (unsigned short *) param->tab, ku = 0;
+ ku < param->max_ku; ku++)
+ for (ten = 0; ten < param->max_ten; ten++)
+ if ((u = tab[(ku * param->max_ten) + ten]) != UBOGON)
+ rmap[u] = ((ku + param->base_ku) << 8) +
+ (ten + param->base_ten) + 0x8080;
+ break;
+
+ case CT_DBYTE: /* 2 byte ASCII + utf8_eucparam */
+ for (param = (struct utf8_eucparam *) cs->tab,
+ tab = (unsigned short *) param->tab, ku = 0;
+ ku < param->max_ku; ku++)
+ for (ten = 0; ten < param->max_ten; ten++)
+ if ((u = tab[(ku * param->max_ten) + ten]) != UBOGON)
+ rmap[u] = ((ku + param->base_ku) << 8) + (ten + param->base_ten);
+ break;
+ case CT_DBYTE2: /* 2 byte ASCII + utf8_eucparam plane1/2 */
+ param = (struct utf8_eucparam *) cs->tab;
+ p2 = param + 1; /* plane 2 parameters */
+ /* only ten parameters should differ */
+ if ((param->base_ku != p2->base_ku) || (param->max_ku != p2->max_ku))
+ fatal ("ku definition error for CT_DBYTE2 charset");
+ /* total codepoints in each ku */
+ m = param->max_ten + p2->max_ten;
+ tab = (unsigned short *) param->tab;
+ for (ku = 0; ku < param->max_ku; ku++) {
+ for (ten = 0; ten < param->max_ten; ten++)
+ if ((u = tab[(ku * m) + ten]) != UBOGON)
+ rmap[u] = ((ku + param->base_ku) << 8) + (ten + param->base_ten);
+ for (ten = 0; ten < p2->max_ten; ten++)
+ if ((u = tab[(ku * m) + param->max_ten + ten]) != UBOGON)
+ rmap[u] = ((ku + param->base_ku) << 8) + (ten + p2->base_ten);
+ }
+ break;
+ case CT_SJIS: /* 2 byte Shift-JIS */
+ for (ku = 0; ku < MAX_JIS0208_KU; ku++)
+ for (ten = 0; ten < MAX_JIS0208_TEN; ten++)
+ if ((u = jis0208tab[ku][ten]) != UBOGON) {
+ int sku = ku + BASE_JIS0208_KU;
+ int sten = ten + BASE_JIS0208_TEN;
+ rmap[u] = ((((sku + 1) >> 1) + ((sku < 95) ? 112 : 176)) << 8) +
+ sten + ((sku % 2) ? ((sten > 95) ? 32 : 31) : 126);
+ }
+ /* JIS Roman */
+ rmap[UCS2_YEN] = JISROMAN_YEN;
+ rmap[UCS2_OVERLINE] = JISROMAN_OVERLINE;
+ /* JIS hankaku katakana */
+ for (u = 0; u < (MAX_KANA_8 - MIN_KANA_8); u++)
+ rmap[UCS2_KATAKANA + u] = MIN_KANA_8 + u;
+ break;
+ }
+ /* hack: map NBSP to SP if otherwise no map */
+ if (rmap[0x00a0] == NOCHAR) rmap[0x00a0] = rmap[0x0020];
+ }
+ return rmap; /* return map */
+}
+
+/* Convert UTF-8 sized text to charset using rmap
+ * Accepts: source sized text
+ * conversion rmap
+ * pointer to returned sized text
+ * substitute character if not in rmap, else NIL to return failure
+ * ISO-2022-JP conversion flag
+ * Returns T if successful, NIL if failure
+ *
+ * This routine doesn't try to convert to all possible charsets; in particular
+ * it doesn't support other Unicode encodings or any ISO 2022 other than
+ * ISO-2022-JP.
+ */
+
+long utf8_rmaptext (SIZEDTEXT *text,unsigned short *rmap,SIZEDTEXT *ret,
+ unsigned long errch,long iso2022jp)
+{
+ unsigned long i,u,c;
+ /* get size of buffer */
+ if (i = utf8_rmapsize (text,rmap,errch,iso2022jp)) {
+ unsigned char *s = text->data;
+ unsigned char *t = ret->data = (unsigned char *) fs_get (i);
+ ret->size = i - 1; /* number of octets in destination buffer */
+ /* start non-zero ISO-2022-JP state at 1 */
+ if (iso2022jp) iso2022jp = 1;
+ /* convert string, ignore BOM */
+ for (i = text->size; i;) if ((u = utf8_get (&s,&i)) != UCS2_BOM) {
+ /* substitute error character for NOCHAR */
+ if ((u & U8GM_NONBMP) || ((c = rmap[u]) == NOCHAR)) c = errch;
+ switch (iso2022jp) { /* depends upon ISO 2022 mode */
+ case 0: /* ISO 2022 not in effect */
+ /* two-byte character */
+ if (c > 0xff) *t++ = (unsigned char) (c >> 8);
+ /* single-byte or low-byte of two-byte */
+ *t++ = (unsigned char) (c & 0xff);
+ break;
+ case 1: /* ISO 2022 Roman */
+ /* <ch> */
+ if (c < 0x80) *t++ = (unsigned char) c;
+ else { /* JIS character */
+ *t++ = I2C_ESC; /* ESC $ B <hi> <lo> */
+ *t++ = I2C_MULTI;
+ *t++ = I2CS_94x94_JIS_NEW;
+ *t++ = (unsigned char) (c >> 8) & 0x7f;
+ *t++ = (unsigned char) c & 0x7f;
+ iso2022jp = 2; /* shift to ISO 2022 JIS */
+ }
+ break;
+ case 2: /* ISO 2022 JIS */
+ if (c > 0x7f) { /* <hi> <lo> */
+ *t++ = (unsigned char) (c >> 8) & 0x7f;
+ *t++ = (unsigned char) c & 0x7f;
+ }
+ else { /* ASCII character */
+ *t++ = I2C_ESC; /* ESC ( J <ch> */
+ *t++ = I2C_G0_94;
+ *t++ = I2CS_94_JIS_ROMAN;
+ *t++ = (unsigned char) c;
+ iso2022jp = 1; /* shift to ISO 2022 Roman */
+ }
+ break;
+ }
+ }
+ if (iso2022jp == 2) { /* ISO-2022-JP string must end in Roman */
+ *t++ = I2C_ESC; /* ESC ( J */
+ *t++ = I2C_G0_94;
+ *t++ = I2CS_94_JIS_ROMAN;
+ }
+ *t++ = NIL; /* tie off returned data */
+ return LONGT; /* return success */
+ }
+ ret->data = NIL;
+ ret->size = 0;
+ return NIL; /* failure */
+}
+
+/* Calculate size of convertsion of UTF-8 sized text to charset using rmap
+ * Accepts: source sized text
+ * conversion rmap
+ * pointer to returned sized text
+ * substitute character if not in rmap, else NIL to return failure
+ * ISO-2022-JP conversion flag
+ * Returns size+1 if successful, NIL if failure
+ *
+ * This routine doesn't try to handle to all possible charsets; in particular
+ * it doesn't support other Unicode encodings or any ISO 2022 other than
+ * ISO-2022-JP.
+ */
+
+unsigned long utf8_rmapsize (SIZEDTEXT *text,unsigned short *rmap,
+ unsigned long errch,long iso2022jp)
+{
+ unsigned long i,u,c;
+ unsigned long ret = 1; /* terminating NUL */
+ unsigned char *s = text->data;
+ if (iso2022jp) iso2022jp = 1; /* start non-zero ISO-2022-JP state at 1 */
+ for (i = text->size; i;) if ((u = utf8_get (&s,&i)) != UCS2_BOM) {
+ if ((u & U8GM_NONBMP) || (((c = rmap[u]) == NOCHAR) && !(c = errch)))
+ return NIL; /* not in BMP, or NOCHAR and no err char */
+ switch (iso2022jp) { /* depends upon ISO 2022 mode */
+ case 0: /* ISO 2022 not in effect */
+ ret += (c > 0xff) ? 2 : 1;
+ break;
+ case 1: /* ISO 2022 Roman */
+ if (c < 0x80) ret += 1; /* <ch> */
+ else { /* JIS character */
+ ret += 5; /* ESC $ B <hi> <lo> */
+ iso2022jp = 2; /* shift to ISO 2022 JIS */
+ }
+ break;
+ case 2: /* ISO 2022 JIS */
+ if (c > 0x7f) ret += 2; /* <hi> <lo> */
+ else { /* ASCII character */
+ ret += 4; /* ESC ( J <ch> */
+ iso2022jp = 1; /* shift to ISO 2022 Roman */
+ }
+ break;
+ }
+ }
+ if (iso2022jp == 2) { /* ISO-2022-JP string must end in Roman */
+ ret += 3; /* ESC ( J */
+ iso2022jp = 1; /* reset state to Roman */
+ }
+ return ret;
+}
+
+/* Convert UCS-4 to charset using rmap
+ * Accepts: source UCS-4 character(s)
+ * numver of UCS-4 characters
+ * conversion rmap
+ * pointer to returned sized text
+ * substitute character if not in rmap, else NIL to return failure
+ * Returns T if successful, NIL if failure
+ *
+ * Currently only supports BMP characters, and does not support ISO-2022
+ */
+
+long ucs4_rmaptext (unsigned long *ucs4,unsigned long len,unsigned short *rmap,
+ SIZEDTEXT *ret,unsigned long errch)
+{
+ long size = ucs4_rmaplen (ucs4,len,rmap,errch);
+ return (size >= 0) ? /* build in newly-created buffer */
+ ucs4_rmapbuf (ret->data = (unsigned char *) fs_get ((ret->size = size) +1),
+ ucs4,len,rmap,errch) : NIL;
+}
+
+/* Return size of UCS-4 string converted to other CS via rmap
+ * Accepts: source UCS-4 character(s)
+ * numver of UCS-4 characters
+ * conversion rmap
+ * substitute character if not in rmap, else NIL to return failure
+ * Returns: length if success, negative if failure (no-convert)
+ */
+
+long ucs4_rmaplen (unsigned long *ucs4,unsigned long len,unsigned short *rmap,
+ unsigned long errch)
+{
+ long ret;
+ unsigned long i,u,c;
+ /* count non-BOM characters */
+ for (ret = 0,i = 0; i < len; ++i) if ((u = ucs4[i]) != UCS2_BOM) {
+ if ((u & U8GM_NONBMP) || (((c = rmap[u]) == NOCHAR) && !(c = errch)))
+ return -1; /* not in BMP, or NOCHAR and no err char? */
+ ret += (c > 0xff) ? 2 : 1;
+ }
+ return ret;
+}
+
+
+/* Stuff buffer with UCS-4 string converted to other CS via rmap
+ * Accepts: destination buffer
+ * source UCS-4 character(s)
+ * number of UCS-4 characters
+ * conversion rmap
+ * substitute character if not in rmap, else NIL to return failure
+ * Returns: T, always
+ */
+
+long ucs4_rmapbuf (unsigned char *t,unsigned long *ucs4,unsigned long len,
+ unsigned short *rmap,unsigned long errch)
+{
+ unsigned long i,u,c;
+ /* convert non-BOM characters */
+ for (i = 0; i < len; ++i) if ((u = ucs4[i]) != UCS2_BOM) {
+ /* substitute error character for NOCHAR */
+ if ((u & U8GM_NONBMP) || ((c = rmap[u]) == NOCHAR)) c = errch;
+ /* two-byte character? */
+ if (c > 0xff) *t++ = (unsigned char) (c >> 8);
+ /* single-byte or low-byte of two-byte */
+ *t++ = (unsigned char) (c & 0xff);
+ }
+ *t++ = NIL; /* tie off returned data */
+ return LONGT;
+}
+
+/* Return UCS-4 Unicode character from UTF-8 string
+ * Accepts: pointer to string
+ * remaining octets in string
+ * Returns: UCS-4 character with pointer and count updated
+ * or error code with pointer and count unchanged
+ */
+
+unsigned long utf8_get (unsigned char **s,unsigned long *i)
+{
+ unsigned char *t = *s;
+ unsigned long j = *i;
+ /* decode raw UTF-8 string */
+ unsigned long ret = utf8_get_raw (&t,&j);
+ if (ret & U8G_ERROR); /* invalid raw UTF-8 decoding? */
+ /* no, is it surrogate? */
+ else if ((ret >= UTF16_SURR) && (ret <= UTF16_MAXSURR)) ret = U8G_SURROGA;
+ /* or in non-Unicode ISO 10646 space? */
+ else if (ret > UCS4_MAXUNICODE) ret = U8G_NOTUNIC;
+ else {
+ *s = t; /* all is well, update pointer */
+ *i = j; /* and counter */
+ }
+ return ret; /* return value */
+}
+
+/* Return raw (including non-Unicode) UCS-4 character from UTF-8 string
+ * Accepts: pointer to string
+ * remaining octets in string
+ * Returns: UCS-4 character with pointer and count updated
+ * or error code with pointer and count unchanged
+ */
+
+unsigned long utf8_get_raw (unsigned char **s,unsigned long *i)
+{
+ unsigned char c,c1;
+ unsigned char *t = *s;
+ unsigned long j = *i;
+ unsigned long ret = U8G_NOTUTF8;
+ int more = 0;
+ do { /* make sure have source octets available */
+ if (!j--) return more ? U8G_ENDSTRI : U8G_ENDSTRG;
+ /* UTF-8 continuation? */
+ else if (((c = *t++) > 0x7f) && (c < 0xc0)) {
+ /* continuation when not in progress */
+ if (!more) return U8G_BADCONT;
+ --more; /* found a continuation octet */
+ ret <<= 6; /* shift current value by 6 bits */
+ ret |= c & 0x3f; /* merge continuation octet */
+ }
+ /* incomplete UTF-8 character */
+ else if (more) return U8G_INCMPLT;
+ else { /* start of sequence */
+ c1 = j ? *t : 0xbf; /* assume valid continuation if incomplete */
+ if (c < 0x80) ret = c; /* U+0000 - U+007f */
+ else if (c < 0xc2); /* c0 and c1 never valid */
+ else if (c < 0xe0) { /* U+0080 - U+07ff */
+ if (c &= 0x1f) more = 1;
+ }
+ else if (c < 0xf0) { /* U+0800 - U+ffff */
+ if ((c &= 0x0f) || (c1 >= 0xa0)) more = 2;
+ }
+ else if (c < 0xf8) { /* U+10000 - U+10ffff (and 110000 - 1fffff) */
+ if ((c &= 0x07) || (c1 >= 0x90)) more = 3;
+ }
+ else if (c < 0xfc) { /* ISO 10646 200000 - 3ffffff */
+ if ((c &= 0x03) || (c1 >= 0x88)) more = 4;
+ }
+ else if (c < 0xfe) { /* ISO 10646 4000000 - 7fffffff */
+ if ((c &= 0x01) || (c1 >= 0x84)) more = 5;
+ }
+ /* fe and ff never valid */
+ if (more) { /* multi-octet, make sure more to come */
+ if (!j) return U8G_ENDSTRI;
+ ret = c; /* continuation needed, save start bits */
+ }
+ }
+ } while (more);
+ if (!(ret & U8G_ERROR)) { /* success return? */
+ *s = t; /* yes, update pointer */
+ *i = j; /* and counter */
+ }
+ return ret; /* return value */
+}
+
+/* Return UCS-4 character from named charset string
+ * Accepts: charset
+ * pointer to string
+ * remaining octets in string
+ * Returns: UCS-4 character with pointer and count updated, negative if error
+ *
+ * Error codes are the same as utf8_get().
+ */
+
+unsigned long ucs4_cs_get (CHARSET *cs,unsigned char **s,unsigned long *i)
+{
+ unsigned char c,c1,ku,ten;
+ unsigned long ret,d;
+ unsigned char *t = *s;
+ unsigned long j = *i;
+ struct utf8_eucparam *p1,*p2,*p3;
+ if (j--) c = *t++; /* get first octet */
+ else return U8G_ENDSTRG; /* empty string */
+ switch (cs->type) { /* convert if type known */
+ case CT_UTF8: /* variable UTF-8 encoded Unicode no table */
+ return utf8_get (s,i);
+ case CT_ASCII: /* 7-bit ASCII no table */
+ if (c >= 0x80) return U8G_NOTUTF8;
+ case CT_1BYTE0: /* 1 byte no table */
+ ret = c; /* identity */
+ break;
+ case CT_1BYTE: /* 1 byte ASCII + table 0x80-0xff */
+ ret = (c > 0x80) ? ((unsigned short *) cs->tab)[c & BITS7] : c;
+ break;
+ case CT_1BYTE8: /* 1 byte table 0x00 - 0xff */
+ ret = ((unsigned short *) cs->tab)[c];
+ break;
+
+ case CT_EUC: /* 2 byte ASCII + utf8_eucparam base/CS2/CS3 */
+ if (c & BIT8) {
+ p1 = (struct utf8_eucparam *) cs->tab;
+ p2 = p1 + 1;
+ p3 = p1 + 2;
+ if (j--) c1 = *t++; /* get second octet */
+ else return U8G_ENDSTRI;
+ if (!(c1 & BIT8)) return U8G_NOTUTF8;
+ switch (c) { /* check 8bit code set */
+ case EUC_CS2: /* CS2 */
+ if (p2->base_ku) { /* CS2 set up? */
+ if (p2->base_ten) { /* yes, multibyte? */
+ if (j--) c = *t++; /* get second octet */
+ else return U8G_ENDSTRI;
+ if ((c & BIT8) &&
+ ((ku = (c1 & BITS7) - p2->base_ku) < p2->max_ku) &&
+ ((ten = (c & BITS7) - p2->base_ten) < p2->max_ten)) {
+ ret = ((unsigned short *) p2->tab)[(ku*p2->max_ten) + ten];
+ break;
+ }
+ }
+ else if ((c1 >= p2->base_ku) && (c1 < p2->max_ku)) {
+ ret = c1 + ((unsigned long) p2->tab);
+ break;
+ }
+ }
+ return U8G_NOTUTF8; /* CS2 not set up or bogus */
+ case EUC_CS3: /* CS3 */
+ if (p3->base_ku) { /* CS3 set up? */
+ if (p3->base_ten) { /* yes, multibyte? */
+ if (j--) c = *t++; /* get second octet */
+ else return U8G_ENDSTRI;
+ if ((c & BIT8) &&
+ ((ku = (c1 & BITS7) - p3->base_ku) < p3->max_ku) &&
+ ((ten = (c & BITS7) - p3->base_ten) < p3->max_ten)) {
+ ret = ((unsigned short *) p3->tab)[(ku*p3->max_ten) + ten];
+ break;
+ }
+ }
+ else if ((c1 >= p3->base_ku) && (c1 < p3->max_ku)) {
+ ret = c1 + ((unsigned long) p3->tab);
+ break;
+ }
+ }
+ return U8G_NOTUTF8; /* CS3 not set up or bogus */
+ default:
+ if (((ku = (c & BITS7) - p1->base_ku) >= p1->max_ku) ||
+ ((ten = (c1 & BITS7) - p1->base_ten) >= p1->max_ten))
+ return U8G_NOTUTF8;
+ ret = ((unsigned short *) p1->tab)[(ku*p1->max_ten) + ten];
+ /* special hack for JIS X 0212: merge rows less than 10 */
+ if ((ret == UBOGON) && ku && (ku < 10) && p3->tab && p3->base_ten)
+ ret = ((unsigned short *) p3->tab)
+ [((ku - (p3->base_ku - p1->base_ku))*p3->max_ten) + ten];
+ break;
+ }
+ }
+ else ret = c; /* ASCII character */
+ break;
+
+ case CT_DBYTE: /* 2 byte ASCII + utf8_eucparam */
+ if (c & BIT8) { /* double-byte character? */
+ p1 = (struct utf8_eucparam *) cs->tab;
+ if (j--) c1 = *t++; /* get second octet */
+ else return U8G_ENDSTRI;
+ if (((ku = c - p1->base_ku) < p1->max_ku) &&
+ ((ten = c1 - p1->base_ten) < p1->max_ten))
+ ret = ((unsigned short *) p1->tab)[(ku*p1->max_ten) + ten];
+ else return U8G_NOTUTF8;
+ }
+ else ret = c; /* ASCII character */
+ break;
+ case CT_DBYTE2: /* 2 byte ASCII + utf8_eucparam plane1/2 */
+ if (c & BIT8) { /* double-byte character? */
+ p1 = (struct utf8_eucparam *) cs->tab;
+ p2 = p1 + 1;
+ if (j--) c1 = *t++; /* get second octet */
+ else return U8G_ENDSTRI;
+ if (c1 & BIT8) { /* high vs. low plane */
+ if ((ku = c - p2->base_ku) < p2->max_ku &&
+ ((ten = c1 - p2->base_ten) < p2->max_ten))
+ ret = ((unsigned short *) p1->tab)
+ [(ku*(p1->max_ten + p2->max_ten)) + p1->max_ten + ten];
+ else return U8G_NOTUTF8;
+ }
+ else if ((ku = c - p1->base_ku) < p1->max_ku &&
+ ((ten = c1 - p1->base_ten) < p1->max_ten))
+ ret = ((unsigned short *) p1->tab)
+ [(ku*(p1->max_ten + p2->max_ten)) + ten];
+ else return U8G_NOTUTF8;
+ }
+ else ret = c; /* ASCII character */
+ break;
+ case CT_SJIS: /* 2 byte Shift-JIS encoded JIS no table */
+ /* compromise - do yen sign but not overline */
+ if (!(c & BIT8)) ret = (c == JISROMAN_YEN) ? UCS2_YEN : c;
+ /* half-width katakana? */
+ else if ((c >= MIN_KANA_8) && (c < MAX_KANA_8)) ret = c + KANA_8;
+ else { /* Shift-JIS */
+ if (j--) c1 = *t++; /* get second octet */
+ else return U8G_ENDSTRI;
+ SJISTOJIS (c,c1);
+ c = JISTOUNICODE (c,c1,ku,ten);
+ }
+ break;
+
+ case CT_UCS2: /* 2 byte 16-bit Unicode no table */
+ ret = c << 8;
+ if (j--) c = *t++; /* get second octet */
+ else return U8G_ENDSTRI; /* empty string */
+ ret |= c;
+ break;
+ case CT_UCS4: /* 4 byte 32-bit Unicode no table */
+ if (c & 0x80) return U8G_NOTUTF8;
+ if (j < 3) return U8G_ENDSTRI;
+ j -= 3; /* count three octets */
+ ret = c << 24;
+ ret |= (*t++) << 16;
+ ret |= (*t++) << 8;
+ ret |= (*t++);
+ break;
+ case CT_UTF16: /* variable UTF-16 encoded Unicode no table */
+ ret = c << 8;
+ if (j--) c = *t++; /* get second octet */
+ else return U8G_ENDSTRI; /* empty string */
+ ret |= c;
+ /* surrogate? */
+ if ((ret >= UTF16_SURR) && (ret <= UTF16_MAXSURR)) {
+ /* invalid first surrogate */
+ if ((ret > UTF16_SURRHEND) || (j < 2)) return U8G_NOTUTF8;
+ j -= 2; /* count two octets */
+ d = (*t++) << 8; /* first octet of second surrogate */
+ d |= *t++; /* second octet of second surrogate */
+ if ((d < UTF16_SURRL) || (d > UTF16_SURRLEND)) return U8G_NOTUTF8;
+ ret = UTF16_BASE + ((ret & UTF16_MASK) << UTF16_SHIFT) +
+ (d & UTF16_MASK);
+ }
+ break;
+ default: /* unknown/unsupported character set type */
+ return U8G_NOTUTF8;
+ }
+ *s = t; /* update pointer and counter */
+ *i = j;
+ return ret;
+}
+
+/* Produce charset validity map for BMP
+ * Accepts: list of charsets to map
+ * Returns: validity map, indexed by BMP codepoint
+ *
+ * Bit 0x1 is the "not-CJK" character bit
+ */
+
+unsigned long *utf8_csvalidmap (char *charsets[])
+{
+ unsigned short u,*tab;
+ unsigned int m,ku,ten;
+ unsigned long i,csi,csb;
+ struct utf8_eucparam *param,*p2;
+ char *s;
+ const CHARSET *cs;
+ unsigned long *ret = (unsigned long *)
+ fs_get (i = 0x10000 * sizeof (unsigned long));
+ memset (ret,0,i); /* zero the entire vector */
+ /* mark all the non-CJK codepoints */
+ /* U+0000 - U+2E7F non-CJK */
+ for (i = 0; i < 0x2E7F; ++i) ret[i] = 0x1;
+ /* U+2E80 - U+2EFF CJK Radicals Supplement
+ * U+2F00 - U+2FDF Kangxi Radicals
+ * U+2FE0 - U+2FEF unassigned
+ * U+2FF0 - U+2FFF Ideographic Description Characters
+ * U+3000 - U+303F CJK Symbols and Punctuation
+ * U+3040 - U+309F Hiragana
+ * U+30A0 - U+30FF Katakana
+ * U+3100 - U+312F BoPoMoFo
+ * U+3130 - U+318F Hangul Compatibility Jamo
+ * U+3190 - U+319F Kanbun
+ * U+31A0 - U+31BF BoPoMoFo Extended
+ * U+31C0 - U+31EF CJK Strokes
+ * U+31F0 - U+31FF Katakana Phonetic Extensions
+ * U+3200 - U+32FF Enclosed CJK Letters and Months
+ * U+3300 - U+33FF CJK Compatibility
+ * U+3400 - U+4DBF CJK Unified Ideographs Extension A
+ * U+4DC0 - U+4DFF Yijing Hexagram Symbols
+ * U+4E00 - U+9FFF CJK Unified Ideographs
+ * U+A000 - U+A48F Yi Syllables
+ * U+A490 - U+A4CF Yi Radicals
+ * U+A700 - U+A71F Modifier Tone Letters
+ */
+ for (i = 0xa720; i < 0xabff; ++i) ret[i] = 0x1;
+ /* U+AC00 - U+D7FF Hangul Syllables */
+ for (i = 0xd800; i < 0xf8ff; ++i) ret[i] = 0x1;
+ /* U+F900 - U+FAFF CJK Compatibility Ideographs */
+ for (i = 0xfb00; i < 0xfe2f; ++i) ret[i] = 0x1;
+ /* U+FE30 - U+FE4F CJK Compatibility Forms
+ * U+FE50 - U+FE6F Small Form Variants (for CNS 11643)
+ */
+ for (i = 0xfe70; i < 0xfeff; ++i) ret[i] = 0x1;
+ /* U+FF00 - U+FFEF CJK Compatibility Ideographs */
+ for (i = 0xfff0; i < 0x10000; ++i) ret[i] = 0x1;
+
+ /* for each supplied charset */
+ for (csi = 1; ret && charsets && (s = charsets[csi - 1]); ++csi) {
+ /* substitute EUC-JP for ISO-2022-JP */
+ if (!compare_cstring (s,"ISO-2022-JP")) s = "EUC-JP";
+ /* look up charset */
+ if (cs = utf8_charset (s)) {
+ csb = 1 << csi; /* charset bit */
+ switch (cs->type) {
+ case CT_ASCII: /* 7-bit ASCII no table */
+ case CT_1BYTE0: /* 1 byte no table */
+ case CT_1BYTE: /* 1 byte ASCII + table 0x80-0xff */
+ case CT_1BYTE8: /* 1 byte table 0x00 - 0xff */
+ case CT_EUC: /* 2 byte ASCII + utf8_eucparam base/CS2/CS3 */
+ case CT_DBYTE: /* 2 byte ASCII + utf8_eucparam */
+ case CT_DBYTE2: /* 2 byte ASCII + utf8_eucparam plane1/2 */
+ case CT_SJIS: /* 2 byte Shift-JIS */
+ /* supported charset type, all ASCII is OK */
+ for (i = 0; i < 128; ++i) ret[i] |= csb;
+ break;
+ default: /* unsupported charset type */
+ fs_give ((void **) &ret);
+ break;
+ }
+ /* now do additional operations */
+ if (ret) switch (cs->type) {
+ case CT_1BYTE0: /* 1 byte no table */
+ for (i = 128; i < 256; i++) ret[i] |= csb;
+ break;
+ case CT_1BYTE: /* 1 byte ASCII + table 0x80-0xff */
+ for (tab = (unsigned short *) cs->tab,i = 128; i < 256; i++)
+ if (tab[i & BITS7] != UBOGON) ret[tab[i & BITS7]] |= csb;
+ break;
+ case CT_1BYTE8: /* 1 byte table 0x00 - 0xff */
+ for (tab = (unsigned short *) cs->tab,i = 0; i < 256; i++)
+ if (tab[i] != UBOGON) ret[tab[i]] |= csb;
+ break;
+ case CT_EUC: /* 2 byte ASCII + utf8_eucparam base/CS2/CS3 */
+ for (param = (struct utf8_eucparam *) cs->tab,
+ tab = (unsigned short *) param->tab, ku = 0;
+ ku < param->max_ku; ku++)
+ for (ten = 0; ten < param->max_ten; ten++)
+ if ((u = tab[(ku * param->max_ten) + ten]) != UBOGON)
+ ret[u] |= csb;
+ break;
+
+ case CT_DBYTE: /* 2 byte ASCII + utf8_eucparam */
+ for (param = (struct utf8_eucparam *) cs->tab,
+ tab = (unsigned short *) param->tab, ku = 0;
+ ku < param->max_ku; ku++)
+ for (ten = 0; ten < param->max_ten; ten++)
+ if ((u = tab[(ku * param->max_ten) + ten]) != UBOGON)
+ ret[u] |= csb;
+ break;
+ case CT_DBYTE2: /* 2 byte ASCII + utf8_eucparam plane1/2 */
+ param = (struct utf8_eucparam *) cs->tab;
+ p2 = param + 1; /* plane 2 parameters */
+ /* only ten parameters should differ */
+ if ((param->base_ku != p2->base_ku) || (param->max_ku != p2->max_ku))
+ fatal ("ku definition error for CT_DBYTE2 charset");
+ /* total codepoints in each ku */
+ m = param->max_ten + p2->max_ten;
+ tab = (unsigned short *) param->tab;
+ for (ku = 0; ku < param->max_ku; ku++) {
+ for (ten = 0; ten < param->max_ten; ten++)
+ if ((u = tab[(ku * m) + ten]) != UBOGON)
+ ret[u] |= csb;
+ for (ten = 0; ten < p2->max_ten; ten++)
+ if ((u = tab[(ku * m) + param->max_ten + ten]) != UBOGON)
+ ret[u] |= csb;
+ }
+ break;
+ case CT_SJIS: /* 2 byte Shift-JIS */
+ for (ku = 0; ku < MAX_JIS0208_KU; ku++)
+ for (ten = 0; ten < MAX_JIS0208_TEN; ten++)
+ if ((u = jis0208tab[ku][ten]) != UBOGON) ret[u] |= csb;
+ /* JIS hankaku katakana */
+ for (u = 0; u < (MAX_KANA_8 - MIN_KANA_8); u++)
+ ret[UCS2_KATAKANA + u] |= csb;
+ break;
+ }
+ }
+ /* invalid charset, punt */
+ else fs_give ((void **) &ret);
+ }
+ return ret;
+}
+
+/* Infer charset from unlabelled sized text
+ * Accepts: sized text
+ * Returns: charset if one inferred, or NIL if unknown
+ */
+
+const CHARSET *utf8_infercharset (SIZEDTEXT *src)
+{
+ long iso2022jp = NIL;
+ long eightbit = NIL;
+ unsigned long i;
+ /* look for ISO 2022 */
+ if (src) for (i = 0; i < src->size; i++) {
+ /* ESC sequence? */
+ if ((src->data[i] == I2C_ESC) && (++i < src->size)) switch (src->data[i]) {
+ case I2C_MULTI: /* yes, multibyte? */
+ if (++i < src->size) switch (src->data[i]) {
+ case I2CS_94x94_JIS_OLD: /* JIS X 0208-1978 */
+ case I2CS_94x94_JIS_NEW: /* JIS X 0208-1983 */
+ case I2CS_94x94_JIS_EXT: /* JIS X 0212-1990 (kludge...) */
+ iso2022jp = T; /* found an ISO-2022-JP sequence */
+ break;
+ default: /* other multibyte */
+ return NIL; /* definitely invalid */
+ }
+ break;
+ case I2C_G0_94: /* single byte */
+ if (++i < src->size) switch (src->data[i]) {
+ case I2CS_94_JIS_BUGROM: /* in case old buggy software */
+ case I2CS_94_JIS_ROMAN: /* JIS X 0201-1976 left half */
+ case I2CS_94_ASCII: /* ASCII */
+ case I2CS_94_BRITISH: /* good enough for gov't work */
+ break;
+ default: /* other 94 single byte */
+ return NIL; /* definitely invalid */
+ }
+ }
+ /* if possible UTF-8 and not ISO-2022-JP */
+ else if (!iso2022jp && (eightbit >= 0) && (src->data[i] & BIT8) &&
+ (eightbit = utf8_validate (src->data + i,src->size - i)) > 0)
+ i += eightbit - 1; /* skip past all but last of UTF-8 char */
+ }
+ /* ISO-2022-JP overrides other guesses */
+ if (iso2022jp) return utf8_charset ("ISO-2022-JP");
+ if (eightbit > 0) return utf8_charset ("UTF-8");
+ return eightbit ? NIL : utf8_charset ("US-ASCII");
+}
+
+
+/* Validate that character at this position is UTF-8
+ * Accepts: string pointer
+ * size of remaining string
+ * Returns: size of UTF-8 character in octets or -1 if not UTF-8
+ */
+
+long utf8_validate (unsigned char *s,unsigned long i)
+{
+ unsigned long j = i;
+ return (utf8_get (&s,&i) & U8G_ERROR) ? -1 : j - i;
+}
+
+/* Convert ISO 8859-1 to UTF-8
+ * Accepts: source sized text
+ * pointer to return sized text
+ * canonicalization function
+ */
+
+void utf8_text_1byte0 (SIZEDTEXT *text,SIZEDTEXT *ret,ucs4cn_t cv,ucs4de_t de)
+{
+ unsigned long i;
+ unsigned char *s;
+ unsigned int c;
+ for (ret->size = i = 0; i < text->size;) {
+ c = text->data[i++];
+ UTF8_COUNT_BMP (ret->size,c,cv,de)
+ }
+ (s = ret->data = (unsigned char *) fs_get (ret->size + 1))[ret->size] =NIL;
+ for (i = 0; i < text->size;) {
+ c = text->data[i++];
+ UTF8_WRITE_BMP (s,c,cv,de) /* convert UCS-2 to UTF-8 */
+ }
+}
+
+
+/* Convert single byte ASCII+8bit character set sized text to UTF-8
+ * Accepts: source sized text
+ * pointer to return sized text
+ * conversion table
+ * canonicalization function
+ */
+
+void utf8_text_1byte (SIZEDTEXT *text,SIZEDTEXT *ret,void *tab,ucs4cn_t cv,
+ ucs4de_t de)
+{
+ unsigned long i;
+ unsigned char *s;
+ unsigned int c;
+ unsigned short *tbl = (unsigned short *) tab;
+ for (ret->size = i = 0; i < text->size;) {
+ if ((c = text->data[i++]) & BIT8) c = tbl[c & BITS7];
+ UTF8_COUNT_BMP (ret->size,c,cv,de)
+ }
+ (s = ret->data = (unsigned char *) fs_get (ret->size + 1))[ret->size] =NIL;
+ for (i = 0; i < text->size;) {
+ if ((c = text->data[i++]) & BIT8) c = tbl[c & BITS7];
+ UTF8_WRITE_BMP (s,c,cv,de) /* convert UCS-2 to UTF-8 */
+ }
+}
+
+/* Convert single byte 8bit character set sized text to UTF-8
+ * Accepts: source sized text
+ * pointer to return sized text
+ * conversion table
+ * canonicalization function
+ */
+
+void utf8_text_1byte8 (SIZEDTEXT *text,SIZEDTEXT *ret,void *tab,ucs4cn_t cv,
+ ucs4de_t de)
+{
+ unsigned long i;
+ unsigned char *s;
+ unsigned int c;
+ unsigned short *tbl = (unsigned short *) tab;
+ for (ret->size = i = 0; i < text->size;) {
+ c = tbl[text->data[i++]];
+ UTF8_COUNT_BMP (ret->size,c,cv,de)
+ }
+ (s = ret->data = (unsigned char *) fs_get (ret->size + 1))[ret->size] =NIL;
+ for (i = 0; i < text->size;) {
+ c = tbl[text->data[i++]];
+ UTF8_WRITE_BMP (s,c,cv,de) /* convert UCS-2 to UTF-8 */
+ }
+}
+
+/* Convert EUC sized text to UTF-8
+ * Accepts: source sized text
+ * pointer to return sized text
+ * EUC parameter table
+ * canonicalization function
+ */
+
+void utf8_text_euc (SIZEDTEXT *text,SIZEDTEXT *ret,void *tab,ucs4cn_t cv,
+ ucs4de_t de)
+{
+ unsigned long i;
+ unsigned char *s;
+ unsigned int pass,c,c1,ku,ten;
+ struct utf8_eucparam *p1 = (struct utf8_eucparam *) tab;
+ struct utf8_eucparam *p2 = p1 + 1;
+ struct utf8_eucparam *p3 = p1 + 2;
+ unsigned short *t1 = (unsigned short *) p1->tab;
+ unsigned short *t2 = (unsigned short *) p2->tab;
+ unsigned short *t3 = (unsigned short *) p3->tab;
+ for (pass = 0,s = NIL,ret->size = 0; pass <= 1; pass++) {
+ for (i = 0; i < text->size;) {
+ /* not CS0? */
+ if ((c = text->data[i++]) & BIT8) {
+ /* yes, must have another high byte */
+ if ((i >= text->size) || !((c1 = text->data[i++]) & BIT8))
+ c = UBOGON; /* out of space or bogon */
+ else switch (c) { /* check 8bit code set */
+ case EUC_CS2: /* CS2 */
+ if (p2->base_ku) { /* CS2 set up? */
+ if (p2->base_ten) /* yes, multibyte? */
+ c = ((i < text->size) && ((c = text->data[i++]) & BIT8) &&
+ ((ku = (c1 & BITS7) - p2->base_ku) < p2->max_ku) &&
+ ((ten = (c & BITS7) - p2->base_ten) < p2->max_ten)) ?
+ t2[(ku*p2->max_ten) + ten] : UBOGON;
+ else c = ((c1 >= p2->base_ku) && (c1 < p2->max_ku)) ?
+ c1 + ((unsigned long) p2->tab) : UBOGON;
+ }
+ else { /* CS2 not set up */
+ c = UBOGON; /* swallow byte, say bogon */
+ if (i < text->size) i++;
+ }
+ break;
+ case EUC_CS3: /* CS3 */
+ if (p3->base_ku) { /* CS3 set up? */
+ if (p3->base_ten) /* yes, multibyte? */
+ c = ((i < text->size) && ((c = text->data[i++]) & BIT8) &&
+ ((ku = (c1 & BITS7) - p3->base_ku) < p3->max_ku) &&
+ ((ten = (c & BITS7) - p3->base_ten) < p3->max_ten)) ?
+ t3[(ku*p3->max_ten) + ten] : UBOGON;
+ else c = ((c1 >= p3->base_ku) && (c1 < p3->max_ku)) ?
+ c1 + ((unsigned long) p3->tab) : UBOGON;
+ }
+ else { /* CS3 not set up */
+ c = UBOGON; /* swallow byte, say bogon */
+ if (i < text->size) i++;
+ }
+ break;
+
+ default:
+ if (((ku = (c & BITS7) - p1->base_ku) >= p1->max_ku) ||
+ ((ten = (c1 & BITS7) - p1->base_ten) >= p1->max_ten)) c = UBOGON;
+ else if (((c = t1[(ku*p1->max_ten) + ten]) == UBOGON) &&
+ /* special hack for JIS X 0212: merge rows less than 10 */
+ ku && (ku < 10) && t3 && p3->base_ten)
+ c = t3[((ku - (p3->base_ku - p1->base_ku))*p3->max_ten) + ten];
+ }
+ }
+ /* convert if second pass */
+ if (pass) UTF8_WRITE_BMP (s,c,cv,de)
+ else UTF8_COUNT_BMP (ret->size,c,cv,de);
+ }
+ if (!pass) (s = ret->data = (unsigned char *)
+ fs_get (ret->size + 1))[ret->size] =NIL;
+ }
+}
+
+
+/* Convert ASCII + double-byte sized text to UTF-8
+ * Accepts: source sized text
+ * pointer to return sized text
+ * conversion table
+ * canonicalization function
+ */
+
+void utf8_text_dbyte (SIZEDTEXT *text,SIZEDTEXT *ret,void *tab,ucs4cn_t cv,
+ ucs4de_t de)
+{
+ unsigned long i;
+ unsigned char *s;
+ unsigned int c,c1,ku,ten;
+ struct utf8_eucparam *p1 = (struct utf8_eucparam *) tab;
+ unsigned short *t1 = (unsigned short *) p1->tab;
+ for (ret->size = i = 0; i < text->size;) {
+ if ((c = text->data[i++]) & BIT8) {
+ /* special hack for GBK: 0x80 is Euro */
+ if ((c == 0x80) && (t1 == (unsigned short *) gb2312tab)) c = UCS2_EURO;
+ else c = ((i < text->size) && (c1 = text->data[i++]) &&
+ ((ku = c - p1->base_ku) < p1->max_ku) &&
+ ((ten = c1 - p1->base_ten) < p1->max_ten)) ?
+ t1[(ku*p1->max_ten) + ten] : UBOGON;
+ }
+ UTF8_COUNT_BMP (ret->size,c,cv,de)
+ }
+ (s = ret->data = (unsigned char *) fs_get (ret->size + 1))[ret->size] = NIL;
+ for (i = 0; i < text->size;) {
+ if ((c = text->data[i++]) & BIT8) {
+ /* special hack for GBK: 0x80 is Euro */
+ if ((c == 0x80) && (t1 == (unsigned short *) gb2312tab)) c = UCS2_EURO;
+ else c = ((i < text->size) && (c1 = text->data[i++]) &&
+ ((ku = c - p1->base_ku) < p1->max_ku) &&
+ ((ten = c1 - p1->base_ten) < p1->max_ten)) ?
+ t1[(ku*p1->max_ten) + ten] : UBOGON;
+ }
+ UTF8_WRITE_BMP (s,c,cv,de) /* convert UCS-2 to UTF-8 */
+ }
+}
+
+/* Convert ASCII + double byte 2 plane sized text to UTF-8
+ * Accepts: source sized text
+ * pointer to return sized text
+ * conversion table
+ * canonicalization function
+ */
+
+void utf8_text_dbyte2 (SIZEDTEXT *text,SIZEDTEXT *ret,void *tab,ucs4cn_t cv,
+ ucs4de_t de)
+{
+ unsigned long i;
+ unsigned char *s;
+ unsigned int c,c1,ku,ten;
+ struct utf8_eucparam *p1 = (struct utf8_eucparam *) tab;
+ struct utf8_eucparam *p2 = p1 + 1;
+ unsigned short *t = (unsigned short *) p1->tab;
+ for (ret->size = i = 0; i < text->size;) {
+ if ((c = text->data[i++]) & BIT8) {
+ if ((i >= text->size) || !(c1 = text->data[i++]))
+ c = UBOGON; /* out of space or bogon */
+ else if (c1 & BIT8) /* high vs. low plane */
+ c = ((ku = c - p2->base_ku) < p2->max_ku &&
+ ((ten = c1 - p2->base_ten) < p2->max_ten)) ?
+ t[(ku*(p1->max_ten + p2->max_ten)) + p1->max_ten + ten] :UBOGON;
+ else c = ((ku = c - p1->base_ku) < p1->max_ku &&
+ ((ten = c1 - p1->base_ten) < p1->max_ten)) ?
+ t[(ku*(p1->max_ten + p2->max_ten)) + ten] : UBOGON;
+ }
+ UTF8_COUNT_BMP (ret->size,c,cv,de)
+ }
+ (s = ret->data = (unsigned char *) fs_get (ret->size + 1))[ret->size] = NIL;
+ for (i = 0; i < text->size;) {
+ if ((c = text->data[i++]) & BIT8) {
+ if ((i >= text->size) || !(c1 = text->data[i++]))
+ c = UBOGON; /* out of space or bogon */
+ else if (c1 & BIT8) /* high vs. low plane */
+ c = ((ku = c - p2->base_ku) < p2->max_ku &&
+ ((ten = c1 - p2->base_ten) < p2->max_ten)) ?
+ t[(ku*(p1->max_ten + p2->max_ten)) + p1->max_ten + ten] :UBOGON;
+ else c = ((ku = c - p1->base_ku) < p1->max_ku &&
+ ((ten = c1 - p1->base_ten) < p1->max_ten)) ?
+ t[(ku*(p1->max_ten + p2->max_ten)) + ten] : UBOGON;
+ }
+ UTF8_WRITE_BMP (s,c,cv,de) /* convert UCS-2 to UTF-8 */
+ }
+}
+
+#ifdef JISTOUNICODE /* Japanese */
+/* Convert Shift JIS sized text to UTF-8
+ * Accepts: source sized text
+ * pointer to return sized text
+ * canonicalization function
+ */
+
+void utf8_text_sjis (SIZEDTEXT *text,SIZEDTEXT *ret,ucs4cn_t cv,
+ ucs4de_t de)
+{
+ unsigned long i;
+ unsigned char *s;
+ unsigned int c,c1,ku,ten;
+ for (ret->size = i = 0; i < text->size;) {
+ if ((c = text->data[i++]) & BIT8) {
+ /* half-width katakana */
+ if ((c >= MIN_KANA_8) && (c < MAX_KANA_8)) c += KANA_8;
+ else if (i >= text->size) c = UBOGON;
+ else { /* Shift-JIS */
+ c1 = text->data[i++];
+ SJISTOJIS (c,c1);
+ c = JISTOUNICODE (c,c1,ku,ten);
+ }
+ }
+ /* compromise - do yen sign but not overline */
+ else if (c == JISROMAN_YEN) c = UCS2_YEN;
+ UTF8_COUNT_BMP (ret->size,c,cv,de)
+ }
+ (s = ret->data = (unsigned char *) fs_get (ret->size + 1))[ret->size] = NIL;
+ for (i = 0; i < text->size;) {
+ if ((c = text->data[i++]) & BIT8) {
+ /* half-width katakana */
+ if ((c >= MIN_KANA_8) && (c < MAX_KANA_8)) c += KANA_8;
+ else { /* Shift-JIS */
+ c1 = text->data[i++];
+ SJISTOJIS (c,c1);
+ c = JISTOUNICODE (c,c1,ku,ten);
+ }
+ }
+ /* compromise - do yen sign but not overline */
+ else if (c == JISROMAN_YEN) c = UCS2_YEN;
+ UTF8_WRITE_BMP (s,c,cv,de) /* convert UCS-2 to UTF-8 */
+ }
+}
+#endif
+
+/* Convert ISO-2022 sized text to UTF-8
+ * Accepts: source sized text
+ * pointer to returned sized text
+ * canonicalization function
+ */
+
+void utf8_text_2022 (SIZEDTEXT *text,SIZEDTEXT *ret,ucs4cn_t cv,ucs4de_t de)
+{
+ unsigned long i;
+ unsigned char *s;
+ unsigned int pass,state,c,co,gi,gl,gr,g[4],ku,ten;
+ for (pass = 0,s = NIL,ret->size = 0; pass <= 1; pass++) {
+ gi = 0; /* quell compiler warnings */
+ state = I2S_CHAR; /* initialize engine */
+ g[0]= g[2] = I2CS_ASCII; /* G0 and G2 are ASCII */
+ g[1]= g[3] = I2CS_ISO8859_1;/* G1 and G3 are ISO-8850-1 */
+ gl = I2C_G0; gr = I2C_G1; /* left is G0, right is G1 */
+ for (i = 0; i < text->size;) {
+ c = text->data[i++];
+ switch (state) { /* dispatch based upon engine state */
+ case I2S_ESC: /* ESC seen */
+ switch (c) { /* process intermediate character */
+ case I2C_MULTI: /* multibyte character? */
+ state = I2S_MUL; /* mark multibyte flag seen */
+ break;
+ case I2C_SS2: /* single shift GL to G2 */
+ case I2C_SS2_ALT: /* Taiwan SeedNet */
+ gl |= I2C_SG2;
+ break;
+ case I2C_SS3: /* single shift GL to G3 */
+ case I2C_SS3_ALT: /* Taiwan SeedNet */
+ gl |= I2C_SG3;
+ break;
+ case I2C_LS2: /* shift GL to G2 */
+ gl = I2C_G2;
+ break;
+ case I2C_LS3: /* shift GL to G3 */
+ gl = I2C_G3;
+ break;
+ case I2C_LS1R: /* shift GR to G1 */
+ gr = I2C_G1;
+ break;
+ case I2C_LS2R: /* shift GR to G2 */
+ gr = I2C_G2;
+ break;
+ case I2C_LS3R: /* shift GR to G3 */
+ gr = I2C_G3;
+ break;
+ case I2C_G0_94: case I2C_G1_94: case I2C_G2_94: case I2C_G3_94:
+ g[gi = c - I2C_G0_94] = (state == I2S_MUL) ? I2CS_94x94 : I2CS_94;
+ state = I2S_INT; /* ready for character set */
+ break;
+ case I2C_G0_96: case I2C_G1_96: case I2C_G2_96: case I2C_G3_96:
+ g[gi = c - I2C_G0_96] = (state == I2S_MUL) ? I2CS_96x96 : I2CS_96;
+ state = I2S_INT; /* ready for character set */
+ break;
+ default: /* bogon */
+ if (pass) *s++ = I2C_ESC,*s++ = c;
+ else ret->size += 2;
+ state = I2S_CHAR; /* return to previous state */
+ }
+ break;
+
+ case I2S_MUL: /* ESC $ */
+ switch (c) { /* process multibyte intermediate character */
+ case I2C_G0_94: case I2C_G1_94: case I2C_G2_94: case I2C_G3_94:
+ g[gi = c - I2C_G0_94] = I2CS_94x94;
+ state = I2S_INT; /* ready for character set */
+ break;
+ case I2C_G0_96: case I2C_G1_96: case I2C_G2_96: case I2C_G3_96:
+ g[gi = c - I2C_G0_96] = I2CS_96x96;
+ state = I2S_INT; /* ready for character set */
+ break;
+ default: /* probably omitted I2CS_94x94 */
+ g[gi = I2C_G0] = I2CS_94x94 | c;
+ state = I2S_CHAR; /* return to character state */
+ }
+ break;
+ case I2S_INT:
+ state = I2S_CHAR; /* return to character state */
+ g[gi] |= c; /* set character set */
+ break;
+
+ case I2S_CHAR: /* character data */
+ switch (c) {
+ case I2C_ESC: /* ESC character */
+ state = I2S_ESC; /* see if ISO-2022 prefix */
+ break;
+ case I2C_SI: /* shift GL to G0 */
+ gl = I2C_G0;
+ break;
+ case I2C_SO: /* shift GL to G1 */
+ gl = I2C_G1;
+ break;
+ case I2C_SS2_ALT: /* single shift GL to G2 */
+ case I2C_SS2_ALT_7:
+ gl |= I2C_SG2;
+ break;
+ case I2C_SS3_ALT: /* single shift GL to G3 */
+ case I2C_SS3_ALT_7:
+ gl |= I2C_SG3;
+ break;
+
+ default: /* ordinary character */
+ co = c; /* note original character */
+ if (gl & (3 << 2)) { /* single shifted? */
+ gi = g[gl >> 2]; /* get shifted character set */
+ gl &= 0x3; /* cancel shift */
+ }
+ /* select left or right half */
+ else gi = (c & BIT8) ? g[gr] : g[gl];
+ c &= BITS7; /* make 7-bit */
+ switch (gi) { /* interpret in character set */
+ case I2CS_ASCII: /* ASCII */
+ break; /* easy! */
+ case I2CS_BRITISH: /* British ASCII */
+ /* Pound sterling sign */
+ if (c == BRITISH_POUNDSTERLING) c = UCS2_POUNDSTERLING;
+ break;
+ case I2CS_JIS_ROMAN: /* JIS Roman */
+ case I2CS_JIS_BUGROM: /* old bugs */
+ switch (c) { /* two exceptions to ASCII */
+ case JISROMAN_YEN: /* Yen sign */
+ c = UCS2_YEN;
+ break;
+ /* overline */
+ case JISROMAN_OVERLINE:
+ c = UCS2_OVERLINE;
+ break;
+ }
+ break;
+ case I2CS_JIS_KANA: /* JIS hankaku katakana */
+ if ((c >= MIN_KANA_7) && (c < MAX_KANA_7)) c += KANA_7;
+ break;
+
+ case I2CS_ISO8859_1: /* Latin-1 (West European) */
+ c |= BIT8; /* just turn on high bit */
+ break;
+ case I2CS_ISO8859_2: /* Latin-2 (Czech, Slovak) */
+ c = iso8859_2tab[c];
+ break;
+ case I2CS_ISO8859_3: /* Latin-3 (Dutch, Turkish) */
+ c = iso8859_3tab[c];
+ break;
+ case I2CS_ISO8859_4: /* Latin-4 (Scandinavian) */
+ c = iso8859_4tab[c];
+ break;
+ case I2CS_ISO8859_5: /* Cyrillic */
+ c = iso8859_5tab[c];
+ break;
+ case I2CS_ISO8859_6: /* Arabic */
+ c = iso8859_6tab[c];
+ break;
+ case I2CS_ISO8859_7: /* Greek */
+ c = iso8859_7tab[c];
+ break;
+ case I2CS_ISO8859_8: /* Hebrew */
+ c = iso8859_8tab[c];
+ break;
+ case I2CS_ISO8859_9: /* Latin-5 (Finnish, Portuguese) */
+ c = iso8859_9tab[c];
+ break;
+ case I2CS_TIS620: /* Thai */
+ c = tis620tab[c];
+ break;
+ case I2CS_ISO8859_10: /* Latin-6 (Northern Europe) */
+ c = iso8859_10tab[c];
+ break;
+ case I2CS_ISO8859_13: /* Latin-7 (Baltic) */
+ c = iso8859_13tab[c];
+ break;
+ case I2CS_VSCII: /* Vietnamese */
+ c = visciitab[c];
+ break;
+ case I2CS_ISO8859_14: /* Latin-8 (Celtic) */
+ c = iso8859_14tab[c];
+ break;
+ case I2CS_ISO8859_15: /* Latin-9 (Euro) */
+ c = iso8859_15tab[c];
+ break;
+ case I2CS_ISO8859_16: /* Latin-10 (Baltic) */
+ c = iso8859_16tab[c];
+ break;
+
+ default: /* all other character sets */
+ /* multibyte character set */
+ if ((gi & I2CS_MUL) && !(c & BIT8) && isgraph (c)) {
+ c = (i < text->size) ? text->data[i++] : 0;
+ switch (gi) {
+#ifdef GBTOUNICODE
+ case I2CS_GB: /* GB 2312 */
+ co |= BIT8; /* make into EUC */
+ c |= BIT8;
+ c = GBTOUNICODE (co,c,ku,ten);
+ break;
+#endif
+#ifdef JISTOUNICODE
+ case I2CS_JIS_OLD:/* JIS X 0208-1978 */
+ case I2CS_JIS_NEW:/* JIS X 0208-1983 */
+ c = JISTOUNICODE (co,c,ku,ten);
+ break;
+#endif
+#ifdef JIS0212TOUNICODE
+ case I2CS_JIS_EXT:/* JIS X 0212-1990 */
+ c = JIS0212TOUNICODE (co,c,ku,ten);
+ break;
+#endif
+#ifdef KSCTOUNICODE
+ case I2CS_KSC: /* KSC 5601 */
+ co |= BIT8; /* make into EUC */
+ c |= BIT8;
+ c = KSCTOUNICODE (co,c,ku,ten);
+ break;
+#endif
+#ifdef CNS1TOUNICODE
+ case I2CS_CNS1: /* CNS 11643 plane 1 */
+ c = CNS1TOUNICODE (co,c,ku,ten);
+ break;
+#endif
+#ifdef CNS2TOUNICODE
+ case I2CS_CNS2: /* CNS 11643 plane 2 */
+ c = CNS2TOUNICODE (co,c,ku,ten);
+ break;
+#endif
+#ifdef CNS3TOUNICODE
+ case I2CS_CNS3: /* CNS 11643 plane 3 */
+ c = CNS3TOUNICODE (co,c,ku,ten);
+ break;
+#endif
+#ifdef CNS4TOUNICODE
+ case I2CS_CNS4: /* CNS 11643 plane 4 */
+ c = CNS4TOUNICODE (co,c,ku,ten);
+ break;
+#endif
+#ifdef CNS5TOUNICODE
+ case I2CS_CNS5: /* CNS 11643 plane 5 */
+ c = CNS5TOUNICODE (co,c,ku,ten);
+ break;
+#endif
+#ifdef CNS6TOUNICODE
+ case I2CS_CNS6: /* CNS 11643 plane 6 */
+ c = CNS6TOUNICODE (co,c,ku,ten);
+ break;
+#endif
+#ifdef CNS7TOUNICODE
+ case I2CS_CNS7: /* CNS 11643 plane 7 */
+ c = CNS7TOUNICODE (co,c,ku,ten);
+ break;
+#endif
+ default: /* unknown multibyte, treat as UCS-2 */
+ c |= (co << 8); /* wrong, but nothing else to do */
+ break;
+ }
+ }
+ else c = co; /* unknown single byte, treat as 8859-1 */
+ }
+ /* convert if second pass */
+ if (pass) UTF8_WRITE_BMP (s,c,cv,de)
+ else UTF8_COUNT_BMP (ret->size,c,cv,de);
+ }
+ }
+ }
+ if (!pass) (s = ret->data = (unsigned char *)
+ fs_get (ret->size + 1))[ret->size] = NIL;
+ else if (((unsigned long) (s - ret->data)) != ret->size)
+ fatal ("ISO-2022 to UTF-8 botch");
+ }
+}
+
+/* Convert UTF-7 sized text to UTF-8
+ * Accepts: source sized text
+ * pointer to returned sized text
+ * canonicalization function
+ */
+
+void utf8_text_utf7 (SIZEDTEXT *text,SIZEDTEXT *ret,ucs4cn_t cv,ucs4de_t de)
+{
+ unsigned long i;
+ unsigned char *s;
+ unsigned int c,c1,d,uc,pass,e,e1,state,surrh;
+ for (pass = 0,s = NIL,ret->size = 0; pass <= 1; pass++) {
+ c1 = d = uc = e = e1 = 0;
+ for (i = 0,state = NIL; i < text->size;) {
+ c = text->data[i++]; /* get next byte */
+ switch (state) {
+ case U7_PLUS: /* previous character was + */
+ if (c == '-') { /* +- means textual + */
+ c = '+';
+ state = U7_ASCII; /* revert to ASCII */
+ break;
+ }
+ state = U7_UNICODE; /* enter Unicode state */
+ e = e1 = 0; /* initialize Unicode quantum position */
+ case U7_UNICODE: /* Unicode state */
+ if (c == '-') state = U7_MINUS;
+ else { /* decode Unicode */
+ /* don't use isupper/islower since this is ASCII only */
+ if ((c >= 'A') && (c <= 'Z')) c -= 'A';
+ else if ((c >= 'a') && (c <= 'z')) c -= 'a' - 26;
+ else if (isdigit (c)) c -= '0' - 52;
+ else if (c == '+') c = 62;
+ else if (c == '/') c = 63;
+ else state = U7_ASCII;/* end of modified BASE64 */
+ }
+ break;
+ case U7_MINUS: /* previous character was absorbed - */
+ state = U7_ASCII; /* revert to ASCII */
+ case U7_ASCII: /* ASCII state */
+ if (c == '+') state = U7_PLUS;
+ break;
+ }
+
+ switch (state) { /* store character if in character mode */
+ case U7_UNICODE: /* Unicode */
+ switch (e++) { /* install based on BASE64 state */
+ case 0:
+ c1 = c << 2; /* byte 1: high 6 bits */
+ break;
+ case 1:
+ d = c1 | (c >> 4); /* byte 1: low 2 bits */
+ c1 = c << 4; /* byte 2: high 4 bits */
+ break;
+ case 2:
+ d = c1 | (c >> 2); /* byte 2: low 4 bits */
+ c1 = c << 6; /* byte 3: high 2 bits */
+ break;
+ case 3:
+ d = c | c1; /* byte 3: low 6 bits */
+ e = 0; /* reinitialize mechanism */
+ break;
+ }
+ if (e == 1) break; /* done if first BASE64 state */
+ if (!e1) { /* first byte of UCS-2 character */
+ uc = (d & 0xff) << 8; /* note first byte */
+ e1 = T; /* enter second UCS-2 state */
+ break; /* done */
+ }
+ c = uc | (d & 0xff); /* build UCS-2 character */
+ e1 = NIL; /* back to first UCS-2 state, drop in */
+ /* surrogate pair? */
+ if ((c >= UTF16_SURR) && (c <= UTF16_MAXSURR)) {
+ /* save high surrogate for later */
+ if (c < UTF16_SURRL) surrh = c;
+ else c = UTF16_BASE + ((surrh & UTF16_MASK) << UTF16_SHIFT) +
+ (c & UTF16_MASK);
+ break; /* either way with surrogates, we're done */
+ }
+ case U7_ASCII: /* just install if ASCII */
+ /* convert if second pass */
+ if (pass) UTF8_WRITE_BMP (s,c,cv,de)
+ else UTF8_COUNT_BMP (ret->size,c,cv,de);
+ }
+ }
+ if (!pass) (s = ret->data = (unsigned char *)
+ fs_get (ret->size + 1))[ret->size] = NIL;
+ else if (((unsigned long) (s - ret->data)) != ret->size)
+ fatal ("UTF-7 to UTF-8 botch");
+ }
+}
+
+
+/* Convert UTF-8 sized text to UTF-8
+ * Accepts: source sized text
+ * pointer to returned sized text
+ * canonicalization function
+ */
+
+void utf8_text_utf8 (SIZEDTEXT *text,SIZEDTEXT *ret,ucs4cn_t cv,ucs4de_t de)
+{
+ unsigned long i,c;
+ unsigned char *s,*t;
+ for (ret->size = 0, t = text->data, i = text->size; i;) {
+ if ((c = utf8_get (&t,&i)) & U8G_ERROR) {
+ ret->data = text->data; /* conversion failed */
+ ret->size = text->size;
+ return;
+ }
+ UTF8_COUNT (ret->size,c,cv,de)
+ }
+ (s = ret->data = (unsigned char *) fs_get (ret->size + 1))[ret->size] =NIL;
+ for (t = text->data, i = text->size; i;) {
+ c = utf8_get (&t,&i);
+ UTF8_WRITE (s,c,cv,de) /* convert UCS-4 to UTF-8 */
+ }
+ if (((unsigned long) (s - ret->data)) != ret->size)
+ fatal ("UTF-8 to UTF-8 botch");
+}
+
+/* Convert UCS-2 sized text to UTF-8
+ * Accepts: source sized text
+ * pointer to returned sized text
+ * canonicalization function
+ */
+
+void utf8_text_ucs2 (SIZEDTEXT *text,SIZEDTEXT *ret,ucs4cn_t cv,ucs4de_t de)
+{
+ unsigned long i;
+ unsigned char *s,*t;
+ unsigned int c;
+ for (ret->size = 0, t = text->data, i = text->size / 2; i; --i) {
+ c = *t++ << 8;
+ c |= *t++;
+ UTF8_COUNT_BMP (ret->size,c,cv,de);
+ }
+ (s = ret->data = (unsigned char *) fs_get (ret->size + 1))[ret->size] = NIL;
+ for (t = text->data, i = text->size / 2; i; --i) {
+ c = *t++ << 8;
+ c |= *t++;
+ UTF8_WRITE_BMP (s,c,cv,de) /* convert UCS-2 to UTF-8 */
+ }
+ if (((unsigned long) (s - ret->data)) != ret->size)
+ fatal ("UCS-2 to UTF-8 botch");
+}
+
+
+/* Convert UCS-4 sized text to UTF-8
+ * Accepts: source sized text
+ * pointer to returned sized text
+ * canonicalization function
+ */
+
+void utf8_text_ucs4 (SIZEDTEXT *text,SIZEDTEXT *ret,ucs4cn_t cv,ucs4de_t de)
+{
+ unsigned long i;
+ unsigned char *s,*t;
+ unsigned long c;
+ for (ret->size = 0, t = text->data, i = text->size / 4; i; --i) {
+ c = *t++ << 24; c |= *t++ << 16; c |= *t++ << 8; c |= *t++;
+ UTF8_COUNT (ret->size,c,cv,de);
+ }
+ (s = ret->data = (unsigned char *) fs_get (ret->size + 1))[ret->size] = NIL;
+ for (t = text->data, i = text->size / 2; i; --i) {
+ c = *t++ << 24; c |= *t++ << 16; c |= *t++ << 8; c |= *t++;
+ UTF8_WRITE (s,c,cv,de) /* convert UCS-4 to UTF-8 */
+ }
+ if (((unsigned long) (s - ret->data)) != ret->size)
+ fatal ("UCS-4 to UTF-8 botch");
+}
+
+/* Convert UTF-16 sized text to UTF-8
+ * Accepts: source sized text
+ * pointer to returned sized text
+ * canonicalization function
+ */
+
+void utf8_text_utf16 (SIZEDTEXT *text,SIZEDTEXT *ret,ucs4cn_t cv,ucs4de_t de)
+{
+ unsigned long i;
+ unsigned char *s,*t;
+ unsigned long c,d;
+ for (ret->size = 0, t = text->data, i = text->size / 2; i; --i) {
+ c = *t++ << 8;
+ c |= *t++;
+ /* possible surrogate? */
+ if ((c >= UTF16_SURR) && (c <= UTF16_MAXSURR)) {
+ /* invalid first surrogate */
+ if ((c > UTF16_SURRHEND) || !i) c = UBOGON;
+ else { /* get second surrogate */
+ d = *t++ << 8;
+ d |= *t++;
+ --i; /* swallowed another 16-bits */
+ /* invalid second surrogate */
+ if ((d < UTF16_SURRL) || (d > UTF16_SURRLEND)) c = UBOGON;
+ else c = UTF16_BASE + ((c & UTF16_MASK) << UTF16_SHIFT) +
+ (d & UTF16_MASK);
+ }
+ }
+ UTF8_COUNT (ret->size,c,cv,de);
+ }
+ (s = ret->data = (unsigned char *) fs_get (ret->size + 1))[ret->size] = NIL;
+ for (t = text->data, i = text->size / 2; i; --i) {
+ c = *t++ << 8;
+ c |= *t++;
+ /* possible surrogate? */
+ if ((c >= UTF16_SURR) && (c <= UTF16_MAXSURR)) {
+ /* invalid first surrogate */
+ if ((c > UTF16_SURRHEND) || !i) c = UBOGON;
+ else { /* get second surrogate */
+ d = *t++ << 8;
+ d |= *t++;
+ --i; /* swallowed another 16-bits */
+ /* invalid second surrogate */
+ if ((d < UTF16_SURRL) || (d > UTF16_SURRLEND)) c = UBOGON;
+ else c = UTF16_BASE + ((c & UTF16_MASK) << UTF16_SHIFT) +
+ (d & UTF16_MASK);
+ }
+ }
+ UTF8_WRITE (s,c,cv,de) /* convert UCS-4 to UTF-8 */
+ }
+ if (((unsigned long) (s - ret->data)) != ret->size)
+ fatal ("UTF-16 to UTF-8 botch");
+}
+
+/* Size of UCS-4 character, possibly not in BMP, as UTF-8 octets
+ * Accepts: character
+ * Returns: size (0 means bogon)
+ *
+ * Use UTF8_SIZE macro if known to be in the BMP
+ */
+
+unsigned long utf8_size (unsigned long c)
+{
+ if (c < 0x80) return 1;
+ else if (c < 0x800) return 2;
+ else if (c < 0x10000) return 3;
+ else if (c < 0x200000) return 4;
+ else if (c < 0x4000000) return 5;
+ else if (c < 0x80000000) return 6;
+ return 0;
+}
+
+
+/* Put UCS-4 character, possibly not in BMP, as UTF-8 octets
+ * Accepts: destination string pointer
+ * character
+ * Returns: updated destination pointer
+ *
+ * Use UTF8_PUT_BMP macro if known to be in the BMP
+ */
+
+unsigned char *utf8_put (unsigned char *s,unsigned long c)
+{
+ unsigned char mark[6] = {0x00,0xc0,0xe0,0xf0,0xf8,0xfc};
+ unsigned long size = utf8_size (c);
+ switch (size) {
+ case 6:
+ s[5] = 0x80 | (unsigned char) (c & 0x3f);
+ c >>= 6;
+ case 5:
+ s[4] = 0x80 | (unsigned char) (c & 0x3f);
+ c >>= 6;
+ case 4:
+ s[3] = 0x80 | (unsigned char) (c & 0x3f);
+ c >>= 6;
+ case 3:
+ s[2] = 0x80 | (unsigned char) (c & 0x3f);
+ c >>= 6;
+ case 2:
+ s[1] = 0x80 | (unsigned char) (c & 0x3f);
+ c >>= 6;
+ case 1:
+ *s = mark[size-1] | (unsigned char) (c & 0x7f);
+ break;
+ }
+ return s + size;
+}
+
+/* Return title case of a fixed-width UCS-4 character
+ * Accepts: character
+ * Returns: title case of character
+ */
+
+unsigned long ucs4_titlecase (unsigned long c)
+{
+ if (c <= UCS4_TMAPMAX) return ucs4_tmaptab[c];
+ if (c < UCS4_TMAPHIMIN) return c;
+ if (c <= UCS4_TMAPHIMAX) return c - UCS4_TMAPHIMAP;
+ if (c < UCS4_TMAPDESERETMIN) return c;
+ if (c <= UCS4_TMAPDESERETMAX) return c - UCS4_TMAPDESERETMAP;
+ return c;
+}
+
+
+/* Return width of a fixed-width UCS-4 character in planes 0-2
+ * Accepts: character
+ * Returns: width (0, 1, 2) or negative error condition if not valid
+ */
+
+long ucs4_width (unsigned long c)
+{
+ long ret;
+ /* out of range, not-a-char, or surrogates */
+ if ((c > UCS4_MAXUNICODE) || ((c & 0xfffe) == 0xfffe) ||
+ ((c >= UTF16_SURR) && (c <= UTF16_MAXSURR))) ret = U4W_NOTUNCD;
+ /* private-use */
+ else if (c >= UCS4_PVTBASE) ret = U4W_PRIVATE;
+ /* SSP are not printing characters */
+ else if (c >= UCS4_SSPBASE) ret = U4W_SSPCHAR;
+ /* unassigned planes */
+ else if (c >= UCS4_UNABASE) ret = U4W_UNASSGN;
+ /* SIP and reserved plane 3 are wide */
+ else if (c >= UCS4_SIPBASE) ret = 2;
+#if (UCS4_WIDLEN != UCS4_SIPBASE)
+#error "UCS4_WIDLEN != UCS4_SIPBASE"
+#endif
+ /* C0/C1 controls */
+ else if ((c <= UCS2_C0CONTROLEND) ||
+ ((c >= UCS2_C1CONTROL) && (c <= UCS2_C1CONTROLEND)))
+ ret = U4W_CONTROL;
+ /* BMP and SMP get value from table */
+ else switch (ret = (ucs4_widthtab[(c >> 2)] >> ((3 - (c & 0x3)) << 1)) &0x3){
+ case 0: /* zero-width */
+ if (c == 0x00ad) ret = 1; /* force U+00ad (SOFT HYPHEN) to width 1 */
+ case 1: /* single-width */
+ case 2: /* double-width */
+ break;
+ case 3: /* ambiguous width */
+ ret = (c >= 0x2100) ? 2 : 1;/* need to do something better than this */
+ break;
+ }
+ return ret;
+}
+
+/* Return screen width of UTF-8 string
+ * Accepts: string
+ * Returns: width or negative if not valid UTF-8
+ */
+
+long utf8_strwidth (unsigned char *s)
+{
+ unsigned long c,i,ret;
+ /* go through string */
+ for (ret = 0; *s; ret += ucs4_width (c)) {
+ /* It's alright to give a fake value for the byte count to utf8_get()
+ * since the null of a null-terminated string will stop processing anyway.
+ */
+ i = 6; /* fake value */
+ if ((c = utf8_get (&s,&i)) & U8G_ERROR) return -1;
+ }
+ return ret;
+}
+
+
+/* Return screen width of UTF-8 text
+ * Accepts: SIZEDTEXT to string
+ * Returns: width or negative if not valid UTF-8
+ */
+
+long utf8_textwidth (SIZEDTEXT *utf8)
+{
+ unsigned long c;
+ unsigned char *s = utf8->data;
+ unsigned long i = utf8->size;
+ unsigned long ret = 0;
+ while (i) { /* while there's a string to process */
+ if ((c = utf8_get (&s,&i)) & U8G_ERROR) return -1;
+ ret += ucs4_width (c);
+ }
+ return ret;
+}
+
+/* Decomposition (phew!) */
+
+#define MORESINGLE 1 /* single UCS-4 tail value */
+#define MOREMULTIPLE 2 /* multiple UCS-2 tail values */
+
+struct decomposemore {
+ short type; /* type of more */
+ union {
+ unsigned long single; /* single decomposed value */
+ struct { /* multiple BMP values */
+ unsigned short *next;
+ unsigned long count;
+ } multiple;
+ } data;
+};
+
+#define RECURSIVEMORE struct recursivemore
+
+RECURSIVEMORE {
+ struct decomposemore *more;
+ RECURSIVEMORE *next;
+};
+
+
+/* Return decomposition of a UCS-4 character
+ * Accepts: character or U8G_ERROR to return next from "more"
+ * pointer to returned more
+ * Returns: [next] decomposed value, more set if still more decomposition
+ */
+
+unsigned long ucs4_decompose (unsigned long c,void **more)
+{
+ unsigned long i,ix,ret;
+ struct decomposemore *m;
+ if (c & U8G_ERROR) { /* want to chase more? */
+ /* do sanity check */
+ if (m = (struct decomposemore *) *more) switch (m->type) {
+ case MORESINGLE: /* single value */
+ ret = m->data.single;
+ fs_give (more); /* no more decomposition */
+ break;
+ case MOREMULTIPLE: /* multiple value */
+ ret = *m->data.multiple.next++;
+ if (!--m->data.multiple.count) fs_give (more);
+ break;
+ default: /* uh-oh */
+ fatal ("invalid more block argument to ucs4_decompose!");
+ }
+ else fatal ("no more block provided to ucs4_decompose!");
+ }
+
+ else { /* start decomposition */
+ *more = NIL; /* initially set no more */
+ /* BMP low decompositions */
+ if (c < UCS4_BMPLOMIN) ret = c;
+ /* fix this someday */
+ else if (c == UCS4_BMPLOMIN) ret = ucs4_dbmplotab[0];
+ else if (c <= UCS4_BMPLOMAX) {
+ /* within range - have a decomposition? */
+ if (i = ucs4_dbmploixtab[c - UCS4_BMPLOMIN]) {
+ /* get first value of decomposition */
+ ret = ucs4_dbmplotab[ix = i & UCS4_BMPLOIXMASK];
+ /* has continuation? */
+ if (i & UCS4_BMPLOSIZEMASK) {
+ m = (struct decomposemore *)
+ (*more = memset (fs_get (sizeof (struct decomposemore)),0,
+ sizeof (struct decomposemore)));
+ m->type = MOREMULTIPLE;
+ m->data.multiple.next = &ucs4_dbmplotab[++ix];
+ m->data.multiple.count = i >> UCS4_BMPLOSIZESHIFT;
+ }
+ }
+ else ret = c; /* in range but doesn't decompose */
+ }
+ /* BMP CJK compatibility */
+ else if (c < UCS4_BMPCJKMIN) ret = c;
+ else if (c <= UCS4_BMPCJKMAX) {
+ if (!(ret = ucs4_bmpcjk1decomptab[c - UCS4_BMPCJKMIN])) ret = c;
+ }
+ /* BMP CJK compatibility - some not in BMP */
+#if UCS4_BMPCJK2MIN - (UCS4_BMPCJKMAX + 1)
+ else if (c < UCS4_BMPCJK2MIN) ret = c;
+#endif
+ else if (c <= UCS4_BMPCJK2MAX)
+ ret = ucs4_bmpcjk2decomptab[c - UCS4_BMPCJK2MIN];
+ /* BMP high decompositions */
+ else if (c < UCS4_BMPHIMIN) ret = c;
+ else if (c <= UCS4_BMPHIMAX) {
+ /* within range - have a decomposition? */
+ if (i = ucs4_dbmphiixtab[c - UCS4_BMPHIMIN]) {
+ /* get first value of decomposition */
+ ret = ucs4_dbmphitab[ix = i & UCS4_BMPHIIXMASK];
+ /* has continuation? */
+ if (i & UCS4_BMPHISIZEMASK) {
+ m = (struct decomposemore *)
+ (*more = memset (fs_get (sizeof (struct decomposemore)),0,
+ sizeof (struct decomposemore)));
+ m->type = MOREMULTIPLE;
+ m->data.multiple.next = &ucs4_dbmphitab[++ix];
+ m->data.multiple.count = i >> UCS4_BMPHISIZESHIFT;
+ }
+ }
+ else ret = c; /* in range but doesn't decompose */
+ }
+
+ /* BMP half and full width forms */
+ else if (c < UCS4_BMPHALFFULLMIN) ret = c;
+ else if (c <= UCS4_BMPHALFFULLMAX) {
+ if (!(ret = ucs4_bmphalffulldecomptab[c - UCS4_BMPHALFFULLMIN])) ret = c;
+ }
+ /* SMP music */
+ else if (c < UCS4_SMPMUSIC1MIN) ret = c;
+ else if (c <= UCS4_SMPMUSIC1MAX) {
+ ret = ucs4_smpmusic1decomptab[c -= UCS4_SMPMUSIC1MIN][0];
+ m = (struct decomposemore *)
+ (*more = memset (fs_get (sizeof (struct decomposemore)),0,
+ sizeof (struct decomposemore)));
+ m->type = MORESINGLE;
+ m->data.single = ucs4_smpmusic1decomptab[c][1];
+ }
+ else if (c < UCS4_SMPMUSIC2MIN) ret = c;
+ else if (c <= UCS4_SMPMUSIC2MAX) {
+ ret = ucs4_smpmusic2decomptab[c -= UCS4_SMPMUSIC2MIN][0];
+ m = (struct decomposemore *)
+ (*more = memset (fs_get (sizeof (struct decomposemore)),0,
+ sizeof (struct decomposemore)));
+ m->type = MORESINGLE;
+ m->data.single = ucs4_smpmusic2decomptab[c][1];
+ }
+ /* SMP mathematical forms */
+ else if (c < UCS4_SMPMATHMIN) ret = c;
+ else if (c <= UCS4_SMPMATHMAX) {
+ if (!(ret = ucs4_smpmathdecomptab[c - UCS4_SMPMATHMIN])) ret = c;
+ }
+ /* CJK compatibility ideographs in SIP */
+ else if (!(ret = ((c >= UCS4_SIPMIN) && (c <= UCS4_SIPMAX)) ?
+ ucs4_sipdecomptab[c - UCS4_SIPMIN] : c)) ret = c;
+ }
+ return ret;
+}
+
+/* Return recursive decomposition of a UCS-4 character
+ * Accepts: character or U8G_ERROR to return next from "more"
+ * pointer to returned more
+ * Returns: [next] decomposed value, more set if still more decomposition
+ */
+
+unsigned long ucs4_decompose_recursive (unsigned long c,void **more)
+{
+ unsigned long c1;
+ void *m,*mn;
+ RECURSIVEMORE *mr;
+ if (c & U8G_ERROR) { /* want to chase more? */
+ mn = NIL;
+ if (mr = (RECURSIVEMORE *) *more) switch (mr->more->type) {
+ case MORESINGLE: /* decompose single value */
+ c = ucs4_decompose_recursive (mr->more->data.single,&mn);
+ *more = mr->next; /* done with this more, remove it */
+ fs_give ((void **) &mr->more);
+ fs_give ((void **) &mr);
+ break;
+ case MOREMULTIPLE: /* decompose current value in multiple */
+ c = ucs4_decompose_recursive (*mr->more->data.multiple.next++,&mn);
+ /* if done with this multiple decomposition */
+ if (!--mr->more->data.multiple.count) {
+ *more = mr->next; /* done with this more, remove it */
+ fs_give ((void **) &mr->more);
+ fs_give ((void **) &mr);
+ }
+ break;
+ default: /* uh-oh */
+ fatal ("invalid more block argument to ucs4_decompose_recursive!");
+ }
+ else fatal ("no more block provided to ucs4_decompose_recursive!");
+ if (mr = mn) { /* did this value recurse on us? */
+ mr->next = *more; /* yes, insert new more at head */
+ *more = mr;
+ }
+ }
+ else { /* start decomposition */
+ *more = NIL; /* initially set no more */
+ mr = NIL;
+ do { /* repeatedly decompose this codepoint */
+ c = ucs4_decompose (c1 = c,&m);
+ if (m) { /* multi-byte decomposition */
+ if (c1 == c) fatal ("endless multiple decomposition!");
+ /* create a block to stash this more */
+ mr = memset (fs_get (sizeof (RECURSIVEMORE)),0,sizeof (RECURSIVEMORE));
+ mr->more = m; /* note the expansion */
+ mr->next = *more; /* old list is the tail */
+ *more = mr; /* and this is the new head */
+ }
+ } while (c1 != c); /* until nothing more to decompose */
+ }
+ return c;
+}
diff --git a/imap/src/c-client/utf8.h b/imap/src/c-client/utf8.h
new file mode 100644
index 00000000..105f856d
--- /dev/null
+++ b/imap/src/c-client/utf8.h
@@ -0,0 +1,584 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UTF-8 routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 June 1997
+ * Last Edited: 17 January 2008
+ */
+
+/* UTF-8 size and conversion routines from UCS-2 values (thus in the BMP).
+ * Don't use these if UTF-16 data (surrogate pairs) are an issue.
+ * For UCS-4 values, use the utf8_size() and utf8_put() functions.
+ */
+
+#define UTF8_SIZE_BMP(c) ((c & 0xff80) ? ((c & 0xf800) ? 3 : 2) : 1)
+
+#define UTF8_PUT_BMP(b,c) { \
+ if (c & 0xff80) { /* non-ASCII? */ \
+ if (c & 0xf800) { /* three byte code */ \
+ *b++ = 0xe0 | (c >> 12); \
+ *b++ = 0x80 | ((c >> 6) & 0x3f); \
+ } \
+ else *b++ = 0xc0 | ((c >> 6) & 0x3f); \
+ *b++ = 0x80 | (c & 0x3f); \
+ } \
+ else *b++ = c; \
+}
+
+/* utf8_text() flag values */
+
+#define U8T_CASECANON 2 /* canonicalize case */
+#define U8T_DECOMPOSE 4 /* decompose */
+ /* full canonicalization */
+#define U8T_CANONICAL (U8T_CASECANON | U8T_DECOMPOSE)
+
+
+/* utf8_get() return values */
+
+ /* 0x0000 - 0xffff BMP plane */
+#define U8GM_NONBMP 0xffff0000 /* mask for non-BMP values */
+ /* 0x10000 - 0x10ffff extended planes */
+ /* 0x110000 - 0x7ffffff non-Unicode */
+#define U8G_ERROR 0x80000000 /* error flag */
+#define U8G_BADCONT U8G_ERROR+1 /* continuation when not in progress */
+#define U8G_INCMPLT U8G_ERROR+2 /* incomplete UTF-8 character */
+#define U8G_NOTUTF8 U8G_ERROR+3 /* not a valid UTF-8 octet */
+#define U8G_ENDSTRG U8G_ERROR+4 /* end of string */
+#define U8G_ENDSTRI U8G_ERROR+5 /* end of string w/ incomplete UTF-8 char */
+#define U8G_SURROGA U8G_ERROR+6 /* surrogate codepoint */
+#define U8G_NOTUNIC U8G_ERROR+7 /* non-Unicode codepoint */
+
+
+/* ucs4_width() return values */
+
+#define U4W_ERROR 0x80000000 /* error flags */
+#define U4W_NOTUNCD U4W_ERROR+1 /* not a Unicode char */
+#define U4W_PRIVATE U4W_ERROR+2 /* private-space plane */
+#define U4W_SSPCHAR U4W_ERROR+3 /* Supplementary Special-purpose Plane */
+#define U4W_UNASSGN U4W_ERROR+4 /* unassigned space plane */
+#define U4W_CONTROL U4W_ERROR+5 /* C0/C1 control */
+#define U4W_CTLSRGT U4W_CONTROL /* in case legacy code references this */
+
+/* ISO-2022 engine states */
+
+#define I2S_CHAR 0 /* character */
+#define I2S_ESC 1 /* previous character was ESC */
+#define I2S_MUL 2 /* previous character was multi-byte code */
+#define I2S_INT 3 /* previous character was intermediate */
+
+
+/* ISO-2022 Gn selections */
+
+#define I2C_G0 0 /* G0 */
+#define I2C_G1 1 /* G1 */
+#define I2C_G2 2 /* G2 */
+#define I2C_G3 3 /* G3 */
+#define I2C_SG2 (2 << 2) /* single shift G2 */
+#define I2C_SG3 (3 << 2) /* single shift G2 */
+
+
+/* ISO-2022 octet definitions */
+
+#define I2C_ESC 0x1b /* ESCape */
+
+ /* Intermediate character */
+#define I2C_STRUCTURE 0x20 /* announce code structure */
+#define I2C_C0 0x21 /* C0 */
+#define I2C_C1 0x22 /* C1 */
+#define I2C_CONTROL 0x23 /* single control function */
+#define I2C_MULTI 0x24 /* multi-byte character set */
+#define I2C_OTHER 0x25 /* other coding system */
+#define I2C_REVISED 0x26 /* revised registration */
+#define I2C_G0_94 0x28 /* G0 94-character set */
+#define I2C_G1_94 0x29 /* G1 94-character set */
+#define I2C_G2_94 0x2A /* G2 94-character set */
+#define I2C_G3_94 0x2B /* G3 94-character set */
+#define I2C_G0_96 0x2C /* (not in ISO-2022) G0 96-character set */
+#define I2C_G1_96 0x2D /* G1 96-character set */
+#define I2C_G2_96 0x2E /* G2 96-character set */
+#define I2C_G3_96 0x2F /* G3 96-character set */
+
+ /* Locking shifts */
+#define I2C_SI 0x0f /* lock shift to G0 (Shift In) */
+#define I2C_SO 0x0e /* lock shift to G1 (Shift Out) */
+ /* prefixed by ESC */
+#define I2C_LS2 0x6e /* lock shift to G2 */
+#define I2C_LS3 0x6f /* lock shift to G3 */
+#define I2C_LS1R 0x7e /* lock shift GR to G1 */
+#define I2C_LS2R 0x7d /* lock shift GR to G2 */
+#define I2C_LS3R 0x7c /* lock shift GR to G3 */
+
+ /* Single shifts */
+#define I2C_SS2_ALT 0x8e /* single shift to G2 (SS2) */
+#define I2C_SS3_ALT 0x8f /* single shift to G3 (SS3) */
+#define I2C_SS2_ALT_7 0x19 /* single shift to G2 (SS2) */
+#define I2C_SS3_ALT_7 0x1d /* single shift to G3 (SS3) */
+ /* prefixed by ESC */
+#define I2C_SS2 0x4e /* single shift to G2 (SS2) */
+#define I2C_SS3 0x4f /* single shift to G3 (SS3) */
+
+/* 94 character sets */
+
+ /* 4/0 ISO 646 IRV */
+#define I2CS_94_BRITISH 0x41 /* 4/1 ISO 646 British */
+#define I2CS_94_ASCII 0x42 /* 4/2 ISO 646 USA (ASCII) */
+ /* 4/3 NATS Finland/Sweden (primary) */
+ /* 4/4 NATS Finland/Sweden (secondary) */
+ /* 4/5 NATS Denmark/Norway (primary) */
+ /* 4/6 NATS Denmark/Norway (secondary) */
+ /* 4/7 ISO 646 Swedish SEN 850200 */
+ /* 4/8 ISO 646 Swedish names */
+#define I2CS_94_JIS_BUGROM 0x48 /* 4/8 some buggy software does this */
+#define I2CS_94_JIS_KANA 0x49 /* 4/9 JIS X 0201-1976 right half */
+#define I2CS_94_JIS_ROMAN 0x4a /* 4/a JIS X 0201-1976 left half */
+ /* 4/b ISO 646 German */
+ /* 4/c ISO 646 Portuguese (Olivetti) */
+ /* 4/d ISO 6438 African */
+ /* 4/e ISO 5427 Cyrillic (Honeywell-Bull) */
+ /* 4/f DIN 31624 extended bibliography */
+ /* 5/0 ISO 5426-1980 Bibliography */
+ /* 5/1 ISO 5427-1981 Cyrillic*/
+ /* 5/2 ISO 646 French (withdrawn) */
+ /* 5/3 ISO 5428-1980 Greek bibliography */
+ /* 5/4 GB 1988-80 Chinese */
+ /* 5/5 Latin-Greek (Honeywell-Bull) */
+ /* 5/6 UK Viewdata/Teletext */
+ /* 5/7 INIS (IRV subset) */
+ /* 5/8 ISO 5428 Greek Bibliography */
+ /* 5/9 ISO 646 Italian (Olivetti) */
+ /* 5/a ISO 646 Spanish (Olivetti) */
+ /* 5/b Greek (Olivetti) */
+ /* 5/c Latin-Greek (Olivetti) */
+ /* 5/d INIS non-standard extension */
+ /* 5/e INIS Cyrillic extension */
+ /* 5/f Arabic CODAR-U IERA */
+ /* 6/0 ISO 646 Norwegian */
+ /* 6/1 Norwegian version 2 (withdrawn) */
+ /* 6/2 Videotex supplementary */
+ /* 6/3 Videotex supplementary #2 */
+ /* 6/4 Videotex supplementary #3 */
+ /* 6/5 APL */
+ /* 6/6 ISO 646 French */
+ /* 6/7 ISO 646 Portuguese (IBM) */
+ /* 6/8 ISO 646 Spanish (IBM) */
+ /* 6/9 ISO 646 Hungarian */
+ /* 6/a Greek ELOT (withdrawn) */
+ /* 6/b ISO 9036 Arabic 7-bit */
+ /* 6/c ISO 646 IRV supplementary set */
+ /* 6/d JIS C6229-1984 OCR-A */
+ /* 6/e JIS C6229-1984 OCR-B */
+ /* 6/f JIS C6229-1984 OCR-B additional */
+ /* 7/0 JIS C6229-1984 hand-printed */
+ /* 7/1 JIS C6229-1984 additional hand-printd */
+ /* 7/2 JIS C6229-1984 katakana hand-printed */
+ /* 7/3 E13B Japanese graphic */
+ /* 7/4 Supplementary Videotex (withdrawn) */
+ /* 7/5 Teletex primary CCITT T.61 */
+ /* 7/6 Teletex secondary CCITT T.61 */
+ /* 7/7 CSA Z 243.4-1985 Alternate primary #1 */
+ /* 7/8 CSA Z 243.4-1985 Alternate primary #2 */
+ /* 7/9 Mosaic CCITT T.101 */
+ /* 7/a Serbocroatian/Slovenian Latin */
+ /* 7/b Serbocroatian Cyrillic */
+ /* 7/c Supplementary CCITT T.101 */
+ /* 7/d Macedonian Cyrillic */
+
+/* 94 character sets - second intermediate byte */
+
+ /* 4/0 Greek primary CCITT */
+ /* 4/1 Cuba */
+ /* 4/2 ISO/IEC 646 invariant */
+ /* 4/3 Irish Gaelic 7-bit */
+ /* 4/4 Turkmen */
+
+
+/* 94x94 character sets */
+
+#define I2CS_94x94_JIS_OLD 0x40 /* 4/0 JIS X 0208-1978 */
+#define I2CS_94x94_GB 0x41 /* 4/1 GB 2312 */
+#define I2CS_94x94_JIS_NEW 0x42 /* 4/2 JIS X 0208-1983 */
+#define I2CS_94x94_KSC 0x43 /* 4/3 KSC 5601 */
+#define I2CS_94x94_JIS_EXT 0x44 /* 4/4 JIS X 0212-1990 */
+ /* 4/5 CCITT Chinese */
+ /* 4/6 Blisssymbol Graphic */
+#define I2CS_94x94_CNS1 0x47 /* 4/7 CNS 11643 plane 1 */
+#define I2CS_94x94_CNS2 0x48 /* 4/8 CNS 11643 plane 2 */
+#define I2CS_94x94_CNS3 0x49 /* 4/9 CNS 11643 plane 3 */
+#define I2CS_94x94_CNS4 0x4a /* 4/a CNS 11643 plane 4 */
+#define I2CS_94x94_CNS5 0x4b /* 4/b CNS 11643 plane 5 */
+#define I2CS_94x94_CNS6 0x4c /* 4/c CNS 11643 plane 6 */
+#define I2CS_94x94_CNS7 0x4d /* 4/d CNS 11643 plane 7 */
+ /* 4/e DPRK (North Korea) KGCII */
+ /* 4/f JGCII plane 1 */
+ /* 5/0 JGCII plane 2 */
+
+/* 96 character sets */
+
+#define I2CS_96_ISO8859_1 0x41 /* 4/1 Latin-1 (Western Europe) */
+#define I2CS_96_ISO8859_2 0x42 /* 4/2 Latin-2 (Czech, Slovak) */
+#define I2CS_96_ISO8859_3 0x43 /* 4/3 Latin-3 (Dutch, Turkish) */
+#define I2CS_96_ISO8859_4 0x44 /* 4/4 Latin-4 (Scandinavian) */
+ /* 4/5 CSA Z 243.4-1985 */
+#define I2CS_96_ISO8859_7 0x46 /* 4/6 Greek */
+#define I2CS_96_ISO8859_6 0x47 /* 4/7 Arabic */
+#define I2CS_96_ISO8859_8 0x48 /* 4/8 Hebrew */
+ /* 4/9 Czechoslovak CSN 369103 */
+ /* 4/a Supplementary Latin and non-alpha */
+ /* 4/b Technical */
+#define I2CS_96_ISO8859_5 0x4c /* 4/c Cyrillic */
+#define I2CS_96_ISO8859_9 0x4d /* 4/d Latin-5 (Finnish, Portuguese) */
+ /* 4/e ISO 6937-2 residual */
+ /* 4/f Basic Cyrillic */
+ /* 5/0 Supplementary Latin 1, 2 and 5 */
+ /* 5/1 Basic Box */
+ /* 5/2 Supplementary ISO/IEC 6937 : 1992 */
+ /* 5/3 CCITT Hebrew supplementary */
+#define I2CS_96_TIS620 0x54 /* 5/4 TIS 620 */
+ /* 5/5 Arabic/French/German */
+#define I2CS_96_ISO8859_10 0x56 /* 5/6 Latin-6 (Northern Europe) */
+ /* 5/7 ??? */
+ /* 5/8 Sami (Lappish) supplementary */
+#define I2CS_96_ISO8859_13 0x59 /* 5/9 Latin-7 (Baltic) */
+#define I2CS_96_VSCII 0x5a /* 5/a Vietnamese */
+ /* 5/b Technical #1 IEC 1289 */
+#define I2CS_96_ISO8859_14 0x5c /* 5/c Latin-8 (Celtic) */
+ /* 5/d Sami supplementary Latin */
+ /* 5/e Latin/Hebrew */
+ /* 5/f Celtic supplementary Latin */
+ /* 6/0 Uralic supplementary Cyrillic */
+ /* 6/1 Volgaic supplementary Cyrillic */
+#define I2CS_96_ISO8859_15 0x62 /* 6/2 Latin-9 (Euro) */
+ /* 6/3 Latin-1 with Euro */
+ /* 6/4 Latin-4 with Euro */
+ /* 6/5 Latin-7 with Euro */
+#define I2CS_96_ISO8859_16 0x66 /* 6/6 Latin-10 (Balkan) */
+ /* 6/7 Ogham */
+ /* 6/8 Sami supplementary Latin #2 */
+ /* 7/d Supplementary Mosaic for CCITT 101 */
+
+/* 96x96 character sets */
+
+/* Types of character sets */
+
+#define I2CS_94 0x000 /* 94 character set */
+#define I2CS_96 0x100 /* 96 character set */
+#define I2CS_MUL 0x200 /* multi-byte */
+#define I2CS_94x94 (I2CS_MUL | I2CS_94)
+#define I2CS_96x96 (I2CS_MUL | I2CS_96)
+
+
+/* Character set identifiers stored in Gn */
+
+#define I2CS_BRITISH (I2CS_94 | I2CS_94_BRITISH)
+#define I2CS_ASCII (I2CS_94 | I2CS_94_ASCII)
+#define I2CS_JIS_BUGROM (I2CS_94 | I2CS_94_JIS_BUGROM)
+#define I2CS_JIS_KANA (I2CS_94 | I2CS_94_JIS_KANA)
+#define I2CS_JIS_ROMAN (I2CS_94 | I2CS_94_JIS_ROMAN)
+#define I2CS_JIS_OLD (I2CS_94x94 | I2CS_94x94_JIS_OLD)
+#define I2CS_GB (I2CS_94x94 | I2CS_94x94_GB)
+#define I2CS_JIS_NEW (I2CS_94x94 | I2CS_94x94_JIS_NEW)
+#define I2CS_KSC (I2CS_94x94 | I2CS_94x94_KSC)
+#define I2CS_JIS_EXT (I2CS_94x94 | I2CS_94x94_JIS_EXT)
+#define I2CS_CNS1 (I2CS_94x94 | I2CS_94x94_CNS1)
+#define I2CS_CNS2 (I2CS_94x94 | I2CS_94x94_CNS2)
+#define I2CS_CNS3 (I2CS_94x94 | I2CS_94x94_CNS3)
+#define I2CS_CNS4 (I2CS_94x94 | I2CS_94x94_CNS4)
+#define I2CS_CNS5 (I2CS_94x94 | I2CS_94x94_CNS5)
+#define I2CS_CNS6 (I2CS_94x94 | I2CS_94x94_CNS6)
+#define I2CS_CNS7 (I2CS_94x94 | I2CS_94x94_CNS7)
+#define I2CS_ISO8859_1 (I2CS_96 | I2CS_96_ISO8859_1)
+#define I2CS_ISO8859_2 (I2CS_96 | I2CS_96_ISO8859_2)
+#define I2CS_ISO8859_3 (I2CS_96 | I2CS_96_ISO8859_3)
+#define I2CS_ISO8859_4 (I2CS_96 | I2CS_96_ISO8859_4)
+#define I2CS_ISO8859_7 (I2CS_96 | I2CS_96_ISO8859_7)
+#define I2CS_ISO8859_6 (I2CS_96 | I2CS_96_ISO8859_6)
+#define I2CS_ISO8859_8 (I2CS_96 | I2CS_96_ISO8859_8)
+#define I2CS_ISO8859_5 (I2CS_96 | I2CS_96_ISO8859_5)
+#define I2CS_ISO8859_9 (I2CS_96 | I2CS_96_ISO8859_9)
+#define I2CS_TIS620 (I2CS_96 | I2CS_96_TIS620)
+#define I2CS_ISO8859_10 (I2CS_96 | I2CS_96_ISO8859_10)
+#define I2CS_ISO8859_13 (I2CS_96 | I2CS_96_ISO8859_13)
+#define I2CS_VSCII (I2CS_96 | I2CS_96_VSCII)
+#define I2CS_ISO8859_14 (I2CS_96 | I2CS_96_ISO8859_14)
+#define I2CS_ISO8859_15 (I2CS_96 | I2CS_96_ISO8859_15)
+#define I2CS_ISO8859_16 (I2CS_96 | I2CS_96_ISO8859_16)
+
+
+/* Miscellaneous ISO 2022 definitions */
+
+#define EUC_CS2 0x8e /* single shift CS2 */
+#define EUC_CS3 0x8f /* single shift CS3 */
+
+#define BITS7 0x7f /* 7-bit value mask */
+#define BIT8 0x80 /* 8th bit mask */
+
+/* The following saves us from having to have yet more charset tables */
+
+/* Unicode codepoints */
+
+#define UCS2_C0CONTROL 0x00 /* first C0 control */
+#define UCS2_C0CONTROLEND 0x1F /* last C0 control */
+#define UCS2_C1CONTROL 0x80 /* first C1 control */
+#define UCS2_C1CONTROLEND 0x9F /* last C1 control */
+
+ /* ISO 646 substituted Unicode codepoints */
+#define UCS2_POUNDSTERLING 0x00a3
+#define UCS2_YEN 0x00a5
+#define UCS2_OVERLINE 0x203e
+#define UCS2_EURO 0x20ac
+#define UCS2_KATAKANA 0xff61 /* first katakana codepoint */
+#define UCS2_BOM 0xfeff /* byte order mark */
+#define UCS2_BOGON 0xfffd /* replacement character */
+ /* next two codepoints are not Unicode chars */
+#define UCS2_BOMCHECK 0xfffe /* used to check byte order with UCS2_BOM */
+#define UCS2_NOTCHAR 0xffff /* not a character */
+
+#define UCS4_BMPBASE 0x0000 /* Basic Multilingual Plane */
+#define UCS4_SMPBASE 0x10000 /* Supplementary Multilinugual Plane */
+#define UCS4_SIPBASE 0x20000 /* Supplementary Ideographic Plane */
+ /* EastAsianWidth says plane 3 is wide */
+#define UCS4_UNABASE 0x40000 /* unassigned space */
+#define UCS4_SSPBASE 0xe0000 /* Supplementary Special-purpose Plane */
+#define UCS4_PVTBASE 0xf0000 /* private-space (two planes) */
+#define UCS4_MAXUNICODE 0x10ffff/* highest Unicode codepoint */
+
+#define UTF16_BASE 0x10000 /* base of codepoints needing surrogates */
+#define UTF16_SHIFT 10 /* surrogate shift */
+#define UTF16_MASK 0x3ff /* surrogate mask */
+#define UTF16_SURR 0xd800 /* UTF-16 surrogate area */
+#define UTF16_SURRH 0xd800 /* UTF-16 first high surrogate */
+#define UTF16_SURRHEND 0xdbff /* UTF-16 last high surrogate */
+#define UTF16_SURRL 0xdc00 /* UTF-16 first low surrogate */
+#define UTF16_SURRLEND 0xdfff /* UTF-16 last low surrogate */
+#define UTF16_MAXSURR 0xdfff /* end of UTF-16 surrogates */
+
+
+/* UBOGON is used to represent a codepoint in a character set which does not
+ * map to Unicode. It is also used for mapping failures, e.g. incomplete
+ * shift sequences. This name has the same text width as 0x????, for
+ * convenience in the mapping tables.
+ *
+ * NOCHAR is used to represent a codepoint in Unicode which does not map to
+ * the target character set in a reverse mapping table. This name has the
+ * same text width as 0x???? in case we ever add static reverse mapping tables.
+ */
+
+#define UBOGON UCS2_BOGON
+#define NOCHAR UCS2_NOTCHAR
+
+/* Codepoints in non-Unicode character sets */
+
+/* Codepoints in ISO 646 character sets */
+
+/* British ASCII codepoints */
+
+#define BRITISH_POUNDSTERLING 0x23
+
+/* JIS Roman codepoints */
+
+#define JISROMAN_YEN 0x5c
+#define JISROMAN_OVERLINE 0x7e
+
+
+/* Hankaku katakana codepoints & parameters
+ *
+ * In earlier versions, MAX_KANA_7 and MAX_KANA_8 were the maximum codepoint
+ * values. Although this made sense, it was confusing with the "max ku" and
+ * "max ten" values used in the double-byte tables; there are 1-origin, but
+ * the calculated values used for "ku" and "ten" are 0-origin (derived by
+ * substracting the "base"). What this all meant is that for double byte
+ * characters the limit test is of the form (value < max_ku), but for single
+ * byte characters (which used the same cell to hold the max ku) the limit
+ * test was (value <= max_ku).
+ *
+ * By making MAX_KANA_[78] be maximum+1, the same (value < max_ku) limit test
+ * is used throughout. - 6/15/2006
+ */
+
+#define MIN_KANA_7 0x21
+#define MAX_KANA_7 0x60 /* maximum value + 1 */
+#define KANA_7 (UCS2_KATAKANA - MIN_KANA_7)
+#define MIN_KANA_8 (MIN_KANA_7 | BIT8)
+#define MAX_KANA_8 (MAX_KANA_7 | BIT8)
+#define KANA_8 (UCS2_KATAKANA - MIN_KANA_8)
+
+/* Charset scripts */
+
+/* The term "script" is used here in a very loose sense, enough to make
+ * purists cringe. Basically, the idea is to give the main program some
+ * idea of how it should treat the characters of text in a charset with
+ * respect to font, drawing routines, etc.
+ *
+ * In some cases, "script" is associated with a charset; in other cases,
+ * it's more closely tied to a language.
+ */
+
+#define SC_UNICODE 0x1 /* Unicode */
+#define SC_LATIN_1 0x10 /* Western Europe */
+#define SC_LATIN_2 0x20 /* Eastern Europe */
+#define SC_LATIN_3 0x40 /* Southern Europe */
+#define SC_LATIN_4 0x80 /* Northern Europe */
+#define SC_LATIN_5 0x100 /* Turkish */
+#define SC_LATIN_6 0x200 /* Nordic */
+#define SC_LATIN_7 0x400 /* Baltic */
+#define SC_LATIN_8 0x800 /* Celtic */
+#define SC_LATIN_9 0x1000 /* Euro */
+#define SC_LATIN_0 SC_LATIN_9 /* colloquial name for Latin-9 */
+#define SC_ARABIC 0x2000
+#define SC_CYRILLIC 0x4000
+#define SC_GREEK 0x8000
+#define SC_HEBREW 0x10000
+#define SC_THAI 0x20000
+#define SC_UKRANIAN 0x40000
+#define SC_LATIN_10 0x80000 /* Balkan */
+#define SC_VIETNAMESE 0x100000
+#define SC_CHINESE_SIMPLIFIED 0x1000000
+#define SC_CHINESE_TRADITIONAL 0x2000000
+#define SC_JAPANESE 0x4000000
+#define SC_KOREAN 0x8000000
+
+
+/* Script table */
+
+typedef struct utf8_scent {
+ char *name; /* script name */
+ char *description; /* script description */
+ unsigned long script; /* script bitmask */
+} SCRIPT;
+
+/* Character set table support */
+
+typedef struct utf8_csent {
+ char *name; /* charset name */
+ unsigned short type; /* type of charset */
+ unsigned short flags; /* charset flags */
+ void *tab; /* additional data */
+ unsigned long script; /* script(s) implemented by this charset */
+ char *preferred; /* preferred charset over this one */
+} CHARSET;
+
+
+struct utf8_eucparam {
+ unsigned int base_ku : 8; /* base row */
+ unsigned int base_ten : 8; /* base column */
+ unsigned int max_ku : 8; /* maximum row */
+ unsigned int max_ten : 8; /* maximum column */
+ void *tab; /* conversion table */
+};
+
+
+/* Charset types */
+
+#define CT_UNKNOWN 0 /* unknown 8-bit */
+#define CT_ASCII 1 /* 7-bit ASCII no table */
+#define CT_UCS2 2 /* 2 byte 16-bit Unicode no table */
+#define CT_UCS4 3 /* 4 byte 32-bit Unicode no table */
+#define CT_1BYTE0 10 /* 1 byte ISO 8859-1 no table */
+#define CT_1BYTE 11 /* 1 byte ASCII + table 0x80-0xff */
+#define CT_1BYTE8 12 /* 1 byte table 0x00 - 0xff */
+#define CT_EUC 100 /* 2 byte ASCII + utf8_eucparam base/CS2/CS3 */
+#define CT_DBYTE 101 /* 2 byte ASCII + utf8_eucparam */
+#define CT_DBYTE2 102 /* 2 byte ASCII + utf8_eucparam plane1/2 */
+#define CT_UTF16 1000 /* variable UTF-16 encoded Unicode no table */
+#define CT_UTF8 1001 /* variable UTF-8 encoded Unicode no table */
+#define CT_UTF7 1002 /* variable UTF-7 encoded Unicode no table */
+#define CT_2022 10000 /* variable ISO-2022 encoded no table */
+#define CT_SJIS 10001 /* 2 byte Shift-JIS encoded JIS no table */
+
+
+/* Character set flags */
+
+#define CF_PRIMARY 0x1 /* primary name for this charset */
+#define CF_DISPLAY 0x2 /* charset used in displays */
+#define CF_POSTING 0x4 /* charset used in email posting */
+#define CF_UNSUPRT 0x8 /* charset unsupported (can't convert to it) */
+#define CF_NOEMAIL 0x10 /* charset not used in email */
+
+
+/* UTF-7 engine states */
+
+#define U7_ASCII 0 /* ASCII character */
+#define U7_PLUS 1 /* plus seen */
+#define U7_UNICODE 2 /* Unicode characters */
+#define U7_MINUS 3 /* absorbed minus seen */
+
+/* Function prototypes */
+
+typedef unsigned long (*ucs4cn_t) (unsigned long c);
+typedef unsigned long (*ucs4de_t) (unsigned long c,void **more);
+
+SCRIPT *utf8_script (char *script);
+const CHARSET *utf8_charset (char *charset);
+char *utf8_badcharset (char *charset);
+long utf8_text (SIZEDTEXT *text,char *charset,SIZEDTEXT *ret,long flags);
+long utf8_text_cs (SIZEDTEXT *text,const CHARSET *cs,SIZEDTEXT *ret,
+ ucs4cn_t cv,ucs4de_t de);
+long utf8_cstext (SIZEDTEXT *text,char *charset,SIZEDTEXT *ret,
+ unsigned long errch);
+long utf8_cstocstext (SIZEDTEXT *text,char *sc,SIZEDTEXT *ret,char *dc,
+ unsigned long errch);
+unsigned short *utf8_rmap (char *charset);
+unsigned short *utf8_rmap_cs (const CHARSET *cs);
+unsigned short *utf8_rmap_gen (const CHARSET *cs,unsigned short *oldmap);
+long utf8_rmaptext (SIZEDTEXT *text,unsigned short *rmap,SIZEDTEXT *ret,
+ unsigned long errch,long iso2022jp);
+unsigned long utf8_rmapsize (SIZEDTEXT *text,unsigned short *rmap,
+ unsigned long errch,long iso2022jp);
+long ucs4_rmaptext (unsigned long *ucs4,unsigned long len,unsigned short *rmap,
+ SIZEDTEXT *ret,unsigned long errch);
+long ucs4_rmaplen (unsigned long *ucs4,unsigned long len,unsigned short *rmap,
+ unsigned long errch);
+long ucs4_rmapbuf (unsigned char *t,unsigned long *ucs4,unsigned long len,
+ unsigned short *rmap,unsigned long errch);
+unsigned long utf8_get (unsigned char **s,unsigned long *i);
+unsigned long utf8_get_raw (unsigned char **s,unsigned long *i);
+unsigned long ucs4_cs_get (CHARSET *cs,unsigned char **s,unsigned long *i);
+unsigned long *utf8_csvalidmap (char *charsets[]);
+const CHARSET *utf8_infercharset (SIZEDTEXT *src);
+long utf8_validate (unsigned char *s,unsigned long i);
+void utf8_text_1byte0 (SIZEDTEXT *text,SIZEDTEXT *ret,ucs4cn_t cv,ucs4de_t de);
+void utf8_text_1byte (SIZEDTEXT *text,SIZEDTEXT *ret,void *tab,ucs4cn_t cv,
+ ucs4de_t de);
+void utf8_text_1byte8 (SIZEDTEXT *text,SIZEDTEXT *ret,void *tab,ucs4cn_t cv,
+ ucs4de_t de);
+void utf8_text_euc (SIZEDTEXT *text,SIZEDTEXT *ret,void *tab,ucs4cn_t cv,
+ ucs4de_t de);
+void utf8_text_dbyte (SIZEDTEXT *text,SIZEDTEXT *ret,void *tab,ucs4cn_t cv,
+ ucs4de_t de);
+void utf8_text_dbyte2 (SIZEDTEXT *text,SIZEDTEXT *ret,void *tab,ucs4cn_t cv,
+ ucs4de_t de);
+void utf8_text_sjis (SIZEDTEXT *text,SIZEDTEXT *ret,ucs4cn_t cv,ucs4de_t de);
+void utf8_text_2022 (SIZEDTEXT *text,SIZEDTEXT *ret,ucs4cn_t cv,ucs4de_t de);
+void utf8_text_utf7 (SIZEDTEXT *text,SIZEDTEXT *ret,ucs4cn_t cv,ucs4de_t de);
+void utf8_text_utf8 (SIZEDTEXT *text,SIZEDTEXT *ret,ucs4cn_t cv,ucs4de_t de);
+void utf8_text_ucs2 (SIZEDTEXT *text,SIZEDTEXT *ret,ucs4cn_t cv,ucs4de_t de);
+void utf8_text_ucs4 (SIZEDTEXT *text,SIZEDTEXT *ret,ucs4cn_t cv,ucs4de_t de);
+void utf8_text_utf16 (SIZEDTEXT *text,SIZEDTEXT *ret,ucs4cn_t cv,ucs4de_t de);
+unsigned long utf8_size (unsigned long c);
+unsigned char *utf8_put (unsigned char *s,unsigned long c);
+unsigned long ucs4_titlecase (unsigned long c);
+long ucs4_width (unsigned long c);
+long utf8_strwidth (unsigned char *s);
+long utf8_textwidth (SIZEDTEXT *utf8);
+unsigned long ucs4_decompose (unsigned long c,void **more);
+unsigned long ucs4_decompose_recursive (unsigned long c,void **more);
diff --git a/imap/src/c-client/utf8aux.c b/imap/src/c-client/utf8aux.c
new file mode 100644
index 00000000..5138987b
--- /dev/null
+++ b/imap/src/c-client/utf8aux.c
@@ -0,0 +1,449 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UTF-8 auxillary routines (c-client and MIME2 support)
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 June 1997
+ * Last Edited: 12 October 2007
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include "c-client.h"
+
+/* Convert charset labelled stringlist to UTF-8 in place
+ * Accepts: string list
+ * charset
+ */
+
+static void utf8_stringlist (STRINGLIST *st,char *charset)
+{
+ SIZEDTEXT txt;
+ /* convert entire stringstruct */
+ if (st) do if (utf8_text (&st->text,charset,&txt,U8T_CANONICAL)) {
+ fs_give ((void **) &st->text.data);
+ st->text.data = txt.data; /* transfer this text */
+ st->text.size = txt.size;
+ } while (st = st->next);
+}
+
+
+/* Convert charset labelled searchpgm to UTF-8 in place
+ * Accepts: search program
+ * charset
+ */
+
+void utf8_searchpgm (SEARCHPGM *pgm,char *charset)
+{
+ SIZEDTEXT txt;
+ SEARCHHEADER *hl;
+ SEARCHOR *ol;
+ SEARCHPGMLIST *pl;
+ if (pgm) { /* must have a search program */
+ utf8_stringlist (pgm->bcc,charset);
+ utf8_stringlist (pgm->cc,charset);
+ utf8_stringlist (pgm->from,charset);
+ utf8_stringlist (pgm->to,charset);
+ utf8_stringlist (pgm->subject,charset);
+ for (hl = pgm->header; hl; hl = hl->next) {
+ if (utf8_text (&hl->line,charset,&txt,U8T_CANONICAL)) {
+ fs_give ((void **) &hl->line.data);
+ hl->line.data = txt.data;
+ hl->line.size = txt.size;
+ }
+ if (utf8_text (&hl->text,charset,&txt,U8T_CANONICAL)) {
+ fs_give ((void **) &hl->text.data);
+ hl->text.data = txt.data;
+ hl->text.size = txt.size;
+ }
+ }
+ utf8_stringlist (pgm->body,charset);
+ utf8_stringlist (pgm->text,charset);
+ for (ol = pgm->or; ol; ol = ol->next) {
+ utf8_searchpgm (ol->first,charset);
+ utf8_searchpgm (ol->second,charset);
+ }
+ for (pl = pgm->not; pl; pl = pl->next) utf8_searchpgm (pl->pgm,charset);
+ utf8_stringlist (pgm->return_path,charset);
+ utf8_stringlist (pgm->sender,charset);
+ utf8_stringlist (pgm->reply_to,charset);
+ utf8_stringlist (pgm->in_reply_to,charset);
+ utf8_stringlist (pgm->message_id,charset);
+ utf8_stringlist (pgm->newsgroups,charset);
+ utf8_stringlist (pgm->followup_to,charset);
+ utf8_stringlist (pgm->references,charset);
+ }
+}
+
+/* Convert MIME-2 sized text to UTF-8
+ * Accepts: source sized text
+ * charset
+ * flags (same as utf8_text())
+ * Returns: T if successful, NIL if failure
+ */
+
+#define MINENCWORD 9
+#define MAXENCWORD 75
+
+/* This resizing algorithm is stupid, but hopefully it should never be triggered
+ * except for a pathological header. The main concern is that we don't get a
+ * buffer overflow.
+ */
+
+#define DSIZE 65536 /* real headers should never be this big */
+#define FUZZ 10 /* paranoia fuzz */
+
+long utf8_mime2text (SIZEDTEXT *src,SIZEDTEXT *dst,long flags)
+{
+ unsigned char *s,*se,*e,*ee,*t,*te;
+ char *cs,*ce,*ls;
+ SIZEDTEXT txt,rtxt;
+ unsigned long i;
+ size_t dsize = min (DSIZE,((src->size / 4) + 1) * 9);
+ /* always create buffer if canonicalizing */
+ dst->data = (flags & U8T_CANONICAL) ?
+ (unsigned char *) fs_get ((size_t) dsize) : NIL;
+ dst->size = 0; /* nothing written yet */
+ /* look for encoded words */
+ for (s = src->data, se = src->data + src->size; s < se; s++) {
+ if (((se - s) > MINENCWORD) && (*s == '=') && (s[1] == '?') &&
+ (cs = (char *) mime2_token (s+2,se,(unsigned char **) &ce)) &&
+ (e = mime2_token ((unsigned char *) ce+1,se,&ee)) &&
+ (te = mime2_text (t = e+2,se)) && (ee == e + 1) &&
+ ((te - s) < MAXENCWORD)) {
+ if (mime2_decode (e,t,te,&txt)) {
+ *ce = '\0'; /* temporarily tie off charset */
+ if (ls = strchr (cs,'*')) *ls = '\0';
+ /* convert to UTF-8 as best we can */
+ if (!utf8_text (&txt,cs,&rtxt,flags)) utf8_text (&txt,NIL,&rtxt,flags);
+ if (dst->data) { /* make sure existing buffer fits */
+ while (dsize <= (dst->size + rtxt.size + FUZZ)) {
+ dsize += DSIZE; /* kick it up */
+ fs_resize ((void **) &dst->data,dsize);
+ }
+ }
+ else { /* make a new buffer */
+ while (dsize <= (dst->size + rtxt.size)) dsize += DSIZE;
+ memcpy (dst->data = (unsigned char *) fs_get (dsize),src->data,
+ dst->size = s - src->data);
+ }
+ for (i = 0; i < rtxt.size; i++) dst->data[dst->size++] = rtxt.data[i];
+
+ /* all done with converted text */
+ if (rtxt.data != txt.data) fs_give ((void **) &rtxt.data);
+ if (ls) *ls = '*'; /* restore language tag delimiter */
+ *ce = '?'; /* restore charset delimiter */
+ /* all done with decoded text */
+ fs_give ((void **) &txt.data);
+ s = te+1; /* continue scan after encoded word */
+ /* skip leading whitespace */
+ for (t = s + 1; (t < se) && ((*t == ' ') || (*t == '\t')); t++);
+ /* see if likely continuation encoded word */
+ if (t < (se - MINENCWORD)) switch (*t) {
+ case '=': /* possible encoded word? */
+ if (t[1] == '?') s = t - 1;
+ break;
+ case '\015': /* CR, eat a following LF */
+ if (t[1] == '\012') t++;
+ case '\012': /* possible end of logical line */
+ if ((t[1] == ' ') || (t[1] == '\t')) {
+ do t++;
+ while ((t < (se - MINENCWORD)) && ((t[1] == ' ')||(t[1] == '\t')));
+ if ((t < (se - MINENCWORD)) && (t[1] == '=') && (t[2] == '?'))
+ s = t; /* definitely looks like continuation */
+ }
+ }
+ }
+ else { /* restore original text */
+ if (dst->data) fs_give ((void **) &dst->data);
+ dst->data = src->data;
+ dst->size = src->size;
+ return NIL; /* syntax error: MIME-2 decoding failure */
+ }
+ }
+ else do if (dst->data) { /* stash ASCII characters until LWSP */
+ if (dsize < (dst->size + FUZZ)) {
+ dsize += DSIZE; /* kick it up */
+ fs_resize ((void **) &dst->data,dsize);
+ }
+ /* kludge: assumes ASCII doesn't decompose and titlecases to one byte */
+ dst->data[dst->size++] = (flags & U8T_CASECANON) ?
+ (unsigned char) ucs4_titlecase (*s) : *s;
+ }
+ while ((*s != ' ') && (*s != '\t') && (*s != '\015') && (*s != '\012') &&
+ (++s < se));
+ }
+ if (dst->data) dst->data[dst->size] = '\0';
+ else { /* nothing converted, return identity */
+ dst->data = src->data;
+ dst->size = src->size;
+ }
+ return T; /* success */
+}
+
+/* Decode MIME-2 text
+ * Accepts: Encoding
+ * text
+ * text end
+ * destination sized text
+ * Returns: T if successful, else NIL
+ */
+
+long mime2_decode (unsigned char *e,unsigned char *t,unsigned char *te,
+ SIZEDTEXT *txt)
+{
+ unsigned char *q;
+ txt->data = NIL; /* initially no returned data */
+ switch (*e) { /* dispatch based upon encoding */
+ case 'Q': case 'q': /* sort-of QUOTED-PRINTABLE */
+ txt->data = (unsigned char *) fs_get ((size_t) (te - t) + 1);
+ for (q = t,txt->size = 0; q < te; q++) switch (*q) {
+ case '=': /* quoted character */
+ /* both must be hex */
+ if (!isxdigit (q[1]) || !isxdigit (q[2])) {
+ fs_give ((void **) &txt->data);
+ return NIL; /* syntax error: bad quoted character */
+ }
+ /* assemble character */
+ txt->data[txt->size++] = hex2byte (q[1],q[2]);
+ q += 2; /* advance past quoted character */
+ break;
+ case '_': /* convert to space */
+ txt->data[txt->size++] = ' ';
+ break;
+ default: /* ordinary character */
+ txt->data[txt->size++] = *q;
+ break;
+ }
+ txt->data[txt->size] = '\0';
+ break;
+ case 'B': case 'b': /* BASE64 */
+ if (txt->data = (unsigned char *) rfc822_base64 (t,te - t,&txt->size))
+ break;
+ default: /* any other encoding is unknown */
+ return NIL; /* syntax error: unknown encoding */
+ }
+ return T;
+}
+
+/* Get MIME-2 token from encoded word
+ * Accepts: current text pointer
+ * text limit pointer
+ * pointer to returned end pointer
+ * Returns: current text pointer & end pointer if success, else NIL
+ */
+
+unsigned char *mime2_token (unsigned char *s,unsigned char *se,
+ unsigned char **t)
+{
+ for (*t = s; **t != '?'; ++*t) {
+ if ((*t < se) && isgraph (**t)) switch (**t) {
+ case '(': case ')': case '<': case '>': case '@': case ',': case ';':
+ case ':': case '\\': case '"': case '/': case '[': case ']': case '.':
+ case '=':
+ return NIL; /* none of these are valid in tokens */
+ }
+ else return NIL; /* out of text or CTL or space */
+ }
+ return s;
+}
+
+
+/* Get MIME-2 text from encoded word
+ * Accepts: current text pointer
+ * text limit pointer
+ * pointer to returned end pointer
+ * Returns: end pointer if success, else NIL
+ */
+
+unsigned char *mime2_text (unsigned char *s,unsigned char *se)
+{
+ unsigned char *t = se - 1;
+ /* search for closing ?, make sure valid */
+ while ((s < t) && (*s != '?') && isgraph (*s++));
+ return ((s < t) && (*s == '?') && (s[1] == '=') &&
+ ((se == (s + 2)) || (s[2] == ' ') || (s[2] == '\t') ||
+ (s[2] == '\015') || (s[2] == '\012'))) ? s : NIL;
+}
+
+/* Convert UTF-16 string to Modified Base64
+ * Accepts: destination pointer
+ * source string
+ * source length in octets
+ * Returns: updated destination pointer
+ */
+
+static unsigned char *utf16_to_mbase64 (unsigned char *t,unsigned char *s,
+ size_t i)
+{
+ char *v = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
+ *t++ = '&'; /* write shift-in */
+ while (i >= 3) { /* process tuplets */
+ *t++ = v[s[0] >> 2]; /* byte 1: high 6 bits (1) */
+ /* byte 2: low 2 bits (1), high 4 bits (2) */
+ *t++ = v[((s[0] << 4) + (s[1] >> 4)) & 0x3f];
+ /* byte 3: low 4 bits (2), high 2 bits (3) */
+ *t++ = v[((s[1] << 2) + (s[2] >> 6)) & 0x3f];
+ *t++ = v[s[2] & 0x3f]; /* byte 4: low 6 bits (3) */
+ s += 3;
+ i -= 3;
+ }
+ if (i) {
+ *t++ = v[s[0] >> 2]; /* byte 1: high 6 bits (1) */
+ /* byte 2: low 2 bits (1), high 4 bits (2) */
+ *t++ = v[((s[0] << 4) + (--i ? (s[1] >> 4) : 0)) & 0x3f];
+ /* byte 3: low 4 bits (2) */
+ if (i) *t++ = v[(s[1] << 2) & 0x3f];
+ }
+ *t++ = '-'; /* write shift-out */
+ return t;
+}
+
+
+/* Poot a UTF-16 value to a buffer
+ * Accepts: buffer pointer
+ * value
+ * Returns: updated pointer
+ */
+
+static unsigned char *utf16_poot (unsigned char *s,unsigned long c)
+{
+ *s++ = (unsigned char) (c >> 8);
+ *s++ = (unsigned char) (c & 0xff);
+ return s;
+}
+
+/* Convert UTF-8 to Modified UTF-7
+ * Accepts: UTF-8 string
+ * Returns: Modified UTF-7 string on success, NIL if invalid UTF-8
+ */
+
+#define MAXUNIUTF8 4 /* maximum length of Unicode UTF-8 sequence */
+
+unsigned char *utf8_to_mutf7 (unsigned char *src)
+{
+ unsigned char *u16buf,*utf16;
+ unsigned char *ret,*t;
+ unsigned long j,c;
+ unsigned char *s = src;
+ unsigned long i = 0;
+ int nonascii = 0;
+ while (*s) { /* pass one: count destination octets */
+ if (*s & 0x80) { /* non-ASCII character? */
+ j = MAXUNIUTF8; /* get single UCS-4 codepoint */
+ if ((c = utf8_get (&s,&j)) & U8G_ERROR) return NIL;
+ /* tally number of UTF-16 octets */
+ nonascii += (c & U8GM_NONBMP) ? 4 : 2;
+ }
+ else { /* ASCII character */
+ if (nonascii) { /* add pending Modified BASE64 size + shifts */
+ i += ((nonascii / 3) * 4) + ((j = nonascii % 3) ? j + 1 : 0) + 2;
+ nonascii = 0; /* back to ASCII */
+ }
+ if (*s == '&') i += 2; /* two octets if the escape */
+ else ++i; /* otherwise just count another octet */
+ ++s; /* advance to next source octet */
+ }
+ }
+ if (nonascii) /* add pending Modified BASE64 size + shifts */
+ i += ((nonascii / 3) * 4) + ((j = nonascii % 3) ? j + 1 : 0) + 2;
+
+ /* create return buffer */
+ t = ret = (unsigned char *) fs_get (i + 1);
+ /* and scratch buffer */
+ utf16 = u16buf = (unsigned char *) fs_get (i + 1);
+ for (s = src; *s;) { /* pass two: copy destination octets */
+ if (*s & 0x80) { /* non-ASCII character? */
+ j = MAXUNIUTF8; /* get single UCS-4 codepoint */
+ if ((c = utf8_get (&s,&j)) & U8G_ERROR) return NIL;
+ if (c & U8GM_NONBMP) { /* non-BMP? */
+ c -= UTF16_BASE; /* yes, convert to surrogate */
+ utf16 = utf16_poot (utf16_poot (utf16,(c >> UTF16_SHIFT)+UTF16_SURRH),
+ (c & UTF16_MASK) + UTF16_SURRL);
+ }
+ else utf16 = utf16_poot (utf16,c);
+ }
+ else { /* ASCII character */
+ if (utf16 != u16buf) { /* add pending Modified BASE64 size + shifts */
+ t = utf16_to_mbase64 (t,u16buf,utf16 - u16buf);
+ utf16 = u16buf; /* reset buffer */
+ }
+ *t++ = *s; /* copy the character */
+ if (*s == '&') *t++ = '-';/* special sequence if the escape */
+ ++s; /* advance to next source octet */
+ }
+ }
+ /* add pending Modified BASE64 size + shifts */
+ if (utf16 != u16buf) t = utf16_to_mbase64 (t,u16buf,utf16 - u16buf);
+ *t = '\0'; /* tie off destination */
+ if (i != (t - ret)) fatal ("utf8_to_mutf7 botch");
+ fs_give ((void **) &u16buf);
+ return ret;
+}
+
+/* Convert Modified UTF-7 to UTF-8
+ * Accepts: Modified UTF-7 string
+ * Returns: UTF-8 string on success, NIL if invalid Modified UTF-7
+ */
+
+unsigned char *utf8_from_mutf7 (unsigned char *src)
+{
+ SIZEDTEXT utf8,utf7;
+ unsigned char *s;
+ int mbase64 = 0;
+ /* disallow bogus strings */
+ if (mail_utf7_valid (src)) return NIL;
+ /* initialize SIZEDTEXTs */
+ memset (&utf7,0,sizeof (SIZEDTEXT));
+ memset (&utf8,0,sizeof (SIZEDTEXT));
+ /* make copy of source */
+ for (s = cpytxt (&utf7,src,strlen (src)); *s; ++s) switch (*s) {
+ case '&': /* Modified UTF-7 uses & instead of + */
+ *s = '+';
+ mbase64 = T; /* note that we are in Modified BASE64 */
+ break;
+ case '+': /* temporarily swap text + to & */
+ if (!mbase64) *s = '&';
+ break;
+ case '-': /* shift back to ASCII */
+ mbase64 = NIL;
+ break;
+ case ',': /* Modified UTF-7 uses , instead of / ... */
+ if (mbase64) *s = '/'; /* ...in Modified BASE64 */
+ break;
+ }
+ /* do the conversion */
+ utf8_text_utf7 (&utf7,&utf8,NIL,NIL);
+ /* no longer need copy of source */
+ fs_give ((void **) &utf7.data);
+ /* post-process: switch & and + */
+ for (s = utf8.data; *s; ++s) switch (*s) {
+ case '&':
+ *s = '+';
+ break;
+ case '+':
+ *s = '&';
+ break;
+ }
+ return utf8.data;
+}
diff --git a/imap/src/c-client/utf8aux.h b/imap/src/c-client/utf8aux.h
new file mode 100644
index 00000000..beb55057
--- /dev/null
+++ b/imap/src/c-client/utf8aux.h
@@ -0,0 +1,44 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UTF-8 auxillary routines (c-client and MIME2 support)
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 June 1997
+ * Last Edited: 9 October 2007
+ */
+
+
+/* Following routines are in utf8aux.c as these depend upon c-client.
+ * Splitting these routines out makes it possible for pico to link with utf8.o
+ * and a few rump routines (e.g., fs_get()) but not all the rest of c-client
+ * (which pico does not need).
+ */
+
+void utf8_searchpgm (SEARCHPGM *pgm,char *charset);
+long utf8_mime2text (SIZEDTEXT *src,SIZEDTEXT *dst,long flags);
+unsigned char *mime2_token (unsigned char *s,unsigned char *se,
+ unsigned char **t);
+unsigned char *mime2_text (unsigned char *s,unsigned char *se);
+long mime2_decode (unsigned char *e,unsigned char *t,unsigned char *te,
+ SIZEDTEXT *txt);
+unsigned char *utf8_to_mutf7 (unsigned char *src);
+unsigned char *utf8_from_mutf7 (unsigned char *src);
diff --git a/imap/src/charset/big5.c b/imap/src/charset/big5.c
new file mode 100644
index 00000000..2bd4a35e
--- /dev/null
+++ b/imap/src/charset/big5.c
@@ -0,0 +1,2016 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: BIG-5 conversion table
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 July 1997
+ * Last Edited: 30 August 2006
+ */
+
+/* Big-5 is the de-facto industrial standard of the Republic of China
+ * (Taiwan), and is commonly used in the Hong Kong Special Administrative
+ * Region of the People's Republic of China (mainland China).
+ */
+
+#define BASE_BIG5_KU 0xa1
+#define BASE_BIG5_TEN_0 0x40
+#define BASE_BIG5_TEN_1 0xa1
+#define MAX_BIG5_KU 89
+#define MAX_BIG5_TEN_0 63
+#define MAX_BIG5_TEN_1 94
+
+
+#define BIG5TOUNICODE(c,c1,ku,ten) \
+ (((ku = c - BASE_BIG5_KU) < MAX_BIG5_KU) ? \
+ ((c1 & 0x80) ? \
+ (((ten = c1 - BASE_BIG5_TEN_1) < MAX_BIG5_TEN_1) ? \
+ big5tab[ku].plane1[ten] : UBOGON) : \
+ (((ten = c1 - BASE_BIG5_TEN_0) < MAX_BIG5_TEN_0) ? \
+ big5tab[ku].plane0[ten] : UBOGON)) : UBOGON)
+
+
+typedef struct big5_ku {
+ unsigned short plane0[MAX_BIG5_TEN_0];
+ unsigned short plane1[MAX_BIG5_TEN_1];
+} BIG5;
+
+
+static const BIG5 big5tab[MAX_BIG5_KU] = {
+ { /* ku 01 */
+ {
+ 0x3000,0xff0c,0x3001,0x3002,0xff0e,0x2022,0xff1b,0xff1a,0xff1f,0xff01,
+ 0xfe30,0x2026,0x2025,0xfe50,0xff64,0xfe52,0x00b7,0xfe54,0xfe55,0xfe56,
+ 0xfe57,0xff5c,0x2013,0xfe31,0x2014,0xfe33,0xfffd,0xfe34,0xfe4f,0xff08,
+ 0xff09,0xfe35,0xfe36,0xff5b,0xff5d,0xfe37,0xfe38,0x3014,0x3015,0xfe39,
+ 0xfe3a,0x3010,0x3011,0xfe3b,0xfe3c,0x300a,0x300b,0xfe3d,0xfe3e,0x3008,
+ 0x3009,0xfe3f,0xfe40,0x300c,0x300d,0xfe41,0xfe42,0x300e,0x300f,0xfe43,
+ 0xfe44,0xfe59,0xfe5a
+ },{
+ 0xfe5b,0xfe5c,0xfe5d,0xfe5e,0x2018,0x2019,0x201c,0x201d,0x301d,0x301e,
+ 0x2035,0x2032,0xff03,0xff06,0xff0a,0x203b,0x00a7,0x3003,0x25cb,0x25cf,
+ 0x25b3,0x25b2,0x25ce,0x2606,0x2605,0x25c7,0x25c6,0x25a1,0x25a0,0x25bd,
+ 0x25bc,0x32a3,0x2105,0x203e,0xfffd,0xff3f,0xfffd,0xfe49,0xfe4a,0xfe4d,
+ 0xfe4e,0xfe4b,0xfe4c,0xfe5f,0xfe60,0xfe61,0xff0b,0xff0d,0x00d7,0x00f7,
+ 0x00b1,0x221a,0xff1c,0xff1e,0xff1d,0x2266,0x2267,0x2260,0x221e,0x2252,
+ 0x2261,0xfe62,0xfe63,0xfe64,0xfe65,0xfe66,0x223c,0x2229,0x222a,0x22a5,
+ 0x2220,0x221f,0x22bf,0x33d2,0x33d1,0x222b,0x222e,0x2235,0x2234,0x2640,
+ 0x2642,0x2641,0x2609,0x2191,0x2193,0x2190,0x2192,0x2196,0x2197,0x2199,
+ 0x2198,0x2225,0x2223,0xfffd
+ }
+ },
+ { /* ku 02 */
+ {
+ 0xfffd,0xff0f,0xff3c,0xff04,0x00a5,0x3012,0x00a2,0x00a3,0xff05,0xff20,
+ 0x2103,0x2109,0xfe69,0xfe6a,0xfe6b,0x33d5,0x339c,0x339d,0x339e,0x33ce,
+ 0x33a1,0x338e,0x338f,0x33c4,0x00b0,0x5159,0x515b,0x515e,0x515d,0x5161,
+ 0x5163,0x55e7,0x74e9,0x7cce,0x2581,0x2582,0x2583,0x2584,0x2585,0x2586,
+ 0x2587,0x2588,0x258f,0x258e,0x258d,0x258c,0x258b,0x258a,0x2589,0x253c,
+ 0x2534,0x252c,0x2524,0x251c,0x2594,0x2500,0x2502,0x2595,0x250c,0x2510,
+ 0x2514,0x2518,0x256d
+ },{
+ 0x256e,0x2570,0x256f,0x2550,0x255e,0x256a,0x2561,0x25e2,0x25e3,0x25e5,
+ 0x25e4,0x2571,0x2572,0x2573,0xff10,0xff11,0xff12,0xff13,0xff14,0xff15,
+ 0xff16,0xff17,0xff18,0xff19,0x2160,0x2161,0x2162,0x2163,0x2164,0x2165,
+ 0x2166,0x2167,0x2168,0x2169,0x3021,0x3022,0x3023,0x3024,0x3025,0x3026,
+ 0x3027,0x3028,0x3029,0xfffd,0x5344,0xfffd,0xff21,0xff22,0xff23,0xff24,
+ 0xff25,0xff26,0xff27,0xff28,0xff29,0xff2a,0xff2b,0xff2c,0xff2d,0xff2e,
+ 0xff2f,0xff30,0xff31,0xff32,0xff33,0xff34,0xff35,0xff36,0xff37,0xff38,
+ 0xff39,0xff3a,0xff41,0xff42,0xff43,0xff44,0xff45,0xff46,0xff47,0xff48,
+ 0xff49,0xff4a,0xff4b,0xff4c,0xff4d,0xff4e,0xff4f,0xff50,0xff51,0xff52,
+ 0xff53,0xff54,0xff55,0xff56
+ }
+ },
+ { /* ku 03 */
+ {
+ 0xff57,0xff58,0xff59,0xff5a,0x0391,0x0392,0x0393,0x0394,0x0395,0x0396,
+ 0x0397,0x0398,0x0399,0x039a,0x039b,0x039c,0x039d,0x039e,0x039f,0x03a0,
+ 0x03a1,0x03a3,0x03a4,0x03a5,0x03a6,0x03a7,0x03a8,0x03a9,0x03b1,0x03b2,
+ 0x03b3,0x03b4,0x03b5,0x03b6,0x03b7,0x03b8,0x03b9,0x03ba,0x03bb,0x03bc,
+ 0x03bd,0x03be,0x03bf,0x03c0,0x03c1,0x03c3,0x03c4,0x03c5,0x03c6,0x03c7,
+ 0x03c8,0x03c9,0x3105,0x3106,0x3107,0x3108,0x3109,0x310a,0x310b,0x310c,
+ 0x310d,0x310e,0x310f
+ },{
+ 0x3110,0x3111,0x3112,0x3113,0x3114,0x3115,0x3116,0x3117,0x3118,0x3119,
+ 0x311a,0x311b,0x311c,0x311d,0x311e,0x311f,0x3120,0x3121,0x3122,0x3123,
+ 0x3124,0x3125,0x3126,0x3127,0x3128,0x3129,0x02d9,0x02c9,0x02ca,0x02c7,
+ 0x02cb,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ }
+ },
+ { /* ku 04 */
+ {
+ 0x4e00,0x4e59,0x4e01,0x4e03,0x4e43,0x4e5d,0x4e86,0x4e8c,0x4eba,0x513f,
+ 0x5165,0x516b,0x51e0,0x5200,0x5201,0x529b,0x5315,0x5341,0x535c,0x53c8,
+ 0x4e09,0x4e0b,0x4e08,0x4e0a,0x4e2b,0x4e38,0x51e1,0x4e45,0x4e48,0x4e5f,
+ 0x4e5e,0x4e8e,0x4ea1,0x5140,0x5203,0x52fa,0x5343,0x53c9,0x53e3,0x571f,
+ 0x58eb,0x5915,0x5927,0x5973,0x5b50,0x5b51,0x5b53,0x5bf8,0x5c0f,0x5c22,
+ 0x5c38,0x5c71,0x5ddd,0x5de5,0x5df1,0x5df2,0x5df3,0x5dfe,0x5e72,0x5efe,
+ 0x5f0b,0x5f13,0x624d
+ },{
+ 0x4e11,0x4e10,0x4e0d,0x4e2d,0x4e30,0x4e39,0x4e4b,0x5c39,0x4e88,0x4e91,
+ 0x4e95,0x4e92,0x4e94,0x4ea2,0x4ec1,0x4ec0,0x4ec3,0x4ec6,0x4ec7,0x4ecd,
+ 0x4eca,0x4ecb,0x4ec4,0x5143,0x5141,0x5167,0x516d,0x516e,0x516c,0x5197,
+ 0x51f6,0x5206,0x5207,0x5208,0x52fb,0x52fe,0x52ff,0x5316,0x5339,0x5348,
+ 0x5347,0x5345,0x535e,0x5384,0x53cb,0x53ca,0x53cd,0x58ec,0x5929,0x592b,
+ 0x592a,0x592d,0x5b54,0x5c11,0x5c24,0x5c3a,0x5c6f,0x5df4,0x5e7b,0x5eff,
+ 0x5f14,0x5f15,0x5fc3,0x6208,0x6236,0x624b,0x624e,0x652f,0x6587,0x6597,
+ 0x65a4,0x65b9,0x65e5,0x66f0,0x6708,0x6728,0x6b20,0x6b62,0x6b79,0x6bcb,
+ 0x6bd4,0x6bdb,0x6c0f,0x6c34,0x706b,0x722a,0x7236,0x723b,0x7247,0x7259,
+ 0x725b,0x72ac,0x738b,0x4e19
+ }
+ },
+ { /* ku 05 */
+ {
+ 0x4e16,0x4e15,0x4e14,0x4e18,0x4e3b,0x4e4d,0x4e4f,0x4e4e,0x4ee5,0x4ed8,
+ 0x4ed4,0x4ed5,0x4ed6,0x4ed7,0x4ee3,0x4ee4,0x4ed9,0x4ede,0x5145,0x5144,
+ 0x5189,0x518a,0x51ac,0x51f9,0x51fa,0x51f8,0x520a,0x52a0,0x529f,0x5305,
+ 0x5306,0x5317,0x531d,0x4edf,0x534a,0x5349,0x5361,0x5360,0x536f,0x536e,
+ 0x53bb,0x53ef,0x53e4,0x53f3,0x53ec,0x53ee,0x53e9,0x53e8,0x53fc,0x53f8,
+ 0x53f5,0x53eb,0x53e6,0x53ea,0x53f2,0x53f1,0x53f0,0x53e5,0x53ed,0x53fb,
+ 0x56db,0x56da,0x5916
+ },{
+ 0x592e,0x5931,0x5974,0x5976,0x5b55,0x5b83,0x5c3c,0x5de8,0x5de7,0x5de6,
+ 0x5e02,0x5e03,0x5e73,0x5e7c,0x5f01,0x5f18,0x5f17,0x5fc5,0x620a,0x6253,
+ 0x6254,0x6252,0x6251,0x65a5,0x65e6,0x672e,0x672c,0x672a,0x672b,0x672d,
+ 0x6b63,0x6bcd,0x6c11,0x6c10,0x6c38,0x6c41,0x6c40,0x6c3e,0x72af,0x7384,
+ 0x7389,0x74dc,0x74e6,0x7518,0x751f,0x7528,0x7529,0x7530,0x7531,0x7532,
+ 0x7533,0x758b,0x767d,0x76ae,0x76bf,0x76ee,0x77db,0x77e2,0x77f3,0x793a,
+ 0x79be,0x7a74,0x7acb,0x4e1e,0x4e1f,0x4e52,0x4e53,0x4e69,0x4e99,0x4ea4,
+ 0x4ea6,0x4ea5,0x4eff,0x4f09,0x4f19,0x4f0a,0x4f15,0x4f0d,0x4f10,0x4f11,
+ 0x4f0f,0x4ef2,0x4ef6,0x4efb,0x4ef0,0x4ef3,0x4efd,0x4f01,0x4f0b,0x5149,
+ 0x5147,0x5146,0x5148,0x5168
+ }
+ },
+ { /* ku 06 */
+ {
+ 0x5171,0x518d,0x51b0,0x5217,0x5211,0x5212,0x520e,0x5216,0x52a3,0x5308,
+ 0x5321,0x5320,0x5370,0x5371,0x5409,0x540f,0x540c,0x540a,0x5410,0x5401,
+ 0x540b,0x5404,0x5411,0x540d,0x5408,0x5403,0x540e,0x5406,0x5412,0x56e0,
+ 0x56de,0x56dd,0x5733,0x5730,0x5728,0x572d,0x572c,0x572f,0x5729,0x5919,
+ 0x591a,0x5937,0x5938,0x5984,0x5978,0x5983,0x597d,0x5979,0x5982,0x5981,
+ 0x5b57,0x5b58,0x5b87,0x5b88,0x5b85,0x5b89,0x5bfa,0x5c16,0x5c79,0x5dde,
+ 0x5e06,0x5e76,0x5e74
+ },{
+ 0x5f0f,0x5f1b,0x5fd9,0x5fd6,0x620e,0x620c,0x620d,0x6210,0x6263,0x625b,
+ 0x6258,0x6536,0x65e9,0x65e8,0x65ec,0x65ed,0x66f2,0x66f3,0x6709,0x673d,
+ 0x6734,0x6731,0x6735,0x6b21,0x6b64,0x6b7b,0x6c16,0x6c5d,0x6c57,0x6c59,
+ 0x6c5f,0x6c60,0x6c50,0x6c55,0x6c61,0x6c5b,0x6c4d,0x6c4e,0x7070,0x725f,
+ 0x725d,0x767e,0x7af9,0x7c73,0x7cf8,0x7f36,0x7f8a,0x7fbd,0x8001,0x8003,
+ 0x800c,0x8012,0x8033,0x807f,0x8089,0x808b,0x808c,0x81e3,0x81ea,0x81f3,
+ 0x81fc,0x820c,0x821b,0x821f,0x826e,0x8272,0x827e,0x866b,0x8840,0x884c,
+ 0x8863,0x897f,0x9621,0x4e32,0x4ea8,0x4f4d,0x4f4f,0x4f47,0x4f57,0x4f5e,
+ 0x4f34,0x4f5b,0x4f55,0x4f30,0x4f50,0x4f51,0x4f3d,0x4f3a,0x4f38,0x4f43,
+ 0x4f54,0x4f3c,0x4f46,0x4f63
+ }
+ },
+ { /* ku 07 */
+ {
+ 0x4f5c,0x4f60,0x4f2f,0x4f4e,0x4f36,0x4f59,0x4f5d,0x4f48,0x4f5a,0x514c,
+ 0x514b,0x514d,0x5175,0x51b6,0x51b7,0x5225,0x5224,0x5229,0x522a,0x5228,
+ 0x52ab,0x52a9,0x52aa,0x52ac,0x5323,0x5373,0x5375,0x541d,0x542d,0x541e,
+ 0x543e,0x5426,0x544e,0x5427,0x5446,0x5443,0x5433,0x5448,0x5442,0x541b,
+ 0x5429,0x544a,0x5439,0x543b,0x5438,0x542e,0x5435,0x5436,0x5420,0x543c,
+ 0x5440,0x5431,0x542b,0x541f,0x542c,0x56ea,0x56f0,0x56e4,0x56eb,0x574a,
+ 0x5751,0x5740,0x574d
+ },{
+ 0x5747,0x574e,0x573e,0x5750,0x574f,0x573b,0x58ef,0x593e,0x599d,0x5992,
+ 0x59a8,0x599e,0x59a3,0x5999,0x5996,0x598d,0x59a4,0x5993,0x598a,0x59a5,
+ 0x5b5d,0x5b5c,0x5b5a,0x5b5b,0x5b8c,0x5b8b,0x5b8f,0x5c2c,0x5c40,0x5c41,
+ 0x5c3f,0x5c3e,0x5c90,0x5c91,0x5c94,0x5c8c,0x5deb,0x5e0c,0x5e8f,0x5e87,
+ 0x5e8a,0x5ef7,0x5f04,0x5f1f,0x5f64,0x5f62,0x5f77,0x5f79,0x5fd8,0x5fcc,
+ 0x5fd7,0x5fcd,0x5ff1,0x5feb,0x5ff8,0x5fea,0x6212,0x6211,0x6284,0x6297,
+ 0x6296,0x6280,0x6276,0x6289,0x626d,0x628a,0x627c,0x627e,0x6279,0x6273,
+ 0x6292,0x626f,0x6298,0x626e,0x6295,0x6293,0x6291,0x6286,0x6539,0x653b,
+ 0x6538,0x65f1,0x66f4,0x675f,0x674e,0x674f,0x6750,0x6751,0x675c,0x6756,
+ 0x675e,0x6749,0x6746,0x6760
+ }
+ },
+ { /* ku 08 */
+ {
+ 0x6753,0x6757,0x6b65,0x6bcf,0x6c42,0x6c5e,0x6c99,0x6c81,0x6c88,0x6c89,
+ 0x6c85,0x6c9b,0x6c6a,0x6c7a,0x6c90,0x6c70,0x6c8c,0x6c68,0x6c96,0x6c92,
+ 0x6c7d,0x6c83,0x6c72,0x6c7e,0x6c74,0x6c86,0x6c76,0x6c8d,0x6c94,0x6c98,
+ 0x6c82,0x7076,0x707c,0x707d,0x7078,0x7262,0x7261,0x7260,0x72c4,0x72c2,
+ 0x7396,0x752c,0x752b,0x7537,0x7538,0x7682,0x76ef,0x77e3,0x79c1,0x79c0,
+ 0x79bf,0x7a76,0x7cfb,0x7f55,0x8096,0x8093,0x809d,0x8098,0x809b,0x809a,
+ 0x80b2,0x826f,0x8292
+ },{
+ 0x828b,0x828d,0x898b,0x89d2,0x8a00,0x8c37,0x8c46,0x8c55,0x8c9d,0x8d64,
+ 0x8d70,0x8db3,0x8eab,0x8eca,0x8f9b,0x8fb0,0x8fc2,0x8fc6,0x8fc5,0x8fc4,
+ 0x5de1,0x9091,0x90a2,0x90aa,0x90a6,0x90a3,0x9149,0x91c6,0x91cc,0x9632,
+ 0x962e,0x9631,0x962a,0x962c,0x4e26,0x4e56,0x4e73,0x4e8b,0x4e9b,0x4e9e,
+ 0x4eab,0x4eac,0x4f6f,0x4f9d,0x4f8d,0x4f73,0x4f7f,0x4f6c,0x4f9b,0x4f8b,
+ 0x4f86,0x4f83,0x4f70,0x4f75,0x4f88,0x4f69,0x4f7b,0x4f96,0x4f7e,0x4f8f,
+ 0x4f91,0x4f7a,0x5154,0x5152,0x5155,0x5169,0x5177,0x5176,0x5178,0x51bd,
+ 0x51fd,0x523b,0x5238,0x5237,0x523a,0x5230,0x522e,0x5236,0x5241,0x52be,
+ 0x52bb,0x5352,0x5354,0x5353,0x5351,0x5366,0x5377,0x5378,0x5379,0x53d6,
+ 0x53d4,0x53d7,0x5473,0x5475
+ }
+ },
+ { /* ku 09 */
+ {
+ 0x5496,0x5478,0x5495,0x5480,0x547b,0x5477,0x5484,0x5492,0x5486,0x547c,
+ 0x5490,0x5471,0x5476,0x548c,0x549a,0x5462,0x5468,0x548b,0x547d,0x548e,
+ 0x56fa,0x5783,0x5777,0x576a,0x5769,0x5761,0x5766,0x5764,0x577c,0x591c,
+ 0x5949,0x5947,0x5948,0x5944,0x5954,0x59be,0x59bb,0x59d4,0x59b9,0x59ae,
+ 0x59d1,0x59c6,0x59d0,0x59cd,0x59cb,0x59d3,0x59ca,0x59af,0x59b3,0x59d2,
+ 0x59c5,0x5b5f,0x5b64,0x5b63,0x5b97,0x5b9a,0x5b98,0x5b9c,0x5b99,0x5b9b,
+ 0x5c1a,0x5c48,0x5c45
+ },{
+ 0x5c46,0x5cb7,0x5ca1,0x5cb8,0x5ca9,0x5cab,0x5cb1,0x5cb3,0x5e18,0x5e1a,
+ 0x5e16,0x5e15,0x5e1b,0x5e11,0x5e78,0x5e9a,0x5e97,0x5e9c,0x5e95,0x5e96,
+ 0x5ef6,0x5f26,0x5f27,0x5f29,0x5f80,0x5f81,0x5f7f,0x5f7c,0x5fdd,0x5fe0,
+ 0x5ffd,0x5ff5,0x5fff,0x600f,0x6014,0x602f,0x6035,0x6016,0x602a,0x6015,
+ 0x6021,0x6027,0x6029,0x602b,0x601b,0x6216,0x6215,0x623f,0x623e,0x6240,
+ 0x627f,0x62c9,0x62cc,0x62c4,0x62bf,0x62c2,0x62b9,0x62d2,0x62db,0x62ab,
+ 0x62d3,0x62d4,0x62cb,0x62c8,0x62a8,0x62bd,0x62bc,0x62d0,0x62d9,0x62c7,
+ 0x62cd,0x62b5,0x62da,0x62b1,0x62d8,0x62d6,0x62d7,0x62c6,0x62ac,0x62ce,
+ 0x653e,0x65a7,0x65bc,0x65fa,0x6614,0x6613,0x660c,0x6606,0x6602,0x660e,
+ 0x6600,0x660f,0x6615,0x660a
+ }
+ },
+ { /* ku 0a */
+ {
+ 0x6607,0x670d,0x670b,0x676d,0x678b,0x6795,0x6771,0x679c,0x6773,0x6777,
+ 0x6787,0x679d,0x6797,0x676f,0x6770,0x677f,0x6789,0x677e,0x6790,0x6775,
+ 0x679a,0x6793,0x677c,0x676a,0x6772,0x6b23,0x6b66,0x6b67,0x6b7f,0x6c13,
+ 0x6c1b,0x6ce3,0x6ce8,0x6cf3,0x6cb1,0x6ccc,0x6ce5,0x6cb3,0x6cbd,0x6cbe,
+ 0x6cbc,0x6ce2,0x6cab,0x6cd5,0x6cd3,0x6cb8,0x6cc4,0x6cb9,0x6cc1,0x6cae,
+ 0x6cd7,0x6cc5,0x6cf1,0x6cbf,0x6cbb,0x6ce1,0x6cdb,0x6cca,0x6cac,0x6cef,
+ 0x6cdc,0x6cd6,0x6ce0
+ },{
+ 0x7095,0x708e,0x7092,0x708a,0x7099,0x722c,0x722d,0x7238,0x7248,0x7267,
+ 0x7269,0x72c0,0x72ce,0x72d9,0x72d7,0x72d0,0x73a9,0x73a8,0x739f,0x73ab,
+ 0x73a5,0x753d,0x759d,0x7599,0x759a,0x7684,0x76c2,0x76f2,0x76f4,0x77e5,
+ 0x77fd,0x793e,0x7940,0x7941,0x79c9,0x79c8,0x7a7a,0x7a79,0x7afa,0x7cfe,
+ 0x7f54,0x7f8c,0x7f8b,0x8005,0x80ba,0x80a5,0x80a2,0x80b1,0x80a1,0x80ab,
+ 0x80a9,0x80b4,0x80aa,0x80af,0x81e5,0x81fe,0x820d,0x82b3,0x829d,0x8299,
+ 0x82ad,0x82bd,0x829f,0x82b9,0x82b1,0x82ac,0x82a5,0x82af,0x82b8,0x82a3,
+ 0x82b0,0x82be,0x82b7,0x864e,0x8671,0x521d,0x8868,0x8ecb,0x8fce,0x8fd4,
+ 0x8fd1,0x90b5,0x90b8,0x90b1,0x90b6,0x91c7,0x91d1,0x9577,0x9580,0x961c,
+ 0x9640,0x963f,0x963b,0x9644
+ }
+ },
+ { /* ku 0b */
+ {
+ 0x9642,0x96b9,0x96e8,0x9752,0x975e,0x4e9f,0x4ead,0x4eae,0x4fe1,0x4fb5,
+ 0x4faf,0x4fbf,0x4fe0,0x4fd1,0x4fcf,0x4fdd,0x4fc3,0x4fb6,0x4fd8,0x4fdf,
+ 0x4fca,0x4fd7,0x4fae,0x4fd0,0x4fc4,0x4fc2,0x4fda,0x4fce,0x4fde,0x4fb7,
+ 0x5157,0x5192,0x5191,0x51a0,0x524e,0x5243,0x524a,0x524d,0x524c,0x524b,
+ 0x5247,0x52c7,0x52c9,0x52c3,0x52c1,0x530d,0x5357,0x537b,0x539a,0x53db,
+ 0x54ac,0x54c0,0x54a8,0x54ce,0x54c9,0x54b8,0x54a6,0x54b3,0x54c7,0x54c2,
+ 0x54bd,0x54aa,0x54c1
+ },{
+ 0x54c4,0x54c8,0x54af,0x54ab,0x54b1,0x54bb,0x54a9,0x54a7,0x54bf,0x56ff,
+ 0x5782,0x578b,0x57a0,0x57a3,0x57a2,0x57ce,0x57ae,0x5793,0x5955,0x5951,
+ 0x594f,0x594e,0x5950,0x59dc,0x59d8,0x59ff,0x59e3,0x59e8,0x5a03,0x59e5,
+ 0x59ea,0x59da,0x59e6,0x5a01,0x59fb,0x5b69,0x5ba3,0x5ba6,0x5ba4,0x5ba2,
+ 0x5ba5,0x5c01,0x5c4e,0x5c4f,0x5c4d,0x5c4b,0x5cd9,0x5cd2,0x5df7,0x5e1d,
+ 0x5e25,0x5e1f,0x5e7d,0x5ea0,0x5ea6,0x5efa,0x5f08,0x5f2d,0x5f65,0x5f88,
+ 0x5f85,0x5f8a,0x5f8b,0x5f87,0x5f8c,0x5f89,0x6012,0x601d,0x6020,0x6025,
+ 0x600e,0x6028,0x604d,0x6070,0x6068,0x6062,0x6046,0x6043,0x606c,0x606b,
+ 0x606a,0x6064,0x6241,0x62dc,0x6316,0x6309,0x62fc,0x62ed,0x6301,0x62ee,
+ 0x62fd,0x6307,0x62f1,0x62f7
+ }
+ },
+ { /* ku 0c */
+ {
+ 0x62ef,0x62ec,0x62fe,0x62f4,0x6311,0x6302,0x653f,0x6545,0x65ab,0x65bd,
+ 0x65e2,0x6625,0x662d,0x6620,0x6627,0x662f,0x661f,0x6628,0x6631,0x6624,
+ 0x66f7,0x67ff,0x67d3,0x67f1,0x67d4,0x67d0,0x67ec,0x67b6,0x67af,0x67f5,
+ 0x67e9,0x67ef,0x67c4,0x67d1,0x67b4,0x67da,0x67e5,0x67b8,0x67cf,0x67de,
+ 0x67f3,0x67b0,0x67d9,0x67e2,0x67dd,0x67d2,0x6b6a,0x6b83,0x6b86,0x6bb5,
+ 0x6bd2,0x6bd7,0x6c1f,0x6cc9,0x6d0b,0x6d32,0x6d2a,0x6d41,0x6d25,0x6d0c,
+ 0x6d31,0x6d1e,0x6d17
+ },{
+ 0x6d3b,0x6d3d,0x6d3e,0x6d36,0x6d1b,0x6cf5,0x6d39,0x6d27,0x6d38,0x6d29,
+ 0x6d2e,0x6d35,0x6d0e,0x6d2b,0x70ab,0x70ba,0x70b3,0x70ac,0x70af,0x70ad,
+ 0x70b8,0x70ae,0x70a4,0x7230,0x7272,0x726f,0x7274,0x72e9,0x72e0,0x72e1,
+ 0x73b7,0x73ca,0x73bb,0x73b2,0x73cd,0x73c0,0x73b3,0x751a,0x752d,0x754f,
+ 0x754c,0x754e,0x754b,0x75ab,0x75a4,0x75a5,0x75a2,0x75a3,0x7678,0x7686,
+ 0x7687,0x7688,0x76c8,0x76c6,0x76c3,0x76c5,0x7701,0x76f9,0x76f8,0x7709,
+ 0x770b,0x76fe,0x76fc,0x7707,0x77dc,0x7802,0x7814,0x780c,0x780d,0x7946,
+ 0x7949,0x7948,0x7947,0x79b9,0x79ba,0x79d1,0x79d2,0x79cb,0x7a7f,0x7a81,
+ 0x7aff,0x7afd,0x7c7d,0x7d02,0x7d05,0x7d00,0x7d09,0x7d07,0x7d04,0x7d06,
+ 0x7f38,0x7f8e,0x7fbf,0x8004
+ }
+ },
+ { /* ku 0d */
+ {
+ 0x8010,0x800d,0x8011,0x8036,0x80d6,0x80e5,0x80da,0x80c3,0x80c4,0x80cc,
+ 0x80e1,0x80db,0x80ce,0x80de,0x80e4,0x80dd,0x81f4,0x8222,0x82e7,0x8303,
+ 0x8305,0x82e3,0x82db,0x82e6,0x8304,0x82e5,0x8302,0x8309,0x82d2,0x82d7,
+ 0x82f1,0x8301,0x82dc,0x82d4,0x82d1,0x82de,0x82d3,0x82df,0x82ef,0x8306,
+ 0x8650,0x8679,0x867b,0x867a,0x884d,0x886b,0x8981,0x89d4,0x8a08,0x8a02,
+ 0x8a03,0x8c9e,0x8ca0,0x8d74,0x8d73,0x8db4,0x8ecd,0x8ecc,0x8ff0,0x8fe6,
+ 0x8fe2,0x8fea,0x8fe5
+ },{
+ 0x8fed,0x8feb,0x8fe4,0x8fe8,0x90ca,0x90ce,0x90c1,0x90c3,0x914b,0x914a,
+ 0x91cd,0x9582,0x9650,0x964b,0x964c,0x964d,0x9762,0x9769,0x97cb,0x97ed,
+ 0x97f3,0x9801,0x98a8,0x98db,0x98df,0x9996,0x9999,0x4e58,0x4eb3,0x500c,
+ 0x500d,0x5023,0x4fef,0x5026,0x5025,0x4ff8,0x5029,0x5016,0x5006,0x503c,
+ 0x501f,0x501a,0x5012,0x5011,0x4ffa,0x5000,0x5014,0x5028,0x4ff1,0x5021,
+ 0x500b,0x5019,0x5018,0x4ff3,0x4fee,0x502d,0x502a,0x4ffe,0x502b,0x5009,
+ 0x517c,0x51a4,0x51a5,0x51a2,0x51cd,0x51cc,0x51c6,0x51cb,0x5256,0x525c,
+ 0x5254,0x525b,0x525d,0x532a,0x537f,0x539f,0x539d,0x53df,0x54e8,0x5510,
+ 0x5501,0x5537,0x54fc,0x54e5,0x54f2,0x5506,0x54fa,0x5514,0x54e9,0x54ed,
+ 0x54e1,0x5509,0x54ee,0x54ea
+ }
+ },
+ { /* ku 0e */
+ {
+ 0x54e6,0x5527,0x5507,0x54fd,0x550f,0x5703,0x5704,0x57c2,0x57d4,0x57cb,
+ 0x57c3,0x5809,0x590f,0x5957,0x5958,0x595a,0x5a11,0x5a18,0x5a1c,0x5a1f,
+ 0x5a1b,0x5a13,0x59ec,0x5a20,0x5a23,0x5a29,0x5a25,0x5a0c,0x5a09,0x5b6b,
+ 0x5c58,0x5bb0,0x5bb3,0x5bb6,0x5bb4,0x5bae,0x5bb5,0x5bb9,0x5bb8,0x5c04,
+ 0x5c51,0x5c55,0x5c50,0x5ced,0x5cfd,0x5cfb,0x5cea,0x5ce8,0x5cf0,0x5cf6,
+ 0x5d01,0x5cf4,0x5dee,0x5e2d,0x5e2b,0x5eab,0x5ead,0x5ea7,0x5f31,0x5f92,
+ 0x5f91,0x5f90,0x6059
+ },{
+ 0x6063,0x6065,0x6050,0x6055,0x606d,0x6069,0x606f,0x6084,0x609f,0x609a,
+ 0x608d,0x6094,0x608c,0x6085,0x6096,0x6247,0x62f3,0x6308,0x62ff,0x634e,
+ 0x633e,0x632f,0x6355,0x6342,0x6346,0x634f,0x6349,0x633a,0x6350,0x633d,
+ 0x632a,0x632b,0x6328,0x634d,0x634c,0x6548,0x6549,0x6599,0x65c1,0x65c5,
+ 0x6642,0x6649,0x664f,0x6643,0x6652,0x664c,0x6645,0x6641,0x66f8,0x6714,
+ 0x6715,0x6717,0x6821,0x6838,0x6848,0x6846,0x6853,0x6839,0x6842,0x6854,
+ 0x6829,0x68b3,0x6817,0x684c,0x6851,0x683d,0x67f4,0x6850,0x6840,0x683c,
+ 0x6843,0x682a,0x6845,0x6813,0x6818,0x6841,0x6b8a,0x6b89,0x6bb7,0x6c23,
+ 0x6c27,0x6c28,0x6c26,0x6c24,0x6cf0,0x6d6a,0x6d95,0x6d88,0x6d87,0x6d66,
+ 0x6d78,0x6d77,0x6d59,0x6d93
+ }
+ },
+ { /* ku 0f */
+ {
+ 0x6d6c,0x6d89,0x6d6e,0x6d5a,0x6d74,0x6d69,0x6d8c,0x6d8a,0x6d79,0x6d85,
+ 0x6d65,0x6d94,0x70ca,0x70d8,0x70e4,0x70d9,0x70c8,0x70cf,0x7239,0x7279,
+ 0x72fc,0x72f9,0x72fd,0x72f8,0x72f7,0x7386,0x73ed,0x7409,0x73ee,0x73e0,
+ 0x73ea,0x73de,0x7554,0x755d,0x755c,0x755a,0x7559,0x75be,0x75c5,0x75c7,
+ 0x75b2,0x75b3,0x75bd,0x75bc,0x75b9,0x75c2,0x75b8,0x768b,0x76b0,0x76ca,
+ 0x76cd,0x76ce,0x7729,0x771f,0x7720,0x7728,0x77e9,0x7830,0x7827,0x7838,
+ 0x781d,0x7834,0x7837
+ },{
+ 0x7825,0x782d,0x7820,0x781f,0x7832,0x7955,0x7950,0x7960,0x795f,0x7956,
+ 0x795e,0x795d,0x7957,0x795a,0x79e4,0x79e3,0x79e7,0x79df,0x79e6,0x79e9,
+ 0x79d8,0x7a84,0x7a88,0x7ad9,0x7b06,0x7b11,0x7c89,0x7d21,0x7d17,0x7d0b,
+ 0x7d0a,0x7d20,0x7d22,0x7d14,0x7d10,0x7d15,0x7d1a,0x7d1c,0x7d0d,0x7d19,
+ 0x7d1b,0x7f3a,0x7f5f,0x7f94,0x7fc5,0x7fc1,0x8006,0x8018,0x8015,0x8019,
+ 0x8017,0x803d,0x803f,0x80f1,0x8102,0x80f0,0x8105,0x80ed,0x80f4,0x8106,
+ 0x80f8,0x80f3,0x8108,0x80fd,0x810a,0x80fc,0x80ef,0x81ed,0x81ec,0x8200,
+ 0x8210,0x822a,0x822b,0x8228,0x822c,0x82bb,0x832b,0x8352,0x8354,0x834a,
+ 0x8338,0x8350,0x8349,0x8335,0x8334,0x834f,0x8332,0x8339,0x8336,0x8317,
+ 0x8340,0x8331,0x8328,0x8343
+ }
+ },
+ { /* ku 10 */
+ {
+ 0x8654,0x868a,0x86aa,0x8693,0x86a4,0x86a9,0x868c,0x86a3,0x869c,0x8870,
+ 0x8877,0x8881,0x8882,0x887d,0x8879,0x8a18,0x8a10,0x8a0e,0x8a0c,0x8a15,
+ 0x8a0a,0x8a17,0x8a13,0x8a16,0x8a0f,0x8a11,0x8c48,0x8c7a,0x8c79,0x8ca1,
+ 0x8ca2,0x8d77,0x8eac,0x8ed2,0x8ed4,0x8ecf,0x8fb1,0x9001,0x9006,0x8ff7,
+ 0x9000,0x8ffa,0x8ff4,0x9003,0x8ffd,0x9005,0x8ff8,0x9095,0x90e1,0x90dd,
+ 0x90e2,0x9152,0x914d,0x914c,0x91d8,0x91dd,0x91d7,0x91dc,0x91d9,0x9583,
+ 0x9662,0x9663,0x9661
+ },{
+ 0x965b,0x965d,0x9664,0x9658,0x965e,0x96bb,0x98e2,0x99ac,0x9aa8,0x9ad8,
+ 0x9b25,0x9b32,0x9b3c,0x4e7e,0x507a,0x507d,0x505c,0x5047,0x5043,0x504c,
+ 0x505a,0x5049,0x5065,0x5076,0x504e,0x5055,0x5075,0x5074,0x5077,0x504f,
+ 0x500f,0x506f,0x506d,0x515c,0x5195,0x51f0,0x526a,0x526f,0x52d2,0x52d9,
+ 0x52d8,0x52d5,0x5310,0x530f,0x5319,0x533f,0x5340,0x533e,0x53c3,0x66fc,
+ 0x5546,0x556a,0x5566,0x5544,0x555e,0x5561,0x5543,0x554a,0x5531,0x5556,
+ 0x554f,0x5555,0x552f,0x5564,0x5538,0x552e,0x555c,0x552c,0x5563,0x5533,
+ 0x5541,0x5557,0x5708,0x570b,0x5709,0x57df,0x5805,0x580a,0x5806,0x57e0,
+ 0x57e4,0x57fa,0x5802,0x5835,0x57f7,0x57f9,0x5920,0x5962,0x5a36,0x5a41,
+ 0x5a49,0x5a66,0x5a6a,0x5a40
+ }
+ },
+ { /* ku 11 */
+ {
+ 0x5a3c,0x5a62,0x5a5a,0x5a46,0x5a4a,0x5b70,0x5bc7,0x5bc5,0x5bc4,0x5bc2,
+ 0x5bbf,0x5bc6,0x5c09,0x5c08,0x5c07,0x5c60,0x5c5c,0x5c5d,0x5d07,0x5d06,
+ 0x5d0e,0x5d1b,0x5d16,0x5d22,0x5d11,0x5d29,0x5d14,0x5d19,0x5d24,0x5d27,
+ 0x5d17,0x5de2,0x5e38,0x5e36,0x5e33,0x5e37,0x5eb7,0x5eb8,0x5eb6,0x5eb5,
+ 0x5ebe,0x5f35,0x5f37,0x5f57,0x5f6c,0x5f69,0x5f6b,0x5f97,0x5f99,0x5f9e,
+ 0x5f98,0x5fa1,0x5fa0,0x5f9c,0x607f,0x60a3,0x6089,0x60a0,0x60a8,0x60cb,
+ 0x60b4,0x60e6,0x60bd
+ },{
+ 0x60c5,0x60bb,0x60b5,0x60dc,0x60bc,0x60d8,0x60d5,0x60c6,0x60df,0x60b8,
+ 0x60da,0x60c7,0x621a,0x621b,0x6248,0x63a0,0x63a7,0x6372,0x6396,0x63a2,
+ 0x63a5,0x6377,0x6367,0x6398,0x63aa,0x6371,0x63a9,0x6389,0x6383,0x639b,
+ 0x636b,0x63a8,0x6384,0x6388,0x6399,0x63a1,0x63ac,0x6392,0x638f,0x6380,
+ 0x637b,0x6369,0x6368,0x637a,0x655d,0x6556,0x6551,0x6559,0x6557,0x555f,
+ 0x654f,0x6558,0x6555,0x6554,0x659c,0x659b,0x65ac,0x65cf,0x65cb,0x65cc,
+ 0x65ce,0x665d,0x665a,0x6664,0x6668,0x6666,0x665e,0x66f9,0x52d7,0x671b,
+ 0x6881,0x68af,0x68a2,0x6893,0x68b5,0x687f,0x6876,0x68b1,0x68a7,0x6897,
+ 0x68b0,0x6883,0x68c4,0x68ad,0x6886,0x6885,0x6894,0x689d,0x68a8,0x689f,
+ 0x68a1,0x6882,0x6b32,0x6bba
+ }
+ },
+ { /* ku 12 */
+ {
+ 0x6beb,0x6bec,0x6c2b,0x6d8e,0x6dbc,0x6df3,0x6dd9,0x6db2,0x6de1,0x6dcc,
+ 0x6de4,0x6dfb,0x6dfa,0x6e05,0x6dc7,0x6dcb,0x6daf,0x6dd1,0x6dae,0x6dde,
+ 0x6df9,0x6db8,0x6df7,0x6df5,0x6dc5,0x6dd2,0x6e1a,0x6db5,0x6dda,0x6deb,
+ 0x6dd8,0x6dea,0x6df1,0x6dee,0x6de8,0x6dc6,0x6dc4,0x6daa,0x6dec,0x6dbf,
+ 0x6de6,0x70f9,0x7109,0x710a,0x70fd,0x70ef,0x723d,0x727d,0x7281,0x731c,
+ 0x731b,0x7316,0x7313,0x7319,0x7387,0x7405,0x740a,0x7403,0x7406,0x73fe,
+ 0x740d,0x74e0,0x74f6
+ },{
+ 0x74f7,0x751c,0x7522,0x7565,0x7566,0x7562,0x7570,0x758f,0x75d4,0x75d5,
+ 0x75b5,0x75ca,0x75cd,0x768e,0x76d4,0x76d2,0x76db,0x7737,0x773e,0x773c,
+ 0x7736,0x7738,0x773a,0x786b,0x7843,0x784e,0x7965,0x7968,0x796d,0x79fb,
+ 0x7a92,0x7a95,0x7b20,0x7b28,0x7b1b,0x7b2c,0x7b26,0x7b19,0x7b1e,0x7b2e,
+ 0x7c92,0x7c97,0x7c95,0x7d46,0x7d43,0x7d71,0x7d2e,0x7d39,0x7d3c,0x7d40,
+ 0x7d30,0x7d33,0x7d44,0x7d2f,0x7d42,0x7d32,0x7d31,0x7f3d,0x7f9e,0x7f9a,
+ 0x7fcc,0x7fce,0x7fd2,0x801c,0x804a,0x8046,0x812f,0x8116,0x8123,0x812b,
+ 0x8129,0x8130,0x8124,0x8202,0x8235,0x8237,0x8236,0x8239,0x838e,0x839e,
+ 0x8398,0x8378,0x83a2,0x8396,0x83bd,0x83ab,0x8392,0x838a,0x8393,0x8389,
+ 0x83a0,0x8377,0x837b,0x837c
+ }
+ },
+ { /* ku 13 */
+ {
+ 0x8386,0x83a7,0x8655,0x5f6a,0x86c7,0x86c0,0x86b6,0x86c4,0x86b5,0x86c6,
+ 0x86cb,0x86b1,0x86af,0x86c9,0x8853,0x889e,0x8888,0x88ab,0x8892,0x8896,
+ 0x888d,0x888b,0x8993,0x898f,0x8a2a,0x8a1d,0x8a23,0x8a25,0x8a31,0x8a2d,
+ 0x8a1f,0x8a1b,0x8a22,0x8c49,0x8c5a,0x8ca9,0x8cac,0x8cab,0x8ca8,0x8caa,
+ 0x8ca7,0x8d67,0x8d66,0x8dbe,0x8dba,0x8edb,0x8edf,0x9019,0x900d,0x901a,
+ 0x9017,0x9023,0x901f,0x901d,0x9010,0x9015,0x901e,0x9020,0x900f,0x9022,
+ 0x9016,0x901b,0x9014
+ },{
+ 0x90e8,0x90ed,0x90fd,0x9157,0x91ce,0x91f5,0x91e6,0x91e3,0x91e7,0x91ed,
+ 0x91e9,0x9589,0x966a,0x9675,0x9673,0x9678,0x9670,0x9674,0x9676,0x9677,
+ 0x966c,0x96c0,0x96ea,0x96e9,0x7ae0,0x7adf,0x9802,0x9803,0x9b5a,0x9ce5,
+ 0x9e75,0x9e7f,0x9ea5,0x9ebb,0x50a2,0x508d,0x5085,0x5099,0x5091,0x5080,
+ 0x5096,0x5098,0x509a,0x6700,0x51f1,0x5272,0x5274,0x5275,0x5269,0x52de,
+ 0x52dd,0x52db,0x535a,0x53a5,0x557b,0x5580,0x55a7,0x557c,0x558a,0x559d,
+ 0x5598,0x5582,0x559c,0x55aa,0x5594,0x5587,0x558b,0x5583,0x55b3,0x55ae,
+ 0x559f,0x553e,0x55b2,0x559a,0x55bb,0x55ac,0x55b1,0x557e,0x5589,0x55ab,
+ 0x5599,0x570d,0x582f,0x582a,0x5834,0x5824,0x5830,0x5831,0x5821,0x581d,
+ 0x5820,0x58f9,0x58fa,0x5960
+ }
+ },
+ { /* ku 14 */
+ {
+ 0x5a77,0x5a9a,0x5a7f,0x5a92,0x5a9b,0x5aa7,0x5b73,0x5b71,0x5bd2,0x5bcc,
+ 0x5bd3,0x5bd0,0x5c0a,0x5c0b,0x5c31,0x5d4c,0x5d50,0x5d34,0x5d47,0x5dfd,
+ 0x5e45,0x5e3d,0x5e40,0x5e43,0x5e7e,0x5eca,0x5ec1,0x5ec2,0x5ec4,0x5f3c,
+ 0x5f6d,0x5fa9,0x5faa,0x5fa8,0x60d1,0x60e1,0x60b2,0x60b6,0x60e0,0x611c,
+ 0x6123,0x60fa,0x6115,0x60f0,0x60fb,0x60f4,0x6168,0x60f1,0x610e,0x60f6,
+ 0x6109,0x6100,0x6112,0x621f,0x6249,0x63a3,0x638c,0x63cf,0x63c0,0x63e9,
+ 0x63c9,0x63c6,0x63cd
+ },{
+ 0x63d2,0x63e3,0x63d0,0x63e1,0x63d6,0x63ed,0x63ee,0x6376,0x63f4,0x63ea,
+ 0x63db,0x6452,0x63da,0x63f9,0x655e,0x6566,0x6562,0x6563,0x6591,0x6590,
+ 0x65af,0x666e,0x6670,0x6674,0x6676,0x666f,0x6691,0x667a,0x667e,0x6677,
+ 0x66fe,0x66ff,0x671f,0x671d,0x68fa,0x68d5,0x68e0,0x68d8,0x68d7,0x6905,
+ 0x68df,0x68f5,0x68ee,0x68e7,0x68f9,0x68d2,0x68f2,0x68e3,0x68cb,0x68cd,
+ 0x690d,0x6912,0x690e,0x68c9,0x68da,0x696e,0x68fb,0x6b3e,0x6b3a,0x6b3d,
+ 0x6b98,0x6b96,0x6bbc,0x6bef,0x6c2e,0x6c2f,0x6c2c,0x6e2f,0x6e38,0x6e54,
+ 0x6e21,0x6e32,0x6e67,0x6e4a,0x6e20,0x6e25,0x6e23,0x6e1b,0x6e5b,0x6e58,
+ 0x6e24,0x6e56,0x6e6e,0x6e2d,0x6e26,0x6e6f,0x6e34,0x6e4d,0x6e3a,0x6e2c,
+ 0x6e43,0x6e1d,0x6e3e,0x6ecb
+ }
+ },
+ { /* ku 15 */
+ {
+ 0x6e89,0x6e19,0x6e4e,0x6e63,0x6e44,0x6e72,0x6e69,0x6e5f,0x7119,0x711a,
+ 0x7126,0x7130,0x7121,0x7136,0x716e,0x711c,0x724c,0x7284,0x7280,0x7336,
+ 0x7325,0x7334,0x7329,0x743a,0x742a,0x7433,0x7422,0x7425,0x7435,0x7436,
+ 0x7434,0x742f,0x741b,0x7426,0x7428,0x7525,0x7526,0x756b,0x756a,0x75e2,
+ 0x75db,0x75e3,0x75d9,0x75d8,0x75de,0x75e0,0x767b,0x767c,0x7696,0x7693,
+ 0x76b4,0x76dc,0x774f,0x77ed,0x785d,0x786c,0x786f,0x7a0d,0x7a08,0x7a0b,
+ 0x7a05,0x7a00,0x7a98
+ },{
+ 0x7a97,0x7a96,0x7ae5,0x7ae3,0x7b49,0x7b56,0x7b46,0x7b50,0x7b52,0x7b54,
+ 0x7b4d,0x7b4b,0x7b4f,0x7b51,0x7c9f,0x7ca5,0x7d5e,0x7d50,0x7d68,0x7d55,
+ 0x7d2b,0x7d6e,0x7d72,0x7d61,0x7d66,0x7d62,0x7d70,0x7d73,0x5584,0x7fd4,
+ 0x7fd5,0x800b,0x8052,0x8085,0x8155,0x8154,0x814b,0x8151,0x814e,0x8139,
+ 0x8146,0x813e,0x814c,0x8153,0x8174,0x8212,0x821c,0x83e9,0x8403,0x83f8,
+ 0x840d,0x83e0,0x83c5,0x840b,0x83c1,0x83ef,0x83f1,0x83f4,0x8457,0x840a,
+ 0x83f0,0x840c,0x83cc,0x83fd,0x83f2,0x83ca,0x8438,0x840e,0x8404,0x83dc,
+ 0x8407,0x83d4,0x83df,0x865b,0x86df,0x86d9,0x86ed,0x86d4,0x86db,0x86e4,
+ 0x86d0,0x86de,0x8857,0x88c1,0x88c2,0x88b1,0x8983,0x8996,0x8a3b,0x8a60,
+ 0x8a55,0x8a5e,0x8a3c,0x8a41
+ }
+ },
+ { /* ku 16 */
+ {
+ 0x8a54,0x8a5b,0x8a50,0x8a46,0x8a34,0x8a3a,0x8a36,0x8a56,0x8c61,0x8c82,
+ 0x8caf,0x8cbc,0x8cb3,0x8cbd,0x8cc1,0x8cbb,0x8cc0,0x8cb4,0x8cb7,0x8cb6,
+ 0x8cbf,0x8cb8,0x8d8a,0x8d85,0x8d81,0x8dce,0x8ddd,0x8dcb,0x8dda,0x8dd1,
+ 0x8dcc,0x8ddb,0x8dc6,0x8efb,0x8ef8,0x8efc,0x8f9c,0x902e,0x9035,0x9031,
+ 0x9038,0x9032,0x9036,0x9102,0x90f5,0x9109,0x90fe,0x9163,0x9165,0x91cf,
+ 0x9214,0x9215,0x9223,0x9209,0x921e,0x920d,0x9210,0x9207,0x9211,0x9594,
+ 0x958f,0x958b,0x9591
+ },{
+ 0x9593,0x9592,0x958e,0x968a,0x968e,0x968b,0x967d,0x9685,0x9686,0x968d,
+ 0x9672,0x9684,0x96c1,0x96c5,0x96c4,0x96c6,0x96c7,0x96ef,0x96f2,0x97cc,
+ 0x9805,0x9806,0x9808,0x98e7,0x98ea,0x98ef,0x98e9,0x98f2,0x98ed,0x99ae,
+ 0x99ad,0x9ec3,0x9ecd,0x9ed1,0x4e82,0x50ad,0x50b5,0x50b2,0x50b3,0x50c5,
+ 0x50be,0x50ac,0x50b7,0x50bb,0x50af,0x50c7,0x527f,0x5277,0x527d,0x52df,
+ 0x52e6,0x52e4,0x52e2,0x52e3,0x532f,0x55df,0x55e8,0x55d3,0x55e6,0x55ce,
+ 0x55dc,0x55c7,0x55d1,0x55e3,0x55e4,0x55ef,0x55da,0x55e1,0x55c5,0x55c6,
+ 0x55e5,0x55c9,0x5712,0x5713,0x585e,0x5851,0x5858,0x5857,0x585a,0x5854,
+ 0x586b,0x584c,0x586d,0x584a,0x5862,0x5852,0x584b,0x5967,0x5ac1,0x5ac9,
+ 0x5acc,0x5abe,0x5abd,0x5abc
+ }
+ },
+ { /* ku 17 */
+ {
+ 0x5ab3,0x5ac2,0x5ab2,0x5d69,0x5d6f,0x5e4c,0x5e79,0x5ec9,0x5ec8,0x5f12,
+ 0x5f59,0x5fac,0x5fae,0x611a,0x610f,0x6148,0x611f,0x60f3,0x611b,0x60f9,
+ 0x6101,0x6108,0x614e,0x614c,0x6144,0x614d,0x613e,0x6134,0x6127,0x610d,
+ 0x6106,0x6137,0x6221,0x6222,0x6413,0x643e,0x641e,0x642a,0x642d,0x643d,
+ 0x642c,0x640f,0x641c,0x6414,0x640d,0x6436,0x6416,0x6417,0x6406,0x656c,
+ 0x659f,0x65b0,0x6697,0x6689,0x6687,0x6688,0x6696,0x6684,0x6698,0x668d,
+ 0x6703,0x6994,0x696d
+ },{
+ 0x695a,0x6977,0x6960,0x6954,0x6975,0x6930,0x6982,0x694a,0x6968,0x696b,
+ 0x695e,0x6953,0x6979,0x6986,0x695d,0x6963,0x695b,0x6b47,0x6b72,0x6bc0,
+ 0x6bbf,0x6bd3,0x6bfd,0x6ea2,0x6eaf,0x6ed3,0x6eb6,0x6ec2,0x6e90,0x6e9d,
+ 0x6ec7,0x6ec5,0x6ea5,0x6e98,0x6ebc,0x6eba,0x6eab,0x6ed1,0x6e96,0x6e9c,
+ 0x6ec4,0x6ed4,0x6eaa,0x6ea7,0x6eb4,0x714e,0x7159,0x7169,0x7164,0x7149,
+ 0x7167,0x715c,0x716c,0x7166,0x714c,0x7165,0x715e,0x7146,0x7168,0x7156,
+ 0x723a,0x7252,0x7337,0x7345,0x733f,0x733e,0x746f,0x745a,0x7455,0x745f,
+ 0x745e,0x7441,0x743f,0x7459,0x745b,0x745c,0x7576,0x7578,0x7600,0x75f0,
+ 0x7601,0x75f2,0x75f1,0x75fa,0x75ff,0x75f4,0x75f3,0x76de,0x76df,0x775b,
+ 0x776b,0x7766,0x775e,0x7763
+ }
+ },
+ { /* ku 18 */
+ {
+ 0x7779,0x776a,0x776c,0x775c,0x7765,0x7768,0x7762,0x77ee,0x788e,0x78b0,
+ 0x7897,0x7898,0x788c,0x7889,0x787c,0x7891,0x7893,0x787f,0x797a,0x797f,
+ 0x7981,0x842c,0x79bd,0x7a1c,0x7a1a,0x7a20,0x7a14,0x7a1f,0x7a1e,0x7a9f,
+ 0x7aa0,0x7b77,0x7bc0,0x7b60,0x7b6e,0x7b67,0x7cb1,0x7cb3,0x7cb5,0x7d93,
+ 0x7d79,0x7d91,0x7d81,0x7d8f,0x7d5b,0x7f6e,0x7f69,0x7f6a,0x7f72,0x7fa9,
+ 0x7fa8,0x7fa4,0x8056,0x8058,0x8086,0x8084,0x8171,0x8170,0x8178,0x8165,
+ 0x816e,0x8173,0x816b
+ },{
+ 0x8179,0x817a,0x8166,0x8205,0x8247,0x8482,0x8477,0x843d,0x8431,0x8475,
+ 0x8466,0x846b,0x8449,0x846c,0x845b,0x843c,0x8435,0x8461,0x8463,0x8469,
+ 0x846d,0x8446,0x865e,0x865c,0x865f,0x86f9,0x8713,0x8708,0x8707,0x8700,
+ 0x86fe,0x86fb,0x8702,0x8703,0x8706,0x870a,0x8859,0x88df,0x88d4,0x88d9,
+ 0x88dc,0x88d8,0x88dd,0x88e1,0x88ca,0x88d5,0x88d2,0x899c,0x89e3,0x8a6b,
+ 0x8a72,0x8a73,0x8a66,0x8a69,0x8a70,0x8a87,0x8a7c,0x8a63,0x8aa0,0x8a71,
+ 0x8a85,0x8a6d,0x8a62,0x8a6e,0x8a6c,0x8a79,0x8a7b,0x8a3e,0x8a68,0x8c62,
+ 0x8c8a,0x8c89,0x8cca,0x8cc7,0x8cc8,0x8cc4,0x8cb2,0x8cc3,0x8cc2,0x8cc5,
+ 0x8de1,0x8ddf,0x8de8,0x8def,0x8df3,0x8dfa,0x8dea,0x8de4,0x8de6,0x8eb2,
+ 0x8f03,0x8f09,0x8efe,0x8f0a
+ }
+ },
+ { /* ku 19 */
+ {
+ 0x8f9f,0x8fb2,0x904b,0x904a,0x9053,0x9042,0x9054,0x903c,0x9055,0x9050,
+ 0x9047,0x904f,0x904e,0x904d,0x9051,0x903e,0x9041,0x9112,0x9117,0x916c,
+ 0x916a,0x9169,0x91c9,0x9237,0x9257,0x9238,0x923d,0x9240,0x923e,0x925b,
+ 0x924b,0x9264,0x9251,0x9234,0x9249,0x924d,0x9245,0x9239,0x923f,0x925a,
+ 0x9598,0x9698,0x9694,0x9695,0x96cd,0x96cb,0x96c9,0x96ca,0x96f7,0x96fb,
+ 0x96f9,0x96f6,0x9756,0x9774,0x9776,0x9810,0x9811,0x9813,0x980a,0x9812,
+ 0x980c,0x98fc,0x98f4
+ },{
+ 0x98fd,0x98fe,0x99b3,0x99b1,0x99b4,0x9ae1,0x9ce9,0x9e82,0x9f0e,0x9f13,
+ 0x9f20,0x50e7,0x50ee,0x50e5,0x50d6,0x50ed,0x50da,0x50d5,0x50cf,0x50d1,
+ 0x50f1,0x50ce,0x50e9,0x5162,0x51f3,0x5283,0x5282,0x5331,0x53ad,0x55fe,
+ 0x5600,0x561b,0x5617,0x55fd,0x5614,0x5606,0x5609,0x560d,0x560e,0x55f7,
+ 0x5616,0x561f,0x5608,0x5610,0x55f6,0x5718,0x5716,0x5875,0x587e,0x5883,
+ 0x5893,0x588a,0x5879,0x5885,0x587d,0x58fd,0x5925,0x5922,0x5924,0x596a,
+ 0x5969,0x5ae1,0x5ae6,0x5ae9,0x5ad7,0x5ad6,0x5ad8,0x5ae3,0x5b75,0x5bde,
+ 0x5be7,0x5be1,0x5be5,0x5be6,0x5be8,0x5be2,0x5be4,0x5bdf,0x5c0d,0x5c62,
+ 0x5d84,0x5d87,0x5e5b,0x5e63,0x5e55,0x5e57,0x5e54,0x5ed3,0x5ed6,0x5f0a,
+ 0x5f46,0x5f70,0x5fb9,0x6147
+ }
+ },
+ { /* ku 1a */
+ {
+ 0x613f,0x614b,0x6177,0x6162,0x6163,0x615f,0x615a,0x6158,0x6175,0x622a,
+ 0x6487,0x6458,0x6454,0x64a4,0x6478,0x645f,0x647a,0x6451,0x6467,0x6434,
+ 0x646d,0x647b,0x6572,0x65a1,0x65d7,0x65d6,0x66a2,0x66a8,0x669d,0x699c,
+ 0x69a8,0x6995,0x69c1,0x69ae,0x69d3,0x69cb,0x699b,0x69b7,0x69bb,0x69ab,
+ 0x69b4,0x69d0,0x69cd,0x69ad,0x69cc,0x69a6,0x69c3,0x69a3,0x6b49,0x6b4c,
+ 0x6c33,0x6f33,0x6f14,0x6efe,0x6f13,0x6ef4,0x6f29,0x6f3e,0x6f20,0x6f2c,
+ 0x6f0f,0x6f02,0x6f22
+ },{
+ 0x6eff,0x6eef,0x6f06,0x6f31,0x6f38,0x6f32,0x6f23,0x6f15,0x6f2b,0x6f2f,
+ 0x6f88,0x6f2a,0x6eec,0x6f01,0x6ef2,0x6ecc,0x6ef7,0x7194,0x7199,0x717d,
+ 0x718a,0x7184,0x7192,0x723e,0x7292,0x7296,0x7344,0x7350,0x7464,0x7463,
+ 0x746a,0x7470,0x746d,0x7504,0x7591,0x7627,0x760d,0x760b,0x7609,0x7613,
+ 0x76e1,0x76e3,0x7784,0x777d,0x777f,0x7761,0x78c1,0x789f,0x78a7,0x78b3,
+ 0x78a9,0x78a3,0x798e,0x798f,0x798d,0x7a2e,0x7a31,0x7aaa,0x7aa9,0x7aed,
+ 0x7aef,0x7ba1,0x7b95,0x7b8b,0x7b75,0x7b97,0x7b9d,0x7b94,0x7b8f,0x7bb8,
+ 0x7b87,0x7b84,0x7cb9,0x7cbd,0x7cbe,0x7dbb,0x7db0,0x7d9c,0x7dbd,0x7dbe,
+ 0x7da0,0x7dca,0x7db4,0x7db2,0x7db1,0x7dba,0x7da2,0x7dbf,0x7db5,0x7db8,
+ 0x7dad,0x7dd2,0x7dc7,0x7dac
+ }
+ },
+ { /* ku 1b */
+ {
+ 0x7f70,0x7fe0,0x7fe1,0x7fdf,0x805e,0x805a,0x8087,0x8150,0x8180,0x818f,
+ 0x8188,0x818a,0x817f,0x8182,0x81e7,0x81fa,0x8207,0x8214,0x821e,0x824b,
+ 0x84c9,0x84bf,0x84c6,0x84c4,0x8499,0x849e,0x84b2,0x849c,0x84cb,0x84b8,
+ 0x84c0,0x84d3,0x8490,0x84bc,0x84d1,0x84ca,0x873f,0x871c,0x873b,0x8722,
+ 0x8725,0x8734,0x8718,0x8755,0x8737,0x8729,0x88f3,0x8902,0x88f4,0x88f9,
+ 0x88f8,0x88fd,0x88e8,0x891a,0x88ef,0x8aa6,0x8a8c,0x8a9e,0x8aa3,0x8a8d,
+ 0x8aa1,0x8a93,0x8aa4
+ },{
+ 0x8aaa,0x8aa5,0x8aa8,0x8a98,0x8a91,0x8a9a,0x8aa7,0x8c6a,0x8c8d,0x8c8c,
+ 0x8cd3,0x8cd1,0x8cd2,0x8d6b,0x8d99,0x8d95,0x8dfc,0x8f14,0x8f12,0x8f15,
+ 0x8f13,0x8fa3,0x9060,0x9058,0x905c,0x9063,0x9059,0x905e,0x9062,0x905d,
+ 0x905b,0x9119,0x9118,0x911e,0x9175,0x9178,0x9177,0x9174,0x9278,0x9280,
+ 0x9285,0x9298,0x9296,0x927b,0x9293,0x929c,0x92a8,0x927c,0x9291,0x95a1,
+ 0x95a8,0x95a9,0x95a3,0x95a5,0x95a4,0x9699,0x969c,0x969b,0x96cc,0x96d2,
+ 0x9700,0x977c,0x9785,0x97f6,0x9817,0x9818,0x98af,0x98b1,0x9903,0x9905,
+ 0x990c,0x9909,0x99c1,0x9aaf,0x9ab0,0x9ae6,0x9b41,0x9b42,0x9cf4,0x9cf6,
+ 0x9cf3,0x9ebc,0x9f3b,0x9f4a,0x5104,0x5100,0x50fb,0x50f5,0x50f9,0x5102,
+ 0x5108,0x5109,0x5105,0x51dc
+ }
+ },
+ { /* ku 1c */
+ {
+ 0x5287,0x5288,0x5289,0x528d,0x528a,0x52f0,0x53b2,0x562e,0x563b,0x5639,
+ 0x5632,0x563f,0x5634,0x5629,0x5653,0x564e,0x5657,0x5674,0x5636,0x562f,
+ 0x5630,0x5880,0x589f,0x589e,0x58b3,0x589c,0x58ae,0x58a9,0x58a6,0x596d,
+ 0x5b09,0x5afb,0x5b0b,0x5af5,0x5b0c,0x5b08,0x5bee,0x5bec,0x5be9,0x5beb,
+ 0x5c64,0x5c65,0x5d9d,0x5d94,0x5e62,0x5e5f,0x5e61,0x5ee2,0x5eda,0x5edf,
+ 0x5edd,0x5ee3,0x5ee0,0x5f48,0x5f71,0x5fb7,0x5fb5,0x6176,0x6167,0x616e,
+ 0x615d,0x6155,0x6182
+ },{
+ 0x617c,0x6170,0x616b,0x617e,0x61a7,0x6190,0x61ab,0x618e,0x61ac,0x619a,
+ 0x61a4,0x6194,0x61ae,0x622e,0x6469,0x646f,0x6479,0x649e,0x64b2,0x6488,
+ 0x6490,0x64b0,0x64a5,0x6493,0x6495,0x64a9,0x6492,0x64ae,0x64ad,0x64ab,
+ 0x649a,0x64ac,0x6499,0x64a2,0x64b3,0x6575,0x6577,0x6578,0x66ae,0x66ab,
+ 0x66b4,0x66b1,0x6a23,0x6a1f,0x69e8,0x6a01,0x6a1e,0x6a19,0x69fd,0x6a21,
+ 0x6a13,0x6a0a,0x69f3,0x6a02,0x6a05,0x69ed,0x6a11,0x6b50,0x6b4e,0x6ba4,
+ 0x6bc5,0x6bc6,0x6f3f,0x6f7c,0x6f84,0x6f51,0x6f66,0x6f54,0x6f86,0x6f6d,
+ 0x6f5b,0x6f78,0x6f6e,0x6f8e,0x6f7a,0x6f70,0x6f64,0x6f97,0x6f58,0x6ed5,
+ 0x6f6f,0x6f60,0x6f5f,0x719f,0x71ac,0x71b1,0x71a8,0x7256,0x729b,0x734e,
+ 0x7357,0x7469,0x748b,0x7483
+ }
+ },
+ { /* ku 1d */
+ {
+ 0x747e,0x7480,0x757f,0x7620,0x7629,0x761f,0x7624,0x7626,0x7621,0x7622,
+ 0x769a,0x76ba,0x76e4,0x778e,0x7787,0x778c,0x7791,0x778b,0x78cb,0x78c5,
+ 0x78ba,0x78ca,0x78be,0x78d5,0x78bc,0x78d0,0x7a3f,0x7a3c,0x7a40,0x7a3d,
+ 0x7a37,0x7a3b,0x7aaf,0x7aae,0x7bad,0x7bb1,0x7bc4,0x7bb4,0x7bc6,0x7bc7,
+ 0x7bc1,0x7ba0,0x7bcc,0x7cca,0x7de0,0x7df4,0x7def,0x7dfb,0x7dd8,0x7dec,
+ 0x7ddd,0x7de8,0x7de3,0x7dda,0x7dde,0x7de9,0x7d9e,0x7dd9,0x7df2,0x7df9,
+ 0x7f75,0x7f77,0x7faf
+ },{
+ 0x7fe9,0x8026,0x819b,0x819c,0x819d,0x81a0,0x819a,0x8198,0x8517,0x853d,
+ 0x851a,0x84ee,0x852c,0x852d,0x8513,0x8511,0x8523,0x8521,0x8514,0x84ec,
+ 0x8525,0x84ff,0x8506,0x8782,0x8774,0x8776,0x8760,0x8766,0x8778,0x8768,
+ 0x8759,0x8757,0x874c,0x8753,0x885b,0x885d,0x8910,0x8907,0x8912,0x8913,
+ 0x8915,0x890a,0x8abc,0x8ad2,0x8ac7,0x8ac4,0x8a95,0x8acb,0x8af8,0x8ab2,
+ 0x8ac9,0x8ac2,0x8abf,0x8ab0,0x8ad6,0x8acd,0x8ab6,0x8ab9,0x8adb,0x8c4c,
+ 0x8c4e,0x8c6c,0x8ce0,0x8cde,0x8ce6,0x8ce4,0x8cec,0x8ced,0x8ce2,0x8ce3,
+ 0x8cdc,0x8cea,0x8ce1,0x8d6d,0x8d9f,0x8da3,0x8e2b,0x8e10,0x8e1d,0x8e22,
+ 0x8e0f,0x8e29,0x8e1f,0x8e21,0x8e1e,0x8eba,0x8f1d,0x8f1b,0x8f1f,0x8f29,
+ 0x8f26,0x8f2a,0x8f1c,0x8f1e
+ }
+ },
+ { /* ku 1e */
+ {
+ 0x8f25,0x9069,0x906e,0x9068,0x906d,0x9077,0x9130,0x912d,0x9127,0x9131,
+ 0x9187,0x9189,0x918b,0x9183,0x92c5,0x92bb,0x92b7,0x92ea,0x92ac,0x92e4,
+ 0x92c1,0x92b3,0x92bc,0x92d2,0x92c7,0x92f0,0x92b2,0x95ad,0x95b1,0x9704,
+ 0x9706,0x9707,0x9709,0x9760,0x978d,0x978b,0x978f,0x9821,0x982b,0x981c,
+ 0x98b3,0x990a,0x9913,0x9912,0x9918,0x99dd,0x99d0,0x99df,0x99db,0x99d1,
+ 0x99d5,0x99d2,0x99d9,0x9ab7,0x9aee,0x9aef,0x9b27,0x9b45,0x9b44,0x9b77,
+ 0x9b6f,0x9d06,0x9d09
+ },{
+ 0x9d03,0x9ea9,0x9ebe,0x9ece,0x58a8,0x9f52,0x5112,0x5118,0x5114,0x5110,
+ 0x5115,0x5180,0x51aa,0x51dd,0x5291,0x5293,0x52f3,0x5659,0x566b,0x5679,
+ 0x5669,0x5664,0x5678,0x566a,0x5668,0x5665,0x5671,0x566f,0x566c,0x5662,
+ 0x5676,0x58c1,0x58be,0x58c7,0x58c5,0x596e,0x5b1d,0x5b34,0x5b78,0x5bf0,
+ 0x5c0e,0x5f4a,0x61b2,0x6191,0x61a9,0x618a,0x61cd,0x61b6,0x61be,0x61ca,
+ 0x61c8,0x6230,0x64c5,0x64c1,0x64cb,0x64bb,0x64bc,0x64da,0x64c4,0x64c7,
+ 0x64c2,0x64cd,0x64bf,0x64d2,0x64d4,0x64be,0x6574,0x66c6,0x66c9,0x66b9,
+ 0x66c4,0x66c7,0x66b8,0x6a3d,0x6a38,0x6a3a,0x6a59,0x6a6b,0x6a58,0x6a39,
+ 0x6a44,0x6a62,0x6a61,0x6a4b,0x6a47,0x6a35,0x6a5f,0x6a48,0x6b59,0x6b77,
+ 0x6c05,0x6fc2,0x6fb1,0x6fa1
+ }
+ },
+ { /* ku 1f */
+ {
+ 0x6fc3,0x6fa4,0x6fc1,0x6fa7,0x6fb3,0x6fc0,0x6fb9,0x6fb6,0x6fa6,0x6fa0,
+ 0x6fb4,0x71be,0x71c9,0x71d0,0x71d2,0x71c8,0x71d5,0x71b9,0x71ce,0x71d9,
+ 0x71dc,0x71c3,0x71c4,0x7368,0x749c,0x74a3,0x7498,0x749f,0x749e,0x74e2,
+ 0x750c,0x750d,0x7634,0x7638,0x763a,0x76e7,0x76e5,0x77a0,0x779e,0x779f,
+ 0x77a5,0x78e8,0x78da,0x78ec,0x78e7,0x79a6,0x7a4d,0x7a4e,0x7a46,0x7a4c,
+ 0x7a4b,0x7aba,0x7bd9,0x7c11,0x7bc9,0x7be4,0x7bdb,0x7be1,0x7be9,0x7be6,
+ 0x7cd5,0x7cd6,0x7e0a
+ },{
+ 0x7e11,0x7e08,0x7e1b,0x7e23,0x7e1e,0x7e1d,0x7e09,0x7e10,0x7f79,0x7fb2,
+ 0x7ff0,0x7ff1,0x7fee,0x8028,0x81b3,0x81a9,0x81a8,0x81fb,0x8208,0x8258,
+ 0x8259,0x854a,0x8559,0x8548,0x8568,0x8569,0x8543,0x8549,0x856d,0x856a,
+ 0x855e,0x8783,0x879f,0x879e,0x87a2,0x878d,0x8861,0x892a,0x8932,0x8925,
+ 0x892b,0x8921,0x89aa,0x89a6,0x8ae6,0x8afa,0x8aeb,0x8af1,0x8b00,0x8adc,
+ 0x8ae7,0x8aee,0x8afe,0x8b01,0x8b02,0x8af7,0x8aed,0x8af3,0x8af6,0x8afc,
+ 0x8c6b,0x8c6d,0x8c93,0x8cf4,0x8e44,0x8e31,0x8e34,0x8e42,0x8e39,0x8e35,
+ 0x8f3b,0x8f2f,0x8f38,0x8f33,0x8fa8,0x8fa6,0x9075,0x9074,0x9078,0x9072,
+ 0x907c,0x907a,0x9134,0x9192,0x9320,0x9336,0x92f8,0x9333,0x932f,0x9322,
+ 0x92fc,0x932b,0x9304,0x931a
+ }
+ },
+ { /* ku 20 */
+ {
+ 0x9310,0x9326,0x9321,0x9315,0x932e,0x9319,0x95bb,0x96a7,0x96a8,0x96aa,
+ 0x96d5,0x970e,0x9711,0x9716,0x970d,0x9713,0x970f,0x975b,0x975c,0x9766,
+ 0x9798,0x9830,0x9838,0x983b,0x9837,0x982d,0x9839,0x9824,0x9910,0x9928,
+ 0x991e,0x991b,0x9921,0x991a,0x99ed,0x99e2,0x99f1,0x9ab8,0x9abc,0x9afb,
+ 0x9aed,0x9b28,0x9b91,0x9d15,0x9d23,0x9d26,0x9d28,0x9d12,0x9d1b,0x9ed8,
+ 0x9ed4,0x9f8d,0x9f9c,0x512a,0x511f,0x5121,0x5132,0x52f5,0x568e,0x5680,
+ 0x5690,0x5685,0x5687
+ },{
+ 0x568f,0x58d5,0x58d3,0x58d1,0x58ce,0x5b30,0x5b2a,0x5b24,0x5b7a,0x5c37,
+ 0x5c68,0x5dbc,0x5dba,0x5dbd,0x5db8,0x5e6b,0x5f4c,0x5fbd,0x61c9,0x61c2,
+ 0x61c7,0x61e6,0x61cb,0x6232,0x6234,0x64ce,0x64ca,0x64d8,0x64e0,0x64f0,
+ 0x64e6,0x64ec,0x64f1,0x64e2,0x64ed,0x6582,0x6583,0x66d9,0x66d6,0x6a80,
+ 0x6a94,0x6a84,0x6aa2,0x6a9c,0x6adb,0x6aa3,0x6a7e,0x6a97,0x6a90,0x6aa0,
+ 0x6b5c,0x6bae,0x6bda,0x6c08,0x6fd8,0x6ff1,0x6fdf,0x6fe0,0x6fdb,0x6fe4,
+ 0x6feb,0x6fef,0x6f80,0x6fec,0x6fe1,0x6fe9,0x6fd5,0x6fee,0x6ff0,0x71e7,
+ 0x71df,0x71ee,0x71e6,0x71e5,0x71ed,0x71ec,0x71f4,0x71e0,0x7235,0x7246,
+ 0x7370,0x7372,0x74a9,0x74b0,0x74a6,0x74a8,0x7646,0x7642,0x764c,0x76ea,
+ 0x77b3,0x77aa,0x77b0,0x77ac
+ }
+ },
+ { /* ku 21 */
+ {
+ 0x77a7,0x77ad,0x77ef,0x78f7,0x78fa,0x78f4,0x78ef,0x7901,0x79a7,0x79aa,
+ 0x7a57,0x7abf,0x7c07,0x7c0d,0x7bfe,0x7bf7,0x7c0c,0x7be0,0x7ce0,0x7cdc,
+ 0x7cde,0x7ce2,0x7cdf,0x7cd9,0x7cdd,0x7e2e,0x7e3e,0x7e46,0x7e37,0x7e32,
+ 0x7e43,0x7e2b,0x7e3d,0x7e31,0x7e45,0x7e41,0x7e34,0x7e39,0x7e48,0x7e35,
+ 0x7e3f,0x7e2f,0x7f44,0x7ff3,0x7ffc,0x8071,0x8072,0x8070,0x806f,0x8073,
+ 0x81c6,0x81c3,0x81ba,0x81c2,0x81c0,0x81bf,0x81bd,0x81c9,0x81be,0x81e8,
+ 0x8209,0x8271,0x85aa
+ },{
+ 0x8584,0x857e,0x859c,0x8591,0x8594,0x85af,0x859b,0x8587,0x85a8,0x858a,
+ 0x8667,0x87c0,0x87d1,0x87b3,0x87d2,0x87c6,0x87ab,0x87bb,0x87ba,0x87c8,
+ 0x87cb,0x893b,0x8936,0x8944,0x8938,0x893d,0x89ac,0x8b0e,0x8b17,0x8b19,
+ 0x8b1b,0x8b0a,0x8b20,0x8b1d,0x8b04,0x8b10,0x8c41,0x8c3f,0x8c73,0x8cfa,
+ 0x8cfd,0x8cfc,0x8cf8,0x8cfb,0x8da8,0x8e49,0x8e4b,0x8e48,0x8e4a,0x8f44,
+ 0x8f3e,0x8f42,0x8f45,0x8f3f,0x907f,0x907d,0x9084,0x9081,0x9082,0x9080,
+ 0x9139,0x91a3,0x919e,0x919c,0x934d,0x9382,0x9328,0x9375,0x934a,0x9365,
+ 0x934b,0x9318,0x937e,0x936c,0x935b,0x9370,0x935a,0x9354,0x95ca,0x95cb,
+ 0x95cc,0x95c8,0x95c6,0x96b1,0x96b8,0x96d6,0x971c,0x971e,0x97a0,0x97d3,
+ 0x9846,0x98b6,0x9935,0x9a01
+ }
+ },
+ { /* ku 22 */
+ {
+ 0x99ff,0x9bae,0x9bab,0x9baa,0x9bad,0x9d3b,0x9d3f,0x9e8b,0x9ecf,0x9ede,
+ 0x9edc,0x9edd,0x9edb,0x9f3e,0x9f4b,0x53e2,0x5695,0x56ae,0x58d9,0x58d8,
+ 0x5b38,0x5f5d,0x61e3,0x6233,0x64f4,0x64f2,0x64fe,0x6506,0x64fa,0x64fb,
+ 0x64f7,0x65b7,0x66dc,0x6726,0x6ab3,0x6aac,0x6ac3,0x6abb,0x6ab8,0x6ac2,
+ 0x6aae,0x6aaf,0x6b5f,0x6b78,0x6baf,0x7009,0x700b,0x6ffe,0x7006,0x6ffa,
+ 0x7011,0x700f,0x71fb,0x71fc,0x71fe,0x71f8,0x7377,0x7375,0x74a7,0x74bf,
+ 0x7515,0x7656,0x7658
+ },{
+ 0x7652,0x77bd,0x77bf,0x77bb,0x77bc,0x790e,0x79ae,0x7a61,0x7a62,0x7a60,
+ 0x7ac4,0x7ac5,0x7c2b,0x7c27,0x7c2a,0x7c1e,0x7c23,0x7c21,0x7ce7,0x7e54,
+ 0x7e55,0x7e5e,0x7e5a,0x7e61,0x7e52,0x7e59,0x7f48,0x7ff9,0x7ffb,0x8077,
+ 0x8076,0x81cd,0x81cf,0x820a,0x85cf,0x85a9,0x85cd,0x85d0,0x85c9,0x85b0,
+ 0x85ba,0x85b9,0x85a6,0x87ef,0x87ec,0x87f2,0x87e0,0x8986,0x89b2,0x89f4,
+ 0x8b28,0x8b39,0x8b2c,0x8b2b,0x8c50,0x8d05,0x8e59,0x8e63,0x8e66,0x8e64,
+ 0x8e5f,0x8e55,0x8ec0,0x8f49,0x8f4d,0x9087,0x9083,0x9088,0x91ab,0x91ac,
+ 0x91d0,0x9394,0x938a,0x9396,0x93a2,0x93b3,0x93ae,0x93ac,0x93b0,0x9398,
+ 0x939a,0x9397,0x95d4,0x95d6,0x95d0,0x95d5,0x96e2,0x96dc,0x96d9,0x96db,
+ 0x96de,0x9724,0x97a3,0x97a6
+ }
+ },
+ { /* ku 23 */
+ {
+ 0x97ad,0x97f9,0x984d,0x984f,0x984c,0x984e,0x9853,0x98ba,0x993e,0x993f,
+ 0x993d,0x992e,0x99a5,0x9a0e,0x9ac1,0x9b03,0x9b06,0x9b4f,0x9b4e,0x9b4d,
+ 0x9bca,0x9bc9,0x9bfd,0x9bc8,0x9bc0,0x9d51,0x9d5d,0x9d60,0x9ee0,0x9f15,
+ 0x9f2c,0x5133,0x56a5,0x58de,0x58df,0x58e2,0x5bf5,0x9f90,0x5eec,0x61f2,
+ 0x61f7,0x61f6,0x61f5,0x6500,0x650f,0x66e0,0x66dd,0x6ae5,0x6add,0x6ada,
+ 0x6ad3,0x701b,0x701f,0x7028,0x701a,0x701d,0x7015,0x7018,0x7206,0x720d,
+ 0x7258,0x72a2,0x7378
+ },{
+ 0x737a,0x74bd,0x74ca,0x74e3,0x7587,0x7586,0x765f,0x7661,0x77c7,0x7919,
+ 0x79b1,0x7a6b,0x7a69,0x7c3e,0x7c3f,0x7c38,0x7c3d,0x7c37,0x7c40,0x7e6b,
+ 0x7e6d,0x7e79,0x7e69,0x7e6a,0x7f85,0x7e73,0x7fb6,0x7fb9,0x7fb8,0x81d8,
+ 0x85e9,0x85dd,0x85ea,0x85d5,0x85e4,0x85e5,0x85f7,0x87fb,0x8805,0x880d,
+ 0x87f9,0x87fe,0x8960,0x895f,0x8956,0x895e,0x8b41,0x8b5c,0x8b58,0x8b49,
+ 0x8b5a,0x8b4e,0x8b4f,0x8b46,0x8b59,0x8d08,0x8d0a,0x8e7c,0x8e72,0x8e87,
+ 0x8e76,0x8e6c,0x8e7a,0x8e74,0x8f54,0x8f4e,0x8fad,0x908a,0x908b,0x91b1,
+ 0x91ae,0x93e1,0x93d1,0x93df,0x93c3,0x93c8,0x93dc,0x93dd,0x93d6,0x93e2,
+ 0x93cd,0x93d8,0x93e4,0x93d7,0x93e8,0x95dc,0x96b4,0x96e3,0x972a,0x9727,
+ 0x9761,0x97dc,0x97fb,0x985e
+ }
+ },
+ { /* ku 24 */
+ {
+ 0x9858,0x985b,0x98bc,0x9945,0x9949,0x9a16,0x9a19,0x9b0d,0x9be8,0x9be7,
+ 0x9bd6,0x9bdb,0x9d89,0x9d61,0x9d72,0x9d6a,0x9d6c,0x9e92,0x9e97,0x9e93,
+ 0x9eb4,0x52f8,0x56a8,0x56b7,0x56b6,0x56b4,0x56bc,0x58e4,0x5b40,0x5b43,
+ 0x5b7d,0x5bf6,0x5dc9,0x61f8,0x61fa,0x6518,0x6514,0x6519,0x66e6,0x6727,
+ 0x6aec,0x703e,0x7030,0x7032,0x7210,0x737b,0x74cf,0x7662,0x7665,0x7926,
+ 0x792a,0x792c,0x792b,0x7ac7,0x7af6,0x7c4c,0x7c43,0x7c4d,0x7cef,0x7cf0,
+ 0x8fae,0x7e7d,0x7e7c
+ },{
+ 0x7e82,0x7f4c,0x8000,0x81da,0x8266,0x85fb,0x85f9,0x8611,0x85fa,0x8606,
+ 0x860b,0x8607,0x860a,0x8814,0x8815,0x8964,0x89ba,0x89f8,0x8b70,0x8b6c,
+ 0x8b66,0x8b6f,0x8b5f,0x8b6b,0x8d0f,0x8d0d,0x8e89,0x8e81,0x8e85,0x8e82,
+ 0x91b4,0x91cb,0x9418,0x9403,0x93fd,0x95e1,0x9730,0x98c4,0x9952,0x9951,
+ 0x99a8,0x9a2b,0x9a30,0x9a37,0x9a35,0x9c13,0x9c0d,0x9e79,0x9eb5,0x9ee8,
+ 0x9f2f,0x9f5f,0x9f63,0x9f61,0x5137,0x5138,0x56c1,0x56c0,0x56c2,0x5914,
+ 0x5c6c,0x5dcd,0x61fc,0x61fe,0x651d,0x651c,0x6595,0x66e9,0x6afb,0x6b04,
+ 0x6afa,0x6bb2,0x704c,0x721b,0x72a7,0x74d6,0x74d4,0x7669,0x77d3,0x7c50,
+ 0x7e8f,0x7e8c,0x7fbc,0x8617,0x862d,0x861a,0x8823,0x8822,0x8821,0x881f,
+ 0x896a,0x896c,0x89bd,0x8b74
+ }
+ },
+ { /* ku 25 */
+ {
+ 0x8b77,0x8b7d,0x8d13,0x8e8a,0x8e8d,0x8e8b,0x8f5f,0x8faf,0x91ba,0x942e,
+ 0x9433,0x9435,0x943a,0x9438,0x9432,0x942b,0x95e2,0x9738,0x9739,0x9732,
+ 0x97ff,0x9867,0x9865,0x9957,0x9a45,0x9a43,0x9a40,0x9a3e,0x9acf,0x9b54,
+ 0x9b51,0x9c2d,0x9c25,0x9daf,0x9db4,0x9dc2,0x9db8,0x9e9d,0x9eef,0x9f19,
+ 0x9f5c,0x9f66,0x9f67,0x513c,0x513b,0x56c8,0x56ca,0x56c9,0x5b7f,0x5dd4,
+ 0x5dd2,0x5f4e,0x61ff,0x6524,0x6b0a,0x6b61,0x7051,0x7058,0x7380,0x74e4,
+ 0x758a,0x766e,0x766c
+ },{
+ 0x79b3,0x7c60,0x7c5f,0x807e,0x807d,0x81df,0x8972,0x896f,0x89fc,0x8b80,
+ 0x8d16,0x8d17,0x8e91,0x8e93,0x8f61,0x9148,0x9444,0x9451,0x9452,0x973d,
+ 0x973e,0x97c3,0x97c1,0x986b,0x9955,0x9a55,0x9a4d,0x9ad2,0x9b1a,0x9c49,
+ 0x9c31,0x9c3e,0x9c3b,0x9dd3,0x9dd7,0x9f34,0x9f6c,0x9f6a,0x9f94,0x56cc,
+ 0x5dd6,0x6200,0x6523,0x652b,0x652a,0x66ec,0x6b10,0x74da,0x7aca,0x7c64,
+ 0x7c63,0x7c65,0x7e93,0x7e96,0x7e94,0x81e2,0x8638,0x863f,0x8831,0x8b8a,
+ 0x9090,0x908f,0x9463,0x9460,0x9464,0x9768,0x986f,0x995c,0x9a5a,0x9a5b,
+ 0x9a57,0x9ad3,0x9ad4,0x9ad1,0x9c54,0x9c57,0x9c56,0x9de5,0x9e9f,0x9ef4,
+ 0x56d1,0x58e9,0x652c,0x705e,0x7671,0x7672,0x77d7,0x7f50,0x7f88,0x8836,
+ 0x8839,0x8862,0x8b93,0x8b92
+ }
+ },
+ { /* ku 26 */
+ {
+ 0x8b96,0x8277,0x8d1b,0x91c0,0x946a,0x9742,0x9748,0x9744,0x97c6,0x9870,
+ 0x9a5f,0x9b22,0x9b58,0x9c5f,0x9df9,0x9dfa,0x9e7c,0x9e7d,0x9f07,0x9f77,
+ 0x9f72,0x5ef3,0x6b16,0x7063,0x7c6c,0x7c6e,0x883b,0x89c0,0x8ea1,0x91c1,
+ 0x9472,0x9470,0x9871,0x995e,0x9ad6,0x9b23,0x9ecc,0x7064,0x77da,0x8b9a,
+ 0x9477,0x97c9,0x9a62,0x9a65,0x7e9c,0x8b9c,0x8eaa,0x91c5,0x947d,0x947e,
+ 0x947c,0x9c77,0x9c78,0x9ef7,0x8c54,0x947f,0x9e1a,0x7228,0x9a6a,0x9b31,
+ 0x9e1b,0x9e1e,0x7c72
+ },{
+ 0x30fe,0x309d,0x309e,0x3005,0x3041,0x3042,0x3043,0x3044,0x3045,0x3046,
+ 0x3047,0x3048,0x3049,0x304a,0x304b,0x304c,0x304d,0x304e,0x304f,0x3050,
+ 0x3051,0x3052,0x3053,0x3054,0x3055,0x3056,0x3057,0x3058,0x3059,0x305a,
+ 0x305b,0x305c,0x305d,0x305e,0x305f,0x3060,0x3061,0x3062,0x3063,0x3064,
+ 0x3065,0x3066,0x3067,0x3068,0x3069,0x306a,0x306b,0x306c,0x306d,0x306e,
+ 0x306f,0x3070,0x3071,0x3072,0x3073,0x3074,0x3075,0x3076,0x3077,0x3078,
+ 0x3079,0x307a,0x307b,0x307c,0x307d,0x307e,0x307f,0x3080,0x3081,0x3082,
+ 0x3083,0x3084,0x3085,0x3086,0x3087,0x3088,0x3089,0x308a,0x308b,0x308c,
+ 0x308d,0x308e,0x308f,0x3090,0x3091,0x3092,0x3093,0x30a1,0x30a2,0x30a3,
+ 0x30a4,0x30a5,0x30a6,0x30a7
+ }
+ },
+ { /* ku 27 */
+ {
+ 0x30a8,0x30a9,0x30aa,0x30ab,0x30ac,0x30ad,0x30ae,0x30af,0x30b0,0x30b1,
+ 0x30b2,0x30b3,0x30b4,0x30b5,0x30b6,0x30b7,0x30b8,0x30b9,0x30ba,0x30bb,
+ 0x30bc,0x30bd,0x30be,0x30bf,0x30c0,0x30c1,0x30c2,0x30c3,0x30c4,0x30c5,
+ 0x30c6,0x30c7,0x30c8,0x30c9,0x30ca,0x30cb,0x30cc,0x30cd,0x30ce,0x30cf,
+ 0x30d0,0x30d1,0x30d2,0x30d3,0x30d4,0x30d5,0x30d6,0x30d7,0x30d8,0x30d9,
+ 0x30da,0x30db,0x30dc,0x30dd,0x30de,0x30df,0x30e0,0x30e1,0x30e2,0x30e3,
+ 0x30e4,0x30e5,0x30e6
+ },{
+ 0x30e7,0x30e8,0x30e9,0x30ea,0x30eb,0x30ec,0x30ed,0x30ee,0x30ef,0x30f0,
+ 0x30f1,0x30f2,0x30f3,0x30f4,0x30f5,0x30f6,0x0414,0x0415,0x0401,0x0416,
+ 0x0417,0x0418,0x0419,0x041a,0x041b,0x041c,0x0423,0x0424,0x0425,0x0426,
+ 0x0427,0x0428,0x0429,0x042a,0x042b,0x042c,0x042d,0x042e,0x042f,0x0430,
+ 0x0431,0x0432,0x0433,0x0434,0x0435,0x0451,0x0436,0x0437,0x0438,0x0439,
+ 0x043a,0x043b,0x043c,0x043d,0x043e,0x043f,0x0440,0x0441,0x0442,0x0443,
+ 0x0444,0x0445,0x0446,0x0447,0x0448,0x0449,0x044a,0x044b,0x044c,0x044d,
+ 0x044e,0x044f,0x2460,0x2461,0x2462,0x2463,0x2464,0x2465,0x2466,0x2467,
+ 0x2468,0x2469,0x2474,0x2475,0x2476,0x2477,0x2478,0x2479,0x247a,0x247b,
+ 0x247c,0x247d,UBOGON,UBOGON
+ }
+ },
+ { /* ku 28 */
+ {
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON
+ },{
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ }
+ },
+ { /* ku 29 */
+ {
+ 0x4e42,0x4e5c,0x51f5,0x531a,0x5382,0x4e07,0x4e0c,0x4e47,0x4e8d,0x56d7,
+ 0xfa0c,0x5c6e,0x5f73,0x4e0f,0x5187,0x4e0e,0x4e2e,0x4e93,0x4ec2,0x4ec9,
+ 0x4ec8,0x5198,0x52fc,0x536c,0x53b9,0x5720,0x5903,0x592c,0x5c10,0x5dff,
+ 0x65e1,0x6bb3,0x6bcc,0x6c14,0x723f,0x4e31,0x4e3c,0x4ee8,0x4edc,0x4ee9,
+ 0x4ee1,0x4edd,0x4eda,0x520c,0x531c,0x534c,0x5722,0x5723,0x5917,0x592f,
+ 0x5b81,0x5b84,0x5c12,0x5c3b,0x5c74,0x5c73,0x5e04,0x5e80,0x5e82,0x5fc9,
+ 0x6209,0x6250,0x6c15
+ },{
+ 0x6c36,0x6c43,0x6c3f,0x6c3b,0x72ae,0x72b0,0x738a,0x79b8,0x808a,0x961e,
+ 0x4f0e,0x4f18,0x4f2c,0x4ef5,0x4f14,0x4ef1,0x4f00,0x4ef7,0x4f08,0x4f1d,
+ 0x4f02,0x4f05,0x4f22,0x4f13,0x4f04,0x4ef4,0x4f12,0x51b1,0x5213,0x5209,
+ 0x5210,0x52a6,0x5322,0x531f,0x534d,0x538a,0x5407,0x56e1,0x56df,0x572e,
+ 0x572a,0x5734,0x593c,0x5980,0x597c,0x5985,0x597b,0x597e,0x5977,0x597f,
+ 0x5b56,0x5c15,0x5c25,0x5c7c,0x5c7a,0x5c7b,0x5c7e,0x5ddf,0x5e75,0x5e84,
+ 0x5f02,0x5f1a,0x5f74,0x5fd5,0x5fd4,0x5fcf,0x625c,0x625e,0x6264,0x6261,
+ 0x6266,0x6262,0x6259,0x6260,0x625a,0x6265,0x65ef,0x65ee,0x673e,0x6739,
+ 0x6738,0x673b,0x673a,0x673f,0x673c,0x6733,0x6c18,0x6c46,0x6c52,0x6c5c,
+ 0x6c4f,0x6c4a,0x6c54,0x6c4b
+ }
+ },
+ { /* ku 2a */
+ {
+ 0x6c4c,0x7071,0x725e,0x72b4,0x72b5,0x738e,0x752a,0x767f,0x7a75,0x7f51,
+ 0x8278,0x827c,0x8280,0x827d,0x827f,0x864d,0x897e,0x9099,0x9097,0x9098,
+ 0x909b,0x9094,0x9622,0x9624,0x9620,0x9623,0x4f56,0x4f3b,0x4f62,0x4f49,
+ 0x4f53,0x4f64,0x4f3e,0x4f67,0x4f52,0x4f5f,0x4f41,0x4f58,0x4f2d,0x4f33,
+ 0x4f3f,0x4f61,0x518f,0x51b9,0x521c,0x521e,0x5221,0x52ad,0x52ae,0x5309,
+ 0x5363,0x5372,0x538e,0x538f,0x5430,0x5437,0x542a,0x5454,0x5445,0x5419,
+ 0x541c,0x5425,0x5418
+ },{
+ 0x543d,0x544f,0x5441,0x5428,0x5424,0x5447,0x56ee,0x56e7,0x56e5,0x5741,
+ 0x5745,0x574c,0x5749,0x574b,0x5752,0x5906,0x5940,0x59a6,0x5998,0x59a0,
+ 0x5997,0x598e,0x59a2,0x5990,0x598f,0x59a7,0x59a1,0x5b8e,0x5b92,0x5c28,
+ 0x5c2a,0x5c8d,0x5c8f,0x5c88,0x5c8b,0x5c89,0x5c92,0x5c8a,0x5c86,0x5c93,
+ 0x5c95,0x5de0,0x5e0a,0x5e0e,0x5e8b,0x5e89,0x5e8c,0x5e88,0x5e8d,0x5f05,
+ 0x5f1d,0x5f78,0x5f76,0x5fd2,0x5fd1,0x5fd0,0x5fed,0x5fe8,0x5fee,0x5ff3,
+ 0x5fe1,0x5fe4,0x5fe3,0x5ffa,0x5fef,0x5ff7,0x5ffb,0x6000,0x5ff4,0x623a,
+ 0x6283,0x628c,0x628e,0x628f,0x6294,0x6287,0x6271,0x627b,0x627a,0x6270,
+ 0x6281,0x6288,0x6277,0x627d,0x6272,0x6274,0x6537,0x65f0,0x65f4,0x65f3,
+ 0x65f2,0x65f5,0x6745,0x6747
+ }
+ },
+ { /* ku 2b */
+ {
+ 0x6759,0x6755,0x674c,0x6748,0x675d,0x674d,0x675a,0x674b,0x6bd0,0x6c19,
+ 0x6c1a,0x6c78,0x6c67,0x6c6b,0x6c84,0x6c8b,0x6c8f,0x6c71,0x6c6f,0x6c69,
+ 0x6c9a,0x6c6d,0x6c87,0x6c95,0x6c9c,0x6c66,0x6c73,0x6c65,0x6c7b,0x6c8e,
+ 0x7074,0x707a,0x7263,0x72bf,0x72bd,0x72c3,0x72c6,0x72c1,0x72ba,0x72c5,
+ 0x7395,0x7397,0x7393,0x7394,0x7392,0x753a,0x7539,0x7594,0x7595,0x7681,
+ 0x793d,0x8034,0x8095,0x8099,0x8090,0x8092,0x809c,0x8290,0x828f,0x8285,
+ 0x828e,0x8291,0x8293
+ },{
+ 0x828a,0x8283,0x8284,0x8c78,0x8fc9,0x8fbf,0x909f,0x90a1,0x90a5,0x909e,
+ 0x90a7,0x90a0,0x9630,0x9628,0x962f,0x962d,0x4e33,0x4f98,0x4f7c,0x4f85,
+ 0x4f7d,0x4f80,0x4f87,0x4f76,0x4f74,0x4f89,0x4f84,0x4f77,0x4f4c,0x4f97,
+ 0x4f6a,0x4f9a,0x4f79,0x4f81,0x4f78,0x4f90,0x4f9c,0x4f94,0x4f9e,0x4f92,
+ 0x4f82,0x4f95,0x4f6b,0x4f6e,0x519e,0x51bc,0x51be,0x5235,0x5232,0x5233,
+ 0x5246,0x5231,0x52bc,0x530a,0x530b,0x533c,0x5392,0x5394,0x5487,0x547f,
+ 0x5481,0x5491,0x5482,0x5488,0x546b,0x547a,0x547e,0x5465,0x546c,0x5474,
+ 0x5466,0x548d,0x546f,0x5461,0x5460,0x5498,0x5463,0x5467,0x5464,0x56f7,
+ 0x56f9,0x576f,0x5772,0x576d,0x576b,0x5771,0x5770,0x5776,0x5780,0x5775,
+ 0x577b,0x5773,0x5774,0x5762
+ }
+ },
+ { /* ku 2c */
+ {
+ 0x5768,0x577d,0x590c,0x5945,0x59b5,0x59ba,0x59cf,0x59ce,0x59b2,0x59cc,
+ 0x59c1,0x59b6,0x59bc,0x59c3,0x59d6,0x59b1,0x59bd,0x59c0,0x59c8,0x59b4,
+ 0x59c7,0x5b62,0x5b65,0x5b93,0x5b95,0x5c44,0x5c47,0x5cae,0x5ca4,0x5ca0,
+ 0x5cb5,0x5caf,0x5ca8,0x5cac,0x5c9f,0x5ca3,0x5cad,0x5ca2,0x5caa,0x5ca7,
+ 0x5c9d,0x5ca5,0x5cb6,0x5cb0,0x5ca6,0x5e17,0x5e14,0x5e19,0x5f28,0x5f22,
+ 0x5f23,0x5f24,0x5f54,0x5f82,0x5f7e,0x5f7d,0x5fde,0x5fe5,0x602d,0x6026,
+ 0x6019,0x6032,0x600b
+ },{
+ 0x6034,0x600a,0x6017,0x6033,0x601a,0x601e,0x602c,0x6022,0x600d,0x6010,
+ 0x602e,0x6013,0x6011,0x600c,0x6009,0x601c,0x6214,0x623d,0x62ad,0x62b4,
+ 0x62d1,0x62be,0x62aa,0x62b6,0x62ca,0x62ae,0x62b3,0x62af,0x62bb,0x62a9,
+ 0x62b0,0x62b8,0x653d,0x65a8,0x65bb,0x6609,0x65fc,0x6604,0x6612,0x6608,
+ 0x65fb,0x6603,0x660b,0x660d,0x6605,0x65fd,0x6611,0x6610,0x66f6,0x670a,
+ 0x6785,0x676c,0x678e,0x6792,0x6776,0x677b,0x6798,0x6786,0x6784,0x6774,
+ 0x678d,0x678c,0x677a,0x679f,0x6791,0x6799,0x6783,0x677d,0x6781,0x6778,
+ 0x6779,0x6794,0x6b25,0x6b80,0x6b7e,0x6bde,0x6c1d,0x6c93,0x6cec,0x6ceb,
+ 0x6cee,0x6cd9,0x6cb6,0x6cd4,0x6cad,0x6ce7,0x6cb7,0x6cd0,0x6cc2,0x6cba,
+ 0x6cc3,0x6cc6,0x6ced,0x6cf2
+ }
+ },
+ { /* ku 2d */
+ {
+ 0x6cd2,0x6cdd,0x6cb4,0x6c8a,0x6c9d,0x6c80,0x6cde,0x6cc0,0x6d30,0x6ccd,
+ 0x6cc7,0x6cb0,0x6cf9,0x6ccf,0x6ce9,0x6cd1,0x7094,0x7098,0x7085,0x7093,
+ 0x7086,0x7084,0x7091,0x7096,0x7082,0x709a,0x7083,0x726a,0x72d6,0x72cb,
+ 0x72d8,0x72c9,0x72dc,0x72d2,0x72d4,0x72da,0x72cc,0x72d1,0x73a4,0x73a1,
+ 0x73ad,0x73a6,0x73a2,0x73a0,0x73ac,0x739d,0x74dd,0x74e8,0x753f,0x7540,
+ 0x753e,0x758c,0x7598,0x76af,0x76f3,0x76f1,0x76f0,0x76f5,0x77f8,0x77fc,
+ 0x77f9,0x77fb,0x77fa
+ },{
+ 0x77f7,0x7942,0x793f,0x79c5,0x7a78,0x7a7b,0x7afb,0x7c75,0x7cfd,0x8035,
+ 0x808f,0x80ae,0x80a3,0x80b8,0x80b5,0x80ad,0x8220,0x82a0,0x82c0,0x82ab,
+ 0x829a,0x8298,0x829b,0x82b5,0x82a7,0x82ae,0x82bc,0x829e,0x82ba,0x82b4,
+ 0x82a8,0x82a1,0x82a9,0x82c2,0x82a4,0x82c3,0x82b6,0x82a2,0x8670,0x866f,
+ 0x866d,0x866e,0x8c56,0x8fd2,0x8fcb,0x8fd3,0x8fcd,0x8fd6,0x8fd5,0x8fd7,
+ 0x90b2,0x90b4,0x90af,0x90b3,0x90b0,0x9639,0x963d,0x963c,0x963a,0x9643,
+ 0x4fcd,0x4fc5,0x4fd3,0x4fb2,0x4fc9,0x4fcb,0x4fc1,0x4fd4,0x4fdc,0x4fd9,
+ 0x4fbb,0x4fb3,0x4fdb,0x4fc7,0x4fd6,0x4fba,0x4fc0,0x4fb9,0x4fec,0x5244,
+ 0x5249,0x52c0,0x52c2,0x533d,0x537c,0x5397,0x5396,0x5399,0x5398,0x54ba,
+ 0x54a1,0x54ad,0x54a5,0x54cf
+ }
+ },
+ { /* ku 2e */
+ {
+ 0x54c3,0x830d,0x54b7,0x54ae,0x54d6,0x54b6,0x54c5,0x54c6,0x54a0,0x5470,
+ 0x54bc,0x54a2,0x54be,0x5472,0x54de,0x54b0,0x57b5,0x579e,0x579f,0x57a4,
+ 0x578c,0x5797,0x579d,0x579b,0x5794,0x5798,0x578f,0x5799,0x57a5,0x579a,
+ 0x5795,0x58f4,0x590d,0x5953,0x59e1,0x59de,0x59ee,0x5a00,0x59f1,0x59dd,
+ 0x59fa,0x59fd,0x59fc,0x59f6,0x59e4,0x59f2,0x59f7,0x59db,0x59e9,0x59f3,
+ 0x59f5,0x59e0,0x59fe,0x59f4,0x59ed,0x5ba8,0x5c4c,0x5cd0,0x5cd8,0x5ccc,
+ 0x5cd7,0x5ccb,0x5cdb
+ },{
+ 0x5cde,0x5cda,0x5cc9,0x5cc7,0x5cca,0x5cd6,0x5cd3,0x5cd4,0x5ccf,0x5cc8,
+ 0x5cc6,0x5cce,0x5cdf,0x5cf8,0x5df9,0x5e21,0x5e22,0x5e23,0x5e20,0x5e24,
+ 0x5eb0,0x5ea4,0x5ea2,0x5e9b,0x5ea3,0x5ea5,0x5f07,0x5f2e,0x5f56,0x5f86,
+ 0x6037,0x6039,0x6054,0x6072,0x605e,0x6045,0x6053,0x6047,0x6049,0x605b,
+ 0x604c,0x6040,0x6042,0x605f,0x6024,0x6044,0x6058,0x6066,0x606e,0x6242,
+ 0x6243,0x62cf,0x630d,0x630b,0x62f5,0x630e,0x6303,0x62eb,0x62f9,0x630f,
+ 0x630c,0x62f8,0x62f6,0x6300,0x6313,0x6314,0x62fa,0x6315,0x62fb,0x62f0,
+ 0x6541,0x6543,0x65aa,0x65bf,0x6636,0x6621,0x6632,0x6635,0x661c,0x6626,
+ 0x6622,0x6633,0x662b,0x663a,0x661d,0x6634,0x6639,0x662e,0x670f,0x6710,
+ 0x67c1,0x67f2,0x67c8,0x67ba
+ }
+ },
+ { /* ku 2f */
+ {
+ 0x67dc,0x67bb,0x67f8,0x67d8,0x67c0,0x67b7,0x67c5,0x67eb,0x67e4,0x67df,
+ 0x67b5,0x67cd,0x67b3,0x67f7,0x67f6,0x67ee,0x67e3,0x67c2,0x67b9,0x67ce,
+ 0x67e7,0x67f0,0x67b2,0x67fc,0x67c6,0x67ed,0x67cc,0x67ae,0x67e6,0x67db,
+ 0x67fa,0x67c9,0x67ca,0x67c3,0x67ea,0x67cb,0x6b28,0x6b82,0x6b84,0x6bb6,
+ 0x6bd6,0x6bd8,0x6be0,0x6c20,0x6c21,0x6d28,0x6d34,0x6d2d,0x6d1f,0x6d3c,
+ 0x6d3f,0x6d12,0x6d0a,0x6cda,0x6d33,0x6d04,0x6d19,0x6d3a,0x6d1a,0x6d11,
+ 0x6d00,0x6d1d,0x6d42
+ },{
+ 0x6d01,0x6d18,0x6d37,0x6d03,0x6d0f,0x6d40,0x6d07,0x6d20,0x6d2c,0x6d08,
+ 0x6d22,0x6d09,0x6d10,0x70b7,0x709f,0x70be,0x70b1,0x70b0,0x70a1,0x70b4,
+ 0x70b5,0x70a9,0x7241,0x7249,0x724a,0x726c,0x7270,0x7273,0x726e,0x72ca,
+ 0x72e4,0x72e8,0x72eb,0x72df,0x72ea,0x72e6,0x72e3,0x7385,0x73cc,0x73c2,
+ 0x73c8,0x73c5,0x73b9,0x73b6,0x73b5,0x73b4,0x73eb,0x73bf,0x73c7,0x73be,
+ 0x73c3,0x73c6,0x73b8,0x73cb,0x74ec,0x74ee,0x752e,0x7547,0x7548,0x75a7,
+ 0x75aa,0x7679,0x76c4,0x7708,0x7703,0x7704,0x7705,0x770a,0x76f7,0x76fb,
+ 0x76fa,0x77e7,0x77e8,0x7806,0x7811,0x7812,0x7805,0x7810,0x780f,0x780e,
+ 0x7809,0x7803,0x7813,0x794a,0x794c,0x794b,0x7945,0x7944,0x79d5,0x79cd,
+ 0x79cf,0x79d6,0x79ce,0x7a80
+ }
+ },
+ { /* ku 30 */
+ {
+ 0x7a7e,0x7ad1,0x7b00,0x7b01,0x7c7a,0x7c78,0x7c79,0x7c7f,0x7c80,0x7c81,
+ 0x7d03,0x7d08,0x7d01,0x7f58,0x7f91,0x7f8d,0x7fbe,0x8007,0x800e,0x800f,
+ 0x8014,0x8037,0x80d8,0x80c7,0x80e0,0x80d1,0x80c8,0x80c2,0x80d0,0x80c5,
+ 0x80e3,0x80d9,0x80dc,0x80ca,0x80d5,0x80c9,0x80cf,0x80d7,0x80e6,0x80cd,
+ 0x81ff,0x8221,0x8294,0x82d9,0x82fe,0x82f9,0x8307,0x82e8,0x8300,0x82d5,
+ 0x833a,0x82eb,0x82d6,0x82f4,0x82ec,0x82e1,0x82f2,0x82f5,0x830c,0x82fb,
+ 0x82f6,0x82f0,0x82ea
+ },{
+ 0x82e4,0x82e0,0x82fa,0x82f3,0x82ed,0x8677,0x8674,0x867c,0x8673,0x8841,
+ 0x884e,0x8867,0x886a,0x8869,0x89d3,0x8a04,0x8a07,0x8d72,0x8fe3,0x8fe1,
+ 0x8fee,0x8fe0,0x90f1,0x90bd,0x90bf,0x90d5,0x90c5,0x90be,0x90c7,0x90cb,
+ 0x90c8,0x91d4,0x91d3,0x9654,0x964f,0x9651,0x9653,0x964a,0x964e,0x501e,
+ 0x5005,0x5007,0x5013,0x5022,0x5030,0x501b,0x4ff5,0x4ff4,0x5033,0x5037,
+ 0x502c,0x4ff6,0x4ff7,0x5017,0x501c,0x5020,0x5027,0x5035,0x502f,0x5031,
+ 0x500e,0x515a,0x5194,0x5193,0x51ca,0x51c4,0x51c5,0x51c8,0x51ce,0x5261,
+ 0x525a,0x5252,0x525e,0x525f,0x5255,0x5262,0x52cd,0x530e,0x539e,0x5526,
+ 0x54e2,0x5517,0x5512,0x54e7,0x54f3,0x54e4,0x551a,0x54ff,0x5504,0x5508,
+ 0x54eb,0x5511,0x5505,0x54f1
+ }
+ },
+ { /* ku 31 */
+ {
+ 0x550a,0x54fb,0x54f7,0x54f8,0x54e0,0x550e,0x5503,0x550b,0x5701,0x5702,
+ 0x57cc,0x5832,0x57d5,0x57d2,0x57ba,0x57c6,0x57bd,0x57bc,0x57b8,0x57b6,
+ 0x57bf,0x57c7,0x57d0,0x57b9,0x57c1,0x590e,0x594a,0x5a19,0x5a16,0x5a2d,
+ 0x5a2e,0x5a15,0x5a0f,0x5a17,0x5a0a,0x5a1e,0x5a33,0x5b6c,0x5ba7,0x5bad,
+ 0x5bac,0x5c03,0x5c56,0x5c54,0x5cec,0x5cff,0x5cee,0x5cf1,0x5cf7,0x5d00,
+ 0x5cf9,0x5e29,0x5e28,0x5ea8,0x5eae,0x5eaa,0x5eac,0x5f33,0x5f30,0x5f67,
+ 0x605d,0x605a,0x6067
+ },{
+ 0x6041,0x60a2,0x6088,0x6080,0x6092,0x6081,0x609d,0x6083,0x6095,0x609b,
+ 0x6097,0x6087,0x609c,0x608e,0x6219,0x6246,0x62f2,0x6310,0x6356,0x632c,
+ 0x6344,0x6345,0x6336,0x6343,0x63e4,0x6339,0x634b,0x634a,0x633c,0x6329,
+ 0x6341,0x6334,0x6358,0x6354,0x6359,0x632d,0x6347,0x6333,0x635a,0x6351,
+ 0x6338,0x6357,0x6340,0x6348,0x654a,0x6546,0x65c6,0x65c3,0x65c4,0x65c2,
+ 0x664a,0x665f,0x6647,0x6651,0x6712,0x6713,0x681f,0x681a,0x6849,0x6832,
+ 0x6833,0x683b,0x684b,0x684f,0x6816,0x6831,0x681c,0x6835,0x682b,0x682d,
+ 0x682f,0x684e,0x6844,0x6834,0x681d,0x6812,0x6814,0x6826,0x6828,0x682e,
+ 0x684d,0x683a,0x6825,0x6820,0x6b2c,0x6b2f,0x6b2d,0x6b31,0x6b34,0x6b6d,
+ 0x8082,0x6b88,0x6be6,0x6be4
+ }
+ },
+ { /* ku 32 */
+ {
+ 0x6be8,0x6be3,0x6be2,0x6be7,0x6c25,0x6d7a,0x6d63,0x6d64,0x6d76,0x6d0d,
+ 0x6d61,0x6d92,0x6d58,0x6d62,0x6d6d,0x6d6f,0x6d91,0x6d8d,0x6def,0x6d7f,
+ 0x6d86,0x6d5e,0x6d67,0x6d60,0x6d97,0x6d70,0x6d7c,0x6d5f,0x6d82,0x6d98,
+ 0x6d2f,0x6d68,0x6d8b,0x6d7e,0x6d80,0x6d84,0x6d16,0x6d83,0x6d7b,0x6d7d,
+ 0x6d75,0x6d90,0x70dc,0x70d3,0x70d1,0x70dd,0x70cb,0x7f39,0x70e2,0x70d7,
+ 0x70d2,0x70de,0x70e0,0x70d4,0x70cd,0x70c5,0x70c6,0x70c7,0x70da,0x70ce,
+ 0x70e1,0x7242,0x7278
+ },{
+ 0x7277,0x7276,0x7300,0x72fa,0x72f4,0x72fe,0x72f6,0x72f3,0x72fb,0x7301,
+ 0x73d3,0x73d9,0x73e5,0x73d6,0x73bc,0x73e7,0x73e3,0x73e9,0x73dc,0x73d2,
+ 0x73db,0x73d4,0x73dd,0x73da,0x73d7,0x73d8,0x73e8,0x74de,0x74df,0x74f4,
+ 0x74f5,0x7521,0x755b,0x755f,0x75b0,0x75c1,0x75bb,0x75c4,0x75c0,0x75bf,
+ 0x75b6,0x75ba,0x768a,0x76c9,0x771d,0x771b,0x7710,0x7713,0x7712,0x7723,
+ 0x7711,0x7715,0x7719,0x771a,0x7722,0x7727,0x7823,0x782c,0x7822,0x7835,
+ 0x782f,0x7828,0x782e,0x782b,0x7821,0x7829,0x7833,0x782a,0x7831,0x7954,
+ 0x795b,0x794f,0x795c,0x7953,0x7952,0x7951,0x79eb,0x79ec,0x79e0,0x79ee,
+ 0x79ed,0x79ea,0x79dc,0x79de,0x79dd,0x7a86,0x7a89,0x7a85,0x7a8b,0x7a8c,
+ 0x7a8a,0x7a87,0x7ad8,0x7b10
+ }
+ },
+ { /* ku 33 */
+ {
+ 0x7b04,0x7b13,0x7b05,0x7b0f,0x7b08,0x7b0a,0x7b0e,0x7b09,0x7b12,0x7c84,
+ 0x7c91,0x7c8a,0x7c8c,0x7c88,0x7c8d,0x7c85,0x7d1e,0x7d1d,0x7d11,0x7d0e,
+ 0x7d18,0x7d16,0x7d13,0x7d1f,0x7d12,0x7d0f,0x7d0c,0x7f5c,0x7f61,0x7f5e,
+ 0x7f60,0x7f5d,0x7f5b,0x7f96,0x7f92,0x7fc3,0x7fc2,0x7fc0,0x8016,0x803e,
+ 0x8039,0x80fa,0x80f2,0x80f9,0x80f5,0x8101,0x80fb,0x8100,0x8201,0x822f,
+ 0x8225,0x8333,0x832d,0x8344,0x8319,0x8351,0x8325,0x8356,0x833f,0x8341,
+ 0x8326,0x831c,0x8322
+ },{
+ 0x8342,0x834e,0x831b,0x832a,0x8308,0x833c,0x834d,0x8316,0x8324,0x8320,
+ 0x8337,0x832f,0x8329,0x8347,0x8345,0x834c,0x8353,0x831e,0x832c,0x834b,
+ 0x8327,0x8348,0x8653,0x8652,0x86a2,0x86a8,0x8696,0x868d,0x8691,0x869e,
+ 0x8687,0x8697,0x8686,0x868b,0x869a,0x8685,0x86a5,0x8699,0x86a1,0x86a7,
+ 0x8695,0x8698,0x868e,0x869d,0x8690,0x8694,0x8843,0x8844,0x886d,0x8875,
+ 0x8876,0x8872,0x8880,0x8871,0x887f,0x886f,0x8883,0x887e,0x8874,0x887c,
+ 0x8a12,0x8c47,0x8c57,0x8c7b,0x8ca4,0x8ca3,0x8d76,0x8d78,0x8db5,0x8db7,
+ 0x8db6,0x8ed1,0x8ed3,0x8ffe,0x8ff5,0x9002,0x8fff,0x8ffb,0x9004,0x8ffc,
+ 0x8ff6,0x90d6,0x90e0,0x90d9,0x90da,0x90e3,0x90df,0x90e5,0x90d8,0x90db,
+ 0x90d7,0x90dc,0x90e4,0x9150
+ }
+ },
+ { /* ku 34 */
+ {
+ 0x914e,0x914f,0x91d5,0x91e2,0x91da,0x965c,0x965f,0x96bc,0x98e3,0x9adf,
+ 0x9b2f,0x4e7f,0x5070,0x506a,0x5061,0x505e,0x5060,0x5053,0x504b,0x505d,
+ 0x5072,0x5048,0x504d,0x5041,0x505b,0x504a,0x5062,0x5015,0x5045,0x505f,
+ 0x5069,0x506b,0x5063,0x5064,0x5046,0x5040,0x506e,0x5073,0x5057,0x5051,
+ 0x51d0,0x526b,0x526d,0x526c,0x526e,0x52d6,0x52d3,0x532d,0x539c,0x5575,
+ 0x5576,0x553c,0x554d,0x5550,0x5534,0x552a,0x5551,0x5562,0x5536,0x5535,
+ 0x5530,0x5552,0x5545
+ },{
+ 0x550c,0x5532,0x5565,0x554e,0x5539,0x5548,0x552d,0x553b,0x5540,0x554b,
+ 0x570a,0x5707,0x57fb,0x5814,0x57e2,0x57f6,0x57dc,0x57f4,0x5800,0x57ed,
+ 0x57fd,0x5808,0x57f8,0x580b,0x57f3,0x57cf,0x5807,0x57ee,0x57e3,0x57f2,
+ 0x57e5,0x57ec,0x57e1,0x580e,0x57fc,0x5810,0x57e7,0x5801,0x580c,0x57f1,
+ 0x57e9,0x57f0,0x580d,0x5804,0x595c,0x5a60,0x5a58,0x5a55,0x5a67,0x5a5e,
+ 0x5a38,0x5a35,0x5a6d,0x5a50,0x5a5f,0x5a65,0x5a6c,0x5a53,0x5a64,0x5a57,
+ 0x5a43,0x5a5d,0x5a52,0x5a44,0x5a5b,0x5a48,0x5a8e,0x5a3e,0x5a4d,0x5a39,
+ 0x5a4c,0x5a70,0x5a69,0x5a47,0x5a51,0x5a56,0x5a42,0x5a5c,0x5b72,0x5b6e,
+ 0x5bc1,0x5bc0,0x5c59,0x5d1e,0x5d0b,0x5d1d,0x5d1a,0x5d20,0x5d0c,0x5d28,
+ 0x5d0d,0x5d26,0x5d25,0x5d0f
+ }
+ },
+ { /* ku 35 */
+ {
+ 0x5d30,0x5d12,0x5d23,0x5d1f,0x5d2e,0x5e3e,0x5e34,0x5eb1,0x5eb4,0x5eb9,
+ 0x5eb2,0x5eb3,0x5f36,0x5f38,0x5f9b,0x5f96,0x5f9f,0x608a,0x6090,0x6086,
+ 0x60be,0x60b0,0x60ba,0x60d3,0x60d4,0x60cf,0x60e4,0x60d9,0x60dd,0x60c8,
+ 0x60b1,0x60db,0x60b7,0x60ca,0x60bf,0x60c3,0x60cd,0x60c0,0x6332,0x6365,
+ 0x638a,0x6382,0x637d,0x63bd,0x639e,0x63ad,0x639d,0x6397,0x63ab,0x638e,
+ 0x636f,0x6387,0x6390,0x636e,0x63af,0x6375,0x639c,0x636d,0x63ae,0x637c,
+ 0x63a4,0x633b,0x639f
+ },{
+ 0x6378,0x6385,0x6381,0x6391,0x638d,0x6370,0x6553,0x65cd,0x6665,0x6661,
+ 0x665b,0x6659,0x665c,0x6662,0x6718,0x6879,0x6887,0x6890,0x689c,0x686d,
+ 0x686e,0x68ae,0x68ab,0x6956,0x686f,0x68a3,0x68ac,0x68a9,0x6875,0x6874,
+ 0x68b2,0x688f,0x6877,0x6892,0x687c,0x686b,0x6872,0x68aa,0x6880,0x6871,
+ 0x687e,0x689b,0x6896,0x688b,0x68a0,0x6889,0x68a4,0x6878,0x687b,0x6891,
+ 0x688c,0x688a,0x687d,0x6b36,0x6b33,0x6b37,0x6b38,0x6b91,0x6b8f,0x6b8d,
+ 0x6b8e,0x6b8c,0x6c2a,0x6dc0,0x6dab,0x6db4,0x6db3,0x6e74,0x6dac,0x6de9,
+ 0x6de2,0x6db7,0x6df6,0x6dd4,0x6e00,0x6dc8,0x6de0,0x6ddf,0x6dd6,0x6dbe,
+ 0x6de5,0x6ddc,0x6ddd,0x6ddb,0x6df4,0x6dca,0x6dbd,0x6ded,0x6df0,0x6dba,
+ 0x6dd5,0x6dc2,0x6dcf,0x6dc9
+ }
+ },
+ { /* ku 36 */
+ {
+ 0x6dd0,0x6df2,0x6dd3,0x6dfd,0x6dd7,0x6dcd,0x6de3,0x6dbb,0x70fa,0x710d,
+ 0x70f7,0x7117,0x70f4,0x710c,0x70f0,0x7104,0x70f3,0x7110,0x70fc,0x70ff,
+ 0x7106,0x7113,0x7100,0x70f8,0x70f6,0x710b,0x7102,0x710e,0x727e,0x727b,
+ 0x727c,0x727f,0x731d,0x7317,0x7307,0x7311,0x7318,0x730a,0x7308,0x72ff,
+ 0x730f,0x731e,0x7388,0x73f6,0x73f8,0x73f5,0x7404,0x7401,0x73fd,0x7407,
+ 0x7400,0x73fa,0x73fc,0x73ff,0x740c,0x740b,0x73f4,0x7408,0x7564,0x7563,
+ 0x75ce,0x75d2,0x75cf
+ },{
+ 0x75cb,0x75cc,0x75d1,0x75d0,0x768f,0x7689,0x76d3,0x7739,0x772f,0x772d,
+ 0x7731,0x7732,0x7734,0x7733,0x773d,0x7725,0x773b,0x7735,0x7848,0x7852,
+ 0x7849,0x784d,0x784a,0x784c,0x7826,0x7845,0x7850,0x7964,0x7967,0x7969,
+ 0x796a,0x7963,0x796b,0x7961,0x79bb,0x79fa,0x79f8,0x79f6,0x79f7,0x7a8f,
+ 0x7a94,0x7a90,0x7b35,0x7b47,0x7b34,0x7b25,0x7b30,0x7b22,0x7b24,0x7b33,
+ 0x7b18,0x7b2a,0x7b1d,0x7b31,0x7b2b,0x7b2d,0x7b2f,0x7b32,0x7b38,0x7b1a,
+ 0x7b23,0x7c94,0x7c98,0x7c96,0x7ca3,0x7d35,0x7d3d,0x7d38,0x7d36,0x7d3a,
+ 0x7d45,0x7d2c,0x7d29,0x7d41,0x7d47,0x7d3e,0x7d3f,0x7d4a,0x7d3b,0x7d28,
+ 0x7f63,0x7f95,0x7f9c,0x7f9d,0x7f9b,0x7fca,0x7fcb,0x7fcd,0x7fd0,0x7fd1,
+ 0x7fc7,0x7fcf,0x7fc9,0x801f
+ }
+ },
+ { /* ku 37 */
+ {
+ 0x801e,0x801b,0x8047,0x8043,0x8048,0x8118,0x8125,0x8119,0x811b,0x812d,
+ 0x811f,0x812c,0x811e,0x8121,0x8115,0x8127,0x811d,0x8122,0x8211,0x8238,
+ 0x8233,0x823a,0x8234,0x8232,0x8274,0x8390,0x83a3,0x83a8,0x838d,0x837a,
+ 0x8373,0x83a4,0x8374,0x838f,0x8381,0x8395,0x8399,0x8375,0x8394,0x83a9,
+ 0x837d,0x8383,0x838c,0x839d,0x839b,0x83aa,0x838b,0x837e,0x83a5,0x83af,
+ 0x8388,0x8397,0x83b0,0x837f,0x83a6,0x8387,0x83ae,0x8376,0x839a,0x8659,
+ 0x8656,0x86bf,0x86b7
+ },{
+ 0x86c2,0x86c1,0x86c5,0x86ba,0x86b0,0x86c8,0x86b9,0x86b3,0x86b8,0x86cc,
+ 0x86b4,0x86bb,0x86bc,0x86c3,0x86bd,0x86be,0x8852,0x8889,0x8895,0x88a8,
+ 0x88a2,0x88aa,0x889a,0x8891,0x88a1,0x889f,0x8898,0x88a7,0x8899,0x889b,
+ 0x8897,0x88a4,0x88ac,0x888c,0x8893,0x888e,0x8982,0x89d6,0x89d9,0x89d5,
+ 0x8a30,0x8a27,0x8a2c,0x8a1e,0x8c39,0x8c3b,0x8c5c,0x8c5d,0x8c7d,0x8ca5,
+ 0x8d7d,0x8d7b,0x8d79,0x8dbc,0x8dc2,0x8db9,0x8dbf,0x8dc1,0x8ed8,0x8ede,
+ 0x8edd,0x8edc,0x8ed7,0x8ee0,0x8ee1,0x9024,0x900b,0x9011,0x901c,0x900c,
+ 0x9021,0x90ef,0x90ea,0x90f0,0x90f4,0x90f2,0x90f3,0x90d4,0x90eb,0x90ec,
+ 0x90e9,0x9156,0x9158,0x915a,0x9153,0x9155,0x91ec,0x91f4,0x91f1,0x91f3,
+ 0x91f8,0x91e4,0x91f9,0x91ea
+ }
+ },
+ { /* ku 38 */
+ {
+ 0x91eb,0x91f7,0x91e8,0x91ee,0x957a,0x9586,0x9588,0x967c,0x966d,0x966b,
+ 0x9671,0x966f,0x96bf,0x976a,0x9804,0x98e5,0x9997,0x509b,0x5095,0x5094,
+ 0x509e,0x508b,0x50a3,0x5083,0x508c,0x508e,0x509d,0x5068,0x509c,0x5092,
+ 0x5082,0x5087,0x515f,0x51d4,0x5312,0x5311,0x53a4,0x53a7,0x5591,0x55a8,
+ 0x55a5,0x55ad,0x5577,0x5645,0x55a2,0x5593,0x5588,0x558f,0x55b5,0x5581,
+ 0x55a3,0x5592,0x55a4,0x557d,0x558c,0x55a6,0x557f,0x5595,0x55a1,0x558e,
+ 0x570c,0x5829,0x5837
+ },{
+ 0x5819,0x581e,0x5827,0x5823,0x5828,0x57f5,0x5848,0x5825,0x581c,0x581b,
+ 0x5833,0x583f,0x5836,0x582e,0x5839,0x5838,0x582d,0x582c,0x583b,0x5961,
+ 0x5aaf,0x5a94,0x5a9f,0x5a7a,0x5aa2,0x5a9e,0x5a78,0x5aa6,0x5a7c,0x5aa5,
+ 0x5aac,0x5a95,0x5aae,0x5a37,0x5a84,0x5a8a,0x5a97,0x5a83,0x5a8b,0x5aa9,
+ 0x5a7b,0x5a7d,0x5a8c,0x5a9c,0x5a8f,0x5a93,0x5a9d,0x5bea,0x5bcd,0x5bcb,
+ 0x5bd4,0x5bd1,0x5bca,0x5bce,0x5c0c,0x5c30,0x5d37,0x5d43,0x5d6b,0x5d41,
+ 0x5d4b,0x5d3f,0x5d35,0x5d51,0x5d4e,0x5d55,0x5d33,0x5d3a,0x5d52,0x5d3d,
+ 0x5d31,0x5d59,0x5d42,0x5d39,0x5d49,0x5d38,0x5d3c,0x5d32,0x5d36,0x5d40,
+ 0x5d45,0x5e44,0x5e41,0x5f58,0x5fa6,0x5fa5,0x5fab,0x60c9,0x60b9,0x60cc,
+ 0x60e2,0x60ce,0x60c4,0x6114
+ }
+ },
+ { /* ku 39 */
+ {
+ 0x60f2,0x610a,0x6116,0x6105,0x60f5,0x6113,0x60f8,0x60fc,0x60fe,0x60c1,
+ 0x6103,0x6118,0x611d,0x6110,0x60ff,0x6104,0x610b,0x624a,0x6394,0x63b1,
+ 0x63b0,0x63ce,0x63e5,0x63e8,0x63ef,0x63c3,0x649d,0x63f3,0x63ca,0x63e0,
+ 0x63f6,0x63d5,0x63f2,0x63f5,0x6461,0x63df,0x63be,0x63dd,0x63dc,0x63c4,
+ 0x63d8,0x63d3,0x63c2,0x63c7,0x63cc,0x63cb,0x63c8,0x63f0,0x63d7,0x63d9,
+ 0x6532,0x6567,0x656a,0x6564,0x655c,0x6568,0x6565,0x658c,0x659d,0x659e,
+ 0x65ae,0x65d0,0x65d2
+ },{
+ 0x667c,0x666c,0x667b,0x6680,0x6671,0x6679,0x666a,0x6672,0x6701,0x690c,
+ 0x68d3,0x6904,0x68dc,0x692a,0x68ec,0x68ea,0x68f1,0x690f,0x68d6,0x68f7,
+ 0x68eb,0x68e4,0x68f6,0x6913,0x6910,0x68f3,0x68e1,0x6907,0x68cc,0x6908,
+ 0x6970,0x68b4,0x6911,0x68ef,0x68c6,0x6914,0x68f8,0x68d0,0x68fd,0x68fc,
+ 0x68e8,0x690b,0x690a,0x6917,0x68ce,0x68c8,0x68dd,0x68de,0x68e6,0x68f4,
+ 0x68d1,0x6906,0x68d4,0x68e9,0x6915,0x6925,0x68c7,0x6b39,0x6b3b,0x6b3f,
+ 0x6b3c,0x6b94,0x6b97,0x6b99,0x6b95,0x6bbd,0x6bf0,0x6bf2,0x6bf3,0x6c30,
+ 0x6dfc,0x6e46,0x6e47,0x6e1f,0x6e49,0x6e88,0x6e3c,0x6e3d,0x6e45,0x6e62,
+ 0x6e2b,0x6e3f,0x6e41,0x6e5d,0x6e73,0x6e1c,0x6e33,0x6e4b,0x6e40,0x6e51,
+ 0x6e3b,0x6e03,0x6e2e,0x6e5e
+ }
+ },
+ { /* ku 3a */
+ {
+ 0x6e68,0x6e5c,0x6e61,0x6e31,0x6e28,0x6e60,0x6e71,0x6e6b,0x6e39,0x6e22,
+ 0x6e30,0x6e53,0x6e65,0x6e27,0x6e78,0x6e64,0x6e77,0x6e55,0x6e79,0x6e52,
+ 0x6e66,0x6e35,0x6e36,0x6e5a,0x7120,0x711e,0x712f,0x70fb,0x712e,0x7131,
+ 0x7123,0x7125,0x7122,0x7132,0x711f,0x7128,0x713a,0x711b,0x724b,0x725a,
+ 0x7288,0x7289,0x7286,0x7285,0x728b,0x7312,0x730b,0x7330,0x7322,0x7331,
+ 0x7333,0x7327,0x7332,0x732d,0x7326,0x7323,0x7335,0x730c,0x742e,0x742c,
+ 0x7430,0x742b,0x7416
+ },{
+ 0x741a,0x7421,0x742d,0x7431,0x7424,0x7423,0x741d,0x7429,0x7420,0x7432,
+ 0x74fb,0x752f,0x756f,0x756c,0x75e7,0x75da,0x75e1,0x75e6,0x75dd,0x75df,
+ 0x75e4,0x75d7,0x7695,0x7692,0x76da,0x7746,0x7747,0x7744,0x774d,0x7745,
+ 0x774a,0x774e,0x774b,0x774c,0x77de,0x77ec,0x7860,0x7864,0x7865,0x785c,
+ 0x786d,0x7871,0x786a,0x786e,0x7870,0x7869,0x7868,0x785e,0x7862,0x7974,
+ 0x7973,0x7972,0x7970,0x7a02,0x7a0a,0x7a03,0x7a0c,0x7a04,0x7a99,0x7ae6,
+ 0x7ae4,0x7b4a,0x7b3b,0x7b44,0x7b48,0x7b4c,0x7b4e,0x7b40,0x7b58,0x7b45,
+ 0x7ca2,0x7c9e,0x7ca8,0x7ca1,0x7d58,0x7d6f,0x7d63,0x7d53,0x7d56,0x7d67,
+ 0x7d6a,0x7d4f,0x7d6d,0x7d5c,0x7d6b,0x7d52,0x7d54,0x7d69,0x7d51,0x7d5f,
+ 0x7d4e,0x7f3e,0x7f3f,0x7f65
+ }
+ },
+ { /* ku 3b */
+ {
+ 0x7f66,0x7fa2,0x7fa0,0x7fa1,0x7fd7,0x8051,0x804f,0x8050,0x80fe,0x80d4,
+ 0x8143,0x814a,0x8152,0x814f,0x8147,0x813d,0x814d,0x813a,0x81e6,0x81ee,
+ 0x81f7,0x81f8,0x81f9,0x8204,0x823c,0x823d,0x823f,0x8275,0x833b,0x83cf,
+ 0x83f9,0x8423,0x83c0,0x83e8,0x8412,0x83e7,0x83e4,0x83fc,0x83f6,0x8410,
+ 0x83c6,0x83c8,0x83eb,0x83e3,0x83bf,0x8401,0x83dd,0x83e5,0x83d8,0x83ff,
+ 0x83e1,0x83cb,0x83ce,0x83d6,0x83f5,0x83c9,0x8409,0x840f,0x83de,0x8411,
+ 0x8406,0x83c2,0x83f3
+ },{
+ 0x83d5,0x83fa,0x83c7,0x83d1,0x83ea,0x8413,0x83c3,0x83ec,0x83ee,0x83c4,
+ 0x83fb,0x83d7,0x83e2,0x841b,0x83db,0x83fe,0x86d8,0x86e2,0x86e6,0x86d3,
+ 0x86e3,0x86da,0x86ea,0x86dd,0x86eb,0x86dc,0x86ec,0x86e9,0x86d7,0x86e8,
+ 0x86d1,0x8848,0x8856,0x8855,0x88ba,0x88d7,0x88b9,0x88b8,0x88c0,0x88be,
+ 0x88b6,0x88bc,0x88b7,0x88bd,0x88b2,0x8901,0x88c9,0x8995,0x8998,0x8997,
+ 0x89dd,0x89da,0x89db,0x8a4e,0x8a4d,0x8a39,0x8a59,0x8a40,0x8a57,0x8a58,
+ 0x8a44,0x8a45,0x8a52,0x8a48,0x8a51,0x8a4a,0x8a4c,0x8a4f,0x8c5f,0x8c81,
+ 0x8c80,0x8cba,0x8cbe,0x8cb0,0x8cb9,0x8cb5,0x8d84,0x8d80,0x8d89,0x8dd8,
+ 0x8dd3,0x8dcd,0x8dc7,0x8dd6,0x8ddc,0x8dcf,0x8dd5,0x8dd9,0x8dc8,0x8dd7,
+ 0x8dc5,0x8eef,0x8ef7,0x8efa
+ }
+ },
+ { /* ku 3c */
+ {
+ 0x8ef9,0x8ee6,0x8eee,0x8ee5,0x8ef5,0x8ee7,0x8ee8,0x8ef6,0x8eeb,0x8ef1,
+ 0x8eec,0x8ef4,0x8ee9,0x902d,0x9034,0x902f,0x9106,0x912c,0x9104,0x90ff,
+ 0x90fc,0x9108,0x90f9,0x90fb,0x9101,0x9100,0x9107,0x9105,0x9103,0x9161,
+ 0x9164,0x915f,0x9162,0x9160,0x9201,0x920a,0x9225,0x9203,0x921a,0x9226,
+ 0x920f,0x920c,0x9200,0x9212,0x91ff,0x91fd,0x9206,0x9204,0x9227,0x9202,
+ 0x921c,0x9224,0x9219,0x9217,0x9205,0x9216,0x957b,0x958d,0x958c,0x9590,
+ 0x9687,0x967e,0x9688
+ },{
+ 0x9689,0x9683,0x9680,0x96c2,0x96c8,0x96c3,0x96f1,0x96f0,0x976c,0x9770,
+ 0x976e,0x9807,0x98a9,0x98eb,0x9ce6,0x9ef9,0x4e83,0x4e84,0x4eb6,0x50bd,
+ 0x50bf,0x50c6,0x50ae,0x50c4,0x50ca,0x50b4,0x50c8,0x50c2,0x50b0,0x50c1,
+ 0x50ba,0x50b1,0x50cb,0x50c9,0x50b6,0x50b8,0x51d7,0x527a,0x5278,0x527b,
+ 0x527c,0x55c3,0x55db,0x55cc,0x55d0,0x55cb,0x55ca,0x55dd,0x55c0,0x55d4,
+ 0x55c4,0x55e9,0x55bf,0x55d2,0x558d,0x55cf,0x55d5,0x55e2,0x55d6,0x55c8,
+ 0x55f2,0x55cd,0x55d9,0x55c2,0x5714,0x5853,0x5868,0x5864,0x584f,0x584d,
+ 0x5849,0x586f,0x5855,0x584e,0x585d,0x5859,0x5865,0x585b,0x583d,0x5863,
+ 0x5871,0x58fc,0x5ac7,0x5ac4,0x5acb,0x5aba,0x5ab8,0x5ab1,0x5ab5,0x5ab0,
+ 0x5abf,0x5ac8,0x5abb,0x5ac6
+ }
+ },
+ { /* ku 3d */
+ {
+ 0x5ab7,0x5ac0,0x5aca,0x5ab4,0x5ab6,0x5acd,0x5ab9,0x5a90,0x5bd6,0x5bd8,
+ 0x5bd9,0x5c1f,0x5c33,0x5d71,0x5d63,0x5d4a,0x5d65,0x5d72,0x5d6c,0x5d5e,
+ 0x5d68,0x5d67,0x5d62,0x5df0,0x5e4f,0x5e4e,0x5e4a,0x5e4d,0x5e4b,0x5ec5,
+ 0x5ecc,0x5ec6,0x5ecb,0x5ec7,0x5f40,0x5faf,0x5fad,0x60f7,0x6149,0x614a,
+ 0x612b,0x6145,0x6136,0x6132,0x612e,0x6146,0x612f,0x614f,0x6129,0x6140,
+ 0x6220,0x9168,0x6223,0x6225,0x6224,0x63c5,0x63f1,0x63eb,0x6410,0x6412,
+ 0x6409,0x6420,0x6424
+ },{
+ 0x6433,0x6443,0x641f,0x6415,0x6418,0x6439,0x6437,0x6422,0x6423,0x640c,
+ 0x6426,0x6430,0x6428,0x6441,0x6435,0x642f,0x640a,0x641a,0x6440,0x6425,
+ 0x6427,0x640b,0x63e7,0x641b,0x642e,0x6421,0x640e,0x656f,0x6592,0x65d3,
+ 0x6686,0x668c,0x6695,0x6690,0x668b,0x668a,0x6699,0x6694,0x6678,0x6720,
+ 0x6966,0x695f,0x6938,0x694e,0x6962,0x6971,0x693f,0x6945,0x696a,0x6939,
+ 0x6942,0x6957,0x6959,0x697a,0x6948,0x6949,0x6935,0x696c,0x6933,0x693d,
+ 0x6965,0x68f0,0x6978,0x6934,0x6969,0x6940,0x696f,0x6944,0x6976,0x6958,
+ 0x6941,0x6974,0x694c,0x693b,0x694b,0x6937,0x695c,0x694f,0x6951,0x6932,
+ 0x6952,0x692f,0x697b,0x693c,0x6b46,0x6b45,0x6b43,0x6b42,0x6b48,0x6b41,
+ 0x6b9b,0xfa0d,0x6bfb,0x6bfc
+ }
+ },
+ { /* ku 3e */
+ {
+ 0x6bf9,0x6bf7,0x6bf8,0x6e9b,0x6ed6,0x6ec8,0x6e8f,0x6ec0,0x6e9f,0x6e93,
+ 0x6e94,0x6ea0,0x6eb1,0x6eb9,0x6ec6,0x6ed2,0x6ebd,0x6ec1,0x6e9e,0x6ec9,
+ 0x6eb7,0x6eb0,0x6ecd,0x6ea6,0x6ecf,0x6eb2,0x6ebe,0x6ec3,0x6edc,0x6ed8,
+ 0x6e99,0x6e92,0x6e8e,0x6e8d,0x6ea4,0x6ea1,0x6ebf,0x6eb3,0x6ed0,0x6eca,
+ 0x6e97,0x6eae,0x6ea3,0x7147,0x7154,0x7152,0x7163,0x7160,0x7141,0x715d,
+ 0x7162,0x7172,0x7178,0x716a,0x7161,0x7142,0x7158,0x7143,0x714b,0x7170,
+ 0x715f,0x7150,0x7153
+ },{
+ 0x7144,0x714d,0x715a,0x724f,0x728d,0x728c,0x7291,0x7290,0x728e,0x733c,
+ 0x7342,0x733b,0x733a,0x7340,0x734a,0x7349,0x7444,0x744a,0x744b,0x7452,
+ 0x7451,0x7457,0x7440,0x744f,0x7450,0x744e,0x7442,0x7446,0x744d,0x7454,
+ 0x74e1,0x74ff,0x74fe,0x74fd,0x751d,0x7579,0x7577,0x6983,0x75ef,0x760f,
+ 0x7603,0x75f7,0x75fe,0x75fc,0x75f9,0x75f8,0x7610,0x75fb,0x75f6,0x75ed,
+ 0x75f5,0x75fd,0x7699,0x76b5,0x76dd,0x7755,0x775f,0x7760,0x7752,0x7756,
+ 0x775a,0x7769,0x7767,0x7754,0x7759,0x776d,0x77e0,0x7887,0x789a,0x7894,
+ 0x788f,0x7884,0x7895,0x7885,0x7886,0x78a1,0x7883,0x7879,0x7899,0x7880,
+ 0x7896,0x787b,0x797c,0x7982,0x797d,0x7979,0x7a11,0x7a18,0x7a19,0x7a12,
+ 0x7a17,0x7a15,0x7a22,0x7a13
+ }
+ },
+ { /* ku 3f */
+ {
+ 0x7a1b,0x7a10,0x7aa3,0x7aa2,0x7a9e,0x7aeb,0x7b66,0x7b64,0x7b6d,0x7b74,
+ 0x7b69,0x7b72,0x7b65,0x7b73,0x7b71,0x7b70,0x7b61,0x7b78,0x7b76,0x7b63,
+ 0x7cb2,0x7cb4,0x7caf,0x7d88,0x7d86,0x7d80,0x7d8d,0x7d7f,0x7d85,0x7d7a,
+ 0x7d8e,0x7d7b,0x7d83,0x7d7c,0x7d8c,0x7d94,0x7d84,0x7d7d,0x7d92,0x7f6d,
+ 0x7f6b,0x7f67,0x7f68,0x7f6c,0x7fa6,0x7fa5,0x7fa7,0x7fdb,0x7fdc,0x8021,
+ 0x8164,0x8160,0x8177,0x815c,0x8169,0x815b,0x8162,0x8172,0x6721,0x815e,
+ 0x8176,0x8167,0x816f
+ },{
+ 0x8144,0x8161,0x821d,0x8249,0x8244,0x8240,0x8242,0x8245,0x84f1,0x843f,
+ 0x8456,0x8476,0x8479,0x848f,0x848d,0x8465,0x8451,0x8440,0x8486,0x8467,
+ 0x8430,0x844d,0x847d,0x845a,0x8459,0x8474,0x8473,0x845d,0x8507,0x845e,
+ 0x8437,0x843a,0x8434,0x847a,0x8443,0x8478,0x8432,0x8445,0x8429,0x83d9,
+ 0x844b,0x842f,0x8442,0x842d,0x845f,0x8470,0x8439,0x844e,0x844c,0x8452,
+ 0x846f,0x84c5,0x848e,0x843b,0x8447,0x8436,0x8433,0x8468,0x847e,0x8444,
+ 0x842b,0x8460,0x8454,0x846e,0x8450,0x870b,0x8704,0x86f7,0x870c,0x86fa,
+ 0x86d6,0x86f5,0x874d,0x86f8,0x870e,0x8709,0x8701,0x86f6,0x870d,0x8705,
+ 0x88d6,0x88cb,0x88cd,0x88ce,0x88de,0x88db,0x88da,0x88cc,0x88d0,0x8985,
+ 0x899b,0x89df,0x89e5,0x89e4
+ }
+ },
+ { /* ku 40 */
+ {
+ 0x89e1,0x89e0,0x89e2,0x89dc,0x89e6,0x8a76,0x8a86,0x8a7f,0x8a61,0x8a3f,
+ 0x8a77,0x8a82,0x8a84,0x8a75,0x8a83,0x8a81,0x8a74,0x8a7a,0x8c3c,0x8c4b,
+ 0x8c4a,0x8c65,0x8c64,0x8c66,0x8c86,0x8c84,0x8c85,0x8ccc,0x8d68,0x8d69,
+ 0x8d91,0x8d8c,0x8d8e,0x8d8f,0x8d8d,0x8d93,0x8d94,0x8d90,0x8d92,0x8df0,
+ 0x8de0,0x8dec,0x8df1,0x8dee,0x8dd0,0x8de9,0x8de3,0x8de2,0x8de7,0x8df2,
+ 0x8deb,0x8df4,0x8f06,0x8eff,0x8f01,0x8f00,0x8f05,0x8f07,0x8f08,0x8f02,
+ 0x8f0b,0x9052,0x903f
+ },{
+ 0x9044,0x9049,0x903d,0x9110,0x910d,0x910f,0x9111,0x9116,0x9114,0x910b,
+ 0x910e,0x916e,0x916f,0x9248,0x9252,0x9230,0x923a,0x9266,0x9233,0x9265,
+ 0x925e,0x9283,0x922e,0x924a,0x9246,0x926d,0x926c,0x924f,0x9260,0x9267,
+ 0x926f,0x9236,0x9261,0x9270,0x9231,0x9254,0x9263,0x9250,0x9272,0x924e,
+ 0x9253,0x924c,0x9256,0x9232,0x959f,0x959c,0x959e,0x959b,0x9692,0x9693,
+ 0x9691,0x9697,0x96ce,0x96fa,0x96fd,0x96f8,0x96f5,0x9773,0x9777,0x9778,
+ 0x9772,0x980f,0x980d,0x980e,0x98ac,0x98f6,0x98f9,0x99af,0x99b2,0x99b0,
+ 0x99b5,0x9aad,0x9aab,0x9b5b,0x9cea,0x9ced,0x9ce7,0x9e80,0x9efd,0x50e6,
+ 0x50d4,0x50d7,0x50e8,0x50f3,0x50db,0x50ea,0x50dd,0x50e4,0x50d3,0x50ec,
+ 0x50f0,0x50ef,0x50e3,0x50e0
+ }
+ },
+ { /* ku 41 */
+ {
+ 0x51d8,0x5280,0x5281,0x52e9,0x52eb,0x5330,0x53ac,0x5627,0x5615,0x560c,
+ 0x5612,0x55fc,0x560f,0x561c,0x5601,0x5613,0x5602,0x55fa,0x561d,0x5604,
+ 0x55ff,0x55f9,0x5889,0x587c,0x5890,0x5898,0x5886,0x5881,0x587f,0x5874,
+ 0x588b,0x587a,0x5887,0x5891,0x588e,0x5876,0x5882,0x5888,0x587b,0x5894,
+ 0x588f,0x58fe,0x596b,0x5adc,0x5aee,0x5ae5,0x5ad5,0x5aea,0x5ada,0x5aed,
+ 0x5aeb,0x5af3,0x5ae2,0x5ae0,0x5adb,0x5aec,0x5ade,0x5add,0x5ad9,0x5ae8,
+ 0x5adf,0x5b77,0x5be0
+ },{
+ 0x5be3,0x5c63,0x5d82,0x5d80,0x5d7d,0x5d86,0x5d7a,0x5d81,0x5d77,0x5d8a,
+ 0x5d89,0x5d88,0x5d7e,0x5d7c,0x5d8d,0x5d79,0x5d7f,0x5e58,0x5e59,0x5e53,
+ 0x5ed8,0x5ed1,0x5ed7,0x5ece,0x5edc,0x5ed5,0x5ed9,0x5ed2,0x5ed4,0x5f44,
+ 0x5f43,0x5f6f,0x5fb6,0x612c,0x6128,0x6141,0x615e,0x6171,0x6173,0x6152,
+ 0x6153,0x6172,0x616c,0x6180,0x6174,0x6154,0x617a,0x615b,0x6165,0x613b,
+ 0x616a,0x6161,0x6156,0x6229,0x6227,0x622b,0x642b,0x644d,0x645b,0x645d,
+ 0x6474,0x6476,0x6472,0x6473,0x647d,0x6475,0x6466,0x64a6,0x644e,0x6482,
+ 0x645e,0x645c,0x644b,0x6453,0x6460,0x6450,0x647f,0x643f,0x646c,0x646b,
+ 0x6459,0x6465,0x6477,0x6573,0x65a0,0x66a1,0x66a0,0x669f,0x6705,0x6704,
+ 0x6722,0x69b1,0x69b6,0x69c9
+ }
+ },
+ { /* ku 42 */
+ {
+ 0x69a0,0x69ce,0x6996,0x69b0,0x69ac,0x69bc,0x6991,0x6999,0x698e,0x69a7,
+ 0x698d,0x69a9,0x69be,0x69af,0x69bf,0x69c4,0x69bd,0x69a4,0x69d4,0x69b9,
+ 0x69ca,0x699a,0x69cf,0x69b3,0x6993,0x69aa,0x69a1,0x699e,0x69d9,0x6997,
+ 0x6990,0x69c2,0x69b5,0x69a5,0x69c6,0x6b4a,0x6b4d,0x6b4b,0x6b9e,0x6b9f,
+ 0x6ba0,0x6bc3,0x6bc4,0x6bfe,0x6ece,0x6ef5,0x6ef1,0x6f03,0x6f25,0x6ef8,
+ 0x6f37,0x6efb,0x6f2e,0x6f09,0x6f4e,0x6f19,0x6f1a,0x6f27,0x6f18,0x6f3b,
+ 0x6f12,0x6eed,0x6f0a
+ },{
+ 0x6f36,0x6f73,0x6ef9,0x6eee,0x6f2d,0x6f40,0x6f30,0x6f3c,0x6f35,0x6eeb,
+ 0x6f07,0x6f0e,0x6f43,0x6f05,0x6efd,0x6ef6,0x6f39,0x6f1c,0x6efc,0x6f3a,
+ 0x6f1f,0x6f0d,0x6f1e,0x6f08,0x6f21,0x7187,0x7190,0x7189,0x7180,0x7185,
+ 0x7182,0x718f,0x717b,0x7186,0x7181,0x7197,0x7244,0x7253,0x7297,0x7295,
+ 0x7293,0x7343,0x734d,0x7351,0x734c,0x7462,0x7473,0x7471,0x7475,0x7472,
+ 0x7467,0x746e,0x7500,0x7502,0x7503,0x757d,0x7590,0x7616,0x7608,0x760c,
+ 0x7615,0x7611,0x760a,0x7614,0x76b8,0x7781,0x777c,0x7785,0x7782,0x776e,
+ 0x7780,0x776f,0x777e,0x7783,0x78b2,0x78aa,0x78b4,0x78ad,0x78a8,0x787e,
+ 0x78ab,0x789e,0x78a5,0x78a0,0x78ac,0x78a2,0x78a4,0x7998,0x798a,0x798b,
+ 0x7996,0x7995,0x7994,0x7993
+ }
+ },
+ { /* ku 43 */
+ {
+ 0x7997,0x7988,0x7992,0x7990,0x7a2b,0x7a4a,0x7a30,0x7a2f,0x7a28,0x7a26,
+ 0x7aa8,0x7aab,0x7aac,0x7aee,0x7b88,0x7b9c,0x7b8a,0x7b91,0x7b90,0x7b96,
+ 0x7b8d,0x7b8c,0x7b9b,0x7b8e,0x7b85,0x7b98,0x5284,0x7b99,0x7ba4,0x7b82,
+ 0x7cbb,0x7cbf,0x7cbc,0x7cba,0x7da7,0x7db7,0x7dc2,0x7da3,0x7daa,0x7dc1,
+ 0x7dc0,0x7dc5,0x7d9d,0x7dce,0x7dc4,0x7dc6,0x7dcb,0x7dcc,0x7daf,0x7db9,
+ 0x7d96,0x7dbc,0x7d9f,0x7da6,0x7dae,0x7da9,0x7da1,0x7dc9,0x7f73,0x7fe2,
+ 0x7fe3,0x7fe5,0x7fde
+ },{
+ 0x8024,0x805d,0x805c,0x8189,0x8186,0x8183,0x8187,0x818d,0x818c,0x818b,
+ 0x8215,0x8497,0x84a4,0x84a1,0x849f,0x84ba,0x84ce,0x84c2,0x84ac,0x84ae,
+ 0x84ab,0x84b9,0x84b4,0x84c1,0x84cd,0x84aa,0x849a,0x84b1,0x84d0,0x849d,
+ 0x84a7,0x84bb,0x84a2,0x8494,0x84c7,0x84cc,0x849b,0x84a9,0x84af,0x84a8,
+ 0x84d6,0x8498,0x84b6,0x84cf,0x84a0,0x84d7,0x84d4,0x84d2,0x84db,0x84b0,
+ 0x8491,0x8661,0x8733,0x8723,0x8728,0x876b,0x8740,0x872e,0x871e,0x8721,
+ 0x8719,0x871b,0x8743,0x872c,0x8741,0x873e,0x8746,0x8720,0x8732,0x872a,
+ 0x872d,0x873c,0x8712,0x873a,0x8731,0x8735,0x8742,0x8726,0x8727,0x8738,
+ 0x8724,0x871a,0x8730,0x8711,0x88f7,0x88e7,0x88f1,0x88f2,0x88fa,0x88fe,
+ 0x88ee,0x88fc,0x88f6,0x88fb
+ }
+ },
+ { /* ku 44 */
+ {
+ 0x88f0,0x88ec,0x88eb,0x899d,0x89a1,0x899f,0x899e,0x89e9,0x89eb,0x89e8,
+ 0x8aab,0x8a99,0x8a8b,0x8a92,0x8a8f,0x8a96,0x8c3d,0x8c68,0x8c69,0x8cd5,
+ 0x8ccf,0x8cd7,0x8d96,0x8e09,0x8e02,0x8dff,0x8e0d,0x8dfd,0x8e0a,0x8e03,
+ 0x8e07,0x8e06,0x8e05,0x8dfe,0x8e00,0x8e04,0x8f10,0x8f11,0x8f0e,0x8f0d,
+ 0x9123,0x911c,0x9120,0x9122,0x911f,0x911d,0x911a,0x9124,0x9121,0x911b,
+ 0x917a,0x9172,0x9179,0x9173,0x92a5,0x92a4,0x9276,0x929b,0x927a,0x92a0,
+ 0x9294,0x92aa,0x928d
+ },{
+ 0x92a6,0x929a,0x92ab,0x9279,0x9297,0x927f,0x92a3,0x92ee,0x928e,0x9282,
+ 0x9295,0x92a2,0x927d,0x9288,0x92a1,0x928a,0x9286,0x928c,0x9299,0x92a7,
+ 0x927e,0x9287,0x92a9,0x929d,0x928b,0x922d,0x969e,0x96a1,0x96ff,0x9758,
+ 0x977d,0x977a,0x977e,0x9783,0x9780,0x9782,0x977b,0x9784,0x9781,0x977f,
+ 0x97ce,0x97cd,0x9816,0x98ad,0x98ae,0x9902,0x9900,0x9907,0x999d,0x999c,
+ 0x99c3,0x99b9,0x99bb,0x99ba,0x99c2,0x99bd,0x99c7,0x9ab1,0x9ae3,0x9ae7,
+ 0x9b3e,0x9b3f,0x9b60,0x9b61,0x9b5f,0x9cf1,0x9cf2,0x9cf5,0x9ea7,0x50ff,
+ 0x5103,0x5130,0x50f8,0x5106,0x5107,0x50f6,0x50fe,0x510b,0x510c,0x50fd,
+ 0x510a,0x528b,0x528c,0x52f1,0x52ef,0x5648,0x5642,0x564c,0x5635,0x5641,
+ 0x564a,0x5649,0x5646,0x5658
+ }
+ },
+ { /* ku 45 */
+ {
+ 0x565a,0x5640,0x5633,0x563d,0x562c,0x563e,0x5638,0x562a,0x563a,0x571a,
+ 0x58ab,0x589d,0x58b1,0x58a0,0x58a3,0x58af,0x58ac,0x58a5,0x58a1,0x58ff,
+ 0x5aff,0x5af4,0x5afd,0x5af7,0x5af6,0x5b03,0x5af8,0x5b02,0x5af9,0x5b01,
+ 0x5b07,0x5b05,0x5b0f,0x5c67,0x5d99,0x5d97,0x5d9f,0x5d92,0x5da2,0x5d93,
+ 0x5d95,0x5da0,0x5d9c,0x5da1,0x5d9a,0x5d9e,0x5e69,0x5e5d,0x5e60,0x5e5c,
+ 0x7df3,0x5edb,0x5ede,0x5ee1,0x5f49,0x5fb2,0x618b,0x6183,0x6179,0x61b1,
+ 0x61b0,0x61a2,0x6189
+ },{
+ 0x619b,0x6193,0x61af,0x61ad,0x619f,0x6192,0x61aa,0x61a1,0x618d,0x6166,
+ 0x61b3,0x622d,0x646e,0x6470,0x6496,0x64a0,0x6485,0x6497,0x649c,0x648f,
+ 0x648b,0x648a,0x648c,0x64a3,0x649f,0x6468,0x64b1,0x6498,0x6576,0x657a,
+ 0x6579,0x657b,0x65b2,0x65b3,0x66b5,0x66b0,0x66a9,0x66b2,0x66b7,0x66aa,
+ 0x66af,0x6a00,0x6a06,0x6a17,0x69e5,0x69f8,0x6a15,0x69f1,0x69e4,0x6a20,
+ 0x69ff,0x69ec,0x69e2,0x6a1b,0x6a1d,0x69fe,0x6a27,0x69f2,0x69ee,0x6a14,
+ 0x69f7,0x69e7,0x6a40,0x6a08,0x69e6,0x69fb,0x6a0d,0x69fc,0x69eb,0x6a09,
+ 0x6a04,0x6a18,0x6a25,0x6a0f,0x69f6,0x6a26,0x6a07,0x69f4,0x6a16,0x6b51,
+ 0x6ba5,0x6ba3,0x6ba2,0x6ba6,0x6c01,0x6c00,0x6bff,0x6c02,0x6f41,0x6f26,
+ 0x6f7e,0x6f87,0x6fc6,0x6f92
+ }
+ },
+ { /* ku 46 */
+ {
+ 0x6f8d,0x6f89,0x6f8c,0x6f62,0x6f4f,0x6f85,0x6f5a,0x6f96,0x6f76,0x6f6c,
+ 0x6f82,0x6f55,0x6f72,0x6f52,0x6f50,0x6f57,0x6f94,0x6f93,0x6f5d,0x6f00,
+ 0x6f61,0x6f6b,0x6f7d,0x6f67,0x6f90,0x6f53,0x6f8b,0x6f69,0x6f7f,0x6f95,
+ 0x6f63,0x6f77,0x6f6a,0x6f7b,0x71b2,0x71af,0x719b,0x71b0,0x71a0,0x719a,
+ 0x71a9,0x71b5,0x719d,0x71a5,0x719e,0x71a4,0x71a1,0x71aa,0x719c,0x71a7,
+ 0x71b3,0x7298,0x729a,0x7358,0x7352,0x735e,0x735f,0x7360,0x735d,0x735b,
+ 0x7361,0x735a,0x7359
+ },{
+ 0x7362,0x7487,0x7489,0x748a,0x7486,0x7481,0x747d,0x7485,0x7488,0x747c,
+ 0x7479,0x7508,0x7507,0x757e,0x7625,0x761e,0x7619,0x761d,0x761c,0x7623,
+ 0x761a,0x7628,0x761b,0x769c,0x769d,0x769e,0x769b,0x778d,0x778f,0x7789,
+ 0x7788,0x78cd,0x78bb,0x78cf,0x78cc,0x78d1,0x78ce,0x78d4,0x78c8,0x78c3,
+ 0x78c4,0x78c9,0x799a,0x79a1,0x79a0,0x799c,0x79a2,0x799b,0x6b76,0x7a39,
+ 0x7ab2,0x7ab4,0x7ab3,0x7bb7,0x7bcb,0x7bbe,0x7bac,0x7bce,0x7baf,0x7bb9,
+ 0x7bca,0x7bb5,0x7cc5,0x7cc8,0x7ccc,0x7ccb,0x7df7,0x7ddb,0x7dea,0x7de7,
+ 0x7dd7,0x7de1,0x7e03,0x7dfa,0x7de6,0x7df6,0x7df1,0x7df0,0x7dee,0x7ddf,
+ 0x7f76,0x7fac,0x7fb0,0x7fad,0x7fed,0x7feb,0x7fea,0x7fec,0x7fe6,0x7fe8,
+ 0x8064,0x8067,0x81a3,0x819f
+ }
+ },
+ { /* ku 47 */
+ {
+ 0x819e,0x8195,0x81a2,0x8199,0x8197,0x8216,0x824f,0x8253,0x8252,0x8250,
+ 0x824e,0x8251,0x8524,0x853b,0x850f,0x8500,0x8529,0x850e,0x8509,0x850d,
+ 0x851f,0x850a,0x8527,0x851c,0x84fb,0x852b,0x84fa,0x8508,0x850c,0x84f4,
+ 0x852a,0x84f2,0x8515,0x84f7,0x84eb,0x84f3,0x84fc,0x8512,0x84ea,0x84e9,
+ 0x8516,0x84fe,0x8528,0x851d,0x852e,0x8502,0x84fd,0x851e,0x84f6,0x8531,
+ 0x8526,0x84e7,0x84e8,0x84f0,0x84ef,0x84f9,0x8518,0x8520,0x8530,0x850b,
+ 0x8519,0x852f,0x8662
+ },{
+ 0x8756,0x8763,0x8764,0x8777,0x87e1,0x8773,0x8758,0x8754,0x875b,0x8752,
+ 0x8761,0x875a,0x8751,0x875e,0x876d,0x876a,0x8750,0x874e,0x875f,0x875d,
+ 0x876f,0x876c,0x877a,0x876e,0x875c,0x8765,0x874f,0x877b,0x8775,0x8762,
+ 0x8767,0x8769,0x885a,0x8905,0x890c,0x8914,0x890b,0x8917,0x8918,0x8919,
+ 0x8906,0x8916,0x8911,0x890e,0x8909,0x89a2,0x89a4,0x89a3,0x89ed,0x89f0,
+ 0x89ec,0x8acf,0x8ac6,0x8ab8,0x8ad3,0x8ad1,0x8ad4,0x8ad5,0x8abb,0x8ad7,
+ 0x8abe,0x8ac0,0x8ac5,0x8ad8,0x8ac3,0x8aba,0x8abd,0x8ad9,0x8c3e,0x8c4d,
+ 0x8c8f,0x8ce5,0x8cdf,0x8cd9,0x8ce8,0x8cda,0x8cdd,0x8ce7,0x8da0,0x8d9c,
+ 0x8da1,0x8d9b,0x8e20,0x8e23,0x8e25,0x8e24,0x8e2e,0x8e15,0x8e1b,0x8e16,
+ 0x8e11,0x8e19,0x8e26,0x8e27
+ }
+ },
+ { /* ku 48 */
+ {
+ 0x8e14,0x8e12,0x8e18,0x8e13,0x8e1c,0x8e17,0x8e1a,0x8f2c,0x8f24,0x8f18,
+ 0x8f1a,0x8f20,0x8f23,0x8f16,0x8f17,0x9073,0x9070,0x906f,0x9067,0x906b,
+ 0x912f,0x912b,0x9129,0x912a,0x9132,0x9126,0x912e,0x9185,0x9186,0x918a,
+ 0x9181,0x9182,0x9184,0x9180,0x92d0,0x92c3,0x92c4,0x92c0,0x92d9,0x92b6,
+ 0x92cf,0x92f1,0x92df,0x92d8,0x92e9,0x92d7,0x92dd,0x92cc,0x92ef,0x92c2,
+ 0x92e8,0x92ca,0x92c8,0x92ce,0x92e6,0x92cd,0x92d5,0x92c9,0x92e0,0x92de,
+ 0x92e7,0x92d1,0x92d3
+ },{
+ 0x92b5,0x92e1,0x92c6,0x92b4,0x957c,0x95ac,0x95ab,0x95ae,0x95b0,0x96a4,
+ 0x96a2,0x96d3,0x9705,0x9708,0x9702,0x975a,0x978a,0x978e,0x9788,0x97d0,
+ 0x97cf,0x981e,0x981d,0x9826,0x9829,0x9828,0x9820,0x981b,0x9827,0x98b2,
+ 0x9908,0x98fa,0x9911,0x9914,0x9916,0x9917,0x9915,0x99dc,0x99cd,0x99cf,
+ 0x99d3,0x99d4,0x99ce,0x99c9,0x99d6,0x99d8,0x99cb,0x99d7,0x99cc,0x9ab3,
+ 0x9aec,0x9aeb,0x9af3,0x9af2,0x9af1,0x9b46,0x9b43,0x9b67,0x9b74,0x9b71,
+ 0x9b66,0x9b76,0x9b75,0x9b70,0x9b68,0x9b64,0x9b6c,0x9cfc,0x9cfa,0x9cfd,
+ 0x9cff,0x9cf7,0x9d07,0x9d00,0x9cf9,0x9cfb,0x9d08,0x9d05,0x9d04,0x9e83,
+ 0x9ed3,0x9f0f,0x9f10,0x511c,0x5113,0x5117,0x511a,0x5111,0x51de,0x5334,
+ 0x53e1,0x5670,0x5660,0x566e
+ }
+ },
+ { /* ku 49 */
+ {
+ 0x5673,0x5666,0x5663,0x566d,0x5672,0x565e,0x5677,0x571c,0x571b,0x58c8,
+ 0x58bd,0x58c9,0x58bf,0x58ba,0x58c2,0x58bc,0x58c6,0x5b17,0x5b19,0x5b1b,
+ 0x5b21,0x5b14,0x5b13,0x5b10,0x5b16,0x5b28,0x5b1a,0x5b20,0x5b1e,0x5bef,
+ 0x5dac,0x5db1,0x5da9,0x5da7,0x5db5,0x5db0,0x5dae,0x5daa,0x5da8,0x5db2,
+ 0x5dad,0x5daf,0x5db4,0x5e67,0x5e68,0x5e66,0x5e6f,0x5ee9,0x5ee7,0x5ee6,
+ 0x5ee8,0x5ee5,0x5f4b,0x5fbc,0x619d,0x61a8,0x6196,0x61c5,0x61b4,0x61c6,
+ 0x61c1,0x61cc,0x61ba
+ },{
+ 0x61bf,0x61b8,0x618c,0x64d7,0x64d6,0x64d0,0x64cf,0x64c9,0x64bd,0x6489,
+ 0x64c3,0x64db,0x64f3,0x64d9,0x6533,0x657f,0x657c,0x65a2,0x66c8,0x66be,
+ 0x66c0,0x66ca,0x66cb,0x66cf,0x66bd,0x66bb,0x66ba,0x66cc,0x6723,0x6a34,
+ 0x6a66,0x6a49,0x6a67,0x6a32,0x6a68,0x6a3e,0x6a5d,0x6a6d,0x6a76,0x6a5b,
+ 0x6a51,0x6a28,0x6a5a,0x6a3b,0x6a3f,0x6a41,0x6a6a,0x6a64,0x6a50,0x6a4f,
+ 0x6a54,0x6a6f,0x6a69,0x6a60,0x6a3c,0x6a5e,0x6a56,0x6a55,0x6a4d,0x6a4e,
+ 0x6a46,0x6b55,0x6b54,0x6b56,0x6ba7,0x6baa,0x6bab,0x6bc8,0x6bc7,0x6c04,
+ 0x6c03,0x6c06,0x6fad,0x6fcb,0x6fa3,0x6fc7,0x6fbc,0x6fce,0x6fc8,0x6f5e,
+ 0x6fc4,0x6fbd,0x6f9e,0x6fca,0x6fa8,0x7004,0x6fa5,0x6fae,0x6fba,0x6fac,
+ 0x6faa,0x6fcf,0x6fbf,0x6fb8
+ }
+ },
+ { /* ku 4a */
+ {
+ 0x6fa2,0x6fc9,0x6fab,0x6fcd,0x6faf,0x6fb2,0x6fb0,0x71c5,0x71c2,0x71bf,
+ 0x71b8,0x71d6,0x71c0,0x71c1,0x71cb,0x71d4,0x71ca,0x71c7,0x71cf,0x71bd,
+ 0x71d8,0x71bc,0x71c6,0x71da,0x71db,0x729d,0x729e,0x7369,0x7366,0x7367,
+ 0x736c,0x7365,0x736b,0x736a,0x747f,0x749a,0x74a0,0x7494,0x7492,0x7495,
+ 0x74a1,0x750b,0x7580,0x762f,0x762d,0x7631,0x763d,0x7633,0x763c,0x7635,
+ 0x7632,0x7630,0x76bb,0x76e6,0x779a,0x779d,0x77a1,0x779c,0x779b,0x77a2,
+ 0x77a3,0x7795,0x7799
+ },{
+ 0x7797,0x78dd,0x78e9,0x78e5,0x78ea,0x78de,0x78e3,0x78db,0x78e1,0x78e2,
+ 0x78ed,0x78df,0x78e0,0x79a4,0x7a44,0x7a48,0x7a47,0x7ab6,0x7ab8,0x7ab5,
+ 0x7ab1,0x7ab7,0x7bde,0x7be3,0x7be7,0x7bdd,0x7bd5,0x7be5,0x7bda,0x7be8,
+ 0x7bf9,0x7bd4,0x7bea,0x7be2,0x7bdc,0x7beb,0x7bd8,0x7bdf,0x7cd2,0x7cd4,
+ 0x7cd7,0x7cd0,0x7cd1,0x7e12,0x7e21,0x7e17,0x7e0c,0x7e1f,0x7e20,0x7e13,
+ 0x7e0e,0x7e1c,0x7e15,0x7e1a,0x7e22,0x7e0b,0x7e0f,0x7e16,0x7e0d,0x7e14,
+ 0x7e25,0x7e24,0x7f43,0x7f7b,0x7f7c,0x7f7a,0x7fb1,0x7fef,0x802a,0x8029,
+ 0x806c,0x81b1,0x81a6,0x81ae,0x81b9,0x81b5,0x81ab,0x81b0,0x81ac,0x81b4,
+ 0x81b2,0x81b7,0x81a7,0x81f2,0x8255,0x8256,0x8257,0x8556,0x8545,0x856b,
+ 0x854d,0x8553,0x8561,0x8558
+ }
+ },
+ { /* ku 4b */
+ {
+ 0x8540,0x8546,0x8564,0x8541,0x8562,0x8544,0x8551,0x8547,0x8563,0x853e,
+ 0x855b,0x8571,0x854e,0x856e,0x8575,0x8555,0x8567,0x8560,0x858c,0x8566,
+ 0x855d,0x8554,0x8565,0x856c,0x8663,0x8665,0x8664,0x879b,0x878f,0x8797,
+ 0x8793,0x8792,0x8788,0x8781,0x8796,0x8798,0x8779,0x8787,0x87a3,0x8785,
+ 0x8790,0x8791,0x879d,0x8784,0x8794,0x879c,0x879a,0x8789,0x891e,0x8926,
+ 0x8930,0x892d,0x892e,0x8927,0x8931,0x8922,0x8929,0x8923,0x892f,0x892c,
+ 0x891f,0x89f1,0x8ae0
+ },{
+ 0x8ae2,0x8af2,0x8af4,0x8af5,0x8add,0x8b14,0x8ae4,0x8adf,0x8af0,0x8ac8,
+ 0x8ade,0x8ae1,0x8ae8,0x8aff,0x8aef,0x8afb,0x8c91,0x8c92,0x8c90,0x8cf5,
+ 0x8cee,0x8cf1,0x8cf0,0x8cf3,0x8d6c,0x8d6e,0x8da5,0x8da7,0x8e33,0x8e3e,
+ 0x8e38,0x8e40,0x8e45,0x8e36,0x8e3c,0x8e3d,0x8e41,0x8e30,0x8e3f,0x8ebd,
+ 0x8f36,0x8f2e,0x8f35,0x8f32,0x8f39,0x8f37,0x8f34,0x9076,0x9079,0x907b,
+ 0x9086,0x90fa,0x9133,0x9135,0x9136,0x9193,0x9190,0x9191,0x918d,0x918f,
+ 0x9327,0x931e,0x9308,0x931f,0x9306,0x930f,0x937a,0x9338,0x933c,0x931b,
+ 0x9323,0x9312,0x9301,0x9346,0x932d,0x930e,0x930d,0x92cb,0x931d,0x92fa,
+ 0x9325,0x9313,0x92f9,0x92f7,0x9334,0x9302,0x9324,0x92ff,0x9329,0x9339,
+ 0x9335,0x932a,0x9314,0x930c
+ }
+ },
+ { /* ku 4c */
+ {
+ 0x930b,0x92fe,0x9309,0x9300,0x92fb,0x9316,0x95bc,0x95cd,0x95be,0x95b9,
+ 0x95ba,0x95b6,0x95bf,0x95b5,0x95bd,0x96a9,0x96d4,0x970b,0x9712,0x9710,
+ 0x9799,0x9797,0x9794,0x97f0,0x97f8,0x9835,0x982f,0x9832,0x9924,0x991f,
+ 0x9927,0x9929,0x999e,0x99ee,0x99ec,0x99e5,0x99e4,0x99f0,0x99e3,0x99ea,
+ 0x99e9,0x99e7,0x9ab9,0x9abf,0x9ab4,0x9abb,0x9af6,0x9afa,0x9af9,0x9af7,
+ 0x9b33,0x9b80,0x9b85,0x9b87,0x9b7c,0x9b7e,0x9b7b,0x9b82,0x9b93,0x9b92,
+ 0x9b90,0x9b7a,0x9b95
+ },{
+ 0x9b7d,0x9b88,0x9d25,0x9d17,0x9d20,0x9d1e,0x9d14,0x9d29,0x9d1d,0x9d18,
+ 0x9d22,0x9d10,0x9d19,0x9d1f,0x9e88,0x9e86,0x9e87,0x9eae,0x9ead,0x9ed5,
+ 0x9ed6,0x9efa,0x9f12,0x9f3d,0x5126,0x5125,0x5122,0x5124,0x5120,0x5129,
+ 0x52f4,0x5693,0x568c,0x568d,0x5686,0x5684,0x5683,0x567e,0x5682,0x567f,
+ 0x5681,0x58d6,0x58d4,0x58cf,0x58d2,0x5b2d,0x5b25,0x5b32,0x5b23,0x5b2c,
+ 0x5b27,0x5b26,0x5b2f,0x5b2e,0x5b7b,0x5bf1,0x5bf2,0x5db7,0x5e6c,0x5e6a,
+ 0x5fbe,0x5fbb,0x61c3,0x61b5,0x61bc,0x61e7,0x61e0,0x61e5,0x61e4,0x61e8,
+ 0x61de,0x64ef,0x64e9,0x64e3,0x64eb,0x64e4,0x64e8,0x6581,0x6580,0x65b6,
+ 0x65da,0x66d2,0x6a8d,0x6a96,0x6a81,0x6aa5,0x6a89,0x6a9f,0x6a9b,0x6aa1,
+ 0x6a9e,0x6a87,0x6a93,0x6a8e
+ }
+ },
+ { /* ku 4d */
+ {
+ 0x6a95,0x6a83,0x6aa8,0x6aa4,0x6a91,0x6a7f,0x6aa6,0x6a9a,0x6a85,0x6a8c,
+ 0x6a92,0x6b5b,0x6bad,0x6c09,0x6fcc,0x6fa9,0x6ff4,0x6fd4,0x6fe3,0x6fdc,
+ 0x6fed,0x6fe7,0x6fe6,0x6fde,0x6ff2,0x6fdd,0x6fe2,0x6fe8,0x71e1,0x71f1,
+ 0x71e8,0x71f2,0x71e4,0x71f0,0x71e2,0x7373,0x736e,0x736f,0x7497,0x74b2,
+ 0x74ab,0x7490,0x74aa,0x74ad,0x74b1,0x74a5,0x74af,0x7510,0x7511,0x7512,
+ 0x750f,0x7584,0x7643,0x7648,0x7649,0x7647,0x76a4,0x76e9,0x77b5,0x77ab,
+ 0x77b2,0x77b7,0x77b6
+ },{
+ 0x77b4,0x77b1,0x77a8,0x77f0,0x78f3,0x78fd,0x7902,0x78fb,0x78fc,0x78f2,
+ 0x7905,0x78f9,0x78fe,0x7904,0x79ab,0x79a8,0x7a5c,0x7a5b,0x7a56,0x7a58,
+ 0x7a54,0x7a5a,0x7abe,0x7ac0,0x7ac1,0x7c05,0x7c0f,0x7bf2,0x7c00,0x7bff,
+ 0x7bfb,0x7c0e,0x7bf4,0x7c0b,0x7bf3,0x7c02,0x7c09,0x7c03,0x7c01,0x7bf8,
+ 0x7bfd,0x7c06,0x7bf0,0x7bf1,0x7c10,0x7c0a,0x7ce8,0x7e2d,0x7e3c,0x7e42,
+ 0x7e33,0x9848,0x7e38,0x7e2a,0x7e49,0x7e40,0x7e47,0x7e29,0x7e4c,0x7e30,
+ 0x7e3b,0x7e36,0x7e44,0x7e3a,0x7f45,0x7f7f,0x7f7e,0x7f7d,0x7ff4,0x7ff2,
+ 0x802c,0x81bb,0x81c4,0x81cc,0x81ca,0x81c5,0x81c7,0x81bc,0x81e9,0x825b,
+ 0x825a,0x825c,0x8583,0x8580,0x858f,0x85a7,0x8595,0x85a0,0x858b,0x85a3,
+ 0x857b,0x85a4,0x859a,0x859e
+ }
+ },
+ { /* ku 4e */
+ {
+ 0x8577,0x857c,0x8589,0x85a1,0x857a,0x8578,0x8557,0x858e,0x8596,0x8586,
+ 0x858d,0x8599,0x859d,0x8581,0x85a2,0x8582,0x8588,0x8585,0x8579,0x8576,
+ 0x8598,0x8590,0x859f,0x8668,0x87be,0x87aa,0x87ad,0x87c5,0x87b0,0x87ac,
+ 0x87b9,0x87b5,0x87bc,0x87ae,0x87c9,0x87c3,0x87c2,0x87cc,0x87b7,0x87af,
+ 0x87c4,0x87ca,0x87b4,0x87b6,0x87bf,0x87b8,0x87bd,0x87de,0x87b2,0x8935,
+ 0x8933,0x893c,0x893e,0x8941,0x8952,0x8937,0x8942,0x89ad,0x89af,0x89ae,
+ 0x89f2,0x89f3,0x8b1e
+ },{
+ 0x8b18,0x8b16,0x8b11,0x8b05,0x8b0b,0x8b22,0x8b0f,0x8b12,0x8b15,0x8b07,
+ 0x8b0d,0x8b08,0x8b06,0x8b1c,0x8b13,0x8b1a,0x8c4f,0x8c70,0x8c72,0x8c71,
+ 0x8c6f,0x8c95,0x8c94,0x8cf9,0x8d6f,0x8e4e,0x8e4d,0x8e53,0x8e50,0x8e4c,
+ 0x8e47,0x8f43,0x8f40,0x9085,0x907e,0x9138,0x919a,0x91a2,0x919b,0x9199,
+ 0x919f,0x91a1,0x919d,0x91a0,0x93a1,0x9383,0x93af,0x9364,0x9356,0x9347,
+ 0x937c,0x9358,0x935c,0x9376,0x9349,0x9350,0x9351,0x9360,0x936d,0x938f,
+ 0x934c,0x936a,0x9379,0x9357,0x9355,0x9352,0x934f,0x9371,0x9377,0x937b,
+ 0x9361,0x935e,0x9363,0x9367,0x9380,0x934e,0x9359,0x95c7,0x95c0,0x95c9,
+ 0x95c3,0x95c5,0x95b7,0x96ae,0x96b0,0x96ac,0x9720,0x971f,0x9718,0x971d,
+ 0x9719,0x979a,0x97a1,0x979c
+ }
+ },
+ { /* ku 4f */
+ {
+ 0x979e,0x979d,0x97d5,0x97d4,0x97f1,0x9841,0x9844,0x984a,0x9849,0x9845,
+ 0x9843,0x9925,0x992b,0x992c,0x992a,0x9933,0x9932,0x992f,0x992d,0x9931,
+ 0x9930,0x9998,0x99a3,0x99a1,0x9a02,0x99fa,0x99f4,0x99f7,0x99f9,0x99f8,
+ 0x99f6,0x99fb,0x99fd,0x99fe,0x99fc,0x9a03,0x9abe,0x9afe,0x9afd,0x9b01,
+ 0x9afc,0x9b48,0x9b9a,0x9ba8,0x9b9e,0x9b9b,0x9ba6,0x9ba1,0x9ba5,0x9ba4,
+ 0x9b86,0x9ba2,0x9ba0,0x9baf,0x9d33,0x9d41,0x9d67,0x9d36,0x9d2e,0x9d2f,
+ 0x9d31,0x9d38,0x9d30
+ },{
+ 0x9d45,0x9d42,0x9d43,0x9d3e,0x9d37,0x9d40,0x9d3d,0x7ff5,0x9d2d,0x9e8a,
+ 0x9e89,0x9e8d,0x9eb0,0x9ec8,0x9eda,0x9efb,0x9eff,0x9f24,0x9f23,0x9f22,
+ 0x9f54,0x9fa0,0x5131,0x512d,0x512e,0x5698,0x569c,0x5697,0x569a,0x569d,
+ 0x5699,0x5970,0x5b3c,0x5c69,0x5c6a,0x5dc0,0x5e6d,0x5e6e,0x61d8,0x61df,
+ 0x61ed,0x61ee,0x61f1,0x61ea,0x61f0,0x61eb,0x61d6,0x61e9,0x64ff,0x6504,
+ 0x64fd,0x64f8,0x6501,0x6503,0x64fc,0x6594,0x65db,0x66da,0x66db,0x66d8,
+ 0x6ac5,0x6ab9,0x6abd,0x6ae1,0x6ac6,0x6aba,0x6ab6,0x6ab7,0x6ac7,0x6ab4,
+ 0x6aad,0x6b5e,0x6bc9,0x6c0b,0x7007,0x700c,0x700d,0x7001,0x7005,0x7014,
+ 0x700e,0x6fff,0x7000,0x6ffb,0x7026,0x6ffc,0x6ff7,0x700a,0x7201,0x71ff,
+ 0x71f9,0x7203,0x71fd,0x7376
+ }
+ },
+ { /* ku 50 */
+ {
+ 0x74b8,0x74c0,0x74b5,0x74c1,0x74be,0x74b6,0x74bb,0x74c2,0x7514,0x7513,
+ 0x765c,0x7664,0x7659,0x7650,0x7653,0x7657,0x765a,0x76a6,0x76bd,0x76ec,
+ 0x77c2,0x77ba,0x78ff,0x790c,0x7913,0x7914,0x7909,0x7910,0x7912,0x7911,
+ 0x79ad,0x79ac,0x7a5f,0x7c1c,0x7c29,0x7c19,0x7c20,0x7c1f,0x7c2d,0x7c1d,
+ 0x7c26,0x7c28,0x7c22,0x7c25,0x7c30,0x7e5c,0x7e50,0x7e56,0x7e63,0x7e58,
+ 0x7e62,0x7e5f,0x7e51,0x7e60,0x7e57,0x7e53,0x7fb5,0x7fb3,0x7ff7,0x7ff8,
+ 0x8075,0x81d1,0x81d2
+ },{
+ 0x81d0,0x825f,0x825e,0x85b4,0x85c6,0x85c0,0x85c3,0x85c2,0x85b3,0x85b5,
+ 0x85bd,0x85c7,0x85c4,0x85bf,0x85cb,0x85ce,0x85c8,0x85c5,0x85b1,0x85b6,
+ 0x85d2,0x8624,0x85b8,0x85b7,0x85be,0x8669,0x87e7,0x87e6,0x87e2,0x87db,
+ 0x87eb,0x87ea,0x87e5,0x87df,0x87f3,0x87e4,0x87d4,0x87dc,0x87d3,0x87ed,
+ 0x87d8,0x87e3,0x87a4,0x87d7,0x87d9,0x8801,0x87f4,0x87e8,0x87dd,0x8953,
+ 0x894b,0x894f,0x894c,0x8946,0x8950,0x8951,0x8949,0x8b2a,0x8b27,0x8b23,
+ 0x8b33,0x8b30,0x8b35,0x8b47,0x8b2f,0x8b3c,0x8b3e,0x8b31,0x8b25,0x8b37,
+ 0x8b26,0x8b36,0x8b2e,0x8b24,0x8b3b,0x8b3d,0x8b3a,0x8c42,0x8c75,0x8c99,
+ 0x8c98,0x8c97,0x8cfe,0x8d04,0x8d02,0x8d00,0x8e5c,0x8e62,0x8e60,0x8e57,
+ 0x8e56,0x8e5e,0x8e65,0x8e67
+ }
+ },
+ { /* ku 51 */
+ {
+ 0x8e5b,0x8e5a,0x8e61,0x8e5d,0x8e69,0x8e54,0x8f46,0x8f47,0x8f48,0x8f4b,
+ 0x9128,0x913a,0x913b,0x913e,0x91a8,0x91a5,0x91a7,0x91af,0x91aa,0x93b5,
+ 0x938c,0x9392,0x93b7,0x939b,0x939d,0x9389,0x93a7,0x938e,0x93aa,0x939e,
+ 0x93a6,0x9395,0x9388,0x9399,0x939f,0x938d,0x93b1,0x9391,0x93b2,0x93a4,
+ 0x93a8,0x93b4,0x93a3,0x93a5,0x95d2,0x95d3,0x95d1,0x96b3,0x96d7,0x96da,
+ 0x5dc2,0x96df,0x96d8,0x96dd,0x9723,0x9722,0x9725,0x97ac,0x97ae,0x97a8,
+ 0x97ab,0x97a4,0x97aa
+ },{
+ 0x97a2,0x97a5,0x97d7,0x97d9,0x97d6,0x97d8,0x97fa,0x9850,0x9851,0x9852,
+ 0x98b8,0x9941,0x993c,0x993a,0x9a0f,0x9a0b,0x9a09,0x9a0d,0x9a04,0x9a11,
+ 0x9a0a,0x9a05,0x9a07,0x9a06,0x9ac0,0x9adc,0x9b08,0x9b04,0x9b05,0x9b29,
+ 0x9b35,0x9b4a,0x9b4c,0x9b4b,0x9bc7,0x9bc6,0x9bc3,0x9bbf,0x9bc1,0x9bb5,
+ 0x9bb8,0x9bd3,0x9bb6,0x9bc4,0x9bb9,0x9bbd,0x9d5c,0x9d53,0x9d4f,0x9d4a,
+ 0x9d5b,0x9d4b,0x9d59,0x9d56,0x9d4c,0x9d57,0x9d52,0x9d54,0x9d5f,0x9d58,
+ 0x9d5a,0x9e8e,0x9e8c,0x9edf,0x9f01,0x9f00,0x9f16,0x9f25,0x9f2b,0x9f2a,
+ 0x9f29,0x9f28,0x9f4c,0x9f55,0x5134,0x5135,0x5296,0x52f7,0x53b4,0x56ab,
+ 0x56ad,0x56a6,0x56a7,0x56aa,0x56ac,0x58da,0x58dd,0x58db,0x5912,0x5b3d,
+ 0x5b3e,0x5b3f,0x5dc3,0x5e70
+ }
+ },
+ { /* ku 52 */
+ {
+ 0x5fbf,0x61fb,0x6507,0x6510,0x650d,0x6509,0x650c,0x650e,0x6584,0x65de,
+ 0x65dd,0x66de,0x6ae7,0x6ae0,0x6acc,0x6ad1,0x6ad9,0x6acb,0x6adf,0x6adc,
+ 0x6ad0,0x6aeb,0x6acf,0x6acd,0x6ade,0x6b60,0x6bb0,0x6c0c,0x7019,0x7027,
+ 0x7020,0x7016,0x702b,0x7021,0x7022,0x7023,0x7029,0x7017,0x7024,0x701c,
+ 0x702a,0x720c,0x720a,0x7207,0x7202,0x7205,0x72a5,0x72a6,0x72a4,0x72a3,
+ 0x72a1,0x74cb,0x74c5,0x74b7,0x74c3,0x7516,0x7660,0x77c9,0x77ca,0x77c4,
+ 0x77f1,0x791d,0x791b
+ },{
+ 0x7921,0x791c,0x7917,0x791e,0x79b0,0x7a67,0x7a68,0x7c33,0x7c3c,0x7c39,
+ 0x7c2c,0x7c3b,0x7cec,0x7cea,0x7e76,0x7e75,0x7e78,0x7e70,0x7e77,0x7e6f,
+ 0x7e7a,0x7e72,0x7e74,0x7e68,0x7f4b,0x7f4a,0x7f83,0x7f86,0x7fb7,0x7ffd,
+ 0x7ffe,0x8078,0x81d7,0x81d5,0x8264,0x8261,0x8263,0x85eb,0x85f1,0x85ed,
+ 0x85d9,0x85e1,0x85e8,0x85da,0x85d7,0x85ec,0x85f2,0x85f8,0x85d8,0x85df,
+ 0x85e3,0x85dc,0x85d1,0x85f0,0x85e6,0x85ef,0x85de,0x85e2,0x8800,0x87fa,
+ 0x8803,0x87f6,0x87f7,0x8809,0x880c,0x880b,0x8806,0x87fc,0x8808,0x87ff,
+ 0x880a,0x8802,0x8962,0x895a,0x895b,0x8957,0x8961,0x895c,0x8958,0x895d,
+ 0x8959,0x8988,0x89b7,0x89b6,0x89f6,0x8b50,0x8b48,0x8b4a,0x8b40,0x8b53,
+ 0x8b56,0x8b54,0x8b4b,0x8b55
+ }
+ },
+ { /* ku 53 */
+ {
+ 0x8b51,0x8b42,0x8b52,0x8b57,0x8c43,0x8c77,0x8c76,0x8c9a,0x8d06,0x8d07,
+ 0x8d09,0x8dac,0x8daa,0x8dad,0x8dab,0x8e6d,0x8e78,0x8e73,0x8e6a,0x8e6f,
+ 0x8e7b,0x8ec2,0x8f52,0x8f51,0x8f4f,0x8f50,0x8f53,0x8fb4,0x9140,0x913f,
+ 0x91b0,0x91ad,0x93de,0x93c7,0x93cf,0x93c2,0x93da,0x93d0,0x93f9,0x93ec,
+ 0x93cc,0x93d9,0x93a9,0x93e6,0x93ca,0x93d4,0x93ee,0x93e3,0x93d5,0x93c4,
+ 0x93ce,0x93c0,0x93d2,0x93e7,0x957d,0x95da,0x95db,0x96e1,0x9729,0x972b,
+ 0x972c,0x9728,0x9726
+ },{
+ 0x97b3,0x97b7,0x97b6,0x97dd,0x97de,0x97df,0x985c,0x9859,0x985d,0x9857,
+ 0x98bf,0x98bd,0x98bb,0x98be,0x9948,0x9947,0x9943,0x99a6,0x99a7,0x9a1a,
+ 0x9a15,0x9a25,0x9a1d,0x9a24,0x9a1b,0x9a22,0x9a20,0x9a27,0x9a23,0x9a1e,
+ 0x9a1c,0x9a14,0x9ac2,0x9b0b,0x9b0a,0x9b0e,0x9b0c,0x9b37,0x9bea,0x9beb,
+ 0x9be0,0x9bde,0x9be4,0x9be6,0x9be2,0x9bf0,0x9bd4,0x9bd7,0x9bec,0x9bdc,
+ 0x9bd9,0x9be5,0x9bd5,0x9be1,0x9bda,0x9d77,0x9d81,0x9d8a,0x9d84,0x9d88,
+ 0x9d71,0x9d80,0x9d78,0x9d86,0x9d8b,0x9d8c,0x9d7d,0x9d6b,0x9d74,0x9d75,
+ 0x9d70,0x9d69,0x9d85,0x9d73,0x9d7b,0x9d82,0x9d6f,0x9d79,0x9d7f,0x9d87,
+ 0x9d68,0x9e94,0x9e91,0x9ec0,0x9efc,0x9f2d,0x9f40,0x9f41,0x9f4d,0x9f56,
+ 0x9f57,0x9f58,0x5337,0x56b2
+ }
+ },
+ { /* ku 54 */
+ {
+ 0x56b5,0x56b3,0x58e3,0x5b45,0x5dc6,0x5dc7,0x5eee,0x5eef,0x5fc0,0x5fc1,
+ 0x61f9,0x6517,0x6516,0x6515,0x6513,0x65df,0x66e8,0x66e3,0x66e4,0x6af3,
+ 0x6af0,0x6aea,0x6ae8,0x6af9,0x6af1,0x6aee,0x6aef,0x703c,0x7035,0x702f,
+ 0x7037,0x7034,0x7031,0x7042,0x7038,0x703f,0x703a,0x7039,0x7040,0x703b,
+ 0x7033,0x7041,0x7213,0x7214,0x72a8,0x737d,0x737c,0x74ba,0x76ab,0x76aa,
+ 0x76be,0x76ed,0x77cc,0x77ce,0x77cf,0x77cd,0x77f2,0x7925,0x7923,0x7927,
+ 0x7928,0x7924,0x7929
+ },{
+ 0x79b2,0x7a6e,0x7a6c,0x7a6d,0x7af7,0x7c49,0x7c48,0x7c4a,0x7c47,0x7c45,
+ 0x7cee,0x7e7b,0x7e7e,0x7e81,0x7e80,0x7fba,0x7fff,0x8079,0x81db,0x81d9,
+ 0x820b,0x8268,0x8269,0x8622,0x85ff,0x8601,0x85fe,0x861b,0x8600,0x85f6,
+ 0x8604,0x8609,0x8605,0x860c,0x85fd,0x8819,0x8810,0x8811,0x8817,0x8813,
+ 0x8816,0x8963,0x8966,0x89b9,0x89f7,0x8b60,0x8b6a,0x8b5d,0x8b68,0x8b63,
+ 0x8b65,0x8b67,0x8b6d,0x8dae,0x8e86,0x8e88,0x8e84,0x8f59,0x8f56,0x8f57,
+ 0x8f55,0x8f58,0x8f5a,0x908d,0x9143,0x9141,0x91b7,0x91b5,0x91b2,0x91b3,
+ 0x940b,0x9413,0x93fb,0x9420,0x940f,0x9414,0x93fe,0x9415,0x9410,0x9428,
+ 0x9419,0x940d,0x93f5,0x9400,0x93f7,0x9407,0x940e,0x9416,0x9412,0x93fa,
+ 0x9409,0x93f8,0x940a,0x93ff
+ }
+ },
+ { /* ku 55 */
+ {
+ 0x93fc,0x940c,0x93f6,0x9411,0x9406,0x95de,0x95e0,0x95df,0x972e,0x972f,
+ 0x97b9,0x97bb,0x97fd,0x97fe,0x9860,0x9862,0x9863,0x985f,0x98c1,0x98c2,
+ 0x9950,0x994e,0x9959,0x994c,0x994b,0x9953,0x9a32,0x9a34,0x9a31,0x9a2c,
+ 0x9a2a,0x9a36,0x9a29,0x9a2e,0x9a38,0x9a2d,0x9ac7,0x9aca,0x9ac6,0x9b10,
+ 0x9b12,0x9b11,0x9c0b,0x9c08,0x9bf7,0x9c05,0x9c12,0x9bf8,0x9c40,0x9c07,
+ 0x9c0e,0x9c06,0x9c17,0x9c14,0x9c09,0x9d9f,0x9d99,0x9da4,0x9d9d,0x9d92,
+ 0x9d98,0x9d90,0x9d9b
+ },{
+ 0x9da0,0x9d94,0x9d9c,0x9daa,0x9d97,0x9da1,0x9d9a,0x9da2,0x9da8,0x9d9e,
+ 0x9da3,0x9dbf,0x9da9,0x9d96,0x9da6,0x9da7,0x9e99,0x9e9b,0x9e9a,0x9ee5,
+ 0x9ee4,0x9ee7,0x9ee6,0x9f30,0x9f2e,0x9f5b,0x9f60,0x9f5e,0x9f5d,0x9f59,
+ 0x9f91,0x513a,0x5139,0x5298,0x5297,0x56c3,0x56bd,0x56be,0x5b48,0x5b47,
+ 0x5dcb,0x5dcf,0x5ef1,0x61fd,0x651b,0x6b02,0x6afc,0x6b03,0x6af8,0x6b00,
+ 0x7043,0x7044,0x704a,0x7048,0x7049,0x7045,0x7046,0x721d,0x721a,0x7219,
+ 0x737e,0x7517,0x766a,0x77d0,0x792d,0x7931,0x792f,0x7c54,0x7c53,0x7cf2,
+ 0x7e8a,0x7e87,0x7e88,0x7e8b,0x7e86,0x7e8d,0x7f4d,0x7fbb,0x8030,0x81dd,
+ 0x8618,0x862a,0x8626,0x861f,0x8623,0x861c,0x8619,0x8627,0x862e,0x8621,
+ 0x8620,0x8629,0x861e,0x8625
+ }
+ },
+ { /* ku 56 */
+ {
+ 0x8829,0x881d,0x881b,0x8820,0x8824,0x881c,0x882b,0x884a,0x896d,0x8969,
+ 0x896e,0x896b,0x89fa,0x8b79,0x8b78,0x8b45,0x8b7a,0x8b7b,0x8d10,0x8d14,
+ 0x8daf,0x8e8e,0x8e8c,0x8f5e,0x8f5b,0x8f5d,0x9146,0x9144,0x9145,0x91b9,
+ 0x943f,0x943b,0x9436,0x9429,0x943d,0x943c,0x9430,0x9439,0x942a,0x9437,
+ 0x942c,0x9440,0x9431,0x95e5,0x95e4,0x95e3,0x9735,0x973a,0x97bf,0x97e1,
+ 0x9864,0x98c9,0x98c6,0x98c0,0x9958,0x9956,0x9a39,0x9a3d,0x9a46,0x9a44,
+ 0x9a42,0x9a41,0x9a3a
+ },{
+ 0x9a3f,0x9acd,0x9b15,0x9b17,0x9b18,0x9b16,0x9b3a,0x9b52,0x9c2b,0x9c1d,
+ 0x9c1c,0x9c2c,0x9c23,0x9c28,0x9c29,0x9c24,0x9c21,0x9db7,0x9db6,0x9dbc,
+ 0x9dc1,0x9dc7,0x9dca,0x9dcf,0x9dbe,0x9dc5,0x9dc3,0x9dbb,0x9db5,0x9dce,
+ 0x9db9,0x9dba,0x9dac,0x9dc8,0x9db1,0x9dad,0x9dcc,0x9db3,0x9dcd,0x9db2,
+ 0x9e7a,0x9e9c,0x9eeb,0x9eee,0x9eed,0x9f1b,0x9f18,0x9f1a,0x9f31,0x9f4e,
+ 0x9f65,0x9f64,0x9f92,0x4eb9,0x56c6,0x56c5,0x56cb,0x5971,0x5b4b,0x5b4c,
+ 0x5dd5,0x5dd1,0x5ef2,0x6521,0x6520,0x6526,0x6522,0x6b0b,0x6b08,0x6b09,
+ 0x6c0d,0x7055,0x7056,0x7057,0x7052,0x721e,0x721f,0x72a9,0x737f,0x74d8,
+ 0x74d5,0x74d9,0x74d7,0x766d,0x76ad,0x7935,0x79b4,0x7a70,0x7a71,0x7c57,
+ 0x7c5c,0x7c59,0x7c5b,0x7c5a
+ }
+ },
+ { /* ku 57 */
+ {
+ 0x7cf4,0x7cf1,0x7e91,0x7f4f,0x7f87,0x81de,0x826b,0x8634,0x8635,0x8633,
+ 0x862c,0x8632,0x8636,0x882c,0x8828,0x8826,0x882a,0x8825,0x8971,0x89bf,
+ 0x89be,0x89fb,0x8b7e,0x8b84,0x8b82,0x8b86,0x8b85,0x8b7f,0x8d15,0x8e95,
+ 0x8e94,0x8e9a,0x8e92,0x8e90,0x8e96,0x8e97,0x8f60,0x8f62,0x9147,0x944c,
+ 0x9450,0x944a,0x944b,0x944f,0x9447,0x9445,0x9448,0x9449,0x9446,0x973f,
+ 0x97e3,0x986a,0x9869,0x98cb,0x9954,0x995b,0x9a4e,0x9a53,0x9a54,0x9a4c,
+ 0x9a4f,0x9a48,0x9a4a
+ },{
+ 0x9a49,0x9a52,0x9a50,0x9ad0,0x9b19,0x9b2b,0x9b3b,0x9b56,0x9b55,0x9c46,
+ 0x9c48,0x9c3f,0x9c44,0x9c39,0x9c33,0x9c41,0x9c3c,0x9c37,0x9c34,0x9c32,
+ 0x9c3d,0x9c36,0x9ddb,0x9dd2,0x9dde,0x9dda,0x9dcb,0x9dd0,0x9ddc,0x9dd1,
+ 0x9ddf,0x9de9,0x9dd9,0x9dd8,0x9dd6,0x9df5,0x9dd5,0x9ddd,0x9eb6,0x9ef0,
+ 0x9f35,0x9f33,0x9f32,0x9f42,0x9f6b,0x9f95,0x9fa2,0x513d,0x5299,0x58e8,
+ 0x58e7,0x5972,0x5b4d,0x5dd8,0x882f,0x5f4f,0x6201,0x6203,0x6204,0x6529,
+ 0x6525,0x6596,0x66eb,0x6b11,0x6b12,0x6b0f,0x6bca,0x705b,0x705a,0x7222,
+ 0x7382,0x7381,0x7383,0x7670,0x77d4,0x7c67,0x7c66,0x7e95,0x826c,0x863a,
+ 0x8640,0x8639,0x863c,0x8631,0x863b,0x863e,0x8830,0x8832,0x882e,0x8833,
+ 0x8976,0x8974,0x8973,0x89fe
+ }
+ },
+ { /* ku 58 */
+ {
+ 0x8b8c,0x8b8e,0x8b8b,0x8b88,0x8c45,0x8d19,0x8e98,0x8f64,0x8f63,0x91bc,
+ 0x9462,0x9455,0x945d,0x9457,0x945e,0x97c4,0x97c5,0x9800,0x9a56,0x9a59,
+ 0x9b1e,0x9b1f,0x9b20,0x9c52,0x9c58,0x9c50,0x9c4a,0x9c4d,0x9c4b,0x9c55,
+ 0x9c59,0x9c4c,0x9c4e,0x9dfb,0x9df7,0x9def,0x9de3,0x9deb,0x9df8,0x9de4,
+ 0x9df6,0x9de1,0x9dee,0x9de6,0x9df2,0x9df0,0x9de2,0x9dec,0x9df4,0x9df3,
+ 0x9de8,0x9ded,0x9ec2,0x9ed0,0x9ef2,0x9ef3,0x9f06,0x9f1c,0x9f38,0x9f37,
+ 0x9f36,0x9f43,0x9f4f
+ },{
+ 0x9f71,0x9f70,0x9f6e,0x9f6f,0x56d3,0x56cd,0x5b4e,0x5c6d,0x652d,0x66ed,
+ 0x66ee,0x6b13,0x705f,0x7061,0x705d,0x7060,0x7223,0x74db,0x74e5,0x77d5,
+ 0x7938,0x79b7,0x79b6,0x7c6a,0x7e97,0x7f89,0x826d,0x8643,0x8838,0x8837,
+ 0x8835,0x884b,0x8b94,0x8b95,0x8e9e,0x8e9f,0x8ea0,0x8e9d,0x91be,0x91bd,
+ 0x91c2,0x946b,0x9468,0x9469,0x96e5,0x9746,0x9743,0x9747,0x97c7,0x97e5,
+ 0x9a5e,0x9ad5,0x9b59,0x9c63,0x9c67,0x9c66,0x9c62,0x9c5e,0x9c60,0x9e02,
+ 0x9dfe,0x9e07,0x9e03,0x9e06,0x9e05,0x9e00,0x9e01,0x9e09,0x9dff,0x9dfd,
+ 0x9e04,0x9ea0,0x9f1e,0x9f46,0x9f74,0x9f75,0x9f76,0x56d4,0x652e,0x65b8,
+ 0x6b18,0x6b19,0x6b17,0x6b1a,0x7062,0x7226,0x72aa,0x77d8,0x77d9,0x7939,
+ 0x7c69,0x7c6b,0x7cf6,0x7e9a
+ }
+ },
+ { /* ku 59 */
+ {
+ 0x7e98,0x7e9b,0x7e99,0x81e0,0x81e1,0x8646,0x8647,0x8648,0x8979,0x897a,
+ 0x897c,0x897b,0x89ff,0x8b98,0x8b99,0x8ea5,0x8ea4,0x8ea3,0x946e,0x946d,
+ 0x946f,0x9471,0x9473,0x9749,0x9872,0x995f,0x9c68,0x9c6e,0x9c6d,0x9e0b,
+ 0x9e0d,0x9e10,0x9e0f,0x9e12,0x9e11,0x9ea1,0x9ef5,0x9f09,0x9f47,0x9f78,
+ 0x9f7b,0x9f7a,0x9f79,0x571e,0x7066,0x7c6f,0x883c,0x8db2,0x8ea6,0x91c3,
+ 0x9474,0x9478,0x9476,0x9475,0x9a60,0x9c74,0x9c73,0x9c71,0x9c75,0x9e14,
+ 0x9e13,0x9ef6,0x9f0a
+ },{
+ 0x9fa4,0x7068,0x7065,0x7cf7,0x866a,0x883e,0x883d,0x883f,0x8b9e,0x8c9c,
+ 0x8ea9,0x8ec9,0x974b,0x9873,0x9874,0x98cc,0x9961,0x99ab,0x9a64,0x9a66,
+ 0x9a67,0x9b24,0x9e15,0x9e17,0x9f48,0x6207,0x6b1e,0x7227,0x864c,0x8ea8,
+ 0x9482,0x9480,0x9481,0x9a69,0x9a68,0x9b2e,0x9e19,0x7229,0x864b,0x8b9f,
+ 0x9483,0x9c79,0x9eb7,0x7675,0x9a6b,0x9c7a,0x9e1d,0x7069,0x706a,0x9ea4,
+ 0x9f7e,0x9f49,0x9f98,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ }
+ }
+};
diff --git a/imap/src/charset/cns11643.c b/imap/src/charset/cns11643.c
new file mode 100644
index 00000000..1c209d50
--- /dev/null
+++ b/imap/src/charset/cns11643.c
@@ -0,0 +1,8590 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: CSN 11643 conversion table
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 July 1997
+ * Last Edited: 30 August 2006
+ */
+
+/* CNS 11643 is the national standard of the Republic of China (Taiwan).
+ * Thanks to Emily Hsu of the ROC's Institute for Information Industry for
+ * this data.
+ *
+ * Note: It is assumed that CJK Unified Ideographs Extension A are encoded
+ * in the BMP at U+3400 to U+4DB5.
+ */
+
+#define CNS_EXTENDED 1 /* include extended planes 3-15 */
+#define CNS_OBSOLETE 0 /* include obsolete plane 14 */
+#define CNS_EXTENSION 0 /* include extension plane 15 */
+
+#define BASE_CNS11643_KU 0x21
+#define BASE_CNS11643_TEN 0x21
+#define MAX_CNS11643_KU_1 93
+#define MAX_CNS11643_KU_2 82
+#if CNS_EXTENDED
+#define MAX_CNS11643_KU_3 71
+#define MAX_CNS11643_KU_4 78
+#define MAX_CNS11643_KU_5 92
+#define MAX_CNS11643_KU_6 68
+#define MAX_CNS11643_KU_7 69
+#if CNS_OBSOLETE
+#define MAX_CNS11643_KU_14 71
+#endif
+#if CNS_EXTENSION
+#define MAX_CNS11643_KU_15 77
+#endif
+#endif
+#define MAX_CNS11643_TEN 94
+
+
+#define CNS1TOUNICODE(c,c1,ku,ten) \
+ ((((ku = (c & 0x7f) - BASE_CNS11643_KU) < MAX_CNS11643_KU_1) && \
+ ((ten = (c1 & 0x7f) - BASE_CNS11643_TEN) < MAX_CNS11643_TEN)) ? \
+ cns11643_1tab[ku][ten] : UBOGON)
+
+
+#define CNS2TOUNICODE(c,c1,ku,ten) \
+ ((((ku = (c & 0x7f) - BASE_CNS11643_KU) < MAX_CNS11643_KU_2) && \
+ ((ten = (c1 & 0x7f) - BASE_CNS11643_TEN) < MAX_CNS11643_TEN)) ? \
+ cns11643_2tab[ku][ten] : UBOGON)
+
+
+#if CNS_EXTENDED
+#define CNS3TOUNICODE(c,c1,ku,ten) \
+ ((((ku = (c & 0x7f) - BASE_CNS11643_KU) < MAX_CNS11643_KU_3) && \
+ ((ten = (c1 & 0x7f) - BASE_CNS11643_TEN) < MAX_CNS11643_TEN)) ? \
+ cns11643_3tab[ku][ten] : UBOGON)
+
+
+#define CNS4TOUNICODE(c,c1,ku,ten) \
+ ((((ku = (c & 0x7f) - BASE_CNS11643_KU) < MAX_CNS11643_KU_4) && \
+ ((ten = (c1 & 0x7f) - BASE_CNS11643_TEN) < MAX_CNS11643_TEN)) ? \
+ cns11643_4tab[ku][ten] : UBOGON)
+
+
+#define CNS5TOUNICODE(c,c1,ku,ten) \
+ ((((ku = (c & 0x7f) - BASE_CNS11643_KU) < MAX_CNS11643_KU_5) && \
+ ((ten = (c1 & 0x7f) - BASE_CNS11643_TEN) < MAX_CNS11643_TEN)) ? \
+ cns11643_5tab[ku][ten] : UBOGON)
+
+
+#define CNS6TOUNICODE(c,c1,ku,ten) \
+ ((((ku = (c & 0x7f) - BASE_CNS11643_KU) < MAX_CNS11643_KU_6) && \
+ ((ten = (c1 & 0x7f) - BASE_CNS11643_TEN) < MAX_CNS11643_TEN)) ? \
+ cns11643_6tab[ku][ten] : UBOGON)
+
+
+#define CNS7TOUNICODE(c,c1,ku,ten) \
+ ((((ku = (c & 0x7f) - BASE_CNS11643_KU) < MAX_CNS11643_KU_7) && \
+ ((ten = (c1 & 0x7f) - BASE_CNS11643_TEN) < MAX_CNS11643_TEN)) ? \
+ cns11643_7tab[ku][ten] : UBOGON)
+
+
+#if CNS_OBSOLETE
+#define CNS14TOUNICODE(c,c1,ku,ten) \
+ ((((ku = (c & 0x7f) - BASE_CNS11643_KU) < MAX_CNS11643_KU_14) && \
+ ((ten = (c1 & 0x7f) - BASE_CNS11643_TEN) < MAX_CNS11643_TEN)) ? \
+ cns11643_14tab[ku][ten] : UBOGON)
+#endif
+
+
+#if CNS_EXTENSION
+#define CNS15TOUNICODE(c,c1,ku,ten) \
+ ((((ku = (c & 0x7f) - BASE_CNS11643_KU) < MAX_CNS11643_KU_15) && \
+ ((ten = (c1 & 0x7f) - BASE_CNS11643_TEN) < MAX_CNS11643_TEN)) ? \
+ cns11643_15tab[ku][ten] : UBOGON)
+#endif
+#endif
+
+/* CNS 11643 plane 1 conversion table */
+
+static const unsigned short
+ cns11643_1tab[MAX_CNS11643_KU_1][MAX_CNS11643_TEN] = {
+ { /* ku 01 */
+ 0x3000,0xff0c,0x3001,0x3002,0xff0e,0x30fb,0xff1b,0xff1a,0xff1f,0xff01,
+ 0xfe30,0x2026,0x2025,0xfe50,0xfe51,0xfe52,0x00b7,0xfe54,0xfe55,0xfe56,
+ 0xfe57,0xfe31,0x2014,0xfe32,0x2013,UBOGON,UBOGON,UBOGON,UBOGON,0xff08,
+ 0xff09,0xfe35,0xfe36,0xff5b,0xff5d,0xfe37,0xfe38,0x3014,0x3015,0xfe39,
+ 0xfe3a,0x3010,0x3011,0xfe3b,0xfe3c,0x300a,0x300b,0xfe3d,0xfe3e,0x3008,
+ 0x3009,0xfe3f,0xfe40,0x300c,0x300d,0xfe41,0xfe42,0x300e,0x300f,0xfe43,
+ 0xfe44,0xfe59,0xfe5a,0xfe5b,0xfe5c,0xfe5d,0xfe5e,0x2018,0x2019,0x201c,
+ 0x201d,0x301d,0x301e,0x2032,0x2035,0xff03,0xff06,0xff0a,0x203b,0x00a7,
+ 0x3003,0x25cb,0x25cf,0x25b3,0x25b2,0x25ce,0x2606,0x2605,0x25c7,0x25c6,
+ 0x25a1,0x25a0,0x25bd,0x25bc
+ },
+ { /* ku 02 */
+ 0x32a3,0x2105,0x203e,UBOGON,0xff3f,UBOGON,0xfe49,0xfe4a,0xfe4d,0xfe4e,
+ 0xfe4b,0xfe4c,0xfe5f,0xfe60,0xfe61,0xff0b,0xff0d,0x00d7,0x00f7,0x00b1,
+ 0x221a,0xff1c,0xff1e,0xff1d,0x2266,0x2267,0x2260,0x221e,0x2252,0x2261,
+ 0xfe62,0xfe63,0xfe64,0xfe66,0xfe65,0x223c,0x2229,0x222a,0x22a5,0x2220,
+ 0x221f,0x22bf,0x33d2,0x33d1,0x222b,0x222e,0x2235,0x2234,0x2640,0x2642,
+ 0x2641,0x2609,0x2191,0x2193,0x2192,0x2190,0x2196,0x2197,0x2199,0x2198,
+ 0x2016,0xff5c,0xff0f,0xff3c,0x2215,0xfe68,0xff04,0xffe5,0x3012,0xffe0,
+ 0xffe1,0xff05,0xff20,0x2103,0x2109,0xfe69,0xfe6a,0xfe6b,0x33d5,0x339c,
+ 0x339d,0x339e,0x33ce,0x33a1,0x338e,0x338f,0x33c4,0x00b0,0x5159,0x515b,
+ 0x515e,0x515d,0x5161,0x5163
+ },
+ { /* ku 03 */
+ 0x55e7,0x74e9,0x7cce,0x2581,0x2582,0x2583,0x2584,0x2585,0x2586,0x2587,
+ 0x2588,0x258f,0x258e,0x258d,0x258c,0x258b,0x258a,0x2589,0x253c,0x2534,
+ 0x252c,0x2524,0x251c,0x2594,0x2500,0x2502,0x2595,0x250c,0x2510,0x2514,
+ 0x2518,0x256d,0x256e,0x2570,0x256f,0x2550,0x255e,0x256a,0x2561,0x25e2,
+ 0x25e3,0x25e5,0x25e4,0x2571,0x2572,0x2573,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 04 */
+ 0xff10,0xff11,0xff12,0xff13,0xff14,0xff15,0xff16,0xff17,0xff18,0xff19,
+ 0x2160,0x2161,0x2162,0x2163,0x2164,0x2165,0x2166,0x2167,0x2168,0x2169,
+ 0x3021,0x3022,0x3023,0x3024,0x3025,0x3026,0x3027,0x3028,0x3029,UBOGON,
+ 0x5344,UBOGON,0xff21,0xff22,0xff23,0xff24,0xff25,0xff26,0xff27,0xff28,
+ 0xff29,0xff2a,0xff2b,0xff2c,0xff2d,0xff2e,0xff2f,0xff30,0xff31,0xff32,
+ 0xff33,0xff34,0xff35,0xff36,0xff37,0xff38,0xff39,0xff3a,0xff41,0xff42,
+ 0xff43,0xff44,0xff45,0xff46,0xff47,0xff48,0xff49,0xff4a,0xff4b,0xff4c,
+ 0xff4d,0xff4e,0xff4f,0xff50,0xff51,0xff52,0xff53,0xff54,0xff55,0xff56,
+ 0xff57,0xff58,0xff59,0xff5a,0x0391,0x0392,0x0393,0x0394,0x0395,0x0396,
+ 0x0397,0x0398,0x0399,0x039a
+ },
+ { /* ku 05 */
+ 0x039b,0x039c,0x039d,0x039e,0x039f,0x03a0,0x03a1,0x03a3,0x03a4,0x03a5,
+ 0x03a6,0x03a7,0x03a8,0x03a9,0x03b1,0x03b2,0x03b3,0x03b4,0x03b5,0x03b6,
+ 0x03b7,0x03b8,0x03b9,0x03ba,0x03bb,0x03bc,0x03bd,0x03be,0x03bf,0x03c0,
+ 0x03c1,0x03c3,0x03c4,0x03c5,0x03c6,0x03c7,0x03c8,0x03c9,0x3105,0x3106,
+ 0x3107,0x3108,0x3109,0x310a,0x310b,0x310c,0x310d,0x310e,0x310f,0x3110,
+ 0x3111,0x3112,0x3113,0x3114,0x3115,0x3116,0x3117,0x3118,0x3119,0x311a,
+ 0x311b,0x311c,0x311d,0x311e,0x311f,0x3120,0x3121,0x3122,0x3123,0x3124,
+ 0x3125,0x3126,0x3127,0x3128,0x3129,0x02d9,0x02c9,0x02ca,0x02c7,0x02cb,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 06 */
+ 0x2460,0x2461,0x2462,0x2463,0x2464,0x2465,0x2466,0x2467,0x2468,0x2469,
+ 0x2474,0x2475,0x2476,0x2477,0x2478,0x2479,0x247a,0x247b,0x247c,0x247d,
+ 0x2170,0x2171,0x2172,0x2173,0x2174,0x2175,0x2176,0x2177,0x2178,0x2179,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 07 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4ea0,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x51ab,UBOGON,UBOGON,UBOGON,UBOGON,0x52f9,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 08 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 09 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 10 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 11 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 12 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 13 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 14 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 15 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 16 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 17 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 18 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 19 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 20 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 21 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 22 */
+ 0x2400,0x2401,0x2402,0x2403,0x2404,0x2405,0x2406,0x2407,0x2408,0x2409,
+ 0x240a,0x240b,0x240c,0x240d,0x240e,0x240f,0x2410,0x2411,0x2412,0x2413,
+ 0x2414,0x2415,0x2416,0x2417,0x2418,0x2419,0x241a,0x241b,0x241c,0x241d,
+ 0x241e,0x241f,0x2421,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 23 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 24 */
+ 0x4e00,0x4e59,0x4e01,0x4e03,0x4e43,0x4e5d,0x4e86,0x4e8c,0x4eba,0x513f,
+ 0x5165,0x516b,0x51e0,0x5200,0x5201,0x529b,0x5315,0x5341,0x535c,0x53c8,
+ 0x4e09,0x4e0b,0x4e08,0x4e0a,0x4e2b,0x4e38,0x51e1,0x4e45,0x4e48,0x4e5f,
+ 0x4e5e,0x4e8e,0x4ea1,0x5140,0x5203,0x52fa,0x5343,0x53c9,0x53e3,0x571f,
+ 0x58eb,0x5915,0x5927,0x5973,0x5b50,0x5b51,0x5b53,0x5bf8,0x5c0f,0x5c22,
+ 0x5c38,0x5c71,0x5ddd,0x5de5,0x5df1,0x5df2,0x5df3,0x5dfe,0x5e72,0x5efe,
+ 0x5f0b,0x5f13,0x624d,0x4e11,0x4e10,0x4e0d,0x4e2d,0x4e30,0x4e39,0x4e4b,
+ 0x5c39,0x4e88,0x4e91,0x4e95,0x4e92,0x4e94,0x4ea2,0x4ec1,0x4ec0,0x4ec3,
+ 0x4ec6,0x4ec7,0x4ecd,0x4eca,0x4ecb,0x4ec4,0x5143,0x5141,0x5167,0x516d,
+ 0x516e,0x516c,0x5197,0x51f6
+ },
+ { /* ku 25 */
+ 0x5206,0x5207,0x5208,0x52fb,0x52fe,0x52ff,0x5316,0x5339,0x5348,0x5347,
+ 0x5345,0x535e,0x5384,0x53cb,0x53ca,0x53cd,0x58ec,0x5929,0x592b,0x592a,
+ 0x592d,0x5b54,0x5c11,0x5c24,0x5c3a,0x5c6f,0x5df4,0x5e7b,0x5eff,0x5f14,
+ 0x5f15,0x5fc3,0x6208,0x6236,0x624b,0x624e,0x652f,0x6587,0x6597,0x65a4,
+ 0x65b9,0x65e5,0x66f0,0x6708,0x6728,0x6b20,0x6b62,0x6b79,0x6bcb,0x6bd4,
+ 0x6bdb,0x6c0f,0x6c34,0x706b,0x722a,0x7236,0x723b,0x7247,0x7259,0x725b,
+ 0x72ac,0x738b,0x4e19,0x4e16,0x4e15,0x4e14,0x4e18,0x4e3b,0x4e4d,0x4e4f,
+ 0x4e4e,0x4ee5,0x4ed8,0x4ed4,0x4ed5,0x4ed6,0x4ed7,0x4ee3,0x4ee4,0x4ed9,
+ 0x4ede,0x5145,0x5144,0x5189,0x518a,0x51ac,0x51f9,0x51fa,0x51f8,0x520a,
+ 0x52a0,0x529f,0x5305,0x5306
+ },
+ { /* ku 26 */
+ 0x5317,0x531d,0x4edf,0x534a,0x5349,0x5361,0x5360,0x536f,0x536e,0x53bb,
+ 0x53ef,0x53e4,0x53f3,0x53ec,0x53ee,0x53e9,0x53e8,0x53fc,0x53f8,0x53f5,
+ 0x53eb,0x53e6,0x53ea,0x53f2,0x53f1,0x53f0,0x53e5,0x53ed,0x53fb,0x56db,
+ 0x56da,0x5916,0x592e,0x5931,0x5974,0x5976,0x5b55,0x5b83,0x5c3c,0x5de8,
+ 0x5de7,0x5de6,0x5e02,0x5e03,0x5e73,0x5e7c,0x5f01,0x5f18,0x5f17,0x5fc5,
+ 0x620a,0x6253,0x6254,0x6252,0x6251,0x65a5,0x65e6,0x672e,0x672c,0x672a,
+ 0x672b,0x672d,0x6b63,0x6bcd,0x6c11,0x6c10,0x6c38,0x6c41,0x6c40,0x6c3e,
+ 0x72af,0x7384,0x7389,0x74dc,0x74e6,0x7518,0x751f,0x7528,0x7529,0x7530,
+ 0x7531,0x7532,0x7533,0x758b,0x767d,0x76ae,0x76bf,0x76ee,0x77db,0x77e2,
+ 0x77f3,0x793a,0x79be,0x7a74
+ },
+ { /* ku 27 */
+ 0x7acb,0x4e1e,0x4e1f,0x4e52,0x4e53,0x4e69,0x4e99,0x4ea4,0x4ea6,0x4ea5,
+ 0x4eff,0x4f09,0x4f19,0x4f0a,0x4f15,0x4f0d,0x4f10,0x4f11,0x4f0f,0x4ef2,
+ 0x4ef6,0x4efb,0x4ef0,0x4ef3,0x4efd,0x4f01,0x4f0b,0x5149,0x5147,0x5146,
+ 0x5148,0x5168,0x5171,0x518d,0x51b0,0x5217,0x5211,0x5212,0x520e,0x5216,
+ 0x52a3,0x5308,0x5321,0x5320,0x5370,0x5371,0x5409,0x540f,0x540c,0x540a,
+ 0x5410,0x5401,0x540b,0x5404,0x5411,0x540d,0x5408,0x5403,0x540e,0x5406,
+ 0x5412,0x56e0,0x56de,0x56dd,0x5733,0x5730,0x5728,0x572d,0x572c,0x572f,
+ 0x5729,0x5919,0x591a,0x5937,0x5938,0x5984,0x5978,0x5983,0x597d,0x5979,
+ 0x5982,0x5981,0x5b57,0x5b58,0x5b87,0x5b88,0x5b85,0x5b89,0x5bfa,0x5c16,
+ 0x5c79,0x5dde,0x5e06,0x5e76
+ },
+ { /* ku 28 */
+ 0x5e74,0x5f0f,0x5f1b,0x5fd9,0x5fd6,0x620e,0x620c,0x620d,0x6210,0x6263,
+ 0x625b,0x6258,0x6536,0x65e9,0x65e8,0x65ec,0x65ed,0x66f2,0x66f3,0x6709,
+ 0x673d,0x6734,0x6731,0x6735,0x6b21,0x6b64,0x6b7b,0x6c16,0x6c5d,0x6c57,
+ 0x6c59,0x6c5f,0x6c60,0x6c50,0x6c55,0x6c61,0x6c5b,0x6c4d,0x6c4e,0x7070,
+ 0x725f,0x725d,0x767e,0x7af9,0x7c73,0x7cf8,0x7f36,0x7f8a,0x7fbd,0x8001,
+ 0x8003,0x800c,0x8012,0x8033,0x807f,0x8089,0x808b,0x808c,0x81e3,0x81ea,
+ 0x81f3,0x81fc,0x820c,0x821b,0x821f,0x826e,0x8272,0x827e,0x866b,0x8840,
+ 0x884c,0x8863,0x897f,0x9621,0x4e32,0x4ea8,0x4f4d,0x4f4f,0x4f47,0x4f57,
+ 0x4f5e,0x4f34,0x4f5b,0x4f55,0x4f30,0x4f50,0x4f51,0x4f3d,0x4f3a,0x4f38,
+ 0x4f43,0x4f54,0x4f3c,0x4f46
+ },
+ { /* ku 29 */
+ 0x4f63,0x4f5c,0x4f60,0x4f2f,0x4f4e,0x4f36,0x4f59,0x4f5d,0x4f48,0x4f5a,
+ 0x514c,0x514b,0x514d,0x5175,0x51b6,0x51b7,0x5225,0x5224,0x5229,0x522a,
+ 0x5228,0x52ab,0x52a9,0x52aa,0x52ac,0x5323,0x5373,0x5375,0x541d,0x542d,
+ 0x541e,0x543e,0x5426,0x544e,0x5427,0x5446,0x5443,0x5433,0x5448,0x5442,
+ 0x541b,0x5429,0x544a,0x5439,0x543b,0x5438,0x542e,0x5435,0x5436,0x5420,
+ 0x543c,0x5440,0x5431,0x542b,0x541f,0x542c,0x56ea,0x56f0,0x56e4,0x56eb,
+ 0x574a,0x5751,0x5740,0x574d,0x5747,0x574e,0x573e,0x5750,0x574f,0x573b,
+ 0x58ef,0x593e,0x599d,0x5992,0x59a8,0x599e,0x59a3,0x5999,0x5996,0x598d,
+ 0x59a4,0x5993,0x598a,0x59a5,0x5b5d,0x5b5c,0x5b5a,0x5b5b,0x5b8c,0x5b8b,
+ 0x5b8f,0x5c2c,0x5c40,0x5c41
+ },
+ { /* ku 2a */
+ 0x5c3f,0x5c3e,0x5c90,0x5c91,0x5c94,0x5c8c,0x5deb,0x5e0c,0x5e8f,0x5e87,
+ 0x5e8a,0x5ef7,0x5f04,0x5f1f,0x5f64,0x5f62,0x5f77,0x5f79,0x5fd8,0x5fcc,
+ 0x5fd7,0x5fcd,0x5ff1,0x5feb,0x5ff8,0x5fea,0x6212,0x6211,0x6284,0x6297,
+ 0x6296,0x6280,0x6276,0x6289,0x626d,0x628a,0x627c,0x627e,0x6279,0x6273,
+ 0x6292,0x626f,0x6298,0x626e,0x6295,0x6293,0x6291,0x6286,0x6539,0x653b,
+ 0x6538,0x65f1,0x66f4,0x675f,0x674e,0x674f,0x6750,0x6751,0x675c,0x6756,
+ 0x675e,0x6749,0x6746,0x6760,0x6753,0x6757,0x6b65,0x6bcf,0x6c42,0x6c5e,
+ 0x6c99,0x6c81,0x6c88,0x6c89,0x6c85,0x6c9b,0x6c6a,0x6c7a,0x6c90,0x6c70,
+ 0x6c8c,0x6c68,0x6c96,0x6c92,0x6c7d,0x6c83,0x6c72,0x6c7e,0x6c74,0x6c86,
+ 0x6c76,0x6c8d,0x6c94,0x6c98
+ },
+ { /* ku 2b */
+ 0x6c82,0x7076,0x707c,0x707d,0x7078,0x7262,0x7261,0x7260,0x72c4,0x72c2,
+ 0x7396,0x752c,0x752b,0x7537,0x7538,0x7682,0x76ef,0x77e3,0x79c1,0x79c0,
+ 0x79bf,0x7a76,0x7cfb,0x7f55,0x8096,0x8093,0x809d,0x8098,0x809b,0x809a,
+ 0x80b2,0x826f,0x8292,0x828b,0x828d,0x898b,0x89d2,0x8a00,0x8c37,0x8c46,
+ 0x8c55,0x8c9d,0x8d64,0x8d70,0x8db3,0x8eab,0x8eca,0x8f9b,0x8fb0,0x8fc2,
+ 0x8fc6,0x8fc5,0x8fc4,0x5de1,0x9091,0x90a2,0x90aa,0x90a6,0x90a3,0x9149,
+ 0x91c6,0x91cc,0x9632,0x962e,0x9631,0x962a,0x962c,0x4e26,0x4e56,0x4e73,
+ 0x4e8b,0x4e9b,0x4e9e,0x4eab,0x4eac,0x4f6f,0x4f9d,0x4f8d,0x4f73,0x4f7f,
+ 0x4f6c,0x4f9b,0x4f8b,0x4f86,0x4f83,0x4f70,0x4f75,0x4f88,0x4f69,0x4f7b,
+ 0x4f96,0x4f7e,0x4f8f,0x4f91
+ },
+ { /* ku 2c */
+ 0x4f7a,0x5154,0x5152,0x5155,0x5169,0x5177,0x5176,0x5178,0x51bd,0x51fd,
+ 0x523b,0x5238,0x5237,0x523a,0x5230,0x522e,0x5236,0x5241,0x52be,0x52bb,
+ 0x5352,0x5354,0x5353,0x5351,0x5366,0x5377,0x5378,0x5379,0x53d6,0x53d4,
+ 0x53d7,0x5473,0x5475,0x5496,0x5478,0x5495,0x5480,0x547b,0x5477,0x5484,
+ 0x5492,0x5486,0x547c,0x5490,0x5471,0x5476,0x548c,0x549a,0x5462,0x5468,
+ 0x548b,0x547d,0x548e,0x56fa,0x5783,0x5777,0x576a,0x5769,0x5761,0x5766,
+ 0x5764,0x577c,0x591c,0x5949,0x5947,0x5948,0x5944,0x5954,0x59be,0x59bb,
+ 0x59d4,0x59b9,0x59ae,0x59d1,0x59c6,0x59d0,0x59cd,0x59cb,0x59d3,0x59ca,
+ 0x59af,0x59b3,0x59d2,0x59c5,0x5b5f,0x5b64,0x5b63,0x5b97,0x5b9a,0x5b98,
+ 0x5b9c,0x5b99,0x5b9b,0x5c1a
+ },
+ { /* ku 2d */
+ 0x5c48,0x5c45,0x5c46,0x5cb7,0x5ca1,0x5cb8,0x5ca9,0x5cab,0x5cb1,0x5cb3,
+ 0x5e18,0x5e1a,0x5e16,0x5e15,0x5e1b,0x5e11,0x5e78,0x5e9a,0x5e97,0x5e9c,
+ 0x5e95,0x5e96,0x5ef6,0x5f26,0x5f27,0x5f29,0x5f80,0x5f81,0x5f7f,0x5f7c,
+ 0x5fdd,0x5fe0,0x5ffd,0x5ff5,0x5fff,0x600f,0x6014,0x602f,0x6035,0x6016,
+ 0x602a,0x6015,0x6021,0x6027,0x6029,0x602b,0x601b,0x6216,0x6215,0x623f,
+ 0x623e,0x6240,0x627f,0x62c9,0x62cc,0x62c4,0x62bf,0x62c2,0x62b9,0x62d2,
+ 0x62db,0x62ab,0x62d3,0x62d4,0x62cb,0x62c8,0x62a8,0x62bd,0x62bc,0x62d0,
+ 0x62d9,0x62c7,0x62cd,0x62b5,0x62da,0x62b1,0x62d8,0x62d6,0x62d7,0x62c6,
+ 0x62ac,0x62ce,0x653e,0x65a7,0x65bc,0x65fa,0x6614,0x6613,0x660c,0x6606,
+ 0x6602,0x660e,0x6600,0x660f
+ },
+ { /* ku 2e */
+ 0x6615,0x660a,0x6607,0x670d,0x670b,0x676d,0x678b,0x6795,0x6771,0x679c,
+ 0x6773,0x6777,0x6787,0x679d,0x6797,0x676f,0x6770,0x677f,0x6789,0x677e,
+ 0x6790,0x6775,0x679a,0x6793,0x677c,0x676a,0x6772,0x6b23,0x6b66,0x6b67,
+ 0x6b7f,0x6c13,0x6c1b,0x6ce3,0x6ce8,0x6cf3,0x6cb1,0x6ccc,0x6ce5,0x6cb3,
+ 0x6cbd,0x6cbe,0x6cbc,0x6ce2,0x6cab,0x6cd5,0x6cd3,0x6cb8,0x6cc4,0x6cb9,
+ 0x6cc1,0x6cae,0x6cd7,0x6cc5,0x6cf1,0x6cbf,0x6cbb,0x6ce1,0x6cdb,0x6cca,
+ 0x6cac,0x6cef,0x6cdc,0x6cd6,0x6ce0,0x7095,0x708e,0x7092,0x708a,0x7099,
+ 0x722c,0x722d,0x7238,0x7248,0x7267,0x7269,0x72c0,0x72ce,0x72d9,0x72d7,
+ 0x72d0,0x73a9,0x73a8,0x739f,0x73ab,0x73a5,0x753d,0x759d,0x7599,0x759a,
+ 0x7684,0x76c2,0x76f2,0x76f4
+ },
+ { /* ku 2f */
+ 0x77e5,0x77fd,0x793e,0x7940,0x7941,0x79c9,0x79c8,0x7a7a,0x7a79,0x7afa,
+ 0x7cfe,0x7f54,0x7f8c,0x7f8b,0x8005,0x80ba,0x80a5,0x80a2,0x80b1,0x80a1,
+ 0x80ab,0x80a9,0x80b4,0x80aa,0x80af,0x81e5,0x81fe,0x820d,0x82b3,0x829d,
+ 0x8299,0x82ad,0x82bd,0x829f,0x82b9,0x82b1,0x82ac,0x82a5,0x82af,0x82b8,
+ 0x82a3,0x82b0,0x82be,0x82b7,0x864e,0x8671,0x521d,0x8868,0x8ecb,0x8fce,
+ 0x8fd4,0x8fd1,0x90b5,0x90b8,0x90b1,0x90b6,0x91c7,0x91d1,0x9577,0x9580,
+ 0x961c,0x9640,0x963f,0x963b,0x9644,0x9642,0x96b9,0x96e8,0x9752,0x975e,
+ 0x4e9f,0x4ead,0x4eae,0x4fe1,0x4fb5,0x4faf,0x4fbf,0x4fe0,0x4fd1,0x4fcf,
+ 0x4fdd,0x4fc3,0x4fb6,0x4fd8,0x4fdf,0x4fca,0x4fd7,0x4fae,0x4fd0,0x4fc4,
+ 0x4fc2,0x4fda,0x4fce,0x4fde
+ },
+ { /* ku 30 */
+ 0x4fb7,0x5157,0x5192,0x5191,0x51a0,0x524e,0x5243,0x524a,0x524d,0x524c,
+ 0x524b,0x5247,0x52c7,0x52c9,0x52c3,0x52c1,0x530d,0x5357,0x537b,0x539a,
+ 0x53db,0x54ac,0x54c0,0x54a8,0x54ce,0x54c9,0x54b8,0x54a6,0x54b3,0x54c7,
+ 0x54c2,0x54bd,0x54aa,0x54c1,0x54c4,0x54c8,0x54af,0x54ab,0x54b1,0x54bb,
+ 0x54a9,0x54a7,0x54bf,0x56ff,0x5782,0x578b,0x57a0,0x57a3,0x57a2,0x57ce,
+ 0x57ae,0x5793,0x5955,0x5951,0x594f,0x594e,0x5950,0x59dc,0x59d8,0x59ff,
+ 0x59e3,0x59e8,0x5a03,0x59e5,0x59ea,0x59da,0x59e6,0x5a01,0x59fb,0x5b69,
+ 0x5ba3,0x5ba6,0x5ba4,0x5ba2,0x5ba5,0x5c01,0x5c4e,0x5c4f,0x5c4d,0x5c4b,
+ 0x5cd9,0x5cd2,0x5df7,0x5e1d,0x5e25,0x5e1f,0x5e7d,0x5ea0,0x5ea6,0x5efa,
+ 0x5f08,0x5f2d,0x5f65,0x5f88
+ },
+ { /* ku 31 */
+ 0x5f85,0x5f8a,0x5f8b,0x5f87,0x5f8c,0x5f89,0x6012,0x601d,0x6020,0x6025,
+ 0x600e,0x6028,0x604d,0x6070,0x6068,0x6062,0x6046,0x6043,0x606c,0x606b,
+ 0x606a,0x6064,0x6241,0x62dc,0x6316,0x6309,0x62fc,0x62ed,0x6301,0x62ee,
+ 0x62fd,0x6307,0x62f1,0x62f7,0x62ef,0x62ec,0x62fe,0x62f4,0x6311,0x6302,
+ 0x653f,0x6545,0x65ab,0x65bd,0x65e2,0x6625,0x662d,0x6620,0x6627,0x662f,
+ 0x661f,0x6628,0x6631,0x6624,0x66f7,0x67ff,0x67d3,0x67f1,0x67d4,0x67d0,
+ 0x67ec,0x67b6,0x67af,0x67f5,0x67e9,0x67ef,0x67c4,0x67d1,0x67b4,0x67da,
+ 0x67e5,0x67b8,0x67cf,0x67de,0x67f3,0x67b0,0x67d9,0x67e2,0x67dd,0x67d2,
+ 0x6b6a,0x6b83,0x6b86,0x6bb5,0x6bd2,0x6bd7,0x6c1f,0x6cc9,0x6d0b,0x6d32,
+ 0x6d2a,0x6d41,0x6d25,0x6d0c
+ },
+ { /* ku 32 */
+ 0x6d31,0x6d1e,0x6d17,0x6d3b,0x6d3d,0x6d3e,0x6d36,0x6d1b,0x6cf5,0x6d39,
+ 0x6d27,0x6d38,0x6d29,0x6d2e,0x6d35,0x6d0e,0x6d2b,0x70ab,0x70ba,0x70b3,
+ 0x70ac,0x70af,0x70ad,0x70b8,0x70ae,0x70a4,0x7230,0x7272,0x726f,0x7274,
+ 0x72e9,0x72e0,0x72e1,0x73b7,0x73ca,0x73bb,0x73b2,0x73cd,0x73c0,0x73b3,
+ 0x751a,0x752d,0x754f,0x754c,0x754e,0x754b,0x75ab,0x75a4,0x75a5,0x75a2,
+ 0x75a3,0x7678,0x7686,0x7687,0x7688,0x76c8,0x76c6,0x76c3,0x76c5,0x7701,
+ 0x76f9,0x76f8,0x7709,0x770b,0x76fe,0x76fc,0x7707,0x77dc,0x7802,0x7814,
+ 0x780c,0x780d,0x7946,0x7949,0x7948,0x7947,0x79b9,0x79ba,0x79d1,0x79d2,
+ 0x79cb,0x7a7f,0x7a81,0x7aff,0x7afd,0x7c7d,0x7d02,0x7d05,0x7d00,0x7d09,
+ 0x7d07,0x7d04,0x7d06,0x7f38
+ },
+ { /* ku 33 */
+ 0x7f8e,0x7fbf,0x8010,0x800d,0x8011,0x8036,0x80d6,0x80e5,0x80da,0x80c3,
+ 0x80c4,0x80cc,0x80e1,0x80db,0x80ce,0x80de,0x80e4,0x80dd,0x81f4,0x8222,
+ 0x82e7,0x8303,0x8305,0x82e3,0x82db,0x82e6,0x8304,0x82e5,0x8302,0x8309,
+ 0x82d2,0x82d7,0x82f1,0x8301,0x82dc,0x82d4,0x82d1,0x82de,0x82d3,0x82df,
+ 0x82ef,0x8306,0x8650,0x8679,0x867b,0x867a,0x884d,0x886b,0x8981,0x89d4,
+ 0x8a08,0x8a02,0x8a03,0x8c9e,0x8ca0,0x8d74,0x8d73,0x8db4,0x8ecd,0x8ecc,
+ 0x8ff0,0x8fe6,0x8fe2,0x8fea,0x8fe5,0x8fed,0x8feb,0x8fe4,0x8fe8,0x90ca,
+ 0x90ce,0x90c1,0x90c3,0x914b,0x914a,0x91cd,0x9582,0x9650,0x964b,0x964c,
+ 0x964d,0x9762,0x9769,0x97cb,0x97ed,0x97f3,0x9801,0x98a8,0x98db,0x98df,
+ 0x9996,0x9999,0x4e58,0x4eb3
+ },
+ { /* ku 34 */
+ 0x500c,0x500d,0x5023,0x4fef,0x5026,0x5025,0x4ff8,0x5029,0x5016,0x5006,
+ 0x503c,0x501f,0x501a,0x5012,0x5011,0x4ffa,0x5000,0x5014,0x5028,0x4ff1,
+ 0x5021,0x500b,0x5019,0x5018,0x4ff3,0x4fee,0x502d,0x502a,0x4ffe,0x502b,
+ 0x5009,0x517c,0x51a4,0x51a5,0x51a2,0x51cd,0x51cc,0x51c6,0x51cb,0x5256,
+ 0x525c,0x5254,0x525b,0x525d,0x532a,0x537f,0x539f,0x539d,0x53df,0x54e8,
+ 0x5510,0x5501,0x5537,0x54fc,0x54e5,0x54f2,0x5506,0x54fa,0x5514,0x54e9,
+ 0x54ed,0x54e1,0x5509,0x54ee,0x54ea,0x54e6,0x5527,0x5507,0x54fd,0x550f,
+ 0x5703,0x5704,0x57c2,0x57d4,0x57cb,0x57c3,0x5809,0x590f,0x5957,0x5958,
+ 0x595a,0x5a11,0x5a18,0x5a1c,0x5a1f,0x5a1b,0x5a13,0x59ec,0x5a20,0x5a23,
+ 0x5a29,0x5a25,0x5a0c,0x5a09
+ },
+ { /* ku 35 */
+ 0x5b6b,0x5c58,0x5bb0,0x5bb3,0x5bb6,0x5bb4,0x5bae,0x5bb5,0x5bb9,0x5bb8,
+ 0x5c04,0x5c51,0x5c55,0x5c50,0x5ced,0x5cfd,0x5cfb,0x5cea,0x5ce8,0x5cf0,
+ 0x5cf6,0x5d01,0x5cf4,0x5dee,0x5e2d,0x5e2b,0x5eab,0x5ead,0x5ea7,0x5f31,
+ 0x5f92,0x5f91,0x5f90,0x6059,0x6063,0x6065,0x6050,0x6055,0x606d,0x6069,
+ 0x606f,0x6084,0x609f,0x609a,0x608d,0x6094,0x608c,0x6085,0x6096,0x6247,
+ 0x62f3,0x6308,0x62ff,0x634e,0x633e,0x632f,0x6355,0x6342,0x6346,0x634f,
+ 0x6349,0x633a,0x6350,0x633d,0x632a,0x632b,0x6328,0x634d,0x634c,0x6548,
+ 0x6549,0x6599,0x65c1,0x65c5,0x6642,0x6649,0x664f,0x6643,0x6652,0x664c,
+ 0x6645,0x6641,0x66f8,0x6714,0x6715,0x6717,0x6821,0x6838,0x6848,0x6846,
+ 0x6853,0x6839,0x6842,0x6854
+ },
+ { /* ku 36 */
+ 0x6829,0x68b3,0x6817,0x684c,0x6851,0x683d,0x67f4,0x6850,0x6840,0x683c,
+ 0x6843,0x682a,0x6845,0x6813,0x6818,0x6841,0x6b8a,0x6b89,0x6bb7,0x6c23,
+ 0x6c27,0x6c28,0x6c26,0x6c24,0x6cf0,0x6d6a,0x6d95,0x6d88,0x6d87,0x6d66,
+ 0x6d78,0x6d77,0x6d59,0x6d93,0x6d6c,0x6d89,0x6d6e,0x6d5a,0x6d74,0x6d69,
+ 0x6d8c,0x6d8a,0x6d79,0x6d85,0x6d65,0x6d94,0x70ca,0x70d8,0x70e4,0x70d9,
+ 0x70c8,0x70cf,0x7239,0x7279,0x72fc,0x72f9,0x72fd,0x72f8,0x72f7,0x7386,
+ 0x73ed,0x7409,0x73ee,0x73e0,0x73ea,0x73de,0x7554,0x755d,0x755c,0x755a,
+ 0x7559,0x75be,0x75c5,0x75c7,0x75b2,0x75b3,0x75bd,0x75bc,0x75b9,0x75c2,
+ 0x75b8,0x768b,0x76b0,0x76ca,0x76cd,0x76ce,0x7729,0x771f,0x7720,0x7728,
+ 0x77e9,0x7830,0x7827,0x7838
+ },
+ { /* ku 37 */
+ 0x781d,0x7834,0x7837,0x7825,0x782d,0x7820,0x781f,0x7832,0x7955,0x7950,
+ 0x7960,0x795f,0x7956,0x795e,0x795d,0x7957,0x795a,0x79e4,0x79e3,0x79e7,
+ 0x79df,0x79e6,0x79e9,0x79d8,0x7a84,0x7a88,0x7ad9,0x7b06,0x7b11,0x7c89,
+ 0x7d21,0x7d17,0x7d0b,0x7d0a,0x7d20,0x7d22,0x7d14,0x7d10,0x7d15,0x7d1a,
+ 0x7d1c,0x7d0d,0x7d19,0x7d1b,0x7f3a,0x7f5f,0x7f94,0x7fc5,0x7fc1,0x8006,
+ 0x8004,0x8018,0x8015,0x8019,0x8017,0x803d,0x803f,0x80f1,0x8102,0x80f0,
+ 0x8105,0x80ed,0x80f4,0x8106,0x80f8,0x80f3,0x8108,0x80fd,0x810a,0x80fc,
+ 0x80ef,0x81ed,0x81ec,0x8200,0x8210,0x822a,0x822b,0x8228,0x822c,0x82bb,
+ 0x832b,0x8352,0x8354,0x834a,0x8338,0x8350,0x8349,0x8335,0x8334,0x834f,
+ 0x8332,0x8339,0x8336,0x8317
+ },
+ { /* ku 38 */
+ 0x8340,0x8331,0x8328,0x8343,0x8654,0x868a,0x86aa,0x8693,0x86a4,0x86a9,
+ 0x868c,0x86a3,0x869c,0x8870,0x8877,0x8881,0x8882,0x887d,0x8879,0x8a18,
+ 0x8a10,0x8a0e,0x8a0c,0x8a15,0x8a0a,0x8a17,0x8a13,0x8a16,0x8a0f,0x8a11,
+ 0x8c48,0x8c7a,0x8c79,0x8ca1,0x8ca2,0x8d77,0x8eac,0x8ed2,0x8ed4,0x8ecf,
+ 0x8fb1,0x9001,0x9006,0x8ff7,0x9000,0x8ffa,0x8ff4,0x9003,0x8ffd,0x9005,
+ 0x8ff8,0x9095,0x90e1,0x90dd,0x90e2,0x9152,0x914d,0x914c,0x91d8,0x91dd,
+ 0x91d7,0x91dc,0x91d9,0x9583,0x9662,0x9663,0x9661,0x965b,0x965d,0x9664,
+ 0x9658,0x965e,0x96bb,0x98e2,0x99ac,0x9aa8,0x9ad8,0x9b25,0x9b32,0x9b3c,
+ 0x4e7e,0x507a,0x507d,0x505c,0x5047,0x5043,0x504c,0x505a,0x5049,0x5065,
+ 0x5076,0x504e,0x5055,0x5075
+ },
+ { /* ku 39 */
+ 0x5074,0x5077,0x504f,0x500f,0x506f,0x506d,0x515c,0x5195,0x51f0,0x526a,
+ 0x526f,0x52d2,0x52d9,0x52d8,0x52d5,0x5310,0x530f,0x5319,0x533f,0x5340,
+ 0x533e,0x53c3,0x66fc,0x5546,0x556a,0x5566,0x5544,0x555e,0x5561,0x5543,
+ 0x554a,0x5531,0x5556,0x554f,0x5555,0x552f,0x5564,0x5538,0x552e,0x555c,
+ 0x552c,0x5563,0x5533,0x5541,0x5557,0x5708,0x570b,0x5709,0x57df,0x5805,
+ 0x580a,0x5806,0x57e0,0x57e4,0x57fa,0x5802,0x5835,0x57f7,0x57f9,0x5920,
+ 0x5962,0x5a36,0x5a41,0x5a49,0x5a66,0x5a6a,0x5a40,0x5a3c,0x5a62,0x5a5a,
+ 0x5a46,0x5a4a,0x5b70,0x5bc7,0x5bc5,0x5bc4,0x5bc2,0x5bbf,0x5bc6,0x5c09,
+ 0x5c08,0x5c07,0x5c60,0x5c5c,0x5c5d,0x5d07,0x5d06,0x5d0e,0x5d1b,0x5d16,
+ 0x5d22,0x5d11,0x5d29,0x5d14
+ },
+ { /* ku 3a */
+ 0x5d19,0x5d24,0x5d27,0x5d17,0x5de2,0x5e38,0x5e36,0x5e33,0x5e37,0x5eb7,
+ 0x5eb8,0x5eb6,0x5eb5,0x5ebe,0x5f35,0x5f37,0x5f57,0x5f6c,0x5f69,0x5f6b,
+ 0x5f97,0x5f99,0x5f9e,0x5f98,0x5fa1,0x5fa0,0x5f9c,0x607f,0x60a3,0x6089,
+ 0x60a0,0x60a8,0x60cb,0x60b4,0x60e6,0x60bd,0x60c5,0x60bb,0x60b5,0x60dc,
+ 0x60bc,0x60d8,0x60d5,0x60c6,0x60df,0x60b8,0x60da,0x60c7,0x621a,0x621b,
+ 0x6248,0x63a0,0x63a7,0x6372,0x6396,0x63a2,0x63a5,0x6377,0x6367,0x6398,
+ 0x63aa,0x6371,0x63a9,0x6389,0x6383,0x639b,0x636b,0x63a8,0x6384,0x6388,
+ 0x6399,0x63a1,0x63ac,0x6392,0x638f,0x6380,0x637b,0x6369,0x6368,0x637a,
+ 0x655d,0x6556,0x6551,0x6559,0x6557,0x555f,0x654f,0x6558,0x6555,0x6554,
+ 0x659c,0x659b,0x65ac,0x65cf
+ },
+ { /* ku 3b */
+ 0x65cb,0x65cc,0x65ce,0x665d,0x665a,0x6664,0x6668,0x6666,0x665e,0x66f9,
+ 0x52d7,0x671b,0x6881,0x68af,0x68a2,0x6893,0x68b5,0x687f,0x6876,0x68b1,
+ 0x68a7,0x6897,0x68b0,0x6883,0x68c4,0x68ad,0x6886,0x6885,0x6894,0x689d,
+ 0x68a8,0x689f,0x68a1,0x6882,0x6b32,0x6bba,0x6beb,0x6bec,0x6c2b,0x6d8e,
+ 0x6dbc,0x6df3,0x6dd9,0x6db2,0x6de1,0x6dcc,0x6de4,0x6dfb,0x6dfa,0x6e05,
+ 0x6dc7,0x6dcb,0x6daf,0x6dd1,0x6dae,0x6dde,0x6df9,0x6db8,0x6df7,0x6df5,
+ 0x6dc5,0x6dd2,0x6e1a,0x6db5,0x6dda,0x6deb,0x6dd8,0x6dea,0x6df1,0x6dee,
+ 0x6de8,0x6dc6,0x6dc4,0x6daa,0x6dec,0x6dbf,0x6de6,0x70f9,0x7109,0x710a,
+ 0x70fd,0x70ef,0x723d,0x727d,0x7281,0x731c,0x731b,0x7316,0x7313,0x7319,
+ 0x7387,0x7405,0x740a,0x7403
+ },
+ { /* ku 3c */
+ 0x7406,0x73fe,0x740d,0x74e0,0x74f6,0x74f7,0x751c,0x7522,0x7565,0x7566,
+ 0x7562,0x7570,0x758f,0x75d4,0x75d5,0x75b5,0x75ca,0x75cd,0x768e,0x76d4,
+ 0x76d2,0x76db,0x7737,0x773e,0x773c,0x7736,0x7738,0x773a,0x786b,0x7843,
+ 0x784e,0x7965,0x7968,0x796d,0x79fb,0x7a92,0x7a95,0x7b20,0x7b28,0x7b1b,
+ 0x7b2c,0x7b26,0x7b19,0x7b1e,0x7b2e,0x7c92,0x7c97,0x7c95,0x7d46,0x7d43,
+ 0x7d71,0x7d2e,0x7d39,0x7d3c,0x7d40,0x7d30,0x7d33,0x7d44,0x7d2f,0x7d42,
+ 0x7d32,0x7d31,0x7f3d,0x7f9e,0x7f9a,0x7fcc,0x7fce,0x7fd2,0x801c,0x804a,
+ 0x8046,0x812f,0x8116,0x8123,0x812b,0x8129,0x8130,0x8124,0x8202,0x8235,
+ 0x8237,0x8236,0x8239,0x838e,0x839e,0x8398,0x8378,0x83a2,0x8396,0x83bd,
+ 0x83ab,0x8392,0x838a,0x8393
+ },
+ { /* ku 3d */
+ 0x8389,0x83a0,0x8377,0x837b,0x837c,0x8386,0x83a7,0x8655,0x5f6a,0x86c7,
+ 0x86c0,0x86b6,0x86c4,0x86b5,0x86c6,0x86cb,0x86b1,0x86af,0x86c9,0x8853,
+ 0x889e,0x8888,0x88ab,0x8892,0x8896,0x888d,0x888b,0x8993,0x898f,0x8a2a,
+ 0x8a1d,0x8a23,0x8a25,0x8a31,0x8a2d,0x8a1f,0x8a1b,0x8a22,0x8c49,0x8c5a,
+ 0x8ca9,0x8cac,0x8cab,0x8ca8,0x8caa,0x8ca7,0x8d67,0x8d66,0x8dbe,0x8dba,
+ 0x8edb,0x8edf,0x9019,0x900d,0x901a,0x9017,0x9023,0x901f,0x901d,0x9010,
+ 0x9015,0x901e,0x9020,0x900f,0x9022,0x9016,0x901b,0x9014,0x90e8,0x90ed,
+ 0x90fd,0x9157,0x91ce,0x91f5,0x91e6,0x91e3,0x91e7,0x91ed,0x91e9,0x9589,
+ 0x966a,0x9675,0x9673,0x9678,0x9670,0x9674,0x9676,0x9677,0x966c,0x96c0,
+ 0x96ea,0x96e9,0x7ae0,0x7adf
+ },
+ { /* ku 3e */
+ 0x9802,0x9803,0x9b5a,0x9ce5,0x9e75,0x9e7f,0x9ea5,0x9ebb,0x50a2,0x508d,
+ 0x5085,0x5099,0x5091,0x5080,0x5096,0x5098,0x509a,0x6700,0x51f1,0x5272,
+ 0x5274,0x5275,0x5269,0x52de,0x52dd,0x52db,0x535a,0x53a5,0x557b,0x5580,
+ 0x55a7,0x557c,0x558a,0x559d,0x5598,0x5582,0x559c,0x55aa,0x5594,0x5587,
+ 0x558b,0x5583,0x55b3,0x55ae,0x559f,0x553e,0x55b2,0x559a,0x55bb,0x55ac,
+ 0x55b1,0x557e,0x5589,0x55ab,0x5599,0x570d,0x582f,0x582a,0x5834,0x5824,
+ 0x5830,0x5831,0x5821,0x581d,0x5820,0x58f9,0x58fa,0x5960,0x5a77,0x5a9a,
+ 0x5a7f,0x5a92,0x5a9b,0x5aa7,0x5b73,0x5b71,0x5bd2,0x5bcc,0x5bd3,0x5bd0,
+ 0x5c0a,0x5c0b,0x5c31,0x5d4c,0x5d50,0x5d34,0x5d47,0x5dfd,0x5e45,0x5e3d,
+ 0x5e40,0x5e43,0x5e7e,0x5eca
+ },
+ { /* ku 3f */
+ 0x5ec1,0x5ec2,0x5ec4,0x5f3c,0x5f6d,0x5fa9,0x5faa,0x5fa8,0x60d1,0x60e1,
+ 0x60b2,0x60b6,0x60e0,0x611c,0x6123,0x60fa,0x6115,0x60f0,0x60fb,0x60f4,
+ 0x6168,0x60f1,0x610e,0x60f6,0x6109,0x6100,0x6112,0x621f,0x6249,0x63a3,
+ 0x638c,0x63cf,0x63c0,0x63e9,0x63c9,0x63c6,0x63cd,0x63d2,0x63e3,0x63d0,
+ 0x63e1,0x63d6,0x63ed,0x63ee,0x6376,0x63f4,0x63ea,0x63db,0x6452,0x63da,
+ 0x63f9,0x655e,0x6566,0x6562,0x6563,0x6591,0x6590,0x65af,0x666e,0x6670,
+ 0x6674,0x6676,0x666f,0x6691,0x667a,0x667e,0x6677,0x66fe,0x66ff,0x671f,
+ 0x671d,0x68fa,0x68d5,0x68e0,0x68d8,0x68d7,0x6905,0x68df,0x68f5,0x68ee,
+ 0x68e7,0x68f9,0x68d2,0x68f2,0x68e3,0x68cb,0x68cd,0x690d,0x6912,0x690e,
+ 0x68c9,0x68da,0x696e,0x68fb
+ },
+ { /* ku 40 */
+ 0x6b3e,0x6b3a,0x6b3d,0x6b98,0x6b96,0x6bbc,0x6bef,0x6c2e,0x6c2f,0x6c2c,
+ 0x6e2f,0x6e38,0x6e54,0x6e21,0x6e32,0x6e67,0x6e4a,0x6e20,0x6e25,0x6e23,
+ 0x6e1b,0x6e5b,0x6e58,0x6e24,0x6e56,0x6e6e,0x6e2d,0x6e26,0x6e6f,0x6e34,
+ 0x6e4d,0x6e3a,0x6e2c,0x6e43,0x6e1d,0x6e3e,0x6ecb,0x6e89,0x6e19,0x6e4e,
+ 0x6e63,0x6e44,0x6e72,0x6e69,0x6e5f,0x7119,0x711a,0x7126,0x7130,0x7121,
+ 0x7136,0x716e,0x711c,0x724c,0x7284,0x7280,0x7336,0x7325,0x7334,0x7329,
+ 0x743a,0x742a,0x7433,0x7422,0x7425,0x7435,0x7436,0x7434,0x742f,0x741b,
+ 0x7426,0x7428,0x7525,0x7526,0x756b,0x756a,0x75e2,0x75db,0x75e3,0x75d9,
+ 0x75d8,0x75de,0x75e0,0x767b,0x767c,0x7696,0x7693,0x76b4,0x76dc,0x774f,
+ 0x77ed,0x785d,0x786c,0x786f
+ },
+ { /* ku 41 */
+ 0x7a0d,0x7a08,0x7a0b,0x7a05,0x7a00,0x7a98,0x7a97,0x7a96,0x7ae5,0x7ae3,
+ 0x7b49,0x7b56,0x7b46,0x7b50,0x7b52,0x7b54,0x7b4d,0x7b4b,0x7b4f,0x7b51,
+ 0x7c9f,0x7ca5,0x7d5e,0x7d50,0x7d68,0x7d55,0x7d2b,0x7d6e,0x7d72,0x7d61,
+ 0x7d66,0x7d62,0x7d70,0x7d73,0x5584,0x7fd4,0x7fd5,0x800b,0x8052,0x8085,
+ 0x8155,0x8154,0x814b,0x8151,0x814e,0x8139,0x8146,0x813e,0x814c,0x8153,
+ 0x8174,0x8212,0x821c,0x83e9,0x8403,0x83f8,0x840d,0x83e0,0x83c5,0x840b,
+ 0x83c1,0x83ef,0x83f1,0x83f4,0x8457,0x840a,0x83f0,0x840c,0x83cc,0x83fd,
+ 0x83f2,0x83ca,0x8438,0x840e,0x8404,0x83dc,0x8407,0x83d4,0x83df,0x865b,
+ 0x86df,0x86d9,0x86ed,0x86d4,0x86db,0x86e4,0x86d0,0x86de,0x8857,0x88c1,
+ 0x88c2,0x88b1,0x8983,0x8996
+ },
+ { /* ku 42 */
+ 0x8a3b,0x8a60,0x8a55,0x8a5e,0x8a3c,0x8a41,0x8a54,0x8a5b,0x8a50,0x8a46,
+ 0x8a34,0x8a3a,0x8a36,0x8a56,0x8c61,0x8c82,0x8caf,0x8cbc,0x8cb3,0x8cbd,
+ 0x8cc1,0x8cbb,0x8cc0,0x8cb4,0x8cb7,0x8cb6,0x8cbf,0x8cb8,0x8d8a,0x8d85,
+ 0x8d81,0x8dce,0x8ddd,0x8dcb,0x8dda,0x8dd1,0x8dcc,0x8ddb,0x8dc6,0x8efb,
+ 0x8ef8,0x8efc,0x8f9c,0x902e,0x9035,0x9031,0x9038,0x9032,0x9036,0x9102,
+ 0x90f5,0x9109,0x90fe,0x9163,0x9165,0x91cf,0x9214,0x9215,0x9223,0x9209,
+ 0x921e,0x920d,0x9210,0x9207,0x9211,0x9594,0x958f,0x958b,0x9591,0x9593,
+ 0x9592,0x958e,0x968a,0x968e,0x968b,0x967d,0x9685,0x9686,0x968d,0x9672,
+ 0x9684,0x96c1,0x96c5,0x96c4,0x96c6,0x96c7,0x96ef,0x96f2,0x97cc,0x9805,
+ 0x9806,0x9808,0x98e7,0x98ea
+ },
+ { /* ku 43 */
+ 0x98ef,0x98e9,0x98f2,0x98ed,0x99ae,0x99ad,0x9ec3,0x9ecd,0x9ed1,0x4e82,
+ 0x50ad,0x50b5,0x50b2,0x50b3,0x50c5,0x50be,0x50ac,0x50b7,0x50bb,0x50af,
+ 0x50c7,0x527f,0x5277,0x527d,0x52df,0x52e6,0x52e4,0x52e2,0x52e3,0x532f,
+ 0x55df,0x55e8,0x55d3,0x55e6,0x55ce,0x55dc,0x55c7,0x55d1,0x55e3,0x55e4,
+ 0x55ef,0x55da,0x55e1,0x55c5,0x55c6,0x55e5,0x55c9,0x5712,0x5713,0x585e,
+ 0x5851,0x5858,0x5857,0x585a,0x5854,0x586b,0x584c,0x586d,0x584a,0x5862,
+ 0x5852,0x584b,0x5967,0x5ac1,0x5ac9,0x5acc,0x5abe,0x5abd,0x5abc,0x5ab3,
+ 0x5ac2,0x5ab2,0x5d69,0x5d6f,0x5e4c,0x5e79,0x5ec9,0x5ec8,0x5f12,0x5f59,
+ 0x5fac,0x5fae,0x611a,0x610f,0x6148,0x611f,0x60f3,0x611b,0x60f9,0x6101,
+ 0x6108,0x614e,0x614c,0x6144
+ },
+ { /* ku 44 */
+ 0x614d,0x613e,0x6134,0x6127,0x610d,0x6106,0x6137,0x6221,0x6222,0x6413,
+ 0x643e,0x641e,0x642a,0x642d,0x643d,0x642c,0x640f,0x641c,0x6414,0x640d,
+ 0x6436,0x6416,0x6417,0x6406,0x656c,0x659f,0x65b0,0x6697,0x6689,0x6687,
+ 0x6688,0x6696,0x6684,0x6698,0x668d,0x6703,0x6994,0x696d,0x695a,0x6977,
+ 0x6960,0x6954,0x6975,0x6930,0x6982,0x694a,0x6968,0x696b,0x695e,0x6953,
+ 0x6979,0x6986,0x695d,0x6963,0x695b,0x6b47,0x6b72,0x6bc0,0x6bbf,0x6bd3,
+ 0x6bfd,0x6ea2,0x6eaf,0x6ed3,0x6eb6,0x6ec2,0x6e90,0x6e9d,0x6ec7,0x6ec5,
+ 0x6ea5,0x6e98,0x6ebc,0x6eba,0x6eab,0x6ed1,0x6e96,0x6e9c,0x6ec4,0x6ed4,
+ 0x6eaa,0x6ea7,0x6eb4,0x714e,0x7159,0x7169,0x7164,0x7149,0x7167,0x715c,
+ 0x716c,0x7166,0x714c,0x7165
+ },
+ { /* ku 45 */
+ 0x715e,0x7146,0x7168,0x7156,0x723a,0x7252,0x7337,0x7345,0x733f,0x733e,
+ 0x746f,0x745a,0x7455,0x745f,0x745e,0x7441,0x743f,0x7459,0x745b,0x745c,
+ 0x7576,0x7578,0x7600,0x75f0,0x7601,0x75f2,0x75f1,0x75fa,0x75ff,0x75f4,
+ 0x75f3,0x76de,0x76df,0x775b,0x776b,0x7766,0x775e,0x7763,0x7779,0x776a,
+ 0x776c,0x775c,0x7765,0x7768,0x7762,0x77ee,0x788e,0x78b0,0x7897,0x7898,
+ 0x788c,0x7889,0x787c,0x7891,0x7893,0x787f,0x797a,0x797f,0x7981,0x842c,
+ 0x79bd,0x7a1c,0x7a1a,0x7a20,0x7a14,0x7a1f,0x7a1e,0x7a9f,0x7aa0,0x7b77,
+ 0x7bc0,0x7b60,0x7b6e,0x7b67,0x7cb1,0x7cb3,0x7cb5,0x7d93,0x7d79,0x7d91,
+ 0x7d81,0x7d8f,0x7d5b,0x7f6e,0x7f69,0x7f6a,0x7f72,0x7fa9,0x7fa8,0x7fa4,
+ 0x8056,0x8058,0x8086,0x8084
+ },
+ { /* ku 46 */
+ 0x8171,0x8170,0x8178,0x8165,0x816e,0x8173,0x816b,0x8179,0x817a,0x8166,
+ 0x8205,0x8247,0x8482,0x8477,0x843d,0x8431,0x8475,0x8466,0x846b,0x8449,
+ 0x846c,0x845b,0x843c,0x8435,0x8461,0x8463,0x8469,0x846d,0x8446,0x865e,
+ 0x865c,0x865f,0x86f9,0x8713,0x8708,0x8707,0x8700,0x86fe,0x86fb,0x8702,
+ 0x8703,0x8706,0x870a,0x8859,0x88df,0x88d4,0x88d9,0x88dc,0x88d8,0x88dd,
+ 0x88e1,0x88ca,0x88d5,0x88d2,0x899c,0x89e3,0x8a6b,0x8a72,0x8a73,0x8a66,
+ 0x8a69,0x8a70,0x8a87,0x8a7c,0x8a63,0x8aa0,0x8a71,0x8a85,0x8a6d,0x8a62,
+ 0x8a6e,0x8a6c,0x8a79,0x8a7b,0x8a3e,0x8a68,0x8c62,0x8c8a,0x8c89,0x8cca,
+ 0x8cc7,0x8cc8,0x8cc4,0x8cb2,0x8cc3,0x8cc2,0x8cc5,0x8de1,0x8ddf,0x8de8,
+ 0x8def,0x8df3,0x8dfa,0x8dea
+ },
+ { /* ku 47 */
+ 0x8de4,0x8de6,0x8eb2,0x8f03,0x8f09,0x8efe,0x8f0a,0x8f9f,0x8fb2,0x904b,
+ 0x904a,0x9053,0x9042,0x9054,0x903c,0x9055,0x9050,0x9047,0x904f,0x904e,
+ 0x904d,0x9051,0x903e,0x9041,0x9112,0x9117,0x916c,0x916a,0x9169,0x91c9,
+ 0x9237,0x9257,0x9238,0x923d,0x9240,0x923e,0x925b,0x924b,0x9264,0x9251,
+ 0x9234,0x9249,0x924d,0x9245,0x9239,0x923f,0x925a,0x9598,0x9698,0x9694,
+ 0x9695,0x96cd,0x96cb,0x96c9,0x96ca,0x96f7,0x96fb,0x96f9,0x96f6,0x9756,
+ 0x9774,0x9776,0x9810,0x9811,0x9813,0x980a,0x9812,0x980c,0x98fc,0x98f4,
+ 0x98fd,0x98fe,0x99b3,0x99b1,0x99b4,0x9ae1,0x9ce9,0x9e82,0x9f0e,0x9f13,
+ 0x9f20,0x50e7,0x50ee,0x50e5,0x50d6,0x50ed,0x50da,0x50d5,0x50cf,0x50d1,
+ 0x50f1,0x50ce,0x50e9,0x5162
+ },
+ { /* ku 48 */
+ 0x51f3,0x5283,0x5282,0x5331,0x53ad,0x55fe,0x5600,0x561b,0x5617,0x55fd,
+ 0x5614,0x5606,0x5609,0x560d,0x560e,0x55f7,0x5616,0x561f,0x5608,0x5610,
+ 0x55f6,0x5718,0x5716,0x5875,0x587e,0x5883,0x5893,0x588a,0x5879,0x5885,
+ 0x587d,0x58fd,0x5925,0x5922,0x5924,0x596a,0x5969,0x5ae1,0x5ae6,0x5ae9,
+ 0x5ad7,0x5ad6,0x5ad8,0x5ae3,0x5b75,0x5bde,0x5be7,0x5be1,0x5be5,0x5be6,
+ 0x5be8,0x5be2,0x5be4,0x5bdf,0x5c0d,0x5c62,0x5d84,0x5d87,0x5e5b,0x5e63,
+ 0x5e55,0x5e57,0x5e54,0x5ed3,0x5ed6,0x5f0a,0x5f46,0x5f70,0x5fb9,0x6147,
+ 0x613f,0x614b,0x6177,0x6162,0x6163,0x615f,0x615a,0x6158,0x6175,0x622a,
+ 0x6487,0x6458,0x6454,0x64a4,0x6478,0x645f,0x647a,0x6451,0x6467,0x6434,
+ 0x646d,0x647b,0x6572,0x65a1
+ },
+ { /* ku 49 */
+ 0x65d7,0x65d6,0x66a2,0x66a8,0x669d,0x699c,0x69a8,0x6995,0x69c1,0x69ae,
+ 0x69d3,0x69cb,0x699b,0x69b7,0x69bb,0x69ab,0x69b4,0x69d0,0x69cd,0x69ad,
+ 0x69cc,0x69a6,0x69c3,0x69a3,0x6b49,0x6b4c,0x6c33,0x6f33,0x6f14,0x6efe,
+ 0x6f13,0x6ef4,0x6f29,0x6f3e,0x6f20,0x6f2c,0x6f0f,0x6f02,0x6f22,0x6eff,
+ 0x6eef,0x6f06,0x6f31,0x6f38,0x6f32,0x6f23,0x6f15,0x6f2b,0x6f2f,0x6f88,
+ 0x6f2a,0x6eec,0x6f01,0x6ef2,0x6ecc,0x6ef7,0x7194,0x7199,0x717d,0x718a,
+ 0x7184,0x7192,0x723e,0x7292,0x7296,0x7344,0x7350,0x7464,0x7463,0x746a,
+ 0x7470,0x746d,0x7504,0x7591,0x7627,0x760d,0x760b,0x7609,0x7613,0x76e1,
+ 0x76e3,0x7784,0x777d,0x777f,0x7761,0x78c1,0x789f,0x78a7,0x78b3,0x78a9,
+ 0x78a3,0x798e,0x798f,0x798d
+ },
+ { /* ku 4a */
+ 0x7a2e,0x7a31,0x7aaa,0x7aa9,0x7aed,0x7aef,0x7ba1,0x7b95,0x7b8b,0x7b75,
+ 0x7b97,0x7b9d,0x7b94,0x7b8f,0x7bb8,0x7b87,0x7b84,0x7cb9,0x7cbd,0x7cbe,
+ 0x7dbb,0x7db0,0x7d9c,0x7dbd,0x7dbe,0x7da0,0x7dca,0x7db4,0x7db2,0x7db1,
+ 0x7dba,0x7da2,0x7dbf,0x7db5,0x7db8,0x7dad,0x7dd2,0x7dc7,0x7dac,0x7f70,
+ 0x7fe0,0x7fe1,0x7fdf,0x805e,0x805a,0x8087,0x8150,0x8180,0x818f,0x8188,
+ 0x818a,0x817f,0x8182,0x81e7,0x81fa,0x8207,0x8214,0x821e,0x824b,0x84c9,
+ 0x84bf,0x84c6,0x84c4,0x8499,0x849e,0x84b2,0x849c,0x84cb,0x84b8,0x84c0,
+ 0x84d3,0x8490,0x84bc,0x84d1,0x84ca,0x873f,0x871c,0x873b,0x8722,0x8725,
+ 0x8734,0x8718,0x8755,0x8737,0x8729,0x88f3,0x8902,0x88f4,0x88f9,0x88f8,
+ 0x88fd,0x88e8,0x891a,0x88ef
+ },
+ { /* ku 4b */
+ 0x8aa6,0x8a8c,0x8a9e,0x8aa3,0x8a8d,0x8aa1,0x8a93,0x8aa4,0x8aaa,0x8aa5,
+ 0x8aa8,0x8a98,0x8a91,0x8a9a,0x8aa7,0x8c6a,0x8c8d,0x8c8c,0x8cd3,0x8cd1,
+ 0x8cd2,0x8d6b,0x8d99,0x8d95,0x8dfc,0x8f14,0x8f12,0x8f15,0x8f13,0x8fa3,
+ 0x9060,0x9058,0x905c,0x9063,0x9059,0x905e,0x9062,0x905d,0x905b,0x9119,
+ 0x9118,0x911e,0x9175,0x9178,0x9177,0x9174,0x9278,0x92ac,0x9280,0x9285,
+ 0x9298,0x9296,0x927b,0x9293,0x929c,0x92a8,0x927c,0x9291,0x95a1,0x95a8,
+ 0x95a9,0x95a3,0x95a5,0x95a4,0x9699,0x969c,0x969b,0x96cc,0x96d2,0x9700,
+ 0x977c,0x9785,0x97f6,0x9817,0x9818,0x98af,0x98b1,0x9903,0x9905,0x990c,
+ 0x9909,0x99c1,0x9aaf,0x9ab0,0x9ae6,0x9b41,0x9b42,0x9cf4,0x9cf6,0x9cf3,
+ 0x9ebc,0x9f3b,0x9f4a,0x5104
+ },
+ { /* ku 4c */
+ 0x5100,0x50fb,0x50f5,0x50f9,0x5102,0x5108,0x5109,0x5105,0x51dc,0x5287,
+ 0x5288,0x5289,0x528d,0x528a,0x52f0,0x53b2,0x562e,0x563b,0x5639,0x5632,
+ 0x563f,0x5634,0x5629,0x5653,0x564e,0x5657,0x5674,0x5636,0x562f,0x5630,
+ 0x5880,0x589f,0x589e,0x58b3,0x589c,0x58ae,0x58a9,0x58a6,0x596d,0x5b09,
+ 0x5afb,0x5b0b,0x5af5,0x5b0c,0x5b08,0x5bee,0x5bec,0x5be9,0x5beb,0x5c64,
+ 0x5c65,0x5d9d,0x5d94,0x5e62,0x5e5f,0x5e61,0x5ee2,0x5eda,0x5edf,0x5edd,
+ 0x5ee3,0x5ee0,0x5f48,0x5f71,0x5fb7,0x5fb5,0x6176,0x6167,0x616e,0x615d,
+ 0x6155,0x6182,0x617c,0x6170,0x616b,0x617e,0x61a7,0x6190,0x61ab,0x618e,
+ 0x61ac,0x619a,0x61a4,0x6194,0x61ae,0x622e,0x6469,0x646f,0x6479,0x649e,
+ 0x64b2,0x6488,0x6490,0x64b0
+ },
+ { /* ku 4d */
+ 0x64a5,0x6493,0x6495,0x64a9,0x6492,0x64ae,0x64ad,0x64ab,0x649a,0x64ac,
+ 0x6499,0x64a2,0x64b3,0x6575,0x6577,0x6578,0x66ae,0x66ab,0x66b4,0x66b1,
+ 0x6a23,0x6a1f,0x69e8,0x6a01,0x6a1e,0x6a19,0x69fd,0x6a21,0x6a13,0x6a0a,
+ 0x69f3,0x6a02,0x6a05,0x69ed,0x6a11,0x6b50,0x6b4e,0x6ba4,0x6bc5,0x6bc6,
+ 0x6f3f,0x6f7c,0x6f84,0x6f51,0x6f66,0x6f54,0x6f86,0x6f6d,0x6f5b,0x6f78,
+ 0x6f6e,0x6f8e,0x6f7a,0x6f70,0x6f64,0x6f97,0x6f58,0x6ed5,0x6f6f,0x6f60,
+ 0x6f5f,0x719f,0x71ac,0x71b1,0x71a8,0x7256,0x729b,0x734e,0x7357,0x7469,
+ 0x748b,0x7483,0x747e,0x7480,0x757f,0x7620,0x7629,0x761f,0x7624,0x7626,
+ 0x7621,0x7622,0x769a,0x76ba,0x76e4,0x778e,0x7787,0x778c,0x7791,0x778b,
+ 0x78cb,0x78c5,0x78ba,0x78ca
+ },
+ { /* ku 4e */
+ 0x78be,0x78d5,0x78bc,0x78d0,0x7a3f,0x7a3c,0x7a40,0x7a3d,0x7a37,0x7a3b,
+ 0x7aaf,0x7aae,0x7bad,0x7bb1,0x7bc4,0x7bb4,0x7bc6,0x7bc7,0x7bc1,0x7ba0,
+ 0x7bcc,0x7cca,0x7de0,0x7df4,0x7def,0x7dfb,0x7dd8,0x7dec,0x7ddd,0x7de8,
+ 0x7de3,0x7dda,0x7dde,0x7de9,0x7d9e,0x7dd9,0x7df2,0x7df9,0x7f75,0x7f77,
+ 0x7faf,0x7fe9,0x8026,0x819b,0x819c,0x819d,0x81a0,0x819a,0x8198,0x8517,
+ 0x853d,0x851a,0x84ee,0x852c,0x852d,0x8513,0x8511,0x8523,0x8521,0x8514,
+ 0x84ec,0x8525,0x84ff,0x8506,0x8782,0x8774,0x8776,0x8760,0x8766,0x8778,
+ 0x8768,0x8759,0x8757,0x874c,0x8753,0x885b,0x885d,0x8910,0x8907,0x8912,
+ 0x8913,0x8915,0x890a,0x8abc,0x8ad2,0x8ac7,0x8ac4,0x8a95,0x8acb,0x8af8,
+ 0x8ab2,0x8ac9,0x8ac2,0x8abf
+ },
+ { /* ku 4f */
+ 0x8ab0,0x8ad6,0x8acd,0x8ab6,0x8ab9,0x8adb,0x8c4c,0x8c4e,0x8c6c,0x8ce0,
+ 0x8cde,0x8ce6,0x8ce4,0x8cec,0x8ced,0x8ce2,0x8ce3,0x8cdc,0x8cea,0x8ce1,
+ 0x8d6d,0x8d9f,0x8da3,0x8e2b,0x8e10,0x8e1d,0x8e22,0x8e0f,0x8e29,0x8e1f,
+ 0x8e21,0x8e1e,0x8eba,0x8f1d,0x8f1b,0x8f1f,0x8f29,0x8f26,0x8f2a,0x8f1c,
+ 0x8f1e,0x8f25,0x9069,0x906e,0x9068,0x906d,0x9077,0x9130,0x912d,0x9127,
+ 0x9131,0x9187,0x9189,0x918b,0x9183,0x92c5,0x92bb,0x92b7,0x92ea,0x92e4,
+ 0x92c1,0x92b3,0x92bc,0x92d2,0x92c7,0x92f0,0x92b2,0x95ad,0x95b1,0x9704,
+ 0x9706,0x9707,0x9709,0x9760,0x978d,0x978b,0x978f,0x9821,0x982b,0x981c,
+ 0x98b3,0x990a,0x9913,0x9912,0x9918,0x99dd,0x99d0,0x99df,0x99db,0x99d1,
+ 0x99d5,0x99d2,0x99d9,0x9ab7
+ },
+ { /* ku 50 */
+ 0x9aee,0x9aef,0x9b27,0x9b45,0x9b44,0x9b77,0x9b6f,0x9d06,0x9d09,0x9d03,
+ 0x9ea9,0x9ebe,0x9ece,0x58a8,0x9f52,0x5112,0x5118,0x5114,0x5110,0x5115,
+ 0x5180,0x51aa,0x51dd,0x5291,0x5293,0x52f3,0x5659,0x566b,0x5679,0x5669,
+ 0x5664,0x5678,0x566a,0x5668,0x5665,0x5671,0x566f,0x566c,0x5662,0x5676,
+ 0x58c1,0x58be,0x58c7,0x58c5,0x596e,0x5b1d,0x5b34,0x5b78,0x5bf0,0x5c0e,
+ 0x5f4a,0x61b2,0x6191,0x61a9,0x618a,0x61cd,0x61b6,0x61be,0x61ca,0x61c8,
+ 0x6230,0x64c5,0x64c1,0x64cb,0x64bb,0x64bc,0x64da,0x64c4,0x64c7,0x64c2,
+ 0x64cd,0x64bf,0x64d2,0x64d4,0x64be,0x6574,0x66c6,0x66c9,0x66b9,0x66c4,
+ 0x66c7,0x66b8,0x6a3d,0x6a38,0x6a3a,0x6a59,0x6a6b,0x6a58,0x6a39,0x6a44,
+ 0x6a62,0x6a61,0x6a4b,0x6a47
+ },
+ { /* ku 51 */
+ 0x6a35,0x6a5f,0x6a48,0x6b59,0x6b77,0x6c05,0x6fc2,0x6fb1,0x6fa1,0x6fc3,
+ 0x6fa4,0x6fc1,0x6fa7,0x6fb3,0x6fc0,0x6fb9,0x6fb6,0x6fa6,0x6fa0,0x6fb4,
+ 0x71be,0x71c9,0x71d0,0x71d2,0x71c8,0x71d5,0x71b9,0x71ce,0x71d9,0x71dc,
+ 0x71c3,0x71c4,0x7368,0x749c,0x74a3,0x7498,0x749f,0x749e,0x74e2,0x750c,
+ 0x750d,0x7634,0x7638,0x763a,0x76e7,0x76e5,0x77a0,0x779e,0x779f,0x77a5,
+ 0x78e8,0x78da,0x78ec,0x78e7,0x79a6,0x7a4d,0x7a4e,0x7a46,0x7a4c,0x7a4b,
+ 0x7aba,0x7bd9,0x7c11,0x7bc9,0x7be4,0x7bdb,0x7be1,0x7be9,0x7be6,0x7cd5,
+ 0x7cd6,0x7e0a,0x7e11,0x7e08,0x7e1b,0x7e23,0x7e1e,0x7e1d,0x7e09,0x7e10,
+ 0x7f79,0x7fb2,0x7ff0,0x7ff1,0x7fee,0x8028,0x81b3,0x81a9,0x81a8,0x81fb,
+ 0x8208,0x8258,0x8259,0x854a
+ },
+ { /* ku 52 */
+ 0x8559,0x8548,0x8568,0x8569,0x8543,0x8549,0x856d,0x856a,0x855e,0x8783,
+ 0x879f,0x879e,0x87a2,0x878d,0x8861,0x892a,0x8932,0x8925,0x892b,0x8921,
+ 0x89aa,0x89a6,0x8ae6,0x8afa,0x8aeb,0x8af1,0x8b00,0x8adc,0x8ae7,0x8aee,
+ 0x8afe,0x8b01,0x8b02,0x8af7,0x8aed,0x8af3,0x8af6,0x8afc,0x8c6b,0x8c6d,
+ 0x8c93,0x8cf4,0x8e44,0x8e31,0x8e34,0x8e42,0x8e39,0x8e35,0x8f3b,0x8f2f,
+ 0x8f38,0x8f33,0x8fa8,0x8fa6,0x9075,0x9074,0x9078,0x9072,0x907c,0x907a,
+ 0x9134,0x9192,0x9320,0x9336,0x92f8,0x9333,0x932f,0x9322,0x92fc,0x932b,
+ 0x9304,0x931a,0x9310,0x9326,0x9321,0x9315,0x932e,0x9319,0x95bb,0x96a7,
+ 0x96a8,0x96aa,0x96d5,0x970e,0x9711,0x9716,0x970d,0x9713,0x970f,0x975b,
+ 0x975c,0x9766,0x9798,0x9830
+ },
+ { /* ku 53 */
+ 0x9838,0x983b,0x9837,0x982d,0x9839,0x9824,0x9910,0x9928,0x991e,0x991b,
+ 0x9921,0x991a,0x99ed,0x99e2,0x99f1,0x9ab8,0x9abc,0x9afb,0x9aed,0x9b28,
+ 0x9b91,0x9d15,0x9d23,0x9d26,0x9d28,0x9d12,0x9d1b,0x9ed8,0x9ed4,0x9f8d,
+ 0x9f9c,0x512a,0x511f,0x5121,0x5132,0x52f5,0x568e,0x5680,0x5690,0x5685,
+ 0x5687,0x568f,0x58d5,0x58d3,0x58d1,0x58ce,0x5b30,0x5b2a,0x5b24,0x5b7a,
+ 0x5c37,0x5c68,0x5dbc,0x5dba,0x5dbd,0x5db8,0x5e6b,0x5f4c,0x5fbd,0x61c9,
+ 0x61c2,0x61c7,0x61e6,0x61cb,0x6232,0x6234,0x64ce,0x64ca,0x64d8,0x64e0,
+ 0x64f0,0x64e6,0x64ec,0x64f1,0x64e2,0x64ed,0x6582,0x6583,0x66d9,0x66d6,
+ 0x6a80,0x6a94,0x6a84,0x6aa2,0x6a9c,0x6adb,0x6aa3,0x6a7e,0x6a97,0x6a90,
+ 0x6aa0,0x6b5c,0x6bae,0x6bda
+ },
+ { /* ku 54 */
+ 0x6c08,0x6fd8,0x6ff1,0x6fdf,0x6fe0,0x6fdb,0x6fe4,0x6feb,0x6fef,0x6f80,
+ 0x6fec,0x6fe1,0x6fe9,0x6fd5,0x6fee,0x6ff0,0x71e7,0x71df,0x71ee,0x71e6,
+ 0x71e5,0x71ed,0x71ec,0x71f4,0x71e0,0x7235,0x7246,0x7370,0x7372,0x74a9,
+ 0x74b0,0x74a6,0x74a8,0x7646,0x7642,0x764c,0x76ea,0x77b3,0x77aa,0x77b0,
+ 0x77ac,0x77a7,0x77ad,0x77ef,0x78f7,0x78fa,0x78f4,0x78ef,0x7901,0x79a7,
+ 0x79aa,0x7a57,0x7abf,0x7c07,0x7c0d,0x7bfe,0x7bf7,0x7c0c,0x7be0,0x7ce0,
+ 0x7cdc,0x7cde,0x7ce2,0x7cdf,0x7cd9,0x7cdd,0x7e2e,0x7e3e,0x7e46,0x7e37,
+ 0x7e32,0x7e43,0x7e2b,0x7e3d,0x7e31,0x7e45,0x7e41,0x7e34,0x7e39,0x7e48,
+ 0x7e35,0x7e3f,0x7e2f,0x7f44,0x7ff3,0x7ffc,0x8071,0x8072,0x8070,0x806f,
+ 0x8073,0x81c6,0x81c3,0x81ba
+ },
+ { /* ku 55 */
+ 0x81c2,0x81c0,0x81bf,0x81bd,0x81c9,0x81be,0x81e8,0x8209,0x8271,0x85aa,
+ 0x8584,0x857e,0x859c,0x8591,0x8594,0x85af,0x859b,0x8587,0x85a8,0x858a,
+ 0x85a6,0x8667,0x87c0,0x87d1,0x87b3,0x87d2,0x87c6,0x87ab,0x87bb,0x87ba,
+ 0x87c8,0x87cb,0x893b,0x8936,0x8944,0x8938,0x893d,0x89ac,0x8b0e,0x8b17,
+ 0x8b19,0x8b1b,0x8b0a,0x8b20,0x8b1d,0x8b04,0x8b10,0x8c41,0x8c3f,0x8c73,
+ 0x8cfa,0x8cfd,0x8cfc,0x8cf8,0x8cfb,0x8da8,0x8e49,0x8e4b,0x8e48,0x8e4a,
+ 0x8f44,0x8f3e,0x8f42,0x8f45,0x8f3f,0x907f,0x907d,0x9084,0x9081,0x9082,
+ 0x9080,0x9139,0x91a3,0x919e,0x919c,0x934d,0x9382,0x9328,0x9375,0x934a,
+ 0x9365,0x934b,0x9318,0x937e,0x936c,0x935b,0x9370,0x935a,0x9354,0x95ca,
+ 0x95cb,0x95cc,0x95c8,0x95c6
+ },
+ { /* ku 56 */
+ 0x96b1,0x96b8,0x96d6,0x971c,0x971e,0x97a0,0x97d3,0x9846,0x98b6,0x9935,
+ 0x9a01,0x99ff,0x9bae,0x9bab,0x9baa,0x9bad,0x9d3b,0x9d3f,0x9e8b,0x9ecf,
+ 0x9ede,0x9edc,0x9edd,0x9edb,0x9f3e,0x9f4b,0x53e2,0x5695,0x56ae,0x58d9,
+ 0x58d8,0x5b38,0x5f5e,0x61e3,0x6233,0x64f4,0x64f2,0x64fe,0x6506,0x64fa,
+ 0x64fb,0x64f7,0x65b7,0x66dc,0x6726,0x6ab3,0x6aac,0x6ac3,0x6abb,0x6ab8,
+ 0x6ac2,0x6aae,0x6aaf,0x6b5f,0x6b78,0x6baf,0x7009,0x700b,0x6ffe,0x7006,
+ 0x6ffa,0x7011,0x700f,0x71fb,0x71fc,0x71fe,0x71f8,0x7377,0x7375,0x74a7,
+ 0x74bf,0x7515,0x7656,0x7658,0x7652,0x77bd,0x77bf,0x77bb,0x77bc,0x790e,
+ 0x79ae,0x7a61,0x7a62,0x7a60,0x7ac4,0x7ac5,0x7c2b,0x7c27,0x7c2a,0x7c1e,
+ 0x7c23,0x7c21,0x7ce7,0x7e54
+ },
+ { /* ku 57 */
+ 0x7e55,0x7e5e,0x7e5a,0x7e61,0x7e52,0x7e59,0x7f48,0x7ff9,0x7ffb,0x8077,
+ 0x8076,0x81cd,0x81cf,0x820a,0x85cf,0x85a9,0x85cd,0x85d0,0x85c9,0x85b0,
+ 0x85ba,0x85b9,0x87ef,0x87ec,0x87f2,0x87e0,0x8986,0x89b2,0x89f4,0x8b28,
+ 0x8b39,0x8b2c,0x8b2b,0x8c50,0x8d05,0x8e59,0x8e63,0x8e66,0x8e64,0x8e5f,
+ 0x8e55,0x8ec0,0x8f49,0x8f4d,0x9087,0x9083,0x9088,0x91ab,0x91ac,0x91d0,
+ 0x9394,0x938a,0x9396,0x93a2,0x93b3,0x93ae,0x93ac,0x93b0,0x9398,0x939a,
+ 0x9397,0x95d4,0x95d6,0x95d0,0x95d5,0x96e2,0x96dc,0x96d9,0x96db,0x96de,
+ 0x9724,0x97a3,0x97a6,0x97ad,0x97f9,0x984d,0x984f,0x984c,0x984e,0x9853,
+ 0x98ba,0x993e,0x993f,0x993d,0x992e,0x99a5,0x9a0e,0x9ac1,0x9b03,0x9b06,
+ 0x9b4f,0x9b4e,0x9b4d,0x9bca
+ },
+ { /* ku 58 */
+ 0x9bc9,0x9bfd,0x9bc8,0x9bc0,0x9d51,0x9d5d,0x9d60,0x9ee0,0x9f15,0x9f2c,
+ 0x5133,0x56a5,0x56a8,0x58de,0x58df,0x58e2,0x5bf5,0x9f90,0x5eec,0x61f2,
+ 0x61f7,0x61f6,0x61f5,0x6500,0x650f,0x66e0,0x66dd,0x6ae5,0x6add,0x6ada,
+ 0x6ad3,0x701b,0x701f,0x7028,0x701a,0x701d,0x7015,0x7018,0x7206,0x720d,
+ 0x7258,0x72a2,0x7378,0x737a,0x74bd,0x74ca,0x74e3,0x7587,0x7586,0x765f,
+ 0x7661,0x77c7,0x7919,0x79b1,0x7a6b,0x7a69,0x7c3e,0x7c3f,0x7c38,0x7c3d,
+ 0x7c37,0x7c40,0x7e6b,0x7e6d,0x7e79,0x7e69,0x7e6a,0x7e73,0x7f85,0x7fb6,
+ 0x7fb9,0x7fb8,0x81d8,0x85e9,0x85dd,0x85ea,0x85d5,0x85e4,0x85e5,0x85f7,
+ 0x87fb,0x8805,0x880d,0x87f9,0x87fe,0x8960,0x895f,0x8956,0x895e,0x8b41,
+ 0x8b5c,0x8b58,0x8b49,0x8b5a
+ },
+ { /* ku 59 */
+ 0x8b4e,0x8b4f,0x8b46,0x8b59,0x8d08,0x8d0a,0x8e7c,0x8e72,0x8e87,0x8e76,
+ 0x8e6c,0x8e7a,0x8e74,0x8f54,0x8f4e,0x8fad,0x908a,0x908b,0x91b1,0x91ae,
+ 0x93e1,0x93d1,0x93df,0x93c3,0x93c8,0x93dc,0x93dd,0x93d6,0x93e2,0x93cd,
+ 0x93d8,0x93e4,0x93d7,0x93e8,0x95dc,0x96b4,0x96e3,0x972a,0x9727,0x9761,
+ 0x97dc,0x97fb,0x985e,0x9858,0x985b,0x98bc,0x9945,0x9949,0x9a16,0x9a19,
+ 0x9b0d,0x9be8,0x9be7,0x9bd6,0x9bdb,0x9d89,0x9d61,0x9d72,0x9d6a,0x9d6c,
+ 0x9e92,0x9e97,0x9e93,0x9eb4,0x52f8,0x56b7,0x56b6,0x56b4,0x56bc,0x58e4,
+ 0x5b40,0x5b43,0x5b7d,0x5bf6,0x5dc9,0x61f8,0x61fa,0x6518,0x6514,0x6519,
+ 0x66e6,0x6727,0x6aec,0x703e,0x7030,0x7032,0x7210,0x737b,0x74cf,0x7662,
+ 0x7665,0x7926,0x792a,0x792c
+ },
+ { /* ku 5a */
+ 0x792b,0x7ac7,0x7af6,0x7c4c,0x7c43,0x7c4d,0x7cef,0x7cf0,0x8fae,0x7e7d,
+ 0x7e7c,0x7e82,0x7f4c,0x8000,0x81da,0x8266,0x85fb,0x85f9,0x8611,0x85fa,
+ 0x8606,0x860b,0x8607,0x860a,0x8814,0x8815,0x8964,0x89ba,0x89f8,0x8b70,
+ 0x8b6c,0x8b66,0x8b6f,0x8b5f,0x8b6b,0x8d0f,0x8d0d,0x8e89,0x8e81,0x8e85,
+ 0x8e82,0x91b4,0x91cb,0x9418,0x9403,0x93fd,0x95e1,0x9730,0x98c4,0x9952,
+ 0x9951,0x99a8,0x9a2b,0x9a30,0x9a37,0x9a35,0x9c13,0x9c0d,0x9e79,0x9eb5,
+ 0x9ee8,0x9f2f,0x9f5f,0x9f63,0x9f61,0x5137,0x5138,0x56c1,0x56c0,0x56c2,
+ 0x5914,0x5c6c,0x5dcd,0x61fc,0x61fe,0x651d,0x651c,0x6595,0x66e9,0x6afb,
+ 0x6b04,0x6afa,0x6bb2,0x704c,0x721b,0x72a7,0x74d6,0x74d4,0x7669,0x77d3,
+ 0x7c50,0x7e8f,0x7e8c,0x7fbc
+ },
+ { /* ku 5b */
+ 0x8617,0x862d,0x861a,0x8823,0x8822,0x8821,0x881f,0x896a,0x896c,0x89bd,
+ 0x8b74,0x8b77,0x8b7d,0x8d13,0x8e8a,0x8e8d,0x8e8b,0x8f5f,0x8faf,0x91ba,
+ 0x942e,0x9433,0x9435,0x943a,0x9438,0x9432,0x942b,0x95e2,0x9738,0x9739,
+ 0x9732,0x97ff,0x9867,0x9865,0x9957,0x9a45,0x9a43,0x9a40,0x9a3e,0x9acf,
+ 0x9b54,0x9b51,0x9c2d,0x9c25,0x9daf,0x9db4,0x9dc2,0x9db8,0x9e9d,0x9eef,
+ 0x9f19,0x9f5c,0x9f66,0x9f67,0x513c,0x513b,0x56c8,0x56ca,0x56c9,0x5b7f,
+ 0x5dd4,0x5dd2,0x5f4e,0x61ff,0x6524,0x6b0a,0x6b61,0x7051,0x7058,0x7380,
+ 0x74e4,0x758a,0x766e,0x766c,0x79b3,0x7c60,0x7c5f,0x807e,0x807d,0x81df,
+ 0x8972,0x896f,0x89fc,0x8b80,0x8d16,0x8d17,0x8e91,0x8e93,0x8f61,0x9148,
+ 0x9444,0x9451,0x9452,0x973d
+ },
+ { /* ku 5c */
+ 0x973e,0x97c3,0x97c1,0x986b,0x9955,0x9a55,0x9a4d,0x9ad2,0x9b1a,0x9c49,
+ 0x9c31,0x9c3e,0x9c3b,0x9dd3,0x9dd7,0x9f34,0x9f6c,0x9f6a,0x9f94,0x56cc,
+ 0x5dd6,0x6200,0x6523,0x652b,0x652a,0x66ec,0x6b10,0x74da,0x7aca,0x7c64,
+ 0x7c63,0x7c65,0x7e93,0x7e96,0x7e94,0x81e2,0x8638,0x863f,0x8831,0x8b8a,
+ 0x9090,0x908f,0x9463,0x9460,0x9464,0x9768,0x986f,0x995c,0x9a5a,0x9a5b,
+ 0x9a57,0x9ad3,0x9ad4,0x9ad1,0x9c54,0x9c57,0x9c56,0x9de5,0x9e9f,0x9ef4,
+ 0x56d1,0x58e9,0x652c,0x705e,0x7671,0x7672,0x77d7,0x7f50,0x7f88,0x8836,
+ 0x8839,0x8862,0x8b93,0x8b92,0x8b96,0x8277,0x8d1b,0x91c0,0x946a,0x9742,
+ 0x9748,0x9744,0x97c6,0x9870,0x9a5f,0x9b22,0x9b58,0x9c5f,0x9df9,0x9dfa,
+ 0x9e7c,0x9e7d,0x9f07,0x9f77
+ },
+ { /* ku 5d */
+ 0x9f72,0x5ef3,0x6b16,0x7063,0x7c6c,0x7c6e,0x883b,0x89c0,0x8ea1,0x91c1,
+ 0x9472,0x9470,0x9871,0x995e,0x9ad6,0x9b23,0x9ecc,0x7064,0x77da,0x8b9a,
+ 0x9477,0x97c9,0x9a62,0x9a65,0x7e9c,0x8b9c,0x8eaa,0x91c5,0x947d,0x947e,
+ 0x947c,0x9c77,0x9c78,0x9ef7,0x8c54,0x947f,0x9e1a,0x7228,0x9a6a,0x9b31,
+ 0x9e1b,0x9e1e,0x7c72,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ }
+};
+
+/* CNS 11643 plane 2 conversion table */
+
+static const unsigned short
+ cns11643_2tab[MAX_CNS11643_KU_2][MAX_CNS11643_TEN] = {
+ { /* ku 01 */
+ 0x4e42,0x4e5c,0x51f5,0x531a,0x5382,0x4e07,0x4e0c,0x4e47,0x4e8d,0x56d7,
+ 0x5c6e,0x5f73,0x4e0f,0x5187,0x4e0e,0x4e2e,0x4e93,0x4ec2,0x4ec9,0x4ec8,
+ 0x5198,0x52fc,0x536c,0x53b9,0x5720,0x5903,0x592c,0x5c10,0x5dff,0x65e1,
+ 0x6bb3,0x6bcc,0x6c14,0x723f,0x4e31,0x4e3c,0x4ee8,0x4edc,0x4ee9,0x4ee1,
+ 0x4edd,0x4eda,0x520c,0x5209,0x531c,0x534c,0x5722,0x5723,0x5917,0x592f,
+ 0x5b81,0x5b84,0x5c12,0x5c3b,0x5c74,0x5c73,0x5e04,0x5e80,0x5e82,0x5fc9,
+ 0x6209,0x6250,0x6c15,0x6c36,0x6c43,0x6c3f,0x6c3b,0x72ae,0x72b0,0x738a,
+ 0x79b8,0x808a,0x961e,0x4f0e,0x4f18,0x4f2c,0x4ef5,0x4f14,0x4ef1,0x4f00,
+ 0x4ef7,0x4f08,0x4f1d,0x4f02,0x4f05,0x4f22,0x4f13,0x4f04,0x4ef4,0x4f12,
+ 0x51b1,0x5213,0x5210,0x52a6
+ },
+ { /* ku 02 */
+ 0x5322,0x531f,0x534d,0x538a,0x5407,0x56e1,0x56df,0x572e,0x572a,0x5734,
+ 0x593c,0x5980,0x597c,0x5985,0x597b,0x597e,0x5977,0x597f,0x5b56,0x5c15,
+ 0x5c25,0x5c7c,0x5c7a,0x5c7b,0x5c7e,0x5ddf,0x5e75,0x5e84,0x5f02,0x5f1a,
+ 0x5f74,0x5fd5,0x5fd4,0x5fcf,0x625c,0x625e,0x6264,0x6261,0x6266,0x6262,
+ 0x6259,0x6260,0x625a,0x6265,0x6537,0x65ef,0x65ee,0x673e,0x6739,0x6738,
+ 0x673b,0x673a,0x673f,0x673c,0x6733,0x6c18,0x6c46,0x6c52,0x6c5c,0x6c4f,
+ 0x6c4a,0x6c54,0x6c4b,0x6c4c,0x7071,0x725e,0x72b4,0x72b5,0x738e,0x752a,
+ 0x767f,0x7a75,0x7f51,0x8278,0x827c,0x8280,0x827d,0x827f,0x864d,0x897e,
+ 0x9099,0x9097,0x9098,0x909b,0x9094,0x9622,0x9624,0x9620,0x9623,0x4f56,
+ 0x4f3b,0x4f62,0x4f49,0x4f53
+ },
+ { /* ku 03 */
+ 0x4f64,0x4f3e,0x4f67,0x4f52,0x4f5f,0x4f41,0x4f58,0x4f2d,0x4f33,0x4f3f,
+ 0x4f61,0x518f,0x51b9,0x521c,0x521e,0x5221,0x52ad,0x52ae,0x5309,0x5363,
+ 0x5372,0x538e,0x538f,0x5430,0x5437,0x542a,0x5454,0x5445,0x5419,0x541c,
+ 0x5425,0x5418,0x543d,0x544f,0x5441,0x5428,0x5424,0x5447,0x56ee,0x56e7,
+ 0x56e5,0x5741,0x5745,0x574c,0x5749,0x574b,0x5752,0x5906,0x5940,0x59a6,
+ 0x5998,0x59a0,0x5997,0x598e,0x59a2,0x5990,0x598f,0x59a7,0x59a1,0x5b8e,
+ 0x5b92,0x5c28,0x5c2a,0x5c8d,0x5c8f,0x5c88,0x5c8b,0x5c89,0x5c92,0x5c8a,
+ 0x5c86,0x5c93,0x5c95,0x5de0,0x5e0a,0x5e0e,0x5e8b,0x5e89,0x5e8c,0x5e88,
+ 0x5e8d,0x5f05,0x5f1d,0x5f78,0x5f76,0x5fd2,0x5fd1,0x5fd0,0x5fed,0x5fe8,
+ 0x5fee,0x5ff3,0x5fe1,0x5fe4
+ },
+ { /* ku 04 */
+ 0x5fe3,0x5ffa,0x5fef,0x5ff7,0x5ffb,0x6000,0x5ff4,0x623a,0x6283,0x628c,
+ 0x628e,0x628f,0x6294,0x6287,0x6271,0x627b,0x627a,0x6270,0x6281,0x6288,
+ 0x6277,0x627d,0x6272,0x6274,0x65f0,0x65f4,0x65f3,0x65f2,0x65f5,0x6745,
+ 0x6747,0x6759,0x6755,0x674c,0x6748,0x675d,0x674d,0x675a,0x674b,0x6bd0,
+ 0x6c19,0x6c1a,0x6c78,0x6c67,0x6c6b,0x6c84,0x6c8b,0x6c8f,0x6c71,0x6c6f,
+ 0x6c69,0x6c9a,0x6c6d,0x6c87,0x6c95,0x6c9c,0x6c66,0x6c73,0x6c65,0x6c7b,
+ 0x6c8e,0x7074,0x707a,0x7263,0x72bf,0x72bd,0x72c3,0x72c6,0x72c1,0x72ba,
+ 0x72c5,0x7395,0x7397,0x7393,0x7394,0x7392,0x753a,0x7539,0x7594,0x7595,
+ 0x7681,0x793d,0x8034,0x8095,0x8099,0x8090,0x8092,0x809c,0x8290,0x828f,
+ 0x8285,0x828e,0x8291,0x8293
+ },
+ { /* ku 05 */
+ 0x828a,0x8283,0x8284,0x8c78,0x8fc9,0x8fbf,0x909f,0x90a1,0x90a5,0x909e,
+ 0x90a7,0x90a0,0x9630,0x9628,0x962f,0x962d,0x4e33,0x4f98,0x4f7c,0x4f85,
+ 0x4f7d,0x4f80,0x4f87,0x4f76,0x4f74,0x4f89,0x4f84,0x4f77,0x4f4c,0x4f97,
+ 0x4f6a,0x4f9a,0x4f79,0x4f81,0x4f78,0x4f90,0x4f9c,0x4f94,0x4f9e,0x4f92,
+ 0x4f82,0x4f95,0x4f6b,0x4f6e,0x519e,0x51bc,0x51be,0x5235,0x5232,0x5233,
+ 0x5246,0x5231,0x52bc,0x530a,0x530b,0x533c,0x5392,0x5394,0x5487,0x547f,
+ 0x5481,0x5491,0x5482,0x5488,0x546b,0x547a,0x547e,0x5465,0x546c,0x5474,
+ 0x5466,0x548d,0x546f,0x5461,0x5460,0x5498,0x5463,0x5467,0x5464,0x56f7,
+ 0x56f9,0x576f,0x5772,0x576d,0x576b,0x5771,0x5770,0x5776,0x5780,0x5775,
+ 0x577b,0x5773,0x5774,0x5762
+ },
+ { /* ku 06 */
+ 0x5768,0x577d,0x590c,0x5945,0x59b5,0x59ba,0x59cf,0x59ce,0x59b2,0x59cc,
+ 0x59c1,0x59b6,0x59bc,0x59c3,0x59d6,0x59b1,0x59bd,0x59c0,0x59c8,0x59b4,
+ 0x59c7,0x5b62,0x5b65,0x5b93,0x5b95,0x5c44,0x5c47,0x5cae,0x5ca4,0x5ca0,
+ 0x5cb5,0x5caf,0x5ca8,0x5cac,0x5c9f,0x5ca3,0x5cad,0x5ca2,0x5caa,0x5ca7,
+ 0x5c9d,0x5ca5,0x5cb6,0x5cb0,0x5ca6,0x5e17,0x5e14,0x5e19,0x5f28,0x5f22,
+ 0x5f23,0x5f24,0x5f54,0x5f82,0x5f7e,0x5f7d,0x5fde,0x5fe5,0x602d,0x6026,
+ 0x6019,0x6032,0x600b,0x6034,0x600a,0x6017,0x6033,0x601a,0x601e,0x602c,
+ 0x6022,0x600d,0x6010,0x602e,0x6013,0x6011,0x600c,0x6009,0x601c,0x6214,
+ 0x623d,0x62ad,0x62b4,0x62d1,0x62be,0x62aa,0x62b6,0x62ca,0x62ae,0x62b3,
+ 0x62af,0x62bb,0x62a9,0x62b0
+ },
+ { /* ku 07 */
+ 0x62b8,0x653d,0x65a8,0x65bb,0x6609,0x65fc,0x6604,0x6612,0x6608,0x65fb,
+ 0x6603,0x660b,0x660d,0x6605,0x65fd,0x6611,0x6610,0x66f6,0x670a,0x6785,
+ 0x676c,0x678e,0x6792,0x6776,0x677b,0x6798,0x6786,0x6784,0x6774,0x678d,
+ 0x678c,0x677a,0x679f,0x6791,0x6799,0x6783,0x677d,0x6781,0x6778,0x6779,
+ 0x6794,0x6b25,0x6b80,0x6b7e,0x6bde,0x6c1d,0x6c93,0x6cec,0x6ceb,0x6cee,
+ 0x6cd9,0x6cb6,0x6cd4,0x6cad,0x6ce7,0x6cb7,0x6cd0,0x6cc2,0x6cba,0x6cc3,
+ 0x6cc6,0x6ced,0x6cf2,0x6cd2,0x6cdd,0x6cb4,0x6c8a,0x6c9d,0x6c80,0x6cde,
+ 0x6cc0,0x6d30,0x6ccd,0x6cc7,0x6cb0,0x6cf9,0x6ccf,0x6ce9,0x6cd1,0x7094,
+ 0x7098,0x7085,0x7093,0x7086,0x7084,0x7091,0x7096,0x7082,0x709a,0x7083,
+ 0x726a,0x72d6,0x72cb,0x72d8
+ },
+ { /* ku 08 */
+ 0x72c9,0x72dc,0x72d2,0x72d4,0x72da,0x72cc,0x72d1,0x73a4,0x73a1,0x73ad,
+ 0x73a6,0x73a2,0x73a0,0x73ac,0x739d,0x74dd,0x74e8,0x753f,0x7540,0x753e,
+ 0x758c,0x7598,0x76af,0x76f3,0x76f1,0x76f0,0x76f5,0x77f8,0x77fc,0x77f9,
+ 0x77fb,0x77fa,0x77f7,0x7942,0x793f,0x79c5,0x7a78,0x7a7b,0x7afb,0x7c75,
+ 0x7cfd,0x8035,0x808f,0x80ae,0x80a3,0x80b8,0x80b5,0x80ad,0x8220,0x82a0,
+ 0x82c0,0x82ab,0x829a,0x8298,0x829b,0x82b5,0x82a7,0x82ae,0x82bc,0x829e,
+ 0x82ba,0x82b4,0x82a8,0x82a1,0x82a9,0x82c2,0x82a4,0x82c3,0x82b6,0x82a2,
+ 0x8670,0x866f,0x866d,0x866e,0x8c56,0x8fd2,0x8fcb,0x8fd3,0x8fcd,0x8fd6,
+ 0x8fd5,0x8fd7,0x90b2,0x90b4,0x90af,0x90b3,0x90b0,0x9639,0x963d,0x963c,
+ 0x963a,0x9643,0x4fcd,0x4fc5
+ },
+ { /* ku 09 */
+ 0x4fd3,0x4fb2,0x4fc9,0x4fcb,0x4fc1,0x4fd4,0x4fdc,0x4fd9,0x4fbb,0x4fb3,
+ 0x4fdb,0x4fc7,0x4fd6,0x4fba,0x4fc0,0x4fb9,0x4fec,0x5244,0x5249,0x52c0,
+ 0x52c2,0x533d,0x537c,0x5397,0x5396,0x5399,0x5398,0x54ba,0x54a1,0x54ad,
+ 0x54a5,0x54cf,0x54c3,0x830d,0x54b7,0x54ae,0x54d6,0x54b6,0x54c5,0x54c6,
+ 0x54a0,0x5470,0x54bc,0x54a2,0x54be,0x5472,0x54de,0x54b0,0x57b5,0x579e,
+ 0x579f,0x57a4,0x578c,0x5797,0x579d,0x579b,0x5794,0x5798,0x578f,0x5799,
+ 0x57a5,0x579a,0x5795,0x58f4,0x590d,0x5953,0x59e1,0x59de,0x59ee,0x5a00,
+ 0x59f1,0x59dd,0x59fa,0x59fd,0x59fc,0x59f6,0x59e4,0x59f2,0x59f7,0x59db,
+ 0x59e9,0x59f3,0x59f5,0x59e0,0x59fe,0x59f4,0x59ed,0x5ba8,0x5c4c,0x5cd0,
+ 0x5cd8,0x5ccc,0x5cd7,0x5ccb
+ },
+ { /* ku 0a */
+ 0x5cdb,0x5cde,0x5cda,0x5cc9,0x5cc7,0x5cca,0x5cd6,0x5cd3,0x5cd4,0x5ccf,
+ 0x5cc8,0x5cc6,0x5cce,0x5cdf,0x5cf8,0x5df9,0x5e21,0x5e22,0x5e23,0x5e20,
+ 0x5e24,0x5eb0,0x5ea4,0x5ea2,0x5e9b,0x5ea3,0x5ea5,0x5f07,0x5f2e,0x5f56,
+ 0x5f86,0x6037,0x6039,0x6054,0x6072,0x605e,0x6045,0x6053,0x6047,0x6049,
+ 0x605b,0x604c,0x6040,0x6042,0x605f,0x6024,0x6044,0x6058,0x6066,0x606e,
+ 0x6242,0x6243,0x62cf,0x630d,0x630b,0x62f5,0x630e,0x6303,0x62eb,0x62f9,
+ 0x630f,0x630c,0x62f8,0x62f6,0x6300,0x6313,0x6314,0x62fa,0x6315,0x62fb,
+ 0x62f0,0x6541,0x6543,0x65aa,0x65bf,0x6636,0x6621,0x6632,0x6635,0x661c,
+ 0x6626,0x6622,0x6633,0x662b,0x663a,0x661d,0x6634,0x6639,0x662e,0x670f,
+ 0x6710,0x67c1,0x67f2,0x67c8
+ },
+ { /* ku 0b */
+ 0x67ba,0x67dc,0x67bb,0x67f8,0x67d8,0x67c0,0x67b7,0x67c5,0x67eb,0x67e4,
+ 0x67df,0x67b5,0x67cd,0x67b3,0x67f7,0x67f6,0x67ee,0x67e3,0x67c2,0x67b9,
+ 0x67ce,0x67e7,0x67f0,0x67b2,0x67fc,0x67c6,0x67ed,0x67cc,0x67ae,0x67e6,
+ 0x67db,0x67fa,0x67c9,0x67ca,0x67c3,0x67ea,0x67cb,0x6b28,0x6b82,0x6b84,
+ 0x6bb6,0x6bd6,0x6bd8,0x6be0,0x6c20,0x6c21,0x6d28,0x6d34,0x6d2d,0x6d1f,
+ 0x6d3c,0x6d3f,0x6d12,0x6d0a,0x6cda,0x6d33,0x6d04,0x6d19,0x6d3a,0x6d1a,
+ 0x6d11,0x6d00,0x6d1d,0x6d42,0x6d01,0x6d18,0x6d37,0x6d03,0x6d0f,0x6d40,
+ 0x6d07,0x6d20,0x6d2c,0x6d08,0x6d22,0x6d09,0x6d10,0x70b7,0x709f,0x70be,
+ 0x70b1,0x70b0,0x70a1,0x70b4,0x70b5,0x70a9,0x7241,0x7249,0x724a,0x726c,
+ 0x7270,0x7273,0x726e,0x72ca
+ },
+ { /* ku 0c */
+ 0x72e4,0x72e8,0x72eb,0x72df,0x72ea,0x72e6,0x72e3,0x7385,0x73cc,0x73c2,
+ 0x73c8,0x73c5,0x73b9,0x73b6,0x73b5,0x73b4,0x73eb,0x73bf,0x73c7,0x73be,
+ 0x73c3,0x73c6,0x73b8,0x73cb,0x74ec,0x74ee,0x752e,0x7547,0x7548,0x75a7,
+ 0x75aa,0x7679,0x76c4,0x7708,0x7703,0x7704,0x7705,0x770a,0x76f7,0x76fb,
+ 0x76fa,0x77e7,0x77e8,0x7806,0x7811,0x7812,0x7805,0x7810,0x780f,0x780e,
+ 0x7809,0x7803,0x7813,0x794a,0x794c,0x794b,0x7945,0x7944,0x79d5,0x79cd,
+ 0x79cf,0x79d6,0x79ce,0x7a80,0x7a7e,0x7ad1,0x7b00,0x7b01,0x7c7a,0x7c78,
+ 0x7c79,0x7c7f,0x7c80,0x7c81,0x7d03,0x7d08,0x7d01,0x7f58,0x7f91,0x7f8d,
+ 0x7fbe,0x8007,0x800e,0x800f,0x8014,0x8037,0x80d8,0x80c7,0x80e0,0x80d1,
+ 0x80c8,0x80c2,0x80d0,0x80c5
+ },
+ { /* ku 0d */
+ 0x80e3,0x80d9,0x80dc,0x80ca,0x80d5,0x80c9,0x80cf,0x80d7,0x80e6,0x80cd,
+ 0x81ff,0x8221,0x8294,0x82d9,0x82fe,0x82f9,0x8307,0x82e8,0x8300,0x82d5,
+ 0x833a,0x82eb,0x82d6,0x82f4,0x82ec,0x82e1,0x82f2,0x82f5,0x830c,0x82fb,
+ 0x82f6,0x82f0,0x82ea,0x82e4,0x82e0,0x82fa,0x82f3,0x82ed,0x8677,0x8674,
+ 0x867c,0x8673,0x8841,0x884e,0x8867,0x886a,0x8869,0x89d3,0x8a04,0x8a07,
+ 0x8d72,0x8fe3,0x8fe1,0x8fee,0x8fe0,0x90f1,0x90bd,0x90bf,0x90d5,0x90c5,
+ 0x90be,0x90c7,0x90cb,0x90c8,0x91d4,0x91d3,0x9654,0x964f,0x9651,0x9653,
+ 0x964a,0x964e,0x501e,0x5005,0x5007,0x5013,0x5022,0x5030,0x501b,0x4ff5,
+ 0x4ff4,0x5033,0x5037,0x502c,0x4ff6,0x4ff7,0x5017,0x501c,0x5020,0x5027,
+ 0x5035,0x502f,0x5031,0x500e
+ },
+ { /* ku 0e */
+ 0x515a,0x5194,0x5193,0x51ca,0x51c4,0x51c5,0x51c8,0x51ce,0x5261,0x525a,
+ 0x5252,0x525e,0x525f,0x5255,0x5262,0x52cd,0x530e,0x539e,0x5526,0x54e2,
+ 0x5517,0x5512,0x54e7,0x54f3,0x54e4,0x551a,0x54ff,0x5504,0x5508,0x54eb,
+ 0x5511,0x5505,0x54f1,0x550a,0x54fb,0x54f7,0x54f8,0x54e0,0x550e,0x5503,
+ 0x550b,0x5701,0x5702,0x57cc,0x5832,0x57d5,0x57d2,0x57ba,0x57c6,0x57bd,
+ 0x57bc,0x57b8,0x57b6,0x57bf,0x57c7,0x57d0,0x57b9,0x57c1,0x590e,0x594a,
+ 0x5a19,0x5a16,0x5a2d,0x5a2e,0x5a15,0x5a0f,0x5a17,0x5a0a,0x5a1e,0x5a33,
+ 0x5b6c,0x5ba7,0x5bad,0x5bac,0x5c03,0x5c56,0x5c54,0x5cec,0x5cff,0x5cee,
+ 0x5cf1,0x5cf7,0x5d00,0x5cf9,0x5e29,0x5e28,0x5ea8,0x5eae,0x5eaa,0x5eac,
+ 0x5f33,0x5f30,0x5f67,0x605d
+ },
+ { /* ku 0f */
+ 0x605a,0x6067,0x6041,0x60a2,0x6088,0x6080,0x6092,0x6081,0x609d,0x6083,
+ 0x6095,0x609b,0x6097,0x6087,0x609c,0x608e,0x6219,0x6246,0x62f2,0x6310,
+ 0x6356,0x632c,0x6344,0x6345,0x6336,0x6343,0x63e4,0x6339,0x634b,0x634a,
+ 0x633c,0x6329,0x6341,0x6334,0x6358,0x6354,0x6359,0x632d,0x6347,0x6333,
+ 0x635a,0x6351,0x6338,0x6357,0x6340,0x6348,0x654a,0x6546,0x65c6,0x65c3,
+ 0x65c4,0x65c2,0x664a,0x665f,0x6647,0x6651,0x6712,0x6713,0x681f,0x681a,
+ 0x6849,0x6832,0x6833,0x683b,0x684b,0x684f,0x6816,0x6831,0x681c,0x6835,
+ 0x682b,0x682d,0x682f,0x684e,0x6844,0x6834,0x681d,0x6812,0x6814,0x6826,
+ 0x6828,0x682e,0x684d,0x683a,0x6825,0x6820,0x6b2c,0x6b2f,0x6b2d,0x6b31,
+ 0x6b34,0x6b6d,0x8082,0x6b88
+ },
+ { /* ku 10 */
+ 0x6be6,0x6be4,0x6be8,0x6be3,0x6be2,0x6be7,0x6c25,0x6d7a,0x6d63,0x6d64,
+ 0x6d76,0x6d0d,0x6d61,0x6d92,0x6d58,0x6d62,0x6d6d,0x6d6f,0x6d91,0x6d8d,
+ 0x6def,0x6d7f,0x6d86,0x6d5e,0x6d67,0x6d60,0x6d97,0x6d70,0x6d7c,0x6d5f,
+ 0x6d82,0x6d98,0x6d2f,0x6d68,0x6d8b,0x6d7e,0x6d80,0x6d84,0x6d16,0x6d83,
+ 0x6d7b,0x6d7d,0x6d75,0x6d90,0x70dc,0x70d3,0x70d1,0x70dd,0x70cb,0x7f39,
+ 0x70e2,0x70d7,0x70d2,0x70de,0x70e0,0x70d4,0x70cd,0x70c5,0x70c6,0x70c7,
+ 0x70da,0x70ce,0x70e1,0x7242,0x7278,0x7277,0x7276,0x7300,0x72fa,0x72f4,
+ 0x72fe,0x72f6,0x72f3,0x72fb,0x7301,0x73d3,0x73d9,0x73e5,0x73d6,0x73bc,
+ 0x73e7,0x73e3,0x73e9,0x73dc,0x73d2,0x73db,0x73d4,0x73dd,0x73da,0x73d7,
+ 0x73d8,0x73e8,0x74de,0x74df
+ },
+ { /* ku 11 */
+ 0x74f4,0x74f5,0x7521,0x755b,0x755f,0x75b0,0x75c1,0x75bb,0x75c4,0x75c0,
+ 0x75bf,0x75b6,0x75ba,0x768a,0x76c9,0x771d,0x771b,0x7710,0x7713,0x7712,
+ 0x7723,0x7711,0x7715,0x7719,0x771a,0x7722,0x7727,0x7823,0x782c,0x7822,
+ 0x7835,0x782f,0x7828,0x782e,0x782b,0x7821,0x7829,0x7833,0x782a,0x7831,
+ 0x7954,0x795b,0x794f,0x795c,0x7953,0x7952,0x7951,0x79eb,0x79ec,0x79e0,
+ 0x79ee,0x79ed,0x79ea,0x79dc,0x79de,0x79dd,0x7a86,0x7a89,0x7a85,0x7a8b,
+ 0x7a8c,0x7a8a,0x7a87,0x7ad8,0x7b10,0x7b04,0x7b13,0x7b05,0x7b0f,0x7b08,
+ 0x7b0a,0x7b0e,0x7b09,0x7b12,0x7c84,0x7c91,0x7c8a,0x7c8c,0x7c88,0x7c8d,
+ 0x7c85,0x7d1e,0x7d1d,0x7d11,0x7d0e,0x7d18,0x7d16,0x7d13,0x7d1f,0x7d12,
+ 0x7d0f,0x7d0c,0x7f5c,0x7f61
+ },
+ { /* ku 12 */
+ 0x7f5e,0x7f60,0x7f5d,0x7f5b,0x7f96,0x7f92,0x7fc3,0x7fc2,0x7fc0,0x8016,
+ 0x803e,0x8039,0x80fa,0x80f2,0x80f9,0x80f5,0x8101,0x80fb,0x8100,0x8201,
+ 0x822f,0x8225,0x8333,0x832d,0x8344,0x8319,0x8351,0x8325,0x8356,0x833f,
+ 0x8341,0x8326,0x831c,0x8322,0x8342,0x834e,0x831b,0x832a,0x8308,0x833c,
+ 0x834d,0x8316,0x8324,0x8320,0x8337,0x832f,0x8329,0x8347,0x8345,0x834c,
+ 0x8353,0x831e,0x832c,0x834b,0x8327,0x8348,0x8653,0x8652,0x86a2,0x86a8,
+ 0x8696,0x868d,0x8691,0x869e,0x8687,0x8697,0x8686,0x868b,0x869a,0x8685,
+ 0x86a5,0x8699,0x86a1,0x86a7,0x8695,0x8698,0x868e,0x869d,0x8690,0x8694,
+ 0x8843,0x8844,0x886d,0x8875,0x8876,0x8872,0x8880,0x8871,0x887f,0x886f,
+ 0x8883,0x887e,0x8874,0x887c
+ },
+ { /* ku 13 */
+ 0x8a12,0x8c47,0x8c57,0x8c7b,0x8ca4,0x8ca3,0x8d76,0x8d78,0x8db5,0x8db7,
+ 0x8db6,0x8ed1,0x8ed3,0x8ffe,0x8ff5,0x9002,0x8fff,0x8ffb,0x9004,0x8ffc,
+ 0x8ff6,0x90d6,0x90e0,0x90d9,0x90da,0x90e3,0x90df,0x90e5,0x90d8,0x90db,
+ 0x90d7,0x90dc,0x90e4,0x9150,0x914e,0x914f,0x91d5,0x91e2,0x91da,0x965c,
+ 0x965f,0x96bc,0x98e3,0x9adf,0x9b2f,0x4e7f,0x5070,0x506a,0x5061,0x505e,
+ 0x5060,0x5053,0x504b,0x505d,0x5072,0x5048,0x504d,0x5041,0x505b,0x504a,
+ 0x5062,0x5015,0x5045,0x505f,0x5069,0x506b,0x5063,0x5064,0x5046,0x5040,
+ 0x506e,0x5073,0x5057,0x5051,0x51d0,0x526b,0x526d,0x526c,0x526e,0x52d6,
+ 0x52d3,0x532d,0x539c,0x5575,0x5576,0x553c,0x554d,0x5550,0x5534,0x552a,
+ 0x5551,0x5562,0x5536,0x5535
+ },
+ { /* ku 14 */
+ 0x5530,0x5552,0x5545,0x550c,0x5532,0x5565,0x554e,0x5539,0x5548,0x552d,
+ 0x553b,0x5540,0x554b,0x570a,0x5707,0x57fb,0x5814,0x57e2,0x57f6,0x57dc,
+ 0x57f4,0x5800,0x57ed,0x57fd,0x5808,0x57f8,0x580b,0x57f3,0x57cf,0x5807,
+ 0x57ee,0x57e3,0x57f2,0x57e5,0x57ec,0x57e1,0x580e,0x57fc,0x5810,0x57e7,
+ 0x5801,0x580c,0x57f1,0x57e9,0x57f0,0x580d,0x5804,0x595c,0x5a60,0x5a58,
+ 0x5a55,0x5a67,0x5a5e,0x5a38,0x5a35,0x5a6d,0x5a50,0x5a5f,0x5a65,0x5a6c,
+ 0x5a53,0x5a64,0x5a57,0x5a43,0x5a5d,0x5a52,0x5a44,0x5a5b,0x5a48,0x5a8e,
+ 0x5a3e,0x5a4d,0x5a39,0x5a4c,0x5a70,0x5a69,0x5a47,0x5a51,0x5a56,0x5a42,
+ 0x5a5c,0x5b72,0x5b6e,0x5bc1,0x5bc0,0x5c59,0x5d1e,0x5d0b,0x5d1d,0x5d1a,
+ 0x5d20,0x5d0c,0x5d28,0x5d0d
+ },
+ { /* ku 15 */
+ 0x5d26,0x5d25,0x5d0f,0x5d30,0x5d12,0x5d23,0x5d1f,0x5d2e,0x5e3e,0x5e34,
+ 0x5eb1,0x5eb4,0x5eb9,0x5eb2,0x5eb3,0x5f36,0x5f38,0x5f9b,0x5f96,0x5f9f,
+ 0x608a,0x6090,0x6086,0x60be,0x60b0,0x60ba,0x60d3,0x60d4,0x60cf,0x60e4,
+ 0x60d9,0x60dd,0x60c8,0x60b1,0x60db,0x60b7,0x60ca,0x60bf,0x60c3,0x60cd,
+ 0x60c0,0x6332,0x6365,0x638a,0x6382,0x637d,0x63bd,0x639e,0x63ad,0x639d,
+ 0x6397,0x63ab,0x638e,0x636f,0x6387,0x6390,0x636e,0x63af,0x6375,0x639c,
+ 0x636d,0x63ae,0x637c,0x63a4,0x633b,0x639f,0x6378,0x6385,0x6381,0x6391,
+ 0x638d,0x6370,0x6553,0x65cd,0x6665,0x6661,0x665b,0x6659,0x665c,0x6662,
+ 0x6718,0x6879,0x6887,0x6890,0x689c,0x686d,0x686e,0x68ae,0x68ab,0x6956,
+ 0x686f,0x68a3,0x68ac,0x68a9
+ },
+ { /* ku 16 */
+ 0x6875,0x6874,0x68b2,0x688f,0x6877,0x6892,0x687c,0x686b,0x6872,0x68aa,
+ 0x6880,0x6871,0x687e,0x689b,0x6896,0x688b,0x68a0,0x6889,0x68a4,0x6878,
+ 0x687b,0x6891,0x688c,0x688a,0x687d,0x6b36,0x6b33,0x6b37,0x6b38,0x6b91,
+ 0x6b8f,0x6b8d,0x6b8e,0x6b8c,0x6c2a,0x6dc0,0x6dab,0x6db4,0x6db3,0x6e74,
+ 0x6dac,0x6de9,0x6de2,0x6db7,0x6df6,0x6dd4,0x6e00,0x6dc8,0x6de0,0x6ddf,
+ 0x6dd6,0x6dbe,0x6de5,0x6ddc,0x6ddd,0x6ddb,0x6df4,0x6dca,0x6dbd,0x6ded,
+ 0x6df0,0x6dba,0x6dd5,0x6dc2,0x6dcf,0x6dc9,0x6dd0,0x6df2,0x6dd3,0x6dfd,
+ 0x6dd7,0x6dcd,0x6de3,0x6dbb,0x70fa,0x710d,0x70f7,0x7117,0x70f4,0x710c,
+ 0x70f0,0x7104,0x70f3,0x7110,0x70fc,0x70ff,0x7106,0x7113,0x7100,0x70f8,
+ 0x70f6,0x710b,0x7102,0x710e
+ },
+ { /* ku 17 */
+ 0x727e,0x727b,0x727c,0x727f,0x731d,0x7317,0x7307,0x7311,0x7318,0x730a,
+ 0x7308,0x72ff,0x730f,0x731e,0x7388,0x73f6,0x73f8,0x73f5,0x7404,0x7401,
+ 0x73fd,0x7407,0x7400,0x73fa,0x73fc,0x73ff,0x740c,0x740b,0x73f4,0x7408,
+ 0x7564,0x7563,0x75ce,0x75d2,0x75cf,0x75cb,0x75cc,0x75d1,0x75d0,0x768f,
+ 0x7689,0x76d3,0x7739,0x772f,0x772d,0x7731,0x7732,0x7734,0x7733,0x773d,
+ 0x7725,0x773b,0x7735,0x7848,0x7852,0x7849,0x784d,0x784a,0x784c,0x7826,
+ 0x7845,0x7850,0x7964,0x7967,0x7969,0x796a,0x7963,0x796b,0x7961,0x79bb,
+ 0x79fa,0x79f8,0x79f6,0x79f7,0x7a8f,0x7a94,0x7a90,0x7b35,0x7b3b,0x7b34,
+ 0x7b25,0x7b30,0x7b22,0x7b24,0x7b33,0x7b18,0x7b2a,0x7b1d,0x7b31,0x7b2b,
+ 0x7b2d,0x7b2f,0x7b32,0x7b38
+ },
+ { /* ku 18 */
+ 0x7b1a,0x7b23,0x7c94,0x7c98,0x7c96,0x7ca3,0x7d35,0x7d3d,0x7d38,0x7d36,
+ 0x7d3a,0x7d45,0x7d2c,0x7d29,0x7d41,0x7d47,0x7d3e,0x7d3f,0x7d4a,0x7d3b,
+ 0x7d28,0x7f63,0x7f95,0x7f9c,0x7f9d,0x7f9b,0x7fca,0x7fcb,0x7fcd,0x7fd0,
+ 0x7fd1,0x7fc7,0x7fcf,0x7fc9,0x801f,0x801e,0x801b,0x8047,0x8043,0x8048,
+ 0x8118,0x8125,0x8119,0x811b,0x812d,0x811f,0x812c,0x811e,0x8121,0x8115,
+ 0x8127,0x811d,0x8122,0x8211,0x8238,0x8233,0x823a,0x8234,0x8232,0x8274,
+ 0x8390,0x83a3,0x83a8,0x838d,0x837a,0x8373,0x83a4,0x8374,0x838f,0x8381,
+ 0x8395,0x8399,0x8375,0x8394,0x83a9,0x837d,0x8383,0x838c,0x839d,0x839b,
+ 0x83aa,0x838b,0x837e,0x83a5,0x83af,0x8388,0x8397,0x83b0,0x837f,0x83a6,
+ 0x8387,0x83ae,0x8376,0x8659
+ },
+ { /* ku 19 */
+ 0x8656,0x86bf,0x86b7,0x86c2,0x86c1,0x86c5,0x86ba,0x86b0,0x86c8,0x86b9,
+ 0x86b3,0x86b8,0x86cc,0x86b4,0x86bb,0x86bc,0x86c3,0x86bd,0x86be,0x8852,
+ 0x8889,0x8895,0x88a8,0x88a2,0x88aa,0x889a,0x8891,0x88a1,0x889f,0x8898,
+ 0x88a7,0x8899,0x889b,0x8897,0x88a4,0x88ac,0x888c,0x8893,0x888e,0x8982,
+ 0x89d6,0x89d9,0x89d5,0x8a30,0x8a27,0x8a2c,0x8a1e,0x8c39,0x8c3b,0x8c5c,
+ 0x8c5d,0x8c7d,0x8ca5,0x8d7d,0x8d7b,0x8d79,0x8dbc,0x8dc2,0x8db9,0x8dbf,
+ 0x8dc1,0x8ed8,0x8ede,0x8edd,0x8edc,0x8ed7,0x8ee0,0x8ee1,0x9024,0x900b,
+ 0x9011,0x901c,0x900c,0x9021,0x90ef,0x90ea,0x90f0,0x90f4,0x90f2,0x90f3,
+ 0x90d4,0x90eb,0x90ec,0x90e9,0x9156,0x9158,0x915a,0x9153,0x9155,0x91ec,
+ 0x91f4,0x91f1,0x91f3,0x91f8
+ },
+ { /* ku 1a */
+ 0x91e4,0x91f9,0x91ea,0x91eb,0x91f7,0x91e8,0x91ee,0x957a,0x9586,0x9588,
+ 0x967c,0x966d,0x966b,0x9671,0x966f,0x96bf,0x976a,0x9804,0x98e5,0x9997,
+ 0x509b,0x5095,0x5094,0x509e,0x508b,0x50a3,0x5083,0x508c,0x508e,0x509d,
+ 0x5068,0x509c,0x5092,0x5082,0x5087,0x515f,0x51d4,0x5312,0x5311,0x53a4,
+ 0x53a7,0x5591,0x55a8,0x55a5,0x55ad,0x5577,0x5645,0x55a2,0x5593,0x5588,
+ 0x558f,0x55b5,0x5581,0x55a3,0x5592,0x55a4,0x557d,0x558c,0x55a6,0x557f,
+ 0x5595,0x55a1,0x558e,0x570c,0x5829,0x5837,0x5819,0x581e,0x5827,0x5823,
+ 0x5828,0x57f5,0x5848,0x5825,0x581c,0x581b,0x5833,0x583f,0x5836,0x582e,
+ 0x5839,0x5838,0x582d,0x582c,0x583b,0x5961,0x5aaf,0x5a94,0x5a9f,0x5a7a,
+ 0x5aa2,0x5a9e,0x5a78,0x5aa6
+ },
+ { /* ku 1b */
+ 0x5a7c,0x5aa5,0x5aac,0x5a95,0x5aae,0x5a37,0x5a84,0x5a8a,0x5a97,0x5a83,
+ 0x5a8b,0x5aa9,0x5a7b,0x5a7d,0x5a8c,0x5a9c,0x5a8f,0x5a93,0x5a9d,0x5bea,
+ 0x5bcd,0x5bcb,0x5bd4,0x5bd1,0x5bca,0x5bce,0x5c0c,0x5c30,0x5d37,0x5d43,
+ 0x5d6b,0x5d41,0x5d4b,0x5d3f,0x5d35,0x5d51,0x5d4e,0x5d55,0x5d33,0x5d3a,
+ 0x5d52,0x5d3d,0x5d31,0x5d59,0x5d42,0x5d39,0x5d49,0x5d38,0x5d3c,0x5d32,
+ 0x5d36,0x5d40,0x5d45,0x5e44,0x5e41,0x5f58,0x5fa6,0x5fa5,0x5fab,0x60c9,
+ 0x60b9,0x60cc,0x60e2,0x60ce,0x60c4,0x6114,0x60f2,0x610a,0x6116,0x6105,
+ 0x60f5,0x6113,0x60f8,0x60fc,0x60fe,0x60c1,0x6103,0x6118,0x611d,0x6110,
+ 0x60ff,0x6104,0x610b,0x624a,0x6394,0x63b1,0x63b0,0x63ce,0x63e5,0x63e8,
+ 0x63ef,0x63c3,0x649d,0x63f3
+ },
+ { /* ku 1c */
+ 0x63ca,0x63e0,0x63f6,0x63d5,0x63f2,0x63f5,0x6461,0x63df,0x63be,0x63dd,
+ 0x63dc,0x63c4,0x63d8,0x63d3,0x63c2,0x63c7,0x63cc,0x63cb,0x63c8,0x63f0,
+ 0x63d7,0x63d9,0x6532,0x6567,0x656a,0x6564,0x655c,0x6568,0x6565,0x658c,
+ 0x659d,0x659e,0x65ae,0x65d0,0x65d2,0x667c,0x666c,0x667b,0x6680,0x6671,
+ 0x6679,0x666a,0x6672,0x6701,0x690c,0x68d3,0x6904,0x68dc,0x692a,0x68ec,
+ 0x68ea,0x68f1,0x690f,0x68d6,0x68f7,0x68eb,0x68e4,0x68f6,0x6913,0x6910,
+ 0x68f3,0x68e1,0x6907,0x68cc,0x6908,0x6970,0x68b4,0x6911,0x68ef,0x68c6,
+ 0x6914,0x68f8,0x68d0,0x68fd,0x68fc,0x68e8,0x690b,0x690a,0x6917,0x68ce,
+ 0x68c8,0x68dd,0x68de,0x68e6,0x68f4,0x68d1,0x6906,0x68d4,0x68e9,0x6915,
+ 0x6925,0x68c7,0x6b39,0x6b3b
+ },
+ { /* ku 1d */
+ 0x6b3f,0x6b3c,0x6b94,0x6b97,0x6b99,0x6b95,0x6bbd,0x6bf0,0x6bf2,0x6bf3,
+ 0x6c30,0x6dfc,0x6e46,0x6e47,0x6e1f,0x6e49,0x6e88,0x6e3c,0x6e3d,0x6e45,
+ 0x6e62,0x6e2b,0x6e3f,0x6e41,0x6e5d,0x6e73,0x6e1c,0x6e33,0x6e4b,0x6e40,
+ 0x6e51,0x6e3b,0x6e03,0x6e2e,0x6e5e,0x6e68,0x6e5c,0x6e61,0x6e31,0x6e28,
+ 0x6e60,0x6e71,0x6e6b,0x6e39,0x6e22,0x6e30,0x6e53,0x6e65,0x6e27,0x6e78,
+ 0x6e64,0x6e77,0x6e55,0x6e79,0x6e52,0x6e66,0x6e35,0x6e36,0x6e5a,0x7120,
+ 0x711e,0x712f,0x70fb,0x712e,0x7131,0x7123,0x7125,0x7122,0x7132,0x711f,
+ 0x7128,0x713a,0x711b,0x724b,0x725a,0x7288,0x7289,0x7286,0x7285,0x728b,
+ 0x7312,0x730b,0x7330,0x7322,0x7331,0x7333,0x7327,0x7332,0x732d,0x7326,
+ 0x7323,0x7335,0x730c,0x742e
+ },
+ { /* ku 1e */
+ 0x742c,0x7430,0x742b,0x7416,0x741a,0x7421,0x742d,0x7431,0x7424,0x7423,
+ 0x741d,0x7429,0x7420,0x7432,0x74fb,0x752f,0x756f,0x756c,0x75e7,0x75da,
+ 0x75e1,0x75e6,0x75dd,0x75df,0x75e4,0x75d7,0x7695,0x7692,0x76da,0x7746,
+ 0x7747,0x7744,0x774d,0x7745,0x774a,0x774e,0x774b,0x774c,0x77de,0x77ec,
+ 0x7860,0x7864,0x7865,0x785c,0x786d,0x7871,0x786a,0x786e,0x7870,0x7869,
+ 0x7868,0x785e,0x7862,0x7974,0x7973,0x7972,0x7970,0x7a02,0x7a0a,0x7a03,
+ 0x7a0c,0x7a04,0x7a99,0x7ae6,0x7ae4,0x7b4a,0x7b47,0x7b44,0x7b48,0x7b4c,
+ 0x7b4e,0x7b40,0x7b58,0x7b45,0x7ca2,0x7c9e,0x7ca8,0x7ca1,0x7d58,0x7d6f,
+ 0x7d63,0x7d53,0x7d56,0x7d67,0x7d6a,0x7d4f,0x7d6d,0x7d5c,0x7d6b,0x7d52,
+ 0x7d54,0x7d69,0x7d51,0x7d5f
+ },
+ { /* ku 1f */
+ 0x7d4e,0x7f3e,0x7f3f,0x7f65,0x7f66,0x7fa2,0x7fa0,0x7fa1,0x7fd7,0x8051,
+ 0x804f,0x8050,0x80fe,0x80d4,0x8143,0x814a,0x8152,0x814f,0x8147,0x813d,
+ 0x814d,0x813a,0x81e6,0x81ee,0x81f7,0x81f8,0x81f9,0x8204,0x823c,0x823d,
+ 0x823f,0x8275,0x833b,0x83cf,0x83f9,0x8423,0x83c0,0x83e8,0x8412,0x83e7,
+ 0x83e4,0x83fc,0x83f6,0x8410,0x83c6,0x83c8,0x83eb,0x83e3,0x83bf,0x8401,
+ 0x83dd,0x83e5,0x83d8,0x83ff,0x83e1,0x83cb,0x83ce,0x83d6,0x83f5,0x83c9,
+ 0x8409,0x840f,0x83de,0x8411,0x8406,0x83c2,0x83f3,0x83d5,0x83fa,0x83c7,
+ 0x83d1,0x83ea,0x8413,0x839a,0x83c3,0x83ec,0x83ee,0x83c4,0x83fb,0x83d7,
+ 0x83e2,0x841b,0x83db,0x83fe,0x86d8,0x86e2,0x86e6,0x86d3,0x86e3,0x86da,
+ 0x86ea,0x86dd,0x86eb,0x86dc
+ },
+ { /* ku 20 */
+ 0x86ec,0x86e9,0x86d7,0x86e8,0x86d1,0x8848,0x8856,0x8855,0x88ba,0x88d7,
+ 0x88b9,0x88b8,0x88c0,0x88be,0x88b6,0x88bc,0x88b7,0x88bd,0x88b2,0x8901,
+ 0x88c9,0x8995,0x8998,0x8997,0x89dd,0x89da,0x89db,0x8a4e,0x8a4d,0x8a39,
+ 0x8a59,0x8a40,0x8a57,0x8a58,0x8a44,0x8a45,0x8a52,0x8a48,0x8a51,0x8a4a,
+ 0x8a4c,0x8a4f,0x8c5f,0x8c81,0x8c80,0x8cba,0x8cbe,0x8cb0,0x8cb9,0x8cb5,
+ 0x8d84,0x8d80,0x8d89,0x8dd8,0x8dd3,0x8dcd,0x8dc7,0x8dd6,0x8ddc,0x8dcf,
+ 0x8dd5,0x8dd9,0x8dc8,0x8dd7,0x8dc5,0x8eef,0x8ef7,0x8efa,0x8ef9,0x8ee6,
+ 0x8eee,0x8ee5,0x8ef5,0x8ee7,0x8ee8,0x8ef6,0x8eeb,0x8ef1,0x8eec,0x8ef4,
+ 0x8ee9,0x902d,0x9034,0x902f,0x9106,0x912c,0x9104,0x90ff,0x90fc,0x9108,
+ 0x90f9,0x90fb,0x9101,0x9100
+ },
+ { /* ku 21 */
+ 0x9107,0x9105,0x9103,0x9161,0x9164,0x915f,0x9162,0x9160,0x9201,0x920a,
+ 0x9225,0x9203,0x921a,0x9226,0x920f,0x920c,0x9200,0x9212,0x91ff,0x91fd,
+ 0x9206,0x9204,0x9227,0x9202,0x921c,0x9224,0x9219,0x9217,0x9205,0x9216,
+ 0x957b,0x958d,0x958c,0x9590,0x9687,0x967e,0x9688,0x9689,0x9683,0x9680,
+ 0x96c2,0x96c8,0x96c3,0x96f1,0x96f0,0x976c,0x9770,0x976e,0x9807,0x98a9,
+ 0x98eb,0x9ce6,0x9ef9,0x4e83,0x4e84,0x4eb6,0x50bd,0x50bf,0x50c6,0x50ae,
+ 0x50c4,0x50ca,0x50b4,0x50c8,0x50c2,0x50b0,0x50c1,0x50ba,0x50b1,0x50cb,
+ 0x50c9,0x50b6,0x50b8,0x51d7,0x527a,0x5278,0x527b,0x527c,0x55c3,0x55db,
+ 0x55cc,0x55d0,0x55cb,0x55ca,0x55dd,0x55c0,0x55d4,0x55c4,0x55e9,0x55bf,
+ 0x55d2,0x558d,0x55cf,0x55d5
+ },
+ { /* ku 22 */
+ 0x55e2,0x55d6,0x55c8,0x55f2,0x55cd,0x55d9,0x55c2,0x5714,0x5853,0x5868,
+ 0x5864,0x584f,0x584d,0x5849,0x586f,0x5855,0x584e,0x585d,0x5859,0x5865,
+ 0x585b,0x583d,0x5863,0x5871,0x58fc,0x5ac7,0x5ac4,0x5acb,0x5aba,0x5ab8,
+ 0x5ab1,0x5ab5,0x5ab0,0x5abf,0x5ac8,0x5abb,0x5ac6,0x5ab7,0x5ac0,0x5aca,
+ 0x5ab4,0x5ab6,0x5acd,0x5ab9,0x5a90,0x5bd6,0x5bd8,0x5bd9,0x5c1f,0x5c33,
+ 0x5d71,0x5d63,0x5d4a,0x5d65,0x5d72,0x5d6c,0x5d5e,0x5d68,0x5d67,0x5d62,
+ 0x5df0,0x5e4f,0x5e4e,0x5e4a,0x5e4d,0x5e4b,0x5ec5,0x5ecc,0x5ec6,0x5ecb,
+ 0x5ec7,0x5f40,0x5faf,0x5fad,0x60f7,0x6149,0x614a,0x612b,0x6145,0x6136,
+ 0x6132,0x612e,0x6146,0x612f,0x614f,0x6129,0x6140,0x6220,0x9168,0x6223,
+ 0x6225,0x6224,0x63c5,0x63f1
+ },
+ { /* ku 23 */
+ 0x63eb,0x6410,0x6412,0x6409,0x6420,0x6424,0x6433,0x6443,0x641f,0x6415,
+ 0x6418,0x6439,0x6437,0x6422,0x6423,0x640c,0x6426,0x6430,0x6428,0x6441,
+ 0x6435,0x642f,0x640a,0x641a,0x6440,0x6425,0x6427,0x640b,0x63e7,0x641b,
+ 0x642e,0x6421,0x640e,0x656f,0x6592,0x65d3,0x6686,0x668c,0x6695,0x6690,
+ 0x668b,0x668a,0x6699,0x6694,0x6678,0x6720,0x6966,0x695f,0x6938,0x694e,
+ 0x6962,0x6971,0x693f,0x6945,0x696a,0x6939,0x6942,0x6957,0x6959,0x697a,
+ 0x6948,0x6949,0x6935,0x696c,0x6933,0x693d,0x6965,0x68f0,0x6978,0x6934,
+ 0x6969,0x6940,0x696f,0x6944,0x6976,0x6958,0x6941,0x6974,0x694c,0x693b,
+ 0x694b,0x6937,0x695c,0x694f,0x6951,0x6932,0x6952,0x692f,0x697b,0x693c,
+ 0x6b46,0x6b45,0x6b43,0x6b42
+ },
+ { /* ku 24 */
+ 0x6b48,0x6b41,0x6b9b,0x6bfb,0x6bfc,0x6bf9,0x6bf7,0x6bf8,0x6e9b,0x6ed6,
+ 0x6ec8,0x6e8f,0x6ec0,0x6e9f,0x6e93,0x6e94,0x6ea0,0x6eb1,0x6eb9,0x6ec6,
+ 0x6ed2,0x6ebd,0x6ec1,0x6e9e,0x6ec9,0x6eb7,0x6eb0,0x6ecd,0x6ea6,0x6ecf,
+ 0x6eb2,0x6ebe,0x6ec3,0x6edc,0x6ed8,0x6e99,0x6e92,0x6e8e,0x6e8d,0x6ea4,
+ 0x6ea1,0x6ebf,0x6eb3,0x6ed0,0x6eca,0x6e97,0x6eae,0x6ea3,0x7147,0x7154,
+ 0x7152,0x7163,0x7160,0x7141,0x715d,0x7162,0x7172,0x7178,0x716a,0x7161,
+ 0x7142,0x7158,0x7143,0x714b,0x7170,0x715f,0x7150,0x7153,0x7144,0x714d,
+ 0x715a,0x724f,0x728d,0x728c,0x7291,0x7290,0x728e,0x733c,0x7342,0x733b,
+ 0x733a,0x7340,0x734a,0x7349,0x7444,0x744a,0x744b,0x7452,0x7451,0x7457,
+ 0x7440,0x744f,0x7450,0x744e
+ },
+ { /* ku 25 */
+ 0x7442,0x7446,0x744d,0x7454,0x74e1,0x74ff,0x74fe,0x74fd,0x751d,0x7579,
+ 0x7577,0x6983,0x75ef,0x760f,0x7603,0x75f7,0x75fe,0x75fc,0x75f9,0x75f8,
+ 0x7610,0x75fb,0x75f6,0x75ed,0x75f5,0x75fd,0x7699,0x76b5,0x76dd,0x7755,
+ 0x775f,0x7760,0x7752,0x7756,0x775a,0x7769,0x7767,0x7754,0x7759,0x776d,
+ 0x77e0,0x7887,0x789a,0x7894,0x788f,0x7884,0x7895,0x7885,0x7886,0x78a1,
+ 0x7883,0x7879,0x7899,0x7880,0x7896,0x787b,0x797c,0x7982,0x797d,0x7979,
+ 0x7a11,0x7a18,0x7a19,0x7a12,0x7a17,0x7a15,0x7a22,0x7a13,0x7a1b,0x7a10,
+ 0x7aa3,0x7aa2,0x7a9e,0x7aeb,0x7b66,0x7b64,0x7b6d,0x7b74,0x7b69,0x7b72,
+ 0x7b65,0x7b73,0x7b71,0x7b70,0x7b61,0x7b78,0x7b76,0x7b63,0x7cb2,0x7cb4,
+ 0x7caf,0x7d88,0x7d86,0x7d80
+ },
+ { /* ku 26 */
+ 0x7d8d,0x7d7f,0x7d85,0x7d7a,0x7d8e,0x7d7b,0x7d83,0x7d7c,0x7d8c,0x7d94,
+ 0x7d84,0x7d7d,0x7d92,0x7f6d,0x7f6b,0x7f67,0x7f68,0x7f6c,0x7fa6,0x7fa5,
+ 0x7fa7,0x7fdb,0x7fdc,0x8021,0x8164,0x8160,0x8177,0x815c,0x8169,0x815b,
+ 0x8162,0x8172,0x6721,0x815e,0x8176,0x8167,0x816f,0x8144,0x8161,0x821d,
+ 0x8249,0x8244,0x8240,0x8242,0x8245,0x84f1,0x843f,0x8456,0x8476,0x8479,
+ 0x848f,0x848d,0x8465,0x8451,0x8440,0x8486,0x8467,0x8430,0x844d,0x847d,
+ 0x845a,0x8459,0x8474,0x8473,0x845d,0x8507,0x845e,0x8437,0x843a,0x8434,
+ 0x847a,0x8443,0x8478,0x8432,0x8445,0x8429,0x83d9,0x844b,0x842f,0x8442,
+ 0x842d,0x845f,0x8470,0x8439,0x844e,0x844c,0x8452,0x846f,0x84c5,0x848e,
+ 0x843b,0x8447,0x8436,0x8433
+ },
+ { /* ku 27 */
+ 0x8468,0x847e,0x8444,0x842b,0x8460,0x8454,0x846e,0x8450,0x870b,0x8704,
+ 0x86f7,0x870c,0x86fa,0x86d6,0x86f5,0x874d,0x86f8,0x870e,0x8709,0x8701,
+ 0x86f6,0x870d,0x8705,0x88d6,0x88cb,0x88cd,0x88ce,0x88de,0x88db,0x88da,
+ 0x88cc,0x88d0,0x8985,0x899b,0x89df,0x89e5,0x89e4,0x89e1,0x89e0,0x89e2,
+ 0x89dc,0x89e6,0x8a76,0x8a86,0x8a7f,0x8a61,0x8a3f,0x8a77,0x8a82,0x8a84,
+ 0x8a75,0x8a83,0x8a81,0x8a74,0x8a7a,0x8c3c,0x8c4b,0x8c4a,0x8c65,0x8c64,
+ 0x8c66,0x8c86,0x8c84,0x8c85,0x8ccc,0x8d68,0x8d69,0x8d91,0x8d8c,0x8d8e,
+ 0x8d8f,0x8d8d,0x8d93,0x8d94,0x8d90,0x8d92,0x8df0,0x8de0,0x8dec,0x8df1,
+ 0x8dee,0x8dd0,0x8de9,0x8de3,0x8de2,0x8de7,0x8df2,0x8deb,0x8df4,0x8f06,
+ 0x8eff,0x8f01,0x8f00,0x8f05
+ },
+ { /* ku 28 */
+ 0x8f07,0x8f08,0x8f02,0x8f0b,0x9052,0x903f,0x9044,0x9049,0x903d,0x9110,
+ 0x910d,0x910f,0x9111,0x9116,0x9114,0x910b,0x910e,0x916e,0x916f,0x9248,
+ 0x9252,0x9230,0x923a,0x9266,0x9233,0x9265,0x925e,0x9283,0x922e,0x924a,
+ 0x9246,0x926d,0x926c,0x924f,0x9260,0x9267,0x926f,0x9236,0x9261,0x9270,
+ 0x9231,0x9254,0x9263,0x9250,0x9272,0x924e,0x9253,0x924c,0x9256,0x9232,
+ 0x959f,0x959c,0x959e,0x959b,0x9692,0x9693,0x9691,0x9697,0x96ce,0x96fa,
+ 0x96fd,0x96f8,0x96f5,0x9773,0x9777,0x9778,0x9772,0x980f,0x980d,0x980e,
+ 0x98ac,0x98f6,0x98f9,0x99af,0x99b2,0x99b0,0x99b5,0x9aad,0x9aab,0x9b5b,
+ 0x9cea,0x9ced,0x9ce7,0x9e80,0x9efd,0x50e6,0x50d4,0x50d7,0x50e8,0x50f3,
+ 0x50db,0x50ea,0x50dd,0x50e4
+ },
+ { /* ku 29 */
+ 0x50d3,0x50ec,0x50f0,0x50ef,0x50e3,0x50e0,0x51d8,0x5280,0x5281,0x52e9,
+ 0x52eb,0x5330,0x53ac,0x5627,0x5615,0x560c,0x5612,0x55fc,0x560f,0x561c,
+ 0x5601,0x5613,0x5602,0x55fa,0x561d,0x5604,0x55ff,0x55f9,0x5889,0x587c,
+ 0x5890,0x5898,0x5886,0x5881,0x587f,0x5874,0x588b,0x587a,0x5887,0x5891,
+ 0x588e,0x5876,0x5882,0x5888,0x587b,0x5894,0x588f,0x58fe,0x596b,0x5adc,
+ 0x5aee,0x5ae5,0x5ad5,0x5aea,0x5ada,0x5aed,0x5aeb,0x5af3,0x5ae2,0x5ae0,
+ 0x5adb,0x5aec,0x5ade,0x5add,0x5ad9,0x5ae8,0x5adf,0x5b77,0x5be0,0x5be3,
+ 0x5c63,0x5d82,0x5d80,0x5d7d,0x5d86,0x5d7a,0x5d81,0x5d77,0x5d8a,0x5d89,
+ 0x5d88,0x5d7e,0x5d7c,0x5d8d,0x5d79,0x5d7f,0x5e58,0x5e59,0x5e53,0x5ed8,
+ 0x5ed1,0x5ed7,0x5ece,0x5edc
+ },
+ { /* ku 2a */
+ 0x5ed5,0x5ed9,0x5ed2,0x5ed4,0x5f44,0x5f43,0x5f6f,0x5fb6,0x612c,0x6128,
+ 0x6141,0x615e,0x6171,0x6173,0x6152,0x6153,0x6172,0x616c,0x6180,0x6174,
+ 0x6154,0x617a,0x615b,0x6165,0x613b,0x616a,0x6161,0x6156,0x6229,0x6227,
+ 0x622b,0x642b,0x644d,0x645b,0x645d,0x6474,0x6476,0x6472,0x6473,0x647d,
+ 0x6475,0x6466,0x64a6,0x644e,0x6482,0x645e,0x645c,0x644b,0x6453,0x6460,
+ 0x6450,0x647f,0x643f,0x646c,0x646b,0x6459,0x6465,0x6477,0x6573,0x65a0,
+ 0x66a1,0x66a0,0x669f,0x6705,0x6704,0x6722,0x69b1,0x69b6,0x69c9,0x69a0,
+ 0x69ce,0x6996,0x69b0,0x69ac,0x69bc,0x6991,0x6999,0x698e,0x69a7,0x698d,
+ 0x69a9,0x69be,0x69af,0x69bf,0x69c4,0x69bd,0x69a4,0x69d4,0x69b9,0x69ca,
+ 0x699a,0x69cf,0x69b3,0x6993
+ },
+ { /* ku 2b */
+ 0x69aa,0x69a1,0x699e,0x69d9,0x6997,0x6990,0x69c2,0x69b5,0x69a5,0x69c6,
+ 0x6b4a,0x6b4d,0x6b4b,0x6b9e,0x6b9f,0x6ba0,0x6bc3,0x6bc4,0x6bfe,0x6ece,
+ 0x6ef5,0x6ef1,0x6f03,0x6f25,0x6ef8,0x6f37,0x6efb,0x6f2e,0x6f09,0x6f4e,
+ 0x6f19,0x6f1a,0x6f27,0x6f18,0x6f3b,0x6f12,0x6eed,0x6f0a,0x6f36,0x6f73,
+ 0x6ef9,0x6eee,0x6f2d,0x6f40,0x6f30,0x6f3c,0x6f35,0x6eeb,0x6f07,0x6f0e,
+ 0x6f43,0x6f05,0x6efd,0x6ef6,0x6f39,0x6f1c,0x6efc,0x6f3a,0x6f1f,0x6f0d,
+ 0x6f1e,0x6f08,0x6f21,0x7187,0x7190,0x7189,0x7180,0x7185,0x7182,0x718f,
+ 0x717b,0x7186,0x7181,0x7197,0x7244,0x7253,0x7297,0x7295,0x7293,0x7343,
+ 0x734d,0x7351,0x734c,0x7462,0x7473,0x7471,0x7475,0x7472,0x7467,0x746e,
+ 0x7500,0x7502,0x7503,0x757d
+ },
+ { /* ku 2c */
+ 0x7590,0x7616,0x7608,0x760c,0x7615,0x7611,0x760a,0x7614,0x76b8,0x7781,
+ 0x777c,0x7785,0x7782,0x776e,0x7780,0x776f,0x777e,0x7783,0x78b2,0x78aa,
+ 0x78b4,0x78ad,0x78a8,0x787e,0x78ab,0x789e,0x78a5,0x78a0,0x78ac,0x78a2,
+ 0x78a4,0x7998,0x798a,0x798b,0x7996,0x7995,0x7994,0x7993,0x7997,0x7988,
+ 0x7992,0x7990,0x7a2b,0x7a4a,0x7a30,0x7a2f,0x7a28,0x7a26,0x7aa8,0x7aab,
+ 0x7aac,0x7aee,0x7b88,0x7b9c,0x7b8a,0x7b91,0x7b90,0x7b96,0x7b8d,0x7b8c,
+ 0x7b9b,0x7b8e,0x7b85,0x7b98,0x5284,0x7b99,0x7ba4,0x7b82,0x7cbb,0x7cbf,
+ 0x7cbc,0x7cba,0x7da7,0x7db7,0x7dc2,0x7da3,0x7daa,0x7dc1,0x7dc0,0x7dc5,
+ 0x7d9d,0x7dce,0x7dc4,0x7dc6,0x7dcb,0x7dcc,0x7daf,0x7db9,0x7d96,0x7dbc,
+ 0x7d9f,0x7da6,0x7dae,0x7da9
+ },
+ { /* ku 2d */
+ 0x7da1,0x7dc9,0x7f73,0x7fe2,0x7fe3,0x7fe5,0x7fde,0x8024,0x805d,0x805c,
+ 0x8189,0x8186,0x8183,0x8187,0x818d,0x818c,0x818b,0x8215,0x8497,0x84a4,
+ 0x84a1,0x849f,0x84ba,0x84ce,0x84c2,0x84ac,0x84ae,0x84ab,0x84b9,0x84b4,
+ 0x84c1,0x84cd,0x84aa,0x849a,0x84b1,0x84d0,0x849d,0x84a7,0x84bb,0x84a2,
+ 0x8494,0x84c7,0x84cc,0x849b,0x84a9,0x84af,0x84a8,0x84d6,0x8498,0x84b6,
+ 0x84cf,0x84a0,0x84d7,0x84d4,0x84d2,0x84db,0x84b0,0x8491,0x8661,0x8733,
+ 0x8723,0x8728,0x876b,0x8740,0x872e,0x871e,0x8721,0x8719,0x871b,0x8743,
+ 0x872c,0x8741,0x873e,0x8746,0x8720,0x8732,0x872a,0x872d,0x873c,0x8712,
+ 0x873a,0x8731,0x8735,0x8742,0x8726,0x8727,0x8738,0x8724,0x871a,0x8730,
+ 0x8711,0x88f7,0x88e7,0x88f1
+ },
+ { /* ku 2e */
+ 0x88f2,0x88fa,0x88fe,0x88ee,0x88fc,0x88f6,0x88fb,0x88f0,0x88ec,0x88eb,
+ 0x899d,0x89a1,0x899f,0x899e,0x89e9,0x89eb,0x89e8,0x8aab,0x8a99,0x8a8b,
+ 0x8a92,0x8a8f,0x8a96,0x8c3d,0x8c68,0x8c69,0x8cd5,0x8ccf,0x8cd7,0x8d96,
+ 0x8e09,0x8e02,0x8dff,0x8e0d,0x8dfd,0x8e0a,0x8e03,0x8e07,0x8e06,0x8e05,
+ 0x8dfe,0x8e00,0x8e04,0x8f10,0x8f11,0x8f0e,0x8f0d,0x9123,0x911c,0x9120,
+ 0x9122,0x911f,0x911d,0x911a,0x9124,0x9121,0x911b,0x917a,0x9172,0x9179,
+ 0x9173,0x92a5,0x92a4,0x9276,0x929b,0x927a,0x92a0,0x9294,0x92aa,0x928d,
+ 0x92a6,0x929a,0x92ab,0x9279,0x9297,0x927f,0x92a3,0x92ee,0x928e,0x9282,
+ 0x9295,0x92a2,0x927d,0x9288,0x92a1,0x928a,0x9286,0x928c,0x9299,0x92a7,
+ 0x927e,0x9287,0x92a9,0x929d
+ },
+ { /* ku 2f */
+ 0x928b,0x922d,0x969e,0x96a1,0x96ff,0x9758,0x977d,0x977a,0x977e,0x9783,
+ 0x9780,0x9782,0x977b,0x9784,0x9781,0x977f,0x97ce,0x97cd,0x9816,0x98ad,
+ 0x98ae,0x9902,0x9900,0x9907,0x999d,0x999c,0x99c3,0x99b9,0x99bb,0x99ba,
+ 0x99c2,0x99bd,0x99c7,0x9ab1,0x9ae3,0x9ae7,0x9b3e,0x9b3f,0x9b60,0x9b61,
+ 0x9b5f,0x9cf1,0x9cf2,0x9cf5,0x9ea7,0x50ff,0x5103,0x5130,0x50f8,0x5106,
+ 0x5107,0x50f6,0x50fe,0x510b,0x510c,0x50fd,0x510a,0x528b,0x528c,0x52f1,
+ 0x52ef,0x5648,0x5642,0x564c,0x5635,0x5641,0x564a,0x5649,0x5646,0x5658,
+ 0x565a,0x5640,0x5633,0x563d,0x562c,0x563e,0x5638,0x562a,0x563a,0x571a,
+ 0x58ab,0x589d,0x58b1,0x58a0,0x58a3,0x58af,0x58ac,0x58a5,0x58a1,0x58ff,
+ 0x5aff,0x5af4,0x5afd,0x5af7
+ },
+ { /* ku 30 */
+ 0x5af6,0x5b03,0x5af8,0x5b02,0x5af9,0x5b01,0x5b07,0x5b05,0x5b0f,0x5c67,
+ 0x5d99,0x5d97,0x5d9f,0x5d92,0x5da2,0x5d93,0x5d95,0x5da0,0x5d9c,0x5da1,
+ 0x5d9a,0x5d9e,0x5e69,0x5e5d,0x5e60,0x5e5c,0x7df3,0x5edb,0x5ede,0x5ee1,
+ 0x5f49,0x5fb2,0x618b,0x6183,0x6179,0x61b1,0x61b0,0x61a2,0x6189,0x619b,
+ 0x6193,0x61af,0x61ad,0x619f,0x6192,0x61aa,0x61a1,0x618d,0x6166,0x61b3,
+ 0x622d,0x646e,0x6470,0x6496,0x64a0,0x6485,0x6497,0x649c,0x648f,0x648b,
+ 0x648a,0x648c,0x64a3,0x649f,0x6468,0x64b1,0x6498,0x6576,0x657a,0x6579,
+ 0x657b,0x65b2,0x65b3,0x66b5,0x66b0,0x66a9,0x66b2,0x66b7,0x66aa,0x66af,
+ 0x6a00,0x6a06,0x6a17,0x69e5,0x69f8,0x6a15,0x69f1,0x69e4,0x6a20,0x69ff,
+ 0x69ec,0x69e2,0x6a1b,0x6a1d
+ },
+ { /* ku 31 */
+ 0x69fe,0x6a27,0x69f2,0x69ee,0x6a14,0x69f7,0x69e7,0x6a40,0x6a08,0x69e6,
+ 0x69fb,0x6a0d,0x69fc,0x69eb,0x6a09,0x6a04,0x6a18,0x6a25,0x6a0f,0x69f6,
+ 0x6a26,0x6a07,0x69f4,0x6a16,0x6b51,0x6ba5,0x6ba3,0x6ba2,0x6ba6,0x6c01,
+ 0x6c00,0x6bff,0x6c02,0x6f41,0x6f26,0x6f7e,0x6f87,0x6fc6,0x6f92,0x6f8d,
+ 0x6f89,0x6f8c,0x6f62,0x6f4f,0x6f85,0x6f5a,0x6f96,0x6f76,0x6f6c,0x6f82,
+ 0x6f55,0x6f72,0x6f52,0x6f50,0x6f57,0x6f94,0x6f93,0x6f5d,0x6f00,0x6f61,
+ 0x6f6b,0x6f7d,0x6f67,0x6f90,0x6f53,0x6f8b,0x6f69,0x6f7f,0x6f95,0x6f63,
+ 0x6f77,0x6f6a,0x6f7b,0x71b2,0x71af,0x719b,0x71b0,0x71a0,0x719a,0x71a9,
+ 0x71b5,0x719d,0x71a5,0x719e,0x71a4,0x71a1,0x71aa,0x719c,0x71a7,0x71b3,
+ 0x7298,0x729a,0x7358,0x7352
+ },
+ { /* ku 32 */
+ 0x735e,0x735f,0x7360,0x735d,0x735b,0x7361,0x735a,0x7359,0x7362,0x7487,
+ 0x7489,0x748a,0x7486,0x7481,0x747d,0x7485,0x7488,0x747c,0x7479,0x7508,
+ 0x7507,0x757e,0x7625,0x761e,0x7619,0x761d,0x761c,0x7623,0x761a,0x7628,
+ 0x761b,0x769c,0x769d,0x769e,0x769b,0x778d,0x778f,0x7789,0x7788,0x78cd,
+ 0x78bb,0x78cf,0x78cc,0x78d1,0x78ce,0x78d4,0x78c8,0x78c3,0x78c4,0x78c9,
+ 0x799a,0x79a1,0x79a0,0x799c,0x79a2,0x799b,0x6b76,0x7a39,0x7ab2,0x7ab4,
+ 0x7ab3,0x7bb7,0x7bcb,0x7bbe,0x7bac,0x7bce,0x7baf,0x7bb9,0x7bca,0x7bb5,
+ 0x7cc5,0x7cc8,0x7ccc,0x7ccb,0x7df7,0x7ddb,0x7dea,0x7de7,0x7dd7,0x7de1,
+ 0x7e03,0x7dfa,0x7de6,0x7df6,0x7df1,0x7df0,0x7dee,0x7ddf,0x7f76,0x7fac,
+ 0x7fb0,0x7fad,0x7fed,0x7feb
+ },
+ { /* ku 33 */
+ 0x7fea,0x7fec,0x7fe6,0x7fe8,0x8064,0x8067,0x81a3,0x819f,0x819e,0x8195,
+ 0x81a2,0x8199,0x8197,0x8216,0x824f,0x8253,0x8252,0x8250,0x824e,0x8251,
+ 0x8524,0x853b,0x850f,0x8500,0x8529,0x850e,0x8509,0x850d,0x851f,0x850a,
+ 0x8527,0x851c,0x84fb,0x852b,0x84fa,0x8508,0x850c,0x84f4,0x852a,0x84f2,
+ 0x8515,0x84f7,0x84eb,0x84f3,0x84fc,0x8512,0x84ea,0x84e9,0x8516,0x84fe,
+ 0x8528,0x851d,0x852e,0x8502,0x84fd,0x851e,0x84f6,0x8531,0x8526,0x84e7,
+ 0x84e8,0x84f0,0x84ef,0x84f9,0x8518,0x8520,0x8530,0x850b,0x8519,0x852f,
+ 0x8662,0x8756,0x8763,0x8764,0x8777,0x87e1,0x8773,0x8758,0x8754,0x875b,
+ 0x8752,0x8761,0x875a,0x8751,0x875e,0x876d,0x876a,0x8750,0x874e,0x875f,
+ 0x875d,0x876f,0x876c,0x877a
+ },
+ { /* ku 34 */
+ 0x876e,0x875c,0x8765,0x874f,0x877b,0x8775,0x8762,0x8767,0x8769,0x885a,
+ 0x8905,0x890c,0x8914,0x890b,0x8917,0x8918,0x8919,0x8906,0x8916,0x8911,
+ 0x890e,0x8909,0x89a2,0x89a4,0x89a3,0x89ed,0x89f0,0x89ec,0x8acf,0x8ac6,
+ 0x8ab8,0x8ad3,0x8ad1,0x8ad4,0x8ad5,0x8abb,0x8ad7,0x8abe,0x8ac0,0x8ac5,
+ 0x8ad8,0x8ac3,0x8aba,0x8abd,0x8ad9,0x8c3e,0x8c4d,0x8c8f,0x8ce5,0x8cdf,
+ 0x8cd9,0x8ce8,0x8cda,0x8cdd,0x8ce7,0x8da0,0x8d9c,0x8da1,0x8d9b,0x8e20,
+ 0x8e23,0x8e25,0x8e24,0x8e2e,0x8e15,0x8e1b,0x8e16,0x8e11,0x8e19,0x8e26,
+ 0x8e27,0x8e14,0x8e12,0x8e18,0x8e13,0x8e1c,0x8e17,0x8e1a,0x8f2c,0x8f24,
+ 0x8f18,0x8f1a,0x8f20,0x8f23,0x8f16,0x8f17,0x9073,0x9070,0x906f,0x9067,
+ 0x906b,0x912f,0x912b,0x9129
+ },
+ { /* ku 35 */
+ 0x912a,0x9132,0x9126,0x912e,0x9185,0x9186,0x918a,0x9181,0x9182,0x9184,
+ 0x9180,0x92d0,0x92c3,0x92c4,0x92c0,0x92d9,0x92b6,0x92cf,0x92f1,0x92df,
+ 0x92d8,0x92e9,0x92d7,0x92dd,0x92cc,0x92ef,0x92c2,0x92e8,0x92ca,0x92c8,
+ 0x92ce,0x92e6,0x92cd,0x92d5,0x92c9,0x92e0,0x92de,0x92e7,0x92d1,0x92d3,
+ 0x92b5,0x92e1,0x9325,0x92c6,0x92b4,0x957c,0x95ac,0x95ab,0x95ae,0x95b0,
+ 0x96a4,0x96a2,0x96d3,0x9705,0x9708,0x9702,0x975a,0x978a,0x978e,0x9788,
+ 0x97d0,0x97cf,0x981e,0x981d,0x9826,0x9829,0x9828,0x9820,0x981b,0x9827,
+ 0x98b2,0x9908,0x98fa,0x9911,0x9914,0x9916,0x9917,0x9915,0x99dc,0x99cd,
+ 0x99cf,0x99d3,0x99d4,0x99ce,0x99c9,0x99d6,0x99d8,0x99cb,0x99d7,0x99cc,
+ 0x9ab3,0x9aec,0x9aeb,0x9af3
+ },
+ { /* ku 36 */
+ 0x9af2,0x9af1,0x9b46,0x9b43,0x9b67,0x9b74,0x9b71,0x9b66,0x9b76,0x9b75,
+ 0x9b70,0x9b68,0x9b64,0x9b6c,0x9cfc,0x9cfa,0x9cfd,0x9cff,0x9cf7,0x9d07,
+ 0x9d00,0x9cf9,0x9cfb,0x9d08,0x9d05,0x9d04,0x9e83,0x9ed3,0x9f0f,0x9f10,
+ 0x511c,0x5113,0x5117,0x511a,0x5111,0x51de,0x5334,0x53e1,0x5670,0x5660,
+ 0x566e,0x5673,0x5666,0x5663,0x566d,0x5672,0x565e,0x5677,0x571c,0x571b,
+ 0x58c8,0x58bd,0x58c9,0x58bf,0x58ba,0x58c2,0x58bc,0x58c6,0x5b17,0x5b19,
+ 0x5b1b,0x5b21,0x5b14,0x5b13,0x5b10,0x5b16,0x5b28,0x5b1a,0x5b20,0x5b1e,
+ 0x5bef,0x5dac,0x5db1,0x5da9,0x5da7,0x5db5,0x5db0,0x5dae,0x5daa,0x5da8,
+ 0x5db2,0x5dad,0x5daf,0x5db4,0x5e67,0x5e68,0x5e66,0x5e6f,0x5ee9,0x5ee7,
+ 0x5ee6,0x5ee8,0x5ee5,0x5f4b
+ },
+ { /* ku 37 */
+ 0x5fbc,0x5fbb,0x619d,0x61a8,0x6196,0x61c5,0x61b4,0x61c6,0x61c1,0x61cc,
+ 0x61ba,0x61bf,0x61b8,0x618c,0x64d7,0x64d6,0x64d0,0x64cf,0x64c9,0x64bd,
+ 0x6489,0x64c3,0x64db,0x64f3,0x64d9,0x6533,0x657f,0x657c,0x65a2,0x66c8,
+ 0x66be,0x66c0,0x66ca,0x66cb,0x66cf,0x66bd,0x66bb,0x66ba,0x66cc,0x6723,
+ 0x6a34,0x6a66,0x6a49,0x6a67,0x6a32,0x6a68,0x6a3e,0x6a5d,0x6a6d,0x6a76,
+ 0x6a5b,0x6a51,0x6a28,0x6a5a,0x6a3b,0x6a3f,0x6a41,0x6a6a,0x6a64,0x6a50,
+ 0x6a4f,0x6a54,0x6a6f,0x6a69,0x6a60,0x6a3c,0x6a5e,0x6a56,0x6a55,0x6a4d,
+ 0x6a4e,0x6a46,0x6b55,0x6b54,0x6b56,0x6ba7,0x6baa,0x6bab,0x6bc8,0x6bc7,
+ 0x6c04,0x6c03,0x6c06,0x6fad,0x6fcb,0x6fa3,0x6fc7,0x6fbc,0x6fce,0x6fc8,
+ 0x6f5e,0x6fc4,0x6fbd,0x6f9e
+ },
+ { /* ku 38 */
+ 0x6fca,0x6fa8,0x7004,0x6fa5,0x6fae,0x6fba,0x6fac,0x6faa,0x6fcf,0x6fbf,
+ 0x6fb8,0x6fa2,0x6fc9,0x6fab,0x6fcd,0x6faf,0x6fb2,0x6fb0,0x71c5,0x71c2,
+ 0x71bf,0x71b8,0x71d6,0x71c0,0x71c1,0x71cb,0x71d4,0x71ca,0x71c7,0x71cf,
+ 0x71bd,0x71d8,0x71bc,0x71c6,0x71da,0x71db,0x729d,0x729e,0x7369,0x7366,
+ 0x7367,0x736c,0x7365,0x736b,0x736a,0x747f,0x749a,0x74a0,0x7494,0x7492,
+ 0x7495,0x74a1,0x750b,0x7580,0x762f,0x762d,0x7631,0x763d,0x7633,0x763c,
+ 0x7635,0x7632,0x7630,0x76bb,0x76e6,0x779a,0x779d,0x77a1,0x779c,0x779b,
+ 0x77a2,0x77a3,0x7795,0x7799,0x7797,0x78dd,0x78e9,0x78e5,0x78ea,0x78de,
+ 0x78e3,0x78db,0x78e1,0x78e2,0x78ed,0x78df,0x78e0,0x79a4,0x7a44,0x7a48,
+ 0x7a47,0x7ab6,0x7ab8,0x7ab5
+ },
+ { /* ku 39 */
+ 0x7ab1,0x7ab7,0x7bde,0x7be3,0x7be7,0x7bdd,0x7bd5,0x7be5,0x7bda,0x7be8,
+ 0x7bf9,0x7bd4,0x7bea,0x7be2,0x7bdc,0x7beb,0x7bd8,0x7bdf,0x7cd2,0x7cd4,
+ 0x7cd7,0x7cd0,0x7cd1,0x7e12,0x7e21,0x7e17,0x7e0c,0x7e1f,0x7e20,0x7e13,
+ 0x7e0e,0x7e1c,0x7e15,0x7e1a,0x7e22,0x7e0b,0x7e0f,0x7e16,0x7e0d,0x7e14,
+ 0x7e25,0x7e24,0x7f43,0x7f7b,0x7f7c,0x7f7a,0x7fb1,0x7fef,0x802a,0x8029,
+ 0x806c,0x81b1,0x81a6,0x81ae,0x81b9,0x81b5,0x81ab,0x81b0,0x81ac,0x81b4,
+ 0x81b2,0x81b7,0x81a7,0x81f2,0x8255,0x8256,0x8257,0x8556,0x8545,0x856b,
+ 0x854d,0x8553,0x8561,0x8558,0x8540,0x8546,0x8564,0x8541,0x8562,0x8544,
+ 0x8551,0x8547,0x8563,0x853e,0x855b,0x8571,0x854e,0x856e,0x8575,0x8555,
+ 0x8567,0x8560,0x858c,0x8566
+ },
+ { /* ku 3a */
+ 0x855d,0x8554,0x8565,0x856c,0x8663,0x8665,0x8664,0x87a4,0x879b,0x878f,
+ 0x8797,0x8793,0x8792,0x8788,0x8781,0x8796,0x8798,0x8779,0x8787,0x87a3,
+ 0x8785,0x8790,0x8791,0x879d,0x8784,0x8794,0x879c,0x879a,0x8789,0x891e,
+ 0x8926,0x8930,0x892d,0x892e,0x8927,0x8931,0x8922,0x8929,0x8923,0x892f,
+ 0x892c,0x891f,0x89f1,0x8ae0,0x8ae2,0x8af2,0x8af4,0x8af5,0x8add,0x8b14,
+ 0x8ae4,0x8adf,0x8af0,0x8ac8,0x8ade,0x8ae1,0x8ae8,0x8aff,0x8aef,0x8afb,
+ 0x8c91,0x8c92,0x8c90,0x8cf5,0x8cee,0x8cf1,0x8cf0,0x8cf3,0x8d6c,0x8d6e,
+ 0x8da5,0x8da7,0x8e33,0x8e3e,0x8e38,0x8e40,0x8e45,0x8e36,0x8e3c,0x8e3d,
+ 0x8e41,0x8e30,0x8e3f,0x8ebd,0x8f36,0x8f2e,0x8f35,0x8f32,0x8f39,0x8f37,
+ 0x8f34,0x9076,0x9079,0x907b
+ },
+ { /* ku 3b */
+ 0x9086,0x90fa,0x9133,0x9135,0x9136,0x9193,0x9190,0x9191,0x918d,0x918f,
+ 0x9327,0x931e,0x9308,0x931f,0x9306,0x930f,0x937a,0x9338,0x933c,0x931b,
+ 0x9323,0x9312,0x9301,0x9346,0x932d,0x930e,0x930d,0x92cb,0x931d,0x92fa,
+ 0x9313,0x92f9,0x92f7,0x9334,0x9302,0x9324,0x92ff,0x9329,0x9339,0x9335,
+ 0x932a,0x9314,0x930c,0x930b,0x92fe,0x9309,0x9300,0x92fb,0x9316,0x95bc,
+ 0x95cd,0x95be,0x95b9,0x95ba,0x95b6,0x95bf,0x95b5,0x95bd,0x96a9,0x96d4,
+ 0x970b,0x9712,0x9710,0x9799,0x9797,0x9794,0x97f0,0x97f8,0x9835,0x982f,
+ 0x9832,0x9924,0x991f,0x9927,0x9929,0x999e,0x99ee,0x99ec,0x99e5,0x99e4,
+ 0x99f0,0x99e3,0x99ea,0x99e9,0x99e7,0x9ab9,0x9abf,0x9ab4,0x9abb,0x9af6,
+ 0x9afa,0x9af9,0x9af7,0x9b33
+ },
+ { /* ku 3c */
+ 0x9b80,0x9b85,0x9b87,0x9b7c,0x9b7e,0x9b7b,0x9b82,0x9b93,0x9b92,0x9b90,
+ 0x9b7a,0x9b95,0x9b7d,0x9b88,0x9d25,0x9d17,0x9d20,0x9d1e,0x9d14,0x9d29,
+ 0x9d1d,0x9d18,0x9d22,0x9d10,0x9d19,0x9d1f,0x9e88,0x9e86,0x9e87,0x9eae,
+ 0x9ead,0x9ed5,0x9ed6,0x9efa,0x9f12,0x9f3d,0x5126,0x5125,0x5122,0x5124,
+ 0x5120,0x5129,0x52f4,0x5693,0x568c,0x568d,0x5686,0x5684,0x5683,0x567e,
+ 0x5682,0x567f,0x5681,0x58d6,0x58d4,0x58cf,0x58d2,0x5b2d,0x5b25,0x5b32,
+ 0x5b23,0x5b2c,0x5b27,0x5b26,0x5b2f,0x5b2e,0x5b7b,0x5bf1,0x5bf2,0x5db7,
+ 0x5e6c,0x5e6a,0x5fbe,0x61c3,0x61b5,0x61bc,0x61e7,0x61e0,0x61e5,0x61e4,
+ 0x61e8,0x61de,0x64ef,0x64e9,0x64e3,0x64eb,0x64e4,0x64e8,0x6581,0x6580,
+ 0x65b6,0x65da,0x66d2,0x6a8d
+ },
+ { /* ku 3d */
+ 0x6a96,0x6a81,0x6aa5,0x6a89,0x6a9f,0x6a9b,0x6aa1,0x6a9e,0x6a87,0x6a93,
+ 0x6a8e,0x6a95,0x6a83,0x6aa8,0x6aa4,0x6a91,0x6a7f,0x6aa6,0x6a9a,0x6a85,
+ 0x6a8c,0x6a92,0x6b5b,0x6bad,0x6c09,0x6fcc,0x6fa9,0x6ff4,0x6fd4,0x6fe3,
+ 0x6fdc,0x6fed,0x6fe7,0x6fe6,0x6fde,0x6ff2,0x6fdd,0x6fe2,0x6fe8,0x71e1,
+ 0x71f1,0x71e8,0x71f2,0x71e4,0x71f0,0x71e2,0x7373,0x736e,0x736f,0x7497,
+ 0x74b2,0x74ab,0x7490,0x74aa,0x74ad,0x74b1,0x74a5,0x74af,0x7510,0x7511,
+ 0x7512,0x750f,0x7584,0x7643,0x7648,0x7649,0x7647,0x76a4,0x76e9,0x77b5,
+ 0x77ab,0x77b2,0x77b7,0x77b6,0x77b4,0x77b1,0x77a8,0x77f0,0x78f3,0x78fd,
+ 0x7902,0x78fb,0x78fc,0x78ff,0x78f2,0x7905,0x78f9,0x78fe,0x7904,0x79ab,
+ 0x79a8,0x7a5c,0x7a5b,0x7a56
+ },
+ { /* ku 3e */
+ 0x7a58,0x7a54,0x7a5a,0x7abe,0x7ac0,0x7ac1,0x7c05,0x7c0f,0x7bf2,0x7c00,
+ 0x7bff,0x7bfb,0x7c0e,0x7bf4,0x7c0b,0x7bf3,0x7c02,0x7c09,0x7c03,0x7c01,
+ 0x7bf8,0x7bfd,0x7c06,0x7bf0,0x7bf1,0x7c10,0x7c0a,0x7ce8,0x7e2d,0x7e3c,
+ 0x7e42,0x7e33,0x9848,0x7e38,0x7e2a,0x7e49,0x7e40,0x7e47,0x7e29,0x7e4c,
+ 0x7e30,0x7e3b,0x7e36,0x7e44,0x7e3a,0x7f45,0x7f7f,0x7f7e,0x7f7d,0x7ff4,
+ 0x7ff2,0x802c,0x81bb,0x81c4,0x81cc,0x81ca,0x81c5,0x81c7,0x81bc,0x81e9,
+ 0x825b,0x825a,0x825c,0x8583,0x8580,0x858f,0x85a7,0x8595,0x85a0,0x858b,
+ 0x85a3,0x857b,0x85a4,0x859a,0x859e,0x8577,0x857c,0x8589,0x85a1,0x857a,
+ 0x8578,0x8557,0x858e,0x8596,0x8586,0x858d,0x8599,0x859d,0x8581,0x85a2,
+ 0x8582,0x8588,0x8585,0x8579
+ },
+ { /* ku 3f */
+ 0x8576,0x8598,0x8590,0x859f,0x8668,0x87be,0x87aa,0x87ad,0x87c5,0x87b0,
+ 0x87ac,0x87b9,0x87b5,0x87bc,0x87ae,0x87c9,0x87c3,0x87c2,0x87cc,0x87b7,
+ 0x87af,0x87c4,0x87ca,0x87b4,0x87b6,0x87bf,0x87b8,0x87bd,0x87de,0x87b2,
+ 0x8935,0x8933,0x893c,0x893e,0x8941,0x8952,0x8937,0x8942,0x89ad,0x89af,
+ 0x89ae,0x89f2,0x89f3,0x8b1e,0x8b18,0x8b16,0x8b11,0x8b05,0x8b0b,0x8b22,
+ 0x8b0f,0x8b12,0x8b15,0x8b07,0x8b0d,0x8b08,0x8b06,0x8b1c,0x8b13,0x8b1a,
+ 0x8c4f,0x8c70,0x8c72,0x8c71,0x8c6f,0x8c95,0x8c94,0x8cf9,0x8d6f,0x8e4e,
+ 0x8e4d,0x8e53,0x8e50,0x8e4c,0x8e47,0x8f43,0x8f40,0x9085,0x907e,0x9138,
+ 0x919a,0x91a2,0x919b,0x9199,0x919f,0x91a1,0x919d,0x91a0,0x93a1,0x9383,
+ 0x93af,0x9364,0x9356,0x9347
+ },
+ { /* ku 40 */
+ 0x937c,0x9358,0x935c,0x9376,0x9349,0x9350,0x9351,0x9360,0x936d,0x938f,
+ 0x934c,0x936a,0x9379,0x9357,0x9355,0x9352,0x934f,0x9371,0x9377,0x937b,
+ 0x9361,0x935e,0x9363,0x9367,0x934e,0x9359,0x95c7,0x95c0,0x95c9,0x95c3,
+ 0x95c5,0x95b7,0x96ae,0x96b0,0x96ac,0x9720,0x971f,0x9718,0x971d,0x9719,
+ 0x979a,0x97a1,0x979c,0x979e,0x979d,0x97d5,0x97d4,0x97f1,0x9841,0x9844,
+ 0x984a,0x9849,0x9845,0x9843,0x9925,0x992b,0x992c,0x992a,0x9933,0x9932,
+ 0x992f,0x992d,0x9931,0x9930,0x9998,0x99a3,0x99a1,0x9a02,0x99fa,0x99f4,
+ 0x99f7,0x99f9,0x99f8,0x99f6,0x99fb,0x99fd,0x99fe,0x99fc,0x9a03,0x9abe,
+ 0x9afe,0x9afd,0x9b01,0x9afc,0x9b48,0x9b9a,0x9ba8,0x9b9e,0x9b9b,0x9ba6,
+ 0x9ba1,0x9ba5,0x9ba4,0x9b86
+ },
+ { /* ku 41 */
+ 0x9ba2,0x9ba0,0x9baf,0x9d33,0x9d41,0x9d67,0x9d36,0x9d2e,0x9d2f,0x9d31,
+ 0x9d38,0x9d30,0x9d45,0x9d42,0x9d43,0x9d3e,0x9d37,0x9d40,0x9d3d,0x7ff5,
+ 0x9d2d,0x9e8a,0x9e89,0x9e8d,0x9eb0,0x9ec8,0x9eda,0x9efb,0x9eff,0x9f24,
+ 0x9f23,0x9f22,0x9f54,0x9fa0,0x5131,0x512d,0x512e,0x5698,0x569c,0x5697,
+ 0x569a,0x569d,0x5699,0x5970,0x5b3c,0x5c69,0x5c6a,0x5dc0,0x5e6d,0x5e6e,
+ 0x61d8,0x61df,0x61ed,0x61ee,0x61f1,0x61ea,0x61f0,0x61eb,0x61d6,0x61e9,
+ 0x64ff,0x6504,0x64fd,0x64f8,0x6501,0x6503,0x64fc,0x6594,0x65db,0x66da,
+ 0x66db,0x66d8,0x6ac5,0x6ab9,0x6abd,0x6ae1,0x6ac6,0x6aba,0x6ab6,0x6ab7,
+ 0x6ac7,0x6ab4,0x6aad,0x6b5e,0x6bc9,0x6c0b,0x7007,0x700c,0x700d,0x7001,
+ 0x7005,0x7014,0x700e,0x6fff
+ },
+ { /* ku 42 */
+ 0x7000,0x6ffb,0x7026,0x6ffc,0x6ff7,0x700a,0x7201,0x71ff,0x71f9,0x7203,
+ 0x71fd,0x7376,0x74b8,0x74c0,0x74b5,0x74c1,0x74be,0x74b6,0x74bb,0x74c2,
+ 0x7514,0x7513,0x765c,0x7664,0x7659,0x7650,0x7653,0x7657,0x765a,0x76a6,
+ 0x76bd,0x76ec,0x77c2,0x77ba,0x790c,0x7913,0x7914,0x7909,0x7910,0x7912,
+ 0x7911,0x79ad,0x79ac,0x7a5f,0x7c1c,0x7c29,0x7c19,0x7c20,0x7c1f,0x7c2d,
+ 0x7c1d,0x7c26,0x7c28,0x7c22,0x7c25,0x7c30,0x7e5c,0x7e50,0x7e56,0x7e63,
+ 0x7e58,0x7e62,0x7e5f,0x7e51,0x7e60,0x7e57,0x7e53,0x7fb5,0x7fb3,0x7ff7,
+ 0x7ff8,0x8075,0x81d1,0x81d2,0x81d0,0x825f,0x825e,0x85b4,0x85c6,0x85c0,
+ 0x85c3,0x85c2,0x85b3,0x85b5,0x85bd,0x85c7,0x85c4,0x85bf,0x85cb,0x85ce,
+ 0x85c8,0x85c5,0x85b1,0x85b6
+ },
+ { /* ku 43 */
+ 0x85d2,0x8624,0x85b8,0x85b7,0x85be,0x8669,0x87e7,0x87e6,0x87e2,0x87db,
+ 0x87eb,0x87ea,0x87e5,0x87df,0x87f3,0x87e4,0x87d4,0x87dc,0x87d3,0x87ed,
+ 0x87d8,0x87e3,0x87d7,0x87d9,0x8801,0x87f4,0x87e8,0x87dd,0x8953,0x894b,
+ 0x894f,0x894c,0x8946,0x8950,0x8951,0x8949,0x8b2a,0x8b27,0x8b23,0x8b33,
+ 0x8b30,0x8b35,0x8b47,0x8b2f,0x8b3c,0x8b3e,0x8b31,0x8b25,0x8b37,0x8b26,
+ 0x8b36,0x8b2e,0x8b24,0x8b3b,0x8b3d,0x8b3a,0x8c42,0x8c75,0x8c99,0x8c98,
+ 0x8c97,0x8cfe,0x8d04,0x8d02,0x8d00,0x8e5c,0x8e62,0x8e60,0x8e57,0x8e56,
+ 0x8e5e,0x8e65,0x8e67,0x8e5b,0x8e5a,0x8e61,0x8e5d,0x8e69,0x8e54,0x8f46,
+ 0x8f47,0x8f48,0x8f4b,0x9128,0x913a,0x913b,0x913e,0x91a8,0x91a5,0x91a7,
+ 0x91af,0x91aa,0x93b5,0x938c
+ },
+ { /* ku 44 */
+ 0x9392,0x93b7,0x939b,0x939d,0x9389,0x93a7,0x938e,0x93aa,0x939e,0x93a6,
+ 0x9395,0x9388,0x9399,0x939f,0x9380,0x938d,0x93b1,0x9391,0x93b2,0x93a4,
+ 0x93a8,0x93b4,0x93a3,0x95d2,0x95d3,0x95d1,0x96b3,0x96d7,0x96da,0x5dc2,
+ 0x96df,0x96d8,0x96dd,0x9723,0x9722,0x9725,0x97ac,0x97ae,0x97a8,0x97ab,
+ 0x97a4,0x97aa,0x97a2,0x97a5,0x97d7,0x97d9,0x97d6,0x97d8,0x97fa,0x9850,
+ 0x9851,0x9852,0x98b8,0x9941,0x993c,0x993a,0x9a0f,0x9a0b,0x9a09,0x9a0d,
+ 0x9a04,0x9a11,0x9a0a,0x9a05,0x9a07,0x9a06,0x9ac0,0x9adc,0x9b08,0x9b04,
+ 0x9b05,0x9b29,0x9b35,0x9b4a,0x9b4c,0x9b4b,0x9bc7,0x9bc6,0x9bc3,0x9bbf,
+ 0x9bc1,0x9bb5,0x9bb8,0x9bd3,0x9bb6,0x9bc4,0x9bb9,0x9bbd,0x9d5c,0x9d53,
+ 0x9d4f,0x9d4a,0x9d5b,0x9d4b
+ },
+ { /* ku 45 */
+ 0x9d59,0x9d56,0x9d4c,0x9d57,0x9d52,0x9d54,0x9d5f,0x9d58,0x9d5a,0x9e8e,
+ 0x9e8c,0x9edf,0x9f01,0x9f00,0x9f16,0x9f25,0x9f2b,0x9f2a,0x9f29,0x9f28,
+ 0x9f4c,0x9f55,0x5134,0x5135,0x5296,0x52f7,0x53b4,0x56ab,0x56ad,0x56a6,
+ 0x56a7,0x56aa,0x56ac,0x58da,0x58dd,0x58db,0x5912,0x5b3d,0x5b3e,0x5b3f,
+ 0x5dc3,0x5e70,0x5fbf,0x61fb,0x6507,0x6510,0x650d,0x6509,0x650c,0x650e,
+ 0x6584,0x65de,0x65dd,0x66de,0x6ae7,0x6ae0,0x6acc,0x6ad1,0x6ad9,0x6acb,
+ 0x6adf,0x6adc,0x6ad0,0x6aeb,0x6acf,0x6acd,0x6ade,0x6b60,0x6bb0,0x6c0c,
+ 0x7019,0x7027,0x7020,0x7016,0x702b,0x7021,0x7022,0x7023,0x7029,0x7017,
+ 0x7024,0x701c,0x720c,0x720a,0x7207,0x7202,0x7205,0x72a5,0x72a6,0x72a4,
+ 0x72a3,0x72a1,0x74cb,0x74c5
+ },
+ { /* ku 46 */
+ 0x74b7,0x74c3,0x7516,0x7660,0x77c9,0x77ca,0x77c4,0x77f1,0x791d,0x791b,
+ 0x7921,0x791c,0x7917,0x791e,0x79b0,0x7a67,0x7a68,0x7c33,0x7c3c,0x7c39,
+ 0x7c2c,0x7c3b,0x7cec,0x7cea,0x7e76,0x7e75,0x7e78,0x7e70,0x7e77,0x7e6f,
+ 0x7e7a,0x7e72,0x7e74,0x7e68,0x7f4b,0x7f4a,0x7f83,0x7f86,0x7fb7,0x7ffd,
+ 0x7ffe,0x8078,0x81d7,0x81d5,0x820b,0x8264,0x8261,0x8263,0x85eb,0x85f1,
+ 0x85ed,0x85d9,0x85e1,0x85e8,0x85da,0x85d7,0x85ec,0x85f2,0x85f8,0x85d8,
+ 0x85df,0x85e3,0x85dc,0x85d1,0x85f0,0x85e6,0x85ef,0x85de,0x85e2,0x8800,
+ 0x87fa,0x8803,0x87f6,0x87f7,0x8809,0x880c,0x880b,0x8806,0x87fc,0x8808,
+ 0x87ff,0x880a,0x8802,0x8962,0x895a,0x895b,0x8957,0x8961,0x895c,0x8958,
+ 0x895d,0x8959,0x8988,0x89b7
+ },
+ { /* ku 47 */
+ 0x89b6,0x89f6,0x8b50,0x8b48,0x8b4a,0x8b40,0x8b53,0x8b56,0x8b54,0x8b4b,
+ 0x8b55,0x8b51,0x8b42,0x8b52,0x8b57,0x8c43,0x8c77,0x8c76,0x8c9a,0x8d06,
+ 0x8d07,0x8d09,0x8dac,0x8daa,0x8dad,0x8dab,0x8e6d,0x8e78,0x8e73,0x8e6a,
+ 0x8e6f,0x8e7b,0x8ec2,0x8f52,0x8f51,0x8f4f,0x8f50,0x8f53,0x8fb4,0x9140,
+ 0x913f,0x91b0,0x91ad,0x93de,0x93c7,0x93cf,0x93c2,0x93da,0x93d0,0x93f9,
+ 0x93ec,0x93cc,0x93d9,0x93a9,0x93e6,0x93ca,0x93d4,0x93ee,0x93e3,0x93d5,
+ 0x93c4,0x93ce,0x93c0,0x93d2,0x93a5,0x93e7,0x957d,0x95da,0x95db,0x96e1,
+ 0x9729,0x972b,0x972c,0x9728,0x9726,0x97b3,0x97b7,0x97b6,0x97dd,0x97de,
+ 0x97df,0x985c,0x9859,0x985d,0x9857,0x98bf,0x98bd,0x98bb,0x98be,0x9948,
+ 0x9947,0x9943,0x99a6,0x99a7
+ },
+ { /* ku 48 */
+ 0x9a1a,0x9a15,0x9a25,0x9a1d,0x9a24,0x9a1b,0x9a22,0x9a20,0x9a27,0x9a23,
+ 0x9a1e,0x9a1c,0x9a14,0x9ac2,0x9b0b,0x9b0a,0x9b0e,0x9b0c,0x9b37,0x9bea,
+ 0x9beb,0x9be0,0x9bde,0x9be4,0x9be6,0x9be2,0x9bf0,0x9bd4,0x9bd7,0x9bec,
+ 0x9bdc,0x9bd9,0x9be5,0x9bd5,0x9be1,0x9bda,0x9d77,0x9d81,0x9d8a,0x9d84,
+ 0x9d88,0x9d71,0x9d80,0x9d78,0x9d86,0x9d8b,0x9d8c,0x9d7d,0x9d6b,0x9d74,
+ 0x9d75,0x9d70,0x9d69,0x9d85,0x9d73,0x9d7b,0x9d82,0x9d6f,0x9d79,0x9d7f,
+ 0x9d87,0x9d68,0x9e94,0x9e91,0x9ec0,0x9efc,0x9f2d,0x9f40,0x9f41,0x9f4d,
+ 0x9f56,0x9f57,0x9f58,0x5337,0x56b2,0x56b5,0x56b3,0x58e3,0x5b45,0x5dc6,
+ 0x5dc7,0x5eee,0x5eef,0x5fc0,0x5fc1,0x61f9,0x6517,0x6516,0x6515,0x6513,
+ 0x65df,0x66e8,0x66e3,0x66e4
+ },
+ { /* ku 49 */
+ 0x6af3,0x6af0,0x6aea,0x6ae8,0x6af9,0x6af1,0x6aee,0x6aef,0x703c,0x7035,
+ 0x702f,0x7037,0x7034,0x7031,0x7042,0x7038,0x703f,0x703a,0x7039,0x702a,
+ 0x7040,0x703b,0x7033,0x7041,0x7213,0x7214,0x72a8,0x737d,0x737c,0x74ba,
+ 0x76ab,0x76aa,0x76be,0x76ed,0x77cc,0x77ce,0x77cf,0x77cd,0x77f2,0x7925,
+ 0x7923,0x7927,0x7928,0x7924,0x7929,0x79b2,0x7a6e,0x7a6c,0x7a6d,0x7af7,
+ 0x7c49,0x7c48,0x7c4a,0x7c47,0x7c45,0x7cee,0x7e7b,0x7e7e,0x7e81,0x7e80,
+ 0x7fba,0x7fff,0x8079,0x81db,0x81d9,0x8268,0x8269,0x8622,0x85ff,0x8601,
+ 0x85fe,0x861b,0x8600,0x85f6,0x8604,0x8609,0x8605,0x860c,0x85fd,0x8819,
+ 0x8810,0x8811,0x8817,0x8813,0x8816,0x8963,0x8966,0x89b9,0x89f7,0x8b60,
+ 0x8b6a,0x8b5d,0x8b68,0x8b63
+ },
+ { /* ku 4a */
+ 0x8b65,0x8b67,0x8b6d,0x8dae,0x8e86,0x8e88,0x8e84,0x8f59,0x8f56,0x8f57,
+ 0x8f55,0x8f58,0x8f5a,0x908d,0x9143,0x9141,0x91b7,0x91b5,0x91b2,0x91b3,
+ 0x940b,0x9413,0x93fb,0x9420,0x940f,0x9414,0x93fe,0x9415,0x9410,0x9428,
+ 0x9419,0x940d,0x93f5,0x9400,0x93f7,0x9407,0x940e,0x9416,0x9412,0x93fa,
+ 0x9409,0x93f8,0x943c,0x940a,0x93ff,0x93fc,0x940c,0x93f6,0x9411,0x9406,
+ 0x95de,0x95e0,0x95df,0x972e,0x972f,0x97b9,0x97bb,0x97fd,0x97fe,0x9860,
+ 0x9862,0x9863,0x985f,0x98c1,0x98c2,0x9950,0x994e,0x9959,0x994c,0x994b,
+ 0x9953,0x9a32,0x9a34,0x9a31,0x9a2c,0x9a2a,0x9a36,0x9a29,0x9a2e,0x9a38,
+ 0x9a2d,0x9ac7,0x9aca,0x9ac6,0x9b10,0x9b12,0x9b11,0x9c0b,0x9c08,0x9bf7,
+ 0x9c05,0x9c12,0x9bf8,0x9c40
+ },
+ { /* ku 4b */
+ 0x9c07,0x9c0e,0x9c06,0x9c17,0x9c14,0x9c09,0x9d9f,0x9d99,0x9da4,0x9d9d,
+ 0x9d92,0x9d98,0x9d90,0x9d9b,0x9da0,0x9d94,0x9d9c,0x9daa,0x9d97,0x9da1,
+ 0x9d9a,0x9da2,0x9da8,0x9d9e,0x9da3,0x9dbf,0x9da9,0x9d96,0x9da6,0x9da7,
+ 0x9e99,0x9e9b,0x9e9a,0x9ee5,0x9ee4,0x9ee7,0x9ee6,0x9f30,0x9f2e,0x9f5b,
+ 0x9f60,0x9f5e,0x9f5d,0x9f59,0x9f91,0x513a,0x5139,0x5298,0x5297,0x56c3,
+ 0x56bd,0x56be,0x5b48,0x5b47,0x5dcb,0x5dcf,0x5ef1,0x61fd,0x651b,0x6b02,
+ 0x6afc,0x6b03,0x6af8,0x6b00,0x7043,0x7044,0x704a,0x7048,0x7049,0x7045,
+ 0x7046,0x721d,0x721a,0x7219,0x737e,0x7517,0x766a,0x77d0,0x792d,0x7931,
+ 0x792f,0x7c54,0x7c53,0x7cf2,0x7e8a,0x7e87,0x7e88,0x7e8b,0x7e86,0x7e8d,
+ 0x7f4d,0x7fbb,0x8030,0x81dd
+ },
+ { /* ku 4c */
+ 0x8618,0x862a,0x8626,0x861f,0x8623,0x861c,0x8619,0x8627,0x862e,0x8621,
+ 0x8620,0x8629,0x861e,0x8625,0x8829,0x881d,0x881b,0x8820,0x8824,0x881c,
+ 0x882b,0x884a,0x896d,0x8969,0x896e,0x896b,0x89fa,0x8b79,0x8b78,0x8b45,
+ 0x8b7a,0x8b7b,0x8d10,0x8d14,0x8daf,0x8e8e,0x8e8c,0x8f5e,0x8f5b,0x8f5d,
+ 0x9146,0x9144,0x9145,0x91b9,0x943f,0x943b,0x9436,0x9429,0x943d,0x9430,
+ 0x9439,0x942a,0x9437,0x942c,0x9440,0x9431,0x95e5,0x95e4,0x95e3,0x9735,
+ 0x973a,0x97bf,0x97e1,0x9864,0x98c9,0x98c6,0x98c0,0x9958,0x9956,0x9a39,
+ 0x9a3d,0x9a46,0x9a44,0x9a42,0x9a41,0x9a3a,0x9a3f,0x9acd,0x9b15,0x9b17,
+ 0x9b18,0x9b16,0x9b3a,0x9b52,0x9c2b,0x9c1d,0x9c1c,0x9c2c,0x9c23,0x9c28,
+ 0x9c29,0x9c24,0x9c21,0x9db7
+ },
+ { /* ku 4d */
+ 0x9db6,0x9dbc,0x9dc1,0x9dc7,0x9dca,0x9dcf,0x9dbe,0x9dc5,0x9dc3,0x9dbb,
+ 0x9db5,0x9dce,0x9db9,0x9dba,0x9dac,0x9dc8,0x9db1,0x9dad,0x9dcc,0x9db3,
+ 0x9dcd,0x9db2,0x9e7a,0x9e9c,0x9eeb,0x9eee,0x9eed,0x9f1b,0x9f18,0x9f1a,
+ 0x9f31,0x9f4e,0x9f65,0x9f64,0x9f92,0x4eb9,0x56c6,0x56c5,0x56cb,0x5971,
+ 0x5b4b,0x5b4c,0x5dd5,0x5dd1,0x5ef2,0x6521,0x6520,0x6526,0x6522,0x6b0b,
+ 0x6b08,0x6b09,0x6c0d,0x7055,0x7056,0x7057,0x7052,0x721e,0x721f,0x72a9,
+ 0x737f,0x74d8,0x74d5,0x74d9,0x74d7,0x766d,0x76ad,0x7935,0x79b4,0x7a70,
+ 0x7a71,0x7c57,0x7c5c,0x7c59,0x7c5b,0x7c5a,0x7cf4,0x7cf1,0x7e91,0x7f4f,
+ 0x7f87,0x81de,0x826b,0x8634,0x8635,0x8633,0x862c,0x8632,0x8636,0x882c,
+ 0x8828,0x8826,0x882a,0x8825
+ },
+ { /* ku 4e */
+ 0x8971,0x89bf,0x89be,0x89fb,0x8b7e,0x8b84,0x8b82,0x8b86,0x8b85,0x8b7f,
+ 0x8d15,0x8e95,0x8e94,0x8e9a,0x8e92,0x8e90,0x8e96,0x8e97,0x8f60,0x8f62,
+ 0x9147,0x944c,0x9450,0x944a,0x944b,0x944f,0x9447,0x9445,0x9448,0x9449,
+ 0x9446,0x973f,0x97e3,0x986a,0x9869,0x98cb,0x9954,0x995b,0x9a4e,0x9a53,
+ 0x9a54,0x9a4c,0x9a4f,0x9a48,0x9a4a,0x9a49,0x9a52,0x9a50,0x9ad0,0x9b19,
+ 0x9b2b,0x9b3b,0x9b56,0x9b55,0x9c46,0x9c48,0x9c3f,0x9c44,0x9c39,0x9c33,
+ 0x9c41,0x9c3c,0x9c37,0x9c34,0x9c32,0x9c3d,0x9c36,0x9ddb,0x9dd2,0x9dde,
+ 0x9dda,0x9dcb,0x9dd0,0x9ddc,0x9dd1,0x9ddf,0x9de9,0x9dd9,0x9dd8,0x9dd6,
+ 0x9df5,0x9dd5,0x9ddd,0x9eb6,0x9ef0,0x9f35,0x9f33,0x9f32,0x9f42,0x9f6b,
+ 0x9f95,0x9fa2,0x513d,0x5299
+ },
+ { /* ku 4f */
+ 0x58e8,0x58e7,0x5972,0x5b4d,0x5dd8,0x882f,0x5f4f,0x6201,0x6203,0x6204,
+ 0x6529,0x6525,0x6596,0x66eb,0x6b11,0x6b12,0x6b0f,0x6bca,0x705b,0x705a,
+ 0x7222,0x7382,0x7381,0x7383,0x7670,0x77d4,0x7c67,0x7c66,0x7e95,0x826c,
+ 0x863a,0x8640,0x8639,0x863c,0x8631,0x863b,0x863e,0x8830,0x8832,0x882e,
+ 0x8833,0x8976,0x8974,0x8973,0x89fe,0x8b8c,0x8b8e,0x8b8b,0x8b88,0x8c45,
+ 0x8d19,0x8e98,0x8f64,0x8f63,0x91bc,0x9462,0x9455,0x945d,0x9457,0x945e,
+ 0x97c4,0x97c5,0x9800,0x9a56,0x9a59,0x9b1e,0x9b1f,0x9b20,0x9c52,0x9c58,
+ 0x9c50,0x9c4a,0x9c4d,0x9c4b,0x9c55,0x9c59,0x9c4c,0x9c4e,0x9dfb,0x9df7,
+ 0x9def,0x9de3,0x9deb,0x9df8,0x9de4,0x9df6,0x9de1,0x9dee,0x9de6,0x9df2,
+ 0x9df0,0x9de2,0x9dec,0x9df4
+ },
+ { /* ku 50 */
+ 0x9df3,0x9de8,0x9ded,0x9ec2,0x9ed0,0x9ef2,0x9ef3,0x9f06,0x9f1c,0x9f38,
+ 0x9f37,0x9f36,0x9f43,0x9f4f,0x9f71,0x9f70,0x9f6e,0x9f6f,0x56d3,0x56cd,
+ 0x5b4e,0x5c6d,0x652d,0x66ed,0x66ee,0x6b13,0x705f,0x7061,0x705d,0x7060,
+ 0x7223,0x74db,0x74e5,0x77d5,0x7938,0x79b7,0x79b6,0x7c6a,0x7e97,0x7f89,
+ 0x826d,0x8643,0x8838,0x8837,0x8835,0x884b,0x8b94,0x8b95,0x8e9e,0x8e9f,
+ 0x8ea0,0x8e9d,0x91be,0x91bd,0x91c2,0x946b,0x9468,0x9469,0x96e5,0x9746,
+ 0x9743,0x9747,0x97c7,0x97e5,0x9a5e,0x9ad5,0x9b59,0x9c63,0x9c67,0x9c66,
+ 0x9c62,0x9c5e,0x9c60,0x9e02,0x9dfe,0x9e07,0x9e03,0x9e06,0x9e05,0x9e00,
+ 0x9e01,0x9e09,0x9dff,0x9dfd,0x9e04,0x9ea0,0x9f1e,0x9f46,0x9f74,0x9f75,
+ 0x9f76,0x56d4,0x652e,0x65b8
+ },
+ { /* ku 51 */
+ 0x6b18,0x6b19,0x6b17,0x6b1a,0x7062,0x7226,0x72aa,0x77d8,0x77d9,0x7939,
+ 0x7c69,0x7c6b,0x7cf6,0x7e9a,0x7e98,0x7e9b,0x7e99,0x81e0,0x81e1,0x8646,
+ 0x8647,0x8648,0x8979,0x897a,0x897c,0x897b,0x89ff,0x8b98,0x8b99,0x8ea5,
+ 0x8ea4,0x8ea3,0x946e,0x946d,0x946f,0x9471,0x9473,0x9749,0x9872,0x995f,
+ 0x9c68,0x9c6e,0x9c6d,0x9e0b,0x9e0d,0x9e10,0x9e0f,0x9e12,0x9e11,0x9ea1,
+ 0x9ef5,0x9f09,0x9f47,0x9f78,0x9f7b,0x9f7a,0x9f79,0x571e,0x7066,0x7c6f,
+ 0x883c,0x8db2,0x8ea6,0x91c3,0x9474,0x9478,0x9476,0x9475,0x9a60,0x9b2e,
+ 0x9c74,0x9c73,0x9c71,0x9c75,0x9e14,0x9e13,0x9ef6,0x9f0a,0x9fa4,0x7068,
+ 0x7065,0x7cf7,0x866a,0x883e,0x883d,0x883f,0x8b9e,0x8c9c,0x8ea9,0x8ec9,
+ 0x974b,0x9873,0x9874,0x98cc
+ },
+ { /* ku 52 */
+ 0x9961,0x99ab,0x9a64,0x9a66,0x9a67,0x9b24,0x9e15,0x9e17,0x9f48,0x6207,
+ 0x6b1e,0x7227,0x864c,0x8ea8,0x9482,0x9480,0x9481,0x9a69,0x9a68,0x9e19,
+ 0x864b,0x8b9f,0x9483,0x9c79,0x9eb7,0x7675,0x9a6b,0x9c7a,0x9e1d,0x7069,
+ 0x706a,0x7229,0x9ea4,0x9f7e,0x9f49,0x9f98,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ }
+};
+
+#if CNS_EXTENDED
+/* CNS 11643 plane 3 conversion table */
+
+static const unsigned short
+ cns11643_3tab[MAX_CNS11643_KU_3][MAX_CNS11643_TEN] = {
+ { /* ku 01 */
+ 0x4e28,0x4e36,0x4e3f,0x4e85,0x4e05,0x4e04,0x5182,0x5196,0x5338,0x5369,
+ 0x53b6,0x4e2a,0x4e87,0x4e49,0x51e2,0x4e46,0x4e8f,0x4ebc,0x4ebe,0x5166,
+ 0x51e3,0x5204,0x529c,0x5344,0x5902,0x590a,0x5b80,0x5ddb,0x5e7a,0x5e7f,
+ 0x5ef4,0x5f50,0x5f51,0x5f61,0x961d,UBOGON,0x4e63,0x4e62,0x4ea3,0x5185,
+ 0x4ec5,0x4ecf,0x4ece,0x4ecc,0x5184,0x5186,UBOGON,0x34c5,0x51e4,0x5205,
+ 0x529e,0x529d,0x52fd,0x5300,0x533a,0x3539,0x5346,0x535d,0x5386,0x53b7,
+ 0x3555,0x53cc,0x355b,0x53ce,0x5721,0x37a2,0x5e00,0x5f0c,0x6237,0x6238,
+ 0x6534,0x6535,0x65e0,0x3e26,0x738d,0x4e97,0x4ee0,0x3432,UBOGON,0x4ee7,
+ 0x3433,0x4ee6,0x3434,0x36a2,0x3431,0x34b0,0x56d8,0x518b,0x518c,0x5199,
+ 0x51e5,UBOGON,0x520b,0x34dc
+ },
+ { /* ku 02 */
+ 0x361e,0x5304,0x5303,0x5307,UBOGON,0x531e,0x535f,0x536d,0x5389,0x53ba,
+ 0x53d0,0x3565,0x53f6,0x53f7,0x53f9,0x3564,0x53f4,0x361d,0x3626,0x5724,
+ 0x5904,0x5918,0x5932,0x5930,0x5934,0x368e,0x5975,0x374a,0x5b82,0x5bf9,
+ 0x5c14,0x378b,0x37a6,0x37a4,0x37a5,0x37a7,0x382f,0x3832,0x5e81,0x5e83,
+ 0x5f0d,0x5f52,0x38d4,0x5fca,0x5fc7,0x6239,0x39c5,0x624f,0x65e7,0x672f,
+ 0x6b7a,0x6c39,0x3cba,0x3cb9,0x6c37,0x6c44,0x6c45,0x738c,0x7592,0x7676,
+ 0x9093,0x9092,0x48b3,0x49ba,0x4e21,0x4e20,0x4e22,0x4e68,0x4e89,0x4e98,
+ 0x4ef9,0x4eef,0x343b,0x343c,0x4ef8,0x4f06,0x4f03,0x4efc,0x4eee,0x4f16,
+ 0x3439,0x4f28,0x4f1c,0x4f07,0x4f1a,0x4efa,0x4f17,0x514a,0x34b2,0x5172,
+ UBOGON,0x51b4,0x51b3,0x51b2
+ },
+ { /* ku 03 */
+ 0x34c7,0x51e8,0x342b,0x5214,0x520f,0x5215,0x5218,0x52a8,UBOGON,0x534b,
+ 0x534f,0x353b,0x5350,0x3544,0x538b,0x3542,0x53be,0x355c,0x53d2,0x5416,
+ 0x53ff,0x3567,0x5400,0x3566,0x5405,0x5413,0x5415,UBOGON,0x361f,0x56e3,
+ 0x5735,0x5736,0x5731,0x5732,0x58ee,0x5905,0x4e54,0x368f,0x5936,0x3690,
+ 0x36a8,0x36a4,0x597a,0x36a3,0x5986,0x373d,0x374c,0x5b86,0x5f53,0x5c18,
+ 0x378c,0x5c3d,0x5c78,0x37a8,0x37ad,0x37af,UBOGON,0x5c80,0x3829,0x5e08,
+ 0x3836,0x3871,0x3870,0x386f,0x5ef5,0x5f0e,0x38a9,0x38aa,0x38fb,0x5fd3,
+ 0x5fda,0x38fc,0x5fdb,0x39ae,0x620f,0x625d,0x625f,0x6267,0x6257,0x9f50,
+ 0x3ac3,0x65eb,0x65ea,0x3b30,0x6737,0x3b41,0x6732,0x6736,0x6b22,0x6bce,
+ 0x3c8c,0x6c58,0x6c51,0x6c77
+ },
+ { /* ku 04 */
+ 0x6c3c,0x3cbb,0x6c5a,UBOGON,0x6c53,0x706f,0x7072,0x706e,UBOGON,0x3da1,
+ 0x7073,0x72b1,0x72b2,0x3ea8,0x738f,0x3eaa,0x3eab,0x4096,0x793c,0x41c2,
+ 0x808d,0x808e,0x4493,0x827b,0x4494,0x8d71,0x8fb9,0x9096,0x909a,0x49bb,
+ 0x4e24,0x4e71,UBOGON,0x4e9c,0x4f45,0x4f4a,0x4f39,0x4f37,0x3443,0x4f32,
+ 0x4f42,0x3442,0x4f44,0x4f4b,0x3444,0x4f40,0x4f35,0x4f31,0x5151,UBOGON,
+ 0x5150,0x514e,0x34b3,0x34b7,0x519d,0x34c8,0x51b5,0x51b8,0x51ec,0x5223,
+ 0x5227,0x5226,0x521f,0x522b,0x5220,0x52b4,0x52b3,0x3518,0x5325,0x533b,
+ 0x5374,0x3547,0x3546,0x3545,0x356b,0x3569,0x544d,0x3572,0x3571,0x543a,
+ 0x356c,0x356f,0x5444,0x544c,0x5423,0x541a,0x5432,0x544b,0x5421,0x3573,
+ 0x5434,0x5449,0x5450,0x5422
+ },
+ { /* ku 05 */
+ 0x543f,0x5451,0x545a,0x542f,0x3576,0x56e9,0x56f2,0x56f3,0x56ef,0x56ed,
+ 0x56ec,0x56e6,0x5748,0x3627,0x5744,0x573f,0x573c,0x5753,0x5756,0x3630,
+ 0x575f,0x5743,0x5758,0x5757,0x3629,0x362a,0x362f,0x5746,0x362c,0x573d,
+ 0x362d,0x5742,0x5754,0x5755,0x58f1,0x58f2,0x58f0,0x590b,0x9ea6,0x56f1,
+ 0x593d,0x3693,0x5994,0x598c,0x36ad,0x599c,0x36ac,0x36ab,0x599f,0x36a9,
+ 0x599b,0x36ae,0x5989,0x599a,0x36aa,0x6588,0x374e,0x5b8d,0x3750,0x5bfe,
+ 0x5bff,0x5bfd,0x5c2b,0x37b2,0x5c84,0x5c8e,0x5c9c,0x37b5,0x37b6,0x5c85,
+ 0x5df5,0x5e09,0x3839,0x383b,0x5e0b,0x3872,0x5e92,0x5e90,0x5f03,0x38ac,
+ 0x5f1e,0x5f63,0x3908,0x5fe7,0x5ffe,0x5fe6,0x5fdc,0x5fce,0x3903,0x5ffc,
+ 0x5fdf,0x5fec,0x5ff6,UBOGON
+ },
+ { /* ku 06 */
+ 0x5ff2,0x5ff0,0x5ff9,0x390b,0x6213,0x39af,UBOGON,0x623b,0x623c,0x6282,
+ 0x39ce,0x39cb,0x39cc,0x6278,0x628b,0x39cd,0x629e,0x62a5,0x629b,0x629c,
+ 0x6299,0x628d,0x6285,0x629d,0x6275,0x3a80,0x3aaf,0x3ad3,0x65f6,0x3ad5,
+ 0x3ad4,0x3ad7,0x66f5,0x675b,0x3b42,0x6754,0x6752,0x3b44,0x6758,0x6744,
+ 0x674a,0x6761,0x3cc6,0x6c7f,0x6c91,0x6c9e,0x3cc0,0x6c6e,0x6c7c,0x6c9f,
+ 0x6c75,0x3cbe,0x6c56,0x6ca2,0x6c79,0x3cca,0x6ca1,0x3cc4,0x6caa,0x6ca0,
+ 0x3cc2,0x7079,0x7077,0x707e,0x3da4,0x7075,0x707b,0x7264,0x3e29,0x72bb,
+ 0x72bc,0x72c7,0x72b9,0x72be,0x72b6,0x3e60,0x3e5e,0x7398,0x3ead,0x3eae,
+ 0x3eac,0x3f57,0x7593,0x7680,0x3fdd,0x7683,0x76c0,0x76c1,0x400e,0x4097,
+ 0x77f4,0x77f5,0x4127,0x7acc
+ },
+ { /* ku 07 */
+ 0x7acd,0x7cfa,0x809f,0x8091,0x8097,0x8094,0x4495,0x8286,0x828c,UBOGON,
+ 0x8295,0x4498,0x866c,0x459d,0x8fb5,0x8fbe,0x8fc7,0x488a,0x8fc1,0x90a9,
+ 0x90a4,0x48b5,0x48b6,0x48b7,0x90a8,0x9627,0x9626,0x962b,0x9633,0x9634,
+ 0x9629,0x4e3d,0x3428,0x4e9d,0x4f93,0x4f8a,0x344d,0x3449,0x4f6d,0x4f8e,
+ 0x4fa0,0x4fa2,0x4fa1,0x4f9f,0x4fa3,UBOGON,0x4f72,0x3451,0x4f8c,0x5156,
+ UBOGON,UBOGON,0x5190,0x34cb,0x34ca,0x34cc,0x51ed,0x51fe,0x522f,UBOGON,
+ 0x523c,0x5234,0x5239,0x52b9,0x52b5,0x52bf,0x5355,0x353d,0x5376,0x537a,
+ 0x5393,0x3548,0x53c1,0x53c2,0x53d5,0x5485,0x3578,0x545f,0x5493,0x5489,
+ 0x5479,0x9efe,0x548f,0x5469,0x546d,0x357a,0x5494,0x546a,0x548a,0x3577,
+ 0x56fd,0x56fb,0x56f8,0x3621
+ },
+ { /* ku 08 */
+ 0x56fc,0x56f6,0x5765,0x5781,0x5763,0x5767,0x3631,0x576e,0x5778,0x577f,
+ 0x3633,0x3634,0x58f3,0x594b,0x594c,0x36c1,0x36b0,0x36b4,0x59ad,0x36b8,
+ 0x59c4,0x36bc,0x59c2,0x59b0,0x36bf,0x36b5,0x36b1,0x36bd,0x59bf,0x36bb,
+ 0x59c9,0x59b8,0x59ac,0x36b3,0x36b6,0x36ba,0x59b7,0x59d7,0x36b7,0x5b60,
+ 0x3740,0x5b96,0x5b9e,0x5b94,0x5b9f,0x5b9d,0x3752,0x5c00,0x5c19,0x3790,
+ 0x3791,0x5c49,0x5c4a,0x37be,0x5cbb,0x5cc1,0x37c0,0x37c1,0x37b9,0x5cb9,
+ 0x5c9e,0x5cb4,0x5cba,0x5df6,0x5e13,0x5e12,0x5e77,0x3879,0x5e98,0x387b,
+ 0x5e99,0x5e9d,0x5ef8,0x38a0,0x5ef9,0x3429,0x5f06,0x5f21,0x38ae,0x5f25,
+ 0x5f55,0x38cd,0x38cb,0x38d9,0x5f84,0x5f83,0x6030,0x6007,0x390c,0x6036,
+ 0x3901,0x3905,0x3902,0x5fe9
+ },
+ { /* ku 09 */
+ 0x603d,0x6008,0x3913,0x3911,0x62ba,0x62b2,0x39e4,0x62b7,0x62e4,0x62a7,
+ 0x39da,0x39d5,0x39d3,0x62d5,0x62e1,0x62dd,0x62a6,0x62c1,0x62c5,0x62c0,
+ 0x62df,0x62e0,0x62de,0x39d6,0x6589,0x3ab4,0x65a6,0x65ba,0x3ad9,0x65ff,
+ 0x3ad8,0x6617,0x6618,0x6601,0x65fe,0x3b33,0x670c,0x3b48,0x676b,0x6796,
+ 0x6782,0x678a,0x3b47,0x67a3,0x3b4b,0x67a2,0x678f,0x3b4a,0x67f9,0x6780,
+ 0x6b26,0x6b27,0x6b68,0x6b69,0x3c5a,0x6b81,0x6bb4,0x6bd1,0x3c8e,0x3cb4,
+ 0x6c1c,0x3ccd,0x3ccc,0x3ccf,0x3ccb,0x3cce,0x6c97,0x6c6c,0x6cdf,0x3cd2,
+ 0x6cea,0x3cd1,0x6ce4,0x6cd8,0x6cb2,0x6cce,0x6cc8,0x3da6,0x708b,0x7088,
+ 0x7090,0x708f,0x3daa,0x7087,0x7089,0x708d,0x7081,0x3da8,0x708c,0x3e13,
+ 0x3e1a,0x7240,0x3e1d,0x3e1e
+ },
+ { /* ku 0a */
+ 0x7265,0x7266,0x7268,0x3e65,0x3e66,0x72cd,0x72d3,0x72db,0x3e64,0x72cf,
+ 0x73a7,0x73a3,0x739e,0x3eb0,0x73af,0x3eb3,0x3eb5,0x73aa,0x739c,0x3f19,
+ 0x7542,0x7544,0x753b,0x7541,UBOGON,0x759b,0x759e,0x3f75,0x79c4,0x79c3,
+ 0x79c6,0x412b,0x412c,0x79c7,0x412d,0x79ca,UBOGON,0x41c3,0x7acf,0x7c76,
+ 0x7c74,0x7cff,0x7cfc,0x34ba,0x4350,0x7f59,0x80a8,0x43d3,0x43d0,0x80b0,
+ 0x43dc,0x80b3,0x43d2,0x80a4,0x80b6,0x80a7,0x80ac,0x43db,0x80a6,0x5367,
+ 0x820e,0x82c4,0x833e,0x829c,0x44a5,0x449f,0x449a,0x449c,0x44a2,0x82aa,
+ 0x449b,0x82c9,0x44a3,0x449d,0x82a6,0x82b2,0x4588,0x461a,0x488d,0x8fcc,
+ 0x8fd9,0x8fca,0x8fd8,0x8fcf,0x90b7,0x48b8,0x90ad,0x90b9,0x9637,0x49c3,
+ 0x9641,0x963e,0x96b6,0x9751
+ },
+ { /* ku 0b */
+ 0x9763,0x4e57,0x4e79,0x4eb2,0x4eb0,0x4eaf,0x4eb1,0x4fd2,0x4fd5,0x345d,
+ 0x4fbe,0x4fb8,0x4fb0,0x4fb1,0x4fc8,0x345a,0x3457,0x4fc6,0x4fcc,0x4fe5,
+ 0x4fe3,0x4fb4,0x516a,0x34b8,0x519f,0x34c2,0x51c1,0x34cf,0x51c2,0x51c3,
+ 0x5245,0x5248,0x34e7,0x34e9,0x524f,0x4452,0x34e8,0x52c5,0x52ca,0x52c4,
+ 0x5327,0x5358,0x537d,0x354a,0x53dd,0x53dc,0x53da,0x53d9,0x54b9,0x3580,
+ 0x54d0,0x54b4,0x54ca,0x3587,0x54a3,0x54da,0x54a4,0x3584,0x54b2,0x549e,
+ 0x549f,0x54b5,0x3582,0x3581,0x54cd,0x3583,0x54cc,0x3622,0x5700,0x57ac,
+ 0x5791,0x578e,0x578d,0x5792,0x57a1,0x5790,0x57a6,0x57a8,0x363b,0x579c,
+ 0x5796,0x57a7,0x363a,0x3638,0x3639,0x3636,0x58f5,0x3685,0x5909,0x5908,
+ 0x3c54,0x5952,0x369a,0x36c4
+ },
+ { /* ku 0c */
+ 0x59df,0x36c5,0x59eb,0x59ef,0x59f0,0x59d5,0x5a0d,0x5a04,0x59f9,0x5a02,
+ 0x59f8,0x59e2,0x59d9,0x59e7,0x5b6a,0x3754,0x3755,0x5bab,0x3756,0x5c1b,
+ 0x5c2f,0x3796,0x663c,0x3795,0x3794,0x37c4,0x5cd1,0x5cdc,0x5ce6,0x5ce1,
+ 0x5ccd,UBOGON,0x5ce2,0x5cdd,0x5ce5,0x5dfb,0x5dfa,0x5e1e,0x3844,0x5ea1,
+ 0x387d,0x387e,0x5efc,0x5efb,0x5f2f,0x38b2,0x38b6,0x5f66,UBOGON,0x38dc,
+ 0x38df,0x605c,0x3928,0x604e,0x6051,0x3919,0x3910,0x6023,0x6031,0x607c,
+ 0x6052,0x392c,0x6060,0x604a,0x6061,0x391b,0x6218,0x39c2,0x39ef,0x39e3,
+ 0x39e5,0x39ea,0x39e6,0x39ee,0x631f,0x6317,0x62ea,0x6321,0x6304,0x6305,
+ 0x39e8,0x6531,0x6544,0x6540,0x3a85,0x6542,0x65be,0x3ae0,0x6629,0x661b,
+ 0x3add,0x6623,0x662c,0x661a
+ },
+ { /* ku 0d */
+ 0x6630,0x663b,0x661e,0x6637,0x6638,0x3ae1,0x670e,0x3b51,0x3b55,0x67e8,
+ 0x67d6,0x3b52,0x67c7,0x67bc,0x6852,0x67bf,0x67d5,0x67fe,0x8363,0x67fb,
+ UBOGON,0x67b1,0x6801,0x6805,0x6800,0x67d7,0x409e,0x6b2a,0x6b6b,0x3c52,
+ 0x3c5e,0x3c60,0x3c5f,0x6be1,0x3c92,0x3cd6,0x6d23,0x6cff,0x6d14,0x6d05,
+ 0x6d13,0x6d06,0x6d21,0x3cde,0x6d15,0x6caf,0x6cf4,0x6d02,0x6d45,UBOGON,
+ 0x6d26,0x3cd9,0x6d44,0x3cdd,0x6d24,0x70a5,0x3dac,0x70a3,0x3db0,0x70a2,
+ 0x70bb,0x70a0,0x70aa,0x3daf,0x3dae,0x70a8,0x70b6,0x70b2,0x70a7,0x3dad,
+ 0x3dab,0x70b9,0x722e,0x3e16,0x723c,0x3e30,0x726d,0x3e33,0x3e31,0x72e7,
+ 0x72ed,0x3e6e,0x72ec,0x72e5,0x72e2,0x3eb1,0x73c4,0x73bd,0x73cf,0x73c9,
+ 0x73c1,0x73d0,0x3eb7,0x73ce
+ },
+ { /* ku 0e */
+ 0x74ed,0x74eb,0x3f1a,0x74ef,0x7549,0x7550,0x7546,0x754a,0x3f59,0x754d,
+ 0x75a6,0x3f7a,0x3f78,0x3f7b,0x75a8,0x3fde,0x3fec,0x76c7,0x76ff,0x401e,
+ 0x76fd,0x77e6,0x780a,0x409b,0x7804,0x780b,0x7807,0x409d,0x7815,0x7808,
+ 0x40fd,0x79d3,0x79d4,0x79d0,0x79d7,0x7a7c,0x4194,0x4193,0x7a7d,0x7a83,
+ 0x7a82,0x41c6,0x7ad4,0x7ad5,0x7ad3,0x7ad0,0x7ad2,0x7afe,0x7afc,0x7c77,
+ 0x7c7c,0x7c7b,0x42b8,UBOGON,0x42b7,0x42b9,0x4353,UBOGON,0x4352,0x4351,
+ 0x7f8f,0x80d3,0x43e3,0x80cb,0x80d2,0x43e2,0x8109,0x80e2,0x80df,0x80c6,
+ 0x4463,0x8224,0x82f7,0x82d8,0x82dd,0x44aa,0x44a6,0x82f8,0x82fc,0x44a8,
+ 0x44a9,0x82e9,0x44ab,0x82ee,0x44ac,0x82d0,0x830e,0x82e2,0x830b,0x82fd,
+ 0x5179,0x8676,0x459e,0x8678
+ },
+ { /* ku 0f */
+ 0x459f,0x45a0,0x8675,0x867d,0x460f,0x8842,0x8866,0x461c,0x898c,0x8a05,
+ 0x46ae,0x8a06,0x46b0,0x8c9f,0x47d4,0x8ff1,0x8fe7,0x8fe9,0x8fef,0x90c2,
+ 0x90bc,0x48bb,0x90c6,0x90c0,0x48c1,0x48c2,0x90cd,0x90c9,0x48be,0x90c4,
+ 0x48e5,0x9581,0x49c6,0x9cec,0x5032,0x4ff9,0x501d,0x4fff,0x5004,0x4ff0,
+ 0x5003,0x462e,0x5002,0x4ffc,0x4ff2,0x5024,0x5008,0x5036,0x502e,0x3465,
+ 0x5010,0x5038,0x5039,0x4ffd,0x5056,0x4ffb,0x51a3,0x51a6,0x51a1,0x34d1,
+ 0x34d0,0x51c7,0x51c9,0x5260,0x5264,0x5259,0x5265,0x5267,0x5257,0x5263,
+ 0x34ee,0x5253,0x34ef,0x52cf,0x351e,0x52ce,0x52d0,0x52d1,0x52cc,0x354b,
+ 0x354d,0x3556,0x550d,0x54f4,0x3592,0x5513,0x54ef,0x54f5,0x54f9,0x5502,
+ 0x5500,0x3593,0x3590,0x5518
+ },
+ { /* ku 10 */
+ 0x54f0,0x54f6,UBOGON,0x3597,0x5519,0x3623,0x5705,0x57c9,0x363f,0x57b7,
+ 0x57cd,0x3643,0x3642,0x3644,0x57be,0x57bb,0x3645,0x57db,0x57c8,0x57c4,
+ 0x57c5,0x57d1,0x57ca,0x57c0,0x36d9,0x36de,0x5a21,0x5a2a,0x36cf,0x5a1d,
+ 0x36cd,0x5a0b,0x36dd,0x36ce,0x36d3,0x36d6,0x5a22,0x36dc,0x36d1,0x5a24,
+ 0x36d0,0x5a14,0x5a31,0x36d5,0x5a2f,0x5a1a,0x5a12,0x36d4,0x36db,0x5a26,
+ UBOGON,0x3743,0x5bbc,0x5bbb,0x5bb7,0x5c05,0x5c06,0x5c52,0x5c53,0x37cd,
+ 0x37d1,0x5cfa,0x5ceb,0x37ca,0x5cf3,0x5cf5,0x5ce9,0x5cef,0x37d4,0x5e2a,
+ 0x5e30,0x5e2e,0x5e2c,0x5e2f,0x5eaf,0x5ea9,0x3886,0x5efd,0x5f32,0x5f8e,
+ 0x5f93,0x5f8f,0x604f,0x6099,0x3933,0x607e,0x3937,0x6074,0x604b,0x6073,
+ 0x6075,0x392a,0x391f,0x6056
+ },
+ { /* ku 11 */
+ 0x60a9,0x608b,0x60a6,0x3939,0x6093,0x60ae,0x609e,0x60a7,0x6245,0x39f2,
+ 0x39f8,0x632e,0x39f7,0x6352,0x6330,0x635b,0x39f4,0x6319,0x631b,0x39f1,
+ 0x6331,0x635d,0x6337,0x6335,0x6353,0x39f5,0x635c,0x633f,0x654b,0x3a87,
+ 0x4369,0x658b,0x3ab6,0x659a,0x6650,0x6646,0x664e,0x6640,0x3ae9,0x664b,
+ 0x6648,0x3aeb,0x6660,0x6644,0x664d,0x3b34,0x6837,0x6824,0x3b62,0x3b5c,
+ 0x681b,0x6836,0x3b60,0x682c,0x6819,0x6856,0x6847,0x683e,0x681e,UBOGON,
+ 0x6815,0x6822,0x6827,0x6859,0x6858,0x6855,0x6830,0x6823,0x6b2e,0x6b2b,
+ 0x6b30,0x6b6c,0x3c61,0x6b8b,0x3c7f,0x6be9,0x6bea,0x6be5,0x6d6b,0x3ce5,
+ 0x3ce6,0x6d73,0x6d57,0x3ce9,0x3cf3,0x6d5d,0x6d56,0x6d8f,0x6d5b,0x6d1c,
+ 0x6d9a,0x6d9b,0x6d99,0x3cee
+ },
+ { /* ku 12 */
+ 0x6d81,0x6d71,0x3ced,0x3cec,0x6d72,0x6d5c,0x6d96,0x70c4,0x70db,0x70cc,
+ 0x70d0,0x70e3,0x70df,0x3db3,0x70d6,0x70ee,0x70d5,0x3db5,0x3e27,0x3e35,
+ 0x3e36,0x727a,0x3e71,0x72f5,0x7302,0x3eb8,0x3ec2,0x73e2,0x73ec,0x73d5,
+ 0x73f9,0x73df,0x73e6,0x3ec8,0x3ec0,0x3ec1,0x3ec4,0x73e4,0x73e1,0x74f3,
+ 0x3f1f,0x3f1c,0x3f1d,0x3f4d,0x7556,0x7555,0x7558,0x7557,0x755e,0x75c3,
+ 0x3f87,0x3f82,0x75b4,0x3f7d,0x75b1,0x3fdf,0x4000,0x76cb,0x76cc,0x772a,
+ 0x4020,0x7716,0x770f,0x4022,0x4024,0x773f,0x772b,0x770e,0x7724,0x4021,
+ 0x7721,0x7718,0x77dd,0x40a4,0x40a5,0x7824,0x7836,0x4101,0x7958,0x7959,
+ 0x4103,0x7962,0x79da,0x79d9,0x4137,0x79e1,0x79e5,0x79e8,0x79db,0x4138,
+ 0x79e2,0x79f0,0x4199,0x4198
+ },
+ { /* ku 13 */
+ 0x4197,0x41c9,0x7ada,0x7add,0x41c7,0x7adb,0x7adc,0x41d9,0x41db,0x7b0d,
+ 0x7b0b,0x7b14,0x7c8e,0x7c86,0x427b,0x7c87,0x7c83,0x7c8b,0x427c,0x42bd,
+ 0x42bc,0x42c3,0x7d24,0x42c1,0x42bf,0x42c4,0x7d25,0x7f62,0x7f93,0x7f99,
+ 0x7f97,0x437e,0x437f,0x7fc4,0x7fc6,0x800a,0x43b4,0x43b3,0x8040,0x803c,
+ 0x803b,0x80f6,0x80ff,0x80ee,0x8104,0x8103,0x8107,UBOGON,0x43e6,0x80f7,
+ 0x4459,0x445a,0x822d,0x4464,0x8227,0x8229,0x831f,0x8357,0x44b4,0x44b9,
+ 0x44b7,0x44b5,0x8321,0x44c1,0x44b1,0x8318,0x8358,0x44b3,0x44ba,0x458c,
+ 0x458b,0x458d,0x8684,0x869f,0x869b,0x8689,0x86a6,0x8692,0x868f,0x86a0,
+ 0x884f,0x8878,0x887a,0x886e,0x887b,0x8884,0x8873,0x4678,0x4677,0x8a0d,
+ 0x8a0b,0x8a19,0x46b2,0x47d6
+ },
+ { /* ku 14 */
+ 0x8ed0,0x4845,0x4892,0x4895,0x8ff9,0x9009,0x9008,0x48c6,0x90de,0x9151,
+ 0x48e7,0x48e8,0x91db,0x91df,0x91de,0x91d6,0x91e0,0x9585,0x9660,0x9659,
+ 0x49cb,0x9656,0x49cd,0x49f1,0x96bd,0x4b22,0x3421,0x5042,0x5059,0x346f,
+ 0x5044,0x5066,0x5052,0x5054,0x5071,0x5050,0x507b,0x507c,0x5058,0x3470,
+ 0x3464,0x5079,0x506c,0x5078,0x51a8,0x51d1,0x51cf,0x5268,0x5276,0x52d4,
+ 0x352d,0x53a0,0x53c4,0x3558,0x5558,0x554c,0x5568,0x35a6,0x5549,0x35a4,
+ 0x359f,0x555d,0x5529,UBOGON,0x5554,0x5553,0x35a3,0x555a,0x35a0,0x553a,
+ 0x553f,0x552b,0x57ea,0x364a,0x57ef,0x3647,0x3648,0x57dd,0x57fe,UBOGON,
+ 0x57de,0x57e6,0x3649,0x57e8,0x57ff,0x5803,0x58f7,0x68a6,0x591f,0x369e,
+ 0x595b,0x595d,0x595e,UBOGON
+ },
+ { /* ku 15 */
+ 0x36e8,0x5a2b,0x36ec,0x5a3b,0x36ed,0x36e6,0x5a61,0x5a3a,0x5a6e,0x5a4b,
+ 0x5a6b,0x36eb,0x36e7,0x5a45,0x5a4e,0x5a68,0x5a3d,0x5a71,0x5a3f,0x5a6f,
+ 0x5a75,0x36e9,0x5a73,0x5a2c,0x5a59,0x5a54,0x5a4f,0x5a63,0x375c,0x375d,
+ 0x5bc8,0x3760,0x5bc3,0x375b,0x5c5b,0x5c61,0x3799,0x5d21,0x5d0a,0x5d09,
+ 0x37d8,0x5d2c,0x5d08,0x37da,0x37dd,0x5d2a,0x5d15,0x37e0,0x5d10,0x5d13,
+ 0x37e5,0x5d2f,0x5d18,0x37d7,0x5de3,0x5e39,0x5e35,0x5e3a,0x5e32,0x384e,
+ 0x388c,0x3888,UBOGON,0x5ebb,0x5eba,0x5f34,0x5f39,0x38ce,UBOGON,0x38e5,
+ 0x38e6,0x6098,0x3932,0x60d0,0x3940,0x3947,0x394c,0x60d7,0x60aa,0x3935,
+ 0x60a1,0x60a4,0x3930,0x60ee,0x3943,0x60e7,0x394d,0x60e8,0x60de,0x39b7,
+ 0x39f3,0x637e,0x638b,0x3a02
+ },
+ { /* ku 16 */
+ 0x3a0b,0x6379,0x6386,0x6393,0x3a04,0x6373,0x636a,UBOGON,0x636c,0x3a08,
+ 0x637f,0x39fc,0x63b2,0x63ba,0x39ff,0x3a00,0x6366,0x6374,0x3a8b,0x655a,
+ 0x3a8d,0x654e,0x654d,0x658d,0x658e,0x65ad,0x3aca,0x65c7,0x65ca,0x3acb,
+ 0x65c9,UBOGON,0x65e3,0x6657,0x3af3,0x6663,0x6667,0x671a,0x6719,0x6716,
+ 0x3b36,0x3b6a,0x689e,0x68b6,0x6898,0x6873,0x3b6b,0x689a,0x688e,0x68b7,
+ 0x68db,0x68a5,0x686c,0x68c1,0x6884,0x3b71,0x3b68,0x6895,0x687a,0x6899,
+ 0x3b72,0x68b8,0x68b9,0x6870,0x3c2e,0x6b35,0x3c62,0x6b90,0x6bbb,0x6bed,
+ 0x3c98,0x3cb5,0x3ceb,0x6dc1,0x6dc3,0x6dce,0x3cfb,0x3cf8,0x6dad,0x6e04,
+ 0x3cf5,0x6db9,0x3d08,0x6de7,UBOGON,0x6e08,0x6e06,0x3d0a,0x6e0a,0x6db0,
+ 0x3d06,0x6df8,0x6e0c,0x3cfd
+ },
+ { /* ku 17 */
+ 0x6db1,0x3cfa,0x6e02,0x6e07,0x6e09,0x6e01,0x6e17,0x6dff,0x6e12,0x3dba,
+ 0x3db9,0x7103,0x7107,0x7101,0x70f5,0x70f1,0x7108,0x70f2,0x710f,0x3dbb,
+ 0x70fe,0x3e18,0x3e40,0x3e3d,0x731a,0x7310,0x730e,0x7402,0x73f3,0x3ecd,
+ 0x3ec9,0x73fb,0x3ecb,0x3eca,0x3ece,0x751b,0x7523,0x7561,0x7568,0x3f5e,
+ 0x7567,0x75d3,0x3f91,0x3f8c,0x7690,0x3fe1,0x4002,0x76d5,0x76d7,0x76d6,
+ 0x7730,0x402b,0x7726,0x402a,0x7740,0x3e14,0x771e,0x40ad,0x40a3,0x40ab,
+ 0x7847,0x40af,0x784b,0x7851,0x784f,0x7842,0x7846,0x4104,0x796e,0x796c,
+ 0x79f2,0x4144,0x79f1,0x79f5,0x79f3,0x79f9,0x413d,0x4147,0x419c,0x7a9a,
+ 0x7a93,0x7a91,0x7ae1,0x41e0,0x41e4,0x7b21,0x7b1c,0x7b16,0x7b17,0x7b36,
+ 0x7b1f,0x4280,0x7c93,0x7c99
+ },
+ { /* ku 18 */
+ 0x7c9a,0x7c9c,0x42ca,0x7d49,0x42d4,0x7d34,0x7d37,0x42d2,0x7d2d,0x42cb,
+ 0x7d4c,0x42ce,0x42d3,0x7d48,0x4344,0x4348,0x7f3b,0x4345,0x4381,0x4386,
+ 0x4385,0x8008,0x801a,0x43a3,0x801d,0x43b5,0x8049,0x8045,0x8044,0x7c9b,
+ 0x43fa,0x43f9,0x812a,0x812e,0x43fb,0x43f2,0x8131,0x43ef,0x811a,0x8134,
+ 0x8117,0x445b,0x4466,0x44ce,0x831d,0x8371,0x8384,0x8380,0x8372,0x83a1,
+ 0x35b4,0x8379,0x8391,0x44c8,0x839f,0x83ad,0x44d1,0x44c5,0x8323,0x44d2,
+ 0x8385,0x839c,0x83b7,0x8658,0x865a,0x458f,0x8657,0x86b2,0x45a7,0x86ae,
+ 0x45a5,0x45a4,0x4611,0x8845,0x889c,0x8894,0x88a3,0x888f,0x88a5,0x88a9,
+ 0x88a6,0x888a,0x88a0,0x8890,0x8992,0x8991,0x8994,0x46b5,0x8a26,0x8a32,
+ 0x8a28,0x46b4,0x46bd,0x8a1c
+ },
+ { /* ku 19 */
+ 0x46bb,0x8a2b,0x8a20,0x46b9,0x8a29,0x46c2,0x46be,0x46ba,0x8a21,0x8c3a,
+ 0x3ab7,0x8c5b,0x8c58,0x8c7c,0x4758,0x8ca6,0x8cae,0x8cad,0x8d65,0x479b,
+ 0x8d7e,0x479c,0x8d7c,0x8d7f,0x8d7a,0x8dbd,0x47da,0x47de,0x8dc0,0x8dbb,
+ 0x8ead,0x8eaf,0x8ed6,0x484d,0x4846,0x4847,0x484b,0x484c,0x8ed9,0x4848,
+ 0x4899,0x9012,0x900e,0x9025,0x489b,0x9013,0x90ee,0x48ce,0x90ab,0x90f7,
+ 0x48eb,0x9159,0x9154,0x91f2,0x91f0,0x91e5,0x91f6,0x491c,0x498c,0x9587,
+ 0x49d1,0x965a,0x49d6,0x49d3,0x966e,0x49d4,0x49d0,0x49d5,0x9679,0x4a0b,
+ 0x98e1,0x98e6,0x4bc6,0x9ec4,0x9ed2,0x4e80,0x3424,0x4e81,0x508f,0x5097,
+ 0x5088,0x5089,0x3474,0x347a,0x5081,0x5160,UBOGON,0x34c3,0x5e42,0x51d3,
+ 0x34d4,0x34d5,0x51d2,0x51d6
+ },
+ { /* ku 1a */
+ 0x5273,0x34fb,0x5270,0x34f7,0x3532,UBOGON,0x53a8,0x53a6,0x53c5,0x5597,
+ 0x55de,0x35ba,0x35bf,0x5596,0x55b4,0x35c7,0x5585,0x35b7,0x559b,0x55a0,
+ 0x35b9,0x5559,0x35c3,0x5586,0x35bd,0x35d0,0x55af,0x557a,0x35c1,0x35be,
+ 0x35cd,0x559e,0x35cb,0x55a9,0x570f,0x570e,0x581a,0x364f,0x581f,0x3653,
+ 0x583c,0x5818,0x583e,0x5826,0x3655,0x583a,UBOGON,0x5822,0x3651,0x58fb,
+ 0x5963,0x5964,0x369f,0x5aa8,0x5aa3,0x5a82,0x5a88,0x5aa1,0x5a85,0x5a98,
+ 0x36fe,0x5a99,0x36fb,0x5a89,0x5a81,0x5a96,0x5a80,0x36f1,0x36f5,0x5a91,
+ 0x36ef,0x3704,0x3703,0x36f4,0x5acf,0x36f3,0x3702,0x36f7,0x36fa,0x36fd,
+ 0x36ee,0x5a87,0x5aa0,0x36f0,0x5a79,0x36f2,0x5a86,0x5aab,0x5aaa,0x5aa4,
+ 0x5a8d,0x5a7e,0x3744,0x5bd5
+ },
+ { /* ku 1b */
+ 0x3762,0x3777,0x3dc9,0x5c1e,0x5c5f,0x5c5e,0x5d44,0x5d3e,0x37e8,0x5d48,
+ 0x5d1c,0x37ef,0x5d5b,0x5d4d,0x37e6,0x37ed,0x5d57,0x37e7,0x5d53,0x5d4f,
+ 0x37eb,0x5d3b,0x5d46,0x382d,0x3855,0x5e46,0x5e47,0x3853,0x5e48,0x5ec0,
+ 0x5ebd,0x5ebf,0x3890,0x5f11,0x38be,0x5f3e,0x5f3b,0x38bd,0x5f3a,0x38cf,
+ 0x38d0,0x38ec,0x5fa7,0x394b,0x60ea,0x3948,0x6107,0x6122,0x610c,0x3955,
+ 0x3951,0x60b3,0x60d6,0x60d2,0x394e,0x60e3,0x60e5,0x60e9,0x396b,0x395e,
+ 0x6111,0x60fd,0x3960,0x3967,0x611e,0x6120,0x6121,0x621e,0x39b8,0x63e2,
+ 0x63de,0x63e6,0x3a14,0x3a0f,0x3a07,0x3a13,0x63f8,0x3a17,0x63fe,0x63c1,
+ 0x63bf,0x63f7,0x63d1,0x655f,0x6560,0x6561,0x3a9a,0x3ab8,0x65d1,0x3af7,
+ 0x3af8,0x667d,0x666b,0x667f
+ },
+ { /* ku 1c */
+ 0x3afd,0x3af5,0x6673,0x6681,0x666d,0x6669,0x3afa,0x3b38,0x671e,0x68ed,
+ 0x3b87,0x3b80,0x3b88,0x3b79,0x6903,0x3b7c,0x68fe,0x68e5,0x691e,0x6902,
+ 0x3b83,0x3b85,0x6909,0x68ca,0x6900,UBOGON,0x6901,0x6918,0x68e2,0x68cf,
+ 0x3b7b,0x692e,0x68c5,0x68ff,0x3b86,0x691c,0x68c3,0x3c34,0x6b6f,0x3c55,
+ 0x6b6e,0x3c68,0x6bbe,0x3c9c,0x6bf4,0x6c2d,0x3cfc,0x6db6,0x6e75,0x6e1e,
+ 0x3d1a,0x6e18,0x3d17,0x6e48,0x3d1b,0x6e4f,0x3d13,0x6e42,0x6e6a,0x6e70,
+ 0x6dfe,0x3d05,0x3d07,0x6e6d,0x3d1c,0x6e7b,0x6e7e,0x6e59,0x3d11,0x6e57,
+ 0x3d16,0x6e80,0x6e50,0x3d15,0x6e29,0x6e76,0x6e2a,0x6e4c,0x712a,0x3dcb,
+ 0x7135,0x712c,0x7137,0x711d,0x3dc5,0x3dc2,0x7138,0x3dcd,0x7134,0x712b,
+ 0x7133,0x7127,0x7124,0x3dca
+ },
+ { /* ku 1d */
+ 0x712d,0x7232,0x7283,0x7282,0x7287,0x7306,0x7324,0x7338,0x732a,0x732c,
+ 0x732b,0x3e83,0x732f,0x7328,0x7417,0x3ed6,0x3ed5,0x7419,0x7438,0x3ed1,
+ 0x741f,0x7414,0x743c,0x73f7,0x741c,0x7415,0x7418,0x7439,0x74f9,0x7524,
+ UBOGON,0x3f52,0x3f5f,0x756e,0x756d,0x7571,0x758e,0x3f95,0x75e5,0x3f9d,
+ 0x3f98,0x3f9e,0x3f96,0x7694,0x76b3,0x4003,0x76d9,0x402f,0x7748,0x7749,
+ 0x7743,0x4031,0x4033,0x7742,0x77df,0x40b4,0x7863,0x7876,0x40b0,0x785f,
+ 0x7866,0x7966,0x7971,0x4108,0x4107,0x7976,0x7984,0x7975,0x79ff,0x7a07,
+ 0x414e,0x7a0e,0x7a09,0x4150,0x4152,0x41a1,0x41a3,0x41a5,0x41cc,0x7ae7,
+ 0x7ae2,0x7b55,0x41ef,0x41ea,0x7b43,0x7b57,0x7b6c,0x7b42,0x7b53,0x41ed,
+ 0x7b41,0x4285,0x4284,0x7ca7
+ },
+ { /* ku 1e */
+ 0x7ca0,0x7ca6,0x7ca4,0x7d74,0x42db,0x7d59,0x42d9,0x7d60,0x7d57,0x7d6c,
+ 0x7d7e,0x7d64,0x42d7,0x7d5a,0x7d5d,0x42da,0x42de,0x42d8,0x7d76,0x7d4d,
+ 0x7d75,0x42d5,0x7fd3,0x7fd6,0x439c,0x439d,0x8060,0x804e,0x8145,0x813b,
+ 0x43fe,0x8148,0x8142,0x8149,0x8140,0x8114,0x8141,0x4407,0x81ef,0x81f6,
+ 0x8203,0x446a,0x83ed,0x44e7,0x83da,0x8418,0x83d2,0x8408,0x44e2,0x8400,
+ 0x44df,0x44e1,0x44e5,0x8417,0x8346,0x8414,0x83d3,0x8405,0x841f,0x8402,
+ 0x8416,0x83cd,0x83e6,0x4591,0x865d,0x86d5,0x86e1,0x45b4,0x45b0,0x45b5,
+ 0x45ae,0x86ee,0x8847,0x8846,0x462d,0x462c,0x88bb,0x462b,0x88bf,0x88b4,
+ 0x4629,0x88b5,0x467f,0x899a,0x8a43,0x46c9,0x46cb,0x8a5a,0x46c5,0x46c6,
+ 0x46ca,0x8a35,0x8a38,0x8a42
+ },
+ { /* ku 1f */
+ 0x8a49,0x8a5d,0x8a4b,0x8a3d,0x46d2,0x46d0,0x472d,0x4735,0x8c60,0x8c5e,
+ 0x8c7f,0x8c7e,0x8c83,0x476c,0x8cb1,0x8d87,0x479d,0x47a0,0x8d88,0x8d83,
+ 0x47a2,0x479f,0x8d86,0x8d8b,0x8d82,0x8dca,0x8dd2,0x47eb,0x47e2,0x8dd4,
+ 0x8dc9,0x8eb0,0x4836,0x4832,0x4850,0x8ef2,0x8ee4,0x8ef3,0x8eea,0x484f,
+ 0x8efd,0x4852,0x8f9d,0x902b,0x902a,0x489e,0x9028,0x9029,0x902c,0x48a0,
+ 0x489c,0x903a,0x9030,0x9037,0x903b,0x48d1,0x910a,0x48ef,0x48f0,0x48f1,
+ 0x91fe,0x9220,0x491d,0x920b,0x491f,0x9218,0x9222,0x491e,0x921b,0x9208,
+ 0x4920,0x920e,0x9213,0x498e,0x4991,0x9595,UBOGON,0x4990,0x49d7,0x968c,
+ 0x967b,0x967f,0x9681,0x49d9,0x9682,0x49f4,0x49f6,0x3560,0x49f5,0x49f3,
+ 0x96ee,0x96ed,0x4a0c,0x96ec
+ },
+ { /* ku 20 */
+ 0x975f,0x976f,0x4a51,0x976d,0x4aa6,0x4aa7,0x4aa8,0x4b27,0x4b24,0x4b25,
+ 0x98f0,0x4b2a,0x4b74,0x4bc7,0x9aa9,0x4be7,0x4bed,0x9ae0,0x4eb7,0x342e,
+ 0x347b,0x50cc,0x50bc,0x347c,0x50aa,0x50b9,0x347d,0x50ab,0x50c3,0x50cd,
+ 0x517e,0x527e,0x5279,0x34fd,UBOGON,0x52e1,0x52e0,0x52e7,0x5380,0x53ab,
+ 0x53aa,0x53a9,0x53e0,0x55ea,0x35da,0x55d7,0x35d6,0x35db,0x55c1,0x5715,
+ 0x365b,0x586c,0x365c,0x585c,0x5850,0x5861,0x586a,0x5869,0x5856,0x5860,
+ 0x5866,0x585f,0x5923,0x5966,0x5968,0x3706,0x370b,0x5ace,0x370d,0x5ac5,
+ 0x5ac3,0x370a,0x3713,0x5ad0,0x3710,0x3712,0x3709,0x3708,0x3711,0x370f,
+ 0x5b74,0x5b76,0x5bdc,0x5bd7,0x5bda,0x5bdb,0x3767,0x5c20,0x5d6d,0x5d66,
+ 0x37f6,0x5d64,0x5d6e,UBOGON
+ },
+ { /* ku 21 */
+ 0x5d60,0x5f42,0x5f5a,0x5f6e,0x3964,0x396c,0x6130,0x613a,0x612a,0x6143,
+ 0x6119,0x6131,0x396d,0x613d,0x397a,0x3975,0x3a0d,0x6408,0x6432,0x6438,
+ 0x3a1e,0x6431,0x3a1b,0x6419,0x3a2a,0x6411,0x3a1f,0x3a22,0x6429,0x641d,
+ 0x3a25,0x3a27,0x3a29,0x643c,0x3a24,0x6446,0x6447,0x3a28,0x3a26,0x643a,
+ 0x6407,0x3a23,0x656b,0x3a9f,0x6570,0x656d,0x3ab1,0x65e4,0x6693,0x3b03,
+ 0x3b07,0x3b0c,0x3b06,0x668f,0x3b04,0x3b09,0x6692,0x3b05,0x668e,0x3b08,
+ 0x6946,0x3b96,0x3b9c,0x3b9f,0x3b9b,0x3b98,0x3b99,0x3b94,0x6931,0x3b8d,
+ 0x3ba3,0x693e,0x3b93,0x697c,0x6943,0x3b92,0x6973,UBOGON,0x6955,0x3b8e,
+ 0x3b8c,0x6985,0x694d,0x6950,0x6947,0x6967,0x6936,0x6964,0x6961,0x3b9a,
+ 0x697d,0x6b44,0x6b40,0x6b71
+ },
+ { /* ku 22 */
+ 0x6b73,0x6b9c,0x3c6a,0x3c6d,0x3c84,0x6bc1,0x3ca0,0x6bfa,0x6c31,0x6c32,
+ 0x3d1d,0x3d26,0x6eb8,0x6ea8,0x3d33,0x6e91,0x6ebb,0x3d38,0x6e9a,0x3d30,
+ 0x3d28,0x6ea9,0x3d27,0x3d2a,0x6eb5,0x6e6c,0x6ee8,0x3d31,0x6edd,0x6eda,
+ 0x6ee6,0x6eac,0x3d34,0x3d2e,0x3d3b,0x6ed9,0x6ee3,0x6ee9,0x6edb,0x3d29,
+ 0x716f,0x3dd2,0x3dd8,0x7148,0x3dcf,0x714a,0x716b,0x3dd9,0x714f,0x7157,
+ 0x7174,0x3dce,0x3dd3,0x3dd0,0x7145,0x7151,0x716d,0x3ba1,0x7251,0x7250,
+ 0x724e,0x3e47,0x7341,0x3e8b,0x732e,0x7346,0x3ed4,0x7427,0x3ede,0x7448,
+ 0x7453,0x743d,0x3edf,0x745d,0x7456,0x3ed7,0x741e,0x7447,0x7443,0x7458,
+ 0x7449,0x3ee1,0x744c,0x7445,0x743e,0x3f2f,0x7501,0x751e,0x3f62,0x3f63,
+ 0x757a,0x75ee,0x7602,0x7697
+ },
+ { /* ku 23 */
+ 0x7698,0x3fe2,0x4004,0x4043,0x775d,0x7764,0x7753,0x7758,0x7882,0x7890,
+ 0x788a,0x40be,0x787a,0x787d,0x40ba,0x788b,0x7878,0x40bc,UBOGON,0x788d,
+ 0x7888,0x7892,0x7881,0x797e,0x7983,0x410d,0x410e,0x4111,0x7980,0x410f,
+ 0x4112,0x4155,0x7a0f,0x4159,0x415b,0x7a1d,0x4157,0x7aa1,0x7aa4,0x41ce,
+ 0x7ae9,0x7aea,0x41fe,0x7b62,0x7b6b,0x41fc,0x7b5e,0x41f5,0x7b79,0x41f9,
+ 0x41fa,0x7b6f,0x7b68,0x4288,0x4289,0x7cae,0x428a,0x4287,0x428b,0x7cb0,
+ 0x42e6,0x7d90,0x42ed,0x7d8a,0x42e5,0x7d8b,0x7d99,0x7d95,0x42e0,0x7d87,
+ 0x7d78,0x7d97,0x7d89,0x7d98,0x42e1,0x435b,0x435c,0x7fa3,0x438f,0x438b,
+ 0x438d,0x7fdd,0x8057,0x43b9,0x8163,0x816a,0x816c,0x440f,0x4419,0x4413,
+ 0x815d,0x8175,0x4418,0x815f
+ },
+ { /* ku 24 */
+ 0x4416,0x817d,0x816d,0x4453,UBOGON,0x8241,0x844f,0x8484,0x44f6,0x847f,
+ 0x44f5,0x8448,0x842a,0x847b,0x8472,0x8464,0x842e,0x845c,0x8453,0x44f7,
+ 0x8441,0x84c8,0x44f0,0x8462,0x8480,0x843e,0x8483,0x8471,0x44f9,0x844a,
+ 0x8455,0x8458,0x4592,0x4595,0x4596,0x86fc,0x86fd,0x8715,0x45b9,0x8716,
+ 0x86ff,0x45bd,0x45b8,0x4612,0x8858,0x88cf,0x88e0,0x4680,0x4681,0x469a,
+ 0x4698,0x89e7,0x8a6a,0x8a80,0x46d4,0x8a6f,0x8a65,0x46da,0x8a78,0x8a7d,
+ 0x8a88,0x46d6,0x46db,0x8a64,0x8a7e,0x46dc,0x8a67,0x8c63,0x8c88,0x4771,
+ 0x8ccd,0x4772,0x8cc9,0x47a8,0x8ded,0x47f0,UBOGON,0x47f1,0x47fd,0x4838,
+ 0x4837,0x4839,0x8eb1,0x4855,0x4853,0x8f04,0x8f9e,0x8fa0,0x9043,0x9046,
+ 0x9048,0x9045,0x9040,0x904c
+ },
+ { /* ku 25 */
+ 0x48d5,0x48bd,0x910c,0x9113,0x9115,0x48f5,0x916b,0x9167,0x925d,0x9255,
+ 0x9235,0x4921,0x9259,0x922f,0x923c,0x928f,0x925c,0x926a,0x9262,0x925f,
+ 0x926b,0x926e,0x923b,0x9244,0x9241,0x959a,0x4992,0x9599,0x49de,0x49db,
+ 0x49da,0x968f,0x49df,0x9696,0x49f9,0x49f8,0x49fa,0x96f4,0x96fc,0x4a0e,
+ 0x9755,0x4a43,0x9779,0x4a56,0x4a53,0x4a9e,0x97ee,0x97f5,0x4aa9,0x980b,
+ 0x4afa,0x98f3,0x4b31,0x4b30,0x98f7,0x98ff,0x98f5,0x4b32,0x98ec,0x98f1,
+ 0x4b29,0x4b2e,0x999a,0x4b76,0x9ae2,0x9b3d,0x9b5d,0x9ce8,0x4ca5,0x9ceb,
+ 0x9cef,0x9cee,0x9e81,0x9f14,0x50d0,0x50d9,0x50dc,0x50d8,0x348c,0x50e1,
+ 0x50eb,0x348b,0x3489,0x50f4,0x50e2,0x50de,0x348d,0x3486,0x34d7,0x51f4,
+ 0x3504,0x3507,0x3503,0x52ed
+ },
+ { /* ku 26 */
+ 0x52ea,0x3522,0x5332,0x3551,0x53ae,0x53b0,0x3561,0x55fb,0x5603,0x560b,
+ 0x35e9,0x5607,0x35e5,0x55f8,0x35e4,0x5628,0x561e,0x35e3,0x5618,0x5611,
+ 0x5651,0x5605,0x5717,0x5892,0x3665,0x588c,0x3663,0x5878,0x5884,0x5873,
+ 0x58ad,0x5897,0x5895,0x5877,0x5872,0x5896,0x588d,0x5910,0x368c,0x596c,
+ 0x371a,0x5ae7,0x3715,0x5ae4,0x3720,0x3721,0x5aef,0x5626,0x371c,0x371b,
+ 0x5af0,0x5d7b,0x37fe,0x5d83,0x3804,0x3801,0x5d8b,0x5d8c,0x3800,0x5d78,
+ 0x5e52,0x386d,0x3893,0x5ed0,0x5ecf,0x38a1,0x5fb3,0x5fb4,0x3976,0x3979,
+ 0x3972,0x617b,0x3983,0x616f,0x6181,0x613c,0x6142,0x6138,0x6133,UBOGON,
+ 0x6160,0x6169,0x617d,0x6186,0x622c,0x6228,0x3a38,0x644c,0x3a30,0x6457,
+ 0x647c,0x3a34,0x3a3a,0x6455
+ },
+ { /* ku 27 */
+ 0x6462,0x6471,0x646a,0x6456,0x643b,0x6481,0x3a35,0x644f,0x647e,0x6464,
+ 0x3a3f,0x3a40,0x3a32,0x3a31,0x3a36,0x6571,UBOGON,0x3b0f,0x66a5,0x669a,
+ 0x669c,0x3b10,0x66a6,0x3b0d,0x66a4,0x698f,0x69c5,0x69c8,0x6992,0x69b2,
+ 0x3ba9,0x3bb4,0x3bac,0x69e3,0x69c0,0x69d6,0x69d1,0x699f,0x69a2,0x69d2,
+ 0x3bb8,0x3bae,UBOGON,0x69e1,0x69d5,0x699d,0x3bb3,0x3bba,0x6998,0x3c3f,
+ 0x6b74,0x6ba1,0x3d3c,0x6ef0,0x6ef3,0x3d42,0x3d40,0x6f1b,0x6f0c,0x6f1d,
+ 0x6f34,0x6f28,0x6f17,0x3d3e,0x6f44,0x6f42,0x6f04,0x6f11,0x6efa,0x6f4a,
+ 0x7191,0x718e,0x3de1,0x718b,0x718d,0x717f,0x718c,0x717e,0x717c,0x7183,
+ 0x3de6,0x7188,0x3de0,0x3e15,0x7294,0x3e93,0x7355,0x7353,0x734f,0x7354,
+ 0x746c,0x7465,0x7466,0x7461
+ },
+ { /* ku 28 */
+ 0x746b,0x7468,0x7476,0x3ee7,0x7460,UBOGON,0x7474,0x7506,0x760e,0x3fad,
+ 0x7607,0x3fae,0x3fe3,0x76b9,0x3ff5,0x76b7,0x76e2,0x4006,0x7774,0x7777,
+ 0x7776,0x7775,0x404f,0x7778,0x7771,0x4054,0x777a,0x715b,0x777b,0x78a6,
+ 0x78ae,0x78b8,0x40cb,0x40e3,0x40c9,0x78b1,0x78af,0x4113,0x7989,0x7987,
+ 0x4115,0x4161,0x7a29,0x4166,0x7a2a,0x4164,0x7a2d,0x7a2c,0x4160,0x7a32,
+ 0x4163,0x7aec,0x7af0,0x7b81,0x7b9e,0x7b83,0x420a,0x7b92,0x4204,0x7ba3,
+ 0x7b9f,0x7b93,0x4207,0x7b86,0x7cb8,0x7cb7,0x428d,0x428f,0x4290,0x4292,
+ 0x42ec,0x7dc8,0x7db6,UBOGON,0x7dd1,0x42e7,0x7da8,0x7dab,0x42f2,0x7db3,
+ 0x7dcd,0x42ee,0x7dcf,0x7da4,0x42ef,0x434c,0x7f41,0x7f6f,0x7f71,0x435e,
+ 0x435f,0x4376,0x4374,0x4372
+ },
+ { /* ku 29 */
+ 0x4390,0x8023,0x805b,0x43be,0x8061,0x805f,0x8181,0x4426,0x4425,0x8184,
+ 0x8213,0x4474,0x824a,0x824c,0x44fd,0x4505,0x4501,0x84bd,0x8495,0x4509,
+ 0x8492,0x84c3,0x450c,0x8496,0x84a5,0x84b5,0x84b3,0x84a3,0x84e4,0x84d8,
+ 0x84d5,0x450d,0x84b7,0x84ad,0x84da,0x8493,0x8736,0x45c0,0x45c5,0x45c9,
+ 0x873d,0x872b,0x8747,0x8739,0x45d5,0x8745,0x871d,0x4641,0x88ff,0x88ea,
+ 0x4633,0x88f5,0x463a,0x8900,0x88ed,0x8903,0x88e9,0x4640,0x4642,0x89ea,
+ 0x46e8,0x8a9b,0x8a8e,0x8aa2,0x46e4,0x8a9c,0x8a94,0x8a90,0x8aa9,0x8aac,
+ 0x46e7,0x8a9f,0x46e6,0x46e1,0x8a9d,0x4739,0x8c67,0x475c,0x4775,0x8cd0,
+ 0x8cd6,0x8cd4,0x8d98,0x8d9a,0x8d97,0x47ae,0x47b0,0x47fa,0x8e0b,0x8e08,
+ 0x8e01,0x8eb4,0x8eb3,0x485b
+ },
+ { /* ku 2a */
+ 0x8fa1,0x8fa2,0x48a5,0x905a,0x48a2,0x9061,0x905f,0x48db,0x48da,0x9125,
+ 0x917b,0x9176,0x917c,0x4924,0x9289,0x92f6,0x92b1,0x92ad,0x9292,0x9281,
+ 0x9284,0x4926,0x92ae,0x9290,0x929e,0x4998,0x4996,0x499a,0x95a2,0x95a7,
+ 0x4997,0x49e1,0x49e0,0x49e3,0x49e2,0x96a0,0x969d,0x969f,0x96d0,0x49fb,
+ 0x96d1,0x4a12,0x4a14,0x9759,0x4a45,0x9764,0x4a5c,0x4a5d,0x4ab8,0x9819,
+ 0x4aba,0x9814,0x9815,0x981a,0x4b03,0x4b35,0x4b36,0x4b39,0x9906,0x4b2d,
+ 0x98f8,0x9901,0x4b7a,0x99be,0x99bc,0x99b7,0x99b6,0x99c0,0x4b78,0x99b8,
+ 0x4b7b,0x4b7c,0x4b7e,0x99c4,0x4b7d,0x99bf,0x4bc9,0x9ada,0x9ae4,0x9ae9,
+ 0x9ae8,0x9aea,0x9ae5,0x4bf3,0x9b26,0x4c1a,0x4c19,0x9b40,0x4c1f,0x4ca6,
+ 0x4ca7,0x4ca8,0x4cab,0x4ca9
+ },
+ { /* ku 2b */
+ 0x4d2e,0x9ebd,0x4d5e,0x3495,0x3493,0x3492,0x510e,0x3496,0x50f7,0x3497,
+ 0x50fc,0x510d,0x5101,0x51da,0x51d9,0x51db,0x5286,0x528e,0x52ee,0x5333,
+ 0x53b1,0x35f5,0x5647,0x562d,0x5654,0x35ea,0x564b,0x5652,0x5631,0x5644,
+ 0x5656,0x5650,0x562b,0x35f3,0x564d,0x5637,0x564f,0x58a2,0x58b7,0x3669,
+ 0x58b2,0x366b,0x58aa,0x58b5,0x58b0,0x366c,0x58b4,0x58a4,0x58a7,0x3668,
+ 0x5926,0x5afe,0x3728,0x5b04,0x3726,0x5afc,0x3725,0x5b06,0x5b0a,0x5afa,
+ 0x5b0d,0x5b00,0x5b0e,0x376b,0x380f,0x3808,0x5d91,0x380c,0x5d8f,0x5d90,
+ 0x5d98,0x5da4,0x5d9b,0x5da3,0x5d96,0x5de4,0x5e5a,0x3860,0x3862,0x5e5e,
+ 0x3898,0x5fb8,0x6157,0x615c,0x61a6,0x6195,0x6188,0x398a,0x61a3,0x618f,
+ 0x3984,0x6164,0x397f,0x6159
+ },
+ { /* ku 2c */
+ 0x6178,0x3982,0x6185,0x6187,0x619e,0x3996,0x3989,0x6198,0x619c,0x398d,
+ 0x39bc,0x622f,0x6480,0x649b,0x648e,0x648d,0x6494,0x64c6,0x3a44,0x64a8,
+ 0x6483,0x3a3c,0x64b9,0x6486,0x64b4,0x64af,0x6491,0x3a4e,0x64aa,0x64a1,
+ 0x64a7,0x66b6,0x66b3,0x3b14,0x66bc,0x66ac,0x3b15,0x66ad,0x6a0e,0x3bce,
+ 0x6a1c,0x6a1a,0x3be0,0x3bc2,0x6a0b,0x3bbf,0x69ef,0x6a0c,0x69f0,0x6a22,
+ 0x3bc4,0x69d8,0x3bcf,0x6a12,0x69fa,0x3bc8,0x6a2a,0x3bcc,0x6a10,0x3bcd,
+ 0x3bc7,0x6a29,0x69f9,0x69ea,0x6a2c,0x6a24,0x4cb7,0x69e9,0x6b52,0x6b4f,
+ 0x6b53,0x3c43,0x3cb6,0x6f10,0x6f65,0x6f75,0x3d51,0x3d4a,0x3d4d,0x3d56,
+ 0x6fd0,0x3d53,0x6f5c,0x6f3d,0x6f71,0x3d59,0x6f91,0x6f0b,0x6f79,0x6f81,
+ 0x6f8f,0x3d4e,0x6f59,0x6f74
+ },
+ { /* ku 2d */
+ 0x3dee,0x71ae,0x3dec,0x71a3,0x71ad,0x3deb,0x3def,0x71ab,0x71a6,0x71a2,
+ 0x3ded,0x52f2,0x7257,0x7255,0x7299,0x734b,0x747a,0x3ef2,0x3eef,0x3ef1,
+ 0x748c,0x7484,0x3eed,0x3ef0,0x7482,0x7493,0x747b,0x3eee,0x7509,0x4c1b,
+ 0x3f50,0x3f66,0x3684,0x3fb8,0x3ff6,0x778a,0x4057,0x7790,0x405e,0x78c6,
+ 0x78d3,0x78c0,0x78d2,0x78c7,0x78c2,0x4119,0x799f,0x799d,0x799e,0x4170,
+ 0x7a41,0x416e,0x7a38,0x7a3a,0x7a42,0x4172,0x4176,0x7a3e,0x7ab0,0x7bae,
+ 0x7bb3,0x4212,0x421f,0x7bbf,0x4211,0x4216,0x7bcd,0x4219,0x7bb2,0x4224,
+ 0x4214,0x4225,0x4295,0x4296,0x4293,0x4294,0x7cc4,0x7ccd,0x7cc2,0x7cc6,
+ 0x7cc3,0x7cc9,0x7cc7,0x42a0,0x7df8,0x42fb,0x7ded,0x7de2,0x42fc,0x4300,
+ 0x42f8,0x7ddc,0x7e02,0x7e01
+ },
+ { /* ku 2e */
+ 0x42f9,0x7dd6,0x4304,0x7de4,0x7dfe,0x4303,0x7e00,0x7dfc,0x7dfd,0x42f3,
+ 0x7df5,0x7dff,0x42fa,0x7deb,0x7de5,0x7f78,0x7fae,0x7fe7,0x43bf,0x8065,
+ 0x806a,0x8066,0x8068,0x806b,0x8194,0x81a1,0x8192,0x8196,0x8193,0x4479,
+ 0x4510,0x8501,0x4514,0x84f8,0x450e,0x84f5,0x451a,0x8504,0x4519,0x4521,
+ 0x4523,0x451f,0x851b,0x8503,0x8533,0x8534,0x84ed,0x4525,0x452b,0x8535,
+ 0x4516,0x8505,0x4522,0x451b,0x45ce,0x45cf,0x877d,0x45cb,0x45d1,0x45cc,
+ 0x8771,0x4617,0x885c,0x88e6,0x890f,0x891b,0x4651,0x89a9,0x89a5,0x89ee,
+ 0x8ab1,0x46ed,0x8acc,0x8ace,0x46f4,0x8ab7,0x46f1,0x8ab5,0x8ae9,0x8ab4,
+ 0x46f8,0x8ab3,0x8ac1,0x8aaf,0x8aca,0x8ad0,0x472f,0x475e,0x475d,0x8c8e,
+ 0x4776,0x4777,0x8ce9,0x8cdb
+ },
+ { /* ku 2f */
+ 0x477e,0x8ceb,0x8da4,0x47b6,0x8da2,0x8d9d,0x47b3,0x47fc,0x4803,0x4800,
+ 0x8e2a,0x8e28,0x480a,0x4802,0x8eb8,0x8eb6,0x8eb9,0x8eb7,0x8f22,0x8f2b,
+ 0x8f27,0x8f19,0x8fa4,0x4887,0x8fb3,0x48a6,0x9071,0x906a,0x48a9,0x48de,
+ 0x9188,0x918c,0x92bf,0x92b8,0x92be,0x92dc,0x92e5,0x492e,0x492d,0x92d4,
+ 0x92d6,0x4930,0x92da,0x92ed,0x92f3,0x92db,0x492b,0x92b9,0x92e2,0x92eb,
+ 0x95af,0x499e,0x95b2,0x95b3,0x499f,0x49e5,0x49e4,0x96a3,0x96a5,0x49fd,
+ 0x49fc,0x4a17,0x4a19,0x970a,0x4a18,0x9787,0x9789,0x978c,0x97ef,0x982a,
+ 0x9822,0x4abf,0x981f,0x4b3c,0x9919,0x4b6b,0x99ca,0x99da,0x4b83,0x4b81,
+ 0x4b80,0x99de,0x99c8,0x99e0,0x4bca,0x9ab6,0x9ab5,0x4bce,0x9af4,0x4bf6,
+ 0x9b6b,0x9b69,0x9b72,0x9b63
+ },
+ { /* ku 30 */
+ 0x4c39,0x9d0d,0x4cae,0x9d01,0x9d0c,0x4cb5,0x9cf8,0x4cb3,0x4cb4,0x9cfe,
+ 0x9d02,0x9e84,0x4d22,0x9eab,0x9eaa,0x511d,0x5116,0x3499,0x512b,0x511e,
+ 0x511b,0x5290,0x5294,0x5314,UBOGON,0x3602,0x5667,0x3601,0x567b,0x36a1,
+ 0x565f,0x5661,0x35fd,0x3673,0x3674,0x3670,0x3676,0x3675,0x3672,0x58c3,
+ 0x58ca,0x58bb,0x58c0,0x58c4,0x5901,0x5b1f,0x5b18,0x5b11,0x5b15,0x3729,
+ 0x5b12,0x5b1c,0x372a,0x5b22,0x5b79,0x5da6,0x3816,0x5db3,0x5dab,0x5eea,
+ 0x3899,0x5f5b,0x38d3,0x38f5,0x61b7,0x61ce,0x61b9,0x61bd,0x61cf,0x61c0,
+ 0x6199,0x6197,0x3994,0x61bb,0x61d0,0x61c4,0x6231,0x3a56,0x64d3,0x64c0,
+ 0x3a59,0x3a58,0x3a55,0x3a52,0x64dc,0x64d1,0x64c8,0x3a57,0x64d5,0x66c3,
+ 0x3b1b,0x3b1c,0x66bf,0x66c5
+ },
+ { /* ku 31 */
+ 0x3b19,0x66cd,0x66c1,0x6706,0x3b3f,0x6724,0x6a63,0x6a42,0x6a52,0x3bdb,
+ 0x6a43,0x6a33,0x3be2,0x6a6c,0x6a57,0x3bd7,0x6a4c,0x6a6e,0x3bde,0x3be5,
+ 0x3be4,0x3be6,0x3bd6,0x6a37,0x3bdf,0x6a71,0x6a4a,0x6a36,0x3bdc,0x6a53,
+ 0x3bda,0x6a45,0x6a70,0x3bd3,0x3bd0,0x6a5c,0x6b58,0x6b57,0x3c86,0x3c87,
+ 0x3cad,0x3cb7,0x3d58,0x3d6a,0x6fbb,0x3d62,0x3d61,0x6fbe,0x3d69,0x3d6c,
+ 0x3d65,0x6fb5,0x6fd3,0x6f9f,0x3d66,0x6fb7,0x6ff5,0x71b7,0x3df5,0x71bb,
+ 0x3df4,0x71d1,0x3df7,0x71ba,0x3df8,0x71b6,0x71cc,0x3dfb,0x3dfc,0x71d3,
+ 0x749b,0x3ef5,0x3ef8,0x7496,0x74a2,0x749d,0x750a,0x750e,0x3f3c,0x7581,
+ 0x762c,0x7637,0x7636,0x763b,0x3fc5,0x76a1,0x4062,0x4063,0x7798,0x4067,
+ 0x7796,0x4066,0x40d9,0x40db
+ },
+ { /* ku 32 */
+ 0x78d6,0x78eb,0x40d8,0x78dc,0x411b,0x79a5,0x79a9,0x9834,0x7a53,0x7a45,
+ 0x4179,0x7a4f,0x417d,0x7abd,0x7abb,0x7af1,0x422c,0x4237,0x7bec,0x7bed,
+ 0x4230,0x429a,0x7cd3,0x4a00,0x7ce1,0x4305,0x7e19,0x4307,0x4309,0x430a,
+ 0x7e27,0x7e26,0x4379,0x43c2,0x806e,0x81af,0x4438,0x4437,0x81ad,0x4421,
+ 0x81aa,0x8218,0x445e,0x453d,0x4537,0x4540,0x856f,0x854c,0x451d,0x8542,
+ 0x4533,0x855c,0x8570,0x855f,0x4535,0x855a,0x854b,0x853f,0x878a,0x45d8,
+ 0x878b,0x87a1,0x878e,0x45dc,0x45de,0x8799,0x885e,0x885f,0x8924,0x89a7,
+ 0x8aea,0x8afd,0x8af9,0x8ae3,0x8ae5,0x46fa,0x46fb,0x8aec,0x473d,0x473b,
+ 0x473f,0x475f,0x8cf2,0x477f,0x8cef,0x4784,0x8da6,0x47bc,0x4814,0x480f,
+ 0x8e3b,0x8e43,0x480e,0x8e32
+ },
+ { /* ku 33 */
+ 0x8f31,0x8f30,0x4860,0x8f2d,0x8f3c,0x8fa7,0x8fa5,0x48ab,0x48ac,0x48aa,
+ 0x9137,0x9195,0x918e,0x4904,0x9196,0x4908,0x9345,0x930a,0x4933,0x4934,
+ 0x92fd,0x9317,0x931c,0x9307,0x9331,0x9332,0x932c,0x9330,0x9303,0x9305,
+ 0x49a2,0x95c2,0x49a4,0x95b8,0x49a5,0x95c1,0x49a7,0x49a6,0x49e7,0x96ab,
+ 0x96b7,0x49ff,0x49fe,0x9715,0x9714,0x4a1d,0x4a1c,0x970c,0x9717,0x4a67,
+ 0x9793,0x4a94,0x97d2,0x4ac5,0x4ac8,0x9836,0x9831,0x9833,0x983c,0x982e,
+ 0x983a,0x4ac9,0x983d,0x4ac7,0x98b5,0x9922,0x9923,0x9920,0x991c,0x991d,
+ 0x4b6c,0x99a0,0x4b8a,0x99ef,0x99e8,0x99eb,0x4b88,0x4b87,0x4b86,0x99e1,
+ 0x99e6,0x4bcf,0x4bd0,0x9af8,0x9af5,0x4c1c,0x4c23,0x9b83,0x9b94,0x9b84,
+ 0x4c49,0x9b8b,0x9b8f,0x4c43
+ },
+ { /* ku 34 */
+ 0x9b8c,0x4c48,0x9b89,0x4c47,0x9b8e,0x4c46,0x4c3f,0x4c44,0x9d24,0x9d0f,
+ 0x4cbe,0x9d13,0x9d0a,0x4cc2,0x4cba,0x4cbc,0x4cc6,0x9d2a,0x9d1a,0x4cc8,
+ 0x9d27,0x9d16,0x9d21,0x4d23,0x9e85,0x9eac,0x9ec6,0x9ec5,0x9ed7,0x9f53,
+ 0x349d,0x5128,0x5127,0x51df,0x3524,0x5335,0x53b3,0x3607,0x568a,0x567d,
+ 0x5689,0x3679,0x58cd,0x58d0,0x3678,0x5b2b,0x5b33,0x5b29,0x5b35,0x5b31,
+ 0x5b37,0x5c36,0x5dbe,0x3819,0x5db9,0x381c,0x5dbb,0x3818,0x61e2,0x61db,
+ 0x61dd,0x61dc,0x61da,UBOGON,0x61d9,0x39bd,0x3a5d,0x64df,0x3a5a,0x3a5e,
+ 0x64e1,0x3a5c,0x64ee,0x3a5b,0x65b5,0x66d4,0x66d5,0x3b21,0x66d0,0x66d1,
+ 0x66ce,0x66d7,0x3b20,0x3b32,0x6a7d,0x6a8a,0x3bf2,0x6aa7,0x3bf5,0x6a99,
+ 0x6a82,0x6a88,0x3bee,0x3bec
+ },
+ { /* ku 35 */
+ 0x6a86,0x3bea,0x6a98,0x6a9d,0x3bed,0x3bf3,0x6a8f,0x3bf6,0x6aaa,0x3c48,
+ 0x6b5d,0x3c49,0x6c0a,0x3d75,0x6fd7,0x6fd6,0x6fe5,0x3d6f,0x3d7b,0x3d73,
+ 0x6fd9,0x6fda,0x6fea,0x3d70,0x6ff6,UBOGON,0x3d78,0x71e3,0x3dfe,0x71e9,
+ 0x3e00,0x71eb,0x71ef,0x71f3,0x71ea,0x3e01,UBOGON,0x3e55,0x3e56,0x3e9d,
+ 0x7371,0x3ef9,0x74ae,0x3eff,0x74b3,0x3efd,0x74ac,0x3f43,0x3f41,0x7583,
+ 0x7645,0x764e,0x7644,0x76a3,0x76a5,0x77a6,0x77a4,0x406f,0x77a9,0x77af,
+ 0x408a,0x40e5,0x40e6,0x78f0,0x78f8,0x78f1,0x417f,0x7a49,0x41b5,0x41b6,
+ 0x41bb,0x7ac2,0x7af2,0x7af3,0x7bfa,0x4240,0x7bf6,0x7bfc,0x7c18,0x7c08,
+ 0x7c12,0x429d,0x429c,0x7cdb,0x7cda,0x430f,0x4311,0x430d,0x7e2c,0x7e4d,
+ 0x4314,0x4313,0x7f46,0x7ff6
+ },
+ { /* ku 36 */
+ 0x802b,0x8074,0x81b8,0x81c8,0x4482,0x4483,0x454d,0x8592,0x8593,0x454f,
+ 0x857f,0x85ab,0x8597,0x454c,0x4551,0x85ac,0x45ee,0x45e8,0x4ccb,0x87ce,
+ 0x45eb,0x87cd,0x45e2,0x45e6,0x87c1,0x87b1,0x87c7,0x45ec,0x8940,0x4659,
+ 0x893f,0x8939,0x465d,0x8943,0x4657,0x465b,0x4656,0x89ab,0x46fe,0x8b1f,
+ 0x8b09,0x8b0c,0x4700,0x4701,0x8c40,0x4742,0x8c96,0x4760,0x8cf6,0x8cf7,
+ 0x481d,0x8e46,0x8e4f,0x483e,0x4869,0x4865,0x8f3d,0x8f41,0x9366,0x9378,
+ 0x935d,0x9369,0x9374,0x937d,0x936e,0x9372,0x9373,0x9362,0x9348,0x9353,
+ 0x935f,0x9368,0x4938,0x937f,0x936b,0x49ae,0x95c4,0x49ad,0x96af,0x96ad,
+ 0x96b2,0x4a02,0x4a1f,0x971a,0x971b,0x4a22,0x4a20,UBOGON,0x4a6c,0x979b,
+ 0x979f,0x4a68,0x4a6d,0x4a6e
+ },
+ { /* ku 37 */
+ 0x4aa0,0x4ace,0x4ad0,0x4ad1,0x4acb,0x9840,0x4ad2,0x9847,0x4ad3,0x98b7,
+ 0x4b20,0x4b4e,0x4b4b,0x4b72,0x4b70,0x99a2,0x4b92,0x4b8f,0x9a00,0x99f3,
+ 0x4b90,UBOGON,0x99f5,0x4bd9,0x4bd5,0x9abd,0x9b00,0x9b02,0x4bfa,0x9b34,
+ 0x9b49,0x9b9f,0x4c4b,0x9ba3,0x9bcd,0x9b99,0x9b9d,0x4cd0,0x4cce,0x9d39,
+ 0x4ccf,0x9d44,0x4cc4,0x4ccc,0x9d35,0x4cd2,0x4d35,0x9eaf,0x3e03,0x512f,
+ 0x349e,0x34af,0x9f8e,0x360c,0x569f,0x569b,0x569e,0x5696,0x5694,0x56a0,
+ 0x367c,0x5b3b,0x3730,0x3731,0x5b3a,0x5dc1,0x5f4d,0x5f5d,0x61f3,0x39a1,
+ 0x399e,0x3a68,0x3a61,0x64f6,0x64e5,0x64ea,0x64e7,0x6505,0x3a65,0x64f9,
+ 0x3a66,0x3a6a,0x3aab,0x6aab,0x6aed,0x6ab2,0x6ab0,0x6ab5,0x6abe,0x6ac1,
+ 0x6ac8,0x3bf9,0x6ac0,0x6abc
+ },
+ { /* ku 38 */
+ 0x6ab1,0x6ac4,0x6abf,0x3c58,0x3c8a,0x7008,0x7003,0x6ffd,0x7010,0x7002,
+ 0x7013,0x3e04,0x71fa,0x7200,0x74b9,0x74bc,0x3f02,0x765b,0x7651,0x764f,
+ 0x76eb,0x77b8,0x4079,0x77b9,0x77c1,0x77c0,0x77be,0x790b,0x40eb,0x7907,
+ 0x790a,0x7908,0x40e9,0x790d,0x7906,0x7915,0x79af,0x4120,0x4121,0x4181,
+ 0x7af5,0x424d,0x4259,0x7c2e,0x4258,0x7c1b,UBOGON,0x7c1a,0x7c24,0x42a5,
+ 0x42a9,0x7ce6,0x7ce3,0x431a,0x4319,0x7e5d,0x7e4f,0x7e66,0x7e5b,0x7f47,
+ 0x7fb4,0x4396,0x4398,0x4397,0x7ffa,0x802e,UBOGON,0x43c8,0x81ce,0x4443,
+ 0x4445,0x8219,0x4552,0x4557,0x85cc,0x85b2,0x4555,0x85bb,0x85c1,0x4556,
+ 0x4558,0x45f2,0x87e9,0x87ee,0x87f0,0x87d6,0x880e,0x87da,0x8948,0x894a,
+ 0x894e,0x894d,0x89b1,0x89b0
+ },
+ { /* ku 39 */
+ 0x89b3,0x4707,0x8b38,0x8b32,0x4708,0x8b2d,0x470a,0x8b34,0x431b,0x8b29,
+ 0x8c74,0x4761,0x4762,0x8d03,0x47c2,0x47c6,0x8da9,0x8e58,0x481e,0x4825,
+ 0x8ebf,0x8ec1,0x8f4a,0x8fac,0x48b0,0x9089,0x913d,0x913c,0x91a9,0x93a0,
+ 0x493d,0x9390,0x493e,0x9393,0x938b,0x93ad,0x93bb,0x93b8,0x4946,0x4945,
+ 0x939c,0x95d8,0x95d7,0x4a03,0x4a26,0x4a27,0x975d,0x97a9,0x97da,0x4a98,
+ 0x4aad,0x4ad5,0x4ada,0x9854,0x4ad9,0x9855,0x984b,0x4add,0x983f,0x98b9,
+ 0x4b15,0x4b16,0x4b17,0x4b21,0x9938,0x9936,0x9940,0x4b4c,0x993b,0x9939,
+ 0x99a4,0x4b96,0x4b98,0x9a08,0x9a0c,0x4b9b,0x9a10,0x4bff,0x9b07,0x4c25,
+ 0x9bd2,0x4c4f,0x9bc2,0x9bbb,0x9bcc,0x9bcb,0x4c56,0x4c54,0x9d4d,0x9d63,
+ 0x9d4e,0x4cd8,0x9d50,0x9d55
+ },
+ { /* ku 3a */
+ 0x4cd7,0x9d5e,0x4d26,0x9e90,0x9eb2,0x9eb1,0x4d38,0x9eca,0x9f02,0x9f27,
+ 0x9f26,0x4d8a,0x56af,0x58e0,0x58dc,0x3734,0x5b39,0x3735,UBOGON,0x5b7c,
+ 0x5bf3,UBOGON,0x37a1,0x5c6b,0x5dc4,0x650b,0x6508,0x650a,0x3a6c,0x3a6d,
+ 0x65dc,0x3b29,0x3b2a,0x66e1,0x66df,0x6ace,0x6ad4,0x6ae3,0x6ad7,0x6ae2,
+ 0x3c00,0x3c08,0x3c06,0x3c05,0x6ad8,0x6ad5,0x6ad2,0x3cb1,0x3d88,0x701e,
+ 0x702c,0x7025,0x6ff3,0x7204,0x7208,0x7215,0x3e09,0x74c4,0x74c9,0x74c7,
+ 0x74c8,0x76a9,0x77c6,0x77c5,0x7918,0x791a,0x7920,0x4122,0x7a66,0x7a64,
+ 0x7a6a,0x41d5,0x4261,0x425d,0x4262,0x424f,0x4260,0x7c35,0x7c34,0x42aa,
+ 0x4322,0x7e6c,0x4321,0x7e6e,0x7e71,0x4446,0x81d4,0x81d6,0x821a,0x8262,
+ 0x8265,0x8276,0x85db,0x85d6
+ },
+ { /* ku 3b */
+ 0x4562,0x85e7,0x4560,0x4564,0x85f4,UBOGON,0x87fd,0x87d5,0x8807,0x45f6,
+ 0x880f,0x87f8,UBOGON,0x4619,0x8987,0x4691,0x89b5,0x89f5,0x470d,0x8b3f,
+ 0x8b43,0x8b4c,0x4765,0x8d0b,0x8e6b,0x8e68,0x8e70,0x8e75,0x8e77,0x483f,
+ 0x8ec3,0x494b,0x93e9,0x93ea,0x93cb,0x93c5,0x93c6,0x4948,0x93ed,0x93d3,
+ 0x4952,0x93e5,0x494a,0x4951,0x93db,0x93eb,0x93e0,0x93c1,0x4950,0x494c,
+ 0x95dd,0x49ee,0x4a04,0x4a06,0x4a2d,0x4a2e,0x4a2f,0x4a7b,0x4a78,0x4a77,
+ 0x97b2,0x97b4,0x97b1,0x97b5,0x97f2,0x4aa2,0x4aa1,0x4ae3,0x9856,0x4b1a,
+ 0x4b19,0x4b57,0x9944,0x4b9e,0x9a26,0x9a1f,0x9a18,0x9a21,0x9a17,0x4bdd,
+ 0x9b09,0x4c05,0x4c28,0x9bc5,0x9bdf,0x4c60,0x9be3,0x4c66,0x9be9,0x9bee,
+ 0x4c67,0x4c68,0x9d66,0x9d7a
+ },
+ { /* ku 3c */
+ 0x4cde,0x9d6e,0x9d91,0x9d83,0x9d76,0x9d7e,0x9d6d,0x4ce1,0x9e95,0x9ee3,
+ 0x4d69,0x4d77,0x9f03,0x9f04,UBOGON,0x9f17,0x34a6,0x5136,0x34a5,0x5336,
+ 0x3614,0x5b42,0x3736,0x3738,0x5b44,0x5b46,0x5b7e,0x5dca,0x5dc8,0x5dcc,
+ 0x5ef0,0x3a70,0x6585,0x66e5,0x66e7,0x3b2b,0x3c11,0x3c0a,0x6af4,0x3c0d,
+ 0x6ae9,0x3c16,0x3c10,0x3c09,0x3c0e,0x3c7a,0x703d,0x3d8c,0x7036,0x3d91,
+ 0x7216,0x3e0a,0x7212,0x720f,0x7217,0x7211,0x720b,0x3e08,0x3e0b,0x74cd,
+ 0x74d0,0x74cc,0x74ce,0x74d1,0x3f07,0x7589,0x40f2,0x7a6f,0x7c4b,0x7c44,
+ 0x7c55,0x42ae,0x4324,0x4326,0x4327,0x7e7f,0x8b71,0x4399,0x802f,0x807a,
+ 0x807b,0x807c,0x455f,0x456a,0x4571,0x85fc,0x8610,0x8602,0x456c,0x456f,
+ 0x85ee,0x8603,0x4568,0x860d
+ },
+ { /* ku 3d */
+ 0x8613,0x8608,0x860f,0x8818,0x8812,0x4601,0x4668,0x8967,0x8965,0x89bb,
+ 0x8b69,0x8b62,0x4713,0x8b6e,0x4716,0x8b61,0x4718,0x8b64,0x8b4d,0x8c51,
+ 0x4789,0x47c8,0x8e83,0x8ec6,0x4884,0x941f,0x4954,0x9404,0x9417,0x9408,
+ 0x9405,0x4956,0x93f3,0x941e,0x9402,0x941a,0x941b,0x9427,0x941c,0x495a,
+ 0x96b5,0x4a05,0x4a07,0x9733,0x4a31,0x9734,0x9731,0x97b8,0x97ba,0x4aa3,
+ 0x97fc,0x4aeb,0x4b1c,0x98c3,0x4b5a,0x994d,0x4b5b,0x9a2f,0x4ba6,0x4baa,
+ 0x4ba5,0x9ac9,0x4be1,0x9ac8,0x9ac4,0x9b2a,0x9b38,0x9b50,0x4c2a,0x9c0a,
+ 0x9bfb,0x9c04,0x9bfc,0x9bfe,0x4c72,0x4c6f,0x4c73,0x9c02,0x9bf6,0x9c1b,
+ 0x9bf9,0x9c15,0x9c10,0x9bff,0x9c00,0x9c0c,0x4c6b,0x4ce6,0x9d95,0x9da5,
+ 0x4ce9,0x4cec,0x4ce8,0x4cf0
+ },
+ { /* ku 3e */
+ 0x9e98,0x9ec1,0x4d8c,0x9f5a,0x5164,0x56bb,0x3615,0x58e6,0x5b49,0x5bf7,
+ 0x3771,0x3826,0x5dd0,0x38c6,0x5fc2,0x39a8,0x6511,0x3a73,0x6aff,0x6afe,
+ 0x6afd,0x3c15,0x6b01,0x3d98,0x3d97,0x704b,0x704d,0x7047,0x74d3,0x7668,
+ 0x7667,0x3fd7,0x4080,0x77d1,0x7930,0x7932,0x792e,0x4188,0x9f9d,0x7ac9,
+ 0x7ac8,0x4269,0x7c56,0x7c51,0x426b,0x4329,0x4328,0x7e85,0x7e89,0x7e8e,
+ 0x7e84,0x445f,0x826a,0x862b,0x862f,0x8628,0x4574,0x8616,0x8615,0x861d,
+ 0x881a,0x4602,0x466a,0x4694,0x89bc,0x8b75,0x8b7c,0x478a,0x8d11,0x8d12,
+ 0x8f5c,0x91bb,0x4964,0x93f4,0x495e,0x4961,0x942d,0x4965,0x4966,0x96e4,
+ 0x9737,0x9736,0x9767,0x97be,0x97bd,0x97e2,0x9868,0x9866,0x98c8,0x98ca,
+ 0x98c7,0x98dc,0x4b5f,0x994f
+ },
+ { /* ku 3f */
+ 0x99a9,0x9a3c,0x4baf,0x9a3b,0x9ace,0x4c0d,0x9b14,0x9b53,0x4c7c,0x9c2e,
+ 0x4c7a,0x9c1f,0x4c76,0x4c79,0x4c7d,0x4c77,0x9db0,0x9dbd,0x4cf6,0x4cf1,
+ 0x9dae,0x9dc4,0x9e7b,0x400b,0x4d29,0x9e9e,0x4d6f,0x9f05,0x4d9a,0x9f69,
+ 0x9fa1,0x56c7,0x571d,0x5b4a,0x5dd3,0x3869,0x5f72,0x6202,0x39ab,0x6235,
+ 0x6527,0x651e,0x651f,0x3b2c,0x3b2d,0x6b07,0x6b06,0x3c17,0x3d9a,0x7054,
+ 0x721c,0x7220,0x7af8,0x426e,0x7c5d,0x7c58,0x432c,0x7e92,0x7f4e,0x43ca,
+ 0x4578,0x4606,0x8827,0x4607,0x8b81,0x8b83,0x4720,0x8c44,0x4753,0x47ce,
+ 0x487a,0x4879,0x9442,0x944d,0x9454,0x944e,0x496b,0x9443,0x4967,0x496d,
+ 0x973c,0x9740,0x97c0,0x4a85,0x4ab0,0x4af3,0x4b63,0x995a,0x9a51,0x4bb6,
+ 0x9add,0x4c82,0x4c7f,0x9c38
+ },
+ { /* ku 40 */
+ 0x4c86,0x9c45,0x9c3a,0x4c84,0x9c35,0x4cfc,0x4cfd,0x4cfa,0x9ef1,0x4d87,
+ 0x9f93,0x529a,0x361a,0x3619,0x8641,0x5dd7,0x3a75,0x6528,0x3c1a,0x3c1b,
+ 0x3c19,0x7053,0x7059,0x3d9c,0x7221,0x3e10,0x766f,0x7937,0x79b5,0x7c62,
+ 0x7c5e,0x7cf5,0x457b,0x457c,0x863d,0x4608,0x882d,0x8989,0x8b8d,0x8b87,
+ 0x8b90,0x8d1a,0x8e99,0x4841,0x48e3,0x4972,0x945f,0x4973,0x4968,0x9456,
+ 0x9461,0x945b,0x945a,0x945c,0x9465,0x4a35,0x9741,0x4a88,0x4a9d,0x986e,
+ 0x986c,0x986d,0x4275,0x99aa,0x9a5c,0x9a58,0x9ade,0x4c8f,0x9c4f,0x9c51,
+ 0x4c8e,0x9c53,0x4d05,0x4d04,0x4cff,0x9dfc,0x9f39,0x4d9e,0x513e,0x3554,
+ 0x56d2,0x3681,0x5b4f,0x6b14,0x40fa,0x7a72,0x7a73,0x4332,0x4670,0x466e,
+ 0x8b91,UBOGON,0x487c,0x91bf
+ },
+ { /* ku 41 */
+ 0x4975,0x946c,0x4974,0x4977,0x96e6,0x9745,0x4a37,0x97c8,0x97e4,0x995d,
+ 0x4bba,0x9b21,0x4c11,0x9b2c,0x9b57,0x4c92,0x4c99,0x9c5d,0x9c61,0x9c65,
+ 0x9e08,0x4d0a,0x4d2a,0x4d2b,0x4d44,0x4d79,0x9f45,0x34aa,0x3748,0x6205,
+ 0x66ef,0x6b1b,0x6b1d,0x7225,0x7224,0x7c6d,0x42b4,0x8642,0x8649,0x460d,
+ 0x8978,0x898a,0x8b97,0x4754,0x8c9b,0x8d1c,0x4830,0x8ea2,0x4a09,0x4a38,
+ 0x4a36,0x4a8b,0x4af7,0x4b66,0x4bbd,0x4c1e,0x9c6c,0x4c96,0x9c6f,0x4d0d,
+ 0x9e0e,0x4d73,0x9f08,0x9f1d,0x9fa3,0x373b,0x373c,0x5f60,0x6b1c,0x3da0,
+ 0x40fb,UBOGON,0x7cf3,0x4581,0x8b9b,0x8ea7,0x91c4,0x4978,0x947a,0x4a8d,
+ 0x4b73,0x9a61,0x9a63,0x9ad7,0x9c76,0x4da6,0x9fa5,0x39ad,0x7067,0x3e11,
+ 0x72ab,0x864a,0x897d,0x8b9d
+ },
+ { /* ku 42 */
+ 0x8c53,0x8f65,0x947b,0x4a39,0x98cd,0x98dd,0x4bbf,0x9b30,0x9e16,0x4d0f,
+ 0x4da7,0x4db5,0x3fdc,0x4831,0x96e7,0x9e18,0x9ea2,0x4da8,0x9f7c,0x4125,
+ 0x7e9e,0x9484,0x4bc1,0x9e1c,0x4190,0x7c71,0x97ca,0x4696,0x487f,0x4d10,
+ 0x9ea3,0x4a0a,0x9c7b,0x9f97,0x4d12,0x4a3a,0x9750,0x4a3b,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 43 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 44 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4f66,
+ 0x4f68,0x4fe7,0x503f,UBOGON,0x50a6,0x510f,0x523e,0x5324,0x5365,0x539b,
+ 0x517f,0x54cb,0x5573,0x5571,0x556b,0x55f4,0x5622,0x5620,0x5692,0x56ba,
+ 0x5691,0x56b0,0x5759,0x578a,0x580f,0x5812,0x5813,0x5847,0x589b,0x5900,
+ 0x594d,0x5ad1,0x5ad3,0x5b67,0x5c57,0x5c77,0x5cd5,0x5d75,0x5d8e,0x5da5,
+ 0x5db6,0x5dbf,0x5e65,0x5ecd,0x5eed,0x5f94,0x5f9a,0x5fba,0x6125,0x6150,
+ 0x62a3,0x6360,0x6364,0x63b6
+ },
+ { /* ku 45 */
+ 0x6403,0x64b6,0x651a,0x7a25,0x5c21,0x66e2,0x6702,0x67a4,0x67ac,0x6810,
+ 0x6806,0x685e,0x685a,0x692c,0x6929,0x6a2d,0x6a77,0x6a7a,0x6aca,0x6ae6,
+ 0x6af5,0x6b0d,0x6b0e,0x6bdc,0x6bdd,0x6bf6,0x6c1e,0x6c63,0x6da5,0x6e0f,
+ 0x6e8a,0x6e84,0x6e8b,0x6e7c,0x6f4c,0x6f48,0x6f49,0x6f9d,0x6f99,0x6ff8,
+ 0x702e,0x702d,0x705c,0x79cc,0x70bf,0x70ea,0x70e5,0x7111,0x7112,0x713f,
+ 0x7139,0x713b,0x713d,0x7177,0x7175,0x7176,0x7171,0x7196,0x7193,0x71b4,
+ 0x71dd,0x71de,0x720e,0x5911,0x7218,0x7347,0x7348,0x73ef,0x7412,0x743b,
+ 0x74a4,0x748d,0x74b4,0x7673,0x7677,0x76bc,0x7819,0x781b,0x783d,0x7853,
+ 0x7854,0x7858,0x78b7,0x78d8,0x78ee,0x7922,0x794d,0x7986,0x7999,0x79a3,
+ 0x79bc,0x7aa7,0x7b37,0x7b59
+ },
+ { /* ku 46 */
+ 0x7bd0,0x7c2f,0x7c32,0x7c42,0x7c4e,0x7c68,0x7ca9,0x7ced,0x7dd0,0x7e07,
+ 0x7dd3,0x7e64,0x7f40,UBOGON,0x8041,0x8063,0x80bb,0x6711,0x6725,0x8248,
+ 0x8310,0x8362,0x8312,0x8421,0x841e,0x84e2,0x84de,0x84e1,0x8573,0x85d4,
+ 0x85f5,0x8637,0x8645,0x8672,0x874a,0x87a9,0x87a5,0x87f5,0x8834,0x8850,
+ 0x8887,0x8954,0x8984,0x8b03,0x8c52,0x8cd8,0x8d0c,0x8d18,0x8db0,0x8ebc,
+ 0x8ed5,0x8faa,0x909c,UBOGON,0x915c,0x922b,0x9221,0x9273,0x92f4,0x92f5,
+ 0x933f,0x9342,0x9386,0x93be,0x93bc,0x93bd,0x93f1,0x93f2,0x93ef,0x9422,
+ 0x9423,0x9424,0x9467,0x9466,0x9597,0x95ce,0x95e7,0x973b,0x974d,0x98e4,
+ 0x9942,0x9b1d,0x9b98,UBOGON,0x9d49,0x6449,0x5e71,0x5e85,0x61d3,0x990e,
+ 0x8002,0x781e,UBOGON,UBOGON
+ },
+ { /* ku 47 */
+ 0x5528,0x5572,0x55ba,0x55f0,0x55ee,0x56b8,0x56b9,0x56c4,0x8053,0x92b0,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ }
+};
+
+/* CNS 11643 plane 4 conversion table */
+
+static const unsigned short
+ cns11643_4tab[MAX_CNS11643_KU_4][MAX_CNS11643_TEN] = {
+ { /* ku 01 */
+ UBOGON,0x4e40,0x4e41,0x4e5a,UBOGON,0x4e02,0x4e29,UBOGON,UBOGON,0x5202,
+ 0x353e,0x5ddc,UBOGON,UBOGON,UBOGON,0x5342,0x536a,0x5b52,UBOGON,UBOGON,
+ UBOGON,0x5fc4,0x624c,0x72ad,0x4e12,0x4e2f,0x4e96,0x4ed0,0x5142,0x5183,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5383,0x53b8,UBOGON,UBOGON,UBOGON,
+ 0x5928,UBOGON,0x5c23,0x5e01,0x5f00,UBOGON,0x3cb8,0x706c,0x722b,0x5188,
+ 0x8279,0x8fb6,0x4e17,UBOGON,0x340c,UBOGON,0x3430,0x4ee2,0x4edb,UBOGON,
+ UBOGON,0x51ad,UBOGON,0x51f7,0x34da,UBOGON,UBOGON,0x3513,0x531b,0x5388,
+ 0x5387,UBOGON,0x53cf,0x53fd,0x3563,0x53e7,0x56dc,UBOGON,0x56d9,0x5725,
+ 0x5727,0x5933,0x5c13,UBOGON,UBOGON,0x5c75,UBOGON,UBOGON,UBOGON,0x39c4,
+ 0x39c3,0x66f1,UBOGON,UBOGON
+ },
+ { /* ku 02 */
+ 0x7f52,UBOGON,UBOGON,0x3401,UBOGON,UBOGON,0x4e51,0x4e6a,UBOGON,0x4f0c,
+ UBOGON,UBOGON,0x4efe,0x4f1b,UBOGON,UBOGON,0x343a,UBOGON,0x34ab,0x5173,
+ UBOGON,0x518e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x34dd,UBOGON,UBOGON,
+ 0x52a5,0x3515,0x52a7,0x52a4,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x53bd,
+ UBOGON,UBOGON,UBOGON,0x5402,UBOGON,UBOGON,UBOGON,UBOGON,0x572b,0x591b,
+ 0x5935,UBOGON,0x36a7,0x36a5,UBOGON,UBOGON,0x36a6,UBOGON,UBOGON,0x5c17,
+ 0x377c,UBOGON,0x5c70,0x5c7d,0x37a9,UBOGON,0x5de9,UBOGON,0x3834,0x3835,
+ UBOGON,UBOGON,UBOGON,0x38a8,0x5f19,0x5f1c,0x5f75,UBOGON,UBOGON,0x38ff,
+ 0x5fc8,UBOGON,0x39c7,0x39c6,0x39c8,UBOGON,UBOGON,0x3ad0,0x3ad1,UBOGON,
+ UBOGON,0x3c59,UBOGON,UBOGON
+ },
+ { /* ku 03 */
+ 0x6c12,0x3cbd,UBOGON,UBOGON,UBOGON,0x3e28,0x72b3,UBOGON,0x3ea9,0x7390,
+ 0x7536,UBOGON,0x43cc,UBOGON,0x8281,0x8fb8,UBOGON,0x48b4,UBOGON,UBOGON,
+ 0x4e23,0x3416,0x342c,UBOGON,0x4f2e,UBOGON,0x514f,UBOGON,0x51ba,0x34df,
+ 0x34e0,0x5222,UBOGON,UBOGON,UBOGON,0x3517,UBOGON,0x52af,0x52b0,0x52b1,
+ UBOGON,UBOGON,0x352f,UBOGON,0x5364,UBOGON,0x53d3,UBOGON,0x3574,UBOGON,
+ 0x3570,0x356d,UBOGON,UBOGON,UBOGON,UBOGON,0x356e,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x362b,0x3628,UBOGON,UBOGON,0x593f,UBOGON,UBOGON,0x3692,UBOGON,
+ 0x598b,UBOGON,0x5991,0x5995,UBOGON,UBOGON,0x373f,UBOGON,0x5b8a,0x374f,
+ 0x3774,UBOGON,UBOGON,0x377d,UBOGON,0x37b7,0x37a3,0x37b0,0x37b1,0x5c87,
+ 0x37ab,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 04 */
+ 0x383a,0x3837,0x5e0d,0x3838,0x3840,UBOGON,UBOGON,0x5e8e,0x389f,UBOGON,
+ UBOGON,0x5f7a,UBOGON,0x3904,0x3909,0x3906,0x38fd,0x390a,0x3907,UBOGON,
+ UBOGON,0x39ca,UBOGON,UBOGON,0x6290,0x39c9,UBOGON,0x629a,UBOGON,0x653c,
+ 0x653a,0x3a7f,0x6598,UBOGON,0x3ad2,UBOGON,UBOGON,0x6765,UBOGON,0x3b43,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3cc1,UBOGON,0x3cc5,0x3da3,
+ UBOGON,UBOGON,0x3e2a,0x3e5f,UBOGON,UBOGON,0x3e5d,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3f17,UBOGON,UBOGON,0x3f71,0x3f72,UBOGON,UBOGON,0x400f,UBOGON,
+ UBOGON,0x79c2,0x4191,UBOGON,UBOGON,UBOGON,0x43b2,0x43cf,0x43ce,0x809e,
+ UBOGON,UBOGON,0x81eb,UBOGON,0x8289,0x4496,UBOGON,UBOGON,0x8296,UBOGON,
+ 0x8287,UBOGON,0x4497,UBOGON
+ },
+ { /* ku 05 */
+ 0x8fc0,0x488b,0x8fc3,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x9578,UBOGON,UBOGON,0x9625,UBOGON,0x4e75,0x4e74,UBOGON,UBOGON,0x342d,
+ UBOGON,0x4f99,UBOGON,0x3450,0x344b,UBOGON,0x344f,0x344c,UBOGON,0x4f71,
+ 0x5153,0x51bf,UBOGON,UBOGON,0x51c0,UBOGON,0x51ee,UBOGON,0x34e4,0x34e3,
+ UBOGON,0x34e1,UBOGON,0x34e2,UBOGON,0x523d,0x3519,0x52bd,0x530c,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3541,0x7f37,UBOGON,0x53c0,0x355e,UBOGON,UBOGON,
+ 0x3579,UBOGON,0x546e,0x5483,UBOGON,UBOGON,0x545e,0x545d,0x577e,0x5779,
+ UBOGON,0x577a,0x576c,UBOGON,UBOGON,UBOGON,0x3632,0x5787,UBOGON,0x591d,
+ 0x3694,0x5946,0x3697,UBOGON,0x5943,UBOGON,0x3696,0x3698,UBOGON,UBOGON,
+ 0x36b2,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 06 */
+ 0x36b9,0x5b61,0x5b66,UBOGON,0x5b90,0x3775,0x377f,0x377e,0x5c29,0x378f,
+ UBOGON,UBOGON,UBOGON,0x37bd,0x5cb2,UBOGON,0x37bb,0x37bc,UBOGON,0x5cc0,
+ UBOGON,0x383d,0x383e,0x3874,UBOGON,0x387a,0x3876,0x3878,0x3875,UBOGON,
+ UBOGON,0x38af,0x38b0,0x38c7,0x38cc,UBOGON,UBOGON,0x3916,UBOGON,0x3912,
+ 0x391d,UBOGON,UBOGON,0x3915,0x390f,0x3914,0x601f,0x5fe2,UBOGON,UBOGON,
+ UBOGON,0x39b0,0x39bf,0x39c0,UBOGON,0x39d2,0x39d9,UBOGON,0x3a7a,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x6616,0x65f9,0x3ada,UBOGON,0x6788,UBOGON,
+ 0x679b,UBOGON,0x676e,0x679e,UBOGON,0x3c22,0x3c1f,UBOGON,UBOGON,0x3c21,
+ 0x6b24,UBOGON,UBOGON,UBOGON,0x3c5c,0x6b7d,UBOGON,0x3c7d,0x3c8d,0x3c8f,
+ 0x6ce6,UBOGON,0x6ccb,0x3cd0
+ },
+ { /* ku 07 */
+ UBOGON,0x3cd8,UBOGON,UBOGON,UBOGON,0x6cb5,0x3da7,UBOGON,0x7097,UBOGON,
+ 0x709b,0x3e12,UBOGON,UBOGON,0x3e2f,UBOGON,0x726b,0x3e2e,0x3e2c,0x3e5c,
+ UBOGON,0x72d5,UBOGON,UBOGON,0x3e62,0x3e67,0x3eb4,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x7543,UBOGON,UBOGON,0x759c,UBOGON,UBOGON,0x3fea,UBOGON,0x3ffb,
+ UBOGON,0x4014,UBOGON,0x4013,0x4012,0x4010,0x4011,UBOGON,0x4086,0x77e4,
+ 0x4098,UBOGON,UBOGON,0x412a,UBOGON,UBOGON,UBOGON,UBOGON,0x7ace,0x42b5,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x8013,0x43d6,0x43d8,0x80b7,0x43d9,0x43d4,
+ 0x43d7,UBOGON,0x80b9,UBOGON,UBOGON,0x81e4,0x81fd,0x820f,0x4460,UBOGON,
+ UBOGON,0x449e,0x44a1,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x82bf,
+ 0x82ca,UBOGON,UBOGON,0x82c1
+ },
+ { /* ku 08 */
+ 0x44a0,UBOGON,UBOGON,UBOGON,UBOGON,0x8fd0,UBOGON,UBOGON,0x48b9,UBOGON,
+ 0x90ae,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x49c1,0x49c2,0x9638,UBOGON,
+ 0x341c,UBOGON,0x345e,0x4fbc,0x3459,0x345c,UBOGON,0x345f,0x4fe9,0x4fbd,
+ 0x4fe2,0x5158,UBOGON,UBOGON,0x34ce,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x52c6,UBOGON,0x52c8,UBOGON,UBOGON,0x5328,UBOGON,0x5329,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x355f,UBOGON,0x3585,UBOGON,UBOGON,0x3586,
+ UBOGON,0x57b4,UBOGON,0x57a9,0x3687,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x36ca,UBOGON,0x36c3,UBOGON,UBOGON,UBOGON,0x36c2,0x5b68,UBOGON,0x3741,
+ UBOGON,UBOGON,UBOGON,0x3780,0x3781,UBOGON,UBOGON,0x3793,0x3792,UBOGON,
+ 0x37c5,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 09 */
+ 0x3846,0x3841,0x3845,0x3842,0x383f,UBOGON,UBOGON,0x3882,0x3881,0x387f,
+ 0x38a5,0x5f2b,0x38b3,0x38b5,UBOGON,UBOGON,0x5f8d,0x38da,UBOGON,0x38db,
+ 0x390d,0x6018,0x390e,UBOGON,0x391e,0x3925,0x3926,0x391c,0x3921,0x6057,
+ 0x6048,0x3927,0x391a,UBOGON,UBOGON,UBOGON,0x6038,UBOGON,UBOGON,0x3924,
+ UBOGON,UBOGON,0x6071,UBOGON,0x39c1,0x39e1,UBOGON,UBOGON,0x6312,0x39eb,
+ UBOGON,0x39e2,0x39d7,0x39e9,UBOGON,UBOGON,0x630a,UBOGON,0x6323,UBOGON,
+ 0x3a84,UBOGON,UBOGON,UBOGON,0x3ab5,0x3abc,0x3adc,0x3ade,0x3adf,UBOGON,
+ 0x662a,UBOGON,UBOGON,UBOGON,UBOGON,0x3b54,0x67e0,0x67be,0x3b53,0x3c24,
+ 0x3c25,0x6b29,0x3c28,0x3c27,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3c8b,UBOGON,UBOGON,0x3cdc
+ },
+ { /* ku 0a */
+ 0x6d43,UBOGON,UBOGON,UBOGON,UBOGON,0x70a6,0x3db2,0x70c0,UBOGON,0x722f,
+ UBOGON,0x3e1b,UBOGON,0x3e32,0x7271,UBOGON,UBOGON,0x3e6b,UBOGON,0x3e6c,
+ 0x3e6d,UBOGON,0x3eb9,0x3eba,0x3f09,0x3f0a,UBOGON,0x74ea,0x3f1b,UBOGON,
+ UBOGON,0x7520,0x3f58,UBOGON,0x3f5a,UBOGON,UBOGON,0x3f77,UBOGON,UBOGON,
+ 0x3f79,0x75a9,UBOGON,0x7685,UBOGON,0x3feb,UBOGON,0x3ffd,0x3ffc,0x7706,
+ 0x4015,0x4018,0x76f6,0x4016,0x4017,0x4019,0x7700,0x401b,UBOGON,UBOGON,
+ 0x7702,UBOGON,0x4087,UBOGON,UBOGON,0x409c,UBOGON,0x409a,UBOGON,0x40ff,
+ 0x40fe,UBOGON,0x4131,0x412e,0x4130,0x4132,UBOGON,UBOGON,0x412f,UBOGON,
+ 0x4195,0x4196,UBOGON,UBOGON,UBOGON,0x41c5,0x427a,UBOGON,0x4342,UBOGON,
+ 0x4354,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0b */
+ UBOGON,UBOGON,UBOGON,UBOGON,0x8009,UBOGON,0x439f,0x43a0,0x43a2,0x43e0,
+ UBOGON,0x43e1,UBOGON,UBOGON,0x43df,UBOGON,UBOGON,0x4462,0x4461,UBOGON,
+ UBOGON,0x44a7,UBOGON,UBOGON,UBOGON,0x82da,UBOGON,UBOGON,0x830a,0x4589,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x461d,UBOGON,UBOGON,
+ 0x472a,UBOGON,UBOGON,0x47d3,0x4842,0x4843,UBOGON,0x4891,UBOGON,UBOGON,
+ 0x4890,0x48bf,UBOGON,0x48bc,UBOGON,0x48c0,0x49c7,0x49c5,0x9655,UBOGON,
+ 0x9652,0x4e35,UBOGON,UBOGON,0x5034,0x5001,UBOGON,0x500a,0x3466,UBOGON,
+ UBOGON,UBOGON,0x34ad,UBOGON,UBOGON,UBOGON,UBOGON,0x5258,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x351d,UBOGON,0x3531,0x532b,UBOGON,0x354c,
+ UBOGON,UBOGON,0x3595,0x3591
+ },
+ { /* ku 0c */
+ 0x3594,UBOGON,UBOGON,UBOGON,0x358f,0x54ec,UBOGON,0x5515,0x54fe,UBOGON,
+ UBOGON,UBOGON,0x54e3,0x5516,0x3640,UBOGON,0x3641,UBOGON,0x57d3,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x5959,0x5a27,0x36d8,0x36d2,0x36da,0x5a28,0x5a10,
+ 0x36d7,0x5a0e,0x3742,0x3757,UBOGON,UBOGON,UBOGON,0x5baf,UBOGON,0x5bba,
+ 0x5bb1,UBOGON,UBOGON,0x3778,0x3782,0x3797,UBOGON,UBOGON,0x37c9,UBOGON,
+ 0x37c8,0x37d6,0x37cc,UBOGON,0x37d0,UBOGON,UBOGON,0x37ce,0x37c7,0x5cfc,
+ UBOGON,0x37cf,0x37cb,0x5cf2,0x5cfe,UBOGON,UBOGON,0x5df8,UBOGON,0x3847,
+ UBOGON,0x3848,UBOGON,UBOGON,0x3883,0x3885,0x3884,UBOGON,UBOGON,0x5f2c,
+ 0x38b8,0x38bc,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0d */
+ UBOGON,0x3931,0x3934,0x3936,0x6082,UBOGON,UBOGON,0x3923,UBOGON,UBOGON,
+ 0x393a,0x6091,0x608f,UBOGON,0x39b4,0x39b5,0x39ed,0x39ec,0x39d8,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x39f6,0x39e7,UBOGON,UBOGON,
+ 0x3a7c,0x3a7b,UBOGON,UBOGON,0x6547,0x654c,UBOGON,UBOGON,0x658a,UBOGON,
+ 0x3abe,UBOGON,0x3ae7,0x3ae5,0x3aee,0x67e1,0x684a,UBOGON,0x3b59,0x3b5e,
+ UBOGON,0x3b5a,0x683f,0x3b61,0x3b58,0x3b5b,0x67bd,UBOGON,0x3b5f,UBOGON,
+ 0x3c2a,0x3c2d,UBOGON,0x3c23,0x3c2b,0x3c2c,UBOGON,0x3c7e,UBOGON,0x3c93,
+ 0x3c99,UBOGON,UBOGON,0x3cb3,0x3ce7,0x3cea,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3db4,UBOGON,0x70c9,0x3e17,UBOGON,UBOGON,UBOGON,0x3e21,
+ UBOGON,0x3e38,0x3e37,0x3e74
+ },
+ { /* ku 0e */
+ 0x3e73,0x3e75,UBOGON,UBOGON,UBOGON,0x3e76,0x3e78,UBOGON,UBOGON,0x73ba,
+ 0x3f0c,0x3f20,0x3f1e,UBOGON,0x3f5b,UBOGON,0x3f5c,UBOGON,0x3f83,0x75c6,
+ 0x3f80,0x3f81,0x3f7e,0x3f88,0x3f85,0x3f89,0x3f7f,0x3f8e,UBOGON,UBOGON,
+ 0x3f84,0x75b7,0x768c,UBOGON,0x768d,UBOGON,0x3fee,0x3fed,0x3ffe,UBOGON,
+ 0x3fff,UBOGON,UBOGON,UBOGON,0x4023,UBOGON,0x7717,UBOGON,0x771c,0x401f,
+ UBOGON,UBOGON,0x7714,UBOGON,0x408f,0x4090,UBOGON,UBOGON,UBOGON,0x40a0,
+ UBOGON,0x40a6,0x409f,UBOGON,0x40a7,0x40a1,UBOGON,0x4102,0x4136,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x41c8,UBOGON,UBOGON,0x41d8,
+ 0x7b0c,0x41dd,0x41dc,UBOGON,0x41d7,UBOGON,0x41da,0x42ba,UBOGON,UBOGON,
+ UBOGON,0x42be,0x42c2,0x42bb
+ },
+ { /* ku 0f */
+ 0x42c0,UBOGON,UBOGON,UBOGON,0x7d23,UBOGON,UBOGON,UBOGON,0x4343,0x4355,
+ UBOGON,UBOGON,0x4357,0x4368,0x7f98,0x7f90,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x43a1,UBOGON,0x803a,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x43ea,UBOGON,0x43e7,UBOGON,UBOGON,UBOGON,UBOGON,0x43e8,0x43e9,UBOGON,
+ UBOGON,0x4454,UBOGON,UBOGON,UBOGON,UBOGON,0x8226,0x4465,UBOGON,UBOGON,
+ 0x448a,UBOGON,0x44b0,UBOGON,UBOGON,UBOGON,0x44bc,0x832e,UBOGON,0x8355,
+ 0x831a,0x44b8,0x833d,UBOGON,0x44b2,UBOGON,0x8330,0x44bd,UBOGON,UBOGON,
+ 0x458a,0x8651,0x45a1,UBOGON,0x45a2,UBOGON,0x8688,UBOGON,0x4615,UBOGON,
+ UBOGON,0x4620,0x4673,UBOGON,0x898e,0x898d,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x8a09,0x8a14,0x46b1
+ },
+ { /* ku 10 */
+ UBOGON,UBOGON,0x472b,UBOGON,0x4745,UBOGON,0x4797,0x4798,UBOGON,0x47d5,
+ UBOGON,UBOGON,0x4893,0x4896,0x9007,0x4894,UBOGON,UBOGON,0x48c7,0x48c5,
+ UBOGON,UBOGON,UBOGON,0x48c4,UBOGON,0x9579,0x9584,0x49ce,0x49ca,0x49cc,
+ 0x9657,0x49c9,0x96ba,UBOGON,UBOGON,UBOGON,0x346e,UBOGON,0x5067,UBOGON,
+ UBOGON,0x3471,UBOGON,0x34bb,0x34d3,UBOGON,UBOGON,UBOGON,UBOGON,0x34f3,
+ 0x34ed,0x34f5,UBOGON,UBOGON,0x34f1,0x34f2,0x34f6,0x3520,UBOGON,0x3528,
+ UBOGON,0x5318,0x532c,0x5359,UBOGON,UBOGON,UBOGON,0x5368,0x537e,UBOGON,
+ UBOGON,0x53a1,UBOGON,0x35a1,0x555b,0x35aa,0x35a9,UBOGON,0x35b5,0x35a5,
+ 0x35a8,0x5542,0x35a7,0x5547,UBOGON,UBOGON,0x553d,UBOGON,UBOGON,0x5560,
+ 0x57eb,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 11 */
+ 0x364d,UBOGON,UBOGON,UBOGON,0x369c,0x595f,UBOGON,0x36ea,0x36e5,UBOGON,
+ UBOGON,0x5b6f,UBOGON,0x375e,UBOGON,UBOGON,UBOGON,0x3786,0x3784,UBOGON,
+ 0x5c5a,UBOGON,0x37d9,UBOGON,0x37de,UBOGON,UBOGON,UBOGON,0x37db,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3831,UBOGON,UBOGON,0x384b,UBOGON,0x3849,0x384a,
+ 0x384c,UBOGON,UBOGON,0x388a,UBOGON,0x3889,0x388b,UBOGON,0x38bb,0x5fa2,
+ 0x5f9d,0x38e4,UBOGON,UBOGON,0x5fa3,UBOGON,UBOGON,0x393b,0x392e,0x393e,
+ 0x3946,0x3953,UBOGON,0x3944,UBOGON,0x393f,0x3942,0x394f,UBOGON,0x3952,
+ 0x394a,0x60c2,UBOGON,0x395a,0x60a5,0x3949,UBOGON,0x621c,UBOGON,0x621d,
+ 0x3a03,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6395,0x639a,0x3a01,0x3a06,
+ 0x39fb,0x39f9,UBOGON,0x3a05
+ },
+ { /* ku 12 */
+ 0x39fa,UBOGON,0x63a6,UBOGON,0x39fe,UBOGON,0x3a7d,UBOGON,UBOGON,UBOGON,
+ 0x6550,UBOGON,UBOGON,UBOGON,0x6552,UBOGON,UBOGON,UBOGON,UBOGON,0x65c8,
+ UBOGON,0x3af0,0x3af2,UBOGON,0x6658,0x3af1,0x3ae6,UBOGON,UBOGON,UBOGON,
+ 0x6888,UBOGON,0x3b6f,0x3b6d,0x3b69,UBOGON,UBOGON,0x3b6e,UBOGON,0x3c2f,
+ 0x3c30,0x3c63,UBOGON,UBOGON,UBOGON,0x6bb8,0x3c80,0x6bb9,0x3c9a,0x3c94,
+ 0x3c96,0x3c95,0x3c97,UBOGON,0x3cf4,0x3cfe,UBOGON,0x3d01,UBOGON,0x3d02,
+ UBOGON,0x3cf9,UBOGON,UBOGON,UBOGON,UBOGON,0x3cf6,0x3cf7,UBOGON,UBOGON,
+ UBOGON,0x3cff,UBOGON,UBOGON,UBOGON,UBOGON,0x6e0b,UBOGON,UBOGON,0x3dbf,
+ 0x3dbc,0x7105,UBOGON,UBOGON,UBOGON,0x3dbe,0x3dc0,UBOGON,0x3e3b,0x3e39,
+ UBOGON,UBOGON,UBOGON,0x3e3c
+ },
+ { /* ku 13 */
+ UBOGON,0x7314,0x7304,UBOGON,0x3e7d,UBOGON,0x3e7f,0x3e7a,0x3e7c,0x7305,
+ 0x3e7e,0x7315,0x730d,0x3e80,0x3ebf,0x3ec3,UBOGON,UBOGON,0x3ecc,0x3f0e,
+ 0x3f0d,UBOGON,0x3f26,0x3f24,0x3f25,0x3f23,0x3f21,0x3f29,UBOGON,UBOGON,
+ 0x3f8f,0x3f8d,UBOGON,0x3f8b,0x3f92,UBOGON,0x3f90,UBOGON,0x3fef,0x3ff0,
+ UBOGON,UBOGON,0x4001,UBOGON,0x402e,0x402d,0x772e,0x4028,0x4029,0x402c,
+ UBOGON,UBOGON,UBOGON,0x7741,0x4088,UBOGON,0x4092,0x4091,0x77ea,UBOGON,
+ 0x7844,0x40a9,0x40ac,0x40ae,0x40aa,0x4106,0x4105,0x414a,0x413e,0x413c,
+ 0x413b,UBOGON,0x4142,0x4141,0x4143,UBOGON,0x4145,UBOGON,0x419a,0x419b,
+ 0x419f,0x419e,UBOGON,UBOGON,UBOGON,0x41de,0x41e2,0x41e6,UBOGON,0x7b29,
+ 0x41e3,0x7b27,0x41df,UBOGON
+ },
+ { /* ku 14 */
+ UBOGON,0x7c9d,UBOGON,UBOGON,0x427e,0x42c9,0x42cc,UBOGON,0x42d1,UBOGON,
+ 0x42d0,UBOGON,0x42cf,0x42c8,UBOGON,0x42cd,UBOGON,UBOGON,0x4349,0x4347,
+ 0x4358,0x436b,0x436c,0x436a,UBOGON,0x4380,0x4382,0x4384,0x7fc8,0x4383,
+ UBOGON,UBOGON,0x43b6,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8126,0x43f1,
+ UBOGON,0x43f6,0x43f3,0x43f0,0x811c,UBOGON,UBOGON,0x8128,0x43f5,0x43f4,
+ 0x43f7,UBOGON,UBOGON,UBOGON,UBOGON,0x4455,UBOGON,UBOGON,UBOGON,0x448b,
+ 0x44cb,0x44c2,UBOGON,UBOGON,UBOGON,0x44ca,0x44cc,UBOGON,0x44c7,0x44c9,
+ 0x8370,UBOGON,0x44c6,UBOGON,UBOGON,0x44c3,0x8382,UBOGON,0x83ac,UBOGON,
+ 0x44c4,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x45a9,0x86ad,
+ 0x45a8,0x45a6,UBOGON,UBOGON
+ },
+ { /* ku 15 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x86ca,0x8851,UBOGON,UBOGON,0x4622,
+ UBOGON,0x4626,0x4624,0x4625,0x889d,0x462a,0x4674,UBOGON,0x4679,0x8990,
+ 0x467a,UBOGON,UBOGON,0x89d8,0x89d7,0x4697,UBOGON,UBOGON,0x8a2e,UBOGON,
+ 0x46bc,UBOGON,0x46b3,UBOGON,0x46bf,0x46b7,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4734,0x4746,0x4748,0x8c59,UBOGON,UBOGON,0x4756,0x4767,UBOGON,
+ UBOGON,0x4768,0x4799,0x479a,UBOGON,UBOGON,0x47d8,UBOGON,0x47db,0x47dc,
+ 0x47dd,0x47d7,UBOGON,UBOGON,0x4849,0x484a,0x8eda,UBOGON,UBOGON,0x9033,
+ UBOGON,0x9018,0x489a,UBOGON,0x48cd,0x48ca,UBOGON,0x48cb,UBOGON,0x48cf,
+ UBOGON,UBOGON,0x48cc,0x48ea,0x48ed,UBOGON,UBOGON,0x48e9,UBOGON,0x491a,
+ 0x91ef,0x498d,0x49d2,UBOGON
+ },
+ { /* ku 16 */
+ UBOGON,UBOGON,UBOGON,0x49f2,UBOGON,UBOGON,UBOGON,0x4a3d,UBOGON,0x4a3e,
+ 0x4af8,0x4b23,0x9ad9,0x4eb4,UBOGON,0x50a0,0x5090,0x3475,0x5086,0x5084,
+ UBOGON,0x508a,0x3476,0x3473,0x509f,0x50a1,UBOGON,0x5093,0x34bd,UBOGON,
+ 0x51d5,UBOGON,UBOGON,0x34f9,UBOGON,UBOGON,0x34fa,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3521,UBOGON,0x3529,UBOGON,UBOGON,0x3538,0x354e,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x35c8,0x35bc,UBOGON,UBOGON,0x5590,
+ UBOGON,0x35bb,0x35c2,0x35c0,UBOGON,UBOGON,0x35ca,UBOGON,0x35c9,UBOGON,
+ 0x35b8,0x5710,0x5817,UBOGON,0x364e,UBOGON,0x5844,0x3650,0x582b,UBOGON,
+ 0x5845,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x368a,UBOGON,0x5965,UBOGON,
+ UBOGON,UBOGON,0x36fc,0x36f9
+ },
+ { /* ku 17 */
+ UBOGON,0x3763,UBOGON,0x5bcf,UBOGON,UBOGON,UBOGON,0x3787,0x3788,UBOGON,
+ 0x379a,UBOGON,UBOGON,UBOGON,0x5d56,UBOGON,UBOGON,0x37e9,UBOGON,0x37ea,
+ 0x5d54,0x3850,UBOGON,UBOGON,0x3856,0x3852,0x384f,0x3854,0x3851,UBOGON,
+ UBOGON,0x388e,0x388f,UBOGON,UBOGON,UBOGON,0x5f3d,UBOGON,UBOGON,0x38ed,
+ 0x38eb,0x5fa4,UBOGON,UBOGON,UBOGON,0x3962,UBOGON,0x395d,UBOGON,UBOGON,
+ UBOGON,0x3961,0x3965,0x395c,UBOGON,UBOGON,0x395f,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x63ec,0x3a16,0x3a0a,UBOGON,0x3a0e,0x3a12,UBOGON,
+ UBOGON,0x3a11,UBOGON,UBOGON,0x3a10,UBOGON,UBOGON,0x3a09,0x63fa,0x3a15,
+ 0x63d4,UBOGON,UBOGON,UBOGON,0x3a91,0x3a95,0x3a93,0x3a92,0x3a8f,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3af6
+ },
+ { /* ku 18 */
+ UBOGON,0x3afb,UBOGON,0x6675,0x3af9,UBOGON,UBOGON,UBOGON,0x671c,0x3b7d,
+ UBOGON,0x3b7a,0x3b7f,UBOGON,0x3b78,UBOGON,UBOGON,0x68d9,UBOGON,0x3b70,
+ 0x3b82,UBOGON,0x3b84,UBOGON,0x3c33,UBOGON,0x3c32,0x3c36,UBOGON,UBOGON,
+ 0x3c56,UBOGON,UBOGON,0x3c67,UBOGON,0x3c65,0x3c64,0x3c66,UBOGON,UBOGON,
+ 0x3c81,0x3c82,0x3c83,0x3c9e,UBOGON,0x6bf1,0x3c9d,UBOGON,0x3d0f,0x3d12,
+ UBOGON,UBOGON,UBOGON,0x3d10,0x3d18,UBOGON,0x3d14,0x3d19,0x6e37,UBOGON,
+ UBOGON,0x6e7d,0x6e86,0x3dc8,0x3dc4,0x3dc6,UBOGON,0x3dc7,0x3dc3,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3e19,0x3e1c,UBOGON,UBOGON,UBOGON,0x3e41,
+ UBOGON,0x3e42,0x3e43,UBOGON,UBOGON,UBOGON,0x3e82,UBOGON,0x3e81,0x3e94,
+ 0x3e84,UBOGON,0x3ed2,0x3f0f
+ },
+ { /* ku 19 */
+ 0x3f22,UBOGON,0x3f27,0x3f2a,0x74fa,0x3f28,UBOGON,0x3f60,UBOGON,UBOGON,
+ UBOGON,0x7572,UBOGON,UBOGON,0x3f9b,0x3f9c,UBOGON,0x3f93,0x3f94,0x75dc,
+ 0x3fa0,0x3f99,UBOGON,0x3fa1,UBOGON,0x3ff1,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4036,UBOGON,0x4037,0x403f,0x403c,UBOGON,0x4034,0x4039,0x403b,0x4035,
+ 0x4030,0x4032,0x4038,0x403e,0x403a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x40b6,0x7867,UBOGON,UBOGON,UBOGON,0x40b3,0x4109,0x7977,UBOGON,UBOGON,
+ UBOGON,0x414c,UBOGON,UBOGON,0x4153,0x414d,0x4151,0x414f,0x7a9b,UBOGON,
+ 0x41a2,UBOGON,UBOGON,0x41cd,UBOGON,UBOGON,0x41e7,UBOGON,0x41f0,UBOGON,
+ 0x41e9,0x41ec,UBOGON,0x41e8,0x41ee,0x4202,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4282,0x4283,0x4286,UBOGON
+ },
+ { /* ku 1a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7d2a,0x7d65,0x434a,UBOGON,0x435a,
+ 0x7f64,UBOGON,0x436e,UBOGON,UBOGON,0x4370,0x436f,UBOGON,0x438a,0x4387,
+ 0x4388,UBOGON,UBOGON,0x8020,0x43b7,0x43fd,0x8120,UBOGON,0x4405,0x813c,
+ 0x4408,0x4403,0x4402,0x4404,0x3b39,0x4409,0x43ff,UBOGON,0x813f,UBOGON,
+ 0x43fc,0x4401,0x440a,0x81f0,0x81f5,0x446b,0x446c,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x44de,UBOGON,UBOGON,0x44db,UBOGON,0x44dd,0x44e3,
+ UBOGON,0x44e0,0x44d9,0x44d8,0x44e4,UBOGON,UBOGON,0x44da,0x44ef,UBOGON,
+ 0x8415,0x83be,UBOGON,UBOGON,UBOGON,0x44d7,0x45b3,0x45bb,0x86e5,0x45b2,
+ 0x86d2,0x45ad,UBOGON,0x45af,UBOGON,0x86e0,UBOGON,0x4616,0x4628,0x4623,
+ 0x88b3,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1b */
+ 0x4675,0x467e,0x467c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x46ce,0x46cd,
+ 0x46cf,0x8a53,UBOGON,UBOGON,0x8a37,0x8a47,0x8a5c,UBOGON,0x46c4,0x46cc,
+ 0x46c8,0x46c7,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x475a,
+ 0x476a,UBOGON,0x476b,0x476d,0x476f,UBOGON,0x479e,UBOGON,0x47a4,0x47a3,
+ 0x47e4,0x47e8,0x47e9,0x47e0,0x47e3,UBOGON,0x47ea,0x47e1,0x47ed,0x4834,
+ 0x4835,0x4851,0x8ef0,UBOGON,0x489d,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x48d0,UBOGON,0x48ee,UBOGON,UBOGON,UBOGON,0x48f2,UBOGON,0x921d,
+ 0x4988,UBOGON,UBOGON,UBOGON,0x498f,UBOGON,0x49d8,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4a3f,UBOGON,0x4a52,0x976b,UBOGON,0x4a50,UBOGON,
+ 0x4ab1,UBOGON,UBOGON,0x4af9
+ },
+ { /* ku 1c */
+ UBOGON,UBOGON,UBOGON,0x4b26,0x4b28,UBOGON,UBOGON,0x3480,0x50c0,0x3481,
+ UBOGON,0x347e,0x347f,UBOGON,UBOGON,UBOGON,UBOGON,0x34be,UBOGON,0x34d6,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x52e5,UBOGON,0x3534,
+ UBOGON,0x53af,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x35d5,0x35d8,UBOGON,
+ UBOGON,0x35d4,0x55d8,0x35d9,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5711,
+ 0x5867,UBOGON,UBOGON,0x365d,0x5843,0x365e,0x3659,UBOGON,0x365a,0x36a0,
+ UBOGON,UBOGON,0x3705,UBOGON,0x3707,UBOGON,0x370e,0x370c,UBOGON,UBOGON,
+ 0x3745,UBOGON,UBOGON,0x3764,UBOGON,0x3765,UBOGON,0x5bdd,0x3766,UBOGON,
+ 0x3789,0x37ec,0x37f1,0x5d70,0x5d6a,0x37f0,0x37f8,0x5d74,0x5d5f,UBOGON,
+ 0x5d61,0x5d73,UBOGON,0x37f2
+ },
+ { /* ku 1d */
+ 0x37f4,UBOGON,0x3858,UBOGON,UBOGON,0x385a,0x3859,0x3857,0x385b,0x5e50,
+ UBOGON,UBOGON,0x38a6,0x38c2,0x38c1,0x5f3f,UBOGON,UBOGON,0x38ef,0x5fb0,
+ UBOGON,UBOGON,0x3968,0x6135,0x612d,0x3973,0x396e,0x3974,0x6102,0x3966,
+ UBOGON,UBOGON,UBOGON,0x39b9,0x6226,UBOGON,0x3a0c,UBOGON,UBOGON,0x3a20,
+ UBOGON,0x3a1d,UBOGON,0x3a1c,UBOGON,0x3a21,0x3a1a,0x3a19,UBOGON,UBOGON,
+ UBOGON,0x3a7e,UBOGON,UBOGON,UBOGON,UBOGON,0x3a9d,UBOGON,0x3a9e,UBOGON,
+ 0x656e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x65b1,0x65d4,0x3acd,UBOGON,
+ 0x3b0b,0x3b0a,0x6685,UBOGON,0x3b8f,0x6972,0x3b95,0x3b90,0x3b91,UBOGON,
+ 0x693a,0x3bb9,UBOGON,UBOGON,0x3b97,0x3b9e,UBOGON,0x3b8b,UBOGON,UBOGON,
+ 0x3c3b,0x3c3a,0x3c3c,0x3c3d
+ },
+ { /* ku 1e */
+ 0x3c39,0x3c3e,0x3c6b,0x3c6c,UBOGON,UBOGON,0x3ca2,0x3ca1,0x3c9f,UBOGON,
+ UBOGON,UBOGON,0x3d2d,UBOGON,0x3d36,0x3d2b,UBOGON,0x3d37,UBOGON,UBOGON,
+ UBOGON,0x6ead,0x3d25,0x3d2f,0x3d2c,UBOGON,UBOGON,0x3d32,UBOGON,UBOGON,
+ 0x6e95,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3dd5,UBOGON,0x3dd4,
+ 0x3dd6,UBOGON,0x3dd1,0x7243,UBOGON,0x3e46,0x728f,UBOGON,UBOGON,0x3e8c,
+ 0x3e8a,0x3e88,UBOGON,UBOGON,UBOGON,UBOGON,0x3edd,UBOGON,UBOGON,UBOGON,
+ 0x3f2d,UBOGON,UBOGON,0x3f2e,0x3f2c,0x3f2b,0x3f30,UBOGON,0x3f4e,UBOGON,
+ UBOGON,0x3f64,0x3f61,UBOGON,0x7575,UBOGON,0x3f70,0x3fa6,0x3fa4,UBOGON,
+ UBOGON,UBOGON,0x3fa8,0x3fa2,UBOGON,0x3fa7,0x75ec,0x3fa5,UBOGON,0x3fa9,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1f */
+ UBOGON,0x403d,UBOGON,UBOGON,0x4044,0x4045,0x4046,UBOGON,0x7757,UBOGON,
+ 0x4047,0x4048,0x4042,UBOGON,UBOGON,0x4041,UBOGON,UBOGON,0x4094,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x40c0,0x40b8,0x40c1,0x40c2,0x40bb,0x40bd,0x40bf,
+ 0x40b9,0x40b7,UBOGON,0x40c7,UBOGON,0x410c,0x410b,0x797b,0x4110,UBOGON,
+ 0x415d,0x7a21,0x415a,0x4158,0x4156,UBOGON,0x4154,0x7a16,UBOGON,0x41a8,
+ 0x41a7,0x41cf,0x41d0,UBOGON,0x7ae8,UBOGON,UBOGON,0x41d1,0x41eb,UBOGON,
+ 0x41fb,0x7b6a,UBOGON,0x41fd,0x41f8,0x41f7,0x4200,UBOGON,UBOGON,0x41f6,
+ 0x7b5f,UBOGON,UBOGON,0x42df,UBOGON,UBOGON,UBOGON,0x42e2,0x42e4,UBOGON,
+ 0x7d82,UBOGON,0x42e3,UBOGON,0x4359,0x4371,0x438e,0x438c,UBOGON,0x43a4,
+ UBOGON,0x8055,0x4414,UBOGON
+ },
+ { /* ku 20 */
+ UBOGON,UBOGON,0x4411,UBOGON,0x441b,0x4412,0x440e,0x4415,0x8168,0x4410,
+ UBOGON,0x4417,0x8246,0x8243,0x4470,0x44ed,UBOGON,0x44ee,UBOGON,UBOGON,
+ UBOGON,0x8481,UBOGON,UBOGON,UBOGON,0x44f4,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x847c,UBOGON,UBOGON,0x846a,UBOGON,0x8488,0x44f2,0x44f8,
+ 0x44f3,UBOGON,UBOGON,0x44fa,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8710,
+ UBOGON,UBOGON,0x871f,0x45b6,0x45b7,UBOGON,0x870f,UBOGON,0x45ba,UBOGON,
+ 0x45bc,UBOGON,UBOGON,0x463b,0x88d3,0x462f,UBOGON,UBOGON,UBOGON,0x4637,
+ 0x4699,UBOGON,UBOGON,UBOGON,UBOGON,0x46d9,0x46d8,0x46d7,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4736,UBOGON,UBOGON,UBOGON,0x8c87,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x8cc6
+ },
+ { /* ku 21 */
+ 0x4770,UBOGON,UBOGON,UBOGON,UBOGON,0x47a5,0x47a6,0x47a9,0x47ee,0x4854,
+ UBOGON,0x4857,UBOGON,UBOGON,0x48a1,UBOGON,UBOGON,UBOGON,UBOGON,0x48d3,
+ UBOGON,0x48d4,UBOGON,0x48d7,0x90cc,0x916d,0x9170,0x48f7,0x48f6,0x48f9,
+ 0x48f8,0x9258,0x9242,0x9268,0x9269,UBOGON,UBOGON,0x9243,UBOGON,0x9247,
+ 0x498a,UBOGON,UBOGON,UBOGON,UBOGON,0x4994,UBOGON,0x4993,UBOGON,UBOGON,
+ 0x959d,0x49dd,0x49dc,0x49f7,0x96cf,UBOGON,UBOGON,0x4a42,UBOGON,UBOGON,
+ 0x4a54,UBOGON,0x4a55,UBOGON,0x4a8f,UBOGON,0x97f4,0x4ab4,0x4ab3,UBOGON,
+ UBOGON,0x9809,UBOGON,UBOGON,UBOGON,UBOGON,0x4afb,0x4afd,UBOGON,UBOGON,
+ 0x98ab,0x4afc,UBOGON,0x4b2c,0x4b2f,UBOGON,0x4b2b,UBOGON,0x4b33,0x4b34,
+ 0x98fb,UBOGON,0x9aac,0x9aae
+ },
+ { /* ku 22 */
+ 0x9aaa,0x4be8,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x9b5c,UBOGON,
+ UBOGON,0x4d5d,0x50d2,0x3485,0x3488,UBOGON,UBOGON,0x348e,0x3484,UBOGON,
+ 0x50df,UBOGON,0x3483,UBOGON,UBOGON,UBOGON,UBOGON,0x3502,UBOGON,0x3506,
+ 0x3505,UBOGON,0x34fe,0x3501,0x3500,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x35e7,0x5619,UBOGON,UBOGON,UBOGON,0x35e6,UBOGON,0x35ed,0x35e2,
+ 0x35eb,UBOGON,0x35e8,0x35ec,0x560a,0x3624,0x589a,UBOGON,0x3662,UBOGON,
+ 0x3661,0x3660,0x3664,0x368b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3719,
+ 0x3716,0x3718,0x3722,UBOGON,0x371d,0x3717,0x371e,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3769,0x376a,UBOGON,0x376c,0x377a,0x378a,UBOGON,
+ UBOGON,UBOGON,0x379c,UBOGON
+ },
+ { /* ku 23 */
+ 0x37fd,0x37f9,UBOGON,0x37ff,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x37fc,
+ UBOGON,0x5d85,0x37fb,0x3802,0x385f,0x5e56,0x385e,0x385d,0x385c,UBOGON,
+ 0x5e51,0x3892,UBOGON,UBOGON,0x3894,0x3895,0x38d1,UBOGON,0x38f1,UBOGON,
+ 0x5fb1,UBOGON,UBOGON,0x3977,0x396f,UBOGON,UBOGON,0x3987,0x397d,0x397c,
+ 0x397e,0x3985,0x398b,0x3986,0x3980,UBOGON,UBOGON,0x3978,UBOGON,UBOGON,
+ UBOGON,0x39ba,UBOGON,0x3a33,UBOGON,0x3a2d,UBOGON,UBOGON,UBOGON,0x3a37,
+ 0x645a,0x6463,UBOGON,UBOGON,0x3a2e,UBOGON,UBOGON,0x3a3d,UBOGON,0x3aa0,
+ UBOGON,UBOGON,0x3aa3,UBOGON,0x669b,UBOGON,0x66a3,0x3b0e,0x669e,UBOGON,
+ 0x3bb6,UBOGON,0x3bab,0x3bad,0x3ba6,UBOGON,0x69b8,0x3baa,0x69ba,0x3bb1,
+ UBOGON,0x3ba8,0x3baf,0x3bb0
+ },
+ { /* ku 24 */
+ 0x3ba7,0x3bb2,0x3b9d,0x3ba5,0x3bb5,UBOGON,0x69c7,0x69d7,UBOGON,0x3c41,
+ UBOGON,UBOGON,0x6b70,UBOGON,UBOGON,0x3c72,0x6b9d,0x3c6f,0x3c71,UBOGON,
+ 0x3c85,UBOGON,UBOGON,0x3ca4,0x3ca5,0x3ca6,UBOGON,0x3ca8,UBOGON,UBOGON,
+ 0x3ca3,UBOGON,UBOGON,UBOGON,0x6f16,0x6f24,UBOGON,0x3d43,UBOGON,UBOGON,
+ 0x3d3d,0x3d45,UBOGON,UBOGON,0x3d44,UBOGON,UBOGON,UBOGON,UBOGON,0x6f45,
+ UBOGON,UBOGON,UBOGON,0x3de3,0x7179,UBOGON,0x3ddf,0x3de4,0x717a,0x3de5,
+ UBOGON,0x7254,0x3e22,0x3e4a,UBOGON,0x3e49,0x3e44,0x3e4b,0x3e87,0x3e89,
+ 0x3e92,0x3e91,0x3e90,0x3e8e,UBOGON,UBOGON,UBOGON,UBOGON,0x3f12,0x3f10,
+ 0x3f11,UBOGON,0x3f32,0x3f34,0x3f37,0x3f33,0x3f36,0x3f35,0x3f65,UBOGON,
+ 0x757c,0x757b,UBOGON,0x7612
+ },
+ { /* ku 25 */
+ 0x3fb0,UBOGON,0x3faf,0x3faa,UBOGON,UBOGON,UBOGON,0x3fab,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3ff3,UBOGON,0x3ff4,UBOGON,0x76b6,
+ 0x76e0,0x4008,0x404e,0x4055,0x404b,UBOGON,UBOGON,0x404d,0x7773,UBOGON,
+ 0x4052,0x7772,0x404c,0x7770,0x4050,0x4053,UBOGON,0x4051,UBOGON,UBOGON,
+ UBOGON,0x4089,UBOGON,0x4095,0x40cc,0x40c8,0x40ce,UBOGON,0x40ca,UBOGON,
+ 0x789d,UBOGON,0x40cd,UBOGON,0x415c,0x4167,0x4169,0x4165,0x4162,UBOGON,
+ 0x7a27,0x7a35,UBOGON,0x41aa,UBOGON,UBOGON,0x41d2,0x7ba2,0x4203,0x420c,
+ UBOGON,0x4209,0x4206,0x4205,0x7b89,UBOGON,0x420b,0x4208,UBOGON,0x7ba5,
+ UBOGON,0x428e,UBOGON,0x7cb6,0x42e8,UBOGON,UBOGON,UBOGON,0x42ea,UBOGON,
+ 0x7da5,0x7dc3,UBOGON,0x42e9
+ },
+ { /* ku 26 */
+ 0x42eb,UBOGON,0x42f0,UBOGON,0x434b,0x7fab,0x4373,0x4375,0x4392,0x4391,
+ 0x4393,0x8025,0x43a7,0x43a6,0x43a8,0x43aa,UBOGON,0x43a9,0x8059,0x43bb,
+ 0x43bc,0x43ba,0x43bd,0x4427,0x8185,UBOGON,0x4424,0x441e,0x441f,0x441d,
+ 0x4420,0x4423,0x4429,0x4422,UBOGON,UBOGON,0x441c,0x818e,0x4428,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4471,0x4473,0x4472,UBOGON,
+ 0x4502,UBOGON,UBOGON,UBOGON,0x44fe,0x84be,UBOGON,UBOGON,UBOGON,0x4508,
+ UBOGON,UBOGON,UBOGON,0x4507,0x4504,UBOGON,UBOGON,0x4500,0x44fc,UBOGON,
+ 0x4544,UBOGON,0x44f1,UBOGON,UBOGON,0x84a6,0x4506,UBOGON,UBOGON,0x45c6,
+ 0x45c3,0x45c1,0x45c2,UBOGON,0x45c4,0x45c7,UBOGON,0x45bf,0x45d2,UBOGON,
+ 0x45ca,UBOGON,UBOGON,0x872f
+ },
+ { /* ku 27 */
+ UBOGON,UBOGON,0x4613,UBOGON,0x4630,0x463e,0x4639,UBOGON,UBOGON,0x463c,
+ 0x463f,UBOGON,0x4634,0x463d,UBOGON,UBOGON,0x4638,UBOGON,UBOGON,UBOGON,
+ 0x89a0,0x4682,UBOGON,0x4683,UBOGON,0x469b,UBOGON,0x46e0,0x46dd,UBOGON,
+ UBOGON,0x46de,UBOGON,0x46e3,0x46e5,UBOGON,0x8a97,0x46e2,UBOGON,UBOGON,
+ UBOGON,0x46df,UBOGON,0x472e,UBOGON,0x4737,0x4738,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x8c8b,UBOGON,0x3562,UBOGON,0x4794,0x4793,0x47ab,0x47ad,UBOGON,
+ UBOGON,0x47f5,0x47f7,UBOGON,0x47f6,0x47f8,UBOGON,0x47fb,0x47f9,0x4858,
+ 0x485a,UBOGON,0x4859,0x8f0f,0x4885,0x48a4,0x48d8,0x48d9,UBOGON,0x48dd,
+ 0x48c8,UBOGON,0x48fa,0x48fb,0x9275,0x4927,0x929f,0x492a,0x4925,UBOGON,
+ 0x4928,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 28 */
+ UBOGON,0x95a6,0x4995,0x969a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4a0f,0x4a11,UBOGON,UBOGON,0x4a10,UBOGON,0x4a15,0x4a13,
+ 0x9757,UBOGON,0x4a47,0x4a46,UBOGON,0x4a59,0x4a5b,UBOGON,0x4a5e,UBOGON,
+ UBOGON,0x4a5a,0x4a91,0x4a92,0x4a90,0x4a93,UBOGON,0x97f7,0x4abe,UBOGON,
+ UBOGON,UBOGON,0x4abc,0x4abb,0x4ab7,0x4ab9,UBOGON,0x4b01,0x4afe,UBOGON,
+ UBOGON,0x4b02,UBOGON,0x4aff,0x98b0,UBOGON,0x4b00,UBOGON,0x4b37,0x4b3a,
+ 0x4b6f,0x4b77,0x4b79,0x99c6,UBOGON,0x4bc8,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4bf2,UBOGON,0x4bf1,0x4bf0,0x9b62,UBOGON,0x4c34,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4d2c,0x4d2d,UBOGON,UBOGON,UBOGON,UBOGON,0x50fa,UBOGON,
+ 0x3491,UBOGON,0x3494,UBOGON
+ },
+ { /* ku 29 */
+ UBOGON,0x34c4,0x350a,UBOGON,0x5285,UBOGON,0x3552,UBOGON,0x3559,0x366f,
+ UBOGON,0x35f2,0x35f4,0x5643,UBOGON,0x35f1,0x563c,UBOGON,0x366a,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3724,UBOGON,0x3723,UBOGON,UBOGON,
+ UBOGON,0x3727,UBOGON,UBOGON,0x376d,0x5bed,0x376e,0x376f,UBOGON,UBOGON,
+ 0x5c35,0x379f,0x380a,0x3806,0x380e,UBOGON,0x380d,0x3805,UBOGON,UBOGON,
+ 0x380b,0x3810,0x382e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3896,0x3897,
+ 0x38c4,0x5f47,0x38c5,UBOGON,0x38d2,UBOGON,UBOGON,UBOGON,0x3981,UBOGON,
+ 0x398e,0x3990,0x398f,UBOGON,0x3991,0x3995,0x3993,UBOGON,0x616d,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3a3b,0x3a48,UBOGON,UBOGON,0x3a46,0x3a47,
+ UBOGON,UBOGON,UBOGON,0x3a4c
+ },
+ { /* ku 2a */
+ UBOGON,0x3a4a,0x3a50,0x3a43,UBOGON,UBOGON,UBOGON,0x3a49,0x3aa6,0x3aa5,
+ 0x3aa4,UBOGON,UBOGON,0x3ab9,UBOGON,0x3ace,0x3acf,0x3b13,UBOGON,UBOGON,
+ UBOGON,0x3bc6,0x3bc5,0x3bca,0x3bd9,0x3bc1,UBOGON,UBOGON,0x69f5,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3bcb,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6a03,
+ UBOGON,UBOGON,0x6a65,0x3c42,UBOGON,0x6b75,0x3c74,0x3c73,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x6f8a,0x6f56,0x3d52,UBOGON,UBOGON,UBOGON,
+ 0x3d50,UBOGON,0x3d4b,UBOGON,0x3d4c,0x3d6d,UBOGON,0x6f98,UBOGON,UBOGON,
+ UBOGON,0x3d4f,0x6f68,0x3df0,UBOGON,0x7234,0x7245,0x3e4d,0x3e4c,UBOGON,
+ 0x3e4f,0x3e4e,0x3e50,UBOGON,0x735c,0x3e96,0x7356,UBOGON,UBOGON,0x3e97,
+ 0x3e95,0x3e98,UBOGON,UBOGON
+ },
+ { /* ku 2b */
+ 0x3eec,0x3eeb,0x3f13,0x3f14,0x3f38,0x3f3a,0x3f39,UBOGON,0x3f68,0x3f67,
+ UBOGON,UBOGON,UBOGON,0x3fbe,0x3fbc,UBOGON,UBOGON,UBOGON,0x3fbb,UBOGON,
+ 0x3fba,UBOGON,0x3fb9,0x3fb7,UBOGON,UBOGON,0x3fc1,UBOGON,0x3ff7,UBOGON,
+ 0x4060,UBOGON,UBOGON,0x4059,0x405c,0x405a,0x4058,UBOGON,0x405b,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x405d,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x40d4,0x40d3,0x78bf,0x40d2,0x78bd,UBOGON,0x40d7,0x40d1,
+ 0x78e4,0x40d5,UBOGON,UBOGON,0x416d,0x416f,0x7a34,0x4175,0x416c,0x4174,
+ UBOGON,0x4173,UBOGON,UBOGON,0x7a36,0x41ac,UBOGON,UBOGON,UBOGON,0x4210,
+ UBOGON,UBOGON,0x7bba,0x7bbc,0x420f,0x7bc8,0x4223,0x7bc3,0x421d,0x7bb6,
+ 0x420e,UBOGON,UBOGON,0x4215
+ },
+ { /* ku 2c */
+ 0x7bc2,0x4213,UBOGON,UBOGON,0x421b,0x7bc5,0x4222,0x4226,UBOGON,0x7bbd,
+ 0x7bb0,0x4221,0x421c,0x4217,UBOGON,0x421a,0x7bbb,UBOGON,UBOGON,UBOGON,
+ 0x4299,UBOGON,0x4297,UBOGON,UBOGON,0x42fd,UBOGON,0x42f6,0x42fe,0x42f5,
+ 0x42ff,0x42f7,UBOGON,UBOGON,UBOGON,0x4301,0x7e04,UBOGON,UBOGON,UBOGON,
+ 0x4377,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x43ab,UBOGON,UBOGON,0x43c0,
+ UBOGON,0x4431,0x442e,UBOGON,UBOGON,0x442c,UBOGON,UBOGON,0x4432,0x442f,
+ 0x442b,0x442d,0x4433,UBOGON,UBOGON,0x81f1,0x4457,0x445c,0x447b,UBOGON,
+ UBOGON,0x447a,UBOGON,0x8522,0x4513,0x451e,0x4517,0x4520,0x452a,0x4511,
+ 0x4515,0x450f,0x4518,0x8538,UBOGON,UBOGON,0x452c,0x8532,UBOGON,0x8510,
+ UBOGON,UBOGON,0x451c,UBOGON
+ },
+ { /* ku 2d */
+ 0x4529,UBOGON,UBOGON,0x4512,0x854f,0x4597,UBOGON,0x8772,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x45d4,0x45d0,0x877c,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x45d3,0x4614,UBOGON,0x4646,0x4645,UBOGON,0x4643,UBOGON,0x890d,
+ 0x4644,0x4648,UBOGON,0x4647,UBOGON,UBOGON,UBOGON,0x8908,0x4649,0x4685,
+ UBOGON,0x4684,UBOGON,0x469d,UBOGON,0x469e,0x46a0,UBOGON,0x469c,UBOGON,
+ 0x469f,UBOGON,0x46f7,0x46ea,UBOGON,0x46ef,0x46e9,0x46f3,0x46f0,0x46eb,
+ UBOGON,0x46ec,0x46f2,0x46f5,0x46ee,UBOGON,UBOGON,0x473a,0x474b,UBOGON,
+ 0x474a,0x474c,UBOGON,UBOGON,UBOGON,0x4779,0x477b,0x4778,UBOGON,UBOGON,
+ 0x47b5,UBOGON,0x47b4,0x47b7,0x8d9e,0x4809,0x47fe,0x4808,0x4807,UBOGON,
+ UBOGON,UBOGON,0x4806,0x4804
+ },
+ { /* ku 2e */
+ 0x4805,0x47ff,0x480b,UBOGON,UBOGON,0x483b,0x485d,0x485c,0x485f,0x485e,
+ 0x8f28,UBOGON,0x8f21,0x4883,UBOGON,UBOGON,0x48a7,0x9066,0x906c,UBOGON,
+ 0x48a8,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x90f6,0x48e0,0x48df,0x48fe,
+ 0x48fc,0x48ff,0x48fd,UBOGON,0x492c,0x92ec,0x92ba,0x92e3,0x92bd,0x499d,
+ UBOGON,0x95b4,UBOGON,0x4a40,UBOGON,UBOGON,0x4a5f,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x97d1,UBOGON,0x4ac0,0x9823,UBOGON,0x4ac1,
+ 0x4ac6,UBOGON,UBOGON,0x4b04,0x4b05,UBOGON,0x990b,0x4b3e,0x4b3d,0x4b40,
+ 0x4b3f,UBOGON,0x4b42,UBOGON,UBOGON,UBOGON,0x4b84,0x4b82,0x4b7f,0x4b85,
+ UBOGON,UBOGON,0x4bcc,0x9ab2,0x4bcb,0x4bcd,UBOGON,UBOGON,0x9adb,UBOGON,
+ 0x4bf5,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2f */
+ 0x9af0,UBOGON,UBOGON,0x4c20,0x4c21,UBOGON,UBOGON,0x4c37,0x4c3e,0x9b73,
+ 0x4c3d,0x9b6e,UBOGON,UBOGON,UBOGON,0x9b65,UBOGON,0x4c3c,UBOGON,0x4c38,
+ 0x9b6a,UBOGON,0x9b6d,UBOGON,0x4c3b,UBOGON,0x4cb0,UBOGON,UBOGON,UBOGON,
+ 0x4cad,0x4cb2,0x4cb8,0x9d0b,UBOGON,0x4caf,UBOGON,UBOGON,0x4d1a,0x9e76,
+ 0x4d20,0x4d21,0x4d30,0x9ea8,0x4d2f,UBOGON,UBOGON,UBOGON,UBOGON,0x4d5f,
+ 0x4d60,UBOGON,UBOGON,0x9f11,UBOGON,UBOGON,0x348a,0x5119,0x349c,UBOGON,
+ 0x349a,UBOGON,UBOGON,UBOGON,0x350c,0x350b,0x350d,0x5292,UBOGON,UBOGON,
+ 0x35fe,UBOGON,UBOGON,0x35ff,0x35fb,0x35fc,0x3609,UBOGON,0x3600,UBOGON,
+ 0x5675,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3671,UBOGON,UBOGON,
+ 0x596f,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 30 */
+ 0x372b,UBOGON,UBOGON,UBOGON,0x3814,0x3811,0x3812,UBOGON,0x3863,UBOGON,
+ 0x386e,0x389a,UBOGON,0x389b,UBOGON,UBOGON,0x38c8,UBOGON,UBOGON,0x38f6,
+ UBOGON,UBOGON,0x61a5,0x398c,0x3997,0x39a2,0x61a0,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3a54,UBOGON,UBOGON,UBOGON,
+ 0x3aa8,UBOGON,UBOGON,0x3aa9,UBOGON,0x65b4,0x65d8,0x66c2,0x3b18,0x3b17,
+ 0x3b1d,UBOGON,UBOGON,0x3b31,UBOGON,UBOGON,0x3bd8,0x3bd5,UBOGON,UBOGON,
+ UBOGON,0x3be1,UBOGON,0x3bd4,UBOGON,UBOGON,UBOGON,0x3be3,UBOGON,0x3c44,
+ 0x3c45,UBOGON,UBOGON,0x3c76,0x3c75,UBOGON,0x6ba8,0x3c88,0x3caa,UBOGON,
+ 0x3cab,0x3cac,UBOGON,0x3d57,0x6f83,0x3d60,0x3d5d,0x3d6b,UBOGON,0x3d63,
+ 0x3d67,UBOGON,0x3d5e,UBOGON
+ },
+ { /* ku 31 */
+ UBOGON,0x6fc5,0x71cd,0x3df9,0x3df3,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3df6,UBOGON,0x729c,0x3e51,0x3e53,0x3e52,UBOGON,0x3e9b,UBOGON,0x3e9c,
+ UBOGON,UBOGON,0x3ef7,0x7499,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3f3b,UBOGON,0x3f3e,0x3f3d,UBOGON,0x3f69,UBOGON,UBOGON,UBOGON,0x3fc3,
+ 0x3fc4,0x3fc7,UBOGON,UBOGON,0x7639,0x3fc6,0x762e,0x3fc8,UBOGON,UBOGON,
+ UBOGON,0x769f,0x76a0,0x3fe6,0x3ff8,UBOGON,UBOGON,0x4007,UBOGON,0x4064,
+ 0x4068,UBOGON,0x7794,0x4065,0x77ae,UBOGON,UBOGON,0x4069,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x40da,0x40e0,0x78e6,UBOGON,0x40de,
+ UBOGON,UBOGON,UBOGON,0x411c,0x411d,0x411a,UBOGON,0x417b,0x417a,0x417c,
+ UBOGON,0x4178,0x4177,UBOGON
+ },
+ { /* ku 32 */
+ UBOGON,0x41b1,UBOGON,0x41b2,0x41b0,UBOGON,0x7abc,UBOGON,0x4236,UBOGON,
+ 0x422e,UBOGON,0x7bd6,UBOGON,0x4234,UBOGON,UBOGON,UBOGON,UBOGON,0x422a,
+ UBOGON,0x4233,0x422d,0x422f,0x4231,0x422b,0x4232,UBOGON,0x4235,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7ccf,UBOGON,UBOGON,0x4308,UBOGON,
+ 0x4306,UBOGON,UBOGON,0x7e18,UBOGON,0x434d,0x4361,UBOGON,UBOGON,UBOGON,
+ 0x4378,UBOGON,0x4394,0x4395,UBOGON,UBOGON,UBOGON,0x43c1,0x43c3,0x806d,
+ UBOGON,0x4439,UBOGON,UBOGON,0x443a,0x443b,0x4435,0x4436,UBOGON,0x443c,
+ 0x8190,UBOGON,UBOGON,UBOGON,0x4458,0x447c,0x447d,0x448d,0x448c,UBOGON,
+ UBOGON,0x453b,0x453f,UBOGON,UBOGON,0x4532,0x452d,UBOGON,0x452f,0x4539,
+ 0x452e,0x453a,UBOGON,0x4536
+ },
+ { /* ku 33 */
+ 0x4531,0x453e,0x4538,0x8552,0x4534,UBOGON,0x4541,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4530,UBOGON,UBOGON,UBOGON,UBOGON,0x4543,UBOGON,0x8550,UBOGON,
+ UBOGON,0x4598,UBOGON,UBOGON,0x87a0,UBOGON,UBOGON,0x8786,0x45da,0x45d7,
+ UBOGON,UBOGON,0x8795,UBOGON,UBOGON,0x878c,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4618,0x8860,UBOGON,UBOGON,UBOGON,0x4652,0x8928,UBOGON,0x464e,
+ 0x8920,UBOGON,0x464f,0x4650,UBOGON,UBOGON,0x89a8,0x4686,0x4687,0x4689,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x46a2,0x46a3,UBOGON,0x46a1,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x46f9,0x46fd,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x473e,0x473c,UBOGON,0x474d,UBOGON,0x474e,UBOGON,0x4781,0x4783,
+ 0x4782,UBOGON,0x4780,0x4788
+ },
+ { /* ku 34 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x47b9,UBOGON,UBOGON,0x8e3a,UBOGON,
+ UBOGON,UBOGON,0x4811,0x480d,0x4810,0x4813,UBOGON,0x483c,0x4862,0x4863,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4903,0x4906,0x4902,0x4901,UBOGON,
+ UBOGON,0x4905,UBOGON,0x9194,0x9311,UBOGON,0x9337,0x4936,UBOGON,0x4935,
+ 0x9343,UBOGON,0x49a1,0x49a3,UBOGON,UBOGON,0x49a0,UBOGON,0x49ea,0x96a6,
+ 0x49e8,UBOGON,UBOGON,UBOGON,UBOGON,0x4a21,0x4a1b,UBOGON,UBOGON,0x4a49,
+ 0x4a48,UBOGON,0x9795,0x4a62,0x4a61,0x4a64,0x4a60,0x4a63,UBOGON,UBOGON,
+ 0x9796,0x4a66,0x4aac,0x4aab,UBOGON,UBOGON,0x4ac3,UBOGON,0x4ac4,0x9825,
+ UBOGON,UBOGON,0x4b08,0x4b09,0x4b0a,0x4b06,0x4b07,0x4b41,UBOGON,0x4b45,
+ UBOGON,0x4b43,0x4b44,0x9926
+ },
+ { /* ku 35 */
+ 0x9934,0x4b47,UBOGON,UBOGON,UBOGON,0x4b71,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4b8b,0x4bd4,0x4bd3,0x4bd1,0x9aba,0x4bd2,UBOGON,UBOGON,
+ 0x4bf7,0x4bf8,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4c22,UBOGON,0x4c45,0x4c41,0x9b81,0x4c40,0x9b8a,UBOGON,0x9b7f,0x4c42,
+ UBOGON,0x4cc1,0x4cc5,UBOGON,0x4cbb,0x4cb9,0x4cbd,0x4cc9,UBOGON,0x9d11,
+ UBOGON,0x4cbf,0x4cc7,UBOGON,0x4cc3,0x4d24,0x4d31,0x4d33,UBOGON,0x4d32,
+ 0x4d34,0x4d52,0x4d61,0x9ed9,0x4d7a,0x4d82,0x9f3c,UBOGON,0x5123,UBOGON,
+ UBOGON,UBOGON,0x512c,UBOGON,UBOGON,0x350f,0x5295,UBOGON,0x3523,0x3525,
+ UBOGON,0x3606,0x3608,0x5688,UBOGON,UBOGON,UBOGON,0x568b,UBOGON,UBOGON,
+ 0x367a,0x3677,UBOGON,UBOGON
+ },
+ { /* ku 36 */
+ UBOGON,0x372e,UBOGON,0x372f,UBOGON,UBOGON,0x381b,0x3813,UBOGON,UBOGON,
+ 0x3866,UBOGON,0x3865,UBOGON,0x38f7,UBOGON,UBOGON,0x61e1,0x61d7,UBOGON,
+ UBOGON,0x399c,UBOGON,UBOGON,0x3a53,UBOGON,UBOGON,UBOGON,UBOGON,0x3aba,
+ 0x65a3,0x3b22,0x66d3,UBOGON,UBOGON,UBOGON,0x6a8b,UBOGON,UBOGON,0x3beb,
+ 0x3bdd,UBOGON,0x3bef,UBOGON,UBOGON,0x3c47,0x3c46,UBOGON,0x3c78,0x6bac,
+ 0x3c89,UBOGON,UBOGON,0x3d68,0x3d76,0x3d74,0x3d79,UBOGON,0x3d7a,0x3d77,
+ UBOGON,0x3d71,UBOGON,0x3d72,UBOGON,0x3dff,UBOGON,0x3e05,UBOGON,UBOGON,
+ 0x3e54,UBOGON,UBOGON,0x3e9e,0x3e9f,0x7374,UBOGON,UBOGON,0x3efa,UBOGON,
+ 0x3f44,0x3f3f,0x3f40,UBOGON,0x3f42,UBOGON,UBOGON,0x3f51,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 37 */
+ 0x7640,0x3fca,UBOGON,0x7641,0x3fce,0x3fc9,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4009,0x76e8,0x406c,0x406e,0x4070,0x406d,0x406b,0x4071,0x4072,
+ UBOGON,UBOGON,UBOGON,0x408c,0x40e4,UBOGON,0x40e1,UBOGON,UBOGON,0x78f6,
+ 0x40e7,0x7900,0x40e2,0x411f,UBOGON,UBOGON,0x417e,UBOGON,0x4180,0x7a59,
+ 0x7a55,UBOGON,0x41b9,0x41b7,0x41b8,UBOGON,0x41ba,0x7af4,UBOGON,0x41d3,
+ 0x423f,0x7c04,0x4245,0x4241,0x7c15,0x4242,0x4243,0x423b,0x4238,UBOGON,
+ UBOGON,0x423a,0x7bf5,UBOGON,UBOGON,0x423c,UBOGON,UBOGON,0x423e,UBOGON,
+ UBOGON,UBOGON,0x429e,0x429f,0x42a1,UBOGON,0x429b,0x4312,UBOGON,UBOGON,
+ UBOGON,0x4318,0x430c,UBOGON,0x4362,UBOGON,0x437a,UBOGON,UBOGON,0x43ae,
+ 0x43af,UBOGON,0x43ad,UBOGON
+ },
+ { /* ku 38 */
+ 0x43c4,0x43c7,0x43c6,0x43c5,UBOGON,UBOGON,0x81c1,0x4440,UBOGON,UBOGON,
+ 0x443f,0x4441,UBOGON,UBOGON,UBOGON,0x447f,UBOGON,0x4486,0x4481,0x4480,
+ 0x448e,0x454a,UBOGON,0x4547,UBOGON,UBOGON,0x454b,0x4546,0x454e,0x857d,
+ UBOGON,0x85a5,UBOGON,0x4548,UBOGON,0x4545,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x45db,0x45e7,0x45e4,UBOGON,UBOGON,0x45e1,
+ UBOGON,0x45e9,UBOGON,0x45e5,0x45e0,0x45e3,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x45ea,UBOGON,UBOGON,0x893a,0x4654,0x4658,0x465c,UBOGON,
+ 0x4655,0x468b,0x468c,0x46a6,0x46a5,UBOGON,UBOGON,UBOGON,UBOGON,0x46ff,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4730,0x4740,0x4741,UBOGON,UBOGON,0x4786,
+ UBOGON,0x47bf,0x47bd,UBOGON
+ },
+ { /* ku 39 */
+ UBOGON,UBOGON,UBOGON,UBOGON,0x47be,0x4819,UBOGON,0x481c,UBOGON,0x481b,
+ 0x4817,0x4818,0x8e51,UBOGON,UBOGON,0x483d,0x486a,0x4866,UBOGON,UBOGON,
+ 0x4867,UBOGON,0x4868,0x48ad,0x48ae,UBOGON,UBOGON,0x48d6,0x4909,UBOGON,
+ 0x9198,UBOGON,0x490c,0x490a,UBOGON,0x493b,0x493a,0x9384,0x9381,UBOGON,
+ 0x936f,UBOGON,UBOGON,UBOGON,0x49af,0x49aa,0x49ab,UBOGON,UBOGON,0x49b1,
+ UBOGON,0x49ac,0x49ec,UBOGON,UBOGON,0x4a01,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4a23,UBOGON,0x4a24,0x4a1e,UBOGON,0x4a4a,0x4a65,0x4a6a,UBOGON,UBOGON,
+ UBOGON,0x4a69,UBOGON,UBOGON,0x4a95,UBOGON,UBOGON,UBOGON,UBOGON,0x9842,
+ UBOGON,UBOGON,0x4acc,UBOGON,UBOGON,UBOGON,0x4acf,UBOGON,UBOGON,0x4b0f,
+ UBOGON,0x4b0e,0x4b0b,0x4b10
+ },
+ { /* ku 3a */
+ 0x4b0d,0x4b0c,UBOGON,UBOGON,UBOGON,UBOGON,0x4b46,0x4b48,0x9937,0x4b49,
+ UBOGON,UBOGON,0x4b91,0x4b8e,UBOGON,0x4bd8,0x4bd6,UBOGON,0x4bda,UBOGON,
+ 0x4bd7,UBOGON,0x9aff,0x4bf9,UBOGON,UBOGON,0x4bfc,UBOGON,UBOGON,UBOGON,
+ 0x9ba9,0x4c4a,0x9ba7,0x4c4e,0x9bb3,0x9bac,0x9bb0,UBOGON,UBOGON,UBOGON,
+ 0x9b9c,UBOGON,UBOGON,UBOGON,0x9d3c,0x9d1c,0x9d3a,0x4cd3,0x4ccd,0x4cd1,
+ UBOGON,UBOGON,0x9d32,0x9d34,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x9ec7,UBOGON,0x4d62,UBOGON,UBOGON,UBOGON,0x4d83,0x9f3f,UBOGON,0x4d92,
+ 0x349f,0x34a0,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3527,UBOGON,0x360b,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x368d,
+ UBOGON,UBOGON,0x3770,UBOGON
+ },
+ { /* ku 3b */
+ 0x5eeb,UBOGON,0x399a,0x399f,0x399d,UBOGON,UBOGON,UBOGON,0x399b,UBOGON,
+ 0x61d5,UBOGON,0x3a60,0x3a64,0x3a69,0x3a63,0x3a67,0x3a62,UBOGON,UBOGON,
+ UBOGON,0x6502,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3b26,0x3b23,0x3b25,UBOGON,UBOGON,0x3bf8,UBOGON,UBOGON,UBOGON,0x3bf7,
+ 0x3bfb,0x3bfa,UBOGON,UBOGON,UBOGON,UBOGON,0x3cb0,0x3caf,UBOGON,UBOGON,
+ UBOGON,0x3d7e,UBOGON,0x3d7d,0x3d80,UBOGON,UBOGON,0x3d7f,0x3d86,UBOGON,
+ UBOGON,0x7012,UBOGON,0x3d81,UBOGON,UBOGON,UBOGON,0x3e24,0x3e58,0x3e57,
+ 0x3ea0,UBOGON,0x3efe,UBOGON,UBOGON,0x3f15,UBOGON,0x3f47,0x3f46,UBOGON,
+ UBOGON,0x3f6b,0x3f6c,0x7585,0x7654,UBOGON,0x3fcc,UBOGON,0x7655,UBOGON,
+ 0x3fcb,0x76a7,0x76a8,0x3ff9
+ },
+ { /* ku 3c */
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4078,0x407a,0x4075,UBOGON,0x4076,0x4077,
+ UBOGON,UBOGON,0x40ea,0x40ee,0x40ed,UBOGON,0x40ec,0x790f,UBOGON,UBOGON,
+ 0x4184,0x4185,0x4183,UBOGON,0x41bc,0x41bd,0x41d4,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4255,UBOGON,0x4250,0x424c,0x4248,UBOGON,0x4253,UBOGON,0x4257,
+ 0x4254,0x424e,0x424a,0x4251,UBOGON,UBOGON,0x4249,0x424b,0x4263,UBOGON,
+ UBOGON,0x42a7,0x42a6,0x42a4,UBOGON,UBOGON,UBOGON,0x7ce4,0x7ce5,UBOGON,
+ UBOGON,0x7e65,0x7e4e,0x4317,UBOGON,0x4316,UBOGON,UBOGON,0x4363,UBOGON,
+ UBOGON,0x7f82,UBOGON,0x437b,0x437c,UBOGON,UBOGON,UBOGON,UBOGON,0x43b0,
+ 0x802d,UBOGON,UBOGON,0x4442,UBOGON,0x4444,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4488,0x448f,0x4553
+ },
+ { /* ku 3d */
+ 0x455b,UBOGON,0x4559,UBOGON,UBOGON,UBOGON,UBOGON,0x85ca,UBOGON,UBOGON,
+ 0x4554,0x85bc,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4599,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x45f1,UBOGON,0x45ef,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4662,UBOGON,0x4663,UBOGON,0x4660,
+ 0x4661,0x465f,UBOGON,UBOGON,UBOGON,0x468d,UBOGON,0x468e,UBOGON,UBOGON,
+ UBOGON,0x4709,UBOGON,UBOGON,0x4705,UBOGON,UBOGON,0x4703,0x4706,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4731,UBOGON,UBOGON,0x474f,UBOGON,UBOGON,
+ UBOGON,0x4766,0x8cff,0x47c4,UBOGON,0x47c3,0x47c1,0x47c5,UBOGON,UBOGON,
+ UBOGON,0x4821,UBOGON,0x481f,0x4822,UBOGON,0x4827,0x4820,UBOGON,0x486d,
+ 0x486c,0x486b,0x486f,0x4870
+ },
+ { /* ku 3e */
+ UBOGON,UBOGON,UBOGON,0x91a6,UBOGON,UBOGON,0x4942,UBOGON,0x93b6,UBOGON,
+ 0x4944,0x4940,UBOGON,UBOGON,0x493f,UBOGON,0x93ab,0x498b,UBOGON,UBOGON,
+ 0x4a25,0x4a28,UBOGON,0x9721,UBOGON,UBOGON,0x4a75,0x4a72,UBOGON,0x4a6f,
+ UBOGON,UBOGON,0x4a76,0x4a71,0x97a7,UBOGON,0x4a97,UBOGON,0x4ad7,UBOGON,
+ 0x4ad6,UBOGON,0x4ad8,0x4adc,0x4adb,0x4ad4,0x983e,0x4b13,0x4b11,0x4b14,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4b51,0x4b50,0x4b53,0x4b54,0x4b52,UBOGON,
+ UBOGON,UBOGON,0x4b6d,UBOGON,UBOGON,0x4b95,0x4b99,UBOGON,0x4b9a,UBOGON,
+ 0x4b93,0x4b97,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4bdc,UBOGON,0x4bfd,
+ UBOGON,UBOGON,0x4bfe,0x4c00,0x4c02,0x4c01,0x4c03,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4c27,0x4c26,0x4c24
+ },
+ { /* ku 3f */
+ 0x4c4c,0x9bbc,0x4c50,0x4c55,0x4c53,0x9bb7,0x4c52,UBOGON,0x4c57,0x9bbe,
+ 0x4c58,0x4cd6,UBOGON,UBOGON,0x4cd4,UBOGON,0x4cda,0x4cd9,UBOGON,0x9d62,
+ 0x4cd5,0x4ce4,UBOGON,0x4cdc,0x4d1b,0x9e8f,0x4d37,0x4d36,0x4d4b,0x9ecb,
+ 0x4d66,0x4d76,UBOGON,0x4d7e,0x4d7d,0x4d7f,0x4d84,0x4d8b,UBOGON,0x4d94,
+ 0x34a1,0x3511,UBOGON,0x3610,0x56a9,UBOGON,UBOGON,UBOGON,UBOGON,0x5913,
+ UBOGON,0x3732,0x5bf4,UBOGON,UBOGON,UBOGON,UBOGON,0x3820,UBOGON,UBOGON,
+ UBOGON,0x389d,UBOGON,0x61ec,0x61ef,UBOGON,UBOGON,0x39a5,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3c02,0x3bfe,0x3c01,
+ 0x6ad6,0x3c03,0x3bff,UBOGON,0x3c04,UBOGON,0x3c4a,UBOGON,0x3d87,UBOGON,
+ 0x3d84,UBOGON,0x3d85,0x7209
+ },
+ { /* ku 40 */
+ UBOGON,UBOGON,UBOGON,0x3e59,0x7379,UBOGON,0x74c6,UBOGON,UBOGON,0x3f04,
+ 0x3f49,0x3f48,UBOGON,0x3f6d,0x3fd2,0x3fd3,UBOGON,0x3fd1,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3fe7,0x400a,0x77c3,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x40f0,UBOGON,UBOGON,0x791f,UBOGON,UBOGON,0x7a65,UBOGON,UBOGON,0x41be,
+ 0x41bf,UBOGON,0x7ac6,0x7c3a,UBOGON,0x7c36,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x425e,UBOGON,UBOGON,0x425b,UBOGON,UBOGON,UBOGON,0x7ceb,0x42ab,UBOGON,
+ 0x42ac,UBOGON,UBOGON,UBOGON,UBOGON,0x431f,0x431d,UBOGON,UBOGON,0x431c,
+ UBOGON,0x431e,UBOGON,UBOGON,UBOGON,UBOGON,0x4364,UBOGON,0x7f84,UBOGON,
+ UBOGON,UBOGON,0x4448,0x4447,UBOGON,UBOGON,0x455e,0x4561,UBOGON,0x85e0,
+ 0x85f3,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 41 */
+ 0x45f7,UBOGON,UBOGON,0x45f4,0x45f8,0x881e,UBOGON,UBOGON,UBOGON,0x4664,
+ UBOGON,0x4692,0x468f,0x4690,0x89b4,0x4693,0x46a8,UBOGON,UBOGON,0x46a9,
+ UBOGON,UBOGON,0x89f9,UBOGON,UBOGON,0x8b44,0x470e,UBOGON,0x470f,UBOGON,
+ UBOGON,0x470b,UBOGON,UBOGON,UBOGON,UBOGON,0x4710,0x4751,UBOGON,0x4750,
+ UBOGON,0x4763,UBOGON,UBOGON,0x47c7,UBOGON,0x8e71,0x4824,0x4826,0x8e6e,
+ UBOGON,0x8e79,UBOGON,0x8ec4,0x4874,0x4873,0x4872,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x48b1,0x908c,UBOGON,0x490e,0x4911,0x4910,0x490f,0x4912,0x4949,
+ 0x93c9,0x494f,0x494d,UBOGON,UBOGON,0x4955,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4a2c,0x4a2b,UBOGON,UBOGON,0x4a79,UBOGON,UBOGON,UBOGON,
+ 0x4a7a,0x97b0,0x4a99,0x4a9a
+ },
+ { /* ku 42 */
+ UBOGON,0x4aae,0x4aaf,UBOGON,0x4ae4,0x4ae1,0x4ade,0x4ae6,0x4adf,UBOGON,
+ 0x4ae7,0x4ae2,0x4ae0,UBOGON,0x4ae5,0x985a,UBOGON,0x4b18,UBOGON,UBOGON,
+ UBOGON,0x4b56,UBOGON,0x9946,UBOGON,UBOGON,UBOGON,0x4b6e,UBOGON,UBOGON,
+ 0x4b9d,0x4ba0,0x4b9c,UBOGON,UBOGON,0x4ba1,0x4ba2,0x4b9f,0x4bdf,0x4bde,
+ 0x9ac3,0x4bea,UBOGON,0x4c06,UBOGON,0x4c04,0x9b0f,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4c5f,0x9bf4,0x9bfa,0x4c5c,UBOGON,0x4c5e,UBOGON,UBOGON,0x9bdd,
+ 0x4c59,UBOGON,UBOGON,0x4c64,0x4c5d,0x4c62,UBOGON,0x4c65,UBOGON,0x9bed,
+ 0x4c5b,0x9bef,UBOGON,0x4cdd,UBOGON,0x4cdf,UBOGON,UBOGON,0x4ce2,UBOGON,
+ UBOGON,0x4d27,UBOGON,UBOGON,0x9e96,0x4d3a,0x4d3c,UBOGON,0x4d39,UBOGON,
+ UBOGON,0x4d3d,0x4d3b,0x9eb3
+ },
+ { /* ku 43 */
+ 0x4d4c,UBOGON,0x4d68,0x9ee2,UBOGON,0x4d80,0x4d85,UBOGON,0x4d95,UBOGON,
+ 0x4d96,UBOGON,0x9f8f,UBOGON,0x34a4,0x3512,0x56b1,0x3625,UBOGON,0x5b41,
+ 0x3737,UBOGON,UBOGON,UBOGON,UBOGON,0x3868,0x3867,0x389e,UBOGON,UBOGON,
+ UBOGON,0x39aa,UBOGON,0x39a9,0x39a4,UBOGON,UBOGON,0x3a71,0x3a6f,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3aad,UBOGON,0x6af6,0x3c0c,0x6af2,0x3c0b,UBOGON,
+ UBOGON,0x3c0f,0x3c79,UBOGON,UBOGON,UBOGON,UBOGON,0x3d8d,0x3d8f,UBOGON,
+ UBOGON,0x3d8e,0x3e0c,UBOGON,UBOGON,UBOGON,0x3ea6,UBOGON,0x3ea3,0x3ea4,
+ 0x3ea5,0x7588,0x3f6e,UBOGON,UBOGON,0x3ffa,UBOGON,0x407c,0x407e,0x407b,
+ 0x407d,UBOGON,UBOGON,0x408d,0x40f4,0x40f3,UBOGON,UBOGON,0x4189,UBOGON,
+ UBOGON,0x41c0,UBOGON,0x4265
+ },
+ { /* ku 44 */
+ UBOGON,UBOGON,0x42ad,0x4325,UBOGON,UBOGON,UBOGON,0x43c9,UBOGON,0x444a,
+ UBOGON,0x8267,0x4489,UBOGON,0x4566,0x4570,UBOGON,0x456d,0x4569,0x4567,
+ UBOGON,0x4572,0x860e,0x456e,UBOGON,0x459c,0x45fc,0x45fd,0x4604,0x45ff,
+ UBOGON,0x45fe,0x4600,UBOGON,0x4666,0x4669,UBOGON,0x46aa,0x46ab,0x4717,
+ UBOGON,UBOGON,UBOGON,0x4715,0x8b5e,0x4712,0x8d0e,UBOGON,UBOGON,UBOGON,
+ 0x47ca,UBOGON,0x47c9,0x47cb,UBOGON,UBOGON,UBOGON,0x4829,0x4828,UBOGON,
+ UBOGON,UBOGON,0x4840,0x4875,0x4876,UBOGON,0x4888,UBOGON,0x91b6,0x4957,
+ 0x9401,UBOGON,0x495f,UBOGON,0x941d,0x4958,0x495b,UBOGON,0x942f,UBOGON,
+ 0x49b3,UBOGON,0x49ef,UBOGON,0x4a30,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4a41,0x4a4b,UBOGON,0x4a7d
+ },
+ { /* ku 45 */
+ UBOGON,UBOGON,0x4a7c,UBOGON,UBOGON,UBOGON,0x97e0,UBOGON,0x97db,UBOGON,
+ UBOGON,0x9861,UBOGON,UBOGON,0x4ae8,0x4aea,0x4ae9,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4b1b,UBOGON,UBOGON,0x4b55,0x994a,0x4b59,0x4b58,UBOGON,UBOGON,
+ UBOGON,0x4ba4,0x4ba3,UBOGON,UBOGON,UBOGON,UBOGON,0x9a33,0x4ba7,UBOGON,
+ 0x4be0,UBOGON,UBOGON,UBOGON,0x4c08,0x4c0a,0x4c09,UBOGON,UBOGON,UBOGON,
+ 0x4c71,0x9c0f,0x4c6c,UBOGON,0x9c11,UBOGON,0x9c03,0x9c01,0x4c6e,UBOGON,
+ 0x9c16,UBOGON,UBOGON,UBOGON,0x4ce0,0x4cee,UBOGON,0x4ceb,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x9d93,0x4cea,0x4cef,0x4ce7,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4d48,0x4d49,UBOGON,UBOGON,UBOGON,0x4d4d,UBOGON,UBOGON,0x4d55,UBOGON,
+ UBOGON,0x4d6a,0x4d6c,UBOGON
+ },
+ { /* ku 46 */
+ 0x4d6b,UBOGON,UBOGON,UBOGON,0x4d98,0x4d99,0x4d97,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x535b,UBOGON,0x3616,UBOGON,0x56bf,
+ UBOGON,UBOGON,0x3739,UBOGON,UBOGON,0x3825,0x5dce,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3a74,UBOGON,UBOGON,0x3aae,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3d92,0x3d94,UBOGON,0x3d95,UBOGON,0x3e0d,UBOGON,0x3e25,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3fd5,0x3fd6,0x76ac,0x3fe8,UBOGON,
+ 0x407f,0x77d2,0x40f5,0x40f6,0x40f7,UBOGON,0x4124,0x418d,0x418a,UBOGON,
+ UBOGON,0x426c,0x4266,0x426a,UBOGON,0x4267,0x426d,0x4268,0x7c52,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4365,UBOGON,0x439a,UBOGON
+ },
+ { /* ku 47 */
+ 0x43b1,0x444b,0x444d,0x444c,0x444e,UBOGON,0x4573,0x4575,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4603,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x471e,UBOGON,0x8b73,UBOGON,0x4719,0x471c,
+ 0x471a,0x471d,0x8b76,UBOGON,UBOGON,0x4743,0x4752,UBOGON,UBOGON,0x4795,
+ UBOGON,0x47cc,UBOGON,0x482b,UBOGON,UBOGON,UBOGON,0x482a,0x8ec7,0x4877,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4913,0x4914,0x9434,
+ UBOGON,UBOGON,0x495d,UBOGON,0x4960,0x943e,0x4962,UBOGON,UBOGON,0x49b2,
+ 0x49f0,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4a4c,UBOGON,0x4a82,
+ 0x97bc,0x4a81,0x4a9b,UBOGON,0x4aa4,0x4aee,0x4aec,UBOGON,0x4aed,UBOGON,
+ 0x4af0,0x4aef,UBOGON,0x4b1d
+ },
+ { /* ku 48 */
+ UBOGON,0x4b60,0x4b5e,0x4b5d,UBOGON,UBOGON,UBOGON,0x4bb1,0x4bab,0x4bac,
+ 0x4bad,UBOGON,0x4bae,UBOGON,UBOGON,0x4be2,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x9b39,UBOGON,UBOGON,0x9c2a,0x4c7b,0x9c26,0x4c78,0x4c75,0x9c27,
+ UBOGON,0x4cf2,0x4cf4,0x4cf3,0x9dc0,0x9dc9,UBOGON,UBOGON,UBOGON,0x4d3f,
+ 0x4d3e,0x4d40,0x4d4e,0x4d57,0x4d59,0x4d58,0x4d56,UBOGON,UBOGON,0x4d6e,
+ UBOGON,UBOGON,UBOGON,0x9eec,UBOGON,UBOGON,0x4d81,0x4d86,UBOGON,0x4d8f,
+ UBOGON,UBOGON,UBOGON,0x9f68,0x4d9b,0x4db1,0x4db3,UBOGON,0x373a,UBOGON,
+ UBOGON,0x3827,UBOGON,UBOGON,0x386a,0x39ac,UBOGON,0x3c18,UBOGON,UBOGON,
+ 0x3c4c,UBOGON,0x3d96,UBOGON,0x3f4a,UBOGON,UBOGON,0x4081,UBOGON,0x4083,
+ 0x40f9,0x40f8,UBOGON,0x418e
+ },
+ { /* ku 49 */
+ 0x418f,0x41c1,UBOGON,UBOGON,UBOGON,0x4270,UBOGON,0x4271,UBOGON,0x432a,
+ 0x432d,0x437d,0x8032,0x8031,UBOGON,0x444f,UBOGON,UBOGON,0x4490,UBOGON,
+ UBOGON,UBOGON,0x4579,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4605,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x89fd,UBOGON,UBOGON,0x4721,
+ UBOGON,UBOGON,UBOGON,0x4732,UBOGON,UBOGON,0x47cf,UBOGON,UBOGON,0x908e,
+ 0x4916,0x4915,0x49b5,0x4a08,UBOGON,0x4a32,UBOGON,0x4a33,0x4a34,0x4a3c,
+ UBOGON,0x97c2,UBOGON,0x4a9c,UBOGON,0x4af4,0x4af2,UBOGON,0x4b62,UBOGON,
+ 0x4b61,0x4b64,0x4bb5,0x9a4b,0x4bb4,UBOGON,UBOGON,0x4be3,UBOGON,UBOGON,
+ 0x9b1c,0x4c0e,UBOGON,0x9b1b,UBOGON,0x4c2c,0x4c2b,UBOGON,UBOGON,UBOGON,
+ 0x4c85,0x4c81,0x4c7e,0x4c83
+ },
+ { /* ku 4a */
+ 0x4c80,UBOGON,0x9c42,UBOGON,0x9dd4,0x4cfb,0x4cf7,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4cf8,UBOGON,UBOGON,UBOGON,UBOGON,0x4d5a,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4d78,UBOGON,UBOGON,UBOGON,UBOGON,0x4d9d,0x4d9c,UBOGON,
+ UBOGON,UBOGON,0x34a9,0x34bf,0x56d0,0x56cf,UBOGON,0x5dda,UBOGON,0x3a77,
+ 0x3a76,UBOGON,0x3abb,0x66ea,UBOGON,0x3d9b,UBOGON,0x3e0f,0x3e5b,UBOGON,
+ 0x3f4c,0x3f6f,0x3fd9,UBOGON,0x4082,UBOGON,UBOGON,UBOGON,UBOGON,0x4274,
+ 0x4272,UBOGON,UBOGON,UBOGON,0x4273,UBOGON,UBOGON,UBOGON,0x42b1,0x432e,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x434e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x460b,UBOGON,UBOGON,
+ 0x466c,0x8b89,UBOGON,UBOGON
+ },
+ { /* ku 4b */
+ 0x478b,UBOGON,0x47d0,0x482d,UBOGON,0x48e4,0x4971,UBOGON,0x9458,0x496f,
+ UBOGON,0x4a87,0x4aa5,UBOGON,UBOGON,0x4b1e,0x4b65,0x4bb9,0x4bb7,0x4bb8,
+ 0x4be4,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4c8c,0x4c89,0x4c8a,UBOGON,
+ UBOGON,0x4c8b,UBOGON,UBOGON,UBOGON,0x4d01,0x4cfe,0x9de7,0x4d03,0x4d06,
+ UBOGON,0x9dea,0x9df1,UBOGON,0x4d1d,0x4d43,UBOGON,UBOGON,UBOGON,0x4d4f,
+ UBOGON,UBOGON,0x4d5b,0x4d70,UBOGON,0x4d88,UBOGON,UBOGON,0x4d89,0x9f44,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x9f6d,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x5dd9,UBOGON,UBOGON,UBOGON,UBOGON,0x3d9e,0x3d9f,0x3ea7,0x3f4b,
+ 0x3fdb,0x3fda,UBOGON,0x77d6,0x408e,0x4276,UBOGON,0x4330,0x432f,UBOGON,
+ 0x4366,UBOGON,0x457e,UBOGON
+ },
+ { /* ku 4c */
+ UBOGON,UBOGON,0x883a,UBOGON,0x8975,0x466f,UBOGON,0x47d1,0x482f,UBOGON,
+ UBOGON,0x48b2,0x4918,0x4917,UBOGON,0x4976,UBOGON,UBOGON,0x4a4f,0x4a89,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4af5,0x4b1f,UBOGON,UBOGON,0x9a5d,0x4be5,
+ UBOGON,UBOGON,0x4c10,UBOGON,0x4c0f,UBOGON,UBOGON,UBOGON,UBOGON,0x4c2f,
+ 0x4c30,0x9c64,UBOGON,UBOGON,0x4c93,0x4c94,UBOGON,0x4d07,0x4d09,0x4d08,
+ UBOGON,0x4d0b,UBOGON,0x9e0a,UBOGON,UBOGON,UBOGON,0x4d50,0x4d71,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4d7b,0x4d7c,UBOGON,0x9f73,UBOGON,0x4da1,
+ UBOGON,UBOGON,0x4da0,0x4da2,UBOGON,0x361b,UBOGON,0x3682,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3fe9,UBOGON,0x4084,0x77e1,UBOGON,UBOGON,UBOGON,0x42b3,
+ 0x4334,0x4333,0x4580,UBOGON
+ },
+ { /* ku 4d */
+ UBOGON,0x46ad,UBOGON,0x4744,0x4755,UBOGON,0x47d2,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4a8a,UBOGON,UBOGON,0x4b67,UBOGON,UBOGON,0x4be6,UBOGON,
+ 0x4c13,UBOGON,0x9b2d,UBOGON,0x4c97,0x9e0c,UBOGON,UBOGON,0x4d0c,UBOGON,
+ UBOGON,UBOGON,0x4d46,0x4d5c,0x4d74,0x4d72,UBOGON,UBOGON,UBOGON,0x9f1f,
+ UBOGON,UBOGON,0x4da4,0x4da3,UBOGON,UBOGON,UBOGON,UBOGON,0x4db4,UBOGON,
+ 0x3536,UBOGON,UBOGON,UBOGON,0x3cb2,UBOGON,UBOGON,0x3f16,0x7c70,0x4277,
+ UBOGON,0x457f,UBOGON,UBOGON,0x487d,0x9479,UBOGON,0x974a,0x4a8c,UBOGON,
+ 0x4b68,0x4bbe,0x4c15,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4d75,0x4da5,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4278,0x4335,0x7e9d,0x4582,UBOGON,
+ 0x4583,UBOGON,0x4671,UBOGON
+ },
+ { /* ku 4e */
+ 0x487e,0x4a8e,UBOGON,0x9960,0x4b69,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4c9a,0x4c9b,UBOGON,UBOGON,UBOGON,0x4d90,UBOGON,0x9f9e,UBOGON,
+ UBOGON,0x4586,0x4585,UBOGON,0x460e,UBOGON,0x4695,UBOGON,0x4919,UBOGON,
+ 0x4bc0,UBOGON,UBOGON,UBOGON,0x9ef8,0x9f3a,0x9f7d,UBOGON,UBOGON,0x400d,
+ 0x4c16,UBOGON,0x4da9,0x4daa,0x4085,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x9f96,UBOGON,0x4bc2,0x4c31,0x4d11,0x4dab,0x4c9c,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ }
+};
+
+/* CNS 11643 plane 5 conversion table */
+
+static const unsigned short
+ cns11643_5tab[MAX_CNS11643_KU_5][MAX_CNS11643_TEN] = {
+ { /* ku 01 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x355a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6729,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3cbc,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x49b9,UBOGON
+ },
+ { /* ku 02 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x34de,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3543,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x37ac,0x37aa,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5e07,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5fcb,
+ 0x38fe,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 03 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3441,
+ UBOGON,UBOGON,0x34b4,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x37b3,
+ UBOGON,UBOGON,UBOGON,0x37b4,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 04 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3c1d,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3c7c,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3f55,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4126,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x49bc,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 05 */
+ UBOGON,UBOGON,UBOGON,UBOGON,0x344a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x344e,UBOGON,UBOGON,0x34c9,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x52b7,0x52b8,UBOGON,0x52b6,0x52ba,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x357b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3620,UBOGON,UBOGON,UBOGON,UBOGON,0x3689,0x3695,UBOGON,UBOGON,UBOGON,
+ 0x36be,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x37c3,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 06 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3877,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x39d4,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3b4c,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3c20,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3c5b,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3e2d,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 07 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3e63,UBOGON,UBOGON,UBOGON,0x3f18,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3f74,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4128,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x43da,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x44a4,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x488e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 08 */
+ 0x345b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3753,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 09 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3880,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x38dd,UBOGON,UBOGON,UBOGON,0x38de,UBOGON,
+ 0x3922,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6306,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3adb,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x6b85,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3cda,0x3cdb,UBOGON,UBOGON
+ },
+ { /* ku 0a */
+ UBOGON,UBOGON,UBOGON,0x3cd7,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x401a,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0b */
+ 0x41d6,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x43de,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x43e5,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4672,UBOGON,
+ 0x46af,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x49c4,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3463,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x34ec,UBOGON,UBOGON
+ },
+ { /* ku 0c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3598,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3758,UBOGON,UBOGON
+ },
+ { /* ku 0d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x37d3,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x38e2,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3954,UBOGON,UBOGON,0x392f,UBOGON,UBOGON,
+ 0x39b6,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0e */
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3b35,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3b5d,UBOGON,UBOGON,0x3c29,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3e1f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3e72,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3f0b,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x40a2,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x43ec,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 10 */
+ 0x44bb,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4621,UBOGON,UBOGON,UBOGON,0x461f,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8c38,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4791,0x4796,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 11 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x34bc,UBOGON,UBOGON,0x34d8,UBOGON,
+ UBOGON,0x34f4,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3785,UBOGON,0x3783,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 12 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x38ba,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3969,UBOGON,UBOGON,0x3945,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3b6c,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 13 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3d04,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3e3a,UBOGON,UBOGON,UBOGON,0x3e79,UBOGON,
+ UBOGON,0x7309,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3f5d,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3f8a,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4027,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 14 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4146,0x4140,UBOGON,0x413f,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x419d,0x41cb,UBOGON,0x41e1,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x427f,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4346,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x441a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 15 */
+ UBOGON,UBOGON,UBOGON,0x44d3,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x44d0,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x458e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x46c3,0x46b6,UBOGON,UBOGON,UBOGON,0x8a2f,UBOGON,
+ 0x46c0,0x46b8,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x47d9,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 16 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x48ec,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x52dc,UBOGON,UBOGON,UBOGON,0x35cc,UBOGON,0x35a2,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x35b6,UBOGON,UBOGON,0x35c5,0x35c6,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 17 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3683,0x5921,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x36f8,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x36f6,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x379b,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x5e3f,UBOGON,UBOGON
+ },
+ { /* ku 18 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x388d,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3956,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x395b,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3a96,UBOGON,UBOGON
+ },
+ { /* ku 19 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3b7e,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3b81,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3c35,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3c9b,UBOGON,
+ UBOGON,0x3d00,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3ed3,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3f9f,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x40b1,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x414b,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1b */
+ UBOGON,UBOGON,UBOGON,0x7b3f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x42d6,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4389,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4400,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x44dc,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x83d0,0x4590,
+ UBOGON,UBOGON,UBOGON,0x45b1,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x86e7,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x45aa,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x467d,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4769,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x47a1,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x47e7,
+ UBOGON,UBOGON,0x47ec,UBOGON,UBOGON,UBOGON,0x47df,UBOGON,UBOGON,UBOGON,
+ 0x4833,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4bee,
+ UBOGON,0x4c32,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x34fc,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x352a,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x35d3,UBOGON,UBOGON,UBOGON,0x35d7,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x37f3,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1f */
+ 0x3891,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x38c0,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6117,UBOGON,0x3963,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3970,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3b02,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 20 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3d35,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3ee0,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3f9a,UBOGON,UBOGON,UBOGON,0x3fa3,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4005,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 21 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x415e,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x41a6,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x435d,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x43b8,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 22 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4434,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x446f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4594,0x4593,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x8714,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x88d1,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 23 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x8ccb,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4792,
+ UBOGON,UBOGON,UBOGON,0x47aa,UBOGON,UBOGON,UBOGON,0x47a7,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x47ef,UBOGON,UBOGON,UBOGON,UBOGON,0x8eed,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4922,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 24 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4ab5,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4b75,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3482,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x52ec,UBOGON,UBOGON,UBOGON,
+ 0x52e8,UBOGON,UBOGON,UBOGON,UBOGON,0x3535,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 25 */
+ UBOGON,0x35f0,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x38f0,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 26 */
+ UBOGON,UBOGON,0x3a3e,UBOGON,0x3a39,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3aa1,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3b3b,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3bb7,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3c57,0x3c70,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3ca7,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3d54,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 27 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3f31,
+ 0x7527,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3fbf,UBOGON,0x3fe4,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x404a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x40cf,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x798c
+ },
+ { /* ku 28 */
+ UBOGON,0x7991,UBOGON,0x4114,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x420d,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4201,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x42f1,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 29 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4476,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x450a,0x4503,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8660,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4635,0x4636,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4773,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x47b1,UBOGON,0x47af,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x47f4,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x48dc,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4999,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4ab6,
+ 0x4abd,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x9904,UBOGON,0x999b,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4bf4,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4c35,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4caa,UBOGON,0x4d1f,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3550,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x377b,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3809,UBOGON,UBOGON,UBOGON,0x3807,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x39bb,UBOGON,UBOGON,0x3a4b,UBOGON,UBOGON,UBOGON,0x3a4d,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3ac2,UBOGON,UBOGON,UBOGON,UBOGON,0x3b1a,UBOGON,UBOGON,
+ 0x3b12,UBOGON,UBOGON,UBOGON,UBOGON,0x3b3c,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3bc3,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3bc0,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x729f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3fe5,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x405f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4118,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2f */
+ UBOGON,UBOGON,0x41ad,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x421e,UBOGON,UBOGON,
+ UBOGON,0x4227,UBOGON,UBOGON,UBOGON,UBOGON,0x4218,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4220,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x42f4,0x4302,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 30 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4524,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x8770,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 31 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x477d,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8e37,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4864,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 32 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x9703,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4ac2,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 33 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4cac,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x349b,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x52f6,0x3526,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 34 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3815,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x389c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x64cc,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 35 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3d64,0x3d6e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3d5f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3ef6,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x764a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 36 */
+ UBOGON,0x4073,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x40dd,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7ab9,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x7bd7,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 37 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 38 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4688,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x47bb,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x47ba,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4812,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4861,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x48e1
+ },
+ { /* ku 39 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x999f,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4b89,UBOGON,0x4b8c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x9b8d,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x9e77,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x381a,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3998,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3bf1,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3f6a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x764b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x76a2,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4485,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4239,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x423d,0x4244
+ },
+ { /* ku 3d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4246,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x430e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4310,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x443e,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4484,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4549,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x45df,UBOGON,UBOGON,
+ 0x45f0,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x468a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4702,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x47c0,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4815,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x48e2,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 40 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4b4d,UBOGON,UBOGON,UBOGON,UBOGON,0x4b4a,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 41 */
+ UBOGON,UBOGON,0x4d63,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4d93,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x350e,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x360a,UBOGON,UBOGON,UBOGON,UBOGON,0x360d,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 42 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4074,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 43 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4256,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x425a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x42a3,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x42a8,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 44 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x453c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x459b,0x459a,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x87f1,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x465e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x46a7,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 45 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x486e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x490d,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4943,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4a73,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4a74,UBOGON,UBOGON,0x4a70
+ },
+ { /* ku 46 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4bdb,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4c29,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4c51,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x9bba,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 47 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4cdb,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4d65,UBOGON,
+ UBOGON,0x4d64,0x4d67,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3611,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3733,UBOGON,UBOGON,UBOGON,UBOGON,0x3821,0x3822,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x39a6,UBOGON,UBOGON,UBOGON,UBOGON,0x39a3,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 48 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3ea1,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x425f,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x425c,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 49 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x45f5,UBOGON,0x8804,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4711,0x470c,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 4a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x494e,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4b94,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 4b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4c63,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4c61,UBOGON,0x9bd8,UBOGON,
+ 0x4c5a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4ce3,UBOGON,UBOGON,
+ 0x9d7c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4daf,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3613,UBOGON,UBOGON,0x3680,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 4c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4db0,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7c46,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 4d */
+ UBOGON,0x4323,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4449,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x45fa,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 4e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4959,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4a7e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4ba8,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4ba9,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 4f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x9d8d,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4d54,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4d6d,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4d8e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 50 */
+ UBOGON,UBOGON,UBOGON,0x3a72,UBOGON,UBOGON,0x3c14,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3d93,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x426f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x42b0,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x352e,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 51 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4969,0x957e,UBOGON,UBOGON,UBOGON,UBOGON,0x49b4,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4a4d,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4a83,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 52 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4bb0,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4c0c,UBOGON,0x4c18,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x9c20,UBOGON,UBOGON,0x9c22,UBOGON,UBOGON,0x9c1e,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4cf5,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 53 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3e0e,UBOGON,UBOGON,UBOGON,UBOGON,0x3fd8,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 54 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x8970,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x482c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x496e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4a86,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4bb3,UBOGON,UBOGON,UBOGON,0x4beb,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 55 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x9c43,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x9de0,UBOGON,UBOGON,0x4d42,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4d41,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4dac,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3b2f,0x3b2e,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3c4d,0x3c7b,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 56 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x42b2,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4722,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x9459,UBOGON,0x4970,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 57 */
+ UBOGON,UBOGON,UBOGON,0x4c95,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4d00,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4d02,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4d9f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 58 */
+ UBOGON,UBOGON,UBOGON,UBOGON,0x457d,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4af6,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4c12,
+ UBOGON,UBOGON,UBOGON,0x4c91,UBOGON,UBOGON,UBOGON,UBOGON,0x4c90,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 59 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3a79,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3c4e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4d1e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 5a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x9c72,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 5b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3828,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4d91,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 5c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4587,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ }
+};
+
+/* CNS 11643 plane 6 conversion table */
+
+static const unsigned short
+ cns11643_6tab[MAX_CNS11643_KU_6][MAX_CNS11643_TEN] = {
+ { /* ku 01 */
+ UBOGON,UBOGON,0x3405,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x353f,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x382a,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x38a7,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x38fa
+ },
+ { /* ku 02 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3400,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x34db,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 03 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3438,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6530,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x353a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 04 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x38c9,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 05 */
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3c4f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x43cd,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4492,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3445,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x34b5,UBOGON,0x34b6,UBOGON
+ },
+ { /* ku 06 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3537,UBOGON,0x3530,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x373e,0x374d,0x3751,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 07 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3873,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x39be,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 08 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3c1e,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3cc8,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3cc3,0x3cc7,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3f56,UBOGON,0x3540,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 09 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x49bd,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x351a,UBOGON,
+ UBOGON,0x352c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3549,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x357c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x37bf,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x37ba,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x38d8,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x39b2,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3ac4,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3e2b,UBOGON,UBOGON,UBOGON,0x3e61,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x72dd
+ },
+ { /* ku 0d */
+ 0x3eb2,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4129,UBOGON,UBOGON,UBOGON,UBOGON,0x4192,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3458,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x351c,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3637,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 10 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3843,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x38b4,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 11 */
+ UBOGON,UBOGON,UBOGON,UBOGON,0x38e0,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3ae4,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x6804,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3c26,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 12 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3e6f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 13 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x401c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4100,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x42b6,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x43e4,UBOGON
+ },
+ { /* ku 14 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x44ad,UBOGON,0x82ff,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x8fec,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x48c3,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 15 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3467,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x53de,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3596,UBOGON
+ },
+ { /* ku 16 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x5a30,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3759,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5bb2,
+ UBOGON,0x3776,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x37d2,UBOGON,UBOGON
+ },
+ { /* ku 17 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3920,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3929,UBOGON,UBOGON,
+ UBOGON,0x3938,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 18 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3a89,0x3ab0,UBOGON,UBOGON,UBOGON,0x3abf,UBOGON,UBOGON,UBOGON,0x3ac5,
+ UBOGON,UBOGON,0x3aea,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3c37,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3ce4,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 19 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3ce8,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3e20,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3f86,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4356,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4367,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1b */
+ 0x8038,UBOGON,UBOGON,0x8081,UBOGON,UBOGON,UBOGON,0x43d1,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x43eb,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x44b6,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x45a3,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4610,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x491b,UBOGON,0x4987,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3557,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x369d,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x375f,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3779,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3798,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x37dc,UBOGON,UBOGON,0x37df,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 20 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x39fd,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3af4,UBOGON,0x3aef,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3c31,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 21 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3d03,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3d09,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3e3e,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3e77,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3e7b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 22 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x413a,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x41a0,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x41e5,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 23 */
+ UBOGON,0x427d,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x436d,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x43f8,UBOGON,0x8158,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4450,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4468,
+ 0x4467,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 24 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x44cf,UBOGON,UBOGON,
+ UBOGON,0x44cd,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x45ab,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x46c1,UBOGON,UBOGON,0x8a24,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 25 */
+ UBOGON,0x4747,UBOGON,0x4757,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x8dc3,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x484e,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 26 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3477,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x51f2,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 27 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x55b6,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x35c4,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3652,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 28 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x386b,0x5ec3,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3941,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3950,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 29 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3ac0,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3afc,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3479,UBOGON,UBOGON,0x440b
+ },
+ { /* ku 2a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3d1e,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3e85,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3f97,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4093,UBOGON,UBOGON,UBOGON,0x7861,UBOGON,UBOGON,UBOGON,0x40b2,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7a01,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x41a4,UBOGON,UBOGON,0x41f2,UBOGON,UBOGON,UBOGON,UBOGON,0x41f1,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4281,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x42dc,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x440c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x446d,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x44e6,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8849,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x8999,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x46d1,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x472c,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x476e,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x47e5,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x47e6,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x489f,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x921f,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 30 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4a0d,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x34f8,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x5313,UBOGON,0x3533,UBOGON,UBOGON,UBOGON,0x353c,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 31 */
+ UBOGON,0x354f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x35dc,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x55e0,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 32 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x37f5,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x38c3,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6139,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3971,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 33 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3b01,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 34 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6ed7,UBOGON,0x3d41,UBOGON,UBOGON,
+ UBOGON,0x3dd7,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3e45,UBOGON,UBOGON,
+ UBOGON,0x733d,UBOGON,UBOGON
+ },
+ { /* ku 35 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3ff2,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4049,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 36 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x41a9,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x43cb,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x440d,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 37 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 38 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4631,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x46d5,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 39 */
+ UBOGON,UBOGON,UBOGON,0x4856,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x48f4,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3a */
+ UBOGON,UBOGON,UBOGON,0x9775,UBOGON,UBOGON,UBOGON,0x4a58,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3487,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x37fa,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x38f2,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3a2f,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3ac1,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3c40,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3d3f,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3d46,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3e8f,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3fac,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4168,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x41ab,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 40 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4291,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7fe4,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x8088,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4430,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 41 */
+ 0x445d,UBOGON,UBOGON,0x4475,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x44ff,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x450b,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x45c8,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 42 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4774,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x47ac,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 43 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4a16,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 44 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4b3b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4bef,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ }
+};
+
+/* CNS 11643 plane 7 conversion table */
+
+static const unsigned short
+ cns11643_7tab[MAX_CNS11643_KU_7][MAX_CNS11643_TEN] = {
+ { /* ku 01 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x35f6,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5655,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3667,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 02 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x617f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3992,UBOGON,UBOGON
+ },
+ { /* ku 03 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3a45,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3bc9,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 04 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3d55,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 05 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3ee8,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3fb6,UBOGON,UBOGON,UBOGON,0x3fbd,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 06 */
+ UBOGON,UBOGON,UBOGON,0x40d6,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4171,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4298,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 07 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 08 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x45cd,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x46f6,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 09 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x477a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x47b8,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4801,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x492f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4931,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x499c,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x49e6,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4b38,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4c3a,UBOGON,UBOGON,0x4cb1,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4d47,UBOGON,0x4d51,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3747,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3817,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x38a3,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3b1e,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x71d7,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3e9a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 10 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3fc2,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x40dc,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x41b3,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 11 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 12 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x8666,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x45d9,UBOGON,0x45dd,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 13 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x46fc,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 14 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8f3a,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4907,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x933d,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x49a8,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 15 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4aca,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 16 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4b8d,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4cc0,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4cca,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4d25,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4d4a,UBOGON,
+ 0x4d53,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 17 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3605,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 18 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3bf0,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 19 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3e02,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3e23,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4315,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x455d,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x465a,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4785,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x490b,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4939,UBOGON,UBOGON,UBOGON,
+ 0x4937,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4a6b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4acd,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4c4d,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3510,UBOGON
+ },
+ { /* ku 1f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x367b,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x381d,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x64f5,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3b27,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 20 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3fcf,UBOGON,UBOGON,UBOGON,0x3fcd,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 21 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4182,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4252,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x7f80,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 22 */
+ UBOGON,UBOGON,UBOGON,0x4451,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x455a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 23 */
+ UBOGON,0x4665,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8d01,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x48af,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 24 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4941,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4a29,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4a2a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4a96,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4b12
+ },
+ { /* ku 25 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 26 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x367e,0x58e1,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 27 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x39a7,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4320,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 28 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x7ce9,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x81cb,UBOGON,UBOGON
+ },
+ { /* ku 29 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4565,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2a */
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4704,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4764,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4823,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x95d9,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x34b9,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2e */
+ UBOGON,0x3c4b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x40f1,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 30 */
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4667,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4714,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4889,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 31 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4c0b,UBOGON,
+ UBOGON,UBOGON,0x4c17,UBOGON
+ },
+ { /* ku 32 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4c6d,UBOGON,0x4c70,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4ced,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4d8d,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x34a7,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 33 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6707,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3e5a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 34 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x42af,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x456b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 35 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x471b,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4963,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 36 */
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4a80,0x4a84,0x4a7f,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4af1,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x9a47,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 37 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4d1c,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x34a8,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 38 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x418b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x432b,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x457a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 39 */
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4609,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x466d,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x471f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x496a,0x496c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4bb2,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4c1d,UBOGON,UBOGON,UBOGON,0x4c2d,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4d45,UBOGON
+ },
+ { /* ku 3b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x460a,UBOGON,UBOGON,UBOGON,UBOGON,0x460c,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x482e,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3e */
+ UBOGON,UBOGON,0x4c8d,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4331,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 40 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4bbc,0x4bbb,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 41 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x4c14,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 42 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x361c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3772,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 43 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4d0e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 44 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7674,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x4bec,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 45 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4279,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ }
+};
+
+#if CNS_OBSOLETE
+/* CNS 11643 plane 14 conversion table */
+
+static const unsigned short
+ cns11643_14tab[MAX_CNS11643_KU_14][MAX_CNS11643_TEN] = {
+ { /* ku 01 */
+ 0x4e28,0x4e36,0x4e3f,0x4e85,0x4e05,0x4e04,0x5182,0x5196,0x5338,0x5369,
+ 0x53b6,0x4e2a,0x4e87,0x4e49,0x51e2,0x4e46,0x4e8f,0x4ebc,0x4ebe,0x5166,
+ 0x51e3,0x5204,0x529c,UBOGON,0x5902,0x590a,0x5b80,0x5ddb,0x5e7a,0x5e7f,
+ 0x5ef4,0x5f50,0x5f51,0x5f61,0x961d,UBOGON,0x4e63,0x4e62,0x4ea3,0x5185,
+ 0x4ec5,0x4ecf,0x4ece,0x4ecc,0x5184,0x5186,UBOGON,UBOGON,0x51e4,0x5205,
+ 0x529e,0x529d,0x52fd,0x5300,0x533a,UBOGON,0x5346,0x535d,0x5386,0x53b7,
+ UBOGON,0x53cc,UBOGON,0x53ce,0x5721,UBOGON,0x5e00,0x5f0c,0x6237,0x6238,
+ 0x6534,0x6535,0x65e0,UBOGON,0x738d,0x4e97,0x4ee0,UBOGON,UBOGON,0x4ee7,
+ UBOGON,0x4ee6,UBOGON,UBOGON,UBOGON,UBOGON,0x56d8,0x518b,0x518c,0x5199,
+ 0x51e5,UBOGON,0x520b,UBOGON
+ },
+ { /* ku 02 */
+ UBOGON,0x5304,0x5303,0x5307,UBOGON,0x531e,0x535f,0x536d,0x5389,0x53ba,
+ 0x53d0,UBOGON,0x53f6,0x53f7,0x53f9,UBOGON,0x53f4,UBOGON,UBOGON,0x5724,
+ 0x5904,0x5918,0x5932,0x5930,0x5934,UBOGON,0x5975,UBOGON,0x5b82,0x5bf9,
+ 0x5c14,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5e81,0x5e83,
+ 0x5f0d,0x5f52,UBOGON,0x5fca,0x5fc7,0x6239,UBOGON,0x624f,0x65e7,0x672f,
+ 0x6b7a,0x6c39,UBOGON,UBOGON,0x6c37,0x6c44,0x6c45,0x738c,0x7592,0x7676,
+ 0x9093,0x9092,UBOGON,UBOGON,0x4e21,0x4e20,0x4e22,0x4e68,0x4e89,0x4e98,
+ 0x4ef9,0x4eef,UBOGON,UBOGON,0x4ef8,0x4f06,0x4f03,0x4efc,0x4eee,0x4f16,
+ UBOGON,0x4f28,0x4f1c,0x4f07,0x4f1a,0x4efa,0x4f17,0x514a,UBOGON,0x5172,
+ UBOGON,0x51b4,0x51b3,0x51b2
+ },
+ { /* ku 03 */
+ UBOGON,0x51e8,UBOGON,0x5214,0x520f,0x5215,0x5218,0x52a8,UBOGON,0x534b,
+ 0x534f,UBOGON,0x5350,UBOGON,0x538b,UBOGON,0x53be,UBOGON,0x53d2,0x5416,
+ 0x53ff,UBOGON,0x5400,UBOGON,0x5405,0x5413,0x5415,UBOGON,UBOGON,0x56e3,
+ 0x5735,0x5736,0x5731,0x5732,0x58ee,0x5905,0x4e54,UBOGON,0x5936,UBOGON,
+ UBOGON,UBOGON,0x597a,UBOGON,0x5986,UBOGON,UBOGON,0x5b86,0x5f53,0x5c18,
+ UBOGON,0x5c3d,0x5c78,UBOGON,UBOGON,UBOGON,UBOGON,0x5c80,UBOGON,0x5e08,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x5ef5,0x5f0e,UBOGON,UBOGON,UBOGON,0x5fd3,
+ 0x5fda,UBOGON,0x5fdb,UBOGON,0x620f,0x625d,0x625f,0x6267,0x6257,0x9f50,
+ UBOGON,0x65eb,0x65ea,UBOGON,0x6737,UBOGON,0x6732,0x6736,0x6b22,0x6bce,
+ UBOGON,0x6c58,0x6c51,0x6c77
+ },
+ { /* ku 04 */
+ 0x6c3c,UBOGON,0x6c5a,UBOGON,0x6c53,0x706f,0x7072,0x706e,UBOGON,UBOGON,
+ 0x7073,0x72b1,0x72b2,UBOGON,0x738f,UBOGON,UBOGON,UBOGON,0x793c,UBOGON,
+ 0x808d,0x808e,UBOGON,0x827b,UBOGON,0x8d71,0x8fb9,0x9096,0x909a,UBOGON,
+ 0x4e24,0x4e71,UBOGON,0x4e9c,0x4f45,0x4f4a,0x4f39,0x4f37,UBOGON,0x4f32,
+ 0x4f42,UBOGON,0x4f44,0x4f4b,UBOGON,0x4f40,0x4f35,0x4f31,0x5151,UBOGON,
+ 0x5150,0x514e,UBOGON,UBOGON,0x519d,UBOGON,0x51b5,0x51b8,0x51ec,0x5223,
+ 0x5227,0x5226,0x521f,0x522b,0x5220,0x52b4,0x52b3,UBOGON,0x5325,0x533b,
+ 0x5374,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x544d,UBOGON,UBOGON,0x543a,
+ UBOGON,UBOGON,0x5444,0x544c,0x5423,0x541a,0x5432,0x544b,0x5421,UBOGON,
+ 0x5434,0x5449,0x5450,0x5422
+ },
+ { /* ku 05 */
+ 0x543f,0x5451,0x545a,0x542f,UBOGON,0x56e9,0x56f2,0x56f3,0x56ef,0x56ed,
+ 0x56ec,0x56e6,0x5748,UBOGON,0x5744,0x573f,0x573c,0x5753,0x5756,UBOGON,
+ 0x575f,0x5743,0x5758,0x5757,UBOGON,UBOGON,UBOGON,0x5746,UBOGON,0x573d,
+ UBOGON,0x5742,0x5754,0x5755,0x58f1,0x58f2,0x58f0,0x590b,0x9ea6,0x56f1,
+ 0x593d,UBOGON,0x5994,0x598c,UBOGON,0x599c,UBOGON,UBOGON,0x599f,UBOGON,
+ 0x599b,UBOGON,0x5989,0x599a,UBOGON,0x6588,UBOGON,0x5b8d,UBOGON,0x5bfe,
+ 0x5bff,0x5bfd,0x5c2b,UBOGON,0x5c84,0x5c8e,0x5c9c,UBOGON,UBOGON,0x5c85,
+ 0x5df5,0x5e09,UBOGON,UBOGON,0x5e0b,UBOGON,0x5e92,0x5e90,0x5f03,UBOGON,
+ 0x5f1e,0x5f63,UBOGON,0x5fe7,0x5ffe,0x5fe6,0x5fdc,0x5fce,UBOGON,0x5ffc,
+ 0x5fdf,0x5fec,0x5ff6,UBOGON
+ },
+ { /* ku 06 */
+ 0x5ff2,0x5ff0,0x5ff9,UBOGON,0x6213,UBOGON,UBOGON,0x623b,0x623c,0x6282,
+ UBOGON,UBOGON,UBOGON,0x6278,0x628b,UBOGON,0x629e,0x62a5,0x629b,0x629c,
+ 0x6299,0x628d,0x6285,0x629d,0x6275,UBOGON,UBOGON,UBOGON,0x65f6,UBOGON,
+ UBOGON,UBOGON,0x66f5,0x675b,UBOGON,0x6754,0x6752,UBOGON,0x6758,0x6744,
+ 0x674a,0x6761,UBOGON,0x6c7f,0x6c91,0x6c9e,UBOGON,0x6c6e,0x6c7c,0x6c9f,
+ 0x6c75,UBOGON,0x6c56,0x6ca2,0x6c79,UBOGON,0x6ca1,UBOGON,0x6caa,0x6ca0,
+ UBOGON,0x7079,0x7077,0x707e,UBOGON,0x7075,0x707b,0x7264,UBOGON,0x72bb,
+ 0x72bc,0x72c7,0x72b9,0x72be,0x72b6,UBOGON,UBOGON,0x7398,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x7593,0x7680,UBOGON,0x7683,0x76c0,0x76c1,UBOGON,UBOGON,
+ 0x77f4,0x77f5,UBOGON,0x7acc
+ },
+ { /* ku 07 */
+ 0x7acd,0x7cfa,0x809f,0x8091,0x8097,0x8094,UBOGON,0x8286,0x828c,UBOGON,
+ 0x8295,UBOGON,0x866c,UBOGON,0x8fb5,0x8fbe,0x8fc7,UBOGON,0x8fc1,0x90a9,
+ 0x90a4,UBOGON,UBOGON,UBOGON,0x90a8,0x9627,0x9626,0x962b,0x9633,0x9634,
+ 0x9629,0x4e3d,UBOGON,0x4e9d,0x4f93,0x4f8a,UBOGON,UBOGON,0x4f6d,0x4f8e,
+ 0x4fa0,0x4fa2,0x4fa1,0x4f9f,0x4fa3,UBOGON,0x4f72,UBOGON,0x4f8c,0x5156,
+ UBOGON,UBOGON,0x5190,UBOGON,UBOGON,UBOGON,0x51ed,0x51fe,0x522f,UBOGON,
+ 0x523c,0x5234,0x5239,0x52b9,0x52b5,0x52bf,0x5355,UBOGON,0x5376,0x537a,
+ 0x5393,UBOGON,0x53c1,0x53c2,0x53d5,0x5485,UBOGON,0x545f,0x5493,0x5489,
+ 0x5479,0x9efe,0x548f,0x5469,0x546d,UBOGON,0x5494,0x546a,0x548a,UBOGON,
+ 0x56fd,0x56fb,0x56f8,UBOGON
+ },
+ { /* ku 08 */
+ 0x56fc,0x56f6,0x5765,0x5781,0x5763,0x5767,UBOGON,0x576e,0x5778,0x577f,
+ UBOGON,UBOGON,0x58f3,0x594b,0x594c,UBOGON,UBOGON,UBOGON,0x59ad,UBOGON,
+ 0x59c4,UBOGON,0x59c2,0x59b0,UBOGON,UBOGON,UBOGON,UBOGON,0x59bf,UBOGON,
+ 0x59c9,0x59b8,0x59ac,UBOGON,UBOGON,UBOGON,0x59b7,0x59d7,UBOGON,0x5b60,
+ UBOGON,0x5b96,0x5b9e,0x5b94,0x5b9f,0x5b9d,UBOGON,0x5c00,0x5c19,UBOGON,
+ UBOGON,0x5c49,0x5c4a,UBOGON,0x5cbb,0x5cc1,UBOGON,UBOGON,UBOGON,0x5cb9,
+ 0x5c9e,0x5cb4,0x5cba,0x5df6,0x5e13,0x5e12,0x5e77,UBOGON,0x5e98,UBOGON,
+ 0x5e99,0x5e9d,0x5ef8,UBOGON,0x5ef9,UBOGON,0x5f06,0x5f21,UBOGON,0x5f25,
+ 0x5f55,UBOGON,UBOGON,UBOGON,0x5f84,0x5f83,0x6030,0x6007,UBOGON,0x6036,
+ UBOGON,UBOGON,UBOGON,0x5fe9
+ },
+ { /* ku 09 */
+ 0x603d,0x6008,UBOGON,UBOGON,0x62ba,0x62b2,UBOGON,0x62b7,0x62e4,0x62a7,
+ UBOGON,UBOGON,UBOGON,0x62d5,0x62e1,0x62dd,0x62a6,0x62c1,0x62c5,0x62c0,
+ 0x62df,0x62e0,0x62de,UBOGON,0x6589,UBOGON,0x65a6,0x65ba,UBOGON,0x65ff,
+ UBOGON,0x6617,0x6618,0x6601,0x65fe,UBOGON,0x670c,UBOGON,0x676b,0x6796,
+ 0x6782,0x678a,UBOGON,0x67a3,UBOGON,0x67a2,0x678f,UBOGON,0x67f9,0x6780,
+ 0x6b26,0x6b27,0x6b68,0x6b69,UBOGON,0x6b81,0x6bb4,0x6bd1,UBOGON,UBOGON,
+ 0x6c1c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6c97,0x6c6c,0x6cdf,UBOGON,
+ 0x6cea,UBOGON,0x6ce4,0x6cd8,0x6cb2,0x6cce,0x6cc8,UBOGON,0x708b,0x7088,
+ 0x7090,0x708f,UBOGON,0x7087,0x7089,0x708d,0x7081,UBOGON,0x708c,UBOGON,
+ UBOGON,0x7240,UBOGON,UBOGON
+ },
+ { /* ku 0a */
+ 0x7265,0x7266,0x7268,UBOGON,UBOGON,0x72cd,0x72d3,0x72db,UBOGON,0x72cf,
+ 0x73a7,0x73a3,0x739e,UBOGON,0x73af,UBOGON,UBOGON,0x73aa,0x739c,UBOGON,
+ 0x7542,0x7544,0x753b,0x7541,UBOGON,0x759b,0x759e,UBOGON,0x79c4,0x79c3,
+ 0x79c6,UBOGON,UBOGON,0x79c7,UBOGON,0x79ca,UBOGON,UBOGON,0x7acf,0x7c76,
+ 0x7c74,0x7cff,0x7cfc,UBOGON,UBOGON,0x7f59,0x80a8,UBOGON,UBOGON,0x80b0,
+ UBOGON,0x80b3,UBOGON,0x80a4,0x80b6,0x80a7,0x80ac,UBOGON,0x80a6,0x5367,
+ 0x820e,0x82c4,0x833e,0x829c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x82aa,
+ UBOGON,0x82c9,UBOGON,UBOGON,0x82a6,0x82b2,UBOGON,UBOGON,UBOGON,0x8fcc,
+ 0x8fd9,0x8fca,0x8fd8,0x8fcf,0x90b7,UBOGON,0x90ad,0x90b9,0x9637,UBOGON,
+ 0x9641,0x963e,0x96b6,0x9751
+ },
+ { /* ku 0b */
+ 0x9763,0x4e57,0x4e79,0x4eb2,0x4eb0,0x4eaf,0x4eb1,0x4fd2,0x4fd5,UBOGON,
+ 0x4fbe,0x4fb8,0x4fb0,0x4fb1,0x4fc8,UBOGON,UBOGON,0x4fc6,0x4fcc,0x4fe5,
+ 0x4fe3,0x4fb4,0x516a,UBOGON,0x519f,UBOGON,0x51c1,UBOGON,0x51c2,0x51c3,
+ 0x5245,0x5248,UBOGON,UBOGON,0x524f,UBOGON,UBOGON,0x52c5,0x52ca,0x52c4,
+ 0x5327,0x5358,0x537d,UBOGON,0x53dd,0x53dc,0x53da,0x53d9,0x54b9,UBOGON,
+ 0x54d0,0x54b4,0x54ca,UBOGON,0x54a3,0x54da,0x54a4,UBOGON,0x54b2,0x549e,
+ 0x549f,0x54b5,UBOGON,UBOGON,0x54cd,UBOGON,0x54cc,UBOGON,0x5700,0x57ac,
+ 0x5791,0x578e,0x578d,0x5792,0x57a1,0x5790,0x57a6,0x57a8,UBOGON,0x579c,
+ 0x5796,0x57a7,UBOGON,UBOGON,UBOGON,UBOGON,0x58f5,UBOGON,0x5909,0x5908,
+ UBOGON,0x5952,UBOGON,UBOGON
+ },
+ { /* ku 0c */
+ 0x59df,UBOGON,0x59eb,0x59ef,0x59f0,0x59d5,0x5a0d,0x5a04,0x59f9,0x5a02,
+ 0x59f8,0x59e2,0x59d9,0x59e7,0x5b6a,UBOGON,UBOGON,0x5bab,UBOGON,0x5c1b,
+ 0x5c2f,UBOGON,0x663c,UBOGON,UBOGON,UBOGON,0x5cd1,0x5cdc,0x5ce6,0x5ce1,
+ 0x5ccd,UBOGON,0x5ce2,0x5cdd,0x5ce5,0x5dfb,0x5dfa,0x5e1e,UBOGON,0x5ea1,
+ UBOGON,UBOGON,0x5efc,0x5efb,0x5f2f,UBOGON,UBOGON,0x5f66,UBOGON,UBOGON,
+ UBOGON,0x605c,UBOGON,0x604e,0x6051,UBOGON,UBOGON,0x6023,0x6031,0x607c,
+ 0x6052,UBOGON,0x6060,0x604a,0x6061,UBOGON,0x6218,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x631f,0x6317,0x62ea,0x6321,0x6304,0x6305,
+ UBOGON,0x6531,0x6544,0x6540,UBOGON,0x6542,0x65be,UBOGON,0x6629,0x661b,
+ UBOGON,0x6623,0x662c,0x661a
+ },
+ { /* ku 0d */
+ 0x6630,0x663b,0x661e,0x6637,0x6638,UBOGON,0x670e,UBOGON,UBOGON,0x67e8,
+ 0x67d6,UBOGON,0x67c7,0x67bc,0x6852,0x67bf,0x67d5,0x67fe,0x8363,0x67fb,
+ UBOGON,0x67b1,0x6801,0x6805,0x6800,0x67d7,UBOGON,0x6b2a,0x6b6b,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x6be1,UBOGON,UBOGON,0x6d23,0x6cff,0x6d14,0x6d05,
+ 0x6d13,0x6d06,0x6d21,UBOGON,0x6d15,0x6caf,0x6cf4,0x6d02,0x6d45,UBOGON,
+ 0x6d26,UBOGON,0x6d44,UBOGON,0x6d24,0x70a5,UBOGON,0x70a3,UBOGON,0x70a2,
+ 0x70bb,0x70a0,0x70aa,UBOGON,UBOGON,0x70a8,0x70b6,0x70b2,0x70a7,UBOGON,
+ UBOGON,0x70b9,0x722e,UBOGON,0x723c,UBOGON,0x726d,UBOGON,UBOGON,0x72e7,
+ 0x72ed,UBOGON,0x72ec,0x72e5,0x72e2,UBOGON,0x73c4,0x73bd,0x73cf,0x73c9,
+ 0x73c1,0x73d0,UBOGON,0x73ce
+ },
+ { /* ku 0e */
+ 0x74ed,0x74eb,UBOGON,0x74ef,0x7549,0x7550,0x7546,0x754a,UBOGON,0x754d,
+ 0x75a6,UBOGON,UBOGON,UBOGON,0x75a8,UBOGON,UBOGON,0x76c7,0x76ff,UBOGON,
+ 0x76fd,0x77e6,0x780a,UBOGON,0x7804,0x780b,0x7807,UBOGON,0x7815,0x7808,
+ UBOGON,0x79d3,0x79d4,0x79d0,0x79d7,0x7a7c,UBOGON,UBOGON,0x7a7d,0x7a83,
+ 0x7a82,UBOGON,0x7ad4,0x7ad5,0x7ad3,0x7ad0,0x7ad2,0x7afe,0x7afc,0x7c77,
+ 0x7c7c,0x7c7b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x7f8f,0x80d3,UBOGON,0x80cb,0x80d2,UBOGON,0x8109,0x80e2,0x80df,0x80c6,
+ UBOGON,0x8224,0x82f7,0x82d8,0x82dd,UBOGON,UBOGON,0x82f8,0x82fc,UBOGON,
+ UBOGON,0x82e9,UBOGON,0x82ee,UBOGON,0x82d0,0x830e,0x82e2,0x830b,0x82fd,
+ 0x5179,0x8676,UBOGON,0x8678
+ },
+ { /* ku 0f */
+ UBOGON,UBOGON,0x8675,0x867d,UBOGON,0x8842,0x8866,UBOGON,0x898c,0x8a05,
+ UBOGON,0x8a06,UBOGON,0x8c9f,UBOGON,0x8ff1,0x8fe7,0x8fe9,0x8fef,0x90c2,
+ 0x90bc,UBOGON,0x90c6,0x90c0,UBOGON,UBOGON,0x90cd,0x90c9,UBOGON,0x90c4,
+ UBOGON,0x9581,UBOGON,0x9cec,0x5032,0x4ff9,0x501d,0x4fff,0x5004,0x4ff0,
+ 0x5003,UBOGON,0x5002,0x4ffc,0x4ff2,0x5024,0x5008,0x5036,0x502e,UBOGON,
+ 0x5010,0x5038,0x5039,0x4ffd,0x5056,0x4ffb,0x51a3,0x51a6,0x51a1,UBOGON,
+ UBOGON,0x51c7,0x51c9,0x5260,0x5264,0x5259,0x5265,0x5267,0x5257,0x5263,
+ UBOGON,0x5253,UBOGON,0x52cf,UBOGON,0x52ce,0x52d0,0x52d1,0x52cc,UBOGON,
+ UBOGON,UBOGON,0x550d,0x54f4,UBOGON,0x5513,0x54ef,0x54f5,0x54f9,0x5502,
+ 0x5500,UBOGON,UBOGON,0x5518
+ },
+ { /* ku 10 */
+ 0x54f0,0x54f6,UBOGON,UBOGON,0x5519,UBOGON,0x5705,0x57c9,UBOGON,0x57b7,
+ 0x57cd,UBOGON,UBOGON,UBOGON,0x57be,0x57bb,UBOGON,0x57db,0x57c8,0x57c4,
+ 0x57c5,0x57d1,0x57ca,0x57c0,UBOGON,UBOGON,0x5a21,0x5a2a,UBOGON,0x5a1d,
+ UBOGON,0x5a0b,UBOGON,UBOGON,UBOGON,UBOGON,0x5a22,UBOGON,UBOGON,0x5a24,
+ UBOGON,0x5a14,0x5a31,UBOGON,0x5a2f,0x5a1a,0x5a12,UBOGON,UBOGON,0x5a26,
+ UBOGON,UBOGON,0x5bbc,0x5bbb,0x5bb7,0x5c05,0x5c06,0x5c52,0x5c53,UBOGON,
+ UBOGON,0x5cfa,0x5ceb,UBOGON,0x5cf3,0x5cf5,0x5ce9,0x5cef,UBOGON,0x5e2a,
+ 0x5e30,0x5e2e,0x5e2c,0x5e2f,0x5eaf,0x5ea9,UBOGON,0x5efd,0x5f32,0x5f8e,
+ 0x5f93,0x5f8f,0x604f,0x6099,UBOGON,0x607e,UBOGON,0x6074,0x604b,0x6073,
+ 0x6075,UBOGON,UBOGON,0x6056
+ },
+ { /* ku 11 */
+ 0x60a9,0x608b,0x60a6,UBOGON,0x6093,0x60ae,0x609e,0x60a7,0x6245,UBOGON,
+ UBOGON,0x632e,UBOGON,0x6352,0x6330,0x635b,UBOGON,0x6319,0x631b,UBOGON,
+ 0x6331,0x635d,0x6337,0x6335,0x6353,UBOGON,0x635c,0x633f,0x654b,UBOGON,
+ UBOGON,0x658b,UBOGON,0x659a,0x6650,0x6646,0x664e,0x6640,UBOGON,0x664b,
+ 0x6648,UBOGON,0x6660,0x6644,0x664d,UBOGON,0x6837,0x6824,UBOGON,UBOGON,
+ 0x681b,0x6836,UBOGON,0x682c,0x6819,0x6856,0x6847,0x683e,0x681e,UBOGON,
+ 0x6815,0x6822,0x6827,0x6859,0x6858,0x6855,0x6830,0x6823,0x6b2e,0x6b2b,
+ 0x6b30,0x6b6c,UBOGON,0x6b8b,UBOGON,0x6be9,0x6bea,0x6be5,0x6d6b,UBOGON,
+ UBOGON,0x6d73,0x6d57,UBOGON,UBOGON,0x6d5d,0x6d56,0x6d8f,0x6d5b,0x6d1c,
+ 0x6d9a,0x6d9b,0x6d99,UBOGON
+ },
+ { /* ku 12 */
+ 0x6d81,0x6d71,UBOGON,UBOGON,0x6d72,0x6d5c,0x6d96,0x70c4,0x70db,0x70cc,
+ 0x70d0,0x70e3,0x70df,UBOGON,0x70d6,0x70ee,0x70d5,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x727a,UBOGON,0x72f5,0x7302,UBOGON,UBOGON,0x73e2,0x73ec,0x73d5,
+ 0x73f9,0x73df,0x73e6,UBOGON,UBOGON,UBOGON,UBOGON,0x73e4,0x73e1,0x74f3,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x7556,0x7555,0x7558,0x7557,0x755e,0x75c3,
+ UBOGON,UBOGON,0x75b4,UBOGON,0x75b1,UBOGON,UBOGON,0x76cb,0x76cc,0x772a,
+ UBOGON,0x7716,0x770f,UBOGON,UBOGON,0x773f,0x772b,0x770e,0x7724,UBOGON,
+ 0x7721,0x7718,0x77dd,UBOGON,UBOGON,0x7824,0x7836,UBOGON,0x7958,0x7959,
+ UBOGON,0x7962,0x79da,0x79d9,UBOGON,0x79e1,0x79e5,0x79e8,0x79db,UBOGON,
+ 0x79e2,0x79f0,UBOGON,UBOGON
+ },
+ { /* ku 13 */
+ UBOGON,UBOGON,0x7ada,0x7add,UBOGON,0x7adb,0x7adc,UBOGON,UBOGON,0x7b0d,
+ 0x7b0b,0x7b14,0x7c8e,0x7c86,UBOGON,0x7c87,0x7c83,0x7c8b,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x7d24,UBOGON,UBOGON,UBOGON,0x7d25,0x7f62,0x7f93,0x7f99,
+ 0x7f97,UBOGON,UBOGON,0x7fc4,0x7fc6,0x800a,UBOGON,UBOGON,0x8040,0x803c,
+ 0x803b,0x80f6,0x80ff,0x80ee,0x8104,0x8103,0x8107,UBOGON,UBOGON,0x80f7,
+ UBOGON,UBOGON,0x822d,UBOGON,0x8227,0x8229,0x831f,0x8357,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x8321,UBOGON,UBOGON,0x8318,0x8358,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x8684,0x869f,0x869b,0x8689,0x86a6,0x8692,0x868f,0x86a0,
+ 0x884f,0x8878,0x887a,0x886e,0x887b,0x8884,0x8873,UBOGON,UBOGON,0x8a0d,
+ 0x8a0b,0x8a19,UBOGON,UBOGON
+ },
+ { /* ku 14 */
+ UBOGON,UBOGON,UBOGON,UBOGON,0x8ff9,0x9009,0x9008,UBOGON,0x90de,0x9151,
+ UBOGON,UBOGON,0x91db,0x91df,0x91de,0x91d6,0x91e0,0x9585,0x9660,0x9659,
+ UBOGON,0x9656,UBOGON,UBOGON,0x96bd,UBOGON,UBOGON,0x5042,0x5059,UBOGON,
+ 0x5044,0x5066,0x5052,0x5054,0x5071,0x5050,0x507b,0x507c,0x5058,UBOGON,
+ UBOGON,0x5079,0x506c,0x5078,0x51a8,0x51d1,0x51cf,0x5268,0x5276,0x52d4,
+ UBOGON,0x53a0,0x53c4,UBOGON,0x5558,0x554c,0x5568,UBOGON,0x5549,UBOGON,
+ UBOGON,0x555d,0x5529,UBOGON,0x5554,0x5553,UBOGON,0x555a,UBOGON,0x553a,
+ 0x553f,0x552b,0x57ea,UBOGON,0x57ef,UBOGON,UBOGON,0x57dd,0x57fe,UBOGON,
+ 0x57de,0x57e6,UBOGON,0x57e8,0x57ff,0x5803,0x58f7,0x68a6,0x591f,UBOGON,
+ 0x595b,0x595d,0x595e,UBOGON
+ },
+ { /* ku 15 */
+ UBOGON,0x5a2b,UBOGON,0x5a3b,UBOGON,UBOGON,0x5a61,0x5a3a,0x5a6e,0x5a4b,
+ 0x5a6b,UBOGON,UBOGON,0x5a45,0x5a4e,0x5a68,0x5a3d,0x5a71,0x5a3f,0x5a6f,
+ 0x5a75,UBOGON,0x5a73,0x5a2c,0x5a59,0x5a54,0x5a4f,0x5a63,UBOGON,UBOGON,
+ 0x5bc8,UBOGON,0x5bc3,UBOGON,0x5c5b,0x5c61,UBOGON,0x5d21,0x5d0a,0x5d09,
+ UBOGON,0x5d2c,0x5d08,UBOGON,UBOGON,0x5d2a,0x5d15,UBOGON,0x5d10,0x5d13,
+ UBOGON,0x5d2f,0x5d18,UBOGON,0x5de3,0x5e39,0x5e35,0x5e3a,0x5e32,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x5ebb,0x5eba,0x5f34,0x5f39,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x6098,UBOGON,0x60d0,UBOGON,UBOGON,UBOGON,0x60d7,0x60aa,UBOGON,
+ 0x60a1,0x60a4,UBOGON,0x60ee,UBOGON,0x60e7,UBOGON,UBOGON,0x60de,UBOGON,
+ UBOGON,0x637e,0x638b,UBOGON
+ },
+ { /* ku 16 */
+ UBOGON,0x6379,0x6386,0x6393,UBOGON,0x6373,0x636a,UBOGON,0x636c,UBOGON,
+ 0x637f,UBOGON,0x63b2,0x63ba,UBOGON,UBOGON,0x6366,0x6374,UBOGON,0x655a,
+ UBOGON,0x654e,0x654d,0x658d,0x658e,0x65ad,UBOGON,0x65c7,0x65ca,UBOGON,
+ 0x65c9,UBOGON,0x65e3,0x6657,UBOGON,0x6663,0x6667,0x671a,0x6719,0x6716,
+ UBOGON,UBOGON,0x689e,0x68b6,0x6898,0x6873,UBOGON,0x689a,0x688e,0x68b7,
+ 0x68db,0x68a5,0x686c,0x68c1,0x6884,UBOGON,UBOGON,0x6895,0x687a,0x6899,
+ UBOGON,0x68b8,0x68b9,0x6870,UBOGON,0x6b35,UBOGON,0x6b90,0x6bbb,0x6bed,
+ UBOGON,UBOGON,UBOGON,0x6dc1,0x6dc3,0x6dce,UBOGON,UBOGON,0x6dad,0x6e04,
+ UBOGON,0x6db9,UBOGON,0x6de7,UBOGON,0x6e08,0x6e06,UBOGON,0x6e0a,0x6db0,
+ UBOGON,0x6df8,0x6e0c,UBOGON
+ },
+ { /* ku 17 */
+ 0x6db1,UBOGON,0x6e02,0x6e07,0x6e09,0x6e01,0x6e17,0x6dff,0x6e12,UBOGON,
+ UBOGON,0x7103,0x7107,0x7101,0x70f5,0x70f1,0x7108,0x70f2,0x710f,UBOGON,
+ 0x70fe,UBOGON,UBOGON,UBOGON,0x731a,0x7310,0x730e,0x7402,0x73f3,UBOGON,
+ UBOGON,0x73fb,UBOGON,UBOGON,UBOGON,0x751b,0x7523,0x7561,0x7568,UBOGON,
+ 0x7567,0x75d3,UBOGON,UBOGON,0x7690,UBOGON,UBOGON,0x76d5,0x76d7,0x76d6,
+ 0x7730,UBOGON,0x7726,UBOGON,0x7740,UBOGON,0x771e,UBOGON,UBOGON,UBOGON,
+ 0x7847,UBOGON,0x784b,0x7851,0x784f,0x7842,0x7846,UBOGON,0x796e,0x796c,
+ 0x79f2,UBOGON,0x79f1,0x79f5,0x79f3,0x79f9,UBOGON,UBOGON,UBOGON,0x7a9a,
+ 0x7a93,0x7a91,0x7ae1,UBOGON,UBOGON,0x7b21,0x7b1c,0x7b16,0x7b17,0x7b36,
+ 0x7b1f,UBOGON,0x7c93,0x7c99
+ },
+ { /* ku 18 */
+ 0x7c9a,0x7c9c,UBOGON,0x7d49,UBOGON,0x7d34,0x7d37,UBOGON,0x7d2d,UBOGON,
+ 0x7d4c,UBOGON,UBOGON,0x7d48,UBOGON,UBOGON,0x7f3b,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x8008,0x801a,UBOGON,0x801d,UBOGON,0x8049,0x8045,0x8044,0x7c9b,
+ UBOGON,UBOGON,0x812a,0x812e,UBOGON,UBOGON,0x8131,UBOGON,0x811a,0x8134,
+ 0x8117,UBOGON,UBOGON,UBOGON,0x831d,0x8371,0x8384,0x8380,0x8372,0x83a1,
+ UBOGON,0x8379,0x8391,UBOGON,0x839f,0x83ad,UBOGON,UBOGON,0x8323,UBOGON,
+ 0x8385,0x839c,0x83b7,0x8658,0x865a,UBOGON,0x8657,0x86b2,UBOGON,0x86ae,
+ UBOGON,UBOGON,UBOGON,0x8845,0x889c,0x8894,0x88a3,0x888f,0x88a5,0x88a9,
+ 0x88a6,0x888a,0x88a0,0x8890,0x8992,0x8991,0x8994,UBOGON,0x8a26,0x8a32,
+ 0x8a28,UBOGON,UBOGON,0x8a1c
+ },
+ { /* ku 19 */
+ UBOGON,0x8a2b,0x8a20,UBOGON,0x8a29,UBOGON,UBOGON,UBOGON,0x8a21,0x8c3a,
+ UBOGON,0x8c5b,0x8c58,0x8c7c,UBOGON,0x8ca6,0x8cae,0x8cad,0x8d65,UBOGON,
+ 0x8d7e,UBOGON,0x8d7c,0x8d7f,0x8d7a,0x8dbd,UBOGON,UBOGON,0x8dc0,0x8dbb,
+ 0x8ead,0x8eaf,0x8ed6,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8ed9,UBOGON,
+ UBOGON,0x9012,0x900e,0x9025,UBOGON,0x9013,0x90ee,UBOGON,0x90ab,0x90f7,
+ UBOGON,0x9159,0x9154,0x91f2,0x91f0,0x91e5,0x91f6,UBOGON,UBOGON,0x9587,
+ UBOGON,0x965a,UBOGON,UBOGON,0x966e,UBOGON,UBOGON,UBOGON,0x9679,UBOGON,
+ 0x98e1,0x98e6,UBOGON,0x9ec4,0x9ed2,0x4e80,UBOGON,0x4e81,0x508f,0x5097,
+ 0x5088,0x5089,UBOGON,UBOGON,0x5081,0x5160,UBOGON,UBOGON,0x5e42,0x51d3,
+ UBOGON,UBOGON,0x51d2,0x51d6
+ },
+ { /* ku 1a */
+ 0x5273,UBOGON,0x5270,UBOGON,UBOGON,UBOGON,0x53a8,0x53a6,0x53c5,0x5597,
+ 0x55de,UBOGON,UBOGON,0x5596,0x55b4,UBOGON,0x5585,UBOGON,0x559b,0x55a0,
+ UBOGON,0x5559,UBOGON,0x5586,UBOGON,UBOGON,0x55af,0x557a,UBOGON,UBOGON,
+ UBOGON,0x559e,UBOGON,0x55a9,0x570f,0x570e,0x581a,UBOGON,0x581f,UBOGON,
+ 0x583c,0x5818,0x583e,0x5826,UBOGON,0x583a,UBOGON,0x5822,UBOGON,0x58fb,
+ 0x5963,0x5964,UBOGON,0x5aa8,0x5aa3,0x5a82,0x5a88,0x5aa1,0x5a85,0x5a98,
+ UBOGON,0x5a99,UBOGON,0x5a89,0x5a81,0x5a96,0x5a80,UBOGON,UBOGON,0x5a91,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x5acf,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x5a87,0x5aa0,UBOGON,0x5a79,UBOGON,0x5a86,0x5aab,0x5aaa,0x5aa4,
+ 0x5a8d,0x5a7e,UBOGON,0x5bd5
+ },
+ { /* ku 1b */
+ UBOGON,UBOGON,UBOGON,0x5c1e,0x5c5f,0x5c5e,0x5d44,0x5d3e,UBOGON,0x5d48,
+ 0x5d1c,UBOGON,0x5d5b,0x5d4d,UBOGON,UBOGON,0x5d57,UBOGON,0x5d53,0x5d4f,
+ UBOGON,0x5d3b,0x5d46,UBOGON,UBOGON,0x5e46,0x5e47,UBOGON,0x5e48,0x5ec0,
+ 0x5ebd,0x5ebf,UBOGON,0x5f11,UBOGON,0x5f3e,0x5f3b,UBOGON,0x5f3a,UBOGON,
+ UBOGON,UBOGON,0x5fa7,UBOGON,0x60ea,UBOGON,0x6107,0x6122,0x610c,UBOGON,
+ UBOGON,0x60b3,0x60d6,0x60d2,UBOGON,0x60e3,0x60e5,0x60e9,UBOGON,UBOGON,
+ 0x6111,0x60fd,UBOGON,UBOGON,0x611e,0x6120,0x6121,0x621e,UBOGON,0x63e2,
+ 0x63de,0x63e6,UBOGON,UBOGON,UBOGON,UBOGON,0x63f8,UBOGON,0x63fe,0x63c1,
+ 0x63bf,0x63f7,0x63d1,0x655f,0x6560,0x6561,UBOGON,UBOGON,0x65d1,UBOGON,
+ UBOGON,0x667d,0x666b,0x667f
+ },
+ { /* ku 1c */
+ UBOGON,UBOGON,0x6673,0x6681,0x666d,0x6669,UBOGON,UBOGON,0x671e,0x68ed,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x6903,UBOGON,0x68fe,0x68e5,0x691e,0x6902,
+ UBOGON,UBOGON,0x6909,0x68ca,0x6900,UBOGON,0x6901,0x6918,0x68e2,0x68cf,
+ UBOGON,0x692e,0x68c5,0x68ff,UBOGON,0x691c,0x68c3,UBOGON,0x6b6f,UBOGON,
+ 0x6b6e,UBOGON,0x6bbe,UBOGON,0x6bf4,0x6c2d,UBOGON,0x6db6,0x6e75,0x6e1e,
+ UBOGON,0x6e18,UBOGON,0x6e48,UBOGON,0x6e4f,UBOGON,0x6e42,0x6e6a,0x6e70,
+ 0x6dfe,UBOGON,UBOGON,0x6e6d,UBOGON,0x6e7b,0x6e7e,0x6e59,UBOGON,0x6e57,
+ UBOGON,0x6e80,0x6e50,UBOGON,0x6e29,0x6e76,0x6e2a,0x6e4c,0x712a,UBOGON,
+ 0x7135,0x712c,0x7137,0x711d,UBOGON,UBOGON,0x7138,UBOGON,0x7134,0x712b,
+ 0x7133,0x7127,0x7124,UBOGON
+ },
+ { /* ku 1d */
+ 0x712d,0x7232,0x7283,0x7282,0x7287,0x7306,0x7324,0x7338,0x732a,0x732c,
+ 0x732b,UBOGON,0x732f,0x7328,0x7417,UBOGON,UBOGON,0x7419,0x7438,UBOGON,
+ 0x741f,0x7414,0x743c,0x73f7,0x741c,0x7415,0x7418,0x7439,0x74f9,0x7524,
+ UBOGON,UBOGON,UBOGON,0x756e,0x756d,0x7571,0x758e,UBOGON,0x75e5,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x7694,0x76b3,UBOGON,0x76d9,UBOGON,0x7748,0x7749,
+ 0x7743,UBOGON,UBOGON,0x7742,0x77df,UBOGON,0x7863,0x7876,UBOGON,0x785f,
+ 0x7866,0x7966,0x7971,UBOGON,UBOGON,0x7976,0x7984,0x7975,0x79ff,0x7a07,
+ UBOGON,0x7a0e,0x7a09,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7ae7,
+ 0x7ae2,0x7b55,UBOGON,UBOGON,0x7b43,0x7b57,0x7b6c,0x7b42,0x7b53,UBOGON,
+ 0x7b41,UBOGON,UBOGON,0x7ca7
+ },
+ { /* ku 1e */
+ 0x7ca0,0x7ca6,0x7ca4,0x7d74,UBOGON,0x7d59,UBOGON,0x7d60,0x7d57,0x7d6c,
+ 0x7d7e,0x7d64,UBOGON,0x7d5a,0x7d5d,UBOGON,UBOGON,UBOGON,0x7d76,0x7d4d,
+ 0x7d75,UBOGON,0x7fd3,0x7fd6,UBOGON,UBOGON,0x8060,0x804e,0x8145,0x813b,
+ UBOGON,0x8148,0x8142,0x8149,0x8140,0x8114,0x8141,UBOGON,0x81ef,0x81f6,
+ 0x8203,UBOGON,0x83ed,UBOGON,0x83da,0x8418,0x83d2,0x8408,UBOGON,0x8400,
+ UBOGON,UBOGON,UBOGON,0x8417,0x8346,0x8414,0x83d3,0x8405,0x841f,0x8402,
+ 0x8416,0x83cd,0x83e6,UBOGON,0x865d,0x86d5,0x86e1,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x86ee,0x8847,0x8846,UBOGON,UBOGON,0x88bb,UBOGON,0x88bf,0x88b4,
+ UBOGON,0x88b5,UBOGON,0x899a,0x8a43,UBOGON,UBOGON,0x8a5a,UBOGON,UBOGON,
+ UBOGON,0x8a35,0x8a38,0x8a42
+ },
+ { /* ku 1f */
+ 0x8a49,0x8a5d,0x8a4b,0x8a3d,UBOGON,UBOGON,UBOGON,UBOGON,0x8c60,0x8c5e,
+ 0x8c7f,0x8c7e,0x8c83,UBOGON,0x8cb1,0x8d87,UBOGON,UBOGON,0x8d88,0x8d83,
+ UBOGON,UBOGON,0x8d86,0x8d8b,0x8d82,0x8dca,0x8dd2,UBOGON,UBOGON,0x8dd4,
+ 0x8dc9,0x8eb0,UBOGON,UBOGON,UBOGON,0x8ef2,0x8ee4,0x8ef3,0x8eea,UBOGON,
+ 0x8efd,UBOGON,0x8f9d,0x902b,0x902a,UBOGON,0x9028,0x9029,0x902c,UBOGON,
+ UBOGON,0x903a,0x9030,0x9037,0x903b,UBOGON,0x910a,UBOGON,UBOGON,UBOGON,
+ 0x91fe,0x9220,UBOGON,0x920b,UBOGON,0x9218,0x9222,UBOGON,0x921b,0x9208,
+ UBOGON,0x920e,0x9213,UBOGON,UBOGON,0x9595,UBOGON,UBOGON,UBOGON,0x968c,
+ 0x967b,0x967f,0x9681,UBOGON,0x9682,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x96ee,0x96ed,UBOGON,0x96ec
+ },
+ { /* ku 20 */
+ 0x975f,0x976f,UBOGON,0x976d,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x98f0,UBOGON,UBOGON,UBOGON,0x9aa9,UBOGON,UBOGON,0x9ae0,0x4eb7,UBOGON,
+ UBOGON,0x50cc,0x50bc,UBOGON,0x50aa,0x50b9,UBOGON,0x50ab,0x50c3,0x50cd,
+ 0x517e,0x527e,0x5279,UBOGON,UBOGON,0x52e1,0x52e0,0x52e7,0x5380,0x53ab,
+ 0x53aa,0x53a9,0x53e0,0x55ea,UBOGON,0x55d7,UBOGON,UBOGON,0x55c1,0x5715,
+ UBOGON,0x586c,UBOGON,0x585c,0x5850,0x5861,0x586a,0x5869,0x5856,0x5860,
+ 0x5866,0x585f,0x5923,0x5966,0x5968,UBOGON,UBOGON,0x5ace,UBOGON,0x5ac5,
+ 0x5ac3,UBOGON,UBOGON,0x5ad0,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x5b74,0x5b76,0x5bdc,0x5bd7,0x5bda,0x5bdb,UBOGON,0x5c20,0x5d6d,0x5d66,
+ UBOGON,0x5d64,0x5d6e,UBOGON
+ },
+ { /* ku 21 */
+ 0x5d60,0x5f42,0x5f5a,0x5f6e,UBOGON,UBOGON,0x6130,0x613a,0x612a,0x6143,
+ 0x6119,0x6131,UBOGON,0x613d,UBOGON,UBOGON,UBOGON,0x6408,0x6432,0x6438,
+ UBOGON,0x6431,UBOGON,0x6419,UBOGON,0x6411,UBOGON,UBOGON,0x6429,0x641d,
+ UBOGON,UBOGON,UBOGON,0x643c,UBOGON,0x6446,0x6447,UBOGON,UBOGON,0x643a,
+ 0x6407,UBOGON,0x656b,UBOGON,0x6570,0x656d,UBOGON,0x65e4,0x6693,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x668f,UBOGON,UBOGON,0x6692,UBOGON,0x668e,UBOGON,
+ 0x6946,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6931,UBOGON,
+ UBOGON,0x693e,UBOGON,0x697c,0x6943,UBOGON,0x6973,UBOGON,0x6955,UBOGON,
+ UBOGON,0x6985,0x694d,0x6950,0x6947,0x6967,0x6936,0x6964,0x6961,UBOGON,
+ 0x697d,0x6b44,0x6b40,0x6b71
+ },
+ { /* ku 22 */
+ 0x6b73,0x6b9c,UBOGON,UBOGON,UBOGON,0x6bc1,UBOGON,0x6bfa,0x6c31,0x6c32,
+ UBOGON,UBOGON,0x6eb8,0x6ea8,UBOGON,0x6e91,0x6ebb,UBOGON,0x6e9a,UBOGON,
+ UBOGON,0x6ea9,UBOGON,UBOGON,0x6eb5,0x6e6c,0x6ee8,UBOGON,0x6edd,0x6eda,
+ 0x6ee6,0x6eac,UBOGON,UBOGON,UBOGON,0x6ed9,0x6ee3,0x6ee9,0x6edb,UBOGON,
+ 0x716f,UBOGON,UBOGON,0x7148,UBOGON,0x714a,0x716b,UBOGON,0x714f,0x7157,
+ 0x7174,UBOGON,UBOGON,UBOGON,0x7145,0x7151,0x716d,UBOGON,0x7251,0x7250,
+ 0x724e,UBOGON,0x7341,UBOGON,0x732e,0x7346,UBOGON,0x7427,UBOGON,0x7448,
+ 0x7453,0x743d,UBOGON,0x745d,0x7456,UBOGON,0x741e,0x7447,0x7443,0x7458,
+ 0x7449,UBOGON,0x744c,0x7445,0x743e,UBOGON,0x7501,0x751e,UBOGON,UBOGON,
+ 0x757a,0x75ee,0x7602,0x7697
+ },
+ { /* ku 23 */
+ 0x7698,UBOGON,UBOGON,UBOGON,0x775d,0x7764,0x7753,0x7758,0x7882,0x7890,
+ 0x788a,UBOGON,0x787a,0x787d,UBOGON,0x788b,0x7878,UBOGON,UBOGON,0x788d,
+ 0x7888,0x7892,0x7881,0x797e,0x7983,UBOGON,UBOGON,UBOGON,0x7980,UBOGON,
+ UBOGON,UBOGON,0x7a0f,UBOGON,UBOGON,0x7a1d,UBOGON,0x7aa1,0x7aa4,UBOGON,
+ 0x7ae9,0x7aea,UBOGON,0x7b62,0x7b6b,UBOGON,0x7b5e,UBOGON,0x7b79,UBOGON,
+ UBOGON,0x7b6f,0x7b68,UBOGON,UBOGON,0x7cae,UBOGON,UBOGON,UBOGON,0x7cb0,
+ UBOGON,0x7d90,UBOGON,0x7d8a,UBOGON,0x7d8b,0x7d99,0x7d95,UBOGON,0x7d87,
+ 0x7d78,0x7d97,0x7d89,0x7d98,UBOGON,UBOGON,UBOGON,0x7fa3,UBOGON,UBOGON,
+ UBOGON,0x7fdd,0x8057,UBOGON,0x8163,0x816a,0x816c,UBOGON,UBOGON,UBOGON,
+ 0x815d,0x8175,UBOGON,0x815f
+ },
+ { /* ku 24 */
+ UBOGON,0x817d,0x816d,UBOGON,UBOGON,0x8241,0x844f,0x8484,UBOGON,0x847f,
+ UBOGON,0x8448,0x842a,0x847b,0x8472,0x8464,0x842e,0x845c,0x8453,UBOGON,
+ 0x8441,0x84c8,UBOGON,0x8462,0x8480,0x843e,0x8483,0x8471,UBOGON,0x844a,
+ 0x8455,0x8458,UBOGON,UBOGON,UBOGON,0x86fc,0x86fd,0x8715,UBOGON,0x8716,
+ 0x86ff,UBOGON,UBOGON,UBOGON,0x8858,0x88cf,0x88e0,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x89e7,0x8a6a,0x8a80,UBOGON,0x8a6f,0x8a65,UBOGON,0x8a78,0x8a7d,
+ 0x8a88,UBOGON,UBOGON,0x8a64,0x8a7e,UBOGON,0x8a67,0x8c63,0x8c88,UBOGON,
+ 0x8ccd,UBOGON,0x8cc9,UBOGON,0x8ded,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x8eb1,UBOGON,UBOGON,0x8f04,0x8f9e,0x8fa0,0x9043,0x9046,
+ 0x9048,0x9045,0x9040,0x904c
+ },
+ { /* ku 25 */
+ UBOGON,UBOGON,0x910c,0x9113,0x9115,UBOGON,0x916b,0x9167,0x925d,0x9255,
+ 0x9235,UBOGON,0x9259,0x922f,0x923c,0x928f,0x925c,0x926a,0x9262,0x925f,
+ 0x926b,0x926e,0x923b,0x9244,0x9241,0x959a,UBOGON,0x9599,UBOGON,UBOGON,
+ UBOGON,0x968f,UBOGON,0x9696,UBOGON,UBOGON,UBOGON,0x96f4,0x96fc,UBOGON,
+ 0x9755,UBOGON,0x9779,UBOGON,UBOGON,UBOGON,0x97ee,0x97f5,UBOGON,0x980b,
+ UBOGON,0x98f3,UBOGON,UBOGON,0x98f7,0x98ff,0x98f5,UBOGON,0x98ec,0x98f1,
+ UBOGON,UBOGON,0x999a,UBOGON,0x9ae2,0x9b3d,0x9b5d,0x9ce8,UBOGON,0x9ceb,
+ 0x9cef,0x9cee,0x9e81,0x9f14,0x50d0,0x50d9,0x50dc,0x50d8,UBOGON,0x50e1,
+ 0x50eb,UBOGON,UBOGON,0x50f4,0x50e2,0x50de,UBOGON,UBOGON,UBOGON,0x51f4,
+ UBOGON,UBOGON,UBOGON,0x52ed
+ },
+ { /* ku 26 */
+ 0x52ea,UBOGON,0x5332,UBOGON,0x53ae,0x53b0,UBOGON,0x55fb,0x5603,0x560b,
+ UBOGON,0x5607,UBOGON,0x55f8,UBOGON,0x5628,0x561e,UBOGON,0x5618,0x5611,
+ 0x5651,0x5605,0x5717,0x5892,UBOGON,0x588c,UBOGON,0x5878,0x5884,0x5873,
+ 0x58ad,0x5897,0x5895,0x5877,0x5872,0x5896,0x588d,0x5910,UBOGON,0x596c,
+ UBOGON,0x5ae7,UBOGON,0x5ae4,UBOGON,UBOGON,0x5aef,0x5626,UBOGON,UBOGON,
+ 0x5af0,0x5d7b,UBOGON,0x5d83,UBOGON,UBOGON,0x5d8b,0x5d8c,UBOGON,0x5d78,
+ 0x5e52,UBOGON,UBOGON,0x5ed0,0x5ecf,UBOGON,0x5fb3,0x5fb4,UBOGON,UBOGON,
+ UBOGON,0x617b,UBOGON,0x616f,0x6181,0x613c,0x6142,0x6138,0x6133,UBOGON,
+ 0x6160,0x6169,0x617d,0x6186,0x622c,0x6228,UBOGON,0x644c,UBOGON,0x6457,
+ 0x647c,UBOGON,UBOGON,0x6455
+ },
+ { /* ku 27 */
+ 0x6462,0x6471,0x646a,0x6456,0x643b,0x6481,UBOGON,0x644f,0x647e,0x6464,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6571,UBOGON,UBOGON,0x66a5,0x669a,
+ 0x669c,UBOGON,0x66a6,UBOGON,0x66a4,0x698f,0x69c5,0x69c8,0x6992,0x69b2,
+ UBOGON,UBOGON,UBOGON,0x69e3,0x69c0,0x69d6,0x69d1,0x699f,0x69a2,0x69d2,
+ UBOGON,UBOGON,UBOGON,0x69e1,0x69d5,0x699d,UBOGON,UBOGON,0x6998,UBOGON,
+ 0x6b74,0x6ba1,UBOGON,0x6ef0,0x6ef3,UBOGON,UBOGON,0x6f1b,0x6f0c,0x6f1d,
+ 0x6f34,0x6f28,0x6f17,UBOGON,0x6f44,0x6f42,0x6f04,0x6f11,0x6efa,0x6f4a,
+ 0x7191,0x718e,UBOGON,0x718b,0x718d,0x717f,0x718c,0x717e,0x717c,0x7183,
+ UBOGON,0x7188,UBOGON,UBOGON,0x7294,UBOGON,0x7355,0x7353,0x734f,0x7354,
+ 0x746c,0x7465,0x7466,0x7461
+ },
+ { /* ku 28 */
+ 0x746b,0x7468,0x7476,UBOGON,0x7460,UBOGON,0x7474,0x7506,0x760e,UBOGON,
+ 0x7607,UBOGON,UBOGON,0x76b9,UBOGON,0x76b7,0x76e2,UBOGON,0x7774,0x7777,
+ 0x7776,0x7775,UBOGON,0x7778,0x7771,UBOGON,0x777a,0x715b,0x777b,0x78a6,
+ 0x78ae,0x78b8,UBOGON,UBOGON,UBOGON,0x78b1,0x78af,UBOGON,0x7989,0x7987,
+ UBOGON,UBOGON,0x7a29,UBOGON,0x7a2a,UBOGON,0x7a2d,0x7a2c,UBOGON,0x7a32,
+ UBOGON,0x7aec,0x7af0,0x7b81,0x7b9e,0x7b83,UBOGON,0x7b92,UBOGON,0x7ba3,
+ 0x7b9f,0x7b93,UBOGON,0x7b86,0x7cb8,0x7cb7,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x7dc8,0x7db6,UBOGON,0x7dd1,UBOGON,0x7da8,0x7dab,UBOGON,0x7db3,
+ 0x7dcd,UBOGON,0x7dcf,0x7da4,UBOGON,UBOGON,0x7f41,0x7f6f,0x7f71,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 29 */
+ UBOGON,0x8023,0x805b,UBOGON,0x8061,0x805f,0x8181,UBOGON,UBOGON,0x8184,
+ 0x8213,UBOGON,0x824a,0x824c,UBOGON,UBOGON,UBOGON,0x84bd,0x8495,UBOGON,
+ 0x8492,0x84c3,UBOGON,0x8496,0x84a5,0x84b5,0x84b3,0x84a3,0x84e4,0x84d8,
+ 0x84d5,UBOGON,0x84b7,0x84ad,0x84da,0x8493,0x8736,UBOGON,UBOGON,UBOGON,
+ 0x873d,0x872b,0x8747,0x8739,UBOGON,0x8745,0x871d,UBOGON,0x88ff,0x88ea,
+ UBOGON,0x88f5,UBOGON,0x8900,0x88ed,0x8903,0x88e9,UBOGON,UBOGON,0x89ea,
+ UBOGON,0x8a9b,0x8a8e,0x8aa2,UBOGON,0x8a9c,0x8a94,0x8a90,0x8aa9,0x8aac,
+ UBOGON,0x8a9f,UBOGON,UBOGON,0x8a9d,UBOGON,0x8c67,UBOGON,UBOGON,0x8cd0,
+ 0x8cd6,0x8cd4,0x8d98,0x8d9a,0x8d97,UBOGON,UBOGON,UBOGON,0x8e0b,0x8e08,
+ 0x8e01,0x8eb4,0x8eb3,UBOGON
+ },
+ { /* ku 2a */
+ 0x8fa1,0x8fa2,UBOGON,0x905a,UBOGON,0x9061,0x905f,UBOGON,UBOGON,0x9125,
+ 0x917b,0x9176,0x917c,UBOGON,0x9289,0x92f6,0x92b1,0x92ad,0x9292,0x9281,
+ 0x9284,UBOGON,0x92ae,0x9290,0x929e,UBOGON,UBOGON,UBOGON,0x95a2,0x95a7,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x96a0,0x969d,0x969f,0x96d0,UBOGON,
+ 0x96d1,UBOGON,UBOGON,0x9759,UBOGON,0x9764,UBOGON,UBOGON,UBOGON,0x9819,
+ UBOGON,0x9814,0x9815,0x981a,UBOGON,UBOGON,UBOGON,UBOGON,0x9906,UBOGON,
+ 0x98f8,0x9901,UBOGON,0x99be,0x99bc,0x99b7,0x99b6,0x99c0,UBOGON,0x99b8,
+ UBOGON,UBOGON,UBOGON,0x99c4,UBOGON,0x99bf,UBOGON,0x9ada,0x9ae4,0x9ae9,
+ 0x9ae8,0x9aea,0x9ae5,UBOGON,0x9b26,UBOGON,UBOGON,0x9b40,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2b */
+ UBOGON,0x9ebd,UBOGON,UBOGON,UBOGON,UBOGON,0x510e,UBOGON,0x50f7,UBOGON,
+ 0x50fc,0x510d,0x5101,0x51da,0x51d9,0x51db,0x5286,0x528e,0x52ee,0x5333,
+ 0x53b1,UBOGON,0x5647,0x562d,0x5654,UBOGON,0x564b,0x5652,0x5631,0x5644,
+ 0x5656,0x5650,0x562b,UBOGON,0x564d,0x5637,0x564f,0x58a2,0x58b7,UBOGON,
+ 0x58b2,UBOGON,0x58aa,0x58b5,0x58b0,UBOGON,0x58b4,0x58a4,0x58a7,UBOGON,
+ 0x5926,0x5afe,UBOGON,0x5b04,UBOGON,0x5afc,UBOGON,0x5b06,0x5b0a,0x5afa,
+ 0x5b0d,0x5b00,0x5b0e,UBOGON,UBOGON,UBOGON,0x5d91,UBOGON,0x5d8f,0x5d90,
+ 0x5d98,0x5da4,0x5d9b,0x5da3,0x5d96,0x5de4,0x5e5a,UBOGON,UBOGON,0x5e5e,
+ UBOGON,0x5fb8,0x6157,0x615c,0x61a6,0x6195,0x6188,UBOGON,0x61a3,0x618f,
+ UBOGON,0x6164,UBOGON,0x6159
+ },
+ { /* ku 2c */
+ 0x6178,UBOGON,0x6185,0x6187,0x619e,UBOGON,UBOGON,0x6198,0x619c,UBOGON,
+ UBOGON,0x622f,0x6480,0x649b,0x648e,0x648d,0x6494,0x64c6,UBOGON,0x64a8,
+ 0x6483,UBOGON,0x64b9,0x6486,0x64b4,0x64af,0x6491,UBOGON,0x64aa,0x64a1,
+ 0x64a7,0x66b6,0x66b3,UBOGON,0x66bc,0x66ac,UBOGON,0x66ad,0x6a0e,UBOGON,
+ 0x6a1c,0x6a1a,UBOGON,UBOGON,0x6a0b,UBOGON,0x69ef,0x6a0c,0x69f0,0x6a22,
+ UBOGON,0x69d8,UBOGON,0x6a12,0x69fa,UBOGON,0x6a2a,UBOGON,0x6a10,UBOGON,
+ UBOGON,0x6a29,0x69f9,0x69ea,0x6a2c,0x6a24,UBOGON,0x69e9,0x6b52,0x6b4f,
+ 0x6b53,UBOGON,UBOGON,0x6f10,0x6f65,0x6f75,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x6fd0,UBOGON,0x6f5c,0x6f3d,0x6f71,UBOGON,0x6f91,0x6f0b,0x6f79,0x6f81,
+ 0x6f8f,UBOGON,0x6f59,0x6f74
+ },
+ { /* ku 2d */
+ UBOGON,0x71ae,UBOGON,0x71a3,0x71ad,UBOGON,UBOGON,0x71ab,0x71a6,0x71a2,
+ UBOGON,0x52f2,0x7257,0x7255,0x7299,0x734b,0x747a,UBOGON,UBOGON,UBOGON,
+ 0x748c,0x7484,UBOGON,UBOGON,0x7482,0x7493,0x747b,UBOGON,0x7509,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x778a,UBOGON,0x7790,UBOGON,0x78c6,
+ 0x78d3,0x78c0,0x78d2,0x78c7,0x78c2,UBOGON,0x799f,0x799d,0x799e,UBOGON,
+ 0x7a41,UBOGON,0x7a38,0x7a3a,0x7a42,UBOGON,UBOGON,0x7a3e,0x7ab0,0x7bae,
+ 0x7bb3,UBOGON,UBOGON,0x7bbf,UBOGON,UBOGON,0x7bcd,UBOGON,0x7bb2,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7cc4,0x7ccd,0x7cc2,0x7cc6,
+ 0x7cc3,0x7cc9,0x7cc7,UBOGON,0x7df8,UBOGON,0x7ded,0x7de2,UBOGON,UBOGON,
+ UBOGON,0x7ddc,0x7e02,0x7e01
+ },
+ { /* ku 2e */
+ UBOGON,0x7dd6,UBOGON,0x7de4,0x7dfe,UBOGON,0x7e00,0x7dfc,0x7dfd,UBOGON,
+ 0x7df5,0x7dff,UBOGON,0x7deb,0x7de5,0x7f78,0x7fae,0x7fe7,UBOGON,0x8065,
+ 0x806a,0x8066,0x8068,0x806b,0x8194,0x81a1,0x8192,0x8196,0x8193,UBOGON,
+ UBOGON,0x8501,UBOGON,0x84f8,UBOGON,0x84f5,UBOGON,0x8504,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x851b,0x8503,0x8533,0x8534,0x84ed,UBOGON,UBOGON,0x8535,
+ UBOGON,0x8505,UBOGON,UBOGON,UBOGON,UBOGON,0x877d,UBOGON,UBOGON,UBOGON,
+ 0x8771,UBOGON,0x885c,0x88e6,0x890f,0x891b,UBOGON,0x89a9,0x89a5,0x89ee,
+ 0x8ab1,UBOGON,0x8acc,0x8ace,UBOGON,0x8ab7,UBOGON,0x8ab5,0x8ae9,0x8ab4,
+ UBOGON,0x8ab3,0x8ac1,0x8aaf,0x8aca,0x8ad0,UBOGON,UBOGON,UBOGON,0x8c8e,
+ UBOGON,UBOGON,0x8ce9,0x8cdb
+ },
+ { /* ku 2f */
+ UBOGON,0x8ceb,0x8da4,UBOGON,0x8da2,0x8d9d,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x8e2a,0x8e28,UBOGON,UBOGON,0x8eb8,0x8eb6,0x8eb9,0x8eb7,0x8f22,0x8f2b,
+ 0x8f27,0x8f19,0x8fa4,UBOGON,0x8fb3,UBOGON,0x9071,0x906a,UBOGON,UBOGON,
+ 0x9188,0x918c,0x92bf,0x92b8,0x92be,0x92dc,0x92e5,UBOGON,UBOGON,0x92d4,
+ 0x92d6,UBOGON,0x92da,0x92ed,0x92f3,0x92db,UBOGON,0x92b9,0x92e2,0x92eb,
+ 0x95af,UBOGON,0x95b2,0x95b3,UBOGON,UBOGON,UBOGON,0x96a3,0x96a5,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x970a,UBOGON,0x9787,0x9789,0x978c,0x97ef,0x982a,
+ 0x9822,UBOGON,0x981f,UBOGON,0x9919,UBOGON,0x99ca,0x99da,UBOGON,UBOGON,
+ UBOGON,0x99de,0x99c8,0x99e0,UBOGON,0x9ab6,0x9ab5,UBOGON,0x9af4,UBOGON,
+ 0x9b6b,0x9b69,0x9b72,0x9b63
+ },
+ { /* ku 30 */
+ UBOGON,0x9d0d,UBOGON,0x9d01,0x9d0c,UBOGON,0x9cf8,UBOGON,UBOGON,0x9cfe,
+ 0x9d02,0x9e84,UBOGON,0x9eab,0x9eaa,0x511d,0x5116,UBOGON,0x512b,0x511e,
+ 0x511b,0x5290,0x5294,0x5314,UBOGON,UBOGON,0x5667,UBOGON,0x567b,UBOGON,
+ 0x565f,0x5661,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x58c3,
+ 0x58ca,0x58bb,0x58c0,0x58c4,0x5901,0x5b1f,0x5b18,0x5b11,0x5b15,UBOGON,
+ 0x5b12,0x5b1c,UBOGON,0x5b22,0x5b79,0x5da6,UBOGON,0x5db3,0x5dab,0x5eea,
+ UBOGON,0x5f5b,UBOGON,UBOGON,0x61b7,0x61ce,0x61b9,0x61bd,0x61cf,0x61c0,
+ 0x6199,0x6197,UBOGON,0x61bb,0x61d0,0x61c4,0x6231,UBOGON,0x64d3,0x64c0,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x64dc,0x64d1,0x64c8,UBOGON,0x64d5,0x66c3,
+ UBOGON,UBOGON,0x66bf,0x66c5
+ },
+ { /* ku 31 */
+ UBOGON,0x66cd,0x66c1,0x6706,UBOGON,0x6724,0x6a63,0x6a42,0x6a52,UBOGON,
+ 0x6a43,0x6a33,UBOGON,0x6a6c,0x6a57,UBOGON,0x6a4c,0x6a6e,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x6a37,UBOGON,0x6a71,0x6a4a,0x6a36,UBOGON,0x6a53,
+ UBOGON,0x6a45,0x6a70,UBOGON,UBOGON,0x6a5c,0x6b58,0x6b57,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x6fbb,UBOGON,UBOGON,0x6fbe,UBOGON,UBOGON,
+ UBOGON,0x6fb5,0x6fd3,0x6f9f,UBOGON,0x6fb7,0x6ff5,0x71b7,UBOGON,0x71bb,
+ UBOGON,0x71d1,UBOGON,0x71ba,UBOGON,0x71b6,0x71cc,UBOGON,UBOGON,0x71d3,
+ 0x749b,UBOGON,UBOGON,0x7496,0x74a2,0x749d,0x750a,0x750e,UBOGON,0x7581,
+ 0x762c,0x7637,0x7636,0x763b,UBOGON,0x76a1,UBOGON,UBOGON,0x7798,UBOGON,
+ 0x7796,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 32 */
+ 0x78d6,0x78eb,UBOGON,0x78dc,UBOGON,0x79a5,0x79a9,0x9834,0x7a53,0x7a45,
+ UBOGON,0x7a4f,UBOGON,0x7abd,0x7abb,0x7af1,UBOGON,UBOGON,0x7bec,0x7bed,
+ UBOGON,UBOGON,0x7cd3,UBOGON,0x7ce1,UBOGON,0x7e19,UBOGON,UBOGON,UBOGON,
+ 0x7e27,0x7e26,UBOGON,UBOGON,0x806e,0x81af,UBOGON,UBOGON,0x81ad,UBOGON,
+ 0x81aa,0x8218,UBOGON,UBOGON,UBOGON,UBOGON,0x856f,0x854c,UBOGON,0x8542,
+ UBOGON,0x855c,0x8570,0x855f,UBOGON,0x855a,0x854b,0x853f,0x878a,UBOGON,
+ 0x878b,0x87a1,0x878e,UBOGON,UBOGON,0x8799,0x885e,0x885f,0x8924,0x89a7,
+ 0x8aea,0x8afd,0x8af9,0x8ae3,0x8ae5,UBOGON,UBOGON,0x8aec,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x8cf2,UBOGON,0x8cef,UBOGON,0x8da6,UBOGON,UBOGON,UBOGON,
+ 0x8e3b,0x8e43,UBOGON,0x8e32
+ },
+ { /* ku 33 */
+ 0x8f31,0x8f30,UBOGON,0x8f2d,0x8f3c,0x8fa7,0x8fa5,UBOGON,UBOGON,UBOGON,
+ 0x9137,0x9195,0x918e,UBOGON,0x9196,UBOGON,0x9345,0x930a,UBOGON,UBOGON,
+ 0x92fd,0x9317,0x931c,0x9307,0x9331,0x9332,0x932c,0x9330,0x9303,0x9305,
+ UBOGON,0x95c2,UBOGON,0x95b8,UBOGON,0x95c1,UBOGON,UBOGON,UBOGON,0x96ab,
+ 0x96b7,UBOGON,UBOGON,0x9715,0x9714,UBOGON,UBOGON,0x970c,0x9717,UBOGON,
+ 0x9793,UBOGON,0x97d2,UBOGON,UBOGON,0x9836,0x9831,0x9833,0x983c,0x982e,
+ 0x983a,UBOGON,0x983d,UBOGON,0x98b5,0x9922,0x9923,0x9920,0x991c,0x991d,
+ UBOGON,0x99a0,UBOGON,0x99ef,0x99e8,0x99eb,UBOGON,UBOGON,UBOGON,0x99e1,
+ 0x99e6,UBOGON,UBOGON,0x9af8,0x9af5,UBOGON,UBOGON,0x9b83,0x9b94,0x9b84,
+ UBOGON,0x9b8b,0x9b8f,UBOGON
+ },
+ { /* ku 34 */
+ 0x9b8c,UBOGON,0x9b89,UBOGON,0x9b8e,UBOGON,UBOGON,UBOGON,0x9d24,0x9d0f,
+ UBOGON,0x9d13,0x9d0a,UBOGON,UBOGON,UBOGON,UBOGON,0x9d2a,0x9d1a,UBOGON,
+ 0x9d27,0x9d16,0x9d21,UBOGON,0x9e85,0x9eac,0x9ec6,0x9ec5,0x9ed7,0x9f53,
+ UBOGON,0x5128,0x5127,0x51df,UBOGON,0x5335,0x53b3,UBOGON,0x568a,0x567d,
+ 0x5689,UBOGON,0x58cd,0x58d0,UBOGON,0x5b2b,0x5b33,0x5b29,0x5b35,0x5b31,
+ 0x5b37,0x5c36,0x5dbe,UBOGON,0x5db9,UBOGON,0x5dbb,UBOGON,0x61e2,0x61db,
+ 0x61dd,0x61dc,0x61da,UBOGON,0x61d9,UBOGON,UBOGON,0x64df,UBOGON,UBOGON,
+ 0x64e1,UBOGON,0x64ee,UBOGON,0x65b5,0x66d4,0x66d5,UBOGON,0x66d0,0x66d1,
+ 0x66ce,0x66d7,UBOGON,UBOGON,0x6a7d,0x6a8a,UBOGON,0x6aa7,UBOGON,0x6a99,
+ 0x6a82,0x6a88,UBOGON,UBOGON
+ },
+ { /* ku 35 */
+ 0x6a86,UBOGON,0x6a98,0x6a9d,UBOGON,UBOGON,0x6a8f,UBOGON,0x6aaa,UBOGON,
+ 0x6b5d,UBOGON,0x6c0a,UBOGON,0x6fd7,0x6fd6,0x6fe5,UBOGON,UBOGON,UBOGON,
+ 0x6fd9,0x6fda,0x6fea,UBOGON,0x6ff6,UBOGON,UBOGON,0x71e3,UBOGON,0x71e9,
+ UBOGON,0x71eb,0x71ef,0x71f3,0x71ea,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x7371,UBOGON,0x74ae,UBOGON,0x74b3,UBOGON,0x74ac,UBOGON,UBOGON,0x7583,
+ 0x7645,0x764e,0x7644,0x76a3,0x76a5,0x77a6,0x77a4,UBOGON,0x77a9,0x77af,
+ UBOGON,UBOGON,UBOGON,0x78f0,0x78f8,0x78f1,UBOGON,0x7a49,UBOGON,UBOGON,
+ UBOGON,0x7ac2,0x7af2,0x7af3,0x7bfa,UBOGON,0x7bf6,0x7bfc,0x7c18,0x7c08,
+ 0x7c12,UBOGON,UBOGON,0x7cdb,0x7cda,UBOGON,UBOGON,UBOGON,0x7e2c,0x7e4d,
+ UBOGON,UBOGON,0x7f46,0x7ff6
+ },
+ { /* ku 36 */
+ 0x802b,0x8074,0x81b8,0x81c8,UBOGON,UBOGON,UBOGON,0x8592,0x8593,UBOGON,
+ 0x857f,0x85ab,0x8597,UBOGON,UBOGON,0x85ac,UBOGON,UBOGON,UBOGON,0x87ce,
+ UBOGON,0x87cd,UBOGON,UBOGON,0x87c1,0x87b1,0x87c7,UBOGON,0x8940,UBOGON,
+ 0x893f,0x8939,UBOGON,0x8943,UBOGON,UBOGON,UBOGON,0x89ab,UBOGON,0x8b1f,
+ 0x8b09,0x8b0c,UBOGON,UBOGON,0x8c40,UBOGON,0x8c96,UBOGON,0x8cf6,0x8cf7,
+ UBOGON,0x8e46,0x8e4f,UBOGON,UBOGON,UBOGON,0x8f3d,0x8f41,0x9366,0x9378,
+ 0x935d,0x9369,0x9374,0x937d,0x936e,0x9372,0x9373,0x9362,0x9348,0x9353,
+ 0x935f,0x9368,UBOGON,0x937f,0x936b,UBOGON,0x95c4,UBOGON,0x96af,0x96ad,
+ 0x96b2,UBOGON,UBOGON,0x971a,0x971b,UBOGON,UBOGON,UBOGON,UBOGON,0x979b,
+ 0x979f,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 37 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x9840,UBOGON,0x9847,UBOGON,0x98b7,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x99a2,UBOGON,UBOGON,0x9a00,0x99f3,
+ UBOGON,UBOGON,0x99f5,UBOGON,UBOGON,0x9abd,0x9b00,0x9b02,UBOGON,0x9b34,
+ 0x9b49,0x9b9f,UBOGON,0x9ba3,0x9bcd,0x9b99,0x9b9d,UBOGON,UBOGON,0x9d39,
+ UBOGON,0x9d44,UBOGON,UBOGON,0x9d35,UBOGON,UBOGON,0x9eaf,UBOGON,0x512f,
+ UBOGON,UBOGON,0x9f8e,UBOGON,0x569f,0x569b,0x569e,0x5696,0x5694,0x56a0,
+ UBOGON,0x5b3b,UBOGON,UBOGON,0x5b3a,0x5dc1,0x5f4d,0x5f5d,0x61f3,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x64f6,0x64e5,0x64ea,0x64e7,0x6505,UBOGON,0x64f9,
+ UBOGON,UBOGON,UBOGON,0x6aab,0x6aed,0x6ab2,0x6ab0,0x6ab5,0x6abe,0x6ac1,
+ 0x6ac8,UBOGON,0x6ac0,0x6abc
+ },
+ { /* ku 38 */
+ 0x6ab1,0x6ac4,0x6abf,UBOGON,UBOGON,0x7008,0x7003,0x6ffd,0x7010,0x7002,
+ 0x7013,UBOGON,0x71fa,0x7200,0x74b9,0x74bc,UBOGON,0x765b,0x7651,0x764f,
+ 0x76eb,0x77b8,UBOGON,0x77b9,0x77c1,0x77c0,0x77be,0x790b,UBOGON,0x7907,
+ 0x790a,0x7908,UBOGON,0x790d,0x7906,0x7915,0x79af,UBOGON,UBOGON,UBOGON,
+ 0x7af5,UBOGON,UBOGON,0x7c2e,UBOGON,0x7c1b,UBOGON,0x7c1a,0x7c24,UBOGON,
+ UBOGON,0x7ce6,0x7ce3,UBOGON,UBOGON,0x7e5d,0x7e4f,0x7e66,0x7e5b,0x7f47,
+ 0x7fb4,UBOGON,UBOGON,UBOGON,0x7ffa,0x802e,UBOGON,UBOGON,0x81ce,UBOGON,
+ UBOGON,0x8219,UBOGON,UBOGON,0x85cc,0x85b2,UBOGON,0x85bb,0x85c1,UBOGON,
+ UBOGON,UBOGON,0x87e9,0x87ee,0x87f0,0x87d6,0x880e,0x87da,0x8948,0x894a,
+ 0x894e,0x894d,0x89b1,0x89b0
+ },
+ { /* ku 39 */
+ 0x89b3,UBOGON,0x8b38,0x8b32,UBOGON,0x8b2d,UBOGON,0x8b34,UBOGON,0x8b29,
+ 0x8c74,UBOGON,UBOGON,0x8d03,UBOGON,UBOGON,0x8da9,0x8e58,UBOGON,UBOGON,
+ 0x8ebf,0x8ec1,0x8f4a,0x8fac,UBOGON,0x9089,0x913d,0x913c,0x91a9,0x93a0,
+ UBOGON,0x9390,UBOGON,0x9393,0x938b,0x93ad,0x93bb,0x93b8,UBOGON,UBOGON,
+ 0x939c,0x95d8,0x95d7,UBOGON,UBOGON,UBOGON,0x975d,0x97a9,0x97da,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x9854,UBOGON,0x9855,0x984b,UBOGON,0x983f,0x98b9,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x9938,0x9936,0x9940,UBOGON,0x993b,0x9939,
+ 0x99a4,UBOGON,UBOGON,0x9a08,0x9a0c,UBOGON,0x9a10,UBOGON,0x9b07,UBOGON,
+ 0x9bd2,UBOGON,0x9bc2,0x9bbb,0x9bcc,0x9bcb,UBOGON,UBOGON,0x9d4d,0x9d63,
+ 0x9d4e,UBOGON,0x9d50,0x9d55
+ },
+ { /* ku 3a */
+ UBOGON,0x9d5e,UBOGON,0x9e90,0x9eb2,0x9eb1,UBOGON,0x9eca,0x9f02,0x9f27,
+ 0x9f26,UBOGON,0x56af,0x58e0,0x58dc,UBOGON,0x5b39,UBOGON,UBOGON,0x5b7c,
+ 0x5bf3,UBOGON,UBOGON,0x5c6b,0x5dc4,0x650b,0x6508,0x650a,UBOGON,UBOGON,
+ 0x65dc,UBOGON,UBOGON,0x66e1,0x66df,0x6ace,0x6ad4,0x6ae3,0x6ad7,0x6ae2,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x6ad8,0x6ad5,0x6ad2,UBOGON,UBOGON,0x701e,
+ 0x702c,0x7025,0x6ff3,0x7204,0x7208,0x7215,UBOGON,0x74c4,0x74c9,0x74c7,
+ 0x74c8,0x76a9,0x77c6,0x77c5,0x7918,0x791a,0x7920,UBOGON,0x7a66,0x7a64,
+ 0x7a6a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7c35,0x7c34,UBOGON,
+ UBOGON,0x7e6c,UBOGON,0x7e6e,0x7e71,UBOGON,0x81d4,0x81d6,0x821a,0x8262,
+ 0x8265,0x8276,0x85db,0x85d6
+ },
+ { /* ku 3b */
+ UBOGON,0x85e7,UBOGON,UBOGON,0x85f4,UBOGON,0x87fd,0x87d5,0x8807,UBOGON,
+ 0x880f,0x87f8,UBOGON,UBOGON,0x8987,UBOGON,0x89b5,0x89f5,UBOGON,0x8b3f,
+ 0x8b43,0x8b4c,UBOGON,0x8d0b,0x8e6b,0x8e68,0x8e70,0x8e75,0x8e77,UBOGON,
+ 0x8ec3,UBOGON,0x93e9,0x93ea,0x93cb,0x93c5,0x93c6,UBOGON,0x93ed,0x93d3,
+ UBOGON,0x93e5,UBOGON,UBOGON,0x93db,0x93eb,0x93e0,0x93c1,UBOGON,UBOGON,
+ 0x95dd,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x97b2,0x97b4,0x97b1,0x97b5,0x97f2,UBOGON,UBOGON,UBOGON,0x9856,UBOGON,
+ UBOGON,UBOGON,0x9944,UBOGON,0x9a26,0x9a1f,0x9a18,0x9a21,0x9a17,UBOGON,
+ 0x9b09,UBOGON,UBOGON,0x9bc5,0x9bdf,UBOGON,0x9be3,UBOGON,0x9be9,0x9bee,
+ UBOGON,UBOGON,0x9d66,0x9d7a
+ },
+ { /* ku 3c */
+ UBOGON,0x9d6e,0x9d91,0x9d83,0x9d76,0x9d7e,0x9d6d,UBOGON,0x9e95,0x9ee3,
+ UBOGON,UBOGON,0x9f03,0x9f04,UBOGON,0x9f17,UBOGON,0x5136,UBOGON,0x5336,
+ UBOGON,0x5b42,UBOGON,UBOGON,0x5b44,0x5b46,0x5b7e,0x5dca,0x5dc8,0x5dcc,
+ 0x5ef0,UBOGON,0x6585,0x66e5,0x66e7,UBOGON,UBOGON,UBOGON,0x6af4,UBOGON,
+ 0x6ae9,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x703d,UBOGON,0x7036,UBOGON,
+ 0x7216,UBOGON,0x7212,0x720f,0x7217,0x7211,0x720b,UBOGON,UBOGON,0x74cd,
+ 0x74d0,0x74cc,0x74ce,0x74d1,UBOGON,0x7589,UBOGON,0x7a6f,0x7c4b,0x7c44,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7e7f,0x8b71,UBOGON,0x802f,0x807a,
+ 0x807b,0x807c,UBOGON,UBOGON,UBOGON,0x85fc,0x8610,0x8602,UBOGON,UBOGON,
+ 0x85ee,0x8603,UBOGON,0x860d
+ },
+ { /* ku 3d */
+ 0x8613,0x8608,0x860f,0x8818,0x8812,UBOGON,UBOGON,0x8967,0x8965,0x89bb,
+ 0x8b69,0x8b62,UBOGON,0x8b6e,UBOGON,0x8b61,UBOGON,0x8b64,0x8b4d,0x8c51,
+ UBOGON,UBOGON,0x8e83,0x8ec6,UBOGON,0x941f,UBOGON,0x9404,0x9417,0x9408,
+ 0x9405,UBOGON,0x93f3,0x941e,0x9402,0x941a,0x941b,0x9427,0x941c,UBOGON,
+ 0x96b5,UBOGON,UBOGON,0x9733,UBOGON,0x9734,0x9731,0x97b8,0x97ba,UBOGON,
+ 0x97fc,UBOGON,UBOGON,0x98c3,UBOGON,0x994d,UBOGON,0x9a2f,UBOGON,UBOGON,
+ UBOGON,0x9ac9,UBOGON,0x9ac8,0x9ac4,0x9b2a,0x9b38,0x9b50,UBOGON,0x9c0a,
+ 0x9bfb,0x9c04,0x9bfc,0x9bfe,UBOGON,UBOGON,UBOGON,0x9c02,0x9bf6,0x9c1b,
+ 0x9bf9,0x9c15,0x9c10,0x9bff,0x9c00,0x9c0c,UBOGON,UBOGON,0x9d95,0x9da5,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3e */
+ 0x9e98,0x9ec1,UBOGON,0x9f5a,0x5164,0x56bb,UBOGON,0x58e6,0x5b49,0x5bf7,
+ UBOGON,UBOGON,0x5dd0,UBOGON,0x5fc2,UBOGON,0x6511,UBOGON,0x6aff,0x6afe,
+ 0x6afd,UBOGON,0x6b01,UBOGON,UBOGON,0x704b,0x704d,0x7047,0x74d3,0x7668,
+ 0x7667,UBOGON,UBOGON,0x77d1,0x7930,0x7932,0x792e,UBOGON,0x9f9d,0x7ac9,
+ 0x7ac8,UBOGON,0x7c56,0x7c51,UBOGON,UBOGON,UBOGON,0x7e85,0x7e89,0x7e8e,
+ 0x7e84,UBOGON,0x826a,0x862b,0x862f,0x8628,UBOGON,0x8616,0x8615,0x861d,
+ 0x881a,UBOGON,UBOGON,UBOGON,0x89bc,0x8b75,0x8b7c,UBOGON,0x8d11,0x8d12,
+ 0x8f5c,0x91bb,UBOGON,0x93f4,UBOGON,UBOGON,0x942d,UBOGON,UBOGON,0x96e4,
+ 0x9737,0x9736,0x9767,0x97be,0x97bd,0x97e2,0x9868,0x9866,0x98c8,0x98ca,
+ 0x98c7,0x98dc,UBOGON,0x994f
+ },
+ { /* ku 3f */
+ 0x99a9,0x9a3c,UBOGON,0x9a3b,0x9ace,UBOGON,0x9b14,0x9b53,UBOGON,0x9c2e,
+ UBOGON,0x9c1f,UBOGON,UBOGON,UBOGON,UBOGON,0x9db0,0x9dbd,UBOGON,UBOGON,
+ 0x9dae,0x9dc4,0x9e7b,UBOGON,UBOGON,0x9e9e,UBOGON,0x9f05,UBOGON,0x9f69,
+ 0x9fa1,0x56c7,0x571d,0x5b4a,0x5dd3,UBOGON,0x5f72,0x6202,UBOGON,0x6235,
+ 0x6527,0x651e,0x651f,UBOGON,UBOGON,0x6b07,0x6b06,UBOGON,UBOGON,0x7054,
+ 0x721c,0x7220,0x7af8,UBOGON,0x7c5d,0x7c58,UBOGON,0x7e92,0x7f4e,UBOGON,
+ UBOGON,UBOGON,0x8827,UBOGON,0x8b81,0x8b83,UBOGON,0x8c44,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x9442,0x944d,0x9454,0x944e,UBOGON,0x9443,UBOGON,UBOGON,
+ 0x973c,0x9740,0x97c0,UBOGON,UBOGON,UBOGON,UBOGON,0x995a,0x9a51,UBOGON,
+ 0x9add,UBOGON,UBOGON,0x9c38
+ },
+ { /* ku 40 */
+ UBOGON,0x9c45,0x9c3a,UBOGON,0x9c35,UBOGON,UBOGON,UBOGON,0x9ef1,UBOGON,
+ 0x9f93,0x529a,UBOGON,UBOGON,0x8641,0x5dd7,UBOGON,0x6528,UBOGON,UBOGON,
+ UBOGON,0x7053,0x7059,UBOGON,0x7221,UBOGON,0x766f,0x7937,0x79b5,0x7c62,
+ 0x7c5e,0x7cf5,UBOGON,UBOGON,0x863d,UBOGON,0x882d,0x8989,0x8b8d,0x8b87,
+ 0x8b90,0x8d1a,0x8e99,UBOGON,UBOGON,UBOGON,0x945f,UBOGON,UBOGON,0x9456,
+ 0x9461,0x945b,0x945a,0x945c,0x9465,UBOGON,0x9741,UBOGON,UBOGON,0x986e,
+ 0x986c,0x986d,UBOGON,0x99aa,0x9a5c,0x9a58,0x9ade,UBOGON,0x9c4f,0x9c51,
+ UBOGON,0x9c53,UBOGON,UBOGON,UBOGON,0x9dfc,0x9f39,UBOGON,0x513e,UBOGON,
+ 0x56d2,UBOGON,0x5b4f,0x6b14,UBOGON,0x7a72,0x7a73,UBOGON,UBOGON,UBOGON,
+ 0x8b91,UBOGON,UBOGON,0x91bf
+ },
+ { /* ku 41 */
+ UBOGON,0x946c,UBOGON,UBOGON,0x96e6,0x9745,UBOGON,0x97c8,0x97e4,0x995d,
+ UBOGON,0x9b21,UBOGON,0x9b2c,0x9b57,UBOGON,UBOGON,0x9c5d,0x9c61,0x9c65,
+ 0x9e08,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x9f45,UBOGON,UBOGON,0x6205,
+ 0x66ef,0x6b1b,0x6b1d,0x7225,0x7224,0x7c6d,UBOGON,0x8642,0x8649,UBOGON,
+ 0x8978,0x898a,0x8b97,UBOGON,0x8c9b,0x8d1c,UBOGON,0x8ea2,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x9c6c,UBOGON,0x9c6f,UBOGON,
+ 0x9e0e,UBOGON,0x9f08,0x9f1d,0x9fa3,UBOGON,UBOGON,0x5f60,0x6b1c,UBOGON,
+ UBOGON,UBOGON,0x7cf3,UBOGON,0x8b9b,0x8ea7,0x91c4,UBOGON,0x947a,UBOGON,
+ UBOGON,0x9a61,0x9a63,0x9ad7,0x9c76,UBOGON,0x9fa5,UBOGON,0x7067,UBOGON,
+ 0x72ab,0x864a,0x897d,0x8b9d
+ },
+ { /* ku 42 */
+ 0x8c53,0x8f65,0x947b,UBOGON,0x98cd,0x98dd,UBOGON,0x9b30,0x9e16,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x96e7,0x9e18,0x9ea2,UBOGON,0x9f7c,UBOGON,
+ 0x7e9e,0x9484,UBOGON,0x9e1c,UBOGON,0x7c71,0x97ca,UBOGON,UBOGON,UBOGON,
+ 0x9ea3,UBOGON,0x9c7b,0x9f97,UBOGON,UBOGON,0x9750,UBOGON,UBOGON,UBOGON,
+ 0x5727,0x5c13,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5fc8,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x6765,UBOGON,UBOGON,0x52bd,UBOGON,0x5b66,
+ UBOGON,0x65f9,0x6788,0x6ce6,0x6ccb,UBOGON,0x4fbd,0x5f8d,UBOGON,0x6018,
+ 0x6048,UBOGON,0x6b29,0x70a6,UBOGON,0x7706,UBOGON,UBOGON,UBOGON,0x5a10,
+ 0x5cfc,0x5cfe,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x70c9,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 43 */
+ UBOGON,UBOGON,0x9579,UBOGON,0x96ba,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x7b29,0x8128,UBOGON,0x8a2e,UBOGON,UBOGON,UBOGON,0x9ad9,
+ UBOGON,0x582b,0x5845,UBOGON,0x63fa,UBOGON,UBOGON,UBOGON,0x6e86,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x5867,UBOGON,0x5bdd,0x656e,UBOGON,UBOGON,
+ UBOGON,0x8c87,UBOGON,0x50d2,0x50df,UBOGON,UBOGON,UBOGON,UBOGON,0x69ba,
+ UBOGON,0x6b9d,UBOGON,0x8059,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6f8a,UBOGON,UBOGON,0x7bc3,
+ 0x7bc2,UBOGON,UBOGON,UBOGON,UBOGON,0x90f6,UBOGON,0x9823,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x71cd,0x7499,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x9842,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 44 */
+ UBOGON,0x7f84,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8d0e,UBOGON,0x9861,
+ UBOGON,UBOGON,0x8b73,UBOGON,0x9c27,UBOGON,0x9458,0x77d6,0x9b2d,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4f66,
+ 0x4f68,0x4fe7,0x503f,UBOGON,0x50a6,0x510f,0x523e,0x5324,0x5365,0x539b,
+ 0x517f,0x54cb,0x5573,0x5571,0x556b,0x55f4,0x5622,0x5620,0x5692,0x56ba,
+ 0x5691,0x56b0,0x5759,0x578a,0x580f,0x5812,0x5813,0x5847,0x589b,0x5900,
+ 0x594d,0x5ad1,0x5ad3,0x5b67,0x5c57,0x5c77,0x5cd5,0x5d75,0x5d8e,0x5da5,
+ 0x5db6,0x5dbf,0x5e65,0x5ecd,0x5eed,0x5f94,0x5f9a,0x5fba,0x6125,0x6150,
+ 0x62a3,0x6360,0x6364,0x63b6
+ },
+ { /* ku 45 */
+ 0x6403,0x64b6,0x651a,0x7a25,0x5c21,0x66e2,0x6702,0x67a4,0x67ac,0x6810,
+ 0x6806,0x685e,0x685a,0x692c,0x6929,0x6a2d,0x6a77,0x6a7a,0x6aca,0x6ae6,
+ 0x6af5,0x6b0d,0x6b0e,0x6bdc,0x6bdd,0x6bf6,0x6c1e,0x6c63,0x6da5,0x6e0f,
+ 0x6e8a,0x6e84,0x6e8b,0x6e7c,0x6f4c,0x6f48,0x6f49,0x6f9d,0x6f99,0x6ff8,
+ 0x702e,0x702d,0x705c,0x79cc,0x70bf,0x70ea,0x70e5,0x7111,0x7112,0x713f,
+ 0x7139,0x713b,0x713d,0x7177,0x7175,0x7176,0x7171,0x7196,0x7193,0x71b4,
+ 0x71dd,0x71de,0x720e,0x5911,0x7218,0x7347,0x7348,0x73ef,0x7412,0x743b,
+ 0x74a4,0x748d,0x74b4,0x7673,0x7677,0x76bc,0x7819,0x781b,0x783d,0x7853,
+ 0x7854,0x7858,0x78b7,0x78d8,0x78ee,0x7922,0x794d,0x7986,0x7999,0x79a3,
+ 0x79bc,0x7aa7,0x7b37,0x7b59
+ },
+ { /* ku 46 */
+ 0x7bd0,0x7c2f,0x7c32,0x7c42,0x7c4e,0x7c68,0x7ca9,0x7ced,0x7dd0,0x7e07,
+ 0x7dd3,0x7e64,0x7f40,UBOGON,0x8041,0x8063,0x80bb,0x6711,0x6725,0x8248,
+ 0x8310,0x8362,0x8312,0x8421,0x841e,0x84e2,0x84de,0x84e1,0x8573,0x85d4,
+ 0x85f5,0x8637,0x8645,0x8672,0x874a,0x87a9,0x87a5,0x87f5,0x8834,0x8850,
+ 0x8887,0x8954,0x8984,0x8b03,0x8c52,0x8cd8,0x8d0c,0x8d18,0x8db0,0x8ebc,
+ 0x8ed5,0x8faa,0x909c,UBOGON,0x915c,0x922b,0x9221,0x9273,0x92f4,0x92f5,
+ 0x933f,0x9342,0x9386,0x93be,0x93bc,0x93bd,0x93f1,0x93f2,0x93ef,0x9422,
+ 0x9423,0x9424,0x9467,0x9466,0x9597,0x95ce,0x95e7,0x973b,0x974d,0x98e4,
+ 0x9942,0x9b1d,0x9b98,UBOGON,0x9d49,0x6449,0x5e71,0x5e85,0x61d3,0x990e,
+ 0x8002,0x781e,UBOGON,UBOGON
+ },
+ { /* ku 47 */
+ 0x5528,0x5572,0x55ba,0x55f0,0x55ee,0x56b8,0x56b9,0x56c4,0x8053,0x92b0,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ }
+};
+#endif
+
+#if CNS_EXTENSION
+/* CNS 11643 plane 15 conversion table */
+
+static const unsigned short
+ cns11643_15tab[MAX_CNS11643_KU_15][MAX_CNS11643_TEN] = {
+ { /* ku 01 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5301,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3436,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x53fa,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x9f99,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6c49,
+ UBOGON,UBOGON,UBOGON,0x8fb7,UBOGON,0x3406,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4f29,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 02 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x534e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5c81,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5f10,UBOGON,UBOGON,0x6268,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x6742,0x6740,0x51ea,UBOGON,UBOGON,UBOGON,
+ 0x6c62,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7391,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x8fbb,0x8fbc,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 03 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3575,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x56e8,UBOGON,0x575b,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x5c97,UBOGON,UBOGON
+ },
+ { /* ku 04 */
+ UBOGON,0x6762,UBOGON,UBOGON,0x383c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x62a4,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x6766,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x6ca3,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x707f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 05 */
+ UBOGON,UBOGON,0x77f6,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8fc8,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4fab,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x3453,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x5c2d,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x549c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 06 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5788,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x34ac,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 07 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x62c3,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6619,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3b49,UBOGON,0x67a1,UBOGON,0x67a6,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3c91,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3cd3,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 08 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x77fe,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x7f57,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x43d5,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x82c5,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8fdf,UBOGON,UBOGON,
+ 0x8fdc,0x488c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 09 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4fe4,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x551b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3588,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x57aa,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x57ab,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x36c9,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x5ba9,UBOGON,UBOGON,UBOGON,UBOGON,0x3917,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6811,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7551,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3ebd,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x7553,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7818,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4133,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7ad7,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0d */
+ 0x7c7e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x867e,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4844,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x5266,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x5520,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x5521,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x57d7,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x36e1,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x36e2,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x5bbe
+ },
+ { /* ku 0f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x387c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x38e3,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3aec,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 10 */
+ UBOGON,UBOGON,UBOGON,0x6857,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 11 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7f3c,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 12 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8273,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4627,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x96be,UBOGON,
+ UBOGON,UBOGON,0x66fa,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 13 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x35ab,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x364b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x5a72,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 14 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x37e2,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 15 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x68bd,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 16 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6e15,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3dc1,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x7413,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x74f8,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x7b3d
+ },
+ { /* ku 17 */
+ UBOGON,UBOGON,0x76d8,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x79fc,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x7b39,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x7d4b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x83b9,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 18 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x86cf,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8eae,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 19 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x96eb,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x55b0,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x5840,0x5842
+ },
+ { /* ku 1a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3701,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x692b,UBOGON,0x6916,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1c */
+ 0x691b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6927,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6bf5,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x3d20,UBOGON,UBOGON,UBOGON,0x6e82,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3d21,UBOGON,UBOGON,
+ 0x6e7a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x7129
+ },
+ { /* ku 1d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3eda,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1e */
+ 0x7cab,UBOGON,0x7cac,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x83f7,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x44ea,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 1f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4989,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x9596
+ },
+ { /* ku 20 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4ab2,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x55f1,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 21 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x5f41,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 22 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x698a,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x698c,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6980,UBOGON,
+ 0x697f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3dda,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 23 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3ddb,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3ee2,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3ee3,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 24 */
+ UBOGON,UBOGON,UBOGON,UBOGON,0x789c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7b7b,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 25 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x47f3,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x90d2,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x95a0,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 26 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4a57,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x51a9,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 27 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 28 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3bbc,
+ UBOGON,UBOGON,0x3ba4,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 29 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x7195,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3de8,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x7198,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7478,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x78b9,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7a33,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7cc0,UBOGON,UBOGON,UBOGON,
+ 0x7cc1,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8744,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4886,UBOGON,UBOGON,UBOGON,0x9064,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2d */
+ UBOGON,0x9277,UBOGON,UBOGON,UBOGON,UBOGON,0x92af,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2e */
+ UBOGON,UBOGON,UBOGON,UBOGON,0x366d,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x366e,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x5e64,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x6a2b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6f46,UBOGON,
+ UBOGON,UBOGON,0x6f9a,UBOGON
+ },
+ { /* ku 30 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3f53,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 31 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4526,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x4527,UBOGON,UBOGON,0x4528,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 32 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x92f2,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 33 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x9b79,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x567a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 34 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x372c,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x5f5c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x65d9,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x6a72,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 35 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6a78,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x6b5a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3efb,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 36 */
+ UBOGON,0x3efc,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 37 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 38 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8ebe,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x933b,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x9340,UBOGON,UBOGON,UBOGON,0x933a,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 39 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x9b96,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x39a0,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x71f5,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3f01,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7a50,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x481a,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x9387,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x9385,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x493c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x9bb1,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3e */
+ UBOGON,UBOGON,UBOGON,0x9d47,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x3a6b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 3f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 40 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x455c,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4787,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 41 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x93b9,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x93bf,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x9bcf,UBOGON,UBOGON,UBOGON,UBOGON,0x9d64,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x9ebf,UBOGON,UBOGON
+ },
+ { /* ku 42 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x3c07,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3f05,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 43 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x45f9,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x89b8,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 44 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4953,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x9bf3,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x4c69,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 45 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3c12,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x7c4f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 46 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x9425,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 47 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 48 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x4878,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x95e6,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 49 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x9c2f,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x6b0c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 4a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x9c47,
+ 0x4c88,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7936,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 4b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6b15,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 4c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4584,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 4d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x53b5,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ }
+};
+#endif
+#endif
diff --git a/imap/src/charset/decomtab.c b/imap/src/charset/decomtab.c
new file mode 100644
index 00000000..8a02e322
--- /dev/null
+++ b/imap/src/charset/decomtab.c
@@ -0,0 +1,2909 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Unicode decomposition tables (current as of Unicode 5.0)
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 13 April 2006
+ * Last Edited: 6 December 2006
+ */
+
+/* BMP decompositions */
+
+#define UCS4_BMPLOMIN 0x00a0
+#define UCS4_BMPLOMAX 0x33ff
+#define UCS4_BMPLOIXMASK 0x1fff
+#define UCS4_BMPLOSIZEMASK 0xe000
+#define UCS4_BMPLOSIZESHIFT 13
+
+ /* BMP low decomposition indices - sssi iiii iiii iiii */
+static unsigned short ucs4_dbmploixtab[13152] = {
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x2001,0x0000,0x0003,0x0000,0x0000,0x0000,0x0000,0x2004,
+ 0x0000,0x0000,0x0006,0x0007,0x2008,0x000a,0x0000,0x0000,
+ 0x200b,0x000d,0x000e,0x0000,0x400f,0x4012,0x4015,0x0000,
+ 0x2018,0x201a,0x201c,0x201e,0x2020,0x2022,0x0000,0x2024,
+ 0x2026,0x2028,0x202a,0x202c,0x202e,0x2030,0x2032,0x2034,
+ 0x0000,0x2036,0x2038,0x203a,0x203c,0x203e,0x2040,0x0000,
+ 0x0000,0x2042,0x2044,0x2046,0x2048,0x204a,0x0000,0x0000,
+ 0x204c,0x204e,0x2050,0x2052,0x2054,0x2056,0x0000,0x2058,
+ 0x205a,0x205c,0x205e,0x2060,0x2062,0x2064,0x2066,0x2068,
+ 0x0000,0x206a,0x206c,0x206e,0x2070,0x2072,0x2074,0x0000,
+ 0x0000,0x2076,0x2078,0x207a,0x207c,0x207e,0x0000,0x2080,
+ 0x2082,0x2084,0x2086,0x2088,0x208a,0x208c,0x208e,0x2090,
+ 0x2092,0x2094,0x2096,0x2098,0x209a,0x209c,0x209e,0x20a0,
+ 0x0000,0x0000,0x20a2,0x20a4,0x20a6,0x20a8,0x20aa,0x20ac,
+ 0x20ae,0x20b0,0x20b2,0x20b4,0x20b6,0x20b8,0x20ba,0x20bc,
+ 0x20be,0x20c0,0x20c2,0x20c4,0x20c6,0x20c8,0x0000,0x0000,
+ 0x20ca,0x20cc,0x20ce,0x20d0,0x20d2,0x20d4,0x20d6,0x20d8,
+ 0x20da,0x0000,0x20dc,0x20de,0x20e0,0x20e2,0x20e4,0x20e6,
+ 0x0000,0x20e8,0x20ea,0x20ec,0x20ee,0x20f0,0x20f2,0x20f4,
+ 0x20f6,0x0000,0x0000,0x20f8,0x20fa,0x20fc,0x20fe,0x2100,
+ 0x2102,0x2104,0x0000,0x0000,0x2106,0x2108,0x210a,0x210c,
+ 0x210e,0x2110,0x0000,0x0000,0x2112,0x2114,0x2116,0x2118,
+ 0x211a,0x211c,0x211e,0x2120,0x2122,0x2124,0x2126,0x2128,
+ 0x212a,0x212c,0x212e,0x2130,0x2132,0x2134,0x0000,0x0000,
+ 0x2136,0x2138,0x213a,0x213c,0x213e,0x2140,0x2142,0x2144,
+ 0x2146,0x2148,0x214a,0x214c,0x214e,0x2150,0x2152,0x2154,
+ 0x2156,0x2158,0x215a,0x215c,0x215e,0x2160,0x2162,0x0164,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x2165,0x2167,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x2169,
+ 0x216b,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x216d,0x216f,0x2171,0x2173,
+ 0x2175,0x2177,0x2179,0x217b,0x217d,0x217f,0x2181,0x2183,
+ 0x2185,0x2187,0x2189,0x218b,0x218d,0x218f,0x2191,0x2193,
+ 0x2195,0x2197,0x2199,0x219b,0x219d,0x0000,0x219f,0x21a1,
+ 0x21a3,0x21a5,0x21a7,0x21a9,0x0000,0x0000,0x21ab,0x21ad,
+ 0x21af,0x21b1,0x21b3,0x21b5,0x21b7,0x21b9,0x21bb,0x21bd,
+ 0x21bf,0x21c1,0x21c3,0x21c5,0x21c7,0x21c9,0x0000,0x0000,
+ 0x21cb,0x21cd,0x21cf,0x21d1,0x21d3,0x21d5,0x21d7,0x21d9,
+ 0x21db,0x21dd,0x21df,0x21e1,0x21e3,0x21e5,0x21e7,0x21e9,
+ 0x21eb,0x21ed,0x21ef,0x21f1,0x21f3,0x21f5,0x21f7,0x21f9,
+ 0x21fb,0x21fd,0x21ff,0x2201,0x2203,0x2205,0x2207,0x2209,
+ 0x220b,0x220d,0x220f,0x2211,0x0000,0x0000,0x2213,0x2215,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x2217,0x2219,
+ 0x221b,0x221d,0x221f,0x2221,0x2223,0x2225,0x2227,0x2229,
+ 0x222b,0x222d,0x222f,0x2231,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0233,0x0234,0x0235,0x0236,0x0237,0x0238,0x0239,0x023a,
+ 0x023b,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x223c,0x223e,0x2240,0x2242,0x2244,0x2246,0x0000,0x0000,
+ 0x0248,0x0249,0x024a,0x024b,0x024c,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x024d,0x024e,0x0000,0x024f,0x2250,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0252,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x2253,0x0000,0x0000,0x0000,0x0255,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x2256,0x2258,0x225a,0x025c,
+ 0x225d,0x225f,0x2261,0x0000,0x2263,0x0000,0x2265,0x2267,
+ 0x2269,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x226b,0x226d,0x226f,0x2271,0x2273,0x2275,
+ 0x2277,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x2279,0x227b,0x227d,0x227f,0x2281,0x0000,
+ 0x0283,0x0284,0x0285,0x2286,0x2288,0x028a,0x028b,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x028c,0x028d,0x028e,0x0000,0x028f,0x0290,0x0000,0x0000,
+ 0x0000,0x0291,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x2292,0x2294,0x0000,0x2296,0x0000,0x0000,0x0000,0x2298,
+ 0x0000,0x0000,0x0000,0x0000,0x229a,0x229c,0x229e,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x22a0,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x22a2,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x22a4,0x22a6,0x0000,0x22a8,0x0000,0x0000,0x0000,0x22aa,
+ 0x0000,0x0000,0x0000,0x0000,0x22ac,0x22ae,0x22b0,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x22b2,0x22b4,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x22b6,0x22b8,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x22ba,0x22bc,0x22be,0x22c0,0x0000,0x0000,0x22c2,0x22c4,
+ 0x0000,0x0000,0x22c6,0x22c8,0x22ca,0x22cc,0x22ce,0x22d0,
+ 0x0000,0x0000,0x22d2,0x22d4,0x22d6,0x22d8,0x22da,0x22dc,
+ 0x0000,0x0000,0x22de,0x22e0,0x22e2,0x22e4,0x22e6,0x22e8,
+ 0x22ea,0x22ec,0x22ee,0x22f0,0x22f2,0x22f4,0x0000,0x0000,
+ 0x22f6,0x22f8,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x22fa,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x22fc,0x22fe,0x2300,0x2302,0x2304,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x2306,0x2308,0x230a,
+ 0x230c,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x230e,0x0000,0x2310,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x2312,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x2314,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x2316,0x0000,0x0000,0x2318,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x231a,0x231c,0x231e,0x2320,0x2322,0x2324,0x2326,0x2328,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x232a,0x232c,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x232e,0x2330,0x0000,0x2332,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x2334,0x0000,0x0000,0x2336,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x2338,0x233a,0x233c,0x0000,0x0000,0x233e,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x2340,0x0000,0x0000,0x2342,0x2344,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x2346,0x2348,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x234a,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x234c,0x234e,0x2350,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x2352,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x2354,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x2356,
+ 0x2358,0x0000,0x235a,0x235c,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x235e,0x2360,0x2362,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x2364,0x0000,0x2366,0x2368,0x236a,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x236c,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x236e,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x2370,0x2372,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0374,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x2375,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x2377,0x0000,0x0000,
+ 0x0000,0x0000,0x2379,0x0000,0x0000,0x0000,0x0000,0x237b,
+ 0x0000,0x0000,0x0000,0x0000,0x237d,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x237f,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x2381,0x0000,0x2383,0x2385,0x2387,
+ 0x2389,0x238b,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x238d,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x238f,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x2391,0x0000,0x0000,
+ 0x0000,0x0000,0x2393,0x0000,0x0000,0x0000,0x0000,0x2395,
+ 0x0000,0x0000,0x0000,0x0000,0x2397,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x2399,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x239b,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x039d,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x239e,0x0000,
+ 0x23a0,0x0000,0x23a2,0x0000,0x23a4,0x0000,0x23a6,0x0000,
+ 0x0000,0x0000,0x23a8,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x23aa,0x0000,0x23ac,0x0000,0x0000,
+ 0x23ae,0x23b0,0x0000,0x23b2,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x03b4,0x03b5,0x03b6,0x0000,
+ 0x03b7,0x03b8,0x03b9,0x03ba,0x03bb,0x03bc,0x03bd,0x03be,
+ 0x03bf,0x03c0,0x03c1,0x0000,0x03c2,0x03c3,0x03c4,0x03c5,
+ 0x03c6,0x03c7,0x03c8,0x03c9,0x03ca,0x03cb,0x03cc,0x03cd,
+ 0x03ce,0x03cf,0x03d0,0x03d1,0x03d2,0x03d3,0x0000,0x03d4,
+ 0x03d5,0x03d6,0x03d7,0x03d8,0x03d9,0x03da,0x03db,0x03dc,
+ 0x03dd,0x03de,0x03df,0x03e0,0x03e1,0x03e2,0x03e3,0x03e4,
+ 0x03e5,0x03e6,0x03e7,0x03e8,0x03e9,0x03ea,0x03eb,0x03ec,
+ 0x03ed,0x03ee,0x03ef,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x03f0,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x03f1,0x03f2,0x03f3,0x03f4,0x03f5,
+ 0x03f6,0x03f7,0x03f8,0x03f9,0x03fa,0x03fb,0x03fc,0x03fd,
+ 0x03fe,0x03ff,0x0400,0x0401,0x0402,0x0403,0x0404,0x0405,
+ 0x0406,0x0407,0x0408,0x0409,0x040a,0x040b,0x040c,0x040d,
+ 0x040e,0x040f,0x0410,0x0411,0x0412,0x0413,0x0414,0x0415,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x2416,0x2418,0x241a,0x241c,0x241e,0x2420,0x2422,0x2424,
+ 0x2426,0x2428,0x242a,0x242c,0x242e,0x2430,0x2432,0x2434,
+ 0x2436,0x2438,0x243a,0x243c,0x243e,0x2440,0x2442,0x2444,
+ 0x2446,0x2448,0x244a,0x244c,0x244e,0x2450,0x2452,0x2454,
+ 0x2456,0x2458,0x245a,0x245c,0x245e,0x2460,0x2462,0x2464,
+ 0x2466,0x2468,0x246a,0x246c,0x246e,0x2470,0x2472,0x2474,
+ 0x2476,0x2478,0x247a,0x247c,0x247e,0x2480,0x2482,0x2484,
+ 0x2486,0x2488,0x248a,0x248c,0x248e,0x2490,0x2492,0x2494,
+ 0x2496,0x2498,0x249a,0x249c,0x249e,0x24a0,0x24a2,0x24a4,
+ 0x24a6,0x24a8,0x24aa,0x24ac,0x24ae,0x24b0,0x24b2,0x24b4,
+ 0x24b6,0x24b8,0x24ba,0x24bc,0x24be,0x24c0,0x24c2,0x24c4,
+ 0x24c6,0x24c8,0x24ca,0x24cc,0x24ce,0x24d0,0x24d2,0x24d4,
+ 0x24d6,0x24d8,0x24da,0x24dc,0x24de,0x24e0,0x24e2,0x24e4,
+ 0x24e6,0x24e8,0x24ea,0x24ec,0x24ee,0x24f0,0x24f2,0x24f4,
+ 0x24f6,0x24f8,0x24fa,0x24fc,0x24fe,0x2500,0x2502,0x2504,
+ 0x2506,0x2508,0x250a,0x250c,0x250e,0x2510,0x2512,0x2514,
+ 0x2516,0x2518,0x251a,0x251c,0x251e,0x2520,0x2522,0x2524,
+ 0x2526,0x2528,0x252a,0x252c,0x252e,0x2530,0x2532,0x2534,
+ 0x2536,0x2538,0x253a,0x253c,0x253e,0x2540,0x2542,0x2544,
+ 0x2546,0x2548,0x254a,0x254c,0x0000,0x0000,0x0000,0x0000,
+ 0x254e,0x2550,0x2552,0x2554,0x2556,0x2558,0x255a,0x255c,
+ 0x255e,0x2560,0x2562,0x2564,0x2566,0x2568,0x256a,0x256c,
+ 0x256e,0x2570,0x2572,0x2574,0x2576,0x2578,0x257a,0x257c,
+ 0x257e,0x2580,0x2582,0x2584,0x2586,0x2588,0x258a,0x258c,
+ 0x258e,0x2590,0x2592,0x2594,0x2596,0x2598,0x259a,0x259c,
+ 0x259e,0x25a0,0x25a2,0x25a4,0x25a6,0x25a8,0x25aa,0x25ac,
+ 0x25ae,0x25b0,0x25b2,0x25b4,0x25b6,0x25b8,0x25ba,0x25bc,
+ 0x25be,0x25c0,0x25c2,0x25c4,0x25c6,0x25c8,0x25ca,0x25cc,
+ 0x25ce,0x25d0,0x25d2,0x25d4,0x25d6,0x25d8,0x25da,0x25dc,
+ 0x25de,0x25e0,0x25e2,0x25e4,0x25e6,0x25e8,0x25ea,0x25ec,
+ 0x25ee,0x25f0,0x25f2,0x25f4,0x25f6,0x25f8,0x25fa,0x25fc,
+ 0x25fe,0x2600,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x2602,0x2604,0x2606,0x2608,0x260a,0x260c,0x260e,0x2610,
+ 0x2612,0x2614,0x2616,0x2618,0x261a,0x261c,0x261e,0x2620,
+ 0x2622,0x2624,0x2626,0x2628,0x262a,0x262c,0x0000,0x0000,
+ 0x262e,0x2630,0x2632,0x2634,0x2636,0x2638,0x0000,0x0000,
+ 0x263a,0x263c,0x263e,0x2640,0x2642,0x2644,0x2646,0x2648,
+ 0x264a,0x264c,0x264e,0x2650,0x2652,0x2654,0x2656,0x2658,
+ 0x265a,0x265c,0x265e,0x2660,0x2662,0x2664,0x2666,0x2668,
+ 0x266a,0x266c,0x266e,0x2670,0x2672,0x2674,0x2676,0x2678,
+ 0x267a,0x267c,0x267e,0x2680,0x2682,0x2684,0x0000,0x0000,
+ 0x2686,0x2688,0x268a,0x268c,0x268e,0x2690,0x0000,0x0000,
+ 0x2692,0x2694,0x2696,0x2698,0x269a,0x269c,0x269e,0x26a0,
+ 0x0000,0x26a2,0x0000,0x26a4,0x0000,0x26a6,0x0000,0x26a8,
+ 0x26aa,0x26ac,0x26ae,0x26b0,0x26b2,0x26b4,0x26b6,0x26b8,
+ 0x26ba,0x26bc,0x26be,0x26c0,0x26c2,0x26c4,0x26c6,0x26c8,
+ 0x26ca,0x06cc,0x26cd,0x06cf,0x26d0,0x06d2,0x26d3,0x06d5,
+ 0x26d6,0x06d8,0x26d9,0x06db,0x26dc,0x06de,0x0000,0x0000,
+ 0x26df,0x26e1,0x26e3,0x26e5,0x26e7,0x26e9,0x26eb,0x26ed,
+ 0x26ef,0x26f1,0x26f3,0x26f5,0x26f7,0x26f9,0x26fb,0x26fd,
+ 0x26ff,0x2701,0x2703,0x2705,0x2707,0x2709,0x270b,0x270d,
+ 0x270f,0x2711,0x2713,0x2715,0x2717,0x2719,0x271b,0x271d,
+ 0x271f,0x2721,0x2723,0x2725,0x2727,0x2729,0x272b,0x272d,
+ 0x272f,0x2731,0x2733,0x2735,0x2737,0x2739,0x273b,0x273d,
+ 0x273f,0x2741,0x2743,0x2745,0x2747,0x0000,0x2749,0x274b,
+ 0x274d,0x274f,0x2751,0x0753,0x2754,0x2756,0x0758,0x2759,
+ 0x275b,0x275d,0x275f,0x2761,0x2763,0x0000,0x2765,0x2767,
+ 0x2769,0x076b,0x276c,0x076e,0x276f,0x2771,0x2773,0x2775,
+ 0x2777,0x2779,0x277b,0x077d,0x0000,0x0000,0x277e,0x2780,
+ 0x2782,0x2784,0x2786,0x0788,0x0000,0x2789,0x278b,0x278d,
+ 0x278f,0x2791,0x2793,0x0795,0x2796,0x2798,0x279a,0x279c,
+ 0x279e,0x27a0,0x27a2,0x07a4,0x27a5,0x27a7,0x07a9,0x07aa,
+ 0x0000,0x0000,0x27ab,0x27ad,0x27af,0x0000,0x27b1,0x27b3,
+ 0x27b5,0x07b7,0x27b8,0x07ba,0x27bb,0x07bd,0x27be,0x0000,
+ 0x07c0,0x07c1,0x07c2,0x07c3,0x07c4,0x07c5,0x07c6,0x07c7,
+ 0x07c8,0x07c9,0x07ca,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x07cb,0x0000,0x0000,0x0000,0x0000,0x0000,0x27cc,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x07ce,0x27cf,0x47d1,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x07d4,
+ 0x0000,0x0000,0x0000,0x27d5,0x47d7,0x0000,0x27da,0x47dc,
+ 0x0000,0x0000,0x0000,0x0000,0x27df,0x0000,0x27e1,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x27e3,
+ 0x27e5,0x27e7,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x67e9,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x07ed,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x07ee,0x07ef,0x0000,0x0000,0x07f0,0x07f1,0x07f2,0x07f3,
+ 0x07f4,0x07f5,0x07f6,0x07f7,0x07f8,0x07f9,0x07fa,0x07fb,
+ 0x07fc,0x07fd,0x07fe,0x07ff,0x0800,0x0801,0x0802,0x0803,
+ 0x0804,0x0805,0x0806,0x0807,0x0808,0x0809,0x080a,0x0000,
+ 0x080b,0x080c,0x080d,0x080e,0x080f,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x2810,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x4812,0x4815,0x0818,0x2819,0x0000,0x481b,0x481e,0x0821,
+ 0x0000,0x2822,0x0824,0x0825,0x0826,0x0827,0x0828,0x0829,
+ 0x082a,0x082b,0x082c,0x082d,0x0000,0x082e,0x282f,0x0000,
+ 0x0000,0x0831,0x0832,0x0833,0x0834,0x0835,0x0000,0x0000,
+ 0x2836,0x4838,0x283b,0x0000,0x083d,0x0000,0x083e,0x0000,
+ 0x083f,0x0000,0x0840,0x0841,0x0842,0x0843,0x0000,0x0844,
+ 0x0845,0x0846,0x0000,0x0847,0x0848,0x0849,0x084a,0x084b,
+ 0x084c,0x084d,0x0000,0x484e,0x0851,0x0852,0x0853,0x0854,
+ 0x0855,0x0000,0x0000,0x0000,0x0000,0x0856,0x0857,0x0858,
+ 0x0859,0x085a,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x485b,0x485e,0x4861,0x4864,0x4867,
+ 0x486a,0x486d,0x4870,0x4873,0x4876,0x4879,0x487c,0x287f,
+ 0x0881,0x2882,0x4884,0x2887,0x0889,0x288a,0x488c,0x688f,
+ 0x2893,0x0895,0x2896,0x4898,0x089b,0x089c,0x089d,0x089e,
+ 0x089f,0x28a0,0x48a2,0x28a5,0x08a7,0x28a8,0x48aa,0x68ad,
+ 0x28b1,0x08b3,0x28b4,0x48b6,0x08b9,0x08ba,0x08bb,0x08bc,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x28bd,0x28bf,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x28c1,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x28c3,0x28c5,0x28c7,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x28c9,0x0000,0x0000,0x0000,
+ 0x0000,0x28cb,0x0000,0x0000,0x28cd,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x28cf,0x0000,0x28d1,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x28d3,0x48d5,0x0000,0x28d8,
+ 0x48da,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x28dd,0x0000,0x0000,0x28df,0x0000,0x0000,0x28e1,
+ 0x0000,0x28e3,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x28e5,0x0000,0x28e7,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x28e9,0x28eb,0x28ed,
+ 0x28ef,0x28f1,0x0000,0x0000,0x28f3,0x28f5,0x0000,0x0000,
+ 0x28f7,0x28f9,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x28fb,0x28fd,0x0000,0x0000,0x28ff,0x2901,0x0000,0x0000,
+ 0x2903,0x2905,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x2907,0x2909,0x290b,0x290d,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x290f,0x2911,0x2913,0x2915,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x2917,0x2919,0x291b,0x291d,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x091f,0x0920,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0921,0x0922,0x0923,0x0924,0x0925,0x0926,0x0927,0x0928,
+ 0x0929,0x292a,0x292c,0x292e,0x2930,0x2932,0x2934,0x2936,
+ 0x2938,0x293a,0x293c,0x293e,0x4940,0x4943,0x4946,0x4949,
+ 0x494c,0x494f,0x4952,0x4955,0x4958,0x695b,0x695f,0x6963,
+ 0x6967,0x696b,0x696f,0x6973,0x6977,0x697b,0x697f,0x6983,
+ 0x2987,0x2989,0x298b,0x298d,0x298f,0x2991,0x2993,0x2995,
+ 0x2997,0x4999,0x499c,0x499f,0x49a2,0x49a5,0x49a8,0x49ab,
+ 0x49ae,0x49b1,0x49b4,0x49b7,0x49ba,0x49bd,0x49c0,0x49c3,
+ 0x49c6,0x49c9,0x49cc,0x49cf,0x49d2,0x49d5,0x49d8,0x49db,
+ 0x49de,0x49e1,0x49e4,0x49e7,0x49ea,0x49ed,0x49f0,0x49f3,
+ 0x49f6,0x49f9,0x49fc,0x49ff,0x4a02,0x4a05,0x0a08,0x0a09,
+ 0x0a0a,0x0a0b,0x0a0c,0x0a0d,0x0a0e,0x0a0f,0x0a10,0x0a11,
+ 0x0a12,0x0a13,0x0a14,0x0a15,0x0a16,0x0a17,0x0a18,0x0a19,
+ 0x0a1a,0x0a1b,0x0a1c,0x0a1d,0x0a1e,0x0a1f,0x0a20,0x0a21,
+ 0x0a22,0x0a23,0x0a24,0x0a25,0x0a26,0x0a27,0x0a28,0x0a29,
+ 0x0a2a,0x0a2b,0x0a2c,0x0a2d,0x0a2e,0x0a2f,0x0a30,0x0a31,
+ 0x0a32,0x0a33,0x0a34,0x0a35,0x0a36,0x0a37,0x0a38,0x0a39,
+ 0x0a3a,0x0a3b,0x0a3c,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x6a3d,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x4a41,0x2a44,0x4a46,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x2a49,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0a4b,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0a4c,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0a4d,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0a4e,0x0a4f,0x0a50,0x0a51,0x0a52,0x0a53,0x0a54,0x0a55,
+ 0x0a56,0x0a57,0x0a58,0x0a59,0x0a5a,0x0a5b,0x0a5c,0x0a5d,
+ 0x0a5e,0x0a5f,0x0a60,0x0a61,0x0a62,0x0a63,0x0a64,0x0a65,
+ 0x0a66,0x0a67,0x0a68,0x0a69,0x0a6a,0x0a6b,0x0a6c,0x0a6d,
+ 0x0a6e,0x0a6f,0x0a70,0x0a71,0x0a72,0x0a73,0x0a74,0x0a75,
+ 0x0a76,0x0a77,0x0a78,0x0a79,0x0a7a,0x0a7b,0x0a7c,0x0a7d,
+ 0x0a7e,0x0a7f,0x0a80,0x0a81,0x0a82,0x0a83,0x0a84,0x0a85,
+ 0x0a86,0x0a87,0x0a88,0x0a89,0x0a8a,0x0a8b,0x0a8c,0x0a8d,
+ 0x0a8e,0x0a8f,0x0a90,0x0a91,0x0a92,0x0a93,0x0a94,0x0a95,
+ 0x0a96,0x0a97,0x0a98,0x0a99,0x0a9a,0x0a9b,0x0a9c,0x0a9d,
+ 0x0a9e,0x0a9f,0x0aa0,0x0aa1,0x0aa2,0x0aa3,0x0aa4,0x0aa5,
+ 0x0aa6,0x0aa7,0x0aa8,0x0aa9,0x0aaa,0x0aab,0x0aac,0x0aad,
+ 0x0aae,0x0aaf,0x0ab0,0x0ab1,0x0ab2,0x0ab3,0x0ab4,0x0ab5,
+ 0x0ab6,0x0ab7,0x0ab8,0x0ab9,0x0aba,0x0abb,0x0abc,0x0abd,
+ 0x0abe,0x0abf,0x0ac0,0x0ac1,0x0ac2,0x0ac3,0x0ac4,0x0ac5,
+ 0x0ac6,0x0ac7,0x0ac8,0x0ac9,0x0aca,0x0acb,0x0acc,0x0acd,
+ 0x0ace,0x0acf,0x0ad0,0x0ad1,0x0ad2,0x0ad3,0x0ad4,0x0ad5,
+ 0x0ad6,0x0ad7,0x0ad8,0x0ad9,0x0ada,0x0adb,0x0adc,0x0add,
+ 0x0ade,0x0adf,0x0ae0,0x0ae1,0x0ae2,0x0ae3,0x0ae4,0x0ae5,
+ 0x0ae6,0x0ae7,0x0ae8,0x0ae9,0x0aea,0x0aeb,0x0aec,0x0aed,
+ 0x0aee,0x0aef,0x0af0,0x0af1,0x0af2,0x0af3,0x0af4,0x0af5,
+ 0x0af6,0x0af7,0x0af8,0x0af9,0x0afa,0x0afb,0x0afc,0x0afd,
+ 0x0afe,0x0aff,0x0b00,0x0b01,0x0b02,0x0b03,0x0b04,0x0b05,
+ 0x0b06,0x0b07,0x0b08,0x0b09,0x0b0a,0x0b0b,0x0b0c,0x0b0d,
+ 0x0b0e,0x0b0f,0x0b10,0x0b11,0x0b12,0x0b13,0x0b14,0x0b15,
+ 0x0b16,0x0b17,0x0b18,0x0b19,0x0b1a,0x0b1b,0x0b1c,0x0b1d,
+ 0x0b1e,0x0b1f,0x0b20,0x0b21,0x0b22,0x0b23,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0b24,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0b25,0x0000,
+ 0x0b26,0x0b27,0x0b28,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x2b29,0x0000,0x2b2b,0x0000,
+ 0x2b2d,0x0000,0x2b2f,0x0000,0x2b31,0x0000,0x2b33,0x0000,
+ 0x2b35,0x0000,0x2b37,0x0000,0x2b39,0x0000,0x2b3b,0x0000,
+ 0x2b3d,0x0000,0x2b3f,0x0000,0x0000,0x2b41,0x0000,0x2b43,
+ 0x0000,0x2b45,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x2b47,0x2b49,0x0000,0x2b4b,0x2b4d,0x0000,0x2b4f,0x2b51,
+ 0x0000,0x2b53,0x2b55,0x0000,0x2b57,0x2b59,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x2b5b,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x2b5d,0x2b5f,0x0000,0x2b61,0x2b63,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x2b65,0x0000,0x2b67,0x0000,
+ 0x2b69,0x0000,0x2b6b,0x0000,0x2b6d,0x0000,0x2b6f,0x0000,
+ 0x2b71,0x0000,0x2b73,0x0000,0x2b75,0x0000,0x2b77,0x0000,
+ 0x2b79,0x0000,0x2b7b,0x0000,0x0000,0x2b7d,0x0000,0x2b7f,
+ 0x0000,0x2b81,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x2b83,0x2b85,0x0000,0x2b87,0x2b89,0x0000,0x2b8b,0x2b8d,
+ 0x0000,0x2b8f,0x2b91,0x0000,0x2b93,0x2b95,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x2b97,0x0000,0x0000,0x2b99,
+ 0x2b9b,0x2b9d,0x2b9f,0x0000,0x0000,0x0000,0x2ba1,0x2ba3,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0ba5,0x0ba6,0x0ba7,0x0ba8,0x0ba9,0x0baa,0x0bab,
+ 0x0bac,0x0bad,0x0bae,0x0baf,0x0bb0,0x0bb1,0x0bb2,0x0bb3,
+ 0x0bb4,0x0bb5,0x0bb6,0x0bb7,0x0bb8,0x0bb9,0x0bba,0x0bbb,
+ 0x0bbc,0x0bbd,0x0bbe,0x0bbf,0x0bc0,0x0bc1,0x0bc2,0x0bc3,
+ 0x0bc4,0x0bc5,0x0bc6,0x0bc7,0x0bc8,0x0bc9,0x0bca,0x0bcb,
+ 0x0bcc,0x0bcd,0x0bce,0x0bcf,0x0bd0,0x0bd1,0x0bd2,0x0bd3,
+ 0x0bd4,0x0bd5,0x0bd6,0x0bd7,0x0bd8,0x0bd9,0x0bda,0x0bdb,
+ 0x0bdc,0x0bdd,0x0bde,0x0bdf,0x0be0,0x0be1,0x0be2,0x0be3,
+ 0x0be4,0x0be5,0x0be6,0x0be7,0x0be8,0x0be9,0x0bea,0x0beb,
+ 0x0bec,0x0bed,0x0bee,0x0bef,0x0bf0,0x0bf1,0x0bf2,0x0bf3,
+ 0x0bf4,0x0bf5,0x0bf6,0x0bf7,0x0bf8,0x0bf9,0x0bfa,0x0bfb,
+ 0x0bfc,0x0bfd,0x0bfe,0x0bff,0x0c00,0x0c01,0x0c02,0x0000,
+ 0x0000,0x0000,0x0c03,0x0c04,0x0c05,0x0c06,0x0c07,0x0c08,
+ 0x0c09,0x0c0a,0x0c0b,0x0c0c,0x0c0d,0x0c0e,0x0c0f,0x0c10,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x4c11,0x4c14,0x4c17,0x4c1a,0x4c1d,0x4c20,0x4c23,0x4c26,
+ 0x4c29,0x4c2c,0x4c2f,0x4c32,0x4c35,0x4c38,0x6c3b,0x6c3f,
+ 0x6c43,0x6c47,0x6c4b,0x6c4f,0x6c53,0x6c57,0x6c5b,0x6c5f,
+ 0x6c63,0x6c67,0x6c6b,0x6c6f,0x6c73,0xcc77,0xac7e,0x0000,
+ 0x4c84,0x4c87,0x4c8a,0x4c8d,0x4c90,0x4c93,0x4c96,0x4c99,
+ 0x4c9c,0x4c9f,0x4ca2,0x4ca5,0x4ca8,0x4cab,0x4cae,0x4cb1,
+ 0x4cb4,0x4cb7,0x4cba,0x4cbd,0x4cc0,0x4cc3,0x4cc6,0x4cc9,
+ 0x4ccc,0x4ccf,0x4cd2,0x4cd5,0x4cd8,0x4cdb,0x4cde,0x4ce1,
+ 0x4ce4,0x4ce7,0x4cea,0x4ced,0x0000,0x0000,0x0000,0x0000,
+ 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x4cf0,0x2cf3,0x2cf5,0x2cf7,0x2cf9,0x2cfb,0x2cfd,0x2cff,
+ 0x2d01,0x2d03,0x2d05,0x2d07,0x2d09,0x2d0b,0x2d0d,0x2d0f,
+ 0x0d11,0x0d12,0x0d13,0x0d14,0x0d15,0x0d16,0x0d17,0x0d18,
+ 0x0d19,0x0d1a,0x0d1b,0x0d1c,0x0d1d,0x0d1e,0x2d1f,0x2d21,
+ 0x2d23,0x2d25,0x2d27,0x2d29,0x2d2b,0x2d2d,0x2d2f,0x2d31,
+ 0x2d33,0x2d35,0x2d37,0x2d39,0x8d3b,0x6d40,0x2d44,0x0000,
+ 0x0d46,0x0d47,0x0d48,0x0d49,0x0d4a,0x0d4b,0x0d4c,0x0d4d,
+ 0x0d4e,0x0d4f,0x0d50,0x0d51,0x0d52,0x0d53,0x0d54,0x0d55,
+ 0x0d56,0x0d57,0x0d58,0x0d59,0x0d5a,0x0d5b,0x0d5c,0x0d5d,
+ 0x0d5e,0x0d5f,0x0d60,0x0d61,0x0d62,0x0d63,0x0d64,0x0d65,
+ 0x0d66,0x0d67,0x0d68,0x0d69,0x0d6a,0x0d6b,0x0d6c,0x0d6d,
+ 0x0d6e,0x0d6f,0x0d70,0x0d71,0x0d72,0x0d73,0x0d74,0x0d75,
+ 0x0d76,0x2d77,0x2d79,0x2d7b,0x2d7d,0x2d7f,0x2d81,0x2d83,
+ 0x2d85,0x2d87,0x2d89,0x2d8b,0x2d8d,0x2d8f,0x2d91,0x2d93,
+ 0x2d95,0x2d97,0x2d99,0x2d9b,0x2d9d,0x2d9f,0x2da1,0x2da3,
+ 0x2da5,0x4da7,0x4daa,0x4dad,0x2db0,0x4db2,0x2db5,0x4db7,
+ 0x0dba,0x0dbb,0x0dbc,0x0dbd,0x0dbe,0x0dbf,0x0dc0,0x0dc1,
+ 0x0dc2,0x0dc3,0x0dc4,0x0dc5,0x0dc6,0x0dc7,0x0dc8,0x0dc9,
+ 0x0dca,0x0dcb,0x0dcc,0x0dcd,0x0dce,0x0dcf,0x0dd0,0x0dd1,
+ 0x0dd2,0x0dd3,0x0dd4,0x0dd5,0x0dd6,0x0dd7,0x0dd8,0x0dd9,
+ 0x0dda,0x0ddb,0x0ddc,0x0ddd,0x0dde,0x0ddf,0x0de0,0x0de1,
+ 0x0de2,0x0de3,0x0de4,0x0de5,0x0de6,0x0de7,0x0de8,0x0000,
+ 0x6de9,0x6ded,0x6df1,0x4df5,0x6df8,0x4dfc,0x4dff,0x8e02,
+ 0x6e07,0x4e0b,0x4e0e,0x4e11,0x6e14,0x6e18,0x4e1c,0x4e1f,
+ 0x2e22,0x4e24,0x6e27,0x6e2b,0x2e2f,0x8e31,0xae36,0x8e3c,
+ 0x4e41,0x8e44,0x8e49,0x6e4e,0x4e52,0x4e55,0x4e58,0x6e5b,
+ 0x8e5f,0x6e64,0x4e68,0x4e6b,0x4e6e,0x2e71,0x2e73,0x2e75,
+ 0x2e77,0x4e79,0x4e7c,0x8e7f,0x4e84,0x6e87,0x8e8b,0x4e90,
+ 0x2e93,0x2e95,0x8e97,0x6e9c,0x8ea0,0x4ea5,0x8ea8,0x2ead,
+ 0x4eaf,0x4eb2,0x4eb5,0x4eb8,0x4ebb,0x6ebe,0x4ec2,0x2ec5,
+ 0x4ec7,0x4eca,0x4ecd,0x6ed0,0x4ed4,0x4ed7,0x4eda,0x8edd,
+ 0x6ee2,0x2ee6,0x8ee8,0x2eed,0x6eef,0x6ef3,0x4ef7,0x4efa,
+ 0x4efd,0x6f00,0x2f04,0x4f06,0x6f09,0x2f0d,0x8f0f,0x4f14,
+ 0x2f17,0x2f19,0x2f1b,0x2f1d,0x2f1f,0x2f21,0x2f23,0x2f25,
+ 0x2f27,0x2f29,0x4f2b,0x4f2e,0x4f31,0x4f34,0x4f37,0x4f3a,
+ 0x4f3d,0x4f40,0x4f43,0x4f46,0x4f49,0x4f4c,0x4f4f,0x4f52,
+ 0x4f55,0x4f58,0x2f5b,0x2f5d,0x4f5f,0x2f62,0x2f64,0x2f66,
+ 0x4f68,0x4f6b,0x2f6e,0x2f70,0x2f72,0x2f74,0x2f76,0x6f78,
+ 0x2f7c,0x2f7e,0x2f80,0x2f82,0x2f84,0x2f86,0x2f88,0x2f8a,
+ 0x4f8c,0x6f8f,0x2f93,0x2f95,0x2f97,0x2f99,0x2f9b,0x2f9d,
+ 0x2f9f,0x4fa1,0x4fa4,0x4fa7,0x4faa,0x2fad,0x2faf,0x2fb1,
+ 0x2fb3,0x2fb5,0x2fb7,0x2fb9,0x2fbb,0x2fbd,0x2fbf,0x4fc1,
+ 0x4fc4,0x2fc7,0x4fc9,0x4fcc,0x4fcf,0x2fd2,0x4fd4,0x4fd7,
+ 0x6fda,0x2fde,0x4fe0,0x4fe3,0x4fe6,0x4fe9,0x8fec,0xaff1,
+ 0x2ff7,0x2ff9,0x2ffb,0x2ffd,0x2fff,0x3001,0x3003,0x3005,
+ 0x3007,0x3009,0x300b,0x300d,0x300f,0x3011,0x3013,0x3015,
+ 0x3017,0x3019,0x701b,0x301f,0x3021,0x3023,0x7025,0x5029,
+ 0x302c,0x302e,0x3030,0x3032,0x3034,0x3036,0x3038,0x303a,
+ 0x303c,0x303e,0x5040,0x3043,0x3045,0x5047,0x504a,0x304d,
+ 0x704f,0x5053,0x3056,0x3058,0x305a,0x305c,0x505e,0x5061,
+ 0x3064,0x3066,0x3068,0x306a,0x306c,0x306e,0x3070,0x3072,
+ 0x3074,0x5076,0x5079,0x507c,0x507f,0x5082,0x5085,0x5088,
+ 0x508b,0x508e,0x5091,0x5094,0x5097,0x509a,0x509d,0x50a0,
+ 0x50a3,0x50a6,0x50a9,0x50ac,0x50af,0x50b2,0x50b5,0x50b8,
+};
+
+ /* BMP low decompositions */
+static unsigned short ucs4_dbmplotab[4283] = {
+ 0x0020,0x0020,0x0308,0x0061,0x0020,0x0304,0x0032,0x0033,
+ 0x0020,0x0301,0x03bc,0x0020,0x0327,0x0031,0x006f,0x0031,
+ 0x2044,0x0034,0x0031,0x2044,0x0032,0x0033,0x2044,0x0034,
+ 0x0041,0x0300,0x0041,0x0301,0x0041,0x0302,0x0041,0x0303,
+ 0x0041,0x0308,0x0041,0x030a,0x0043,0x0327,0x0045,0x0300,
+ 0x0045,0x0301,0x0045,0x0302,0x0045,0x0308,0x0049,0x0300,
+ 0x0049,0x0301,0x0049,0x0302,0x0049,0x0308,0x004e,0x0303,
+ 0x004f,0x0300,0x004f,0x0301,0x004f,0x0302,0x004f,0x0303,
+ 0x004f,0x0308,0x0055,0x0300,0x0055,0x0301,0x0055,0x0302,
+ 0x0055,0x0308,0x0059,0x0301,0x0061,0x0300,0x0061,0x0301,
+ 0x0061,0x0302,0x0061,0x0303,0x0061,0x0308,0x0061,0x030a,
+ 0x0063,0x0327,0x0065,0x0300,0x0065,0x0301,0x0065,0x0302,
+ 0x0065,0x0308,0x0069,0x0300,0x0069,0x0301,0x0069,0x0302,
+ 0x0069,0x0308,0x006e,0x0303,0x006f,0x0300,0x006f,0x0301,
+ 0x006f,0x0302,0x006f,0x0303,0x006f,0x0308,0x0075,0x0300,
+ 0x0075,0x0301,0x0075,0x0302,0x0075,0x0308,0x0079,0x0301,
+ 0x0079,0x0308,0x0041,0x0304,0x0061,0x0304,0x0041,0x0306,
+ 0x0061,0x0306,0x0041,0x0328,0x0061,0x0328,0x0043,0x0301,
+ 0x0063,0x0301,0x0043,0x0302,0x0063,0x0302,0x0043,0x0307,
+ 0x0063,0x0307,0x0043,0x030c,0x0063,0x030c,0x0044,0x030c,
+ 0x0064,0x030c,0x0045,0x0304,0x0065,0x0304,0x0045,0x0306,
+ 0x0065,0x0306,0x0045,0x0307,0x0065,0x0307,0x0045,0x0328,
+ 0x0065,0x0328,0x0045,0x030c,0x0065,0x030c,0x0047,0x0302,
+ 0x0067,0x0302,0x0047,0x0306,0x0067,0x0306,0x0047,0x0307,
+ 0x0067,0x0307,0x0047,0x0327,0x0067,0x0327,0x0048,0x0302,
+ 0x0068,0x0302,0x0049,0x0303,0x0069,0x0303,0x0049,0x0304,
+ 0x0069,0x0304,0x0049,0x0306,0x0069,0x0306,0x0049,0x0328,
+ 0x0069,0x0328,0x0049,0x0307,0x0049,0x004a,0x0069,0x006a,
+ 0x004a,0x0302,0x006a,0x0302,0x004b,0x0327,0x006b,0x0327,
+ 0x004c,0x0301,0x006c,0x0301,0x004c,0x0327,0x006c,0x0327,
+ 0x004c,0x030c,0x006c,0x030c,0x004c,0x00b7,0x006c,0x00b7,
+ 0x004e,0x0301,0x006e,0x0301,0x004e,0x0327,0x006e,0x0327,
+ 0x004e,0x030c,0x006e,0x030c,0x02bc,0x006e,0x004f,0x0304,
+ 0x006f,0x0304,0x004f,0x0306,0x006f,0x0306,0x004f,0x030b,
+ 0x006f,0x030b,0x0052,0x0301,0x0072,0x0301,0x0052,0x0327,
+ 0x0072,0x0327,0x0052,0x030c,0x0072,0x030c,0x0053,0x0301,
+ 0x0073,0x0301,0x0053,0x0302,0x0073,0x0302,0x0053,0x0327,
+ 0x0073,0x0327,0x0053,0x030c,0x0073,0x030c,0x0054,0x0327,
+ 0x0074,0x0327,0x0054,0x030c,0x0074,0x030c,0x0055,0x0303,
+ 0x0075,0x0303,0x0055,0x0304,0x0075,0x0304,0x0055,0x0306,
+ 0x0075,0x0306,0x0055,0x030a,0x0075,0x030a,0x0055,0x030b,
+ 0x0075,0x030b,0x0055,0x0328,0x0075,0x0328,0x0057,0x0302,
+ 0x0077,0x0302,0x0059,0x0302,0x0079,0x0302,0x0059,0x0308,
+ 0x005a,0x0301,0x007a,0x0301,0x005a,0x0307,0x007a,0x0307,
+ 0x005a,0x030c,0x007a,0x030c,0x0073,0x004f,0x031b,0x006f,
+ 0x031b,0x0055,0x031b,0x0075,0x031b,0x0044,0x017d,0x0044,
+ 0x017e,0x0064,0x017e,0x004c,0x004a,0x004c,0x006a,0x006c,
+ 0x006a,0x004e,0x004a,0x004e,0x006a,0x006e,0x006a,0x0041,
+ 0x030c,0x0061,0x030c,0x0049,0x030c,0x0069,0x030c,0x004f,
+ 0x030c,0x006f,0x030c,0x0055,0x030c,0x0075,0x030c,0x00dc,
+ 0x0304,0x00fc,0x0304,0x00dc,0x0301,0x00fc,0x0301,0x00dc,
+ 0x030c,0x00fc,0x030c,0x00dc,0x0300,0x00fc,0x0300,0x00c4,
+ 0x0304,0x00e4,0x0304,0x0226,0x0304,0x0227,0x0304,0x00c6,
+ 0x0304,0x00e6,0x0304,0x0047,0x030c,0x0067,0x030c,0x004b,
+ 0x030c,0x006b,0x030c,0x004f,0x0328,0x006f,0x0328,0x01ea,
+ 0x0304,0x01eb,0x0304,0x01b7,0x030c,0x0292,0x030c,0x006a,
+ 0x030c,0x0044,0x005a,0x0044,0x007a,0x0064,0x007a,0x0047,
+ 0x0301,0x0067,0x0301,0x004e,0x0300,0x006e,0x0300,0x00c5,
+ 0x0301,0x00e5,0x0301,0x00c6,0x0301,0x00e6,0x0301,0x00d8,
+ 0x0301,0x00f8,0x0301,0x0041,0x030f,0x0061,0x030f,0x0041,
+ 0x0311,0x0061,0x0311,0x0045,0x030f,0x0065,0x030f,0x0045,
+ 0x0311,0x0065,0x0311,0x0049,0x030f,0x0069,0x030f,0x0049,
+ 0x0311,0x0069,0x0311,0x004f,0x030f,0x006f,0x030f,0x004f,
+ 0x0311,0x006f,0x0311,0x0052,0x030f,0x0072,0x030f,0x0052,
+ 0x0311,0x0072,0x0311,0x0055,0x030f,0x0075,0x030f,0x0055,
+ 0x0311,0x0075,0x0311,0x0053,0x0326,0x0073,0x0326,0x0054,
+ 0x0326,0x0074,0x0326,0x0048,0x030c,0x0068,0x030c,0x0041,
+ 0x0307,0x0061,0x0307,0x0045,0x0327,0x0065,0x0327,0x00d6,
+ 0x0304,0x00f6,0x0304,0x00d5,0x0304,0x00f5,0x0304,0x004f,
+ 0x0307,0x006f,0x0307,0x022e,0x0304,0x022f,0x0304,0x0059,
+ 0x0304,0x0079,0x0304,0x0068,0x0266,0x006a,0x0072,0x0279,
+ 0x027b,0x0281,0x0077,0x0079,0x0020,0x0306,0x0020,0x0307,
+ 0x0020,0x030a,0x0020,0x0328,0x0020,0x0303,0x0020,0x030b,
+ 0x0263,0x006c,0x0073,0x0078,0x0295,0x0300,0x0301,0x0313,
+ 0x0308,0x0301,0x02b9,0x0020,0x0345,0x003b,0x0020,0x0301,
+ 0x00a8,0x0301,0x0391,0x0301,0x00b7,0x0395,0x0301,0x0397,
+ 0x0301,0x0399,0x0301,0x039f,0x0301,0x03a5,0x0301,0x03a9,
+ 0x0301,0x03ca,0x0301,0x0399,0x0308,0x03a5,0x0308,0x03b1,
+ 0x0301,0x03b5,0x0301,0x03b7,0x0301,0x03b9,0x0301,0x03cb,
+ 0x0301,0x03b9,0x0308,0x03c5,0x0308,0x03bf,0x0301,0x03c5,
+ 0x0301,0x03c9,0x0301,0x03b2,0x03b8,0x03a5,0x03d2,0x0301,
+ 0x03d2,0x0308,0x03c6,0x03c0,0x03ba,0x03c1,0x03c2,0x0398,
+ 0x03b5,0x03a3,0x0415,0x0300,0x0415,0x0308,0x0413,0x0301,
+ 0x0406,0x0308,0x041a,0x0301,0x0418,0x0300,0x0423,0x0306,
+ 0x0418,0x0306,0x0438,0x0306,0x0435,0x0300,0x0435,0x0308,
+ 0x0433,0x0301,0x0456,0x0308,0x043a,0x0301,0x0438,0x0300,
+ 0x0443,0x0306,0x0474,0x030f,0x0475,0x030f,0x0416,0x0306,
+ 0x0436,0x0306,0x0410,0x0306,0x0430,0x0306,0x0410,0x0308,
+ 0x0430,0x0308,0x0415,0x0306,0x0435,0x0306,0x04d8,0x0308,
+ 0x04d9,0x0308,0x0416,0x0308,0x0436,0x0308,0x0417,0x0308,
+ 0x0437,0x0308,0x0418,0x0304,0x0438,0x0304,0x0418,0x0308,
+ 0x0438,0x0308,0x041e,0x0308,0x043e,0x0308,0x04e8,0x0308,
+ 0x04e9,0x0308,0x042d,0x0308,0x044d,0x0308,0x0423,0x0304,
+ 0x0443,0x0304,0x0423,0x0308,0x0443,0x0308,0x0423,0x030b,
+ 0x0443,0x030b,0x0427,0x0308,0x0447,0x0308,0x042b,0x0308,
+ 0x044b,0x0308,0x0565,0x0582,0x0627,0x0653,0x0627,0x0654,
+ 0x0648,0x0654,0x0627,0x0655,0x064a,0x0654,0x0627,0x0674,
+ 0x0648,0x0674,0x06c7,0x0674,0x064a,0x0674,0x06d5,0x0654,
+ 0x06c1,0x0654,0x06d2,0x0654,0x0928,0x093c,0x0930,0x093c,
+ 0x0933,0x093c,0x0915,0x093c,0x0916,0x093c,0x0917,0x093c,
+ 0x091c,0x093c,0x0921,0x093c,0x0922,0x093c,0x092b,0x093c,
+ 0x092f,0x093c,0x09c7,0x09be,0x09c7,0x09d7,0x09a1,0x09bc,
+ 0x09a2,0x09bc,0x09af,0x09bc,0x0a32,0x0a3c,0x0a38,0x0a3c,
+ 0x0a16,0x0a3c,0x0a17,0x0a3c,0x0a1c,0x0a3c,0x0a2b,0x0a3c,
+ 0x0b47,0x0b56,0x0b47,0x0b3e,0x0b47,0x0b57,0x0b21,0x0b3c,
+ 0x0b22,0x0b3c,0x0b92,0x0bd7,0x0bc6,0x0bbe,0x0bc7,0x0bbe,
+ 0x0bc6,0x0bd7,0x0c46,0x0c56,0x0cbf,0x0cd5,0x0cc6,0x0cd5,
+ 0x0cc6,0x0cd6,0x0cc6,0x0cc2,0x0cca,0x0cd5,0x0d46,0x0d3e,
+ 0x0d47,0x0d3e,0x0d46,0x0d57,0x0dd9,0x0dca,0x0dd9,0x0dcf,
+ 0x0ddc,0x0dca,0x0dd9,0x0ddf,0x0e4d,0x0e32,0x0ecd,0x0eb2,
+ 0x0eab,0x0e99,0x0eab,0x0ea1,0x0f0b,0x0f42,0x0fb7,0x0f4c,
+ 0x0fb7,0x0f51,0x0fb7,0x0f56,0x0fb7,0x0f5b,0x0fb7,0x0f40,
+ 0x0fb5,0x0f71,0x0f72,0x0f71,0x0f74,0x0fb2,0x0f80,0x0fb2,
+ 0x0f81,0x0fb3,0x0f80,0x0fb3,0x0f81,0x0f71,0x0f80,0x0f92,
+ 0x0fb7,0x0f9c,0x0fb7,0x0fa1,0x0fb7,0x0fa6,0x0fb7,0x0fab,
+ 0x0fb7,0x0f90,0x0fb5,0x1025,0x102e,0x10dc,0x1b05,0x1b35,
+ 0x1b07,0x1b35,0x1b09,0x1b35,0x1b0b,0x1b35,0x1b0d,0x1b35,
+ 0x1b11,0x1b35,0x1b3a,0x1b35,0x1b3c,0x1b35,0x1b3e,0x1b35,
+ 0x1b3f,0x1b35,0x1b42,0x1b35,0x0041,0x00c6,0x0042,0x0044,
+ 0x0045,0x018e,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,
+ 0x004d,0x004e,0x004f,0x0222,0x0050,0x0052,0x0054,0x0055,
+ 0x0057,0x0061,0x0250,0x0251,0x1d02,0x0062,0x0064,0x0065,
+ 0x0259,0x025b,0x025c,0x0067,0x006b,0x006d,0x014b,0x006f,
+ 0x0254,0x1d16,0x1d17,0x0070,0x0074,0x0075,0x1d1d,0x026f,
+ 0x0076,0x1d25,0x03b2,0x03b3,0x03b4,0x03c6,0x03c7,0x0069,
+ 0x0072,0x0075,0x0076,0x03b2,0x03b3,0x03c1,0x03c6,0x03c7,
+ 0x043d,0x0252,0x0063,0x0255,0x00f0,0x025c,0x0066,0x025f,
+ 0x0261,0x0265,0x0268,0x0269,0x026a,0x1d7b,0x029d,0x026d,
+ 0x1d85,0x029f,0x0271,0x0270,0x0272,0x0273,0x0274,0x0275,
+ 0x0278,0x0282,0x0283,0x01ab,0x0289,0x028a,0x1d1c,0x028b,
+ 0x028c,0x007a,0x0290,0x0291,0x0292,0x03b8,0x0041,0x0325,
+ 0x0061,0x0325,0x0042,0x0307,0x0062,0x0307,0x0042,0x0323,
+ 0x0062,0x0323,0x0042,0x0331,0x0062,0x0331,0x00c7,0x0301,
+ 0x00e7,0x0301,0x0044,0x0307,0x0064,0x0307,0x0044,0x0323,
+ 0x0064,0x0323,0x0044,0x0331,0x0064,0x0331,0x0044,0x0327,
+ 0x0064,0x0327,0x0044,0x032d,0x0064,0x032d,0x0112,0x0300,
+ 0x0113,0x0300,0x0112,0x0301,0x0113,0x0301,0x0045,0x032d,
+ 0x0065,0x032d,0x0045,0x0330,0x0065,0x0330,0x0228,0x0306,
+ 0x0229,0x0306,0x0046,0x0307,0x0066,0x0307,0x0047,0x0304,
+ 0x0067,0x0304,0x0048,0x0307,0x0068,0x0307,0x0048,0x0323,
+ 0x0068,0x0323,0x0048,0x0308,0x0068,0x0308,0x0048,0x0327,
+ 0x0068,0x0327,0x0048,0x032e,0x0068,0x032e,0x0049,0x0330,
+ 0x0069,0x0330,0x00cf,0x0301,0x00ef,0x0301,0x004b,0x0301,
+ 0x006b,0x0301,0x004b,0x0323,0x006b,0x0323,0x004b,0x0331,
+ 0x006b,0x0331,0x004c,0x0323,0x006c,0x0323,0x1e36,0x0304,
+ 0x1e37,0x0304,0x004c,0x0331,0x006c,0x0331,0x004c,0x032d,
+ 0x006c,0x032d,0x004d,0x0301,0x006d,0x0301,0x004d,0x0307,
+ 0x006d,0x0307,0x004d,0x0323,0x006d,0x0323,0x004e,0x0307,
+ 0x006e,0x0307,0x004e,0x0323,0x006e,0x0323,0x004e,0x0331,
+ 0x006e,0x0331,0x004e,0x032d,0x006e,0x032d,0x00d5,0x0301,
+ 0x00f5,0x0301,0x00d5,0x0308,0x00f5,0x0308,0x014c,0x0300,
+ 0x014d,0x0300,0x014c,0x0301,0x014d,0x0301,0x0050,0x0301,
+ 0x0070,0x0301,0x0050,0x0307,0x0070,0x0307,0x0052,0x0307,
+ 0x0072,0x0307,0x0052,0x0323,0x0072,0x0323,0x1e5a,0x0304,
+ 0x1e5b,0x0304,0x0052,0x0331,0x0072,0x0331,0x0053,0x0307,
+ 0x0073,0x0307,0x0053,0x0323,0x0073,0x0323,0x015a,0x0307,
+ 0x015b,0x0307,0x0160,0x0307,0x0161,0x0307,0x1e62,0x0307,
+ 0x1e63,0x0307,0x0054,0x0307,0x0074,0x0307,0x0054,0x0323,
+ 0x0074,0x0323,0x0054,0x0331,0x0074,0x0331,0x0054,0x032d,
+ 0x0074,0x032d,0x0055,0x0324,0x0075,0x0324,0x0055,0x0330,
+ 0x0075,0x0330,0x0055,0x032d,0x0075,0x032d,0x0168,0x0301,
+ 0x0169,0x0301,0x016a,0x0308,0x016b,0x0308,0x0056,0x0303,
+ 0x0076,0x0303,0x0056,0x0323,0x0076,0x0323,0x0057,0x0300,
+ 0x0077,0x0300,0x0057,0x0301,0x0077,0x0301,0x0057,0x0308,
+ 0x0077,0x0308,0x0057,0x0307,0x0077,0x0307,0x0057,0x0323,
+ 0x0077,0x0323,0x0058,0x0307,0x0078,0x0307,0x0058,0x0308,
+ 0x0078,0x0308,0x0059,0x0307,0x0079,0x0307,0x005a,0x0302,
+ 0x007a,0x0302,0x005a,0x0323,0x007a,0x0323,0x005a,0x0331,
+ 0x007a,0x0331,0x0068,0x0331,0x0074,0x0308,0x0077,0x030a,
+ 0x0079,0x030a,0x0061,0x02be,0x017f,0x0307,0x0041,0x0323,
+ 0x0061,0x0323,0x0041,0x0309,0x0061,0x0309,0x00c2,0x0301,
+ 0x00e2,0x0301,0x00c2,0x0300,0x00e2,0x0300,0x00c2,0x0309,
+ 0x00e2,0x0309,0x00c2,0x0303,0x00e2,0x0303,0x1ea0,0x0302,
+ 0x1ea1,0x0302,0x0102,0x0301,0x0103,0x0301,0x0102,0x0300,
+ 0x0103,0x0300,0x0102,0x0309,0x0103,0x0309,0x0102,0x0303,
+ 0x0103,0x0303,0x1ea0,0x0306,0x1ea1,0x0306,0x0045,0x0323,
+ 0x0065,0x0323,0x0045,0x0309,0x0065,0x0309,0x0045,0x0303,
+ 0x0065,0x0303,0x00ca,0x0301,0x00ea,0x0301,0x00ca,0x0300,
+ 0x00ea,0x0300,0x00ca,0x0309,0x00ea,0x0309,0x00ca,0x0303,
+ 0x00ea,0x0303,0x1eb8,0x0302,0x1eb9,0x0302,0x0049,0x0309,
+ 0x0069,0x0309,0x0049,0x0323,0x0069,0x0323,0x004f,0x0323,
+ 0x006f,0x0323,0x004f,0x0309,0x006f,0x0309,0x00d4,0x0301,
+ 0x00f4,0x0301,0x00d4,0x0300,0x00f4,0x0300,0x00d4,0x0309,
+ 0x00f4,0x0309,0x00d4,0x0303,0x00f4,0x0303,0x1ecc,0x0302,
+ 0x1ecd,0x0302,0x01a0,0x0301,0x01a1,0x0301,0x01a0,0x0300,
+ 0x01a1,0x0300,0x01a0,0x0309,0x01a1,0x0309,0x01a0,0x0303,
+ 0x01a1,0x0303,0x01a0,0x0323,0x01a1,0x0323,0x0055,0x0323,
+ 0x0075,0x0323,0x0055,0x0309,0x0075,0x0309,0x01af,0x0301,
+ 0x01b0,0x0301,0x01af,0x0300,0x01b0,0x0300,0x01af,0x0309,
+ 0x01b0,0x0309,0x01af,0x0303,0x01b0,0x0303,0x01af,0x0323,
+ 0x01b0,0x0323,0x0059,0x0300,0x0079,0x0300,0x0059,0x0323,
+ 0x0079,0x0323,0x0059,0x0309,0x0079,0x0309,0x0059,0x0303,
+ 0x0079,0x0303,0x03b1,0x0313,0x03b1,0x0314,0x1f00,0x0300,
+ 0x1f01,0x0300,0x1f00,0x0301,0x1f01,0x0301,0x1f00,0x0342,
+ 0x1f01,0x0342,0x0391,0x0313,0x0391,0x0314,0x1f08,0x0300,
+ 0x1f09,0x0300,0x1f08,0x0301,0x1f09,0x0301,0x1f08,0x0342,
+ 0x1f09,0x0342,0x03b5,0x0313,0x03b5,0x0314,0x1f10,0x0300,
+ 0x1f11,0x0300,0x1f10,0x0301,0x1f11,0x0301,0x0395,0x0313,
+ 0x0395,0x0314,0x1f18,0x0300,0x1f19,0x0300,0x1f18,0x0301,
+ 0x1f19,0x0301,0x03b7,0x0313,0x03b7,0x0314,0x1f20,0x0300,
+ 0x1f21,0x0300,0x1f20,0x0301,0x1f21,0x0301,0x1f20,0x0342,
+ 0x1f21,0x0342,0x0397,0x0313,0x0397,0x0314,0x1f28,0x0300,
+ 0x1f29,0x0300,0x1f28,0x0301,0x1f29,0x0301,0x1f28,0x0342,
+ 0x1f29,0x0342,0x03b9,0x0313,0x03b9,0x0314,0x1f30,0x0300,
+ 0x1f31,0x0300,0x1f30,0x0301,0x1f31,0x0301,0x1f30,0x0342,
+ 0x1f31,0x0342,0x0399,0x0313,0x0399,0x0314,0x1f38,0x0300,
+ 0x1f39,0x0300,0x1f38,0x0301,0x1f39,0x0301,0x1f38,0x0342,
+ 0x1f39,0x0342,0x03bf,0x0313,0x03bf,0x0314,0x1f40,0x0300,
+ 0x1f41,0x0300,0x1f40,0x0301,0x1f41,0x0301,0x039f,0x0313,
+ 0x039f,0x0314,0x1f48,0x0300,0x1f49,0x0300,0x1f48,0x0301,
+ 0x1f49,0x0301,0x03c5,0x0313,0x03c5,0x0314,0x1f50,0x0300,
+ 0x1f51,0x0300,0x1f50,0x0301,0x1f51,0x0301,0x1f50,0x0342,
+ 0x1f51,0x0342,0x03a5,0x0314,0x1f59,0x0300,0x1f59,0x0301,
+ 0x1f59,0x0342,0x03c9,0x0313,0x03c9,0x0314,0x1f60,0x0300,
+ 0x1f61,0x0300,0x1f60,0x0301,0x1f61,0x0301,0x1f60,0x0342,
+ 0x1f61,0x0342,0x03a9,0x0313,0x03a9,0x0314,0x1f68,0x0300,
+ 0x1f69,0x0300,0x1f68,0x0301,0x1f69,0x0301,0x1f68,0x0342,
+ 0x1f69,0x0342,0x03b1,0x0300,0x03ac,0x03b5,0x0300,0x03ad,
+ 0x03b7,0x0300,0x03ae,0x03b9,0x0300,0x03af,0x03bf,0x0300,
+ 0x03cc,0x03c5,0x0300,0x03cd,0x03c9,0x0300,0x03ce,0x1f00,
+ 0x0345,0x1f01,0x0345,0x1f02,0x0345,0x1f03,0x0345,0x1f04,
+ 0x0345,0x1f05,0x0345,0x1f06,0x0345,0x1f07,0x0345,0x1f08,
+ 0x0345,0x1f09,0x0345,0x1f0a,0x0345,0x1f0b,0x0345,0x1f0c,
+ 0x0345,0x1f0d,0x0345,0x1f0e,0x0345,0x1f0f,0x0345,0x1f20,
+ 0x0345,0x1f21,0x0345,0x1f22,0x0345,0x1f23,0x0345,0x1f24,
+ 0x0345,0x1f25,0x0345,0x1f26,0x0345,0x1f27,0x0345,0x1f28,
+ 0x0345,0x1f29,0x0345,0x1f2a,0x0345,0x1f2b,0x0345,0x1f2c,
+ 0x0345,0x1f2d,0x0345,0x1f2e,0x0345,0x1f2f,0x0345,0x1f60,
+ 0x0345,0x1f61,0x0345,0x1f62,0x0345,0x1f63,0x0345,0x1f64,
+ 0x0345,0x1f65,0x0345,0x1f66,0x0345,0x1f67,0x0345,0x1f68,
+ 0x0345,0x1f69,0x0345,0x1f6a,0x0345,0x1f6b,0x0345,0x1f6c,
+ 0x0345,0x1f6d,0x0345,0x1f6e,0x0345,0x1f6f,0x0345,0x03b1,
+ 0x0306,0x03b1,0x0304,0x1f70,0x0345,0x03b1,0x0345,0x03ac,
+ 0x0345,0x03b1,0x0342,0x1fb6,0x0345,0x0391,0x0306,0x0391,
+ 0x0304,0x0391,0x0300,0x0386,0x0391,0x0345,0x0020,0x0313,
+ 0x03b9,0x0020,0x0313,0x0020,0x0342,0x00a8,0x0342,0x1f74,
+ 0x0345,0x03b7,0x0345,0x03ae,0x0345,0x03b7,0x0342,0x1fc6,
+ 0x0345,0x0395,0x0300,0x0388,0x0397,0x0300,0x0389,0x0397,
+ 0x0345,0x1fbf,0x0300,0x1fbf,0x0301,0x1fbf,0x0342,0x03b9,
+ 0x0306,0x03b9,0x0304,0x03ca,0x0300,0x0390,0x03b9,0x0342,
+ 0x03ca,0x0342,0x0399,0x0306,0x0399,0x0304,0x0399,0x0300,
+ 0x038a,0x1ffe,0x0300,0x1ffe,0x0301,0x1ffe,0x0342,0x03c5,
+ 0x0306,0x03c5,0x0304,0x03cb,0x0300,0x03b0,0x03c1,0x0313,
+ 0x03c1,0x0314,0x03c5,0x0342,0x03cb,0x0342,0x03a5,0x0306,
+ 0x03a5,0x0304,0x03a5,0x0300,0x038e,0x03a1,0x0314,0x00a8,
+ 0x0300,0x0385,0x0060,0x1f7c,0x0345,0x03c9,0x0345,0x03ce,
+ 0x0345,0x03c9,0x0342,0x1ff6,0x0345,0x039f,0x0300,0x038c,
+ 0x03a9,0x0300,0x038f,0x03a9,0x0345,0x00b4,0x0020,0x0314,
+ 0x2002,0x2003,0x0020,0x0020,0x0020,0x0020,0x0020,0x0020,
+ 0x0020,0x0020,0x0020,0x2010,0x0020,0x0333,0x002e,0x002e,
+ 0x002e,0x002e,0x002e,0x002e,0x0020,0x2032,0x2032,0x2032,
+ 0x2032,0x2032,0x2035,0x2035,0x2035,0x2035,0x2035,0x0021,
+ 0x0021,0x0020,0x0305,0x003f,0x003f,0x003f,0x0021,0x0021,
+ 0x003f,0x2032,0x2032,0x2032,0x2032,0x0020,0x0030,0x0069,
+ 0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x002b,0x2212,
+ 0x003d,0x0028,0x0029,0x006e,0x0030,0x0031,0x0032,0x0033,
+ 0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x002b,0x2212,
+ 0x003d,0x0028,0x0029,0x0061,0x0065,0x006f,0x0078,0x0259,
+ 0x0052,0x0073,0x0061,0x002f,0x0063,0x0061,0x002f,0x0073,
+ 0x0043,0x00b0,0x0043,0x0063,0x002f,0x006f,0x0063,0x002f,
+ 0x0075,0x0190,0x00b0,0x0046,0x0067,0x0048,0x0048,0x0048,
+ 0x0068,0x0127,0x0049,0x0049,0x004c,0x006c,0x004e,0x004e,
+ 0x006f,0x0050,0x0051,0x0052,0x0052,0x0052,0x0053,0x004d,
+ 0x0054,0x0045,0x004c,0x0054,0x004d,0x005a,0x03a9,0x005a,
+ 0x004b,0x00c5,0x0042,0x0043,0x0065,0x0045,0x0046,0x004d,
+ 0x006f,0x05d0,0x05d1,0x05d2,0x05d3,0x0069,0x0046,0x0041,
+ 0x0058,0x03c0,0x03b3,0x0393,0x03a0,0x2211,0x0044,0x0064,
+ 0x0065,0x0069,0x006a,0x0031,0x2044,0x0033,0x0032,0x2044,
+ 0x0033,0x0031,0x2044,0x0035,0x0032,0x2044,0x0035,0x0033,
+ 0x2044,0x0035,0x0034,0x2044,0x0035,0x0031,0x2044,0x0036,
+ 0x0035,0x2044,0x0036,0x0031,0x2044,0x0038,0x0033,0x2044,
+ 0x0038,0x0035,0x2044,0x0038,0x0037,0x2044,0x0038,0x0031,
+ 0x2044,0x0049,0x0049,0x0049,0x0049,0x0049,0x0049,0x0049,
+ 0x0056,0x0056,0x0056,0x0049,0x0056,0x0049,0x0049,0x0056,
+ 0x0049,0x0049,0x0049,0x0049,0x0058,0x0058,0x0058,0x0049,
+ 0x0058,0x0049,0x0049,0x004c,0x0043,0x0044,0x004d,0x0069,
+ 0x0069,0x0069,0x0069,0x0069,0x0069,0x0069,0x0076,0x0076,
+ 0x0076,0x0069,0x0076,0x0069,0x0069,0x0076,0x0069,0x0069,
+ 0x0069,0x0069,0x0078,0x0078,0x0078,0x0069,0x0078,0x0069,
+ 0x0069,0x006c,0x0063,0x0064,0x006d,0x2190,0x0338,0x2192,
+ 0x0338,0x2194,0x0338,0x21d0,0x0338,0x21d4,0x0338,0x21d2,
+ 0x0338,0x2203,0x0338,0x2208,0x0338,0x220b,0x0338,0x2223,
+ 0x0338,0x2225,0x0338,0x222b,0x222b,0x222b,0x222b,0x222b,
+ 0x222e,0x222e,0x222e,0x222e,0x222e,0x223c,0x0338,0x2243,
+ 0x0338,0x2245,0x0338,0x2248,0x0338,0x003d,0x0338,0x2261,
+ 0x0338,0x224d,0x0338,0x003c,0x0338,0x003e,0x0338,0x2264,
+ 0x0338,0x2265,0x0338,0x2272,0x0338,0x2273,0x0338,0x2276,
+ 0x0338,0x2277,0x0338,0x227a,0x0338,0x227b,0x0338,0x2282,
+ 0x0338,0x2283,0x0338,0x2286,0x0338,0x2287,0x0338,0x22a2,
+ 0x0338,0x22a8,0x0338,0x22a9,0x0338,0x22ab,0x0338,0x227c,
+ 0x0338,0x227d,0x0338,0x2291,0x0338,0x2292,0x0338,0x22b2,
+ 0x0338,0x22b3,0x0338,0x22b4,0x0338,0x22b5,0x0338,0x3008,
+ 0x3009,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,
+ 0x0038,0x0039,0x0031,0x0030,0x0031,0x0031,0x0031,0x0032,
+ 0x0031,0x0033,0x0031,0x0034,0x0031,0x0035,0x0031,0x0036,
+ 0x0031,0x0037,0x0031,0x0038,0x0031,0x0039,0x0032,0x0030,
+ 0x0028,0x0031,0x0029,0x0028,0x0032,0x0029,0x0028,0x0033,
+ 0x0029,0x0028,0x0034,0x0029,0x0028,0x0035,0x0029,0x0028,
+ 0x0036,0x0029,0x0028,0x0037,0x0029,0x0028,0x0038,0x0029,
+ 0x0028,0x0039,0x0029,0x0028,0x0031,0x0030,0x0029,0x0028,
+ 0x0031,0x0031,0x0029,0x0028,0x0031,0x0032,0x0029,0x0028,
+ 0x0031,0x0033,0x0029,0x0028,0x0031,0x0034,0x0029,0x0028,
+ 0x0031,0x0035,0x0029,0x0028,0x0031,0x0036,0x0029,0x0028,
+ 0x0031,0x0037,0x0029,0x0028,0x0031,0x0038,0x0029,0x0028,
+ 0x0031,0x0039,0x0029,0x0028,0x0032,0x0030,0x0029,0x0031,
+ 0x002e,0x0032,0x002e,0x0033,0x002e,0x0034,0x002e,0x0035,
+ 0x002e,0x0036,0x002e,0x0037,0x002e,0x0038,0x002e,0x0039,
+ 0x002e,0x0031,0x0030,0x002e,0x0031,0x0031,0x002e,0x0031,
+ 0x0032,0x002e,0x0031,0x0033,0x002e,0x0031,0x0034,0x002e,
+ 0x0031,0x0035,0x002e,0x0031,0x0036,0x002e,0x0031,0x0037,
+ 0x002e,0x0031,0x0038,0x002e,0x0031,0x0039,0x002e,0x0032,
+ 0x0030,0x002e,0x0028,0x0061,0x0029,0x0028,0x0062,0x0029,
+ 0x0028,0x0063,0x0029,0x0028,0x0064,0x0029,0x0028,0x0065,
+ 0x0029,0x0028,0x0066,0x0029,0x0028,0x0067,0x0029,0x0028,
+ 0x0068,0x0029,0x0028,0x0069,0x0029,0x0028,0x006a,0x0029,
+ 0x0028,0x006b,0x0029,0x0028,0x006c,0x0029,0x0028,0x006d,
+ 0x0029,0x0028,0x006e,0x0029,0x0028,0x006f,0x0029,0x0028,
+ 0x0070,0x0029,0x0028,0x0071,0x0029,0x0028,0x0072,0x0029,
+ 0x0028,0x0073,0x0029,0x0028,0x0074,0x0029,0x0028,0x0075,
+ 0x0029,0x0028,0x0076,0x0029,0x0028,0x0077,0x0029,0x0028,
+ 0x0078,0x0029,0x0028,0x0079,0x0029,0x0028,0x007a,0x0029,
+ 0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,
+ 0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,0x0050,
+ 0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,
+ 0x0059,0x005a,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,
+ 0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,
+ 0x006f,0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,
+ 0x0077,0x0078,0x0079,0x007a,0x0030,0x222b,0x222b,0x222b,
+ 0x222b,0x003a,0x003a,0x003d,0x003d,0x003d,0x003d,0x003d,
+ 0x003d,0x2add,0x0338,0x2d61,0x6bcd,0x9f9f,0x4e00,0x4e28,
+ 0x4e36,0x4e3f,0x4e59,0x4e85,0x4e8c,0x4ea0,0x4eba,0x513f,
+ 0x5165,0x516b,0x5182,0x5196,0x51ab,0x51e0,0x51f5,0x5200,
+ 0x529b,0x52f9,0x5315,0x531a,0x5338,0x5341,0x535c,0x5369,
+ 0x5382,0x53b6,0x53c8,0x53e3,0x56d7,0x571f,0x58eb,0x5902,
+ 0x590a,0x5915,0x5927,0x5973,0x5b50,0x5b80,0x5bf8,0x5c0f,
+ 0x5c22,0x5c38,0x5c6e,0x5c71,0x5ddb,0x5de5,0x5df1,0x5dfe,
+ 0x5e72,0x5e7a,0x5e7f,0x5ef4,0x5efe,0x5f0b,0x5f13,0x5f50,
+ 0x5f61,0x5f73,0x5fc3,0x6208,0x6236,0x624b,0x652f,0x6534,
+ 0x6587,0x6597,0x65a4,0x65b9,0x65e0,0x65e5,0x66f0,0x6708,
+ 0x6728,0x6b20,0x6b62,0x6b79,0x6bb3,0x6bcb,0x6bd4,0x6bdb,
+ 0x6c0f,0x6c14,0x6c34,0x706b,0x722a,0x7236,0x723b,0x723f,
+ 0x7247,0x7259,0x725b,0x72ac,0x7384,0x7389,0x74dc,0x74e6,
+ 0x7518,0x751f,0x7528,0x7530,0x758b,0x7592,0x7676,0x767d,
+ 0x76ae,0x76bf,0x76ee,0x77db,0x77e2,0x77f3,0x793a,0x79b8,
+ 0x79be,0x7a74,0x7acb,0x7af9,0x7c73,0x7cf8,0x7f36,0x7f51,
+ 0x7f8a,0x7fbd,0x8001,0x800c,0x8012,0x8033,0x807f,0x8089,
+ 0x81e3,0x81ea,0x81f3,0x81fc,0x820c,0x821b,0x821f,0x826e,
+ 0x8272,0x8278,0x864d,0x866b,0x8840,0x884c,0x8863,0x897e,
+ 0x898b,0x89d2,0x8a00,0x8c37,0x8c46,0x8c55,0x8c78,0x8c9d,
+ 0x8d64,0x8d70,0x8db3,0x8eab,0x8eca,0x8f9b,0x8fb0,0x8fb5,
+ 0x9091,0x9149,0x91c6,0x91cc,0x91d1,0x9577,0x9580,0x961c,
+ 0x96b6,0x96b9,0x96e8,0x9751,0x975e,0x9762,0x9769,0x97cb,
+ 0x97ed,0x97f3,0x9801,0x98a8,0x98db,0x98df,0x9996,0x9999,
+ 0x99ac,0x9aa8,0x9ad8,0x9adf,0x9b25,0x9b2f,0x9b32,0x9b3c,
+ 0x9b5a,0x9ce5,0x9e75,0x9e7f,0x9ea5,0x9ebb,0x9ec3,0x9ecd,
+ 0x9ed1,0x9ef9,0x9efd,0x9f0e,0x9f13,0x9f20,0x9f3b,0x9f4a,
+ 0x9f52,0x9f8d,0x9f9c,0x9fa0,0x0020,0x3012,0x5341,0x5344,
+ 0x5345,0x304b,0x3099,0x304d,0x3099,0x304f,0x3099,0x3051,
+ 0x3099,0x3053,0x3099,0x3055,0x3099,0x3057,0x3099,0x3059,
+ 0x3099,0x305b,0x3099,0x305d,0x3099,0x305f,0x3099,0x3061,
+ 0x3099,0x3064,0x3099,0x3066,0x3099,0x3068,0x3099,0x306f,
+ 0x3099,0x306f,0x309a,0x3072,0x3099,0x3072,0x309a,0x3075,
+ 0x3099,0x3075,0x309a,0x3078,0x3099,0x3078,0x309a,0x307b,
+ 0x3099,0x307b,0x309a,0x3046,0x3099,0x0020,0x3099,0x0020,
+ 0x309a,0x309d,0x3099,0x3088,0x308a,0x30ab,0x3099,0x30ad,
+ 0x3099,0x30af,0x3099,0x30b1,0x3099,0x30b3,0x3099,0x30b5,
+ 0x3099,0x30b7,0x3099,0x30b9,0x3099,0x30bb,0x3099,0x30bd,
+ 0x3099,0x30bf,0x3099,0x30c1,0x3099,0x30c4,0x3099,0x30c6,
+ 0x3099,0x30c8,0x3099,0x30cf,0x3099,0x30cf,0x309a,0x30d2,
+ 0x3099,0x30d2,0x309a,0x30d5,0x3099,0x30d5,0x309a,0x30d8,
+ 0x3099,0x30d8,0x309a,0x30db,0x3099,0x30db,0x309a,0x30a6,
+ 0x3099,0x30ef,0x3099,0x30f0,0x3099,0x30f1,0x3099,0x30f2,
+ 0x3099,0x30fd,0x3099,0x30b3,0x30c8,0x1100,0x1101,0x11aa,
+ 0x1102,0x11ac,0x11ad,0x1103,0x1104,0x1105,0x11b0,0x11b1,
+ 0x11b2,0x11b3,0x11b4,0x11b5,0x111a,0x1106,0x1107,0x1108,
+ 0x1121,0x1109,0x110a,0x110b,0x110c,0x110d,0x110e,0x110f,
+ 0x1110,0x1111,0x1112,0x1161,0x1162,0x1163,0x1164,0x1165,
+ 0x1166,0x1167,0x1168,0x1169,0x116a,0x116b,0x116c,0x116d,
+ 0x116e,0x116f,0x1170,0x1171,0x1172,0x1173,0x1174,0x1175,
+ 0x1160,0x1114,0x1115,0x11c7,0x11c8,0x11cc,0x11ce,0x11d3,
+ 0x11d7,0x11d9,0x111c,0x11dd,0x11df,0x111d,0x111e,0x1120,
+ 0x1122,0x1123,0x1127,0x1129,0x112b,0x112c,0x112d,0x112e,
+ 0x112f,0x1132,0x1136,0x1140,0x1147,0x114c,0x11f1,0x11f2,
+ 0x1157,0x1158,0x1159,0x1184,0x1185,0x1188,0x1191,0x1192,
+ 0x1194,0x119e,0x11a1,0x4e00,0x4e8c,0x4e09,0x56db,0x4e0a,
+ 0x4e2d,0x4e0b,0x7532,0x4e59,0x4e19,0x4e01,0x5929,0x5730,
+ 0x4eba,0x0028,0x1100,0x0029,0x0028,0x1102,0x0029,0x0028,
+ 0x1103,0x0029,0x0028,0x1105,0x0029,0x0028,0x1106,0x0029,
+ 0x0028,0x1107,0x0029,0x0028,0x1109,0x0029,0x0028,0x110b,
+ 0x0029,0x0028,0x110c,0x0029,0x0028,0x110e,0x0029,0x0028,
+ 0x110f,0x0029,0x0028,0x1110,0x0029,0x0028,0x1111,0x0029,
+ 0x0028,0x1112,0x0029,0x0028,0x1100,0x1161,0x0029,0x0028,
+ 0x1102,0x1161,0x0029,0x0028,0x1103,0x1161,0x0029,0x0028,
+ 0x1105,0x1161,0x0029,0x0028,0x1106,0x1161,0x0029,0x0028,
+ 0x1107,0x1161,0x0029,0x0028,0x1109,0x1161,0x0029,0x0028,
+ 0x110b,0x1161,0x0029,0x0028,0x110c,0x1161,0x0029,0x0028,
+ 0x110e,0x1161,0x0029,0x0028,0x110f,0x1161,0x0029,0x0028,
+ 0x1110,0x1161,0x0029,0x0028,0x1111,0x1161,0x0029,0x0028,
+ 0x1112,0x1161,0x0029,0x0028,0x110c,0x116e,0x0029,0x0028,
+ 0x110b,0x1169,0x110c,0x1165,0x11ab,0x0029,0x0028,0x110b,
+ 0x1169,0x1112,0x116e,0x0029,0x0028,0x4e00,0x0029,0x0028,
+ 0x4e8c,0x0029,0x0028,0x4e09,0x0029,0x0028,0x56db,0x0029,
+ 0x0028,0x4e94,0x0029,0x0028,0x516d,0x0029,0x0028,0x4e03,
+ 0x0029,0x0028,0x516b,0x0029,0x0028,0x4e5d,0x0029,0x0028,
+ 0x5341,0x0029,0x0028,0x6708,0x0029,0x0028,0x706b,0x0029,
+ 0x0028,0x6c34,0x0029,0x0028,0x6728,0x0029,0x0028,0x91d1,
+ 0x0029,0x0028,0x571f,0x0029,0x0028,0x65e5,0x0029,0x0028,
+ 0x682a,0x0029,0x0028,0x6709,0x0029,0x0028,0x793e,0x0029,
+ 0x0028,0x540d,0x0029,0x0028,0x7279,0x0029,0x0028,0x8ca1,
+ 0x0029,0x0028,0x795d,0x0029,0x0028,0x52b4,0x0029,0x0028,
+ 0x4ee3,0x0029,0x0028,0x547c,0x0029,0x0028,0x5b66,0x0029,
+ 0x0028,0x76e3,0x0029,0x0028,0x4f01,0x0029,0x0028,0x8cc7,
+ 0x0029,0x0028,0x5354,0x0029,0x0028,0x796d,0x0029,0x0028,
+ 0x4f11,0x0029,0x0028,0x81ea,0x0029,0x0028,0x81f3,0x0029,
+ 0x0050,0x0054,0x0045,0x0032,0x0031,0x0032,0x0032,0x0032,
+ 0x0033,0x0032,0x0034,0x0032,0x0035,0x0032,0x0036,0x0032,
+ 0x0037,0x0032,0x0038,0x0032,0x0039,0x0033,0x0030,0x0033,
+ 0x0031,0x0033,0x0032,0x0033,0x0033,0x0033,0x0034,0x0033,
+ 0x0035,0x1100,0x1102,0x1103,0x1105,0x1106,0x1107,0x1109,
+ 0x110b,0x110c,0x110e,0x110f,0x1110,0x1111,0x1112,0x1100,
+ 0x1161,0x1102,0x1161,0x1103,0x1161,0x1105,0x1161,0x1106,
+ 0x1161,0x1107,0x1161,0x1109,0x1161,0x110b,0x1161,0x110c,
+ 0x1161,0x110e,0x1161,0x110f,0x1161,0x1110,0x1161,0x1111,
+ 0x1161,0x1112,0x1161,0x110e,0x1161,0x11b7,0x1100,0x1169,
+ 0x110c,0x116e,0x110b,0x1174,0x110b,0x116e,0x4e00,0x4e8c,
+ 0x4e09,0x56db,0x4e94,0x516d,0x4e03,0x516b,0x4e5d,0x5341,
+ 0x6708,0x706b,0x6c34,0x6728,0x91d1,0x571f,0x65e5,0x682a,
+ 0x6709,0x793e,0x540d,0x7279,0x8ca1,0x795d,0x52b4,0x79d8,
+ 0x7537,0x5973,0x9069,0x512a,0x5370,0x6ce8,0x9805,0x4f11,
+ 0x5199,0x6b63,0x4e0a,0x4e2d,0x4e0b,0x5de6,0x53f3,0x533b,
+ 0x5b97,0x5b66,0x76e3,0x4f01,0x8cc7,0x5354,0x591c,0x0033,
+ 0x0036,0x0033,0x0037,0x0033,0x0038,0x0033,0x0039,0x0034,
+ 0x0030,0x0034,0x0031,0x0034,0x0032,0x0034,0x0033,0x0034,
+ 0x0034,0x0034,0x0035,0x0034,0x0036,0x0034,0x0037,0x0034,
+ 0x0038,0x0034,0x0039,0x0035,0x0030,0x0031,0x6708,0x0032,
+ 0x6708,0x0033,0x6708,0x0034,0x6708,0x0035,0x6708,0x0036,
+ 0x6708,0x0037,0x6708,0x0038,0x6708,0x0039,0x6708,0x0031,
+ 0x0030,0x6708,0x0031,0x0031,0x6708,0x0031,0x0032,0x6708,
+ 0x0048,0x0067,0x0065,0x0072,0x0067,0x0065,0x0056,0x004c,
+ 0x0054,0x0044,0x30a2,0x30a4,0x30a6,0x30a8,0x30aa,0x30ab,
+ 0x30ad,0x30af,0x30b1,0x30b3,0x30b5,0x30b7,0x30b9,0x30bb,
+ 0x30bd,0x30bf,0x30c1,0x30c4,0x30c6,0x30c8,0x30ca,0x30cb,
+ 0x30cc,0x30cd,0x30ce,0x30cf,0x30d2,0x30d5,0x30d8,0x30db,
+ 0x30de,0x30df,0x30e0,0x30e1,0x30e2,0x30e4,0x30e6,0x30e8,
+ 0x30e9,0x30ea,0x30eb,0x30ec,0x30ed,0x30ef,0x30f0,0x30f1,
+ 0x30f2,0x30a2,0x30d1,0x30fc,0x30c8,0x30a2,0x30eb,0x30d5,
+ 0x30a1,0x30a2,0x30f3,0x30da,0x30a2,0x30a2,0x30fc,0x30eb,
+ 0x30a4,0x30cb,0x30f3,0x30b0,0x30a4,0x30f3,0x30c1,0x30a6,
+ 0x30a9,0x30f3,0x30a8,0x30b9,0x30af,0x30fc,0x30c9,0x30a8,
+ 0x30fc,0x30ab,0x30fc,0x30aa,0x30f3,0x30b9,0x30aa,0x30fc,
+ 0x30e0,0x30ab,0x30a4,0x30ea,0x30ab,0x30e9,0x30c3,0x30c8,
+ 0x30ab,0x30ed,0x30ea,0x30fc,0x30ac,0x30ed,0x30f3,0x30ac,
+ 0x30f3,0x30de,0x30ae,0x30ac,0x30ae,0x30cb,0x30fc,0x30ad,
+ 0x30e5,0x30ea,0x30fc,0x30ae,0x30eb,0x30c0,0x30fc,0x30ad,
+ 0x30ed,0x30ad,0x30ed,0x30b0,0x30e9,0x30e0,0x30ad,0x30ed,
+ 0x30e1,0x30fc,0x30c8,0x30eb,0x30ad,0x30ed,0x30ef,0x30c3,
+ 0x30c8,0x30b0,0x30e9,0x30e0,0x30b0,0x30e9,0x30e0,0x30c8,
+ 0x30f3,0x30af,0x30eb,0x30bc,0x30a4,0x30ed,0x30af,0x30ed,
+ 0x30fc,0x30cd,0x30b1,0x30fc,0x30b9,0x30b3,0x30eb,0x30ca,
+ 0x30b3,0x30fc,0x30dd,0x30b5,0x30a4,0x30af,0x30eb,0x30b5,
+ 0x30f3,0x30c1,0x30fc,0x30e0,0x30b7,0x30ea,0x30f3,0x30b0,
+ 0x30bb,0x30f3,0x30c1,0x30bb,0x30f3,0x30c8,0x30c0,0x30fc,
+ 0x30b9,0x30c7,0x30b7,0x30c9,0x30eb,0x30c8,0x30f3,0x30ca,
+ 0x30ce,0x30ce,0x30c3,0x30c8,0x30cf,0x30a4,0x30c4,0x30d1,
+ 0x30fc,0x30bb,0x30f3,0x30c8,0x30d1,0x30fc,0x30c4,0x30d0,
+ 0x30fc,0x30ec,0x30eb,0x30d4,0x30a2,0x30b9,0x30c8,0x30eb,
+ 0x30d4,0x30af,0x30eb,0x30d4,0x30b3,0x30d3,0x30eb,0x30d5,
+ 0x30a1,0x30e9,0x30c3,0x30c9,0x30d5,0x30a3,0x30fc,0x30c8,
+ 0x30d6,0x30c3,0x30b7,0x30a7,0x30eb,0x30d5,0x30e9,0x30f3,
+ 0x30d8,0x30af,0x30bf,0x30fc,0x30eb,0x30da,0x30bd,0x30da,
+ 0x30cb,0x30d2,0x30d8,0x30eb,0x30c4,0x30da,0x30f3,0x30b9,
+ 0x30da,0x30fc,0x30b8,0x30d9,0x30fc,0x30bf,0x30dd,0x30a4,
+ 0x30f3,0x30c8,0x30dc,0x30eb,0x30c8,0x30db,0x30f3,0x30dd,
+ 0x30f3,0x30c9,0x30db,0x30fc,0x30eb,0x30db,0x30fc,0x30f3,
+ 0x30de,0x30a4,0x30af,0x30ed,0x30de,0x30a4,0x30eb,0x30de,
+ 0x30c3,0x30cf,0x30de,0x30eb,0x30af,0x30de,0x30f3,0x30b7,
+ 0x30e7,0x30f3,0x30df,0x30af,0x30ed,0x30f3,0x30df,0x30ea,
+ 0x30df,0x30ea,0x30d0,0x30fc,0x30eb,0x30e1,0x30ac,0x30e1,
+ 0x30ac,0x30c8,0x30f3,0x30e1,0x30fc,0x30c8,0x30eb,0x30e4,
+ 0x30fc,0x30c9,0x30e4,0x30fc,0x30eb,0x30e6,0x30a2,0x30f3,
+ 0x30ea,0x30c3,0x30c8,0x30eb,0x30ea,0x30e9,0x30eb,0x30d4,
+ 0x30fc,0x30eb,0x30fc,0x30d6,0x30eb,0x30ec,0x30e0,0x30ec,
+ 0x30f3,0x30c8,0x30b2,0x30f3,0x30ef,0x30c3,0x30c8,0x0030,
+ 0x70b9,0x0031,0x70b9,0x0032,0x70b9,0x0033,0x70b9,0x0034,
+ 0x70b9,0x0035,0x70b9,0x0036,0x70b9,0x0037,0x70b9,0x0038,
+ 0x70b9,0x0039,0x70b9,0x0031,0x0030,0x70b9,0x0031,0x0031,
+ 0x70b9,0x0031,0x0032,0x70b9,0x0031,0x0033,0x70b9,0x0031,
+ 0x0034,0x70b9,0x0031,0x0035,0x70b9,0x0031,0x0036,0x70b9,
+ 0x0031,0x0037,0x70b9,0x0031,0x0038,0x70b9,0x0031,0x0039,
+ 0x70b9,0x0032,0x0030,0x70b9,0x0032,0x0031,0x70b9,0x0032,
+ 0x0032,0x70b9,0x0032,0x0033,0x70b9,0x0032,0x0034,0x70b9,
+ 0x0068,0x0050,0x0061,0x0064,0x0061,0x0041,0x0055,0x0062,
+ 0x0061,0x0072,0x006f,0x0056,0x0070,0x0063,0x0064,0x006d,
+ 0x0064,0x006d,0x00b2,0x0064,0x006d,0x00b3,0x0049,0x0055,
+ 0x5e73,0x6210,0x662d,0x548c,0x5927,0x6b63,0x660e,0x6cbb,
+ 0x682a,0x5f0f,0x4f1a,0x793e,0x0070,0x0041,0x006e,0x0041,
+ 0x03bc,0x0041,0x006d,0x0041,0x006b,0x0041,0x004b,0x0042,
+ 0x004d,0x0042,0x0047,0x0042,0x0063,0x0061,0x006c,0x006b,
+ 0x0063,0x0061,0x006c,0x0070,0x0046,0x006e,0x0046,0x03bc,
+ 0x0046,0x03bc,0x0067,0x006d,0x0067,0x006b,0x0067,0x0048,
+ 0x007a,0x006b,0x0048,0x007a,0x004d,0x0048,0x007a,0x0047,
+ 0x0048,0x007a,0x0054,0x0048,0x007a,0x03bc,0x2113,0x006d,
+ 0x2113,0x0064,0x2113,0x006b,0x2113,0x0066,0x006d,0x006e,
+ 0x006d,0x03bc,0x006d,0x006d,0x006d,0x0063,0x006d,0x006b,
+ 0x006d,0x006d,0x006d,0x00b2,0x0063,0x006d,0x00b2,0x006d,
+ 0x00b2,0x006b,0x006d,0x00b2,0x006d,0x006d,0x00b3,0x0063,
+ 0x006d,0x00b3,0x006d,0x00b3,0x006b,0x006d,0x00b3,0x006d,
+ 0x2215,0x0073,0x006d,0x2215,0x0073,0x00b2,0x0050,0x0061,
+ 0x006b,0x0050,0x0061,0x004d,0x0050,0x0061,0x0047,0x0050,
+ 0x0061,0x0072,0x0061,0x0064,0x0072,0x0061,0x0064,0x2215,
+ 0x0073,0x0072,0x0061,0x0064,0x2215,0x0073,0x00b2,0x0070,
+ 0x0073,0x006e,0x0073,0x03bc,0x0073,0x006d,0x0073,0x0070,
+ 0x0056,0x006e,0x0056,0x03bc,0x0056,0x006d,0x0056,0x006b,
+ 0x0056,0x004d,0x0056,0x0070,0x0057,0x006e,0x0057,0x03bc,
+ 0x0057,0x006d,0x0057,0x006b,0x0057,0x004d,0x0057,0x006b,
+ 0x03a9,0x004d,0x03a9,0x0061,0x002e,0x006d,0x002e,0x0042,
+ 0x0071,0x0063,0x0063,0x0063,0x0064,0x0043,0x2215,0x006b,
+ 0x0067,0x0043,0x006f,0x002e,0x0064,0x0042,0x0047,0x0079,
+ 0x0068,0x0061,0x0048,0x0050,0x0069,0x006e,0x004b,0x004b,
+ 0x004b,0x004d,0x006b,0x0074,0x006c,0x006d,0x006c,0x006e,
+ 0x006c,0x006f,0x0067,0x006c,0x0078,0x006d,0x0062,0x006d,
+ 0x0069,0x006c,0x006d,0x006f,0x006c,0x0050,0x0048,0x0070,
+ 0x002e,0x006d,0x002e,0x0050,0x0050,0x004d,0x0050,0x0052,
+ 0x0073,0x0072,0x0053,0x0076,0x0057,0x0062,0x0056,0x2215,
+ 0x006d,0x0041,0x2215,0x006d,0x0031,0x65e5,0x0032,0x65e5,
+ 0x0033,0x65e5,0x0034,0x65e5,0x0035,0x65e5,0x0036,0x65e5,
+ 0x0037,0x65e5,0x0038,0x65e5,0x0039,0x65e5,0x0031,0x0030,
+ 0x65e5,0x0031,0x0031,0x65e5,0x0031,0x0032,0x65e5,0x0031,
+ 0x0033,0x65e5,0x0031,0x0034,0x65e5,0x0031,0x0035,0x65e5,
+ 0x0031,0x0036,0x65e5,0x0031,0x0037,0x65e5,0x0031,0x0038,
+ 0x65e5,0x0031,0x0039,0x65e5,0x0032,0x0030,0x65e5,0x0032,
+ 0x0031,0x65e5,0x0032,0x0032,0x65e5,0x0032,0x0033,0x65e5,
+ 0x0032,0x0034,0x65e5,0x0032,0x0035,0x65e5,0x0032,0x0036,
+ 0x65e5,0x0032,0x0037,0x65e5,0x0032,0x0038,0x65e5,0x0032,
+ 0x0039,0x65e5,0x0033,0x0030,0x65e5,0x0033,0x0031,0x65e5,
+ 0x0067,0x0061,0x006c
+};
+
+
+#define UCS4_BMPCJKMIN 0xf900
+#define UCS4_BMPCJKMAX 0xface
+
+ /* CJK Compatibility - 0 means hole (no decomposition) */
+static const unsigned short ucs4_bmpcjk1decomptab[463] = {
+ 0x8c48,0x66f4,0x8eca,0x8cc8,0x6ed1,0x4e32,0x53e5,0x9f9c,
+ 0x9f9c,0x5951,0x91d1,0x5587,0x5948,0x61f6,0x7669,0x7f85,
+ 0x863f,0x87ba,0x88f8,0x908f,0x6a02,0x6d1b,0x70d9,0x73de,
+ 0x843d,0x916a,0x99f1,0x4e82,0x5375,0x6b04,0x721b,0x862d,
+ 0x9e1e,0x5d50,0x6feb,0x85cd,0x8964,0x62c9,0x81d8,0x881f,
+ 0x5eca,0x6717,0x6d6a,0x72fc,0x90ce,0x4f86,0x51b7,0x52de,
+ 0x64c4,0x6ad3,0x7210,0x76e7,0x8001,0x8606,0x865c,0x8def,
+ 0x9732,0x9b6f,0x9dfa,0x788c,0x797f,0x7da0,0x83c9,0x9304,
+ 0x9e7f,0x8ad6,0x58df,0x5f04,0x7c60,0x807e,0x7262,0x78ca,
+ 0x8cc2,0x96f7,0x58d8,0x5c62,0x6a13,0x6dda,0x6f0f,0x7d2f,
+ 0x7e37,0x964b,0x52d2,0x808b,0x51dc,0x51cc,0x7a1c,0x7dbe,
+ 0x83f1,0x9675,0x8b80,0x62cf,0x6a02,0x8afe,0x4e39,0x5be7,
+ 0x6012,0x7387,0x7570,0x5317,0x78fb,0x4fbf,0x5fa9,0x4e0d,
+ 0x6ccc,0x6578,0x7d22,0x53c3,0x585e,0x7701,0x8449,0x8aaa,
+ 0x6bba,0x8fb0,0x6c88,0x62fe,0x82e5,0x63a0,0x7565,0x4eae,
+ 0x5169,0x51c9,0x6881,0x7ce7,0x826f,0x8ad2,0x91cf,0x52f5,
+ 0x5442,0x5973,0x5eec,0x65c5,0x6ffe,0x792a,0x95ad,0x9a6a,
+ 0x9e97,0x9ece,0x529b,0x66c6,0x6b77,0x8f62,0x5e74,0x6190,
+ 0x6200,0x649a,0x6f23,0x7149,0x7489,0x79ca,0x7df4,0x806f,
+ 0x8f26,0x84ee,0x9023,0x934a,0x5217,0x52a3,0x54bd,0x70c8,
+ 0x88c2,0x8aaa,0x5ec9,0x5ff5,0x637b,0x6bae,0x7c3e,0x7375,
+ 0x4ee4,0x56f9,0x5be7,0x5dba,0x601c,0x73b2,0x7469,0x7f9a,
+ 0x8046,0x9234,0x96f6,0x9748,0x9818,0x4f8b,0x79ae,0x91b4,
+ 0x96b8,0x60e1,0x4e86,0x50da,0x5bee,0x5c3f,0x6599,0x6a02,
+ 0x71ce,0x7642,0x84fc,0x907c,0x9f8d,0x6688,0x962e,0x5289,
+ 0x677b,0x67f3,0x6d41,0x6e9c,0x7409,0x7559,0x786b,0x7d10,
+ 0x985e,0x516d,0x622e,0x9678,0x502b,0x5d19,0x6dea,0x8f2a,
+ 0x5f8b,0x6144,0x6817,0x7387,0x9686,0x5229,0x540f,0x5c65,
+ 0x6613,0x674e,0x68a8,0x6ce5,0x7406,0x75e2,0x7f79,0x88cf,
+ 0x88e1,0x91cc,0x96e2,0x533f,0x6eba,0x541d,0x71d0,0x7498,
+ 0x85fa,0x96a3,0x9c57,0x9e9f,0x6797,0x6dcb,0x81e8,0x7acb,
+ 0x7b20,0x7c92,0x72c0,0x7099,0x8b58,0x4ec0,0x8336,0x523a,
+ 0x5207,0x5ea6,0x62d3,0x7cd6,0x5b85,0x6d1e,0x66b4,0x8f3b,
+ 0x884c,0x964d,0x898b,0x5ed3,0x5140,0x55c0,0x0000,0x0000,
+ 0x585a,0x0000,0x6674,0x0000,0x0000,0x51de,0x732a,0x76ca,
+ 0x793c,0x795e,0x7965,0x798f,0x9756,0x7cbe,0x7fbd,0x0000,
+ 0x8612,0x0000,0x8af8,0x0000,0x0000,0x9038,0x90fd,0x0000,
+ 0x0000,0x0000,0x98ef,0x98fc,0x9928,0x9db4,0x0000,0x0000,
+ 0x4fae,0x50e7,0x514d,0x52c9,0x52e4,0x5351,0x559d,0x5606,
+ 0x5668,0x5840,0x58a8,0x5c64,0x5c6e,0x6094,0x6168,0x618e,
+ 0x61f2,0x654f,0x65e2,0x6691,0x6885,0x6d77,0x6e1a,0x6f22,
+ 0x716e,0x722b,0x7422,0x7891,0x793e,0x7949,0x7948,0x7950,
+ 0x7956,0x795d,0x798d,0x798e,0x7a40,0x7a81,0x7bc0,0x7df4,
+ 0x7e09,0x7e41,0x7f72,0x8005,0x81ed,0x8279,0x8279,0x8457,
+ 0x8910,0x8996,0x8b01,0x8b39,0x8cd3,0x8d08,0x8fb6,0x9038,
+ 0x96e3,0x97ff,0x983b,0x0000,0x0000,0x0000,0x0000,0x0000,
+ 0x4e26,0x51b5,0x5168,0x4f80,0x5145,0x5180,0x52c7,0x52fa,
+ 0x559d,0x5555,0x5599,0x55e2,0x585a,0x58b3,0x5944,0x5954,
+ 0x5a62,0x5b28,0x5ed2,0x5ed9,0x5f69,0x5fad,0x60d8,0x614e,
+ 0x6108,0x618e,0x6160,0x61f2,0x6234,0x63c4,0x641c,0x6452,
+ 0x6556,0x6674,0x6717,0x671b,0x6756,0x6b79,0x6bba,0x6d41,
+ 0x6edb,0x6ecb,0x6f22,0x701e,0x716e,0x77a7,0x7235,0x72af,
+ 0x732a,0x7471,0x7506,0x753b,0x761d,0x761f,0x76ca,0x76db,
+ 0x76f4,0x774a,0x7740,0x78cc,0x7ab1,0x7bc0,0x7c7b,0x7d5b,
+ 0x7df4,0x7f3e,0x8005,0x8352,0x83ef,0x8779,0x8941,0x8986,
+ 0x8996,0x8abf,0x8af8,0x8acb,0x8b01,0x8afe,0x8aed,0x8b39,
+ 0x8b8a,0x8d08,0x8f38,0x9072,0x9199,0x9276,0x967c,0x96e3,
+ 0x9756,0x97db,0x97ff,0x980b,0x983b,0x9b12,0x9f9c
+};
+
+
+#define UCS4_BMPCJK2MIN 0xfacf
+#define UCS4_BMPCJK2MAX 0xfad9
+
+
+ /* CJK Compatibility - some values not in BMP */
+static const unsigned long ucs4_bmpcjk2decomptab[11] = {
+ 0x2284a,0x22844,0x233d5, 0x3b9d, 0x4018, 0x4039,
+ 0x25249,0x25cd0,0x27ed3, 0x9f43, 0x9f8e
+};
+
+
+#define UCS4_BMPHIMIN 0xfb00
+#define UCS4_BMPHIMAX 0xfefc
+#define UCS4_BMPHIIXMASK 0x7ff
+#define UCS4_BMPHISIZEMASK 0xf800
+#define UCS4_BMPHISIZESHIFT 11
+
+ /* BMP hi decomposition indices - ssss siii iiii iiii */
+static unsigned short ucs4_dbmphiixtab[1021] = {
+0x0800,0x0802,0x0804,0x1006,0x1009,0x080c,0x080e,0x0000,
+0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+0x0000,0x0000,0x0000,0x0810,0x0812,0x0814,0x0816,0x0818,
+0x0000,0x0000,0x0000,0x0000,0x0000,0x081a,0x0000,0x081c,
+0x001e,0x001f,0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,
+0x0026,0x0027,0x0828,0x082a,0x082c,0x082e,0x0830,0x0832,
+0x0834,0x0836,0x0838,0x083a,0x083c,0x083e,0x0840,0x0000,
+0x0842,0x0844,0x0846,0x0848,0x084a,0x0000,0x084c,0x0000,
+0x084e,0x0850,0x0000,0x0852,0x0854,0x0000,0x0856,0x0858,
+0x085a,0x085c,0x085e,0x0860,0x0862,0x0864,0x0866,0x0868,
+0x006a,0x006b,0x006c,0x006d,0x006e,0x006f,0x0070,0x0071,
+0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,
+0x007a,0x007b,0x007c,0x007d,0x007e,0x007f,0x0080,0x0081,
+0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,0x0088,0x0089,
+0x008a,0x008b,0x008c,0x008d,0x008e,0x008f,0x0090,0x0091,
+0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,0x0098,0x0099,
+0x009a,0x009b,0x009c,0x009d,0x009e,0x009f,0x00a0,0x00a1,
+0x00a2,0x00a3,0x00a4,0x00a5,0x00a6,0x00a7,0x00a8,0x00a9,
+0x00aa,0x00ab,0x00ac,0x00ad,0x00ae,0x00af,0x00b0,0x00b1,
+0x00b2,0x00b3,0x00b4,0x00b5,0x00b6,0x00b7,0x00b8,0x00b9,
+0x00ba,0x00bb,0x00bc,0x00bd,0x00be,0x00bf,0x00c0,0x00c1,
+0x00c2,0x00c3,0x00c4,0x00c5,0x00c6,0x00c7,0x00c8,0x00c9,
+0x00ca,0x00cb,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+0x0000,0x0000,0x0000,0x00cc,0x00cd,0x00ce,0x00cf,0x00d0,
+0x00d1,0x00d2,0x00d3,0x00d4,0x00d5,0x00d6,0x00d7,0x00d8,
+0x00d9,0x00da,0x00db,0x00dc,0x00dd,0x00de,0x00df,0x00e0,
+0x00e1,0x00e2,0x08e3,0x08e5,0x08e7,0x08e9,0x08eb,0x08ed,
+0x08ef,0x08f1,0x08f3,0x08f5,0x08f7,0x08f9,0x08fb,0x08fd,
+0x08ff,0x0901,0x0903,0x0905,0x0107,0x0108,0x0109,0x010a,
+0x090b,0x090d,0x090f,0x0911,0x0913,0x0915,0x0917,0x0919,
+0x091b,0x091d,0x091f,0x0921,0x0923,0x0925,0x0927,0x0929,
+0x092b,0x092d,0x092f,0x0931,0x0933,0x0935,0x0937,0x0939,
+0x093b,0x093d,0x093f,0x0941,0x0943,0x0945,0x0947,0x0949,
+0x094b,0x094d,0x094f,0x0951,0x0953,0x0955,0x0957,0x0959,
+0x095b,0x095d,0x095f,0x0961,0x0963,0x0965,0x0967,0x0969,
+0x096b,0x096d,0x096f,0x0971,0x0973,0x0975,0x0977,0x0979,
+0x097b,0x097d,0x097f,0x0981,0x0983,0x0985,0x0987,0x0989,
+0x098b,0x098d,0x098f,0x0991,0x0993,0x0995,0x0997,0x0999,
+0x099b,0x099d,0x099f,0x09a1,0x09a3,0x09a5,0x09a7,0x09a9,
+0x09ab,0x09ad,0x09af,0x09b1,0x09b3,0x09b5,0x09b7,0x09b9,
+0x09bb,0x09bd,0x09bf,0x09c1,0x09c3,0x09c5,0x11c7,0x11ca,
+0x11cd,0x11d0,0x11d3,0x11d6,0x09d9,0x09db,0x09dd,0x09df,
+0x09e1,0x09e3,0x09e5,0x09e7,0x09e9,0x09eb,0x09ed,0x09ef,
+0x09f1,0x09f3,0x09f5,0x09f7,0x09f9,0x09fb,0x09fd,0x09ff,
+0x0a01,0x0a03,0x0a05,0x0a07,0x0a09,0x0a0b,0x0a0d,0x0a0f,
+0x0a11,0x0a13,0x0a15,0x0a17,0x0a19,0x0a1b,0x0a1d,0x0a1f,
+0x0a21,0x0a23,0x0a25,0x0a27,0x0a29,0x0a2b,0x0a2d,0x0a2f,
+0x0a31,0x0a33,0x0a35,0x0a37,0x0a39,0x0a3b,0x0a3d,0x0a3f,
+0x0a41,0x0a43,0x0a45,0x0a47,0x0a49,0x0a4b,0x0a4d,0x0a4f,
+0x0a51,0x0a53,0x0a55,0x0a57,0x0a59,0x0a5b,0x0a5d,0x0a5f,
+0x0a61,0x0a63,0x0a65,0x0a67,0x0a69,0x0a6b,0x0a6d,0x0a6f,
+0x0a71,0x0a73,0x0a75,0x0a77,0x0a79,0x0a7b,0x0a7d,0x0a7f,
+0x0a81,0x0a83,0x0a85,0x0a87,0x0a89,0x0a8b,0x0a8d,0x0a8f,
+0x0a91,0x0a93,0x0a95,0x0a97,0x0a99,0x0a9b,0x0a9d,0x0a9f,
+0x0aa1,0x0aa3,0x0aa5,0x0aa7,0x0aa9,0x0aab,0x0aad,0x0aaf,
+0x0ab1,0x0ab3,0x0ab5,0x0ab7,0x0ab9,0x0abb,0x0abd,0x0abf,
+0x0ac1,0x0ac3,0x0ac5,0x0ac7,0x0ac9,0x0acb,0x0acd,0x0acf,
+0x0ad1,0x0ad3,0x0ad5,0x0ad7,0x0ad9,0x0adb,0x0add,0x0adf,
+0x0ae1,0x0ae3,0x0ae5,0x0ae7,0x0ae9,0x0aeb,0x0aed,0x0aef,
+0x0af1,0x0af3,0x12f5,0x12f8,0x12fb,0x0afe,0x0b00,0x0b02,
+0x0b04,0x0b06,0x0b08,0x0b0a,0x0b0c,0x0b0e,0x0b10,0x0b12,
+0x0b14,0x0b16,0x0b18,0x0b1a,0x0b1c,0x0b1e,0x0b20,0x0b22,
+0x0b24,0x0b26,0x0b28,0x0b2a,0x0b2c,0x0b2e,0x0b30,0x0b32,
+0x0b34,0x0b36,0x0b38,0x0b3a,0x0b3c,0x0b3e,0x0b40,0x0b42,
+0x0b44,0x0b46,0x0b48,0x0b4a,0x0b4c,0x0b4e,0x0b50,0x0b52,
+0x0b54,0x0b56,0x0b58,0x0b5a,0x0b5c,0x0b5e,0x0b60,0x0b62,
+0x0b64,0x0b66,0x0b68,0x0b6a,0x0b6c,0x0b6e,0x0b70,0x0b72,
+0x0b74,0x0b76,0x0b78,0x0b7a,0x0b7c,0x0b7e,0x0b80,0x0b82,
+0x0b84,0x0b86,0x0b88,0x0b8a,0x0b8c,0x0b8e,0x0000,0x0000,
+0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+0x1390,0x1393,0x1396,0x1399,0x139c,0x139f,0x13a2,0x13a5,
+0x13a8,0x13ab,0x13ae,0x13b1,0x13b4,0x13b7,0x13ba,0x13bd,
+0x13c0,0x13c3,0x13c6,0x13c9,0x13cc,0x13cf,0x13d2,0x13d5,
+0x13d8,0x13db,0x13de,0x13e1,0x13e4,0x13e7,0x13ea,0x13ed,
+0x13f0,0x13f3,0x13f6,0x13f9,0x13fc,0x13ff,0x1402,0x1405,
+0x1408,0x140b,0x140e,0x1411,0x1414,0x1417,0x141a,0x141d,
+0x1420,0x1423,0x1426,0x1429,0x142c,0x142f,0x1432,0x1435,
+0x1438,0x143b,0x143e,0x1441,0x1444,0x1447,0x144a,0x144d,
+0x0000,0x0000,0x1450,0x1453,0x1456,0x1459,0x145c,0x145f,
+0x1462,0x1465,0x1468,0x146b,0x146e,0x1471,0x1474,0x1477,
+0x147a,0x147d,0x1480,0x1483,0x1486,0x1489,0x148c,0x148f,
+0x1492,0x1495,0x1498,0x149b,0x149e,0x14a1,0x14a4,0x14a7,
+0x14aa,0x14ad,0x14b0,0x14b3,0x14b6,0x14b9,0x14bc,0x14bf,
+0x14c2,0x14c5,0x14c8,0x14cb,0x14ce,0x14d1,0x14d4,0x14d7,
+0x14da,0x14dd,0x14e0,0x14e3,0x14e6,0x14e9,0x14ec,0x14ef,
+0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+0x14f2,0x14f5,0x1cf8,0x1cfc,0x1d00,0x1d04,0x1d08,0x1d0c,
+0x1d10,0x1514,0x8d17,0x3d29,0x1d31,0x0000,0x0000,0x0000,
+0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+0x0535,0x0536,0x0537,0x0538,0x0539,0x053a,0x053b,0x053c,
+0x053d,0x053e,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
+0x053f,0x0540,0x0541,0x0542,0x0543,0x0544,0x0545,0x0546,
+0x0547,0x0548,0x0549,0x054a,0x054b,0x054c,0x054d,0x054e,
+0x054f,0x0550,0x0551,0x0552,0x0553,0x0000,0x0000,0x0554,
+0x0555,0x0556,0x0557,0x0558,0x0559,0x055a,0x055b,0x055c,
+0x055d,0x055e,0x055f,0x0000,0x0560,0x0561,0x0562,0x0563,
+0x0564,0x0565,0x0566,0x0567,0x0568,0x0569,0x056a,0x056b,
+0x056c,0x056d,0x056e,0x056f,0x0570,0x0571,0x0572,0x0000,
+0x0573,0x0574,0x0575,0x0576,0x0000,0x0000,0x0000,0x0000,
+0x0d77,0x0d79,0x0d7b,0x0000,0x0d7d,0x0000,0x0d7f,0x0d81,
+0x0d83,0x0d85,0x0d87,0x0d89,0x0d8b,0x0d8d,0x0d8f,0x0d91,
+0x0593,0x0594,0x0595,0x0596,0x0597,0x0598,0x0599,0x059a,
+0x059b,0x059c,0x059d,0x059e,0x059f,0x05a0,0x05a1,0x05a2,
+0x05a3,0x05a4,0x05a5,0x05a6,0x05a7,0x05a8,0x05a9,0x05aa,
+0x05ab,0x05ac,0x05ad,0x05ae,0x05af,0x05b0,0x05b1,0x05b2,
+0x05b3,0x05b4,0x05b5,0x05b6,0x05b7,0x05b8,0x05b9,0x05ba,
+0x05bb,0x05bc,0x05bd,0x05be,0x05bf,0x05c0,0x05c1,0x05c2,
+0x05c3,0x05c4,0x05c5,0x05c6,0x05c7,0x05c8,0x05c9,0x05ca,
+0x05cb,0x05cc,0x05cd,0x05ce,0x05cf,0x05d0,0x05d1,0x05d2,
+0x05d3,0x05d4,0x05d5,0x05d6,0x05d7,0x05d8,0x05d9,0x05da,
+0x05db,0x05dc,0x05dd,0x05de,0x05df,0x05e0,0x05e1,0x05e2,
+0x05e3,0x05e4,0x05e5,0x05e6,0x05e7,0x05e8,0x05e9,0x05ea,
+0x05eb,0x05ec,0x05ed,0x05ee,0x05ef,0x05f0,0x05f1,0x05f2,
+0x05f3,0x05f4,0x05f5,0x05f6,0x05f7,0x05f8,0x05f9,0x05fa,
+0x05fb,0x05fc,0x05fd,0x05fe,0x05ff,0x0600,0x0601,0x0602,
+0x0603,0x0604,0x0605,0x0606,0x0607,0x0e08,0x0e0a,0x0e0c,
+0x0e0e,0x0e10,0x0e12,0x0e14,0x0e16
+};
+
+ /* BMP high decompositions */
+static unsigned short ucs4_dbmphitab[1560] = {
+0x0066,0x0066,0x0066,0x0069,0x0066,0x006c,0x0066,0x0066,
+0x0069,0x0066,0x0066,0x006c,0x017f,0x0074,0x0073,0x0074,
+0x0574,0x0576,0x0574,0x0565,0x0574,0x056b,0x057e,0x0576,
+0x0574,0x056d,0x05d9,0x05b4,0x05f2,0x05b7,0x05e2,0x05d0,
+0x05d3,0x05d4,0x05db,0x05dc,0x05dd,0x05e8,0x05ea,0x002b,
+0x05e9,0x05c1,0x05e9,0x05c2,0xfb49,0x05c1,0xfb49,0x05c2,
+0x05d0,0x05b7,0x05d0,0x05b8,0x05d0,0x05bc,0x05d1,0x05bc,
+0x05d2,0x05bc,0x05d3,0x05bc,0x05d4,0x05bc,0x05d5,0x05bc,
+0x05d6,0x05bc,0x05d8,0x05bc,0x05d9,0x05bc,0x05da,0x05bc,
+0x05db,0x05bc,0x05dc,0x05bc,0x05de,0x05bc,0x05e0,0x05bc,
+0x05e1,0x05bc,0x05e3,0x05bc,0x05e4,0x05bc,0x05e6,0x05bc,
+0x05e7,0x05bc,0x05e8,0x05bc,0x05e9,0x05bc,0x05ea,0x05bc,
+0x05d5,0x05b9,0x05d1,0x05bf,0x05db,0x05bf,0x05e4,0x05bf,
+0x05d0,0x05dc,0x0671,0x0671,0x067b,0x067b,0x067b,0x067b,
+0x067e,0x067e,0x067e,0x067e,0x0680,0x0680,0x0680,0x0680,
+0x067a,0x067a,0x067a,0x067a,0x067f,0x067f,0x067f,0x067f,
+0x0679,0x0679,0x0679,0x0679,0x06a4,0x06a4,0x06a4,0x06a4,
+0x06a6,0x06a6,0x06a6,0x06a6,0x0684,0x0684,0x0684,0x0684,
+0x0683,0x0683,0x0683,0x0683,0x0686,0x0686,0x0686,0x0686,
+0x0687,0x0687,0x0687,0x0687,0x068d,0x068d,0x068c,0x068c,
+0x068e,0x068e,0x0688,0x0688,0x0698,0x0698,0x0691,0x0691,
+0x06a9,0x06a9,0x06a9,0x06a9,0x06af,0x06af,0x06af,0x06af,
+0x06b3,0x06b3,0x06b3,0x06b3,0x06b1,0x06b1,0x06b1,0x06b1,
+0x06ba,0x06ba,0x06bb,0x06bb,0x06bb,0x06bb,0x06c0,0x06c0,
+0x06c1,0x06c1,0x06c1,0x06c1,0x06be,0x06be,0x06be,0x06be,
+0x06d2,0x06d2,0x06d3,0x06d3,0x06ad,0x06ad,0x06ad,0x06ad,
+0x06c7,0x06c7,0x06c6,0x06c6,0x06c8,0x06c8,0x0677,0x06cb,
+0x06cb,0x06c5,0x06c5,0x06c9,0x06c9,0x06d0,0x06d0,0x06d0,
+0x06d0,0x0649,0x0649,0x0626,0x0627,0x0626,0x0627,0x0626,
+0x06d5,0x0626,0x06d5,0x0626,0x0648,0x0626,0x0648,0x0626,
+0x06c7,0x0626,0x06c7,0x0626,0x06c6,0x0626,0x06c6,0x0626,
+0x06c8,0x0626,0x06c8,0x0626,0x06d0,0x0626,0x06d0,0x0626,
+0x06d0,0x0626,0x0649,0x0626,0x0649,0x0626,0x0649,0x06cc,
+0x06cc,0x06cc,0x06cc,0x0626,0x062c,0x0626,0x062d,0x0626,
+0x0645,0x0626,0x0649,0x0626,0x064a,0x0628,0x062c,0x0628,
+0x062d,0x0628,0x062e,0x0628,0x0645,0x0628,0x0649,0x0628,
+0x064a,0x062a,0x062c,0x062a,0x062d,0x062a,0x062e,0x062a,
+0x0645,0x062a,0x0649,0x062a,0x064a,0x062b,0x062c,0x062b,
+0x0645,0x062b,0x0649,0x062b,0x064a,0x062c,0x062d,0x062c,
+0x0645,0x062d,0x062c,0x062d,0x0645,0x062e,0x062c,0x062e,
+0x062d,0x062e,0x0645,0x0633,0x062c,0x0633,0x062d,0x0633,
+0x062e,0x0633,0x0645,0x0635,0x062d,0x0635,0x0645,0x0636,
+0x062c,0x0636,0x062d,0x0636,0x062e,0x0636,0x0645,0x0637,
+0x062d,0x0637,0x0645,0x0638,0x0645,0x0639,0x062c,0x0639,
+0x0645,0x063a,0x062c,0x063a,0x0645,0x0641,0x062c,0x0641,
+0x062d,0x0641,0x062e,0x0641,0x0645,0x0641,0x0649,0x0641,
+0x064a,0x0642,0x062d,0x0642,0x0645,0x0642,0x0649,0x0642,
+0x064a,0x0643,0x0627,0x0643,0x062c,0x0643,0x062d,0x0643,
+0x062e,0x0643,0x0644,0x0643,0x0645,0x0643,0x0649,0x0643,
+0x064a,0x0644,0x062c,0x0644,0x062d,0x0644,0x062e,0x0644,
+0x0645,0x0644,0x0649,0x0644,0x064a,0x0645,0x062c,0x0645,
+0x062d,0x0645,0x062e,0x0645,0x0645,0x0645,0x0649,0x0645,
+0x064a,0x0646,0x062c,0x0646,0x062d,0x0646,0x062e,0x0646,
+0x0645,0x0646,0x0649,0x0646,0x064a,0x0647,0x062c,0x0647,
+0x0645,0x0647,0x0649,0x0647,0x064a,0x064a,0x062c,0x064a,
+0x062d,0x064a,0x062e,0x064a,0x0645,0x064a,0x0649,0x064a,
+0x064a,0x0630,0x0670,0x0631,0x0670,0x0649,0x0670,0x0020,
+0x064c,0x0651,0x0020,0x064d,0x0651,0x0020,0x064e,0x0651,
+0x0020,0x064f,0x0651,0x0020,0x0650,0x0651,0x0020,0x0651,
+0x0670,0x0626,0x0631,0x0626,0x0632,0x0626,0x0645,0x0626,
+0x0646,0x0626,0x0649,0x0626,0x064a,0x0628,0x0631,0x0628,
+0x0632,0x0628,0x0645,0x0628,0x0646,0x0628,0x0649,0x0628,
+0x064a,0x062a,0x0631,0x062a,0x0632,0x062a,0x0645,0x062a,
+0x0646,0x062a,0x0649,0x062a,0x064a,0x062b,0x0631,0x062b,
+0x0632,0x062b,0x0645,0x062b,0x0646,0x062b,0x0649,0x062b,
+0x064a,0x0641,0x0649,0x0641,0x064a,0x0642,0x0649,0x0642,
+0x064a,0x0643,0x0627,0x0643,0x0644,0x0643,0x0645,0x0643,
+0x0649,0x0643,0x064a,0x0644,0x0645,0x0644,0x0649,0x0644,
+0x064a,0x0645,0x0627,0x0645,0x0645,0x0646,0x0631,0x0646,
+0x0632,0x0646,0x0645,0x0646,0x0646,0x0646,0x0649,0x0646,
+0x064a,0x0649,0x0670,0x064a,0x0631,0x064a,0x0632,0x064a,
+0x0645,0x064a,0x0646,0x064a,0x0649,0x064a,0x064a,0x0626,
+0x062c,0x0626,0x062d,0x0626,0x062e,0x0626,0x0645,0x0626,
+0x0647,0x0628,0x062c,0x0628,0x062d,0x0628,0x062e,0x0628,
+0x0645,0x0628,0x0647,0x062a,0x062c,0x062a,0x062d,0x062a,
+0x062e,0x062a,0x0645,0x062a,0x0647,0x062b,0x0645,0x062c,
+0x062d,0x062c,0x0645,0x062d,0x062c,0x062d,0x0645,0x062e,
+0x062c,0x062e,0x0645,0x0633,0x062c,0x0633,0x062d,0x0633,
+0x062e,0x0633,0x0645,0x0635,0x062d,0x0635,0x062e,0x0635,
+0x0645,0x0636,0x062c,0x0636,0x062d,0x0636,0x062e,0x0636,
+0x0645,0x0637,0x062d,0x0638,0x0645,0x0639,0x062c,0x0639,
+0x0645,0x063a,0x062c,0x063a,0x0645,0x0641,0x062c,0x0641,
+0x062d,0x0641,0x062e,0x0641,0x0645,0x0642,0x062d,0x0642,
+0x0645,0x0643,0x062c,0x0643,0x062d,0x0643,0x062e,0x0643,
+0x0644,0x0643,0x0645,0x0644,0x062c,0x0644,0x062d,0x0644,
+0x062e,0x0644,0x0645,0x0644,0x0647,0x0645,0x062c,0x0645,
+0x062d,0x0645,0x062e,0x0645,0x0645,0x0646,0x062c,0x0646,
+0x062d,0x0646,0x062e,0x0646,0x0645,0x0646,0x0647,0x0647,
+0x062c,0x0647,0x0645,0x0647,0x0670,0x064a,0x062c,0x064a,
+0x062d,0x064a,0x062e,0x064a,0x0645,0x064a,0x0647,0x0626,
+0x0645,0x0626,0x0647,0x0628,0x0645,0x0628,0x0647,0x062a,
+0x0645,0x062a,0x0647,0x062b,0x0645,0x062b,0x0647,0x0633,
+0x0645,0x0633,0x0647,0x0634,0x0645,0x0634,0x0647,0x0643,
+0x0644,0x0643,0x0645,0x0644,0x0645,0x0646,0x0645,0x0646,
+0x0647,0x064a,0x0645,0x064a,0x0647,0x0640,0x064e,0x0651,
+0x0640,0x064f,0x0651,0x0640,0x0650,0x0651,0x0637,0x0649,
+0x0637,0x064a,0x0639,0x0649,0x0639,0x064a,0x063a,0x0649,
+0x063a,0x064a,0x0633,0x0649,0x0633,0x064a,0x0634,0x0649,
+0x0634,0x064a,0x062d,0x0649,0x062d,0x064a,0x062c,0x0649,
+0x062c,0x064a,0x062e,0x0649,0x062e,0x064a,0x0635,0x0649,
+0x0635,0x064a,0x0636,0x0649,0x0636,0x064a,0x0634,0x062c,
+0x0634,0x062d,0x0634,0x062e,0x0634,0x0645,0x0634,0x0631,
+0x0633,0x0631,0x0635,0x0631,0x0636,0x0631,0x0637,0x0649,
+0x0637,0x064a,0x0639,0x0649,0x0639,0x064a,0x063a,0x0649,
+0x063a,0x064a,0x0633,0x0649,0x0633,0x064a,0x0634,0x0649,
+0x0634,0x064a,0x062d,0x0649,0x062d,0x064a,0x062c,0x0649,
+0x062c,0x064a,0x062e,0x0649,0x062e,0x064a,0x0635,0x0649,
+0x0635,0x064a,0x0636,0x0649,0x0636,0x064a,0x0634,0x062c,
+0x0634,0x062d,0x0634,0x062e,0x0634,0x0645,0x0634,0x0631,
+0x0633,0x0631,0x0635,0x0631,0x0636,0x0631,0x0634,0x062c,
+0x0634,0x062d,0x0634,0x062e,0x0634,0x0645,0x0633,0x0647,
+0x0634,0x0647,0x0637,0x0645,0x0633,0x062c,0x0633,0x062d,
+0x0633,0x062e,0x0634,0x062c,0x0634,0x062d,0x0634,0x062e,
+0x0637,0x0645,0x0638,0x0645,0x0627,0x064b,0x0627,0x064b,
+0x062a,0x062c,0x0645,0x062a,0x062d,0x062c,0x062a,0x062d,
+0x062c,0x062a,0x062d,0x0645,0x062a,0x062e,0x0645,0x062a,
+0x0645,0x062c,0x062a,0x0645,0x062d,0x062a,0x0645,0x062e,
+0x062c,0x0645,0x062d,0x062c,0x0645,0x062d,0x062d,0x0645,
+0x064a,0x062d,0x0645,0x0649,0x0633,0x062d,0x062c,0x0633,
+0x062c,0x062d,0x0633,0x062c,0x0649,0x0633,0x0645,0x062d,
+0x0633,0x0645,0x062d,0x0633,0x0645,0x062c,0x0633,0x0645,
+0x0645,0x0633,0x0645,0x0645,0x0635,0x062d,0x062d,0x0635,
+0x062d,0x062d,0x0635,0x0645,0x0645,0x0634,0x062d,0x0645,
+0x0634,0x062d,0x0645,0x0634,0x062c,0x064a,0x0634,0x0645,
+0x062e,0x0634,0x0645,0x062e,0x0634,0x0645,0x0645,0x0634,
+0x0645,0x0645,0x0636,0x062d,0x0649,0x0636,0x062e,0x0645,
+0x0636,0x062e,0x0645,0x0637,0x0645,0x062d,0x0637,0x0645,
+0x062d,0x0637,0x0645,0x0645,0x0637,0x0645,0x064a,0x0639,
+0x062c,0x0645,0x0639,0x0645,0x0645,0x0639,0x0645,0x0645,
+0x0639,0x0645,0x0649,0x063a,0x0645,0x0645,0x063a,0x0645,
+0x064a,0x063a,0x0645,0x0649,0x0641,0x062e,0x0645,0x0641,
+0x062e,0x0645,0x0642,0x0645,0x062d,0x0642,0x0645,0x0645,
+0x0644,0x062d,0x0645,0x0644,0x062d,0x064a,0x0644,0x062d,
+0x0649,0x0644,0x062c,0x062c,0x0644,0x062c,0x062c,0x0644,
+0x062e,0x0645,0x0644,0x062e,0x0645,0x0644,0x0645,0x062d,
+0x0644,0x0645,0x062d,0x0645,0x062d,0x062c,0x0645,0x062d,
+0x0645,0x0645,0x062d,0x064a,0x0645,0x062c,0x062d,0x0645,
+0x062c,0x0645,0x0645,0x062e,0x062c,0x0645,0x062e,0x0645,
+0x0645,0x062c,0x062e,0x0647,0x0645,0x062c,0x0647,0x0645,
+0x0645,0x0646,0x062d,0x0645,0x0646,0x062d,0x0649,0x0646,
+0x062c,0x0645,0x0646,0x062c,0x0645,0x0646,0x062c,0x0649,
+0x0646,0x0645,0x064a,0x0646,0x0645,0x0649,0x064a,0x0645,
+0x0645,0x064a,0x0645,0x0645,0x0628,0x062e,0x064a,0x062a,
+0x062c,0x064a,0x062a,0x062c,0x0649,0x062a,0x062e,0x064a,
+0x062a,0x062e,0x0649,0x062a,0x0645,0x064a,0x062a,0x0645,
+0x0649,0x062c,0x0645,0x064a,0x062c,0x062d,0x0649,0x062c,
+0x0645,0x0649,0x0633,0x062e,0x0649,0x0635,0x062d,0x064a,
+0x0634,0x062d,0x064a,0x0636,0x062d,0x064a,0x0644,0x062c,
+0x064a,0x0644,0x0645,0x064a,0x064a,0x062d,0x064a,0x064a,
+0x062c,0x064a,0x064a,0x0645,0x064a,0x0645,0x0645,0x064a,
+0x0642,0x0645,0x064a,0x0646,0x062d,0x064a,0x0642,0x0645,
+0x062d,0x0644,0x062d,0x0645,0x0639,0x0645,0x064a,0x0643,
+0x0645,0x064a,0x0646,0x062c,0x062d,0x0645,0x062e,0x064a,
+0x0644,0x062c,0x0645,0x0643,0x0645,0x0645,0x0644,0x062c,
+0x0645,0x0646,0x062c,0x062d,0x062c,0x062d,0x064a,0x062d,
+0x062c,0x064a,0x0645,0x062c,0x064a,0x0641,0x0645,0x064a,
+0x0628,0x062d,0x064a,0x0643,0x0645,0x0645,0x0639,0x062c,
+0x0645,0x0635,0x0645,0x0645,0x0633,0x062e,0x064a,0x0646,
+0x062c,0x064a,0x0635,0x0644,0x06d2,0x0642,0x0644,0x06d2,
+0x0627,0x0644,0x0644,0x0647,0x0627,0x0643,0x0628,0x0631,
+0x0645,0x062d,0x0645,0x062f,0x0635,0x0644,0x0639,0x0645,
+0x0631,0x0633,0x0648,0x0644,0x0639,0x0644,0x064a,0x0647,
+0x0648,0x0633,0x0644,0x0645,0x0635,0x0644,0x0649,0x0635,
+0x0644,0x0649,0x0020,0x0627,0x0644,0x0644,0x0647,0x0020,
+0x0639,0x0644,0x064a,0x0647,0x0020,0x0648,0x0633,0x0644,
+0x0645,0x062c,0x0644,0x0020,0x062c,0x0644,0x0627,0x0644,
+0x0647,0x0631,0x06cc,0x0627,0x0644,0x002c,0x3001,0x3002,
+0x003a,0x003b,0x0021,0x003f,0x3016,0x3017,0x2026,0x2025,
+0x2014,0x2013,0x005f,0x005f,0x0028,0x0029,0x007b,0x007d,
+0x3014,0x3015,0x3010,0x3011,0x300a,0x300b,0x3008,0x3009,
+0x300c,0x300d,0x300e,0x300f,0x005b,0x005d,0x203e,0x203e,
+0x203e,0x203e,0x005f,0x005f,0x005f,0x002c,0x3001,0x002e,
+0x003b,0x003a,0x003f,0x0021,0x2014,0x0028,0x0029,0x007b,
+0x007d,0x3014,0x3015,0x0023,0x0026,0x002a,0x002b,0x002d,
+0x003c,0x003e,0x003d,0x005c,0x0024,0x0025,0x0040,0x0020,
+0x064b,0x0640,0x064b,0x0020,0x064c,0x0020,0x064d,0x0020,
+0x064e,0x0640,0x064e,0x0020,0x064f,0x0640,0x064f,0x0020,
+0x0650,0x0640,0x0650,0x0020,0x0651,0x0640,0x0651,0x0020,
+0x0652,0x0640,0x0652,0x0621,0x0622,0x0622,0x0623,0x0623,
+0x0624,0x0624,0x0625,0x0625,0x0626,0x0626,0x0626,0x0626,
+0x0627,0x0627,0x0628,0x0628,0x0628,0x0628,0x0629,0x0629,
+0x062a,0x062a,0x062a,0x062a,0x062b,0x062b,0x062b,0x062b,
+0x062c,0x062c,0x062c,0x062c,0x062d,0x062d,0x062d,0x062d,
+0x062e,0x062e,0x062e,0x062e,0x062f,0x062f,0x0630,0x0630,
+0x0631,0x0631,0x0632,0x0632,0x0633,0x0633,0x0633,0x0633,
+0x0634,0x0634,0x0634,0x0634,0x0635,0x0635,0x0635,0x0635,
+0x0636,0x0636,0x0636,0x0636,0x0637,0x0637,0x0637,0x0637,
+0x0638,0x0638,0x0638,0x0638,0x0639,0x0639,0x0639,0x0639,
+0x063a,0x063a,0x063a,0x063a,0x0641,0x0641,0x0641,0x0641,
+0x0642,0x0642,0x0642,0x0642,0x0643,0x0643,0x0643,0x0643,
+0x0644,0x0644,0x0644,0x0644,0x0645,0x0645,0x0645,0x0645,
+0x0646,0x0646,0x0646,0x0646,0x0647,0x0647,0x0647,0x0647,
+0x0648,0x0648,0x0649,0x0649,0x064a,0x064a,0x064a,0x064a,
+0x0644,0x0622,0x0644,0x0622,0x0644,0x0623,0x0644,0x0623,
+0x0644,0x0625,0x0644,0x0625,0x0644,0x0627,0x0644,0x0627,
+};
+
+
+#define UCS4_BMPHALFFULLMIN 0xff00
+#define UCS4_BMPHALFFULLMAX 0xffef
+
+static const unsigned short ucs4_bmphalffulldecomptab[240] = {
+ 0x0000,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,
+ 0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f,
+ 0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,
+ 0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f,
+ 0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,
+ 0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,
+ 0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,
+ 0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f,
+ 0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,
+ 0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f,
+ 0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,
+ 0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x2985,
+ 0x2986,0x3002,0x300c,0x300d,0x3001,0x30fb,0x30f2,0x30a1,
+ 0x30a3,0x30a5,0x30a7,0x30a9,0x30e3,0x30e5,0x30e7,0x30c3,
+ 0x30fc,0x30a2,0x30a4,0x30a6,0x30a8,0x30aa,0x30ab,0x30ad,
+ 0x30af,0x30b1,0x30b3,0x30b5,0x30b7,0x30b9,0x30bb,0x30bd,
+ 0x30bf,0x30c1,0x30c4,0x30c6,0x30c8,0x30ca,0x30cb,0x30cc,
+ 0x30cd,0x30ce,0x30cf,0x30d2,0x30d5,0x30d8,0x30db,0x30de,
+ 0x30df,0x30e0,0x30e1,0x30e2,0x30e4,0x30e6,0x30e8,0x30e9,
+ 0x30ea,0x30eb,0x30ec,0x30ed,0x30ef,0x30f3,0x3099,0x309a,
+ 0x3164,0x3131,0x3132,0x3133,0x3134,0x3135,0x3136,0x3137,
+ 0x3138,0x3139,0x313a,0x313b,0x313c,0x313d,0x313e,0x313f,
+ 0x3140,0x3141,0x3142,0x3143,0x3144,0x3145,0x3146,0x3147,
+ 0x3148,0x3149,0x314a,0x314b,0x314c,0x314d,0x314e,0x0000,
+ 0x0000,0x0000,0x314f,0x3150,0x3151,0x3152,0x3153,0x3154,
+ 0x0000,0x0000,0x3155,0x3156,0x3157,0x3158,0x3159,0x315a,
+ 0x0000,0x0000,0x315b,0x315c,0x315d,0x315e,0x315f,0x3160,
+ 0x0000,0x0000,0x3161,0x3162,0x3163,0x0000,0x0000,0x0000,
+ 0x00a2,0x00a3,0x00ac,0x00af,0x00a6,0x00a5,0x20a9,0x0000,
+ 0x2502,0x2190,0x2191,0x2192,0x2193,0x25a0,0x25cb,0x0000
+};
+
+/* SMP decompositions */
+
+ /* Musical */
+
+#define UCS4_SMPMUSIC1MIN 0x1d15e
+#define UCS4_SMPMUSIC1MAX 0x1d164
+
+static const unsigned long ucs4_smpmusic1decomptab[7][2] = {
+ {0x1d157,0x1d165},{0x1d158,0x1d165},{0x1d15f,0x1d16e},{0x1d15f,0x1d16f},
+ {0x1d15f,0x1d170},{0x1d15f,0x1d171},{0x1d15f,0x1d172}
+};
+
+
+#define UCS4_SMPMUSIC2MIN 0x1d1bb
+#define UCS4_SMPMUSIC2MAX 0x1d1c0
+
+static const unsigned long ucs4_smpmusic2decomptab[6][2] = {
+ {0x1d1b9,0x1d165},{0x1d1ba,0x1d165},{0x1d1bb,0x1d16e},
+ {0x1d1bc,0x1d16e},{0x1d1bb,0x1d16f},{0x1d1bc,0x1d16f}
+};
+
+
+#define UCS4_SMPMATHMIN 0x1d400
+#define UCS4_SMPMATHMAX 0x1d7ff
+
+ /* Mathematical - 0 means hole (no decomposition) */
+static const unsigned short ucs4_smpmathdecomptab[1024] = {
+ 0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,
+ 0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,0x0050,
+ 0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,
+ 0x0059,0x005a,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,
+ 0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,
+ 0x006f,0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,
+ 0x0077,0x0078,0x0079,0x007a,0x0041,0x0042,0x0043,0x0044,
+ 0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,
+ 0x004d,0x004e,0x004f,0x0050,0x0051,0x0052,0x0053,0x0054,
+ 0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x0061,0x0062,
+ 0x0063,0x0064,0x0065,0x0066,0x0067,0x0000,0x0069,0x006a,
+ 0x006b,0x006c,0x006d,0x006e,0x006f,0x0070,0x0071,0x0072,
+ 0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,
+ 0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,
+ 0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,0x0050,
+ 0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,
+ 0x0059,0x005a,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,
+ 0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,
+ 0x006f,0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,
+ 0x0077,0x0078,0x0079,0x007a,0x0041,0x0000,0x0043,0x0044,
+ 0x0000,0x0000,0x0047,0x0000,0x004a,0x004b,0x0000,0x0000,
+ 0x004e,0x004f,0x0050,0x0051,0x0000,0x0053,0x0054,0x0055,
+ 0x0000,0x0056,0x0057,0x0058,0x0059,0x005a,0x0061,0x0062,
+ 0x0063,0x0064,0x0000,0x0066,0x0000,0x0068,0x0069,0x006a,
+ 0x006b,0x006c,0x006d,0x006e,0x0000,0x0070,0x0071,0x0072,
+ 0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,
+ 0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,
+ 0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,0x0050,
+ 0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,
+ 0x0059,0x005a,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,
+ 0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,
+ 0x006f,0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,
+ 0x0077,0x0078,0x0079,0x007a,0x0041,0x0042,0x0000,0x0044,
+ 0x0045,0x0046,0x0047,0x0000,0x004a,0x004b,0x004c,0x004d,
+ 0x0000,0x004e,0x004f,0x0050,0x0051,0x0000,0x0053,0x0054,
+ 0x0055,0x0056,0x0057,0x0058,0x0059,0x0000,0x0061,0x0062,
+ 0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,
+ 0x006b,0x006c,0x006d,0x006e,0x006f,0x0070,0x0071,0x0072,
+ 0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,
+ 0x0041,0x0042,0x0000,0x0044,0x0045,0x0046,0x0047,0x0000,
+ 0x0049,0x004a,0x004b,0x004c,0x004d,0x0000,0x004f,0x0000,
+ 0x0000,0x0000,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,
+ 0x0059,0x0000,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,
+ 0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,
+ 0x006f,0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,
+ 0x0077,0x0078,0x0079,0x007a,0x0041,0x0042,0x0043,0x0044,
+ 0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,
+ 0x004d,0x004e,0x004f,0x0050,0x0051,0x0052,0x0053,0x0054,
+ 0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x0061,0x0062,
+ 0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,
+ 0x006b,0x006c,0x006d,0x006e,0x006f,0x0070,0x0071,0x0072,
+ 0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,
+ 0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,
+ 0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,0x0050,
+ 0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,
+ 0x0059,0x005a,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,
+ 0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,
+ 0x006f,0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,
+ 0x0077,0x0078,0x0079,0x007a,0x0041,0x0042,0x0043,0x0044,
+ 0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,
+ 0x004d,0x004e,0x004f,0x0050,0x0051,0x0052,0x0053,0x0054,
+ 0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x0061,0x0062,
+ 0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,
+ 0x006b,0x006c,0x006d,0x006e,0x006f,0x0070,0x0071,0x0072,
+ 0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,
+ 0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,
+ 0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,0x0050,
+ 0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,
+ 0x0059,0x005a,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,
+ 0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,
+ 0x006f,0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,
+ 0x0077,0x0078,0x0079,0x007a,0x0041,0x0042,0x0043,0x0044,
+ 0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,
+ 0x004d,0x004e,0x004f,0x0050,0x0051,0x0052,0x0053,0x0054,
+ 0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x0061,0x0062,
+ 0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,
+ 0x006b,0x006c,0x006d,0x006e,0x006f,0x0070,0x0071,0x0072,
+ 0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,
+ 0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,
+ 0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,0x0050,
+ 0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,
+ 0x0059,0x005a,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,
+ 0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,
+ 0x006f,0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,
+ 0x0077,0x0078,0x0079,0x007a,0x0131,0x0237,0x0000,0x0000,
+ 0x0391,0x0392,0x0393,0x0394,0x0395,0x0396,0x0397,0x0398,
+ 0x0399,0x039a,0x039b,0x039c,0x039d,0x039e,0x039f,0x03a0,
+ 0x03a1,0x03f4,0x03a3,0x03a4,0x03a5,0x03a6,0x03a7,0x03a8,
+ 0x03a9,0x2207,0x03b1,0x03b2,0x03b3,0x03b4,0x03b5,0x03b6,
+ 0x03b7,0x03b8,0x03b9,0x03ba,0x03bb,0x03bc,0x03bd,0x03be,
+ 0x03bf,0x03c0,0x03c1,0x03c2,0x03c3,0x03c4,0x03c5,0x03c6,
+ 0x03c7,0x03c8,0x03c9,0x2202,0x03f5,0x03d1,0x03f0,0x03d5,
+ 0x03f1,0x03d6,0x0391,0x0392,0x0393,0x0394,0x0395,0x0396,
+ 0x0397,0x0398,0x0399,0x039a,0x039b,0x039c,0x039d,0x039e,
+ 0x039f,0x03a0,0x03a1,0x03f4,0x03a3,0x03a4,0x03a5,0x03a6,
+ 0x03a7,0x03a8,0x03a9,0x2207,0x03b1,0x03b2,0x03b3,0x03b4,
+ 0x03b5,0x03b6,0x03b7,0x03b8,0x03b9,0x03ba,0x03bb,0x03bc,
+ 0x03bd,0x03be,0x03bf,0x03c0,0x03c1,0x03c2,0x03c3,0x03c4,
+ 0x03c5,0x03c6,0x03c7,0x03c8,0x03c9,0x2202,0x03f5,0x03d1,
+ 0x03f0,0x03d5,0x03f1,0x03d6,0x0391,0x0392,0x0393,0x0394,
+ 0x0395,0x0396,0x0397,0x0398,0x0399,0x039a,0x039b,0x039c,
+ 0x039d,0x039e,0x039f,0x03a0,0x03a1,0x03f4,0x03a3,0x03a4,
+ 0x03a5,0x03a6,0x03a7,0x03a8,0x03a9,0x2207,0x03b1,0x03b2,
+ 0x03b3,0x03b4,0x03b5,0x03b6,0x03b7,0x03b8,0x03b9,0x03ba,
+ 0x03bb,0x03bc,0x03bd,0x03be,0x03bf,0x03c0,0x03c1,0x03c2,
+ 0x03c3,0x03c4,0x03c5,0x03c6,0x03c7,0x03c8,0x03c9,0x2202,
+ 0x03f5,0x03d1,0x03f0,0x03d5,0x03f1,0x03d6,0x0391,0x0392,
+ 0x0393,0x0394,0x0395,0x0396,0x0397,0x0398,0x0399,0x039a,
+ 0x039b,0x039c,0x039d,0x039e,0x039f,0x03a0,0x03a1,0x03f4,
+ 0x03a3,0x03a4,0x03a5,0x03a6,0x03a7,0x03a8,0x03a9,0x2207,
+ 0x03b1,0x03b2,0x03b3,0x03b4,0x03b5,0x03b6,0x03b7,0x03b8,
+ 0x03b9,0x03ba,0x03bb,0x03bc,0x03bd,0x03be,0x03bf,0x03c0,
+ 0x03c1,0x03c2,0x03c3,0x03c4,0x03c5,0x03c6,0x03c7,0x03c8,
+ 0x03c9,0x2202,0x03f5,0x03d1,0x03f0,0x03d5,0x03f1,0x03d6,
+ 0x0391,0x0392,0x0393,0x0394,0x0395,0x0396,0x0397,0x0398,
+ 0x0399,0x039a,0x039b,0x039c,0x039d,0x039e,0x039f,0x03a0,
+ 0x03a1,0x03f4,0x03a3,0x03a4,0x03a5,0x03a6,0x03a7,0x03a8,
+ 0x03a9,0x2207,0x03b1,0x03b2,0x03b3,0x03b4,0x03b5,0x03b6,
+ 0x03b7,0x03b8,0x03b9,0x03ba,0x03bb,0x03bc,0x03bd,0x03be,
+ 0x03bf,0x03c0,0x03c1,0x03c2,0x03c3,0x03c4,0x03c5,0x03c6,
+ 0x03c7,0x03c8,0x03c9,0x2202,0x03f5,0x03d1,0x03f0,0x03d5,
+ 0x03f1,0x03d6,0x03dc,0x03dd,0x0000,0x0000,0x0030,0x0031,
+ 0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,
+ 0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,
+ 0x0038,0x0039,0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,
+ 0x0036,0x0037,0x0038,0x0039,0x0030,0x0031,0x0032,0x0033,
+ 0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x0030,0x0031,
+ 0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039
+};
+
+/* SIP decompositions */
+
+#define UCS4_SIPMIN 0x2f800
+#define UCS4_SIPMAX 0x2fa1d
+
+ /* CJK compatibility ideographs - no holes */
+static const unsigned long ucs4_sipdecomptab[542] = {
+ 0x4e3d, 0x4e38, 0x4e41,0x20122, 0x4f60, 0x4fae, 0x4fbb, 0x5002,
+ 0x507a, 0x5099, 0x50e7, 0x50cf, 0x349e,0x2063a, 0x514d, 0x5154,
+ 0x5164, 0x5177,0x2051c, 0x34b9, 0x5167, 0x518d,0x2054b, 0x5197,
+ 0x51a4, 0x4ecc, 0x51ac, 0x51b5,0x291df, 0x51f5, 0x5203, 0x34df,
+ 0x523b, 0x5246, 0x5272, 0x5277, 0x3515, 0x52c7, 0x52c9, 0x52e4,
+ 0x52fa, 0x5305, 0x5306, 0x5317, 0x5349, 0x5351, 0x535a, 0x5373,
+ 0x537d, 0x537f, 0x537f, 0x537f,0x20a2c, 0x7070, 0x53ca, 0x53df,
+ 0x20b63, 0x53eb, 0x53f1, 0x5406, 0x549e, 0x5438, 0x5448, 0x5468,
+ 0x54a2, 0x54f6, 0x5510, 0x5553, 0x5563, 0x5584, 0x5584, 0x5599,
+ 0x55ab, 0x55b3, 0x55c2, 0x5716, 0x5606, 0x5717, 0x5651, 0x5674,
+ 0x5207, 0x58ee, 0x57ce, 0x57f4, 0x580d, 0x578b, 0x5832, 0x5831,
+ 0x58ac,0x214e4, 0x58f2, 0x58f7, 0x5906, 0x591a, 0x5922, 0x5962,
+ 0x216a8,0x216ea, 0x59ec, 0x5a1b, 0x5a27, 0x59d8, 0x5a66, 0x36ee,
+ 0x36fc, 0x5b08, 0x5b3e, 0x5b3e,0x219c8, 0x5bc3, 0x5bd8, 0x5be7,
+ 0x5bf3,0x21b18, 0x5bff, 0x5c06, 0x5f53, 0x5c22, 0x3781, 0x5c60,
+ 0x5c6e, 0x5cc0, 0x5c8d,0x21de4, 0x5d43,0x21de6, 0x5d6e, 0x5d6b,
+ 0x5d7c, 0x5de1, 0x5de2, 0x382f, 0x5dfd, 0x5e28, 0x5e3d, 0x5e69,
+ 0x3862,0x22183, 0x387c, 0x5eb0, 0x5eb3, 0x5eb6, 0x5eca,0x2a392,
+ 0x5efe,0x22331,0x22331, 0x8201, 0x5f22, 0x5f22, 0x38c7,0x232b8,
+ 0x261da, 0x5f62, 0x5f6b, 0x38e3, 0x5f9a, 0x5fcd, 0x5fd7, 0x5ff9,
+ 0x6081, 0x393a, 0x391c, 0x6094,0x226d4, 0x60c7, 0x6148, 0x614c,
+ 0x614e, 0x614c, 0x617a, 0x618e, 0x61b2, 0x61a4, 0x61af, 0x61de,
+ 0x61f2, 0x61f6, 0x6210, 0x621b, 0x625d, 0x62b1, 0x62d4, 0x6350,
+ 0x22b0c, 0x633d, 0x62fc, 0x6368, 0x6383, 0x63e4,0x22bf1, 0x6422,
+ 0x63c5, 0x63a9, 0x3a2e, 0x6469, 0x647e, 0x649d, 0x6477, 0x3a6c,
+ 0x654f, 0x656c,0x2300a, 0x65e3, 0x66f8, 0x6649, 0x3b19, 0x6691,
+ 0x3b08, 0x3ae4, 0x5192, 0x5195, 0x6700, 0x669c, 0x80ad, 0x43d9,
+ 0x6717, 0x671b, 0x6721, 0x675e, 0x6753,0x233c3, 0x3b49, 0x67fa,
+ 0x6785, 0x6852, 0x6885,0x2346d, 0x688e, 0x681f, 0x6914, 0x3b9d,
+ 0x6942, 0x69a3, 0x69ea, 0x6aa8,0x236a3, 0x6adb, 0x3c18, 0x6b21,
+ 0x238a7, 0x6b54, 0x3c4e, 0x6b72, 0x6b9f, 0x6bba, 0x6bbb,0x23a8d,
+ 0x21d0b,0x23afa, 0x6c4e,0x23cbc, 0x6cbf, 0x6ccd, 0x6c67, 0x6d16,
+ 0x6d3e, 0x6d77, 0x6d41, 0x6d69, 0x6d78, 0x6d85,0x23d1e, 0x6d34,
+ 0x6e2f, 0x6e6e, 0x3d33, 0x6ecb, 0x6ec7,0x23ed1, 0x6df9, 0x6f6e,
+ 0x23f5e,0x23f8e, 0x6fc6, 0x7039, 0x701e, 0x701b, 0x3d96, 0x704a,
+ 0x707d, 0x7077, 0x70ad,0x20525, 0x7145,0x24263, 0x719c,0x243ab,
+ 0x7228, 0x7235, 0x7250,0x24608, 0x7280, 0x7295,0x24735,0x24814,
+ 0x737a, 0x738b, 0x3eac, 0x73a5, 0x3eb8, 0x3eb8, 0x7447, 0x745c,
+ 0x7471, 0x7485, 0x74ca, 0x3f1b, 0x7524,0x24c36, 0x753e,0x24c92,
+ 0x7570,0x2219f, 0x7610,0x24fa1,0x24fb8,0x25044, 0x3ffc, 0x4008,
+ 0x76f4,0x250f3,0x250f2,0x25119,0x25133, 0x771e, 0x771f, 0x771f,
+ 0x774a, 0x4039, 0x778b, 0x4046, 0x4096,0x2541d, 0x784e, 0x788c,
+ 0x78cc, 0x40e3,0x25626, 0x7956,0x2569a,0x256c5, 0x798f, 0x79eb,
+ 0x412f, 0x7a40, 0x7a4a, 0x7a4f,0x2597c,0x25aa7,0x25aa7, 0x7aee,
+ 0x4202,0x25bab, 0x7bc6, 0x7bc9, 0x4227,0x25c80, 0x7cd2, 0x42a0,
+ 0x7ce8, 0x7ce3, 0x7d00,0x25f86, 0x7d63, 0x4301, 0x7dc7, 0x7e02,
+ 0x7e45, 0x4334,0x26228,0x26247, 0x4359,0x262d9, 0x7f7a,0x2633e,
+ 0x7f95, 0x7ffa, 0x8005,0x264da,0x26523, 0x8060,0x265a8, 0x8070,
+ 0x2335f, 0x43d5, 0x80b2, 0x8103, 0x440b, 0x813e, 0x5ab5,0x267a7,
+ 0x267b5,0x23393,0x2339c, 0x8201, 0x8204, 0x8f9e, 0x446b, 0x8291,
+ 0x828b, 0x829d, 0x52b3, 0x82b1, 0x82b3, 0x82bd, 0x82e6,0x26b3c,
+ 0x82e5, 0x831d, 0x8363, 0x83ad, 0x8323, 0x83bd, 0x83e7, 0x8457,
+ 0x8353, 0x83ca, 0x83cc, 0x83dc,0x26c36,0x26d6b,0x26cd5, 0x452b,
+ 0x84f1, 0x84f3, 0x8516,0x273ca, 0x8564,0x26f2c, 0x455d, 0x4561,
+ 0x26fb1,0x270d2, 0x456b, 0x8650, 0x865c, 0x8667, 0x8669, 0x86a9,
+ 0x8688, 0x870e, 0x86e2, 0x8779, 0x8728, 0x876b, 0x8786, 0x45d7,
+ 0x87e1, 0x8801, 0x45f9, 0x8860, 0x8863,0x27667, 0x88d7, 0x88de,
+ 0x4635, 0x88fa, 0x34bb,0x278ae,0x27966, 0x46be, 0x46c7, 0x8aa0,
+ 0x8aed, 0x8b8a, 0x8c55,0x27ca8, 0x8cab, 0x8cc1, 0x8d1b, 0x8d77,
+ 0x27f2f,0x20804, 0x8dcb, 0x8dbc, 0x8df0,0x208de, 0x8ed4, 0x8f38,
+ 0x285d2,0x285ed, 0x9094, 0x90f1, 0x9111,0x2872e, 0x911b, 0x9238,
+ 0x92d7, 0x92d8, 0x927c, 0x93f9, 0x9415,0x28bfa, 0x958b, 0x4995,
+ 0x95b7,0x28d77, 0x49e6, 0x96c3, 0x5db2, 0x9723,0x29145,0x2921a,
+ 0x4a6e, 0x4a76, 0x97e0,0x2940a, 0x4ab2,0x29496, 0x980b, 0x980b,
+ 0x9829,0x295b6, 0x98e2, 0x4b33, 0x9929, 0x99a7, 0x99c2, 0x99fe,
+ 0x4bce,0x29b30, 0x9b12, 0x9c40, 0x9cfd, 0x4cce, 0x4ced, 0x9d67,
+ 0x2a0ce, 0x4cf8,0x2a105,0x2a20e,0x2a291, 0x9ebb, 0x4d56, 0x9ef9,
+ 0x9efe, 0x9f05, 0x9f0f, 0x9f16, 0x9f3b,0x2a600
+};
diff --git a/imap/src/charset/gb_12345.c b/imap/src/charset/gb_12345.c
new file mode 100644
index 00000000..88eaccb3
--- /dev/null
+++ b/imap/src/charset/gb_12345.c
@@ -0,0 +1,1114 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: GB 12345 conversion table
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 May 1998
+ * Last Edited: 30 August 2006
+ */
+
+/* GB 12345 is the national standard of the People's Republic of China
+ * (mainland China) for traditional Chinese characters.
+ */
+
+#define BASE_GB12345_KU 0xa1
+#define BASE_GB12345_TEN 0xa1
+#define MAX_GB12345_KU 89
+#define MAX_GB12345_TEN 94
+
+
+#define GB12345TOUNICODE(c,c1,ku,ten) \
+ ((((ku = (c & 0x7f) - BASE_GB12345_KU) < MAX_GB12345_KU) && \
+ ((ten = (c1 & 0x7f) - BASE_GB12345_TEN) < MAX_GB12345_TEN)) ? \
+ gb12345tab[ku][ten] : UBOGON)
+
+
+static const unsigned short gb12345tab[MAX_GB12345_KU][MAX_GB12345_TEN] = {
+ { /* ku 01 */
+ 0x3000,0x3001,0x3002,0x30fb,0x02c9,0x02c7,0x00a8,0x3003,0x3005,0x2015,
+ 0xff5e,0x2225,0x2026,0x2018,0x2019,0x201c,0x201d,0x3014,0x3015,0x3008,
+ 0x3009,0x300a,0x300b,0x300c,0x300d,0x300e,0x300f,0x3016,0x3017,0x3010,
+ 0x3011,0x00b1,0x00d7,0x00f7,0x2236,0x2227,0x2228,0x2211,0x220f,0x222a,
+ 0x2229,0x2208,0x2237,0x221a,0x22a5,0x2225,0x2220,0x2312,0x2299,0x222b,
+ 0x222e,0x2261,0x224c,0x2248,0x223d,0x221d,0x2260,0x226e,0x226f,0x2264,
+ 0x2265,0x221e,0x2235,0x2234,0x2642,0x2640,0x00b0,0x2032,0x2033,0x2103,
+ 0xff04,0x00a4,0xffe0,0xffe1,0x2030,0x00a7,0x2116,0x2606,0x2605,0x25cb,
+ 0x25cf,0x25ce,0x25c7,0x25c6,0x25a1,0x25a0,0x25b3,0x25b2,0x203b,0x2192,
+ 0x2190,0x2191,0x2193,0x3013
+ },
+ { /* ku 02 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x2488,0x2489,0x248a,0x248b,
+ 0x248c,0x248d,0x248e,0x248f,0x2490,0x2491,0x2492,0x2493,0x2494,0x2495,
+ 0x2496,0x2497,0x2498,0x2499,0x249a,0x249b,0x2474,0x2475,0x2476,0x2477,
+ 0x2478,0x2479,0x247a,0x247b,0x247c,0x247d,0x247e,0x247f,0x2480,0x2481,
+ 0x2482,0x2483,0x2484,0x2485,0x2486,0x2487,0x2460,0x2461,0x2462,0x2463,
+ 0x2464,0x2465,0x2466,0x2467,0x2468,0x2469,UBOGON,UBOGON,0x3220,0x3221,
+ 0x3222,0x3223,0x3224,0x3225,0x3226,0x3227,0x3228,0x3229,UBOGON,UBOGON,
+ 0x2160,0x2161,0x2162,0x2163,0x2164,0x2165,0x2166,0x2167,0x2168,0x2169,
+ 0x216a,0x216b,UBOGON,UBOGON
+ },
+ { /* ku 03 */
+ 0xff01,0xff02,0xff03,0xffe5,0xff05,0xff06,0xff07,0xff08,0xff09,0xff0a,
+ 0xff0b,0xff0c,0xff0d,0xff0e,0xff0f,0xff10,0xff11,0xff12,0xff13,0xff14,
+ 0xff15,0xff16,0xff17,0xff18,0xff19,0xff1a,0xff1b,0xff1c,0xff1d,0xff1e,
+ 0xff1f,0xff20,0xff21,0xff22,0xff23,0xff24,0xff25,0xff26,0xff27,0xff28,
+ 0xff29,0xff2a,0xff2b,0xff2c,0xff2d,0xff2e,0xff2f,0xff30,0xff31,0xff32,
+ 0xff33,0xff34,0xff35,0xff36,0xff37,0xff38,0xff39,0xff3a,0xff3b,0xff3c,
+ 0xff3d,0xff3e,0xff3f,0xff40,0xff41,0xff42,0xff43,0xff44,0xff45,0xff46,
+ 0xff47,0xff48,0xff49,0xff4a,0xff4b,0xff4c,0xff4d,0xff4e,0xff4f,0xff50,
+ 0xff51,0xff52,0xff53,0xff54,0xff55,0xff56,0xff57,0xff58,0xff59,0xff5a,
+ 0xff5b,0xff5c,0xff5d,0xffe3
+ },
+ { /* ku 04 */
+ 0x3041,0x3042,0x3043,0x3044,0x3045,0x3046,0x3047,0x3048,0x3049,0x304a,
+ 0x304b,0x304c,0x304d,0x304e,0x304f,0x3050,0x3051,0x3052,0x3053,0x3054,
+ 0x3055,0x3056,0x3057,0x3058,0x3059,0x305a,0x305b,0x305c,0x305d,0x305e,
+ 0x305f,0x3060,0x3061,0x3062,0x3063,0x3064,0x3065,0x3066,0x3067,0x3068,
+ 0x3069,0x306a,0x306b,0x306c,0x306d,0x306e,0x306f,0x3070,0x3071,0x3072,
+ 0x3073,0x3074,0x3075,0x3076,0x3077,0x3078,0x3079,0x307a,0x307b,0x307c,
+ 0x307d,0x307e,0x307f,0x3080,0x3081,0x3082,0x3083,0x3084,0x3085,0x3086,
+ 0x3087,0x3088,0x3089,0x308a,0x308b,0x308c,0x308d,0x308e,0x308f,0x3090,
+ 0x3091,0x3092,0x3093,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 05 */
+ 0x30a1,0x30a2,0x30a3,0x30a4,0x30a5,0x30a6,0x30a7,0x30a8,0x30a9,0x30aa,
+ 0x30ab,0x30ac,0x30ad,0x30ae,0x30af,0x30b0,0x30b1,0x30b2,0x30b3,0x30b4,
+ 0x30b5,0x30b6,0x30b7,0x30b8,0x30b9,0x30ba,0x30bb,0x30bc,0x30bd,0x30be,
+ 0x30bf,0x30c0,0x30c1,0x30c2,0x30c3,0x30c4,0x30c5,0x30c6,0x30c7,0x30c8,
+ 0x30c9,0x30ca,0x30cb,0x30cc,0x30cd,0x30ce,0x30cf,0x30d0,0x30d1,0x30d2,
+ 0x30d3,0x30d4,0x30d5,0x30d6,0x30d7,0x30d8,0x30d9,0x30da,0x30db,0x30dc,
+ 0x30dd,0x30de,0x30df,0x30e0,0x30e1,0x30e2,0x30e3,0x30e4,0x30e5,0x30e6,
+ 0x30e7,0x30e8,0x30e9,0x30ea,0x30eb,0x30ec,0x30ed,0x30ee,0x30ef,0x30f0,
+ 0x30f1,0x30f2,0x30f3,0x30f4,0x30f5,0x30f6,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 06 */
+ 0x0391,0x0392,0x0393,0x0394,0x0395,0x0396,0x0397,0x0398,0x0399,0x039a,
+ 0x039b,0x039c,0x039d,0x039e,0x039f,0x03a0,0x03a1,0x03a3,0x03a4,0x03a5,
+ 0x03a6,0x03a7,0x03a8,0x03a9,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x03b1,0x03b2,0x03b3,0x03b4,0x03b5,0x03b6,0x03b7,0x03b8,
+ 0x03b9,0x03ba,0x03bb,0x03bc,0x03bd,0x03be,0x03bf,0x03c0,0x03c1,0x03c3,
+ 0x03c4,0x03c5,0x03c6,0x03c7,0x03c8,0x03c9,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 07 */
+ 0x0410,0x0411,0x0412,0x0413,0x0414,0x0415,0x0401,0x0416,0x0417,0x0418,
+ 0x0419,0x041a,0x041b,0x041c,0x041d,0x041e,0x041f,0x0420,0x0421,0x0422,
+ 0x0423,0x0424,0x0425,0x0426,0x0427,0x0428,0x0429,0x042a,0x042b,0x042c,
+ 0x042d,0x042e,0x042f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x0430,0x0431,
+ 0x0432,0x0433,0x0434,0x0435,0x0451,0x0436,0x0437,0x0438,0x0439,0x043a,
+ 0x043b,0x043c,0x043d,0x043e,0x043f,0x0440,0x0441,0x0442,0x0443,0x0444,
+ 0x0445,0x0446,0x0447,0x0448,0x0449,0x044a,0x044b,0x044c,0x044d,0x044e,
+ 0x044f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 08 */
+ 0x0101,0x00e1,0x01ce,0x00e0,0x0113,0x00e9,0x011b,0x00e8,0x012b,0x00ed,
+ 0x01d0,0x00ec,0x014d,0x00f3,0x01d2,0x00f2,0x016b,0x00fa,0x01d4,0x00f9,
+ 0x01d6,0x01d8,0x01da,0x01dc,0x00fc,0x00ea,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3105,0x3106,0x3107,0x3108,
+ 0x3109,0x310a,0x310b,0x310c,0x310d,0x310e,0x310f,0x3110,0x3111,0x3112,
+ 0x3113,0x3114,0x3115,0x3116,0x3117,0x3118,0x3119,0x311a,0x311b,0x311c,
+ 0x311d,0x311e,0x311f,0x3120,0x3121,0x3122,0x3123,0x3124,0x3125,0x3126,
+ 0x3127,0x3128,0x3129,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 09 */
+ UBOGON,UBOGON,UBOGON,0x2500,0x2501,0x2502,0x2503,0x2504,0x2505,0x2506,
+ 0x2507,0x2508,0x2509,0x250a,0x250b,0x250c,0x250d,0x250e,0x250f,0x2510,
+ 0x2511,0x2512,0x2513,0x2514,0x2515,0x2516,0x2517,0x2518,0x2519,0x251a,
+ 0x251b,0x251c,0x251d,0x251e,0x251f,0x2520,0x2521,0x2522,0x2523,0x2524,
+ 0x2525,0x2526,0x2527,0x2528,0x2529,0x252a,0x252b,0x252c,0x252d,0x252e,
+ 0x252f,0x2530,0x2531,0x2532,0x2533,0x2534,0x2535,0x2536,0x2537,0x2538,
+ 0x2539,0x253a,0x253b,0x253c,0x253d,0x253e,0x253f,0x2540,0x2541,0x2542,
+ 0x2543,0x2544,0x2545,0x2546,0x2547,0x2548,0x2549,0x254a,0x254b,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 10 */
+ 0x554a,0x963f,0x57c3,0x6328,0x54ce,0x5509,0x54c0,0x769a,0x764c,0x85f9,
+ 0x77ee,0x827e,0x7919,0x611b,0x9698,0x978d,0x6c28,0x5b89,0x4ffa,0x6309,
+ 0x6697,0x5cb8,0x80fa,0x6848,0x9aaf,0x6602,0x76ce,0x51f9,0x6556,0x71ac,
+ 0x7ff1,0x8956,0x50b2,0x5965,0x61ca,0x6fb3,0x82ad,0x634c,0x6252,0x53ed,
+ 0x5427,0x7b06,0x516b,0x75a4,0x5df4,0x62d4,0x8dcb,0x9776,0x628a,0x8019,
+ 0x58e9,0x9738,0x7f77,0x7238,0x767d,0x67cf,0x767e,0x64fa,0x4f70,0x6557,
+ 0x62dc,0x7a17,0x6591,0x73ed,0x642c,0x6273,0x822c,0x9812,0x677f,0x7248,
+ 0x626e,0x62cc,0x4f34,0x74e3,0x534a,0x8fa6,0x7d46,0x90a6,0x5e6b,0x6886,
+ 0x699c,0x8180,0x7d81,0x68d2,0x78c5,0x868c,0x938a,0x508d,0x8b17,0x82de,
+ 0x80de,0x5305,0x8912,0x5265
+ },
+ { /* ku 11 */
+ 0x8584,0x96f9,0x4fdd,0x5821,0x98fd,0x5bf6,0x62b1,0x5831,0x66b4,0x8c79,
+ 0x9b91,0x7206,0x676f,0x7891,0x60b2,0x5351,0x5317,0x8f29,0x80cc,0x8c9d,
+ 0x92c7,0x500d,0x72fd,0x5099,0x618a,0x7119,0x88ab,0x5954,0x82ef,0x672c,
+ 0x7b28,0x5d29,0x7db3,0x752d,0x6cf5,0x8e66,0x8ff8,0x903c,0x9f3b,0x6bd4,
+ 0x9119,0x7b46,0x5f7c,0x78a7,0x84d6,0x853d,0x7562,0x6583,0x6bd6,0x5e63,
+ 0x5e87,0x75f9,0x9589,0x655d,0x5f0a,0x5fc5,0x8f9f,0x58c1,0x81c2,0x907f,
+ 0x965b,0x97ad,0x908a,0x7de8,0x8cb6,0x6241,0x4fbf,0x8b8a,0x535e,0x8fa8,
+ 0x8faf,0x8fae,0x904d,0x6a19,0x5f6a,0x8198,0x8868,0x9c49,0x618b,0x522b,
+ 0x765f,0x5f6c,0x658c,0x7015,0x6ff1,0x8cd3,0x64ef,0x5175,0x51b0,0x67c4,
+ 0x4e19,0x79c9,0x9905,0x70b3
+ },
+ { /* ku 12 */
+ 0x75c5,0x5e76,0x73bb,0x83e0,0x64ad,0x64a5,0x9262,0x6ce2,0x535a,0x52c3,
+ 0x640f,0x9251,0x7b94,0x4f2f,0x5e1b,0x8236,0x8116,0x818a,0x6e24,0x6cca,
+ 0x99c1,0x6355,0x535c,0x54fa,0x88dc,0x57e0,0x4e0d,0x5e03,0x6b65,0x7c3f,
+ 0x90e8,0x6016,0x64e6,0x731c,0x88c1,0x6750,0x624d,0x8ca1,0x776c,0x8e29,
+ 0x91c7,0x5f69,0x83dc,0x8521,0x9910,0x53c3,0x8836,0x6b98,0x615a,0x6158,
+ 0x71e6,0x84bc,0x8259,0x5009,0x6ec4,0x85cf,0x64cd,0x7cd9,0x69fd,0x66f9,
+ 0x8349,0x53a0,0x7b56,0x5074,0x518c,0x6e2c,0x5c64,0x8e6d,0x63d2,0x53c9,
+ 0x832c,0x8336,0x67e5,0x78b4,0x643d,0x5bdf,0x5c94,0x5dee,0x8a6b,0x62c6,
+ 0x67f4,0x8c7a,0x6519,0x647b,0x87ec,0x995e,0x8b92,0x7e8f,0x93df,0x7523,
+ 0x95e1,0x986b,0x660c,0x7316
+ },
+ { /* ku 13 */
+ 0x5834,0x5617,0x5e38,0x9577,0x511f,0x8178,0x5ee0,0x655e,0x66a2,0x5531,
+ 0x5021,0x8d85,0x6284,0x9214,0x671d,0x5632,0x6f6e,0x5de2,0x5435,0x7092,
+ 0x8eca,0x626f,0x64a4,0x63a3,0x5fb9,0x6f88,0x90f4,0x81e3,0x8fb0,0x5875,
+ 0x6668,0x5ff1,0x6c89,0x9673,0x8d81,0x896f,0x6491,0x7a31,0x57ce,0x6a59,
+ 0x6210,0x5448,0x4e58,0x7a0b,0x61f2,0x6f84,0x8aa0,0x627f,0x901e,0x9a01,
+ 0x79e4,0x5403,0x75f4,0x6301,0x5319,0x6c60,0x9072,0x5f1b,0x99b3,0x803b,
+ 0x9f52,0x4f88,0x5c3a,0x8d64,0x7fc5,0x65a5,0x71be,0x5145,0x885d,0x87f2,
+ 0x5d07,0x5bf5,0x62bd,0x916c,0x7587,0x8e8a,0x7a20,0x6101,0x7c4c,0x4ec7,
+ 0x7da2,0x7785,0x919c,0x81ed,0x521d,0x51fa,0x6a71,0x53a8,0x8e87,0x92e4,
+ 0x96db,0x6ec1,0x9664,0x695a
+ },
+ { /* ku 14 */
+ 0x790e,0x5132,0x77d7,0x6410,0x89f8,0x8655,0x63e3,0x5ddd,0x7a7f,0x693d,
+ 0x50b3,0x8239,0x5598,0x4e32,0x7621,0x7a97,0x5e62,0x5e8a,0x95d6,0x5275,
+ 0x5439,0x708a,0x6376,0x9318,0x5782,0x6625,0x693f,0x9187,0x5507,0x6df3,
+ 0x7d14,0x8822,0x6233,0x7dbd,0x75b5,0x8328,0x78c1,0x96cc,0x8fad,0x6148,
+ 0x74f7,0x8a5e,0x6b64,0x523a,0x8cdc,0x6b21,0x8070,0x8471,0x56f1,0x5306,
+ 0x5f9e,0x53e2,0x51d1,0x7c97,0x918b,0x7c07,0x4fc3,0x8ea5,0x7be1,0x7ac4,
+ 0x6467,0x5d14,0x50ac,0x8106,0x7601,0x7cb9,0x6dec,0x7fe0,0x6751,0x5b58,
+ 0x5bf8,0x78cb,0x64ae,0x6413,0x63aa,0x632b,0x932f,0x642d,0x9054,0x7b54,
+ 0x7629,0x6253,0x5927,0x5446,0x6b79,0x50a3,0x6234,0x5e36,0x6b86,0x4ee3,
+ 0x8cb8,0x888b,0x5f85,0x902e
+ },
+ { /* ku 15 */
+ 0x6020,0x803d,0x64d4,0x4e39,0x55ae,0x9132,0x64a3,0x81bd,0x65e6,0x6c2e,
+ 0x4f46,0x619a,0x6de1,0x8a95,0x5f48,0x86cb,0x7576,0x64cb,0x9ee8,0x8569,
+ 0x6a94,0x5200,0x6417,0x8e48,0x5012,0x5cf6,0x79b1,0x5c0e,0x5230,0x7a3b,
+ 0x60bc,0x9053,0x76d7,0x5fb7,0x5f97,0x7684,0x8e6c,0x71c8,0x767b,0x7b49,
+ 0x77aa,0x51f3,0x9127,0x5824,0x4f4e,0x6ef4,0x8fea,0x6575,0x7b1b,0x72c4,
+ 0x6ecc,0x7fdf,0x5ae1,0x62b5,0x5e95,0x5730,0x8482,0x7b2c,0x5e1d,0x5f1f,
+ 0x905e,0x7de0,0x985b,0x6382,0x6ec7,0x7898,0x9ede,0x5178,0x975b,0x588a,
+ 0x96fb,0x4f43,0x7538,0x5e97,0x60e6,0x5960,0x6fb1,0x6bbf,0x7889,0x53fc,
+ 0x96d5,0x51cb,0x5201,0x6389,0x540a,0x91e3,0x8abf,0x8dcc,0x7239,0x789f,
+ 0x8776,0x8fed,0x8adc,0x758a
+ },
+ { /* ku 16 */
+ 0x4e01,0x76ef,0x53ee,0x91d8,0x9802,0x9f0e,0x9320,0x5b9a,0x8a02,0x4e22,
+ 0x6771,0x51ac,0x8463,0x61c2,0x52d5,0x68df,0x4f97,0x606b,0x51cd,0x6d1e,
+ 0x515c,0x6296,0x9b25,0x9661,0x8c46,0x9017,0x75d8,0x90fd,0x7763,0x6bd2,
+ 0x72a2,0x7368,0x8b80,0x5835,0x7779,0x8ced,0x675c,0x934d,0x809a,0x5ea6,
+ 0x6e21,0x5992,0x7aef,0x77ed,0x935b,0x6bb5,0x65b7,0x7dde,0x5806,0x5151,
+ 0x968a,0x5c0d,0x58a9,0x5678,0x8e72,0x6566,0x9813,0x56e4,0x920d,0x76fe,
+ 0x9041,0x6387,0x54c6,0x591a,0x596a,0x579b,0x8eb2,0x6735,0x8dfa,0x8235,
+ 0x5241,0x60f0,0x58ae,0x86fe,0x5ce8,0x9d5d,0x4fc4,0x984d,0x8a1b,0x5a25,
+ 0x60e1,0x5384,0x627c,0x904f,0x9102,0x9913,0x6069,0x800c,0x5152,0x8033,
+ 0x723e,0x990c,0x6d31,0x4e8c
+ },
+ { /* ku 17 */
+ 0x8cb3,0x767c,0x7f70,0x7b4f,0x4f10,0x4e4f,0x95a5,0x6cd5,0x73d0,0x85e9,
+ 0x5e06,0x756a,0x7ffb,0x6a0a,0x792c,0x91e9,0x7e41,0x51e1,0x7169,0x53cd,
+ 0x8fd4,0x7bc4,0x8ca9,0x72af,0x98ef,0x6cdb,0x574a,0x82b3,0x65b9,0x80aa,
+ 0x623f,0x9632,0x59a8,0x4eff,0x8a2a,0x7d21,0x653e,0x83f2,0x975e,0x5561,
+ 0x98db,0x80a5,0x532a,0x8ab9,0x5420,0x80ba,0x5ee2,0x6cb8,0x8cbb,0x82ac,
+ 0x915a,0x5429,0x6c1b,0x5206,0x7d1b,0x58b3,0x711a,0x6c7e,0x7c89,0x596e,
+ 0x4efd,0x5fff,0x61a4,0x7cde,0x8c50,0x5c01,0x6953,0x8702,0x5cf0,0x92d2,
+ 0x98a8,0x760b,0x70fd,0x9022,0x99ae,0x7e2b,0x8af7,0x5949,0x9cf3,0x4f5b,
+ 0x5426,0x592b,0x6577,0x819a,0x5b75,0x6276,0x62c2,0x8f3b,0x5e45,0x6c1f,
+ 0x7b26,0x4f0f,0x4fd8,0x670d
+ },
+ { /* ku 18 */
+ 0x6d6e,0x6daa,0x798f,0x88b1,0x5f17,0x752b,0x64ab,0x8f14,0x4fef,0x91dc,
+ 0x65a7,0x812f,0x8151,0x5e9c,0x8150,0x8d74,0x526f,0x8986,0x8ce6,0x5fa9,
+ 0x5085,0x4ed8,0x961c,0x7236,0x8179,0x8ca0,0x5bcc,0x8a03,0x9644,0x5a66,
+ 0x7e1b,0x5490,0x5676,0x560e,0x8a72,0x6539,0x6982,0x9223,0x84cb,0x6e89,
+ 0x5e79,0x7518,0x6746,0x67d1,0x7aff,0x809d,0x8d95,0x611f,0x79c6,0x6562,
+ 0x8d1b,0x5ca1,0x525b,0x92fc,0x7f38,0x809b,0x7db1,0x5d17,0x6e2f,0x6760,
+ 0x7bd9,0x768b,0x9ad8,0x818f,0x7f94,0x7cd5,0x641e,0x93ac,0x7a3f,0x544a,
+ 0x54e5,0x6b4c,0x64f1,0x6208,0x9d3f,0x80f3,0x7599,0x5272,0x9769,0x845b,
+ 0x683c,0x86e4,0x95a3,0x9694,0x927b,0x500b,0x5404,0x7d66,0x6839,0x8ddf,
+ 0x8015,0x66f4,0x5e9a,0x7fb9
+ },
+ { /* ku 19 */
+ 0x57c2,0x803f,0x6897,0x5de5,0x653b,0x529f,0x606d,0x9f94,0x4f9b,0x8eac,
+ 0x516c,0x5bab,0x5f13,0x978f,0x6c5e,0x62f1,0x8ca2,0x5171,0x920e,0x52fe,
+ 0x6e9d,0x82df,0x72d7,0x57a2,0x69cb,0x8cfc,0x591f,0x8f9c,0x83c7,0x5495,
+ 0x7b8d,0x4f30,0x6cbd,0x5b64,0x59d1,0x9f13,0x53e4,0x8831,0x9aa8,0x8c37,
+ 0x80a1,0x6545,0x9867,0x56fa,0x96c7,0x522e,0x74dc,0x526e,0x5be1,0x6302,
+ 0x8902,0x4e56,0x62d0,0x602a,0x68fa,0x95dc,0x5b98,0x51a0,0x89c0,0x7ba1,
+ 0x9928,0x7f50,0x6163,0x704c,0x8cab,0x5149,0x5ee3,0x901b,0x7470,0x898f,
+ 0x572d,0x7845,0x6b78,0x9f9c,0x95a8,0x8ecc,0x9b3c,0x8a6d,0x7678,0x6842,
+ 0x6ac3,0x8dea,0x8cb4,0x528a,0x8f25,0x6eda,0x68cd,0x934b,0x90ed,0x570b,
+ 0x679c,0x88f9,0x904e,0x54c8
+ },
+ { /* ku 1a */
+ 0x9ab8,0x5b69,0x6d77,0x6c26,0x4ea5,0x5bb3,0x99ed,0x9163,0x61a8,0x90af,
+ 0x97d3,0x542b,0x6db5,0x5bd2,0x51fd,0x558a,0x7f55,0x7ff0,0x64bc,0x634d,
+ 0x65f1,0x61be,0x608d,0x710a,0x6c57,0x6f22,0x592f,0x676d,0x822a,0x58d5,
+ 0x568e,0x8c6a,0x6beb,0x90dd,0x597d,0x8017,0x865f,0x6d69,0x5475,0x559d,
+ 0x8377,0x83cf,0x6838,0x79be,0x548c,0x4f55,0x5408,0x76d2,0x8c89,0x95a1,
+ 0x6cb3,0x6db8,0x8d6b,0x8910,0x9db4,0x8cc0,0x563f,0x9ed1,0x75d5,0x5f88,
+ 0x72e0,0x6068,0x54fc,0x4ea8,0x6a2a,0x8861,0x6052,0x8f5f,0x54c4,0x70d8,
+ 0x8679,0x9d3b,0x6d2a,0x5b8f,0x5f18,0x7d05,0x5589,0x4faf,0x7334,0x543c,
+ 0x539a,0x5019,0x5f8c,0x547c,0x4e4e,0x5ffd,0x745a,0x58fa,0x846b,0x80e1,
+ 0x8774,0x72d0,0x7cca,0x6e56
+ },
+ { /* ku 1b */
+ 0x5f27,0x864e,0x552c,0x8b77,0x4e92,0x6eec,0x6237,0x82b1,0x5629,0x83ef,
+ 0x733e,0x6ed1,0x756b,0x5283,0x5316,0x8a71,0x69d0,0x5f8a,0x61f7,0x6dee,
+ 0x58de,0x6b61,0x74b0,0x6853,0x9084,0x7de9,0x63db,0x60a3,0x559a,0x7613,
+ 0x8c62,0x7165,0x6e19,0x5ba6,0x5e7b,0x8352,0x614c,0x9ec4,0x78fa,0x8757,
+ 0x7c27,0x7687,0x51f0,0x60f6,0x714c,0x6643,0x5e4c,0x604d,0x8b0a,0x7070,
+ 0x63ee,0x8f1d,0x5fbd,0x6062,0x86d4,0x56de,0x6bc1,0x6094,0x6167,0x5349,
+ 0x60e0,0x6666,0x8cc4,0x7a62,0x6703,0x71f4,0x532f,0x8af1,0x8aa8,0x7e6a,
+ 0x8477,0x660f,0x5a5a,0x9b42,0x6e3e,0x6df7,0x8c41,0x6d3b,0x4f19,0x706b,
+ 0x7372,0x6216,0x60d1,0x970d,0x8ca8,0x798d,0x64ca,0x573e,0x57fa,0x6a5f,
+ 0x7578,0x7a3d,0x7a4d,0x7b95
+ },
+ { /* ku 1c */
+ 0x808c,0x9951,0x8ff9,0x6fc0,0x8b4f,0x9dc4,0x59ec,0x7e3e,0x7ddd,0x5409,
+ 0x6975,0x68d8,0x8f2f,0x7c4d,0x96c6,0x53ca,0x6025,0x75be,0x6c72,0x5373,
+ 0x5ac9,0x7d1a,0x64e0,0x5e7e,0x810a,0x5df1,0x858a,0x6280,0x5180,0x5b63,
+ 0x4f0e,0x796d,0x5291,0x60b8,0x6fdf,0x5bc4,0x5bc2,0x8a08,0x8a18,0x65e2,
+ 0x5fcc,0x969b,0x5993,0x7e7c,0x7d00,0x5609,0x67b7,0x593e,0x4f73,0x5bb6,
+ 0x52a0,0x83a2,0x9830,0x8cc8,0x7532,0x9240,0x5047,0x7a3c,0x50f9,0x67b6,
+ 0x99d5,0x5ac1,0x6bb2,0x76e3,0x5805,0x5c16,0x7b8b,0x9593,0x714e,0x517c,
+ 0x80a9,0x8271,0x5978,0x7dd8,0x7e6d,0x6aa2,0x67ec,0x78b1,0x9e7c,0x63c0,
+ 0x64bf,0x7c21,0x5109,0x526a,0x51cf,0x85a6,0x6abb,0x9452,0x8e10,0x8ce4,
+ 0x898b,0x9375,0x7bad,0x4ef6
+ },
+ { /* ku 1d */
+ 0x5065,0x8266,0x528d,0x991e,0x6f38,0x6ffa,0x6f97,0x5efa,0x50f5,0x59dc,
+ 0x5c07,0x6f3f,0x6c5f,0x7586,0x8523,0x69f3,0x596c,0x8b1b,0x5320,0x91ac,
+ 0x964d,0x8549,0x6912,0x7901,0x7126,0x81a0,0x4ea4,0x90ca,0x6f86,0x9a55,
+ 0x5b0c,0x56bc,0x652a,0x9278,0x77ef,0x50e5,0x811a,0x72e1,0x89d2,0x9903,
+ 0x7e73,0x7d5e,0x527f,0x6559,0x9175,0x8f4e,0x8f03,0x53eb,0x7a96,0x63ed,
+ 0x63a5,0x7686,0x79f8,0x8857,0x968e,0x622a,0x52ab,0x7bc0,0x6854,0x6770,
+ 0x6377,0x776b,0x7aed,0x6f54,0x7d50,0x89e3,0x59d0,0x6212,0x85c9,0x82a5,
+ 0x754c,0x501f,0x4ecb,0x75a5,0x8aa1,0x5c4a,0x5dfe,0x7b4b,0x65a4,0x91d1,
+ 0x4eca,0x6d25,0x895f,0x7dca,0x9326,0x50c5,0x8b39,0x9032,0x9773,0x6649,
+ 0x7981,0x8fd1,0x71fc,0x6d78
+ },
+ { /* ku 1e */
+ 0x76e1,0x52c1,0x8346,0x5162,0x8396,0x775b,0x6676,0x9be8,0x4eac,0x9a5a,
+ 0x7cbe,0x7cb3,0x7d93,0x4e95,0x8b66,0x666f,0x9838,0x975c,0x5883,0x656c,
+ 0x93e1,0x5f91,0x75d9,0x9756,0x7adf,0x7af6,0x51c8,0x70af,0x7a98,0x63ea,
+ 0x7a76,0x7cfe,0x7396,0x97ed,0x4e45,0x7078,0x4e5d,0x9152,0x53a9,0x6551,
+ 0x820a,0x81fc,0x8205,0x548e,0x5c31,0x759a,0x97a0,0x62d8,0x72d9,0x75bd,
+ 0x5c45,0x99d2,0x83ca,0x5c40,0x5480,0x77e9,0x8209,0x6cae,0x805a,0x62d2,
+ 0x64da,0x5de8,0x5177,0x8ddd,0x8e1e,0x92f8,0x4ff1,0x53e5,0x61fc,0x70ac,
+ 0x5287,0x6350,0x9d51,0x5a1f,0x5026,0x7737,0x5377,0x7d79,0x6485,0x652b,
+ 0x6289,0x6398,0x5014,0x7235,0x89ba,0x51b3,0x8a23,0x7d76,0x5747,0x83cc,
+ 0x921e,0x8ecd,0x541b,0x5cfb
+ },
+ { /* ku 1f */
+ 0x4fca,0x7ae3,0x6d5a,0x90e1,0x99ff,0x5580,0x5496,0x5361,0x54af,0x958b,
+ 0x63e9,0x6977,0x51f1,0x6168,0x520a,0x582a,0x52d8,0x574e,0x780d,0x770b,
+ 0x5eb7,0x6177,0x7ce0,0x625b,0x6297,0x4ea2,0x7095,0x8003,0x62f7,0x70e4,
+ 0x9760,0x5777,0x82db,0x67ef,0x68f5,0x78d5,0x9846,0x79d1,0x6bbb,0x54b3,
+ 0x53ef,0x6e34,0x514b,0x523b,0x5ba2,0x8ab2,0x80af,0x5543,0x58be,0x61c7,
+ 0x5751,0x542d,0x7a7a,0x6050,0x5b54,0x63a7,0x6473,0x53e3,0x6263,0x5bc7,
+ 0x67af,0x54ed,0x7a9f,0x82e6,0x9177,0x5eab,0x8932,0x8a87,0x57ae,0x630e,
+ 0x8de8,0x80ef,0x584a,0x7b77,0x5108,0x5feb,0x5bec,0x6b3e,0x5321,0x7b50,
+ 0x72c2,0x6846,0x7926,0x7736,0x66e0,0x51b5,0x8667,0x76d4,0x5dcb,0x7aba,
+ 0x8475,0x594e,0x9b41,0x5080
+ },
+ { /* ku 20 */
+ 0x994b,0x6127,0x6f70,0x5764,0x6606,0x6346,0x56f0,0x62ec,0x64f4,0x5ed3,
+ 0x95ca,0x5783,0x62c9,0x5587,0x881f,0x81d8,0x8fa3,0x5566,0x840a,0x4f86,
+ 0x8cf4,0x85cd,0x5a6a,0x6b04,0x6514,0x7c43,0x95cc,0x862d,0x703e,0x8b95,
+ 0x652c,0x89bd,0x61f6,0x7e9c,0x721b,0x6feb,0x7405,0x6994,0x72fc,0x5eca,
+ 0x90ce,0x6717,0x6d6a,0x6488,0x52de,0x7262,0x8001,0x4f6c,0x59e5,0x916a,
+ 0x70d9,0x6f87,0x52d2,0x6a02,0x96f7,0x9433,0x857e,0x78ca,0x7d2f,0x5121,
+ 0x58d8,0x64c2,0x808b,0x985e,0x6cea,0x68f1,0x695e,0x51b7,0x5398,0x68a8,
+ 0x7281,0x9ece,0x7c6c,0x72f8,0x96e2,0x7055,0x7406,0x674e,0x88cf,0x9bc9,
+ 0x79ae,0x8389,0x8354,0x540f,0x6817,0x9e97,0x53b2,0x52f5,0x792b,0x6b77,
+ 0x5229,0x5088,0x4f8b,0x4fd0
+ },
+ { /* ku 21 */
+ 0x75e2,0x7acb,0x7c92,0x701d,0x96b8,0x529b,0x7483,0x54e9,0x5006,0x806f,
+ 0x84ee,0x9023,0x942e,0x5ec9,0x6190,0x6f23,0x7c3e,0x6582,0x81c9,0x93c8,
+ 0x6200,0x7149,0x7df4,0x7ce7,0x51c9,0x6881,0x7cb1,0x826f,0x5169,0x8f1b,
+ 0x91cf,0x667e,0x4eae,0x8ad2,0x64a9,0x804a,0x50da,0x7642,0x71ce,0x5be5,
+ 0x907c,0x6f66,0x4e86,0x6482,0x9410,0x5ed6,0x6599,0x5217,0x88c2,0x70c8,
+ 0x52a3,0x7375,0x7433,0x6797,0x78f7,0x9716,0x81e8,0x9130,0x9c57,0x6dcb,
+ 0x51db,0x8cc3,0x541d,0x62ce,0x73b2,0x83f1,0x96f6,0x9f61,0x9234,0x4f36,
+ 0x7f9a,0x51cc,0x9748,0x9675,0x5dba,0x9818,0x53e6,0x4ee4,0x6e9c,0x7409,
+ 0x69b4,0x786b,0x993e,0x7559,0x5289,0x7624,0x6d41,0x67f3,0x516d,0x9f8d,
+ 0x807e,0x56a8,0x7c60,0x7abf
+ },
+ { /* ku 22 */
+ 0x9686,0x58df,0x650f,0x96b4,0x6a13,0x5a41,0x645f,0x7c0d,0x6f0f,0x964b,
+ 0x8606,0x76e7,0x9871,0x5eec,0x7210,0x64c4,0x6ef7,0x865c,0x9b6f,0x9e93,
+ 0x788c,0x9732,0x8def,0x8cc2,0x9e7f,0x6f5e,0x7984,0x9332,0x9678,0x622e,
+ 0x9a62,0x5415,0x92c1,0x4fa3,0x65c5,0x5c65,0x5c62,0x7e37,0x616e,0x6c2f,
+ 0x5f8b,0x7387,0x6ffe,0x7dd1,0x5dd2,0x6523,0x5b7f,0x7064,0x5375,0x4e82,
+ 0x63a0,0x7565,0x6384,0x8f2a,0x502b,0x4f96,0x6dea,0x7db8,0x8ad6,0x863f,
+ 0x87ba,0x7f85,0x908f,0x947c,0x7c6e,0x9a3e,0x88f8,0x843d,0x6d1b,0x99f1,
+ 0x7d61,0x5abd,0x9ebb,0x746a,0x78bc,0x879e,0x99ac,0x99e1,0x561b,0x55ce,
+ 0x57cb,0x8cb7,0x9ea5,0x8ce3,0x9081,0x8109,0x779e,0x9945,0x883b,0x6eff,
+ 0x8513,0x66fc,0x6162,0x6f2b
+ },
+ { /* ku 23 */
+ 0x8b3e,0x8292,0x832b,0x76f2,0x6c13,0x5fd9,0x83bd,0x732b,0x8305,0x9328,
+ 0x6bdb,0x77db,0x925a,0x536f,0x8302,0x5192,0x5e3d,0x8c8c,0x8cbf,0x9ebd,
+ 0x73ab,0x679a,0x6885,0x9176,0x9709,0x7164,0x6ca1,0x7709,0x5a92,0x9382,
+ 0x6bcf,0x7f8e,0x6627,0x5bd0,0x59b9,0x5a9a,0x9580,0x60b6,0x5011,0x840c,
+ 0x8499,0x6aac,0x76df,0x9333,0x731b,0x5922,0x5b5f,0x772f,0x919a,0x9761,
+ 0x7cdc,0x8ff7,0x8b0e,0x5f4c,0x7c73,0x79d8,0x8993,0x6ccc,0x871c,0x5bc6,
+ 0x5e42,0x68c9,0x7720,0x7dbf,0x5195,0x514d,0x52c9,0x5a29,0x7dec,0x9762,
+ 0x82d7,0x63cf,0x7784,0x85d0,0x79d2,0x6e3a,0x5edf,0x5999,0x8511,0x6ec5,
+ 0x6c11,0x62bf,0x76bf,0x654f,0x61ab,0x95a9,0x660e,0x879f,0x9cf4,0x9298,
+ 0x540d,0x547d,0x8b2c,0x6478
+ },
+ { /* ku 24 */
+ 0x6479,0x8611,0x6a21,0x819c,0x78e8,0x6469,0x9b54,0x62b9,0x672b,0x83ab,
+ 0x58a8,0x9ed8,0x6cab,0x6f20,0x5bde,0x964c,0x8b00,0x725f,0x67d0,0x62c7,
+ 0x7261,0x755d,0x59c6,0x6bcd,0x5893,0x66ae,0x5e55,0x52df,0x6155,0x6728,
+ 0x76ee,0x7766,0x7267,0x7a46,0x62ff,0x54ea,0x5450,0x9209,0x90a3,0x5a1c,
+ 0x7d0d,0x6c16,0x4e43,0x5976,0x8010,0x5948,0x5357,0x7537,0x96e3,0x56ca,
+ 0x6493,0x8166,0x60f1,0x9b27,0x6dd6,0x5462,0x9912,0x5185,0x5ae9,0x80fd,
+ 0x59ae,0x9713,0x502a,0x6ce5,0x5c3c,0x64ec,0x4f60,0x533f,0x81a9,0x9006,
+ 0x6eba,0x852b,0x62c8,0x5e74,0x78be,0x6506,0x637b,0x5ff5,0x5a18,0x91c0,
+ 0x9ce5,0x5c3f,0x634f,0x8076,0x5b7d,0x5699,0x9477,0x93b3,0x6d85,0x60a8,
+ 0x6ab8,0x7370,0x51dd,0x5be7
+ },
+ { /* ku 25 */
+ 0x64f0,0x6fd8,0x725b,0x626d,0x9215,0x7d10,0x81bf,0x6fc3,0x8fb2,0x5f04,
+ 0x5974,0x52aa,0x6012,0x5973,0x6696,0x8650,0x7627,0x632a,0x61e6,0x7cef,
+ 0x8afe,0x54e6,0x6b50,0x9dd7,0x6bc6,0x85d5,0x5614,0x5076,0x6f1a,0x556a,
+ 0x8db4,0x722c,0x5e15,0x6015,0x7436,0x62cd,0x6392,0x724c,0x5f98,0x6e43,
+ 0x6d3e,0x6500,0x6f58,0x76e4,0x78d0,0x76fc,0x7554,0x5224,0x53db,0x4e53,
+ 0x9f90,0x65c1,0x802a,0x80d6,0x629b,0x5486,0x5228,0x70ae,0x888d,0x8dd1,
+ 0x6ce1,0x5478,0x80da,0x57f9,0x88f4,0x8ce0,0x966a,0x914d,0x4f69,0x6c9b,
+ 0x5674,0x76c6,0x7830,0x62a8,0x70f9,0x6f8e,0x5f6d,0x84ec,0x68da,0x787c,
+ 0x7bf7,0x81a8,0x670b,0x9d6c,0x6367,0x78b0,0x576f,0x7812,0x9739,0x6279,
+ 0x62ab,0x5288,0x7435,0x6bd7
+ },
+ { /* ku 26 */
+ 0x5564,0x813e,0x75b2,0x76ae,0x5339,0x75de,0x50fb,0x5c41,0x8b6c,0x7bc7,
+ 0x504f,0x7247,0x9a19,0x98c4,0x6f02,0x74e2,0x7968,0x6487,0x77a5,0x62fc,
+ 0x983b,0x8ca7,0x54c1,0x8058,0x4e52,0x576a,0x860b,0x840d,0x5e73,0x6191,
+ 0x74f6,0x8a55,0x5c4f,0x5761,0x6f51,0x9817,0x5a46,0x7834,0x9b44,0x8feb,
+ 0x7c95,0x5256,0x64b2,0x92ea,0x50d5,0x8386,0x8461,0x83e9,0x84b2,0x57d4,
+ 0x6a38,0x5703,0x666e,0x6d66,0x8b5c,0x66dd,0x7011,0x671f,0x6b3a,0x68f2,
+ 0x621a,0x59bb,0x4e03,0x51c4,0x6f06,0x67d2,0x6c8f,0x5176,0x68cb,0x5947,
+ 0x6b67,0x7566,0x5d0e,0x81cd,0x9f4a,0x65d7,0x7948,0x7941,0x9a0e,0x8d77,
+ 0x8c48,0x4e5e,0x4f01,0x5553,0x5951,0x780c,0x5668,0x6c23,0x8fc4,0x68c4,
+ 0x6c7d,0x6ce3,0x8a16,0x6390
+ },
+ { /* ku 27 */
+ 0x6070,0x6d3d,0x727d,0x6266,0x91fa,0x925b,0x5343,0x9077,0x7c3d,0x4edf,
+ 0x8b19,0x4e7e,0x9ed4,0x9322,0x9257,0x524d,0x6f5b,0x9063,0x6dfa,0x8b74,
+ 0x5879,0x5d4c,0x6b20,0x6b49,0x69cd,0x55c6,0x8154,0x7f8c,0x58bb,0x8594,
+ 0x5f3a,0x6436,0x6a47,0x936c,0x6572,0x6084,0x6a4b,0x77a7,0x55ac,0x50d1,
+ 0x5de7,0x9798,0x64ac,0x7ff9,0x5ced,0x4fcf,0x7ac5,0x5207,0x8304,0x4e14,
+ 0x602f,0x7aca,0x6b3d,0x4fb5,0x89aa,0x79e6,0x7434,0x52e4,0x82b9,0x64d2,
+ 0x79bd,0x5be2,0x6c81,0x9752,0x8f15,0x6c2b,0x50be,0x537f,0x6e05,0x64ce,
+ 0x6674,0x6c30,0x60c5,0x9803,0x8acb,0x6176,0x74ca,0x7aae,0x79cb,0x4e18,
+ 0x90b1,0x7403,0x6c42,0x56da,0x914b,0x6cc5,0x8da8,0x5340,0x86c6,0x66f2,
+ 0x8ec0,0x5c48,0x9a45,0x6e20
+ },
+ { /* ku 28 */
+ 0x53d6,0x5a36,0x9f72,0x8da3,0x53bb,0x5708,0x9874,0x6b0a,0x919b,0x6cc9,
+ 0x5168,0x75ca,0x62f3,0x72ac,0x5238,0x52f8,0x7f3a,0x7094,0x7638,0x5374,
+ 0x9d72,0x69b7,0x78ba,0x96c0,0x88d9,0x7fa4,0x7136,0x71c3,0x5189,0x67d3,
+ 0x74e4,0x58e4,0x6518,0x56b7,0x8b93,0x9952,0x64fe,0x7e5e,0x60f9,0x71b1,
+ 0x58ec,0x4ec1,0x4eba,0x5fcd,0x97cc,0x4efb,0x8a8d,0x5203,0x598a,0x7d09,
+ 0x6254,0x4ecd,0x65e5,0x620e,0x8338,0x84c9,0x69ae,0x878d,0x7194,0x6eb6,
+ 0x5bb9,0x7d68,0x5197,0x63c9,0x67d4,0x8089,0x8339,0x8815,0x5112,0x5b7a,
+ 0x5982,0x8fb1,0x4e73,0x6c5d,0x5165,0x8925,0x8edf,0x962e,0x854a,0x745e,
+ 0x92ed,0x958f,0x6f64,0x82e5,0x5f31,0x6492,0x7051,0x85a9,0x816e,0x9c13,
+ 0x585e,0x8cfd,0x4e09,0x53c1
+ },
+ { /* ku 29 */
+ 0x5098,0x6563,0x6851,0x55d3,0x55aa,0x6414,0x9a37,0x6383,0x5ac2,0x745f,
+ 0x8272,0x6f80,0x68ee,0x50e7,0x838e,0x7802,0x6bba,0x5239,0x6c99,0x7d17,
+ 0x50bb,0x5565,0x715e,0x7be9,0x66ec,0x73ca,0x82eb,0x6749,0x5c71,0x5220,
+ 0x717d,0x886b,0x9583,0x965d,0x64c5,0x8d0d,0x81b3,0x5584,0x6c55,0x6247,
+ 0x7e55,0x5892,0x50b7,0x5546,0x8cde,0x664c,0x4e0a,0x5c1a,0x88f3,0x68a2,
+ 0x634e,0x7a0d,0x71d2,0x828d,0x52fa,0x97f6,0x5c11,0x54e8,0x90b5,0x7d39,
+ 0x5962,0x8cd2,0x86c7,0x820c,0x6368,0x8d66,0x651d,0x5c04,0x61fe,0x6d89,
+ 0x793e,0x8a2d,0x7837,0x7533,0x547b,0x4f38,0x8eab,0x6df1,0x5a20,0x7d33,
+ 0x795e,0x6c88,0x5be9,0x5b38,0x751a,0x814e,0x614e,0x6ef2,0x8072,0x751f,
+ 0x7525,0x7272,0x5347,0x7e69
+ },
+ { /* ku 2a */
+ 0x7701,0x76db,0x5269,0x52dd,0x8056,0x5e2b,0x5931,0x7345,0x65bd,0x6fd5,
+ 0x8a69,0x5c38,0x8671,0x5341,0x77f3,0x62fe,0x6642,0x4ec0,0x98df,0x8755,
+ 0x5be6,0x8b58,0x53f2,0x77e2,0x4f7f,0x5c4e,0x99db,0x59cb,0x5f0f,0x793a,
+ 0x58eb,0x4e16,0x67ff,0x4e8b,0x62ed,0x8a93,0x901d,0x52e2,0x662f,0x55dc,
+ 0x566c,0x9069,0x4ed5,0x4f8d,0x91cb,0x98fe,0x6c0f,0x5e02,0x6043,0x5ba4,
+ 0x8996,0x8a66,0x6536,0x624b,0x9996,0x5b88,0x58fd,0x6388,0x552e,0x53d7,
+ 0x7626,0x7378,0x852c,0x6a1e,0x68b3,0x6b8a,0x6292,0x8f38,0x53d4,0x8212,
+ 0x6dd1,0x758f,0x66f8,0x8d16,0x5b70,0x719f,0x85af,0x6691,0x66d9,0x7f72,
+ 0x8700,0x9ecd,0x9f20,0x5c6c,0x8853,0x8ff0,0x6a39,0x675f,0x620d,0x7aea,
+ 0x5885,0x5eb6,0x6578,0x6f31
+ },
+ { /* ku 2b */
+ 0x6055,0x5237,0x800d,0x6454,0x8870,0x7529,0x5e25,0x6813,0x62f4,0x971c,
+ 0x96d9,0x723d,0x8ab0,0x6c34,0x7761,0x7a0e,0x542e,0x77ac,0x9806,0x821c,
+ 0x8aac,0x78a9,0x6714,0x720d,0x65af,0x6495,0x5636,0x601d,0x79c1,0x53f8,
+ 0x7d72,0x6b7b,0x8086,0x5bfa,0x55e3,0x56db,0x4f3a,0x4f3c,0x98fc,0x5df3,
+ 0x9b06,0x8073,0x616b,0x980c,0x9001,0x5b8b,0x8a1f,0x8aa6,0x641c,0x8258,
+ 0x64fb,0x55fd,0x8607,0x9165,0x4fd7,0x7d20,0x901f,0x7c9f,0x50f3,0x5851,
+ 0x6eaf,0x5bbf,0x8a34,0x8085,0x9178,0x849c,0x7b97,0x96d6,0x968b,0x96a8,
+ 0x7d8f,0x9ad3,0x788e,0x6b72,0x7a57,0x9042,0x96a7,0x795f,0x5b6b,0x640d,
+ 0x7b0b,0x84d1,0x68ad,0x5506,0x7e2e,0x7463,0x7d22,0x9396,0x6240,0x584c,
+ 0x4ed6,0x5b83,0x5979,0x5854
+ },
+ { /* ku 2c */
+ 0x737a,0x64bb,0x8e4b,0x8e0f,0x80ce,0x82d4,0x62ac,0x81fa,0x6cf0,0x915e,
+ 0x592a,0x614b,0x6c70,0x574d,0x6524,0x8caa,0x7671,0x7058,0x58c7,0x6a80,
+ 0x75f0,0x6f6d,0x8b5a,0x8ac7,0x5766,0x6bef,0x8892,0x78b3,0x63a2,0x5606,
+ 0x70ad,0x6e6f,0x5858,0x642a,0x5802,0x68e0,0x819b,0x5510,0x7cd6,0x5018,
+ 0x8eba,0x6dcc,0x8d9f,0x71d9,0x638f,0x6fe4,0x6ed4,0x7e27,0x8404,0x6843,
+ 0x9003,0x6dd8,0x9676,0x8a0e,0x5957,0x7279,0x85e4,0x9a30,0x75bc,0x8b04,
+ 0x68af,0x5254,0x8e22,0x92bb,0x63d0,0x984c,0x8e44,0x557c,0x9ad4,0x66ff,
+ 0x568f,0x60d5,0x6d95,0x5243,0x5c49,0x5929,0x6dfb,0x586b,0x7530,0x751c,
+ 0x606c,0x8214,0x8146,0x6311,0x689d,0x8fe2,0x773a,0x8df3,0x8cbc,0x9435,
+ 0x5e16,0x5ef3,0x807d,0x70f4
+ },
+ { /* ku 2d */
+ 0x6c40,0x5ef7,0x505c,0x4ead,0x5ead,0x633a,0x8247,0x901a,0x6850,0x916e,
+ 0x77b3,0x540c,0x9285,0x5f64,0x7ae5,0x6876,0x6345,0x7b52,0x7d71,0x75db,
+ 0x5077,0x6295,0x982d,0x900f,0x51f8,0x79c3,0x7a81,0x5716,0x5f92,0x9014,
+ 0x5857,0x5c60,0x571f,0x5410,0x5154,0x6e4d,0x5718,0x63a8,0x983d,0x817f,
+ 0x8715,0x892a,0x9000,0x541e,0x5c6f,0x81c0,0x62d6,0x6258,0x8131,0x9d15,
+ 0x9640,0x99b1,0x99dd,0x6a62,0x59a5,0x62d3,0x553e,0x6316,0x54c7,0x86d9,
+ 0x7aaa,0x5a03,0x74e6,0x896a,0x6b6a,0x5916,0x8c4c,0x5f4e,0x7063,0x73a9,
+ 0x9811,0x4e38,0x70f7,0x5b8c,0x7897,0x633d,0x665a,0x7696,0x60cb,0x5b9b,
+ 0x5a49,0x842c,0x8155,0x6c6a,0x738b,0x4ea1,0x6789,0x7db2,0x5f80,0x65fa,
+ 0x671b,0x5fd8,0x5984,0x5a01
+ },
+ { /* ku 2e */
+ 0x5dcd,0x5fae,0x5371,0x97cb,0x9055,0x6845,0x570d,0x552f,0x60df,0x7232,
+ 0x6ff0,0x7dad,0x8466,0x840e,0x59d4,0x5049,0x50de,0x5c3e,0x7def,0x672a,
+ 0x851a,0x5473,0x754f,0x80c3,0x5582,0x9b4f,0x4f4d,0x6e2d,0x8b02,0x5c09,
+ 0x6170,0x885b,0x761f,0x6e29,0x868a,0x6587,0x805e,0x7d0b,0x543b,0x7a69,
+ 0x7d0a,0x554f,0x55e1,0x7fc1,0x74ee,0x64be,0x8778,0x6e26,0x7aa9,0x6211,
+ 0x65a1,0x5367,0x63e1,0x6c83,0x5deb,0x55da,0x93a2,0x70cf,0x6c61,0x8aa3,
+ 0x5c4b,0x7121,0x856a,0x68a7,0x543e,0x5434,0x6bcb,0x6b66,0x4e94,0x6342,
+ 0x5348,0x821e,0x4f0d,0x4fae,0x5862,0x620a,0x9727,0x6664,0x7269,0x52ff,
+ 0x52d9,0x609f,0x8aa4,0x6614,0x7199,0x6790,0x897f,0x7852,0x77fd,0x6670,
+ 0x563b,0x5438,0x932b,0x72a7
+ },
+ { /* ku 2f */
+ 0x7a00,0x606f,0x5e0c,0x6089,0x819d,0x5915,0x60dc,0x7184,0x70ef,0x6eaa,
+ 0x6c50,0x7280,0x6a84,0x8972,0x5e2d,0x7fd2,0x5ab3,0x559c,0x9291,0x6d17,
+ 0x7cfb,0x9699,0x6232,0x7d30,0x778e,0x8766,0x5323,0x971e,0x8f44,0x6687,
+ 0x5cfd,0x4fe0,0x72f9,0x4e0b,0x53a6,0x590f,0x5687,0x6380,0x9341,0x5148,
+ 0x4ed9,0x9bae,0x7e96,0x54b8,0x8ce2,0x929c,0x8237,0x9591,0x6d8e,0x5f26,
+ 0x5acc,0x986f,0x96aa,0x73fe,0x737b,0x7e23,0x817a,0x9921,0x7fa1,0x61b2,
+ 0x9677,0x9650,0x7dab,0x76f8,0x53a2,0x9472,0x9999,0x7bb1,0x8944,0x6e58,
+ 0x9109,0x7fd4,0x7965,0x8a73,0x60f3,0x97ff,0x4eab,0x9805,0x5df7,0x6a61,
+ 0x50cf,0x5411,0x8c61,0x856d,0x785d,0x9704,0x524a,0x54ee,0x56c2,0x92b7,
+ 0x6d88,0x5bb5,0x6dc6,0x66c9
+ },
+ { /* ku 30 */
+ 0x5c0f,0x5b5d,0x6821,0x8096,0x562f,0x7b11,0x6548,0x6954,0x4e9b,0x6b47,
+ 0x874e,0x978b,0x5354,0x633e,0x643a,0x90aa,0x659c,0x8105,0x8ae7,0x5beb,
+ 0x68b0,0x5378,0x87f9,0x61c8,0x6cc4,0x7009,0x8b1d,0x5c51,0x85aa,0x82af,
+ 0x92c5,0x6b23,0x8f9b,0x65b0,0x5ffb,0x5fc3,0x4fe1,0x91c1,0x661f,0x8165,
+ 0x7329,0x60fa,0x8208,0x5211,0x578b,0x5f62,0x90a2,0x884c,0x9192,0x5e78,
+ 0x674f,0x6027,0x59d3,0x5144,0x51f6,0x80f8,0x5308,0x6c79,0x96c4,0x718a,
+ 0x4f11,0x4fee,0x7f9e,0x673d,0x55c5,0x92b9,0x79c0,0x8896,0x7d89,0x589f,
+ 0x620c,0x9700,0x865a,0x5618,0x9808,0x5f90,0x8a31,0x84c4,0x9157,0x53d9,
+ 0x65ed,0x5e8f,0x755c,0x6064,0x7d6e,0x5a7f,0x7dd2,0x7e8c,0x8ed2,0x55a7,
+ 0x5ba3,0x61f8,0x65cb,0x7384
+ },
+ { /* ku 31 */
+ 0x9078,0x766c,0x7729,0x7d62,0x9774,0x859b,0x5b78,0x7a74,0x96ea,0x8840,
+ 0x52db,0x718f,0x5faa,0x65ec,0x8a62,0x5c0b,0x99b4,0x5de1,0x6b89,0x6c5b,
+ 0x8a13,0x8a0a,0x905c,0x8fc5,0x58d3,0x62bc,0x9d09,0x9d28,0x5440,0x4e2b,
+ 0x82bd,0x7259,0x869c,0x5d16,0x8859,0x6daf,0x96c5,0x555e,0x4e9e,0x8a1d,
+ 0x7109,0x54bd,0x95b9,0x70df,0x6df9,0x9e7d,0x56b4,0x7814,0x8712,0x5ca9,
+ 0x5ef6,0x8a00,0x9854,0x95bb,0x708e,0x6cbf,0x5944,0x63a9,0x773c,0x884d,
+ 0x6f14,0x8277,0x5830,0x71d5,0x53ad,0x786f,0x96c1,0x5501,0x5f66,0x7130,
+ 0x5bb4,0x8afa,0x9a57,0x6b83,0x592e,0x9d26,0x79e7,0x694a,0x63da,0x4f6f,
+ 0x760d,0x7f8a,0x6d0b,0x967d,0x6c27,0x4ef0,0x7662,0x990a,0x6a23,0x6f3e,
+ 0x9080,0x8170,0x5996,0x7476
+ },
+ { /* ku 32 */
+ 0x6447,0x582f,0x9065,0x7a91,0x8b21,0x59da,0x54ac,0x8200,0x85e5,0x8981,
+ 0x8000,0x6930,0x564e,0x8036,0x723a,0x91ce,0x51b6,0x4e5f,0x9801,0x6396,
+ 0x696d,0x8449,0x66f3,0x814b,0x591c,0x6db2,0x4e00,0x58f9,0x91ab,0x63d6,
+ 0x92a5,0x4f9d,0x4f0a,0x8863,0x9824,0x5937,0x907a,0x79fb,0x5100,0x80f0,
+ 0x7591,0x6c82,0x5b9c,0x59e8,0x5f5d,0x6905,0x87fb,0x501a,0x5df2,0x4e59,
+ 0x77e3,0x4ee5,0x85dd,0x6291,0x6613,0x9091,0x5c79,0x5104,0x5f79,0x81c6,
+ 0x9038,0x8084,0x75ab,0x4ea6,0x88d4,0x610f,0x6bc5,0x61b6,0x7fa9,0x76ca,
+ 0x6ea2,0x8a63,0x8b70,0x8abc,0x8b6f,0x5f02,0x7ffc,0x7fcc,0x7e79,0x8335,
+ 0x852d,0x56e0,0x6bb7,0x97f3,0x9670,0x59fb,0x541f,0x9280,0x6deb,0x5bc5,
+ 0x98f2,0x5c39,0x5f15,0x96b1
+ },
+ { /* ku 33 */
+ 0x5370,0x82f1,0x6afb,0x5b30,0x9df9,0x61c9,0x7e93,0x7469,0x87a2,0x71df,
+ 0x7192,0x8805,0x8fce,0x8d0f,0x76c8,0x5f71,0x7a4e,0x786c,0x6620,0x55b2,
+ 0x64c1,0x50ad,0x81c3,0x7670,0x5eb8,0x96cd,0x8e34,0x86f9,0x548f,0x6cf3,
+ 0x6d8c,0x6c38,0x607f,0x52c7,0x7528,0x5e7d,0x512a,0x60a0,0x6182,0x5c24,
+ 0x7531,0x90f5,0x923e,0x7336,0x6cb9,0x6e38,0x9149,0x6709,0x53cb,0x53f3,
+ 0x4f51,0x91c9,0x8a98,0x53c8,0x5e7c,0x8fc2,0x6de4,0x4e8e,0x76c2,0x6986,
+ 0x865e,0x611a,0x8f3f,0x9918,0x4fde,0x903e,0x9b5a,0x6109,0x6e1d,0x6f01,
+ 0x9685,0x4e88,0x5a31,0x96e8,0x8207,0x5dbc,0x79b9,0x5b87,0x8a9e,0x7fbd,
+ 0x7389,0x57df,0x828b,0x9b31,0x5401,0x9047,0x55bb,0x5cea,0x5fa1,0x6108,
+ 0x6b32,0x7344,0x80b2,0x8b7d
+ },
+ { /* ku 34 */
+ 0x6d74,0x5bd3,0x88d5,0x9810,0x8c6b,0x99ad,0x9d1b,0x6df5,0x51a4,0x5143,
+ 0x57a3,0x8881,0x539f,0x63f4,0x8f45,0x5712,0x54e1,0x5713,0x733f,0x6e90,
+ 0x7de3,0x9060,0x82d1,0x9858,0x6028,0x9662,0x66f0,0x7d04,0x8d8a,0x8e8d,
+ 0x9470,0x5cb3,0x7ca4,0x6708,0x60a6,0x95b2,0x8018,0x96f2,0x9116,0x5300,
+ 0x9695,0x5141,0x904b,0x85f4,0x9196,0x6688,0x97f5,0x5b55,0x531d,0x7838,
+ 0x96dc,0x683d,0x54c9,0x707e,0x5bb0,0x8f09,0x518d,0x5728,0x54b1,0x6522,
+ 0x66ab,0x8d0a,0x8d1c,0x81df,0x846c,0x906d,0x7cdf,0x947f,0x85fb,0x68d7,
+ 0x65e9,0x6fa1,0x86a4,0x8e81,0x566a,0x9020,0x7682,0x7ac8,0x71e5,0x8cac,
+ 0x64c7,0x5247,0x6fa4,0x8cca,0x600e,0x589e,0x618e,0x66fe,0x8d08,0x624e,
+ 0x55b3,0x6e23,0x672d,0x8ecb
+ },
+ { /* ku 35 */
+ 0x9358,0x9598,0x7728,0x6805,0x69a8,0x548b,0x4e4d,0x70b8,0x8a50,0x6458,
+ 0x9f4b,0x5b85,0x7a84,0x50b5,0x5be8,0x77bb,0x6c08,0x8a79,0x7c98,0x6cbe,
+ 0x76de,0x65ac,0x8f3e,0x5d84,0x5c55,0x8638,0x68e7,0x5360,0x6230,0x7ad9,
+ 0x6e5b,0x7dbb,0x6a1f,0x7ae0,0x5f70,0x6f33,0x5f35,0x638c,0x6f32,0x6756,
+ 0x4e08,0x5e33,0x8cec,0x4ed7,0x8139,0x7634,0x969c,0x62db,0x662d,0x627e,
+ 0x6cbc,0x8d99,0x7167,0x7f69,0x5146,0x8087,0x53ec,0x906e,0x6298,0x54f2,
+ 0x87c4,0x8f4d,0x8005,0x937a,0x8517,0x9019,0x6d59,0x73cd,0x659f,0x771f,
+ 0x7504,0x7827,0x81fb,0x8c9e,0x91dd,0x5075,0x6795,0x75b9,0x8a3a,0x9707,
+ 0x632f,0x93ae,0x9663,0x84b8,0x6399,0x775c,0x5f81,0x7319,0x722d,0x6014,
+ 0x6574,0x62ef,0x6b63,0x653f
+ },
+ { /* ku 36 */
+ 0x5e40,0x7665,0x912d,0x8b49,0x829d,0x679d,0x652f,0x5431,0x8718,0x77e5,
+ 0x80a2,0x8102,0x6c41,0x4e4b,0x7e54,0x8077,0x76f4,0x690d,0x6b96,0x57f7,
+ 0x503c,0x4f84,0x5740,0x6307,0x6b62,0x8dbe,0x8879,0x65e8,0x7d19,0x5fd7,
+ 0x646f,0x64f2,0x81f3,0x81f4,0x7f6e,0x5e5f,0x5cd9,0x5236,0x667a,0x79e9,
+ 0x7a1a,0x8cea,0x7099,0x75d4,0x6eef,0x6cbb,0x7a92,0x4e2d,0x76c5,0x5fe0,
+ 0x9418,0x8877,0x7d42,0x7a2e,0x816b,0x91cd,0x4ef2,0x8846,0x821f,0x5468,
+ 0x5dde,0x6d32,0x8b05,0x7ca5,0x8ef8,0x8098,0x5e1a,0x5492,0x76ba,0x5b99,
+ 0x665d,0x9a5f,0x73e0,0x682a,0x86db,0x6731,0x732a,0x8af8,0x8a85,0x9010,
+ 0x7af9,0x71ed,0x716e,0x62c4,0x77da,0x56d1,0x4e3b,0x8457,0x67f1,0x52a9,
+ 0x86c0,0x8caf,0x9444,0x7bc9
+ },
+ { /* ku 37 */
+ 0x4f4f,0x6ce8,0x795d,0x99d0,0x6293,0x722a,0x62fd,0x5c08,0x78da,0x8f49,
+ 0x64b0,0x8cfa,0x7bc6,0x6a01,0x838a,0x88dd,0x599d,0x649e,0x58ef,0x72c0,
+ 0x690e,0x9310,0x8ffd,0x8d05,0x589c,0x7db4,0x8ac4,0x6e96,0x6349,0x62d9,
+ 0x5353,0x684c,0x7422,0x8301,0x914c,0x5544,0x7740,0x707c,0x6fc1,0x5179,
+ 0x54a8,0x8cc7,0x59ff,0x6ecb,0x6dc4,0x5b5c,0x7d2b,0x4ed4,0x7c7d,0x6ed3,
+ 0x5b50,0x81ea,0x6f2c,0x5b57,0x9b03,0x68d5,0x8e2a,0x5b97,0x7d9c,0x7e3d,
+ 0x7e31,0x9112,0x8d70,0x594f,0x63cd,0x79df,0x8db3,0x5352,0x65cf,0x7956,
+ 0x8a5b,0x963b,0x7d44,0x947d,0x7e82,0x5634,0x9189,0x6700,0x7f6a,0x5c0a,
+ 0x9075,0x6628,0x5de6,0x4f50,0x67de,0x505a,0x4f5c,0x5750,0x5ea7,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 38 */
+ 0x4e8d,0x4e0c,0x5140,0x4e10,0x5eff,0x5345,0x4e15,0x4e98,0x4e1e,0x9b32,
+ 0x5b6c,0x5669,0x4e28,0x79ba,0x4e3f,0x5315,0x4e47,0x592d,0x723b,0x536e,
+ 0x6c10,0x56df,0x80e4,0x9997,0x6bd3,0x777e,0x9f17,0x4e36,0x4e9f,0x9f10,
+ 0x4e5c,0x4e69,0x4e93,0x8288,0x5b5b,0x55c7,0x560f,0x4ec4,0x5399,0x539d,
+ 0x53b4,0x53a5,0x53ae,0x9768,0x8d0b,0x531a,0x53f5,0x532d,0x5331,0x533e,
+ 0x8cfe,0x5366,0x5363,0x5202,0x5208,0x520e,0x5244,0x5233,0x528c,0x5274,
+ 0x524c,0x525e,0x5261,0x525c,0x84af,0x527d,0x5282,0x5281,0x5290,0x5293,
+ 0x5182,0x7f54,0x4ebb,0x4ec3,0x4ec9,0x4ec2,0x4ee8,0x4ee1,0x4eeb,0x4ede,
+ 0x50b4,0x4ef3,0x4f22,0x4f64,0x4ef5,0x5000,0x5096,0x4f09,0x4f47,0x4f5e,
+ 0x4f67,0x6538,0x4f5a,0x4f5d
+ },
+ { /* ku 39 */
+ 0x4f5f,0x4f57,0x4f32,0x4f3d,0x4f76,0x4f74,0x4f91,0x4f89,0x4f83,0x4f8f,
+ 0x4f7e,0x4f7b,0x5115,0x4f7c,0x5102,0x4f94,0x5114,0x513c,0x5137,0x4fc5,
+ 0x4fda,0x4fe3,0x4fdc,0x4fd1,0x4fdf,0x4ff8,0x5029,0x504c,0x4ff3,0x502c,
+ 0x500f,0x502e,0x502d,0x4ffe,0x501c,0x500c,0x5025,0x5028,0x50e8,0x5043,
+ 0x5055,0x5048,0x504e,0x506c,0x50c2,0x513b,0x5110,0x513a,0x50ba,0x50d6,
+ 0x5106,0x50ed,0x50ec,0x50e6,0x50ee,0x5107,0x510b,0x4edd,0x6c3d,0x4f58,
+ 0x50c9,0x4fce,0x9fa0,0x6c46,0x7cf4,0x516e,0x5dfd,0x9ecc,0x9998,0x56c5,
+ 0x5914,0x52f9,0x530d,0x8a07,0x5310,0x9cec,0x5919,0x5155,0x4ea0,0x5156,
+ 0x4eb3,0x886e,0x88a4,0x893b,0x81e0,0x88d2,0x7980,0x5b34,0x8803,0x7fb8,
+ 0x51ab,0x51b1,0x51bd,0x51bc
+ },
+ { /* ku 3a */
+ 0x51c7,0x5196,0x51a2,0x51a5,0x8a01,0x8a10,0x8a0c,0x8a15,0x8b33,0x8a4e,
+ 0x8a25,0x8a41,0x8a36,0x8a46,0x8a54,0x8a58,0x8a52,0x8a86,0x8a84,0x8a7f,
+ 0x8a70,0x8a7c,0x8a75,0x8a6c,0x8a6e,0x8acd,0x8ae2,0x8a61,0x8a9a,0x8aa5,
+ 0x8a91,0x8a92,0x8acf,0x8ad1,0x8ac9,0x8adb,0x8ad7,0x8ac2,0x8ab6,0x8af6,
+ 0x8aeb,0x8b14,0x8b01,0x8ae4,0x8aed,0x8afc,0x8af3,0x8ae6,0x8aee,0x8ade,
+ 0x8b28,0x8b9c,0x8b16,0x8b1a,0x8b10,0x8b2b,0x8b2d,0x8b56,0x8b59,0x8b4e,
+ 0x8b9e,0x8b6b,0x8b96,0x5369,0x537a,0x961d,0x9622,0x9621,0x9631,0x962a,
+ 0x963d,0x963c,0x9642,0x9658,0x9654,0x965f,0x9689,0x966c,0x9672,0x9674,
+ 0x9688,0x968d,0x9697,0x96b0,0x9097,0x909b,0x913a,0x9099,0x9114,0x90a1,
+ 0x90b4,0x90b3,0x90b6,0x9134
+ },
+ { /* ku 3b */
+ 0x90b8,0x90b0,0x90df,0x90c5,0x90be,0x9136,0x90c4,0x90c7,0x9106,0x9148,
+ 0x90e2,0x90dc,0x90d7,0x90db,0x90eb,0x90ef,0x90fe,0x9104,0x9122,0x911e,
+ 0x9123,0x9131,0x912f,0x9139,0x9143,0x9146,0x82bb,0x5950,0x52f1,0x52ac,
+ 0x52ad,0x52be,0x54ff,0x52d0,0x52d6,0x52f0,0x53df,0x71ee,0x77cd,0x5ef4,
+ 0x51f5,0x51fc,0x9b2f,0x53b6,0x5f01,0x755a,0x5df0,0x574c,0x580a,0x57a1,
+ 0x587e,0x58bc,0x58c5,0x58d1,0x5729,0x572c,0x572a,0x5733,0x58d9,0x572e,
+ 0x572f,0x58e2,0x573b,0x5742,0x5769,0x58e0,0x576b,0x58da,0x577c,0x577b,
+ 0x5768,0x576d,0x5776,0x5773,0x57e1,0x57a4,0x578c,0x584f,0x57cf,0x57a7,
+ 0x5816,0x5793,0x57a0,0x57d5,0x5852,0x581d,0x5864,0x57d2,0x57b8,0x57f4,
+ 0x57ef,0x57f8,0x57e4,0x57dd
+ },
+ { /* ku 3c */
+ 0x580b,0x580d,0x57fd,0x57ed,0x5800,0x581e,0x5819,0x5844,0x5820,0x5865,
+ 0x586c,0x5881,0x5889,0x589a,0x5880,0x99a8,0x9f19,0x61ff,0x8279,0x827d,
+ 0x827f,0x828f,0x828a,0x82a8,0x8284,0x828e,0x8291,0x858c,0x8299,0x82ab,
+ 0x8553,0x82be,0x82b0,0x85f6,0x82ca,0x82e3,0x8298,0x82b7,0x82ae,0x83a7,
+ 0x8407,0x84ef,0x82a9,0x82b4,0x82a1,0x82aa,0x829f,0x82c4,0x82e7,0x82a4,
+ 0x82e1,0x8309,0x82f7,0x82e4,0x8622,0x8307,0x82dc,0x82f4,0x82d2,0x82d8,
+ 0x830c,0x82fb,0x82d3,0x8526,0x831a,0x8306,0x584b,0x7162,0x82e0,0x82d5,
+ 0x831c,0x8351,0x8558,0x84fd,0x8308,0x8392,0x833c,0x8334,0x8331,0x839b,
+ 0x854e,0x832f,0x834f,0x8347,0x8343,0x8588,0x8340,0x8317,0x85ba,0x832d,
+ 0x833a,0x8333,0x7296,0x6ece
+ },
+ { /* ku 3d */
+ 0x8541,0x831b,0x85ce,0x8552,0x84c0,0x8452,0x8464,0x83b0,0x8378,0x8494,
+ 0x8435,0x83a0,0x83aa,0x8393,0x839c,0x8385,0x837c,0x859f,0x83a9,0x837d,
+ 0x8555,0x837b,0x8398,0x839e,0x83a8,0x9daf,0x8493,0x83c1,0x8401,0x83e5,
+ 0x83d8,0x5807,0x8418,0x840b,0x83dd,0x83fd,0x83d6,0x841c,0x8438,0x8411,
+ 0x8406,0x83d4,0x83df,0x840f,0x8403,0x83f8,0x83f9,0x83ea,0x83c5,0x83c0,
+ 0x7e08,0x83f0,0x83e1,0x845c,0x8451,0x845a,0x8459,0x8473,0x8546,0x8488,
+ 0x847a,0x8562,0x8478,0x843c,0x8446,0x8469,0x8476,0x851e,0x848e,0x8431,
+ 0x846d,0x84c1,0x84cd,0x84d0,0x9a40,0x84bd,0x84d3,0x84ca,0x84bf,0x84ba,
+ 0x863a,0x84a1,0x84b9,0x84b4,0x8497,0x93a3,0x8577,0x850c,0x750d,0x8538,
+ 0x84f0,0x861e,0x851f,0x85fa
+ },
+ { /* ku 3e */
+ 0x8556,0x853b,0x84ff,0x84fc,0x8559,0x8548,0x8568,0x8564,0x855e,0x857a,
+ 0x77a2,0x8543,0x8604,0x857b,0x85a4,0x85a8,0x8587,0x858f,0x8579,0x85ea,
+ 0x859c,0x8585,0x85b9,0x85b7,0x85b0,0x861a,0x85c1,0x85dc,0x85ff,0x8627,
+ 0x8605,0x8629,0x8616,0x863c,0x5efe,0x5f08,0x593c,0x5969,0x8037,0x5955,
+ 0x595a,0x5958,0x530f,0x5c22,0x5c25,0x5c2c,0x5c37,0x624c,0x636b,0x6476,
+ 0x62bb,0x62ca,0x62da,0x62d7,0x62ee,0x649f,0x62f6,0x6339,0x634b,0x6343,
+ 0x63ad,0x63f6,0x6371,0x637a,0x638e,0x6451,0x636d,0x63ac,0x638a,0x6369,
+ 0x63ae,0x645c,0x63f2,0x63f8,0x63e0,0x64b3,0x63c4,0x63de,0x63ce,0x6452,
+ 0x63c6,0x63be,0x6504,0x6441,0x640b,0x641b,0x6420,0x640c,0x6426,0x6421,
+ 0x645e,0x6516,0x646d,0x6496
+ },
+ { /* ku 3f */
+ 0x647a,0x64f7,0x64fc,0x6499,0x651b,0x64c0,0x64d0,0x64d7,0x64e4,0x64e2,
+ 0x6509,0x6525,0x652e,0x5f0b,0x5fd2,0x7519,0x5f11,0x535f,0x53f1,0x5630,
+ 0x53e9,0x53e8,0x53fb,0x5412,0x5416,0x5406,0x544b,0x5638,0x56c8,0x5454,
+ 0x56a6,0x5443,0x5421,0x5504,0x54bc,0x5423,0x5432,0x5482,0x5494,0x5477,
+ 0x5471,0x5464,0x549a,0x5680,0x5484,0x5476,0x5466,0x565d,0x54d0,0x54ad,
+ 0x54c2,0x54b4,0x5660,0x54a7,0x54a6,0x5635,0x55f6,0x5472,0x54a3,0x5666,
+ 0x54bb,0x54bf,0x54cc,0x5672,0x54da,0x568c,0x54a9,0x54aa,0x54a4,0x5665,
+ 0x54cf,0x54de,0x561c,0x54e7,0x562e,0x54fd,0x5514,0x54f3,0x55e9,0x5523,
+ 0x550f,0x5511,0x5527,0x552a,0x5616,0x558f,0x55b5,0x5549,0x56c0,0x5541,
+ 0x5555,0x553f,0x5550,0x553c
+ },
+ { /* ku 40 */
+ 0x5537,0x5556,0x5575,0x5576,0x5577,0x5533,0x5530,0x555c,0x558b,0x55d2,
+ 0x5583,0x55b1,0x55b9,0x5588,0x5581,0x559f,0x557e,0x55d6,0x5591,0x557b,
+ 0x55df,0x560d,0x56b3,0x5594,0x5599,0x55ea,0x55f7,0x55c9,0x561f,0x55d1,
+ 0x56c1,0x55ec,0x55d4,0x55e6,0x55dd,0x55c4,0x55ef,0x55e5,0x55f2,0x566f,
+ 0x55cc,0x55cd,0x55e8,0x55f5,0x55e4,0x8f61,0x561e,0x5608,0x560c,0x5601,
+ 0x56b6,0x5623,0x55fe,0x5600,0x5627,0x562d,0x5658,0x5639,0x5657,0x562c,
+ 0x564d,0x5662,0x5659,0x5695,0x564c,0x5654,0x5686,0x5664,0x5671,0x566b,
+ 0x567b,0x567c,0x5685,0x5693,0x56af,0x56d4,0x56d7,0x56dd,0x56e1,0x5707,
+ 0x56eb,0x56f9,0x56ff,0x5704,0x570a,0x5709,0x571c,0x5e43,0x5e19,0x5e14,
+ 0x5e11,0x5e6c,0x5e58,0x5e57
+ },
+ { /* ku 41 */
+ 0x5e37,0x5e44,0x5e54,0x5e5b,0x5e5e,0x5e61,0x5c8c,0x5c7a,0x5c8d,0x5c90,
+ 0x5d87,0x5c88,0x5cf4,0x5c99,0x5c91,0x5d50,0x5c9c,0x5cb5,0x5ca2,0x5d2c,
+ 0x5cac,0x5cab,0x5cb1,0x5ca3,0x5cc1,0x5cb7,0x5da7,0x5cd2,0x5da0,0x5ccb,
+ 0x5d22,0x5d97,0x5d0d,0x5d27,0x5d26,0x5d2e,0x5d24,0x5d1e,0x5d06,0x5d1b,
+ 0x5db8,0x5d3e,0x5d34,0x5d3d,0x5d6c,0x5d5b,0x5d6f,0x5d81,0x5d6b,0x5d4b,
+ 0x5d4a,0x5d69,0x5d74,0x5d82,0x5d99,0x5d9d,0x8c73,0x5db7,0x5dd4,0x5f73,
+ 0x5f77,0x5f82,0x5f87,0x5f89,0x540e,0x5fa0,0x5f99,0x5f9c,0x5fa8,0x5fad,
+ 0x5fb5,0x5fbc,0x8862,0x5f61,0x72ad,0x72b0,0x72b4,0x7377,0x7341,0x72c3,
+ 0x72c1,0x72ce,0x72cd,0x72d2,0x72e8,0x736a,0x72e9,0x733b,0x72f4,0x72f7,
+ 0x7301,0x72f3,0x736b,0x72fa
+ },
+ { /* ku 42 */
+ 0x72fb,0x7317,0x7313,0x7380,0x730a,0x731e,0x731d,0x737c,0x7322,0x7339,
+ 0x7325,0x732c,0x7338,0x7331,0x7350,0x734d,0x7357,0x7360,0x736c,0x736f,
+ 0x737e,0x821b,0x5925,0x98e7,0x5924,0x5902,0x98e0,0x9933,0x98e9,0x993c,
+ 0x98ea,0x98eb,0x98ed,0x98f4,0x9909,0x9911,0x4f59,0x991b,0x9937,0x993f,
+ 0x9943,0x9948,0x9949,0x994a,0x994c,0x9962,0x5e80,0x5ee1,0x5e8b,0x5e96,
+ 0x5ea5,0x5ea0,0x5eb9,0x5eb5,0x5ebe,0x5eb3,0x8ce1,0x5ed2,0x5ed1,0x5edb,
+ 0x5ee8,0x5eea,0x81ba,0x5fc4,0x5fc9,0x5fd6,0x61fa,0x61ae,0x5fee,0x616a,
+ 0x5fe1,0x5fe4,0x613e,0x60b5,0x6134,0x5fea,0x5fed,0x5ff8,0x6019,0x6035,
+ 0x6026,0x601b,0x600f,0x600d,0x6029,0x602b,0x600a,0x61cc,0x6021,0x615f,
+ 0x61e8,0x60fb,0x6137,0x6042
+ },
+ { /* ku 43 */
+ 0x606a,0x60f2,0x6096,0x609a,0x6173,0x609d,0x6083,0x6092,0x608c,0x609b,
+ 0x611c,0x60bb,0x60b1,0x60dd,0x60d8,0x60c6,0x60da,0x60b4,0x6120,0x6192,
+ 0x6115,0x6123,0x60f4,0x6100,0x610e,0x612b,0x614a,0x6175,0x61ac,0x6194,
+ 0x61a7,0x61b7,0x61d4,0x61f5,0x5fdd,0x96b3,0x9582,0x9586,0x95c8,0x958e,
+ 0x9594,0x958c,0x95e5,0x95ad,0x95ab,0x9b2e,0x95ac,0x95be,0x95b6,0x9b29,
+ 0x95bf,0x95bd,0x95bc,0x95c3,0x95cb,0x95d4,0x95d0,0x95d5,0x95de,0x4e2c,
+ 0x723f,0x6215,0x6c35,0x6c54,0x6c5c,0x6c4a,0x7043,0x6c85,0x6c90,0x6c94,
+ 0x6c8c,0x6c68,0x6c69,0x6c74,0x6c76,0x6c86,0x6f59,0x6cd0,0x6cd4,0x6cad,
+ 0x7027,0x7018,0x6cf1,0x6cd7,0x6cb2,0x6ce0,0x6cd6,0x6ffc,0x6ceb,0x6cee,
+ 0x6cb1,0x6cd3,0x6cef,0x6d87
+ },
+ { /* ku 44 */
+ 0x6d39,0x6d27,0x6d0c,0x6d79,0x6e5e,0x6d07,0x6d04,0x6d19,0x6d0e,0x6d2b,
+ 0x6fae,0x6d2e,0x6d35,0x6d1a,0x700f,0x6ef8,0x6f6f,0x6d33,0x6d91,0x6d6f,
+ 0x6df6,0x6f7f,0x6d5e,0x6d93,0x6d94,0x6d5c,0x6d60,0x6d7c,0x6d63,0x6e1a,
+ 0x6dc7,0x6dc5,0x6dde,0x7006,0x6dbf,0x6de0,0x6fa0,0x6de6,0x6ddd,0x6dd9,
+ 0x700b,0x6dab,0x6e0c,0x6dae,0x6e2b,0x6e6e,0x6e4e,0x6e6b,0x6eb2,0x6e5f,
+ 0x6e86,0x6e53,0x6e54,0x6e32,0x6e25,0x6e44,0x7067,0x6eb1,0x6e98,0x7044,
+ 0x6f2d,0x7005,0x6ea5,0x6ea7,0x6ebd,0x6ebb,0x6eb7,0x6f77,0x6eb4,0x6ecf,
+ 0x6e8f,0x6ec2,0x6e9f,0x6f62,0x7020,0x701f,0x6f24,0x6f15,0x6ef9,0x6f2f,
+ 0x6f36,0x7032,0x6f74,0x6f2a,0x6f09,0x6f29,0x6f89,0x6f8d,0x6f8c,0x6f78,
+ 0x6f72,0x6f7c,0x6f7a,0x7028
+ },
+ { /* ku 45 */
+ 0x6fc9,0x6fa7,0x6fb9,0x6fb6,0x6fc2,0x6fe1,0x6fee,0x6fde,0x6fe0,0x6fef,
+ 0x701a,0x7023,0x701b,0x7039,0x7035,0x705d,0x705e,0x5b80,0x5b84,0x5b95,
+ 0x5b93,0x5ba5,0x5bb8,0x752f,0x9a2b,0x6434,0x5be4,0x5bee,0x8930,0x5bf0,
+ 0x8e47,0x8b07,0x8fb6,0x8fd3,0x8fd5,0x8fe5,0x8fee,0x8fe4,0x9087,0x8fe6,
+ 0x9015,0x8fe8,0x9005,0x9004,0x900b,0x9090,0x9011,0x900d,0x9016,0x9021,
+ 0x9035,0x9036,0x902d,0x902f,0x9044,0x9051,0x9052,0x9050,0x9068,0x9058,
+ 0x9062,0x905b,0x66b9,0x9074,0x907d,0x9082,0x9088,0x9083,0x908b,0x5f50,
+ 0x5f57,0x5f56,0x5f58,0x5c3b,0x54ab,0x5c50,0x5c59,0x5b71,0x5c63,0x5c68,
+ 0x7fbc,0x5f33,0x5f29,0x5f2d,0x8274,0x5f3c,0x9b3b,0x5c6e,0x5981,0x5983,
+ 0x598d,0x5af5,0x5ad7,0x59a3
+ },
+ { /* ku 46 */
+ 0x5997,0x59ca,0x5b00,0x599e,0x59a4,0x59d2,0x59b2,0x59af,0x59d7,0x59be,
+ 0x5a6d,0x5b08,0x59dd,0x5b4c,0x59e3,0x59d8,0x59f9,0x5a0c,0x5a09,0x5aa7,
+ 0x5afb,0x5a11,0x5a23,0x5a13,0x5a40,0x5a67,0x5a4a,0x5a55,0x5a3c,0x5a62,
+ 0x5b0b,0x80ec,0x5aaa,0x5a9b,0x5a77,0x5a7a,0x5abe,0x5aeb,0x5ab2,0x5b21,
+ 0x5b2a,0x5ab8,0x5ae0,0x5ae3,0x5b19,0x5ad6,0x5ae6,0x5ad8,0x5adc,0x5b09,
+ 0x5b17,0x5b16,0x5b32,0x5b37,0x5b40,0x5c15,0x5c1c,0x5b5a,0x5b65,0x5b73,
+ 0x5b51,0x5b53,0x5b62,0x99d4,0x99df,0x99d9,0x9a36,0x9a5b,0x99d1,0x99d8,
+ 0x9a4d,0x9a4a,0x99e2,0x9a6a,0x9a0f,0x9a0d,0x9a05,0x9a42,0x9a2d,0x9a16,
+ 0x9a41,0x9a2e,0x9a38,0x9a43,0x9a44,0x9a4f,0x9a65,0x9a64,0x7cf9,0x7d06,
+ 0x7d02,0x7d07,0x7d08,0x7e8a
+ },
+ { /* ku 47 */
+ 0x7d1c,0x7d15,0x7d13,0x7d3a,0x7d32,0x7d31,0x7e10,0x7d3c,0x7d40,0x7d3f,
+ 0x7d5d,0x7d4e,0x7d73,0x7d86,0x7d83,0x7d88,0x7dbe,0x7dba,0x7dcb,0x7dd4,
+ 0x7dc4,0x7d9e,0x7dac,0x7db9,0x7da3,0x7db0,0x7dc7,0x7dd9,0x7dd7,0x7df9,
+ 0x7df2,0x7e62,0x7de6,0x7df6,0x7df1,0x7e0b,0x7de1,0x7e09,0x7e1d,0x7e1f,
+ 0x7e1e,0x7e2d,0x7e0a,0x7e11,0x7e7d,0x7e39,0x7e35,0x7e32,0x7e46,0x7e45,
+ 0x7e88,0x7e5a,0x7e52,0x7e6e,0x7e7e,0x7e70,0x7e6f,0x7e98,0x5e7a,0x757f,
+ 0x5ddb,0x753e,0x9095,0x738e,0x74a3,0x744b,0x73a2,0x739f,0x73cf,0x73c2,
+ 0x74cf,0x73b7,0x73b3,0x73c0,0x73c9,0x73c8,0x73e5,0x73d9,0x980a,0x740a,
+ 0x73e9,0x73e7,0x73de,0x74bd,0x743f,0x7489,0x742a,0x745b,0x7426,0x7425,
+ 0x7428,0x7430,0x742e,0x742c
+ },
+ { /* ku 48 */
+ 0x741b,0x741a,0x7441,0x745c,0x7457,0x7455,0x7459,0x74a6,0x746d,0x747e,
+ 0x749c,0x74d4,0x7480,0x7481,0x7487,0x748b,0x749e,0x74a8,0x74a9,0x7490,
+ 0x74a7,0x74da,0x74ba,0x97d9,0x97de,0x97dc,0x674c,0x6753,0x675e,0x6748,
+ 0x69aa,0x6aea,0x6787,0x676a,0x6773,0x6798,0x6898,0x6775,0x68d6,0x6a05,
+ 0x689f,0x678b,0x6777,0x677c,0x67f0,0x6adb,0x67d8,0x6af3,0x67e9,0x67b0,
+ 0x6ae8,0x67d9,0x67b5,0x67da,0x67b3,0x67dd,0x6800,0x67c3,0x67b8,0x67e2,
+ 0x6adf,0x67c1,0x6a89,0x6832,0x6833,0x690f,0x6a48,0x684e,0x6968,0x6844,
+ 0x69bf,0x6883,0x681d,0x6855,0x6a3a,0x6841,0x6a9c,0x6840,0x6b12,0x684a,
+ 0x6849,0x6829,0x68b5,0x688f,0x6874,0x6877,0x6893,0x686b,0x6b1e,0x696e,
+ 0x68fc,0x6add,0x69e7,0x68f9
+ },
+ { /* ku 49 */
+ 0x6b0f,0x68f0,0x690b,0x6901,0x6957,0x68e3,0x6910,0x6971,0x6939,0x6960,
+ 0x6942,0x695d,0x6b16,0x696b,0x6980,0x6998,0x6978,0x6934,0x69cc,0x6aec,
+ 0x6ada,0x69ce,0x6af8,0x6966,0x6963,0x6979,0x699b,0x69a7,0x69bb,0x69ab,
+ 0x69ad,0x69d4,0x69b1,0x69c1,0x69ca,0x6ab3,0x6995,0x6ae7,0x698d,0x69ff,
+ 0x6aa3,0x69ed,0x6a17,0x6a18,0x6a65,0x69f2,0x6a44,0x6a3e,0x6aa0,0x6a50,
+ 0x6a5b,0x6a35,0x6a8e,0x6ad3,0x6a3d,0x6a28,0x6a58,0x6ade,0x6a91,0x6a90,
+ 0x6aa9,0x6a97,0x6aab,0x7337,0x7352,0x6b81,0x6b82,0x6ba4,0x6b84,0x6b9e,
+ 0x6bae,0x6b8d,0x6bab,0x6b9b,0x6baf,0x6baa,0x8ed4,0x8edb,0x8ef2,0x8efb,
+ 0x8f64,0x8ef9,0x8efc,0x8eeb,0x8ee4,0x8f62,0x8efa,0x8efe,0x8f0a,0x8f07,
+ 0x8f05,0x8f12,0x8f26,0x8f1e
+ },
+ { /* ku 4a */
+ 0x8f1f,0x8f1c,0x8f33,0x8f46,0x8f54,0x8ece,0x6214,0x6227,0x621b,0x621f,
+ 0x6222,0x6221,0x6225,0x6224,0x6229,0x81e7,0x750c,0x74f4,0x74ff,0x750f,
+ 0x7511,0x7513,0x6534,0x65ee,0x65ef,0x65f0,0x660a,0x66c7,0x6772,0x6603,
+ 0x6615,0x6600,0x7085,0x66f7,0x661d,0x6634,0x6631,0x6636,0x6635,0x8006,
+ 0x665f,0x66c4,0x6641,0x664f,0x6689,0x6661,0x6657,0x6677,0x6684,0x668c,
+ 0x66d6,0x669d,0x66be,0x66db,0x66dc,0x66e6,0x66e9,0x8cc1,0x8cb0,0x8cba,
+ 0x8cbd,0x8d04,0x8cb2,0x8cc5,0x8d10,0x8cd1,0x8cda,0x8cd5,0x8ceb,0x8ce7,
+ 0x8cfb,0x8998,0x89ac,0x89a1,0x89bf,0x89a6,0x89af,0x89b2,0x89b7,0x726e,
+ 0x729f,0x725d,0x7266,0x726f,0x727e,0x727f,0x7284,0x728b,0x728d,0x728f,
+ 0x7292,0x6308,0x6332,0x63b0
+ },
+ { /* ku 4b */
+ 0x643f,0x64d8,0x8004,0x6bea,0x6bf3,0x6bfd,0x6bff,0x6bf9,0x6c05,0x6c0c,
+ 0x6c06,0x6c0d,0x6c15,0x6c18,0x6c19,0x6c1a,0x6c21,0x6c2c,0x6c24,0x6c2a,
+ 0x6c32,0x6535,0x6555,0x656b,0x7258,0x7252,0x7256,0x7230,0x8662,0x5216,
+ 0x809f,0x809c,0x8093,0x80bc,0x670a,0x80bd,0x80b1,0x80ab,0x80ad,0x80b4,
+ 0x80b7,0x6727,0x8156,0x80e9,0x81da,0x80db,0x80c2,0x80c4,0x80d9,0x80cd,
+ 0x80d7,0x6710,0x80dd,0x811b,0x80f1,0x80f4,0x80ed,0x81be,0x810e,0x80f2,
+ 0x80fc,0x6715,0x8112,0x8c5a,0x8161,0x811e,0x812c,0x8118,0x8132,0x8148,
+ 0x814c,0x8153,0x8174,0x8159,0x815a,0x8171,0x8160,0x8169,0x817c,0x817d,
+ 0x816d,0x8167,0x584d,0x5ab5,0x8188,0x8182,0x81cf,0x6ed5,0x81a3,0x81aa,
+ 0x81cc,0x6726,0x81ca,0x81bb
+ },
+ { /* ku 4c */
+ 0x81c1,0x81a6,0x6b5f,0x6b37,0x6b39,0x6b43,0x6b46,0x6b59,0x98ae,0x98af,
+ 0x98b6,0x98bc,0x98c6,0x98c8,0x6bb3,0x5f40,0x8f42,0x89f3,0x6590,0x9f4f,
+ 0x6595,0x65bc,0x65c6,0x65c4,0x65c3,0x65cc,0x65ce,0x65d2,0x65d6,0x716c,
+ 0x7152,0x7096,0x7197,0x70bb,0x70c0,0x70b7,0x70ab,0x70b1,0x71c1,0x70ca,
+ 0x7110,0x7113,0x71dc,0x712f,0x7131,0x7173,0x715c,0x7168,0x7145,0x7172,
+ 0x714a,0x7178,0x717a,0x7198,0x71b3,0x71b5,0x71a8,0x71a0,0x71e0,0x71d4,
+ 0x71e7,0x71f9,0x721d,0x7228,0x706c,0x71fe,0x7166,0x71b9,0x623e,0x623d,
+ 0x6243,0x6248,0x6249,0x793b,0x7940,0x7946,0x7949,0x795b,0x795c,0x7953,
+ 0x795a,0x79b0,0x7957,0x7960,0x798e,0x7967,0x797a,0x79aa,0x798a,0x799a,
+ 0x79a7,0x79b3,0x5fd1,0x5fd0
+ },
+ { /* ku 4d */
+ 0x61df,0x605d,0x605a,0x6067,0x6041,0x6059,0x6063,0x6164,0x6106,0x610d,
+ 0x615d,0x61a9,0x619d,0x61cb,0x61e3,0x6207,0x8080,0x807f,0x6c93,0x6fa9,
+ 0x6dfc,0x78ef,0x77f8,0x78ad,0x7809,0x7868,0x7818,0x7811,0x65ab,0x782d,
+ 0x78b8,0x781d,0x7839,0x792a,0x7931,0x781f,0x783c,0x7825,0x782c,0x7823,
+ 0x7829,0x784e,0x786d,0x7864,0x78fd,0x7826,0x7850,0x7847,0x784c,0x786a,
+ 0x78e7,0x7893,0x789a,0x7887,0x78e3,0x78a1,0x78a3,0x78b2,0x78b9,0x78a5,
+ 0x78d4,0x78d9,0x78c9,0x78ec,0x78f2,0x7905,0x78f4,0x7913,0x7924,0x791e,
+ 0x7934,0x9f95,0x9ef9,0x9efb,0x9efc,0x76f1,0x7704,0x7798,0x76f9,0x7707,
+ 0x7708,0x771a,0x7722,0x7719,0x772d,0x7726,0x7735,0x7738,0x775e,0x77bc,
+ 0x7747,0x7743,0x775a,0x7768
+ },
+ { /* ku 4e */
+ 0x7762,0x7765,0x777f,0x778d,0x777d,0x7780,0x778c,0x7791,0x779f,0x77a0,
+ 0x77b0,0x77b5,0x77bd,0x753a,0x7540,0x754e,0x754b,0x7548,0x755b,0x7572,
+ 0x7579,0x7583,0x7f58,0x7f61,0x7f5f,0x8a48,0x7f68,0x7f86,0x7f71,0x7f79,
+ 0x7f88,0x7f7e,0x76cd,0x76e5,0x8832,0x91d2,0x91d3,0x91d4,0x91d9,0x91d7,
+ 0x91d5,0x91f7,0x91e7,0x91e4,0x9346,0x91f5,0x91f9,0x9208,0x9226,0x9245,
+ 0x9211,0x9210,0x9201,0x9227,0x9204,0x9225,0x9200,0x923a,0x9266,0x9237,
+ 0x9233,0x9255,0x923d,0x9238,0x925e,0x926c,0x926d,0x923f,0x9460,0x9230,
+ 0x9249,0x9248,0x924d,0x922e,0x9239,0x9438,0x92ac,0x92a0,0x927a,0x92aa,
+ 0x92ee,0x92cf,0x9403,0x92e3,0x943a,0x92b1,0x92a6,0x93a7,0x9296,0x92cc,
+ 0x92a9,0x93f5,0x9293,0x927f
+ },
+ { /* ku 4f */
+ 0x93a9,0x929a,0x931a,0x92ab,0x9283,0x940b,0x92a8,0x92a3,0x9412,0x9338,
+ 0x92f1,0x93d7,0x92e5,0x92f0,0x92ef,0x92e8,0x92bc,0x92dd,0x92f6,0x9426,
+ 0x9427,0x92c3,0x92df,0x92e6,0x9312,0x9306,0x9369,0x931b,0x9340,0x9301,
+ 0x9315,0x932e,0x9343,0x9307,0x9308,0x931f,0x9319,0x9365,0x9347,0x9376,
+ 0x9354,0x9364,0x93aa,0x9370,0x9384,0x93e4,0x93d8,0x9428,0x9387,0x93cc,
+ 0x9398,0x93b8,0x93bf,0x93a6,0x93b0,0x93b5,0x944c,0x93e2,0x93dc,0x93dd,
+ 0x93cd,0x93de,0x93c3,0x93c7,0x93d1,0x9414,0x941d,0x93f7,0x9465,0x9413,
+ 0x946d,0x9420,0x9479,0x93f9,0x9419,0x944a,0x9432,0x943f,0x9454,0x9463,
+ 0x937e,0x77e7,0x77ec,0x96c9,0x79d5,0x79ed,0x79e3,0x79eb,0x7a06,0x5d47,
+ 0x7a03,0x7a02,0x7a1e,0x7a14
+ },
+ { /* ku 50 */
+ 0x7a39,0x7a37,0x7a61,0x9ecf,0x99a5,0x7a70,0x7688,0x768e,0x7693,0x7699,
+ 0x76a4,0x74de,0x74e0,0x752c,0x9ce9,0x9cf6,0x9d07,0x9d06,0x9d23,0x9d87,
+ 0x9e15,0x9d1d,0x9d1f,0x9de5,0x9d2f,0x9dd9,0x9d30,0x9d42,0x9e1e,0x9d53,
+ 0x9e1d,0x9d60,0x9d52,0x9df3,0x9d5c,0x9d61,0x9d93,0x9d6a,0x9d6f,0x9d89,
+ 0x9d98,0x9d9a,0x9dc0,0x9da5,0x9da9,0x9dc2,0x9dbc,0x9e1a,0x9dd3,0x9dda,
+ 0x9def,0x9de6,0x9df2,0x9df8,0x9e0c,0x9dfa,0x9e1b,0x7592,0x7594,0x7664,
+ 0x7658,0x759d,0x7667,0x75a3,0x75b3,0x75b4,0x75b8,0x75c4,0x75b1,0x75b0,
+ 0x75c3,0x75c2,0x7602,0x75cd,0x75e3,0x7646,0x75e6,0x75e4,0x7647,0x75e7,
+ 0x7603,0x75f1,0x75fc,0x75ff,0x7610,0x7600,0x7649,0x760c,0x761e,0x760a,
+ 0x7625,0x763b,0x7615,0x7619
+ },
+ { /* ku 51 */
+ 0x761b,0x763c,0x7622,0x7620,0x7640,0x762d,0x7630,0x766d,0x7635,0x7643,
+ 0x766e,0x7633,0x764d,0x7669,0x7654,0x765c,0x7656,0x7672,0x766f,0x7fca,
+ 0x7ae6,0x7a78,0x7a79,0x7a80,0x7a86,0x7a88,0x7a95,0x7ac7,0x7aa0,0x7aac,
+ 0x7aa8,0x7ab6,0x7ab3,0x8864,0x8869,0x8872,0x887d,0x887f,0x8882,0x88a2,
+ 0x8960,0x88b7,0x88bc,0x88c9,0x8933,0x88ce,0x895d,0x8947,0x88f1,0x891a,
+ 0x88fc,0x88e8,0x88fe,0x88f0,0x8921,0x8919,0x8913,0x8938,0x890a,0x8964,
+ 0x892b,0x8936,0x8941,0x8966,0x897b,0x758b,0x80e5,0x76b8,0x76b4,0x77dc,
+ 0x8012,0x8014,0x8016,0x801c,0x8020,0x802e,0x8025,0x8026,0x802c,0x8029,
+ 0x8028,0x8031,0x800b,0x8035,0x8043,0x8046,0x8079,0x8052,0x8075,0x8071,
+ 0x8983,0x9807,0x980e,0x980f
+ },
+ { /* ku 52 */
+ 0x9821,0x981c,0x6f41,0x9826,0x9837,0x984e,0x9853,0x9873,0x9862,0x9859,
+ 0x9865,0x986c,0x9870,0x864d,0x8654,0x866c,0x87e3,0x8806,0x867a,0x867c,
+ 0x867b,0x86a8,0x868d,0x868b,0x8706,0x869d,0x86a7,0x86a3,0x86aa,0x8693,
+ 0x86a9,0x86b6,0x86c4,0x86b5,0x8823,0x86b0,0x86ba,0x86b1,0x86af,0x86c9,
+ 0x87f6,0x86b4,0x86e9,0x86fa,0x87ef,0x86ed,0x8784,0x86d0,0x8713,0x86de,
+ 0x8810,0x86df,0x86d8,0x86d1,0x8703,0x8707,0x86f8,0x8708,0x870a,0x870d,
+ 0x8709,0x8723,0x873b,0x871e,0x8725,0x872e,0x871a,0x873e,0x87c8,0x8734,
+ 0x8731,0x8729,0x8737,0x873f,0x8782,0x8722,0x877d,0x8811,0x877b,0x8760,
+ 0x8770,0x874c,0x876e,0x878b,0x8753,0x8763,0x87bb,0x8764,0x8759,0x8765,
+ 0x8793,0x87af,0x87ce,0x87d2
+ },
+ { /* ku 53 */
+ 0x87c6,0x8788,0x8785,0x87ad,0x8797,0x8783,0x87ab,0x87e5,0x87ac,0x87b5,
+ 0x87b3,0x87cb,0x87d3,0x87bd,0x87d1,0x87c0,0x87ca,0x87db,0x87ea,0x87e0,
+ 0x87ee,0x8816,0x8813,0x87fe,0x880a,0x881b,0x8821,0x8839,0x883c,0x7f36,
+ 0x7f4c,0x7f44,0x7f45,0x8210,0x7afa,0x7afd,0x7b08,0x7be4,0x7b04,0x7b67,
+ 0x7b0a,0x7b2b,0x7b0f,0x7b47,0x7b38,0x7b2a,0x7b19,0x7b2e,0x7b31,0x7b20,
+ 0x7b25,0x7b24,0x7b33,0x7c69,0x7b1e,0x7b58,0x7bf3,0x7b45,0x7b75,0x7b4c,
+ 0x7b8f,0x7b60,0x7b6e,0x7b7b,0x7b62,0x7b72,0x7b71,0x7b90,0x7c00,0x7bcb,
+ 0x7bb8,0x7bac,0x7b9d,0x7c5c,0x7b85,0x7c1e,0x7b9c,0x7ba2,0x7c2b,0x7bb4,
+ 0x7c23,0x7bc1,0x7bcc,0x7bdd,0x7bda,0x7be5,0x7be6,0x7bea,0x7c0c,0x7bfe,
+ 0x7bfc,0x7c0f,0x7c6a,0x7c0b
+ },
+ { /* ku 54 */
+ 0x7c1f,0x7c2a,0x7c26,0x7c38,0x7c5f,0x7c40,0x81fe,0x8201,0x8202,0x8204,
+ 0x81ec,0x8844,0x8221,0x8222,0x8264,0x822d,0x822f,0x8228,0x822b,0x8238,
+ 0x826b,0x8233,0x8234,0x823e,0x8244,0x8249,0x824b,0x824f,0x825a,0x825f,
+ 0x8268,0x887e,0x88ca,0x8888,0x88d8,0x88df,0x895e,0x7f9d,0x7fa5,0x7fa7,
+ 0x7faf,0x7fb0,0x7fb2,0x7c7c,0x6549,0x7c91,0x7cf2,0x7cf6,0x7c9e,0x7ca2,
+ 0x7cb2,0x7cbc,0x7cbd,0x7cdd,0x7cc7,0x7ccc,0x7ccd,0x7cc8,0x7cc5,0x7cd7,
+ 0x7ce8,0x826e,0x66a8,0x7fbf,0x7fce,0x7fd5,0x7fe5,0x7fe1,0x7fe6,0x7fe9,
+ 0x7fee,0x7ff3,0x7cf8,0x7e36,0x7da6,0x7dae,0x7e47,0x7e9b,0x9ea9,0x9eb4,
+ 0x8d73,0x8d84,0x8d94,0x8d91,0x8db2,0x8d67,0x8d6d,0x8c47,0x8c49,0x914a,
+ 0x9150,0x914e,0x914f,0x9164
+ },
+ { /* ku 55 */
+ 0x9162,0x9161,0x9170,0x9169,0x916f,0x91c5,0x91c3,0x9172,0x9174,0x9179,
+ 0x918c,0x9185,0x9190,0x918d,0x9191,0x91a2,0x91a3,0x91aa,0x91ad,0x91ae,
+ 0x91af,0x91b5,0x91b4,0x91ba,0x8c55,0x9e7a,0x8e89,0x8deb,0x8e05,0x8e59,
+ 0x8e69,0x8db5,0x8dbf,0x8dbc,0x8dba,0x8e4c,0x8dd6,0x8dd7,0x8dda,0x8e92,
+ 0x8dce,0x8dcf,0x8ddb,0x8dc6,0x8dec,0x8e7a,0x8e55,0x8de3,0x8e9a,0x8e8b,
+ 0x8de4,0x8e09,0x8dfd,0x8e14,0x8e1d,0x8e1f,0x8e93,0x8e2e,0x8e23,0x8e91,
+ 0x8e3a,0x8e40,0x8e39,0x8e35,0x8e3d,0x8e31,0x8e49,0x8e41,0x8e42,0x8ea1,
+ 0x8e63,0x8e4a,0x8e70,0x8e76,0x8e7c,0x8e6f,0x8e74,0x8e85,0x8eaa,0x8e94,
+ 0x8e90,0x8ea6,0x8e9e,0x8c78,0x8c82,0x8c8a,0x8c85,0x8c98,0x8c94,0x659b,
+ 0x89d6,0x89f4,0x89da,0x89dc
+ },
+ { /* ku 56 */
+ 0x89e5,0x89eb,0x89f6,0x8a3e,0x8b26,0x975a,0x96e9,0x9742,0x96ef,0x9706,
+ 0x973d,0x9708,0x970f,0x970e,0x972a,0x9744,0x9730,0x973e,0x9f54,0x9f5f,
+ 0x9f59,0x9f60,0x9f5c,0x9f66,0x9f6c,0x9f6a,0x9f77,0x9efd,0x9eff,0x9f09,
+ 0x96b9,0x96bc,0x96bd,0x96ce,0x96d2,0x77bf,0x8b8e,0x928e,0x947e,0x92c8,
+ 0x93e8,0x936a,0x93ca,0x938f,0x943e,0x946b,0x9b77,0x9b74,0x9b81,0x9b83,
+ 0x9b8e,0x9c78,0x7a4c,0x9b92,0x9c5f,0x9b90,0x9bad,0x9b9a,0x9baa,0x9b9e,
+ 0x9c6d,0x9bab,0x9b9d,0x9c58,0x9bc1,0x9c7a,0x9c31,0x9c39,0x9c23,0x9c37,
+ 0x9bc0,0x9bca,0x9bc7,0x9bfd,0x9bd6,0x9bea,0x9beb,0x9be1,0x9be4,0x9be7,
+ 0x9bdd,0x9be2,0x9bf0,0x9bdb,0x9bf4,0x9bd4,0x9c5d,0x9c08,0x9c10,0x9c0d,
+ 0x9c12,0x9c09,0x9bff,0x9c20
+ },
+ { /* ku 57 */
+ 0x9c32,0x9c2d,0x9c28,0x9c25,0x9c29,0x9c33,0x9c3e,0x9c48,0x9c3b,0x9c35,
+ 0x9c45,0x9c56,0x9c54,0x9c52,0x9c67,0x977c,0x9785,0x97c3,0x97bd,0x9794,
+ 0x97c9,0x97ab,0x97a3,0x97b2,0x97b4,0x9ab1,0x9ab0,0x9ab7,0x9dbb,0x9ab6,
+ 0x9aba,0x9abc,0x9ac1,0x9ac0,0x9acf,0x9ac2,0x9ad6,0x9ad5,0x9ad1,0x9b45,
+ 0x9b43,0x9b58,0x9b4e,0x9b48,0x9b4d,0x9b51,0x9957,0x995c,0x992e,0x9955,
+ 0x9954,0x9adf,0x9ae1,0x9ae6,0x9aef,0x9aeb,0x9afb,0x9aed,0x9af9,0x9b08,
+ 0x9b0f,0x9b22,0x9b1f,0x9b23,0x4e48,0x9ebe,0x7e3b,0x9e82,0x9e87,0x9e88,
+ 0x9e8b,0x9e92,0x93d6,0x9e9d,0x9e9f,0x9edb,0x9edc,0x9edd,0x9ee0,0x9edf,
+ 0x9ee2,0x9ef7,0x9ee7,0x9ee5,0x9ef2,0x9eef,0x9f22,0x9f2c,0x9f2f,0x9f39,
+ 0x9f37,0x9f3d,0x9f3e,0x9f44
+ },
+ { /* ku 58 */
+ 0x896c,0x95c6,0x9336,0x5f46,0x8514,0x7e94,0x5382,0x51b2,0x4e11,0x9f63,
+ 0x5679,0x515a,0x6dc0,0x9f15,0x6597,0x5641,0x9aee,0x8303,0x4e30,0x8907,
+ 0x5e72,0x7a40,0x98b3,0x5e7f,0x95a4,0x9b0d,0x5212,0x8ff4,0x5f59,0x7a6b,
+ 0x98e2,0x51e0,0x50a2,0x4ef7,0x8350,0x8591,0x5118,0x636e,0x6372,0x524b,
+ 0x5938,0x774f,0x8721,0x814a,0x7e8d,0x91cc,0x66c6,0x5e18,0x77ad,0x9e75,
+ 0x56c9,0x9ef4,0x6fdb,0x61de,0x77c7,0x7030,0x9eb5,0x884a,0x95e2,0x82f9,
+ 0x51ed,0x6251,0x4ec6,0x6734,0x97c6,0x7c64,0x7e34,0x97a6,0x9eaf,0x786e,
+ 0x820d,0x672f,0x677e,0x56cc,0x53f0,0x98b1,0x6aaf,0x7f4e,0x6d82,0x7cf0,
+ 0x4e07,0x4fc2,0x7e6b,0x9e79,0x56ae,0x9b1a,0x846f,0x53f6,0x90c1,0x79a6,
+ 0x7c72,0x613f,0x4e91,0x9ad2
+ },
+ { /* ku 59 */
+ 0x75c7,0x96bb,0x53ea,0x7dfb,0x88fd,0x79cd,0x7843,0x7b51,0x51c6,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ }
+};
diff --git a/imap/src/charset/gb_2312.c b/imap/src/charset/gb_2312.c
new file mode 100644
index 00000000..78efbbb8
--- /dev/null
+++ b/imap/src/charset/gb_2312.c
@@ -0,0 +1,2795 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: GBK conversion table
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 July 1997
+ * Last Edited: 30 August 2006
+ */
+
+/* GB 2312 is the national standard of the People's Republic of China
+ * (mainland China). This is actually the GBK extended version.
+ */
+
+#define BASE_GB2312_KU 0x81
+#define BASE_GB2312_TEN 0x40
+#define MAX_GB2312_KU 125
+#define MAX_GB2312_TEN 191
+
+
+#define GBTOUNICODE(c,c1,ku,ten) \
+ ((((ku = c - BASE_GB2312_KU) < MAX_GB2312_KU) && \
+ ((ten = c1 - BASE_GB2312_TEN) < MAX_GB2312_TEN)) ? \
+ gb2312tab[ku][ten] : UBOGON)
+
+static const unsigned short gb2312tab[MAX_GB2312_KU][MAX_GB2312_TEN] = {
+ { /* ku 01 */
+ 0x4e02,0x4e04,0x4e05,0x4e06,0x4e0f,0x4e12,0x4e17,0x4e1f,0x4e20,0x4e21,
+ 0x4e23,0x4e26,0x4e29,0x4e2e,0x4e2f,0x4e31,0x4e33,0x4e35,0x4e37,0x4e3c,
+ 0x4e40,0x4e41,0x4e42,0x4e44,0x4e46,0x4e4a,0x4e51,0x4e55,0x4e57,0x4e5a,
+ 0x4e5b,0x4e62,0x4e63,0x4e64,0x4e65,0x4e67,0x4e68,0x4e6a,0x4e6b,0x4e6c,
+ 0x4e6d,0x4e6e,0x4e6f,0x4e72,0x4e74,0x4e75,0x4e76,0x4e77,0x4e78,0x4e79,
+ 0x4e7a,0x4e7b,0x4e7c,0x4e7d,0x4e7f,0x4e80,0x4e81,0x4e82,0x4e83,0x4e84,
+ 0x4e85,0x4e87,0x4e8a,UBOGON,0x4e90,0x4e96,0x4e97,0x4e99,0x4e9c,0x4e9d,
+ 0x4e9e,0x4ea3,0x4eaa,0x4eaf,0x4eb0,0x4eb1,0x4eb4,0x4eb6,0x4eb7,0x4eb8,
+ 0x4eb9,0x4ebc,0x4ebd,0x4ebe,0x4ec8,0x4ecc,0x4ecf,0x4ed0,0x4ed2,0x4eda,
+ 0x4edb,0x4edc,0x4ee0,0x4ee2,0x4ee6,0x4ee7,0x4ee9,0x4eed,0x4eee,0x4eef,
+ 0x4ef1,0x4ef4,0x4ef8,0x4ef9,0x4efa,0x4efc,0x4efe,0x4f00,0x4f02,0x4f03,
+ 0x4f04,0x4f05,0x4f06,0x4f07,0x4f08,0x4f0b,0x4f0c,0x4f12,0x4f13,0x4f14,
+ 0x4f15,0x4f16,0x4f1c,0x4f1d,0x4f21,0x4f23,0x4f28,0x4f29,0x4f2c,0x4f2d,
+ 0x4f2e,0x4f31,0x4f33,0x4f35,0x4f37,0x4f39,0x4f3b,0x4f3e,0x4f3f,0x4f40,
+ 0x4f41,0x4f42,0x4f44,0x4f45,0x4f47,0x4f48,0x4f49,0x4f4a,0x4f4b,0x4f4c,
+ 0x4f52,0x4f54,0x4f56,0x4f61,0x4f62,0x4f66,0x4f68,0x4f6a,0x4f6b,0x4f6d,
+ 0x4f6e,0x4f71,0x4f72,0x4f75,0x4f77,0x4f78,0x4f79,0x4f7a,0x4f7d,0x4f80,
+ 0x4f81,0x4f82,0x4f85,0x4f86,0x4f87,0x4f8a,0x4f8c,0x4f8e,0x4f90,0x4f92,
+ 0x4f93,0x4f95,0x4f96,0x4f98,0x4f99,0x4f9a,0x4f9c,0x4f9e,0x4f9f,0x4fa1,
+ 0x4fa2
+ },
+ { /* ku 02 */
+ 0x4fa4,0x4fab,0x4fad,0x4fb0,0x4fb1,0x4fb2,0x4fb3,0x4fb4,0x4fb6,0x4fb7,
+ 0x4fb8,0x4fb9,0x4fba,0x4fbb,0x4fbc,0x4fbd,0x4fbe,0x4fc0,0x4fc1,0x4fc2,
+ 0x4fc6,0x4fc7,0x4fc8,0x4fc9,0x4fcb,0x4fcc,0x4fcd,0x4fd2,0x4fd3,0x4fd4,
+ 0x4fd5,0x4fd6,0x4fd9,0x4fdb,0x4fe0,0x4fe2,0x4fe4,0x4fe5,0x4fe7,0x4feb,
+ 0x4fec,0x4ff0,0x4ff2,0x4ff4,0x4ff5,0x4ff6,0x4ff7,0x4ff9,0x4ffb,0x4ffc,
+ 0x4ffd,0x4fff,0x5000,0x5001,0x5002,0x5003,0x5004,0x5005,0x5006,0x5007,
+ 0x5008,0x5009,0x500a,UBOGON,0x500b,0x500e,0x5010,0x5011,0x5013,0x5015,
+ 0x5016,0x5017,0x501b,0x501d,0x501e,0x5020,0x5022,0x5023,0x5024,0x5027,
+ 0x502b,0x502f,0x5030,0x5031,0x5032,0x5033,0x5034,0x5035,0x5036,0x5037,
+ 0x5038,0x5039,0x503b,0x503d,0x503f,0x5040,0x5041,0x5042,0x5044,0x5045,
+ 0x5046,0x5049,0x504a,0x504b,0x504d,0x5050,0x5051,0x5052,0x5053,0x5054,
+ 0x5056,0x5057,0x5058,0x5059,0x505b,0x505d,0x505e,0x505f,0x5060,0x5061,
+ 0x5062,0x5063,0x5064,0x5066,0x5067,0x5068,0x5069,0x506a,0x506b,0x506d,
+ 0x506e,0x506f,0x5070,0x5071,0x5072,0x5073,0x5074,0x5075,0x5078,0x5079,
+ 0x507a,0x507c,0x507d,0x5081,0x5082,0x5083,0x5084,0x5086,0x5087,0x5089,
+ 0x508a,0x508b,0x508c,0x508e,0x508f,0x5090,0x5091,0x5092,0x5093,0x5094,
+ 0x5095,0x5096,0x5097,0x5098,0x5099,0x509a,0x509b,0x509c,0x509d,0x509e,
+ 0x509f,0x50a0,0x50a1,0x50a2,0x50a4,0x50a6,0x50aa,0x50ab,0x50ad,0x50ae,
+ 0x50af,0x50b0,0x50b1,0x50b3,0x50b4,0x50b5,0x50b6,0x50b7,0x50b8,0x50b9,
+ 0x50bc
+ },
+ { /* ku 03 */
+ 0x50bd,0x50be,0x50bf,0x50c0,0x50c1,0x50c2,0x50c3,0x50c4,0x50c5,0x50c6,
+ 0x50c7,0x50c8,0x50c9,0x50ca,0x50cb,0x50cc,0x50cd,0x50ce,0x50d0,0x50d1,
+ 0x50d2,0x50d3,0x50d4,0x50d5,0x50d7,0x50d8,0x50d9,0x50db,0x50dc,0x50dd,
+ 0x50de,0x50df,0x50e0,0x50e1,0x50e2,0x50e3,0x50e4,0x50e5,0x50e8,0x50e9,
+ 0x50ea,0x50eb,0x50ef,0x50f0,0x50f1,0x50f2,0x50f4,0x50f6,0x50f7,0x50f8,
+ 0x50f9,0x50fa,0x50fc,0x50fd,0x50fe,0x50ff,0x5100,0x5101,0x5102,0x5103,
+ 0x5104,0x5105,0x5108,UBOGON,0x5109,0x510a,0x510c,0x510d,0x510e,0x510f,
+ 0x5110,0x5111,0x5113,0x5114,0x5115,0x5116,0x5117,0x5118,0x5119,0x511a,
+ 0x511b,0x511c,0x511d,0x511e,0x511f,0x5120,0x5122,0x5123,0x5124,0x5125,
+ 0x5126,0x5127,0x5128,0x5129,0x512a,0x512b,0x512c,0x512d,0x512e,0x512f,
+ 0x5130,0x5131,0x5132,0x5133,0x5134,0x5135,0x5136,0x5137,0x5138,0x5139,
+ 0x513a,0x513b,0x513c,0x513d,0x513e,0x5142,0x5147,0x514a,0x514c,0x514e,
+ 0x514f,0x5150,0x5152,0x5153,0x5157,0x5158,0x5159,0x515b,0x515d,0x515e,
+ 0x515f,0x5160,0x5161,0x5163,0x5164,0x5166,0x5167,0x5169,0x516a,0x516f,
+ 0x5172,0x517a,0x517e,0x517f,0x5183,0x5184,0x5186,0x5187,0x518a,0x518b,
+ 0x518e,0x518f,0x5190,0x5191,0x5193,0x5194,0x5198,0x519a,0x519d,0x519e,
+ 0x519f,0x51a1,0x51a3,0x51a6,0x51a7,0x51a8,0x51a9,0x51aa,0x51ad,0x51ae,
+ 0x51b4,0x51b8,0x51b9,0x51ba,0x51be,0x51bf,0x51c1,0x51c2,0x51c3,0x51c5,
+ 0x51c8,0x51ca,0x51cd,0x51ce,0x51d0,0x51d2,0x51d3,0x51d4,0x51d5,0x51d6,
+ 0x51d7
+ },
+ { /* ku 04 */
+ 0x51d8,0x51d9,0x51da,0x51dc,0x51de,0x51df,0x51e2,0x51e3,0x51e5,0x51e6,
+ 0x51e7,0x51e8,0x51e9,0x51ea,0x51ec,0x51ee,0x51f1,0x51f2,0x51f4,0x51f7,
+ 0x51fe,0x5204,0x5205,0x5209,0x520b,0x520c,0x520f,0x5210,0x5213,0x5214,
+ 0x5215,0x521c,0x521e,0x521f,0x5221,0x5222,0x5223,0x5225,0x5226,0x5227,
+ 0x522a,0x522c,0x522f,0x5231,0x5232,0x5234,0x5235,0x523c,0x523e,0x5244,
+ 0x5245,0x5246,0x5247,0x5248,0x5249,0x524b,0x524e,0x524f,0x5252,0x5253,
+ 0x5255,0x5257,0x5258,UBOGON,0x5259,0x525a,0x525b,0x525d,0x525f,0x5260,
+ 0x5262,0x5263,0x5264,0x5266,0x5268,0x526b,0x526c,0x526d,0x526e,0x5270,
+ 0x5271,0x5273,0x5274,0x5275,0x5276,0x5277,0x5278,0x5279,0x527a,0x527b,
+ 0x527c,0x527e,0x5280,0x5283,0x5284,0x5285,0x5286,0x5287,0x5289,0x528a,
+ 0x528b,0x528c,0x528d,0x528e,0x528f,0x5291,0x5292,0x5294,0x5295,0x5296,
+ 0x5297,0x5298,0x5299,0x529a,0x529c,0x52a4,0x52a5,0x52a6,0x52a7,0x52ae,
+ 0x52af,0x52b0,0x52b4,0x52b5,0x52b6,0x52b7,0x52b8,0x52b9,0x52ba,0x52bb,
+ 0x52bc,0x52bd,0x52c0,0x52c1,0x52c2,0x52c4,0x52c5,0x52c6,0x52c8,0x52ca,
+ 0x52cc,0x52cd,0x52ce,0x52cf,0x52d1,0x52d3,0x52d4,0x52d5,0x52d7,0x52d9,
+ 0x52da,0x52db,0x52dc,0x52dd,0x52de,0x52e0,0x52e1,0x52e2,0x52e3,0x52e5,
+ 0x52e6,0x52e7,0x52e8,0x52e9,0x52ea,0x52eb,0x52ec,0x52ed,0x52ee,0x52ef,
+ 0x52f1,0x52f2,0x52f3,0x52f4,0x52f5,0x52f6,0x52f7,0x52f8,0x52fb,0x52fc,
+ 0x52fd,0x5301,0x5302,0x5303,0x5304,0x5307,0x5309,0x530a,0x530b,0x530c,
+ 0x530e
+ },
+ { /* ku 05 */
+ 0x5311,0x5312,0x5313,0x5314,0x5318,0x531b,0x531c,0x531e,0x531f,0x5322,
+ 0x5324,0x5325,0x5327,0x5328,0x5329,0x532b,0x532c,0x532d,0x532f,0x5330,
+ 0x5331,0x5332,0x5333,0x5334,0x5335,0x5336,0x5337,0x5338,0x533c,0x533d,
+ 0x5340,0x5342,0x5344,0x5346,0x534b,0x534c,0x534d,0x5350,0x5354,0x5358,
+ 0x5359,0x535b,0x535d,0x5365,0x5368,0x536a,0x536c,0x536d,0x5372,0x5376,
+ 0x5379,0x537b,0x537c,0x537d,0x537e,0x5380,0x5381,0x5383,0x5387,0x5388,
+ 0x538a,0x538e,0x538f,UBOGON,0x5390,0x5391,0x5392,0x5393,0x5394,0x5396,
+ 0x5397,0x5399,0x539b,0x539c,0x539e,0x53a0,0x53a1,0x53a4,0x53a7,0x53aa,
+ 0x53ab,0x53ac,0x53ad,0x53af,0x53b0,0x53b1,0x53b2,0x53b3,0x53b4,0x53b5,
+ 0x53b7,0x53b8,0x53b9,0x53ba,0x53bc,0x53bd,0x53be,0x53c0,0x53c3,0x53c4,
+ 0x53c5,0x53c6,0x53c7,0x53ce,0x53cf,0x53d0,0x53d2,0x53d3,0x53d5,0x53da,
+ 0x53dc,0x53dd,0x53de,0x53e1,0x53e2,0x53e7,0x53f4,0x53fa,0x53fe,0x53ff,
+ 0x5400,0x5402,0x5405,0x5407,0x540b,0x5414,0x5418,0x5419,0x541a,0x541c,
+ 0x5422,0x5424,0x5425,0x542a,0x5430,0x5433,0x5436,0x5437,0x543a,0x543d,
+ 0x543f,0x5441,0x5442,0x5444,0x5445,0x5447,0x5449,0x544c,0x544d,0x544e,
+ 0x544f,0x5451,0x545a,0x545d,0x545e,0x545f,0x5460,0x5461,0x5463,0x5465,
+ 0x5467,0x5469,0x546a,0x546b,0x546c,0x546d,0x546e,0x546f,0x5470,0x5474,
+ 0x5479,0x547a,0x547e,0x547f,0x5481,0x5483,0x5485,0x5487,0x5488,0x5489,
+ 0x548a,0x548d,0x5491,0x5493,0x5497,0x5498,0x549c,0x549e,0x549f,0x54a0,
+ 0x54a1
+ },
+ { /* ku 06 */
+ 0x54a2,0x54a5,0x54ae,0x54b0,0x54b2,0x54b5,0x54b6,0x54b7,0x54b9,0x54ba,
+ 0x54bc,0x54be,0x54c3,0x54c5,0x54ca,0x54cb,0x54d6,0x54d8,0x54db,0x54e0,
+ 0x54e1,0x54e2,0x54e3,0x54e4,0x54eb,0x54ec,0x54ef,0x54f0,0x54f1,0x54f4,
+ 0x54f5,0x54f6,0x54f7,0x54f8,0x54f9,0x54fb,0x54fe,0x5500,0x5502,0x5503,
+ 0x5504,0x5505,0x5508,0x550a,0x550b,0x550c,0x550d,0x550e,0x5512,0x5513,
+ 0x5515,0x5516,0x5517,0x5518,0x5519,0x551a,0x551c,0x551d,0x551e,0x551f,
+ 0x5521,0x5525,0x5526,UBOGON,0x5528,0x5529,0x552b,0x552d,0x5532,0x5534,
+ 0x5535,0x5536,0x5538,0x5539,0x553a,0x553b,0x553d,0x5540,0x5542,0x5545,
+ 0x5547,0x5548,0x554b,0x554c,0x554d,0x554e,0x554f,0x5551,0x5552,0x5553,
+ 0x5554,0x5557,0x5558,0x5559,0x555a,0x555b,0x555d,0x555e,0x555f,0x5560,
+ 0x5562,0x5563,0x5568,0x5569,0x556b,0x556f,0x5570,0x5571,0x5572,0x5573,
+ 0x5574,0x5579,0x557a,0x557d,0x557f,0x5585,0x5586,0x558c,0x558d,0x558e,
+ 0x5590,0x5592,0x5593,0x5595,0x5596,0x5597,0x559a,0x559b,0x559e,0x55a0,
+ 0x55a1,0x55a2,0x55a3,0x55a4,0x55a5,0x55a6,0x55a8,0x55a9,0x55aa,0x55ab,
+ 0x55ac,0x55ad,0x55ae,0x55af,0x55b0,0x55b2,0x55b4,0x55b6,0x55b8,0x55ba,
+ 0x55bc,0x55bf,0x55c0,0x55c1,0x55c2,0x55c3,0x55c6,0x55c7,0x55c8,0x55ca,
+ 0x55cb,0x55ce,0x55cf,0x55d0,0x55d5,0x55d7,0x55d8,0x55d9,0x55da,0x55db,
+ 0x55de,0x55e0,0x55e2,0x55e7,0x55e9,0x55ed,0x55ee,0x55f0,0x55f1,0x55f4,
+ 0x55f6,0x55f8,0x55f9,0x55fa,0x55fb,0x55fc,0x55ff,0x5602,0x5603,0x5604,
+ 0x5605
+ },
+ { /* ku 07 */
+ 0x5606,0x5607,0x560a,0x560b,0x560d,0x5610,0x5611,0x5612,0x5613,0x5614,
+ 0x5615,0x5616,0x5617,0x5619,0x561a,0x561c,0x561d,0x5620,0x5621,0x5622,
+ 0x5625,0x5626,0x5628,0x5629,0x562a,0x562b,0x562e,0x562f,0x5630,0x5633,
+ 0x5635,0x5637,0x5638,0x563a,0x563c,0x563d,0x563e,0x5640,0x5641,0x5642,
+ 0x5643,0x5644,0x5645,0x5646,0x5647,0x5648,0x5649,0x564a,0x564b,0x564f,
+ 0x5650,0x5651,0x5652,0x5653,0x5655,0x5656,0x565a,0x565b,0x565d,0x565e,
+ 0x565f,0x5660,0x5661,UBOGON,0x5663,0x5665,0x5666,0x5667,0x566d,0x566e,
+ 0x566f,0x5670,0x5672,0x5673,0x5674,0x5675,0x5677,0x5678,0x5679,0x567a,
+ 0x567d,0x567e,0x567f,0x5680,0x5681,0x5682,0x5683,0x5684,0x5687,0x5688,
+ 0x5689,0x568a,0x568b,0x568c,0x568d,0x5690,0x5691,0x5692,0x5694,0x5695,
+ 0x5696,0x5697,0x5698,0x5699,0x569a,0x569b,0x569c,0x569d,0x569e,0x569f,
+ 0x56a0,0x56a1,0x56a2,0x56a4,0x56a5,0x56a6,0x56a7,0x56a8,0x56a9,0x56aa,
+ 0x56ab,0x56ac,0x56ad,0x56ae,0x56b0,0x56b1,0x56b2,0x56b3,0x56b4,0x56b5,
+ 0x56b6,0x56b8,0x56b9,0x56ba,0x56bb,0x56bd,0x56be,0x56bf,0x56c0,0x56c1,
+ 0x56c2,0x56c3,0x56c4,0x56c5,0x56c6,0x56c7,0x56c8,0x56c9,0x56cb,0x56cc,
+ 0x56cd,0x56ce,0x56cf,0x56d0,0x56d1,0x56d2,0x56d3,0x56d5,0x56d6,0x56d8,
+ 0x56d9,0x56dc,0x56e3,0x56e5,0x56e6,0x56e7,0x56e8,0x56e9,0x56ea,0x56ec,
+ 0x56ee,0x56ef,0x56f2,0x56f3,0x56f6,0x56f7,0x56f8,0x56fb,0x56fc,0x5700,
+ 0x5701,0x5702,0x5705,0x5707,0x570b,0x570c,0x570d,0x570e,0x570f,0x5710,
+ 0x5711
+ },
+ { /* ku 08 */
+ 0x5712,0x5713,0x5714,0x5715,0x5716,0x5717,0x5718,0x5719,0x571a,0x571b,
+ 0x571d,0x571e,0x5720,0x5721,0x5722,0x5724,0x5725,0x5726,0x5727,0x572b,
+ 0x5731,0x5732,0x5734,0x5735,0x5736,0x5737,0x5738,0x573c,0x573d,0x573f,
+ 0x5741,0x5743,0x5744,0x5745,0x5746,0x5748,0x5749,0x574b,0x5752,0x5753,
+ 0x5754,0x5755,0x5756,0x5758,0x5759,0x5762,0x5763,0x5765,0x5767,0x576c,
+ 0x576e,0x5770,0x5771,0x5772,0x5774,0x5775,0x5778,0x5779,0x577a,0x577d,
+ 0x577e,0x577f,0x5780,UBOGON,0x5781,0x5787,0x5788,0x5789,0x578a,0x578d,
+ 0x578e,0x578f,0x5790,0x5791,0x5794,0x5795,0x5796,0x5797,0x5798,0x5799,
+ 0x579a,0x579c,0x579d,0x579e,0x579f,0x57a5,0x57a8,0x57aa,0x57ac,0x57af,
+ 0x57b0,0x57b1,0x57b3,0x57b5,0x57b6,0x57b7,0x57b9,0x57ba,0x57bb,0x57bc,
+ 0x57bd,0x57be,0x57bf,0x57c0,0x57c1,0x57c4,0x57c5,0x57c6,0x57c7,0x57c8,
+ 0x57c9,0x57ca,0x57cc,0x57cd,0x57d0,0x57d1,0x57d3,0x57d6,0x57d7,0x57db,
+ 0x57dc,0x57de,0x57e1,0x57e2,0x57e3,0x57e5,0x57e6,0x57e7,0x57e8,0x57e9,
+ 0x57ea,0x57eb,0x57ec,0x57ee,0x57f0,0x57f1,0x57f2,0x57f3,0x57f5,0x57f6,
+ 0x57f7,0x57fb,0x57fc,0x57fe,0x57ff,0x5801,0x5803,0x5804,0x5805,0x5808,
+ 0x5809,0x580a,0x580c,0x580e,0x580f,0x5810,0x5812,0x5813,0x5814,0x5816,
+ 0x5817,0x5818,0x581a,0x581b,0x581c,0x581d,0x581f,0x5822,0x5823,0x5825,
+ 0x5826,0x5827,0x5828,0x5829,0x582b,0x582c,0x582d,0x582e,0x582f,0x5831,
+ 0x5832,0x5833,0x5834,0x5836,0x5837,0x5838,0x5839,0x583a,0x583b,0x583c,
+ 0x583d
+ },
+ { /* ku 09 */
+ 0x583e,0x583f,0x5840,0x5841,0x5842,0x5843,0x5845,0x5846,0x5847,0x5848,
+ 0x5849,0x584a,0x584b,0x584e,0x584f,0x5850,0x5852,0x5853,0x5855,0x5856,
+ 0x5857,0x5859,0x585a,0x585b,0x585c,0x585d,0x585f,0x5860,0x5861,0x5862,
+ 0x5863,0x5864,0x5866,0x5867,0x5868,0x5869,0x586a,0x586d,0x586e,0x586f,
+ 0x5870,0x5871,0x5872,0x5873,0x5874,0x5875,0x5876,0x5877,0x5878,0x5879,
+ 0x587a,0x587b,0x587c,0x587d,0x587f,0x5882,0x5884,0x5886,0x5887,0x5888,
+ 0x588a,0x588b,0x588c,UBOGON,0x588d,0x588e,0x588f,0x5890,0x5891,0x5894,
+ 0x5895,0x5896,0x5897,0x5898,0x589b,0x589c,0x589d,0x58a0,0x58a1,0x58a2,
+ 0x58a3,0x58a4,0x58a5,0x58a6,0x58a7,0x58aa,0x58ab,0x58ac,0x58ad,0x58ae,
+ 0x58af,0x58b0,0x58b1,0x58b2,0x58b3,0x58b4,0x58b5,0x58b6,0x58b7,0x58b8,
+ 0x58b9,0x58ba,0x58bb,0x58bd,0x58be,0x58bf,0x58c0,0x58c2,0x58c3,0x58c4,
+ 0x58c6,0x58c7,0x58c8,0x58c9,0x58ca,0x58cb,0x58cc,0x58cd,0x58ce,0x58cf,
+ 0x58d0,0x58d2,0x58d3,0x58d4,0x58d6,0x58d7,0x58d8,0x58d9,0x58da,0x58db,
+ 0x58dc,0x58dd,0x58de,0x58df,0x58e0,0x58e1,0x58e2,0x58e3,0x58e5,0x58e6,
+ 0x58e7,0x58e8,0x58e9,0x58ea,0x58ed,0x58ef,0x58f1,0x58f2,0x58f4,0x58f5,
+ 0x58f7,0x58f8,0x58fa,0x58fb,0x58fc,0x58fd,0x58fe,0x58ff,0x5900,0x5901,
+ 0x5903,0x5905,0x5906,0x5908,0x5909,0x590a,0x590b,0x590c,0x590e,0x5910,
+ 0x5911,0x5912,0x5913,0x5917,0x5918,0x591b,0x591d,0x591e,0x5920,0x5921,
+ 0x5922,0x5923,0x5926,0x5928,0x592c,0x5930,0x5932,0x5933,0x5935,0x5936,
+ 0x593b
+ },
+ { /* ku 0a */
+ 0x593d,0x593e,0x593f,0x5940,0x5943,0x5945,0x5946,0x594a,0x594c,0x594d,
+ 0x5950,0x5952,0x5953,0x5959,0x595b,0x595c,0x595d,0x595e,0x595f,0x5961,
+ 0x5963,0x5964,0x5966,0x5967,0x5968,0x5969,0x596a,0x596b,0x596c,0x596d,
+ 0x596e,0x596f,0x5970,0x5971,0x5972,0x5975,0x5977,0x597a,0x597b,0x597c,
+ 0x597e,0x597f,0x5980,0x5985,0x5989,0x598b,0x598c,0x598e,0x598f,0x5990,
+ 0x5991,0x5994,0x5995,0x5998,0x599a,0x599b,0x599c,0x599d,0x599f,0x59a0,
+ 0x59a1,0x59a2,0x59a6,UBOGON,0x59a7,0x59ac,0x59ad,0x59b0,0x59b1,0x59b3,
+ 0x59b4,0x59b5,0x59b6,0x59b7,0x59b8,0x59ba,0x59bc,0x59bd,0x59bf,0x59c0,
+ 0x59c1,0x59c2,0x59c3,0x59c4,0x59c5,0x59c7,0x59c8,0x59c9,0x59cc,0x59cd,
+ 0x59ce,0x59cf,0x59d5,0x59d6,0x59d9,0x59db,0x59de,0x59df,0x59e0,0x59e1,
+ 0x59e2,0x59e4,0x59e6,0x59e7,0x59e9,0x59ea,0x59eb,0x59ed,0x59ee,0x59ef,
+ 0x59f0,0x59f1,0x59f2,0x59f3,0x59f4,0x59f5,0x59f6,0x59f7,0x59f8,0x59fa,
+ 0x59fc,0x59fd,0x59fe,0x5a00,0x5a02,0x5a0a,0x5a0b,0x5a0d,0x5a0e,0x5a0f,
+ 0x5a10,0x5a12,0x5a14,0x5a15,0x5a16,0x5a17,0x5a19,0x5a1a,0x5a1b,0x5a1d,
+ 0x5a1e,0x5a21,0x5a22,0x5a24,0x5a26,0x5a27,0x5a28,0x5a2a,0x5a2b,0x5a2c,
+ 0x5a2d,0x5a2e,0x5a2f,0x5a30,0x5a33,0x5a35,0x5a37,0x5a38,0x5a39,0x5a3a,
+ 0x5a3b,0x5a3d,0x5a3e,0x5a3f,0x5a41,0x5a42,0x5a43,0x5a44,0x5a45,0x5a47,
+ 0x5a48,0x5a4b,0x5a4c,0x5a4d,0x5a4e,0x5a4f,0x5a50,0x5a51,0x5a52,0x5a53,
+ 0x5a54,0x5a56,0x5a57,0x5a58,0x5a59,0x5a5b,0x5a5c,0x5a5d,0x5a5e,0x5a5f,
+ 0x5a60
+ },
+ { /* ku 0b */
+ 0x5a61,0x5a63,0x5a64,0x5a65,0x5a66,0x5a68,0x5a69,0x5a6b,0x5a6c,0x5a6d,
+ 0x5a6e,0x5a6f,0x5a70,0x5a71,0x5a72,0x5a73,0x5a78,0x5a79,0x5a7b,0x5a7c,
+ 0x5a7d,0x5a7e,0x5a80,0x5a81,0x5a82,0x5a83,0x5a84,0x5a85,0x5a86,0x5a87,
+ 0x5a88,0x5a89,0x5a8a,0x5a8b,0x5a8c,0x5a8d,0x5a8e,0x5a8f,0x5a90,0x5a91,
+ 0x5a93,0x5a94,0x5a95,0x5a96,0x5a97,0x5a98,0x5a99,0x5a9c,0x5a9d,0x5a9e,
+ 0x5a9f,0x5aa0,0x5aa1,0x5aa2,0x5aa3,0x5aa4,0x5aa5,0x5aa6,0x5aa7,0x5aa8,
+ 0x5aa9,0x5aab,0x5aac,UBOGON,0x5aad,0x5aae,0x5aaf,0x5ab0,0x5ab1,0x5ab4,
+ 0x5ab6,0x5ab7,0x5ab9,0x5aba,0x5abb,0x5abc,0x5abd,0x5abf,0x5ac0,0x5ac3,
+ 0x5ac4,0x5ac5,0x5ac6,0x5ac7,0x5ac8,0x5aca,0x5acb,0x5acd,0x5ace,0x5acf,
+ 0x5ad0,0x5ad1,0x5ad3,0x5ad5,0x5ad7,0x5ad9,0x5ada,0x5adb,0x5add,0x5ade,
+ 0x5adf,0x5ae2,0x5ae4,0x5ae5,0x5ae7,0x5ae8,0x5aea,0x5aec,0x5aed,0x5aee,
+ 0x5aef,0x5af0,0x5af2,0x5af3,0x5af4,0x5af5,0x5af6,0x5af7,0x5af8,0x5af9,
+ 0x5afa,0x5afb,0x5afc,0x5afd,0x5afe,0x5aff,0x5b00,0x5b01,0x5b02,0x5b03,
+ 0x5b04,0x5b05,0x5b06,0x5b07,0x5b08,0x5b0a,0x5b0b,0x5b0c,0x5b0d,0x5b0e,
+ 0x5b0f,0x5b10,0x5b11,0x5b12,0x5b13,0x5b14,0x5b15,0x5b18,0x5b19,0x5b1a,
+ 0x5b1b,0x5b1c,0x5b1d,0x5b1e,0x5b1f,0x5b20,0x5b21,0x5b22,0x5b23,0x5b24,
+ 0x5b25,0x5b26,0x5b27,0x5b28,0x5b29,0x5b2a,0x5b2b,0x5b2c,0x5b2d,0x5b2e,
+ 0x5b2f,0x5b30,0x5b31,0x5b33,0x5b35,0x5b36,0x5b38,0x5b39,0x5b3a,0x5b3b,
+ 0x5b3c,0x5b3d,0x5b3e,0x5b3f,0x5b41,0x5b42,0x5b43,0x5b44,0x5b45,0x5b46,
+ 0x5b47
+ },
+ { /* ku 0c */
+ 0x5b48,0x5b49,0x5b4a,0x5b4b,0x5b4c,0x5b4d,0x5b4e,0x5b4f,0x5b52,0x5b56,
+ 0x5b5e,0x5b60,0x5b61,0x5b67,0x5b68,0x5b6b,0x5b6d,0x5b6e,0x5b6f,0x5b72,
+ 0x5b74,0x5b76,0x5b77,0x5b78,0x5b79,0x5b7b,0x5b7c,0x5b7e,0x5b7f,0x5b82,
+ 0x5b86,0x5b8a,0x5b8d,0x5b8e,0x5b90,0x5b91,0x5b92,0x5b94,0x5b96,0x5b9f,
+ 0x5ba7,0x5ba8,0x5ba9,0x5bac,0x5bad,0x5bae,0x5baf,0x5bb1,0x5bb2,0x5bb7,
+ 0x5bba,0x5bbb,0x5bbc,0x5bc0,0x5bc1,0x5bc3,0x5bc8,0x5bc9,0x5bca,0x5bcb,
+ 0x5bcd,0x5bce,0x5bcf,UBOGON,0x5bd1,0x5bd4,0x5bd5,0x5bd6,0x5bd7,0x5bd8,
+ 0x5bd9,0x5bda,0x5bdb,0x5bdc,0x5be0,0x5be2,0x5be3,0x5be6,0x5be7,0x5be9,
+ 0x5bea,0x5beb,0x5bec,0x5bed,0x5bef,0x5bf1,0x5bf2,0x5bf3,0x5bf4,0x5bf5,
+ 0x5bf6,0x5bf7,0x5bfd,0x5bfe,0x5c00,0x5c02,0x5c03,0x5c05,0x5c07,0x5c08,
+ 0x5c0b,0x5c0c,0x5c0d,0x5c0e,0x5c10,0x5c12,0x5c13,0x5c17,0x5c19,0x5c1b,
+ 0x5c1e,0x5c1f,0x5c20,0x5c21,0x5c23,0x5c26,0x5c28,0x5c29,0x5c2a,0x5c2b,
+ 0x5c2d,0x5c2e,0x5c2f,0x5c30,0x5c32,0x5c33,0x5c35,0x5c36,0x5c37,0x5c43,
+ 0x5c44,0x5c46,0x5c47,0x5c4c,0x5c4d,0x5c52,0x5c53,0x5c54,0x5c56,0x5c57,
+ 0x5c58,0x5c5a,0x5c5b,0x5c5c,0x5c5d,0x5c5f,0x5c62,0x5c64,0x5c67,0x5c68,
+ 0x5c69,0x5c6a,0x5c6b,0x5c6c,0x5c6d,0x5c70,0x5c72,0x5c73,0x5c74,0x5c75,
+ 0x5c76,0x5c77,0x5c78,0x5c7b,0x5c7c,0x5c7d,0x5c7e,0x5c80,0x5c83,0x5c84,
+ 0x5c85,0x5c86,0x5c87,0x5c89,0x5c8a,0x5c8b,0x5c8e,0x5c8f,0x5c92,0x5c93,
+ 0x5c95,0x5c9d,0x5c9e,0x5c9f,0x5ca0,0x5ca1,0x5ca4,0x5ca5,0x5ca6,0x5ca7,
+ 0x5ca8
+ },
+ { /* ku 0d */
+ 0x5caa,0x5cae,0x5caf,0x5cb0,0x5cb2,0x5cb4,0x5cb6,0x5cb9,0x5cba,0x5cbb,
+ 0x5cbc,0x5cbe,0x5cc0,0x5cc2,0x5cc3,0x5cc5,0x5cc6,0x5cc7,0x5cc8,0x5cc9,
+ 0x5cca,0x5ccc,0x5ccd,0x5cce,0x5ccf,0x5cd0,0x5cd1,0x5cd3,0x5cd4,0x5cd5,
+ 0x5cd6,0x5cd7,0x5cd8,0x5cda,0x5cdb,0x5cdc,0x5cdd,0x5cde,0x5cdf,0x5ce0,
+ 0x5ce2,0x5ce3,0x5ce7,0x5ce9,0x5ceb,0x5cec,0x5cee,0x5cef,0x5cf1,0x5cf2,
+ 0x5cf3,0x5cf4,0x5cf5,0x5cf6,0x5cf7,0x5cf8,0x5cf9,0x5cfa,0x5cfc,0x5cfd,
+ 0x5cfe,0x5cff,0x5d00,UBOGON,0x5d01,0x5d04,0x5d05,0x5d08,0x5d09,0x5d0a,
+ 0x5d0b,0x5d0c,0x5d0d,0x5d0f,0x5d10,0x5d11,0x5d12,0x5d13,0x5d15,0x5d17,
+ 0x5d18,0x5d19,0x5d1a,0x5d1c,0x5d1d,0x5d1f,0x5d20,0x5d21,0x5d22,0x5d23,
+ 0x5d25,0x5d28,0x5d2a,0x5d2b,0x5d2c,0x5d2f,0x5d30,0x5d31,0x5d32,0x5d33,
+ 0x5d35,0x5d36,0x5d37,0x5d38,0x5d39,0x5d3a,0x5d3b,0x5d3c,0x5d3f,0x5d40,
+ 0x5d41,0x5d42,0x5d43,0x5d44,0x5d45,0x5d46,0x5d48,0x5d49,0x5d4d,0x5d4e,
+ 0x5d4f,0x5d50,0x5d51,0x5d52,0x5d53,0x5d54,0x5d55,0x5d56,0x5d57,0x5d59,
+ 0x5d5a,0x5d5c,0x5d5e,0x5d5f,0x5d60,0x5d61,0x5d62,0x5d63,0x5d64,0x5d65,
+ 0x5d66,0x5d67,0x5d68,0x5d6a,0x5d6d,0x5d6e,0x5d70,0x5d71,0x5d72,0x5d73,
+ 0x5d75,0x5d76,0x5d77,0x5d78,0x5d79,0x5d7a,0x5d7b,0x5d7c,0x5d7d,0x5d7e,
+ 0x5d7f,0x5d80,0x5d81,0x5d83,0x5d84,0x5d85,0x5d86,0x5d87,0x5d88,0x5d89,
+ 0x5d8a,0x5d8b,0x5d8c,0x5d8d,0x5d8e,0x5d8f,0x5d90,0x5d91,0x5d92,0x5d93,
+ 0x5d94,0x5d95,0x5d96,0x5d97,0x5d98,0x5d9a,0x5d9b,0x5d9c,0x5d9e,0x5d9f,
+ 0x5da0
+ },
+ { /* ku 0e */
+ 0x5da1,0x5da2,0x5da3,0x5da4,0x5da5,0x5da6,0x5da7,0x5da8,0x5da9,0x5daa,
+ 0x5dab,0x5dac,0x5dad,0x5dae,0x5daf,0x5db0,0x5db1,0x5db2,0x5db3,0x5db4,
+ 0x5db5,0x5db6,0x5db8,0x5db9,0x5dba,0x5dbb,0x5dbc,0x5dbd,0x5dbe,0x5dbf,
+ 0x5dc0,0x5dc1,0x5dc2,0x5dc3,0x5dc4,0x5dc6,0x5dc7,0x5dc8,0x5dc9,0x5dca,
+ 0x5dcb,0x5dcc,0x5dce,0x5dcf,0x5dd0,0x5dd1,0x5dd2,0x5dd3,0x5dd4,0x5dd5,
+ 0x5dd6,0x5dd7,0x5dd8,0x5dd9,0x5dda,0x5ddc,0x5ddf,0x5de0,0x5de3,0x5de4,
+ 0x5dea,0x5dec,0x5ded,UBOGON,0x5df0,0x5df5,0x5df6,0x5df8,0x5df9,0x5dfa,
+ 0x5dfb,0x5dfc,0x5dff,0x5e00,0x5e04,0x5e07,0x5e09,0x5e0a,0x5e0b,0x5e0d,
+ 0x5e0e,0x5e12,0x5e13,0x5e17,0x5e1e,0x5e1f,0x5e20,0x5e21,0x5e22,0x5e23,
+ 0x5e24,0x5e25,0x5e28,0x5e29,0x5e2a,0x5e2b,0x5e2c,0x5e2f,0x5e30,0x5e32,
+ 0x5e33,0x5e34,0x5e35,0x5e36,0x5e39,0x5e3a,0x5e3e,0x5e3f,0x5e40,0x5e41,
+ 0x5e43,0x5e46,0x5e47,0x5e48,0x5e49,0x5e4a,0x5e4b,0x5e4d,0x5e4e,0x5e4f,
+ 0x5e50,0x5e51,0x5e52,0x5e53,0x5e56,0x5e57,0x5e58,0x5e59,0x5e5a,0x5e5c,
+ 0x5e5d,0x5e5f,0x5e60,0x5e63,0x5e64,0x5e65,0x5e66,0x5e67,0x5e68,0x5e69,
+ 0x5e6a,0x5e6b,0x5e6c,0x5e6d,0x5e6e,0x5e6f,0x5e70,0x5e71,0x5e75,0x5e77,
+ 0x5e79,0x5e7e,0x5e81,0x5e82,0x5e83,0x5e85,0x5e88,0x5e89,0x5e8c,0x5e8d,
+ 0x5e8e,0x5e92,0x5e98,0x5e9b,0x5e9d,0x5ea1,0x5ea2,0x5ea3,0x5ea4,0x5ea8,
+ 0x5ea9,0x5eaa,0x5eab,0x5eac,0x5eae,0x5eaf,0x5eb0,0x5eb1,0x5eb2,0x5eb4,
+ 0x5eba,0x5ebb,0x5ebc,0x5ebd,0x5ebf,0x5ec0,0x5ec1,0x5ec2,0x5ec3,0x5ec4,
+ 0x5ec5
+ },
+ { /* ku 0f */
+ 0x5ec6,0x5ec7,0x5ec8,0x5ecb,0x5ecc,0x5ecd,0x5ece,0x5ecf,0x5ed0,0x5ed4,
+ 0x5ed5,0x5ed7,0x5ed8,0x5ed9,0x5eda,0x5edc,0x5edd,0x5ede,0x5edf,0x5ee0,
+ 0x5ee1,0x5ee2,0x5ee3,0x5ee4,0x5ee5,0x5ee6,0x5ee7,0x5ee9,0x5eeb,0x5eec,
+ 0x5eed,0x5eee,0x5eef,0x5ef0,0x5ef1,0x5ef2,0x5ef3,0x5ef5,0x5ef8,0x5ef9,
+ 0x5efb,0x5efc,0x5efd,0x5f05,0x5f06,0x5f07,0x5f09,0x5f0c,0x5f0d,0x5f0e,
+ 0x5f10,0x5f12,0x5f14,0x5f16,0x5f19,0x5f1a,0x5f1c,0x5f1d,0x5f1e,0x5f21,
+ 0x5f22,0x5f23,0x5f24,UBOGON,0x5f28,0x5f2b,0x5f2c,0x5f2e,0x5f30,0x5f32,
+ 0x5f33,0x5f34,0x5f35,0x5f36,0x5f37,0x5f38,0x5f3b,0x5f3d,0x5f3e,0x5f3f,
+ 0x5f41,0x5f42,0x5f43,0x5f44,0x5f45,0x5f46,0x5f47,0x5f48,0x5f49,0x5f4a,
+ 0x5f4b,0x5f4c,0x5f4d,0x5f4e,0x5f4f,0x5f51,0x5f54,0x5f59,0x5f5a,0x5f5b,
+ 0x5f5c,0x5f5e,0x5f5f,0x5f60,0x5f63,0x5f65,0x5f67,0x5f68,0x5f6b,0x5f6e,
+ 0x5f6f,0x5f72,0x5f74,0x5f75,0x5f76,0x5f78,0x5f7a,0x5f7d,0x5f7e,0x5f7f,
+ 0x5f83,0x5f86,0x5f8d,0x5f8e,0x5f8f,0x5f91,0x5f93,0x5f94,0x5f96,0x5f9a,
+ 0x5f9b,0x5f9d,0x5f9e,0x5f9f,0x5fa0,0x5fa2,0x5fa3,0x5fa4,0x5fa5,0x5fa6,
+ 0x5fa7,0x5fa9,0x5fab,0x5fac,0x5faf,0x5fb0,0x5fb1,0x5fb2,0x5fb3,0x5fb4,
+ 0x5fb6,0x5fb8,0x5fb9,0x5fba,0x5fbb,0x5fbe,0x5fbf,0x5fc0,0x5fc1,0x5fc2,
+ 0x5fc7,0x5fc8,0x5fca,0x5fcb,0x5fce,0x5fd3,0x5fd4,0x5fd5,0x5fda,0x5fdb,
+ 0x5fdc,0x5fde,0x5fdf,0x5fe2,0x5fe3,0x5fe5,0x5fe6,0x5fe8,0x5fe9,0x5fec,
+ 0x5fef,0x5ff0,0x5ff2,0x5ff3,0x5ff4,0x5ff6,0x5ff7,0x5ff9,0x5ffa,0x5ffc,
+ 0x6007
+ },
+ { /* ku 10 */
+ 0x6008,0x6009,0x600b,0x600c,0x6010,0x6011,0x6013,0x6017,0x6018,0x601a,
+ 0x601e,0x601f,0x6022,0x6023,0x6024,0x602c,0x602d,0x602e,0x6030,0x6031,
+ 0x6032,0x6033,0x6034,0x6036,0x6037,0x6038,0x6039,0x603a,0x603d,0x603e,
+ 0x6040,0x6044,0x6045,0x6046,0x6047,0x6048,0x6049,0x604a,0x604c,0x604e,
+ 0x604f,0x6051,0x6053,0x6054,0x6056,0x6057,0x6058,0x605b,0x605c,0x605e,
+ 0x605f,0x6060,0x6061,0x6065,0x6066,0x606e,0x6071,0x6072,0x6074,0x6075,
+ 0x6077,0x607e,0x6080,UBOGON,0x6081,0x6082,0x6085,0x6086,0x6087,0x6088,
+ 0x608a,0x608b,0x608e,0x608f,0x6090,0x6091,0x6093,0x6095,0x6097,0x6098,
+ 0x6099,0x609c,0x609e,0x60a1,0x60a2,0x60a4,0x60a5,0x60a7,0x60a9,0x60aa,
+ 0x60ae,0x60b0,0x60b3,0x60b5,0x60b6,0x60b7,0x60b9,0x60ba,0x60bd,0x60be,
+ 0x60bf,0x60c0,0x60c1,0x60c2,0x60c3,0x60c4,0x60c7,0x60c8,0x60c9,0x60cc,
+ 0x60cd,0x60ce,0x60cf,0x60d0,0x60d2,0x60d3,0x60d4,0x60d6,0x60d7,0x60d9,
+ 0x60db,0x60de,0x60e1,0x60e2,0x60e3,0x60e4,0x60e5,0x60ea,0x60f1,0x60f2,
+ 0x60f5,0x60f7,0x60f8,0x60fb,0x60fc,0x60fd,0x60fe,0x60ff,0x6102,0x6103,
+ 0x6104,0x6105,0x6107,0x610a,0x610b,0x610c,0x6110,0x6111,0x6112,0x6113,
+ 0x6114,0x6116,0x6117,0x6118,0x6119,0x611b,0x611c,0x611d,0x611e,0x6121,
+ 0x6122,0x6125,0x6128,0x6129,0x612a,0x612c,0x612d,0x612e,0x612f,0x6130,
+ 0x6131,0x6132,0x6133,0x6134,0x6135,0x6136,0x6137,0x6138,0x6139,0x613a,
+ 0x613b,0x613c,0x613d,0x613e,0x6140,0x6141,0x6142,0x6143,0x6144,0x6145,
+ 0x6146
+ },
+ { /* ku 11 */
+ 0x6147,0x6149,0x614b,0x614d,0x614f,0x6150,0x6152,0x6153,0x6154,0x6156,
+ 0x6157,0x6158,0x6159,0x615a,0x615b,0x615c,0x615e,0x615f,0x6160,0x6161,
+ 0x6163,0x6164,0x6165,0x6166,0x6169,0x616a,0x616b,0x616c,0x616d,0x616e,
+ 0x616f,0x6171,0x6172,0x6173,0x6174,0x6176,0x6178,0x6179,0x617a,0x617b,
+ 0x617c,0x617d,0x617e,0x617f,0x6180,0x6181,0x6182,0x6183,0x6184,0x6185,
+ 0x6186,0x6187,0x6188,0x6189,0x618a,0x618c,0x618d,0x618f,0x6190,0x6191,
+ 0x6192,0x6193,0x6195,UBOGON,0x6196,0x6197,0x6198,0x6199,0x619a,0x619b,
+ 0x619c,0x619e,0x619f,0x61a0,0x61a1,0x61a2,0x61a3,0x61a4,0x61a5,0x61a6,
+ 0x61aa,0x61ab,0x61ad,0x61ae,0x61af,0x61b0,0x61b1,0x61b2,0x61b3,0x61b4,
+ 0x61b5,0x61b6,0x61b8,0x61b9,0x61ba,0x61bb,0x61bc,0x61bd,0x61bf,0x61c0,
+ 0x61c1,0x61c3,0x61c4,0x61c5,0x61c6,0x61c7,0x61c9,0x61cc,0x61cd,0x61ce,
+ 0x61cf,0x61d0,0x61d3,0x61d5,0x61d6,0x61d7,0x61d8,0x61d9,0x61da,0x61db,
+ 0x61dc,0x61dd,0x61de,0x61df,0x61e0,0x61e1,0x61e2,0x61e3,0x61e4,0x61e5,
+ 0x61e7,0x61e8,0x61e9,0x61ea,0x61eb,0x61ec,0x61ed,0x61ee,0x61ef,0x61f0,
+ 0x61f1,0x61f2,0x61f3,0x61f4,0x61f6,0x61f7,0x61f8,0x61f9,0x61fa,0x61fb,
+ 0x61fc,0x61fd,0x61fe,0x6200,0x6201,0x6202,0x6203,0x6204,0x6205,0x6207,
+ 0x6209,0x6213,0x6214,0x6219,0x621c,0x621d,0x621e,0x6220,0x6223,0x6226,
+ 0x6227,0x6228,0x6229,0x622b,0x622d,0x622f,0x6230,0x6231,0x6232,0x6235,
+ 0x6236,0x6238,0x6239,0x623a,0x623b,0x623c,0x6242,0x6244,0x6245,0x6246,
+ 0x624a
+ },
+ { /* ku 12 */
+ 0x624f,0x6250,0x6255,0x6256,0x6257,0x6259,0x625a,0x625c,0x625d,0x625e,
+ 0x625f,0x6260,0x6261,0x6262,0x6264,0x6265,0x6268,0x6271,0x6272,0x6274,
+ 0x6275,0x6277,0x6278,0x627a,0x627b,0x627d,0x6281,0x6282,0x6283,0x6285,
+ 0x6286,0x6287,0x6288,0x628b,0x628c,0x628d,0x628e,0x628f,0x6290,0x6294,
+ 0x6299,0x629c,0x629d,0x629e,0x62a3,0x62a6,0x62a7,0x62a9,0x62aa,0x62ad,
+ 0x62ae,0x62af,0x62b0,0x62b2,0x62b3,0x62b4,0x62b6,0x62b7,0x62b8,0x62ba,
+ 0x62be,0x62c0,0x62c1,UBOGON,0x62c3,0x62cb,0x62cf,0x62d1,0x62d5,0x62dd,
+ 0x62de,0x62e0,0x62e1,0x62e4,0x62ea,0x62eb,0x62f0,0x62f2,0x62f5,0x62f8,
+ 0x62f9,0x62fa,0x62fb,0x6300,0x6303,0x6304,0x6305,0x6306,0x630a,0x630b,
+ 0x630c,0x630d,0x630f,0x6310,0x6312,0x6313,0x6314,0x6315,0x6317,0x6318,
+ 0x6319,0x631c,0x6326,0x6327,0x6329,0x632c,0x632d,0x632e,0x6330,0x6331,
+ 0x6333,0x6334,0x6335,0x6336,0x6337,0x6338,0x633b,0x633c,0x633e,0x633f,
+ 0x6340,0x6341,0x6344,0x6347,0x6348,0x634a,0x6351,0x6352,0x6353,0x6354,
+ 0x6356,0x6357,0x6358,0x6359,0x635a,0x635b,0x635c,0x635d,0x6360,0x6364,
+ 0x6365,0x6366,0x6368,0x636a,0x636b,0x636c,0x636f,0x6370,0x6372,0x6373,
+ 0x6374,0x6375,0x6378,0x6379,0x637c,0x637d,0x637e,0x637f,0x6381,0x6383,
+ 0x6384,0x6385,0x6386,0x638b,0x638d,0x6391,0x6393,0x6394,0x6395,0x6397,
+ 0x6399,0x639a,0x639b,0x639c,0x639d,0x639e,0x639f,0x63a1,0x63a4,0x63a6,
+ 0x63ab,0x63af,0x63b1,0x63b2,0x63b5,0x63b6,0x63b9,0x63bb,0x63bd,0x63bf,
+ 0x63c0
+ },
+ { /* ku 13 */
+ 0x63c1,0x63c2,0x63c3,0x63c5,0x63c7,0x63c8,0x63ca,0x63cb,0x63cc,0x63d1,
+ 0x63d3,0x63d4,0x63d5,0x63d7,0x63d8,0x63d9,0x63da,0x63db,0x63dc,0x63dd,
+ 0x63df,0x63e2,0x63e4,0x63e5,0x63e6,0x63e7,0x63e8,0x63eb,0x63ec,0x63ee,
+ 0x63ef,0x63f0,0x63f1,0x63f3,0x63f5,0x63f7,0x63f9,0x63fa,0x63fb,0x63fc,
+ 0x63fe,0x6403,0x6404,0x6406,0x6407,0x6408,0x6409,0x640a,0x640d,0x640e,
+ 0x6411,0x6412,0x6415,0x6416,0x6417,0x6418,0x6419,0x641a,0x641d,0x641f,
+ 0x6422,0x6423,0x6424,UBOGON,0x6425,0x6427,0x6428,0x6429,0x642b,0x642e,
+ 0x642f,0x6430,0x6431,0x6432,0x6433,0x6435,0x6436,0x6437,0x6438,0x6439,
+ 0x643b,0x643c,0x643e,0x6440,0x6442,0x6443,0x6449,0x644b,0x644c,0x644d,
+ 0x644e,0x644f,0x6450,0x6451,0x6453,0x6455,0x6456,0x6457,0x6459,0x645a,
+ 0x645b,0x645c,0x645d,0x645f,0x6460,0x6461,0x6462,0x6463,0x6464,0x6465,
+ 0x6466,0x6468,0x646a,0x646b,0x646c,0x646e,0x646f,0x6470,0x6471,0x6472,
+ 0x6473,0x6474,0x6475,0x6476,0x6477,0x647b,0x647c,0x647d,0x647e,0x647f,
+ 0x6480,0x6481,0x6483,0x6486,0x6488,0x6489,0x648a,0x648b,0x648c,0x648d,
+ 0x648e,0x648f,0x6490,0x6493,0x6494,0x6497,0x6498,0x649a,0x649b,0x649c,
+ 0x649d,0x649f,0x64a0,0x64a1,0x64a2,0x64a3,0x64a5,0x64a6,0x64a7,0x64a8,
+ 0x64aa,0x64ab,0x64af,0x64b1,0x64b2,0x64b3,0x64b4,0x64b6,0x64b9,0x64bb,
+ 0x64bd,0x64be,0x64bf,0x64c1,0x64c3,0x64c4,0x64c6,0x64c7,0x64c8,0x64c9,
+ 0x64ca,0x64cb,0x64cc,0x64cf,0x64d1,0x64d3,0x64d4,0x64d5,0x64d6,0x64d9,
+ 0x64da
+ },
+ { /* ku 14 */
+ 0x64db,0x64dc,0x64dd,0x64df,0x64e0,0x64e1,0x64e3,0x64e5,0x64e7,0x64e8,
+ 0x64e9,0x64ea,0x64eb,0x64ec,0x64ed,0x64ee,0x64ef,0x64f0,0x64f1,0x64f2,
+ 0x64f3,0x64f4,0x64f5,0x64f6,0x64f7,0x64f8,0x64f9,0x64fa,0x64fb,0x64fc,
+ 0x64fd,0x64fe,0x64ff,0x6501,0x6502,0x6503,0x6504,0x6505,0x6506,0x6507,
+ 0x6508,0x650a,0x650b,0x650c,0x650d,0x650e,0x650f,0x6510,0x6511,0x6513,
+ 0x6514,0x6515,0x6516,0x6517,0x6519,0x651a,0x651b,0x651c,0x651d,0x651e,
+ 0x651f,0x6520,0x6521,UBOGON,0x6522,0x6523,0x6524,0x6526,0x6527,0x6528,
+ 0x6529,0x652a,0x652c,0x652d,0x6530,0x6531,0x6532,0x6533,0x6537,0x653a,
+ 0x653c,0x653d,0x6540,0x6541,0x6542,0x6543,0x6544,0x6546,0x6547,0x654a,
+ 0x654b,0x654d,0x654e,0x6550,0x6552,0x6553,0x6554,0x6557,0x6558,0x655a,
+ 0x655c,0x655f,0x6560,0x6561,0x6564,0x6565,0x6567,0x6568,0x6569,0x656a,
+ 0x656d,0x656e,0x656f,0x6571,0x6573,0x6575,0x6576,0x6578,0x6579,0x657a,
+ 0x657b,0x657c,0x657d,0x657e,0x657f,0x6580,0x6581,0x6582,0x6583,0x6584,
+ 0x6585,0x6586,0x6588,0x6589,0x658a,0x658d,0x658e,0x658f,0x6592,0x6594,
+ 0x6595,0x6596,0x6598,0x659a,0x659d,0x659e,0x65a0,0x65a2,0x65a3,0x65a6,
+ 0x65a8,0x65aa,0x65ac,0x65ae,0x65b1,0x65b2,0x65b3,0x65b4,0x65b5,0x65b6,
+ 0x65b7,0x65b8,0x65ba,0x65bb,0x65be,0x65bf,0x65c0,0x65c2,0x65c7,0x65c8,
+ 0x65c9,0x65ca,0x65cd,0x65d0,0x65d1,0x65d3,0x65d4,0x65d5,0x65d8,0x65d9,
+ 0x65da,0x65db,0x65dc,0x65dd,0x65de,0x65df,0x65e1,0x65e3,0x65e4,0x65ea,
+ 0x65eb
+ },
+ { /* ku 15 */
+ 0x65f2,0x65f3,0x65f4,0x65f5,0x65f8,0x65f9,0x65fb,0x65fc,0x65fd,0x65fe,
+ 0x65ff,0x6601,0x6604,0x6605,0x6607,0x6608,0x6609,0x660b,0x660d,0x6610,
+ 0x6611,0x6612,0x6616,0x6617,0x6618,0x661a,0x661b,0x661c,0x661e,0x6621,
+ 0x6622,0x6623,0x6624,0x6626,0x6629,0x662a,0x662b,0x662c,0x662e,0x6630,
+ 0x6632,0x6633,0x6637,0x6638,0x6639,0x663a,0x663b,0x663d,0x663f,0x6640,
+ 0x6642,0x6644,0x6645,0x6646,0x6647,0x6648,0x6649,0x664a,0x664d,0x664e,
+ 0x6650,0x6651,0x6658,UBOGON,0x6659,0x665b,0x665c,0x665d,0x665e,0x6660,
+ 0x6662,0x6663,0x6665,0x6667,0x6669,0x666a,0x666b,0x666c,0x666d,0x6671,
+ 0x6672,0x6673,0x6675,0x6678,0x6679,0x667b,0x667c,0x667d,0x667f,0x6680,
+ 0x6681,0x6683,0x6685,0x6686,0x6688,0x6689,0x668a,0x668b,0x668d,0x668e,
+ 0x668f,0x6690,0x6692,0x6693,0x6694,0x6695,0x6698,0x6699,0x669a,0x669b,
+ 0x669c,0x669e,0x669f,0x66a0,0x66a1,0x66a2,0x66a3,0x66a4,0x66a5,0x66a6,
+ 0x66a9,0x66aa,0x66ab,0x66ac,0x66ad,0x66af,0x66b0,0x66b1,0x66b2,0x66b3,
+ 0x66b5,0x66b6,0x66b7,0x66b8,0x66ba,0x66bb,0x66bc,0x66bd,0x66bf,0x66c0,
+ 0x66c1,0x66c2,0x66c3,0x66c4,0x66c5,0x66c6,0x66c7,0x66c8,0x66c9,0x66ca,
+ 0x66cb,0x66cc,0x66cd,0x66ce,0x66cf,0x66d0,0x66d1,0x66d2,0x66d3,0x66d4,
+ 0x66d5,0x66d6,0x66d7,0x66d8,0x66da,0x66de,0x66df,0x66e0,0x66e1,0x66e2,
+ 0x66e3,0x66e4,0x66e5,0x66e7,0x66e8,0x66ea,0x66eb,0x66ec,0x66ed,0x66ee,
+ 0x66ef,0x66f1,0x66f5,0x66f6,0x66f8,0x66fa,0x66fb,0x66fd,0x6701,0x6702,
+ 0x6703
+ },
+ { /* ku 16 */
+ 0x6704,0x6705,0x6706,0x6707,0x670c,0x670e,0x670f,0x6711,0x6712,0x6713,
+ 0x6716,0x6718,0x6719,0x671a,0x671c,0x671e,0x6720,0x6721,0x6722,0x6723,
+ 0x6724,0x6725,0x6727,0x6729,0x672e,0x6730,0x6732,0x6733,0x6736,0x6737,
+ 0x6738,0x6739,0x673b,0x673c,0x673e,0x673f,0x6741,0x6744,0x6745,0x6747,
+ 0x674a,0x674b,0x674d,0x6752,0x6754,0x6755,0x6757,0x6758,0x6759,0x675a,
+ 0x675b,0x675d,0x6762,0x6763,0x6764,0x6766,0x6767,0x676b,0x676c,0x676e,
+ 0x6771,0x6774,0x6776,UBOGON,0x6778,0x6779,0x677a,0x677b,0x677d,0x6780,
+ 0x6782,0x6783,0x6785,0x6786,0x6788,0x678a,0x678c,0x678d,0x678e,0x678f,
+ 0x6791,0x6792,0x6793,0x6794,0x6796,0x6799,0x679b,0x679f,0x67a0,0x67a1,
+ 0x67a4,0x67a6,0x67a9,0x67ac,0x67ae,0x67b1,0x67b2,0x67b4,0x67b9,0x67ba,
+ 0x67bb,0x67bc,0x67bd,0x67be,0x67bf,0x67c0,0x67c2,0x67c5,0x67c6,0x67c7,
+ 0x67c8,0x67c9,0x67ca,0x67cb,0x67cc,0x67cd,0x67ce,0x67d5,0x67d6,0x67d7,
+ 0x67db,0x67df,0x67e1,0x67e3,0x67e4,0x67e6,0x67e7,0x67e8,0x67ea,0x67eb,
+ 0x67ed,0x67ee,0x67f2,0x67f5,0x67f6,0x67f7,0x67f8,0x67f9,0x67fa,0x67fb,
+ 0x67fc,0x67fe,0x6801,0x6802,0x6803,0x6804,0x6806,0x680d,0x6810,0x6812,
+ 0x6814,0x6815,0x6818,0x6819,0x681a,0x681b,0x681c,0x681e,0x681f,0x6820,
+ 0x6822,0x6823,0x6824,0x6825,0x6826,0x6827,0x6828,0x682b,0x682c,0x682d,
+ 0x682e,0x682f,0x6830,0x6831,0x6834,0x6835,0x6836,0x683a,0x683b,0x683f,
+ 0x6847,0x684b,0x684d,0x684f,0x6852,0x6856,0x6857,0x6858,0x6859,0x685a,
+ 0x685b
+ },
+ { /* ku 17 */
+ 0x685c,0x685d,0x685e,0x685f,0x686a,0x686c,0x686d,0x686e,0x686f,0x6870,
+ 0x6871,0x6872,0x6873,0x6875,0x6878,0x6879,0x687a,0x687b,0x687c,0x687d,
+ 0x687e,0x687f,0x6880,0x6882,0x6884,0x6887,0x6888,0x6889,0x688a,0x688b,
+ 0x688c,0x688d,0x688e,0x6890,0x6891,0x6892,0x6894,0x6895,0x6896,0x6898,
+ 0x6899,0x689a,0x689b,0x689c,0x689d,0x689e,0x689f,0x68a0,0x68a1,0x68a3,
+ 0x68a4,0x68a5,0x68a9,0x68aa,0x68ab,0x68ac,0x68ae,0x68b1,0x68b2,0x68b4,
+ 0x68b6,0x68b7,0x68b8,UBOGON,0x68b9,0x68ba,0x68bb,0x68bc,0x68bd,0x68be,
+ 0x68bf,0x68c1,0x68c3,0x68c4,0x68c5,0x68c6,0x68c7,0x68c8,0x68ca,0x68cc,
+ 0x68ce,0x68cf,0x68d0,0x68d1,0x68d3,0x68d4,0x68d6,0x68d7,0x68d9,0x68db,
+ 0x68dc,0x68dd,0x68de,0x68df,0x68e1,0x68e2,0x68e4,0x68e5,0x68e6,0x68e7,
+ 0x68e8,0x68e9,0x68ea,0x68eb,0x68ec,0x68ed,0x68ef,0x68f2,0x68f3,0x68f4,
+ 0x68f6,0x68f7,0x68f8,0x68fb,0x68fd,0x68fe,0x68ff,0x6900,0x6902,0x6903,
+ 0x6904,0x6906,0x6907,0x6908,0x6909,0x690a,0x690c,0x690f,0x6911,0x6913,
+ 0x6914,0x6915,0x6916,0x6917,0x6918,0x6919,0x691a,0x691b,0x691c,0x691d,
+ 0x691e,0x6921,0x6922,0x6923,0x6925,0x6926,0x6927,0x6928,0x6929,0x692a,
+ 0x692b,0x692c,0x692e,0x692f,0x6931,0x6932,0x6933,0x6935,0x6936,0x6937,
+ 0x6938,0x693a,0x693b,0x693c,0x693e,0x6940,0x6941,0x6943,0x6944,0x6945,
+ 0x6946,0x6947,0x6948,0x6949,0x694a,0x694b,0x694c,0x694d,0x694e,0x694f,
+ 0x6950,0x6951,0x6952,0x6953,0x6955,0x6956,0x6958,0x6959,0x695b,0x695c,
+ 0x695f
+ },
+ { /* ku 18 */
+ 0x6961,0x6962,0x6964,0x6965,0x6967,0x6968,0x6969,0x696a,0x696c,0x696d,
+ 0x696f,0x6970,0x6972,0x6973,0x6974,0x6975,0x6976,0x697a,0x697b,0x697d,
+ 0x697e,0x697f,0x6981,0x6983,0x6985,0x698a,0x698b,0x698c,0x698e,0x698f,
+ 0x6990,0x6991,0x6992,0x6993,0x6996,0x6997,0x6999,0x699a,0x699d,0x699e,
+ 0x699f,0x69a0,0x69a1,0x69a2,0x69a3,0x69a4,0x69a5,0x69a6,0x69a9,0x69aa,
+ 0x69ac,0x69ae,0x69af,0x69b0,0x69b2,0x69b3,0x69b5,0x69b6,0x69b8,0x69b9,
+ 0x69ba,0x69bc,0x69bd,UBOGON,0x69be,0x69bf,0x69c0,0x69c2,0x69c3,0x69c4,
+ 0x69c5,0x69c6,0x69c7,0x69c8,0x69c9,0x69cb,0x69cd,0x69cf,0x69d1,0x69d2,
+ 0x69d3,0x69d5,0x69d6,0x69d7,0x69d8,0x69d9,0x69da,0x69dc,0x69dd,0x69de,
+ 0x69e1,0x69e2,0x69e3,0x69e4,0x69e5,0x69e6,0x69e7,0x69e8,0x69e9,0x69ea,
+ 0x69eb,0x69ec,0x69ee,0x69ef,0x69f0,0x69f1,0x69f3,0x69f4,0x69f5,0x69f6,
+ 0x69f7,0x69f8,0x69f9,0x69fa,0x69fb,0x69fc,0x69fe,0x6a00,0x6a01,0x6a02,
+ 0x6a03,0x6a04,0x6a05,0x6a06,0x6a07,0x6a08,0x6a09,0x6a0b,0x6a0c,0x6a0d,
+ 0x6a0e,0x6a0f,0x6a10,0x6a11,0x6a12,0x6a13,0x6a14,0x6a15,0x6a16,0x6a19,
+ 0x6a1a,0x6a1b,0x6a1c,0x6a1d,0x6a1e,0x6a20,0x6a22,0x6a23,0x6a24,0x6a25,
+ 0x6a26,0x6a27,0x6a29,0x6a2b,0x6a2c,0x6a2d,0x6a2e,0x6a30,0x6a32,0x6a33,
+ 0x6a34,0x6a36,0x6a37,0x6a38,0x6a39,0x6a3a,0x6a3b,0x6a3c,0x6a3f,0x6a40,
+ 0x6a41,0x6a42,0x6a43,0x6a45,0x6a46,0x6a48,0x6a49,0x6a4a,0x6a4b,0x6a4c,
+ 0x6a4d,0x6a4e,0x6a4f,0x6a51,0x6a52,0x6a53,0x6a54,0x6a55,0x6a56,0x6a57,
+ 0x6a5a
+ },
+ { /* ku 19 */
+ 0x6a5c,0x6a5d,0x6a5e,0x6a5f,0x6a60,0x6a62,0x6a63,0x6a64,0x6a66,0x6a67,
+ 0x6a68,0x6a69,0x6a6a,0x6a6b,0x6a6c,0x6a6d,0x6a6e,0x6a6f,0x6a70,0x6a72,
+ 0x6a73,0x6a74,0x6a75,0x6a76,0x6a77,0x6a78,0x6a7a,0x6a7b,0x6a7d,0x6a7e,
+ 0x6a7f,0x6a81,0x6a82,0x6a83,0x6a85,0x6a86,0x6a87,0x6a88,0x6a89,0x6a8a,
+ 0x6a8b,0x6a8c,0x6a8d,0x6a8f,0x6a92,0x6a93,0x6a94,0x6a95,0x6a96,0x6a98,
+ 0x6a99,0x6a9a,0x6a9b,0x6a9c,0x6a9d,0x6a9e,0x6a9f,0x6aa1,0x6aa2,0x6aa3,
+ 0x6aa4,0x6aa5,0x6aa6,UBOGON,0x6aa7,0x6aa8,0x6aaa,0x6aad,0x6aae,0x6aaf,
+ 0x6ab0,0x6ab1,0x6ab2,0x6ab3,0x6ab4,0x6ab5,0x6ab6,0x6ab7,0x6ab8,0x6ab9,
+ 0x6aba,0x6abb,0x6abc,0x6abd,0x6abe,0x6abf,0x6ac0,0x6ac1,0x6ac2,0x6ac3,
+ 0x6ac4,0x6ac5,0x6ac6,0x6ac7,0x6ac8,0x6ac9,0x6aca,0x6acb,0x6acc,0x6acd,
+ 0x6ace,0x6acf,0x6ad0,0x6ad1,0x6ad2,0x6ad3,0x6ad4,0x6ad5,0x6ad6,0x6ad7,
+ 0x6ad8,0x6ad9,0x6ada,0x6adb,0x6adc,0x6add,0x6ade,0x6adf,0x6ae0,0x6ae1,
+ 0x6ae2,0x6ae3,0x6ae4,0x6ae5,0x6ae6,0x6ae7,0x6ae8,0x6ae9,0x6aea,0x6aeb,
+ 0x6aec,0x6aed,0x6aee,0x6aef,0x6af0,0x6af1,0x6af2,0x6af3,0x6af4,0x6af5,
+ 0x6af6,0x6af7,0x6af8,0x6af9,0x6afa,0x6afb,0x6afc,0x6afd,0x6afe,0x6aff,
+ 0x6b00,0x6b01,0x6b02,0x6b03,0x6b04,0x6b05,0x6b06,0x6b07,0x6b08,0x6b09,
+ 0x6b0a,0x6b0b,0x6b0c,0x6b0d,0x6b0e,0x6b0f,0x6b10,0x6b11,0x6b12,0x6b13,
+ 0x6b14,0x6b15,0x6b16,0x6b17,0x6b18,0x6b19,0x6b1a,0x6b1b,0x6b1c,0x6b1d,
+ 0x6b1e,0x6b1f,0x6b25,0x6b26,0x6b28,0x6b29,0x6b2a,0x6b2b,0x6b2c,0x6b2d,
+ 0x6b2e
+ },
+ { /* ku 1a */
+ 0x6b2f,0x6b30,0x6b31,0x6b33,0x6b34,0x6b35,0x6b36,0x6b38,0x6b3b,0x6b3c,
+ 0x6b3d,0x6b3f,0x6b40,0x6b41,0x6b42,0x6b44,0x6b45,0x6b48,0x6b4a,0x6b4b,
+ 0x6b4d,0x6b4e,0x6b4f,0x6b50,0x6b51,0x6b52,0x6b53,0x6b54,0x6b55,0x6b56,
+ 0x6b57,0x6b58,0x6b5a,0x6b5b,0x6b5c,0x6b5d,0x6b5e,0x6b5f,0x6b60,0x6b61,
+ 0x6b68,0x6b69,0x6b6b,0x6b6c,0x6b6d,0x6b6e,0x6b6f,0x6b70,0x6b71,0x6b72,
+ 0x6b73,0x6b74,0x6b75,0x6b76,0x6b77,0x6b78,0x6b7a,0x6b7d,0x6b7e,0x6b7f,
+ 0x6b80,0x6b85,0x6b88,UBOGON,0x6b8c,0x6b8e,0x6b8f,0x6b90,0x6b91,0x6b94,
+ 0x6b95,0x6b97,0x6b98,0x6b99,0x6b9c,0x6b9d,0x6b9e,0x6b9f,0x6ba0,0x6ba2,
+ 0x6ba3,0x6ba4,0x6ba5,0x6ba6,0x6ba7,0x6ba8,0x6ba9,0x6bab,0x6bac,0x6bad,
+ 0x6bae,0x6baf,0x6bb0,0x6bb1,0x6bb2,0x6bb6,0x6bb8,0x6bb9,0x6bba,0x6bbb,
+ 0x6bbc,0x6bbd,0x6bbe,0x6bc0,0x6bc3,0x6bc4,0x6bc6,0x6bc7,0x6bc8,0x6bc9,
+ 0x6bca,0x6bcc,0x6bce,0x6bd0,0x6bd1,0x6bd8,0x6bda,0x6bdc,0x6bdd,0x6bde,
+ 0x6bdf,0x6be0,0x6be2,0x6be3,0x6be4,0x6be5,0x6be6,0x6be7,0x6be8,0x6be9,
+ 0x6bec,0x6bed,0x6bee,0x6bf0,0x6bf1,0x6bf2,0x6bf4,0x6bf6,0x6bf7,0x6bf8,
+ 0x6bfa,0x6bfb,0x6bfc,0x6bfe,0x6bff,0x6c00,0x6c01,0x6c02,0x6c03,0x6c04,
+ 0x6c08,0x6c09,0x6c0a,0x6c0b,0x6c0c,0x6c0e,0x6c12,0x6c17,0x6c1c,0x6c1d,
+ 0x6c1e,0x6c20,0x6c23,0x6c25,0x6c2b,0x6c2c,0x6c2d,0x6c31,0x6c33,0x6c36,
+ 0x6c37,0x6c39,0x6c3a,0x6c3b,0x6c3c,0x6c3e,0x6c3f,0x6c43,0x6c44,0x6c45,
+ 0x6c48,0x6c4b,0x6c4c,0x6c4d,0x6c4e,0x6c4f,0x6c51,0x6c52,0x6c53,0x6c56,
+ 0x6c58
+ },
+ { /* ku 1b */
+ 0x6c59,0x6c5a,0x6c62,0x6c63,0x6c65,0x6c66,0x6c67,0x6c6b,0x6c6c,0x6c6d,
+ 0x6c6e,0x6c6f,0x6c71,0x6c73,0x6c75,0x6c77,0x6c78,0x6c7a,0x6c7b,0x6c7c,
+ 0x6c7f,0x6c80,0x6c84,0x6c87,0x6c8a,0x6c8b,0x6c8d,0x6c8e,0x6c91,0x6c92,
+ 0x6c95,0x6c96,0x6c97,0x6c98,0x6c9a,0x6c9c,0x6c9d,0x6c9e,0x6ca0,0x6ca2,
+ 0x6ca8,0x6cac,0x6caf,0x6cb0,0x6cb4,0x6cb5,0x6cb6,0x6cb7,0x6cba,0x6cc0,
+ 0x6cc1,0x6cc2,0x6cc3,0x6cc6,0x6cc7,0x6cc8,0x6ccb,0x6ccd,0x6cce,0x6ccf,
+ 0x6cd1,0x6cd2,0x6cd8,UBOGON,0x6cd9,0x6cda,0x6cdc,0x6cdd,0x6cdf,0x6ce4,
+ 0x6ce6,0x6ce7,0x6ce9,0x6cec,0x6ced,0x6cf2,0x6cf4,0x6cf9,0x6cff,0x6d00,
+ 0x6d02,0x6d03,0x6d05,0x6d06,0x6d08,0x6d09,0x6d0a,0x6d0d,0x6d0f,0x6d10,
+ 0x6d11,0x6d13,0x6d14,0x6d15,0x6d16,0x6d18,0x6d1c,0x6d1d,0x6d1f,0x6d20,
+ 0x6d21,0x6d22,0x6d23,0x6d24,0x6d26,0x6d28,0x6d29,0x6d2c,0x6d2d,0x6d2f,
+ 0x6d30,0x6d34,0x6d36,0x6d37,0x6d38,0x6d3a,0x6d3f,0x6d40,0x6d42,0x6d44,
+ 0x6d49,0x6d4c,0x6d50,0x6d55,0x6d56,0x6d57,0x6d58,0x6d5b,0x6d5d,0x6d5f,
+ 0x6d61,0x6d62,0x6d64,0x6d65,0x6d67,0x6d68,0x6d6b,0x6d6c,0x6d6d,0x6d70,
+ 0x6d71,0x6d72,0x6d73,0x6d75,0x6d76,0x6d79,0x6d7a,0x6d7b,0x6d7d,0x6d7e,
+ 0x6d7f,0x6d80,0x6d81,0x6d83,0x6d84,0x6d86,0x6d87,0x6d8a,0x6d8b,0x6d8d,
+ 0x6d8f,0x6d90,0x6d92,0x6d96,0x6d97,0x6d98,0x6d99,0x6d9a,0x6d9c,0x6da2,
+ 0x6da5,0x6dac,0x6dad,0x6db0,0x6db1,0x6db3,0x6db4,0x6db6,0x6db7,0x6db9,
+ 0x6dba,0x6dbb,0x6dbc,0x6dbd,0x6dbe,0x6dc1,0x6dc2,0x6dc3,0x6dc8,0x6dc9,
+ 0x6dca
+ },
+ { /* ku 1c */
+ 0x6dcd,0x6dce,0x6dcf,0x6dd0,0x6dd2,0x6dd3,0x6dd4,0x6dd5,0x6dd7,0x6dda,
+ 0x6ddb,0x6ddc,0x6ddf,0x6de2,0x6de3,0x6de5,0x6de7,0x6de8,0x6de9,0x6dea,
+ 0x6ded,0x6def,0x6df0,0x6df2,0x6df4,0x6df5,0x6df6,0x6df8,0x6dfa,0x6dfd,
+ 0x6dfe,0x6dff,0x6e00,0x6e01,0x6e02,0x6e03,0x6e04,0x6e06,0x6e07,0x6e08,
+ 0x6e09,0x6e0b,0x6e0f,0x6e12,0x6e13,0x6e15,0x6e18,0x6e19,0x6e1b,0x6e1c,
+ 0x6e1e,0x6e1f,0x6e22,0x6e26,0x6e27,0x6e28,0x6e2a,0x6e2c,0x6e2e,0x6e30,
+ 0x6e31,0x6e33,0x6e35,UBOGON,0x6e36,0x6e37,0x6e39,0x6e3b,0x6e3c,0x6e3d,
+ 0x6e3e,0x6e3f,0x6e40,0x6e41,0x6e42,0x6e45,0x6e46,0x6e47,0x6e48,0x6e49,
+ 0x6e4a,0x6e4b,0x6e4c,0x6e4f,0x6e50,0x6e51,0x6e52,0x6e55,0x6e57,0x6e59,
+ 0x6e5a,0x6e5c,0x6e5d,0x6e5e,0x6e60,0x6e61,0x6e62,0x6e63,0x6e64,0x6e65,
+ 0x6e66,0x6e67,0x6e68,0x6e69,0x6e6a,0x6e6c,0x6e6d,0x6e6f,0x6e70,0x6e71,
+ 0x6e72,0x6e73,0x6e74,0x6e75,0x6e76,0x6e77,0x6e78,0x6e79,0x6e7a,0x6e7b,
+ 0x6e7c,0x6e7d,0x6e80,0x6e81,0x6e82,0x6e84,0x6e87,0x6e88,0x6e8a,0x6e8b,
+ 0x6e8c,0x6e8d,0x6e8e,0x6e91,0x6e92,0x6e93,0x6e94,0x6e95,0x6e96,0x6e97,
+ 0x6e99,0x6e9a,0x6e9b,0x6e9d,0x6e9e,0x6ea0,0x6ea1,0x6ea3,0x6ea4,0x6ea6,
+ 0x6ea8,0x6ea9,0x6eab,0x6eac,0x6ead,0x6eae,0x6eb0,0x6eb3,0x6eb5,0x6eb8,
+ 0x6eb9,0x6ebc,0x6ebe,0x6ebf,0x6ec0,0x6ec3,0x6ec4,0x6ec5,0x6ec6,0x6ec8,
+ 0x6ec9,0x6eca,0x6ecc,0x6ecd,0x6ece,0x6ed0,0x6ed2,0x6ed6,0x6ed8,0x6ed9,
+ 0x6edb,0x6edc,0x6edd,0x6ee3,0x6ee7,0x6eea,0x6eeb,0x6eec,0x6eed,0x6eee,
+ 0x6eef
+ },
+ { /* ku 1d */
+ 0x6ef0,0x6ef1,0x6ef2,0x6ef3,0x6ef5,0x6ef6,0x6ef7,0x6ef8,0x6efa,0x6efb,
+ 0x6efc,0x6efd,0x6efe,0x6eff,0x6f00,0x6f01,0x6f03,0x6f04,0x6f05,0x6f07,
+ 0x6f08,0x6f0a,0x6f0b,0x6f0c,0x6f0d,0x6f0e,0x6f10,0x6f11,0x6f12,0x6f16,
+ 0x6f17,0x6f18,0x6f19,0x6f1a,0x6f1b,0x6f1c,0x6f1d,0x6f1e,0x6f1f,0x6f21,
+ 0x6f22,0x6f23,0x6f25,0x6f26,0x6f27,0x6f28,0x6f2c,0x6f2e,0x6f30,0x6f32,
+ 0x6f34,0x6f35,0x6f37,0x6f38,0x6f39,0x6f3a,0x6f3b,0x6f3c,0x6f3d,0x6f3f,
+ 0x6f40,0x6f41,0x6f42,UBOGON,0x6f43,0x6f44,0x6f45,0x6f48,0x6f49,0x6f4a,
+ 0x6f4c,0x6f4e,0x6f4f,0x6f50,0x6f51,0x6f52,0x6f53,0x6f54,0x6f55,0x6f56,
+ 0x6f57,0x6f59,0x6f5a,0x6f5b,0x6f5d,0x6f5f,0x6f60,0x6f61,0x6f63,0x6f64,
+ 0x6f65,0x6f67,0x6f68,0x6f69,0x6f6a,0x6f6b,0x6f6c,0x6f6f,0x6f70,0x6f71,
+ 0x6f73,0x6f75,0x6f76,0x6f77,0x6f79,0x6f7b,0x6f7d,0x6f7e,0x6f7f,0x6f80,
+ 0x6f81,0x6f82,0x6f83,0x6f85,0x6f86,0x6f87,0x6f8a,0x6f8b,0x6f8f,0x6f90,
+ 0x6f91,0x6f92,0x6f93,0x6f94,0x6f95,0x6f96,0x6f97,0x6f98,0x6f99,0x6f9a,
+ 0x6f9b,0x6f9d,0x6f9e,0x6f9f,0x6fa0,0x6fa2,0x6fa3,0x6fa4,0x6fa5,0x6fa6,
+ 0x6fa8,0x6fa9,0x6faa,0x6fab,0x6fac,0x6fad,0x6fae,0x6faf,0x6fb0,0x6fb1,
+ 0x6fb2,0x6fb4,0x6fb5,0x6fb7,0x6fb8,0x6fba,0x6fbb,0x6fbc,0x6fbd,0x6fbe,
+ 0x6fbf,0x6fc1,0x6fc3,0x6fc4,0x6fc5,0x6fc6,0x6fc7,0x6fc8,0x6fca,0x6fcb,
+ 0x6fcc,0x6fcd,0x6fce,0x6fcf,0x6fd0,0x6fd3,0x6fd4,0x6fd5,0x6fd6,0x6fd7,
+ 0x6fd8,0x6fd9,0x6fda,0x6fdb,0x6fdc,0x6fdd,0x6fdf,0x6fe2,0x6fe3,0x6fe4,
+ 0x6fe5
+ },
+ { /* ku 1e */
+ 0x6fe6,0x6fe7,0x6fe8,0x6fe9,0x6fea,0x6feb,0x6fec,0x6fed,0x6ff0,0x6ff1,
+ 0x6ff2,0x6ff3,0x6ff4,0x6ff5,0x6ff6,0x6ff7,0x6ff8,0x6ff9,0x6ffa,0x6ffb,
+ 0x6ffc,0x6ffd,0x6ffe,0x6fff,0x7000,0x7001,0x7002,0x7003,0x7004,0x7005,
+ 0x7006,0x7007,0x7008,0x7009,0x700a,0x700b,0x700c,0x700d,0x700e,0x700f,
+ 0x7010,0x7012,0x7013,0x7014,0x7015,0x7016,0x7017,0x7018,0x7019,0x701c,
+ 0x701d,0x701e,0x701f,0x7020,0x7021,0x7022,0x7024,0x7025,0x7026,0x7027,
+ 0x7028,0x7029,0x702a,UBOGON,0x702b,0x702c,0x702d,0x702e,0x702f,0x7030,
+ 0x7031,0x7032,0x7033,0x7034,0x7036,0x7037,0x7038,0x703a,0x703b,0x703c,
+ 0x703d,0x703e,0x703f,0x7040,0x7041,0x7042,0x7043,0x7044,0x7045,0x7046,
+ 0x7047,0x7048,0x7049,0x704a,0x704b,0x704d,0x704e,0x7050,0x7051,0x7052,
+ 0x7053,0x7054,0x7055,0x7056,0x7057,0x7058,0x7059,0x705a,0x705b,0x705c,
+ 0x705d,0x705f,0x7060,0x7061,0x7062,0x7063,0x7064,0x7065,0x7066,0x7067,
+ 0x7068,0x7069,0x706a,0x706e,0x7071,0x7072,0x7073,0x7074,0x7077,0x7079,
+ 0x707a,0x707b,0x707d,0x7081,0x7082,0x7083,0x7084,0x7086,0x7087,0x7088,
+ 0x708b,0x708c,0x708d,0x708f,0x7090,0x7091,0x7093,0x7097,0x7098,0x709a,
+ 0x709b,0x709e,0x709f,0x70a0,0x70a1,0x70a2,0x70a3,0x70a4,0x70a5,0x70a6,
+ 0x70a7,0x70a8,0x70a9,0x70aa,0x70b0,0x70b2,0x70b4,0x70b5,0x70b6,0x70ba,
+ 0x70be,0x70bf,0x70c4,0x70c5,0x70c6,0x70c7,0x70c9,0x70cb,0x70cc,0x70cd,
+ 0x70ce,0x70cf,0x70d0,0x70d1,0x70d2,0x70d3,0x70d4,0x70d5,0x70d6,0x70d7,
+ 0x70da
+ },
+ { /* ku 1f */
+ 0x70dc,0x70dd,0x70de,0x70e0,0x70e1,0x70e2,0x70e3,0x70e5,0x70ea,0x70ee,
+ 0x70f0,0x70f1,0x70f2,0x70f3,0x70f4,0x70f5,0x70f6,0x70f8,0x70fa,0x70fb,
+ 0x70fc,0x70fe,0x70ff,0x7100,0x7101,0x7102,0x7103,0x7104,0x7105,0x7106,
+ 0x7107,0x7108,0x710b,0x710c,0x710d,0x710e,0x710f,0x7111,0x7112,0x7114,
+ 0x7117,0x711b,0x711c,0x711d,0x711e,0x711f,0x7120,0x7121,0x7122,0x7123,
+ 0x7124,0x7125,0x7127,0x7128,0x7129,0x712a,0x712b,0x712c,0x712d,0x712e,
+ 0x7132,0x7133,0x7134,UBOGON,0x7135,0x7137,0x7138,0x7139,0x713a,0x713b,
+ 0x713c,0x713d,0x713e,0x713f,0x7140,0x7141,0x7142,0x7143,0x7144,0x7146,
+ 0x7147,0x7148,0x7149,0x714b,0x714d,0x714f,0x7150,0x7151,0x7152,0x7153,
+ 0x7154,0x7155,0x7156,0x7157,0x7158,0x7159,0x715a,0x715b,0x715d,0x715f,
+ 0x7160,0x7161,0x7162,0x7163,0x7165,0x7169,0x716a,0x716b,0x716c,0x716d,
+ 0x716f,0x7170,0x7171,0x7174,0x7175,0x7176,0x7177,0x7179,0x717b,0x717c,
+ 0x717e,0x717f,0x7180,0x7181,0x7182,0x7183,0x7185,0x7186,0x7187,0x7188,
+ 0x7189,0x718b,0x718c,0x718d,0x718e,0x7190,0x7191,0x7192,0x7193,0x7195,
+ 0x7196,0x7197,0x719a,0x719b,0x719c,0x719d,0x719e,0x71a1,0x71a2,0x71a3,
+ 0x71a4,0x71a5,0x71a6,0x71a7,0x71a9,0x71aa,0x71ab,0x71ad,0x71ae,0x71af,
+ 0x71b0,0x71b1,0x71b2,0x71b4,0x71b6,0x71b7,0x71b8,0x71ba,0x71bb,0x71bc,
+ 0x71bd,0x71be,0x71bf,0x71c0,0x71c1,0x71c2,0x71c4,0x71c5,0x71c6,0x71c7,
+ 0x71c8,0x71c9,0x71ca,0x71cb,0x71cc,0x71cd,0x71cf,0x71d0,0x71d1,0x71d2,
+ 0x71d3
+ },
+ { /* ku 20 */
+ 0x71d6,0x71d7,0x71d8,0x71d9,0x71da,0x71db,0x71dc,0x71dd,0x71de,0x71df,
+ 0x71e1,0x71e2,0x71e3,0x71e4,0x71e6,0x71e8,0x71e9,0x71ea,0x71eb,0x71ec,
+ 0x71ed,0x71ef,0x71f0,0x71f1,0x71f2,0x71f3,0x71f4,0x71f5,0x71f6,0x71f7,
+ 0x71f8,0x71fa,0x71fb,0x71fc,0x71fd,0x71fe,0x71ff,0x7200,0x7201,0x7202,
+ 0x7203,0x7204,0x7205,0x7207,0x7208,0x7209,0x720a,0x720b,0x720c,0x720d,
+ 0x720e,0x720f,0x7210,0x7211,0x7212,0x7213,0x7214,0x7215,0x7216,0x7217,
+ 0x7218,0x7219,0x721a,UBOGON,0x721b,0x721c,0x721e,0x721f,0x7220,0x7221,
+ 0x7222,0x7223,0x7224,0x7225,0x7226,0x7227,0x7229,0x722b,0x722d,0x722e,
+ 0x722f,0x7232,0x7233,0x7234,0x723a,0x723c,0x723e,0x7240,0x7241,0x7242,
+ 0x7243,0x7244,0x7245,0x7246,0x7249,0x724a,0x724b,0x724e,0x724f,0x7250,
+ 0x7251,0x7253,0x7254,0x7255,0x7257,0x7258,0x725a,0x725c,0x725e,0x7260,
+ 0x7263,0x7264,0x7265,0x7268,0x726a,0x726b,0x726c,0x726d,0x7270,0x7271,
+ 0x7273,0x7274,0x7276,0x7277,0x7278,0x727b,0x727c,0x727d,0x7282,0x7283,
+ 0x7285,0x7286,0x7287,0x7288,0x7289,0x728c,0x728e,0x7290,0x7291,0x7293,
+ 0x7294,0x7295,0x7296,0x7297,0x7298,0x7299,0x729a,0x729b,0x729c,0x729d,
+ 0x729e,0x72a0,0x72a1,0x72a2,0x72a3,0x72a4,0x72a5,0x72a6,0x72a7,0x72a8,
+ 0x72a9,0x72aa,0x72ab,0x72ae,0x72b1,0x72b2,0x72b3,0x72b5,0x72ba,0x72bb,
+ 0x72bc,0x72bd,0x72be,0x72bf,0x72c0,0x72c5,0x72c6,0x72c7,0x72c9,0x72ca,
+ 0x72cb,0x72cc,0x72cf,0x72d1,0x72d3,0x72d4,0x72d5,0x72d6,0x72d8,0x72da,
+ 0x72db
+ },
+ { /* ku 21 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3000,0x3001,0x3002,
+ 0x00b7,0x02c9,0x02c7,0x00a8,0x3003,0x3005,0x2014,0xff5e,0x2016,0x2026,
+ 0x2018,0x2019,0x201c,0x201d,0x3014,0x3015,0x3008,0x3009,0x300a,0x300b,
+ 0x300c,0x300d,0x300e,0x300f,0x3016,0x3017,0x3010,0x3011,0x00b1,0x00d7,
+ 0x00f7,0x2236,0x2227,0x2228,0x2211,0x220f,0x222a,0x2229,0x2208,0x2237,
+ 0x221a,0x22a5,0x2225,0x2220,0x2312,0x2299,0x222b,0x222e,0x2261,0x224c,
+ 0x2248,0x223d,0x221d,0x2260,0x226e,0x226f,0x2264,0x2265,0x221e,0x2235,
+ 0x2234,0x2642,0x2640,0x00b0,0x2032,0x2033,0x2103,0xff04,0x00a4,0xffe0,
+ 0xffe1,0x2030,0x00a7,0x2116,0x2606,0x2605,0x25cb,0x25cf,0x25ce,0x25c7,
+ 0x25c6,0x25a1,0x25a0,0x25b3,0x25b2,0x203b,0x2192,0x2190,0x2191,0x2193,
+ 0x3013
+ },
+ { /* ku 22 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x2170,0x2171,0x2172,
+ 0x2173,0x2174,0x2175,0x2176,0x2177,0x2178,0x2179,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x2488,0x2489,0x248a,0x248b,0x248c,0x248d,0x248e,
+ 0x248f,0x2490,0x2491,0x2492,0x2493,0x2494,0x2495,0x2496,0x2497,0x2498,
+ 0x2499,0x249a,0x249b,0x2474,0x2475,0x2476,0x2477,0x2478,0x2479,0x247a,
+ 0x247b,0x247c,0x247d,0x247e,0x247f,0x2480,0x2481,0x2482,0x2483,0x2484,
+ 0x2485,0x2486,0x2487,0x2460,0x2461,0x2462,0x2463,0x2464,0x2465,0x2466,
+ 0x2467,0x2468,0x2469,UBOGON,UBOGON,0x3220,0x3221,0x3222,0x3223,0x3224,
+ 0x3225,0x3226,0x3227,0x3228,0x3229,UBOGON,UBOGON,0x2160,0x2161,0x2162,
+ 0x2163,0x2164,0x2165,0x2166,0x2167,0x2168,0x2169,0x216a,0x216b,UBOGON,
+ UBOGON
+ },
+ { /* ku 23 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0xff01,0xff02,0xff03,
+ 0xffe5,0xff05,0xff06,0xff07,0xff08,0xff09,0xff0a,0xff0b,0xff0c,0xff0d,
+ 0xff0e,0xff0f,0xff10,0xff11,0xff12,0xff13,0xff14,0xff15,0xff16,0xff17,
+ 0xff18,0xff19,0xff1a,0xff1b,0xff1c,0xff1d,0xff1e,0xff1f,0xff20,0xff21,
+ 0xff22,0xff23,0xff24,0xff25,0xff26,0xff27,0xff28,0xff29,0xff2a,0xff2b,
+ 0xff2c,0xff2d,0xff2e,0xff2f,0xff30,0xff31,0xff32,0xff33,0xff34,0xff35,
+ 0xff36,0xff37,0xff38,0xff39,0xff3a,0xff3b,0xff3c,0xff3d,0xff3e,0xff3f,
+ 0xff40,0xff41,0xff42,0xff43,0xff44,0xff45,0xff46,0xff47,0xff48,0xff49,
+ 0xff4a,0xff4b,0xff4c,0xff4d,0xff4e,0xff4f,0xff50,0xff51,0xff52,0xff53,
+ 0xff54,0xff55,0xff56,0xff57,0xff58,0xff59,0xff5a,0xff5b,0xff5c,0xff5d,
+ 0xffe3
+ },
+ { /* ku 24 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3041,0x3042,0x3043,
+ 0x3044,0x3045,0x3046,0x3047,0x3048,0x3049,0x304a,0x304b,0x304c,0x304d,
+ 0x304e,0x304f,0x3050,0x3051,0x3052,0x3053,0x3054,0x3055,0x3056,0x3057,
+ 0x3058,0x3059,0x305a,0x305b,0x305c,0x305d,0x305e,0x305f,0x3060,0x3061,
+ 0x3062,0x3063,0x3064,0x3065,0x3066,0x3067,0x3068,0x3069,0x306a,0x306b,
+ 0x306c,0x306d,0x306e,0x306f,0x3070,0x3071,0x3072,0x3073,0x3074,0x3075,
+ 0x3076,0x3077,0x3078,0x3079,0x307a,0x307b,0x307c,0x307d,0x307e,0x307f,
+ 0x3080,0x3081,0x3082,0x3083,0x3084,0x3085,0x3086,0x3087,0x3088,0x3089,
+ 0x308a,0x308b,0x308c,0x308d,0x308e,0x308f,0x3090,0x3091,0x3092,0x3093,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 25 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x30a1,0x30a2,0x30a3,
+ 0x30a4,0x30a5,0x30a6,0x30a7,0x30a8,0x30a9,0x30aa,0x30ab,0x30ac,0x30ad,
+ 0x30ae,0x30af,0x30b0,0x30b1,0x30b2,0x30b3,0x30b4,0x30b5,0x30b6,0x30b7,
+ 0x30b8,0x30b9,0x30ba,0x30bb,0x30bc,0x30bd,0x30be,0x30bf,0x30c0,0x30c1,
+ 0x30c2,0x30c3,0x30c4,0x30c5,0x30c6,0x30c7,0x30c8,0x30c9,0x30ca,0x30cb,
+ 0x30cc,0x30cd,0x30ce,0x30cf,0x30d0,0x30d1,0x30d2,0x30d3,0x30d4,0x30d5,
+ 0x30d6,0x30d7,0x30d8,0x30d9,0x30da,0x30db,0x30dc,0x30dd,0x30de,0x30df,
+ 0x30e0,0x30e1,0x30e2,0x30e3,0x30e4,0x30e5,0x30e6,0x30e7,0x30e8,0x30e9,
+ 0x30ea,0x30eb,0x30ec,0x30ed,0x30ee,0x30ef,0x30f0,0x30f1,0x30f2,0x30f3,
+ 0x30f4,0x30f5,0x30f6,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 26 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x0391,0x0392,0x0393,
+ 0x0394,0x0395,0x0396,0x0397,0x0398,0x0399,0x039a,0x039b,0x039c,0x039d,
+ 0x039e,0x039f,0x03a0,0x03a1,0x03a3,0x03a4,0x03a5,0x03a6,0x03a7,0x03a8,
+ 0x03a9,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x03b1,
+ 0x03b2,0x03b3,0x03b4,0x03b5,0x03b6,0x03b7,0x03b8,0x03b9,0x03ba,0x03bb,
+ 0x03bc,0x03bd,0x03be,0x03bf,0x03c0,0x03c1,0x03c3,0x03c4,0x03c5,0x03c6,
+ 0x03c7,0x03c8,0x03c9,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0xfe35,0xfe36,0xfe39,0xfe3a,0xfe3f,0xfe40,0xfe3d,0xfe3e,0xfe41,0xfe42,
+ 0xfe43,0xfe44,UBOGON,UBOGON,0xfe3b,0xfe3c,0xfe37,0xfe38,0xfe31,UBOGON,
+ 0xfe33,0xfe34,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 27 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x0410,0x0411,0x0412,
+ 0x0413,0x0414,0x0415,0x0401,0x0416,0x0417,0x0418,0x0419,0x041a,0x041b,
+ 0x041c,0x041d,0x041e,0x041f,0x0420,0x0421,0x0422,0x0423,0x0424,0x0425,
+ 0x0426,0x0427,0x0428,0x0429,0x042a,0x042b,0x042c,0x042d,0x042e,0x042f,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x0430,0x0431,0x0432,0x0433,0x0434,
+ 0x0435,0x0451,0x0436,0x0437,0x0438,0x0439,0x043a,0x043b,0x043c,0x043d,
+ 0x043e,0x043f,0x0440,0x0441,0x0442,0x0443,0x0444,0x0445,0x0446,0x0447,
+ 0x0448,0x0449,0x044a,0x044b,0x044c,0x044d,0x044e,0x044f,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 28 */
+ 0x02ca,0x02cb,0x02d9,0x2013,0x2015,0x2025,0x2035,0x2105,0x2109,0x2196,
+ 0x2197,0x2198,0x2199,0x2215,0x221f,0x2223,0x2252,0x2266,0x2267,0x22bf,
+ 0x2550,0x2551,0x2552,0x2553,0x2554,0x2555,0x2556,0x2557,0x2558,0x2559,
+ 0x255a,0x255b,0x255c,0x255d,0x255e,0x255f,0x2560,0x2561,0x2562,0x2563,
+ 0x2564,0x2565,0x2566,0x2567,0x2568,0x2569,0x256a,0x256b,0x256c,0x256d,
+ 0x256e,0x256f,0x2570,0x2571,0x2572,0x2573,0x2581,0x2582,0x2583,0x2584,
+ 0x2585,0x2586,0x2587,UBOGON,0x2588,0x2589,0x258a,0x258b,0x258c,0x258d,
+ 0x258e,0x258f,0x2593,0x2594,0x2595,0x25bc,0x25bd,0x25e2,0x25e3,0x25e4,
+ 0x25e5,0x2609,0x2295,0x3012,0x301d,0x301e,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x0101,0x00e1,0x01ce,
+ 0x00e0,0x0113,0x00e9,0x011b,0x00e8,0x012b,0x00ed,0x01d0,0x00ec,0x014d,
+ 0x00f3,0x01d2,0x00f2,0x016b,0x00fa,0x01d4,0x00f9,0x01d6,0x01d8,0x01da,
+ 0x01dc,0x00fc,0x00ea,0x0251,UBOGON,0x0144,0x0148,UBOGON,0x0261,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x3105,0x3106,0x3107,0x3108,0x3109,0x310a,0x310b,
+ 0x310c,0x310d,0x310e,0x310f,0x3110,0x3111,0x3112,0x3113,0x3114,0x3115,
+ 0x3116,0x3117,0x3118,0x3119,0x311a,0x311b,0x311c,0x311d,0x311e,0x311f,
+ 0x3120,0x3121,0x3122,0x3123,0x3124,0x3125,0x3126,0x3127,0x3128,0x3129,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 29 */
+ 0x3021,0x3022,0x3023,0x3024,0x3025,0x3026,0x3027,0x3028,0x3029,0x32a3,
+ 0x338e,0x338f,0x339c,0x339d,0x339e,0x33a1,0x33c4,0x33ce,0x33d1,0x33d2,
+ 0x33d5,0xfe30,0xffe2,0xffe4,UBOGON,0x2121,0x3231,UBOGON,0x2010,UBOGON,
+ UBOGON,UBOGON,0x30fc,0x309b,0x309c,0x30fd,0x30fe,0x3006,0x309d,0x309e,
+ 0xfe49,0xfe4a,0xfe4b,0xfe4c,0xfe4d,0xfe4e,0xfe4f,0xfe50,0xfe51,0xfe52,
+ 0xfe54,0xfe55,0xfe56,0xfe57,0xfe59,0xfe5a,0xfe5b,0xfe5c,0xfe5d,0xfe5e,
+ 0xfe5f,0xfe60,0xfe61,UBOGON,0xfe62,0xfe63,0xfe64,0xfe65,0xfe66,0xfe68,
+ 0xfe69,0xfe6a,0xfe6b,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x3007,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x2500,0x2501,0x2502,0x2503,0x2504,0x2505,0x2506,0x2507,0x2508,0x2509,
+ 0x250a,0x250b,0x250c,0x250d,0x250e,0x250f,0x2510,0x2511,0x2512,0x2513,
+ 0x2514,0x2515,0x2516,0x2517,0x2518,0x2519,0x251a,0x251b,0x251c,0x251d,
+ 0x251e,0x251f,0x2520,0x2521,0x2522,0x2523,0x2524,0x2525,0x2526,0x2527,
+ 0x2528,0x2529,0x252a,0x252b,0x252c,0x252d,0x252e,0x252f,0x2530,0x2531,
+ 0x2532,0x2533,0x2534,0x2535,0x2536,0x2537,0x2538,0x2539,0x253a,0x253b,
+ 0x253c,0x253d,0x253e,0x253f,0x2540,0x2541,0x2542,0x2543,0x2544,0x2545,
+ 0x2546,0x2547,0x2548,0x2549,0x254a,0x254b,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 2a */
+ 0x72dc,0x72dd,0x72df,0x72e2,0x72e3,0x72e4,0x72e5,0x72e6,0x72e7,0x72ea,
+ 0x72eb,0x72f5,0x72f6,0x72f9,0x72fd,0x72fe,0x72ff,0x7300,0x7302,0x7304,
+ 0x7305,0x7306,0x7307,0x7308,0x7309,0x730b,0x730c,0x730d,0x730f,0x7310,
+ 0x7311,0x7312,0x7314,0x7318,0x7319,0x731a,0x731f,0x7320,0x7323,0x7324,
+ 0x7326,0x7327,0x7328,0x732d,0x732f,0x7330,0x7332,0x7333,0x7335,0x7336,
+ 0x733a,0x733b,0x733c,0x733d,0x7340,0x7341,0x7342,0x7343,0x7344,0x7345,
+ 0x7346,0x7347,0x7348,UBOGON,0x7349,0x734a,0x734b,0x734c,0x734e,0x734f,
+ 0x7351,0x7353,0x7354,0x7355,0x7356,0x7358,0x7359,0x735a,0x735b,0x735c,
+ 0x735d,0x735e,0x735f,0x7361,0x7362,0x7363,0x7364,0x7365,0x7366,0x7367,
+ 0x7368,0x7369,0x736a,0x736b,0x736e,0x7370,0x7371,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 2b */
+ 0x7372,0x7373,0x7374,0x7375,0x7376,0x7377,0x7378,0x7379,0x737a,0x737b,
+ 0x737c,0x737d,0x737f,0x7380,0x7381,0x7382,0x7383,0x7385,0x7386,0x7388,
+ 0x738a,0x738c,0x738d,0x738f,0x7390,0x7392,0x7393,0x7394,0x7395,0x7397,
+ 0x7398,0x7399,0x739a,0x739c,0x739d,0x739e,0x73a0,0x73a1,0x73a3,0x73a4,
+ 0x73a5,0x73a6,0x73a7,0x73a8,0x73aa,0x73ac,0x73ad,0x73b1,0x73b4,0x73b5,
+ 0x73b6,0x73b8,0x73b9,0x73bc,0x73bd,0x73be,0x73bf,0x73c1,0x73c3,0x73c4,
+ 0x73c5,0x73c6,0x73c7,UBOGON,0x73cb,0x73cc,0x73ce,0x73d2,0x73d3,0x73d4,
+ 0x73d5,0x73d6,0x73d7,0x73d8,0x73da,0x73db,0x73dc,0x73dd,0x73df,0x73e1,
+ 0x73e2,0x73e3,0x73e4,0x73e6,0x73e8,0x73ea,0x73eb,0x73ec,0x73ee,0x73ef,
+ 0x73f0,0x73f1,0x73f3,0x73f4,0x73f5,0x73f6,0x73f7,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 2c */
+ 0x73f8,0x73f9,0x73fa,0x73fb,0x73fc,0x73fd,0x73fe,0x73ff,0x7400,0x7401,
+ 0x7402,0x7404,0x7407,0x7408,0x740b,0x740c,0x740d,0x740e,0x7411,0x7412,
+ 0x7413,0x7414,0x7415,0x7416,0x7417,0x7418,0x7419,0x741c,0x741d,0x741e,
+ 0x741f,0x7420,0x7421,0x7423,0x7424,0x7427,0x7429,0x742b,0x742d,0x742f,
+ 0x7431,0x7432,0x7437,0x7438,0x7439,0x743a,0x743b,0x743d,0x743e,0x743f,
+ 0x7440,0x7442,0x7443,0x7444,0x7445,0x7446,0x7447,0x7448,0x7449,0x744a,
+ 0x744b,0x744c,0x744d,UBOGON,0x744e,0x744f,0x7450,0x7451,0x7452,0x7453,
+ 0x7454,0x7456,0x7458,0x745d,0x7460,0x7461,0x7462,0x7463,0x7464,0x7465,
+ 0x7466,0x7467,0x7468,0x7469,0x746a,0x746b,0x746c,0x746e,0x746f,0x7471,
+ 0x7472,0x7473,0x7474,0x7475,0x7478,0x7479,0x747a,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 2d */
+ 0x747b,0x747c,0x747d,0x747f,0x7482,0x7484,0x7485,0x7486,0x7488,0x7489,
+ 0x748a,0x748c,0x748d,0x748f,0x7491,0x7492,0x7493,0x7494,0x7495,0x7496,
+ 0x7497,0x7498,0x7499,0x749a,0x749b,0x749d,0x749f,0x74a0,0x74a1,0x74a2,
+ 0x74a3,0x74a4,0x74a5,0x74a6,0x74aa,0x74ab,0x74ac,0x74ad,0x74ae,0x74af,
+ 0x74b0,0x74b1,0x74b2,0x74b3,0x74b4,0x74b5,0x74b6,0x74b7,0x74b8,0x74b9,
+ 0x74bb,0x74bc,0x74bd,0x74be,0x74bf,0x74c0,0x74c1,0x74c2,0x74c3,0x74c4,
+ 0x74c5,0x74c6,0x74c7,UBOGON,0x74c8,0x74c9,0x74ca,0x74cb,0x74cc,0x74cd,
+ 0x74ce,0x74cf,0x74d0,0x74d1,0x74d3,0x74d4,0x74d5,0x74d6,0x74d7,0x74d8,
+ 0x74d9,0x74da,0x74db,0x74dd,0x74df,0x74e1,0x74e5,0x74e7,0x74e8,0x74e9,
+ 0x74ea,0x74eb,0x74ec,0x74ed,0x74f0,0x74f1,0x74f2,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 2e */
+ 0x74f3,0x74f5,0x74f8,0x74f9,0x74fa,0x74fb,0x74fc,0x74fd,0x74fe,0x7500,
+ 0x7501,0x7502,0x7503,0x7505,0x7506,0x7507,0x7508,0x7509,0x750a,0x750b,
+ 0x750c,0x750e,0x7510,0x7512,0x7514,0x7515,0x7516,0x7517,0x751b,0x751d,
+ 0x751e,0x7520,0x7521,0x7522,0x7523,0x7524,0x7526,0x7527,0x752a,0x752e,
+ 0x7534,0x7536,0x7539,0x753c,0x753d,0x753f,0x7541,0x7542,0x7543,0x7544,
+ 0x7546,0x7547,0x7549,0x754a,0x754d,0x7550,0x7551,0x7552,0x7553,0x7555,
+ 0x7556,0x7557,0x7558,UBOGON,0x755d,0x755e,0x755f,0x7560,0x7561,0x7562,
+ 0x7563,0x7564,0x7567,0x7568,0x7569,0x756b,0x756c,0x756d,0x756e,0x756f,
+ 0x7570,0x7571,0x7573,0x7575,0x7576,0x7577,0x757a,0x757b,0x757c,0x757d,
+ 0x757e,0x7580,0x7581,0x7582,0x7584,0x7585,0x7587,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 2f */
+ 0x7588,0x7589,0x758a,0x758c,0x758d,0x758e,0x7590,0x7593,0x7595,0x7598,
+ 0x759b,0x759c,0x759e,0x75a2,0x75a6,0x75a7,0x75a8,0x75a9,0x75aa,0x75ad,
+ 0x75b6,0x75b7,0x75ba,0x75bb,0x75bf,0x75c0,0x75c1,0x75c6,0x75cb,0x75cc,
+ 0x75ce,0x75cf,0x75d0,0x75d1,0x75d3,0x75d7,0x75d9,0x75da,0x75dc,0x75dd,
+ 0x75df,0x75e0,0x75e1,0x75e5,0x75e9,0x75ec,0x75ed,0x75ee,0x75ef,0x75f2,
+ 0x75f3,0x75f5,0x75f6,0x75f7,0x75f8,0x75fa,0x75fb,0x75fd,0x75fe,0x7602,
+ 0x7604,0x7606,0x7607,UBOGON,0x7608,0x7609,0x760b,0x760d,0x760e,0x760f,
+ 0x7611,0x7612,0x7613,0x7614,0x7616,0x761a,0x761c,0x761d,0x761e,0x7621,
+ 0x7623,0x7627,0x7628,0x762c,0x762e,0x762f,0x7631,0x7632,0x7636,0x7637,
+ 0x7639,0x763a,0x763b,0x763d,0x7641,0x7642,0x7644,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 30 */
+ 0x7645,0x7646,0x7647,0x7648,0x7649,0x764a,0x764b,0x764e,0x764f,0x7650,
+ 0x7651,0x7652,0x7653,0x7655,0x7657,0x7658,0x7659,0x765a,0x765b,0x765d,
+ 0x765f,0x7660,0x7661,0x7662,0x7664,0x7665,0x7666,0x7667,0x7668,0x7669,
+ 0x766a,0x766c,0x766d,0x766e,0x7670,0x7671,0x7672,0x7673,0x7674,0x7675,
+ 0x7676,0x7677,0x7679,0x767a,0x767c,0x767f,0x7680,0x7681,0x7683,0x7685,
+ 0x7689,0x768a,0x768c,0x768d,0x768f,0x7690,0x7692,0x7694,0x7695,0x7697,
+ 0x7698,0x769a,0x769b,UBOGON,0x769c,0x769d,0x769e,0x769f,0x76a0,0x76a1,
+ 0x76a2,0x76a3,0x76a5,0x76a6,0x76a7,0x76a8,0x76a9,0x76aa,0x76ab,0x76ac,
+ 0x76ad,0x76af,0x76b0,0x76b3,0x76b5,0x76b6,0x76b7,0x76b8,0x76b9,0x76ba,
+ 0x76bb,0x76bc,0x76bd,0x76be,0x76c0,0x76c1,0x76c3,0x554a,0x963f,0x57c3,
+ 0x6328,0x54ce,0x5509,0x54c0,0x7691,0x764c,0x853c,0x77ee,0x827e,0x788d,
+ 0x7231,0x9698,0x978d,0x6c28,0x5b89,0x4ffa,0x6309,0x6697,0x5cb8,0x80fa,
+ 0x6848,0x80ae,0x6602,0x76ce,0x51f9,0x6556,0x71ac,0x7ff1,0x8884,0x50b2,
+ 0x5965,0x61ca,0x6fb3,0x82ad,0x634c,0x6252,0x53ed,0x5427,0x7b06,0x516b,
+ 0x75a4,0x5df4,0x62d4,0x8dcb,0x9776,0x628a,0x8019,0x575d,0x9738,0x7f62,
+ 0x7238,0x767d,0x67cf,0x767e,0x6446,0x4f70,0x8d25,0x62dc,0x7a17,0x6591,
+ 0x73ed,0x642c,0x6273,0x822c,0x9881,0x677f,0x7248,0x626e,0x62cc,0x4f34,
+ 0x74e3,0x534a,0x529e,0x7eca,0x90a6,0x5e2e,0x6886,0x699c,0x8180,0x7ed1,
+ 0x68d2,0x78c5,0x868c,0x9551,0x508d,0x8c24,0x82de,0x80de,0x5305,0x8912,
+ 0x5265
+ },
+ { /* ku 31 */
+ 0x76c4,0x76c7,0x76c9,0x76cb,0x76cc,0x76d3,0x76d5,0x76d9,0x76da,0x76dc,
+ 0x76dd,0x76de,0x76e0,0x76e1,0x76e2,0x76e3,0x76e4,0x76e6,0x76e7,0x76e8,
+ 0x76e9,0x76ea,0x76eb,0x76ec,0x76ed,0x76f0,0x76f3,0x76f5,0x76f6,0x76f7,
+ 0x76fa,0x76fb,0x76fd,0x76ff,0x7700,0x7702,0x7703,0x7705,0x7706,0x770a,
+ 0x770c,0x770e,0x770f,0x7710,0x7711,0x7712,0x7713,0x7714,0x7715,0x7716,
+ 0x7717,0x7718,0x771b,0x771c,0x771d,0x771e,0x7721,0x7723,0x7724,0x7725,
+ 0x7727,0x772a,0x772b,UBOGON,0x772c,0x772e,0x7730,0x7731,0x7732,0x7733,
+ 0x7734,0x7739,0x773b,0x773d,0x773e,0x773f,0x7742,0x7744,0x7745,0x7746,
+ 0x7748,0x7749,0x774a,0x774b,0x774c,0x774d,0x774e,0x774f,0x7752,0x7753,
+ 0x7754,0x7755,0x7756,0x7757,0x7758,0x7759,0x775c,0x8584,0x96f9,0x4fdd,
+ 0x5821,0x9971,0x5b9d,0x62b1,0x62a5,0x66b4,0x8c79,0x9c8d,0x7206,0x676f,
+ 0x7891,0x60b2,0x5351,0x5317,0x8f88,0x80cc,0x8d1d,0x94a1,0x500d,0x72c8,
+ 0x5907,0x60eb,0x7119,0x88ab,0x5954,0x82ef,0x672c,0x7b28,0x5d29,0x7ef7,
+ 0x752d,0x6cf5,0x8e66,0x8ff8,0x903c,0x9f3b,0x6bd4,0x9119,0x7b14,0x5f7c,
+ 0x78a7,0x84d6,0x853d,0x6bd5,0x6bd9,0x6bd6,0x5e01,0x5e87,0x75f9,0x95ed,
+ 0x655d,0x5f0a,0x5fc5,0x8f9f,0x58c1,0x81c2,0x907f,0x965b,0x97ad,0x8fb9,
+ 0x7f16,0x8d2c,0x6241,0x4fbf,0x53d8,0x535e,0x8fa8,0x8fa9,0x8fab,0x904d,
+ 0x6807,0x5f6a,0x8198,0x8868,0x9cd6,0x618b,0x522b,0x762a,0x5f6c,0x658c,
+ 0x6fd2,0x6ee8,0x5bbe,0x6448,0x5175,0x51b0,0x67c4,0x4e19,0x79c9,0x997c,
+ 0x70b3
+ },
+ { /* ku 32 */
+ 0x775d,0x775e,0x775f,0x7760,0x7764,0x7767,0x7769,0x776a,0x776d,0x776e,
+ 0x776f,0x7770,0x7771,0x7772,0x7773,0x7774,0x7775,0x7776,0x7777,0x7778,
+ 0x777a,0x777b,0x777c,0x7781,0x7782,0x7783,0x7786,0x7787,0x7788,0x7789,
+ 0x778a,0x778b,0x778f,0x7790,0x7793,0x7794,0x7795,0x7796,0x7797,0x7798,
+ 0x7799,0x779a,0x779b,0x779c,0x779d,0x779e,0x77a1,0x77a3,0x77a4,0x77a6,
+ 0x77a8,0x77ab,0x77ad,0x77ae,0x77af,0x77b1,0x77b2,0x77b4,0x77b6,0x77b7,
+ 0x77b8,0x77b9,0x77ba,UBOGON,0x77bc,0x77be,0x77c0,0x77c1,0x77c2,0x77c3,
+ 0x77c4,0x77c5,0x77c6,0x77c7,0x77c8,0x77c9,0x77ca,0x77cb,0x77cc,0x77ce,
+ 0x77cf,0x77d0,0x77d1,0x77d2,0x77d3,0x77d4,0x77d5,0x77d6,0x77d8,0x77d9,
+ 0x77da,0x77dd,0x77de,0x77df,0x77e0,0x77e1,0x77e4,0x75c5,0x5e76,0x73bb,
+ 0x83e0,0x64ad,0x62e8,0x94b5,0x6ce2,0x535a,0x52c3,0x640f,0x94c2,0x7b94,
+ 0x4f2f,0x5e1b,0x8236,0x8116,0x818a,0x6e24,0x6cca,0x9a73,0x6355,0x535c,
+ 0x54fa,0x8865,0x57e0,0x4e0d,0x5e03,0x6b65,0x7c3f,0x90e8,0x6016,0x64e6,
+ 0x731c,0x88c1,0x6750,0x624d,0x8d22,0x776c,0x8e29,0x91c7,0x5f69,0x83dc,
+ 0x8521,0x9910,0x53c2,0x8695,0x6b8b,0x60ed,0x60e8,0x707f,0x82cd,0x8231,
+ 0x4ed3,0x6ca7,0x85cf,0x64cd,0x7cd9,0x69fd,0x66f9,0x8349,0x5395,0x7b56,
+ 0x4fa7,0x518c,0x6d4b,0x5c42,0x8e6d,0x63d2,0x53c9,0x832c,0x8336,0x67e5,
+ 0x78b4,0x643d,0x5bdf,0x5c94,0x5dee,0x8be7,0x62c6,0x67f4,0x8c7a,0x6400,
+ 0x63ba,0x8749,0x998b,0x8c17,0x7f20,0x94f2,0x4ea7,0x9610,0x98a4,0x660c,
+ 0x7316
+ },
+ { /* ku 33 */
+ 0x77e6,0x77e8,0x77ea,0x77ef,0x77f0,0x77f1,0x77f2,0x77f4,0x77f5,0x77f7,
+ 0x77f9,0x77fa,0x77fb,0x77fc,0x7803,0x7804,0x7805,0x7806,0x7807,0x7808,
+ 0x780a,0x780b,0x780e,0x780f,0x7810,0x7813,0x7815,0x7819,0x781b,0x781e,
+ 0x7820,0x7821,0x7822,0x7824,0x7828,0x782a,0x782b,0x782e,0x782f,0x7831,
+ 0x7832,0x7833,0x7835,0x7836,0x783d,0x783f,0x7841,0x7842,0x7843,0x7844,
+ 0x7846,0x7848,0x7849,0x784a,0x784b,0x784d,0x784f,0x7851,0x7853,0x7854,
+ 0x7858,0x7859,0x785a,UBOGON,0x785b,0x785c,0x785e,0x785f,0x7860,0x7861,
+ 0x7862,0x7863,0x7864,0x7865,0x7866,0x7867,0x7868,0x7869,0x786f,0x7870,
+ 0x7871,0x7872,0x7873,0x7874,0x7875,0x7876,0x7878,0x7879,0x787a,0x787b,
+ 0x787d,0x787e,0x787f,0x7880,0x7881,0x7882,0x7883,0x573a,0x5c1d,0x5e38,
+ 0x957f,0x507f,0x80a0,0x5382,0x655e,0x7545,0x5531,0x5021,0x8d85,0x6284,
+ 0x949e,0x671d,0x5632,0x6f6e,0x5de2,0x5435,0x7092,0x8f66,0x626f,0x64a4,
+ 0x63a3,0x5f7b,0x6f88,0x90f4,0x81e3,0x8fb0,0x5c18,0x6668,0x5ff1,0x6c89,
+ 0x9648,0x8d81,0x886c,0x6491,0x79f0,0x57ce,0x6a59,0x6210,0x5448,0x4e58,
+ 0x7a0b,0x60e9,0x6f84,0x8bda,0x627f,0x901e,0x9a8b,0x79e4,0x5403,0x75f4,
+ 0x6301,0x5319,0x6c60,0x8fdf,0x5f1b,0x9a70,0x803b,0x9f7f,0x4f88,0x5c3a,
+ 0x8d64,0x7fc5,0x65a5,0x70bd,0x5145,0x51b2,0x866b,0x5d07,0x5ba0,0x62bd,
+ 0x916c,0x7574,0x8e0c,0x7a20,0x6101,0x7b79,0x4ec7,0x7ef8,0x7785,0x4e11,
+ 0x81ed,0x521d,0x51fa,0x6a71,0x53a8,0x8e87,0x9504,0x96cf,0x6ec1,0x9664,
+ 0x695a
+ },
+ { /* ku 34 */
+ 0x7884,0x7885,0x7886,0x7888,0x788a,0x788b,0x788f,0x7890,0x7892,0x7894,
+ 0x7895,0x7896,0x7899,0x789d,0x789e,0x78a0,0x78a2,0x78a4,0x78a6,0x78a8,
+ 0x78a9,0x78aa,0x78ab,0x78ac,0x78ad,0x78ae,0x78af,0x78b5,0x78b6,0x78b7,
+ 0x78b8,0x78ba,0x78bb,0x78bc,0x78bd,0x78bf,0x78c0,0x78c2,0x78c3,0x78c4,
+ 0x78c6,0x78c7,0x78c8,0x78cc,0x78cd,0x78ce,0x78cf,0x78d1,0x78d2,0x78d3,
+ 0x78d6,0x78d7,0x78d8,0x78da,0x78db,0x78dc,0x78dd,0x78de,0x78df,0x78e0,
+ 0x78e1,0x78e2,0x78e3,UBOGON,0x78e4,0x78e5,0x78e6,0x78e7,0x78e9,0x78ea,
+ 0x78eb,0x78ed,0x78ee,0x78ef,0x78f0,0x78f1,0x78f3,0x78f5,0x78f6,0x78f8,
+ 0x78f9,0x78fb,0x78fc,0x78fd,0x78fe,0x78ff,0x7900,0x7902,0x7903,0x7904,
+ 0x7906,0x7907,0x7908,0x7909,0x790a,0x790b,0x790c,0x7840,0x50a8,0x77d7,
+ 0x6410,0x89e6,0x5904,0x63e3,0x5ddd,0x7a7f,0x693d,0x4f20,0x8239,0x5598,
+ 0x4e32,0x75ae,0x7a97,0x5e62,0x5e8a,0x95ef,0x521b,0x5439,0x708a,0x6376,
+ 0x9524,0x5782,0x6625,0x693f,0x9187,0x5507,0x6df3,0x7eaf,0x8822,0x6233,
+ 0x7ef0,0x75b5,0x8328,0x78c1,0x96cc,0x8f9e,0x6148,0x74f7,0x8bcd,0x6b64,
+ 0x523a,0x8d50,0x6b21,0x806a,0x8471,0x56f1,0x5306,0x4ece,0x4e1b,0x51d1,
+ 0x7c97,0x918b,0x7c07,0x4fc3,0x8e7f,0x7be1,0x7a9c,0x6467,0x5d14,0x50ac,
+ 0x8106,0x7601,0x7cb9,0x6dec,0x7fe0,0x6751,0x5b58,0x5bf8,0x78cb,0x64ae,
+ 0x6413,0x63aa,0x632b,0x9519,0x642d,0x8fbe,0x7b54,0x7629,0x6253,0x5927,
+ 0x5446,0x6b79,0x50a3,0x6234,0x5e26,0x6b86,0x4ee3,0x8d37,0x888b,0x5f85,
+ 0x902e
+ },
+ { /* ku 35 */
+ 0x790d,0x790e,0x790f,0x7910,0x7911,0x7912,0x7914,0x7915,0x7916,0x7917,
+ 0x7918,0x7919,0x791a,0x791b,0x791c,0x791d,0x791f,0x7920,0x7921,0x7922,
+ 0x7923,0x7925,0x7926,0x7927,0x7928,0x7929,0x792a,0x792b,0x792c,0x792d,
+ 0x792e,0x792f,0x7930,0x7931,0x7932,0x7933,0x7935,0x7936,0x7937,0x7938,
+ 0x7939,0x793d,0x793f,0x7942,0x7943,0x7944,0x7945,0x7947,0x794a,0x794b,
+ 0x794c,0x794d,0x794e,0x794f,0x7950,0x7951,0x7952,0x7954,0x7955,0x7958,
+ 0x7959,0x7961,0x7963,UBOGON,0x7964,0x7966,0x7969,0x796a,0x796b,0x796c,
+ 0x796e,0x7970,0x7971,0x7972,0x7973,0x7974,0x7975,0x7976,0x7979,0x797b,
+ 0x797c,0x797d,0x797e,0x797f,0x7982,0x7983,0x7986,0x7987,0x7988,0x7989,
+ 0x798b,0x798c,0x798d,0x798e,0x7990,0x7991,0x7992,0x6020,0x803d,0x62c5,
+ 0x4e39,0x5355,0x90f8,0x63b8,0x80c6,0x65e6,0x6c2e,0x4f46,0x60ee,0x6de1,
+ 0x8bde,0x5f39,0x86cb,0x5f53,0x6321,0x515a,0x8361,0x6863,0x5200,0x6363,
+ 0x8e48,0x5012,0x5c9b,0x7977,0x5bfc,0x5230,0x7a3b,0x60bc,0x9053,0x76d7,
+ 0x5fb7,0x5f97,0x7684,0x8e6c,0x706f,0x767b,0x7b49,0x77aa,0x51f3,0x9093,
+ 0x5824,0x4f4e,0x6ef4,0x8fea,0x654c,0x7b1b,0x72c4,0x6da4,0x7fdf,0x5ae1,
+ 0x62b5,0x5e95,0x5730,0x8482,0x7b2c,0x5e1d,0x5f1f,0x9012,0x7f14,0x98a0,
+ 0x6382,0x6ec7,0x7898,0x70b9,0x5178,0x975b,0x57ab,0x7535,0x4f43,0x7538,
+ 0x5e97,0x60e6,0x5960,0x6dc0,0x6bbf,0x7889,0x53fc,0x96d5,0x51cb,0x5201,
+ 0x6389,0x540a,0x9493,0x8c03,0x8dcc,0x7239,0x789f,0x8776,0x8fed,0x8c0d,
+ 0x53e0
+ },
+ { /* ku 36 */
+ 0x7993,0x7994,0x7995,0x7996,0x7997,0x7998,0x7999,0x799b,0x799c,0x799d,
+ 0x799e,0x799f,0x79a0,0x79a1,0x79a2,0x79a3,0x79a4,0x79a5,0x79a6,0x79a8,
+ 0x79a9,0x79aa,0x79ab,0x79ac,0x79ad,0x79ae,0x79af,0x79b0,0x79b1,0x79b2,
+ 0x79b4,0x79b5,0x79b6,0x79b7,0x79b8,0x79bc,0x79bf,0x79c2,0x79c4,0x79c5,
+ 0x79c7,0x79c8,0x79ca,0x79cc,0x79ce,0x79cf,0x79d0,0x79d3,0x79d4,0x79d6,
+ 0x79d7,0x79d9,0x79da,0x79db,0x79dc,0x79dd,0x79de,0x79e0,0x79e1,0x79e2,
+ 0x79e5,0x79e8,0x79ea,UBOGON,0x79ec,0x79ee,0x79f1,0x79f2,0x79f3,0x79f4,
+ 0x79f5,0x79f6,0x79f7,0x79f9,0x79fa,0x79fc,0x79fe,0x79ff,0x7a01,0x7a04,
+ 0x7a05,0x7a07,0x7a08,0x7a09,0x7a0a,0x7a0c,0x7a0f,0x7a10,0x7a11,0x7a12,
+ 0x7a13,0x7a15,0x7a16,0x7a18,0x7a19,0x7a1b,0x7a1c,0x4e01,0x76ef,0x53ee,
+ 0x9489,0x9876,0x9f0e,0x952d,0x5b9a,0x8ba2,0x4e22,0x4e1c,0x51ac,0x8463,
+ 0x61c2,0x52a8,0x680b,0x4f97,0x606b,0x51bb,0x6d1e,0x515c,0x6296,0x6597,
+ 0x9661,0x8c46,0x9017,0x75d8,0x90fd,0x7763,0x6bd2,0x728a,0x72ec,0x8bfb,
+ 0x5835,0x7779,0x8d4c,0x675c,0x9540,0x809a,0x5ea6,0x6e21,0x5992,0x7aef,
+ 0x77ed,0x953b,0x6bb5,0x65ad,0x7f0e,0x5806,0x5151,0x961f,0x5bf9,0x58a9,
+ 0x5428,0x8e72,0x6566,0x987f,0x56e4,0x949d,0x76fe,0x9041,0x6387,0x54c6,
+ 0x591a,0x593a,0x579b,0x8eb2,0x6735,0x8dfa,0x8235,0x5241,0x60f0,0x5815,
+ 0x86fe,0x5ce8,0x9e45,0x4fc4,0x989d,0x8bb9,0x5a25,0x6076,0x5384,0x627c,
+ 0x904f,0x9102,0x997f,0x6069,0x800c,0x513f,0x8033,0x5c14,0x9975,0x6d31,
+ 0x4e8c
+ },
+ { /* ku 37 */
+ 0x7a1d,0x7a1f,0x7a21,0x7a22,0x7a24,0x7a25,0x7a26,0x7a27,0x7a28,0x7a29,
+ 0x7a2a,0x7a2b,0x7a2c,0x7a2d,0x7a2e,0x7a2f,0x7a30,0x7a31,0x7a32,0x7a34,
+ 0x7a35,0x7a36,0x7a38,0x7a3a,0x7a3e,0x7a40,0x7a41,0x7a42,0x7a43,0x7a44,
+ 0x7a45,0x7a47,0x7a48,0x7a49,0x7a4a,0x7a4b,0x7a4c,0x7a4d,0x7a4e,0x7a4f,
+ 0x7a50,0x7a52,0x7a53,0x7a54,0x7a55,0x7a56,0x7a58,0x7a59,0x7a5a,0x7a5b,
+ 0x7a5c,0x7a5d,0x7a5e,0x7a5f,0x7a60,0x7a61,0x7a62,0x7a63,0x7a64,0x7a65,
+ 0x7a66,0x7a67,0x7a68,UBOGON,0x7a69,0x7a6a,0x7a6b,0x7a6c,0x7a6d,0x7a6e,
+ 0x7a6f,0x7a71,0x7a72,0x7a73,0x7a75,0x7a7b,0x7a7c,0x7a7d,0x7a7e,0x7a82,
+ 0x7a85,0x7a87,0x7a89,0x7a8a,0x7a8b,0x7a8c,0x7a8e,0x7a8f,0x7a90,0x7a93,
+ 0x7a94,0x7a99,0x7a9a,0x7a9b,0x7a9e,0x7aa1,0x7aa2,0x8d30,0x53d1,0x7f5a,
+ 0x7b4f,0x4f10,0x4e4f,0x9600,0x6cd5,0x73d0,0x85e9,0x5e06,0x756a,0x7ffb,
+ 0x6a0a,0x77fe,0x9492,0x7e41,0x51e1,0x70e6,0x53cd,0x8fd4,0x8303,0x8d29,
+ 0x72af,0x996d,0x6cdb,0x574a,0x82b3,0x65b9,0x80aa,0x623f,0x9632,0x59a8,
+ 0x4eff,0x8bbf,0x7eba,0x653e,0x83f2,0x975e,0x5561,0x98de,0x80a5,0x532a,
+ 0x8bfd,0x5420,0x80ba,0x5e9f,0x6cb8,0x8d39,0x82ac,0x915a,0x5429,0x6c1b,
+ 0x5206,0x7eb7,0x575f,0x711a,0x6c7e,0x7c89,0x594b,0x4efd,0x5fff,0x6124,
+ 0x7caa,0x4e30,0x5c01,0x67ab,0x8702,0x5cf0,0x950b,0x98ce,0x75af,0x70fd,
+ 0x9022,0x51af,0x7f1d,0x8bbd,0x5949,0x51e4,0x4f5b,0x5426,0x592b,0x6577,
+ 0x80a4,0x5b75,0x6276,0x62c2,0x8f90,0x5e45,0x6c1f,0x7b26,0x4f0f,0x4fd8,
+ 0x670d
+ },
+ { /* ku 38 */
+ 0x7aa3,0x7aa4,0x7aa7,0x7aa9,0x7aaa,0x7aab,0x7aae,0x7aaf,0x7ab0,0x7ab1,
+ 0x7ab2,0x7ab4,0x7ab5,0x7ab6,0x7ab7,0x7ab8,0x7ab9,0x7aba,0x7abb,0x7abc,
+ 0x7abd,0x7abe,0x7ac0,0x7ac1,0x7ac2,0x7ac3,0x7ac4,0x7ac5,0x7ac6,0x7ac7,
+ 0x7ac8,0x7ac9,0x7aca,0x7acc,0x7acd,0x7ace,0x7acf,0x7ad0,0x7ad1,0x7ad2,
+ 0x7ad3,0x7ad4,0x7ad5,0x7ad7,0x7ad8,0x7ada,0x7adb,0x7adc,0x7add,0x7ae1,
+ 0x7ae2,0x7ae4,0x7ae7,0x7ae8,0x7ae9,0x7aea,0x7aeb,0x7aec,0x7aee,0x7af0,
+ 0x7af1,0x7af2,0x7af3,UBOGON,0x7af4,0x7af5,0x7af6,0x7af7,0x7af8,0x7afb,
+ 0x7afc,0x7afe,0x7b00,0x7b01,0x7b02,0x7b05,0x7b07,0x7b09,0x7b0c,0x7b0d,
+ 0x7b0e,0x7b10,0x7b12,0x7b13,0x7b16,0x7b17,0x7b18,0x7b1a,0x7b1c,0x7b1d,
+ 0x7b1f,0x7b21,0x7b22,0x7b23,0x7b27,0x7b29,0x7b2d,0x6d6e,0x6daa,0x798f,
+ 0x88b1,0x5f17,0x752b,0x629a,0x8f85,0x4fef,0x91dc,0x65a7,0x812f,0x8151,
+ 0x5e9c,0x8150,0x8d74,0x526f,0x8986,0x8d4b,0x590d,0x5085,0x4ed8,0x961c,
+ 0x7236,0x8179,0x8d1f,0x5bcc,0x8ba3,0x9644,0x5987,0x7f1a,0x5490,0x5676,
+ 0x560e,0x8be5,0x6539,0x6982,0x9499,0x76d6,0x6e89,0x5e72,0x7518,0x6746,
+ 0x67d1,0x7aff,0x809d,0x8d76,0x611f,0x79c6,0x6562,0x8d63,0x5188,0x521a,
+ 0x94a2,0x7f38,0x809b,0x7eb2,0x5c97,0x6e2f,0x6760,0x7bd9,0x768b,0x9ad8,
+ 0x818f,0x7f94,0x7cd5,0x641e,0x9550,0x7a3f,0x544a,0x54e5,0x6b4c,0x6401,
+ 0x6208,0x9e3d,0x80f3,0x7599,0x5272,0x9769,0x845b,0x683c,0x86e4,0x9601,
+ 0x9694,0x94ec,0x4e2a,0x5404,0x7ed9,0x6839,0x8ddf,0x8015,0x66f4,0x5e9a,
+ 0x7fb9
+ },
+ { /* ku 39 */
+ 0x7b2f,0x7b30,0x7b32,0x7b34,0x7b35,0x7b36,0x7b37,0x7b39,0x7b3b,0x7b3d,
+ 0x7b3f,0x7b40,0x7b41,0x7b42,0x7b43,0x7b44,0x7b46,0x7b48,0x7b4a,0x7b4d,
+ 0x7b4e,0x7b53,0x7b55,0x7b57,0x7b59,0x7b5c,0x7b5e,0x7b5f,0x7b61,0x7b63,
+ 0x7b64,0x7b65,0x7b66,0x7b67,0x7b68,0x7b69,0x7b6a,0x7b6b,0x7b6c,0x7b6d,
+ 0x7b6f,0x7b70,0x7b73,0x7b74,0x7b76,0x7b78,0x7b7a,0x7b7c,0x7b7d,0x7b7f,
+ 0x7b81,0x7b82,0x7b83,0x7b84,0x7b86,0x7b87,0x7b88,0x7b89,0x7b8a,0x7b8b,
+ 0x7b8c,0x7b8e,0x7b8f,UBOGON,0x7b91,0x7b92,0x7b93,0x7b96,0x7b98,0x7b99,
+ 0x7b9a,0x7b9b,0x7b9e,0x7b9f,0x7ba0,0x7ba3,0x7ba4,0x7ba5,0x7bae,0x7baf,
+ 0x7bb0,0x7bb2,0x7bb3,0x7bb5,0x7bb6,0x7bb7,0x7bb9,0x7bba,0x7bbb,0x7bbc,
+ 0x7bbd,0x7bbe,0x7bbf,0x7bc0,0x7bc2,0x7bc3,0x7bc4,0x57c2,0x803f,0x6897,
+ 0x5de5,0x653b,0x529f,0x606d,0x9f9a,0x4f9b,0x8eac,0x516c,0x5bab,0x5f13,
+ 0x5de9,0x6c5e,0x62f1,0x8d21,0x5171,0x94a9,0x52fe,0x6c9f,0x82df,0x72d7,
+ 0x57a2,0x6784,0x8d2d,0x591f,0x8f9c,0x83c7,0x5495,0x7b8d,0x4f30,0x6cbd,
+ 0x5b64,0x59d1,0x9f13,0x53e4,0x86ca,0x9aa8,0x8c37,0x80a1,0x6545,0x987e,
+ 0x56fa,0x96c7,0x522e,0x74dc,0x5250,0x5be1,0x6302,0x8902,0x4e56,0x62d0,
+ 0x602a,0x68fa,0x5173,0x5b98,0x51a0,0x89c2,0x7ba1,0x9986,0x7f50,0x60ef,
+ 0x704c,0x8d2f,0x5149,0x5e7f,0x901b,0x7470,0x89c4,0x572d,0x7845,0x5f52,
+ 0x9f9f,0x95fa,0x8f68,0x9b3c,0x8be1,0x7678,0x6842,0x67dc,0x8dea,0x8d35,
+ 0x523d,0x8f8a,0x6eda,0x68cd,0x9505,0x90ed,0x56fd,0x679c,0x88f9,0x8fc7,
+ 0x54c8
+ },
+ { /* ku 3a */
+ 0x7bc5,0x7bc8,0x7bc9,0x7bca,0x7bcb,0x7bcd,0x7bce,0x7bcf,0x7bd0,0x7bd2,
+ 0x7bd4,0x7bd5,0x7bd6,0x7bd7,0x7bd8,0x7bdb,0x7bdc,0x7bde,0x7bdf,0x7be0,
+ 0x7be2,0x7be3,0x7be4,0x7be7,0x7be8,0x7be9,0x7beb,0x7bec,0x7bed,0x7bef,
+ 0x7bf0,0x7bf2,0x7bf3,0x7bf4,0x7bf5,0x7bf6,0x7bf8,0x7bf9,0x7bfa,0x7bfb,
+ 0x7bfd,0x7bff,0x7c00,0x7c01,0x7c02,0x7c03,0x7c04,0x7c05,0x7c06,0x7c08,
+ 0x7c09,0x7c0a,0x7c0d,0x7c0e,0x7c10,0x7c11,0x7c12,0x7c13,0x7c14,0x7c15,
+ 0x7c17,0x7c18,0x7c19,UBOGON,0x7c1a,0x7c1b,0x7c1c,0x7c1d,0x7c1e,0x7c20,
+ 0x7c21,0x7c22,0x7c23,0x7c24,0x7c25,0x7c28,0x7c29,0x7c2b,0x7c2c,0x7c2d,
+ 0x7c2e,0x7c2f,0x7c30,0x7c31,0x7c32,0x7c33,0x7c34,0x7c35,0x7c36,0x7c37,
+ 0x7c39,0x7c3a,0x7c3b,0x7c3c,0x7c3d,0x7c3e,0x7c42,0x9ab8,0x5b69,0x6d77,
+ 0x6c26,0x4ea5,0x5bb3,0x9a87,0x9163,0x61a8,0x90af,0x97e9,0x542b,0x6db5,
+ 0x5bd2,0x51fd,0x558a,0x7f55,0x7ff0,0x64bc,0x634d,0x65f1,0x61be,0x608d,
+ 0x710a,0x6c57,0x6c49,0x592f,0x676d,0x822a,0x58d5,0x568e,0x8c6a,0x6beb,
+ 0x90dd,0x597d,0x8017,0x53f7,0x6d69,0x5475,0x559d,0x8377,0x83cf,0x6838,
+ 0x79be,0x548c,0x4f55,0x5408,0x76d2,0x8c89,0x9602,0x6cb3,0x6db8,0x8d6b,
+ 0x8910,0x9e64,0x8d3a,0x563f,0x9ed1,0x75d5,0x5f88,0x72e0,0x6068,0x54fc,
+ 0x4ea8,0x6a2a,0x8861,0x6052,0x8f70,0x54c4,0x70d8,0x8679,0x9e3f,0x6d2a,
+ 0x5b8f,0x5f18,0x7ea2,0x5589,0x4faf,0x7334,0x543c,0x539a,0x5019,0x540e,
+ 0x547c,0x4e4e,0x5ffd,0x745a,0x58f6,0x846b,0x80e1,0x8774,0x72d0,0x7cca,
+ 0x6e56
+ },
+ { /* ku 3b */
+ 0x7c43,0x7c44,0x7c45,0x7c46,0x7c47,0x7c48,0x7c49,0x7c4a,0x7c4b,0x7c4c,
+ 0x7c4e,0x7c4f,0x7c50,0x7c51,0x7c52,0x7c53,0x7c54,0x7c55,0x7c56,0x7c57,
+ 0x7c58,0x7c59,0x7c5a,0x7c5b,0x7c5c,0x7c5d,0x7c5e,0x7c5f,0x7c60,0x7c61,
+ 0x7c62,0x7c63,0x7c64,0x7c65,0x7c66,0x7c67,0x7c68,0x7c69,0x7c6a,0x7c6b,
+ 0x7c6c,0x7c6d,0x7c6e,0x7c6f,0x7c70,0x7c71,0x7c72,0x7c75,0x7c76,0x7c77,
+ 0x7c78,0x7c79,0x7c7a,0x7c7e,0x7c7f,0x7c80,0x7c81,0x7c82,0x7c83,0x7c84,
+ 0x7c85,0x7c86,0x7c87,UBOGON,0x7c88,0x7c8a,0x7c8b,0x7c8c,0x7c8d,0x7c8e,
+ 0x7c8f,0x7c90,0x7c93,0x7c94,0x7c96,0x7c99,0x7c9a,0x7c9b,0x7ca0,0x7ca1,
+ 0x7ca3,0x7ca6,0x7ca7,0x7ca8,0x7ca9,0x7cab,0x7cac,0x7cad,0x7caf,0x7cb0,
+ 0x7cb4,0x7cb5,0x7cb6,0x7cb7,0x7cb8,0x7cba,0x7cbb,0x5f27,0x864e,0x552c,
+ 0x62a4,0x4e92,0x6caa,0x6237,0x82b1,0x54d7,0x534e,0x733e,0x6ed1,0x753b,
+ 0x5212,0x5316,0x8bdd,0x69d0,0x5f8a,0x6000,0x6dee,0x574f,0x6b22,0x73af,
+ 0x6853,0x8fd8,0x7f13,0x6362,0x60a3,0x5524,0x75ea,0x8c62,0x7115,0x6da3,
+ 0x5ba6,0x5e7b,0x8352,0x614c,0x9ec4,0x78fa,0x8757,0x7c27,0x7687,0x51f0,
+ 0x60f6,0x714c,0x6643,0x5e4c,0x604d,0x8c0e,0x7070,0x6325,0x8f89,0x5fbd,
+ 0x6062,0x86d4,0x56de,0x6bc1,0x6094,0x6167,0x5349,0x60e0,0x6666,0x8d3f,
+ 0x79fd,0x4f1a,0x70e9,0x6c47,0x8bb3,0x8bf2,0x7ed8,0x8364,0x660f,0x5a5a,
+ 0x9b42,0x6d51,0x6df7,0x8c41,0x6d3b,0x4f19,0x706b,0x83b7,0x6216,0x60d1,
+ 0x970d,0x8d27,0x7978,0x51fb,0x573e,0x57fa,0x673a,0x7578,0x7a3d,0x79ef,
+ 0x7b95
+ },
+ { /* ku 3c */
+ 0x7cbf,0x7cc0,0x7cc2,0x7cc3,0x7cc4,0x7cc6,0x7cc9,0x7ccb,0x7cce,0x7ccf,
+ 0x7cd0,0x7cd1,0x7cd2,0x7cd3,0x7cd4,0x7cd8,0x7cda,0x7cdb,0x7cdd,0x7cde,
+ 0x7ce1,0x7ce2,0x7ce3,0x7ce4,0x7ce5,0x7ce6,0x7ce7,0x7ce9,0x7cea,0x7ceb,
+ 0x7cec,0x7ced,0x7cee,0x7cf0,0x7cf1,0x7cf2,0x7cf3,0x7cf4,0x7cf5,0x7cf6,
+ 0x7cf7,0x7cf9,0x7cfa,0x7cfc,0x7cfd,0x7cfe,0x7cff,0x7d00,0x7d01,0x7d02,
+ 0x7d03,0x7d04,0x7d05,0x7d06,0x7d07,0x7d08,0x7d09,0x7d0b,0x7d0c,0x7d0d,
+ 0x7d0e,0x7d0f,0x7d10,UBOGON,0x7d11,0x7d12,0x7d13,0x7d14,0x7d15,0x7d16,
+ 0x7d17,0x7d18,0x7d19,0x7d1a,0x7d1b,0x7d1c,0x7d1d,0x7d1e,0x7d1f,0x7d21,
+ 0x7d23,0x7d24,0x7d25,0x7d26,0x7d28,0x7d29,0x7d2a,0x7d2c,0x7d2d,0x7d2e,
+ 0x7d30,0x7d31,0x7d32,0x7d33,0x7d34,0x7d35,0x7d36,0x808c,0x9965,0x8ff9,
+ 0x6fc0,0x8ba5,0x9e21,0x59ec,0x7ee9,0x7f09,0x5409,0x6781,0x68d8,0x8f91,
+ 0x7c4d,0x96c6,0x53ca,0x6025,0x75be,0x6c72,0x5373,0x5ac9,0x7ea7,0x6324,
+ 0x51e0,0x810a,0x5df1,0x84df,0x6280,0x5180,0x5b63,0x4f0e,0x796d,0x5242,
+ 0x60b8,0x6d4e,0x5bc4,0x5bc2,0x8ba1,0x8bb0,0x65e2,0x5fcc,0x9645,0x5993,
+ 0x7ee7,0x7eaa,0x5609,0x67b7,0x5939,0x4f73,0x5bb6,0x52a0,0x835a,0x988a,
+ 0x8d3e,0x7532,0x94be,0x5047,0x7a3c,0x4ef7,0x67b6,0x9a7e,0x5ac1,0x6b7c,
+ 0x76d1,0x575a,0x5c16,0x7b3a,0x95f4,0x714e,0x517c,0x80a9,0x8270,0x5978,
+ 0x7f04,0x8327,0x68c0,0x67ec,0x78b1,0x7877,0x62e3,0x6361,0x7b80,0x4fed,
+ 0x526a,0x51cf,0x8350,0x69db,0x9274,0x8df5,0x8d31,0x89c1,0x952e,0x7bad,
+ 0x4ef6
+ },
+ { /* ku 3d */
+ 0x7d37,0x7d38,0x7d39,0x7d3a,0x7d3b,0x7d3c,0x7d3d,0x7d3e,0x7d3f,0x7d40,
+ 0x7d41,0x7d42,0x7d43,0x7d44,0x7d45,0x7d46,0x7d47,0x7d48,0x7d49,0x7d4a,
+ 0x7d4b,0x7d4c,0x7d4d,0x7d4e,0x7d4f,0x7d50,0x7d51,0x7d52,0x7d53,0x7d54,
+ 0x7d55,0x7d56,0x7d57,0x7d58,0x7d59,0x7d5a,0x7d5b,0x7d5c,0x7d5d,0x7d5e,
+ 0x7d5f,0x7d60,0x7d61,0x7d62,0x7d63,0x7d64,0x7d65,0x7d66,0x7d67,0x7d68,
+ 0x7d69,0x7d6a,0x7d6b,0x7d6c,0x7d6d,0x7d6f,0x7d70,0x7d71,0x7d72,0x7d73,
+ 0x7d74,0x7d75,0x7d76,UBOGON,0x7d78,0x7d79,0x7d7a,0x7d7b,0x7d7c,0x7d7d,
+ 0x7d7e,0x7d7f,0x7d80,0x7d81,0x7d82,0x7d83,0x7d84,0x7d85,0x7d86,0x7d87,
+ 0x7d88,0x7d89,0x7d8a,0x7d8b,0x7d8c,0x7d8d,0x7d8e,0x7d8f,0x7d90,0x7d91,
+ 0x7d92,0x7d93,0x7d94,0x7d95,0x7d96,0x7d97,0x7d98,0x5065,0x8230,0x5251,
+ 0x996f,0x6e10,0x6e85,0x6da7,0x5efa,0x50f5,0x59dc,0x5c06,0x6d46,0x6c5f,
+ 0x7586,0x848b,0x6868,0x5956,0x8bb2,0x5320,0x9171,0x964d,0x8549,0x6912,
+ 0x7901,0x7126,0x80f6,0x4ea4,0x90ca,0x6d47,0x9a84,0x5a07,0x56bc,0x6405,
+ 0x94f0,0x77eb,0x4fa5,0x811a,0x72e1,0x89d2,0x997a,0x7f34,0x7ede,0x527f,
+ 0x6559,0x9175,0x8f7f,0x8f83,0x53eb,0x7a96,0x63ed,0x63a5,0x7686,0x79f8,
+ 0x8857,0x9636,0x622a,0x52ab,0x8282,0x6854,0x6770,0x6377,0x776b,0x7aed,
+ 0x6d01,0x7ed3,0x89e3,0x59d0,0x6212,0x85c9,0x82a5,0x754c,0x501f,0x4ecb,
+ 0x75a5,0x8beb,0x5c4a,0x5dfe,0x7b4b,0x65a4,0x91d1,0x4eca,0x6d25,0x895f,
+ 0x7d27,0x9526,0x4ec5,0x8c28,0x8fdb,0x9773,0x664b,0x7981,0x8fd1,0x70ec,
+ 0x6d78
+ },
+ { /* ku 3e */
+ 0x7d99,0x7d9a,0x7d9b,0x7d9c,0x7d9d,0x7d9e,0x7d9f,0x7da0,0x7da1,0x7da2,
+ 0x7da3,0x7da4,0x7da5,0x7da7,0x7da8,0x7da9,0x7daa,0x7dab,0x7dac,0x7dad,
+ 0x7daf,0x7db0,0x7db1,0x7db2,0x7db3,0x7db4,0x7db5,0x7db6,0x7db7,0x7db8,
+ 0x7db9,0x7dba,0x7dbb,0x7dbc,0x7dbd,0x7dbe,0x7dbf,0x7dc0,0x7dc1,0x7dc2,
+ 0x7dc3,0x7dc4,0x7dc5,0x7dc6,0x7dc7,0x7dc8,0x7dc9,0x7dca,0x7dcb,0x7dcc,
+ 0x7dcd,0x7dce,0x7dcf,0x7dd0,0x7dd1,0x7dd2,0x7dd3,0x7dd4,0x7dd5,0x7dd6,
+ 0x7dd7,0x7dd8,0x7dd9,UBOGON,0x7dda,0x7ddb,0x7ddc,0x7ddd,0x7dde,0x7ddf,
+ 0x7de0,0x7de1,0x7de2,0x7de3,0x7de4,0x7de5,0x7de6,0x7de7,0x7de8,0x7de9,
+ 0x7dea,0x7deb,0x7dec,0x7ded,0x7dee,0x7def,0x7df0,0x7df1,0x7df2,0x7df3,
+ 0x7df4,0x7df5,0x7df6,0x7df7,0x7df8,0x7df9,0x7dfa,0x5c3d,0x52b2,0x8346,
+ 0x5162,0x830e,0x775b,0x6676,0x9cb8,0x4eac,0x60ca,0x7cbe,0x7cb3,0x7ecf,
+ 0x4e95,0x8b66,0x666f,0x9888,0x9759,0x5883,0x656c,0x955c,0x5f84,0x75c9,
+ 0x9756,0x7adf,0x7ade,0x51c0,0x70af,0x7a98,0x63ea,0x7a76,0x7ea0,0x7396,
+ 0x97ed,0x4e45,0x7078,0x4e5d,0x9152,0x53a9,0x6551,0x65e7,0x81fc,0x8205,
+ 0x548e,0x5c31,0x759a,0x97a0,0x62d8,0x72d9,0x75bd,0x5c45,0x9a79,0x83ca,
+ 0x5c40,0x5480,0x77e9,0x4e3e,0x6cae,0x805a,0x62d2,0x636e,0x5de8,0x5177,
+ 0x8ddd,0x8e1e,0x952f,0x4ff1,0x53e5,0x60e7,0x70ac,0x5267,0x6350,0x9e43,
+ 0x5a1f,0x5026,0x7737,0x5377,0x7ee2,0x6485,0x652b,0x6289,0x6398,0x5014,
+ 0x7235,0x89c9,0x51b3,0x8bc0,0x7edd,0x5747,0x83cc,0x94a7,0x519b,0x541b,
+ 0x5cfb
+ },
+ { /* ku 3f */
+ 0x7dfb,0x7dfc,0x7dfd,0x7dfe,0x7dff,0x7e00,0x7e01,0x7e02,0x7e03,0x7e04,
+ 0x7e05,0x7e06,0x7e07,0x7e08,0x7e09,0x7e0a,0x7e0b,0x7e0c,0x7e0d,0x7e0e,
+ 0x7e0f,0x7e10,0x7e11,0x7e12,0x7e13,0x7e14,0x7e15,0x7e16,0x7e17,0x7e18,
+ 0x7e19,0x7e1a,0x7e1b,0x7e1c,0x7e1d,0x7e1e,0x7e1f,0x7e20,0x7e21,0x7e22,
+ 0x7e23,0x7e24,0x7e25,0x7e26,0x7e27,0x7e28,0x7e29,0x7e2a,0x7e2b,0x7e2c,
+ 0x7e2d,0x7e2e,0x7e2f,0x7e30,0x7e31,0x7e32,0x7e33,0x7e34,0x7e35,0x7e36,
+ 0x7e37,0x7e38,0x7e39,UBOGON,0x7e3a,0x7e3c,0x7e3d,0x7e3e,0x7e3f,0x7e40,
+ 0x7e42,0x7e43,0x7e44,0x7e45,0x7e46,0x7e48,0x7e49,0x7e4a,0x7e4b,0x7e4c,
+ 0x7e4d,0x7e4e,0x7e4f,0x7e50,0x7e51,0x7e52,0x7e53,0x7e54,0x7e55,0x7e56,
+ 0x7e57,0x7e58,0x7e59,0x7e5a,0x7e5b,0x7e5c,0x7e5d,0x4fca,0x7ae3,0x6d5a,
+ 0x90e1,0x9a8f,0x5580,0x5496,0x5361,0x54af,0x5f00,0x63e9,0x6977,0x51ef,
+ 0x6168,0x520a,0x582a,0x52d8,0x574e,0x780d,0x770b,0x5eb7,0x6177,0x7ce0,
+ 0x625b,0x6297,0x4ea2,0x7095,0x8003,0x62f7,0x70e4,0x9760,0x5777,0x82db,
+ 0x67ef,0x68f5,0x78d5,0x9897,0x79d1,0x58f3,0x54b3,0x53ef,0x6e34,0x514b,
+ 0x523b,0x5ba2,0x8bfe,0x80af,0x5543,0x57a6,0x6073,0x5751,0x542d,0x7a7a,
+ 0x6050,0x5b54,0x63a7,0x62a0,0x53e3,0x6263,0x5bc7,0x67af,0x54ed,0x7a9f,
+ 0x82e6,0x9177,0x5e93,0x88e4,0x5938,0x57ae,0x630e,0x8de8,0x80ef,0x5757,
+ 0x7b77,0x4fa9,0x5feb,0x5bbd,0x6b3e,0x5321,0x7b50,0x72c2,0x6846,0x77ff,
+ 0x7736,0x65f7,0x51b5,0x4e8f,0x76d4,0x5cbf,0x7aa5,0x8475,0x594e,0x9b41,
+ 0x5080
+ },
+ { /* ku 40 */
+ 0x7e5e,0x7e5f,0x7e60,0x7e61,0x7e62,0x7e63,0x7e64,0x7e65,0x7e66,0x7e67,
+ 0x7e68,0x7e69,0x7e6a,0x7e6b,0x7e6c,0x7e6d,0x7e6e,0x7e6f,0x7e70,0x7e71,
+ 0x7e72,0x7e73,0x7e74,0x7e75,0x7e76,0x7e77,0x7e78,0x7e79,0x7e7a,0x7e7b,
+ 0x7e7c,0x7e7d,0x7e7e,0x7e7f,0x7e80,0x7e81,0x7e83,0x7e84,0x7e85,0x7e86,
+ 0x7e87,0x7e88,0x7e89,0x7e8a,0x7e8b,0x7e8c,0x7e8d,0x7e8e,0x7e8f,0x7e90,
+ 0x7e91,0x7e92,0x7e93,0x7e94,0x7e95,0x7e96,0x7e97,0x7e98,0x7e99,0x7e9a,
+ 0x7e9c,0x7e9d,0x7e9e,UBOGON,0x7eae,0x7eb4,0x7ebb,0x7ebc,0x7ed6,0x7ee4,
+ 0x7eec,0x7ef9,0x7f0a,0x7f10,0x7f1e,0x7f37,0x7f39,0x7f3b,0x7f3c,0x7f3d,
+ 0x7f3e,0x7f3f,0x7f40,0x7f41,0x7f43,0x7f46,0x7f47,0x7f48,0x7f49,0x7f4a,
+ 0x7f4b,0x7f4c,0x7f4d,0x7f4e,0x7f4f,0x7f52,0x7f53,0x9988,0x6127,0x6e83,
+ 0x5764,0x6606,0x6346,0x56f0,0x62ec,0x6269,0x5ed3,0x9614,0x5783,0x62c9,
+ 0x5587,0x8721,0x814a,0x8fa3,0x5566,0x83b1,0x6765,0x8d56,0x84dd,0x5a6a,
+ 0x680f,0x62e6,0x7bee,0x9611,0x5170,0x6f9c,0x8c30,0x63fd,0x89c8,0x61d2,
+ 0x7f06,0x70c2,0x6ee5,0x7405,0x6994,0x72fc,0x5eca,0x90ce,0x6717,0x6d6a,
+ 0x635e,0x52b3,0x7262,0x8001,0x4f6c,0x59e5,0x916a,0x70d9,0x6d9d,0x52d2,
+ 0x4e50,0x96f7,0x956d,0x857e,0x78ca,0x7d2f,0x5121,0x5792,0x64c2,0x808b,
+ 0x7c7b,0x6cea,0x68f1,0x695e,0x51b7,0x5398,0x68a8,0x7281,0x9ece,0x7bf1,
+ 0x72f8,0x79bb,0x6f13,0x7406,0x674e,0x91cc,0x9ca4,0x793c,0x8389,0x8354,
+ 0x540f,0x6817,0x4e3d,0x5389,0x52b1,0x783e,0x5386,0x5229,0x5088,0x4f8b,
+ 0x4fd0
+ },
+ { /* ku 41 */
+ 0x7f56,0x7f59,0x7f5b,0x7f5c,0x7f5d,0x7f5e,0x7f60,0x7f63,0x7f64,0x7f65,
+ 0x7f66,0x7f67,0x7f6b,0x7f6c,0x7f6d,0x7f6f,0x7f70,0x7f73,0x7f75,0x7f76,
+ 0x7f77,0x7f78,0x7f7a,0x7f7b,0x7f7c,0x7f7d,0x7f7f,0x7f80,0x7f82,0x7f83,
+ 0x7f84,0x7f85,0x7f86,0x7f87,0x7f88,0x7f89,0x7f8b,0x7f8d,0x7f8f,0x7f90,
+ 0x7f91,0x7f92,0x7f93,0x7f95,0x7f96,0x7f97,0x7f98,0x7f99,0x7f9b,0x7f9c,
+ 0x7fa0,0x7fa2,0x7fa3,0x7fa5,0x7fa6,0x7fa8,0x7fa9,0x7faa,0x7fab,0x7fac,
+ 0x7fad,0x7fae,0x7fb1,UBOGON,0x7fb3,0x7fb4,0x7fb5,0x7fb6,0x7fb7,0x7fba,
+ 0x7fbb,0x7fbe,0x7fc0,0x7fc2,0x7fc3,0x7fc4,0x7fc6,0x7fc7,0x7fc8,0x7fc9,
+ 0x7fcb,0x7fcd,0x7fcf,0x7fd0,0x7fd1,0x7fd2,0x7fd3,0x7fd6,0x7fd7,0x7fd9,
+ 0x7fda,0x7fdb,0x7fdc,0x7fdd,0x7fde,0x7fe2,0x7fe3,0x75e2,0x7acb,0x7c92,
+ 0x6ca5,0x96b6,0x529b,0x7483,0x54e9,0x4fe9,0x8054,0x83b2,0x8fde,0x9570,
+ 0x5ec9,0x601c,0x6d9f,0x5e18,0x655b,0x8138,0x94fe,0x604b,0x70bc,0x7ec3,
+ 0x7cae,0x51c9,0x6881,0x7cb1,0x826f,0x4e24,0x8f86,0x91cf,0x667e,0x4eae,
+ 0x8c05,0x64a9,0x804a,0x50da,0x7597,0x71ce,0x5be5,0x8fbd,0x6f66,0x4e86,
+ 0x6482,0x9563,0x5ed6,0x6599,0x5217,0x88c2,0x70c8,0x52a3,0x730e,0x7433,
+ 0x6797,0x78f7,0x9716,0x4e34,0x90bb,0x9cde,0x6dcb,0x51db,0x8d41,0x541d,
+ 0x62ce,0x73b2,0x83f1,0x96f6,0x9f84,0x94c3,0x4f36,0x7f9a,0x51cc,0x7075,
+ 0x9675,0x5cad,0x9886,0x53e6,0x4ee4,0x6e9c,0x7409,0x69b4,0x786b,0x998f,
+ 0x7559,0x5218,0x7624,0x6d41,0x67f3,0x516d,0x9f99,0x804b,0x5499,0x7b3c,
+ 0x7abf
+ },
+ { /* ku 42 */
+ 0x7fe4,0x7fe7,0x7fe8,0x7fea,0x7feb,0x7fec,0x7fed,0x7fef,0x7ff2,0x7ff4,
+ 0x7ff5,0x7ff6,0x7ff7,0x7ff8,0x7ff9,0x7ffa,0x7ffd,0x7ffe,0x7fff,0x8002,
+ 0x8007,0x8008,0x8009,0x800a,0x800e,0x800f,0x8011,0x8013,0x801a,0x801b,
+ 0x801d,0x801e,0x801f,0x8021,0x8023,0x8024,0x802b,0x802c,0x802d,0x802e,
+ 0x802f,0x8030,0x8032,0x8034,0x8039,0x803a,0x803c,0x803e,0x8040,0x8041,
+ 0x8044,0x8045,0x8047,0x8048,0x8049,0x804e,0x804f,0x8050,0x8051,0x8053,
+ 0x8055,0x8056,0x8057,UBOGON,0x8059,0x805b,0x805c,0x805d,0x805e,0x805f,
+ 0x8060,0x8061,0x8062,0x8063,0x8064,0x8065,0x8066,0x8067,0x8068,0x806b,
+ 0x806c,0x806d,0x806e,0x806f,0x8070,0x8072,0x8073,0x8074,0x8075,0x8076,
+ 0x8077,0x8078,0x8079,0x807a,0x807b,0x807c,0x807d,0x9686,0x5784,0x62e2,
+ 0x9647,0x697c,0x5a04,0x6402,0x7bd3,0x6f0f,0x964b,0x82a6,0x5362,0x9885,
+ 0x5e90,0x7089,0x63b3,0x5364,0x864f,0x9c81,0x9e93,0x788c,0x9732,0x8def,
+ 0x8d42,0x9e7f,0x6f5e,0x7984,0x5f55,0x9646,0x622e,0x9a74,0x5415,0x94dd,
+ 0x4fa3,0x65c5,0x5c65,0x5c61,0x7f15,0x8651,0x6c2f,0x5f8b,0x7387,0x6ee4,
+ 0x7eff,0x5ce6,0x631b,0x5b6a,0x6ee6,0x5375,0x4e71,0x63a0,0x7565,0x62a1,
+ 0x8f6e,0x4f26,0x4ed1,0x6ca6,0x7eb6,0x8bba,0x841d,0x87ba,0x7f57,0x903b,
+ 0x9523,0x7ba9,0x9aa1,0x88f8,0x843d,0x6d1b,0x9a86,0x7edc,0x5988,0x9ebb,
+ 0x739b,0x7801,0x8682,0x9a6c,0x9a82,0x561b,0x5417,0x57cb,0x4e70,0x9ea6,
+ 0x5356,0x8fc8,0x8109,0x7792,0x9992,0x86ee,0x6ee1,0x8513,0x66fc,0x6162,
+ 0x6f2b
+ },
+ { /* ku 43 */
+ 0x807e,0x8081,0x8082,0x8085,0x8088,0x808a,0x808d,0x808e,0x808f,0x8090,
+ 0x8091,0x8092,0x8094,0x8095,0x8097,0x8099,0x809e,0x80a3,0x80a6,0x80a7,
+ 0x80a8,0x80ac,0x80b0,0x80b3,0x80b5,0x80b6,0x80b8,0x80b9,0x80bb,0x80c5,
+ 0x80c7,0x80c8,0x80c9,0x80ca,0x80cb,0x80cf,0x80d0,0x80d1,0x80d2,0x80d3,
+ 0x80d4,0x80d5,0x80d8,0x80df,0x80e0,0x80e2,0x80e3,0x80e6,0x80ee,0x80f5,
+ 0x80f7,0x80f9,0x80fb,0x80fe,0x80ff,0x8100,0x8101,0x8103,0x8104,0x8105,
+ 0x8107,0x8108,0x810b,UBOGON,0x810c,0x8115,0x8117,0x8119,0x811b,0x811c,
+ 0x811d,0x811f,0x8120,0x8121,0x8122,0x8123,0x8124,0x8125,0x8126,0x8127,
+ 0x8128,0x8129,0x812a,0x812b,0x812d,0x812e,0x8130,0x8133,0x8134,0x8135,
+ 0x8137,0x8139,0x813a,0x813b,0x813c,0x813d,0x813f,0x8c29,0x8292,0x832b,
+ 0x76f2,0x6c13,0x5fd9,0x83bd,0x732b,0x8305,0x951a,0x6bdb,0x77db,0x94c6,
+ 0x536f,0x8302,0x5192,0x5e3d,0x8c8c,0x8d38,0x4e48,0x73ab,0x679a,0x6885,
+ 0x9176,0x9709,0x7164,0x6ca1,0x7709,0x5a92,0x9541,0x6bcf,0x7f8e,0x6627,
+ 0x5bd0,0x59b9,0x5a9a,0x95e8,0x95f7,0x4eec,0x840c,0x8499,0x6aac,0x76df,
+ 0x9530,0x731b,0x68a6,0x5b5f,0x772f,0x919a,0x9761,0x7cdc,0x8ff7,0x8c1c,
+ 0x5f25,0x7c73,0x79d8,0x89c5,0x6ccc,0x871c,0x5bc6,0x5e42,0x68c9,0x7720,
+ 0x7ef5,0x5195,0x514d,0x52c9,0x5a29,0x7f05,0x9762,0x82d7,0x63cf,0x7784,
+ 0x85d0,0x79d2,0x6e3a,0x5e99,0x5999,0x8511,0x706d,0x6c11,0x62bf,0x76bf,
+ 0x654f,0x60af,0x95fd,0x660e,0x879f,0x9e23,0x94ed,0x540d,0x547d,0x8c2c,
+ 0x6478
+ },
+ { /* ku 44 */
+ 0x8140,0x8141,0x8142,0x8143,0x8144,0x8145,0x8147,0x8149,0x814d,0x814e,
+ 0x814f,0x8152,0x8156,0x8157,0x8158,0x815b,0x815c,0x815d,0x815e,0x815f,
+ 0x8161,0x8162,0x8163,0x8164,0x8166,0x8168,0x816a,0x816b,0x816c,0x816f,
+ 0x8172,0x8173,0x8175,0x8176,0x8177,0x8178,0x8181,0x8183,0x8184,0x8185,
+ 0x8186,0x8187,0x8189,0x818b,0x818c,0x818d,0x818e,0x8190,0x8192,0x8193,
+ 0x8194,0x8195,0x8196,0x8197,0x8199,0x819a,0x819e,0x819f,0x81a0,0x81a1,
+ 0x81a2,0x81a4,0x81a5,UBOGON,0x81a7,0x81a9,0x81ab,0x81ac,0x81ad,0x81ae,
+ 0x81af,0x81b0,0x81b1,0x81b2,0x81b4,0x81b5,0x81b6,0x81b7,0x81b8,0x81b9,
+ 0x81bc,0x81bd,0x81be,0x81bf,0x81c4,0x81c5,0x81c7,0x81c8,0x81c9,0x81cb,
+ 0x81cd,0x81ce,0x81cf,0x81d0,0x81d1,0x81d2,0x81d3,0x6479,0x8611,0x6a21,
+ 0x819c,0x78e8,0x6469,0x9b54,0x62b9,0x672b,0x83ab,0x58a8,0x9ed8,0x6cab,
+ 0x6f20,0x5bde,0x964c,0x8c0b,0x725f,0x67d0,0x62c7,0x7261,0x4ea9,0x59c6,
+ 0x6bcd,0x5893,0x66ae,0x5e55,0x52df,0x6155,0x6728,0x76ee,0x7766,0x7267,
+ 0x7a46,0x62ff,0x54ea,0x5450,0x94a0,0x90a3,0x5a1c,0x7eb3,0x6c16,0x4e43,
+ 0x5976,0x8010,0x5948,0x5357,0x7537,0x96be,0x56ca,0x6320,0x8111,0x607c,
+ 0x95f9,0x6dd6,0x5462,0x9981,0x5185,0x5ae9,0x80fd,0x59ae,0x9713,0x502a,
+ 0x6ce5,0x5c3c,0x62df,0x4f60,0x533f,0x817b,0x9006,0x6eba,0x852b,0x62c8,
+ 0x5e74,0x78be,0x64b5,0x637b,0x5ff5,0x5a18,0x917f,0x9e1f,0x5c3f,0x634f,
+ 0x8042,0x5b7d,0x556e,0x954a,0x954d,0x6d85,0x60a8,0x67e0,0x72de,0x51dd,
+ 0x5b81
+ },
+ { /* ku 45 */
+ 0x81d4,0x81d5,0x81d6,0x81d7,0x81d8,0x81d9,0x81da,0x81db,0x81dc,0x81dd,
+ 0x81de,0x81df,0x81e0,0x81e1,0x81e2,0x81e4,0x81e5,0x81e6,0x81e8,0x81e9,
+ 0x81eb,0x81ee,0x81ef,0x81f0,0x81f1,0x81f2,0x81f5,0x81f6,0x81f7,0x81f8,
+ 0x81f9,0x81fa,0x81fd,0x81ff,0x8203,0x8207,0x8208,0x8209,0x820a,0x820b,
+ 0x820e,0x820f,0x8211,0x8213,0x8215,0x8216,0x8217,0x8218,0x8219,0x821a,
+ 0x821d,0x8220,0x8224,0x8225,0x8226,0x8227,0x8229,0x822e,0x8232,0x823a,
+ 0x823c,0x823d,0x823f,UBOGON,0x8240,0x8241,0x8242,0x8243,0x8245,0x8246,
+ 0x8248,0x824a,0x824c,0x824d,0x824e,0x8250,0x8251,0x8252,0x8253,0x8254,
+ 0x8255,0x8256,0x8257,0x8259,0x825b,0x825c,0x825d,0x825e,0x8260,0x8261,
+ 0x8262,0x8263,0x8264,0x8265,0x8266,0x8267,0x8269,0x62e7,0x6cde,0x725b,
+ 0x626d,0x94ae,0x7ebd,0x8113,0x6d53,0x519c,0x5f04,0x5974,0x52aa,0x6012,
+ 0x5973,0x6696,0x8650,0x759f,0x632a,0x61e6,0x7cef,0x8bfa,0x54e6,0x6b27,
+ 0x9e25,0x6bb4,0x85d5,0x5455,0x5076,0x6ca4,0x556a,0x8db4,0x722c,0x5e15,
+ 0x6015,0x7436,0x62cd,0x6392,0x724c,0x5f98,0x6e43,0x6d3e,0x6500,0x6f58,
+ 0x76d8,0x78d0,0x76fc,0x7554,0x5224,0x53db,0x4e53,0x5e9e,0x65c1,0x802a,
+ 0x80d6,0x629b,0x5486,0x5228,0x70ae,0x888d,0x8dd1,0x6ce1,0x5478,0x80da,
+ 0x57f9,0x88f4,0x8d54,0x966a,0x914d,0x4f69,0x6c9b,0x55b7,0x76c6,0x7830,
+ 0x62a8,0x70f9,0x6f8e,0x5f6d,0x84ec,0x68da,0x787c,0x7bf7,0x81a8,0x670b,
+ 0x9e4f,0x6367,0x78b0,0x576f,0x7812,0x9739,0x6279,0x62ab,0x5288,0x7435,
+ 0x6bd7
+ },
+ { /* ku 46 */
+ 0x826a,0x826b,0x826c,0x826d,0x8271,0x8275,0x8276,0x8277,0x8278,0x827b,
+ 0x827c,0x8280,0x8281,0x8283,0x8285,0x8286,0x8287,0x8289,0x828c,0x8290,
+ 0x8293,0x8294,0x8295,0x8296,0x829a,0x829b,0x829e,0x82a0,0x82a2,0x82a3,
+ 0x82a7,0x82b2,0x82b5,0x82b6,0x82ba,0x82bb,0x82bc,0x82bf,0x82c0,0x82c2,
+ 0x82c3,0x82c5,0x82c6,0x82c9,0x82d0,0x82d6,0x82d9,0x82da,0x82dd,0x82e2,
+ 0x82e7,0x82e8,0x82e9,0x82ea,0x82ec,0x82ed,0x82ee,0x82f0,0x82f2,0x82f3,
+ 0x82f5,0x82f6,0x82f8,UBOGON,0x82fa,0x82fc,0x82fd,0x82fe,0x82ff,0x8300,
+ 0x830a,0x830b,0x830d,0x8310,0x8312,0x8313,0x8316,0x8318,0x8319,0x831d,
+ 0x831e,0x831f,0x8320,0x8321,0x8322,0x8323,0x8324,0x8325,0x8326,0x8329,
+ 0x832a,0x832e,0x8330,0x8332,0x8337,0x833b,0x833d,0x5564,0x813e,0x75b2,
+ 0x76ae,0x5339,0x75de,0x50fb,0x5c41,0x8b6c,0x7bc7,0x504f,0x7247,0x9a97,
+ 0x98d8,0x6f02,0x74e2,0x7968,0x6487,0x77a5,0x62fc,0x9891,0x8d2b,0x54c1,
+ 0x8058,0x4e52,0x576a,0x82f9,0x840d,0x5e73,0x51ed,0x74f6,0x8bc4,0x5c4f,
+ 0x5761,0x6cfc,0x9887,0x5a46,0x7834,0x9b44,0x8feb,0x7c95,0x5256,0x6251,
+ 0x94fa,0x4ec6,0x8386,0x8461,0x83e9,0x84b2,0x57d4,0x6734,0x5703,0x666e,
+ 0x6d66,0x8c31,0x66dd,0x7011,0x671f,0x6b3a,0x6816,0x621a,0x59bb,0x4e03,
+ 0x51c4,0x6f06,0x67d2,0x6c8f,0x5176,0x68cb,0x5947,0x6b67,0x7566,0x5d0e,
+ 0x8110,0x9f50,0x65d7,0x7948,0x7941,0x9a91,0x8d77,0x5c82,0x4e5e,0x4f01,
+ 0x542f,0x5951,0x780c,0x5668,0x6c14,0x8fc4,0x5f03,0x6c7d,0x6ce3,0x8bab,
+ 0x6390
+ },
+ { /* ku 47 */
+ 0x833e,0x833f,0x8341,0x8342,0x8344,0x8345,0x8348,0x834a,0x834b,0x834c,
+ 0x834d,0x834e,0x8353,0x8355,0x8356,0x8357,0x8358,0x8359,0x835d,0x8362,
+ 0x8370,0x8371,0x8372,0x8373,0x8374,0x8375,0x8376,0x8379,0x837a,0x837e,
+ 0x837f,0x8380,0x8381,0x8382,0x8383,0x8384,0x8387,0x8388,0x838a,0x838b,
+ 0x838c,0x838d,0x838f,0x8390,0x8391,0x8394,0x8395,0x8396,0x8397,0x8399,
+ 0x839a,0x839d,0x839f,0x83a1,0x83a2,0x83a3,0x83a4,0x83a5,0x83a6,0x83a7,
+ 0x83ac,0x83ad,0x83ae,UBOGON,0x83af,0x83b5,0x83bb,0x83be,0x83bf,0x83c2,
+ 0x83c3,0x83c4,0x83c6,0x83c8,0x83c9,0x83cb,0x83cd,0x83ce,0x83d0,0x83d1,
+ 0x83d2,0x83d3,0x83d5,0x83d7,0x83d9,0x83da,0x83db,0x83de,0x83e2,0x83e3,
+ 0x83e4,0x83e6,0x83e7,0x83e8,0x83eb,0x83ec,0x83ed,0x6070,0x6d3d,0x7275,
+ 0x6266,0x948e,0x94c5,0x5343,0x8fc1,0x7b7e,0x4edf,0x8c26,0x4e7e,0x9ed4,
+ 0x94b1,0x94b3,0x524d,0x6f5c,0x9063,0x6d45,0x8c34,0x5811,0x5d4c,0x6b20,
+ 0x6b49,0x67aa,0x545b,0x8154,0x7f8c,0x5899,0x8537,0x5f3a,0x62a2,0x6a47,
+ 0x9539,0x6572,0x6084,0x6865,0x77a7,0x4e54,0x4fa8,0x5de7,0x9798,0x64ac,
+ 0x7fd8,0x5ced,0x4fcf,0x7a8d,0x5207,0x8304,0x4e14,0x602f,0x7a83,0x94a6,
+ 0x4fb5,0x4eb2,0x79e6,0x7434,0x52e4,0x82b9,0x64d2,0x79bd,0x5bdd,0x6c81,
+ 0x9752,0x8f7b,0x6c22,0x503e,0x537f,0x6e05,0x64ce,0x6674,0x6c30,0x60c5,
+ 0x9877,0x8bf7,0x5e86,0x743c,0x7a77,0x79cb,0x4e18,0x90b1,0x7403,0x6c42,
+ 0x56da,0x914b,0x6cc5,0x8d8b,0x533a,0x86c6,0x66f2,0x8eaf,0x5c48,0x9a71,
+ 0x6e20
+ },
+ { /* ku 48 */
+ 0x83ee,0x83ef,0x83f3,0x83f4,0x83f5,0x83f6,0x83f7,0x83fa,0x83fb,0x83fc,
+ 0x83fe,0x83ff,0x8400,0x8402,0x8405,0x8407,0x8408,0x8409,0x840a,0x8410,
+ 0x8412,0x8413,0x8414,0x8415,0x8416,0x8417,0x8419,0x841a,0x841b,0x841e,
+ 0x841f,0x8420,0x8421,0x8422,0x8423,0x8429,0x842a,0x842b,0x842c,0x842d,
+ 0x842e,0x842f,0x8430,0x8432,0x8433,0x8434,0x8435,0x8436,0x8437,0x8439,
+ 0x843a,0x843b,0x843e,0x843f,0x8440,0x8441,0x8442,0x8443,0x8444,0x8445,
+ 0x8447,0x8448,0x8449,UBOGON,0x844a,0x844b,0x844c,0x844d,0x844e,0x844f,
+ 0x8450,0x8452,0x8453,0x8454,0x8455,0x8456,0x8458,0x845d,0x845e,0x845f,
+ 0x8460,0x8462,0x8464,0x8465,0x8466,0x8467,0x8468,0x846a,0x846e,0x846f,
+ 0x8470,0x8472,0x8474,0x8477,0x8479,0x847b,0x847c,0x53d6,0x5a36,0x9f8b,
+ 0x8da3,0x53bb,0x5708,0x98a7,0x6743,0x919b,0x6cc9,0x5168,0x75ca,0x62f3,
+ 0x72ac,0x5238,0x529d,0x7f3a,0x7094,0x7638,0x5374,0x9e4a,0x69b7,0x786e,
+ 0x96c0,0x88d9,0x7fa4,0x7136,0x71c3,0x5189,0x67d3,0x74e4,0x58e4,0x6518,
+ 0x56b7,0x8ba9,0x9976,0x6270,0x7ed5,0x60f9,0x70ed,0x58ec,0x4ec1,0x4eba,
+ 0x5fcd,0x97e7,0x4efb,0x8ba4,0x5203,0x598a,0x7eab,0x6254,0x4ecd,0x65e5,
+ 0x620e,0x8338,0x84c9,0x8363,0x878d,0x7194,0x6eb6,0x5bb9,0x7ed2,0x5197,
+ 0x63c9,0x67d4,0x8089,0x8339,0x8815,0x5112,0x5b7a,0x5982,0x8fb1,0x4e73,
+ 0x6c5d,0x5165,0x8925,0x8f6f,0x962e,0x854a,0x745e,0x9510,0x95f0,0x6da6,
+ 0x82e5,0x5f31,0x6492,0x6d12,0x8428,0x816e,0x9cc3,0x585e,0x8d5b,0x4e09,
+ 0x53c1
+ },
+ { /* ku 49 */
+ 0x847d,0x847e,0x847f,0x8480,0x8481,0x8483,0x8484,0x8485,0x8486,0x848a,
+ 0x848d,0x848f,0x8490,0x8491,0x8492,0x8493,0x8494,0x8495,0x8496,0x8498,
+ 0x849a,0x849b,0x849d,0x849e,0x849f,0x84a0,0x84a2,0x84a3,0x84a4,0x84a5,
+ 0x84a6,0x84a7,0x84a8,0x84a9,0x84aa,0x84ab,0x84ac,0x84ad,0x84ae,0x84b0,
+ 0x84b1,0x84b3,0x84b5,0x84b6,0x84b7,0x84bb,0x84bc,0x84be,0x84c0,0x84c2,
+ 0x84c3,0x84c5,0x84c6,0x84c7,0x84c8,0x84cb,0x84cc,0x84ce,0x84cf,0x84d2,
+ 0x84d4,0x84d5,0x84d7,UBOGON,0x84d8,0x84d9,0x84da,0x84db,0x84dc,0x84de,
+ 0x84e1,0x84e2,0x84e4,0x84e7,0x84e8,0x84e9,0x84ea,0x84eb,0x84ed,0x84ee,
+ 0x84ef,0x84f1,0x84f2,0x84f3,0x84f4,0x84f5,0x84f6,0x84f7,0x84f8,0x84f9,
+ 0x84fa,0x84fb,0x84fd,0x84fe,0x8500,0x8501,0x8502,0x4f1e,0x6563,0x6851,
+ 0x55d3,0x4e27,0x6414,0x9a9a,0x626b,0x5ac2,0x745f,0x8272,0x6da9,0x68ee,
+ 0x50e7,0x838e,0x7802,0x6740,0x5239,0x6c99,0x7eb1,0x50bb,0x5565,0x715e,
+ 0x7b5b,0x6652,0x73ca,0x82eb,0x6749,0x5c71,0x5220,0x717d,0x886b,0x95ea,
+ 0x9655,0x64c5,0x8d61,0x81b3,0x5584,0x6c55,0x6247,0x7f2e,0x5892,0x4f24,
+ 0x5546,0x8d4f,0x664c,0x4e0a,0x5c1a,0x88f3,0x68a2,0x634e,0x7a0d,0x70e7,
+ 0x828d,0x52fa,0x97f6,0x5c11,0x54e8,0x90b5,0x7ecd,0x5962,0x8d4a,0x86c7,
+ 0x820c,0x820d,0x8d66,0x6444,0x5c04,0x6151,0x6d89,0x793e,0x8bbe,0x7837,
+ 0x7533,0x547b,0x4f38,0x8eab,0x6df1,0x5a20,0x7ec5,0x795e,0x6c88,0x5ba1,
+ 0x5a76,0x751a,0x80be,0x614e,0x6e17,0x58f0,0x751f,0x7525,0x7272,0x5347,
+ 0x7ef3
+ },
+ { /* ku 4a */
+ 0x8503,0x8504,0x8505,0x8506,0x8507,0x8508,0x8509,0x850a,0x850b,0x850d,
+ 0x850e,0x850f,0x8510,0x8512,0x8514,0x8515,0x8516,0x8518,0x8519,0x851b,
+ 0x851c,0x851d,0x851e,0x8520,0x8522,0x8523,0x8524,0x8525,0x8526,0x8527,
+ 0x8528,0x8529,0x852a,0x852d,0x852e,0x852f,0x8530,0x8531,0x8532,0x8533,
+ 0x8534,0x8535,0x8536,0x853e,0x853f,0x8540,0x8541,0x8542,0x8544,0x8545,
+ 0x8546,0x8547,0x854b,0x854c,0x854d,0x854e,0x854f,0x8550,0x8551,0x8552,
+ 0x8553,0x8554,0x8555,UBOGON,0x8557,0x8558,0x855a,0x855b,0x855c,0x855d,
+ 0x855f,0x8560,0x8561,0x8562,0x8563,0x8565,0x8566,0x8567,0x8569,0x856a,
+ 0x856b,0x856c,0x856d,0x856e,0x856f,0x8570,0x8571,0x8573,0x8575,0x8576,
+ 0x8577,0x8578,0x857c,0x857d,0x857f,0x8580,0x8581,0x7701,0x76db,0x5269,
+ 0x80dc,0x5723,0x5e08,0x5931,0x72ee,0x65bd,0x6e7f,0x8bd7,0x5c38,0x8671,
+ 0x5341,0x77f3,0x62fe,0x65f6,0x4ec0,0x98df,0x8680,0x5b9e,0x8bc6,0x53f2,
+ 0x77e2,0x4f7f,0x5c4e,0x9a76,0x59cb,0x5f0f,0x793a,0x58eb,0x4e16,0x67ff,
+ 0x4e8b,0x62ed,0x8a93,0x901d,0x52bf,0x662f,0x55dc,0x566c,0x9002,0x4ed5,
+ 0x4f8d,0x91ca,0x9970,0x6c0f,0x5e02,0x6043,0x5ba4,0x89c6,0x8bd5,0x6536,
+ 0x624b,0x9996,0x5b88,0x5bff,0x6388,0x552e,0x53d7,0x7626,0x517d,0x852c,
+ 0x67a2,0x68b3,0x6b8a,0x6292,0x8f93,0x53d4,0x8212,0x6dd1,0x758f,0x4e66,
+ 0x8d4e,0x5b70,0x719f,0x85af,0x6691,0x66d9,0x7f72,0x8700,0x9ecd,0x9f20,
+ 0x5c5e,0x672f,0x8ff0,0x6811,0x675f,0x620d,0x7ad6,0x5885,0x5eb6,0x6570,
+ 0x6f31
+ },
+ { /* ku 4b */
+ 0x8582,0x8583,0x8586,0x8588,0x8589,0x858a,0x858b,0x858c,0x858d,0x858e,
+ 0x8590,0x8591,0x8592,0x8593,0x8594,0x8595,0x8596,0x8597,0x8598,0x8599,
+ 0x859a,0x859d,0x859e,0x859f,0x85a0,0x85a1,0x85a2,0x85a3,0x85a5,0x85a6,
+ 0x85a7,0x85a9,0x85ab,0x85ac,0x85ad,0x85b1,0x85b2,0x85b3,0x85b4,0x85b5,
+ 0x85b6,0x85b8,0x85ba,0x85bb,0x85bc,0x85bd,0x85be,0x85bf,0x85c0,0x85c2,
+ 0x85c3,0x85c4,0x85c5,0x85c6,0x85c7,0x85c8,0x85ca,0x85cb,0x85cc,0x85cd,
+ 0x85ce,0x85d1,0x85d2,UBOGON,0x85d4,0x85d6,0x85d7,0x85d8,0x85d9,0x85da,
+ 0x85db,0x85dd,0x85de,0x85df,0x85e0,0x85e1,0x85e2,0x85e3,0x85e5,0x85e6,
+ 0x85e7,0x85e8,0x85ea,0x85eb,0x85ec,0x85ed,0x85ee,0x85ef,0x85f0,0x85f1,
+ 0x85f2,0x85f3,0x85f4,0x85f5,0x85f6,0x85f7,0x85f8,0x6055,0x5237,0x800d,
+ 0x6454,0x8870,0x7529,0x5e05,0x6813,0x62f4,0x971c,0x53cc,0x723d,0x8c01,
+ 0x6c34,0x7761,0x7a0e,0x542e,0x77ac,0x987a,0x821c,0x8bf4,0x7855,0x6714,
+ 0x70c1,0x65af,0x6495,0x5636,0x601d,0x79c1,0x53f8,0x4e1d,0x6b7b,0x8086,
+ 0x5bfa,0x55e3,0x56db,0x4f3a,0x4f3c,0x9972,0x5df3,0x677e,0x8038,0x6002,
+ 0x9882,0x9001,0x5b8b,0x8bbc,0x8bf5,0x641c,0x8258,0x64de,0x55fd,0x82cf,
+ 0x9165,0x4fd7,0x7d20,0x901f,0x7c9f,0x50f3,0x5851,0x6eaf,0x5bbf,0x8bc9,
+ 0x8083,0x9178,0x849c,0x7b97,0x867d,0x968b,0x968f,0x7ee5,0x9ad3,0x788e,
+ 0x5c81,0x7a57,0x9042,0x96a7,0x795f,0x5b59,0x635f,0x7b0b,0x84d1,0x68ad,
+ 0x5506,0x7f29,0x7410,0x7d22,0x9501,0x6240,0x584c,0x4ed6,0x5b83,0x5979,
+ 0x5854
+ },
+ { /* ku 4c */
+ 0x85f9,0x85fa,0x85fc,0x85fd,0x85fe,0x8600,0x8601,0x8602,0x8603,0x8604,
+ 0x8606,0x8607,0x8608,0x8609,0x860a,0x860b,0x860c,0x860d,0x860e,0x860f,
+ 0x8610,0x8612,0x8613,0x8614,0x8615,0x8617,0x8618,0x8619,0x861a,0x861b,
+ 0x861c,0x861d,0x861e,0x861f,0x8620,0x8621,0x8622,0x8623,0x8624,0x8625,
+ 0x8626,0x8628,0x862a,0x862b,0x862c,0x862d,0x862e,0x862f,0x8630,0x8631,
+ 0x8632,0x8633,0x8634,0x8635,0x8636,0x8637,0x8639,0x863a,0x863b,0x863d,
+ 0x863e,0x863f,0x8640,UBOGON,0x8641,0x8642,0x8643,0x8644,0x8645,0x8646,
+ 0x8647,0x8648,0x8649,0x864a,0x864b,0x864c,0x8652,0x8653,0x8655,0x8656,
+ 0x8657,0x8658,0x8659,0x865b,0x865c,0x865d,0x865f,0x8660,0x8661,0x8663,
+ 0x8664,0x8665,0x8666,0x8667,0x8668,0x8669,0x866a,0x736d,0x631e,0x8e4b,
+ 0x8e0f,0x80ce,0x82d4,0x62ac,0x53f0,0x6cf0,0x915e,0x592a,0x6001,0x6c70,
+ 0x574d,0x644a,0x8d2a,0x762b,0x6ee9,0x575b,0x6a80,0x75f0,0x6f6d,0x8c2d,
+ 0x8c08,0x5766,0x6bef,0x8892,0x78b3,0x63a2,0x53f9,0x70ad,0x6c64,0x5858,
+ 0x642a,0x5802,0x68e0,0x819b,0x5510,0x7cd6,0x5018,0x8eba,0x6dcc,0x8d9f,
+ 0x70eb,0x638f,0x6d9b,0x6ed4,0x7ee6,0x8404,0x6843,0x9003,0x6dd8,0x9676,
+ 0x8ba8,0x5957,0x7279,0x85e4,0x817e,0x75bc,0x8a8a,0x68af,0x5254,0x8e22,
+ 0x9511,0x63d0,0x9898,0x8e44,0x557c,0x4f53,0x66ff,0x568f,0x60d5,0x6d95,
+ 0x5243,0x5c49,0x5929,0x6dfb,0x586b,0x7530,0x751c,0x606c,0x8214,0x8146,
+ 0x6311,0x6761,0x8fe2,0x773a,0x8df3,0x8d34,0x94c1,0x5e16,0x5385,0x542c,
+ 0x70c3
+ },
+ { /* ku 4d */
+ 0x866d,0x866f,0x8670,0x8672,0x8673,0x8674,0x8675,0x8676,0x8677,0x8678,
+ 0x8683,0x8684,0x8685,0x8686,0x8687,0x8688,0x8689,0x868e,0x868f,0x8690,
+ 0x8691,0x8692,0x8694,0x8696,0x8697,0x8698,0x8699,0x869a,0x869b,0x869e,
+ 0x869f,0x86a0,0x86a1,0x86a2,0x86a5,0x86a6,0x86ab,0x86ad,0x86ae,0x86b2,
+ 0x86b3,0x86b7,0x86b8,0x86b9,0x86bb,0x86bc,0x86bd,0x86be,0x86bf,0x86c1,
+ 0x86c2,0x86c3,0x86c5,0x86c8,0x86cc,0x86cd,0x86d2,0x86d3,0x86d5,0x86d6,
+ 0x86d7,0x86da,0x86dc,UBOGON,0x86dd,0x86e0,0x86e1,0x86e2,0x86e3,0x86e5,
+ 0x86e6,0x86e7,0x86e8,0x86ea,0x86eb,0x86ec,0x86ef,0x86f5,0x86f6,0x86f7,
+ 0x86fa,0x86fb,0x86fc,0x86fd,0x86ff,0x8701,0x8704,0x8705,0x8706,0x870b,
+ 0x870c,0x870e,0x870f,0x8710,0x8711,0x8714,0x8716,0x6c40,0x5ef7,0x505c,
+ 0x4ead,0x5ead,0x633a,0x8247,0x901a,0x6850,0x916e,0x77b3,0x540c,0x94dc,
+ 0x5f64,0x7ae5,0x6876,0x6345,0x7b52,0x7edf,0x75db,0x5077,0x6295,0x5934,
+ 0x900f,0x51f8,0x79c3,0x7a81,0x56fe,0x5f92,0x9014,0x6d82,0x5c60,0x571f,
+ 0x5410,0x5154,0x6e4d,0x56e2,0x63a8,0x9893,0x817f,0x8715,0x892a,0x9000,
+ 0x541e,0x5c6f,0x81c0,0x62d6,0x6258,0x8131,0x9e35,0x9640,0x9a6e,0x9a7c,
+ 0x692d,0x59a5,0x62d3,0x553e,0x6316,0x54c7,0x86d9,0x6d3c,0x5a03,0x74e6,
+ 0x889c,0x6b6a,0x5916,0x8c4c,0x5f2f,0x6e7e,0x73a9,0x987d,0x4e38,0x70f7,
+ 0x5b8c,0x7897,0x633d,0x665a,0x7696,0x60cb,0x5b9b,0x5a49,0x4e07,0x8155,
+ 0x6c6a,0x738b,0x4ea1,0x6789,0x7f51,0x5f80,0x65fa,0x671b,0x5fd8,0x5984,
+ 0x5a01
+ },
+ { /* ku 4e */
+ 0x8719,0x871b,0x871d,0x871f,0x8720,0x8724,0x8726,0x8727,0x8728,0x872a,
+ 0x872b,0x872c,0x872d,0x872f,0x8730,0x8732,0x8733,0x8735,0x8736,0x8738,
+ 0x8739,0x873a,0x873c,0x873d,0x8740,0x8741,0x8742,0x8743,0x8744,0x8745,
+ 0x8746,0x874a,0x874b,0x874d,0x874f,0x8750,0x8751,0x8752,0x8754,0x8755,
+ 0x8756,0x8758,0x875a,0x875b,0x875c,0x875d,0x875e,0x875f,0x8761,0x8762,
+ 0x8766,0x8767,0x8768,0x8769,0x876a,0x876b,0x876c,0x876d,0x876f,0x8771,
+ 0x8772,0x8773,0x8775,UBOGON,0x8777,0x8778,0x8779,0x877a,0x877f,0x8780,
+ 0x8781,0x8784,0x8786,0x8787,0x8789,0x878a,0x878c,0x878e,0x878f,0x8790,
+ 0x8791,0x8792,0x8794,0x8795,0x8796,0x8798,0x8799,0x879a,0x879b,0x879c,
+ 0x879d,0x879e,0x87a0,0x87a1,0x87a2,0x87a3,0x87a4,0x5dcd,0x5fae,0x5371,
+ 0x97e6,0x8fdd,0x6845,0x56f4,0x552f,0x60df,0x4e3a,0x6f4d,0x7ef4,0x82c7,
+ 0x840e,0x59d4,0x4f1f,0x4f2a,0x5c3e,0x7eac,0x672a,0x851a,0x5473,0x754f,
+ 0x80c3,0x5582,0x9b4f,0x4f4d,0x6e2d,0x8c13,0x5c09,0x6170,0x536b,0x761f,
+ 0x6e29,0x868a,0x6587,0x95fb,0x7eb9,0x543b,0x7a33,0x7d0a,0x95ee,0x55e1,
+ 0x7fc1,0x74ee,0x631d,0x8717,0x6da1,0x7a9d,0x6211,0x65a1,0x5367,0x63e1,
+ 0x6c83,0x5deb,0x545c,0x94a8,0x4e4c,0x6c61,0x8bec,0x5c4b,0x65e0,0x829c,
+ 0x68a7,0x543e,0x5434,0x6bcb,0x6b66,0x4e94,0x6342,0x5348,0x821e,0x4f0d,
+ 0x4fae,0x575e,0x620a,0x96fe,0x6664,0x7269,0x52ff,0x52a1,0x609f,0x8bef,
+ 0x6614,0x7199,0x6790,0x897f,0x7852,0x77fd,0x6670,0x563b,0x5438,0x9521,
+ 0x727a
+ },
+ { /* ku 4f */
+ 0x87a5,0x87a6,0x87a7,0x87a9,0x87aa,0x87ae,0x87b0,0x87b1,0x87b2,0x87b4,
+ 0x87b6,0x87b7,0x87b8,0x87b9,0x87bb,0x87bc,0x87be,0x87bf,0x87c1,0x87c2,
+ 0x87c3,0x87c4,0x87c5,0x87c7,0x87c8,0x87c9,0x87cc,0x87cd,0x87ce,0x87cf,
+ 0x87d0,0x87d4,0x87d5,0x87d6,0x87d7,0x87d8,0x87d9,0x87da,0x87dc,0x87dd,
+ 0x87de,0x87df,0x87e1,0x87e2,0x87e3,0x87e4,0x87e6,0x87e7,0x87e8,0x87e9,
+ 0x87eb,0x87ec,0x87ed,0x87ef,0x87f0,0x87f1,0x87f2,0x87f3,0x87f4,0x87f5,
+ 0x87f6,0x87f7,0x87f8,UBOGON,0x87fa,0x87fb,0x87fc,0x87fd,0x87ff,0x8800,
+ 0x8801,0x8802,0x8804,0x8805,0x8806,0x8807,0x8808,0x8809,0x880b,0x880c,
+ 0x880d,0x880e,0x880f,0x8810,0x8811,0x8812,0x8814,0x8817,0x8818,0x8819,
+ 0x881a,0x881c,0x881d,0x881e,0x881f,0x8820,0x8823,0x7a00,0x606f,0x5e0c,
+ 0x6089,0x819d,0x5915,0x60dc,0x7184,0x70ef,0x6eaa,0x6c50,0x7280,0x6a84,
+ 0x88ad,0x5e2d,0x4e60,0x5ab3,0x559c,0x94e3,0x6d17,0x7cfb,0x9699,0x620f,
+ 0x7ec6,0x778e,0x867e,0x5323,0x971e,0x8f96,0x6687,0x5ce1,0x4fa0,0x72ed,
+ 0x4e0b,0x53a6,0x590f,0x5413,0x6380,0x9528,0x5148,0x4ed9,0x9c9c,0x7ea4,
+ 0x54b8,0x8d24,0x8854,0x8237,0x95f2,0x6d8e,0x5f26,0x5acc,0x663e,0x9669,
+ 0x73b0,0x732e,0x53bf,0x817a,0x9985,0x7fa1,0x5baa,0x9677,0x9650,0x7ebf,
+ 0x76f8,0x53a2,0x9576,0x9999,0x7bb1,0x8944,0x6e58,0x4e61,0x7fd4,0x7965,
+ 0x8be6,0x60f3,0x54cd,0x4eab,0x9879,0x5df7,0x6a61,0x50cf,0x5411,0x8c61,
+ 0x8427,0x785d,0x9704,0x524a,0x54ee,0x56a3,0x9500,0x6d88,0x5bb5,0x6dc6,
+ 0x6653
+ },
+ { /* ku 50 */
+ 0x8824,0x8825,0x8826,0x8827,0x8828,0x8829,0x882a,0x882b,0x882c,0x882d,
+ 0x882e,0x882f,0x8830,0x8831,0x8833,0x8834,0x8835,0x8836,0x8837,0x8838,
+ 0x883a,0x883b,0x883d,0x883e,0x883f,0x8841,0x8842,0x8843,0x8846,0x8847,
+ 0x8848,0x8849,0x884a,0x884b,0x884e,0x884f,0x8850,0x8851,0x8852,0x8853,
+ 0x8855,0x8856,0x8858,0x885a,0x885b,0x885c,0x885d,0x885e,0x885f,0x8860,
+ 0x8866,0x8867,0x886a,0x886d,0x886f,0x8871,0x8873,0x8874,0x8875,0x8876,
+ 0x8878,0x8879,0x887a,UBOGON,0x887b,0x887c,0x8880,0x8883,0x8886,0x8887,
+ 0x8889,0x888a,0x888c,0x888e,0x888f,0x8890,0x8891,0x8893,0x8894,0x8895,
+ 0x8897,0x8898,0x8899,0x889a,0x889b,0x889d,0x889e,0x889f,0x88a0,0x88a1,
+ 0x88a3,0x88a5,0x88a6,0x88a7,0x88a8,0x88a9,0x88aa,0x5c0f,0x5b5d,0x6821,
+ 0x8096,0x5578,0x7b11,0x6548,0x6954,0x4e9b,0x6b47,0x874e,0x978b,0x534f,
+ 0x631f,0x643a,0x90aa,0x659c,0x80c1,0x8c10,0x5199,0x68b0,0x5378,0x87f9,
+ 0x61c8,0x6cc4,0x6cfb,0x8c22,0x5c51,0x85aa,0x82af,0x950c,0x6b23,0x8f9b,
+ 0x65b0,0x5ffb,0x5fc3,0x4fe1,0x8845,0x661f,0x8165,0x7329,0x60fa,0x5174,
+ 0x5211,0x578b,0x5f62,0x90a2,0x884c,0x9192,0x5e78,0x674f,0x6027,0x59d3,
+ 0x5144,0x51f6,0x80f8,0x5308,0x6c79,0x96c4,0x718a,0x4f11,0x4fee,0x7f9e,
+ 0x673d,0x55c5,0x9508,0x79c0,0x8896,0x7ee3,0x589f,0x620c,0x9700,0x865a,
+ 0x5618,0x987b,0x5f90,0x8bb8,0x84c4,0x9157,0x53d9,0x65ed,0x5e8f,0x755c,
+ 0x6064,0x7d6e,0x5a7f,0x7eea,0x7eed,0x8f69,0x55a7,0x5ba3,0x60ac,0x65cb,
+ 0x7384
+ },
+ { /* ku 51 */
+ 0x88ac,0x88ae,0x88af,0x88b0,0x88b2,0x88b3,0x88b4,0x88b5,0x88b6,0x88b8,
+ 0x88b9,0x88ba,0x88bb,0x88bd,0x88be,0x88bf,0x88c0,0x88c3,0x88c4,0x88c7,
+ 0x88c8,0x88ca,0x88cb,0x88cc,0x88cd,0x88cf,0x88d0,0x88d1,0x88d3,0x88d6,
+ 0x88d7,0x88da,0x88db,0x88dc,0x88dd,0x88de,0x88e0,0x88e1,0x88e6,0x88e7,
+ 0x88e9,0x88ea,0x88eb,0x88ec,0x88ed,0x88ee,0x88ef,0x88f2,0x88f5,0x88f6,
+ 0x88f7,0x88fa,0x88fb,0x88fd,0x88ff,0x8900,0x8901,0x8903,0x8904,0x8905,
+ 0x8906,0x8907,0x8908,UBOGON,0x8909,0x890b,0x890c,0x890d,0x890e,0x890f,
+ 0x8911,0x8914,0x8915,0x8916,0x8917,0x8918,0x891c,0x891d,0x891e,0x891f,
+ 0x8920,0x8922,0x8923,0x8924,0x8926,0x8927,0x8928,0x8929,0x892c,0x892d,
+ 0x892e,0x892f,0x8931,0x8932,0x8933,0x8935,0x8937,0x9009,0x7663,0x7729,
+ 0x7eda,0x9774,0x859b,0x5b66,0x7a74,0x96ea,0x8840,0x52cb,0x718f,0x5faa,
+ 0x65ec,0x8be2,0x5bfb,0x9a6f,0x5de1,0x6b89,0x6c5b,0x8bad,0x8baf,0x900a,
+ 0x8fc5,0x538b,0x62bc,0x9e26,0x9e2d,0x5440,0x4e2b,0x82bd,0x7259,0x869c,
+ 0x5d16,0x8859,0x6daf,0x96c5,0x54d1,0x4e9a,0x8bb6,0x7109,0x54bd,0x9609,
+ 0x70df,0x6df9,0x76d0,0x4e25,0x7814,0x8712,0x5ca9,0x5ef6,0x8a00,0x989c,
+ 0x960e,0x708e,0x6cbf,0x5944,0x63a9,0x773c,0x884d,0x6f14,0x8273,0x5830,
+ 0x71d5,0x538c,0x781a,0x96c1,0x5501,0x5f66,0x7130,0x5bb4,0x8c1a,0x9a8c,
+ 0x6b83,0x592e,0x9e2f,0x79e7,0x6768,0x626c,0x4f6f,0x75a1,0x7f8a,0x6d0b,
+ 0x9633,0x6c27,0x4ef0,0x75d2,0x517b,0x6837,0x6f3e,0x9080,0x8170,0x5996,
+ 0x7476
+ },
+ { /* ku 52 */
+ 0x8938,0x8939,0x893a,0x893b,0x893c,0x893d,0x893e,0x893f,0x8940,0x8942,
+ 0x8943,0x8945,0x8946,0x8947,0x8948,0x8949,0x894a,0x894b,0x894c,0x894d,
+ 0x894e,0x894f,0x8950,0x8951,0x8952,0x8953,0x8954,0x8955,0x8956,0x8957,
+ 0x8958,0x8959,0x895a,0x895b,0x895c,0x895d,0x8960,0x8961,0x8962,0x8963,
+ 0x8964,0x8965,0x8967,0x8968,0x8969,0x896a,0x896b,0x896c,0x896d,0x896e,
+ 0x896f,0x8970,0x8971,0x8972,0x8973,0x8974,0x8975,0x8976,0x8977,0x8978,
+ 0x8979,0x897a,0x897c,UBOGON,0x897d,0x897e,0x8980,0x8982,0x8984,0x8985,
+ 0x8987,0x8988,0x8989,0x898a,0x898b,0x898c,0x898d,0x898e,0x898f,0x8990,
+ 0x8991,0x8992,0x8993,0x8994,0x8995,0x8996,0x8997,0x8998,0x8999,0x899a,
+ 0x899b,0x899c,0x899d,0x899e,0x899f,0x89a0,0x89a1,0x6447,0x5c27,0x9065,
+ 0x7a91,0x8c23,0x59da,0x54ac,0x8200,0x836f,0x8981,0x8000,0x6930,0x564e,
+ 0x8036,0x7237,0x91ce,0x51b6,0x4e5f,0x9875,0x6396,0x4e1a,0x53f6,0x66f3,
+ 0x814b,0x591c,0x6db2,0x4e00,0x58f9,0x533b,0x63d6,0x94f1,0x4f9d,0x4f0a,
+ 0x8863,0x9890,0x5937,0x9057,0x79fb,0x4eea,0x80f0,0x7591,0x6c82,0x5b9c,
+ 0x59e8,0x5f5d,0x6905,0x8681,0x501a,0x5df2,0x4e59,0x77e3,0x4ee5,0x827a,
+ 0x6291,0x6613,0x9091,0x5c79,0x4ebf,0x5f79,0x81c6,0x9038,0x8084,0x75ab,
+ 0x4ea6,0x88d4,0x610f,0x6bc5,0x5fc6,0x4e49,0x76ca,0x6ea2,0x8be3,0x8bae,
+ 0x8c0a,0x8bd1,0x5f02,0x7ffc,0x7fcc,0x7ece,0x8335,0x836b,0x56e0,0x6bb7,
+ 0x97f3,0x9634,0x59fb,0x541f,0x94f6,0x6deb,0x5bc5,0x996e,0x5c39,0x5f15,
+ 0x9690
+ },
+ { /* ku 53 */
+ 0x89a2,0x89a3,0x89a4,0x89a5,0x89a6,0x89a7,0x89a8,0x89a9,0x89aa,0x89ab,
+ 0x89ac,0x89ad,0x89ae,0x89af,0x89b0,0x89b1,0x89b2,0x89b3,0x89b4,0x89b5,
+ 0x89b6,0x89b7,0x89b8,0x89b9,0x89ba,0x89bb,0x89bc,0x89bd,0x89be,0x89bf,
+ 0x89c0,0x89c3,0x89cd,0x89d3,0x89d4,0x89d5,0x89d7,0x89d8,0x89d9,0x89db,
+ 0x89dd,0x89df,0x89e0,0x89e1,0x89e2,0x89e4,0x89e7,0x89e8,0x89e9,0x89ea,
+ 0x89ec,0x89ed,0x89ee,0x89f0,0x89f1,0x89f2,0x89f4,0x89f5,0x89f6,0x89f7,
+ 0x89f8,0x89f9,0x89fa,UBOGON,0x89fb,0x89fc,0x89fd,0x89fe,0x89ff,0x8a01,
+ 0x8a02,0x8a03,0x8a04,0x8a05,0x8a06,0x8a08,0x8a09,0x8a0a,0x8a0b,0x8a0c,
+ 0x8a0d,0x8a0e,0x8a0f,0x8a10,0x8a11,0x8a12,0x8a13,0x8a14,0x8a15,0x8a16,
+ 0x8a17,0x8a18,0x8a19,0x8a1a,0x8a1b,0x8a1c,0x8a1d,0x5370,0x82f1,0x6a31,
+ 0x5a74,0x9e70,0x5e94,0x7f28,0x83b9,0x8424,0x8425,0x8367,0x8747,0x8fce,
+ 0x8d62,0x76c8,0x5f71,0x9896,0x786c,0x6620,0x54df,0x62e5,0x4f63,0x81c3,
+ 0x75c8,0x5eb8,0x96cd,0x8e0a,0x86f9,0x548f,0x6cf3,0x6d8c,0x6c38,0x607f,
+ 0x52c7,0x7528,0x5e7d,0x4f18,0x60a0,0x5fe7,0x5c24,0x7531,0x90ae,0x94c0,
+ 0x72b9,0x6cb9,0x6e38,0x9149,0x6709,0x53cb,0x53f3,0x4f51,0x91c9,0x8bf1,
+ 0x53c8,0x5e7c,0x8fc2,0x6de4,0x4e8e,0x76c2,0x6986,0x865e,0x611a,0x8206,
+ 0x4f59,0x4fde,0x903e,0x9c7c,0x6109,0x6e1d,0x6e14,0x9685,0x4e88,0x5a31,
+ 0x96e8,0x4e0e,0x5c7f,0x79b9,0x5b87,0x8bed,0x7fbd,0x7389,0x57df,0x828b,
+ 0x90c1,0x5401,0x9047,0x55bb,0x5cea,0x5fa1,0x6108,0x6b32,0x72f1,0x80b2,
+ 0x8a89
+ },
+ { /* ku 54 */
+ 0x8a1e,0x8a1f,0x8a20,0x8a21,0x8a22,0x8a23,0x8a24,0x8a25,0x8a26,0x8a27,
+ 0x8a28,0x8a29,0x8a2a,0x8a2b,0x8a2c,0x8a2d,0x8a2e,0x8a2f,0x8a30,0x8a31,
+ 0x8a32,0x8a33,0x8a34,0x8a35,0x8a36,0x8a37,0x8a38,0x8a39,0x8a3a,0x8a3b,
+ 0x8a3c,0x8a3d,0x8a3f,0x8a40,0x8a41,0x8a42,0x8a43,0x8a44,0x8a45,0x8a46,
+ 0x8a47,0x8a49,0x8a4a,0x8a4b,0x8a4c,0x8a4d,0x8a4e,0x8a4f,0x8a50,0x8a51,
+ 0x8a52,0x8a53,0x8a54,0x8a55,0x8a56,0x8a57,0x8a58,0x8a59,0x8a5a,0x8a5b,
+ 0x8a5c,0x8a5d,0x8a5e,UBOGON,0x8a5f,0x8a60,0x8a61,0x8a62,0x8a63,0x8a64,
+ 0x8a65,0x8a66,0x8a67,0x8a68,0x8a69,0x8a6a,0x8a6b,0x8a6c,0x8a6d,0x8a6e,
+ 0x8a6f,0x8a70,0x8a71,0x8a72,0x8a73,0x8a74,0x8a75,0x8a76,0x8a77,0x8a78,
+ 0x8a7a,0x8a7b,0x8a7c,0x8a7d,0x8a7e,0x8a7f,0x8a80,0x6d74,0x5bd3,0x88d5,
+ 0x9884,0x8c6b,0x9a6d,0x9e33,0x6e0a,0x51a4,0x5143,0x57a3,0x8881,0x539f,
+ 0x63f4,0x8f95,0x56ed,0x5458,0x5706,0x733f,0x6e90,0x7f18,0x8fdc,0x82d1,
+ 0x613f,0x6028,0x9662,0x66f0,0x7ea6,0x8d8a,0x8dc3,0x94a5,0x5cb3,0x7ca4,
+ 0x6708,0x60a6,0x9605,0x8018,0x4e91,0x90e7,0x5300,0x9668,0x5141,0x8fd0,
+ 0x8574,0x915d,0x6655,0x97f5,0x5b55,0x531d,0x7838,0x6742,0x683d,0x54c9,
+ 0x707e,0x5bb0,0x8f7d,0x518d,0x5728,0x54b1,0x6512,0x6682,0x8d5e,0x8d43,
+ 0x810f,0x846c,0x906d,0x7cdf,0x51ff,0x85fb,0x67a3,0x65e9,0x6fa1,0x86a4,
+ 0x8e81,0x566a,0x9020,0x7682,0x7076,0x71e5,0x8d23,0x62e9,0x5219,0x6cfd,
+ 0x8d3c,0x600e,0x589e,0x618e,0x66fe,0x8d60,0x624e,0x55b3,0x6e23,0x672d,
+ 0x8f67
+ },
+ { /* ku 55 */
+ 0x8a81,0x8a82,0x8a83,0x8a84,0x8a85,0x8a86,0x8a87,0x8a88,0x8a8b,0x8a8c,
+ 0x8a8d,0x8a8e,0x8a8f,0x8a90,0x8a91,0x8a92,0x8a94,0x8a95,0x8a96,0x8a97,
+ 0x8a98,0x8a99,0x8a9a,0x8a9b,0x8a9c,0x8a9d,0x8a9e,0x8a9f,0x8aa0,0x8aa1,
+ 0x8aa2,0x8aa3,0x8aa4,0x8aa5,0x8aa6,0x8aa7,0x8aa8,0x8aa9,0x8aaa,0x8aab,
+ 0x8aac,0x8aad,0x8aae,0x8aaf,0x8ab0,0x8ab1,0x8ab2,0x8ab3,0x8ab4,0x8ab5,
+ 0x8ab6,0x8ab7,0x8ab8,0x8ab9,0x8aba,0x8abb,0x8abc,0x8abd,0x8abe,0x8abf,
+ 0x8ac0,0x8ac1,0x8ac2,UBOGON,0x8ac3,0x8ac4,0x8ac5,0x8ac6,0x8ac7,0x8ac8,
+ 0x8ac9,0x8aca,0x8acb,0x8acc,0x8acd,0x8ace,0x8acf,0x8ad0,0x8ad1,0x8ad2,
+ 0x8ad3,0x8ad4,0x8ad5,0x8ad6,0x8ad7,0x8ad8,0x8ad9,0x8ada,0x8adb,0x8adc,
+ 0x8add,0x8ade,0x8adf,0x8ae0,0x8ae1,0x8ae2,0x8ae3,0x94e1,0x95f8,0x7728,
+ 0x6805,0x69a8,0x548b,0x4e4d,0x70b8,0x8bc8,0x6458,0x658b,0x5b85,0x7a84,
+ 0x503a,0x5be8,0x77bb,0x6be1,0x8a79,0x7c98,0x6cbe,0x76cf,0x65a9,0x8f97,
+ 0x5d2d,0x5c55,0x8638,0x6808,0x5360,0x6218,0x7ad9,0x6e5b,0x7efd,0x6a1f,
+ 0x7ae0,0x5f70,0x6f33,0x5f20,0x638c,0x6da8,0x6756,0x4e08,0x5e10,0x8d26,
+ 0x4ed7,0x80c0,0x7634,0x969c,0x62db,0x662d,0x627e,0x6cbc,0x8d75,0x7167,
+ 0x7f69,0x5146,0x8087,0x53ec,0x906e,0x6298,0x54f2,0x86f0,0x8f99,0x8005,
+ 0x9517,0x8517,0x8fd9,0x6d59,0x73cd,0x659f,0x771f,0x7504,0x7827,0x81fb,
+ 0x8d1e,0x9488,0x4fa6,0x6795,0x75b9,0x8bca,0x9707,0x632f,0x9547,0x9635,
+ 0x84b8,0x6323,0x7741,0x5f81,0x72f0,0x4e89,0x6014,0x6574,0x62ef,0x6b63,
+ 0x653f
+ },
+ { /* ku 56 */
+ 0x8ae4,0x8ae5,0x8ae6,0x8ae7,0x8ae8,0x8ae9,0x8aea,0x8aeb,0x8aec,0x8aed,
+ 0x8aee,0x8aef,0x8af0,0x8af1,0x8af2,0x8af3,0x8af4,0x8af5,0x8af6,0x8af7,
+ 0x8af8,0x8af9,0x8afa,0x8afb,0x8afc,0x8afd,0x8afe,0x8aff,0x8b00,0x8b01,
+ 0x8b02,0x8b03,0x8b04,0x8b05,0x8b06,0x8b08,0x8b09,0x8b0a,0x8b0b,0x8b0c,
+ 0x8b0d,0x8b0e,0x8b0f,0x8b10,0x8b11,0x8b12,0x8b13,0x8b14,0x8b15,0x8b16,
+ 0x8b17,0x8b18,0x8b19,0x8b1a,0x8b1b,0x8b1c,0x8b1d,0x8b1e,0x8b1f,0x8b20,
+ 0x8b21,0x8b22,0x8b23,UBOGON,0x8b24,0x8b25,0x8b27,0x8b28,0x8b29,0x8b2a,
+ 0x8b2b,0x8b2c,0x8b2d,0x8b2e,0x8b2f,0x8b30,0x8b31,0x8b32,0x8b33,0x8b34,
+ 0x8b35,0x8b36,0x8b37,0x8b38,0x8b39,0x8b3a,0x8b3b,0x8b3c,0x8b3d,0x8b3e,
+ 0x8b3f,0x8b40,0x8b41,0x8b42,0x8b43,0x8b44,0x8b45,0x5e27,0x75c7,0x90d1,
+ 0x8bc1,0x829d,0x679d,0x652f,0x5431,0x8718,0x77e5,0x80a2,0x8102,0x6c41,
+ 0x4e4b,0x7ec7,0x804c,0x76f4,0x690d,0x6b96,0x6267,0x503c,0x4f84,0x5740,
+ 0x6307,0x6b62,0x8dbe,0x53ea,0x65e8,0x7eb8,0x5fd7,0x631a,0x63b7,0x81f3,
+ 0x81f4,0x7f6e,0x5e1c,0x5cd9,0x5236,0x667a,0x79e9,0x7a1a,0x8d28,0x7099,
+ 0x75d4,0x6ede,0x6cbb,0x7a92,0x4e2d,0x76c5,0x5fe0,0x949f,0x8877,0x7ec8,
+ 0x79cd,0x80bf,0x91cd,0x4ef2,0x4f17,0x821f,0x5468,0x5dde,0x6d32,0x8bcc,
+ 0x7ca5,0x8f74,0x8098,0x5e1a,0x5492,0x76b1,0x5b99,0x663c,0x9aa4,0x73e0,
+ 0x682a,0x86db,0x6731,0x732a,0x8bf8,0x8bdb,0x9010,0x7af9,0x70db,0x716e,
+ 0x62c4,0x77a9,0x5631,0x4e3b,0x8457,0x67f1,0x52a9,0x86c0,0x8d2e,0x94f8,
+ 0x7b51
+ },
+ { /* ku 57 */
+ 0x8b46,0x8b47,0x8b48,0x8b49,0x8b4a,0x8b4b,0x8b4c,0x8b4d,0x8b4e,0x8b4f,
+ 0x8b50,0x8b51,0x8b52,0x8b53,0x8b54,0x8b55,0x8b56,0x8b57,0x8b58,0x8b59,
+ 0x8b5a,0x8b5b,0x8b5c,0x8b5d,0x8b5e,0x8b5f,0x8b60,0x8b61,0x8b62,0x8b63,
+ 0x8b64,0x8b65,0x8b67,0x8b68,0x8b69,0x8b6a,0x8b6b,0x8b6d,0x8b6e,0x8b6f,
+ 0x8b70,0x8b71,0x8b72,0x8b73,0x8b74,0x8b75,0x8b76,0x8b77,0x8b78,0x8b79,
+ 0x8b7a,0x8b7b,0x8b7c,0x8b7d,0x8b7e,0x8b7f,0x8b80,0x8b81,0x8b82,0x8b83,
+ 0x8b84,0x8b85,0x8b86,UBOGON,0x8b87,0x8b88,0x8b89,0x8b8a,0x8b8b,0x8b8c,
+ 0x8b8d,0x8b8e,0x8b8f,0x8b90,0x8b91,0x8b92,0x8b93,0x8b94,0x8b95,0x8b96,
+ 0x8b97,0x8b98,0x8b99,0x8b9a,0x8b9b,0x8b9c,0x8b9d,0x8b9e,0x8b9f,0x8bac,
+ 0x8bb1,0x8bbb,0x8bc7,0x8bd0,0x8bea,0x8c09,0x8c1e,0x4f4f,0x6ce8,0x795d,
+ 0x9a7b,0x6293,0x722a,0x62fd,0x4e13,0x7816,0x8f6c,0x64b0,0x8d5a,0x7bc6,
+ 0x6869,0x5e84,0x88c5,0x5986,0x649e,0x58ee,0x72b6,0x690e,0x9525,0x8ffd,
+ 0x8d58,0x5760,0x7f00,0x8c06,0x51c6,0x6349,0x62d9,0x5353,0x684c,0x7422,
+ 0x8301,0x914c,0x5544,0x7740,0x707c,0x6d4a,0x5179,0x54a8,0x8d44,0x59ff,
+ 0x6ecb,0x6dc4,0x5b5c,0x7d2b,0x4ed4,0x7c7d,0x6ed3,0x5b50,0x81ea,0x6e0d,
+ 0x5b57,0x9b03,0x68d5,0x8e2a,0x5b97,0x7efc,0x603b,0x7eb5,0x90b9,0x8d70,
+ 0x594f,0x63cd,0x79df,0x8db3,0x5352,0x65cf,0x7956,0x8bc5,0x963b,0x7ec4,
+ 0x94bb,0x7e82,0x5634,0x9189,0x6700,0x7f6a,0x5c0a,0x9075,0x6628,0x5de6,
+ 0x4f50,0x67de,0x505a,0x4f5c,0x5750,0x5ea7,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 58 */
+ 0x8c38,0x8c39,0x8c3a,0x8c3b,0x8c3c,0x8c3d,0x8c3e,0x8c3f,0x8c40,0x8c42,
+ 0x8c43,0x8c44,0x8c45,0x8c48,0x8c4a,0x8c4b,0x8c4d,0x8c4e,0x8c4f,0x8c50,
+ 0x8c51,0x8c52,0x8c53,0x8c54,0x8c56,0x8c57,0x8c58,0x8c59,0x8c5b,0x8c5c,
+ 0x8c5d,0x8c5e,0x8c5f,0x8c60,0x8c63,0x8c64,0x8c65,0x8c66,0x8c67,0x8c68,
+ 0x8c69,0x8c6c,0x8c6d,0x8c6e,0x8c6f,0x8c70,0x8c71,0x8c72,0x8c74,0x8c75,
+ 0x8c76,0x8c77,0x8c7b,0x8c7c,0x8c7d,0x8c7e,0x8c7f,0x8c80,0x8c81,0x8c83,
+ 0x8c84,0x8c86,0x8c87,UBOGON,0x8c88,0x8c8b,0x8c8d,0x8c8e,0x8c8f,0x8c90,
+ 0x8c91,0x8c92,0x8c93,0x8c95,0x8c96,0x8c97,0x8c99,0x8c9a,0x8c9b,0x8c9c,
+ 0x8c9d,0x8c9e,0x8c9f,0x8ca0,0x8ca1,0x8ca2,0x8ca3,0x8ca4,0x8ca5,0x8ca6,
+ 0x8ca7,0x8ca8,0x8ca9,0x8caa,0x8cab,0x8cac,0x8cad,0x4e8d,0x4e0c,0x5140,
+ 0x4e10,0x5eff,0x5345,0x4e15,0x4e98,0x4e1e,0x9b32,0x5b6c,0x5669,0x4e28,
+ 0x79ba,0x4e3f,0x5315,0x4e47,0x592d,0x723b,0x536e,0x6c10,0x56df,0x80e4,
+ 0x9997,0x6bd3,0x777e,0x9f17,0x4e36,0x4e9f,0x9f10,0x4e5c,0x4e69,0x4e93,
+ 0x8288,0x5b5b,0x556c,0x560f,0x4ec4,0x538d,0x539d,0x53a3,0x53a5,0x53ae,
+ 0x9765,0x8d5d,0x531a,0x53f5,0x5326,0x532e,0x533e,0x8d5c,0x5366,0x5363,
+ 0x5202,0x5208,0x520e,0x522d,0x5233,0x523f,0x5240,0x524c,0x525e,0x5261,
+ 0x525c,0x84af,0x527d,0x5282,0x5281,0x5290,0x5293,0x5182,0x7f54,0x4ebb,
+ 0x4ec3,0x4ec9,0x4ec2,0x4ee8,0x4ee1,0x4eeb,0x4ede,0x4f1b,0x4ef3,0x4f22,
+ 0x4f64,0x4ef5,0x4f25,0x4f27,0x4f09,0x4f2b,0x4f5e,0x4f67,0x6538,0x4f5a,
+ 0x4f5d
+ },
+ { /* ku 59 */
+ 0x8cae,0x8caf,0x8cb0,0x8cb1,0x8cb2,0x8cb3,0x8cb4,0x8cb5,0x8cb6,0x8cb7,
+ 0x8cb8,0x8cb9,0x8cba,0x8cbb,0x8cbc,0x8cbd,0x8cbe,0x8cbf,0x8cc0,0x8cc1,
+ 0x8cc2,0x8cc3,0x8cc4,0x8cc5,0x8cc6,0x8cc7,0x8cc8,0x8cc9,0x8cca,0x8ccb,
+ 0x8ccc,0x8ccd,0x8cce,0x8ccf,0x8cd0,0x8cd1,0x8cd2,0x8cd3,0x8cd4,0x8cd5,
+ 0x8cd6,0x8cd7,0x8cd8,0x8cd9,0x8cda,0x8cdb,0x8cdc,0x8cdd,0x8cde,0x8cdf,
+ 0x8ce0,0x8ce1,0x8ce2,0x8ce3,0x8ce4,0x8ce5,0x8ce6,0x8ce7,0x8ce8,0x8ce9,
+ 0x8cea,0x8ceb,0x8cec,UBOGON,0x8ced,0x8cee,0x8cef,0x8cf0,0x8cf1,0x8cf2,
+ 0x8cf3,0x8cf4,0x8cf5,0x8cf6,0x8cf7,0x8cf8,0x8cf9,0x8cfa,0x8cfb,0x8cfc,
+ 0x8cfd,0x8cfe,0x8cff,0x8d00,0x8d01,0x8d02,0x8d03,0x8d04,0x8d05,0x8d06,
+ 0x8d07,0x8d08,0x8d09,0x8d0a,0x8d0b,0x8d0c,0x8d0d,0x4f5f,0x4f57,0x4f32,
+ 0x4f3d,0x4f76,0x4f74,0x4f91,0x4f89,0x4f83,0x4f8f,0x4f7e,0x4f7b,0x4faa,
+ 0x4f7c,0x4fac,0x4f94,0x4fe6,0x4fe8,0x4fea,0x4fc5,0x4fda,0x4fe3,0x4fdc,
+ 0x4fd1,0x4fdf,0x4ff8,0x5029,0x504c,0x4ff3,0x502c,0x500f,0x502e,0x502d,
+ 0x4ffe,0x501c,0x500c,0x5025,0x5028,0x507e,0x5043,0x5055,0x5048,0x504e,
+ 0x506c,0x507b,0x50a5,0x50a7,0x50a9,0x50ba,0x50d6,0x5106,0x50ed,0x50ec,
+ 0x50e6,0x50ee,0x5107,0x510b,0x4edd,0x6c3d,0x4f58,0x4f65,0x4fce,0x9fa0,
+ 0x6c46,0x7c74,0x516e,0x5dfd,0x9ec9,0x9998,0x5181,0x5914,0x52f9,0x530d,
+ 0x8a07,0x5310,0x51eb,0x5919,0x5155,0x4ea0,0x5156,0x4eb3,0x886e,0x88a4,
+ 0x4eb5,0x8114,0x88d2,0x7980,0x5b34,0x8803,0x7fb8,0x51ab,0x51b1,0x51bd,
+ 0x51bc
+ },
+ { /* ku 5a */
+ 0x8d0e,0x8d0f,0x8d10,0x8d11,0x8d12,0x8d13,0x8d14,0x8d15,0x8d16,0x8d17,
+ 0x8d18,0x8d19,0x8d1a,0x8d1b,0x8d1c,0x8d20,0x8d51,0x8d52,0x8d57,0x8d5f,
+ 0x8d65,0x8d68,0x8d69,0x8d6a,0x8d6c,0x8d6e,0x8d6f,0x8d71,0x8d72,0x8d78,
+ 0x8d79,0x8d7a,0x8d7b,0x8d7c,0x8d7d,0x8d7e,0x8d7f,0x8d80,0x8d82,0x8d83,
+ 0x8d86,0x8d87,0x8d88,0x8d89,0x8d8c,0x8d8d,0x8d8e,0x8d8f,0x8d90,0x8d92,
+ 0x8d93,0x8d95,0x8d96,0x8d97,0x8d98,0x8d99,0x8d9a,0x8d9b,0x8d9c,0x8d9d,
+ 0x8d9e,0x8da0,0x8da1,UBOGON,0x8da2,0x8da4,0x8da5,0x8da6,0x8da7,0x8da8,
+ 0x8da9,0x8daa,0x8dab,0x8dac,0x8dad,0x8dae,0x8daf,0x8db0,0x8db2,0x8db6,
+ 0x8db7,0x8db9,0x8dbb,0x8dbd,0x8dc0,0x8dc1,0x8dc2,0x8dc5,0x8dc7,0x8dc8,
+ 0x8dc9,0x8dca,0x8dcd,0x8dd0,0x8dd2,0x8dd3,0x8dd4,0x51c7,0x5196,0x51a2,
+ 0x51a5,0x8ba0,0x8ba6,0x8ba7,0x8baa,0x8bb4,0x8bb5,0x8bb7,0x8bc2,0x8bc3,
+ 0x8bcb,0x8bcf,0x8bce,0x8bd2,0x8bd3,0x8bd4,0x8bd6,0x8bd8,0x8bd9,0x8bdc,
+ 0x8bdf,0x8be0,0x8be4,0x8be8,0x8be9,0x8bee,0x8bf0,0x8bf3,0x8bf6,0x8bf9,
+ 0x8bfc,0x8bff,0x8c00,0x8c02,0x8c04,0x8c07,0x8c0c,0x8c0f,0x8c11,0x8c12,
+ 0x8c14,0x8c15,0x8c16,0x8c19,0x8c1b,0x8c18,0x8c1d,0x8c1f,0x8c20,0x8c21,
+ 0x8c25,0x8c27,0x8c2a,0x8c2b,0x8c2e,0x8c2f,0x8c32,0x8c33,0x8c35,0x8c36,
+ 0x5369,0x537a,0x961d,0x9622,0x9621,0x9631,0x962a,0x963d,0x963c,0x9642,
+ 0x9649,0x9654,0x965f,0x9667,0x966c,0x9672,0x9674,0x9688,0x968d,0x9697,
+ 0x96b0,0x9097,0x909b,0x909d,0x9099,0x90ac,0x90a1,0x90b4,0x90b3,0x90b6,
+ 0x90ba
+ },
+ { /* ku 5b */
+ 0x8dd5,0x8dd8,0x8dd9,0x8ddc,0x8de0,0x8de1,0x8de2,0x8de5,0x8de6,0x8de7,
+ 0x8de9,0x8ded,0x8dee,0x8df0,0x8df1,0x8df2,0x8df4,0x8df6,0x8dfc,0x8dfe,
+ 0x8dff,0x8e00,0x8e01,0x8e02,0x8e03,0x8e04,0x8e06,0x8e07,0x8e08,0x8e0b,
+ 0x8e0d,0x8e0e,0x8e10,0x8e11,0x8e12,0x8e13,0x8e15,0x8e16,0x8e17,0x8e18,
+ 0x8e19,0x8e1a,0x8e1b,0x8e1c,0x8e20,0x8e21,0x8e24,0x8e25,0x8e26,0x8e27,
+ 0x8e28,0x8e2b,0x8e2d,0x8e30,0x8e32,0x8e33,0x8e34,0x8e36,0x8e37,0x8e38,
+ 0x8e3b,0x8e3c,0x8e3e,UBOGON,0x8e3f,0x8e43,0x8e45,0x8e46,0x8e4c,0x8e4d,
+ 0x8e4e,0x8e4f,0x8e50,0x8e53,0x8e54,0x8e55,0x8e56,0x8e57,0x8e58,0x8e5a,
+ 0x8e5b,0x8e5c,0x8e5d,0x8e5e,0x8e5f,0x8e60,0x8e61,0x8e62,0x8e63,0x8e64,
+ 0x8e65,0x8e67,0x8e68,0x8e6a,0x8e6b,0x8e6e,0x8e71,0x90b8,0x90b0,0x90cf,
+ 0x90c5,0x90be,0x90d0,0x90c4,0x90c7,0x90d3,0x90e6,0x90e2,0x90dc,0x90d7,
+ 0x90db,0x90eb,0x90ef,0x90fe,0x9104,0x9122,0x911e,0x9123,0x9131,0x912f,
+ 0x9139,0x9143,0x9146,0x520d,0x5942,0x52a2,0x52ac,0x52ad,0x52be,0x54ff,
+ 0x52d0,0x52d6,0x52f0,0x53df,0x71ee,0x77cd,0x5ef4,0x51f5,0x51fc,0x9b2f,
+ 0x53b6,0x5f01,0x755a,0x5def,0x574c,0x57a9,0x57a1,0x587e,0x58bc,0x58c5,
+ 0x58d1,0x5729,0x572c,0x572a,0x5733,0x5739,0x572e,0x572f,0x575c,0x573b,
+ 0x5742,0x5769,0x5785,0x576b,0x5786,0x577c,0x577b,0x5768,0x576d,0x5776,
+ 0x5773,0x57ad,0x57a4,0x578c,0x57b2,0x57cf,0x57a7,0x57b4,0x5793,0x57a0,
+ 0x57d5,0x57d8,0x57da,0x57d9,0x57d2,0x57b8,0x57f4,0x57ef,0x57f8,0x57e4,
+ 0x57dd
+ },
+ { /* ku 5c */
+ 0x8e73,0x8e75,0x8e77,0x8e78,0x8e79,0x8e7a,0x8e7b,0x8e7d,0x8e7e,0x8e80,
+ 0x8e82,0x8e83,0x8e84,0x8e86,0x8e88,0x8e89,0x8e8a,0x8e8b,0x8e8c,0x8e8d,
+ 0x8e8e,0x8e91,0x8e92,0x8e93,0x8e95,0x8e96,0x8e97,0x8e98,0x8e99,0x8e9a,
+ 0x8e9b,0x8e9d,0x8e9f,0x8ea0,0x8ea1,0x8ea2,0x8ea3,0x8ea4,0x8ea5,0x8ea6,
+ 0x8ea7,0x8ea8,0x8ea9,0x8eaa,0x8ead,0x8eae,0x8eb0,0x8eb1,0x8eb3,0x8eb4,
+ 0x8eb5,0x8eb6,0x8eb7,0x8eb8,0x8eb9,0x8ebb,0x8ebc,0x8ebd,0x8ebe,0x8ebf,
+ 0x8ec0,0x8ec1,0x8ec2,UBOGON,0x8ec3,0x8ec4,0x8ec5,0x8ec6,0x8ec7,0x8ec8,
+ 0x8ec9,0x8eca,0x8ecb,0x8ecc,0x8ecd,0x8ecf,0x8ed0,0x8ed1,0x8ed2,0x8ed3,
+ 0x8ed4,0x8ed5,0x8ed6,0x8ed7,0x8ed8,0x8ed9,0x8eda,0x8edb,0x8edc,0x8edd,
+ 0x8ede,0x8edf,0x8ee0,0x8ee1,0x8ee2,0x8ee3,0x8ee4,0x580b,0x580d,0x57fd,
+ 0x57ed,0x5800,0x581e,0x5819,0x5844,0x5820,0x5865,0x586c,0x5881,0x5889,
+ 0x589a,0x5880,0x99a8,0x9f19,0x61ff,0x8279,0x827d,0x827f,0x828f,0x828a,
+ 0x82a8,0x8284,0x828e,0x8291,0x8297,0x8299,0x82ab,0x82b8,0x82be,0x82b0,
+ 0x82c8,0x82ca,0x82e3,0x8298,0x82b7,0x82ae,0x82cb,0x82cc,0x82c1,0x82a9,
+ 0x82b4,0x82a1,0x82aa,0x829f,0x82c4,0x82ce,0x82a4,0x82e1,0x8309,0x82f7,
+ 0x82e4,0x830f,0x8307,0x82dc,0x82f4,0x82d2,0x82d8,0x830c,0x82fb,0x82d3,
+ 0x8311,0x831a,0x8306,0x8314,0x8315,0x82e0,0x82d5,0x831c,0x8351,0x835b,
+ 0x835c,0x8308,0x8392,0x833c,0x8334,0x8331,0x839b,0x835e,0x832f,0x834f,
+ 0x8347,0x8343,0x835f,0x8340,0x8317,0x8360,0x832d,0x833a,0x8333,0x8366,
+ 0x8365
+ },
+ { /* ku 5d */
+ 0x8ee5,0x8ee6,0x8ee7,0x8ee8,0x8ee9,0x8eea,0x8eeb,0x8eec,0x8eed,0x8eee,
+ 0x8eef,0x8ef0,0x8ef1,0x8ef2,0x8ef3,0x8ef4,0x8ef5,0x8ef6,0x8ef7,0x8ef8,
+ 0x8ef9,0x8efa,0x8efb,0x8efc,0x8efd,0x8efe,0x8eff,0x8f00,0x8f01,0x8f02,
+ 0x8f03,0x8f04,0x8f05,0x8f06,0x8f07,0x8f08,0x8f09,0x8f0a,0x8f0b,0x8f0c,
+ 0x8f0d,0x8f0e,0x8f0f,0x8f10,0x8f11,0x8f12,0x8f13,0x8f14,0x8f15,0x8f16,
+ 0x8f17,0x8f18,0x8f19,0x8f1a,0x8f1b,0x8f1c,0x8f1d,0x8f1e,0x8f1f,0x8f20,
+ 0x8f21,0x8f22,0x8f23,UBOGON,0x8f24,0x8f25,0x8f26,0x8f27,0x8f28,0x8f29,
+ 0x8f2a,0x8f2b,0x8f2c,0x8f2d,0x8f2e,0x8f2f,0x8f30,0x8f31,0x8f32,0x8f33,
+ 0x8f34,0x8f35,0x8f36,0x8f37,0x8f38,0x8f39,0x8f3a,0x8f3b,0x8f3c,0x8f3d,
+ 0x8f3e,0x8f3f,0x8f40,0x8f41,0x8f42,0x8f43,0x8f44,0x8368,0x831b,0x8369,
+ 0x836c,0x836a,0x836d,0x836e,0x83b0,0x8378,0x83b3,0x83b4,0x83a0,0x83aa,
+ 0x8393,0x839c,0x8385,0x837c,0x83b6,0x83a9,0x837d,0x83b8,0x837b,0x8398,
+ 0x839e,0x83a8,0x83ba,0x83bc,0x83c1,0x8401,0x83e5,0x83d8,0x5807,0x8418,
+ 0x840b,0x83dd,0x83fd,0x83d6,0x841c,0x8438,0x8411,0x8406,0x83d4,0x83df,
+ 0x840f,0x8403,0x83f8,0x83f9,0x83ea,0x83c5,0x83c0,0x8426,0x83f0,0x83e1,
+ 0x845c,0x8451,0x845a,0x8459,0x8473,0x8487,0x8488,0x847a,0x8489,0x8478,
+ 0x843c,0x8446,0x8469,0x8476,0x848c,0x848e,0x8431,0x846d,0x84c1,0x84cd,
+ 0x84d0,0x84e6,0x84bd,0x84d3,0x84ca,0x84bf,0x84ba,0x84e0,0x84a1,0x84b9,
+ 0x84b4,0x8497,0x84e5,0x84e3,0x850c,0x750d,0x8538,0x84f0,0x8539,0x851f,
+ 0x853a
+ },
+ { /* ku 5e */
+ 0x8f45,0x8f46,0x8f47,0x8f48,0x8f49,0x8f4a,0x8f4b,0x8f4c,0x8f4d,0x8f4e,
+ 0x8f4f,0x8f50,0x8f51,0x8f52,0x8f53,0x8f54,0x8f55,0x8f56,0x8f57,0x8f58,
+ 0x8f59,0x8f5a,0x8f5b,0x8f5c,0x8f5d,0x8f5e,0x8f5f,0x8f60,0x8f61,0x8f62,
+ 0x8f63,0x8f64,0x8f65,0x8f6a,0x8f80,0x8f8c,0x8f92,0x8f9d,0x8fa0,0x8fa1,
+ 0x8fa2,0x8fa4,0x8fa5,0x8fa6,0x8fa7,0x8faa,0x8fac,0x8fad,0x8fae,0x8faf,
+ 0x8fb2,0x8fb3,0x8fb4,0x8fb5,0x8fb7,0x8fb8,0x8fba,0x8fbb,0x8fbc,0x8fbf,
+ 0x8fc0,0x8fc3,0x8fc6,UBOGON,0x8fc9,0x8fca,0x8fcb,0x8fcc,0x8fcd,0x8fcf,
+ 0x8fd2,0x8fd6,0x8fd7,0x8fda,0x8fe0,0x8fe1,0x8fe3,0x8fe7,0x8fec,0x8fef,
+ 0x8ff1,0x8ff2,0x8ff4,0x8ff5,0x8ff6,0x8ffa,0x8ffb,0x8ffc,0x8ffe,0x8fff,
+ 0x9007,0x9008,0x900c,0x900e,0x9013,0x9015,0x9018,0x8556,0x853b,0x84ff,
+ 0x84fc,0x8559,0x8548,0x8568,0x8564,0x855e,0x857a,0x77a2,0x8543,0x8572,
+ 0x857b,0x85a4,0x85a8,0x8587,0x858f,0x8579,0x85ae,0x859c,0x8585,0x85b9,
+ 0x85b7,0x85b0,0x85d3,0x85c1,0x85dc,0x85ff,0x8627,0x8605,0x8629,0x8616,
+ 0x863c,0x5efe,0x5f08,0x593c,0x5941,0x8037,0x5955,0x595a,0x5958,0x530f,
+ 0x5c22,0x5c25,0x5c2c,0x5c34,0x624c,0x626a,0x629f,0x62bb,0x62ca,0x62da,
+ 0x62d7,0x62ee,0x6322,0x62f6,0x6339,0x634b,0x6343,0x63ad,0x63f6,0x6371,
+ 0x637a,0x638e,0x63b4,0x636d,0x63ac,0x638a,0x6369,0x63ae,0x63bc,0x63f2,
+ 0x63f8,0x63e0,0x63ff,0x63c4,0x63de,0x63ce,0x6452,0x63c6,0x63be,0x6445,
+ 0x6441,0x640b,0x641b,0x6420,0x640c,0x6426,0x6421,0x645e,0x6484,0x646d,
+ 0x6496
+ },
+ { /* ku 5f */
+ 0x9019,0x901c,0x9023,0x9024,0x9025,0x9027,0x9028,0x9029,0x902a,0x902b,
+ 0x902c,0x9030,0x9031,0x9032,0x9033,0x9034,0x9037,0x9039,0x903a,0x903d,
+ 0x903f,0x9040,0x9043,0x9045,0x9046,0x9048,0x9049,0x904a,0x904b,0x904c,
+ 0x904e,0x9054,0x9055,0x9056,0x9059,0x905a,0x905c,0x905d,0x905e,0x905f,
+ 0x9060,0x9061,0x9064,0x9066,0x9067,0x9069,0x906a,0x906b,0x906c,0x906f,
+ 0x9070,0x9071,0x9072,0x9073,0x9076,0x9077,0x9078,0x9079,0x907a,0x907b,
+ 0x907c,0x907e,0x9081,UBOGON,0x9084,0x9085,0x9086,0x9087,0x9089,0x908a,
+ 0x908c,0x908d,0x908e,0x908f,0x9090,0x9092,0x9094,0x9096,0x9098,0x909a,
+ 0x909c,0x909e,0x909f,0x90a0,0x90a4,0x90a5,0x90a7,0x90a8,0x90a9,0x90ab,
+ 0x90ad,0x90b2,0x90b7,0x90bc,0x90bd,0x90bf,0x90c0,0x647a,0x64b7,0x64b8,
+ 0x6499,0x64ba,0x64c0,0x64d0,0x64d7,0x64e4,0x64e2,0x6509,0x6525,0x652e,
+ 0x5f0b,0x5fd2,0x7519,0x5f11,0x535f,0x53f1,0x53fd,0x53e9,0x53e8,0x53fb,
+ 0x5412,0x5416,0x5406,0x544b,0x5452,0x5453,0x5454,0x5456,0x5443,0x5421,
+ 0x5457,0x5459,0x5423,0x5432,0x5482,0x5494,0x5477,0x5471,0x5464,0x549a,
+ 0x549b,0x5484,0x5476,0x5466,0x549d,0x54d0,0x54ad,0x54c2,0x54b4,0x54d2,
+ 0x54a7,0x54a6,0x54d3,0x54d4,0x5472,0x54a3,0x54d5,0x54bb,0x54bf,0x54cc,
+ 0x54d9,0x54da,0x54dc,0x54a9,0x54aa,0x54a4,0x54dd,0x54cf,0x54de,0x551b,
+ 0x54e7,0x5520,0x54fd,0x5514,0x54f3,0x5522,0x5523,0x550f,0x5511,0x5527,
+ 0x552a,0x5567,0x558f,0x55b5,0x5549,0x556d,0x5541,0x5555,0x553f,0x5550,
+ 0x553c
+ },
+ { /* ku 60 */
+ 0x90c2,0x90c3,0x90c6,0x90c8,0x90c9,0x90cb,0x90cc,0x90cd,0x90d2,0x90d4,
+ 0x90d5,0x90d6,0x90d8,0x90d9,0x90da,0x90de,0x90df,0x90e0,0x90e3,0x90e4,
+ 0x90e5,0x90e9,0x90ea,0x90ec,0x90ee,0x90f0,0x90f1,0x90f2,0x90f3,0x90f5,
+ 0x90f6,0x90f7,0x90f9,0x90fa,0x90fb,0x90fc,0x90ff,0x9100,0x9101,0x9103,
+ 0x9105,0x9106,0x9107,0x9108,0x9109,0x910a,0x910b,0x910c,0x910d,0x910e,
+ 0x910f,0x9110,0x9111,0x9112,0x9113,0x9114,0x9115,0x9116,0x9117,0x9118,
+ 0x911a,0x911b,0x911c,UBOGON,0x911d,0x911f,0x9120,0x9121,0x9124,0x9125,
+ 0x9126,0x9127,0x9128,0x9129,0x912a,0x912b,0x912c,0x912d,0x912e,0x9130,
+ 0x9132,0x9133,0x9134,0x9135,0x9136,0x9137,0x9138,0x913a,0x913b,0x913c,
+ 0x913d,0x913e,0x913f,0x9140,0x9141,0x9142,0x9144,0x5537,0x5556,0x5575,
+ 0x5576,0x5577,0x5533,0x5530,0x555c,0x558b,0x55d2,0x5583,0x55b1,0x55b9,
+ 0x5588,0x5581,0x559f,0x557e,0x55d6,0x5591,0x557b,0x55df,0x55bd,0x55be,
+ 0x5594,0x5599,0x55ea,0x55f7,0x55c9,0x561f,0x55d1,0x55eb,0x55ec,0x55d4,
+ 0x55e6,0x55dd,0x55c4,0x55ef,0x55e5,0x55f2,0x55f3,0x55cc,0x55cd,0x55e8,
+ 0x55f5,0x55e4,0x8f94,0x561e,0x5608,0x560c,0x5601,0x5624,0x5623,0x55fe,
+ 0x5600,0x5627,0x562d,0x5658,0x5639,0x5657,0x562c,0x564d,0x5662,0x5659,
+ 0x565c,0x564c,0x5654,0x5686,0x5664,0x5671,0x566b,0x567b,0x567c,0x5685,
+ 0x5693,0x56af,0x56d4,0x56d7,0x56dd,0x56e1,0x56f5,0x56eb,0x56f9,0x56ff,
+ 0x5704,0x570a,0x5709,0x571c,0x5e0f,0x5e19,0x5e14,0x5e11,0x5e31,0x5e3b,
+ 0x5e3c
+ },
+ { /* ku 61 */
+ 0x9145,0x9147,0x9148,0x9151,0x9153,0x9154,0x9155,0x9156,0x9158,0x9159,
+ 0x915b,0x915c,0x915f,0x9160,0x9166,0x9167,0x9168,0x916b,0x916d,0x9173,
+ 0x917a,0x917b,0x917c,0x9180,0x9181,0x9182,0x9183,0x9184,0x9186,0x9188,
+ 0x918a,0x918e,0x918f,0x9193,0x9194,0x9195,0x9196,0x9197,0x9198,0x9199,
+ 0x919c,0x919d,0x919e,0x919f,0x91a0,0x91a1,0x91a4,0x91a5,0x91a6,0x91a7,
+ 0x91a8,0x91a9,0x91ab,0x91ac,0x91b0,0x91b1,0x91b2,0x91b3,0x91b6,0x91b7,
+ 0x91b8,0x91b9,0x91bb,UBOGON,0x91bc,0x91bd,0x91be,0x91bf,0x91c0,0x91c1,
+ 0x91c2,0x91c3,0x91c4,0x91c5,0x91c6,0x91c8,0x91cb,0x91d0,0x91d2,0x91d3,
+ 0x91d4,0x91d5,0x91d6,0x91d7,0x91d8,0x91d9,0x91da,0x91db,0x91dd,0x91de,
+ 0x91df,0x91e0,0x91e1,0x91e2,0x91e3,0x91e4,0x91e5,0x5e37,0x5e44,0x5e54,
+ 0x5e5b,0x5e5e,0x5e61,0x5c8c,0x5c7a,0x5c8d,0x5c90,0x5c96,0x5c88,0x5c98,
+ 0x5c99,0x5c91,0x5c9a,0x5c9c,0x5cb5,0x5ca2,0x5cbd,0x5cac,0x5cab,0x5cb1,
+ 0x5ca3,0x5cc1,0x5cb7,0x5cc4,0x5cd2,0x5ce4,0x5ccb,0x5ce5,0x5d02,0x5d03,
+ 0x5d27,0x5d26,0x5d2e,0x5d24,0x5d1e,0x5d06,0x5d1b,0x5d58,0x5d3e,0x5d34,
+ 0x5d3d,0x5d6c,0x5d5b,0x5d6f,0x5d5d,0x5d6b,0x5d4b,0x5d4a,0x5d69,0x5d74,
+ 0x5d82,0x5d99,0x5d9d,0x8c73,0x5db7,0x5dc5,0x5f73,0x5f77,0x5f82,0x5f87,
+ 0x5f89,0x5f8c,0x5f95,0x5f99,0x5f9c,0x5fa8,0x5fad,0x5fb5,0x5fbc,0x8862,
+ 0x5f61,0x72ad,0x72b0,0x72b4,0x72b7,0x72b8,0x72c3,0x72c1,0x72ce,0x72cd,
+ 0x72d2,0x72e8,0x72ef,0x72e9,0x72f2,0x72f4,0x72f7,0x7301,0x72f3,0x7303,
+ 0x72fa
+ },
+ { /* ku 62 */
+ 0x91e6,0x91e7,0x91e8,0x91e9,0x91ea,0x91eb,0x91ec,0x91ed,0x91ee,0x91ef,
+ 0x91f0,0x91f1,0x91f2,0x91f3,0x91f4,0x91f5,0x91f6,0x91f7,0x91f8,0x91f9,
+ 0x91fa,0x91fb,0x91fc,0x91fd,0x91fe,0x91ff,0x9200,0x9201,0x9202,0x9203,
+ 0x9204,0x9205,0x9206,0x9207,0x9208,0x9209,0x920a,0x920b,0x920c,0x920d,
+ 0x920e,0x920f,0x9210,0x9211,0x9212,0x9213,0x9214,0x9215,0x9216,0x9217,
+ 0x9218,0x9219,0x921a,0x921b,0x921c,0x921d,0x921e,0x921f,0x9220,0x9221,
+ 0x9222,0x9223,0x9224,UBOGON,0x9225,0x9226,0x9227,0x9228,0x9229,0x922a,
+ 0x922b,0x922c,0x922d,0x922e,0x922f,0x9230,0x9231,0x9232,0x9233,0x9234,
+ 0x9235,0x9236,0x9237,0x9238,0x9239,0x923a,0x923b,0x923c,0x923d,0x923e,
+ 0x923f,0x9240,0x9241,0x9242,0x9243,0x9244,0x9245,0x72fb,0x7317,0x7313,
+ 0x7321,0x730a,0x731e,0x731d,0x7315,0x7322,0x7339,0x7325,0x732c,0x7338,
+ 0x7331,0x7350,0x734d,0x7357,0x7360,0x736c,0x736f,0x737e,0x821b,0x5925,
+ 0x98e7,0x5924,0x5902,0x9963,0x9967,0x9968,0x9969,0x996a,0x996b,0x996c,
+ 0x9974,0x9977,0x997d,0x9980,0x9984,0x9987,0x998a,0x998d,0x9990,0x9991,
+ 0x9993,0x9994,0x9995,0x5e80,0x5e91,0x5e8b,0x5e96,0x5ea5,0x5ea0,0x5eb9,
+ 0x5eb5,0x5ebe,0x5eb3,0x8d53,0x5ed2,0x5ed1,0x5edb,0x5ee8,0x5eea,0x81ba,
+ 0x5fc4,0x5fc9,0x5fd6,0x5fcf,0x6003,0x5fee,0x6004,0x5fe1,0x5fe4,0x5ffe,
+ 0x6005,0x6006,0x5fea,0x5fed,0x5ff8,0x6019,0x6035,0x6026,0x601b,0x600f,
+ 0x600d,0x6029,0x602b,0x600a,0x603f,0x6021,0x6078,0x6079,0x607b,0x607a,
+ 0x6042
+ },
+ { /* ku 63 */
+ 0x9246,0x9247,0x9248,0x9249,0x924a,0x924b,0x924c,0x924d,0x924e,0x924f,
+ 0x9250,0x9251,0x9252,0x9253,0x9254,0x9255,0x9256,0x9257,0x9258,0x9259,
+ 0x925a,0x925b,0x925c,0x925d,0x925e,0x925f,0x9260,0x9261,0x9262,0x9263,
+ 0x9264,0x9265,0x9266,0x9267,0x9268,0x9269,0x926a,0x926b,0x926c,0x926d,
+ 0x926e,0x926f,0x9270,0x9271,0x9272,0x9273,0x9275,0x9276,0x9277,0x9278,
+ 0x9279,0x927a,0x927b,0x927c,0x927d,0x927e,0x927f,0x9280,0x9281,0x9282,
+ 0x9283,0x9284,0x9285,UBOGON,0x9286,0x9287,0x9288,0x9289,0x928a,0x928b,
+ 0x928c,0x928d,0x928f,0x9290,0x9291,0x9292,0x9293,0x9294,0x9295,0x9296,
+ 0x9297,0x9298,0x9299,0x929a,0x929b,0x929c,0x929d,0x929e,0x929f,0x92a0,
+ 0x92a1,0x92a2,0x92a3,0x92a4,0x92a5,0x92a6,0x92a7,0x606a,0x607d,0x6096,
+ 0x609a,0x60ad,0x609d,0x6083,0x6092,0x608c,0x609b,0x60ec,0x60bb,0x60b1,
+ 0x60dd,0x60d8,0x60c6,0x60da,0x60b4,0x6120,0x6126,0x6115,0x6123,0x60f4,
+ 0x6100,0x610e,0x612b,0x614a,0x6175,0x61ac,0x6194,0x61a7,0x61b7,0x61d4,
+ 0x61f5,0x5fdd,0x96b3,0x95e9,0x95eb,0x95f1,0x95f3,0x95f5,0x95f6,0x95fc,
+ 0x95fe,0x9603,0x9604,0x9606,0x9608,0x960a,0x960b,0x960c,0x960d,0x960f,
+ 0x9612,0x9615,0x9616,0x9617,0x9619,0x961a,0x4e2c,0x723f,0x6215,0x6c35,
+ 0x6c54,0x6c5c,0x6c4a,0x6ca3,0x6c85,0x6c90,0x6c94,0x6c8c,0x6c68,0x6c69,
+ 0x6c74,0x6c76,0x6c86,0x6ca9,0x6cd0,0x6cd4,0x6cad,0x6cf7,0x6cf8,0x6cf1,
+ 0x6cd7,0x6cb2,0x6ce0,0x6cd6,0x6cfa,0x6ceb,0x6cee,0x6cb1,0x6cd3,0x6cef,
+ 0x6cfe
+ },
+ { /* ku 64 */
+ 0x92a8,0x92a9,0x92aa,0x92ab,0x92ac,0x92ad,0x92af,0x92b0,0x92b1,0x92b2,
+ 0x92b3,0x92b4,0x92b5,0x92b6,0x92b7,0x92b8,0x92b9,0x92ba,0x92bb,0x92bc,
+ 0x92bd,0x92be,0x92bf,0x92c0,0x92c1,0x92c2,0x92c3,0x92c4,0x92c5,0x92c6,
+ 0x92c7,0x92c9,0x92ca,0x92cb,0x92cc,0x92cd,0x92ce,0x92cf,0x92d0,0x92d1,
+ 0x92d2,0x92d3,0x92d4,0x92d5,0x92d6,0x92d7,0x92d8,0x92d9,0x92da,0x92db,
+ 0x92dc,0x92dd,0x92de,0x92df,0x92e0,0x92e1,0x92e2,0x92e3,0x92e4,0x92e5,
+ 0x92e6,0x92e7,0x92e8,UBOGON,0x92e9,0x92ea,0x92eb,0x92ec,0x92ed,0x92ee,
+ 0x92ef,0x92f0,0x92f1,0x92f2,0x92f3,0x92f4,0x92f5,0x92f6,0x92f7,0x92f8,
+ 0x92f9,0x92fa,0x92fb,0x92fc,0x92fd,0x92fe,0x92ff,0x9300,0x9301,0x9302,
+ 0x9303,0x9304,0x9305,0x9306,0x9307,0x9308,0x9309,0x6d39,0x6d27,0x6d0c,
+ 0x6d43,0x6d48,0x6d07,0x6d04,0x6d19,0x6d0e,0x6d2b,0x6d4d,0x6d2e,0x6d35,
+ 0x6d1a,0x6d4f,0x6d52,0x6d54,0x6d33,0x6d91,0x6d6f,0x6d9e,0x6da0,0x6d5e,
+ 0x6d93,0x6d94,0x6d5c,0x6d60,0x6d7c,0x6d63,0x6e1a,0x6dc7,0x6dc5,0x6dde,
+ 0x6e0e,0x6dbf,0x6de0,0x6e11,0x6de6,0x6ddd,0x6dd9,0x6e16,0x6dab,0x6e0c,
+ 0x6dae,0x6e2b,0x6e6e,0x6e4e,0x6e6b,0x6eb2,0x6e5f,0x6e86,0x6e53,0x6e54,
+ 0x6e32,0x6e25,0x6e44,0x6edf,0x6eb1,0x6e98,0x6ee0,0x6f2d,0x6ee2,0x6ea5,
+ 0x6ea7,0x6ebd,0x6ebb,0x6eb7,0x6ed7,0x6eb4,0x6ecf,0x6e8f,0x6ec2,0x6e9f,
+ 0x6f62,0x6f46,0x6f47,0x6f24,0x6f15,0x6ef9,0x6f2f,0x6f36,0x6f4b,0x6f74,
+ 0x6f2a,0x6f09,0x6f29,0x6f89,0x6f8d,0x6f8c,0x6f78,0x6f72,0x6f7c,0x6f7a,
+ 0x6fd1
+ },
+ { /* ku 65 */
+ 0x930a,0x930b,0x930c,0x930d,0x930e,0x930f,0x9310,0x9311,0x9312,0x9313,
+ 0x9314,0x9315,0x9316,0x9317,0x9318,0x9319,0x931a,0x931b,0x931c,0x931d,
+ 0x931e,0x931f,0x9320,0x9321,0x9322,0x9323,0x9324,0x9325,0x9326,0x9327,
+ 0x9328,0x9329,0x932a,0x932b,0x932c,0x932d,0x932e,0x932f,0x9330,0x9331,
+ 0x9332,0x9333,0x9334,0x9335,0x9336,0x9337,0x9338,0x9339,0x933a,0x933b,
+ 0x933c,0x933d,0x933f,0x9340,0x9341,0x9342,0x9343,0x9344,0x9345,0x9346,
+ 0x9347,0x9348,0x9349,UBOGON,0x934a,0x934b,0x934c,0x934d,0x934e,0x934f,
+ 0x9350,0x9351,0x9352,0x9353,0x9354,0x9355,0x9356,0x9357,0x9358,0x9359,
+ 0x935a,0x935b,0x935c,0x935d,0x935e,0x935f,0x9360,0x9361,0x9362,0x9363,
+ 0x9364,0x9365,0x9366,0x9367,0x9368,0x9369,0x936b,0x6fc9,0x6fa7,0x6fb9,
+ 0x6fb6,0x6fc2,0x6fe1,0x6fee,0x6fde,0x6fe0,0x6fef,0x701a,0x7023,0x701b,
+ 0x7039,0x7035,0x704f,0x705e,0x5b80,0x5b84,0x5b95,0x5b93,0x5ba5,0x5bb8,
+ 0x752f,0x9a9e,0x6434,0x5be4,0x5bee,0x8930,0x5bf0,0x8e47,0x8b07,0x8fb6,
+ 0x8fd3,0x8fd5,0x8fe5,0x8fee,0x8fe4,0x8fe9,0x8fe6,0x8ff3,0x8fe8,0x9005,
+ 0x9004,0x900b,0x9026,0x9011,0x900d,0x9016,0x9021,0x9035,0x9036,0x902d,
+ 0x902f,0x9044,0x9051,0x9052,0x9050,0x9068,0x9058,0x9062,0x905b,0x66b9,
+ 0x9074,0x907d,0x9082,0x9088,0x9083,0x908b,0x5f50,0x5f57,0x5f56,0x5f58,
+ 0x5c3b,0x54ab,0x5c50,0x5c59,0x5b71,0x5c63,0x5c66,0x7fbc,0x5f2a,0x5f29,
+ 0x5f2d,0x8274,0x5f3c,0x9b3b,0x5c6e,0x5981,0x5983,0x598d,0x59a9,0x59aa,
+ 0x59a3
+ },
+ { /* ku 66 */
+ 0x936c,0x936d,0x936e,0x936f,0x9370,0x9371,0x9372,0x9373,0x9374,0x9375,
+ 0x9376,0x9377,0x9378,0x9379,0x937a,0x937b,0x937c,0x937d,0x937e,0x937f,
+ 0x9380,0x9381,0x9382,0x9383,0x9384,0x9385,0x9386,0x9387,0x9388,0x9389,
+ 0x938a,0x938b,0x938c,0x938d,0x938e,0x9390,0x9391,0x9392,0x9393,0x9394,
+ 0x9395,0x9396,0x9397,0x9398,0x9399,0x939a,0x939b,0x939c,0x939d,0x939e,
+ 0x939f,0x93a0,0x93a1,0x93a2,0x93a3,0x93a4,0x93a5,0x93a6,0x93a7,0x93a8,
+ 0x93a9,0x93aa,0x93ab,UBOGON,0x93ac,0x93ad,0x93ae,0x93af,0x93b0,0x93b1,
+ 0x93b2,0x93b3,0x93b4,0x93b5,0x93b6,0x93b7,0x93b8,0x93b9,0x93ba,0x93bb,
+ 0x93bc,0x93bd,0x93be,0x93bf,0x93c0,0x93c1,0x93c2,0x93c3,0x93c4,0x93c5,
+ 0x93c6,0x93c7,0x93c8,0x93c9,0x93cb,0x93cc,0x93cd,0x5997,0x59ca,0x59ab,
+ 0x599e,0x59a4,0x59d2,0x59b2,0x59af,0x59d7,0x59be,0x5a05,0x5a06,0x59dd,
+ 0x5a08,0x59e3,0x59d8,0x59f9,0x5a0c,0x5a09,0x5a32,0x5a34,0x5a11,0x5a23,
+ 0x5a13,0x5a40,0x5a67,0x5a4a,0x5a55,0x5a3c,0x5a62,0x5a75,0x80ec,0x5aaa,
+ 0x5a9b,0x5a77,0x5a7a,0x5abe,0x5aeb,0x5ab2,0x5ad2,0x5ad4,0x5ab8,0x5ae0,
+ 0x5ae3,0x5af1,0x5ad6,0x5ae6,0x5ad8,0x5adc,0x5b09,0x5b17,0x5b16,0x5b32,
+ 0x5b37,0x5b40,0x5c15,0x5c1c,0x5b5a,0x5b65,0x5b73,0x5b51,0x5b53,0x5b62,
+ 0x9a75,0x9a77,0x9a78,0x9a7a,0x9a7f,0x9a7d,0x9a80,0x9a81,0x9a85,0x9a88,
+ 0x9a8a,0x9a90,0x9a92,0x9a93,0x9a96,0x9a98,0x9a9b,0x9a9c,0x9a9d,0x9a9f,
+ 0x9aa0,0x9aa2,0x9aa3,0x9aa5,0x9aa7,0x7e9f,0x7ea1,0x7ea3,0x7ea5,0x7ea8,
+ 0x7ea9
+ },
+ { /* ku 67 */
+ 0x93ce,0x93cf,0x93d0,0x93d1,0x93d2,0x93d3,0x93d4,0x93d5,0x93d7,0x93d8,
+ 0x93d9,0x93da,0x93db,0x93dc,0x93dd,0x93de,0x93df,0x93e0,0x93e1,0x93e2,
+ 0x93e3,0x93e4,0x93e5,0x93e6,0x93e7,0x93e8,0x93e9,0x93ea,0x93eb,0x93ec,
+ 0x93ed,0x93ee,0x93ef,0x93f0,0x93f1,0x93f2,0x93f3,0x93f4,0x93f5,0x93f6,
+ 0x93f7,0x93f8,0x93f9,0x93fa,0x93fb,0x93fc,0x93fd,0x93fe,0x93ff,0x9400,
+ 0x9401,0x9402,0x9403,0x9404,0x9405,0x9406,0x9407,0x9408,0x9409,0x940a,
+ 0x940b,0x940c,0x940d,UBOGON,0x940e,0x940f,0x9410,0x9411,0x9412,0x9413,
+ 0x9414,0x9415,0x9416,0x9417,0x9418,0x9419,0x941a,0x941b,0x941c,0x941d,
+ 0x941e,0x941f,0x9420,0x9421,0x9422,0x9423,0x9424,0x9425,0x9426,0x9427,
+ 0x9428,0x9429,0x942a,0x942b,0x942c,0x942d,0x942e,0x7ead,0x7eb0,0x7ebe,
+ 0x7ec0,0x7ec1,0x7ec2,0x7ec9,0x7ecb,0x7ecc,0x7ed0,0x7ed4,0x7ed7,0x7edb,
+ 0x7ee0,0x7ee1,0x7ee8,0x7eeb,0x7eee,0x7eef,0x7ef1,0x7ef2,0x7f0d,0x7ef6,
+ 0x7efa,0x7efb,0x7efe,0x7f01,0x7f02,0x7f03,0x7f07,0x7f08,0x7f0b,0x7f0c,
+ 0x7f0f,0x7f11,0x7f12,0x7f17,0x7f19,0x7f1c,0x7f1b,0x7f1f,0x7f21,0x7f22,
+ 0x7f23,0x7f24,0x7f25,0x7f26,0x7f27,0x7f2a,0x7f2b,0x7f2c,0x7f2d,0x7f2f,
+ 0x7f30,0x7f31,0x7f32,0x7f33,0x7f35,0x5e7a,0x757f,0x5ddb,0x753e,0x9095,
+ 0x738e,0x7391,0x73ae,0x73a2,0x739f,0x73cf,0x73c2,0x73d1,0x73b7,0x73b3,
+ 0x73c0,0x73c9,0x73c8,0x73e5,0x73d9,0x987c,0x740a,0x73e9,0x73e7,0x73de,
+ 0x73ba,0x73f2,0x740f,0x742a,0x745b,0x7426,0x7425,0x7428,0x7430,0x742e,
+ 0x742c
+ },
+ { /* ku 68 */
+ 0x942f,0x9430,0x9431,0x9432,0x9433,0x9434,0x9435,0x9436,0x9437,0x9438,
+ 0x9439,0x943a,0x943b,0x943c,0x943d,0x943f,0x9440,0x9441,0x9442,0x9443,
+ 0x9444,0x9445,0x9446,0x9447,0x9448,0x9449,0x944a,0x944b,0x944c,0x944d,
+ 0x944e,0x944f,0x9450,0x9451,0x9452,0x9453,0x9454,0x9455,0x9456,0x9457,
+ 0x9458,0x9459,0x945a,0x945b,0x945c,0x945d,0x945e,0x945f,0x9460,0x9461,
+ 0x9462,0x9463,0x9464,0x9465,0x9466,0x9467,0x9468,0x9469,0x946a,0x946c,
+ 0x946d,0x946e,0x946f,UBOGON,0x9470,0x9471,0x9472,0x9473,0x9474,0x9475,
+ 0x9476,0x9477,0x9478,0x9479,0x947a,0x947b,0x947c,0x947d,0x947e,0x947f,
+ 0x9480,0x9481,0x9482,0x9483,0x9484,0x9491,0x9496,0x9498,0x94c7,0x94cf,
+ 0x94d3,0x94d4,0x94da,0x94e6,0x94fb,0x951c,0x9520,0x741b,0x741a,0x7441,
+ 0x745c,0x7457,0x7455,0x7459,0x7477,0x746d,0x747e,0x749c,0x748e,0x7480,
+ 0x7481,0x7487,0x748b,0x749e,0x74a8,0x74a9,0x7490,0x74a7,0x74d2,0x74ba,
+ 0x97ea,0x97eb,0x97ec,0x674c,0x6753,0x675e,0x6748,0x6769,0x67a5,0x6787,
+ 0x676a,0x6773,0x6798,0x67a7,0x6775,0x67a8,0x679e,0x67ad,0x678b,0x6777,
+ 0x677c,0x67f0,0x6809,0x67d8,0x680a,0x67e9,0x67b0,0x680c,0x67d9,0x67b5,
+ 0x67da,0x67b3,0x67dd,0x6800,0x67c3,0x67b8,0x67e2,0x680e,0x67c1,0x67fd,
+ 0x6832,0x6833,0x6860,0x6861,0x684e,0x6862,0x6844,0x6864,0x6883,0x681d,
+ 0x6855,0x6866,0x6841,0x6867,0x6840,0x683e,0x684a,0x6849,0x6829,0x68b5,
+ 0x688f,0x6874,0x6877,0x6893,0x686b,0x68c2,0x696e,0x68fc,0x691f,0x6920,
+ 0x68f9
+ },
+ { /* ku 69 */
+ 0x9527,0x9533,0x953d,0x9543,0x9548,0x954b,0x9555,0x955a,0x9560,0x956e,
+ 0x9574,0x9575,0x9577,0x9578,0x9579,0x957a,0x957b,0x957c,0x957d,0x957e,
+ 0x9580,0x9581,0x9582,0x9583,0x9584,0x9585,0x9586,0x9587,0x9588,0x9589,
+ 0x958a,0x958b,0x958c,0x958d,0x958e,0x958f,0x9590,0x9591,0x9592,0x9593,
+ 0x9594,0x9595,0x9596,0x9597,0x9598,0x9599,0x959a,0x959b,0x959c,0x959d,
+ 0x959e,0x959f,0x95a0,0x95a1,0x95a2,0x95a3,0x95a4,0x95a5,0x95a6,0x95a7,
+ 0x95a8,0x95a9,0x95aa,UBOGON,0x95ab,0x95ac,0x95ad,0x95ae,0x95af,0x95b0,
+ 0x95b1,0x95b2,0x95b3,0x95b4,0x95b5,0x95b6,0x95b7,0x95b8,0x95b9,0x95ba,
+ 0x95bb,0x95bc,0x95bd,0x95be,0x95bf,0x95c0,0x95c1,0x95c2,0x95c3,0x95c4,
+ 0x95c5,0x95c6,0x95c7,0x95c8,0x95c9,0x95ca,0x95cb,0x6924,0x68f0,0x690b,
+ 0x6901,0x6957,0x68e3,0x6910,0x6971,0x6939,0x6960,0x6942,0x695d,0x6984,
+ 0x696b,0x6980,0x6998,0x6978,0x6934,0x69cc,0x6987,0x6988,0x69ce,0x6989,
+ 0x6966,0x6963,0x6979,0x699b,0x69a7,0x69bb,0x69ab,0x69ad,0x69d4,0x69b1,
+ 0x69c1,0x69ca,0x69df,0x6995,0x69e0,0x698d,0x69ff,0x6a2f,0x69ed,0x6a17,
+ 0x6a18,0x6a65,0x69f2,0x6a44,0x6a3e,0x6aa0,0x6a50,0x6a5b,0x6a35,0x6a8e,
+ 0x6a79,0x6a3d,0x6a28,0x6a58,0x6a7c,0x6a91,0x6a90,0x6aa9,0x6a97,0x6aab,
+ 0x7337,0x7352,0x6b81,0x6b82,0x6b87,0x6b84,0x6b92,0x6b93,0x6b8d,0x6b9a,
+ 0x6b9b,0x6ba1,0x6baa,0x8f6b,0x8f6d,0x8f71,0x8f72,0x8f73,0x8f75,0x8f76,
+ 0x8f78,0x8f77,0x8f79,0x8f7a,0x8f7c,0x8f7e,0x8f81,0x8f82,0x8f84,0x8f87,
+ 0x8f8b
+ },
+ { /* ku 6a */
+ 0x95cc,0x95cd,0x95ce,0x95cf,0x95d0,0x95d1,0x95d2,0x95d3,0x95d4,0x95d5,
+ 0x95d6,0x95d7,0x95d8,0x95d9,0x95da,0x95db,0x95dc,0x95dd,0x95de,0x95df,
+ 0x95e0,0x95e1,0x95e2,0x95e3,0x95e4,0x95e5,0x95e6,0x95e7,0x95ec,0x95ff,
+ 0x9607,0x9613,0x9618,0x961b,0x961e,0x9620,0x9623,0x9624,0x9625,0x9626,
+ 0x9627,0x9628,0x9629,0x962b,0x962c,0x962d,0x962f,0x9630,0x9637,0x9638,
+ 0x9639,0x963a,0x963e,0x9641,0x9643,0x964a,0x964e,0x964f,0x9651,0x9652,
+ 0x9653,0x9656,0x9657,UBOGON,0x9658,0x9659,0x965a,0x965c,0x965d,0x965e,
+ 0x9660,0x9663,0x9665,0x9666,0x966b,0x966d,0x966e,0x966f,0x9670,0x9671,
+ 0x9673,0x9678,0x9679,0x967a,0x967b,0x967c,0x967d,0x967e,0x967f,0x9680,
+ 0x9681,0x9682,0x9683,0x9684,0x9687,0x9689,0x968a,0x8f8d,0x8f8e,0x8f8f,
+ 0x8f98,0x8f9a,0x8ece,0x620b,0x6217,0x621b,0x621f,0x6222,0x6221,0x6225,
+ 0x6224,0x622c,0x81e7,0x74ef,0x74f4,0x74ff,0x750f,0x7511,0x7513,0x6534,
+ 0x65ee,0x65ef,0x65f0,0x660a,0x6619,0x6772,0x6603,0x6615,0x6600,0x7085,
+ 0x66f7,0x661d,0x6634,0x6631,0x6636,0x6635,0x8006,0x665f,0x6654,0x6641,
+ 0x664f,0x6656,0x6661,0x6657,0x6677,0x6684,0x668c,0x66a7,0x669d,0x66be,
+ 0x66db,0x66dc,0x66e6,0x66e9,0x8d32,0x8d33,0x8d36,0x8d3b,0x8d3d,0x8d40,
+ 0x8d45,0x8d46,0x8d48,0x8d49,0x8d47,0x8d4d,0x8d55,0x8d59,0x89c7,0x89ca,
+ 0x89cb,0x89cc,0x89ce,0x89cf,0x89d0,0x89d1,0x726e,0x729f,0x725d,0x7266,
+ 0x726f,0x727e,0x727f,0x7284,0x728b,0x728d,0x728f,0x7292,0x6308,0x6332,
+ 0x63b0
+ },
+ { /* ku 6b */
+ 0x968c,0x968e,0x9691,0x9692,0x9693,0x9695,0x9696,0x969a,0x969b,0x969d,
+ 0x969e,0x969f,0x96a0,0x96a1,0x96a2,0x96a3,0x96a4,0x96a5,0x96a6,0x96a8,
+ 0x96a9,0x96aa,0x96ab,0x96ac,0x96ad,0x96ae,0x96af,0x96b1,0x96b2,0x96b4,
+ 0x96b5,0x96b7,0x96b8,0x96ba,0x96bb,0x96bf,0x96c2,0x96c3,0x96c8,0x96ca,
+ 0x96cb,0x96d0,0x96d1,0x96d3,0x96d4,0x96d6,0x96d7,0x96d8,0x96d9,0x96da,
+ 0x96db,0x96dc,0x96dd,0x96de,0x96df,0x96e1,0x96e2,0x96e3,0x96e4,0x96e5,
+ 0x96e6,0x96e7,0x96eb,UBOGON,0x96ec,0x96ed,0x96ee,0x96f0,0x96f1,0x96f2,
+ 0x96f4,0x96f5,0x96f8,0x96fa,0x96fb,0x96fc,0x96fd,0x96ff,0x9702,0x9703,
+ 0x9705,0x970a,0x970b,0x970c,0x9710,0x9711,0x9712,0x9714,0x9715,0x9717,
+ 0x9718,0x9719,0x971a,0x971b,0x971d,0x971f,0x9720,0x643f,0x64d8,0x8004,
+ 0x6bea,0x6bf3,0x6bfd,0x6bf5,0x6bf9,0x6c05,0x6c07,0x6c06,0x6c0d,0x6c15,
+ 0x6c18,0x6c19,0x6c1a,0x6c21,0x6c29,0x6c24,0x6c2a,0x6c32,0x6535,0x6555,
+ 0x656b,0x724d,0x7252,0x7256,0x7230,0x8662,0x5216,0x809f,0x809c,0x8093,
+ 0x80bc,0x670a,0x80bd,0x80b1,0x80ab,0x80ad,0x80b4,0x80b7,0x80e7,0x80e8,
+ 0x80e9,0x80ea,0x80db,0x80c2,0x80c4,0x80d9,0x80cd,0x80d7,0x6710,0x80dd,
+ 0x80eb,0x80f1,0x80f4,0x80ed,0x810d,0x810e,0x80f2,0x80fc,0x6715,0x8112,
+ 0x8c5a,0x8136,0x811e,0x812c,0x8118,0x8132,0x8148,0x814c,0x8153,0x8174,
+ 0x8159,0x815a,0x8171,0x8160,0x8169,0x817c,0x817d,0x816d,0x8167,0x584d,
+ 0x5ab5,0x8188,0x8182,0x8191,0x6ed5,0x81a3,0x81aa,0x81cc,0x6726,0x81ca,
+ 0x81bb
+ },
+ { /* ku 6c */
+ 0x9721,0x9722,0x9723,0x9724,0x9725,0x9726,0x9727,0x9728,0x9729,0x972b,
+ 0x972c,0x972e,0x972f,0x9731,0x9733,0x9734,0x9735,0x9736,0x9737,0x973a,
+ 0x973b,0x973c,0x973d,0x973f,0x9740,0x9741,0x9742,0x9743,0x9744,0x9745,
+ 0x9746,0x9747,0x9748,0x9749,0x974a,0x974b,0x974c,0x974d,0x974e,0x974f,
+ 0x9750,0x9751,0x9754,0x9755,0x9757,0x9758,0x975a,0x975c,0x975d,0x975f,
+ 0x9763,0x9764,0x9766,0x9767,0x9768,0x976a,0x976b,0x976c,0x976d,0x976e,
+ 0x976f,0x9770,0x9771,UBOGON,0x9772,0x9775,0x9777,0x9778,0x9779,0x977a,
+ 0x977b,0x977d,0x977e,0x977f,0x9780,0x9781,0x9782,0x9783,0x9784,0x9786,
+ 0x9787,0x9788,0x9789,0x978a,0x978c,0x978e,0x978f,0x9790,0x9793,0x9795,
+ 0x9796,0x9797,0x9799,0x979a,0x979b,0x979c,0x979d,0x81c1,0x81a6,0x6b24,
+ 0x6b37,0x6b39,0x6b43,0x6b46,0x6b59,0x98d1,0x98d2,0x98d3,0x98d5,0x98d9,
+ 0x98da,0x6bb3,0x5f40,0x6bc2,0x89f3,0x6590,0x9f51,0x6593,0x65bc,0x65c6,
+ 0x65c4,0x65c3,0x65cc,0x65ce,0x65d2,0x65d6,0x7080,0x709c,0x7096,0x709d,
+ 0x70bb,0x70c0,0x70b7,0x70ab,0x70b1,0x70e8,0x70ca,0x7110,0x7113,0x7116,
+ 0x712f,0x7131,0x7173,0x715c,0x7168,0x7145,0x7172,0x714a,0x7178,0x717a,
+ 0x7198,0x71b3,0x71b5,0x71a8,0x71a0,0x71e0,0x71d4,0x71e7,0x71f9,0x721d,
+ 0x7228,0x706c,0x7118,0x7166,0x71b9,0x623e,0x623d,0x6243,0x6248,0x6249,
+ 0x793b,0x7940,0x7946,0x7949,0x795b,0x795c,0x7953,0x795a,0x7962,0x7957,
+ 0x7960,0x796f,0x7967,0x797a,0x7985,0x798a,0x799a,0x79a7,0x79b3,0x5fd1,
+ 0x5fd0
+ },
+ { /* ku 6d */
+ 0x979e,0x979f,0x97a1,0x97a2,0x97a4,0x97a5,0x97a6,0x97a7,0x97a8,0x97a9,
+ 0x97aa,0x97ac,0x97ae,0x97b0,0x97b1,0x97b3,0x97b5,0x97b6,0x97b7,0x97b8,
+ 0x97b9,0x97ba,0x97bb,0x97bc,0x97bd,0x97be,0x97bf,0x97c0,0x97c1,0x97c2,
+ 0x97c3,0x97c4,0x97c5,0x97c6,0x97c7,0x97c8,0x97c9,0x97ca,0x97cb,0x97cc,
+ 0x97cd,0x97ce,0x97cf,0x97d0,0x97d1,0x97d2,0x97d3,0x97d4,0x97d5,0x97d6,
+ 0x97d7,0x97d8,0x97d9,0x97da,0x97db,0x97dc,0x97dd,0x97de,0x97df,0x97e0,
+ 0x97e1,0x97e2,0x97e3,UBOGON,0x97e4,0x97e5,0x97e8,0x97ee,0x97ef,0x97f0,
+ 0x97f1,0x97f2,0x97f4,0x97f7,0x97f8,0x97f9,0x97fa,0x97fb,0x97fc,0x97fd,
+ 0x97fe,0x97ff,0x9800,0x9801,0x9802,0x9803,0x9804,0x9805,0x9806,0x9807,
+ 0x9808,0x9809,0x980a,0x980b,0x980c,0x980d,0x980e,0x603c,0x605d,0x605a,
+ 0x6067,0x6041,0x6059,0x6063,0x60ab,0x6106,0x610d,0x615d,0x61a9,0x619d,
+ 0x61cb,0x61d1,0x6206,0x8080,0x807f,0x6c93,0x6cf6,0x6dfc,0x77f6,0x77f8,
+ 0x7800,0x7809,0x7817,0x7818,0x7811,0x65ab,0x782d,0x781c,0x781d,0x7839,
+ 0x783a,0x783b,0x781f,0x783c,0x7825,0x782c,0x7823,0x7829,0x784e,0x786d,
+ 0x7856,0x7857,0x7826,0x7850,0x7847,0x784c,0x786a,0x789b,0x7893,0x789a,
+ 0x7887,0x789c,0x78a1,0x78a3,0x78b2,0x78b9,0x78a5,0x78d4,0x78d9,0x78c9,
+ 0x78ec,0x78f2,0x7905,0x78f4,0x7913,0x7924,0x791e,0x7934,0x9f9b,0x9ef9,
+ 0x9efb,0x9efc,0x76f1,0x7704,0x770d,0x76f9,0x7707,0x7708,0x771a,0x7722,
+ 0x7719,0x772d,0x7726,0x7735,0x7738,0x7750,0x7751,0x7747,0x7743,0x775a,
+ 0x7768
+ },
+ { /* ku 6e */
+ 0x980f,0x9810,0x9811,0x9812,0x9813,0x9814,0x9815,0x9816,0x9817,0x9818,
+ 0x9819,0x981a,0x981b,0x981c,0x981d,0x981e,0x981f,0x9820,0x9821,0x9822,
+ 0x9823,0x9824,0x9825,0x9826,0x9827,0x9828,0x9829,0x982a,0x982b,0x982c,
+ 0x982d,0x982e,0x982f,0x9830,0x9831,0x9832,0x9833,0x9834,0x9835,0x9836,
+ 0x9837,0x9838,0x9839,0x983a,0x983b,0x983c,0x983d,0x983e,0x983f,0x9840,
+ 0x9841,0x9842,0x9843,0x9844,0x9845,0x9846,0x9847,0x9848,0x9849,0x984a,
+ 0x984b,0x984c,0x984d,UBOGON,0x984e,0x984f,0x9850,0x9851,0x9852,0x9853,
+ 0x9854,0x9855,0x9856,0x9857,0x9858,0x9859,0x985a,0x985b,0x985c,0x985d,
+ 0x985e,0x985f,0x9860,0x9861,0x9862,0x9863,0x9864,0x9865,0x9866,0x9867,
+ 0x9868,0x9869,0x986a,0x986b,0x986c,0x986d,0x986e,0x7762,0x7765,0x777f,
+ 0x778d,0x777d,0x7780,0x778c,0x7791,0x779f,0x77a0,0x77b0,0x77b5,0x77bd,
+ 0x753a,0x7540,0x754e,0x754b,0x7548,0x755b,0x7572,0x7579,0x7583,0x7f58,
+ 0x7f61,0x7f5f,0x8a48,0x7f68,0x7f74,0x7f71,0x7f79,0x7f81,0x7f7e,0x76cd,
+ 0x76e5,0x8832,0x9485,0x9486,0x9487,0x948b,0x948a,0x948c,0x948d,0x948f,
+ 0x9490,0x9494,0x9497,0x9495,0x949a,0x949b,0x949c,0x94a3,0x94a4,0x94ab,
+ 0x94aa,0x94ad,0x94ac,0x94af,0x94b0,0x94b2,0x94b4,0x94b6,0x94b7,0x94b8,
+ 0x94b9,0x94ba,0x94bc,0x94bd,0x94bf,0x94c4,0x94c8,0x94c9,0x94ca,0x94cb,
+ 0x94cc,0x94cd,0x94ce,0x94d0,0x94d1,0x94d2,0x94d5,0x94d6,0x94d7,0x94d9,
+ 0x94d8,0x94db,0x94de,0x94df,0x94e0,0x94e2,0x94e4,0x94e5,0x94e7,0x94e8,
+ 0x94ea
+ },
+ { /* ku 6f */
+ 0x986f,0x9870,0x9871,0x9872,0x9873,0x9874,0x988b,0x988e,0x9892,0x9895,
+ 0x9899,0x98a3,0x98a8,0x98a9,0x98aa,0x98ab,0x98ac,0x98ad,0x98ae,0x98af,
+ 0x98b0,0x98b1,0x98b2,0x98b3,0x98b4,0x98b5,0x98b6,0x98b7,0x98b8,0x98b9,
+ 0x98ba,0x98bb,0x98bc,0x98bd,0x98be,0x98bf,0x98c0,0x98c1,0x98c2,0x98c3,
+ 0x98c4,0x98c5,0x98c6,0x98c7,0x98c8,0x98c9,0x98ca,0x98cb,0x98cc,0x98cd,
+ 0x98cf,0x98d0,0x98d4,0x98d6,0x98d7,0x98db,0x98dc,0x98dd,0x98e0,0x98e1,
+ 0x98e2,0x98e3,0x98e4,UBOGON,0x98e5,0x98e6,0x98e9,0x98ea,0x98eb,0x98ec,
+ 0x98ed,0x98ee,0x98ef,0x98f0,0x98f1,0x98f2,0x98f3,0x98f4,0x98f5,0x98f6,
+ 0x98f7,0x98f8,0x98f9,0x98fa,0x98fb,0x98fc,0x98fd,0x98fe,0x98ff,0x9900,
+ 0x9901,0x9902,0x9903,0x9904,0x9905,0x9906,0x9907,0x94e9,0x94eb,0x94ee,
+ 0x94ef,0x94f3,0x94f4,0x94f5,0x94f7,0x94f9,0x94fc,0x94fd,0x94ff,0x9503,
+ 0x9502,0x9506,0x9507,0x9509,0x950a,0x950d,0x950e,0x950f,0x9512,0x9513,
+ 0x9514,0x9515,0x9516,0x9518,0x951b,0x951d,0x951e,0x951f,0x9522,0x952a,
+ 0x952b,0x9529,0x952c,0x9531,0x9532,0x9534,0x9536,0x9537,0x9538,0x953c,
+ 0x953e,0x953f,0x9542,0x9535,0x9544,0x9545,0x9546,0x9549,0x954c,0x954e,
+ 0x954f,0x9552,0x9553,0x9554,0x9556,0x9557,0x9558,0x9559,0x955b,0x955e,
+ 0x955f,0x955d,0x9561,0x9562,0x9564,0x9565,0x9566,0x9567,0x9568,0x9569,
+ 0x956a,0x956b,0x956c,0x956f,0x9571,0x9572,0x9573,0x953a,0x77e7,0x77ec,
+ 0x96c9,0x79d5,0x79ed,0x79e3,0x79eb,0x7a06,0x5d47,0x7a03,0x7a02,0x7a1e,
+ 0x7a14
+ },
+ { /* ku 70 */
+ 0x9908,0x9909,0x990a,0x990b,0x990c,0x990e,0x990f,0x9911,0x9912,0x9913,
+ 0x9914,0x9915,0x9916,0x9917,0x9918,0x9919,0x991a,0x991b,0x991c,0x991d,
+ 0x991e,0x991f,0x9920,0x9921,0x9922,0x9923,0x9924,0x9925,0x9926,0x9927,
+ 0x9928,0x9929,0x992a,0x992b,0x992c,0x992d,0x992f,0x9930,0x9931,0x9932,
+ 0x9933,0x9934,0x9935,0x9936,0x9937,0x9938,0x9939,0x993a,0x993b,0x993c,
+ 0x993d,0x993e,0x993f,0x9940,0x9941,0x9942,0x9943,0x9944,0x9945,0x9946,
+ 0x9947,0x9948,0x9949,UBOGON,0x994a,0x994b,0x994c,0x994d,0x994e,0x994f,
+ 0x9950,0x9951,0x9952,0x9953,0x9956,0x9957,0x9958,0x9959,0x995a,0x995b,
+ 0x995c,0x995d,0x995e,0x995f,0x9960,0x9961,0x9962,0x9964,0x9966,0x9973,
+ 0x9978,0x9979,0x997b,0x997e,0x9982,0x9983,0x9989,0x7a39,0x7a37,0x7a51,
+ 0x9ecf,0x99a5,0x7a70,0x7688,0x768e,0x7693,0x7699,0x76a4,0x74de,0x74e0,
+ 0x752c,0x9e20,0x9e22,0x9e28,0x9e29,0x9e2a,0x9e2b,0x9e2c,0x9e32,0x9e31,
+ 0x9e36,0x9e38,0x9e37,0x9e39,0x9e3a,0x9e3e,0x9e41,0x9e42,0x9e44,0x9e46,
+ 0x9e47,0x9e48,0x9e49,0x9e4b,0x9e4c,0x9e4e,0x9e51,0x9e55,0x9e57,0x9e5a,
+ 0x9e5b,0x9e5c,0x9e5e,0x9e63,0x9e66,0x9e67,0x9e68,0x9e69,0x9e6a,0x9e6b,
+ 0x9e6c,0x9e71,0x9e6d,0x9e73,0x7592,0x7594,0x7596,0x75a0,0x759d,0x75ac,
+ 0x75a3,0x75b3,0x75b4,0x75b8,0x75c4,0x75b1,0x75b0,0x75c3,0x75c2,0x75d6,
+ 0x75cd,0x75e3,0x75e8,0x75e6,0x75e4,0x75eb,0x75e7,0x7603,0x75f1,0x75fc,
+ 0x75ff,0x7610,0x7600,0x7605,0x760c,0x7617,0x760a,0x7625,0x7618,0x7615,
+ 0x7619
+ },
+ { /* ku 71 */
+ 0x998c,0x998e,0x999a,0x999b,0x999c,0x999d,0x999e,0x999f,0x99a0,0x99a1,
+ 0x99a2,0x99a3,0x99a4,0x99a6,0x99a7,0x99a9,0x99aa,0x99ab,0x99ac,0x99ad,
+ 0x99ae,0x99af,0x99b0,0x99b1,0x99b2,0x99b3,0x99b4,0x99b5,0x99b6,0x99b7,
+ 0x99b8,0x99b9,0x99ba,0x99bb,0x99bc,0x99bd,0x99be,0x99bf,0x99c0,0x99c1,
+ 0x99c2,0x99c3,0x99c4,0x99c5,0x99c6,0x99c7,0x99c8,0x99c9,0x99ca,0x99cb,
+ 0x99cc,0x99cd,0x99ce,0x99cf,0x99d0,0x99d1,0x99d2,0x99d3,0x99d4,0x99d5,
+ 0x99d6,0x99d7,0x99d8,UBOGON,0x99d9,0x99da,0x99db,0x99dc,0x99dd,0x99de,
+ 0x99df,0x99e0,0x99e1,0x99e2,0x99e3,0x99e4,0x99e5,0x99e6,0x99e7,0x99e8,
+ 0x99e9,0x99ea,0x99eb,0x99ec,0x99ed,0x99ee,0x99ef,0x99f0,0x99f1,0x99f2,
+ 0x99f3,0x99f4,0x99f5,0x99f6,0x99f7,0x99f8,0x99f9,0x761b,0x763c,0x7622,
+ 0x7620,0x7640,0x762d,0x7630,0x763f,0x7635,0x7643,0x763e,0x7633,0x764d,
+ 0x765e,0x7654,0x765c,0x7656,0x766b,0x766f,0x7fca,0x7ae6,0x7a78,0x7a79,
+ 0x7a80,0x7a86,0x7a88,0x7a95,0x7aa6,0x7aa0,0x7aac,0x7aa8,0x7aad,0x7ab3,
+ 0x8864,0x8869,0x8872,0x887d,0x887f,0x8882,0x88a2,0x88c6,0x88b7,0x88bc,
+ 0x88c9,0x88e2,0x88ce,0x88e3,0x88e5,0x88f1,0x891a,0x88fc,0x88e8,0x88fe,
+ 0x88f0,0x8921,0x8919,0x8913,0x891b,0x890a,0x8934,0x892b,0x8936,0x8941,
+ 0x8966,0x897b,0x758b,0x80e5,0x76b2,0x76b4,0x77dc,0x8012,0x8014,0x8016,
+ 0x801c,0x8020,0x8022,0x8025,0x8026,0x8027,0x8029,0x8028,0x8031,0x800b,
+ 0x8035,0x8043,0x8046,0x804d,0x8052,0x8069,0x8071,0x8983,0x9878,0x9880,
+ 0x9883
+ },
+ { /* ku 72 */
+ 0x99fa,0x99fb,0x99fc,0x99fd,0x99fe,0x99ff,0x9a00,0x9a01,0x9a02,0x9a03,
+ 0x9a04,0x9a05,0x9a06,0x9a07,0x9a08,0x9a09,0x9a0a,0x9a0b,0x9a0c,0x9a0d,
+ 0x9a0e,0x9a0f,0x9a10,0x9a11,0x9a12,0x9a13,0x9a14,0x9a15,0x9a16,0x9a17,
+ 0x9a18,0x9a19,0x9a1a,0x9a1b,0x9a1c,0x9a1d,0x9a1e,0x9a1f,0x9a20,0x9a21,
+ 0x9a22,0x9a23,0x9a24,0x9a25,0x9a26,0x9a27,0x9a28,0x9a29,0x9a2a,0x9a2b,
+ 0x9a2c,0x9a2d,0x9a2e,0x9a2f,0x9a30,0x9a31,0x9a32,0x9a33,0x9a34,0x9a35,
+ 0x9a36,0x9a37,0x9a38,UBOGON,0x9a39,0x9a3a,0x9a3b,0x9a3c,0x9a3d,0x9a3e,
+ 0x9a3f,0x9a40,0x9a41,0x9a42,0x9a43,0x9a44,0x9a45,0x9a46,0x9a47,0x9a48,
+ 0x9a49,0x9a4a,0x9a4b,0x9a4c,0x9a4d,0x9a4e,0x9a4f,0x9a50,0x9a51,0x9a52,
+ 0x9a53,0x9a54,0x9a55,0x9a56,0x9a57,0x9a58,0x9a59,0x9889,0x988c,0x988d,
+ 0x988f,0x9894,0x989a,0x989b,0x989e,0x989f,0x98a1,0x98a2,0x98a5,0x98a6,
+ 0x864d,0x8654,0x866c,0x866e,0x867f,0x867a,0x867c,0x867b,0x86a8,0x868d,
+ 0x868b,0x86ac,0x869d,0x86a7,0x86a3,0x86aa,0x8693,0x86a9,0x86b6,0x86c4,
+ 0x86b5,0x86ce,0x86b0,0x86ba,0x86b1,0x86af,0x86c9,0x86cf,0x86b4,0x86e9,
+ 0x86f1,0x86f2,0x86ed,0x86f3,0x86d0,0x8713,0x86de,0x86f4,0x86df,0x86d8,
+ 0x86d1,0x8703,0x8707,0x86f8,0x8708,0x870a,0x870d,0x8709,0x8723,0x873b,
+ 0x871e,0x8725,0x872e,0x871a,0x873e,0x8748,0x8734,0x8731,0x8729,0x8737,
+ 0x873f,0x8782,0x8722,0x877d,0x877e,0x877b,0x8760,0x8770,0x874c,0x876e,
+ 0x878b,0x8753,0x8763,0x877c,0x8764,0x8759,0x8765,0x8793,0x87af,0x87a8,
+ 0x87d2
+ },
+ { /* ku 73 */
+ 0x9a5a,0x9a5b,0x9a5c,0x9a5d,0x9a5e,0x9a5f,0x9a60,0x9a61,0x9a62,0x9a63,
+ 0x9a64,0x9a65,0x9a66,0x9a67,0x9a68,0x9a69,0x9a6a,0x9a6b,0x9a72,0x9a83,
+ 0x9a89,0x9a8d,0x9a8e,0x9a94,0x9a95,0x9a99,0x9aa6,0x9aa9,0x9aaa,0x9aab,
+ 0x9aac,0x9aad,0x9aae,0x9aaf,0x9ab2,0x9ab3,0x9ab4,0x9ab5,0x9ab9,0x9abb,
+ 0x9abd,0x9abe,0x9abf,0x9ac3,0x9ac4,0x9ac6,0x9ac7,0x9ac8,0x9ac9,0x9aca,
+ 0x9acd,0x9ace,0x9acf,0x9ad0,0x9ad2,0x9ad4,0x9ad5,0x9ad6,0x9ad7,0x9ad9,
+ 0x9ada,0x9adb,0x9adc,UBOGON,0x9add,0x9ade,0x9ae0,0x9ae2,0x9ae3,0x9ae4,
+ 0x9ae5,0x9ae7,0x9ae8,0x9ae9,0x9aea,0x9aec,0x9aee,0x9af0,0x9af1,0x9af2,
+ 0x9af3,0x9af4,0x9af5,0x9af6,0x9af7,0x9af8,0x9afa,0x9afc,0x9afd,0x9afe,
+ 0x9aff,0x9b00,0x9b01,0x9b02,0x9b04,0x9b05,0x9b06,0x87c6,0x8788,0x8785,
+ 0x87ad,0x8797,0x8783,0x87ab,0x87e5,0x87ac,0x87b5,0x87b3,0x87cb,0x87d3,
+ 0x87bd,0x87d1,0x87c0,0x87ca,0x87db,0x87ea,0x87e0,0x87ee,0x8816,0x8813,
+ 0x87fe,0x880a,0x881b,0x8821,0x8839,0x883c,0x7f36,0x7f42,0x7f44,0x7f45,
+ 0x8210,0x7afa,0x7afd,0x7b08,0x7b03,0x7b04,0x7b15,0x7b0a,0x7b2b,0x7b0f,
+ 0x7b47,0x7b38,0x7b2a,0x7b19,0x7b2e,0x7b31,0x7b20,0x7b25,0x7b24,0x7b33,
+ 0x7b3e,0x7b1e,0x7b58,0x7b5a,0x7b45,0x7b75,0x7b4c,0x7b5d,0x7b60,0x7b6e,
+ 0x7b7b,0x7b62,0x7b72,0x7b71,0x7b90,0x7ba6,0x7ba7,0x7bb8,0x7bac,0x7b9d,
+ 0x7ba8,0x7b85,0x7baa,0x7b9c,0x7ba2,0x7bab,0x7bb4,0x7bd1,0x7bc1,0x7bcc,
+ 0x7bdd,0x7bda,0x7be5,0x7be6,0x7bea,0x7c0c,0x7bfe,0x7bfc,0x7c0f,0x7c16,
+ 0x7c0b
+ },
+ { /* ku 74 */
+ 0x9b07,0x9b09,0x9b0a,0x9b0b,0x9b0c,0x9b0d,0x9b0e,0x9b10,0x9b11,0x9b12,
+ 0x9b14,0x9b15,0x9b16,0x9b17,0x9b18,0x9b19,0x9b1a,0x9b1b,0x9b1c,0x9b1d,
+ 0x9b1e,0x9b20,0x9b21,0x9b22,0x9b24,0x9b25,0x9b26,0x9b27,0x9b28,0x9b29,
+ 0x9b2a,0x9b2b,0x9b2c,0x9b2d,0x9b2e,0x9b30,0x9b31,0x9b33,0x9b34,0x9b35,
+ 0x9b36,0x9b37,0x9b38,0x9b39,0x9b3a,0x9b3d,0x9b3e,0x9b3f,0x9b40,0x9b46,
+ 0x9b4a,0x9b4b,0x9b4c,0x9b4e,0x9b50,0x9b52,0x9b53,0x9b55,0x9b56,0x9b57,
+ 0x9b58,0x9b59,0x9b5a,UBOGON,0x9b5b,0x9b5c,0x9b5d,0x9b5e,0x9b5f,0x9b60,
+ 0x9b61,0x9b62,0x9b63,0x9b64,0x9b65,0x9b66,0x9b67,0x9b68,0x9b69,0x9b6a,
+ 0x9b6b,0x9b6c,0x9b6d,0x9b6e,0x9b6f,0x9b70,0x9b71,0x9b72,0x9b73,0x9b74,
+ 0x9b75,0x9b76,0x9b77,0x9b78,0x9b79,0x9b7a,0x9b7b,0x7c1f,0x7c2a,0x7c26,
+ 0x7c38,0x7c41,0x7c40,0x81fe,0x8201,0x8202,0x8204,0x81ec,0x8844,0x8221,
+ 0x8222,0x8223,0x822d,0x822f,0x8228,0x822b,0x8238,0x823b,0x8233,0x8234,
+ 0x823e,0x8244,0x8249,0x824b,0x824f,0x825a,0x825f,0x8268,0x887e,0x8885,
+ 0x8888,0x88d8,0x88df,0x895e,0x7f9d,0x7f9f,0x7fa7,0x7faf,0x7fb0,0x7fb2,
+ 0x7c7c,0x6549,0x7c91,0x7c9d,0x7c9c,0x7c9e,0x7ca2,0x7cb2,0x7cbc,0x7cbd,
+ 0x7cc1,0x7cc7,0x7ccc,0x7ccd,0x7cc8,0x7cc5,0x7cd7,0x7ce8,0x826e,0x66a8,
+ 0x7fbf,0x7fce,0x7fd5,0x7fe5,0x7fe1,0x7fe6,0x7fe9,0x7fee,0x7ff3,0x7cf8,
+ 0x7d77,0x7da6,0x7dae,0x7e47,0x7e9b,0x9eb8,0x9eb4,0x8d73,0x8d84,0x8d94,
+ 0x8d91,0x8db1,0x8d67,0x8d6d,0x8c47,0x8c49,0x914a,0x9150,0x914e,0x914f,
+ 0x9164
+ },
+ { /* ku 75 */
+ 0x9b7c,0x9b7d,0x9b7e,0x9b7f,0x9b80,0x9b81,0x9b82,0x9b83,0x9b84,0x9b85,
+ 0x9b86,0x9b87,0x9b88,0x9b89,0x9b8a,0x9b8b,0x9b8c,0x9b8d,0x9b8e,0x9b8f,
+ 0x9b90,0x9b91,0x9b92,0x9b93,0x9b94,0x9b95,0x9b96,0x9b97,0x9b98,0x9b99,
+ 0x9b9a,0x9b9b,0x9b9c,0x9b9d,0x9b9e,0x9b9f,0x9ba0,0x9ba1,0x9ba2,0x9ba3,
+ 0x9ba4,0x9ba5,0x9ba6,0x9ba7,0x9ba8,0x9ba9,0x9baa,0x9bab,0x9bac,0x9bad,
+ 0x9bae,0x9baf,0x9bb0,0x9bb1,0x9bb2,0x9bb3,0x9bb4,0x9bb5,0x9bb6,0x9bb7,
+ 0x9bb8,0x9bb9,0x9bba,UBOGON,0x9bbb,0x9bbc,0x9bbd,0x9bbe,0x9bbf,0x9bc0,
+ 0x9bc1,0x9bc2,0x9bc3,0x9bc4,0x9bc5,0x9bc6,0x9bc7,0x9bc8,0x9bc9,0x9bca,
+ 0x9bcb,0x9bcc,0x9bcd,0x9bce,0x9bcf,0x9bd0,0x9bd1,0x9bd2,0x9bd3,0x9bd4,
+ 0x9bd5,0x9bd6,0x9bd7,0x9bd8,0x9bd9,0x9bda,0x9bdb,0x9162,0x9161,0x9170,
+ 0x9169,0x916f,0x917d,0x917e,0x9172,0x9174,0x9179,0x918c,0x9185,0x9190,
+ 0x918d,0x9191,0x91a2,0x91a3,0x91aa,0x91ad,0x91ae,0x91af,0x91b5,0x91b4,
+ 0x91ba,0x8c55,0x9e7e,0x8db8,0x8deb,0x8e05,0x8e59,0x8e69,0x8db5,0x8dbf,
+ 0x8dbc,0x8dba,0x8dc4,0x8dd6,0x8dd7,0x8dda,0x8dde,0x8dce,0x8dcf,0x8ddb,
+ 0x8dc6,0x8dec,0x8df7,0x8df8,0x8de3,0x8df9,0x8dfb,0x8de4,0x8e09,0x8dfd,
+ 0x8e14,0x8e1d,0x8e1f,0x8e2c,0x8e2e,0x8e23,0x8e2f,0x8e3a,0x8e40,0x8e39,
+ 0x8e35,0x8e3d,0x8e31,0x8e49,0x8e41,0x8e42,0x8e51,0x8e52,0x8e4a,0x8e70,
+ 0x8e76,0x8e7c,0x8e6f,0x8e74,0x8e85,0x8e8f,0x8e94,0x8e90,0x8e9c,0x8e9e,
+ 0x8c78,0x8c82,0x8c8a,0x8c85,0x8c98,0x8c94,0x659b,0x89d6,0x89de,0x89da,
+ 0x89dc
+ },
+ { /* ku 76 */
+ 0x9bdc,0x9bdd,0x9bde,0x9bdf,0x9be0,0x9be1,0x9be2,0x9be3,0x9be4,0x9be5,
+ 0x9be6,0x9be7,0x9be8,0x9be9,0x9bea,0x9beb,0x9bec,0x9bed,0x9bee,0x9bef,
+ 0x9bf0,0x9bf1,0x9bf2,0x9bf3,0x9bf4,0x9bf5,0x9bf6,0x9bf7,0x9bf8,0x9bf9,
+ 0x9bfa,0x9bfb,0x9bfc,0x9bfd,0x9bfe,0x9bff,0x9c00,0x9c01,0x9c02,0x9c03,
+ 0x9c04,0x9c05,0x9c06,0x9c07,0x9c08,0x9c09,0x9c0a,0x9c0b,0x9c0c,0x9c0d,
+ 0x9c0e,0x9c0f,0x9c10,0x9c11,0x9c12,0x9c13,0x9c14,0x9c15,0x9c16,0x9c17,
+ 0x9c18,0x9c19,0x9c1a,UBOGON,0x9c1b,0x9c1c,0x9c1d,0x9c1e,0x9c1f,0x9c20,
+ 0x9c21,0x9c22,0x9c23,0x9c24,0x9c25,0x9c26,0x9c27,0x9c28,0x9c29,0x9c2a,
+ 0x9c2b,0x9c2c,0x9c2d,0x9c2e,0x9c2f,0x9c30,0x9c31,0x9c32,0x9c33,0x9c34,
+ 0x9c35,0x9c36,0x9c37,0x9c38,0x9c39,0x9c3a,0x9c3b,0x89e5,0x89eb,0x89ef,
+ 0x8a3e,0x8b26,0x9753,0x96e9,0x96f3,0x96ef,0x9706,0x9701,0x9708,0x970f,
+ 0x970e,0x972a,0x972d,0x9730,0x973e,0x9f80,0x9f83,0x9f85,0x9f86,0x9f87,
+ 0x9f88,0x9f89,0x9f8a,0x9f8c,0x9efe,0x9f0b,0x9f0d,0x96b9,0x96bc,0x96bd,
+ 0x96ce,0x96d2,0x77bf,0x96e0,0x928e,0x92ae,0x92c8,0x933e,0x936a,0x93ca,
+ 0x938f,0x943e,0x946b,0x9c7f,0x9c82,0x9c85,0x9c86,0x9c87,0x9c88,0x7a23,
+ 0x9c8b,0x9c8e,0x9c90,0x9c91,0x9c92,0x9c94,0x9c95,0x9c9a,0x9c9b,0x9c9e,
+ 0x9c9f,0x9ca0,0x9ca1,0x9ca2,0x9ca3,0x9ca5,0x9ca6,0x9ca7,0x9ca8,0x9ca9,
+ 0x9cab,0x9cad,0x9cae,0x9cb0,0x9cb1,0x9cb2,0x9cb3,0x9cb4,0x9cb5,0x9cb6,
+ 0x9cb7,0x9cba,0x9cbb,0x9cbc,0x9cbd,0x9cc4,0x9cc5,0x9cc6,0x9cc7,0x9cca,
+ 0x9ccb
+ },
+ { /* ku 77 */
+ 0x9c3c,0x9c3d,0x9c3e,0x9c3f,0x9c40,0x9c41,0x9c42,0x9c43,0x9c44,0x9c45,
+ 0x9c46,0x9c47,0x9c48,0x9c49,0x9c4a,0x9c4b,0x9c4c,0x9c4d,0x9c4e,0x9c4f,
+ 0x9c50,0x9c51,0x9c52,0x9c53,0x9c54,0x9c55,0x9c56,0x9c57,0x9c58,0x9c59,
+ 0x9c5a,0x9c5b,0x9c5c,0x9c5d,0x9c5e,0x9c5f,0x9c60,0x9c61,0x9c62,0x9c63,
+ 0x9c64,0x9c65,0x9c66,0x9c67,0x9c68,0x9c69,0x9c6a,0x9c6b,0x9c6c,0x9c6d,
+ 0x9c6e,0x9c6f,0x9c70,0x9c71,0x9c72,0x9c73,0x9c74,0x9c75,0x9c76,0x9c77,
+ 0x9c78,0x9c79,0x9c7a,UBOGON,0x9c7b,0x9c7d,0x9c7e,0x9c80,0x9c83,0x9c84,
+ 0x9c89,0x9c8a,0x9c8c,0x9c8f,0x9c93,0x9c96,0x9c97,0x9c98,0x9c99,0x9c9d,
+ 0x9caa,0x9cac,0x9caf,0x9cb9,0x9cbe,0x9cbf,0x9cc0,0x9cc1,0x9cc2,0x9cc8,
+ 0x9cc9,0x9cd1,0x9cd2,0x9cda,0x9cdb,0x9ce0,0x9ce1,0x9ccc,0x9ccd,0x9cce,
+ 0x9ccf,0x9cd0,0x9cd3,0x9cd4,0x9cd5,0x9cd7,0x9cd8,0x9cd9,0x9cdc,0x9cdd,
+ 0x9cdf,0x9ce2,0x977c,0x9785,0x9791,0x9792,0x9794,0x97af,0x97ab,0x97a3,
+ 0x97b2,0x97b4,0x9ab1,0x9ab0,0x9ab7,0x9e58,0x9ab6,0x9aba,0x9abc,0x9ac1,
+ 0x9ac0,0x9ac5,0x9ac2,0x9acb,0x9acc,0x9ad1,0x9b45,0x9b43,0x9b47,0x9b49,
+ 0x9b48,0x9b4d,0x9b51,0x98e8,0x990d,0x992e,0x9955,0x9954,0x9adf,0x9ae1,
+ 0x9ae6,0x9aef,0x9aeb,0x9afb,0x9aed,0x9af9,0x9b08,0x9b0f,0x9b13,0x9b1f,
+ 0x9b23,0x9ebd,0x9ebe,0x7e3b,0x9e82,0x9e87,0x9e88,0x9e8b,0x9e92,0x93d6,
+ 0x9e9d,0x9e9f,0x9edb,0x9edc,0x9edd,0x9ee0,0x9edf,0x9ee2,0x9ee9,0x9ee7,
+ 0x9ee5,0x9eea,0x9eef,0x9f22,0x9f2c,0x9f2f,0x9f39,0x9f37,0x9f3d,0x9f3e,
+ 0x9f44
+ },
+ { /* ku 78 */
+ 0x9ce3,0x9ce4,0x9ce5,0x9ce6,0x9ce7,0x9ce8,0x9ce9,0x9cea,0x9ceb,0x9cec,
+ 0x9ced,0x9cee,0x9cef,0x9cf0,0x9cf1,0x9cf2,0x9cf3,0x9cf4,0x9cf5,0x9cf6,
+ 0x9cf7,0x9cf8,0x9cf9,0x9cfa,0x9cfb,0x9cfc,0x9cfd,0x9cfe,0x9cff,0x9d00,
+ 0x9d01,0x9d02,0x9d03,0x9d04,0x9d05,0x9d06,0x9d07,0x9d08,0x9d09,0x9d0a,
+ 0x9d0b,0x9d0c,0x9d0d,0x9d0e,0x9d0f,0x9d10,0x9d11,0x9d12,0x9d13,0x9d14,
+ 0x9d15,0x9d16,0x9d17,0x9d18,0x9d19,0x9d1a,0x9d1b,0x9d1c,0x9d1d,0x9d1e,
+ 0x9d1f,0x9d20,0x9d21,UBOGON,0x9d22,0x9d23,0x9d24,0x9d25,0x9d26,0x9d27,
+ 0x9d28,0x9d29,0x9d2a,0x9d2b,0x9d2c,0x9d2d,0x9d2e,0x9d2f,0x9d30,0x9d31,
+ 0x9d32,0x9d33,0x9d34,0x9d35,0x9d36,0x9d37,0x9d38,0x9d39,0x9d3a,0x9d3b,
+ 0x9d3c,0x9d3d,0x9d3e,0x9d3f,0x9d40,0x9d41,0x9d42,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 79 */
+ 0x9d43,0x9d44,0x9d45,0x9d46,0x9d47,0x9d48,0x9d49,0x9d4a,0x9d4b,0x9d4c,
+ 0x9d4d,0x9d4e,0x9d4f,0x9d50,0x9d51,0x9d52,0x9d53,0x9d54,0x9d55,0x9d56,
+ 0x9d57,0x9d58,0x9d59,0x9d5a,0x9d5b,0x9d5c,0x9d5d,0x9d5e,0x9d5f,0x9d60,
+ 0x9d61,0x9d62,0x9d63,0x9d64,0x9d65,0x9d66,0x9d67,0x9d68,0x9d69,0x9d6a,
+ 0x9d6b,0x9d6c,0x9d6d,0x9d6e,0x9d6f,0x9d70,0x9d71,0x9d72,0x9d73,0x9d74,
+ 0x9d75,0x9d76,0x9d77,0x9d78,0x9d79,0x9d7a,0x9d7b,0x9d7c,0x9d7d,0x9d7e,
+ 0x9d7f,0x9d80,0x9d81,UBOGON,0x9d82,0x9d83,0x9d84,0x9d85,0x9d86,0x9d87,
+ 0x9d88,0x9d89,0x9d8a,0x9d8b,0x9d8c,0x9d8d,0x9d8e,0x9d8f,0x9d90,0x9d91,
+ 0x9d92,0x9d93,0x9d94,0x9d95,0x9d96,0x9d97,0x9d98,0x9d99,0x9d9a,0x9d9b,
+ 0x9d9c,0x9d9d,0x9d9e,0x9d9f,0x9da0,0x9da1,0x9da2,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 7a */
+ 0x9da3,0x9da4,0x9da5,0x9da6,0x9da7,0x9da8,0x9da9,0x9daa,0x9dab,0x9dac,
+ 0x9dad,0x9dae,0x9daf,0x9db0,0x9db1,0x9db2,0x9db3,0x9db4,0x9db5,0x9db6,
+ 0x9db7,0x9db8,0x9db9,0x9dba,0x9dbb,0x9dbc,0x9dbd,0x9dbe,0x9dbf,0x9dc0,
+ 0x9dc1,0x9dc2,0x9dc3,0x9dc4,0x9dc5,0x9dc6,0x9dc7,0x9dc8,0x9dc9,0x9dca,
+ 0x9dcb,0x9dcc,0x9dcd,0x9dce,0x9dcf,0x9dd0,0x9dd1,0x9dd2,0x9dd3,0x9dd4,
+ 0x9dd5,0x9dd6,0x9dd7,0x9dd8,0x9dd9,0x9dda,0x9ddb,0x9ddc,0x9ddd,0x9dde,
+ 0x9ddf,0x9de0,0x9de1,UBOGON,0x9de2,0x9de3,0x9de4,0x9de5,0x9de6,0x9de7,
+ 0x9de8,0x9de9,0x9dea,0x9deb,0x9dec,0x9ded,0x9dee,0x9def,0x9df0,0x9df1,
+ 0x9df2,0x9df3,0x9df4,0x9df5,0x9df6,0x9df7,0x9df8,0x9df9,0x9dfa,0x9dfb,
+ 0x9dfc,0x9dfd,0x9dfe,0x9dff,0x9e00,0x9e01,0x9e02,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 7b */
+ 0x9e03,0x9e04,0x9e05,0x9e06,0x9e07,0x9e08,0x9e09,0x9e0a,0x9e0b,0x9e0c,
+ 0x9e0d,0x9e0e,0x9e0f,0x9e10,0x9e11,0x9e12,0x9e13,0x9e14,0x9e15,0x9e16,
+ 0x9e17,0x9e18,0x9e19,0x9e1a,0x9e1b,0x9e1c,0x9e1d,0x9e1e,0x9e24,0x9e27,
+ 0x9e2e,0x9e30,0x9e34,0x9e3b,0x9e3c,0x9e40,0x9e4d,0x9e50,0x9e52,0x9e53,
+ 0x9e54,0x9e56,0x9e59,0x9e5d,0x9e5f,0x9e60,0x9e61,0x9e62,0x9e65,0x9e6e,
+ 0x9e6f,0x9e72,0x9e74,0x9e75,0x9e76,0x9e77,0x9e78,0x9e79,0x9e7a,0x9e7b,
+ 0x9e7c,0x9e7d,0x9e80,UBOGON,0x9e81,0x9e83,0x9e84,0x9e85,0x9e86,0x9e89,
+ 0x9e8a,0x9e8c,0x9e8d,0x9e8e,0x9e8f,0x9e90,0x9e91,0x9e94,0x9e95,0x9e96,
+ 0x9e97,0x9e98,0x9e99,0x9e9a,0x9e9b,0x9e9c,0x9e9e,0x9ea0,0x9ea1,0x9ea2,
+ 0x9ea3,0x9ea4,0x9ea5,0x9ea7,0x9ea8,0x9ea9,0x9eaa,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 7c */
+ 0x9eab,0x9eac,0x9ead,0x9eae,0x9eaf,0x9eb0,0x9eb1,0x9eb2,0x9eb3,0x9eb5,
+ 0x9eb6,0x9eb7,0x9eb9,0x9eba,0x9ebc,0x9ebf,0x9ec0,0x9ec1,0x9ec2,0x9ec3,
+ 0x9ec5,0x9ec6,0x9ec7,0x9ec8,0x9eca,0x9ecb,0x9ecc,0x9ed0,0x9ed2,0x9ed3,
+ 0x9ed5,0x9ed6,0x9ed7,0x9ed9,0x9eda,0x9ede,0x9ee1,0x9ee3,0x9ee4,0x9ee6,
+ 0x9ee8,0x9eeb,0x9eec,0x9eed,0x9eee,0x9ef0,0x9ef1,0x9ef2,0x9ef3,0x9ef4,
+ 0x9ef5,0x9ef6,0x9ef7,0x9ef8,0x9efa,0x9efd,0x9eff,0x9f00,0x9f01,0x9f02,
+ 0x9f03,0x9f04,0x9f05,UBOGON,0x9f06,0x9f07,0x9f08,0x9f09,0x9f0a,0x9f0c,
+ 0x9f0f,0x9f11,0x9f12,0x9f14,0x9f15,0x9f16,0x9f18,0x9f1a,0x9f1b,0x9f1c,
+ 0x9f1d,0x9f1e,0x9f1f,0x9f21,0x9f23,0x9f24,0x9f25,0x9f26,0x9f27,0x9f28,
+ 0x9f29,0x9f2a,0x9f2b,0x9f2d,0x9f2e,0x9f30,0x9f31,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ },
+ { /* ku 7d */
+ 0x9f32,0x9f33,0x9f34,0x9f35,0x9f36,0x9f38,0x9f3a,0x9f3c,0x9f3f,0x9f40,
+ 0x9f41,0x9f42,0x9f43,0x9f45,0x9f46,0x9f47,0x9f48,0x9f49,0x9f4a,0x9f4b,
+ 0x9f4c,0x9f4d,0x9f4e,0x9f4f,0x9f52,0x9f53,0x9f54,0x9f55,0x9f56,0x9f57,
+ 0x9f58,0x9f59,0x9f5a,0x9f5b,0x9f5c,0x9f5d,0x9f5e,0x9f5f,0x9f60,0x9f61,
+ 0x9f62,0x9f63,0x9f64,0x9f65,0x9f66,0x9f67,0x9f68,0x9f69,0x9f6a,0x9f6b,
+ 0x9f6c,0x9f6d,0x9f6e,0x9f6f,0x9f70,0x9f71,0x9f72,0x9f73,0x9f74,0x9f75,
+ 0x9f76,0x9f77,0x9f78,UBOGON,0x9f79,0x9f7a,0x9f7b,0x9f7c,0x9f7d,0x9f7e,
+ 0x9f81,0x9f82,0x9f8d,0x9f8e,0x9f8f,0x9f90,0x9f91,0x9f92,0x9f93,0x9f94,
+ 0x9f95,0x9f96,0x9f97,0x9f98,0x9f9c,0x9f9d,0x9f9e,0x9fa1,0x9fa2,0x9fa3,
+ 0x9fa4,0x9fa5,0xf92c,0xf979,0xf995,0xf9e7,0xf9f1,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON
+ }
+};
diff --git a/imap/src/charset/ibm.c b/imap/src/charset/ibm.c
new file mode 100644
index 00000000..1a717329
--- /dev/null
+++ b/imap/src/charset/ibm.c
@@ -0,0 +1,347 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: IBM conversion tables
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 4 November 2002
+ * Last Edited: 30 August 2006
+ */
+
+ /* IBM Latin US */
+static const unsigned short ibm_437tab[128] = {
+ 0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x00e0,0x00e5,0x00e7,
+ 0x00ea,0x00eb,0x00e8,0x00ef,0x00ee,0x00ec,0x00c4,0x00c5,
+ 0x00c9,0x00e6,0x00c6,0x00f4,0x00f6,0x00f2,0x00fb,0x00f9,
+ 0x00ff,0x00d6,0x00dc,0x00a2,0x00a3,0x00a5,0x20a7,0x0192,
+ 0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,
+ 0x00bf,0x2310,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb,
+ 0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,
+ 0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510,
+ 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,
+ 0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567,
+ 0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,
+ 0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580,
+ 0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,
+ 0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229,
+ 0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,
+ 0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0
+};
+
+ /* IBM Greek */
+static const unsigned short ibm_737tab[128] = {
+ 0x0391,0x0392,0x0393,0x0394,0x0395,0x0396,0x0397,0x0398,
+ 0x0399,0x039a,0x039b,0x039c,0x039d,0x039e,0x039f,0x03a0,
+ 0x03a1,0x03a3,0x03a4,0x03a5,0x03a6,0x03a7,0x03a8,0x03a9,
+ 0x03b1,0x03b2,0x03b3,0x03b4,0x03b5,0x03b6,0x03b7,0x03b8,
+ 0x03b9,0x03ba,0x03bb,0x03bc,0x03bd,0x03be,0x03bf,0x03c0,
+ 0x03c1,0x03c3,0x03c2,0x03c4,0x03c5,0x03c6,0x03c7,0x03c8,
+ 0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,
+ 0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510,
+ 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,
+ 0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567,
+ 0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,
+ 0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580,
+ 0x03c9,0x03ac,0x03ad,0x03ae,0x03ca,0x03af,0x03cc,0x03cd,
+ 0x03cb,0x03ce,0x0386,0x0388,0x0389,0x038a,0x038c,0x038e,
+ 0x038f,0x00b1,0x2265,0x2264,0x03aa,0x03ab,0x00f7,0x2248,
+ 0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0
+};
+
+ /* IBM Baltic Rim */
+static const unsigned short ibm_775tab[128] = {
+ 0x0106,0x00fc,0x00e9,0x0101,0x00e4,0x0123,0x00e5,0x0107,
+ 0x0142,0x0113,0x0156,0x0157,0x012b,0x0179,0x00c4,0x00c5,
+ 0x00c9,0x00e6,0x00c6,0x014d,0x00f6,0x0122,0x00a2,0x015a,
+ 0x015b,0x00d6,0x00dc,0x00f8,0x00a3,0x00d8,0x00d7,0x00a4,
+ 0x0100,0x012a,0x00f3,0x017b,0x017c,0x017a,0x201d,0x00a6,
+ 0x00a9,0x00ae,0x00ac,0x00bd,0x00bc,0x0141,0x00ab,0x00bb,
+ 0x2591,0x2592,0x2593,0x2502,0x2524,0x0104,0x010c,0x0118,
+ 0x0116,0x2563,0x2551,0x2557,0x255d,0x012e,0x0160,0x2510,
+ 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x0172,0x016a,
+ 0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x017d,
+ 0x0105,0x010d,0x0119,0x0117,0x012f,0x0161,0x0173,0x016b,
+ 0x017e,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580,
+ 0x00d3,0x00df,0x014c,0x0143,0x00f5,0x00d5,0x00b5,0x0144,
+ 0x0136,0x0137,0x013b,0x013c,0x0146,0x0112,0x0145,0x2019,
+ 0x00ad,0x00b1,0x201c,0x00be,0x00b6,0x00a7,0x00f7,0x201e,
+ 0x00b0,0x2219,0x00b7,0x00b9,0x00b3,0x00b2,0x25a0,0x00a0
+};
+
+ /* IBM Latin 1 */
+static const unsigned short ibm_850tab[128] = {
+ 0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x00e0,0x00e5,0x00e7,
+ 0x00ea,0x00eb,0x00e8,0x00ef,0x00ee,0x00ec,0x00c4,0x00c5,
+ 0x00c9,0x00e6,0x00c6,0x00f4,0x00f6,0x00f2,0x00fb,0x00f9,
+ 0x00ff,0x00d6,0x00dc,0x00f8,0x00a3,0x00d8,0x00d7,0x0192,
+ 0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,
+ 0x00bf,0x00ae,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb,
+ 0x2591,0x2592,0x2593,0x2502,0x2524,0x00c1,0x00c2,0x00c0,
+ 0x00a9,0x2563,0x2551,0x2557,0x255d,0x00a2,0x00a5,0x2510,
+ 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x00e3,0x00c3,
+ 0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x00a4,
+ 0x00f0,0x00d0,0x00ca,0x00cb,0x00c8,0x0131,0x00cd,0x00ce,
+ 0x00cf,0x2518,0x250c,0x2588,0x2584,0x00a6,0x00cc,0x2580,
+ 0x00d3,0x00df,0x00d4,0x00d2,0x00f5,0x00d5,0x00b5,0x00fe,
+ 0x00de,0x00da,0x00db,0x00d9,0x00fd,0x00dd,0x00af,0x00b4,
+ 0x00ad,0x00b1,0x2017,0x00be,0x00b6,0x00a7,0x00f7,0x00b8,
+ 0x00b0,0x00a8,0x00b7,0x00b9,0x00b3,0x00b2,0x25a0,0x00a0
+};
+
+ /* IBM Latin 2 */
+static const unsigned short ibm_852tab[128] = {
+ 0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x016f,0x0107,0x00e7,
+ 0x0142,0x00eb,0x0150,0x0151,0x00ee,0x0179,0x00c4,0x0106,
+ 0x00c9,0x0139,0x013a,0x00f4,0x00f6,0x013d,0x013e,0x015a,
+ 0x015b,0x00d6,0x00dc,0x0164,0x0165,0x0141,0x00d7,0x010d,
+ 0x00e1,0x00ed,0x00f3,0x00fa,0x0104,0x0105,0x017d,0x017e,
+ 0x0118,0x0119,0x00ac,0x017a,0x010c,0x015f,0x00ab,0x00bb,
+ 0x2591,0x2592,0x2593,0x2502,0x2524,0x00c1,0x00c2,0x011a,
+ 0x015e,0x2563,0x2551,0x2557,0x255d,0x017b,0x017c,0x2510,
+ 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x0102,0x0103,
+ 0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x00a4,
+ 0x0111,0x0110,0x010e,0x00cb,0x010f,0x0147,0x00cd,0x00ce,
+ 0x011b,0x2518,0x250c,0x2588,0x2584,0x0162,0x016e,0x2580,
+ 0x00d3,0x00df,0x00d4,0x0143,0x0144,0x0148,0x0160,0x0161,
+ 0x0154,0x00da,0x0155,0x0170,0x00fd,0x00dd,0x0163,0x00b4,
+ 0x00ad,0x02dd,0x02db,0x02c7,0x02d8,0x00a7,0x00f7,0x00b8,
+ 0x00b0,0x00a8,0x02d9,0x0171,0x0158,0x0159,0x25a0,0x00a0
+};
+
+ /* IBM Cyrillic */
+static const unsigned short ibm_855tab[128] = {
+ 0x0452,0x0402,0x0453,0x0403,0x0451,0x0401,0x0454,0x0404,
+ 0x0455,0x0405,0x0456,0x0406,0x0457,0x0407,0x0458,0x0408,
+ 0x0459,0x0409,0x045a,0x040a,0x045b,0x040b,0x045c,0x040c,
+ 0x045e,0x040e,0x045f,0x040f,0x044e,0x042e,0x044a,0x042a,
+ 0x0430,0x0410,0x0431,0x0411,0x0446,0x0426,0x0434,0x0414,
+ 0x0435,0x0415,0x0444,0x0424,0x0433,0x0413,0x00ab,0x00bb,
+ 0x2591,0x2592,0x2593,0x2502,0x2524,0x0445,0x0425,0x0438,
+ 0x0418,0x2563,0x2551,0x2557,0x255d,0x0439,0x0419,0x2510,
+ 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x043a,0x041a,
+ 0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x00a4,
+ 0x043b,0x041b,0x043c,0x041c,0x043d,0x041d,0x043e,0x041e,
+ 0x043f,0x2518,0x250c,0x2588,0x2584,0x041f,0x044f,0x2580,
+ 0x042f,0x0440,0x0420,0x0441,0x0421,0x0442,0x0422,0x0443,
+ 0x0423,0x0436,0x0416,0x0432,0x0412,0x044c,0x042c,0x2116,
+ 0x00ad,0x044b,0x042b,0x0437,0x0417,0x0448,0x0428,0x044d,
+ 0x042d,0x0449,0x0429,0x0447,0x0427,0x00a7,0x25a0,0x00a0
+};
+
+ /* IBM Turkish */
+static const unsigned short ibm_857tab[128] = {
+ 0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x00e0,0x00e5,0x00e7,
+ 0x00ea,0x00eb,0x00e8,0x00ef,0x00ee,0x0131,0x00c4,0x00c5,
+ 0x00c9,0x00e6,0x00c6,0x00f4,0x00f6,0x00f2,0x00fb,0x00f9,
+ 0x0130,0x00d6,0x00dc,0x00f8,0x00a3,0x00d8,0x015e,0x015f,
+ 0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x011e,0x011f,
+ 0x00bf,0x00ae,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb,
+ 0x2591,0x2592,0x2593,0x2502,0x2524,0x00c1,0x00c2,0x00c0,
+ 0x00a9,0x2563,0x2551,0x2557,0x255d,0x00a2,0x00a5,0x2510,
+ 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x00e3,0x00c3,
+ 0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x00a4,
+ 0x00ba,0x00aa,0x00ca,0x00cb,0x00c8,UBOGON,0x00cd,0x00ce,
+ 0x00cf,0x2518,0x250c,0x2588,0x2584,0x00a6,0x00cc,0x2580,
+ 0x00d3,0x00df,0x00d4,0x00d2,0x00f5,0x00d5,0x00b5,UBOGON,
+ 0x00d7,0x00da,0x00db,0x00d9,0x00ec,0x00ff,0x00af,0x00b4,
+ 0x00ad,0x00b1,UBOGON,0x00be,0x00b6,0x00a7,0x00f7,0x00b8,
+ 0x00b0,0x00a8,0x00b7,0x00b9,0x00b3,0x00b2,0x25a0,0x00a0
+};
+
+ /* IBM Portuguese */
+static const unsigned short ibm_860tab[128] = {
+ 0x00c7,0x00fc,0x00e9,0x00e2,0x00e3,0x00e0,0x00c1,0x00e7,
+ 0x00ea,0x00ca,0x00e8,0x00cd,0x00d4,0x00ec,0x00c3,0x00c2,
+ 0x00c9,0x00c0,0x00c8,0x00f4,0x00f5,0x00f2,0x00da,0x00f9,
+ 0x00cc,0x00d5,0x00dc,0x00a2,0x00a3,0x00d9,0x20a7,0x00d3,
+ 0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,
+ 0x00bf,0x00d2,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb,
+ 0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,
+ 0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510,
+ 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,
+ 0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567,
+ 0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,
+ 0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580,
+ 0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,
+ 0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229,
+ 0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,
+ 0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0
+};
+
+ /* IBM Icelandic */
+static const unsigned short ibm_861tab[128] = {
+ 0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x00e0,0x00e5,0x00e7,
+ 0x00ea,0x00eb,0x00e8,0x00d0,0x00f0,0x00de,0x00c4,0x00c5,
+ 0x00c9,0x00e6,0x00c6,0x00f4,0x00f6,0x00fe,0x00fb,0x00dd,
+ 0x00fd,0x00d6,0x00dc,0x00f8,0x00a3,0x00d8,0x20a7,0x0192,
+ 0x00e1,0x00ed,0x00f3,0x00fa,0x00c1,0x00cd,0x00d3,0x00da,
+ 0x00bf,0x2310,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb,
+ 0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,
+ 0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510,
+ 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,
+ 0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567,
+ 0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,
+ 0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580,
+ 0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,
+ 0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229,
+ 0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,
+ 0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0
+};
+
+ /* IBM Hebrew */
+static const unsigned short ibm_862tab[128] = {
+ 0x05d0,0x05d1,0x05d2,0x05d3,0x05d4,0x05d5,0x05d6,0x05d7,
+ 0x05d8,0x05d9,0x05da,0x05db,0x05dc,0x05dd,0x05de,0x05df,
+ 0x05e0,0x05e1,0x05e2,0x05e3,0x05e4,0x05e5,0x05e6,0x05e7,
+ 0x05e8,0x05e9,0x05ea,0x00a2,0x00a3,0x00a5,0x20a7,0x0192,
+ 0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,
+ 0x00bf,0x2310,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb,
+ 0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,
+ 0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510,
+ 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,
+ 0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567,
+ 0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,
+ 0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580,
+ 0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,
+ 0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229,
+ 0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,
+ 0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0
+};
+
+ /* IBM Canada/French */
+static const unsigned short ibm_863tab[128] = {
+ 0x00c7,0x00fc,0x00e9,0x00e2,0x00c2,0x00e0,0x00b6,0x00e7,
+ 0x00ea,0x00eb,0x00e8,0x00ef,0x00ee,0x2017,0x00c0,0x00a7,
+ 0x00c9,0x00c8,0x00ca,0x00f4,0x00cb,0x00cf,0x00fb,0x00f9,
+ 0x00a4,0x00d4,0x00dc,0x00a2,0x00a3,0x00d9,0x00db,0x0192,
+ 0x00a6,0x00b4,0x00f3,0x00fa,0x00a8,0x00b8,0x00b3,0x00af,
+ 0x00ce,0x2310,0x00ac,0x00bd,0x00bc,0x00be,0x00ab,0x00bb,
+ 0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,
+ 0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510,
+ 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,
+ 0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567,
+ 0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,
+ 0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580,
+ 0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,
+ 0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229,
+ 0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,
+ 0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0
+};
+
+ /* IBM Arabic */
+static const unsigned short ibm_864tab[128] = {
+ 0x00b0,0x00b7,0x2219,0x221a,0x2592,0x2500,0x2502,0x253c,
+ 0x2524,0x252c,0x251c,0x2534,0x2510,0x250c,0x2514,0x2518,
+ 0x03b2,0x221e,0x03c6,0x00b1,0x00bd,0x00bc,0x2248,0x00ab,
+ 0x00bb,0xfef7,0xfef8,UBOGON,UBOGON,0xfefb,0xfefc,UBOGON,
+ 0x00a0,0x00ad,0xfe82,0x00a3,0x00a4,0xfe84,UBOGON,UBOGON,
+ 0xfe8e,0xfe8f,0xfe95,0xfe99,0x060c,0xfe9d,0xfea1,0xfea5,
+ 0x0660,0x0661,0x0662,0x0663,0x0664,0x0665,0x0666,0x0667,
+ 0x0668,0x0669,0xfed1,0x061b,0xfeb1,0xfeb5,0xfeb9,0x061f,
+ 0x00a2,0xfe80,0xfe81,0xfe83,0xfe85,0xfeca,0xfe8b,0xfe8d,
+ 0xfe91,0xfe93,0xfe97,0xfe9b,0xfe9f,0xfea3,0xfea7,0xfea9,
+ 0xfeab,0xfead,0xfeaf,0xfeb3,0xfeb7,0xfebb,0xfebf,0xfec1,
+ 0xfec5,0xfecb,0xfecf,0x00a6,0x00ac,0x00f7,0x00d7,0xfec9,
+ 0x0640,0xfed3,0xfed7,0xfedb,0xfedf,0xfee3,0xfee7,0xfeeb,
+ 0xfeed,0xfeef,0xfef3,0xfebd,0xfecc,0xfece,0xfecd,0xfee1,
+ 0xfe7d,0x0651,0xfee5,0xfee9,0xfeec,0xfef0,0xfef2,0xfed0,
+ 0xfed5,0xfef5,0xfef6,0xfedd,0xfed9,0xfef1,0x25a0,UBOGON
+};
+
+ /* IBM Nordic */
+static const unsigned short ibm_865tab[128] = {
+ 0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x00e0,0x00e5,0x00e7,
+ 0x00ea,0x00eb,0x00e8,0x00ef,0x00ee,0x00ec,0x00c4,0x00c5,
+ 0x00c9,0x00e6,0x00c6,0x00f4,0x00f6,0x00f2,0x00fb,0x00f9,
+ 0x00ff,0x00d6,0x00dc,0x00f8,0x00a3,0x00d8,0x20a7,0x0192,
+ 0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,
+ 0x00bf,0x2310,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00a4,
+ 0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,
+ 0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510,
+ 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,
+ 0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567,
+ 0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,
+ 0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580,
+ 0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,
+ 0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229,
+ 0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,
+ 0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0
+};
+
+ /* IBM Cyrillic/Russian */
+static const unsigned short ibm_866tab[128] = {
+ 0x0410,0x0411,0x0412,0x0413,0x0414,0x0415,0x0416,0x0417,
+ 0x0418,0x0419,0x041a,0x041b,0x041c,0x041d,0x041e,0x041f,
+ 0x0420,0x0421,0x0422,0x0423,0x0424,0x0425,0x0426,0x0427,
+ 0x0428,0x0429,0x042a,0x042b,0x042c,0x042d,0x042e,0x042f,
+ 0x0430,0x0431,0x0432,0x0433,0x0434,0x0435,0x0436,0x0437,
+ 0x0438,0x0439,0x043a,0x043b,0x043c,0x043d,0x043e,0x043f,
+ 0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,
+ 0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510,
+ 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,
+ 0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567,
+ 0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,
+ 0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580,
+ 0x0440,0x0441,0x0442,0x0443,0x0444,0x0445,0x0446,0x0447,
+ 0x0448,0x0449,0x044a,0x044b,0x044c,0x044d,0x044e,0x044f,
+ 0x0401,0x0451,0x0404,0x0454,0x0407,0x0457,0x040e,0x045e,
+ 0x00b0,0x2219,0x00b7,0x221a,0x2116,0x00a4,0x25a0,0x00a0
+};
+
+ /* IBM Greek 2 */
+static const unsigned short ibm_869tab[128] = {
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x0386,UBOGON,
+ 0x00b7,0x00ac,0x00a6,0x2018,0x2019,0x0388,0x2015,0x0389,
+ 0x038a,0x03aa,0x038c,UBOGON,UBOGON,0x038e,0x03ab,0x00a9,
+ 0x038f,0x00b2,0x00b3,0x03ac,0x00a3,0x03ad,0x03ae,0x03af,
+ 0x03ca,0x0390,0x03cc,0x03cd,0x0391,0x0392,0x0393,0x0394,
+ 0x0395,0x0396,0x0397,0x00bd,0x0398,0x0399,0x00ab,0x00bb,
+ 0x2591,0x2592,0x2593,0x2502,0x2524,0x039a,0x039b,0x039c,
+ 0x039d,0x2563,0x2551,0x2557,0x255d,0x039e,0x039f,0x2510,
+ 0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x03a0,0x03a1,
+ 0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x03a3,
+ 0x03a4,0x03a5,0x03a6,0x03a7,0x03a8,0x03a9,0x03b1,0x03b2,
+ 0x03b3,0x2518,0x250c,0x2588,0x2584,0x03b4,0x03b5,0x2580,
+ 0x03b6,0x03b7,0x03b8,0x03b9,0x03ba,0x03bb,0x03bc,0x03bd,
+ 0x03be,0x03bf,0x03c0,0x03c1,0x03c3,0x03c2,0x03c4,0x0384,
+ 0x00ad,0x00b1,0x03c5,0x03c6,0x03c7,0x00a7,0x03c8,0x0385,
+ 0x00b0,0x00a8,0x03c9,0x03cb,0x03b0,0x03ce,0x25a0,0x00a0
+};
+
+ /* IBM Thai */
+static const unsigned short ibm_874tab[128] = {
+ 0x20ac,UBOGON,UBOGON,UBOGON,UBOGON,0x2026,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x2018,0x2019,0x201c,0x201d,0x2022,0x2013,0x2014,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x00a0,0x0e01,0x0e02,0x0e03,0x0e04,0x0e05,0x0e06,0x0e07,
+ 0x0e08,0x0e09,0x0e0a,0x0e0b,0x0e0c,0x0e0d,0x0e0e,0x0e0f,
+ 0x0e10,0x0e11,0x0e12,0x0e13,0x0e14,0x0e15,0x0e16,0x0e17,
+ 0x0e18,0x0e19,0x0e1a,0x0e1b,0x0e1c,0x0e1d,0x0e1e,0x0e1f,
+ 0x0e20,0x0e21,0x0e22,0x0e23,0x0e24,0x0e25,0x0e26,0x0e27,
+ 0x0e28,0x0e29,0x0e2a,0x0e2b,0x0e2c,0x0e2d,0x0e2e,0x0e2f,
+ 0x0e30,0x0e31,0x0e32,0x0e33,0x0e34,0x0e35,0x0e36,0x0e37,
+ 0x0e38,0x0e39,0x0e3a,UBOGON,UBOGON,UBOGON,UBOGON,0x0e3f,
+ 0x0e40,0x0e41,0x0e42,0x0e43,0x0e44,0x0e45,0x0e46,0x0e47,
+ 0x0e48,0x0e49,0x0e4a,0x0e4b,0x0e4c,0x0e4d,0x0e4e,0x0e4f,
+ 0x0e50,0x0e51,0x0e52,0x0e53,0x0e54,0x0e55,0x0e56,0x0e57,
+ 0x0e58,0x0e59,0x0e5a,0x0e5b,UBOGON,UBOGON,UBOGON,UBOGON
+};
diff --git a/imap/src/charset/iso_8859.c b/imap/src/charset/iso_8859.c
new file mode 100644
index 00000000..484c3ecc
--- /dev/null
+++ b/imap/src/charset/iso_8859.c
@@ -0,0 +1,308 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: ISO-8859 conversion tables
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 July 1997
+ * Last Edited: 30 August 2006
+ */
+
+/* ISO-8859 conversion tables */
+
+ /* Latin-2 (East European) */
+static const unsigned short iso8859_2tab[128] = {
+ 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,
+ 0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f,
+ 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,
+ 0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f,
+ 0x00a0,0x0104,0x02d8,0x0141,0x00a4,0x013d,0x015a,0x00a7,
+ 0x00a8,0x0160,0x015e,0x0164,0x0179,0x00ad,0x017d,0x017b,
+ 0x00b0,0x0105,0x02db,0x0142,0x00b4,0x013e,0x015b,0x02c7,
+ 0x00b8,0x0161,0x015f,0x0165,0x017a,0x02dd,0x017e,0x017c,
+ 0x0154,0x00c1,0x00c2,0x0102,0x00c4,0x0139,0x0106,0x00c7,
+ 0x010c,0x00c9,0x0118,0x00cb,0x011a,0x00cd,0x00ce,0x010e,
+ 0x0110,0x0143,0x0147,0x00d3,0x00d4,0x0150,0x00d6,0x00d7,
+ 0x0158,0x016e,0x00da,0x0170,0x00dc,0x00dd,0x0162,0x00df,
+ 0x0155,0x00e1,0x00e2,0x0103,0x00e4,0x013a,0x0107,0x00e7,
+ 0x010d,0x00e9,0x0119,0x00eb,0x011b,0x00ed,0x00ee,0x010f,
+ 0x0111,0x0144,0x0148,0x00f3,0x00f4,0x0151,0x00f6,0x00f7,
+ 0x0159,0x016f,0x00fa,0x0171,0x00fc,0x00fd,0x0163,0x02d9
+};
+
+
+ /* Latin-3 (South European) */
+static const unsigned short iso8859_3tab[128] = {
+ 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,
+ 0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f,
+ 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,
+ 0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f,
+ 0x00a0,0x0126,0x02d8,0x00a3,0x00a4,UBOGON,0x0124,0x00a7,
+ 0x00a8,0x0130,0x015e,0x011e,0x0134,0x00ad,UBOGON,0x017b,
+ 0x00b0,0x0127,0x00b2,0x00b3,0x00b4,0x00b5,0x0125,0x00b7,
+ 0x00b8,0x0131,0x015f,0x011f,0x0135,0x00bd,UBOGON,0x017c,
+ 0x00c0,0x00c1,0x00c2,UBOGON,0x00c4,0x010a,0x0108,0x00c7,
+ 0x00c8,0x00c9,0x00ca,0x00cb,0x00cc,0x00cd,0x00ce,0x00cf,
+ UBOGON,0x00d1,0x00d2,0x00d3,0x00d4,0x0120,0x00d6,0x00d7,
+ 0x011c,0x00d9,0x00da,0x00db,0x00dc,0x016c,0x015c,0x00df,
+ 0x00e0,0x00e1,0x00e2,UBOGON,0x00e4,0x010b,0x0109,0x00e7,
+ 0x00e8,0x00e9,0x00ea,0x00eb,0x00ec,0x00ed,0x00ee,0x00ef,
+ UBOGON,0x00f1,0x00f2,0x00f3,0x00f4,0x0121,0x00f6,0x00f7,
+ 0x011d,0x00f9,0x00fa,0x00fb,0x00fc,0x016d,0x015d,0x02d9
+};
+
+
+ /* Latin-4 (North European) */
+static const unsigned short iso8859_4tab[128] = {
+ 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,
+ 0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f,
+ 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,
+ 0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f,
+ 0x00a0,0x0104,0x0138,0x0156,0x00a4,0x0128,0x013b,0x00a7,
+ 0x00a8,0x0160,0x0112,0x0122,0x0166,0x00ad,0x017d,0x00af,
+ 0x00b0,0x0105,0x02db,0x0157,0x00b4,0x0129,0x013c,0x02c7,
+ 0x00b8,0x0161,0x0113,0x0123,0x0167,0x014a,0x017e,0x014b,
+ 0x0100,0x00c1,0x00c2,0x00c3,0x00c4,0x00c5,0x00c6,0x012e,
+ 0x010c,0x00c9,0x0118,0x00cb,0x0116,0x00cd,0x00ce,0x012a,
+ 0x0110,0x0145,0x014c,0x0136,0x00d4,0x00d5,0x00d6,0x00d7,
+ 0x00d8,0x0172,0x00da,0x00db,0x00dc,0x0168,0x016a,0x00df,
+ 0x0101,0x00e1,0x00e2,0x00e3,0x00e4,0x00e5,0x00e6,0x012f,
+ 0x010d,0x00e9,0x0119,0x00eb,0x0117,0x00ed,0x00ee,0x012b,
+ 0x0111,0x0146,0x014d,0x0137,0x00f4,0x00f5,0x00f6,0x00f7,
+ 0x00f8,0x0173,0x00fa,0x00fb,0x00fc,0x0169,0x016b,0x02d9
+};
+
+
+ /* Cyrillic */
+static const unsigned short iso8859_5tab[128] = {
+ 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,
+ 0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f,
+ 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,
+ 0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f,
+ 0x00a0,0x0401,0x0402,0x0403,0x0404,0x0405,0x0406,0x0407,
+ 0x0408,0x0409,0x040a,0x040b,0x040c,0x00ad,0x040e,0x040f,
+ 0x0410,0x0411,0x0412,0x0413,0x0414,0x0415,0x0416,0x0417,
+ 0x0418,0x0419,0x041a,0x041b,0x041c,0x041d,0x041e,0x041f,
+ 0x0420,0x0421,0x0422,0x0423,0x0424,0x0425,0x0426,0x0427,
+ 0x0428,0x0429,0x042a,0x042b,0x042c,0x042d,0x042e,0x042f,
+ 0x0430,0x0431,0x0432,0x0433,0x0434,0x0435,0x0436,0x0437,
+ 0x0438,0x0439,0x043a,0x043b,0x043c,0x043d,0x043e,0x043f,
+ 0x0440,0x0441,0x0442,0x0443,0x0444,0x0445,0x0446,0x0447,
+ 0x0448,0x0449,0x044a,0x044b,0x044c,0x044d,0x044e,0x044f,
+ 0x2116,0x0451,0x0452,0x0453,0x0454,0x0455,0x0456,0x0457,
+ 0x0458,0x0459,0x045a,0x045b,0x045c,0x00a7,0x045e,0x045f
+};
+
+
+ /* Arabic */
+static const unsigned short iso8859_6tab[128] = {
+ 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,
+ 0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f,
+ 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,
+ 0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f,
+ 0x00a0,UBOGON,UBOGON,UBOGON,0x00a4,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x060c,0x00ad,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x061b,UBOGON,UBOGON,UBOGON,0x061f,
+ UBOGON,0x0621,0x0622,0x0623,0x0624,0x0625,0x0626,0x0627,
+ 0x0628,0x0629,0x062a,0x062b,0x062c,0x062d,0x062e,0x062f,
+ 0x0630,0x0631,0x0632,0x0633,0x0634,0x0635,0x0636,0x0637,
+ 0x0638,0x0639,0x063a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x0640,0x0641,0x0642,0x0643,0x0644,0x0645,0x0646,0x0647,
+ 0x0648,0x0649,0x064a,0x064b,0x064c,0x064d,0x064e,0x064f,
+ 0x0650,0x0651,0x0652,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON
+};
+
+
+ /* Greek */
+static const unsigned short iso8859_7tab[128] = {
+ 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,
+ 0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f,
+ 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,
+ 0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f,
+ 0x00a0,0x2018,0x2019,0x00a3,UBOGON,UBOGON,0x00a6,0x00a7,
+ 0x00a8,0x00a9,UBOGON,0x00ab,0x00ac,0x00ad,UBOGON,0x2015,
+ 0x00b0,0x00b1,0x00b2,0x00b3,0x0384,0x0385,0x0386,0x00b7,
+ 0x0388,0x0389,0x038a,0x00bb,0x038c,0x00bd,0x038e,0x038f,
+ 0x0390,0x0391,0x0392,0x0393,0x0394,0x0395,0x0396,0x0397,
+ 0x0398,0x0399,0x039a,0x039b,0x039c,0x039d,0x039e,0x039f,
+ 0x03a0,0x03a1,UBOGON,0x03a3,0x03a4,0x03a5,0x03a6,0x03a7,
+ 0x03a8,0x03a9,0x03aa,0x03ab,0x03ac,0x03ad,0x03ae,0x03af,
+ 0x03b0,0x03b1,0x03b2,0x03b3,0x03b4,0x03b5,0x03b6,0x03b7,
+ 0x03b8,0x03b9,0x03ba,0x03bb,0x03bc,0x03bd,0x03be,0x03bf,
+ 0x03c0,0x03c1,0x03c2,0x03c3,0x03c4,0x03c5,0x03c6,0x03c7,
+ 0x03c8,0x03c9,0x03ca,0x03cb,0x03cc,0x03cd,0x03ce,UBOGON
+};
+
+
+ /* Hebrew */
+static const unsigned short iso8859_8tab[128] = {
+ 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,
+ 0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f,
+ 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,
+ 0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f,
+ 0x00a0,UBOGON,0x00a2,0x00a3,0x00a4,0x00a5,0x00a6,0x00a7,
+ 0x00a8,0x00a9,0x00d7,0x00ab,0x00ac,0x00ad,0x00ae,0x00af,
+ 0x00b0,0x00b1,0x00b2,0x00b3,0x00b4,0x00b5,0x00b6,0x00b7,
+ 0x00b8,0x00b9,0x00f7,0x00bb,0x00bc,0x00bd,0x00be,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x2017,
+ 0x05d0,0x05d1,0x05d2,0x05d3,0x05d4,0x05d5,0x05d6,0x05d7,
+ 0x05d8,0x05d9,0x05da,0x05db,0x05dc,0x05dd,0x05de,0x05df,
+ 0x05e0,0x05e1,0x05e2,0x05e3,0x05e4,0x05e5,0x05e6,0x05e7,
+ 0x05e8,0x05e9,0x05ea,UBOGON,UBOGON,0x200e,0x200f,UBOGON
+};
+
+
+ /* Latin-5 (Turkish) */
+static const unsigned short iso8859_9tab[128] = {
+ 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,
+ 0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f,
+ 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,
+ 0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f,
+ 0x00a0,0x00a1,0x00a2,0x00a3,0x00a4,0x00a5,0x00a6,0x00a7,
+ 0x00a8,0x00a9,0x00aa,0x00ab,0x00ac,0x00ad,0x00ae,0x00af,
+ 0x00b0,0x00b1,0x00b2,0x00b3,0x00b4,0x00b5,0x00b6,0x00b7,
+ 0x00b8,0x00b9,0x00ba,0x00bb,0x00bc,0x00bd,0x00be,0x00bf,
+ 0x00c0,0x00c1,0x00c2,0x00c3,0x00c4,0x00c5,0x00c6,0x00c7,
+ 0x00c8,0x00c9,0x00ca,0x00cb,0x00cc,0x00cd,0x00ce,0x00cf,
+ 0x011e,0x00d1,0x00d2,0x00d3,0x00d4,0x00d5,0x00d6,0x00d7,
+ 0x00d8,0x00d9,0x00da,0x00db,0x00dc,0x0130,0x015e,0x00df,
+ 0x00e0,0x00e1,0x00e2,0x00e3,0x00e4,0x00e5,0x00e6,0x00e7,
+ 0x00e8,0x00e9,0x00ea,0x00eb,0x00ec,0x00ed,0x00ee,0x00ef,
+ 0x011f,0x00f1,0x00f2,0x00f3,0x00f4,0x00f5,0x00f6,0x00f7,
+ 0x00f8,0x00f9,0x00fa,0x00fb,0x00fc,0x0131,0x015f,0x00ff
+};
+
+
+ /* Latin-6 (Nordic) */
+static const unsigned short iso8859_10tab[128] = {
+ 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,
+ 0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f,
+ 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,
+ 0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f,
+ 0x00a0,0x0104,0x0112,0x0122,0x012a,0x0128,0x0136,0x00a7,
+ 0x013b,0x0110,0x0160,0x0166,0x017d,0x00ad,0x016a,0x014a,
+ 0x00b0,0x0105,0x0113,0x0123,0x012b,0x0129,0x0137,0x00b7,
+ 0x013c,0x0111,0x0161,0x0167,0x017e,0x2015,0x016b,0x014b,
+ 0x0100,0x00c1,0x00c2,0x00c3,0x00c4,0x00c5,0x00c6,0x012e,
+ 0x010c,0x00c9,0x0118,0x00cb,0x0116,0x00cd,0x00ce,0x00cf,
+ 0x00d0,0x0145,0x014c,0x00d3,0x00d4,0x00d5,0x00d6,0x0168,
+ 0x00d8,0x0172,0x00da,0x00db,0x00dc,0x00dd,0x00de,0x00df,
+ 0x0101,0x00e1,0x00e2,0x00e3,0x00e4,0x00e5,0x00e6,0x012f,
+ 0x010d,0x00e9,0x0119,0x00eb,0x0117,0x00ed,0x00ee,0x00ef,
+ 0x00f0,0x0146,0x014d,0x00f3,0x00f4,0x00f5,0x00f6,0x0169,
+ 0x00f8,0x0173,0x00fa,0x00fb,0x00fc,0x00fd,0x00fe,0x0138
+};
+
+
+ /* Thai */
+#define iso8859_11tab tis620tab
+
+ /* reserved for ISCII Indian */
+
+ /* Latin-7 (Baltic) */
+static const unsigned short iso8859_13tab[128] = {
+ 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,
+ 0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f,
+ 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,
+ 0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f,
+ 0x00a0,0x201d,0x00a2,0x00a3,0x00a4,0x201e,0x00a6,0x00a7,
+ 0x00d8,0x00a9,0x0156,0x00ab,0x00ac,0x00ad,0x00ae,0x00c6,
+ 0x00b0,0x00b1,0x00b2,0x00b3,0x201c,0x00b5,0x00b6,0x00b7,
+ 0x00f8,0x00b9,0x0157,0x00bb,0x00bc,0x00bd,0x00be,0x00e6,
+ 0x0104,0x012e,0x0100,0x0106,0x00c4,0x00c5,0x0118,0x0112,
+ 0x010c,0x00c9,0x0179,0x0116,0x0122,0x0136,0x012a,0x013b,
+ 0x0160,0x0143,0x0145,0x00d3,0x014c,0x00d5,0x00d6,0x00d7,
+ 0x0172,0x0141,0x015a,0x016a,0x00dc,0x017b,0x017d,0x00df,
+ 0x0105,0x012f,0x0101,0x0107,0x00e4,0x00e5,0x0119,0x0113,
+ 0x010d,0x00e9,0x017a,0x0117,0x0123,0x0137,0x012b,0x013c,
+ 0x0161,0x0144,0x0146,0x00f3,0x014d,0x00f5,0x00f6,0x00f7,
+ 0x0173,0x0142,0x015b,0x016b,0x00fc,0x017c,0x017e,0x2019
+};
+
+
+ /* Latin-8 (Celtic) */
+
+static const unsigned short iso8859_14tab[128] = {
+ 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,
+ 0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f,
+ 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,
+ 0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f,
+ 0x00a0,0x1e02,0x1e03,0x00a3,0x010a,0x010b,0x1e0a,0x00a7,
+ 0x1e80,0x00a9,0x1e82,0x1e0b,0x1ef2,0x00ad,0x00ae,0x0178,
+ 0x1e1e,0x1e1f,0x0120,0x0121,0x1e40,0x1e41,0x00b6,0x1e56,
+ 0x1e81,0x1e57,0x1e83,0x1e60,0x1ef3,0x1e84,0x1e85,0x1e61,
+ 0x00c0,0x00c1,0x00c2,0x00c3,0x00c4,0x00c5,0x00c6,0x00c7,
+ 0x00c8,0x00c9,0x00ca,0x00cb,0x00cc,0x00cd,0x00ce,0x00cf,
+ 0x0174,0x00d1,0x00d2,0x00d3,0x00d4,0x00d5,0x00d6,0x1e6a,
+ 0x00d8,0x00d9,0x00da,0x00db,0x00dc,0x00dd,0x0176,0x00df,
+ 0x00e0,0x00e1,0x00e2,0x00e3,0x00e4,0x00e5,0x00e6,0x00e7,
+ 0x00e8,0x00e9,0x00ea,0x00eb,0x00ec,0x00ed,0x00ee,0x00ef,
+ 0x0175,0x00f1,0x00f2,0x00f3,0x00f4,0x00f5,0x00f6,0x1e6b,
+ 0x00f8,0x00f9,0x00fa,0x00fb,0x00fc,0x00fd,0x0177,0x00ff
+};
+
+
+ /* Latin-9 a.k.a. Latin-0 (Euro) */
+static const unsigned short iso8859_15tab[128] = {
+ 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,
+ 0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f,
+ 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,
+ 0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f,
+ 0x00a0,0x00a1,0x00a2,0x00a3,0x20ac,0x00a5,0x0160,0x00a7,
+ 0x0161,0x00a9,0x00aa,0x00ab,0x00ac,0x00ad,0x00ae,0x00af,
+ 0x00b0,0x00b1,0x00b2,0x00b3,0x017d,0x00b5,0x00b6,0x00b7,
+ 0x017e,0x00b9,0x00ba,0x00bb,0x0152,0x0153,0x0178,0x00bf,
+ 0x00c0,0x00c1,0x00c2,0x00c3,0x00c4,0x00c5,0x00c6,0x00c7,
+ 0x00c8,0x00c9,0x00ca,0x00cb,0x00cc,0x00cd,0x00ce,0x00cf,
+ 0x00d0,0x00d1,0x00d2,0x00d3,0x00d4,0x00d5,0x00d6,0x00d7,
+ 0x00d8,0x00d9,0x00da,0x00db,0x00dc,0x00dd,0x00de,0x00df,
+ 0x00e0,0x00e1,0x00e2,0x00e3,0x00e4,0x00e5,0x00e6,0x00e7,
+ 0x00e8,0x00e9,0x00ea,0x00eb,0x00ec,0x00ed,0x00ee,0x00ef,
+ 0x00f0,0x00f1,0x00f2,0x00f3,0x00f4,0x00f5,0x00f6,0x00f7,
+ 0x00f8,0x00f9,0x00fa,0x00fb,0x00fc,0x00fd,0x00fe,0x00ff
+};
+
+
+ /* Latin-10 (Balkan) */
+
+static const unsigned short iso8859_16tab[128] = {
+ 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,
+ 0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f,
+ 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,
+ 0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f,
+ 0x00a0,0x0104,0x0105,0x0141,0x20ac,0x201e,0x0160,0x00a7,
+ 0x0161,0x00a9,0x0218,0x00ab,0x0179,0x00ad,0x017a,0x017b,
+ 0x00b0,0x00b1,0x010c,0x0142,0x017d,0x201d,0x00b6,0x00b7,
+ 0x017e,0x010d,0x0219,0x00bb,0x0152,0x0153,0x0178,0x017c,
+ 0x00c0,0x00c1,0x00c2,0x0102,0x00c4,0x0106,0x00c6,0x00c7,
+ 0x00c8,0x00c9,0x00ca,0x00cb,0x00cc,0x00cd,0x00ce,0x00cf,
+ 0x0110,0x0143,0x00d2,0x00d3,0x00d4,0x0150,0x00d6,0x015a,
+ 0x0170,0x00d9,0x00da,0x00db,0x00dc,0x0118,0x021a,0x00df,
+ 0x00e0,0x00e1,0x00e2,0x0103,0x00e4,0x0107,0x00e6,0x00e7,
+ 0x00e8,0x00e9,0x00ea,0x00eb,0x00ec,0x00ed,0x00ee,0x00ef,
+ 0x0111,0x0144,0x00f2,0x00f3,0x00f4,0x0151,0x00f6,0x015b,
+ 0x0171,0x00f9,0x00fa,0x00fb,0x00fc,0x0119,0x021b,0x00ff
+};
diff --git a/imap/src/charset/jis_0208.c b/imap/src/charset/jis_0208.c
new file mode 100644
index 00000000..cdb8f4f6
--- /dev/null
+++ b/imap/src/charset/jis_0208.c
@@ -0,0 +1,1092 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: JIS X0208 conversion table
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 July 1997
+ * Last Edited: 30 August 2006
+ */
+
+/* JIS X0208 is the industrial standard of Japan. */
+
+#define BASE_JIS0208_KU 0x21
+#define BASE_JIS0208_TEN 0x21
+#define MAX_JIS0208_KU 84
+#define MAX_JIS0208_TEN 94
+
+
+#define SJISTOJIS(c,c1) \
+ c = ((c - ((c < 0xa0) ? 0x70 : 0xb0)) << 1); \
+ if (c1 < 0x9f) { \
+ c--; \
+ c1 -= 0x1f + (c1 > 0x7f); \
+ } \
+ else c1 -= 0x7e;
+
+
+#define JISTOUNICODE(c,c1,ku,ten) \
+ ((((ku = (c & 0x7f) - BASE_JIS0208_KU) < MAX_JIS0208_KU) && \
+ ((ten = (c1 & 0x7f) - BASE_JIS0208_TEN) < MAX_JIS0208_TEN)) ? \
+ jis0208tab[ku][ten] : UBOGON)
+
+
+static const unsigned short jis0208tab[MAX_JIS0208_KU][MAX_JIS0208_TEN] = {
+ { /* ku 01 */
+ 0x3000,0x3001,0x3002,0xff0c,0xff0e,0x30fb,0xff1a,0xff1b,0xff1f,0xff01,
+ 0x309b,0x309c,0x00b4,0xff40,0x00a8,0xff3e,0xffe3,0xff3f,0x30fd,0x30fe,
+ 0x309d,0x309e,0x3003,0x4edd,0x3005,0x3006,0x3007,0x30fc,0x2015,0x2010,
+ /* Fullwidth/halfwidth correction:
+ * JIS0208.TXT shows 01/32 as U+005C instead of U+FF3C.
+ *
+ * AOL suggests that 01/33 should be U+FF5E instead of U+301C and
+ * 01/34 should be U+2225 instead of U+2016.
+ * I disagree; 01/33 is JIS punctuation (not a tilde); and 01/34 is
+ * double vertical line.
+ */
+ 0xff0f,0xff3c,0x301c,0x2016,0xff5c,0x2026,0x2025,0x2018,0x2019,0x201c,
+ 0x201d,0xff08,0xff09,0x3014,0x3015,0xff3b,0xff3d,0xff5b,0xff5d,0x3008,
+ 0x3009,0x300a,0x300b,0x300c,0x300d,0x300e,0x300f,0x3010,0x3011,0xff0b,
+ /* Fullwidth/halfwidth correction:
+ * JIS0208.TXT 01/61 has U+2212 instead of U+FF0D.
+ */
+ 0xff0d,0x00b1,0x00d7,0x00f7,0xff1d,0x2260,0xff1c,0xff1e,0x2266,0x2267,
+ 0x221e,0x2234,0x2642,0x2640,0x00b0,0x2032,0x2033,0x2103,0xffe5,0xff04,
+ /* Fullwidth/halfwidth correction:
+ * JIS 0208 shows 01/81 as U+00A2 instead of U+FFE0, and
+ * 01/82 as U+00A3 instead of U+FFE1.
+ */
+ 0xffe0,0xffe1,0xff05,0xff03,0xff06,0xff0a,0xff20,0x00a7,0x2606,0x2605,
+ 0x25cb,0x25cf,0x25ce,0x25c7
+ },
+ { /* ku 02 */
+ 0x25c6,0x25a1,0x25a0,0x25b3,0x25b2,0x25bd,0x25bc,0x203b,0x3012,0x2192,
+ 0x2190,0x2191,0x2193,0x3013,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x2208,0x220b,0x2286,0x2287,0x2282,
+ 0x2283,0x222a,0x2229,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ /* Fullwidth/halfwidth correction:
+ * JIS0208.TXT shows 02/44 as U+00AC instead of U+FFE2.
+ */
+ UBOGON,0x2227,0x2228,0xffe2,0x21d2,0x21d4,0x2200,0x2203,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x2220,
+ 0x22a5,0x2312,0x2202,0x2207,0x2261,0x2252,0x226a,0x226b,0x221a,0x223d,
+ 0x221d,0x2235,0x222b,0x222c,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x212b,0x2030,0x266f,0x266d,0x266a,0x2020,0x2021,0x00b6,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x25ef
+ },
+ { /* ku 03 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0xff10,0xff11,0xff12,0xff13,0xff14,
+ 0xff15,0xff16,0xff17,0xff18,0xff19,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xff21,0xff22,0xff23,0xff24,0xff25,0xff26,0xff27,0xff28,
+ 0xff29,0xff2a,0xff2b,0xff2c,0xff2d,0xff2e,0xff2f,0xff30,0xff31,0xff32,
+ 0xff33,0xff34,0xff35,0xff36,0xff37,0xff38,0xff39,0xff3a,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xff41,0xff42,0xff43,0xff44,0xff45,0xff46,
+ 0xff47,0xff48,0xff49,0xff4a,0xff4b,0xff4c,0xff4d,0xff4e,0xff4f,0xff50,
+ 0xff51,0xff52,0xff53,0xff54,0xff55,0xff56,0xff57,0xff58,0xff59,0xff5a,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 04 */
+ 0x3041,0x3042,0x3043,0x3044,0x3045,0x3046,0x3047,0x3048,0x3049,0x304a,
+ 0x304b,0x304c,0x304d,0x304e,0x304f,0x3050,0x3051,0x3052,0x3053,0x3054,
+ 0x3055,0x3056,0x3057,0x3058,0x3059,0x305a,0x305b,0x305c,0x305d,0x305e,
+ 0x305f,0x3060,0x3061,0x3062,0x3063,0x3064,0x3065,0x3066,0x3067,0x3068,
+ 0x3069,0x306a,0x306b,0x306c,0x306d,0x306e,0x306f,0x3070,0x3071,0x3072,
+ 0x3073,0x3074,0x3075,0x3076,0x3077,0x3078,0x3079,0x307a,0x307b,0x307c,
+ 0x307d,0x307e,0x307f,0x3080,0x3081,0x3082,0x3083,0x3084,0x3085,0x3086,
+ 0x3087,0x3088,0x3089,0x308a,0x308b,0x308c,0x308d,0x308e,0x308f,0x3090,
+ 0x3091,0x3092,0x3093,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 05 */
+ 0x30a1,0x30a2,0x30a3,0x30a4,0x30a5,0x30a6,0x30a7,0x30a8,0x30a9,0x30aa,
+ 0x30ab,0x30ac,0x30ad,0x30ae,0x30af,0x30b0,0x30b1,0x30b2,0x30b3,0x30b4,
+ 0x30b5,0x30b6,0x30b7,0x30b8,0x30b9,0x30ba,0x30bb,0x30bc,0x30bd,0x30be,
+ 0x30bf,0x30c0,0x30c1,0x30c2,0x30c3,0x30c4,0x30c5,0x30c6,0x30c7,0x30c8,
+ 0x30c9,0x30ca,0x30cb,0x30cc,0x30cd,0x30ce,0x30cf,0x30d0,0x30d1,0x30d2,
+ 0x30d3,0x30d4,0x30d5,0x30d6,0x30d7,0x30d8,0x30d9,0x30da,0x30db,0x30dc,
+ 0x30dd,0x30de,0x30df,0x30e0,0x30e1,0x30e2,0x30e3,0x30e4,0x30e5,0x30e6,
+ 0x30e7,0x30e8,0x30e9,0x30ea,0x30eb,0x30ec,0x30ed,0x30ee,0x30ef,0x30f0,
+ 0x30f1,0x30f2,0x30f3,0x30f4,0x30f5,0x30f6,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 06 */
+ 0x0391,0x0392,0x0393,0x0394,0x0395,0x0396,0x0397,0x0398,0x0399,0x039a,
+ 0x039b,0x039c,0x039d,0x039e,0x039f,0x03a0,0x03a1,0x03a3,0x03a4,0x03a5,
+ 0x03a6,0x03a7,0x03a8,0x03a9,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x03b1,0x03b2,0x03b3,0x03b4,0x03b5,0x03b6,0x03b7,0x03b8,
+ 0x03b9,0x03ba,0x03bb,0x03bc,0x03bd,0x03be,0x03bf,0x03c0,0x03c1,0x03c3,
+ 0x03c4,0x03c5,0x03c6,0x03c7,0x03c8,0x03c9,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 07 */
+ 0x0410,0x0411,0x0412,0x0413,0x0414,0x0415,0x0401,0x0416,0x0417,0x0418,
+ 0x0419,0x041a,0x041b,0x041c,0x041d,0x041e,0x041f,0x0420,0x0421,0x0422,
+ 0x0423,0x0424,0x0425,0x0426,0x0427,0x0428,0x0429,0x042a,0x042b,0x042c,
+ 0x042d,0x042e,0x042f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x0430,0x0431,
+ 0x0432,0x0433,0x0434,0x0435,0x0451,0x0436,0x0437,0x0438,0x0439,0x043a,
+ 0x043b,0x043c,0x043d,0x043e,0x043f,0x0440,0x0441,0x0442,0x0443,0x0444,
+ 0x0445,0x0446,0x0447,0x0448,0x0449,0x044a,0x044b,0x044c,0x044d,0x044e,
+ 0x044f,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 08 */
+ 0x2500,0x2502,0x250c,0x2510,0x2518,0x2514,0x251c,0x252c,0x2524,0x2534,
+ 0x253c,0x2501,0x2503,0x250f,0x2513,0x251b,0x2517,0x2523,0x2533,0x252b,
+ 0x253b,0x254b,0x2520,0x252f,0x2528,0x2537,0x253f,0x251d,0x2530,0x2525,
+ 0x2538,0x2542,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 09 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0d */
+#if 0
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+#else /* additional mappings suggested by AOL */
+ 0x2460,0x2461,0x2462,0x2463,0x2464,0x2465,0x2466,0x2467,0x2468,0x2469,
+ 0x246a,0x246b,0x246c,0x246d,0x246e,0x246f,0x2470,0x2471,0x2472,0x2473,
+ 0x2160,0x2161,0x2162,0x2163,0x2164,0x2165,0x2166,0x2167,0x2168,0x2169,
+ UBOGON,0x3349,0x3314,0x3322,0x334d,0x3318,0x3327,0x3303,0x3336,0x3351,
+ 0x3357,0x330d,0x3326,0x3323,0x332b,0x334a,0x333b,0x339c,0x339d,0x339e,
+ 0x338e,0x338f,0x33c4,0x33a1,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x337b,0x301d,0x301f,0x2116,0x33cd,0x2121,0x32a4,0x32a5,
+ 0x32a6,0x32a7,0x32a8,0x3231,0x3232,0x3239,0x337e,0x337d,0x337c,UBOGON,
+ UBOGON,UBOGON,0x222e,0x2211,UBOGON,UBOGON,UBOGON,0x221f,0x22bf,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+#endif
+ },
+ { /* ku 0e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 10 */
+ 0x4e9c,0x5516,0x5a03,0x963f,0x54c0,0x611b,0x6328,0x59f6,0x9022,0x8475,
+ 0x831c,0x7a50,0x60aa,0x63e1,0x6e25,0x65ed,0x8466,0x82a6,0x9bf5,0x6893,
+ 0x5727,0x65a1,0x6271,0x5b9b,0x59d0,0x867b,0x98f4,0x7d62,0x7dbe,0x9b8e,
+ 0x6216,0x7c9f,0x88b7,0x5b89,0x5eb5,0x6309,0x6697,0x6848,0x95c7,0x978d,
+ 0x674f,0x4ee5,0x4f0a,0x4f4d,0x4f9d,0x5049,0x56f2,0x5937,0x59d4,0x5a01,
+ 0x5c09,0x60df,0x610f,0x6170,0x6613,0x6905,0x70ba,0x754f,0x7570,0x79fb,
+ 0x7dad,0x7def,0x80c3,0x840e,0x8863,0x8b02,0x9055,0x907a,0x533b,0x4e95,
+ 0x4ea5,0x57df,0x80b2,0x90c1,0x78ef,0x4e00,0x58f1,0x6ea2,0x9038,0x7a32,
+ 0x8328,0x828b,0x9c2f,0x5141,0x5370,0x54bd,0x54e1,0x56e0,0x59fb,0x5f15,
+ 0x98f2,0x6deb,0x80e4,0x852d
+ },
+ { /* ku 11 */
+ 0x9662,0x9670,0x96a0,0x97fb,0x540b,0x53f3,0x5b87,0x70cf,0x7fbd,0x8fc2,
+ 0x96e8,0x536f,0x9d5c,0x7aba,0x4e11,0x7893,0x81fc,0x6e26,0x5618,0x5504,
+ 0x6b1d,0x851a,0x9c3b,0x59e5,0x53a9,0x6d66,0x74dc,0x958f,0x5642,0x4e91,
+ 0x904b,0x96f2,0x834f,0x990c,0x53e1,0x55b6,0x5b30,0x5f71,0x6620,0x66f3,
+ 0x6804,0x6c38,0x6cf3,0x6d29,0x745b,0x76c8,0x7a4e,0x9834,0x82f1,0x885b,
+ 0x8a60,0x92ed,0x6db2,0x75ab,0x76ca,0x99c5,0x60a6,0x8b01,0x8d8a,0x95b2,
+ 0x698e,0x53ad,0x5186,0x5712,0x5830,0x5944,0x5bb4,0x5ef6,0x6028,0x63a9,
+ 0x63f4,0x6cbf,0x6f14,0x708e,0x7114,0x7159,0x71d5,0x733f,0x7e01,0x8276,
+ 0x82d1,0x8597,0x9060,0x925b,0x9d1b,0x5869,0x65bc,0x6c5a,0x7525,0x51f9,
+ 0x592e,0x5965,0x5f80,0x5fdc
+ },
+ { /* ku 12 */
+ 0x62bc,0x65fa,0x6a2a,0x6b27,0x6bb4,0x738b,0x7fc1,0x8956,0x9d2c,0x9d0e,
+ 0x9ec4,0x5ca1,0x6c96,0x837b,0x5104,0x5c4b,0x61b6,0x81c6,0x6876,0x7261,
+ 0x4e59,0x4ffa,0x5378,0x6069,0x6e29,0x7a4f,0x97f3,0x4e0b,0x5316,0x4eee,
+ 0x4f55,0x4f3d,0x4fa1,0x4f73,0x52a0,0x53ef,0x5609,0x590f,0x5ac1,0x5bb6,
+ 0x5be1,0x79d1,0x6687,0x679c,0x67b6,0x6b4c,0x6cb3,0x706b,0x73c2,0x798d,
+ 0x79be,0x7a3c,0x7b87,0x82b1,0x82db,0x8304,0x8377,0x83ef,0x83d3,0x8766,
+ 0x8ab2,0x5629,0x8ca8,0x8fe6,0x904e,0x971e,0x868a,0x4fc4,0x5ce8,0x6211,
+ 0x7259,0x753b,0x81e5,0x82bd,0x86fe,0x8cc0,0x96c5,0x9913,0x99d5,0x4ecb,
+ 0x4f1a,0x89e3,0x56de,0x584a,0x58ca,0x5efb,0x5feb,0x602a,0x6094,0x6062,
+ 0x61d0,0x6212,0x62d0,0x6539
+ },
+ { /* ku 13 */
+ 0x9b41,0x6666,0x68b0,0x6d77,0x7070,0x754c,0x7686,0x7d75,0x82a5,0x87f9,
+ 0x958b,0x968e,0x8c9d,0x51f1,0x52be,0x5916,0x54b3,0x5bb3,0x5d16,0x6168,
+ 0x6982,0x6daf,0x788d,0x84cb,0x8857,0x8a72,0x93a7,0x9ab8,0x6d6c,0x99a8,
+ 0x86d9,0x57a3,0x67ff,0x86ce,0x920e,0x5283,0x5687,0x5404,0x5ed3,0x62e1,
+ 0x64b9,0x683c,0x6838,0x6bbb,0x7372,0x78ba,0x7a6b,0x899a,0x89d2,0x8d6b,
+ 0x8f03,0x90ed,0x95a3,0x9694,0x9769,0x5b66,0x5cb3,0x697d,0x984d,0x984e,
+ 0x639b,0x7b20,0x6a2b,0x6a7f,0x68b6,0x9c0d,0x6f5f,0x5272,0x559d,0x6070,
+ 0x62ec,0x6d3b,0x6e07,0x6ed1,0x845b,0x8910,0x8f44,0x4e14,0x9c39,0x53f6,
+ 0x691b,0x6a3a,0x9784,0x682a,0x515c,0x7ac3,0x84b2,0x91dc,0x938c,0x565b,
+ 0x9d28,0x6822,0x8305,0x8431
+ },
+ { /* ku 14 */
+ 0x7ca5,0x5208,0x82c5,0x74e6,0x4e7e,0x4f83,0x51a0,0x5bd2,0x520a,0x52d8,
+ 0x52e7,0x5dfb,0x559a,0x582a,0x59e6,0x5b8c,0x5b98,0x5bdb,0x5e72,0x5e79,
+ 0x60a3,0x611f,0x6163,0x61be,0x63db,0x6562,0x67d1,0x6853,0x68fa,0x6b3e,
+ 0x6b53,0x6c57,0x6f22,0x6f97,0x6f45,0x74b0,0x7518,0x76e3,0x770b,0x7aff,
+ 0x7ba1,0x7c21,0x7de9,0x7f36,0x7ff0,0x809d,0x8266,0x839e,0x89b3,0x8acc,
+ 0x8cab,0x9084,0x9451,0x9593,0x9591,0x95a2,0x9665,0x97d3,0x9928,0x8218,
+ 0x4e38,0x542b,0x5cb8,0x5dcc,0x73a9,0x764c,0x773c,0x5ca9,0x7feb,0x8d0b,
+ 0x96c1,0x9811,0x9854,0x9858,0x4f01,0x4f0e,0x5371,0x559c,0x5668,0x57fa,
+ 0x5947,0x5b09,0x5bc4,0x5c90,0x5e0c,0x5e7e,0x5fcc,0x63ee,0x673a,0x65d7,
+ 0x65e2,0x671f,0x68cb,0x68c4
+ },
+ { /* ku 15 */
+ 0x6a5f,0x5e30,0x6bc5,0x6c17,0x6c7d,0x757f,0x7948,0x5b63,0x7a00,0x7d00,
+ 0x5fbd,0x898f,0x8a18,0x8cb4,0x8d77,0x8ecc,0x8f1d,0x98e2,0x9a0e,0x9b3c,
+ 0x4e80,0x507d,0x5100,0x5993,0x5b9c,0x622f,0x6280,0x64ec,0x6b3a,0x72a0,
+ 0x7591,0x7947,0x7fa9,0x87fb,0x8abc,0x8b70,0x63ac,0x83ca,0x97a0,0x5409,
+ 0x5403,0x55ab,0x6854,0x6a58,0x8a70,0x7827,0x6775,0x9ecd,0x5374,0x5ba2,
+ 0x811a,0x8650,0x9006,0x4e18,0x4e45,0x4ec7,0x4f11,0x53ca,0x5438,0x5bae,
+ 0x5f13,0x6025,0x6551,0x673d,0x6c42,0x6c72,0x6ce3,0x7078,0x7403,0x7a76,
+ 0x7aae,0x7b08,0x7d1a,0x7cfe,0x7d66,0x65e7,0x725b,0x53bb,0x5c45,0x5de8,
+ 0x62d2,0x62e0,0x6319,0x6e20,0x865a,0x8a31,0x8ddd,0x92f8,0x6f01,0x79a6,
+ 0x9b5a,0x4ea8,0x4eab,0x4eac
+ },
+ { /* ku 16 */
+ 0x4f9b,0x4fa0,0x50d1,0x5147,0x7af6,0x5171,0x51f6,0x5354,0x5321,0x537f,
+ 0x53eb,0x55ac,0x5883,0x5ce1,0x5f37,0x5f4a,0x602f,0x6050,0x606d,0x631f,
+ 0x6559,0x6a4b,0x6cc1,0x72c2,0x72ed,0x77ef,0x80f8,0x8105,0x8208,0x854e,
+ 0x90f7,0x93e1,0x97ff,0x9957,0x9a5a,0x4ef0,0x51dd,0x5c2d,0x6681,0x696d,
+ 0x5c40,0x66f2,0x6975,0x7389,0x6850,0x7c81,0x50c5,0x52e4,0x5747,0x5dfe,
+ 0x9326,0x65a4,0x6b23,0x6b3d,0x7434,0x7981,0x79bd,0x7b4b,0x7dca,0x82b9,
+ 0x83cc,0x887f,0x895f,0x8b39,0x8fd1,0x91d1,0x541f,0x9280,0x4e5d,0x5036,
+ 0x53e5,0x533a,0x72d7,0x7396,0x77e9,0x82e6,0x8eaf,0x99c6,0x99c8,0x99d2,
+ 0x5177,0x611a,0x865e,0x55b0,0x7a7a,0x5076,0x5bd3,0x9047,0x9685,0x4e32,
+ 0x6adb,0x91e7,0x5c51,0x5c48
+ },
+ { /* ku 17 */
+ 0x6398,0x7a9f,0x6c93,0x9774,0x8f61,0x7aaa,0x718a,0x9688,0x7c82,0x6817,
+ 0x7e70,0x6851,0x936c,0x52f2,0x541b,0x85ab,0x8a13,0x7fa4,0x8ecd,0x90e1,
+ 0x5366,0x8888,0x7941,0x4fc2,0x50be,0x5211,0x5144,0x5553,0x572d,0x73ea,
+ 0x578b,0x5951,0x5f62,0x5f84,0x6075,0x6176,0x6167,0x61a9,0x63b2,0x643a,
+ 0x656c,0x666f,0x6842,0x6e13,0x7566,0x7a3d,0x7cfb,0x7d4c,0x7d99,0x7e4b,
+ 0x7f6b,0x830e,0x834a,0x86cd,0x8a08,0x8a63,0x8b66,0x8efd,0x981a,0x9d8f,
+ 0x82b8,0x8fce,0x9be8,0x5287,0x621f,0x6483,0x6fc0,0x9699,0x6841,0x5091,
+ 0x6b20,0x6c7a,0x6f54,0x7a74,0x7d50,0x8840,0x8a23,0x6708,0x4ef6,0x5039,
+ 0x5026,0x5065,0x517c,0x5238,0x5263,0x55a7,0x570f,0x5805,0x5acc,0x5efa,
+ 0x61b2,0x61f8,0x62f3,0x6372
+ },
+ { /* ku 18 */
+ 0x691c,0x6a29,0x727d,0x72ac,0x732e,0x7814,0x786f,0x7d79,0x770c,0x80a9,
+ 0x898b,0x8b19,0x8ce2,0x8ed2,0x9063,0x9375,0x967a,0x9855,0x9a13,0x9e78,
+ 0x5143,0x539f,0x53b3,0x5e7b,0x5f26,0x6e1b,0x6e90,0x7384,0x73fe,0x7d43,
+ 0x8237,0x8a00,0x8afa,0x9650,0x4e4e,0x500b,0x53e4,0x547c,0x56fa,0x59d1,
+ 0x5b64,0x5df1,0x5eab,0x5f27,0x6238,0x6545,0x67af,0x6e56,0x72d0,0x7cca,
+ 0x88b4,0x80a1,0x80e1,0x83f0,0x864e,0x8a87,0x8de8,0x9237,0x96c7,0x9867,
+ 0x9f13,0x4e94,0x4e92,0x4f0d,0x5348,0x5449,0x543e,0x5a2f,0x5f8c,0x5fa1,
+ 0x609f,0x68a7,0x6a8e,0x745a,0x7881,0x8a9e,0x8aa4,0x8b77,0x9190,0x4e5e,
+ 0x9bc9,0x4ea4,0x4f7c,0x4faf,0x5019,0x5016,0x5149,0x516c,0x529f,0x52b9,
+ 0x52fe,0x539a,0x53e3,0x5411
+ },
+ { /* ku 19 */
+ 0x540e,0x5589,0x5751,0x57a2,0x597d,0x5b54,0x5b5d,0x5b8f,0x5de5,0x5de7,
+ 0x5df7,0x5e78,0x5e83,0x5e9a,0x5eb7,0x5f18,0x6052,0x614c,0x6297,0x62d8,
+ 0x63a7,0x653b,0x6602,0x6643,0x66f4,0x676d,0x6821,0x6897,0x69cb,0x6c5f,
+ 0x6d2a,0x6d69,0x6e2f,0x6e9d,0x7532,0x7687,0x786c,0x7a3f,0x7ce0,0x7d05,
+ 0x7d18,0x7d5e,0x7db1,0x8015,0x8003,0x80af,0x80b1,0x8154,0x818f,0x822a,
+ 0x8352,0x884c,0x8861,0x8b1b,0x8ca2,0x8cfc,0x90ca,0x9175,0x9271,0x783f,
+ 0x92fc,0x95a4,0x964d,0x9805,0x9999,0x9ad8,0x9d3b,0x525b,0x52ab,0x53f7,
+ 0x5408,0x58d5,0x62f7,0x6fe0,0x8c6a,0x8f5f,0x9eb9,0x514b,0x523b,0x544a,
+ 0x56fd,0x7a40,0x9177,0x9d60,0x9ed2,0x7344,0x6f09,0x8170,0x7511,0x5ffd,
+ 0x60da,0x9aa8,0x72db,0x8fbc
+ },
+ { /* ku 1a */
+ 0x6b64,0x9803,0x4eca,0x56f0,0x5764,0x58be,0x5a5a,0x6068,0x61c7,0x660f,
+ 0x6606,0x6839,0x68b1,0x6df7,0x75d5,0x7d3a,0x826e,0x9b42,0x4e9b,0x4f50,
+ 0x53c9,0x5506,0x5d6f,0x5de6,0x5dee,0x67fb,0x6c99,0x7473,0x7802,0x8a50,
+ 0x9396,0x88df,0x5750,0x5ea7,0x632b,0x50b5,0x50ac,0x518d,0x6700,0x54c9,
+ 0x585e,0x59bb,0x5bb0,0x5f69,0x624d,0x63a1,0x683d,0x6b73,0x6e08,0x707d,
+ 0x91c7,0x7280,0x7815,0x7826,0x796d,0x658e,0x7d30,0x83dc,0x88c1,0x8f09,
+ 0x969b,0x5264,0x5728,0x6750,0x7f6a,0x8ca1,0x51b4,0x5742,0x962a,0x583a,
+ 0x698a,0x80b4,0x54b2,0x5d0e,0x57fc,0x7895,0x9dfa,0x4f5c,0x524a,0x548b,
+ 0x643e,0x6628,0x6714,0x67f5,0x7a84,0x7b56,0x7d22,0x932f,0x685c,0x9bad,
+ 0x7b39,0x5319,0x518a,0x5237
+ },
+ { /* ku 1b */
+ 0x5bdf,0x62f6,0x64ae,0x64e6,0x672d,0x6bba,0x85a9,0x96d1,0x7690,0x9bd6,
+ 0x634c,0x9306,0x9bab,0x76bf,0x6652,0x4e09,0x5098,0x53c2,0x5c71,0x60e8,
+ 0x6492,0x6563,0x685f,0x71e6,0x73ca,0x7523,0x7b97,0x7e82,0x8695,0x8b83,
+ 0x8cdb,0x9178,0x9910,0x65ac,0x66ab,0x6b8b,0x4ed5,0x4ed4,0x4f3a,0x4f7f,
+ 0x523a,0x53f8,0x53f2,0x55e3,0x56db,0x58eb,0x59cb,0x59c9,0x59ff,0x5b50,
+ 0x5c4d,0x5e02,0x5e2b,0x5fd7,0x601d,0x6307,0x652f,0x5b5c,0x65af,0x65bd,
+ 0x65e8,0x679d,0x6b62,0x6b7b,0x6c0f,0x7345,0x7949,0x79c1,0x7cf8,0x7d19,
+ 0x7d2b,0x80a2,0x8102,0x81f3,0x8996,0x8a5e,0x8a69,0x8a66,0x8a8c,0x8aee,
+ 0x8cc7,0x8cdc,0x96cc,0x98fc,0x6b6f,0x4e8b,0x4f3c,0x4f8d,0x5150,0x5b57,
+ 0x5bfa,0x6148,0x6301,0x6642
+ },
+ { /* ku 1c */
+ 0x6b21,0x6ecb,0x6cbb,0x723e,0x74bd,0x75d4,0x78c1,0x793a,0x800c,0x8033,
+ 0x81ea,0x8494,0x8f9e,0x6c50,0x9e7f,0x5f0f,0x8b58,0x9d2b,0x7afa,0x8ef8,
+ 0x5b8d,0x96eb,0x4e03,0x53f1,0x57f7,0x5931,0x5ac9,0x5ba4,0x6089,0x6e7f,
+ 0x6f06,0x75be,0x8cea,0x5b9f,0x8500,0x7be0,0x5072,0x67f4,0x829d,0x5c61,
+ 0x854a,0x7e1e,0x820e,0x5199,0x5c04,0x6368,0x8d66,0x659c,0x716e,0x793e,
+ 0x7d17,0x8005,0x8b1d,0x8eca,0x906e,0x86c7,0x90aa,0x501f,0x52fa,0x5c3a,
+ 0x6753,0x707c,0x7235,0x914c,0x91c8,0x932b,0x82e5,0x5bc2,0x5f31,0x60f9,
+ 0x4e3b,0x53d6,0x5b88,0x624b,0x6731,0x6b8a,0x72e9,0x73e0,0x7a2e,0x816b,
+ 0x8da3,0x9152,0x9996,0x5112,0x53d7,0x546a,0x5bff,0x6388,0x6a39,0x7dac,
+ 0x9700,0x56da,0x53ce,0x5468
+ },
+ { /* ku 1d */
+ 0x5b97,0x5c31,0x5dde,0x4fee,0x6101,0x62fe,0x6d32,0x79c0,0x79cb,0x7d42,
+ 0x7e4d,0x7fd2,0x81ed,0x821f,0x8490,0x8846,0x8972,0x8b90,0x8e74,0x8f2f,
+ 0x9031,0x914b,0x916c,0x96c6,0x919c,0x4ec0,0x4f4f,0x5145,0x5341,0x5f93,
+ 0x620e,0x67d4,0x6c41,0x6e0b,0x7363,0x7e26,0x91cd,0x9283,0x53d4,0x5919,
+ 0x5bbf,0x6dd1,0x795d,0x7e2e,0x7c9b,0x587e,0x719f,0x51fa,0x8853,0x8ff0,
+ 0x4fca,0x5cfb,0x6625,0x77ac,0x7ae3,0x821c,0x99ff,0x51c6,0x5faa,0x65ec,
+ 0x696f,0x6b89,0x6df3,0x6e96,0x6f64,0x76fe,0x7d14,0x5de1,0x9075,0x9187,
+ 0x9806,0x51e6,0x521d,0x6240,0x6691,0x66d9,0x6e1a,0x5eb6,0x7dd2,0x7f72,
+ 0x66f8,0x85af,0x85f7,0x8af8,0x52a9,0x53d9,0x5973,0x5e8f,0x5f90,0x6055,
+ 0x92e4,0x9664,0x50b7,0x511f
+ },
+ { /* ku 1e */
+ 0x52dd,0x5320,0x5347,0x53ec,0x54e8,0x5546,0x5531,0x5617,0x5968,0x59be,
+ 0x5a3c,0x5bb5,0x5c06,0x5c0f,0x5c11,0x5c1a,0x5e84,0x5e8a,0x5ee0,0x5f70,
+ 0x627f,0x6284,0x62db,0x638c,0x6377,0x6607,0x660c,0x662d,0x6676,0x677e,
+ 0x68a2,0x6a1f,0x6a35,0x6cbc,0x6d88,0x6e09,0x6e58,0x713c,0x7126,0x7167,
+ 0x75c7,0x7701,0x785d,0x7901,0x7965,0x79f0,0x7ae0,0x7b11,0x7ca7,0x7d39,
+ 0x8096,0x83d6,0x848b,0x8549,0x885d,0x88f3,0x8a1f,0x8a3c,0x8a54,0x8a73,
+ 0x8c61,0x8cde,0x91a4,0x9266,0x937e,0x9418,0x969c,0x9798,0x4e0a,0x4e08,
+ 0x4e1e,0x4e57,0x5197,0x5270,0x57ce,0x5834,0x58cc,0x5b22,0x5e38,0x60c5,
+ 0x64fe,0x6761,0x6756,0x6d44,0x72b6,0x7573,0x7a63,0x84b8,0x8b72,0x91b8,
+ 0x9320,0x5631,0x57f4,0x98fe
+ },
+ { /* ku 1f */
+ 0x62ed,0x690d,0x6b96,0x71ed,0x7e54,0x8077,0x8272,0x89e6,0x98df,0x8755,
+ 0x8fb1,0x5c3b,0x4f38,0x4fe1,0x4fb5,0x5507,0x5a20,0x5bdd,0x5be9,0x5fc3,
+ 0x614e,0x632f,0x65b0,0x664b,0x68ee,0x699b,0x6d78,0x6df1,0x7533,0x75b9,
+ 0x771f,0x795e,0x79e6,0x7d33,0x81e3,0x82af,0x85aa,0x89aa,0x8a3a,0x8eab,
+ 0x8f9b,0x9032,0x91dd,0x9707,0x4eba,0x4ec1,0x5203,0x5875,0x58ec,0x5c0b,
+ 0x751a,0x5c3d,0x814e,0x8a0a,0x8fc5,0x9663,0x976d,0x7b25,0x8acf,0x9808,
+ 0x9162,0x56f3,0x53a8,0x9017,0x5439,0x5782,0x5e25,0x63a8,0x6c34,0x708a,
+ 0x7761,0x7c8b,0x7fe0,0x8870,0x9042,0x9154,0x9310,0x9318,0x968f,0x745e,
+ 0x9ac4,0x5d07,0x5d69,0x6570,0x67a2,0x8da8,0x96db,0x636e,0x6749,0x6919,
+ 0x83c5,0x9817,0x96c0,0x88fe
+ },
+ { /* ku 20 */
+ 0x6f84,0x647a,0x5bf8,0x4e16,0x702c,0x755d,0x662f,0x51c4,0x5236,0x52e2,
+ 0x59d3,0x5f81,0x6027,0x6210,0x653f,0x6574,0x661f,0x6674,0x68f2,0x6816,
+ 0x6b63,0x6e05,0x7272,0x751f,0x76db,0x7cbe,0x8056,0x58f0,0x88fd,0x897f,
+ 0x8aa0,0x8a93,0x8acb,0x901d,0x9192,0x9752,0x9759,0x6589,0x7a0e,0x8106,
+ 0x96bb,0x5e2d,0x60dc,0x621a,0x65a5,0x6614,0x6790,0x77f3,0x7a4d,0x7c4d,
+ 0x7e3e,0x810a,0x8cac,0x8d64,0x8de1,0x8e5f,0x78a9,0x5207,0x62d9,0x63a5,
+ 0x6442,0x6298,0x8a2d,0x7a83,0x7bc0,0x8aac,0x96ea,0x7d76,0x820c,0x8749,
+ 0x4ed9,0x5148,0x5343,0x5360,0x5ba3,0x5c02,0x5c16,0x5ddd,0x6226,0x6247,
+ 0x64b0,0x6813,0x6834,0x6cc9,0x6d45,0x6d17,0x67d3,0x6f5c,0x714e,0x717d,
+ 0x65cb,0x7a7f,0x7bad,0x7dda
+ },
+ { /* ku 21 */
+ 0x7e4a,0x7fa8,0x817a,0x821b,0x8239,0x85a6,0x8a6e,0x8cce,0x8df5,0x9078,
+ 0x9077,0x92ad,0x9291,0x9583,0x9bae,0x524d,0x5584,0x6f38,0x7136,0x5168,
+ 0x7985,0x7e55,0x81b3,0x7cce,0x564c,0x5851,0x5ca8,0x63aa,0x66fe,0x66fd,
+ 0x695a,0x72d9,0x758f,0x758e,0x790e,0x7956,0x79df,0x7c97,0x7d20,0x7d44,
+ 0x8607,0x8a34,0x963b,0x9061,0x9f20,0x50e7,0x5275,0x53cc,0x53e2,0x5009,
+ 0x55aa,0x58ee,0x594f,0x723d,0x5b8b,0x5c64,0x531d,0x60e3,0x60f3,0x635c,
+ 0x6383,0x633f,0x63bb,0x64cd,0x65e9,0x66f9,0x5de3,0x69cd,0x69fd,0x6f15,
+ 0x71e5,0x4e89,0x75e9,0x76f8,0x7a93,0x7cdf,0x7dcf,0x7d9c,0x8061,0x8349,
+ 0x8358,0x846c,0x84bc,0x85fb,0x88c5,0x8d70,0x9001,0x906d,0x9397,0x971c,
+ 0x9a12,0x50cf,0x5897,0x618e
+ },
+ { /* ku 22 */
+ 0x81d3,0x8535,0x8d08,0x9020,0x4fc3,0x5074,0x5247,0x5373,0x606f,0x6349,
+ 0x675f,0x6e2c,0x8db3,0x901f,0x4fd7,0x5c5e,0x8cca,0x65cf,0x7d9a,0x5352,
+ 0x8896,0x5176,0x63c3,0x5b58,0x5b6b,0x5c0a,0x640d,0x6751,0x905c,0x4ed6,
+ 0x591a,0x592a,0x6c70,0x8a51,0x553e,0x5815,0x59a5,0x60f0,0x6253,0x67c1,
+ 0x8235,0x6955,0x9640,0x99c4,0x9a28,0x4f53,0x5806,0x5bfe,0x8010,0x5cb1,
+ 0x5e2f,0x5f85,0x6020,0x614b,0x6234,0x66ff,0x6cf0,0x6ede,0x80ce,0x817f,
+ 0x82d4,0x888b,0x8cb8,0x9000,0x902e,0x968a,0x9edb,0x9bdb,0x4ee3,0x53f0,
+ 0x5927,0x7b2c,0x918d,0x984c,0x9df9,0x6edd,0x7027,0x5353,0x5544,0x5b85,
+ 0x6258,0x629e,0x62d3,0x6ca2,0x6fef,0x7422,0x8a17,0x9438,0x6fc1,0x8afe,
+ 0x8338,0x51e7,0x86f8,0x53ea
+ },
+ { /* ku 23 */
+ 0x53e9,0x4f46,0x9054,0x8fb0,0x596a,0x8131,0x5dfd,0x7aea,0x8fbf,0x68da,
+ 0x8c37,0x72f8,0x9c48,0x6a3d,0x8ab0,0x4e39,0x5358,0x5606,0x5766,0x62c5,
+ 0x63a2,0x65e6,0x6b4e,0x6de1,0x6e5b,0x70ad,0x77ed,0x7aef,0x7baa,0x7dbb,
+ 0x803d,0x80c6,0x86cb,0x8a95,0x935b,0x56e3,0x58c7,0x5f3e,0x65ad,0x6696,
+ 0x6a80,0x6bb5,0x7537,0x8ac7,0x5024,0x77e5,0x5730,0x5f1b,0x6065,0x667a,
+ 0x6c60,0x75f4,0x7a1a,0x7f6e,0x81f4,0x8718,0x9045,0x99b3,0x7bc9,0x755c,
+ 0x7af9,0x7b51,0x84c4,0x9010,0x79e9,0x7a92,0x8336,0x5ae1,0x7740,0x4e2d,
+ 0x4ef2,0x5b99,0x5fe0,0x62bd,0x663c,0x67f1,0x6ce8,0x866b,0x8877,0x8a3b,
+ 0x914e,0x92f3,0x99d0,0x6a17,0x7026,0x732a,0x82e7,0x8457,0x8caf,0x4e01,
+ 0x5146,0x51cb,0x558b,0x5bf5
+ },
+ { /* ku 24 */
+ 0x5e16,0x5e33,0x5e81,0x5f14,0x5f35,0x5f6b,0x5fb4,0x61f2,0x6311,0x66a2,
+ 0x671d,0x6f6e,0x7252,0x753a,0x773a,0x8074,0x8139,0x8178,0x8776,0x8abf,
+ 0x8adc,0x8d85,0x8df3,0x929a,0x9577,0x9802,0x9ce5,0x52c5,0x6357,0x76f4,
+ 0x6715,0x6c88,0x73cd,0x8cc3,0x93ae,0x9673,0x6d25,0x589c,0x690e,0x69cc,
+ 0x8ffd,0x939a,0x75db,0x901a,0x585a,0x6802,0x63b4,0x69fb,0x4f43,0x6f2c,
+ 0x67d8,0x8fbb,0x8526,0x7db4,0x9354,0x693f,0x6f70,0x576a,0x58f7,0x5b2c,
+ 0x7d2c,0x722a,0x540a,0x91e3,0x9db4,0x4ead,0x4f4e,0x505c,0x5075,0x5243,
+ 0x8c9e,0x5448,0x5824,0x5b9a,0x5e1d,0x5e95,0x5ead,0x5ef7,0x5f1f,0x608c,
+ 0x62b5,0x633a,0x63d0,0x68af,0x6c40,0x7887,0x798e,0x7a0b,0x7de0,0x8247,
+ 0x8a02,0x8ae6,0x8e44,0x9013
+ },
+ { /* ku 25 */
+ 0x90b8,0x912d,0x91d8,0x9f0e,0x6ce5,0x6458,0x64e2,0x6575,0x6ef4,0x7684,
+ 0x7b1b,0x9069,0x93d1,0x6eba,0x54f2,0x5fb9,0x64a4,0x8f4d,0x8fed,0x9244,
+ 0x5178,0x586b,0x5929,0x5c55,0x5e97,0x6dfb,0x7e8f,0x751c,0x8cbc,0x8ee2,
+ 0x985b,0x70b9,0x4f1d,0x6bbf,0x6fb1,0x7530,0x96fb,0x514e,0x5410,0x5835,
+ 0x5857,0x59ac,0x5c60,0x5f92,0x6597,0x675c,0x6e21,0x767b,0x83df,0x8ced,
+ 0x9014,0x90fd,0x934d,0x7825,0x783a,0x52aa,0x5ea6,0x571f,0x5974,0x6012,
+ 0x5012,0x515a,0x51ac,0x51cd,0x5200,0x5510,0x5854,0x5858,0x5957,0x5b95,
+ 0x5cf6,0x5d8b,0x60bc,0x6295,0x642d,0x6771,0x6843,0x68bc,0x68df,0x76d7,
+ 0x6dd8,0x6e6f,0x6d9b,0x706f,0x71c8,0x5f53,0x75d8,0x7977,0x7b49,0x7b54,
+ 0x7b52,0x7cd6,0x7d71,0x5230
+ },
+ { /* ku 26 */
+ 0x8463,0x8569,0x85e4,0x8a0e,0x8b04,0x8c46,0x8e0f,0x9003,0x900f,0x9419,
+ 0x9676,0x982d,0x9a30,0x95d8,0x50cd,0x52d5,0x540c,0x5802,0x5c0e,0x61a7,
+ 0x649e,0x6d1e,0x77b3,0x7ae5,0x80f4,0x8404,0x9053,0x9285,0x5ce0,0x9d07,
+ 0x533f,0x5f97,0x5fb3,0x6d9c,0x7279,0x7763,0x79bf,0x7be4,0x6bd2,0x72ec,
+ 0x8aad,0x6803,0x6a61,0x51f8,0x7a81,0x6934,0x5c4a,0x9cf6,0x82eb,0x5bc5,
+ 0x9149,0x701e,0x5678,0x5c6f,0x60c7,0x6566,0x6c8c,0x8c5a,0x9041,0x9813,
+ 0x5451,0x66c7,0x920d,0x5948,0x90a3,0x5185,0x4e4d,0x51ea,0x8599,0x8b0e,
+ 0x7058,0x637a,0x934b,0x6962,0x99b4,0x7e04,0x7577,0x5357,0x6960,0x8edf,
+ 0x96e3,0x6c5d,0x4e8c,0x5c3c,0x5f10,0x8fe9,0x5302,0x8cd1,0x8089,0x8679,
+ 0x5eff,0x65e5,0x4e73,0x5165
+ },
+ { /* ku 27 */
+ 0x5982,0x5c3f,0x97ee,0x4efb,0x598a,0x5fcd,0x8a8d,0x6fe1,0x79b0,0x7962,
+ 0x5be7,0x8471,0x732b,0x71b1,0x5e74,0x5ff5,0x637b,0x649a,0x71c3,0x7c98,
+ 0x4e43,0x5efc,0x4e4b,0x57dc,0x56a2,0x60a9,0x6fc3,0x7d0d,0x80fd,0x8133,
+ 0x81bf,0x8fb2,0x8997,0x86a4,0x5df4,0x628a,0x64ad,0x8987,0x6777,0x6ce2,
+ 0x6d3e,0x7436,0x7834,0x5a46,0x7f75,0x82ad,0x99ac,0x4ff3,0x5ec3,0x62dd,
+ 0x6392,0x6557,0x676f,0x76c3,0x724c,0x80cc,0x80ba,0x8f29,0x914d,0x500d,
+ 0x57f9,0x5a92,0x6885,0x6973,0x7164,0x72fd,0x8cb7,0x58f2,0x8ce0,0x966a,
+ 0x9019,0x877f,0x79e4,0x77e7,0x8429,0x4f2f,0x5265,0x535a,0x62cd,0x67cf,
+ 0x6cca,0x767d,0x7b94,0x7c95,0x8236,0x8584,0x8feb,0x66dd,0x6f20,0x7206,
+ 0x7e1b,0x83ab,0x99c1,0x9ea6
+ },
+ { /* ku 28 */
+ 0x51fd,0x7bb1,0x7872,0x7bb8,0x8087,0x7b48,0x6ae8,0x5e61,0x808c,0x7551,
+ 0x7560,0x516b,0x9262,0x6e8c,0x767a,0x9197,0x9aea,0x4f10,0x7f70,0x629c,
+ 0x7b4f,0x95a5,0x9ce9,0x567a,0x5859,0x86e4,0x96bc,0x4f34,0x5224,0x534a,
+ 0x53cd,0x53db,0x5e06,0x642c,0x6591,0x677f,0x6c3e,0x6c4e,0x7248,0x72af,
+ 0x73ed,0x7554,0x7e41,0x822c,0x85e9,0x8ca9,0x7bc4,0x91c6,0x7169,0x9812,
+ 0x98ef,0x633d,0x6669,0x756a,0x76e4,0x78d0,0x8543,0x86ee,0x532a,0x5351,
+ 0x5426,0x5983,0x5e87,0x5f7c,0x60b2,0x6249,0x6279,0x62ab,0x6590,0x6bd4,
+ 0x6ccc,0x75b2,0x76ae,0x7891,0x79d8,0x7dcb,0x7f77,0x80a5,0x88ab,0x8ab9,
+ 0x8cbb,0x907f,0x975e,0x98db,0x6a0b,0x7c38,0x5099,0x5c3e,0x5fae,0x6787,
+ 0x6bd8,0x7435,0x7709,0x7f8e
+ },
+ { /* ku 29 */
+ 0x9f3b,0x67ca,0x7a17,0x5339,0x758b,0x9aed,0x5f66,0x819d,0x83f1,0x8098,
+ 0x5f3c,0x5fc5,0x7562,0x7b46,0x903c,0x6867,0x59eb,0x5a9b,0x7d10,0x767e,
+ 0x8b2c,0x4ff5,0x5f6a,0x6a19,0x6c37,0x6f02,0x74e2,0x7968,0x8868,0x8a55,
+ 0x8c79,0x5edf,0x63cf,0x75c5,0x79d2,0x82d7,0x9328,0x92f2,0x849c,0x86ed,
+ 0x9c2d,0x54c1,0x5f6c,0x658c,0x6d5c,0x7015,0x8ca7,0x8cd3,0x983b,0x654f,
+ 0x74f6,0x4e0d,0x4ed8,0x57e0,0x592b,0x5a66,0x5bcc,0x51a8,0x5e03,0x5e9c,
+ 0x6016,0x6276,0x6577,0x65a7,0x666e,0x6d6e,0x7236,0x7b26,0x8150,0x819a,
+ 0x8299,0x8b5c,0x8ca0,0x8ce6,0x8d74,0x961c,0x9644,0x4fae,0x64ab,0x6b66,
+ 0x821e,0x8461,0x856a,0x90e8,0x5c01,0x6953,0x98a8,0x847a,0x8557,0x4f0f,
+ 0x526f,0x5fa9,0x5e45,0x670d
+ },
+ { /* ku 2a */
+ 0x798f,0x8179,0x8907,0x8986,0x6df5,0x5f17,0x6255,0x6cb8,0x4ecf,0x7269,
+ 0x9b92,0x5206,0x543b,0x5674,0x58b3,0x61a4,0x626e,0x711a,0x596e,0x7c89,
+ 0x7cde,0x7d1b,0x96f0,0x6587,0x805e,0x4e19,0x4f75,0x5175,0x5840,0x5e63,
+ 0x5e73,0x5f0a,0x67c4,0x4e26,0x853d,0x9589,0x965b,0x7c73,0x9801,0x50fb,
+ 0x58c1,0x7656,0x78a7,0x5225,0x77a5,0x8511,0x7b86,0x504f,0x5909,0x7247,
+ 0x7bc7,0x7de8,0x8fba,0x8fd4,0x904d,0x4fbf,0x52c9,0x5a29,0x5f01,0x97ad,
+ 0x4fdd,0x8217,0x92ea,0x5703,0x6355,0x6b69,0x752b,0x88dc,0x8f14,0x7a42,
+ 0x52df,0x5893,0x6155,0x620a,0x66ae,0x6bcd,0x7c3f,0x83e9,0x5023,0x4ff8,
+ 0x5305,0x5446,0x5831,0x5949,0x5b9d,0x5cf0,0x5cef,0x5d29,0x5e96,0x62b1,
+ 0x6367,0x653e,0x65b9,0x670b
+ },
+ { /* ku 2b */
+ 0x6cd5,0x6ce1,0x70f9,0x7832,0x7e2b,0x80de,0x82b3,0x840c,0x84ec,0x8702,
+ 0x8912,0x8a2a,0x8c4a,0x90a6,0x92d2,0x98fd,0x9cf3,0x9d6c,0x4e4f,0x4ea1,
+ 0x508d,0x5256,0x574a,0x59a8,0x5e3d,0x5fd8,0x5fd9,0x623f,0x66b4,0x671b,
+ 0x67d0,0x68d2,0x5192,0x7d21,0x80aa,0x81a8,0x8b00,0x8c8c,0x8cbf,0x927e,
+ 0x9632,0x5420,0x982c,0x5317,0x50d5,0x535c,0x58a8,0x64b2,0x6734,0x7267,
+ 0x7766,0x7a46,0x91e6,0x52c3,0x6ca1,0x6b86,0x5800,0x5e4c,0x5954,0x672c,
+ 0x7ffb,0x51e1,0x76c6,0x6469,0x78e8,0x9b54,0x9ebb,0x57cb,0x59b9,0x6627,
+ 0x679a,0x6bce,0x54e9,0x69d9,0x5e55,0x819c,0x6795,0x9baa,0x67fe,0x9c52,
+ 0x685d,0x4ea6,0x4fe3,0x53c8,0x62b9,0x672b,0x6cab,0x8fc4,0x4fad,0x7e6d,
+ 0x9ebf,0x4e07,0x6162,0x6e80
+ },
+ { /* ku 2c */
+ 0x6f2b,0x8513,0x5473,0x672a,0x9b45,0x5df3,0x7b95,0x5cac,0x5bc6,0x871c,
+ 0x6e4a,0x84d1,0x7a14,0x8108,0x5999,0x7c8d,0x6c11,0x7720,0x52d9,0x5922,
+ 0x7121,0x725f,0x77db,0x9727,0x9d61,0x690b,0x5a7f,0x5a18,0x51a5,0x540d,
+ 0x547d,0x660e,0x76df,0x8ff7,0x9298,0x9cf4,0x59ea,0x725d,0x6ec5,0x514d,
+ 0x68c9,0x7dbf,0x7dec,0x9762,0x9eba,0x6478,0x6a21,0x8302,0x5984,0x5b5f,
+ 0x6bdb,0x731b,0x76f2,0x7db2,0x8017,0x8499,0x5132,0x6728,0x9ed9,0x76ee,
+ 0x6762,0x52ff,0x9905,0x5c24,0x623b,0x7c7e,0x8cb0,0x554f,0x60b6,0x7d0b,
+ 0x9580,0x5301,0x4e5f,0x51b6,0x591c,0x723a,0x8036,0x91ce,0x5f25,0x77e2,
+ 0x5384,0x5f79,0x7d04,0x85ac,0x8a33,0x8e8d,0x9756,0x67f3,0x85ae,0x9453,
+ 0x6109,0x6108,0x6cb9,0x7652
+ },
+ { /* ku 2d */
+ 0x8aed,0x8f38,0x552f,0x4f51,0x512a,0x52c7,0x53cb,0x5ba5,0x5e7d,0x60a0,
+ 0x6182,0x63d6,0x6709,0x67da,0x6e67,0x6d8c,0x7336,0x7337,0x7531,0x7950,
+ 0x88d5,0x8a98,0x904a,0x9091,0x90f5,0x96c4,0x878d,0x5915,0x4e88,0x4f59,
+ 0x4e0e,0x8a89,0x8f3f,0x9810,0x50ad,0x5e7c,0x5996,0x5bb9,0x5eb8,0x63da,
+ 0x63fa,0x64c1,0x66dc,0x694a,0x69d8,0x6d0b,0x6eb6,0x7194,0x7528,0x7aaf,
+ 0x7f8a,0x8000,0x8449,0x84c9,0x8981,0x8b21,0x8e0a,0x9065,0x967d,0x990a,
+ 0x617e,0x6291,0x6b32,0x6c83,0x6d74,0x7fcc,0x7ffc,0x6dc0,0x7f85,0x87ba,
+ 0x88f8,0x6765,0x83b1,0x983c,0x96f7,0x6d1b,0x7d61,0x843d,0x916a,0x4e71,
+ 0x5375,0x5d50,0x6b04,0x6feb,0x85cd,0x862d,0x89a7,0x5229,0x540f,0x5c65,
+ 0x674e,0x68a8,0x7406,0x7483
+ },
+ { /* ku 2e */
+ 0x75e2,0x88cf,0x88e1,0x91cc,0x96e2,0x9678,0x5f8b,0x7387,0x7acb,0x844e,
+ 0x63a0,0x7565,0x5289,0x6d41,0x6e9c,0x7409,0x7559,0x786b,0x7c92,0x9686,
+ 0x7adc,0x9f8d,0x4fb6,0x616e,0x65c5,0x865c,0x4e86,0x4eae,0x50da,0x4e21,
+ 0x51cc,0x5bee,0x6599,0x6881,0x6dbc,0x731f,0x7642,0x77ad,0x7a1c,0x7ce7,
+ 0x826f,0x8ad2,0x907c,0x91cf,0x9675,0x9818,0x529b,0x7dd1,0x502b,0x5398,
+ 0x6797,0x6dcb,0x71d0,0x7433,0x81e8,0x8f2a,0x96a3,0x9c57,0x9e9f,0x7460,
+ 0x5841,0x6d99,0x7d2f,0x985e,0x4ee4,0x4f36,0x4f8b,0x51b7,0x52b1,0x5dba,
+ 0x601c,0x73b2,0x793c,0x82d3,0x9234,0x96b7,0x96f6,0x970a,0x9e97,0x9f62,
+ 0x66a6,0x6b74,0x5217,0x52a3,0x70c8,0x88c2,0x5ec9,0x604b,0x6190,0x6f23,
+ 0x7149,0x7c3e,0x7df4,0x806f
+ },
+ { /* ku 2f */
+ 0x84ee,0x9023,0x932c,0x5442,0x9b6f,0x6ad3,0x7089,0x8cc2,0x8def,0x9732,
+ 0x52b4,0x5a41,0x5eca,0x5f04,0x6717,0x697c,0x6994,0x6d6a,0x6f0f,0x7262,
+ 0x72fc,0x7bed,0x8001,0x807e,0x874b,0x90ce,0x516d,0x9e93,0x7984,0x808b,
+ 0x9332,0x8ad6,0x502d,0x548c,0x8a71,0x6b6a,0x8cc4,0x8107,0x60d1,0x67a0,
+ 0x9df2,0x4e99,0x4e98,0x9c10,0x8a6b,0x85c1,0x8568,0x6900,0x6e7e,0x7897,
+ 0x8155,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 30 */
+ 0x5f0c,0x4e10,0x4e15,0x4e2a,0x4e31,0x4e36,0x4e3c,0x4e3f,0x4e42,0x4e56,
+ 0x4e58,0x4e82,0x4e85,0x8c6b,0x4e8a,0x8212,0x5f0d,0x4e8e,0x4e9e,0x4e9f,
+ 0x4ea0,0x4ea2,0x4eb0,0x4eb3,0x4eb6,0x4ece,0x4ecd,0x4ec4,0x4ec6,0x4ec2,
+ 0x4ed7,0x4ede,0x4eed,0x4edf,0x4ef7,0x4f09,0x4f5a,0x4f30,0x4f5b,0x4f5d,
+ 0x4f57,0x4f47,0x4f76,0x4f88,0x4f8f,0x4f98,0x4f7b,0x4f69,0x4f70,0x4f91,
+ 0x4f6f,0x4f86,0x4f96,0x5118,0x4fd4,0x4fdf,0x4fce,0x4fd8,0x4fdb,0x4fd1,
+ 0x4fda,0x4fd0,0x4fe4,0x4fe5,0x501a,0x5028,0x5014,0x502a,0x5025,0x5005,
+ 0x4f1c,0x4ff6,0x5021,0x5029,0x502c,0x4ffe,0x4fef,0x5011,0x5006,0x5043,
+ 0x5047,0x6703,0x5055,0x5050,0x5048,0x505a,0x5056,0x506c,0x5078,0x5080,
+ 0x509a,0x5085,0x50b4,0x50b2
+ },
+ { /* ku 31 */
+ 0x50c9,0x50ca,0x50b3,0x50c2,0x50d6,0x50de,0x50e5,0x50ed,0x50e3,0x50ee,
+ 0x50f9,0x50f5,0x5109,0x5101,0x5102,0x5116,0x5115,0x5114,0x511a,0x5121,
+ 0x513a,0x5137,0x513c,0x513b,0x513f,0x5140,0x5152,0x514c,0x5154,0x5162,
+ 0x7af8,0x5169,0x516a,0x516e,0x5180,0x5182,0x56d8,0x518c,0x5189,0x518f,
+ 0x5191,0x5193,0x5195,0x5196,0x51a4,0x51a6,0x51a2,0x51a9,0x51aa,0x51ab,
+ 0x51b3,0x51b1,0x51b2,0x51b0,0x51b5,0x51bd,0x51c5,0x51c9,0x51db,0x51e0,
+ 0x8655,0x51e9,0x51ed,0x51f0,0x51f5,0x51fe,0x5204,0x520b,0x5214,0x520e,
+ 0x5227,0x522a,0x522e,0x5233,0x5239,0x524f,0x5244,0x524b,0x524c,0x525e,
+ 0x5254,0x526a,0x5274,0x5269,0x5273,0x527f,0x527d,0x528d,0x5294,0x5292,
+ 0x5271,0x5288,0x5291,0x8fa8
+ },
+ { /* ku 32 */
+ 0x8fa7,0x52ac,0x52ad,0x52bc,0x52b5,0x52c1,0x52cd,0x52d7,0x52de,0x52e3,
+ 0x52e6,0x98ed,0x52e0,0x52f3,0x52f5,0x52f8,0x52f9,0x5306,0x5308,0x7538,
+ 0x530d,0x5310,0x530f,0x5315,0x531a,0x5323,0x532f,0x5331,0x5333,0x5338,
+ 0x5340,0x5346,0x5345,0x4e17,0x5349,0x534d,0x51d6,0x535e,0x5369,0x536e,
+ 0x5918,0x537b,0x5377,0x5382,0x5396,0x53a0,0x53a6,0x53a5,0x53ae,0x53b0,
+ 0x53b6,0x53c3,0x7c12,0x96d9,0x53df,0x66fc,0x71ee,0x53ee,0x53e8,0x53ed,
+ 0x53fa,0x5401,0x543d,0x5440,0x542c,0x542d,0x543c,0x542e,0x5436,0x5429,
+ 0x541d,0x544e,0x548f,0x5475,0x548e,0x545f,0x5471,0x5477,0x5470,0x5492,
+ 0x547b,0x5480,0x5476,0x5484,0x5490,0x5486,0x54c7,0x54a2,0x54b8,0x54a5,
+ 0x54ac,0x54c4,0x54c8,0x54a8
+ },
+ { /* ku 33 */
+ 0x54ab,0x54c2,0x54a4,0x54be,0x54bc,0x54d8,0x54e5,0x54e6,0x550f,0x5514,
+ 0x54fd,0x54ee,0x54ed,0x54fa,0x54e2,0x5539,0x5540,0x5563,0x554c,0x552e,
+ 0x555c,0x5545,0x5556,0x5557,0x5538,0x5533,0x555d,0x5599,0x5580,0x54af,
+ 0x558a,0x559f,0x557b,0x557e,0x5598,0x559e,0x55ae,0x557c,0x5583,0x55a9,
+ 0x5587,0x55a8,0x55da,0x55c5,0x55df,0x55c4,0x55dc,0x55e4,0x55d4,0x5614,
+ 0x55f7,0x5616,0x55fe,0x55fd,0x561b,0x55f9,0x564e,0x5650,0x71df,0x5634,
+ 0x5636,0x5632,0x5638,0x566b,0x5664,0x562f,0x566c,0x566a,0x5686,0x5680,
+ 0x568a,0x56a0,0x5694,0x568f,0x56a5,0x56ae,0x56b6,0x56b4,0x56c2,0x56bc,
+ 0x56c1,0x56c3,0x56c0,0x56c8,0x56ce,0x56d1,0x56d3,0x56d7,0x56ee,0x56f9,
+ 0x5700,0x56ff,0x5704,0x5709
+ },
+ { /* ku 34 */
+ 0x5708,0x570b,0x570d,0x5713,0x5718,0x5716,0x55c7,0x571c,0x5726,0x5737,
+ 0x5738,0x574e,0x573b,0x5740,0x574f,0x5769,0x57c0,0x5788,0x5761,0x577f,
+ 0x5789,0x5793,0x57a0,0x57b3,0x57a4,0x57aa,0x57b0,0x57c3,0x57c6,0x57d4,
+ 0x57d2,0x57d3,0x580a,0x57d6,0x57e3,0x580b,0x5819,0x581d,0x5872,0x5821,
+ 0x5862,0x584b,0x5870,0x6bc0,0x5852,0x583d,0x5879,0x5885,0x58b9,0x589f,
+ 0x58ab,0x58ba,0x58de,0x58bb,0x58b8,0x58ae,0x58c5,0x58d3,0x58d1,0x58d7,
+ 0x58d9,0x58d8,0x58e5,0x58dc,0x58e4,0x58df,0x58ef,0x58fa,0x58f9,0x58fb,
+ 0x58fc,0x58fd,0x5902,0x590a,0x5910,0x591b,0x68a6,0x5925,0x592c,0x592d,
+ 0x5932,0x5938,0x593e,0x7ad2,0x5955,0x5950,0x594e,0x595a,0x5958,0x5962,
+ 0x5960,0x5967,0x596c,0x5969
+ },
+ { /* ku 35 */
+ 0x5978,0x5981,0x599d,0x4f5e,0x4fab,0x59a3,0x59b2,0x59c6,0x59e8,0x59dc,
+ 0x598d,0x59d9,0x59da,0x5a25,0x5a1f,0x5a11,0x5a1c,0x5a09,0x5a1a,0x5a40,
+ 0x5a6c,0x5a49,0x5a35,0x5a36,0x5a62,0x5a6a,0x5a9a,0x5abc,0x5abe,0x5acb,
+ 0x5ac2,0x5abd,0x5ae3,0x5ad7,0x5ae6,0x5ae9,0x5ad6,0x5afa,0x5afb,0x5b0c,
+ 0x5b0b,0x5b16,0x5b32,0x5ad0,0x5b2a,0x5b36,0x5b3e,0x5b43,0x5b45,0x5b40,
+ 0x5b51,0x5b55,0x5b5a,0x5b5b,0x5b65,0x5b69,0x5b70,0x5b73,0x5b75,0x5b78,
+ 0x6588,0x5b7a,0x5b80,0x5b83,0x5ba6,0x5bb8,0x5bc3,0x5bc7,0x5bc9,0x5bd4,
+ 0x5bd0,0x5be4,0x5be6,0x5be2,0x5bde,0x5be5,0x5beb,0x5bf0,0x5bf6,0x5bf3,
+ 0x5c05,0x5c07,0x5c08,0x5c0d,0x5c13,0x5c20,0x5c22,0x5c28,0x5c38,0x5c39,
+ 0x5c41,0x5c46,0x5c4e,0x5c53
+ },
+ { /* ku 36 */
+ 0x5c50,0x5c4f,0x5b71,0x5c6c,0x5c6e,0x4e62,0x5c76,0x5c79,0x5c8c,0x5c91,
+ 0x5c94,0x599b,0x5cab,0x5cbb,0x5cb6,0x5cbc,0x5cb7,0x5cc5,0x5cbe,0x5cc7,
+ 0x5cd9,0x5ce9,0x5cfd,0x5cfa,0x5ced,0x5d8c,0x5cea,0x5d0b,0x5d15,0x5d17,
+ 0x5d5c,0x5d1f,0x5d1b,0x5d11,0x5d14,0x5d22,0x5d1a,0x5d19,0x5d18,0x5d4c,
+ 0x5d52,0x5d4e,0x5d4b,0x5d6c,0x5d73,0x5d76,0x5d87,0x5d84,0x5d82,0x5da2,
+ 0x5d9d,0x5dac,0x5dae,0x5dbd,0x5d90,0x5db7,0x5dbc,0x5dc9,0x5dcd,0x5dd3,
+ 0x5dd2,0x5dd6,0x5ddb,0x5deb,0x5df2,0x5df5,0x5e0b,0x5e1a,0x5e19,0x5e11,
+ 0x5e1b,0x5e36,0x5e37,0x5e44,0x5e43,0x5e40,0x5e4e,0x5e57,0x5e54,0x5e5f,
+ 0x5e62,0x5e64,0x5e47,0x5e75,0x5e76,0x5e7a,0x9ebc,0x5e7f,0x5ea0,0x5ec1,
+ 0x5ec2,0x5ec8,0x5ed0,0x5ecf
+ },
+ { /* ku 37 */
+ 0x5ed6,0x5ee3,0x5edd,0x5eda,0x5edb,0x5ee2,0x5ee1,0x5ee8,0x5ee9,0x5eec,
+ 0x5ef1,0x5ef3,0x5ef0,0x5ef4,0x5ef8,0x5efe,0x5f03,0x5f09,0x5f5d,0x5f5c,
+ 0x5f0b,0x5f11,0x5f16,0x5f29,0x5f2d,0x5f38,0x5f41,0x5f48,0x5f4c,0x5f4e,
+ 0x5f2f,0x5f51,0x5f56,0x5f57,0x5f59,0x5f61,0x5f6d,0x5f73,0x5f77,0x5f83,
+ 0x5f82,0x5f7f,0x5f8a,0x5f88,0x5f91,0x5f87,0x5f9e,0x5f99,0x5f98,0x5fa0,
+ 0x5fa8,0x5fad,0x5fbc,0x5fd6,0x5ffb,0x5fe4,0x5ff8,0x5ff1,0x5fdd,0x60b3,
+ 0x5fff,0x6021,0x6060,0x6019,0x6010,0x6029,0x600e,0x6031,0x601b,0x6015,
+ 0x602b,0x6026,0x600f,0x603a,0x605a,0x6041,0x606a,0x6077,0x605f,0x604a,
+ 0x6046,0x604d,0x6063,0x6043,0x6064,0x6042,0x606c,0x606b,0x6059,0x6081,
+ 0x608d,0x60e7,0x6083,0x609a
+ },
+ { /* ku 38 */
+ 0x6084,0x609b,0x6096,0x6097,0x6092,0x60a7,0x608b,0x60e1,0x60b8,0x60e0,
+ 0x60d3,0x60b4,0x5ff0,0x60bd,0x60c6,0x60b5,0x60d8,0x614d,0x6115,0x6106,
+ 0x60f6,0x60f7,0x6100,0x60f4,0x60fa,0x6103,0x6121,0x60fb,0x60f1,0x610d,
+ 0x610e,0x6147,0x613e,0x6128,0x6127,0x614a,0x613f,0x613c,0x612c,0x6134,
+ 0x613d,0x6142,0x6144,0x6173,0x6177,0x6158,0x6159,0x615a,0x616b,0x6174,
+ 0x616f,0x6165,0x6171,0x615f,0x615d,0x6153,0x6175,0x6199,0x6196,0x6187,
+ 0x61ac,0x6194,0x619a,0x618a,0x6191,0x61ab,0x61ae,0x61cc,0x61ca,0x61c9,
+ 0x61f7,0x61c8,0x61c3,0x61c6,0x61ba,0x61cb,0x7f79,0x61cd,0x61e6,0x61e3,
+ 0x61f6,0x61fa,0x61f4,0x61ff,0x61fd,0x61fc,0x61fe,0x6200,0x6208,0x6209,
+ 0x620d,0x620c,0x6214,0x621b
+ },
+ { /* ku 39 */
+ 0x621e,0x6221,0x622a,0x622e,0x6230,0x6232,0x6233,0x6241,0x624e,0x625e,
+ 0x6263,0x625b,0x6260,0x6268,0x627c,0x6282,0x6289,0x627e,0x6292,0x6293,
+ 0x6296,0x62d4,0x6283,0x6294,0x62d7,0x62d1,0x62bb,0x62cf,0x62ff,0x62c6,
+ 0x64d4,0x62c8,0x62dc,0x62cc,0x62ca,0x62c2,0x62c7,0x629b,0x62c9,0x630c,
+ 0x62ee,0x62f1,0x6327,0x6302,0x6308,0x62ef,0x62f5,0x6350,0x633e,0x634d,
+ 0x641c,0x634f,0x6396,0x638e,0x6380,0x63ab,0x6376,0x63a3,0x638f,0x6389,
+ 0x639f,0x63b5,0x636b,0x6369,0x63be,0x63e9,0x63c0,0x63c6,0x63e3,0x63c9,
+ 0x63d2,0x63f6,0x63c4,0x6416,0x6434,0x6406,0x6413,0x6426,0x6436,0x651d,
+ 0x6417,0x6428,0x640f,0x6467,0x646f,0x6476,0x644e,0x652a,0x6495,0x6493,
+ 0x64a5,0x64a9,0x6488,0x64bc
+ },
+ { /* ku 3a */
+ 0x64da,0x64d2,0x64c5,0x64c7,0x64bb,0x64d8,0x64c2,0x64f1,0x64e7,0x8209,
+ 0x64e0,0x64e1,0x62ac,0x64e3,0x64ef,0x652c,0x64f6,0x64f4,0x64f2,0x64fa,
+ 0x6500,0x64fd,0x6518,0x651c,0x6505,0x6524,0x6523,0x652b,0x6534,0x6535,
+ 0x6537,0x6536,0x6538,0x754b,0x6548,0x6556,0x6555,0x654d,0x6558,0x655e,
+ 0x655d,0x6572,0x6578,0x6582,0x6583,0x8b8a,0x659b,0x659f,0x65ab,0x65b7,
+ 0x65c3,0x65c6,0x65c1,0x65c4,0x65cc,0x65d2,0x65db,0x65d9,0x65e0,0x65e1,
+ 0x65f1,0x6772,0x660a,0x6603,0x65fb,0x6773,0x6635,0x6636,0x6634,0x661c,
+ 0x664f,0x6644,0x6649,0x6641,0x665e,0x665d,0x6664,0x6667,0x6668,0x665f,
+ 0x6662,0x6670,0x6683,0x6688,0x668e,0x6689,0x6684,0x6698,0x669d,0x66c1,
+ 0x66b9,0x66c9,0x66be,0x66bc
+ },
+ { /* ku 3b */
+ 0x66c4,0x66b8,0x66d6,0x66da,0x66e0,0x663f,0x66e6,0x66e9,0x66f0,0x66f5,
+ 0x66f7,0x670f,0x6716,0x671e,0x6726,0x6727,0x9738,0x672e,0x673f,0x6736,
+ 0x6741,0x6738,0x6737,0x6746,0x675e,0x6760,0x6759,0x6763,0x6764,0x6789,
+ 0x6770,0x67a9,0x677c,0x676a,0x678c,0x678b,0x67a6,0x67a1,0x6785,0x67b7,
+ 0x67ef,0x67b4,0x67ec,0x67b3,0x67e9,0x67b8,0x67e4,0x67de,0x67dd,0x67e2,
+ 0x67ee,0x67b9,0x67ce,0x67c6,0x67e7,0x6a9c,0x681e,0x6846,0x6829,0x6840,
+ 0x684d,0x6832,0x684e,0x68b3,0x682b,0x6859,0x6863,0x6877,0x687f,0x689f,
+ 0x688f,0x68ad,0x6894,0x689d,0x689b,0x6883,0x6aae,0x68b9,0x6874,0x68b5,
+ 0x68a0,0x68ba,0x690f,0x688d,0x687e,0x6901,0x68ca,0x6908,0x68d8,0x6922,
+ 0x6926,0x68e1,0x690c,0x68cd
+ },
+ { /* ku 3c */
+ 0x68d4,0x68e7,0x68d5,0x6936,0x6912,0x6904,0x68d7,0x68e3,0x6925,0x68f9,
+ 0x68e0,0x68ef,0x6928,0x692a,0x691a,0x6923,0x6921,0x68c6,0x6979,0x6977,
+ 0x695c,0x6978,0x696b,0x6954,0x697e,0x696e,0x6939,0x6974,0x693d,0x6959,
+ 0x6930,0x6961,0x695e,0x695d,0x6981,0x696a,0x69b2,0x69ae,0x69d0,0x69bf,
+ 0x69c1,0x69d3,0x69be,0x69ce,0x5be8,0x69ca,0x69dd,0x69bb,0x69c3,0x69a7,
+ 0x6a2e,0x6991,0x69a0,0x699c,0x6995,0x69b4,0x69de,0x69e8,0x6a02,0x6a1b,
+ 0x69ff,0x6b0a,0x69f9,0x69f2,0x69e7,0x6a05,0x69b1,0x6a1e,0x69ed,0x6a14,
+ 0x69eb,0x6a0a,0x6a12,0x6ac1,0x6a23,0x6a13,0x6a44,0x6a0c,0x6a72,0x6a36,
+ 0x6a78,0x6a47,0x6a62,0x6a59,0x6a66,0x6a48,0x6a38,0x6a22,0x6a90,0x6a8d,
+ 0x6aa0,0x6a84,0x6aa2,0x6aa3
+ },
+ { /* ku 3d */
+ 0x6a97,0x8617,0x6abb,0x6ac3,0x6ac2,0x6ab8,0x6ab3,0x6aac,0x6ade,0x6ad1,
+ 0x6adf,0x6aaa,0x6ada,0x6aea,0x6afb,0x6b05,0x8616,0x6afa,0x6b12,0x6b16,
+ 0x9b31,0x6b1f,0x6b38,0x6b37,0x76dc,0x6b39,0x98ee,0x6b47,0x6b43,0x6b49,
+ 0x6b50,0x6b59,0x6b54,0x6b5b,0x6b5f,0x6b61,0x6b78,0x6b79,0x6b7f,0x6b80,
+ 0x6b84,0x6b83,0x6b8d,0x6b98,0x6b95,0x6b9e,0x6ba4,0x6baa,0x6bab,0x6baf,
+ 0x6bb2,0x6bb1,0x6bb3,0x6bb7,0x6bbc,0x6bc6,0x6bcb,0x6bd3,0x6bdf,0x6bec,
+ 0x6beb,0x6bf3,0x6bef,0x9ebe,0x6c08,0x6c13,0x6c14,0x6c1b,0x6c24,0x6c23,
+ 0x6c5e,0x6c55,0x6c62,0x6c6a,0x6c82,0x6c8d,0x6c9a,0x6c81,0x6c9b,0x6c7e,
+ 0x6c68,0x6c73,0x6c92,0x6c90,0x6cc4,0x6cf1,0x6cd3,0x6cbd,0x6cd7,0x6cc5,
+ 0x6cdd,0x6cae,0x6cb1,0x6cbe
+ },
+ { /* ku 3e */
+ 0x6cba,0x6cdb,0x6cef,0x6cd9,0x6cea,0x6d1f,0x884d,0x6d36,0x6d2b,0x6d3d,
+ 0x6d38,0x6d19,0x6d35,0x6d33,0x6d12,0x6d0c,0x6d63,0x6d93,0x6d64,0x6d5a,
+ 0x6d79,0x6d59,0x6d8e,0x6d95,0x6fe4,0x6d85,0x6df9,0x6e15,0x6e0a,0x6db5,
+ 0x6dc7,0x6de6,0x6db8,0x6dc6,0x6dec,0x6dde,0x6dcc,0x6de8,0x6dd2,0x6dc5,
+ 0x6dfa,0x6dd9,0x6de4,0x6dd5,0x6dea,0x6dee,0x6e2d,0x6e6e,0x6e2e,0x6e19,
+ 0x6e72,0x6e5f,0x6e3e,0x6e23,0x6e6b,0x6e2b,0x6e76,0x6e4d,0x6e1f,0x6e43,
+ 0x6e3a,0x6e4e,0x6e24,0x6eff,0x6e1d,0x6e38,0x6e82,0x6eaa,0x6e98,0x6ec9,
+ 0x6eb7,0x6ed3,0x6ebd,0x6eaf,0x6ec4,0x6eb2,0x6ed4,0x6ed5,0x6e8f,0x6ea5,
+ 0x6ec2,0x6e9f,0x6f41,0x6f11,0x704c,0x6eec,0x6ef8,0x6efe,0x6f3f,0x6ef2,
+ 0x6f31,0x6eef,0x6f32,0x6ecc
+ },
+ { /* ku 3f */
+ 0x6f3e,0x6f13,0x6ef7,0x6f86,0x6f7a,0x6f78,0x6f81,0x6f80,0x6f6f,0x6f5b,
+ 0x6ff3,0x6f6d,0x6f82,0x6f7c,0x6f58,0x6f8e,0x6f91,0x6fc2,0x6f66,0x6fb3,
+ 0x6fa3,0x6fa1,0x6fa4,0x6fb9,0x6fc6,0x6faa,0x6fdf,0x6fd5,0x6fec,0x6fd4,
+ 0x6fd8,0x6ff1,0x6fee,0x6fdb,0x7009,0x700b,0x6ffa,0x7011,0x7001,0x700f,
+ 0x6ffe,0x701b,0x701a,0x6f74,0x701d,0x7018,0x701f,0x7030,0x703e,0x7032,
+ 0x7051,0x7063,0x7099,0x7092,0x70af,0x70f1,0x70ac,0x70b8,0x70b3,0x70ae,
+ 0x70df,0x70cb,0x70dd,0x70d9,0x7109,0x70fd,0x711c,0x7119,0x7165,0x7155,
+ 0x7188,0x7166,0x7162,0x714c,0x7156,0x716c,0x718f,0x71fb,0x7184,0x7195,
+ 0x71a8,0x71ac,0x71d7,0x71b9,0x71be,0x71d2,0x71c9,0x71d4,0x71ce,0x71e0,
+ 0x71ec,0x71e7,0x71f5,0x71fc
+ },
+ { /* ku 40 */
+ 0x71f9,0x71ff,0x720d,0x7210,0x721b,0x7228,0x722d,0x722c,0x7230,0x7232,
+ 0x723b,0x723c,0x723f,0x7240,0x7246,0x724b,0x7258,0x7274,0x727e,0x7282,
+ 0x7281,0x7287,0x7292,0x7296,0x72a2,0x72a7,0x72b9,0x72b2,0x72c3,0x72c6,
+ 0x72c4,0x72ce,0x72d2,0x72e2,0x72e0,0x72e1,0x72f9,0x72f7,0x500f,0x7317,
+ 0x730a,0x731c,0x7316,0x731d,0x7334,0x732f,0x7329,0x7325,0x733e,0x734e,
+ 0x734f,0x9ed8,0x7357,0x736a,0x7368,0x7370,0x7378,0x7375,0x737b,0x737a,
+ 0x73c8,0x73b3,0x73ce,0x73bb,0x73c0,0x73e5,0x73ee,0x73de,0x74a2,0x7405,
+ 0x746f,0x7425,0x73f8,0x7432,0x743a,0x7455,0x743f,0x745f,0x7459,0x7441,
+ 0x745c,0x7469,0x7470,0x7463,0x746a,0x7476,0x747e,0x748b,0x749e,0x74a7,
+ 0x74ca,0x74cf,0x74d4,0x73f1
+ },
+ { /* ku 41 */
+ 0x74e0,0x74e3,0x74e7,0x74e9,0x74ee,0x74f2,0x74f0,0x74f1,0x74f8,0x74f7,
+ 0x7504,0x7503,0x7505,0x750c,0x750e,0x750d,0x7515,0x7513,0x751e,0x7526,
+ 0x752c,0x753c,0x7544,0x754d,0x754a,0x7549,0x755b,0x7546,0x755a,0x7569,
+ 0x7564,0x7567,0x756b,0x756d,0x7578,0x7576,0x7586,0x7587,0x7574,0x758a,
+ 0x7589,0x7582,0x7594,0x759a,0x759d,0x75a5,0x75a3,0x75c2,0x75b3,0x75c3,
+ 0x75b5,0x75bd,0x75b8,0x75bc,0x75b1,0x75cd,0x75ca,0x75d2,0x75d9,0x75e3,
+ 0x75de,0x75fe,0x75ff,0x75fc,0x7601,0x75f0,0x75fa,0x75f2,0x75f3,0x760b,
+ 0x760d,0x7609,0x761f,0x7627,0x7620,0x7621,0x7622,0x7624,0x7634,0x7630,
+ 0x763b,0x7647,0x7648,0x7646,0x765c,0x7658,0x7661,0x7662,0x7668,0x7669,
+ 0x766a,0x7667,0x766c,0x7670
+ },
+ { /* ku 42 */
+ 0x7672,0x7676,0x7678,0x767c,0x7680,0x7683,0x7688,0x768b,0x768e,0x7696,
+ 0x7693,0x7699,0x769a,0x76b0,0x76b4,0x76b8,0x76b9,0x76ba,0x76c2,0x76cd,
+ 0x76d6,0x76d2,0x76de,0x76e1,0x76e5,0x76e7,0x76ea,0x862f,0x76fb,0x7708,
+ 0x7707,0x7704,0x7729,0x7724,0x771e,0x7725,0x7726,0x771b,0x7737,0x7738,
+ 0x7747,0x775a,0x7768,0x776b,0x775b,0x7765,0x777f,0x777e,0x7779,0x778e,
+ 0x778b,0x7791,0x77a0,0x779e,0x77b0,0x77b6,0x77b9,0x77bf,0x77bc,0x77bd,
+ 0x77bb,0x77c7,0x77cd,0x77d7,0x77da,0x77dc,0x77e3,0x77ee,0x77fc,0x780c,
+ 0x7812,0x7926,0x7820,0x792a,0x7845,0x788e,0x7874,0x7886,0x787c,0x789a,
+ 0x788c,0x78a3,0x78b5,0x78aa,0x78af,0x78d1,0x78c6,0x78cb,0x78d4,0x78be,
+ 0x78bc,0x78c5,0x78ca,0x78ec
+ },
+ { /* ku 43 */
+ 0x78e7,0x78da,0x78fd,0x78f4,0x7907,0x7912,0x7911,0x7919,0x792c,0x792b,
+ 0x7940,0x7960,0x7957,0x795f,0x795a,0x7955,0x7953,0x797a,0x797f,0x798a,
+ 0x799d,0x79a7,0x9f4b,0x79aa,0x79ae,0x79b3,0x79b9,0x79ba,0x79c9,0x79d5,
+ 0x79e7,0x79ec,0x79e1,0x79e3,0x7a08,0x7a0d,0x7a18,0x7a19,0x7a20,0x7a1f,
+ 0x7980,0x7a31,0x7a3b,0x7a3e,0x7a37,0x7a43,0x7a57,0x7a49,0x7a61,0x7a62,
+ 0x7a69,0x9f9d,0x7a70,0x7a79,0x7a7d,0x7a88,0x7a97,0x7a95,0x7a98,0x7a96,
+ 0x7aa9,0x7ac8,0x7ab0,0x7ab6,0x7ac5,0x7ac4,0x7abf,0x9083,0x7ac7,0x7aca,
+ 0x7acd,0x7acf,0x7ad5,0x7ad3,0x7ad9,0x7ada,0x7add,0x7ae1,0x7ae2,0x7ae6,
+ 0x7aed,0x7af0,0x7b02,0x7b0f,0x7b0a,0x7b06,0x7b33,0x7b18,0x7b19,0x7b1e,
+ 0x7b35,0x7b28,0x7b36,0x7b50
+ },
+ { /* ku 44 */
+ 0x7b7a,0x7b04,0x7b4d,0x7b0b,0x7b4c,0x7b45,0x7b75,0x7b65,0x7b74,0x7b67,
+ 0x7b70,0x7b71,0x7b6c,0x7b6e,0x7b9d,0x7b98,0x7b9f,0x7b8d,0x7b9c,0x7b9a,
+ 0x7b8b,0x7b92,0x7b8f,0x7b5d,0x7b99,0x7bcb,0x7bc1,0x7bcc,0x7bcf,0x7bb4,
+ 0x7bc6,0x7bdd,0x7be9,0x7c11,0x7c14,0x7be6,0x7be5,0x7c60,0x7c00,0x7c07,
+ 0x7c13,0x7bf3,0x7bf7,0x7c17,0x7c0d,0x7bf6,0x7c23,0x7c27,0x7c2a,0x7c1f,
+ 0x7c37,0x7c2b,0x7c3d,0x7c4c,0x7c43,0x7c54,0x7c4f,0x7c40,0x7c50,0x7c58,
+ 0x7c5f,0x7c64,0x7c56,0x7c65,0x7c6c,0x7c75,0x7c83,0x7c90,0x7ca4,0x7cad,
+ 0x7ca2,0x7cab,0x7ca1,0x7ca8,0x7cb3,0x7cb2,0x7cb1,0x7cae,0x7cb9,0x7cbd,
+ 0x7cc0,0x7cc5,0x7cc2,0x7cd8,0x7cd2,0x7cdc,0x7ce2,0x9b3b,0x7cef,0x7cf2,
+ 0x7cf4,0x7cf6,0x7cfa,0x7d06
+ },
+ { /* ku 45 */
+ 0x7d02,0x7d1c,0x7d15,0x7d0a,0x7d45,0x7d4b,0x7d2e,0x7d32,0x7d3f,0x7d35,
+ 0x7d46,0x7d73,0x7d56,0x7d4e,0x7d72,0x7d68,0x7d6e,0x7d4f,0x7d63,0x7d93,
+ 0x7d89,0x7d5b,0x7d8f,0x7d7d,0x7d9b,0x7dba,0x7dae,0x7da3,0x7db5,0x7dc7,
+ 0x7dbd,0x7dab,0x7e3d,0x7da2,0x7daf,0x7ddc,0x7db8,0x7d9f,0x7db0,0x7dd8,
+ 0x7ddd,0x7de4,0x7dde,0x7dfb,0x7df2,0x7de1,0x7e05,0x7e0a,0x7e23,0x7e21,
+ 0x7e12,0x7e31,0x7e1f,0x7e09,0x7e0b,0x7e22,0x7e46,0x7e66,0x7e3b,0x7e35,
+ 0x7e39,0x7e43,0x7e37,0x7e32,0x7e3a,0x7e67,0x7e5d,0x7e56,0x7e5e,0x7e59,
+ 0x7e5a,0x7e79,0x7e6a,0x7e69,0x7e7c,0x7e7b,0x7e83,0x7dd5,0x7e7d,0x8fae,
+ 0x7e7f,0x7e88,0x7e89,0x7e8c,0x7e92,0x7e90,0x7e93,0x7e94,0x7e96,0x7e8e,
+ 0x7e9b,0x7e9c,0x7f38,0x7f3a
+ },
+ { /* ku 46 */
+ 0x7f45,0x7f4c,0x7f4d,0x7f4e,0x7f50,0x7f51,0x7f55,0x7f54,0x7f58,0x7f5f,
+ 0x7f60,0x7f68,0x7f69,0x7f67,0x7f78,0x7f82,0x7f86,0x7f83,0x7f88,0x7f87,
+ 0x7f8c,0x7f94,0x7f9e,0x7f9d,0x7f9a,0x7fa3,0x7faf,0x7fb2,0x7fb9,0x7fae,
+ 0x7fb6,0x7fb8,0x8b71,0x7fc5,0x7fc6,0x7fca,0x7fd5,0x7fd4,0x7fe1,0x7fe6,
+ 0x7fe9,0x7ff3,0x7ff9,0x98dc,0x8006,0x8004,0x800b,0x8012,0x8018,0x8019,
+ 0x801c,0x8021,0x8028,0x803f,0x803b,0x804a,0x8046,0x8052,0x8058,0x805a,
+ 0x805f,0x8062,0x8068,0x8073,0x8072,0x8070,0x8076,0x8079,0x807d,0x807f,
+ 0x8084,0x8086,0x8085,0x809b,0x8093,0x809a,0x80ad,0x5190,0x80ac,0x80db,
+ 0x80e5,0x80d9,0x80dd,0x80c4,0x80da,0x80d6,0x8109,0x80ef,0x80f1,0x811b,
+ 0x8129,0x8123,0x812f,0x814b
+ },
+ { /* ku 47 */
+ 0x968b,0x8146,0x813e,0x8153,0x8151,0x80fc,0x8171,0x816e,0x8165,0x8166,
+ 0x8174,0x8183,0x8188,0x818a,0x8180,0x8182,0x81a0,0x8195,0x81a4,0x81a3,
+ 0x815f,0x8193,0x81a9,0x81b0,0x81b5,0x81be,0x81b8,0x81bd,0x81c0,0x81c2,
+ 0x81ba,0x81c9,0x81cd,0x81d1,0x81d9,0x81d8,0x81c8,0x81da,0x81df,0x81e0,
+ 0x81e7,0x81fa,0x81fb,0x81fe,0x8201,0x8202,0x8205,0x8207,0x820a,0x820d,
+ 0x8210,0x8216,0x8229,0x822b,0x8238,0x8233,0x8240,0x8259,0x8258,0x825d,
+ 0x825a,0x825f,0x8264,0x8262,0x8268,0x826a,0x826b,0x822e,0x8271,0x8277,
+ 0x8278,0x827e,0x828d,0x8292,0x82ab,0x829f,0x82bb,0x82ac,0x82e1,0x82e3,
+ 0x82df,0x82d2,0x82f4,0x82f3,0x82fa,0x8393,0x8303,0x82fb,0x82f9,0x82de,
+ 0x8306,0x82dc,0x8309,0x82d9
+ },
+ { /* ku 48 */
+ 0x8335,0x8334,0x8316,0x8332,0x8331,0x8340,0x8339,0x8350,0x8345,0x832f,
+ 0x832b,0x8317,0x8318,0x8385,0x839a,0x83aa,0x839f,0x83a2,0x8396,0x8323,
+ 0x838e,0x8387,0x838a,0x837c,0x83b5,0x8373,0x8375,0x83a0,0x8389,0x83a8,
+ 0x83f4,0x8413,0x83eb,0x83ce,0x83fd,0x8403,0x83d8,0x840b,0x83c1,0x83f7,
+ 0x8407,0x83e0,0x83f2,0x840d,0x8422,0x8420,0x83bd,0x8438,0x8506,0x83fb,
+ 0x846d,0x842a,0x843c,0x855a,0x8484,0x8477,0x846b,0x84ad,0x846e,0x8482,
+ 0x8469,0x8446,0x842c,0x846f,0x8479,0x8435,0x84ca,0x8462,0x84b9,0x84bf,
+ 0x849f,0x84d9,0x84cd,0x84bb,0x84da,0x84d0,0x84c1,0x84c6,0x84d6,0x84a1,
+ 0x8521,0x84ff,0x84f4,0x8517,0x8518,0x852c,0x851f,0x8515,0x8514,0x84fc,
+ 0x8540,0x8563,0x8558,0x8548
+ },
+ { /* ku 49 */
+ 0x8541,0x8602,0x854b,0x8555,0x8580,0x85a4,0x8588,0x8591,0x858a,0x85a8,
+ 0x856d,0x8594,0x859b,0x85ea,0x8587,0x859c,0x8577,0x857e,0x8590,0x85c9,
+ 0x85ba,0x85cf,0x85b9,0x85d0,0x85d5,0x85dd,0x85e5,0x85dc,0x85f9,0x860a,
+ 0x8613,0x860b,0x85fe,0x85fa,0x8606,0x8622,0x861a,0x8630,0x863f,0x864d,
+ 0x4e55,0x8654,0x865f,0x8667,0x8671,0x8693,0x86a3,0x86a9,0x86aa,0x868b,
+ 0x868c,0x86b6,0x86af,0x86c4,0x86c6,0x86b0,0x86c9,0x8823,0x86ab,0x86d4,
+ 0x86de,0x86e9,0x86ec,0x86df,0x86db,0x86ef,0x8712,0x8706,0x8708,0x8700,
+ 0x8703,0x86fb,0x8711,0x8709,0x870d,0x86f9,0x870a,0x8734,0x873f,0x8737,
+ 0x873b,0x8725,0x8729,0x871a,0x8760,0x875f,0x8778,0x874c,0x874e,0x8774,
+ 0x8757,0x8768,0x876e,0x8759
+ },
+ { /* ku 4a */
+ 0x8753,0x8763,0x876a,0x8805,0x87a2,0x879f,0x8782,0x87af,0x87cb,0x87bd,
+ 0x87c0,0x87d0,0x96d6,0x87ab,0x87c4,0x87b3,0x87c7,0x87c6,0x87bb,0x87ef,
+ 0x87f2,0x87e0,0x880f,0x880d,0x87fe,0x87f6,0x87f7,0x880e,0x87d2,0x8811,
+ 0x8816,0x8815,0x8822,0x8821,0x8831,0x8836,0x8839,0x8827,0x883b,0x8844,
+ 0x8842,0x8852,0x8859,0x885e,0x8862,0x886b,0x8881,0x887e,0x889e,0x8875,
+ 0x887d,0x88b5,0x8872,0x8882,0x8897,0x8892,0x88ae,0x8899,0x88a2,0x888d,
+ 0x88a4,0x88b0,0x88bf,0x88b1,0x88c3,0x88c4,0x88d4,0x88d8,0x88d9,0x88dd,
+ 0x88f9,0x8902,0x88fc,0x88f4,0x88e8,0x88f2,0x8904,0x890c,0x890a,0x8913,
+ 0x8943,0x891e,0x8925,0x892a,0x892b,0x8941,0x8944,0x893b,0x8936,0x8938,
+ 0x894c,0x891d,0x8960,0x895e
+ },
+ { /* ku 4b */
+ 0x8966,0x8964,0x896d,0x896a,0x896f,0x8974,0x8977,0x897e,0x8983,0x8988,
+ 0x898a,0x8993,0x8998,0x89a1,0x89a9,0x89a6,0x89ac,0x89af,0x89b2,0x89ba,
+ 0x89bd,0x89bf,0x89c0,0x89da,0x89dc,0x89dd,0x89e7,0x89f4,0x89f8,0x8a03,
+ 0x8a16,0x8a10,0x8a0c,0x8a1b,0x8a1d,0x8a25,0x8a36,0x8a41,0x8a5b,0x8a52,
+ 0x8a46,0x8a48,0x8a7c,0x8a6d,0x8a6c,0x8a62,0x8a85,0x8a82,0x8a84,0x8aa8,
+ 0x8aa1,0x8a91,0x8aa5,0x8aa6,0x8a9a,0x8aa3,0x8ac4,0x8acd,0x8ac2,0x8ada,
+ 0x8aeb,0x8af3,0x8ae7,0x8ae4,0x8af1,0x8b14,0x8ae0,0x8ae2,0x8af7,0x8ade,
+ 0x8adb,0x8b0c,0x8b07,0x8b1a,0x8ae1,0x8b16,0x8b10,0x8b17,0x8b20,0x8b33,
+ 0x97ab,0x8b26,0x8b2b,0x8b3e,0x8b28,0x8b41,0x8b4c,0x8b4f,0x8b4e,0x8b49,
+ 0x8b56,0x8b5b,0x8b5a,0x8b6b
+ },
+ { /* ku 4c */
+ 0x8b5f,0x8b6c,0x8b6f,0x8b74,0x8b7d,0x8b80,0x8b8c,0x8b8e,0x8b92,0x8b93,
+ 0x8b96,0x8b99,0x8b9a,0x8c3a,0x8c41,0x8c3f,0x8c48,0x8c4c,0x8c4e,0x8c50,
+ 0x8c55,0x8c62,0x8c6c,0x8c78,0x8c7a,0x8c82,0x8c89,0x8c85,0x8c8a,0x8c8d,
+ 0x8c8e,0x8c94,0x8c7c,0x8c98,0x621d,0x8cad,0x8caa,0x8cbd,0x8cb2,0x8cb3,
+ 0x8cae,0x8cb6,0x8cc8,0x8cc1,0x8ce4,0x8ce3,0x8cda,0x8cfd,0x8cfa,0x8cfb,
+ 0x8d04,0x8d05,0x8d0a,0x8d07,0x8d0f,0x8d0d,0x8d10,0x9f4e,0x8d13,0x8ccd,
+ 0x8d14,0x8d16,0x8d67,0x8d6d,0x8d71,0x8d73,0x8d81,0x8d99,0x8dc2,0x8dbe,
+ 0x8dba,0x8dcf,0x8dda,0x8dd6,0x8dcc,0x8ddb,0x8dcb,0x8dea,0x8deb,0x8ddf,
+ 0x8de3,0x8dfc,0x8e08,0x8e09,0x8dff,0x8e1d,0x8e1e,0x8e10,0x8e1f,0x8e42,
+ 0x8e35,0x8e30,0x8e34,0x8e4a
+ },
+ { /* ku 4d */
+ 0x8e47,0x8e49,0x8e4c,0x8e50,0x8e48,0x8e59,0x8e64,0x8e60,0x8e2a,0x8e63,
+ 0x8e55,0x8e76,0x8e72,0x8e7c,0x8e81,0x8e87,0x8e85,0x8e84,0x8e8b,0x8e8a,
+ 0x8e93,0x8e91,0x8e94,0x8e99,0x8eaa,0x8ea1,0x8eac,0x8eb0,0x8ec6,0x8eb1,
+ 0x8ebe,0x8ec5,0x8ec8,0x8ecb,0x8edb,0x8ee3,0x8efc,0x8efb,0x8eeb,0x8efe,
+ 0x8f0a,0x8f05,0x8f15,0x8f12,0x8f19,0x8f13,0x8f1c,0x8f1f,0x8f1b,0x8f0c,
+ 0x8f26,0x8f33,0x8f3b,0x8f39,0x8f45,0x8f42,0x8f3e,0x8f4c,0x8f49,0x8f46,
+ 0x8f4e,0x8f57,0x8f5c,0x8f62,0x8f63,0x8f64,0x8f9c,0x8f9f,0x8fa3,0x8fad,
+ 0x8faf,0x8fb7,0x8fda,0x8fe5,0x8fe2,0x8fea,0x8fef,0x9087,0x8ff4,0x9005,
+ 0x8ff9,0x8ffa,0x9011,0x9015,0x9021,0x900d,0x901e,0x9016,0x900b,0x9027,
+ 0x9036,0x9035,0x9039,0x8ff8
+ },
+ { /* ku 4e */
+ 0x904f,0x9050,0x9051,0x9052,0x900e,0x9049,0x903e,0x9056,0x9058,0x905e,
+ 0x9068,0x906f,0x9076,0x96a8,0x9072,0x9082,0x907d,0x9081,0x9080,0x908a,
+ 0x9089,0x908f,0x90a8,0x90af,0x90b1,0x90b5,0x90e2,0x90e4,0x6248,0x90db,
+ 0x9102,0x9112,0x9119,0x9132,0x9130,0x914a,0x9156,0x9158,0x9163,0x9165,
+ 0x9169,0x9173,0x9172,0x918b,0x9189,0x9182,0x91a2,0x91ab,0x91af,0x91aa,
+ 0x91b5,0x91b4,0x91ba,0x91c0,0x91c1,0x91c9,0x91cb,0x91d0,0x91d6,0x91df,
+ 0x91e1,0x91db,0x91fc,0x91f5,0x91f6,0x921e,0x91ff,0x9214,0x922c,0x9215,
+ 0x9211,0x925e,0x9257,0x9245,0x9249,0x9264,0x9248,0x9295,0x923f,0x924b,
+ 0x9250,0x929c,0x9296,0x9293,0x929b,0x925a,0x92cf,0x92b9,0x92b7,0x92e9,
+ 0x930f,0x92fa,0x9344,0x932e
+ },
+ { /* ku 4f */
+ 0x9319,0x9322,0x931a,0x9323,0x933a,0x9335,0x933b,0x935c,0x9360,0x937c,
+ 0x936e,0x9356,0x93b0,0x93ac,0x93ad,0x9394,0x93b9,0x93d6,0x93d7,0x93e8,
+ 0x93e5,0x93d8,0x93c3,0x93dd,0x93d0,0x93c8,0x93e4,0x941a,0x9414,0x9413,
+ 0x9403,0x9407,0x9410,0x9436,0x942b,0x9435,0x9421,0x943a,0x9441,0x9452,
+ 0x9444,0x945b,0x9460,0x9462,0x945e,0x946a,0x9229,0x9470,0x9475,0x9477,
+ 0x947d,0x945a,0x947c,0x947e,0x9481,0x947f,0x9582,0x9587,0x958a,0x9594,
+ 0x9596,0x9598,0x9599,0x95a0,0x95a8,0x95a7,0x95ad,0x95bc,0x95bb,0x95b9,
+ 0x95be,0x95ca,0x6ff6,0x95c3,0x95cd,0x95cc,0x95d5,0x95d4,0x95d6,0x95dc,
+ 0x95e1,0x95e5,0x95e2,0x9621,0x9628,0x962e,0x962f,0x9642,0x964c,0x964f,
+ 0x964b,0x9677,0x965c,0x965e
+ },
+ { /* ku 50 */
+ 0x965d,0x965f,0x9666,0x9672,0x966c,0x968d,0x9698,0x9695,0x9697,0x96aa,
+ 0x96a7,0x96b1,0x96b2,0x96b0,0x96b4,0x96b6,0x96b8,0x96b9,0x96ce,0x96cb,
+ 0x96c9,0x96cd,0x894d,0x96dc,0x970d,0x96d5,0x96f9,0x9704,0x9706,0x9708,
+ 0x9713,0x970e,0x9711,0x970f,0x9716,0x9719,0x9724,0x972a,0x9730,0x9739,
+ 0x973d,0x973e,0x9744,0x9746,0x9748,0x9742,0x9749,0x975c,0x9760,0x9764,
+ 0x9766,0x9768,0x52d2,0x976b,0x9771,0x9779,0x9785,0x977c,0x9781,0x977a,
+ 0x9786,0x978b,0x978f,0x9790,0x979c,0x97a8,0x97a6,0x97a3,0x97b3,0x97b4,
+ 0x97c3,0x97c6,0x97c8,0x97cb,0x97dc,0x97ed,0x9f4f,0x97f2,0x7adf,0x97f6,
+ 0x97f5,0x980f,0x980c,0x9838,0x9824,0x9821,0x9837,0x983d,0x9846,0x984f,
+ 0x984b,0x986b,0x986f,0x9870
+ },
+ { /* ku 51 */
+ 0x9871,0x9874,0x9873,0x98aa,0x98af,0x98b1,0x98b6,0x98c4,0x98c3,0x98c6,
+ 0x98e9,0x98eb,0x9903,0x9909,0x9912,0x9914,0x9918,0x9921,0x991d,0x991e,
+ 0x9924,0x9920,0x992c,0x992e,0x993d,0x993e,0x9942,0x9949,0x9945,0x9950,
+ 0x994b,0x9951,0x9952,0x994c,0x9955,0x9997,0x9998,0x99a5,0x99ad,0x99ae,
+ 0x99bc,0x99df,0x99db,0x99dd,0x99d8,0x99d1,0x99ed,0x99ee,0x99f1,0x99f2,
+ 0x99fb,0x99f8,0x9a01,0x9a0f,0x9a05,0x99e2,0x9a19,0x9a2b,0x9a37,0x9a45,
+ 0x9a42,0x9a40,0x9a43,0x9a3e,0x9a55,0x9a4d,0x9a5b,0x9a57,0x9a5f,0x9a62,
+ 0x9a65,0x9a64,0x9a69,0x9a6b,0x9a6a,0x9aad,0x9ab0,0x9abc,0x9ac0,0x9acf,
+ 0x9ad1,0x9ad3,0x9ad4,0x9ade,0x9adf,0x9ae2,0x9ae3,0x9ae6,0x9aef,0x9aeb,
+ 0x9aee,0x9af4,0x9af1,0x9af7
+ },
+ { /* ku 52 */
+ 0x9afb,0x9b06,0x9b18,0x9b1a,0x9b1f,0x9b22,0x9b23,0x9b25,0x9b27,0x9b28,
+ 0x9b29,0x9b2a,0x9b2e,0x9b2f,0x9b32,0x9b44,0x9b43,0x9b4f,0x9b4d,0x9b4e,
+ 0x9b51,0x9b58,0x9b74,0x9b93,0x9b83,0x9b91,0x9b96,0x9b97,0x9b9f,0x9ba0,
+ 0x9ba8,0x9bb4,0x9bc0,0x9bca,0x9bb9,0x9bc6,0x9bcf,0x9bd1,0x9bd2,0x9be3,
+ 0x9be2,0x9be4,0x9bd4,0x9be1,0x9c3a,0x9bf2,0x9bf1,0x9bf0,0x9c15,0x9c14,
+ 0x9c09,0x9c13,0x9c0c,0x9c06,0x9c08,0x9c12,0x9c0a,0x9c04,0x9c2e,0x9c1b,
+ 0x9c25,0x9c24,0x9c21,0x9c30,0x9c47,0x9c32,0x9c46,0x9c3e,0x9c5a,0x9c60,
+ 0x9c67,0x9c76,0x9c78,0x9ce7,0x9cec,0x9cf0,0x9d09,0x9d08,0x9ceb,0x9d03,
+ 0x9d06,0x9d2a,0x9d26,0x9daf,0x9d23,0x9d1f,0x9d44,0x9d15,0x9d12,0x9d41,
+ 0x9d3f,0x9d3e,0x9d46,0x9d48
+ },
+ { /* ku 53 */
+ 0x9d5d,0x9d5e,0x9d64,0x9d51,0x9d50,0x9d59,0x9d72,0x9d89,0x9d87,0x9dab,
+ 0x9d6f,0x9d7a,0x9d9a,0x9da4,0x9da9,0x9db2,0x9dc4,0x9dc1,0x9dbb,0x9db8,
+ 0x9dba,0x9dc6,0x9dcf,0x9dc2,0x9dd9,0x9dd3,0x9df8,0x9de6,0x9ded,0x9def,
+ 0x9dfd,0x9e1a,0x9e1b,0x9e1e,0x9e75,0x9e79,0x9e7d,0x9e81,0x9e88,0x9e8b,
+ 0x9e8c,0x9e92,0x9e95,0x9e91,0x9e9d,0x9ea5,0x9ea9,0x9eb8,0x9eaa,0x9ead,
+ 0x9761,0x9ecc,0x9ece,0x9ecf,0x9ed0,0x9ed4,0x9edc,0x9ede,0x9edd,0x9ee0,
+ 0x9ee5,0x9ee8,0x9eef,0x9ef4,0x9ef6,0x9ef7,0x9ef9,0x9efb,0x9efc,0x9efd,
+ 0x9f07,0x9f08,0x76b7,0x9f15,0x9f21,0x9f2c,0x9f3e,0x9f4a,0x9f52,0x9f54,
+ 0x9f63,0x9f5f,0x9f60,0x9f61,0x9f66,0x9f67,0x9f6c,0x9f6a,0x9f77,0x9f72,
+ 0x9f76,0x9f95,0x9f9c,0x9fa0
+ },
+ { /* ku 54 */
+ 0x582f,0x69c7,0x9059,0x7464,0x51dc,0x7199,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ }
+};
diff --git a/imap/src/charset/jis_0212.c b/imap/src/charset/jis_0212.c
new file mode 100644
index 00000000..d47d8a42
--- /dev/null
+++ b/imap/src/charset/jis_0212.c
@@ -0,0 +1,962 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: JIS X0212 conversion table
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 4 August 1997
+ * Last Edited: 30 August 2006
+ */
+
+/* JIS X0212 is the supplemental industrial standard of Japan. */
+
+#define BASE_JIS0212_KU 0x22
+#define BASE_JIS0212_TEN 0x21
+#define MAX_JIS0212_KU 76
+#define MAX_JIS0212_TEN 94
+
+
+#define JIS0212TOUNICODE(c,c1,ku,ten) \
+ ((((ku = (c & 0x7f) - BASE_JIS0212_KU) < MAX_JIS0212_KU) && \
+ ((ten = (c1 & 0x7f) - BASE_JIS0212_TEN) < MAX_JIS0212_TEN)) ? \
+ jis0212tab[ku][ten] : UBOGON)
+
+
+static const unsigned short jis0212tab[MAX_JIS0212_KU][MAX_JIS0212_TEN] = {
+ { /* ku 02 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x02d8,0x02c7,0x00b8,0x02d9,0x02dd,0x00af,
+ 0x02db,0x02da,0x007e,0x0384,0x0385,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x00a1,0x00a6,0x00bf,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x00ba,0x00aa,0x00a9,0x00ae,0x2122,0x00a4,
+ 0x2116,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 03 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 04 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 05 */
+ /* Note: ku/ten codepoints 05/87 - 05/90 are proposed for addition to
+ * JIS X 0212; I don't know if they've been formally accepted yet.
+ * They represent katakana VA, VI, VE, and VO, and are in the BMP but
+ * not in Unicode's JIS conversion tables. They're useful enough that
+ * I decided to put them here.
+ */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x30f7,0x30f8,0x30f9,0x30fa,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 06 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x0386,0x0388,0x0389,0x038a,0x03aa,UBOGON,
+ 0x038c,UBOGON,0x038e,0x03ab,UBOGON,0x038f,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x03ac,0x03ad,0x03ae,0x03af,0x03ca,0x0390,0x03cc,0x03c2,0x03cd,0x03cb,
+ 0x03b0,0x03ce,UBOGON,UBOGON
+ },
+ { /* ku 07 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,0x0402,0x0403,0x0404,0x0405,0x0406,0x0407,0x0408,
+ 0x0409,0x040a,0x040b,0x040c,0x040e,0x040f,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x0452,0x0453,0x0454,0x0455,0x0456,0x0457,0x0458,0x0459,0x045a,
+ 0x045b,0x045c,0x045e,0x045f
+ },
+ { /* ku 08 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 09 */
+ 0x00c6,0x0110,UBOGON,0x0126,UBOGON,0x0132,UBOGON,0x0141,0x013f,UBOGON,
+ 0x014a,0x00d8,0x0152,UBOGON,0x0166,0x00de,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0x00e6,0x0111,0x00f0,0x0127,0x0131,0x0133,0x0138,0x0142,
+ 0x0140,0x0149,0x014b,0x00f8,0x0153,0x00df,0x0167,0x00fe,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0a */
+ 0x00c1,0x00c0,0x00c4,0x00c2,0x0102,0x01cd,0x0100,0x0104,0x00c5,0x00c3,
+ 0x0106,0x0108,0x010c,0x00c7,0x010a,0x010e,0x00c9,0x00c8,0x00cb,0x00ca,
+ 0x011a,0x0116,0x0112,0x0118,UBOGON,0x011c,0x011e,0x0122,0x0120,0x0124,
+ 0x00cd,0x00cc,0x00cf,0x00ce,0x01cf,0x0130,0x012a,0x012e,0x0128,0x0134,
+ 0x0136,0x0139,0x013d,0x013b,0x0143,0x0147,0x0145,0x00d1,0x00d3,0x00d2,
+ 0x00d6,0x00d4,0x01d1,0x0150,0x014c,0x00d5,0x0154,0x0158,0x0156,0x015a,
+ 0x015c,0x0160,0x015e,0x0164,0x0162,0x00da,0x00d9,0x00dc,0x00db,0x016c,
+ 0x01d3,0x0170,0x016a,0x0172,0x016e,0x0168,0x01d7,0x01db,0x01d9,0x01d5,
+ 0x0174,0x00dd,0x0178,0x0176,0x0179,0x017d,0x017b,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0b */
+ 0x00e1,0x00e0,0x00e4,0x00e2,0x0103,0x01ce,0x0101,0x0105,0x00e5,0x00e3,
+ 0x0107,0x0109,0x010d,0x00e7,0x010b,0x010f,0x00e9,0x00e8,0x00eb,0x00ea,
+ 0x011b,0x0117,0x0113,0x0119,0x01f5,0x011d,0x011f,UBOGON,0x0121,0x0125,
+ 0x00ed,0x00ec,0x00ef,0x00ee,0x01d0,UBOGON,0x012b,0x012f,0x0129,0x0135,
+ 0x0137,0x013a,0x013e,0x013c,0x0144,0x0148,0x0146,0x00f1,0x00f3,0x00f2,
+ 0x00f6,0x00f4,0x01d2,0x0151,0x014d,0x00f5,0x0155,0x0159,0x0157,0x015b,
+ 0x015d,0x0161,0x015f,0x0165,0x0163,0x00fa,0x00f9,0x00fc,0x00fb,0x016d,
+ 0x01d4,0x0171,0x016b,0x0173,0x016f,0x0169,0x01d8,0x01dc,0x01da,0x01d6,
+ 0x0175,0x00fd,0x00ff,0x0177,0x017a,0x017e,0x017c,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 0f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 10 */
+ 0x4e02,0x4e04,0x4e05,0x4e0c,0x4e12,0x4e1f,0x4e23,0x4e24,0x4e28,0x4e2b,
+ 0x4e2e,0x4e2f,0x4e30,0x4e35,0x4e40,0x4e41,0x4e44,0x4e47,0x4e51,0x4e5a,
+ 0x4e5c,0x4e63,0x4e68,0x4e69,0x4e74,0x4e75,0x4e79,0x4e7f,0x4e8d,0x4e96,
+ 0x4e97,0x4e9d,0x4eaf,0x4eb9,0x4ec3,0x4ed0,0x4eda,0x4edb,0x4ee0,0x4ee1,
+ 0x4ee2,0x4ee8,0x4eef,0x4ef1,0x4ef3,0x4ef5,0x4efd,0x4efe,0x4eff,0x4f00,
+ 0x4f02,0x4f03,0x4f08,0x4f0b,0x4f0c,0x4f12,0x4f15,0x4f16,0x4f17,0x4f19,
+ 0x4f2e,0x4f31,0x4f60,0x4f33,0x4f35,0x4f37,0x4f39,0x4f3b,0x4f3e,0x4f40,
+ 0x4f42,0x4f48,0x4f49,0x4f4b,0x4f4c,0x4f52,0x4f54,0x4f56,0x4f58,0x4f5f,
+ 0x4f63,0x4f6a,0x4f6c,0x4f6e,0x4f71,0x4f77,0x4f78,0x4f79,0x4f7a,0x4f7d,
+ 0x4f7e,0x4f81,0x4f82,0x4f84
+ },
+ { /* ku 11 */
+ 0x4f85,0x4f89,0x4f8a,0x4f8c,0x4f8e,0x4f90,0x4f92,0x4f93,0x4f94,0x4f97,
+ 0x4f99,0x4f9a,0x4f9e,0x4f9f,0x4fb2,0x4fb7,0x4fb9,0x4fbb,0x4fbc,0x4fbd,
+ 0x4fbe,0x4fc0,0x4fc1,0x4fc5,0x4fc6,0x4fc8,0x4fc9,0x4fcb,0x4fcc,0x4fcd,
+ 0x4fcf,0x4fd2,0x4fdc,0x4fe0,0x4fe2,0x4ff0,0x4ff2,0x4ffc,0x4ffd,0x4fff,
+ 0x5000,0x5001,0x5004,0x5007,0x500a,0x500c,0x500e,0x5010,0x5013,0x5017,
+ 0x5018,0x501b,0x501c,0x501d,0x501e,0x5022,0x5027,0x502e,0x5030,0x5032,
+ 0x5033,0x5035,0x5040,0x5041,0x5042,0x5045,0x5046,0x504a,0x504c,0x504e,
+ 0x5051,0x5052,0x5053,0x5057,0x5059,0x505f,0x5060,0x5062,0x5063,0x5066,
+ 0x5067,0x506a,0x506d,0x5070,0x5071,0x503b,0x5081,0x5083,0x5084,0x5086,
+ 0x508a,0x508e,0x508f,0x5090
+ },
+ { /* ku 12 */
+ 0x5092,0x5093,0x5094,0x5096,0x509b,0x509c,0x509e,0x509f,0x50a0,0x50a1,
+ 0x50a2,0x50aa,0x50af,0x50b0,0x50b9,0x50ba,0x50bd,0x50c0,0x50c3,0x50c4,
+ 0x50c7,0x50cc,0x50ce,0x50d0,0x50d3,0x50d4,0x50d8,0x50dc,0x50dd,0x50df,
+ 0x50e2,0x50e4,0x50e6,0x50e8,0x50e9,0x50ef,0x50f1,0x50f6,0x50fa,0x50fe,
+ 0x5103,0x5106,0x5107,0x5108,0x510b,0x510c,0x510d,0x510e,0x50f2,0x5110,
+ 0x5117,0x5119,0x511b,0x511c,0x511d,0x511e,0x5123,0x5127,0x5128,0x512c,
+ 0x512d,0x512f,0x5131,0x5133,0x5134,0x5135,0x5138,0x5139,0x5142,0x514a,
+ 0x514f,0x5153,0x5155,0x5157,0x5158,0x515f,0x5164,0x5166,0x517e,0x5183,
+ 0x5184,0x518b,0x518e,0x5198,0x519d,0x51a1,0x51a3,0x51ad,0x51b8,0x51ba,
+ 0x51bc,0x51be,0x51bf,0x51c2
+ },
+ { /* ku 13 */
+ 0x51c8,0x51cf,0x51d1,0x51d2,0x51d3,0x51d5,0x51d8,0x51de,0x51e2,0x51e5,
+ 0x51ee,0x51f2,0x51f3,0x51f4,0x51f7,0x5201,0x5202,0x5205,0x5212,0x5213,
+ 0x5215,0x5216,0x5218,0x5222,0x5228,0x5231,0x5232,0x5235,0x523c,0x5245,
+ 0x5249,0x5255,0x5257,0x5258,0x525a,0x525c,0x525f,0x5260,0x5261,0x5266,
+ 0x526e,0x5277,0x5278,0x5279,0x5280,0x5282,0x5285,0x528a,0x528c,0x5293,
+ 0x5295,0x5296,0x5297,0x5298,0x529a,0x529c,0x52a4,0x52a5,0x52a6,0x52a7,
+ 0x52af,0x52b0,0x52b6,0x52b7,0x52b8,0x52ba,0x52bb,0x52bd,0x52c0,0x52c4,
+ 0x52c6,0x52c8,0x52cc,0x52cf,0x52d1,0x52d4,0x52d6,0x52db,0x52dc,0x52e1,
+ 0x52e5,0x52e8,0x52e9,0x52ea,0x52ec,0x52f0,0x52f1,0x52f4,0x52f6,0x52f7,
+ 0x5300,0x5303,0x530a,0x530b
+ },
+ { /* ku 14 */
+ 0x530c,0x5311,0x5313,0x5318,0x531b,0x531c,0x531e,0x531f,0x5325,0x5327,
+ 0x5328,0x5329,0x532b,0x532c,0x532d,0x5330,0x5332,0x5335,0x533c,0x533d,
+ 0x533e,0x5342,0x534c,0x534b,0x5359,0x535b,0x5361,0x5363,0x5365,0x536c,
+ 0x536d,0x5372,0x5379,0x537e,0x5383,0x5387,0x5388,0x538e,0x5393,0x5394,
+ 0x5399,0x539d,0x53a1,0x53a4,0x53aa,0x53ab,0x53af,0x53b2,0x53b4,0x53b5,
+ 0x53b7,0x53b8,0x53ba,0x53bd,0x53c0,0x53c5,0x53cf,0x53d2,0x53d3,0x53d5,
+ 0x53da,0x53dd,0x53de,0x53e0,0x53e6,0x53e7,0x53f5,0x5402,0x5413,0x541a,
+ 0x5421,0x5427,0x5428,0x542a,0x542f,0x5431,0x5434,0x5435,0x5443,0x5444,
+ 0x5447,0x544d,0x544f,0x545e,0x5462,0x5464,0x5466,0x5467,0x5469,0x546b,
+ 0x546d,0x546e,0x5474,0x547f
+ },
+ { /* ku 15 */
+ 0x5481,0x5483,0x5485,0x5488,0x5489,0x548d,0x5491,0x5495,0x5496,0x549c,
+ 0x549f,0x54a1,0x54a6,0x54a7,0x54a9,0x54aa,0x54ad,0x54ae,0x54b1,0x54b7,
+ 0x54b9,0x54ba,0x54bb,0x54bf,0x54c6,0x54ca,0x54cd,0x54ce,0x54e0,0x54ea,
+ 0x54ec,0x54ef,0x54f6,0x54fc,0x54fe,0x54ff,0x5500,0x5501,0x5505,0x5508,
+ 0x5509,0x550c,0x550d,0x550e,0x5515,0x552a,0x552b,0x5532,0x5535,0x5536,
+ 0x553b,0x553c,0x553d,0x5541,0x5547,0x5549,0x554a,0x554d,0x5550,0x5551,
+ 0x5558,0x555a,0x555b,0x555e,0x5560,0x5561,0x5564,0x5566,0x557f,0x5581,
+ 0x5582,0x5586,0x5588,0x558e,0x558f,0x5591,0x5592,0x5593,0x5594,0x5597,
+ 0x55a3,0x55a4,0x55ad,0x55b2,0x55bf,0x55c1,0x55c3,0x55c6,0x55c9,0x55cb,
+ 0x55cc,0x55ce,0x55d1,0x55d2
+ },
+ { /* ku 16 */
+ 0x55d3,0x55d7,0x55d8,0x55db,0x55de,0x55e2,0x55e9,0x55f6,0x55ff,0x5605,
+ 0x5608,0x560a,0x560d,0x560e,0x560f,0x5610,0x5611,0x5612,0x5619,0x562c,
+ 0x5630,0x5633,0x5635,0x5637,0x5639,0x563b,0x563c,0x563d,0x563f,0x5640,
+ 0x5641,0x5643,0x5644,0x5646,0x5649,0x564b,0x564d,0x564f,0x5654,0x565e,
+ 0x5660,0x5661,0x5662,0x5663,0x5666,0x5669,0x566d,0x566f,0x5671,0x5672,
+ 0x5675,0x5684,0x5685,0x5688,0x568b,0x568c,0x5695,0x5699,0x569a,0x569d,
+ 0x569e,0x569f,0x56a6,0x56a7,0x56a8,0x56a9,0x56ab,0x56ac,0x56ad,0x56b1,
+ 0x56b3,0x56b7,0x56be,0x56c5,0x56c9,0x56ca,0x56cb,0x56cf,0x56d0,0x56cc,
+ 0x56cd,0x56d9,0x56dc,0x56dd,0x56df,0x56e1,0x56e4,0x56e5,0x56e6,0x56e7,
+ 0x56e8,0x56f1,0x56eb,0x56ed
+ },
+ { /* ku 17 */
+ 0x56f6,0x56f7,0x5701,0x5702,0x5707,0x570a,0x570c,0x5711,0x5715,0x571a,
+ 0x571b,0x571d,0x5720,0x5722,0x5723,0x5724,0x5725,0x5729,0x572a,0x572c,
+ 0x572e,0x572f,0x5733,0x5734,0x573d,0x573e,0x573f,0x5745,0x5746,0x574c,
+ 0x574d,0x5752,0x5762,0x5765,0x5767,0x5768,0x576b,0x576d,0x576e,0x576f,
+ 0x5770,0x5771,0x5773,0x5774,0x5775,0x5777,0x5779,0x577a,0x577b,0x577c,
+ 0x577e,0x5781,0x5783,0x578c,0x5794,0x5797,0x5799,0x579a,0x579c,0x579d,
+ 0x579e,0x579f,0x57a1,0x5795,0x57a7,0x57a8,0x57a9,0x57ac,0x57b8,0x57bd,
+ 0x57c7,0x57c8,0x57cc,0x57cf,0x57d5,0x57dd,0x57de,0x57e4,0x57e6,0x57e7,
+ 0x57e9,0x57ed,0x57f0,0x57f5,0x57f6,0x57f8,0x57fd,0x57fe,0x57ff,0x5803,
+ 0x5804,0x5808,0x5809,0x57e1
+ },
+ { /* ku 18 */
+ 0x580c,0x580d,0x581b,0x581e,0x581f,0x5820,0x5826,0x5827,0x582d,0x5832,
+ 0x5839,0x583f,0x5849,0x584c,0x584d,0x584f,0x5850,0x5855,0x585f,0x5861,
+ 0x5864,0x5867,0x5868,0x5878,0x587c,0x587f,0x5880,0x5881,0x5887,0x5888,
+ 0x5889,0x588a,0x588c,0x588d,0x588f,0x5890,0x5894,0x5896,0x589d,0x58a0,
+ 0x58a1,0x58a2,0x58a6,0x58a9,0x58b1,0x58b2,0x58c4,0x58bc,0x58c2,0x58c8,
+ 0x58cd,0x58ce,0x58d0,0x58d2,0x58d4,0x58d6,0x58da,0x58dd,0x58e1,0x58e2,
+ 0x58e9,0x58f3,0x5905,0x5906,0x590b,0x590c,0x5912,0x5913,0x5914,0x8641,
+ 0x591d,0x5921,0x5923,0x5924,0x5928,0x592f,0x5930,0x5933,0x5935,0x5936,
+ 0x593f,0x5943,0x5946,0x5952,0x5953,0x5959,0x595b,0x595d,0x595e,0x595f,
+ 0x5961,0x5963,0x596b,0x596d
+ },
+ { /* ku 19 */
+ 0x596f,0x5972,0x5975,0x5976,0x5979,0x597b,0x597c,0x598b,0x598c,0x598e,
+ 0x5992,0x5995,0x5997,0x599f,0x59a4,0x59a7,0x59ad,0x59ae,0x59af,0x59b0,
+ 0x59b3,0x59b7,0x59ba,0x59bc,0x59c1,0x59c3,0x59c4,0x59c8,0x59ca,0x59cd,
+ 0x59d2,0x59dd,0x59de,0x59df,0x59e3,0x59e4,0x59e7,0x59ee,0x59ef,0x59f1,
+ 0x59f2,0x59f4,0x59f7,0x5a00,0x5a04,0x5a0c,0x5a0d,0x5a0e,0x5a12,0x5a13,
+ 0x5a1e,0x5a23,0x5a24,0x5a27,0x5a28,0x5a2a,0x5a2d,0x5a30,0x5a44,0x5a45,
+ 0x5a47,0x5a48,0x5a4c,0x5a50,0x5a55,0x5a5e,0x5a63,0x5a65,0x5a67,0x5a6d,
+ 0x5a77,0x5a7a,0x5a7b,0x5a7e,0x5a8b,0x5a90,0x5a93,0x5a96,0x5a99,0x5a9c,
+ 0x5a9e,0x5a9f,0x5aa0,0x5aa2,0x5aa7,0x5aac,0x5ab1,0x5ab2,0x5ab3,0x5ab5,
+ 0x5ab8,0x5aba,0x5abb,0x5abf
+ },
+ { /* ku 1a */
+ 0x5ac4,0x5ac6,0x5ac8,0x5acf,0x5ada,0x5adc,0x5ae0,0x5ae5,0x5aea,0x5aee,
+ 0x5af5,0x5af6,0x5afd,0x5b00,0x5b01,0x5b08,0x5b17,0x5b34,0x5b19,0x5b1b,
+ 0x5b1d,0x5b21,0x5b25,0x5b2d,0x5b38,0x5b41,0x5b4b,0x5b4c,0x5b52,0x5b56,
+ 0x5b5e,0x5b68,0x5b6e,0x5b6f,0x5b7c,0x5b7d,0x5b7e,0x5b7f,0x5b81,0x5b84,
+ 0x5b86,0x5b8a,0x5b8e,0x5b90,0x5b91,0x5b93,0x5b94,0x5b96,0x5ba8,0x5ba9,
+ 0x5bac,0x5bad,0x5baf,0x5bb1,0x5bb2,0x5bb7,0x5bba,0x5bbc,0x5bc0,0x5bc1,
+ 0x5bcd,0x5bcf,0x5bd6,0x5bd7,0x5bd8,0x5bd9,0x5bda,0x5be0,0x5bef,0x5bf1,
+ 0x5bf4,0x5bfd,0x5c0c,0x5c17,0x5c1e,0x5c1f,0x5c23,0x5c26,0x5c29,0x5c2b,
+ 0x5c2c,0x5c2e,0x5c30,0x5c32,0x5c35,0x5c36,0x5c59,0x5c5a,0x5c5c,0x5c62,
+ 0x5c63,0x5c67,0x5c68,0x5c69
+ },
+ { /* ku 1b */
+ 0x5c6d,0x5c70,0x5c74,0x5c75,0x5c7a,0x5c7b,0x5c7c,0x5c7d,0x5c87,0x5c88,
+ 0x5c8a,0x5c8f,0x5c92,0x5c9d,0x5c9f,0x5ca0,0x5ca2,0x5ca3,0x5ca6,0x5caa,
+ 0x5cb2,0x5cb4,0x5cb5,0x5cba,0x5cc9,0x5ccb,0x5cd2,0x5cdd,0x5cd7,0x5cee,
+ 0x5cf1,0x5cf2,0x5cf4,0x5d01,0x5d06,0x5d0d,0x5d12,0x5d2b,0x5d23,0x5d24,
+ 0x5d26,0x5d27,0x5d31,0x5d34,0x5d39,0x5d3d,0x5d3f,0x5d42,0x5d43,0x5d46,
+ 0x5d48,0x5d55,0x5d51,0x5d59,0x5d4a,0x5d5f,0x5d60,0x5d61,0x5d62,0x5d64,
+ 0x5d6a,0x5d6d,0x5d70,0x5d79,0x5d7a,0x5d7e,0x5d7f,0x5d81,0x5d83,0x5d88,
+ 0x5d8a,0x5d92,0x5d93,0x5d94,0x5d95,0x5d99,0x5d9b,0x5d9f,0x5da0,0x5da7,
+ 0x5dab,0x5db0,0x5db4,0x5db8,0x5db9,0x5dc3,0x5dc7,0x5dcb,0x5dd0,0x5dce,
+ 0x5dd8,0x5dd9,0x5de0,0x5de4
+ },
+ { /* ku 1c */
+ 0x5de9,0x5df8,0x5df9,0x5e00,0x5e07,0x5e0d,0x5e12,0x5e14,0x5e15,0x5e18,
+ 0x5e1f,0x5e20,0x5e2e,0x5e28,0x5e32,0x5e35,0x5e3e,0x5e4b,0x5e50,0x5e49,
+ 0x5e51,0x5e56,0x5e58,0x5e5b,0x5e5c,0x5e5e,0x5e68,0x5e6a,0x5e6b,0x5e6c,
+ 0x5e6d,0x5e6e,0x5e70,0x5e80,0x5e8b,0x5e8e,0x5ea2,0x5ea4,0x5ea5,0x5ea8,
+ 0x5eaa,0x5eac,0x5eb1,0x5eb3,0x5ebd,0x5ebe,0x5ebf,0x5ec6,0x5ecc,0x5ecb,
+ 0x5ece,0x5ed1,0x5ed2,0x5ed4,0x5ed5,0x5edc,0x5ede,0x5ee5,0x5eeb,0x5f02,
+ 0x5f06,0x5f07,0x5f08,0x5f0e,0x5f19,0x5f1c,0x5f1d,0x5f21,0x5f22,0x5f23,
+ 0x5f24,0x5f28,0x5f2b,0x5f2c,0x5f2e,0x5f30,0x5f34,0x5f36,0x5f3b,0x5f3d,
+ 0x5f3f,0x5f40,0x5f44,0x5f45,0x5f47,0x5f4d,0x5f50,0x5f54,0x5f58,0x5f5b,
+ 0x5f60,0x5f63,0x5f64,0x5f67
+ },
+ { /* ku 1d */
+ 0x5f6f,0x5f72,0x5f74,0x5f75,0x5f78,0x5f7a,0x5f7d,0x5f7e,0x5f89,0x5f8d,
+ 0x5f8f,0x5f96,0x5f9c,0x5f9d,0x5fa2,0x5fa7,0x5fab,0x5fa4,0x5fac,0x5faf,
+ 0x5fb0,0x5fb1,0x5fb8,0x5fc4,0x5fc7,0x5fc8,0x5fc9,0x5fcb,0x5fd0,0x5fd1,
+ 0x5fd2,0x5fd3,0x5fd4,0x5fde,0x5fe1,0x5fe2,0x5fe8,0x5fe9,0x5fea,0x5fec,
+ 0x5fed,0x5fee,0x5fef,0x5ff2,0x5ff3,0x5ff6,0x5ffa,0x5ffc,0x6007,0x600a,
+ 0x600d,0x6013,0x6014,0x6017,0x6018,0x601a,0x601f,0x6024,0x602d,0x6033,
+ 0x6035,0x6040,0x6047,0x6048,0x6049,0x604c,0x6051,0x6054,0x6056,0x6057,
+ 0x605d,0x6061,0x6067,0x6071,0x607e,0x607f,0x6082,0x6086,0x6088,0x608a,
+ 0x608e,0x6091,0x6093,0x6095,0x6098,0x609d,0x609e,0x60a2,0x60a4,0x60a5,
+ 0x60a8,0x60b0,0x60b1,0x60b7
+ },
+ { /* ku 1e */
+ 0x60bb,0x60be,0x60c2,0x60c4,0x60c8,0x60c9,0x60ca,0x60cb,0x60ce,0x60cf,
+ 0x60d4,0x60d5,0x60d9,0x60db,0x60dd,0x60de,0x60e2,0x60e5,0x60f2,0x60f5,
+ 0x60f8,0x60fc,0x60fd,0x6102,0x6107,0x610a,0x610c,0x6110,0x6111,0x6112,
+ 0x6113,0x6114,0x6116,0x6117,0x6119,0x611c,0x611e,0x6122,0x612a,0x612b,
+ 0x6130,0x6131,0x6135,0x6136,0x6137,0x6139,0x6141,0x6145,0x6146,0x6149,
+ 0x615e,0x6160,0x616c,0x6172,0x6178,0x617b,0x617c,0x617f,0x6180,0x6181,
+ 0x6183,0x6184,0x618b,0x618d,0x6192,0x6193,0x6197,0x6198,0x619c,0x619d,
+ 0x619f,0x61a0,0x61a5,0x61a8,0x61aa,0x61ad,0x61b8,0x61b9,0x61bc,0x61c0,
+ 0x61c1,0x61c2,0x61ce,0x61cf,0x61d5,0x61dc,0x61dd,0x61de,0x61df,0x61e1,
+ 0x61e2,0x61e7,0x61e9,0x61e5
+ },
+ { /* ku 1f */
+ 0x61ec,0x61ed,0x61ef,0x6201,0x6203,0x6204,0x6207,0x6213,0x6215,0x621c,
+ 0x6220,0x6222,0x6223,0x6227,0x6229,0x622b,0x6239,0x623d,0x6242,0x6243,
+ 0x6244,0x6246,0x624c,0x6250,0x6251,0x6252,0x6254,0x6256,0x625a,0x625c,
+ 0x6264,0x626d,0x626f,0x6273,0x627a,0x627d,0x628d,0x628e,0x628f,0x6290,
+ 0x62a6,0x62a8,0x62b3,0x62b6,0x62b7,0x62ba,0x62be,0x62bf,0x62c4,0x62ce,
+ 0x62d5,0x62d6,0x62da,0x62ea,0x62f2,0x62f4,0x62fc,0x62fd,0x6303,0x6304,
+ 0x630a,0x630b,0x630d,0x6310,0x6313,0x6316,0x6318,0x6329,0x632a,0x632d,
+ 0x6335,0x6336,0x6339,0x633c,0x6341,0x6342,0x6343,0x6344,0x6346,0x634a,
+ 0x634b,0x634e,0x6352,0x6353,0x6354,0x6358,0x635b,0x6365,0x6366,0x636c,
+ 0x636d,0x6371,0x6374,0x6375
+ },
+ { /* ku 20 */
+ 0x6378,0x637c,0x637d,0x637f,0x6382,0x6384,0x6387,0x638a,0x6390,0x6394,
+ 0x6395,0x6399,0x639a,0x639e,0x63a4,0x63a6,0x63ad,0x63ae,0x63af,0x63bd,
+ 0x63c1,0x63c5,0x63c8,0x63ce,0x63d1,0x63d3,0x63d4,0x63d5,0x63dc,0x63e0,
+ 0x63e5,0x63ea,0x63ec,0x63f2,0x63f3,0x63f5,0x63f8,0x63f9,0x6409,0x640a,
+ 0x6410,0x6412,0x6414,0x6418,0x641e,0x6420,0x6422,0x6424,0x6425,0x6429,
+ 0x642a,0x642f,0x6430,0x6435,0x643d,0x643f,0x644b,0x644f,0x6451,0x6452,
+ 0x6453,0x6454,0x645a,0x645b,0x645c,0x645d,0x645f,0x6460,0x6461,0x6463,
+ 0x646d,0x6473,0x6474,0x647b,0x647d,0x6485,0x6487,0x648f,0x6490,0x6491,
+ 0x6498,0x6499,0x649b,0x649d,0x649f,0x64a1,0x64a3,0x64a6,0x64a8,0x64ac,
+ 0x64b3,0x64bd,0x64be,0x64bf
+ },
+ { /* ku 21 */
+ 0x64c4,0x64c9,0x64ca,0x64cb,0x64cc,0x64ce,0x64d0,0x64d1,0x64d5,0x64d7,
+ 0x64e4,0x64e5,0x64e9,0x64ea,0x64ed,0x64f0,0x64f5,0x64f7,0x64fb,0x64ff,
+ 0x6501,0x6504,0x6508,0x6509,0x650a,0x650f,0x6513,0x6514,0x6516,0x6519,
+ 0x651b,0x651e,0x651f,0x6522,0x6526,0x6529,0x652e,0x6531,0x653a,0x653c,
+ 0x653d,0x6543,0x6547,0x6549,0x6550,0x6552,0x6554,0x655f,0x6560,0x6567,
+ 0x656b,0x657a,0x657d,0x6581,0x6585,0x658a,0x6592,0x6595,0x6598,0x659d,
+ 0x65a0,0x65a3,0x65a6,0x65ae,0x65b2,0x65b3,0x65b4,0x65bf,0x65c2,0x65c8,
+ 0x65c9,0x65ce,0x65d0,0x65d4,0x65d6,0x65d8,0x65df,0x65f0,0x65f2,0x65f4,
+ 0x65f5,0x65f9,0x65fe,0x65ff,0x6600,0x6604,0x6608,0x6609,0x660d,0x6611,
+ 0x6612,0x6615,0x6616,0x661d
+ },
+ { /* ku 22 */
+ 0x661e,0x6621,0x6622,0x6623,0x6624,0x6626,0x6629,0x662a,0x662b,0x662c,
+ 0x662e,0x6630,0x6631,0x6633,0x6639,0x6637,0x6640,0x6645,0x6646,0x664a,
+ 0x664c,0x6651,0x664e,0x6657,0x6658,0x6659,0x665b,0x665c,0x6660,0x6661,
+ 0x66fb,0x666a,0x666b,0x666c,0x667e,0x6673,0x6675,0x667f,0x6677,0x6678,
+ 0x6679,0x667b,0x6680,0x667c,0x668b,0x668c,0x668d,0x6690,0x6692,0x6699,
+ 0x669a,0x669b,0x669c,0x669f,0x66a0,0x66a4,0x66ad,0x66b1,0x66b2,0x66b5,
+ 0x66bb,0x66bf,0x66c0,0x66c2,0x66c3,0x66c8,0x66cc,0x66ce,0x66cf,0x66d4,
+ 0x66db,0x66df,0x66e8,0x66eb,0x66ec,0x66ee,0x66fa,0x6705,0x6707,0x670e,
+ 0x6713,0x6719,0x671c,0x6720,0x6722,0x6733,0x673e,0x6745,0x6747,0x6748,
+ 0x674c,0x6754,0x6755,0x675d
+ },
+ { /* ku 23 */
+ 0x6766,0x676c,0x676e,0x6774,0x6776,0x677b,0x6781,0x6784,0x678e,0x678f,
+ 0x6791,0x6793,0x6796,0x6798,0x6799,0x679b,0x67b0,0x67b1,0x67b2,0x67b5,
+ 0x67bb,0x67bc,0x67bd,0x67f9,0x67c0,0x67c2,0x67c3,0x67c5,0x67c8,0x67c9,
+ 0x67d2,0x67d7,0x67d9,0x67dc,0x67e1,0x67e6,0x67f0,0x67f2,0x67f6,0x67f7,
+ 0x6852,0x6814,0x6819,0x681d,0x681f,0x6828,0x6827,0x682c,0x682d,0x682f,
+ 0x6830,0x6831,0x6833,0x683b,0x683f,0x6844,0x6845,0x684a,0x684c,0x6855,
+ 0x6857,0x6858,0x685b,0x686b,0x686e,0x686f,0x6870,0x6871,0x6872,0x6875,
+ 0x6879,0x687a,0x687b,0x687c,0x6882,0x6884,0x6886,0x6888,0x6896,0x6898,
+ 0x689a,0x689c,0x68a1,0x68a3,0x68a5,0x68a9,0x68aa,0x68ae,0x68b2,0x68bb,
+ 0x68c5,0x68c8,0x68cc,0x68cf
+ },
+ { /* ku 24 */
+ 0x68d0,0x68d1,0x68d3,0x68d6,0x68d9,0x68dc,0x68dd,0x68e5,0x68e8,0x68ea,
+ 0x68eb,0x68ec,0x68ed,0x68f0,0x68f1,0x68f5,0x68f6,0x68fb,0x68fc,0x68fd,
+ 0x6906,0x6909,0x690a,0x6910,0x6911,0x6913,0x6916,0x6917,0x6931,0x6933,
+ 0x6935,0x6938,0x693b,0x6942,0x6945,0x6949,0x694e,0x6957,0x695b,0x6963,
+ 0x6964,0x6965,0x6966,0x6968,0x6969,0x696c,0x6970,0x6971,0x6972,0x697a,
+ 0x697b,0x697f,0x6980,0x698d,0x6992,0x6996,0x6998,0x69a1,0x69a5,0x69a6,
+ 0x69a8,0x69ab,0x69ad,0x69af,0x69b7,0x69b8,0x69ba,0x69bc,0x69c5,0x69c8,
+ 0x69d1,0x69d6,0x69d7,0x69e2,0x69e5,0x69ee,0x69ef,0x69f1,0x69f3,0x69f5,
+ 0x69fe,0x6a00,0x6a01,0x6a03,0x6a0f,0x6a11,0x6a15,0x6a1a,0x6a1d,0x6a20,
+ 0x6a24,0x6a28,0x6a30,0x6a32
+ },
+ { /* ku 25 */
+ 0x6a34,0x6a37,0x6a3b,0x6a3e,0x6a3f,0x6a45,0x6a46,0x6a49,0x6a4a,0x6a4e,
+ 0x6a50,0x6a51,0x6a52,0x6a55,0x6a56,0x6a5b,0x6a64,0x6a67,0x6a6a,0x6a71,
+ 0x6a73,0x6a7e,0x6a81,0x6a83,0x6a86,0x6a87,0x6a89,0x6a8b,0x6a91,0x6a9b,
+ 0x6a9d,0x6a9e,0x6a9f,0x6aa5,0x6aab,0x6aaf,0x6ab0,0x6ab1,0x6ab4,0x6abd,
+ 0x6abe,0x6abf,0x6ac6,0x6ac9,0x6ac8,0x6acc,0x6ad0,0x6ad4,0x6ad5,0x6ad6,
+ 0x6adc,0x6add,0x6ae4,0x6ae7,0x6aec,0x6af0,0x6af1,0x6af2,0x6afc,0x6afd,
+ 0x6b02,0x6b03,0x6b06,0x6b07,0x6b09,0x6b0f,0x6b10,0x6b11,0x6b17,0x6b1b,
+ 0x6b1e,0x6b24,0x6b28,0x6b2b,0x6b2c,0x6b2f,0x6b35,0x6b36,0x6b3b,0x6b3f,
+ 0x6b46,0x6b4a,0x6b4d,0x6b52,0x6b56,0x6b58,0x6b5d,0x6b60,0x6b67,0x6b6b,
+ 0x6b6e,0x6b70,0x6b75,0x6b7d
+ },
+ { /* ku 26 */
+ 0x6b7e,0x6b82,0x6b85,0x6b97,0x6b9b,0x6b9f,0x6ba0,0x6ba2,0x6ba3,0x6ba8,
+ 0x6ba9,0x6bac,0x6bad,0x6bae,0x6bb0,0x6bb8,0x6bb9,0x6bbd,0x6bbe,0x6bc3,
+ 0x6bc4,0x6bc9,0x6bcc,0x6bd6,0x6bda,0x6be1,0x6be3,0x6be6,0x6be7,0x6bee,
+ 0x6bf1,0x6bf7,0x6bf9,0x6bff,0x6c02,0x6c04,0x6c05,0x6c09,0x6c0d,0x6c0e,
+ 0x6c10,0x6c12,0x6c19,0x6c1f,0x6c26,0x6c27,0x6c28,0x6c2c,0x6c2e,0x6c33,
+ 0x6c35,0x6c36,0x6c3a,0x6c3b,0x6c3f,0x6c4a,0x6c4b,0x6c4d,0x6c4f,0x6c52,
+ 0x6c54,0x6c59,0x6c5b,0x6c5c,0x6c6b,0x6c6d,0x6c6f,0x6c74,0x6c76,0x6c78,
+ 0x6c79,0x6c7b,0x6c85,0x6c86,0x6c87,0x6c89,0x6c94,0x6c95,0x6c97,0x6c98,
+ 0x6c9c,0x6c9f,0x6cb0,0x6cb2,0x6cb4,0x6cc2,0x6cc6,0x6ccd,0x6ccf,0x6cd0,
+ 0x6cd1,0x6cd2,0x6cd4,0x6cd6
+ },
+ { /* ku 27 */
+ 0x6cda,0x6cdc,0x6ce0,0x6ce7,0x6ce9,0x6ceb,0x6cec,0x6cee,0x6cf2,0x6cf4,
+ 0x6d04,0x6d07,0x6d0a,0x6d0e,0x6d0f,0x6d11,0x6d13,0x6d1a,0x6d26,0x6d27,
+ 0x6d28,0x6c67,0x6d2e,0x6d2f,0x6d31,0x6d39,0x6d3c,0x6d3f,0x6d57,0x6d5e,
+ 0x6d5f,0x6d61,0x6d65,0x6d67,0x6d6f,0x6d70,0x6d7c,0x6d82,0x6d87,0x6d91,
+ 0x6d92,0x6d94,0x6d96,0x6d97,0x6d98,0x6daa,0x6dac,0x6db4,0x6db7,0x6db9,
+ 0x6dbd,0x6dbf,0x6dc4,0x6dc8,0x6dca,0x6dce,0x6dcf,0x6dd6,0x6ddb,0x6ddd,
+ 0x6ddf,0x6de0,0x6de2,0x6de5,0x6de9,0x6def,0x6df0,0x6df4,0x6df6,0x6dfc,
+ 0x6e00,0x6e04,0x6e1e,0x6e22,0x6e27,0x6e32,0x6e36,0x6e39,0x6e3b,0x6e3c,
+ 0x6e44,0x6e45,0x6e48,0x6e49,0x6e4b,0x6e4f,0x6e51,0x6e52,0x6e53,0x6e54,
+ 0x6e57,0x6e5c,0x6e5d,0x6e5e
+ },
+ { /* ku 28 */
+ 0x6e62,0x6e63,0x6e68,0x6e73,0x6e7b,0x6e7d,0x6e8d,0x6e93,0x6e99,0x6ea0,
+ 0x6ea7,0x6ead,0x6eae,0x6eb1,0x6eb3,0x6ebb,0x6ebf,0x6ec0,0x6ec1,0x6ec3,
+ 0x6ec7,0x6ec8,0x6eca,0x6ecd,0x6ece,0x6ecf,0x6eeb,0x6eed,0x6eee,0x6ef9,
+ 0x6efb,0x6efd,0x6f04,0x6f08,0x6f0a,0x6f0c,0x6f0d,0x6f16,0x6f18,0x6f1a,
+ 0x6f1b,0x6f26,0x6f29,0x6f2a,0x6f2f,0x6f30,0x6f33,0x6f36,0x6f3b,0x6f3c,
+ 0x6f2d,0x6f4f,0x6f51,0x6f52,0x6f53,0x6f57,0x6f59,0x6f5a,0x6f5d,0x6f5e,
+ 0x6f61,0x6f62,0x6f68,0x6f6c,0x6f7d,0x6f7e,0x6f83,0x6f87,0x6f88,0x6f8b,
+ 0x6f8c,0x6f8d,0x6f90,0x6f92,0x6f93,0x6f94,0x6f96,0x6f9a,0x6f9f,0x6fa0,
+ 0x6fa5,0x6fa6,0x6fa7,0x6fa8,0x6fae,0x6faf,0x6fb0,0x6fb5,0x6fb6,0x6fbc,
+ 0x6fc5,0x6fc7,0x6fc8,0x6fca
+ },
+ { /* ku 29 */
+ 0x6fda,0x6fde,0x6fe8,0x6fe9,0x6ff0,0x6ff5,0x6ff9,0x6ffc,0x6ffd,0x7000,
+ 0x7005,0x7006,0x7007,0x700d,0x7017,0x7020,0x7023,0x702f,0x7034,0x7037,
+ 0x7039,0x703c,0x7043,0x7044,0x7048,0x7049,0x704a,0x704b,0x7054,0x7055,
+ 0x705d,0x705e,0x704e,0x7064,0x7065,0x706c,0x706e,0x7075,0x7076,0x707e,
+ 0x7081,0x7085,0x7086,0x7094,0x7095,0x7096,0x7097,0x7098,0x709b,0x70a4,
+ 0x70ab,0x70b0,0x70b1,0x70b4,0x70b7,0x70ca,0x70d1,0x70d3,0x70d4,0x70d5,
+ 0x70d6,0x70d8,0x70dc,0x70e4,0x70fa,0x7103,0x7104,0x7105,0x7106,0x7107,
+ 0x710b,0x710c,0x710f,0x711e,0x7120,0x712b,0x712d,0x712f,0x7130,0x7131,
+ 0x7138,0x7141,0x7145,0x7146,0x7147,0x714a,0x714b,0x7150,0x7152,0x7157,
+ 0x715a,0x715c,0x715e,0x7160
+ },
+ { /* ku 2a */
+ 0x7168,0x7179,0x7180,0x7185,0x7187,0x718c,0x7192,0x719a,0x719b,0x71a0,
+ 0x71a2,0x71af,0x71b0,0x71b2,0x71b3,0x71ba,0x71bf,0x71c0,0x71c1,0x71c4,
+ 0x71cb,0x71cc,0x71d3,0x71d6,0x71d9,0x71da,0x71dc,0x71f8,0x71fe,0x7200,
+ 0x7207,0x7208,0x7209,0x7213,0x7217,0x721a,0x721d,0x721f,0x7224,0x722b,
+ 0x722f,0x7234,0x7238,0x7239,0x7241,0x7242,0x7243,0x7245,0x724e,0x724f,
+ 0x7250,0x7253,0x7255,0x7256,0x725a,0x725c,0x725e,0x7260,0x7263,0x7268,
+ 0x726b,0x726e,0x726f,0x7271,0x7277,0x7278,0x727b,0x727c,0x727f,0x7284,
+ 0x7289,0x728d,0x728e,0x7293,0x729b,0x72a8,0x72ad,0x72ae,0x72b1,0x72b4,
+ 0x72be,0x72c1,0x72c7,0x72c9,0x72cc,0x72d5,0x72d6,0x72d8,0x72df,0x72e5,
+ 0x72f3,0x72f4,0x72fa,0x72fb
+ },
+ { /* ku 2b */
+ 0x72fe,0x7302,0x7304,0x7305,0x7307,0x730b,0x730d,0x7312,0x7313,0x7318,
+ 0x7319,0x731e,0x7322,0x7324,0x7327,0x7328,0x732c,0x7331,0x7332,0x7335,
+ 0x733a,0x733b,0x733d,0x7343,0x734d,0x7350,0x7352,0x7356,0x7358,0x735d,
+ 0x735e,0x735f,0x7360,0x7366,0x7367,0x7369,0x736b,0x736c,0x736e,0x736f,
+ 0x7371,0x7377,0x7379,0x737c,0x7380,0x7381,0x7383,0x7385,0x7386,0x738e,
+ 0x7390,0x7393,0x7395,0x7397,0x7398,0x739c,0x739e,0x739f,0x73a0,0x73a2,
+ 0x73a5,0x73a6,0x73aa,0x73ab,0x73ad,0x73b5,0x73b7,0x73b9,0x73bc,0x73bd,
+ 0x73bf,0x73c5,0x73c6,0x73c9,0x73cb,0x73cc,0x73cf,0x73d2,0x73d3,0x73d6,
+ 0x73d9,0x73dd,0x73e1,0x73e3,0x73e6,0x73e7,0x73e9,0x73f4,0x73f5,0x73f7,
+ 0x73f9,0x73fa,0x73fb,0x73fd
+ },
+ { /* ku 2c */
+ 0x73ff,0x7400,0x7401,0x7404,0x7407,0x740a,0x7411,0x741a,0x741b,0x7424,
+ 0x7426,0x7428,0x7429,0x742a,0x742b,0x742c,0x742d,0x742e,0x742f,0x7430,
+ 0x7431,0x7439,0x7440,0x7443,0x7444,0x7446,0x7447,0x744b,0x744d,0x7451,
+ 0x7452,0x7457,0x745d,0x7462,0x7466,0x7467,0x7468,0x746b,0x746d,0x746e,
+ 0x7471,0x7472,0x7480,0x7481,0x7485,0x7486,0x7487,0x7489,0x748f,0x7490,
+ 0x7491,0x7492,0x7498,0x7499,0x749a,0x749c,0x749f,0x74a0,0x74a1,0x74a3,
+ 0x74a6,0x74a8,0x74a9,0x74aa,0x74ab,0x74ae,0x74af,0x74b1,0x74b2,0x74b5,
+ 0x74b9,0x74bb,0x74bf,0x74c8,0x74c9,0x74cc,0x74d0,0x74d3,0x74d8,0x74da,
+ 0x74db,0x74de,0x74df,0x74e4,0x74e8,0x74ea,0x74eb,0x74ef,0x74f4,0x74fa,
+ 0x74fb,0x74fc,0x74ff,0x7506
+ },
+ { /* ku 2d */
+ 0x7512,0x7516,0x7517,0x7520,0x7521,0x7524,0x7527,0x7529,0x752a,0x752f,
+ 0x7536,0x7539,0x753d,0x753e,0x753f,0x7540,0x7543,0x7547,0x7548,0x754e,
+ 0x7550,0x7552,0x7557,0x755e,0x755f,0x7561,0x756f,0x7571,0x7579,0x757a,
+ 0x757b,0x757c,0x757d,0x757e,0x7581,0x7585,0x7590,0x7592,0x7593,0x7595,
+ 0x7599,0x759c,0x75a2,0x75a4,0x75b4,0x75ba,0x75bf,0x75c0,0x75c1,0x75c4,
+ 0x75c6,0x75cc,0x75ce,0x75cf,0x75d7,0x75dc,0x75df,0x75e0,0x75e1,0x75e4,
+ 0x75e7,0x75ec,0x75ee,0x75ef,0x75f1,0x75f9,0x7600,0x7602,0x7603,0x7604,
+ 0x7607,0x7608,0x760a,0x760c,0x760f,0x7612,0x7613,0x7615,0x7616,0x7619,
+ 0x761b,0x761c,0x761d,0x761e,0x7623,0x7625,0x7626,0x7629,0x762d,0x7632,
+ 0x7633,0x7635,0x7638,0x7639
+ },
+ { /* ku 2e */
+ 0x763a,0x763c,0x764a,0x7640,0x7641,0x7643,0x7644,0x7645,0x7649,0x764b,
+ 0x7655,0x7659,0x765f,0x7664,0x7665,0x766d,0x766e,0x766f,0x7671,0x7674,
+ 0x7681,0x7685,0x768c,0x768d,0x7695,0x769b,0x769c,0x769d,0x769f,0x76a0,
+ 0x76a2,0x76a3,0x76a4,0x76a5,0x76a6,0x76a7,0x76a8,0x76aa,0x76ad,0x76bd,
+ 0x76c1,0x76c5,0x76c9,0x76cb,0x76cc,0x76ce,0x76d4,0x76d9,0x76e0,0x76e6,
+ 0x76e8,0x76ec,0x76f0,0x76f1,0x76f6,0x76f9,0x76fc,0x7700,0x7706,0x770a,
+ 0x770e,0x7712,0x7714,0x7715,0x7717,0x7719,0x771a,0x771c,0x7722,0x7728,
+ 0x772d,0x772e,0x772f,0x7734,0x7735,0x7736,0x7739,0x773d,0x773e,0x7742,
+ 0x7745,0x7746,0x774a,0x774d,0x774e,0x774f,0x7752,0x7756,0x7757,0x775c,
+ 0x775e,0x775f,0x7760,0x7762
+ },
+ { /* ku 2f */
+ 0x7764,0x7767,0x776a,0x776c,0x7770,0x7772,0x7773,0x7774,0x777a,0x777d,
+ 0x7780,0x7784,0x778c,0x778d,0x7794,0x7795,0x7796,0x779a,0x779f,0x77a2,
+ 0x77a7,0x77aa,0x77ae,0x77af,0x77b1,0x77b5,0x77be,0x77c3,0x77c9,0x77d1,
+ 0x77d2,0x77d5,0x77d9,0x77de,0x77df,0x77e0,0x77e4,0x77e6,0x77ea,0x77ec,
+ 0x77f0,0x77f1,0x77f4,0x77f8,0x77fb,0x7805,0x7806,0x7809,0x780d,0x780e,
+ 0x7811,0x781d,0x7821,0x7822,0x7823,0x782d,0x782e,0x7830,0x7835,0x7837,
+ 0x7843,0x7844,0x7847,0x7848,0x784c,0x784e,0x7852,0x785c,0x785e,0x7860,
+ 0x7861,0x7863,0x7864,0x7868,0x786a,0x786e,0x787a,0x787e,0x788a,0x788f,
+ 0x7894,0x7898,0x78a1,0x789d,0x789e,0x789f,0x78a4,0x78a8,0x78ac,0x78ad,
+ 0x78b0,0x78b1,0x78b2,0x78b3
+ },
+ { /* ku 30 */
+ 0x78bb,0x78bd,0x78bf,0x78c7,0x78c8,0x78c9,0x78cc,0x78ce,0x78d2,0x78d3,
+ 0x78d5,0x78d6,0x78e4,0x78db,0x78df,0x78e0,0x78e1,0x78e6,0x78ea,0x78f2,
+ 0x78f3,0x7900,0x78f6,0x78f7,0x78fa,0x78fb,0x78ff,0x7906,0x790c,0x7910,
+ 0x791a,0x791c,0x791e,0x791f,0x7920,0x7925,0x7927,0x7929,0x792d,0x7931,
+ 0x7934,0x7935,0x793b,0x793d,0x793f,0x7944,0x7945,0x7946,0x794a,0x794b,
+ 0x794f,0x7951,0x7954,0x7958,0x795b,0x795c,0x7967,0x7969,0x796b,0x7972,
+ 0x7979,0x797b,0x797c,0x797e,0x798b,0x798c,0x7991,0x7993,0x7994,0x7995,
+ 0x7996,0x7998,0x799b,0x799c,0x79a1,0x79a8,0x79a9,0x79ab,0x79af,0x79b1,
+ 0x79b4,0x79b8,0x79bb,0x79c2,0x79c4,0x79c7,0x79c8,0x79ca,0x79cf,0x79d4,
+ 0x79d6,0x79da,0x79dd,0x79de
+ },
+ { /* ku 31 */
+ 0x79e0,0x79e2,0x79e5,0x79ea,0x79eb,0x79ed,0x79f1,0x79f8,0x79fc,0x7a02,
+ 0x7a03,0x7a07,0x7a09,0x7a0a,0x7a0c,0x7a11,0x7a15,0x7a1b,0x7a1e,0x7a21,
+ 0x7a27,0x7a2b,0x7a2d,0x7a2f,0x7a30,0x7a34,0x7a35,0x7a38,0x7a39,0x7a3a,
+ 0x7a44,0x7a45,0x7a47,0x7a48,0x7a4c,0x7a55,0x7a56,0x7a59,0x7a5c,0x7a5d,
+ 0x7a5f,0x7a60,0x7a65,0x7a67,0x7a6a,0x7a6d,0x7a75,0x7a78,0x7a7e,0x7a80,
+ 0x7a82,0x7a85,0x7a86,0x7a8a,0x7a8b,0x7a90,0x7a91,0x7a94,0x7a9e,0x7aa0,
+ 0x7aa3,0x7aac,0x7ab3,0x7ab5,0x7ab9,0x7abb,0x7abc,0x7ac6,0x7ac9,0x7acc,
+ 0x7ace,0x7ad1,0x7adb,0x7ae8,0x7ae9,0x7aeb,0x7aec,0x7af1,0x7af4,0x7afb,
+ 0x7afd,0x7afe,0x7b07,0x7b14,0x7b1f,0x7b23,0x7b27,0x7b29,0x7b2a,0x7b2b,
+ 0x7b2d,0x7b2e,0x7b2f,0x7b30
+ },
+ { /* ku 32 */
+ 0x7b31,0x7b34,0x7b3d,0x7b3f,0x7b40,0x7b41,0x7b47,0x7b4e,0x7b55,0x7b60,
+ 0x7b64,0x7b66,0x7b69,0x7b6a,0x7b6d,0x7b6f,0x7b72,0x7b73,0x7b77,0x7b84,
+ 0x7b89,0x7b8e,0x7b90,0x7b91,0x7b96,0x7b9b,0x7b9e,0x7ba0,0x7ba5,0x7bac,
+ 0x7baf,0x7bb0,0x7bb2,0x7bb5,0x7bb6,0x7bba,0x7bbb,0x7bbc,0x7bbd,0x7bc2,
+ 0x7bc5,0x7bc8,0x7bca,0x7bd4,0x7bd6,0x7bd7,0x7bd9,0x7bda,0x7bdb,0x7be8,
+ 0x7bea,0x7bf2,0x7bf4,0x7bf5,0x7bf8,0x7bf9,0x7bfa,0x7bfc,0x7bfe,0x7c01,
+ 0x7c02,0x7c03,0x7c04,0x7c06,0x7c09,0x7c0b,0x7c0c,0x7c0e,0x7c0f,0x7c19,
+ 0x7c1b,0x7c20,0x7c25,0x7c26,0x7c28,0x7c2c,0x7c31,0x7c33,0x7c34,0x7c36,
+ 0x7c39,0x7c3a,0x7c46,0x7c4a,0x7c55,0x7c51,0x7c52,0x7c53,0x7c59,0x7c5a,
+ 0x7c5b,0x7c5c,0x7c5d,0x7c5e
+ },
+ { /* ku 33 */
+ 0x7c61,0x7c63,0x7c67,0x7c69,0x7c6d,0x7c6e,0x7c70,0x7c72,0x7c79,0x7c7c,
+ 0x7c7d,0x7c86,0x7c87,0x7c8f,0x7c94,0x7c9e,0x7ca0,0x7ca6,0x7cb0,0x7cb6,
+ 0x7cb7,0x7cba,0x7cbb,0x7cbc,0x7cbf,0x7cc4,0x7cc7,0x7cc8,0x7cc9,0x7ccd,
+ 0x7ccf,0x7cd3,0x7cd4,0x7cd5,0x7cd7,0x7cd9,0x7cda,0x7cdd,0x7ce6,0x7ce9,
+ 0x7ceb,0x7cf5,0x7d03,0x7d07,0x7d08,0x7d09,0x7d0f,0x7d11,0x7d12,0x7d13,
+ 0x7d16,0x7d1d,0x7d1e,0x7d23,0x7d26,0x7d2a,0x7d2d,0x7d31,0x7d3c,0x7d3d,
+ 0x7d3e,0x7d40,0x7d41,0x7d47,0x7d48,0x7d4d,0x7d51,0x7d53,0x7d57,0x7d59,
+ 0x7d5a,0x7d5c,0x7d5d,0x7d65,0x7d67,0x7d6a,0x7d70,0x7d78,0x7d7a,0x7d7b,
+ 0x7d7f,0x7d81,0x7d82,0x7d83,0x7d85,0x7d86,0x7d88,0x7d8b,0x7d8c,0x7d8d,
+ 0x7d91,0x7d96,0x7d97,0x7d9d
+ },
+ { /* ku 34 */
+ 0x7d9e,0x7da6,0x7da7,0x7daa,0x7db3,0x7db6,0x7db7,0x7db9,0x7dc2,0x7dc3,
+ 0x7dc4,0x7dc5,0x7dc6,0x7dcc,0x7dcd,0x7dce,0x7dd7,0x7dd9,0x7e00,0x7de2,
+ 0x7de5,0x7de6,0x7dea,0x7deb,0x7ded,0x7df1,0x7df5,0x7df6,0x7df9,0x7dfa,
+ 0x7e08,0x7e10,0x7e11,0x7e15,0x7e17,0x7e1c,0x7e1d,0x7e20,0x7e27,0x7e28,
+ 0x7e2c,0x7e2d,0x7e2f,0x7e33,0x7e36,0x7e3f,0x7e44,0x7e45,0x7e47,0x7e4e,
+ 0x7e50,0x7e52,0x7e58,0x7e5f,0x7e61,0x7e62,0x7e65,0x7e6b,0x7e6e,0x7e6f,
+ 0x7e73,0x7e78,0x7e7e,0x7e81,0x7e86,0x7e87,0x7e8a,0x7e8d,0x7e91,0x7e95,
+ 0x7e98,0x7e9a,0x7e9d,0x7e9e,0x7f3c,0x7f3b,0x7f3d,0x7f3e,0x7f3f,0x7f43,
+ 0x7f44,0x7f47,0x7f4f,0x7f52,0x7f53,0x7f5b,0x7f5c,0x7f5d,0x7f61,0x7f63,
+ 0x7f64,0x7f65,0x7f66,0x7f6d
+ },
+ { /* ku 35 */
+ 0x7f71,0x7f7d,0x7f7e,0x7f7f,0x7f80,0x7f8b,0x7f8d,0x7f8f,0x7f90,0x7f91,
+ 0x7f96,0x7f97,0x7f9c,0x7fa1,0x7fa2,0x7fa6,0x7faa,0x7fad,0x7fb4,0x7fbc,
+ 0x7fbf,0x7fc0,0x7fc3,0x7fc8,0x7fce,0x7fcf,0x7fdb,0x7fdf,0x7fe3,0x7fe5,
+ 0x7fe8,0x7fec,0x7fee,0x7fef,0x7ff2,0x7ffa,0x7ffd,0x7ffe,0x7fff,0x8007,
+ 0x8008,0x800a,0x800d,0x800e,0x800f,0x8011,0x8013,0x8014,0x8016,0x801d,
+ 0x801e,0x801f,0x8020,0x8024,0x8026,0x802c,0x802e,0x8030,0x8034,0x8035,
+ 0x8037,0x8039,0x803a,0x803c,0x803e,0x8040,0x8044,0x8060,0x8064,0x8066,
+ 0x806d,0x8071,0x8075,0x8081,0x8088,0x808e,0x809c,0x809e,0x80a6,0x80a7,
+ 0x80ab,0x80b8,0x80b9,0x80c8,0x80cd,0x80cf,0x80d2,0x80d4,0x80d5,0x80d7,
+ 0x80d8,0x80e0,0x80ed,0x80ee
+ },
+ { /* ku 36 */
+ 0x80f0,0x80f2,0x80f3,0x80f6,0x80f9,0x80fa,0x80fe,0x8103,0x810b,0x8116,
+ 0x8117,0x8118,0x811c,0x811e,0x8120,0x8124,0x8127,0x812c,0x8130,0x8135,
+ 0x813a,0x813c,0x8145,0x8147,0x814a,0x814c,0x8152,0x8157,0x8160,0x8161,
+ 0x8167,0x8168,0x8169,0x816d,0x816f,0x8177,0x8181,0x8190,0x8184,0x8185,
+ 0x8186,0x818b,0x818e,0x8196,0x8198,0x819b,0x819e,0x81a2,0x81ae,0x81b2,
+ 0x81b4,0x81bb,0x81cb,0x81c3,0x81c5,0x81ca,0x81ce,0x81cf,0x81d5,0x81d7,
+ 0x81db,0x81dd,0x81de,0x81e1,0x81e4,0x81eb,0x81ec,0x81f0,0x81f1,0x81f2,
+ 0x81f5,0x81f6,0x81f8,0x81f9,0x81fd,0x81ff,0x8200,0x8203,0x820f,0x8213,
+ 0x8214,0x8219,0x821a,0x821d,0x8221,0x8222,0x8228,0x8232,0x8234,0x823a,
+ 0x8243,0x8244,0x8245,0x8246
+ },
+ { /* ku 37 */
+ 0x824b,0x824e,0x824f,0x8251,0x8256,0x825c,0x8260,0x8263,0x8267,0x826d,
+ 0x8274,0x827b,0x827d,0x827f,0x8280,0x8281,0x8283,0x8284,0x8287,0x8289,
+ 0x828a,0x828e,0x8291,0x8294,0x8296,0x8298,0x829a,0x829b,0x82a0,0x82a1,
+ 0x82a3,0x82a4,0x82a7,0x82a8,0x82a9,0x82aa,0x82ae,0x82b0,0x82b2,0x82b4,
+ 0x82b7,0x82ba,0x82bc,0x82be,0x82bf,0x82c6,0x82d0,0x82d5,0x82da,0x82e0,
+ 0x82e2,0x82e4,0x82e8,0x82ea,0x82ed,0x82ef,0x82f6,0x82f7,0x82fd,0x82fe,
+ 0x8300,0x8301,0x8307,0x8308,0x830a,0x830b,0x8354,0x831b,0x831d,0x831e,
+ 0x831f,0x8321,0x8322,0x832c,0x832d,0x832e,0x8330,0x8333,0x8337,0x833a,
+ 0x833c,0x833d,0x8342,0x8343,0x8344,0x8347,0x834d,0x834e,0x8351,0x8355,
+ 0x8356,0x8357,0x8370,0x8378
+ },
+ { /* ku 38 */
+ 0x837d,0x837f,0x8380,0x8382,0x8384,0x8386,0x838d,0x8392,0x8394,0x8395,
+ 0x8398,0x8399,0x839b,0x839c,0x839d,0x83a6,0x83a7,0x83a9,0x83ac,0x83be,
+ 0x83bf,0x83c0,0x83c7,0x83c9,0x83cf,0x83d0,0x83d1,0x83d4,0x83dd,0x8353,
+ 0x83e8,0x83ea,0x83f6,0x83f8,0x83f9,0x83fc,0x8401,0x8406,0x840a,0x840f,
+ 0x8411,0x8415,0x8419,0x83ad,0x842f,0x8439,0x8445,0x8447,0x8448,0x844a,
+ 0x844d,0x844f,0x8451,0x8452,0x8456,0x8458,0x8459,0x845a,0x845c,0x8460,
+ 0x8464,0x8465,0x8467,0x846a,0x8470,0x8473,0x8474,0x8476,0x8478,0x847c,
+ 0x847d,0x8481,0x8485,0x8492,0x8493,0x8495,0x849e,0x84a6,0x84a8,0x84a9,
+ 0x84aa,0x84af,0x84b1,0x84b4,0x84ba,0x84bd,0x84be,0x84c0,0x84c2,0x84c7,
+ 0x84c8,0x84cc,0x84cf,0x84d3
+ },
+ { /* ku 39 */
+ 0x84dc,0x84e7,0x84ea,0x84ef,0x84f0,0x84f1,0x84f2,0x84f7,0x8532,0x84fa,
+ 0x84fb,0x84fd,0x8502,0x8503,0x8507,0x850c,0x850e,0x8510,0x851c,0x851e,
+ 0x8522,0x8523,0x8524,0x8525,0x8527,0x852a,0x852b,0x852f,0x8533,0x8534,
+ 0x8536,0x853f,0x8546,0x854f,0x8550,0x8551,0x8552,0x8553,0x8556,0x8559,
+ 0x855c,0x855d,0x855e,0x855f,0x8560,0x8561,0x8562,0x8564,0x856b,0x856f,
+ 0x8579,0x857a,0x857b,0x857d,0x857f,0x8581,0x8585,0x8586,0x8589,0x858b,
+ 0x858c,0x858f,0x8593,0x8598,0x859d,0x859f,0x85a0,0x85a2,0x85a5,0x85a7,
+ 0x85b4,0x85b6,0x85b7,0x85b8,0x85bc,0x85bd,0x85be,0x85bf,0x85c2,0x85c7,
+ 0x85ca,0x85cb,0x85ce,0x85ad,0x85d8,0x85da,0x85df,0x85e0,0x85e6,0x85e8,
+ 0x85ed,0x85f3,0x85f6,0x85fc
+ },
+ { /* ku 3a */
+ 0x85ff,0x8600,0x8604,0x8605,0x860d,0x860e,0x8610,0x8611,0x8612,0x8618,
+ 0x8619,0x861b,0x861e,0x8621,0x8627,0x8629,0x8636,0x8638,0x863a,0x863c,
+ 0x863d,0x8640,0x8642,0x8646,0x8652,0x8653,0x8656,0x8657,0x8658,0x8659,
+ 0x865d,0x8660,0x8661,0x8662,0x8663,0x8664,0x8669,0x866c,0x866f,0x8675,
+ 0x8676,0x8677,0x867a,0x868d,0x8691,0x8696,0x8698,0x869a,0x869c,0x86a1,
+ 0x86a6,0x86a7,0x86a8,0x86ad,0x86b1,0x86b3,0x86b4,0x86b5,0x86b7,0x86b8,
+ 0x86b9,0x86bf,0x86c0,0x86c1,0x86c3,0x86c5,0x86d1,0x86d2,0x86d5,0x86d7,
+ 0x86da,0x86dc,0x86e0,0x86e3,0x86e5,0x86e7,0x8688,0x86fa,0x86fc,0x86fd,
+ 0x8704,0x8705,0x8707,0x870b,0x870e,0x870f,0x8710,0x8713,0x8714,0x8719,
+ 0x871e,0x871f,0x8721,0x8723
+ },
+ { /* ku 3b */
+ 0x8728,0x872e,0x872f,0x8731,0x8732,0x8739,0x873a,0x873c,0x873d,0x873e,
+ 0x8740,0x8743,0x8745,0x874d,0x8758,0x875d,0x8761,0x8764,0x8765,0x876f,
+ 0x8771,0x8772,0x877b,0x8783,0x8784,0x8785,0x8786,0x8787,0x8788,0x8789,
+ 0x878b,0x878c,0x8790,0x8793,0x8795,0x8797,0x8798,0x8799,0x879e,0x87a0,
+ 0x87a3,0x87a7,0x87ac,0x87ad,0x87ae,0x87b1,0x87b5,0x87be,0x87bf,0x87c1,
+ 0x87c8,0x87c9,0x87ca,0x87ce,0x87d5,0x87d6,0x87d9,0x87da,0x87dc,0x87df,
+ 0x87e2,0x87e3,0x87e4,0x87ea,0x87eb,0x87ed,0x87f1,0x87f3,0x87f8,0x87fa,
+ 0x87ff,0x8801,0x8803,0x8806,0x8809,0x880a,0x880b,0x8810,0x8819,0x8812,
+ 0x8813,0x8814,0x8818,0x881a,0x881b,0x881c,0x881e,0x881f,0x8828,0x882d,
+ 0x882e,0x8830,0x8832,0x8835
+ },
+ { /* ku 3c */
+ 0x883a,0x883c,0x8841,0x8843,0x8845,0x8848,0x8849,0x884a,0x884b,0x884e,
+ 0x8851,0x8855,0x8856,0x8858,0x885a,0x885c,0x885f,0x8860,0x8864,0x8869,
+ 0x8871,0x8879,0x887b,0x8880,0x8898,0x889a,0x889b,0x889c,0x889f,0x88a0,
+ 0x88a8,0x88aa,0x88ba,0x88bd,0x88be,0x88c0,0x88ca,0x88cb,0x88cc,0x88cd,
+ 0x88ce,0x88d1,0x88d2,0x88d3,0x88db,0x88de,0x88e7,0x88ef,0x88f0,0x88f1,
+ 0x88f5,0x88f7,0x8901,0x8906,0x890d,0x890e,0x890f,0x8915,0x8916,0x8918,
+ 0x8919,0x891a,0x891c,0x8920,0x8926,0x8927,0x8928,0x8930,0x8931,0x8932,
+ 0x8935,0x8939,0x893a,0x893e,0x8940,0x8942,0x8945,0x8946,0x8949,0x894f,
+ 0x8952,0x8957,0x895a,0x895b,0x895c,0x8961,0x8962,0x8963,0x896b,0x896e,
+ 0x8970,0x8973,0x8975,0x897a
+ },
+ { /* ku 3d */
+ 0x897b,0x897c,0x897d,0x8989,0x898d,0x8990,0x8994,0x8995,0x899b,0x899c,
+ 0x899f,0x89a0,0x89a5,0x89b0,0x89b4,0x89b5,0x89b6,0x89b7,0x89bc,0x89d4,
+ 0x89d5,0x89d6,0x89d7,0x89d8,0x89e5,0x89e9,0x89eb,0x89ed,0x89f1,0x89f3,
+ 0x89f6,0x89f9,0x89fd,0x89ff,0x8a04,0x8a05,0x8a07,0x8a0f,0x8a11,0x8a12,
+ 0x8a14,0x8a15,0x8a1e,0x8a20,0x8a22,0x8a24,0x8a26,0x8a2b,0x8a2c,0x8a2f,
+ 0x8a35,0x8a37,0x8a3d,0x8a3e,0x8a40,0x8a43,0x8a45,0x8a47,0x8a49,0x8a4d,
+ 0x8a4e,0x8a53,0x8a56,0x8a57,0x8a58,0x8a5c,0x8a5d,0x8a61,0x8a65,0x8a67,
+ 0x8a75,0x8a76,0x8a77,0x8a79,0x8a7a,0x8a7b,0x8a7e,0x8a7f,0x8a80,0x8a83,
+ 0x8a86,0x8a8b,0x8a8f,0x8a90,0x8a92,0x8a96,0x8a97,0x8a99,0x8a9f,0x8aa7,
+ 0x8aa9,0x8aae,0x8aaf,0x8ab3
+ },
+ { /* ku 3e */
+ 0x8ab6,0x8ab7,0x8abb,0x8abe,0x8ac3,0x8ac6,0x8ac8,0x8ac9,0x8aca,0x8ad1,
+ 0x8ad3,0x8ad4,0x8ad5,0x8ad7,0x8add,0x8adf,0x8aec,0x8af0,0x8af4,0x8af5,
+ 0x8af6,0x8afc,0x8aff,0x8b05,0x8b06,0x8b0b,0x8b11,0x8b1c,0x8b1e,0x8b1f,
+ 0x8b0a,0x8b2d,0x8b30,0x8b37,0x8b3c,0x8b42,0x8b43,0x8b44,0x8b45,0x8b46,
+ 0x8b48,0x8b52,0x8b53,0x8b54,0x8b59,0x8b4d,0x8b5e,0x8b63,0x8b6d,0x8b76,
+ 0x8b78,0x8b79,0x8b7c,0x8b7e,0x8b81,0x8b84,0x8b85,0x8b8b,0x8b8d,0x8b8f,
+ 0x8b94,0x8b95,0x8b9c,0x8b9e,0x8b9f,0x8c38,0x8c39,0x8c3d,0x8c3e,0x8c45,
+ 0x8c47,0x8c49,0x8c4b,0x8c4f,0x8c51,0x8c53,0x8c54,0x8c57,0x8c58,0x8c5b,
+ 0x8c5d,0x8c59,0x8c63,0x8c64,0x8c66,0x8c68,0x8c69,0x8c6d,0x8c73,0x8c75,
+ 0x8c76,0x8c7b,0x8c7e,0x8c86
+ },
+ { /* ku 3f */
+ 0x8c87,0x8c8b,0x8c90,0x8c92,0x8c93,0x8c99,0x8c9b,0x8c9c,0x8ca4,0x8cb9,
+ 0x8cba,0x8cc5,0x8cc6,0x8cc9,0x8ccb,0x8ccf,0x8cd6,0x8cd5,0x8cd9,0x8cdd,
+ 0x8ce1,0x8ce8,0x8cec,0x8cef,0x8cf0,0x8cf2,0x8cf5,0x8cf7,0x8cf8,0x8cfe,
+ 0x8cff,0x8d01,0x8d03,0x8d09,0x8d12,0x8d17,0x8d1b,0x8d65,0x8d69,0x8d6c,
+ 0x8d6e,0x8d7f,0x8d82,0x8d84,0x8d88,0x8d8d,0x8d90,0x8d91,0x8d95,0x8d9e,
+ 0x8d9f,0x8da0,0x8da6,0x8dab,0x8dac,0x8daf,0x8db2,0x8db5,0x8db7,0x8db9,
+ 0x8dbb,0x8dc0,0x8dc5,0x8dc6,0x8dc7,0x8dc8,0x8dca,0x8dce,0x8dd1,0x8dd4,
+ 0x8dd5,0x8dd7,0x8dd9,0x8de4,0x8de5,0x8de7,0x8dec,0x8df0,0x8dbc,0x8df1,
+ 0x8df2,0x8df4,0x8dfd,0x8e01,0x8e04,0x8e05,0x8e06,0x8e0b,0x8e11,0x8e14,
+ 0x8e16,0x8e20,0x8e21,0x8e22
+ },
+ { /* ku 40 */
+ 0x8e23,0x8e26,0x8e27,0x8e31,0x8e33,0x8e36,0x8e37,0x8e38,0x8e39,0x8e3d,
+ 0x8e40,0x8e41,0x8e4b,0x8e4d,0x8e4e,0x8e4f,0x8e54,0x8e5b,0x8e5c,0x8e5d,
+ 0x8e5e,0x8e61,0x8e62,0x8e69,0x8e6c,0x8e6d,0x8e6f,0x8e70,0x8e71,0x8e79,
+ 0x8e7a,0x8e7b,0x8e82,0x8e83,0x8e89,0x8e90,0x8e92,0x8e95,0x8e9a,0x8e9b,
+ 0x8e9d,0x8e9e,0x8ea2,0x8ea7,0x8ea9,0x8ead,0x8eae,0x8eb3,0x8eb5,0x8eba,
+ 0x8ebb,0x8ec0,0x8ec1,0x8ec3,0x8ec4,0x8ec7,0x8ecf,0x8ed1,0x8ed4,0x8edc,
+ 0x8ee8,0x8eee,0x8ef0,0x8ef1,0x8ef7,0x8ef9,0x8efa,0x8eed,0x8f00,0x8f02,
+ 0x8f07,0x8f08,0x8f0f,0x8f10,0x8f16,0x8f17,0x8f18,0x8f1e,0x8f20,0x8f21,
+ 0x8f23,0x8f25,0x8f27,0x8f28,0x8f2c,0x8f2d,0x8f2e,0x8f34,0x8f35,0x8f36,
+ 0x8f37,0x8f3a,0x8f40,0x8f41
+ },
+ { /* ku 41 */
+ 0x8f43,0x8f47,0x8f4f,0x8f51,0x8f52,0x8f53,0x8f54,0x8f55,0x8f58,0x8f5d,
+ 0x8f5e,0x8f65,0x8f9d,0x8fa0,0x8fa1,0x8fa4,0x8fa5,0x8fa6,0x8fb5,0x8fb6,
+ 0x8fb8,0x8fbe,0x8fc0,0x8fc1,0x8fc6,0x8fca,0x8fcb,0x8fcd,0x8fd0,0x8fd2,
+ 0x8fd3,0x8fd5,0x8fe0,0x8fe3,0x8fe4,0x8fe8,0x8fee,0x8ff1,0x8ff5,0x8ff6,
+ 0x8ffb,0x8ffe,0x9002,0x9004,0x9008,0x900c,0x9018,0x901b,0x9028,0x9029,
+ 0x902f,0x902a,0x902c,0x902d,0x9033,0x9034,0x9037,0x903f,0x9043,0x9044,
+ 0x904c,0x905b,0x905d,0x9062,0x9066,0x9067,0x906c,0x9070,0x9074,0x9079,
+ 0x9085,0x9088,0x908b,0x908c,0x908e,0x9090,0x9095,0x9097,0x9098,0x9099,
+ 0x909b,0x90a0,0x90a1,0x90a2,0x90a5,0x90b0,0x90b2,0x90b3,0x90b4,0x90b6,
+ 0x90bd,0x90cc,0x90be,0x90c3
+ },
+ { /* ku 42 */
+ 0x90c4,0x90c5,0x90c7,0x90c8,0x90d5,0x90d7,0x90d8,0x90d9,0x90dc,0x90dd,
+ 0x90df,0x90e5,0x90d2,0x90f6,0x90eb,0x90ef,0x90f0,0x90f4,0x90fe,0x90ff,
+ 0x9100,0x9104,0x9105,0x9106,0x9108,0x910d,0x9110,0x9114,0x9116,0x9117,
+ 0x9118,0x911a,0x911c,0x911e,0x9120,0x9125,0x9122,0x9123,0x9127,0x9129,
+ 0x912e,0x912f,0x9131,0x9134,0x9136,0x9137,0x9139,0x913a,0x913c,0x913d,
+ 0x9143,0x9147,0x9148,0x914f,0x9153,0x9157,0x9159,0x915a,0x915b,0x9161,
+ 0x9164,0x9167,0x916d,0x9174,0x9179,0x917a,0x917b,0x9181,0x9183,0x9185,
+ 0x9186,0x918a,0x918e,0x9191,0x9193,0x9194,0x9195,0x9198,0x919e,0x91a1,
+ 0x91a6,0x91a8,0x91ac,0x91ad,0x91ae,0x91b0,0x91b1,0x91b2,0x91b3,0x91b6,
+ 0x91bb,0x91bc,0x91bd,0x91bf
+ },
+ { /* ku 43 */
+ 0x91c2,0x91c3,0x91c5,0x91d3,0x91d4,0x91d7,0x91d9,0x91da,0x91de,0x91e4,
+ 0x91e5,0x91e9,0x91ea,0x91ec,0x91ed,0x91ee,0x91ef,0x91f0,0x91f1,0x91f7,
+ 0x91f9,0x91fb,0x91fd,0x9200,0x9201,0x9204,0x9205,0x9206,0x9207,0x9209,
+ 0x920a,0x920c,0x9210,0x9212,0x9213,0x9216,0x9218,0x921c,0x921d,0x9223,
+ 0x9224,0x9225,0x9226,0x9228,0x922e,0x922f,0x9230,0x9233,0x9235,0x9236,
+ 0x9238,0x9239,0x923a,0x923c,0x923e,0x9240,0x9242,0x9243,0x9246,0x9247,
+ 0x924a,0x924d,0x924e,0x924f,0x9251,0x9258,0x9259,0x925c,0x925d,0x9260,
+ 0x9261,0x9265,0x9267,0x9268,0x9269,0x926e,0x926f,0x9270,0x9275,0x9276,
+ 0x9277,0x9278,0x9279,0x927b,0x927c,0x927d,0x927f,0x9288,0x9289,0x928a,
+ 0x928d,0x928e,0x9292,0x9297
+ },
+ { /* ku 44 */
+ 0x9299,0x929f,0x92a0,0x92a4,0x92a5,0x92a7,0x92a8,0x92ab,0x92af,0x92b2,
+ 0x92b6,0x92b8,0x92ba,0x92bb,0x92bc,0x92bd,0x92bf,0x92c0,0x92c1,0x92c2,
+ 0x92c3,0x92c5,0x92c6,0x92c7,0x92c8,0x92cb,0x92cc,0x92cd,0x92ce,0x92d0,
+ 0x92d3,0x92d5,0x92d7,0x92d8,0x92d9,0x92dc,0x92dd,0x92df,0x92e0,0x92e1,
+ 0x92e3,0x92e5,0x92e7,0x92e8,0x92ec,0x92ee,0x92f0,0x92f9,0x92fb,0x92ff,
+ 0x9300,0x9302,0x9308,0x930d,0x9311,0x9314,0x9315,0x931c,0x931d,0x931e,
+ 0x931f,0x9321,0x9324,0x9325,0x9327,0x9329,0x932a,0x9333,0x9334,0x9336,
+ 0x9337,0x9347,0x9348,0x9349,0x9350,0x9351,0x9352,0x9355,0x9357,0x9358,
+ 0x935a,0x935e,0x9364,0x9365,0x9367,0x9369,0x936a,0x936d,0x936f,0x9370,
+ 0x9371,0x9373,0x9374,0x9376
+ },
+ { /* ku 45 */
+ 0x937a,0x937d,0x937f,0x9380,0x9381,0x9382,0x9388,0x938a,0x938b,0x938d,
+ 0x938f,0x9392,0x9395,0x9398,0x939b,0x939e,0x93a1,0x93a3,0x93a4,0x93a6,
+ 0x93a8,0x93ab,0x93b4,0x93b5,0x93b6,0x93ba,0x93a9,0x93c1,0x93c4,0x93c5,
+ 0x93c6,0x93c7,0x93c9,0x93ca,0x93cb,0x93cc,0x93cd,0x93d3,0x93d9,0x93dc,
+ 0x93de,0x93df,0x93e2,0x93e6,0x93e7,0x93f9,0x93f7,0x93f8,0x93fa,0x93fb,
+ 0x93fd,0x9401,0x9402,0x9404,0x9408,0x9409,0x940d,0x940e,0x940f,0x9415,
+ 0x9416,0x9417,0x941f,0x942e,0x942f,0x9431,0x9432,0x9433,0x9434,0x943b,
+ 0x943f,0x943d,0x9443,0x9445,0x9448,0x944a,0x944c,0x9455,0x9459,0x945c,
+ 0x945f,0x9461,0x9463,0x9468,0x946b,0x946d,0x946e,0x946f,0x9471,0x9472,
+ 0x9484,0x9483,0x9578,0x9579
+ },
+ { /* ku 46 */
+ 0x957e,0x9584,0x9588,0x958c,0x958d,0x958e,0x959d,0x959e,0x959f,0x95a1,
+ 0x95a6,0x95a9,0x95ab,0x95ac,0x95b4,0x95b6,0x95ba,0x95bd,0x95bf,0x95c6,
+ 0x95c8,0x95c9,0x95cb,0x95d0,0x95d1,0x95d2,0x95d3,0x95d9,0x95da,0x95dd,
+ 0x95de,0x95df,0x95e0,0x95e4,0x95e6,0x961d,0x961e,0x9622,0x9624,0x9625,
+ 0x9626,0x962c,0x9631,0x9633,0x9637,0x9638,0x9639,0x963a,0x963c,0x963d,
+ 0x9641,0x9652,0x9654,0x9656,0x9657,0x9658,0x9661,0x966e,0x9674,0x967b,
+ 0x967c,0x967e,0x967f,0x9681,0x9682,0x9683,0x9684,0x9689,0x9691,0x9696,
+ 0x969a,0x969d,0x969f,0x96a4,0x96a5,0x96a6,0x96a9,0x96ae,0x96af,0x96b3,
+ 0x96ba,0x96ca,0x96d2,0x5db2,0x96d8,0x96da,0x96dd,0x96de,0x96df,0x96e9,
+ 0x96ef,0x96f1,0x96fa,0x9702
+ },
+ { /* ku 47 */
+ 0x9703,0x9705,0x9709,0x971a,0x971b,0x971d,0x9721,0x9722,0x9723,0x9728,
+ 0x9731,0x9733,0x9741,0x9743,0x974a,0x974e,0x974f,0x9755,0x9757,0x9758,
+ 0x975a,0x975b,0x9763,0x9767,0x976a,0x976e,0x9773,0x9776,0x9777,0x9778,
+ 0x977b,0x977d,0x977f,0x9780,0x9789,0x9795,0x9796,0x9797,0x9799,0x979a,
+ 0x979e,0x979f,0x97a2,0x97ac,0x97ae,0x97b1,0x97b2,0x97b5,0x97b6,0x97b8,
+ 0x97b9,0x97ba,0x97bc,0x97be,0x97bf,0x97c1,0x97c4,0x97c5,0x97c7,0x97c9,
+ 0x97ca,0x97cc,0x97cd,0x97ce,0x97d0,0x97d1,0x97d4,0x97d7,0x97d8,0x97d9,
+ 0x97dd,0x97de,0x97e0,0x97db,0x97e1,0x97e4,0x97ef,0x97f1,0x97f4,0x97f7,
+ 0x97f8,0x97fa,0x9807,0x980a,0x9819,0x980d,0x980e,0x9814,0x9816,0x981c,
+ 0x981e,0x9820,0x9823,0x9826
+ },
+ { /* ku 48 */
+ 0x982b,0x982e,0x982f,0x9830,0x9832,0x9833,0x9835,0x9825,0x983e,0x9844,
+ 0x9847,0x984a,0x9851,0x9852,0x9853,0x9856,0x9857,0x9859,0x985a,0x9862,
+ 0x9863,0x9865,0x9866,0x986a,0x986c,0x98ab,0x98ad,0x98ae,0x98b0,0x98b4,
+ 0x98b7,0x98b8,0x98ba,0x98bb,0x98bf,0x98c2,0x98c5,0x98c8,0x98cc,0x98e1,
+ 0x98e3,0x98e5,0x98e6,0x98e7,0x98ea,0x98f3,0x98f6,0x9902,0x9907,0x9908,
+ 0x9911,0x9915,0x9916,0x9917,0x991a,0x991b,0x991c,0x991f,0x9922,0x9926,
+ 0x9927,0x992b,0x9931,0x9932,0x9933,0x9934,0x9935,0x9939,0x993a,0x993b,
+ 0x993c,0x9940,0x9941,0x9946,0x9947,0x9948,0x994d,0x994e,0x9954,0x9958,
+ 0x9959,0x995b,0x995c,0x995e,0x995f,0x9960,0x999b,0x999d,0x999f,0x99a6,
+ 0x99b0,0x99b1,0x99b2,0x99b5
+ },
+ { /* ku 49 */
+ 0x99b9,0x99ba,0x99bd,0x99bf,0x99c3,0x99c9,0x99d3,0x99d4,0x99d9,0x99da,
+ 0x99dc,0x99de,0x99e7,0x99ea,0x99eb,0x99ec,0x99f0,0x99f4,0x99f5,0x99f9,
+ 0x99fd,0x99fe,0x9a02,0x9a03,0x9a04,0x9a0b,0x9a0c,0x9a10,0x9a11,0x9a16,
+ 0x9a1e,0x9a20,0x9a22,0x9a23,0x9a24,0x9a27,0x9a2d,0x9a2e,0x9a33,0x9a35,
+ 0x9a36,0x9a38,0x9a47,0x9a41,0x9a44,0x9a4a,0x9a4b,0x9a4c,0x9a4e,0x9a51,
+ 0x9a54,0x9a56,0x9a5d,0x9aaa,0x9aac,0x9aae,0x9aaf,0x9ab2,0x9ab4,0x9ab5,
+ 0x9ab6,0x9ab9,0x9abb,0x9abe,0x9abf,0x9ac1,0x9ac3,0x9ac6,0x9ac8,0x9ace,
+ 0x9ad0,0x9ad2,0x9ad5,0x9ad6,0x9ad7,0x9adb,0x9adc,0x9ae0,0x9ae4,0x9ae5,
+ 0x9ae7,0x9ae9,0x9aec,0x9af2,0x9af3,0x9af5,0x9af9,0x9afa,0x9afd,0x9aff,
+ 0x9b00,0x9b01,0x9b02,0x9b03
+ },
+ { /* ku 4a */
+ 0x9b04,0x9b05,0x9b08,0x9b09,0x9b0b,0x9b0c,0x9b0d,0x9b0e,0x9b10,0x9b12,
+ 0x9b16,0x9b19,0x9b1b,0x9b1c,0x9b20,0x9b26,0x9b2b,0x9b2d,0x9b33,0x9b34,
+ 0x9b35,0x9b37,0x9b39,0x9b3a,0x9b3d,0x9b48,0x9b4b,0x9b4c,0x9b55,0x9b56,
+ 0x9b57,0x9b5b,0x9b5e,0x9b61,0x9b63,0x9b65,0x9b66,0x9b68,0x9b6a,0x9b6b,
+ 0x9b6c,0x9b6d,0x9b6e,0x9b73,0x9b75,0x9b77,0x9b78,0x9b79,0x9b7f,0x9b80,
+ 0x9b84,0x9b85,0x9b86,0x9b87,0x9b89,0x9b8a,0x9b8b,0x9b8d,0x9b8f,0x9b90,
+ 0x9b94,0x9b9a,0x9b9d,0x9b9e,0x9ba6,0x9ba7,0x9ba9,0x9bac,0x9bb0,0x9bb1,
+ 0x9bb2,0x9bb7,0x9bb8,0x9bbb,0x9bbc,0x9bbe,0x9bbf,0x9bc1,0x9bc7,0x9bc8,
+ 0x9bce,0x9bd0,0x9bd7,0x9bd8,0x9bdd,0x9bdf,0x9be5,0x9be7,0x9bea,0x9beb,
+ 0x9bef,0x9bf3,0x9bf7,0x9bf8
+ },
+ { /* ku 4b */
+ 0x9bf9,0x9bfa,0x9bfd,0x9bff,0x9c00,0x9c02,0x9c0b,0x9c0f,0x9c11,0x9c16,
+ 0x9c18,0x9c19,0x9c1a,0x9c1c,0x9c1e,0x9c22,0x9c23,0x9c26,0x9c27,0x9c28,
+ 0x9c29,0x9c2a,0x9c31,0x9c35,0x9c36,0x9c37,0x9c3d,0x9c41,0x9c43,0x9c44,
+ 0x9c45,0x9c49,0x9c4a,0x9c4e,0x9c4f,0x9c50,0x9c53,0x9c54,0x9c56,0x9c58,
+ 0x9c5b,0x9c5d,0x9c5e,0x9c5f,0x9c63,0x9c69,0x9c6a,0x9c5c,0x9c6b,0x9c68,
+ 0x9c6e,0x9c70,0x9c72,0x9c75,0x9c77,0x9c7b,0x9ce6,0x9cf2,0x9cf7,0x9cf9,
+ 0x9d0b,0x9d02,0x9d11,0x9d17,0x9d18,0x9d1c,0x9d1d,0x9d1e,0x9d2f,0x9d30,
+ 0x9d32,0x9d33,0x9d34,0x9d3a,0x9d3c,0x9d45,0x9d3d,0x9d42,0x9d43,0x9d47,
+ 0x9d4a,0x9d53,0x9d54,0x9d5f,0x9d63,0x9d62,0x9d65,0x9d69,0x9d6a,0x9d6b,
+ 0x9d70,0x9d76,0x9d77,0x9d7b
+ },
+ { /* ku 4c */
+ 0x9d7c,0x9d7e,0x9d83,0x9d84,0x9d86,0x9d8a,0x9d8d,0x9d8e,0x9d92,0x9d93,
+ 0x9d95,0x9d96,0x9d97,0x9d98,0x9da1,0x9daa,0x9dac,0x9dae,0x9db1,0x9db5,
+ 0x9db9,0x9dbc,0x9dbf,0x9dc3,0x9dc7,0x9dc9,0x9dca,0x9dd4,0x9dd5,0x9dd6,
+ 0x9dd7,0x9dda,0x9dde,0x9ddf,0x9de0,0x9de5,0x9de7,0x9de9,0x9deb,0x9dee,
+ 0x9df0,0x9df3,0x9df4,0x9dfe,0x9e0a,0x9e02,0x9e07,0x9e0e,0x9e10,0x9e11,
+ 0x9e12,0x9e15,0x9e16,0x9e19,0x9e1c,0x9e1d,0x9e7a,0x9e7b,0x9e7c,0x9e80,
+ 0x9e82,0x9e83,0x9e84,0x9e85,0x9e87,0x9e8e,0x9e8f,0x9e96,0x9e98,0x9e9b,
+ 0x9e9e,0x9ea4,0x9ea8,0x9eac,0x9eae,0x9eaf,0x9eb0,0x9eb3,0x9eb4,0x9eb5,
+ 0x9ec6,0x9ec8,0x9ecb,0x9ed5,0x9edf,0x9ee4,0x9ee7,0x9eec,0x9eed,0x9eee,
+ 0x9ef0,0x9ef1,0x9ef2,0x9ef5
+ },
+ { /* ku 4d */
+ 0x9ef8,0x9eff,0x9f02,0x9f03,0x9f09,0x9f0f,0x9f10,0x9f11,0x9f12,0x9f14,
+ 0x9f16,0x9f17,0x9f19,0x9f1a,0x9f1b,0x9f1f,0x9f22,0x9f26,0x9f2a,0x9f2b,
+ 0x9f2f,0x9f31,0x9f32,0x9f34,0x9f37,0x9f39,0x9f3a,0x9f3c,0x9f3d,0x9f3f,
+ 0x9f41,0x9f43,0x9f44,0x9f45,0x9f46,0x9f47,0x9f53,0x9f55,0x9f56,0x9f57,
+ 0x9f58,0x9f5a,0x9f5d,0x9f5e,0x9f68,0x9f69,0x9f6d,0x9f6e,0x9f6f,0x9f70,
+ 0x9f71,0x9f73,0x9f75,0x9f7a,0x9f7d,0x9f8f,0x9f90,0x9f91,0x9f92,0x9f94,
+ 0x9f96,0x9f97,0x9f9e,0x9fa1,0x9fa2,0x9fa3,0x9fa5,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON
+ }
+};
diff --git a/imap/src/charset/koi8_r.c b/imap/src/charset/koi8_r.c
new file mode 100644
index 00000000..92dfcbcd
--- /dev/null
+++ b/imap/src/charset/koi8_r.c
@@ -0,0 +1,48 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: KOI8-R conversion table
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 July 1997
+ * Last Edited: 30 August 2006
+ */
+
+/* KOI8-R is a de-facto standard of Russia */
+
+static const unsigned short koi8rtab[128] = {
+ 0x2500,0x2502,0x250c,0x2510,0x2514,0x2518,0x251c,0x2524,
+ 0x252c,0x2534,0x253c,0x2580,0x2584,0x2588,0x258c,0x2590,
+ 0x2591,0x2592,0x2593,0x2320,0x25a0,0x2219,0x221a,0x2248,
+ 0x2264,0x2265,0x00a0,0x2321,0x00b0,0x00b2,0x00b7,0x00f7,
+ 0x2550,0x2551,0x2552,0x0451,0x2553,0x2554,0x2555,0x2556,
+ 0x2557,0x2558,0x2559,0x255a,0x255b,0x255c,0x255d,0x255e,
+ 0x255f,0x2560,0x2561,0x0401,0x2562,0x2563,0x2564,0x2565,
+ 0x2566,0x2567,0x2568,0x2569,0x256a,0x256b,0x256c,0x00a9,
+ 0x044e,0x0430,0x0431,0x0446,0x0434,0x0435,0x0444,0x0433,
+ 0x0445,0x0438,0x0439,0x043a,0x043b,0x043c,0x043d,0x043e,
+ 0x043f,0x044f,0x0440,0x0441,0x0442,0x0443,0x0436,0x0432,
+ 0x044c,0x044b,0x0437,0x0448,0x044d,0x0449,0x0447,0x044a,
+ 0x042e,0x0410,0x0411,0x0426,0x0414,0x0415,0x0424,0x0413,
+ 0x0425,0x0418,0x0419,0x041a,0x041b,0x041c,0x041d,0x041e,
+ 0x041f,0x042f,0x0420,0x0421,0x0422,0x0423,0x0416,0x0412,
+ 0x042c,0x042b,0x0417,0x0428,0x042d,0x0429,0x0427,0x042a
+};
diff --git a/imap/src/charset/koi8_u.c b/imap/src/charset/koi8_u.c
new file mode 100644
index 00000000..4fc8507b
--- /dev/null
+++ b/imap/src/charset/koi8_u.c
@@ -0,0 +1,48 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: KOI8-U conversion table
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 25 August 1997
+ * Last Edited: 30 August 2006
+ */
+
+/* KOI8-U is a de-facto standard of Ukraine */
+
+static const unsigned short koi8utab[128] = {
+ 0x2500,0x2502,0x250c,0x2510,0x2514,0x2518,0x251c,0x2524,
+ 0x252c,0x2534,0x253c,0x2580,0x2584,0x2588,0x258c,0x2590,
+ 0x2591,0x2592,0x2593,0x2320,0x25a0,0x2219,0x221a,0x2248,
+ 0x2264,0x2265,0x00a0,0x2321,0x00b0,0x00b2,0x00b7,0x00f7,
+ 0x2550,0x2551,0x2552,0x0451,0x0454,0x2554,0x0456,0x0457,
+ 0x2557,0x2558,0x2559,0x255a,0x255b,0x0491,0x255d,0x255e,
+ 0x255f,0x2560,0x2561,0x0401,0x0403,0x2563,0x0406,0x0407,
+ 0x2566,0x2567,0x2568,0x2569,0x256a,0x0490,0x256c,0x00a9,
+ 0x044e,0x0430,0x0431,0x0446,0x0434,0x0435,0x0444,0x0433,
+ 0x0445,0x0438,0x0439,0x043a,0x043b,0x043c,0x043d,0x043e,
+ 0x043f,0x044f,0x0440,0x0441,0x0442,0x0443,0x0436,0x0432,
+ 0x044c,0x044b,0x0437,0x0448,0x044d,0x0449,0x0447,0x044a,
+ 0x042e,0x0410,0x0411,0x0426,0x0414,0x0415,0x0424,0x0413,
+ 0x0425,0x0418,0x0419,0x041a,0x041b,0x041c,0x041d,0x041e,
+ 0x041f,0x042f,0x0420,0x0421,0x0422,0x0423,0x0416,0x0412,
+ 0x042c,0x042b,0x0417,0x0428,0x042d,0x0429,0x0427,0x042a
+};
diff --git a/imap/src/charset/ksc_5601.c b/imap/src/charset/ksc_5601.c
new file mode 100644
index 00000000..43a2f73b
--- /dev/null
+++ b/imap/src/charset/ksc_5601.c
@@ -0,0 +1,2673 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: KSC 5601 conversion table
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 July 1997
+ * Last Edited: 30 August 2006
+ */
+
+/* KSC 5601 is the national standard of the Republic of Korea (South Korea).
+ * It is believed that it is also the de-facto standard of the Democratic
+ * People's Republic of Korea (North Korea), although North Korea has its
+ * own official national standard (KPS 9566-97).
+ */
+
+#define BASE_KSC5601_KU 0x81
+#define BASE_KSC5601_TEN 0x41
+#define MAX_KSC5601_KU 125
+#define MAX_KSC5601_TEN 190
+
+
+#define KSCTOUNICODE(c,c1,ku,ten) \
+ ((((ku = c - BASE_KSC5601_KU) < MAX_KSC5601_KU) && \
+ ((ten = c1 - BASE_KSC5601_TEN) < MAX_KSC5601_TEN)) ? \
+ ksc5601tab[ku][ten] : UBOGON)
+
+
+static const unsigned short ksc5601tab[MAX_KSC5601_KU][MAX_KSC5601_TEN] = {
+ { /* ku 01 */
+ 0xac02,0xac03,0xac05,0xac06,0xac0b,0xac0c,0xac0d,0xac0e,0xac0f,0xac18,
+ 0xac1e,0xac1f,0xac21,0xac22,0xac23,0xac25,0xac26,0xac27,0xac28,0xac29,
+ 0xac2a,0xac2b,0xac2e,0xac32,0xac33,0xac34,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xac35,0xac36,0xac37,0xac3a,0xac3b,0xac3d,0xac3e,0xac3f,
+ 0xac41,0xac42,0xac43,0xac44,0xac45,0xac46,0xac47,0xac48,0xac49,0xac4a,
+ 0xac4c,0xac4e,0xac4f,0xac50,0xac51,0xac52,0xac53,0xac55,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xac56,0xac57,0xac59,0xac5a,0xac5b,0xac5d,
+ 0xac5e,0xac5f,0xac60,0xac61,0xac62,0xac63,0xac64,0xac65,0xac66,0xac67,
+ 0xac68,0xac69,0xac6a,0xac6b,0xac6c,0xac6d,0xac6e,0xac6f,0xac72,0xac73,
+ 0xac75,0xac76,0xac79,0xac7b,0xac7c,0xac7d,0xac7e,0xac7f,0xac82,0xac87,
+ 0xac88,0xac8d,0xac8e,0xac8f,0xac91,0xac92,0xac93,0xac95,0xac96,0xac97,
+ 0xac98,0xac99,0xac9a,0xac9b,0xac9e,0xaca2,0xaca3,0xaca4,0xaca5,0xaca6,
+ 0xaca7,0xacab,0xacad,0xacae,0xacb1,0xacb2,0xacb3,0xacb4,0xacb5,0xacb6,
+ 0xacb7,0xacba,0xacbe,0xacbf,0xacc0,0xacc2,0xacc3,0xacc5,0xacc6,0xacc7,
+ 0xacc9,0xacca,0xaccb,0xaccd,0xacce,0xaccf,0xacd0,0xacd1,0xacd2,0xacd3,
+ 0xacd4,0xacd6,0xacd8,0xacd9,0xacda,0xacdb,0xacdc,0xacdd,0xacde,0xacdf,
+ 0xace2,0xace3,0xace5,0xace6,0xace9,0xaceb,0xaced,0xacee,0xacf2,0xacf4,
+ 0xacf7,0xacf8,0xacf9,0xacfa,0xacfb,0xacfe,0xacff,0xad01,0xad02,0xad03,
+ 0xad05,0xad07,0xad08,0xad09,0xad0a,0xad0b,0xad0e,0xad10,0xad12,0xad13
+ },
+ { /* ku 02 */
+ 0xad14,0xad15,0xad16,0xad17,0xad19,0xad1a,0xad1b,0xad1d,0xad1e,0xad1f,
+ 0xad21,0xad22,0xad23,0xad24,0xad25,0xad26,0xad27,0xad28,0xad2a,0xad2b,
+ 0xad2e,0xad2f,0xad30,0xad31,0xad32,0xad33,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xad36,0xad37,0xad39,0xad3a,0xad3b,0xad3d,0xad3e,0xad3f,
+ 0xad40,0xad41,0xad42,0xad43,0xad46,0xad48,0xad4a,0xad4b,0xad4c,0xad4d,
+ 0xad4e,0xad4f,0xad51,0xad52,0xad53,0xad55,0xad56,0xad57,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xad59,0xad5a,0xad5b,0xad5c,0xad5d,0xad5e,
+ 0xad5f,0xad60,0xad62,0xad64,0xad65,0xad66,0xad67,0xad68,0xad69,0xad6a,
+ 0xad6b,0xad6e,0xad6f,0xad71,0xad72,0xad77,0xad78,0xad79,0xad7a,0xad7e,
+ 0xad80,0xad83,0xad84,0xad85,0xad86,0xad87,0xad8a,0xad8b,0xad8d,0xad8e,
+ 0xad8f,0xad91,0xad92,0xad93,0xad94,0xad95,0xad96,0xad97,0xad98,0xad99,
+ 0xad9a,0xad9b,0xad9e,0xad9f,0xada0,0xada1,0xada2,0xada3,0xada5,0xada6,
+ 0xada7,0xada8,0xada9,0xadaa,0xadab,0xadac,0xadad,0xadae,0xadaf,0xadb0,
+ 0xadb1,0xadb2,0xadb3,0xadb4,0xadb5,0xadb6,0xadb8,0xadb9,0xadba,0xadbb,
+ 0xadbc,0xadbd,0xadbe,0xadbf,0xadc2,0xadc3,0xadc5,0xadc6,0xadc7,0xadc9,
+ 0xadca,0xadcb,0xadcc,0xadcd,0xadce,0xadcf,0xadd2,0xadd4,0xadd5,0xadd6,
+ 0xadd7,0xadd8,0xadd9,0xadda,0xaddb,0xaddd,0xadde,0xaddf,0xade1,0xade2,
+ 0xade3,0xade5,0xade6,0xade7,0xade8,0xade9,0xadea,0xadeb,0xadec,0xaded,
+ 0xadee,0xadef,0xadf0,0xadf1,0xadf2,0xadf3,0xadf4,0xadf5,0xadf6,0xadf7
+ },
+ { /* ku 03 */
+ 0xadfa,0xadfb,0xadfd,0xadfe,0xae02,0xae03,0xae04,0xae05,0xae06,0xae07,
+ 0xae0a,0xae0c,0xae0e,0xae0f,0xae10,0xae11,0xae12,0xae13,0xae15,0xae16,
+ 0xae17,0xae18,0xae19,0xae1a,0xae1b,0xae1c,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xae1d,0xae1e,0xae1f,0xae20,0xae21,0xae22,0xae23,0xae24,
+ 0xae25,0xae26,0xae27,0xae28,0xae29,0xae2a,0xae2b,0xae2c,0xae2d,0xae2e,
+ 0xae2f,0xae32,0xae33,0xae35,0xae36,0xae39,0xae3b,0xae3c,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xae3d,0xae3e,0xae3f,0xae42,0xae44,0xae47,
+ 0xae48,0xae49,0xae4b,0xae4f,0xae51,0xae52,0xae53,0xae55,0xae57,0xae58,
+ 0xae59,0xae5a,0xae5b,0xae5e,0xae62,0xae63,0xae64,0xae66,0xae67,0xae6a,
+ 0xae6b,0xae6d,0xae6e,0xae6f,0xae71,0xae72,0xae73,0xae74,0xae75,0xae76,
+ 0xae77,0xae7a,0xae7e,0xae7f,0xae80,0xae81,0xae82,0xae83,0xae86,0xae87,
+ 0xae88,0xae89,0xae8a,0xae8b,0xae8d,0xae8e,0xae8f,0xae90,0xae91,0xae92,
+ 0xae93,0xae94,0xae95,0xae96,0xae97,0xae98,0xae99,0xae9a,0xae9b,0xae9c,
+ 0xae9d,0xae9e,0xae9f,0xaea0,0xaea1,0xaea2,0xaea3,0xaea4,0xaea5,0xaea6,
+ 0xaea7,0xaea8,0xaea9,0xaeaa,0xaeab,0xaeac,0xaead,0xaeae,0xaeaf,0xaeb0,
+ 0xaeb1,0xaeb2,0xaeb3,0xaeb4,0xaeb5,0xaeb6,0xaeb7,0xaeb8,0xaeb9,0xaeba,
+ 0xaebb,0xaebf,0xaec1,0xaec2,0xaec3,0xaec5,0xaec6,0xaec7,0xaec8,0xaec9,
+ 0xaeca,0xaecb,0xaece,0xaed2,0xaed3,0xaed4,0xaed5,0xaed6,0xaed7,0xaeda,
+ 0xaedb,0xaedd,0xaede,0xaedf,0xaee0,0xaee1,0xaee2,0xaee3,0xaee4,0xaee5
+ },
+ { /* ku 04 */
+ 0xaee6,0xaee7,0xaee9,0xaeea,0xaeec,0xaeee,0xaeef,0xaef0,0xaef1,0xaef2,
+ 0xaef3,0xaef5,0xaef6,0xaef7,0xaef9,0xaefa,0xaefb,0xaefd,0xaefe,0xaeff,
+ 0xaf00,0xaf01,0xaf02,0xaf03,0xaf04,0xaf05,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xaf06,0xaf09,0xaf0a,0xaf0b,0xaf0c,0xaf0e,0xaf0f,0xaf11,
+ 0xaf12,0xaf13,0xaf14,0xaf15,0xaf16,0xaf17,0xaf18,0xaf19,0xaf1a,0xaf1b,
+ 0xaf1c,0xaf1d,0xaf1e,0xaf1f,0xaf20,0xaf21,0xaf22,0xaf23,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xaf24,0xaf25,0xaf26,0xaf27,0xaf28,0xaf29,
+ 0xaf2a,0xaf2b,0xaf2e,0xaf2f,0xaf31,0xaf33,0xaf35,0xaf36,0xaf37,0xaf38,
+ 0xaf39,0xaf3a,0xaf3b,0xaf3e,0xaf40,0xaf44,0xaf45,0xaf46,0xaf47,0xaf4a,
+ 0xaf4b,0xaf4c,0xaf4d,0xaf4e,0xaf4f,0xaf51,0xaf52,0xaf53,0xaf54,0xaf55,
+ 0xaf56,0xaf57,0xaf58,0xaf59,0xaf5a,0xaf5b,0xaf5e,0xaf5f,0xaf60,0xaf61,
+ 0xaf62,0xaf63,0xaf66,0xaf67,0xaf68,0xaf69,0xaf6a,0xaf6b,0xaf6c,0xaf6d,
+ 0xaf6e,0xaf6f,0xaf70,0xaf71,0xaf72,0xaf73,0xaf74,0xaf75,0xaf76,0xaf77,
+ 0xaf78,0xaf7a,0xaf7b,0xaf7c,0xaf7d,0xaf7e,0xaf7f,0xaf81,0xaf82,0xaf83,
+ 0xaf85,0xaf86,0xaf87,0xaf89,0xaf8a,0xaf8b,0xaf8c,0xaf8d,0xaf8e,0xaf8f,
+ 0xaf92,0xaf93,0xaf94,0xaf96,0xaf97,0xaf98,0xaf99,0xaf9a,0xaf9b,0xaf9d,
+ 0xaf9e,0xaf9f,0xafa0,0xafa1,0xafa2,0xafa3,0xafa4,0xafa5,0xafa6,0xafa7,
+ 0xafa8,0xafa9,0xafaa,0xafab,0xafac,0xafad,0xafae,0xafaf,0xafb0,0xafb1,
+ 0xafb2,0xafb3,0xafb4,0xafb5,0xafb6,0xafb7,0xafba,0xafbb,0xafbd,0xafbe
+ },
+ { /* ku 05 */
+ 0xafbf,0xafc1,0xafc2,0xafc3,0xafc4,0xafc5,0xafc6,0xafca,0xafcc,0xafcf,
+ 0xafd0,0xafd1,0xafd2,0xafd3,0xafd5,0xafd6,0xafd7,0xafd8,0xafd9,0xafda,
+ 0xafdb,0xafdd,0xafde,0xafdf,0xafe0,0xafe1,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xafe2,0xafe3,0xafe4,0xafe5,0xafe6,0xafe7,0xafea,0xafeb,
+ 0xafec,0xafed,0xafee,0xafef,0xaff2,0xaff3,0xaff5,0xaff6,0xaff7,0xaff9,
+ 0xaffa,0xaffb,0xaffc,0xaffd,0xaffe,0xafff,0xb002,0xb003,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xb005,0xb006,0xb007,0xb008,0xb009,0xb00a,
+ 0xb00b,0xb00d,0xb00e,0xb00f,0xb011,0xb012,0xb013,0xb015,0xb016,0xb017,
+ 0xb018,0xb019,0xb01a,0xb01b,0xb01e,0xb01f,0xb020,0xb021,0xb022,0xb023,
+ 0xb024,0xb025,0xb026,0xb027,0xb029,0xb02a,0xb02b,0xb02c,0xb02d,0xb02e,
+ 0xb02f,0xb030,0xb031,0xb032,0xb033,0xb034,0xb035,0xb036,0xb037,0xb038,
+ 0xb039,0xb03a,0xb03b,0xb03c,0xb03d,0xb03e,0xb03f,0xb040,0xb041,0xb042,
+ 0xb043,0xb046,0xb047,0xb049,0xb04b,0xb04d,0xb04f,0xb050,0xb051,0xb052,
+ 0xb056,0xb058,0xb05a,0xb05b,0xb05c,0xb05e,0xb05f,0xb060,0xb061,0xb062,
+ 0xb063,0xb064,0xb065,0xb066,0xb067,0xb068,0xb069,0xb06a,0xb06b,0xb06c,
+ 0xb06d,0xb06e,0xb06f,0xb070,0xb071,0xb072,0xb073,0xb074,0xb075,0xb076,
+ 0xb077,0xb078,0xb079,0xb07a,0xb07b,0xb07e,0xb07f,0xb081,0xb082,0xb083,
+ 0xb085,0xb086,0xb087,0xb088,0xb089,0xb08a,0xb08b,0xb08e,0xb090,0xb092,
+ 0xb093,0xb094,0xb095,0xb096,0xb097,0xb09b,0xb09d,0xb09e,0xb0a3,0xb0a4
+ },
+ { /* ku 06 */
+ 0xb0a5,0xb0a6,0xb0a7,0xb0aa,0xb0b0,0xb0b2,0xb0b6,0xb0b7,0xb0b9,0xb0ba,
+ 0xb0bb,0xb0bd,0xb0be,0xb0bf,0xb0c0,0xb0c1,0xb0c2,0xb0c3,0xb0c6,0xb0ca,
+ 0xb0cb,0xb0cc,0xb0cd,0xb0ce,0xb0cf,0xb0d2,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xb0d3,0xb0d5,0xb0d6,0xb0d7,0xb0d9,0xb0da,0xb0db,0xb0dc,
+ 0xb0dd,0xb0de,0xb0df,0xb0e1,0xb0e2,0xb0e3,0xb0e4,0xb0e6,0xb0e7,0xb0e8,
+ 0xb0e9,0xb0ea,0xb0eb,0xb0ec,0xb0ed,0xb0ee,0xb0ef,0xb0f0,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xb0f1,0xb0f2,0xb0f3,0xb0f4,0xb0f5,0xb0f6,
+ 0xb0f7,0xb0f8,0xb0f9,0xb0fa,0xb0fb,0xb0fc,0xb0fd,0xb0fe,0xb0ff,0xb100,
+ 0xb101,0xb102,0xb103,0xb104,0xb105,0xb106,0xb107,0xb10a,0xb10d,0xb10e,
+ 0xb10f,0xb111,0xb114,0xb115,0xb116,0xb117,0xb11a,0xb11e,0xb11f,0xb120,
+ 0xb121,0xb122,0xb126,0xb127,0xb129,0xb12a,0xb12b,0xb12d,0xb12e,0xb12f,
+ 0xb130,0xb131,0xb132,0xb133,0xb136,0xb13a,0xb13b,0xb13c,0xb13d,0xb13e,
+ 0xb13f,0xb142,0xb143,0xb145,0xb146,0xb147,0xb149,0xb14a,0xb14b,0xb14c,
+ 0xb14d,0xb14e,0xb14f,0xb152,0xb153,0xb156,0xb157,0xb159,0xb15a,0xb15b,
+ 0xb15d,0xb15e,0xb15f,0xb161,0xb162,0xb163,0xb164,0xb165,0xb166,0xb167,
+ 0xb168,0xb169,0xb16a,0xb16b,0xb16c,0xb16d,0xb16e,0xb16f,0xb170,0xb171,
+ 0xb172,0xb173,0xb174,0xb175,0xb176,0xb177,0xb17a,0xb17b,0xb17d,0xb17e,
+ 0xb17f,0xb181,0xb183,0xb184,0xb185,0xb186,0xb187,0xb18a,0xb18c,0xb18e,
+ 0xb18f,0xb190,0xb191,0xb195,0xb196,0xb197,0xb199,0xb19a,0xb19b,0xb19d
+ },
+ { /* ku 07 */
+ 0xb19e,0xb19f,0xb1a0,0xb1a1,0xb1a2,0xb1a3,0xb1a4,0xb1a5,0xb1a6,0xb1a7,
+ 0xb1a9,0xb1aa,0xb1ab,0xb1ac,0xb1ad,0xb1ae,0xb1af,0xb1b0,0xb1b1,0xb1b2,
+ 0xb1b3,0xb1b4,0xb1b5,0xb1b6,0xb1b7,0xb1b8,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xb1b9,0xb1ba,0xb1bb,0xb1bc,0xb1bd,0xb1be,0xb1bf,0xb1c0,
+ 0xb1c1,0xb1c2,0xb1c3,0xb1c4,0xb1c5,0xb1c6,0xb1c7,0xb1c8,0xb1c9,0xb1ca,
+ 0xb1cb,0xb1cd,0xb1ce,0xb1cf,0xb1d1,0xb1d2,0xb1d3,0xb1d5,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xb1d6,0xb1d7,0xb1d8,0xb1d9,0xb1da,0xb1db,
+ 0xb1de,0xb1e0,0xb1e1,0xb1e2,0xb1e3,0xb1e4,0xb1e5,0xb1e6,0xb1e7,0xb1ea,
+ 0xb1eb,0xb1ed,0xb1ee,0xb1ef,0xb1f1,0xb1f2,0xb1f3,0xb1f4,0xb1f5,0xb1f6,
+ 0xb1f7,0xb1f8,0xb1fa,0xb1fc,0xb1fe,0xb1ff,0xb200,0xb201,0xb202,0xb203,
+ 0xb206,0xb207,0xb209,0xb20a,0xb20d,0xb20e,0xb20f,0xb210,0xb211,0xb212,
+ 0xb213,0xb216,0xb218,0xb21a,0xb21b,0xb21c,0xb21d,0xb21e,0xb21f,0xb221,
+ 0xb222,0xb223,0xb224,0xb225,0xb226,0xb227,0xb228,0xb229,0xb22a,0xb22b,
+ 0xb22c,0xb22d,0xb22e,0xb22f,0xb230,0xb231,0xb232,0xb233,0xb235,0xb236,
+ 0xb237,0xb238,0xb239,0xb23a,0xb23b,0xb23d,0xb23e,0xb23f,0xb240,0xb241,
+ 0xb242,0xb243,0xb244,0xb245,0xb246,0xb247,0xb248,0xb249,0xb24a,0xb24b,
+ 0xb24c,0xb24d,0xb24e,0xb24f,0xb250,0xb251,0xb252,0xb253,0xb254,0xb255,
+ 0xb256,0xb257,0xb259,0xb25a,0xb25b,0xb25d,0xb25e,0xb25f,0xb261,0xb262,
+ 0xb263,0xb264,0xb265,0xb266,0xb267,0xb26a,0xb26b,0xb26c,0xb26d,0xb26e
+ },
+ { /* ku 08 */
+ 0xb26f,0xb270,0xb271,0xb272,0xb273,0xb276,0xb277,0xb278,0xb279,0xb27a,
+ 0xb27b,0xb27d,0xb27e,0xb27f,0xb280,0xb281,0xb282,0xb283,0xb286,0xb287,
+ 0xb288,0xb28a,0xb28b,0xb28c,0xb28d,0xb28e,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xb28f,0xb292,0xb293,0xb295,0xb296,0xb297,0xb29b,0xb29c,
+ 0xb29d,0xb29e,0xb29f,0xb2a2,0xb2a4,0xb2a7,0xb2a8,0xb2a9,0xb2ab,0xb2ad,
+ 0xb2ae,0xb2af,0xb2b1,0xb2b2,0xb2b3,0xb2b5,0xb2b6,0xb2b7,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xb2b8,0xb2b9,0xb2ba,0xb2bb,0xb2bc,0xb2bd,
+ 0xb2be,0xb2bf,0xb2c0,0xb2c1,0xb2c2,0xb2c3,0xb2c4,0xb2c5,0xb2c6,0xb2c7,
+ 0xb2ca,0xb2cb,0xb2cd,0xb2ce,0xb2cf,0xb2d1,0xb2d3,0xb2d4,0xb2d5,0xb2d6,
+ 0xb2d7,0xb2da,0xb2dc,0xb2de,0xb2df,0xb2e0,0xb2e1,0xb2e3,0xb2e7,0xb2e9,
+ 0xb2ea,0xb2f0,0xb2f1,0xb2f2,0xb2f6,0xb2fc,0xb2fd,0xb2fe,0xb302,0xb303,
+ 0xb305,0xb306,0xb307,0xb309,0xb30a,0xb30b,0xb30c,0xb30d,0xb30e,0xb30f,
+ 0xb312,0xb316,0xb317,0xb318,0xb319,0xb31a,0xb31b,0xb31d,0xb31e,0xb31f,
+ 0xb320,0xb321,0xb322,0xb323,0xb324,0xb325,0xb326,0xb327,0xb328,0xb329,
+ 0xb32a,0xb32b,0xb32c,0xb32d,0xb32e,0xb32f,0xb330,0xb331,0xb332,0xb333,
+ 0xb334,0xb335,0xb336,0xb337,0xb338,0xb339,0xb33a,0xb33b,0xb33c,0xb33d,
+ 0xb33e,0xb33f,0xb340,0xb341,0xb342,0xb343,0xb344,0xb345,0xb346,0xb347,
+ 0xb348,0xb349,0xb34a,0xb34b,0xb34c,0xb34d,0xb34e,0xb34f,0xb350,0xb351,
+ 0xb352,0xb353,0xb357,0xb359,0xb35a,0xb35d,0xb360,0xb361,0xb362,0xb363
+ },
+ { /* ku 09 */
+ 0xb366,0xb368,0xb36a,0xb36c,0xb36d,0xb36f,0xb372,0xb373,0xb375,0xb376,
+ 0xb377,0xb379,0xb37a,0xb37b,0xb37c,0xb37d,0xb37e,0xb37f,0xb382,0xb386,
+ 0xb387,0xb388,0xb389,0xb38a,0xb38b,0xb38d,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xb38e,0xb38f,0xb391,0xb392,0xb393,0xb395,0xb396,0xb397,
+ 0xb398,0xb399,0xb39a,0xb39b,0xb39c,0xb39d,0xb39e,0xb39f,0xb3a2,0xb3a3,
+ 0xb3a4,0xb3a5,0xb3a6,0xb3a7,0xb3a9,0xb3aa,0xb3ab,0xb3ad,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xb3ae,0xb3af,0xb3b0,0xb3b1,0xb3b2,0xb3b3,
+ 0xb3b4,0xb3b5,0xb3b6,0xb3b7,0xb3b8,0xb3b9,0xb3ba,0xb3bb,0xb3bc,0xb3bd,
+ 0xb3be,0xb3bf,0xb3c0,0xb3c1,0xb3c2,0xb3c3,0xb3c6,0xb3c7,0xb3c9,0xb3ca,
+ 0xb3cd,0xb3cf,0xb3d1,0xb3d2,0xb3d3,0xb3d6,0xb3d8,0xb3da,0xb3dc,0xb3de,
+ 0xb3df,0xb3e1,0xb3e2,0xb3e3,0xb3e5,0xb3e6,0xb3e7,0xb3e9,0xb3ea,0xb3eb,
+ 0xb3ec,0xb3ed,0xb3ee,0xb3ef,0xb3f0,0xb3f1,0xb3f2,0xb3f3,0xb3f4,0xb3f5,
+ 0xb3f6,0xb3f7,0xb3f8,0xb3f9,0xb3fa,0xb3fb,0xb3fd,0xb3fe,0xb3ff,0xb400,
+ 0xb401,0xb402,0xb403,0xb404,0xb405,0xb406,0xb407,0xb408,0xb409,0xb40a,
+ 0xb40b,0xb40c,0xb40d,0xb40e,0xb40f,0xb411,0xb412,0xb413,0xb414,0xb415,
+ 0xb416,0xb417,0xb419,0xb41a,0xb41b,0xb41d,0xb41e,0xb41f,0xb421,0xb422,
+ 0xb423,0xb424,0xb425,0xb426,0xb427,0xb42a,0xb42c,0xb42d,0xb42e,0xb42f,
+ 0xb430,0xb431,0xb432,0xb433,0xb435,0xb436,0xb437,0xb438,0xb439,0xb43a,
+ 0xb43b,0xb43c,0xb43d,0xb43e,0xb43f,0xb440,0xb441,0xb442,0xb443,0xb444
+ },
+ { /* ku 0a */
+ 0xb445,0xb446,0xb447,0xb448,0xb449,0xb44a,0xb44b,0xb44c,0xb44d,0xb44e,
+ 0xb44f,0xb452,0xb453,0xb455,0xb456,0xb457,0xb459,0xb45a,0xb45b,0xb45c,
+ 0xb45d,0xb45e,0xb45f,0xb462,0xb464,0xb466,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xb467,0xb468,0xb469,0xb46a,0xb46b,0xb46d,0xb46e,0xb46f,
+ 0xb470,0xb471,0xb472,0xb473,0xb474,0xb475,0xb476,0xb477,0xb478,0xb479,
+ 0xb47a,0xb47b,0xb47c,0xb47d,0xb47e,0xb47f,0xb481,0xb482,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xb483,0xb484,0xb485,0xb486,0xb487,0xb489,
+ 0xb48a,0xb48b,0xb48c,0xb48d,0xb48e,0xb48f,0xb490,0xb491,0xb492,0xb493,
+ 0xb494,0xb495,0xb496,0xb497,0xb498,0xb499,0xb49a,0xb49b,0xb49c,0xb49e,
+ 0xb49f,0xb4a0,0xb4a1,0xb4a2,0xb4a3,0xb4a5,0xb4a6,0xb4a7,0xb4a9,0xb4aa,
+ 0xb4ab,0xb4ad,0xb4ae,0xb4af,0xb4b0,0xb4b1,0xb4b2,0xb4b3,0xb4b4,0xb4b6,
+ 0xb4b8,0xb4ba,0xb4bb,0xb4bc,0xb4bd,0xb4be,0xb4bf,0xb4c1,0xb4c2,0xb4c3,
+ 0xb4c5,0xb4c6,0xb4c7,0xb4c9,0xb4ca,0xb4cb,0xb4cc,0xb4cd,0xb4ce,0xb4cf,
+ 0xb4d1,0xb4d2,0xb4d3,0xb4d4,0xb4d6,0xb4d7,0xb4d8,0xb4d9,0xb4da,0xb4db,
+ 0xb4de,0xb4df,0xb4e1,0xb4e2,0xb4e5,0xb4e7,0xb4e8,0xb4e9,0xb4ea,0xb4eb,
+ 0xb4ee,0xb4f0,0xb4f2,0xb4f3,0xb4f4,0xb4f5,0xb4f6,0xb4f7,0xb4f9,0xb4fa,
+ 0xb4fb,0xb4fc,0xb4fd,0xb4fe,0xb4ff,0xb500,0xb501,0xb502,0xb503,0xb504,
+ 0xb505,0xb506,0xb507,0xb508,0xb509,0xb50a,0xb50b,0xb50c,0xb50d,0xb50e,
+ 0xb50f,0xb510,0xb511,0xb512,0xb513,0xb516,0xb517,0xb519,0xb51a,0xb51d
+ },
+ { /* ku 0b */
+ 0xb51e,0xb51f,0xb520,0xb521,0xb522,0xb523,0xb526,0xb52b,0xb52c,0xb52d,
+ 0xb52e,0xb52f,0xb532,0xb533,0xb535,0xb536,0xb537,0xb539,0xb53a,0xb53b,
+ 0xb53c,0xb53d,0xb53e,0xb53f,0xb542,0xb546,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xb547,0xb548,0xb549,0xb54a,0xb54e,0xb54f,0xb551,0xb552,
+ 0xb553,0xb555,0xb556,0xb557,0xb558,0xb559,0xb55a,0xb55b,0xb55e,0xb562,
+ 0xb563,0xb564,0xb565,0xb566,0xb567,0xb568,0xb569,0xb56a,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xb56b,0xb56c,0xb56d,0xb56e,0xb56f,0xb570,
+ 0xb571,0xb572,0xb573,0xb574,0xb575,0xb576,0xb577,0xb578,0xb579,0xb57a,
+ 0xb57b,0xb57c,0xb57d,0xb57e,0xb57f,0xb580,0xb581,0xb582,0xb583,0xb584,
+ 0xb585,0xb586,0xb587,0xb588,0xb589,0xb58a,0xb58b,0xb58c,0xb58d,0xb58e,
+ 0xb58f,0xb590,0xb591,0xb592,0xb593,0xb594,0xb595,0xb596,0xb597,0xb598,
+ 0xb599,0xb59a,0xb59b,0xb59c,0xb59d,0xb59e,0xb59f,0xb5a2,0xb5a3,0xb5a5,
+ 0xb5a6,0xb5a7,0xb5a9,0xb5ac,0xb5ad,0xb5ae,0xb5af,0xb5b2,0xb5b6,0xb5b7,
+ 0xb5b8,0xb5b9,0xb5ba,0xb5be,0xb5bf,0xb5c1,0xb5c2,0xb5c3,0xb5c5,0xb5c6,
+ 0xb5c7,0xb5c8,0xb5c9,0xb5ca,0xb5cb,0xb5ce,0xb5d2,0xb5d3,0xb5d4,0xb5d5,
+ 0xb5d6,0xb5d7,0xb5d9,0xb5da,0xb5db,0xb5dc,0xb5dd,0xb5de,0xb5df,0xb5e0,
+ 0xb5e1,0xb5e2,0xb5e3,0xb5e4,0xb5e5,0xb5e6,0xb5e7,0xb5e8,0xb5e9,0xb5ea,
+ 0xb5eb,0xb5ed,0xb5ee,0xb5ef,0xb5f0,0xb5f1,0xb5f2,0xb5f3,0xb5f4,0xb5f5,
+ 0xb5f6,0xb5f7,0xb5f8,0xb5f9,0xb5fa,0xb5fb,0xb5fc,0xb5fd,0xb5fe,0xb5ff
+ },
+ { /* ku 0c */
+ 0xb600,0xb601,0xb602,0xb603,0xb604,0xb605,0xb606,0xb607,0xb608,0xb609,
+ 0xb60a,0xb60b,0xb60c,0xb60d,0xb60e,0xb60f,0xb612,0xb613,0xb615,0xb616,
+ 0xb617,0xb619,0xb61a,0xb61b,0xb61c,0xb61d,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xb61e,0xb61f,0xb620,0xb621,0xb622,0xb623,0xb624,0xb626,
+ 0xb627,0xb628,0xb629,0xb62a,0xb62b,0xb62d,0xb62e,0xb62f,0xb630,0xb631,
+ 0xb632,0xb633,0xb635,0xb636,0xb637,0xb638,0xb639,0xb63a,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xb63b,0xb63c,0xb63d,0xb63e,0xb63f,0xb640,
+ 0xb641,0xb642,0xb643,0xb644,0xb645,0xb646,0xb647,0xb649,0xb64a,0xb64b,
+ 0xb64c,0xb64d,0xb64e,0xb64f,0xb650,0xb651,0xb652,0xb653,0xb654,0xb655,
+ 0xb656,0xb657,0xb658,0xb659,0xb65a,0xb65b,0xb65c,0xb65d,0xb65e,0xb65f,
+ 0xb660,0xb661,0xb662,0xb663,0xb665,0xb666,0xb667,0xb669,0xb66a,0xb66b,
+ 0xb66c,0xb66d,0xb66e,0xb66f,0xb670,0xb671,0xb672,0xb673,0xb674,0xb675,
+ 0xb676,0xb677,0xb678,0xb679,0xb67a,0xb67b,0xb67c,0xb67d,0xb67e,0xb67f,
+ 0xb680,0xb681,0xb682,0xb683,0xb684,0xb685,0xb686,0xb687,0xb688,0xb689,
+ 0xb68a,0xb68b,0xb68c,0xb68d,0xb68e,0xb68f,0xb690,0xb691,0xb692,0xb693,
+ 0xb694,0xb695,0xb696,0xb697,0xb698,0xb699,0xb69a,0xb69b,0xb69e,0xb69f,
+ 0xb6a1,0xb6a2,0xb6a3,0xb6a5,0xb6a6,0xb6a7,0xb6a8,0xb6a9,0xb6aa,0xb6ad,
+ 0xb6ae,0xb6af,0xb6b0,0xb6b2,0xb6b3,0xb6b4,0xb6b5,0xb6b6,0xb6b7,0xb6b8,
+ 0xb6b9,0xb6ba,0xb6bb,0xb6bc,0xb6bd,0xb6be,0xb6bf,0xb6c0,0xb6c1,0xb6c2
+ },
+ { /* ku 0d */
+ 0xb6c3,0xb6c4,0xb6c5,0xb6c6,0xb6c7,0xb6c8,0xb6c9,0xb6ca,0xb6cb,0xb6cc,
+ 0xb6cd,0xb6ce,0xb6cf,0xb6d0,0xb6d1,0xb6d2,0xb6d3,0xb6d5,0xb6d6,0xb6d7,
+ 0xb6d8,0xb6d9,0xb6da,0xb6db,0xb6dc,0xb6dd,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xb6de,0xb6df,0xb6e0,0xb6e1,0xb6e2,0xb6e3,0xb6e4,0xb6e5,
+ 0xb6e6,0xb6e7,0xb6e8,0xb6e9,0xb6ea,0xb6eb,0xb6ec,0xb6ed,0xb6ee,0xb6ef,
+ 0xb6f1,0xb6f2,0xb6f3,0xb6f5,0xb6f6,0xb6f7,0xb6f9,0xb6fa,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xb6fb,0xb6fc,0xb6fd,0xb6fe,0xb6ff,0xb702,
+ 0xb703,0xb704,0xb706,0xb707,0xb708,0xb709,0xb70a,0xb70b,0xb70c,0xb70d,
+ 0xb70e,0xb70f,0xb710,0xb711,0xb712,0xb713,0xb714,0xb715,0xb716,0xb717,
+ 0xb718,0xb719,0xb71a,0xb71b,0xb71c,0xb71d,0xb71e,0xb71f,0xb720,0xb721,
+ 0xb722,0xb723,0xb724,0xb725,0xb726,0xb727,0xb72a,0xb72b,0xb72d,0xb72e,
+ 0xb731,0xb732,0xb733,0xb734,0xb735,0xb736,0xb737,0xb73a,0xb73c,0xb73d,
+ 0xb73e,0xb73f,0xb740,0xb741,0xb742,0xb743,0xb745,0xb746,0xb747,0xb749,
+ 0xb74a,0xb74b,0xb74d,0xb74e,0xb74f,0xb750,0xb751,0xb752,0xb753,0xb756,
+ 0xb757,0xb758,0xb759,0xb75a,0xb75b,0xb75c,0xb75d,0xb75e,0xb75f,0xb761,
+ 0xb762,0xb763,0xb765,0xb766,0xb767,0xb769,0xb76a,0xb76b,0xb76c,0xb76d,
+ 0xb76e,0xb76f,0xb772,0xb774,0xb776,0xb777,0xb778,0xb779,0xb77a,0xb77b,
+ 0xb77e,0xb77f,0xb781,0xb782,0xb783,0xb785,0xb786,0xb787,0xb788,0xb789,
+ 0xb78a,0xb78b,0xb78e,0xb793,0xb794,0xb795,0xb79a,0xb79b,0xb79d,0xb79e
+ },
+ { /* ku 0e */
+ 0xb79f,0xb7a1,0xb7a2,0xb7a3,0xb7a4,0xb7a5,0xb7a6,0xb7a7,0xb7aa,0xb7ae,
+ 0xb7af,0xb7b0,0xb7b1,0xb7b2,0xb7b3,0xb7b6,0xb7b7,0xb7b9,0xb7ba,0xb7bb,
+ 0xb7bc,0xb7bd,0xb7be,0xb7bf,0xb7c0,0xb7c1,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xb7c2,0xb7c3,0xb7c4,0xb7c5,0xb7c6,0xb7c8,0xb7ca,0xb7cb,
+ 0xb7cc,0xb7cd,0xb7ce,0xb7cf,0xb7d0,0xb7d1,0xb7d2,0xb7d3,0xb7d4,0xb7d5,
+ 0xb7d6,0xb7d7,0xb7d8,0xb7d9,0xb7da,0xb7db,0xb7dc,0xb7dd,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xb7de,0xb7df,0xb7e0,0xb7e1,0xb7e2,0xb7e3,
+ 0xb7e4,0xb7e5,0xb7e6,0xb7e7,0xb7e8,0xb7e9,0xb7ea,0xb7eb,0xb7ee,0xb7ef,
+ 0xb7f1,0xb7f2,0xb7f3,0xb7f5,0xb7f6,0xb7f7,0xb7f8,0xb7f9,0xb7fa,0xb7fb,
+ 0xb7fe,0xb802,0xb803,0xb804,0xb805,0xb806,0xb80a,0xb80b,0xb80d,0xb80e,
+ 0xb80f,0xb811,0xb812,0xb813,0xb814,0xb815,0xb816,0xb817,0xb81a,0xb81c,
+ 0xb81e,0xb81f,0xb820,0xb821,0xb822,0xb823,0xb826,0xb827,0xb829,0xb82a,
+ 0xb82b,0xb82d,0xb82e,0xb82f,0xb830,0xb831,0xb832,0xb833,0xb836,0xb83a,
+ 0xb83b,0xb83c,0xb83d,0xb83e,0xb83f,0xb841,0xb842,0xb843,0xb845,0xb846,
+ 0xb847,0xb848,0xb849,0xb84a,0xb84b,0xb84c,0xb84d,0xb84e,0xb84f,0xb850,
+ 0xb852,0xb854,0xb855,0xb856,0xb857,0xb858,0xb859,0xb85a,0xb85b,0xb85e,
+ 0xb85f,0xb861,0xb862,0xb863,0xb865,0xb866,0xb867,0xb868,0xb869,0xb86a,
+ 0xb86b,0xb86e,0xb870,0xb872,0xb873,0xb874,0xb875,0xb876,0xb877,0xb879,
+ 0xb87a,0xb87b,0xb87d,0xb87e,0xb87f,0xb880,0xb881,0xb882,0xb883,0xb884
+ },
+ { /* ku 0f */
+ 0xb885,0xb886,0xb887,0xb888,0xb889,0xb88a,0xb88b,0xb88c,0xb88e,0xb88f,
+ 0xb890,0xb891,0xb892,0xb893,0xb894,0xb895,0xb896,0xb897,0xb898,0xb899,
+ 0xb89a,0xb89b,0xb89c,0xb89d,0xb89e,0xb89f,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xb8a0,0xb8a1,0xb8a2,0xb8a3,0xb8a4,0xb8a5,0xb8a6,0xb8a7,
+ 0xb8a9,0xb8aa,0xb8ab,0xb8ac,0xb8ad,0xb8ae,0xb8af,0xb8b1,0xb8b2,0xb8b3,
+ 0xb8b5,0xb8b6,0xb8b7,0xb8b9,0xb8ba,0xb8bb,0xb8bc,0xb8bd,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xb8be,0xb8bf,0xb8c2,0xb8c4,0xb8c6,0xb8c7,
+ 0xb8c8,0xb8c9,0xb8ca,0xb8cb,0xb8cd,0xb8ce,0xb8cf,0xb8d1,0xb8d2,0xb8d3,
+ 0xb8d5,0xb8d6,0xb8d7,0xb8d8,0xb8d9,0xb8da,0xb8db,0xb8dc,0xb8de,0xb8e0,
+ 0xb8e2,0xb8e3,0xb8e4,0xb8e5,0xb8e6,0xb8e7,0xb8ea,0xb8eb,0xb8ed,0xb8ee,
+ 0xb8ef,0xb8f1,0xb8f2,0xb8f3,0xb8f4,0xb8f5,0xb8f6,0xb8f7,0xb8fa,0xb8fc,
+ 0xb8fe,0xb8ff,0xb900,0xb901,0xb902,0xb903,0xb905,0xb906,0xb907,0xb908,
+ 0xb909,0xb90a,0xb90b,0xb90c,0xb90d,0xb90e,0xb90f,0xb910,0xb911,0xb912,
+ 0xb913,0xb914,0xb915,0xb916,0xb917,0xb919,0xb91a,0xb91b,0xb91c,0xb91d,
+ 0xb91e,0xb91f,0xb921,0xb922,0xb923,0xb924,0xb925,0xb926,0xb927,0xb928,
+ 0xb929,0xb92a,0xb92b,0xb92c,0xb92d,0xb92e,0xb92f,0xb930,0xb931,0xb932,
+ 0xb933,0xb934,0xb935,0xb936,0xb937,0xb938,0xb939,0xb93a,0xb93b,0xb93e,
+ 0xb93f,0xb941,0xb942,0xb943,0xb945,0xb946,0xb947,0xb948,0xb949,0xb94a,
+ 0xb94b,0xb94d,0xb94e,0xb950,0xb952,0xb953,0xb954,0xb955,0xb956,0xb957
+ },
+ { /* ku 10 */
+ 0xb95a,0xb95b,0xb95d,0xb95e,0xb95f,0xb961,0xb962,0xb963,0xb964,0xb965,
+ 0xb966,0xb967,0xb96a,0xb96c,0xb96e,0xb96f,0xb970,0xb971,0xb972,0xb973,
+ 0xb976,0xb977,0xb979,0xb97a,0xb97b,0xb97d,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xb97e,0xb97f,0xb980,0xb981,0xb982,0xb983,0xb986,0xb988,
+ 0xb98b,0xb98c,0xb98f,0xb990,0xb991,0xb992,0xb993,0xb994,0xb995,0xb996,
+ 0xb997,0xb998,0xb999,0xb99a,0xb99b,0xb99c,0xb99d,0xb99e,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xb99f,0xb9a0,0xb9a1,0xb9a2,0xb9a3,0xb9a4,
+ 0xb9a5,0xb9a6,0xb9a7,0xb9a8,0xb9a9,0xb9aa,0xb9ab,0xb9ae,0xb9af,0xb9b1,
+ 0xb9b2,0xb9b3,0xb9b5,0xb9b6,0xb9b7,0xb9b8,0xb9b9,0xb9ba,0xb9bb,0xb9be,
+ 0xb9c0,0xb9c2,0xb9c3,0xb9c4,0xb9c5,0xb9c6,0xb9c7,0xb9ca,0xb9cb,0xb9cd,
+ 0xb9d3,0xb9d4,0xb9d5,0xb9d6,0xb9d7,0xb9da,0xb9dc,0xb9df,0xb9e0,0xb9e2,
+ 0xb9e6,0xb9e7,0xb9e9,0xb9ea,0xb9eb,0xb9ed,0xb9ee,0xb9ef,0xb9f0,0xb9f1,
+ 0xb9f2,0xb9f3,0xb9f6,0xb9fb,0xb9fc,0xb9fd,0xb9fe,0xb9ff,0xba02,0xba03,
+ 0xba04,0xba05,0xba06,0xba07,0xba09,0xba0a,0xba0b,0xba0c,0xba0d,0xba0e,
+ 0xba0f,0xba10,0xba11,0xba12,0xba13,0xba14,0xba16,0xba17,0xba18,0xba19,
+ 0xba1a,0xba1b,0xba1c,0xba1d,0xba1e,0xba1f,0xba20,0xba21,0xba22,0xba23,
+ 0xba24,0xba25,0xba26,0xba27,0xba28,0xba29,0xba2a,0xba2b,0xba2c,0xba2d,
+ 0xba2e,0xba2f,0xba30,0xba31,0xba32,0xba33,0xba34,0xba35,0xba36,0xba37,
+ 0xba3a,0xba3b,0xba3d,0xba3e,0xba3f,0xba41,0xba43,0xba44,0xba45,0xba46
+ },
+ { /* ku 11 */
+ 0xba47,0xba4a,0xba4c,0xba4f,0xba50,0xba51,0xba52,0xba56,0xba57,0xba59,
+ 0xba5a,0xba5b,0xba5d,0xba5e,0xba5f,0xba60,0xba61,0xba62,0xba63,0xba66,
+ 0xba6a,0xba6b,0xba6c,0xba6d,0xba6e,0xba6f,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xba72,0xba73,0xba75,0xba76,0xba77,0xba79,0xba7a,0xba7b,
+ 0xba7c,0xba7d,0xba7e,0xba7f,0xba80,0xba81,0xba82,0xba86,0xba88,0xba89,
+ 0xba8a,0xba8b,0xba8d,0xba8e,0xba8f,0xba90,0xba91,0xba92,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xba93,0xba94,0xba95,0xba96,0xba97,0xba98,
+ 0xba99,0xba9a,0xba9b,0xba9c,0xba9d,0xba9e,0xba9f,0xbaa0,0xbaa1,0xbaa2,
+ 0xbaa3,0xbaa4,0xbaa5,0xbaa6,0xbaa7,0xbaaa,0xbaad,0xbaae,0xbaaf,0xbab1,
+ 0xbab3,0xbab4,0xbab5,0xbab6,0xbab7,0xbaba,0xbabc,0xbabe,0xbabf,0xbac0,
+ 0xbac1,0xbac2,0xbac3,0xbac5,0xbac6,0xbac7,0xbac9,0xbaca,0xbacb,0xbacc,
+ 0xbacd,0xbace,0xbacf,0xbad0,0xbad1,0xbad2,0xbad3,0xbad4,0xbad5,0xbad6,
+ 0xbad7,0xbada,0xbadb,0xbadc,0xbadd,0xbade,0xbadf,0xbae0,0xbae1,0xbae2,
+ 0xbae3,0xbae4,0xbae5,0xbae6,0xbae7,0xbae8,0xbae9,0xbaea,0xbaeb,0xbaec,
+ 0xbaed,0xbaee,0xbaef,0xbaf0,0xbaf1,0xbaf2,0xbaf3,0xbaf4,0xbaf5,0xbaf6,
+ 0xbaf7,0xbaf8,0xbaf9,0xbafa,0xbafb,0xbafd,0xbafe,0xbaff,0xbb01,0xbb02,
+ 0xbb03,0xbb05,0xbb06,0xbb07,0xbb08,0xbb09,0xbb0a,0xbb0b,0xbb0c,0xbb0e,
+ 0xbb10,0xbb12,0xbb13,0xbb14,0xbb15,0xbb16,0xbb17,0xbb19,0xbb1a,0xbb1b,
+ 0xbb1d,0xbb1e,0xbb1f,0xbb21,0xbb22,0xbb23,0xbb24,0xbb25,0xbb26,0xbb27
+ },
+ { /* ku 12 */
+ 0xbb28,0xbb2a,0xbb2c,0xbb2d,0xbb2e,0xbb2f,0xbb30,0xbb31,0xbb32,0xbb33,
+ 0xbb37,0xbb39,0xbb3a,0xbb3f,0xbb40,0xbb41,0xbb42,0xbb43,0xbb46,0xbb48,
+ 0xbb4a,0xbb4b,0xbb4c,0xbb4e,0xbb51,0xbb52,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xbb53,0xbb55,0xbb56,0xbb57,0xbb59,0xbb5a,0xbb5b,0xbb5c,
+ 0xbb5d,0xbb5e,0xbb5f,0xbb60,0xbb62,0xbb64,0xbb65,0xbb66,0xbb67,0xbb68,
+ 0xbb69,0xbb6a,0xbb6b,0xbb6d,0xbb6e,0xbb6f,0xbb70,0xbb71,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xbb72,0xbb73,0xbb74,0xbb75,0xbb76,0xbb77,
+ 0xbb78,0xbb79,0xbb7a,0xbb7b,0xbb7c,0xbb7d,0xbb7e,0xbb7f,0xbb80,0xbb81,
+ 0xbb82,0xbb83,0xbb84,0xbb85,0xbb86,0xbb87,0xbb89,0xbb8a,0xbb8b,0xbb8d,
+ 0xbb8e,0xbb8f,0xbb91,0xbb92,0xbb93,0xbb94,0xbb95,0xbb96,0xbb97,0xbb98,
+ 0xbb99,0xbb9a,0xbb9b,0xbb9c,0xbb9d,0xbb9e,0xbb9f,0xbba0,0xbba1,0xbba2,
+ 0xbba3,0xbba5,0xbba6,0xbba7,0xbba9,0xbbaa,0xbbab,0xbbad,0xbbae,0xbbaf,
+ 0xbbb0,0xbbb1,0xbbb2,0xbbb3,0xbbb5,0xbbb6,0xbbb8,0xbbb9,0xbbba,0xbbbb,
+ 0xbbbc,0xbbbd,0xbbbe,0xbbbf,0xbbc1,0xbbc2,0xbbc3,0xbbc5,0xbbc6,0xbbc7,
+ 0xbbc9,0xbbca,0xbbcb,0xbbcc,0xbbcd,0xbbce,0xbbcf,0xbbd1,0xbbd2,0xbbd4,
+ 0xbbd5,0xbbd6,0xbbd7,0xbbd8,0xbbd9,0xbbda,0xbbdb,0xbbdc,0xbbdd,0xbbde,
+ 0xbbdf,0xbbe0,0xbbe1,0xbbe2,0xbbe3,0xbbe4,0xbbe5,0xbbe6,0xbbe7,0xbbe8,
+ 0xbbe9,0xbbea,0xbbeb,0xbbec,0xbbed,0xbbee,0xbbef,0xbbf0,0xbbf1,0xbbf2,
+ 0xbbf3,0xbbf4,0xbbf5,0xbbf6,0xbbf7,0xbbfa,0xbbfb,0xbbfd,0xbbfe,0xbc01
+ },
+ { /* ku 13 */
+ 0xbc03,0xbc04,0xbc05,0xbc06,0xbc07,0xbc0a,0xbc0e,0xbc10,0xbc12,0xbc13,
+ 0xbc19,0xbc1a,0xbc20,0xbc21,0xbc22,0xbc23,0xbc26,0xbc28,0xbc2a,0xbc2b,
+ 0xbc2c,0xbc2e,0xbc2f,0xbc32,0xbc33,0xbc35,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xbc36,0xbc37,0xbc39,0xbc3a,0xbc3b,0xbc3c,0xbc3d,0xbc3e,
+ 0xbc3f,0xbc42,0xbc46,0xbc47,0xbc48,0xbc4a,0xbc4b,0xbc4e,0xbc4f,0xbc51,
+ 0xbc52,0xbc53,0xbc54,0xbc55,0xbc56,0xbc57,0xbc58,0xbc59,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xbc5a,0xbc5b,0xbc5c,0xbc5e,0xbc5f,0xbc60,
+ 0xbc61,0xbc62,0xbc63,0xbc64,0xbc65,0xbc66,0xbc67,0xbc68,0xbc69,0xbc6a,
+ 0xbc6b,0xbc6c,0xbc6d,0xbc6e,0xbc6f,0xbc70,0xbc71,0xbc72,0xbc73,0xbc74,
+ 0xbc75,0xbc76,0xbc77,0xbc78,0xbc79,0xbc7a,0xbc7b,0xbc7c,0xbc7d,0xbc7e,
+ 0xbc7f,0xbc80,0xbc81,0xbc82,0xbc83,0xbc86,0xbc87,0xbc89,0xbc8a,0xbc8d,
+ 0xbc8f,0xbc90,0xbc91,0xbc92,0xbc93,0xbc96,0xbc98,0xbc9b,0xbc9c,0xbc9d,
+ 0xbc9e,0xbc9f,0xbca2,0xbca3,0xbca5,0xbca6,0xbca9,0xbcaa,0xbcab,0xbcac,
+ 0xbcad,0xbcae,0xbcaf,0xbcb2,0xbcb6,0xbcb7,0xbcb8,0xbcb9,0xbcba,0xbcbb,
+ 0xbcbe,0xbcbf,0xbcc1,0xbcc2,0xbcc3,0xbcc5,0xbcc6,0xbcc7,0xbcc8,0xbcc9,
+ 0xbcca,0xbccb,0xbccc,0xbcce,0xbcd2,0xbcd3,0xbcd4,0xbcd6,0xbcd7,0xbcd9,
+ 0xbcda,0xbcdb,0xbcdd,0xbcde,0xbcdf,0xbce0,0xbce1,0xbce2,0xbce3,0xbce4,
+ 0xbce5,0xbce6,0xbce7,0xbce8,0xbce9,0xbcea,0xbceb,0xbcec,0xbced,0xbcee,
+ 0xbcef,0xbcf0,0xbcf1,0xbcf2,0xbcf3,0xbcf7,0xbcf9,0xbcfa,0xbcfb,0xbcfd
+ },
+ { /* ku 14 */
+ 0xbcfe,0xbcff,0xbd00,0xbd01,0xbd02,0xbd03,0xbd06,0xbd08,0xbd0a,0xbd0b,
+ 0xbd0c,0xbd0d,0xbd0e,0xbd0f,0xbd11,0xbd12,0xbd13,0xbd15,0xbd16,0xbd17,
+ 0xbd18,0xbd19,0xbd1a,0xbd1b,0xbd1c,0xbd1d,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xbd1e,0xbd1f,0xbd20,0xbd21,0xbd22,0xbd23,0xbd25,0xbd26,
+ 0xbd27,0xbd28,0xbd29,0xbd2a,0xbd2b,0xbd2d,0xbd2e,0xbd2f,0xbd30,0xbd31,
+ 0xbd32,0xbd33,0xbd34,0xbd35,0xbd36,0xbd37,0xbd38,0xbd39,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xbd3a,0xbd3b,0xbd3c,0xbd3d,0xbd3e,0xbd3f,
+ 0xbd41,0xbd42,0xbd43,0xbd44,0xbd45,0xbd46,0xbd47,0xbd4a,0xbd4b,0xbd4d,
+ 0xbd4e,0xbd4f,0xbd51,0xbd52,0xbd53,0xbd54,0xbd55,0xbd56,0xbd57,0xbd5a,
+ 0xbd5b,0xbd5c,0xbd5d,0xbd5e,0xbd5f,0xbd60,0xbd61,0xbd62,0xbd63,0xbd65,
+ 0xbd66,0xbd67,0xbd69,0xbd6a,0xbd6b,0xbd6c,0xbd6d,0xbd6e,0xbd6f,0xbd70,
+ 0xbd71,0xbd72,0xbd73,0xbd74,0xbd75,0xbd76,0xbd77,0xbd78,0xbd79,0xbd7a,
+ 0xbd7b,0xbd7c,0xbd7d,0xbd7e,0xbd7f,0xbd82,0xbd83,0xbd85,0xbd86,0xbd8b,
+ 0xbd8c,0xbd8d,0xbd8e,0xbd8f,0xbd92,0xbd94,0xbd96,0xbd97,0xbd98,0xbd9b,
+ 0xbd9d,0xbd9e,0xbd9f,0xbda0,0xbda1,0xbda2,0xbda3,0xbda5,0xbda6,0xbda7,
+ 0xbda8,0xbda9,0xbdaa,0xbdab,0xbdac,0xbdad,0xbdae,0xbdaf,0xbdb1,0xbdb2,
+ 0xbdb3,0xbdb4,0xbdb5,0xbdb6,0xbdb7,0xbdb9,0xbdba,0xbdbb,0xbdbc,0xbdbd,
+ 0xbdbe,0xbdbf,0xbdc0,0xbdc1,0xbdc2,0xbdc3,0xbdc4,0xbdc5,0xbdc6,0xbdc7,
+ 0xbdc8,0xbdc9,0xbdca,0xbdcb,0xbdcc,0xbdcd,0xbdce,0xbdcf,0xbdd0,0xbdd1
+ },
+ { /* ku 15 */
+ 0xbdd2,0xbdd3,0xbdd6,0xbdd7,0xbdd9,0xbdda,0xbddb,0xbddd,0xbdde,0xbddf,
+ 0xbde0,0xbde1,0xbde2,0xbde3,0xbde4,0xbde5,0xbde6,0xbde7,0xbde8,0xbdea,
+ 0xbdeb,0xbdec,0xbded,0xbdee,0xbdef,0xbdf1,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xbdf2,0xbdf3,0xbdf5,0xbdf6,0xbdf7,0xbdf9,0xbdfa,0xbdfb,
+ 0xbdfc,0xbdfd,0xbdfe,0xbdff,0xbe01,0xbe02,0xbe04,0xbe06,0xbe07,0xbe08,
+ 0xbe09,0xbe0a,0xbe0b,0xbe0e,0xbe0f,0xbe11,0xbe12,0xbe13,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xbe15,0xbe16,0xbe17,0xbe18,0xbe19,0xbe1a,
+ 0xbe1b,0xbe1e,0xbe20,0xbe21,0xbe22,0xbe23,0xbe24,0xbe25,0xbe26,0xbe27,
+ 0xbe28,0xbe29,0xbe2a,0xbe2b,0xbe2c,0xbe2d,0xbe2e,0xbe2f,0xbe30,0xbe31,
+ 0xbe32,0xbe33,0xbe34,0xbe35,0xbe36,0xbe37,0xbe38,0xbe39,0xbe3a,0xbe3b,
+ 0xbe3c,0xbe3d,0xbe3e,0xbe3f,0xbe40,0xbe41,0xbe42,0xbe43,0xbe46,0xbe47,
+ 0xbe49,0xbe4a,0xbe4b,0xbe4d,0xbe4f,0xbe50,0xbe51,0xbe52,0xbe53,0xbe56,
+ 0xbe58,0xbe5c,0xbe5d,0xbe5e,0xbe5f,0xbe62,0xbe63,0xbe65,0xbe66,0xbe67,
+ 0xbe69,0xbe6b,0xbe6c,0xbe6d,0xbe6e,0xbe6f,0xbe72,0xbe76,0xbe77,0xbe78,
+ 0xbe79,0xbe7a,0xbe7e,0xbe7f,0xbe81,0xbe82,0xbe83,0xbe85,0xbe86,0xbe87,
+ 0xbe88,0xbe89,0xbe8a,0xbe8b,0xbe8e,0xbe92,0xbe93,0xbe94,0xbe95,0xbe96,
+ 0xbe97,0xbe9a,0xbe9b,0xbe9c,0xbe9d,0xbe9e,0xbe9f,0xbea0,0xbea1,0xbea2,
+ 0xbea3,0xbea4,0xbea5,0xbea6,0xbea7,0xbea9,0xbeaa,0xbeab,0xbeac,0xbead,
+ 0xbeae,0xbeaf,0xbeb0,0xbeb1,0xbeb2,0xbeb3,0xbeb4,0xbeb5,0xbeb6,0xbeb7
+ },
+ { /* ku 16 */
+ 0xbeb8,0xbeb9,0xbeba,0xbebb,0xbebc,0xbebd,0xbebe,0xbebf,0xbec0,0xbec1,
+ 0xbec2,0xbec3,0xbec4,0xbec5,0xbec6,0xbec7,0xbec8,0xbec9,0xbeca,0xbecb,
+ 0xbecc,0xbecd,0xbece,0xbecf,0xbed2,0xbed3,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xbed5,0xbed6,0xbed9,0xbeda,0xbedb,0xbedc,0xbedd,0xbede,
+ 0xbedf,0xbee1,0xbee2,0xbee6,0xbee7,0xbee8,0xbee9,0xbeea,0xbeeb,0xbeed,
+ 0xbeee,0xbeef,0xbef0,0xbef1,0xbef2,0xbef3,0xbef4,0xbef5,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xbef6,0xbef7,0xbef8,0xbef9,0xbefa,0xbefb,
+ 0xbefc,0xbefd,0xbefe,0xbeff,0xbf00,0xbf02,0xbf03,0xbf04,0xbf05,0xbf06,
+ 0xbf07,0xbf0a,0xbf0b,0xbf0c,0xbf0d,0xbf0e,0xbf0f,0xbf10,0xbf11,0xbf12,
+ 0xbf13,0xbf14,0xbf15,0xbf16,0xbf17,0xbf1a,0xbf1e,0xbf1f,0xbf20,0xbf21,
+ 0xbf22,0xbf23,0xbf24,0xbf25,0xbf26,0xbf27,0xbf28,0xbf29,0xbf2a,0xbf2b,
+ 0xbf2c,0xbf2d,0xbf2e,0xbf2f,0xbf30,0xbf31,0xbf32,0xbf33,0xbf34,0xbf35,
+ 0xbf36,0xbf37,0xbf38,0xbf39,0xbf3a,0xbf3b,0xbf3c,0xbf3d,0xbf3e,0xbf3f,
+ 0xbf42,0xbf43,0xbf45,0xbf46,0xbf47,0xbf49,0xbf4a,0xbf4b,0xbf4c,0xbf4d,
+ 0xbf4e,0xbf4f,0xbf52,0xbf53,0xbf54,0xbf56,0xbf57,0xbf58,0xbf59,0xbf5a,
+ 0xbf5b,0xbf5c,0xbf5d,0xbf5e,0xbf5f,0xbf60,0xbf61,0xbf62,0xbf63,0xbf64,
+ 0xbf65,0xbf66,0xbf67,0xbf68,0xbf69,0xbf6a,0xbf6b,0xbf6c,0xbf6d,0xbf6e,
+ 0xbf6f,0xbf70,0xbf71,0xbf72,0xbf73,0xbf74,0xbf75,0xbf76,0xbf77,0xbf78,
+ 0xbf79,0xbf7a,0xbf7b,0xbf7c,0xbf7d,0xbf7e,0xbf7f,0xbf80,0xbf81,0xbf82
+ },
+ { /* ku 17 */
+ 0xbf83,0xbf84,0xbf85,0xbf86,0xbf87,0xbf88,0xbf89,0xbf8a,0xbf8b,0xbf8c,
+ 0xbf8d,0xbf8e,0xbf8f,0xbf90,0xbf91,0xbf92,0xbf93,0xbf95,0xbf96,0xbf97,
+ 0xbf98,0xbf99,0xbf9a,0xbf9b,0xbf9c,0xbf9d,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xbf9e,0xbf9f,0xbfa0,0xbfa1,0xbfa2,0xbfa3,0xbfa4,0xbfa5,
+ 0xbfa6,0xbfa7,0xbfa8,0xbfa9,0xbfaa,0xbfab,0xbfac,0xbfad,0xbfae,0xbfaf,
+ 0xbfb1,0xbfb2,0xbfb3,0xbfb4,0xbfb5,0xbfb6,0xbfb7,0xbfb8,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xbfb9,0xbfba,0xbfbb,0xbfbc,0xbfbd,0xbfbe,
+ 0xbfbf,0xbfc0,0xbfc1,0xbfc2,0xbfc3,0xbfc4,0xbfc6,0xbfc7,0xbfc8,0xbfc9,
+ 0xbfca,0xbfcb,0xbfce,0xbfcf,0xbfd1,0xbfd2,0xbfd3,0xbfd5,0xbfd6,0xbfd7,
+ 0xbfd8,0xbfd9,0xbfda,0xbfdb,0xbfdd,0xbfde,0xbfe0,0xbfe2,0xbfe3,0xbfe4,
+ 0xbfe5,0xbfe6,0xbfe7,0xbfe8,0xbfe9,0xbfea,0xbfeb,0xbfec,0xbfed,0xbfee,
+ 0xbfef,0xbff0,0xbff1,0xbff2,0xbff3,0xbff4,0xbff5,0xbff6,0xbff7,0xbff8,
+ 0xbff9,0xbffa,0xbffb,0xbffc,0xbffd,0xbffe,0xbfff,0xc000,0xc001,0xc002,
+ 0xc003,0xc004,0xc005,0xc006,0xc007,0xc008,0xc009,0xc00a,0xc00b,0xc00c,
+ 0xc00d,0xc00e,0xc00f,0xc010,0xc011,0xc012,0xc013,0xc014,0xc015,0xc016,
+ 0xc017,0xc018,0xc019,0xc01a,0xc01b,0xc01c,0xc01d,0xc01e,0xc01f,0xc020,
+ 0xc021,0xc022,0xc023,0xc024,0xc025,0xc026,0xc027,0xc028,0xc029,0xc02a,
+ 0xc02b,0xc02c,0xc02d,0xc02e,0xc02f,0xc030,0xc031,0xc032,0xc033,0xc034,
+ 0xc035,0xc036,0xc037,0xc038,0xc039,0xc03a,0xc03b,0xc03d,0xc03e,0xc03f
+ },
+ { /* ku 18 */
+ 0xc040,0xc041,0xc042,0xc043,0xc044,0xc045,0xc046,0xc047,0xc048,0xc049,
+ 0xc04a,0xc04b,0xc04c,0xc04d,0xc04e,0xc04f,0xc050,0xc052,0xc053,0xc054,
+ 0xc055,0xc056,0xc057,0xc059,0xc05a,0xc05b,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xc05d,0xc05e,0xc05f,0xc061,0xc062,0xc063,0xc064,0xc065,
+ 0xc066,0xc067,0xc06a,0xc06b,0xc06c,0xc06d,0xc06e,0xc06f,0xc070,0xc071,
+ 0xc072,0xc073,0xc074,0xc075,0xc076,0xc077,0xc078,0xc079,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xc07a,0xc07b,0xc07c,0xc07d,0xc07e,0xc07f,
+ 0xc080,0xc081,0xc082,0xc083,0xc084,0xc085,0xc086,0xc087,0xc088,0xc089,
+ 0xc08a,0xc08b,0xc08c,0xc08d,0xc08e,0xc08f,0xc092,0xc093,0xc095,0xc096,
+ 0xc097,0xc099,0xc09a,0xc09b,0xc09c,0xc09d,0xc09e,0xc09f,0xc0a2,0xc0a4,
+ 0xc0a6,0xc0a7,0xc0a8,0xc0a9,0xc0aa,0xc0ab,0xc0ae,0xc0b1,0xc0b2,0xc0b7,
+ 0xc0b8,0xc0b9,0xc0ba,0xc0bb,0xc0be,0xc0c2,0xc0c3,0xc0c4,0xc0c6,0xc0c7,
+ 0xc0ca,0xc0cb,0xc0cd,0xc0ce,0xc0cf,0xc0d1,0xc0d2,0xc0d3,0xc0d4,0xc0d5,
+ 0xc0d6,0xc0d7,0xc0da,0xc0de,0xc0df,0xc0e0,0xc0e1,0xc0e2,0xc0e3,0xc0e6,
+ 0xc0e7,0xc0e9,0xc0ea,0xc0eb,0xc0ed,0xc0ee,0xc0ef,0xc0f0,0xc0f1,0xc0f2,
+ 0xc0f3,0xc0f6,0xc0f8,0xc0fa,0xc0fb,0xc0fc,0xc0fd,0xc0fe,0xc0ff,0xc101,
+ 0xc102,0xc103,0xc105,0xc106,0xc107,0xc109,0xc10a,0xc10b,0xc10c,0xc10d,
+ 0xc10e,0xc10f,0xc111,0xc112,0xc113,0xc114,0xc116,0xc117,0xc118,0xc119,
+ 0xc11a,0xc11b,0xc121,0xc122,0xc125,0xc128,0xc129,0xc12a,0xc12b,0xc12e
+ },
+ { /* ku 19 */
+ 0xc132,0xc133,0xc134,0xc135,0xc137,0xc13a,0xc13b,0xc13d,0xc13e,0xc13f,
+ 0xc141,0xc142,0xc143,0xc144,0xc145,0xc146,0xc147,0xc14a,0xc14e,0xc14f,
+ 0xc150,0xc151,0xc152,0xc153,0xc156,0xc157,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xc159,0xc15a,0xc15b,0xc15d,0xc15e,0xc15f,0xc160,0xc161,
+ 0xc162,0xc163,0xc166,0xc16a,0xc16b,0xc16c,0xc16d,0xc16e,0xc16f,0xc171,
+ 0xc172,0xc173,0xc175,0xc176,0xc177,0xc179,0xc17a,0xc17b,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xc17c,0xc17d,0xc17e,0xc17f,0xc180,0xc181,
+ 0xc182,0xc183,0xc184,0xc186,0xc187,0xc188,0xc189,0xc18a,0xc18b,0xc18f,
+ 0xc191,0xc192,0xc193,0xc195,0xc197,0xc198,0xc199,0xc19a,0xc19b,0xc19e,
+ 0xc1a0,0xc1a2,0xc1a3,0xc1a4,0xc1a6,0xc1a7,0xc1aa,0xc1ab,0xc1ad,0xc1ae,
+ 0xc1af,0xc1b1,0xc1b2,0xc1b3,0xc1b4,0xc1b5,0xc1b6,0xc1b7,0xc1b8,0xc1b9,
+ 0xc1ba,0xc1bb,0xc1bc,0xc1be,0xc1bf,0xc1c0,0xc1c1,0xc1c2,0xc1c3,0xc1c5,
+ 0xc1c6,0xc1c7,0xc1c9,0xc1ca,0xc1cb,0xc1cd,0xc1ce,0xc1cf,0xc1d0,0xc1d1,
+ 0xc1d2,0xc1d3,0xc1d5,0xc1d6,0xc1d9,0xc1da,0xc1db,0xc1dc,0xc1dd,0xc1de,
+ 0xc1df,0xc1e1,0xc1e2,0xc1e3,0xc1e5,0xc1e6,0xc1e7,0xc1e9,0xc1ea,0xc1eb,
+ 0xc1ec,0xc1ed,0xc1ee,0xc1ef,0xc1f2,0xc1f4,0xc1f5,0xc1f6,0xc1f7,0xc1f8,
+ 0xc1f9,0xc1fa,0xc1fb,0xc1fe,0xc1ff,0xc201,0xc202,0xc203,0xc205,0xc206,
+ 0xc207,0xc208,0xc209,0xc20a,0xc20b,0xc20e,0xc210,0xc212,0xc213,0xc214,
+ 0xc215,0xc216,0xc217,0xc21a,0xc21b,0xc21d,0xc21e,0xc221,0xc222,0xc223
+ },
+ { /* ku 1a */
+ 0xc224,0xc225,0xc226,0xc227,0xc22a,0xc22c,0xc22e,0xc230,0xc233,0xc235,
+ 0xc236,0xc237,0xc238,0xc239,0xc23a,0xc23b,0xc23c,0xc23d,0xc23e,0xc23f,
+ 0xc240,0xc241,0xc242,0xc243,0xc244,0xc245,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xc246,0xc247,0xc249,0xc24a,0xc24b,0xc24c,0xc24d,0xc24e,
+ 0xc24f,0xc252,0xc253,0xc255,0xc256,0xc257,0xc259,0xc25a,0xc25b,0xc25c,
+ 0xc25d,0xc25e,0xc25f,0xc261,0xc262,0xc263,0xc264,0xc266,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xc267,0xc268,0xc269,0xc26a,0xc26b,0xc26e,
+ 0xc26f,0xc271,0xc272,0xc273,0xc275,0xc276,0xc277,0xc278,0xc279,0xc27a,
+ 0xc27b,0xc27e,0xc280,0xc282,0xc283,0xc284,0xc285,0xc286,0xc287,0xc28a,
+ 0xc28b,0xc28c,0xc28d,0xc28e,0xc28f,0xc291,0xc292,0xc293,0xc294,0xc295,
+ 0xc296,0xc297,0xc299,0xc29a,0xc29c,0xc29e,0xc29f,0xc2a0,0xc2a1,0xc2a2,
+ 0xc2a3,0xc2a6,0xc2a7,0xc2a9,0xc2aa,0xc2ab,0xc2ae,0xc2af,0xc2b0,0xc2b1,
+ 0xc2b2,0xc2b3,0xc2b6,0xc2b8,0xc2ba,0xc2bb,0xc2bc,0xc2bd,0xc2be,0xc2bf,
+ 0xc2c0,0xc2c1,0xc2c2,0xc2c3,0xc2c4,0xc2c5,0xc2c6,0xc2c7,0xc2c8,0xc2c9,
+ 0xc2ca,0xc2cb,0xc2cc,0xc2cd,0xc2ce,0xc2cf,0xc2d0,0xc2d1,0xc2d2,0xc2d3,
+ 0xc2d4,0xc2d5,0xc2d6,0xc2d7,0xc2d8,0xc2d9,0xc2da,0xc2db,0xc2de,0xc2df,
+ 0xc2e1,0xc2e2,0xc2e5,0xc2e6,0xc2e7,0xc2e8,0xc2e9,0xc2ea,0xc2ee,0xc2f0,
+ 0xc2f2,0xc2f3,0xc2f4,0xc2f5,0xc2f7,0xc2fa,0xc2fd,0xc2fe,0xc2ff,0xc301,
+ 0xc302,0xc303,0xc304,0xc305,0xc306,0xc307,0xc30a,0xc30b,0xc30e,0xc30f
+ },
+ { /* ku 1b */
+ 0xc310,0xc311,0xc312,0xc316,0xc317,0xc319,0xc31a,0xc31b,0xc31d,0xc31e,
+ 0xc31f,0xc320,0xc321,0xc322,0xc323,0xc326,0xc327,0xc32a,0xc32b,0xc32c,
+ 0xc32d,0xc32e,0xc32f,0xc330,0xc331,0xc332,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xc333,0xc334,0xc335,0xc336,0xc337,0xc338,0xc339,0xc33a,
+ 0xc33b,0xc33c,0xc33d,0xc33e,0xc33f,0xc340,0xc341,0xc342,0xc343,0xc344,
+ 0xc346,0xc347,0xc348,0xc349,0xc34a,0xc34b,0xc34c,0xc34d,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xc34e,0xc34f,0xc350,0xc351,0xc352,0xc353,
+ 0xc354,0xc355,0xc356,0xc357,0xc358,0xc359,0xc35a,0xc35b,0xc35c,0xc35d,
+ 0xc35e,0xc35f,0xc360,0xc361,0xc362,0xc363,0xc364,0xc365,0xc366,0xc367,
+ 0xc36a,0xc36b,0xc36d,0xc36e,0xc36f,0xc371,0xc373,0xc374,0xc375,0xc376,
+ 0xc377,0xc37a,0xc37b,0xc37e,0xc37f,0xc380,0xc381,0xc382,0xc383,0xc385,
+ 0xc386,0xc387,0xc389,0xc38a,0xc38b,0xc38d,0xc38e,0xc38f,0xc390,0xc391,
+ 0xc392,0xc393,0xc394,0xc395,0xc396,0xc397,0xc398,0xc399,0xc39a,0xc39b,
+ 0xc39c,0xc39d,0xc39e,0xc39f,0xc3a0,0xc3a1,0xc3a2,0xc3a3,0xc3a4,0xc3a5,
+ 0xc3a6,0xc3a7,0xc3a8,0xc3a9,0xc3aa,0xc3ab,0xc3ac,0xc3ad,0xc3ae,0xc3af,
+ 0xc3b0,0xc3b1,0xc3b2,0xc3b3,0xc3b4,0xc3b5,0xc3b6,0xc3b7,0xc3b8,0xc3b9,
+ 0xc3ba,0xc3bb,0xc3bc,0xc3bd,0xc3be,0xc3bf,0xc3c1,0xc3c2,0xc3c3,0xc3c4,
+ 0xc3c5,0xc3c6,0xc3c7,0xc3c8,0xc3c9,0xc3ca,0xc3cb,0xc3cc,0xc3cd,0xc3ce,
+ 0xc3cf,0xc3d0,0xc3d1,0xc3d2,0xc3d3,0xc3d4,0xc3d5,0xc3d6,0xc3d7,0xc3da
+ },
+ { /* ku 1c */
+ 0xc3db,0xc3dd,0xc3de,0xc3e1,0xc3e3,0xc3e4,0xc3e5,0xc3e6,0xc3e7,0xc3ea,
+ 0xc3eb,0xc3ec,0xc3ee,0xc3ef,0xc3f0,0xc3f1,0xc3f2,0xc3f3,0xc3f6,0xc3f7,
+ 0xc3f9,0xc3fa,0xc3fb,0xc3fc,0xc3fd,0xc3fe,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xc3ff,0xc400,0xc401,0xc402,0xc403,0xc404,0xc405,0xc406,
+ 0xc407,0xc409,0xc40a,0xc40b,0xc40c,0xc40d,0xc40e,0xc40f,0xc411,0xc412,
+ 0xc413,0xc414,0xc415,0xc416,0xc417,0xc418,0xc419,0xc41a,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xc41b,0xc41c,0xc41d,0xc41e,0xc41f,0xc420,
+ 0xc421,0xc422,0xc423,0xc425,0xc426,0xc427,0xc428,0xc429,0xc42a,0xc42b,
+ 0xc42d,0xc42e,0xc42f,0xc431,0xc432,0xc433,0xc435,0xc436,0xc437,0xc438,
+ 0xc439,0xc43a,0xc43b,0xc43e,0xc43f,0xc440,0xc441,0xc442,0xc443,0xc444,
+ 0xc445,0xc446,0xc447,0xc449,0xc44a,0xc44b,0xc44c,0xc44d,0xc44e,0xc44f,
+ 0xc450,0xc451,0xc452,0xc453,0xc454,0xc455,0xc456,0xc457,0xc458,0xc459,
+ 0xc45a,0xc45b,0xc45c,0xc45d,0xc45e,0xc45f,0xc460,0xc461,0xc462,0xc463,
+ 0xc466,0xc467,0xc469,0xc46a,0xc46b,0xc46d,0xc46e,0xc46f,0xc470,0xc471,
+ 0xc472,0xc473,0xc476,0xc477,0xc478,0xc47a,0xc47b,0xc47c,0xc47d,0xc47e,
+ 0xc47f,0xc481,0xc482,0xc483,0xc484,0xc485,0xc486,0xc487,0xc488,0xc489,
+ 0xc48a,0xc48b,0xc48c,0xc48d,0xc48e,0xc48f,0xc490,0xc491,0xc492,0xc493,
+ 0xc495,0xc496,0xc497,0xc498,0xc499,0xc49a,0xc49b,0xc49d,0xc49e,0xc49f,
+ 0xc4a0,0xc4a1,0xc4a2,0xc4a3,0xc4a4,0xc4a5,0xc4a6,0xc4a7,0xc4a8,0xc4a9
+ },
+ { /* ku 1d */
+ 0xc4aa,0xc4ab,0xc4ac,0xc4ad,0xc4ae,0xc4af,0xc4b0,0xc4b1,0xc4b2,0xc4b3,
+ 0xc4b4,0xc4b5,0xc4b6,0xc4b7,0xc4b9,0xc4ba,0xc4bb,0xc4bd,0xc4be,0xc4bf,
+ 0xc4c0,0xc4c1,0xc4c2,0xc4c3,0xc4c4,0xc4c5,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xc4c6,0xc4c7,0xc4c8,0xc4c9,0xc4ca,0xc4cb,0xc4cc,0xc4cd,
+ 0xc4ce,0xc4cf,0xc4d0,0xc4d1,0xc4d2,0xc4d3,0xc4d4,0xc4d5,0xc4d6,0xc4d7,
+ 0xc4d8,0xc4d9,0xc4da,0xc4db,0xc4dc,0xc4dd,0xc4de,0xc4df,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xc4e0,0xc4e1,0xc4e2,0xc4e3,0xc4e4,0xc4e5,
+ 0xc4e6,0xc4e7,0xc4e8,0xc4ea,0xc4eb,0xc4ec,0xc4ed,0xc4ee,0xc4ef,0xc4f2,
+ 0xc4f3,0xc4f5,0xc4f6,0xc4f7,0xc4f9,0xc4fb,0xc4fc,0xc4fd,0xc4fe,0xc502,
+ 0xc503,0xc504,0xc505,0xc506,0xc507,0xc508,0xc509,0xc50a,0xc50b,0xc50d,
+ 0xc50e,0xc50f,0xc511,0xc512,0xc513,0xc515,0xc516,0xc517,0xc518,0xc519,
+ 0xc51a,0xc51b,0xc51d,0xc51e,0xc51f,0xc520,0xc521,0xc522,0xc523,0xc524,
+ 0xc525,0xc526,0xc527,0xc52a,0xc52b,0xc52d,0xc52e,0xc52f,0xc531,0xc532,
+ 0xc533,0xc534,0xc535,0xc536,0xc537,0xc53a,0xc53c,0xc53e,0xc53f,0xc540,
+ 0xc541,0xc542,0xc543,0xc546,0xc547,0xc54b,0xc54f,0xc550,0xc551,0xc552,
+ 0xc556,0xc55a,0xc55b,0xc55c,0xc55f,0xc562,0xc563,0xc565,0xc566,0xc567,
+ 0xc569,0xc56a,0xc56b,0xc56c,0xc56d,0xc56e,0xc56f,0xc572,0xc576,0xc577,
+ 0xc578,0xc579,0xc57a,0xc57b,0xc57e,0xc57f,0xc581,0xc582,0xc583,0xc585,
+ 0xc586,0xc588,0xc589,0xc58a,0xc58b,0xc58e,0xc590,0xc592,0xc593,0xc594
+ },
+ { /* ku 1e */
+ 0xc596,0xc599,0xc59a,0xc59b,0xc59d,0xc59e,0xc59f,0xc5a1,0xc5a2,0xc5a3,
+ 0xc5a4,0xc5a5,0xc5a6,0xc5a7,0xc5a8,0xc5aa,0xc5ab,0xc5ac,0xc5ad,0xc5ae,
+ 0xc5af,0xc5b0,0xc5b1,0xc5b2,0xc5b3,0xc5b6,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xc5b7,0xc5ba,0xc5bf,0xc5c0,0xc5c1,0xc5c2,0xc5c3,0xc5cb,
+ 0xc5cd,0xc5cf,0xc5d2,0xc5d3,0xc5d5,0xc5d6,0xc5d7,0xc5d9,0xc5da,0xc5db,
+ 0xc5dc,0xc5dd,0xc5de,0xc5df,0xc5e2,0xc5e4,0xc5e6,0xc5e7,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xc5e8,0xc5e9,0xc5ea,0xc5eb,0xc5ef,0xc5f1,
+ 0xc5f2,0xc5f3,0xc5f5,0xc5f8,0xc5f9,0xc5fa,0xc5fb,0xc602,0xc603,0xc604,
+ 0xc609,0xc60a,0xc60b,0xc60d,0xc60e,0xc60f,0xc611,0xc612,0xc613,0xc614,
+ 0xc615,0xc616,0xc617,0xc61a,0xc61d,0xc61e,0xc61f,0xc620,0xc621,0xc622,
+ 0xc623,0xc626,0xc627,0xc629,0xc62a,0xc62b,0xc62f,0xc631,0xc632,0xc636,
+ 0xc638,0xc63a,0xc63c,0xc63d,0xc63e,0xc63f,0xc642,0xc643,0xc645,0xc646,
+ 0xc647,0xc649,0xc64a,0xc64b,0xc64c,0xc64d,0xc64e,0xc64f,0xc652,0xc656,
+ 0xc657,0xc658,0xc659,0xc65a,0xc65b,0xc65e,0xc65f,0xc661,0xc662,0xc663,
+ 0xc664,0xc665,0xc666,0xc667,0xc668,0xc669,0xc66a,0xc66b,0xc66d,0xc66e,
+ 0xc670,0xc672,0xc673,0xc674,0xc675,0xc676,0xc677,0xc67a,0xc67b,0xc67d,
+ 0xc67e,0xc67f,0xc681,0xc682,0xc683,0xc684,0xc685,0xc686,0xc687,0xc68a,
+ 0xc68c,0xc68e,0xc68f,0xc690,0xc691,0xc692,0xc693,0xc696,0xc697,0xc699,
+ 0xc69a,0xc69b,0xc69d,0xc69e,0xc69f,0xc6a0,0xc6a1,0xc6a2,0xc6a3,0xc6a6
+ },
+ { /* ku 1f */
+ 0xc6a8,0xc6aa,0xc6ab,0xc6ac,0xc6ad,0xc6ae,0xc6af,0xc6b2,0xc6b3,0xc6b5,
+ 0xc6b6,0xc6b7,0xc6bb,0xc6bc,0xc6bd,0xc6be,0xc6bf,0xc6c2,0xc6c4,0xc6c6,
+ 0xc6c7,0xc6c8,0xc6c9,0xc6ca,0xc6cb,0xc6ce,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xc6cf,0xc6d1,0xc6d2,0xc6d3,0xc6d5,0xc6d6,0xc6d7,0xc6d8,
+ 0xc6d9,0xc6da,0xc6db,0xc6de,0xc6df,0xc6e2,0xc6e3,0xc6e4,0xc6e5,0xc6e6,
+ 0xc6e7,0xc6ea,0xc6eb,0xc6ed,0xc6ee,0xc6ef,0xc6f1,0xc6f2,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xc6f3,0xc6f4,0xc6f5,0xc6f6,0xc6f7,0xc6fa,
+ 0xc6fb,0xc6fc,0xc6fe,0xc6ff,0xc700,0xc701,0xc702,0xc703,0xc706,0xc707,
+ 0xc709,0xc70a,0xc70b,0xc70d,0xc70e,0xc70f,0xc710,0xc711,0xc712,0xc713,
+ 0xc716,0xc718,0xc71a,0xc71b,0xc71c,0xc71d,0xc71e,0xc71f,0xc722,0xc723,
+ 0xc725,0xc726,0xc727,0xc729,0xc72a,0xc72b,0xc72c,0xc72d,0xc72e,0xc72f,
+ 0xc732,0xc734,0xc736,0xc738,0xc739,0xc73a,0xc73b,0xc73e,0xc73f,0xc741,
+ 0xc742,0xc743,0xc745,0xc746,0xc747,0xc748,0xc749,0xc74b,0xc74e,0xc750,
+ 0xc759,0xc75a,0xc75b,0xc75d,0xc75e,0xc75f,0xc761,0xc762,0xc763,0xc764,
+ 0xc765,0xc766,0xc767,0xc769,0xc76a,0xc76c,0xc76d,0xc76e,0xc76f,0xc770,
+ 0xc771,0xc772,0xc773,0xc776,0xc777,0xc779,0xc77a,0xc77b,0xc77f,0xc780,
+ 0xc781,0xc782,0xc786,0xc78b,0xc78c,0xc78d,0xc78f,0xc792,0xc793,0xc795,
+ 0xc799,0xc79b,0xc79c,0xc79d,0xc79e,0xc79f,0xc7a2,0xc7a7,0xc7a8,0xc7a9,
+ 0xc7aa,0xc7ab,0xc7ae,0xc7af,0xc7b1,0xc7b2,0xc7b3,0xc7b5,0xc7b6,0xc7b7
+ },
+ { /* ku 20 */
+ 0xc7b8,0xc7b9,0xc7ba,0xc7bb,0xc7be,0xc7c2,0xc7c3,0xc7c4,0xc7c5,0xc7c6,
+ 0xc7c7,0xc7ca,0xc7cb,0xc7cd,0xc7cf,0xc7d1,0xc7d2,0xc7d3,0xc7d4,0xc7d5,
+ 0xc7d6,0xc7d7,0xc7d9,0xc7da,0xc7db,0xc7dc,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xc7de,0xc7df,0xc7e0,0xc7e1,0xc7e2,0xc7e3,0xc7e5,0xc7e6,
+ 0xc7e7,0xc7e9,0xc7ea,0xc7eb,0xc7ed,0xc7ee,0xc7ef,0xc7f0,0xc7f1,0xc7f2,
+ 0xc7f3,0xc7f4,0xc7f5,0xc7f6,0xc7f7,0xc7f8,0xc7f9,0xc7fa,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xc7fb,0xc7fc,0xc7fd,0xc7fe,0xc7ff,0xc802,
+ 0xc803,0xc805,0xc806,0xc807,0xc809,0xc80b,0xc80c,0xc80d,0xc80e,0xc80f,
+ 0xc812,0xc814,0xc817,0xc818,0xc819,0xc81a,0xc81b,0xc81e,0xc81f,0xc821,
+ 0xc822,0xc823,0xc825,0xc826,0xc827,0xc828,0xc829,0xc82a,0xc82b,0xc82e,
+ 0xc830,0xc832,0xc833,0xc834,0xc835,0xc836,0xc837,0xc839,0xc83a,0xc83b,
+ 0xc83d,0xc83e,0xc83f,0xc841,0xc842,0xc843,0xc844,0xc845,0xc846,0xc847,
+ 0xc84a,0xc84b,0xc84e,0xc84f,0xc850,0xc851,0xc852,0xc853,0xc855,0xc856,
+ 0xc857,0xc858,0xc859,0xc85a,0xc85b,0xc85c,0xc85d,0xc85e,0xc85f,0xc860,
+ 0xc861,0xc862,0xc863,0xc864,0xc865,0xc866,0xc867,0xc868,0xc869,0xc86a,
+ 0xc86b,0xc86c,0xc86d,0xc86e,0xc86f,0xc872,0xc873,0xc875,0xc876,0xc877,
+ 0xc879,0xc87b,0xc87c,0xc87d,0xc87e,0xc87f,0xc882,0xc884,0xc888,0xc889,
+ 0xc88a,0xc88e,0xc88f,0xc890,0xc891,0xc892,0xc893,0xc895,0xc896,0xc897,
+ 0xc898,0xc899,0xc89a,0xc89b,0xc89c,0xc89e,0xc8a0,0xc8a2,0xc8a3,0xc8a4
+ },
+ { /* ku 21 */
+ 0xc8a5,0xc8a6,0xc8a7,0xc8a9,0xc8aa,0xc8ab,0xc8ac,0xc8ad,0xc8ae,0xc8af,
+ 0xc8b0,0xc8b1,0xc8b2,0xc8b3,0xc8b4,0xc8b5,0xc8b6,0xc8b7,0xc8b8,0xc8b9,
+ 0xc8ba,0xc8bb,0xc8be,0xc8bf,0xc8c0,0xc8c1,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xc8c2,0xc8c3,0xc8c5,0xc8c6,0xc8c7,0xc8c9,0xc8ca,0xc8cb,
+ 0xc8cd,0xc8ce,0xc8cf,0xc8d0,0xc8d1,0xc8d2,0xc8d3,0xc8d6,0xc8d8,0xc8da,
+ 0xc8db,0xc8dc,0xc8dd,0xc8de,0xc8df,0xc8e2,0xc8e3,0xc8e5,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xc8e6,0xc8e7,0xc8e8,0xc8e9,0xc8ea,0xc8eb,
+ 0xc8ec,0xc8ed,0xc8ee,0xc8ef,0xc8f0,0xc8f1,0xc8f2,0xc8f3,0xc8f4,0xc8f6,
+ 0xc8f7,0xc8f8,0xc8f9,0xc8fa,0xc8fb,0xc8fe,0xc8ff,0xc901,0xc902,0xc903,
+ 0xc907,0xc908,0xc909,0xc90a,0xc90b,0xc90e,0x3000,0x3001,0x3002,0x00b7,
+ 0x2025,0x2026,0x00a8,0x3003,0x00ad,0x2015,0x2225,0xff3c,0x223c,0x2018,
+ 0x2019,0x201c,0x201d,0x3014,0x3015,0x3008,0x3009,0x300a,0x300b,0x300c,
+ 0x300d,0x300e,0x300f,0x3010,0x3011,0x00b1,0x00d7,0x00f7,0x2260,0x2264,
+ 0x2265,0x221e,0x2234,0x00b0,0x2032,0x2033,0x2103,0x212b,0xffe0,0xffe1,
+ 0xffe5,0x2642,0x2640,0x2220,0x22a5,0x2312,0x2202,0x2207,0x2261,0x2252,
+ 0x00a7,0x203b,0x2606,0x2605,0x25cb,0x25cf,0x25ce,0x25c7,0x25c6,0x25a1,
+ 0x25a0,0x25b3,0x25b2,0x25bd,0x25bc,0x2192,0x2190,0x2191,0x2193,0x2194,
+ 0x3013,0x226a,0x226b,0x221a,0x223d,0x221d,0x2235,0x222b,0x222c,0x2208,
+ 0x220b,0x2286,0x2287,0x2282,0x2283,0x222a,0x2229,0x2227,0x2228,0xffe2
+ },
+ { /* ku 22 */
+ 0xc910,0xc912,0xc913,0xc914,0xc915,0xc916,0xc917,0xc919,0xc91a,0xc91b,
+ 0xc91c,0xc91d,0xc91e,0xc91f,0xc920,0xc921,0xc922,0xc923,0xc924,0xc925,
+ 0xc926,0xc927,0xc928,0xc929,0xc92a,0xc92b,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xc92d,0xc92e,0xc92f,0xc930,0xc931,0xc932,0xc933,0xc935,
+ 0xc936,0xc937,0xc938,0xc939,0xc93a,0xc93b,0xc93c,0xc93d,0xc93e,0xc93f,
+ 0xc940,0xc941,0xc942,0xc943,0xc944,0xc945,0xc946,0xc947,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xc948,0xc949,0xc94a,0xc94b,0xc94c,0xc94d,
+ 0xc94e,0xc94f,0xc952,0xc953,0xc955,0xc956,0xc957,0xc959,0xc95a,0xc95b,
+ 0xc95c,0xc95d,0xc95e,0xc95f,0xc962,0xc964,0xc965,0xc966,0xc967,0xc968,
+ 0xc969,0xc96a,0xc96b,0xc96d,0xc96e,0xc96f,0x21d2,0x21d4,0x2200,0x2203,
+ 0x00b4,0xff5e,0x02c7,0x02d8,0x02dd,0x02da,0x02d9,0x00b8,0x02db,0x00a1,
+ 0x00bf,0x02d0,0x222e,0x2211,0x220f,0x00a4,0x2109,0x2030,0x25c1,0x25c0,
+ 0x25b7,0x25b6,0x2664,0x2660,0x2661,0x2665,0x2667,0x2663,0x2299,0x25c8,
+ 0x25a3,0x25d0,0x25d1,0x2592,0x25a4,0x25a5,0x25a8,0x25a7,0x25a6,0x25a9,
+ 0x2668,0x260f,0x260e,0x261c,0x261e,0x00b6,0x2020,0x2021,0x2195,0x2197,
+ 0x2199,0x2196,0x2198,0x266d,0x2669,0x266a,0x266c,0x327f,0x321c,0x2116,
+ 0x33c7,0x2122,0x33c2,0x33d8,0x2121,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 23 */
+ 0xc971,0xc972,0xc973,0xc975,0xc976,0xc977,0xc978,0xc979,0xc97a,0xc97b,
+ 0xc97d,0xc97e,0xc97f,0xc980,0xc981,0xc982,0xc983,0xc984,0xc985,0xc986,
+ 0xc987,0xc98a,0xc98b,0xc98d,0xc98e,0xc98f,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xc991,0xc992,0xc993,0xc994,0xc995,0xc996,0xc997,0xc99a,
+ 0xc99c,0xc99e,0xc99f,0xc9a0,0xc9a1,0xc9a2,0xc9a3,0xc9a4,0xc9a5,0xc9a6,
+ 0xc9a7,0xc9a8,0xc9a9,0xc9aa,0xc9ab,0xc9ac,0xc9ad,0xc9ae,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xc9af,0xc9b0,0xc9b1,0xc9b2,0xc9b3,0xc9b4,
+ 0xc9b5,0xc9b6,0xc9b7,0xc9b8,0xc9b9,0xc9ba,0xc9bb,0xc9bc,0xc9bd,0xc9be,
+ 0xc9bf,0xc9c2,0xc9c3,0xc9c5,0xc9c6,0xc9c9,0xc9cb,0xc9cc,0xc9cd,0xc9ce,
+ 0xc9cf,0xc9d2,0xc9d4,0xc9d7,0xc9d8,0xc9db,0xff01,0xff02,0xff03,0xff04,
+ 0xff05,0xff06,0xff07,0xff08,0xff09,0xff0a,0xff0b,0xff0c,0xff0d,0xff0e,
+ 0xff0f,0xff10,0xff11,0xff12,0xff13,0xff14,0xff15,0xff16,0xff17,0xff18,
+ 0xff19,0xff1a,0xff1b,0xff1c,0xff1d,0xff1e,0xff1f,0xff20,0xff21,0xff22,
+ 0xff23,0xff24,0xff25,0xff26,0xff27,0xff28,0xff29,0xff2a,0xff2b,0xff2c,
+ 0xff2d,0xff2e,0xff2f,0xff30,0xff31,0xff32,0xff33,0xff34,0xff35,0xff36,
+ 0xff37,0xff38,0xff39,0xff3a,0xff3b,0xffe6,0xff3d,0xff3e,0xff3f,0xff40,
+ 0xff41,0xff42,0xff43,0xff44,0xff45,0xff46,0xff47,0xff48,0xff49,0xff4a,
+ 0xff4b,0xff4c,0xff4d,0xff4e,0xff4f,0xff50,0xff51,0xff52,0xff53,0xff54,
+ 0xff55,0xff56,0xff57,0xff58,0xff59,0xff5a,0xff5b,0xff5c,0xff5d,0xffe3
+ },
+ { /* ku 24 */
+ 0xc9de,0xc9df,0xc9e1,0xc9e3,0xc9e5,0xc9e6,0xc9e8,0xc9e9,0xc9ea,0xc9eb,
+ 0xc9ee,0xc9f2,0xc9f3,0xc9f4,0xc9f5,0xc9f6,0xc9f7,0xc9fa,0xc9fb,0xc9fd,
+ 0xc9fe,0xc9ff,0xca01,0xca02,0xca03,0xca04,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xca05,0xca06,0xca07,0xca0a,0xca0e,0xca0f,0xca10,0xca11,
+ 0xca12,0xca13,0xca15,0xca16,0xca17,0xca19,0xca1a,0xca1b,0xca1c,0xca1d,
+ 0xca1e,0xca1f,0xca20,0xca21,0xca22,0xca23,0xca24,0xca25,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xca26,0xca27,0xca28,0xca2a,0xca2b,0xca2c,
+ 0xca2d,0xca2e,0xca2f,0xca30,0xca31,0xca32,0xca33,0xca34,0xca35,0xca36,
+ 0xca37,0xca38,0xca39,0xca3a,0xca3b,0xca3c,0xca3d,0xca3e,0xca3f,0xca40,
+ 0xca41,0xca42,0xca43,0xca44,0xca45,0xca46,0x3131,0x3132,0x3133,0x3134,
+ 0x3135,0x3136,0x3137,0x3138,0x3139,0x313a,0x313b,0x313c,0x313d,0x313e,
+ 0x313f,0x3140,0x3141,0x3142,0x3143,0x3144,0x3145,0x3146,0x3147,0x3148,
+ 0x3149,0x314a,0x314b,0x314c,0x314d,0x314e,0x314f,0x3150,0x3151,0x3152,
+ 0x3153,0x3154,0x3155,0x3156,0x3157,0x3158,0x3159,0x315a,0x315b,0x315c,
+ 0x315d,0x315e,0x315f,0x3160,0x3161,0x3162,0x3163,0x3164,0x3165,0x3166,
+ 0x3167,0x3168,0x3169,0x316a,0x316b,0x316c,0x316d,0x316e,0x316f,0x3170,
+ 0x3171,0x3172,0x3173,0x3174,0x3175,0x3176,0x3177,0x3178,0x3179,0x317a,
+ 0x317b,0x317c,0x317d,0x317e,0x317f,0x3180,0x3181,0x3182,0x3183,0x3184,
+ 0x3185,0x3186,0x3187,0x3188,0x3189,0x318a,0x318b,0x318c,0x318d,0x318e
+ },
+ { /* ku 25 */
+ 0xca47,0xca48,0xca49,0xca4a,0xca4b,0xca4e,0xca4f,0xca51,0xca52,0xca53,
+ 0xca55,0xca56,0xca57,0xca58,0xca59,0xca5a,0xca5b,0xca5e,0xca62,0xca63,
+ 0xca64,0xca65,0xca66,0xca67,0xca69,0xca6a,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xca6b,0xca6c,0xca6d,0xca6e,0xca6f,0xca70,0xca71,0xca72,
+ 0xca73,0xca74,0xca75,0xca76,0xca77,0xca78,0xca79,0xca7a,0xca7b,0xca7c,
+ 0xca7e,0xca7f,0xca80,0xca81,0xca82,0xca83,0xca85,0xca86,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xca87,0xca88,0xca89,0xca8a,0xca8b,0xca8c,
+ 0xca8d,0xca8e,0xca8f,0xca90,0xca91,0xca92,0xca93,0xca94,0xca95,0xca96,
+ 0xca97,0xca99,0xca9a,0xca9b,0xca9c,0xca9d,0xca9e,0xca9f,0xcaa0,0xcaa1,
+ 0xcaa2,0xcaa3,0xcaa4,0xcaa5,0xcaa6,0xcaa7,0x2170,0x2171,0x2172,0x2173,
+ 0x2174,0x2175,0x2176,0x2177,0x2178,0x2179,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x2160,0x2161,0x2162,0x2163,0x2164,0x2165,0x2166,0x2167,0x2168,
+ 0x2169,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x0391,0x0392,
+ 0x0393,0x0394,0x0395,0x0396,0x0397,0x0398,0x0399,0x039a,0x039b,0x039c,
+ 0x039d,0x039e,0x039f,0x03a0,0x03a1,0x03a3,0x03a4,0x03a5,0x03a6,0x03a7,
+ 0x03a8,0x03a9,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x03b1,0x03b2,0x03b3,0x03b4,0x03b5,0x03b6,0x03b7,0x03b8,0x03b9,0x03ba,
+ 0x03bb,0x03bc,0x03bd,0x03be,0x03bf,0x03c0,0x03c1,0x03c3,0x03c4,0x03c5,
+ 0x03c6,0x03c7,0x03c8,0x03c9,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 26 */
+ 0xcaa8,0xcaa9,0xcaaa,0xcaab,0xcaac,0xcaad,0xcaae,0xcaaf,0xcab0,0xcab1,
+ 0xcab2,0xcab3,0xcab4,0xcab5,0xcab6,0xcab7,0xcab8,0xcab9,0xcaba,0xcabb,
+ 0xcabe,0xcabf,0xcac1,0xcac2,0xcac3,0xcac5,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xcac6,0xcac7,0xcac8,0xcac9,0xcaca,0xcacb,0xcace,0xcad0,
+ 0xcad2,0xcad4,0xcad5,0xcad6,0xcad7,0xcada,0xcadb,0xcadc,0xcadd,0xcade,
+ 0xcadf,0xcae1,0xcae2,0xcae3,0xcae4,0xcae5,0xcae6,0xcae7,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xcae8,0xcae9,0xcaea,0xcaeb,0xcaed,0xcaee,
+ 0xcaef,0xcaf0,0xcaf1,0xcaf2,0xcaf3,0xcaf5,0xcaf6,0xcaf7,0xcaf8,0xcaf9,
+ 0xcafa,0xcafb,0xcafc,0xcafd,0xcafe,0xcaff,0xcb00,0xcb01,0xcb02,0xcb03,
+ 0xcb04,0xcb05,0xcb06,0xcb07,0xcb09,0xcb0a,0x2500,0x2502,0x250c,0x2510,
+ 0x2518,0x2514,0x251c,0x252c,0x2524,0x2534,0x253c,0x2501,0x2503,0x250f,
+ 0x2513,0x251b,0x2517,0x2523,0x2533,0x252b,0x253b,0x254b,0x2520,0x252f,
+ 0x2528,0x2537,0x253f,0x251d,0x2530,0x2525,0x2538,0x2542,0x2512,0x2511,
+ 0x251a,0x2519,0x2516,0x2515,0x250e,0x250d,0x251e,0x251f,0x2521,0x2522,
+ 0x2526,0x2527,0x2529,0x252a,0x252d,0x252e,0x2531,0x2532,0x2535,0x2536,
+ 0x2539,0x253a,0x253d,0x253e,0x2540,0x2541,0x2543,0x2544,0x2545,0x2546,
+ 0x2547,0x2548,0x2549,0x254a,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 27 */
+ 0xcb0b,0xcb0c,0xcb0d,0xcb0e,0xcb0f,0xcb11,0xcb12,0xcb13,0xcb15,0xcb16,
+ 0xcb17,0xcb19,0xcb1a,0xcb1b,0xcb1c,0xcb1d,0xcb1e,0xcb1f,0xcb22,0xcb23,
+ 0xcb24,0xcb25,0xcb26,0xcb27,0xcb28,0xcb29,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xcb2a,0xcb2b,0xcb2c,0xcb2d,0xcb2e,0xcb2f,0xcb30,0xcb31,
+ 0xcb32,0xcb33,0xcb34,0xcb35,0xcb36,0xcb37,0xcb38,0xcb39,0xcb3a,0xcb3b,
+ 0xcb3c,0xcb3d,0xcb3e,0xcb3f,0xcb40,0xcb42,0xcb43,0xcb44,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xcb45,0xcb46,0xcb47,0xcb4a,0xcb4b,0xcb4d,
+ 0xcb4e,0xcb4f,0xcb51,0xcb52,0xcb53,0xcb54,0xcb55,0xcb56,0xcb57,0xcb5a,
+ 0xcb5b,0xcb5c,0xcb5e,0xcb5f,0xcb60,0xcb61,0xcb62,0xcb63,0xcb65,0xcb66,
+ 0xcb67,0xcb68,0xcb69,0xcb6a,0xcb6b,0xcb6c,0x3395,0x3396,0x3397,0x2113,
+ 0x3398,0x33c4,0x33a3,0x33a4,0x33a5,0x33a6,0x3399,0x339a,0x339b,0x339c,
+ 0x339d,0x339e,0x339f,0x33a0,0x33a1,0x33a2,0x33ca,0x338d,0x338e,0x338f,
+ 0x33cf,0x3388,0x3389,0x33c8,0x33a7,0x33a8,0x33b0,0x33b1,0x33b2,0x33b3,
+ 0x33b4,0x33b5,0x33b6,0x33b7,0x33b8,0x33b9,0x3380,0x3381,0x3382,0x3383,
+ 0x3384,0x33ba,0x33bb,0x33bc,0x33bd,0x33be,0x33bf,0x3390,0x3391,0x3392,
+ 0x3393,0x3394,0x2126,0x33c0,0x33c1,0x338a,0x338b,0x338c,0x33d6,0x33c5,
+ 0x33ad,0x33ae,0x33af,0x33db,0x33a9,0x33aa,0x33ab,0x33ac,0x33dd,0x33d0,
+ 0x33d3,0x33c3,0x33c9,0x33dc,0x33c6,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 28 */
+ 0xcb6d,0xcb6e,0xcb6f,0xcb70,0xcb71,0xcb72,0xcb73,0xcb74,0xcb75,0xcb76,
+ 0xcb77,0xcb7a,0xcb7b,0xcb7c,0xcb7d,0xcb7e,0xcb7f,0xcb80,0xcb81,0xcb82,
+ 0xcb83,0xcb84,0xcb85,0xcb86,0xcb87,0xcb88,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xcb89,0xcb8a,0xcb8b,0xcb8c,0xcb8d,0xcb8e,0xcb8f,0xcb90,
+ 0xcb91,0xcb92,0xcb93,0xcb94,0xcb95,0xcb96,0xcb97,0xcb98,0xcb99,0xcb9a,
+ 0xcb9b,0xcb9d,0xcb9e,0xcb9f,0xcba0,0xcba1,0xcba2,0xcba3,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xcba4,0xcba5,0xcba6,0xcba7,0xcba8,0xcba9,
+ 0xcbaa,0xcbab,0xcbac,0xcbad,0xcbae,0xcbaf,0xcbb0,0xcbb1,0xcbb2,0xcbb3,
+ 0xcbb4,0xcbb5,0xcbb6,0xcbb7,0xcbb9,0xcbba,0xcbbb,0xcbbc,0xcbbd,0xcbbe,
+ 0xcbbf,0xcbc0,0xcbc1,0xcbc2,0xcbc3,0xcbc4,0x00c6,0x00d0,0x00aa,0x0126,
+ UBOGON,0x0132,UBOGON,0x013f,0x0141,0x00d8,0x0152,0x00ba,0x00de,0x0166,
+ 0x014a,UBOGON,0x3260,0x3261,0x3262,0x3263,0x3264,0x3265,0x3266,0x3267,
+ 0x3268,0x3269,0x326a,0x326b,0x326c,0x326d,0x326e,0x326f,0x3270,0x3271,
+ 0x3272,0x3273,0x3274,0x3275,0x3276,0x3277,0x3278,0x3279,0x327a,0x327b,
+ 0x24d0,0x24d1,0x24d2,0x24d3,0x24d4,0x24d5,0x24d6,0x24d7,0x24d8,0x24d9,
+ 0x24da,0x24db,0x24dc,0x24dd,0x24de,0x24df,0x24e0,0x24e1,0x24e2,0x24e3,
+ 0x24e4,0x24e5,0x24e6,0x24e7,0x24e8,0x24e9,0x2460,0x2461,0x2462,0x2463,
+ 0x2464,0x2465,0x2466,0x2467,0x2468,0x2469,0x246a,0x246b,0x246c,0x246d,
+ 0x246e,0x00bd,0x2153,0x2154,0x00bc,0x00be,0x215b,0x215c,0x215d,0x215e
+ },
+ { /* ku 29 */
+ 0xcbc5,0xcbc6,0xcbc7,0xcbc8,0xcbc9,0xcbca,0xcbcb,0xcbcc,0xcbcd,0xcbce,
+ 0xcbcf,0xcbd0,0xcbd1,0xcbd2,0xcbd3,0xcbd5,0xcbd6,0xcbd7,0xcbd8,0xcbd9,
+ 0xcbda,0xcbdb,0xcbdc,0xcbdd,0xcbde,0xcbdf,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xcbe0,0xcbe1,0xcbe2,0xcbe3,0xcbe5,0xcbe6,0xcbe8,0xcbea,
+ 0xcbeb,0xcbec,0xcbed,0xcbee,0xcbef,0xcbf0,0xcbf1,0xcbf2,0xcbf3,0xcbf4,
+ 0xcbf5,0xcbf6,0xcbf7,0xcbf8,0xcbf9,0xcbfa,0xcbfb,0xcbfc,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xcbfd,0xcbfe,0xcbff,0xcc00,0xcc01,0xcc02,
+ 0xcc03,0xcc04,0xcc05,0xcc06,0xcc07,0xcc08,0xcc09,0xcc0a,0xcc0b,0xcc0e,
+ 0xcc0f,0xcc11,0xcc12,0xcc13,0xcc15,0xcc16,0xcc17,0xcc18,0xcc19,0xcc1a,
+ 0xcc1b,0xcc1e,0xcc1f,0xcc20,0xcc23,0xcc24,0x00e6,0x0111,0x00f0,0x0127,
+ 0x0131,0x0133,0x0138,0x0140,0x0142,0x00f8,0x0153,0x00df,0x00fe,0x0167,
+ 0x014b,0x0149,0x3200,0x3201,0x3202,0x3203,0x3204,0x3205,0x3206,0x3207,
+ 0x3208,0x3209,0x320a,0x320b,0x320c,0x320d,0x320e,0x320f,0x3210,0x3211,
+ 0x3212,0x3213,0x3214,0x3215,0x3216,0x3217,0x3218,0x3219,0x321a,0x321b,
+ 0x249c,0x249d,0x249e,0x249f,0x24a0,0x24a1,0x24a2,0x24a3,0x24a4,0x24a5,
+ 0x24a6,0x24a7,0x24a8,0x24a9,0x24aa,0x24ab,0x24ac,0x24ad,0x24ae,0x24af,
+ 0x24b0,0x24b1,0x24b2,0x24b3,0x24b4,0x24b5,0x2474,0x2475,0x2476,0x2477,
+ 0x2478,0x2479,0x247a,0x247b,0x247c,0x247d,0x247e,0x247f,0x2480,0x2481,
+ 0x2482,0x00b9,0x00b2,0x00b3,0x2074,0x207f,0x2081,0x2082,0x2083,0x2084
+ },
+ { /* ku 2a */
+ 0xcc25,0xcc26,0xcc2a,0xcc2b,0xcc2d,0xcc2f,0xcc31,0xcc32,0xcc33,0xcc34,
+ 0xcc35,0xcc36,0xcc37,0xcc3a,0xcc3f,0xcc40,0xcc41,0xcc42,0xcc43,0xcc46,
+ 0xcc47,0xcc49,0xcc4a,0xcc4b,0xcc4d,0xcc4e,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xcc4f,0xcc50,0xcc51,0xcc52,0xcc53,0xcc56,0xcc5a,0xcc5b,
+ 0xcc5c,0xcc5d,0xcc5e,0xcc5f,0xcc61,0xcc62,0xcc63,0xcc65,0xcc67,0xcc69,
+ 0xcc6a,0xcc6b,0xcc6c,0xcc6d,0xcc6e,0xcc6f,0xcc71,0xcc72,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xcc73,0xcc74,0xcc76,0xcc77,0xcc78,0xcc79,
+ 0xcc7a,0xcc7b,0xcc7c,0xcc7d,0xcc7e,0xcc7f,0xcc80,0xcc81,0xcc82,0xcc83,
+ 0xcc84,0xcc85,0xcc86,0xcc87,0xcc88,0xcc89,0xcc8a,0xcc8b,0xcc8c,0xcc8d,
+ 0xcc8e,0xcc8f,0xcc90,0xcc91,0xcc92,0xcc93,0x3041,0x3042,0x3043,0x3044,
+ 0x3045,0x3046,0x3047,0x3048,0x3049,0x304a,0x304b,0x304c,0x304d,0x304e,
+ 0x304f,0x3050,0x3051,0x3052,0x3053,0x3054,0x3055,0x3056,0x3057,0x3058,
+ 0x3059,0x305a,0x305b,0x305c,0x305d,0x305e,0x305f,0x3060,0x3061,0x3062,
+ 0x3063,0x3064,0x3065,0x3066,0x3067,0x3068,0x3069,0x306a,0x306b,0x306c,
+ 0x306d,0x306e,0x306f,0x3070,0x3071,0x3072,0x3073,0x3074,0x3075,0x3076,
+ 0x3077,0x3078,0x3079,0x307a,0x307b,0x307c,0x307d,0x307e,0x307f,0x3080,
+ 0x3081,0x3082,0x3083,0x3084,0x3085,0x3086,0x3087,0x3088,0x3089,0x308a,
+ 0x308b,0x308c,0x308d,0x308e,0x308f,0x3090,0x3091,0x3092,0x3093,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2b */
+ 0xcc94,0xcc95,0xcc96,0xcc97,0xcc9a,0xcc9b,0xcc9d,0xcc9e,0xcc9f,0xcca1,
+ 0xcca2,0xcca3,0xcca4,0xcca5,0xcca6,0xcca7,0xccaa,0xccae,0xccaf,0xccb0,
+ 0xccb1,0xccb2,0xccb3,0xccb6,0xccb7,0xccb9,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xccba,0xccbb,0xccbd,0xccbe,0xccbf,0xccc0,0xccc1,0xccc2,
+ 0xccc3,0xccc6,0xccc8,0xccca,0xcccb,0xcccc,0xcccd,0xccce,0xcccf,0xccd1,
+ 0xccd2,0xccd3,0xccd5,0xccd6,0xccd7,0xccd8,0xccd9,0xccda,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xccdb,0xccdc,0xccdd,0xccde,0xccdf,0xcce0,
+ 0xcce1,0xcce2,0xcce3,0xcce5,0xcce6,0xcce7,0xcce8,0xcce9,0xccea,0xcceb,
+ 0xcced,0xccee,0xccef,0xccf1,0xccf2,0xccf3,0xccf4,0xccf5,0xccf6,0xccf7,
+ 0xccf8,0xccf9,0xccfa,0xccfb,0xccfc,0xccfd,0x30a1,0x30a2,0x30a3,0x30a4,
+ 0x30a5,0x30a6,0x30a7,0x30a8,0x30a9,0x30aa,0x30ab,0x30ac,0x30ad,0x30ae,
+ 0x30af,0x30b0,0x30b1,0x30b2,0x30b3,0x30b4,0x30b5,0x30b6,0x30b7,0x30b8,
+ 0x30b9,0x30ba,0x30bb,0x30bc,0x30bd,0x30be,0x30bf,0x30c0,0x30c1,0x30c2,
+ 0x30c3,0x30c4,0x30c5,0x30c6,0x30c7,0x30c8,0x30c9,0x30ca,0x30cb,0x30cc,
+ 0x30cd,0x30ce,0x30cf,0x30d0,0x30d1,0x30d2,0x30d3,0x30d4,0x30d5,0x30d6,
+ 0x30d7,0x30d8,0x30d9,0x30da,0x30db,0x30dc,0x30dd,0x30de,0x30df,0x30e0,
+ 0x30e1,0x30e2,0x30e3,0x30e4,0x30e5,0x30e6,0x30e7,0x30e8,0x30e9,0x30ea,
+ 0x30eb,0x30ec,0x30ed,0x30ee,0x30ef,0x30f0,0x30f1,0x30f2,0x30f3,0x30f4,
+ 0x30f5,0x30f6,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2c */
+ 0xccfe,0xccff,0xcd00,0xcd02,0xcd03,0xcd04,0xcd05,0xcd06,0xcd07,0xcd0a,
+ 0xcd0b,0xcd0d,0xcd0e,0xcd0f,0xcd11,0xcd12,0xcd13,0xcd14,0xcd15,0xcd16,
+ 0xcd17,0xcd1a,0xcd1c,0xcd1e,0xcd1f,0xcd20,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xcd21,0xcd22,0xcd23,0xcd25,0xcd26,0xcd27,0xcd29,0xcd2a,
+ 0xcd2b,0xcd2d,0xcd2e,0xcd2f,0xcd30,0xcd31,0xcd32,0xcd33,0xcd34,0xcd35,
+ 0xcd36,0xcd37,0xcd38,0xcd3a,0xcd3b,0xcd3c,0xcd3d,0xcd3e,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xcd3f,0xcd40,0xcd41,0xcd42,0xcd43,0xcd44,
+ 0xcd45,0xcd46,0xcd47,0xcd48,0xcd49,0xcd4a,0xcd4b,0xcd4c,0xcd4d,0xcd4e,
+ 0xcd4f,0xcd50,0xcd51,0xcd52,0xcd53,0xcd54,0xcd55,0xcd56,0xcd57,0xcd58,
+ 0xcd59,0xcd5a,0xcd5b,0xcd5d,0xcd5e,0xcd5f,0x0410,0x0411,0x0412,0x0413,
+ 0x0414,0x0415,0x0401,0x0416,0x0417,0x0418,0x0419,0x041a,0x041b,0x041c,
+ 0x041d,0x041e,0x041f,0x0420,0x0421,0x0422,0x0423,0x0424,0x0425,0x0426,
+ 0x0427,0x0428,0x0429,0x042a,0x042b,0x042c,0x042d,0x042e,0x042f,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0x0430,0x0431,0x0432,0x0433,0x0434,0x0435,
+ 0x0451,0x0436,0x0437,0x0438,0x0439,0x043a,0x043b,0x043c,0x043d,0x043e,
+ 0x043f,0x0440,0x0441,0x0442,0x0443,0x0444,0x0445,0x0446,0x0447,0x0448,
+ 0x0449,0x044a,0x044b,0x044c,0x044d,0x044e,0x044f,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2d */
+ 0xcd61,0xcd62,0xcd63,0xcd65,0xcd66,0xcd67,0xcd68,0xcd69,0xcd6a,0xcd6b,
+ 0xcd6e,0xcd70,0xcd72,0xcd73,0xcd74,0xcd75,0xcd76,0xcd77,0xcd79,0xcd7a,
+ 0xcd7b,0xcd7c,0xcd7d,0xcd7e,0xcd7f,0xcd80,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xcd81,0xcd82,0xcd83,0xcd84,0xcd85,0xcd86,0xcd87,0xcd89,
+ 0xcd8a,0xcd8b,0xcd8c,0xcd8d,0xcd8e,0xcd8f,0xcd90,0xcd91,0xcd92,0xcd93,
+ 0xcd96,0xcd97,0xcd99,0xcd9a,0xcd9b,0xcd9d,0xcd9e,0xcd9f,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xcda0,0xcda1,0xcda2,0xcda3,0xcda6,0xcda8,
+ 0xcdaa,0xcdab,0xcdac,0xcdad,0xcdae,0xcdaf,0xcdb1,0xcdb2,0xcdb3,0xcdb4,
+ 0xcdb5,0xcdb6,0xcdb7,0xcdb8,0xcdb9,0xcdba,0xcdbb,0xcdbc,0xcdbd,0xcdbe,
+ 0xcdbf,0xcdc0,0xcdc1,0xcdc2,0xcdc3,0xcdc5,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2e */
+ 0xcdc6,0xcdc7,0xcdc8,0xcdc9,0xcdca,0xcdcb,0xcdcd,0xcdce,0xcdcf,0xcdd1,
+ 0xcdd2,0xcdd3,0xcdd4,0xcdd5,0xcdd6,0xcdd7,0xcdd8,0xcdd9,0xcdda,0xcddb,
+ 0xcddc,0xcddd,0xcdde,0xcddf,0xcde0,0xcde1,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xcde2,0xcde3,0xcde4,0xcde5,0xcde6,0xcde7,0xcde9,0xcdea,
+ 0xcdeb,0xcded,0xcdee,0xcdef,0xcdf1,0xcdf2,0xcdf3,0xcdf4,0xcdf5,0xcdf6,
+ 0xcdf7,0xcdfa,0xcdfc,0xcdfe,0xcdff,0xce00,0xce01,0xce02,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xce03,0xce05,0xce06,0xce07,0xce09,0xce0a,
+ 0xce0b,0xce0d,0xce0e,0xce0f,0xce10,0xce11,0xce12,0xce13,0xce15,0xce16,
+ 0xce17,0xce18,0xce1a,0xce1b,0xce1c,0xce1d,0xce1e,0xce1f,0xce22,0xce23,
+ 0xce25,0xce26,0xce27,0xce29,0xce2a,0xce2b,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 2f */
+ 0xce2c,0xce2d,0xce2e,0xce2f,0xce32,0xce34,0xce36,0xce37,0xce38,0xce39,
+ 0xce3a,0xce3b,0xce3c,0xce3d,0xce3e,0xce3f,0xce40,0xce41,0xce42,0xce43,
+ 0xce44,0xce45,0xce46,0xce47,0xce48,0xce49,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xce4a,0xce4b,0xce4c,0xce4d,0xce4e,0xce4f,0xce50,0xce51,
+ 0xce52,0xce53,0xce54,0xce55,0xce56,0xce57,0xce5a,0xce5b,0xce5d,0xce5e,
+ 0xce62,0xce63,0xce64,0xce65,0xce66,0xce67,0xce6a,0xce6c,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xce6e,0xce6f,0xce70,0xce71,0xce72,0xce73,
+ 0xce76,0xce77,0xce79,0xce7a,0xce7b,0xce7d,0xce7e,0xce7f,0xce80,0xce81,
+ 0xce82,0xce83,0xce86,0xce88,0xce8a,0xce8b,0xce8c,0xce8d,0xce8e,0xce8f,
+ 0xce92,0xce93,0xce95,0xce96,0xce97,0xce99,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 30 */
+ 0xce9a,0xce9b,0xce9c,0xce9d,0xce9e,0xce9f,0xcea2,0xcea6,0xcea7,0xcea8,
+ 0xcea9,0xceaa,0xceab,0xceae,0xceaf,0xceb0,0xceb1,0xceb2,0xceb3,0xceb4,
+ 0xceb5,0xceb6,0xceb7,0xceb8,0xceb9,0xceba,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xcebb,0xcebc,0xcebd,0xcebe,0xcebf,0xcec0,0xcec2,0xcec3,
+ 0xcec4,0xcec5,0xcec6,0xcec7,0xcec8,0xcec9,0xceca,0xcecb,0xcecc,0xcecd,
+ 0xcece,0xcecf,0xced0,0xced1,0xced2,0xced3,0xced4,0xced5,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xced6,0xced7,0xced8,0xced9,0xceda,0xcedb,
+ 0xcedc,0xcedd,0xcede,0xcedf,0xcee0,0xcee1,0xcee2,0xcee3,0xcee6,0xcee7,
+ 0xcee9,0xceea,0xceed,0xceee,0xceef,0xcef0,0xcef1,0xcef2,0xcef3,0xcef6,
+ 0xcefa,0xcefb,0xcefc,0xcefd,0xcefe,0xceff,0xac00,0xac01,0xac04,0xac07,
+ 0xac08,0xac09,0xac0a,0xac10,0xac11,0xac12,0xac13,0xac14,0xac15,0xac16,
+ 0xac17,0xac19,0xac1a,0xac1b,0xac1c,0xac1d,0xac20,0xac24,0xac2c,0xac2d,
+ 0xac2f,0xac30,0xac31,0xac38,0xac39,0xac3c,0xac40,0xac4b,0xac4d,0xac54,
+ 0xac58,0xac5c,0xac70,0xac71,0xac74,0xac77,0xac78,0xac7a,0xac80,0xac81,
+ 0xac83,0xac84,0xac85,0xac86,0xac89,0xac8a,0xac8b,0xac8c,0xac90,0xac94,
+ 0xac9c,0xac9d,0xac9f,0xaca0,0xaca1,0xaca8,0xaca9,0xacaa,0xacac,0xacaf,
+ 0xacb0,0xacb8,0xacb9,0xacbb,0xacbc,0xacbd,0xacc1,0xacc4,0xacc8,0xaccc,
+ 0xacd5,0xacd7,0xace0,0xace1,0xace4,0xace7,0xace8,0xacea,0xacec,0xacef,
+ 0xacf0,0xacf1,0xacf3,0xacf5,0xacf6,0xacfc,0xacfd,0xad00,0xad04,0xad06
+ },
+ { /* ku 31 */
+ 0xcf02,0xcf03,0xcf05,0xcf06,0xcf07,0xcf09,0xcf0a,0xcf0b,0xcf0c,0xcf0d,
+ 0xcf0e,0xcf0f,0xcf12,0xcf14,0xcf16,0xcf17,0xcf18,0xcf19,0xcf1a,0xcf1b,
+ 0xcf1d,0xcf1e,0xcf1f,0xcf21,0xcf22,0xcf23,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xcf25,0xcf26,0xcf27,0xcf28,0xcf29,0xcf2a,0xcf2b,0xcf2e,
+ 0xcf32,0xcf33,0xcf34,0xcf35,0xcf36,0xcf37,0xcf39,0xcf3a,0xcf3b,0xcf3c,
+ 0xcf3d,0xcf3e,0xcf3f,0xcf40,0xcf41,0xcf42,0xcf43,0xcf44,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xcf45,0xcf46,0xcf47,0xcf48,0xcf49,0xcf4a,
+ 0xcf4b,0xcf4c,0xcf4d,0xcf4e,0xcf4f,0xcf50,0xcf51,0xcf52,0xcf53,0xcf56,
+ 0xcf57,0xcf59,0xcf5a,0xcf5b,0xcf5d,0xcf5e,0xcf5f,0xcf60,0xcf61,0xcf62,
+ 0xcf63,0xcf66,0xcf68,0xcf6a,0xcf6b,0xcf6c,0xad0c,0xad0d,0xad0f,0xad11,
+ 0xad18,0xad1c,0xad20,0xad29,0xad2c,0xad2d,0xad34,0xad35,0xad38,0xad3c,
+ 0xad44,0xad45,0xad47,0xad49,0xad50,0xad54,0xad58,0xad61,0xad63,0xad6c,
+ 0xad6d,0xad70,0xad73,0xad74,0xad75,0xad76,0xad7b,0xad7c,0xad7d,0xad7f,
+ 0xad81,0xad82,0xad88,0xad89,0xad8c,0xad90,0xad9c,0xad9d,0xada4,0xadb7,
+ 0xadc0,0xadc1,0xadc4,0xadc8,0xadd0,0xadd1,0xadd3,0xaddc,0xade0,0xade4,
+ 0xadf8,0xadf9,0xadfc,0xadff,0xae00,0xae01,0xae08,0xae09,0xae0b,0xae0d,
+ 0xae14,0xae30,0xae31,0xae34,0xae37,0xae38,0xae3a,0xae40,0xae41,0xae43,
+ 0xae45,0xae46,0xae4a,0xae4c,0xae4d,0xae4e,0xae50,0xae54,0xae56,0xae5c,
+ 0xae5d,0xae5f,0xae60,0xae61,0xae65,0xae68,0xae69,0xae6c,0xae70,0xae78
+ },
+ { /* ku 32 */
+ 0xcf6d,0xcf6e,0xcf6f,0xcf72,0xcf73,0xcf75,0xcf76,0xcf77,0xcf79,0xcf7a,
+ 0xcf7b,0xcf7c,0xcf7d,0xcf7e,0xcf7f,0xcf81,0xcf82,0xcf83,0xcf84,0xcf86,
+ 0xcf87,0xcf88,0xcf89,0xcf8a,0xcf8b,0xcf8d,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xcf8e,0xcf8f,0xcf90,0xcf91,0xcf92,0xcf93,0xcf94,0xcf95,
+ 0xcf96,0xcf97,0xcf98,0xcf99,0xcf9a,0xcf9b,0xcf9c,0xcf9d,0xcf9e,0xcf9f,
+ 0xcfa0,0xcfa2,0xcfa3,0xcfa4,0xcfa5,0xcfa6,0xcfa7,0xcfa9,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xcfaa,0xcfab,0xcfac,0xcfad,0xcfae,0xcfaf,
+ 0xcfb1,0xcfb2,0xcfb3,0xcfb4,0xcfb5,0xcfb6,0xcfb7,0xcfb8,0xcfb9,0xcfba,
+ 0xcfbb,0xcfbc,0xcfbd,0xcfbe,0xcfbf,0xcfc0,0xcfc1,0xcfc2,0xcfc3,0xcfc5,
+ 0xcfc6,0xcfc7,0xcfc8,0xcfc9,0xcfca,0xcfcb,0xae79,0xae7b,0xae7c,0xae7d,
+ 0xae84,0xae85,0xae8c,0xaebc,0xaebd,0xaebe,0xaec0,0xaec4,0xaecc,0xaecd,
+ 0xaecf,0xaed0,0xaed1,0xaed8,0xaed9,0xaedc,0xaee8,0xaeeb,0xaeed,0xaef4,
+ 0xaef8,0xaefc,0xaf07,0xaf08,0xaf0d,0xaf10,0xaf2c,0xaf2d,0xaf30,0xaf32,
+ 0xaf34,0xaf3c,0xaf3d,0xaf3f,0xaf41,0xaf42,0xaf43,0xaf48,0xaf49,0xaf50,
+ 0xaf5c,0xaf5d,0xaf64,0xaf65,0xaf79,0xaf80,0xaf84,0xaf88,0xaf90,0xaf91,
+ 0xaf95,0xaf9c,0xafb8,0xafb9,0xafbc,0xafc0,0xafc7,0xafc8,0xafc9,0xafcb,
+ 0xafcd,0xafce,0xafd4,0xafdc,0xafe8,0xafe9,0xaff0,0xaff1,0xaff4,0xaff8,
+ 0xb000,0xb001,0xb004,0xb00c,0xb010,0xb014,0xb01c,0xb01d,0xb028,0xb044,
+ 0xb045,0xb048,0xb04a,0xb04c,0xb04e,0xb053,0xb054,0xb055,0xb057,0xb059
+ },
+ { /* ku 33 */
+ 0xcfcc,0xcfcd,0xcfce,0xcfcf,0xcfd0,0xcfd1,0xcfd2,0xcfd3,0xcfd4,0xcfd5,
+ 0xcfd6,0xcfd7,0xcfd8,0xcfd9,0xcfda,0xcfdb,0xcfdc,0xcfdd,0xcfde,0xcfdf,
+ 0xcfe2,0xcfe3,0xcfe5,0xcfe6,0xcfe7,0xcfe9,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xcfea,0xcfeb,0xcfec,0xcfed,0xcfee,0xcfef,0xcff2,0xcff4,
+ 0xcff6,0xcff7,0xcff8,0xcff9,0xcffa,0xcffb,0xcffd,0xcffe,0xcfff,0xd001,
+ 0xd002,0xd003,0xd005,0xd006,0xd007,0xd008,0xd009,0xd00a,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd00b,0xd00c,0xd00d,0xd00e,0xd00f,0xd010,
+ 0xd012,0xd013,0xd014,0xd015,0xd016,0xd017,0xd019,0xd01a,0xd01b,0xd01c,
+ 0xd01d,0xd01e,0xd01f,0xd020,0xd021,0xd022,0xd023,0xd024,0xd025,0xd026,
+ 0xd027,0xd028,0xd029,0xd02a,0xd02b,0xd02c,0xb05d,0xb07c,0xb07d,0xb080,
+ 0xb084,0xb08c,0xb08d,0xb08f,0xb091,0xb098,0xb099,0xb09a,0xb09c,0xb09f,
+ 0xb0a0,0xb0a1,0xb0a2,0xb0a8,0xb0a9,0xb0ab,0xb0ac,0xb0ad,0xb0ae,0xb0af,
+ 0xb0b1,0xb0b3,0xb0b4,0xb0b5,0xb0b8,0xb0bc,0xb0c4,0xb0c5,0xb0c7,0xb0c8,
+ 0xb0c9,0xb0d0,0xb0d1,0xb0d4,0xb0d8,0xb0e0,0xb0e5,0xb108,0xb109,0xb10b,
+ 0xb10c,0xb110,0xb112,0xb113,0xb118,0xb119,0xb11b,0xb11c,0xb11d,0xb123,
+ 0xb124,0xb125,0xb128,0xb12c,0xb134,0xb135,0xb137,0xb138,0xb139,0xb140,
+ 0xb141,0xb144,0xb148,0xb150,0xb151,0xb154,0xb155,0xb158,0xb15c,0xb160,
+ 0xb178,0xb179,0xb17c,0xb180,0xb182,0xb188,0xb189,0xb18b,0xb18d,0xb192,
+ 0xb193,0xb194,0xb198,0xb19c,0xb1a8,0xb1cc,0xb1d0,0xb1d4,0xb1dc,0xb1dd
+ },
+ { /* ku 34 */
+ 0xd02e,0xd02f,0xd030,0xd031,0xd032,0xd033,0xd036,0xd037,0xd039,0xd03a,
+ 0xd03b,0xd03d,0xd03e,0xd03f,0xd040,0xd041,0xd042,0xd043,0xd046,0xd048,
+ 0xd04a,0xd04b,0xd04c,0xd04d,0xd04e,0xd04f,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd051,0xd052,0xd053,0xd055,0xd056,0xd057,0xd059,0xd05a,
+ 0xd05b,0xd05c,0xd05d,0xd05e,0xd05f,0xd061,0xd062,0xd063,0xd064,0xd065,
+ 0xd066,0xd067,0xd068,0xd069,0xd06a,0xd06b,0xd06e,0xd06f,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd071,0xd072,0xd073,0xd075,0xd076,0xd077,
+ 0xd078,0xd079,0xd07a,0xd07b,0xd07e,0xd07f,0xd080,0xd082,0xd083,0xd084,
+ 0xd085,0xd086,0xd087,0xd088,0xd089,0xd08a,0xd08b,0xd08c,0xd08d,0xd08e,
+ 0xd08f,0xd090,0xd091,0xd092,0xd093,0xd094,0xb1df,0xb1e8,0xb1e9,0xb1ec,
+ 0xb1f0,0xb1f9,0xb1fb,0xb1fd,0xb204,0xb205,0xb208,0xb20b,0xb20c,0xb214,
+ 0xb215,0xb217,0xb219,0xb220,0xb234,0xb23c,0xb258,0xb25c,0xb260,0xb268,
+ 0xb269,0xb274,0xb275,0xb27c,0xb284,0xb285,0xb289,0xb290,0xb291,0xb294,
+ 0xb298,0xb299,0xb29a,0xb2a0,0xb2a1,0xb2a3,0xb2a5,0xb2a6,0xb2aa,0xb2ac,
+ 0xb2b0,0xb2b4,0xb2c8,0xb2c9,0xb2cc,0xb2d0,0xb2d2,0xb2d8,0xb2d9,0xb2db,
+ 0xb2dd,0xb2e2,0xb2e4,0xb2e5,0xb2e6,0xb2e8,0xb2eb,0xb2ec,0xb2ed,0xb2ee,
+ 0xb2ef,0xb2f3,0xb2f4,0xb2f5,0xb2f7,0xb2f8,0xb2f9,0xb2fa,0xb2fb,0xb2ff,
+ 0xb300,0xb301,0xb304,0xb308,0xb310,0xb311,0xb313,0xb314,0xb315,0xb31c,
+ 0xb354,0xb355,0xb356,0xb358,0xb35b,0xb35c,0xb35e,0xb35f,0xb364,0xb365
+ },
+ { /* ku 35 */
+ 0xd095,0xd096,0xd097,0xd098,0xd099,0xd09a,0xd09b,0xd09c,0xd09d,0xd09e,
+ 0xd09f,0xd0a0,0xd0a1,0xd0a2,0xd0a3,0xd0a6,0xd0a7,0xd0a9,0xd0aa,0xd0ab,
+ 0xd0ad,0xd0ae,0xd0af,0xd0b0,0xd0b1,0xd0b2,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd0b3,0xd0b6,0xd0b8,0xd0ba,0xd0bb,0xd0bc,0xd0bd,0xd0be,
+ 0xd0bf,0xd0c2,0xd0c3,0xd0c5,0xd0c6,0xd0c7,0xd0ca,0xd0cb,0xd0cc,0xd0cd,
+ 0xd0ce,0xd0cf,0xd0d2,0xd0d6,0xd0d7,0xd0d8,0xd0d9,0xd0da,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd0db,0xd0de,0xd0df,0xd0e1,0xd0e2,0xd0e3,
+ 0xd0e5,0xd0e6,0xd0e7,0xd0e8,0xd0e9,0xd0ea,0xd0eb,0xd0ee,0xd0f2,0xd0f3,
+ 0xd0f4,0xd0f5,0xd0f6,0xd0f7,0xd0f9,0xd0fa,0xd0fb,0xd0fc,0xd0fd,0xd0fe,
+ 0xd0ff,0xd100,0xd101,0xd102,0xd103,0xd104,0xb367,0xb369,0xb36b,0xb36e,
+ 0xb370,0xb371,0xb374,0xb378,0xb380,0xb381,0xb383,0xb384,0xb385,0xb38c,
+ 0xb390,0xb394,0xb3a0,0xb3a1,0xb3a8,0xb3ac,0xb3c4,0xb3c5,0xb3c8,0xb3cb,
+ 0xb3cc,0xb3ce,0xb3d0,0xb3d4,0xb3d5,0xb3d7,0xb3d9,0xb3db,0xb3dd,0xb3e0,
+ 0xb3e4,0xb3e8,0xb3fc,0xb410,0xb418,0xb41c,0xb420,0xb428,0xb429,0xb42b,
+ 0xb434,0xb450,0xb451,0xb454,0xb458,0xb460,0xb461,0xb463,0xb465,0xb46c,
+ 0xb480,0xb488,0xb49d,0xb4a4,0xb4a8,0xb4ac,0xb4b5,0xb4b7,0xb4b9,0xb4c0,
+ 0xb4c4,0xb4c8,0xb4d0,0xb4d5,0xb4dc,0xb4dd,0xb4e0,0xb4e3,0xb4e4,0xb4e6,
+ 0xb4ec,0xb4ed,0xb4ef,0xb4f1,0xb4f8,0xb514,0xb515,0xb518,0xb51b,0xb51c,
+ 0xb524,0xb525,0xb527,0xb528,0xb529,0xb52a,0xb530,0xb531,0xb534,0xb538
+ },
+ { /* ku 36 */
+ 0xd105,0xd106,0xd107,0xd108,0xd109,0xd10a,0xd10b,0xd10c,0xd10e,0xd10f,
+ 0xd110,0xd111,0xd112,0xd113,0xd114,0xd115,0xd116,0xd117,0xd118,0xd119,
+ 0xd11a,0xd11b,0xd11c,0xd11d,0xd11e,0xd11f,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd120,0xd121,0xd122,0xd123,0xd124,0xd125,0xd126,0xd127,
+ 0xd128,0xd129,0xd12a,0xd12b,0xd12c,0xd12d,0xd12e,0xd12f,0xd132,0xd133,
+ 0xd135,0xd136,0xd137,0xd139,0xd13b,0xd13c,0xd13d,0xd13e,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd13f,0xd142,0xd146,0xd147,0xd148,0xd149,
+ 0xd14a,0xd14b,0xd14e,0xd14f,0xd151,0xd152,0xd153,0xd155,0xd156,0xd157,
+ 0xd158,0xd159,0xd15a,0xd15b,0xd15e,0xd160,0xd162,0xd163,0xd164,0xd165,
+ 0xd166,0xd167,0xd169,0xd16a,0xd16b,0xd16d,0xb540,0xb541,0xb543,0xb544,
+ 0xb545,0xb54b,0xb54c,0xb54d,0xb550,0xb554,0xb55c,0xb55d,0xb55f,0xb560,
+ 0xb561,0xb5a0,0xb5a1,0xb5a4,0xb5a8,0xb5aa,0xb5ab,0xb5b0,0xb5b1,0xb5b3,
+ 0xb5b4,0xb5b5,0xb5bb,0xb5bc,0xb5bd,0xb5c0,0xb5c4,0xb5cc,0xb5cd,0xb5cf,
+ 0xb5d0,0xb5d1,0xb5d8,0xb5ec,0xb610,0xb611,0xb614,0xb618,0xb625,0xb62c,
+ 0xb634,0xb648,0xb664,0xb668,0xb69c,0xb69d,0xb6a0,0xb6a4,0xb6ab,0xb6ac,
+ 0xb6b1,0xb6d4,0xb6f0,0xb6f4,0xb6f8,0xb700,0xb701,0xb705,0xb728,0xb729,
+ 0xb72c,0xb72f,0xb730,0xb738,0xb739,0xb73b,0xb744,0xb748,0xb74c,0xb754,
+ 0xb755,0xb760,0xb764,0xb768,0xb770,0xb771,0xb773,0xb775,0xb77c,0xb77d,
+ 0xb780,0xb784,0xb78c,0xb78d,0xb78f,0xb790,0xb791,0xb792,0xb796,0xb797
+ },
+ { /* ku 37 */
+ 0xd16e,0xd16f,0xd170,0xd171,0xd172,0xd173,0xd174,0xd175,0xd176,0xd177,
+ 0xd178,0xd179,0xd17a,0xd17b,0xd17d,0xd17e,0xd17f,0xd180,0xd181,0xd182,
+ 0xd183,0xd185,0xd186,0xd187,0xd189,0xd18a,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd18b,0xd18c,0xd18d,0xd18e,0xd18f,0xd190,0xd191,0xd192,
+ 0xd193,0xd194,0xd195,0xd196,0xd197,0xd198,0xd199,0xd19a,0xd19b,0xd19c,
+ 0xd19d,0xd19e,0xd19f,0xd1a2,0xd1a3,0xd1a5,0xd1a6,0xd1a7,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd1a9,0xd1aa,0xd1ab,0xd1ac,0xd1ad,0xd1ae,
+ 0xd1af,0xd1b2,0xd1b4,0xd1b6,0xd1b7,0xd1b8,0xd1b9,0xd1bb,0xd1bd,0xd1be,
+ 0xd1bf,0xd1c1,0xd1c2,0xd1c3,0xd1c4,0xd1c5,0xd1c6,0xd1c7,0xd1c8,0xd1c9,
+ 0xd1ca,0xd1cb,0xd1cc,0xd1cd,0xd1ce,0xd1cf,0xb798,0xb799,0xb79c,0xb7a0,
+ 0xb7a8,0xb7a9,0xb7ab,0xb7ac,0xb7ad,0xb7b4,0xb7b5,0xb7b8,0xb7c7,0xb7c9,
+ 0xb7ec,0xb7ed,0xb7f0,0xb7f4,0xb7fc,0xb7fd,0xb7ff,0xb800,0xb801,0xb807,
+ 0xb808,0xb809,0xb80c,0xb810,0xb818,0xb819,0xb81b,0xb81d,0xb824,0xb825,
+ 0xb828,0xb82c,0xb834,0xb835,0xb837,0xb838,0xb839,0xb840,0xb844,0xb851,
+ 0xb853,0xb85c,0xb85d,0xb860,0xb864,0xb86c,0xb86d,0xb86f,0xb871,0xb878,
+ 0xb87c,0xb88d,0xb8a8,0xb8b0,0xb8b4,0xb8b8,0xb8c0,0xb8c1,0xb8c3,0xb8c5,
+ 0xb8cc,0xb8d0,0xb8d4,0xb8dd,0xb8df,0xb8e1,0xb8e8,0xb8e9,0xb8ec,0xb8f0,
+ 0xb8f8,0xb8f9,0xb8fb,0xb8fd,0xb904,0xb918,0xb920,0xb93c,0xb93d,0xb940,
+ 0xb944,0xb94c,0xb94f,0xb951,0xb958,0xb959,0xb95c,0xb960,0xb968,0xb969
+ },
+ { /* ku 38 */
+ 0xd1d0,0xd1d1,0xd1d2,0xd1d3,0xd1d4,0xd1d5,0xd1d6,0xd1d7,0xd1d9,0xd1da,
+ 0xd1db,0xd1dc,0xd1dd,0xd1de,0xd1df,0xd1e0,0xd1e1,0xd1e2,0xd1e3,0xd1e4,
+ 0xd1e5,0xd1e6,0xd1e7,0xd1e8,0xd1e9,0xd1ea,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd1eb,0xd1ec,0xd1ed,0xd1ee,0xd1ef,0xd1f0,0xd1f1,0xd1f2,
+ 0xd1f3,0xd1f5,0xd1f6,0xd1f7,0xd1f9,0xd1fa,0xd1fb,0xd1fc,0xd1fd,0xd1fe,
+ 0xd1ff,0xd200,0xd201,0xd202,0xd203,0xd204,0xd205,0xd206,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd208,0xd20a,0xd20b,0xd20c,0xd20d,0xd20e,
+ 0xd20f,0xd211,0xd212,0xd213,0xd214,0xd215,0xd216,0xd217,0xd218,0xd219,
+ 0xd21a,0xd21b,0xd21c,0xd21d,0xd21e,0xd21f,0xd220,0xd221,0xd222,0xd223,
+ 0xd224,0xd225,0xd226,0xd227,0xd228,0xd229,0xb96b,0xb96d,0xb974,0xb975,
+ 0xb978,0xb97c,0xb984,0xb985,0xb987,0xb989,0xb98a,0xb98d,0xb98e,0xb9ac,
+ 0xb9ad,0xb9b0,0xb9b4,0xb9bc,0xb9bd,0xb9bf,0xb9c1,0xb9c8,0xb9c9,0xb9cc,
+ 0xb9ce,0xb9cf,0xb9d0,0xb9d1,0xb9d2,0xb9d8,0xb9d9,0xb9db,0xb9dd,0xb9de,
+ 0xb9e1,0xb9e3,0xb9e4,0xb9e5,0xb9e8,0xb9ec,0xb9f4,0xb9f5,0xb9f7,0xb9f8,
+ 0xb9f9,0xb9fa,0xba00,0xba01,0xba08,0xba15,0xba38,0xba39,0xba3c,0xba40,
+ 0xba42,0xba48,0xba49,0xba4b,0xba4d,0xba4e,0xba53,0xba54,0xba55,0xba58,
+ 0xba5c,0xba64,0xba65,0xba67,0xba68,0xba69,0xba70,0xba71,0xba74,0xba78,
+ 0xba83,0xba84,0xba85,0xba87,0xba8c,0xbaa8,0xbaa9,0xbaab,0xbaac,0xbab0,
+ 0xbab2,0xbab8,0xbab9,0xbabb,0xbabd,0xbac4,0xbac8,0xbad8,0xbad9,0xbafc
+ },
+ { /* ku 39 */
+ 0xd22a,0xd22b,0xd22e,0xd22f,0xd231,0xd232,0xd233,0xd235,0xd236,0xd237,
+ 0xd238,0xd239,0xd23a,0xd23b,0xd23e,0xd240,0xd242,0xd243,0xd244,0xd245,
+ 0xd246,0xd247,0xd249,0xd24a,0xd24b,0xd24c,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd24d,0xd24e,0xd24f,0xd250,0xd251,0xd252,0xd253,0xd254,
+ 0xd255,0xd256,0xd257,0xd258,0xd259,0xd25a,0xd25b,0xd25d,0xd25e,0xd25f,
+ 0xd260,0xd261,0xd262,0xd263,0xd265,0xd266,0xd267,0xd268,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd269,0xd26a,0xd26b,0xd26c,0xd26d,0xd26e,
+ 0xd26f,0xd270,0xd271,0xd272,0xd273,0xd274,0xd275,0xd276,0xd277,0xd278,
+ 0xd279,0xd27a,0xd27b,0xd27c,0xd27d,0xd27e,0xd27f,0xd282,0xd283,0xd285,
+ 0xd286,0xd287,0xd289,0xd28a,0xd28b,0xd28c,0xbb00,0xbb04,0xbb0d,0xbb0f,
+ 0xbb11,0xbb18,0xbb1c,0xbb20,0xbb29,0xbb2b,0xbb34,0xbb35,0xbb36,0xbb38,
+ 0xbb3b,0xbb3c,0xbb3d,0xbb3e,0xbb44,0xbb45,0xbb47,0xbb49,0xbb4d,0xbb4f,
+ 0xbb50,0xbb54,0xbb58,0xbb61,0xbb63,0xbb6c,0xbb88,0xbb8c,0xbb90,0xbba4,
+ 0xbba8,0xbbac,0xbbb4,0xbbb7,0xbbc0,0xbbc4,0xbbc8,0xbbd0,0xbbd3,0xbbf8,
+ 0xbbf9,0xbbfc,0xbbff,0xbc00,0xbc02,0xbc08,0xbc09,0xbc0b,0xbc0c,0xbc0d,
+ 0xbc0f,0xbc11,0xbc14,0xbc15,0xbc16,0xbc17,0xbc18,0xbc1b,0xbc1c,0xbc1d,
+ 0xbc1e,0xbc1f,0xbc24,0xbc25,0xbc27,0xbc29,0xbc2d,0xbc30,0xbc31,0xbc34,
+ 0xbc38,0xbc40,0xbc41,0xbc43,0xbc44,0xbc45,0xbc49,0xbc4c,0xbc4d,0xbc50,
+ 0xbc5d,0xbc84,0xbc85,0xbc88,0xbc8b,0xbc8c,0xbc8e,0xbc94,0xbc95,0xbc97
+ },
+ { /* ku 3a */
+ 0xd28d,0xd28e,0xd28f,0xd292,0xd293,0xd294,0xd296,0xd297,0xd298,0xd299,
+ 0xd29a,0xd29b,0xd29d,0xd29e,0xd29f,0xd2a1,0xd2a2,0xd2a3,0xd2a5,0xd2a6,
+ 0xd2a7,0xd2a8,0xd2a9,0xd2aa,0xd2ab,0xd2ad,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd2ae,0xd2af,0xd2b0,0xd2b2,0xd2b3,0xd2b4,0xd2b5,0xd2b6,
+ 0xd2b7,0xd2ba,0xd2bb,0xd2bd,0xd2be,0xd2c1,0xd2c3,0xd2c4,0xd2c5,0xd2c6,
+ 0xd2c7,0xd2ca,0xd2cc,0xd2cd,0xd2ce,0xd2cf,0xd2d0,0xd2d1,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd2d2,0xd2d3,0xd2d5,0xd2d6,0xd2d7,0xd2d9,
+ 0xd2da,0xd2db,0xd2dd,0xd2de,0xd2df,0xd2e0,0xd2e1,0xd2e2,0xd2e3,0xd2e6,
+ 0xd2e7,0xd2e8,0xd2e9,0xd2ea,0xd2eb,0xd2ec,0xd2ed,0xd2ee,0xd2ef,0xd2f2,
+ 0xd2f3,0xd2f5,0xd2f6,0xd2f7,0xd2f9,0xd2fa,0xbc99,0xbc9a,0xbca0,0xbca1,
+ 0xbca4,0xbca7,0xbca8,0xbcb0,0xbcb1,0xbcb3,0xbcb4,0xbcb5,0xbcbc,0xbcbd,
+ 0xbcc0,0xbcc4,0xbccd,0xbccf,0xbcd0,0xbcd1,0xbcd5,0xbcd8,0xbcdc,0xbcf4,
+ 0xbcf5,0xbcf6,0xbcf8,0xbcfc,0xbd04,0xbd05,0xbd07,0xbd09,0xbd10,0xbd14,
+ 0xbd24,0xbd2c,0xbd40,0xbd48,0xbd49,0xbd4c,0xbd50,0xbd58,0xbd59,0xbd64,
+ 0xbd68,0xbd80,0xbd81,0xbd84,0xbd87,0xbd88,0xbd89,0xbd8a,0xbd90,0xbd91,
+ 0xbd93,0xbd95,0xbd99,0xbd9a,0xbd9c,0xbda4,0xbdb0,0xbdb8,0xbdd4,0xbdd5,
+ 0xbdd8,0xbddc,0xbde9,0xbdf0,0xbdf4,0xbdf8,0xbe00,0xbe03,0xbe05,0xbe0c,
+ 0xbe0d,0xbe10,0xbe14,0xbe1c,0xbe1d,0xbe1f,0xbe44,0xbe45,0xbe48,0xbe4c,
+ 0xbe4e,0xbe54,0xbe55,0xbe57,0xbe59,0xbe5a,0xbe5b,0xbe60,0xbe61,0xbe64
+ },
+ { /* ku 3b */
+ 0xd2fb,0xd2fc,0xd2fd,0xd2fe,0xd2ff,0xd302,0xd304,0xd306,0xd307,0xd308,
+ 0xd309,0xd30a,0xd30b,0xd30f,0xd311,0xd312,0xd313,0xd315,0xd317,0xd318,
+ 0xd319,0xd31a,0xd31b,0xd31e,0xd322,0xd323,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd324,0xd326,0xd327,0xd32a,0xd32b,0xd32d,0xd32e,0xd32f,
+ 0xd331,0xd332,0xd333,0xd334,0xd335,0xd336,0xd337,0xd33a,0xd33e,0xd33f,
+ 0xd340,0xd341,0xd342,0xd343,0xd346,0xd347,0xd348,0xd349,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd34a,0xd34b,0xd34c,0xd34d,0xd34e,0xd34f,
+ 0xd350,0xd351,0xd352,0xd353,0xd354,0xd355,0xd356,0xd357,0xd358,0xd359,
+ 0xd35a,0xd35b,0xd35c,0xd35d,0xd35e,0xd35f,0xd360,0xd361,0xd362,0xd363,
+ 0xd364,0xd365,0xd366,0xd367,0xd368,0xd369,0xbe68,0xbe6a,0xbe70,0xbe71,
+ 0xbe73,0xbe74,0xbe75,0xbe7b,0xbe7c,0xbe7d,0xbe80,0xbe84,0xbe8c,0xbe8d,
+ 0xbe8f,0xbe90,0xbe91,0xbe98,0xbe99,0xbea8,0xbed0,0xbed1,0xbed4,0xbed7,
+ 0xbed8,0xbee0,0xbee3,0xbee4,0xbee5,0xbeec,0xbf01,0xbf08,0xbf09,0xbf18,
+ 0xbf19,0xbf1b,0xbf1c,0xbf1d,0xbf40,0xbf41,0xbf44,0xbf48,0xbf50,0xbf51,
+ 0xbf55,0xbf94,0xbfb0,0xbfc5,0xbfcc,0xbfcd,0xbfd0,0xbfd4,0xbfdc,0xbfdf,
+ 0xbfe1,0xc03c,0xc051,0xc058,0xc05c,0xc060,0xc068,0xc069,0xc090,0xc091,
+ 0xc094,0xc098,0xc0a0,0xc0a1,0xc0a3,0xc0a5,0xc0ac,0xc0ad,0xc0af,0xc0b0,
+ 0xc0b3,0xc0b4,0xc0b5,0xc0b6,0xc0bc,0xc0bd,0xc0bf,0xc0c0,0xc0c1,0xc0c5,
+ 0xc0c8,0xc0c9,0xc0cc,0xc0d0,0xc0d8,0xc0d9,0xc0db,0xc0dc,0xc0dd,0xc0e4
+ },
+ { /* ku 3c */
+ 0xd36a,0xd36b,0xd36c,0xd36d,0xd36e,0xd36f,0xd370,0xd371,0xd372,0xd373,
+ 0xd374,0xd375,0xd376,0xd377,0xd378,0xd379,0xd37a,0xd37b,0xd37e,0xd37f,
+ 0xd381,0xd382,0xd383,0xd385,0xd386,0xd387,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd388,0xd389,0xd38a,0xd38b,0xd38e,0xd392,0xd393,0xd394,
+ 0xd395,0xd396,0xd397,0xd39a,0xd39b,0xd39d,0xd39e,0xd39f,0xd3a1,0xd3a2,
+ 0xd3a3,0xd3a4,0xd3a5,0xd3a6,0xd3a7,0xd3aa,0xd3ac,0xd3ae,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd3af,0xd3b0,0xd3b1,0xd3b2,0xd3b3,0xd3b5,
+ 0xd3b6,0xd3b7,0xd3b9,0xd3ba,0xd3bb,0xd3bd,0xd3be,0xd3bf,0xd3c0,0xd3c1,
+ 0xd3c2,0xd3c3,0xd3c6,0xd3c7,0xd3ca,0xd3cb,0xd3cc,0xd3cd,0xd3ce,0xd3cf,
+ 0xd3d1,0xd3d2,0xd3d3,0xd3d4,0xd3d5,0xd3d6,0xc0e5,0xc0e8,0xc0ec,0xc0f4,
+ 0xc0f5,0xc0f7,0xc0f9,0xc100,0xc104,0xc108,0xc110,0xc115,0xc11c,0xc11d,
+ 0xc11e,0xc11f,0xc120,0xc123,0xc124,0xc126,0xc127,0xc12c,0xc12d,0xc12f,
+ 0xc130,0xc131,0xc136,0xc138,0xc139,0xc13c,0xc140,0xc148,0xc149,0xc14b,
+ 0xc14c,0xc14d,0xc154,0xc155,0xc158,0xc15c,0xc164,0xc165,0xc167,0xc168,
+ 0xc169,0xc170,0xc174,0xc178,0xc185,0xc18c,0xc18d,0xc18e,0xc190,0xc194,
+ 0xc196,0xc19c,0xc19d,0xc19f,0xc1a1,0xc1a5,0xc1a8,0xc1a9,0xc1ac,0xc1b0,
+ 0xc1bd,0xc1c4,0xc1c8,0xc1cc,0xc1d4,0xc1d7,0xc1d8,0xc1e0,0xc1e4,0xc1e8,
+ 0xc1f0,0xc1f1,0xc1f3,0xc1fc,0xc1fd,0xc200,0xc204,0xc20c,0xc20d,0xc20f,
+ 0xc211,0xc218,0xc219,0xc21c,0xc21f,0xc220,0xc228,0xc229,0xc22b,0xc22d
+ },
+ { /* ku 3d */
+ 0xd3d7,0xd3d9,0xd3da,0xd3db,0xd3dc,0xd3dd,0xd3de,0xd3df,0xd3e0,0xd3e2,
+ 0xd3e4,0xd3e5,0xd3e6,0xd3e7,0xd3e8,0xd3e9,0xd3ea,0xd3eb,0xd3ee,0xd3ef,
+ 0xd3f1,0xd3f2,0xd3f3,0xd3f5,0xd3f6,0xd3f7,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd3f8,0xd3f9,0xd3fa,0xd3fb,0xd3fe,0xd400,0xd402,0xd403,
+ 0xd404,0xd405,0xd406,0xd407,0xd409,0xd40a,0xd40b,0xd40c,0xd40d,0xd40e,
+ 0xd40f,0xd410,0xd411,0xd412,0xd413,0xd414,0xd415,0xd416,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd417,0xd418,0xd419,0xd41a,0xd41b,0xd41c,
+ 0xd41e,0xd41f,0xd420,0xd421,0xd422,0xd423,0xd424,0xd425,0xd426,0xd427,
+ 0xd428,0xd429,0xd42a,0xd42b,0xd42c,0xd42d,0xd42e,0xd42f,0xd430,0xd431,
+ 0xd432,0xd433,0xd434,0xd435,0xd436,0xd437,0xc22f,0xc231,0xc232,0xc234,
+ 0xc248,0xc250,0xc251,0xc254,0xc258,0xc260,0xc265,0xc26c,0xc26d,0xc270,
+ 0xc274,0xc27c,0xc27d,0xc27f,0xc281,0xc288,0xc289,0xc290,0xc298,0xc29b,
+ 0xc29d,0xc2a4,0xc2a5,0xc2a8,0xc2ac,0xc2ad,0xc2b4,0xc2b5,0xc2b7,0xc2b9,
+ 0xc2dc,0xc2dd,0xc2e0,0xc2e3,0xc2e4,0xc2eb,0xc2ec,0xc2ed,0xc2ef,0xc2f1,
+ 0xc2f6,0xc2f8,0xc2f9,0xc2fb,0xc2fc,0xc300,0xc308,0xc309,0xc30c,0xc30d,
+ 0xc313,0xc314,0xc315,0xc318,0xc31c,0xc324,0xc325,0xc328,0xc329,0xc345,
+ 0xc368,0xc369,0xc36c,0xc370,0xc372,0xc378,0xc379,0xc37c,0xc37d,0xc384,
+ 0xc388,0xc38c,0xc3c0,0xc3d8,0xc3d9,0xc3dc,0xc3df,0xc3e0,0xc3e2,0xc3e8,
+ 0xc3e9,0xc3ed,0xc3f4,0xc3f5,0xc3f8,0xc408,0xc410,0xc424,0xc42c,0xc430
+ },
+ { /* ku 3e */
+ 0xd438,0xd439,0xd43a,0xd43b,0xd43c,0xd43d,0xd43e,0xd43f,0xd441,0xd442,
+ 0xd443,0xd445,0xd446,0xd447,0xd448,0xd449,0xd44a,0xd44b,0xd44c,0xd44d,
+ 0xd44e,0xd44f,0xd450,0xd451,0xd452,0xd453,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd454,0xd455,0xd456,0xd457,0xd458,0xd459,0xd45a,0xd45b,
+ 0xd45d,0xd45e,0xd45f,0xd461,0xd462,0xd463,0xd465,0xd466,0xd467,0xd468,
+ 0xd469,0xd46a,0xd46b,0xd46c,0xd46e,0xd470,0xd471,0xd472,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd473,0xd474,0xd475,0xd476,0xd477,0xd47a,
+ 0xd47b,0xd47d,0xd47e,0xd481,0xd483,0xd484,0xd485,0xd486,0xd487,0xd48a,
+ 0xd48c,0xd48e,0xd48f,0xd490,0xd491,0xd492,0xd493,0xd495,0xd496,0xd497,
+ 0xd498,0xd499,0xd49a,0xd49b,0xd49c,0xd49d,0xc434,0xc43c,0xc43d,0xc448,
+ 0xc464,0xc465,0xc468,0xc46c,0xc474,0xc475,0xc479,0xc480,0xc494,0xc49c,
+ 0xc4b8,0xc4bc,0xc4e9,0xc4f0,0xc4f1,0xc4f4,0xc4f8,0xc4fa,0xc4ff,0xc500,
+ 0xc501,0xc50c,0xc510,0xc514,0xc51c,0xc528,0xc529,0xc52c,0xc530,0xc538,
+ 0xc539,0xc53b,0xc53d,0xc544,0xc545,0xc548,0xc549,0xc54a,0xc54c,0xc54d,
+ 0xc54e,0xc553,0xc554,0xc555,0xc557,0xc558,0xc559,0xc55d,0xc55e,0xc560,
+ 0xc561,0xc564,0xc568,0xc570,0xc571,0xc573,0xc574,0xc575,0xc57c,0xc57d,
+ 0xc580,0xc584,0xc587,0xc58c,0xc58d,0xc58f,0xc591,0xc595,0xc597,0xc598,
+ 0xc59c,0xc5a0,0xc5a9,0xc5b4,0xc5b5,0xc5b8,0xc5b9,0xc5bb,0xc5bc,0xc5bd,
+ 0xc5be,0xc5c4,0xc5c5,0xc5c6,0xc5c7,0xc5c8,0xc5c9,0xc5ca,0xc5cc,0xc5ce
+ },
+ { /* ku 3f */
+ 0xd49e,0xd49f,0xd4a0,0xd4a1,0xd4a2,0xd4a3,0xd4a4,0xd4a5,0xd4a6,0xd4a7,
+ 0xd4a8,0xd4aa,0xd4ab,0xd4ac,0xd4ad,0xd4ae,0xd4af,0xd4b0,0xd4b1,0xd4b2,
+ 0xd4b3,0xd4b4,0xd4b5,0xd4b6,0xd4b7,0xd4b8,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd4b9,0xd4ba,0xd4bb,0xd4bc,0xd4bd,0xd4be,0xd4bf,0xd4c0,
+ 0xd4c1,0xd4c2,0xd4c3,0xd4c4,0xd4c5,0xd4c6,0xd4c7,0xd4c8,0xd4c9,0xd4ca,
+ 0xd4cb,0xd4cd,0xd4ce,0xd4cf,0xd4d1,0xd4d2,0xd4d3,0xd4d5,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd4d6,0xd4d7,0xd4d8,0xd4d9,0xd4da,0xd4db,
+ 0xd4dd,0xd4de,0xd4e0,0xd4e1,0xd4e2,0xd4e3,0xd4e4,0xd4e5,0xd4e6,0xd4e7,
+ 0xd4e9,0xd4ea,0xd4eb,0xd4ed,0xd4ee,0xd4ef,0xd4f1,0xd4f2,0xd4f3,0xd4f4,
+ 0xd4f5,0xd4f6,0xd4f7,0xd4f9,0xd4fa,0xd4fc,0xc5d0,0xc5d1,0xc5d4,0xc5d8,
+ 0xc5e0,0xc5e1,0xc5e3,0xc5e5,0xc5ec,0xc5ed,0xc5ee,0xc5f0,0xc5f4,0xc5f6,
+ 0xc5f7,0xc5fc,0xc5fd,0xc5fe,0xc5ff,0xc600,0xc601,0xc605,0xc606,0xc607,
+ 0xc608,0xc60c,0xc610,0xc618,0xc619,0xc61b,0xc61c,0xc624,0xc625,0xc628,
+ 0xc62c,0xc62d,0xc62e,0xc630,0xc633,0xc634,0xc635,0xc637,0xc639,0xc63b,
+ 0xc640,0xc641,0xc644,0xc648,0xc650,0xc651,0xc653,0xc654,0xc655,0xc65c,
+ 0xc65d,0xc660,0xc66c,0xc66f,0xc671,0xc678,0xc679,0xc67c,0xc680,0xc688,
+ 0xc689,0xc68b,0xc68d,0xc694,0xc695,0xc698,0xc69c,0xc6a4,0xc6a5,0xc6a7,
+ 0xc6a9,0xc6b0,0xc6b1,0xc6b4,0xc6b8,0xc6b9,0xc6ba,0xc6c0,0xc6c1,0xc6c3,
+ 0xc6c5,0xc6cc,0xc6cd,0xc6d0,0xc6d4,0xc6dc,0xc6dd,0xc6e0,0xc6e1,0xc6e8
+ },
+ { /* ku 40 */
+ 0xd4fe,0xd4ff,0xd500,0xd501,0xd502,0xd503,0xd505,0xd506,0xd507,0xd509,
+ 0xd50a,0xd50b,0xd50d,0xd50e,0xd50f,0xd510,0xd511,0xd512,0xd513,0xd516,
+ 0xd518,0xd519,0xd51a,0xd51b,0xd51c,0xd51d,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd51e,0xd51f,0xd520,0xd521,0xd522,0xd523,0xd524,0xd525,
+ 0xd526,0xd527,0xd528,0xd529,0xd52a,0xd52b,0xd52c,0xd52d,0xd52e,0xd52f,
+ 0xd530,0xd531,0xd532,0xd533,0xd534,0xd535,0xd536,0xd537,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd538,0xd539,0xd53a,0xd53b,0xd53e,0xd53f,
+ 0xd541,0xd542,0xd543,0xd545,0xd546,0xd547,0xd548,0xd549,0xd54a,0xd54b,
+ 0xd54e,0xd550,0xd552,0xd553,0xd554,0xd555,0xd556,0xd557,0xd55a,0xd55b,
+ 0xd55d,0xd55e,0xd55f,0xd561,0xd562,0xd563,0xc6e9,0xc6ec,0xc6f0,0xc6f8,
+ 0xc6f9,0xc6fd,0xc704,0xc705,0xc708,0xc70c,0xc714,0xc715,0xc717,0xc719,
+ 0xc720,0xc721,0xc724,0xc728,0xc730,0xc731,0xc733,0xc735,0xc737,0xc73c,
+ 0xc73d,0xc740,0xc744,0xc74a,0xc74c,0xc74d,0xc74f,0xc751,0xc752,0xc753,
+ 0xc754,0xc755,0xc756,0xc757,0xc758,0xc75c,0xc760,0xc768,0xc76b,0xc774,
+ 0xc775,0xc778,0xc77c,0xc77d,0xc77e,0xc783,0xc784,0xc785,0xc787,0xc788,
+ 0xc789,0xc78a,0xc78e,0xc790,0xc791,0xc794,0xc796,0xc797,0xc798,0xc79a,
+ 0xc7a0,0xc7a1,0xc7a3,0xc7a4,0xc7a5,0xc7a6,0xc7ac,0xc7ad,0xc7b0,0xc7b4,
+ 0xc7bc,0xc7bd,0xc7bf,0xc7c0,0xc7c1,0xc7c8,0xc7c9,0xc7cc,0xc7ce,0xc7d0,
+ 0xc7d8,0xc7dd,0xc7e4,0xc7e8,0xc7ec,0xc800,0xc801,0xc804,0xc808,0xc80a
+ },
+ { /* ku 41 */
+ 0xd564,0xd566,0xd567,0xd56a,0xd56c,0xd56e,0xd56f,0xd570,0xd571,0xd572,
+ 0xd573,0xd576,0xd577,0xd579,0xd57a,0xd57b,0xd57d,0xd57e,0xd57f,0xd580,
+ 0xd581,0xd582,0xd583,0xd586,0xd58a,0xd58b,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd58c,0xd58d,0xd58e,0xd58f,0xd591,0xd592,0xd593,0xd594,
+ 0xd595,0xd596,0xd597,0xd598,0xd599,0xd59a,0xd59b,0xd59c,0xd59d,0xd59e,
+ 0xd59f,0xd5a0,0xd5a1,0xd5a2,0xd5a3,0xd5a4,0xd5a6,0xd5a7,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd5a8,0xd5a9,0xd5aa,0xd5ab,0xd5ac,0xd5ad,
+ 0xd5ae,0xd5af,0xd5b0,0xd5b1,0xd5b2,0xd5b3,0xd5b4,0xd5b5,0xd5b6,0xd5b7,
+ 0xd5b8,0xd5b9,0xd5ba,0xd5bb,0xd5bc,0xd5bd,0xd5be,0xd5bf,0xd5c0,0xd5c1,
+ 0xd5c2,0xd5c3,0xd5c4,0xd5c5,0xd5c6,0xd5c7,0xc810,0xc811,0xc813,0xc815,
+ 0xc816,0xc81c,0xc81d,0xc820,0xc824,0xc82c,0xc82d,0xc82f,0xc831,0xc838,
+ 0xc83c,0xc840,0xc848,0xc849,0xc84c,0xc84d,0xc854,0xc870,0xc871,0xc874,
+ 0xc878,0xc87a,0xc880,0xc881,0xc883,0xc885,0xc886,0xc887,0xc88b,0xc88c,
+ 0xc88d,0xc894,0xc89d,0xc89f,0xc8a1,0xc8a8,0xc8bc,0xc8bd,0xc8c4,0xc8c8,
+ 0xc8cc,0xc8d4,0xc8d5,0xc8d7,0xc8d9,0xc8e0,0xc8e1,0xc8e4,0xc8f5,0xc8fc,
+ 0xc8fd,0xc900,0xc904,0xc905,0xc906,0xc90c,0xc90d,0xc90f,0xc911,0xc918,
+ 0xc92c,0xc934,0xc950,0xc951,0xc954,0xc958,0xc960,0xc961,0xc963,0xc96c,
+ 0xc970,0xc974,0xc97c,0xc988,0xc989,0xc98c,0xc990,0xc998,0xc999,0xc99b,
+ 0xc99d,0xc9c0,0xc9c1,0xc9c4,0xc9c7,0xc9c8,0xc9ca,0xc9d0,0xc9d1,0xc9d3
+ },
+ { /* ku 42 */
+ 0xd5ca,0xd5cb,0xd5cd,0xd5ce,0xd5cf,0xd5d1,0xd5d3,0xd5d4,0xd5d5,0xd5d6,
+ 0xd5d7,0xd5da,0xd5dc,0xd5de,0xd5df,0xd5e0,0xd5e1,0xd5e2,0xd5e3,0xd5e6,
+ 0xd5e7,0xd5e9,0xd5ea,0xd5eb,0xd5ed,0xd5ee,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd5ef,0xd5f0,0xd5f1,0xd5f2,0xd5f3,0xd5f6,0xd5f8,0xd5fa,
+ 0xd5fb,0xd5fc,0xd5fd,0xd5fe,0xd5ff,0xd602,0xd603,0xd605,0xd606,0xd607,
+ 0xd609,0xd60a,0xd60b,0xd60c,0xd60d,0xd60e,0xd60f,0xd612,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd616,0xd617,0xd618,0xd619,0xd61a,0xd61b,
+ 0xd61d,0xd61e,0xd61f,0xd621,0xd622,0xd623,0xd625,0xd626,0xd627,0xd628,
+ 0xd629,0xd62a,0xd62b,0xd62c,0xd62e,0xd62f,0xd630,0xd631,0xd632,0xd633,
+ 0xd634,0xd635,0xd636,0xd637,0xd63a,0xd63b,0xc9d5,0xc9d6,0xc9d9,0xc9da,
+ 0xc9dc,0xc9dd,0xc9e0,0xc9e2,0xc9e4,0xc9e7,0xc9ec,0xc9ed,0xc9ef,0xc9f0,
+ 0xc9f1,0xc9f8,0xc9f9,0xc9fc,0xca00,0xca08,0xca09,0xca0b,0xca0c,0xca0d,
+ 0xca14,0xca18,0xca29,0xca4c,0xca4d,0xca50,0xca54,0xca5c,0xca5d,0xca5f,
+ 0xca60,0xca61,0xca68,0xca7d,0xca84,0xca98,0xcabc,0xcabd,0xcac0,0xcac4,
+ 0xcacc,0xcacd,0xcacf,0xcad1,0xcad3,0xcad8,0xcad9,0xcae0,0xcaec,0xcaf4,
+ 0xcb08,0xcb10,0xcb14,0xcb18,0xcb20,0xcb21,0xcb41,0xcb48,0xcb49,0xcb4c,
+ 0xcb50,0xcb58,0xcb59,0xcb5d,0xcb64,0xcb78,0xcb79,0xcb9c,0xcbb8,0xcbd4,
+ 0xcbe4,0xcbe7,0xcbe9,0xcc0c,0xcc0d,0xcc10,0xcc14,0xcc1c,0xcc1d,0xcc21,
+ 0xcc22,0xcc27,0xcc28,0xcc29,0xcc2c,0xcc2e,0xcc30,0xcc38,0xcc39,0xcc3b
+ },
+ { /* ku 43 */
+ 0xd63d,0xd63e,0xd63f,0xd641,0xd642,0xd643,0xd644,0xd646,0xd647,0xd64a,
+ 0xd64c,0xd64e,0xd64f,0xd650,0xd652,0xd653,0xd656,0xd657,0xd659,0xd65a,
+ 0xd65b,0xd65d,0xd65e,0xd65f,0xd660,0xd661,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd662,0xd663,0xd664,0xd665,0xd666,0xd668,0xd66a,0xd66b,
+ 0xd66c,0xd66d,0xd66e,0xd66f,0xd672,0xd673,0xd675,0xd676,0xd677,0xd678,
+ 0xd679,0xd67a,0xd67b,0xd67c,0xd67d,0xd67e,0xd67f,0xd680,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd681,0xd682,0xd684,0xd686,0xd687,0xd688,
+ 0xd689,0xd68a,0xd68b,0xd68e,0xd68f,0xd691,0xd692,0xd693,0xd695,0xd696,
+ 0xd697,0xd698,0xd699,0xd69a,0xd69b,0xd69c,0xd69e,0xd6a0,0xd6a2,0xd6a3,
+ 0xd6a4,0xd6a5,0xd6a6,0xd6a7,0xd6a9,0xd6aa,0xcc3c,0xcc3d,0xcc3e,0xcc44,
+ 0xcc45,0xcc48,0xcc4c,0xcc54,0xcc55,0xcc57,0xcc58,0xcc59,0xcc60,0xcc64,
+ 0xcc66,0xcc68,0xcc70,0xcc75,0xcc98,0xcc99,0xcc9c,0xcca0,0xcca8,0xcca9,
+ 0xccab,0xccac,0xccad,0xccb4,0xccb5,0xccb8,0xccbc,0xccc4,0xccc5,0xccc7,
+ 0xccc9,0xccd0,0xccd4,0xcce4,0xccec,0xccf0,0xcd01,0xcd08,0xcd09,0xcd0c,
+ 0xcd10,0xcd18,0xcd19,0xcd1b,0xcd1d,0xcd24,0xcd28,0xcd2c,0xcd39,0xcd5c,
+ 0xcd60,0xcd64,0xcd6c,0xcd6d,0xcd6f,0xcd71,0xcd78,0xcd88,0xcd94,0xcd95,
+ 0xcd98,0xcd9c,0xcda4,0xcda5,0xcda7,0xcda9,0xcdb0,0xcdc4,0xcdcc,0xcdd0,
+ 0xcde8,0xcdec,0xcdf0,0xcdf8,0xcdf9,0xcdfb,0xcdfd,0xce04,0xce08,0xce0c,
+ 0xce14,0xce19,0xce20,0xce21,0xce24,0xce28,0xce30,0xce31,0xce33,0xce35
+ },
+ { /* ku 44 */
+ 0xd6ab,0xd6ad,0xd6ae,0xd6af,0xd6b1,0xd6b2,0xd6b3,0xd6b4,0xd6b5,0xd6b6,
+ 0xd6b7,0xd6b8,0xd6ba,0xd6bc,0xd6bd,0xd6be,0xd6bf,0xd6c0,0xd6c1,0xd6c2,
+ 0xd6c3,0xd6c6,0xd6c7,0xd6c9,0xd6ca,0xd6cb,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd6cd,0xd6ce,0xd6cf,0xd6d0,0xd6d2,0xd6d3,0xd6d5,0xd6d6,
+ 0xd6d8,0xd6da,0xd6db,0xd6dc,0xd6dd,0xd6de,0xd6df,0xd6e1,0xd6e2,0xd6e3,
+ 0xd6e5,0xd6e6,0xd6e7,0xd6e9,0xd6ea,0xd6eb,0xd6ec,0xd6ed,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd6ee,0xd6ef,0xd6f1,0xd6f2,0xd6f3,0xd6f4,
+ 0xd6f6,0xd6f7,0xd6f8,0xd6f9,0xd6fa,0xd6fb,0xd6fe,0xd6ff,0xd701,0xd702,
+ 0xd703,0xd705,0xd706,0xd707,0xd708,0xd709,0xd70a,0xd70b,0xd70c,0xd70d,
+ 0xd70e,0xd70f,0xd710,0xd712,0xd713,0xd714,0xce58,0xce59,0xce5c,0xce5f,
+ 0xce60,0xce61,0xce68,0xce69,0xce6b,0xce6d,0xce74,0xce75,0xce78,0xce7c,
+ 0xce84,0xce85,0xce87,0xce89,0xce90,0xce91,0xce94,0xce98,0xcea0,0xcea1,
+ 0xcea3,0xcea4,0xcea5,0xceac,0xcead,0xcec1,0xcee4,0xcee5,0xcee8,0xceeb,
+ 0xceec,0xcef4,0xcef5,0xcef7,0xcef8,0xcef9,0xcf00,0xcf01,0xcf04,0xcf08,
+ 0xcf10,0xcf11,0xcf13,0xcf15,0xcf1c,0xcf20,0xcf24,0xcf2c,0xcf2d,0xcf2f,
+ 0xcf30,0xcf31,0xcf38,0xcf54,0xcf55,0xcf58,0xcf5c,0xcf64,0xcf65,0xcf67,
+ 0xcf69,0xcf70,0xcf71,0xcf74,0xcf78,0xcf80,0xcf85,0xcf8c,0xcfa1,0xcfa8,
+ 0xcfb0,0xcfc4,0xcfe0,0xcfe1,0xcfe4,0xcfe8,0xcff0,0xcff1,0xcff3,0xcff5,
+ 0xcffc,0xd000,0xd004,0xd011,0xd018,0xd02d,0xd034,0xd035,0xd038,0xd03c
+ },
+ { /* ku 45 */
+ 0xd715,0xd716,0xd717,0xd71a,0xd71b,0xd71d,0xd71e,0xd71f,0xd721,0xd722,
+ 0xd723,0xd724,0xd725,0xd726,0xd727,0xd72a,0xd72c,0xd72e,0xd72f,0xd730,
+ 0xd731,0xd732,0xd733,0xd736,0xd737,0xd739,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,0xd73a,0xd73b,0xd73d,0xd73e,0xd73f,0xd740,0xd741,0xd742,
+ 0xd743,0xd745,0xd746,0xd748,0xd74a,0xd74b,0xd74c,0xd74d,0xd74e,0xd74f,
+ 0xd752,0xd753,0xd755,0xd75a,0xd75b,0xd75c,0xd75d,0xd75e,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,0xd75f,0xd762,0xd764,0xd766,0xd767,0xd768,
+ 0xd76a,0xd76b,0xd76d,0xd76e,0xd76f,0xd771,0xd772,0xd773,0xd775,0xd776,
+ 0xd777,0xd778,0xd779,0xd77a,0xd77b,0xd77e,0xd77f,0xd780,0xd782,0xd783,
+ 0xd784,0xd785,0xd786,0xd787,0xd78a,0xd78b,0xd044,0xd045,0xd047,0xd049,
+ 0xd050,0xd054,0xd058,0xd060,0xd06c,0xd06d,0xd070,0xd074,0xd07c,0xd07d,
+ 0xd081,0xd0a4,0xd0a5,0xd0a8,0xd0ac,0xd0b4,0xd0b5,0xd0b7,0xd0b9,0xd0c0,
+ 0xd0c1,0xd0c4,0xd0c8,0xd0c9,0xd0d0,0xd0d1,0xd0d3,0xd0d4,0xd0d5,0xd0dc,
+ 0xd0dd,0xd0e0,0xd0e4,0xd0ec,0xd0ed,0xd0ef,0xd0f0,0xd0f1,0xd0f8,0xd10d,
+ 0xd130,0xd131,0xd134,0xd138,0xd13a,0xd140,0xd141,0xd143,0xd144,0xd145,
+ 0xd14c,0xd14d,0xd150,0xd154,0xd15c,0xd15d,0xd15f,0xd161,0xd168,0xd16c,
+ 0xd17c,0xd184,0xd188,0xd1a0,0xd1a1,0xd1a4,0xd1a8,0xd1b0,0xd1b1,0xd1b3,
+ 0xd1b5,0xd1ba,0xd1bc,0xd1c0,0xd1d8,0xd1f4,0xd1f8,0xd207,0xd209,0xd210,
+ 0xd22c,0xd22d,0xd230,0xd234,0xd23c,0xd23d,0xd23f,0xd241,0xd248,0xd25c
+ },
+ { /* ku 46 */
+ 0xd78d,0xd78e,0xd78f,0xd791,0xd792,0xd793,0xd794,0xd795,0xd796,0xd797,
+ 0xd79a,0xd79c,0xd79e,0xd79f,0xd7a0,0xd7a1,0xd7a2,0xd7a3,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0xd264,0xd280,0xd281,0xd284,
+ 0xd288,0xd290,0xd291,0xd295,0xd29c,0xd2a0,0xd2a4,0xd2ac,0xd2b1,0xd2b8,
+ 0xd2b9,0xd2bc,0xd2bf,0xd2c0,0xd2c2,0xd2c8,0xd2c9,0xd2cb,0xd2d4,0xd2d8,
+ 0xd2dc,0xd2e4,0xd2e5,0xd2f0,0xd2f1,0xd2f4,0xd2f8,0xd300,0xd301,0xd303,
+ 0xd305,0xd30c,0xd30d,0xd30e,0xd310,0xd314,0xd316,0xd31c,0xd31d,0xd31f,
+ 0xd320,0xd321,0xd325,0xd328,0xd329,0xd32c,0xd330,0xd338,0xd339,0xd33b,
+ 0xd33c,0xd33d,0xd344,0xd345,0xd37c,0xd37d,0xd380,0xd384,0xd38c,0xd38d,
+ 0xd38f,0xd390,0xd391,0xd398,0xd399,0xd39c,0xd3a0,0xd3a8,0xd3a9,0xd3ab,
+ 0xd3ad,0xd3b4,0xd3b8,0xd3bc,0xd3c4,0xd3c5,0xd3c8,0xd3c9,0xd3d0,0xd3d8,
+ 0xd3e1,0xd3e3,0xd3ec,0xd3ed,0xd3f0,0xd3f4,0xd3fc,0xd3fd,0xd3ff,0xd401
+ },
+ { /* ku 47 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0xd408,0xd41d,0xd440,0xd444,
+ 0xd45c,0xd460,0xd464,0xd46d,0xd46f,0xd478,0xd479,0xd47c,0xd47f,0xd480,
+ 0xd482,0xd488,0xd489,0xd48b,0xd48d,0xd494,0xd4a9,0xd4cc,0xd4d0,0xd4d4,
+ 0xd4dc,0xd4df,0xd4e8,0xd4ec,0xd4f0,0xd4f8,0xd4fb,0xd4fd,0xd504,0xd508,
+ 0xd50c,0xd514,0xd515,0xd517,0xd53c,0xd53d,0xd540,0xd544,0xd54c,0xd54d,
+ 0xd54f,0xd551,0xd558,0xd559,0xd55c,0xd560,0xd565,0xd568,0xd569,0xd56b,
+ 0xd56d,0xd574,0xd575,0xd578,0xd57c,0xd584,0xd585,0xd587,0xd588,0xd589,
+ 0xd590,0xd5a5,0xd5c8,0xd5c9,0xd5cc,0xd5d0,0xd5d2,0xd5d8,0xd5d9,0xd5db,
+ 0xd5dd,0xd5e4,0xd5e5,0xd5e8,0xd5ec,0xd5f4,0xd5f5,0xd5f7,0xd5f9,0xd600,
+ 0xd601,0xd604,0xd608,0xd610,0xd611,0xd613,0xd614,0xd615,0xd61c,0xd620
+ },
+ { /* ku 48 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0xd624,0xd62d,0xd638,0xd639,
+ 0xd63c,0xd640,0xd645,0xd648,0xd649,0xd64b,0xd64d,0xd651,0xd654,0xd655,
+ 0xd658,0xd65c,0xd667,0xd669,0xd670,0xd671,0xd674,0xd683,0xd685,0xd68c,
+ 0xd68d,0xd690,0xd694,0xd69d,0xd69f,0xd6a1,0xd6a8,0xd6ac,0xd6b0,0xd6b9,
+ 0xd6bb,0xd6c4,0xd6c5,0xd6c8,0xd6cc,0xd6d1,0xd6d4,0xd6d7,0xd6d9,0xd6e0,
+ 0xd6e4,0xd6e8,0xd6f0,0xd6f5,0xd6fc,0xd6fd,0xd700,0xd704,0xd711,0xd718,
+ 0xd719,0xd71c,0xd720,0xd728,0xd729,0xd72b,0xd72d,0xd734,0xd735,0xd738,
+ 0xd73c,0xd744,0xd747,0xd749,0xd750,0xd751,0xd754,0xd756,0xd757,0xd758,
+ 0xd759,0xd760,0xd761,0xd763,0xd765,0xd769,0xd76c,0xd770,0xd774,0xd77c,
+ 0xd77d,0xd781,0xd788,0xd789,0xd78c,0xd790,0xd798,0xd799,0xd79b,0xd79d
+ },
+ { /* ku 49 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON
+ },
+ { /* ku 4a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4f3d,0x4f73,0x5047,0x50f9,
+ 0x52a0,0x53ef,0x5475,0x54e5,0x5609,0x5ac1,0x5bb6,0x6687,0x67b6,0x67b7,
+ 0x67ef,0x6b4c,0x73c2,0x75c2,0x7a3c,0x82db,0x8304,0x8857,0x8888,0x8a36,
+ 0x8cc8,0x8dcf,0x8efb,0x8fe6,0x99d5,0x523b,0x5374,0x5404,0x606a,0x6164,
+ 0x6bbc,0x73cf,0x811a,0x89ba,0x89d2,0x95a3,0x4f83,0x520a,0x58be,0x5978,
+ 0x59e6,0x5e72,0x5e79,0x61c7,0x63c0,0x6746,0x67ec,0x687f,0x6f97,0x764e,
+ 0x770b,0x78f5,0x7a08,0x7aff,0x7c21,0x809d,0x826e,0x8271,0x8aeb,0x9593,
+ 0x4e6b,0x559d,0x66f7,0x6e34,0x78a3,0x7aed,0x845b,0x8910,0x874e,0x97a8,
+ 0x52d8,0x574e,0x582a,0x5d4c,0x611f,0x61be,0x6221,0x6562,0x67d1,0x6a44,
+ 0x6e1b,0x7518,0x75b3,0x76e3,0x77b0,0x7d3a,0x90af,0x9451,0x9452,0x9f95
+ },
+ { /* ku 4b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5323,0x5cac,0x7532,0x80db,
+ 0x9240,0x9598,0x525b,0x5808,0x59dc,0x5ca1,0x5d17,0x5eb7,0x5f3a,0x5f4a,
+ 0x6177,0x6c5f,0x757a,0x7586,0x7ce0,0x7d73,0x7db1,0x7f8c,0x8154,0x8221,
+ 0x8591,0x8941,0x8b1b,0x92fc,0x964d,0x9c47,0x4ecb,0x4ef7,0x500b,0x51f1,
+ 0x584f,0x6137,0x613e,0x6168,0x6539,0x69ea,0x6f11,0x75a5,0x7686,0x76d6,
+ 0x7b87,0x82a5,0x84cb,0xf900,0x93a7,0x958b,0x5580,0x5ba2,0x5751,0xf901,
+ 0x7cb3,0x7fb9,0x91b5,0x5028,0x53bb,0x5c45,0x5de8,0x62d2,0x636e,0x64da,
+ 0x64e7,0x6e20,0x70ac,0x795b,0x8ddd,0x8e1e,0xf902,0x907d,0x9245,0x92f8,
+ 0x4e7e,0x4ef6,0x5065,0x5dfe,0x5efa,0x6106,0x6957,0x8171,0x8654,0x8e47,
+ 0x9375,0x9a2b,0x4e5e,0x5091,0x6770,0x6840,0x5109,0x528d,0x5292,0x6aa2
+ },
+ { /* ku 4c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x77bc,0x9210,0x9ed4,0x52ab,
+ 0x602f,0x8ff2,0x5048,0x61a9,0x63ed,0x64ca,0x683c,0x6a84,0x6fc0,0x8188,
+ 0x89a1,0x9694,0x5805,0x727d,0x72ac,0x7504,0x7d79,0x7e6d,0x80a9,0x898b,
+ 0x8b74,0x9063,0x9d51,0x6289,0x6c7a,0x6f54,0x7d50,0x7f3a,0x8a23,0x517c,
+ 0x614a,0x7b9d,0x8b19,0x9257,0x938c,0x4eac,0x4fd3,0x501e,0x50be,0x5106,
+ 0x52c1,0x52cd,0x537f,0x5770,0x5883,0x5e9a,0x5f91,0x6176,0x61ac,0x64ce,
+ 0x656c,0x666f,0x66bb,0x66f4,0x6897,0x6d87,0x7085,0x70f1,0x749f,0x74a5,
+ 0x74ca,0x75d9,0x786c,0x78ec,0x7adf,0x7af6,0x7d45,0x7d93,0x8015,0x803f,
+ 0x811b,0x8396,0x8b66,0x8f15,0x9015,0x93e1,0x9803,0x9838,0x9a5a,0x9be8,
+ 0x4fc2,0x5553,0x583a,0x5951,0x5b63,0x5c46,0x60b8,0x6212,0x6842,0x68b0
+ },
+ { /* ku 4d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x68e8,0x6eaa,0x754c,0x7678,
+ 0x78ce,0x7a3d,0x7cfb,0x7e6b,0x7e7c,0x8a08,0x8aa1,0x8c3f,0x968e,0x9dc4,
+ 0x53e4,0x53e9,0x544a,0x5471,0x56fa,0x59d1,0x5b64,0x5c3b,0x5eab,0x62f7,
+ 0x6537,0x6545,0x6572,0x66a0,0x67af,0x69c1,0x6cbd,0x75fc,0x7690,0x777e,
+ 0x7a3f,0x7f94,0x8003,0x80a1,0x818f,0x82e6,0x82fd,0x83f0,0x85c1,0x8831,
+ 0x88b4,0x8aa5,0xf903,0x8f9c,0x932e,0x96c7,0x9867,0x9ad8,0x9f13,0x54ed,
+ 0x659b,0x66f2,0x688f,0x7a40,0x8c37,0x9d60,0x56f0,0x5764,0x5d11,0x6606,
+ 0x68b1,0x68cd,0x6efe,0x7428,0x889e,0x9be4,0x6c68,0xf904,0x9aa8,0x4f9b,
+ 0x516c,0x5171,0x529f,0x5b54,0x5de5,0x6050,0x606d,0x62f1,0x63a7,0x653b,
+ 0x73d9,0x7a7a,0x86a3,0x8ca2,0x978f,0x4e32,0x5be1,0x6208,0x679c,0x74dc
+ },
+ { /* ku 4e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x79d1,0x83d3,0x8a87,0x8ab2,
+ 0x8de8,0x904e,0x934b,0x9846,0x5ed3,0x69e8,0x85ff,0x90ed,0xf905,0x51a0,
+ 0x5b98,0x5bec,0x6163,0x68fa,0x6b3e,0x704c,0x742f,0x74d8,0x7ba1,0x7f50,
+ 0x83c5,0x89c0,0x8cab,0x95dc,0x9928,0x522e,0x605d,0x62ec,0x9002,0x4f8a,
+ 0x5149,0x5321,0x58d9,0x5ee3,0x66e0,0x6d38,0x709a,0x72c2,0x73d6,0x7b50,
+ 0x80f1,0x945b,0x5366,0x639b,0x7f6b,0x4e56,0x5080,0x584a,0x58de,0x602a,
+ 0x6127,0x62d0,0x69d0,0x9b41,0x5b8f,0x7d18,0x80b1,0x8f5f,0x4ea4,0x50d1,
+ 0x54ac,0x55ac,0x5b0c,0x5da0,0x5de7,0x652a,0x654e,0x6821,0x6a4b,0x72e1,
+ 0x768e,0x77ef,0x7d5e,0x7ff9,0x81a0,0x854e,0x86df,0x8f03,0x8f4e,0x90ca,
+ 0x9903,0x9a55,0x9bab,0x4e18,0x4e45,0x4e5d,0x4ec7,0x4ff1,0x5177,0x52fe
+ },
+ { /* ku 4f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5340,0x53e3,0x53e5,0x548e,
+ 0x5614,0x5775,0x57a2,0x5bc7,0x5d87,0x5ed0,0x61fc,0x62d8,0x6551,0x67b8,
+ 0x67e9,0x69cb,0x6b50,0x6bc6,0x6bec,0x6c42,0x6e9d,0x7078,0x72d7,0x7396,
+ 0x7403,0x77bf,0x77e9,0x7a76,0x7d7f,0x8009,0x81fc,0x8205,0x820a,0x82df,
+ 0x8862,0x8b33,0x8cfc,0x8ec0,0x9011,0x90b1,0x9264,0x92b6,0x99d2,0x9a45,
+ 0x9ce9,0x9dd7,0x9f9c,0x570b,0x5c40,0x83ca,0x97a0,0x97ab,0x9eb4,0x541b,
+ 0x7a98,0x7fa4,0x88d9,0x8ecd,0x90e1,0x5800,0x5c48,0x6398,0x7a9f,0x5bae,
+ 0x5f13,0x7a79,0x7aae,0x828e,0x8eac,0x5026,0x5238,0x52f8,0x5377,0x5708,
+ 0x62f3,0x6372,0x6b0a,0x6dc3,0x7737,0x53a5,0x7357,0x8568,0x8e76,0x95d5,
+ 0x673a,0x6ac3,0x6f70,0x8a6d,0x8ecc,0x994b,0xf906,0x6677,0x6b78,0x8cb4
+ },
+ { /* ku 50 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x9b3c,0xf907,0x53eb,0x572d,
+ 0x594e,0x63c6,0x69fb,0x73ea,0x7845,0x7aba,0x7ac5,0x7cfe,0x8475,0x898f,
+ 0x8d73,0x9035,0x95a8,0x52fb,0x5747,0x7547,0x7b60,0x83cc,0x921e,0xf908,
+ 0x6a58,0x514b,0x524b,0x5287,0x621f,0x68d8,0x6975,0x9699,0x50c5,0x52a4,
+ 0x52e4,0x61c3,0x65a4,0x6839,0x69ff,0x747e,0x7b4b,0x82b9,0x83eb,0x89b2,
+ 0x8b39,0x8fd1,0x9949,0xf909,0x4eca,0x5997,0x64d2,0x6611,0x6a8e,0x7434,
+ 0x7981,0x79bd,0x82a9,0x887e,0x887f,0x895f,0xf90a,0x9326,0x4f0b,0x53ca,
+ 0x6025,0x6271,0x6c72,0x7d1a,0x7d66,0x4e98,0x5162,0x77dc,0x80af,0x4f01,
+ 0x4f0e,0x5176,0x5180,0x55dc,0x5668,0x573b,0x57fa,0x57fc,0x5914,0x5947,
+ 0x5993,0x5bc4,0x5c90,0x5d0e,0x5df1,0x5e7e,0x5fcc,0x6280,0x65d7,0x65e3
+ },
+ { /* ku 51 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x671e,0x671f,0x675e,0x68cb,
+ 0x68c4,0x6a5f,0x6b3a,0x6c23,0x6c7d,0x6c82,0x6dc7,0x7398,0x7426,0x742a,
+ 0x7482,0x74a3,0x7578,0x757f,0x7881,0x78ef,0x7941,0x7947,0x7948,0x797a,
+ 0x7b95,0x7d00,0x7dba,0x7f88,0x8006,0x802d,0x808c,0x8a18,0x8b4f,0x8c48,
+ 0x8d77,0x9321,0x9324,0x98e2,0x9951,0x9a0e,0x9a0f,0x9a65,0x9e92,0x7dca,
+ 0x4f76,0x5409,0x62ee,0x6854,0x91d1,0x55ab,0x513a,0xf90b,0xf90c,0x5a1c,
+ 0x61e6,0xf90d,0x62cf,0x62ff,0xf90e,0xf90f,0xf910,0xf911,0xf912,0xf913,
+ 0x90a3,0xf914,0xf915,0xf916,0xf917,0xf918,0x8afe,0xf919,0xf91a,0xf91b,
+ 0xf91c,0x6696,0xf91d,0x7156,0xf91e,0xf91f,0x96e3,0xf920,0x634f,0x637a,
+ 0x5357,0xf921,0x678f,0x6960,0x6e73,0xf922,0x7537,0xf923,0xf924,0xf925
+ },
+ { /* ku 52 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7d0d,0xf926,0xf927,0x8872,
+ 0x56ca,0x5a18,0xf928,0xf929,0xf92a,0xf92b,0xf92c,0x4e43,0xf92d,0x5167,
+ 0x5948,0x67f0,0x8010,0xf92e,0x5973,0x5e74,0x649a,0x79ca,0x5ff5,0x606c,
+ 0x62c8,0x637b,0x5be7,0x5bd7,0x52aa,0xf92f,0x5974,0x5f29,0x6012,0xf930,
+ 0xf931,0xf932,0x7459,0xf933,0xf934,0xf935,0xf936,0xf937,0xf938,0x99d1,
+ 0xf939,0xf93a,0xf93b,0xf93c,0xf93d,0xf93e,0xf93f,0xf940,0xf941,0xf942,
+ 0xf943,0x6fc3,0xf944,0xf945,0x81bf,0x8fb2,0x60f1,0xf946,0xf947,0x8166,
+ 0xf948,0xf949,0x5c3f,0xf94a,0xf94b,0xf94c,0xf94d,0xf94e,0xf94f,0xf950,
+ 0xf951,0x5ae9,0x8a25,0x677b,0x7d10,0xf952,0xf953,0xf954,0xf955,0xf956,
+ 0xf957,0x80fd,0xf958,0xf959,0x5c3c,0x6ce5,0x533f,0x6eba,0x591a,0x8336
+ },
+ { /* ku 53 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x4e39,0x4eb6,0x4f46,0x55ae,
+ 0x5718,0x58c7,0x5f56,0x65b7,0x65e6,0x6a80,0x6bb5,0x6e4d,0x77ed,0x7aef,
+ 0x7c1e,0x7dde,0x86cb,0x8892,0x9132,0x935b,0x64bb,0x6fbe,0x737a,0x75b8,
+ 0x9054,0x5556,0x574d,0x61ba,0x64d4,0x66c7,0x6de1,0x6e5b,0x6f6d,0x6fb9,
+ 0x75f0,0x8043,0x81bd,0x8541,0x8983,0x8ac7,0x8b5a,0x931f,0x6c93,0x7553,
+ 0x7b54,0x8e0f,0x905d,0x5510,0x5802,0x5858,0x5e62,0x6207,0x649e,0x68e0,
+ 0x7576,0x7cd6,0x87b3,0x9ee8,0x4ee3,0x5788,0x576e,0x5927,0x5c0d,0x5cb1,
+ 0x5e36,0x5f85,0x6234,0x64e1,0x73b3,0x81fa,0x888b,0x8cb8,0x968a,0x9edb,
+ 0x5b85,0x5fb7,0x60b3,0x5012,0x5200,0x5230,0x5716,0x5835,0x5857,0x5c0e,
+ 0x5c60,0x5cf6,0x5d8b,0x5ea6,0x5f92,0x60bc,0x6311,0x6389,0x6417,0x6843
+ },
+ { /* ku 54 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x68f9,0x6ac2,0x6dd8,0x6e21,
+ 0x6ed4,0x6fe4,0x71fe,0x76dc,0x7779,0x79b1,0x7a3b,0x8404,0x89a9,0x8ced,
+ 0x8df3,0x8e48,0x9003,0x9014,0x9053,0x90fd,0x934d,0x9676,0x97dc,0x6bd2,
+ 0x7006,0x7258,0x72a2,0x7368,0x7763,0x79bf,0x7be4,0x7e9b,0x8b80,0x58a9,
+ 0x60c7,0x6566,0x65fd,0x66be,0x6c8c,0x711e,0x71c9,0x8c5a,0x9813,0x4e6d,
+ 0x7a81,0x4edd,0x51ac,0x51cd,0x52d5,0x540c,0x61a7,0x6771,0x6850,0x68df,
+ 0x6d1e,0x6f7c,0x75bc,0x77b3,0x7ae5,0x80f4,0x8463,0x9285,0x515c,0x6597,
+ 0x675c,0x6793,0x75d8,0x7ac7,0x8373,0xf95a,0x8c46,0x9017,0x982d,0x5c6f,
+ 0x81c0,0x829a,0x9041,0x906f,0x920d,0x5f97,0x5d9d,0x6a59,0x71c8,0x767b,
+ 0x7b49,0x85e4,0x8b04,0x9127,0x9a30,0x5587,0x61f6,0xf95b,0x7669,0x7f85
+ },
+ { /* ku 55 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x863f,0x87ba,0x88f8,0x908f,
+ 0xf95c,0x6d1b,0x70d9,0x73de,0x7d61,0x843d,0xf95d,0x916a,0x99f1,0xf95e,
+ 0x4e82,0x5375,0x6b04,0x6b12,0x703e,0x721b,0x862d,0x9e1e,0x524c,0x8fa3,
+ 0x5d50,0x64e5,0x652c,0x6b16,0x6feb,0x7c43,0x7e9c,0x85cd,0x8964,0x89bd,
+ 0x62c9,0x81d8,0x881f,0x5eca,0x6717,0x6d6a,0x72fc,0x7405,0x746f,0x8782,
+ 0x90de,0x4f86,0x5d0d,0x5fa0,0x840a,0x51b7,0x63a0,0x7565,0x4eae,0x5006,
+ 0x5169,0x51c9,0x6881,0x6a11,0x7cae,0x7cb1,0x7ce7,0x826f,0x8ad2,0x8f1b,
+ 0x91cf,0x4fb6,0x5137,0x52f5,0x5442,0x5eec,0x616e,0x623e,0x65c5,0x6ada,
+ 0x6ffe,0x792a,0x85dc,0x8823,0x95ad,0x9a62,0x9a6a,0x9e97,0x9ece,0x529b,
+ 0x66c6,0x6b77,0x701d,0x792b,0x8f62,0x9742,0x6190,0x6200,0x6523,0x6f23
+ },
+ { /* ku 56 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7149,0x7489,0x7df4,0x806f,
+ 0x84ee,0x8f26,0x9023,0x934a,0x51bd,0x5217,0x52a3,0x6d0c,0x70c8,0x88c2,
+ 0x5ec9,0x6582,0x6bae,0x6fc2,0x7c3e,0x7375,0x4ee4,0x4f36,0x56f9,0xf95f,
+ 0x5cba,0x5dba,0x601c,0x73b2,0x7b2d,0x7f9a,0x7fce,0x8046,0x901e,0x9234,
+ 0x96f6,0x9748,0x9818,0x9f61,0x4f8b,0x6fa7,0x79ae,0x91b4,0x96b7,0x52de,
+ 0xf960,0x6488,0x64c4,0x6ad3,0x6f5e,0x7018,0x7210,0x76e7,0x8001,0x8606,
+ 0x865c,0x8def,0x8f05,0x9732,0x9b6f,0x9dfa,0x9e75,0x788c,0x797f,0x7da0,
+ 0x83c9,0x9304,0x9e7f,0x9e93,0x8ad6,0x58df,0x5f04,0x6727,0x7027,0x74cf,
+ 0x7c60,0x807e,0x5121,0x7028,0x7262,0x78ca,0x8cc2,0x8cda,0x8cf4,0x96f7,
+ 0x4e86,0x50da,0x5bee,0x5ed6,0x6599,0x71ce,0x7642,0x77ad,0x804a,0x84fc
+ },
+ { /* ku 57 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x907c,0x9b27,0x9f8d,0x58d8,
+ 0x5a41,0x5c62,0x6a13,0x6dda,0x6f0f,0x763b,0x7d2f,0x7e37,0x851e,0x8938,
+ 0x93e4,0x964b,0x5289,0x65d2,0x67f3,0x69b4,0x6d41,0x6e9c,0x700f,0x7409,
+ 0x7460,0x7559,0x7624,0x786b,0x8b2c,0x985e,0x516d,0x622e,0x9678,0x4f96,
+ 0x502b,0x5d19,0x6dea,0x7db8,0x8f2a,0x5f8b,0x6144,0x6817,0xf961,0x9686,
+ 0x52d2,0x808b,0x51dc,0x51cc,0x695e,0x7a1c,0x7dbe,0x83f1,0x9675,0x4fda,
+ 0x5229,0x5398,0x540f,0x550e,0x5c65,0x60a7,0x674e,0x68a8,0x6d6c,0x7281,
+ 0x72f8,0x7406,0x7483,0xf962,0x75e2,0x7c6c,0x7f79,0x7fb8,0x8389,0x88cf,
+ 0x88e1,0x91cc,0x91d0,0x96e2,0x9bc9,0x541d,0x6f7e,0x71d0,0x7498,0x85fa,
+ 0x8eaa,0x96a3,0x9c57,0x9e9f,0x6797,0x6dcb,0x7433,0x81e8,0x9716,0x782c
+ },
+ { /* ku 58 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7acb,0x7b20,0x7c92,0x6469,
+ 0x746a,0x75f2,0x78bc,0x78e8,0x99ac,0x9b54,0x9ebb,0x5bde,0x5e55,0x6f20,
+ 0x819c,0x83ab,0x9088,0x4e07,0x534d,0x5a29,0x5dd2,0x5f4e,0x6162,0x633d,
+ 0x6669,0x66fc,0x6eff,0x6f2b,0x7063,0x779e,0x842c,0x8513,0x883b,0x8f13,
+ 0x9945,0x9c3b,0x551c,0x62b9,0x672b,0x6cab,0x8309,0x896a,0x977a,0x4ea1,
+ 0x5984,0x5fd8,0x5fd9,0x671b,0x7db2,0x7f54,0x8292,0x832b,0x83bd,0x8f1e,
+ 0x9099,0x57cb,0x59b9,0x5a92,0x5bd0,0x6627,0x679a,0x6885,0x6bcf,0x7164,
+ 0x7f75,0x8cb7,0x8ce3,0x9081,0x9b45,0x8108,0x8c8a,0x964c,0x9a40,0x9ea5,
+ 0x5b5f,0x6c13,0x731b,0x76f2,0x76df,0x840c,0x51aa,0x8993,0x514d,0x5195,
+ 0x52c9,0x68c9,0x6c94,0x7704,0x7720,0x7dbf,0x7dec,0x9762,0x9eb5,0x6ec5
+ },
+ { /* ku 59 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8511,0x51a5,0x540d,0x547d,
+ 0x660e,0x669d,0x6927,0x6e9f,0x76bf,0x7791,0x8317,0x84c2,0x879f,0x9169,
+ 0x9298,0x9cf4,0x8882,0x4fae,0x5192,0x52df,0x59c6,0x5e3d,0x6155,0x6478,
+ 0x6479,0x66ae,0x67d0,0x6a21,0x6bcd,0x6bdb,0x725f,0x7261,0x7441,0x7738,
+ 0x77db,0x8017,0x82bc,0x8305,0x8b00,0x8b28,0x8c8c,0x6728,0x6c90,0x7267,
+ 0x76ee,0x7766,0x7a46,0x9da9,0x6b7f,0x6c92,0x5922,0x6726,0x8499,0x536f,
+ 0x5893,0x5999,0x5edf,0x63cf,0x6634,0x6773,0x6e3a,0x732b,0x7ad7,0x82d7,
+ 0x9328,0x52d9,0x5deb,0x61ae,0x61cb,0x620a,0x62c7,0x64ab,0x65e0,0x6959,
+ 0x6b66,0x6bcb,0x7121,0x73f7,0x755d,0x7e46,0x821e,0x8302,0x856a,0x8aa3,
+ 0x8cbf,0x9727,0x9d61,0x58a8,0x9ed8,0x5011,0x520e,0x543b,0x554f,0x6587
+ },
+ { /* ku 5a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6c76,0x7d0a,0x7d0b,0x805e,
+ 0x868a,0x9580,0x96ef,0x52ff,0x6c95,0x7269,0x5473,0x5a9a,0x5c3e,0x5d4b,
+ 0x5f4c,0x5fae,0x672a,0x68b6,0x6963,0x6e3c,0x6e44,0x7709,0x7c73,0x7f8e,
+ 0x8587,0x8b0e,0x8ff7,0x9761,0x9ef4,0x5cb7,0x60b6,0x610d,0x61ab,0x654f,
+ 0x65fb,0x65fc,0x6c11,0x6cef,0x739f,0x73c9,0x7de1,0x9594,0x5bc6,0x871c,
+ 0x8b10,0x525d,0x535a,0x62cd,0x640f,0x64b2,0x6734,0x6a38,0x6cca,0x73c0,
+ 0x749e,0x7b94,0x7c95,0x7e1b,0x818a,0x8236,0x8584,0x8feb,0x96f9,0x99c1,
+ 0x4f34,0x534a,0x53cd,0x53db,0x62cc,0x642c,0x6500,0x6591,0x69c3,0x6cee,
+ 0x6f58,0x73ed,0x7554,0x7622,0x76e4,0x76fc,0x78d0,0x78fb,0x792c,0x7d46,
+ 0x822c,0x87e0,0x8fd4,0x9812,0x98ef,0x52c3,0x62d4,0x64a5,0x6e24,0x6f51
+ },
+ { /* ku 5b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x767c,0x8dcb,0x91b1,0x9262,
+ 0x9aee,0x9b43,0x5023,0x508d,0x574a,0x59a8,0x5c28,0x5e47,0x5f77,0x623f,
+ 0x653e,0x65b9,0x65c1,0x6609,0x678b,0x699c,0x6ec2,0x78c5,0x7d21,0x80aa,
+ 0x8180,0x822b,0x82b3,0x84a1,0x868c,0x8a2a,0x8b17,0x90a6,0x9632,0x9f90,
+ 0x500d,0x4ff3,0xf963,0x57f9,0x5f98,0x62dc,0x6392,0x676f,0x6e43,0x7119,
+ 0x76c3,0x80cc,0x80da,0x88f4,0x88f5,0x8919,0x8ce0,0x8f29,0x914d,0x966a,
+ 0x4f2f,0x4f70,0x5e1b,0x67cf,0x6822,0x767d,0x767e,0x9b44,0x5e61,0x6a0a,
+ 0x7169,0x71d4,0x756a,0xf964,0x7e41,0x8543,0x85e9,0x98dc,0x4f10,0x7b4f,
+ 0x7f70,0x95a5,0x51e1,0x5e06,0x68b5,0x6c3e,0x6c4e,0x6cdb,0x72af,0x7bc4,
+ 0x8303,0x6cd5,0x743a,0x50fb,0x5288,0x58c1,0x64d8,0x6a97,0x74a7,0x7656
+ },
+ { /* ku 5c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x78a7,0x8617,0x95e2,0x9739,
+ 0xf965,0x535e,0x5f01,0x8b8a,0x8fa8,0x8faf,0x908a,0x5225,0x77a5,0x9c49,
+ 0x9f08,0x4e19,0x5002,0x5175,0x5c5b,0x5e77,0x661e,0x663a,0x67c4,0x68c5,
+ 0x70b3,0x7501,0x75c5,0x79c9,0x7add,0x8f27,0x9920,0x9a08,0x4fdd,0x5821,
+ 0x5831,0x5bf6,0x666e,0x6b65,0x6d11,0x6e7a,0x6f7d,0x73e4,0x752b,0x83e9,
+ 0x88dc,0x8913,0x8b5c,0x8f14,0x4f0f,0x50d5,0x5310,0x535c,0x5b93,0x5fa9,
+ 0x670d,0x798f,0x8179,0x832f,0x8514,0x8907,0x8986,0x8f39,0x8f3b,0x99a5,
+ 0x9c12,0x672c,0x4e76,0x4ff8,0x5949,0x5c01,0x5cef,0x5cf0,0x6367,0x68d2,
+ 0x70fd,0x71a2,0x742b,0x7e2b,0x84ec,0x8702,0x9022,0x92d2,0x9cf3,0x4e0d,
+ 0x4ed8,0x4fef,0x5085,0x5256,0x526f,0x5426,0x5490,0x57e0,0x592b,0x5a66
+ },
+ { /* ku 5d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5b5a,0x5b75,0x5bcc,0x5e9c,
+ 0xf966,0x6276,0x6577,0x65a7,0x6d6e,0x6ea5,0x7236,0x7b26,0x7c3f,0x7f36,
+ 0x8150,0x8151,0x819a,0x8240,0x8299,0x83a9,0x8a03,0x8ca0,0x8ce6,0x8cfb,
+ 0x8d74,0x8dba,0x90e8,0x91dc,0x961c,0x9644,0x99d9,0x9ce7,0x5317,0x5206,
+ 0x5429,0x5674,0x58b3,0x5954,0x596e,0x5fff,0x61a4,0x626e,0x6610,0x6c7e,
+ 0x711a,0x76c6,0x7c89,0x7cde,0x7d1b,0x82ac,0x8cc1,0x96f0,0xf967,0x4f5b,
+ 0x5f17,0x5f7f,0x62c2,0x5d29,0x670b,0x68da,0x787c,0x7e43,0x9d6c,0x4e15,
+ 0x5099,0x5315,0x532a,0x5351,0x5983,0x5a62,0x5e87,0x60b2,0x618a,0x6249,
+ 0x6279,0x6590,0x6787,0x69a7,0x6bd4,0x6bd6,0x6bd7,0x6bd8,0x6cb8,0xf968,
+ 0x7435,0x75fa,0x7812,0x7891,0x79d5,0x79d8,0x7c83,0x7dcb,0x7fe1,0x80a5
+ },
+ { /* ku 5e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x813e,0x81c2,0x83f2,0x871a,
+ 0x88e8,0x8ab9,0x8b6c,0x8cbb,0x9119,0x975e,0x98db,0x9f3b,0x56ac,0x5b2a,
+ 0x5f6c,0x658c,0x6ab3,0x6baf,0x6d5c,0x6ff1,0x7015,0x725d,0x73ad,0x8ca7,
+ 0x8cd3,0x983b,0x6191,0x6c37,0x8058,0x9a01,0x4e4d,0x4e8b,0x4e9b,0x4ed5,
+ 0x4f3a,0x4f3c,0x4f7f,0x4fdf,0x50ff,0x53f2,0x53f8,0x5506,0x55e3,0x56db,
+ 0x58eb,0x5962,0x5a11,0x5beb,0x5bfa,0x5c04,0x5df3,0x5e2b,0x5f99,0x601d,
+ 0x6368,0x659c,0x65af,0x67f6,0x67fb,0x68ad,0x6b7b,0x6c99,0x6cd7,0x6e23,
+ 0x7009,0x7345,0x7802,0x793e,0x7940,0x7960,0x79c1,0x7be9,0x7d17,0x7d72,
+ 0x8086,0x820d,0x838e,0x84d1,0x86c7,0x88df,0x8a50,0x8a5e,0x8b1d,0x8cdc,
+ 0x8d66,0x8fad,0x90aa,0x98fc,0x99df,0x9e9d,0x524a,0xf969,0x6714,0xf96a
+ },
+ { /* ku 5f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5098,0x522a,0x5c71,0x6563,
+ 0x6c55,0x73ca,0x7523,0x759d,0x7b97,0x849c,0x9178,0x9730,0x4e77,0x6492,
+ 0x6bba,0x715e,0x85a9,0x4e09,0xf96b,0x6749,0x68ee,0x6e17,0x829f,0x8518,
+ 0x886b,0x63f7,0x6f81,0x9212,0x98af,0x4e0a,0x50b7,0x50cf,0x511f,0x5546,
+ 0x55aa,0x5617,0x5b40,0x5c19,0x5ce0,0x5e38,0x5e8a,0x5ea0,0x5ec2,0x60f3,
+ 0x6851,0x6a61,0x6e58,0x723d,0x7240,0x72c0,0x76f8,0x7965,0x7bb1,0x7fd4,
+ 0x88f3,0x89f4,0x8a73,0x8c61,0x8cde,0x971c,0x585e,0x74bd,0x8cfd,0x55c7,
+ 0xf96c,0x7a61,0x7d22,0x8272,0x7272,0x751f,0x7525,0xf96d,0x7b19,0x5885,
+ 0x58fb,0x5dbc,0x5e8f,0x5eb6,0x5f90,0x6055,0x6292,0x637f,0x654d,0x6691,
+ 0x66d9,0x66f8,0x6816,0x68f2,0x7280,0x745e,0x7b6e,0x7d6e,0x7dd6,0x7f72
+ },
+ { /* ku 60 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x80e5,0x8212,0x85af,0x897f,
+ 0x8a93,0x901d,0x92e4,0x9ecd,0x9f20,0x5915,0x596d,0x5e2d,0x60dc,0x6614,
+ 0x6673,0x6790,0x6c50,0x6dc5,0x6f5f,0x77f3,0x78a9,0x84c6,0x91cb,0x932b,
+ 0x4ed9,0x50ca,0x5148,0x5584,0x5b0b,0x5ba3,0x6247,0x657e,0x65cb,0x6e32,
+ 0x717d,0x7401,0x7444,0x7487,0x74bf,0x766c,0x79aa,0x7dda,0x7e55,0x7fa8,
+ 0x817a,0x81b3,0x8239,0x861a,0x87ec,0x8a75,0x8de3,0x9078,0x9291,0x9425,
+ 0x994d,0x9bae,0x5368,0x5c51,0x6954,0x6cc4,0x6d29,0x6e2b,0x820c,0x859b,
+ 0x893b,0x8a2d,0x8aaa,0x96ea,0x9f67,0x5261,0x66b9,0x6bb2,0x7e96,0x87fe,
+ 0x8d0d,0x9583,0x965d,0x651d,0x6d89,0x71ee,0xf96e,0x57ce,0x59d3,0x5bac,
+ 0x6027,0x60fa,0x6210,0x661f,0x665f,0x7329,0x73f9,0x76db,0x7701,0x7b6c
+ },
+ { /* ku 61 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8056,0x8072,0x8165,0x8aa0,
+ 0x9192,0x4e16,0x52e2,0x6b72,0x6d17,0x7a05,0x7b39,0x7d30,0xf96f,0x8cb0,
+ 0x53ec,0x562f,0x5851,0x5bb5,0x5c0f,0x5c11,0x5de2,0x6240,0x6383,0x6414,
+ 0x662d,0x68b3,0x6cbc,0x6d88,0x6eaf,0x701f,0x70a4,0x71d2,0x7526,0x758f,
+ 0x758e,0x7619,0x7b11,0x7be0,0x7c2b,0x7d20,0x7d39,0x852c,0x856d,0x8607,
+ 0x8a34,0x900d,0x9061,0x90b5,0x92b7,0x97f6,0x9a37,0x4fd7,0x5c6c,0x675f,
+ 0x6d91,0x7c9f,0x7e8c,0x8b16,0x8d16,0x901f,0x5b6b,0x5dfd,0x640d,0x84c0,
+ 0x905c,0x98e1,0x7387,0x5b8b,0x609a,0x677e,0x6dde,0x8a1f,0x8aa6,0x9001,
+ 0x980c,0x5237,0xf970,0x7051,0x788e,0x9396,0x8870,0x91d7,0x4fee,0x53d7,
+ 0x55fd,0x56da,0x5782,0x58fd,0x5ac2,0x5b88,0x5cab,0x5cc0,0x5e25,0x6101
+ },
+ { /* ku 62 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x620d,0x624b,0x6388,0x641c,
+ 0x6536,0x6578,0x6a39,0x6b8a,0x6c34,0x6d19,0x6f31,0x71e7,0x72e9,0x7378,
+ 0x7407,0x74b2,0x7626,0x7761,0x79c0,0x7a57,0x7aea,0x7cb9,0x7d8f,0x7dac,
+ 0x7e61,0x7f9e,0x8129,0x8331,0x8490,0x84da,0x85ea,0x8896,0x8ab0,0x8b90,
+ 0x8f38,0x9042,0x9083,0x916c,0x9296,0x92b9,0x968b,0x96a7,0x96a8,0x96d6,
+ 0x9700,0x9808,0x9996,0x9ad3,0x9b1a,0x53d4,0x587e,0x5919,0x5b70,0x5bbf,
+ 0x6dd1,0x6f5a,0x719f,0x7421,0x74b9,0x8085,0x83fd,0x5de1,0x5f87,0x5faa,
+ 0x6042,0x65ec,0x6812,0x696f,0x6a53,0x6b89,0x6d35,0x6df3,0x73e3,0x76fe,
+ 0x77ac,0x7b4d,0x7d14,0x8123,0x821c,0x8340,0x84f4,0x8563,0x8a62,0x8ac4,
+ 0x9187,0x931e,0x9806,0x99b4,0x620c,0x8853,0x8ff0,0x9265,0x5d07,0x5d27
+ },
+ { /* ku 63 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5d69,0x745f,0x819d,0x8768,
+ 0x6fd5,0x62fe,0x7fd2,0x8936,0x8972,0x4e1e,0x4e58,0x50e7,0x52dd,0x5347,
+ 0x627f,0x6607,0x7e69,0x8805,0x965e,0x4f8d,0x5319,0x5636,0x59cb,0x5aa4,
+ 0x5c38,0x5c4e,0x5c4d,0x5e02,0x5f11,0x6043,0x65bd,0x662f,0x6642,0x67be,
+ 0x67f4,0x731c,0x77e2,0x793a,0x7fc5,0x8494,0x84cd,0x8996,0x8a66,0x8a69,
+ 0x8ae1,0x8c55,0x8c7a,0x57f4,0x5bd4,0x5f0f,0x606f,0x62ed,0x690d,0x6b96,
+ 0x6e5c,0x7184,0x7bd2,0x8755,0x8b58,0x8efe,0x98df,0x98fe,0x4f38,0x4f81,
+ 0x4fe1,0x547b,0x5a20,0x5bb8,0x613c,0x65b0,0x6668,0x71fc,0x7533,0x795e,
+ 0x7d33,0x814e,0x81e3,0x8398,0x85aa,0x85ce,0x8703,0x8a0a,0x8eab,0x8f9b,
+ 0xf971,0x8fc5,0x5931,0x5ba4,0x5be6,0x6089,0x5be9,0x5c0b,0x5fc3,0x6c81
+ },
+ { /* ku 64 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0xf972,0x6df1,0x700b,0x751a,
+ 0x82af,0x8af6,0x4ec0,0x5341,0xf973,0x96d9,0x6c0f,0x4e9e,0x4fc4,0x5152,
+ 0x555e,0x5a25,0x5ce8,0x6211,0x7259,0x82bd,0x83aa,0x86fe,0x8859,0x8a1d,
+ 0x963f,0x96c5,0x9913,0x9d09,0x9d5d,0x580a,0x5cb3,0x5dbd,0x5e44,0x60e1,
+ 0x6115,0x63e1,0x6a02,0x6e25,0x9102,0x9354,0x984e,0x9c10,0x9f77,0x5b89,
+ 0x5cb8,0x6309,0x664f,0x6848,0x773c,0x96c1,0x978d,0x9854,0x9b9f,0x65a1,
+ 0x8b01,0x8ecb,0x95bc,0x5535,0x5ca9,0x5dd6,0x5eb5,0x6697,0x764c,0x83f4,
+ 0x95c7,0x58d3,0x62bc,0x72ce,0x9d28,0x4ef0,0x592e,0x600f,0x663b,0x6b83,
+ 0x79e7,0x9d26,0x5393,0x54c0,0x57c3,0x5d16,0x611b,0x66d6,0x6daf,0x788d,
+ 0x827e,0x9698,0x9744,0x5384,0x627c,0x6396,0x6db2,0x7e0a,0x814b,0x984d
+ },
+ { /* ku 65 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6afb,0x7f4c,0x9daf,0x9e1a,
+ 0x4e5f,0x503b,0x51b6,0x591c,0x60f9,0x63f6,0x6930,0x723a,0x8036,0xf974,
+ 0x91ce,0x5f31,0xf975,0xf976,0x7d04,0x82e5,0x846f,0x84bb,0x85e5,0x8e8d,
+ 0xf977,0x4f6f,0xf978,0xf979,0x58e4,0x5b43,0x6059,0x63da,0x6518,0x656d,
+ 0x6698,0xf97a,0x694a,0x6a23,0x6d0b,0x7001,0x716c,0x75d2,0x760d,0x79b3,
+ 0x7a70,0xf97b,0x7f8a,0xf97c,0x8944,0xf97d,0x8b93,0x91c0,0x967d,0xf97e,
+ 0x990a,0x5704,0x5fa1,0x65bc,0x6f01,0x7600,0x79a6,0x8a9e,0x99ad,0x9b5a,
+ 0x9f6c,0x5104,0x61b6,0x6291,0x6a8d,0x81c6,0x5043,0x5830,0x5f66,0x7109,
+ 0x8a00,0x8afa,0x5b7c,0x8616,0x4ffa,0x513c,0x56b4,0x5944,0x63a9,0x6df9,
+ 0x5daa,0x696d,0x5186,0x4e88,0x4f59,0xf97f,0xf980,0xf981,0x5982,0xf982
+ },
+ { /* ku 66 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0xf983,0x6b5f,0x6c5d,0xf984,
+ 0x74b5,0x7916,0xf985,0x8207,0x8245,0x8339,0x8f3f,0x8f5d,0xf986,0x9918,
+ 0xf987,0xf988,0xf989,0x4ea6,0xf98a,0x57df,0x5f79,0x6613,0xf98b,0xf98c,
+ 0x75ab,0x7e79,0x8b6f,0xf98d,0x9006,0x9a5b,0x56a5,0x5827,0x59f8,0x5a1f,
+ 0x5bb4,0xf98e,0x5ef6,0xf98f,0xf990,0x6350,0x633b,0xf991,0x693d,0x6c87,
+ 0x6cbf,0x6d8e,0x6d93,0x6df5,0x6f14,0xf992,0x70df,0x7136,0x7159,0xf993,
+ 0x71c3,0x71d5,0xf994,0x784f,0x786f,0xf995,0x7b75,0x7de3,0xf996,0x7e2f,
+ 0xf997,0x884d,0x8edf,0xf998,0xf999,0xf99a,0x925b,0xf99b,0x9cf6,0xf99c,
+ 0xf99d,0xf99e,0x6085,0x6d85,0xf99f,0x71b1,0xf9a0,0xf9a1,0x95b1,0x53ad,
+ 0xf9a2,0xf9a3,0xf9a4,0x67d3,0xf9a5,0x708e,0x7130,0x7430,0x8276,0x82d2
+ },
+ { /* ku 67 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0xf9a6,0x95bb,0x9ae5,0x9e7d,
+ 0x66c4,0xf9a7,0x71c1,0x8449,0xf9a8,0xf9a9,0x584b,0xf9aa,0xf9ab,0x5db8,
+ 0x5f71,0xf9ac,0x6620,0x668e,0x6979,0x69ae,0x6c38,0x6cf3,0x6e36,0x6f41,
+ 0x6fda,0x701b,0x702f,0x7150,0x71df,0x7370,0xf9ad,0x745b,0xf9ae,0x74d4,
+ 0x76c8,0x7a4e,0x7e93,0xf9af,0xf9b0,0x82f1,0x8a60,0x8fce,0xf9b1,0x9348,
+ 0xf9b2,0x9719,0xf9b3,0xf9b4,0x4e42,0x502a,0xf9b5,0x5208,0x53e1,0x66f3,
+ 0x6c6d,0x6fca,0x730a,0x777f,0x7a62,0x82ae,0x85dd,0x8602,0xf9b6,0x88d4,
+ 0x8a63,0x8b7d,0x8c6b,0xf9b7,0x92b3,0xf9b8,0x9713,0x9810,0x4e94,0x4f0d,
+ 0x4fc9,0x50b2,0x5348,0x543e,0x5433,0x55da,0x5862,0x58ba,0x5967,0x5a1b,
+ 0x5be4,0x609f,0xf9b9,0x61ca,0x6556,0x65ff,0x6664,0x68a7,0x6c5a,0x6fb3
+ },
+ { /* ku 68 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x70cf,0x71ac,0x7352,0x7b7d,
+ 0x8708,0x8aa4,0x9c32,0x9f07,0x5c4b,0x6c83,0x7344,0x7389,0x923a,0x6eab,
+ 0x7465,0x761f,0x7a69,0x7e15,0x860a,0x5140,0x58c5,0x64c1,0x74ee,0x7515,
+ 0x7670,0x7fc1,0x9095,0x96cd,0x9954,0x6e26,0x74e6,0x7aa9,0x7aaa,0x81e5,
+ 0x86d9,0x8778,0x8a1b,0x5a49,0x5b8c,0x5b9b,0x68a1,0x6900,0x6d63,0x73a9,
+ 0x7413,0x742c,0x7897,0x7de9,0x7feb,0x8118,0x8155,0x839e,0x8c4c,0x962e,
+ 0x9811,0x66f0,0x5f80,0x65fa,0x6789,0x6c6a,0x738b,0x502d,0x5a03,0x6b6a,
+ 0x77ee,0x5916,0x5d6c,0x5dcd,0x7325,0x754f,0xf9ba,0xf9bb,0x50e5,0x51f9,
+ 0x582f,0x592d,0x5996,0x59da,0x5be5,0xf9bc,0xf9bd,0x5da2,0x62d7,0x6416,
+ 0x6493,0x64fe,0xf9be,0x66dc,0xf9bf,0x6a48,0xf9c0,0x71ff,0x7464,0xf9c1
+ },
+ { /* ku 69 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x7a88,0x7aaf,0x7e47,0x7e5e,
+ 0x8000,0x8170,0xf9c2,0x87ef,0x8981,0x8b20,0x9059,0xf9c3,0x9080,0x9952,
+ 0x617e,0x6b32,0x6d74,0x7e1f,0x8925,0x8fb1,0x4fd1,0x50ad,0x5197,0x52c7,
+ 0x57c7,0x5889,0x5bb9,0x5eb8,0x6142,0x6995,0x6d8c,0x6e67,0x6eb6,0x7194,
+ 0x7462,0x7528,0x752c,0x8073,0x8338,0x84c9,0x8e0a,0x9394,0x93de,0xf9c4,
+ 0x4e8e,0x4f51,0x5076,0x512a,0x53c8,0x53cb,0x53f3,0x5b87,0x5bd3,0x5c24,
+ 0x611a,0x6182,0x65f4,0x725b,0x7397,0x7440,0x76c2,0x7950,0x7991,0x79b9,
+ 0x7d06,0x7fbd,0x828b,0x85d5,0x865e,0x8fc2,0x9047,0x90f5,0x91ea,0x9685,
+ 0x96e8,0x96e9,0x52d6,0x5f67,0x65ed,0x6631,0x682f,0x715c,0x7a36,0x90c1,
+ 0x980a,0x4e91,0xf9c5,0x6a52,0x6b9e,0x6f90,0x7189,0x8018,0x82b8,0x8553
+ },
+ { /* ku 6a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x904b,0x9695,0x96f2,0x97fb,
+ 0x851a,0x9b31,0x4e90,0x718a,0x96c4,0x5143,0x539f,0x54e1,0x5713,0x5712,
+ 0x57a3,0x5a9b,0x5ac4,0x5bc3,0x6028,0x613f,0x63f4,0x6c85,0x6d39,0x6e72,
+ 0x6e90,0x7230,0x733f,0x7457,0x82d1,0x8881,0x8f45,0x9060,0xf9c6,0x9662,
+ 0x9858,0x9d1b,0x6708,0x8d8a,0x925e,0x4f4d,0x5049,0x50de,0x5371,0x570d,
+ 0x59d4,0x5a01,0x5c09,0x6170,0x6690,0x6e2d,0x7232,0x744b,0x7def,0x80c3,
+ 0x840e,0x8466,0x853f,0x875f,0x885b,0x8918,0x8b02,0x9055,0x97cb,0x9b4f,
+ 0x4e73,0x4f91,0x5112,0x516a,0xf9c7,0x552f,0x55a9,0x5b7a,0x5ba5,0x5e7c,
+ 0x5e7d,0x5ebe,0x60a0,0x60df,0x6108,0x6109,0x63c4,0x6538,0x6709,0xf9c8,
+ 0x67d4,0x67da,0xf9c9,0x6961,0x6962,0x6cb9,0x6d27,0xf9ca,0x6e38,0xf9cb
+ },
+ { /* ku 6b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6fe1,0x7336,0x7337,0xf9cc,
+ 0x745c,0x7531,0xf9cd,0x7652,0xf9ce,0xf9cf,0x7dad,0x81fe,0x8438,0x88d5,
+ 0x8a98,0x8adb,0x8aed,0x8e30,0x8e42,0x904a,0x903e,0x907a,0x9149,0x91c9,
+ 0x936e,0xf9d0,0xf9d1,0x5809,0xf9d2,0x6bd3,0x8089,0x80b2,0xf9d3,0xf9d4,
+ 0x5141,0x596b,0x5c39,0xf9d5,0xf9d6,0x6f64,0x73a7,0x80e4,0x8d07,0xf9d7,
+ 0x9217,0x958f,0xf9d8,0xf9d9,0xf9da,0xf9db,0x807f,0x620e,0x701c,0x7d68,
+ 0x878d,0xf9dc,0x57a0,0x6069,0x6147,0x6bb7,0x8abe,0x9280,0x96b1,0x4e59,
+ 0x541f,0x6deb,0x852d,0x9670,0x97f3,0x98ee,0x63d6,0x6ce3,0x9091,0x51dd,
+ 0x61c9,0x81ba,0x9df9,0x4f9d,0x501a,0x5100,0x5b9c,0x610f,0x61ff,0x64ec,
+ 0x6905,0x6bc5,0x7591,0x77e3,0x7fa9,0x8264,0x858f,0x87fb,0x8863,0x8abc
+ },
+ { /* ku 6c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8b70,0x91ab,0x4e8c,0x4ee5,
+ 0x4f0a,0xf9dd,0xf9de,0x5937,0x59e8,0xf9df,0x5df2,0x5f1b,0x5f5b,0x6021,
+ 0xf9e0,0xf9e1,0xf9e2,0xf9e3,0x723e,0x73e5,0xf9e4,0x7570,0x75cd,0xf9e5,
+ 0x79fb,0xf9e6,0x800c,0x8033,0x8084,0x82e1,0x8351,0xf9e7,0xf9e8,0x8cbd,
+ 0x8cb3,0x9087,0xf9e9,0xf9ea,0x98f4,0x990c,0xf9eb,0xf9ec,0x7037,0x76ca,
+ 0x7fca,0x7fcc,0x7ffc,0x8b1a,0x4eba,0x4ec1,0x5203,0x5370,0xf9ed,0x54bd,
+ 0x56e0,0x59fb,0x5bc5,0x5f15,0x5fcd,0x6e6e,0xf9ee,0xf9ef,0x7d6a,0x8335,
+ 0xf9f0,0x8693,0x8a8d,0xf9f1,0x976d,0x9777,0xf9f2,0xf9f3,0x4e00,0x4f5a,
+ 0x4f7e,0x58f9,0x65e5,0x6ea2,0x9038,0x93b0,0x99b9,0x4efb,0x58ec,0x598a,
+ 0x59d9,0x6041,0xf9f4,0xf9f5,0x7a14,0xf9f6,0x834f,0x8cc3,0x5165,0x5344
+ },
+ { /* ku 6d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0xf9f7,0xf9f8,0xf9f9,0x4ecd,
+ 0x5269,0x5b55,0x82bf,0x4ed4,0x523a,0x54a8,0x59c9,0x59ff,0x5b50,0x5b57,
+ 0x5b5c,0x6063,0x6148,0x6ecb,0x7099,0x716e,0x7386,0x74f7,0x75b5,0x78c1,
+ 0x7d2b,0x8005,0x81ea,0x8328,0x8517,0x85c9,0x8aee,0x8cc7,0x96cc,0x4f5c,
+ 0x52fa,0x56bc,0x65ab,0x6628,0x707c,0x70b8,0x7235,0x7dbd,0x828d,0x914c,
+ 0x96c0,0x9d72,0x5b71,0x68e7,0x6b98,0x6f7a,0x76de,0x5c91,0x66ab,0x6f5b,
+ 0x7bb4,0x7c2a,0x8836,0x96dc,0x4e08,0x4ed7,0x5320,0x5834,0x58bb,0x58ef,
+ 0x596c,0x5c07,0x5e33,0x5e84,0x5f35,0x638c,0x66b2,0x6756,0x6a1f,0x6aa3,
+ 0x6b0c,0x6f3f,0x7246,0xf9fa,0x7350,0x748b,0x7ae0,0x7ca7,0x8178,0x81df,
+ 0x81e7,0x838a,0x846c,0x8523,0x8594,0x85cf,0x88dd,0x8d13,0x91ac,0x9577
+ },
+ { /* ku 6e */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x969c,0x518d,0x54c9,0x5728,
+ 0x5bb0,0x624d,0x6750,0x683d,0x6893,0x6e3d,0x6ed3,0x707d,0x7e21,0x88c1,
+ 0x8ca1,0x8f09,0x9f4b,0x9f4e,0x722d,0x7b8f,0x8acd,0x931a,0x4f47,0x4f4e,
+ 0x5132,0x5480,0x59d0,0x5e95,0x62b5,0x6775,0x696e,0x6a17,0x6cae,0x6e1a,
+ 0x72d9,0x732a,0x75bd,0x7bb8,0x7d35,0x82e7,0x83f9,0x8457,0x85f7,0x8a5b,
+ 0x8caf,0x8e87,0x9019,0x90b8,0x96ce,0x9f5f,0x52e3,0x540a,0x5ae1,0x5bc2,
+ 0x6458,0x6575,0x6ef4,0x72c4,0xf9fb,0x7684,0x7a4d,0x7b1b,0x7c4d,0x7e3e,
+ 0x7fdf,0x837b,0x8b2b,0x8cca,0x8d64,0x8de1,0x8e5f,0x8fea,0x8ff9,0x9069,
+ 0x93d1,0x4f43,0x4f7a,0x50b3,0x5168,0x5178,0x524d,0x526a,0x5861,0x587c,
+ 0x5960,0x5c08,0x5c55,0x5edb,0x609b,0x6230,0x6813,0x6bbf,0x6c08,0x6fb1
+ },
+ { /* ku 6f */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x714e,0x7420,0x7530,0x7538,
+ 0x7551,0x7672,0x7b4c,0x7b8b,0x7bad,0x7bc6,0x7e8f,0x8a6e,0x8f3e,0x8f49,
+ 0x923f,0x9293,0x9322,0x942b,0x96fb,0x985a,0x986b,0x991e,0x5207,0x622a,
+ 0x6298,0x6d59,0x7664,0x7aca,0x7bc0,0x7d76,0x5360,0x5cbe,0x5e97,0x6f38,
+ 0x70b9,0x7c98,0x9711,0x9b8e,0x9ede,0x63a5,0x647a,0x8776,0x4e01,0x4e95,
+ 0x4ead,0x505c,0x5075,0x5448,0x59c3,0x5b9a,0x5e40,0x5ead,0x5ef7,0x5f81,
+ 0x60c5,0x633a,0x653f,0x6574,0x65cc,0x6676,0x6678,0x67fe,0x6968,0x6a89,
+ 0x6b63,0x6c40,0x6dc0,0x6de8,0x6e1f,0x6e5e,0x701e,0x70a1,0x738e,0x73fd,
+ 0x753a,0x775b,0x7887,0x798e,0x7a0b,0x7a7d,0x7cbe,0x7d8e,0x8247,0x8a02,
+ 0x8aea,0x8c9e,0x912d,0x914a,0x91d8,0x9266,0x92cc,0x9320,0x9706,0x9756
+ },
+ { /* ku 70 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x975c,0x9802,0x9f0e,0x5236,
+ 0x5291,0x557c,0x5824,0x5e1d,0x5f1f,0x608c,0x63d0,0x68af,0x6fdf,0x796d,
+ 0x7b2c,0x81cd,0x85ba,0x88fd,0x8af8,0x8e44,0x918d,0x9664,0x969b,0x973d,
+ 0x984c,0x9f4a,0x4fce,0x5146,0x51cb,0x52a9,0x5632,0x5f14,0x5f6b,0x63aa,
+ 0x64cd,0x65e9,0x6641,0x66fa,0x66f9,0x671d,0x689d,0x68d7,0x69fd,0x6f15,
+ 0x6f6e,0x7167,0x71e5,0x722a,0x74aa,0x773a,0x7956,0x795a,0x79df,0x7a20,
+ 0x7a95,0x7c97,0x7cdf,0x7d44,0x7e70,0x8087,0x85fb,0x86a4,0x8a54,0x8abf,
+ 0x8d99,0x8e81,0x9020,0x906d,0x91e3,0x963b,0x96d5,0x9ce5,0x65cf,0x7c07,
+ 0x8db3,0x93c3,0x5b58,0x5c0a,0x5352,0x62d9,0x731d,0x5027,0x5b97,0x5f9e,
+ 0x60b0,0x616b,0x68d5,0x6dd9,0x742e,0x7a2e,0x7d42,0x7d9c,0x7e31,0x816b
+ },
+ { /* ku 71 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8e2a,0x8e35,0x937e,0x9418,
+ 0x4f50,0x5750,0x5de6,0x5ea7,0x632b,0x7f6a,0x4e3b,0x4f4f,0x4f8f,0x505a,
+ 0x59dd,0x80c4,0x546a,0x5468,0x55fe,0x594f,0x5b99,0x5dde,0x5eda,0x665d,
+ 0x6731,0x67f1,0x682a,0x6ce8,0x6d32,0x6e4a,0x6f8d,0x70b7,0x73e0,0x7587,
+ 0x7c4c,0x7d02,0x7d2c,0x7da2,0x821f,0x86db,0x8a3b,0x8a85,0x8d70,0x8e8a,
+ 0x8f33,0x9031,0x914e,0x9152,0x9444,0x99d0,0x7af9,0x7ca5,0x4fca,0x5101,
+ 0x51c6,0x57c8,0x5bef,0x5cfb,0x6659,0x6a3d,0x6d5a,0x6e96,0x6fec,0x710c,
+ 0x756f,0x7ae3,0x8822,0x9021,0x9075,0x96cb,0x99ff,0x8301,0x4e2d,0x4ef2,
+ 0x8846,0x91cd,0x537d,0x6adb,0x696b,0x6c41,0x847a,0x589e,0x618e,0x66fe,
+ 0x62ef,0x70dd,0x7511,0x75c7,0x7e52,0x84b8,0x8b49,0x8d08,0x4e4b,0x53ea
+ },
+ { /* ku 72 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x54ab,0x5730,0x5740,0x5fd7,
+ 0x6301,0x6307,0x646f,0x652f,0x65e8,0x667a,0x679d,0x67b3,0x6b62,0x6c60,
+ 0x6c9a,0x6f2c,0x77e5,0x7825,0x7949,0x7957,0x7d19,0x80a2,0x8102,0x81f3,
+ 0x829d,0x82b7,0x8718,0x8a8c,0xf9fc,0x8d04,0x8dbe,0x9072,0x76f4,0x7a19,
+ 0x7a37,0x7e54,0x8077,0x5507,0x55d4,0x5875,0x632f,0x6422,0x6649,0x664b,
+ 0x686d,0x699b,0x6b84,0x6d25,0x6eb1,0x73cd,0x7468,0x74a1,0x755b,0x75b9,
+ 0x76e1,0x771e,0x778b,0x79e6,0x7e09,0x7e1d,0x81fb,0x852f,0x8897,0x8a3a,
+ 0x8cd1,0x8eeb,0x8fb0,0x9032,0x93ad,0x9663,0x9673,0x9707,0x4f84,0x53f1,
+ 0x59ea,0x5ac9,0x5e19,0x684e,0x74c6,0x75be,0x79e9,0x7a92,0x81a3,0x86ed,
+ 0x8cea,0x8dcc,0x8fed,0x659f,0x6715,0xf9fd,0x57f7,0x6f57,0x7ddd,0x8f2f
+ },
+ { /* ku 73 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x93f6,0x96c6,0x5fb5,0x61f2,
+ 0x6f84,0x4e14,0x4f98,0x501f,0x53c9,0x55df,0x5d6f,0x5dee,0x6b21,0x6b64,
+ 0x78cb,0x7b9a,0xf9fe,0x8e49,0x8eca,0x906e,0x6349,0x643e,0x7740,0x7a84,
+ 0x932f,0x947f,0x9f6a,0x64b0,0x6faf,0x71e6,0x74a8,0x74da,0x7ac4,0x7c12,
+ 0x7e82,0x7cb2,0x7e98,0x8b9a,0x8d0a,0x947d,0x9910,0x994c,0x5239,0x5bdf,
+ 0x64e6,0x672d,0x7d2e,0x50ed,0x53c3,0x5879,0x6158,0x6159,0x61fa,0x65ac,
+ 0x7ad9,0x8b92,0x8b96,0x5009,0x5021,0x5275,0x5531,0x5a3c,0x5ee0,0x5f70,
+ 0x6134,0x655e,0x660c,0x6636,0x66a2,0x69cd,0x6ec4,0x6f32,0x7316,0x7621,
+ 0x7a93,0x8139,0x8259,0x83d6,0x84bc,0x50b5,0x57f0,0x5bc0,0x5be8,0x5f69,
+ 0x63a1,0x7826,0x7db5,0x83dc,0x8521,0x91c7,0x91f5,0x518a,0x67f5,0x7b56
+ },
+ { /* ku 74 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8cac,0x51c4,0x59bb,0x60bd,
+ 0x8655,0x501c,0xf9ff,0x5254,0x5c3a,0x617d,0x621a,0x62d3,0x64f2,0x65a5,
+ 0x6ecc,0x7620,0x810a,0x8e60,0x965f,0x96bb,0x4edf,0x5343,0x5598,0x5929,
+ 0x5ddd,0x64c5,0x6cc9,0x6dfa,0x7394,0x7a7f,0x821b,0x85a6,0x8ce4,0x8e10,
+ 0x9077,0x91e7,0x95e1,0x9621,0x97c6,0x51f8,0x54f2,0x5586,0x5fb9,0x64a4,
+ 0x6f88,0x7db4,0x8f1f,0x8f4d,0x9435,0x50c9,0x5c16,0x6cbe,0x6dfb,0x751b,
+ 0x77bb,0x7c3d,0x7c64,0x8a79,0x8ac2,0x581e,0x59be,0x5e16,0x6377,0x7252,
+ 0x758a,0x776b,0x8adc,0x8cbc,0x8f12,0x5ef3,0x6674,0x6df8,0x807d,0x83c1,
+ 0x8acb,0x9751,0x9bd6,0xfa00,0x5243,0x66ff,0x6d95,0x6eef,0x7de0,0x8ae6,
+ 0x902e,0x905e,0x9ad4,0x521d,0x527f,0x54e8,0x6194,0x6284,0x62db,0x68a2
+ },
+ { /* ku 75 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x6912,0x695a,0x6a35,0x7092,
+ 0x7126,0x785d,0x7901,0x790e,0x79d2,0x7a0d,0x8096,0x8278,0x82d5,0x8349,
+ 0x8549,0x8c82,0x8d85,0x9162,0x918b,0x91ae,0x4fc3,0x56d1,0x71ed,0x77d7,
+ 0x8700,0x89f8,0x5bf8,0x5fd6,0x6751,0x90a8,0x53e2,0x585a,0x5bf5,0x60a4,
+ 0x6181,0x6460,0x7e3d,0x8070,0x8525,0x9283,0x64ae,0x50ac,0x5d14,0x6700,
+ 0x589c,0x62bd,0x63a8,0x690e,0x6978,0x6a1e,0x6e6b,0x76ba,0x79cb,0x82bb,
+ 0x8429,0x8acf,0x8da8,0x8ffd,0x9112,0x914b,0x919c,0x9310,0x9318,0x939a,
+ 0x96db,0x9a36,0x9c0d,0x4e11,0x755c,0x795d,0x7afa,0x7b51,0x7bc9,0x7e2e,
+ 0x84c4,0x8e59,0x8e74,0x8ef8,0x9010,0x6625,0x693f,0x7443,0x51fa,0x672e,
+ 0x9edc,0x5145,0x5fe0,0x6c96,0x87f2,0x885d,0x8877,0x60b4,0x81b5,0x8403
+ },
+ { /* ku 76 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x8d05,0x53d6,0x5439,0x5634,
+ 0x5a36,0x5c31,0x708a,0x7fe0,0x805a,0x8106,0x81ed,0x8da3,0x9189,0x9a5f,
+ 0x9df2,0x5074,0x4ec4,0x53a0,0x60fb,0x6e2c,0x5c64,0x4f88,0x5024,0x55e4,
+ 0x5cd9,0x5e5f,0x6065,0x6894,0x6cbb,0x6dc4,0x71be,0x75d4,0x75f4,0x7661,
+ 0x7a1a,0x7a49,0x7dc7,0x7dfb,0x7f6e,0x81f4,0x86a9,0x8f1c,0x96c9,0x99b3,
+ 0x9f52,0x5247,0x52c5,0x98ed,0x89aa,0x4e03,0x67d2,0x6f06,0x4fb5,0x5be2,
+ 0x6795,0x6c88,0x6d78,0x741b,0x7827,0x91dd,0x937c,0x87c4,0x79e4,0x7a31,
+ 0x5feb,0x4ed6,0x54a4,0x553e,0x58ae,0x59a5,0x60f0,0x6253,0x62d6,0x6736,
+ 0x6955,0x8235,0x9640,0x99b1,0x99dd,0x502c,0x5353,0x5544,0x577c,0xfa01,
+ 0x6258,0xfa02,0x64e2,0x666b,0x67dd,0x6fc1,0x6fef,0x7422,0x7438,0x8a17
+ },
+ { /* ku 77 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x9438,0x5451,0x5606,0x5766,
+ 0x5f48,0x619a,0x6b4e,0x7058,0x70ad,0x7dbb,0x8a95,0x596a,0x812b,0x63a2,
+ 0x7708,0x803d,0x8caa,0x5854,0x642d,0x69bb,0x5b95,0x5e11,0x6e6f,0xfa03,
+ 0x8569,0x514c,0x53f0,0x592a,0x6020,0x614b,0x6b86,0x6c70,0x6cf0,0x7b1e,
+ 0x80ce,0x82d4,0x8dc6,0x90b0,0x98b1,0xfa04,0x64c7,0x6fa4,0x6491,0x6504,
+ 0x514e,0x5410,0x571f,0x8a0e,0x615f,0x6876,0xfa05,0x75db,0x7b52,0x7d71,
+ 0x901a,0x5806,0x69cc,0x817f,0x892a,0x9000,0x9839,0x5078,0x5957,0x59ac,
+ 0x6295,0x900f,0x9b2a,0x615d,0x7279,0x95d6,0x5761,0x5a46,0x5df4,0x628a,
+ 0x64ad,0x64fa,0x6777,0x6ce2,0x6d3e,0x722c,0x7436,0x7834,0x7f77,0x82ad,
+ 0x8ddb,0x9817,0x5224,0x5742,0x677f,0x7248,0x74e3,0x8ca9,0x8fa6,0x9211
+ },
+ { /* ku 78 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x962a,0x516b,0x53ed,0x634c,
+ 0x4f69,0x5504,0x6096,0x6557,0x6c9b,0x6d7f,0x724c,0x72fd,0x7a17,0x8987,
+ 0x8c9d,0x5f6d,0x6f8e,0x70f9,0x81a8,0x610e,0x4fbf,0x504f,0x6241,0x7247,
+ 0x7bc7,0x7de8,0x7fe9,0x904d,0x97ad,0x9a19,0x8cb6,0x576a,0x5e73,0x67b0,
+ 0x840d,0x8a55,0x5420,0x5b16,0x5e63,0x5ee2,0x5f0a,0x6583,0x80ba,0x853d,
+ 0x9589,0x965b,0x4f48,0x5305,0x530d,0x530f,0x5486,0x54fa,0x5703,0x5e03,
+ 0x6016,0x629b,0x62b1,0x6355,0xfa06,0x6ce1,0x6d66,0x75b1,0x7832,0x80de,
+ 0x812f,0x82de,0x8461,0x84b2,0x888d,0x8912,0x900b,0x92ea,0x98fd,0x9b91,
+ 0x5e45,0x66b4,0x66dd,0x7011,0x7206,0xfa07,0x4ff5,0x527d,0x5f6a,0x6153,
+ 0x6753,0x6a19,0x6f02,0x74e2,0x7968,0x8868,0x8c79,0x98c7,0x98c4,0x9a43
+ },
+ { /* ku 79 */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x54c1,0x7a1f,0x6953,0x8af7,
+ 0x8c4a,0x98a8,0x99ae,0x5f7c,0x62ab,0x75b2,0x76ae,0x88ab,0x907f,0x9642,
+ 0x5339,0x5f3c,0x5fc5,0x6ccc,0x73cc,0x7562,0x758b,0x7b46,0x82fe,0x999d,
+ 0x4e4f,0x903c,0x4e0b,0x4f55,0x53a6,0x590f,0x5ec8,0x6630,0x6cb3,0x7455,
+ 0x8377,0x8766,0x8cc0,0x9050,0x971e,0x9c15,0x58d1,0x5b78,0x8650,0x8b14,
+ 0x9db4,0x5bd2,0x6068,0x608d,0x65f1,0x6c57,0x6f22,0x6fa3,0x701a,0x7f55,
+ 0x7ff0,0x9591,0x9592,0x9650,0x97d3,0x5272,0x8f44,0x51fd,0x542b,0x54b8,
+ 0x5563,0x558a,0x6abb,0x6db5,0x7dd8,0x8266,0x929c,0x9677,0x9e79,0x5408,
+ 0x54c8,0x76d2,0x86e4,0x95a4,0x95d4,0x965c,0x4ea2,0x4f09,0x59ee,0x5ae6,
+ 0x5df7,0x6052,0x6297,0x676d,0x6841,0x6c86,0x6e2f,0x7f38,0x809b,0x822a
+ },
+ { /* ku 7a */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0xfa08,0xfa09,0x9805,0x4ea5,
+ 0x5055,0x54b3,0x5793,0x595a,0x5b69,0x5bb3,0x61c8,0x6977,0x6d77,0x7023,
+ 0x87f9,0x89e3,0x8a72,0x8ae7,0x9082,0x99ed,0x9ab8,0x52be,0x6838,0x5016,
+ 0x5e78,0x674f,0x8347,0x884c,0x4eab,0x5411,0x56ae,0x73e6,0x9115,0x97ff,
+ 0x9909,0x9957,0x9999,0x5653,0x589f,0x865b,0x8a31,0x61b2,0x6af6,0x737b,
+ 0x8ed2,0x6b47,0x96aa,0x9a57,0x5955,0x7200,0x8d6b,0x9769,0x4fd4,0x5cf4,
+ 0x5f26,0x61f8,0x665b,0x6ceb,0x70ab,0x7384,0x73b9,0x73fe,0x7729,0x774d,
+ 0x7d43,0x7d62,0x7e23,0x8237,0x8852,0xfa0a,0x8ce2,0x9249,0x986f,0x5b51,
+ 0x7a74,0x8840,0x9801,0x5acc,0x4fe0,0x5354,0x593e,0x5cfd,0x633e,0x6d79,
+ 0x72f9,0x8105,0x8107,0x83a2,0x92cf,0x9830,0x4ea8,0x5144,0x5211,0x578b
+ },
+ { /* ku 7b */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x5f62,0x6cc2,0x6ece,0x7005,
+ 0x7050,0x70af,0x7192,0x73e9,0x7469,0x834a,0x87a2,0x8861,0x9008,0x90a2,
+ 0x93a3,0x99a8,0x516e,0x5f57,0x60e0,0x6167,0x66b3,0x8559,0x8e4a,0x91af,
+ 0x978b,0x4e4e,0x4e92,0x547c,0x58d5,0x58fa,0x597d,0x5cb5,0x5f27,0x6236,
+ 0x6248,0x660a,0x6667,0x6beb,0x6d69,0x6dcf,0x6e56,0x6ef8,0x6f94,0x6fe0,
+ 0x6fe9,0x705d,0x72d0,0x7425,0x745a,0x74e0,0x7693,0x795c,0x7cca,0x7e1e,
+ 0x80e1,0x82a6,0x846b,0x84bf,0x864e,0x865f,0x8774,0x8b77,0x8c6a,0x93ac,
+ 0x9800,0x9865,0x60d1,0x6216,0x9177,0x5a5a,0x660f,0x6df7,0x6e3e,0x743f,
+ 0x9b42,0x5ffd,0x60da,0x7b0f,0x54c4,0x5f18,0x6c5e,0x6cd3,0x6d2a,0x70d8,
+ 0x7d05,0x8679,0x8a0c,0x9d3b,0x5316,0x548c,0x5b05,0x6a3a,0x706b,0x7575
+ },
+ { /* ku 7c */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x798d,0x79be,0x82b1,0x83ef,
+ 0x8a71,0x8b41,0x8ca8,0x9774,0xfa0b,0x64f4,0x652b,0x78ba,0x78bb,0x7a6b,
+ 0x4e38,0x559a,0x5950,0x5ba6,0x5e7b,0x60a3,0x63db,0x6b61,0x6665,0x6853,
+ 0x6e19,0x7165,0x74b0,0x7d08,0x9084,0x9a69,0x9c25,0x6d3b,0x6ed1,0x733e,
+ 0x8c41,0x95ca,0x51f0,0x5e4c,0x5fa8,0x604d,0x60f6,0x6130,0x614c,0x6643,
+ 0x6644,0x69a5,0x6cc1,0x6e5f,0x6ec9,0x6f62,0x714c,0x749c,0x7687,0x7bc1,
+ 0x7c27,0x8352,0x8757,0x9051,0x968d,0x9ec3,0x532f,0x56de,0x5efb,0x5f8a,
+ 0x6062,0x6094,0x61f7,0x6666,0x6703,0x6a9c,0x6dee,0x6fae,0x7070,0x736a,
+ 0x7e6a,0x81be,0x8334,0x86d4,0x8aa8,0x8cc4,0x5283,0x7372,0x5b96,0x6a6b,
+ 0x9404,0x54ee,0x5686,0x5b5d,0x6548,0x6585,0x66c9,0x689f,0x6d8d,0x6dc6
+ },
+ { /* ku 7d */
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,0x723b,0x80b4,0x9175,0x9a4d,
+ 0x4faf,0x5019,0x539a,0x540e,0x543c,0x5589,0x55c5,0x5e3f,0x5f8c,0x673d,
+ 0x7166,0x73dd,0x9005,0x52db,0x52f3,0x5864,0x58ce,0x7104,0x718f,0x71fb,
+ 0x85b0,0x8a13,0x6688,0x85a8,0x55a7,0x6684,0x714a,0x8431,0x5349,0x5599,
+ 0x6bc1,0x5f59,0x5fbd,0x63ee,0x6689,0x7147,0x8af1,0x8f1d,0x9ebe,0x4f11,
+ 0x643a,0x70cb,0x7566,0x8667,0x6064,0x8b4e,0x9df8,0x5147,0x51f6,0x5308,
+ 0x6d36,0x80f8,0x9ed1,0x6615,0x6b23,0x7098,0x75d5,0x5403,0x5c79,0x7d07,
+ 0x8a16,0x6b20,0x6b3d,0x6b46,0x5438,0x6070,0x6d3d,0x7fd5,0x8208,0x50d6,
+ 0x51de,0x559c,0x566b,0x56cd,0x59ec,0x5b09,0x5e0c,0x6199,0x6198,0x6231,
+ 0x665e,0x66e6,0x7199,0x71b9,0x71ba,0x72a7,0x79a7,0x7a00,0x7fb2,0x8a70
+ }
+};
diff --git a/imap/src/charset/tis_620.c b/imap/src/charset/tis_620.c
new file mode 100644
index 00000000..e1a9bcd8
--- /dev/null
+++ b/imap/src/charset/tis_620.c
@@ -0,0 +1,51 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: TIS 620-2529 conversion table
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 24 October 1997
+ * Last Edited: 30 August 2006
+ */
+
+/* TIS 620-2529 is the "Thai Industrial Standard for Thai Character Code
+ * for Computer", published by the Thai Industrial Standards Institute,
+ * Ministry of Industry of Thailand.
+ */
+
+static const unsigned short tis620tab[128] = {
+ 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,
+ 0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f,
+ 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,
+ 0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f,
+ UBOGON,0x0e01,0x0e02,0x0e03,0x0e04,0x0e05,0x0e06,0x0e07,
+ 0x0e08,0x0e09,0x0e0a,0x0e0b,0x0e0c,0x0e0d,0x0e0e,0x0e0f,
+ 0x0e10,0x0e11,0x0e12,0x0e13,0x0e14,0x0e15,0x0e16,0x0e17,
+ 0x0e18,0x0e19,0x0e1a,0x0e1b,0x0e1c,0x0e1d,0x0e1e,0x0e1f,
+ 0x0e20,0x0e21,0x0e22,0x0e23,0x0e24,0x0e25,0x0e26,0x0e27,
+ 0x0e28,0x0e29,0x0e2a,0x0e2b,0x0e2c,0x0e2d,0x0e2e,0x0e2f,
+ 0x0e30,0x0e31,0x0e32,0x0e33,0x0e34,0x0e35,0x0e36,0x0e37,
+ 0x0e38,0x0e39,0x0e3a,UBOGON,UBOGON,UBOGON,UBOGON,0x0e3f,
+ 0x0e40,0x0e41,0x0e42,0x0e43,0x0e44,0x0e45,0x0e46,0x0e47,
+ 0x0e48,0x0e49,0x0e4a,0x0e4b,0x0e4c,0x0e4d,0x0e4e,0x0e4f,
+ 0x0e50,0x0e51,0x0e52,0x0e53,0x0e54,0x0e55,0x0e56,0x0e57,
+ 0x0e58,0x0e59,0x0e5a,0x0e5b,UBOGON,UBOGON,UBOGON,UBOGON
+};
diff --git a/imap/src/charset/tmap.c b/imap/src/charset/tmap.c
new file mode 100644
index 00000000..7b523180
--- /dev/null
+++ b/imap/src/charset/tmap.c
@@ -0,0 +1,1487 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Unicode title case mapping table (current as of Unicode 5.0)
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 7 April 2006
+ * Last Edited: 6 December 2006
+ */
+
+
+#define UCS4_TMAPMAX 0x2d25 /* size of mapping table */
+
+#define UCS4_TMAPHIMIN 0xff41 /* high mapping minimum */
+#define UCS4_TMAPHIMAX 0xff5a /* high mapping maximum */
+#define UCS4_TMAPHIMAP 0x20 /* high mapping offset */
+
+ /* Values for Deseret */
+#define UCS4_TMAPDESERETMIN 0x10428
+#define UCS4_TMAPDESERETMAX 0x1044f
+#define UCS4_TMAPDESERETMAP 0x28
+
+static const unsigned short ucs4_tmaptab[UCS4_TMAPMAX+1] = {
+ 0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,
+ 0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f,
+ 0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,
+ 0x0018,0x0019,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f,
+ 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,
+ 0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f,
+ 0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,
+ 0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f,
+ 0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,
+ 0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,
+ 0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,
+ 0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f,
+ 0x0060,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,
+ 0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,
+ 0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,
+ 0x0058,0x0059,0x005a,0x007b,0x007c,0x007d,0x007e,0x007f,
+ 0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,
+ 0x0088,0x0089,0x008a,0x008b,0x008c,0x008d,0x008e,0x008f,
+ 0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,
+ 0x0098,0x0099,0x009a,0x009b,0x009c,0x009d,0x009e,0x009f,
+ 0x00a0,0x00a1,0x00a2,0x00a3,0x00a4,0x00a5,0x00a6,0x00a7,
+ 0x00a8,0x00a9,0x00aa,0x00ab,0x00ac,0x00ad,0x00ae,0x00af,
+ 0x00b0,0x00b1,0x00b2,0x00b3,0x00b4,0x039c,0x00b6,0x00b7,
+ 0x00b8,0x00b9,0x00ba,0x00bb,0x00bc,0x00bd,0x00be,0x00bf,
+ 0x00c0,0x00c1,0x00c2,0x00c3,0x00c4,0x00c5,0x00c6,0x00c7,
+ 0x00c8,0x00c9,0x00ca,0x00cb,0x00cc,0x00cd,0x00ce,0x00cf,
+ 0x00d0,0x00d1,0x00d2,0x00d3,0x00d4,0x00d5,0x00d6,0x00d7,
+ 0x00d8,0x00d9,0x00da,0x00db,0x00dc,0x00dd,0x00de,0x00df,
+ 0x00c0,0x00c1,0x00c2,0x00c3,0x00c4,0x00c5,0x00c6,0x00c7,
+ 0x00c8,0x00c9,0x00ca,0x00cb,0x00cc,0x00cd,0x00ce,0x00cf,
+ 0x00d0,0x00d1,0x00d2,0x00d3,0x00d4,0x00d5,0x00d6,0x00f7,
+ 0x00d8,0x00d9,0x00da,0x00db,0x00dc,0x00dd,0x00de,0x0178,
+ 0x0100,0x0100,0x0102,0x0102,0x0104,0x0104,0x0106,0x0106,
+ 0x0108,0x0108,0x010a,0x010a,0x010c,0x010c,0x010e,0x010e,
+ 0x0110,0x0110,0x0112,0x0112,0x0114,0x0114,0x0116,0x0116,
+ 0x0118,0x0118,0x011a,0x011a,0x011c,0x011c,0x011e,0x011e,
+ 0x0120,0x0120,0x0122,0x0122,0x0124,0x0124,0x0126,0x0126,
+ 0x0128,0x0128,0x012a,0x012a,0x012c,0x012c,0x012e,0x012e,
+ 0x0130,0x0049,0x0132,0x0132,0x0134,0x0134,0x0136,0x0136,
+ 0x0138,0x0139,0x0139,0x013b,0x013b,0x013d,0x013d,0x013f,
+ 0x013f,0x0141,0x0141,0x0143,0x0143,0x0145,0x0145,0x0147,
+ 0x0147,0x0149,0x014a,0x014a,0x014c,0x014c,0x014e,0x014e,
+ 0x0150,0x0150,0x0152,0x0152,0x0154,0x0154,0x0156,0x0156,
+ 0x0158,0x0158,0x015a,0x015a,0x015c,0x015c,0x015e,0x015e,
+ 0x0160,0x0160,0x0162,0x0162,0x0164,0x0164,0x0166,0x0166,
+ 0x0168,0x0168,0x016a,0x016a,0x016c,0x016c,0x016e,0x016e,
+ 0x0170,0x0170,0x0172,0x0172,0x0174,0x0174,0x0176,0x0176,
+ 0x0178,0x0179,0x0179,0x017b,0x017b,0x017d,0x017d,0x0053,
+ 0x0243,0x0181,0x0182,0x0182,0x0184,0x0184,0x0186,0x0187,
+ 0x0187,0x0189,0x018a,0x018b,0x018b,0x018d,0x018e,0x018f,
+ 0x0190,0x0191,0x0191,0x0193,0x0194,0x01f6,0x0196,0x0197,
+ 0x0198,0x0198,0x023d,0x019b,0x019c,0x019d,0x0220,0x019f,
+ 0x01a0,0x01a0,0x01a2,0x01a2,0x01a4,0x01a4,0x01a6,0x01a7,
+ 0x01a7,0x01a9,0x01aa,0x01ab,0x01ac,0x01ac,0x01ae,0x01af,
+ 0x01af,0x01b1,0x01b2,0x01b3,0x01b3,0x01b5,0x01b5,0x01b7,
+ 0x01b8,0x01b8,0x01ba,0x01bb,0x01bc,0x01bc,0x01be,0x01f7,
+ 0x01c0,0x01c1,0x01c2,0x01c3,0x01c5,0x01c5,0x01c5,0x01c8,
+ 0x01c8,0x01c8,0x01cb,0x01cb,0x01cb,0x01cd,0x01cd,0x01cf,
+ 0x01cf,0x01d1,0x01d1,0x01d3,0x01d3,0x01d5,0x01d5,0x01d7,
+ 0x01d7,0x01d9,0x01d9,0x01db,0x01db,0x018e,0x01de,0x01de,
+ 0x01e0,0x01e0,0x01e2,0x01e2,0x01e4,0x01e4,0x01e6,0x01e6,
+ 0x01e8,0x01e8,0x01ea,0x01ea,0x01ec,0x01ec,0x01ee,0x01ee,
+ 0x01f0,0x01f2,0x01f2,0x01f2,0x01f4,0x01f4,0x01f6,0x01f7,
+ 0x01f8,0x01f8,0x01fa,0x01fa,0x01fc,0x01fc,0x01fe,0x01fe,
+ 0x0200,0x0200,0x0202,0x0202,0x0204,0x0204,0x0206,0x0206,
+ 0x0208,0x0208,0x020a,0x020a,0x020c,0x020c,0x020e,0x020e,
+ 0x0210,0x0210,0x0212,0x0212,0x0214,0x0214,0x0216,0x0216,
+ 0x0218,0x0218,0x021a,0x021a,0x021c,0x021c,0x021e,0x021e,
+ 0x0220,0x0221,0x0222,0x0222,0x0224,0x0224,0x0226,0x0226,
+ 0x0228,0x0228,0x022a,0x022a,0x022c,0x022c,0x022e,0x022e,
+ 0x0230,0x0230,0x0232,0x0232,0x0234,0x0235,0x0236,0x0237,
+ 0x0238,0x0239,0x023a,0x023b,0x023b,0x023d,0x023e,0x023f,
+ 0x0240,0x0241,0x0241,0x0243,0x0244,0x0245,0x0246,0x0246,
+ 0x0248,0x0248,0x024a,0x024a,0x024c,0x024c,0x024e,0x024e,
+ 0x0250,0x0251,0x0252,0x0181,0x0186,0x0255,0x0189,0x018a,
+ 0x0258,0x018f,0x025a,0x0190,0x025c,0x025d,0x025e,0x025f,
+ 0x0193,0x0261,0x0262,0x0194,0x0264,0x0265,0x0266,0x0267,
+ 0x0197,0x0196,0x026a,0x2c62,0x026c,0x026d,0x026e,0x019c,
+ 0x0270,0x0271,0x019d,0x0273,0x0274,0x019f,0x0276,0x0277,
+ 0x0278,0x0279,0x027a,0x027b,0x027c,0x2c64,0x027e,0x027f,
+ 0x01a6,0x0281,0x0282,0x01a9,0x0284,0x0285,0x0286,0x0287,
+ 0x01ae,0x0244,0x01b1,0x01b2,0x0245,0x028d,0x028e,0x028f,
+ 0x0290,0x0291,0x01b7,0x0293,0x0294,0x0295,0x0296,0x0297,
+ 0x0298,0x0299,0x029a,0x029b,0x029c,0x029d,0x029e,0x029f,
+ 0x02a0,0x02a1,0x02a2,0x02a3,0x02a4,0x02a5,0x02a6,0x02a7,
+ 0x02a8,0x02a9,0x02aa,0x02ab,0x02ac,0x02ad,0x02ae,0x02af,
+ 0x02b0,0x02b1,0x02b2,0x02b3,0x02b4,0x02b5,0x02b6,0x02b7,
+ 0x02b8,0x02b9,0x02ba,0x02bb,0x02bc,0x02bd,0x02be,0x02bf,
+ 0x02c0,0x02c1,0x02c2,0x02c3,0x02c4,0x02c5,0x02c6,0x02c7,
+ 0x02c8,0x02c9,0x02ca,0x02cb,0x02cc,0x02cd,0x02ce,0x02cf,
+ 0x02d0,0x02d1,0x02d2,0x02d3,0x02d4,0x02d5,0x02d6,0x02d7,
+ 0x02d8,0x02d9,0x02da,0x02db,0x02dc,0x02dd,0x02de,0x02df,
+ 0x02e0,0x02e1,0x02e2,0x02e3,0x02e4,0x02e5,0x02e6,0x02e7,
+ 0x02e8,0x02e9,0x02ea,0x02eb,0x02ec,0x02ed,0x02ee,0x02ef,
+ 0x02f0,0x02f1,0x02f2,0x02f3,0x02f4,0x02f5,0x02f6,0x02f7,
+ 0x02f8,0x02f9,0x02fa,0x02fb,0x02fc,0x02fd,0x02fe,0x02ff,
+ 0x0300,0x0301,0x0302,0x0303,0x0304,0x0305,0x0306,0x0307,
+ 0x0308,0x0309,0x030a,0x030b,0x030c,0x030d,0x030e,0x030f,
+ 0x0310,0x0311,0x0312,0x0313,0x0314,0x0315,0x0316,0x0317,
+ 0x0318,0x0319,0x031a,0x031b,0x031c,0x031d,0x031e,0x031f,
+ 0x0320,0x0321,0x0322,0x0323,0x0324,0x0325,0x0326,0x0327,
+ 0x0328,0x0329,0x032a,0x032b,0x032c,0x032d,0x032e,0x032f,
+ 0x0330,0x0331,0x0332,0x0333,0x0334,0x0335,0x0336,0x0337,
+ 0x0338,0x0339,0x033a,0x033b,0x033c,0x033d,0x033e,0x033f,
+ 0x0340,0x0341,0x0342,0x0343,0x0344,0x0399,0x0346,0x0347,
+ 0x0348,0x0349,0x034a,0x034b,0x034c,0x034d,0x034e,0x034f,
+ 0x0350,0x0351,0x0352,0x0353,0x0354,0x0355,0x0356,0x0357,
+ 0x0358,0x0359,0x035a,0x035b,0x035c,0x035d,0x035e,0x035f,
+ 0x0360,0x0361,0x0362,0x0363,0x0364,0x0365,0x0366,0x0367,
+ 0x0368,0x0369,0x036a,0x036b,0x036c,0x036d,0x036e,0x036f,
+ 0x0370,0x0371,0x0372,0x0373,0x0374,0x0375,0x0376,0x0377,
+ 0x0378,0x0379,0x037a,0x03fd,0x03fe,0x03ff,0x037e,0x037f,
+ 0x0380,0x0381,0x0382,0x0383,0x0384,0x0385,0x0386,0x0387,
+ 0x0388,0x0389,0x038a,0x038b,0x038c,0x038d,0x038e,0x038f,
+ 0x0390,0x0391,0x0392,0x0393,0x0394,0x0395,0x0396,0x0397,
+ 0x0398,0x0399,0x039a,0x039b,0x039c,0x039d,0x039e,0x039f,
+ 0x03a0,0x03a1,0x03a2,0x03a3,0x03a4,0x03a5,0x03a6,0x03a7,
+ 0x03a8,0x03a9,0x03aa,0x03ab,0x0386,0x0388,0x0389,0x038a,
+ 0x03b0,0x0391,0x0392,0x0393,0x0394,0x0395,0x0396,0x0397,
+ 0x0398,0x0399,0x039a,0x039b,0x039c,0x039d,0x039e,0x039f,
+ 0x03a0,0x03a1,0x03a3,0x03a3,0x03a4,0x03a5,0x03a6,0x03a7,
+ 0x03a8,0x03a9,0x03aa,0x03ab,0x038c,0x038e,0x038f,0x03cf,
+ 0x0392,0x0398,0x03d2,0x03d3,0x03d4,0x03a6,0x03a0,0x03d7,
+ 0x03d8,0x03d8,0x03da,0x03da,0x03dc,0x03dc,0x03de,0x03de,
+ 0x03e0,0x03e0,0x03e2,0x03e2,0x03e4,0x03e4,0x03e6,0x03e6,
+ 0x03e8,0x03e8,0x03ea,0x03ea,0x03ec,0x03ec,0x03ee,0x03ee,
+ 0x039a,0x03a1,0x03f9,0x03f3,0x03f4,0x0395,0x03f6,0x03f7,
+ 0x03f7,0x03f9,0x03fa,0x03fa,0x03fc,0x03fd,0x03fe,0x03ff,
+ 0x0400,0x0401,0x0402,0x0403,0x0404,0x0405,0x0406,0x0407,
+ 0x0408,0x0409,0x040a,0x040b,0x040c,0x040d,0x040e,0x040f,
+ 0x0410,0x0411,0x0412,0x0413,0x0414,0x0415,0x0416,0x0417,
+ 0x0418,0x0419,0x041a,0x041b,0x041c,0x041d,0x041e,0x041f,
+ 0x0420,0x0421,0x0422,0x0423,0x0424,0x0425,0x0426,0x0427,
+ 0x0428,0x0429,0x042a,0x042b,0x042c,0x042d,0x042e,0x042f,
+ 0x0410,0x0411,0x0412,0x0413,0x0414,0x0415,0x0416,0x0417,
+ 0x0418,0x0419,0x041a,0x041b,0x041c,0x041d,0x041e,0x041f,
+ 0x0420,0x0421,0x0422,0x0423,0x0424,0x0425,0x0426,0x0427,
+ 0x0428,0x0429,0x042a,0x042b,0x042c,0x042d,0x042e,0x042f,
+ 0x0400,0x0401,0x0402,0x0403,0x0404,0x0405,0x0406,0x0407,
+ 0x0408,0x0409,0x040a,0x040b,0x040c,0x040d,0x040e,0x040f,
+ 0x0460,0x0460,0x0462,0x0462,0x0464,0x0464,0x0466,0x0466,
+ 0x0468,0x0468,0x046a,0x046a,0x046c,0x046c,0x046e,0x046e,
+ 0x0470,0x0470,0x0472,0x0472,0x0474,0x0474,0x0476,0x0476,
+ 0x0478,0x0478,0x047a,0x047a,0x047c,0x047c,0x047e,0x047e,
+ 0x0480,0x0480,0x0482,0x0483,0x0484,0x0485,0x0486,0x0487,
+ 0x0488,0x0489,0x048a,0x048a,0x048c,0x048c,0x048e,0x048e,
+ 0x0490,0x0490,0x0492,0x0492,0x0494,0x0494,0x0496,0x0496,
+ 0x0498,0x0498,0x049a,0x049a,0x049c,0x049c,0x049e,0x049e,
+ 0x04a0,0x04a0,0x04a2,0x04a2,0x04a4,0x04a4,0x04a6,0x04a6,
+ 0x04a8,0x04a8,0x04aa,0x04aa,0x04ac,0x04ac,0x04ae,0x04ae,
+ 0x04b0,0x04b0,0x04b2,0x04b2,0x04b4,0x04b4,0x04b6,0x04b6,
+ 0x04b8,0x04b8,0x04ba,0x04ba,0x04bc,0x04bc,0x04be,0x04be,
+ 0x04c0,0x04c1,0x04c1,0x04c3,0x04c3,0x04c5,0x04c5,0x04c7,
+ 0x04c7,0x04c9,0x04c9,0x04cb,0x04cb,0x04cd,0x04cd,0x04c0,
+ 0x04d0,0x04d0,0x04d2,0x04d2,0x04d4,0x04d4,0x04d6,0x04d6,
+ 0x04d8,0x04d8,0x04da,0x04da,0x04dc,0x04dc,0x04de,0x04de,
+ 0x04e0,0x04e0,0x04e2,0x04e2,0x04e4,0x04e4,0x04e6,0x04e6,
+ 0x04e8,0x04e8,0x04ea,0x04ea,0x04ec,0x04ec,0x04ee,0x04ee,
+ 0x04f0,0x04f0,0x04f2,0x04f2,0x04f4,0x04f4,0x04f6,0x04f6,
+ 0x04f8,0x04f8,0x04fa,0x04fa,0x04fc,0x04fc,0x04fe,0x04fe,
+ 0x0500,0x0500,0x0502,0x0502,0x0504,0x0504,0x0506,0x0506,
+ 0x0508,0x0508,0x050a,0x050a,0x050c,0x050c,0x050e,0x050e,
+ 0x0510,0x0510,0x0512,0x0512,0x0514,0x0515,0x0516,0x0517,
+ 0x0518,0x0519,0x051a,0x051b,0x051c,0x051d,0x051e,0x051f,
+ 0x0520,0x0521,0x0522,0x0523,0x0524,0x0525,0x0526,0x0527,
+ 0x0528,0x0529,0x052a,0x052b,0x052c,0x052d,0x052e,0x052f,
+ 0x0530,0x0531,0x0532,0x0533,0x0534,0x0535,0x0536,0x0537,
+ 0x0538,0x0539,0x053a,0x053b,0x053c,0x053d,0x053e,0x053f,
+ 0x0540,0x0541,0x0542,0x0543,0x0544,0x0545,0x0546,0x0547,
+ 0x0548,0x0549,0x054a,0x054b,0x054c,0x054d,0x054e,0x054f,
+ 0x0550,0x0551,0x0552,0x0553,0x0554,0x0555,0x0556,0x0557,
+ 0x0558,0x0559,0x055a,0x055b,0x055c,0x055d,0x055e,0x055f,
+ 0x0560,0x0531,0x0532,0x0533,0x0534,0x0535,0x0536,0x0537,
+ 0x0538,0x0539,0x053a,0x053b,0x053c,0x053d,0x053e,0x053f,
+ 0x0540,0x0541,0x0542,0x0543,0x0544,0x0545,0x0546,0x0547,
+ 0x0548,0x0549,0x054a,0x054b,0x054c,0x054d,0x054e,0x054f,
+ 0x0550,0x0551,0x0552,0x0553,0x0554,0x0555,0x0556,0x0587,
+ 0x0588,0x0589,0x058a,0x058b,0x058c,0x058d,0x058e,0x058f,
+ 0x0590,0x0591,0x0592,0x0593,0x0594,0x0595,0x0596,0x0597,
+ 0x0598,0x0599,0x059a,0x059b,0x059c,0x059d,0x059e,0x059f,
+ 0x05a0,0x05a1,0x05a2,0x05a3,0x05a4,0x05a5,0x05a6,0x05a7,
+ 0x05a8,0x05a9,0x05aa,0x05ab,0x05ac,0x05ad,0x05ae,0x05af,
+ 0x05b0,0x05b1,0x05b2,0x05b3,0x05b4,0x05b5,0x05b6,0x05b7,
+ 0x05b8,0x05b9,0x05ba,0x05bb,0x05bc,0x05bd,0x05be,0x05bf,
+ 0x05c0,0x05c1,0x05c2,0x05c3,0x05c4,0x05c5,0x05c6,0x05c7,
+ 0x05c8,0x05c9,0x05ca,0x05cb,0x05cc,0x05cd,0x05ce,0x05cf,
+ 0x05d0,0x05d1,0x05d2,0x05d3,0x05d4,0x05d5,0x05d6,0x05d7,
+ 0x05d8,0x05d9,0x05da,0x05db,0x05dc,0x05dd,0x05de,0x05df,
+ 0x05e0,0x05e1,0x05e2,0x05e3,0x05e4,0x05e5,0x05e6,0x05e7,
+ 0x05e8,0x05e9,0x05ea,0x05eb,0x05ec,0x05ed,0x05ee,0x05ef,
+ 0x05f0,0x05f1,0x05f2,0x05f3,0x05f4,0x05f5,0x05f6,0x05f7,
+ 0x05f8,0x05f9,0x05fa,0x05fb,0x05fc,0x05fd,0x05fe,0x05ff,
+ 0x0600,0x0601,0x0602,0x0603,0x0604,0x0605,0x0606,0x0607,
+ 0x0608,0x0609,0x060a,0x060b,0x060c,0x060d,0x060e,0x060f,
+ 0x0610,0x0611,0x0612,0x0613,0x0614,0x0615,0x0616,0x0617,
+ 0x0618,0x0619,0x061a,0x061b,0x061c,0x061d,0x061e,0x061f,
+ 0x0620,0x0621,0x0622,0x0623,0x0624,0x0625,0x0626,0x0627,
+ 0x0628,0x0629,0x062a,0x062b,0x062c,0x062d,0x062e,0x062f,
+ 0x0630,0x0631,0x0632,0x0633,0x0634,0x0635,0x0636,0x0637,
+ 0x0638,0x0639,0x063a,0x063b,0x063c,0x063d,0x063e,0x063f,
+ 0x0640,0x0641,0x0642,0x0643,0x0644,0x0645,0x0646,0x0647,
+ 0x0648,0x0649,0x064a,0x064b,0x064c,0x064d,0x064e,0x064f,
+ 0x0650,0x0651,0x0652,0x0653,0x0654,0x0655,0x0656,0x0657,
+ 0x0658,0x0659,0x065a,0x065b,0x065c,0x065d,0x065e,0x065f,
+ 0x0660,0x0661,0x0662,0x0663,0x0664,0x0665,0x0666,0x0667,
+ 0x0668,0x0669,0x066a,0x066b,0x066c,0x066d,0x066e,0x066f,
+ 0x0670,0x0671,0x0672,0x0673,0x0674,0x0675,0x0676,0x0677,
+ 0x0678,0x0679,0x067a,0x067b,0x067c,0x067d,0x067e,0x067f,
+ 0x0680,0x0681,0x0682,0x0683,0x0684,0x0685,0x0686,0x0687,
+ 0x0688,0x0689,0x068a,0x068b,0x068c,0x068d,0x068e,0x068f,
+ 0x0690,0x0691,0x0692,0x0693,0x0694,0x0695,0x0696,0x0697,
+ 0x0698,0x0699,0x069a,0x069b,0x069c,0x069d,0x069e,0x069f,
+ 0x06a0,0x06a1,0x06a2,0x06a3,0x06a4,0x06a5,0x06a6,0x06a7,
+ 0x06a8,0x06a9,0x06aa,0x06ab,0x06ac,0x06ad,0x06ae,0x06af,
+ 0x06b0,0x06b1,0x06b2,0x06b3,0x06b4,0x06b5,0x06b6,0x06b7,
+ 0x06b8,0x06b9,0x06ba,0x06bb,0x06bc,0x06bd,0x06be,0x06bf,
+ 0x06c0,0x06c1,0x06c2,0x06c3,0x06c4,0x06c5,0x06c6,0x06c7,
+ 0x06c8,0x06c9,0x06ca,0x06cb,0x06cc,0x06cd,0x06ce,0x06cf,
+ 0x06d0,0x06d1,0x06d2,0x06d3,0x06d4,0x06d5,0x06d6,0x06d7,
+ 0x06d8,0x06d9,0x06da,0x06db,0x06dc,0x06dd,0x06de,0x06df,
+ 0x06e0,0x06e1,0x06e2,0x06e3,0x06e4,0x06e5,0x06e6,0x06e7,
+ 0x06e8,0x06e9,0x06ea,0x06eb,0x06ec,0x06ed,0x06ee,0x06ef,
+ 0x06f0,0x06f1,0x06f2,0x06f3,0x06f4,0x06f5,0x06f6,0x06f7,
+ 0x06f8,0x06f9,0x06fa,0x06fb,0x06fc,0x06fd,0x06fe,0x06ff,
+ 0x0700,0x0701,0x0702,0x0703,0x0704,0x0705,0x0706,0x0707,
+ 0x0708,0x0709,0x070a,0x070b,0x070c,0x070d,0x070e,0x070f,
+ 0x0710,0x0711,0x0712,0x0713,0x0714,0x0715,0x0716,0x0717,
+ 0x0718,0x0719,0x071a,0x071b,0x071c,0x071d,0x071e,0x071f,
+ 0x0720,0x0721,0x0722,0x0723,0x0724,0x0725,0x0726,0x0727,
+ 0x0728,0x0729,0x072a,0x072b,0x072c,0x072d,0x072e,0x072f,
+ 0x0730,0x0731,0x0732,0x0733,0x0734,0x0735,0x0736,0x0737,
+ 0x0738,0x0739,0x073a,0x073b,0x073c,0x073d,0x073e,0x073f,
+ 0x0740,0x0741,0x0742,0x0743,0x0744,0x0745,0x0746,0x0747,
+ 0x0748,0x0749,0x074a,0x074b,0x074c,0x074d,0x074e,0x074f,
+ 0x0750,0x0751,0x0752,0x0753,0x0754,0x0755,0x0756,0x0757,
+ 0x0758,0x0759,0x075a,0x075b,0x075c,0x075d,0x075e,0x075f,
+ 0x0760,0x0761,0x0762,0x0763,0x0764,0x0765,0x0766,0x0767,
+ 0x0768,0x0769,0x076a,0x076b,0x076c,0x076d,0x076e,0x076f,
+ 0x0770,0x0771,0x0772,0x0773,0x0774,0x0775,0x0776,0x0777,
+ 0x0778,0x0779,0x077a,0x077b,0x077c,0x077d,0x077e,0x077f,
+ 0x0780,0x0781,0x0782,0x0783,0x0784,0x0785,0x0786,0x0787,
+ 0x0788,0x0789,0x078a,0x078b,0x078c,0x078d,0x078e,0x078f,
+ 0x0790,0x0791,0x0792,0x0793,0x0794,0x0795,0x0796,0x0797,
+ 0x0798,0x0799,0x079a,0x079b,0x079c,0x079d,0x079e,0x079f,
+ 0x07a0,0x07a1,0x07a2,0x07a3,0x07a4,0x07a5,0x07a6,0x07a7,
+ 0x07a8,0x07a9,0x07aa,0x07ab,0x07ac,0x07ad,0x07ae,0x07af,
+ 0x07b0,0x07b1,0x07b2,0x07b3,0x07b4,0x07b5,0x07b6,0x07b7,
+ 0x07b8,0x07b9,0x07ba,0x07bb,0x07bc,0x07bd,0x07be,0x07bf,
+ 0x07c0,0x07c1,0x07c2,0x07c3,0x07c4,0x07c5,0x07c6,0x07c7,
+ 0x07c8,0x07c9,0x07ca,0x07cb,0x07cc,0x07cd,0x07ce,0x07cf,
+ 0x07d0,0x07d1,0x07d2,0x07d3,0x07d4,0x07d5,0x07d6,0x07d7,
+ 0x07d8,0x07d9,0x07da,0x07db,0x07dc,0x07dd,0x07de,0x07df,
+ 0x07e0,0x07e1,0x07e2,0x07e3,0x07e4,0x07e5,0x07e6,0x07e7,
+ 0x07e8,0x07e9,0x07ea,0x07eb,0x07ec,0x07ed,0x07ee,0x07ef,
+ 0x07f0,0x07f1,0x07f2,0x07f3,0x07f4,0x07f5,0x07f6,0x07f7,
+ 0x07f8,0x07f9,0x07fa,0x07fb,0x07fc,0x07fd,0x07fe,0x07ff,
+ 0x0800,0x0801,0x0802,0x0803,0x0804,0x0805,0x0806,0x0807,
+ 0x0808,0x0809,0x080a,0x080b,0x080c,0x080d,0x080e,0x080f,
+ 0x0810,0x0811,0x0812,0x0813,0x0814,0x0815,0x0816,0x0817,
+ 0x0818,0x0819,0x081a,0x081b,0x081c,0x081d,0x081e,0x081f,
+ 0x0820,0x0821,0x0822,0x0823,0x0824,0x0825,0x0826,0x0827,
+ 0x0828,0x0829,0x082a,0x082b,0x082c,0x082d,0x082e,0x082f,
+ 0x0830,0x0831,0x0832,0x0833,0x0834,0x0835,0x0836,0x0837,
+ 0x0838,0x0839,0x083a,0x083b,0x083c,0x083d,0x083e,0x083f,
+ 0x0840,0x0841,0x0842,0x0843,0x0844,0x0845,0x0846,0x0847,
+ 0x0848,0x0849,0x084a,0x084b,0x084c,0x084d,0x084e,0x084f,
+ 0x0850,0x0851,0x0852,0x0853,0x0854,0x0855,0x0856,0x0857,
+ 0x0858,0x0859,0x085a,0x085b,0x085c,0x085d,0x085e,0x085f,
+ 0x0860,0x0861,0x0862,0x0863,0x0864,0x0865,0x0866,0x0867,
+ 0x0868,0x0869,0x086a,0x086b,0x086c,0x086d,0x086e,0x086f,
+ 0x0870,0x0871,0x0872,0x0873,0x0874,0x0875,0x0876,0x0877,
+ 0x0878,0x0879,0x087a,0x087b,0x087c,0x087d,0x087e,0x087f,
+ 0x0880,0x0881,0x0882,0x0883,0x0884,0x0885,0x0886,0x0887,
+ 0x0888,0x0889,0x088a,0x088b,0x088c,0x088d,0x088e,0x088f,
+ 0x0890,0x0891,0x0892,0x0893,0x0894,0x0895,0x0896,0x0897,
+ 0x0898,0x0899,0x089a,0x089b,0x089c,0x089d,0x089e,0x089f,
+ 0x08a0,0x08a1,0x08a2,0x08a3,0x08a4,0x08a5,0x08a6,0x08a7,
+ 0x08a8,0x08a9,0x08aa,0x08ab,0x08ac,0x08ad,0x08ae,0x08af,
+ 0x08b0,0x08b1,0x08b2,0x08b3,0x08b4,0x08b5,0x08b6,0x08b7,
+ 0x08b8,0x08b9,0x08ba,0x08bb,0x08bc,0x08bd,0x08be,0x08bf,
+ 0x08c0,0x08c1,0x08c2,0x08c3,0x08c4,0x08c5,0x08c6,0x08c7,
+ 0x08c8,0x08c9,0x08ca,0x08cb,0x08cc,0x08cd,0x08ce,0x08cf,
+ 0x08d0,0x08d1,0x08d2,0x08d3,0x08d4,0x08d5,0x08d6,0x08d7,
+ 0x08d8,0x08d9,0x08da,0x08db,0x08dc,0x08dd,0x08de,0x08df,
+ 0x08e0,0x08e1,0x08e2,0x08e3,0x08e4,0x08e5,0x08e6,0x08e7,
+ 0x08e8,0x08e9,0x08ea,0x08eb,0x08ec,0x08ed,0x08ee,0x08ef,
+ 0x08f0,0x08f1,0x08f2,0x08f3,0x08f4,0x08f5,0x08f6,0x08f7,
+ 0x08f8,0x08f9,0x08fa,0x08fb,0x08fc,0x08fd,0x08fe,0x08ff,
+ 0x0900,0x0901,0x0902,0x0903,0x0904,0x0905,0x0906,0x0907,
+ 0x0908,0x0909,0x090a,0x090b,0x090c,0x090d,0x090e,0x090f,
+ 0x0910,0x0911,0x0912,0x0913,0x0914,0x0915,0x0916,0x0917,
+ 0x0918,0x0919,0x091a,0x091b,0x091c,0x091d,0x091e,0x091f,
+ 0x0920,0x0921,0x0922,0x0923,0x0924,0x0925,0x0926,0x0927,
+ 0x0928,0x0929,0x092a,0x092b,0x092c,0x092d,0x092e,0x092f,
+ 0x0930,0x0931,0x0932,0x0933,0x0934,0x0935,0x0936,0x0937,
+ 0x0938,0x0939,0x093a,0x093b,0x093c,0x093d,0x093e,0x093f,
+ 0x0940,0x0941,0x0942,0x0943,0x0944,0x0945,0x0946,0x0947,
+ 0x0948,0x0949,0x094a,0x094b,0x094c,0x094d,0x094e,0x094f,
+ 0x0950,0x0951,0x0952,0x0953,0x0954,0x0955,0x0956,0x0957,
+ 0x0958,0x0959,0x095a,0x095b,0x095c,0x095d,0x095e,0x095f,
+ 0x0960,0x0961,0x0962,0x0963,0x0964,0x0965,0x0966,0x0967,
+ 0x0968,0x0969,0x096a,0x096b,0x096c,0x096d,0x096e,0x096f,
+ 0x0970,0x0971,0x0972,0x0973,0x0974,0x0975,0x0976,0x0977,
+ 0x0978,0x0979,0x097a,0x097b,0x097c,0x097d,0x097e,0x097f,
+ 0x0980,0x0981,0x0982,0x0983,0x0984,0x0985,0x0986,0x0987,
+ 0x0988,0x0989,0x098a,0x098b,0x098c,0x098d,0x098e,0x098f,
+ 0x0990,0x0991,0x0992,0x0993,0x0994,0x0995,0x0996,0x0997,
+ 0x0998,0x0999,0x099a,0x099b,0x099c,0x099d,0x099e,0x099f,
+ 0x09a0,0x09a1,0x09a2,0x09a3,0x09a4,0x09a5,0x09a6,0x09a7,
+ 0x09a8,0x09a9,0x09aa,0x09ab,0x09ac,0x09ad,0x09ae,0x09af,
+ 0x09b0,0x09b1,0x09b2,0x09b3,0x09b4,0x09b5,0x09b6,0x09b7,
+ 0x09b8,0x09b9,0x09ba,0x09bb,0x09bc,0x09bd,0x09be,0x09bf,
+ 0x09c0,0x09c1,0x09c2,0x09c3,0x09c4,0x09c5,0x09c6,0x09c7,
+ 0x09c8,0x09c9,0x09ca,0x09cb,0x09cc,0x09cd,0x09ce,0x09cf,
+ 0x09d0,0x09d1,0x09d2,0x09d3,0x09d4,0x09d5,0x09d6,0x09d7,
+ 0x09d8,0x09d9,0x09da,0x09db,0x09dc,0x09dd,0x09de,0x09df,
+ 0x09e0,0x09e1,0x09e2,0x09e3,0x09e4,0x09e5,0x09e6,0x09e7,
+ 0x09e8,0x09e9,0x09ea,0x09eb,0x09ec,0x09ed,0x09ee,0x09ef,
+ 0x09f0,0x09f1,0x09f2,0x09f3,0x09f4,0x09f5,0x09f6,0x09f7,
+ 0x09f8,0x09f9,0x09fa,0x09fb,0x09fc,0x09fd,0x09fe,0x09ff,
+ 0x0a00,0x0a01,0x0a02,0x0a03,0x0a04,0x0a05,0x0a06,0x0a07,
+ 0x0a08,0x0a09,0x0a0a,0x0a0b,0x0a0c,0x0a0d,0x0a0e,0x0a0f,
+ 0x0a10,0x0a11,0x0a12,0x0a13,0x0a14,0x0a15,0x0a16,0x0a17,
+ 0x0a18,0x0a19,0x0a1a,0x0a1b,0x0a1c,0x0a1d,0x0a1e,0x0a1f,
+ 0x0a20,0x0a21,0x0a22,0x0a23,0x0a24,0x0a25,0x0a26,0x0a27,
+ 0x0a28,0x0a29,0x0a2a,0x0a2b,0x0a2c,0x0a2d,0x0a2e,0x0a2f,
+ 0x0a30,0x0a31,0x0a32,0x0a33,0x0a34,0x0a35,0x0a36,0x0a37,
+ 0x0a38,0x0a39,0x0a3a,0x0a3b,0x0a3c,0x0a3d,0x0a3e,0x0a3f,
+ 0x0a40,0x0a41,0x0a42,0x0a43,0x0a44,0x0a45,0x0a46,0x0a47,
+ 0x0a48,0x0a49,0x0a4a,0x0a4b,0x0a4c,0x0a4d,0x0a4e,0x0a4f,
+ 0x0a50,0x0a51,0x0a52,0x0a53,0x0a54,0x0a55,0x0a56,0x0a57,
+ 0x0a58,0x0a59,0x0a5a,0x0a5b,0x0a5c,0x0a5d,0x0a5e,0x0a5f,
+ 0x0a60,0x0a61,0x0a62,0x0a63,0x0a64,0x0a65,0x0a66,0x0a67,
+ 0x0a68,0x0a69,0x0a6a,0x0a6b,0x0a6c,0x0a6d,0x0a6e,0x0a6f,
+ 0x0a70,0x0a71,0x0a72,0x0a73,0x0a74,0x0a75,0x0a76,0x0a77,
+ 0x0a78,0x0a79,0x0a7a,0x0a7b,0x0a7c,0x0a7d,0x0a7e,0x0a7f,
+ 0x0a80,0x0a81,0x0a82,0x0a83,0x0a84,0x0a85,0x0a86,0x0a87,
+ 0x0a88,0x0a89,0x0a8a,0x0a8b,0x0a8c,0x0a8d,0x0a8e,0x0a8f,
+ 0x0a90,0x0a91,0x0a92,0x0a93,0x0a94,0x0a95,0x0a96,0x0a97,
+ 0x0a98,0x0a99,0x0a9a,0x0a9b,0x0a9c,0x0a9d,0x0a9e,0x0a9f,
+ 0x0aa0,0x0aa1,0x0aa2,0x0aa3,0x0aa4,0x0aa5,0x0aa6,0x0aa7,
+ 0x0aa8,0x0aa9,0x0aaa,0x0aab,0x0aac,0x0aad,0x0aae,0x0aaf,
+ 0x0ab0,0x0ab1,0x0ab2,0x0ab3,0x0ab4,0x0ab5,0x0ab6,0x0ab7,
+ 0x0ab8,0x0ab9,0x0aba,0x0abb,0x0abc,0x0abd,0x0abe,0x0abf,
+ 0x0ac0,0x0ac1,0x0ac2,0x0ac3,0x0ac4,0x0ac5,0x0ac6,0x0ac7,
+ 0x0ac8,0x0ac9,0x0aca,0x0acb,0x0acc,0x0acd,0x0ace,0x0acf,
+ 0x0ad0,0x0ad1,0x0ad2,0x0ad3,0x0ad4,0x0ad5,0x0ad6,0x0ad7,
+ 0x0ad8,0x0ad9,0x0ada,0x0adb,0x0adc,0x0add,0x0ade,0x0adf,
+ 0x0ae0,0x0ae1,0x0ae2,0x0ae3,0x0ae4,0x0ae5,0x0ae6,0x0ae7,
+ 0x0ae8,0x0ae9,0x0aea,0x0aeb,0x0aec,0x0aed,0x0aee,0x0aef,
+ 0x0af0,0x0af1,0x0af2,0x0af3,0x0af4,0x0af5,0x0af6,0x0af7,
+ 0x0af8,0x0af9,0x0afa,0x0afb,0x0afc,0x0afd,0x0afe,0x0aff,
+ 0x0b00,0x0b01,0x0b02,0x0b03,0x0b04,0x0b05,0x0b06,0x0b07,
+ 0x0b08,0x0b09,0x0b0a,0x0b0b,0x0b0c,0x0b0d,0x0b0e,0x0b0f,
+ 0x0b10,0x0b11,0x0b12,0x0b13,0x0b14,0x0b15,0x0b16,0x0b17,
+ 0x0b18,0x0b19,0x0b1a,0x0b1b,0x0b1c,0x0b1d,0x0b1e,0x0b1f,
+ 0x0b20,0x0b21,0x0b22,0x0b23,0x0b24,0x0b25,0x0b26,0x0b27,
+ 0x0b28,0x0b29,0x0b2a,0x0b2b,0x0b2c,0x0b2d,0x0b2e,0x0b2f,
+ 0x0b30,0x0b31,0x0b32,0x0b33,0x0b34,0x0b35,0x0b36,0x0b37,
+ 0x0b38,0x0b39,0x0b3a,0x0b3b,0x0b3c,0x0b3d,0x0b3e,0x0b3f,
+ 0x0b40,0x0b41,0x0b42,0x0b43,0x0b44,0x0b45,0x0b46,0x0b47,
+ 0x0b48,0x0b49,0x0b4a,0x0b4b,0x0b4c,0x0b4d,0x0b4e,0x0b4f,
+ 0x0b50,0x0b51,0x0b52,0x0b53,0x0b54,0x0b55,0x0b56,0x0b57,
+ 0x0b58,0x0b59,0x0b5a,0x0b5b,0x0b5c,0x0b5d,0x0b5e,0x0b5f,
+ 0x0b60,0x0b61,0x0b62,0x0b63,0x0b64,0x0b65,0x0b66,0x0b67,
+ 0x0b68,0x0b69,0x0b6a,0x0b6b,0x0b6c,0x0b6d,0x0b6e,0x0b6f,
+ 0x0b70,0x0b71,0x0b72,0x0b73,0x0b74,0x0b75,0x0b76,0x0b77,
+ 0x0b78,0x0b79,0x0b7a,0x0b7b,0x0b7c,0x0b7d,0x0b7e,0x0b7f,
+ 0x0b80,0x0b81,0x0b82,0x0b83,0x0b84,0x0b85,0x0b86,0x0b87,
+ 0x0b88,0x0b89,0x0b8a,0x0b8b,0x0b8c,0x0b8d,0x0b8e,0x0b8f,
+ 0x0b90,0x0b91,0x0b92,0x0b93,0x0b94,0x0b95,0x0b96,0x0b97,
+ 0x0b98,0x0b99,0x0b9a,0x0b9b,0x0b9c,0x0b9d,0x0b9e,0x0b9f,
+ 0x0ba0,0x0ba1,0x0ba2,0x0ba3,0x0ba4,0x0ba5,0x0ba6,0x0ba7,
+ 0x0ba8,0x0ba9,0x0baa,0x0bab,0x0bac,0x0bad,0x0bae,0x0baf,
+ 0x0bb0,0x0bb1,0x0bb2,0x0bb3,0x0bb4,0x0bb5,0x0bb6,0x0bb7,
+ 0x0bb8,0x0bb9,0x0bba,0x0bbb,0x0bbc,0x0bbd,0x0bbe,0x0bbf,
+ 0x0bc0,0x0bc1,0x0bc2,0x0bc3,0x0bc4,0x0bc5,0x0bc6,0x0bc7,
+ 0x0bc8,0x0bc9,0x0bca,0x0bcb,0x0bcc,0x0bcd,0x0bce,0x0bcf,
+ 0x0bd0,0x0bd1,0x0bd2,0x0bd3,0x0bd4,0x0bd5,0x0bd6,0x0bd7,
+ 0x0bd8,0x0bd9,0x0bda,0x0bdb,0x0bdc,0x0bdd,0x0bde,0x0bdf,
+ 0x0be0,0x0be1,0x0be2,0x0be3,0x0be4,0x0be5,0x0be6,0x0be7,
+ 0x0be8,0x0be9,0x0bea,0x0beb,0x0bec,0x0bed,0x0bee,0x0bef,
+ 0x0bf0,0x0bf1,0x0bf2,0x0bf3,0x0bf4,0x0bf5,0x0bf6,0x0bf7,
+ 0x0bf8,0x0bf9,0x0bfa,0x0bfb,0x0bfc,0x0bfd,0x0bfe,0x0bff,
+ 0x0c00,0x0c01,0x0c02,0x0c03,0x0c04,0x0c05,0x0c06,0x0c07,
+ 0x0c08,0x0c09,0x0c0a,0x0c0b,0x0c0c,0x0c0d,0x0c0e,0x0c0f,
+ 0x0c10,0x0c11,0x0c12,0x0c13,0x0c14,0x0c15,0x0c16,0x0c17,
+ 0x0c18,0x0c19,0x0c1a,0x0c1b,0x0c1c,0x0c1d,0x0c1e,0x0c1f,
+ 0x0c20,0x0c21,0x0c22,0x0c23,0x0c24,0x0c25,0x0c26,0x0c27,
+ 0x0c28,0x0c29,0x0c2a,0x0c2b,0x0c2c,0x0c2d,0x0c2e,0x0c2f,
+ 0x0c30,0x0c31,0x0c32,0x0c33,0x0c34,0x0c35,0x0c36,0x0c37,
+ 0x0c38,0x0c39,0x0c3a,0x0c3b,0x0c3c,0x0c3d,0x0c3e,0x0c3f,
+ 0x0c40,0x0c41,0x0c42,0x0c43,0x0c44,0x0c45,0x0c46,0x0c47,
+ 0x0c48,0x0c49,0x0c4a,0x0c4b,0x0c4c,0x0c4d,0x0c4e,0x0c4f,
+ 0x0c50,0x0c51,0x0c52,0x0c53,0x0c54,0x0c55,0x0c56,0x0c57,
+ 0x0c58,0x0c59,0x0c5a,0x0c5b,0x0c5c,0x0c5d,0x0c5e,0x0c5f,
+ 0x0c60,0x0c61,0x0c62,0x0c63,0x0c64,0x0c65,0x0c66,0x0c67,
+ 0x0c68,0x0c69,0x0c6a,0x0c6b,0x0c6c,0x0c6d,0x0c6e,0x0c6f,
+ 0x0c70,0x0c71,0x0c72,0x0c73,0x0c74,0x0c75,0x0c76,0x0c77,
+ 0x0c78,0x0c79,0x0c7a,0x0c7b,0x0c7c,0x0c7d,0x0c7e,0x0c7f,
+ 0x0c80,0x0c81,0x0c82,0x0c83,0x0c84,0x0c85,0x0c86,0x0c87,
+ 0x0c88,0x0c89,0x0c8a,0x0c8b,0x0c8c,0x0c8d,0x0c8e,0x0c8f,
+ 0x0c90,0x0c91,0x0c92,0x0c93,0x0c94,0x0c95,0x0c96,0x0c97,
+ 0x0c98,0x0c99,0x0c9a,0x0c9b,0x0c9c,0x0c9d,0x0c9e,0x0c9f,
+ 0x0ca0,0x0ca1,0x0ca2,0x0ca3,0x0ca4,0x0ca5,0x0ca6,0x0ca7,
+ 0x0ca8,0x0ca9,0x0caa,0x0cab,0x0cac,0x0cad,0x0cae,0x0caf,
+ 0x0cb0,0x0cb1,0x0cb2,0x0cb3,0x0cb4,0x0cb5,0x0cb6,0x0cb7,
+ 0x0cb8,0x0cb9,0x0cba,0x0cbb,0x0cbc,0x0cbd,0x0cbe,0x0cbf,
+ 0x0cc0,0x0cc1,0x0cc2,0x0cc3,0x0cc4,0x0cc5,0x0cc6,0x0cc7,
+ 0x0cc8,0x0cc9,0x0cca,0x0ccb,0x0ccc,0x0ccd,0x0cce,0x0ccf,
+ 0x0cd0,0x0cd1,0x0cd2,0x0cd3,0x0cd4,0x0cd5,0x0cd6,0x0cd7,
+ 0x0cd8,0x0cd9,0x0cda,0x0cdb,0x0cdc,0x0cdd,0x0cde,0x0cdf,
+ 0x0ce0,0x0ce1,0x0ce2,0x0ce3,0x0ce4,0x0ce5,0x0ce6,0x0ce7,
+ 0x0ce8,0x0ce9,0x0cea,0x0ceb,0x0cec,0x0ced,0x0cee,0x0cef,
+ 0x0cf0,0x0cf1,0x0cf2,0x0cf3,0x0cf4,0x0cf5,0x0cf6,0x0cf7,
+ 0x0cf8,0x0cf9,0x0cfa,0x0cfb,0x0cfc,0x0cfd,0x0cfe,0x0cff,
+ 0x0d00,0x0d01,0x0d02,0x0d03,0x0d04,0x0d05,0x0d06,0x0d07,
+ 0x0d08,0x0d09,0x0d0a,0x0d0b,0x0d0c,0x0d0d,0x0d0e,0x0d0f,
+ 0x0d10,0x0d11,0x0d12,0x0d13,0x0d14,0x0d15,0x0d16,0x0d17,
+ 0x0d18,0x0d19,0x0d1a,0x0d1b,0x0d1c,0x0d1d,0x0d1e,0x0d1f,
+ 0x0d20,0x0d21,0x0d22,0x0d23,0x0d24,0x0d25,0x0d26,0x0d27,
+ 0x0d28,0x0d29,0x0d2a,0x0d2b,0x0d2c,0x0d2d,0x0d2e,0x0d2f,
+ 0x0d30,0x0d31,0x0d32,0x0d33,0x0d34,0x0d35,0x0d36,0x0d37,
+ 0x0d38,0x0d39,0x0d3a,0x0d3b,0x0d3c,0x0d3d,0x0d3e,0x0d3f,
+ 0x0d40,0x0d41,0x0d42,0x0d43,0x0d44,0x0d45,0x0d46,0x0d47,
+ 0x0d48,0x0d49,0x0d4a,0x0d4b,0x0d4c,0x0d4d,0x0d4e,0x0d4f,
+ 0x0d50,0x0d51,0x0d52,0x0d53,0x0d54,0x0d55,0x0d56,0x0d57,
+ 0x0d58,0x0d59,0x0d5a,0x0d5b,0x0d5c,0x0d5d,0x0d5e,0x0d5f,
+ 0x0d60,0x0d61,0x0d62,0x0d63,0x0d64,0x0d65,0x0d66,0x0d67,
+ 0x0d68,0x0d69,0x0d6a,0x0d6b,0x0d6c,0x0d6d,0x0d6e,0x0d6f,
+ 0x0d70,0x0d71,0x0d72,0x0d73,0x0d74,0x0d75,0x0d76,0x0d77,
+ 0x0d78,0x0d79,0x0d7a,0x0d7b,0x0d7c,0x0d7d,0x0d7e,0x0d7f,
+ 0x0d80,0x0d81,0x0d82,0x0d83,0x0d84,0x0d85,0x0d86,0x0d87,
+ 0x0d88,0x0d89,0x0d8a,0x0d8b,0x0d8c,0x0d8d,0x0d8e,0x0d8f,
+ 0x0d90,0x0d91,0x0d92,0x0d93,0x0d94,0x0d95,0x0d96,0x0d97,
+ 0x0d98,0x0d99,0x0d9a,0x0d9b,0x0d9c,0x0d9d,0x0d9e,0x0d9f,
+ 0x0da0,0x0da1,0x0da2,0x0da3,0x0da4,0x0da5,0x0da6,0x0da7,
+ 0x0da8,0x0da9,0x0daa,0x0dab,0x0dac,0x0dad,0x0dae,0x0daf,
+ 0x0db0,0x0db1,0x0db2,0x0db3,0x0db4,0x0db5,0x0db6,0x0db7,
+ 0x0db8,0x0db9,0x0dba,0x0dbb,0x0dbc,0x0dbd,0x0dbe,0x0dbf,
+ 0x0dc0,0x0dc1,0x0dc2,0x0dc3,0x0dc4,0x0dc5,0x0dc6,0x0dc7,
+ 0x0dc8,0x0dc9,0x0dca,0x0dcb,0x0dcc,0x0dcd,0x0dce,0x0dcf,
+ 0x0dd0,0x0dd1,0x0dd2,0x0dd3,0x0dd4,0x0dd5,0x0dd6,0x0dd7,
+ 0x0dd8,0x0dd9,0x0dda,0x0ddb,0x0ddc,0x0ddd,0x0dde,0x0ddf,
+ 0x0de0,0x0de1,0x0de2,0x0de3,0x0de4,0x0de5,0x0de6,0x0de7,
+ 0x0de8,0x0de9,0x0dea,0x0deb,0x0dec,0x0ded,0x0dee,0x0def,
+ 0x0df0,0x0df1,0x0df2,0x0df3,0x0df4,0x0df5,0x0df6,0x0df7,
+ 0x0df8,0x0df9,0x0dfa,0x0dfb,0x0dfc,0x0dfd,0x0dfe,0x0dff,
+ 0x0e00,0x0e01,0x0e02,0x0e03,0x0e04,0x0e05,0x0e06,0x0e07,
+ 0x0e08,0x0e09,0x0e0a,0x0e0b,0x0e0c,0x0e0d,0x0e0e,0x0e0f,
+ 0x0e10,0x0e11,0x0e12,0x0e13,0x0e14,0x0e15,0x0e16,0x0e17,
+ 0x0e18,0x0e19,0x0e1a,0x0e1b,0x0e1c,0x0e1d,0x0e1e,0x0e1f,
+ 0x0e20,0x0e21,0x0e22,0x0e23,0x0e24,0x0e25,0x0e26,0x0e27,
+ 0x0e28,0x0e29,0x0e2a,0x0e2b,0x0e2c,0x0e2d,0x0e2e,0x0e2f,
+ 0x0e30,0x0e31,0x0e32,0x0e33,0x0e34,0x0e35,0x0e36,0x0e37,
+ 0x0e38,0x0e39,0x0e3a,0x0e3b,0x0e3c,0x0e3d,0x0e3e,0x0e3f,
+ 0x0e40,0x0e41,0x0e42,0x0e43,0x0e44,0x0e45,0x0e46,0x0e47,
+ 0x0e48,0x0e49,0x0e4a,0x0e4b,0x0e4c,0x0e4d,0x0e4e,0x0e4f,
+ 0x0e50,0x0e51,0x0e52,0x0e53,0x0e54,0x0e55,0x0e56,0x0e57,
+ 0x0e58,0x0e59,0x0e5a,0x0e5b,0x0e5c,0x0e5d,0x0e5e,0x0e5f,
+ 0x0e60,0x0e61,0x0e62,0x0e63,0x0e64,0x0e65,0x0e66,0x0e67,
+ 0x0e68,0x0e69,0x0e6a,0x0e6b,0x0e6c,0x0e6d,0x0e6e,0x0e6f,
+ 0x0e70,0x0e71,0x0e72,0x0e73,0x0e74,0x0e75,0x0e76,0x0e77,
+ 0x0e78,0x0e79,0x0e7a,0x0e7b,0x0e7c,0x0e7d,0x0e7e,0x0e7f,
+ 0x0e80,0x0e81,0x0e82,0x0e83,0x0e84,0x0e85,0x0e86,0x0e87,
+ 0x0e88,0x0e89,0x0e8a,0x0e8b,0x0e8c,0x0e8d,0x0e8e,0x0e8f,
+ 0x0e90,0x0e91,0x0e92,0x0e93,0x0e94,0x0e95,0x0e96,0x0e97,
+ 0x0e98,0x0e99,0x0e9a,0x0e9b,0x0e9c,0x0e9d,0x0e9e,0x0e9f,
+ 0x0ea0,0x0ea1,0x0ea2,0x0ea3,0x0ea4,0x0ea5,0x0ea6,0x0ea7,
+ 0x0ea8,0x0ea9,0x0eaa,0x0eab,0x0eac,0x0ead,0x0eae,0x0eaf,
+ 0x0eb0,0x0eb1,0x0eb2,0x0eb3,0x0eb4,0x0eb5,0x0eb6,0x0eb7,
+ 0x0eb8,0x0eb9,0x0eba,0x0ebb,0x0ebc,0x0ebd,0x0ebe,0x0ebf,
+ 0x0ec0,0x0ec1,0x0ec2,0x0ec3,0x0ec4,0x0ec5,0x0ec6,0x0ec7,
+ 0x0ec8,0x0ec9,0x0eca,0x0ecb,0x0ecc,0x0ecd,0x0ece,0x0ecf,
+ 0x0ed0,0x0ed1,0x0ed2,0x0ed3,0x0ed4,0x0ed5,0x0ed6,0x0ed7,
+ 0x0ed8,0x0ed9,0x0eda,0x0edb,0x0edc,0x0edd,0x0ede,0x0edf,
+ 0x0ee0,0x0ee1,0x0ee2,0x0ee3,0x0ee4,0x0ee5,0x0ee6,0x0ee7,
+ 0x0ee8,0x0ee9,0x0eea,0x0eeb,0x0eec,0x0eed,0x0eee,0x0eef,
+ 0x0ef0,0x0ef1,0x0ef2,0x0ef3,0x0ef4,0x0ef5,0x0ef6,0x0ef7,
+ 0x0ef8,0x0ef9,0x0efa,0x0efb,0x0efc,0x0efd,0x0efe,0x0eff,
+ 0x0f00,0x0f01,0x0f02,0x0f03,0x0f04,0x0f05,0x0f06,0x0f07,
+ 0x0f08,0x0f09,0x0f0a,0x0f0b,0x0f0c,0x0f0d,0x0f0e,0x0f0f,
+ 0x0f10,0x0f11,0x0f12,0x0f13,0x0f14,0x0f15,0x0f16,0x0f17,
+ 0x0f18,0x0f19,0x0f1a,0x0f1b,0x0f1c,0x0f1d,0x0f1e,0x0f1f,
+ 0x0f20,0x0f21,0x0f22,0x0f23,0x0f24,0x0f25,0x0f26,0x0f27,
+ 0x0f28,0x0f29,0x0f2a,0x0f2b,0x0f2c,0x0f2d,0x0f2e,0x0f2f,
+ 0x0f30,0x0f31,0x0f32,0x0f33,0x0f34,0x0f35,0x0f36,0x0f37,
+ 0x0f38,0x0f39,0x0f3a,0x0f3b,0x0f3c,0x0f3d,0x0f3e,0x0f3f,
+ 0x0f40,0x0f41,0x0f42,0x0f43,0x0f44,0x0f45,0x0f46,0x0f47,
+ 0x0f48,0x0f49,0x0f4a,0x0f4b,0x0f4c,0x0f4d,0x0f4e,0x0f4f,
+ 0x0f50,0x0f51,0x0f52,0x0f53,0x0f54,0x0f55,0x0f56,0x0f57,
+ 0x0f58,0x0f59,0x0f5a,0x0f5b,0x0f5c,0x0f5d,0x0f5e,0x0f5f,
+ 0x0f60,0x0f61,0x0f62,0x0f63,0x0f64,0x0f65,0x0f66,0x0f67,
+ 0x0f68,0x0f69,0x0f6a,0x0f6b,0x0f6c,0x0f6d,0x0f6e,0x0f6f,
+ 0x0f70,0x0f71,0x0f72,0x0f73,0x0f74,0x0f75,0x0f76,0x0f77,
+ 0x0f78,0x0f79,0x0f7a,0x0f7b,0x0f7c,0x0f7d,0x0f7e,0x0f7f,
+ 0x0f80,0x0f81,0x0f82,0x0f83,0x0f84,0x0f85,0x0f86,0x0f87,
+ 0x0f88,0x0f89,0x0f8a,0x0f8b,0x0f8c,0x0f8d,0x0f8e,0x0f8f,
+ 0x0f90,0x0f91,0x0f92,0x0f93,0x0f94,0x0f95,0x0f96,0x0f97,
+ 0x0f98,0x0f99,0x0f9a,0x0f9b,0x0f9c,0x0f9d,0x0f9e,0x0f9f,
+ 0x0fa0,0x0fa1,0x0fa2,0x0fa3,0x0fa4,0x0fa5,0x0fa6,0x0fa7,
+ 0x0fa8,0x0fa9,0x0faa,0x0fab,0x0fac,0x0fad,0x0fae,0x0faf,
+ 0x0fb0,0x0fb1,0x0fb2,0x0fb3,0x0fb4,0x0fb5,0x0fb6,0x0fb7,
+ 0x0fb8,0x0fb9,0x0fba,0x0fbb,0x0fbc,0x0fbd,0x0fbe,0x0fbf,
+ 0x0fc0,0x0fc1,0x0fc2,0x0fc3,0x0fc4,0x0fc5,0x0fc6,0x0fc7,
+ 0x0fc8,0x0fc9,0x0fca,0x0fcb,0x0fcc,0x0fcd,0x0fce,0x0fcf,
+ 0x0fd0,0x0fd1,0x0fd2,0x0fd3,0x0fd4,0x0fd5,0x0fd6,0x0fd7,
+ 0x0fd8,0x0fd9,0x0fda,0x0fdb,0x0fdc,0x0fdd,0x0fde,0x0fdf,
+ 0x0fe0,0x0fe1,0x0fe2,0x0fe3,0x0fe4,0x0fe5,0x0fe6,0x0fe7,
+ 0x0fe8,0x0fe9,0x0fea,0x0feb,0x0fec,0x0fed,0x0fee,0x0fef,
+ 0x0ff0,0x0ff1,0x0ff2,0x0ff3,0x0ff4,0x0ff5,0x0ff6,0x0ff7,
+ 0x0ff8,0x0ff9,0x0ffa,0x0ffb,0x0ffc,0x0ffd,0x0ffe,0x0fff,
+ 0x1000,0x1001,0x1002,0x1003,0x1004,0x1005,0x1006,0x1007,
+ 0x1008,0x1009,0x100a,0x100b,0x100c,0x100d,0x100e,0x100f,
+ 0x1010,0x1011,0x1012,0x1013,0x1014,0x1015,0x1016,0x1017,
+ 0x1018,0x1019,0x101a,0x101b,0x101c,0x101d,0x101e,0x101f,
+ 0x1020,0x1021,0x1022,0x1023,0x1024,0x1025,0x1026,0x1027,
+ 0x1028,0x1029,0x102a,0x102b,0x102c,0x102d,0x102e,0x102f,
+ 0x1030,0x1031,0x1032,0x1033,0x1034,0x1035,0x1036,0x1037,
+ 0x1038,0x1039,0x103a,0x103b,0x103c,0x103d,0x103e,0x103f,
+ 0x1040,0x1041,0x1042,0x1043,0x1044,0x1045,0x1046,0x1047,
+ 0x1048,0x1049,0x104a,0x104b,0x104c,0x104d,0x104e,0x104f,
+ 0x1050,0x1051,0x1052,0x1053,0x1054,0x1055,0x1056,0x1057,
+ 0x1058,0x1059,0x105a,0x105b,0x105c,0x105d,0x105e,0x105f,
+ 0x1060,0x1061,0x1062,0x1063,0x1064,0x1065,0x1066,0x1067,
+ 0x1068,0x1069,0x106a,0x106b,0x106c,0x106d,0x106e,0x106f,
+ 0x1070,0x1071,0x1072,0x1073,0x1074,0x1075,0x1076,0x1077,
+ 0x1078,0x1079,0x107a,0x107b,0x107c,0x107d,0x107e,0x107f,
+ 0x1080,0x1081,0x1082,0x1083,0x1084,0x1085,0x1086,0x1087,
+ 0x1088,0x1089,0x108a,0x108b,0x108c,0x108d,0x108e,0x108f,
+ 0x1090,0x1091,0x1092,0x1093,0x1094,0x1095,0x1096,0x1097,
+ 0x1098,0x1099,0x109a,0x109b,0x109c,0x109d,0x109e,0x109f,
+ 0x10a0,0x10a1,0x10a2,0x10a3,0x10a4,0x10a5,0x10a6,0x10a7,
+ 0x10a8,0x10a9,0x10aa,0x10ab,0x10ac,0x10ad,0x10ae,0x10af,
+ 0x10b0,0x10b1,0x10b2,0x10b3,0x10b4,0x10b5,0x10b6,0x10b7,
+ 0x10b8,0x10b9,0x10ba,0x10bb,0x10bc,0x10bd,0x10be,0x10bf,
+ 0x10c0,0x10c1,0x10c2,0x10c3,0x10c4,0x10c5,0x10c6,0x10c7,
+ 0x10c8,0x10c9,0x10ca,0x10cb,0x10cc,0x10cd,0x10ce,0x10cf,
+ 0x10d0,0x10d1,0x10d2,0x10d3,0x10d4,0x10d5,0x10d6,0x10d7,
+ 0x10d8,0x10d9,0x10da,0x10db,0x10dc,0x10dd,0x10de,0x10df,
+ 0x10e0,0x10e1,0x10e2,0x10e3,0x10e4,0x10e5,0x10e6,0x10e7,
+ 0x10e8,0x10e9,0x10ea,0x10eb,0x10ec,0x10ed,0x10ee,0x10ef,
+ 0x10f0,0x10f1,0x10f2,0x10f3,0x10f4,0x10f5,0x10f6,0x10f7,
+ 0x10f8,0x10f9,0x10fa,0x10fb,0x10fc,0x10fd,0x10fe,0x10ff,
+ 0x1100,0x1101,0x1102,0x1103,0x1104,0x1105,0x1106,0x1107,
+ 0x1108,0x1109,0x110a,0x110b,0x110c,0x110d,0x110e,0x110f,
+ 0x1110,0x1111,0x1112,0x1113,0x1114,0x1115,0x1116,0x1117,
+ 0x1118,0x1119,0x111a,0x111b,0x111c,0x111d,0x111e,0x111f,
+ 0x1120,0x1121,0x1122,0x1123,0x1124,0x1125,0x1126,0x1127,
+ 0x1128,0x1129,0x112a,0x112b,0x112c,0x112d,0x112e,0x112f,
+ 0x1130,0x1131,0x1132,0x1133,0x1134,0x1135,0x1136,0x1137,
+ 0x1138,0x1139,0x113a,0x113b,0x113c,0x113d,0x113e,0x113f,
+ 0x1140,0x1141,0x1142,0x1143,0x1144,0x1145,0x1146,0x1147,
+ 0x1148,0x1149,0x114a,0x114b,0x114c,0x114d,0x114e,0x114f,
+ 0x1150,0x1151,0x1152,0x1153,0x1154,0x1155,0x1156,0x1157,
+ 0x1158,0x1159,0x115a,0x115b,0x115c,0x115d,0x115e,0x115f,
+ 0x1160,0x1161,0x1162,0x1163,0x1164,0x1165,0x1166,0x1167,
+ 0x1168,0x1169,0x116a,0x116b,0x116c,0x116d,0x116e,0x116f,
+ 0x1170,0x1171,0x1172,0x1173,0x1174,0x1175,0x1176,0x1177,
+ 0x1178,0x1179,0x117a,0x117b,0x117c,0x117d,0x117e,0x117f,
+ 0x1180,0x1181,0x1182,0x1183,0x1184,0x1185,0x1186,0x1187,
+ 0x1188,0x1189,0x118a,0x118b,0x118c,0x118d,0x118e,0x118f,
+ 0x1190,0x1191,0x1192,0x1193,0x1194,0x1195,0x1196,0x1197,
+ 0x1198,0x1199,0x119a,0x119b,0x119c,0x119d,0x119e,0x119f,
+ 0x11a0,0x11a1,0x11a2,0x11a3,0x11a4,0x11a5,0x11a6,0x11a7,
+ 0x11a8,0x11a9,0x11aa,0x11ab,0x11ac,0x11ad,0x11ae,0x11af,
+ 0x11b0,0x11b1,0x11b2,0x11b3,0x11b4,0x11b5,0x11b6,0x11b7,
+ 0x11b8,0x11b9,0x11ba,0x11bb,0x11bc,0x11bd,0x11be,0x11bf,
+ 0x11c0,0x11c1,0x11c2,0x11c3,0x11c4,0x11c5,0x11c6,0x11c7,
+ 0x11c8,0x11c9,0x11ca,0x11cb,0x11cc,0x11cd,0x11ce,0x11cf,
+ 0x11d0,0x11d1,0x11d2,0x11d3,0x11d4,0x11d5,0x11d6,0x11d7,
+ 0x11d8,0x11d9,0x11da,0x11db,0x11dc,0x11dd,0x11de,0x11df,
+ 0x11e0,0x11e1,0x11e2,0x11e3,0x11e4,0x11e5,0x11e6,0x11e7,
+ 0x11e8,0x11e9,0x11ea,0x11eb,0x11ec,0x11ed,0x11ee,0x11ef,
+ 0x11f0,0x11f1,0x11f2,0x11f3,0x11f4,0x11f5,0x11f6,0x11f7,
+ 0x11f8,0x11f9,0x11fa,0x11fb,0x11fc,0x11fd,0x11fe,0x11ff,
+ 0x1200,0x1201,0x1202,0x1203,0x1204,0x1205,0x1206,0x1207,
+ 0x1208,0x1209,0x120a,0x120b,0x120c,0x120d,0x120e,0x120f,
+ 0x1210,0x1211,0x1212,0x1213,0x1214,0x1215,0x1216,0x1217,
+ 0x1218,0x1219,0x121a,0x121b,0x121c,0x121d,0x121e,0x121f,
+ 0x1220,0x1221,0x1222,0x1223,0x1224,0x1225,0x1226,0x1227,
+ 0x1228,0x1229,0x122a,0x122b,0x122c,0x122d,0x122e,0x122f,
+ 0x1230,0x1231,0x1232,0x1233,0x1234,0x1235,0x1236,0x1237,
+ 0x1238,0x1239,0x123a,0x123b,0x123c,0x123d,0x123e,0x123f,
+ 0x1240,0x1241,0x1242,0x1243,0x1244,0x1245,0x1246,0x1247,
+ 0x1248,0x1249,0x124a,0x124b,0x124c,0x124d,0x124e,0x124f,
+ 0x1250,0x1251,0x1252,0x1253,0x1254,0x1255,0x1256,0x1257,
+ 0x1258,0x1259,0x125a,0x125b,0x125c,0x125d,0x125e,0x125f,
+ 0x1260,0x1261,0x1262,0x1263,0x1264,0x1265,0x1266,0x1267,
+ 0x1268,0x1269,0x126a,0x126b,0x126c,0x126d,0x126e,0x126f,
+ 0x1270,0x1271,0x1272,0x1273,0x1274,0x1275,0x1276,0x1277,
+ 0x1278,0x1279,0x127a,0x127b,0x127c,0x127d,0x127e,0x127f,
+ 0x1280,0x1281,0x1282,0x1283,0x1284,0x1285,0x1286,0x1287,
+ 0x1288,0x1289,0x128a,0x128b,0x128c,0x128d,0x128e,0x128f,
+ 0x1290,0x1291,0x1292,0x1293,0x1294,0x1295,0x1296,0x1297,
+ 0x1298,0x1299,0x129a,0x129b,0x129c,0x129d,0x129e,0x129f,
+ 0x12a0,0x12a1,0x12a2,0x12a3,0x12a4,0x12a5,0x12a6,0x12a7,
+ 0x12a8,0x12a9,0x12aa,0x12ab,0x12ac,0x12ad,0x12ae,0x12af,
+ 0x12b0,0x12b1,0x12b2,0x12b3,0x12b4,0x12b5,0x12b6,0x12b7,
+ 0x12b8,0x12b9,0x12ba,0x12bb,0x12bc,0x12bd,0x12be,0x12bf,
+ 0x12c0,0x12c1,0x12c2,0x12c3,0x12c4,0x12c5,0x12c6,0x12c7,
+ 0x12c8,0x12c9,0x12ca,0x12cb,0x12cc,0x12cd,0x12ce,0x12cf,
+ 0x12d0,0x12d1,0x12d2,0x12d3,0x12d4,0x12d5,0x12d6,0x12d7,
+ 0x12d8,0x12d9,0x12da,0x12db,0x12dc,0x12dd,0x12de,0x12df,
+ 0x12e0,0x12e1,0x12e2,0x12e3,0x12e4,0x12e5,0x12e6,0x12e7,
+ 0x12e8,0x12e9,0x12ea,0x12eb,0x12ec,0x12ed,0x12ee,0x12ef,
+ 0x12f0,0x12f1,0x12f2,0x12f3,0x12f4,0x12f5,0x12f6,0x12f7,
+ 0x12f8,0x12f9,0x12fa,0x12fb,0x12fc,0x12fd,0x12fe,0x12ff,
+ 0x1300,0x1301,0x1302,0x1303,0x1304,0x1305,0x1306,0x1307,
+ 0x1308,0x1309,0x130a,0x130b,0x130c,0x130d,0x130e,0x130f,
+ 0x1310,0x1311,0x1312,0x1313,0x1314,0x1315,0x1316,0x1317,
+ 0x1318,0x1319,0x131a,0x131b,0x131c,0x131d,0x131e,0x131f,
+ 0x1320,0x1321,0x1322,0x1323,0x1324,0x1325,0x1326,0x1327,
+ 0x1328,0x1329,0x132a,0x132b,0x132c,0x132d,0x132e,0x132f,
+ 0x1330,0x1331,0x1332,0x1333,0x1334,0x1335,0x1336,0x1337,
+ 0x1338,0x1339,0x133a,0x133b,0x133c,0x133d,0x133e,0x133f,
+ 0x1340,0x1341,0x1342,0x1343,0x1344,0x1345,0x1346,0x1347,
+ 0x1348,0x1349,0x134a,0x134b,0x134c,0x134d,0x134e,0x134f,
+ 0x1350,0x1351,0x1352,0x1353,0x1354,0x1355,0x1356,0x1357,
+ 0x1358,0x1359,0x135a,0x135b,0x135c,0x135d,0x135e,0x135f,
+ 0x1360,0x1361,0x1362,0x1363,0x1364,0x1365,0x1366,0x1367,
+ 0x1368,0x1369,0x136a,0x136b,0x136c,0x136d,0x136e,0x136f,
+ 0x1370,0x1371,0x1372,0x1373,0x1374,0x1375,0x1376,0x1377,
+ 0x1378,0x1379,0x137a,0x137b,0x137c,0x137d,0x137e,0x137f,
+ 0x1380,0x1381,0x1382,0x1383,0x1384,0x1385,0x1386,0x1387,
+ 0x1388,0x1389,0x138a,0x138b,0x138c,0x138d,0x138e,0x138f,
+ 0x1390,0x1391,0x1392,0x1393,0x1394,0x1395,0x1396,0x1397,
+ 0x1398,0x1399,0x139a,0x139b,0x139c,0x139d,0x139e,0x139f,
+ 0x13a0,0x13a1,0x13a2,0x13a3,0x13a4,0x13a5,0x13a6,0x13a7,
+ 0x13a8,0x13a9,0x13aa,0x13ab,0x13ac,0x13ad,0x13ae,0x13af,
+ 0x13b0,0x13b1,0x13b2,0x13b3,0x13b4,0x13b5,0x13b6,0x13b7,
+ 0x13b8,0x13b9,0x13ba,0x13bb,0x13bc,0x13bd,0x13be,0x13bf,
+ 0x13c0,0x13c1,0x13c2,0x13c3,0x13c4,0x13c5,0x13c6,0x13c7,
+ 0x13c8,0x13c9,0x13ca,0x13cb,0x13cc,0x13cd,0x13ce,0x13cf,
+ 0x13d0,0x13d1,0x13d2,0x13d3,0x13d4,0x13d5,0x13d6,0x13d7,
+ 0x13d8,0x13d9,0x13da,0x13db,0x13dc,0x13dd,0x13de,0x13df,
+ 0x13e0,0x13e1,0x13e2,0x13e3,0x13e4,0x13e5,0x13e6,0x13e7,
+ 0x13e8,0x13e9,0x13ea,0x13eb,0x13ec,0x13ed,0x13ee,0x13ef,
+ 0x13f0,0x13f1,0x13f2,0x13f3,0x13f4,0x13f5,0x13f6,0x13f7,
+ 0x13f8,0x13f9,0x13fa,0x13fb,0x13fc,0x13fd,0x13fe,0x13ff,
+ 0x1400,0x1401,0x1402,0x1403,0x1404,0x1405,0x1406,0x1407,
+ 0x1408,0x1409,0x140a,0x140b,0x140c,0x140d,0x140e,0x140f,
+ 0x1410,0x1411,0x1412,0x1413,0x1414,0x1415,0x1416,0x1417,
+ 0x1418,0x1419,0x141a,0x141b,0x141c,0x141d,0x141e,0x141f,
+ 0x1420,0x1421,0x1422,0x1423,0x1424,0x1425,0x1426,0x1427,
+ 0x1428,0x1429,0x142a,0x142b,0x142c,0x142d,0x142e,0x142f,
+ 0x1430,0x1431,0x1432,0x1433,0x1434,0x1435,0x1436,0x1437,
+ 0x1438,0x1439,0x143a,0x143b,0x143c,0x143d,0x143e,0x143f,
+ 0x1440,0x1441,0x1442,0x1443,0x1444,0x1445,0x1446,0x1447,
+ 0x1448,0x1449,0x144a,0x144b,0x144c,0x144d,0x144e,0x144f,
+ 0x1450,0x1451,0x1452,0x1453,0x1454,0x1455,0x1456,0x1457,
+ 0x1458,0x1459,0x145a,0x145b,0x145c,0x145d,0x145e,0x145f,
+ 0x1460,0x1461,0x1462,0x1463,0x1464,0x1465,0x1466,0x1467,
+ 0x1468,0x1469,0x146a,0x146b,0x146c,0x146d,0x146e,0x146f,
+ 0x1470,0x1471,0x1472,0x1473,0x1474,0x1475,0x1476,0x1477,
+ 0x1478,0x1479,0x147a,0x147b,0x147c,0x147d,0x147e,0x147f,
+ 0x1480,0x1481,0x1482,0x1483,0x1484,0x1485,0x1486,0x1487,
+ 0x1488,0x1489,0x148a,0x148b,0x148c,0x148d,0x148e,0x148f,
+ 0x1490,0x1491,0x1492,0x1493,0x1494,0x1495,0x1496,0x1497,
+ 0x1498,0x1499,0x149a,0x149b,0x149c,0x149d,0x149e,0x149f,
+ 0x14a0,0x14a1,0x14a2,0x14a3,0x14a4,0x14a5,0x14a6,0x14a7,
+ 0x14a8,0x14a9,0x14aa,0x14ab,0x14ac,0x14ad,0x14ae,0x14af,
+ 0x14b0,0x14b1,0x14b2,0x14b3,0x14b4,0x14b5,0x14b6,0x14b7,
+ 0x14b8,0x14b9,0x14ba,0x14bb,0x14bc,0x14bd,0x14be,0x14bf,
+ 0x14c0,0x14c1,0x14c2,0x14c3,0x14c4,0x14c5,0x14c6,0x14c7,
+ 0x14c8,0x14c9,0x14ca,0x14cb,0x14cc,0x14cd,0x14ce,0x14cf,
+ 0x14d0,0x14d1,0x14d2,0x14d3,0x14d4,0x14d5,0x14d6,0x14d7,
+ 0x14d8,0x14d9,0x14da,0x14db,0x14dc,0x14dd,0x14de,0x14df,
+ 0x14e0,0x14e1,0x14e2,0x14e3,0x14e4,0x14e5,0x14e6,0x14e7,
+ 0x14e8,0x14e9,0x14ea,0x14eb,0x14ec,0x14ed,0x14ee,0x14ef,
+ 0x14f0,0x14f1,0x14f2,0x14f3,0x14f4,0x14f5,0x14f6,0x14f7,
+ 0x14f8,0x14f9,0x14fa,0x14fb,0x14fc,0x14fd,0x14fe,0x14ff,
+ 0x1500,0x1501,0x1502,0x1503,0x1504,0x1505,0x1506,0x1507,
+ 0x1508,0x1509,0x150a,0x150b,0x150c,0x150d,0x150e,0x150f,
+ 0x1510,0x1511,0x1512,0x1513,0x1514,0x1515,0x1516,0x1517,
+ 0x1518,0x1519,0x151a,0x151b,0x151c,0x151d,0x151e,0x151f,
+ 0x1520,0x1521,0x1522,0x1523,0x1524,0x1525,0x1526,0x1527,
+ 0x1528,0x1529,0x152a,0x152b,0x152c,0x152d,0x152e,0x152f,
+ 0x1530,0x1531,0x1532,0x1533,0x1534,0x1535,0x1536,0x1537,
+ 0x1538,0x1539,0x153a,0x153b,0x153c,0x153d,0x153e,0x153f,
+ 0x1540,0x1541,0x1542,0x1543,0x1544,0x1545,0x1546,0x1547,
+ 0x1548,0x1549,0x154a,0x154b,0x154c,0x154d,0x154e,0x154f,
+ 0x1550,0x1551,0x1552,0x1553,0x1554,0x1555,0x1556,0x1557,
+ 0x1558,0x1559,0x155a,0x155b,0x155c,0x155d,0x155e,0x155f,
+ 0x1560,0x1561,0x1562,0x1563,0x1564,0x1565,0x1566,0x1567,
+ 0x1568,0x1569,0x156a,0x156b,0x156c,0x156d,0x156e,0x156f,
+ 0x1570,0x1571,0x1572,0x1573,0x1574,0x1575,0x1576,0x1577,
+ 0x1578,0x1579,0x157a,0x157b,0x157c,0x157d,0x157e,0x157f,
+ 0x1580,0x1581,0x1582,0x1583,0x1584,0x1585,0x1586,0x1587,
+ 0x1588,0x1589,0x158a,0x158b,0x158c,0x158d,0x158e,0x158f,
+ 0x1590,0x1591,0x1592,0x1593,0x1594,0x1595,0x1596,0x1597,
+ 0x1598,0x1599,0x159a,0x159b,0x159c,0x159d,0x159e,0x159f,
+ 0x15a0,0x15a1,0x15a2,0x15a3,0x15a4,0x15a5,0x15a6,0x15a7,
+ 0x15a8,0x15a9,0x15aa,0x15ab,0x15ac,0x15ad,0x15ae,0x15af,
+ 0x15b0,0x15b1,0x15b2,0x15b3,0x15b4,0x15b5,0x15b6,0x15b7,
+ 0x15b8,0x15b9,0x15ba,0x15bb,0x15bc,0x15bd,0x15be,0x15bf,
+ 0x15c0,0x15c1,0x15c2,0x15c3,0x15c4,0x15c5,0x15c6,0x15c7,
+ 0x15c8,0x15c9,0x15ca,0x15cb,0x15cc,0x15cd,0x15ce,0x15cf,
+ 0x15d0,0x15d1,0x15d2,0x15d3,0x15d4,0x15d5,0x15d6,0x15d7,
+ 0x15d8,0x15d9,0x15da,0x15db,0x15dc,0x15dd,0x15de,0x15df,
+ 0x15e0,0x15e1,0x15e2,0x15e3,0x15e4,0x15e5,0x15e6,0x15e7,
+ 0x15e8,0x15e9,0x15ea,0x15eb,0x15ec,0x15ed,0x15ee,0x15ef,
+ 0x15f0,0x15f1,0x15f2,0x15f3,0x15f4,0x15f5,0x15f6,0x15f7,
+ 0x15f8,0x15f9,0x15fa,0x15fb,0x15fc,0x15fd,0x15fe,0x15ff,
+ 0x1600,0x1601,0x1602,0x1603,0x1604,0x1605,0x1606,0x1607,
+ 0x1608,0x1609,0x160a,0x160b,0x160c,0x160d,0x160e,0x160f,
+ 0x1610,0x1611,0x1612,0x1613,0x1614,0x1615,0x1616,0x1617,
+ 0x1618,0x1619,0x161a,0x161b,0x161c,0x161d,0x161e,0x161f,
+ 0x1620,0x1621,0x1622,0x1623,0x1624,0x1625,0x1626,0x1627,
+ 0x1628,0x1629,0x162a,0x162b,0x162c,0x162d,0x162e,0x162f,
+ 0x1630,0x1631,0x1632,0x1633,0x1634,0x1635,0x1636,0x1637,
+ 0x1638,0x1639,0x163a,0x163b,0x163c,0x163d,0x163e,0x163f,
+ 0x1640,0x1641,0x1642,0x1643,0x1644,0x1645,0x1646,0x1647,
+ 0x1648,0x1649,0x164a,0x164b,0x164c,0x164d,0x164e,0x164f,
+ 0x1650,0x1651,0x1652,0x1653,0x1654,0x1655,0x1656,0x1657,
+ 0x1658,0x1659,0x165a,0x165b,0x165c,0x165d,0x165e,0x165f,
+ 0x1660,0x1661,0x1662,0x1663,0x1664,0x1665,0x1666,0x1667,
+ 0x1668,0x1669,0x166a,0x166b,0x166c,0x166d,0x166e,0x166f,
+ 0x1670,0x1671,0x1672,0x1673,0x1674,0x1675,0x1676,0x1677,
+ 0x1678,0x1679,0x167a,0x167b,0x167c,0x167d,0x167e,0x167f,
+ 0x1680,0x1681,0x1682,0x1683,0x1684,0x1685,0x1686,0x1687,
+ 0x1688,0x1689,0x168a,0x168b,0x168c,0x168d,0x168e,0x168f,
+ 0x1690,0x1691,0x1692,0x1693,0x1694,0x1695,0x1696,0x1697,
+ 0x1698,0x1699,0x169a,0x169b,0x169c,0x169d,0x169e,0x169f,
+ 0x16a0,0x16a1,0x16a2,0x16a3,0x16a4,0x16a5,0x16a6,0x16a7,
+ 0x16a8,0x16a9,0x16aa,0x16ab,0x16ac,0x16ad,0x16ae,0x16af,
+ 0x16b0,0x16b1,0x16b2,0x16b3,0x16b4,0x16b5,0x16b6,0x16b7,
+ 0x16b8,0x16b9,0x16ba,0x16bb,0x16bc,0x16bd,0x16be,0x16bf,
+ 0x16c0,0x16c1,0x16c2,0x16c3,0x16c4,0x16c5,0x16c6,0x16c7,
+ 0x16c8,0x16c9,0x16ca,0x16cb,0x16cc,0x16cd,0x16ce,0x16cf,
+ 0x16d0,0x16d1,0x16d2,0x16d3,0x16d4,0x16d5,0x16d6,0x16d7,
+ 0x16d8,0x16d9,0x16da,0x16db,0x16dc,0x16dd,0x16de,0x16df,
+ 0x16e0,0x16e1,0x16e2,0x16e3,0x16e4,0x16e5,0x16e6,0x16e7,
+ 0x16e8,0x16e9,0x16ea,0x16eb,0x16ec,0x16ed,0x16ee,0x16ef,
+ 0x16f0,0x16f1,0x16f2,0x16f3,0x16f4,0x16f5,0x16f6,0x16f7,
+ 0x16f8,0x16f9,0x16fa,0x16fb,0x16fc,0x16fd,0x16fe,0x16ff,
+ 0x1700,0x1701,0x1702,0x1703,0x1704,0x1705,0x1706,0x1707,
+ 0x1708,0x1709,0x170a,0x170b,0x170c,0x170d,0x170e,0x170f,
+ 0x1710,0x1711,0x1712,0x1713,0x1714,0x1715,0x1716,0x1717,
+ 0x1718,0x1719,0x171a,0x171b,0x171c,0x171d,0x171e,0x171f,
+ 0x1720,0x1721,0x1722,0x1723,0x1724,0x1725,0x1726,0x1727,
+ 0x1728,0x1729,0x172a,0x172b,0x172c,0x172d,0x172e,0x172f,
+ 0x1730,0x1731,0x1732,0x1733,0x1734,0x1735,0x1736,0x1737,
+ 0x1738,0x1739,0x173a,0x173b,0x173c,0x173d,0x173e,0x173f,
+ 0x1740,0x1741,0x1742,0x1743,0x1744,0x1745,0x1746,0x1747,
+ 0x1748,0x1749,0x174a,0x174b,0x174c,0x174d,0x174e,0x174f,
+ 0x1750,0x1751,0x1752,0x1753,0x1754,0x1755,0x1756,0x1757,
+ 0x1758,0x1759,0x175a,0x175b,0x175c,0x175d,0x175e,0x175f,
+ 0x1760,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,
+ 0x1768,0x1769,0x176a,0x176b,0x176c,0x176d,0x176e,0x176f,
+ 0x1770,0x1771,0x1772,0x1773,0x1774,0x1775,0x1776,0x1777,
+ 0x1778,0x1779,0x177a,0x177b,0x177c,0x177d,0x177e,0x177f,
+ 0x1780,0x1781,0x1782,0x1783,0x1784,0x1785,0x1786,0x1787,
+ 0x1788,0x1789,0x178a,0x178b,0x178c,0x178d,0x178e,0x178f,
+ 0x1790,0x1791,0x1792,0x1793,0x1794,0x1795,0x1796,0x1797,
+ 0x1798,0x1799,0x179a,0x179b,0x179c,0x179d,0x179e,0x179f,
+ 0x17a0,0x17a1,0x17a2,0x17a3,0x17a4,0x17a5,0x17a6,0x17a7,
+ 0x17a8,0x17a9,0x17aa,0x17ab,0x17ac,0x17ad,0x17ae,0x17af,
+ 0x17b0,0x17b1,0x17b2,0x17b3,0x17b4,0x17b5,0x17b6,0x17b7,
+ 0x17b8,0x17b9,0x17ba,0x17bb,0x17bc,0x17bd,0x17be,0x17bf,
+ 0x17c0,0x17c1,0x17c2,0x17c3,0x17c4,0x17c5,0x17c6,0x17c7,
+ 0x17c8,0x17c9,0x17ca,0x17cb,0x17cc,0x17cd,0x17ce,0x17cf,
+ 0x17d0,0x17d1,0x17d2,0x17d3,0x17d4,0x17d5,0x17d6,0x17d7,
+ 0x17d8,0x17d9,0x17da,0x17db,0x17dc,0x17dd,0x17de,0x17df,
+ 0x17e0,0x17e1,0x17e2,0x17e3,0x17e4,0x17e5,0x17e6,0x17e7,
+ 0x17e8,0x17e9,0x17ea,0x17eb,0x17ec,0x17ed,0x17ee,0x17ef,
+ 0x17f0,0x17f1,0x17f2,0x17f3,0x17f4,0x17f5,0x17f6,0x17f7,
+ 0x17f8,0x17f9,0x17fa,0x17fb,0x17fc,0x17fd,0x17fe,0x17ff,
+ 0x1800,0x1801,0x1802,0x1803,0x1804,0x1805,0x1806,0x1807,
+ 0x1808,0x1809,0x180a,0x180b,0x180c,0x180d,0x180e,0x180f,
+ 0x1810,0x1811,0x1812,0x1813,0x1814,0x1815,0x1816,0x1817,
+ 0x1818,0x1819,0x181a,0x181b,0x181c,0x181d,0x181e,0x181f,
+ 0x1820,0x1821,0x1822,0x1823,0x1824,0x1825,0x1826,0x1827,
+ 0x1828,0x1829,0x182a,0x182b,0x182c,0x182d,0x182e,0x182f,
+ 0x1830,0x1831,0x1832,0x1833,0x1834,0x1835,0x1836,0x1837,
+ 0x1838,0x1839,0x183a,0x183b,0x183c,0x183d,0x183e,0x183f,
+ 0x1840,0x1841,0x1842,0x1843,0x1844,0x1845,0x1846,0x1847,
+ 0x1848,0x1849,0x184a,0x184b,0x184c,0x184d,0x184e,0x184f,
+ 0x1850,0x1851,0x1852,0x1853,0x1854,0x1855,0x1856,0x1857,
+ 0x1858,0x1859,0x185a,0x185b,0x185c,0x185d,0x185e,0x185f,
+ 0x1860,0x1861,0x1862,0x1863,0x1864,0x1865,0x1866,0x1867,
+ 0x1868,0x1869,0x186a,0x186b,0x186c,0x186d,0x186e,0x186f,
+ 0x1870,0x1871,0x1872,0x1873,0x1874,0x1875,0x1876,0x1877,
+ 0x1878,0x1879,0x187a,0x187b,0x187c,0x187d,0x187e,0x187f,
+ 0x1880,0x1881,0x1882,0x1883,0x1884,0x1885,0x1886,0x1887,
+ 0x1888,0x1889,0x188a,0x188b,0x188c,0x188d,0x188e,0x188f,
+ 0x1890,0x1891,0x1892,0x1893,0x1894,0x1895,0x1896,0x1897,
+ 0x1898,0x1899,0x189a,0x189b,0x189c,0x189d,0x189e,0x189f,
+ 0x18a0,0x18a1,0x18a2,0x18a3,0x18a4,0x18a5,0x18a6,0x18a7,
+ 0x18a8,0x18a9,0x18aa,0x18ab,0x18ac,0x18ad,0x18ae,0x18af,
+ 0x18b0,0x18b1,0x18b2,0x18b3,0x18b4,0x18b5,0x18b6,0x18b7,
+ 0x18b8,0x18b9,0x18ba,0x18bb,0x18bc,0x18bd,0x18be,0x18bf,
+ 0x18c0,0x18c1,0x18c2,0x18c3,0x18c4,0x18c5,0x18c6,0x18c7,
+ 0x18c8,0x18c9,0x18ca,0x18cb,0x18cc,0x18cd,0x18ce,0x18cf,
+ 0x18d0,0x18d1,0x18d2,0x18d3,0x18d4,0x18d5,0x18d6,0x18d7,
+ 0x18d8,0x18d9,0x18da,0x18db,0x18dc,0x18dd,0x18de,0x18df,
+ 0x18e0,0x18e1,0x18e2,0x18e3,0x18e4,0x18e5,0x18e6,0x18e7,
+ 0x18e8,0x18e9,0x18ea,0x18eb,0x18ec,0x18ed,0x18ee,0x18ef,
+ 0x18f0,0x18f1,0x18f2,0x18f3,0x18f4,0x18f5,0x18f6,0x18f7,
+ 0x18f8,0x18f9,0x18fa,0x18fb,0x18fc,0x18fd,0x18fe,0x18ff,
+ 0x1900,0x1901,0x1902,0x1903,0x1904,0x1905,0x1906,0x1907,
+ 0x1908,0x1909,0x190a,0x190b,0x190c,0x190d,0x190e,0x190f,
+ 0x1910,0x1911,0x1912,0x1913,0x1914,0x1915,0x1916,0x1917,
+ 0x1918,0x1919,0x191a,0x191b,0x191c,0x191d,0x191e,0x191f,
+ 0x1920,0x1921,0x1922,0x1923,0x1924,0x1925,0x1926,0x1927,
+ 0x1928,0x1929,0x192a,0x192b,0x192c,0x192d,0x192e,0x192f,
+ 0x1930,0x1931,0x1932,0x1933,0x1934,0x1935,0x1936,0x1937,
+ 0x1938,0x1939,0x193a,0x193b,0x193c,0x193d,0x193e,0x193f,
+ 0x1940,0x1941,0x1942,0x1943,0x1944,0x1945,0x1946,0x1947,
+ 0x1948,0x1949,0x194a,0x194b,0x194c,0x194d,0x194e,0x194f,
+ 0x1950,0x1951,0x1952,0x1953,0x1954,0x1955,0x1956,0x1957,
+ 0x1958,0x1959,0x195a,0x195b,0x195c,0x195d,0x195e,0x195f,
+ 0x1960,0x1961,0x1962,0x1963,0x1964,0x1965,0x1966,0x1967,
+ 0x1968,0x1969,0x196a,0x196b,0x196c,0x196d,0x196e,0x196f,
+ 0x1970,0x1971,0x1972,0x1973,0x1974,0x1975,0x1976,0x1977,
+ 0x1978,0x1979,0x197a,0x197b,0x197c,0x197d,0x197e,0x197f,
+ 0x1980,0x1981,0x1982,0x1983,0x1984,0x1985,0x1986,0x1987,
+ 0x1988,0x1989,0x198a,0x198b,0x198c,0x198d,0x198e,0x198f,
+ 0x1990,0x1991,0x1992,0x1993,0x1994,0x1995,0x1996,0x1997,
+ 0x1998,0x1999,0x199a,0x199b,0x199c,0x199d,0x199e,0x199f,
+ 0x19a0,0x19a1,0x19a2,0x19a3,0x19a4,0x19a5,0x19a6,0x19a7,
+ 0x19a8,0x19a9,0x19aa,0x19ab,0x19ac,0x19ad,0x19ae,0x19af,
+ 0x19b0,0x19b1,0x19b2,0x19b3,0x19b4,0x19b5,0x19b6,0x19b7,
+ 0x19b8,0x19b9,0x19ba,0x19bb,0x19bc,0x19bd,0x19be,0x19bf,
+ 0x19c0,0x19c1,0x19c2,0x19c3,0x19c4,0x19c5,0x19c6,0x19c7,
+ 0x19c8,0x19c9,0x19ca,0x19cb,0x19cc,0x19cd,0x19ce,0x19cf,
+ 0x19d0,0x19d1,0x19d2,0x19d3,0x19d4,0x19d5,0x19d6,0x19d7,
+ 0x19d8,0x19d9,0x19da,0x19db,0x19dc,0x19dd,0x19de,0x19df,
+ 0x19e0,0x19e1,0x19e2,0x19e3,0x19e4,0x19e5,0x19e6,0x19e7,
+ 0x19e8,0x19e9,0x19ea,0x19eb,0x19ec,0x19ed,0x19ee,0x19ef,
+ 0x19f0,0x19f1,0x19f2,0x19f3,0x19f4,0x19f5,0x19f6,0x19f7,
+ 0x19f8,0x19f9,0x19fa,0x19fb,0x19fc,0x19fd,0x19fe,0x19ff,
+ 0x1a00,0x1a01,0x1a02,0x1a03,0x1a04,0x1a05,0x1a06,0x1a07,
+ 0x1a08,0x1a09,0x1a0a,0x1a0b,0x1a0c,0x1a0d,0x1a0e,0x1a0f,
+ 0x1a10,0x1a11,0x1a12,0x1a13,0x1a14,0x1a15,0x1a16,0x1a17,
+ 0x1a18,0x1a19,0x1a1a,0x1a1b,0x1a1c,0x1a1d,0x1a1e,0x1a1f,
+ 0x1a20,0x1a21,0x1a22,0x1a23,0x1a24,0x1a25,0x1a26,0x1a27,
+ 0x1a28,0x1a29,0x1a2a,0x1a2b,0x1a2c,0x1a2d,0x1a2e,0x1a2f,
+ 0x1a30,0x1a31,0x1a32,0x1a33,0x1a34,0x1a35,0x1a36,0x1a37,
+ 0x1a38,0x1a39,0x1a3a,0x1a3b,0x1a3c,0x1a3d,0x1a3e,0x1a3f,
+ 0x1a40,0x1a41,0x1a42,0x1a43,0x1a44,0x1a45,0x1a46,0x1a47,
+ 0x1a48,0x1a49,0x1a4a,0x1a4b,0x1a4c,0x1a4d,0x1a4e,0x1a4f,
+ 0x1a50,0x1a51,0x1a52,0x1a53,0x1a54,0x1a55,0x1a56,0x1a57,
+ 0x1a58,0x1a59,0x1a5a,0x1a5b,0x1a5c,0x1a5d,0x1a5e,0x1a5f,
+ 0x1a60,0x1a61,0x1a62,0x1a63,0x1a64,0x1a65,0x1a66,0x1a67,
+ 0x1a68,0x1a69,0x1a6a,0x1a6b,0x1a6c,0x1a6d,0x1a6e,0x1a6f,
+ 0x1a70,0x1a71,0x1a72,0x1a73,0x1a74,0x1a75,0x1a76,0x1a77,
+ 0x1a78,0x1a79,0x1a7a,0x1a7b,0x1a7c,0x1a7d,0x1a7e,0x1a7f,
+ 0x1a80,0x1a81,0x1a82,0x1a83,0x1a84,0x1a85,0x1a86,0x1a87,
+ 0x1a88,0x1a89,0x1a8a,0x1a8b,0x1a8c,0x1a8d,0x1a8e,0x1a8f,
+ 0x1a90,0x1a91,0x1a92,0x1a93,0x1a94,0x1a95,0x1a96,0x1a97,
+ 0x1a98,0x1a99,0x1a9a,0x1a9b,0x1a9c,0x1a9d,0x1a9e,0x1a9f,
+ 0x1aa0,0x1aa1,0x1aa2,0x1aa3,0x1aa4,0x1aa5,0x1aa6,0x1aa7,
+ 0x1aa8,0x1aa9,0x1aaa,0x1aab,0x1aac,0x1aad,0x1aae,0x1aaf,
+ 0x1ab0,0x1ab1,0x1ab2,0x1ab3,0x1ab4,0x1ab5,0x1ab6,0x1ab7,
+ 0x1ab8,0x1ab9,0x1aba,0x1abb,0x1abc,0x1abd,0x1abe,0x1abf,
+ 0x1ac0,0x1ac1,0x1ac2,0x1ac3,0x1ac4,0x1ac5,0x1ac6,0x1ac7,
+ 0x1ac8,0x1ac9,0x1aca,0x1acb,0x1acc,0x1acd,0x1ace,0x1acf,
+ 0x1ad0,0x1ad1,0x1ad2,0x1ad3,0x1ad4,0x1ad5,0x1ad6,0x1ad7,
+ 0x1ad8,0x1ad9,0x1ada,0x1adb,0x1adc,0x1add,0x1ade,0x1adf,
+ 0x1ae0,0x1ae1,0x1ae2,0x1ae3,0x1ae4,0x1ae5,0x1ae6,0x1ae7,
+ 0x1ae8,0x1ae9,0x1aea,0x1aeb,0x1aec,0x1aed,0x1aee,0x1aef,
+ 0x1af0,0x1af1,0x1af2,0x1af3,0x1af4,0x1af5,0x1af6,0x1af7,
+ 0x1af8,0x1af9,0x1afa,0x1afb,0x1afc,0x1afd,0x1afe,0x1aff,
+ 0x1b00,0x1b01,0x1b02,0x1b03,0x1b04,0x1b05,0x1b06,0x1b07,
+ 0x1b08,0x1b09,0x1b0a,0x1b0b,0x1b0c,0x1b0d,0x1b0e,0x1b0f,
+ 0x1b10,0x1b11,0x1b12,0x1b13,0x1b14,0x1b15,0x1b16,0x1b17,
+ 0x1b18,0x1b19,0x1b1a,0x1b1b,0x1b1c,0x1b1d,0x1b1e,0x1b1f,
+ 0x1b20,0x1b21,0x1b22,0x1b23,0x1b24,0x1b25,0x1b26,0x1b27,
+ 0x1b28,0x1b29,0x1b2a,0x1b2b,0x1b2c,0x1b2d,0x1b2e,0x1b2f,
+ 0x1b30,0x1b31,0x1b32,0x1b33,0x1b34,0x1b35,0x1b36,0x1b37,
+ 0x1b38,0x1b39,0x1b3a,0x1b3b,0x1b3c,0x1b3d,0x1b3e,0x1b3f,
+ 0x1b40,0x1b41,0x1b42,0x1b43,0x1b44,0x1b45,0x1b46,0x1b47,
+ 0x1b48,0x1b49,0x1b4a,0x1b4b,0x1b4c,0x1b4d,0x1b4e,0x1b4f,
+ 0x1b50,0x1b51,0x1b52,0x1b53,0x1b54,0x1b55,0x1b56,0x1b57,
+ 0x1b58,0x1b59,0x1b5a,0x1b5b,0x1b5c,0x1b5d,0x1b5e,0x1b5f,
+ 0x1b60,0x1b61,0x1b62,0x1b63,0x1b64,0x1b65,0x1b66,0x1b67,
+ 0x1b68,0x1b69,0x1b6a,0x1b6b,0x1b6c,0x1b6d,0x1b6e,0x1b6f,
+ 0x1b70,0x1b71,0x1b72,0x1b73,0x1b74,0x1b75,0x1b76,0x1b77,
+ 0x1b78,0x1b79,0x1b7a,0x1b7b,0x1b7c,0x1b7d,0x1b7e,0x1b7f,
+ 0x1b80,0x1b81,0x1b82,0x1b83,0x1b84,0x1b85,0x1b86,0x1b87,
+ 0x1b88,0x1b89,0x1b8a,0x1b8b,0x1b8c,0x1b8d,0x1b8e,0x1b8f,
+ 0x1b90,0x1b91,0x1b92,0x1b93,0x1b94,0x1b95,0x1b96,0x1b97,
+ 0x1b98,0x1b99,0x1b9a,0x1b9b,0x1b9c,0x1b9d,0x1b9e,0x1b9f,
+ 0x1ba0,0x1ba1,0x1ba2,0x1ba3,0x1ba4,0x1ba5,0x1ba6,0x1ba7,
+ 0x1ba8,0x1ba9,0x1baa,0x1bab,0x1bac,0x1bad,0x1bae,0x1baf,
+ 0x1bb0,0x1bb1,0x1bb2,0x1bb3,0x1bb4,0x1bb5,0x1bb6,0x1bb7,
+ 0x1bb8,0x1bb9,0x1bba,0x1bbb,0x1bbc,0x1bbd,0x1bbe,0x1bbf,
+ 0x1bc0,0x1bc1,0x1bc2,0x1bc3,0x1bc4,0x1bc5,0x1bc6,0x1bc7,
+ 0x1bc8,0x1bc9,0x1bca,0x1bcb,0x1bcc,0x1bcd,0x1bce,0x1bcf,
+ 0x1bd0,0x1bd1,0x1bd2,0x1bd3,0x1bd4,0x1bd5,0x1bd6,0x1bd7,
+ 0x1bd8,0x1bd9,0x1bda,0x1bdb,0x1bdc,0x1bdd,0x1bde,0x1bdf,
+ 0x1be0,0x1be1,0x1be2,0x1be3,0x1be4,0x1be5,0x1be6,0x1be7,
+ 0x1be8,0x1be9,0x1bea,0x1beb,0x1bec,0x1bed,0x1bee,0x1bef,
+ 0x1bf0,0x1bf1,0x1bf2,0x1bf3,0x1bf4,0x1bf5,0x1bf6,0x1bf7,
+ 0x1bf8,0x1bf9,0x1bfa,0x1bfb,0x1bfc,0x1bfd,0x1bfe,0x1bff,
+ 0x1c00,0x1c01,0x1c02,0x1c03,0x1c04,0x1c05,0x1c06,0x1c07,
+ 0x1c08,0x1c09,0x1c0a,0x1c0b,0x1c0c,0x1c0d,0x1c0e,0x1c0f,
+ 0x1c10,0x1c11,0x1c12,0x1c13,0x1c14,0x1c15,0x1c16,0x1c17,
+ 0x1c18,0x1c19,0x1c1a,0x1c1b,0x1c1c,0x1c1d,0x1c1e,0x1c1f,
+ 0x1c20,0x1c21,0x1c22,0x1c23,0x1c24,0x1c25,0x1c26,0x1c27,
+ 0x1c28,0x1c29,0x1c2a,0x1c2b,0x1c2c,0x1c2d,0x1c2e,0x1c2f,
+ 0x1c30,0x1c31,0x1c32,0x1c33,0x1c34,0x1c35,0x1c36,0x1c37,
+ 0x1c38,0x1c39,0x1c3a,0x1c3b,0x1c3c,0x1c3d,0x1c3e,0x1c3f,
+ 0x1c40,0x1c41,0x1c42,0x1c43,0x1c44,0x1c45,0x1c46,0x1c47,
+ 0x1c48,0x1c49,0x1c4a,0x1c4b,0x1c4c,0x1c4d,0x1c4e,0x1c4f,
+ 0x1c50,0x1c51,0x1c52,0x1c53,0x1c54,0x1c55,0x1c56,0x1c57,
+ 0x1c58,0x1c59,0x1c5a,0x1c5b,0x1c5c,0x1c5d,0x1c5e,0x1c5f,
+ 0x1c60,0x1c61,0x1c62,0x1c63,0x1c64,0x1c65,0x1c66,0x1c67,
+ 0x1c68,0x1c69,0x1c6a,0x1c6b,0x1c6c,0x1c6d,0x1c6e,0x1c6f,
+ 0x1c70,0x1c71,0x1c72,0x1c73,0x1c74,0x1c75,0x1c76,0x1c77,
+ 0x1c78,0x1c79,0x1c7a,0x1c7b,0x1c7c,0x1c7d,0x1c7e,0x1c7f,
+ 0x1c80,0x1c81,0x1c82,0x1c83,0x1c84,0x1c85,0x1c86,0x1c87,
+ 0x1c88,0x1c89,0x1c8a,0x1c8b,0x1c8c,0x1c8d,0x1c8e,0x1c8f,
+ 0x1c90,0x1c91,0x1c92,0x1c93,0x1c94,0x1c95,0x1c96,0x1c97,
+ 0x1c98,0x1c99,0x1c9a,0x1c9b,0x1c9c,0x1c9d,0x1c9e,0x1c9f,
+ 0x1ca0,0x1ca1,0x1ca2,0x1ca3,0x1ca4,0x1ca5,0x1ca6,0x1ca7,
+ 0x1ca8,0x1ca9,0x1caa,0x1cab,0x1cac,0x1cad,0x1cae,0x1caf,
+ 0x1cb0,0x1cb1,0x1cb2,0x1cb3,0x1cb4,0x1cb5,0x1cb6,0x1cb7,
+ 0x1cb8,0x1cb9,0x1cba,0x1cbb,0x1cbc,0x1cbd,0x1cbe,0x1cbf,
+ 0x1cc0,0x1cc1,0x1cc2,0x1cc3,0x1cc4,0x1cc5,0x1cc6,0x1cc7,
+ 0x1cc8,0x1cc9,0x1cca,0x1ccb,0x1ccc,0x1ccd,0x1cce,0x1ccf,
+ 0x1cd0,0x1cd1,0x1cd2,0x1cd3,0x1cd4,0x1cd5,0x1cd6,0x1cd7,
+ 0x1cd8,0x1cd9,0x1cda,0x1cdb,0x1cdc,0x1cdd,0x1cde,0x1cdf,
+ 0x1ce0,0x1ce1,0x1ce2,0x1ce3,0x1ce4,0x1ce5,0x1ce6,0x1ce7,
+ 0x1ce8,0x1ce9,0x1cea,0x1ceb,0x1cec,0x1ced,0x1cee,0x1cef,
+ 0x1cf0,0x1cf1,0x1cf2,0x1cf3,0x1cf4,0x1cf5,0x1cf6,0x1cf7,
+ 0x1cf8,0x1cf9,0x1cfa,0x1cfb,0x1cfc,0x1cfd,0x1cfe,0x1cff,
+ 0x1d00,0x1d01,0x1d02,0x1d03,0x1d04,0x1d05,0x1d06,0x1d07,
+ 0x1d08,0x1d09,0x1d0a,0x1d0b,0x1d0c,0x1d0d,0x1d0e,0x1d0f,
+ 0x1d10,0x1d11,0x1d12,0x1d13,0x1d14,0x1d15,0x1d16,0x1d17,
+ 0x1d18,0x1d19,0x1d1a,0x1d1b,0x1d1c,0x1d1d,0x1d1e,0x1d1f,
+ 0x1d20,0x1d21,0x1d22,0x1d23,0x1d24,0x1d25,0x1d26,0x1d27,
+ 0x1d28,0x1d29,0x1d2a,0x1d2b,0x1d2c,0x1d2d,0x1d2e,0x1d2f,
+ 0x1d30,0x1d31,0x1d32,0x1d33,0x1d34,0x1d35,0x1d36,0x1d37,
+ 0x1d38,0x1d39,0x1d3a,0x1d3b,0x1d3c,0x1d3d,0x1d3e,0x1d3f,
+ 0x1d40,0x1d41,0x1d42,0x1d43,0x1d44,0x1d45,0x1d46,0x1d47,
+ 0x1d48,0x1d49,0x1d4a,0x1d4b,0x1d4c,0x1d4d,0x1d4e,0x1d4f,
+ 0x1d50,0x1d51,0x1d52,0x1d53,0x1d54,0x1d55,0x1d56,0x1d57,
+ 0x1d58,0x1d59,0x1d5a,0x1d5b,0x1d5c,0x1d5d,0x1d5e,0x1d5f,
+ 0x1d60,0x1d61,0x1d62,0x1d63,0x1d64,0x1d65,0x1d66,0x1d67,
+ 0x1d68,0x1d69,0x1d6a,0x1d6b,0x1d6c,0x1d6d,0x1d6e,0x1d6f,
+ 0x1d70,0x1d71,0x1d72,0x1d73,0x1d74,0x1d75,0x1d76,0x1d77,
+ 0x1d78,0x1d79,0x1d7a,0x1d7b,0x1d7c,0x2c63,0x1d7e,0x1d7f,
+ 0x1d80,0x1d81,0x1d82,0x1d83,0x1d84,0x1d85,0x1d86,0x1d87,
+ 0x1d88,0x1d89,0x1d8a,0x1d8b,0x1d8c,0x1d8d,0x1d8e,0x1d8f,
+ 0x1d90,0x1d91,0x1d92,0x1d93,0x1d94,0x1d95,0x1d96,0x1d97,
+ 0x1d98,0x1d99,0x1d9a,0x1d9b,0x1d9c,0x1d9d,0x1d9e,0x1d9f,
+ 0x1da0,0x1da1,0x1da2,0x1da3,0x1da4,0x1da5,0x1da6,0x1da7,
+ 0x1da8,0x1da9,0x1daa,0x1dab,0x1dac,0x1dad,0x1dae,0x1daf,
+ 0x1db0,0x1db1,0x1db2,0x1db3,0x1db4,0x1db5,0x1db6,0x1db7,
+ 0x1db8,0x1db9,0x1dba,0x1dbb,0x1dbc,0x1dbd,0x1dbe,0x1dbf,
+ 0x1dc0,0x1dc1,0x1dc2,0x1dc3,0x1dc4,0x1dc5,0x1dc6,0x1dc7,
+ 0x1dc8,0x1dc9,0x1dca,0x1dcb,0x1dcc,0x1dcd,0x1dce,0x1dcf,
+ 0x1dd0,0x1dd1,0x1dd2,0x1dd3,0x1dd4,0x1dd5,0x1dd6,0x1dd7,
+ 0x1dd8,0x1dd9,0x1dda,0x1ddb,0x1ddc,0x1ddd,0x1dde,0x1ddf,
+ 0x1de0,0x1de1,0x1de2,0x1de3,0x1de4,0x1de5,0x1de6,0x1de7,
+ 0x1de8,0x1de9,0x1dea,0x1deb,0x1dec,0x1ded,0x1dee,0x1def,
+ 0x1df0,0x1df1,0x1df2,0x1df3,0x1df4,0x1df5,0x1df6,0x1df7,
+ 0x1df8,0x1df9,0x1dfa,0x1dfb,0x1dfc,0x1dfd,0x1dfe,0x1dff,
+ 0x1e00,0x1e00,0x1e02,0x1e02,0x1e04,0x1e04,0x1e06,0x1e06,
+ 0x1e08,0x1e08,0x1e0a,0x1e0a,0x1e0c,0x1e0c,0x1e0e,0x1e0e,
+ 0x1e10,0x1e10,0x1e12,0x1e12,0x1e14,0x1e14,0x1e16,0x1e16,
+ 0x1e18,0x1e18,0x1e1a,0x1e1a,0x1e1c,0x1e1c,0x1e1e,0x1e1e,
+ 0x1e20,0x1e20,0x1e22,0x1e22,0x1e24,0x1e24,0x1e26,0x1e26,
+ 0x1e28,0x1e28,0x1e2a,0x1e2a,0x1e2c,0x1e2c,0x1e2e,0x1e2e,
+ 0x1e30,0x1e30,0x1e32,0x1e32,0x1e34,0x1e34,0x1e36,0x1e36,
+ 0x1e38,0x1e38,0x1e3a,0x1e3a,0x1e3c,0x1e3c,0x1e3e,0x1e3e,
+ 0x1e40,0x1e40,0x1e42,0x1e42,0x1e44,0x1e44,0x1e46,0x1e46,
+ 0x1e48,0x1e48,0x1e4a,0x1e4a,0x1e4c,0x1e4c,0x1e4e,0x1e4e,
+ 0x1e50,0x1e50,0x1e52,0x1e52,0x1e54,0x1e54,0x1e56,0x1e56,
+ 0x1e58,0x1e58,0x1e5a,0x1e5a,0x1e5c,0x1e5c,0x1e5e,0x1e5e,
+ 0x1e60,0x1e60,0x1e62,0x1e62,0x1e64,0x1e64,0x1e66,0x1e66,
+ 0x1e68,0x1e68,0x1e6a,0x1e6a,0x1e6c,0x1e6c,0x1e6e,0x1e6e,
+ 0x1e70,0x1e70,0x1e72,0x1e72,0x1e74,0x1e74,0x1e76,0x1e76,
+ 0x1e78,0x1e78,0x1e7a,0x1e7a,0x1e7c,0x1e7c,0x1e7e,0x1e7e,
+ 0x1e80,0x1e80,0x1e82,0x1e82,0x1e84,0x1e84,0x1e86,0x1e86,
+ 0x1e88,0x1e88,0x1e8a,0x1e8a,0x1e8c,0x1e8c,0x1e8e,0x1e8e,
+ 0x1e90,0x1e90,0x1e92,0x1e92,0x1e94,0x1e94,0x1e96,0x1e97,
+ 0x1e98,0x1e99,0x1e9a,0x1e60,0x1e9c,0x1e9d,0x1e9e,0x1e9f,
+ 0x1ea0,0x1ea0,0x1ea2,0x1ea2,0x1ea4,0x1ea4,0x1ea6,0x1ea6,
+ 0x1ea8,0x1ea8,0x1eaa,0x1eaa,0x1eac,0x1eac,0x1eae,0x1eae,
+ 0x1eb0,0x1eb0,0x1eb2,0x1eb2,0x1eb4,0x1eb4,0x1eb6,0x1eb6,
+ 0x1eb8,0x1eb8,0x1eba,0x1eba,0x1ebc,0x1ebc,0x1ebe,0x1ebe,
+ 0x1ec0,0x1ec0,0x1ec2,0x1ec2,0x1ec4,0x1ec4,0x1ec6,0x1ec6,
+ 0x1ec8,0x1ec8,0x1eca,0x1eca,0x1ecc,0x1ecc,0x1ece,0x1ece,
+ 0x1ed0,0x1ed0,0x1ed2,0x1ed2,0x1ed4,0x1ed4,0x1ed6,0x1ed6,
+ 0x1ed8,0x1ed8,0x1eda,0x1eda,0x1edc,0x1edc,0x1ede,0x1ede,
+ 0x1ee0,0x1ee0,0x1ee2,0x1ee2,0x1ee4,0x1ee4,0x1ee6,0x1ee6,
+ 0x1ee8,0x1ee8,0x1eea,0x1eea,0x1eec,0x1eec,0x1eee,0x1eee,
+ 0x1ef0,0x1ef0,0x1ef2,0x1ef2,0x1ef4,0x1ef4,0x1ef6,0x1ef6,
+ 0x1ef8,0x1ef8,0x1efa,0x1efb,0x1efc,0x1efd,0x1efe,0x1eff,
+ 0x1f08,0x1f09,0x1f0a,0x1f0b,0x1f0c,0x1f0d,0x1f0e,0x1f0f,
+ 0x1f08,0x1f09,0x1f0a,0x1f0b,0x1f0c,0x1f0d,0x1f0e,0x1f0f,
+ 0x1f18,0x1f19,0x1f1a,0x1f1b,0x1f1c,0x1f1d,0x1f16,0x1f17,
+ 0x1f18,0x1f19,0x1f1a,0x1f1b,0x1f1c,0x1f1d,0x1f1e,0x1f1f,
+ 0x1f28,0x1f29,0x1f2a,0x1f2b,0x1f2c,0x1f2d,0x1f2e,0x1f2f,
+ 0x1f28,0x1f29,0x1f2a,0x1f2b,0x1f2c,0x1f2d,0x1f2e,0x1f2f,
+ 0x1f38,0x1f39,0x1f3a,0x1f3b,0x1f3c,0x1f3d,0x1f3e,0x1f3f,
+ 0x1f38,0x1f39,0x1f3a,0x1f3b,0x1f3c,0x1f3d,0x1f3e,0x1f3f,
+ 0x1f48,0x1f49,0x1f4a,0x1f4b,0x1f4c,0x1f4d,0x1f46,0x1f47,
+ 0x1f48,0x1f49,0x1f4a,0x1f4b,0x1f4c,0x1f4d,0x1f4e,0x1f4f,
+ 0x1f50,0x1f59,0x1f52,0x1f5b,0x1f54,0x1f5d,0x1f56,0x1f5f,
+ 0x1f58,0x1f59,0x1f5a,0x1f5b,0x1f5c,0x1f5d,0x1f5e,0x1f5f,
+ 0x1f68,0x1f69,0x1f6a,0x1f6b,0x1f6c,0x1f6d,0x1f6e,0x1f6f,
+ 0x1f68,0x1f69,0x1f6a,0x1f6b,0x1f6c,0x1f6d,0x1f6e,0x1f6f,
+ 0x1fba,0x1fbb,0x1fc8,0x1fc9,0x1fca,0x1fcb,0x1fda,0x1fdb,
+ 0x1ff8,0x1ff9,0x1fea,0x1feb,0x1ffa,0x1ffb,0x1f7e,0x1f7f,
+ 0x1f88,0x1f89,0x1f8a,0x1f8b,0x1f8c,0x1f8d,0x1f8e,0x1f8f,
+ 0x1f88,0x1f89,0x1f8a,0x1f8b,0x1f8c,0x1f8d,0x1f8e,0x1f8f,
+ 0x1f98,0x1f99,0x1f9a,0x1f9b,0x1f9c,0x1f9d,0x1f9e,0x1f9f,
+ 0x1f98,0x1f99,0x1f9a,0x1f9b,0x1f9c,0x1f9d,0x1f9e,0x1f9f,
+ 0x1fa8,0x1fa9,0x1faa,0x1fab,0x1fac,0x1fad,0x1fae,0x1faf,
+ 0x1fa8,0x1fa9,0x1faa,0x1fab,0x1fac,0x1fad,0x1fae,0x1faf,
+ 0x1fb8,0x1fb9,0x1fb2,0x1fbc,0x1fb4,0x1fb5,0x1fb6,0x1fb7,
+ 0x1fb8,0x1fb9,0x1fba,0x1fbb,0x1fbc,0x1fbd,0x0399,0x1fbf,
+ 0x1fc0,0x1fc1,0x1fc2,0x1fcc,0x1fc4,0x1fc5,0x1fc6,0x1fc7,
+ 0x1fc8,0x1fc9,0x1fca,0x1fcb,0x1fcc,0x1fcd,0x1fce,0x1fcf,
+ 0x1fd8,0x1fd9,0x1fd2,0x1fd3,0x1fd4,0x1fd5,0x1fd6,0x1fd7,
+ 0x1fd8,0x1fd9,0x1fda,0x1fdb,0x1fdc,0x1fdd,0x1fde,0x1fdf,
+ 0x1fe8,0x1fe9,0x1fe2,0x1fe3,0x1fe4,0x1fec,0x1fe6,0x1fe7,
+ 0x1fe8,0x1fe9,0x1fea,0x1feb,0x1fec,0x1fed,0x1fee,0x1fef,
+ 0x1ff0,0x1ff1,0x1ff2,0x1ffc,0x1ff4,0x1ff5,0x1ff6,0x1ff7,
+ 0x1ff8,0x1ff9,0x1ffa,0x1ffb,0x1ffc,0x1ffd,0x1ffe,0x1fff,
+ 0x2000,0x2001,0x2002,0x2003,0x2004,0x2005,0x2006,0x2007,
+ 0x2008,0x2009,0x200a,0x200b,0x200c,0x200d,0x200e,0x200f,
+ 0x2010,0x2011,0x2012,0x2013,0x2014,0x2015,0x2016,0x2017,
+ 0x2018,0x2019,0x201a,0x201b,0x201c,0x201d,0x201e,0x201f,
+ 0x2020,0x2021,0x2022,0x2023,0x2024,0x2025,0x2026,0x2027,
+ 0x2028,0x2029,0x202a,0x202b,0x202c,0x202d,0x202e,0x202f,
+ 0x2030,0x2031,0x2032,0x2033,0x2034,0x2035,0x2036,0x2037,
+ 0x2038,0x2039,0x203a,0x203b,0x203c,0x203d,0x203e,0x203f,
+ 0x2040,0x2041,0x2042,0x2043,0x2044,0x2045,0x2046,0x2047,
+ 0x2048,0x2049,0x204a,0x204b,0x204c,0x204d,0x204e,0x204f,
+ 0x2050,0x2051,0x2052,0x2053,0x2054,0x2055,0x2056,0x2057,
+ 0x2058,0x2059,0x205a,0x205b,0x205c,0x205d,0x205e,0x205f,
+ 0x2060,0x2061,0x2062,0x2063,0x2064,0x2065,0x2066,0x2067,
+ 0x2068,0x2069,0x206a,0x206b,0x206c,0x206d,0x206e,0x206f,
+ 0x2070,0x2071,0x2072,0x2073,0x2074,0x2075,0x2076,0x2077,
+ 0x2078,0x2079,0x207a,0x207b,0x207c,0x207d,0x207e,0x207f,
+ 0x2080,0x2081,0x2082,0x2083,0x2084,0x2085,0x2086,0x2087,
+ 0x2088,0x2089,0x208a,0x208b,0x208c,0x208d,0x208e,0x208f,
+ 0x2090,0x2091,0x2092,0x2093,0x2094,0x2095,0x2096,0x2097,
+ 0x2098,0x2099,0x209a,0x209b,0x209c,0x209d,0x209e,0x209f,
+ 0x20a0,0x20a1,0x20a2,0x20a3,0x20a4,0x20a5,0x20a6,0x20a7,
+ 0x20a8,0x20a9,0x20aa,0x20ab,0x20ac,0x20ad,0x20ae,0x20af,
+ 0x20b0,0x20b1,0x20b2,0x20b3,0x20b4,0x20b5,0x20b6,0x20b7,
+ 0x20b8,0x20b9,0x20ba,0x20bb,0x20bc,0x20bd,0x20be,0x20bf,
+ 0x20c0,0x20c1,0x20c2,0x20c3,0x20c4,0x20c5,0x20c6,0x20c7,
+ 0x20c8,0x20c9,0x20ca,0x20cb,0x20cc,0x20cd,0x20ce,0x20cf,
+ 0x20d0,0x20d1,0x20d2,0x20d3,0x20d4,0x20d5,0x20d6,0x20d7,
+ 0x20d8,0x20d9,0x20da,0x20db,0x20dc,0x20dd,0x20de,0x20df,
+ 0x20e0,0x20e1,0x20e2,0x20e3,0x20e4,0x20e5,0x20e6,0x20e7,
+ 0x20e8,0x20e9,0x20ea,0x20eb,0x20ec,0x20ed,0x20ee,0x20ef,
+ 0x20f0,0x20f1,0x20f2,0x20f3,0x20f4,0x20f5,0x20f6,0x20f7,
+ 0x20f8,0x20f9,0x20fa,0x20fb,0x20fc,0x20fd,0x20fe,0x20ff,
+ 0x2100,0x2101,0x2102,0x2103,0x2104,0x2105,0x2106,0x2107,
+ 0x2108,0x2109,0x210a,0x210b,0x210c,0x210d,0x210e,0x210f,
+ 0x2110,0x2111,0x2112,0x2113,0x2114,0x2115,0x2116,0x2117,
+ 0x2118,0x2119,0x211a,0x211b,0x211c,0x211d,0x211e,0x211f,
+ 0x2120,0x2121,0x2122,0x2123,0x2124,0x2125,0x2126,0x2127,
+ 0x2128,0x2129,0x212a,0x212b,0x212c,0x212d,0x212e,0x212f,
+ 0x2130,0x2131,0x2132,0x2133,0x2134,0x2135,0x2136,0x2137,
+ 0x2138,0x2139,0x213a,0x213b,0x213c,0x213d,0x213e,0x213f,
+ 0x2140,0x2141,0x2142,0x2143,0x2144,0x2145,0x2146,0x2147,
+ 0x2148,0x2149,0x214a,0x214b,0x214c,0x214d,0x2132,0x214f,
+ 0x2150,0x2151,0x2152,0x2153,0x2154,0x2155,0x2156,0x2157,
+ 0x2158,0x2159,0x215a,0x215b,0x215c,0x215d,0x215e,0x215f,
+ 0x2160,0x2161,0x2162,0x2163,0x2164,0x2165,0x2166,0x2167,
+ 0x2168,0x2169,0x216a,0x216b,0x216c,0x216d,0x216e,0x216f,
+ 0x2160,0x2161,0x2162,0x2163,0x2164,0x2165,0x2166,0x2167,
+ 0x2168,0x2169,0x216a,0x216b,0x216c,0x216d,0x216e,0x216f,
+ 0x2180,0x2181,0x2182,0x2183,0x2183,0x2185,0x2186,0x2187,
+ 0x2188,0x2189,0x218a,0x218b,0x218c,0x218d,0x218e,0x218f,
+ 0x2190,0x2191,0x2192,0x2193,0x2194,0x2195,0x2196,0x2197,
+ 0x2198,0x2199,0x219a,0x219b,0x219c,0x219d,0x219e,0x219f,
+ 0x21a0,0x21a1,0x21a2,0x21a3,0x21a4,0x21a5,0x21a6,0x21a7,
+ 0x21a8,0x21a9,0x21aa,0x21ab,0x21ac,0x21ad,0x21ae,0x21af,
+ 0x21b0,0x21b1,0x21b2,0x21b3,0x21b4,0x21b5,0x21b6,0x21b7,
+ 0x21b8,0x21b9,0x21ba,0x21bb,0x21bc,0x21bd,0x21be,0x21bf,
+ 0x21c0,0x21c1,0x21c2,0x21c3,0x21c4,0x21c5,0x21c6,0x21c7,
+ 0x21c8,0x21c9,0x21ca,0x21cb,0x21cc,0x21cd,0x21ce,0x21cf,
+ 0x21d0,0x21d1,0x21d2,0x21d3,0x21d4,0x21d5,0x21d6,0x21d7,
+ 0x21d8,0x21d9,0x21da,0x21db,0x21dc,0x21dd,0x21de,0x21df,
+ 0x21e0,0x21e1,0x21e2,0x21e3,0x21e4,0x21e5,0x21e6,0x21e7,
+ 0x21e8,0x21e9,0x21ea,0x21eb,0x21ec,0x21ed,0x21ee,0x21ef,
+ 0x21f0,0x21f1,0x21f2,0x21f3,0x21f4,0x21f5,0x21f6,0x21f7,
+ 0x21f8,0x21f9,0x21fa,0x21fb,0x21fc,0x21fd,0x21fe,0x21ff,
+ 0x2200,0x2201,0x2202,0x2203,0x2204,0x2205,0x2206,0x2207,
+ 0x2208,0x2209,0x220a,0x220b,0x220c,0x220d,0x220e,0x220f,
+ 0x2210,0x2211,0x2212,0x2213,0x2214,0x2215,0x2216,0x2217,
+ 0x2218,0x2219,0x221a,0x221b,0x221c,0x221d,0x221e,0x221f,
+ 0x2220,0x2221,0x2222,0x2223,0x2224,0x2225,0x2226,0x2227,
+ 0x2228,0x2229,0x222a,0x222b,0x222c,0x222d,0x222e,0x222f,
+ 0x2230,0x2231,0x2232,0x2233,0x2234,0x2235,0x2236,0x2237,
+ 0x2238,0x2239,0x223a,0x223b,0x223c,0x223d,0x223e,0x223f,
+ 0x2240,0x2241,0x2242,0x2243,0x2244,0x2245,0x2246,0x2247,
+ 0x2248,0x2249,0x224a,0x224b,0x224c,0x224d,0x224e,0x224f,
+ 0x2250,0x2251,0x2252,0x2253,0x2254,0x2255,0x2256,0x2257,
+ 0x2258,0x2259,0x225a,0x225b,0x225c,0x225d,0x225e,0x225f,
+ 0x2260,0x2261,0x2262,0x2263,0x2264,0x2265,0x2266,0x2267,
+ 0x2268,0x2269,0x226a,0x226b,0x226c,0x226d,0x226e,0x226f,
+ 0x2270,0x2271,0x2272,0x2273,0x2274,0x2275,0x2276,0x2277,
+ 0x2278,0x2279,0x227a,0x227b,0x227c,0x227d,0x227e,0x227f,
+ 0x2280,0x2281,0x2282,0x2283,0x2284,0x2285,0x2286,0x2287,
+ 0x2288,0x2289,0x228a,0x228b,0x228c,0x228d,0x228e,0x228f,
+ 0x2290,0x2291,0x2292,0x2293,0x2294,0x2295,0x2296,0x2297,
+ 0x2298,0x2299,0x229a,0x229b,0x229c,0x229d,0x229e,0x229f,
+ 0x22a0,0x22a1,0x22a2,0x22a3,0x22a4,0x22a5,0x22a6,0x22a7,
+ 0x22a8,0x22a9,0x22aa,0x22ab,0x22ac,0x22ad,0x22ae,0x22af,
+ 0x22b0,0x22b1,0x22b2,0x22b3,0x22b4,0x22b5,0x22b6,0x22b7,
+ 0x22b8,0x22b9,0x22ba,0x22bb,0x22bc,0x22bd,0x22be,0x22bf,
+ 0x22c0,0x22c1,0x22c2,0x22c3,0x22c4,0x22c5,0x22c6,0x22c7,
+ 0x22c8,0x22c9,0x22ca,0x22cb,0x22cc,0x22cd,0x22ce,0x22cf,
+ 0x22d0,0x22d1,0x22d2,0x22d3,0x22d4,0x22d5,0x22d6,0x22d7,
+ 0x22d8,0x22d9,0x22da,0x22db,0x22dc,0x22dd,0x22de,0x22df,
+ 0x22e0,0x22e1,0x22e2,0x22e3,0x22e4,0x22e5,0x22e6,0x22e7,
+ 0x22e8,0x22e9,0x22ea,0x22eb,0x22ec,0x22ed,0x22ee,0x22ef,
+ 0x22f0,0x22f1,0x22f2,0x22f3,0x22f4,0x22f5,0x22f6,0x22f7,
+ 0x22f8,0x22f9,0x22fa,0x22fb,0x22fc,0x22fd,0x22fe,0x22ff,
+ 0x2300,0x2301,0x2302,0x2303,0x2304,0x2305,0x2306,0x2307,
+ 0x2308,0x2309,0x230a,0x230b,0x230c,0x230d,0x230e,0x230f,
+ 0x2310,0x2311,0x2312,0x2313,0x2314,0x2315,0x2316,0x2317,
+ 0x2318,0x2319,0x231a,0x231b,0x231c,0x231d,0x231e,0x231f,
+ 0x2320,0x2321,0x2322,0x2323,0x2324,0x2325,0x2326,0x2327,
+ 0x2328,0x2329,0x232a,0x232b,0x232c,0x232d,0x232e,0x232f,
+ 0x2330,0x2331,0x2332,0x2333,0x2334,0x2335,0x2336,0x2337,
+ 0x2338,0x2339,0x233a,0x233b,0x233c,0x233d,0x233e,0x233f,
+ 0x2340,0x2341,0x2342,0x2343,0x2344,0x2345,0x2346,0x2347,
+ 0x2348,0x2349,0x234a,0x234b,0x234c,0x234d,0x234e,0x234f,
+ 0x2350,0x2351,0x2352,0x2353,0x2354,0x2355,0x2356,0x2357,
+ 0x2358,0x2359,0x235a,0x235b,0x235c,0x235d,0x235e,0x235f,
+ 0x2360,0x2361,0x2362,0x2363,0x2364,0x2365,0x2366,0x2367,
+ 0x2368,0x2369,0x236a,0x236b,0x236c,0x236d,0x236e,0x236f,
+ 0x2370,0x2371,0x2372,0x2373,0x2374,0x2375,0x2376,0x2377,
+ 0x2378,0x2379,0x237a,0x237b,0x237c,0x237d,0x237e,0x237f,
+ 0x2380,0x2381,0x2382,0x2383,0x2384,0x2385,0x2386,0x2387,
+ 0x2388,0x2389,0x238a,0x238b,0x238c,0x238d,0x238e,0x238f,
+ 0x2390,0x2391,0x2392,0x2393,0x2394,0x2395,0x2396,0x2397,
+ 0x2398,0x2399,0x239a,0x239b,0x239c,0x239d,0x239e,0x239f,
+ 0x23a0,0x23a1,0x23a2,0x23a3,0x23a4,0x23a5,0x23a6,0x23a7,
+ 0x23a8,0x23a9,0x23aa,0x23ab,0x23ac,0x23ad,0x23ae,0x23af,
+ 0x23b0,0x23b1,0x23b2,0x23b3,0x23b4,0x23b5,0x23b6,0x23b7,
+ 0x23b8,0x23b9,0x23ba,0x23bb,0x23bc,0x23bd,0x23be,0x23bf,
+ 0x23c0,0x23c1,0x23c2,0x23c3,0x23c4,0x23c5,0x23c6,0x23c7,
+ 0x23c8,0x23c9,0x23ca,0x23cb,0x23cc,0x23cd,0x23ce,0x23cf,
+ 0x23d0,0x23d1,0x23d2,0x23d3,0x23d4,0x23d5,0x23d6,0x23d7,
+ 0x23d8,0x23d9,0x23da,0x23db,0x23dc,0x23dd,0x23de,0x23df,
+ 0x23e0,0x23e1,0x23e2,0x23e3,0x23e4,0x23e5,0x23e6,0x23e7,
+ 0x23e8,0x23e9,0x23ea,0x23eb,0x23ec,0x23ed,0x23ee,0x23ef,
+ 0x23f0,0x23f1,0x23f2,0x23f3,0x23f4,0x23f5,0x23f6,0x23f7,
+ 0x23f8,0x23f9,0x23fa,0x23fb,0x23fc,0x23fd,0x23fe,0x23ff,
+ 0x2400,0x2401,0x2402,0x2403,0x2404,0x2405,0x2406,0x2407,
+ 0x2408,0x2409,0x240a,0x240b,0x240c,0x240d,0x240e,0x240f,
+ 0x2410,0x2411,0x2412,0x2413,0x2414,0x2415,0x2416,0x2417,
+ 0x2418,0x2419,0x241a,0x241b,0x241c,0x241d,0x241e,0x241f,
+ 0x2420,0x2421,0x2422,0x2423,0x2424,0x2425,0x2426,0x2427,
+ 0x2428,0x2429,0x242a,0x242b,0x242c,0x242d,0x242e,0x242f,
+ 0x2430,0x2431,0x2432,0x2433,0x2434,0x2435,0x2436,0x2437,
+ 0x2438,0x2439,0x243a,0x243b,0x243c,0x243d,0x243e,0x243f,
+ 0x2440,0x2441,0x2442,0x2443,0x2444,0x2445,0x2446,0x2447,
+ 0x2448,0x2449,0x244a,0x244b,0x244c,0x244d,0x244e,0x244f,
+ 0x2450,0x2451,0x2452,0x2453,0x2454,0x2455,0x2456,0x2457,
+ 0x2458,0x2459,0x245a,0x245b,0x245c,0x245d,0x245e,0x245f,
+ 0x2460,0x2461,0x2462,0x2463,0x2464,0x2465,0x2466,0x2467,
+ 0x2468,0x2469,0x246a,0x246b,0x246c,0x246d,0x246e,0x246f,
+ 0x2470,0x2471,0x2472,0x2473,0x2474,0x2475,0x2476,0x2477,
+ 0x2478,0x2479,0x247a,0x247b,0x247c,0x247d,0x247e,0x247f,
+ 0x2480,0x2481,0x2482,0x2483,0x2484,0x2485,0x2486,0x2487,
+ 0x2488,0x2489,0x248a,0x248b,0x248c,0x248d,0x248e,0x248f,
+ 0x2490,0x2491,0x2492,0x2493,0x2494,0x2495,0x2496,0x2497,
+ 0x2498,0x2499,0x249a,0x249b,0x249c,0x249d,0x249e,0x249f,
+ 0x24a0,0x24a1,0x24a2,0x24a3,0x24a4,0x24a5,0x24a6,0x24a7,
+ 0x24a8,0x24a9,0x24aa,0x24ab,0x24ac,0x24ad,0x24ae,0x24af,
+ 0x24b0,0x24b1,0x24b2,0x24b3,0x24b4,0x24b5,0x24b6,0x24b7,
+ 0x24b8,0x24b9,0x24ba,0x24bb,0x24bc,0x24bd,0x24be,0x24bf,
+ 0x24c0,0x24c1,0x24c2,0x24c3,0x24c4,0x24c5,0x24c6,0x24c7,
+ 0x24c8,0x24c9,0x24ca,0x24cb,0x24cc,0x24cd,0x24ce,0x24cf,
+ 0x24b6,0x24b7,0x24b8,0x24b9,0x24ba,0x24bb,0x24bc,0x24bd,
+ 0x24be,0x24bf,0x24c0,0x24c1,0x24c2,0x24c3,0x24c4,0x24c5,
+ 0x24c6,0x24c7,0x24c8,0x24c9,0x24ca,0x24cb,0x24cc,0x24cd,
+ 0x24ce,0x24cf,0x24ea,0x24eb,0x24ec,0x24ed,0x24ee,0x24ef,
+ 0x24f0,0x24f1,0x24f2,0x24f3,0x24f4,0x24f5,0x24f6,0x24f7,
+ 0x24f8,0x24f9,0x24fa,0x24fb,0x24fc,0x24fd,0x24fe,0x24ff,
+ 0x2500,0x2501,0x2502,0x2503,0x2504,0x2505,0x2506,0x2507,
+ 0x2508,0x2509,0x250a,0x250b,0x250c,0x250d,0x250e,0x250f,
+ 0x2510,0x2511,0x2512,0x2513,0x2514,0x2515,0x2516,0x2517,
+ 0x2518,0x2519,0x251a,0x251b,0x251c,0x251d,0x251e,0x251f,
+ 0x2520,0x2521,0x2522,0x2523,0x2524,0x2525,0x2526,0x2527,
+ 0x2528,0x2529,0x252a,0x252b,0x252c,0x252d,0x252e,0x252f,
+ 0x2530,0x2531,0x2532,0x2533,0x2534,0x2535,0x2536,0x2537,
+ 0x2538,0x2539,0x253a,0x253b,0x253c,0x253d,0x253e,0x253f,
+ 0x2540,0x2541,0x2542,0x2543,0x2544,0x2545,0x2546,0x2547,
+ 0x2548,0x2549,0x254a,0x254b,0x254c,0x254d,0x254e,0x254f,
+ 0x2550,0x2551,0x2552,0x2553,0x2554,0x2555,0x2556,0x2557,
+ 0x2558,0x2559,0x255a,0x255b,0x255c,0x255d,0x255e,0x255f,
+ 0x2560,0x2561,0x2562,0x2563,0x2564,0x2565,0x2566,0x2567,
+ 0x2568,0x2569,0x256a,0x256b,0x256c,0x256d,0x256e,0x256f,
+ 0x2570,0x2571,0x2572,0x2573,0x2574,0x2575,0x2576,0x2577,
+ 0x2578,0x2579,0x257a,0x257b,0x257c,0x257d,0x257e,0x257f,
+ 0x2580,0x2581,0x2582,0x2583,0x2584,0x2585,0x2586,0x2587,
+ 0x2588,0x2589,0x258a,0x258b,0x258c,0x258d,0x258e,0x258f,
+ 0x2590,0x2591,0x2592,0x2593,0x2594,0x2595,0x2596,0x2597,
+ 0x2598,0x2599,0x259a,0x259b,0x259c,0x259d,0x259e,0x259f,
+ 0x25a0,0x25a1,0x25a2,0x25a3,0x25a4,0x25a5,0x25a6,0x25a7,
+ 0x25a8,0x25a9,0x25aa,0x25ab,0x25ac,0x25ad,0x25ae,0x25af,
+ 0x25b0,0x25b1,0x25b2,0x25b3,0x25b4,0x25b5,0x25b6,0x25b7,
+ 0x25b8,0x25b9,0x25ba,0x25bb,0x25bc,0x25bd,0x25be,0x25bf,
+ 0x25c0,0x25c1,0x25c2,0x25c3,0x25c4,0x25c5,0x25c6,0x25c7,
+ 0x25c8,0x25c9,0x25ca,0x25cb,0x25cc,0x25cd,0x25ce,0x25cf,
+ 0x25d0,0x25d1,0x25d2,0x25d3,0x25d4,0x25d5,0x25d6,0x25d7,
+ 0x25d8,0x25d9,0x25da,0x25db,0x25dc,0x25dd,0x25de,0x25df,
+ 0x25e0,0x25e1,0x25e2,0x25e3,0x25e4,0x25e5,0x25e6,0x25e7,
+ 0x25e8,0x25e9,0x25ea,0x25eb,0x25ec,0x25ed,0x25ee,0x25ef,
+ 0x25f0,0x25f1,0x25f2,0x25f3,0x25f4,0x25f5,0x25f6,0x25f7,
+ 0x25f8,0x25f9,0x25fa,0x25fb,0x25fc,0x25fd,0x25fe,0x25ff,
+ 0x2600,0x2601,0x2602,0x2603,0x2604,0x2605,0x2606,0x2607,
+ 0x2608,0x2609,0x260a,0x260b,0x260c,0x260d,0x260e,0x260f,
+ 0x2610,0x2611,0x2612,0x2613,0x2614,0x2615,0x2616,0x2617,
+ 0x2618,0x2619,0x261a,0x261b,0x261c,0x261d,0x261e,0x261f,
+ 0x2620,0x2621,0x2622,0x2623,0x2624,0x2625,0x2626,0x2627,
+ 0x2628,0x2629,0x262a,0x262b,0x262c,0x262d,0x262e,0x262f,
+ 0x2630,0x2631,0x2632,0x2633,0x2634,0x2635,0x2636,0x2637,
+ 0x2638,0x2639,0x263a,0x263b,0x263c,0x263d,0x263e,0x263f,
+ 0x2640,0x2641,0x2642,0x2643,0x2644,0x2645,0x2646,0x2647,
+ 0x2648,0x2649,0x264a,0x264b,0x264c,0x264d,0x264e,0x264f,
+ 0x2650,0x2651,0x2652,0x2653,0x2654,0x2655,0x2656,0x2657,
+ 0x2658,0x2659,0x265a,0x265b,0x265c,0x265d,0x265e,0x265f,
+ 0x2660,0x2661,0x2662,0x2663,0x2664,0x2665,0x2666,0x2667,
+ 0x2668,0x2669,0x266a,0x266b,0x266c,0x266d,0x266e,0x266f,
+ 0x2670,0x2671,0x2672,0x2673,0x2674,0x2675,0x2676,0x2677,
+ 0x2678,0x2679,0x267a,0x267b,0x267c,0x267d,0x267e,0x267f,
+ 0x2680,0x2681,0x2682,0x2683,0x2684,0x2685,0x2686,0x2687,
+ 0x2688,0x2689,0x268a,0x268b,0x268c,0x268d,0x268e,0x268f,
+ 0x2690,0x2691,0x2692,0x2693,0x2694,0x2695,0x2696,0x2697,
+ 0x2698,0x2699,0x269a,0x269b,0x269c,0x269d,0x269e,0x269f,
+ 0x26a0,0x26a1,0x26a2,0x26a3,0x26a4,0x26a5,0x26a6,0x26a7,
+ 0x26a8,0x26a9,0x26aa,0x26ab,0x26ac,0x26ad,0x26ae,0x26af,
+ 0x26b0,0x26b1,0x26b2,0x26b3,0x26b4,0x26b5,0x26b6,0x26b7,
+ 0x26b8,0x26b9,0x26ba,0x26bb,0x26bc,0x26bd,0x26be,0x26bf,
+ 0x26c0,0x26c1,0x26c2,0x26c3,0x26c4,0x26c5,0x26c6,0x26c7,
+ 0x26c8,0x26c9,0x26ca,0x26cb,0x26cc,0x26cd,0x26ce,0x26cf,
+ 0x26d0,0x26d1,0x26d2,0x26d3,0x26d4,0x26d5,0x26d6,0x26d7,
+ 0x26d8,0x26d9,0x26da,0x26db,0x26dc,0x26dd,0x26de,0x26df,
+ 0x26e0,0x26e1,0x26e2,0x26e3,0x26e4,0x26e5,0x26e6,0x26e7,
+ 0x26e8,0x26e9,0x26ea,0x26eb,0x26ec,0x26ed,0x26ee,0x26ef,
+ 0x26f0,0x26f1,0x26f2,0x26f3,0x26f4,0x26f5,0x26f6,0x26f7,
+ 0x26f8,0x26f9,0x26fa,0x26fb,0x26fc,0x26fd,0x26fe,0x26ff,
+ 0x2700,0x2701,0x2702,0x2703,0x2704,0x2705,0x2706,0x2707,
+ 0x2708,0x2709,0x270a,0x270b,0x270c,0x270d,0x270e,0x270f,
+ 0x2710,0x2711,0x2712,0x2713,0x2714,0x2715,0x2716,0x2717,
+ 0x2718,0x2719,0x271a,0x271b,0x271c,0x271d,0x271e,0x271f,
+ 0x2720,0x2721,0x2722,0x2723,0x2724,0x2725,0x2726,0x2727,
+ 0x2728,0x2729,0x272a,0x272b,0x272c,0x272d,0x272e,0x272f,
+ 0x2730,0x2731,0x2732,0x2733,0x2734,0x2735,0x2736,0x2737,
+ 0x2738,0x2739,0x273a,0x273b,0x273c,0x273d,0x273e,0x273f,
+ 0x2740,0x2741,0x2742,0x2743,0x2744,0x2745,0x2746,0x2747,
+ 0x2748,0x2749,0x274a,0x274b,0x274c,0x274d,0x274e,0x274f,
+ 0x2750,0x2751,0x2752,0x2753,0x2754,0x2755,0x2756,0x2757,
+ 0x2758,0x2759,0x275a,0x275b,0x275c,0x275d,0x275e,0x275f,
+ 0x2760,0x2761,0x2762,0x2763,0x2764,0x2765,0x2766,0x2767,
+ 0x2768,0x2769,0x276a,0x276b,0x276c,0x276d,0x276e,0x276f,
+ 0x2770,0x2771,0x2772,0x2773,0x2774,0x2775,0x2776,0x2777,
+ 0x2778,0x2779,0x277a,0x277b,0x277c,0x277d,0x277e,0x277f,
+ 0x2780,0x2781,0x2782,0x2783,0x2784,0x2785,0x2786,0x2787,
+ 0x2788,0x2789,0x278a,0x278b,0x278c,0x278d,0x278e,0x278f,
+ 0x2790,0x2791,0x2792,0x2793,0x2794,0x2795,0x2796,0x2797,
+ 0x2798,0x2799,0x279a,0x279b,0x279c,0x279d,0x279e,0x279f,
+ 0x27a0,0x27a1,0x27a2,0x27a3,0x27a4,0x27a5,0x27a6,0x27a7,
+ 0x27a8,0x27a9,0x27aa,0x27ab,0x27ac,0x27ad,0x27ae,0x27af,
+ 0x27b0,0x27b1,0x27b2,0x27b3,0x27b4,0x27b5,0x27b6,0x27b7,
+ 0x27b8,0x27b9,0x27ba,0x27bb,0x27bc,0x27bd,0x27be,0x27bf,
+ 0x27c0,0x27c1,0x27c2,0x27c3,0x27c4,0x27c5,0x27c6,0x27c7,
+ 0x27c8,0x27c9,0x27ca,0x27cb,0x27cc,0x27cd,0x27ce,0x27cf,
+ 0x27d0,0x27d1,0x27d2,0x27d3,0x27d4,0x27d5,0x27d6,0x27d7,
+ 0x27d8,0x27d9,0x27da,0x27db,0x27dc,0x27dd,0x27de,0x27df,
+ 0x27e0,0x27e1,0x27e2,0x27e3,0x27e4,0x27e5,0x27e6,0x27e7,
+ 0x27e8,0x27e9,0x27ea,0x27eb,0x27ec,0x27ed,0x27ee,0x27ef,
+ 0x27f0,0x27f1,0x27f2,0x27f3,0x27f4,0x27f5,0x27f6,0x27f7,
+ 0x27f8,0x27f9,0x27fa,0x27fb,0x27fc,0x27fd,0x27fe,0x27ff,
+ 0x2800,0x2801,0x2802,0x2803,0x2804,0x2805,0x2806,0x2807,
+ 0x2808,0x2809,0x280a,0x280b,0x280c,0x280d,0x280e,0x280f,
+ 0x2810,0x2811,0x2812,0x2813,0x2814,0x2815,0x2816,0x2817,
+ 0x2818,0x2819,0x281a,0x281b,0x281c,0x281d,0x281e,0x281f,
+ 0x2820,0x2821,0x2822,0x2823,0x2824,0x2825,0x2826,0x2827,
+ 0x2828,0x2829,0x282a,0x282b,0x282c,0x282d,0x282e,0x282f,
+ 0x2830,0x2831,0x2832,0x2833,0x2834,0x2835,0x2836,0x2837,
+ 0x2838,0x2839,0x283a,0x283b,0x283c,0x283d,0x283e,0x283f,
+ 0x2840,0x2841,0x2842,0x2843,0x2844,0x2845,0x2846,0x2847,
+ 0x2848,0x2849,0x284a,0x284b,0x284c,0x284d,0x284e,0x284f,
+ 0x2850,0x2851,0x2852,0x2853,0x2854,0x2855,0x2856,0x2857,
+ 0x2858,0x2859,0x285a,0x285b,0x285c,0x285d,0x285e,0x285f,
+ 0x2860,0x2861,0x2862,0x2863,0x2864,0x2865,0x2866,0x2867,
+ 0x2868,0x2869,0x286a,0x286b,0x286c,0x286d,0x286e,0x286f,
+ 0x2870,0x2871,0x2872,0x2873,0x2874,0x2875,0x2876,0x2877,
+ 0x2878,0x2879,0x287a,0x287b,0x287c,0x287d,0x287e,0x287f,
+ 0x2880,0x2881,0x2882,0x2883,0x2884,0x2885,0x2886,0x2887,
+ 0x2888,0x2889,0x288a,0x288b,0x288c,0x288d,0x288e,0x288f,
+ 0x2890,0x2891,0x2892,0x2893,0x2894,0x2895,0x2896,0x2897,
+ 0x2898,0x2899,0x289a,0x289b,0x289c,0x289d,0x289e,0x289f,
+ 0x28a0,0x28a1,0x28a2,0x28a3,0x28a4,0x28a5,0x28a6,0x28a7,
+ 0x28a8,0x28a9,0x28aa,0x28ab,0x28ac,0x28ad,0x28ae,0x28af,
+ 0x28b0,0x28b1,0x28b2,0x28b3,0x28b4,0x28b5,0x28b6,0x28b7,
+ 0x28b8,0x28b9,0x28ba,0x28bb,0x28bc,0x28bd,0x28be,0x28bf,
+ 0x28c0,0x28c1,0x28c2,0x28c3,0x28c4,0x28c5,0x28c6,0x28c7,
+ 0x28c8,0x28c9,0x28ca,0x28cb,0x28cc,0x28cd,0x28ce,0x28cf,
+ 0x28d0,0x28d1,0x28d2,0x28d3,0x28d4,0x28d5,0x28d6,0x28d7,
+ 0x28d8,0x28d9,0x28da,0x28db,0x28dc,0x28dd,0x28de,0x28df,
+ 0x28e0,0x28e1,0x28e2,0x28e3,0x28e4,0x28e5,0x28e6,0x28e7,
+ 0x28e8,0x28e9,0x28ea,0x28eb,0x28ec,0x28ed,0x28ee,0x28ef,
+ 0x28f0,0x28f1,0x28f2,0x28f3,0x28f4,0x28f5,0x28f6,0x28f7,
+ 0x28f8,0x28f9,0x28fa,0x28fb,0x28fc,0x28fd,0x28fe,0x28ff,
+ 0x2900,0x2901,0x2902,0x2903,0x2904,0x2905,0x2906,0x2907,
+ 0x2908,0x2909,0x290a,0x290b,0x290c,0x290d,0x290e,0x290f,
+ 0x2910,0x2911,0x2912,0x2913,0x2914,0x2915,0x2916,0x2917,
+ 0x2918,0x2919,0x291a,0x291b,0x291c,0x291d,0x291e,0x291f,
+ 0x2920,0x2921,0x2922,0x2923,0x2924,0x2925,0x2926,0x2927,
+ 0x2928,0x2929,0x292a,0x292b,0x292c,0x292d,0x292e,0x292f,
+ 0x2930,0x2931,0x2932,0x2933,0x2934,0x2935,0x2936,0x2937,
+ 0x2938,0x2939,0x293a,0x293b,0x293c,0x293d,0x293e,0x293f,
+ 0x2940,0x2941,0x2942,0x2943,0x2944,0x2945,0x2946,0x2947,
+ 0x2948,0x2949,0x294a,0x294b,0x294c,0x294d,0x294e,0x294f,
+ 0x2950,0x2951,0x2952,0x2953,0x2954,0x2955,0x2956,0x2957,
+ 0x2958,0x2959,0x295a,0x295b,0x295c,0x295d,0x295e,0x295f,
+ 0x2960,0x2961,0x2962,0x2963,0x2964,0x2965,0x2966,0x2967,
+ 0x2968,0x2969,0x296a,0x296b,0x296c,0x296d,0x296e,0x296f,
+ 0x2970,0x2971,0x2972,0x2973,0x2974,0x2975,0x2976,0x2977,
+ 0x2978,0x2979,0x297a,0x297b,0x297c,0x297d,0x297e,0x297f,
+ 0x2980,0x2981,0x2982,0x2983,0x2984,0x2985,0x2986,0x2987,
+ 0x2988,0x2989,0x298a,0x298b,0x298c,0x298d,0x298e,0x298f,
+ 0x2990,0x2991,0x2992,0x2993,0x2994,0x2995,0x2996,0x2997,
+ 0x2998,0x2999,0x299a,0x299b,0x299c,0x299d,0x299e,0x299f,
+ 0x29a0,0x29a1,0x29a2,0x29a3,0x29a4,0x29a5,0x29a6,0x29a7,
+ 0x29a8,0x29a9,0x29aa,0x29ab,0x29ac,0x29ad,0x29ae,0x29af,
+ 0x29b0,0x29b1,0x29b2,0x29b3,0x29b4,0x29b5,0x29b6,0x29b7,
+ 0x29b8,0x29b9,0x29ba,0x29bb,0x29bc,0x29bd,0x29be,0x29bf,
+ 0x29c0,0x29c1,0x29c2,0x29c3,0x29c4,0x29c5,0x29c6,0x29c7,
+ 0x29c8,0x29c9,0x29ca,0x29cb,0x29cc,0x29cd,0x29ce,0x29cf,
+ 0x29d0,0x29d1,0x29d2,0x29d3,0x29d4,0x29d5,0x29d6,0x29d7,
+ 0x29d8,0x29d9,0x29da,0x29db,0x29dc,0x29dd,0x29de,0x29df,
+ 0x29e0,0x29e1,0x29e2,0x29e3,0x29e4,0x29e5,0x29e6,0x29e7,
+ 0x29e8,0x29e9,0x29ea,0x29eb,0x29ec,0x29ed,0x29ee,0x29ef,
+ 0x29f0,0x29f1,0x29f2,0x29f3,0x29f4,0x29f5,0x29f6,0x29f7,
+ 0x29f8,0x29f9,0x29fa,0x29fb,0x29fc,0x29fd,0x29fe,0x29ff,
+ 0x2a00,0x2a01,0x2a02,0x2a03,0x2a04,0x2a05,0x2a06,0x2a07,
+ 0x2a08,0x2a09,0x2a0a,0x2a0b,0x2a0c,0x2a0d,0x2a0e,0x2a0f,
+ 0x2a10,0x2a11,0x2a12,0x2a13,0x2a14,0x2a15,0x2a16,0x2a17,
+ 0x2a18,0x2a19,0x2a1a,0x2a1b,0x2a1c,0x2a1d,0x2a1e,0x2a1f,
+ 0x2a20,0x2a21,0x2a22,0x2a23,0x2a24,0x2a25,0x2a26,0x2a27,
+ 0x2a28,0x2a29,0x2a2a,0x2a2b,0x2a2c,0x2a2d,0x2a2e,0x2a2f,
+ 0x2a30,0x2a31,0x2a32,0x2a33,0x2a34,0x2a35,0x2a36,0x2a37,
+ 0x2a38,0x2a39,0x2a3a,0x2a3b,0x2a3c,0x2a3d,0x2a3e,0x2a3f,
+ 0x2a40,0x2a41,0x2a42,0x2a43,0x2a44,0x2a45,0x2a46,0x2a47,
+ 0x2a48,0x2a49,0x2a4a,0x2a4b,0x2a4c,0x2a4d,0x2a4e,0x2a4f,
+ 0x2a50,0x2a51,0x2a52,0x2a53,0x2a54,0x2a55,0x2a56,0x2a57,
+ 0x2a58,0x2a59,0x2a5a,0x2a5b,0x2a5c,0x2a5d,0x2a5e,0x2a5f,
+ 0x2a60,0x2a61,0x2a62,0x2a63,0x2a64,0x2a65,0x2a66,0x2a67,
+ 0x2a68,0x2a69,0x2a6a,0x2a6b,0x2a6c,0x2a6d,0x2a6e,0x2a6f,
+ 0x2a70,0x2a71,0x2a72,0x2a73,0x2a74,0x2a75,0x2a76,0x2a77,
+ 0x2a78,0x2a79,0x2a7a,0x2a7b,0x2a7c,0x2a7d,0x2a7e,0x2a7f,
+ 0x2a80,0x2a81,0x2a82,0x2a83,0x2a84,0x2a85,0x2a86,0x2a87,
+ 0x2a88,0x2a89,0x2a8a,0x2a8b,0x2a8c,0x2a8d,0x2a8e,0x2a8f,
+ 0x2a90,0x2a91,0x2a92,0x2a93,0x2a94,0x2a95,0x2a96,0x2a97,
+ 0x2a98,0x2a99,0x2a9a,0x2a9b,0x2a9c,0x2a9d,0x2a9e,0x2a9f,
+ 0x2aa0,0x2aa1,0x2aa2,0x2aa3,0x2aa4,0x2aa5,0x2aa6,0x2aa7,
+ 0x2aa8,0x2aa9,0x2aaa,0x2aab,0x2aac,0x2aad,0x2aae,0x2aaf,
+ 0x2ab0,0x2ab1,0x2ab2,0x2ab3,0x2ab4,0x2ab5,0x2ab6,0x2ab7,
+ 0x2ab8,0x2ab9,0x2aba,0x2abb,0x2abc,0x2abd,0x2abe,0x2abf,
+ 0x2ac0,0x2ac1,0x2ac2,0x2ac3,0x2ac4,0x2ac5,0x2ac6,0x2ac7,
+ 0x2ac8,0x2ac9,0x2aca,0x2acb,0x2acc,0x2acd,0x2ace,0x2acf,
+ 0x2ad0,0x2ad1,0x2ad2,0x2ad3,0x2ad4,0x2ad5,0x2ad6,0x2ad7,
+ 0x2ad8,0x2ad9,0x2ada,0x2adb,0x2adc,0x2add,0x2ade,0x2adf,
+ 0x2ae0,0x2ae1,0x2ae2,0x2ae3,0x2ae4,0x2ae5,0x2ae6,0x2ae7,
+ 0x2ae8,0x2ae9,0x2aea,0x2aeb,0x2aec,0x2aed,0x2aee,0x2aef,
+ 0x2af0,0x2af1,0x2af2,0x2af3,0x2af4,0x2af5,0x2af6,0x2af7,
+ 0x2af8,0x2af9,0x2afa,0x2afb,0x2afc,0x2afd,0x2afe,0x2aff,
+ 0x2b00,0x2b01,0x2b02,0x2b03,0x2b04,0x2b05,0x2b06,0x2b07,
+ 0x2b08,0x2b09,0x2b0a,0x2b0b,0x2b0c,0x2b0d,0x2b0e,0x2b0f,
+ 0x2b10,0x2b11,0x2b12,0x2b13,0x2b14,0x2b15,0x2b16,0x2b17,
+ 0x2b18,0x2b19,0x2b1a,0x2b1b,0x2b1c,0x2b1d,0x2b1e,0x2b1f,
+ 0x2b20,0x2b21,0x2b22,0x2b23,0x2b24,0x2b25,0x2b26,0x2b27,
+ 0x2b28,0x2b29,0x2b2a,0x2b2b,0x2b2c,0x2b2d,0x2b2e,0x2b2f,
+ 0x2b30,0x2b31,0x2b32,0x2b33,0x2b34,0x2b35,0x2b36,0x2b37,
+ 0x2b38,0x2b39,0x2b3a,0x2b3b,0x2b3c,0x2b3d,0x2b3e,0x2b3f,
+ 0x2b40,0x2b41,0x2b42,0x2b43,0x2b44,0x2b45,0x2b46,0x2b47,
+ 0x2b48,0x2b49,0x2b4a,0x2b4b,0x2b4c,0x2b4d,0x2b4e,0x2b4f,
+ 0x2b50,0x2b51,0x2b52,0x2b53,0x2b54,0x2b55,0x2b56,0x2b57,
+ 0x2b58,0x2b59,0x2b5a,0x2b5b,0x2b5c,0x2b5d,0x2b5e,0x2b5f,
+ 0x2b60,0x2b61,0x2b62,0x2b63,0x2b64,0x2b65,0x2b66,0x2b67,
+ 0x2b68,0x2b69,0x2b6a,0x2b6b,0x2b6c,0x2b6d,0x2b6e,0x2b6f,
+ 0x2b70,0x2b71,0x2b72,0x2b73,0x2b74,0x2b75,0x2b76,0x2b77,
+ 0x2b78,0x2b79,0x2b7a,0x2b7b,0x2b7c,0x2b7d,0x2b7e,0x2b7f,
+ 0x2b80,0x2b81,0x2b82,0x2b83,0x2b84,0x2b85,0x2b86,0x2b87,
+ 0x2b88,0x2b89,0x2b8a,0x2b8b,0x2b8c,0x2b8d,0x2b8e,0x2b8f,
+ 0x2b90,0x2b91,0x2b92,0x2b93,0x2b94,0x2b95,0x2b96,0x2b97,
+ 0x2b98,0x2b99,0x2b9a,0x2b9b,0x2b9c,0x2b9d,0x2b9e,0x2b9f,
+ 0x2ba0,0x2ba1,0x2ba2,0x2ba3,0x2ba4,0x2ba5,0x2ba6,0x2ba7,
+ 0x2ba8,0x2ba9,0x2baa,0x2bab,0x2bac,0x2bad,0x2bae,0x2baf,
+ 0x2bb0,0x2bb1,0x2bb2,0x2bb3,0x2bb4,0x2bb5,0x2bb6,0x2bb7,
+ 0x2bb8,0x2bb9,0x2bba,0x2bbb,0x2bbc,0x2bbd,0x2bbe,0x2bbf,
+ 0x2bc0,0x2bc1,0x2bc2,0x2bc3,0x2bc4,0x2bc5,0x2bc6,0x2bc7,
+ 0x2bc8,0x2bc9,0x2bca,0x2bcb,0x2bcc,0x2bcd,0x2bce,0x2bcf,
+ 0x2bd0,0x2bd1,0x2bd2,0x2bd3,0x2bd4,0x2bd5,0x2bd6,0x2bd7,
+ 0x2bd8,0x2bd9,0x2bda,0x2bdb,0x2bdc,0x2bdd,0x2bde,0x2bdf,
+ 0x2be0,0x2be1,0x2be2,0x2be3,0x2be4,0x2be5,0x2be6,0x2be7,
+ 0x2be8,0x2be9,0x2bea,0x2beb,0x2bec,0x2bed,0x2bee,0x2bef,
+ 0x2bf0,0x2bf1,0x2bf2,0x2bf3,0x2bf4,0x2bf5,0x2bf6,0x2bf7,
+ 0x2bf8,0x2bf9,0x2bfa,0x2bfb,0x2bfc,0x2bfd,0x2bfe,0x2bff,
+ 0x2c00,0x2c01,0x2c02,0x2c03,0x2c04,0x2c05,0x2c06,0x2c07,
+ 0x2c08,0x2c09,0x2c0a,0x2c0b,0x2c0c,0x2c0d,0x2c0e,0x2c0f,
+ 0x2c10,0x2c11,0x2c12,0x2c13,0x2c14,0x2c15,0x2c16,0x2c17,
+ 0x2c18,0x2c19,0x2c1a,0x2c1b,0x2c1c,0x2c1d,0x2c1e,0x2c1f,
+ 0x2c20,0x2c21,0x2c22,0x2c23,0x2c24,0x2c25,0x2c26,0x2c27,
+ 0x2c28,0x2c29,0x2c2a,0x2c2b,0x2c2c,0x2c2d,0x2c2e,0x2c2f,
+ 0x2c00,0x2c01,0x2c02,0x2c03,0x2c04,0x2c05,0x2c06,0x2c07,
+ 0x2c08,0x2c09,0x2c0a,0x2c0b,0x2c0c,0x2c0d,0x2c0e,0x2c0f,
+ 0x2c10,0x2c11,0x2c12,0x2c13,0x2c14,0x2c15,0x2c16,0x2c17,
+ 0x2c18,0x2c19,0x2c1a,0x2c1b,0x2c1c,0x2c1d,0x2c1e,0x2c1f,
+ 0x2c20,0x2c21,0x2c22,0x2c23,0x2c24,0x2c25,0x2c26,0x2c27,
+ 0x2c28,0x2c29,0x2c2a,0x2c2b,0x2c2c,0x2c2d,0x2c2e,0x2c5f,
+ 0x2c60,0x2c60,0x2c62,0x2c63,0x2c64,0x023a,0x023e,0x2c67,
+ 0x2c67,0x2c69,0x2c69,0x2c6b,0x2c6b,0x2c6d,0x2c6e,0x2c6f,
+ 0x2c70,0x2c71,0x2c72,0x2c73,0x2c74,0x2c75,0x2c75,0x2c77,
+ 0x2c78,0x2c79,0x2c7a,0x2c7b,0x2c7c,0x2c7d,0x2c7e,0x2c7f,
+ 0x2c80,0x2c80,0x2c82,0x2c82,0x2c84,0x2c84,0x2c86,0x2c86,
+ 0x2c88,0x2c88,0x2c8a,0x2c8a,0x2c8c,0x2c8c,0x2c8e,0x2c8e,
+ 0x2c90,0x2c90,0x2c92,0x2c92,0x2c94,0x2c94,0x2c96,0x2c96,
+ 0x2c98,0x2c98,0x2c9a,0x2c9a,0x2c9c,0x2c9c,0x2c9e,0x2c9e,
+ 0x2ca0,0x2ca0,0x2ca2,0x2ca2,0x2ca4,0x2ca4,0x2ca6,0x2ca6,
+ 0x2ca8,0x2ca8,0x2caa,0x2caa,0x2cac,0x2cac,0x2cae,0x2cae,
+ 0x2cb0,0x2cb0,0x2cb2,0x2cb2,0x2cb4,0x2cb4,0x2cb6,0x2cb6,
+ 0x2cb8,0x2cb8,0x2cba,0x2cba,0x2cbc,0x2cbc,0x2cbe,0x2cbe,
+ 0x2cc0,0x2cc0,0x2cc2,0x2cc2,0x2cc4,0x2cc4,0x2cc6,0x2cc6,
+ 0x2cc8,0x2cc8,0x2cca,0x2cca,0x2ccc,0x2ccc,0x2cce,0x2cce,
+ 0x2cd0,0x2cd0,0x2cd2,0x2cd2,0x2cd4,0x2cd4,0x2cd6,0x2cd6,
+ 0x2cd8,0x2cd8,0x2cda,0x2cda,0x2cdc,0x2cdc,0x2cde,0x2cde,
+ 0x2ce0,0x2ce0,0x2ce2,0x2ce2,0x2ce4,0x2ce5,0x2ce6,0x2ce7,
+ 0x2ce8,0x2ce9,0x2cea,0x2ceb,0x2cec,0x2ced,0x2cee,0x2cef,
+ 0x2cf0,0x2cf1,0x2cf2,0x2cf3,0x2cf4,0x2cf5,0x2cf6,0x2cf7,
+ 0x2cf8,0x2cf9,0x2cfa,0x2cfb,0x2cfc,0x2cfd,0x2cfe,0x2cff,
+ 0x10a0,0x10a1,0x10a2,0x10a3,0x10a4,0x10a5,0x10a6,0x10a7,
+ 0x10a8,0x10a9,0x10aa,0x10ab,0x10ac,0x10ad,0x10ae,0x10af,
+ 0x10b0,0x10b1,0x10b2,0x10b3,0x10b4,0x10b5,0x10b6,0x10b7,
+ 0x10b8,0x10b9,0x10ba,0x10bb,0x10bc,0x10bd,0x10be,0x10bf,
+ 0x10c0,0x10c1,0x10c2,0x10c3,0x10c4,0x10c5
+};
diff --git a/imap/src/charset/viscii.c b/imap/src/charset/viscii.c
new file mode 100644
index 00000000..9cc24e68
--- /dev/null
+++ b/imap/src/charset/viscii.c
@@ -0,0 +1,67 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: VISCII conversion table
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 2 June 1998
+ * Last Edited: 30 August 2006
+ */
+
+/* VISCII is the VIetnamese Standard Code for Information Interchange,
+ * defined by the Vietnamese Standardization Working Group, RFC-1456,
+ * May 1993.
+ */
+
+static const unsigned short visciitab[256] = {
+ 0x0000,0x0001,0x1eb2,0x0003,0x0004,0x1eb4,0x1eaa,0x0007,
+ 0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x000e,0x000f,
+ 0x0010,0x0011,0x0012,0x0013,0x1ef6,0x0015,0x0016,0x0017,
+ 0x0018,0x1ef8,0x001a,0x001b,0x001c,0x001d,0x1ef4,0x001f,
+ 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,
+ 0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f,
+ 0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,
+ 0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f,
+ 0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,
+ 0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,
+ 0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,
+ 0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f,
+ 0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,
+ 0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f,
+ 0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,
+ 0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x007f,
+ 0x1ea0,0x1eae,0x1eb0,0x1eb6,0x1ea4,0x1ea6,0x1ea8,0x1eac,
+ 0x1ebc,0x1eb8,0x1ebe,0x1ec0,0x1ec2,0x1ec4,0x1ec6,0x1ed0,
+ 0x1ed2,0x1ed4,0x1ed6,0x1ed8,0x1ee2,0x1eda,0x1edc,0x1ede,
+ 0x1eca,0x1ece,0x1ecc,0x1ec8,0x1ee6,0x0168,0x1ee4,0x1ef2,
+ 0x00d5,0x1eaf,0x1eb1,0x1eb7,0x1ea5,0x1ea7,0x1ea9,0x1ead,
+ 0x1ebd,0x1eb9,0x1ebf,0x1ec1,0x1ec3,0x1ec5,0x1ec7,0x1ed1,
+ 0x1ed3,0x1ed5,0x1ed7,0x1ee0,0x01a0,0x1ed9,0x1edd,0x1edf,
+ 0x1ecb,0x1ef0,0x1ee8,0x1eea,0x1eec,0x01a1,0x1edb,0x01af,
+ 0x00c0,0x00c1,0x00c2,0x00c3,0x1ea2,0x0102,0x1eb3,0x1eb5,
+ 0x00c8,0x00c9,0x00ca,0x1eba,0x00cc,0x00cd,0x0128,0x1ef3,
+ 0x0110,0x1ee9,0x00d2,0x00d3,0x00d4,0x1ea1,0x1ef7,0x1eeb,
+ 0x1eed,0x00d9,0x00da,0x1ef9,0x1ef5,0x00dd,0x1ee1,0x01b0,
+ 0x00e0,0x00e1,0x00e2,0x00e3,0x1ea3,0x0103,0x1eef,0x1eab,
+ 0x00e8,0x00e9,0x00ea,0x1ebb,0x00ec,0x00ed,0x0129,0x1ec9,
+ 0x0111,0x1ef1,0x00f2,0x00f3,0x00f4,0x00f5,0x1ecf,0x1ecd,
+ 0x1ee5,0x00f9,0x00fa,0x0169,0x1ee7,0x00fd,0x1ee3,0x1eee
+};
diff --git a/imap/src/charset/widths.c b/imap/src/charset/widths.c
new file mode 100644
index 00000000..3b0655a9
--- /dev/null
+++ b/imap/src/charset/widths.c
@@ -0,0 +1,4136 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Unicode width table (current as of Unicode 5.0)
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 20 March 2006
+ * Last Edited: 1 March 2007
+ */
+
+/* Table of 2-bit character widths, indexed by Unicode codepoint and
+ * big-endian within bytes:
+ * 0 zero-width
+ * 1 single-width (half-width)
+ * 2 double-width (full-width)
+ * 3 ambiguous
+ */
+
+#define UCS4_WIDLEN 131072
+
+static const unsigned char ucs4_widthtab[32768] = {
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* U+0000 - U+001f */
+ 0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55, /* U+0020 - U+003f */
+ 0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55, /* U+0040 - U+005f */
+ 0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x54, /* U+0060 - U+007f */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* U+0080 - U+009f */
+ 0xf5,0xd7,0xff,0x4d,0xff,0xff,0xff,0xff, /* U+00a0 - U+00bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+00c0 - U+00df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+00e0 - U+00ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0100 - U+011f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0120 - U+013f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0140 - U+015f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0160 - U+017f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0180 - U+019f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+01a0 - U+01bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+01c0 - U+01df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+01e0 - U+01ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0200 - U+021f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0220 - U+023f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0240 - U+025f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0260 - U+027f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0280 - U+029f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+02a0 - U+02bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+02c0 - U+02df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+02e0 - U+02ff */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* U+0300 - U+031f */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* U+0320 - U+033f */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* U+0340 - U+035f */
+ 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff, /* U+0360 - U+037f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0380 - U+039f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+03a0 - U+03bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+03c0 - U+03df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+03e0 - U+03ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0400 - U+041f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0420 - U+043f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0440 - U+045f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0460 - U+047f */
+ 0xfc,0x03,0x0f,0xff,0xff,0xff,0xff,0xff, /* U+0480 - U+049f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+04a0 - U+04bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+04c0 - U+04df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+04e0 - U+04ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0500 - U+051f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0520 - U+053f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0540 - U+055f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0560 - U+057f */
+ 0xff,0xff,0xff,0xff,0xc0,0x00,0x00,0x00, /* U+0580 - U+059f */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c, /* U+05a0 - U+05bf */
+ 0xc3,0x0c,0xff,0xff,0xff,0xff,0xff,0xff, /* U+05c0 - U+05df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+05e0 - U+05ff */
+ 0x00,0xff,0xff,0xff,0x00,0x0f,0xff,0xff, /* U+0600 - U+061f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0620 - U+063f */
+ 0xff,0xff,0xfc,0x00,0x00,0x00,0x00,0x03, /* U+0640 - U+065f */
+ 0xff,0xff,0xff,0xff,0x3f,0xff,0xff,0xff, /* U+0660 - U+067f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0680 - U+069f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+06a0 - U+06bf */
+ 0xff,0xff,0xff,0xff,0xff,0xf0,0x00,0x00, /* U+06c0 - U+06df */
+ 0x00,0x3c,0x30,0x0f,0xff,0xff,0xff,0xff, /* U+06e0 - U+06ff */
+ 0xff,0xff,0xff,0xfc,0xcf,0xff,0xff,0xff, /* U+0700 - U+071f */
+ 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, /* U+0720 - U+073f */
+ 0x00,0x00,0x03,0xff,0xff,0xff,0xff,0xff, /* U+0740 - U+075f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0760 - U+077f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0780 - U+079f */
+ 0xff,0xf0,0x00,0x00,0x3f,0xff,0xff,0xff, /* U+07a0 - U+07bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+07c0 - U+07df */
+ 0xff,0xff,0xfc,0x00,0x00,0xff,0xff,0xff, /* U+07e0 - U+07ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0800 - U+081f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0820 - U+083f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0840 - U+085f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0860 - U+087f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0880 - U+089f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+08a0 - U+08bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+08c0 - U+08df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+08e0 - U+08ff */
+ 0xc3,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0900 - U+091f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x3f, /* U+0920 - U+093f */
+ 0xc0,0x00,0x3f,0xcf,0xc0,0x3f,0xff,0xff, /* U+0940 - U+095f */
+ 0xf0,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0960 - U+097f */
+ 0xcf,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0980 - U+099f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x3f, /* U+09a0 - U+09bf */
+ 0xc0,0x3f,0xff,0xcf,0xff,0xff,0xff,0xff, /* U+09c0 - U+09df */
+ 0xf0,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+09e0 - U+09ff */
+ 0xc3,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0a00 - U+0a1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x3f, /* U+0a20 - U+0a3f */
+ 0xc3,0xfc,0x3c,0x0f,0xff,0xff,0xff,0xff, /* U+0a40 - U+0a5f */
+ 0xff,0xff,0xff,0xff,0x0f,0xff,0xff,0xff, /* U+0a60 - U+0a7f */
+ 0xc3,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0a80 - U+0a9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x3f, /* U+0aa0 - U+0abf */
+ 0xc0,0x0c,0x3f,0xcf,0xff,0xff,0xff,0xff, /* U+0ac0 - U+0adf */
+ 0xf0,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0ae0 - U+0aff */
+ 0xcf,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0b00 - U+0b1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x3c, /* U+0b20 - U+0b3f */
+ 0xc0,0xff,0xff,0xcf,0xff,0xf3,0xff,0xff, /* U+0b40 - U+0b5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0b60 - U+0b7f */
+ 0xf3,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0b80 - U+0b9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0ba0 - U+0bbf */
+ 0x3f,0xff,0xff,0xcf,0xff,0xff,0xff,0xff, /* U+0bc0 - U+0bdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0be0 - U+0bff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0c00 - U+0c1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf0, /* U+0c20 - U+0c3f */
+ 0x3f,0xf0,0x30,0x0f,0xff,0xc3,0xff,0xff, /* U+0c40 - U+0c5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0c60 - U+0c7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0c80 - U+0c9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x3f, /* U+0ca0 - U+0cbf */
+ 0xff,0xff,0xff,0x0f,0xff,0xff,0xff,0xff, /* U+0cc0 - U+0cdf */
+ 0xf0,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0ce0 - U+0cff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0d00 - U+0d1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0d20 - U+0d3f */
+ 0xc0,0xff,0xff,0xcf,0xff,0xff,0xff,0xff, /* U+0d40 - U+0d5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0d60 - U+0d7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0d80 - U+0d9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0da0 - U+0dbf */
+ 0xff,0xff,0xf3,0xff,0xf0,0x33,0xff,0xff, /* U+0dc0 - U+0ddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0de0 - U+0dff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0e00 - U+0e1f */
+ 0xff,0xff,0xff,0xff,0xcf,0x00,0x03,0xff, /* U+0e20 - U+0e3f */
+ 0xff,0xfc,0x00,0x03,0xff,0xff,0xff,0xff, /* U+0e40 - U+0e5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0e60 - U+0e7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0e80 - U+0e9f */
+ 0xff,0xff,0xff,0xff,0xcf,0x00,0x0c,0x3f, /* U+0ea0 - U+0ebf */
+ 0xff,0xff,0x00,0x0f,0xff,0xff,0xff,0xff, /* U+0ec0 - U+0edf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0ee0 - U+0eff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0x0f,0xff, /* U+0f00 - U+0f1f */
+ 0xff,0xff,0xff,0xff,0xff,0xcc,0xcf,0xff, /* U+0f20 - U+0f3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0f40 - U+0f5f */
+ 0xff,0xff,0xff,0xff,0xc0,0x00,0x00,0x03, /* U+0f60 - U+0f7f */
+ 0x00,0x30,0xff,0xff,0x00,0x00,0xc0,0x00, /* U+0f80 - U+0f9f */
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3f, /* U+0fa0 - U+0fbf */
+ 0xff,0xf3,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0fc0 - U+0fdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+0fe0 - U+0fff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1000 - U+101f */
+ 0xff,0xff,0xff,0xc0,0x33,0xf0,0xcf,0xff, /* U+1020 - U+103f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0x0f,0xff, /* U+1040 - U+105f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1060 - U+107f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1080 - U+109f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10a0 - U+10bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10c0 - U+10df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10e0 - U+10ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+1100 - U+111f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+1120 - U+113f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaf,0xfe, /* U+1140 - U+115f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1160 - U+117f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1180 - U+119f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11a0 - U+11bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11c0 - U+11df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11e0 - U+11ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1200 - U+121f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1220 - U+123f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1240 - U+125f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1260 - U+127f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1280 - U+129f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12a0 - U+12bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12c0 - U+12df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12e0 - U+12ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1300 - U+131f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1320 - U+133f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc, /* U+1340 - U+135f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1360 - U+137f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1380 - U+139f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13a0 - U+13bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13c0 - U+13df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13e0 - U+13ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1400 - U+141f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1420 - U+143f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1440 - U+145f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1460 - U+147f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1480 - U+149f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14a0 - U+14bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14c0 - U+14df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14e0 - U+14ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1500 - U+151f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1520 - U+153f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1540 - U+155f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1560 - U+157f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1580 - U+159f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15a0 - U+15bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15c0 - U+15df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15e0 - U+15ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1600 - U+161f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1620 - U+163f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1640 - U+165f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1660 - U+167f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1680 - U+169f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16a0 - U+16bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16c0 - U+16df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16e0 - U+16ff */
+ 0xff,0xff,0xff,0xff,0xf0,0x3f,0xff,0xff, /* U+1700 - U+171f */
+ 0xff,0xff,0xff,0xff,0xf0,0x3f,0xff,0xff, /* U+1720 - U+173f */
+ 0xff,0xff,0xff,0xff,0xf0,0xff,0xff,0xff, /* U+1740 - U+175f */
+ 0xff,0xff,0xff,0xff,0xf0,0xff,0xff,0xff, /* U+1760 - U+177f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1780 - U+179f */
+ 0xff,0xff,0xff,0xff,0xff,0x0c,0x00,0x0f, /* U+17a0 - U+17bf */
+ 0xff,0xf3,0xc0,0x00,0x00,0xff,0xff,0xcf, /* U+17c0 - U+17df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17e0 - U+17ff */
+ 0xff,0xff,0xfc,0x0f,0xff,0xff,0xff,0xff, /* U+1800 - U+181f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1820 - U+183f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1840 - U+185f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1860 - U+187f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1880 - U+189f */
+ 0xff,0xff,0xcf,0xff,0xff,0xff,0xff,0xff, /* U+18a0 - U+18bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18c0 - U+18df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18e0 - U+18ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1900 - U+191f */
+ 0x03,0xfc,0x00,0xff,0xf3,0xff,0xc0,0xff, /* U+1920 - U+193f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1940 - U+195f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1960 - U+197f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1980 - U+199f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19a0 - U+19bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19c0 - U+19df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19e0 - U+19ff */
+ 0xff,0xff,0xff,0xff,0xff,0xfc,0x3f,0xff, /* U+1a00 - U+1a1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a20 - U+1a3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a40 - U+1a5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a60 - U+1a7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a80 - U+1a9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1aa0 - U+1abf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ac0 - U+1adf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ae0 - U+1aff */
+ 0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b00 - U+1b1f */
+ 0xff,0xff,0xff,0xff,0xff,0x30,0x03,0x3f, /* U+1b20 - U+1b3f */
+ 0xf3,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b40 - U+1b5f */
+ 0xff,0xff,0xfc,0x00,0x00,0xff,0xff,0xff, /* U+1b60 - U+1b7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b80 - U+1b9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ba0 - U+1bbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bc0 - U+1bdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1be0 - U+1bff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c00 - U+1c1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c20 - U+1c3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c40 - U+1c5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c60 - U+1c7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c80 - U+1c9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ca0 - U+1cbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cc0 - U+1cdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ce0 - U+1cff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d00 - U+1d1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d20 - U+1d3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d40 - U+1d5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d60 - U+1d7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d80 - U+1d9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1da0 - U+1dbf */
+ 0x00,0x00,0x03,0xff,0xff,0xff,0xff,0xff, /* U+1dc0 - U+1ddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf0, /* U+1de0 - U+1dff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e00 - U+1e1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e20 - U+1e3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e40 - U+1e5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e60 - U+1e7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e80 - U+1e9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ea0 - U+1ebf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ec0 - U+1edf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ee0 - U+1eff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f00 - U+1f1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f20 - U+1f3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f40 - U+1f5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f60 - U+1f7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f80 - U+1f9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fa0 - U+1fbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fc0 - U+1fdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fe0 - U+1fff */
+ 0xff,0xff,0xfc,0x00,0xff,0xff,0xff,0xff, /* U+2000 - U+201f */
+ 0xff,0xff,0xf0,0x03,0xff,0xff,0xff,0xff, /* U+2020 - U+203f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2040 - U+205f */
+ 0x00,0xff,0xf0,0x00,0xff,0xff,0xff,0xff, /* U+2060 - U+207f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2080 - U+209f */
+ 0xff,0xff,0xdf,0xff,0xff,0xff,0xff,0xff, /* U+20a0 - U+20bf */
+ 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, /* U+20c0 - U+20df */
+ 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff, /* U+20e0 - U+20ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2100 - U+211f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2120 - U+213f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2140 - U+215f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2160 - U+217f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2180 - U+219f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+21a0 - U+21bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+21c0 - U+21df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+21e0 - U+21ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2200 - U+221f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2220 - U+223f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2240 - U+225f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2260 - U+227f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2280 - U+229f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+22a0 - U+22bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+22c0 - U+22df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+22e0 - U+22ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2300 - U+231f */
+ 0xff,0xff,0xeb,0xff,0xff,0xff,0xff,0xff, /* U+2320 - U+233f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2340 - U+235f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2360 - U+237f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2380 - U+239f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+23a0 - U+23bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+23c0 - U+23df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+23e0 - U+23ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2400 - U+241f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2420 - U+243f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2440 - U+245f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2460 - U+247f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2480 - U+249f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+24a0 - U+24bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+24c0 - U+24df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+24e0 - U+24ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2500 - U+251f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2520 - U+253f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2540 - U+255f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2560 - U+257f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2580 - U+259f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+25a0 - U+25bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+25c0 - U+25df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+25e0 - U+25ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2600 - U+261f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2620 - U+263f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2640 - U+265f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2660 - U+267f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2680 - U+269f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+26a0 - U+26bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+26c0 - U+26df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+26e0 - U+26ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2700 - U+271f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2720 - U+273f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2740 - U+275f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2760 - U+277f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2780 - U+279f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+27a0 - U+27bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+27c0 - U+27df */
+ 0xff,0xf5,0x55,0xff,0xff,0xff,0xff,0xff, /* U+27e0 - U+27ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2800 - U+281f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2820 - U+283f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2840 - U+285f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2860 - U+287f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2880 - U+289f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+28a0 - U+28bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+28c0 - U+28df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+28e0 - U+28ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2900 - U+291f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2920 - U+293f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2940 - U+295f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2960 - U+297f */
+ 0xff,0xd7,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2980 - U+299f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+29a0 - U+29bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+29c0 - U+29df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+29e0 - U+29ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2a00 - U+2a1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2a20 - U+2a3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2a40 - U+2a5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2a60 - U+2a7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2a80 - U+2a9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2aa0 - U+2abf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2ac0 - U+2adf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2ae0 - U+2aff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2b00 - U+2b1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2b20 - U+2b3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2b40 - U+2b5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2b60 - U+2b7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2b80 - U+2b9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2ba0 - U+2bbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2bc0 - U+2bdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2be0 - U+2bff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2c00 - U+2c1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2c20 - U+2c3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2c40 - U+2c5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2c60 - U+2c7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2c80 - U+2c9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2ca0 - U+2cbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2cc0 - U+2cdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2ce0 - U+2cff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2d00 - U+2d1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2d20 - U+2d3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2d40 - U+2d5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2d60 - U+2d7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2d80 - U+2d9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2da0 - U+2dbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2dc0 - U+2ddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2de0 - U+2dff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2e00 - U+2e1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2e20 - U+2e3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2e40 - U+2e5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+2e60 - U+2e7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xae,0xaa, /* U+2e80 - U+2e9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+2ea0 - U+2ebf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+2ec0 - U+2edf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xff,0xff,0xff, /* U+2ee0 - U+2eff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+2f00 - U+2f1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+2f20 - U+2f3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+2f40 - U+2f5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+2f60 - U+2f7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+2f80 - U+2f9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+2fa0 - U+2fbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaf,0xff,0xff, /* U+2fc0 - U+2fdf */
+ 0xff,0xff,0xff,0xff,0xaa,0xaa,0xaa,0xff, /* U+2fe0 - U+2fff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3000 - U+301f */
+ 0xaa,0xaa,0xa0,0x00,0xaa,0xaa,0xaa,0xab, /* U+3020 - U+303f */
+ 0xea,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3040 - U+305f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3060 - U+307f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xab,0xc2,0xaa, /* U+3080 - U+309f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+30a0 - U+30bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+30c0 - U+30df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+30e0 - U+30ff */
+ 0xff,0xea,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3100 - U+311f */
+ 0xaa,0xaa,0xaa,0xbf,0xea,0xaa,0xaa,0xaa, /* U+3120 - U+313f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3140 - U+315f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3160 - U+317f */
+ 0xaa,0xaa,0xaa,0xab,0xaa,0xaa,0xaa,0xaa, /* U+3180 - U+319f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xff,0xff, /* U+31a0 - U+31bf */
+ 0xaa,0xaa,0xaa,0xaa,0xff,0xff,0xff,0xff, /* U+31c0 - U+31df */
+ 0xff,0xff,0xff,0xff,0xaa,0xaa,0xaa,0xaa, /* U+31e0 - U+31ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xab, /* U+3200 - U+321f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3220 - U+323f */
+ 0xaa,0xff,0xff,0xff,0xaa,0xaa,0xaa,0xaa, /* U+3240 - U+325f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3260 - U+327f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3280 - U+329f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+32a0 - U+32bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+32c0 - U+32df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xab, /* U+32e0 - U+32ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3300 - U+331f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3320 - U+333f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3340 - U+335f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3360 - U+337f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3380 - U+339f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+33a0 - U+33bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+33c0 - U+33df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+33e0 - U+33ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3400 - U+341f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3420 - U+343f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3440 - U+345f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3460 - U+347f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3480 - U+349f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+34a0 - U+34bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+34c0 - U+34df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+34e0 - U+34ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3500 - U+351f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3520 - U+353f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3540 - U+355f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3560 - U+357f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3580 - U+359f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+35a0 - U+35bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+35c0 - U+35df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+35e0 - U+35ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3600 - U+361f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3620 - U+363f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3640 - U+365f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3660 - U+367f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3680 - U+369f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+36a0 - U+36bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+36c0 - U+36df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+36e0 - U+36ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3700 - U+371f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3720 - U+373f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3740 - U+375f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3760 - U+377f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3780 - U+379f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+37a0 - U+37bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+37c0 - U+37df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+37e0 - U+37ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3800 - U+381f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3820 - U+383f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3840 - U+385f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3860 - U+387f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3880 - U+389f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+38a0 - U+38bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+38c0 - U+38df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+38e0 - U+38ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3900 - U+391f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3920 - U+393f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3940 - U+395f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3960 - U+397f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3980 - U+399f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+39a0 - U+39bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+39c0 - U+39df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+39e0 - U+39ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3a00 - U+3a1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3a20 - U+3a3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3a40 - U+3a5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3a60 - U+3a7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3a80 - U+3a9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3aa0 - U+3abf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3ac0 - U+3adf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3ae0 - U+3aff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3b00 - U+3b1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3b20 - U+3b3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3b40 - U+3b5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3b60 - U+3b7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3b80 - U+3b9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3ba0 - U+3bbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3bc0 - U+3bdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3be0 - U+3bff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3c00 - U+3c1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3c20 - U+3c3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3c40 - U+3c5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3c60 - U+3c7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3c80 - U+3c9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3ca0 - U+3cbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3cc0 - U+3cdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3ce0 - U+3cff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3d00 - U+3d1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3d20 - U+3d3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3d40 - U+3d5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3d60 - U+3d7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3d80 - U+3d9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3da0 - U+3dbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3dc0 - U+3ddf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3de0 - U+3dff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3e00 - U+3e1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3e20 - U+3e3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3e40 - U+3e5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3e60 - U+3e7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3e80 - U+3e9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3ea0 - U+3ebf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3ec0 - U+3edf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3ee0 - U+3eff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3f00 - U+3f1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3f20 - U+3f3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3f40 - U+3f5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3f60 - U+3f7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3f80 - U+3f9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3fa0 - U+3fbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3fc0 - U+3fdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+3fe0 - U+3fff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4000 - U+401f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4020 - U+403f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4040 - U+405f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4060 - U+407f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4080 - U+409f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+40a0 - U+40bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+40c0 - U+40df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+40e0 - U+40ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4100 - U+411f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4120 - U+413f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4140 - U+415f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4160 - U+417f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4180 - U+419f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+41a0 - U+41bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+41c0 - U+41df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+41e0 - U+41ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4200 - U+421f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4220 - U+423f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4240 - U+425f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4260 - U+427f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4280 - U+429f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+42a0 - U+42bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+42c0 - U+42df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+42e0 - U+42ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4300 - U+431f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4320 - U+433f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4340 - U+435f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4360 - U+437f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4380 - U+439f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+43a0 - U+43bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+43c0 - U+43df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+43e0 - U+43ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4400 - U+441f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4420 - U+443f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4440 - U+445f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4460 - U+447f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4480 - U+449f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+44a0 - U+44bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+44c0 - U+44df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+44e0 - U+44ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4500 - U+451f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4520 - U+453f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4540 - U+455f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4560 - U+457f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4580 - U+459f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+45a0 - U+45bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+45c0 - U+45df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+45e0 - U+45ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4600 - U+461f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4620 - U+463f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4640 - U+465f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4660 - U+467f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4680 - U+469f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+46a0 - U+46bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+46c0 - U+46df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+46e0 - U+46ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4700 - U+471f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4720 - U+473f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4740 - U+475f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4760 - U+477f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4780 - U+479f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+47a0 - U+47bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+47c0 - U+47df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+47e0 - U+47ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4800 - U+481f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4820 - U+483f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4840 - U+485f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4860 - U+487f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4880 - U+489f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+48a0 - U+48bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+48c0 - U+48df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+48e0 - U+48ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4900 - U+491f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4920 - U+493f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4940 - U+495f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4960 - U+497f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4980 - U+499f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+49a0 - U+49bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+49c0 - U+49df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+49e0 - U+49ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4a00 - U+4a1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4a20 - U+4a3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4a40 - U+4a5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4a60 - U+4a7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4a80 - U+4a9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4aa0 - U+4abf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4ac0 - U+4adf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4ae0 - U+4aff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4b00 - U+4b1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4b20 - U+4b3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4b40 - U+4b5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4b60 - U+4b7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4b80 - U+4b9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4ba0 - U+4bbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4bc0 - U+4bdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4be0 - U+4bff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4c00 - U+4c1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4c20 - U+4c3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4c40 - U+4c5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4c60 - U+4c7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4c80 - U+4c9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4ca0 - U+4cbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4cc0 - U+4cdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4ce0 - U+4cff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4d00 - U+4d1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4d20 - U+4d3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4d40 - U+4d5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4d60 - U+4d7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4d80 - U+4d9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaf,0xff,0xff, /* U+4da0 - U+4dbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+4dc0 - U+4ddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+4de0 - U+4dff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4e00 - U+4e1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4e20 - U+4e3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4e40 - U+4e5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4e60 - U+4e7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4e80 - U+4e9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4ea0 - U+4ebf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4ec0 - U+4edf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4ee0 - U+4eff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4f00 - U+4f1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4f20 - U+4f3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4f40 - U+4f5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4f60 - U+4f7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4f80 - U+4f9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4fa0 - U+4fbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4fc0 - U+4fdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+4fe0 - U+4fff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5000 - U+501f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5020 - U+503f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5040 - U+505f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5060 - U+507f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5080 - U+509f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+50a0 - U+50bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+50c0 - U+50df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+50e0 - U+50ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5100 - U+511f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5120 - U+513f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5140 - U+515f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5160 - U+517f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5180 - U+519f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+51a0 - U+51bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+51c0 - U+51df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+51e0 - U+51ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5200 - U+521f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5220 - U+523f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5240 - U+525f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5260 - U+527f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5280 - U+529f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+52a0 - U+52bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+52c0 - U+52df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+52e0 - U+52ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5300 - U+531f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5320 - U+533f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5340 - U+535f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5360 - U+537f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5380 - U+539f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+53a0 - U+53bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+53c0 - U+53df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+53e0 - U+53ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5400 - U+541f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5420 - U+543f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5440 - U+545f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5460 - U+547f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5480 - U+549f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+54a0 - U+54bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+54c0 - U+54df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+54e0 - U+54ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5500 - U+551f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5520 - U+553f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5540 - U+555f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5560 - U+557f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5580 - U+559f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+55a0 - U+55bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+55c0 - U+55df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+55e0 - U+55ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5600 - U+561f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5620 - U+563f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5640 - U+565f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5660 - U+567f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5680 - U+569f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+56a0 - U+56bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+56c0 - U+56df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+56e0 - U+56ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5700 - U+571f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5720 - U+573f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5740 - U+575f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5760 - U+577f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5780 - U+579f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+57a0 - U+57bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+57c0 - U+57df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+57e0 - U+57ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5800 - U+581f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5820 - U+583f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5840 - U+585f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5860 - U+587f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5880 - U+589f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+58a0 - U+58bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+58c0 - U+58df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+58e0 - U+58ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5900 - U+591f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5920 - U+593f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5940 - U+595f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5960 - U+597f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5980 - U+599f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+59a0 - U+59bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+59c0 - U+59df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+59e0 - U+59ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5a00 - U+5a1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5a20 - U+5a3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5a40 - U+5a5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5a60 - U+5a7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5a80 - U+5a9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5aa0 - U+5abf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5ac0 - U+5adf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5ae0 - U+5aff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5b00 - U+5b1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5b20 - U+5b3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5b40 - U+5b5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5b60 - U+5b7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5b80 - U+5b9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5ba0 - U+5bbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5bc0 - U+5bdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5be0 - U+5bff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5c00 - U+5c1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5c20 - U+5c3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5c40 - U+5c5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5c60 - U+5c7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5c80 - U+5c9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5ca0 - U+5cbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5cc0 - U+5cdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5ce0 - U+5cff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5d00 - U+5d1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5d20 - U+5d3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5d40 - U+5d5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5d60 - U+5d7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5d80 - U+5d9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5da0 - U+5dbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5dc0 - U+5ddf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5de0 - U+5dff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5e00 - U+5e1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5e20 - U+5e3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5e40 - U+5e5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5e60 - U+5e7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5e80 - U+5e9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5ea0 - U+5ebf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5ec0 - U+5edf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5ee0 - U+5eff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5f00 - U+5f1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5f20 - U+5f3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5f40 - U+5f5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5f60 - U+5f7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5f80 - U+5f9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5fa0 - U+5fbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5fc0 - U+5fdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+5fe0 - U+5fff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6000 - U+601f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6020 - U+603f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6040 - U+605f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6060 - U+607f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6080 - U+609f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+60a0 - U+60bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+60c0 - U+60df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+60e0 - U+60ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6100 - U+611f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6120 - U+613f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6140 - U+615f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6160 - U+617f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6180 - U+619f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+61a0 - U+61bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+61c0 - U+61df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+61e0 - U+61ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6200 - U+621f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6220 - U+623f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6240 - U+625f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6260 - U+627f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6280 - U+629f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+62a0 - U+62bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+62c0 - U+62df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+62e0 - U+62ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6300 - U+631f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6320 - U+633f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6340 - U+635f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6360 - U+637f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6380 - U+639f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+63a0 - U+63bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+63c0 - U+63df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+63e0 - U+63ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6400 - U+641f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6420 - U+643f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6440 - U+645f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6460 - U+647f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6480 - U+649f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+64a0 - U+64bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+64c0 - U+64df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+64e0 - U+64ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6500 - U+651f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6520 - U+653f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6540 - U+655f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6560 - U+657f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6580 - U+659f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+65a0 - U+65bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+65c0 - U+65df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+65e0 - U+65ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6600 - U+661f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6620 - U+663f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6640 - U+665f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6660 - U+667f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6680 - U+669f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+66a0 - U+66bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+66c0 - U+66df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+66e0 - U+66ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6700 - U+671f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6720 - U+673f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6740 - U+675f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6760 - U+677f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6780 - U+679f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+67a0 - U+67bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+67c0 - U+67df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+67e0 - U+67ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6800 - U+681f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6820 - U+683f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6840 - U+685f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6860 - U+687f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6880 - U+689f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+68a0 - U+68bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+68c0 - U+68df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+68e0 - U+68ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6900 - U+691f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6920 - U+693f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6940 - U+695f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6960 - U+697f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6980 - U+699f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+69a0 - U+69bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+69c0 - U+69df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+69e0 - U+69ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6a00 - U+6a1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6a20 - U+6a3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6a40 - U+6a5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6a60 - U+6a7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6a80 - U+6a9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6aa0 - U+6abf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6ac0 - U+6adf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6ae0 - U+6aff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6b00 - U+6b1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6b20 - U+6b3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6b40 - U+6b5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6b60 - U+6b7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6b80 - U+6b9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6ba0 - U+6bbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6bc0 - U+6bdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6be0 - U+6bff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6c00 - U+6c1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6c20 - U+6c3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6c40 - U+6c5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6c60 - U+6c7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6c80 - U+6c9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6ca0 - U+6cbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6cc0 - U+6cdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6ce0 - U+6cff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6d00 - U+6d1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6d20 - U+6d3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6d40 - U+6d5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6d60 - U+6d7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6d80 - U+6d9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6da0 - U+6dbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6dc0 - U+6ddf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6de0 - U+6dff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6e00 - U+6e1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6e20 - U+6e3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6e40 - U+6e5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6e60 - U+6e7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6e80 - U+6e9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6ea0 - U+6ebf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6ec0 - U+6edf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6ee0 - U+6eff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6f00 - U+6f1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6f20 - U+6f3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6f40 - U+6f5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6f60 - U+6f7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6f80 - U+6f9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6fa0 - U+6fbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6fc0 - U+6fdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+6fe0 - U+6fff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7000 - U+701f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7020 - U+703f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7040 - U+705f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7060 - U+707f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7080 - U+709f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+70a0 - U+70bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+70c0 - U+70df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+70e0 - U+70ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7100 - U+711f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7120 - U+713f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7140 - U+715f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7160 - U+717f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7180 - U+719f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+71a0 - U+71bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+71c0 - U+71df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+71e0 - U+71ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7200 - U+721f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7220 - U+723f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7240 - U+725f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7260 - U+727f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7280 - U+729f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+72a0 - U+72bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+72c0 - U+72df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+72e0 - U+72ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7300 - U+731f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7320 - U+733f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7340 - U+735f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7360 - U+737f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7380 - U+739f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+73a0 - U+73bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+73c0 - U+73df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+73e0 - U+73ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7400 - U+741f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7420 - U+743f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7440 - U+745f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7460 - U+747f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7480 - U+749f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+74a0 - U+74bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+74c0 - U+74df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+74e0 - U+74ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7500 - U+751f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7520 - U+753f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7540 - U+755f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7560 - U+757f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7580 - U+759f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+75a0 - U+75bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+75c0 - U+75df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+75e0 - U+75ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7600 - U+761f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7620 - U+763f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7640 - U+765f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7660 - U+767f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7680 - U+769f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+76a0 - U+76bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+76c0 - U+76df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+76e0 - U+76ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7700 - U+771f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7720 - U+773f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7740 - U+775f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7760 - U+777f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7780 - U+779f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+77a0 - U+77bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+77c0 - U+77df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+77e0 - U+77ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7800 - U+781f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7820 - U+783f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7840 - U+785f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7860 - U+787f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7880 - U+789f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+78a0 - U+78bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+78c0 - U+78df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+78e0 - U+78ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7900 - U+791f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7920 - U+793f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7940 - U+795f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7960 - U+797f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7980 - U+799f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+79a0 - U+79bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+79c0 - U+79df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+79e0 - U+79ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7a00 - U+7a1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7a20 - U+7a3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7a40 - U+7a5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7a60 - U+7a7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7a80 - U+7a9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7aa0 - U+7abf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7ac0 - U+7adf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7ae0 - U+7aff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7b00 - U+7b1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7b20 - U+7b3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7b40 - U+7b5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7b60 - U+7b7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7b80 - U+7b9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7ba0 - U+7bbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7bc0 - U+7bdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7be0 - U+7bff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7c00 - U+7c1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7c20 - U+7c3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7c40 - U+7c5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7c60 - U+7c7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7c80 - U+7c9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7ca0 - U+7cbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7cc0 - U+7cdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7ce0 - U+7cff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7d00 - U+7d1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7d20 - U+7d3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7d40 - U+7d5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7d60 - U+7d7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7d80 - U+7d9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7da0 - U+7dbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7dc0 - U+7ddf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7de0 - U+7dff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7e00 - U+7e1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7e20 - U+7e3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7e40 - U+7e5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7e60 - U+7e7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7e80 - U+7e9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7ea0 - U+7ebf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7ec0 - U+7edf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7ee0 - U+7eff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7f00 - U+7f1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7f20 - U+7f3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7f40 - U+7f5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7f60 - U+7f7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7f80 - U+7f9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7fa0 - U+7fbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7fc0 - U+7fdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+7fe0 - U+7fff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8000 - U+801f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8020 - U+803f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8040 - U+805f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8060 - U+807f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8080 - U+809f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+80a0 - U+80bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+80c0 - U+80df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+80e0 - U+80ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8100 - U+811f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8120 - U+813f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8140 - U+815f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8160 - U+817f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8180 - U+819f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+81a0 - U+81bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+81c0 - U+81df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+81e0 - U+81ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8200 - U+821f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8220 - U+823f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8240 - U+825f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8260 - U+827f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8280 - U+829f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+82a0 - U+82bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+82c0 - U+82df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+82e0 - U+82ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8300 - U+831f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8320 - U+833f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8340 - U+835f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8360 - U+837f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8380 - U+839f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+83a0 - U+83bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+83c0 - U+83df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+83e0 - U+83ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8400 - U+841f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8420 - U+843f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8440 - U+845f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8460 - U+847f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8480 - U+849f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+84a0 - U+84bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+84c0 - U+84df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+84e0 - U+84ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8500 - U+851f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8520 - U+853f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8540 - U+855f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8560 - U+857f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8580 - U+859f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+85a0 - U+85bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+85c0 - U+85df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+85e0 - U+85ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8600 - U+861f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8620 - U+863f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8640 - U+865f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8660 - U+867f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8680 - U+869f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+86a0 - U+86bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+86c0 - U+86df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+86e0 - U+86ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8700 - U+871f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8720 - U+873f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8740 - U+875f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8760 - U+877f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8780 - U+879f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+87a0 - U+87bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+87c0 - U+87df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+87e0 - U+87ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8800 - U+881f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8820 - U+883f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8840 - U+885f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8860 - U+887f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8880 - U+889f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+88a0 - U+88bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+88c0 - U+88df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+88e0 - U+88ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8900 - U+891f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8920 - U+893f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8940 - U+895f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8960 - U+897f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8980 - U+899f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+89a0 - U+89bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+89c0 - U+89df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+89e0 - U+89ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8a00 - U+8a1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8a20 - U+8a3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8a40 - U+8a5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8a60 - U+8a7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8a80 - U+8a9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8aa0 - U+8abf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8ac0 - U+8adf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8ae0 - U+8aff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8b00 - U+8b1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8b20 - U+8b3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8b40 - U+8b5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8b60 - U+8b7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8b80 - U+8b9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8ba0 - U+8bbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8bc0 - U+8bdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8be0 - U+8bff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8c00 - U+8c1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8c20 - U+8c3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8c40 - U+8c5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8c60 - U+8c7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8c80 - U+8c9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8ca0 - U+8cbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8cc0 - U+8cdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8ce0 - U+8cff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8d00 - U+8d1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8d20 - U+8d3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8d40 - U+8d5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8d60 - U+8d7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8d80 - U+8d9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8da0 - U+8dbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8dc0 - U+8ddf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8de0 - U+8dff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8e00 - U+8e1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8e20 - U+8e3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8e40 - U+8e5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8e60 - U+8e7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8e80 - U+8e9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8ea0 - U+8ebf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8ec0 - U+8edf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8ee0 - U+8eff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8f00 - U+8f1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8f20 - U+8f3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8f40 - U+8f5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8f60 - U+8f7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8f80 - U+8f9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8fa0 - U+8fbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8fc0 - U+8fdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+8fe0 - U+8fff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9000 - U+901f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9020 - U+903f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9040 - U+905f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9060 - U+907f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9080 - U+909f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+90a0 - U+90bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+90c0 - U+90df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+90e0 - U+90ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9100 - U+911f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9120 - U+913f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9140 - U+915f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9160 - U+917f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9180 - U+919f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+91a0 - U+91bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+91c0 - U+91df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+91e0 - U+91ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9200 - U+921f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9220 - U+923f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9240 - U+925f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9260 - U+927f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9280 - U+929f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+92a0 - U+92bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+92c0 - U+92df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+92e0 - U+92ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9300 - U+931f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9320 - U+933f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9340 - U+935f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9360 - U+937f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9380 - U+939f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+93a0 - U+93bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+93c0 - U+93df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+93e0 - U+93ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9400 - U+941f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9420 - U+943f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9440 - U+945f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9460 - U+947f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9480 - U+949f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+94a0 - U+94bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+94c0 - U+94df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+94e0 - U+94ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9500 - U+951f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9520 - U+953f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9540 - U+955f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9560 - U+957f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9580 - U+959f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+95a0 - U+95bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+95c0 - U+95df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+95e0 - U+95ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9600 - U+961f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9620 - U+963f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9640 - U+965f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9660 - U+967f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9680 - U+969f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+96a0 - U+96bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+96c0 - U+96df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+96e0 - U+96ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9700 - U+971f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9720 - U+973f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9740 - U+975f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9760 - U+977f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9780 - U+979f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+97a0 - U+97bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+97c0 - U+97df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+97e0 - U+97ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9800 - U+981f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9820 - U+983f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9840 - U+985f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9860 - U+987f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9880 - U+989f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+98a0 - U+98bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+98c0 - U+98df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+98e0 - U+98ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9900 - U+991f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9920 - U+993f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9940 - U+995f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9960 - U+997f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9980 - U+999f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+99a0 - U+99bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+99c0 - U+99df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+99e0 - U+99ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9a00 - U+9a1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9a20 - U+9a3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9a40 - U+9a5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9a60 - U+9a7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9a80 - U+9a9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9aa0 - U+9abf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9ac0 - U+9adf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9ae0 - U+9aff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9b00 - U+9b1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9b20 - U+9b3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9b40 - U+9b5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9b60 - U+9b7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9b80 - U+9b9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9ba0 - U+9bbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9bc0 - U+9bdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9be0 - U+9bff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9c00 - U+9c1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9c20 - U+9c3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9c40 - U+9c5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9c60 - U+9c7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9c80 - U+9c9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9ca0 - U+9cbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9cc0 - U+9cdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9ce0 - U+9cff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9d00 - U+9d1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9d20 - U+9d3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9d40 - U+9d5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9d60 - U+9d7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9d80 - U+9d9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9da0 - U+9dbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9dc0 - U+9ddf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9de0 - U+9dff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9e00 - U+9e1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9e20 - U+9e3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9e40 - U+9e5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9e60 - U+9e7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9e80 - U+9e9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9ea0 - U+9ebf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9ec0 - U+9edf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9ee0 - U+9eff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9f00 - U+9f1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9f20 - U+9f3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9f40 - U+9f5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9f60 - U+9f7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+9f80 - U+9f9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xff, /* U+9fa0 - U+9fbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+9fc0 - U+9fdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+9fe0 - U+9fff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a000 - U+a01f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a020 - U+a03f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a040 - U+a05f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a060 - U+a07f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a080 - U+a09f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a0a0 - U+a0bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a0c0 - U+a0df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a0e0 - U+a0ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a100 - U+a11f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a120 - U+a13f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a140 - U+a15f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a160 - U+a17f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a180 - U+a19f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a1a0 - U+a1bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a1c0 - U+a1df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a1e0 - U+a1ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a200 - U+a21f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a220 - U+a23f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a240 - U+a25f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a260 - U+a27f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a280 - U+a29f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a2a0 - U+a2bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a2c0 - U+a2df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a2e0 - U+a2ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a300 - U+a31f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a320 - U+a33f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a340 - U+a35f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a360 - U+a37f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a380 - U+a39f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a3a0 - U+a3bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a3c0 - U+a3df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a3e0 - U+a3ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a400 - U+a41f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a420 - U+a43f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a440 - U+a45f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a460 - U+a47f */
+ 0xaa,0xaa,0xaa,0xbf,0xaa,0xaa,0xaa,0xaa, /* U+a480 - U+a49f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+a4a0 - U+a4bf */
+ 0xaa,0xab,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a4c0 - U+a4df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a4e0 - U+a4ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a500 - U+a51f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a520 - U+a53f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a540 - U+a55f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a560 - U+a57f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a580 - U+a59f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a5a0 - U+a5bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a5c0 - U+a5df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a5e0 - U+a5ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a600 - U+a61f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a620 - U+a63f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a640 - U+a65f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a660 - U+a67f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a680 - U+a69f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a6a0 - U+a6bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a6c0 - U+a6df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a6e0 - U+a6ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a700 - U+a71f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a720 - U+a73f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a740 - U+a75f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a760 - U+a77f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a780 - U+a79f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a7a0 - U+a7bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a7c0 - U+a7df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a7e0 - U+a7ff */
+ 0xf3,0xf3,0xfc,0xff,0xff,0xff,0xff,0xff, /* U+a800 - U+a81f */
+ 0xff,0xc3,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a820 - U+a83f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a840 - U+a85f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a860 - U+a87f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a880 - U+a89f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a8a0 - U+a8bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a8c0 - U+a8df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a8e0 - U+a8ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a900 - U+a91f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a920 - U+a93f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a940 - U+a95f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a960 - U+a97f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a980 - U+a99f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a9a0 - U+a9bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a9c0 - U+a9df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+a9e0 - U+a9ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+aa00 - U+aa1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+aa20 - U+aa3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+aa40 - U+aa5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+aa60 - U+aa7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+aa80 - U+aa9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+aaa0 - U+aabf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+aac0 - U+aadf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+aae0 - U+aaff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ab00 - U+ab1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ab20 - U+ab3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ab40 - U+ab5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ab60 - U+ab7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ab80 - U+ab9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+aba0 - U+abbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+abc0 - U+abdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+abe0 - U+abff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ac00 - U+ac1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ac20 - U+ac3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ac40 - U+ac5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ac60 - U+ac7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ac80 - U+ac9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+aca0 - U+acbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+acc0 - U+acdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ace0 - U+acff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ad00 - U+ad1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ad20 - U+ad3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ad40 - U+ad5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ad60 - U+ad7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ad80 - U+ad9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ada0 - U+adbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+adc0 - U+addf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ade0 - U+adff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ae00 - U+ae1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ae20 - U+ae3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ae40 - U+ae5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ae60 - U+ae7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ae80 - U+ae9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+aea0 - U+aebf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+aec0 - U+aedf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+aee0 - U+aeff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+af00 - U+af1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+af20 - U+af3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+af40 - U+af5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+af60 - U+af7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+af80 - U+af9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+afa0 - U+afbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+afc0 - U+afdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+afe0 - U+afff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b000 - U+b01f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b020 - U+b03f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b040 - U+b05f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b060 - U+b07f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b080 - U+b09f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b0a0 - U+b0bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b0c0 - U+b0df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b0e0 - U+b0ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b100 - U+b11f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b120 - U+b13f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b140 - U+b15f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b160 - U+b17f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b180 - U+b19f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b1a0 - U+b1bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b1c0 - U+b1df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b1e0 - U+b1ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b200 - U+b21f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b220 - U+b23f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b240 - U+b25f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b260 - U+b27f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b280 - U+b29f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b2a0 - U+b2bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b2c0 - U+b2df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b2e0 - U+b2ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b300 - U+b31f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b320 - U+b33f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b340 - U+b35f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b360 - U+b37f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b380 - U+b39f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b3a0 - U+b3bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b3c0 - U+b3df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b3e0 - U+b3ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b400 - U+b41f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b420 - U+b43f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b440 - U+b45f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b460 - U+b47f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b480 - U+b49f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b4a0 - U+b4bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b4c0 - U+b4df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b4e0 - U+b4ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b500 - U+b51f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b520 - U+b53f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b540 - U+b55f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b560 - U+b57f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b580 - U+b59f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b5a0 - U+b5bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b5c0 - U+b5df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b5e0 - U+b5ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b600 - U+b61f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b620 - U+b63f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b640 - U+b65f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b660 - U+b67f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b680 - U+b69f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b6a0 - U+b6bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b6c0 - U+b6df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b6e0 - U+b6ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b700 - U+b71f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b720 - U+b73f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b740 - U+b75f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b760 - U+b77f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b780 - U+b79f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b7a0 - U+b7bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b7c0 - U+b7df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b7e0 - U+b7ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b800 - U+b81f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b820 - U+b83f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b840 - U+b85f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b860 - U+b87f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b880 - U+b89f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b8a0 - U+b8bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b8c0 - U+b8df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b8e0 - U+b8ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b900 - U+b91f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b920 - U+b93f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b940 - U+b95f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b960 - U+b97f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b980 - U+b99f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b9a0 - U+b9bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b9c0 - U+b9df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+b9e0 - U+b9ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ba00 - U+ba1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ba20 - U+ba3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ba40 - U+ba5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ba60 - U+ba7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ba80 - U+ba9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+baa0 - U+babf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bac0 - U+badf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bae0 - U+baff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bb00 - U+bb1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bb20 - U+bb3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bb40 - U+bb5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bb60 - U+bb7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bb80 - U+bb9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bba0 - U+bbbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bbc0 - U+bbdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bbe0 - U+bbff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bc00 - U+bc1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bc20 - U+bc3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bc40 - U+bc5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bc60 - U+bc7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bc80 - U+bc9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bca0 - U+bcbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bcc0 - U+bcdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bce0 - U+bcff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bd00 - U+bd1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bd20 - U+bd3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bd40 - U+bd5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bd60 - U+bd7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bd80 - U+bd9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bda0 - U+bdbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bdc0 - U+bddf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bde0 - U+bdff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+be00 - U+be1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+be20 - U+be3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+be40 - U+be5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+be60 - U+be7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+be80 - U+be9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bea0 - U+bebf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bec0 - U+bedf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bee0 - U+beff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bf00 - U+bf1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bf20 - U+bf3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bf40 - U+bf5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bf60 - U+bf7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bf80 - U+bf9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bfa0 - U+bfbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bfc0 - U+bfdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+bfe0 - U+bfff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c000 - U+c01f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c020 - U+c03f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c040 - U+c05f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c060 - U+c07f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c080 - U+c09f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c0a0 - U+c0bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c0c0 - U+c0df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c0e0 - U+c0ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c100 - U+c11f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c120 - U+c13f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c140 - U+c15f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c160 - U+c17f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c180 - U+c19f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c1a0 - U+c1bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c1c0 - U+c1df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c1e0 - U+c1ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c200 - U+c21f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c220 - U+c23f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c240 - U+c25f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c260 - U+c27f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c280 - U+c29f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c2a0 - U+c2bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c2c0 - U+c2df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c2e0 - U+c2ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c300 - U+c31f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c320 - U+c33f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c340 - U+c35f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c360 - U+c37f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c380 - U+c39f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c3a0 - U+c3bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c3c0 - U+c3df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c3e0 - U+c3ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c400 - U+c41f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c420 - U+c43f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c440 - U+c45f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c460 - U+c47f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c480 - U+c49f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c4a0 - U+c4bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c4c0 - U+c4df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c4e0 - U+c4ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c500 - U+c51f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c520 - U+c53f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c540 - U+c55f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c560 - U+c57f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c580 - U+c59f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c5a0 - U+c5bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c5c0 - U+c5df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c5e0 - U+c5ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c600 - U+c61f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c620 - U+c63f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c640 - U+c65f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c660 - U+c67f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c680 - U+c69f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c6a0 - U+c6bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c6c0 - U+c6df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c6e0 - U+c6ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c700 - U+c71f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c720 - U+c73f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c740 - U+c75f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c760 - U+c77f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c780 - U+c79f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c7a0 - U+c7bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c7c0 - U+c7df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c7e0 - U+c7ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c800 - U+c81f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c820 - U+c83f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c840 - U+c85f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c860 - U+c87f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c880 - U+c89f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c8a0 - U+c8bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c8c0 - U+c8df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c8e0 - U+c8ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c900 - U+c91f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c920 - U+c93f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c940 - U+c95f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c960 - U+c97f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c980 - U+c99f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c9a0 - U+c9bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c9c0 - U+c9df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+c9e0 - U+c9ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ca00 - U+ca1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ca20 - U+ca3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ca40 - U+ca5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ca60 - U+ca7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ca80 - U+ca9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+caa0 - U+cabf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cac0 - U+cadf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cae0 - U+caff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cb00 - U+cb1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cb20 - U+cb3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cb40 - U+cb5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cb60 - U+cb7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cb80 - U+cb9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cba0 - U+cbbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cbc0 - U+cbdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cbe0 - U+cbff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cc00 - U+cc1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cc20 - U+cc3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cc40 - U+cc5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cc60 - U+cc7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cc80 - U+cc9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cca0 - U+ccbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ccc0 - U+ccdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cce0 - U+ccff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cd00 - U+cd1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cd20 - U+cd3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cd40 - U+cd5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cd60 - U+cd7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cd80 - U+cd9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cda0 - U+cdbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cdc0 - U+cddf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cde0 - U+cdff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ce00 - U+ce1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ce20 - U+ce3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ce40 - U+ce5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ce60 - U+ce7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ce80 - U+ce9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cea0 - U+cebf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cec0 - U+cedf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cee0 - U+ceff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cf00 - U+cf1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cf20 - U+cf3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cf40 - U+cf5f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cf60 - U+cf7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cf80 - U+cf9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cfa0 - U+cfbf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cfc0 - U+cfdf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+cfe0 - U+cfff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d000 - U+d01f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d020 - U+d03f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d040 - U+d05f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d060 - U+d07f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d080 - U+d09f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d0a0 - U+d0bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d0c0 - U+d0df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d0e0 - U+d0ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d100 - U+d11f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d120 - U+d13f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d140 - U+d15f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d160 - U+d17f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d180 - U+d19f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d1a0 - U+d1bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d1c0 - U+d1df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d1e0 - U+d1ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d200 - U+d21f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d220 - U+d23f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d240 - U+d25f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d260 - U+d27f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d280 - U+d29f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d2a0 - U+d2bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d2c0 - U+d2df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d2e0 - U+d2ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d300 - U+d31f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d320 - U+d33f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d340 - U+d35f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d360 - U+d37f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d380 - U+d39f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d3a0 - U+d3bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d3c0 - U+d3df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d3e0 - U+d3ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d400 - U+d41f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d420 - U+d43f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d440 - U+d45f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d460 - U+d47f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d480 - U+d49f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d4a0 - U+d4bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d4c0 - U+d4df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d4e0 - U+d4ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d500 - U+d51f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d520 - U+d53f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d540 - U+d55f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d560 - U+d57f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d580 - U+d59f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d5a0 - U+d5bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d5c0 - U+d5df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d5e0 - U+d5ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d600 - U+d61f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d620 - U+d63f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d640 - U+d65f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d660 - U+d67f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d680 - U+d69f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d6a0 - U+d6bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d6c0 - U+d6df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d6e0 - U+d6ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d700 - U+d71f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d720 - U+d73f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d740 - U+d75f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d760 - U+d77f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+d780 - U+d79f */
+ 0xaa,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d7a0 - U+d7bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d7c0 - U+d7df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d7e0 - U+d7ff */
+ 0x3f,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d800 - U+d81f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d820 - U+d83f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d840 - U+d85f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d860 - U+d87f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d880 - U+d89f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d8a0 - U+d8bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d8c0 - U+d8df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d8e0 - U+d8ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d900 - U+d91f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d920 - U+d93f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d940 - U+d95f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d960 - U+d97f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d980 - U+d99f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d9a0 - U+d9bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d9c0 - U+d9df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+d9e0 - U+d9ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+da00 - U+da1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+da20 - U+da3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+da40 - U+da5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+da60 - U+da7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+da80 - U+da9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+daa0 - U+dabf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dac0 - U+dadf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dae0 - U+daff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+db00 - U+db1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+db20 - U+db3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+db40 - U+db5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc, /* U+db60 - U+db7f */
+ 0x3f,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+db80 - U+db9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dba0 - U+dbbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dbc0 - U+dbdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc, /* U+dbe0 - U+dbff */
+ 0x3f,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dc00 - U+dc1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dc20 - U+dc3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dc40 - U+dc5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dc60 - U+dc7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dc80 - U+dc9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dca0 - U+dcbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dcc0 - U+dcdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dce0 - U+dcff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dd00 - U+dd1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dd20 - U+dd3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dd40 - U+dd5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dd60 - U+dd7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dd80 - U+dd9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dda0 - U+ddbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ddc0 - U+dddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dde0 - U+ddff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+de00 - U+de1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+de20 - U+de3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+de40 - U+de5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+de60 - U+de7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+de80 - U+de9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dea0 - U+debf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dec0 - U+dedf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dee0 - U+deff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+df00 - U+df1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+df20 - U+df3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+df40 - U+df5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+df60 - U+df7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+df80 - U+df9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dfa0 - U+dfbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+dfc0 - U+dfdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc, /* U+dfe0 - U+dfff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e000 - U+e01f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e020 - U+e03f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e040 - U+e05f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e060 - U+e07f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e080 - U+e09f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e0a0 - U+e0bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e0c0 - U+e0df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e0e0 - U+e0ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e100 - U+e11f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e120 - U+e13f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e140 - U+e15f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e160 - U+e17f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e180 - U+e19f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e1a0 - U+e1bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e1c0 - U+e1df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e1e0 - U+e1ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e200 - U+e21f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e220 - U+e23f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e240 - U+e25f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e260 - U+e27f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e280 - U+e29f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e2a0 - U+e2bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e2c0 - U+e2df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e2e0 - U+e2ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e300 - U+e31f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e320 - U+e33f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e340 - U+e35f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e360 - U+e37f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e380 - U+e39f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e3a0 - U+e3bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e3c0 - U+e3df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e3e0 - U+e3ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e400 - U+e41f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e420 - U+e43f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e440 - U+e45f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e460 - U+e47f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e480 - U+e49f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e4a0 - U+e4bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e4c0 - U+e4df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e4e0 - U+e4ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e500 - U+e51f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e520 - U+e53f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e540 - U+e55f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e560 - U+e57f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e580 - U+e59f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e5a0 - U+e5bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e5c0 - U+e5df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e5e0 - U+e5ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e600 - U+e61f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e620 - U+e63f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e640 - U+e65f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e660 - U+e67f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e680 - U+e69f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e6a0 - U+e6bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e6c0 - U+e6df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e6e0 - U+e6ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e700 - U+e71f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e720 - U+e73f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e740 - U+e75f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e760 - U+e77f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e780 - U+e79f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e7a0 - U+e7bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e7c0 - U+e7df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e7e0 - U+e7ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e800 - U+e81f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e820 - U+e83f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e840 - U+e85f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e860 - U+e87f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e880 - U+e89f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e8a0 - U+e8bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e8c0 - U+e8df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e8e0 - U+e8ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e900 - U+e91f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e920 - U+e93f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e940 - U+e95f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e960 - U+e97f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e980 - U+e99f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e9a0 - U+e9bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e9c0 - U+e9df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+e9e0 - U+e9ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ea00 - U+ea1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ea20 - U+ea3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ea40 - U+ea5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ea60 - U+ea7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ea80 - U+ea9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+eaa0 - U+eabf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+eac0 - U+eadf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+eae0 - U+eaff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+eb00 - U+eb1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+eb20 - U+eb3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+eb40 - U+eb5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+eb60 - U+eb7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+eb80 - U+eb9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+eba0 - U+ebbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ebc0 - U+ebdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ebe0 - U+ebff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ec00 - U+ec1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ec20 - U+ec3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ec40 - U+ec5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ec60 - U+ec7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ec80 - U+ec9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+eca0 - U+ecbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ecc0 - U+ecdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ece0 - U+ecff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ed00 - U+ed1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ed20 - U+ed3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ed40 - U+ed5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ed60 - U+ed7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ed80 - U+ed9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+eda0 - U+edbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+edc0 - U+eddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ede0 - U+edff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ee00 - U+ee1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ee20 - U+ee3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ee40 - U+ee5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ee60 - U+ee7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ee80 - U+ee9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+eea0 - U+eebf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+eec0 - U+eedf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+eee0 - U+eeff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ef00 - U+ef1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ef20 - U+ef3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ef40 - U+ef5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ef60 - U+ef7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+ef80 - U+ef9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+efa0 - U+efbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+efc0 - U+efdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+efe0 - U+efff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f000 - U+f01f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f020 - U+f03f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f040 - U+f05f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f060 - U+f07f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f080 - U+f09f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f0a0 - U+f0bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f0c0 - U+f0df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f0e0 - U+f0ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f100 - U+f11f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f120 - U+f13f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f140 - U+f15f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f160 - U+f17f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f180 - U+f19f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f1a0 - U+f1bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f1c0 - U+f1df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f1e0 - U+f1ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f200 - U+f21f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f220 - U+f23f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f240 - U+f25f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f260 - U+f27f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f280 - U+f29f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f2a0 - U+f2bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f2c0 - U+f2df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f2e0 - U+f2ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f300 - U+f31f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f320 - U+f33f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f340 - U+f35f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f360 - U+f37f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f380 - U+f39f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f3a0 - U+f3bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f3c0 - U+f3df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f3e0 - U+f3ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f400 - U+f41f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f420 - U+f43f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f440 - U+f45f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f460 - U+f47f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f480 - U+f49f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f4a0 - U+f4bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f4c0 - U+f4df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f4e0 - U+f4ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f500 - U+f51f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f520 - U+f53f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f540 - U+f55f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f560 - U+f57f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f580 - U+f59f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f5a0 - U+f5bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f5c0 - U+f5df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f5e0 - U+f5ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f600 - U+f61f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f620 - U+f63f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f640 - U+f65f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f660 - U+f67f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f680 - U+f69f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f6a0 - U+f6bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f6c0 - U+f6df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f6e0 - U+f6ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f700 - U+f71f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f720 - U+f73f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f740 - U+f75f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f760 - U+f77f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f780 - U+f79f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f7a0 - U+f7bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f7c0 - U+f7df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f7e0 - U+f7ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f800 - U+f81f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f820 - U+f83f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f840 - U+f85f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f860 - U+f87f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f880 - U+f89f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f8a0 - U+f8bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f8c0 - U+f8df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+f8e0 - U+f8ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+f900 - U+f91f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+f920 - U+f93f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+f940 - U+f95f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+f960 - U+f97f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+f980 - U+f99f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+f9a0 - U+f9bf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+f9c0 - U+f9df */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+f9e0 - U+f9ff */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+fa00 - U+fa1f */
+ 0xaa,0xaa,0xaa,0xaf,0xaa,0xaa,0xaa,0xaa, /* U+fa20 - U+fa3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+fa40 - U+fa5f */
+ 0xaa,0xaa,0xab,0xff,0xaa,0xaa,0xaa,0xaa, /* U+fa60 - U+fa7f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+fa80 - U+fa9f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+faa0 - U+fabf */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaf,0xff, /* U+fac0 - U+fadf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fae0 - U+faff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf3, /* U+fb00 - U+fb1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fb20 - U+fb3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fb40 - U+fb5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fb60 - U+fb7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fb80 - U+fb9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fba0 - U+fbbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fbc0 - U+fbdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fbe0 - U+fbff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fc00 - U+fc1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fc20 - U+fc3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fc40 - U+fc5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fc60 - U+fc7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fc80 - U+fc9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fca0 - U+fcbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fcc0 - U+fcdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fce0 - U+fcff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fd00 - U+fd1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fd20 - U+fd3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fd40 - U+fd5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fd60 - U+fd7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fd80 - U+fd9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fda0 - U+fdbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fdc0 - U+fddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fde0 - U+fdff */
+ 0x00,0x00,0x00,0x00,0xaa,0xaa,0xaf,0xff, /* U+fe00 - U+fe1f */
+ 0x00,0xff,0xff,0xff,0xaa,0xaa,0xaa,0xaa, /* U+fe20 - U+fe3f */
+ 0xaa,0xaa,0xaa,0xaa,0xab,0xaa,0xaa,0xaa, /* U+fe40 - U+fe5f */
+ 0xaa,0xab,0xaa,0xff,0xff,0xff,0xff,0xff, /* U+fe60 - U+fe7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fe80 - U+fe9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fea0 - U+febf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+fec0 - U+fedf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc, /* U+fee0 - U+feff */
+ 0xea,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ff00 - U+ff1f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ff20 - U+ff3f */
+ 0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa, /* U+ff40 - U+ff5f */
+ 0x95,0x55,0x55,0x55,0x55,0x55,0x55,0x55, /* U+ff60 - U+ff7f */
+ 0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55, /* U+ff80 - U+ff9f */
+ 0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x57, /* U+ffa0 - U+ffbf */
+ 0xf5,0x55,0xf5,0x55,0xf5,0x55,0xf5,0x7f, /* U+ffc0 - U+ffdf */
+ 0xaa,0xab,0x55,0x57,0xff,0xff,0xc0,0xff, /* U+ffe0 - U+ffff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10000 - U+1001f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10020 - U+1003f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10040 - U+1005f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10060 - U+1007f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10080 - U+1009f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+100a0 - U+100bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+100c0 - U+100df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+100e0 - U+100ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10100 - U+1011f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10120 - U+1013f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10140 - U+1015f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10160 - U+1017f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10180 - U+1019f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+101a0 - U+101bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+101c0 - U+101df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+101e0 - U+101ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10200 - U+1021f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10220 - U+1023f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10240 - U+1025f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10260 - U+1027f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10280 - U+1029f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+102a0 - U+102bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+102c0 - U+102df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+102e0 - U+102ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10300 - U+1031f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10320 - U+1033f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10340 - U+1035f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10360 - U+1037f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10380 - U+1039f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+103a0 - U+103bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+103c0 - U+103df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+103e0 - U+103ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10400 - U+1041f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10420 - U+1043f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10440 - U+1045f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10460 - U+1047f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10480 - U+1049f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+104a0 - U+104bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+104c0 - U+104df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+104e0 - U+104ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10500 - U+1051f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10520 - U+1053f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10540 - U+1055f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10560 - U+1057f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10580 - U+1059f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+105a0 - U+105bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+105c0 - U+105df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+105e0 - U+105ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10600 - U+1061f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10620 - U+1063f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10640 - U+1065f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10660 - U+1067f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10680 - U+1069f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+106a0 - U+106bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+106c0 - U+106df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+106e0 - U+106ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10700 - U+1071f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10720 - U+1073f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10740 - U+1075f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10760 - U+1077f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10780 - U+1079f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+107a0 - U+107bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+107c0 - U+107df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+107e0 - U+107ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10800 - U+1081f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10820 - U+1083f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10840 - U+1085f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10860 - U+1087f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10880 - U+1089f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+108a0 - U+108bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+108c0 - U+108df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+108e0 - U+108ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10900 - U+1091f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10920 - U+1093f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10940 - U+1095f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10960 - U+1097f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10980 - U+1099f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+109a0 - U+109bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+109c0 - U+109df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+109e0 - U+109ff */
+ 0xc0,0xc3,0xff,0x00,0xff,0xff,0xff,0xff, /* U+10a00 - U+10a1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0x03,0xfc, /* U+10a20 - U+10a3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10a40 - U+10a5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10a60 - U+10a7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10a80 - U+10a9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10aa0 - U+10abf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10ac0 - U+10adf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10ae0 - U+10aff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10b00 - U+10b1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10b20 - U+10b3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10b40 - U+10b5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10b60 - U+10b7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10b80 - U+10b9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10ba0 - U+10bbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10bc0 - U+10bdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10be0 - U+10bff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10c00 - U+10c1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10c20 - U+10c3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10c40 - U+10c5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10c60 - U+10c7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10c80 - U+10c9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10ca0 - U+10cbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10cc0 - U+10cdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10ce0 - U+10cff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10d00 - U+10d1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10d20 - U+10d3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10d40 - U+10d5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10d60 - U+10d7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10d80 - U+10d9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10da0 - U+10dbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10dc0 - U+10ddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10de0 - U+10dff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10e00 - U+10e1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10e20 - U+10e3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10e40 - U+10e5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10e60 - U+10e7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10e80 - U+10e9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10ea0 - U+10ebf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10ec0 - U+10edf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10ee0 - U+10eff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10f00 - U+10f1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10f20 - U+10f3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10f40 - U+10f5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10f60 - U+10f7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10f80 - U+10f9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10fa0 - U+10fbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10fc0 - U+10fdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+10fe0 - U+10fff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11000 - U+1101f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11020 - U+1103f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11040 - U+1105f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11060 - U+1107f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11080 - U+1109f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+110a0 - U+110bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+110c0 - U+110df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+110e0 - U+110ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11100 - U+1111f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11120 - U+1113f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11140 - U+1115f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11160 - U+1117f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11180 - U+1119f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+111a0 - U+111bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+111c0 - U+111df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+111e0 - U+111ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11200 - U+1121f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11220 - U+1123f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11240 - U+1125f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11260 - U+1127f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11280 - U+1129f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+112a0 - U+112bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+112c0 - U+112df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+112e0 - U+112ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11300 - U+1131f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11320 - U+1133f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11340 - U+1135f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11360 - U+1137f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11380 - U+1139f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+113a0 - U+113bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+113c0 - U+113df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+113e0 - U+113ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11400 - U+1141f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11420 - U+1143f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11440 - U+1145f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11460 - U+1147f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11480 - U+1149f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+114a0 - U+114bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+114c0 - U+114df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+114e0 - U+114ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11500 - U+1151f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11520 - U+1153f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11540 - U+1155f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11560 - U+1157f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11580 - U+1159f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+115a0 - U+115bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+115c0 - U+115df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+115e0 - U+115ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11600 - U+1161f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11620 - U+1163f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11640 - U+1165f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11660 - U+1167f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11680 - U+1169f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+116a0 - U+116bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+116c0 - U+116df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+116e0 - U+116ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11700 - U+1171f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11720 - U+1173f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11740 - U+1175f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11760 - U+1177f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11780 - U+1179f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+117a0 - U+117bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+117c0 - U+117df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+117e0 - U+117ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11800 - U+1181f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11820 - U+1183f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11840 - U+1185f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11860 - U+1187f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11880 - U+1189f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+118a0 - U+118bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+118c0 - U+118df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+118e0 - U+118ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11900 - U+1191f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11920 - U+1193f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11940 - U+1195f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11960 - U+1197f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11980 - U+1199f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+119a0 - U+119bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+119c0 - U+119df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+119e0 - U+119ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11a00 - U+11a1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11a20 - U+11a3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11a40 - U+11a5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11a60 - U+11a7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11a80 - U+11a9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11aa0 - U+11abf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11ac0 - U+11adf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11ae0 - U+11aff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11b00 - U+11b1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11b20 - U+11b3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11b40 - U+11b5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11b60 - U+11b7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11b80 - U+11b9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11ba0 - U+11bbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11bc0 - U+11bdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11be0 - U+11bff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11c00 - U+11c1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11c20 - U+11c3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11c40 - U+11c5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11c60 - U+11c7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11c80 - U+11c9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11ca0 - U+11cbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11cc0 - U+11cdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11ce0 - U+11cff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11d00 - U+11d1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11d20 - U+11d3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11d40 - U+11d5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11d60 - U+11d7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11d80 - U+11d9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11da0 - U+11dbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11dc0 - U+11ddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11de0 - U+11dff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11e00 - U+11e1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11e20 - U+11e3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11e40 - U+11e5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11e60 - U+11e7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11e80 - U+11e9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11ea0 - U+11ebf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11ec0 - U+11edf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11ee0 - U+11eff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11f00 - U+11f1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11f20 - U+11f3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11f40 - U+11f5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11f60 - U+11f7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11f80 - U+11f9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11fa0 - U+11fbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11fc0 - U+11fdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+11fe0 - U+11fff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12000 - U+1201f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12020 - U+1203f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12040 - U+1205f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12060 - U+1207f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12080 - U+1209f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+120a0 - U+120bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+120c0 - U+120df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+120e0 - U+120ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12100 - U+1211f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12120 - U+1213f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12140 - U+1215f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12160 - U+1217f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12180 - U+1219f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+121a0 - U+121bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+121c0 - U+121df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+121e0 - U+121ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12200 - U+1221f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12220 - U+1223f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12240 - U+1225f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12260 - U+1227f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12280 - U+1229f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+122a0 - U+122bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+122c0 - U+122df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+122e0 - U+122ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12300 - U+1231f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12320 - U+1233f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12340 - U+1235f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12360 - U+1237f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12380 - U+1239f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+123a0 - U+123bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+123c0 - U+123df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+123e0 - U+123ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12400 - U+1241f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12420 - U+1243f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12440 - U+1245f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12460 - U+1247f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12480 - U+1249f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+124a0 - U+124bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+124c0 - U+124df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+124e0 - U+124ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12500 - U+1251f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12520 - U+1253f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12540 - U+1255f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12560 - U+1257f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12580 - U+1259f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+125a0 - U+125bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+125c0 - U+125df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+125e0 - U+125ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12600 - U+1261f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12620 - U+1263f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12640 - U+1265f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12660 - U+1267f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12680 - U+1269f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+126a0 - U+126bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+126c0 - U+126df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+126e0 - U+126ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12700 - U+1271f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12720 - U+1273f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12740 - U+1275f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12760 - U+1277f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12780 - U+1279f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+127a0 - U+127bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+127c0 - U+127df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+127e0 - U+127ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12800 - U+1281f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12820 - U+1283f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12840 - U+1285f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12860 - U+1287f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12880 - U+1289f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+128a0 - U+128bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+128c0 - U+128df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+128e0 - U+128ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12900 - U+1291f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12920 - U+1293f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12940 - U+1295f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12960 - U+1297f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12980 - U+1299f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+129a0 - U+129bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+129c0 - U+129df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+129e0 - U+129ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12a00 - U+12a1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12a20 - U+12a3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12a40 - U+12a5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12a60 - U+12a7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12a80 - U+12a9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12aa0 - U+12abf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12ac0 - U+12adf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12ae0 - U+12aff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12b00 - U+12b1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12b20 - U+12b3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12b40 - U+12b5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12b60 - U+12b7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12b80 - U+12b9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12ba0 - U+12bbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12bc0 - U+12bdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12be0 - U+12bff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12c00 - U+12c1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12c20 - U+12c3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12c40 - U+12c5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12c60 - U+12c7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12c80 - U+12c9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12ca0 - U+12cbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12cc0 - U+12cdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12ce0 - U+12cff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12d00 - U+12d1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12d20 - U+12d3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12d40 - U+12d5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12d60 - U+12d7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12d80 - U+12d9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12da0 - U+12dbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12dc0 - U+12ddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12de0 - U+12dff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12e00 - U+12e1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12e20 - U+12e3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12e40 - U+12e5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12e60 - U+12e7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12e80 - U+12e9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12ea0 - U+12ebf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12ec0 - U+12edf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12ee0 - U+12eff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12f00 - U+12f1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12f20 - U+12f3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12f40 - U+12f5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12f60 - U+12f7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12f80 - U+12f9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12fa0 - U+12fbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12fc0 - U+12fdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+12fe0 - U+12fff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13000 - U+1301f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13020 - U+1303f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13040 - U+1305f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13060 - U+1307f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13080 - U+1309f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+130a0 - U+130bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+130c0 - U+130df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+130e0 - U+130ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13100 - U+1311f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13120 - U+1313f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13140 - U+1315f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13160 - U+1317f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13180 - U+1319f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+131a0 - U+131bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+131c0 - U+131df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+131e0 - U+131ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13200 - U+1321f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13220 - U+1323f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13240 - U+1325f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13260 - U+1327f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13280 - U+1329f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+132a0 - U+132bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+132c0 - U+132df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+132e0 - U+132ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13300 - U+1331f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13320 - U+1333f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13340 - U+1335f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13360 - U+1337f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13380 - U+1339f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+133a0 - U+133bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+133c0 - U+133df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+133e0 - U+133ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13400 - U+1341f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13420 - U+1343f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13440 - U+1345f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13460 - U+1347f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13480 - U+1349f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+134a0 - U+134bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+134c0 - U+134df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+134e0 - U+134ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13500 - U+1351f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13520 - U+1353f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13540 - U+1355f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13560 - U+1357f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13580 - U+1359f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+135a0 - U+135bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+135c0 - U+135df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+135e0 - U+135ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13600 - U+1361f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13620 - U+1363f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13640 - U+1365f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13660 - U+1367f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13680 - U+1369f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+136a0 - U+136bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+136c0 - U+136df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+136e0 - U+136ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13700 - U+1371f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13720 - U+1373f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13740 - U+1375f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13760 - U+1377f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13780 - U+1379f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+137a0 - U+137bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+137c0 - U+137df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+137e0 - U+137ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13800 - U+1381f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13820 - U+1383f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13840 - U+1385f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13860 - U+1387f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13880 - U+1389f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+138a0 - U+138bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+138c0 - U+138df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+138e0 - U+138ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13900 - U+1391f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13920 - U+1393f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13940 - U+1395f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13960 - U+1397f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13980 - U+1399f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+139a0 - U+139bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+139c0 - U+139df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+139e0 - U+139ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13a00 - U+13a1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13a20 - U+13a3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13a40 - U+13a5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13a60 - U+13a7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13a80 - U+13a9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13aa0 - U+13abf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13ac0 - U+13adf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13ae0 - U+13aff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13b00 - U+13b1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13b20 - U+13b3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13b40 - U+13b5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13b60 - U+13b7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13b80 - U+13b9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13ba0 - U+13bbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13bc0 - U+13bdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13be0 - U+13bff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13c00 - U+13c1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13c20 - U+13c3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13c40 - U+13c5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13c60 - U+13c7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13c80 - U+13c9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13ca0 - U+13cbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13cc0 - U+13cdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13ce0 - U+13cff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13d00 - U+13d1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13d20 - U+13d3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13d40 - U+13d5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13d60 - U+13d7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13d80 - U+13d9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13da0 - U+13dbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13dc0 - U+13ddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13de0 - U+13dff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13e00 - U+13e1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13e20 - U+13e3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13e40 - U+13e5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13e60 - U+13e7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13e80 - U+13e9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13ea0 - U+13ebf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13ec0 - U+13edf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13ee0 - U+13eff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13f00 - U+13f1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13f20 - U+13f3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13f40 - U+13f5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13f60 - U+13f7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13f80 - U+13f9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13fa0 - U+13fbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13fc0 - U+13fdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+13fe0 - U+13fff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14000 - U+1401f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14020 - U+1403f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14040 - U+1405f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14060 - U+1407f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14080 - U+1409f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+140a0 - U+140bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+140c0 - U+140df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+140e0 - U+140ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14100 - U+1411f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14120 - U+1413f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14140 - U+1415f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14160 - U+1417f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14180 - U+1419f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+141a0 - U+141bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+141c0 - U+141df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+141e0 - U+141ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14200 - U+1421f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14220 - U+1423f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14240 - U+1425f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14260 - U+1427f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14280 - U+1429f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+142a0 - U+142bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+142c0 - U+142df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+142e0 - U+142ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14300 - U+1431f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14320 - U+1433f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14340 - U+1435f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14360 - U+1437f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14380 - U+1439f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+143a0 - U+143bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+143c0 - U+143df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+143e0 - U+143ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14400 - U+1441f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14420 - U+1443f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14440 - U+1445f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14460 - U+1447f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14480 - U+1449f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+144a0 - U+144bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+144c0 - U+144df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+144e0 - U+144ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14500 - U+1451f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14520 - U+1453f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14540 - U+1455f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14560 - U+1457f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14580 - U+1459f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+145a0 - U+145bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+145c0 - U+145df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+145e0 - U+145ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14600 - U+1461f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14620 - U+1463f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14640 - U+1465f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14660 - U+1467f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14680 - U+1469f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+146a0 - U+146bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+146c0 - U+146df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+146e0 - U+146ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14700 - U+1471f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14720 - U+1473f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14740 - U+1475f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14760 - U+1477f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14780 - U+1479f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+147a0 - U+147bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+147c0 - U+147df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+147e0 - U+147ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14800 - U+1481f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14820 - U+1483f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14840 - U+1485f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14860 - U+1487f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14880 - U+1489f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+148a0 - U+148bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+148c0 - U+148df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+148e0 - U+148ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14900 - U+1491f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14920 - U+1493f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14940 - U+1495f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14960 - U+1497f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14980 - U+1499f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+149a0 - U+149bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+149c0 - U+149df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+149e0 - U+149ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14a00 - U+14a1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14a20 - U+14a3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14a40 - U+14a5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14a60 - U+14a7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14a80 - U+14a9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14aa0 - U+14abf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14ac0 - U+14adf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14ae0 - U+14aff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14b00 - U+14b1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14b20 - U+14b3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14b40 - U+14b5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14b60 - U+14b7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14b80 - U+14b9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14ba0 - U+14bbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14bc0 - U+14bdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14be0 - U+14bff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14c00 - U+14c1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14c20 - U+14c3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14c40 - U+14c5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14c60 - U+14c7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14c80 - U+14c9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14ca0 - U+14cbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14cc0 - U+14cdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14ce0 - U+14cff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14d00 - U+14d1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14d20 - U+14d3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14d40 - U+14d5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14d60 - U+14d7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14d80 - U+14d9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14da0 - U+14dbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14dc0 - U+14ddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14de0 - U+14dff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14e00 - U+14e1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14e20 - U+14e3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14e40 - U+14e5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14e60 - U+14e7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14e80 - U+14e9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14ea0 - U+14ebf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14ec0 - U+14edf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14ee0 - U+14eff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14f00 - U+14f1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14f20 - U+14f3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14f40 - U+14f5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14f60 - U+14f7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14f80 - U+14f9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14fa0 - U+14fbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14fc0 - U+14fdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+14fe0 - U+14fff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15000 - U+1501f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15020 - U+1503f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15040 - U+1505f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15060 - U+1507f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15080 - U+1509f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+150a0 - U+150bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+150c0 - U+150df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+150e0 - U+150ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15100 - U+1511f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15120 - U+1513f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15140 - U+1515f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15160 - U+1517f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15180 - U+1519f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+151a0 - U+151bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+151c0 - U+151df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+151e0 - U+151ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15200 - U+1521f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15220 - U+1523f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15240 - U+1525f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15260 - U+1527f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15280 - U+1529f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+152a0 - U+152bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+152c0 - U+152df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+152e0 - U+152ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15300 - U+1531f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15320 - U+1533f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15340 - U+1535f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15360 - U+1537f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15380 - U+1539f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+153a0 - U+153bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+153c0 - U+153df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+153e0 - U+153ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15400 - U+1541f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15420 - U+1543f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15440 - U+1545f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15460 - U+1547f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15480 - U+1549f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+154a0 - U+154bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+154c0 - U+154df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+154e0 - U+154ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15500 - U+1551f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15520 - U+1553f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15540 - U+1555f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15560 - U+1557f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15580 - U+1559f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+155a0 - U+155bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+155c0 - U+155df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+155e0 - U+155ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15600 - U+1561f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15620 - U+1563f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15640 - U+1565f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15660 - U+1567f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15680 - U+1569f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+156a0 - U+156bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+156c0 - U+156df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+156e0 - U+156ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15700 - U+1571f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15720 - U+1573f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15740 - U+1575f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15760 - U+1577f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15780 - U+1579f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+157a0 - U+157bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+157c0 - U+157df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+157e0 - U+157ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15800 - U+1581f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15820 - U+1583f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15840 - U+1585f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15860 - U+1587f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15880 - U+1589f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+158a0 - U+158bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+158c0 - U+158df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+158e0 - U+158ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15900 - U+1591f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15920 - U+1593f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15940 - U+1595f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15960 - U+1597f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15980 - U+1599f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+159a0 - U+159bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+159c0 - U+159df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+159e0 - U+159ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15a00 - U+15a1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15a20 - U+15a3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15a40 - U+15a5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15a60 - U+15a7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15a80 - U+15a9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15aa0 - U+15abf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15ac0 - U+15adf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15ae0 - U+15aff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15b00 - U+15b1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15b20 - U+15b3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15b40 - U+15b5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15b60 - U+15b7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15b80 - U+15b9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15ba0 - U+15bbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15bc0 - U+15bdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15be0 - U+15bff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15c00 - U+15c1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15c20 - U+15c3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15c40 - U+15c5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15c60 - U+15c7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15c80 - U+15c9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15ca0 - U+15cbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15cc0 - U+15cdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15ce0 - U+15cff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15d00 - U+15d1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15d20 - U+15d3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15d40 - U+15d5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15d60 - U+15d7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15d80 - U+15d9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15da0 - U+15dbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15dc0 - U+15ddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15de0 - U+15dff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15e00 - U+15e1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15e20 - U+15e3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15e40 - U+15e5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15e60 - U+15e7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15e80 - U+15e9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15ea0 - U+15ebf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15ec0 - U+15edf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15ee0 - U+15eff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15f00 - U+15f1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15f20 - U+15f3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15f40 - U+15f5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15f60 - U+15f7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15f80 - U+15f9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15fa0 - U+15fbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15fc0 - U+15fdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+15fe0 - U+15fff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16000 - U+1601f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16020 - U+1603f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16040 - U+1605f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16060 - U+1607f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16080 - U+1609f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+160a0 - U+160bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+160c0 - U+160df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+160e0 - U+160ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16100 - U+1611f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16120 - U+1613f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16140 - U+1615f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16160 - U+1617f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16180 - U+1619f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+161a0 - U+161bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+161c0 - U+161df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+161e0 - U+161ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16200 - U+1621f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16220 - U+1623f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16240 - U+1625f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16260 - U+1627f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16280 - U+1629f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+162a0 - U+162bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+162c0 - U+162df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+162e0 - U+162ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16300 - U+1631f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16320 - U+1633f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16340 - U+1635f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16360 - U+1637f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16380 - U+1639f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+163a0 - U+163bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+163c0 - U+163df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+163e0 - U+163ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16400 - U+1641f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16420 - U+1643f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16440 - U+1645f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16460 - U+1647f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16480 - U+1649f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+164a0 - U+164bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+164c0 - U+164df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+164e0 - U+164ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16500 - U+1651f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16520 - U+1653f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16540 - U+1655f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16560 - U+1657f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16580 - U+1659f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+165a0 - U+165bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+165c0 - U+165df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+165e0 - U+165ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16600 - U+1661f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16620 - U+1663f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16640 - U+1665f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16660 - U+1667f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16680 - U+1669f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+166a0 - U+166bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+166c0 - U+166df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+166e0 - U+166ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16700 - U+1671f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16720 - U+1673f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16740 - U+1675f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16760 - U+1677f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16780 - U+1679f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+167a0 - U+167bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+167c0 - U+167df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+167e0 - U+167ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16800 - U+1681f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16820 - U+1683f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16840 - U+1685f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16860 - U+1687f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16880 - U+1689f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+168a0 - U+168bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+168c0 - U+168df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+168e0 - U+168ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16900 - U+1691f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16920 - U+1693f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16940 - U+1695f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16960 - U+1697f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16980 - U+1699f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+169a0 - U+169bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+169c0 - U+169df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+169e0 - U+169ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16a00 - U+16a1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16a20 - U+16a3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16a40 - U+16a5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16a60 - U+16a7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16a80 - U+16a9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16aa0 - U+16abf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16ac0 - U+16adf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16ae0 - U+16aff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16b00 - U+16b1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16b20 - U+16b3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16b40 - U+16b5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16b60 - U+16b7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16b80 - U+16b9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16ba0 - U+16bbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16bc0 - U+16bdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16be0 - U+16bff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16c00 - U+16c1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16c20 - U+16c3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16c40 - U+16c5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16c60 - U+16c7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16c80 - U+16c9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16ca0 - U+16cbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16cc0 - U+16cdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16ce0 - U+16cff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16d00 - U+16d1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16d20 - U+16d3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16d40 - U+16d5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16d60 - U+16d7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16d80 - U+16d9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16da0 - U+16dbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16dc0 - U+16ddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16de0 - U+16dff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16e00 - U+16e1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16e20 - U+16e3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16e40 - U+16e5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16e60 - U+16e7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16e80 - U+16e9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16ea0 - U+16ebf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16ec0 - U+16edf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16ee0 - U+16eff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16f00 - U+16f1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16f20 - U+16f3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16f40 - U+16f5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16f60 - U+16f7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16f80 - U+16f9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16fa0 - U+16fbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16fc0 - U+16fdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+16fe0 - U+16fff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17000 - U+1701f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17020 - U+1703f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17040 - U+1705f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17060 - U+1707f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17080 - U+1709f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+170a0 - U+170bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+170c0 - U+170df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+170e0 - U+170ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17100 - U+1711f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17120 - U+1713f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17140 - U+1715f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17160 - U+1717f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17180 - U+1719f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+171a0 - U+171bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+171c0 - U+171df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+171e0 - U+171ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17200 - U+1721f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17220 - U+1723f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17240 - U+1725f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17260 - U+1727f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17280 - U+1729f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+172a0 - U+172bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+172c0 - U+172df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+172e0 - U+172ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17300 - U+1731f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17320 - U+1733f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17340 - U+1735f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17360 - U+1737f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17380 - U+1739f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+173a0 - U+173bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+173c0 - U+173df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+173e0 - U+173ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17400 - U+1741f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17420 - U+1743f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17440 - U+1745f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17460 - U+1747f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17480 - U+1749f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+174a0 - U+174bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+174c0 - U+174df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+174e0 - U+174ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17500 - U+1751f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17520 - U+1753f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17540 - U+1755f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17560 - U+1757f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17580 - U+1759f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+175a0 - U+175bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+175c0 - U+175df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+175e0 - U+175ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17600 - U+1761f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17620 - U+1763f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17640 - U+1765f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17660 - U+1767f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17680 - U+1769f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+176a0 - U+176bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+176c0 - U+176df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+176e0 - U+176ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17700 - U+1771f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17720 - U+1773f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17740 - U+1775f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17760 - U+1777f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17780 - U+1779f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+177a0 - U+177bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+177c0 - U+177df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+177e0 - U+177ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17800 - U+1781f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17820 - U+1783f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17840 - U+1785f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17860 - U+1787f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17880 - U+1789f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+178a0 - U+178bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+178c0 - U+178df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+178e0 - U+178ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17900 - U+1791f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17920 - U+1793f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17940 - U+1795f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17960 - U+1797f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17980 - U+1799f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+179a0 - U+179bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+179c0 - U+179df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+179e0 - U+179ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17a00 - U+17a1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17a20 - U+17a3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17a40 - U+17a5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17a60 - U+17a7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17a80 - U+17a9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17aa0 - U+17abf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17ac0 - U+17adf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17ae0 - U+17aff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17b00 - U+17b1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17b20 - U+17b3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17b40 - U+17b5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17b60 - U+17b7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17b80 - U+17b9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17ba0 - U+17bbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17bc0 - U+17bdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17be0 - U+17bff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17c00 - U+17c1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17c20 - U+17c3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17c40 - U+17c5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17c60 - U+17c7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17c80 - U+17c9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17ca0 - U+17cbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17cc0 - U+17cdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17ce0 - U+17cff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17d00 - U+17d1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17d20 - U+17d3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17d40 - U+17d5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17d60 - U+17d7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17d80 - U+17d9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17da0 - U+17dbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17dc0 - U+17ddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17de0 - U+17dff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17e00 - U+17e1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17e20 - U+17e3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17e40 - U+17e5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17e60 - U+17e7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17e80 - U+17e9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17ea0 - U+17ebf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17ec0 - U+17edf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17ee0 - U+17eff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17f00 - U+17f1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17f20 - U+17f3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17f40 - U+17f5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17f60 - U+17f7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17f80 - U+17f9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17fa0 - U+17fbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17fc0 - U+17fdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+17fe0 - U+17fff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18000 - U+1801f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18020 - U+1803f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18040 - U+1805f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18060 - U+1807f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18080 - U+1809f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+180a0 - U+180bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+180c0 - U+180df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+180e0 - U+180ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18100 - U+1811f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18120 - U+1813f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18140 - U+1815f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18160 - U+1817f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18180 - U+1819f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+181a0 - U+181bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+181c0 - U+181df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+181e0 - U+181ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18200 - U+1821f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18220 - U+1823f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18240 - U+1825f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18260 - U+1827f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18280 - U+1829f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+182a0 - U+182bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+182c0 - U+182df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+182e0 - U+182ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18300 - U+1831f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18320 - U+1833f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18340 - U+1835f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18360 - U+1837f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18380 - U+1839f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+183a0 - U+183bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+183c0 - U+183df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+183e0 - U+183ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18400 - U+1841f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18420 - U+1843f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18440 - U+1845f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18460 - U+1847f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18480 - U+1849f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+184a0 - U+184bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+184c0 - U+184df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+184e0 - U+184ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18500 - U+1851f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18520 - U+1853f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18540 - U+1855f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18560 - U+1857f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18580 - U+1859f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+185a0 - U+185bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+185c0 - U+185df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+185e0 - U+185ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18600 - U+1861f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18620 - U+1863f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18640 - U+1865f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18660 - U+1867f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18680 - U+1869f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+186a0 - U+186bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+186c0 - U+186df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+186e0 - U+186ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18700 - U+1871f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18720 - U+1873f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18740 - U+1875f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18760 - U+1877f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18780 - U+1879f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+187a0 - U+187bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+187c0 - U+187df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+187e0 - U+187ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18800 - U+1881f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18820 - U+1883f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18840 - U+1885f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18860 - U+1887f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18880 - U+1889f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+188a0 - U+188bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+188c0 - U+188df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+188e0 - U+188ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18900 - U+1891f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18920 - U+1893f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18940 - U+1895f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18960 - U+1897f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18980 - U+1899f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+189a0 - U+189bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+189c0 - U+189df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+189e0 - U+189ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18a00 - U+18a1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18a20 - U+18a3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18a40 - U+18a5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18a60 - U+18a7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18a80 - U+18a9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18aa0 - U+18abf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18ac0 - U+18adf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18ae0 - U+18aff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18b00 - U+18b1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18b20 - U+18b3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18b40 - U+18b5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18b60 - U+18b7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18b80 - U+18b9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18ba0 - U+18bbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18bc0 - U+18bdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18be0 - U+18bff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18c00 - U+18c1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18c20 - U+18c3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18c40 - U+18c5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18c60 - U+18c7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18c80 - U+18c9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18ca0 - U+18cbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18cc0 - U+18cdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18ce0 - U+18cff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18d00 - U+18d1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18d20 - U+18d3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18d40 - U+18d5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18d60 - U+18d7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18d80 - U+18d9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18da0 - U+18dbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18dc0 - U+18ddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18de0 - U+18dff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18e00 - U+18e1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18e20 - U+18e3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18e40 - U+18e5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18e60 - U+18e7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18e80 - U+18e9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18ea0 - U+18ebf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18ec0 - U+18edf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18ee0 - U+18eff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18f00 - U+18f1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18f20 - U+18f3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18f40 - U+18f5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18f60 - U+18f7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18f80 - U+18f9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18fa0 - U+18fbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18fc0 - U+18fdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+18fe0 - U+18fff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19000 - U+1901f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19020 - U+1903f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19040 - U+1905f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19060 - U+1907f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19080 - U+1909f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+190a0 - U+190bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+190c0 - U+190df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+190e0 - U+190ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19100 - U+1911f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19120 - U+1913f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19140 - U+1915f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19160 - U+1917f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19180 - U+1919f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+191a0 - U+191bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+191c0 - U+191df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+191e0 - U+191ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19200 - U+1921f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19220 - U+1923f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19240 - U+1925f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19260 - U+1927f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19280 - U+1929f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+192a0 - U+192bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+192c0 - U+192df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+192e0 - U+192ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19300 - U+1931f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19320 - U+1933f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19340 - U+1935f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19360 - U+1937f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19380 - U+1939f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+193a0 - U+193bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+193c0 - U+193df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+193e0 - U+193ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19400 - U+1941f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19420 - U+1943f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19440 - U+1945f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19460 - U+1947f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19480 - U+1949f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+194a0 - U+194bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+194c0 - U+194df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+194e0 - U+194ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19500 - U+1951f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19520 - U+1953f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19540 - U+1955f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19560 - U+1957f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19580 - U+1959f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+195a0 - U+195bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+195c0 - U+195df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+195e0 - U+195ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19600 - U+1961f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19620 - U+1963f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19640 - U+1965f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19660 - U+1967f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19680 - U+1969f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+196a0 - U+196bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+196c0 - U+196df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+196e0 - U+196ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19700 - U+1971f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19720 - U+1973f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19740 - U+1975f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19760 - U+1977f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19780 - U+1979f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+197a0 - U+197bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+197c0 - U+197df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+197e0 - U+197ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19800 - U+1981f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19820 - U+1983f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19840 - U+1985f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19860 - U+1987f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19880 - U+1989f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+198a0 - U+198bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+198c0 - U+198df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+198e0 - U+198ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19900 - U+1991f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19920 - U+1993f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19940 - U+1995f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19960 - U+1997f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19980 - U+1999f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+199a0 - U+199bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+199c0 - U+199df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+199e0 - U+199ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19a00 - U+19a1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19a20 - U+19a3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19a40 - U+19a5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19a60 - U+19a7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19a80 - U+19a9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19aa0 - U+19abf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19ac0 - U+19adf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19ae0 - U+19aff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19b00 - U+19b1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19b20 - U+19b3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19b40 - U+19b5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19b60 - U+19b7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19b80 - U+19b9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19ba0 - U+19bbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19bc0 - U+19bdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19be0 - U+19bff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19c00 - U+19c1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19c20 - U+19c3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19c40 - U+19c5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19c60 - U+19c7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19c80 - U+19c9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19ca0 - U+19cbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19cc0 - U+19cdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19ce0 - U+19cff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19d00 - U+19d1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19d20 - U+19d3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19d40 - U+19d5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19d60 - U+19d7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19d80 - U+19d9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19da0 - U+19dbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19dc0 - U+19ddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19de0 - U+19dff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19e00 - U+19e1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19e20 - U+19e3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19e40 - U+19e5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19e60 - U+19e7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19e80 - U+19e9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19ea0 - U+19ebf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19ec0 - U+19edf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19ee0 - U+19eff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19f00 - U+19f1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19f20 - U+19f3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19f40 - U+19f5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19f60 - U+19f7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19f80 - U+19f9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19fa0 - U+19fbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19fc0 - U+19fdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+19fe0 - U+19fff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a000 - U+1a01f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a020 - U+1a03f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a040 - U+1a05f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a060 - U+1a07f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a080 - U+1a09f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a0a0 - U+1a0bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a0c0 - U+1a0df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a0e0 - U+1a0ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a100 - U+1a11f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a120 - U+1a13f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a140 - U+1a15f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a160 - U+1a17f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a180 - U+1a19f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a1a0 - U+1a1bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a1c0 - U+1a1df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a1e0 - U+1a1ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a200 - U+1a21f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a220 - U+1a23f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a240 - U+1a25f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a260 - U+1a27f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a280 - U+1a29f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a2a0 - U+1a2bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a2c0 - U+1a2df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a2e0 - U+1a2ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a300 - U+1a31f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a320 - U+1a33f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a340 - U+1a35f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a360 - U+1a37f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a380 - U+1a39f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a3a0 - U+1a3bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a3c0 - U+1a3df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a3e0 - U+1a3ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a400 - U+1a41f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a420 - U+1a43f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a440 - U+1a45f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a460 - U+1a47f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a480 - U+1a49f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a4a0 - U+1a4bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a4c0 - U+1a4df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a4e0 - U+1a4ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a500 - U+1a51f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a520 - U+1a53f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a540 - U+1a55f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a560 - U+1a57f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a580 - U+1a59f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a5a0 - U+1a5bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a5c0 - U+1a5df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a5e0 - U+1a5ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a600 - U+1a61f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a620 - U+1a63f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a640 - U+1a65f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a660 - U+1a67f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a680 - U+1a69f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a6a0 - U+1a6bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a6c0 - U+1a6df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a6e0 - U+1a6ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a700 - U+1a71f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a720 - U+1a73f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a740 - U+1a75f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a760 - U+1a77f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a780 - U+1a79f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a7a0 - U+1a7bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a7c0 - U+1a7df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a7e0 - U+1a7ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a800 - U+1a81f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a820 - U+1a83f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a840 - U+1a85f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a860 - U+1a87f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a880 - U+1a89f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a8a0 - U+1a8bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a8c0 - U+1a8df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a8e0 - U+1a8ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a900 - U+1a91f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a920 - U+1a93f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a940 - U+1a95f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a960 - U+1a97f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a980 - U+1a99f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a9a0 - U+1a9bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a9c0 - U+1a9df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1a9e0 - U+1a9ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1aa00 - U+1aa1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1aa20 - U+1aa3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1aa40 - U+1aa5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1aa60 - U+1aa7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1aa80 - U+1aa9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1aaa0 - U+1aabf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1aac0 - U+1aadf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1aae0 - U+1aaff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ab00 - U+1ab1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ab20 - U+1ab3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ab40 - U+1ab5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ab60 - U+1ab7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ab80 - U+1ab9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1aba0 - U+1abbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1abc0 - U+1abdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1abe0 - U+1abff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ac00 - U+1ac1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ac20 - U+1ac3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ac40 - U+1ac5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ac60 - U+1ac7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ac80 - U+1ac9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1aca0 - U+1acbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1acc0 - U+1acdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ace0 - U+1acff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ad00 - U+1ad1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ad20 - U+1ad3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ad40 - U+1ad5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ad60 - U+1ad7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ad80 - U+1ad9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ada0 - U+1adbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1adc0 - U+1addf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ade0 - U+1adff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ae00 - U+1ae1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ae20 - U+1ae3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ae40 - U+1ae5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ae60 - U+1ae7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ae80 - U+1ae9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1aea0 - U+1aebf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1aec0 - U+1aedf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1aee0 - U+1aeff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1af00 - U+1af1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1af20 - U+1af3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1af40 - U+1af5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1af60 - U+1af7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1af80 - U+1af9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1afa0 - U+1afbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1afc0 - U+1afdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1afe0 - U+1afff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b000 - U+1b01f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b020 - U+1b03f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b040 - U+1b05f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b060 - U+1b07f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b080 - U+1b09f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b0a0 - U+1b0bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b0c0 - U+1b0df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b0e0 - U+1b0ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b100 - U+1b11f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b120 - U+1b13f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b140 - U+1b15f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b160 - U+1b17f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b180 - U+1b19f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b1a0 - U+1b1bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b1c0 - U+1b1df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b1e0 - U+1b1ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b200 - U+1b21f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b220 - U+1b23f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b240 - U+1b25f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b260 - U+1b27f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b280 - U+1b29f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b2a0 - U+1b2bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b2c0 - U+1b2df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b2e0 - U+1b2ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b300 - U+1b31f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b320 - U+1b33f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b340 - U+1b35f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b360 - U+1b37f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b380 - U+1b39f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b3a0 - U+1b3bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b3c0 - U+1b3df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b3e0 - U+1b3ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b400 - U+1b41f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b420 - U+1b43f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b440 - U+1b45f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b460 - U+1b47f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b480 - U+1b49f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b4a0 - U+1b4bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b4c0 - U+1b4df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b4e0 - U+1b4ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b500 - U+1b51f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b520 - U+1b53f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b540 - U+1b55f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b560 - U+1b57f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b580 - U+1b59f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b5a0 - U+1b5bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b5c0 - U+1b5df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b5e0 - U+1b5ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b600 - U+1b61f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b620 - U+1b63f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b640 - U+1b65f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b660 - U+1b67f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b680 - U+1b69f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b6a0 - U+1b6bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b6c0 - U+1b6df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b6e0 - U+1b6ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b700 - U+1b71f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b720 - U+1b73f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b740 - U+1b75f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b760 - U+1b77f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b780 - U+1b79f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b7a0 - U+1b7bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b7c0 - U+1b7df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b7e0 - U+1b7ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b800 - U+1b81f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b820 - U+1b83f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b840 - U+1b85f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b860 - U+1b87f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b880 - U+1b89f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b8a0 - U+1b8bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b8c0 - U+1b8df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b8e0 - U+1b8ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b900 - U+1b91f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b920 - U+1b93f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b940 - U+1b95f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b960 - U+1b97f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b980 - U+1b99f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b9a0 - U+1b9bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b9c0 - U+1b9df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1b9e0 - U+1b9ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ba00 - U+1ba1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ba20 - U+1ba3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ba40 - U+1ba5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ba60 - U+1ba7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ba80 - U+1ba9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1baa0 - U+1babf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bac0 - U+1badf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bae0 - U+1baff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bb00 - U+1bb1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bb20 - U+1bb3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bb40 - U+1bb5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bb60 - U+1bb7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bb80 - U+1bb9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bba0 - U+1bbbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bbc0 - U+1bbdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bbe0 - U+1bbff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bc00 - U+1bc1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bc20 - U+1bc3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bc40 - U+1bc5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bc60 - U+1bc7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bc80 - U+1bc9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bca0 - U+1bcbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bcc0 - U+1bcdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bce0 - U+1bcff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bd00 - U+1bd1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bd20 - U+1bd3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bd40 - U+1bd5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bd60 - U+1bd7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bd80 - U+1bd9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bda0 - U+1bdbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bdc0 - U+1bddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bde0 - U+1bdff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1be00 - U+1be1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1be20 - U+1be3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1be40 - U+1be5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1be60 - U+1be7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1be80 - U+1be9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bea0 - U+1bebf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bec0 - U+1bedf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bee0 - U+1beff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bf00 - U+1bf1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bf20 - U+1bf3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bf40 - U+1bf5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bf60 - U+1bf7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bf80 - U+1bf9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bfa0 - U+1bfbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bfc0 - U+1bfdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1bfe0 - U+1bfff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c000 - U+1c01f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c020 - U+1c03f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c040 - U+1c05f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c060 - U+1c07f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c080 - U+1c09f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c0a0 - U+1c0bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c0c0 - U+1c0df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c0e0 - U+1c0ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c100 - U+1c11f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c120 - U+1c13f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c140 - U+1c15f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c160 - U+1c17f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c180 - U+1c19f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c1a0 - U+1c1bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c1c0 - U+1c1df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c1e0 - U+1c1ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c200 - U+1c21f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c220 - U+1c23f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c240 - U+1c25f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c260 - U+1c27f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c280 - U+1c29f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c2a0 - U+1c2bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c2c0 - U+1c2df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c2e0 - U+1c2ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c300 - U+1c31f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c320 - U+1c33f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c340 - U+1c35f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c360 - U+1c37f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c380 - U+1c39f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c3a0 - U+1c3bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c3c0 - U+1c3df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c3e0 - U+1c3ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c400 - U+1c41f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c420 - U+1c43f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c440 - U+1c45f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c460 - U+1c47f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c480 - U+1c49f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c4a0 - U+1c4bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c4c0 - U+1c4df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c4e0 - U+1c4ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c500 - U+1c51f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c520 - U+1c53f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c540 - U+1c55f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c560 - U+1c57f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c580 - U+1c59f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c5a0 - U+1c5bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c5c0 - U+1c5df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c5e0 - U+1c5ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c600 - U+1c61f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c620 - U+1c63f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c640 - U+1c65f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c660 - U+1c67f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c680 - U+1c69f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c6a0 - U+1c6bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c6c0 - U+1c6df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c6e0 - U+1c6ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c700 - U+1c71f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c720 - U+1c73f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c740 - U+1c75f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c760 - U+1c77f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c780 - U+1c79f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c7a0 - U+1c7bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c7c0 - U+1c7df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c7e0 - U+1c7ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c800 - U+1c81f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c820 - U+1c83f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c840 - U+1c85f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c860 - U+1c87f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c880 - U+1c89f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c8a0 - U+1c8bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c8c0 - U+1c8df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c8e0 - U+1c8ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c900 - U+1c91f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c920 - U+1c93f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c940 - U+1c95f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c960 - U+1c97f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c980 - U+1c99f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c9a0 - U+1c9bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c9c0 - U+1c9df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1c9e0 - U+1c9ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ca00 - U+1ca1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ca20 - U+1ca3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ca40 - U+1ca5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ca60 - U+1ca7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ca80 - U+1ca9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1caa0 - U+1cabf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cac0 - U+1cadf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cae0 - U+1caff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cb00 - U+1cb1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cb20 - U+1cb3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cb40 - U+1cb5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cb60 - U+1cb7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cb80 - U+1cb9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cba0 - U+1cbbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cbc0 - U+1cbdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cbe0 - U+1cbff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cc00 - U+1cc1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cc20 - U+1cc3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cc40 - U+1cc5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cc60 - U+1cc7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cc80 - U+1cc9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cca0 - U+1ccbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ccc0 - U+1ccdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cce0 - U+1ccff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cd00 - U+1cd1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cd20 - U+1cd3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cd40 - U+1cd5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cd60 - U+1cd7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cd80 - U+1cd9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cda0 - U+1cdbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cdc0 - U+1cddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cde0 - U+1cdff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ce00 - U+1ce1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ce20 - U+1ce3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ce40 - U+1ce5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ce60 - U+1ce7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ce80 - U+1ce9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cea0 - U+1cebf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cec0 - U+1cedf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cee0 - U+1ceff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cf00 - U+1cf1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cf20 - U+1cf3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cf40 - U+1cf5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cf60 - U+1cf7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cf80 - U+1cf9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cfa0 - U+1cfbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cfc0 - U+1cfdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1cfe0 - U+1cfff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d000 - U+1d01f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d020 - U+1d03f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d040 - U+1d05f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d060 - U+1d07f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d080 - U+1d09f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d0a0 - U+1d0bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d0c0 - U+1d0df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d0e0 - U+1d0ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d100 - U+1d11f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d120 - U+1d13f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d140 - U+1d15f */
+ 0xff,0xfc,0x0f,0xff,0xfc,0x00,0x00,0x00, /* U+1d160 - U+1d17f */
+ 0x03,0xc0,0x00,0xff,0xff,0xff,0xff,0xff, /* U+1d180 - U+1d19f */
+ 0xff,0xff,0xf0,0x0f,0xff,0xff,0xff,0xff, /* U+1d1a0 - U+1d1bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d1c0 - U+1d1df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d1e0 - U+1d1ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d200 - U+1d21f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d220 - U+1d23f */
+ 0xf0,0x3f,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d240 - U+1d25f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d260 - U+1d27f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d280 - U+1d29f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d2a0 - U+1d2bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d2c0 - U+1d2df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d2e0 - U+1d2ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d300 - U+1d31f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d320 - U+1d33f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d340 - U+1d35f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d360 - U+1d37f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d380 - U+1d39f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d3a0 - U+1d3bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d3c0 - U+1d3df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d3e0 - U+1d3ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d400 - U+1d41f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d420 - U+1d43f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d440 - U+1d45f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d460 - U+1d47f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d480 - U+1d49f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d4a0 - U+1d4bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d4c0 - U+1d4df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d4e0 - U+1d4ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d500 - U+1d51f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d520 - U+1d53f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d540 - U+1d55f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d560 - U+1d57f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d580 - U+1d59f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d5a0 - U+1d5bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d5c0 - U+1d5df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d5e0 - U+1d5ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d600 - U+1d61f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d620 - U+1d63f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d640 - U+1d65f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d660 - U+1d67f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d680 - U+1d69f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d6a0 - U+1d6bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d6c0 - U+1d6df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d6e0 - U+1d6ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d700 - U+1d71f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d720 - U+1d73f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d740 - U+1d75f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d760 - U+1d77f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d780 - U+1d79f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d7a0 - U+1d7bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d7c0 - U+1d7df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d7e0 - U+1d7ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d800 - U+1d81f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d820 - U+1d83f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d840 - U+1d85f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d860 - U+1d87f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d880 - U+1d89f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d8a0 - U+1d8bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d8c0 - U+1d8df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d8e0 - U+1d8ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d900 - U+1d91f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d920 - U+1d93f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d940 - U+1d95f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d960 - U+1d97f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d980 - U+1d99f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d9a0 - U+1d9bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d9c0 - U+1d9df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1d9e0 - U+1d9ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1da00 - U+1da1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1da20 - U+1da3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1da40 - U+1da5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1da60 - U+1da7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1da80 - U+1da9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1daa0 - U+1dabf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dac0 - U+1dadf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dae0 - U+1daff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1db00 - U+1db1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1db20 - U+1db3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1db40 - U+1db5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1db60 - U+1db7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1db80 - U+1db9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dba0 - U+1dbbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dbc0 - U+1dbdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dbe0 - U+1dbff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dc00 - U+1dc1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dc20 - U+1dc3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dc40 - U+1dc5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dc60 - U+1dc7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dc80 - U+1dc9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dca0 - U+1dcbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dcc0 - U+1dcdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dce0 - U+1dcff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dd00 - U+1dd1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dd20 - U+1dd3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dd40 - U+1dd5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dd60 - U+1dd7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dd80 - U+1dd9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dda0 - U+1ddbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ddc0 - U+1dddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dde0 - U+1ddff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1de00 - U+1de1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1de20 - U+1de3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1de40 - U+1de5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1de60 - U+1de7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1de80 - U+1de9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dea0 - U+1debf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dec0 - U+1dedf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dee0 - U+1deff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1df00 - U+1df1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1df20 - U+1df3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1df40 - U+1df5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1df60 - U+1df7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1df80 - U+1df9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dfa0 - U+1dfbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dfc0 - U+1dfdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1dfe0 - U+1dfff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e000 - U+1e01f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e020 - U+1e03f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e040 - U+1e05f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e060 - U+1e07f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e080 - U+1e09f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e0a0 - U+1e0bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e0c0 - U+1e0df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e0e0 - U+1e0ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e100 - U+1e11f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e120 - U+1e13f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e140 - U+1e15f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e160 - U+1e17f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e180 - U+1e19f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e1a0 - U+1e1bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e1c0 - U+1e1df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e1e0 - U+1e1ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e200 - U+1e21f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e220 - U+1e23f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e240 - U+1e25f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e260 - U+1e27f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e280 - U+1e29f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e2a0 - U+1e2bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e2c0 - U+1e2df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e2e0 - U+1e2ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e300 - U+1e31f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e320 - U+1e33f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e340 - U+1e35f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e360 - U+1e37f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e380 - U+1e39f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e3a0 - U+1e3bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e3c0 - U+1e3df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e3e0 - U+1e3ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e400 - U+1e41f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e420 - U+1e43f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e440 - U+1e45f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e460 - U+1e47f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e480 - U+1e49f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e4a0 - U+1e4bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e4c0 - U+1e4df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e4e0 - U+1e4ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e500 - U+1e51f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e520 - U+1e53f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e540 - U+1e55f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e560 - U+1e57f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e580 - U+1e59f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e5a0 - U+1e5bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e5c0 - U+1e5df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e5e0 - U+1e5ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e600 - U+1e61f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e620 - U+1e63f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e640 - U+1e65f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e660 - U+1e67f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e680 - U+1e69f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e6a0 - U+1e6bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e6c0 - U+1e6df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e6e0 - U+1e6ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e700 - U+1e71f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e720 - U+1e73f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e740 - U+1e75f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e760 - U+1e77f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e780 - U+1e79f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e7a0 - U+1e7bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e7c0 - U+1e7df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e7e0 - U+1e7ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e800 - U+1e81f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e820 - U+1e83f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e840 - U+1e85f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e860 - U+1e87f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e880 - U+1e89f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e8a0 - U+1e8bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e8c0 - U+1e8df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e8e0 - U+1e8ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e900 - U+1e91f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e920 - U+1e93f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e940 - U+1e95f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e960 - U+1e97f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e980 - U+1e99f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e9a0 - U+1e9bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e9c0 - U+1e9df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1e9e0 - U+1e9ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ea00 - U+1ea1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ea20 - U+1ea3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ea40 - U+1ea5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ea60 - U+1ea7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ea80 - U+1ea9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1eaa0 - U+1eabf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1eac0 - U+1eadf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1eae0 - U+1eaff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1eb00 - U+1eb1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1eb20 - U+1eb3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1eb40 - U+1eb5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1eb60 - U+1eb7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1eb80 - U+1eb9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1eba0 - U+1ebbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ebc0 - U+1ebdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ebe0 - U+1ebff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ec00 - U+1ec1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ec20 - U+1ec3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ec40 - U+1ec5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ec60 - U+1ec7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ec80 - U+1ec9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1eca0 - U+1ecbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ecc0 - U+1ecdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ece0 - U+1ecff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ed00 - U+1ed1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ed20 - U+1ed3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ed40 - U+1ed5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ed60 - U+1ed7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ed80 - U+1ed9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1eda0 - U+1edbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1edc0 - U+1eddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ede0 - U+1edff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ee00 - U+1ee1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ee20 - U+1ee3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ee40 - U+1ee5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ee60 - U+1ee7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ee80 - U+1ee9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1eea0 - U+1eebf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1eec0 - U+1eedf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1eee0 - U+1eeff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ef00 - U+1ef1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ef20 - U+1ef3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ef40 - U+1ef5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ef60 - U+1ef7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ef80 - U+1ef9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1efa0 - U+1efbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1efc0 - U+1efdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1efe0 - U+1efff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f000 - U+1f01f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f020 - U+1f03f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f040 - U+1f05f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f060 - U+1f07f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f080 - U+1f09f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f0a0 - U+1f0bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f0c0 - U+1f0df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f0e0 - U+1f0ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f100 - U+1f11f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f120 - U+1f13f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f140 - U+1f15f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f160 - U+1f17f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f180 - U+1f19f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f1a0 - U+1f1bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f1c0 - U+1f1df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f1e0 - U+1f1ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f200 - U+1f21f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f220 - U+1f23f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f240 - U+1f25f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f260 - U+1f27f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f280 - U+1f29f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f2a0 - U+1f2bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f2c0 - U+1f2df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f2e0 - U+1f2ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f300 - U+1f31f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f320 - U+1f33f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f340 - U+1f35f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f360 - U+1f37f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f380 - U+1f39f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f3a0 - U+1f3bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f3c0 - U+1f3df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f3e0 - U+1f3ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f400 - U+1f41f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f420 - U+1f43f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f440 - U+1f45f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f460 - U+1f47f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f480 - U+1f49f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f4a0 - U+1f4bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f4c0 - U+1f4df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f4e0 - U+1f4ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f500 - U+1f51f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f520 - U+1f53f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f540 - U+1f55f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f560 - U+1f57f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f580 - U+1f59f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f5a0 - U+1f5bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f5c0 - U+1f5df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f5e0 - U+1f5ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f600 - U+1f61f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f620 - U+1f63f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f640 - U+1f65f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f660 - U+1f67f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f680 - U+1f69f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f6a0 - U+1f6bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f6c0 - U+1f6df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f6e0 - U+1f6ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f700 - U+1f71f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f720 - U+1f73f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f740 - U+1f75f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f760 - U+1f77f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f780 - U+1f79f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f7a0 - U+1f7bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f7c0 - U+1f7df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f7e0 - U+1f7ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f800 - U+1f81f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f820 - U+1f83f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f840 - U+1f85f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f860 - U+1f87f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f880 - U+1f89f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f8a0 - U+1f8bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f8c0 - U+1f8df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f8e0 - U+1f8ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f900 - U+1f91f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f920 - U+1f93f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f940 - U+1f95f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f960 - U+1f97f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f980 - U+1f99f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f9a0 - U+1f9bf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f9c0 - U+1f9df */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1f9e0 - U+1f9ff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fa00 - U+1fa1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fa20 - U+1fa3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fa40 - U+1fa5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fa60 - U+1fa7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fa80 - U+1fa9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1faa0 - U+1fabf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fac0 - U+1fadf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fae0 - U+1faff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fb00 - U+1fb1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fb20 - U+1fb3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fb40 - U+1fb5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fb60 - U+1fb7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fb80 - U+1fb9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fba0 - U+1fbbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fbc0 - U+1fbdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fbe0 - U+1fbff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fc00 - U+1fc1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fc20 - U+1fc3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fc40 - U+1fc5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fc60 - U+1fc7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fc80 - U+1fc9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fca0 - U+1fcbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fcc0 - U+1fcdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fce0 - U+1fcff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fd00 - U+1fd1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fd20 - U+1fd3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fd40 - U+1fd5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fd60 - U+1fd7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fd80 - U+1fd9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fda0 - U+1fdbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fdc0 - U+1fddf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fde0 - U+1fdff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fe00 - U+1fe1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fe20 - U+1fe3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fe40 - U+1fe5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fe60 - U+1fe7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fe80 - U+1fe9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fea0 - U+1febf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fec0 - U+1fedf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1fee0 - U+1feff */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ff00 - U+1ff1f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ff20 - U+1ff3f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ff40 - U+1ff5f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ff60 - U+1ff7f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ff80 - U+1ff9f */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ffa0 - U+1ffbf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* U+1ffc0 - U+1ffdf */
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff /* U+1ffe0 - U+1ffff */
+};
diff --git a/imap/src/charset/windows.c b/imap/src/charset/windows.c
new file mode 100644
index 00000000..0f8edc97
--- /dev/null
+++ b/imap/src/charset/windows.c
@@ -0,0 +1,228 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Windows conversion tables
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 16 October 2000
+ * Last Edited: 30 August 2006
+ */
+
+ /* Windows Thai */
+static const unsigned short windows_874tab[128] = {
+ 0x20ac,UBOGON,UBOGON,UBOGON,UBOGON,0x2026,UBOGON,UBOGON,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x2018,0x2019,0x201c,0x201d,0x2022,0x2013,0x2014,
+ UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x00a0,0x0e01,0x0e02,0x0e03,0x0e04,0x0e05,0x0e06,0x0e07,
+ 0x0e08,0x0e09,0x0e0a,0x0e0b,0x0e0c,0x0e0d,0x0e0e,0x0e0f,
+ 0x0e10,0x0e11,0x0e12,0x0e13,0x0e14,0x0e15,0x0e16,0x0e17,
+ 0x0e18,0x0e19,0x0e1a,0x0e1b,0x0e1c,0x0e1d,0x0e1e,0x0e1f,
+ 0x0e20,0x0e21,0x0e22,0x0e23,0x0e24,0x0e25,0x0e26,0x0e27,
+ 0x0e28,0x0e29,0x0e2a,0x0e2b,0x0e2c,0x0e2d,0x0e2e,0x0e2f,
+ 0x0e30,0x0e31,0x0e32,0x0e33,0x0e34,0x0e35,0x0e36,0x0e37,
+ 0x0e38,0x0e39,0x0e3a,UBOGON,UBOGON,UBOGON,UBOGON,0x0e3f,
+ 0x0e40,0x0e41,0x0e42,0x0e43,0x0e44,0x0e45,0x0e46,0x0e47,
+ 0x0e48,0x0e49,0x0e4a,0x0e4b,0x0e4c,0x0e4d,0x0e4e,0x0e4f,
+ 0x0e50,0x0e51,0x0e52,0x0e53,0x0e54,0x0e55,0x0e56,0x0e57,
+ 0x0e58,0x0e59,0x0e5a,0x0e5b,UBOGON,UBOGON,UBOGON,UBOGON
+};
+
+ /* Windows Latin-2 */
+static const unsigned short windows_1250tab[128] = {
+ 0x20ac,UBOGON,0x201a,UBOGON,0x201e,0x2026,0x2020,0x2021,
+ UBOGON,0x2030,0x0160,0x2039,0x015a,0x0164,0x017d,0x0179,
+ UBOGON,0x2018,0x2019,0x201c,0x201d,0x2022,0x2013,0x2014,
+ UBOGON,0x2122,0x0161,0x203a,0x015b,0x0165,0x017e,0x017a,
+ 0x00a0,0x02c7,0x02d8,0x0141,0x00a4,0x0104,0x00a6,0x00a7,
+ 0x00a8,0x00a9,0x015e,0x00ab,0x00ac,0x00ad,0x00ae,0x017b,
+ 0x00b0,0x00b1,0x02db,0x0142,0x00b4,0x00b5,0x00b6,0x00b7,
+ 0x00b8,0x0105,0x015f,0x00bb,0x013d,0x02dd,0x013e,0x017c,
+ 0x0154,0x00c1,0x00c2,0x0102,0x00c4,0x0139,0x0106,0x00c7,
+ 0x010c,0x00c9,0x0118,0x00cb,0x011a,0x00cd,0x00ce,0x010e,
+ 0x0110,0x0143,0x0147,0x00d3,0x00d4,0x0150,0x00d6,0x00d7,
+ 0x0158,0x016e,0x00da,0x0170,0x00dc,0x00dd,0x0162,0x00df,
+ 0x0155,0x00e1,0x00e2,0x0103,0x00e4,0x013a,0x0107,0x00e7,
+ 0x010d,0x00e9,0x0119,0x00eb,0x011b,0x00ed,0x00ee,0x010f,
+ 0x0111,0x0144,0x0148,0x00f3,0x00f4,0x0151,0x00f6,0x00f7,
+ 0x0159,0x016f,0x00fa,0x0171,0x00fc,0x00fd,0x0163,0x02d9
+};
+
+ /* Windows Cyrillic */
+static const unsigned short windows_1251tab[128] = {
+ 0x0402,0x0403,0x201a,0x0453,0x201e,0x2026,0x2020,0x2021,
+ 0x20ac,0x2030,0x0409,0x2039,0x040a,0x040c,0x040b,0x040f,
+ 0x0452,0x2018,0x2019,0x201c,0x201d,0x2022,0x2013,0x2014,
+ UBOGON,0x2122,0x0459,0x203a,0x045a,0x045c,0x045b,0x045f,
+ 0x00a0,0x040e,0x045e,0x0408,0x00a4,0x0490,0x00a6,0x00a7,
+ 0x0401,0x00a9,0x0404,0x00ab,0x00ac,0x00ad,0x00ae,0x0407,
+ 0x00b0,0x00b1,0x0406,0x0456,0x0491,0x00b5,0x00b6,0x00b7,
+ 0x0451,0x2116,0x0454,0x00bb,0x0458,0x0405,0x0455,0x0457,
+ 0x0410,0x0411,0x0412,0x0413,0x0414,0x0415,0x0416,0x0417,
+ 0x0418,0x0419,0x041a,0x041b,0x041c,0x041d,0x041e,0x041f,
+ 0x0420,0x0421,0x0422,0x0423,0x0424,0x0425,0x0426,0x0427,
+ 0x0428,0x0429,0x042a,0x042b,0x042c,0x042d,0x042e,0x042f,
+ 0x0430,0x0431,0x0432,0x0433,0x0434,0x0435,0x0436,0x0437,
+ 0x0438,0x0439,0x043a,0x043b,0x043c,0x043d,0x043e,0x043f,
+ 0x0440,0x0441,0x0442,0x0443,0x0444,0x0445,0x0446,0x0447,
+ 0x0448,0x0449,0x044a,0x044b,0x044c,0x044d,0x044e,0x044f
+};
+
+ /* Windows Latin-1 */
+static const unsigned short windows_1252tab[128] = {
+ 0x20ac,UBOGON,0x201a,0x0192,0x201e,0x2026,0x2020,0x2021,
+ 0x02c6,0x2030,0x0160,0x2039,0x0152,UBOGON,0x017d,UBOGON,
+ UBOGON,0x2018,0x2019,0x201c,0x201d,0x2022,0x2013,0x2014,
+ 0x02dc,0x2122,0x0161,0x203a,0x0153,UBOGON,0x017e,0x0178,
+ 0x00a0,0x00a1,0x00a2,0x00a3,0x00a4,0x00a5,0x00a6,0x00a7,
+ 0x00a8,0x00a9,0x00aa,0x00ab,0x00ac,0x00ad,0x00ae,0x00af,
+ 0x00b0,0x00b1,0x00b2,0x00b3,0x00b4,0x00b5,0x00b6,0x00b7,
+ 0x00b8,0x00b9,0x00ba,0x00bb,0x00bc,0x00bd,0x00be,0x00bf,
+ 0x00c0,0x00c1,0x00c2,0x00c3,0x00c4,0x00c5,0x00c6,0x00c7,
+ 0x00c8,0x00c9,0x00ca,0x00cb,0x00cc,0x00cd,0x00ce,0x00cf,
+ 0x00d0,0x00d1,0x00d2,0x00d3,0x00d4,0x00d5,0x00d6,0x00d7,
+ 0x00d8,0x00d9,0x00da,0x00db,0x00dc,0x00dd,0x00de,0x00df,
+ 0x00e0,0x00e1,0x00e2,0x00e3,0x00e4,0x00e5,0x00e6,0x00e7,
+ 0x00e8,0x00e9,0x00ea,0x00eb,0x00ec,0x00ed,0x00ee,0x00ef,
+ 0x00f0,0x00f1,0x00f2,0x00f3,0x00f4,0x00f5,0x00f6,0x00f7,
+ 0x00f8,0x00f9,0x00fa,0x00fb,0x00fc,0x00fd,0x00fe,0x00ff
+};
+
+
+ /* Windows Greek */
+static const unsigned short windows_1253tab[128] = {
+ 0x20ac,UBOGON,0x201a,0x0192,0x201e,0x2026,0x2020,0x2021,
+ UBOGON,0x2030,UBOGON,0x2039,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x2018,0x2019,0x201c,0x201d,0x2022,0x2013,0x2014,
+ UBOGON,0x2122,UBOGON,0x203a,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x00a0,0x0385,0x0386,0x00a3,0x00a4,0x00a5,0x00a6,0x00a7,
+ 0x00a8,0x00a9,UBOGON,0x00ab,0x00ac,0x00ad,0x00ae,0x2015,
+ 0x00b0,0x00b1,0x00b2,0x00b3,0x0384,0x00b5,0x00b6,0x00b7,
+ 0x0388,0x0389,0x038a,0x00bb,0x038c,0x00bd,0x038e,0x038f,
+ 0x0390,0x0391,0x0392,0x0393,0x0394,0x0395,0x0396,0x0397,
+ 0x0398,0x0399,0x039a,0x039b,0x039c,0x039d,0x039e,0x039f,
+ 0x03a0,0x03a1,UBOGON,0x03a3,0x03a4,0x03a5,0x03a6,0x03a7,
+ 0x03a8,0x03a9,0x03aa,0x03ab,0x03ac,0x03ad,0x03ae,0x03af,
+ 0x03b0,0x03b1,0x03b2,0x03b3,0x03b4,0x03b5,0x03b6,0x03b7,
+ 0x03b8,0x03b9,0x03ba,0x03bb,0x03bc,0x03bd,0x03be,0x03bf,
+ 0x03c0,0x03c1,0x03c2,0x03c3,0x03c4,0x03c5,0x03c6,0x03c7,
+ 0x03c8,0x03c9,0x03ca,0x03cb,0x03cc,0x03cd,0x03ce,UBOGON
+};
+
+ /* Windows Turkish */
+static const unsigned short windows_1254tab[128] = {
+ 0x20ac,UBOGON,0x201a,0x0192,0x201e,0x2026,0x2020,0x2021,
+ 0x02c6,0x2030,0x0160,0x2039,0x0152,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x2018,0x2019,0x201c,0x201d,0x2022,0x2013,0x2014,
+ 0x02dc,0x2122,0x0161,0x203a,0x0153,UBOGON,UBOGON,0x0178,
+ 0x00a0,0x00a1,0x00a2,0x00a3,0x00a4,0x00a5,0x00a6,0x00a7,
+ 0x00a8,0x00a9,0x00aa,0x00ab,0x00ac,0x00ad,0x00ae,0x00af,
+ 0x00b0,0x00b1,0x00b2,0x00b3,0x00b4,0x00b5,0x00b6,0x00b7,
+ 0x00b8,0x00b9,0x00ba,0x00bb,0x00bc,0x00bd,0x00be,0x00bf,
+ 0x00c0,0x00c1,0x00c2,0x00c3,0x00c4,0x00c5,0x00c6,0x00c7,
+ 0x00c8,0x00c9,0x00ca,0x00cb,0x00cc,0x00cd,0x00ce,0x00cf,
+ 0x011e,0x00d1,0x00d2,0x00d3,0x00d4,0x00d5,0x00d6,0x00d7,
+ 0x00d8,0x00d9,0x00da,0x00db,0x00dc,0x0130,0x015e,0x00df,
+ 0x00e0,0x00e1,0x00e2,0x00e3,0x00e4,0x00e5,0x00e6,0x00e7,
+ 0x00e8,0x00e9,0x00ea,0x00eb,0x00ec,0x00ed,0x00ee,0x00ef,
+ 0x011f,0x00f1,0x00f2,0x00f3,0x00f4,0x00f5,0x00f6,0x00f7,
+ 0x00f8,0x00f9,0x00fa,0x00fb,0x00fc,0x0131,0x015f,0x00ff
+};
+
+ /* Windows Hebrew */
+static const unsigned short windows_1255tab[128] = {
+ 0x20ac,UBOGON,0x201a,0x0192,0x201e,0x2026,0x2020,0x2021,
+ 0x02c6,0x2030,UBOGON,0x2039,UBOGON,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x2018,0x2019,0x201c,0x201d,0x2022,0x2013,0x2014,
+ 0x02dc,0x2122,UBOGON,0x203a,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x00a0,0x00a1,0x00a2,0x00a3,0x20aa,0x00a5,0x00a6,0x00a7,
+ 0x00a8,0x00a9,0x00d7,0x00ab,0x00ac,0x00ad,0x00ae,0x00af,
+ 0x00b0,0x00b1,0x00b2,0x00b3,0x00b4,0x00b5,0x00b6,0x00b7,
+ 0x00b8,0x00b9,0x00f7,0x00bb,0x00bc,0x00bd,0x00be,0x00bf,
+ 0x05b0,0x05b1,0x05b2,0x05b3,0x05b4,0x05b5,0x05b6,0x05b7,
+ 0x05b8,0x05b9,UBOGON,0x05bb,0x05bc,0x05bd,0x05be,0x05bf,
+ 0x05c0,0x05c1,0x05c2,0x05c3,0x05f0,0x05f1,0x05f2,0x05f3,
+ 0x05f4,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,UBOGON,
+ 0x05d0,0x05d1,0x05d2,0x05d3,0x05d4,0x05d5,0x05d6,0x05d7,
+ 0x05d8,0x05d9,0x05da,0x05db,0x05dc,0x05dd,0x05de,0x05df,
+ 0x05e0,0x05e1,0x05e2,0x05e3,0x05e4,0x05e5,0x05e6,0x05e7,
+ 0x05e8,0x05e9,0x05ea,UBOGON,UBOGON,0x200e,0x200f,UBOGON
+};
+
+ /* Windows Arabic */
+static const unsigned short windows_1256tab[128] = {
+ 0x20ac,0x067e,0x201a,0x0192,0x201e,0x2026,0x2020,0x2021,
+ 0x02c6,0x2030,0x0679,0x2039,0x0152,0x0686,0x0698,0x0688,
+ 0x06af,0x2018,0x2019,0x201c,0x201d,0x2022,0x2013,0x2014,
+ 0x06a9,0x2122,0x0691,0x203a,0x0153,0x200c,0x200d,0x06ba,
+ 0x00a0,0x060c,0x00a2,0x00a3,0x00a4,0x00a5,0x00a6,0x00a7,
+ 0x00a8,0x00a9,0x06be,0x00ab,0x00ac,0x00ad,0x00ae,0x00af,
+ 0x00b0,0x00b1,0x00b2,0x00b3,0x00b4,0x00b5,0x00b6,0x00b7,
+ 0x00b8,0x00b9,0x061b,0x00bb,0x00bc,0x00bd,0x00be,0x061f,
+ 0x06c1,0x0621,0x0622,0x0623,0x0624,0x0625,0x0626,0x0627,
+ 0x0628,0x0629,0x062a,0x062b,0x062c,0x062d,0x062e,0x062f,
+ 0x0630,0x0631,0x0632,0x0633,0x0634,0x0635,0x0636,0x00d7,
+ 0x0637,0x0638,0x0639,0x063a,0x0640,0x0641,0x0642,0x0643,
+ 0x00e0,0x0644,0x00e2,0x0645,0x0646,0x0647,0x0648,0x00e7,
+ 0x00e8,0x00e9,0x00ea,0x00eb,0x0649,0x064a,0x00ee,0x00ef,
+ 0x064b,0x064c,0x064d,0x064e,0x00f4,0x064f,0x0650,0x00f7,
+ 0x0651,0x00f9,0x0652,0x00fb,0x00fc,0x200e,0x200f,0x06d2
+};
+
+ /* Windows Baltic */
+static const unsigned short windows_1257tab[128] = {
+ 0x20ac,UBOGON,0x201a,UBOGON,0x201e,0x2026,0x2020,0x2021,
+ UBOGON,0x2030,UBOGON,0x2039,UBOGON,0x00a8,0x02c7,0x00b8,
+ UBOGON,0x2018,0x2019,0x201c,0x201d,0x2022,0x2013,0x2014,
+ UBOGON,0x2122,UBOGON,0x203a,UBOGON,0x00af,0x02db,UBOGON,
+ 0x00a0,UBOGON,0x00a2,0x00a3,0x00a4,UBOGON,0x00a6,0x00a7,
+ 0x00d8,0x00a9,0x0156,0x00ab,0x00ac,0x00ad,0x00ae,0x00c6,
+ 0x00b0,0x00b1,0x00b2,0x00b3,0x00b4,0x00b5,0x00b6,0x00b7,
+ 0x00f8,0x00b9,0x0157,0x00bb,0x00bc,0x00bd,0x00be,0x00e6,
+ 0x0104,0x012e,0x0100,0x0106,0x00c4,0x00c5,0x0118,0x0112,
+ 0x010c,0x00c9,0x0179,0x0116,0x0122,0x0136,0x012a,0x013b,
+ 0x0160,0x0143,0x0145,0x00d3,0x014c,0x00d5,0x00d6,0x00d7,
+ 0x0172,0x0141,0x015a,0x016a,0x00dc,0x017b,0x017d,0x00df,
+ 0x0105,0x012f,0x0101,0x0107,0x00e4,0x00e5,0x0119,0x0113,
+ 0x010d,0x00e9,0x017a,0x0117,0x0123,0x0137,0x012b,0x013c,
+ 0x0161,0x0144,0x0146,0x00f3,0x014d,0x00f5,0x00f6,0x00f7,
+ 0x0173,0x0142,0x015b,0x016b,0x00fc,0x017c,0x017e,0x02d9
+};
+
+ /* Windows Vietnamese */
+static const unsigned short windows_1258tab[128] = {
+ 0x20ac,UBOGON,0x201a,0x0192,0x201e,0x2026,0x2020,0x2021,
+ 0x02c6,0x2030,UBOGON,0x2039,0x0152,UBOGON,UBOGON,UBOGON,
+ UBOGON,0x2018,0x2019,0x201c,0x201d,0x2022,0x2013,0x2014,
+ 0x02dc,0x2122,UBOGON,0x203a,0x0153,UBOGON,UBOGON,0x0178,
+ 0x00a0,0x00a1,0x00a2,0x00a3,0x00a4,0x00a5,0x00a6,0x00a7,
+ 0x00a8,0x00a9,0x00aa,0x00ab,0x00ac,0x00ad,0x00ae,0x00af,
+ 0x00b0,0x00b1,0x00b2,0x00b3,0x00b4,0x00b5,0x00b6,0x00b7,
+ 0x00b8,0x00b9,0x00ba,0x00bb,0x00bc,0x00bd,0x00be,0x00bf,
+ 0x00c0,0x00c1,0x00c2,0x0102,0x00c4,0x00c5,0x00c6,0x00c7,
+ 0x00c8,0x00c9,0x00ca,0x00cb,0x0300,0x00cd,0x00ce,0x00cf,
+ 0x0110,0x00d1,0x0309,0x00d3,0x00d4,0x01a0,0x00d6,0x00d7,
+ 0x00d8,0x00d9,0x00da,0x00db,0x00dc,0x01af,0x0303,0x00df,
+ 0x00e0,0x00e1,0x00e2,0x0103,0x00e4,0x00e5,0x00e6,0x00e7,
+ 0x00e8,0x00e9,0x00ea,0x00eb,0x0301,0x00ed,0x00ee,0x00ef,
+ 0x0111,0x00f1,0x0323,0x00f3,0x00f4,0x01a1,0x00f6,0x00f7,
+ 0x00f8,0x00f9,0x00fa,0x00fb,0x00fc,0x01b0,0x20ab,0x00ff
+};
diff --git a/imap/src/dmail/Makefile b/imap/src/dmail/Makefile
new file mode 100644
index 00000000..4aef24cf
--- /dev/null
+++ b/imap/src/dmail/Makefile
@@ -0,0 +1,53 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: dmail Makefile
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 5 April 1993
+# Last Edited: 10 September 2007
+
+
+C = ../c-client
+CCLIENTLIB = $C/c-client.a
+SHELL = /bin/sh
+
+# Get local definitions from c-client directory
+
+CC = `cat $C/CCTYPE`
+CFLAGS = -I$C `cat $C/CFLAGS`
+LDFLAGS = $(CCLIENTLIB) `cat $C/LDFLAGS`
+
+dmail: $(CCLIENTLIB) dmail.o dquota.o
+ $(CC) $(CFLAGS) -o dmail dmail.o dquota.o $(LDFLAGS)
+
+dmail.o: $C/mail.h $C/misc.h $C/osdep.h dquota.h
+
+dquota.o: dquota.h
+
+$(CCLIENTLIB):
+ cd $C;make
+
+clean:
+ rm -f *.o dmail
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo 'not war?'
diff --git a/imap/src/dmail/dmail.1 b/imap/src/dmail/dmail.1
new file mode 100644
index 00000000..92cd894f
--- /dev/null
+++ b/imap/src/dmail/dmail.1
@@ -0,0 +1,121 @@
+.ig
+ * ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+..
+.TH DMAIL 1 "June 18, 2007"
+.SH NAME
+dmail \- procmail Mail Delivery Module
+.nh
+.SH SYNOPSIS
+.B dmail
+.I [\-D] [\-f from_name] [-s] [-k keyword_list] [user][+folder]
+.SH DESCRIPTION
+.I dmail
+delivers mail to a user's INBOX or a designated folder.
+.I dmail
+may be configured as a drop-in replacement for
+.IR binmail (1),
+.IR mail.local (1)
+for use with a mail delivery filter such as
+.IR procmail (1) .
+.PP
+Because of security considerations (see below)
+.I dmail
+is not intended to be used for direct delivery by the mailer daemon;
+.IR tmail (1)
+is the preferred tool for this purpose. If
+.I dmail
+is used for mailer daemon delivery, the mailer daemon must invoke
+.I dmail
+with the
+.I dmail
+process' user id set to the recipient's user id.
+.PP
+When
+.I dmail
+exits, it returns exit status values to enable
+.IR procmail (1)
+to determine whether a message was delivered successfully or had a
+temporary (requeue for later delivery) or permanent (return to sender)
+failure.
+.PP
+If the
+.I user
+name is present, it must be the same as the logged-in user name.
+.PP
+If the
+.I +folder
+extension is included in the user argument (or appears by itself if there
+is no user argument),
+.I dmail
+will attempt to deliver to the designated folder. If the folder does not
+exist or the extension is not included, the message is delivered to the
+user's INBOX.
+If delivery is to INBOX and no INBOX currently exists,
+.I dmail
+will create a new INBOX.
+.I dmail
+recognizes the format of an existing INBOX or folder, and appends the new
+message in that format.
+.PP
+The \fB-D\fR flag specifies debugging; this enables additional message
+telemetry.
+.PP
+The \fB-f\fR or \fB-r\fR flag is used to specify a Return-Path. The header
+.br
+ Return-Path: <\fIfrom_name\fR>
+.br
+is prepended to the message before delivery.
+.PP
+The
+.B -s
+flag specifies that the message will be flagged as being "seen".
+.PP
+The \fB-k\fR flag is used to specify delivery keywords, which are set on
+the message at delivery time if and
+.B only
+if the keywords are already defined in the mailbox. Multiple keywords can be
+specified by using a quoted string, e.g.,
+.br
+ dmail -k "$Junk Discard" +junkbox
+.br
+.SH RESTRICTIONS
+Absolute pathnames and
+.I ~user
+specifications are not permitted in
+.I +folder
+extensions.
+.SH SECURITY CONSIDERATIONS
+Unlike
+.I tmail
+you can use
+.I dmail
+to deliver to IMAP4 namespace names via
+.I +folder
+extensions. This means that it is possible to deliver to
+.IR mh (1)
+format mailboxes.
+.PP
+However, this can also include such namespaces as #shared, #public,
+and #ftp. In most cases, it is undesirable to allow anybody sending
+mail to the user to deliver to these namespaces. Consequently, there
+needs to be a rule in place in the configuration of either
+.IR sendmail (8)
+or
+.IR procmail (1)
+to prevent such abuse.
+.SH AUTHOR
+Mark Crispin, MRC@CAC.Washington.EDU
+.SH "SEE ALSO"
+binmail(1)
+.br
+procmail(1)
diff --git a/imap/src/dmail/dmail.c b/imap/src/dmail/dmail.c
new file mode 100644
index 00000000..f78b9576
--- /dev/null
+++ b/imap/src/dmail/dmail.c
@@ -0,0 +1,661 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Procmail-Callable Mail Delivery Module
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 5 April 1993
+ * Last Edited: 30 October 2008
+ */
+
+#include <stdio.h>
+#include <pwd.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <sysexits.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include "c-client.h"
+#include "dquota.h"
+
+
+/* Globals */
+
+char *version = "18"; /* dmail edit version */
+int debug = NIL; /* debugging (don't fork) */
+int flagseen = NIL; /* flag message as seen */
+int trycreate = NIL; /* flag saying gotta create before appending */
+int critical = NIL; /* flag saying in critical code */
+char *sender = NIL; /* message origin */
+char *keywords = NIL; /* keyword list */
+long precedence = 0; /* delivery precedence - used by quota hook */
+
+
+/* Function prototypes */
+
+void file_string_init (STRING *s,void *data,unsigned long size);
+char file_string_next (STRING *s);
+void file_string_setpos (STRING *s,unsigned long i);
+int main (int argc,char *argv[]);
+int deliver (FILE *f,unsigned long msglen,char *user);
+long ibxpath (MAILSTREAM *ds,char **mailbox,char *path);
+int deliver_safely (MAILSTREAM *prt,STRING *st,char *mailbox,char *path,
+ char *tmp);
+int delivery_unsafe (char *path,struct stat *sbuf,char *tmp);
+int fail (char *string,int code);
+
+
+/* File string driver for file stringstructs */
+
+STRINGDRIVER file_string = {
+ file_string_init, /* initialize string structure */
+ file_string_next, /* get next byte in string structure */
+ file_string_setpos /* set position in string structure */
+};
+
+
+/* Cache buffer for file stringstructs */
+
+#define CHUNKLEN 16384
+char chunk[CHUNKLEN];
+
+/* Initialize file string structure for file stringstruct
+ * Accepts: string structure
+ * pointer to string
+ * size of string
+ */
+
+void file_string_init (STRING *s,void *data,unsigned long size)
+{
+ s->data = data; /* note fd */
+ s->size = size; /* note size */
+ s->chunk = chunk;
+ s->chunksize = (unsigned long) CHUNKLEN;
+ SETPOS (s,0); /* set initial position */
+}
+
+
+/* Get next character from file stringstruct
+ * Accepts: string structure
+ * Returns: character, string structure chunk refreshed
+ */
+
+char file_string_next (STRING *s)
+{
+ char c = *s->curpos++; /* get next byte */
+ SETPOS (s,GETPOS (s)); /* move to next chunk */
+ return c; /* return the byte */
+}
+
+
+/* Set string pointer position for file stringstruct
+ * Accepts: string structure
+ * new position
+ */
+
+void file_string_setpos (STRING *s,unsigned long i)
+{
+ if (i > s->size) i = s->size; /* don't permit setting beyond EOF */
+ s->offset = i; /* set new offset */
+ s->curpos = s->chunk; /* reset position */
+ /* set size of data */
+ if (s->cursize = min (s->chunksize,SIZE (s))) {
+ /* move to that position in the file */
+ fseek ((FILE *) s->data,s->offset,SEEK_SET);
+ fread (s->curpos,sizeof (char),(unsigned int) s->cursize,(FILE *) s->data);
+ }
+}
+
+/* Main program */
+
+int main (int argc,char *argv[])
+{
+ FILE *f = NIL;
+ int c,ret = 0;
+ unsigned long msglen;
+ char *s,tmp[MAILTMPLEN];
+ uid_t ruid = getuid ();
+ struct passwd *pwd = ruid ? getpwnam ("daemon") : NIL;
+ openlog ("dmail",LOG_PID,LOG_MAIL);
+ /* must not be root or daemon! */
+ if (!ruid || (pwd && (pwd->pw_uid == ruid)))
+ _exit (fail ("dmail may not be invoked by root or daemon",EX_USAGE));
+#include "linkage.c"
+ /* process all flags */
+ for (--argc; argc && (*(s = *++argv)) == '-'; argc--) switch (s[1]) {
+ case 'D': /* debug */
+ debug = T; /* extra debugging */
+ break;
+ case 's': /* deliver as seen */
+ flagseen = T;
+ break;
+ case 'f':
+ case 'r': /* flag giving return path */
+ if (sender) _exit (fail ("duplicate -r",EX_USAGE));
+ if (argc--) sender = cpystr (*++argv);
+ else _exit (fail ("missing argument to -r",EX_USAGE));
+ break;
+ case 'k':
+ if (keywords) _exit (fail ("duplicate -k",EX_USAGE));
+ if (argc--) keywords = cpystr (*++argv);
+ else _exit (fail ("missing argument to -k",EX_USAGE));
+ break;
+ case 'p':
+ if (s[2] && ((s[2] == '-') || isdigit (s[2]))) precedence = atol (s + 2);
+ else if (argc-- && ((*(s = *++argv) == '-') || isdigit (*s)))
+ precedence = atol (s);
+ else _exit (fail ("missing argument to -p",EX_USAGE));
+ break;
+ default: /* anything else */
+ _exit (fail ("unknown switch",EX_USAGE));
+ }
+
+ if (argc > 1) _exit (fail ("too many recipients",EX_USAGE));
+ else if (!(f = tmpfile ())) _exit(fail ("can't make temp file",EX_TEMPFAIL));
+ /* build delivery headers */
+ if (sender) fprintf (f,"Return-Path: <%s>\015\012",sender);
+ /* start Received line: */
+ fprintf (f,"Received: via dmail-%s.%s for %s; ",CCLIENTVERSION,version,
+ (argc == 1) ? *argv : myusername ());
+ rfc822_date (tmp);
+ fputs (tmp,f);
+ fputs ("\015\012",f);
+ /* copy text from standard input */
+ if (!fgets (tmp,MAILTMPLEN-1,stdin) || !(s = strchr (tmp,'\n')) ||
+ (s == tmp) || s[1]) _exit (fail ("bad first message line",EX_USAGE));
+ else if (s[-1] == '\015') { /* nuke leading "From " line */
+ if ((tmp[0] != 'F') || (tmp[1] != 'r') || (tmp[2] != 'o') ||
+ (tmp[3] != 'm') || (tmp[4] != ' ')) fputs (tmp,f);
+ while ((c = getchar ()) != EOF) putc (c,f);
+ }
+ else {
+ if ((tmp[0] != 'F') || (tmp[1] != 'r') || (tmp[2] != 'o') ||
+ (tmp[3] != 'm') || (tmp[4] != ' ')) {
+ *s++ = '\015'; /* overwrite NL with CRLF */
+ *s++ = '\012';
+ *s = '\0'; /* tie off string */
+ fputs (tmp,f); /* write line */
+ }
+ }
+ /* copy text from standard input */
+ while ((c = getchar ()) != EOF) {
+ /* add CR if needed */
+ if (c == '\012') putc ('\015',f);
+ putc (c,f);
+ }
+ msglen = ftell (f); /* size of message */
+ fflush (f); /* make sure all changes written out */
+ if (ferror (f)) ret = fail ("error writing temp file",EX_TEMPFAIL);
+ else if (!msglen) ret = fail ("empty message",EX_TEMPFAIL);
+ /* single delivery */
+ else ret = deliver (f,msglen,argc ? *argv : myusername ());
+ fclose (f); /* all done with temporary file */
+ _exit (ret); /* normal exit */
+ return 0; /* stupid gcc */
+}
+
+/* Deliver message to recipient list
+ * Accepts: file description of message temporary file
+ * size of message temporary file in bytes
+ * recipient name
+ * Returns: NIL if success, else error code
+ */
+
+int deliver (FILE *f,unsigned long msglen,char *user)
+{
+ MAILSTREAM *ds = NIL;
+ char *s,*mailbox,tmp[MAILTMPLEN],path[MAILTMPLEN];
+ STRING st;
+ struct stat sbuf;
+ /* have a mailbox specifier? */
+ if (mailbox = strchr (user,'+')) {
+ *mailbox++ = '\0'; /* yes, tie off user name */
+ if (!*mailbox || !compare_cstring ((unsigned char *) mailbox,"INBOX"))
+ mailbox = NIL; /* user+ and user+INBOX same as user */
+ }
+ if (!*user) user = myusername ();
+ else if (strcmp (user,myusername ()))
+ return fail ("can't deliver to other user",EX_CANTCREAT);
+ sprintf (tmp,"delivering to %.80s+%.80s",user,mailbox ? mailbox : "INBOX");
+ mm_dlog (tmp);
+ /* prepare stringstruct */
+ INIT (&st,file_string,(void *) f,msglen);
+ if (mailbox) { /* non-INBOX name */
+ switch (mailbox[0]) { /* make sure a valid name */
+ default: /* other names, try to deliver if not INBOX */
+ if (!strstr (mailbox,"..") && !strstr (mailbox,"//") &&
+ !strstr (mailbox,"/~") && mailboxfile (path,mailbox) && path[0] &&
+ !deliver_safely (NIL,&st,mailbox,path,tmp)) return NIL;
+ case '%': case '*': /* wildcards not valid */
+ case '/': /* absolute path names not valid */
+ case '~': /* user names not valid */
+ sprintf (tmp,"invalid mailbox name %.80s+%.80s",user,mailbox);
+ mm_log (tmp,WARN);
+ break;
+ }
+ mm_dlog ("retrying delivery to INBOX");
+ SETPOS (&st,0); /* rewind stringstruct just in case */
+ }
+
+ /* no -I, resolve "INBOX" into path */
+ if (mailboxfile (path,mailbox = "INBOX") && !path[0]) {
+ /* clear box, get generic INBOX prototype */
+ if (!(ds = mail_open (NIL,"INBOX",OP_PROTOTYPE)))
+ fatal ("no INBOX prototype");
+ /* standard system driver? */
+ if (!strcmp (ds->dtb->name,"unix") || !strcmp (ds->dtb->name,"mmdf")) {
+ strcpy (path,sysinbox ());/* use system INBOX */
+ if (!lstat (path,&sbuf)) /* deliver to existing system INBOX */
+ return deliver_safely (ds,&st,mailbox,path,tmp);
+ }
+ else { /* other driver, try ~/INBOX */
+ if ((mailboxfile (path,"&&&&&") == path) &&
+ (s = strstr (path,"&&&&&")) && strcpy (s,"INBOX") &&
+ !lstat (path,&sbuf)){ /* deliver to existing ~/INBOX */
+ sprintf (tmp,"#driver.%s/INBOX",ds->dtb->name);
+ return deliver_safely (ds,&st,cpystr (tmp),path,tmp);
+ }
+ }
+ /* not dummy, deliver to driver imputed path */
+ if (strcmp (ds->dtb->name,"dummy"))
+ return (ibxpath (ds,&mailbox,path) && !lstat (path,&sbuf)) ?
+ deliver_safely (ds,&st,mailbox,path,tmp) :
+ fail ("unable to resolve INBOX path",EX_CANTCREAT);
+ /* dummy, empty imputed append path exist? */
+ if (ibxpath (ds = default_proto (T),&mailbox,path) &&
+ !lstat (path,&sbuf) && !sbuf.st_size)
+ return deliver_safely (ds,&st,mailbox,path,tmp);
+ /* impute path that we will create */
+ if (!ibxpath (ds = default_proto (NIL),&mailbox,path))
+ return fail ("unable to resolve INBOX",EX_CANTCREAT);
+ }
+ /* black box, must create, get create proto */
+ else if (lstat (path,&sbuf)) ds = default_proto (NIL);
+ else { /* black box, existing file */
+ /* empty file, get append prototype */
+ if (!sbuf.st_size) ds = default_proto (T);
+ /* non-empty, get prototype from its data */
+ else if (!(ds = mail_open (NIL,"INBOX",OP_PROTOTYPE)))
+ fatal ("no INBOX prototype");
+ /* error if unknown format */
+ if (!strcmp (ds->dtb->name,"phile"))
+ return fail ("unknown format INBOX",EX_UNAVAILABLE);
+ /* otherwise can deliver to it */
+ return deliver_safely (ds,&st,mailbox,path,tmp);
+ }
+ sprintf (tmp,"attempting to create mailbox %.80s path %.80s",mailbox,path);
+ mm_dlog (tmp);
+ /* supplicate to the Evil One */
+ if (!path_create (ds,path)) return fail ("can't create INBOX",EX_CANTCREAT);
+ sprintf (tmp,"created %.80s",path);
+ mm_dlog (tmp);
+ /* deliver the message */
+ return deliver_safely (ds,&st,mailbox,path,tmp);
+}
+
+/* Resolve INBOX from driver prototype into mailbox name and filesystem path
+ * Accepts: driver prototype
+ * pointer to mailbox name string pointer
+ * buffer to return mailbox path
+ * Returns: T if success, NIL if error
+ */
+
+long ibxpath (MAILSTREAM *ds,char **mailbox,char *path)
+{
+ char *s,tmp[MAILTMPLEN];
+ long ret = T;
+ if (!ds) return NIL;
+ else if (!strcmp (ds->dtb->name,"unix") || !strcmp (ds->dtb->name,"mmdf"))
+ strcpy (path,sysinbox ()); /* use system INBOX for unix and MMDF */
+ else if (!strcmp (ds->dtb->name,"tenex"))
+ ret = (mailboxfile (path,"mail.txt") == path) ? T : NIL;
+ else if (!strcmp (ds->dtb->name,"mtx"))
+ ret = (mailboxfile (path,"INBOX.MTX") == path) ? T : NIL;
+ else if (!strcmp (ds->dtb->name,"mbox"))
+ ret = (mailboxfile (path,"mbox") == path) ? T : NIL;
+ /* better not be a namespace driver */
+ else if (ds->dtb->flags & DR_NAMESPACE) return NIL;
+ /* INBOX in home directory */
+ else ret = ((mailboxfile (path,"&&&&&") == path) &&
+ (s = strstr (path,"&&&&&")) && strcpy (s,"INBOX")) ? T : NIL;
+ if (ret) { /* don't bother if lossage */
+ sprintf (tmp,"#driver.%s/INBOX",ds->dtb->name);
+ *mailbox = cpystr (tmp); /* name of INBOX in this namespace */
+ }
+ return ret;
+}
+
+/* Deliver safely
+ * Accepts: prototype stream to force mailbox format
+ * stringstruct of message temporary file or NIL for check only
+ * mailbox name
+ * filesystem path name
+ * scratch buffer for messages
+ * Returns: NIL if success, else error code
+ */
+
+int deliver_safely (MAILSTREAM *prt,STRING *st,char *mailbox,char *path,
+ char *tmp)
+{
+ struct stat sbuf;
+ char *flags = NIL;
+ int i = delivery_unsafe (path,&sbuf,tmp);
+ if (i) return i; /* give up now if delivery unsafe */
+ /* directory, not file */
+ if ((sbuf.st_mode & S_IFMT) == S_IFDIR) {
+ if (sbuf.st_mode & 0001) { /* listable directories may be worrisome */
+ sprintf (tmp,"WARNING: directory %.80s is listable",path);
+ mm_log (tmp,WARN);
+ }
+ }
+ else { /* file, not directory */
+ if (sbuf.st_nlink != 1) { /* multiple links may be worrisome */
+ sprintf (tmp,"WARNING: multiple links to file %.80s",path);
+ mm_log (tmp,WARN);
+ }
+ if (sbuf.st_mode & 0111) { /* executable files may be worrisome */
+ sprintf (tmp,"WARNING: file %.80s is executable",path);
+ mm_log (tmp,WARN);
+ }
+ }
+ if (sbuf.st_mode & 0002) { /* public-write files may be worrisome */
+ sprintf (tmp,"WARNING: file %.80s is publicly-writable",path);
+ mm_log (tmp,WARN);
+ }
+ if (sbuf.st_mode & 0004) { /* public-write files may be worrisome */
+ sprintf (tmp,"WARNING: file %.80s is publicly-readable",path);
+ mm_log (tmp,WARN);
+ }
+ /* check site-written quota procedure */
+ if (!dmail_quota (st,path,tmp,sender,precedence))
+ return fail (tmp,EX_CANTCREAT);
+ /* so far, so good */
+ sprintf (tmp,"%s appending to %.80s (%s %.80s)",
+ prt ? prt->dtb->name : "default",mailbox,
+ ((sbuf.st_mode & S_IFMT) == S_IFDIR) ? "directory" : "file",path);
+ mm_dlog (tmp);
+ if (keywords) { /* any keywords requested? */
+ if (flagseen) sprintf (flags = tmp,"\\Seen %.1000s",keywords);
+ else flags = keywords;
+ }
+ else if (flagseen) flags = "\\Seen";
+ /* do the append now! */
+ if (!mail_append_full (prt,mailbox,flags,NIL,st)) {
+ sprintf (tmp,"message delivery failed to %.80s",path);
+ return fail (tmp,EX_CANTCREAT);
+ }
+ /* note success */
+ sprintf (tmp,"delivered to %.80s",path);
+ mm_log (tmp,NIL);
+ /* make sure nothing evil this way comes */
+ return delivery_unsafe (path,&sbuf,tmp);
+}
+
+/* Verify that delivery is safe
+ * Accepts: path name
+ * stat buffer
+ * scratch buffer for messages
+ * Returns: NIL if delivery is safe, error code if unsafe
+ */
+
+int delivery_unsafe (char *path,struct stat *sbuf,char *tmp)
+{
+ u_short type;
+ sprintf (tmp,"Verifying safe delivery to %.80s",path);
+ mm_dlog (tmp);
+ /* prepare message just in case */
+ sprintf (tmp,"delivery to %.80s unsafe: ",path);
+ /* unsafe if can't get its status */
+ if (lstat (path,sbuf)) strcat (tmp,strerror (errno));
+ /* check file type */
+ else switch (sbuf->st_mode & S_IFMT) {
+ case S_IFDIR: /* directory is always OK */
+ return NIL;
+ case S_IFREG: /* file is unsafe if setuid */
+ if (sbuf->st_mode & S_ISUID) strcat (tmp,"setuid file");
+ /* or setgid */
+ else if (sbuf->st_mode & S_ISGID) strcat (tmp,"setgid file");
+ else return NIL; /* otherwise safe */
+ break;
+ case S_IFCHR: strcat (tmp,"character special"); break;
+ case S_IFBLK: strcat (tmp,"block special"); break;
+ case S_IFLNK: strcat (tmp,"symbolic link"); break;
+ case S_IFSOCK: strcat (tmp,"socket"); break;
+ default:
+ sprintf (tmp + strlen (tmp),"file type %07o",(unsigned int) type);
+ }
+ return fail (tmp,EX_CANTCREAT);
+}
+
+/* Report an error
+ * Accepts: string to output
+ */
+
+int fail (char *string,int code)
+{
+ mm_log (string,ERROR); /* pass up the string */
+ switch (code) {
+#if T
+ case EX_USAGE:
+ case EX_OSERR:
+ case EX_SOFTWARE:
+ case EX_NOUSER:
+ case EX_CANTCREAT:
+ code = EX_TEMPFAIL; /* coerce these to TEMPFAIL */
+ break;
+#endif
+ case -1: /* quota failure... */
+ code = EX_CANTCREAT; /* ...really returns this code */
+ break;
+ default:
+ break;
+ }
+ return code; /* error code to return */
+}
+
+/* Co-routines from MAIL library */
+
+
+/* Message matches a search
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_searched (MAILSTREAM *stream,unsigned long msgno)
+{
+ fatal ("mm_searched() call");
+}
+
+
+/* Message exists (i.e. there are that many messages in the mailbox)
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_exists (MAILSTREAM *stream,unsigned long number)
+{
+ fatal ("mm_exists() call");
+}
+
+
+/* Message expunged
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_expunged (MAILSTREAM *stream,unsigned long number)
+{
+ fatal ("mm_expunged() call");
+}
+
+
+/* Message flags update seen
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_flags (MAILSTREAM *stream,unsigned long number)
+{
+}
+
+/* Mailbox found
+ * Accepts: MAIL stream
+ * delimiter
+ * mailbox name
+ * mailbox attributes
+ */
+
+void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes)
+{
+ fatal ("mm_list() call");
+}
+
+
+/* Subscribed mailbox found
+ * Accepts: MAIL stream
+ * delimiter
+ * mailbox name
+ * mailbox attributes
+ */
+
+void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes)
+{
+ fatal ("mm_lsub() call");
+}
+
+
+/* Mailbox status
+ * Accepts: MAIL stream
+ * mailbox name
+ * mailbox status
+ */
+
+void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
+{
+ fatal ("mm_status() call");
+}
+
+/* Notification event
+ * Accepts: MAIL stream
+ * string to log
+ * error flag
+ */
+
+void mm_notify (MAILSTREAM *stream,char *string,long errflg)
+{
+ char tmp[MAILTMPLEN];
+ tmp[11] = '\0'; /* see if TRYCREATE */
+ if (!strcmp (ucase (strncpy (tmp,string,11)),"[TRYCREATE]")) trycreate = T;
+ mm_log (string,errflg); /* just do mm_log action */
+}
+
+
+/* Log an event for the user to see
+ * Accepts: string to log
+ * error flag
+ */
+
+void mm_log (char *string,long errflg)
+{
+ if (trycreate)mm_dlog(string);/* debug logging only if trycreate in effect */
+ else { /* ordinary logging */
+ fprintf (stderr,"%s\n",string);
+ switch (errflg) {
+ case NIL: /* no error */
+ syslog (LOG_INFO,"%s",string);
+ break;
+ case PARSE: /* parsing problem */
+ case WARN: /* warning */
+ syslog (LOG_WARNING,"%s",string);
+ break;
+ case ERROR: /* error */
+ default:
+ syslog (LOG_ERR,"%s",string);
+ break;
+ }
+ }
+}
+
+
+/* Log an event to debugging telemetry
+ * Accepts: string to log
+ */
+
+void mm_dlog (char *string)
+{
+ if (debug) fprintf (stderr,"%s\n",string);
+ syslog (LOG_DEBUG,"%s",string);
+}
+
+/* Get user name and password for this host
+ * Accepts: parse of network mailbox name
+ * where to return user name
+ * where to return password
+ * trial count
+ */
+
+void mm_login (NETMBX *mb,char *username,char *password,long trial)
+{
+ fatal ("mm_login() call");
+}
+
+
+/* About to enter critical code
+ * Accepts: stream
+ */
+
+void mm_critical (MAILSTREAM *stream)
+{
+ critical = T; /* note in critical code */
+}
+
+
+/* About to exit critical code
+ * Accepts: stream
+ */
+
+void mm_nocritical (MAILSTREAM *stream)
+{
+ critical = NIL; /* note not in critical code */
+}
+
+
+/* Disk error found
+ * Accepts: stream
+ * system error code
+ * flag indicating that mailbox may be clobbered
+ * Returns: T if user wants to abort
+ */
+
+long mm_diskerror (MAILSTREAM *stream,long errcode,long serious)
+{
+ return T;
+}
+
+
+/* Log a fatal error event
+ * Accepts: string to log
+ */
+
+void mm_fatal (char *string)
+{
+ printf ("?%s\n",string); /* shouldn't happen normally */
+}
diff --git a/imap/src/dmail/dquota.c b/imap/src/dmail/dquota.c
new file mode 100644
index 00000000..38267075
--- /dev/null
+++ b/imap/src/dmail/dquota.c
@@ -0,0 +1,44 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Procmail-Callable Mail Delivery Module Quota Hook
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 September 2007
+ * Last Edited: 10 September 2007
+ */
+
+#include "c-client.h"
+
+/* Site-written routine to validate delivery per quota and policy
+ * Accepts: stringstruct of message temporary file
+ * filesystem path
+ * return path
+ * buffer to write error message
+ * precedence setting
+ * Returns: T if can deliver, or NIL if quota issue and must bounce
+ */
+
+long dmail_quota (STRING *msg,char *path,char *tmp,char *sender,
+ long precedence)
+{
+ return LONGT; /* dummy success return */
+}
diff --git a/imap/src/dmail/dquota.h b/imap/src/dmail/dquota.h
new file mode 100644
index 00000000..44280df3
--- /dev/null
+++ b/imap/src/dmail/dquota.h
@@ -0,0 +1,32 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Procmail-Callable Mail Delivery Module Quota Hook
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 September 2007
+ * Last Edited: 10 September 2007
+ */
+
+/* Function prototypes */
+
+long dmail_quota (STRING *msg,char *path,char *tmp,char *sender,
+ long precedence);
diff --git a/imap/src/imapd/Makefile b/imap/src/imapd/Makefile
new file mode 100644
index 00000000..c3078f42
--- /dev/null
+++ b/imap/src/imapd/Makefile
@@ -0,0 +1,68 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: IMAPD Makefile
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 5 November 1990
+# Last Edited: 30 August 2006
+
+
+ALERT=/etc/imapd.alert
+USERALERT=.imapalert
+SHUTDOWN=/etc/nologin
+ANO=/etc/anonymous.newsgroups
+NNTP=/etc/imapd.nntp
+SHELL= /bin/sh
+
+
+# Un-comment this to get somewhat better interoperability with Netscape. It
+# causes the "Manage Mail" menu item to open the given URL, e.g. to point to
+# an alternative IMAP client (e.g. Pine) or perhaps to a homebrew mail
+# account management page.
+#NSBD= -DNETSCAPE_BRAIN_DAMAGE=\"http://www.washington.edu/pine\"
+
+
+# Get local definitions from c-client directory
+
+C = ../c-client
+CCLIENTLIB = $C/c-client.a
+CC = `cat $C/CCTYPE`
+CFLAGS = -I$C `cat $C/CFLAGS` $(NSBD) $(ENBD) -DANOFILE=\"$(ANO)\" \
+ -DALERTFILE=\"$(ALERT)\" -DNNTPFILE=\"$(NNTP)\" \
+ -DUSERALERTFILE=\"$(USERALERT)\" -DSHUTDOWNFILE=\"$(SHUTDOWN)\"
+LDFLAGS = $(CCLIENTLIB) `cat $C/LDFLAGS`
+
+all: imapd
+
+imapd: $(CCLIENTLIB) imapd.o
+ $(CC) $(CFLAGS) -o imapd imapd.o $(LDFLAGS)
+
+imapd.o: $C/mail.h $C/misc.h $C/osdep.h
+
+$(CCLIENTLIB):
+ cd $C;make
+
+clean:
+ rm -f *.o imapd || true
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo 'not war?'
diff --git a/imap/src/imapd/imapd.8 b/imap/src/imapd/imapd.8
new file mode 100644
index 00000000..2d2f8c8e
--- /dev/null
+++ b/imap/src/imapd/imapd.8
@@ -0,0 +1,48 @@
+.ig
+ * ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+..
+.TH IMAPD 8 "August 30, 2006"
+.UC 5
+.SH NAME
+IMAPd \- Internet Message Access Protocol server
+.SH SYNOPSIS
+.B /usr/etc/imapd
+.SH DESCRIPTION
+.I imapd
+is a server which supports the
+.B IMAP4rev1
+remote mail access protocol as documented in RFC-3501.
+.I imapd
+is invoked by the internet server (see
+.IR inetd (8)),
+normally for requests to connect to the
+.B IMAP
+port as indicated by the
+.I /etc/services
+file (see
+.IR services (5)).
+Normally, this is port 143 for plaintext IMAP and 993 for SSL IMAP.
+.PP
+This daemons contains CRAM-MD5 support. See the md5.txt documentation
+file for additional information.
+.PP
+.I imapd
+can also be accessed via
+.IR rsh (1)
+by many Unix-based clients. To do this, the
+.I imapd
+binary must have a link to
+.I /etc/rimapd
+since this is where this software expects it to be located.
+.SH "SEE ALSO"
+rsh(1) ipopd(8)
diff --git a/imap/src/imapd/imapd.c b/imap/src/imapd/imapd.c
new file mode 100644
index 00000000..d3d15660
--- /dev/null
+++ b/imap/src/imapd/imapd.c
@@ -0,0 +1,4608 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: IMAP4rev1 server
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@Washington.EDU
+ *
+ * Date: 5 November 1990
+ * Last Edited: 3 March 2008
+ */
+
+/* Parameter files */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <signal.h>
+#include <setjmp.h>
+#include <time.h>
+#include "c-client.h"
+#include "newsrc.h"
+#include <sys/stat.h>
+
+
+#define CRLF PSOUT ("\015\012") /* primary output terpri */
+
+
+/* Timeouts and timers */
+
+#define MINUTES *60
+
+#define LOGINTIMEOUT 3 MINUTES /* not logged in autologout timer */
+#define TIMEOUT 30 MINUTES /* RFC 3501 minimum autologout timer */
+#define INPUTTIMEOUT 5 MINUTES /* timer for additional command input */
+#define ALERTTIMER 1 MINUTES /* alert check timer */
+#define SHUTDOWNTIMER 1 MINUTES /* shutdown dally timer */
+#define IDLETIMER 1 MINUTES /* IDLE command poll timer */
+#define CHECKTIMER 15 MINUTES /* IDLE command last checkpoint timer */
+
+
+#define LITSTKLEN 20 /* length of literal stack */
+#define MAXCLIENTLIT 10000 /* maximum non-APPEND client literal size
+ * must be smaller than 4294967295
+ */
+#define MAXAPPENDTXT 0x40000000 /* maximum APPEND literal size
+ * must be smaller than 4294967295
+ */
+#define CMDLEN 65536 /* size of command buffer */
+
+
+/* Server states */
+
+#define LOGIN 0
+#define SELECT 1
+#define OPEN 2
+#define LOGOUT 3
+
+/* Body text fetching */
+
+typedef struct text_args {
+ char *section; /* body section */
+ STRINGLIST *lines; /* header lines */
+ unsigned long first; /* first octet to fetch */
+ unsigned long last; /* number of octets to fetch */
+ long flags; /* fetch flags */
+ long binary; /* binary flags */
+} TEXTARGS;
+
+#define FTB_BINARY 0x1 /* fetch as binary */
+#define FTB_SIZE 0x2 /* fetch size only */
+
+
+/* Append data */
+
+typedef struct append_data {
+ unsigned char *arg; /* append argument pointer */
+ char *flags; /* message flags */
+ char *date; /* message date */
+ char *msg; /* message text */
+ STRING *message; /* message stringstruct */
+} APPENDDATA;
+
+
+/* Message pointer */
+
+typedef struct msg_data {
+ MAILSTREAM *stream; /* stream */
+ unsigned long msgno; /* message number */
+ char *flags; /* current flags */
+ char *date; /* current date */
+ STRING *message; /* stringstruct of message */
+} MSGDATA;
+
+/* Function prototypes */
+
+int main (int argc,char *argv[]);
+void ping_mailbox (unsigned long uid);
+time_t palert (char *file,time_t oldtime);
+void msg_string_init (STRING *s,void *data,unsigned long size);
+char msg_string_next (STRING *s);
+void msg_string_setpos (STRING *s,unsigned long i);
+void new_flags (MAILSTREAM *stream);
+void settimeout (unsigned int i);
+void clkint (void);
+void kodint (void);
+void hupint (void);
+void trmint (void);
+void staint (void);
+char *sout (char *s,char *t);
+char *nout (char *s,unsigned long n,unsigned long base);
+void slurp (char *s,int n,unsigned long timeout);
+void inliteral (char *s,unsigned long n);
+unsigned char *flush (void);
+void ioerror (FILE *f,char *reason);
+unsigned char *parse_astring (unsigned char **arg,unsigned long *i,
+ unsigned char *del);
+unsigned char *snarf (unsigned char **arg);
+unsigned char *snarf_base64 (unsigned char **arg);
+unsigned char *snarf_list (unsigned char **arg);
+STRINGLIST *parse_stringlist (unsigned char **s,int *list);
+unsigned long uidmax (MAILSTREAM *stream);
+long parse_criteria (SEARCHPGM *pgm,unsigned char **arg,unsigned long maxmsg,
+ unsigned long maxuid,unsigned long depth);
+long parse_criterion (SEARCHPGM *pgm,unsigned char **arg,unsigned long msgmsg,
+ unsigned long maxuid,unsigned long depth);
+long crit_date (unsigned short *date,unsigned char **arg);
+long crit_date_work (unsigned short *date,unsigned char **arg);
+long crit_set (SEARCHSET **set,unsigned char **arg,unsigned long maxima);
+long crit_number (unsigned long *number,unsigned char **arg);
+long crit_string (STRINGLIST **string,unsigned char **arg);
+
+void fetch (char *t,unsigned long uid);
+typedef void (*fetchfn_t) (unsigned long i,void *args);
+void fetch_work (char *t,unsigned long uid,fetchfn_t f[],void *fa[]);
+void fetch_bodystructure (unsigned long i,void *args);
+void fetch_body (unsigned long i,void *args);
+void fetch_body_part_mime (unsigned long i,void *args);
+void fetch_body_part_contents (unsigned long i,void *args);
+void fetch_body_part_binary (unsigned long i,void *args);
+void fetch_body_part_header (unsigned long i,void *args);
+void fetch_body_part_text (unsigned long i,void *args);
+void remember (unsigned long uid,char *id,SIZEDTEXT *st);
+void fetch_envelope (unsigned long i,void *args);
+void fetch_encoding (unsigned long i,void *args);
+void changed_flags (unsigned long i,int f);
+void fetch_flags (unsigned long i,void *args);
+void put_flag (int *c,char *s);
+void fetch_internaldate (unsigned long i,void *args);
+void fetch_uid (unsigned long i,void *args);
+void fetch_rfc822 (unsigned long i,void *args);
+void fetch_rfc822_header (unsigned long i,void *args);
+void fetch_rfc822_size (unsigned long i,void *args);
+void fetch_rfc822_text (unsigned long i,void *args);
+void penv (ENVELOPE *env);
+void pbodystructure (BODY *body);
+void pbody (BODY *body);
+void pparam (PARAMETER *param);
+void paddr (ADDRESS *a);
+void pset (SEARCHSET **set);
+void pnum (unsigned long i);
+void pstring (char *s);
+void pnstring (char *s);
+void pastring (char *s);
+void psizedquoted (SIZEDTEXT *s);
+void psizedliteral (SIZEDTEXT *s,STRING *st);
+void psizedstring (SIZEDTEXT *s,STRING *st);
+void psizedastring (SIZEDTEXT *s);
+void pastringlist (STRINGLIST *s);
+void pnstringorlist (STRINGLIST *s);
+void pbodypartstring (unsigned long msgno,char *id,SIZEDTEXT *st,STRING *bs,
+ TEXTARGS *ta);
+void ptext (SIZEDTEXT *s,STRING *st);
+void pthread (THREADNODE *thr);
+void pcapability (long flag);
+long nameok (char *ref,char *name);
+char *bboardname (char *cmd,char *name);
+long isnewsproxy (char *name);
+long newsproxypattern (char *ref,char *pat,char *pattern,long flag);
+char *imap_responder (void *challenge,unsigned long clen,unsigned long *rlen);
+long proxycopy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long proxy_append (MAILSTREAM *stream,void *data,char **flags,char **date,
+ STRING **message);
+long append_msg (MAILSTREAM *stream,void *data,char **flags,char **date,
+ STRING **message);
+void copyuid (MAILSTREAM *stream,char *mailbox,unsigned long uidvalidity,
+ SEARCHSET *sourceset,SEARCHSET *destset);
+void appenduid (char *mailbox,unsigned long uidvalidity,SEARCHSET *set);
+char *referral (MAILSTREAM *stream,char *url,long code);
+void mm_list_work (char *what,int delimiter,char *name,long attributes);
+char *lasterror (void);
+
+/* Global storage */
+
+char *version = "404"; /* edit number of this server */
+char *logout = "Logout"; /* syslogreason for logout */
+char *goodbye = NIL; /* bye reason */
+time_t alerttime = 0; /* time of last alert */
+time_t sysalerttime = 0; /* time of last system alert */
+time_t useralerttime = 0; /* time of last user alert */
+time_t lastcheck = 0; /* time of last checkpoint */
+time_t shutdowntime = 0; /* time of last shutdown */
+int state = LOGIN; /* server state */
+int cancelled = NIL; /* authenticate cancelled */
+int trycreate = 0; /* saw a trycreate */
+int finding = NIL; /* doing old FIND command */
+int anonymous = 0; /* non-zero if anonymous */
+int critical = NIL; /* non-zero if in critical code */
+int quell_events = NIL; /* non-zero if in FETCH response */
+int existsquelled = NIL; /* non-zero if an EXISTS was quelled */
+int proxylist = NIL; /* doing a proxy LIST */
+MAILSTREAM *stream = NIL; /* mailbox stream */
+DRIVER *curdriver = NIL; /* note current driver */
+MAILSTREAM *tstream = NIL; /* temporary mailbox stream */
+unsigned int nflags = 0; /* current number of keywords */
+unsigned long nmsgs =0xffffffff;/* last reported # of messages and recent */
+unsigned long recent = 0xffffffff;
+char *nntpproxy = NIL; /* NNTP proxy name */
+unsigned char *user = NIL; /* user name */
+unsigned char *pass = NIL; /* password */
+unsigned char *initial = NIL; /* initial response */
+unsigned char cmdbuf[CMDLEN]; /* command buffer */
+char *status = "starting up"; /* server status */
+char *tag; /* tag portion of command */
+unsigned char *cmd; /* command portion of command */
+unsigned char *arg; /* pointer to current argument of command */
+char *lstwrn = NIL; /* last warning message from c-client */
+char *lsterr = NIL; /* last error message from c-client */
+char *lstref = NIL; /* last referral from c-client */
+char *response = NIL; /* command response */
+struct {
+ unsigned long size; /* size of current LITERAL+ */
+ unsigned int ok : 1; /* LITERAL+ in effect */
+} litplus;
+int litsp = 0; /* literal stack pointer */
+char *litstk[LITSTKLEN]; /* stack to hold literals */
+unsigned long uidvalidity = 0; /* last reported UID validity */
+unsigned long lastuid = 0; /* last fetched uid */
+char *lastid = NIL; /* last fetched body id for this message */
+char *lastsel = NIL; /* last selected mailbox name */
+SIZEDTEXT lastst = {NIL,0}; /* last sizedtext */
+unsigned long cauidvalidity = 0;/* UIDVALIDITY for COPYUID/APPENDUID */
+SEARCHSET *csset = NIL; /* COPYUID source set */
+SEARCHSET *caset = NIL; /* COPYUID/APPENDUID destination set */
+jmp_buf jmpenv; /* stack context for setjmp */
+
+
+/* Response texts which appear in multiple places */
+
+char *win = "%.80s OK ";
+char *rowin = "%.80s OK [READ-ONLY] %.80s completed\015\012";
+char *rwwin = "%.80s OK [READ-WRITE] %.80s completed\015\012";
+char *lose = "%.80s NO ";
+char *logwin = "%.80s OK [";
+char *losetry = "%.80s NO [TRYCREATE] %.80s failed: %.900s\015\012";
+char *loseunknowncte = "%.80s NO [UNKNOWN-CTE] %.80s failed: %.900s\015\012";
+char *badcmd = "%.80s BAD Command unrecognized: %.80s\015\012";
+char *misarg = "%.80s BAD Missing or invalid argument to %.80s\015\012";
+char *badarg = "%.80s BAD Argument given to %.80s when none expected\015\012";
+char *badseq = "%.80s BAD Bogus sequence in %.80s: %.80s\015\012";
+char *badatt = "%.80s BAD Bogus attribute list in %.80s\015\012";
+char *badbin = "%.80s BAD Syntax error in binary specifier\015\012";
+
+/* Message string driver for message stringstructs */
+
+STRINGDRIVER msg_string = {
+ msg_string_init, /* initialize string structure */
+ msg_string_next, /* get next byte in string structure */
+ msg_string_setpos /* set position in string structure */
+};
+
+/* Main program */
+
+int main (int argc,char *argv[])
+{
+ unsigned long i,uid;
+ long f;
+ unsigned char *s,*t,*u,*v,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ logouthook_t lgoh;
+ int ret = 0;
+ time_t autologouttime = 0;
+ char *pgmname;
+ /* if case we get borked immediately */
+ if (setjmp (jmpenv)) _exit (1);
+ pgmname = (argc && argv[0]) ?
+ (((s = strrchr (argv[0],'/')) || (s = strrchr (argv[0],'\\'))) ?
+ (char *) s+1 : argv[0]) : "imapd";
+ /* set service name before linkage */
+ mail_parameters (NIL,SET_SERVICENAME,(void *) "imap");
+#include "linkage.c"
+ rfc822_date (tmp); /* get date/time at startup */
+ /* initialize server */
+ server_init (pgmname,"imap","imaps",clkint,kodint,hupint,trmint,staint);
+ /* forbid automatic untagged expunge */
+ mail_parameters (NIL,SET_EXPUNGEATPING,NIL);
+ /* arm proxy copy callback */
+ mail_parameters (NIL,SET_MAILPROXYCOPY,(void *) proxycopy);
+ /* arm referral callback */
+ mail_parameters (NIL,SET_IMAPREFERRAL,(void *) referral);
+ /* arm COPYUID callback */
+ mail_parameters (NIL,SET_COPYUID,(void *) copyuid);
+ /* arm APPENDUID callback */
+ mail_parameters (NIL,SET_APPENDUID,(void *) appenduid);
+
+ if (stat (SHUTDOWNFILE,&sbuf)) {
+ char proxy[MAILTMPLEN];
+ FILE *nntp = fopen (NNTPFILE,"r");
+ if (nntp) { /* desire NNTP proxy? */
+ if (fgets (proxy,MAILTMPLEN,nntp)) {
+ /* remove newline and set NNTP proxy */
+ if (s = strchr (proxy,'\n')) *s = '\0';
+ nntpproxy = cpystr (proxy);
+ /* disable the news driver */
+ mail_parameters (NIL,DISABLE_DRIVER,"news");
+ }
+ fclose (nntp); /* done reading proxy name */
+ }
+ s = myusername_full (&i); /* get user name and flags */
+ switch (i) {
+ case MU_NOTLOGGEDIN:
+ PSOUT ("* OK ["); /* not logged in, ordinary startup */
+ pcapability (-1);
+ break;
+ case MU_ANONYMOUS:
+ anonymous = T; /* anonymous user, fall into default */
+ s = "ANONYMOUS";
+ case MU_LOGGEDIN:
+ PSOUT ("* PREAUTH ["); /* already logged in, pre-authorized */
+ pcapability (1);
+ user = cpystr (s); /* copy user name */
+ pass = cpystr ("*"); /* set fake password */
+ state = SELECT; /* enter select state */
+ break;
+ default:
+ fatal ("Unknown state from myusername_full()");
+ }
+ PSOUT ("] ");
+ if (user) { /* preauthenticated as someone? */
+ PSOUT ("Pre-authenticated user ");
+ PSOUT (user);
+ PBOUT (' ');
+ }
+ }
+ else { /* login disabled */
+ PSOUT ("* BYE Service not available ");
+ state = LOGOUT;
+ }
+ PSOUT (tcp_serverhost ());
+ PSOUT (" IMAP4rev1 ");
+ PSOUT (CCLIENTVERSION);
+ PBOUT ('.');
+ PSOUT (version);
+ PSOUT (" at ");
+ PSOUT (tmp);
+ CRLF;
+ PFLUSH (); /* dump output buffer */
+ switch (state) { /* do this after the banner */
+ case LOGIN:
+ autologouttime = time (0) + LOGINTIMEOUT;
+ break;
+ case SELECT:
+ syslog (LOG_INFO,"Preauthenticated user=%.80s host=%.80s",
+ user,tcp_clienthost ());
+ break;
+ }
+
+ if (setjmp (jmpenv)) { /* die if a signal handler say so */
+ /* in case we get borked now */
+ if (setjmp (jmpenv)) _exit (1);
+ /* need to close stream gracefully? */
+ if (stream && !stream->lock && (stream->dtb->flags & DR_XPOINT))
+ stream = mail_close (stream);
+ ret = 1; /* set exit status */
+ }
+ else while (state != LOGOUT) {/* command processing loop */
+ slurp (cmdbuf,CMDLEN,TIMEOUT);
+ /* no more last error or literal */
+ if (lstwrn) fs_give ((void **) &lstwrn);
+ if (lsterr) fs_give ((void **) &lsterr);
+ if (lstref) fs_give ((void **) &lstref);
+ while (litsp) fs_give ((void **) &litstk[--litsp]);
+ /* find end of line */
+ if (!strchr (cmdbuf,'\012')) {
+ if (t = strchr (cmdbuf,' ')) *t = '\0';
+ if ((t - cmdbuf) > 100) t = NIL;
+ flush (); /* flush excess */
+ if (state == LOGIN) /* error if NLI */
+ syslog (LOG_INFO,"Line too long before authentication host=%.80s",
+ tcp_clienthost ());
+ sprintf (tmp,response,t ? (char *) cmdbuf : "*");
+ PSOUT (tmp);
+ }
+ else if (!(tag = strtok (cmdbuf," \015\012"))) {
+ if (state == LOGIN) /* error if NLI */
+ syslog (LOG_INFO,"Null command before authentication host=%.80s",
+ tcp_clienthost ());
+ PSOUT ("* BAD Null command\015\012");
+ }
+ else if (strlen (tag) > 50) PSOUT ("* BAD Excessively long tag\015\012");
+ else if (!(s = strtok (NIL," \015\012"))) {
+ if (state == LOGIN) /* error if NLI */
+ syslog (LOG_INFO,"Missing command before authentication host=%.80s",
+ tcp_clienthost ());
+ PSOUT (tag);
+ PSOUT (" BAD Missing command\015\012");
+ }
+ else { /* parse command */
+ response = win; /* set default response */
+ finding = NIL; /* no longer FINDing */
+ ucase (s); /* canonicalize command case */
+ /* UID command? */
+ if (!strcmp (s,"UID") && strtok (NIL," \015\012")) {
+ uid = T; /* a UID command */
+ s[3] = ' '; /* restore the space delimiter */
+ ucase (s); /* make sure command all uppercase */
+ }
+ else uid = NIL; /* not a UID command */
+ /* flush previous saved command */
+ if (cmd) fs_give ((void **) &cmd);
+ cmd = cpystr (s); /* save current command */
+ /* snarf argument, see if possible litplus */
+ if ((arg = strtok (NIL,"\015\012")) && ((i = strlen (arg)) > 3) &&
+ (arg[i - 1] == '}') && (arg[i - 2] == '+') && isdigit (arg[i - 3])) {
+ /* back over possible count */
+ for (i -= 4; i && isdigit (arg[i]); i--);
+ if (arg[i] == '{') { /* found a literal? */
+ litplus.ok = T; /* yes, note LITERAL+ in effect, set size */
+ litplus.size = strtoul (arg + i + 1,NIL,10);
+ }
+ }
+
+ /* these commands always valid */
+ if (!strcmp (cmd,"NOOP")) {
+ if (arg) response = badarg;
+ else if (stream) /* allow untagged EXPUNGE */
+ mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
+ }
+ else if (!strcmp (cmd,"LOGOUT")) {
+ if (arg) response = badarg;
+ else { /* time to say farewell */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ if (lastsel) fs_give ((void **) &lastsel);
+ if (state == OPEN) stream = mail_close (stream);
+ state = LOGOUT;
+ PSOUT ("* BYE ");
+ PSOUT (mylocalhost ());
+ PSOUT (" IMAP4rev1 server terminating connection\015\012");
+ }
+ }
+ else if (!strcmp (cmd,"CAPABILITY")) {
+ if (arg) response = badarg;
+ else {
+ PSOUT ("* ");
+ pcapability (0); /* print capabilities */
+ CRLF;
+ }
+ if (stream) /* allow untagged EXPUNGE */
+ mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
+ }
+#ifdef NETSCAPE_BRAIN_DAMAGE
+ else if (!strcmp (cmd,"NETSCAPE")) {
+ PSOUT ("* OK [NETSCAPE]\015\012* VERSION 1.0 UNIX\015\012* ACCOUNT-URL \"");
+ PSOUT (NETSCAPE_BRAIN_DAMAGE);
+ PBOUT ('"');
+ CRLF;
+ }
+#endif
+
+ else switch (state) { /* dispatch depending upon state */
+ case LOGIN: /* waiting to get logged in */
+ /* new style authentication */
+ if (!strcmp (cmd,"AUTHENTICATE")) {
+ if (user) fs_give ((void **) &user);
+ if (pass) fs_give ((void **) &pass);
+ initial = NIL; /* no initial argument */
+ cancelled = NIL; /* not cancelled */
+ /* mandatory first argument */
+ if (!(s = snarf (&arg))) response = misarg;
+ else if (arg && !(initial = snarf_base64 (&arg)))
+ response = misarg; /* optional second argument */
+ else if (arg) response = badarg;
+ else if (!strcmp (ucase (s),"ANONYMOUS") && !stat (ANOFILE,&sbuf)) {
+ if (!(s = imap_responder ("",0,NIL)))
+ response ="%.80s BAD AUTHENTICATE ANONYMOUS cancelled\015\012";
+ else if (anonymous_login (argc,argv)) {
+ anonymous = T; /* note we are anonymous */
+ user = cpystr ("ANONYMOUS");
+ pass = cpystr ("*");
+ state = SELECT; /* make select */
+ alerttime = 0; /* force alert */
+ response = logwin;/* return logged-in capabilities */
+ syslog (LOG_INFO,"Authenticated anonymous=%.80s host=%.80s",s,
+ tcp_clienthost ());
+ fs_give ((void **) &s);
+ }
+ else response ="%.80s NO AUTHENTICATE ANONYMOUS failed\015\012";
+ }
+ else if (user = cpystr (mail_auth (s,imap_responder,argc,argv))) {
+ pass = cpystr ("*");
+ state = SELECT; /* make select */
+ alerttime = 0; /* force alert */
+ response = logwin; /* return logged-in capabilities */
+ syslog (LOG_INFO,"Authenticated user=%.80s host=%.80s mech=%.80s",
+ user,tcp_clienthost (),s);
+ }
+
+ else {
+ AUTHENTICATOR *auth = mail_lookup_auth (1);
+ char *msg = (char *) fs_get (strlen (cmd) + strlen (s) + 2);
+ sprintf (msg,"%s %s",cmd,s);
+ fs_give ((void **) &cmd);
+ cmd = msg;
+ for (i = !mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL);
+ auth && compare_cstring (s,auth->name); auth = auth->next);
+ /* Failed authentication when hidden looks like invalid command.
+ * This is intentional but confused me when I was debugging.
+ */
+ if (auth && auth->server && !(auth->flags & AU_DISABLE) &&
+ !(auth->flags & AU_HIDE) && (i || (auth->flags & AU_SECURE))) {
+ response = lose;
+ if (cancelled) {
+ if (lsterr) fs_give ((void **) &lsterr);
+ lsterr = cpystr ("cancelled by user");
+ }
+ if (!lsterr) /* catch-all */
+ lsterr = cpystr ("Invalid authentication credentials");
+ syslog (LOG_INFO,"AUTHENTICATE %.80s failure host=%.80s",s,
+ tcp_clienthost ());
+ }
+ else {
+ response = badcmd;
+ syslog (LOG_INFO,"AUTHENTICATE %.80s invalid host=%.80s",s,
+ tcp_clienthost ());
+ }
+ }
+ }
+
+ /* plaintext login with password */
+ else if (!strcmp (cmd,"LOGIN")) {
+ if (user) fs_give ((void **) &user);
+ if (pass) fs_give ((void **) &pass);
+ /* two arguments */
+ if (!((user = cpystr (snarf (&arg))) &&
+ (pass = cpystr (snarf (&arg))))) response = misarg;
+ else if (arg) response = badarg;
+ /* see if we allow anonymous */
+ else if (!compare_cstring (user,"ANONYMOUS") &&
+ !stat (ANOFILE,&sbuf) && anonymous_login (argc,argv)) {
+ anonymous = T; /* note we are anonymous */
+ ucase (user); /* make all uppercase for consistency */
+ state = SELECT; /* make select */
+ alerttime = 0; /* force alert */
+ response = logwin; /* return logged-in capabilities */
+ syslog (LOG_INFO,"Login anonymous=%.80s host=%.80s",pass,
+ tcp_clienthost ());
+ }
+ else { /* delimit user from possible admin */
+ if (s = strchr (user,'*')) *s++ ='\0';
+ /* see if username and password are OK */
+ if (server_login (user,pass,s,argc,argv)) {
+ state = SELECT; /* make select */
+ alerttime = 0; /* force alert */
+ response = logwin;/* return logged-in capabilities */
+ syslog (LOG_INFO,"Login user=%.80s host=%.80s",user,
+ tcp_clienthost ());
+ }
+ else {
+ response = lose;
+ if (!lsterr) lsterr = cpystr ("Invalid login credentials");
+ }
+ }
+ }
+ /* start TLS security */
+ else if (!strcmp (cmd,"STARTTLS")) {
+ if (arg) response = badarg;
+ else if (lsterr = ssl_start_tls (pgmname)) response = lose;
+ }
+ else response = badcmd;
+ break;
+
+ case OPEN: /* valid only when mailbox open */
+ /* fetch mailbox attributes */
+ if (!strcmp (cmd,"FETCH") || !strcmp (cmd,"UID FETCH")) {
+ if (!(arg && (s = strtok (arg," ")) && (t = strtok(NIL,"\015\012"))))
+ response = misarg;
+ else if (uid ? mail_uid_sequence (stream,s) :
+ mail_sequence (stream,s)) fetch (t,uid);
+ else response = badseq;
+ }
+ /* store mailbox attributes */
+ else if (!strcmp (cmd,"STORE") || !strcmp (cmd,"UID STORE")) {
+ /* must have three arguments */
+ if (!(arg && (s = strtok (arg," ")) && (v = strtok (NIL," ")) &&
+ (t = strtok (NIL,"\015\012")))) response = misarg;
+ else if (!(uid ? mail_uid_sequence (stream,s) :
+ mail_sequence (stream,s))) response = badseq;
+ else {
+ f = ST_SET | (uid ? ST_UID : NIL)|((v[5]&&v[6]) ? ST_SILENT : NIL);
+ if (!strcmp (ucase (v),"FLAGS") || !strcmp (v,"FLAGS.SILENT")) {
+ strcpy (tmp,"\\Answered \\Flagged \\Deleted \\Draft \\Seen");
+ for (i = 0, u = tmp;
+ (i < NUSERFLAGS) && (v = stream->user_flags[i]); i++)
+ if (strlen (v) <
+ ((size_t) (MAILTMPLEN - ((u += strlen (u)) + 2 - tmp)))) {
+ *u++ = ' '; /* write next flag */
+ strcpy (u,v);
+ }
+ mail_flag (stream,s,tmp,f & ~ST_SET);
+ }
+ else if (!strcmp (v,"-FLAGS") || !strcmp (v,"-FLAGS.SILENT"))
+ f &= ~ST_SET; /* clear flags */
+ else if (strcmp (v,"+FLAGS") && strcmp (v,"+FLAGS.SILENT")) {
+ response = badatt;
+ break;
+ }
+ /* find last keyword */
+ for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i]; i++);
+ mail_flag (stream,s,t,f);
+ /* any new keywords appeared? */
+ if (i < NUSERFLAGS && stream->user_flags[i]) new_flags (stream);
+ /* return flags if silence not wanted */
+ if (uid ? mail_uid_sequence (stream,s) : mail_sequence (stream,s))
+ for (i = 1; i <= nmsgs; i++) if (mail_elt(stream,i)->sequence)
+ mail_elt (stream,i)->spare2 = (f & ST_SILENT) ? NIL : T;
+ }
+ }
+
+ /* check for new mail */
+ else if (!strcmp (cmd,"CHECK")) {
+ /* no arguments */
+ if (arg) response = badarg;
+ else if (!anonymous) {
+ mail_check (stream);
+ /* remember last check time */
+ lastcheck = time (0);
+ }
+ }
+ /* expunge deleted messages */
+ else if (!(anonymous || (strcmp (cmd,"EXPUNGE") &&
+ strcmp (cmd,"UID EXPUNGE")))) {
+ if (uid && !arg) response = misarg;
+ else if (!uid && arg) response = badarg;
+ else { /* expunge deleted or specified UIDs */
+ mail_expunge_full (stream,arg,arg ? EX_UID : NIL);
+ /* remember last checkpoint */
+ lastcheck = time (0);
+ }
+ }
+ /* close mailbox */
+ else if (!strcmp (cmd,"CLOSE") || !strcmp (cmd,"UNSELECT")) {
+ /* no arguments */
+ if (arg) response = badarg;
+ else {
+ /* no last uid */
+ uidvalidity = lastuid = 0;
+ if (lastsel) fs_give ((void **) &lastsel);
+ if (lastid) fs_give ((void **) &lastid);
+ if (lastst.data) fs_give ((void **) &lastst.data);
+ stream = mail_close_full (stream,((*cmd == 'C') && !anonymous) ?
+ CL_EXPUNGE : NIL);
+ state = SELECT; /* no longer opened */
+ lastcheck = 0; /* no last checkpoint */
+ }
+ }
+ else if (!anonymous && /* copy message(s) */
+ (!strcmp (cmd,"COPY") || !strcmp (cmd,"UID COPY"))) {
+ trycreate = NIL; /* no trycreate status */
+ if (!(arg && (s = strtok (arg," ")) && (arg = strtok(NIL,"\015\012"))
+ && (t = snarf (&arg)))) response = misarg;
+ else if (arg) response = badarg;
+ else if (!nmsgs) {
+ response = lose;
+ if (!lsterr) lsterr = cpystr ("Mailbox is empty");
+ }
+ else if (!(uid ? mail_uid_sequence (stream,s) :
+ mail_sequence (stream,s))) response = badseq;
+ /* try copy */
+ else if (!mail_copy_full (stream,s,t,uid ? CP_UID : NIL)) {
+ response = trycreate ? losetry : lose;
+ if (!lsterr) lsterr = cpystr ("No such destination mailbox");
+ }
+ }
+
+ /* sort mailbox */
+ else if (!strcmp (cmd,"SORT") || !strcmp (cmd,"UID SORT")) {
+ /* must have four arguments */
+ if (!(arg && (*arg == '(') && (t = strchr (s = arg + 1,')')) &&
+ (t[1] == ' ') && (*(arg = t + 2)))) response = misarg;
+ else { /* read criteria */
+ SEARCHPGM *spg = NIL;
+ char *cs = NIL;
+ SORTPGM *pgm = NIL,*pg = NIL;
+ unsigned long *slst,*sl;
+ *t = NIL; /* tie off criteria list */
+ if (!(s = strtok (ucase (s)," "))) response = badatt;
+ else {
+ do { /* parse sort attributes */
+ if (pg) pg = pg->next = mail_newsortpgm ();
+ else pgm = pg = mail_newsortpgm ();
+ if (!strcmp (s,"REVERSE")) {
+ pg->reverse = T;
+ if (!(s = strtok (NIL," "))) {
+ s = ""; /* end of attributes */
+ break;
+ }
+ }
+ if (!strcmp (s,"DATE")) pg->function = SORTDATE;
+ else if (!strcmp (s,"ARRIVAL")) pg->function = SORTARRIVAL;
+ else if (!strcmp (s,"FROM")) pg->function = SORTFROM;
+ else if (!strcmp (s,"SUBJECT")) pg->function = SORTSUBJECT;
+ else if (!strcmp (s,"TO")) pg->function = SORTTO;
+ else if (!strcmp (s,"CC")) pg->function = SORTCC;
+ else if (!strcmp (s,"SIZE")) pg->function = SORTSIZE;
+ else break;
+ } while (s = strtok (NIL," "));
+ /* bad SORT attribute */
+ if (s) response = badatt;
+ /* get charset and search criteria */
+ else if (!((t = snarf (&arg)) && (cs = cpystr (t)) && arg &&
+ *arg)) response = misarg;
+ /* parse search criteria */
+ else if (!parse_criteria (spg = mail_newsearchpgm (),&arg,nmsgs,
+ uidmax (stream),0)) response = badatt;
+ else if (arg && *arg) response = badarg;
+ else if (slst = mail_sort (stream,cs,spg,pgm,uid ? SE_UID:NIL)) {
+ PSOUT ("* SORT");
+ for (sl = slst; *sl; sl++) {
+ PBOUT (' ');
+ pnum (*sl);
+ }
+ CRLF;
+ fs_give ((void **) &slst);
+ }
+ }
+ if (pgm) mail_free_sortpgm (&pgm);
+ if (spg) mail_free_searchpgm (&spg);
+ if (cs) fs_give ((void **) &cs);
+ }
+ }
+
+ /* thread mailbox */
+ else if (!strcmp (cmd,"THREAD") || !strcmp (cmd,"UID THREAD")) {
+ THREADNODE *thr;
+ SEARCHPGM *spg = NIL;
+ char *cs = NIL;
+ /* must have four arguments */
+ if (!(arg && (s = strtok (arg," ")) && (cs = strtok (NIL," ")) &&
+ (cs = cpystr (cs)) && (arg = strtok (NIL,"\015\012"))))
+ response = misarg;
+ else if (!parse_criteria (spg = mail_newsearchpgm (),&arg,nmsgs,
+ uidmax (stream),0)) response = badatt;
+ else if (arg && *arg) response = badarg;
+ else {
+ if (thr = mail_thread (stream,s,cs,spg,uid ? SE_UID : NIL)) {
+ PSOUT ("* THREAD ");
+ pthread (thr);
+ mail_free_threadnode (&thr);
+ }
+ else PSOUT ("* THREAD");
+ CRLF;
+ }
+ if (spg) mail_free_searchpgm (&spg);
+ if (cs) fs_give ((void **) &cs);
+ }
+
+ /* search mailbox */
+ else if (!strcmp (cmd,"SEARCH") || !strcmp (cmd,"UID SEARCH")) {
+ int retval = NIL;
+ char *charset = NIL;
+ SEARCHPGM *pgm;
+ response = misarg; /* assume failure */
+ if (!arg) break; /* one or more arguments required */
+ if (((arg[0] == 'R') || (arg[0] == 'r')) &&
+ ((arg[1] == 'E') || (arg[1] == 'e')) &&
+ ((arg[2] == 'T') || (arg[2] == 't')) &&
+ ((arg[3] == 'U') || (arg[3] == 'u')) &&
+ ((arg[4] == 'R') || (arg[4] == 'r')) &&
+ ((arg[5] == 'N') || (arg[5] == 'n')) &&
+ (arg[6] == ' ') && (arg[7] == '(')) {
+ retval = 0x4000; /* return is specified */
+ for (arg += 8; *arg && (*arg != ')'); ) {
+ if (((arg[0] == 'M') || (arg[0] == 'm')) &&
+ ((arg[1] == 'I') || (arg[1] == 'i')) &&
+ ((arg[2] == 'N') || (arg[2] == 'n')) &&
+ ((arg[3] == ' ') || (arg[3] == ')'))) {
+ retval |= 0x1;
+ arg += 3;
+ }
+ else if (((arg[0] == 'M') || (arg[0] == 'm')) &&
+ ((arg[1] == 'A') || (arg[1] == 'a')) &&
+ ((arg[2] == 'X') || (arg[2] == 'x')) &&
+ ((arg[3] == ' ') || (arg[3] == ')'))) {
+ retval |= 0x2;
+ arg += 3;
+ }
+ else if (((arg[0] == 'A') || (arg[0] == 'a')) &&
+ ((arg[1] == 'L') || (arg[1] == 'l')) &&
+ ((arg[2] == 'L') || (arg[2] == 'l')) &&
+ ((arg[3] == ' ') || (arg[3] == ')'))) {
+ retval |= 0x4;
+ arg += 3;
+ }
+ else if (((arg[0] == 'C') || (arg[0] == 'c')) &&
+ ((arg[1] == 'O') || (arg[1] == 'o')) &&
+ ((arg[2] == 'U') || (arg[2] == 'u')) &&
+ ((arg[3] == 'N') || (arg[3] == 'n')) &&
+ ((arg[4] == 'T') || (arg[4] == 't')) &&
+ ((arg[5] == ' ') || (arg[5] == ')'))) {
+ retval |= 0x10;
+ arg += 5;
+ }
+ else break; /* unknown return value */
+ /* more return values to come */
+ if ((*arg == ' ') && (arg[1] != ')')) ++arg;
+ }
+ /* RETURN list must be properly terminated */
+ if ((*arg++ != ')') || (*arg++ != ' ')) break;
+ /* default return value is ALL */
+ if (!(retval &= 0x3fff)) retval = 0x4;
+ }
+
+ /* character set specified? */
+ if (((arg[0] == 'C') || (arg[0] == 'c')) &&
+ ((arg[1] == 'H') || (arg[1] == 'h')) &&
+ ((arg[2] == 'A') || (arg[2] == 'a')) &&
+ ((arg[3] == 'R') || (arg[3] == 'r')) &&
+ ((arg[4] == 'S') || (arg[4] == 's')) &&
+ ((arg[5] == 'E') || (arg[5] == 'e')) &&
+ ((arg[6] == 'T') || (arg[6] == 't')) &&
+ (arg[7] == ' ')) {
+ arg += 8; /* yes, skip over CHARSET token */
+ if (s = snarf (&arg)) charset = cpystr (s);
+ else break; /* missing character set */
+ }
+ /* must have arguments here */
+ if (!(arg && *arg)) break;
+ if (parse_criteria (pgm = mail_newsearchpgm (),&arg,nmsgs,
+ uidmax (stream),0) && !*arg) {
+ response = win; /* looks good, try the search */
+ mail_search_full (stream,charset,pgm,SE_FREE);
+ /* output search results if success */
+ if (response == win) {
+ if (retval) { /* ESEARCH desired */
+ PSOUT ("* ESEARCH (TAG ");
+ pstring (tag);
+ PBOUT (')');
+ if (uid) PSOUT (" UID");
+ /* wants MIN */
+ if (retval & 0x1) {
+ for (i = 1; (i <= nmsgs) && !mail_elt (stream,i)->searched;
+ ++i);
+ if (i <= nmsgs) {
+ PSOUT (" MIN ");
+ pnum (uid ? mail_uid (stream,i) : i);
+ }
+ }
+ /* wants MAX */
+ if (retval & 0x2) {
+ for (i = nmsgs; i && !mail_elt (stream,i)->searched; --i);
+ if (i) {
+ PSOUT (" MAX ");
+ pnum (uid ? mail_uid (stream,i) : i);
+ }
+ }
+
+ /* wants ALL */
+ if (retval & 0x4) {
+ unsigned long j;
+ /* find first match */
+ for (i = 1; (i <= nmsgs) && !mail_elt (stream,i)->searched;
+ ++i);
+ if (i <= nmsgs) {
+ PSOUT (" ALL ");
+ pnum (uid ? mail_uid (stream,i) : i);
+ j = i; /* last message output */
+ }
+ while (++i <= nmsgs) {
+ if (mail_elt (stream,i)->searched) {
+ while ((++i <= nmsgs) && mail_elt (stream,i)->searched);
+ /* previous message is end of range */
+ if (j != --i) {
+ PBOUT (':');
+ pnum (uid ? mail_uid (stream,i) : i);
+ }
+ }
+ /* search for next match */
+ while ((++i <= nmsgs) && !mail_elt (stream,i)->searched);
+ if (i <= nmsgs) {
+ PBOUT (',');
+ pnum (uid ? mail_uid (stream,i) : i);
+ j = i; /* last message output */
+ }
+ }
+ }
+ /* wants COUNT */
+ if (retval & 0x10) {
+ unsigned long j;
+ for (i = 1, j = 0; i <= nmsgs; ++i)
+ if (mail_elt (stream,i)->searched) ++j;
+ PSOUT (" COUNT ");
+ pnum (j);
+ }
+ }
+ else { /* standard search */
+ PSOUT ("* SEARCH");
+ for (i = 1; i <= nmsgs; ++i)
+ if (mail_elt (stream,i)->searched) {
+ PBOUT (' ');
+ pnum (uid ? mail_uid (stream,i) : i);
+ }
+ }
+ CRLF;
+ }
+ }
+ else mail_free_searchpgm (&pgm);
+ if (charset) fs_give ((void **) &charset);
+ }
+
+ else /* fall into select case */
+ case SELECT: /* valid whenever logged in */
+ /* select new mailbox */
+ if (!(strcmp (cmd,"SELECT") && strcmp (cmd,"EXAMINE") &&
+ strcmp (cmd,"BBOARD"))) {
+ /* single argument */
+ if (!(s = snarf (&arg))) response = misarg;
+ else if (arg) response = badarg;
+ else if (nameok (NIL,s = bboardname (cmd,s))) {
+ DRIVER *factory = mail_valid (NIL,s,NIL);
+ f = (anonymous ? OP_ANONYMOUS + OP_READONLY : NIL) |
+ ((*cmd == 'S') ? NIL : OP_READONLY);
+ curdriver = NIL; /* no drivers known */
+ /* no last uid */
+ uidvalidity = lastuid = 0;
+ if (lastid) fs_give ((void **) &lastid);
+ if (lastst.data) fs_give ((void **) &lastst.data);
+ nflags = 0; /* force update */
+ nmsgs = recent = 0xffffffff;
+ if (factory && !strcmp (factory->name,"phile") &&
+ (stream = mail_open (stream,s,f | OP_SILENT)) &&
+ (response == win)) {
+ BODY *b;
+ /* see if proxy open */
+ if ((mail_elt (stream,1)->rfc822_size < 400) &&
+ mail_fetchstructure (stream,1,&b) && (b->type == TYPETEXT) &&
+ (t = mail_fetch_text (stream,1,NIL,&i,NIL)) &&
+ (i < MAILTMPLEN) && (t[0] == '{')) {
+ /* copy and tie off */
+ strncpy (tmp,t,i)[i] = '\0';
+ /* nuke any trailing newline */
+ if (t = strpbrk (tmp,"\r\n")) *t = '\0';
+ /* try to open proxy */
+ if ((tstream = mail_open (NIL,tmp,f | OP_SILENT)) &&
+ (response == win) && tstream->nmsgs) {
+ s = tmp; /* got it, close the link */
+ mail_close (stream);
+ stream = tstream;
+ tstream = NIL;
+ }
+ }
+ /* now give the exists event */
+ stream->silent = NIL;
+ mm_exists (stream,stream->nmsgs);
+ }
+ else if (!factory && isnewsproxy (s)) {
+ sprintf (tmp,"{%.300s/nntp}%.300s",nntpproxy,(char *) s+6);
+ stream = mail_open (stream,tmp,f);
+ }
+ /* open stream normally then */
+ else stream = mail_open (stream,s,f);
+
+ if (stream && (response == win)) {
+ state = OPEN; /* note state open */
+ if (lastsel) fs_give ((void **) &lastsel);
+ /* canonicalize INBOX */
+ if (!compare_cstring (s,"#MHINBOX"))
+ lastsel = cpystr ("#MHINBOX");
+ else lastsel = cpystr (compare_cstring (s,"INBOX") ?
+ (char *) s : "INBOX");
+ /* note readonly/readwrite */
+ response = stream->rdonly ? rowin : rwwin;
+ if (anonymous)
+ syslog (LOG_INFO,"Anonymous select of %.80s host=%.80s",
+ stream->mailbox,tcp_clienthost ());
+ lastcheck = 0; /* no last check */
+ }
+ else { /* failed, nuke old selection */
+ if (stream) stream = mail_close (stream);
+ state = SELECT; /* no mailbox open now */
+ if (lastsel) fs_give ((void **) &lastsel);
+ response = lose; /* open failed */
+ }
+ }
+ }
+
+ /* APPEND message to mailbox */
+ else if (!(anonymous || strcmp (cmd,"APPEND"))) {
+ /* parse mailbox name */
+ if ((s = snarf (&arg)) && arg) {
+ STRING st; /* message stringstruct */
+ APPENDDATA ad;
+ ad.arg = arg; /* command arguments */
+ /* no message yet */
+ ad.flags = ad.date = ad.msg = NIL;
+ ad.message = &st; /* pointer to stringstruct to use */
+ trycreate = NIL; /* no trycreate status */
+ if (!mail_append_multiple (NIL,s,append_msg,(void *) &ad)) {
+ if (response == win) response = trycreate ? losetry : lose;
+ /* this can happen with #driver. hack */
+ if (!lsterr) lsterr = cpystr ("No such destination mailbox");
+ }
+ /* clean up any message text left behind */
+ if (ad.flags) fs_give ((void **) &ad.flags);
+ if (ad.date) fs_give ((void **) &ad.date);
+ if (ad.msg) fs_give ((void **) &ad.msg);
+ }
+ else response = misarg;
+ if (stream) /* allow untagged EXPUNGE */
+ mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
+ }
+ /* list mailboxes */
+ else if (!strcmp (cmd,"LIST") || !strcmp (cmd,"RLIST")) {
+ /* get reference and mailbox argument */
+ if (!((s = snarf (&arg)) && (t = snarf_list (&arg))))
+ response = misarg;
+ else if (arg) response = badarg;
+ /* make sure anonymous can't do bad things */
+ else if (nameok (s,t)) {
+ if (newsproxypattern (s,t,tmp,LONGT)) {
+ proxylist = T;
+ mail_list (NIL,"",tmp);
+ proxylist = NIL;
+ }
+ else mail_list (NIL,s,t);
+ }
+ if (stream) /* allow untagged EXPUNGE */
+ mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
+ }
+ /* scan mailboxes */
+ else if (!strcmp (cmd,"SCAN")) {
+ /* get arguments */
+ if (!((s = snarf (&arg)) && (t = snarf_list (&arg)) &&
+ (u = snarf (&arg)))) response = misarg;
+ else if (arg) response = badarg;
+ /* make sure anonymous can't do bad things */
+ else if (nameok (s,t)) {
+ if (newsproxypattern (s,t,tmp,NIL))
+ mm_log ("SCAN not permitted for news",ERROR);
+ else mail_scan (NIL,s,t,u);
+ }
+ if (stream) /* allow untagged EXPUNGE */
+ mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
+ }
+ /* list subscribed mailboxes */
+ else if (!strcmp (cmd,"LSUB") || !strcmp (cmd,"RLSUB")) {
+ /* get reference and mailbox argument */
+ if (!((s = snarf (&arg)) && (t = snarf_list (&arg))))
+ response = misarg;
+ else if (arg) response = badarg;
+ /* make sure anonymous can't do bad things */
+ else if (nameok (s,t)) {
+ if (newsproxypattern (s,t,tmp,NIL)) newsrc_lsub (NIL,tmp);
+ else mail_lsub (NIL,s,t);
+ }
+ if (stream) /* allow untagged EXPUNGE */
+ mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
+ }
+
+ /* find mailboxes */
+ else if (!strcmp (cmd,"FIND")) {
+ /* get subcommand and true argument */
+ if (!(arg && (s = strtok (arg," \015\012")) && (s == cmd + 5) &&
+ (cmd[4] = ' ') && ucase (s) &&
+ (arg = strtok (NIL,"\015\012")) && (s = snarf_list (&arg))))
+ response = misarg; /* missing required argument */
+ else if (arg) response = badarg;
+ /* punt on single-char wildcards */
+ else if (strpbrk (s,"%?")) response =
+ "%.80s NO IMAP2 ? and %% wildcards not supported: %.80s\015\012";
+ else if (nameok (NIL,s)) {
+ finding = T; /* note that we are FINDing */
+ /* dispatch based on type */
+ if (!strcmp (cmd,"FIND MAILBOXES") && !anonymous)
+ mail_lsub (NIL,NIL,s);
+ else if (!strcmp (cmd,"FIND ALL.MAILBOXES")) {
+ /* convert * to % for compatible behavior */
+ for (t = s; *t; t++) if (*t == '*') *t = '%';
+ mail_list (NIL,NIL,s);
+ }
+ else response = badcmd;
+ }
+ if (stream) /* allow untagged EXPUNGE */
+ mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
+ }
+
+ /* status of mailbox */
+ else if (!strcmp (cmd,"STATUS")) {
+ if (!((s = snarf (&arg)) && arg && (*arg++ == '(') &&
+ (t = strchr (arg,')')) && (t - arg) && !t[1]))
+ response = misarg;
+ else {
+ f = NIL; /* initially no flags */
+ *t = '\0'; /* tie off flag string */
+ /* read flags */
+ t = strtok (ucase (arg)," ");
+ do { /* parse each one; unknown generate warning */
+ if (!strcmp (t,"MESSAGES")) f |= SA_MESSAGES;
+ else if (!strcmp (t,"RECENT")) f |= SA_RECENT;
+ else if (!strcmp (t,"UNSEEN")) f |= SA_UNSEEN;
+ else if (!strcmp (t,"UIDNEXT")) f |= SA_UIDNEXT;
+ else if (!strcmp (t,"UIDVALIDITY")) f |= SA_UIDVALIDITY;
+ else {
+ PSOUT ("* NO Unknown status flag ");
+ PSOUT (t);
+ CRLF;
+ }
+ } while (t = strtok (NIL," "));
+ ping_mailbox (uid); /* in case the fool did STATUS on open mbx */
+ PFLUSH (); /* make sure stdout is dumped in case slave */
+ if (!compare_cstring (s,"INBOX")) s = "INBOX";
+ else if (!compare_cstring (s,"#MHINBOX")) s = "#MHINBOX";
+ if (state == LOGOUT) response = lose;
+ /* get mailbox status */
+ else if (lastsel && (!strcmp (s,lastsel) ||
+ (stream && !strcmp (s,stream->mailbox)))) {
+ unsigned long unseen;
+ /* snarl at cretins which do this */
+ PSOUT ("* NO CLIENT BUG DETECTED: STATUS on selected mailbox: ");
+ PSOUT (s);
+ CRLF;
+ tmp[0] = ' '; tmp[1] = '\0';
+ if (f & SA_MESSAGES)
+ sprintf (tmp + strlen (tmp)," MESSAGES %lu",stream->nmsgs);
+ if (f & SA_RECENT)
+ sprintf (tmp + strlen (tmp)," RECENT %lu",stream->recent);
+ if (f & SA_UNSEEN) {
+ for (i = 1,unseen = 0; i <= stream->nmsgs; i++)
+ if (!mail_elt (stream,i)->seen) unseen++;
+ sprintf (tmp + strlen (tmp)," UNSEEN %lu",unseen);
+ }
+ if (f & SA_UIDNEXT)
+ sprintf (tmp + strlen (tmp)," UIDNEXT %lu",stream->uid_last+1);
+ if (f & SA_UIDVALIDITY)
+ sprintf (tmp + strlen(tmp)," UIDVALIDITY %lu",
+ stream->uid_validity);
+ tmp[1] = '(';
+ strcat (tmp,")\015\012");
+ PSOUT ("* STATUS ");
+ pastring (s);
+ PSOUT (tmp);
+ }
+ else if (isnewsproxy (s)) {
+ sprintf (tmp,"{%.300s/nntp}%.300s",nntpproxy,(char *) s+6);
+ if (!mail_status (NIL,tmp,f)) response = lose;
+ }
+ else if (!mail_status (NIL,s,f)) response = lose;
+ }
+ if (stream) /* allow untagged EXPUNGE */
+ mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
+ }
+
+ /* subscribe to mailbox */
+ else if (!(anonymous || strcmp (cmd,"SUBSCRIBE"))) {
+ /* get <mailbox> or MAILBOX <mailbox> */
+ if (!(s = snarf (&arg))) response = misarg;
+ else if (arg) { /* IMAP2bis form */
+ if (compare_cstring (s,"MAILBOX")) response = badarg;
+ else if (!(s = snarf (&arg))) response = misarg;
+ else if (arg) response = badarg;
+ else mail_subscribe (NIL,s);
+ }
+ else if (isnewsproxy (s)) newsrc_update (NIL,s+6,':');
+ else mail_subscribe (NIL,s);
+ if (stream) /* allow untagged EXPUNGE */
+ mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
+ }
+ /* unsubscribe to mailbox */
+ else if (!(anonymous || strcmp (cmd,"UNSUBSCRIBE"))) {
+ /* get <mailbox> or MAILBOX <mailbox> */
+ if (!(s = snarf (&arg))) response = misarg;
+ else if (arg) { /* IMAP2bis form */
+ if (compare_cstring (s,"MAILBOX")) response = badarg;
+ else if (!(s = snarf (&arg))) response = misarg;
+ else if (arg) response = badarg;
+ else if (isnewsproxy (s)) newsrc_update (NIL,s+6,'!');
+ else mail_unsubscribe (NIL,s);
+ }
+ else mail_unsubscribe (NIL,s);
+ if (stream) /* allow untagged EXPUNGE */
+ mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
+ }
+
+ else if (!strcmp (cmd,"NAMESPACE")) {
+ if (arg) response = badarg;
+ else {
+ NAMESPACE **ns = (NAMESPACE **) mail_parameters(NIL,GET_NAMESPACE,
+ NIL);
+ NAMESPACE *n;
+ PARAMETER *p;
+ PSOUT ("* NAMESPACE");
+ if (ns) for (i = 0; i < 3; i++) {
+ if (n = ns[i]) {
+ PSOUT (" (");
+ do {
+ PBOUT ('(');
+ pstring (n->name);
+ switch (n->delimiter) {
+ case '\\': /* quoted delimiter */
+ case '"':
+ PSOUT (" \"\\\\\"");
+ break;
+ case '\0': /* no delimiter */
+ PSOUT (" NIL");
+ break;
+ default: /* unquoted delimiter */
+ PSOUT (" \"");
+ PBOUT (n->delimiter);
+ PBOUT ('"');
+ break;
+ }
+ /* NAMESPACE extensions are hairy */
+ if (p = n->param) do {
+ PBOUT (' ');
+ pstring (p->attribute);
+ PSOUT (" (");
+ do pstring (p->value);
+ while (p->next && !p->next->attribute && (p = p->next));
+ PBOUT (')');
+ } while (p = p->next);
+ PBOUT (')');
+ } while (n = n->next);
+ PBOUT (')');
+ }
+ else PSOUT (" NIL");
+ }
+ else PSOUT (" NIL NIL NIL");
+ CRLF;
+ }
+ if (stream) /* allow untagged EXPUNGE */
+ mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
+ }
+
+ /* create mailbox */
+ else if (!(anonymous || strcmp (cmd,"CREATE"))) {
+ if (!(s = snarf (&arg))) response = misarg;
+ else if (arg) response = badarg;
+ else mail_create (NIL,s);
+ if (stream) /* allow untagged EXPUNGE */
+ mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
+ }
+ /* delete mailbox */
+ else if (!(anonymous || strcmp (cmd,"DELETE"))) {
+ if (!(s = snarf (&arg))) response = misarg;
+ else if (arg) response = badarg;
+ else { /* make sure not selected */
+ if (lastsel && (!strcmp (s,lastsel) ||
+ (stream && !strcmp (s,stream->mailbox))))
+ mm_log ("Can not DELETE the selected mailbox",ERROR);
+ else mail_delete (NIL,s);
+ }
+ if (stream) /* allow untagged EXPUNGE */
+ mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
+ }
+ /* rename mailbox */
+ else if (!(anonymous || strcmp (cmd,"RENAME"))) {
+ if (!((s = snarf (&arg)) && (t = snarf (&arg)))) response = misarg;
+ else if (arg) response = badarg;
+ else { /* make sure not selected */
+ if (!compare_cstring (s,"INBOX")) s = "INBOX";
+ else if (!compare_cstring (s,"#MHINBOX")) s = "#MHINBOX";
+ if (lastsel && (!strcmp (s,lastsel) ||
+ (stream && !strcmp (s,stream->mailbox))))
+ mm_log ("Can not RENAME the selected mailbox",ERROR);
+ else mail_rename (NIL,s,t);
+ }
+ if (stream) /* allow untagged EXPUNGE */
+ mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
+ }
+
+ /* idle mode */
+ else if (!strcmp (cmd,"IDLE")) {
+ /* no arguments */
+ if (arg) response = badarg;
+ else { /* tell client ready for argument */
+ unsigned long donefake = 0;
+ PSOUT ("+ Waiting for DONE\015\012");
+ PFLUSH (); /* dump output buffer */
+ /* inactivity countdown */
+ i = ((TIMEOUT) / (IDLETIMER)) + 1;
+ do { /* main idle loop */
+ if (!donefake) { /* don't ping mailbox if faking */
+ mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,
+ (void *) stream);
+ ping_mailbox (uid);
+ /* maybe do a checkpoint if not anonymous */
+ if (!anonymous && stream && (time (0) > lastcheck + CHECKTIMER)) {
+ mail_check (stream);
+ /* cancel likely altwin from mail_check() */
+ if (lsterr) fs_give ((void **) &lsterr);
+ if (lstwrn) fs_give ((void **) &lstwrn);
+ /* remember last checkpoint */
+ lastcheck = time (0);
+ }
+ }
+ if (lstwrn) { /* have a warning? */
+ PSOUT ("* NO ");
+ PSOUT (lstwrn);
+ CRLF;
+ fs_give ((void **) &lstwrn);
+ }
+ if (!(i % 2)) { /* prevent NAT timeouts */
+ sprintf (tmp,"* OK Timeout in %lu minutes\015\012",
+ (i * IDLETIMER) / 60);
+ PSOUT (tmp);
+ }
+ /* two minutes before the end... */
+ if ((state == OPEN) && (i <= 2)) {
+ sprintf (tmp,"* %lu EXISTS\015\012* %lu RECENT\015\012",
+ donefake = nmsgs + 1,recent + 1);
+ PSOUT (tmp); /* prod client to wake up */
+ }
+ PFLUSH (); /* dump output buffer */
+ } while ((state != LOGOUT) && !INWAIT (IDLETIMER) && --i);
+
+ /* time to exit idle loop */
+ if (state != LOGOUT) {
+ if (i) { /* still have time left? */
+ /* yes, read expected DONE */
+ slurp (tmp,MAILTMPLEN,INPUTTIMEOUT);
+ if (((tmp[0] != 'D') && (tmp[0] != 'd')) ||
+ ((tmp[1] != 'O') && (tmp[1] != 'o')) ||
+ ((tmp[2] != 'N') && (tmp[2] != 'n')) ||
+ ((tmp[3] != 'E') && (tmp[3] != 'e')) ||
+ (((tmp[4] != '\015') || (tmp[5] != '\012')) &&
+ (tmp[4] != '\012')))
+ response = "%.80s BAD Bogus IDLE continuation\015\012";
+ if (donefake) { /* if faking at the end */
+ /* send EXPUNGE (should be just 1) */
+ while (donefake > nmsgs) {
+ sprintf (tmp,"* %lu EXPUNGE\015\012",donefake--);
+ PSOUT (tmp);
+ }
+ sprintf (tmp,"* %lu EXISTS\015\012* %lu RECENT\015\012",
+ nmsgs,recent);
+ PSOUT (tmp);
+ }
+ }
+ else clkint (); /* otherwise do autologout action */
+ }
+ }
+ }
+ else response = badcmd;
+ break;
+ default:
+ response = "%.80s BAD Unknown state for %.80s command\015\012";
+ break;
+ }
+
+ while (litplus.ok) { /* any unread LITERAL+? */
+ litplus.ok = NIL; /* yes, cancel it now */
+ clearerr (stdin); /* clear stdin errors */
+ status = "discarding unread literal";
+ /* read literal and discard it */
+ while (i = (litplus.size > MAILTMPLEN) ? MAILTMPLEN : litplus.size) {
+ if (state == LOGOUT) litplus.size = 0;
+ else {
+ settimeout (INPUTTIMEOUT);
+ if (PSINR (tmp,i)) litplus.size -= i;
+ else {
+ ioerror (stdin,status);
+ litplus.size = 0; /* in case it continues */
+ }
+ }
+ }
+ settimeout (0); /* stop timeout */
+ /* get new command tail */
+ slurp (tmp,MAILTMPLEN,INPUTTIMEOUT);
+ /* locate end of line */
+ if (t = strchr (tmp,'\012')) {
+ /* back over CR */
+ if ((t > tmp) && (t[-1] == '\015')) --t;
+ *t = NIL; /* tie off CRLF */
+ /* possible LITERAL+? */
+ if (((i = strlen (tmp)) > 3) && (tmp[i - 1] == '}') &&
+ (tmp[i - 2] == '+') && isdigit (tmp[i - 3])) {
+ /* back over possible count */
+ for (i -= 4; i && isdigit (tmp[i]); i--);
+ if (tmp[i] == '{') { /* found a literal? */
+ litplus.ok = T; /* yes, note LITERAL+ in effect, set size */
+ litplus.size = strtoul (tmp + i + 1,NIL,10);
+ }
+ }
+ }
+ else flush (); /* overlong line after LITERAL+, punt */
+ }
+ ping_mailbox (uid); /* update mailbox status before response */
+ if (lstwrn && lsterr) { /* output most recent warning */
+ PSOUT ("* NO ");
+ PSOUT (lstwrn);
+ CRLF;
+ fs_give ((void **) &lstwrn);
+ }
+
+ if (response == logwin) { /* authentication win message */
+ sprintf (tmp,response,lstref ? "*" : tag);
+ PSOUT (tmp); /* start response */
+ pcapability (1); /* print logged-in capabilities */
+ PSOUT ("] User ");
+ PSOUT (user);
+ PSOUT (" authenticated\015\012");
+ if (lstref) {
+ sprintf (tmp,response,tag);
+ PSOUT (tmp); /* start response */
+ PSOUT ("[REFERRAL ");
+ PSOUT (lstref);
+ PSOUT ("] ");
+ PSOUT (lasterror ());
+ CRLF;
+ }
+ }
+ else if ((response == win) || (response == lose)) {
+ sprintf (tmp,response,tag);
+ PSOUT (tmp);
+ if (cauidvalidity) { /* COPYUID/APPENDUID response? */
+ sprintf (tmp,"[%.80sUID %lu ",(char *)
+ ((s = strchr (cmd,' ')) ? s+1 : cmd),cauidvalidity);
+ PSOUT (tmp);
+ cauidvalidity = 0; /* cancel response for future */
+ if (csset) {
+ pset (&csset);
+ PBOUT (' ');
+ }
+ pset (&caset);
+ PSOUT ("] ");
+ }
+ else if (lstref) { /* have a referral? */
+ PSOUT ("[REFERRAL ");
+ PSOUT (lstref);
+ PSOUT ("] ");
+ }
+ if (lsterr || lstwrn) PSOUT (lasterror ());
+ else {
+ PSOUT (cmd);
+ PSOUT ((response == win) ? " completed" : "failed");
+ }
+ CRLF;
+ }
+ else { /* normal response */
+ if ((response == rowin) || (response == rwwin)) {
+ if (lstwrn) { /* output most recent warning */
+ PSOUT ("* NO ");
+ PSOUT (lstwrn);
+ CRLF;
+ fs_give ((void **) &lstwrn);
+ }
+ }
+ sprintf (tmp,response,tag,cmd,lasterror ());
+ PSOUT (tmp); /* output response */
+ }
+ }
+ PFLUSH (); /* make sure output blatted */
+
+ if (autologouttime) { /* have an autologout in effect? */
+ /* cancel if no longer waiting for login */
+ if (state != LOGIN) autologouttime = 0;
+ /* took too long to login */
+ else if (autologouttime < time (0)) {
+ logout = goodbye = "Autologout";
+ stream = NIL;
+ state = LOGOUT; /* sayonara */
+ }
+ }
+ }
+ if (goodbye && !quell_events){/* have a goodbye message? */
+ PSOUT ("* BYE "); /* utter it */
+ PSOUT (goodbye);
+ CRLF;
+ PFLUSH (); /* make sure blatted */
+ }
+ syslog (LOG_INFO,"%s user=%.80s host=%.80s",logout,
+ user ? (char *) user : "???",tcp_clienthost ());
+ /* do logout hook if needed */
+ if (lgoh = (logouthook_t) mail_parameters (NIL,GET_LOGOUTHOOK,NIL))
+ (*lgoh) (mail_parameters (NIL,GET_LOGOUTDATA,NIL));
+ _exit (ret); /* all done */
+ return ret; /* stupid compilers */
+}
+
+/* Ping mailbox during each cycle. Also check alerts
+ * Accepts: last command was UID flag
+ */
+
+void ping_mailbox (unsigned long uid)
+{
+ unsigned long i;
+ char tmp[MAILTMPLEN];
+ if (state == OPEN) {
+ if (!mail_ping (stream)) { /* make sure stream still alive */
+ PSOUT ("* BYE ");
+ PSOUT (mylocalhost ());
+ PSOUT (" Fatal mailbox error: ");
+ PSOUT (lasterror ());
+ CRLF;
+ stream = NIL; /* don't try to clean up stream */
+ state = LOGOUT; /* go away */
+ syslog (LOG_INFO,
+ "Fatal mailbox error user=%.80s host=%.80s mbx=%.80s: %.80s",
+ user ? (char *) user : "???",tcp_clienthost (),
+ (stream && stream->mailbox) ? stream->mailbox : "???",
+ lasterror ());
+ return;
+ }
+ /* change in number of messages? */
+ if (existsquelled || (nmsgs != stream->nmsgs)) {
+ PSOUT ("* ");
+ pnum (nmsgs = stream->nmsgs);
+ PSOUT (" EXISTS\015\012");
+ }
+ /* change in recent messages? */
+ if (existsquelled || (recent != stream->recent)) {
+ PSOUT ("* ");
+ pnum (recent = stream->recent);
+ PSOUT (" RECENT\015\012");
+ }
+ existsquelled = NIL; /* don't do this until asked again */
+ if (stream->uid_validity && (stream->uid_validity != uidvalidity)) {
+ PSOUT ("* OK [UIDVALIDITY ");
+ pnum (stream->uid_validity);
+ PSOUT ("] UID validity status\015\012* OK [UIDNEXT ");
+ pnum (stream->uid_last + 1);
+ PSOUT ("] Predicted next UID\015\012");
+ if (stream->uid_nosticky) {
+ PSOUT ("* NO [UIDNOTSTICKY] Non-permanent unique identifiers: ");
+ PSOUT (stream->mailbox);
+ CRLF;
+ }
+ uidvalidity = stream->uid_validity;
+ }
+
+ /* don't bother if driver changed */
+ if (curdriver == stream->dtb) {
+ /* first report any new flags */
+ if ((nflags < NUSERFLAGS) && stream->user_flags[nflags])
+ new_flags (stream);
+ for (i = 1; i <= nmsgs; i++) if (mail_elt (stream,i)->spare2) {
+ PSOUT ("* ");
+ pnum (i);
+ PSOUT (" FETCH (");
+ fetch_flags (i,NIL); /* output changed flags */
+ if (uid) { /* need to include UIDs in response? */
+ PBOUT (' ');
+ fetch_uid (i,NIL);
+ }
+ PSOUT (")\015\012");
+ }
+ }
+ else { /* driver changed */
+ new_flags (stream); /* send mailbox flags */
+ if (curdriver) { /* note readonly/write if possible change */
+ PSOUT ("* OK [READ-");
+ PSOUT (stream->rdonly ? "ONLY" : "WRITE");
+ PSOUT ("] Mailbox status\015\012");
+ }
+ curdriver = stream->dtb;
+ if (nmsgs) { /* get flags for all messages */
+ sprintf (tmp,"1:%lu",nmsgs);
+ mail_fetch_flags (stream,tmp,NIL);
+ /* don't do this if newsrc already did */
+ if (!(curdriver->flags & DR_NEWS)) {
+ /* find first unseen message */
+ for (i = 1; i <= nmsgs && mail_elt (stream,i)->seen; i++);
+ if (i <= nmsgs) {
+ PSOUT ("* OK [UNSEEN ");
+ pnum (i);
+ PSOUT ("] first unseen message in ");
+ PSOUT (stream->mailbox);
+ CRLF;
+ }
+ }
+ }
+ }
+ }
+ if (shutdowntime && (time (0) > shutdowntime + SHUTDOWNTIMER)) {
+ PSOUT ("* BYE Server shutting down\015\012");
+ state = LOGOUT;
+ }
+ /* don't do these stat()s every cycle */
+ else if (time (0) > alerttime + ALERTTIMER) {
+ struct stat sbuf;
+ /* have a shutdown file? */
+ if (!stat (SHUTDOWNFILE,&sbuf)) {
+ PSOUT ("* OK [ALERT] Server shutting down shortly\015\012");
+ shutdowntime = time (0);
+ }
+ alerttime = time (0); /* output any new alerts */
+ sysalerttime = palert (ALERTFILE,sysalerttime);
+ if (state != LOGIN) /* do user alert if logged in */
+ useralerttime = palert (mailboxfile (tmp,USERALERTFILE),useralerttime);
+ }
+}
+
+/* Print an alert file
+ * Accepts: path of alert file
+ * time of last printed alert file
+ * Returns: updated time of last printed alert file
+ */
+
+time_t palert (char *file,time_t oldtime)
+{
+ FILE *alf;
+ struct stat sbuf;
+ int c,lc = '\012';
+ /* have a new alert file? */
+ if (stat (file,&sbuf) || (sbuf.st_mtime <= oldtime) ||
+ !(alf = fopen (file,"r"))) return oldtime;
+ /* yes, display it */
+ while ((c = getc (alf)) != EOF) {
+ if (lc == '\012') PSOUT ("* OK [ALERT] ");
+ switch (c) { /* output character */
+ case '\012': /* newline means do CRLF */
+ CRLF;
+ case '\015': /* flush CRs */
+ case '\0': /* flush nulls */
+ break;
+ default:
+ PBOUT (c); /* output all other characters */
+ break;
+ }
+ lc = c; /* note previous character */
+ }
+ fclose (alf);
+ if (lc != '\012') CRLF; /* final terminating CRLF */
+ return sbuf.st_mtime; /* return updated last alert time */
+}
+
+/* Initialize file string structure for file stringstruct
+ * Accepts: string structure
+ * pointer to message data structure
+ * size of string
+ */
+
+void msg_string_init (STRING *s,void *data,unsigned long size)
+{
+ MSGDATA *md = (MSGDATA *) data;
+ s->data = data; /* note stream/msgno and header length */
+#if 0
+ s->size = size; /* message size */
+ s->curpos = s->chunk = /* load header */
+ mail_fetchheader_full (md->stream,md->msgno,NIL,&s->data1,
+ FT_PREFETCHTEXT | FT_PEEK);
+#else /* This kludge is necessary because of broken mail stores */
+ mail_fetchtext_full (md->stream,md->msgno,&s->size,FT_PEEK);
+ s->curpos = s->chunk = /* load header */
+ mail_fetchheader_full (md->stream,md->msgno,NIL,&s->data1,FT_PEEK);
+ s->size += s->data1; /* header + body size */
+#endif
+ s->cursize = s->chunksize = s->data1;
+ s->offset = 0; /* offset is start of message */
+}
+
+
+/* Get next character from file stringstruct
+ * Accepts: string structure
+ * Returns: character, string structure chunk refreshed
+ */
+
+char msg_string_next (STRING *s)
+{
+ char c = *s->curpos++; /* get next byte */
+ SETPOS (s,GETPOS (s)); /* move to next chunk */
+ return c; /* return the byte */
+}
+
+
+/* Set string pointer position for file stringstruct
+ * Accepts: string structure
+ * new position
+ */
+
+void msg_string_setpos (STRING *s,unsigned long i)
+{
+ MSGDATA *md = (MSGDATA *) s->data;
+ if (i < s->data1) { /* want header? */
+ s->chunk = mail_fetchheader_full (md->stream,md->msgno,NIL,NIL,FT_PEEK);
+ s->chunksize = s->data1; /* header length */
+ s->offset = 0; /* offset is start of message */
+ }
+ else if (i < s->size) { /* want body */
+ s->chunk = mail_fetchtext_full (md->stream,md->msgno,NIL,FT_PEEK);
+ s->chunksize = s->size - s->data1;
+ s->offset = s->data1; /* offset is end of header */
+ }
+ else { /* off end of message */
+ s->chunk = NIL; /* make sure that we crack on this then */
+ s->chunksize = 1; /* make sure SNX cracks the right way... */
+ s->offset = i;
+ }
+ /* initial position and size */
+ s->curpos = s->chunk + (i -= s->offset);
+ s->cursize = s->chunksize - i;
+}
+
+/* Send flags for stream
+ * Accepts: MAIL stream
+ * scratch buffer
+ */
+
+void new_flags (MAILSTREAM *stream)
+{
+ int i,c;
+ PSOUT ("* FLAGS (");
+ for (i = 0; i < NUSERFLAGS; i++) if (stream->user_flags[i]) {
+ PSOUT (stream->user_flags[i]);
+ PBOUT (' ');
+ nflags = i + 1;
+ }
+ PSOUT ("\\Answered \\Flagged \\Deleted \\Draft \\Seen)\015\012* OK [PERMANENTFLAGS (");
+ for (i = c = 0; i < NUSERFLAGS; i++)
+ if ((stream->perm_user_flags & (1 << i)) && stream->user_flags[i])
+ put_flag (&c,stream->user_flags[i]);
+ if (stream->kwd_create) put_flag (&c,"\\*");
+ if (stream->perm_answered) put_flag (&c,"\\Answered");
+ if (stream->perm_flagged) put_flag (&c,"\\Flagged");
+ if (stream->perm_deleted) put_flag (&c,"\\Deleted");
+ if (stream->perm_draft) put_flag (&c,"\\Draft");
+ if (stream->perm_seen) put_flag (&c,"\\Seen");
+ PSOUT (")] Permanent flags\015\012");
+}
+
+/* Set timeout
+ * Accepts: desired interval
+ */
+
+void settimeout (unsigned int i)
+{
+ /* limit if not logged in */
+ if (i) alarm ((state == LOGIN) ? LOGINTIMEOUT : i);
+ else alarm (0);
+}
+
+
+/* Clock interrupt
+ * Returns only if critical code in progress
+ */
+
+void clkint (void)
+{
+ settimeout (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ logout = "Autologout";
+ goodbye = "Autologout (idle for too long)";
+ if (critical) { /* must defer if in critical code(?) */
+ close (0); /* kill stdin */
+ state = LOGOUT; /* die as soon as we can */
+ }
+ else longjmp (jmpenv,1); /* die now */
+}
+
+
+/* Kiss Of Death interrupt
+ * Returns only if critical code in progress
+ */
+
+void kodint (void)
+{
+ settimeout (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ logout = goodbye = "Killed (lost mailbox lock)";
+ if (critical) { /* must defer if in critical code */
+ close (0); /* kill stdin */
+ state = LOGOUT; /* die as soon as we can */
+ }
+ else longjmp (jmpenv,1); /* die now */
+}
+
+/* Hangup interrupt
+ * Returns only if critical code in progress
+ */
+
+void hupint (void)
+{
+ settimeout (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ logout = "Hangup";
+ goodbye = NIL; /* other end is already gone */
+ if (critical) { /* must defer if in critical code */
+ close (0); /* kill stdin */
+ close (1); /* and stdout */
+ state = LOGOUT; /* die as soon as we can */
+ }
+ else longjmp (jmpenv,1); /* die now */
+}
+
+
+/* Termination interrupt
+ * Returns only if critical code in progress
+ */
+
+void trmint (void)
+{
+ settimeout (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ logout = goodbye = "Killed (terminated)";
+ /* Make no attempt at graceful closure since a shutdown may be in
+ * progress, and we won't have any time to do mail_close() actions
+ */
+ stream = NIL;
+ if (critical) { /* must defer if in critical code */
+ close (0); /* kill stdin */
+ close (1); /* and stdout */
+ state = LOGOUT; /* die as soon as we can */
+ }
+ else longjmp (jmpenv,1); /* die now */
+}
+
+/* The routines on this and the next page eschew the use of non-syscall libc
+ * routines (especially stdio) for a reason. Also, these hideous #if
+ * condtionals need to be replaced.
+ */
+
+#ifndef unix
+#define unix 0
+#endif
+
+
+/* Status request interrupt
+ * Always returns
+ */
+
+void staint (void)
+{
+#if unix
+ int fd;
+ char *s,buf[8*MAILTMPLEN];
+ unsigned long pid = getpid ();
+ /* build file name */
+ s = nout (sout (buf,"/tmp/imapd-status."),pid,10);
+ if (user) s = sout (sout (s,"."),user);
+ *s = '\0'; /* tie off file name */
+ if ((fd = open (buf,O_WRONLY | O_CREAT | O_TRUNC,0666)) >= 0) {
+ fchmod (fd,0666);
+ s = nout (sout (buf,"PID="),pid,10);
+ if (user) s = sout (sout (s,", user="),user);
+ switch (state) {
+ case LOGIN:
+ s = sout (s,", not logged in");
+ break;
+ case SELECT:
+ s = sout (s,", logged in");
+ break;
+ case OPEN:
+ s = sout (s,", mailbox open");
+ break;
+ case LOGOUT:
+ s = sout (s,", logging out");
+ break;
+ }
+ if (stream && stream->mailbox)
+ s = sout (sout (s,"\nmailbox="),stream->mailbox);
+ *s++ = '\n';
+ if (status) {
+ s = sout (s,status);
+ if (cmd) s = sout (sout (s,", last command="),cmd);
+ }
+ else s = sout (sout (s,cmd)," in progress");
+ *s++ = '\n';
+ write (fd,buf,s-buf);
+ close (fd);
+ }
+#endif
+}
+
+/* Write string
+ * Accepts: destination string pointer
+ * string
+ * Returns: updated string pointer
+ */
+
+char *sout (char *s,char *t)
+{
+ while (*t) *s++ = *t++;
+ return s;
+}
+
+
+/* Write number
+ * Accepts: destination string pointer
+ * number
+ * base
+ * Returns: updated string pointer
+ */
+
+char *nout (char *s,unsigned long n,unsigned long base)
+{
+ char stack[256];
+ char *t = stack;
+ /* push PID digits on stack */
+ do *t++ = (char) (n % base) + '0';
+ while (n /= base);
+ /* pop digits from stack */
+ while (t > stack) *s++ = *--t;
+ return s;
+}
+
+/* Slurp a command line
+ * Accepts: buffer pointer
+ * buffer size
+ * input timeout
+ */
+
+void slurp (char *s,int n,unsigned long timeout)
+{
+ memset (s,'\0',n); /* zap buffer */
+ if (state != LOGOUT) { /* get a command under timeout */
+ settimeout (timeout);
+ clearerr (stdin); /* clear stdin errors */
+ status = "reading line";
+ if (!PSIN (s,n-1)) ioerror (stdin,status);
+ settimeout (0); /* make sure timeout disabled */
+ status = NIL;
+ }
+}
+
+
+/* Read a literal
+ * Accepts: destination buffer (must be size+1 for trailing NUL)
+ * size of buffer (must be less than 4294967295)
+ */
+
+void inliteral (char *s,unsigned long n)
+{
+ unsigned long i;
+ if (litplus.ok) { /* no more LITERAL+ to worry about */
+ litplus.ok = NIL;
+ litplus.size = 0;
+ }
+ else { /* otherwise tell client ready for argument */
+ PSOUT ("+ Ready for argument\015\012");
+ PFLUSH (); /* dump output buffer */
+ }
+ clearerr (stdin); /* clear stdin errors */
+ memset (s,'\0',n+1); /* zap buffer */
+ status = "reading literal";
+ while (n) { /* get data under timeout */
+ if (state == LOGOUT) n = 0;
+ else {
+ settimeout (INPUTTIMEOUT);
+ i = min (n,8192); /* must read at least 8K within timeout */
+ if (PSINR (s,i)) {
+ s += i;
+ n -= i;
+ }
+ else {
+ ioerror (stdin,status);
+ n = 0; /* in case it continues */
+ }
+ settimeout (0); /* stop timeout */
+ }
+ }
+}
+
+/* Flush until newline seen
+ * Returns: NIL, always
+ */
+
+unsigned char *flush (void)
+{
+ int c;
+ if (state != LOGOUT) {
+ settimeout (INPUTTIMEOUT);
+ clearerr (stdin); /* clear stdin errors */
+ status = "flushing line";
+ while ((c = PBIN ()) != '\012') if (c == EOF) ioerror (stdin,status);
+ settimeout (0); /* make sure timeout disabled */
+ }
+ response = "%.80s BAD Command line too long\015\012";
+ status = NIL;
+ return NIL;
+}
+
+
+/* Report command stream error and die
+ * Accepts: stdin or stdout (whichever got the error)
+ * reason (what caller was doing)
+ */
+
+void ioerror (FILE *f,char *reason)
+{
+ static char msg[MAILTMPLEN];
+ char *s,*t;
+ if (logout) { /* say nothing if already dying */
+ settimeout (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ /* write error string */
+ for (s = ferror (f) ? strerror (errno) : "Unexpected client disconnect",
+ t = logout = msg; *s; *t++ = *s++);
+ for (s = ", while "; *s; *t++ = *s++);
+ for (s = reason; *s; *t++ = *s++);
+ if (critical) { /* must defer if in critical code */
+ close (0); /* kill stdin */
+ close (1); /* and stdout */
+ state = LOGOUT; /* die as soon as we can */
+ }
+ else longjmp (jmpenv,1); /* die now */
+ }
+}
+
+/* Parse an IMAP astring
+ * Accepts: pointer to argument text pointer
+ * pointer to returned size
+ * pointer to returned delimiter
+ * Returns: argument
+ */
+
+unsigned char *parse_astring (unsigned char **arg,unsigned long *size,
+ unsigned char *del)
+{
+ unsigned long i;
+ unsigned char c,*s,*t,*v;
+ if (!*arg) return NIL; /* better be an argument */
+ switch (**arg) { /* see what the argument is */
+ default: /* atom */
+ for (s = t = *arg, i = 0;
+ (*t > ' ') && (*t < 0x7f) && (*t != '(') && (*t != ')') &&
+ (*t != '{') && (*t != '%') && (*t != '*') && (*t != '"') &&
+ (*t != '\\'); ++t,++i);
+ if (*size = i) break; /* got atom if non-empty */
+ case ')': case '%': case '*': case '\\': case '\0': case ' ':
+ return NIL; /* empty atom is a bogon */
+ case '"': /* hunt for trailing quote */
+ for (s = t = v = *arg + 1; (c = *t++) != '"'; *v++ = c) {
+ /* quote next character */
+ if (c == '\\') switch (c = *t++) {
+ case '"': case '\\': break;
+ default: return NIL; /* invalid quote-next */
+ }
+ /* else must be a CHAR */
+ if (!c || (c & 0x80)) return NIL;
+ }
+ *v = '\0'; /* tie off string */
+ *size = v - s; /* return size */
+ break;
+
+ case '{': /* literal string */
+ s = *arg + 1; /* get size */
+ if (!isdigit (*s)) return NIL;
+ if ((*size = i = strtoul (s,(char **) &t,10)) > MAXCLIENTLIT) {
+ mm_notify (NIL,"Absurdly long client literal",ERROR);
+ syslog (LOG_INFO,"Overlong (%lu) client literal user=%.80s host=%.80s",
+ i,user ? (char *) user : "???",tcp_clienthost ());
+ return NIL;
+ }
+ switch (*t) { /* validate end of literal */
+ case '+': /* non-blocking literal */
+ if (*++t != '}') return NIL;
+ case '}':
+ if (!t[1]) break; /* OK if end of line */
+ default:
+ return NIL; /* bad literal */
+ }
+ if (litsp >= LITSTKLEN) { /* make sure don't overflow stack */
+ mm_notify (NIL,"Too many literals in command",ERROR);
+ return NIL;
+ }
+ /* get a literal buffer */
+ inliteral (s = litstk[litsp++] = (char *) fs_get (i+1),i);
+ /* get new command tail */
+ slurp (*arg = t,CMDLEN - (t - cmdbuf),INPUTTIMEOUT);
+ if (!strchr (t,'\012')) return flush ();
+ /* reset strtok mechanism, tie off if done */
+ if (!strtok (t,"\015\012")) *t = '\0';
+ /* possible LITERAL+? */
+ if (((i = strlen (t)) > 3) && (t[i - 1] == '}') &&
+ (t[i - 2] == '+') && isdigit (t[i - 3])) {
+ /* back over possible count */
+ for (i -= 4; i && isdigit (t[i]); i--);
+ if (t[i] == '{') { /* found a literal? */
+ litplus.ok = T; /* yes, note LITERAL+ in effect, set size */
+ litplus.size = strtoul (t + i + 1,NIL,10);
+ }
+ }
+ break;
+ }
+ if (*del = *t) { /* have a delimiter? */
+ *t++ = '\0'; /* yes, stomp on it */
+ *arg = t; /* update argument pointer */
+ }
+ else *arg = NIL; /* no more arguments */
+ return s;
+}
+
+/* Snarf a command argument (simple jacket into parse_astring())
+ * Accepts: pointer to argument text pointer
+ * Returns: argument
+ */
+
+unsigned char *snarf (unsigned char **arg)
+{
+ unsigned long i;
+ unsigned char c;
+ unsigned char *s = parse_astring (arg,&i,&c);
+ return ((c == ' ') || !c) ? s : NIL;
+}
+
+
+/* Snarf a BASE64 argument for SASL-IR
+ * Accepts: pointer to argument text pointer
+ * Returns: argument
+ */
+
+unsigned char *snarf_base64 (unsigned char **arg)
+{
+ unsigned char *ret = *arg;
+ unsigned char *s = ret + 1;
+ static char base64mask[256] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,
+ 0,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,0,0,0,0,0,
+ 0,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,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+ };
+ if (*(ret = *arg) == '='); /* easy case if zero-length argument */
+ /* must be at least one BASE64 char */
+ else if (!base64mask[*ret]) return NIL;
+ else { /* quick and dirty */
+ while (base64mask[*s++]); /* scan until end of BASE64 */
+ if (*s == '=') ++s; /* allow up to two padding chars */
+ if (*s == '=') ++s;
+ }
+ switch (*s) { /* anything following the argument? */
+ case ' ': /* another argument */
+ *s++ = '\0'; /* tie off previous argument */
+ *arg = s; /* and update argument pointer */
+ break;
+ case '\0': /* end of command */
+ *arg = NIL;
+ break;
+ default: /* syntax error */
+ return NIL;
+ }
+ return ret; /* return BASE64 string */
+}
+
+/* Snarf a list command argument (simple jacket into parse_astring())
+ * Accepts: pointer to argument text pointer
+ * Returns: argument
+ */
+
+unsigned char *snarf_list (unsigned char **arg)
+{
+ unsigned long i;
+ unsigned char c,*s,*t;
+ if (!*arg) return NIL; /* better be an argument */
+ switch (**arg) {
+ default: /* atom and/or wildcard chars */
+ for (s = t = *arg, i = 0;
+ (*t > ' ') && (*t != '(') && (*t != ')') && (*t != '{') &&
+ (*t != '"') && (*t != '\\'); ++t,++i);
+ if (c = *t) { /* have a delimiter? */
+ *t++ = '\0'; /* stomp on it */
+ *arg = t; /* update argument pointer */
+ }
+ else *arg = NIL;
+ break;
+ case ')': case '\\': case '\0': case ' ':
+ return NIL; /* empty name is bogus */
+ case '"': /* quoted string? */
+ case '{': /* or literal? */
+ s = parse_astring (arg,&i,&c);
+ break;
+ }
+ return ((c == ' ') || !c) ? s : NIL;
+}
+
+/* Get a list of header lines
+ * Accepts: pointer to string pointer
+ * pointer to list flag
+ * Returns: string list
+ */
+
+STRINGLIST *parse_stringlist (unsigned char **s,int *list)
+{
+ char c = ' ',*t;
+ unsigned long i;
+ STRINGLIST *ret = NIL,*cur = NIL;
+ if (*s && **s == '(') { /* proper list? */
+ ++*s; /* for each item in list */
+ while ((c == ' ') && (t = parse_astring (s,&i,&c))) {
+ /* get new block */
+ if (cur) cur = cur->next = mail_newstringlist ();
+ else cur = ret = mail_newstringlist ();
+ /* note text */
+ cur->text.data = (unsigned char *) fs_get (i + 1);
+ memcpy (cur->text.data,t,i);
+ cur->text.size = i; /* and size */
+ }
+ /* must be end of list */
+ if (c != ')') mail_free_stringlist (&ret);
+ }
+ if (t = *s) { /* need to reload strtok() state? */
+ /* end of a list? */
+ if (*list && (*t == ')') && !t[1]) *list = NIL;
+ else {
+ *--t = ' '; /* patch a space back in */
+ *--t = 'x'; /* and a hokey character before that */
+ t = strtok (t," "); /* reset to *s */
+ }
+ }
+ return ret;
+}
+
+/* Get value of UID * for criteria parsing
+ * Accepts: stream
+ * Returns: maximum UID
+ */
+
+unsigned long uidmax (MAILSTREAM *stream)
+{
+ return stream->nmsgs ? mail_uid (stream,stream->nmsgs) : 0xffffffff;
+}
+
+
+/* Parse search criteria
+ * Accepts: search program to write criteria into
+ * pointer to argument text pointer
+ * maximum message number
+ * maximum UID
+ * logical nesting depth
+ * Returns: T if success, NIL if error
+ */
+
+long parse_criteria (SEARCHPGM *pgm,unsigned char **arg,unsigned long maxmsg,
+ unsigned long maxuid,unsigned long depth)
+{
+ if (arg && *arg) { /* must be an argument */
+ /* parse criteria */
+ do if (!parse_criterion (pgm,arg,maxmsg,maxuid,depth)) return NIL;
+ /* as long as a space delimiter */
+ while (**arg == ' ' && (*arg)++);
+ /* failed if not end of criteria */
+ if (**arg && **arg != ')') return NIL;
+ }
+ return T; /* success */
+}
+
+/* Parse a search criterion
+ * Accepts: search program to write criterion into
+ * pointer to argument text pointer
+ * maximum message number
+ * maximum UID
+ * logical nesting depth
+ * Returns: T if success, NIL if error
+ */
+
+long parse_criterion (SEARCHPGM *pgm,unsigned char **arg,unsigned long maxmsg,
+ unsigned long maxuid,unsigned long depth)
+{
+ unsigned long i;
+ unsigned char c = NIL,*s,*t,*v,*tail,*del;
+ SEARCHSET **set;
+ SEARCHPGMLIST **not;
+ SEARCHOR **or;
+ SEARCHHEADER **hdr;
+ long ret = NIL;
+ /* better be an argument */
+ if ((depth > 500) || !(arg && *arg));
+ else if (**arg == '(') { /* list of criteria? */
+ (*arg)++; /* yes, parse the criteria */
+ if (parse_criteria (pgm,arg,maxmsg,maxuid,depth+1) && **arg == ')') {
+ (*arg)++; /* skip closing paren */
+ ret = T; /* successful parse of list */
+ }
+ }
+ else { /* find end of criterion */
+ if (!(tail = strpbrk ((s = *arg)," )"))) tail = *arg + strlen (*arg);
+ c = *(del = tail); /* remember the delimiter */
+ *del = '\0'; /* tie off criterion */
+ switch (*ucase (s)) { /* dispatch based on character */
+ case '*': /* sequence */
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if (*(set = &pgm->msgno)){/* already a sequence? */
+ /* silly, but not as silly as the client! */
+ for (not = &pgm->not; *not; not = &(*not)->next);
+ *not = mail_newsearchpgmlist ();
+ set = &((*not)->pgm->not = mail_newsearchpgmlist ())->pgm->msgno;
+ }
+ ret = crit_set (set,&s,maxmsg) && (tail == s);
+ break;
+ case 'A': /* possible ALL, ANSWERED */
+ if (!strcmp (s+1,"LL")) ret = T;
+ else if (!strcmp (s+1,"NSWERED")) ret = pgm->answered = T;
+ break;
+
+ case 'B': /* possible BCC, BEFORE, BODY */
+ if (!strcmp (s+1,"CC") && c == ' ' && *++tail)
+ ret = crit_string (&pgm->bcc,&tail);
+ else if (!strcmp (s+1,"EFORE") && c == ' ' && *++tail)
+ ret = crit_date (&pgm->before,&tail);
+ else if (!strcmp (s+1,"ODY") && c == ' ' && *++tail)
+ ret = crit_string (&pgm->body,&tail);
+ break;
+ case 'C': /* possible CC */
+ if (!strcmp (s+1,"C") && c == ' ' && *++tail)
+ ret = crit_string (&pgm->cc,&tail);
+ break;
+ case 'D': /* possible DELETED */
+ if (!strcmp (s+1,"ELETED")) ret = pgm->deleted = T;
+ if (!strcmp (s+1,"RAFT")) ret = pgm->draft = T;
+ break;
+ case 'F': /* possible FLAGGED, FROM */
+ if (!strcmp (s+1,"LAGGED")) ret = pgm->flagged = T;
+ else if (!strcmp (s+1,"ROM") && c == ' ' && *++tail)
+ ret = crit_string (&pgm->from,&tail);
+ break;
+ case 'H': /* possible HEADER */
+ if (!strcmp (s+1,"EADER") && c == ' ' && *(v = tail + 1) &&
+ (s = parse_astring (&v,&i,&c)) && i && c == ' ' &&
+ (t = parse_astring (&v,&i,&c))) {
+ for (hdr = &pgm->header; *hdr; hdr = &(*hdr)->next);
+ *hdr = mail_newsearchheader (s,t);
+ /* update tail, restore delimiter */
+ *(tail = v ? v - 1 : t + i) = c;
+ ret = T; /* success */
+ }
+ break;
+ case 'K': /* possible KEYWORD */
+ if (!strcmp (s+1,"EYWORD") && c == ' ' && *++tail)
+ ret = crit_string (&pgm->keyword,&tail);
+ break;
+ case 'L':
+ if (!strcmp (s+1,"ARGER") && c == ' ' && *++tail)
+ ret = crit_number (&pgm->larger,&tail);
+ break;
+ case 'N': /* possible NEW, NOT */
+ if (!strcmp (s+1,"EW")) ret = pgm->recent = pgm->unseen = T;
+ else if (!strcmp (s+1,"OT") && c == ' ' && *++tail) {
+ for (not = &pgm->not; *not; not = &(*not)->next);
+ *not = mail_newsearchpgmlist ();
+ ret = parse_criterion ((*not)->pgm,&tail,maxmsg,maxuid,depth+1);
+ }
+ break;
+
+ case 'O': /* possible OLD, ON */
+ if (!strcmp (s+1,"LD")) ret = pgm->old = T;
+ else if (!strcmp (s+1,"N") && c == ' ' && *++tail)
+ ret = crit_date (&pgm->on,&tail);
+ else if (!strcmp (s+1,"R") && c == ' ') {
+ for (or = &pgm->or; *or; or = &(*or)->next);
+ *or = mail_newsearchor ();
+ ret = *++tail && parse_criterion((*or)->first,&tail,maxmsg,maxuid,
+ depth+1) &&
+ (*tail == ' ') && *++tail &&
+ parse_criterion ((*or)->second,&tail,maxmsg,maxuid,depth+1);
+ }
+ else if (!strcmp (s+1,"LDER") && c == ' ' && *++tail)
+ ret = crit_number (&pgm->older,&tail);
+ break;
+ case 'R': /* possible RECENT */
+ if (!strcmp (s+1,"ECENT")) ret = pgm->recent = T;
+ break;
+ case 'S': /* possible SEEN, SINCE, SUBJECT */
+ if (!strcmp (s+1,"EEN")) ret = pgm->seen = T;
+ else if (!strcmp (s+1,"ENTBEFORE") && c == ' ' && *++tail)
+ ret = crit_date (&pgm->sentbefore,&tail);
+ else if (!strcmp (s+1,"ENTON") && c == ' ' && *++tail)
+ ret = crit_date (&pgm->senton,&tail);
+ else if (!strcmp (s+1,"ENTSINCE") && c == ' ' && *++tail)
+ ret = crit_date (&pgm->sentsince,&tail);
+ else if (!strcmp (s+1,"INCE") && c == ' ' && *++tail)
+ ret = crit_date (&pgm->since,&tail);
+ else if (!strcmp (s+1,"MALLER") && c == ' ' && *++tail)
+ ret = crit_number (&pgm->smaller,&tail);
+ else if (!strcmp (s+1,"UBJECT") && c == ' ' && *++tail)
+ ret = crit_string (&pgm->subject,&tail);
+ break;
+ case 'T': /* possible TEXT, TO */
+ if (!strcmp (s+1,"EXT") && c == ' ' && *++tail)
+ ret = crit_string (&pgm->text,&tail);
+ else if (!strcmp (s+1,"O") && c == ' ' && *++tail)
+ ret = crit_string (&pgm->to,&tail);
+ break;
+
+ case 'U': /* possible UID, UN* */
+ if (!strcmp (s+1,"ID") && c== ' ' && *++tail) {
+ if (*(set = &pgm->uid)){/* already a sequence? */
+ /* silly, but not as silly as the client! */
+ for (not = &pgm->not; *not; not = &(*not)->next);
+ *not = mail_newsearchpgmlist ();
+ set = &((*not)->pgm->not = mail_newsearchpgmlist ())->pgm->uid;
+ }
+ ret = crit_set (set,&tail,maxuid);
+ }
+ else if (!strcmp (s+1,"NANSWERED")) ret = pgm->unanswered = T;
+ else if (!strcmp (s+1,"NDELETED")) ret = pgm->undeleted = T;
+ else if (!strcmp (s+1,"NDRAFT")) ret = pgm->undraft = T;
+ else if (!strcmp (s+1,"NFLAGGED")) ret = pgm->unflagged = T;
+ else if (!strcmp (s+1,"NKEYWORD") && c == ' ' && *++tail)
+ ret = crit_string (&pgm->unkeyword,&tail);
+ else if (!strcmp (s+1,"NSEEN")) ret = pgm->unseen = T;
+ break;
+ case 'Y': /* possible YOUNGER */
+ if (!strcmp (s+1,"OUNGER") && c == ' ' && *++tail)
+ ret = crit_number (&pgm->younger,&tail);
+ break;
+ default: /* oh dear */
+ break;
+ }
+ if (ret) { /* only bother if success */
+ *del = c; /* restore delimiter */
+ *arg = tail; /* update argument pointer */
+ }
+ }
+ return ret; /* return more to come */
+}
+
+/* Parse a search date criterion
+ * Accepts: date to write into
+ * pointer to argument text pointer
+ * Returns: T if success, NIL if error
+ */
+
+long crit_date (unsigned short *date,unsigned char **arg)
+{
+ if (*date) return NIL; /* can't double this value */
+ /* handle quoted form */
+ if (**arg != '"') return crit_date_work (date,arg);
+ (*arg)++; /* skip past opening quote */
+ if (!(crit_date_work (date,arg) && (**arg == '"'))) return NIL;
+ (*arg)++; /* skip closing quote */
+ return T;
+}
+
+/* Worker routine to parse a search date criterion
+ * Accepts: date to write into
+ * pointer to argument text pointer
+ * Returns: T if success, NIL if error
+ */
+
+long crit_date_work (unsigned short *date,unsigned char **arg)
+{
+ int d,m,y;
+ /* day */
+ if (isdigit (d = *(*arg)++) || ((d == ' ') && isdigit (**arg))) {
+ if (d == ' ') d = 0; /* leading space */
+ else d -= '0'; /* first digit */
+ if (isdigit (**arg)) { /* if a second digit */
+ d *= 10; /* slide over first digit */
+ d += *(*arg)++ - '0'; /* second digit */
+ }
+ if ((**arg == '-') && (y = *++(*arg))) {
+ m = (y >= 'a' ? y - 'a' : y - 'A') * 1024;
+ if ((y = *++(*arg))) {
+ m += (y >= 'a' ? y - 'a' : y - 'A') * 32;
+ if ((y = *++(*arg))) {
+ m += (y >= 'a' ? y - 'a' : y - 'A');
+ switch (m) { /* determine the month */
+ case (('J'-'A') * 1024) + (('A'-'A') * 32) + ('N'-'A'): m = 1; break;
+ case (('F'-'A') * 1024) + (('E'-'A') * 32) + ('B'-'A'): m = 2; break;
+ case (('M'-'A') * 1024) + (('A'-'A') * 32) + ('R'-'A'): m = 3; break;
+ case (('A'-'A') * 1024) + (('P'-'A') * 32) + ('R'-'A'): m = 4; break;
+ case (('M'-'A') * 1024) + (('A'-'A') * 32) + ('Y'-'A'): m = 5; break;
+ case (('J'-'A') * 1024) + (('U'-'A') * 32) + ('N'-'A'): m = 6; break;
+ case (('J'-'A') * 1024) + (('U'-'A') * 32) + ('L'-'A'): m = 7; break;
+ case (('A'-'A') * 1024) + (('U'-'A') * 32) + ('G'-'A'): m = 8; break;
+ case (('S'-'A') * 1024) + (('E'-'A') * 32) + ('P'-'A'): m = 9; break;
+ case (('O'-'A') * 1024) + (('C'-'A') * 32) + ('T'-'A'): m = 10;break;
+ case (('N'-'A') * 1024) + (('O'-'A') * 32) + ('V'-'A'): m = 11;break;
+ case (('D'-'A') * 1024) + (('E'-'A') * 32) + ('C'-'A'): m = 12;break;
+ default: return NIL;
+ }
+ if ((*++(*arg) == '-') && isdigit (*++(*arg))) {
+ y = 0; /* init year */
+ do {
+ y *= 10; /* add this number */
+ y += *(*arg)++ - '0';
+ }
+ while (isdigit (**arg));
+ /* minimal validity check of date */
+ if (d < 1 || d > 31 || m < 1 || m > 12 || y < 0) return NIL;
+ /* time began on UNIX in 1970 */
+ if (y < 100) y += (y >= (BASEYEAR - 1900)) ? 1900 : 2000;
+ /* return value */
+ *date = mail_shortdate (y - BASEYEAR,m,d);
+ return T; /* success */
+ }
+ }
+ }
+ }
+ }
+ return NIL; /* else error */
+}
+
+/* Parse a search set criterion
+ * Accepts: set to write into
+ * pointer to argument text pointer
+ * maximum value permitted
+ * Returns: T if success, NIL if error
+ */
+
+long crit_set (SEARCHSET **set,unsigned char **arg,unsigned long maxima)
+{
+ unsigned long i = 0;
+ if (*set) return NIL; /* can't double this value */
+ *set = mail_newsearchset (); /* instantiate a new search set */
+ if (**arg == '*') { /* maxnum? */
+ (*arg)++; /* skip past that number */
+ (*set)->first = maxima;
+ }
+ else if (crit_number (&i,arg) && i) (*set)->first = i;
+ else return NIL; /* bogon */
+ switch (**arg) { /* decide based on delimiter */
+ case ':': /* sequence range */
+ i = 0; /* reset for crit_number() */
+ if (*++(*arg) == '*') { /* maxnum? */
+ (*arg)++; /* skip past that number */
+ (*set)->last = maxima;
+ }
+ else if (crit_number (&i,arg) && i) {
+ if (i < (*set)->first) { /* backwards range */
+ (*set)->last = (*set)->first;
+ (*set)->first = i;
+ }
+ else (*set)->last = i; /* set last number */
+ }
+ else return NIL; /* bogon */
+ if (**arg != ',') break; /* drop into comma case if comma seen */
+ case ',':
+ (*arg)++; /* skip past delimiter */
+ return crit_set (&(*set)->next,arg,maxima);
+ default:
+ break;
+ }
+ return T; /* return success */
+}
+
+/* Parse a search number criterion
+ * Accepts: number to write into
+ * pointer to argument text pointer
+ * Returns: T if success, NIL if error
+ */
+
+long crit_number (unsigned long *number,unsigned char **arg)
+{
+ /* can't double this value */
+ if (*number || !isdigit (**arg)) return NIL;
+ *number = 0;
+ while (isdigit (**arg)) { /* found a digit? */
+ *number *= 10; /* add a decade */
+ *number += *(*arg)++ - '0'; /* add number */
+ }
+ return T;
+}
+
+
+/* Parse a search string criterion
+ * Accepts: date to write into
+ * pointer to argument text pointer
+ * Returns: T if success, NIL if error
+ */
+
+long crit_string (STRINGLIST **string,unsigned char **arg)
+{
+ unsigned long i;
+ char c;
+ char *s = parse_astring (arg,&i,&c);
+ if (!s) return NIL;
+ /* find tail of list */
+ while (*string) string = &(*string)->next;
+ *string = mail_newstringlist ();
+ (*string)->text.data = (unsigned char *) fs_get (i + 1);
+ memcpy ((*string)->text.data,s,i);
+ (*string)->text.data[i] = '\0';
+ (*string)->text.size = i;
+ /* if end of arguments, wrap it up here */
+ if (!*arg) *arg = (char *) (*string)->text.data + i;
+ else (*--(*arg) = c); /* back up pointer, restore delimiter */
+ return T;
+}
+
+/* Fetch message data
+ * Accepts: string of data items to be fetched (must be writeable)
+ * UID fetch flag
+ */
+
+#define MAXFETCH 100
+
+void fetch (char *t,unsigned long uid)
+{
+ fetchfn_t f[MAXFETCH +2];
+ void *fa[MAXFETCH + 2];
+ int k;
+ memset ((void *) f,NIL,sizeof (f));
+ memset ((void *) fa,NIL,sizeof (fa));
+ fetch_work (t,uid,f,fa); /* do the work */
+ /* clean up arguments */
+ for (k = 1; f[k]; k++) if (fa[k]) (*f[k]) (0,fa[k]);
+}
+
+
+/* Fetch message data worker routine
+ * Accepts: string of data items to be fetched (must be writeable)
+ * UID fetch flag
+ * function dispatch vector
+ * function argument vector
+ */
+
+void fetch_work (char *t,unsigned long uid,fetchfn_t f[],void *fa[])
+{
+ unsigned char *s,*v;
+ unsigned long i;
+ unsigned long k = 0;
+ BODY *b;
+ int list = NIL;
+ int parse_envs = NIL;
+ int parse_bodies = NIL;
+ if (uid) { /* need to fetch UIDs? */
+ fa[k] = NIL; /* no argument */
+ f[k++] = fetch_uid; /* push a UID fetch on the stack */
+ }
+
+ /* process macros */
+ if (!strcmp (ucase (t),"ALL"))
+ strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE ENVELOPE)");
+ else if (!strcmp (t,"FULL"))
+ strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY)");
+ else if (!strcmp (t,"FAST")) strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE)");
+ if (list = (*t == '(')) t++; /* skip open paren */
+ if (s = strtok (t," ")) do { /* parse attribute list */
+ if (list && (i = strlen (s)) && (s[i-1] == ')')) {
+ list = NIL; /* done with list */
+ s[i-1] = '\0'; /* tie off last item */
+ }
+ fa[k] = NIL; /* default to no argument */
+ if (!strcmp (s,"UID")) { /* no-op if implicit */
+ if (!uid) f[k++] = fetch_uid;
+ }
+ else if (!strcmp (s,"FLAGS")) f[k++] = fetch_flags;
+ else if (!strcmp (s,"INTERNALDATE")) f[k++] = fetch_internaldate;
+ else if (!strcmp (s,"RFC822.SIZE")) f[k++] = fetch_rfc822_size;
+ else if (!strcmp (s,"ENVELOPE")) {
+ parse_envs = T; /* we will need to parse envelopes */
+ f[k++] = fetch_envelope;
+ }
+ else if (!strcmp (s,"BODY")) {
+ parse_envs = parse_bodies = T;
+ f[k++] = fetch_body;
+ }
+ else if (!strcmp (s,"BODYSTRUCTURE")) {
+ parse_envs = parse_bodies = T;
+ f[k++] = fetch_bodystructure;
+ }
+ else if (!strcmp (s,"RFC822")) {
+ fa[k] = s[6] ? (void *) FT_PEEK : NIL;
+ f[k++] = fetch_rfc822;
+ }
+ else if (!strcmp (s,"RFC822.HEADER")) f[k++] = fetch_rfc822_header;
+ else if (!strcmp (s,"RFC822.TEXT")) {
+ fa[k] = s[11] ? (void *) FT_PEEK : NIL;
+ f[k++] = fetch_rfc822_text;
+ }
+
+ else if (!strncmp (s,"BODY[",5) || !strncmp (s,"BODY.PEEK[",10) ||
+ !strncmp (s,"BINARY[",7) || !strncmp (s,"BINARY.PEEK[",12) ||
+ !strncmp (s,"BINARY.SIZE[",12)) {
+ TEXTARGS *ta = (TEXTARGS *)
+ memset (fs_get (sizeof (TEXTARGS)),0,sizeof (TEXTARGS));
+ if (s[1] == 'I') { /* body or binary? */
+ ta->binary = FTB_BINARY;/* binary */
+ f[k] = fetch_body_part_binary;
+ if (s[6] == '.') { /* wanted peek or size? */
+ if (s[7] == 'P') ta->flags = FT_PEEK;
+ else ta->binary |= FTB_SIZE;
+ s += 12; /* skip to section specifier */
+ }
+ else s += 7; /* skip to section specifier */
+ if (!isdigit (*s)) { /* make sure top-level digit */
+ fs_give ((void **) &ta);
+ response = badbin;
+ return;
+ }
+ }
+ else { /* body */
+ f[k] = fetch_body_part_contents;
+ if (s[4] == '.') { /* wanted peek? */
+ ta->flags = FT_PEEK;
+ s += 10; /* skip to section specifier */
+ }
+ else s += 5; /* skip to section specifier */
+ }
+ if (*(v = s) != ']') { /* non-empty section specifier? */
+ if (isdigit (*v)) { /* have section specifier? */
+ /* need envelopes and bodies */
+ parse_envs = parse_bodies = T;
+ while (isdigit (*v)) /* scan to end of section specifier */
+ if ((*++v == '.') && isdigit (v[1])) v++;
+ /* any IMAP4rev1 stuff following? */
+ if ((*v == '.') && isalpha (v[1])) {
+ if (ta->binary) { /* not if binary you don't */
+ fs_give ((void **) &ta);
+ response = badbin;
+ return;
+ }
+ *v++ = '\0'; /* yes, tie off section specifier */
+ if (!strncmp (v,"MIME",4)) {
+ v += 4; /* found <section>.MIME */
+ f[k] = fetch_body_part_mime;
+ }
+ }
+ else if (*v != ']') { /* better be the end if no IMAP4rev1 stuff */
+ fs_give ((void **) &ta);/* clean up */
+ response = "%.80s BAD Syntax error in section specifier\015\012";
+ return;
+ }
+ }
+
+ if (*v != ']') { /* IMAP4rev1 stuff here? */
+ if (!strncmp (v,"HEADER",6)) {
+ *v = '\0'; /* tie off in case top level */
+ v += 6; /* found [<section>.]HEADER */
+ f[k] = fetch_body_part_header;
+ /* partial headers wanted? */
+ if (!strncmp (v,".FIELDS",7)) {
+ v += 7; /* yes */
+ if (!strncmp (v,".NOT",4)) {
+ v += 4; /* want to exclude named headers */
+ ta->flags |= FT_NOT;
+ }
+ if (*v || !(v = strtok (NIL,"\015\012")) ||
+ !(ta->lines = parse_stringlist (&v,&list))) {
+ fs_give ((void **) &ta);/* clean up */
+ response = "%.80s BAD Syntax error in header fields\015\012";
+ return;
+ }
+ }
+ }
+ else if (!strncmp (v,"TEXT",4)) {
+ *v = '\0'; /* tie off in case top level */
+ v += 4; /* found [<section>.]TEXT */
+ f[k] = fetch_body_part_text;
+ }
+ else {
+ fs_give ((void **) &ta);/* clean up */
+ response = "%.80s BAD Unknown section text specifier\015\012";
+ return;
+ }
+ }
+ }
+ /* tie off section */
+ if (*v == ']') *v++ = '\0';
+ else { /* bogon */
+ if (ta->lines) mail_free_stringlist (&ta->lines);
+ fs_give ((void **) &ta);/* clean up */
+ response = "%.80s BAD Section specifier not terminated\015\012";
+ return;
+ }
+
+ if ((*v == '<') && /* partial specifier? */
+ ((ta->binary & FTB_SIZE) ||
+ !(isdigit (v[1]) && ((ta->first = strtoul (v+1,(char **) &v,10)) ||
+ v) &&
+ (*v++ == '.') && (ta->last = strtoul (v,(char **) &v,10)) &&
+ (*v++ == '>')))) {
+ if (ta->lines) mail_free_stringlist (&ta->lines);
+ fs_give ((void **) &ta);
+ response ="%.80s BAD Syntax error in partial text specifier\015\012";
+ return;
+ }
+ switch (*v) { /* what's there now? */
+ case ' ': /* more follows */
+ *--v = ' '; /* patch a space back in */
+ *--v = 'x'; /* and a hokey character before that */
+ strtok (v," "); /* reset strtok mechanism */
+ break;
+ case '\0': /* none */
+ break;
+ case ')': /* end of list */
+ if (list && !v[1]) { /* make sure of that */
+ list = NIL;
+ strtok (v," "); /* reset strtok mechanism */
+ break; /* all done */
+ }
+ /* otherwise it's a bogon, drop in */
+ default: /* bogon */
+ if (ta->lines) mail_free_stringlist (&ta->lines);
+ fs_give ((void **) &ta);
+ response = "%.80s BAD Syntax error after section specifier\015\012";
+ return;
+ }
+ /* make copy of section specifier */
+ if (s && *s) ta->section = cpystr (s);
+ fa[k++] = (void *) ta; /* set argument */
+ }
+ else { /* unknown attribute */
+ response = badatt;
+ return;
+ }
+ } while ((s = strtok (NIL," ")) && (k < MAXFETCH) && list);
+ else {
+ response = misarg; /* missing attribute list */
+ return;
+ }
+
+ if (s) { /* too many attributes? */
+ response = "%.80s BAD Excessively complex FETCH attribute list\015\012";
+ return;
+ }
+ if (list) { /* too many attributes? */
+ response = "%.80s BAD Unterminated FETCH attribute list\015\012";
+ return;
+ }
+ f[k] = NIL; /* tie off attribute list */
+ /* c-client clobbers sequence, use spare */
+ for (i = 1; i <= nmsgs; i++)
+ mail_elt (stream,i)->spare = mail_elt (stream,i)->sequence;
+ /* for each requested message */
+ for (i = 1; (i <= nmsgs) && (response != loseunknowncte); i++) {
+ /* kill if dying */
+ if (state == LOGOUT) longjmp (jmpenv,1);
+ if (mail_elt (stream,i)->spare) {
+ /* parse envelope, set body, do warnings */
+ if (parse_envs) mail_fetchstructure (stream,i,parse_bodies ? &b : NIL);
+ quell_events = T; /* can't do any events now */
+ PSOUT ("* "); /* leader */
+ pnum (i);
+ PSOUT (" FETCH (");
+ (*f[0]) (i,fa[0]); /* do first attribute */
+ /* for each subsequent attribute */
+ for (k = 1; f[k] && (response != loseunknowncte); k++) {
+ PBOUT (' '); /* delimit with space */
+ (*f[k]) (i,fa[k]); /* do that attribute */
+ }
+ PSOUT (")\015\012"); /* trailer */
+ quell_events = NIL; /* events alright now */
+ }
+ }
+}
+
+/* Fetch message body structure (extensible)
+ * Accepts: message number
+ * extra argument
+ */
+
+void fetch_bodystructure (unsigned long i,void *args)
+{
+ BODY *body;
+ mail_fetchstructure (stream,i,&body);
+ PSOUT ("BODYSTRUCTURE ");
+ pbodystructure (body); /* output body */
+}
+
+
+/* Fetch message body structure (non-extensible)
+ * Accepts: message number
+ * extra argument
+ */
+
+
+void fetch_body (unsigned long i,void *args)
+{
+ BODY *body;
+ mail_fetchstructure (stream,i,&body);
+ PSOUT ("BODY "); /* output attribute */
+ pbody (body); /* output body */
+}
+
+/* Fetch body part MIME header
+ * Accepts: message number
+ * extra argument
+ */
+
+void fetch_body_part_mime (unsigned long i,void *args)
+{
+ TEXTARGS *ta = (TEXTARGS *) args;
+ if (i) { /* do work? */
+ SIZEDTEXT st;
+ unsigned long uid = mail_uid (stream,i);
+ char *tmp = (char *) fs_get (100 + strlen (ta->section));
+ sprintf (tmp,"BODY[%s.MIME]",ta->section);
+ /* try to use remembered text */
+ if (lastuid && (uid == lastuid) && !strcmp (tmp,lastid)) st = lastst;
+ else { /* get data */
+ st.data = (unsigned char *)
+ mail_fetch_mime (stream,i,ta->section,&st.size,ta->flags);
+ if (ta->first || ta->last) remember (uid,tmp,&st);
+ }
+ pbodypartstring (i,tmp,&st,NIL,ta);
+ fs_give ((void **) &tmp);
+ }
+ else { /* clean up the arguments */
+ fs_give ((void **) &ta->section);
+ fs_give ((void **) &args);
+ }
+}
+
+
+/* Fetch body part contents
+ * Accepts: message number
+ * extra argument
+ */
+
+void fetch_body_part_contents (unsigned long i,void *args)
+{
+ TEXTARGS *ta = (TEXTARGS *) args;
+ if (i) { /* do work? */
+ SIZEDTEXT st;
+ char *tmp = (char *) fs_get (100+(ta->section ? strlen (ta->section) : 0));
+ unsigned long uid = mail_uid (stream,i);
+ sprintf (tmp,"BODY[%s]",ta->section ? ta->section : "");
+ /* try to use remembered text */
+ if (lastuid && (uid == lastuid) && !strcmp (tmp,lastid)) st = lastst;
+ /* get data */
+ else if ((st.data = (unsigned char *)
+ mail_fetch_body (stream,i,ta->section,&st.size,
+ ta->flags | FT_RETURNSTRINGSTRUCT)) &&
+ (ta->first || ta->last)) remember (uid,tmp,&st);
+ pbodypartstring (i,tmp,&st,&stream->private.string,ta);
+ fs_give ((void **) &tmp);
+ }
+ else { /* clean up the arguments */
+ if (ta->section) fs_give ((void **) &ta->section);
+ fs_give ((void **) &args);
+ }
+}
+
+/* Fetch body part binary
+ * Accepts: message number
+ * extra argument
+ * Someday fix this to use stringstruct instead of memory
+ */
+
+void fetch_body_part_binary (unsigned long i,void *args)
+{
+ TEXTARGS *ta = (TEXTARGS *) args;
+ if (i) { /* do work? */
+ SIZEDTEXT st,cst;
+ BODY *body = mail_body (stream,i,ta->section);
+ char *tmp = (char *) fs_get (100+(ta->section ? strlen (ta->section) : 0));
+ unsigned long uid = mail_uid (stream,i);
+ /* try to use remembered text */
+ if (lastuid && (uid == lastuid) && !strcmp (tmp,lastid)) st = lastst;
+ else { /* get data */
+ st.data = (unsigned char *)
+ mail_fetch_body (stream,i,ta->section,&st.size,ta->flags);
+ if (ta->first || ta->last) remember (uid,tmp,&st);
+ }
+ /* what encoding was used? */
+ if (body) switch (body->encoding) {
+ case ENCBASE64:
+ if (cst.data = rfc822_base64 (st.data,st.size,&cst.size)) break;
+ fetch_uid (i,NIL); /* wrote a space, so must do something */
+ if (lsterr) fs_give ((void **) &lsterr);
+ lsterr = cpystr ("Undecodable BASE64 contents");
+ response = loseunknowncte;
+ fs_give ((void **) &tmp);
+ return;
+ case ENCQUOTEDPRINTABLE:
+ if (cst.data = rfc822_qprint (st.data,st.size,&cst.size)) break;
+ fetch_uid (i,NIL); /* wrote a space, so must do something */
+ if (lsterr) fs_give ((void **) &lsterr);
+ lsterr = cpystr ("Undecodable QUOTED-PRINTABLE contents");
+ response = loseunknowncte;
+ fs_give ((void **) &tmp);
+ return;
+ case ENC7BIT: /* no need to convert any of these */
+ case ENC8BIT:
+ case ENCBINARY:
+ cst.data = NIL; /* no converted data to free */
+ break;
+ default: /* unknown encoding, oops */
+ fetch_uid (i,NIL); /* wrote a space, so must do something */
+ if (lsterr) fs_give ((void **) &lsterr);
+ lsterr = cpystr ("Unknown Content-Transfer-Encoding");
+ response = loseunknowncte;
+ fs_give ((void **) &tmp);
+ return;
+ }
+ else {
+ if (lsterr) fs_give ((void **) &lsterr);
+ lsterr = cpystr ("Invalid body part");
+ response = loseunknowncte;
+ fs_give ((void **) &tmp);
+ return;
+ }
+
+ /* use decoded version if exists */
+ if (cst.data) memcpy ((void *) &st,(void *) &cst,sizeof (SIZEDTEXT));
+ if (ta->binary & FTB_SIZE) {/* just want size? */
+ sprintf (tmp,"BINARY.SIZE[%s] %lu",ta->section ? ta->section : "",
+ st.size);
+ PSOUT (tmp);
+ }
+ else { /* no, blat binary data */
+ int f = mail_elt (stream,i)->seen;
+ if (st.data) { /* only if have useful data */
+ /* partial specifier */
+ if (ta->first || ta->last)
+ sprintf (tmp,"BINARY[%s]<%lu> ",
+ ta->section ? ta->section : "",ta->first);
+ else sprintf (tmp,"BINARY[%s] ",ta->section ? ta->section : "");
+ /* in case first byte beyond end of text */
+ if (st.size <= ta->first) st.size = ta->first = 0;
+ else { /* offset and truncate */
+ st.data += ta->first; /* move to desired position */
+ st.size -= ta->first; /* reduced size */
+ if (ta->last && (st.size > ta->last)) st.size = ta->last;
+ }
+ if (st.size) sprintf (tmp + strlen (tmp),"{%lu}\015\012",st.size);
+ else strcat (tmp,"\"\"");
+ PSOUT (tmp); /* write binary output */
+ if (st.size && (PSOUTR (&st) == EOF)) ioerror(stdout,"writing binary");
+ }
+ else {
+ sprintf (tmp,"BINARY[%s] NIL",ta->section ? ta->section : "");
+ PSOUT (tmp);
+ }
+ changed_flags (i,f); /* write changed flags */
+ }
+ /* free converted data */
+ if (cst.data) fs_give ((void **) &cst.data);
+ fs_give ((void **) &tmp); /* and temporary string */
+ }
+ else { /* clean up the arguments */
+ if (ta->section) fs_give ((void **) &ta->section);
+ fs_give ((void **) &args);
+ }
+}
+
+/* Fetch MESSAGE/RFC822 body part header
+ * Accepts: message number
+ * extra argument
+ */
+
+void fetch_body_part_header (unsigned long i,void *args)
+{
+ TEXTARGS *ta = (TEXTARGS *) args;
+ unsigned long len = 100 + (ta->section ? strlen (ta->section) : 0);
+ STRINGLIST *s;
+ for (s = ta->lines; s; s = s->next) len += s->text.size + 1;
+ if (i) { /* do work? */
+ SIZEDTEXT st;
+ char *tmp = (char *) fs_get (len);
+ PSOUT ("BODY[");
+ /* output attribute */
+ if (ta->section && *ta->section) {
+ PSOUT (ta->section);
+ PBOUT ('.');
+ }
+ PSOUT ("HEADER");
+ if (ta->lines) {
+ PSOUT ((ta->flags & FT_NOT) ? ".FIELDS.NOT " : ".FIELDS ");
+ pastringlist (ta->lines);
+ }
+ strcpy (tmp,"]"); /* close section specifier */
+ st.data = (unsigned char *) /* get data (no hope in using remember here) */
+ mail_fetch_header (stream,i,ta->section,ta->lines,&st.size,ta->flags);
+ pbodypartstring (i,tmp,&st,NIL,ta);
+ fs_give ((void **) &tmp);
+ }
+ else { /* clean up the arguments */
+ if (ta->lines) mail_free_stringlist (&ta->lines);
+ if (ta->section) fs_give ((void **) &ta->section);
+ fs_give ((void **) &args);
+ }
+}
+
+/* Fetch MESSAGE/RFC822 body part text
+ * Accepts: message number
+ * extra argument
+ */
+
+void fetch_body_part_text (unsigned long i,void *args)
+{
+ TEXTARGS *ta = (TEXTARGS *) args;
+ if (i) { /* do work? */
+ SIZEDTEXT st;
+ char *tmp = (char *) fs_get (100+(ta->section ? strlen (ta->section) : 0));
+ unsigned long uid = mail_uid (stream,i);
+ /* output attribute */
+ if (ta->section && *ta->section) sprintf (tmp,"BODY[%s.TEXT]",ta->section);
+ else strcpy (tmp,"BODY[TEXT]");
+ /* try to use remembered text */
+ if (lastuid && (uid == lastuid) && !strcmp (tmp,lastid)) st = lastst;
+ /* get data */
+ else if ((st.data = (unsigned char *)
+ mail_fetch_text (stream,i,ta->section,&st.size,
+ ta->flags | FT_RETURNSTRINGSTRUCT)) &&
+ (ta->first || ta->last)) remember (uid,tmp,&st);
+ pbodypartstring (i,tmp,&st,&stream->private.string,ta);
+ fs_give ((void **) &tmp);
+ }
+ else { /* clean up the arguments */
+ if (ta->section) fs_give ((void **) &ta->section);
+ fs_give ((void **) &args);
+ }
+}
+
+
+/* Remember body part text for subsequent partial fetching
+ * Accepts: message UID
+ * body part id
+ * text
+ * string
+ */
+
+void remember (unsigned long uid,char *id,SIZEDTEXT *st)
+{
+ lastuid = uid; /* remember UID */
+ if (lastid) fs_give ((void **) &lastid);
+ lastid = cpystr (id); /* remember body part id */
+ if (lastst.data) fs_give ((void **) &lastst.data);
+ /* remember text */
+ lastst.data = (unsigned char *)
+ memcpy (fs_get (st->size + 1),st->data,st->size);
+ lastst.size = st->size;
+}
+
+
+/* Fetch envelope
+ * Accepts: message number
+ * extra argument
+ */
+
+void fetch_envelope (unsigned long i,void *args)
+{
+ ENVELOPE *env = mail_fetchenvelope (stream,i);
+ PSOUT ("ENVELOPE "); /* output attribute */
+ penv (env); /* output envelope */
+}
+
+/* Fetch flags
+ * Accepts: message number
+ * extra argument
+ */
+
+void fetch_flags (unsigned long i,void *args)
+{
+ unsigned long u;
+ char *t,tmp[MAILTMPLEN];
+ int c = NIL;
+ MESSAGECACHE *elt = mail_elt (stream,i);
+ if (!elt->valid) { /* have valid flags yet? */
+ sprintf (tmp,"%lu",i);
+ mail_fetch_flags (stream,tmp,NIL);
+ }
+ PSOUT ("FLAGS ("); /* output attribute */
+ /* output system flags */
+ if (elt->recent) put_flag (&c,"\\Recent");
+ if (elt->seen) put_flag (&c,"\\Seen");
+ if (elt->deleted) put_flag (&c,"\\Deleted");
+ if (elt->flagged) put_flag (&c,"\\Flagged");
+ if (elt->answered) put_flag (&c,"\\Answered");
+ if (elt->draft) put_flag (&c,"\\Draft");
+ if (u = elt->user_flags) do /* any user flags? */
+ if (t = stream->user_flags[find_rightmost_bit (&u)]) put_flag (&c,t);
+ while (u); /* until no more user flags */
+ PBOUT (')'); /* end of flags */
+ elt->spare2 = NIL; /* we've sent the update */
+}
+
+
+/* Output a flag
+ * Accepts: pointer to current delimiter character
+ * flag to output
+ * Changes delimiter character to space
+ */
+
+void put_flag (int *c,char *s)
+{
+ if (*c) PBOUT (*c); /* put delimiter */
+ PSOUT (s); /* dump flag */
+ *c = ' '; /* change delimiter if necessary */
+}
+
+
+/* Output flags if was unseen
+ * Accepts: message number
+ * prior value of Seen flag
+ */
+
+void changed_flags (unsigned long i,int f)
+{
+ /* was unseen, now seen? */
+ if (!f && mail_elt (stream,i)->seen) {
+ PBOUT (' '); /* yes, delimit with space */
+ fetch_flags (i,NIL); /* output flags */
+ }
+}
+
+/* Fetch message internal date
+ * Accepts: message number
+ * extra argument
+ */
+
+void fetch_internaldate (unsigned long i,void *args)
+{
+ char tmp[MAILTMPLEN];
+ MESSAGECACHE *elt = mail_elt (stream,i);
+ if (!elt->day) { /* have internal date yet? */
+ sprintf (tmp,"%lu",i);
+ mail_fetch_fast (stream,tmp,NIL);
+ }
+ PSOUT ("INTERNALDATE \"");
+ PSOUT (mail_date (tmp,elt));
+ PBOUT ('"');
+}
+
+
+/* Fetch unique identifier
+ * Accepts: message number
+ * extra argument
+ */
+
+void fetch_uid (unsigned long i,void *args)
+{
+ PSOUT ("UID ");
+ pnum (mail_uid (stream,i));
+}
+
+/* Fetch complete RFC-822 format message
+ * Accepts: message number
+ * extra argument
+ */
+
+void fetch_rfc822 (unsigned long i,void *args)
+{
+ if (i) { /* do work? */
+ int f = mail_elt (stream,i)->seen;
+#if 0
+ SIZEDTEXT st;
+ st.data = (unsigned char *)
+ mail_fetch_message (stream,i,&st.size,(long) args);
+ pbodypartstring (i,"RFC822",&st,NIL,NIL);
+#else
+ /* Yes, this version is bletcherous, but mail_fetch_message() requires
+ too much memory */
+ SIZEDTEXT txt,hdr;
+ char *s = mail_fetch_header (stream,i,NIL,NIL,&hdr.size,FT_PEEK);
+ hdr.data = (unsigned char *) memcpy (fs_get (hdr.size),s,hdr.size);
+ txt.data = (unsigned char *)
+ mail_fetch_text (stream,i,NIL,&txt.size,
+ ((long) args) | FT_RETURNSTRINGSTRUCT);
+ PSOUT ("RFC822 {");
+ pnum (hdr.size + txt.size);
+ PSOUT ("}\015\012");
+ ptext (&hdr,NIL);
+ ptext (&txt,&stream->private.string);
+ fs_give ((void **) &hdr.data);
+#endif
+ changed_flags (i,f); /* output changed flags */
+ }
+}
+
+
+/* Fetch RFC-822 header
+ * Accepts: message number
+ * extra argument
+ */
+
+void fetch_rfc822_header (unsigned long i,void *args)
+{
+ SIZEDTEXT st;
+ st.data = (unsigned char *)
+ mail_fetch_header (stream,i,NIL,NIL,&st.size,FT_PEEK);
+ pbodypartstring (i,"RFC822.HEADER",&st,NIL,NIL);
+}
+
+
+/* Fetch RFC-822 message length
+ * Accepts: message number
+ * extra argument
+ */
+
+void fetch_rfc822_size (unsigned long i,void *args)
+{
+ char tmp[MAILTMPLEN];
+ MESSAGECACHE *elt = mail_elt (stream,i);
+ if (!elt->rfc822_size) { /* have message size yet? */
+ sprintf (tmp,"%lu",i);
+ mail_fetch_fast (stream,tmp,NIL);
+ }
+ PSOUT ("RFC822.SIZE ");
+ pnum (elt->rfc822_size);
+}
+
+/* Fetch RFC-822 text only
+ * Accepts: message number
+ * extra argument
+ */
+
+void fetch_rfc822_text (unsigned long i,void *args)
+{
+ if (i) { /* do work? */
+ int f = mail_elt (stream,i)->seen;
+ SIZEDTEXT st;
+ st.data = (unsigned char *)
+ mail_fetch_text (stream,i,NIL,&st.size,
+ ((long) args) | FT_RETURNSTRINGSTRUCT);
+ pbodypartstring (i,"RFC822.TEXT",&st,&stream->private.string,NIL);
+ }
+}
+
+/* Print envelope
+ * Accepts: body
+ */
+
+void penv (ENVELOPE *env)
+{
+ PBOUT ('('); /* delimiter */
+ if (env) { /* only if there is an envelope */
+ pnstring (env->date); /* output envelope fields */
+ PBOUT (' ');
+ pnstring (env->subject);
+ PBOUT (' ');
+ paddr (env->from);
+ PBOUT (' ');
+ paddr (env->sender);
+ PBOUT (' ');
+ paddr (env->reply_to);
+ PBOUT (' ');
+ paddr (env->to);
+ PBOUT (' ');
+ paddr (env->cc);
+ PBOUT (' ');
+ paddr (env->bcc);
+ PBOUT (' ');
+ pnstring (env->in_reply_to);
+ PBOUT (' ');
+ pnstring (env->message_id);
+ }
+ /* no envelope */
+ else PSOUT ("NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL");
+ PBOUT (')'); /* end of envelope */
+}
+
+/* Print body structure (extensible)
+ * Accepts: body
+ */
+
+void pbodystructure (BODY *body)
+{
+ PBOUT ('('); /* delimiter */
+ if (body) { /* only if there is a body */
+ PART *part;
+ /* multipart type? */
+ if (body->type == TYPEMULTIPART) {
+ /* print each part */
+ if (part = body->nested.part)
+ for (; part; part = part->next) pbodystructure (&(part->body));
+ else pbodystructure (NIL);
+ PBOUT (' '); /* space delimiter */
+ pstring (body->subtype); /* subtype */
+ PBOUT (' ');
+ pparam (body->parameter); /* multipart body extension data */
+ PBOUT (' ');
+ if (body->disposition.type) {
+ PBOUT ('(');
+ pstring (body->disposition.type);
+ PBOUT (' ');
+ pparam (body->disposition.parameter);
+ PBOUT (')');
+ }
+ else PSOUT ("NIL");
+ PBOUT (' ');
+ pnstringorlist (body->language);
+ PBOUT (' ');
+ pnstring (body->location);
+ }
+
+ else { /* non-multipart body type */
+ pstring ((char *) body_types[body->type]);
+ PBOUT (' ');
+ pstring (body->subtype);
+ PBOUT (' ');
+ pparam (body->parameter);
+ PBOUT (' ');
+ pnstring (body->id);
+ PBOUT (' ');
+ pnstring (body->description);
+ PBOUT (' ');
+ pstring ((char *) body_encodings[body->encoding]);
+ PBOUT (' ');
+ pnum (body->size.bytes);
+ switch (body->type) { /* extra stuff depends upon body type */
+ case TYPEMESSAGE:
+ /* can't do this if not RFC822 */
+ if (strcmp (body->subtype,"RFC822")) break;
+ PBOUT (' ');
+ penv (body->nested.msg->env);
+ PBOUT (' ');
+ pbodystructure (body->nested.msg->body);
+ case TYPETEXT:
+ PBOUT (' ');
+ pnum (body->size.lines);
+ break;
+ default:
+ break;
+ }
+ PBOUT (' ');
+ pnstring (body->md5);
+ PBOUT (' ');
+ if (body->disposition.type) {
+ PBOUT ('(');
+ pstring (body->disposition.type);
+ PBOUT (' ');
+ pparam (body->disposition.parameter);
+ PBOUT (')');
+ }
+ else PSOUT ("NIL");
+ PBOUT (' ');
+ pnstringorlist (body->language);
+ PBOUT (' ');
+ pnstring (body->location);
+ }
+ }
+ /* no body */
+ else PSOUT ("\"TEXT\" \"PLAIN\" (\"CHARSET\" \"US-ASCII\") NIL NIL \"7BIT\" 0 0 NIL NIL NIL NIL");
+ PBOUT (')'); /* end of body */
+}
+
+/* Print body (non-extensible)
+ * Accepts: body
+ */
+
+void pbody (BODY *body)
+{
+ PBOUT ('('); /* delimiter */
+ if (body) { /* only if there is a body */
+ PART *part;
+ /* multipart type? */
+ if (body->type == TYPEMULTIPART) {
+ /* print each part */
+ if (part = body->nested.part)
+ for (; part; part = part->next) pbody (&(part->body));
+ else pbody (NIL);
+ PBOUT (' '); /* space delimiter */
+ pstring (body->subtype); /* and finally the subtype */
+ }
+ else { /* non-multipart body type */
+ pstring ((char *) body_types[body->type]);
+ PBOUT (' ');
+ pstring (body->subtype);
+ PBOUT (' ');
+ pparam (body->parameter);
+ PBOUT (' ');
+ pnstring (body->id);
+ PBOUT (' ');
+ pnstring (body->description);
+ PBOUT (' ');
+ pstring ((char *) body_encodings[body->encoding]);
+ PBOUT (' ');
+ pnum (body->size.bytes);
+ switch (body->type) { /* extra stuff depends upon body type */
+ case TYPEMESSAGE:
+ /* can't do this if not RFC822 */
+ if (strcmp (body->subtype,"RFC822")) break;
+ PBOUT (' ');
+ penv (body->nested.msg ? body->nested.msg->env : NIL);
+ PBOUT (' ');
+ pbody (body->nested.msg ? body->nested.msg->body : NIL);
+ case TYPETEXT:
+ PBOUT (' ');
+ pnum (body->size.lines);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ /* no body */
+ else PSOUT ("\"TEXT\" \"PLAIN\" (\"CHARSET\" \"US-ASCII\") NIL NIL \"7BIT\" 0 0");
+ PBOUT (')'); /* end of body */
+}
+
+/* Print parameter list
+ * Accepts: paramter
+ */
+
+void pparam (PARAMETER *param)
+{
+ if (param) { /* one specified? */
+ PBOUT ('(');
+ do {
+ pstring (param->attribute);
+ PBOUT (' ');
+ pstring (param->value);
+ if (param = param->next) PBOUT (' ');
+ } while (param);
+ PBOUT (')'); /* end of parameters */
+ }
+ else PSOUT ("NIL");
+}
+
+
+/* Print address list
+ * Accepts: address list
+ */
+
+void paddr (ADDRESS *a)
+{
+ if (a) { /* have anything in address? */
+ PBOUT ('('); /* open the address list */
+ do { /* for each address */
+ PBOUT ('('); /* open the address */
+ pnstring (a->personal); /* personal name */
+ PBOUT (' ');
+ pnstring (a->adl); /* at-domain-list */
+ PBOUT (' ');
+ pnstring (a->mailbox); /* mailbox */
+ PBOUT (' ');
+ pnstring (a->host); /* domain name of mailbox's host */
+ PBOUT (')'); /* terminate address */
+ } while (a = a->next); /* until end of address */
+ PBOUT (')'); /* close address list */
+ }
+ else PSOUT ("NIL"); /* empty address */
+}
+
+/* Print set
+ * Accepts: set
+ */
+
+void pset (SEARCHSET **set)
+{
+ SEARCHSET *cur = *set;
+ while (cur) { /* while there's a set to do */
+ pnum (cur->first); /* output first value */
+ if (cur->last) { /* if range, output second value of range */
+ PBOUT (':');
+ pnum (cur->last);
+ }
+ if (cur = cur->next) PBOUT (',');
+ }
+ mail_free_searchset (set); /* flush set */
+}
+
+
+/* Print number
+ * Accepts: number
+ */
+
+void pnum (unsigned long i)
+{
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"%lu",i);
+ PSOUT (tmp);
+}
+
+
+/* Print string
+ * Accepts: string
+ */
+
+void pstring (char *s)
+{
+ SIZEDTEXT st;
+ st.data = (unsigned char *) s;/* set up sized text */
+ st.size = strlen (s);
+ psizedstring (&st,NIL); /* print string */
+}
+
+
+/* Print nstring
+ * Accepts: string or NIL
+ */
+
+void pnstring (char *s)
+{
+ if (s) pstring (s); /* print string */
+ else PSOUT ("NIL");
+}
+
+
+/* Print atom or string
+ * Accepts: astring
+ */
+
+void pastring (char *s)
+{
+ char *t;
+ if (!*s) PSOUT ("\"\""); /* empty string */
+ else { /* see if atom */
+ for (t = s; (*t > ' ') && !(*t & 0x80) &&
+ (*t != '"') && (*t != '\\') && (*t != '(') && (*t != ')') &&
+ (*t != '{') && (*t != '%') && (*t != '*'); t++);
+ if (*t) pstring (s); /* not an atom */
+ else PSOUT (s); /* else plop down as atomic */
+ }
+}
+
+/* Print sized text as quoted
+ * Accepts: sized text
+ */
+
+void psizedquoted (SIZEDTEXT *s)
+{
+ PBOUT ('"'); /* use quoted string */
+ ptext (s,NIL);
+ PBOUT ('"');
+}
+
+
+/* Print sized text as literal
+ * Accepts: sized text
+ */
+
+void psizedliteral (SIZEDTEXT *s,STRING *st)
+{
+ PBOUT ('{'); /* print literal size */
+ pnum (s->size);
+ PSOUT ("}\015\012");
+ ptext (s,st);
+}
+
+/* Print sized text as literal or quoted string
+ * Accepts: sized text
+ * alternative stringstruct of text
+ */
+
+void psizedstring (SIZEDTEXT *s,STRING *st)
+{
+ unsigned char c;
+ unsigned long i;
+
+ if (s->data) { /* if text, check if must use literal */
+ for (i = 0; ((i < s->size) && ((c = s->data[i]) & 0xe0) &&
+ !(c & 0x80) && (c != '"') && (c != '\\')); ++i);
+ /* must use literal if not all QUOTED-CHAR */
+ if (i < s->size) psizedliteral (s,st);
+ else psizedquoted (s);
+ }
+ else psizedliteral (s,st);
+}
+
+
+/* Print sized text as literal or quoted string
+ * Accepts: sized text
+ */
+
+void psizedastring (SIZEDTEXT *s)
+{
+ unsigned long i;
+ unsigned int atomp = s->size ? T : NIL;
+ for (i = 0; i < s->size; i++){/* check if must use literal */
+ if (!(s->data[i] & 0xe0) || (s->data[i] & 0x80) ||
+ (s->data[i] == '"') || (s->data[i] == '\\')) {
+ psizedliteral (s,NIL);
+ return;
+ }
+ else switch (s->data[i]) { /* else see if any atom-specials */
+ case '(': case ')': case '{': case ' ':
+ case '%': case '*': /* list-wildcards */
+ case ']': /* resp-specials */
+ /* CTL and quoted-specials in literal check */
+ atomp = NIL; /* not an atom */
+ }
+ }
+ if (atomp) ptext (s,NIL); /* print as atom */
+ else psizedquoted (s); /* print as quoted string */
+}
+
+/* Print string list
+ * Accepts: string list
+ */
+
+void pastringlist (STRINGLIST *s)
+{
+ PBOUT ('('); /* start list */
+ do {
+ psizedastring (&s->text); /* output list member */
+ if (s->next) PBOUT (' ');
+ } while (s = s->next);
+ PBOUT (')'); /* terminate list */
+}
+
+
+/* Print nstring or list of strings
+ * Accepts: string / string list
+ */
+
+void pnstringorlist (STRINGLIST *s)
+{
+ if (!s) PSOUT ("NIL"); /* no argument given */
+ else if (s->next) { /* output list as list of strings*/
+ PBOUT ('('); /* start list */
+ do { /* output list member */
+ psizedstring (&s->text,NIL);
+ if (s->next) PBOUT (' ');
+ } while (s = s->next);
+ PBOUT (')'); /* terminate list */
+ }
+ /* and single-element list as string */
+ else psizedstring (&s->text,NIL);
+}
+
+/* Print body part string
+ * Accepts: message number
+ * body part id (note: must have space at end to append stuff)
+ * sized text of string
+ * alternative stringstruct of string
+ * text printing arguments
+ */
+
+void pbodypartstring (unsigned long msgno,char *id,SIZEDTEXT *st,STRING *bs,
+ TEXTARGS *ta)
+{
+ int f = mail_elt (stream,msgno)->seen;
+ /* ignore stringstruct if non-initialized */
+ if (bs && !bs->curpos) bs = NIL;
+ if (ta && st->size) { /* only if have useful data */
+ /* partial specifier */
+ if (ta->first || ta->last) sprintf (id + strlen (id),"<%lu>",ta->first);
+ /* in case first byte beyond end of text */
+ if (st->size <= ta->first) st->size = ta->first = 0;
+ else {
+ if (st->data) { /* offset and truncate */
+ st->data += ta->first; /* move to desired position */
+ st->size -= ta->first; /* reduced size */
+ }
+ else if (bs && (SIZE (bs) >= ta->first))
+ SETPOS (bs,ta->first + GETPOS (bs));
+ else st->size = 0; /* shouldn't happen */
+ if (ta->last && (st->size > ta->last)) st->size = ta->last;
+ }
+ }
+ PSOUT (id);
+ PBOUT (' ');
+ psizedstring (st,bs); /* output string */
+ changed_flags (msgno,f); /* and changed flags */
+}
+
+/* RFC 3501 technically forbids NULs in literals. Normally, the delivering
+ * MTA would take care of MIME converting the message text so that it is
+ * NUL-free. If it doesn't, then we have the choice of either violating
+ * IMAP by sending NULs, corrupting the data, or going to lots of work to do
+ * MIME conversion in the IMAP server.
+ */
+
+/* Print raw sized text
+ * Accepts: sizedtext
+ */
+
+void ptext (SIZEDTEXT *txt,STRING *st)
+{
+ unsigned char c,*s;
+ unsigned long i = txt->size;
+ if (s = txt->data) while (i && ((PBOUT ((c = *s++) ? c : 0x80) != EOF))) --i;
+ else if (st) while (i && (PBOUT ((c = SNX (st)) ? c : 0x80) != EOF)) --i;
+ /* failed to complete? */
+ if (i) ioerror (stdout,"writing text");
+}
+
+/* Print thread
+ * Accepts: thread
+ */
+
+void pthread (THREADNODE *thr)
+{
+ THREADNODE *t;
+ while (thr) { /* for each branch */
+ PBOUT ('('); /* open branch */
+ if (thr->num) { /* first node message number */
+ pnum (thr->num);
+ if (t = thr->next) { /* any subsequent nodes? */
+ PBOUT (' ');
+ while (t) { /* for each subsequent node */
+ if (t->branch) { /* branches? */
+ pthread (t); /* yes, recurse to do branch */
+ t = NIL; /* done */
+ }
+ else { /* just output this number */
+ pnum (t->num);
+ t = t->next; /* and do next message */
+ }
+ if (t) PBOUT (' '); /* delimit if more to come */
+ }
+ }
+ }
+ else pthread (thr->next); /* nest for dummy */
+ PBOUT (')'); /* done with this branch */
+ thr = thr->branch; /* do next branch */
+ }
+}
+
+/* Print capabilities
+ * Accepts: option flag
+ */
+
+void pcapability (long flag)
+{
+ unsigned long i;
+ char *s;
+ struct stat sbuf;
+ AUTHENTICATOR *auth;
+ THREADER *thr = (THREADER *) mail_parameters (NIL,GET_THREADERS,NIL);
+ /* always output protocol level */
+ PSOUT ("CAPABILITY IMAP4REV1 I18NLEVEL=1 LITERAL+");
+#ifdef NETSCAPE_BRAIN_DAMAGE
+ PSOUT (" X-NETSCAPE");
+#endif
+ if (flag >= 0) { /* want post-authentication capabilities? */
+ PSOUT (" IDLE UIDPLUS NAMESPACE CHILDREN MAILBOX-REFERRALS BINARY UNSELECT ESEARCH WITHIN SCAN SORT");
+ while (thr) { /* threaders */
+ PSOUT (" THREAD=");
+ PSOUT (thr->name);
+ thr = thr->next;
+ }
+ if (!anonymous) PSOUT (" MULTIAPPEND");
+ }
+ if (flag <= 0) { /* want pre-authentication capabilities? */
+ PSOUT (" SASL-IR LOGIN-REFERRALS");
+ if (s = ssl_start_tls (NIL)) fs_give ((void **) &s);
+ else PSOUT (" STARTTLS");
+ /* disable plaintext */
+ if (!(i = !mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL)))
+ PSOUT (" LOGINDISABLED");
+ for (auth = mail_lookup_auth (1); auth; auth = auth->next)
+ if (auth->server && !(auth->flags & AU_DISABLE) &&
+ !(auth->flags & AU_HIDE) && (i || (auth->flags & AU_SECURE))) {
+ PSOUT (" AUTH=");
+ PSOUT (auth->name);
+ }
+ if (!stat (ANOFILE,&sbuf)) PSOUT (" AUTH=ANONYMOUS");
+ }
+}
+
+/* Anonymous users may only use these mailboxes in these namespaces */
+
+char *oktab[] = {"#news.", "#ftp/", "#public/", 0};
+
+
+/* Check if mailbox name is OK
+ * Accepts: reference name
+ * mailbox name
+ */
+
+long nameok (char *ref,char *name)
+{
+ int i;
+ unsigned char *s,*t;
+ if (!name) return NIL; /* failure if missing name */
+ if (!anonymous) return T; /* otherwise OK if not anonymous */
+ /* validate reference */
+ if (ref && ((*ref == '#') || (*ref == '{')))
+ for (i = 0; oktab[i]; i++) {
+ for (s = ref, t = oktab[i]; *t && !compare_uchar (*s,*t); s++, t++);
+ if (!*t) { /* reference OK */
+ if (*name == '#') break;/* check name if override */
+ else return T; /* otherwise done */
+ }
+ }
+ /* ordinary names are OK */
+ if ((*name != '#') && (*name != '{')) return T;
+ for (i = 0; oktab[i]; i++) { /* validate mailbox */
+ for (s = name, t = oktab[i]; *t && !compare_uchar (*s,*t); s++, t++);
+ if (!*t) return T; /* name is OK */
+ }
+ response = "%.80s NO Anonymous may not %.80s this name\015\012";
+ return NIL;
+}
+
+
+/* Convert possible BBoard name to actual name
+ * Accepts: command
+ * mailbox name
+ * Returns: maibox name
+ */
+
+char *bboardname (char *cmd,char *name)
+{
+ if (cmd[0] == 'B') { /* want bboard? */
+ char *s = litstk[litsp++] = (char *) fs_get (strlen (name) + 9);
+ sprintf (s,"#public/%s",(*name == '/') ? name+1 : name);
+ name = s;
+ }
+ return name;
+}
+
+/* Test if name is news proxy
+ * Accepts: name
+ * Returns: T if news proxy, NIL otherwise
+ */
+
+long isnewsproxy (char *name)
+{
+ return (nntpproxy && (name[0] == '#') &&
+ ((name[1] == 'N') || (name[1] == 'n')) &&
+ ((name[2] == 'E') || (name[2] == 'e')) &&
+ ((name[3] == 'W') || (name[3] == 'w')) &&
+ ((name[4] == 'S') || (name[4] == 's')) && (name[5] == '.')) ?
+ LONGT : NIL;
+}
+
+
+/* News proxy generate canonical pattern
+ * Accepts: reference
+ * pattern
+ * buffer to return canonical pattern
+ * Returns: T on success with pattern in buffer, NIL on failure
+ */
+
+long newsproxypattern (char *ref,char *pat,char *pattern,long flag)
+{
+ if (!nntpproxy) return NIL;
+ if (strlen (ref) > NETMAXMBX) {
+ sprintf (pattern,"Invalid reference specification: %.80s",ref);
+ mm_log (pattern,ERROR);
+ return NIL;
+ }
+ if (strlen (pat) > NETMAXMBX) {
+ sprintf (pattern,"Invalid pattern specification: %.80s",pat);
+ mm_log (pattern,ERROR);
+ return NIL;
+ }
+ if (flag) { /* prepend proxy specifier */
+ sprintf (pattern,"{%.300s/nntp}",nntpproxy);
+ pattern += strlen (pattern);
+ }
+ if (*ref) { /* have a reference */
+ strcpy (pattern,ref); /* copy reference to pattern */
+ /* # overrides mailbox field in reference */
+ if (*pat == '#') strcpy (pattern,pat);
+ /* pattern starts, reference ends, with . */
+ else if ((*pat == '.') && (pattern[strlen (pattern) - 1] == '.'))
+ strcat (pattern,pat + 1); /* append, omitting one of the period */
+ else strcat (pattern,pat); /* anything else is just appended */
+ }
+ else strcpy (pattern,pat); /* just have basic name */
+ return isnewsproxy (pattern);
+}
+
+/* IMAP4rev1 Authentication responder
+ * Accepts: challenge
+ * length of challenge
+ * pointer to response length return location if non-NIL
+ * Returns: response
+ */
+
+#define RESPBUFLEN 8*MAILTMPLEN
+
+char *imap_responder (void *challenge,unsigned long clen,unsigned long *rlen)
+{
+ unsigned long i,j;
+ unsigned char *t,resp[RESPBUFLEN];
+ if (initial) { /* initial response given? */
+ if (clen) return NIL; /* not permitted */
+ /* set up response */
+ i = strlen ((char *) (t = initial));
+ initial = NIL; /* no more initial response */
+ if ((*t == '=') && !t[1]) { /* SASL-IR does this for 0-length response */
+ if (rlen) *rlen = 0; /* set length zero if empty */
+ return cpystr (""); /* and return empty string as response */
+ }
+ }
+ else { /* issue challenge, get response */
+ PSOUT ("+ ");
+ for (t = rfc822_binary ((void *) challenge,clen,&i),j = 0; j < i; j++)
+ if (t[j] > ' ') PBOUT (t[j]);
+ fs_give ((void **) &t);
+ CRLF;
+ PFLUSH (); /* dump output buffer */
+ /* slurp response buffer */
+ slurp ((char *) resp,RESPBUFLEN,INPUTTIMEOUT);
+ if (!(t = (unsigned char *) strchr ((char *) resp,'\012')))
+ return (char *) flush ();
+ if (t[-1] == '\015') --t; /* remove CR */
+ *t = '\0'; /* tie off buffer */
+ if (resp[0] == '*') {
+ cancelled = T;
+ return NIL;
+ }
+ i = t - resp; /* length of response */
+ t = resp; /* set up for return call */
+ }
+ return (i % 4) ? NIL : /* return if valid BASE64 */
+ (char *) rfc822_base64 (t,i,rlen ? rlen : &i);
+}
+
+/* Proxy copy across mailbox formats
+ * Accepts: mail stream
+ * sequence to copy on this stream
+ * destination mailbox
+ * option flags
+ * Returns: T if success, else NIL
+ */
+
+long proxycopy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ MAILSTREAM *ts;
+ STRING st;
+ MSGDATA md;
+ SEARCHSET *set;
+ char tmp[MAILTMPLEN];
+ unsigned long i,j;
+ md.stream = stream;
+ md.msgno = 0;
+ md.flags = md.date = NIL;
+ md.message = &st;
+ /* Currently ignores CP_MOVE and CP_DEBUG */
+ if (!((options & CP_UID) ? /* validate sequence */
+ mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence)))
+ return NIL;
+ response = win; /* cancel previous errors */
+ if (lsterr) fs_give ((void **) &lsterr);
+ /* c-client clobbers sequence, use spare */
+ for (i = 1,j = 0,set = mail_newsearchset (); i <= nmsgs; i++)
+ if (mail_elt (stream,i)->spare = mail_elt (stream,i)->sequence) {
+ mail_append_set (set,mail_uid (stream,i));
+ if (!j) md.msgno = (j = i) - 1;
+ }
+ /* only if at least one message to copy */
+ if (j && !mail_append_multiple (NIL,mailbox,proxy_append,(void *) &md)) {
+ response = trycreate ? losetry : lose;
+ if (set) mail_free_searchset (&set);
+ return NIL;
+ }
+ if (caset) csset = set; /* set for return value now */
+ else if (set) mail_free_searchset (&set);
+ response = win; /* stomp any previous babble */
+ if (md.msgno) { /* get new driver name if was dummy */
+ sprintf (tmp,"Cross-format (%.80s -> %.80s) COPY completed",
+ stream->dtb->name,(ts = mail_open (NIL,mailbox,OP_PROTOTYPE)) ?
+ ts->dtb->name : "unknown");
+ mm_log (tmp,NIL);
+ }
+ return LONGT;
+}
+
+/* Proxy append message callback
+ * Accepts: MAIL stream
+ * append data package
+ * pointer to return initial flags
+ * pointer to return message internal date
+ * pointer to return stringstruct of message or NIL to stop
+ * Returns: T if success (have message or stop), NIL if error
+ */
+
+long proxy_append (MAILSTREAM *stream,void *data,char **flags,char **date,
+ STRING **message)
+{
+ MESSAGECACHE *elt;
+ unsigned long i;
+ char *s,*t,tmp[MAILTMPLEN];
+ MSGDATA *md = (MSGDATA *) data;
+ if (md->flags) fs_give ((void **) &md->flags);
+ if (md->date) fs_give ((void **) &md->date);
+ *message = NIL; /* assume all done */
+ *flags = *date = NIL;
+ while (++md->msgno <= nmsgs)
+ if ((elt = mail_elt (md->stream,md->msgno))->spare) {
+ if (!(elt->valid && elt->day)) {
+ sprintf (tmp,"%lu",md->msgno);
+ mail_fetch_fast (md->stream,tmp,NIL);
+ }
+ memset (s = tmp,0,MAILTMPLEN);
+ /* copy flags */
+ if (elt->seen) strcat (s," \\Seen");
+ if (elt->deleted) strcat (s," \\Deleted");
+ if (elt->flagged) strcat (s," \\Flagged");
+ if (elt->answered) strcat (s," \\Answered");
+ if (elt->draft) strcat (s," \\Draft");
+ if (i = elt->user_flags) do
+ if ((t = md->stream->user_flags[find_rightmost_bit (&i)]) && *t &&
+ (strlen (t) < ((size_t) (MAILTMPLEN-((s += strlen (s))+2-tmp))))) {
+ *s++ = ' '; /* space delimiter */
+ strcpy (s,t);
+ } while (i); /* until no more user flags */
+ *message = md->message; /* set up return values */
+ *flags = md->flags = cpystr (tmp + 1);
+ *date = md->date = cpystr (mail_date (tmp,elt));
+ INIT (md->message,msg_string,(void *) md,elt->rfc822_size);
+ break; /* process this message */
+ }
+ return LONGT;
+}
+
+/* Append message callback
+ * Accepts: MAIL stream
+ * append data package
+ * pointer to return initial flags
+ * pointer to return message internal date
+ * pointer to return stringstruct of message or NIL to stop
+ * Returns: T if success (have message or stop), NIL if error
+ */
+
+long append_msg (MAILSTREAM *stream,void *data,char **flags,char **date,
+ STRING **message)
+{
+ unsigned long i,j;
+ char *t;
+ APPENDDATA *ad = (APPENDDATA *) data;
+ unsigned char *arg = ad->arg;
+ /* flush text of previous message */
+ if (t = ad->flags) fs_give ((void **) &ad->flags);
+ if (t = ad->date) fs_give ((void **) &ad->date);
+ if (t = ad->msg) fs_give ((void **) &ad->msg);
+ *flags = *date = NIL; /* assume no flags or date */
+ if (t) { /* have previous message? */
+ if (!*arg) { /* if least one message, and no more coming */
+ *message = NIL; /* set stop */
+ return LONGT; /* return success */
+ }
+ else if (*arg++ != ' ') { /* must have a delimiter to next argument */
+ response = misarg; /* oops */
+ return NIL;
+ }
+ }
+ *message = ad->message; /* return pointer to message stringstruct */
+ if (*arg == '(') { /* parse optional flag list */
+ t = ++arg; /* pointer to flag list contents */
+ while (*arg && (*arg != ')')) arg++;
+ if (*arg) *arg++ = '\0';
+ if (*arg == ' ') arg++;
+ *flags = ad->flags = cpystr (t);
+ }
+ /* parse optional date */
+ if (*arg == '"') *date = ad->date = cpystr (snarf (&arg));
+ if (!arg || (*arg != '{')) /* parse message */
+ response = "%.80s BAD Missing literal in %.80s\015\012";
+ else if (!isdigit (arg[1]))
+ response = "%.80s BAD Missing message to %.80s\015\012";
+ else if (!(i = strtoul (arg+1,&t,10)))
+ response = "%.80s NO Empty message to %.80s\015\012";
+ else if (i > MAXAPPENDTXT) /* maybe relax this a little */
+ response = "%.80s NO Excessively large message to %.80s\015\012";
+ else if (((*t == '+') && (t[1] == '}') && !t[2]) || ((*t == '}') && !t[1])) {
+ /* get a literal buffer */
+ inliteral (ad->msg = (char *) fs_get (i+1),i);
+ /* get new command tail */
+ slurp (ad->arg,CMDLEN - (ad->arg - cmdbuf),INPUTTIMEOUT);
+ if (strchr (ad->arg,'\012')) {
+ /* reset strtok mechanism, tie off if done */
+ if (!strtok (ad->arg,"\015\012")) *ad->arg = '\0';
+ /* possible LITERAL+? */
+ if (((j = strlen (ad->arg)) > 3) && (ad->arg[j - 1] == '}') &&
+ (ad->arg[j - 2] == '+') && isdigit (ad->arg[j - 3])) {
+ /* back over possible count */
+ for (j -= 4; j && isdigit (ad->arg[j]); j--);
+ if (ad->arg[j] == '{') {/* found a literal? */
+ litplus.ok = T; /* yes, note LITERAL+ in effect, set size */
+ litplus.size = strtoul (ad->arg + j + 1,NIL,10);
+ }
+ }
+ /* initialize stringstruct */
+ INIT (ad->message,mail_string,(void *) ad->msg,i);
+ return LONGT; /* ready to go */
+ }
+ flush (); /* didn't find end of line? */
+ fs_give ((void **) &ad->msg);
+ }
+ else response = badarg; /* not a literal */
+ return NIL; /* error */
+}
+
+/* Got COPY UID data
+ * Accepts: MAIL stream
+ * mailbox name
+ * UID validity
+ * source set of UIDs
+ * destination set of UIDs
+ */
+
+void copyuid (MAILSTREAM *stream,char *mailbox,unsigned long uidvalidity,
+ SEARCHSET *sourceset,SEARCHSET *destset)
+{
+ if (cauidvalidity) fatal ("duplicate COPYUID/APPENDUID data");
+ cauidvalidity = uidvalidity;
+ csset = sourceset;
+ caset = destset;
+}
+
+
+/* Got APPEND UID data
+ * Accepts: mailbox name
+ * UID validity
+ * destination set of UIDs
+ */
+
+void appenduid (char *mailbox,unsigned long uidvalidity,SEARCHSET *set)
+{
+ copyuid (NIL,mailbox,uidvalidity,NIL,set);
+}
+
+
+/* Got a referral
+ * Accepts: MAIL stream
+ * URL
+ * referral type code
+ */
+
+char *referral (MAILSTREAM *stream,char *url,long code)
+{
+ if (lstref) fs_give ((void **) &lstref);
+ lstref = cpystr (url); /* set referral */
+ /* set error if not a logged in referral */
+ if (code != REFAUTH) response = lose;
+ if (!lsterr) lsterr = cpystr ("Try referral URL");
+ return NIL; /* don't chase referrals for now */
+}
+
+/* Co-routines from MAIL library */
+
+
+/* Message matches a search
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_searched (MAILSTREAM *s,unsigned long msgno)
+{
+ /* nothing to do here */
+}
+
+
+/* Message exists (i.e. there are that many messages in the mailbox)
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_exists (MAILSTREAM *s,unsigned long number)
+{
+ /* note change in number of messages */
+ if ((s != tstream) && (nmsgs != number)) {
+ nmsgs = number; /* always update number of messages */
+ if (quell_events) existsquelled = T;
+ else {
+ PSOUT ("* ");
+ pnum (nmsgs);
+ PSOUT (" EXISTS\015\012");
+ }
+ recent = 0xffffffff; /* make sure update recent too */
+ }
+}
+
+
+/* Message expunged
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_expunged (MAILSTREAM *s,unsigned long number)
+{
+ if (quell_events) fatal ("Impossible EXPUNGE event");
+ if (s != tstream) {
+ PSOUT ("* ");
+ pnum (number);
+ PSOUT (" EXPUNGE\015\012");
+ }
+ nmsgs--;
+ existsquelled = T; /* do EXISTS when command done */
+}
+
+
+/* Message status changed
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_flags (MAILSTREAM *s,unsigned long number)
+{
+ if (s != tstream) mail_elt (s,number)->spare2 = T;
+}
+
+/* Mailbox found
+ * Accepts: hierarchy delimiter
+ * mailbox name
+ * attributes
+ */
+
+void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes)
+{
+ mm_list_work ("LIST",delimiter,name,attributes);
+}
+
+
+/* Subscribed mailbox found
+ * Accepts: hierarchy delimiter
+ * mailbox name
+ * attributes
+ */
+
+void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes)
+{
+ mm_list_work ("LSUB",delimiter,name,attributes);
+}
+
+
+/* Mailbox status
+ * Accepts: MAIL stream
+ * mailbox name
+ * mailbox status
+ */
+
+void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
+{
+ if (!quell_events) {
+ char tmp[MAILTMPLEN];
+ tmp[0] = tmp[1] = '\0';
+ if (status->flags & SA_MESSAGES)
+ sprintf (tmp + strlen (tmp)," MESSAGES %lu",status->messages);
+ if (status->flags & SA_RECENT)
+ sprintf (tmp + strlen (tmp)," RECENT %lu",status->recent);
+ if (status->flags & SA_UNSEEN)
+ sprintf (tmp + strlen (tmp)," UNSEEN %lu",status->unseen);
+ if (status->flags & SA_UIDNEXT)
+ sprintf (tmp + strlen (tmp)," UIDNEXT %lu",status->uidnext);
+ if (status->flags & SA_UIDVALIDITY)
+ sprintf (tmp + strlen(tmp)," UIDVALIDITY %lu",status->uidvalidity);
+ PSOUT ("* STATUS ");
+ pastring (mailbox);
+ PSOUT (" (");
+ PSOUT (tmp+1);
+ PBOUT (')');
+ CRLF;
+ }
+}
+
+/* Worker routine for LIST and LSUB
+ * Accepts: name of response
+ * hierarchy delimiter
+ * mailbox name
+ * attributes
+ */
+
+void mm_list_work (char *what,int delimiter,char *name,long attributes)
+{
+ char *s;
+ if (!quell_events) {
+ char tmp[MAILTMPLEN];
+ if (finding) {
+ PSOUT ("* MAILBOX ");
+ PSOUT (name);
+ }
+ /* new form */
+ else if ((cmd[0] == 'R') || !(attributes & LATT_REFERRAL)) {
+ PSOUT ("* ");
+ PSOUT (what);
+ PSOUT (" (");
+ tmp[0] = tmp[1] = '\0';
+ if (attributes & LATT_NOINFERIORS) strcat (tmp," \\NoInferiors");
+ if (attributes & LATT_NOSELECT) strcat (tmp," \\NoSelect");
+ if (attributes & LATT_MARKED) strcat (tmp," \\Marked");
+ if (attributes & LATT_UNMARKED) strcat (tmp," \\UnMarked");
+ if (attributes & LATT_HASCHILDREN) strcat (tmp," \\HasChildren");
+ if (attributes & LATT_HASNOCHILDREN) strcat (tmp," \\HasNoChildren");
+ PSOUT (tmp+1);
+ switch (delimiter) {
+ case '\\': /* quoted delimiter */
+ case '"':
+ PSOUT (") \"\\");
+ PBOUT (delimiter);
+ PBOUT ('"');
+ break;
+ case '\0': /* no delimiter */
+ PSOUT (") NIL");
+ break;
+ default: /* unquoted delimiter */
+ PSOUT (") \"");
+ PBOUT (delimiter);
+ PBOUT ('"');
+ break;
+ }
+ PBOUT (' ');
+ /* output mailbox name */
+ if (proxylist && (s = strchr (name,'}'))) pastring (s+1);
+ else pastring (name);
+ }
+ CRLF;
+ }
+}
+
+/* Notification event
+ * Accepts: MAIL stream
+ * string to log
+ * error flag
+ */
+
+void mm_notify (MAILSTREAM *stream,char *string,long errflg)
+{
+ SIZEDTEXT msg;
+ char *s,*code;
+ if (!quell_events && (!tstream || (stream != tstream))) {
+ switch (errflg) {
+ case NIL: /* information message, set as OK response */
+ if ((string[0] == '[') &&
+ ((string[1] == 'T') || (string[1] == 't')) &&
+ ((string[2] == 'R') || (string[2] == 'r')) &&
+ ((string[3] == 'Y') || (string[3] == 'y')) &&
+ ((string[4] == 'C') || (string[4] == 'c')) &&
+ ((string[5] == 'R') || (string[5] == 'r')) &&
+ ((string[6] == 'E') || (string[6] == 'e')) &&
+ ((string[7] == 'A') || (string[7] == 'a')) &&
+ ((string[8] == 'T') || (string[8] == 't')) &&
+ ((string[9] == 'E') || (string[9] == 'e')) && (string[10] == ']'))
+ trycreate = T;
+ case BYE: /* some other server signing off */
+ case PARSE: /* parse glitch, output unsolicited OK */
+ code = "* OK ";
+ break;
+ case WARN: /* warning, output unsolicited NO (kludge!) */
+ code = "* NO ";
+ break;
+ case ERROR: /* error that broke command */
+ default: /* default should never happen */
+ code = "* BAD ";
+ break;
+ }
+ PSOUT (code);
+ msg.size = (s = strpbrk ((char *) (msg.data = (unsigned char *) string),
+ "\015\012")) ?
+ (s - string) : strlen (string);
+ PSOUTR (&msg);
+ CRLF;
+ PFLUSH (); /* let client see it immediately */
+ }
+}
+
+/* Log an event for the user to see
+ * Accepts: string to log
+ * error flag
+ */
+
+void mm_log (char *string,long errflg)
+{
+ SIZEDTEXT msg;
+ char *s;
+ msg.size =
+ (s = strpbrk ((char *) (msg.data = (unsigned char *) string),"\015\012")) ?
+ (s - string) : strlen (string);
+ switch (errflg) {
+ case NIL: /* information message, set as OK response */
+ if (response == win) { /* only if no other response yet */
+ if (lsterr) { /* if there was a previous message */
+ if (!quell_events) {
+ PSOUT ("* OK "); /* blat it out */
+ PSOUT (lsterr);
+ CRLF;
+ PFLUSH (); /* let client see it immediately */
+ }
+ fs_give ((void **) &lsterr);
+ }
+ lsterr = cpystr (string); /* copy string for later use */
+ if (s) lsterr[s - string] = NIL;
+ }
+ break;
+ case PARSE: /* parse glitch, output unsolicited OK */
+ if (!quell_events) {
+ PSOUT ("* OK [PARSE] ");
+ PSOUTR (&msg);
+ CRLF;
+ PFLUSH (); /* let client see it immediately */
+ }
+ break;
+ case WARN: /* warning, output unsolicited NO */
+ /* ignore "Mailbox is empty" (KLUDGE!) */
+ if (strcmp (string,"Mailbox is empty")) {
+ if (lstwrn) { /* have previous warning? */
+ if (!quell_events) {
+ PSOUT ("* NO ");
+ PSOUT (lstwrn);
+ CRLF;
+ PFLUSH (); /* make sure client sees it immediately */
+ }
+ fs_give ((void **) &lstwrn);
+ }
+ lstwrn = cpystr (string); /* note last warning */
+ if (s) lstwrn[s - string] = NIL;
+ }
+ break;
+ case ERROR: /* error that broke command */
+ default: /* default should never happen */
+ response = trycreate ? losetry : lose;
+ if (lsterr) fs_give ((void **) &lsterr);
+ lsterr = cpystr (string); /* note last error */
+ if (s) lsterr[s - string] = NIL;
+ break;
+ }
+}
+
+/* Return last error
+ */
+
+char *lasterror (void)
+{
+ if (lsterr) return lsterr;
+ if (lstwrn) return lstwrn;
+ return "<unknown>";
+}
+
+
+/* Log an event to debugging telemetry
+ * Accepts: string to log
+ */
+
+void mm_dlog (char *string)
+{
+ mm_log (string,WARN); /* shouldn't happen normally */
+}
+
+/* Get user name and password for this host
+ * Accepts: parse of network user name
+ * where to return user name
+ * where to return password
+ * trial count
+ */
+
+void mm_login (NETMBX *mb,char *username,char *password,long trial)
+{
+ /* set user name */
+ strncpy (username,*mb->user ? mb->user : (char *) user,NETMAXUSER);
+ strncpy (password,pass,256); /* and password */
+}
+
+
+/* About to enter critical code
+ * Accepts: stream
+ */
+
+void mm_critical (MAILSTREAM *s)
+{
+ ++critical;
+}
+
+
+/* About to exit critical code
+ * Accepts: stream
+ */
+
+void mm_nocritical (MAILSTREAM *s)
+{
+ /* go non-critical, pending death? */
+ if (!--critical && (state == LOGOUT)) {
+ /* clean up iff needed */
+ if (s && (stream != s) && !s->lock && (s->dtb->flags & DR_XPOINT))
+ s = mail_close (s);
+ longjmp (jmpenv,1); /* die now */
+ }
+}
+
+/* Disk error found
+ * Accepts: stream
+ * system error code
+ * flag indicating that mailbox may be clobbered
+ * Returns: abort flag
+ */
+
+long mm_diskerror (MAILSTREAM *s,long errcode,long serious)
+{
+ if (serious) { /* try your damnest if clobberage likely */
+ mm_notify (s,"Retrying to fix probable mailbox damage!",ERROR);
+ PFLUSH (); /* dump output buffer */
+ syslog (LOG_ALERT,
+ "Retrying after disk error user=%.80s host=%.80s mbx=%.80s: %.80s",
+ user ? (char *) user : "???",tcp_clienthost (),
+ (stream && stream->mailbox) ? stream->mailbox : "???",
+ strerror (errcode));
+ settimeout (0); /* make damn sure timeout disabled */
+ sleep (60); /* give it some time to clear up */
+ return NIL;
+ }
+ if (!quell_events) { /* otherwise die before more damage is done */
+ PSOUT ("* NO Disk error: ");
+ PSOUT (strerror (errcode));
+ CRLF;
+ }
+ return T;
+}
+
+
+/* Log a fatal error event
+ * Accepts: string to log
+ */
+
+void mm_fatal (char *string)
+{
+ SIZEDTEXT msg;
+ char *s;
+ msg.size =
+ (s = strpbrk ((char *) (msg.data = (unsigned char *) string),"\015\012")) ?
+ (s - string) : strlen (string);
+ if (!quell_events) {
+ PSOUT ("* BYE [ALERT] IMAP4rev1 server crashing: ");
+ PSOUTR (&msg);
+ CRLF;
+ PFLUSH ();
+ }
+ syslog (LOG_ALERT,"Fatal error user=%.80s host=%.80s mbx=%.80s: %.80s",
+ user ? (char *) user : "???",tcp_clienthost (),
+ (stream && stream->mailbox) ? stream->mailbox : "???",string);
+}
diff --git a/imap/src/imapd/makefile.nt b/imap/src/imapd/makefile.nt
new file mode 100644
index 00000000..41216701
--- /dev/null
+++ b/imap/src/imapd/makefile.nt
@@ -0,0 +1,55 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: IMAPD Makefile for Windows 9x and Windows NT
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 5 November 1990
+# Last Edited: 30 August 2006
+
+
+ALERT=\\imapd.alert
+USERALERT=alert.txt
+SHUTDOWN=\\nologin
+ANO=\\anonymous.newsgroups
+NNTP=\\imapd.nntp
+
+C = ..\c-client
+CCLIENTLIB = $C\cclient.lib
+LIBS = $(CCLIENTLIB) ws2_32.lib winmm.lib advapi32.lib
+OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0400
+VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE
+CFLAGS= -I$C /MT /W3 $(OSCOMPAT) $(VSCOMPAT) -nologo $(EXTRACFLAGS) -DALERTFILE=\"$(ALERT)\" -DNNTPFILE=\"$(NNTP)\" -DUSERALERTFILE=\"$(USERALERT)\" -DANOFILE=\"$(ANO)\" -DSHUTDOWNFILE=\"$(SHUTDOWN)\"
+
+imapd: $(CCLIENTLIB) imapd.obj
+ LINK /NOLOGO imapd.obj $(LIBS)
+
+imapd.obj: $C\mail.h $C\misc.h $C\osdep.h
+
+$(CCLIENTLIB):
+ @echo Make c-client first
+ false
+
+clean:
+ del *.obj *.exe *.lib *.exp || rem
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo not war?
diff --git a/imap/src/imapd/makefile.ntk b/imap/src/imapd/makefile.ntk
new file mode 100644
index 00000000..5c5500e6
--- /dev/null
+++ b/imap/src/imapd/makefile.ntk
@@ -0,0 +1,58 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: IMAPD Makefile for Windows 9x and Windows NT + Kerberos
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 5 November 1990
+# Last Edited: 30 August 2006
+
+
+ALERT=\\imapd.alert
+USERALERT=alert.txt
+SHUTDOWN=\\nologin
+ANO=\\anonymous.newsgroups
+NNTP=\\imapd.nntp
+
+
+C = ..\c-client
+CCLIENTLIB = $C\cclient.lib
+K5 = \k5\lib
+K5LIB = $(K5)\comerr32.lib $(K5)\gssapi32.lib $(K5)\krb5_32.lib
+LIBS = $(CCLIENTLIB) $(K5LIB) ws2_32.lib winmm.lib advapi32.lib
+OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0400
+VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE
+CFLAGS= -I$C /MT /W3 $(OSCOMPAT) $(VSCOMPAT) -nologo $(EXTRACFLAGS) -DALERTFILE=\"$(ALERT)\" -DNNTPFILE=\"$(NNTP)\" -DUSERALERTFILE=\"$(USERALERT)\" -DANOFILE=\"$(ANO)\" -DSHUTDOWNFILE=\"$(SHUTDOWN)\"
+
+imapd: $(CCLIENTLIB) imapd.obj
+ LINK /NOLOGO imapd.obj $(LIBS)
+
+imapd.obj: $C\mail.h $C\misc.h $C\osdep.h
+
+$(CCLIENTLIB):
+ @echo Make c-client first
+ false
+
+clean:
+ del *.obj *.exe *.lib *.exp || rem
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo not war?
diff --git a/imap/src/imapd/makefile.w2k b/imap/src/imapd/makefile.w2k
new file mode 100644
index 00000000..e10b8271
--- /dev/null
+++ b/imap/src/imapd/makefile.w2k
@@ -0,0 +1,56 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: IMAPD Makefile for Windows 2000/XP
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 5 November 1990
+# Last Edited: 30 August 2006
+
+
+ALERT=\\imapd.alert
+USERALERT=alert.txt
+SHUTDOWN=\\nologin
+ANO=\\anonymous.newsgroups
+NNTP=\\imapd.nntp
+
+
+C = ..\c-client
+CCLIENTLIB = $C\cclient.lib
+LIBS = $(CCLIENTLIB) ws2_32.lib winmm.lib advapi32.lib secur32.lib crypt32.lib
+OSCOMPAT = /DWIN32
+VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE
+CFLAGS= -I$C /MT /W3 $(OSCOMPAT) $(VSCOMPAT) -nologo $(EXTRACFLAGS) -DALERTFILE=\"$(ALERT)\" -DNNTPFILE=\"$(NNTP)\" -DUSERALERTFILE=\"$(USERALERT)\" -DANOFILE=\"$(ANO)\" -DSHUTDOWNFILE=\"$(SHUTDOWN)\"
+
+imapd: $(CCLIENTLIB) imapd.obj
+ LINK /NOLOGO imapd.obj $(LIBS)
+
+imapd.obj: $C\mail.h $C\misc.h $C\osdep.h
+
+$(CCLIENTLIB):
+ @echo Make c-client first
+ false
+
+clean:
+ del *.obj *.exe *.lib *.exp || rem
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo not war?
diff --git a/imap/src/ipopd/Makefile b/imap/src/ipopd/Makefile
new file mode 100644
index 00000000..accb5408
--- /dev/null
+++ b/imap/src/ipopd/Makefile
@@ -0,0 +1,58 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: IPOPD client Makefile
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 28 October 1990
+# Last Edited: 30 August 2006
+
+
+C = ../c-client
+CCLIENTLIB = $C/c-client.a
+SHELL = /bin/sh
+
+# Get local definitions from c-client directory
+
+CC = `cat $C/CCTYPE`
+CFLAGS = -I$C `cat $C/CFLAGS`
+LDFLAGS = $(CCLIENTLIB) `cat $C/LDFLAGS`
+
+ipopd: ipop2d ipop3d
+
+ipop2d: $(CCLIENTLIB) ipop2d.o
+ $(CC) $(CFLAGS) -o ipop2d ipop2d.o $(LDFLAGS)
+
+ipop3d: $(CCLIENTLIB) ipop3d.o
+ $(CC) $(CFLAGS) -o ipop3d ipop3d.o $(LDFLAGS)
+
+ipop2d.o: $C/mail.h $C/misc.h $C/osdep.h
+
+ipop3d.o: $C/mail.h $C/misc.h $C/osdep.h
+
+$(CCLIENTLIB):
+ cd $C;make
+
+clean:
+ rm -f *.o ipop2d ipop3d || true
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo 'not war?'
diff --git a/imap/src/ipopd/ipop2d.c b/imap/src/ipopd/ipop2d.c
new file mode 100644
index 00000000..14cbe52f
--- /dev/null
+++ b/imap/src/ipopd/ipop2d.c
@@ -0,0 +1,711 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: IPOP2D - IMAP to POP2 conversion server
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@Washington.EDU
+ *
+ * Date: 28 October 1990
+ * Last Edited: 13 February 2008
+ */
+
+
+/* Parameter files */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <signal.h>
+#include <time.h>
+#include "c-client.h"
+
+
+/* Autologout timer */
+#define KODTIMEOUT 60*5
+#define LOGINTIMEOUT 60*3
+#define TIMEOUT 60*30
+
+
+/* Size of temporary buffers */
+#define TMPLEN 1024
+
+
+/* Server states */
+
+#define LISN 0
+#define AUTH 1
+#define MBOX 2
+#define ITEM 3
+#define NEXT 4
+#define DONE 5
+
+/* Global storage */
+
+char *version = "75"; /* edit number of this server */
+short state = LISN; /* server state */
+short critical = NIL; /* non-zero if in critical code */
+MAILSTREAM *stream = NIL; /* mailbox stream */
+time_t idletime = 0; /* time we went idle */
+unsigned long nmsgs = 0; /* number of messages */
+unsigned long current = 1; /* current message number */
+unsigned long size = 0; /* size of current message */
+char status[MAILTMPLEN]; /* space for status string */
+char *user = ""; /* user name */
+char *pass = ""; /* password */
+unsigned long *msg = NIL; /* message translation vector */
+char *logout = "Logout";
+char *goodbye = "+ Sayonara\015\012";
+
+
+/* Function prototypes */
+
+int main (int argc,char *argv[]);
+void sayonara (int status);
+void clkint ();
+void kodint ();
+void hupint ();
+void trmint ();
+short c_helo (char *t,int argc,char *argv[]);
+short c_fold (char *t);
+short c_read (char *t);
+short c_retr (char *t);
+short c_acks (char *t);
+short c_ackd (char *t);
+short c_nack (char *t);
+
+/* Main program */
+
+int main (int argc,char *argv[])
+{
+ char *s,*t;
+ char cmdbuf[TMPLEN];
+ char *pgmname = (argc && argv[0]) ?
+ (((s = strrchr (argv[0],'/')) || (s = strrchr (argv[0],'\\'))) ?
+ s+1 : argv[0]) : "ipop2d";
+ /* set service name before linkage */
+ mail_parameters (NIL,SET_SERVICENAME,(void *) "pop");
+#include "linkage.c"
+ if (mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL)) {
+ goodbye = "- POP2 server disabled on this system\015\012";
+ sayonara (1);
+ }
+ /* initialize server */
+ server_init (pgmname,"pop",NIL,clkint,kodint,hupint,trmint,NIL);
+ /* There are reports of POP2 clients which get upset if anything appears
+ * between the "+" and the "POP2" in the greeting.
+ */
+ printf ("+ POP2 %s %s.%s server ready\015\012",tcp_serverhost (),
+ CCLIENTVERSION,version);
+ fflush (stdout); /* dump output buffer */
+ state = AUTH; /* initial server state */
+ while (state != DONE) { /* command processing loop */
+ idletime = time (0); /* get a command under timeout */
+ alarm ((state != AUTH) ? TIMEOUT : LOGINTIMEOUT);
+ clearerr (stdin); /* clear stdin errors */
+ while (!fgets (cmdbuf,TMPLEN-1,stdin)) {
+ if (ferror (stdin) && (errno == EINTR)) clearerr (stdin);
+ else {
+ char *e = ferror (stdin) ?
+ strerror (errno) : "Unexpected client disconnect";
+ alarm (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ sprintf (logout = cmdbuf,"%.80s while reading line",e);
+ state = DONE;
+ stream = mail_close (stream);
+ goodbye = NIL;
+ sayonara (1);
+ }
+ }
+ alarm (0); /* make sure timeout disabled */
+ idletime = 0; /* no longer idle */
+ /* find end of line */
+ if (!strchr (cmdbuf,'\012')) {
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ logout = "- Command line too long\015\012";
+ state = DONE;
+ }
+ else if (!(s = strtok (cmdbuf," \015\012"))) {
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ goodbye = "- Missing or null command\015\012";
+ state = DONE;
+ }
+ else { /* dispatch based on command */
+ ucase (s); /* canonicalize case */
+ /* snarf argument */
+ t = strtok (NIL,"\015\012");
+ if ((state == AUTH) && !strcmp (s,"HELO")) state = c_helo (t,argc,argv);
+ else if ((state == MBOX || state == ITEM) && !strcmp (s,"FOLD"))
+ state = c_fold (t);
+ else if ((state == MBOX || state == ITEM) && !strcmp (s,"READ"))
+ state = c_read (t);
+ else if ((state == ITEM) && !strcmp (s,"RETR")) state = c_retr (t);
+ else if ((state == NEXT) && !strcmp (s,"ACKS")) state = c_acks (t);
+ else if ((state == NEXT) && !strcmp (s,"ACKD")) state = c_ackd (t);
+ else if ((state == NEXT) && !strcmp (s,"NACK")) state = c_nack (t);
+ else if ((state == AUTH || state == MBOX || state == ITEM) &&
+ !strcmp (s,"QUIT")) {
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ state = DONE; /* done in either case */
+ if (t) goodbye = "- Bogus argument given to QUIT\015\012";
+ else { /* expunge the stream */
+ if (stream && nmsgs) stream = mail_close_full (stream,CL_EXPUNGE);
+ stream = NIL; /* don't repeat it */
+ }
+ }
+ else { /* some other or inappropriate command */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ goodbye = "- Bogus or out of sequence command\015\012";
+ state = DONE;
+ }
+ }
+ fflush (stdout); /* make sure output blatted */
+ }
+ /* clean up the stream */
+ if (stream) mail_close (stream);
+ sayonara (0);
+ return 0; /* stupid compilers */
+}
+
+
+/* Say goodbye
+ * Accepts: exit status
+ *
+ * Does not return
+ */
+
+void sayonara (int status)
+{
+ logouthook_t lgoh = (logouthook_t) mail_parameters (NIL,GET_LOGOUTHOOK,NIL);
+ if (goodbye) { /* have a goodbye message? */
+ fputs (goodbye,stdout);
+ fflush (stdout); /* make sure blatted */
+ }
+ syslog (LOG_INFO,"%s user=%.80s host=%.80s",logout,
+ user ? (char *) user : "???",tcp_clienthost ());
+ /* do logout hook if needed */
+ if (lgoh) (*lgoh) (mail_parameters (NIL,GET_LOGOUTDATA,NIL));
+ _exit (status); /* all done */
+}
+
+/* Clock interrupt
+ */
+
+void clkint ()
+{
+ alarm (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ goodbye = "- Autologout; idle for too long\015\012";
+ logout = "Autologout";
+ state = DONE; /* mark state done in either case */
+ if (!critical) { /* badly host if in critical code */
+ if (stream && !stream->lock) mail_close (stream);
+ stream = NIL;
+ sayonara (1); /* die die die */
+ }
+}
+
+
+/* Kiss Of Death interrupt
+ */
+
+void kodint ()
+{
+ /* only if in command wait */
+ if (idletime && ((time (0) - idletime) > KODTIMEOUT)) {
+ alarm (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ goodbye = "- Killed (lost mailbox lock)\015\012";
+ logout = "Killed (lost mailbox lock)";
+ state = DONE; /* mark state done in either case */
+ if (!critical) { /* badly host if in critical code */
+ if (stream && !stream->lock) mail_close (stream);
+ stream = NIL;
+ sayonara (1); /* die die die */
+ }
+ }
+}
+
+
+/* Hangup interrupt
+ */
+
+void hupint ()
+{
+ alarm (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ goodbye = NIL;
+ logout = "Hangup";
+ state = DONE; /* mark state done in either case */
+ if (!critical) { /* badly host if in critical code */
+ if (stream && !stream->lock) mail_close (stream);
+ stream = NIL;
+ sayonara (1); /* die die die */
+ }
+}
+
+
+/* Termination interrupt
+ */
+
+void trmint ()
+{
+ alarm (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ goodbye = "- Killed (terminated)\015\012";
+ logout = "Killed (terminated)";
+ if (critical) state = DONE; /* mark state done in either case */
+ /* Make no attempt at graceful closure since a shutdown may be in
+ * progress, and we won't have any time to do mail_close() actions.
+ */
+ else sayonara (1); /* die die die */
+}
+
+/* Parse HELO command
+ * Accepts: pointer to command argument
+ * Returns: new state
+ */
+
+short c_helo (char *t,int argc,char *argv[])
+{
+ char *s,*u,*p;
+ char tmp[TMPLEN];
+ if ((!(t && *t && (u = strtok (t," ")) && (p = strtok (NIL,"\015\012")))) ||
+ (strlen (p) >= TMPLEN)) { /* get user name and password */
+ fputs ("- Missing user or password\015\012",stdout);
+ return DONE;
+ }
+ /* copy password, handle quoting */
+ for (s = tmp; *p; p++) *s++ = (*p == '\\') ? *++p : *p;
+ *s = '\0'; /* tie off string */
+ pass = cpystr (tmp);
+ if (!(s = strchr (u,':'))) { /* want remote mailbox? */
+ /* no, delimit user from possible admin */
+ if (s = strchr (u,'*')) *s++ = '\0';
+ if (server_login (user = cpystr (u),pass,s,argc,argv)) {
+ syslog (LOG_INFO,"%sLogin user=%.80s host=%.80s",s ? "Admin " : "",
+ user,tcp_clienthost ());
+ return c_fold ("INBOX"); /* local; select INBOX */
+ }
+ }
+#ifndef DISABLE_POP_PROXY
+ /* can't do if can't log in as anonymous */
+ else if (anonymous_login (argc,argv)) {
+ *s++ = '\0'; /* separate host name from user name */
+ user = cpystr (s); /* note user name */
+ syslog (LOG_INFO,"IMAP login to host=%.80s user=%.80s host=%.80s",u,user,
+ tcp_clienthost ());
+ /* initially remote INBOX */
+ sprintf (tmp,"{%.128s/user=%.128s}INBOX",u,user);
+ /* disable rimap just in case */
+ mail_parameters (NIL,SET_RSHTIMEOUT,0);
+ return c_fold (tmp);
+ }
+#endif
+ fputs ("- Bad login\015\012",stdout);
+ return DONE;
+}
+
+/* Parse FOLD command
+ * Accepts: pointer to command argument
+ * Returns: new state
+ */
+
+short c_fold (char *t)
+{
+ unsigned long i,j,flags;
+ char *s = NIL,tmp[2*TMPLEN];
+ NETMBX mb;
+ if (!(t && *t)) { /* make sure there's an argument */
+ fputs ("- Missing mailbox name\015\012",stdout);
+ return DONE;
+ }
+ myusername_full (&flags); /* get user type flags */
+ /* expunge old stream */
+ if (stream && nmsgs) mail_expunge (stream);
+ nmsgs = 0; /* no more messages */
+ if (msg) fs_give ((void **) &msg);
+#ifndef DISABLE_POP_PROXY
+ if (flags == MU_ANONYMOUS) { /* don't permit proxy to leave IMAP */
+ if (stream) { /* not first time */
+ if (!(stream->mailbox && (s = strchr (stream->mailbox,'}'))))
+ fatal ("bad previous mailbox name");
+ strncpy (tmp,stream->mailbox,i = (++s - stream->mailbox));
+ if (i >= TMPLEN) fatal ("ridiculous network prefix");
+ strcpy (tmp+i,t); /* append mailbox to initial spec */
+ t = tmp;
+ }
+ /* must be net name first time */
+ else if (!mail_valid_net_parse (t,&mb)) fatal ("anonymous folder bogon");
+ }
+#endif
+ /* open mailbox, note # of messages */
+ if (j = (stream = mail_open (stream,t,NIL)) ? stream->nmsgs : 0) {
+ sprintf (tmp,"1:%lu",j); /* fetch fast information for all messages */
+ mail_fetch_fast (stream,tmp,NIL);
+ msg = (unsigned long *) fs_get ((stream->nmsgs + 1) *
+ sizeof (unsigned long));
+ for (i = 1; i <= j; i++) /* find undeleted messages, add to vector */
+ if (!mail_elt (stream,i)->deleted) msg[++nmsgs] = i;
+ }
+#ifndef DISABLE_POP_PROXY
+ if (!stream && (flags == MU_ANONYMOUS)) {
+ fputs ("- Bad login\015\012",stdout);
+ return DONE;
+ }
+#endif
+ printf ("#%lu messages in %s\015\012",nmsgs,stream ? stream->mailbox :
+ "<none>");
+ return MBOX;
+}
+
+/* Parse READ command
+ * Accepts: pointer to command argument
+ * Returns: new state
+ */
+
+short c_read (char *t)
+{
+ MESSAGECACHE *elt = NIL;
+ if (t && *t) { /* have a message number argument? */
+ /* validity check message number */
+ if (((current = strtoul (t,NIL,10)) < 1) || (current > nmsgs)) {
+ fputs ("- Invalid message number given to READ\015\012",stdout);
+ return DONE;
+ }
+ }
+ else if (current > nmsgs) { /* at end of mailbox? */
+ fputs ("=0 No more messages\015\012",stdout);
+ return MBOX;
+ }
+ /* set size if message valid and exists */
+ size = msg[current] ? (elt = mail_elt(stream,msg[current]))->rfc822_size : 0;
+ if (elt) sprintf (status,"Status: %s%s\015\012",
+ elt->seen ? "R" : " ",elt->recent ? " " : "O");
+ else status[0] = '\0'; /* no status */
+ size += strlen (status); /* update size to reflect status */
+ /* display results */
+ printf ("=%lu characters in message %lu\015\012",size + 2,current);
+ return ITEM;
+}
+
+
+/* Parse RETR command
+ * Accepts: pointer to command argument
+ * Returns: new state
+ */
+
+short c_retr (char *t)
+{
+ unsigned long i,j;
+ STRING *bs;
+ if (t) { /* disallow argument */
+ fputs ("- Bogus argument given to RETR\015\012",stdout);
+ return DONE;
+ }
+ if (size) { /* message size valid? */
+ t = mail_fetch_header (stream,msg[current],NIL,NIL,&i,FT_PEEK);
+ if (i > 2) { /* only if there is something */
+ i -= 2; /* lop off last two octets */
+ while (i) { /* blat the header */
+ if (!(j = fwrite (t,sizeof (char),i,stdout))) return DONE;
+ if (i -= j) t += j; /* advance to incomplete data */
+ }
+ }
+ fputs (status,stdout); /* yes, output message */
+ fputs ("\015\012",stdout); /* delimit header from text */
+ if (t = mail_fetch_text (stream,msg[current],NIL,&i,FT_RETURNSTRINGSTRUCT))
+ while (i) { /* blat the text */
+ if (!(j = fwrite (t,sizeof (char),i,stdout))) return DONE;
+ if (i -= j) t += j; /* advance to incomplete data */
+ }
+ else for (bs = &stream->private.string; i--; )
+ if (putc (SNX (bs),stdout) == EOF) return DONE;
+ fputs ("\015\012",stdout); /* trailer to coddle PCNFS' NFSMAIL */
+ }
+ else return DONE; /* otherwise go away */
+ return NEXT;
+}
+
+/* Parse ACKS command
+ * Accepts: pointer to command argument
+ * Returns: new state
+ */
+
+short c_acks (char *t)
+{
+ char tmp[TMPLEN];
+ if (t) { /* disallow argument */
+ fputs ("- Bogus argument given to ACKS\015\012",stdout);
+ return DONE;
+ }
+ /* mark message as seen */
+ sprintf (tmp,"%lu",msg[current++]);
+ mail_setflag (stream,tmp,"\\Seen");
+ return c_read (NIL); /* end message reading transaction */
+}
+
+
+/* Parse ACKD command
+ * Accepts: pointer to command argument
+ * Returns: new state
+ */
+
+short c_ackd (char *t)
+{
+ char tmp[TMPLEN];
+ if (t) { /* disallow argument */
+ fputs ("- Bogus argument given to ACKD\015\012",stdout);
+ return DONE;
+ }
+ /* mark message as seen and deleted */
+ sprintf (tmp,"%lu",msg[current]);
+ mail_setflag (stream,tmp,"\\Seen \\Deleted");
+ msg[current++] = 0; /* mark message as deleted */
+ return c_read (NIL); /* end message reading transaction */
+}
+
+
+/* Parse NACK command
+ * Accepts: pointer to command argument
+ * Returns: new state
+ */
+
+short c_nack (char *t)
+{
+ if (t) { /* disallow argument */
+ fputs ("- Bogus argument given to NACK\015\012",stdout);
+ return DONE;
+ }
+ return c_read (NIL); /* end message reading transaction */
+}
+
+/* Co-routines from MAIL library */
+
+
+/* Message matches a search
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_searched (MAILSTREAM *stream,unsigned long msgno)
+{
+ /* Never called */
+}
+
+
+/* Message exists (i.e. there are that many messages in the mailbox)
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_exists (MAILSTREAM *stream,unsigned long number)
+{
+ /* Can't use this mechanism. POP has no means of notifying the client of
+ new mail during the session. */
+}
+
+
+/* Message expunged
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_expunged (MAILSTREAM *stream,unsigned long number)
+{
+ if (state != DONE) { /* ignore if closing */
+ /* someone else screwed us */
+ goodbye = "- Mailbox expunged from under me!\015\012";
+ if (stream && !stream->lock) mail_close (stream);
+ stream = NIL;
+ sayonara (1);
+ }
+}
+
+
+/* Message status changed
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_flags (MAILSTREAM *stream,unsigned long number)
+{
+ /* This isn't used */
+}
+
+
+/* Mailbox found
+ * Accepts: MAIL stream
+ * hierarchy delimiter
+ * mailbox name
+ * mailbox attributes
+ */
+
+void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes)
+{
+ /* This isn't used */
+}
+
+
+/* Subscribe mailbox found
+ * Accepts: MAIL stream
+ * hierarchy delimiter
+ * mailbox name
+ * mailbox attributes
+ */
+
+void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes)
+{
+ /* This isn't used */
+}
+
+
+/* Mailbox status
+ * Accepts: MAIL stream
+ * mailbox name
+ * mailbox status
+ */
+
+void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
+{
+ /* This isn't used */
+}
+
+/* Notification event
+ * Accepts: MAIL stream
+ * string to log
+ * error flag
+ */
+
+void mm_notify (MAILSTREAM *stream,char *string,long errflg)
+{
+ mm_log (string,errflg); /* just do mm_log action */
+}
+
+
+/* Log an event for the user to see
+ * Accepts: string to log
+ * error flag
+ */
+
+void mm_log (char *string,long errflg)
+{
+ switch (errflg) {
+ case NIL: /* information message */
+ case PARSE: /* parse glitch */
+ break; /* too many of these to log */
+ case WARN: /* warning */
+ syslog (LOG_DEBUG,"%s",string);
+ break;
+ case BYE: /* driver broke connection */
+ if (state != DONE) {
+ char tmp[MAILTMPLEN];
+ alarm (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ sprintf (logout = tmp,"Mailbox closed (%.80s)",string);
+ sayonara (1);
+ }
+ break;
+ case ERROR: /* error that broke command */
+ default: /* default should never happen */
+ syslog (LOG_NOTICE,"%s",string);
+ break;
+ }
+}
+
+
+/* Log an event to debugging telemetry
+ * Accepts: string to log
+ */
+
+void mm_dlog (char *string)
+{
+ /* Not doing anything here for now */
+}
+
+
+/* Get user name and password for this host
+ * Accepts: parse of network mailbox name
+ * where to return user name
+ * where to return password
+ * trial count
+ */
+
+void mm_login (NETMBX *mb,char *username,char *password,long trial)
+{
+ /* set user name */
+ strncpy (username,*mb->user ? mb->user : user,NETMAXUSER-1);
+ strncpy (password,pass,255); /* and password */
+ username[NETMAXUSER] = password[255] = '\0';
+}
+
+/* About to enter critical code
+ * Accepts: stream
+ */
+
+void mm_critical (MAILSTREAM *stream)
+{
+ ++critical;
+}
+
+
+/* About to exit critical code
+ * Accepts: stream
+ */
+
+void mm_nocritical (MAILSTREAM *stream)
+{
+ --critical;
+}
+
+
+/* Disk error found
+ * Accepts: stream
+ * system error code
+ * flag indicating that mailbox may be clobbered
+ * Returns: abort flag
+ */
+
+long mm_diskerror (MAILSTREAM *stream,long errcode,long serious)
+{
+ if (serious) { /* try your damnest if clobberage likely */
+ syslog (LOG_ALERT,
+ "Retrying after disk error user=%.80s host=%.80s mbx=%.80s: %.80s",
+ user,tcp_clienthost (),
+ (stream && stream->mailbox) ? stream->mailbox : "???",
+ strerror (errcode));
+ alarm (0); /* make damn sure timeout disabled */
+ sleep (60); /* give it some time to clear up */
+ return NIL;
+ }
+ syslog (LOG_ALERT,"Fatal disk error user=%.80s host=%.80s mbx=%.80s: %.80s",
+ user,tcp_clienthost (),
+ (stream && stream->mailbox) ? stream->mailbox : "???",
+ strerror (errcode));
+ return T;
+}
+
+
+/* Log a fatal error event
+ * Accepts: string to log
+ */
+
+void mm_fatal (char *string)
+{
+ mm_log (string,ERROR); /* shouldn't happen normally */
+}
diff --git a/imap/src/ipopd/ipop3d.c b/imap/src/ipopd/ipop3d.c
new file mode 100644
index 00000000..41dd96a6
--- /dev/null
+++ b/imap/src/ipopd/ipop3d.c
@@ -0,0 +1,1082 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: IPOP3D - IMAP to POP3 conversion server
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@Washington.EDU
+ *
+ * Date: 1 November 1990
+ * Last Edited: 19 February 2008
+ */
+
+/* Parameter files */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <signal.h>
+#include <time.h>
+#include "c-client.h"
+
+
+#define CRLF PSOUT ("\015\012") /* primary output terpri */
+
+
+/* Autologout timer */
+#define KODTIMEOUT 60*5
+#define LOGINTIMEOUT 60*3
+#define TIMEOUT 60*10
+
+
+/* Server states */
+
+#define AUTHORIZATION 0
+#define TRANSACTION 1
+#define UPDATE 2
+#define LOGOUT 3
+
+/* Eudora food */
+
+#define STATUS "Status: %s%s\015\012"
+#define SLEN (sizeof (STATUS)-3)
+
+
+/* Global storage */
+
+char *version = "104"; /* edit number of this server */
+short state = AUTHORIZATION; /* server state */
+short critical = NIL; /* non-zero if in critical code */
+MAILSTREAM *stream = NIL; /* mailbox stream */
+time_t idletime = 0; /* time we went idle */
+unsigned long nmsgs = 0; /* current number of messages */
+unsigned long ndele = 0; /* number of deletes */
+unsigned long nseen = 0; /* number of mark-seens */
+unsigned long last = 0; /* highest message accessed */
+unsigned long il = 0; /* initial last message */
+char challenge[128]; /* challenge */
+char *host = NIL; /* remote host name */
+char *user = NIL; /* user name */
+char *pass = NIL; /* password */
+char *initial = NIL; /* initial response */
+long *msg = NIL; /* message translation vector */
+short *flags = NIL; /* flags */
+char *logout = "Logout";
+char *goodbye = "+OK Sayonara\015\012";
+
+
+/* POP3 flags */
+
+#define DELE 0x1
+#define SEEN 0x2
+
+
+/* Function prototypes */
+
+int main (int argc,char *argv[]);
+void sayonara (int status);
+void clkint ();
+void kodint ();
+void hupint ();
+void trmint ();
+int pass_login (char *t,int argc,char *argv[]);
+char *apop_login (char *chal,char *user,char *md5,int argc,char *argv[]);
+char *responder (void *challenge,unsigned long clen,unsigned long *rlen);
+int mbxopen (char *mailbox);
+long blat (char *text,long lines,unsigned long size,STRING *st);
+void rset ();
+
+/* Main program */
+
+int main (int argc,char *argv[])
+{
+ unsigned long i,j,k;
+ char *s,*t;
+ char tmp[MAILTMPLEN];
+ time_t autologouttime;
+ char *pgmname = (argc && argv[0]) ?
+ (((s = strrchr (argv[0],'/')) || (s = strrchr (argv[0],'\\'))) ?
+ s+1 : argv[0]) : "ipop3d";
+ /* set service name before linkage */
+ mail_parameters (NIL,SET_SERVICENAME,(void *) "pop");
+#include "linkage.c"
+ /* initialize server */
+ server_init (pgmname,"pop3","pop3s",clkint,kodint,hupint,trmint,NIL);
+ mail_parameters (NIL,SET_BLOCKENVINIT,VOIDT);
+ s = myusername_full (&i); /* get user name and flags */
+ mail_parameters (NIL,SET_BLOCKENVINIT,NIL);
+ if (i == MU_LOGGEDIN) { /* allow EXTERNAL if logged in already */
+ mail_parameters (NIL,UNHIDE_AUTHENTICATOR,(void *) "EXTERNAL");
+ mail_parameters (NIL,SET_EXTERNALAUTHID,(void *) s);
+ }
+ { /* set up MD5 challenge */
+ AUTHENTICATOR *auth = mail_lookup_auth (1);
+ while (auth && compare_cstring (auth->name,"CRAM-MD5")) auth = auth->next;
+ /* build challenge -- less than 128 chars */
+ if (auth && auth->server && !(auth->flags & AU_DISABLE))
+ sprintf (challenge,"<%lx.%lx@%.64s>",(unsigned long) getpid (),
+ (unsigned long) time (0),tcp_serverhost ());
+ else challenge[0] = '\0'; /* no MD5 authentication */
+ }
+ /* There are reports of POP3 clients which get upset if anything appears
+ * between the "+OK" and the "POP3" in the greeting.
+ */
+ PSOUT ("+OK POP3 ");
+ if (!challenge[0]) { /* if no MD5 enable, output host name */
+ PSOUT (tcp_serverhost ());
+ PBOUT (' ');
+ }
+ PSOUT (CCLIENTVERSION);
+ PBOUT ('.');
+ PSOUT (version);
+ PSOUT (" server ready");
+ if (challenge[0]) { /* if MD5 enable, output challenge here */
+ PBOUT (' ');
+ PSOUT (challenge);
+ }
+ CRLF;
+ PFLUSH (); /* dump output buffer */
+ autologouttime = time (0) + LOGINTIMEOUT;
+ /* command processing loop */
+ while ((state != UPDATE) && (state != LOGOUT)) {
+ idletime = time (0); /* get a command under timeout */
+ alarm ((state == TRANSACTION) ? TIMEOUT : LOGINTIMEOUT);
+ clearerr (stdin); /* clear stdin errors */
+ /* read command line */
+ while (!PSIN (tmp,MAILTMPLEN)) {
+ /* ignore if some interrupt */
+ if (ferror (stdin) && (errno == EINTR)) clearerr (stdin);
+ else {
+ char *e = ferror (stdin) ?
+ strerror (errno) : "Unexpected client disconnect";
+ alarm (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ sprintf (logout = tmp,"%.80s, while reading line",e);
+ goodbye = NIL;
+ rset (); /* try to gracefully close the stream */
+ if (state == TRANSACTION) mail_close (stream);
+ stream = NIL;
+ state = LOGOUT;
+ sayonara (1);
+ }
+ }
+ alarm (0); /* make sure timeout disabled */
+ idletime = 0; /* no longer idle */
+
+ if (!strchr (tmp,'\012')) /* find end of line */
+ PSOUT ("-ERR Command line too long\015\012");
+ else if (!(s = strtok (tmp," \015\012")))
+ PSOUT ("-ERR Null command\015\012");
+ else { /* dispatch based on command */
+ ucase (s); /* canonicalize case */
+ /* snarf argument */
+ t = strtok (NIL,"\015\012");
+ /* QUIT command always valid */
+ if (!strcmp (s,"QUIT")) state = UPDATE;
+ else if (!strcmp (s,"CAPA")) {
+ AUTHENTICATOR *auth;
+ PSOUT ("+OK Capability list follows:\015\012");
+ PSOUT ("TOP\015\012LOGIN-DELAY 180\015\012UIDL\015\012");
+ if (s = ssl_start_tls (NIL)) fs_give ((void **) &s);
+ else PSOUT ("STLS\015\012");
+ if (i = !mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL))
+ PSOUT ("USER\015\012");
+ /* display secure server authenticators */
+ for (auth = mail_lookup_auth (1), s = "SASL"; auth; auth = auth->next)
+ if (auth->server && !(auth->flags & AU_DISABLE) &&
+ !(auth->flags & AU_HIDE) && (i || (auth->flags & AU_SECURE))) {
+ if (s) {
+ PSOUT (s);
+ s = NIL;
+ }
+ PBOUT (' ');
+ PSOUT (auth->name);
+ }
+ PSOUT (s ? ".\015\012" : "\015\012.\015\012");
+ }
+
+ else switch (state) { /* else dispatch based on state */
+ case AUTHORIZATION: /* waiting to get logged in */
+ if (!strcmp (s,"AUTH")) {
+ if (t && *t) { /* mechanism given? */
+ if (host) fs_give ((void **) &host);
+ if (user) fs_give ((void **) &user);
+ if (pass) fs_give ((void **) &pass);
+ s = strtok (t," "); /* get mechanism name */
+ /* get initial response */
+ if (initial = strtok (NIL,"\015\012")) {
+ if ((*initial == '=') && !initial[1]) ++initial;
+ else if (!*initial) initial = NIL;
+ }
+ if (!(user = cpystr (mail_auth (s,responder,argc,argv)))) {
+ PSOUT ("-ERR Bad authentication\015\012");
+ syslog (LOG_INFO,"AUTHENTICATE %s failure host=%.80s",s,
+ tcp_clienthost ());
+ }
+ else if ((state = mbxopen ("INBOX")) == TRANSACTION)
+ syslog (LOG_INFO,"Auth user=%.80s host=%.80s nmsgs=%lu/%lu",
+ user,tcp_clienthost (),nmsgs,stream->nmsgs);
+ else syslog (LOG_INFO,"Auth user=%.80s host=%.80s no mailbox",
+ user,tcp_clienthost ());
+ }
+ else {
+ AUTHENTICATOR *auth;
+ PSOUT ("+OK Supported authentication mechanisms:\015\012");
+ i = !mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL);
+ for (auth = mail_lookup_auth (1); auth; auth = auth->next)
+ if (auth->server && !(auth->flags & AU_DISABLE) &&
+ !(auth->flags & AU_HIDE) &&
+ (i || (auth->flags & AU_SECURE))) {
+ PSOUT (auth->name);
+ CRLF;
+ }
+ PBOUT ('.');
+ CRLF;
+ }
+ }
+
+ else if (!strcmp (s,"APOP")) {
+ if (challenge[0]) { /* can do it if have an MD5 challenge */
+ if (host) fs_give ((void **) &host);
+ if (user) fs_give ((void **) &user);
+ if (pass) fs_give ((void **) &pass);
+ /* get user name */
+ if (!(t && *t && (s = strtok (t," ")) && (t = strtok(NIL,"\012"))))
+ PSOUT ("-ERR Missing APOP argument\015\012");
+ else if (!(user = apop_login (challenge,s,t,argc,argv)))
+ PSOUT ("-ERR Bad APOP\015\012");
+ else if ((state = mbxopen ("INBOX")) == TRANSACTION)
+ syslog (LOG_INFO,"APOP user=%.80s host=%.80s nmsgs=%lu/%lu",
+ user,tcp_clienthost (),nmsgs,stream->nmsgs);
+ else syslog (LOG_INFO,"APOP user=%.80s host=%.80s no mailbox",
+ user,tcp_clienthost ());
+ }
+ else PSOUT ("-ERR Not supported\015\012");
+ }
+ /* (chuckle) */
+ else if (!strcmp (s,"RPOP"))
+ PSOUT ("-ERR Nice try, bunkie\015\012");
+ else if (!strcmp (s,"STLS")) {
+ if (t = ssl_start_tls (pgmname)) {
+ PSOUT ("-ERR STLS failed: ");
+ PSOUT (t);
+ CRLF;
+ }
+ else PSOUT ("+OK STLS completed\015\012");
+ }
+ else if (!mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL) &&
+ !strcmp (s,"USER")) {
+ if (host) fs_give ((void **) &host);
+ if (user) fs_give ((void **) &user);
+ if (pass) fs_give ((void **) &pass);
+ if (t && *t) { /* if user name given */
+ /* skip leading whitespace (bogus clients!) */
+ while (*t == ' ') ++t;
+ /* remote user name? */
+ if (s = strchr (t,':')) {
+ *s++ = '\0'; /* tie off host name */
+ host = cpystr (t);/* copy host name */
+ user = cpystr (s);/* copy user name */
+ }
+ /* local user name */
+ else user = cpystr (t);
+ PSOUT ("+OK User name accepted, password please\015\012");
+ }
+ else PSOUT ("-ERR Missing username argument\015\012");
+ }
+ else if (!mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL) &&
+ user && *user && !strcmp (s,"PASS"))
+ state = pass_login (t,argc,argv);
+ else PSOUT ("-ERR Unknown AUTHORIZATION state command\015\012");
+ break;
+
+ case TRANSACTION: /* logged in */
+ if (!strcmp (s,"STAT")) {
+ for (i = 1,j = 0,k = 0; i <= nmsgs; i++)
+ /* message still exists? */
+ if (msg[i] && !(flags[i] & DELE)) {
+ j++; /* count one more undeleted message */
+ k += mail_elt (stream,msg[i])->rfc822_size + SLEN;
+ }
+ sprintf (tmp,"+OK %lu %lu\015\012",j,k);
+ PSOUT (tmp);
+ }
+ else if (!strcmp (s,"LIST")) {
+ if (t && *t) { /* argument do single message */
+ if ((i = strtoul (t,NIL,10)) && (i <= nmsgs) && msg[i] &&
+ !(flags[i] & DELE)) {
+ sprintf (tmp,"+OK %lu %lu\015\012",i,
+ mail_elt(stream,msg[i])->rfc822_size + SLEN);
+ PSOUT (tmp);
+ }
+ else PSOUT ("-ERR No such message\015\012");
+ }
+ else { /* entire mailbox */
+ PSOUT ("+OK Mailbox scan listing follows\015\012");
+ for (i = 1,j = 0,k = 0; i <= nmsgs; i++)
+ if (msg[i] && !(flags[i] & DELE)) {
+ sprintf (tmp,"%lu %lu\015\012",i,
+ mail_elt (stream,msg[i])->rfc822_size + SLEN);
+ PSOUT (tmp);
+ }
+ PBOUT ('.'); /* end of list */
+ CRLF;
+ }
+ }
+ else if (!strcmp (s,"UIDL")) {
+ if (t && *t) { /* argument do single message */
+ if ((i = strtoul (t,NIL,10)) && (i <= nmsgs) && msg[i] &&
+ !(flags[i] & DELE)) {
+ sprintf (tmp,"+OK %lu %08lx%08lx\015\012",i,stream->uid_validity,
+ mail_uid (stream,msg[i]));
+ PSOUT (tmp);
+ }
+ else PSOUT ("-ERR No such message\015\012");
+ }
+ else { /* entire mailbox */
+ PSOUT ("+OK Unique-ID listing follows\015\012");
+ for (i = 1,j = 0,k = 0; i <= nmsgs; i++)
+ if (msg[i] && !(flags[i] & DELE)) {
+ sprintf (tmp,"%lu %08lx%08lx\015\012",i,stream->uid_validity,
+ mail_uid (stream,msg[i]));
+ PSOUT (tmp);
+ }
+ PBOUT ('.'); /* end of list */
+ CRLF;
+ }
+ }
+
+ else if (!strcmp (s,"RETR")) {
+ if (t && *t) { /* must have an argument */
+ if ((i = strtoul (t,NIL,10)) && (i <= nmsgs) && msg[i] &&
+ !(flags[i] & DELE)) {
+ MESSAGECACHE *elt;
+ /* update highest message accessed */
+ if (i > last) last = i;
+ sprintf (tmp,"+OK %lu octets\015\012",
+ (elt = mail_elt (stream,msg[i]))->rfc822_size + SLEN);
+ PSOUT (tmp);
+ /* if not marked seen or noted to be marked */
+ if (!(elt->seen || (flags[i] & SEEN))) {
+ ++nseen; /* note that we need to mark it seen */
+ flags[i] |= SEEN;
+ }
+ /* get header */
+ t = mail_fetch_header (stream,msg[i],NIL,NIL,&k,FT_PEEK);
+ blat (t,-1,k,NIL);/* write up to trailing CRLF */
+ /* build status */
+ sprintf (tmp,STATUS,elt->seen ? "R" : " ",
+ elt->recent ? " " : "O");
+ if (k < 4) CRLF; /* don't write Status: if no header */
+ /* normal header ending with CRLF CRLF? */
+ else if (t[k-3] == '\012') {
+ PSOUT (tmp); /* write status */
+ CRLF; /* then write second CRLF */
+ }
+ else { /* abnormal - no blank line at end of header */
+ CRLF; /* write CRLF first then */
+ PSOUT (tmp);
+ }
+ /* output text */
+ t = mail_fetch_text (stream,msg[i],NIL,&k,
+ FT_RETURNSTRINGSTRUCT | FT_PEEK);
+ if (k) { /* only if there is a text body */
+ blat (t,-1,k,&stream->private.string);
+ CRLF; /* end of list */
+ }
+ PBOUT ('.');
+ CRLF;
+ }
+ else PSOUT ("-ERR No such message\015\012");
+ }
+ else PSOUT ("-ERR Missing message number argument\015\012");
+ }
+
+ else if (!strcmp (s,"DELE")) {
+ if (t && *t) { /* must have an argument */
+ if ((i = strtoul (t,NIL,10)) && (i <= nmsgs) && msg[i] &&
+ !(flags[i] & DELE)) {
+ /* update highest message accessed */
+ if (i > last) last = i;
+ flags[i] |= DELE; /* note that deletion is requested */
+ PSOUT ("+OK Message deleted\015\012");
+ ++ndele; /* one more message deleted */
+ }
+ else PSOUT ("-ERR No such message\015\012");
+ }
+ else PSOUT ("-ERR Missing message number argument\015\012");
+ }
+ else if (!strcmp (s,"NOOP"))
+ PSOUT ("+OK No-op to you too!\015\012");
+ else if (!strcmp (s,"LAST")) {
+ sprintf (tmp,"+OK %lu\015\012",last);
+ PSOUT (tmp);
+ }
+ else if (!strcmp (s,"RSET")) {
+ rset (); /* reset the mailbox */
+ PSOUT ("+OK Reset state\015\012");
+ }
+
+ else if (!strcmp (s,"TOP")) {
+ if (t && *t && (i =strtoul (t,&s,10)) && (i <= nmsgs) && msg[i] &&
+ !(flags[i] & DELE)) {
+ /* skip whitespace */
+ while (*s == ' ') s++;
+ /* make sure line count argument good */
+ if ((*s >= '0') && (*s <= '9')) {
+ MESSAGECACHE *elt = mail_elt (stream,msg[i]);
+ j = strtoul (s,NIL,10);
+ /* update highest message accessed */
+ if (i > last) last = i;
+ PSOUT ("+OK Top of message follows\015\012");
+ /* get header */
+ t = mail_fetch_header (stream,msg[i],NIL,NIL,&k,FT_PEEK);
+ blat (t,-1,k,NIL);/* write up to trailing CRLF */
+ /* build status */
+ sprintf (tmp,STATUS,elt->seen ? "R" : " ",
+ elt->recent ? " " : "O");
+ if (k < 4) CRLF; /* don't write Status: if no header */
+ /* normal header ending with CRLF CRLF? */
+ else if (t[k-3] == '\012') {
+ PSOUT (tmp); /* write status */
+ CRLF; /* then write second CRLF */
+ }
+ else { /* abnormal - no blank line at end of header */
+ CRLF; /* write CRLF first then */
+ PSOUT (tmp);
+ }
+ if (j) { /* want any text lines? */
+ /* output text */
+ t = mail_fetch_text (stream,msg[i],NIL,&k,
+ FT_PEEK | FT_RETURNSTRINGSTRUCT);
+ /* tie off final line if full text output */
+ if (k && (j -= blat (t,j,k,&stream->private.string))) CRLF;
+ }
+ PBOUT ('.'); /* end of list */
+ CRLF;
+ }
+ else PSOUT ("-ERR Bad line count argument\015\012");
+ }
+ else PSOUT ("-ERR Bad message number argument\015\012");
+ }
+
+ else if (!strcmp (s,"XTND"))
+ PSOUT ("-ERR Sorry I can't do that\015\012");
+ else PSOUT ("-ERR Unknown TRANSACTION state command\015\012");
+ break;
+ default:
+ PSOUT ("-ERR Server in unknown state\015\012");
+ break;
+ }
+ }
+ PFLUSH (); /* make sure output finished */
+ if (autologouttime) { /* have an autologout in effect? */
+ /* cancel if no longer waiting for login */
+ if (state != AUTHORIZATION) autologouttime = 0;
+ /* took too long to login */
+ else if (autologouttime < time (0)) {
+ goodbye = "-ERR Autologout\015\012";
+ logout = "Autologout";
+ state = LOGOUT; /* sayonara */
+ }
+ }
+ }
+
+ /* open and need to update? */
+ if (stream && (state == UPDATE)) {
+ if (nseen) { /* only bother if messages need marking seen */
+ *(s = tmp) = '\0'; /* clear sequence */
+ for (i = 1; i <= nmsgs; ++i) if (flags[i] & SEEN) {
+ for (j = i + 1, k = 0; (j <= nmsgs) && (flags[j] & SEEN); ++j) k = j;
+ if (k) sprintf (s,",%lu:%lu",i,k);
+ else sprintf (s,",%lu",i);
+ s += strlen (s); /* point to end of string */
+ if ((s - tmp) > (MAILTMPLEN - 30)) {
+ mail_setflag (stream,tmp + 1,"\\Seen");
+ *(s = tmp) = '\0'; /* restart sequence */
+ }
+ i = j; /* continue after the range */
+ }
+ if (tmp[0]) mail_setflag (stream,tmp + 1,"\\Seen");
+ }
+ if (ndele) { /* any messages to delete? */
+ *(s = tmp) = '\0'; /* clear sequence */
+ for (i = 1; i <= nmsgs; ++i) if (flags[i] & DELE) {
+ for (j = i + 1, k = 0; (j <= nmsgs) && (flags[j] & DELE); ++j) k = j;
+ if (k) sprintf (s,",%lu:%lu",i,k);
+ else sprintf (s,",%lu",i);
+ s += strlen (s); /* point to end of string */
+ if ((s - tmp) > (MAILTMPLEN - 30)) {
+ mail_setflag (stream,tmp + 1,"\\Deleted");
+ *(s = tmp) = '\0'; /* restart sequence */
+ }
+ i = j; /* continue after the range */
+ }
+ if (tmp[0]) mail_setflag (stream,tmp + 1,"\\Deleted");
+ mail_expunge (stream);
+ }
+ syslog (LOG_INFO,"Update user=%.80s host=%.80s nmsgs=%lu ndele=%lu nseen=%lu",
+ user,tcp_clienthost (),stream->nmsgs,ndele,nseen);
+ mail_close (stream);
+ }
+ sayonara (0);
+ return 0; /* stupid compilers */
+}
+
+
+/* Say goodbye
+ * Accepts: exit status
+ *
+ * Does not return
+ */
+
+void sayonara (int status)
+{
+ logouthook_t lgoh = (logouthook_t) mail_parameters (NIL,GET_LOGOUTHOOK,NIL);
+ if (goodbye) { /* have a goodbye message? */
+ PSOUT (goodbye);
+ PFLUSH (); /* make sure blatted */
+ }
+ syslog (LOG_INFO,"%s user=%.80s host=%.80s",logout,
+ user ? (char *) user : "???",tcp_clienthost ());
+ /* do logout hook if needed */
+ if (lgoh) (*lgoh) (mail_parameters (NIL,GET_LOGOUTDATA,NIL));
+ _exit (status); /* all done */
+}
+
+/* Clock interrupt
+ */
+
+void clkint ()
+{
+ alarm (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ goodbye = "-ERR Autologout; idle for too long\015\012";
+ logout = "Autologout";
+ if (critical) state = LOGOUT; /* badly hosed if in critical code */
+ else { /* try to gracefully close the stream */
+ if ((state == TRANSACTION) && !stream->lock) {
+ rset ();
+ mail_close (stream);
+ }
+ state = LOGOUT;
+ stream = NIL;
+ sayonara (1);
+ }
+}
+
+
+/* Kiss Of Death interrupt
+ */
+
+void kodint ()
+{
+ /* only if idle */
+ if (idletime && ((time (0) - idletime) > KODTIMEOUT)) {
+ alarm (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ goodbye = "-ERR Received Kiss of Death\015\012";
+ logout = "Killed (lost mailbox lock)";
+ if (critical) state =LOGOUT;/* must defer if in critical code */
+ else { /* try to gracefully close the stream */
+ if ((state == TRANSACTION) && !stream->lock) {
+ rset ();
+ mail_close (stream);
+ }
+ state = LOGOUT;
+ stream = NIL;
+ sayonara (1); /* die die die */
+ }
+ }
+}
+
+
+/* Hangup interrupt
+ */
+
+void hupint ()
+{
+ alarm (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ goodbye = NIL; /* nobody left to talk to */
+ logout = "Hangup";
+ if (critical) state = LOGOUT; /* must defer if in critical code */
+ else { /* try to gracefully close the stream */
+ if ((state == TRANSACTION) && !stream->lock) {
+ rset ();
+ mail_close (stream);
+ }
+ state = LOGOUT;
+ stream = NIL;
+ sayonara (1); /* die die die */
+ }
+}
+
+
+/* Termination interrupt
+ */
+
+void trmint ()
+{
+ alarm (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ goodbye = "-ERR Killed\015\012";
+ logout = "Killed";
+ if (critical) state = LOGOUT; /* must defer if in critical code */
+ /* Make no attempt at graceful closure since a shutdown may be in
+ * progress, and we won't have any time to do mail_close() actions.
+ */
+ else sayonara (1); /* die die die */
+}
+
+/* Parse PASS command
+ * Accepts: pointer to command argument
+ * Returns: new state
+ */
+
+int pass_login (char *t,int argc,char *argv[])
+{
+ char tmp[MAILTMPLEN];
+ /* flush old passowrd */
+ if (pass) fs_give ((void **) &pass);
+ if (!(t && *t)) { /* if no password given */
+ PSOUT ("-ERR Missing password argument\015\012");
+ return AUTHORIZATION;
+ }
+ pass = cpystr (t); /* copy password argument */
+ if (!host) { /* want remote mailbox? */
+ /* no, delimit user from possible admin */
+ if (t = strchr (user,'*')) *t++ ='\0';
+ /* attempt the login */
+ if (server_login (user,pass,t,argc,argv)) {
+ int ret = mbxopen ("INBOX");
+ if (ret == TRANSACTION) /* mailbox opened OK? */
+ syslog (LOG_INFO,"%sLogin user=%.80s host=%.80s nmsgs=%lu/%lu",
+ t ? "Admin " : "",user,tcp_clienthost (),nmsgs,stream->nmsgs);
+ else syslog (LOG_INFO,"%sLogin user=%.80s host=%.80s no mailbox",
+ t ? "Admin " : "",user,tcp_clienthost ());
+ return ret;
+ }
+ }
+#ifndef DISABLE_POP_PROXY
+ /* remote; build remote INBOX */
+ else if (anonymous_login (argc,argv)) {
+ syslog (LOG_INFO,"IMAP login to host=%.80s user=%.80s host=%.80s",host,
+ user,tcp_clienthost ());
+ sprintf (tmp,"{%.128s/user=%.128s}INBOX",host,user);
+ /* disable rimap just in case */
+ mail_parameters (NIL,SET_RSHTIMEOUT,0);
+ return mbxopen (tmp);
+ }
+#endif
+ /* vague error message to confuse crackers */
+ PSOUT ("-ERR Bad login\015\012");
+ return AUTHORIZATION;
+}
+
+/* Authentication responder
+ * Accepts: challenge
+ * length of challenge
+ * pointer to response length return location if non-NIL
+ * Returns: response
+ */
+
+#define RESPBUFLEN 8*MAILTMPLEN
+
+char *responder (void *challenge,unsigned long clen,unsigned long *rlen)
+{
+ unsigned long i,j;
+ unsigned char *t,resp[RESPBUFLEN];
+ char tmp[MAILTMPLEN];
+ if (initial) { /* initial response given? */
+ if (clen) return NIL; /* not permitted */
+ /* set up response */
+ t = (unsigned char *) initial;
+ initial = NIL; /* no more initial response */
+ return (char *) rfc822_base64 (t,strlen ((char *) t),rlen ? rlen : &i);
+ }
+ PSOUT ("+ ");
+ for (t = rfc822_binary (challenge,clen,&i),j = 0; j < i; j++)
+ if (t[j] > ' ') PBOUT (t[j]);
+ fs_give ((void **) &t);
+ CRLF;
+ PFLUSH (); /* dump output buffer */
+ resp[RESPBUFLEN-1] = '\0'; /* last buffer character is guaranteed NUL */
+ alarm (LOGINTIMEOUT); /* get a response under timeout */
+ clearerr (stdin); /* clear stdin errors */
+ /* read buffer */
+ while (!PSIN ((char *) resp,RESPBUFLEN)) {
+ /* ignore if some interrupt */
+ if (ferror (stdin) && (errno == EINTR)) clearerr (stdin);
+ else {
+ char *e = ferror (stdin) ?
+ strerror (errno) : "Command stream end of file";
+ alarm (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ sprintf (logout = tmp,"%.80s, while reading authentication",e);
+ goodbye = NIL;
+ state = LOGOUT;
+ sayonara (1);
+ }
+ }
+ if (!(t = (unsigned char *) strchr ((char *) resp,'\012'))) {
+ int c;
+ while ((c = PBIN ()) != '\012') if (c == EOF) {
+ /* ignore if some interrupt */
+ if (ferror (stdin) && (errno == EINTR)) clearerr (stdin);
+ else {
+ char *e = ferror (stdin) ?
+ strerror (errno) : "Command stream end of file";
+ alarm (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ sprintf (logout = tmp,"%.80s, while reading auth char",e);
+ goodbye = NIL;
+ state = LOGOUT;
+ sayonara (1);
+ }
+ }
+ return NIL;
+ }
+ alarm (0); /* make sure timeout disabled */
+ if (t[-1] == '\015') --t; /* remove CR */
+ *t = '\0'; /* tie off buffer */
+ return (resp[0] != '*') ?
+ (char *) rfc822_base64 (resp,t-resp,rlen ? rlen : &i) : NIL;
+}
+
+/* Select mailbox
+ * Accepts: mailbox name
+ * Returns: new state
+ */
+
+int mbxopen (char *mailbox)
+{
+ unsigned long i,j;
+ char tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ if (msg) fs_give ((void **) &msg);
+ /* open mailbox */
+ if (!(stream = mail_open (stream,mailbox,NIL)))
+ goodbye = "-ERR Unable to open user's INBOX\015\012";
+ else if (stream->rdonly) /* make sure not readonly */
+ goodbye = "-ERR Can't get lock. Mailbox in use\015\012";
+ else {
+ nmsgs = 0; /* no messages yet */
+ if (j = stream->nmsgs) { /* if mailbox non-empty */
+ sprintf (tmp,"1:%lu",j); /* fetch fast information for all messages */
+ mail_fetch_fast (stream,tmp,NIL);
+ }
+ /* create 1-origin tables */
+ msg = (long *) fs_get (++j * sizeof (long));
+ flags = (short *) fs_get (j * sizeof (short));
+ /* build map */
+ for (i = 1; i < j; ++i) if (!(elt = mail_elt (stream,i))->deleted) {
+ msg[++nmsgs] = i; /* note the presence of this message */
+ if (elt->seen) il = nmsgs;/* and set up initial LAST */
+ }
+ /* make sure unused map entries are zero */
+ for (i = nmsgs + 1; i < j; ++i) msg[i] = 0;
+ rset (); /* do implicit RSET */
+ sprintf (tmp,"+OK Mailbox open, %lu messages\015\012",nmsgs);
+ PSOUT (tmp);
+ return TRANSACTION;
+ }
+ syslog (LOG_INFO,"Error opening or locking INBOX user=%.80s host=%.80s",
+ user,tcp_clienthost ());
+ return UPDATE;
+}
+
+/* Blat a string with dot checking
+ * Accepts: string
+ * maximum number of lines if greater than zero
+ * maximum number of bytes to output
+ * alternative stringstruct
+ * Returns: number of lines output
+ *
+ * This routine is uglier and kludgier than it should be, just to be robust
+ * in the case of a message which doesn't end in a newline. Yes, this routine
+ * does truncate the last two bytes from the text. Since it is normally a
+ * newline and the main routine adds it back, it usually does not make a
+ * difference. But if it isn't, since the newline is required and the octet
+ * counts have to match, there's no choice but to truncate.
+ */
+
+long blat (char *text,long lines,unsigned long size,STRING *st)
+{
+ char c,d,e;
+ long ret = 0;
+ /* no-op if zero lines or empty string */
+ if (!(lines && (size-- > 2))) return 0;
+ if (text) {
+ c = *text++; d = *text++; /* collect first two bytes */
+ if (c == '.') PBOUT ('.'); /* double string-leading dot if necessary */
+ while (lines && --size) { /* copy loop */
+ e = *text++; /* get next byte */
+ PBOUT (c); /* output character */
+ if (c == '\012') { /* end of line? */
+ ret++; --lines; /* count another line */
+ /* double leading dot as necessary */
+ if (lines && size && (d == '.')) PBOUT ('.');
+ }
+ c = d; d = e; /* move to next character */
+ }
+ }
+ else {
+ c = SNX (st); d = SNX (st); /* collect first two bytes */
+ if (c == '.') PBOUT ('.'); /* double string-leading dot if necessary */
+ while (lines && --size) { /* copy loop */
+ e = SNX (st); /* get next byte */
+ PBOUT (c); /* output character */
+ if (c == '\012') { /* end of line? */
+ ret++; --lines; /* count another line */
+ /* double leading dot as necessary */
+ if (lines && size && (d == '.')) PBOUT ('.');
+ }
+ c = d; d = e; /* move to next character */
+ }
+ }
+ return ret;
+}
+
+/* Reset mailbox
+ */
+
+void rset ()
+{
+ /* clear all flags */
+ if (flags) memset ((void *) flags,0,(nmsgs + 1) * sizeof (short));
+ ndele = nseen = 0; /* no more deleted or seen messages */
+ last = il; /* restore previous LAST value */
+}
+
+/* Co-routines from MAIL library */
+
+
+/* Message matches a search
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_searched (MAILSTREAM *stream,unsigned long msgno)
+{
+ /* Never called */
+}
+
+
+/* Message exists (i.e. there are that many messages in the mailbox)
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_exists (MAILSTREAM *stream,unsigned long number)
+{
+ /* Can't use this mechanism. POP has no means of notifying the client of
+ new mail during the session. */
+}
+
+
+/* Message expunged
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_expunged (MAILSTREAM *stream,unsigned long number)
+{
+ unsigned long i = number + 1;
+ msg[number] = 0; /* I bet that this will annoy someone */
+ while (i <= nmsgs) --msg[i++];
+}
+
+
+/* Message flag status change
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_flags (MAILSTREAM *stream,unsigned long number)
+{
+ /* This isn't used */
+}
+
+
+/* Mailbox found
+ * Accepts: MAIL stream
+ * hierarchy delimiter
+ * mailbox name
+ * mailbox attributes
+ */
+
+void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes)
+{
+ /* This isn't used */
+}
+
+
+/* Subscribe mailbox found
+ * Accepts: MAIL stream
+ * hierarchy delimiter
+ * mailbox name
+ * mailbox attributes
+ */
+
+void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes)
+{
+ /* This isn't used */
+}
+
+
+/* Mailbox status
+ * Accepts: MAIL stream
+ * mailbox name
+ * mailbox status
+ */
+
+void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
+{
+ /* This isn't used */
+}
+
+/* Notification event
+ * Accepts: MAIL stream
+ * string to log
+ * error flag
+ */
+
+void mm_notify (MAILSTREAM *stream,char *string,long errflg)
+{
+ mm_log (string,errflg); /* just do mm_log action */
+}
+
+
+/* Log an event for the user to see
+ * Accepts: string to log
+ * error flag
+ */
+
+void mm_log (char *string,long errflg)
+{
+ switch (errflg) {
+ case NIL: /* information message */
+ case PARSE: /* parse glitch */
+ break; /* too many of these to log */
+ case WARN: /* warning */
+ syslog (LOG_DEBUG,"%s",string);
+ break;
+ case BYE: /* driver broke connection */
+ if (state != UPDATE) {
+ char tmp[MAILTMPLEN];
+ alarm (0); /* disable all interrupts */
+ server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
+ sprintf (logout = tmp,"Mailbox closed (%.80s)",string);
+ goodbye = NIL;
+ state = LOGOUT;
+ sayonara (1);
+ }
+ break;
+ case ERROR: /* error that broke command */
+ default: /* default should never happen */
+ syslog (LOG_NOTICE,"%s",string);
+ break;
+ }
+}
+
+
+/* Log an event to debugging telemetry
+ * Accepts: string to log
+ */
+
+void mm_dlog (char *string)
+{
+ /* Not doing anything here for now */
+}
+
+
+/* Get user name and password for this host
+ * Accepts: parse of network mailbox name
+ * where to return user name
+ * where to return password
+ * trial count
+ */
+
+void mm_login (NETMBX *mb,char *username,char *password,long trial)
+{
+ /* set user name */
+ strncpy (username,*mb->user ? mb->user : user,NETMAXUSER-1);
+ if (pass) {
+ strncpy (password,pass,255);/* and password */
+ fs_give ((void **) &pass);
+ }
+ else memset (password,0,256); /* no password to send, abort login */
+ username[NETMAXUSER] = password[255] = '\0';
+}
+
+/* About to enter critical code
+ * Accepts: stream
+ */
+
+void mm_critical (MAILSTREAM *stream)
+{
+ ++critical;
+}
+
+
+/* About to exit critical code
+ * Accepts: stream
+ */
+
+void mm_nocritical (MAILSTREAM *stream)
+{
+ --critical;
+}
+
+
+/* Disk error found
+ * Accepts: stream
+ * system error code
+ * flag indicating that mailbox may be clobbered
+ * Returns: abort flag
+ */
+
+long mm_diskerror (MAILSTREAM *stream,long errcode,long serious)
+{
+ if (serious) { /* try your damnest if clobberage likely */
+ syslog (LOG_ALERT,
+ "Retrying after disk error user=%.80s host=%.80s mbx=%.80s: %.80s",
+ user,tcp_clienthost (),
+ (stream && stream->mailbox) ? stream->mailbox : "???",
+ strerror (errcode));
+ alarm (0); /* make damn sure timeout disabled */
+ sleep (60); /* give it some time to clear up */
+ return NIL;
+ }
+ syslog (LOG_ALERT,"Fatal disk error user=%.80s host=%.80s mbx=%.80s: %.80s",
+ user,tcp_clienthost (),
+ (stream && stream->mailbox) ? stream->mailbox : "???",
+ strerror (errcode));
+ return T;
+}
+
+
+/* Log a fatal error event
+ * Accepts: string to log
+ */
+
+void mm_fatal (char *string)
+{
+ mm_log (string,ERROR); /* shouldn't happen normally */
+}
diff --git a/imap/src/ipopd/ipopd.8 b/imap/src/ipopd/ipopd.8
new file mode 100644
index 00000000..95a5b099
--- /dev/null
+++ b/imap/src/ipopd/ipopd.8
@@ -0,0 +1,75 @@
+.ig
+ * ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+..
+.TH IPOPD 8 "August 30, 2006"
+.UC 5
+.SH NAME
+IPOPd \- Post Office Protocol server
+.SH SYNOPSIS
+.B /usr/etc/ipop2d
+.PP
+.B /usr/etc/ipop3d
+.SH DESCRIPTION
+.I ipop2d
+and
+.I ipop3d
+are servers which support the
+.B POP2
+and
+.B POP3
+remote mail access protocols respectively.
+.I ipop2d
+and
+.I ipop3d
+can also be used by
+.B POP2
+and
+.B POP3
+clients respecitively to access mailboxes on
+.B IMAP
+servers by specifying a login user name in the form <host>:<user>
+e.g.,
+.B SERVER.WASHINGTON.EDU:SMITH.
+.PP
+These daemons contain CRAM-MD5 and APOP support. See the md5.txt
+documentation file for additional information.
+.PP
+.I ipop2d
+and
+.I ipop3d
+are invoked by the internet server (see
+.IR inetd (8)),
+normally for requests to connect to the
+.B POP
+port as indicated by the
+.I /etc/services
+file (see
+.IR services (5)).
+.SH "SEE ALSO"
+imapd(8)
+.SH BUGS
+The
+.B POP2
+and
+.B POP3
+protocols are intrinsically less flexible than
+.B IMAP
+and do not maintain `read' vs `unread' state on the server. As a result,
+most
+.B POP
+based software transfers all the mail from the server to the client and
+deletes it from the server. This necessarily locks the user into using only
+a single client.
+.PP
+.B POP3
+does not allow you to specify an alternate folder from the user's default.
diff --git a/imap/src/ipopd/makefile.nt b/imap/src/ipopd/makefile.nt
new file mode 100644
index 00000000..ef32819b
--- /dev/null
+++ b/imap/src/ipopd/makefile.nt
@@ -0,0 +1,57 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: IPOPD Makefile for Windows 9x and Windows NT
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 28 October 1990
+# Last Edited: 30 August 2006
+
+
+C = ..\c-client
+CCLIENTLIB = $C\cclient.lib
+LIBS = $(CCLIENTLIB) ws2_32.lib winmm.lib advapi32.lib
+OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0400
+VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE
+CFLAGS= -I$C /MT /W3 $(OSCOMPAT) $(VSCOMPAT) -nologo $(EXTRACFLAGS)
+
+
+ipopd: ipop2d ipop3d
+
+ipop2d: $(CCLIENTLIB) ipop2d.obj
+ LINK /NOLOGO ipop2d.obj $(LIBS)
+
+ipop3d: $(CCLIENTLIB) ipop3d.obj
+ LINK /NOLOGO ipop3d.obj $(LIBS)
+
+ipop2d.obj: $C\mail.h $C\misc.h $C\osdep.h
+
+ipop3d.obj: $C\mail.h $C\misc.h $C\osdep.h
+
+$(CCLIENTLIB):
+ @echo Make c-client first
+ false
+
+clean:
+ del *.obj *.exe *.lib *.exp || rem
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo not war?
diff --git a/imap/src/ipopd/makefile.ntk b/imap/src/ipopd/makefile.ntk
new file mode 100644
index 00000000..4a90a7a0
--- /dev/null
+++ b/imap/src/ipopd/makefile.ntk
@@ -0,0 +1,58 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: IPOPD Makefile for Windows 9x and Windows NT + Kerberos
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 28 October 1990
+# Last Edited: 30 August 2006
+
+
+C = ..\c-client
+CCLIENTLIB = $C\cclient.lib
+K5 = \k5\lib
+K5LIB = $(K5)\comerr32.lib $(K5)\gssapi32.lib $(K5)\krb5_32.lib
+LIBS = $(CCLIENTLIB) $(K5LIB) ws2_32.lib winmm.lib advapi32.lib
+OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0400
+VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE
+CFLAGS= -I$C /MT /W3 $(OSCOMPAT) $(VSCOMPAT) -nologo $(EXTRACFLAGS)
+
+ipopd: ipop2d ipop3d
+
+ipop2d: $(CCLIENTLIB) ipop2d.obj
+ LINK /NOLOGO ipop2d.obj $(LIBS)
+
+ipop3d: $(CCLIENTLIB) ipop3d.obj
+ LINK /NOLOGO ipop3d.obj $(LIBS)
+
+ipop2d.obj: $C\mail.h $C\misc.h $C\osdep.h
+
+ipop3d.obj: $C\mail.h $C\misc.h $C\osdep.h
+
+$(CCLIENTLIB):
+ @echo Make c-client first
+ false
+
+clean:
+ del *.obj *.exe *.lib *.exp || rem
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo not war?
diff --git a/imap/src/ipopd/makefile.w2k b/imap/src/ipopd/makefile.w2k
new file mode 100644
index 00000000..efa63c2f
--- /dev/null
+++ b/imap/src/ipopd/makefile.w2k
@@ -0,0 +1,56 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: IPOPD Makefile for Windows 2000/XP
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 28 October 1990
+# Last Edited: 30 August 2006
+
+
+C = ..\c-client
+CCLIENTLIB = $C\cclient.lib
+LIBS = $(CCLIENTLIB) ws2_32.lib winmm.lib advapi32.lib secur32.lib crypt32.lib
+OSCOMPAT = /DWIN32
+VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE
+CFLAGS= -I$C /MT /W3 $(OSCOMPAT) $(VSCOMPAT) -nologo $(EXTRACFLAGS)
+
+ipopd: ipop2d ipop3d
+
+ipop2d: $(CCLIENTLIB) ipop2d.obj
+ LINK /NOLOGO ipop2d.obj $(LIBS)
+
+ipop3d: $(CCLIENTLIB) ipop3d.obj
+ LINK /NOLOGO ipop3d.obj $(LIBS)
+
+ipop2d.obj: $C\mail.h $C\misc.h $C\osdep.h
+
+ipop3d.obj: $C\mail.h $C\misc.h $C\osdep.h
+
+$(CCLIENTLIB):
+ @echo Make c-client first
+ false
+
+clean:
+ del *.obj *.exe *.lib *.exp || rem
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo not war?
diff --git a/imap/src/mailutil/Makefile b/imap/src/mailutil/Makefile
new file mode 100644
index 00000000..acc58bbd
--- /dev/null
+++ b/imap/src/mailutil/Makefile
@@ -0,0 +1,51 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: mailutil Makefile
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 2 February 1993
+# Last Edited: 30 August 2006
+
+
+C = ../c-client
+CCLIENTLIB = $C/c-client.a
+SHELL = /bin/sh
+
+# Get local definitions from c-client directory
+
+CC = `cat $C/CCTYPE`
+CFLAGS = -I$C `cat $C/CFLAGS`
+LDFLAGS = $(CCLIENTLIB) `cat $C/LDFLAGS`
+
+mailutil: $(CCLIENTLIB) mailutil.o
+ $(CC) $(CFLAGS) -o mailutil mailutil.o $(LDFLAGS)
+
+mailutil.o: $C/mail.h $C/misc.h $C/osdep.h
+
+$(CCLIENTLIB):
+ cd $C;make
+
+clean:
+ rm -f *.o mailutil
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo 'not war?'
diff --git a/imap/src/mailutil/mailutil.1 b/imap/src/mailutil/mailutil.1
new file mode 100644
index 00000000..964fb1b8
--- /dev/null
+++ b/imap/src/mailutil/mailutil.1
@@ -0,0 +1,264 @@
+.ig
+ * ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+..
+.TH mailutil 1 "March 3, 2008"
+.SH NAME
+mailutil - mail utility program
+.nh
+.SH SYNTAX
+.B mailutil command [switches] [arguments]
+.PP
+All commands accept the -d, -v, and -u switches in addition to any
+command-specific switches.
+.PP
+.B mailutil check [MAILBOX]
+.PP
+.B mailutil create MAILBOX
+.PP
+.B mailutil delete MAILBOX
+.PP
+.B mailutil rename SOURCE DESTINATION
+.PP
+.B mailutil copy [-rw] [-kw] [-ig] SOURCE DESTINATION
+.PP
+.B mailutil move [-rw] [-kw] [-ig] SOURCE DESTINATION
+.PP
+.B mailutil append [-rw] [-kw] [-ig] SOURCE DESTINATION
+.PP
+.B mailutil appenddelete [-rw] [-kw] [-ig] SOURCE DESTINATION
+.PP
+.B mailutil prune MAILBOX CRITERIA
+.PP
+.B mailutil transfer [-m mode] [-rw] [-kw] [-ig] SOURCE DESTINATION
+.SH DESCRIPTION
+.B mailutil
+replaces the old chkmail, imapcopy, imapmove, imapxfer, mbxcopy,
+mbxcreat, and mbxcvt programs.
+.PP
+.B mailutil check
+determines whether new mail exists in the given mailbox (the default
+is INBOX). The number of new messages is defined as the number of
+messages that have "Recent" status set. If the mailbox contains no
+new messages,
+.B mailutil check
+will indicate that no new mail is present;
+otherwise, it will report the number of new messages. In either case,
+it will also indicate the canonical form of the name of the mailbox.
+.PP
+.B mailutil create
+creates a new
+.I mailbox
+with the given name. The mailbox name must not already exist. A mailbox
+can be created in a particular format by prefixing the name with
+.I #driver.
+followed by the format name and a
+.I /
+character. For example, the command
+.br
+ mailutil create #driver.mbx/junkmail
+.br
+will create a new mailbox named "junkmail" in mbx format.
+.PP
+.B mailutil delete
+deletes an existing
+.I mailbox
+with the given name.
+.PP
+.B mailutil rename
+renames an existing mailbox to a new name (which must not already exist).
+This only works if the old and new names are in the same mail store. A
+more general means to rename a mailbox is to do a
+.B mailutil copy
+of the old name to the new name, followed by a
+.B mailutil delete
+of the old name.
+.PP
+.B mailutil copy
+creates a new mailbox and copies messages from the old mailbox to the
+new mailbox. As in
+.B mailutil create
+a mailbox format can be specified with the new mailbox. For example, the
+command
+.br
+ mailutil copy INBOX #driver.mbx/INBOX
+.br
+will copy messages from your existing INBOX to an mbx-format INBOX.
+.PP
+.B mailutil move
+is similar to
+.B mailutil copy
+but in addition will also remove (delete and expunge) the messages from the
+old mailbox after copying them to the new mailbox.
+.PP
+.B mailutil append
+and
+.B mailutil appenddelete
+are similar to
+.B mailutil copy
+and
+.B mailutil move
+respectively except that they do not create the destination mailbox.
+.PP
+.B mailutil prune
+prunes the mailbox of messages which match certain criteria, which are
+in the form of IMAP2 (RFC 1176) SEARCH arguments. For example, the
+command.
+.br
+ mailutil prune INBOX "before 1-jan-2004"
+.br
+will delete and expunge all messages written before January 1, 2004.
+.PP
+Note that mailutil implements pruning by deleting the matching messages,
+and then expunging the mailbox. Consequently, mailutil will also expunge
+any messages which were deleted at the time of the pruning.
+.PP
+.B mailutil transfer
+copies an entire hierarchy of mailboxes from the named source to the
+named destination. Mailboxes are created on the destination as
+needed. Any error in copying messages will cause the transfer to stop.
+.PP
+Normally, any error in creation will cause the transfer to stop.
+However, if
+.B -m MODE
+or
+.B -merge MODE
+is specified, a merging transfer is performed. The
+.B MODE
+argument indicats the type of merge:
+.PP
+.B -m[erge] prompt
+indicates that the user should be asked for an alternative name to create.
+If creating the new name fails, the user will be asked again.
+.PP
+.B -m[erge] append
+indicates that it's alright to copy the messages into an existing mailbox
+with that name. If the mailbox does not exist, the user will be prompted
+for an alternative name.
+.PP
+.B -m[erge] suffix=XXXX
+where XXXX is any string, indicates that an alternative name should be
+built by appending the given suffix to the name. It that alternative name
+can't be created, then the user will be prompted for an alternative name.
+.PP
+The source hierarchy consists of all mailboxes which start
+with the given source name. With the exception of a remote system
+specification (within "{}" braces), the source name is used as the
+name of the destination. The destination hierarchy is a prefix
+applied to any new names being created. For example,
+.br
+ mailutil transfer foo bar
+.br
+will copy all mailboxes with names beginning with "foo" to names
+beginning with "bar" (hence "foobar" will be copied to "barfoobar").
+Similarly,
+.br
+ mailutil transfer "{imap.foo.com}" "{imap.bar.com}old/"
+.br
+will copy all mailboxes from the imap.foo.com IMAP server to
+equivalent names starting with "old/" on the imap.bar.com IMAP server.
+.SH FLAGS
+The
+.B -d
+or
+.B -debug
+flag prints full debugging telemetry including protocol operations.
+.PP
+The
+.B -v
+or
+.B -verbose
+flag prints verbose (non-error) telemetry.
+.PP
+The
+.B -u USERID
+or
+.B -user USERID
+switch attempts to become the indicated user. This is for the benefit of
+system administrators who want to do mailutil operations on a userid that
+does not normally have shell access.
+.PP
+The
+.B -rw
+or
+.B -rwcopy
+flag causes the source mailbox to be open in readwrite mode rather than
+readonly mode. Normally, mailutil tries to use readonly mode to avoid
+altering any flags in the source mailbox, but some mailbox types, e.g.
+POP3, can't be open in readonly mode.
+.PP
+The
+.B -kw
+or
+.B -kwcopy
+flag causes the keywords of the source mailbox to be created in the
+destination mailbox. Normally, mailutil does not create keywords in
+the destination mailbox so only those keywords that are already defined
+in the destination mailbox will be preserved. Note that some IMAP servers
+may automatically create keywords, so this flag may not be necessary.
+.PP
+The
+.B -ig
+or
+.B -ignore
+flag causes the keywords of the source mailbox to be ignored completely
+and no attempt is made to copy them to the destination mailbox.
+.PP
+The
+.B -ig[nore]
+and
+.B -kw[copy]
+flags are mutually exclusive.
+.SH ARGUMENTS
+The arguments are standard c-client mailbox names. A
+variety of mailbox name formats and types of mailboxes are supported
+by c-client; examples of the most common forms of names are:
+.PP
+.I
+.IP Name 15
+.I Meaning
+.IP INBOX
+primary incoming mail folder on the local system
+.IP archive/tx-project
+mail folder named "tx-project" in "archive" subdirectory of local
+filesystem home directory
+.IP {imapserver.foo.com}INBOX
+primary incoming mail folder on IMAP server system
+"imapserver.foo.com"
+.IP {imapserver.foo.com}archive/tx-project
+mail folder named "tx-project" in "archive" subdirectory on IMAP
+server system "imapserver.foo.com"
+.IP #news.comp.mail.misc
+newsgroup "comp.mail.misc" on local filesystem
+.IP {newserver.foo.com/nntp}comp.mail.misc
+newsgroup "comp.mail.misc" on NNTP server system "newserver.foo.com"
+.IP {popserver.foo.com/pop3}
+mail folder on POP3 server system "popserver.foo.com"
+.LP
+See your system manager for more information about the types of
+mailboxes which are available on your system.
+.SH RESTRICTIONS
+You must surround a
+.I {host}mailbox
+argument with quotation marks if you run
+.B mailutil
+from
+.IR csh (1)
+or another shell for which braces have special meaning.
+.PP
+You must surround a
+.I #driver.format/mailbox
+argument with quotation marks if you run
+.B mailutil
+from a shell in which "#" is the comment character.
+.SH AUTHOR
+Mark Crispin, MRC@Washington.EDU
diff --git a/imap/src/mailutil/mailutil.c b/imap/src/mailutil/mailutil.c
new file mode 100644
index 00000000..2489195f
--- /dev/null
+++ b/imap/src/mailutil/mailutil.c
@@ -0,0 +1,942 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Mail utility
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@Washington.EDU
+ *
+ * Date: 2 February 1994
+ * Last Edited: 19 February 2008
+ */
+
+
+#include <stdio.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "c-client.h"
+#ifdef SYSCONFIG /* defined in env_unix.h */
+#include <pwd.h>
+#endif
+
+/* Globals */
+
+char *version = "13"; /* edit number */
+int debugp = NIL; /* flag saying debug */
+int verbosep = NIL; /* flag saying verbose */
+int rwcopyp = NIL; /* flag saying readwrite copy (for POP) */
+int kwcopyp = NIL; /* flag saying keyword copy */
+int ignorep = NIL; /* flag saying ignore keywords */
+int critical = NIL; /* flag saying in critical code */
+int trycreate = NIL; /* [TRYCREATE] seen */
+char *suffix = NIL; /* suffer merge mode suffix text */
+int ddelim = -1; /* destination delimiter */
+FILE *f = NIL;
+
+/* Usage strings */
+
+char *usage2 = "usage: %s %s\n\n%s\n";
+char *usage3 = "usage: %s %s %s\n\n%s\n";
+char *usgchk = "check [MAILBOX]";
+char *usgcre = "create MAILBOX";
+char *usgdel = "delete MAILBOX";
+char *usgren = "rename SOURCE DESTINATION";
+char *usgcpymov = "[-rw[copy]] [-kw[copy]] [-ig[nore]] SOURCE DESTINATION";
+char *usgappdel = "[-rw[copy]] [-kw[copy]] [-ig[nore]] SOURCE DESTINATION";
+char *usgprn = "prune mailbox SEARCH_CRITERIA";
+char *usgxfr = "transfer [-rw[copy]] [-kw[copy]] [-ig[nore]] [-m[erge] m] SOURCE DEST";
+#ifdef SYSCONFIG
+char *stdsw = "Standard switches valid with any command:\n\t[-d[ebug]] [-v[erbose]] [-u[ser] userid] [--]";
+#else
+char *stdsw = "Standard switches valid with any command:\n\t[-d[ebug]] [-v[erbose]]";
+#endif
+
+/* Merge modes */
+
+#define mPROMPT 1
+#define mAPPEND 2
+#define mSUFFIX 3
+
+
+/* Function prototypes */
+
+void ms_init (STRING *s,void *data,unsigned long size);
+char ms_next (STRING *s);
+void ms_setpos (STRING *s,unsigned long i);
+int main (int argc,char *argv[]);
+SEARCHPGM *prune_criteria (char *criteria);
+int prune_criteria_number (unsigned long *number,char **r);
+int mbxcopy (MAILSTREAM *source,MAILSTREAM *dest,char *dst,int create,int del,
+ int mode);
+long mm_append (MAILSTREAM *stream,void *data,char **flags,char **date,
+ STRING **message);
+
+
+/* Append package */
+
+typedef struct append_package {
+ MAILSTREAM *stream; /* source stream */
+ unsigned long msgno; /* current message number */
+ unsigned long msgmax; /* maximum message number */
+ char *flags; /* current flags */
+ char *date; /* message internal date */
+ STRING *message; /* stringstruct of message */
+} APPENDPACKAGE;
+
+
+/* Message string driver for message stringstructs */
+
+STRINGDRIVER mstring = {
+ ms_init, /* initialize string structure */
+ ms_next, /* get next byte in string structure */
+ ms_setpos /* set position in string structure */
+};
+
+/* Initialize file string structure for file stringstruct
+ * Accepts: string structure
+ * pointer to message data structure
+ * size of string
+ */
+
+void ms_init (STRING *s,void *data,unsigned long size)
+{
+ APPENDPACKAGE *md = (APPENDPACKAGE *) data;
+ s->data = data; /* note stream/msgno and header length */
+ mail_fetch_header (md->stream,md->msgno,NIL,NIL,&s->data1,
+ FT_PREFETCHTEXT|FT_PEEK);
+#if 0
+ s->size = size; /* message size */
+#else /* This kludge is necessary because of broken IMAP servers (sigh!) */
+ mail_fetch_text (md->stream,md->msgno,NIL,&s->size,FT_PEEK);
+ s->size += s->data1; /* header + body size */
+#endif
+ SETPOS (s,0);
+}
+
+
+/* Get next character from file stringstruct
+ * Accepts: string structure
+ * Returns: character, string structure chunk refreshed
+ */
+
+char ms_next (STRING *s)
+{
+ char c = *s->curpos++; /* get next byte */
+ SETPOS (s,GETPOS (s)); /* move to next chunk */
+ return c; /* return the byte */
+}
+
+
+/* Set string pointer position for file stringstruct
+ * Accepts: string structure
+ * new position
+ */
+
+void ms_setpos (STRING *s,unsigned long i)
+{
+ APPENDPACKAGE *md = (APPENDPACKAGE *) s->data;
+ if (i < s->data1) { /* want header? */
+ s->chunk = mail_fetch_header (md->stream,md->msgno,NIL,NIL,NIL,FT_PEEK);
+ s->chunksize = s->data1; /* header length */
+ s->offset = 0; /* offset is start of message */
+ }
+ else if (i < s->size) { /* want body */
+ s->chunk = mail_fetch_text (md->stream,md->msgno,NIL,NIL,FT_PEEK);
+ s->chunksize = s->size - s->data1;
+ s->offset = s->data1; /* offset is end of header */
+ }
+ else { /* off end of message */
+ s->chunk = NIL; /* make sure that we crack on this then */
+ s->chunksize = 1; /* make sure SNX cracks the right way... */
+ s->offset = i;
+ }
+ /* initial position and size */
+ s->curpos = s->chunk + (i -= s->offset);
+ s->cursize = s->chunksize - i;
+}
+
+/* Main program */
+
+int main (int argc,char *argv[])
+{
+ MAILSTREAM *source = NIL;
+ MAILSTREAM *dest = NIL;
+ SEARCHPGM *criteria;
+ char c,*s,*dp,*t,*t1,tmp[MAILTMPLEN],mbx[MAILTMPLEN];
+ unsigned long m,len,curlen,start,last;
+ int i;
+ int merge = NIL;
+ int retcode = 1;
+ int moreswitchp = T;
+ char *cmd = NIL;
+ char *src = NIL;
+ char *dst = NIL;
+ char *pgm = argc ? argv[0] : "mailutil";
+#include "linkage.c"
+ for (i = 1; i < argc; i++) {
+ s = argv[i]; /* pick up argument */
+ /* parse switches */
+ if (moreswitchp && (*s == '-')) {
+ if (!strcmp (s,"-debug") || !strcmp (s,"-d")) debugp = T;
+ else if (!strcmp (s,"-verbose") || !strcmp (s,"-v")) verbosep = T;
+ else if (!strcmp (s,"-rwcopy") || !strcmp (s,"-rw")) rwcopyp = T;
+ else if (!strcmp (s,"-kwcopy") || !strcmp (s,"-kw")) kwcopyp = T;
+ else if (!strcmp (s,"-ignore") || !strcmp (s,"-ig")) ignorep = T;
+ else if ((!strcmp (s,"-merge") || !strcmp (s,"-m")) && (++i < argc)) {
+ if (!strcmp (s = argv[i],"prompt")) merge = mPROMPT;
+ else if (!strcmp (s,"append")) merge = mAPPEND;
+ else if (!strncmp (s,"suffix=",7) && s[7]) {
+ merge = mSUFFIX;
+ suffix = cpystr (s+7);
+ }
+ else {
+ printf ("unknown merge option: %s\n",s);
+ exit (retcode);
+ }
+ }
+
+#ifdef SYSCONFIG
+ else if ((!strcmp (s,"-user") || !strcmp (s,"-u")) && (++i < argc)) {
+ struct passwd *pw = getpwnam (s = argv[i]);
+ if (!pw) {
+ printf ("unknown user id: %s\n",argv[i]);
+ exit (retcode);
+ }
+ else if (setuid (pw->pw_uid)) {
+ perror ("unable to change user id");
+ exit (retcode);
+ }
+ }
+#endif
+ /* -- means no more switches, so mailbox
+ name can start with "-" */
+ else if ((s[1] == '-') && !s[2]) moreswitchp = NIL;
+ else {
+ printf ("unknown switch: %s\n",s);
+ exit (retcode);
+ }
+ }
+ else if (!cmd) cmd = s; /* first non-switch is command */
+ else if (!src) src = s; /* second non-switch is source */
+ else if (!dst) dst = s; /* third non-switch is destination */
+ else {
+ printf ("unknown argument: %s\n",s);
+ exit (retcode);
+ }
+ }
+ if (kwcopyp && ignorep) {
+ puts ("-kwcopy and -ignore are mutually exclusive");
+ exit (retcode);
+ }
+ if (!cmd) cmd = ""; /* prevent SEGV */
+
+ if (!strcmp (cmd,"check")) { /* check for new messages */
+ if (!src) src = "INBOX";
+ if (dst || merge || rwcopyp || kwcopyp || ignorep)
+ printf (usage2,pgm,usgchk,stdsw);
+ else if (mail_status (source = (*src == '{') ?
+ mail_open (NIL,src,OP_HALFOPEN |
+ (debugp ? OP_DEBUG : NIL)) : NIL,
+ src,SA_MESSAGES | SA_RECENT | SA_UNSEEN))
+ retcode = 0;
+ }
+ else if (!strcmp (cmd,"create")) {
+ if (!src || dst || merge || rwcopyp || kwcopyp || ignorep)
+ printf (usage2,pgm,usgcre,stdsw);
+ else if (mail_create (source = (*src == '{') ?
+ mail_open (NIL,src,OP_HALFOPEN |
+ (debugp ? OP_DEBUG : NIL)) : NIL,src))
+ retcode = 0;
+ }
+ else if (!strcmp (cmd,"delete")) {
+ if (!src || dst || merge || rwcopyp || kwcopyp || ignorep)
+ printf (usage2,pgm,usgdel,stdsw);
+ else if (mail_delete (source = (*src == '{') ?
+ mail_open (NIL,src,OP_HALFOPEN |
+ (debugp ? OP_DEBUG : NIL)) : NIL,src))
+ retcode = 0;
+ }
+ else if (!strcmp (cmd,"rename")) {
+ if (!src || !dst || merge || rwcopyp || kwcopyp || ignorep)
+ printf (usage2,pgm,usgren,stdsw);
+ else if (mail_rename (source = (*src == '{') ?
+ mail_open (NIL,src,OP_HALFOPEN |
+ (debugp ? OP_DEBUG : NIL)) : NIL,src,dst))
+ retcode = 0;
+ }
+
+ else if ((i = !strcmp (cmd,"move")) || !strcmp (cmd,"copy")) {
+ if (!src || !dst || merge) printf (usage3,pgm,cmd,usgcpymov,stdsw);
+ else if (source = mail_open (NIL,src,((i || rwcopyp) ? NIL : OP_READONLY) |
+ (debugp ? OP_DEBUG : NIL))) {
+ dest = NIL; /* open destination stream if network */
+ if ((*dst != '{') || (dest = mail_open (NIL,dst,OP_HALFOPEN |
+ (debugp ? OP_DEBUG : NIL)))) {
+ if (mbxcopy (source,dest,dst,T,i,merge)) retcode = 0;
+ }
+ }
+ }
+ else if ((i = !strcmp (cmd,"appenddelete")) || !strcmp (cmd,"append")) {
+ if (!src || !dst || merge) printf (usage3,pgm,cmd,usgappdel,stdsw);
+ else if (source = mail_open (NIL,src,((i || rwcopyp) ? NIL : OP_READONLY) |
+ (debugp ? OP_DEBUG : NIL))) {
+ dest = NIL; /* open destination stream if network */
+ if ((*dst != '{') || (dest = mail_open (NIL,dst,OP_HALFOPEN |
+ (debugp ? OP_DEBUG : NIL)))) {
+ if (mbxcopy (source,dest,dst,NIL,i,merge)) retcode = 0;
+ }
+ }
+ }
+
+ else if (!strcmp (cmd,"prune")) {
+ if (!src || !dst || merge || rwcopyp || kwcopyp || ignorep ||
+ !(criteria = prune_criteria (dst))) printf (usage2,pgm,usgprn,stdsw);
+ else if ((source = mail_open (NIL,src,(debugp ? OP_DEBUG : NIL))) &&
+ mail_search_full (source,NIL,criteria,SE_FREE)) {
+ for (m = 1, s = t = NIL, len = start = last = 0; m <= source->nmsgs; m++)
+ if (mail_elt (source,m)->searched) {
+ if (s) { /* continuing a range? */
+ if (m == last + 1) last = m;
+ else { /* no, end of previous range? */
+ if (last != start) sprintf (t,":%lu,%lu",last,m);
+ /* no, just this message */
+ else sprintf (t,",%lu",m);
+ start = last = m; /* either way, start new range */
+ /* running out of space? */
+ if ((len - (curlen = (t += strlen (t)) - s)) < 20) {
+ fs_resize ((void **) &s,len += MAILTMPLEN);
+ t = s + curlen; /* relocate current pointer */
+ }
+ }
+ }
+ else { /* first time, start new buffer */
+ s = (char *) fs_get (len = MAILTMPLEN);
+ sprintf (s,"%lu",start = last = m);
+ t = s + strlen (s); /* end of buffer */
+ }
+ }
+ /* finish last range if necessary */
+ if (last != start) sprintf (t,":%lu",last);
+ if (s) { /* delete/expunge any matching messages */
+ mail_flag (source,s,"\\Deleted",ST_SET);
+ m = source->nmsgs; /* get number of messages before purge */
+ mail_expunge (source);
+ printf ("%lu message(s) purged\n",m - source->nmsgs);
+ fs_give ((void **) &s); /* flush buffer */
+ }
+ else puts ("No matching messages, so nothing purged");
+ source = mail_close (source);
+ }
+ }
+
+ else if (!strcmp (cmd,"transfer")) {
+ if (!src || !dst) printf (usage2,pgm,usgxfr,stdsw);
+ else if ((*src == '{') && /* open source mailbox */
+ !(source = mail_open (NIL,src,OP_HALFOPEN |
+ (debugp ? OP_DEBUG : NIL))));
+ else if ((*dst == '{') && /* open destination server */
+ !(dest = mail_open (NIL,dst,OP_HALFOPEN |
+ (debugp ? OP_DEBUG : NIL))));
+ else if (!(f = tmpfile ())) puts ("can't open temporary file");
+ else {
+ if (verbosep) puts ("Listing mailboxes...");
+ if (dest) strcpy (strchr (strcpy (tmp,dest->mailbox),'}') + 1,
+ dp = strchr (dst,'}') + 1);
+ else {
+ dp = dst;
+ tmp[0] = '\0';
+ }
+ mail_list (dest,tmp,"");
+ rewind (f); /* list all mailboxes matching prefix */
+ if (ddelim < 0) { /* if server failed to give delimiter */
+ puts ("warning: unable to get destination hierarchy delimiter!");
+ ddelim = 0; /* default to none */
+ }
+ if (source) strcpy (strchr (strcpy (tmp,source->mailbox),'}') + 1,
+ strchr (src,'}') + 1);
+ else strcpy (tmp,src);
+ mail_list (source,tmp,"*");
+ rewind (f);
+ /* read back mailbox names */
+ for (retcode = 0; !retcode && (fgets (tmp,MAILTMPLEN-1,f)); ) {
+ if (t = strchr (tmp+1,'\n')) *t = '\0';
+ for (t = mbx,t1 = dest ? dest->mailbox : "",c = NIL; (c != '}') && *t1;
+ *t++ = c= *t1++);
+ for (t1 = dp; *t1; *t++ = *t1++);
+ /* point to name without delim or netspec */
+ t1 = source ? (strchr (tmp+1,'}') + 1) : tmp + 1;
+ /* src and mbx have different delimiters? */
+ if (ddelim && (ddelim != tmp[0]))
+ while (c = *t1++) { /* swap delimiters then */
+ if (c == ddelim) c = tmp[0] ? tmp[0] : 'x';
+ else if (c == tmp[0]) c = ddelim;
+ *t++ = c;
+ }
+ /* easy case */
+ else while (*t1) *t++ = *t1++;
+ *t++ = '\0';
+ if (verbosep) {
+ printf ("Copying %s\n => %s\n",tmp+1,mbx);
+ fflush (stdout);
+ }
+ if (source = mail_open (source,tmp+1,(debugp ? OP_DEBUG : NIL) |
+ (rwcopyp ? NIL : OP_READONLY))) {
+ if (!mbxcopy (source,dest,mbx,T,NIL,merge)) retcode = 1;
+ if (source->dtb->flags & DR_LOCAL) source = mail_close (source);
+ }
+ else printf ("can't open source mailbox %s\n",tmp+1);
+ }
+ }
+ }
+
+ else {
+ printf ("%s version %s.%s\n\n",pgm,CCLIENTVERSION,version);
+ printf (usage2,pgm,"command [switches] arguments",stdsw);
+ printf ("\nCommands:\n %s\n",usgchk);
+ puts (" ;; report number of messages and new messages");
+ printf (" %s\n",usgcre);
+ puts (" ;; create new mailbox");
+ printf (" %s\n",usgdel);
+ puts (" ;; delete existing mailbox");
+ printf (" %s\n",usgren);
+ puts (" ;; rename mailbox to a new name");
+ printf (" copy %s\n",usgcpymov);
+ printf (" move %s\n",usgcpymov);
+ puts (" ;; create new mailbox and copy/move messages");
+ printf (" append %s\n",usgappdel);
+ printf (" appenddelete %s\n",usgappdel);
+ puts (" ;; copy/move messages to existing mailbox");
+ printf (" %s\n",usgprn);
+ puts (" ;; prune mailbox of messages matching criteria");
+ printf (" %s\n",usgxfr);
+ puts (" ;; copy source hierarchy to destination");
+ puts (" ;; -merge modes are prompt, append, or suffix=xxxx");
+ }
+ /* close streams */
+ if (source) mail_close (source);
+ if (dest) mail_close (dest);
+ exit (retcode);
+ return retcode; /* stupid compilers */
+}
+
+/* Pruning criteria, somewhat extended from mail_criteria()
+ * Accepts: criteria
+ * Returns: search program if parse successful, else NIL
+ */
+
+SEARCHPGM *prune_criteria (char *criteria)
+{
+ SEARCHPGM *pgm = NIL;
+ char *criterion,*r,tmp[MAILTMPLEN];
+ int f;
+ if (criteria) { /* only if criteria defined */
+ /* make writeable copy of criteria */
+ criteria = cpystr (criteria);
+ /* for each criterion */
+ for (pgm = mail_newsearchpgm (), criterion = strtok_r (criteria," ",&r);
+ criterion; (criterion = strtok_r (NIL," ",&r))) {
+ f = NIL; /* init then scan the criterion */
+ switch (*ucase (criterion)) {
+ case 'A': /* possible ALL, ANSWERED */
+ if (!strcmp (criterion+1,"LL")) f = T;
+ else if (!strcmp (criterion+1,"NSWERED")) f = pgm->answered = T;
+ break;
+ case 'B': /* possible BCC, BEFORE, BODY */
+ if (!strcmp (criterion+1,"CC"))
+ f = mail_criteria_string (&pgm->bcc,&r);
+ else if (!strcmp (criterion+1,"EFORE"))
+ f = mail_criteria_date (&pgm->before,&r);
+ else if (!strcmp (criterion+1,"ODY"))
+ f = mail_criteria_string (&pgm->body,&r);
+ break;
+ case 'C': /* possible CC */
+ if (!strcmp (criterion+1,"C")) f = mail_criteria_string (&pgm->cc,&r);
+ break;
+ case 'D': /* possible DELETED, DRAFT */
+ if (!strcmp (criterion+1,"ELETED")) f = pgm->deleted = T;
+ else if (!strcmp (criterion+1,"RAFT")) f = pgm->draft = T;
+ break;
+ case 'F': /* possible FLAGGED, FROM */
+ if (!strcmp (criterion+1,"LAGGED")) f = pgm->flagged = T;
+ else if (!strcmp (criterion+1,"ROM"))
+ f = mail_criteria_string (&pgm->from,&r);
+ break;
+ case 'K': /* possible KEYWORD */
+ if (!strcmp (criterion+1,"EYWORD"))
+ f = mail_criteria_string (&pgm->keyword,&r);
+ break;
+ case 'L': /* possible LARGER */
+ if (!strcmp (criterion+1,"ARGER"))
+ f = prune_criteria_number (&pgm->larger,&r);
+
+ case 'N': /* possible NEW */
+ if (!strcmp (criterion+1,"EW")) f = pgm->recent = pgm->unseen = T;
+ break;
+ case 'O': /* possible OLD, ON */
+ if (!strcmp (criterion+1,"LD")) f = pgm->old = T;
+ else if (!strcmp (criterion+1,"N"))
+ f = mail_criteria_date (&pgm->on,&r);
+ break;
+ case 'R': /* possible RECENT */
+ if (!strcmp (criterion+1,"ECENT")) f = pgm->recent = T;
+ break;
+ case 'S': /* possible SEEN, SENT*, SINCE, SMALLER,
+ SUBJECT */
+ if (!strcmp (criterion+1,"EEN")) f = pgm->seen = T;
+ else if (!strncmp (criterion+1,"ENT",3)) {
+ if (!strcmp (criterion+4,"BEFORE"))
+ f = mail_criteria_date (&pgm->sentbefore,&r);
+ else if (!strcmp (criterion+4,"ON"))
+ f = mail_criteria_date (&pgm->senton,&r);
+ else if (!strcmp (criterion+4,"SINCE"))
+ f = mail_criteria_date (&pgm->sentsince,&r);
+ }
+ else if (!strcmp (criterion+1,"INCE"))
+ f = mail_criteria_date (&pgm->since,&r);
+ else if (!strcmp (criterion+1,"MALLER"))
+ f = prune_criteria_number (&pgm->smaller,&r);
+ else if (!strcmp (criterion+1,"UBJECT"))
+ f = mail_criteria_string (&pgm->subject,&r);
+ break;
+ case 'T': /* possible TEXT, TO */
+ if (!strcmp (criterion+1,"EXT"))
+ f = mail_criteria_string (&pgm->text,&r);
+ else if (!strcmp (criterion+1,"O"))
+ f = mail_criteria_string (&pgm->to,&r);
+ break;
+ case 'U': /* possible UN* */
+ if (criterion[1] == 'N') {
+ if (!strcmp (criterion+2,"ANSWERED")) f = pgm->unanswered = T;
+ else if (!strcmp (criterion+2,"DELETED")) f = pgm->undeleted = T;
+ else if (!strcmp (criterion+2,"DRAFT")) f = pgm->undraft = T;
+ else if (!strcmp (criterion+2,"FLAGGED")) f = pgm->unflagged = T;
+ else if (!strcmp (criterion+2,"KEYWORD"))
+ f = mail_criteria_string (&pgm->unkeyword,&r);
+ else if (!strcmp (criterion+2,"SEEN")) f = pgm->unseen = T;
+ }
+ break;
+ default: /* we will barf below */
+ break;
+ }
+
+ if (!f) { /* if can't identify criterion */
+ sprintf (tmp,"Unknown search criterion: %.30s",criterion);
+ MM_LOG (tmp,ERROR);
+ mail_free_searchpgm (&pgm);
+ break;
+ }
+ }
+ /* no longer need copy of criteria */
+ fs_give ((void **) &criteria);
+ }
+ return pgm;
+}
+
+
+/* Parse a number
+ * Accepts: pointer to integer to return
+ * pointer to strtok state
+ * Returns: T if successful, else NIL
+ */
+
+int prune_criteria_number (unsigned long *number,char **r)
+{
+ char *t;
+ STRINGLIST *s = NIL;
+ /* parse the date and return fn if OK */
+ int ret = (mail_criteria_string (&s,r) &&
+ (*number = strtoul ((char *) s->text.data,&t,10)) && !*t) ?
+ T : NIL;
+ if (s) mail_free_stringlist (&s);
+ return ret;
+}
+
+/* Copy mailbox
+ * Accepts: stream open on source
+ * halfopen stream for destination or NIL
+ * destination mailbox name
+ * non-zero to create destination mailbox
+ * non-zero to delete messages from source after copying
+ * merge mode
+ * Returns: T if success, NIL if error
+ */
+
+int mbxcopy (MAILSTREAM *source,MAILSTREAM *dest,char *dst,int create,int del,
+ int mode)
+{
+ char *s,tmp[MAILTMPLEN];
+ APPENDPACKAGE ap;
+ STRING st;
+ char *ndst = NIL;
+ int ret = NIL;
+ trycreate = NIL; /* no TRYCREATE yet */
+ if (create) while (!mail_create (dest,dst) && (mode != mAPPEND)) {
+ switch (mode) {
+ case mPROMPT: /* prompt user for new name */
+ tmp[0] = '\0';
+ while (!tmp[0]) { /* read name */
+ fputs ("alternative name: ",stdout);
+ fflush (stdout);
+ fgets (tmp,MAILTMPLEN-1,stdin);
+ if (s = strchr (tmp,'\n')) *s = '\0';
+ }
+ if (ndst) fs_give ((void **) &ndst);
+ ndst = cpystr (tmp);
+ break;
+ case mSUFFIX: /* try again with new suffix */
+ if (ndst) fs_give ((void **) &ndst);
+ sprintf (ndst = (char *) fs_get (strlen (dst) + strlen (suffix) + 1),
+ "%s%s",dst,suffix);
+ printf ("retry to create %s\n",ndst);
+ mode = mPROMPT; /* switch to prompt mode if name fails */
+ break;
+ case NIL: /* not merging */
+ return NIL;
+ }
+ if (ndst) dst = ndst; /* if alternative name given, use it */
+ }
+
+ if (kwcopyp) {
+ int i;
+ size_t len;
+ char *dummymsg = "Date: Thu, 18 May 2006 00:00 -0700\r\nFrom: dummy@example.com\r\nSubject: dummy\r\n\r\ndummy\r\n";
+ for (i = 0,len = 0; i < NUSERFLAGS; ++i)
+ if (source->user_flags[i]) len += strlen (source->user_flags[i]) + 1;
+ if (len) { /* easy if no user flags to copy... */
+ char *t;
+ char *tail = "\\Deleted)";
+ char *flags = (char *) fs_get (1 + len + strlen (tail) + 1);
+ s = flags; *s++ = '(';
+ for (i = 0; i < NUSERFLAGS; ++i) if (t = source->user_flags[i]) {
+ while (*t) *s++ = *t++;
+ *s++ = ' ';
+ }
+ strcpy (s,tail); /* terminate flags list */
+ if ((dst[0] == '#') && ((dst[1] == 'D') || (dst[1] == 'd')) &&
+ ((dst[2] == 'R') || (dst[2] == 'r')) &&
+ ((dst[3] == 'I') || (dst[3] == 'i')) &&
+ ((dst[4] == 'V') || (dst[4] == 'v')) &&
+ ((dst[5] == 'E') || (dst[5] == 'e')) &&
+ ((dst[6] == 'R') || (dst[6] == 'r')) && (dst[7] == '.') &&
+ (t = strchr (dst+8,'/'))) ++t;
+ else t = dst;
+ INIT (&st,mail_string,dummymsg,strlen (dummymsg));
+ if (!(mail_append (dest,dst,&st) &&
+ (dest = mail_open (dest,t,debugp ? OP_DEBUG : NIL)))) {
+ fs_give ((void **) &flags);
+ return NIL;
+ }
+ mail_setflag (dest,"*",flags);
+ mail_expunge (dest);
+ fs_give ((void **) &flags);
+ }
+ }
+
+ if (source->nmsgs) { /* non-empty source */
+ if (verbosep) printf ("%s [%lu message(s)] => %s\n",
+ source->mailbox,source->nmsgs,dst);
+ ap.stream = source; /* prepare append package */
+ ap.msgno = 0;
+ ap.msgmax = source->nmsgs;
+ ap.flags = ap.date = NIL;
+ ap.message = &st;
+ /* make sure we have all messages */
+ sprintf (tmp,"1:%lu",ap.msgmax);
+ mail_fetchfast (source,tmp);
+ if (mail_append_multiple (dest,dst,mm_append,(void *) &ap)) {
+ --ap.msgno; /* make sure user knows it won */
+ if (verbosep) printf ("[Ok %lu messages(s)]\n",ap.msgno);
+ if (del && ap.msgno) { /* delete source messages */
+ sprintf (tmp,"1:%lu",ap.msgno);
+ mail_flag (source,tmp,"\\Deleted",ST_SET);
+ /* flush moved messages */
+ mail_expunge (source);
+ }
+ ret = T;
+ }
+ else if ((mode == mAPPEND) && trycreate)
+ ret = mbxcopy (source,dest,dst,create,del,mPROMPT);
+ else if (verbosep) puts ("[Failed]");
+ }
+ else { /* empty source */
+ if (verbosep) printf ("%s [empty] => %s\n",source->mailbox,dst);
+ ret = T;
+ }
+ if (ndst) fs_give ((void **) &ndst);
+ return ret;
+}
+
+/* Append callback
+ * Accepts: mail stream
+ * append package
+ * pointer to return flags
+ * pointer to return date
+ * pointer to return message stringstruct
+ * Returns: T on success
+ */
+
+long mm_append (MAILSTREAM *stream,void *data,char **flags,char **date,
+ STRING **message)
+{
+ char *t,*t1,tmp[MAILTMPLEN];
+ unsigned long u;
+ MESSAGECACHE *elt;
+ APPENDPACKAGE *ap = (APPENDPACKAGE *) data;
+ *flags = *date = NIL; /* assume no flags or date */
+ if (ap->flags) fs_give ((void **) &ap->flags);
+ if (ap->date) fs_give ((void **) &ap->date);
+ mail_gc (ap->stream,GC_TEXTS);
+ if (++ap->msgno <= ap->msgmax) {
+ /* initialize flag string */
+ memset (t = tmp,0,MAILTMPLEN);
+ /* output system flags */
+ if ((elt = mail_elt (ap->stream,ap->msgno))->seen) strcat (t," \\Seen");
+ if (elt->deleted) strcat (t," \\Deleted");
+ if (elt->flagged) strcat (t," \\Flagged");
+ if (elt->answered) strcat (t," \\Answered");
+ if (elt->draft) strcat (t," \\Draft");
+ /* any user flags? */
+ if (!ignorep && (u = elt->user_flags)) do
+ if ((t1 = ap->stream->user_flags[find_rightmost_bit (&u)]) &&
+ (MAILTMPLEN - ((t += strlen (t)) - tmp)) > (long) (2 + strlen (t1))){
+ *t++ = ' '; /* space delimiter */
+ strcpy (t,t1); /* copy the user flag */
+ }
+ while (u); /* until no more user flags */
+ *flags = ap->flags = cpystr (tmp + 1);
+ *date = ap->date = cpystr (mail_date (tmp,elt));
+ *message = ap->message; /* message stringstruct */
+ INIT (ap->message,mstring,(void *) ap,elt->rfc822_size);
+ }
+ else *message = NIL; /* all done */
+ return LONGT;
+}
+
+/* Co-routines from MAIL library */
+
+
+/* Message matches a search
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_searched (MAILSTREAM *stream,unsigned long msgno)
+{
+ /* dummy routine */
+}
+
+
+/* Message exists (i.e. there are that many messages in the mailbox)
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_exists (MAILSTREAM *stream,unsigned long number)
+{
+ /* dummy routine */
+}
+
+
+/* Message expunged
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_expunged (MAILSTREAM *stream,unsigned long number)
+{
+ /* dummy routine */
+}
+
+
+/* Message flags update seen
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_flags (MAILSTREAM *stream,unsigned long number)
+{
+ /* dummy routine */
+}
+
+/* Mailbox found
+ * Accepts: MAIL stream
+ * hierarchy delimiter
+ * mailbox name
+ * mailbox attributes
+ */
+
+void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes)
+{
+ /* note destination delimiter */
+ if (ddelim < 0) ddelim = delimiter;
+ /* if got a selectable name */
+ else if (!(attributes & LATT_NOSELECT) && *name)
+ fprintf (f,"%c%s\n",delimiter,name);
+}
+
+
+/* Subscribe mailbox found
+ * Accepts: MAIL stream
+ * hierarchy delimiter
+ * mailbox name
+ * mailbox attributes
+ */
+
+void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes)
+{
+ /* dummy routine */
+}
+
+
+/* Mailbox status
+ * Accepts: MAIL stream
+ * mailbox name
+ * mailbox status
+ */
+
+void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
+{
+ if (status->recent || status->unseen)
+ printf ("%lu new message(s) (%lu unseen),",status->recent,status->unseen);
+ else fputs ("No new messages,",stdout);
+ printf (" %lu total in %s\n",status->messages,mailbox);
+}
+
+/* Notification event
+ * Accepts: MAIL stream
+ * string to log
+ * error flag
+ */
+
+void mm_notify (MAILSTREAM *stream,char *string,long errflg)
+{
+ if (!errflg && (string[0] == '[') &&
+ ((string[1] == 'T') || (string[1] == 't')) &&
+ ((string[2] == 'R') || (string[2] == 'r')) &&
+ ((string[3] == 'Y') || (string[3] == 'y')) &&
+ ((string[4] == 'C') || (string[4] == 'c')) &&
+ ((string[5] == 'R') || (string[5] == 'r')) &&
+ ((string[6] == 'E') || (string[6] == 'e')) &&
+ ((string[7] == 'A') || (string[7] == 'a')) &&
+ ((string[8] == 'T') || (string[8] == 't')) &&
+ ((string[9] == 'E') || (string[9] == 'e')) &&
+ (string[10] == ']'))
+ trycreate = T;
+ mm_log (string,errflg); /* just do mm_log action */
+}
+
+
+/* Log an event for the user to see
+ * Accepts: string to log
+ * error flag
+ */
+
+void mm_log (char *string,long errflg)
+{
+ switch (errflg) {
+ case BYE:
+ case NIL: /* no error */
+ if (verbosep) fprintf (stderr,"[%s]\n",string);
+ break;
+ case PARSE: /* parsing problem */
+ case WARN: /* warning */
+ fprintf (stderr,"warning: %s\n",string);
+ break;
+ case ERROR: /* error */
+ default:
+ fprintf (stderr,"%s\n",string);
+ break;
+ }
+}
+
+
+/* Log an event to debugging telemetry
+ * Accepts: string to log
+ */
+
+void mm_dlog (char *string)
+{
+ fprintf (stderr,"%s\n",string);
+}
+
+/* Get user name and password for this host
+ * Accepts: parse of network mailbox name
+ * where to return user name
+ * where to return password
+ * trial count
+ */
+
+void mm_login (NETMBX *mb,char *username,char *password,long trial)
+{
+ char *s,tmp[MAILTMPLEN];
+ sprintf (s = tmp,"{%s/%s",mb->host,mb->service);
+ if (*mb->user) sprintf (tmp+strlen (tmp),"/user=%s",
+ strcpy (username,mb->user));
+ if (*mb->authuser) sprintf (tmp+strlen (tmp),"/authuser=%s",mb->authuser);
+ if (*mb->user) strcat (s = tmp,"} password:");
+ else {
+ printf ("%s} username: ",tmp);
+ fgets (username,NETMAXUSER-1,stdin);
+ username[NETMAXUSER-1] = '\0';
+ if (s = strchr (username,'\n')) *s = '\0';
+ s = "password: ";
+ }
+ strcpy (password,getpass (s));
+}
+
+
+/* About to enter critical code
+ * Accepts: stream
+ */
+
+void mm_critical (MAILSTREAM *stream)
+{
+ critical = T; /* note in critical code */
+}
+
+
+/* About to exit critical code
+ * Accepts: stream
+ */
+
+void mm_nocritical (MAILSTREAM *stream)
+{
+ critical = NIL; /* note not in critical code */
+}
+
+
+/* Disk error found
+ * Accepts: stream
+ * system error code
+ * flag indicating that mailbox may be clobbered
+ * Returns: T if user wants to abort
+ */
+
+long mm_diskerror (MAILSTREAM *stream,long errcode,long serious)
+{
+ return T;
+}
+
+
+/* Log a fatal error event
+ * Accepts: string to log
+ */
+
+void mm_fatal (char *string)
+{
+ fprintf (stderr,"FATAL: %s\n",string);
+}
diff --git a/imap/src/mailutil/makefile.nt b/imap/src/mailutil/makefile.nt
new file mode 100644
index 00000000..1a6eb515
--- /dev/null
+++ b/imap/src/mailutil/makefile.nt
@@ -0,0 +1,50 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: MAILUTIL Makefile for Windows 9x and Windows NT
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 25 February 1996
+# Last Edited: 30 August 2006
+
+
+C = ..\c-client
+CCLIENTLIB = $C\cclient.lib
+LIBS = $(CCLIENTLIB) ws2_32.lib winmm.lib advapi32.lib
+CFLAGS= -I$C /MT /W3 /DWIN32 /D_WIN32_WINNT=0x0400 -nologo $(EXTRACFLAGS)
+OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0400
+VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE
+CFLAGS= -I$C /MT /W3 $(OSCOMPAT) $(VSCOMPAT) -nologo $(EXTRACFLAGS)
+
+mailutil: $(CCLIENTLIB) mailutil.obj
+ LINK /NOLOGO mailutil.obj $(LIBS)
+
+mailutil.obj: $C\mail.h $C\smtp.h $C\misc.h $C\osdep.h mailutil.c
+
+$(CCLIENTLIB):
+ @echo Make c-client first
+ false
+
+clean:
+ del *.obj *.exe *.lib *.exp || rem
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo not war?
diff --git a/imap/src/mailutil/makefile.ntk b/imap/src/mailutil/makefile.ntk
new file mode 100644
index 00000000..42ae5185
--- /dev/null
+++ b/imap/src/mailutil/makefile.ntk
@@ -0,0 +1,51 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: MAILUTIL Makefile for Windows 9x and Windows NT + Kerberos
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 25 February 1996
+# Last Edited: 30 August 2006
+
+
+C = ..\c-client
+CCLIENTLIB = $C\cclient.lib
+K5 = \k5\lib
+K5LIB = $(K5)\comerr32.lib $(K5)\gssapi32.lib $(K5)\krb5_32.lib
+LIBS = $(CCLIENTLIB) $(K5LIB) ws2_32.lib winmm.lib advapi32.lib
+OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0400
+VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE
+CFLAGS= -I$C /MT /W3 $(OSCOMPAT) $(VSCOMPAT) -nologo $(EXTRACFLAGS)
+
+mailutil: $(CCLIENTLIB) mailutil.obj
+ LINK /NOLOGO mailutil.obj $(LIBS)
+
+mailutil.obj: $C\mail.h $C\smtp.h $C\misc.h $C\osdep.h mailutil.c
+
+$(CCLIENTLIB):
+ @echo Make c-client first
+ false
+
+clean:
+ del *.obj *.exe *.lib *.exp || rem
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo not war?
diff --git a/imap/src/mailutil/makefile.w2k b/imap/src/mailutil/makefile.w2k
new file mode 100644
index 00000000..7f0c4614
--- /dev/null
+++ b/imap/src/mailutil/makefile.w2k
@@ -0,0 +1,49 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: MAILUTIL Makefile for Windows 2000/XP
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 25 February 1996
+# Last Edited: 30 August 2006
+
+
+C = ..\c-client
+CCLIENTLIB = $C\cclient.lib
+LIBS = $(CCLIENTLIB) ws2_32.lib winmm.lib advapi32.lib secur32.lib crypt32.lib
+OSCOMPAT = /DWIN32
+VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE
+CFLAGS= -I$C /MT /W3 $(OSCOMPAT) $(VSCOMPAT) -nologo $(EXTRACFLAGS)
+
+mailutil: $(CCLIENTLIB) mailutil.obj
+ LINK /NOLOGO mailutil.obj $(LIBS)
+
+mailutil.obj: $C\mail.h $C\smtp.h $C\misc.h $C\osdep.h mailutil.c
+
+$(CCLIENTLIB):
+ @echo Make c-client first
+ false
+
+clean:
+ del *.obj *.exe *.lib *.exp || rem
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo not war?
diff --git a/imap/src/mlock/Makefile b/imap/src/mlock/Makefile
new file mode 100644
index 00000000..b7dc44c1
--- /dev/null
+++ b/imap/src/mlock/Makefile
@@ -0,0 +1,51 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: MLOCK Makefile
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 8 February 1999
+# Last Edited: 30 August 2006
+
+
+C = ../c-client
+SHELL = /bin/sh
+
+# Get local definitions from c-client directory
+
+CC = `cat $C/CCTYPE`
+CFLAGS = `cat $C/CFLAGS`
+
+all: mlock
+
+mlock: mlock.o
+ $(CC) $(CFLAGS) -o mlock mlock.o
+
+install: mlock
+ chgrp mail mlock
+ chmod 3711 mlock
+ cp -p mlock /etc/mlock
+
+clean:
+ rm -f *.o mlock || true
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo 'not war?'
diff --git a/imap/src/mlock/mlock.c b/imap/src/mlock/mlock.c
new file mode 100644
index 00000000..1dca40ed
--- /dev/null
+++ b/imap/src/mlock/mlock.c
@@ -0,0 +1,175 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Standalone Mailbox Lock program
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 8 February 1999
+ * Last Edited: 3 March 2008
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sysexits.h>
+#include <syslog.h>
+#include <grp.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <string.h>
+
+#define LOCKTIMEOUT 5 /* lock timeout in minutes */
+#define LOCKPROTECTION 0664
+
+#ifndef MAXHOSTNAMELEN /* Solaris still sucks */
+#define MAXHOSTNAMELEN 256
+#endif
+
+/* Fatal error
+ * Accepts: Message string
+ * exit code
+ * Returns: code
+ */
+
+int die (char *msg,int code)
+{
+ syslog (LOG_NOTICE,"(%u) %s",code,msg);
+ write (1,"?",1); /* indicate "impossible" failure */
+ return code;
+}
+
+
+int main (int argc,char *argv[])
+{
+ int ld,i;
+ int tries = LOCKTIMEOUT * 60 - 1;
+ char *s,*dir,*file,*lock,*hitch,tmp[1024];
+ size_t dlen,len;
+ struct stat sb,fsb;
+ struct group *grp = getgrnam ("mail");
+ /* get syslog */
+ openlog (argv[0],LOG_PID,LOG_MAIL);
+ if (!grp || (grp->gr_gid != getegid ()))
+ return die ("not setgid mail",EX_USAGE);
+ if (argc != 3) return die ("invalid arguments",EX_USAGE);
+ for (s = argv[1]; *s; s++)
+ if (!isdigit (*s)) return die ("invalid fd",EX_USAGE);
+ /* find directory */
+ if ((*argv[2] != '/') || !(file = strrchr (argv[2],'/')) || !file[1])
+ return die ("invalid path",EX_USAGE);
+ /* calculate lengths of directory and file */
+ if (!(dlen = file - argv[2])) dlen = 1;
+ len = strlen (++file);
+ /* make buffers */
+ dir = (char *) malloc (dlen + 1);
+ lock = (char *) malloc (len + 6);
+ hitch = (char *) malloc (len + 6 + 40 + MAXHOSTNAMELEN);
+ if (!dir || !lock || !hitch) return die ("malloc failure",errno);
+ strncpy (dir,argv[2],dlen); /* connect to desired directory */
+ dir[dlen] = '\0';
+ printf ("dir=%s, file=%s\n",dir,file);
+ chdir (dir);
+ /* get device/inode of file descriptor */
+ if (fstat (atoi (argv[1]),&fsb)) return die ("fstat failure",errno);
+ /* better be a regular file */
+ if ((fsb.st_mode & S_IFMT) != S_IFREG)
+ return die ("fd not regular file",EX_USAGE);
+ /* now get device/inode of file */
+ if (lstat (file,&sb)) return die ("lstat failure",errno);
+ /* does it match? */
+ if ((sb.st_mode & S_IFMT) != S_IFREG)
+ return die ("name not regular file",EX_USAGE);
+ if ((sb.st_dev != fsb.st_dev) || (sb.st_ino != fsb.st_ino))
+ return die ("fd and name different",EX_USAGE);
+ /* build lock filename */
+ sprintf (lock,"%s.lock",file);
+ if (!lstat (lock,&sb) && ((sb.st_mode & S_IFMT) != S_IFREG))
+ return die ("existing lock not regular file",EX_NOPERM);
+
+ do { /* until OK or out of tries */
+ if (!stat (lock,&sb) && (time (0) > (sb.st_ctime + LOCKTIMEOUT * 60)))
+ unlink (lock); /* time out lock if enough time has passed */
+ /* SUN-OS had an NFS
+ * As kludgy as an albatross;
+ * And everywhere that it was installed,
+ * It was a total loss.
+ * -- MRC 9/25/91
+ */
+ /* build hitching post file name */
+ sprintf (hitch,"%s.%lu.%lu.",lock,(unsigned long) time (0),
+ (unsigned long) getpid ());
+ len = strlen (hitch); /* append local host name */
+ gethostname (hitch + len,MAXHOSTNAMELEN);
+ /* try to get hitching-post file */
+ if ((ld = open (hitch,O_WRONLY|O_CREAT|O_EXCL,LOCKPROTECTION)) >= 0) {
+ /* make sure others can break the lock */
+ chmod (hitch,LOCKPROTECTION);
+ /* get device/inode of hitch file */
+ if (fstat (ld,&fsb)) return die ("hitch fstat failure",errno);
+ close (ld); /* close the hitching-post */
+ /* Note: link() may return an error even if it actually succeeded. So we
+ * always check for success via the link count, and ignore the error if
+ * the link count is right.
+ */
+ /* tie hitching-post to lock */
+ i = link (hitch,lock) ? errno : 0;
+ /* success if link count now 2 */
+ if (stat (hitch,&sb) || (sb.st_nlink != 2) ||
+ (fsb.st_dev != sb.st_dev) || (fsb.st_ino != sb.st_ino)) {
+ ld = -1; /* failed to hitch */
+ if (i == EPERM) { /* was it because links not allowed? */
+ /* Probably a FAT filesystem on Linux. It can't be NFS, so try
+ * creating the lock file directly.
+ */
+ if ((ld = open (lock,O_WRONLY|O_CREAT|O_EXCL,LOCKPROTECTION)) >= 0) {
+ /* get device/inode of lock file */
+ if (fstat (ld,&fsb)) return die ("lock fstat failure",errno);
+ close (ld); /* close the file */
+ }
+ /* give up immediately if protection failure */
+ else if (errno != EEXIST) tries = 0;
+ }
+ }
+ unlink (hitch); /* flush hitching post */
+ }
+ /* give up immediately if protection failure */
+ else if (errno == EACCES) tries = 0;
+ if (ld < 0) { /* lock failed */
+ if (tries--) sleep (1); /* sleep 1 second and try again */
+ else {
+ write (1,"-",1); /* hard failure */
+ return EX_CANTCREAT;
+ }
+ }
+ } while (ld < 0);
+ write (1,"+",1); /* indicate that all is well */
+ read (0,tmp,1); /* read continue signal from parent */
+ /* flush the lock file */
+ if (!stat (lock,&sb) && (fsb.st_dev == sb.st_dev) &&
+ (fsb.st_ino == sb.st_ino)) unlink (lock);
+ else syslog (LOG_NOTICE,"lock file %s/%s changed dev/inode",dir,lock);
+ return EX_OK;
+}
diff --git a/imap/src/mtest/Makefile b/imap/src/mtest/Makefile
new file mode 100644
index 00000000..a49be581
--- /dev/null
+++ b/imap/src/mtest/Makefile
@@ -0,0 +1,53 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: MTEST Makefile
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 25 February 1996
+# Last Edited: 30 August 2006
+
+
+C = ../c-client
+CCLIENTLIB = $C/c-client.a
+SHELL = /bin/sh
+
+# Get local definitions from c-client directory
+
+CC = `cat $C/CCTYPE`
+CFLAGS = -I$C `cat $C/CFLAGS`
+LDFLAGS = $(CCLIENTLIB) `cat $C/LDFLAGS`
+
+all: mtest
+
+mtest: $(CCLIENTLIB) mtest.o
+ $(CC) $(CFLAGS) -o mtest mtest.o $(LDFLAGS)
+
+mtest.o: $C/mail.h $C/misc.h $C/osdep.h $C/rfc822.h $C/smtp.h $C/nntp.h
+
+$(CCLIENTLIB):
+ cd $C;make
+
+clean:
+ rm -f *.o mtest || true
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo 'not war?'
diff --git a/imap/src/mtest/makefile.nt b/imap/src/mtest/makefile.nt
new file mode 100644
index 00000000..9486e42b
--- /dev/null
+++ b/imap/src/mtest/makefile.nt
@@ -0,0 +1,49 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: MTEST Makefile for Windows 9x and Windows NT
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 25 February 1996
+# Last Edited: 30 August 2006
+
+
+C = ..\c-client
+CCLIENTLIB = $C\cclient.lib
+LIBS = $(CCLIENTLIB) ws2_32.lib winmm.lib advapi32.lib
+OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0400
+VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE
+CFLAGS= -I$C /MT /W3 $(OSCOMPAT) $(VSCOMPAT) -nologo $(EXTRACFLAGS)
+
+mtest: $(CCLIENTLIB) mtest.obj
+ LINK /NOLOGO mtest.obj $(LIBS)
+
+mtest.obj: $C\mail.h $C\smtp.h $C\misc.h $C\osdep.h mtest.c
+
+$(CCLIENTLIB):
+ @echo Make c-client first
+ false
+
+clean:
+ del *.obj *.exe *.lib *.exp || rem
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo not war?
diff --git a/imap/src/mtest/makefile.ntk b/imap/src/mtest/makefile.ntk
new file mode 100644
index 00000000..91508162
--- /dev/null
+++ b/imap/src/mtest/makefile.ntk
@@ -0,0 +1,51 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: MTEST Makefile for Windows 9x and Windows NT + Kerberos
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 25 February 1996
+# Last Edited: 30 August 2006
+
+
+C = ..\c-client
+CCLIENTLIB = $C\cclient.lib
+K5 = \k5\lib
+K5LIB = $(K5)\comerr32.lib $(K5)\gssapi32.lib $(K5)\krb5_32.lib
+LIBS = $(CCLIENTLIB) $(K5LIB) ws2_32.lib winmm.lib advapi32.lib
+OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0400
+VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE
+CFLAGS= -I$C /MT /W3 $(OSCOMPAT) $(VSCOMPAT) -nologo $(EXTRACFLAGS)
+
+mtest: $(CCLIENTLIB) mtest.obj
+ LINK /NOLOGO mtest.obj $(LIBS)
+
+mtest.obj: $C\mail.h $C\smtp.h $C\misc.h $C\osdep.h mtest.c
+
+$(CCLIENTLIB):
+ @echo Make c-client first
+ false
+
+clean:
+ del *.obj *.exe *.lib *.exp || rem
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo not war?
diff --git a/imap/src/mtest/makefile.os2 b/imap/src/mtest/makefile.os2
new file mode 100644
index 00000000..1bf27046
--- /dev/null
+++ b/imap/src/mtest/makefile.os2
@@ -0,0 +1,53 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: MTEST Makefile
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 25 February 1996
+# Last Edited: 30 August 2006
+
+# Thanks to Nicholas Paul Sheppard who contributed the original version
+
+CC = gcc
+CFLAGS = -O2 -Zomf
+LD = gcc
+LDFLAGS = -s -Zomf -Zcrtdll
+
+C = ..\c-client
+CCLIENTLIB = $C\\c-client.lib
+LIBS = $(CCLIENTLIB) -l socket
+
+mtest.exe: $(CCLIENTLIB) mtest.obj
+ $(LD) $(LDFLAGS) -o $@ $^ $(LIBS)
+
+$(CCLIENTLIB):
+ @echo Make c-client first
+ false
+
+mtest.obj: mtest.c $C\mail.h $C\smtp.h $C\misc.h $C\osdep.h
+ $(CC) $(CFLAGS) -I$C -o $@ -c $<
+
+clean:
+ if exist *.obj del *.obj
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo not war?
diff --git a/imap/src/mtest/makefile.w2k b/imap/src/mtest/makefile.w2k
new file mode 100644
index 00000000..c5a1568b
--- /dev/null
+++ b/imap/src/mtest/makefile.w2k
@@ -0,0 +1,49 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: MTEST Makefile for Windows 2000/XP
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 25 February 1996
+# Last Edited: 30 August 2006
+
+
+C = ..\c-client
+CCLIENTLIB = $C\cclient.lib
+LIBS = $(CCLIENTLIB) ws2_32.lib winmm.lib advapi32.lib secur32.lib crypt32.lib
+OSCOMPAT = /DWIN32
+VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE
+CFLAGS= -I$C /MT /W3 $(OSCOMPAT) $(VSCOMPAT) -nologo $(EXTRACFLAGS)
+
+mtest: $(CCLIENTLIB) mtest.obj
+ LINK /NOLOGO mtest.obj $(LIBS)
+
+mtest.obj: $C\mail.h $C\smtp.h $C\misc.h $C\osdep.h mtest.c
+
+$(CCLIENTLIB):
+ @echo Make c-client first
+ false
+
+clean:
+ del *.obj *.exe *.lib *.exp || rem
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo not war?
diff --git a/imap/src/mtest/mtest.c b/imap/src/mtest/mtest.c
new file mode 100644
index 00000000..69af568b
--- /dev/null
+++ b/imap/src/mtest/mtest.c
@@ -0,0 +1,813 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Mail library test program
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 8 July 1988
+ * Last Edited: 5 November 2007
+ *
+ * This original version of this file is
+ * Copyright 1988 Stanford University
+ * and was developed in the Symbolic Systems Resources Group of the Knowledge
+ * Systems Laboratory at Stanford University in 1987-88, and was funded by the
+ * Biomedical Research Technology Program of the NationalInstitutes of Health
+ * under grant number RR-00785.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <signal.h>
+#include "c-client.h"
+#include "imap4r1.h"
+
+/* Excellent reasons to hate ifdefs, and why my real code never uses them */
+
+#ifndef unix
+# define unix 0
+#endif
+
+#if unix
+# define UNIXLIKE 1
+# define MACOS 0
+# include <pwd.h>
+#else
+# define UNIXLIKE 0
+# ifdef noErr
+# define MACOS 1
+# include <Memory.h>
+# else
+# define MACOS 0
+# endif
+#endif
+
+char *curhst = NIL; /* currently connected host */
+char *curusr = NIL; /* current login user */
+char personalname[MAILTMPLEN]; /* user's personal name */
+
+static char *hostlist[] = { /* SMTP server host list */
+ "mailhost",
+ "localhost",
+ NIL
+};
+
+static char *newslist[] = { /* Netnews server host list */
+ "news",
+ NIL
+};
+
+int main (void);
+void mm (MAILSTREAM *stream,long debug);
+void overview_header (MAILSTREAM *stream,unsigned long uid,OVERVIEW *ov,
+ unsigned long msgno);
+void header (MAILSTREAM *stream,long msgno);
+void display_body (BODY *body,char *pfx,long i);
+void status (MAILSTREAM *stream);
+void prompt (char *msg,char *txt);
+void smtptest (long debug);
+
+/* Main program - initialization */
+
+int main ()
+{
+ MAILSTREAM *stream = NIL;
+ void *sdb = NIL;
+ char *s,tmp[MAILTMPLEN];
+ long debug;
+#include "linkage.c"
+#if MACOS
+ {
+ size_t *base = (size_t *) 0x000908;
+ /* increase stack size on a Mac */
+ SetApplLimit ((Ptr) (*base - (size_t) 65535L));
+ }
+#endif
+ curusr = cpystr (((s = myusername ()) && *s) ? s : "somebody");
+#if UNIXLIKE
+ {
+ char *suffix;
+ struct passwd *pwd = getpwnam (curusr);
+ if (pwd) {
+ strcpy (tmp,pwd->pw_gecos);
+ /* dyke out the office and phone poop */
+ if (suffix = strchr (tmp,',')) suffix[0] = '\0';
+ strcpy (personalname,tmp);/* make a permanent copy of it */
+ }
+ else personalname[0] = '\0';
+ }
+#else
+ personalname[0] = '\0';
+#endif
+ curhst = cpystr (mylocalhost ());
+ puts ("MTest -- C client test program");
+ if (!*personalname) prompt ("Personal name: ",personalname);
+ /* user wants protocol telemetry? */
+ prompt ("Debug protocol (y/n)?",tmp);
+ ucase (tmp);
+ debug = (tmp[0] == 'Y') ? T : NIL;
+ do {
+ prompt ("Mailbox ('?' for help): ",tmp);
+ if (!strcmp (tmp,"?")) {
+ puts ("Enter INBOX, mailbox name, or IMAP mailbox as {host}mailbox");
+ puts ("Known local mailboxes:");
+ mail_list (NIL,NIL,"%");
+ if (s = sm_read (&sdb)) {
+ puts ("Local subscribed mailboxes:");
+ do (mm_lsub (NIL,NIL,s,NIL));
+ while (s = sm_read (&sdb));
+ }
+ puts ("or just hit return to quit");
+ }
+ else if (tmp[0]) stream = mail_open (stream,tmp,debug ? OP_DEBUG : NIL);
+ } while (!stream && tmp[0]);
+ mm (stream,debug); /* run user interface if opened */
+#if MACOS
+ /* clean up resolver */
+ if (resolveropen) CloseResolver ();
+#endif
+ return NIL;
+}
+
+/* MM command loop
+ * Accepts: MAIL stream
+ */
+
+void mm (MAILSTREAM *stream,long debug)
+{
+ void *sdb = NIL;
+ char cmd[MAILTMPLEN];
+ char *s,*arg;
+ unsigned long i;
+ unsigned long last = 0;
+ BODY *body;
+ status (stream); /* first report message status */
+ while (stream) {
+ prompt ("MTest>",cmd); /* prompt user, get command */
+ /* get argument */
+ if (arg = strchr (cmd,' ')) *arg++ = '\0';
+ switch (*ucase (cmd)) { /* dispatch based on command */
+ case 'B': /* Body command */
+ if (arg) last = atoi (arg);
+ else if (!last) {
+ puts ("?Missing message number");
+ break;
+ }
+ if (last && (last <= stream->nmsgs)) {
+ mail_fetchstructure (stream,last,&body);
+ if (body) display_body (body,NIL,(long) 0);
+ else puts ("%No body information available");
+ }
+ else puts ("?Bad message number");
+ break;
+ case 'C': /* Check command */
+ mail_check (stream);
+ status (stream);
+ break;
+ case 'D': /* Delete command */
+ if (arg) last = atoi (arg);
+ else {
+ if (last == 0) {
+ puts ("?Missing message number");
+ break;
+ }
+ arg = cmd;
+ sprintf (arg,"%lu",last);
+ }
+ if (last && (last <= stream->nmsgs))
+ mail_setflag (stream,arg,"\\DELETED");
+ else puts ("?Bad message number");
+ break;
+ case 'E': /* Expunge command */
+ mail_expunge (stream);
+ last = 0;
+ break;
+ case 'F': /* Find command */
+ if (!arg) {
+ arg = "%";
+ if (s = sm_read (&sdb)) {
+ puts ("Local network subscribed mailboxes:");
+ do if (*s == '{') (mm_lsub (NIL,NIL,s,NIL));
+ while (s = sm_read (&sdb));
+ }
+ }
+ puts ("Subscribed mailboxes:");
+ mail_lsub (((arg[0] == '{') && (*stream->mailbox == '{')) ? stream : NIL,
+ NIL,arg);
+ puts ("Known mailboxes:");
+ mail_list (((arg[0] == '{') && (*stream->mailbox == '{')) ? stream : NIL,
+ NIL,arg);
+ break;
+ case 'G':
+ mail_gc (stream,GC_ENV|GC_TEXTS|GC_ELT);
+ break;
+ case 'H': /* Headers command */
+ if (arg) {
+ if (!(last = atoi (arg))) {
+ mail_search (stream,arg);
+ for (i = 1; i <= stream->nmsgs; ++i)
+ if (mail_elt (stream,i)->searched) header (stream,i);
+ break;
+ }
+ }
+ else if (last == 0) {
+ puts ("?Missing message number");
+ break;
+ }
+ if (last && (last <= stream->nmsgs)) header (stream,last);
+ else puts ("?Bad message number");
+ break;
+ case 'L': /* Literal command */
+ if (arg) last = atoi (arg);
+ else if (!last) {
+ puts ("?Missing message number");
+ break;
+ }
+ if (last && (last <= stream->nmsgs))
+ puts (mail_fetch_message (stream,last,NIL,NIL));
+ else puts ("?Bad message number");
+ break;
+ case 'M':
+ mail_status (NIL,arg ? arg : stream->mailbox,
+ SA_MESSAGES|SA_RECENT|SA_UNSEEN|SA_UIDNEXT|SA_UIDVALIDITY);
+ break;
+ case 'N': /* New mailbox command */
+ if (!arg) {
+ puts ("?Missing mailbox");
+ break;
+ }
+ /* get the new mailbox */
+ while (!(stream = mail_open (stream,arg,debug))) {
+ prompt ("Mailbox: ",arg);
+ if (!arg[0]) break;
+ }
+ last = 0;
+ status (stream);
+ break;
+ case 'O': /* Overview command */
+ if (!arg) {
+ puts ("?Missing UID");
+ break;
+ }
+ mail_fetch_overview (stream,arg,overview_header);
+ break;
+ case 'P': /* Ping command */
+ mail_ping (stream);
+ status (stream);
+ break;
+ case 'Q': /* Quit command */
+ mail_close (stream);
+ stream = NIL;
+ break;
+ case 'S': /* Send command */
+ smtptest (debug);
+ break;
+ case '\0': /* null command (type next message) */
+ if (!last || (last++ >= stream->nmsgs)) {
+ puts ("%No next message");
+ break;
+ }
+ case 'T': /* Type command */
+ if (arg) last = atoi (arg);
+ else if (!last) {
+ puts ("?Missing message number");
+ break;
+ }
+ if (last && (last <= stream->nmsgs)) {
+ STRINGLIST *lines = mail_newstringlist ();
+ STRINGLIST *cur = lines;
+ cur->text.size = strlen ((char *) (cur->text.data = (unsigned char *)
+ cpystr ("Date")));
+ cur = cur->next = mail_newstringlist ();
+ cur->text.size = strlen ((char *) (cur->text.data = (unsigned char *)
+ cpystr ("From")));
+ cur = cur->next = mail_newstringlist ();
+ cur->text.size = strlen ((char *) (cur->text.data = (unsigned char *)
+ cpystr (">From")));
+ cur = cur->next = mail_newstringlist ();
+ cur->text.size = strlen ((char *) (cur->text.data = (unsigned char *)
+ cpystr ("Subject")));
+ cur = cur->next = mail_newstringlist ();
+ cur->text.size = strlen ((char *) (cur->text.data = (unsigned char *)
+ cpystr ("To")));
+ cur = cur->next = mail_newstringlist ();
+ cur->text.size = strlen ((char *) (cur->text.data = (unsigned char *)
+ cpystr ("cc")));
+ cur = cur->next = mail_newstringlist ();
+ cur->text.size = strlen ((char *) (cur->text.data = (unsigned char *)
+ cpystr ("Newsgroups")));
+ printf ("%s",mail_fetchheader_full (stream,last,lines,NIL,NIL));
+ puts (mail_fetchtext (stream,last));
+ mail_free_stringlist (&lines);
+ }
+ else puts ("?Bad message number");
+ break;
+ case 'U': /* Undelete command */
+ if (arg) last = atoi (arg);
+ else {
+ if (!last) {
+ puts ("?Missing message number");
+ break;
+ }
+ arg = cmd;
+ sprintf (arg,"%lu",last);
+ }
+ if (last > 0 && last <= stream->nmsgs)
+ mail_clearflag (stream,arg,"\\DELETED");
+ else puts ("?Bad message number");
+ break;
+ case 'X': /* Xit command */
+ mail_expunge (stream);
+ mail_close (stream);
+ stream = NIL;
+ break;
+ case '+':
+ mail_debug (stream); debug = T;
+ break;
+ case '-':
+ mail_nodebug (stream); debug = NIL;
+ break;
+ case '?': /* ? command */
+ puts ("Body, Check, Delete, Expunge, Find, GC, Headers, Literal,");
+ puts (" MailboxStatus, New Mailbox, Overview, Ping, Quit, Send, Type,");
+ puts ("Undelete, Xit, +, -, or <RETURN> for next message");
+ break;
+ default: /* bogus command */
+ printf ("?Unrecognized command: %s\n",cmd);
+ break;
+ }
+ }
+}
+
+/* MM display header
+ * Accepts: IMAP2 stream
+ * message number
+ */
+
+void overview_header (MAILSTREAM *stream,unsigned long uid,OVERVIEW *ov,
+ unsigned long msgno)
+{
+ if (ov) {
+ unsigned long i;
+ char *t,tmp[MAILTMPLEN];
+ ADDRESS *adr;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ MESSAGECACHE selt;
+ tmp[0] = elt->recent ? (elt->seen ? 'R': 'N') : ' ';
+ tmp[1] = (elt->recent | elt->seen) ? ' ' : 'U';
+ tmp[2] = elt->flagged ? 'F' : ' ';
+ tmp[3] = elt->answered ? 'A' : ' ';
+ tmp[4] = elt->deleted ? 'D' : ' ';
+ mail_parse_date (&selt,ov->date);
+ sprintf (tmp+5,"%4lu) ",elt->msgno);
+ mail_date (tmp+11,&selt);
+ tmp[17] = ' ';
+ tmp[18] = '\0';
+ memset (tmp+18,' ',(size_t) 20);
+ tmp[38] = '\0'; /* tie off with null */
+ /* get first from address from envelope */
+ for (adr = ov->from; adr && !adr->host; adr = adr->next);
+ if (adr) { /* if a personal name exists use it */
+ if (!(t = adr->personal))
+ sprintf (t = tmp+400,"%s@%s",adr->mailbox,adr->host);
+ memcpy (tmp+18,t,(size_t) min (20,(long) strlen (t)));
+ }
+ strcat (tmp," ");
+ if (i = elt->user_flags) {
+ strcat (tmp,"{");
+ while (i) {
+ strcat (tmp,stream->user_flags[find_rightmost_bit (&i)]);
+ if (i) strcat (tmp," ");
+ }
+ strcat (tmp,"} ");
+ }
+ sprintf (tmp + strlen (tmp),"%.25s (%lu chars)",
+ ov->subject ? ov->subject : " ",ov->optional.octets);
+ puts (tmp);
+ }
+ else printf ("%%No overview for UID %lu\n",uid);
+}
+
+/* MM display header
+ * Accepts: IMAP2 stream
+ * message number
+ */
+
+void header (MAILSTREAM *stream,long msgno)
+{
+ unsigned long i;
+ char tmp[MAILTMPLEN];
+ char *t;
+ MESSAGECACHE *cache = mail_elt (stream,msgno);
+ mail_fetchstructure (stream,msgno,NIL);
+ tmp[0] = cache->recent ? (cache->seen ? 'R': 'N') : ' ';
+ tmp[1] = (cache->recent | cache->seen) ? ' ' : 'U';
+ tmp[2] = cache->flagged ? 'F' : ' ';
+ tmp[3] = cache->answered ? 'A' : ' ';
+ tmp[4] = cache->deleted ? 'D' : ' ';
+ sprintf (tmp+5,"%4lu) ",cache->msgno);
+ mail_date (tmp+11,cache);
+ tmp[17] = ' ';
+ tmp[18] = '\0';
+ mail_fetchfrom (tmp+18,stream,msgno,(long) 20);
+ strcat (tmp," ");
+ if (i = cache->user_flags) {
+ strcat (tmp,"{");
+ while (i) {
+ strcat (tmp,stream->user_flags[find_rightmost_bit (&i)]);
+ if (i) strcat (tmp," ");
+ }
+ strcat (tmp,"} ");
+ }
+ mail_fetchsubject (t = tmp + strlen (tmp),stream,msgno,(long) 25);
+ sprintf (t += strlen (t)," (%lu chars)",cache->rfc822_size);
+ puts (tmp);
+}
+
+/* MM display body
+ * Accepts: BODY structure pointer
+ * prefix string
+ * index
+ */
+
+void display_body (BODY *body,char *pfx,long i)
+{
+ char tmp[MAILTMPLEN];
+ char *s = tmp;
+ PARAMETER *par;
+ PART *part; /* multipart doesn't have a row to itself */
+ if (body->type == TYPEMULTIPART) {
+ /* if not first time, extend prefix */
+ if (pfx) sprintf (tmp,"%s%ld.",pfx,++i);
+ else tmp[0] = '\0';
+ for (i = 0,part = body->nested.part; part; part = part->next)
+ display_body (&part->body,tmp,i++);
+ }
+ else { /* non-multipart, output oneline descriptor */
+ if (!pfx) pfx = ""; /* dummy prefix if top level */
+ sprintf (s," %s%ld %s",pfx,++i,body_types[body->type]);
+ if (body->subtype) sprintf (s += strlen (s),"/%s",body->subtype);
+ if (body->description) sprintf (s += strlen (s)," (%s)",body->description);
+ if (par = body->parameter) do
+ sprintf (s += strlen (s),";%s=%s",par->attribute,par->value);
+ while (par = par->next);
+ if (body->id) sprintf (s += strlen (s),", id = %s",body->id);
+ switch (body->type) { /* bytes or lines depending upon body type */
+ case TYPEMESSAGE: /* encapsulated message */
+ case TYPETEXT: /* plain text */
+ sprintf (s += strlen (s)," (%lu lines)",body->size.lines);
+ break;
+ default:
+ sprintf (s += strlen (s)," (%lu bytes)",body->size.bytes);
+ break;
+ }
+ puts (tmp); /* output this line */
+ /* encapsulated message? */
+ if ((body->type == TYPEMESSAGE) && !strcmp (body->subtype,"RFC822") &&
+ (body = body->nested.msg->body)) {
+ if (body->type == TYPEMULTIPART) display_body (body,pfx,i-1);
+ else { /* build encapsulation prefix */
+ sprintf (tmp,"%s%ld.",pfx,i);
+ display_body (body,tmp,(long) 0);
+ }
+ }
+ }
+}
+
+/* MM status report
+ * Accepts: MAIL stream
+ */
+
+void status (MAILSTREAM *stream)
+{
+ unsigned long i;
+ char *s,date[MAILTMPLEN];
+ THREADER *thr;
+ AUTHENTICATOR *auth;
+ rfc822_date (date);
+ puts (date);
+ if (stream) {
+ if (stream->mailbox)
+ printf (" %s mailbox: %s, %lu messages, %lu recent\n",
+ stream->dtb->name,stream->mailbox,stream->nmsgs,stream->recent);
+ else puts ("%No mailbox is open on this stream");
+ if (stream->user_flags[0]) {
+ printf ("Keywords: %s",stream->user_flags[0]);
+ for (i = 1; i < NUSERFLAGS && stream->user_flags[i]; ++i)
+ printf (", %s",stream->user_flags[i]);
+ puts ("");
+ }
+ if (!strcmp (stream->dtb->name,"imap")) {
+ if (LEVELIMAP4rev1 (stream)) s = "IMAP4rev1 (RFC 3501)";
+ else if (LEVEL1730 (stream)) s = "IMAP4 (RFC 1730)";
+ else if (LEVELIMAP2bis (stream)) s = "IMAP2bis";
+ else if (LEVEL1176 (stream)) s = "IMAP2 (RFC 1176)";
+ else s = "IMAP2 (RFC 1064)";
+ printf ("%s server %s\n",s,imap_host (stream));
+ if (LEVELIMAP4 (stream)) {
+ if (i = imap_cap (stream)->auth) {
+ s = "";
+ printf ("Mutually-supported SASL mechanisms:");
+ while (auth = mail_lookup_auth (find_rightmost_bit (&i) + 1)) {
+ printf (" %s",auth->name);
+ if (!strcmp (auth->name,"PLAIN"))
+ s = "\n [LOGIN will not be listed here if PLAIN is supported]";
+ }
+ puts (s);
+ }
+ printf ("Supported standard extensions:\n");
+ if (LEVELACL (stream)) puts (" Access Control lists (RFC 2086)");
+ if (LEVELQUOTA (stream)) puts (" Quotas (RFC 2087)");
+ if (LEVELLITERALPLUS (stream))
+ puts (" Non-synchronizing literals (RFC 2088)");
+ if (LEVELIDLE (stream)) puts (" IDLE unsolicited update (RFC 2177)");
+ if (LEVELMBX_REF (stream)) puts (" Mailbox referrals (RFC 2193)");
+ if (LEVELLOG_REF (stream)) puts (" Login referrals (RFC 2221)");
+ if (LEVELANONYMOUS (stream)) puts (" Anonymous access (RFC 2245)");
+ if (LEVELNAMESPACE (stream)) puts (" Multiple namespaces (RFC 2342)");
+ if (LEVELUIDPLUS (stream)) puts (" Extended UID behavior (RFC 2359)");
+ if (LEVELSTARTTLS (stream))
+ puts (" Transport Layer Security (RFC 2595)");
+ if (LEVELLOGINDISABLED (stream))
+ puts (" LOGIN command disabled (RFC 2595)");
+ if (LEVELID (stream))
+ puts (" Implementation identity negotiation (RFC 2971)");
+ if (LEVELCHILDREN (stream))
+ puts (" LIST children announcement (RFC 3348)");
+ if (LEVELMULTIAPPEND (stream))
+ puts (" Atomic multiple APPEND (RFC 3502)");
+ if (LEVELBINARY (stream))
+ puts (" Binary body content (RFC 3516)");
+ if (LEVELUNSELECT (stream)) puts (" Mailbox unselect (RFC 3691)");
+ if (LEVELURLAUTH (stream))
+ puts (" URL authenticated fetch (RFC 4467)");
+ if (LEVELCATENATE (stream)) puts (" Catenation (RFC 4469)");
+ if (LEVELCONDSTORE (stream)) puts (" Conditional STORE (RFC 4551)");
+ if (LEVELESEARCH (stream)) puts (" Extended SEARCH (RFC 4731)");
+ puts ("Supported draft extensions:");
+ if (LEVELSASLIR (stream)) puts (" SASL initial client response");
+ if (LEVELSORT (stream)) puts (" Server-based sorting");
+ if (LEVELTHREAD (stream)) {
+ printf (" Server-based threading:");
+ for (thr = imap_cap (stream)->threader; thr; thr = thr->next)
+ printf (" %s",thr->name);
+ putchar ('\n');
+ }
+ if (LEVELSCAN (stream)) puts (" Mailbox text scan");
+ if (i = imap_cap (stream)->extlevel) {
+ printf ("Supported BODYSTRUCTURE extensions:");
+ switch (i) {
+ case BODYEXTLOC: printf (" location");
+ case BODYEXTLANG: printf (" language");
+ case BODYEXTDSP: printf (" disposition");
+ case BODYEXTMD5: printf (" MD5\n");
+ }
+ }
+ }
+ else putchar ('\n');
+ }
+ }
+}
+
+
+/* Prompt user for input
+ * Accepts: pointer to prompt message
+ * pointer to input buffer
+ */
+
+void prompt (char *msg,char *txt)
+{
+ printf ("%s",msg);
+ gets (txt);
+}
+
+/* Interfaces to C-client */
+
+
+void mm_searched (MAILSTREAM *stream,unsigned long number)
+{
+}
+
+
+void mm_exists (MAILSTREAM *stream,unsigned long number)
+{
+}
+
+
+void mm_expunged (MAILSTREAM *stream,unsigned long number)
+{
+}
+
+
+void mm_flags (MAILSTREAM *stream,unsigned long number)
+{
+}
+
+
+void mm_notify (MAILSTREAM *stream,char *string,long errflg)
+{
+ mm_log (string,errflg);
+}
+
+
+void mm_list (MAILSTREAM *stream,int delimiter,char *mailbox,long attributes)
+{
+ putchar (' ');
+ if (delimiter) putchar (delimiter);
+ else fputs ("NIL",stdout);
+ putchar (' ');
+ fputs (mailbox,stdout);
+ if (attributes & LATT_NOINFERIORS) fputs (", no inferiors",stdout);
+ if (attributes & LATT_NOSELECT) fputs (", no select",stdout);
+ if (attributes & LATT_MARKED) fputs (", marked",stdout);
+ if (attributes & LATT_UNMARKED) fputs (", unmarked",stdout);
+ putchar ('\n');
+}
+
+
+void mm_lsub (MAILSTREAM *stream,int delimiter,char *mailbox,long attributes)
+{
+ putchar (' ');
+ if (delimiter) putchar (delimiter);
+ else fputs ("NIL",stdout);
+ putchar (' ');
+ fputs (mailbox,stdout);
+ if (attributes & LATT_NOINFERIORS) fputs (", no inferiors",stdout);
+ if (attributes & LATT_NOSELECT) fputs (", no select",stdout);
+ if (attributes & LATT_MARKED) fputs (", marked",stdout);
+ if (attributes & LATT_UNMARKED) fputs (", unmarked",stdout);
+ putchar ('\n');
+}
+
+
+void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
+{
+ printf (" Mailbox %s",mailbox);
+ if (status->flags & SA_MESSAGES) printf (", %lu messages",status->messages);
+ if (status->flags & SA_RECENT) printf (", %lu recent",status->recent);
+ if (status->flags & SA_UNSEEN) printf (", %lu unseen",status->unseen);
+ if (status->flags & SA_UIDVALIDITY) printf (", %lu UID validity",
+ status->uidvalidity);
+ if (status->flags & SA_UIDNEXT) printf (", %lu next UID",status->uidnext);
+ printf ("\n");
+}
+
+
+void mm_log (char *string,long errflg)
+{
+ switch ((short) errflg) {
+ case NIL:
+ printf ("[%s]\n",string);
+ break;
+ case PARSE:
+ case WARN:
+ printf ("%%%s\n",string);
+ break;
+ case ERROR:
+ printf ("?%s\n",string);
+ break;
+ }
+}
+
+
+void mm_dlog (char *string)
+{
+ puts (string);
+}
+
+
+void mm_login (NETMBX *mb,char *user,char *pwd,long trial)
+{
+ char *s,tmp[MAILTMPLEN];
+ if (curhst) fs_give ((void **) &curhst);
+ curhst = (char *) fs_get (1+strlen (mb->host));
+ strcpy (curhst,mb->host);
+ sprintf (s = tmp,"{%s/%s",mb->host,mb->service);
+ if (*mb->user) sprintf (tmp+strlen (tmp),"/user=%s",strcpy (user,mb->user));
+ if (*mb->authuser) sprintf (tmp+strlen (tmp),"/authuser=%s",mb->authuser);
+ if (*mb->user) strcat (s = tmp,"} password:");
+ else {
+ printf ("%s} username: ",tmp);
+ fgets (user,NETMAXUSER-1,stdin);
+ user[NETMAXUSER-1] = '\0';
+ if (s = strchr (user,'\n')) *s = '\0';
+ s = "password: ";
+ }
+ if (curusr) fs_give ((void **) &curusr);
+ curusr = cpystr (user);
+ strcpy (pwd,getpass (s));
+}
+
+
+void mm_critical (MAILSTREAM *stream)
+{
+}
+
+
+void mm_nocritical (MAILSTREAM *stream)
+{
+}
+
+
+long mm_diskerror (MAILSTREAM *stream,long errcode,long serious)
+{
+#if UNIXLIKE
+ kill (getpid (),SIGSTOP);
+#else
+ abort ();
+#endif
+ return NIL;
+}
+
+
+void mm_fatal (char *string)
+{
+ printf ("?%s\n",string);
+}
+
+/* SMTP tester */
+
+void smtptest (long debug)
+{
+ SENDSTREAM *stream = NIL;
+ char line[MAILTMPLEN];
+ char *text = (char *) fs_get (8*MAILTMPLEN);
+ ENVELOPE *msg = mail_newenvelope ();
+ BODY *body = mail_newbody ();
+ msg->from = mail_newaddr ();
+ msg->from->personal = cpystr (personalname);
+ msg->from->mailbox = cpystr (curusr);
+ msg->from->host = cpystr (curhst);
+ msg->return_path = mail_newaddr ();
+ msg->return_path->mailbox = cpystr (curusr);
+ msg->return_path->host = cpystr (curhst);
+ prompt ("To: ",line);
+ rfc822_parse_adrlist (&msg->to,line,curhst);
+ if (msg->to) {
+ prompt ("cc: ",line);
+ rfc822_parse_adrlist (&msg->cc,line,curhst);
+ }
+ else {
+ prompt ("Newsgroups: ",line);
+ if (*line) msg->newsgroups = cpystr (line);
+ else {
+ mail_free_body (&body);
+ mail_free_envelope (&msg);
+ fs_give ((void **) &text);
+ return;
+ }
+ }
+ prompt ("Subject: ",line);
+ msg->subject = cpystr (line);
+ puts (" Msg (end with a line with only a '.'):");
+ body->type = TYPETEXT;
+ *text = '\0';
+ while (gets (line)) {
+ if (line[0] == '.') {
+ if (line[1] == '\0') break;
+ else strcat (text,".");
+ }
+ strcat (text,line);
+ strcat (text,"\015\012");
+ }
+ body->contents.text.data = (unsigned char *) text;
+ body->contents.text.size = strlen (text);
+ rfc822_date (line);
+ msg->date = (char *) fs_get (1+strlen (line));
+ strcpy (msg->date,line);
+ if (msg->to) {
+ puts ("Sending...");
+ if (stream = smtp_open (hostlist,debug)) {
+ if (smtp_mail (stream,"MAIL",msg,body)) puts ("[Ok]");
+ else printf ("[Failed - %s]\n",stream->reply);
+ }
+ }
+ else {
+ puts ("Posting...");
+ if (stream = nntp_open (newslist,debug)) {
+ if (nntp_mail (stream,msg,body)) puts ("[Ok]");
+ else printf ("[Failed - %s]\n",stream->reply);
+ }
+ }
+ if (stream) smtp_close (stream);
+ else puts ("[Can't open connection to any server]");
+ mail_free_envelope (&msg);
+ mail_free_body (&body);
+}
diff --git a/imap/src/osdep/amiga/Makefile b/imap/src/osdep/amiga/Makefile
new file mode 100644
index 00000000..1f08e97e
--- /dev/null
+++ b/imap/src/osdep/amiga/Makefile
@@ -0,0 +1,231 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: C client makefile for Amiga
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 11 May 1989
+# Last Edited: 5 November 2006
+
+
+# Command line build parameters
+
+EXTRAAUTHENTICATORS=
+EXTRADRIVERS=mbox
+PASSWDTYPE=std
+
+
+# Build parameters normally set by the individual port
+
+AMICFLAGS=-O -DNO_INLINE_STDARG -Dunix
+AMILDFLAGS=/pine/libc.a -lamiga -lauto
+CHECKPW=std
+LOGINPW=std
+ACTIVEFILE=/UULib/News/Active
+SPOOLDIR=/usr/spool
+MAILSPOOL=/AmiTCP/Mail
+NEWSSPOOL=/UUNews
+MD5PWD="/etc/cram-md5.pwd"
+
+
+# Default formats for creating new mailboxes and for empty mailboxes in the
+# default namespace; must be set to the associated driver's prototype.
+#
+# The CREATEPROTO is the default format for new mailbox creation.
+# The EMPTYPROTO is the default format for handling zero-byte files.
+#
+# Normally, this is set by the individual port.
+#
+# NOTE: namespace formats (e.g. mh and news) can not be set as a default format
+# since they do not exist in the default namespace. Also, it is meaningless to
+# set certain other formats (e.g. mbx, mx, and mix) as the EMPTYPROTO since
+# these formats can never be empty files.
+
+CREATEPROTO=unixproto
+EMPTYPROTO=unixproto
+
+
+# Commands possibly overriden by the individual port
+
+ARRC=ar rc
+CC=cc
+LN=cp
+RANLIB=ranlib
+RM=rm -f
+
+
+# Standard distribution build parameters
+
+DEFAULTAUTHENTICATORS=ext md5 pla log
+DEFAULTDRIVERS=imap nntp pop3 mix mx mbx tenex mtx mh mmdf unix news phile
+CHUNKSIZE=65536
+
+
+# Normally no need to change any of these
+
+ARCHIVE=c-client.a
+BINARIES=mail.o misc.o newsrc.o smanager.o osdep.o utf8.o utf8aux.o \
+ dummy.o pseudo.o netmsg.o flstring.o fdstring.o \
+ rfc822.o nntp.o smtp.o imap4r1.o pop3.o \
+ unix.o mbx.o mmdf.o tenex.o mtx.o news.o phile.o mh.o mx.o mix.o
+CFLAGS=$(BASECFLAGS) $(EXTRACFLAGS)
+MAKE=make
+MV=mv
+SHELL=/bin/sh
+
+
+# Primary build command
+
+BUILDOPTIONS= EXTRACFLAGS=$(EXTRACFLAGS) EXTRALDFLAGS=$(EXTRALDFLAGS)\
+ EXTRADRIVERS=$(EXTRADRIVERS) EXTRAAUTHENTICATORS=$(EXTRAAUTHENTICATORS)\
+ PASSWDTYPE=$(PASSWDTYPE)
+BUILD=$(MAKE) build $(BUILDOPTIONS) $(SPECIALS)
+
+
+# Here if no make argument established
+
+missing: osdep.h
+ $(MAKE) $(ARCHIVE) CC=`cat CCTYPE` CFLAGS="`cat CFLAGS`"
+
+osdep.h:
+ @echo You must specify what type of system
+ @false
+
+
+# Current ports
+
+ami: # AmigaDOS
+ $(BUILD) OS=$@ \
+ BASECFLAGS="-DOLD $(AMICFLAGS)" \
+ BASELDFLAGS="$(AMILDFLAGS) -lamitcp000" \
+ CC=gcc
+
+am2: # AmigaDOS with a 68020+
+ $(BUILD) OS=ami \
+ BASECFLAGS="-DOLD -m68020 $(AMICFLAGS)" \
+ BASELDFLAGS="$(AMILDFLAGS) -lamitcp" \
+ CC=gcc
+
+amn: # AmigaDOS with a 680x0 using "new" socket library
+ $(BUILD) OS=ami \
+ BASELDFLAGS="$(AMILDFLAGS) -lnewamitcp000" \
+ CC=gcc
+
+ama: # AmigaDOS using AS225R2
+ $(BUILD) OS=ami \
+ MAILSPOOL=/INet/Mail \
+ BASECFLAGS="-m68020 $(AMICFLAGS)" \
+ BASELDFLAGS="$(AMILDFLAGS) -las225r2" \
+ CC=gcc
+
+# Build it!
+
+build: clean once ckp$(PASSWDTYPE) $(EXTRAAUTHENTICATORS) $(ARCHIVE)
+
+$(ARCHIVE): $(BINARIES)
+ $(RM) $(ARCHIVE) || true
+ $(ARRC) $(ARCHIVE) $(BINARIES)
+ $(RANLIB) $(ARCHIVE)
+
+# Cleanup
+
+clean:
+ $(RM) *.o linkage.[ch] auths.c $(ARCHIVE) osdep.* *TYPE *FLAGS || true
+
+
+# Dependencies
+
+dummy.o: mail.h misc.h osdep.h dummy.h
+fdstring.o: mail.h misc.h osdep.h fdstring.h
+flstring.o: mail.h misc.h osdep.h flstring.h
+imap4r1.o: mail.h misc.h osdep.h imap4r1.h rfc822.h
+mail.o: mail.h misc.h osdep.h rfc822.h linkage.h
+mbx.o: mail.h misc.h osdep.h dummy.h
+mh.o: mail.h misc.h osdep.h mh.h dummy.h
+mix.o: mail.h misc.h osdep.h dummy.h
+mx.o: mail.h misc.h osdep.h mx.h dummy.h
+misc.o: mail.h misc.h osdep.h
+mmdf.o: mail.h misc.h osdep.h pseudo.h dummy.h
+mtx.o: mail.h misc.h osdep.h dummy.h
+netmsg.o: mail.h misc.h osdep.h netmsg.h
+news.o: mail.h misc.h osdep.h
+newsrc.o: mail.h misc.h osdep.h newsrc.h
+nntp.o: mail.h misc.h osdep.h netmsg.h smtp.h nntp.h rfc822.h
+phile.o: mail.h misc.h osdep.h rfc822.h dummy.h
+pseudo.o: pseudo.h
+pop3.o: mail.h misc.h osdep.h pop3.h rfc822.h
+smanager.o: mail.h misc.h osdep.h
+smtp.o: mail.h misc.h osdep.h smtp.h rfc822.h
+rfc822.o: mail.h misc.h osdep.h rfc822.h
+tenex.o: mail.h misc.h osdep.h dummy.h
+unix.o: mail.h misc.h osdep.h unix.h pseudo.h dummy.h
+utf8.o: mail.h misc.h osdep.h utf8.h
+utf8aux.o: mail.h misc.h osdep.h utf8.h
+
+
+# OS-dependent
+
+osdep.o:mail.h misc.h env.h fs.h ftl.h nl.h tcp.h \
+ osdep.h env_ami.h tcp_ami.h \
+ osdep.c env_ami.c fs_ami.c ftl_ami.c nl_ami.c tcp_ami.c \
+ auths.c gethstid.c \
+ gr_waitp.c \
+ auth_log.c auth_md5.c auth_pla.c \
+ pmatch.c scandir.c \
+ tz_bsd.c \
+ write.c \
+ strerror.c strpbrk.c strstr.c strtok.c strtoul.c \
+ OSCFLAGS
+ $(CC) $(CFLAGS) `cat OSCFLAGS` -c osdep.c
+
+osdep.c: osdepbas.c osdepckp.c osdeplog.c osdepssl.c
+ $(RM) osdep.c || true
+ cat osdepbas.c osdepckp.c osdeplog.c osdepssl.c > osdep.c
+
+
+# Once-only environment setup
+
+once:
+ @echo Once-only environment setup...
+ ./drivers $(EXTRADRIVERS) $(DEFAULTDRIVERS) dummy
+ ./mkauths $(EXTRAAUTHENTICATORS) $(DEFAULTAUTHENTICATORS)
+ echo $(CC) > CCTYPE
+ echo $(CFLAGS) -DCHUNKSIZE=$(CHUNKSIZE) > CFLAGS
+ echo -DCREATEPROTO=$(CREATEPROTO) -DEMPTYPROTO=$(EMPTYPROTO) \
+ -DMD5ENABLE=\"$(MD5PWD)\" -DMAILSPOOL=\"$(MAILSPOOL)\" \
+ -DACTIVEFILE=\"$(ACTIVEFILE)\" -DNEWSSPOOL=\"$(NEWSSPOOL)\" \
+ -DANONYMOUSHOME=\"$(MAILSPOOL)/anonymous\" > OSCFLAGS
+ echo $(BASELDFLAGS) $(EXTRALDFLAGS) > LDFLAGS
+ $(LN) os_$(OS).h osdep.h
+ $(LN) os_$(OS).c osdepbas.c
+ $(LN) log_$(LOGINPW).c osdeplog.c
+ $(LN) ssl_none.c osdepssl.c
+
+
+# Password checkers
+
+ckpstd: # Port standard
+ $(LN) ckp_$(CHECKPW).c osdepckp.c
+
+
+# A monument to a hack of long ago and far away...
+
+love:
+ @echo not war?
diff --git a/imap/src/osdep/amiga/ckp_std.c b/imap/src/osdep/amiga/ckp_std.c
new file mode 100644
index 00000000..36255cc1
--- /dev/null
+++ b/imap/src/osdep/amiga/ckp_std.c
@@ -0,0 +1,42 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Standard check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ return (pw->pw_passwd && pw->pw_passwd[0] && pw->pw_passwd[1] &&
+ !strcmp (pw->pw_passwd,(char *) crypt (pass,pw->pw_passwd))) ?
+ pw : NIL;
+}
diff --git a/imap/src/osdep/amiga/drivers b/imap/src/osdep/amiga/drivers
new file mode 100755
index 00000000..bd49365f
--- /dev/null
+++ b/imap/src/osdep/amiga/drivers
@@ -0,0 +1,36 @@
+#!/bin/sh
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+# Program: Driver Linkage Generator
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 11 October 1989
+# Last Edited: 30 August 2006
+
+
+# Erase old driver linkage
+rm -f linkage.[ch]
+
+# Now define the new list
+for driver
+ do
+ echo "extern DRIVER "$driver"driver;" >> linkage.h
+ echo " mail_link (&"$driver"driver); /* link in the $driver driver */" | cat >> linkage.c
+done
diff --git a/imap/src/osdep/amiga/dummy.c b/imap/src/osdep/amiga/dummy.c
new file mode 100644
index 00000000..b003a0ba
--- /dev/null
+++ b/imap/src/osdep/amiga/dummy.c
@@ -0,0 +1,809 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 9 May 1991
+ * Last Edited: 1 June 2007
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <pwd.h>
+#include <sys/stat.h>
+#include "dummy.h"
+#include "misc.h"
+
+/* Function prototypes */
+
+DRIVER *dummy_valid (char *name);
+void *dummy_parameters (long function,void *value);
+void dummy_list_work (MAILSTREAM *stream,char *dir,char *pat,char *contents,
+ long level);
+long dummy_listed (MAILSTREAM *stream,char delimiter,char *name,
+ long attributes,char *contents);
+long dummy_subscribe (MAILSTREAM *stream,char *mailbox);
+MAILSTREAM *dummy_open (MAILSTREAM *stream);
+void dummy_close (MAILSTREAM *stream,long options);
+long dummy_ping (MAILSTREAM *stream);
+void dummy_check (MAILSTREAM *stream);
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options);
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+/* Dummy routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER dummydriver = {
+ "dummy", /* driver name */
+ DR_LOCAL|DR_MAIL, /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ dummy_valid, /* mailbox is valid for us */
+ dummy_parameters, /* manipulate parameters */
+ dummy_scan, /* scan mailboxes */
+ dummy_list, /* list mailboxes */
+ dummy_lsub, /* list subscribed mailboxes */
+ dummy_subscribe, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ dummy_create, /* create mailbox */
+ dummy_delete, /* delete mailbox */
+ dummy_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ dummy_open, /* open mailbox */
+ dummy_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message structure */
+ NIL, /* fetch header */
+ NIL, /* fetch text */
+ NIL, /* fetch message data */
+ NIL, /* unique identifier */
+ NIL, /* message number from UID */
+ NIL, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ dummy_ping, /* ping mailbox to see if still alive */
+ dummy_check, /* check for new messages */
+ dummy_expunge, /* expunge deleted messages */
+ dummy_copy, /* copy messages to another mailbox */
+ dummy_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM dummyproto = {&dummydriver};
+
+/* Dummy validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *dummy_valid (char *name)
+{
+ char *s,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* must be valid local mailbox */
+ if (name && *name && (*name != '{') && (s = mailboxfile (tmp,name))) {
+ /* indeterminate clearbox INBOX */
+ if (!*s) return &dummydriver;
+ else if (!stat (s,&sbuf)) switch (sbuf.st_mode & S_IFMT) {
+ case S_IFREG:
+ case S_IFDIR:
+ return &dummydriver;
+ }
+ /* blackbox INBOX does not exist yet */
+ else if (!compare_cstring (name,"INBOX")) return &dummydriver;
+ }
+ return NIL;
+}
+
+
+/* Dummy manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *dummy_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = dummy_file ((char *) value,"INBOX");
+ break;
+ }
+ return ret;
+}
+
+/* Dummy scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ DRIVER *drivers;
+ char *s,test[MAILTMPLEN],file[MAILTMPLEN];
+ long i;
+ if (!pat || !*pat) { /* empty pattern? */
+ if (dummy_canonicalize (test,ref,"*")) {
+ /* tie off name at root */
+ if (s = strchr (test,'/')) *++s = '\0';
+ else test[0] = '\0';
+ dummy_listed (stream,'/',test,LATT_NOSELECT,NIL);
+ }
+ }
+ /* get canonical form of name */
+ else if (dummy_canonicalize (test,ref,pat)) {
+ /* found any wildcards? */
+ if (s = strpbrk (test,"%*")) {
+ /* yes, copy name up to that point */
+ strncpy (file,test,i = s - test);
+ file[i] = '\0'; /* tie off */
+ }
+ else strcpy (file,test); /* use just that name then */
+ if (s = strrchr (file,'/')){/* find directory name */
+ *++s = '\0'; /* found, tie off at that point */
+ s = file;
+ }
+ /* silly case */
+ else if ((file[0] == '~') || (file[0] == '#')) s = file;
+ /* do the work */
+ dummy_list_work (stream,s,test,contents,0);
+ /* always an INBOX */
+ if (pmatch ("INBOX",ucase (test))) {
+ /* done if have a dirfmt INBOX */
+ for (drivers = (DRIVER *) mail_parameters (NIL,GET_DRIVERS,NIL);
+ drivers && !(!(drivers->flags & DR_DISABLE) &&
+ (drivers->flags & DR_DIRFMT) &&
+ (*drivers->valid) ("INBOX")); drivers = drivers->next);
+ /* list INBOX appropriately */
+ dummy_listed (stream,drivers ? '/' : NIL,"INBOX",
+ drivers ? NIL : LATT_NOINFERIORS,contents);
+ }
+ }
+}
+
+
+/* Dummy list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ dummy_scan (stream,ref,pat,NIL);
+}
+
+/* Dummy list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ void *sdb = NIL;
+ char *s,*t,test[MAILTMPLEN],tmp[MAILTMPLEN];
+ int showuppers = pat[strlen (pat) - 1] == '%';
+ /* get canonical form of name */
+ if (dummy_canonicalize (test,ref,pat) && (s = sm_read (&sdb))) do
+ if (*s != '{') {
+ if (!compare_cstring (s,"INBOX") &&
+ pmatch ("INBOX",ucase (strcpy (tmp,test))))
+ mm_lsub (stream,NIL,s,LATT_NOINFERIORS);
+ else if (pmatch_full (s,test,'/')) mm_lsub (stream,'/',s,NIL);
+ else while (showuppers && (t = strrchr (s,'/'))) {
+ *t = '\0'; /* tie off the name */
+ if (pmatch_full (s,test,'/')) mm_lsub (stream,'/',s,LATT_NOSELECT);
+ }
+ }
+ while (s = sm_read (&sdb)); /* until no more subscriptions */
+}
+
+
+/* Dummy subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* must be valid local mailbox */
+ if ((s = mailboxfile (tmp,mailbox)) && *s && !stat (s,&sbuf))
+ switch (sbuf.st_mode & S_IFMT) {
+ case S_IFDIR: /* allow but snarl */
+ sprintf (tmp,"CLIENT BUG DETECTED: subscribe of non-mailbox directory %.80s",
+ mailbox);
+ MM_NOTIFY (stream,tmp,WARN);
+ case S_IFREG:
+ return sm_subscribe (mailbox);
+ }
+ sprintf (tmp,"Can't subscribe %.80s: not a mailbox",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+}
+
+/* Dummy list mailboxes worker routine
+ * Accepts: mail stream
+ * directory name to search
+ * search pattern
+ * string to scan
+ * search level
+ */
+
+void dummy_list_work (MAILSTREAM *stream,char *dir,char *pat,char *contents,
+ long level)
+{
+ DRIVER *drivers;
+ dirfmttest_t dt;
+ DIR *dp;
+ struct direct *d;
+ struct stat sbuf;
+ char tmp[MAILTMPLEN],path[MAILTMPLEN];
+ size_t len = 0;
+ /* punt if bogus name */
+ if (!mailboxdir (tmp,dir,NIL)) return;
+ if (dp = opendir (tmp)) { /* do nothing if can't open directory */
+ /* see if a non-namespace directory format */
+ for (drivers = (DRIVER *) mail_parameters (NIL,GET_DRIVERS,NIL), dt = NIL;
+ dir && !dt && drivers; drivers = drivers->next)
+ if (!(drivers->flags & DR_DISABLE) && (drivers->flags & DR_DIRFMT) &&
+ (*drivers->valid) (dir))
+ dt = mail_parameters ((*drivers->open) (NIL),GET_DIRFMTTEST,NIL);
+ /* list it if at top-level */
+ if (!level && dir && pmatch_full (dir,pat,'/') && !pmatch (dir,"INBOX"))
+ dummy_listed (stream,'/',dir,dt ? NIL : LATT_NOSELECT,contents);
+
+ /* scan directory, ignore . and .. */
+ if (!dir || dir[(len = strlen (dir)) - 1] == '/') while (d = readdir (dp))
+ if ((!(dt && (*dt) (d->d_name))) &&
+ ((d->d_name[0] != '.') ||
+ (((long) mail_parameters (NIL,GET_HIDEDOTFILES,NIL)) ? NIL :
+ (d->d_name[1] && (((d->d_name[1] != '.') || d->d_name[2]))))) &&
+ ((len + strlen (d->d_name)) <= NETMAXMBX)) {
+ /* see if name is useful */
+ if (dir) sprintf (tmp,"%s%s",dir,d->d_name);
+ else strcpy (tmp,d->d_name);
+ /* make sure useful and can get info */
+ if ((pmatch_full (strcpy (path,tmp),pat,'/') ||
+ pmatch_full (strcat (path,"/"),pat,'/') ||
+ dmatch (path,pat,'/')) &&
+ mailboxdir (path,dir,"x") && (len = strlen (path)) &&
+ strcpy (path+len-1,d->d_name) && !stat (path,&sbuf)) {
+ /* only interested in file type */
+ switch (sbuf.st_mode & S_IFMT) {
+ case S_IFDIR: /* directory? */
+ /* form with trailing / */
+ sprintf (path,"%s/",tmp);
+ /* skip listing if INBOX */
+ if (!pmatch (tmp,"INBOX")) {
+ if (pmatch_full (tmp,pat,'/')) {
+ if (!dummy_listed (stream,'/',tmp,LATT_NOSELECT,contents))
+ break;
+ }
+ /* try again with trailing / */
+ else if (pmatch_full (path,pat,'/') &&
+ !dummy_listed (stream,'/',path,LATT_NOSELECT,contents))
+ break;
+ }
+ if (dmatch (path,pat,'/') &&
+ (level < (long) mail_parameters (NIL,GET_LISTMAXLEVEL,NIL)))
+ dummy_list_work (stream,path,pat,contents,level+1);
+ break;
+ case S_IFREG: /* ordinary name */
+ /* Must use ctime for systems that don't update mtime properly */
+ if (pmatch_full (tmp,pat,'/') && compare_cstring (tmp,"INBOX"))
+ dummy_listed (stream,'/',tmp,LATT_NOINFERIORS +
+ ((sbuf.st_size && (sbuf.st_atime < sbuf.st_ctime))?
+ LATT_MARKED : LATT_UNMARKED),contents);
+ break;
+ }
+ }
+ }
+ closedir (dp); /* all done, flush directory */
+ }
+}
+
+/* Scan file for contents
+ * Accepts: driver to use
+ * file name
+ * desired contents
+ * length of contents
+ * size of file
+ * Returns: NIL if contents not found, T if found
+ */
+
+long scan_contents (DRIVER *dtb,char *name,char *contents,
+ unsigned long csiz,unsigned long fsiz)
+{
+ scancontents_t sc = dtb ?
+ (scancontents_t) (*dtb->parameters) (GET_SCANCONTENTS,NIL) : NIL;
+ return (*(sc ? sc : dummy_scan_contents)) (name,contents,csiz,fsiz);
+}
+
+
+/* Scan file for contents
+ * Accepts: file name
+ * desired contents
+ * length of contents
+ * size of file
+ * Returns: NIL if contents not found, T if found
+ */
+
+#define BUFSIZE 4*MAILTMPLEN
+
+long dummy_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz)
+{
+ int fd;
+ unsigned long ssiz,bsiz;
+ char *buf;
+ /* forget it if can't select or open */
+ if ((fd = open (name,O_RDONLY,NIL)) >= 0) {
+ /* get buffer including slop */
+ buf = (char *) fs_get (BUFSIZE + (ssiz = 4 * ((csiz / 4) + 1)) + 1);
+ memset (buf,'\0',ssiz); /* no slop area the first time */
+ while (fsiz) { /* until end of file */
+ read (fd,buf+ssiz,bsiz = min (fsiz,BUFSIZE));
+ if (search ((unsigned char *) buf,bsiz+ssiz,
+ (unsigned char *) contents,csiz)) break;
+ memcpy (buf,buf+BUFSIZE,ssiz);
+ fsiz -= bsiz; /* note that we read that much */
+ }
+ fs_give ((void **) &buf); /* flush buffer */
+ close (fd); /* finished with file */
+ if (fsiz) return T; /* found */
+ }
+ return NIL; /* not found */
+}
+
+/* Mailbox found
+ * Accepts: MAIL stream
+ * hierarchy delimiter
+ * mailbox name
+ * attributes
+ * contents to search before calling mm_list()
+ * Returns: NIL if should abort hierarchy search, else T (currently always)
+ */
+
+long dummy_listed (MAILSTREAM *stream,char delimiter,char *name,
+ long attributes,char *contents)
+{
+ DRIVER *d;
+ DIR *dp;
+ struct direct *dr;
+ dirfmttest_t dt;
+ unsigned long csiz;
+ struct stat sbuf;
+ int nochild;
+ char *s,tmp[MAILTMPLEN];
+ if (!(attributes & LATT_NOINFERIORS) && mailboxdir (tmp,name,NIL) &&
+ (dp = opendir (tmp))) { /* if not \NoInferiors */
+ /* locate dirfmttest if any */
+ for (d = (DRIVER *) mail_parameters (NIL,GET_DRIVERS,NIL), dt = NIL;
+ !dt && d; d = d->next)
+ if (!(d->flags & DR_DISABLE) && (d->flags & DR_DIRFMT) &&
+ (*d->valid) (name))
+ dt = mail_parameters ((*d->open) (NIL),GET_DIRFMTTEST,NIL);
+ /* scan directory for children */
+ for (nochild = T; nochild && (dr = readdir (dp)); )
+ if ((!(dt && (*dt) (dr->d_name))) &&
+ ((dr->d_name[0] != '.') ||
+ (((long) mail_parameters (NIL,GET_HIDEDOTFILES,NIL)) ? NIL :
+ (dr->d_name[1] && ((dr->d_name[1] != '.') || dr->d_name[2])))))
+ nochild = NIL;
+ attributes |= nochild ? LATT_HASNOCHILDREN : LATT_HASCHILDREN;
+ closedir (dp); /* all done, flush directory */
+ }
+ d = NIL; /* don't \NoSelect dir if it has a driver */
+ if ((attributes & LATT_NOSELECT) && (d = mail_valid (NIL,name,NIL)) &&
+ (d != &dummydriver)) attributes &= ~LATT_NOSELECT;
+ if (!contents || /* notify main program */
+ (!(attributes & LATT_NOSELECT) && (csiz = strlen (contents)) &&
+ (s = mailboxfile (tmp,name)) &&
+ (*s || (s = mail_parameters (NIL,GET_INBOXPATH,tmp))) &&
+ !stat (s,&sbuf) && (d || (csiz <= sbuf.st_size)) &&
+ SAFE_SCAN_CONTENTS (d,tmp,contents,csiz,sbuf.st_size)))
+ mm_list (stream,delimiter,name,attributes);
+ return T;
+}
+
+/* Dummy create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,tmp[MAILTMPLEN];
+ long ret = NIL;
+ /* validate name */
+ if (!(compare_cstring (mailbox,"INBOX") && (s = dummy_file (tmp,mailbox)))) {
+ sprintf (tmp,"Can't create %.80s: invalid name",mailbox);
+ MM_LOG (tmp,ERROR);
+ }
+ /* create the name, done if made directory */
+ else if ((ret = dummy_create_path (stream,tmp,get_dir_protection(mailbox)))&&
+ (s = strrchr (s,'/')) && !s[1]) return T;
+ return ret ? set_mbx_protections (mailbox,tmp) : NIL;
+}
+
+/* Dummy create path
+ * Accepts: mail stream
+ * path name to create
+ * directory mode
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode)
+{
+ struct stat sbuf;
+ char c,*s,tmp[MAILTMPLEN];
+ int fd;
+ long ret = NIL;
+ char *t = strrchr (path,'/');
+ int wantdir = t && !t[1];
+ int mask = umask (0);
+ if (wantdir) *t = '\0'; /* flush trailing delimiter for directory */
+ if (s = strrchr (path,'/')) { /* found superior to this name? */
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (path,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,path,dirmode)) {
+ umask (mask); /* restore mask */
+ return NIL;
+ }
+ *s = c; /* restore full name */
+ }
+ if (wantdir) { /* want to create directory? */
+ ret = !mkdir (path,(int) dirmode);
+ *t = '/'; /* restore directory delimiter */
+ }
+ /* create file */
+ else if ((fd = open (path,O_WRONLY|O_CREAT|O_EXCL,
+ (long) mail_parameters(NIL,GET_MBXPROTECTION,NIL))) >=0)
+ ret = !close (fd);
+ if (!ret) { /* error? */
+ sprintf (tmp,"Can't create mailbox node %.80s: %.80s",path,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ }
+ umask (mask); /* restore mask */
+ return ret; /* return status */
+}
+
+/* Dummy delete mailbox
+ * Accepts: mail stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_delete (MAILSTREAM *stream,char *mailbox)
+{
+ struct stat sbuf;
+ char *s,tmp[MAILTMPLEN];
+ if (!(s = dummy_file (tmp,mailbox))) {
+ sprintf (tmp,"Can't delete - invalid name: %.80s",s);
+ MM_LOG (tmp,ERROR);
+ }
+ /* no trailing / (workaround BSD kernel bug) */
+ if ((s = strrchr (tmp,'/')) && !s[1]) *s = '\0';
+ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) == S_IFDIR) ?
+ rmdir (tmp) : unlink (tmp)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %.80s",mailbox,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ return T; /* return success */
+}
+
+/* Mail rename mailbox
+ * Accepts: mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ struct stat sbuf;
+ char c,*s,tmp[MAILTMPLEN],mbx[MAILTMPLEN],oldname[MAILTMPLEN];
+ /* no trailing / allowed */
+ if (!dummy_file (oldname,old) || !(s = dummy_file (mbx,newname)) ||
+ stat (oldname,&sbuf) || ((s = strrchr (s,'/')) && !s[1] &&
+ ((sbuf.st_mode & S_IFMT) != S_IFDIR))) {
+ sprintf (mbx,"Can't rename %.80s to %.80s: invalid name",old,newname);
+ MM_LOG (mbx,ERROR);
+ return NIL;
+ }
+ if (s) { /* found a directory delimiter? */
+ if (!s[1]) *s = '\0'; /* ignore trailing delimiter */
+ else { /* found superior to destination name? */
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (mbx,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create (stream,mbx)) return NIL;
+ *s = c; /* restore full name */
+ }
+ }
+ /* rename of non-ex INBOX creates dest */
+ if (!compare_cstring (old,"INBOX") && stat (oldname,&sbuf))
+ return dummy_create (NIL,mbx);
+ if (rename (oldname,mbx)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %.80s",old,newname,
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ return T; /* return success */
+}
+
+/* Dummy open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *dummy_open (MAILSTREAM *stream)
+{
+ int fd;
+ char err[MAILTMPLEN],tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* OP_PROTOTYPE call */
+ if (!stream) return &dummyproto;
+ err[0] = '\0'; /* no error message yet */
+ /* can we open the file? */
+ if (!dummy_file (tmp,stream->mailbox))
+ sprintf (err,"Can't open this name: %.80s",stream->mailbox);
+ else if ((fd = open (tmp,O_RDONLY,NIL)) < 0) {
+ /* no, error unless INBOX */
+ if (compare_cstring (stream->mailbox,"INBOX"))
+ sprintf (err,"%.80s: %.80s",strerror (errno),stream->mailbox);
+ }
+ else { /* file had better be empty then */
+ fstat (fd,&sbuf); /* sniff at its size */
+ close (fd);
+ if ((sbuf.st_mode & S_IFMT) != S_IFREG)
+ sprintf (err,"Can't open %.80s: not a selectable mailbox",
+ stream->mailbox);
+ else if (sbuf.st_size) /* bogus format if non-empty */
+ sprintf (err,"Can't open %.80s (file %.80s): not in valid mailbox format",
+ stream->mailbox,tmp);
+ }
+ if (err[0]) { /* if an error happened */
+ MM_LOG (err,stream->silent ? WARN : ERROR);
+ return NIL;
+ }
+ else if (!stream->silent) { /* only if silence not requested */
+ mail_exists (stream,0); /* say there are 0 messages */
+ mail_recent (stream,0); /* and certainly no recent ones! */
+ stream->uid_validity = time (0);
+ }
+ stream->inbox = T; /* note that it's an INBOX */
+ return stream; /* return success */
+}
+
+
+/* Dummy close
+ * Accepts: MAIL stream
+ * options
+ */
+
+void dummy_close (MAILSTREAM *stream,long options)
+{
+ /* return silently */
+}
+
+/* Dummy ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long dummy_ping (MAILSTREAM *stream)
+{
+ MAILSTREAM *test;
+ if (time (0) >= /* time to do another test? */
+ ((time_t) (stream->gensym +
+ (long) mail_parameters (NIL,GET_SNARFINTERVAL,NIL)))) {
+ /* has mailbox format changed? */
+ if ((test = mail_open (NIL,stream->mailbox,OP_PROTOTYPE)) &&
+ (test->dtb != stream->dtb) &&
+ (test = mail_open (NIL,stream->mailbox,NIL))) {
+ /* preserve some resources */
+ test->original_mailbox = stream->original_mailbox;
+ stream->original_mailbox = NIL;
+ test->sparep = stream->sparep;
+ stream->sparep = NIL;
+ test->sequence = stream->sequence;
+ mail_close ((MAILSTREAM *) /* flush resources used by dummy stream */
+ memcpy (fs_get (sizeof (MAILSTREAM)),stream,
+ sizeof (MAILSTREAM)));
+ /* swap the streams */
+ memcpy (stream,test,sizeof (MAILSTREAM));
+ fs_give ((void **) &test);/* flush test now that copied */
+ /* make sure application knows */
+ mail_exists (stream,stream->recent = stream->nmsgs);
+ }
+ /* still hasn't changed */
+ else stream->gensym = time (0);
+ }
+ return T;
+}
+
+
+/* Dummy check mailbox
+ * Accepts: MAIL stream
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+void dummy_check (MAILSTREAM *stream)
+{
+ dummy_ping (stream); /* invoke ping */
+}
+
+
+/* Dummy expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ return LONGT;
+}
+
+/* Dummy copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * options
+ * Returns: T if copy successful, else NIL
+ */
+
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) fatal ("Impossible dummy_copy");
+ return NIL;
+}
+
+
+/* Dummy append message string
+ * Accepts: mail stream
+ * destination mailbox
+ * append callback function
+ * data for callback
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd = -1;
+ int e;
+ char tmp[MAILTMPLEN];
+ MAILSTREAM *ts = default_proto (T);
+ /* append to INBOX? */
+ if (!compare_cstring (mailbox,"INBOX")) {
+ /* yes, if no empty proto try creating */
+ if (!ts && !(*(ts = default_proto (NIL))->dtb->create) (ts,"INBOX"))
+ ts = NIL;
+ }
+ else if (dummy_file (tmp,mailbox) && ((fd = open (tmp,O_RDONLY,NIL)) < 0)) {
+ if ((e = errno) == ENOENT) /* failed, was it no such file? */
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ sprintf (tmp,"%.80s: %.80s",strerror (e),mailbox);
+ MM_LOG (tmp,ERROR); /* pass up error */
+ return NIL; /* always fails */
+ }
+ else if (fd >= 0) { /* found file? */
+ fstat (fd,&sbuf); /* get its size */
+ close (fd); /* toss out the fd */
+ if (sbuf.st_size) ts = NIL; /* non-empty file? */
+ }
+ if (ts) return (*ts->dtb->append) (stream,mailbox,af,data);
+ sprintf (tmp,"Indeterminate mailbox format: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+}
+
+/* Dummy mail generate file string
+ * Accepts: temporary buffer to write into
+ * mailbox name string
+ * Returns: local file string or NIL if failure
+ */
+
+char *dummy_file (char *dst,char *name)
+{
+ char *s = mailboxfile (dst,name);
+ /* return our standard inbox */
+ return (s && !*s) ? strcpy (dst,sysinbox ()) : s;
+}
+
+
+/* Dummy canonicalize name
+ * Accepts: buffer to write name
+ * reference
+ * pattern
+ * Returns: T if success, NIL if failure
+ */
+
+long dummy_canonicalize (char *tmp,char *ref,char *pat)
+{
+ unsigned long i;
+ char *s;
+ if (ref) { /* preliminary reference check */
+ if (*ref == '{') return NIL;/* remote reference not allowed */
+ else if (!*ref) ref = NIL; /* treat empty reference as no reference */
+ }
+ switch (*pat) {
+ case '#': /* namespace name */
+ if (mailboxfile (tmp,pat)) strcpy (tmp,pat);
+ else return NIL; /* unknown namespace */
+ break;
+ case '{': /* remote names not allowed */
+ return NIL;
+ case '/': /* rooted name */
+ case '~': /* home directory name */
+ if (!ref || (*ref != '#')) {/* non-namespace reference? */
+ strcpy (tmp,pat); /* yes, ignore */
+ break;
+ }
+ /* fall through */
+ default: /* apply reference for all other names */
+ if (!ref) strcpy (tmp,pat); /* just copy if no namespace */
+ else if ((*ref != '#') || mailboxfile (tmp,ref)) {
+ /* wants root of name? */
+ if (*pat == '/') strcpy (strchr (strcpy (tmp,ref),'/'),pat);
+ /* otherwise just append */
+ else sprintf (tmp,"%s%s",ref,pat);
+ }
+ else return NIL; /* unknown namespace */
+ }
+ /* count wildcards */
+ for (i = 0, s = tmp; *s; *s++) if ((*s == '*') || (*s == '%')) ++i;
+ if (i > MAXWILDCARDS) { /* ridiculous wildcarding? */
+ MM_LOG ("Excessive wildcards in LIST/LSUB",ERROR);
+ return NIL;
+ }
+ return T;
+}
diff --git a/imap/src/osdep/amiga/dummy.h b/imap/src/osdep/amiga/dummy.h
new file mode 100644
index 00000000..32650e06
--- /dev/null
+++ b/imap/src/osdep/amiga/dummy.h
@@ -0,0 +1,43 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 9 May 1991
+ * Last Edited: 30 August 2006
+ */
+
+/* Exported function prototypes */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat);
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long scan_contents (DRIVER *dtb,char *name,char *contents,
+ unsigned long csiz,unsigned long fsiz);
+long dummy_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz);
+long dummy_create (MAILSTREAM *stream,char *mailbox);
+long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode);
+long dummy_delete (MAILSTREAM *stream,char *mailbox);
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname);
+char *dummy_file (char *dst,char *name);
+long dummy_canonicalize (char *tmp,char *ref,char *pat);
diff --git a/imap/src/osdep/amiga/env_ami.c b/imap/src/osdep/amiga/env_ami.c
new file mode 100644
index 00000000..843757ed
--- /dev/null
+++ b/imap/src/osdep/amiga/env_ami.c
@@ -0,0 +1,1282 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Amiga environment routines
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * Seattle, WA 98195
+ * Internet: MRC@Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 15 May 2008
+ */
+
+#include <grp.h>
+#include <signal.h>
+#include <sys/wait.h>
+
+/* c-client environment parameters */
+
+static char *myUserName = NIL; /* user name */
+static char *myHomeDir = NIL; /* home directory name */
+static char *myMailboxDir = NIL;/* mailbox directory name */
+static char *myLocalHost = NIL; /* local host name */
+static char *myNewsrc = NIL; /* newsrc file name */
+static char *mailsubdir = NIL; /* mail subdirectory name */
+static char *sysInbox = NIL; /* system inbox name */
+static char *newsActive = NIL; /* news active file */
+static char *newsSpool = NIL; /* news spool */
+ /* anonymous home directory */
+static char *anonymousHome = NIL;
+static char *ftpHome = NIL; /* ftp export home directory */
+static char *publicHome = NIL; /* public home directory */
+static char *sharedHome = NIL; /* shared home directory */
+static short anonymous = NIL; /* is anonymous */
+static short restrictBox = NIL; /* is a restricted box */
+static short has_no_life = NIL; /* is a cretin with no life */
+ /* block environment init */
+static short block_env_init = NIL;
+static short hideDotFiles = NIL;/* hide files whose names start with . */
+ /* advertise filesystem root */
+static short advertisetheworld = NIL;
+ /* disable automatic shared namespaces */
+static short noautomaticsharedns = NIL;
+static short no822tztext = NIL; /* disable RFC [2]822 timezone text */
+static short netfsstatbug = NIL;/* compensate for broken stat() on network
+ * filesystems (AFS and old NFS). Don't do
+ * this unless you really have to!
+ */
+ /* 1 = disable plaintext, 2 = if not SSL */
+static long disablePlaintext = NIL;
+static long list_max_level = 20;/* maximum level of list recursion */
+ /* default file protection */
+static long mbx_protection = 0600;
+ /* default directory protection */
+static long dir_protection = 0700;
+ /* default lock file protection */
+static long lock_protection = MANDATORYLOCKPROT;
+ /* default ftp file protection */
+static long ftp_protection = 0644;
+static long ftp_dir_protection = 0755;
+ /* default public file protection */
+static long public_protection = 0666;
+static long public_dir_protection = 0777;
+ /* default shared file protection */
+static long shared_protection = 0660;
+static long shared_dir_protection = 0770;
+static long locktimeout = 5; /* default lock timeout */
+ /* default prototypes */
+static MAILSTREAM *createProto = NIL;
+static MAILSTREAM *appendProto = NIL;
+ /* default user flags */
+static char *userFlags[NUSERFLAGS] = {NIL};
+static NAMESPACE *nslist[3]; /* namespace list */
+static int logtry = 3; /* number of server login tries */
+ /* block notification */
+static blocknotify_t mailblocknotify = mm_blocknotify;
+
+/* Amiga namespaces */
+
+ /* personal mh namespace */
+static NAMESPACE nsmhf = {"#mh/",'/',NIL,NIL};
+static NAMESPACE nsmh = {"#mhinbox",NIL,NIL,&nsmhf};
+ /* home namespace */
+static NAMESPACE nshome = {"",'/',NIL,&nsmh};
+ /* Amiga other user namespace */
+static NAMESPACE nsamigaother = {"~",'/',NIL,NIL};
+ /* public (anonymous OK) namespace */
+static NAMESPACE nspublic = {"#public/",'/',NIL,NIL};
+ /* netnews namespace */
+static NAMESPACE nsnews = {"#news.",'.',NIL,&nspublic};
+ /* FTP export namespace */
+static NAMESPACE nsftp = {"#ftp/",'/',NIL,&nsnews};
+ /* shared (no anonymous) namespace */
+static NAMESPACE nsshared = {"#shared/",'/',NIL,&nsftp};
+ /* world namespace */
+static NAMESPACE nsworld = {"/",'/',NIL,&nsshared};
+
+#include "write.c" /* include safe writing routines */
+#include "pmatch.c" /* include wildcard pattern matcher */
+
+/* Get all authenticators */
+
+#include "auths.c"
+
+/* Environment manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *env_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_NAMESPACE:
+ ret = (void *) nslist;
+ break;
+ case SET_USERNAME:
+ if (myUserName) fs_give ((void **) &myUserName);
+ myUserName = cpystr ((char *) value);
+ case GET_USERNAME:
+ ret = (void *) myUserName;
+ break;
+ case SET_HOMEDIR:
+ if (myHomeDir) fs_give ((void **) &myHomeDir);
+ myHomeDir = cpystr ((char *) value);
+ case GET_HOMEDIR:
+ ret = (void *) myHomeDir;
+ break;
+ case SET_LOCALHOST:
+ if (myLocalHost) fs_give ((void **) &myLocalHost);
+ myLocalHost = cpystr ((char *) value);
+ case GET_LOCALHOST:
+ ret = (void *) myLocalHost;
+ break;
+ case SET_NEWSRC:
+ if (myNewsrc) fs_give ((void **) &myNewsrc);
+ myNewsrc = cpystr ((char *) value);
+ case GET_NEWSRC:
+ ret = (void *) myNewsrc;
+ break;
+ case SET_NEWSACTIVE:
+ if (newsActive) fs_give ((void **) &newsActive);
+ newsActive = cpystr ((char *) value);
+ case GET_NEWSACTIVE:
+ ret = (void *) newsActive;
+ break;
+ case SET_NEWSSPOOL:
+ if (newsSpool) fs_give ((void **) &newsSpool);
+ newsSpool = cpystr ((char *) value);
+ case GET_NEWSSPOOL:
+ ret = (void *) newsSpool;
+ break;
+
+ case SET_ANONYMOUSHOME:
+ if (anonymousHome) fs_give ((void **) &anonymousHome);
+ anonymousHome = cpystr ((char *) value);
+ case GET_ANONYMOUSHOME:
+ if (!anonymousHome) anonymousHome = cpystr (ANONYMOUSHOME);
+ ret = (void *) anonymousHome;
+ break;
+ case SET_FTPHOME:
+ if (ftpHome) fs_give ((void **) &ftpHome);
+ ftpHome = cpystr ((char *) value);
+ case GET_FTPHOME:
+ ret = (void *) ftpHome;
+ break;
+ case SET_PUBLICHOME:
+ if (publicHome) fs_give ((void **) &publicHome);
+ publicHome = cpystr ((char *) value);
+ case GET_PUBLICHOME:
+ ret = (void *) publicHome;
+ break;
+ case SET_SHAREDHOME:
+ if (sharedHome) fs_give ((void **) &sharedHome);
+ sharedHome = cpystr ((char *) value);
+ case GET_SHAREDHOME:
+ ret = (void *) sharedHome;
+ break;
+ case SET_SYSINBOX:
+ if (sysInbox) fs_give ((void **) &sysInbox);
+ sysInbox = cpystr ((char *) value);
+ case GET_SYSINBOX:
+ ret = (void *) sysInbox;
+ break;
+ case SET_LISTMAXLEVEL:
+ list_max_level = (long) value;
+ case GET_LISTMAXLEVEL:
+ ret = (void *) list_max_level;
+ break;
+
+ case SET_MBXPROTECTION:
+ mbx_protection = (long) value;
+ case GET_MBXPROTECTION:
+ ret = (void *) mbx_protection;
+ break;
+ case SET_DIRPROTECTION:
+ dir_protection = (long) value;
+ case GET_DIRPROTECTION:
+ ret = (void *) dir_protection;
+ break;
+ case SET_LOCKPROTECTION:
+ lock_protection = (long) value;
+ case GET_LOCKPROTECTION:
+ ret = (void *) lock_protection;
+ break;
+ case SET_FTPPROTECTION:
+ ftp_protection = (long) value;
+ case GET_FTPPROTECTION:
+ ret = (void *) ftp_protection;
+ break;
+ case SET_PUBLICPROTECTION:
+ public_protection = (long) value;
+ case GET_PUBLICPROTECTION:
+ ret = (void *) public_protection;
+ break;
+ case SET_SHAREDPROTECTION:
+ shared_protection = (long) value;
+ case GET_SHAREDPROTECTION:
+ ret = (void *) shared_protection;
+ break;
+ case SET_FTPDIRPROTECTION:
+ ftp_dir_protection = (long) value;
+ case GET_FTPDIRPROTECTION:
+ ret = (void *) ftp_dir_protection;
+ break;
+ case SET_PUBLICDIRPROTECTION:
+ public_dir_protection = (long) value;
+ case GET_PUBLICDIRPROTECTION:
+ ret = (void *) public_dir_protection;
+ break;
+ case SET_SHAREDDIRPROTECTION:
+ shared_dir_protection = (long) value;
+ case GET_SHAREDDIRPROTECTION:
+ ret = (void *) shared_dir_protection;
+ break;
+
+ case SET_LOCKTIMEOUT:
+ locktimeout = (long) value;
+ case GET_LOCKTIMEOUT:
+ ret = (void *) locktimeout;
+ break;
+ case SET_HIDEDOTFILES:
+ hideDotFiles = value ? T : NIL;
+ case GET_HIDEDOTFILES:
+ ret = (void *) (hideDotFiles ? VOIDT : NIL);
+ break;
+ case SET_DISABLEPLAINTEXT:
+ disablePlaintext = (long) value;
+ case GET_DISABLEPLAINTEXT:
+ ret = (void *) disablePlaintext;
+ break;
+ case SET_ADVERTISETHEWORLD:
+ advertisetheworld = value ? T : NIL;
+ case GET_ADVERTISETHEWORLD:
+ ret = (void *) (advertisetheworld ? VOIDT : NIL);
+ break;
+ case SET_DISABLEAUTOSHAREDNS:
+ noautomaticsharedns = value ? T : NIL;
+ case GET_DISABLEAUTOSHAREDNS:
+ ret = (void *) (noautomaticsharedns ? VOIDT : NIL);
+ break;
+ case SET_DISABLE822TZTEXT:
+ no822tztext = value ? T : NIL;
+ case GET_DISABLE822TZTEXT:
+ ret = (void *) (no822tztext ? VOIDT : NIL);
+ break;
+ case SET_USERHASNOLIFE:
+ has_no_life = value ? T : NIL;
+ case GET_USERHASNOLIFE:
+ ret = (void *) (has_no_life ? VOIDT : NIL);
+ break;
+ case SET_NETFSSTATBUG:
+ netfsstatbug = value ? T : NIL;
+ case GET_NETFSSTATBUG:
+ ret = (void *) (netfsstatbug ? VOIDT : NIL);
+ break;
+ case SET_BLOCKENVINIT:
+ block_env_init = value ? T : NIL;
+ case GET_BLOCKENVINIT:
+ ret = (void *) (block_env_init ? VOIDT : NIL);
+ break;
+ case SET_BLOCKNOTIFY:
+ mailblocknotify = (blocknotify_t) value;
+ case GET_BLOCKNOTIFY:
+ ret = (void *) mailblocknotify;
+ break;
+ }
+ return ret;
+}
+
+/* Write current time
+ * Accepts: destination string
+ * optional format of day-of-week prefix
+ * format of date and time
+ * flag whether to append symbolic timezone
+ */
+
+static void do_date (char *date,char *prefix,char *fmt,int suffix)
+{
+ time_t tn = time (0);
+ struct tm *t = gmtime (&tn);
+ int zone = t->tm_hour * 60 + t->tm_min;
+ int julian = t->tm_yday;
+ t = localtime (&tn); /* get local time now */
+ /* minus UTC minutes since midnight */
+ zone = t->tm_hour * 60 + t->tm_min - zone;
+ /* julian can be one of:
+ * 36x local time is December 31, UTC is January 1, offset -24 hours
+ * 1 local time is 1 day ahead of UTC, offset +24 hours
+ * 0 local time is same day as UTC, no offset
+ * -1 local time is 1 day behind UTC, offset -24 hours
+ * -36x local time is January 1, UTC is December 31, offset +24 hours
+ */
+ if (julian = t->tm_yday -julian)
+ zone += ((julian < 0) == (abs (julian) == 1)) ? -24*60 : 24*60;
+ if (prefix) { /* want day of week? */
+ sprintf (date,prefix,days[t->tm_wday]);
+ date += strlen (date); /* make next sprintf append */
+ }
+ /* output the date */
+ sprintf (date,fmt,t->tm_mday,months[t->tm_mon],t->tm_year+1900,
+ t->tm_hour,t->tm_min,t->tm_sec,zone/60,abs (zone) % 60);
+ /* append timezone suffix if desired */
+ if (suffix) rfc822_timezone (date,(void *) t);
+}
+
+/* Write current time in RFC 822 format
+ * Accepts: destination string
+ */
+
+void rfc822_date (char *date)
+{
+ do_date (date,"%s, ","%d %s %d %02d:%02d:%02d %+03d%02d",
+ no822tztext ? NIL : T);
+}
+
+
+/* Write current time in fixed-width RFC 822 format
+ * Accepts: destination string
+ */
+
+void rfc822_fixed_date (char *date)
+{
+ do_date (date,NIL,"%02d %s %4d %02d:%02d:%02d %+03d%02d",NIL);
+}
+
+
+/* Write current time in internal format
+ * Accepts: destination string
+ */
+
+void internal_date (char *date)
+{
+ do_date (date,NIL,"%02d-%s-%d %02d:%02d:%02d %+03d%02d",NIL);
+}
+
+/* Initialize server
+ * Accepts: server name for syslog or NIL
+ * /etc/services service name or NIL
+ * alternate /etc/services service name or NIL
+ * clock interrupt handler
+ * kiss-of-death interrupt handler
+ * hangup interrupt handler
+ * termination interrupt handler
+ */
+
+void server_init (char *server,char *service,char *sslservice,
+ void *clkint,void *kodint,void *hupint,void *trmint,
+ void *staint)
+{
+ int onceonly = server && service && sslservice;
+ if (onceonly) { /* set server name in syslog */
+ int mask;
+ openlog (myServerName = cpystr (server),LOG_PID,syslog_facility);
+ fclose (stderr); /* possibly save a process ID */
+
+ switch (mask = umask (022)){/* check old umask */
+ case 0: /* definitely unreasonable */
+ case 022: /* don't need to change it */
+ break;
+ default: /* already was a reasonable value */
+ umask (mask); /* so change it back */
+ }
+ }
+ arm_signal (SIGALRM,clkint); /* prepare for clock interrupt */
+ arm_signal (SIGUSR2,kodint); /* prepare for Kiss Of Death */
+ arm_signal (SIGHUP,hupint); /* prepare for hangup */
+ arm_signal (SIGPIPE,hupint); /* alternative hangup */
+ arm_signal (SIGTERM,trmint); /* prepare for termination */
+ /* status dump */
+ if (staint) arm_signal (SIGUSR1,staint);
+ if (onceonly) { /* set up network and maybe SSL */
+ long port;
+ struct servent *sv;
+ /* Use SSL if SSL service, or if server starts with "s" and not service */
+ if (((port = tcp_serverport ()) >= 0)) {
+ if ((sv = getservbyname (service,"tcp")) && (port == ntohs (sv->s_port)))
+ syslog (LOG_DEBUG,"%s service init from %s",service,tcp_clientaddr ());
+ else if ((sv = getservbyname (sslservice,"tcp")) &&
+ (port == ntohs (sv->s_port))) {
+ syslog (LOG_DEBUG,"%s SSL service init from %s",sslservice,
+ tcp_clientaddr ());
+ ssl_server_init (server);
+ }
+ else { /* not service or SSL service port */
+ syslog (LOG_DEBUG,"port %ld service init from %s",port,
+ tcp_clientaddr ());
+ if (*server == 's') ssl_server_init (server);
+ }
+ }
+ }
+}
+
+/* Wait for stdin input
+ * Accepts: timeout in seconds
+ * Returns: T if have input on stdin, else NIL
+ */
+
+long server_input_wait (long seconds)
+{
+ fd_set rfd,efd;
+ struct timeval tmo;
+ FD_ZERO (&rfd);
+ FD_ZERO (&efd);
+ FD_SET (0,&rfd);
+ FD_SET (0,&efd);
+ tmo.tv_sec = seconds; tmo.tv_usec = 0;
+ return select (1,&rfd,0,&efd,&tmo) ? LONGT : NIL;
+}
+
+/* Return Amiga password entry for user name
+ * Accepts: user name string
+ * Returns: password entry
+ *
+ * Tries all-lowercase form of user name if given user name fails
+ */
+
+static struct passwd *pwuser (unsigned char *user)
+{
+ unsigned char *s;
+ struct passwd *pw = getpwnam (user);
+ if (!pw) { /* failed, see if any uppercase characters */
+ for (s = user; *s && ((*s < 'A') || (*s > 'Z')); s++);
+ if (*s) { /* yes, try all lowercase form */
+ pw = getpwnam (s = lcase (cpystr (user)));
+ fs_give ((void **) &s);
+ }
+ }
+ return pw;
+}
+
+
+/* Validate password for user name
+ * Accepts: user name string
+ * password string
+ * argument count
+ * argument vector
+ * Returns: password entry if validated
+ *
+ * Tries password+1 if password fails and starts with space
+ */
+
+static struct passwd *valpwd (char *user,char *pwd,int argc,char *argv[])
+{
+ char *s;
+ struct passwd *pw;
+ struct passwd *ret = NIL;
+ if (auth_md5.server) { /* using CRAM-MD5 authentication? */
+ if (s = auth_md5_pwd (user)) {
+ if (!strcmp (s,pwd) || ((*pwd == ' ') && pwd[1] && !strcmp (s,pwd+1)))
+ ret = pwuser (user); /* validated, get passwd entry for user */
+ memset (s,0,strlen (s)); /* erase sensitive information */
+ fs_give ((void **) &s);
+ }
+ }
+ else if (pw = pwuser (user)) {/* can get user? */
+ s = cpystr (pw->pw_name); /* copy returned name in case we need it */
+ if (*pwd && !(ret = checkpw (pw,pwd,argc,argv)) &&
+ (*pwd == ' ') && pwd[1] && (ret = pwuser (s)))
+ ret = checkpw (pw,pwd+1,argc,argv);
+ fs_give ((void **) &s); /* don't need copy of name any more */
+ }
+ return ret;
+}
+
+/* Server log in
+ * Accepts: user name string
+ * password string
+ * authenticating user name string
+ * argument count
+ * argument vector
+ * Returns: T if password validated, NIL otherwise
+ */
+
+long server_login (char *user,char *pwd,char *authuser,int argc,char *argv[])
+{
+ struct passwd *pw = NIL;
+ int level = LOG_NOTICE;
+ char *err = "failed";
+ /* cretins still haven't given up */
+ if ((strlen (user) >= NETMAXUSER) ||
+ (authuser && (strlen (authuser) >= NETMAXUSER))) {
+ level = LOG_ALERT; /* escalate this alert */
+ err = "SYSTEM BREAK-IN ATTEMPT";
+ logtry = 0; /* render this session useless */
+ }
+ else if (logtry-- <= 0) err = "excessive login failures";
+ else if (disablePlaintext) err = "disabled";
+ else if (!(authuser && *authuser)) pw = valpwd (user,pwd,argc,argv);
+ else if (valpwd (authuser,pwd,argc,argv)) pw = pwuser (user);
+ if (pw && pw_login (pw,authuser,pw->pw_name,NIL,argc,argv)) return T;
+ syslog (level|LOG_AUTH,"Login %s user=%.64s auth=%.64s host=%.80s",err,
+ user,(authuser && *authuser) ? authuser : user,tcp_clienthost ());
+ sleep (3); /* slow down possible cracker */
+ return NIL;
+}
+
+/* Authenticated server log in
+ * Accepts: user name string
+ * authenticating user name string
+ * argument count
+ * argument vector
+ * Returns: T if password validated, NIL otherwise
+ */
+
+long authserver_login (char *user,char *authuser,int argc,char *argv[])
+{
+ return pw_login (pwuser (user),authuser,user,NIL,argc,argv);
+}
+
+
+/* Log in as anonymous daemon
+ * Accepts: argument count
+ * argument vector
+ * Returns: T if successful, NIL if error
+ */
+
+long anonymous_login (int argc,char *argv[])
+{
+ /* log in Mr. A. N. Onymous */
+ return pw_login (getpwnam (ANONYMOUSUSER),NIL,NIL,
+ (char *) mail_parameters (NIL,GET_ANONYMOUSHOME,NIL),
+ argc,argv);
+}
+
+/* Finish log in and environment initialization
+ * Accepts: passwd struct for loginpw()
+ * optional authentication user name
+ * user name (NIL for anonymous)
+ * home directory (NIL to use directory from passwd struct)
+ * argument count
+ * argument vector
+ * Returns: T if successful, NIL if error
+ */
+
+long pw_login (struct passwd *pw,char *auser,char *user,char *home,int argc,
+ char *argv[])
+{
+ struct group *gr;
+ char **t;
+ long ret = NIL;
+ if (pw && pw->pw_uid) { /* must have passwd struct for non-UID 0 */
+ /* make safe copies of user and home */
+ if (user) user = cpystr (pw->pw_name);
+ home = cpystr (home ? home : pw->pw_dir);
+ /* authorization ID .NE. authentication ID? */
+ if (user && auser && *auser && compare_cstring (auser,user)) {
+ /* scan list of mail administrators */
+ if ((gr = getgrnam (ADMINGROUP)) && (t = gr->gr_mem)) while (*t && !ret)
+ if (!compare_cstring (auser,*t++))
+ ret = pw_login (pw,NIL,user,home,argc,argv);
+ syslog (LOG_NOTICE|LOG_AUTH,"%s %.80s override of user=%.80s host=%.80s",
+ ret ? "Admin" : "Failed",auser,user,tcp_clienthost ());
+ }
+ /* normal login */
+ else if (((pw->pw_uid == geteuid ()) || loginpw (pw,argc,argv)) &&
+ (ret = env_init (user,home))) chdir (myhomedir ());
+ fs_give ((void **) &home); /* clean up */
+ if (user) fs_give ((void **) &user);
+ }
+ return ret; /* return status */
+}
+
+/* Initialize environment
+ * Accepts: user name (NIL for anonymous)
+ * home directory name
+ * Returns: T, always
+ */
+
+long env_init (char *user,char *home)
+{
+ extern MAILSTREAM CREATEPROTO;
+ extern MAILSTREAM EMPTYPROTO;
+ struct passwd *pw;
+ struct stat sbuf;
+ char tmp[MAILTMPLEN];
+ /* don't init if blocked */
+ if (block_env_init) return LONGT;
+ if (myUserName) fatal ("env_init called twice!");
+ /* set up user name */
+ myUserName = cpystr (user ? user : ANONYMOUSUSER);
+ if (user) { /* remember user name and home directory */
+ nslist[0] = &nshome; /* home namespace */
+ nslist[1] = &nsamigaother;
+ nslist[2] = advertisetheworld ? &nsworld : &nsshared;
+ }
+ else { /* anonymous user */
+ nslist[0] = nslist[1] = NIL,nslist[2] = &nsftp;
+ sprintf (tmp,"%s/INBOX",
+ home = (char *) mail_parameters (NIL,GET_ANONYMOUSHOME,NIL));
+ sysInbox = cpystr (tmp); /* make system INBOX */
+ anonymous = T; /* flag as anonymous */
+ }
+ myHomeDir = cpystr (home); /* set home directory */
+ if (!noautomaticsharedns) {
+ /* #ftp namespace */
+ if (!ftpHome && (pw = getpwnam ("ftp"))) ftpHome = cpystr (pw->pw_dir);
+ /* #public namespace */
+ if (!publicHome && (pw = getpwnam ("imappublic")))
+ publicHome = cpystr (pw->pw_dir);
+ /* #shared namespace */
+ if (!anonymous && !sharedHome && (pw = getpwnam ("imapshared")))
+ sharedHome = cpystr (pw->pw_dir);
+ }
+ if (!myLocalHost) mylocalhost ();
+ if (!myNewsrc) myNewsrc = cpystr(strcat (strcpy (tmp,myHomeDir),"/.newsrc"));
+ if (!newsActive) newsActive = cpystr (ACTIVEFILE);
+ if (!newsSpool) newsSpool = cpystr (NEWSSPOOL);
+ /* force default prototype to be set */
+ if (!createProto) createProto = &CREATEPROTO;
+ if (!appendProto) appendProto = &EMPTYPROTO;
+ /* re-do open action to get flags */
+ (*createProto->dtb->open) (NIL);
+ endpwent (); /* close pw database */
+ return T;
+}
+
+/* Return my user name
+ * Accepts: pointer to optional flags
+ * Returns: my user name
+ */
+
+char *myusername_full (unsigned long *flags)
+{
+ struct passwd *pw;
+ struct stat sbuf;
+ char *s;
+ unsigned long euid;
+ char *ret = UNLOGGEDUSER;
+ /* no user name yet and not root? */
+ if (!myUserName && (euid = geteuid ())) {
+ /* yes, look up getlogin() user name or EUID */
+ if (((s = (char *) getlogin ()) && *s && (strlen (s) < NETMAXUSER) &&
+ (pw = getpwnam (s)) && (pw->pw_uid == euid)) ||
+ (pw = getpwuid (euid))) {
+ if (block_env_init) { /* don't env_init if blocked */
+ if (flags) *flags = MU_LOGGEDIN;
+ return pw->pw_name;
+ }
+ env_init (pw->pw_name,
+ ((s = getenv ("HOME")) && *s && (strlen (s) < NETMAXMBX) &&
+ !stat (s,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) ?
+ s : pw->pw_dir);
+ }
+ else fatal ("Unable to look up user name");
+ }
+ if (myUserName) { /* logged in? */
+ if (flags) *flags = anonymous ? MU_ANONYMOUS : MU_LOGGEDIN;
+ ret = myUserName; /* return user name */
+ }
+ else if (flags) *flags = MU_NOTLOGGEDIN;
+ return ret;
+}
+/* Return my local host name
+ * Returns: my local host name
+ */
+
+char *mylocalhost ()
+{
+ char tmp[MAILTMPLEN];
+ struct hostent *host_name;
+ if (!myLocalHost) myLocalHost = cpystr (gethostname (tmp,MAILTMPLEN-1) ?
+ "random-pc" : tcp_canonical (tmp));
+ return myLocalHost;
+}
+
+/* Return my home directory name
+ * Returns: my home directory name
+ */
+
+char *myhomedir ()
+{
+ if (!myHomeDir) myusername ();/* initialize if first time */
+ return myHomeDir ? myHomeDir : "";
+}
+
+
+/* Return my home mailbox name
+ * Returns: my home directory name
+ */
+
+static char *mymailboxdir ()
+{
+ char *home = myhomedir ();
+ if (!myMailboxDir && home) { /* initialize if first time */
+ if (mailsubdir) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"%s/%s",home,mailsubdir);
+ myMailboxDir = cpystr (tmp);/* use pre-defined subdirectory of home */
+ }
+ else myMailboxDir = cpystr (home);
+ }
+ return myMailboxDir ? myMailboxDir : "";
+}
+
+
+/* Return system standard INBOX
+ * Accepts: buffer string
+ */
+
+char *sysinbox ()
+{
+ char tmp[MAILTMPLEN];
+ if (!sysInbox) { /* initialize if first time */
+ sprintf (tmp,"%s/%s",MAILSPOOL,myusername ());
+ sysInbox = cpystr (tmp); /* system inbox is from mail spool */
+ }
+ return sysInbox;
+}
+
+/* Return mailbox directory name
+ * Accepts: destination buffer
+ * directory prefix
+ * name in directory
+ * Returns: file name or NIL if error
+ */
+
+char *mailboxdir (char *dst,char *dir,char *name)
+{
+ char tmp[MAILTMPLEN];
+ if (dir || name) { /* if either argument provided */
+ if (dir) {
+ if (strlen (dir) > NETMAXMBX) return NIL;
+ strcpy (tmp,dir); /* write directory prefix */
+ }
+ else tmp[0] = '\0'; /* otherwise null string */
+ if (name) {
+ if (strlen (name) > NETMAXMBX) return NIL;
+ strcat (tmp,name); /* write name in directory */
+ }
+ /* validate name, return its name */
+ if (!mailboxfile (dst,tmp)) return NIL;
+ }
+ /* no arguments, wants mailbox directory */
+ else strcpy (dst,mymailboxdir ());
+ return dst; /* return the name */
+}
+
+/* Return mailbox file name
+ * Accepts: destination buffer
+ * mailbox name
+ * Returns: file name or empty string for driver-selected INBOX or NIL if error
+ */
+
+char *mailboxfile (char *dst,char *name)
+{
+ struct passwd *pw;
+ char *s;
+ if (!name || !*name || (*name == '{') || (strlen (name) > NETMAXMBX) ||
+ ((anonymous || restrictBox || (*name == '#')) &&
+ (strstr (name,"..") || strstr (name,"//") || strstr (name,"/~"))))
+ dst = NIL; /* invalid name */
+ else switch (*name) { /* determine mailbox type based upon name */
+ case '#': /* namespace name */
+ /* #ftp/ namespace */
+ if (((name[1] == 'f') || (name[1] == 'F')) &&
+ ((name[2] == 't') || (name[2] == 'T')) &&
+ ((name[3] == 'p') || (name[3] == 'P')) &&
+ (name[4] == '/') && ftpHome) sprintf (dst,"%s/%s",ftpHome,name+5);
+ /* #public/ and #shared/ namespaces */
+ else if ((((name[1] == 'p') || (name[1] == 'P')) &&
+ ((name[2] == 'u') || (name[2] == 'U')) &&
+ ((name[3] == 'b') || (name[3] == 'B')) &&
+ ((name[4] == 'l') || (name[4] == 'L')) &&
+ ((name[5] == 'i') || (name[5] == 'I')) &&
+ ((name[6] == 'c') || (name[6] == 'C')) &&
+ (name[7] == '/') && (s = publicHome)) ||
+ (!anonymous && ((name[1] == 's') || (name[1] == 'S')) &&
+ ((name[2] == 'h') || (name[2] == 'H')) &&
+ ((name[3] == 'a') || (name[3] == 'A')) &&
+ ((name[4] == 'r') || (name[4] == 'R')) &&
+ ((name[5] == 'e') || (name[5] == 'E')) &&
+ ((name[6] == 'd') || (name[6] == 'D')) &&
+ (name[7] == '/') && (s = sharedHome)))
+ sprintf (dst,"%s/%s",s,compare_cstring (name,"INBOX") ? name : "INBOX");
+ else dst = NIL; /* unknown namespace */
+ break;
+
+ case '/': /* root access */
+ if (anonymous) dst = NIL; /* anonymous forbidden to do this */
+ else if ((restrictBox & RESTRICTROOT) && strcmp (name,sysinbox ()))
+ dst = NIL; /* restricted and not access to sysinbox */
+ else strcpy (dst,name); /* unrestricted, copy root name */
+ break;
+ case '~': /* other user access */
+ /* bad syntax or anonymous can't win */
+ if (!*++name || anonymous) dst = NIL;
+ /* ~/ equivalent to ordinary name */
+ else if (*name == '/') sprintf (dst,"%s/%s",mymailboxdir (),name+1);
+ /* other user forbidden if restricted */
+ else if (restrictBox & RESTRICTOTHERUSER) dst = NIL;
+ else { /* clear box other user */
+ /* copy user name */
+ for (s = dst; *name && (*name != '/'); *s++ = *name++);
+ *s++ = '\0'; /* tie off user name, look up in passwd file */
+ if ((pw = getpwnam (dst)) && pw->pw_dir) {
+ if (*name) name++; /* skip past the slash */
+ /* canonicalize case of INBOX */
+ if (!compare_cstring (name,"INBOX")) name = "INBOX";
+ /* remove trailing / from directory */
+ if ((s = strrchr (pw->pw_dir,'/')) && !s[1]) *s = '\0';
+ /* don't allow ~root/ if restricted root */
+ if ((restrictBox & RESTRICTROOT) && !*pw->pw_dir) dst = NIL;
+ /* build final name w/ subdir if needed */
+ else if (mailsubdir) sprintf (dst,"%s/%s/%s",pw->pw_dir,mailsubdir,name);
+ else sprintf (dst,"%s/%s",pw->pw_dir,name);
+ }
+ else dst = NIL; /* no such user */
+ }
+ break;
+ case 'I': case 'i': /* possible INBOX */
+ if (!compare_cstring (name+1,"NBOX")) {
+ /* if anonymous, use INBOX in mailbox dir */
+ if (anonymous) sprintf (dst,"%s/INBOX",mymailboxdir ());
+ else *dst = '\0'; /* otherwise driver selects the name */
+ break;
+ }
+ /* drop into to ordinary name case */
+ default: /* ordinary name is easy */
+ sprintf (dst,"%s/%s",mymailboxdir (),name);
+ break;
+ }
+ return dst; /* return final name */
+}
+
+/* Dot-lock file locker
+ * Accepts: file name to lock
+ * destination buffer for lock file name
+ * open file description on file name to lock
+ * Returns: T if success, NIL if failure
+ */
+
+long dotlock_lock (char *file,DOTLOCK *base,int fd)
+{
+ int i = locktimeout * 60;
+ int j,mask,retry,pi[2],po[2];
+ char *s,tmp[MAILTMPLEN];
+ struct stat sb;
+ /* flush absurd file name */
+ if (strlen (file) > 512) return NIL;
+ /* build lock filename */
+ sprintf (base->lock,"%s.lock",file);
+ /* assume no pipe */
+ base->pipei = base->pipeo = -1;
+ do { /* make sure not symlink */
+ if (!(j = chk_notsymlink (base->lock,&sb))) return NIL;
+ /* time out if file older than 5 minutes */
+ if ((j > 0) && ((time (0)) >= (sb.st_ctime + locktimeout * 60))) i = 0;
+ /* try to create the lock */
+ if ((j = open (name,O_WRONLY|O_CREAT|O_EXCL,(int) lock_protection)) >= 0) {
+ close (i); /* make the file, now close it */
+ chmod (base->lock,(int) lock_protection);
+ return LONGT;
+ }
+ if (errno == EEXIST) { /* already locked? */
+ retry = -1; /* can try again */
+ if (!(i%15)) { /* time to notify? */
+ sprintf (tmp,"Mailbox %.80s is locked, will override in %d seconds...",
+ file,i);
+ mm_log (tmp,WARN);
+ }
+ sleep (1); /* wait 1 second before next try */
+ }
+ else retry = i = 0; /* hard failure, no more retries */
+ } while (i--); /* until out of retries */
+ if (retry < 0) { /* still returning retry after locktimeout? */
+ if (!(j = chk_notsymlink (base->lock,&sb))) return NIL;
+ if ((j > 0) && ((time (0)) < (sb.st_ctime + locktimeout * 60))) {
+ sprintf (tmp,"Mailbox vulnerable - seizing %ld second old lock",
+ (long) (time (0) - sb.st_ctime));
+ mm_log (tmp,WARN);
+ }
+ mask = umask (0);
+ unlink (base->lock); /* try to remove the old file */
+ /* seize the lock */
+ if ((i = open (base->lock,O_WRONLY|O_CREAT,(int) lock_protection)) >= 0) {
+ close (i); /* don't need descriptor any more */
+ sprintf (tmp,"Mailbox %.80s lock overridden",file);
+ mm_log (tmp,NIL);
+ chmod (base->lock,(int) lock_protection);
+ umask (mask) /* restore old umask */
+ return LONGT;
+ }
+ umask (mask) /* restore old umask */
+ }
+
+ if (fd >= 0) switch (errno) {
+ case EACCES: /* protection failure? */
+ /* make command pipes */
+ if (!stat (LOCKPGM,&sb) && (pipe (pi) >= 0)) {
+ /* if input pipes usable create output pipes */
+ if ((pi[0] < FD_SETSIZE) && (pi[1] < FD_SETSIZE) && (pipe (po) >= 0)) {
+ /* make sure output pipes are usable */
+ if ((po[0] >= FD_SETSIZE) || (po[1] >= FD_SETSIZE));
+ /* all is good, make inferior process */
+ else if (!(j = fork ())) {
+ if (!fork ()) { /* make grandchild so it's inherited by init */
+ long cf; /* don't change caller vars in case vfork() */
+ char *argv[4],arg[20];
+ /* prepare argument vector */
+ sprintf (arg,"%d",fd);
+ argv[0] = LOCKPGM; argv[1] = arg;
+ argv[2] = file; argv[3] = NIL;
+ /* set parent's I/O to my O/I */
+ dup2 (pi[1],1); dup2 (pi[1],2); dup2 (po[0],0);
+ /* close all unnecessary descriptors */
+ for (cf = max (20,max (max (pi[0],pi[1]),max(po[0],po[1])));
+ cf >= 3; --cf) if (cf != fd) close (cf);
+ /* be our own process group */
+ setpgrp (0,getpid ());
+ /* now run it */
+ _exit (execv (argv[0],argv));
+ }
+ _exit (1); /* child is done */
+ }
+ else if (j > 0) { /* parent process */
+ fd_set rfd;
+ struct timeval tmo;
+ FD_ZERO (&rfd);
+ FD_SET (pi[0],&rfd);
+ tmo.tv_sec = locktimeout * 60;
+ grim_pid_reap (j,NIL);/* reap child; grandchild now owned by init */
+ /* read response from locking program */
+ if (select (pi[0]+1,&rfd,0,0,&tmo) &&
+ (read (pi[0],tmp,1) == 1) && (tmp[0] == '+')) {
+ /* success, record pipes */
+ base->pipei = pi[0]; base->pipeo = po[1];
+ /* close child's side of the pipes */
+ close (pi[1]); close (po[0]);
+ return LONGT;
+ }
+ }
+ close (po[0]); close (po[1]);
+ }
+ close (pi[0]); close (pi[1]);
+ }
+ /* find directory/file delimiter */
+ if (s = strrchr (base->lock,'/')) {
+ *s = '\0'; /* tie off at directory */
+ sprintf(tmp, /* generate default message */
+ "Mailbox vulnerable - directory %.80s must have 1777 protection",
+ base->lock);
+ /* definitely not 1777 if can't stat */
+ mask = stat (base->lock,&sb) ? 0 : (sb.st_mode & 1777);
+ *s = '/'; /* restore lock name */
+ if (mask != 1777) { /* default warning if not 1777 */
+ MM_LOG (tmp,WARN);
+ break;
+ }
+ }
+ default:
+ sprintf (tmp,"Mailbox vulnerable - error creating %.80s: %s",
+ base->lock,strerror (errno));
+ mm_log (tmp,WARN); /* this is probably not good */
+ break;
+ }
+ base->lock[0] = '\0'; /* don't use lock files */
+ return NIL;
+}
+
+/* Dot-lock file unlocker
+ * Accepts: lock file name
+ * Returns: T if success, NIL if failure
+ */
+
+long dotlock_unlock (DOTLOCK *base)
+{
+ long ret = LONGT;
+ if (base && base->lock[0]) {
+ if (base->pipei >= 0) { /* if running through a pipe unlocker */
+ ret = (write (base->pipeo,"+",1) == 1);
+ /* nuke the pipes */
+ close (base->pipei); close (base->pipeo);
+ }
+ else ret = !unlink (base->lock);
+ }
+ return ret;
+}
+
+/* Lock file name
+ * Accepts: scratch buffer
+ * file name
+ * type of locking operation (LOCK_SH or LOCK_EX)
+ * pointer to return PID of locker
+ * Returns: file descriptor of lock or negative if error
+ */
+
+int lockname (char *lock,char *fname,int op,long *pid)
+{
+ struct stat sbuf;
+ *pid = 0; /* no locker PID */
+ return stat (fname,&sbuf) ? -1 : lock_work (lock,&sbuf,op,pid);
+}
+
+
+/* Lock file descriptor
+ * Accepts: file descriptor
+ * lock file name buffer
+ * type of locking operation (LOCK_SH or LOCK_EX)
+ * Returns: file descriptor of lock or negative if error
+ */
+
+int lockfd (int fd,char *lock,int op)
+{
+ struct stat sbuf;
+ return fstat (fd,&sbuf) ? -1 : lock_work (lock,&sbuf,op,NIL);
+}
+
+/* Lock file name worker
+ * Accepts: lock file name
+ * pointer to stat() buffer
+ * type of locking operation (LOCK_SH or LOCK_EX)
+ * pointer to return PID of locker
+ * Returns: file descriptor of lock or negative if error
+ */
+
+int lock_work (char *lock,void *sb,int op,long *pid)
+{
+ struct stat lsb,fsb;
+ struct stat *sbuf = (struct stat *) sb;
+ char tmp[MAILTMPLEN];
+ long i;
+ int fd;
+ int mask = umask (0);
+ if (pid) *pid = 0; /* initialize return PID */
+ /* make temporary lock file name */
+ sprintf (lock,"%s/.%lx.%lx","/tmp",
+ (unsigned long) sbuf->st_dev,(unsigned long) sbuf->st_ino);
+ while (T) { /* until get a good lock */
+ do switch ((int) chk_notsymlink (lock,&lsb)) {
+ case 1: /* exists just once */
+ if (((fd = open (lock,O_RDWR,lock_protection)) >= 0) ||
+ (errno != ENOENT) || (chk_notsymlink (lock,&lsb) >= 0)) break;
+ case -1: /* name doesn't exist */
+ fd = open (lock,O_RDWR|O_CREAT|O_EXCL,lock_protection);
+ break;
+ default: /* multiple hard links */
+ mm_log ("hard link to lock name",ERROR);
+ syslog (LOG_CRIT,"SECURITY PROBLEM: hard link to lock name: %.80s",lock);
+ case 0: /* symlink (already did syslog) */
+ umask (mask); /* restore old mask */
+ return -1; /* fail: no lock file */
+ } while ((fd < 0) && (errno == EEXIST));
+ if (fd < 0) { /* failed to get file descriptor */
+ syslog (LOG_INFO,"Mailbox lock file %s open failure: %s",lock,
+ strerror (errno));
+ if (stat ("/tmp",&lsb))
+ syslog (LOG_CRIT,"SYSTEM ERROR: no /tmp: %s",strerror (errno));
+ else if ((lsb.st_mode & 01777) != 01777)
+ mm_log ("Can't lock for write: /tmp must have 1777 protection",WARN);
+ umask (mask); /* restore old mask */
+ return -1; /* fail: can't open lock file */
+ }
+
+ /* non-blocking form */
+ if (op & LOCK_NB) i = flock (fd,op);
+ else { /* blocking form */
+ (*mailblocknotify) (BLOCK_FILELOCK,NIL);
+ i = flock (fd,op);
+ (*mailblocknotify) (BLOCK_NONE,NIL);
+ }
+ if (i) { /* failed, get other process' PID */
+ if (pid && !fstat (fd,&fsb) && (i = min (fsb.st_size,MAILTMPLEN-1)) &&
+ (read (fd,tmp,i) == i) && !(tmp[i] = 0) && ((i = atol (tmp)) > 0))
+ *pid = i;
+ close (fd); /* failed, give up on lock */
+ umask (mask); /* restore old mask */
+ return -1; /* fail: can't lock */
+ }
+ /* make sure this lock is good for us */
+ if (!lstat (lock,&lsb) && ((lsb.st_mode & S_IFMT) != S_IFLNK) &&
+ !fstat (fd,&fsb) && (lsb.st_dev == fsb.st_dev) &&
+ (lsb.st_ino == fsb.st_ino) && (fsb.st_nlink == 1)) break;
+ close (fd); /* lock not right, drop fd and try again */
+ }
+ /* make sure mode OK (don't use fchmod()) */
+ chmod (lock,(int) lock_protection);
+ umask (mask); /* restore old mask */
+ return fd; /* success */
+}
+
+/* Check to make sure not a symlink
+ * Accepts: file name
+ * stat buffer
+ * Returns: -1 if doesn't exist, NIL if symlink, else number of hard links
+ */
+
+long chk_notsymlink (char *name,void *sb)
+{
+ struct stat *sbuf = (struct stat *) sb;
+ /* name exists? */
+ if (lstat (name,sbuf)) return -1;
+ /* forbid symbolic link */
+ if ((sbuf->st_mode & S_IFMT) == S_IFLNK) {
+ mm_log ("symbolic link on lock name",ERROR);
+ syslog (LOG_CRIT,"SECURITY PROBLEM: symbolic link on lock name: %.80s",
+ name);
+ return NIL;
+ }
+ return (long) sbuf->st_nlink; /* return number of hard links */
+}
+
+
+/* Unlock file descriptor
+ * Accepts: file descriptor
+ * lock file name from lockfd()
+ */
+
+void unlockfd (int fd,char *lock)
+{
+ /* delete the file if no sharers */
+ if (!flock (fd,LOCK_EX|LOCK_NB)) unlink (lock);
+ flock (fd,LOCK_UN); /* unlock it */
+ close (fd); /* close it */
+}
+
+/* Set proper file protection for mailbox
+ * Accepts: mailbox name
+ * actual file path name
+ * Returns: T, always
+ */
+
+long set_mbx_protections (char *mailbox,char *path)
+{
+ struct stat sbuf;
+ int mode = (int) mbx_protection;
+ if (*mailbox == '#') { /* possible namespace? */
+ if (((mailbox[1] == 'f') || (mailbox[1] == 'F')) &&
+ ((mailbox[2] == 't') || (mailbox[2] == 'T')) &&
+ ((mailbox[3] == 'p') || (mailbox[3] == 'P')) &&
+ (mailbox[4] == '/')) mode = (int) ftp_protection;
+ else if (((mailbox[1] == 'p') || (mailbox[1] == 'P')) &&
+ ((mailbox[2] == 'u') || (mailbox[2] == 'U')) &&
+ ((mailbox[3] == 'b') || (mailbox[3] == 'B')) &&
+ ((mailbox[4] == 'l') || (mailbox[4] == 'L')) &&
+ ((mailbox[5] == 'i') || (mailbox[5] == 'I')) &&
+ ((mailbox[6] == 'c') || (mailbox[6] == 'C')) &&
+ (mailbox[7] == '/')) mode = (int) public_protection;
+ else if (((mailbox[1] == 's') || (mailbox[1] == 'S')) &&
+ ((mailbox[2] == 'h') || (mailbox[2] == 'H')) &&
+ ((mailbox[3] == 'a') || (mailbox[3] == 'A')) &&
+ ((mailbox[4] == 'r') || (mailbox[4] == 'R')) &&
+ ((mailbox[5] == 'e') || (mailbox[5] == 'E')) &&
+ ((mailbox[6] == 'd') || (mailbox[6] == 'D')) &&
+ (mailbox[7] == '/')) mode = (int) shared_protection;
+ }
+ /* if a directory */
+ if (!stat (path,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) {
+ /* set owner search if allow read or write */
+ if (mode & 0600) mode |= 0100;
+ if (mode & 060) mode |= 010;/* set group search if allow read or write */
+ if (mode & 06) mode |= 01; /* set world search if allow read or write */
+ /* preserve directory SGID bit */
+ if (sbuf.st_mode & S_ISGID) mode |= S_ISGID;
+ }
+ chmod (path,mode); /* set the new protection, ignore failure */
+ return LONGT;
+}
+
+/* Get proper directory protection
+ * Accepts: mailbox name
+ * Returns: directory mode, always
+ */
+
+long get_dir_protection (char *mailbox)
+{
+ if (*mailbox == '#') { /* possible namespace? */
+ if (((mailbox[1] == 'f') || (mailbox[1] == 'F')) &&
+ ((mailbox[2] == 't') || (mailbox[2] == 'T')) &&
+ ((mailbox[3] == 'p') || (mailbox[3] == 'P')) &&
+ (mailbox[4] == '/')) return ftp_dir_protection;
+ else if (((mailbox[1] == 'p') || (mailbox[1] == 'P')) &&
+ ((mailbox[2] == 'u') || (mailbox[2] == 'U')) &&
+ ((mailbox[3] == 'b') || (mailbox[3] == 'B')) &&
+ ((mailbox[4] == 'l') || (mailbox[4] == 'L')) &&
+ ((mailbox[5] == 'i') || (mailbox[5] == 'I')) &&
+ ((mailbox[6] == 'c') || (mailbox[6] == 'C')) &&
+ (mailbox[7] == '/')) return public_dir_protection;
+ else if (((mailbox[1] == 's') || (mailbox[1] == 'S')) &&
+ ((mailbox[2] == 'h') || (mailbox[2] == 'H')) &&
+ ((mailbox[3] == 'a') || (mailbox[3] == 'A')) &&
+ ((mailbox[4] == 'r') || (mailbox[4] == 'R')) &&
+ ((mailbox[5] == 'e') || (mailbox[5] == 'E')) &&
+ ((mailbox[6] == 'd') || (mailbox[6] == 'D')) &&
+ (mailbox[7] == '/')) return shared_dir_protection;
+ }
+ return dir_protection;
+}
+
+/* Determine default prototype stream to user
+ * Accepts: type (NIL for create, T for append)
+ * Returns: default prototype stream
+ */
+
+MAILSTREAM *default_proto (long type)
+{
+ myusername (); /* make sure initialized */
+ /* return default driver's prototype */
+ return type ? appendProto : createProto;
+}
+
+
+/* Set up user flags for stream
+ * Accepts: MAIL stream
+ * Returns: MAIL stream with user flags set up
+ */
+
+MAILSTREAM *user_flags (MAILSTREAM *stream)
+{
+ int i;
+ myusername (); /* make sure initialized */
+ for (i = 0; i < NUSERFLAGS && userFlags[i]; ++i)
+ if (!stream->user_flags[i]) stream->user_flags[i] = cpystr (userFlags[i]);
+ return stream;
+}
+
+
+/* Return nth user flag
+ * Accepts: user flag number
+ * Returns: flag
+ */
+
+char *default_user_flag (unsigned long i)
+{
+ myusername (); /* make sure initialized */
+ return userFlags[i];
+}
+
+/* Default block notify routine
+ * Accepts: reason for calling
+ * data
+ * Returns: data
+ */
+
+void *mm_blocknotify (int reason,void *data)
+{
+ void *ret = data;
+ switch (reason) {
+ case BLOCK_SENSITIVE: /* entering sensitive code */
+ ret = (void *) alarm (0);
+ break;
+ case BLOCK_NONSENSITIVE: /* exiting sensitive code */
+ if ((unsigned int) data) alarm ((unsigned int) data);
+ break;
+ default: /* ignore all other reasons */
+ break;
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/amiga/env_ami.h b/imap/src/osdep/amiga/env_ami.h
new file mode 100644
index 00000000..365e5128
--- /dev/null
+++ b/imap/src/osdep/amiga/env_ami.h
@@ -0,0 +1,95 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX environment routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+typedef struct dotlock_base {
+ char lock[MAILTMPLEN];
+ int pipei;
+ int pipeo;
+} DOTLOCK;
+
+
+/* Bits that can be set in restrictBox */
+
+#define RESTRICTROOT 0x1 /* restricted box doesn't allow root */
+#define RESTRICTOTHERUSER 0x2 /* restricted box doesn't allow other user */
+
+/* Subscription definitions for UNIX */
+
+#define SUBSCRIPTIONFILE(t) sprintf (t,"%s/.mailboxlist",myhomedir ())
+#define SUBSCRIPTIONTEMP(t) sprintf (t,"%s/.mlbxlsttmp",myhomedir ())
+
+
+/* dorc() options */
+
+#define SYSCONFIG "/etc/c-client.cf"
+
+
+/* Special users */
+
+#define ANONYMOUSUSER "nobody" /* anonymous user */
+#define UNLOGGEDUSER "root" /* unlogged-in user */
+#define ADMINGROUP "mailadm" /* mail administrator group */
+
+/* Function prototypes */
+
+#include "env.h"
+
+void rfc822_fixed_date (char *date);
+long env_init (char *user,char *home);
+char *myusername_full (unsigned long *flags);
+#define MU_LOGGEDIN 0
+#define MU_NOTLOGGEDIN 1
+#define MU_ANONYMOUS 2
+#define myusername() \
+ myusername_full (NIL)
+char *sysinbox ();
+char *mailboxdir (char *dst,char *dir,char *name);
+long dotlock_lock (char *file,DOTLOCK *base,int fd);
+long dotlock_unlock (DOTLOCK *base);
+int lockname (char *lock,char *fname,int op,long *pid);
+int lockfd (int fd,char *lock,int op);
+int lock_work (char *lock,void *sbuf,int op,long *pid);
+long chk_notsymlink (char *name,void *sbuf);
+void unlockfd (int fd,char *lock);
+long set_mbx_protections (char *mailbox,char *path);
+long get_dir_protection (char *mailbox);
+MAILSTREAM *user_flags (MAILSTREAM *stream);
+char *default_user_flag (unsigned long i);
+void dorc (char *file,long flag);
+long path_create (MAILSTREAM *stream,char *mailbox);
+void grim_pid_reap_status (int pid,int killreq,void *status);
+#define grim_pid_reap(pid,killreq) \
+ grim_pid_reap_status (pid,killreq,NIL)
+long safe_write (int fd,char *buf,long nbytes);
+void *arm_signal (int sig,void *action);
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[]);
+long loginpw (struct passwd *pw,int argc,char *argv[]);
+long pw_login (struct passwd *pw,char *auser,char *user,char *home,int argc,
+ char *argv[]);
+void *mm_blocknotify (int reason,void *data);
diff --git a/imap/src/osdep/amiga/fdstring.c b/imap/src/osdep/amiga/fdstring.c
new file mode 100644
index 00000000..7a491f7d
--- /dev/null
+++ b/imap/src/osdep/amiga/fdstring.c
@@ -0,0 +1,99 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: File descriptor string routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 April 1997
+ * Last Edited: 4 April 2007
+ */
+
+#include "mail.h"
+#include "osdep.h"
+#include "misc.h"
+#include "fdstring.h"
+
+/* String driver for fd stringstructs */
+
+static void fd_string_init (STRING *s,void *data,unsigned long size);
+static char fd_string_next (STRING *s);
+static void fd_string_setpos (STRING *s,unsigned long i);
+
+STRINGDRIVER fd_string = {
+ fd_string_init, /* initialize string structure */
+ fd_string_next, /* get next byte in string structure */
+ fd_string_setpos /* set position in string structure */
+};
+
+
+/* Initialize string structure for fd stringstruct
+ * Accepts: string structure
+ * pointer to string
+ * size of string
+ */
+
+static void fd_string_init (STRING *s,void *data,unsigned long size)
+{
+ FDDATA *d = (FDDATA *) data;
+ /* note fd */
+ s->data = (void *) (unsigned long) d->fd;
+ s->data1 = d->pos; /* note file offset */
+ s->size = size; /* note size */
+ s->curpos = s->chunk = d->chunk;
+ s->chunksize = (unsigned long) d->chunksize;
+ s->offset = 0; /* initial position */
+ /* and size of data */
+ s->cursize = min (s->chunksize,size);
+ /* move to that position in the file */
+ lseek (d->fd,d->pos,L_SET);
+ read (d->fd,s->chunk,(size_t) s->cursize);
+}
+
+/* Get next character from fd stringstruct
+ * Accepts: string structure
+ * Returns: character, string structure chunk refreshed
+ */
+
+static char fd_string_next (STRING *s)
+{
+ char c = *s->curpos++; /* get next byte */
+ SETPOS (s,GETPOS (s)); /* move to next chunk */
+ return c; /* return the byte */
+}
+
+
+/* Set string pointer position for fd stringstruct
+ * Accepts: string structure
+ * new position
+ */
+
+static void fd_string_setpos (STRING *s,unsigned long i)
+{
+ if (i > s->size) i = s->size; /* don't permit setting beyond EOF */
+ s->offset = i; /* set new offset */
+ s->curpos = s->chunk; /* reset position */
+ /* set size of data */
+ if (s->cursize = min (s->chunksize,SIZE (s))) {
+ /* move to that position in the file */
+ lseek ((long) s->data,s->data1 + s->offset,L_SET);
+ read ((long) s->data,s->curpos,(size_t) s->cursize);
+ }
+}
diff --git a/imap/src/osdep/amiga/fdstring.h b/imap/src/osdep/amiga/fdstring.h
new file mode 100644
index 00000000..d0a021bf
--- /dev/null
+++ b/imap/src/osdep/amiga/fdstring.h
@@ -0,0 +1,39 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: File descriptor string routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 April 1997
+ * Last Edited: 30 August 2006
+ */
+
+/* Driver-dependent data passed to init method */
+
+typedef struct fd_data {
+ int fd; /* file descriptor */
+ unsigned long pos; /* initial position */
+ char *chunk; /* I/O buffer chunk */
+ unsigned long chunksize; /* I/O buffer chunk length */
+} FDDATA;
+
+
+extern STRINGDRIVER fd_string;
diff --git a/imap/src/osdep/amiga/fs_ami.c b/imap/src/osdep/amiga/fs_ami.c
new file mode 100644
index 00000000..b433e1b4
--- /dev/null
+++ b/imap/src/osdep/amiga/fs_ami.c
@@ -0,0 +1,71 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Free storage management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Get a block of free storage
+ * Accepts: size of desired block
+ * Returns: free storage block
+ */
+
+void *fs_get (size_t size)
+{
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ void *data = (*bn) (BLOCK_SENSITIVE,NIL);
+ void *block = malloc (size ? size : (size_t) 1);
+ if (!block) fatal ("Out of memory");
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ return (block);
+}
+
+
+/* Resize a block of free storage
+ * Accepts: ** pointer to current block
+ * new size
+ */
+
+void fs_resize (void **block,size_t size)
+{
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ void *data = (*bn) (BLOCK_SENSITIVE,NIL);
+ if (!(*block = realloc (*block,size ? size : (size_t) 1)))
+ fatal ("Can't resize memory");
+ (*bn) (BLOCK_NONSENSITIVE,data);
+}
+
+
+/* Return a block of free storage
+ * Accepts: ** pointer to free storage block
+ */
+
+void fs_give (void **block)
+{
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ void *data = (*bn) (BLOCK_SENSITIVE,NIL);
+ free (*block);
+ *block = NIL;
+ (*bn) (BLOCK_NONSENSITIVE,data);
+}
diff --git a/imap/src/osdep/amiga/ftl_ami.c b/imap/src/osdep/amiga/ftl_ami.c
new file mode 100644
index 00000000..9e65ef55
--- /dev/null
+++ b/imap/src/osdep/amiga/ftl_ami.c
@@ -0,0 +1,38 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: DOS/VMS/TOPS-20 crash management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Report a fatal error
+ * Accepts: string to output
+ */
+
+void fatal (char *string)
+{
+ mm_fatal (string); /* pass up the string */
+ abort (); /* die horribly */
+}
diff --git a/imap/src/osdep/amiga/gethstid.c b/imap/src/osdep/amiga/gethstid.c
new file mode 100644
index 00000000..01516976
--- /dev/null
+++ b/imap/src/osdep/amiga/gethstid.c
@@ -0,0 +1,38 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Get host ID emulator
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 May 1995
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Emulator for BSD gethostid() call
+ * Returns: unique identifier for this machine
+ */
+
+long gethostid (void)
+{
+ /* No gethostid() here, so just fake it and hope things turn out okay. */
+ return 0xdeadface;
+}
diff --git a/imap/src/osdep/amiga/gr_waitp.c b/imap/src/osdep/amiga/gr_waitp.c
new file mode 100644
index 00000000..8b230f9d
--- /dev/null
+++ b/imap/src/osdep/amiga/gr_waitp.c
@@ -0,0 +1,39 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX Grim PID Reaper -- waitpid() version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 30 November 1993
+ * Last Edited: 30 August 2006
+ */
+
+/* Grim PID reaper
+ * Accepts: process ID
+ * kill request flag
+ * status return value
+ */
+
+void grim_pid_reap_status (int pid,int killreq,void *status)
+{
+ if (killreq) kill(pid,SIGHUP);/* kill if not already dead */
+ while ((waitpid (pid,status,NIL) < 0) && (errno != ECHILD));
+}
diff --git a/imap/src/osdep/amiga/log_std.c b/imap/src/osdep/amiga/log_std.c
new file mode 100644
index 00000000..a36fa88f
--- /dev/null
+++ b/imap/src/osdep/amiga/log_std.c
@@ -0,0 +1,44 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Standard login
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Log in
+ * Accepts: login passwd struct
+ * argument count
+ * argument vector
+ * Returns: T if success, NIL otherwise
+ */
+
+long loginpw (struct passwd *pw,int argc,char *argv[])
+{
+ uid_t uid = pw->pw_uid;
+ char *name = cpystr (pw->pw_name);
+ long ret = !(setgid (pw->pw_gid) || initgroups (name,pw->pw_gid) ||
+ setuid (uid));
+ fs_give ((void **) &name);
+ return ret;
+}
diff --git a/imap/src/osdep/amiga/mbx.c b/imap/src/osdep/amiga/mbx.c
new file mode 100644
index 00000000..1ece5d8d
--- /dev/null
+++ b/imap/src/osdep/amiga/mbx.c
@@ -0,0 +1,1855 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MBX mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 October 1995
+ * Last Edited: 11 October 2007
+ */
+
+
+/* FILE TIME SEMANTICS
+ *
+ * The atime is the last read time of the file.
+ * The mtime is the last flags update time of the file.
+ * The ctime is the last write time of the file.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "misc.h"
+#include "dummy.h"
+#include "fdstring.h"
+
+
+/* Build parameters */
+
+#define HDRSIZE 2048
+
+
+/* Kludge to make Cygwin happy */
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+/* MBX I/O stream local data */
+
+typedef struct mbx_local {
+ unsigned int flagcheck: 1; /* if ping should sweep for flags */
+ unsigned int expok: 1; /* if expunging OK in ping */
+ unsigned int expunged : 1; /* if one or more expunged messages */
+ int fd; /* file descriptor for I/O */
+ int ld; /* lock file descriptor */
+ int ffuserflag; /* first free user flag */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ time_t lastsnarf; /* last snarf time */
+ unsigned long lastpid; /* PID of last writer */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ char lock[MAILTMPLEN]; /* buffer to write lock name */
+} MBXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MBXLOCAL *) stream->local)
+
+/* Function prototypes */
+
+DRIVER *mbx_valid (char *name);
+int mbx_isvalid (MAILSTREAM **stream,char *name,char *tmp,int *ld,char *lock,
+ long flags);
+void *mbx_parameters (long function,void *value);
+void mbx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void mbx_list (MAILSTREAM *stream,char *ref,char *pat);
+void mbx_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long mbx_create (MAILSTREAM *stream,char *mailbox);
+long mbx_delete (MAILSTREAM *stream,char *mailbox);
+long mbx_rename (MAILSTREAM *stream,char *old,char *newname);
+long mbx_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *mbx_open (MAILSTREAM *stream);
+void mbx_close (MAILSTREAM *stream,long options);
+void mbx_abort (MAILSTREAM *stream);
+void mbx_flags (MAILSTREAM *stream,char *sequence,long flags);
+char *mbx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags);
+long mbx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void mbx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+void mbx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long mbx_ping (MAILSTREAM *stream);
+void mbx_check (MAILSTREAM *stream);
+long mbx_expunge (MAILSTREAM *stream,char *sequence,long options);
+void mbx_snarf (MAILSTREAM *stream);
+long mbx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long mbx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+char *mbx_file (char *dst,char *name);
+long mbx_parse (MAILSTREAM *stream);
+MESSAGECACHE *mbx_elt (MAILSTREAM *stream,unsigned long msgno,long expok);
+unsigned long mbx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt);
+void mbx_update_header (MAILSTREAM *stream);
+void mbx_update_status (MAILSTREAM *stream,unsigned long msgno,long flags);
+unsigned long mbx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size,char **hdr);
+unsigned long mbx_rewrite (MAILSTREAM *stream,unsigned long *reclaimed,
+ long flags);
+long mbx_flaglock (MAILSTREAM *stream);
+
+/* MBX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mbxdriver = {
+ "mbx", /* driver name */
+ DR_LOCAL|DR_MAIL|DR_CRLF|DR_LOCKING,
+ /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ mbx_valid, /* mailbox is valid for us */
+ mbx_parameters, /* manipulate parameters */
+ mbx_scan, /* scan mailboxes */
+ mbx_list, /* list mailboxes */
+ mbx_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ mbx_create, /* create mailbox */
+ mbx_delete, /* delete mailbox */
+ mbx_rename, /* rename mailbox */
+ mbx_status, /* status of mailbox */
+ mbx_open, /* open mailbox */
+ mbx_close, /* close mailbox */
+ mbx_flags, /* fetch message "fast" attributes */
+ mbx_flags, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mbx_header, /* fetch message header */
+ mbx_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ mbx_flag, /* modify flags */
+ mbx_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mbx_ping, /* ping mailbox to see if still alive */
+ mbx_check, /* check for new messages */
+ mbx_expunge, /* expunge deleted messages */
+ mbx_copy, /* copy messages to another mailbox */
+ mbx_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mbxproto = {&mbxdriver};
+
+/* MBX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mbx_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ int fd = mbx_isvalid (NIL,name,tmp,NIL,NIL,NIL);
+ if (fd < 0) return NIL;
+ close (fd); /* don't need the fd now */
+ return &mbxdriver;
+}
+
+
+/* MBX mail test for valid mailbox
+ * Accepts: returned stream with valid mailbox keywords
+ * mailbox name
+ * scratch buffer
+ * returned lock fd
+ * returned lock name
+ * RW flags or NIL for readonly
+ * Returns: file descriptor if valid, NIL otherwise
+ */
+
+#define MBXISVALIDNOUID 0x1 /* RW, don't do UID action */
+#define MBXISVALIDUID 0x2 /* RW, do UID action */
+
+int mbx_isvalid (MAILSTREAM **stream,char *name,char *tmp,int *ld,char *lock,
+ long flags)
+{
+ int fd,upd;
+ int ret = -1;
+ unsigned long i;
+ long j,k;
+ off_t pos;
+ char c,*s,*t,hdr[HDRSIZE];
+ struct stat sbuf;
+ time_t tp[2];
+ int error = EINVAL; /* assume invalid argument */
+ if (ld) *ld = -1; /* initially no lock */
+ if ((s = mbx_file (tmp,name)) && !stat (s,&sbuf) &&
+ ((fd = open (tmp,(flags ? O_RDWR : O_RDONLY)|O_BINARY,NIL)) >= 0)) {
+ error = -1; /* bogus format */
+ /* I love cretinous C compilers -- don't you? */
+ if (read (fd,hdr,HDRSIZE) == HDRSIZE)
+ if ((hdr[0] == '*') && (hdr[1] == 'm') && (hdr[2] == 'b') &&
+ (hdr[3] == 'x') && (hdr[4] == '*') && (hdr[5] == '\015') &&
+ (hdr[6] == '\012') && isxdigit (hdr[7]) && isxdigit (hdr[8]))
+ if (isxdigit (hdr[9]) && isxdigit (hdr[10]) && isxdigit (hdr[11]) &&
+ isxdigit (hdr[12]) && isxdigit (hdr[13]) && isxdigit (hdr[14]) &&
+ isxdigit (c = hdr[15]) && isxdigit (hdr[16]))
+ if (isxdigit (hdr[17]) && isxdigit (hdr[18]) &&
+ isxdigit (hdr[19]) && isxdigit (hdr[20]) &&
+ isxdigit (hdr[21]) && isxdigit (hdr[22]) &&
+ (hdr[23] == '\015') && (hdr[24] == '\012')) {
+ ret = fd; /* mbx format */
+
+ if (stream) { /* lock if making mini-stream */
+ if (flock (fd,LOCK_SH) ||
+ (flags && ((*ld = lockfd (fd,lock,LOCK_EX)) < 0))) ret = -1;
+ /* reread data now that locked */
+ else if (lseek (fd,0,L_SET) ||
+ (read (fd,hdr,HDRSIZE) != HDRSIZE)) ret = -1;
+ else {
+ *stream = (MAILSTREAM *) memset (fs_get (sizeof (MAILSTREAM)),
+ 0,sizeof (MAILSTREAM));
+ hdr[15] = '\0'; /* tie off UIDVALIDITY */
+ (*stream)->uid_validity = strtoul (hdr+7,NIL,16);
+ hdr[15] = c; /* now get UIDLAST */
+ (*stream)->uid_last = strtoul (hdr+15,NIL,16);
+ /* parse user flags */
+ for (i = 0, s = hdr + 25;
+ (i < NUSERFLAGS) && (t = strchr (s,'\015')) && (t - s);
+ i++, s = t + 2) {
+ *t = '\0'; /* tie off flag */
+ if (strlen (s) <= MAXUSERFLAG)
+ (*stream)->user_flags[i] = cpystr (s);
+ }
+ /* make sure have true UIDLAST */
+ if (flags & MBXISVALIDUID) {
+ for (upd = NIL,pos = 2048, k = 0; pos < sbuf.st_size;
+ pos += (j + k)) {
+ /* read header for this message */
+ lseek (fd,pos,L_SET);
+ if ((j = read (fd,hdr,64)) >= 0) {
+ hdr[j] = '\0';
+ if ((s = strchr (hdr,'\015')) && (s[1] == '\012')) {
+ *s = '\0';
+ k = s + 2 - hdr;
+ if ((s = strchr (hdr,',')) &&
+ (j = strtol (s+1,&s,10)) && (*s == ';') &&
+ (s = strchr (s+1,'-'))) {
+ /* get UID if there is any */
+ i = strtoul (++s,&t,16);
+ if (!*t && (t == (s + 8)) &&
+ (i <= (*stream)->uid_last)) {
+ if (!i) {
+ lseek (fd,pos + s - hdr,L_SET);
+ sprintf (hdr,"%08lx",++(*stream)->uid_last);
+ write (fd,hdr,8);
+ upd = T;
+ }
+ continue;
+ }
+ }
+ }
+ ret = -1; /* error, give up */
+ *stream = mail_close (*stream);
+ pos = sbuf.st_size + 1;
+ j = k = 0;
+ }
+ }
+
+ if (upd) { /* need to update hdr with new UIDLAST? */
+ lseek (fd,15,L_SET);
+ sprintf (hdr,"%08lx",(*stream)->uid_last);
+ write (fd,hdr,8);
+ }
+ }
+ }
+ }
+ }
+ if (ret != fd) close (fd); /* close the file */
+ else lseek (fd,0,L_SET); /* else rewind to start */
+ /* \Marked status? */
+ if (sbuf.st_ctime > sbuf.st_atime) {
+ tp[0] = sbuf.st_atime; /* preserve atime and mtime */
+ tp[1] = sbuf.st_mtime;
+ utime (tmp,tp); /* set the times */
+ }
+ }
+ /* in case INBOX but not mbx format */
+ else if (((error = errno) == ENOENT) && !compare_cstring (name,"INBOX"))
+ error = -1;
+ if ((ret < 0) && ld && (*ld >= 0)) {
+ unlockfd (*ld,lock);
+ *ld = -1;
+ }
+ errno = error; /* return as last error */
+ return ret; /* return what we should */
+}
+
+/* MBX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mbx_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = mbx_file ((char *) value,"INBOX");
+ break;
+ case SET_ONETIMEEXPUNGEATPING:
+ if (value) ((MBXLOCAL *) ((MAILSTREAM *) value)->local)->expok = T;
+ case GET_ONETIMEEXPUNGEATPING:
+ if (value) ret = (void *)
+ (((MBXLOCAL *) ((MAILSTREAM *) value)->local)->expok ? VOIDT : NIL);
+ break;
+ }
+ return ret;
+}
+
+
+/* MBX mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mbx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* MBX mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mbx_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* MBX mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mbx_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* MBX mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,*t,mbx[MAILTMPLEN],tmp[HDRSIZE];
+ long ret = NIL;
+ int i,fd;
+ if (!(s = mbx_file (mbx,mailbox))) {
+ sprintf (mbx,"Can't create %.80s: invalid name",mailbox);
+ MM_LOG (mbx,ERROR);
+ }
+ /* create underlying file */
+ else if (dummy_create_path (stream,s,get_dir_protection (mailbox))) {
+ /* done if made directory */
+ if ((s = strrchr (s,'/')) && !s[1]) return T;
+ if ((fd = open (mbx,O_WRONLY|O_BINARY,NIL)) < 0) {
+ sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ else {
+ memset (tmp,'\0',HDRSIZE);/* initialize header */
+ sprintf (s = tmp,"*mbx*\015\012%08lx00000000\015\012",
+ (unsigned long) time (0));
+ for (i = 0; i < NUSERFLAGS; ++i) {
+ t = (stream && stream->user_flags[i]) ? stream->user_flags[i] :
+ ((t = default_user_flag (i)) ? t : "");
+ sprintf (s += strlen (s),"%s\015\012",t);
+ }
+ if (write (fd,tmp,HDRSIZE) != HDRSIZE) {
+ sprintf (tmp,"Can't initialize mailbox node %.80s: %s",
+ mbx,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ else ret = T; /* success */
+ close (fd); /* close file */
+ }
+ }
+ /* set proper protections */
+ return ret ? set_mbx_protections (mailbox,mbx) : NIL;
+}
+
+
+/* MBX mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return mbx_rename (stream,mailbox,NIL);
+}
+
+/* MBX mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = LONGT;
+ char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ int fd,ld;
+ struct stat sbuf;
+ if (!mbx_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'/')) && !s[1])))) {
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ else if ((fd = open (file,O_RDWR|O_BINARY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get parse/append permission */
+ if ((ld = lockfd (fd,lock,LOCK_EX)) < 0) {
+ MM_LOG ("Unable to lock rename mailbox",ERROR);
+ return NIL;
+ }
+ /* lock out other users */
+ if (flock (fd,LOCK_EX|LOCK_NB)) {
+ close (fd); /* couldn't lock, give up on it then */
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+ MM_LOG (tmp,ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return NIL;
+ }
+
+ if (newname) { /* want rename? */
+ /* found superior to destination name? */
+ if (s = strrchr (tmp,'/')) {
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* superior name doesn't exist, create it */
+ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,tmp,get_dir_protection (newname)))
+ ret = NIL;
+ else *s = c; /* restore full name */
+ }
+ /* rename the file */
+ if (ret && rename (file,tmp)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ else if (unlink (file)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ flock (fd,LOCK_UN); /* release lock on the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ close (fd); /* close the file */
+ /* recreate file if renamed INBOX */
+ if (ret && !compare_cstring (old,"INBOX")) mbx_create (NIL,"INBOX");
+ return ret; /* return success */
+}
+
+/* MBX Mail status
+ * Accepts: mail stream
+ * mailbox name
+ * status flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_status (MAILSTREAM *stream,char *mbx,long flags)
+{
+ MAILSTATUS status;
+ unsigned long i;
+ MAILSTREAM *tstream = NIL;
+ MAILSTREAM *systream = NIL;
+ /* make temporary stream (unless this mbx) */
+ if (!stream && !(stream = tstream =
+ mail_open (NIL,mbx,OP_READONLY|OP_SILENT)))
+ return NIL;
+ status.flags = flags; /* return status values */
+ status.messages = stream->nmsgs;
+ status.recent = stream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++)
+ if (!mail_elt (stream,i)->seen) status.unseen++;
+ status.uidnext = stream->uid_last + 1;
+ status.uidvalidity = stream->uid_validity;
+ /* calculate post-snarf results */
+ if (!status.recent && stream->inbox &&
+ (systream = mail_open (NIL,sysinbox (),OP_READONLY|OP_SILENT))) {
+ status.messages += systream->nmsgs;
+ status.recent += systream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1; i <= systream->nmsgs; i++)
+ if (!mail_elt (systream,i)->seen) status.unseen++;
+ /* kludge but probably good enough */
+ status.uidnext += systream->nmsgs;
+ }
+ MM_STATUS(stream,mbx,&status);/* pass status to main program */
+ if (tstream) mail_close (tstream);
+ if (systream) mail_close (systream);
+ return T; /* success */
+}
+
+/* MBX mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mbx_open (MAILSTREAM *stream)
+{
+ int fd,ld;
+ short silent;
+ char tmp[MAILTMPLEN];
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return user_flags (&mbxproto);
+ if (stream->local) fatal ("mbx recycle stream");
+ /* canonicalize the mailbox name */
+ if (!mbx_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ MM_LOG (tmp,ERROR);
+ }
+ if (stream->rdonly ||
+ (fd = open (tmp,O_RDWR|O_BINARY,NIL)) < 0) {
+ if ((fd = open (tmp,O_RDONLY|O_BINARY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ else if (!stream->rdonly) { /* got it, but readonly */
+ MM_LOG ("Can't get write access to mailbox, access is readonly",WARN);
+ stream->rdonly = T;
+ }
+ }
+
+ stream->local = memset (fs_get (sizeof (MBXLOCAL)),NIL,sizeof (MBXLOCAL));
+ LOCAL->fd = fd; /* bind the file */
+ LOCAL->ld = -1; /* no flaglock */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ /* get parse/append permission */
+ if ((ld = lockfd (LOCAL->fd,tmp,LOCK_EX)) < 0) {
+ MM_LOG ("Unable to lock open mailbox",ERROR);
+ return NIL;
+ }
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH); /* lock the file */
+ (*bn) (BLOCK_NONE,NIL);
+ unlockfd (ld,tmp); /* release shared parse permission */
+ LOCAL->filesize = HDRSIZE; /* initialize parsed file size */
+ /* time not set up yet */
+ LOCAL->lastsnarf = LOCAL->filetime = 0;
+ LOCAL->expok = LOCAL->flagcheck = NIL;
+ stream->sequence++; /* bump sequence number */
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ silent = stream->silent; /* defer events */
+ stream->silent = T;
+ if (mbx_ping (stream) && !stream->nmsgs)
+ MM_LOG ("Mailbox is empty",(long) NIL);
+ stream->silent = silent; /* now notify upper level */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,stream->recent);
+ if (!LOCAL) return NIL; /* failure if stream died */
+ stream->perm_seen = stream->perm_deleted = stream->perm_flagged =
+ stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ stream->kwd_create = (stream->user_flags[NUSERFLAGS-1] || stream->rdonly) ?
+ NIL : T; /* can we create new user flags? */
+ return stream; /* return stream to caller */
+}
+
+/* MBX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mbx_close (MAILSTREAM *stream,long options)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ /* do an expunge if requested */
+ if (options & CL_EXPUNGE) mbx_expunge (stream,NIL,NIL);
+ else { /* otherwise do a checkpoint to purge */
+ LOCAL->expok = T; /* possible expunged messages */
+ mbx_ping (stream);
+ }
+ stream->silent = silent; /* restore previous status */
+ mbx_abort (stream);
+ }
+}
+
+
+/* MBX mail abort stream
+ * Accepts: MAIL stream
+ */
+
+void mbx_abort (MAILSTREAM *stream)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ flock (LOCAL->fd,LOCK_UN); /* unlock local file */
+ close (LOCAL->fd); /* close the local file */
+ /* free local text buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+
+/* MBX mail fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ * Sniffs at file to see if some other process changed the flags
+ */
+
+void mbx_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned long i;
+ if (mbx_ping (stream) && /* ping mailbox, get new status for messages */
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence && !elt->valid)
+ mbx_elt (stream,i,NIL);
+}
+
+/* MBX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *mbx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags)
+{
+ unsigned long i;
+ char *s;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ /* get header position, possibly header */
+ i = mbx_hdrpos (stream,msgno,length,&s);
+ if (!s) { /* mbx_hdrpos() returned header? */
+ lseek (LOCAL->fd,i,L_SET); /* no, get to header position */
+ /* is buffer big enough? */
+ if (*length > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = *length) + 1);
+ }
+ /* slurp the data */
+ read (LOCAL->fd,s = LOCAL->buf,*length);
+ }
+ s[*length] = '\0'; /* tie off string */
+ return s;
+}
+
+/* MBX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ FDDATA d;
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ /* get message status */
+ elt = mbx_elt (stream,msgno,NIL);
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen && mbx_flaglock (stream)) {
+ elt->seen = T; /* mark message as seen */
+ /* recalculate status */
+ mbx_update_status (stream,msgno,NIL);
+ MM_FLAGS (stream,msgno);
+ /* update flags */
+ mbx_flag (stream,NIL,NIL,NIL);
+ }
+ if (!LOCAL) return NIL; /* mbx_flaglock() could have aborted */
+ /* find header position */
+ i = mbx_hdrpos (stream,msgno,&j,NIL);
+ d.fd = LOCAL->fd; /* set up file descriptor */
+ d.pos = i + j;
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE;
+ INIT (bs,fd_string,&d,elt->rfc822_size - j);
+ return LONGT; /* success */
+}
+
+/* MBX mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ * Unlocks flag lock
+ */
+
+void mbx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ time_t tp[2];
+ struct stat sbuf;
+ unsigned long oldpid = LOCAL->lastpid;
+ /* make sure the update takes */
+ if (!stream->rdonly && LOCAL && (LOCAL->fd >= 0) && (LOCAL->ld >= 0)) {
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ /* we are the last flag updater */
+ LOCAL->lastpid = (unsigned long) getpid ();
+ /* update header if needed */
+ if (((LOCAL->ffuserflag < NUSERFLAGS) &&
+ stream->user_flags[LOCAL->ffuserflag]) || (oldpid != LOCAL->lastpid))
+ mbx_update_header (stream);
+ tp[0] = time (0); /* make sure read comes after all that */
+ utime (stream->mailbox,tp);
+ }
+ if (LOCAL->ld >= 0) { /* unlock now */
+ unlockfd (LOCAL->ld,LOCAL->lock);
+ LOCAL->ld = -1;
+ }
+}
+
+
+/* MBX mail per-message modify flags
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void mbx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ if (mbx_flaglock (stream)) mbx_update_status (stream,elt->msgno,NIL);
+}
+
+/* MBX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream still alive, NIL if not
+ */
+
+long mbx_ping (MAILSTREAM *stream)
+{
+ unsigned long i,pos;
+ long ret = NIL;
+ int ld;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ struct stat sbuf;
+ if (stream && LOCAL) { /* only if stream already open */
+ int snarf = stream->inbox && !stream->rdonly;
+ ret = LONGT; /* assume OK */
+ fstat (LOCAL->fd,&sbuf); /* get current file poop */
+ /* allow expunge if permitted at ping */
+ if (mail_parameters (NIL,GET_EXPUNGEATPING,NIL)) LOCAL->expok = T;
+ /* if external modification */
+ if (LOCAL->filetime && (LOCAL->filetime < sbuf.st_mtime))
+ LOCAL->flagcheck = T; /* upgrade to flag checking */
+ /* new mail or flagcheck handling needed? */
+ if (((sbuf.st_size - LOCAL->filesize) || LOCAL->flagcheck ||
+ !stream->nmsgs || snarf) &&
+ ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) >= 0)) {
+ /* reparse header if not flagchecking */
+ if (!LOCAL->flagcheck) ret = mbx_parse (stream);
+ /* sweep mailbox for changed message status */
+ else if (ret = mbx_parse (stream)) {
+ unsigned long recent = 0;
+ LOCAL->filetime = sbuf.st_mtime;
+ for (i = 1; i <= stream->nmsgs; )
+ if (elt = mbx_elt (stream,i,LOCAL->expok)) {
+ if (elt->recent) ++recent;
+ ++i;
+ }
+ mail_recent (stream,recent);
+ LOCAL->flagcheck = NIL; /* got all the updates */
+ }
+ /* always reparse header at least */
+ if (ret && snarf) { /* snarf new messages if still OK */
+ mbx_snarf (stream);
+ /* parse snarfed messages */
+ ret = mbx_parse (stream);
+ }
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ if (ret) { /* must still be alive */
+ if (!LOCAL->expunged) /* look for holes if none known yet */
+ for (i = 1, pos = HDRSIZE;
+ !LOCAL->expunged && (i <= stream->nmsgs);
+ i++, pos += elt->private.special.text.size + elt->rfc822_size)
+ if ((elt = mail_elt (stream,i))->private.special.offset != pos)
+ LOCAL->expunged = T;/* found a hole */
+ /* burp any holes */
+ if (LOCAL->expunged && !stream->rdonly) {
+ if (mbx_rewrite (stream,&i,NIL)) fatal ("expunge on check");
+ if (i) { /* any space reclaimed? */
+ LOCAL->expunged = NIL;/* no more pending expunge */
+ sprintf (LOCAL->buf,"Reclaimed %lu bytes of expunged space",i);
+ MM_LOG (LOCAL->buf,(long) NIL);
+ }
+ }
+ LOCAL->expok = NIL; /* no more expok */
+ }
+ }
+ return ret; /* return result of the parse */
+}
+
+/* MBX mail check mailbox (reparses status too)
+ * Accepts: MAIL stream
+ */
+
+void mbx_check (MAILSTREAM *stream)
+{
+ if (LOCAL) LOCAL->expok = T; /* mark that a check is desired */
+ if (mbx_ping (stream)) MM_LOG ("Check completed",(long) NIL);
+}
+
+
+/* MBX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T if success, NIL if failure
+ */
+
+long mbx_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ unsigned long nexp,reclaimed;
+ if (ret = sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) {
+ if (!mbx_ping (stream)); /* do nothing if stream dead */
+ else if (stream->rdonly) /* won't do on readonly files! */
+ MM_LOG ("Expunge ignored on readonly mailbox",WARN);
+ /* if expunged any messages */
+ else if (nexp = mbx_rewrite (stream,&reclaimed,sequence ? -1 : 1)) {
+ sprintf (LOCAL->buf,"Expunged %lu messages",nexp);
+ MM_LOG (LOCAL->buf,(long) NIL);
+ }
+ else if (reclaimed) { /* or if any prior expunged space reclaimed */
+ sprintf (LOCAL->buf,"Reclaimed %lu bytes of expunged space",reclaimed);
+ MM_LOG (LOCAL->buf,(long) NIL);
+ }
+ else MM_LOG ("No messages deleted, so no update needed",(long) NIL);
+ }
+ return ret;
+}
+
+/* MBX mail snarf messages from system inbox
+ * Accepts: MAIL stream, already locked
+ */
+
+void mbx_snarf (MAILSTREAM *stream)
+{
+ unsigned long i = 0;
+ unsigned long j,r,hdrlen,txtlen;
+ struct stat sbuf;
+ char *hdr,*txt,tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ MAILSTREAM *sysibx = NIL;
+ /* give up if can't get exclusive permission */
+ if ((time (0) >= (LOCAL->lastsnarf +
+ (long) mail_parameters (NIL,GET_SNARFINTERVAL,NIL))) &&
+ strcmp (sysinbox (),stream->mailbox)) {
+ MM_CRITICAL (stream); /* go critical */
+ /* sizes match and anything in sysinbox? */
+ if (!stat (sysinbox (),&sbuf) && sbuf.st_size &&
+ !fstat (LOCAL->fd,&sbuf) && (sbuf.st_size == LOCAL->filesize) &&
+ (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) &&
+ (!sysibx->rdonly) && (r = sysibx->nmsgs)) {
+ /* yes, go to end of file in our mailbox */
+ lseek (LOCAL->fd,sbuf.st_size,L_SET);
+ /* for each message in sysibx mailbox */
+ while (r && (++i <= sysibx->nmsgs)) {
+ /* snarf message from system INBOX */
+ hdr = cpystr (mail_fetchheader_full (sysibx,i,NIL,&hdrlen,NIL));
+ txt = mail_fetchtext_full (sysibx,i,&txtlen,FT_PEEK);
+ /* if have a message */
+ if (j = hdrlen + txtlen) {
+ /* build header line */
+ mail_date (LOCAL->buf,elt = mail_elt (sysibx,i));
+ sprintf (LOCAL->buf + strlen (LOCAL->buf),
+ ",%lu;00000000%04x-00000000\015\012",j,(unsigned)
+ ((fSEEN * elt->seen) +
+ (fDELETED * elt->deleted) + (fFLAGGED * elt->flagged) +
+ (fANSWERED * elt->answered) + (fDRAFT * elt->draft)));
+ /* copy message */
+ if ((write (LOCAL->fd,LOCAL->buf,strlen (LOCAL->buf)) < 0) ||
+ (write (LOCAL->fd,hdr,hdrlen) < 0) ||
+ (write (LOCAL->fd,txt,txtlen) < 0)) r = 0;
+ }
+ fs_give ((void **) &hdr);
+ }
+
+ /* make sure all the updates take */
+ if (fsync (LOCAL->fd)) r = 0;
+ if (r) { /* delete all the messages we copied */
+ if (r == 1) strcpy (tmp,"1");
+ else sprintf (tmp,"1:%lu",r);
+ mail_setflag (sysibx,tmp,"\\Deleted");
+ mail_expunge (sysibx); /* now expunge all those messages */
+ }
+ else {
+ sprintf (LOCAL->buf,"Can't copy new mail: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,WARN);
+ ftruncate (LOCAL->fd,sbuf.st_size);
+ }
+ fstat (LOCAL->fd,&sbuf); /* yes, get current file size */
+ LOCAL->filetime = sbuf.st_mtime;
+ }
+ if (sysibx) mail_close (sysibx);
+ MM_NOCRITICAL (stream); /* release critical */
+ LOCAL->lastsnarf = time (0);/* note time of last snarf */
+ }
+}
+
+/* MBX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if success, NIL if failed
+ */
+
+long mbx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ time_t tp[2];
+ MESSAGECACHE *elt;
+ unsigned long i,j,k,m;
+ long ret = LONGT;
+ int fd,ld;
+ char *s,*t,file[MAILTMPLEN],lock[MAILTMPLEN];
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ copyuid_t cu = (copyuid_t) mail_parameters (NIL,GET_COPYUID,NIL);
+ SEARCHSET *source = cu ? mail_newsearchset () : NIL;
+ SEARCHSET *dest = cu ? mail_newsearchset () : NIL;
+ MAILSTREAM *dstream = NIL;
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* make sure valid mailbox */
+ if ((fd = mbx_isvalid (&dstream,mailbox,file,&ld,lock,
+ cu ? MBXISVALIDUID : MBXISVALIDNOUID)) < 0)
+ switch (errno) {
+ case ENOENT: /* no such file? */
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid MBX-format mailbox name: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a MBX-format mailbox: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ MM_CRITICAL (stream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
+
+ /* for each requested message */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.special.text.size,L_SET);
+ mail_date(LOCAL->buf,elt);/* build target header */
+ /* get target keyword mask */
+ for (j = elt->user_flags, k = 0; j; )
+ if (s = stream->user_flags[find_rightmost_bit (&j)])
+ for (m = 0; (m < NUSERFLAGS) && (t = dstream->user_flags[m]); m++)
+ if (!compare_cstring (s,t) && (k |= 1 << m)) break;
+ sprintf (LOCAL->buf+strlen(LOCAL->buf),",%lu;%08lx%04x-%08lx\015\012",
+ elt->rfc822_size,k,(unsigned)
+ ((fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)),cu ? ++dstream->uid_last : 0);
+ /* write target header */
+ if (ret = (write (fd,LOCAL->buf,strlen (LOCAL->buf)) > 0)) {
+ for (k = elt->rfc822_size; ret && (j = min (k,LOCAL->buflen)); k -= j){
+ read (LOCAL->fd,LOCAL->buf,j);
+ ret = write (fd,LOCAL->buf,j) >= 0;
+ }
+ if (cu) { /* need to pass back new UID? */
+ mail_append_set (source,mail_uid (stream,i));
+ mail_append_set (dest,dstream->uid_last);
+ }
+ }
+ }
+
+ /* make sure all the updates take */
+ if (!(ret && (ret = !fsync (fd)))) {
+ sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ }
+ if (cu && ret) { /* return sets if doing COPYUID */
+ (*cu) (stream,mailbox,dstream->uid_validity,source,dest);
+ lseek (fd,15,L_SET); /* update UIDLAST */
+ sprintf (LOCAL->buf,"%08lx",dstream->uid_last);
+ write (fd,LOCAL->buf,8);
+ }
+ else { /* flush any sets we may have built */
+ mail_free_searchset (&source);
+ mail_free_searchset (&dest);
+ }
+ if (ret) tp[0] = time (0) - 1;/* set atime to now-1 if successful copy */
+ /* else preserve \Marked status */
+ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0);
+ tp[1] = sbuf.st_mtime; /* preserve mtime */
+ utime (file,tp); /* set the times */
+ close (fd); /* close the file */
+ MM_NOCRITICAL (stream); /* release critical */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ /* delete all requested messages */
+ if (ret && (options & CP_MOVE) && mbx_flaglock (stream)) {
+ for (i = 1; i <= stream->nmsgs; i++) if (mail_elt (stream,i)->sequence) {
+ /* mark message deleted */
+ mbx_elt (stream,i,NIL)->deleted = T;
+ /* recalculate status */
+ mbx_update_status (stream,i,NIL);
+ }
+ /* update flags */
+ mbx_flag (stream,NIL,NIL,NIL);
+ }
+ if (dstream != stream) mail_close (dstream);
+ return ret;
+}
+
+/* MBX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mbx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd,ld;
+ char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ time_t tp[2];
+ FILE *df;
+ MESSAGECACHE elt;
+ long f;
+ unsigned long i,uf;
+ STRING *message;
+ long ret = NIL;
+ MAILSTREAM *dstream = NIL;
+ appenduid_t au = (appenduid_t) mail_parameters (NIL,GET_APPENDUID,NIL);
+ SEARCHSET *dst = au ? mail_newsearchset () : NIL;
+ /* make sure valid mailbox */
+ if ((fd = mbx_isvalid (&dstream,mailbox,file,&ld,lock,
+ au ? MBXISVALIDUID : MBXISVALIDNOUID)) < 0)
+ switch (errno) {
+ case ENOENT: /* no such file? */
+ if (compare_cstring (mailbox,"INBOX")) {
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* can create INBOX here */
+ mbx_create (dstream = stream ? stream : user_flags (&mbxproto),"INBOX");
+ if ((fd = mbx_isvalid (&dstream,mailbox,file,&ld,lock,
+ au ? MBXISVALIDUID : MBXISVALIDNOUID)) >= 0)
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid MBX-format mailbox name: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a MBX-format mailbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+
+ /* get first message */
+ if (!MM_APPEND (af) (dstream,data,&flags,&date,&message)) close (fd);
+ else if (!(df = fdopen (fd,"r+b"))) {
+ MM_LOG ("Unable to reopen append mailbox",ERROR);
+ close (fd);
+ }
+ else {
+ MM_CRITICAL (dstream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ fseek (df,sbuf.st_size,SEEK_SET);
+ errno = 0;
+ for (ret = LONGT; ret && message; ) {
+ if (!SIZE (message)) { /* guard against zero-length */
+ MM_LOG ("Append of zero-length message",ERROR);
+ ret = NIL;
+ break;
+ }
+ f = mail_parse_flags (dstream,flags,&uf);
+ if (date) { /* parse date if given */
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* mark failure */
+ break;
+ }
+ mail_date (tmp,&elt); /* write preseved date */
+ }
+ else internal_date (tmp); /* get current date in IMAP format */
+ /* write header */
+ if (fprintf (df,"%s,%lu;%08lx%04lx-%08lx\015\012",tmp,i = SIZE (message),
+ uf,(unsigned long) f,au ? ++dstream->uid_last : 0) < 0)
+ ret = NIL;
+ else { /* write message */
+ size_t j;
+ if (!message->cursize) SETPOS (message,GETPOS (message));
+ while (i && (j = fwrite (message->curpos,1,message->cursize,df))) {
+ i -= j;
+ SETPOS (message,GETPOS (message) + j);
+ }
+ /* get next message */
+ if (i || !MM_APPEND (af) (dstream,data,&flags,&date,&message))
+ ret = NIL;
+ else if (au) mail_append_set (dst,dstream->uid_last);
+ }
+ }
+
+ /* if error... */
+ if (!ret || (fflush (df) == EOF)) {
+ /* revert file */
+ ftruncate (fd,sbuf.st_size);
+ close (fd); /* make sure fclose() doesn't corrupt us */
+ if (errno) {
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ }
+ ret = NIL;
+ }
+ if (au && ret) { /* return sets if doing APPENDUID */
+ (*au) (mailbox,dstream->uid_validity,dst);
+ fseek (df,15,SEEK_SET); /* update UIDLAST */
+ fprintf (df,"%08lx",dstream->uid_last);
+ }
+ else mail_free_searchset (&dst);
+ /* set atime to now-1 if successful copy */
+ if (ret) tp[0] = time (0) - 1;
+ /* else preserve \Marked status */
+ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0);
+ tp[1] = sbuf.st_mtime; /* preserve mtime */
+ utime (file,tp); /* set the times */
+ fclose (df); /* close the file */
+ MM_NOCRITICAL (dstream); /* release critical */
+ }
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ if (dstream != stream) mail_close (dstream);
+ return ret;
+}
+
+/* Internal routines */
+
+
+/* MBX mail generate file string
+ * Accepts: temporary buffer to write into
+ * mailbox name string
+ * Returns: local file string or NIL if failure
+ */
+
+char *mbx_file (char *dst,char *name)
+{
+ char *s = mailboxfile (dst,name);
+ return (s && !*s) ? mailboxfile (dst,"~/INBOX") : s;
+}
+
+/* MBX mail parse mailbox
+ * Accepts: MAIL stream
+ * Returns: T if parse OK
+ * NIL if failure, stream aborted
+ */
+
+long mbx_parse (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = NIL;
+ unsigned char c,*s,*t,*x;
+ char tmp[MAILTMPLEN];
+ unsigned long i,j,k,m;
+ off_t curpos = LOCAL->filesize;
+ unsigned long nmsgs = stream->nmsgs;
+ unsigned long recent = stream->recent;
+ unsigned long lastuid = 0;
+ short dirty = NIL;
+ short added = NIL;
+ short silent = stream->silent;
+ short uidwarn = T;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ if (sbuf.st_size < curpos) { /* sanity check */
+ sprintf (tmp,"Mailbox shrank from %lu to %lu!",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size);
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ lseek (LOCAL->fd,0,L_SET); /* rewind file */
+ /* read internal header */
+ read (LOCAL->fd,LOCAL->buf,HDRSIZE);
+ LOCAL->buf[HDRSIZE] = '\0'; /* tie off header */
+ c = LOCAL->buf[15]; /* save first character of last UID */
+ LOCAL->buf[15] = '\0';
+ /* parse UID validity */
+ stream->uid_validity = strtoul (LOCAL->buf + 7,NIL,16);
+ LOCAL->buf[15] = c; /* restore first character of last UID */
+ /* parse last UID */
+ i = strtoul (LOCAL->buf + 15,NIL,16);
+ stream->uid_last = stream->rdonly ? max (i,stream->uid_last) : i;
+ /* parse user flags */
+ for (i = 0, s = LOCAL->buf + 25;
+ (i < NUSERFLAGS) && (t = strchr (s,'\015')) && (t - s);
+ i++, s = t + 2) {
+ *t = '\0'; /* tie off flag */
+ if (!stream->user_flags[i] && (strlen (s) <= MAXUSERFLAG))
+ stream->user_flags[i] = cpystr (s);
+ }
+ LOCAL->ffuserflag = (int) i; /* first free user flag */
+
+ /* get current last flag updater PID */
+ i = (isxdigit (LOCAL->buf[HDRSIZE-10]) && isxdigit (LOCAL->buf[HDRSIZE-9]) &&
+ isxdigit (LOCAL->buf[HDRSIZE-8]) && isxdigit (LOCAL->buf[HDRSIZE-7]) &&
+ isxdigit (LOCAL->buf[HDRSIZE-6]) && isxdigit (LOCAL->buf[HDRSIZE-5]) &&
+ isxdigit (LOCAL->buf[HDRSIZE-4]) && isxdigit (LOCAL->buf[HDRSIZE-3]) &&
+ (LOCAL->buf[HDRSIZE-2] == '\015') && (LOCAL->buf[HDRSIZE-1] == '\012'))?
+ strtoul (LOCAL->buf + HDRSIZE - 8,NIL,16) : 0;
+ /* set flagcheck if lastpid changed */
+ if (LOCAL->lastpid && (LOCAL->lastpid != i)) LOCAL->flagcheck = T;
+ LOCAL->lastpid = i; /* set as last PID */
+ stream->silent = T; /* don't pass up exists events yet */
+ while (sbuf.st_size - curpos){/* while there is stuff to parse */
+ /* get to that position in the file */
+ lseek (LOCAL->fd,curpos,L_SET);
+ if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
+ sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size,
+ i ? strerror (errno) : "no data read");
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ LOCAL->buf[i] = '\0'; /* tie off buffer just in case */
+ if (!((s = strchr (LOCAL->buf,'\015')) && (s[1] == '\012'))) {
+ sprintf (tmp,"Unable to find CRLF at %lu in %lu bytes, text: %.80s",
+ (unsigned long) curpos,i,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ *s = '\0'; /* tie off header line */
+ i = (s + 2) - LOCAL->buf; /* note start of text offset */
+ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
+ sprintf (tmp,"Unable to parse internal header at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ if (!(isxdigit (t[1]) && isxdigit (t[2]) && isxdigit (t[3]) &&
+ isxdigit (t[4]) && isxdigit (t[5]) && isxdigit (t[6]) &&
+ isxdigit (t[7]) && isxdigit (t[8]) && isxdigit (t[9]) &&
+ isxdigit (t[10]) && isxdigit (t[11]) && isxdigit (t[12]))) {
+ sprintf (tmp,"Unable to parse message flags at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ if ((t[13] != '-') || t[22] ||
+ !(isxdigit (t[14]) && isxdigit (t[15]) && isxdigit (t[16]) &&
+ isxdigit (t[17]) && isxdigit (t[18]) && isxdigit (t[19]) &&
+ isxdigit (t[20]) && isxdigit (t[21]))) {
+ sprintf (tmp,"Unable to parse message UID at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+
+ *s++ = '\0'; *t++ = '\0'; /* break up fields */
+ /* get message size */
+ if (!(j = strtoul (s,(char **) &x,10)) && (!(x && *x))) {
+ sprintf (tmp,"Unable to parse message size at %lu: %.80s,%.80s;%.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf,(char *) s,
+ (char *) t);
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ /* make sure didn't run off end of file */
+ if (((off_t) (curpos + i + j)) > sbuf.st_size) {
+ sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)",
+ (unsigned long) curpos,(unsigned long) (curpos + i + j),
+ (unsigned long) sbuf.st_size);
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ /* parse UID */
+ if ((m = strtoul (t+13,NIL,16)) &&
+ ((m <= lastuid) || (m > stream->uid_last))) {
+ if (uidwarn) {
+ sprintf (tmp,"Invalid UID %08lx in message %lu, rebuilding UIDs",
+ m,nmsgs+1);
+ MM_LOG (tmp,WARN);
+ uidwarn = NIL;
+ /* restart UID validity */
+ stream->uid_validity = time (0);
+ }
+ m = 0; /* lose this UID */
+ dirty = T; /* mark dirty, set new lastuid */
+ stream->uid_last = lastuid;
+ }
+
+ t[12] = '\0'; /* parse system flags */
+ if ((k = strtoul (t+8,NIL,16)) & fEXPUNGED) {
+ if (m) lastuid = m; /* expunge message, update last UID seen */
+ else { /* no UID assigned? */
+ lastuid = ++stream->uid_last;
+ dirty = T;
+ }
+ }
+ else { /* not expunged, swell the cache */
+ added = T; /* note that a new message was added */
+ mail_exists (stream,++nmsgs);
+ /* instantiate an elt for this message */
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ /* parse the date */
+ if (!mail_parse_date (elt,LOCAL->buf)) {
+ sprintf (tmp,"Unable to parse message date at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ /* note file offset of header */
+ elt->private.special.offset = curpos;
+ /* and internal header size */
+ elt->private.special.text.size = i;
+ /* header size not known yet */
+ elt->private.msg.header.text.size = 0;
+ elt->rfc822_size = j; /* note message size */
+ /* calculate system flags */
+ if (k & fSEEN) elt->seen = T;
+ if (k & fDELETED) elt->deleted = T;
+ if (k & fFLAGGED) elt->flagged = T;
+ if (k & fANSWERED) elt->answered = T;
+ if (k & fDRAFT) elt->draft = T;
+ t[8] = '\0'; /* get user flags value */
+ elt->user_flags = strtoul (t,NIL,16);
+ /* UID already assigned? */
+ if (!(elt->private.uid = m) || !(k & fOLD)) {
+ elt->recent = T; /* no, mark as recent */
+ ++recent; /* count up a new recent message */
+ dirty = T; /* and must rewrite header */
+ /* assign new UID */
+ if (!elt->private.uid) elt->private.uid = ++stream->uid_last;
+ mbx_update_status (stream,elt->msgno,NIL);
+ }
+ /* update last parsed UID */
+ lastuid = elt->private.uid;
+ }
+ curpos += i + j; /* update position */
+ }
+
+ if (dirty && !stream->rdonly){/* update header */
+ mbx_update_header (stream);
+ fsync (LOCAL->fd); /* make sure all the UID updates take */
+ }
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */
+ LOCAL->filetime = sbuf.st_mtime;
+ if (added && !stream->rdonly){/* make sure atime updated */
+ time_t tp[2];
+ tp[0] = time (0);
+ tp[1] = LOCAL->filetime;
+ utime (stream->mailbox,tp);
+ }
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
+ mail_recent (stream,recent); /* and of change in recent messages */
+ return LONGT; /* return the winnage */
+}
+
+/* MBX get cache element with status updating from file
+ * Accepts: MAIL stream
+ * message number
+ * expunge OK flag
+ * Returns: cache element
+ */
+
+MESSAGECACHE *mbx_elt (MAILSTREAM *stream,unsigned long msgno,long expok)
+{
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ struct { /* old flags */
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged;
+ old.answered = elt->answered; old.draft = elt->draft;
+ old.user_flags = elt->user_flags;
+ /* get new flags */
+ if (mbx_read_flags (stream,elt) && expok) {
+ mail_expunged (stream,elt->msgno);
+ return NIL; /* return this message was expunged */
+ }
+ if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
+ (old.flagged != elt->flagged) || (old.answered != elt->answered) ||
+ (old.draft != elt->draft) || (old.user_flags != elt->user_flags))
+ MM_FLAGS (stream,msgno); /* let top level know */
+ return elt;
+}
+
+/* MBX read flags from file
+ * Accepts: MAIL stream
+ * cache element
+ * Returns: non-NIL if message expunged
+ */
+
+unsigned long mbx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ unsigned long i;
+ struct stat sbuf;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ /* make sure file size is good */
+ if (sbuf.st_size < LOCAL->filesize) {
+ sprintf (LOCAL->buf,"Mailbox shrank from %lu to %lu in flag read!",
+ (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size);
+ fatal (LOCAL->buf);
+ }
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 24,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,14) < 0) {
+ sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ if ((LOCAL->buf[0] != ';') || (LOCAL->buf[13] != '-')) {
+ LOCAL->buf[14] = '\0'; /* tie off buffer for error message */
+ sprintf (LOCAL->buf+50,"Invalid flags for message %lu (%lu %lu): %s",
+ elt->msgno,elt->private.special.offset,
+ elt->private.special.text.size,(char *) LOCAL->buf);
+ fatal (LOCAL->buf+50);
+ }
+ LOCAL->buf[13] = '\0'; /* tie off buffer */
+ /* calculate system flags */
+ i = strtoul (LOCAL->buf+9,NIL,16);
+ elt->seen = i & fSEEN ? T : NIL;
+ elt->deleted = i & fDELETED ? T : NIL;
+ elt->flagged = i & fFLAGGED ? T : NIL;
+ elt->answered = i & fANSWERED ? T : NIL;
+ elt->draft = i & fDRAFT ? T : NIL;
+ LOCAL->expunged |= i & fEXPUNGED ? T : NIL;
+ LOCAL->buf[9] = '\0'; /* tie off flags */
+ /* get user flags value */
+ elt->user_flags = strtoul (LOCAL->buf+1,NIL,16);
+ elt->valid = T; /* have valid flags now */
+ return i & fEXPUNGED;
+}
+
+/* MBX update header
+ * Accepts: MAIL stream
+ */
+
+#ifndef CYGKLUDGEOFFSET
+#define CYGKLUDGEOFFSET 0
+#endif
+
+void mbx_update_header (MAILSTREAM *stream)
+{
+ int i;
+ char *s = LOCAL->buf;
+ memset (s,'\0',HDRSIZE); /* initialize header */
+ sprintf (s,"*mbx*\015\012%08lx%08lx\015\012",
+ stream->uid_validity,stream->uid_last);
+ for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i]; ++i)
+ sprintf (s += strlen (s),"%s\015\012",stream->user_flags[i]);
+ LOCAL->ffuserflag = i; /* first free user flag */
+ /* can we create more user flags? */
+ stream->kwd_create = (i < NUSERFLAGS) ? T : NIL;
+ /* write reserved lines */
+ while (i++ < NUSERFLAGS) strcat (s,"\015\012");
+ sprintf (LOCAL->buf + HDRSIZE - 10,"%08lx\015\012",LOCAL->lastpid);
+ while (T) { /* rewind file */
+ lseek (LOCAL->fd,CYGKLUDGEOFFSET,L_SET);
+ /* write new header */
+ if (write (LOCAL->fd,LOCAL->buf + CYGKLUDGEOFFSET,
+ HDRSIZE - CYGKLUDGEOFFSET) > 0) break;
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+}
+
+/* MBX update status string
+ * Accepts: MAIL stream
+ * message number
+ * flags
+ */
+
+void mbx_update_status (MAILSTREAM *stream,unsigned long msgno,long flags)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ /* readonly */
+ if (stream->rdonly || !elt->valid) mbx_read_flags (stream,elt);
+ else { /* readwrite */
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ /* make sure file size is good */
+ if (sbuf.st_size < LOCAL->filesize) {
+ sprintf (LOCAL->buf,"Mailbox shrank from %lu to %lu in flag update!",
+ (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size);
+ fatal (LOCAL->buf);
+ }
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 24,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,14) < 0) {
+ sprintf (LOCAL->buf,"Unable to read old status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ if ((LOCAL->buf[0] != ';') || (LOCAL->buf[13] != '-')) {
+ LOCAL->buf[14] = '\0'; /* tie off buffer for error message */
+ sprintf (LOCAL->buf+50,"Invalid flags for message %lu (%lu %lu): %s",
+ elt->msgno,elt->private.special.offset,
+ elt->private.special.text.size,(char *) LOCAL->buf);
+ fatal (LOCAL->buf+50);
+ }
+ /* print new flag string */
+ sprintf (LOCAL->buf,"%08lx%04x-%08lx",elt->user_flags,(unsigned)
+ (((elt->deleted && flags) ?
+ fEXPUNGED : (strtoul (LOCAL->buf+9,NIL,16)) & fEXPUNGED) +
+ (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft) + fOLD),elt->private.uid);
+ while (T) { /* get to that place in the file */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 23,L_SET);
+ /* write new flags and UID */
+ if (write (LOCAL->fd,LOCAL->buf,21) > 0) break;
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+ }
+}
+
+/* MBX locate header for a message
+ * Accepts: MAIL stream
+ * message number
+ * pointer to returned header size
+ * pointer to possible returned header
+ * Returns: position of header in file
+ */
+
+#define HDRBUFLEN 16384 /* good enough for most headers */
+#define SLOP 4 /* CR LF CR LF */
+
+unsigned long mbx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size,char **hdr)
+{
+ unsigned long siz,done;
+ long i;
+ unsigned char *s,*t,*te;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ unsigned long ret = elt->private.special.offset +
+ elt->private.special.text.size;
+ if (hdr) *hdr = NIL; /* assume no header returned */
+ /* is header size known? */
+ if (*size = elt->private.msg.header.text.size) return ret;
+ /* paranoia check */
+ if (LOCAL->buflen < (HDRBUFLEN + SLOP))
+ fatal ("LOCAL->buf smaller than HDRBUFLEN");
+ lseek (LOCAL->fd,ret,L_SET); /* get to header position */
+ /* read HDRBUFLEN chunks with 4 byte slop */
+ for (done = siz = 0, s = LOCAL->buf;
+ (i = min ((long) (elt->rfc822_size - done),(long) HDRBUFLEN)) &&
+ (read (LOCAL->fd,s,i) == i);
+ done += i, siz += (t - LOCAL->buf) - SLOP, s = LOCAL->buf + SLOP) {
+ te = (t = s + i) - 12; /* calculate end of fast scan */
+ /* fast scan for CR */
+ for (s = LOCAL->buf; s < te;)
+ if (((*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') ||
+ (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') ||
+ (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') ||
+ (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015')) &&
+ (*s == '\012') && (*++s == '\015') && (*++s == '\012')) {
+ *size = elt->private.msg.header.text.size = siz + (++s - LOCAL->buf);
+ if (hdr) *hdr = LOCAL->buf;
+ return ret;
+ }
+ for (te = t - 3; (s < te);) /* final character-at-a-time scan */
+ if ((*s++ == '\015') && (*s == '\012') && (*++s == '\015') &&
+ (*++s == '\012')) {
+ *size = elt->private.msg.header.text.size = siz + (++s - LOCAL->buf);
+ if (hdr) *hdr = LOCAL->buf;
+ return ret;
+ }
+ if (i <= SLOP) break; /* end of data */
+ /* slide over last 4 bytes */
+ memmove (LOCAL->buf,t - SLOP,SLOP);
+ hdr = NIL; /* can't return header this way */
+ }
+ /* not found: header consumes entire message */
+ elt->private.msg.header.text.size = *size = elt->rfc822_size;
+ if (hdr) *hdr = LOCAL->buf; /* possibly return header too */
+ return ret;
+}
+
+/* MBX mail rewrite mailbox
+ * Accepts: MAIL stream
+ * pointer to return reclaimed size
+ * flags (0 = no expunge, 1 = expunge deleted, -1 = expunge sequence)
+ * Returns: number of expunged messages
+ */
+
+unsigned long mbx_rewrite (MAILSTREAM *stream,unsigned long *reclaimed,
+ long flags)
+{
+ time_t tp[2];
+ struct stat sbuf;
+ off_t pos,ppos;
+ int ld;
+ unsigned long i,j,k,m,delta;
+ unsigned long n = *reclaimed = 0;
+ unsigned long recent = 0;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ /* The cretins who designed flock() created a window of vulnerability in
+ * upgrading locks from shared to exclusive or downgrading from exclusive
+ * to shared. Rather than maintain the lock at shared status at a minimum,
+ * flock() actually *releases* the former lock. Obviously they never talked
+ * to any database guys. Fortunately, we have the parse/append permission
+ * lock. If we require this lock before going exclusive on the mailbox,
+ * another process can not sneak in and steal the exclusive mailbox lock on
+ * us, because it will block on trying to get parse/append permission first.
+ */
+ /* get parse/append permission */
+ if ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) < 0) {
+ MM_LOG ("Unable to lock mailbox for rewrite",ERROR);
+ return 0;
+ }
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime && !LOCAL->flagcheck &&
+ (LOCAL->filetime < sbuf.st_mtime)) LOCAL->flagcheck = T;
+ if (!mbx_parse (stream)) { /* make sure see any newly-arrived messages */
+ unlockfd (ld,lock); /* failed?? */
+ return 0;
+ }
+ if (LOCAL->flagcheck) { /* sweep flags if need flagcheck */
+ LOCAL->filetime = sbuf.st_mtime;
+ for (i = 1; i <= stream->nmsgs; ++i) mbx_elt (stream,i,NIL);
+ LOCAL->flagcheck = NIL;
+ }
+
+ /* get exclusive access */
+ if (!flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
+ MM_CRITICAL (stream); /* go critical */
+ for (i = 1,delta = 0,pos = ppos = HDRSIZE; i <= stream->nmsgs; ) {
+ /* note if message not at predicted location */
+ if (m = (elt = mbx_elt (stream,i,NIL))->private.special.offset - ppos) {
+ ppos = elt->private.special.offset;
+ *reclaimed += m; /* note reclaimed message space */
+ delta += m; /* and as expunge delta */
+ }
+ /* number of bytes to smash or preserve */
+ ppos += (k = elt->private.special.text.size + elt->rfc822_size);
+ /* if need to expunge this message*/
+ if (flags && elt->deleted && ((flags > 0) || elt->sequence)) {
+ delta += k; /* number of bytes to delete */
+ mail_expunged(stream,i);/* notify upper levels */
+ n++; /* count up one more expunged message */
+ }
+ else { /* preserved message */
+ i++; /* count this message */
+ if (elt->recent) ++recent;
+ if (delta) { /* moved, note first byte to preserve */
+ j = elt->private.special.offset;
+ do { /* read from source position */
+ m = min (k,LOCAL->buflen);
+ lseek (LOCAL->fd,j,L_SET);
+ read (LOCAL->fd,LOCAL->buf,m);
+ pos = j - delta; /* write to destination position */
+ while (T) {
+ lseek (LOCAL->fd,pos,L_SET);
+ if (write (LOCAL->fd,LOCAL->buf,m) > 0) break;
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+ pos += m; /* new position */
+ j += m; /* next chunk, perhaps */
+ } while (k -= m); /* until done */
+ /* note the new address of this text */
+ elt->private.special.offset -= delta;
+ }
+ /* preserved but no deleted messages yet */
+ else pos = elt->private.special.offset + k;
+ }
+ }
+ /* deltaed file size match position? */
+ if (m = (LOCAL->filesize -= delta) - pos) {
+ *reclaimed += m; /* probably an fEXPUNGED msg */
+ LOCAL->filesize = pos; /* set correct size */
+ }
+ /* truncate file after last message */
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ fsync (LOCAL->fd); /* force disk update */
+ MM_NOCRITICAL (stream); /* release critical */
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH); /* allow sharers again */
+ (*bn) (BLOCK_NONE,NIL);
+ }
+
+ else { /* can't get exclusive */
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH); /* recover previous shared mailbox lock */
+ (*bn) (BLOCK_NONE,NIL);
+ /* do hide-expunge when shared */
+ if (flags) for (i = 1; i <= stream->nmsgs; ) {
+ if (elt = mbx_elt (stream,i,T)) {
+ /* make the message invisible */
+ if (elt->deleted && ((flags > 0) || elt->sequence)) {
+ mbx_update_status (stream,elt->msgno,LONGT);
+ /* notify upper levels */
+ mail_expunged (stream,i);
+ n++; /* count up one more expunged message */
+ }
+ else {
+ i++; /* preserved message */
+ if (elt->recent) ++recent;
+ }
+ }
+ else n++; /* count up one more expunged message */
+ }
+ fsync (LOCAL->fd); /* force disk update */
+ }
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* reset atime to now */
+ utime (stream->mailbox,tp);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ return n; /* return number of expunged messages */
+}
+
+/* MBX mail lock for flag updating
+ * Accepts: stream
+ * Returns: T if successful, NIL if failure
+ */
+
+long mbx_flaglock (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ unsigned long i;
+ int ld;
+ char lock[MAILTMPLEN];
+ /* no-op if readonly or already locked */
+ if (!stream->rdonly && LOCAL && (LOCAL->fd >= 0) && (LOCAL->ld < 0)) {
+ /* lock now */
+ if ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) < 0) return NIL;
+ if (!LOCAL->flagcheck) { /* don't do this if flagcheck already needed */
+ if (LOCAL->filetime) { /* know previous time? */
+ fstat (LOCAL->fd,&sbuf);/* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->flagcheck = T;
+ LOCAL->filetime = 0; /* don't do this test for any other messages */
+ }
+ if (!mbx_parse (stream)) {/* parse mailbox */
+ unlockfd (ld,lock); /* shouldn't happen */
+ return NIL;
+ }
+ if (LOCAL->flagcheck) /* invalidate cache if flagcheck */
+ for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->valid = NIL;
+ }
+ LOCAL->ld = ld; /* copy to stream for subsequent calls */
+ memcpy (LOCAL->lock,lock,MAILTMPLEN);
+ }
+ return LONGT;
+}
diff --git a/imap/src/osdep/amiga/mh.c b/imap/src/osdep/amiga/mh.c
new file mode 100644
index 00000000..0226b7af
--- /dev/null
+++ b/imap/src/osdep/amiga/mh.c
@@ -0,0 +1,1283 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MH mail routines
+ *
+ * Author(s): Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 23 February 1992
+ * Last Edited: 11 October 2007
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "misc.h"
+#include "dummy.h"
+#include "fdstring.h"
+
+
+/* Build parameters */
+
+#define MHINBOX "#mhinbox" /* corresponds to namespace in env_unix.c */
+#define MHINBOXDIR "inbox"
+#define MHPROFILE ".mh_profile"
+#define MHCOMMA ','
+#define MHSEQUENCE ".mh_sequence"
+#define MHSEQUENCES ".mh_sequences"
+#define MHPATH "Mail"
+
+
+/* mh_load_message() flags */
+
+#define MLM_HEADER 0x1 /* load message text */
+#define MLM_TEXT 0x2 /* load message text */
+
+/* MH I/O stream local data */
+
+typedef struct mh_local {
+ char *dir; /* spool directory name */
+ unsigned char buf[CHUNKSIZE]; /* temporary buffer */
+ unsigned long cachedtexts; /* total size of all cached texts */
+ time_t scantime; /* last time directory scanned */
+} MHLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MHLOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *mh_valid (char *name);
+int mh_isvalid (char *name,char *tmp,long synonly);
+int mh_namevalid (char *name);
+char *mh_path (char *tmp);
+void *mh_parameters (long function,void *value);
+long mh_dirfmttest (char *name);
+void mh_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void mh_list (MAILSTREAM *stream,char *ref,char *pat);
+void mh_lsub (MAILSTREAM *stream,char *ref,char *pat);
+void mh_list_work (MAILSTREAM *stream,char *dir,char *pat,long level);
+long mh_subscribe (MAILSTREAM *stream,char *mailbox);
+long mh_unsubscribe (MAILSTREAM *stream,char *mailbox);
+long mh_create (MAILSTREAM *stream,char *mailbox);
+long mh_delete (MAILSTREAM *stream,char *mailbox);
+long mh_rename (MAILSTREAM *stream,char *old,char *newname);
+MAILSTREAM *mh_open (MAILSTREAM *stream);
+void mh_close (MAILSTREAM *stream,long options);
+void mh_fast (MAILSTREAM *stream,char *sequence,long flags);
+void mh_load_message (MAILSTREAM *stream,unsigned long msgno,long flags);
+char *mh_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags);
+long mh_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+long mh_ping (MAILSTREAM *stream);
+void mh_check (MAILSTREAM *stream);
+long mh_expunge (MAILSTREAM *stream,char *sequence,long options);
+long mh_copy (MAILSTREAM *stream,char *sequence,char *mailbox,
+ long options);
+long mh_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+int mh_select (struct direct *name);
+int mh_numsort (const void *d1,const void *d2);
+char *mh_file (char *dst,char *name);
+long mh_canonicalize (char *pattern,char *ref,char *pat);
+void mh_setdate (char *file,MESSAGECACHE *elt);
+
+/* MH mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mhdriver = {
+ "mh", /* driver name */
+ /* driver flags */
+ DR_MAIL|DR_LOCAL|DR_NOFAST|DR_NAMESPACE|DR_NOSTICKY|DR_DIRFMT,
+ (DRIVER *) NIL, /* next driver */
+ mh_valid, /* mailbox is valid for us */
+ mh_parameters, /* manipulate parameters */
+ mh_scan, /* scan mailboxes */
+ mh_list, /* find mailboxes */
+ mh_lsub, /* find subscribed mailboxes */
+ mh_subscribe, /* subscribe to mailbox */
+ mh_unsubscribe, /* unsubscribe from mailbox */
+ mh_create, /* create mailbox */
+ mh_delete, /* delete mailbox */
+ mh_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ mh_open, /* open mailbox */
+ mh_close, /* close mailbox */
+ mh_fast, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mh_header, /* fetch message header */
+ mh_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ NIL, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mh_ping, /* ping mailbox to see if still alive */
+ mh_check, /* check for new messages */
+ mh_expunge, /* expunge deleted messages */
+ mh_copy, /* copy messages to another mailbox */
+ mh_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mhproto = {&mhdriver};
+
+
+static char *mh_profile = NIL; /* holds MH profile */
+static char *mh_pathname = NIL; /* holds MH path name */
+static long mh_once = 0; /* already snarled once */
+static long mh_allow_inbox =NIL;/* allow INBOX as well as MHINBOX */
+
+/* MH mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mh_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return mh_isvalid (name,tmp,T) ? &mhdriver : NIL;
+}
+
+
+/* MH mail test for valid mailbox
+ * Accepts: mailbox name
+ * temporary buffer to use
+ * syntax only test flag
+ * Returns: T if valid, NIL otherwise
+ */
+
+int mh_isvalid (char *name,char *tmp,long synonly)
+{
+ struct stat sbuf;
+ char *s,*t,altname[MAILTMPLEN];
+ unsigned long i;
+ int ret = NIL;
+ errno = NIL; /* zap any error condition */
+ /* mh name? */
+ if ((mh_allow_inbox && !compare_cstring (name,"INBOX")) ||
+ !compare_cstring (name,MHINBOX) ||
+ ((name[0] == '#') && ((name[1] == 'm') || (name[1] == 'M')) &&
+ ((name[2] == 'h') || (name[2] == 'H')) && (name[3] == '/') && name[4])){
+ if (mh_path (tmp)) /* validate name if INBOX or not synonly */
+ ret = (synonly && compare_cstring (name,"INBOX")) ?
+ T : ((stat (mh_file (tmp,name),&sbuf) == 0) &&
+ (sbuf.st_mode & S_IFMT) == S_IFDIR);
+ else if (!mh_once++) { /* only report error once */
+ sprintf (tmp,"%.900s not found, mh format names disabled",mh_profile);
+ mm_log (tmp,WARN);
+ }
+ }
+ /* see if non-NS name within mh hierarchy */
+ else if ((name[0] != '#') && (s = mh_path (tmp)) && (i = strlen (s)) &&
+ (t = mailboxfile (tmp,name)) && !strncmp (t,s,i) &&
+ (tmp[i] == '/') && tmp[i+1]) {
+ sprintf (altname,"#mh%.900s",tmp+i);
+ /* can't do synonly here! */
+ ret = mh_isvalid (altname,tmp,NIL);
+ }
+ else errno = EINVAL; /* bogus name */
+ return ret;
+}
+
+/* MH mail test for valid mailbox
+ * Accepts: mailbox name
+ * Returns: T if valid, NIL otherwise
+ */
+
+int mh_namevalid (char *name)
+{
+ char *s;
+ if (name[0] == '#' && (name[1] == 'm' || name[1] == 'M') &&
+ (name[2] == 'h' || name[2] == 'H') && name[3] == '/')
+ for (s = name; s && *s;) { /* make sure no all-digit nodes */
+ if (isdigit (*s)) s++; /* digit, check this node further... */
+ else if (*s == '/') break;/* all digit node, barf */
+ /* non-digit, skip to next node or return */
+ else if (!((s = strchr (s+1,'/')) && *++s)) return T;
+ }
+ return NIL; /* all numeric or empty node */
+}
+
+/* Return MH path
+ * Accepts: temporary buffer
+ * Returns: MH path or NIL if MH disabled
+ */
+
+char *mh_path (char *tmp)
+{
+ char *s,*t,*v,*r;
+ int fd;
+ struct stat sbuf;
+ if (!mh_profile) { /* build mh_profile and mh_pathname now */
+ sprintf (tmp,"%s/%s",myhomedir (),MHPROFILE);
+ if ((fd = open (mh_profile = cpystr (tmp),O_RDONLY,NIL)) >= 0) {
+ fstat (fd,&sbuf); /* yes, get size and read file */
+ read (fd,(t = (char *) fs_get (sbuf.st_size + 1)),sbuf.st_size);
+ close (fd); /* don't need the file any more */
+ t[sbuf.st_size] = '\0'; /* tie it off */
+ /* parse profile file */
+ for (s = strtok_r (t,"\r\n",&r); s && *s; s = strtok_r (NIL,"\r\n",&r)) {
+ /* found space in line? */
+ if (v = strpbrk (s," \t")) {
+ *v++ = '\0'; /* tie off, is keyword "Path:"? */
+ if (!compare_cstring (s,"Path:")) {
+ /* skip whitespace */
+ while ((*v == ' ') || (*v == '\t')) ++v;
+ /* absolute path? */
+ if (*v == '/') s = v;
+ else sprintf (s = tmp,"%s/%s",myhomedir (),v);
+ /* copy name */
+ mh_pathname = cpystr (s);
+ break; /* don't need to look at rest of file */
+ }
+ }
+ }
+ fs_give ((void **) &t); /* flush profile text */
+ if (!mh_pathname) { /* default path if not in the profile */
+ sprintf (tmp,"%s/%s",myhomedir (),MHPATH);
+ mh_pathname = cpystr (tmp);
+ }
+ }
+ }
+ return mh_pathname;
+}
+
+/* MH manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mh_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = mh_file ((char *) value,"INBOX");
+ break;
+ case GET_DIRFMTTEST:
+ ret = (void *) mh_dirfmttest;
+ break;
+ case SET_MHPROFILE:
+ if (mh_profile) fs_give ((void **) &mh_profile);
+ mh_profile = cpystr ((char *) value);
+ case GET_MHPROFILE:
+ ret = (void *) mh_profile;
+ break;
+ case SET_MHPATH:
+ if (mh_pathname) fs_give ((void **) &mh_pathname);
+ mh_pathname = cpystr ((char *) value);
+ case GET_MHPATH:
+ ret = (void *) mh_pathname;
+ break;
+ case SET_MHALLOWINBOX:
+ mh_allow_inbox = value ? T : NIL;
+ case GET_MHALLOWINBOX:
+ ret = (void *) (mh_allow_inbox ? VOIDT : NIL);
+ }
+ return ret;
+}
+
+
+/* MH test for directory format internal node
+ * Accepts: candidate node name
+ * Returns: T if internal name, NIL otherwise
+ */
+
+long mh_dirfmttest (char *s)
+{
+ int c;
+ /* sequence(s) file is an internal name */
+ if (strcmp (s,MHSEQUENCE) && strcmp (s,MHSEQUENCES)) {
+ if (*s == MHCOMMA) ++s; /* else comma + all numeric name */
+ /* success if all-numeric */
+ while (c = *s++) if (!isdigit (c)) return NIL;
+ }
+ return LONGT;
+}
+
+/* MH scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mh_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ char *s,test[MAILTMPLEN],file[MAILTMPLEN];
+ long i = 0;
+ if (!pat || !*pat) { /* empty pattern? */
+ if (mh_canonicalize (test,ref,"*")) {
+ /* tie off name at root */
+ if (s = strchr (test,'/')) *++s = '\0';
+ else test[0] = '\0';
+ mm_list (stream,'/',test,LATT_NOSELECT);
+ }
+ }
+ /* get canonical form of name */
+ else if (mh_canonicalize (test,ref,pat)) {
+ if (contents) { /* maybe I'll implement this someday */
+ mm_log ("Scan not valid for mh mailboxes",ERROR);
+ return;
+ }
+ if (test[3] == '/') { /* looking down levels? */
+ /* yes, found any wildcards? */
+ if (s = strpbrk (test,"%*")) {
+ /* yes, copy name up to that point */
+ strncpy (file,test+4,i = s - (test+4));
+ file[i] = '\0'; /* tie off */
+ }
+ else strcpy (file,test+4);/* use just that name then */
+ /* find directory name */
+ if (s = strrchr (file,'/')) {
+ *s = '\0'; /* found, tie off at that point */
+ s = file;
+ }
+ /* do the work */
+ mh_list_work (stream,s,test,0);
+ }
+ /* always an INBOX */
+ if (!compare_cstring (test,MHINBOX))
+ mm_list (stream,NIL,MHINBOX,LATT_NOINFERIORS);
+ }
+}
+
+/* MH list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mh_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ mh_scan (stream,ref,pat,NIL);
+}
+
+
+/* MH list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mh_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ void *sdb = NIL;
+ char *s,test[MAILTMPLEN];
+ /* get canonical form of name */
+ if (mh_canonicalize (test,ref,pat) && (s = sm_read (&sdb))) {
+ do if (pmatch_full (s,test,'/')) mm_lsub (stream,'/',s,NIL);
+ while (s = sm_read (&sdb)); /* until no more subscriptions */
+ }
+}
+
+/* MH list mailboxes worker routine
+ * Accepts: mail stream
+ * directory name to search
+ * search pattern
+ * search level
+ */
+
+void mh_list_work (MAILSTREAM *stream,char *dir,char *pat,long level)
+{
+ DIR *dp;
+ struct direct *d;
+ struct stat sbuf;
+ char *cp,*np,curdir[MAILTMPLEN],name[MAILTMPLEN];
+ /* build MH name to search */
+ if (dir) sprintf (name,"#mh/%s/",dir);
+ else strcpy (name,"#mh/");
+ /* make directory name, punt if bogus */
+ if (!mh_file (curdir,name)) return;
+ cp = curdir + strlen (curdir);/* end of directory name */
+ np = name + strlen (name); /* end of MH name */
+ if (dp = opendir (curdir)) { /* open directory */
+ while (d = readdir (dp)) /* scan, ignore . and numeric names */
+ if ((d->d_name[0] != '.') && !mh_select (d)) {
+ strcpy (cp,d->d_name); /* make directory name */
+ if (!stat (curdir,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) {
+ strcpy (np,d->d_name);/* make mh name of directory name */
+ /* yes, an MH name if full match */
+ if (pmatch_full (name,pat,'/')) mm_list (stream,'/',name,NIL);
+ /* check if should recurse */
+ if (dmatch (name,pat,'/') &&
+ (level < (long) mail_parameters (NIL,GET_LISTMAXLEVEL,NIL)))
+ mh_list_work (stream,name+4,pat,level+1);
+ }
+ }
+ closedir (dp); /* all done, flush directory */
+ }
+}
+
+/* MH mail subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long mh_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return sm_subscribe (mailbox);
+}
+
+
+/* MH mail unsubscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to delete from subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long mh_unsubscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return sm_unsubscribe (mailbox);
+}
+
+/* MH mail create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mh_create (MAILSTREAM *stream,char *mailbox)
+{
+ char tmp[MAILTMPLEN];
+ if (!mh_namevalid (mailbox)) /* validate name */
+ sprintf (tmp,"Can't create mailbox %.80s: invalid MH-format name",mailbox);
+ /* must not already exist */
+ else if (mh_isvalid (mailbox,tmp,NIL))
+ sprintf (tmp,"Can't create mailbox %.80s: mailbox already exists",mailbox);
+ else if (!mh_path (tmp)) return NIL;
+ /* try to make it */
+ else if (!(mh_file (tmp,mailbox) &&
+ dummy_create_path (stream,strcat (tmp,"/"),
+ get_dir_protection (mailbox))))
+ sprintf (tmp,"Can't create mailbox %.80s: %s",mailbox,strerror (errno));
+ else return LONGT; /* success */
+ mm_log (tmp,ERROR);
+ return NIL;
+}
+
+/* MH mail delete mailbox
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mh_delete (MAILSTREAM *stream,char *mailbox)
+{
+ DIR *dirp;
+ struct direct *d;
+ int i;
+ char tmp[MAILTMPLEN];
+ /* is mailbox valid? */
+ if (!mh_isvalid (mailbox,tmp,NIL)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: no such mailbox",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get name of directory */
+ i = strlen (mh_file (tmp,mailbox));
+ if (dirp = opendir (tmp)) { /* open directory */
+ tmp[i++] = '/'; /* now apply trailing delimiter */
+ /* massacre all mh owned files */
+ while (d = readdir (dirp)) if (mh_dirfmttest (d->d_name)) {
+ strcpy (tmp + i,d->d_name);
+ unlink (tmp); /* sayonara */
+ }
+ closedir (dirp); /* flush directory */
+ }
+ /* try to remove the directory */
+ if (rmdir (mh_file (tmp,mailbox))) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %s",mailbox,strerror (errno));
+ mm_log (tmp,WARN);
+ }
+ return T; /* return success */
+}
+
+/* MH mail rename mailbox
+ * Accepts: MH mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long mh_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ char c,*s,tmp[MAILTMPLEN],tmp1[MAILTMPLEN];
+ struct stat sbuf;
+ /* old mailbox name must be valid */
+ if (!mh_isvalid (old,tmp,NIL))
+ sprintf (tmp,"Can't rename mailbox %.80s: no such mailbox",old);
+ else if (!mh_namevalid (newname))
+ sprintf (tmp,"Can't rename to mailbox %.80s: invalid MH-format name",
+ newname);
+ /* new mailbox name must not be valid */
+ else if (mh_isvalid (newname,tmp,NIL))
+ sprintf (tmp,"Can't rename to mailbox %.80s: destination already exists",
+ newname);
+ /* success if can rename the directory */
+ else { /* found superior to destination name? */
+ if (s = strrchr (mh_file (tmp1,newname),'/')) {
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (tmp1,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,tmp1,get_dir_protection (newname)))
+ return NIL;
+ *s = c; /* restore full name */
+ }
+ if (!rename (mh_file (tmp,old),tmp1)) return T;
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",
+ old,newname,strerror (errno));
+ }
+ mm_log (tmp,ERROR); /* something failed */
+ return NIL;
+}
+
+/* MH mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mh_open (MAILSTREAM *stream)
+{
+ char tmp[MAILTMPLEN];
+ if (!stream) return &mhproto; /* return prototype for OP_PROTOTYPE call */
+ if (stream->local) fatal ("mh recycle stream");
+ stream->local = fs_get (sizeof (MHLOCAL));
+ /* INBOXness is one of the following:
+ * #mhinbox (case-independent)
+ * #mh/inbox (mh is case-independent, inbox is case-dependent)
+ * INBOX (case-independent
+ */
+ stream->inbox = /* note if an INBOX or not */
+ (!compare_cstring (stream->mailbox,MHINBOX) ||
+ ((stream->mailbox[0] == '#') &&
+ ((stream->mailbox[1] == 'm') || (stream->mailbox[1] == 'M')) &&
+ ((stream->mailbox[2] == 'h') || (stream->mailbox[2] == 'H')) &&
+ (stream->mailbox[3] == '/') && !strcmp (stream->mailbox+4,MHINBOXDIR)) ||
+ !compare_cstring (stream->mailbox,"INBOX")) ? T : NIL;
+ mh_file (tmp,stream->mailbox);/* get directory name */
+ LOCAL->dir = cpystr (tmp); /* copy directory name for later */
+ LOCAL->scantime = 0; /* not scanned yet */
+ LOCAL->cachedtexts = 0; /* no cached texts */
+ stream->sequence++; /* bump sequence number */
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ if (!mh_ping (stream)) return NIL;
+ if (!(stream->nmsgs || stream->silent))
+ mm_log ("Mailbox is empty",(long) NIL);
+ return stream; /* return stream to caller */
+}
+
+/* MH mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mh_close (MAILSTREAM *stream,long options)
+{
+ if (LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ if (options & CL_EXPUNGE) mh_expunge (stream,NIL,NIL);
+ if (LOCAL->dir) fs_give ((void **) &LOCAL->dir);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ stream->silent = silent; /* reset silent state */
+ }
+}
+
+
+/* MH mail fetch fast information
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ */
+
+void mh_fast (MAILSTREAM *stream,char *sequence,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned long i;
+ /* set up metadata for all messages */
+ if (stream && LOCAL && ((flags & FT_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence &&
+ !(elt->day && elt->rfc822_size)) mh_load_message (stream,i,NIL);
+}
+
+/* MH load message into cache
+ * Accepts: MAIL stream
+ * message #
+ * option flags
+ */
+
+void mh_load_message (MAILSTREAM *stream,unsigned long msgno,long flags)
+{
+ unsigned long i,j,nlseen;
+ int fd;
+ unsigned char c,*t;
+ struct stat sbuf;
+ MESSAGECACHE *elt;
+ FDDATA d;
+ STRING bs;
+ elt = mail_elt (stream,msgno);/* get elt */
+ /* build message file name */
+ sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid);
+ /* anything we need not currently cached? */
+ if ((!elt->day || !elt->rfc822_size ||
+ ((flags & MLM_HEADER) && !elt->private.msg.header.text.data) ||
+ ((flags & MLM_TEXT) && !elt->private.msg.text.text.data)) &&
+ ((fd = open (LOCAL->buf,O_RDONLY,NIL)) >= 0)) {
+ fstat (fd,&sbuf); /* get file metadata */
+ d.fd = fd; /* set up file descriptor */
+ d.pos = 0; /* start of file */
+ d.chunk = LOCAL->buf;
+ d.chunksize = CHUNKSIZE;
+ INIT (&bs,fd_string,&d,sbuf.st_size);
+ if (!elt->day) { /* set internaldate to file date */
+ struct tm *tm = gmtime (&sbuf.st_mtime);
+ elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1;
+ elt->year = tm->tm_year + 1900 - BASEYEAR;
+ elt->hours = tm->tm_hour; elt->minutes = tm->tm_min;
+ elt->seconds = tm->tm_sec;
+ elt->zhours = 0; elt->zminutes = 0;
+ }
+
+ if (!elt->rfc822_size) { /* know message size yet? */
+ for (i = 0, j = SIZE (&bs), nlseen = 0; j--; ) switch (SNX (&bs)) {
+ case '\015': /* unlikely carriage return */
+ if (!j || (CHR (&bs) != '\012')) {
+ i++; /* ugh, raw CR */
+ nlseen = NIL;
+ break;
+ }
+ SNX (&bs); /* eat the line feed, drop in */
+ --j;
+ case '\012': /* line feed? */
+ i += 2; /* count a CRLF */
+ /* header size known yet? */
+ if (!elt->private.msg.header.text.size && nlseen) {
+ /* note position in file */
+ elt->private.special.text.size = GETPOS (&bs);
+ /* and CRLF-adjusted size */
+ elt->private.msg.header.text.size = i;
+ }
+ nlseen = T; /* note newline seen */
+ break;
+ default: /* ordinary chararacter */
+ i++;
+ nlseen = NIL;
+ break;
+ }
+ SETPOS (&bs,0); /* restore old position */
+ elt->rfc822_size = i; /* note that we have size now */
+ /* header is entire message if no delimiter */
+ if (!elt->private.msg.header.text.size)
+ elt->private.msg.header.text.size = elt->rfc822_size;
+ /* text is remainder of message */
+ elt->private.msg.text.text.size =
+ elt->rfc822_size - elt->private.msg.header.text.size;
+ }
+ /* need to load cache with message data? */
+ if (((flags & MLM_HEADER) && !elt->private.msg.header.text.data) ||
+ ((flags & MLM_TEXT) && !elt->private.msg.text.text.data)) {
+ /* purge cache if too big */
+ if (LOCAL->cachedtexts > max (stream->nmsgs * 4096,2097152)) {
+ /* just can't keep that much */
+ mail_gc (stream,GC_TEXTS);
+ LOCAL->cachedtexts = 0;
+ }
+
+ if ((flags & MLM_HEADER) && !elt->private.msg.header.text.data) {
+ t = elt->private.msg.header.text.data =
+ (unsigned char *) fs_get (elt->private.msg.header.text.size + 1);
+ LOCAL->cachedtexts += elt->private.msg.header.text.size;
+ /* read in message header */
+ for (i = 0; i < elt->private.msg.header.text.size; i++)
+ switch (c = SNX (&bs)) {
+ case '\015': /* unlikely carriage return */
+ *t++ = c;
+ if ((CHR (&bs) == '\012')) {
+ *t++ = SNX (&bs);
+ i++;
+ }
+ break;
+ case '\012': /* line feed? */
+ *t++ = '\015';
+ i++;
+ default:
+ *t++ = c;
+ break;
+ }
+ *t = '\0'; /* tie off string */
+ if ((t - elt->private.msg.header.text.data) !=
+ elt->private.msg.header.text.size) fatal ("mh hdr size mismatch");
+ }
+ if ((flags & MLM_TEXT) && !elt->private.msg.text.text.data) {
+ t = elt->private.msg.text.text.data =
+ (unsigned char *) fs_get (elt->private.msg.text.text.size + 1);
+ SETPOS (&bs,elt->private.special.text.size);
+ LOCAL->cachedtexts += elt->private.msg.text.text.size;
+ /* read in message text */
+ for (i = 0; i < elt->private.msg.text.text.size; i++)
+ switch (c = SNX (&bs)) {
+ case '\015': /* unlikely carriage return */
+ *t++ = c;
+ if ((CHR (&bs) == '\012')) {
+ *t++ = SNX (&bs);
+ i++;
+ }
+ break;
+ case '\012': /* line feed? */
+ *t++ = '\015';
+ i++;
+ default:
+ *t++ = c;
+ break;
+ }
+ *t = '\0'; /* tie off string */
+ if ((t - elt->private.msg.text.text.data) !=
+ elt->private.msg.text.text.size) fatal ("mh txt size mismatch");
+ }
+ }
+ close (fd); /* flush message file */
+ }
+}
+
+/* MH mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *mh_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags)
+{
+ MESSAGECACHE *elt;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ elt = mail_elt (stream,msgno);/* get elt */
+ if (!elt->private.msg.header.text.data)
+ mh_load_message (stream,msgno,MLM_HEADER);
+ *length = elt->private.msg.header.text.size;
+ return (char *) elt->private.msg.header.text.data;
+}
+
+
+/* MH mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mh_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mail_elt (stream,msgno);/* get elt */
+ /* snarf message if don't have it yet */
+ if (!elt->private.msg.text.text.data) {
+ mh_load_message (stream,msgno,MLM_TEXT);
+ if (!elt->private.msg.text.text.data) return NIL;
+ }
+ if (!(flags & FT_PEEK)) { /* mark as seen */
+ mail_elt (stream,msgno)->seen = T;
+ mm_flags (stream,msgno);
+ }
+ INIT (bs,mail_string,elt->private.msg.text.text.data,
+ elt->private.msg.text.text.size);
+ return T;
+}
+
+/* MH mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long mh_ping (MAILSTREAM *stream)
+{
+ MAILSTREAM *sysibx = NIL;
+ MESSAGECACHE *elt,*selt;
+ struct stat sbuf;
+ char *s,tmp[MAILTMPLEN];
+ int fd;
+ unsigned long i,j,r;
+ unsigned long old = stream->uid_last;
+ long nmsgs = stream->nmsgs;
+ long recent = stream->recent;
+ int silent = stream->silent;
+ if (stat (LOCAL->dir,&sbuf)) {/* directory exists? */
+ if (stream->inbox && /* no, create if INBOX */
+ dummy_create_path (stream,strcat (mh_file (tmp,MHINBOX),"/"),
+ get_dir_protection ("INBOX"))) return T;
+ sprintf (tmp,"Can't open mailbox %.80s: no such mailbox",stream->mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ stream->silent = T; /* don't pass up mm_exists() events yet */
+ if (sbuf.st_ctime != LOCAL->scantime) {
+ struct direct **names = NIL;
+ long nfiles = scandir (LOCAL->dir,&names,mh_select,mh_numsort);
+ if (nfiles < 0) nfiles = 0; /* in case error */
+ /* note scanned now */
+ LOCAL->scantime = sbuf.st_ctime;
+ /* scan directory */
+ for (i = 0; i < nfiles; ++i) {
+ /* if newly seen, add to list */
+ if ((j = atoi (names[i]->d_name)) > old) {
+ mail_exists (stream,++nmsgs);
+ stream->uid_last = (elt = mail_elt (stream,nmsgs))->private.uid = j;
+ elt->valid = T; /* note valid flags */
+ if (old) { /* other than the first pass? */
+ elt->recent = T; /* yup, mark as recent */
+ recent++; /* bump recent count */
+ }
+ else { /* see if already read */
+ sprintf (tmp,"%s/%s",LOCAL->dir,names[i]->d_name);
+ if (!stat (tmp,&sbuf) && (sbuf.st_atime > sbuf.st_mtime))
+ elt->seen = T;
+ }
+ }
+ fs_give ((void **) &names[i]);
+ }
+ /* free directory */
+ if (s = (void *) names) fs_give ((void **) &s);
+ }
+
+ /* if INBOX, snarf from system INBOX */
+ if (stream->inbox && strcmp (sysinbox (),stream->mailbox)) {
+ old = stream->uid_last;
+ mm_critical (stream); /* go critical */
+ /* see if anything in system inbox */
+ if (!stat (sysinbox (),&sbuf) && sbuf.st_size &&
+ (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) &&
+ !sysibx->rdonly && (r = sysibx->nmsgs)) {
+ for (i = 1; i <= r; ++i) {/* for each message in sysinbox mailbox */
+ /* build file name we will use */
+ sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,++old);
+ /* snarf message from Berkeley mailbox */
+ selt = mail_elt (sysibx,i);
+ if (((fd = open (LOCAL->buf,O_WRONLY|O_CREAT|O_EXCL,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL)))
+ >= 0) &&
+ (s = mail_fetchheader_full (sysibx,i,NIL,&j,FT_INTERNAL)) &&
+ (write (fd,s,j) == j) &&
+ (s = mail_fetchtext_full (sysibx,i,&j,FT_INTERNAL|FT_PEEK)) &&
+ (write (fd,s,j) == j) && !fsync (fd) && !close (fd)) {
+ /* swell the cache */
+ mail_exists (stream,++nmsgs);
+ stream->uid_last = /* create new elt, note its file number */
+ (elt = mail_elt (stream,nmsgs))->private.uid = old;
+ recent++; /* bump recent count */
+ /* set up initial flags and date */
+ elt->valid = elt->recent = T;
+ elt->seen = selt->seen;
+ elt->deleted = selt->deleted;
+ elt->flagged = selt->flagged;
+ elt->answered = selt->answered;
+ elt->draft = selt->draft;
+ elt->day = selt->day;elt->month = selt->month;elt->year = selt->year;
+ elt->hours = selt->hours;elt->minutes = selt->minutes;
+ elt->seconds = selt->seconds;
+ elt->zhours = selt->zhours; elt->zminutes = selt->zminutes;
+ elt->zoccident = selt->zoccident;
+ mh_setdate (LOCAL->buf,elt);
+ sprintf (tmp,"%lu",i);/* delete it from the sysinbox */
+ mail_flag (sysibx,tmp,"\\Deleted",ST_SET);
+ }
+
+ else { /* failed to snarf */
+ if (fd) { /* did it ever get opened? */
+ close (fd); /* close descriptor */
+ unlink (LOCAL->buf);/* flush this file */
+ }
+ sprintf (tmp,"Message copy to MH mailbox failed: %.80s",
+ s,strerror (errno));
+ mm_log (tmp,ERROR);
+ r = 0; /* stop the snarf in its tracks */
+ }
+ }
+ /* update scan time */
+ if (!stat (LOCAL->dir,&sbuf)) LOCAL->scantime = sbuf.st_ctime;
+ mail_expunge (sysibx); /* now expunge all those messages */
+ }
+ if (sysibx) mail_close (sysibx);
+ mm_nocritical (stream); /* release critical */
+ }
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of mailbox size */
+ mail_recent (stream,recent);
+ return T; /* return that we are alive */
+}
+
+/* MH mail check mailbox
+ * Accepts: MAIL stream
+ */
+
+void mh_check (MAILSTREAM *stream)
+{
+ /* Perhaps in the future this will preserve flags */
+ if (mh_ping (stream)) mm_log ("Check completed",(long) NIL);
+}
+
+
+/* MH mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long mh_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ MESSAGECACHE *elt;
+ unsigned long i = 1;
+ unsigned long n = 0;
+ unsigned long recent = stream->recent;
+ if (ret = sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) {
+ mm_critical (stream); /* go critical */
+ while (i <= stream->nmsgs) {/* for each message */
+ elt = mail_elt (stream,i);/* if deleted, need to trash it */
+ if (elt->deleted && (sequence ? elt->sequence : T)) {
+ sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid);
+ if (unlink (LOCAL->buf)) {/* try to delete the message */
+ sprintf (LOCAL->buf,"Expunge of message %lu failed, aborted: %s",i,
+ strerror (errno));
+ mm_log (LOCAL->buf,(long) NIL);
+ break;
+ }
+ /* note uncached */
+ LOCAL->cachedtexts -= ((elt->private.msg.header.text.data ?
+ elt->private.msg.header.text.size : 0) +
+ (elt->private.msg.text.text.data ?
+ elt->private.msg.text.text.size : 0));
+ mail_gc_msg (&elt->private.msg,GC_ENV | GC_TEXTS);
+ /* if recent, note one less recent message */
+ if (elt->recent) --recent;
+ /* notify upper levels */
+ mail_expunged (stream,i);
+ n++; /* count up one more expunged message */
+ }
+ else i++; /* otherwise try next message */
+ }
+ if (n) { /* output the news if any expunged */
+ sprintf (LOCAL->buf,"Expunged %lu messages",n);
+ mm_log (LOCAL->buf,(long) NIL);
+ }
+ else mm_log ("No messages deleted, so no update needed",(long) NIL);
+ mm_nocritical (stream); /* release critical */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ }
+ return ret;
+}
+
+/* MH mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if copy successful, else NIL
+ */
+
+long mh_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ FDDATA d;
+ STRING st;
+ MESSAGECACHE *elt;
+ struct stat sbuf;
+ int fd;
+ unsigned long i;
+ char flags[MAILTMPLEN],date[MAILTMPLEN];
+ appenduid_t au = (appenduid_t) mail_parameters (NIL,GET_APPENDUID,NIL);
+ long ret = NIL;
+ /* copy the messages */
+ if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid);
+ if ((fd = open (LOCAL->buf,O_RDONLY,NIL)) < 0) return NIL;
+ fstat (fd,&sbuf); /* get size of message */
+ if (!elt->day) { /* set internaldate to file date if needed */
+ struct tm *tm = gmtime (&sbuf.st_mtime);
+ elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1;
+ elt->year = tm->tm_year + 1900 - BASEYEAR;
+ elt->hours = tm->tm_hour; elt->minutes = tm->tm_min;
+ elt->seconds = tm->tm_sec;
+ elt->zhours = 0; elt->zminutes = 0;
+ }
+ d.fd = fd; /* set up file descriptor */
+ d.pos = 0; /* start of file */
+ d.chunk = LOCAL->buf;
+ d.chunksize = CHUNKSIZE;
+ /* kludge; mh_append would just strip CRs */
+ INIT (&st,fd_string,&d,sbuf.st_size);
+ /* init flag string */
+ flags[0] = flags[1] = '\0';
+ if (elt->seen) strcat (flags," \\Seen");
+ if (elt->deleted) strcat (flags," \\Deleted");
+ if (elt->flagged) strcat (flags," \\Flagged");
+ if (elt->answered) strcat (flags," \\Answered");
+ if (elt->draft) strcat (flags," \\Draft");
+ flags[0] = '('; /* open list */
+ strcat (flags,")"); /* close list */
+ mail_date (date,elt); /* generate internal date */
+ if (au) mail_parameters (NIL,SET_APPENDUID,NIL);
+ if ((ret = mail_append_full (NIL,mailbox,flags,date,&st)) &&
+ (options & CP_MOVE)) elt->deleted = T;
+ if (au) mail_parameters (NIL,SET_APPENDUID,(void *) au);
+ close (fd);
+ }
+ if (ret && mail_parameters (NIL,GET_COPYUID,NIL))
+ mm_log ("Can not return meaningful COPYUID with this mailbox format",WARN);
+ return ret; /* return success */
+}
+
+/* MH mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mh_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct direct **names = NIL;
+ int fd;
+ char c,*flags,*date,*s,tmp[MAILTMPLEN];
+ STRING *message;
+ MESSAGECACHE elt;
+ FILE *df;
+ long i,size,last,nfiles;
+ long ret = LONGT;
+ /* default stream to prototype */
+ if (!stream) stream = &mhproto;
+ /* make sure valid mailbox */
+ if (!mh_isvalid (mailbox,tmp,NIL)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (!((!compare_cstring (mailbox,MHINBOX) ||
+ !compare_cstring (mailbox,"INBOX")) &&
+ (mh_file (tmp,MHINBOX) &&
+ dummy_create_path (stream,strcat (tmp,"/"),
+ get_dir_protection (mailbox))))) {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* falls through */
+ case 0: /* merely empty file? */
+ break;
+ case EINVAL:
+ sprintf (tmp,"Invalid MH-format mailbox name: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a MH-format mailbox: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get first message */
+ if (!(*af) (stream,data,&flags,&date,&message)) return NIL;
+ if ((nfiles = scandir (tmp,&names,mh_select,mh_numsort)) > 0) {
+ /* largest number */
+ last = atoi (names[nfiles-1]->d_name);
+ for (i = 0; i < nfiles; ++i) /* free directory */
+ fs_give ((void **) &names[i]);
+ }
+ else last = 0; /* no messages here yet */
+ if (s = (void *) names) fs_give ((void **) &s);
+
+ mm_critical (stream); /* go critical */
+ do {
+ if (!SIZE (message)) { /* guard against zero-length */
+ mm_log ("Append of zero-length message",ERROR);
+ ret = NIL;
+ break;
+ }
+ if (date) { /* want to preserve date? */
+ /* yes, parse date into an elt */
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ mm_log (tmp,ERROR);
+ ret = NIL;
+ break;
+ }
+ }
+ mh_file (tmp,mailbox); /* build file name we will use */
+ sprintf (tmp + strlen (tmp),"/%ld",++last);
+ if (((fd = open (tmp,O_WRONLY|O_CREAT|O_EXCL,
+ (long)mail_parameters (NIL,GET_MBXPROTECTION,NIL))) < 0)||
+ !(df = fdopen (fd,"ab"))) {
+ sprintf (tmp,"Can't open append message: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL;
+ break;
+ }
+ /* copy the data w/o CR's */
+ for (size = 0,i = SIZE (message); i && ret; --i)
+ if (((c = SNX (message)) != '\015') && (putc (c,df) == EOF)) ret = NIL;
+ /* close the file */
+ if (!ret || fclose (df)) {
+ unlink (tmp); /* delete message */
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL;
+ }
+ if (ret) { /* set the date for this message */
+ if (date) mh_setdate (tmp,&elt);
+ /* get next message */
+ if (!(*af) (stream,data,&flags,&date,&message)) ret = NIL;
+ }
+ } while (ret && message);
+ mm_nocritical (stream); /* release critical */
+ if (ret && mail_parameters (NIL,GET_APPENDUID,NIL))
+ mm_log ("Can not return meaningful APPENDUID with this mailbox format",
+ WARN);
+ return ret;
+}
+
+/* Internal routines */
+
+
+/* MH file name selection test
+ * Accepts: candidate directory entry
+ * Returns: T to use file name, NIL to skip it
+ */
+
+int mh_select (struct direct *name)
+{
+ char c;
+ char *s = name->d_name;
+ while (c = *s++) if (!isdigit (c)) return NIL;
+ return T;
+}
+
+
+/* MH file name comparision
+ * Accepts: first candidate directory entry
+ * second candidate directory entry
+ * Returns: negative if d1 < d2, 0 if d1 == d2, postive if d1 > d2
+ */
+
+int mh_numsort (const void *d1,const void *d2)
+{
+ return atoi ((*(struct direct **) d1)->d_name) -
+ atoi ((*(struct direct **) d2)->d_name);
+}
+
+
+/* MH mail build file name
+ * Accepts: destination string
+ * source
+ * Returns: destination
+ */
+
+char *mh_file (char *dst,char *name)
+{
+ char *s;
+ char *path = mh_path (dst);
+ if (!path) fatal ("No mh path in mh_file()!");
+ /* INBOX becomes "inbox" in the MH path */
+ if (!compare_cstring (name,MHINBOX) || !compare_cstring (name,"INBOX"))
+ sprintf (dst,"%.900s/%.80s",path,MHINBOXDIR);
+ /* #mh names skip past prefix */
+ else if (*name == '#') sprintf (dst,"%.100s/%.900s",path,name + 4);
+ else mailboxfile (dst,name); /* all other names */
+ /* tie off unnecessary trailing / */
+ if ((s = strrchr (dst,'/')) && !s[1] && (s[-1] == '/')) *s = '\0';
+ return dst;
+}
+
+/* MH canonicalize name
+ * Accepts: buffer to write name
+ * reference
+ * pattern
+ * Returns: T if success, NIL if failure
+ */
+
+long mh_canonicalize (char *pattern,char *ref,char *pat)
+{
+ unsigned long i;
+ char *s,tmp[MAILTMPLEN];
+ if (ref && *ref) { /* have a reference */
+ strcpy (pattern,ref); /* copy reference to pattern */
+ /* # overrides mailbox field in reference */
+ if (*pat == '#') strcpy (pattern,pat);
+ /* pattern starts, reference ends, with / */
+ else if ((*pat == '/') && (pattern[strlen (pattern) - 1] == '/'))
+ strcat (pattern,pat + 1); /* append, omitting one of the period */
+ else strcat (pattern,pat); /* anything else is just appended */
+ }
+ else strcpy (pattern,pat); /* just have basic name */
+ if (mh_isvalid (pattern,tmp,T)) {
+ /* count wildcards */
+ for (i = 0, s = pattern; *s; *s++) if ((*s == '*') || (*s == '%')) ++i;
+ /* success if not too many */
+ if (i <= MAXWILDCARDS) return LONGT;
+ mm_log ("Excessive wildcards in LIST/LSUB",ERROR);
+ }
+ return NIL;
+}
+
+/* Set date for message
+ * Accepts: file name
+ * elt containing date
+ */
+
+void mh_setdate (char *file,MESSAGECACHE *elt)
+{
+ time_t tp[2];
+ tp[0] = time (0); /* atime is now */
+ tp[1] = mail_longdate (elt); /* modification time */
+ utime (file,tp); /* set the times */
+}
diff --git a/imap/src/osdep/amiga/mix.c b/imap/src/osdep/amiga/mix.c
new file mode 100644
index 00000000..fbf4a023
--- /dev/null
+++ b/imap/src/osdep/amiga/mix.c
@@ -0,0 +1,2834 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MIX mail routines
+ *
+ * Author(s): Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@Washington.EDU
+ *
+ * Date: 1 March 2006
+ * Last Edited: 7 May 2008
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "misc.h"
+#include "dummy.h"
+#include "fdstring.h"
+
+/* MIX definitions */
+
+#define MEGABYTE (1024*1024)
+
+#define MIXDATAROLL MEGABYTE /* size at which we roll to a new file */
+
+
+/* MIX files */
+
+#define MIXNAME ".mix" /* prefix for all MIX file names */
+#define MIXMETA "meta" /* suffix for metadata */
+#define MIXINDEX "index" /* suffix for index */
+#define MIXSTATUS "status" /* suffix for status */
+#define MIXSORTCACHE "sortcache"/* suffix for sortcache */
+#define METAMAX (MEGABYTE-1) /* maximum metadata file size (sanity check) */
+
+
+/* MIX file formats */
+
+ /* sequence format (all but msg files) */
+#define SEQFMT "S%08lx\015\012"
+ /* metadata file format */
+#define MTAFMT "V%08lx\015\012L%08lx\015\012N%08lx\015\012"
+ /* index file record format */
+#define IXRFMT ":%08lx:%04d%02d%02d%02d%02d%02d%c%02d%02d:%08lx:%08lx:%08lx:%08lx:%08lx:\015\012"
+ /* status file record format */
+#define STRFMT ":%08lx:%08lx:%04x:%08lx:\015\012"
+ /* message file header format */
+#define MSRFMT "%s%08lx:%04d%02d%02d%02d%02d%02d%c%02d%02d:%08lx:\015\012"
+#define MSGTOK ":msg:"
+#define MSGTSZ (sizeof(MSGTOK)-1)
+ /* sortcache file record format */
+#define SCRFMT ":%08lx:%08lx:%08lx:%08lx:%08lx:%c%08lx:%08lx:%08lx:\015\012"
+
+/* MIX I/O stream local data */
+
+typedef struct mix_local {
+ unsigned long curmsg; /* current message file number */
+ unsigned long newmsg; /* current new message file number */
+ time_t lastsnarf; /* last snarf time */
+ int msgfd; /* file description of current msg file */
+ int mfd; /* file descriptor of open metadata */
+ unsigned long metaseq; /* metadata sequence */
+ char *index; /* mailbox index name */
+ unsigned long indexseq; /* index sequence */
+ char *status; /* mailbox status name */
+ unsigned long statusseq; /* status sequence */
+ char *sortcache; /* mailbox sortcache name */
+ unsigned long sortcacheseq; /* sortcache sequence */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ unsigned int expok : 1; /* non-zero if expunge reports OK */
+ unsigned int internal : 1; /* internally opened, do not validate */
+} MIXLOCAL;
+
+
+#define MIXBURP struct mix_burp
+
+MIXBURP {
+ unsigned long fileno; /* message file number */
+ char *name; /* message file name */
+ SEARCHSET *tail; /* tail of ranges */
+ SEARCHSET set; /* set of retained ranges */
+ MIXBURP *next; /* next file to burp */
+};
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MIXLOCAL *) stream->local)
+
+/* Function prototypes */
+
+DRIVER *mix_valid (char *name);
+long mix_isvalid (char *name,char *meta);
+void *mix_parameters (long function,void *value);
+long mix_dirfmttest (char *name);
+void mix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+long mix_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz);
+void mix_list (MAILSTREAM *stream,char *ref,char *pat);
+void mix_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long mix_subscribe (MAILSTREAM *stream,char *mailbox);
+long mix_unsubscribe (MAILSTREAM *stream,char *mailbox);
+long mix_create (MAILSTREAM *stream,char *mailbox);
+long mix_delete (MAILSTREAM *stream,char *mailbox);
+long mix_rename (MAILSTREAM *stream,char *old,char *newname);
+int mix_rselect (struct direct *name);
+MAILSTREAM *mix_open (MAILSTREAM *stream);
+void mix_close (MAILSTREAM *stream,long options);
+void mix_abort (MAILSTREAM *stream);
+char *mix_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags);
+long mix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void mix_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+unsigned long *mix_sort (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
+ SORTPGM *pgm,long flags);
+THREADNODE *mix_thread (MAILSTREAM *stream,char *type,char *charset,
+ SEARCHPGM *spg,long flags);
+long mix_ping (MAILSTREAM *stream);
+void mix_check (MAILSTREAM *stream);
+long mix_expunge (MAILSTREAM *stream,char *sequence,long options);
+int mix_select (struct direct *name);
+int mix_msgfsort (const void *d1,const void *d2);
+long mix_addset (SEARCHSET **set,unsigned long start,unsigned long size);
+long mix_burp (MAILSTREAM *stream,MIXBURP *burp,unsigned long *reclaimed);
+long mix_burp_check (SEARCHSET *set,size_t size,char *file);
+long mix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,
+ long options);
+long mix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+long mix_append_msg (MAILSTREAM *stream,FILE *f,char *flags,MESSAGECACHE *delt,
+ STRING *msg,SEARCHSET *set,unsigned long seq);
+
+FILE *mix_parse (MAILSTREAM *stream,FILE **idxf,long iflags,long sflags);
+char *mix_meta_slurp (MAILSTREAM *stream,unsigned long *seq);
+long mix_meta_update (MAILSTREAM *stream);
+long mix_index_update (MAILSTREAM *stream,FILE *idxf,long flag);
+long mix_status_update (MAILSTREAM *stream,FILE *statf,long flag);
+FILE *mix_data_open (MAILSTREAM *stream,int *fd,long *size,
+ unsigned long newsize);
+FILE *mix_sortcache_open (MAILSTREAM *stream);
+long mix_sortcache_update (MAILSTREAM *stream,FILE **sortcache);
+char *mix_read_record (FILE *f,char *buf,unsigned long buflen,char *type);
+unsigned long mix_read_sequence (FILE *f);
+char *mix_dir (char *dst,char *name);
+char *mix_file (char *dst,char *dir,char *name);
+char *mix_file_data (char *dst,char *dir,unsigned long data);
+unsigned long mix_modseq (unsigned long oldseq);
+
+/* MIX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mixdriver = {
+ "mix", /* driver name */
+ /* driver flags */
+ DR_MAIL|DR_LOCAL|DR_NOFAST|DR_CRLF|DR_LOCKING|DR_DIRFMT|DR_MODSEQ,
+ (DRIVER *) NIL, /* next driver */
+ mix_valid, /* mailbox is valid for us */
+ mix_parameters, /* manipulate parameters */
+ mix_scan, /* scan mailboxes */
+ mix_list, /* find mailboxes */
+ mix_lsub, /* find subscribed mailboxes */
+ mix_subscribe, /* subscribe to mailbox */
+ mix_unsubscribe, /* unsubscribe from mailbox */
+ mix_create, /* create mailbox */
+ mix_delete, /* delete mailbox */
+ mix_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ mix_open, /* open mailbox */
+ mix_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mix_header, /* fetch message header only */
+ mix_text, /* fetch message body only */
+ NIL, /* fetch partial message test */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ mix_flag, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ mix_sort, /* sort messages */
+ mix_thread, /* thread messages */
+ mix_ping, /* ping mailbox to see if still alive */
+ mix_check, /* check for new messages */
+ mix_expunge, /* expunge deleted messages */
+ mix_copy, /* copy messages to another mailbox */
+ mix_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mixproto = {&mixdriver};
+
+/* MIX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mix_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return mix_isvalid (name,tmp) ? &mixdriver : NIL;
+}
+
+
+/* MIX mail test for valid mailbox
+ * Accepts: mailbox name
+ * buffer to return meta name
+ * Returns: T if valid, NIL otherwise, metadata name written in both cases
+ */
+
+long mix_isvalid (char *name,char *meta)
+{
+ char dir[MAILTMPLEN];
+ struct stat sbuf;
+ /* validate name as directory */
+ if (!(errno = ((strlen (name) > NETMAXMBX) ? ENAMETOOLONG : NIL)) &&
+ *mix_dir (dir,name) && mix_file (meta,dir,MIXMETA) &&
+ !stat (dir,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) {
+ /* name is directory; is it mix? */
+ if (!stat (meta,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFREG))
+ return LONGT;
+ else errno = NIL; /* directory but not mix */
+ }
+ return NIL;
+}
+
+/* MIX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mix_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = mailboxfile ((char *) value,"~/INBOX");
+ break;
+ case GET_DIRFMTTEST:
+ ret = (void *) mix_dirfmttest;
+ break;
+ case GET_SCANCONTENTS:
+ ret = (void *) mix_scan_contents;
+ break;
+ case SET_ONETIMEEXPUNGEATPING:
+ if (value) ((MIXLOCAL *) ((MAILSTREAM *) value)->local)->expok = T;
+ case GET_ONETIMEEXPUNGEATPING:
+ if (value) ret = (void *)
+ (((MIXLOCAL *) ((MAILSTREAM *) value)->local)->expok ? VOIDT : NIL);
+ break;
+ }
+ return ret;
+}
+
+
+/* MIX test for directory format internal node
+ * Accepts: candidate node name
+ * Returns: T if internal name, NIL otherwise
+ */
+
+long mix_dirfmttest (char *name)
+{
+ /* belongs to MIX if starts with .mix */
+ return strncmp (name,MIXNAME,sizeof (MIXNAME) - 1) ? NIL : LONGT;
+}
+
+/* MIX mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* MIX scan mailbox for contents
+ * Accepts: mailbox name
+ * desired contents
+ * contents size
+ * file size (ignored)
+ * Returns: NIL if contents not found, T if found
+ */
+
+long mix_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz)
+{
+ long i,nfiles;
+ void *a;
+ char *s;
+ long ret = NIL;
+ size_t namelen = strlen (name);
+ struct stat sbuf;
+ struct direct **names = NIL;
+ if ((nfiles = scandir (name,&names,mix_select,mix_msgfsort)) > 0)
+ for (i = 0; i < nfiles; ++i) {
+ if (!ret) {
+ sprintf (s = (char *) fs_get (namelen + strlen (names[i]->d_name) + 2),
+ "%s/%s",name,names[i]->d_name);
+ if (!stat (s,&sbuf) && (csiz <= sbuf.st_size))
+ ret = dummy_scan_contents (s,contents,csiz,sbuf.st_size);
+ fs_give ((void **) &s);
+ }
+ fs_give ((void **) &names[i]);
+ }
+ /* free directory list */
+ if (a = (void *) names) fs_give ((void **) &a);
+ return ret;
+}
+
+/* MIX list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mix_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* MIX list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mix_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* MIX mail subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long mix_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return sm_subscribe (mailbox);
+}
+
+
+/* MIX mail unsubscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to delete from subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long mix_unsubscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return sm_unsubscribe (mailbox);
+}
+
+/* MIX mail create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mix_create (MAILSTREAM *stream,char *mailbox)
+{
+ DRIVER *test;
+ FILE *f;
+ int c,i;
+ char *t,tmp[MAILTMPLEN],file[MAILTMPLEN];
+ char *s = strrchr (mailbox,'/');
+ unsigned long now = time (NIL);
+ long ret = NIL;
+ /* always create \NoSelect if trailing / */
+ if (s && !s[1]) return dummy_create (stream,mailbox);
+ /* validate name */
+ if (mix_dirfmttest (s ? s + 1 : mailbox))
+ sprintf(tmp,"Can't create mailbox %.80s: invalid MIX-format name",mailbox);
+ /* must not already exist */
+ else if ((test = mail_valid (NIL,mailbox,NIL)) &&
+ strcmp (test->name,"dummy"))
+ sprintf (tmp,"Can't create mailbox %.80s: mailbox already exists",mailbox);
+ /* create directory and metadata */
+ else if (!dummy_create_path (stream,
+ mix_file (file,mix_dir (tmp,mailbox),MIXMETA),
+ get_dir_protection (mailbox)))
+ sprintf (tmp,"Can't create mailbox %.80s: %.80s",mailbox,strerror (errno));
+ else if (!(f = fopen (file,"w")))
+ sprintf (tmp,"Can't re-open metadata %.80s: %.80s",mailbox,
+ strerror (errno));
+ else { /* success, write initial metadata */
+ fprintf (f,SEQFMT,now);
+ fprintf (f,MTAFMT,now,0,now);
+ for (i = 0, c = 'K'; (i < NUSERFLAGS) &&
+ (t = (stream && stream->user_flags[i]) ? stream->user_flags[i] :
+ default_user_flag (i)) && *t; ++i) {
+ putc (c,f); /* write another keyword */
+ fputs (t,f);
+ c = ' '; /* delimiter is now space */
+ }
+ fclose (f);
+ set_mbx_protections (mailbox,file);
+ /* point to suffix */
+ s = file + strlen (file) - (sizeof (MIXMETA) - 1);
+ strcpy (s,MIXINDEX); /* create index */
+ if (!dummy_create_path (stream,file,get_dir_protection (mailbox)))
+ sprintf (tmp,"Can't create mix mailbox index: %.80s",strerror (errno));
+ else {
+ set_mbx_protections (mailbox,file);
+ strcpy (s,MIXSTATUS); /* create status */
+ if (!dummy_create_path (stream,file,get_dir_protection (mailbox)))
+ sprintf (tmp,"Can't create mix mailbox status: %.80s",
+ strerror (errno));
+ else {
+ set_mbx_protections (mailbox,file);
+ sprintf (s,"%08lx",now);/* message file */
+ if (!dummy_create_path (stream,file,get_dir_protection (mailbox)))
+ sprintf (tmp,"Can't create mix mailbox data: %.80s",
+ strerror (errno));
+ else {
+ set_mbx_protections (mailbox,file);
+ ret = LONGT; /* declare success at this point */
+ }
+ }
+ }
+ }
+ if (!ret) MM_LOG (tmp,ERROR); /* some error */
+ return ret;
+}
+
+/* MIX mail delete mailbox
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mix_delete (MAILSTREAM *stream,char *mailbox)
+{
+ DIR *dirp;
+ struct direct *d;
+ int fd = -1;
+ char *s,tmp[MAILTMPLEN];
+ if (!mix_isvalid (mailbox,tmp))
+ sprintf (tmp,"Can't delete mailbox %.80s: no such mailbox",mailbox);
+ else if (((fd = open (tmp,O_RDWR,NIL)) < 0) || flock (fd,LOCK_EX|LOCK_NB))
+ sprintf (tmp,"Can't lock mailbox for delete: %.80s",mailbox);
+ /* delete metadata */
+ else if (unlink (tmp)) sprintf (tmp,"Can't delete mailbox %.80s index: %80s",
+ mailbox,strerror (errno));
+ else {
+ close (fd); /* close descriptor on deleted metadata */
+ /* get directory name */
+ *(s = strrchr (tmp,'/')) = '\0';
+ if (dirp = opendir (tmp)) { /* open directory */
+ *s++ = '/'; /* restore delimiter */
+ /* massacre messages */
+ while (d = readdir (dirp)) if (mix_dirfmttest (d->d_name)) {
+ strcpy (s,d->d_name); /* make path */
+ unlink (tmp); /* sayonara */
+ }
+ closedir (dirp); /* flush directory */
+ *(s = strrchr (tmp,'/')) = '\0';
+ if (rmdir (tmp)) { /* try to remove the directory */
+ sprintf (tmp,"Can't delete name %.80s: %.80s",
+ mailbox,strerror (errno));
+ MM_LOG (tmp,WARN);
+ }
+ }
+ return T; /* always success */
+ }
+ if (fd >= 0) close (fd); /* close any descriptor on metadata */
+ MM_LOG (tmp,ERROR); /* something failed */
+ return NIL;
+}
+
+/* MIX mail rename mailbox
+ * Accepts: MIX mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long mix_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ char c,*s,tmp[MAILTMPLEN],tmp1[MAILTMPLEN];
+ struct stat sbuf;
+ int fd = -1;
+ if (!mix_isvalid (old,tmp))
+ sprintf (tmp,"Can't rename mailbox %.80s: no such mailbox",old);
+ else if (((fd = open (tmp,O_RDWR,NIL)) < 0) || flock (fd,LOCK_EX|LOCK_NB))
+ sprintf (tmp,"Can't lock mailbox for rename: %.80s",old);
+ else if (mix_dirfmttest ((s = strrchr (newname,'/')) ? s + 1 : newname))
+ sprintf (tmp,"Can't rename to mailbox %.80s: invalid MIX-format name",
+ newname);
+ /* new mailbox name must not be valid */
+ else if (mix_isvalid (newname,tmp))
+ sprintf (tmp,"Can't rename to mailbox %.80s: destination already exists",
+ newname);
+ else {
+ mix_dir (tmp,old); /* build old directory name */
+ mix_dir (tmp1,newname); /* and new directory name */
+ /* easy if not INBOX */
+ if (compare_cstring (old,"INBOX")) {
+ /* found superior to destination name? */
+ if (s = strrchr (tmp1,'/')) {
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (tmp1,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,tmp1,get_dir_protection (newname)))
+ return NIL;
+ *s = c; /* restore full name */
+ }
+ if (!rename (tmp,tmp1)) {
+ close (fd); /* close descriptor on metadata */
+ return LONGT;
+ }
+ }
+
+ /* RFC 3501 requires this */
+ else if (dummy_create_path (stream,strcat (tmp1,"/"),
+ get_dir_protection (newname))) {
+ void *a;
+ int i,n,lasterror;
+ char *src,*dst;
+ struct direct **names = NIL;
+ size_t srcl = strlen (tmp);
+ size_t dstl = strlen (tmp1);
+ /* rename each mix file to new directory */
+ for (i = lasterror = 0,n = scandir (tmp,&names,mix_rselect,alphasort);
+ i < n; ++i) {
+ size_t len = strlen (names[i]->d_name);
+ sprintf (src = (char *) fs_get (srcl + len + 2),"%s/%s",
+ tmp,names[i]->d_name);
+ sprintf (dst = (char *) fs_get (dstl + len + 1),"%s%s",
+ tmp1,names[i]->d_name);
+ if (rename (src,dst)) lasterror = errno;
+ fs_give ((void **) &src);
+ fs_give ((void **) &dst);
+ fs_give ((void **) &names[i]);
+ }
+ /* free directory list */
+ if (a = (void *) names) fs_give ((void **) &a);
+ if (lasterror) errno = lasterror;
+ else {
+ close (fd); /* close descriptor on metadata */
+ return mix_create (NIL,"INBOX");
+ }
+ }
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %.80s",
+ old,newname,strerror (errno));
+ }
+ if (fd >= 0) close (fd); /* close any descriptor on metadata */
+ MM_LOG (tmp,ERROR); /* something failed */
+ return NIL;
+}
+
+
+/* MIX test for mix name
+ * Accepts: candidate directory name
+ * Returns: T if mix file name, NIL otherwise
+ */
+
+int mix_rselect (struct direct *name)
+{
+ return mix_dirfmttest (name->d_name);
+}
+
+/* MIX mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mix_open (MAILSTREAM *stream)
+{
+ short silent;
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return user_flags (&mixproto);
+ if (stream->local) fatal ("mix recycle stream");
+ stream->local = memset (fs_get (sizeof (MIXLOCAL)),0,sizeof (MIXLOCAL));
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ /* make temporary buffer */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ /* set stream->mailbox to be directory name */
+ mix_dir (LOCAL->buf,stream->mailbox);
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (LOCAL->buf);
+ LOCAL->msgfd = -1; /* currently no file open */
+ if (!(((!stream->rdonly && /* open metadata file */
+ ((LOCAL->mfd = open (mix_file (LOCAL->buf,stream->mailbox,MIXMETA),
+ O_RDWR,NIL)) >= 0)) ||
+ ((stream->rdonly = T) &&
+ ((LOCAL->mfd = open (mix_file (LOCAL->buf,stream->mailbox,MIXMETA),
+ O_RDONLY,NIL)) >= 0))) &&
+ !flock (LOCAL->mfd,LOCK_SH))) {
+ MM_LOG ("Error opening mix metadata file",ERROR);
+ mix_abort (stream);
+ stream = NIL; /* open fails */
+ }
+ else { /* metadata open, complete open */
+ LOCAL->index = cpystr (mix_file (LOCAL->buf,stream->mailbox,MIXINDEX));
+ LOCAL->status = cpystr (mix_file (LOCAL->buf,stream->mailbox,MIXSTATUS));
+ LOCAL->sortcache = cpystr (mix_file (LOCAL->buf,stream->mailbox,
+ MIXSORTCACHE));
+ stream->sequence++; /* bump sequence number */
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ if (silent = stream->silent) LOCAL->internal = T;
+ stream->silent = T;
+ if (mix_ping (stream)) { /* do initial ping */
+ /* try burping in case we are exclusive */
+ if (!stream->rdonly) mix_expunge (stream,"",NIL);
+ if (!(stream->nmsgs || stream->silent))
+ MM_LOG ("Mailbox is empty",(long) NIL);
+ stream->silent = silent; /* now notify upper level */
+ mail_exists (stream,stream->nmsgs);
+ stream->perm_seen = stream->perm_deleted = stream->perm_flagged =
+ stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ stream->kwd_create = /* can we create new user flags? */
+ (stream->user_flags[NUSERFLAGS-1] || stream->rdonly) ? NIL : T;
+ }
+ else { /* got murdelyzed in ping */
+ mix_abort (stream);
+ stream = NIL;
+ }
+ }
+ return stream; /* return stream to caller */
+}
+
+/* MIX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mix_close (MAILSTREAM *stream,long options)
+{
+ if (LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ /* burp-only or expunge */
+ mix_expunge (stream,(options & CL_EXPUNGE) ? NIL : "",NIL);
+ mix_abort (stream);
+ stream->silent = silent; /* reset silent state */
+ }
+}
+
+
+/* MIX mail abort stream
+ * Accepts: MAIL stream
+ */
+
+void mix_abort (MAILSTREAM *stream)
+{
+ if (LOCAL) { /* only if a file is open */
+ /* close current message file if open */
+ if (LOCAL->msgfd >= 0) close (LOCAL->msgfd);
+ /* close current metadata file if open */
+ if (LOCAL->mfd >= 0) close (LOCAL->mfd);
+ if (LOCAL->index) fs_give ((void **) &LOCAL->index);
+ if (LOCAL->status) fs_give ((void **) &LOCAL->status);
+ if (LOCAL->sortcache) fs_give ((void **) &LOCAL->sortcache);
+ /* free local scratch buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* MIX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *mix_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags)
+{
+ unsigned long i,j,k;
+ int fd;
+ char *s,tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ if (length) *length = 0; /* default return */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ elt = mail_elt (stream,msgno);/* get elt */
+ /* is message in current message file? */
+ if ((LOCAL->msgfd < 0) || (elt->private.spare.data != LOCAL->curmsg)) {
+ if (LOCAL->msgfd >= 0) close (LOCAL->msgfd);
+ if ((LOCAL->msgfd = open (mix_file_data (LOCAL->buf,stream->mailbox,
+ elt->private.spare.data),
+ O_RDONLY,NIL)) < 0) return "";
+ /* got file */
+ LOCAL->curmsg = elt->private.spare.data;
+ }
+ lseek (LOCAL->msgfd,elt->private.special.offset,L_SET);
+ /* size of special data and header */
+ j = elt->private.msg.header.offset + elt->private.msg.header.text.size;
+ if (j > LOCAL->buflen) { /* is buffer big enough? */
+ /* no, make one that is */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = j) + 1);
+ }
+ /* Maybe someday validate internaldate too */
+ /* slurp special data + header, validate */
+ if ((read (LOCAL->msgfd,LOCAL->buf,j) == j) &&
+ !strncmp (LOCAL->buf,MSGTOK,MSGTSZ) &&
+ (elt->private.uid == strtoul ((char *) LOCAL->buf + MSGTSZ,&s,16)) &&
+ (*s++ == ':') && (s = strchr (s,':')) &&
+ (k = strtoul (s+1,&s,16)) && (*s++ == ':') &&
+ (s < (char *) (LOCAL->buf + elt->private.msg.header.offset))) {
+ /* won, set offset and size of message */
+ i = elt->private.msg.header.offset;
+ *length = elt->private.msg.header.text.size;
+ if (k != elt->rfc822_size) {
+ sprintf (tmp,"Inconsistency in mix message size, uid=%lx (%lu != %lu)",
+ elt->private.uid,elt->rfc822_size,k);
+ MM_LOG (tmp,WARN);
+ }
+ }
+ else { /* document the problem */
+ LOCAL->buf[100] = '\0'; /* tie off buffer at no more than 100 octets */
+ /* or at newline, whichever is first */
+ if (s = strpbrk (LOCAL->buf,"\015\012")) *s = '\0';
+ sprintf (tmp,"Error reading mix message header, uid=%lx, s=%.0lx, h=%s",
+ elt->private.uid,elt->rfc822_size,LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ *length = i = j = 0; /* default to empty */
+ }
+ LOCAL->buf[j] = '\0'; /* tie off buffer at the end */
+ return (char *) LOCAL->buf + i;
+}
+
+/* MIX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ unsigned long i;
+ FDDATA d;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mail_elt (stream,msgno);
+ /* is message in current message file? */
+ if ((LOCAL->msgfd < 0) || (elt->private.spare.data != LOCAL->curmsg)) {
+ if (LOCAL->msgfd >= 0) close (LOCAL->msgfd);
+ if ((LOCAL->msgfd = open (mix_file_data (LOCAL->buf,stream->mailbox,
+ elt->private.spare.data),
+ O_RDONLY,NIL)) < 0) return NIL;
+ /* got file */
+ LOCAL->curmsg = elt->private.spare.data;
+ }
+ /* doing non-peek fetch? */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ FILE *idxf; /* yes, process metadata/index/status */
+ FILE *statf = mix_parse (stream,&idxf,NIL,LONGT);
+ elt->seen = T; /* mark as seen */
+ MM_FLAGS (stream,elt->msgno);
+ /* update status file if possible */
+ if (statf && !stream->rdonly) {
+ elt->private.mod = LOCAL->statusseq = mix_modseq (LOCAL->statusseq);
+ mix_status_update (stream,statf,NIL);
+ }
+ if (idxf) fclose (idxf); /* release index and status file */
+ if (statf) fclose (statf);
+ }
+ d.fd = LOCAL->msgfd; /* set up file descriptor */
+ /* offset of message text */
+ d.pos = elt->private.special.offset + elt->private.msg.header.offset +
+ elt->private.msg.header.text.size;
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE; /* chunk size */
+ INIT (bs,fd_string,&d,elt->rfc822_size - elt->private.msg.header.text.size);
+ return T;
+}
+
+/* MIX mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ */
+
+void mix_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned long i,uf,ffkey;
+ long f;
+ short nf;
+ FILE *idxf;
+ FILE *statf = mix_parse (stream,&idxf,NIL,LONGT);
+ unsigned long seq = mix_modseq (LOCAL->statusseq);
+ /* find first free key */
+ for (ffkey = 0; (ffkey < NUSERFLAGS) && stream->user_flags[ffkey]; ++ffkey);
+ /* parse sequence and flags */
+ if (((flags & ST_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) &&
+ ((f = mail_parse_flags (stream,flag,&uf)) || uf)) {
+ /* alter flags */
+ for (i = 1,nf = (flags & ST_SET) ? T : NIL; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ struct { /* old flags */
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.seen = elt->seen; old.deleted = elt->deleted;
+ old.flagged = elt->flagged; old.answered = elt->answered;
+ old.draft = elt->draft; old.user_flags = elt->user_flags;
+ if (f&fSEEN) elt->seen = nf;
+ if (f&fDELETED) elt->deleted = nf;
+ if (f&fFLAGGED) elt->flagged = nf;
+ if (f&fANSWERED) elt->answered = nf;
+ if (f&fDRAFT) elt->draft = nf;
+ /* user flags */
+ if (flags & ST_SET) elt->user_flags |= uf;
+ else elt->user_flags &= ~uf;
+ if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
+ (old.flagged != elt->flagged) ||
+ (old.answered != elt->answered) || (old.draft != elt->draft) ||
+ (old.user_flags != elt->user_flags)) {
+ if (!stream->rdonly) elt->private.mod = LOCAL->statusseq = seq;
+ MM_FLAGS (stream,elt->msgno);
+ }
+ }
+ /* update status file after change */
+ if (statf && (seq == LOCAL->statusseq))
+ mix_status_update (stream,statf,NIL);
+ /* update metadata if created a keyword */
+ if ((ffkey < NUSERFLAGS) && stream->user_flags[ffkey] &&
+ !mix_meta_update (stream))
+ MM_LOG ("Error updating mix metadata after keyword creation",ERROR);
+ }
+ if (statf) fclose (statf); /* release status file if still open */
+ if (idxf) fclose (idxf); /* release index file */
+}
+
+/* MIX mail sort messages
+ * Accepts: mail stream
+ * character set
+ * search program
+ * sort program
+ * option flags
+ * Returns: vector of sorted message sequences or NIL if error
+ */
+
+unsigned long *mix_sort (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
+ SORTPGM *pgm,long flags)
+{
+ unsigned long *ret;
+ FILE *sortcache = mix_sortcache_open (stream);
+ ret = mail_sort_msgs (stream,charset,spg,pgm,flags);
+ mix_sortcache_update (stream,&sortcache);
+ return ret;
+}
+
+
+/* MIX mail thread messages
+ * Accepts: mail stream
+ * thread type
+ * character set
+ * search program
+ * option flags
+ * Returns: thread node tree or NIL if error
+ */
+
+THREADNODE *mix_thread (MAILSTREAM *stream,char *type,char *charset,
+ SEARCHPGM *spg,long flags)
+{
+ THREADNODE *ret;
+ FILE *sortcache = mix_sortcache_open (stream);
+ ret = mail_thread_msgs (stream,type,charset,spg,flags,mail_sort_msgs);
+ mix_sortcache_update (stream,&sortcache);
+ return ret;
+}
+
+/* MIX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+static int snarfing = 0; /* lock against recursive snarfing */
+
+long mix_ping (MAILSTREAM *stream)
+{
+ FILE *idxf,*statf;
+ struct stat sbuf;
+ STRING msg;
+ MESSAGECACHE *elt;
+ int mfd,ifd,sfd;
+ unsigned long i,msglen;
+ char *message,date[MAILTMPLEN],flags[MAILTMPLEN];
+ MAILSTREAM *sysibx = NIL;
+ long ret = NIL;
+ long snarfok = LONGT;
+ /* time to snarf? */
+ if (stream->inbox && !stream->rdonly && !snarfing &&
+ (time (0) >= (LOCAL->lastsnarf +
+ (time_t) mail_parameters (NIL,GET_SNARFINTERVAL,NIL)))) {
+ appenduid_t au = (appenduid_t) mail_parameters (NIL,GET_APPENDUID,NIL);
+ copyuid_t cu = (copyuid_t) mail_parameters (NIL,GET_COPYUID,NIL);
+ MM_CRITICAL (stream); /* go critical */
+ snarfing = T; /* don't recursively snarf */
+ /* disable APPENDUID/COPYUID callbacks */
+ mail_parameters (NIL,SET_APPENDUID,NIL);
+ mail_parameters (NIL,SET_COPYUID,NIL);
+ /* sizes match and anything in sysinbox? */
+ if (!stat (sysinbox (),&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFREG) &&
+ sbuf.st_size && (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) &&
+ !sysibx->rdonly && sysibx->nmsgs) {
+ /* for each message in sysibx mailbox */
+ for (i = 1; snarfok && (i <= sysibx->nmsgs); ++i)
+ if (!(elt = mail_elt (sysibx,i))->deleted &&
+ (message = mail_fetch_message (sysibx,i,&msglen,FT_PEEK)) &&
+ msglen) {
+ mail_date (date,elt); /* make internal date string */
+ /* make flag string */
+ flags[0] = flags[1] = '\0';
+ if (elt->seen) strcat (flags," \\Seen");
+ if (elt->flagged) strcat (flags," \\Flagged");
+ if (elt->answered) strcat (flags," \\Answered");
+ if (elt->draft) strcat (flags," \\Draft");
+ flags[0] = '(';
+ strcat (flags,")");
+ INIT (&msg,mail_string,message,msglen);
+ if (snarfok = mail_append_full (stream,"INBOX",flags,date,&msg)) {
+ char sequence[15];
+ sprintf (sequence,"%lu",i);
+ mail_flag (sysibx,sequence,"\\Deleted",ST_SET);
+ }
+ }
+
+ /* now expunge all those messages */
+ if (snarfok) mail_expunge (sysibx);
+ else {
+ sprintf (LOCAL->buf,"Can't copy new mail at message: %lu",i - 1);
+ MM_LOG (LOCAL->buf,WARN);
+ }
+ }
+ if (sysibx) mail_close (sysibx);
+ /* reenable APPENDUID/COPYUID */
+ mail_parameters (NIL,SET_APPENDUID,(void *) au);
+ mail_parameters (NIL,SET_COPYUID,(void *) cu);
+ snarfing = NIL; /* no longer snarfing */
+ MM_NOCRITICAL (stream); /* release critical */
+ LOCAL->lastsnarf = time (0);/* note time of last snarf */
+ }
+ /* expunging OK if global flag set */
+ if (mail_parameters (NIL,GET_EXPUNGEATPING,NIL)) LOCAL->expok = T;
+ /* process metadata/index/status */
+ if (statf = mix_parse (stream,&idxf,LONGT,
+ (LOCAL->internal ? NIL : LONGT))) {
+ fclose (statf); /* just close the status file */
+ ret = LONGT; /* declare success */
+ }
+ if (idxf) fclose (idxf); /* release index file */
+ LOCAL->expok = NIL; /* expunge no longer OK */
+ if (!ret) mix_abort (stream); /* murdelyze stream if ping fails */
+ return ret;
+}
+
+
+/* MIX mail checkpoint mailbox (burp only)
+ * Accepts: MAIL stream
+ */
+
+void mix_check (MAILSTREAM *stream)
+{
+ if (stream->rdonly) /* won't do on readonly files! */
+ MM_LOG ("Checkpoint ignored on readonly mailbox",NIL);
+ /* do burp-only expunge action */
+ if (mix_expunge (stream,"",NIL)) MM_LOG ("Check completed",(long) NIL);
+}
+
+/* MIX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL, empty string for burp only
+ * expunge options
+ * Returns: T on success, NIL if failure
+ */
+
+long mix_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ FILE *idxf = NIL;
+ FILE *statf = NIL;
+ MESSAGECACHE *elt;
+ int ifd,sfd;
+ long ret;
+ unsigned long i;
+ unsigned long nexp = 0;
+ unsigned long reclaimed = 0;
+ int burponly = (sequence && !*sequence);
+ LOCAL->expok = T; /* expunge during ping is OK */
+ if (!(ret = burponly || !sequence ||
+ ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) || stream->rdonly);
+ /* read index and open status exclusive */
+ else if (statf = mix_parse (stream,&idxf,LONGT,
+ LOCAL->internal ? NIL : LONGT)) {
+ /* expunge unless just burping */
+ if (!burponly) for (i = 1; i <= stream->nmsgs;) {
+ elt = mail_elt (stream,i);/* need to expunge this message? */
+ if (sequence ? elt->sequence : elt->deleted) {
+ ++nexp; /* yes, make it so */
+ mail_expunged (stream,i);
+ }
+ else ++i; /* otherwise advance to next message */
+ }
+
+ /* burp if can get exclusive access */
+ if (!flock (LOCAL->mfd,LOCK_EX|LOCK_NB)) {
+ void *a;
+ struct direct **names = NIL;
+ long nfiles = scandir (stream->mailbox,&names,mix_select,mix_msgfsort);
+ if (nfiles > 0) { /* if have message files */
+ MIXBURP *burp,*cur;
+ /* initialize burp list */
+ for (i = 0, burp = cur = NIL; i < nfiles; ++i) {
+ MIXBURP *nxt = (MIXBURP *) memset (fs_get (sizeof (MIXBURP)),0,
+ sizeof (MIXBURP));
+ /* another file found */
+ if (cur) cur = cur->next = nxt;
+ else cur = burp = nxt;
+ cur->name = names[i]->d_name;
+ cur->fileno = strtoul (cur->name + sizeof (MIXNAME) - 1,NIL,16);
+ cur->tail = &cur->set;
+ fs_give ((void **) &names[i]);
+ }
+ /* now load ranges */
+ for (i = 1, cur = burp; ret && (i <= stream->nmsgs); i++) {
+ /* is this message in current set? */
+ elt = mail_elt (stream,i);
+ if (cur && (elt->private.spare.data != cur->fileno)) {
+ /* restart if necessary */
+ if (elt->private.spare.data < cur->fileno) cur = burp;
+ /* hunt for appropriate mailbox */
+ while (cur && (elt->private.spare.data > cur->fileno))
+ cur = cur->next;
+ /* ought to have found it now... */
+ if (cur && (elt->private.spare.data != cur->fileno)) cur = NIL;
+ }
+ /* if found, add to set */
+ if (cur) ret = mix_addset (&cur->tail,elt->private.special.offset,
+ elt->private.msg.header.offset +
+ elt->rfc822_size);
+ else { /* uh-oh */
+ sprintf (LOCAL->buf,"Can't locate mix message file %.08lx",
+ elt->private.spare.data);
+ MM_LOG (LOCAL->buf,ERROR);
+ ret = NIL;
+ }
+ }
+ if (ret) /* if no errors, burp all files */
+ for (cur = burp; ret && cur; cur = cur->next) {
+ /* if non-empty, burp it */
+ if (cur->set.last) ret = mix_burp (stream,cur,&reclaimed);
+ /* empty, delete it unless new msg file */
+ else if (mix_file_data (LOCAL->buf,stream->mailbox,cur->fileno) &&
+ ((cur->fileno == LOCAL->newmsg) ?
+ truncate (LOCAL->buf,0) : unlink (LOCAL->buf))) {
+ sprintf (LOCAL->buf,
+ "Can't delete empty message file %.80s: %.80s",
+ cur->name,strerror (errno));
+ MM_LOG (LOCAL->buf,WARN);
+ }
+ }
+ }
+ else MM_LOG ("No mix message files found during expunge",WARN);
+ /* free directory list */
+ if (a = (void *) names) fs_give ((void **) &a);
+ }
+
+ /* either way, re-acquire shared lock */
+ if (flock (LOCAL->mfd,LOCK_SH|LOCK_NB))
+ fatal ("Unable to re-acquire metadata shared lock!");
+ /* Do this step even if ret is NIL (meaning some burp problem)! */
+ if (nexp || reclaimed) { /* rewrite index and status if changed */
+ LOCAL->indexseq = mix_modseq (LOCAL->indexseq);
+ if (mix_index_update (stream,idxf,NIL)) {
+ LOCAL->statusseq = mix_modseq (LOCAL->statusseq);
+ /* set failure if update fails */
+ ret = mix_status_update (stream,statf,NIL);
+ }
+ }
+ }
+ if (statf) fclose (statf); /* close status if still open */
+ if (idxf) fclose (idxf); /* close index if still open */
+ LOCAL->expok = NIL; /* cancel expok */
+ if (ret) { /* only if success */
+ char *s = NIL;
+ if (nexp) sprintf (s = LOCAL->buf,"Expunged %lu messages",nexp);
+ else if (reclaimed)
+ sprintf (s=LOCAL->buf,"Reclaimed %lu bytes of expunged space",reclaimed);
+ else if (!burponly)
+ s = stream->rdonly ? "Expunge ignored on readonly mailbox" :
+ "No messages deleted, so no update needed";
+ if (s) MM_LOG (s,(long) NIL);
+ }
+ return ret;
+}
+
+/* MIX test for message file name
+ * Accepts: candidate directory name
+ * Returns: T if message file name, NIL otherwise
+ *
+ * ".mix" with no suffix was used by experimental versions
+ */
+
+int mix_select (struct direct *name)
+{
+ char c,*s;
+ /* make sure name has prefix */
+ if (mix_dirfmttest (name->d_name)) {
+ for (c = *(s = name->d_name + sizeof (MIXNAME) - 1); c && isxdigit (c);
+ c = *s++);
+ if (!c) return T; /* all-hex or no suffix */
+ }
+ return NIL; /* not suffix or non-hex */
+}
+
+
+/* MIX msg file name comparision
+ * Accepts: first candidate directory entry
+ * second candidate directory entry
+ * Returns: -1 if d1 < d2, 0 if d1 == d2, 1 d1 > d2
+ */
+
+int mix_msgfsort (const void *d1,const void *d2)
+{
+ char *n1 = (*(struct direct **) d1)->d_name + sizeof (MIXNAME) - 1;
+ char *n2 = (*(struct direct **) d2)->d_name + sizeof (MIXNAME) - 1;
+ return compare_ulong (*n1 ? strtoul (n1,NIL,16) : 0,
+ *n2 ? strtoul (n2,NIL,16) : 0);
+}
+
+
+/* MIX add a range to a set
+ * Accepts: pointer to set to add
+ * start of set
+ * size of set
+ * Returns: T if success, set updated, NIL otherwise
+ */
+
+long mix_addset (SEARCHSET **set,unsigned long start,unsigned long size)
+{
+ SEARCHSET *s = *set;
+ if (start < s->last) { /* sanity check */
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Backwards-running mix index %lu < %lu",start,s->last);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* range initially empty? */
+ if (!s->last) s->first = start;
+ else if (start > s->last) /* no, start new range if can't append */
+ (*set = s = s->next = mail_newsearchset ())->first = start;
+ s->last = start + size; /* end of current range */
+ return LONGT;
+}
+
+/* MIX burp message file
+ * Accepts: MAIL stream
+ * current burp block for this message
+ * Returns: T if successful, NIL if failed
+ */
+
+static char *staterr = "Error in stat of mix message file %.80s: %.80s";
+static char *truncerr = "Error truncating mix message file %.80s: %.80s";
+
+long mix_burp (MAILSTREAM *stream,MIXBURP *burp,unsigned long *reclaimed)
+{
+ MESSAGECACHE *elt;
+ SEARCHSET *set;
+ struct stat sbuf;
+ off_t rpos,wpos;
+ size_t size,wsize,wpending,written;
+ int fd;
+ FILE *f;
+ void *s;
+ unsigned long i;
+ long ret = NIL;
+ /* build file name */
+ mix_file_data (LOCAL->buf,stream->mailbox,burp->fileno);
+ /* need to burp at start or multiple ranges? */
+ if (!burp->set.first && !burp->set.next) {
+ /* easy case, single range at start of file */
+ if (stat (LOCAL->buf,&sbuf)) {
+ sprintf (LOCAL->buf,staterr,burp->name,strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ }
+ /* is this range sane? */
+ else if (mix_burp_check (&burp->set,sbuf.st_size,LOCAL->buf)) {
+ /* if matches range then no burp needed! */
+ if (burp->set.last == sbuf.st_size) ret = LONGT;
+ /* just need to remove cruft at end */
+ else if (ret = !truncate (LOCAL->buf,burp->set.last))
+ *reclaimed += sbuf.st_size - burp->set.last;
+ else {
+ sprintf (LOCAL->buf,truncerr,burp->name,strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ }
+ }
+ }
+ /* have to do more work, get the file */
+ else if (((fd = open (LOCAL->buf,O_RDWR,NIL)) < 0) ||
+ !(f = fdopen (fd,"r+b"))) {
+ sprintf (LOCAL->buf,"Error opening mix message file %.80s: %.80s",
+ burp->name,strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ if (fd >= 0) close (fd); /* in case fdopen() failure */
+ }
+ else if (fstat (fd,&sbuf)) { /* get file size */
+ sprintf (LOCAL->buf,staterr,burp->name,strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ fclose (f);
+ }
+
+ /* only if sane */
+ else if (mix_burp_check (&burp->set,sbuf.st_size,LOCAL->buf)) {
+ /* make sure each range starts with token */
+ for (set = &burp->set; set; set = set->next)
+ if (fseek (f,set->first,SEEK_SET) ||
+ (fread (LOCAL->buf,1,MSGTSZ,f) != MSGTSZ) ||
+ strncmp (LOCAL->buf,MSGTOK,MSGTSZ)) {
+ sprintf (LOCAL->buf,"Bad message token in mix message file at %lu",
+ set->first);
+ MM_LOG (LOCAL->buf,ERROR);
+ fclose (f);
+ return NIL; /* burp fails for this file */
+ }
+ /* burp out each old message */
+ for (set = &burp->set, wpos = 0; set; set = set->next) {
+ /* move down this range */
+ for (rpos = set->first, size = set->last - set->first;
+ size; size -= wsize) {
+ if (rpos != wpos) { /* data to skip at start? */
+ /* no, slide this buffer down */
+ wsize = min (size,LOCAL->buflen);
+ /* failure is not an option here */
+ while (fseek (f,rpos,SEEK_SET) ||
+ (fread (LOCAL->buf,1,wsize,f) != wsize)) {
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+ /* nor here */
+ while (fseek (f,wpos,SEEK_SET)) {
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+ /* and especially not here */
+ for (s = LOCAL->buf, wpending = wsize; wpending; wpending -= written)
+ if (!(written = fwrite (LOCAL->buf,1,wpending,f))) {
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+ }
+ else wsize = size; /* nothing to skip, say we wrote it all */
+ rpos += wsize; wpos += wsize;
+ }
+ }
+
+ while (fflush (f)) { /* failure also not an option here... */
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+ if (ftruncate (fd,wpos)) { /* flush cruft at end of file */
+ sprintf (LOCAL->buf,truncerr,burp->name,strerror (errno));
+ MM_LOG (LOCAL->buf,WARN);
+ }
+ else *reclaimed += rpos - wpos;
+ ret = !fclose (f); /* close file */
+ /* slide down message positions in index */
+ for (i = 1,rpos = 0; i <= stream->nmsgs; ++i)
+ if ((elt = mail_elt (stream,i))->private.spare.data == burp->fileno) {
+ elt->private.special.offset = rpos;
+ rpos += elt->private.msg.header.offset + elt->rfc822_size;
+ }
+ /* debugging */
+ if (rpos != wpos) fatal ("burp size consistency check!");
+ }
+ return ret;
+}
+
+
+/* MIX burp sanity check to make sure not burping off end of file
+ * Accepts: burp set
+ * file size
+ * file name
+ * Returns: T if sane, NIL if insane
+ */
+
+long mix_burp_check (SEARCHSET *set,size_t size,char *file)
+{
+ do if (set->last > size) { /* sanity check */
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Unexpected short mix message file %.80s %lu < %lu",
+ file,size,set->last);
+ MM_LOG (tmp,ERROR);
+ return NIL; /* don't burp this file at all */
+ } while (set = set->next);
+ return LONGT;
+}
+
+/* MIX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if copy successful, else NIL
+ */
+
+long mix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ FDDATA d;
+ STRING st;
+ char tmp[2*MAILTMPLEN];
+ long ret = mix_isvalid (mailbox,LOCAL->buf);
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ MAILSTREAM *astream = NIL;
+ FILE *idxf = NIL;
+ FILE *msgf = NIL;
+ FILE *statf = NIL;
+ if (!ret) switch (errno) { /* make sure valid mailbox */
+ case NIL: /* no error in stat() */
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (tmp,"Not a MIX-format mailbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ break;
+ default: /* some stat() error */
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ break;
+ }
+ /* get sequence to copy */
+ else if (!(ret = ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))));
+ /* acquire stream to append */
+ else if (ret = ((astream = mail_open (NIL,mailbox,OP_SILENT)) &&
+ !astream->rdonly &&
+ (((MIXLOCAL *) astream->local)->expok = T) &&
+ (statf = mix_parse (astream,&idxf,LONGT,NIL))) ?
+ LONGT : NIL) {
+ int fd;
+ unsigned long i;
+ MESSAGECACHE *elt;
+ unsigned long newsize,hdrsize,size;
+ MIXLOCAL *local = (MIXLOCAL *) astream->local;
+ unsigned long seq = mix_modseq (local->metaseq);
+ /* make sure new modseq fits */
+ if (local->indexseq > seq) seq = local->indexseq + 1;
+ if (local->statusseq > seq) seq = local->statusseq + 1;
+ /* calculate size of per-message header */
+ sprintf (local->buf,MSRFMT,MSGTOK,0,0,0,0,0,0,0,'+',0,0,0);
+ hdrsize = strlen (local->buf);
+
+ MM_CRITICAL (stream); /* go critical */
+ astream->silent = T; /* no events here */
+ /* calculate size that will be added */
+ for (i = 1, newsize = 0; i <= stream->nmsgs; ++i)
+ if ((elt = mail_elt (stream,i))->sequence)
+ newsize += hdrsize + elt->rfc822_size;
+ /* open data file */
+ if (msgf = mix_data_open (astream,&fd,&size,newsize)) {
+ char *t;
+ unsigned long j,uid,uidv;
+ copyuid_t cu = (copyuid_t) mail_parameters (NIL,GET_COPYUID,NIL);
+ SEARCHSET *source = cu ? mail_newsearchset () : NIL;
+ SEARCHSET *dest = cu ? mail_newsearchset () : NIL;
+ for (i = 1,uid = uidv = 0; ret && (i <= stream->nmsgs); ++i)
+ if (((elt = mail_elt (stream,i))->sequence) && elt->rfc822_size) {
+ /* is message in current message file? */
+ if ((LOCAL->msgfd < 0) ||
+ (elt->private.spare.data != LOCAL->curmsg)) {
+ if (LOCAL->msgfd >= 0) close (LOCAL->msgfd);
+ if ((LOCAL->msgfd = open (mix_file_data (LOCAL->buf,
+ stream->mailbox,
+ elt->private.spare.data),
+ O_RDONLY,NIL)) >= 0)
+ LOCAL->curmsg = elt->private.spare.data;
+ }
+ if (LOCAL->msgfd < 0) ret = NIL;
+ else { /* got file */
+ d.fd = LOCAL->msgfd;/* set up file descriptor */
+ /* start of message */
+ d.pos = elt->private.special.offset +
+ elt->private.msg.header.offset;
+ d.chunk = LOCAL->buf;
+ d.chunksize = CHUNKSIZE;
+ INIT (&st,fd_string,&d,elt->rfc822_size);
+ /* init flag string */
+ tmp[0] = tmp[1] = '\0';
+ if (j = elt->user_flags) do
+ if ((t = stream->user_flags[find_rightmost_bit (&j)]) && *t)
+ strcat (strcat (tmp," "),t);
+ while (j);
+ if (elt->seen) strcat (tmp," \\Seen");
+ if (elt->deleted) strcat (tmp," \\Deleted");
+ if (elt->flagged) strcat (tmp," \\Flagged");
+ if (elt->answered) strcat (tmp," \\Answered");
+ if (elt->draft) strcat (tmp," \\Draft");
+ tmp[0] = '('; /* wrap list */
+ strcat (tmp,")");
+ /* if append OK, add to source set */
+ if ((ret = mix_append_msg (astream,msgf,tmp,elt,&st,dest,
+ seq)) && source)
+ mail_append_set (source,mail_uid (stream,i));
+ }
+ }
+
+ /* finish write if success */
+ if (ret && (ret = !fflush (msgf))) {
+ fclose (msgf); /* all good, close the msg file now */
+ /* write new metadata, index, and status */
+ local->metaseq = local->indexseq = local->statusseq = seq;
+ if (ret = (mix_meta_update (astream) &&
+ mix_index_update (astream,idxf,LONGT))) {
+ /* success, delete if doing a move */
+ if (options & CP_MOVE)
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ elt->deleted = T;
+ if (!stream->rdonly) elt->private.mod = LOCAL->statusseq = seq;
+ MM_FLAGS (stream,elt->msgno);
+ }
+ /* done with status file now */
+ mix_status_update (astream,statf,LONGT);
+ /* return sets if doing COPYUID */
+ if (cu) (*cu) (stream,mailbox,astream->uid_validity,source,dest);
+ source = dest = NIL; /* don't free these sets now */
+ }
+ }
+ else { /* error */
+ if (errno) { /* output error message if system call error */
+ sprintf (tmp,"Message copy failed: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ }
+ ftruncate (fd,size); /* revert file */
+ close (fd); /* make sure that fclose doesn't corrupt us */
+ fclose (msgf); /* free the stdio resources */
+ }
+ /* flush any sets remaining */
+ mail_free_searchset (&source);
+ mail_free_searchset (&dest);
+ }
+ else { /* message file open failed */
+ sprintf (tmp,"Error opening copy message file: %.80s",
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL;
+ }
+ MM_NOCRITICAL (stream);
+ }
+ else MM_LOG ("Can't open copy mailbox",ERROR);
+ if (statf) fclose (statf); /* close status if still open */
+ if (idxf) fclose (idxf); /* close index if still open */
+ /* finished with append stream */
+ if (astream) mail_close (astream);
+ return ret; /* return state */
+}
+
+/* MIX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ STRING *message;
+ char *flags,*date,tmp[MAILTMPLEN];
+ /* N.B.: can't use LOCAL->buf for tmp */
+ long ret = mix_isvalid (mailbox,tmp);
+ /* default stream to prototype */
+ if (!stream) stream = user_flags (&mixproto);
+ if (!ret) switch (errno) { /* if not valid mailbox */
+ case ENOENT: /* no such file? */
+ if (ret = compare_cstring (mailbox,"INBOX") ?
+ NIL : mix_create (NIL,"INBOX"))
+ break;
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ break;
+ default:
+ sprintf (tmp,"Not a MIX-format mailbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ break;
+ }
+
+ /* get first message */
+ if (ret && MM_APPEND (af) (stream,data,&flags,&date,&message)) {
+ MAILSTREAM *astream;
+ FILE *idxf = NIL;
+ FILE *msgf = NIL;
+ FILE *statf = NIL;
+ if (ret = ((astream = mail_open (NIL,mailbox,OP_SILENT)) &&
+ !astream->rdonly &&
+ (((MIXLOCAL *) astream->local)->expok = T) &&
+ (statf = mix_parse (astream,&idxf,LONGT,NIL))) ?
+ LONGT : NIL) {
+ int fd;
+ unsigned long size,hdrsize;
+ MESSAGECACHE elt;
+ MIXLOCAL *local = (MIXLOCAL *) astream->local;
+ unsigned long seq = mix_modseq (local->metaseq);
+ /* make sure new modseq fits */
+ if (local->indexseq > seq) seq = local->indexseq + 1;
+ if (local->statusseq > seq) seq = local->statusseq + 1;
+ /* calculate size of per-message header */
+ sprintf (local->buf,MSRFMT,MSGTOK,0,0,0,0,0,0,0,'+',0,0,0);
+ hdrsize = strlen (local->buf);
+ MM_CRITICAL (astream); /* go critical */
+ astream->silent = T; /* no events here */
+ /* open data file */
+ if (msgf = mix_data_open (astream,&fd,&size,hdrsize + SIZE (message))) {
+ appenduid_t au = (appenduid_t) mail_parameters (NIL,GET_APPENDUID,NIL);
+ SEARCHSET *dst = au ? mail_newsearchset () : NIL;
+ while (ret && message) {/* while good to go and have messages */
+ errno = NIL; /* in case one of these causes failure */
+ /* guard against zero-length */
+ if (!(ret = SIZE (message)))
+ MM_LOG ("Append of zero-length message",ERROR);
+ else if (date && !(ret = mail_parse_date (&elt,date))) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ MM_LOG (tmp,ERROR);
+ }
+ else {
+ if (!date) { /* if date not specified, use now */
+ internal_date (tmp);
+ mail_parse_date (&elt,tmp);
+ }
+ ret = mix_append_msg (astream,msgf,flags,&elt,message,dst,seq) &&
+ MM_APPEND (af) (stream,data,&flags,&date,&message);
+ }
+ }
+
+ /* finish write if success */
+ if (ret && (ret = !fflush (msgf))) {
+ fclose (msgf); /* all good, close the msg file now */
+ /* write new metadata, index, and status */
+ local->metaseq = local->indexseq = local->statusseq = seq;
+ if ((ret = (mix_meta_update (astream) &&
+ mix_index_update (astream,idxf,LONGT) &&
+ mix_status_update (astream,statf,LONGT))) && au) {
+ (*au) (mailbox,astream->uid_validity,dst);
+ dst = NIL; /* don't free this set now */
+ }
+ }
+ else { /* failure */
+ if (errno) { /* output error message if system call error */
+ sprintf (tmp,"Message append failed: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ }
+ ftruncate (fd,size); /* revert all writes to file*/
+ close (fd); /* make sure that fclose doesn't corrupt us */
+ fclose (msgf); /* free the stdio resources */
+ }
+ /* flush any set remaining */
+ mail_free_searchset (&dst);
+ }
+ else { /* message file open failed */
+ sprintf (tmp,"Error opening append message file: %.80s",
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL;
+ }
+ MM_NOCRITICAL (astream); /* release critical */
+ }
+ else MM_LOG ("Can't open append mailbox",ERROR);
+ if (statf) fclose (statf); /* close status if still open */
+ if (idxf) fclose (idxf); /* close index if still open */
+ if (astream) mail_close (astream);
+ }
+ return ret;
+}
+
+/* MIX mail append single message
+ * Accepts: MAIL stream
+ * flags for new message if non-NIL
+ * elt with source date if non-NIL
+ * stringstruct of message text
+ * searchset to place UID
+ * modseq of message
+ * Returns: T if success, NIL if failure
+ */
+
+long mix_append_msg (MAILSTREAM *stream,FILE *f,char *flags,MESSAGECACHE *delt,
+ STRING *msg,SEARCHSET *set,unsigned long seq)
+{
+ MESSAGECACHE *elt;
+ int c,cs;
+ unsigned long i,j,k,uf,hoff;
+ long sf;
+ stream->kwd_create = NIL; /* don't copy unknown keywords */
+ sf = mail_parse_flags (stream,flags,&uf);
+ /* swell the cache */
+ mail_exists (stream,++stream->nmsgs);
+ /* assign new UID from metadata */
+ (elt = mail_elt (stream,stream->nmsgs))->private.uid = ++stream->uid_last;
+ elt->private.mod = seq; /* set requested modseq in status */
+ elt->rfc822_size = SIZE (msg);/* copy message size and date to index */
+ elt->year = delt->year; elt->month = delt->month; elt->day = delt->day;
+ elt->hours = delt->hours; elt->minutes = delt->minutes;
+ elt->seconds = delt->seconds; elt->zoccident = delt->zoccident;
+ elt->zhours = delt->zhours; elt->zminutes = delt->zminutes;
+ /*
+ * Do NOT set elt->valid here! mix_status_update() uses it to determine
+ * whether a message should be marked as old.
+ */
+ if (sf&fSEEN) elt->seen = T; /* copy flags to status */
+ if (sf&fDELETED) elt->deleted = T;
+ if (sf&fFLAGGED) elt->flagged = T;
+ if (sf&fANSWERED) elt->answered = T;
+ if (sf&fDRAFT) elt->draft = T;
+ elt->user_flags |= uf;
+ /* message is in new message file */
+ elt->private.spare.data = LOCAL->newmsg;
+
+ /* offset to message internal header */
+ elt->private.special.offset = ftell (f);
+ /* build header for message */
+ fprintf (f,MSRFMT,MSGTOK,elt->private.uid,
+ elt->year + BASEYEAR,elt->month,elt->day,
+ elt->hours,elt->minutes,elt->seconds,
+ elt->zoccident ? '-' : '+',elt->zhours,elt->zminutes,
+ elt->rfc822_size);
+ /* offset to header from internal header */
+ elt->private.msg.header.offset = ftell (f) - elt->private.special.offset;
+ for (cs = 0; SIZE (msg); ) { /* copy message */
+ if (elt->private.msg.header.text.size) {
+ if (msg->cursize) /* blat entire chunk if have it */
+ for (j = msg->cursize; j; j -= k)
+ if (!(k = fwrite (msg->curpos,1,j,f))) return NIL;
+ SETPOS (msg,GETPOS (msg) + msg->cursize);
+ }
+ else { /* still searching for delimiter */
+ c = 0xff & SNX (msg); /* get source character */
+ if (putc (c,f) == EOF) return NIL;
+ switch (cs) { /* decide what to do based on state */
+ case 0: /* previous char ordinary */
+ if (c == '\015') cs = 1;/* advance if CR */
+ break;
+ case 1: /* previous CR, advance if LF */
+ cs = (c == '\012') ? 2 : 0;
+ break;
+ case 2: /* previous CRLF, advance if CR */
+ cs = (c == '\015') ? 3 : 0;
+ break;
+ case 3: /* previous CRLFCR, done if LF */
+ if (c == '\012') elt->private.msg.header.text.size =
+ elt->rfc822_size - SIZE (msg);
+ cs = 0; /* reset mechanism */
+ break;
+ }
+ }
+ }
+ /* if no delimiter, header is entire msg */
+ if (!elt->private.msg.header.text.size)
+ elt->private.msg.header.text.size = elt->rfc822_size;
+ /* add this message to set */
+ mail_append_set (set,elt->private.uid);
+ return LONGT; /* success */
+}
+
+/* MIX mail read metadata, index, and status
+ * Accepts: MAIL stream
+ * returned index file
+ * index file flags (non-NIL if want to add/remove messages)
+ * status file flags (non-NIL if want to update elt->valid and old)
+ * Returns: open status file, or NIL if failure
+ *
+ * Note that this routine can return an open index file even if it fails!
+ */
+
+static char *shortmsg =
+ "message %lu (UID=%.08lx) truncated by %lu byte(s) (%lu < %lu)";
+
+FILE *mix_parse (MAILSTREAM *stream,FILE **idxf,long iflags,long sflags)
+{
+ int fd;
+ unsigned long i;
+ char *s,*t;
+ struct stat sbuf;
+ FILE *statf = NIL;
+ short metarepairneeded = 0;
+ short indexrepairneeded = 0;
+ short silent = stream->silent;
+ *idxf = NIL; /* in case error */
+ /* readonly means no updates */
+ if (stream->rdonly) iflags = sflags = NIL;
+ /* open index file */
+ if ((fd = open (LOCAL->index,iflags ? O_RDWR : O_RDONLY,NIL)) < 0)
+ MM_LOG ("Error opening mix index file",ERROR);
+ /* acquire exclusive access and FILE */
+ else if (!flock (fd,iflags ? LOCK_EX : LOCK_SH) &&
+ !(*idxf = fdopen (fd,iflags ? "r+b" : "rb"))) {
+ MM_LOG ("Error obtaining stream on mix index file",ERROR);
+ flock (fd,LOCK_UN); /* relinquish lock */
+ close (fd);
+ }
+
+ /* slurp metadata */
+ else if (s = mix_meta_slurp (stream,&i)) {
+ unsigned long j = 0; /* non-zero if UIDVALIDITY/UIDLAST changed */
+ if (i != LOCAL->metaseq) { /* metadata changed? */
+ char *t,*k;
+ LOCAL->metaseq = i; /* note new metadata sequence */
+ while (s && *s) { /* parse entire metadata file */
+ /* locate end of line */
+ if (s = strstr (t = s,"\015\012")) {
+ *s = '\0'; /* tie off line */
+ s += 2; /* skip past CRLF */
+ switch (*t++) { /* parse line */
+ case 'V': /* UIDVALIDITY */
+ if (!isxdigit (*t) || !(i = strtoul (t,&t,16))) {
+ MM_LOG ("Error in mix metadata file UIDVALIDITY record",ERROR);
+ return NIL; /* give up */
+ }
+ if (i != stream->uid_validity) j = stream->uid_validity = i;
+ break;
+ case 'L': /* new UIDLAST */
+ if (!isxdigit (*t)) {
+ MM_LOG ("Error in mix metadata file UIDLAST record",ERROR);
+ return NIL; /* give up */
+ }
+ if ((i = strtoul (t,&t,16)) != stream->uid_last)
+ j = stream->uid_last = i;
+ break;
+ case 'N': /* new message file */
+ if (!isxdigit (*t)) {
+ MM_LOG ("Error in mix metadata file new msg record",ERROR);
+ return NIL; /* give up */
+ }
+ if ((i = strtoul (t,&t,16)) != stream->uid_last)
+ LOCAL->newmsg = i;
+ break;
+ case 'K': /* new keyword list */
+ for (i = 0; t && *t && (i < NUSERFLAGS); ++i) {
+ if (t = strchr (k = t,' ')) *t++ = '\0';
+ /* make sure keyword non-empty */
+ if (*k && (strlen (k) <= MAXUSERFLAG)) {
+ /* in case value changes (shouldn't happen) */
+ if (stream->user_flags[i] && strcmp (stream->user_flags[i],k)){
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"flag rename old=%.80s new=%.80s",
+ stream->user_flags[i],k);
+ MM_LOG (tmp,WARN);
+ fs_give ((void **) &stream->user_flags[i]);
+ }
+ if (!stream->user_flags[i]) stream->user_flags[i] = cpystr (k);
+ }
+ else break; /* empty keyword */
+ }
+ if ((i < NUSERFLAGS) && stream->user_flags[i]) {
+ MM_LOG ("Error in mix metadata file keyword record",ERROR);
+ return NIL; /* give up */
+ }
+ else if (i == NUSERFLAGS) stream->kwd_create = NIL;
+ break;
+ }
+ }
+ if (t && *t) { /* junk in line */
+ MM_LOG ("Error in mix metadata record",ERROR);
+ return NIL; /* give up */
+ }
+ }
+ }
+
+ /* get sequence */
+ if (!(i = mix_read_sequence (*idxf)) || (i < LOCAL->indexseq)) {
+ MM_LOG ("Error in mix index file sequence record",ERROR);
+ return NIL; /* give up */
+ }
+ /* sequence changed from last time? */
+ else if (j || (i > LOCAL->indexseq)) {
+ unsigned long uid,nmsgs,curfile,curfilesize,curpos;
+ char *t,*msg,tmp[MAILTMPLEN];
+ /* start with no messages */
+ curfile = curfilesize = curpos = nmsgs = 0;
+ /* update sequence iff expunging OK */
+ if (LOCAL->expok) LOCAL->indexseq = i;
+ /* get first elt */
+ while ((s = mix_read_record (*idxf,LOCAL->buf,LOCAL->buflen,"index")) &&
+ *s)
+ switch (*s) {
+ case ':': /* message record */
+ if (!(isxdigit (*++s) && (uid = strtoul (s,&t,16)))) msg = "UID";
+ else if (!((*t++ == ':') && isdigit (*t) && isdigit (t[1]) &&
+ isdigit (t[2]) && isdigit (t[3]) && isdigit (t[4]) &&
+ isdigit (t[5]) && isdigit (t[6]) && isdigit (t[7]) &&
+ isdigit (t[8]) && isdigit (t[9]) && isdigit (t[10]) &&
+ isdigit (t[11]) && isdigit (t[12]) && isdigit (t[13]) &&
+ ((t[14] == '+') || (t[14] == '-')) &&
+ isdigit (t[15]) && isdigit (t[16]) && isdigit (t[17]) &&
+ isdigit (t[18]))) msg = "internaldate";
+ else if ((*(s = t+19) != ':') || !isxdigit (*++s)) msg = "size";
+ else {
+ unsigned int y = (((*t - '0') * 1000) + ((t[1] - '0') * 100) +
+ ((t[2] - '0') * 10) + t[3] - '0') - BASEYEAR;
+ unsigned int m = ((t[4] - '0') * 10) + t[5] - '0';
+ unsigned int d = ((t[6] - '0') * 10) + t[7] - '0';
+ unsigned int hh = ((t[8] - '0') * 10) + t[9] - '0';
+ unsigned int mm = ((t[10] - '0') * 10) + t[11] - '0';
+ unsigned int ss = ((t[12] - '0') * 10) + t[13] - '0';
+ unsigned int z = (t[14] == '-') ? 1 : 0;
+ unsigned int zh = ((t[15] - '0') * 10) + t[16] - '0';
+ unsigned int zm = ((t[17] - '0') * 10) + t[18] - '0';
+ unsigned long size = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ unsigned long file = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ unsigned long pos = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ unsigned long hpos = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ unsigned long hsiz = strtoul (s,&s,16);
+ if (uid > stream->uid_last) {
+ sprintf (tmp,"mix index invalid UID (%08lx < %08lx)",
+ uid,stream->uid_last);
+ if (stream->rdonly) {
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ strcat (tmp,", repaired");
+ MM_LOG (tmp,WARN);
+ stream->uid_last = uid;
+ metarepairneeded = T;
+ }
+
+ /* ignore expansion values */
+ if (*s++ == ':') {
+ MESSAGECACHE *elt;
+ ++nmsgs; /* this is another mesage */
+ /* within current known range of messages? */
+ while (nmsgs <= stream->nmsgs) {
+ /* yes, get corresponding elt */
+ elt = mail_elt (stream,nmsgs);
+ /* existing message with matching data? */
+ if (uid == elt->private.uid) {
+ /* beware of Dracula's resurrection */
+ if (elt->private.ghost) {
+ sprintf (tmp,"mix index data unexpunged UID: %lx",
+ uid);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* also of static data changing */
+ if ((size != elt->rfc822_size) ||
+ (file != elt->private.spare.data) ||
+ (pos != elt->private.special.offset) ||
+ (hpos != elt->private.msg.header.offset) ||
+ (hsiz != elt->private.msg.header.text.size) ||
+ (y != elt->year) || (m != elt->month) ||
+ (d != elt->day) || (hh != elt->hours) ||
+ (mm != elt->minutes) || (ss != elt->seconds) ||
+ (z != elt->zoccident) || (zh != elt->zhours) ||
+ (zm != elt->zminutes)) {
+ sprintf (tmp,"mix index data mismatch: %lx",uid);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ break;
+ }
+ /* existing msg with lower UID is expunged */
+ else if (uid > elt->private.uid) {
+ if (LOCAL->expok) mail_expunged (stream,nmsgs);
+ else {/* message expunged, but not yet for us */
+ ++nmsgs;
+ elt->private.ghost = T;
+ }
+ }
+ else { /* unexpected message record */
+ sprintf (tmp,"mix index UID mismatch (%lx < %lx)",
+ uid,elt->private.uid);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ }
+
+ /* time to create a new message? */
+ if (nmsgs > stream->nmsgs) {
+ /* defer announcing until later */
+ stream->silent = T;
+ mail_exists (stream,nmsgs);
+ stream->silent = silent;
+ (elt = mail_elt (stream,nmsgs))->recent = T;
+ elt->private.uid = uid; elt->rfc822_size = size;
+ elt->private.spare.data = file;
+ elt->private.special.offset = pos;
+ elt->private.msg.header.offset = hpos;
+ elt->private.msg.header.text.size = hsiz;
+ elt->year = y; elt->month = m; elt->day = d;
+ elt->hours = hh; elt->minutes = mm;
+ elt->seconds = ss; elt->zoccident = z;
+ elt->zhours = zh; elt->zminutes = zm;
+ /* message in same file? */
+ if (curfile == file) {
+ if (pos < curpos) {
+ MESSAGECACHE *plt = mail_elt (stream,elt->msgno-1);
+ /* uh-oh, calculate delta? */
+ i = curpos - pos;
+ sprintf (tmp,shortmsg,plt->msgno,plt->private.uid,
+ i,pos,curpos);
+ /* possible to fix? */
+ if (!stream->rdonly && LOCAL->expok &&
+ (i < plt->rfc822_size)) {
+ plt->rfc822_size -= i;
+ if (plt->rfc822_size <
+ plt->private.msg.header.text.size)
+ plt->private.msg.header.text.size =
+ plt->rfc822_size;
+ strcat (tmp,", repaired");
+ indexrepairneeded = T;
+ }
+ MM_LOG (tmp,WARN);
+ }
+ }
+ else { /* new file, restart */
+ if (stat (mix_file_data (LOCAL->buf,stream->mailbox,
+ curfile = file),&sbuf)) {
+ sprintf (tmp,"Missing mix data file: %.500s",
+ LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ curfile = file;
+ curfilesize = sbuf.st_size;
+ }
+
+ /* position of message in file */
+ curpos = pos + elt->private.msg.header.offset +
+ elt->rfc822_size;
+ /* short file? */
+ if (curfilesize < curpos) {
+ /* uh-oh, calculate delta? */
+ i = curpos - curfilesize;
+ sprintf (tmp,shortmsg,elt->msgno,elt->private.uid,
+ i,curfilesize,curpos);
+ /* possible to fix? */
+ if (!stream->rdonly && LOCAL->expok &&
+ (i < elt->rfc822_size)) {
+ elt->rfc822_size -= i;
+ if (elt->rfc822_size <
+ elt->private.msg.header.text.size)
+ elt->private.msg.header.text.size =
+ elt->rfc822_size;
+ strcat (tmp,", repaired");
+ indexrepairneeded = T;
+ }
+ MM_LOG (tmp,WARN);
+ }
+ }
+ break;
+ }
+ else msg = "expansion";
+ }
+ else msg = "header size";
+ }
+ else msg = "header position";
+ }
+ else msg = "message position";
+ }
+ else msg = "file#";
+ }
+ sprintf (tmp,"Error in %s in mix index file: %.500s",msg,s);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Unknown record in mix index file: %.500s",s);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ if (!s) return NIL; /* barfage from mix_read_record() */
+ /* expunge trailing messages not in index */
+ if (LOCAL->expok) while (nmsgs < stream->nmsgs)
+ mail_expunged (stream,stream->nmsgs);
+ }
+
+ /* repair metadata and index if needed */
+ if ((metarepairneeded ? mix_meta_update (stream) : T) &&
+ (indexrepairneeded ? mix_index_update (stream,*idxf,NIL) : T)) {
+ MESSAGECACHE *elt;
+ int fd;
+ unsigned long uid,uf,sf,mod;
+ char *s;
+ int updatep = NIL;
+ /* open status file */
+ if ((fd = open (LOCAL->status,
+ stream->rdonly ? O_RDONLY : O_RDWR,NIL)) < 0)
+ MM_LOG ("Error opening mix status file",ERROR);
+ /* acquire exclusive access and FILE */
+ else if (!flock (fd,stream->rdonly ? LOCK_SH : LOCK_EX) &&
+ !(statf = fdopen (fd,stream->rdonly ? "rb" : "r+b"))) {
+ MM_LOG ("Error obtaining stream on mix status file",ERROR);
+ flock (fd,LOCK_UN); /* relinquish lock */
+ close (fd);
+ }
+ /* get sequence */
+ else if (!(i = mix_read_sequence (statf)) ||
+ ((i < LOCAL->statusseq) && stream->nmsgs && (i != 1))) {
+ sprintf (LOCAL->buf,
+ "Error in mix status sequence record, i=%lx, seq=%lx",
+ i,LOCAL->statusseq);
+ MM_LOG (LOCAL->buf,ERROR);
+ }
+ /* sequence changed from last time? */
+ else if (i != LOCAL->statusseq) {
+ /* update sequence, get first elt */
+ if (i > LOCAL->statusseq) LOCAL->statusseq = i;
+ if (stream->nmsgs) {
+ elt = mail_elt (stream,i = 1);
+
+ /* read message records */
+ while ((t = s = mix_read_record (statf,LOCAL->buf,LOCAL->buflen,
+ "status")) && *s && (*s++ == ':') &&
+ isxdigit (*s)) {
+ uid = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ uf = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ sf = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ mod = strtoul (s,&s,16);
+ /* ignore expansion values */
+ if (*s++ == ':') {
+ /* need to move ahead to next elt? */
+ while ((uid > elt->private.uid) && (i < stream->nmsgs))
+ elt = mail_elt (stream,++i);
+ /* update elt if altered */
+ if ((uid == elt->private.uid) &&
+ (!elt->valid || (mod != elt->private.mod))) {
+ elt->user_flags = uf;
+ elt->private.mod = mod;
+ elt->seen = (sf & fSEEN) ? T : NIL;
+ elt->deleted = (sf & fDELETED) ? T : NIL;
+ elt->flagged = (sf & fFLAGGED) ? T : NIL;
+ elt->answered = (sf & fANSWERED) ? T : NIL;
+ elt->draft = (sf & fDRAFT) ? T : NIL;
+ /* announce if altered existing message */
+ if (elt->valid) MM_FLAGS (stream,elt->msgno);
+ /* first time, is old message? */
+ else if (sf & fOLD) {
+ /* yes, clear recent and set valid */
+ elt->recent = NIL;
+ elt->valid = T;
+ }
+ /* recent, allowed to update its status? */
+ else if (sflags) {
+ /* yes, set valid and check in status */
+ elt->valid = T;
+ elt->private.mod = mix_modseq (elt->private.mod);
+ updatep = T;
+ }
+ /* leave valid unset and recent if sflags not set */
+ }
+ continue; /* everything looks good */
+ }
+ }
+ }
+ }
+ break; /* error somewhere */
+ }
+
+ if (t && *t) { /* non-null means bogus record */
+ char msg[MAILTMPLEN];
+ sprintf (msg,"Error in mix status file message record%s: %.80s",
+ stream->rdonly ? "" : ", fixing",t);
+ MM_LOG (msg,WARN);
+ /* update it if not readonly */
+ if (!stream->rdonly) updatep = T;
+ }
+ if (updatep) { /* need to update? */
+ LOCAL->statusseq = mix_modseq (LOCAL->statusseq);
+ mix_status_update (stream,statf,LONGT);
+ }
+ }
+ }
+ }
+ }
+ if (statf) { /* still happy? */
+ unsigned long j;
+ stream->silent = silent; /* now notify upper level */
+ mail_exists (stream,stream->nmsgs);
+ for (i = 1, j = 0; i <= stream->nmsgs; ++i)
+ if (mail_elt (stream,i)->recent) ++j;
+ mail_recent (stream,j);
+ }
+ return statf;
+}
+
+/* MIX metadata file routines */
+
+/* MIX read metadata
+ * Accepts: MAIL stream
+ * return pointer for modseq
+ * Returns: pointer to metadata after modseq or NIL if failure
+ */
+
+char *mix_meta_slurp (MAILSTREAM *stream,unsigned long *seq)
+{
+ struct stat sbuf;
+ char *s;
+ char *ret = NIL;
+ if (fstat (LOCAL->mfd,&sbuf))
+ MM_LOG ("Error obtaining size of mix metatdata file",ERROR);
+ if (sbuf.st_size > LOCAL->buflen) {
+ /* should be just a few dozen bytes */
+ if (sbuf.st_size > METAMAX) fatal ("absurd mix metadata file size");
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = sbuf.st_size) + 1);
+ }
+ /* read current metadata file */
+ LOCAL->buf[sbuf.st_size] = '\0';
+ if (lseek (LOCAL->mfd,0,L_SET) ||
+ (read (LOCAL->mfd,s = LOCAL->buf,sbuf.st_size) != sbuf.st_size))
+ MM_LOG ("Error reading mix metadata file",ERROR);
+ else if ((*s != 'S') || !isxdigit (s[1]) ||
+ ((*seq = strtoul (s+1,&s,16)) < LOCAL->metaseq) ||
+ (*s++ != '\015') || (*s++ != '\012'))
+ MM_LOG ("Error in mix metadata file sequence record",ERROR);
+ else ret = s;
+ return ret;
+}
+
+/* MIX update metadata
+ * Accepts: MAIL stream
+ * Returns: T on success, NIL if error
+ *
+ * Index MUST be locked!!
+ */
+
+long mix_meta_update (MAILSTREAM *stream)
+{
+ long ret;
+ /* do nothing if stream readonly */
+ if (stream->rdonly) ret = LONGT;
+ else {
+ unsigned char c,*s,*ss,*t;
+ unsigned long i;
+ /* The worst-case metadata is limited to:
+ * 4 * (1 + 8 + 2) + (NUSERFLAGS * (MAXUSERFLAG + 1))
+ * which comes out to 1994 octets. This is much smaller than the normal
+ * CHUNKSIZE definition of 64K, and CHUNKSIZE is the smallest size of
+ * LOCAL->buf.
+ *
+ * If more stuff gets added to the metadata, or if you change the value
+ * of NUSERFLAGS, MAXUSERFLAG or CHUNKSIZE, be sure to recalculate the
+ * above assertation.
+ */
+ sprintf (LOCAL->buf,SEQFMT,LOCAL->metaseq = mix_modseq (LOCAL->metaseq));
+ sprintf (LOCAL->buf + strlen (LOCAL->buf),MTAFMT,
+ stream->uid_validity,stream->uid_last,LOCAL->newmsg);
+ for (i = 0, c = 'K', s = ss = LOCAL->buf + strlen (LOCAL->buf);
+ (i < NUSERFLAGS) && (t = stream->user_flags[i]); ++i) {
+ if (!*t) fatal ("impossible empty keyword");
+ *s++ = c; /* write delimiter */
+ while (*t) *s++ = *t++; /* write keyword */
+ c = ' '; /* delimiter is now space */
+ }
+ if (s != ss) { /* tie off keywords line */
+ *s++ = '\015'; *s++ = '\012';
+ }
+ /* calculate length of metadata */
+ if ((i = s - LOCAL->buf) > LOCAL->buflen)
+ fatal ("impossible buffer overflow");
+ lseek (LOCAL->mfd,0,L_SET); /* rewind file */
+ /* write new metadata */
+ ret = (write (LOCAL->mfd,LOCAL->buf,i) == i) ? LONGT : NIL;
+ ftruncate (LOCAL->mfd,i); /* and tie off at that point */
+ }
+ return ret;
+}
+
+/* MIX index file routines */
+
+
+/* MIX update index
+ * Accepts: MAIL stream
+ * open FILE
+ * expansion check flag
+ * Returns: T on success, NIL if error
+ */
+
+long mix_index_update (MAILSTREAM *stream,FILE *idxf,long flag)
+{
+ unsigned long i;
+ long ret = LONGT;
+ if (!stream->rdonly) { /* do nothing if stream readonly */
+ if (flag) { /* need to do expansion check? */
+ char tmp[MAILTMPLEN];
+ size_t size;
+ struct stat sbuf;
+ /* calculate file size we need */
+ for (i = 1, size = 0; i <= stream->nmsgs; ++i)
+ if (!mail_elt (stream,i)->private.ghost) ++size;
+ if (size) { /* Winston Smith's first dairy entry */
+ sprintf (tmp,IXRFMT,0,14,4,4,13,0,0,'+',0,0,0,0,0,0,0);
+ size *= strlen (tmp);
+ }
+ /* calculate file size we need */
+ sprintf (tmp,SEQFMT,LOCAL->indexseq);
+ size += strlen (tmp);
+ /* get current file size */
+ if (fstat (fileno (idxf),&sbuf)) {
+ MM_LOG ("Error getting size of mix index file",ERROR);
+ ret = NIL;
+ }
+ /* need to write additional space? */
+ else if (sbuf.st_size < size) {
+ void *buf = fs_get (size -= sbuf.st_size);
+ memset (buf,0,size);
+ if (fseek (idxf,0,SEEK_END) || (fwrite (buf,1,size,idxf) != size) ||
+ fflush (idxf)) {
+ fseek (idxf,sbuf.st_size,SEEK_SET);
+ ftruncate (fileno (idxf),sbuf.st_size);
+ MM_LOG ("Error extending mix index file",ERROR);
+ ret = NIL;
+ }
+ fs_give ((void **) &buf);
+ }
+ }
+
+ if (ret) { /* if still good to go */
+ rewind (idxf); /* let's start at the very beginning */
+ /* write modseq first */
+ fprintf (idxf,SEQFMT,LOCAL->indexseq);
+ /* then write all messages */
+ for (i = 1; ret && (i <= stream->nmsgs); i++) {
+ MESSAGECACHE *elt = mail_elt (stream,i);
+ if (!elt->private.ghost)/* only write living messages */
+ fprintf (idxf,IXRFMT,elt->private.uid,
+ elt->year + BASEYEAR,elt->month,elt->day,
+ elt->hours,elt->minutes,elt->seconds,
+ elt->zoccident ? '-' : '+',elt->zhours,elt->zminutes,
+ elt->rfc822_size,elt->private.spare.data,
+ elt->private.special.offset,
+ elt->private.msg.header.offset,
+ elt->private.msg.header.text.size);
+ if (ferror (idxf)) {
+ MM_LOG ("Error updating mix index file",ERROR);
+ ret = NIL;
+ }
+ }
+ if (fflush (idxf)) {
+ MM_LOG ("Error flushing mix index file",ERROR);
+ ret = NIL;
+ }
+ if (ret) ftruncate (fileno (idxf),ftell (idxf));
+ }
+ }
+ return ret;
+}
+
+/* MIX status file routines */
+
+
+/* MIX update status
+ * Accepts: MAIL stream
+ * pointer to open FILE
+ * expansion check flag
+ * Returns: T on success, NIL if error
+ */
+
+long mix_status_update (MAILSTREAM *stream,FILE *statf,long flag)
+{
+ unsigned long i;
+ char tmp[MAILTMPLEN];
+ long ret = LONGT;
+ if (!stream->rdonly) { /* do nothing if stream readonly */
+ if (flag) { /* need to do expansion check? */
+ char tmp[MAILTMPLEN];
+ size_t size;
+ struct stat sbuf;
+ /* calculate file size we need */
+ for (i = 1, size = 0; i <= stream->nmsgs; ++i)
+ if (!mail_elt (stream,i)->private.ghost) ++size;
+ if (size) { /* number of living messages */
+ sprintf (tmp,STRFMT,0,0,0,0);
+ size *= strlen (tmp);
+ }
+ sprintf (tmp,SEQFMT,LOCAL->statusseq);
+ size += strlen (tmp);
+ /* get current file size */
+ if (fstat (fileno (statf),&sbuf)) {
+ MM_LOG ("Error getting size of mix status file",ERROR);
+ ret = NIL;
+ }
+ /* need to write additional space? */
+ else if (sbuf.st_size < size) {
+ void *buf = fs_get (size -= sbuf.st_size);
+ memset (buf,0,size);
+ if (fseek (statf,0,SEEK_END) || (fwrite (buf,1,size,statf) != size) ||
+ fflush (statf)) {
+ fseek (statf,sbuf.st_size,SEEK_SET);
+ ftruncate (fileno (statf),sbuf.st_size);
+ MM_LOG ("Error extending mix status file",ERROR);
+ ret = NIL;
+ }
+ fs_give ((void **) &buf);
+ }
+ }
+
+ if (ret) { /* if still good to go */
+ rewind (statf); /* let's start at the very beginning */
+ /* write sequence */
+ fprintf (statf,SEQFMT,LOCAL->statusseq);
+ /* write message status records */
+ for (i = 1; ret && (i <= stream->nmsgs); ++i) {
+ MESSAGECACHE *elt = mail_elt (stream,i);
+ /* make sure all messages have a modseq */
+ if (!elt->private.mod) elt->private.mod = LOCAL->statusseq;
+ if (!elt->private.ghost)/* only write living messages */
+ fprintf (statf,STRFMT,elt->private.uid,elt->user_flags,
+ (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft) + (elt->valid ? fOLD : NIL),
+ elt->private.mod);
+ if (ferror (statf)) {
+ sprintf (tmp,"Error updating mix status file: %.80s",
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL;
+ }
+ }
+ if (ret && fflush (statf)) {
+ MM_LOG ("Error flushing mix status file",ERROR);
+ ret = NIL;
+ }
+ if (ret) ftruncate (fileno (statf),ftell (statf));
+ }
+ }
+ return ret;
+}
+
+/* MIX data file routines */
+
+
+/* MIX open data file
+ * Accepts: MAIL stream
+ * pointer to returned fd if success
+ * pointer to returned size if success
+ * size of new data to be added
+ * Returns: open FILE, or NIL if failure
+ *
+ * The curend test assumes that the last message of the mailbox is the furthest
+ * point that the current data file extends, and thus that is all that needs to
+ * be tested for short file prevention.
+ */
+
+FILE *mix_data_open (MAILSTREAM *stream,int *fd,long *size,
+ unsigned long newsize)
+{
+ FILE *msgf = NIL;
+ struct stat sbuf;
+ MESSAGECACHE *elt = stream->nmsgs ? mail_elt (stream,stream->nmsgs) : NIL;
+ unsigned long curend = (elt && (elt->private.spare.data == LOCAL->newmsg)) ?
+ elt->private.special.offset + elt->private.msg.header.offset +
+ elt->rfc822_size : 0;
+ /* allow create if curend 0 */
+ if ((*fd = open (mix_file_data (LOCAL->buf,stream->mailbox,LOCAL->newmsg),
+ O_RDWR | (curend ? NIL : O_CREAT),NIL)) >= 0) {
+ fstat (*fd,&sbuf); /* get current file size */
+ /* can we use this file? */
+ if ((curend <= sbuf.st_size) &&
+ (!sbuf.st_size || ((sbuf.st_size + newsize) <= MIXDATAROLL)))
+ *size = sbuf.st_size; /* yes, return current size */
+ else { /* short file or becoming too long */
+ if (curend > sbuf.st_size) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"short mix message file %.08lx (%ld > %ld), rolling",
+ LOCAL->newmsg,curend,sbuf.st_size);
+ MM_LOG (tmp,WARN); /* shouldn't happen */
+ }
+ close (*fd); /* roll to a new file */
+ while ((*fd = open (mix_file_data
+ (LOCAL->buf,stream->mailbox,
+ LOCAL->newmsg = mix_modseq (LOCAL->newmsg)),
+ O_RDWR | O_CREAT | O_EXCL,sbuf.st_mode)) < 0);
+ *size = 0; /* brand new file */
+ fchmod (*fd,sbuf.st_mode);/* with same mode as previous file */
+ }
+ }
+ if (*fd >= 0) { /* have a data file? */
+ /* yes, get stdio and set position */
+ if (msgf = fdopen (*fd,"r+b")) fseek (msgf,*size,SEEK_SET);
+ else close (*fd); /* fdopen() failed? */
+ }
+ return msgf; /* return results */
+}
+
+/* MIX open sortcache
+ * Accepts: MAIL stream
+ * Returns: open FILE, or NIL if failure or could only get readonly sortcache
+ */
+
+FILE *mix_sortcache_open (MAILSTREAM *stream)
+{
+ int fd,refwd;
+ unsigned long i,uid,sentdate,fromlen,tolen,cclen,subjlen,msgidlen,reflen;
+ char *s,*t,*msg,tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ SORTCACHE *sc;
+ STRINGLIST *sl;
+ struct stat sbuf;
+ int rdonly = NIL;
+ FILE *srtcf = NIL;
+ mailcache_t mc = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL);
+ fstat (LOCAL->mfd,&sbuf);
+ if (!stream->nmsgs); /* do nothing if mailbox empty */
+ /* open sortcache file */
+ else if (((fd = open (LOCAL->sortcache,O_RDWR|O_CREAT,sbuf.st_mode)) < 0) &&
+ !(rdonly = ((fd = open (LOCAL->sortcache,O_RDONLY,NIL)) >= 0)))
+ MM_LOG ("Error opening mix sortcache file",WARN);
+ /* acquire lock and FILE */
+ else if (!flock (fd,rdonly ? LOCK_SH : LOCK_EX) &&
+ !(srtcf = fdopen (fd,rdonly ? "rb" : "r+b"))) {
+ MM_LOG ("Error obtaining stream on mix sortcache file",WARN);
+ flock (fd,LOCK_UN); /* relinquish lock */
+ close (fd);
+ }
+ else if (!(i = mix_read_sequence (srtcf)) || (i < LOCAL->sortcacheseq))
+ MM_LOG ("Error in mix sortcache file sequence record",WARN);
+ /* sequence changed from last time? */
+ else if (i > LOCAL->sortcacheseq) {
+ LOCAL->sortcacheseq = i; /* update sequence */
+ while ((s = t = mix_read_record (srtcf,LOCAL->buf,LOCAL->buflen,
+ "sortcache")) && *s &&
+ (msg = "uid") && (*s++ == ':') && isxdigit (*s)) {
+ uid = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ sentdate = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ fromlen = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ tolen = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ cclen = strtoul (s,&s,16);
+ if ((*s++ == ':') && ((*s == 'R') || (*s == ' ')) &&
+ isxdigit (s[1])) {
+ refwd = (*s++ == 'R') ? T : NIL;
+ subjlen = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ msgidlen = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ reflen = strtoul (s,&s,16);
+ /* ignore expansion values */
+ if (*s++ == ':') {
+
+ if (i = mail_msgno (stream,uid)) {
+ sc = (SORTCACHE *) (*mc) (stream,i,CH_SORTCACHE);
+ sc->size = (elt = mail_elt (stream,i))->rfc822_size;
+ sc->date = sentdate;
+ sc->arrival = elt->day ? mail_longdate (elt) : 1;
+ if (refwd) sc->refwd = T;
+ if (fromlen) {
+ if (sc->from) fseek (srtcf,fromlen + 2,SEEK_CUR);
+ else if ((getc (srtcf) != 'F') ||
+ (fread (sc->from = (char *) fs_get(fromlen),
+ 1,fromlen-1,srtcf) != (fromlen-1))||
+ (sc->from[fromlen-1] = '\0') ||
+ (getc (srtcf) != '\015') ||
+ (getc (srtcf) != '\012')) {
+ msg = "from data";
+ break;
+ }
+ }
+ if (tolen) {
+ if (sc->to) fseek (srtcf,tolen + 2,SEEK_CUR);
+ else if ((getc (srtcf) != 'T') ||
+ (fread (sc->to = (char *) fs_get (tolen),
+ 1,tolen-1,srtcf) != (tolen - 1)) ||
+ (sc->to[tolen-1] = '\0') ||
+ (getc (srtcf) != '\015') ||
+ (getc (srtcf) != '\012')) {
+ msg = "to data";
+ break;
+ }
+ }
+ if (cclen) {
+ if (sc->cc) fseek (srtcf,cclen + 2,SEEK_CUR);
+ else if ((getc (srtcf) != 'C') ||
+ (fread (sc->cc = (char *) fs_get (cclen),
+ 1,cclen-1,srtcf) != (cclen - 1)) ||
+ (sc->cc[cclen-1] = '\0') ||
+ (getc (srtcf) != '\015') ||
+ (getc (srtcf) != '\012')) {
+ msg = "cc data";
+ break;
+ }
+ }
+ if (subjlen) {
+ if (sc->subject) fseek (srtcf,subjlen + 2,SEEK_CUR);
+ else if ((getc (srtcf) != 'S') ||
+ (fread (sc->subject =
+ (char *) fs_get (subjlen),1,
+ subjlen-1,srtcf) != (subjlen-1))||
+ (sc->subject[subjlen-1] = '\0') ||
+ (getc (srtcf) != '\015') ||
+ (getc (srtcf) != '\012')) {
+ msg = "subject data";
+ break;
+ }
+ }
+
+ if (msgidlen) {
+ if (sc->message_id)
+ fseek (srtcf,msgidlen + 2,SEEK_CUR);
+ else if ((getc (srtcf) != 'M') ||
+ (fread (sc->message_id =
+ (char *) fs_get (msgidlen),1,
+ msgidlen-1,srtcf) != (msgidlen-1))||
+ (sc->message_id[msgidlen-1] = '\0') ||
+ (getc (srtcf) != '\015') ||
+ (getc (srtcf) != '\012')) {
+ msg = "message-id data";
+ break;
+ }
+ }
+ if (reflen) {
+ if (sc->references) fseek(srtcf,reflen + 2,SEEK_CUR);
+ /* make sure it fits */
+ else {
+ if (reflen >= LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *)
+ fs_get ((LOCAL->buflen = reflen) + 1);
+ }
+ if ((getc (srtcf) != 'R') ||
+ (fread (LOCAL->buf,1,reflen-1,srtcf) !=
+ (reflen - 1)) ||
+ (LOCAL->buf[reflen-1] = '\0') ||
+ (getc (srtcf) != '\015') ||
+ (getc (srtcf) != '\012')) {
+ msg = "references data";
+ break;
+ }
+ for (s = LOCAL->buf,sl = NIL,
+ sc->references = mail_newstringlist ();
+ s && *s; s += i + 1) {
+ if ((i = strtoul (s,&s,16)) && (*s++ == ':') &&
+ (s[i] == ':')) {
+ if (sl) sl = sl->next = mail_newstringlist();
+ else sl = sc->references;
+ s[i] = '\0';
+ sl->text.data = cpystr (s);
+ sl->text.size = i;
+ }
+ else s = NIL;
+ }
+ if (!s || *s ||
+ (s != ((char *) LOCAL->buf + reflen - 1))) {
+ msg = "references length consistency check";
+ break;
+ }
+ }
+ }
+ }
+
+ /* UID not found, ignore this message */
+ else fseek (srtcf,((fromlen ? fromlen + 2 : 0) +
+ (tolen ? tolen + 2 : 0) +
+ (cclen ? cclen + 2 : 0) +
+ (subjlen ? subjlen + 2 : 0) +
+ (msgidlen ? msgidlen + 2 : 0) +
+ (reflen ? reflen + 2 : 0)),
+ SEEK_CUR);
+ continue;
+ }
+ else msg = "expansion";
+ }
+ else msg = "references";
+ }
+ else msg = "message-id";
+ }
+ else msg = "subject";
+ }
+ else msg = "cc";
+ }
+ else msg = "to";
+ }
+ else msg = "from";
+ }
+ else msg = "sentdate";
+ break; /* error somewhere */
+ }
+ if (!t || *t) { /* error detected? */
+ if (t) { /* non-null means bogus record */
+ sprintf (tmp,"Error in %s in mix sortcache record: %.500s",msg,t);
+ MM_LOG (tmp,WARN);
+ }
+ fclose (srtcf); /* either way, must punt */
+ srtcf = NIL;
+ }
+ }
+ if (rdonly && srtcf) { /* can't update if readonly */
+ unlink (LOCAL->sortcache); /* try deleting it */
+ fclose (srtcf); /* so close it and return as if error */
+ srtcf = NIL;
+ }
+ else fchmod (fd,sbuf.st_mode);
+ return srtcf;
+}
+
+/* MIX update and close sortcache
+ * Accepts: MAIL stream
+ * pointer to open FILE (if FILE is NIL, do nothing)
+ * Returns: T on success, NIL on error
+ */
+
+long mix_sortcache_update (MAILSTREAM *stream,FILE **sortcache)
+{
+ FILE *f = *sortcache;
+ long ret = LONGT;
+ if (f) { /* ignore if no file */
+ unsigned long i,j;
+ mailcache_t mc = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL);
+ for (i = 1; (i <= stream->nmsgs) &&
+ !((SORTCACHE *) (*mc) (stream,i,CH_SORTCACHE))->dirty; ++i);
+ if (i <= stream->nmsgs) { /* only update if some entry is dirty */
+ rewind (f); /* let's start at the very beginning */
+ /* write sequence */
+ fprintf (f,SEQFMT,LOCAL->sortcacheseq = mix_modseq(LOCAL->sortcacheseq));
+ for (i = 1; ret && (i <= stream->nmsgs); ++i) {
+ MESSAGECACHE *elt = mail_elt (stream,i);
+ SORTCACHE *s = (SORTCACHE *) (*mc) (stream,i,CH_SORTCACHE);
+ STRINGLIST *sl;
+ s->dirty = NIL; /* no longer dirty */
+ if (sl = s->references) /* count length of references */
+ for (j = 1; sl && sl->text.data; sl = sl->next)
+ j += 10 + sl->text.size;
+ else j = 0; /* no references yet */
+ fprintf (f,SCRFMT,elt->private.uid,s->date,
+ s->from ? strlen (s->from) + 1 : 0,
+ s->to ? strlen (s->to) + 1 : 0,s->cc ? strlen (s->cc) + 1 : 0,
+ s->refwd ? 'R' : ' ',s->subject ? strlen (s->subject) + 1: 0,
+ s->message_id ? strlen (s->message_id) + 1 : 0,j);
+ if (s->from) fprintf (f,"F%s\015\012",s->from);
+ if (s->to) fprintf (f,"T%s\015\012",s->to);
+ if (s->cc) fprintf (f,"C%s\015\012",s->cc);
+ if (s->subject) fprintf (f,"S%s\015\012",s->subject);
+ if (s->message_id) fprintf (f,"M%s\015\012",s->message_id);
+ if (j) { /* any references to write? */
+ fputc ('R',f); /* yes, do so */
+ for (sl = s->references; sl && sl->text.data; sl = sl->next)
+ fprintf (f,"%08lx:%s:",sl->text.size,sl->text.data);
+ fputs ("\015\012",f);
+ }
+ if (ferror (f)) {
+ MM_LOG ("Error updating mix sortcache file",WARN);
+ ret = NIL;
+ }
+ }
+ if (ret && fflush (f)) {
+ MM_LOG ("Error flushing mix sortcache file",WARN);
+ ret = NIL;
+ }
+ if (ret) ftruncate (fileno (f),ftell (f));
+ }
+ if (fclose (f)) {
+ MM_LOG ("Error closing mix sortcache file",WARN);
+ ret = NIL;
+ }
+ }
+ return ret;
+}
+
+/* MIX generic file routines */
+
+/* MIX read record
+ * Accepts: open FILE
+ * buffer
+ * buffer length
+ * record type
+ * Returns: buffer if success, else NIL (zero-length buffer means EOF)
+ */
+
+char *mix_read_record (FILE *f,char *buf,unsigned long buflen,char *type)
+{
+ char *s,tmp[MAILTMPLEN];
+ /* ensure string tied off */
+ buf[buflen-2] = buf[buflen-1] = '\0';
+ while (fgets (buf,buflen-1,f)) {
+ if (s = strchr (buf,'\012')) {
+ if ((s != buf) && (s[-1] == '\015')) --s;
+ *s = '\0'; /* tie off buffer */
+ if (s != buf) return buf; /* return if non-empty buffer */
+ sprintf (tmp,"Empty mix %s record",type);
+ MM_LOG (tmp,WARN);
+ }
+ else if (buf[buflen-2]) { /* overlong record is bad news */
+ sprintf (tmp,"Oversize mix %s record: %.512s",type,buf);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ else {
+ sprintf (tmp,"Truncated mix %s record: %.512s",type,buf);
+ MM_LOG (tmp,WARN);
+ return buf; /* pass to caller anyway */
+ }
+ }
+ buf[0] = '\0'; /* return empty buffer on EOF */
+ return buf;
+}
+
+/* MIX read sequence record
+ * Accepts: open FILE
+ * Returns: sequence value, or NIL if failure
+ */
+
+unsigned long mix_read_sequence (FILE *f)
+{
+ unsigned long ret;
+ char *s,tmp[MAILTMPLEN];
+ if (!mix_read_record (f,tmp,MAILTMPLEN-1,"sequence")) return NIL;
+ switch (tmp[0]) { /* examine record */
+ case '\0': /* end of file */
+ ret = 1; /* start a new sequence regime */
+ break;
+ case 'S': /* sequence record */
+ if (isxdigit (tmp[1])) { /* must be followed by hex value */
+ ret = strtoul (tmp+1,&s,16);
+ if (!*s) break; /* and nothing more */
+ }
+ /* drop into default case */
+ default: /* anything else is an error */
+ return NIL; /* return error */
+ }
+ return ret;
+}
+
+/* MIX internal routines */
+
+
+/* MIX mail build directory name
+ * Accepts: destination string
+ * source
+ * Returns: destination or empty string if error
+ */
+
+char *mix_dir (char *dst,char *name)
+{
+ char *s;
+ /* empty string if mailboxfile fails */
+ if (!mailboxfile (dst,name)) *dst = '\0';
+ /* driver-selected INBOX */
+ else if (!*dst) mailboxfile (dst,"~/INBOX");
+ /* tie off unnecessary trailing / */
+ else if ((s = strrchr (dst,'/')) && !s[1]) *s = '\0';
+ return dst;
+}
+
+
+/* MIX mail build file name
+ * Accepts: destination string
+ * directory name
+ * file name
+ * Returns: destination
+ */
+
+char *mix_file (char *dst,char *dir,char *name)
+{
+ sprintf (dst,"%.500s/%.80s%.80s",dir,MIXNAME,name);
+ return dst;
+}
+
+
+/* MIX mail build file name from data file number
+ * Accepts: destination string
+ * directory name
+ * data file number
+ * Returns: destination
+ */
+
+char *mix_file_data (char *dst,char *dir,unsigned long data)
+{
+ char tmp[MAILTMPLEN];
+ if (data) sprintf (tmp,"%08lx",data);
+ else tmp[0] = '\0'; /* compatibility with experimental version */
+ return mix_file (dst,dir,tmp);
+}
+
+/* MIX mail get new modseq
+ * Accepts: old modseq
+ * Returns: new modseq value
+ */
+
+unsigned long mix_modseq (unsigned long oldseq)
+{
+ /* normally time now */
+ unsigned long ret = (unsigned long) time (NIL);
+ /* ensure that modseq doesn't go backwards */
+ if (ret <= oldseq) ret = oldseq + 1;
+ return ret;
+}
diff --git a/imap/src/osdep/amiga/mkauths b/imap/src/osdep/amiga/mkauths
new file mode 100755
index 00000000..8e9cc0cf
--- /dev/null
+++ b/imap/src/osdep/amiga/mkauths
@@ -0,0 +1,40 @@
+#!/bin/sh
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+# Program: Authenticator Linkage Generator
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 5 December 1995
+# Last Edited: 30 August 2006
+
+# Erase old authenticators list
+rm -f auths.c
+touch auths.c
+
+# Now define the new list
+for authenticator
+ do
+ if [ -f Makefile."$authenticator" ]; then
+ make -f Makefile."$authenticator" `cat SPECIALS`
+ fi
+ echo "extern AUTHENTICATOR auth_"$authenticator";" >> linkage.h
+ echo " auth_link (&auth_"$authenticator"); /* link in the $authenticator authenticator */" | cat >> linkage.c
+ echo "#include \"auth_"$authenticator".c\"" >> auths.c
+done
diff --git a/imap/src/osdep/amiga/mmdf.c b/imap/src/osdep/amiga/mmdf.c
new file mode 100644
index 00000000..e962434e
--- /dev/null
+++ b/imap/src/osdep/amiga/mmdf.c
@@ -0,0 +1,2549 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MMDF mail routines
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@Washington.EDU
+ *
+ * Date: 20 December 1989
+ * Last Edited: 27 March 2008
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <signal.h>
+#include "mail.h"
+#include "osdep.h"
+#include <time.h>
+#include <sys/stat.h>
+#include "pseudo.h"
+#include "fdstring.h"
+#include "misc.h"
+#include "dummy.h"
+
+/* Supposedly, this page has everything the MMDF driver needs to know about
+ * the MMDF delimiter. By changing these macros, the MMDF driver should
+ * change with it. Note that if you change the length of MMDFHDRTXT you
+ * also need to change the ISMMDF and RETIFMMDFWRD macros to reflect the new
+ * size.
+ */
+
+
+/* Useful MMDF constants */
+
+#define MMDFCHR '\01' /* MMDF character */
+#define MMDFCHRS 0x01010101 /* MMDF header character spread in a word */
+ /* MMDF header text */
+#define MMDFHDRTXT "\01\01\01\01\n"
+ /* length of MMDF header text */
+#define MMDFHDRLEN (sizeof (MMDFHDRTXT) - 1)
+
+
+/* Validate MMDF header
+ * Accepts: pointer to candidate string to validate as an MMDF header
+ * Returns: T if valid; else NIL
+ */
+
+#define ISMMDF(s) \
+ ((*(s) == MMDFCHR) && ((s)[1] == MMDFCHR) && ((s)[2] == MMDFCHR) && \
+ ((s)[3] == MMDFCHR) && ((s)[4] == '\n'))
+
+
+/* Return if a 32-bit word has the start of an MMDF header
+ * Accepts: pointer to word of four bytes to validate as an MMDF header
+ * Returns: pointer to MMDF header, else proceeds
+ */
+
+#define RETIFMMDFWRD(s) { \
+ if (s[3] == MMDFCHR) { \
+ if ((s[4] == MMDFCHR) && (s[5] == MMDFCHR) && (s[6] == MMDFCHR) && \
+ (s[7] == '\n')) return s + 3; \
+ else if (s[2] == MMDFCHR) { \
+ if ((s[4] == MMDFCHR) && (s[5] == MMDFCHR) && (s[6] == '\n')) \
+ return s + 2; \
+ else if (s[1] == MMDFCHR) { \
+ if ((s[4] == MMDFCHR) && (s[5] == '\n')) return s + 1; \
+ else if ((*s == MMDFCHR) && (s[4] == '\n')) return s; \
+ } \
+ } \
+ } \
+}
+
+/* Validate line
+ * Accepts: pointer to candidate string to validate as a From header
+ * return pointer to end of date/time field
+ * return pointer to offset from t of time (hours of ``mmm dd hh:mm'')
+ * return pointer to offset from t of time zone (if non-zero)
+ * Returns: t,ti,zn set if valid From string, else ti is NIL
+ */
+
+#define VALID(s,x,ti,zn) { \
+ ti = 0; \
+ if ((*s == 'F') && (s[1] == 'r') && (s[2] == 'o') && (s[3] == 'm') && \
+ (s[4] == ' ')) { \
+ for (x = s + 5; *x && *x != '\n'; x++); \
+ if (*x) { \
+ if (x - s >= 41) { \
+ for (zn = -1; x[zn] != ' '; zn--); \
+ if ((x[zn-1] == 'm') && (x[zn-2] == 'o') && (x[zn-3] == 'r') && \
+ (x[zn-4] == 'f') && (x[zn-5] == ' ') && (x[zn-6] == 'e') && \
+ (x[zn-7] == 't') && (x[zn-8] == 'o') && (x[zn-9] == 'm') && \
+ (x[zn-10] == 'e') && (x[zn-11] == 'r') && (x[zn-12] == ' '))\
+ x += zn - 12; \
+ } \
+ if (x - s >= 27) { \
+ if (x[-5] == ' ') { \
+ if (x[-8] == ':') zn = 0,ti = -5; \
+ else if (x[-9] == ' ') ti = zn = -9; \
+ else if ((x[-11] == ' ') && ((x[-10]=='+') || (x[-10]=='-'))) \
+ ti = zn = -11; \
+ } \
+ else if (x[-4] == ' ') { \
+ if (x[-9] == ' ') zn = -4,ti = -9; \
+ } \
+ else if (x[-6] == ' ') { \
+ if ((x[-11] == ' ') && ((x[-5] == '+') || (x[-5] == '-'))) \
+ zn = -6,ti = -11; \
+ } \
+ if (ti && !((x[ti - 3] == ':') && \
+ (x[ti -= ((x[ti - 6] == ':') ? 9 : 6)] == ' ') && \
+ (x[ti - 3] == ' ') && (x[ti - 7] == ' ') && \
+ (x[ti - 11] == ' '))) ti = 0; \
+ } \
+ } \
+ } \
+}
+
+/* You are not expected to understand this macro, but read the next page if
+ * you are not faint of heart.
+ *
+ * Known formats to the VALID macro are:
+ * From user Wed Dec 2 05:53 1992
+ * BSD From user Wed Dec 2 05:53:22 1992
+ * SysV From user Wed Dec 2 05:53 PST 1992
+ * rn From user Wed Dec 2 05:53:22 PST 1992
+ * From user Wed Dec 2 05:53 -0700 1992
+ * emacs From user Wed Dec 2 05:53:22 -0700 1992
+ * From user Wed Dec 2 05:53 1992 PST
+ * From user Wed Dec 2 05:53:22 1992 PST
+ * From user Wed Dec 2 05:53 1992 -0700
+ * Solaris From user Wed Dec 2 05:53:22 1992 -0700
+ *
+ * Plus all of the above with `` remote from xxx'' after it. Thank you very
+ * much, smail and Solaris, for making my life considerably more complicated.
+ */
+
+/*
+ * What? You want to understand the VALID macro anyway? Alright, since you
+ * insist. Actually, it isn't really all that difficult, provided that you
+ * take it step by step.
+ *
+ * Line 1 Initializes the return ti value to failure (0);
+ * Lines 2-3 Validates that the 1st-5th characters are ``From ''.
+ * Lines 4-5 Validates that there is an end of line and points x at it.
+ * Lines 6-13 First checks to see if the line is at least 41 characters long.
+ * If so, it scans backwards to find the rightmost space. From
+ * that point, it scans backwards to see if the string matches
+ * `` remote from''. If so, it sets x to point to the space at
+ * the start of the string.
+ * Line 14 Makes sure that there are at least 27 characters in the line.
+ * Lines 15-20 Checks if the date/time ends with the year (there is a space
+ * five characters back). If there is a colon three characters
+ * further back, there is no timezone field, so zn is set to 0
+ * and ti is set in front of the year. Otherwise, there must
+ * either to be a space four characters back for a three-letter
+ * timezone, or a space six characters back followed by a + or -
+ * for a numeric timezone; in either case, zn and ti become the
+ * offset of the space immediately before it.
+ * Lines 21-23 Are the failure case for line 14. If there is a space four
+ * characters back, it is a three-letter timezone; there must be a
+ * space for the year nine characters back. zn is the zone
+ * offset; ti is the offset of the space.
+ * Lines 24-27 Are the failure case for line 20. If there is a space six
+ * characters back, it is a numeric timezone; there must be a
+ * space eleven characters back and a + or - five characters back.
+ * zn is the zone offset; ti is the offset of the space.
+ * Line 28-31 If ti is valid, make sure that the string before ti is of the
+ * form www mmm dd hh:mm or www mmm dd hh:mm:ss, otherwise
+ * invalidate ti. There must be a colon three characters back
+ * and a space six or nine characters back (depending upon
+ * whether or not the character six characters back is a colon).
+ * There must be a space three characters further back (in front
+ * of the day), one seven characters back (in front of the month),
+ * and one eleven characters back (in front of the day of week).
+ * ti is set to be the offset of the space before the time.
+ *
+ * Why a macro? It gets invoked a *lot* in a tight loop. On some of the
+ * newer pipelined machines it is faster being open-coded than it would be if
+ * subroutines are called.
+ *
+ * Why does it scan backwards from the end of the line, instead of doing the
+ * much easier forward scan? There is no deterministic way to parse the
+ * ``user'' field, because it may contain unquoted spaces! Yes, I tested it to
+ * see if unquoted spaces were possible. They are, and I've encountered enough
+ * evil mail to be totally unwilling to trust that ``it will never happen''.
+ */
+
+/* Build parameters */
+
+#define KODRETRY 15 /* kiss-of-death retry in seconds */
+#define LOCKTIMEOUT 5 /* lock timeout in minutes */
+
+
+/* MMDF I/O stream local data */
+
+typedef struct mmdf_local {
+ unsigned int dirty : 1; /* disk copy needs updating */
+ unsigned int ddirty : 1; /* double-dirty, ping becomes checkpoint */
+ unsigned int pseudo : 1; /* uses a pseudo message */
+ unsigned int appending : 1; /* don't mark new messages as old */
+ int fd; /* mailbox file descriptor */
+ int ld; /* lock file descriptor */
+ char *lname; /* lock file name */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ unsigned long uid; /* current text uid */
+ SIZEDTEXT text; /* current text */
+ unsigned long textlen; /* current text length */
+ char *line; /* returned line */
+ char *linebuf; /* line readin buffer */
+ unsigned long linebuflen; /* current line readin buffer length */
+} MMDFLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MMDFLOCAL *) stream->local)
+
+
+/* MMDF protected file structure */
+
+typedef struct mmdf_file {
+ MAILSTREAM *stream; /* current stream */
+ off_t curpos; /* current file position */
+ off_t protect; /* protected position */
+ off_t filepos; /* current last written file position */
+ char *buf; /* overflow buffer */
+ size_t buflen; /* current overflow buffer length */
+ char *bufpos; /* current buffer position */
+} MMDFFILE;
+
+/* Function prototypes */
+
+DRIVER *mmdf_valid (char *name);
+long mmdf_isvalid (char *name,char *tmp);
+long mmdf_isvalid_fd (int fd,char *tmp);
+void *mmdf_parameters (long function,void *value);
+void mmdf_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void mmdf_list (MAILSTREAM *stream,char *ref,char *pat);
+void mmdf_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long mmdf_create (MAILSTREAM *stream,char *mailbox);
+long mmdf_delete (MAILSTREAM *stream,char *mailbox);
+long mmdf_rename (MAILSTREAM *stream,char *old,char *newname);
+MAILSTREAM *mmdf_open (MAILSTREAM *stream);
+void mmdf_close (MAILSTREAM *stream,long options);
+char *mmdf_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long mmdf_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+char *mmdf_text_work (MAILSTREAM *stream,MESSAGECACHE *elt,
+ unsigned long *length,long flags);
+void mmdf_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long mmdf_ping (MAILSTREAM *stream);
+void mmdf_check (MAILSTREAM *stream);
+long mmdf_expunge (MAILSTREAM *stream,char *sequence,long options);
+long mmdf_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long mmdf_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+int mmdf_collect_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
+ STRING *msg);
+int mmdf_append_msgs (MAILSTREAM *stream,FILE *sf,FILE *df,SEARCHSET *set);
+
+void mmdf_abort (MAILSTREAM *stream);
+char *mmdf_file (char *dst,char *name);
+int mmdf_lock (char *file,int flags,int mode,DOTLOCK *lock,int op);
+void mmdf_unlock (int fd,MAILSTREAM *stream,DOTLOCK *lock);
+int mmdf_parse (MAILSTREAM *stream,DOTLOCK *lock,int op);
+char *mmdf_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size);
+unsigned long mmdf_pseudo (MAILSTREAM *stream,char *hdr);
+unsigned long mmdf_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt,
+ unsigned long uid,long flag);
+long mmdf_rewrite (MAILSTREAM *stream,unsigned long *nexp,DOTLOCK *lock,
+ long flags);
+long mmdf_extend (MAILSTREAM *stream,unsigned long size);
+void mmdf_write (MMDFFILE *f,char *s,unsigned long i);
+void mmdf_phys_write (MMDFFILE *f,char *buf,size_t size);
+
+/* MMDF mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mmdfdriver = {
+ "mmdf", /* driver name */
+ /* driver flags */
+ DR_LOCAL|DR_MAIL|DR_LOCKING|DR_NONEWMAILRONLY|DR_XPOINT,
+ (DRIVER *) NIL, /* next driver */
+ mmdf_valid, /* mailbox is valid for us */
+ mmdf_parameters, /* manipulate parameters */
+ mmdf_scan, /* scan mailboxes */
+ mmdf_list, /* list mailboxes */
+ mmdf_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ mmdf_create, /* create mailbox */
+ mmdf_delete, /* delete mailbox */
+ mmdf_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ mmdf_open, /* open mailbox */
+ mmdf_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mmdf_header, /* fetch message header */
+ mmdf_text, /* fetch message text */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ NIL, /* modify flags */
+ mmdf_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mmdf_ping, /* ping mailbox to see if still alive */
+ mmdf_check, /* check for new messages */
+ mmdf_expunge, /* expunge deleted messages */
+ mmdf_copy, /* copy messages to another mailbox */
+ mmdf_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mmdfproto = {&mmdfdriver};
+
+char *mmdfhdr = MMDFHDRTXT; /* MMDF header */
+
+/* MMDF mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mmdf_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return mmdf_isvalid (name,tmp) ? &mmdfdriver : NIL;
+}
+
+
+/* MMDF mail test for valid mailbox name
+ * Accepts: mailbox name
+ * scratch buffer
+ * Returns: T if valid, NIL otherwise
+ */
+
+long mmdf_isvalid (char *name,char *tmp)
+{
+ int fd;
+ int ret = NIL;
+ char *t,file[MAILTMPLEN];
+ struct stat sbuf;
+ time_t tp[2];
+ errno = EINVAL; /* assume invalid argument */
+ /* must be non-empty file */
+ if ((t = dummy_file (file,name)) && !stat (t,&sbuf)) {
+ if (!sbuf.st_size)errno = 0;/* empty file */
+ else if ((fd = open (file,O_RDONLY,NIL)) >= 0) {
+ /* error -1 for invalid format */
+ if (!(ret = mmdf_isvalid_fd (fd,tmp))) errno = -1;
+ close (fd); /* close the file */
+ /* \Marked status? */
+ if ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) {
+ tp[0] = sbuf.st_atime; /* preserve atime and mtime */
+ tp[1] = sbuf.st_mtime;
+ utime (file,tp); /* set the times */
+ }
+ }
+ }
+ return ret; /* return what we should */
+}
+
+/* MMDF mail test for valid mailbox
+ * Accepts: file descriptor
+ * scratch buffer
+ * Returns: T if valid, NIL otherwise
+ */
+
+long mmdf_isvalid_fd (int fd,char *tmp)
+{
+ int ret = NIL;
+ memset (tmp,'\0',MAILTMPLEN);
+ if (read (fd,tmp,MAILTMPLEN-1) >= 0) ret = ISMMDF (tmp) ? T : NIL;
+ return ret; /* return what we should */
+}
+
+
+/* MMDF manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mmdf_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = dummy_file ((char *) value,"INBOX");
+ break;
+ }
+ return ret;
+}
+
+/* MMDF mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mmdf_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* MMDF mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mmdf_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* MMDF mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mmdf_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* MMDF mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mmdf_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,mbx[MAILTMPLEN],tmp[MAILTMPLEN];
+ long ret = NIL;
+ int i,fd;
+ time_t ti = time (0);
+ if (!(s = dummy_file (mbx,mailbox))) {
+ sprintf (tmp,"Can't create %.80s: invalid name",mailbox);
+ MM_LOG (tmp,ERROR);
+ }
+ /* create underlying file */
+ else if (dummy_create_path (stream,s,get_dir_protection (mailbox))) {
+ /* done if dir-only or whiner */
+ if (((s = strrchr (s,'/')) && !s[1]) ||
+ mail_parameters (NIL,GET_USERHASNOLIFE,NIL)) ret = T;
+ else if ((fd = open (mbx,O_WRONLY,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL))) < 0) {
+ sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ else { /* initialize header */
+ memset (tmp,'\0',MAILTMPLEN);
+ sprintf (tmp,"%sFrom %s %sDate: ",mmdfhdr,pseudo_from,ctime (&ti));
+ rfc822_date (s = tmp + strlen (tmp));
+ sprintf (s += strlen (s), /* write the pseudo-header */
+ "\nFrom: %s <%s@%s>\nSubject: %s\nX-IMAP: %010lu 0000000000",
+ pseudo_name,pseudo_from,mylocalhost (),pseudo_subject,
+ (unsigned long) ti);
+ for (i = 0; i < NUSERFLAGS; ++i) if (default_user_flag (i))
+ sprintf (s += strlen (s)," %s",default_user_flag (i));
+ sprintf (s += strlen (s),"\nStatus: RO\n\n%s\n%s",pseudo_msg,mmdfhdr);
+ if (write (fd,tmp,strlen (tmp)) > 0) ret = T;
+ else {
+ sprintf (tmp,"Can't initialize mailbox node %.80s: %s",mbx,
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ close (fd); /* close file */
+ }
+ }
+ /* set proper protections */
+ return ret ? set_mbx_protections (mailbox,mbx) : NIL;
+}
+
+/* MMDF mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mmdf_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return mmdf_rename (stream,mailbox,NIL);
+}
+
+
+/* MMDF mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long mmdf_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = NIL;
+ char c,*s = NIL;
+ char tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ DOTLOCK lockx;
+ int fd,ld;
+ long i;
+ struct stat sbuf;
+ MM_CRITICAL (stream); /* get the c-client lock */
+ if (!dummy_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'/')) && !s[1]))))
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ /* lock out other c-clients */
+ else if ((ld = lockname (lock,file,LOCK_EX|LOCK_NB,&i)) < 0)
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+
+ else {
+ if ((fd = mmdf_lock (file,O_RDWR,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL),
+ &lockx,LOCK_EX)) < 0)
+ sprintf (tmp,"Can't lock mailbox %.80s: %s",old,strerror (errno));
+ else {
+ if (newname) { /* want rename? */
+ /* found superior to destination name? */
+ if (s = strrchr (s,'/')) {
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,tmp,get_dir_protection (newname))) {
+ mmdf_unlock (fd,NIL,&lockx);
+ mmdf_unlock (ld,NIL,NIL);
+ unlink (lock);
+ MM_NOCRITICAL (stream);
+ return ret; /* return success or failure */
+ }
+ *s = c; /* restore full name */
+ }
+ if (rename (file,tmp))
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ else ret = T; /* set success */
+ }
+ else if (unlink (file))
+ sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
+ else ret = T; /* set success */
+ mmdf_unlock (fd,NIL,&lockx);
+ }
+ mmdf_unlock (ld,NIL,NIL); /* flush the lock */
+ unlink (lock);
+ }
+ MM_NOCRITICAL (stream); /* no longer critical */
+ if (!ret) MM_LOG (tmp,ERROR); /* log error */
+ return ret; /* return success or failure */
+}
+
+/* MMDF mail open
+ * Accepts: Stream to open
+ * Returns: Stream on success, NIL on failure
+ */
+
+MAILSTREAM *mmdf_open (MAILSTREAM *stream)
+{
+ long i;
+ int fd;
+ char tmp[MAILTMPLEN];
+ DOTLOCK lock;
+ long retry;
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return user_flags (&mmdfproto);
+ retry = stream->silent ? 1 : KODRETRY;
+ if (stream->local) fatal ("mmdf recycle stream");
+ stream->local = memset (fs_get (sizeof (MMDFLOCAL)),0,sizeof (MMDFLOCAL));
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ /* canonicalize the stream mailbox name */
+ if (!dummy_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* flush old name */
+ fs_give ((void **) &stream->mailbox);
+ /* save canonical name */
+ stream->mailbox = cpystr (tmp);
+ LOCAL->fd = LOCAL->ld = -1; /* no file or state locking yet */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ LOCAL->text.data = (unsigned char *) fs_get (CHUNKSIZE);
+ LOCAL->text.size = CHUNKSIZE - 1;
+ LOCAL->linebuf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->linebuflen = CHUNKSIZE - 1;
+ stream->sequence++; /* bump sequence number */
+
+ /* make lock for read/write access */
+ if (!stream->rdonly) while (retry) {
+ /* try to lock file */
+ if ((fd = lockname (tmp,stream->mailbox,LOCK_EX|LOCK_NB,&i)) < 0) {
+ /* suppressing kiss-of-death? */
+ if (stream->nokod) retry = 0;
+ /* no, first time through? */
+ else if (retry-- == KODRETRY) {
+ /* learned other guy's PID and can signal? */
+ if (i && !kill ((int) i,SIGUSR2)) {
+ sprintf (tmp,"Trying to get mailbox lock from process %ld",i);
+ MM_LOG (tmp,WARN);
+ }
+ else retry = 0; /* give up */
+ }
+ if (!stream->silent) { /* nothing if silent stream */
+ if (retry) sleep (1); /* wait a second before trying again */
+ else MM_LOG ("Mailbox is open by another process, access is readonly",
+ WARN);
+ }
+ }
+ else { /* got the lock, nobody else can alter state */
+ LOCAL->ld = fd; /* note lock's fd and name */
+ LOCAL->lname = cpystr (tmp);
+ /* make sure mode OK (don't use fchmod()) */
+ chmod (LOCAL->lname,(long) mail_parameters (NIL,GET_LOCKPROTECTION,NIL));
+ if (stream->silent) i = 0;/* silent streams won't accept KOD */
+ else { /* note our PID in the lock */
+ sprintf (tmp,"%d",getpid ());
+ write (fd,tmp,(i = strlen (tmp))+1);
+ }
+ ftruncate (fd,i); /* make sure tied off */
+ fsync (fd); /* make sure it's available */
+ retry = 0; /* no more need to try */
+ }
+ }
+
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ /* will we be able to get write access? */
+ if ((LOCAL->ld >= 0) && access (stream->mailbox,W_OK) && (errno == EACCES)) {
+ MM_LOG ("Can't get write access to mailbox, access is readonly",WARN);
+ flock (LOCAL->ld,LOCK_UN); /* release the lock */
+ close (LOCAL->ld); /* close the lock file */
+ LOCAL->ld = -1; /* no more lock fd */
+ unlink (LOCAL->lname); /* delete it */
+ }
+ /* reset UID validity */
+ stream->uid_validity = stream->uid_last = 0;
+ if (stream->silent && !stream->rdonly && (LOCAL->ld < 0))
+ mmdf_abort (stream); /* abort if can't get RW silent stream */
+ /* parse mailbox */
+ else if (mmdf_parse (stream,&lock,LOCK_SH)) {
+ mmdf_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream);
+ MM_NOCRITICAL (stream); /* done with critical */
+ }
+ if (!LOCAL) return NIL; /* failure if stream died */
+ /* make sure upper level knows readonly */
+ stream->rdonly = (LOCAL->ld < 0);
+ /* notify about empty mailbox */
+ if (!(stream->nmsgs || stream->silent)) MM_LOG ("Mailbox is empty",NIL);
+ if (!stream->rdonly) { /* flags stick if readwrite */
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft = T;
+ if (!stream->uid_nosticky) {/* users with lives get permanent keywords */
+ stream->perm_user_flags = 0xffffffff;
+ /* and maybe can create them too! */
+ stream->kwd_create = stream->user_flags[NUSERFLAGS-1] ? NIL : T;
+ }
+ }
+ return stream; /* return stream alive to caller */
+}
+
+
+/* MMDF mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mmdf_close (MAILSTREAM *stream,long options)
+{
+ int silent = stream->silent;
+ stream->silent = T; /* go silent */
+ /* expunge if requested */
+ if (options & CL_EXPUNGE) mmdf_expunge (stream,NIL,NIL);
+ /* else dump final checkpoint */
+ else if (LOCAL->dirty) mmdf_check (stream);
+ stream->silent = silent; /* restore old silence state */
+ mmdf_abort (stream); /* now punt the file and local data */
+}
+
+/* MMDF mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+ /* lines to filter from header */
+static STRINGLIST *mmdf_hlines = NIL;
+
+char *mmdf_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned char *s,*t,*tl;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ elt = mail_elt (stream,msgno);/* get cache */
+ if (!mmdf_hlines) { /* once only code */
+ STRINGLIST *lines = mmdf_hlines = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "Status"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-Status"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-Keywords"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-UID"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-IMAP"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-IMAPbase"));
+ }
+ /* go to header position */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.msg.header.offset,L_SET);
+
+ if (flags & FT_INTERNAL) { /* initial data OK? */
+ if (elt->private.msg.header.text.size > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
+ elt->private.msg.header.text.size) + 1);
+ }
+ /* read message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.header.text.size);
+ /* got text, tie off string */
+ LOCAL->buf[*length = elt->private.msg.header.text.size] = '\0';
+ /* squeeze out CRs (in case from PC) */
+ for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t < tl; t++)
+ if (*t != '\r') *s++ = *t;
+ *s = '\0';
+ *length = s - LOCAL->buf; /* adjust length */
+ }
+ else { /* need to make a CRLF version */
+ read (LOCAL->fd,s = (char *) fs_get (elt->private.msg.header.text.size+1),
+ elt->private.msg.header.text.size);
+ /* tie off string, and convert to CRLF */
+ s[elt->private.msg.header.text.size] = '\0';
+ *length = strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,s,
+ elt->private.msg.header.text.size);
+ fs_give ((void **) &s); /* free readin buffer */
+ /* squeeze out spurious CRs */
+ for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t < tl; t++)
+ if ((*t != '\r') || (t[1] == '\n')) *s++ = *t;
+ *s = '\0';
+ *length = s - LOCAL->buf; /* adjust length */
+ }
+ *length = mail_filter (LOCAL->buf,*length,mmdf_hlines,FT_NOT);
+ return (char *) LOCAL->buf; /* return processed copy */
+}
+
+/* MMDF mail fetch message text
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T on success, NIL if failure
+ */
+
+long mmdf_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ char *s;
+ unsigned long i;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mail_elt (stream,msgno);/* get cache element */
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ /* mark message seen and dirty */
+ elt->seen = elt->private.dirty = LOCAL->dirty = T;
+ MM_FLAGS (stream,msgno);
+ }
+ s = mmdf_text_work (stream,elt,&i,flags);
+ INIT (bs,mail_string,s,i); /* set up stringstruct */
+ return T; /* success */
+}
+
+/* MMDF mail fetch message text worker routine
+ * Accepts: MAIL stream
+ * message cache element
+ * pointer to returned header text length
+ * option flags
+ */
+
+char *mmdf_text_work (MAILSTREAM *stream,MESSAGECACHE *elt,
+ unsigned long *length,long flags)
+{
+ FDDATA d;
+ STRING bs;
+ unsigned char c,*s,*t,*tl,tmp[CHUNKSIZE];
+ /* go to text position */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.msg.text.offset,L_SET);
+ if (flags & FT_INTERNAL) { /* initial data OK? */
+ if (elt->private.msg.text.text.size > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
+ elt->private.msg.text.text.size) + 1);
+ }
+ /* read message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.text.text.size);
+ /* got text, tie off string */
+ LOCAL->buf[*length = elt->private.msg.text.text.size] = '\0';
+ /* squeeze out CRs (in case from PC) */
+ for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t < tl; t++)
+ if (*t != '\r') *s++ = *t;
+ *s = '\0';
+ *length = s - LOCAL->buf; /* adjust length */
+ return (char *) LOCAL->buf;
+ }
+
+ /* have it cached already? */
+ if (elt->private.uid != LOCAL->uid) {
+ /* not cached, cache it now */
+ LOCAL->uid = elt->private.uid;
+ /* is buffer big enough? */
+ if (elt->rfc822_size > LOCAL->text.size) {
+ /* excessively conservative, but the right thing is too hard to do */
+ fs_give ((void **) &LOCAL->text.data);
+ LOCAL->text.data = (unsigned char *)
+ fs_get ((LOCAL->text.size = elt->rfc822_size) + 1);
+ }
+ d.fd = LOCAL->fd; /* yes, set up file descriptor */
+ d.pos = elt->private.special.offset + elt->private.msg.text.offset;
+ d.chunk = tmp; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE; /* file chunk size */
+ INIT (&bs,fd_string,&d,elt->private.msg.text.text.size);
+ for (s = (char *) LOCAL->text.data; SIZE (&bs);) switch (c = SNX (&bs)) {
+ case '\r': /* carriage return seen */
+ break;
+ case '\n':
+ *s++ = '\r'; /* insert a CR */
+ default:
+ *s++ = c; /* copy characters */
+ }
+ *s = '\0'; /* tie off buffer */
+ /* calculate length of cached data */
+ LOCAL->textlen = s - LOCAL->text.data;
+ }
+ *length = LOCAL->textlen; /* return from cache */
+ return (char *) LOCAL->text.data;
+}
+
+/* MMDF per-message modify flag
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void mmdf_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ /* only after finishing */
+ if (elt->valid) elt->private.dirty = LOCAL->dirty = T;
+}
+
+
+/* MMDF mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long mmdf_ping (MAILSTREAM *stream)
+{
+ DOTLOCK lock;
+ struct stat sbuf;
+ long reparse;
+ /* big no-op if not readwrite */
+ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock) {
+ if (stream->rdonly) { /* does he want to give up readwrite? */
+ /* checkpoint if we changed something */
+ if (LOCAL->dirty) mmdf_check (stream);
+ flock (LOCAL->ld,LOCK_UN);/* release readwrite lock */
+ close (LOCAL->ld); /* close the readwrite lock file */
+ LOCAL->ld = -1; /* no more readwrite lock fd */
+ unlink (LOCAL->lname); /* delete the readwrite lock file */
+ }
+ else { /* see if need to reparse */
+ if (!(reparse = (long) mail_parameters (NIL,GET_NETFSSTATBUG,NIL))) {
+ /* get current mailbox size */
+ if (LOCAL->fd >= 0) fstat (LOCAL->fd,&sbuf);
+ else if (stat (stream->mailbox,&sbuf)) {
+ sprintf (LOCAL->buf,"Mailbox stat failed, aborted: %s",
+ strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ mmdf_abort (stream);
+ return NIL;
+ }
+ reparse = (sbuf.st_size != LOCAL->filesize);
+ }
+ /* parse if mailbox changed */
+ if ((LOCAL->ddirty || reparse) && mmdf_parse (stream,&lock,LOCK_EX)) {
+ /* force checkpoint if double-dirty */
+ if (LOCAL->ddirty) mmdf_rewrite (stream,NIL,&lock,NIL);
+ /* unlock mailbox */
+ else mmdf_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream); /* and stream */
+ /* done with critical */
+ MM_NOCRITICAL (stream);
+ }
+ }
+ }
+ return LOCAL ? LONGT : NIL; /* return if still alive */
+}
+
+/* MMDF mail check mailbox
+ * Accepts: MAIL stream
+ */
+
+void mmdf_check (MAILSTREAM *stream)
+{
+ DOTLOCK lock;
+ /* parse and lock mailbox */
+ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock &&
+ mmdf_parse (stream,&lock,LOCK_EX)) {
+ /* any unsaved changes? */
+ if (LOCAL->dirty && mmdf_rewrite (stream,NIL,&lock,NIL)) {
+ if (!stream->silent) MM_LOG ("Checkpoint completed",NIL);
+ }
+ /* no checkpoint needed, just unlock */
+ else mmdf_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream); /* unlock the stream */
+ MM_NOCRITICAL (stream); /* done with critical */
+ }
+}
+
+
+/* MMDF mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long mmdf_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ unsigned long i;
+ DOTLOCK lock;
+ char *msg = NIL;
+ if (ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ LOCAL && (LOCAL->ld >= 0) && !stream->lock &&
+ mmdf_parse (stream,&lock,LOCK_EX)) {
+ /* check expunged messages if not dirty */
+ for (i = 1; !LOCAL->dirty && (i <= stream->nmsgs); i++) {
+ MESSAGECACHE *elt = mail_elt (stream,i);
+ if (mail_elt (stream,i)->deleted) LOCAL->dirty = T;
+ }
+ if (!LOCAL->dirty) { /* not dirty and no expunged messages */
+ mmdf_unlock (LOCAL->fd,stream,&lock);
+ msg = "No messages deleted, so no update needed";
+ }
+ else if (mmdf_rewrite (stream,&i,&lock,sequence ? LONGT : NIL)) {
+ if (i) sprintf (msg = LOCAL->buf,"Expunged %lu messages",i);
+ else msg = "Mailbox checkpointed, but no messages expunged";
+ }
+ /* rewrite failed */
+ else mmdf_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream); /* unlock the stream */
+ MM_NOCRITICAL (stream); /* done with critical */
+ if (msg && !stream->silent) MM_LOG (msg,NIL);
+ }
+ else if (!stream->silent)
+ MM_LOG ("Expunge ignored on readonly mailbox",WARN);
+ return ret;
+}
+
+/* MMDF mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if copy successful, else NIL
+ */
+
+long mmdf_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ int fd;
+ char *s,file[MAILTMPLEN];
+ DOTLOCK lock;
+ time_t tp[2];
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ long ret = T;
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ copyuid_t cu = (copyuid_t) (mail_parameters (NIL,GET_USERHASNOLIFE,NIL) ?
+ NIL : mail_parameters (NIL,GET_COPYUID,NIL));
+ SEARCHSET *source = cu ? mail_newsearchset () : NIL;
+ SEARCHSET *dest = cu ? mail_newsearchset () : NIL;
+ MAILSTREAM *tstream = NIL;
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* make sure destination is valid */
+ if (!(mmdf_valid (mailbox) || !errno))
+ switch (errno) {
+ case ENOENT: /* no such file? */
+ if (compare_cstring (mailbox,"INBOX")) {
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ }
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ mmdf_create (NIL,"INBOX");/* create empty INBOX */
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid MMDF-format mailbox name: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a MMDF-format mailbox: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+
+ /* try to open rewrite for UIDPLUS */
+ if ((tstream = mail_open_work (&mmdfdriver,NIL,mailbox,
+ OP_SILENT|OP_NOKOD)) && tstream->rdonly)
+ tstream = mail_close (tstream);
+ if (cu && !tstream) { /* wanted a COPYUID? */
+ sprintf (LOCAL->buf,"Unable to write-open mailbox for COPYUID: %.80s",
+ mailbox);
+ MM_LOG (LOCAL->buf,WARN);
+ cu = NIL; /* don't try to do COPYUID */
+ }
+ LOCAL->buf[0] = '\0';
+ MM_CRITICAL (stream); /* go critical */
+ if ((fd = mmdf_lock (dummy_file (file,mailbox),O_WRONLY|O_APPEND,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL),
+ &lock,LOCK_EX)) < 0) {
+ MM_NOCRITICAL (stream); /* done with critical */
+ sprintf (LOCAL->buf,"Can't open destination mailbox: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR); /* log the error */
+ return NIL; /* failed */
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ /* write all requested messages to mailbox */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size);
+ if (write (fd,LOCAL->buf,elt->private.special.text.size) < 0) ret = NIL;
+ else { /* internal header succeeded */
+ s = mmdf_header (stream,i,&j,FT_INTERNAL);
+ /* header size, sans trailing newline */
+ if (j && (s[j - 2] == '\n')) j--;
+ if (write (fd,s,j) < 0) ret = NIL;
+ else { /* message header succeeded */
+ j = tstream ? /* write UIDPLUS data if have readwrite */
+ mmdf_xstatus (stream,LOCAL->buf,elt,++(tstream->uid_last),LONGT) :
+ mmdf_xstatus (stream,LOCAL->buf,elt,NIL,NIL);
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ else { /* message status succeeded */
+ s = mmdf_text_work (stream,elt,&j,FT_INTERNAL);
+ if ((write (fd,s,j) < 0) || (write (fd,mmdfhdr,MMDFHDRLEN) < 0))
+ ret = NIL;
+ else if (cu) { /* need to pass back new UID? */
+ mail_append_set (source,mail_uid (stream,i));
+ mail_append_set (dest,tstream->uid_last);
+ }
+ }
+ }
+ }
+ }
+
+ if (!ret || fsync (fd)) { /* force out the update */
+ sprintf (LOCAL->buf,"Message copy failed: %s",strerror (errno));
+ ftruncate (fd,sbuf.st_size);
+ ret = NIL;
+ }
+ /* force UIDVALIDITY assignment now */
+ if (tstream && !tstream->uid_validity) tstream->uid_validity = time (0);
+ /* return sets if doing COPYUID */
+ if (cu && ret) (*cu) (stream,mailbox,tstream->uid_validity,source,dest);
+ else { /* flush any sets we may have built */
+ mail_free_searchset (&source);
+ mail_free_searchset (&dest);
+ }
+ tp[1] = time (0); /* set mtime to now */
+ if (ret) tp[0] = tp[1] - 1; /* set atime to now-1 if successful copy */
+ else tp[0] = /* else preserve \Marked status */
+ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ?
+ sbuf.st_atime : tp[1];
+ utime (file,tp); /* set the times */
+ mmdf_unlock (fd,NIL,&lock); /* unlock and close mailbox */
+ if (tstream) { /* update last UID if we can */
+ MMDFLOCAL *local = (MMDFLOCAL *) tstream->local;
+ local->dirty = T; /* do a rewrite */
+ local->appending = T; /* but not at the cost of marking as old */
+ tstream = mail_close (tstream);
+ }
+ /* log the error */
+ if (!ret) MM_LOG (LOCAL->buf,ERROR);
+ /* delete if requested message */
+ else if (options & CP_MOVE) for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence)
+ elt->deleted = elt->private.dirty = LOCAL->dirty = T;
+ MM_NOCRITICAL (stream); /* release critical */
+ return ret;
+}
+
+/* MMDF mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+#define BUFLEN 8*MAILTMPLEN
+
+long mmdf_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd;
+ unsigned long i;
+ char *flags,*date,buf[BUFLEN],tmp[MAILTMPLEN],file[MAILTMPLEN];
+ time_t tp[2];
+ FILE *sf,*df;
+ MESSAGECACHE elt;
+ DOTLOCK lock;
+ STRING *message;
+ unsigned long uidlocation = 0;
+ appenduid_t au = (appenduid_t)
+ (mail_parameters (NIL,GET_USERHASNOLIFE,NIL) ? NIL :
+ mail_parameters (NIL,GET_APPENDUID,NIL));
+ SEARCHSET *dst = au ? mail_newsearchset () : NIL;
+ long ret = LONGT;
+ MAILSTREAM *tstream = NIL;
+ /* default stream to prototype */
+ if (!stream) { /* stream specified? */
+ stream = &mmdfproto; /* no, default stream to prototype */
+ for (i = 0; i < NUSERFLAGS && stream->user_flags[i]; ++i)
+ fs_give ((void **) &stream->user_flags[i]);
+ }
+ if (!mmdf_valid (mailbox)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (compare_cstring (mailbox,"INBOX")) {
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ mmdf_create (NIL,"INBOX"); /* create empty INBOX */
+ case 0: /* merely empty file? */
+ tstream = stream;
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid MMDF-format mailbox name: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a MMDF-format mailbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get sniffing stream for keywords */
+ else if (!(tstream = mail_open (NIL,mailbox,
+ OP_READONLY|OP_SILENT|OP_NOKOD|OP_SNIFF))) {
+ sprintf (tmp,"Unable to examine mailbox for APPEND: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+
+ /* get first message */
+ if (!MM_APPEND (af) (tstream,data,&flags,&date,&message)) return NIL;
+ if (!(sf = tmpfile ())) { /* must have scratch file */
+ sprintf (tmp,".%lx.%lx",(unsigned long) time (0),(unsigned long)getpid ());
+ if (!stat (tmp,&sbuf) || !(sf = fopen (tmp,"wb+"))) {
+ sprintf (tmp,"Unable to create scratch file: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ unlink (tmp);
+ }
+ do { /* parse date */
+ if (!date) rfc822_date (date = tmp);
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ MM_LOG (tmp,ERROR);
+ }
+ else { /* user wants to suppress time zones? */
+ if (mail_parameters (NIL,GET_NOTIMEZONES,NIL)) {
+ time_t when = mail_longdate (&elt);
+ date = ctime (&when); /* use traditional date */
+ }
+ /* use POSIX-style date */
+ else date = mail_cdate (tmp,&elt);
+ if (!SIZE (message)) MM_LOG ("Append of zero-length message",ERROR);
+ else if (!mmdf_collect_msg (tstream,sf,flags,date,message)) {
+ sprintf (tmp,"Error writing scratch file: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ }
+ /* get next message */
+ else if (MM_APPEND (af) (tstream,data,&flags,&date,&message)) continue;
+ }
+ fclose (sf); /* punt scratch file */
+ return NIL; /* give up */
+ } while (message); /* until no more messages */
+ if (fflush (sf)) {
+ sprintf (tmp,"Error finishing scratch file: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ fclose (sf); /* punt scratch file */
+ return NIL; /* give up */
+ }
+ i = ftell (sf); /* size of scratch file */
+ if (tstream != stream) tstream = mail_close (tstream);
+
+ MM_CRITICAL (stream); /* go critical */
+ /* try to open readwrite for UIDPLUS */
+ if ((tstream = mail_open_work (&mmdfdriver,NIL,mailbox,
+ OP_SILENT|OP_NOKOD)) && tstream->rdonly)
+ tstream = mail_close (tstream);
+ if (au && !tstream) { /* wanted an APPENDUID? */
+ sprintf (tmp,"Unable to re-open mailbox for APPENDUID: %.80s",mailbox);
+ MM_LOG (tmp,WARN);
+ au = NIL;
+ }
+ if (((fd = mmdf_lock (dummy_file (file,mailbox),O_WRONLY|O_APPEND,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL),
+ &lock,LOCK_EX)) < 0) ||
+ !(df = fdopen (fd,"ab"))) {
+ MM_NOCRITICAL (stream); /* done with critical */
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ rewind (sf);
+ tp[1] = time (0); /* set mtime to now */
+ /* write all messages */
+ if (!mmdf_append_msgs (tstream,sf,df,au ? dst : NIL) ||
+ (fflush (df) == EOF) || fsync (fd)) {
+ sprintf (buf,"Message append failed: %s",strerror (errno));
+ MM_LOG (buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ tp[0] = /* preserve \Marked status */
+ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ?
+ sbuf.st_atime : tp[1];
+ ret = NIL; /* return error */
+ }
+ else tp[0] = tp[1] - 1; /* set atime to now-1 if successful copy */
+ utime (file,tp); /* set the times */
+ fclose (sf); /* done with scratch file */
+ /* force UIDVALIDITY assignment now */
+ if (tstream && !tstream->uid_validity) tstream->uid_validity = time (0);
+ /* return sets if doing APPENDUID */
+ if (au && ret) (*au) (mailbox,tstream->uid_validity,dst);
+ else mail_free_searchset (&dst);
+ mmdf_unlock (fd,NIL,&lock); /* unlock and close mailbox */
+ fclose (df);
+ if (tstream) { /* update last UID if we can */
+ MMDFLOCAL *local = (MMDFLOCAL *) tstream->local;
+ local->dirty = T; /* do a rewrite */
+ local->appending = T; /* but not at the cost of marking as old */
+ tstream = mail_close (tstream);
+ }
+ MM_NOCRITICAL (stream); /* release critical */
+ return ret;
+}
+
+/* Collect and write single message to append scratch file
+ * Accepts: MAIL stream
+ * scratch file
+ * flags
+ * date
+ * message stringstruct
+ * Returns: NIL if write error, else T
+ */
+
+int mmdf_collect_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
+ STRING *msg)
+{
+ unsigned char *s,*t;
+ unsigned long uf;
+ long f = mail_parse_flags (stream,flags,&uf);
+ /* write metadata, note date ends with NL */
+ if (fprintf (sf,"%ld %lu %s",f,SIZE (msg) + 1,date) < 0) return NIL;
+ while (uf) /* write user flags */
+ if ((s = stream->user_flags[find_rightmost_bit (&uf)]) &&
+ (fprintf (sf," %s",s) < 0)) return NIL;
+ if (putc ('\n',sf) == EOF) return NIL;
+ while (SIZE (msg)) { /* copy text to scratch file */
+ for (s = (unsigned char *) msg->curpos, t = s + msg->cursize; s < t; ++s)
+ if (!*s) *s = 0x80; /* disallow NUL */
+ /* write buffered text */
+ if (fwrite (msg->curpos,1,msg->cursize,sf) == msg->cursize)
+ SETPOS (msg,GETPOS (msg) + msg->cursize);
+ else return NIL; /* failed */
+ }
+ /* write trailing newline and return */
+ return (putc ('\n',sf) == EOF) ? NIL : T;
+}
+
+/* Append messages from scratch file to mailbox
+ * Accepts: MAIL stream
+ * source file
+ * destination file
+ * uidset to update if non-NIL
+ * Returns: T if success, NIL if failure
+ */
+
+int mmdf_append_msgs (MAILSTREAM *stream,FILE *sf,FILE *df,SEARCHSET *set)
+{
+ int c;
+ long f;
+ unsigned long i,j;
+ char *x,tmp[MAILTMPLEN];
+ int hdrp = T;
+ /* get message metadata line */
+ while (fgets (tmp,MAILTMPLEN,sf)) {
+ if (!(isdigit (tmp[0]) && strchr (tmp,'\n'))) return NIL;
+ f = strtol (tmp,&x,10); /* get flags */
+ if (!((*x++ == ' ') && isdigit (*x))) return NIL;
+ i = strtoul (x,&x,10); /* get message size */
+ if ((*x++ != ' ') || /* build initial header */
+ (fprintf (df,"%sFrom %s@%s %sStatus: ",mmdfhdr,myusername(),
+ mylocalhost(),x) < 0) ||
+ (f&fSEEN && (putc ('R',df) == EOF)) ||
+ (fputs ("\nX-Status: ",df) == EOF) ||
+ (f&fDELETED && (putc ('D',df) == EOF)) ||
+ (f&fFLAGGED && (putc ('F',df) == EOF)) ||
+ (f&fANSWERED && (putc ('A',df) == EOF)) ||
+ (f&fDRAFT && (putc ('T',df) == EOF)) ||
+ (fputs ("\nX-Keywords:",df) == EOF)) return NIL;
+ /* copy keywords */
+ while ((c = getc (sf)) != '\n') switch (c) {
+ case EOF:
+ return NIL;
+ default:
+ if (putc (c,df) == EOF) return NIL;
+ }
+ if ((putc ('\n',df) == EOF) ||
+ (set && (fprintf (df,"X-UID: %lu\n",++(stream->uid_last)) < 0)))
+ return NIL;
+
+ for (c = '\n'; i && fgets (tmp,MAILTMPLEN,sf); c = tmp[j-1]) {
+ /* get read line length */
+ if (i < (j = strlen (tmp))) fatal ("mmdf_append_msgs overrun");
+ i -= j; /* number of bytes left */
+ /* squish out ^A and CRs (note copies NUL) */
+ for (x = tmp; x = strpbrk (x,"\01\r"); --j) memmove (x,x+1,j-(x-tmp));
+ if (!j) continue; /* do nothing if line emptied */
+ /* start of line? */
+ if ((c == '\n')) switch (tmp[0]) {
+ case 'S': case 's': /* possible "Status:" */
+ if (hdrp && (j > 6) && ((tmp[1] == 't') || (tmp[1] == 'T')) &&
+ ((tmp[2] == 'a') || (tmp[2] == 'A')) &&
+ ((tmp[3] == 't') || (tmp[3] == 'T')) &&
+ ((tmp[4] == 'u') || (tmp[4] == 'U')) &&
+ ((tmp[5] == 's') || (tmp[5] == 'S')) && (tmp[6] == ':') &&
+ (fputs ("X-Original-",df) == EOF)) return NIL;
+ break;
+ case 'X': case 'x': /* possible X-??? header */
+ if (hdrp && (tmp[1] == '-') &&
+ /* possible X-UID: */
+ (((j > 5) && ((tmp[2] == 'U') || (tmp[2] == 'u')) &&
+ ((tmp[3] == 'I') || (tmp[3] == 'i')) &&
+ ((tmp[4] == 'D') || (tmp[4] == 'd')) && (tmp[5] == ':')) ||
+ /* possible X-IMAP: */
+ ((j > 6) && ((tmp[2] == 'I') || (tmp[2] == 'i')) &&
+ ((tmp[3] == 'M') || (tmp[3] == 'm')) &&
+ ((tmp[4] == 'A') || (tmp[4] == 'a')) &&
+ ((tmp[5] == 'P') || (tmp[5] == 'p')) &&
+ ((tmp[6] == ':') ||
+ /* or X-IMAPbase: */
+ ((j > 10) && ((tmp[6] == 'b') || (tmp[6] == 'B')) &&
+ ((tmp[7] == 'a') || (tmp[7] == 'A')) &&
+ ((tmp[8] == 's') || (tmp[8] == 'S')) &&
+ ((tmp[9] == 'e') || (tmp[9] == 'E')) && (tmp[10] == ':')))) ||
+ /* possible X-Status: */
+ ((j > 8) && ((tmp[2] == 'S') || (tmp[2] == 's')) &&
+ ((tmp[3] == 't') || (tmp[3] == 'T')) &&
+ ((tmp[4] == 'a') || (tmp[4] == 'A')) &&
+ ((tmp[5] == 't') || (tmp[5] == 'T')) &&
+ ((tmp[6] == 'u') || (tmp[6] == 'U')) &&
+ ((tmp[7] == 's') || (tmp[7] == 'S')) && (tmp[8] == ':')) ||
+ /* possible X-Keywords: */
+ ((j > 10) && ((tmp[2] == 'K') || (tmp[2] == 'k')) &&
+ ((tmp[3] == 'e') || (tmp[3] == 'E')) &&
+ ((tmp[4] == 'y') || (tmp[4] == 'Y')) &&
+ ((tmp[5] == 'w') || (tmp[5] == 'W')) &&
+ ((tmp[6] == 'o') || (tmp[6] == 'O')) &&
+ ((tmp[7] == 'r') || (tmp[7] == 'R')) &&
+ ((tmp[8] == 'd') || (tmp[8] == 'D')) &&
+ ((tmp[9] == 's') || (tmp[9] == 'S')) && (tmp[10] == ':'))) &&
+ (fputs ("X-Original-",df) == EOF)) return NIL;
+ break;
+ case '\n': /* blank line */
+ hdrp = NIL;
+ break;
+ default: /* nothing to do */
+ break;
+ }
+ /* just write the line */
+ if (fwrite (tmp,1,j,df) != j) return NIL;
+ }
+ /* make sure read entire msg & wrote trailer */
+ if (i || (fputs (mmdfhdr,df) == EOF)) return NIL;
+ /* update set */
+ if (stream) mail_append_set (set,stream->uid_last);
+ }
+ return T;
+}
+
+/* Internal routines */
+
+
+/* MMDF mail abort stream
+ * Accepts: MAIL stream
+ */
+
+void mmdf_abort (MAILSTREAM *stream)
+{
+ if (LOCAL) { /* only if a file is open */
+ if (LOCAL->fd >= 0) close (LOCAL->fd);
+ if (LOCAL->ld >= 0) { /* have a mailbox lock? */
+ flock (LOCAL->ld,LOCK_UN);/* yes, release the lock */
+ close (LOCAL->ld); /* close the lock file */
+ unlink (LOCAL->lname); /* and delete it */
+ }
+ if (LOCAL->lname) fs_give ((void **) &LOCAL->lname);
+ /* free local text buffers */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data);
+ if (LOCAL->linebuf) fs_give ((void **) &LOCAL->linebuf);
+ if (LOCAL->line) fs_give ((void **) &LOCAL->line);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* MMDF open and lock mailbox
+ * Accepts: file name to open/lock
+ * file open mode
+ * destination buffer for lock file name
+ * type of locking operation (LOCK_SH or LOCK_EX)
+ */
+
+int mmdf_lock (char *file,int flags,int mode,DOTLOCK *lock,int op)
+{
+ int fd;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ (*bn) (BLOCK_FILELOCK,NIL);
+ /* try locking the easy way */
+ if (dotlock_lock (file,lock,-1)) {
+ /* got dotlock file, easy open */
+ if ((fd = open (file,flags,mode)) >= 0) flock (fd,op);
+ else dotlock_unlock (lock); /* open failed, free the dotlock */
+ }
+ /* no dot lock file, open file now */
+ else if ((fd = open (file,flags,mode)) >= 0) {
+ /* try paranoid way to make a dot lock file */
+ if (dotlock_lock (file,lock,fd)) {
+ close (fd); /* get fresh fd in case of timing race */
+ if ((fd = open (file,flags,mode)) >= 0) flock (fd,op);
+ /* open failed, free the dotlock */
+ else dotlock_unlock (lock);
+ }
+ else flock (fd,op); /* paranoid way failed, just flock() it */
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return fd;
+}
+
+/* MMDF unlock and close mailbox
+ * Accepts: file descriptor
+ * (optional) mailbox stream to check atime/mtime
+ * (optional) lock file name
+ */
+
+void mmdf_unlock (int fd,MAILSTREAM *stream,DOTLOCK *lock)
+{
+ if (stream) { /* need to muck with times? */
+ struct stat sbuf;
+ time_t tp[2];
+ time_t now = time (0);
+ fstat (fd,&sbuf); /* get file times */
+ if (LOCAL->ld >= 0) { /* yes, readwrite session? */
+ tp[0] = now; /* set atime to now */
+ /* set mtime to (now - 1) if necessary */
+ tp[1] = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1;
+ }
+ else if (stream->recent) { /* readonly with recent messages */
+ if ((sbuf.st_atime >= sbuf.st_mtime) ||
+ (sbuf.st_atime >= sbuf.st_ctime))
+ /* keep past mtime, whack back atime */
+ tp[0] = (tp[1] = (sbuf.st_mtime < now) ? sbuf.st_mtime : now) - 1;
+ else now = 0; /* no time change needed */
+ }
+ /* readonly with no recent messages */
+ else if ((sbuf.st_atime < sbuf.st_mtime) ||
+ (sbuf.st_atime < sbuf.st_ctime)) {
+ tp[0] = now; /* set atime to now */
+ /* set mtime to (now - 1) if necessary */
+ tp[1] = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1;
+ }
+ else now = 0; /* no time change needed */
+ /* set the times, note change */
+ if (now && !utime (stream->mailbox,tp)) LOCAL->filetime = tp[1];
+ }
+ flock (fd,LOCK_UN); /* release flock'ers */
+ if (!stream) close (fd); /* close the file if no stream */
+ dotlock_unlock (lock); /* flush the lock file if any */
+}
+
+/* MMDF mail parse and lock mailbox
+ * Accepts: MAIL stream
+ * space to write lock file name
+ * type of locking operation
+ * Returns: T if parse OK, critical & mailbox is locked shared; NIL if failure
+ */
+
+int mmdf_parse (MAILSTREAM *stream,DOTLOCK *lock,int op)
+{
+ int ti,zn,m;
+ unsigned long i,j,k;
+ unsigned char c,*s,*t,*u,tmp[MAILTMPLEN],date[30];
+ int retain = T;
+ unsigned long nmsgs = stream->nmsgs;
+ unsigned long prevuid = nmsgs ? mail_elt (stream,nmsgs)->private.uid : 0;
+ unsigned long recent = stream->recent;
+ unsigned long oldnmsgs = stream->nmsgs;
+ short silent = stream->silent;
+ short pseudoseen = NIL;
+ struct stat sbuf;
+ STRING bs;
+ FDDATA d;
+ MESSAGECACHE *elt;
+ mail_lock (stream); /* guard against recursion or pingers */
+ /* toss out previous descriptor */
+ if (LOCAL->fd >= 0) close (LOCAL->fd);
+ MM_CRITICAL (stream); /* open and lock mailbox (shared OK) */
+ if ((LOCAL->fd = mmdf_lock (stream->mailbox,(LOCAL->ld >= 0) ?
+ O_RDWR : O_RDONLY,
+ (long)mail_parameters(NIL,GET_MBXPROTECTION,NIL),
+ lock,op)) < 0) {
+ sprintf (tmp,"Mailbox open failed, aborted: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ mmdf_abort (stream);
+ mail_unlock (stream);
+ MM_NOCRITICAL (stream); /* done with critical */
+ return NIL;
+ }
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ /* validate change in size */
+ if (sbuf.st_size < LOCAL->filesize) {
+ sprintf (tmp,"Mailbox shrank from %lu to %lu bytes, aborted",
+ (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size);
+ MM_LOG (tmp,ERROR); /* this is pretty bad */
+ mmdf_unlock (LOCAL->fd,stream,lock);
+ mmdf_abort (stream);
+ mail_unlock (stream);
+ MM_NOCRITICAL (stream); /* done with critical */
+ return NIL;
+ }
+
+ /* new data? */
+ else if (i = sbuf.st_size - LOCAL->filesize) {
+ d.fd = LOCAL->fd; /* yes, set up file descriptor */
+ d.pos = LOCAL->filesize; /* get to that position in the file */
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE; /* file chunk size */
+ INIT (&bs,fd_string,&d,i); /* initialize stringstruct */
+ /* skip leading whitespace for broken MTAs */
+ while (((c = CHR (&bs)) == '\n') || (c == '\r') ||
+ (c == ' ') || (c == '\t')) SNX (&bs);
+ if (SIZE (&bs)) { /* read new data */
+ /* remember internal header position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ s = mmdf_mbxline (stream,&bs,&i);
+ stream->silent = T; /* quell main program new message events */
+ do { /* read MMDF header */
+ if (!(i && ISMMDF (s))){/* see if valid MMDF header */
+ sprintf (tmp,"Unexpected changes to mailbox (try restarting): %.20s",
+ (char *) s);
+ /* see if we can back up to a line */
+ if (i && (j > MMDFHDRLEN)) {
+ SETPOS (&bs,j -= MMDFHDRLEN);
+ /* read previous line */
+ s = mmdf_mbxline (stream,&bs,&i);
+ /* kill the error if it looks good */
+ if (i && ISMMDF (s)) tmp[0] = '\0';
+ }
+ if (tmp[0]) {
+ MM_LOG (tmp,ERROR);
+ mmdf_unlock (LOCAL->fd,stream,lock);
+ mmdf_abort (stream);
+ mail_unlock (stream);
+ MM_NOCRITICAL (stream);
+ return NIL;
+ }
+ }
+ /* instantiate first new message */
+ mail_exists (stream,++nmsgs);
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ recent++; /* assume recent by default */
+ elt->recent = T;
+ /* note position/size of internal header */
+ elt->private.special.offset = j;
+ elt->private.special.text.size = i;
+
+ s = mmdf_mbxline (stream,&bs,&i);
+ ti = 0; /* assume not a valid date */
+ zn = 0,t = NIL;
+ if (i) VALID (s,t,ti,zn);
+ if (ti) { /* generate plausible IMAPish date string */
+ /* this is also part of header */
+ elt->private.special.text.size += i;
+ date[2] = date[6] = date[20] = '-'; date[11] = ' ';
+ date[14] = date[17] = ':';
+ /* dd */
+ date[0] = t[ti - 2]; date[1] = t[ti - 1];
+ /* mmm */
+ date[3] = t[ti - 6]; date[4] = t[ti - 5]; date[5] = t[ti - 4];
+ /* hh */
+ date[12] = t[ti + 1]; date[13] = t[ti + 2];
+ /* mm */
+ date[15] = t[ti + 4]; date[16] = t[ti + 5];
+ if (t[ti += 6]==':'){ /* ss */
+ date[18] = t[++ti]; date[19] = t[++ti];
+ ti++; /* move to space */
+ }
+ else date[18] = date[19] = '0';
+ /* yy -- advance over timezone if necessary */
+ if (zn == ti) ti += (((t[zn+1] == '+') || (t[zn+1] == '-')) ? 6 : 4);
+ date[7] = t[ti + 1]; date[8] = t[ti + 2];
+ date[9] = t[ti + 3]; date[10] = t[ti + 4];
+ /* zzz */
+ t = zn ? (t + zn + 1) : (unsigned char *) "LCL";
+ date[21] = *t++; date[22] = *t++; date[23] = *t++;
+ if ((date[21] != '+') && (date[21] != '-')) date[24] = '\0';
+ else { /* numeric time zone */
+ date[24] = *t++; date[25] = *t++;
+ date[26] = '\0'; date[20] = ' ';
+ }
+ /* set internal date */
+ if (!mail_parse_date (elt,date)) {
+ sprintf (tmp,"Unable to parse internal date: %s",(char *) date);
+ MM_LOG (tmp,WARN);
+ }
+ }
+ else { /* make date from file date */
+ struct tm *tm = gmtime (&sbuf.st_mtime);
+ elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1;
+ elt->year = tm->tm_year + 1900 - BASEYEAR;
+ elt->hours = tm->tm_hour; elt->minutes = tm->tm_min;
+ elt->seconds = tm->tm_sec;
+ elt->zhours = 0; elt->zminutes = 0;
+ t = NIL; /* suppress line read */
+ }
+ /* header starts here */
+ elt->private.msg.header.offset = elt->private.special.text.size;
+
+ do { /* look for message body */
+ j = GETPOS (&bs); /* note position before line */
+ if (t) s = t = mmdf_mbxline (stream,&bs,&i);
+ else t = s; /* this line read was suppressed */
+ if (ISMMDF (s)) { /* found terminator in header? */
+ SETPOS (&bs,j); /* oops, back up before line */
+ /* must insert a newline */
+ elt->private.spare.data++;
+ break; /* punt */
+ }
+ /* this line is part of header */
+ elt->private.msg.header.text.size += i;
+ if (i) switch (*s) { /* check header lines */
+ case 'X': /* possible X-???: line */
+ if (s[1] == '-') { /* must be immediately followed by hyphen */
+ /* X-Status: becomes Status: in S case */
+ if (s[2] == 'S' && s[3] == 't' && s[4] == 'a' && s[5] == 't' &&
+ s[6] == 'u' && s[7] == 's' && s[8] == ':') s += 2;
+ /* possible X-Keywords */
+ else if (s[2] == 'K' && s[3] == 'e' && s[4] == 'y' &&
+ s[5] == 'w' && s[6] == 'o' && s[7] == 'r' &&
+ s[8] == 'd' && s[9] == 's' && s[10] == ':') {
+ SIZEDTEXT uf;
+ retain = NIL; /* don't retain continuation */
+ s += 11; /* flush leading whitespace */
+ while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n'))){
+ while (*s == ' ') s++;
+ /* find end of keyword */
+ if (!(u = strpbrk (s," \n\r"))) u = s + strlen (s);
+ /* got a keyword? */
+ if ((k = (u - s)) && (k <= MAXUSERFLAG)) {
+ uf.data = (unsigned char *) s;
+ uf.size = k;
+ for (j = 0; (j < NUSERFLAGS) && stream->user_flags[j]; ++j)
+ if (!compare_csizedtext (stream->user_flags[j],&uf)) {
+ elt->user_flags |= ((long) 1) << j;
+ break;
+ }
+ }
+ s = u; /* advance to next keyword */
+ }
+ break;
+ }
+
+ /* possible X-IMAP */
+ else if ((s[2] == 'I') && (s[3] == 'M') && (s[4] == 'A') &&
+ (s[5] == 'P') && ((m = (s[6] == ':')) ||
+ ((s[6] == 'b') && (s[7] == 'a') &&
+ (s[8] == 's') && (s[9] == 'e') &&
+ (s[10] == ':')))) {
+ retain = NIL; /* don't retain continuation */
+ if ((nmsgs == 1) && !stream->uid_validity) {
+ /* advance to data */
+ s += m ? 7 : 11;
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ j = 0; /* slurp UID validity */
+ /* found a digit? */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ /* must have valid UID validity and UID last */
+ if (j && isdigit (*s)) {
+ /* pseudo-header seen if X-IMAP */
+ if (m) pseudoseen = LOCAL->pseudo = T;
+ /* save UID validity */
+ stream->uid_validity = j;
+ j = 0; /* slurp UID last */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* save UID last */
+ stream->uid_last = j;
+ /* process keywords */
+ for (j = 0; (*s != '\n') && ((*s != '\r')||(s[1] != '\n'));
+ s = u,j++) {
+ /* flush leading whitespace */
+ while (*s == ' ') s++;
+ u = strpbrk (s," \n\r");
+ /* got a keyword? */
+ if ((j < NUSERFLAGS) && (k = (u - s)) &&
+ (k <= MAXUSERFLAG)) {
+ if (stream->user_flags[j])
+ fs_give ((void **) &stream->user_flags[j]);
+ stream->user_flags[j] = (char *) fs_get (k + 1);
+ strncpy (stream->user_flags[j],s,k);
+ stream->user_flags[j][k] = '\0';
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ /* possible X-UID */
+ else if (s[2] == 'U' && s[3] == 'I' && s[4] == 'D' &&
+ s[5] == ':') {
+ retain = NIL; /* don't retain continuation */
+ /* only believe if have a UID validity */
+ if (stream->uid_validity && ((nmsgs > 1) || !pseudoseen)) {
+ s += 6; /* advance to UID value */
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ j = 0;
+ /* found a digit? */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* flush remainder of line */
+ while (*s != '\n') s++;
+ /* make sure not duplicated */
+ if (elt->private.uid)
+ sprintf (tmp,"Message %lu UID %lu already has UID %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,elt->private.uid);
+ /* make sure UID doesn't go backwards */
+ else if (j <= prevuid)
+ sprintf (tmp,"Message %lu UID %lu less than %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,prevuid + 1);
+#if 0 /* this is currently broken by UIDPLUS */
+ /* or skip by mailbox's recorded last */
+ else if (j > stream->uid_last)
+ sprintf (tmp,"Message %lu UID %lu greater than last %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,stream->uid_last);
+#endif
+ else { /* normal UID case */
+ prevuid = elt->private.uid = j;
+#if 1 /* temporary kludge for UIDPLUS */
+ if (prevuid > stream->uid_last) {
+ stream->uid_last = prevuid;
+ LOCAL->ddirty = LOCAL->dirty = T;
+ }
+#endif
+ break; /* exit this cruft */
+ }
+ MM_LOG (tmp,WARN);
+ /* invalidate UID validity */
+ stream->uid_validity = 0;
+ elt->private.uid = 0;
+ }
+ break;
+ }
+ }
+ /* otherwise fall into S case */
+
+ case 'S': /* possible Status: line */
+ if (s[0] == 'S' && s[1] == 't' && s[2] == 'a' && s[3] == 't' &&
+ s[4] == 'u' && s[5] == 's' && s[6] == ':') {
+ retain = NIL; /* don't retain continuation */
+ s += 6; /* advance to status flags */
+ do switch (*s++) {/* parse flags */
+ case 'R': /* message read */
+ elt->seen = T;
+ break;
+ case 'O': /* message old */
+ if (elt->recent) {
+ elt->recent = NIL;
+ recent--; /* it really wasn't recent */
+ }
+ break;
+ case 'D': /* message deleted */
+ elt->deleted = T;
+ break;
+ case 'F': /* message flagged */
+ elt->flagged = T;
+ break;
+ case 'A': /* message answered */
+ elt->answered = T;
+ break;
+ case 'T': /* message is a draft */
+ elt->draft = T;
+ break;
+ default: /* some other crap */
+ break;
+ } while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n')));
+ break; /* all done */
+ }
+ /* otherwise fall into default case */
+
+ default: /* ordinary header line */
+ if ((*s == 'S') || (*s == 's') ||
+ (((*s == 'X') || (*s == 'x')) && (s[1] == '-'))) {
+ unsigned char *e,*v;
+ /* must match what mail_filter() does */
+ for (u = s,v = tmp,e = u + min (i,MAILTMPLEN - 1);
+ (u < e) && ((c = (*u ? *u : (*u = ' '))) != ':') &&
+ ((c > ' ') || ((c != ' ') && (c != '\t') &&
+ (c != '\r') && (c != '\n')));
+ *v++ = *u++);
+ *v = '\0'; /* tie off */
+ /* matches internal header? */
+ if (!compare_cstring (tmp,"STATUS") ||
+ !compare_cstring (tmp,"X-STATUS") ||
+ !compare_cstring (tmp,"X-KEYWORDS") ||
+ !compare_cstring (tmp,"X-UID") ||
+ !compare_cstring (tmp,"X-IMAP") ||
+ !compare_cstring (tmp,"X-IMAPBASE")) {
+ char err[MAILTMPLEN];
+ sprintf (err,"Discarding bogus %s header in message %lu",
+ (char *) tmp,elt->msgno);
+ MM_LOG (err,WARN);
+ retain = NIL; /* don't retain continuation */
+ break; /* different case or something */
+ }
+ }
+ /* retain or non-continuation? */
+ if (retain || ((*s != ' ') && (*s != '\t'))) {
+ retain = T; /* retaining continuation now */
+ /* line length in LF format newline */
+ for (j = k = 0; j < i; ++j) if (s[j] != '\r') ++k;
+ /* "internal" header size */
+ elt->private.spare.data += k;
+ /* message size */
+ elt->rfc822_size += k + 1;
+ }
+ else {
+ char err[MAILTMPLEN];
+ sprintf (err,"Discarding bogus continuation in msg %lu: %.80s",
+ elt->msgno,(char *) s);
+ if (u = strpbrk (err,"\r\n")) *u = '\0';
+ MM_LOG (err,WARN);
+ break; /* different case or something */
+ }
+ break;
+ }
+ } while (i && (*t != '\n') && ((*t != '\r') || (t[1] != '\n')));
+ /* "internal" header sans trailing newline */
+ if (i) elt->private.spare.data--;
+ /* assign a UID if none found */
+ if (((nmsgs > 1) || !pseudoseen) && !elt->private.uid) {
+ prevuid = elt->private.uid = ++stream->uid_last;
+ elt->private.dirty = T;
+ }
+ else elt->private.dirty = elt->recent;
+
+ /* note size of header, location of text */
+ elt->private.msg.header.text.size =
+ (elt->private.msg.text.offset =
+ (LOCAL->filesize + GETPOS (&bs)) - elt->private.special.offset) -
+ elt->private.special.text.size;
+ /* note current position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ if (i) do { /* look for next message */
+ s = mmdf_mbxline (stream,&bs,&i);
+ if (i) { /* got new data? */
+ if (ISMMDF (s)) break;
+ else { /* not a header line, add it to message */
+ elt->rfc822_size += i;
+ for (j = 0; j < i; ++j) switch (s[j]) {
+ case '\r': /* squeeze out CRs */
+ elt->rfc822_size -= 1;
+ break;
+ case '\n': /* LF becomes CRLF */
+ elt->rfc822_size += 1;
+ break;
+ default:
+ break;
+ }
+ /* update current position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ }
+ }
+ } while (i); /* until found a header */
+ elt->private.msg.text.text.size = j -
+ (elt->private.special.offset + elt->private.msg.text.offset);
+ if (i) { /* get next header line */
+ /* remember first internal header position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ s = mmdf_mbxline (stream,&bs,&i);
+ }
+ /* until end of buffer */
+ } while (!stream->sniff && i);
+ if (pseudoseen) { /* flush pseudo-message if present */
+ /* decrement recent count */
+ if (mail_elt (stream,1)->recent) recent--;
+ /* and the exists count */
+ mail_exists (stream,nmsgs--);
+ mail_expunged(stream,1);/* fake an expunge of that message */
+ }
+ /* need to start a new UID validity? */
+ if (!stream->uid_validity) {
+ stream->uid_validity = time (0);
+ /* in case a whiner with no life */
+ if (mail_parameters (NIL,GET_USERHASNOLIFE,NIL))
+ stream->uid_nosticky = T;
+ else if (nmsgs) { /* don't bother if empty file */
+ /* make dirty to restart UID epoch */
+ LOCAL->ddirty = LOCAL->dirty = T;
+ /* need to rewrite msg 1 if not pseudo */
+ if (!LOCAL->pseudo) mail_elt (stream,1)->private.dirty = T;
+ MM_LOG ("Assigning new unique identifiers to all messages",NIL);
+ }
+ }
+ stream->nmsgs = oldnmsgs; /* whack it back down */
+ stream->silent = silent; /* restore old silent setting */
+ /* notify upper level of new mailbox sizes */
+ mail_exists (stream,nmsgs);
+ mail_recent (stream,recent);
+ /* mark dirty so O flags are set */
+ if (recent) LOCAL->dirty = T;
+ }
+ }
+ /* no change, don't babble if never got time */
+ else if (LOCAL->filetime && LOCAL->filetime != sbuf.st_mtime)
+ MM_LOG ("New mailbox modification time but apparently no changes",WARN);
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ LOCAL->filetime = sbuf.st_mtime;
+ return T; /* return the winnage */
+}
+
+/* MMDF read line from mailbox
+ * Accepts: mail stream
+ * stringstruct
+ * pointer to line size
+ * Returns: pointer to input line
+ */
+
+char *mmdf_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size)
+{
+ unsigned long i,j,k,m;
+ char *s,*t,*te;
+ char *ret = "";
+ /* flush old buffer */
+ if (LOCAL->line) fs_give ((void **) &LOCAL->line);
+ /* if buffer needs refreshing */
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ if (SIZE (bs)) { /* find newline */
+ /* end of fast scan */
+ te = (t = (s = bs->curpos) + bs->cursize) - 12;
+ while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) {
+ --s; /* back up */
+ break; /* exit loop */
+ }
+ /* final character-at-a-time scan */
+ while ((s < t) && (*s != '\n')) ++s;
+ /* difficult case if line spans buffer */
+ if ((i = s - bs->curpos) == bs->cursize) {
+ /* have space in line buffer? */
+ if (i > LOCAL->linebuflen) {
+ fs_give ((void **) &LOCAL->linebuf);
+ LOCAL->linebuf = (char *) fs_get (LOCAL->linebuflen = i);
+ }
+ /* remember what we have so far */
+ memcpy (LOCAL->linebuf,bs->curpos,i);
+ /* load next buffer */
+ SETPOS (bs,k = GETPOS (bs) + i);
+ /* end of fast scan */
+ te = (t = (s = bs->curpos) + bs->cursize) - 12;
+ /* fast scan in overlap buffer */
+ while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) {
+ --s; /* back up */
+ break; /* exit loop */
+ }
+
+ /* final character-at-a-time scan */
+ while ((s < t) && (*s != '\n')) ++s;
+ /* huge line? */
+ if ((j = s - bs->curpos) == bs->cursize) {
+ SETPOS (bs,GETPOS (bs) + j);
+ /* look for end of line (s-l-o-w!!) */
+ for (m = SIZE (bs); m && (SNX (bs) != '\n'); --m,++j);
+ SETPOS (bs,k); /* go back to where it started */
+ }
+ /* got size of data, make buffer for return */
+ ret = LOCAL->line = (char *) fs_get (i + j + 2);
+ /* copy first chunk */
+ memcpy (ret,LOCAL->linebuf,i);
+ while (j) { /* copy remainder */
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ memcpy (ret + i,bs->curpos,k = min (j,bs->cursize));
+ i += k; /* account for this much read in */
+ j -= k;
+ bs->curpos += k; /* increment new position */
+ bs->cursize -= k; /* eat that many bytes */
+ }
+ /* read newline at end */
+ if (SIZE (bs)) ret[i++] = SNX (bs);
+ ret[i] = '\0'; /* makes debugging easier */
+ }
+ else { /* this is easy */
+ ret = bs->curpos; /* string it at this position */
+ bs->curpos += ++i; /* increment new position */
+ bs->cursize -= i; /* eat that many bytes */
+ }
+ *size = i; /* return that to user */
+ }
+ else *size = 0; /* end of data, return empty */
+ /* embedded MMDF header at end of line? */
+ if ((*size > sizeof (MMDFHDRTXT)) &&
+ (s = ret + *size - (i = sizeof (MMDFHDRTXT) - 1)) && ISMMDF (s)) {
+ SETPOS (bs,GETPOS (bs) - i);/* back up to start of MMDF header */
+ *size -= i; /* reduce length of line */
+ ret[*size - 1] = '\n'; /* force newline at end */
+ }
+ return ret;
+}
+
+/* MMDF make pseudo-header
+ * Accepts: MAIL stream
+ * buffer to write pseudo-header
+ * Returns: length of pseudo-header
+ */
+
+unsigned long mmdf_pseudo (MAILSTREAM *stream,char *hdr)
+{
+ int i;
+ char *s,tmp[MAILTMPLEN];
+ time_t now = time (0);
+ rfc822_fixed_date (tmp);
+ sprintf (hdr,"%sFrom %s %.24s\nDate: %s\nFrom: %s <%s@%.80s>\nSubject: %s\nMessage-ID: <%lu@%.80s>\nX-IMAP: %010lu %010lu",
+ mmdfhdr,pseudo_from,ctime (&now),
+ tmp,pseudo_name,pseudo_from,mylocalhost (),pseudo_subject,
+ (unsigned long) now,mylocalhost (),stream->uid_validity,
+ stream->uid_last);
+ for (s = hdr + strlen (hdr),i = 0; i < NUSERFLAGS; ++i)
+ if (stream->user_flags[i])
+ sprintf (s += strlen (s)," %s",stream->user_flags[i]);
+ sprintf (s += strlen (s),"\nStatus: RO\n\n%s\n%s",pseudo_msg,mmdfhdr);
+ return strlen (hdr);
+}
+
+/* MMDF make status string
+ * Accepts: MAIL stream
+ * destination string to write
+ * message cache entry
+ * UID to write if non-zero (else use elt->private.uid)
+ * non-zero flag to write UID (.LT. 0 to write UID base info too)
+ * Returns: length of string
+ */
+
+unsigned long mmdf_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt,
+ unsigned long uid,long flag)
+{
+ char *t,stack[64];
+ char *s = status;
+ unsigned long n;
+ int pad = 50;
+ int sticky = uid ? T : !stream->uid_nosticky;
+ /* This used to use sprintf(), but thanks to certain cretinous C libraries
+ with horribly slow implementations of sprintf() I had to change it to this
+ mess. At least it should be fast. */
+ if ((flag < 0) && sticky) { /* need to write X-IMAPbase: header? */
+ *s++ = 'X'; *s++ = '-'; *s++ = 'I'; *s++ = 'M'; *s++ = 'A'; *s++ = 'P';
+ *s++ = 'b'; *s++ = 'a'; *s++ = 's'; *s++ = 'e'; *s++ = ':'; *s++ = ' ';
+ t = stack;
+ n = stream->uid_validity; /* push UID validity digits on the stack */
+ do *t++ = (char) (n % 10) + '0';
+ while (n /= 10);
+ /* pop UID validity digits from stack */
+ while (t > stack) *s++ = *--t;
+ *s++ = ' ';
+ n = stream->uid_last; /* push UID last digits on the stack */
+ do *t++ = (char) (n % 10) + '0';
+ while (n /= 10);
+ /* pop UID last digits from stack */
+ while (t > stack) *s++ = *--t;
+ for (n = 0; n < NUSERFLAGS; ++n) if (t = stream->user_flags[n])
+ for (*s++ = ' '; *t; *s++ = *t++);
+ *s++ = '\n';
+ pad += 30; /* increased padding if have IMAPbase */
+ }
+ *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't'; *s++ = 'u'; *s++ = 's';
+ *s++ = ':'; *s++ = ' ';
+ if (elt->seen) *s++ = 'R';
+ /* only write O if have a UID */
+ if (flag && (!elt->recent || !LOCAL->appending)) *s++ = 'O';
+ *s++ = '\n';
+ *s++ = 'X'; *s++ = '-'; *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't';
+ *s++ = 'u'; *s++ = 's'; *s++ = ':'; *s++ = ' ';
+ if (elt->deleted) *s++ = 'D';
+ if (elt->flagged) *s++ = 'F';
+ if (elt->answered) *s++ = 'A';
+ if (elt->draft) *s++ = 'T';
+ *s++ = '\n';
+
+ if (sticky) { /* only do this if UIDs sticky */
+ *s++ = 'X'; *s++ = '-'; *s++ = 'K'; *s++ = 'e'; *s++ = 'y'; *s++ = 'w';
+ *s++ = 'o'; *s++ = 'r'; *s++ = 'd'; *s++ = 's'; *s++ = ':';
+ if (n = elt->user_flags) do {
+ *s++ = ' ';
+ for (t = stream->user_flags[find_rightmost_bit (&n)]; *t; *s++ = *t++);
+ } while (n);
+ n = s - status; /* get size of stuff so far */
+ /* pad X-Keywords to make size constant */
+ if (n < pad) for (n = pad - n; n > 0; --n) *s++ = ' ';
+ *s++ = '\n';
+ if (flag) { /* want to include UID? */
+ t = stack;
+ /* push UID digits on the stack */
+ n = uid ? uid : elt->private.uid;
+ do *t++ = (char) (n % 10) + '0';
+ while (n /= 10);
+ *s++ = 'X'; *s++ = '-'; *s++ = 'U'; *s++ = 'I'; *s++ = 'D'; *s++ = ':';
+ *s++ = ' ';
+ /* pop UID from stack */
+ while (t > stack) *s++ = *--t;
+ *s++ = '\n';
+ }
+ }
+ *s++ = '\n'; *s = '\0'; /* end of extended message status */
+ return s - status; /* return size of resulting string */
+}
+
+/* Rewrite mailbox file
+ * Accepts: MAIL stream, must be critical and locked
+ * return pointer to number of expunged messages if want expunge
+ * lock file name
+ * expunge sequence, not deleted flag
+ * Returns: T if success and mailbox unlocked, NIL if failure
+ */
+
+#define OVERFLOWBUFLEN 8192 /* initial overflow buffer length */
+
+long mmdf_rewrite (MAILSTREAM *stream,unsigned long *nexp,DOTLOCK *lock,
+ long flags)
+{
+ MESSAGECACHE *elt;
+ MMDFFILE f;
+ char *s;
+ time_t tp[2];
+ long ret,flag;
+ unsigned long i,j;
+ unsigned long recent = stream->recent;
+ unsigned long size = LOCAL->pseudo ? mmdf_pseudo (stream,LOCAL->buf) : 0;
+ if (nexp) *nexp = 0; /* initially nothing expunged */
+ /* calculate size of mailbox after rewrite */
+ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs; i++) {
+ elt = mail_elt (stream,i); /* get cache */
+ if (!(nexp && elt->deleted && (flags ? elt->sequence : T))) {
+ /* add RFC822 size of this message */
+ size += elt->private.special.text.size + elt->private.spare.data +
+ mmdf_xstatus (stream,LOCAL->buf,elt,NIL,flag) +
+ elt->private.msg.text.text.size + MMDFHDRLEN;
+ flag = 1; /* only count X-IMAPbase once */
+ }
+ }
+ /* no messages, has a life, and no pseudo */
+ if (!size && !mail_parameters (NIL,GET_USERHASNOLIFE,NIL)) {
+ LOCAL->pseudo = T; /* so make a pseudo-message now */
+ size = mmdf_pseudo (stream,LOCAL->buf);
+ }
+ /* extend the file as necessary */
+ if (ret = mmdf_extend (stream,size)) {
+ /* Set up buffered I/O file structure
+ * curpos current position being written through buffering
+ * filepos current position being written physically to the disk
+ * bufpos current position being written in the buffer
+ * protect current maximum position that can be written to the disk
+ * before buffering is forced
+ * The code tries to buffer so that that disk is written in multiples of
+ * OVERBLOWBUFLEN bytes.
+ */
+ f.stream = stream; /* note mail stream */
+ f.curpos = f.filepos = 0; /* start of file */
+ f.protect = stream->nmsgs ? /* initial protection pointer */
+ mail_elt (stream,1)->private.special.offset : 8192;
+ f.bufpos = f.buf = (char *) fs_get (f.buflen = OVERFLOWBUFLEN);
+
+ if (LOCAL->pseudo) /* update pseudo-header */
+ mmdf_write (&f,LOCAL->buf,mmdf_pseudo (stream,LOCAL->buf));
+ /* loop through all messages */
+ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs;) {
+ elt = mail_elt (stream,i);/* get cache */
+ /* expunge this message? */
+ if (nexp && elt->deleted && (flags ? elt->sequence : T)) {
+ /* one less recent message */
+ if (elt->recent) --recent;
+ mail_expunged(stream,i);/* notify upper levels */
+ ++*nexp; /* count up one more expunged message */
+ }
+ else { /* preserve this message */
+ i++; /* advance to next message */
+ if ((flag < 0) || /* need to rewrite message? */
+ elt->private.dirty || (f.curpos != elt->private.special.offset) ||
+ (elt->private.msg.header.text.size !=
+ (elt->private.spare.data +
+ mmdf_xstatus (stream,LOCAL->buf,elt,NIL,flag)))) {
+ unsigned long newoffset = f.curpos;
+ /* yes, seek to internal header */
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size);
+ /* see if need to squeeze out a CR */
+ if (LOCAL->buf[elt->private.special.text.size - 2] == '\r') {
+ LOCAL->buf[--elt->private.special.text.size - 1] = '\n';
+ --size; /* squeezed out a CR from PC */
+ }
+ /* protection pointer moves to RFC822 header */
+ f.protect = elt->private.special.offset +
+ elt->private.msg.header.offset;
+ /* write internal header */
+ mmdf_write (&f,LOCAL->buf,elt->private.special.text.size);
+ /* get RFC822 header */
+ s = mmdf_header (stream,elt->msgno,&j,FT_INTERNAL);
+ /* in case this got decremented */
+ elt->private.msg.header.offset = elt->private.special.text.size;
+ /* header size, sans trailing newline */
+ if ((j < 2) || (s[j - 2] == '\n')) j--;
+ /* this can happen if CRs were squeezed */
+ if (j < elt->private.spare.data) {
+ /* so fix up counts */
+ size -= elt->private.spare.data - j;
+ elt->private.spare.data = j;
+ }
+ else if (j != elt->private.spare.data)
+ fatal ("header size inconsistent");
+ /* protection pointer moves to RFC822 text */
+ f.protect = elt->private.special.offset +
+ elt->private.msg.text.offset;
+ mmdf_write (&f,s,j); /* write RFC822 header */
+ /* write status and UID */
+ mmdf_write (&f,LOCAL->buf,
+ j = mmdf_xstatus (stream,LOCAL->buf,elt,NIL,flag));
+ flag = 1; /* only write X-IMAPbase once */
+ /* new file header size */
+ elt->private.msg.header.text.size = elt->private.spare.data + j;
+
+ /* did text move? */
+ if (f.curpos != f.protect) {
+ /* get message text */
+ s = mmdf_text_work (stream,elt,&j,FT_INTERNAL);
+ /* this can happen if CRs were squeezed */
+ if (j < elt->private.msg.text.text.size) {
+ /* so fix up counts */
+ size -= elt->private.msg.text.text.size - j;
+ elt->private.msg.text.text.size = j;
+ }
+ /* can't happen it says here */
+ else if (j > elt->private.msg.text.text.size)
+ fatal ("text size inconsistent");
+ /* new text offset, status/UID may change it */
+ elt->private.msg.text.offset = f.curpos - newoffset;
+ /* protection pointer moves to next message */
+ f.protect = (i <= stream->nmsgs) ?
+ mail_elt (stream,i)->private.special.offset :
+ (f.curpos + j + MMDFHDRLEN);
+ mmdf_write (&f,s,j);/* write text */
+ /* write trailing newline */
+ mmdf_write (&f,mmdfhdr,MMDFHDRLEN);
+ }
+ else { /* tie off header and status */
+ mmdf_write (&f,NIL,NIL);
+ f.curpos = f.protect =/* restart everything at end of message */
+ f.filepos += elt->private.msg.text.text.size + MMDFHDRLEN;
+ }
+ /* new internal header offset */
+ elt->private.special.offset = newoffset;
+ elt->private.dirty =NIL;/* message is now clean */
+ }
+ else { /* no need to rewrite this message */
+ /* tie off previous message if needed */
+ mmdf_write (&f,NIL,NIL);
+ f.curpos = f.protect =/* restart everything at end of message */
+ f.filepos += elt->private.special.text.size +
+ elt->private.msg.header.text.size +
+ elt->private.msg.text.text.size + MMDFHDRLEN;
+ }
+ }
+ }
+
+ mmdf_write (&f,NIL,NIL); /* tie off final message */
+ if (size != f.filepos) fatal ("file size inconsistent");
+ fs_give ((void **) &f.buf); /* free buffer */
+ /* make sure tied off */
+ ftruncate (LOCAL->fd,LOCAL->filesize = size);
+ fsync (LOCAL->fd); /* make sure the updates take */
+ if (size && (flag < 0)) fatal ("lost UID base information");
+ /* no longer dirty */
+ LOCAL->ddirty = LOCAL->dirty = NIL;
+ /* notify upper level of new mailbox sizes */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ /* set atime to now, mtime a second earlier */
+ tp[1] = (tp[0] = time (0)) - 1;
+ /* set the times, note change */
+ if (!utime (stream->mailbox,tp)) LOCAL->filetime = tp[1];
+ close (LOCAL->fd); /* close and reopen file */
+ if ((LOCAL->fd = open (stream->mailbox,O_RDWR,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL)))
+ < 0) {
+ sprintf (LOCAL->buf,"Mailbox open failed, aborted: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ mmdf_abort (stream);
+ }
+ dotlock_unlock (lock); /* flush the lock file */
+ }
+ return ret; /* return state from algorithm */
+}
+
+/* Extend MMDF mailbox file
+ * Accepts: MAIL stream
+ * new desired size
+ * Return: T if success, else NIL
+ */
+
+long mmdf_extend (MAILSTREAM *stream,unsigned long size)
+{
+ unsigned long i = (size > LOCAL->filesize) ? size - LOCAL->filesize : 0;
+ if (i) { /* does the mailbox need to grow? */
+ if (i > LOCAL->buflen) { /* make sure have enough space */
+ /* this user won the lottery all right */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1);
+ }
+ memset (LOCAL->buf,'\0',i); /* get a block of nulls */
+ while (T) { /* until write successful or punt */
+ lseek (LOCAL->fd,LOCAL->filesize,L_SET);
+ if ((write (LOCAL->fd,LOCAL->buf,i) >= 0) && !fsync (LOCAL->fd)) break;
+ else {
+ long e = errno; /* note error before doing ftruncate */
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ if (MM_DISKERROR (stream,e,NIL)) {
+ fsync (LOCAL->fd); /* user chose to punt */
+ sprintf (LOCAL->buf,"Unable to extend mailbox: %s",strerror (e));
+ if (!stream->silent) MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ }
+ }
+ }
+ return LONGT;
+}
+
+/* Write data to buffered file
+ * Accepts: buffered file pointer
+ * file data or NIL to indicate "flush buffer"
+ * date size (ignored for "flush buffer")
+ * Does not return until success
+ */
+
+void mmdf_write (MMDFFILE *f,char *buf,unsigned long size)
+{
+ unsigned long i,j,k;
+ if (buf) { /* doing buffered write? */
+ i = f->bufpos - f->buf; /* yes, get size of current buffer data */
+ /* yes, have space in current buffer chunk? */
+ if (j = i ? ((f->buflen - i) % OVERFLOWBUFLEN) : f->buflen) {
+ /* yes, fill up buffer as much as we can */
+ memcpy (f->bufpos,buf,k = min (j,size));
+ f->bufpos += k; /* new buffer position */
+ f->curpos += k; /* new current position */
+ if (j -= k) return; /* all done if still have buffer free space */
+ buf += k; /* full, get new unwritten data pointer */
+ size -= k; /* new data size */
+ i += k; /* new buffer data size */
+ }
+ /* This chunk of the buffer is full. See if can make some space by
+ * writing to the disk, if there's enough unprotected space to do so.
+ * Try to fill out any unaligned chunk, along with any subsequent full
+ * chunks that will fit in unprotected space.
+ */
+ /* any unprotected space we can write to? */
+ if (j = min (i,f->protect - f->filepos)) {
+ /* yes, filepos not at chunk boundary? */
+ if ((k = f->filepos % OVERFLOWBUFLEN) && ((k = OVERFLOWBUFLEN - k) < j))
+ j -= k; /* yes, and can write out partial chunk */
+ else k = 0; /* no partial chunk to write */
+ /* if at least a chunk free, write that too */
+ if (j > OVERFLOWBUFLEN) k += j - (j % OVERFLOWBUFLEN);
+ if (k) { /* write data if there is anything we can */
+ mmdf_phys_write (f,f->buf,k);
+ /* slide buffer */
+ if (i -= k) memmove (f->buf,f->buf + k,i);
+ f->bufpos = f->buf + i; /* new end of buffer */
+ }
+ }
+
+ /* Have flushed the buffer as best as possible. All done if no more
+ * data to write. Otherwise, if the buffer is empty AND if the unwritten
+ * data is larger than a chunk AND the unprotected space is also larger
+ * than a chunk, then write as many chunks as we can directly from the
+ * data. Buffer the rest, expanding the buffer as needed.
+ */
+ if (size) { /* have more data that we need to buffer? */
+ /* can write any of it to disk instead? */
+ if ((f->bufpos == f->buf) &&
+ ((j = min (f->protect - f->filepos,size)) > OVERFLOWBUFLEN)) {
+ /* write as much as we can right now */
+ mmdf_phys_write (f,buf,j -= (j % OVERFLOWBUFLEN));
+ buf += j; /* new data pointer */
+ size -= j; /* new data size */
+ f->curpos += j; /* advance current pointer */
+ }
+ if (size) { /* still have data that we need to buffer? */
+ /* yes, need to expand the buffer? */
+ if ((i = ((f->bufpos + size) - f->buf)) > f->buflen) {
+ /* note current position in buffer */
+ j = f->bufpos - f->buf;
+ i += OVERFLOWBUFLEN; /* yes, grow another chunk */
+ fs_resize ((void **) &f->buf,f->buflen = i - (i % OVERFLOWBUFLEN));
+ /* in case buffer relocated */
+ f->bufpos = f->buf + j;
+ }
+ /* buffer remaining data */
+ memcpy (f->bufpos,buf,size);
+ f->bufpos += size; /* new end of buffer */
+ f->curpos += size; /* advance current pointer */
+ }
+ }
+ }
+ else { /* flush buffer to disk */
+ mmdf_phys_write (f,f->buf,i = f->bufpos - f->buf);
+ f->bufpos = f->buf; /* reset buffer */
+ /* update positions */
+ f->curpos = f->protect = f->filepos;
+ }
+}
+
+/* Physical disk write
+ * Accepts: buffered file pointer
+ * buffer address
+ * buffer size
+ * Does not return until success
+ */
+
+void mmdf_phys_write (MMDFFILE *f,char *buf,size_t size)
+{
+ MAILSTREAM *stream = f->stream;
+ /* write data at desired position */
+ while (size && ((lseek (LOCAL->fd,f->filepos,L_SET) < 0) ||
+ (write (LOCAL->fd,buf,size) < 0))) {
+ int e;
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Unable to write to mailbox: %s",strerror (e = errno));
+ MM_LOG (tmp,ERROR);
+ MM_DISKERROR (NIL,e,T); /* serious problem, must retry */
+ }
+ f->filepos += size; /* update file position */
+}
diff --git a/imap/src/osdep/amiga/mtx.c b/imap/src/osdep/amiga/mtx.c
new file mode 100644
index 00000000..8e6f76e8
--- /dev/null
+++ b/imap/src/osdep/amiga/mtx.c
@@ -0,0 +1,1371 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MTX mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 22 May 1990
+ * Last Edited: 11 October 2007
+ */
+
+
+/* FILE TIME SEMANTICS
+ *
+ * The atime is the last read time of the file.
+ * The mtime is the last flags update time of the file.
+ * The ctime is the last write time of the file.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "misc.h"
+#include "dummy.h"
+#include "fdstring.h"
+
+/* MTX I/O stream local data */
+
+typedef struct mtx_local {
+ unsigned int shouldcheck: 1; /* if ping should do a check instead */
+ unsigned int mustcheck: 1; /* if ping must do a check instead */
+ int fd; /* file descriptor for I/O */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ time_t lastsnarf; /* last snarf time */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+} MTXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MTXLOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *mtx_valid (char *name);
+int mtx_isvalid (char *name,char *tmp);
+void *mtx_parameters (long function,void *value);
+void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void mtx_list (MAILSTREAM *stream,char *ref,char *pat);
+void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long mtx_create (MAILSTREAM *stream,char *mailbox);
+long mtx_delete (MAILSTREAM *stream,char *mailbox);
+long mtx_rename (MAILSTREAM *stream,char *old,char *newname);
+long mtx_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *mtx_open (MAILSTREAM *stream);
+void mtx_close (MAILSTREAM *stream,long options);
+void mtx_flags (MAILSTREAM *stream,char *sequence,long flags);
+char *mtx_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void mtx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long mtx_ping (MAILSTREAM *stream);
+void mtx_check (MAILSTREAM *stream);
+void mtx_snarf (MAILSTREAM *stream);
+long mtx_expunge (MAILSTREAM *stream,char *sequence,long options);
+long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+char *mtx_file (char *dst,char *name);
+long mtx_parse (MAILSTREAM *stream);
+MESSAGECACHE *mtx_elt (MAILSTREAM *stream,unsigned long msgno);
+void mtx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt);
+void mtx_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag);
+unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size);
+
+/* MTX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mtxdriver = {
+ "mtx", /* driver name */
+ /* driver flags */
+ DR_LOCAL|DR_MAIL|DR_CRLF|DR_NOSTICKY|DR_LOCKING,
+ (DRIVER *) NIL, /* next driver */
+ mtx_valid, /* mailbox is valid for us */
+ mtx_parameters, /* manipulate parameters */
+ mtx_scan, /* scan mailboxes */
+ mtx_list, /* list mailboxes */
+ mtx_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ dummy_create, /* create mailbox */
+ mtx_delete, /* delete mailbox */
+ mtx_rename, /* rename mailbox */
+ mtx_status, /* status of mailbox */
+ mtx_open, /* open mailbox */
+ mtx_close, /* close mailbox */
+ mtx_flags, /* fetch message "fast" attributes */
+ mtx_flags, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mtx_header, /* fetch message header */
+ mtx_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ mtx_flag, /* modify flags */
+ mtx_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mtx_ping, /* ping mailbox to see if still alive */
+ mtx_check, /* check for new messages */
+ mtx_expunge, /* expunge deleted messages */
+ mtx_copy, /* copy messages to another mailbox */
+ mtx_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mtxproto = {&mtxdriver};
+
+/* MTX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mtx_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return mtx_isvalid (name,tmp) ? &mtxdriver : NIL;
+}
+
+
+/* MTX mail test for valid mailbox
+ * Accepts: mailbox name
+ * Returns: T if valid, NIL otherwise
+ */
+
+int mtx_isvalid (char *name,char *tmp)
+{
+ int fd;
+ int ret = NIL;
+ char *s,file[MAILTMPLEN];
+ struct stat sbuf;
+ time_t tp[2];
+ errno = EINVAL; /* assume invalid argument */
+ /* if file, get its status */
+ if ((s = mtx_file (file,name)) && !stat (s,&sbuf)) {
+ if (!sbuf.st_size) { /* allow empty file if INBOX */
+ if ((s = mailboxfile (tmp,name)) && !*s) ret = T;
+ else errno = 0; /* empty file */
+ }
+ else if ((fd = open (file,O_RDONLY,NIL)) >= 0) {
+ memset (tmp,'\0',MAILTMPLEN);
+ if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\015')) &&
+ (s[1] == '\012')) { /* valid format? */
+ *s = '\0'; /* tie off header */
+ /* must begin with dd-mmm-yy" */
+ ret = (((tmp[2] == '-' && tmp[6] == '-') ||
+ (tmp[1] == '-' && tmp[5] == '-')) &&
+ (s = strchr (tmp+18,',')) && strchr (s+2,';')) ? T : NIL;
+ }
+ else errno = -1; /* bogus format */
+ close (fd); /* close the file */
+ /* \Marked status? */
+ if (sbuf.st_ctime > sbuf.st_atime) {
+ tp[0] = sbuf.st_atime; /* preserve atime and mtime */
+ tp[1] = sbuf.st_mtime;
+ utime (file,tp); /* set the times */
+ }
+ }
+ }
+ /* in case INBOX but not mtx format */
+ else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1;
+ return ret; /* return what we should */
+}
+
+/* MTX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mtx_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = mtx_file ((char *) value,"INBOX");
+ break;
+ }
+ return ret;
+}
+
+
+/* MTX mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* MTX mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mtx_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* MTX mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* MTX mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mtx_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return mtx_rename (stream,mailbox,NIL);
+}
+
+
+/* MTX mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long mtx_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = T;
+ char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ int fd,ld;
+ struct stat sbuf;
+ if (!mtx_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'/')) && !s[1])))) {
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ else if ((fd = open (file,O_RDWR,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get exclusive parse/append permission */
+ if ((ld = lockfd (fd,lock,LOCK_EX)) < 0) {
+ MM_LOG ("Unable to lock rename mailbox",ERROR);
+ return NIL;
+ }
+ /* lock out other users */
+ if (flock (fd,LOCK_EX|LOCK_NB)) {
+ close (fd); /* couldn't lock, give up on it then */
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+ MM_LOG (tmp,ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return NIL;
+ }
+
+ if (newname) { /* want rename? */
+ if (s = strrchr (tmp,'/')) {/* found superior to destination name? */
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,tmp,get_dir_protection (newname)))
+ ret = NIL;
+ else *s = c; /* restore full name */
+ }
+ /* rename the file */
+ if (ret && rename (file,tmp)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ else if (unlink (file)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* close the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ /* recreate file if renamed INBOX */
+ if (ret && !compare_cstring (old,"INBOX")) dummy_create (NIL,"INBOX.MTX");
+ return ret; /* return success */
+}
+
+/* Mtx Mail status
+ * Accepts: mail stream
+ * mailbox name
+ * status flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mtx_status (MAILSTREAM *stream,char *mbx,long flags)
+{
+ MAILSTATUS status;
+ unsigned long i;
+ MAILSTREAM *tstream = NIL;
+ MAILSTREAM *systream = NIL;
+ /* make temporary stream (unless this mbx) */
+ if (!stream && !(stream = tstream =
+ mail_open (NIL,mbx,OP_READONLY|OP_SILENT))) return NIL;
+ status.flags = flags; /* return status values */
+ status.messages = stream->nmsgs;
+ status.recent = stream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++)
+ if (!mail_elt (stream,i)->seen) status.unseen++;
+ status.uidnext = stream->uid_last + 1;
+ status.uidvalidity = stream->uid_validity;
+ /* calculate post-snarf results */
+ if (!status.recent && stream->inbox &&
+ (systream = mail_open (NIL,sysinbox (),OP_READONLY|OP_SILENT))) {
+ status.messages += systream->nmsgs;
+ status.recent += systream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1; i <= systream->nmsgs; i++)
+ if (!mail_elt (systream,i)->seen) status.unseen++;
+ /* kludge but probably good enough */
+ status.uidnext += systream->nmsgs;
+ }
+ MM_STATUS(stream,mbx,&status);/* pass status to main program */
+ if (tstream) mail_close (tstream);
+ if (systream) mail_close (systream);
+ return T; /* success */
+}
+
+/* MTX mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mtx_open (MAILSTREAM *stream)
+{
+ int fd,ld;
+ char tmp[MAILTMPLEN];
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return user_flags (&mtxproto);
+ if (stream->local) fatal ("mtx recycle stream");
+ user_flags (stream); /* set up user flags */
+ /* canonicalize the mailbox name */
+ if (!mtx_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ MM_LOG (tmp,ERROR);
+ }
+ if (stream->rdonly ||
+ (fd = open (tmp,O_RDWR,NIL)) < 0) {
+ if ((fd = open (tmp,O_RDONLY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ else if (!stream->rdonly) { /* got it, but readonly */
+ MM_LOG ("Can't get write access to mailbox, access is readonly",WARN);
+ stream->rdonly = T;
+ }
+ }
+ stream->local = fs_get (sizeof (MTXLOCAL));
+ LOCAL->fd = fd; /* bind the file */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ /* get shared parse permission */
+ if ((ld = lockfd (fd,tmp,LOCK_SH)) < 0) {
+ MM_LOG ("Unable to lock open mailbox",ERROR);
+ return NIL;
+ }
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH); /* lock the file */
+ (*bn) (BLOCK_NONE,NIL);
+ unlockfd (ld,tmp); /* release shared parse permission */
+ LOCAL->filesize = 0; /* initialize parsed file size */
+ /* time not set up yet */
+ LOCAL->lastsnarf = LOCAL->filetime = 0;
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ stream->sequence++; /* bump sequence number */
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ if (mtx_ping (stream) && !stream->nmsgs)
+ MM_LOG ("Mailbox is empty",(long) NIL);
+ if (!LOCAL) return NIL; /* failure if stream died */
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft =
+ stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ return stream; /* return stream to caller */
+}
+
+/* MTX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mtx_close (MAILSTREAM *stream,long options)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ if (options & CL_EXPUNGE) mtx_expunge (stream,NIL,NIL);
+ stream->silent = silent; /* restore previous status */
+ flock (LOCAL->fd,LOCK_UN); /* unlock local file */
+ close (LOCAL->fd); /* close the local file */
+ /* free local text buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+
+/* MTX mail fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ * Sniffs at file to see if some other process changed the flags
+ */
+
+void mtx_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ unsigned long i;
+ if (mtx_ping (stream) && /* ping mailbox, get new status for messages */
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if (mail_elt (stream,i)->sequence) mtx_elt (stream,i);
+}
+
+/* MTX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *mtx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags)
+{
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ /* get to header position */
+ lseek (LOCAL->fd,mtx_hdrpos (stream,msgno,length),L_SET);
+ /* is buffer big enough? */
+ if (*length > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = *length) + 1);
+ }
+ LOCAL->buf[*length] = '\0'; /* tie off string */
+ /* slurp the data */
+ read (LOCAL->fd,LOCAL->buf,*length);
+ return (char *) LOCAL->buf;
+}
+
+/* MTX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: T, always
+ */
+
+long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ FDDATA d;
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mtx_elt (stream,msgno); /* get message status */
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ elt->seen = T; /* mark message as seen */
+ /* recalculate status */
+ mtx_update_status (stream,msgno,NIL);
+ MM_FLAGS (stream,msgno);
+ }
+ /* find header position */
+ i = mtx_hdrpos (stream,msgno,&j);
+ d.fd = LOCAL->fd; /* set up file descriptor */
+ d.pos = i + j;
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE;
+ INIT (bs,fd_string,&d,elt->rfc822_size - j);
+ return T; /* success */
+}
+
+/* MTX mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ */
+
+void mtx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ time_t tp[2];
+ struct stat sbuf;
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* make sure read comes after all that */
+ utime (stream->mailbox,tp);
+ }
+}
+
+
+/* MTX mail per-message modify flags
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ struct stat sbuf;
+ /* maybe need to do a checkpoint? */
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ LOCAL->filetime = 0; /* don't do this test for any other messages */
+ }
+ /* recalculate status */
+ mtx_update_status (stream,elt->msgno,NIL);
+}
+
+/* MTX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream still alive, NIL if not
+ */
+
+long mtx_ping (MAILSTREAM *stream)
+{
+ unsigned long i = 1;
+ long r = T;
+ int ld;
+ char lock[MAILTMPLEN];
+ struct stat sbuf;
+ if (stream && LOCAL) { /* only if stream already open */
+ fstat (LOCAL->fd,&sbuf); /* get current file poop */
+ if (LOCAL->filetime && !(LOCAL->mustcheck || LOCAL->shouldcheck) &&
+ (LOCAL->filetime < sbuf.st_mtime)) LOCAL->shouldcheck = T;
+ /* check for changed message status */
+ if (LOCAL->mustcheck || LOCAL->shouldcheck) {
+ LOCAL->filetime = sbuf.st_mtime;
+ if (LOCAL->shouldcheck) /* babble when we do this unilaterally */
+ MM_NOTIFY (stream,"[CHECK] Checking for flag updates",NIL);
+ while (i <= stream->nmsgs) mtx_elt (stream,i++);
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ }
+ /* get shared parse/append permission */
+ if ((sbuf.st_size != LOCAL->filesize) &&
+ ((ld = lockfd (LOCAL->fd,lock,LOCK_SH)) >= 0)) {
+ /* parse resulting mailbox */
+ r = (mtx_parse (stream)) ? T : NIL;
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ if (LOCAL) { /* stream must still be alive */
+ /* snarf if this is a read-write inbox */
+ if (stream->inbox && !stream->rdonly) {
+ mtx_snarf (stream);
+ fstat (LOCAL->fd,&sbuf);/* see if file changed now */
+ if ((sbuf.st_size != LOCAL->filesize) &&
+ ((ld = lockfd (LOCAL->fd,lock,LOCK_SH)) >= 0)) {
+ /* parse resulting mailbox */
+ r = (mtx_parse (stream)) ? T : NIL;
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ }
+ }
+ }
+ return r; /* return result of the parse */
+}
+
+
+/* MTX mail check mailbox (reparses status too)
+ * Accepts: MAIL stream
+ */
+
+void mtx_check (MAILSTREAM *stream)
+{
+ /* mark that a check is desired */
+ if (LOCAL) LOCAL->mustcheck = T;
+ if (mtx_ping (stream)) MM_LOG ("Check completed",(long) NIL);
+}
+
+/* MTX mail snarf messages from system inbox
+ * Accepts: MAIL stream
+ */
+
+void mtx_snarf (MAILSTREAM *stream)
+{
+ unsigned long i = 0;
+ unsigned long j,r,hdrlen,txtlen;
+ struct stat sbuf;
+ char *hdr,*txt,lock[MAILTMPLEN],tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ MAILSTREAM *sysibx = NIL;
+ int ld;
+ /* give up if can't get exclusive permission */
+ if ((time (0) >= (LOCAL->lastsnarf +
+ (long) mail_parameters (NIL,GET_SNARFINTERVAL,NIL))) &&
+ strcmp (sysinbox (),stream->mailbox) &&
+ ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) >= 0)) {
+ MM_CRITICAL (stream); /* go critical */
+ /* sizes match and anything in sysinbox? */
+ if (!stat (sysinbox (),&sbuf) && sbuf.st_size &&
+ !fstat (LOCAL->fd,&sbuf) && (sbuf.st_size == LOCAL->filesize) &&
+ (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) &&
+ (!sysibx->rdonly) && (r = sysibx->nmsgs)) {
+ /* yes, go to end of file in our mailbox */
+ lseek (LOCAL->fd,sbuf.st_size,L_SET);
+ /* for each message in sysibx mailbox */
+ while (r && (++i <= sysibx->nmsgs)) {
+ /* snarf message from system INBOX */
+ hdr = cpystr (mail_fetchheader_full (sysibx,i,NIL,&hdrlen,NIL));
+ txt = mail_fetchtext_full (sysibx,i,&txtlen,FT_PEEK);
+ /* if have a message */
+ if (j = hdrlen + txtlen) {
+ /* calculate header line */
+ mail_date (LOCAL->buf,elt = mail_elt (sysibx,i));
+ sprintf (LOCAL->buf + strlen (LOCAL->buf),
+ ",%lu;0000000000%02o\015\012",j,(unsigned)
+ ((fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)));
+ /* copy message */
+ if ((write (LOCAL->fd,LOCAL->buf,strlen (LOCAL->buf)) < 0) ||
+ (write (LOCAL->fd,hdr,hdrlen) < 0) ||
+ (write (LOCAL->fd,txt,txtlen) < 0)) r = 0;
+ }
+ fs_give ((void **) &hdr);
+ }
+
+ /* make sure all the updates take */
+ if (fsync (LOCAL->fd)) r = 0;
+ if (r) { /* delete all the messages we copied */
+ if (r == 1) strcpy (tmp,"1");
+ else sprintf (tmp,"1:%lu",r);
+ mail_flag (sysibx,tmp,"\\Deleted",ST_SET);
+ mail_expunge (sysibx); /* now expunge all those messages */
+ }
+ else {
+ sprintf (LOCAL->buf,"Can't copy new mail: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,WARN);
+ ftruncate (LOCAL->fd,sbuf.st_size);
+ }
+ fstat (LOCAL->fd,&sbuf); /* yes, get current file size */
+ LOCAL->filetime = sbuf.st_mtime;
+ }
+ if (sysibx) mail_close (sysibx);
+ MM_NOCRITICAL (stream); /* release critical */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ LOCAL->lastsnarf = time (0);/* note time of last snarf */
+ }
+}
+
+/* MTX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long mtx_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ time_t tp[2];
+ struct stat sbuf;
+ off_t pos = 0;
+ int ld;
+ unsigned long i = 1;
+ unsigned long j,k,m,recent;
+ unsigned long n = 0;
+ unsigned long delta = 0;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (!(ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ mtx_ping (stream))); /* parse sequence if given, ping stream */
+ else if (stream->rdonly) MM_LOG ("Expunge ignored on readonly mailbox",WARN);
+ else {
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ }
+ /* The cretins who designed flock() created a window of vulnerability in
+ * upgrading locks from shared to exclusive or downgrading from exclusive
+ * to shared. Rather than maintain the lock at shared status at a minimum,
+ * flock() actually *releases* the former lock. Obviously they never talked
+ * to any database guys. Fortunately, we have the parse/append permission
+ * lock. If we require this lock before going exclusive on the mailbox,
+ * another process can not sneak in and steal the exclusive mailbox lock on
+ * us, because it will block on trying to get parse/append permission first.
+ */
+ /* get exclusive parse/append permission */
+ if ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) < 0)
+ MM_LOG ("Unable to lock expunge mailbox",ERROR);
+ /* make sure see any newly-arrived messages */
+ else if (!mtx_parse (stream));
+ /* get exclusive access */
+ else if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH);/* recover previous lock */
+ (*bn) (BLOCK_NONE,NIL);
+ MM_LOG ("Can't expunge because mailbox is in use by another process",
+ ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+
+ else {
+ MM_CRITICAL (stream); /* go critical */
+ recent = stream->recent; /* get recent now that pinged and locked */
+ /* for each message */
+ while (i <= stream->nmsgs) {
+ /* get cache element */
+ elt = mtx_elt (stream,i);
+ /* number of bytes to smash or preserve */
+ k = elt->private.special.text.size + elt->rfc822_size;
+ /* if need to expunge this message */
+ if (elt->deleted && (sequence ? elt->sequence : T)) {
+ /* if recent, note one less recent message */
+ if (elt->recent) --recent;
+ delta += k; /* number of bytes to delete */
+ /* notify upper levels */
+ mail_expunged (stream,i);
+ n++; /* count up one more expunged message */
+ }
+ else if (i++ && delta) {/* preserved message */
+ /* first byte to preserve */
+ j = elt->private.special.offset;
+ do { /* read from source position */
+ m = min (k,LOCAL->buflen);
+ lseek (LOCAL->fd,j,L_SET);
+ read (LOCAL->fd,LOCAL->buf,m);
+ pos = j - delta; /* write to destination position */
+ lseek (LOCAL->fd,pos,L_SET);
+ while (T) {
+ lseek (LOCAL->fd,pos,L_SET);
+ if (write (LOCAL->fd,LOCAL->buf,m) > 0) break;
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+ pos += m; /* new position */
+ j += m; /* next chunk, perhaps */
+ } while (k -= m); /* until done */
+ /* note the new address of this text */
+ elt->private.special.offset -= delta;
+ }
+ /* preserved but no deleted messages */
+ else pos = elt->private.special.offset + k;
+ }
+ if (n) { /* truncate file after last message */
+ if (pos != (LOCAL->filesize -= delta)) {
+ sprintf (LOCAL->buf,
+ "Calculated size mismatch %lu != %lu, delta = %lu",
+ (unsigned long) pos,(unsigned long) LOCAL->filesize,delta);
+ MM_LOG (LOCAL->buf,WARN);
+ LOCAL->filesize = pos;/* fix it then */
+ }
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ sprintf (LOCAL->buf,"Expunged %lu messages",n);
+ /* output the news */
+ MM_LOG (LOCAL->buf,(long) NIL);
+ }
+ else MM_LOG ("No messages deleted, so no update needed",(long) NIL);
+ fsync (LOCAL->fd); /* force disk update */
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* reset atime to now */
+ utime (stream->mailbox,tp);
+ MM_NOCRITICAL (stream); /* release critical */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH);/* allow sharers again */
+ (*bn) (BLOCK_NONE,NIL);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+ }
+ return ret;
+}
+
+/* MTX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if success, NIL if failed
+ */
+
+long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ time_t tp[2];
+ MESSAGECACHE *elt;
+ unsigned long i,j,k;
+ long ret = LONGT;
+ int fd,ld;
+ char file[MAILTMPLEN],lock[MAILTMPLEN];
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ /* make sure valid mailbox */
+ if (!mtx_isvalid (mailbox,LOCAL->buf)) switch (errno) {
+ case ENOENT: /* no such file? */
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid MTX-format mailbox name: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a MTX-format mailbox: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* got file? */
+ if ((fd = open (mtx_file (file,mailbox),O_RDWR,NIL)) < 0) {
+ sprintf (LOCAL->buf,"Unable to open copy mailbox: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ MM_CRITICAL (stream); /* go critical */
+ /* get exclusive parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockfd (fd,lock,LOCK_EX)) < 0)) {
+ MM_LOG ("Unable to lock copy mailbox",ERROR);
+ MM_NOCRITICAL (stream);
+ return NIL;
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
+
+ /* for each requested message */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ /* number of bytes to copy */
+ k = elt->private.special.text.size + elt->rfc822_size;
+ do { /* read from source position */
+ j = min (k,LOCAL->buflen);
+ read (LOCAL->fd,LOCAL->buf,j);
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ } while (ret && (k -= j));/* until done */
+ }
+ /* make sure all the updates take */
+ if (!(ret && (ret = !fsync (fd)))) {
+ sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ }
+ if (ret) tp[0] = time (0) - 1;/* set atime to now-1 if successful copy */
+ /* else preserve \Marked status */
+ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0);
+ tp[1] = sbuf.st_mtime; /* preserve mtime */
+ utime (file,tp); /* set the times */
+ close (fd); /* close the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ MM_NOCRITICAL (stream); /* release critical */
+ /* delete all requested messages */
+ if (ret && (options & CP_MOVE)) {
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mtx_elt (stream,i))->sequence) {
+ elt->deleted = T; /* mark message deleted */
+ /* recalculate status */
+ mtx_update_status (stream,i,NIL);
+ }
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* make sure atime remains greater */
+ utime (stream->mailbox,tp);
+ }
+ }
+ if (ret && mail_parameters (NIL,GET_COPYUID,NIL))
+ MM_LOG ("Can not return meaningful COPYUID with this mailbox format",WARN);
+ return ret;
+}
+
+/* MTX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd,ld,c;
+ char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ time_t tp[2];
+ FILE *df;
+ MESSAGECACHE elt;
+ long f;
+ unsigned long i,uf;
+ STRING *message;
+ long ret = LONGT;
+ /* default stream to prototype */
+ if (!stream) stream = user_flags (&mtxproto);
+ /* make sure valid mailbox */
+ if (!mtx_isvalid (mailbox,tmp)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (!compare_cstring (mailbox,"INBOX")) dummy_create (NIL,"INBOX.MTX");
+ else {
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* falls through */
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid MTX-format mailbox name: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a MTX-format mailbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get first message */
+ if (!MM_APPEND (af) (stream,data,&flags,&date,&message)) return NIL;
+
+ /* open destination mailbox */
+ if (((fd = open (mtx_file (file,mailbox),O_WRONLY|O_APPEND,NIL)) < 0) ||
+ !(df = fdopen (fd,"ab"))) {
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockfd (fd,lock,LOCK_EX)) < 0)) {
+ MM_LOG ("Unable to lock append mailbox",ERROR);
+ close (fd);
+ return NIL;
+ }
+ MM_CRITICAL (stream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ errno = 0;
+ do { /* parse flags */
+ if (!SIZE (message)) { /* guard against zero-length */
+ MM_LOG ("Append of zero-length message",ERROR);
+ ret = NIL;
+ break;
+ }
+ f = mail_parse_flags (stream,flags,&i);
+ /* reverse bits (dontcha wish we had CIRC?) */
+ for (uf = 0; i; uf |= 1 << (29 - find_rightmost_bit (&i)));
+ if (date) { /* parse date if given */
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* mark failure */
+ break;
+ }
+ mail_date (tmp,&elt); /* write preseved date */
+ }
+ else internal_date (tmp); /* get current date in IMAP format */
+ /* write header */
+ if (fprintf (df,"%s,%lu;%010lo%02lo\015\012",tmp,i = SIZE (message),uf,
+ (unsigned long) f) < 0) ret = NIL;
+ else { /* write message */
+ if (i) do c = 0xff & SNX (message);
+ while ((putc (c,df) != EOF) && --i);
+ /* get next message */
+ if (i || !MM_APPEND (af) (stream,data,&flags,&date,&message)) ret = NIL;
+ }
+ } while (ret && message);
+ /* if error... */
+ if (!ret || (fflush (df) == EOF)) {
+ ftruncate (fd,sbuf.st_size);/* revert file */
+ close (fd); /* make sure fclose() doesn't corrupt us */
+ if (errno) {
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ }
+ ret = NIL;
+ }
+ if (ret) tp[0] = time (0) - 1;/* set atime to now-1 if successful copy */
+ /* else preserve \Marked status */
+ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0);
+ tp[1] = sbuf.st_mtime; /* preserve mtime */
+ utime (file,tp); /* set the times */
+ fclose (df); /* close the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ MM_NOCRITICAL (stream); /* release critical */
+ if (ret && mail_parameters (NIL,GET_APPENDUID,NIL))
+ MM_LOG ("Can not return meaningful APPENDUID with this mailbox format",
+ WARN);
+ return ret;
+}
+
+/* Internal routines */
+
+
+/* MTX mail generate file string
+ * Accepts: temporary buffer to write into
+ * mailbox name string
+ * Returns: local file string or NIL if failure
+ */
+
+char *mtx_file (char *dst,char *name)
+{
+ char tmp[MAILTMPLEN];
+ char *s = mailboxfile (dst,name);
+ /* return our standard inbox */
+ return (s && !*s) ? mailboxfile (dst,mtx_isvalid ("~/INBOX",tmp) ?
+ "~/INBOX" : "INBOX.MTX") : s;
+}
+
+/* MTX mail parse mailbox
+ * Accepts: MAIL stream
+ * Returns: T if parse OK
+ * NIL if failure, stream aborted
+ */
+
+long mtx_parse (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = NIL;
+ unsigned char c,*s,*t,*x;
+ char tmp[MAILTMPLEN];
+ unsigned long i,j;
+ long curpos = LOCAL->filesize;
+ long nmsgs = stream->nmsgs;
+ long recent = stream->recent;
+ short added = NIL;
+ short silent = stream->silent;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ if (sbuf.st_size < curpos) { /* sanity check */
+ sprintf (tmp,"Mailbox shrank from %lu to %lu!",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size);
+ MM_LOG (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ stream->silent = T; /* don't pass up exists events yet */
+ while (sbuf.st_size - curpos){/* while there is stuff to parse */
+ /* get to that position in the file */
+ lseek (LOCAL->fd,curpos,L_SET);
+ if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
+ sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size,
+ i ? strerror (errno) : "no data read");
+ MM_LOG (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ LOCAL->buf[i] = '\0'; /* tie off buffer just in case */
+ if (!((s = strchr (LOCAL->buf,'\015')) && (s[1] == '\012'))) {
+ sprintf (tmp,"Unable to find CRLF at %lu in %lu bytes, text: %s",
+ (unsigned long) curpos,i,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ *s = '\0'; /* tie off header line */
+ i = (s + 2) - LOCAL->buf; /* note start of text offset */
+ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
+ sprintf (tmp,"Unable to parse internal header at %lu: %s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ *s++ = '\0'; *t++ = '\0'; /* tie off fields */
+
+ added = T; /* note that a new message was added */
+ /* swell the cache */
+ mail_exists (stream,++nmsgs);
+ /* instantiate an elt for this message */
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ elt->private.uid = ++stream->uid_last;
+ /* note file offset of header */
+ elt->private.special.offset = curpos;
+ /* in case error */
+ elt->private.special.text.size = 0;
+ /* header size not known yet */
+ elt->private.msg.header.text.size = 0;
+ x = s; /* parse the header components */
+ if (mail_parse_date (elt,LOCAL->buf) &&
+ (elt->rfc822_size = strtoul (s,(char **) &s,10)) && (!(s && *s)) &&
+ isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) &&
+ isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) &&
+ isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) &&
+ isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12])
+ elt->private.special.text.size = i;
+ else { /* oops */
+ sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s",
+ curpos,(char *) LOCAL->buf,(char *) x,(char *) t);
+ MM_LOG (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ /* make sure didn't run off end of file */
+ if ((curpos += (elt->rfc822_size + i)) > sbuf.st_size) {
+ sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)",
+ elt->private.special.offset,(unsigned long) curpos,
+ (unsigned long) sbuf.st_size);
+ MM_LOG (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ c = t[10]; /* remember first system flags byte */
+ t[10] = '\0'; /* tie off flags */
+ j = strtoul (t,NIL,8); /* get user flags value */
+ t[10] = c; /* restore first system flags byte */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ /* calculate system flags */
+ if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T;
+ if (j & fDELETED) elt->deleted = T;
+ if (j & fFLAGGED) elt->flagged = T;
+ if (j & fANSWERED) elt->answered = T;
+ if (j & fDRAFT) elt->draft = T;
+ if (!(j & fOLD)) { /* newly arrived message? */
+ elt->recent = T;
+ recent++; /* count up a new recent message */
+ /* mark it as old */
+ mtx_update_status (stream,nmsgs,NIL);
+ }
+ }
+ fsync (LOCAL->fd); /* make sure all the fOLD flags take */
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */
+ LOCAL->filetime = sbuf.st_mtime;
+ if (added && !stream->rdonly){/* make sure atime updated */
+ time_t tp[2];
+ tp[0] = time (0);
+ tp[1] = LOCAL->filetime;
+ utime (stream->mailbox,tp);
+ }
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
+ mail_recent (stream,recent); /* and of change in recent messages */
+ return LONGT; /* return the winnage */
+}
+
+/* MTX get cache element with status updating from file
+ * Accepts: MAIL stream
+ * message number
+ * Returns: cache element
+ */
+
+MESSAGECACHE *mtx_elt (MAILSTREAM *stream,unsigned long msgno)
+{
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ struct { /* old flags */
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged;
+ old.answered = elt->answered; old.draft = elt->draft;
+ old.user_flags = elt->user_flags;
+ mtx_read_flags (stream,elt);
+ if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
+ (old.flagged != elt->flagged) || (old.answered != elt->answered) ||
+ (old.draft != elt->draft) || (old.user_flags != elt->user_flags))
+ MM_FLAGS (stream,msgno); /* let top level know */
+ return elt;
+}
+
+/* MTX read flags from file
+ * Accepts: MAIL stream
+ * Returns: cache element
+ */
+
+void mtx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ unsigned long i,j;
+ /* noop if readonly and have valid flags */
+ if (stream->rdonly && elt->valid) return;
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 14,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,12) < 0) {
+ sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ /* calculate system flags */
+ i = (((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0');
+ elt->seen = i & fSEEN ? T : NIL; elt->deleted = i & fDELETED ? T : NIL;
+ elt->flagged = i & fFLAGGED ? T : NIL;
+ elt->answered = i & fANSWERED ? T : NIL; elt->draft = i & fDRAFT ? T : NIL;
+ LOCAL->buf[10] = '\0'; /* tie off flags */
+ j = strtoul(LOCAL->buf,NIL,8);/* get user flags value */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ elt->valid = T; /* have valid flags now */
+}
+
+/* MTX update status string
+ * Accepts: MAIL stream
+ * message number
+ * flag saying whether or not to sync
+ */
+
+void mtx_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag)
+{
+ time_t tp[2];
+ struct stat sbuf;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ unsigned long j,k = 0;
+ /* readonly */
+ if (stream->rdonly || !elt->valid) mtx_read_flags (stream,elt);
+ else { /* readwrite */
+ j = elt->user_flags; /* get user flags */
+ /* reverse bits (dontcha wish we had CIRC?) */
+ while (j) k |= 1 << (29 - find_rightmost_bit (&j));
+ /* print new flag string */
+ sprintf (LOCAL->buf,"%010lo%02o",k,(unsigned)
+ (fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)));
+ /* get to that place in the file */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 14,L_SET);
+ /* write new flags */
+ write (LOCAL->fd,LOCAL->buf,12);
+ if (syncflag) { /* sync if requested */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* make sure read is later */
+ utime (stream->mailbox,tp);
+ }
+ }
+}
+
+/* MTX locate header for a message
+ * Accepts: MAIL stream
+ * message number
+ * pointer to returned header size
+ * Returns: position of header in file
+ */
+
+unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size)
+{
+ unsigned long siz;
+ long i = 0;
+ int q = 0;
+ char *s,tmp[MAILTMPLEN];
+ MESSAGECACHE *elt = mtx_elt (stream,msgno);
+ unsigned long ret = elt->private.special.offset +
+ elt->private.special.text.size;
+ /* is header size known? */
+ if (!(*size = elt->private.msg.header.text.size)) {
+ lseek (LOCAL->fd,ret,L_SET);/* get to header position */
+ /* search message for CRLF CRLF */
+ for (siz = 1,s = tmp; siz <= elt->rfc822_size; siz++) {
+ /* read another buffer as necessary */
+ if ((--i <= 0) && /* buffer empty? */
+ (read (LOCAL->fd,s = tmp,
+ i = min (elt->rfc822_size - siz,(long) MAILTMPLEN)) < 0))
+ return ret; /* I/O error? */
+ switch (q) { /* sniff at buffer */
+ case 0: /* first character */
+ q = (*s++ == '\015') ? 1 : 0;
+ break;
+ case 1: /* second character */
+ q = (*s++ == '\012') ? 2 : 0;
+ break;
+ case 2: /* third character */
+ q = (*s++ == '\015') ? 3 : 0;
+ break;
+ case 3: /* fourth character */
+ if (*s++ == '\012') { /* have the sequence? */
+ /* yes, note for later */
+ elt->private.msg.header.text.size = *size = siz;
+ return ret;
+ }
+ q = 0; /* lost... */
+ break;
+ }
+ }
+ /* header consumes entire message */
+ elt->private.msg.header.text.size = *size = elt->rfc822_size;
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/amiga/mx.c b/imap/src/osdep/amiga/mx.c
new file mode 100644
index 00000000..45495279
--- /dev/null
+++ b/imap/src/osdep/amiga/mx.c
@@ -0,0 +1,1287 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MX mail routines
+ *
+ * Author(s): Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 May 1996
+ * Last Edited: 6 January 2008
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "misc.h"
+#include "dummy.h"
+#include "fdstring.h"
+
+/* Index file */
+
+#define MXINDEXNAME "/.mxindex"
+#define MXINDEX(d,s) strcat (mx_file (d,s),MXINDEXNAME)
+
+
+/* MX I/O stream local data */
+
+typedef struct mx_local {
+ int fd; /* file descriptor of open index */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ unsigned long cachedtexts; /* total size of all cached texts */
+ time_t scantime; /* last time directory scanned */
+} MXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MXLOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *mx_valid (char *name);
+int mx_isvalid (char *name,char *tmp);
+int mx_namevalid (char *name);
+void *mx_parameters (long function,void *value);
+long mx_dirfmttest (char *name);
+void mx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+long mx_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz);
+void mx_list (MAILSTREAM *stream,char *ref,char *pat);
+void mx_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long mx_subscribe (MAILSTREAM *stream,char *mailbox);
+long mx_unsubscribe (MAILSTREAM *stream,char *mailbox);
+long mx_create (MAILSTREAM *stream,char *mailbox);
+long mx_delete (MAILSTREAM *stream,char *mailbox);
+long mx_rename (MAILSTREAM *stream,char *old,char *newname);
+int mx_rename_work (char *src,size_t srcl,char *dst,size_t dstl,char *name);
+MAILSTREAM *mx_open (MAILSTREAM *stream);
+void mx_close (MAILSTREAM *stream,long options);
+void mx_fast (MAILSTREAM *stream,char *sequence,long flags);
+char *mx_fast_work (MAILSTREAM *stream,MESSAGECACHE *elt);
+char *mx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags);
+long mx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void mx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+void mx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long mx_ping (MAILSTREAM *stream);
+void mx_check (MAILSTREAM *stream);
+long mx_expunge (MAILSTREAM *stream,char *sequence,long options);
+long mx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,
+ long options);
+long mx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+long mx_append_msg (MAILSTREAM *stream,char *flags,MESSAGECACHE *elt,
+ STRING *st,SEARCHSET *set);
+
+int mx_select (struct direct *name);
+int mx_numsort (const void *d1,const void *d2);
+char *mx_file (char *dst,char *name);
+long mx_lockindex (MAILSTREAM *stream);
+void mx_unlockindex (MAILSTREAM *stream);
+void mx_setdate (char *file,MESSAGECACHE *elt);
+
+
+/* MX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mxdriver = {
+ "mx", /* driver name */
+ /* driver flags */
+ DR_MAIL|DR_LOCAL|DR_NOFAST|DR_CRLF|DR_LOCKING|DR_DIRFMT,
+ (DRIVER *) NIL, /* next driver */
+ mx_valid, /* mailbox is valid for us */
+ mx_parameters, /* manipulate parameters */
+ mx_scan, /* scan mailboxes */
+ mx_list, /* find mailboxes */
+ mx_lsub, /* find subscribed mailboxes */
+ mx_subscribe, /* subscribe to mailbox */
+ mx_unsubscribe, /* unsubscribe from mailbox */
+ mx_create, /* create mailbox */
+ mx_delete, /* delete mailbox */
+ mx_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ mx_open, /* open mailbox */
+ mx_close, /* close mailbox */
+ mx_fast, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mx_header, /* fetch message header only */
+ mx_text, /* fetch message body only */
+ NIL, /* fetch partial message test */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ mx_flag, /* modify flags */
+ mx_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mx_ping, /* ping mailbox to see if still alive */
+ mx_check, /* check for new messages */
+ mx_expunge, /* expunge deleted messages */
+ mx_copy, /* copy messages to another mailbox */
+ mx_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mxproto = {&mxdriver};
+
+/* MX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mx_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return mx_isvalid (name,tmp) ? &mxdriver : NIL;
+}
+
+
+/* MX mail test for valid mailbox
+ * Accepts: mailbox name
+ * temporary buffer to use
+ * Returns: T if valid, NIL otherwise with errno holding dir stat error
+ */
+
+int mx_isvalid (char *name,char *tmp)
+{
+ struct stat sbuf;
+ errno = NIL; /* zap error */
+ if ((strlen (name) <= NETMAXMBX) && *mx_file (tmp,name) &&
+ !stat (tmp,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) {
+ /* name is directory; is it mx? */
+ if (!stat (MXINDEX (tmp,name),&sbuf) &&
+ ((sbuf.st_mode & S_IFMT) == S_IFREG)) return T;
+ errno = NIL; /* directory but not mx */
+ }
+ else if (!compare_cstring (name,"INBOX")) errno = NIL;
+ return NIL;
+}
+
+
+/* MX mail test for valid mailbox
+ * Accepts: mailbox name
+ * Returns: T if valid, NIL otherwise
+ */
+
+int mx_namevalid (char *name)
+{
+ char *s = (*name == '/') ? name + 1 : name;
+ while (s && *s) { /* make sure valid name */
+ if (isdigit (*s)) s++; /* digit, check this node further... */
+ else if (*s == '/') break; /* all digit node, barf */
+ /* non-digit, skip to next node or return */
+ else if (!((s = strchr (s+1,'/')) && *++s)) return T;
+ }
+ return NIL; /* all numeric or empty node */
+}
+
+/* MX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mx_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = mailboxfile ((char *) value,"~/INBOX");
+ break;
+ case GET_DIRFMTTEST:
+ ret = (void *) mx_dirfmttest;
+ break;
+ case GET_SCANCONTENTS:
+ ret = (void *) mx_scan_contents;
+ break;
+ }
+ return ret;
+}
+
+
+/* MX test for directory format internal node
+ * Accepts: candidate node name
+ * Returns: T if internal name, NIL otherwise
+ */
+
+long mx_dirfmttest (char *name)
+{
+ int c;
+ /* success if index name or all-numberic */
+ if (strcmp (name,MXINDEXNAME+1))
+ while (c = *name++) if (!isdigit (c)) return NIL;
+ return LONGT;
+}
+
+/* MX scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* MX scan mailbox for contents
+ * Accepts: mailbox name
+ * desired contents
+ * contents size
+ * file size (ignored)
+ * Returns: NIL if contents not found, T if found
+ */
+
+long mx_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz)
+{
+ long i,nfiles;
+ void *a;
+ char *s;
+ long ret = NIL;
+ size_t namelen = strlen (name);
+ struct stat sbuf;
+ struct direct **names = NIL;
+ if ((nfiles = scandir (name,&names,mx_select,mx_numsort)) > 0)
+ for (i = 0; i < nfiles; ++i) {
+ if (!ret) {
+ sprintf (s = (char *) fs_get (namelen + strlen (names[i]->d_name) + 2),
+ "%s/%s",name,names[i]->d_name);
+ if (!stat (s,&sbuf) && (csiz <= sbuf.st_size))
+ ret = dummy_scan_contents (s,contents,csiz,sbuf.st_size);
+ fs_give ((void **) &s);
+ }
+ fs_give ((void **) &names[i]);
+ }
+ /* free directory list */
+ if (a = (void *) names) fs_give ((void **) &a);
+ return ret;
+}
+
+/* MX list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mx_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* MX list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mx_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* MX mail subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long mx_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return sm_subscribe (mailbox);
+}
+
+
+/* MX mail unsubscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to delete from subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long mx_unsubscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return sm_unsubscribe (mailbox);
+}
+
+/* MX mail create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mx_create (MAILSTREAM *stream,char *mailbox)
+{
+ DRIVER *test;
+ int fd;
+ char *s,tmp[MAILTMPLEN];
+ int mask = umask (0);
+ long ret = NIL;
+ if (!mx_namevalid (mailbox)) /* validate name */
+ sprintf (tmp,"Can't create mailbox %.80s: invalid MX-format name",mailbox);
+ /* must not already exist */
+ else if ((test = mail_valid (NIL,mailbox,NIL)) &&
+ strcmp (test->name,"dummy"))
+ sprintf (tmp,"Can't create mailbox %.80s: mailbox already exists",mailbox);
+ /* create directory */
+ else if (!dummy_create_path (stream,MXINDEX (tmp,mailbox),
+ get_dir_protection (mailbox)))
+ sprintf (tmp,"Can't create mailbox %.80s: %s",mailbox,strerror (errno));
+ else { /* success */
+ /* set index protection */
+ set_mbx_protections (mailbox,tmp);
+ /* tie off directory name */
+ *(s = strrchr (tmp,'/') + 1) = '\0';
+ /* set directory protection */
+ set_mbx_protections (mailbox,tmp);
+ ret = LONGT;
+ }
+ umask (mask); /* restore mask */
+ if (!ret) MM_LOG (tmp,ERROR); /* some error */
+ return ret;
+}
+
+/* MX mail delete mailbox
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mx_delete (MAILSTREAM *stream,char *mailbox)
+{
+ DIR *dirp;
+ struct direct *d;
+ char *s;
+ char tmp[MAILTMPLEN];
+ if (!mx_isvalid (mailbox,tmp))
+ sprintf (tmp,"Can't delete mailbox %.80s: no such mailbox",mailbox);
+ /* delete index */
+ else if (unlink (MXINDEX (tmp,mailbox)))
+ sprintf (tmp,"Can't delete mailbox %.80s index: %s",
+ mailbox,strerror (errno));
+ else { /* get directory name */
+ *(s = strrchr (tmp,'/')) = '\0';
+ if (dirp = opendir (tmp)) { /* open directory */
+ *s++ = '/'; /* restore delimiter */
+ /* massacre messages */
+ while (d = readdir (dirp)) if (mx_select (d)) {
+ strcpy (s,d->d_name); /* make path */
+ unlink (tmp); /* sayonara */
+ }
+ closedir (dirp); /* flush directory */
+ *(s = strrchr (tmp,'/')) = '\0';
+ if (rmdir (tmp)) { /* try to remove the directory */
+ sprintf (tmp,"Can't delete name %.80s: %s",mailbox,strerror (errno));
+ MM_LOG (tmp,WARN);
+ }
+ }
+ return T; /* always success */
+ }
+ MM_LOG (tmp,ERROR); /* something failed */
+ return NIL;
+}
+
+/* MX mail rename mailbox
+ * Accepts: MX mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long mx_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ char c,*s,tmp[MAILTMPLEN],tmp1[MAILTMPLEN];
+ struct stat sbuf;
+ if (!mx_isvalid (old,tmp))
+ sprintf (tmp,"Can't rename mailbox %.80s: no such mailbox",old);
+ else if (!mx_namevalid (newname))
+ sprintf (tmp,"Can't rename to mailbox %.80s: invalid MX-format name",
+ newname);
+ /* new mailbox name must not be valid */
+ else if (mx_isvalid (newname,tmp))
+ sprintf (tmp,"Can't rename to mailbox %.80s: destination already exists",
+ newname);
+ else {
+ mx_file (tmp,old); /* build old directory name */
+ mx_file (tmp1,newname); /* and new directory name */
+ /* easy if not INBOX */
+ if (compare_cstring (old,"INBOX")) {
+ /* found superior to destination name? */
+ if (s = strrchr (mx_file (tmp1,newname),'/')) {
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (tmp1,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,tmp1,get_dir_protection (newname)))
+ return NIL;
+ *s = c; /* restore full name */
+ }
+ if (!rename (tmp,tmp1)) return LONGT;
+ }
+
+ /* RFC 3501 requires this */
+ else if (dummy_create_path (stream,strcat (tmp1,"/"),
+ get_dir_protection (newname))) {
+ void *a;
+ int i,n,lasterror;
+ struct direct **names = NIL;
+ size_t srcl = strlen (tmp);
+ size_t dstl = strlen (tmp1);
+ /* rename each mx file to new directory */
+ for (i = lasterror = 0,n = scandir (tmp,&names,mx_select,mx_numsort);
+ i < n; ++i) {
+ if (mx_rename_work (tmp,srcl,tmp1,dstl,names[i]->d_name))
+ lasterror = errno;
+ fs_give ((void **) &names[i]);
+ }
+ /* free directory list */
+ if (a = (void *) names) fs_give ((void **) &a);
+ if (lasterror || mx_rename_work (tmp,srcl,tmp1,dstl,MXINDEXNAME+1))
+ errno = lasterror;
+ else return mx_create (NIL,"INBOX");
+ }
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",
+ old,newname,strerror (errno));
+ }
+ MM_LOG (tmp,ERROR); /* something failed */
+ return NIL;
+}
+
+
+/* MX rename worker routine
+ * Accepts: source directory name
+ * source directory name length
+ * destination directory name
+ * destination directory name length
+ * name of node to move
+ * Returns: zero if success, non-zero if error
+ */
+
+int mx_rename_work (char *src,size_t srcl,char *dst,size_t dstl,char *name)
+{
+ int ret;
+ size_t len = strlen (name);
+ char *s = (char *) fs_get (srcl + len + 2);
+ char *d = (char *) fs_get (dstl + len + 1);
+ sprintf (s,"%s/%s",src,name);
+ sprintf (d,"%s%s",dst,name);
+ ret = rename (s,d);
+ fs_give ((void **) &s);
+ fs_give ((void **) &d);
+ return ret;
+}
+
+/* MX mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mx_open (MAILSTREAM *stream)
+{
+ char tmp[MAILTMPLEN];
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return user_flags (&mxproto);
+ if (stream->local) fatal ("mx recycle stream");
+ stream->local = fs_get (sizeof (MXLOCAL));
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ mx_file (tmp,stream->mailbox);/* get directory name */
+ /* canonicalize mailbox name */
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ /* make temporary buffer */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ LOCAL->scantime = 0; /* not scanned yet */
+ LOCAL->fd = -1; /* no index yet */
+ LOCAL->cachedtexts = 0; /* no cached texts */
+ stream->sequence++; /* bump sequence number */
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ if (mx_ping (stream) && !(stream->nmsgs || stream->silent))
+ MM_LOG ("Mailbox is empty",(long) NIL);
+ stream->perm_seen = stream->perm_deleted = stream->perm_flagged =
+ stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ stream->kwd_create = (stream->user_flags[NUSERFLAGS-1] || stream->rdonly) ?
+ NIL : T; /* can we create new user flags? */
+ return stream; /* return stream to caller */
+}
+
+/* MX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mx_close (MAILSTREAM *stream,long options)
+{
+ if (LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ if (options & CL_EXPUNGE) mx_expunge (stream,NIL,NIL);
+ /* free local scratch buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ stream->silent = silent; /* reset silent state */
+ }
+}
+
+/* MX mail fetch fast information
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ */
+
+void mx_fast (MAILSTREAM *stream,char *sequence,long flags)
+{
+ unsigned long i;
+ MESSAGECACHE *elt;
+ if (stream && LOCAL &&
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) mx_fast_work (stream,elt);
+}
+
+
+/* MX mail fetch fast information
+ * Accepts: MAIL stream
+ * message cache element
+ * Returns: name of message file
+ */
+
+char *mx_fast_work (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ struct stat sbuf;
+ struct tm *tm;
+ /* build message file name */
+ sprintf (LOCAL->buf,"%s/%lu",stream->mailbox,elt->private.uid);
+ /* have size yet? */
+ if (!elt->rfc822_size && !stat (LOCAL->buf,&sbuf)) {
+ /* make plausible IMAPish date string */
+ tm = gmtime (&sbuf.st_mtime);
+ elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1;
+ elt->year = tm->tm_year + 1900 - BASEYEAR;
+ elt->hours = tm->tm_hour; elt->minutes = tm->tm_min;
+ elt->seconds = tm->tm_sec;
+ elt->zhours = 0; elt->zminutes = 0; elt->zoccident = 0;
+ elt->rfc822_size = sbuf.st_size;
+ }
+ return (char *) LOCAL->buf; /* return file name */
+}
+
+/* MX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *mx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags)
+{
+ unsigned long i;
+ int fd;
+ MESSAGECACHE *elt;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ elt = mail_elt (stream,msgno);/* get elt */
+ if (!elt->private.msg.header.text.data) {
+ /* purge cache if too big */
+ if (LOCAL->cachedtexts > max (stream->nmsgs * 4096,2097152)) {
+ mail_gc (stream,GC_TEXTS);/* just can't keep that much */
+ LOCAL->cachedtexts = 0;
+ }
+ if ((fd = open (mx_fast_work (stream,elt),O_RDONLY,NIL)) < 0) return "";
+ /* is buffer big enough? */
+ if (elt->rfc822_size > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = elt->rfc822_size) + 1);
+ }
+ /* slurp message */
+ read (fd,LOCAL->buf,elt->rfc822_size);
+ /* tie off file */
+ LOCAL->buf[elt->rfc822_size] = '\0';
+ close (fd); /* flush message file */
+ /* find end of header */
+ if (elt->rfc822_size < 4) i = 0;
+ else for (i = 4; (i < elt->rfc822_size) &&
+ !((LOCAL->buf[i - 4] == '\015') &&
+ (LOCAL->buf[i - 3] == '\012') &&
+ (LOCAL->buf[i - 2] == '\015') &&
+ (LOCAL->buf[i - 1] == '\012')); i++);
+ /* copy header */
+ cpytxt (&elt->private.msg.header.text,LOCAL->buf,i);
+ cpytxt (&elt->private.msg.text.text,LOCAL->buf+i,elt->rfc822_size - i);
+ /* add to cached size */
+ LOCAL->cachedtexts += elt->rfc822_size;
+ }
+ *length = elt->private.msg.header.text.size;
+ return (char *) elt->private.msg.header.text.data;
+}
+
+/* MX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ unsigned long i;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mail_elt (stream,msgno);
+ /* snarf message if don't have it yet */
+ if (!elt->private.msg.text.text.data) {
+ mx_header (stream,msgno,&i,flags);
+ if (!elt->private.msg.text.text.data) return NIL;
+ }
+ /* mark as seen */
+ if (!(flags & FT_PEEK) && mx_lockindex (stream)) {
+ elt->seen = T;
+ mx_unlockindex (stream);
+ MM_FLAGS (stream,msgno);
+ }
+ INIT (bs,mail_string,elt->private.msg.text.text.data,
+ elt->private.msg.text.text.size);
+ return T;
+}
+
+/* MX mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ */
+
+void mx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ mx_unlockindex (stream); /* finished with index */
+}
+
+
+/* MX per-message modify flags
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void mx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ mx_lockindex (stream); /* lock index if not already locked */
+}
+
+/* MX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long mx_ping (MAILSTREAM *stream)
+{
+ MAILSTREAM *sysibx = NIL;
+ MESSAGECACHE *elt,*selt;
+ struct stat sbuf;
+ char *s,tmp[MAILTMPLEN];
+ int fd;
+ unsigned long i,j,r,old;
+ long nmsgs = stream->nmsgs;
+ long recent = stream->recent;
+ int silent = stream->silent;
+ if (stat (stream->mailbox,&sbuf)) return NIL;
+ stream->silent = T; /* don't pass up exists events yet */
+ if (sbuf.st_ctime != LOCAL->scantime) {
+ struct direct **names = NIL;
+ long nfiles = scandir (stream->mailbox,&names,mx_select,mx_numsort);
+ if (nfiles < 0) nfiles = 0; /* in case error */
+ old = stream->uid_last;
+ /* note scanned now */
+ LOCAL->scantime = sbuf.st_ctime;
+ /* scan directory */
+ for (i = 0; i < nfiles; ++i) {
+ /* if newly seen, add to list */
+ if ((j = atoi (names[i]->d_name)) > old) {
+ /* swell the cache */
+ mail_exists (stream,++nmsgs);
+ stream->uid_last = (elt = mail_elt (stream,nmsgs))->private.uid = j;
+ elt->valid = T; /* note valid flags */
+ if (old) { /* other than the first pass? */
+ elt->recent = T; /* yup, mark as recent */
+ recent++; /* bump recent count */
+ }
+ }
+ fs_give ((void **) &names[i]);
+ }
+ /* free directory */
+ if (s = (void *) names) fs_give ((void **) &s);
+ }
+ stream->nmsgs = nmsgs; /* don't upset mail_uid() */
+
+ /* if INBOX, snarf from system INBOX */
+ if (mx_lockindex (stream) && stream->inbox &&
+ !strcmp (sysinbox (),stream->mailbox)) {
+ old = stream->uid_last;
+ MM_CRITICAL (stream); /* go critical */
+ /* see if anything in system inbox */
+ if (!stat (sysinbox (),&sbuf) && sbuf.st_size &&
+ (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) &&
+ !sysibx->rdonly && (r = sysibx->nmsgs)) {
+ for (i = 1; i <= r; ++i) {/* for each message in sysinbox mailbox */
+ /* build file name we will use */
+ sprintf (LOCAL->buf,"%s/%lu",stream->mailbox,++old);
+ /* snarf message from Berkeley mailbox */
+ selt = mail_elt (sysibx,i);
+ if (((fd = open (LOCAL->buf,O_WRONLY|O_CREAT|O_EXCL,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL)))
+ >= 0) &&
+ (s = mail_fetchheader_full (sysibx,i,NIL,&j,FT_PEEK)) &&
+ (write (fd,s,j) == j) &&
+ (s = mail_fetchtext_full (sysibx,i,&j,FT_PEEK)) &&
+ (write (fd,s,j) == j) && !fsync (fd) && !close (fd)) {
+ /* swell the cache */
+ mail_exists (stream,++nmsgs);
+ stream->uid_last = /* create new elt, note its file number */
+ (elt = mail_elt (stream,nmsgs))->private.uid = old;
+ recent++; /* bump recent count */
+ /* set up initial flags and date */
+ elt->valid = elt->recent = T;
+ elt->seen = selt->seen;
+ elt->deleted = selt->deleted;
+ elt->flagged = selt->flagged;
+ elt->answered = selt->answered;
+ elt->draft = selt->draft;
+ elt->day = selt->day;elt->month = selt->month;elt->year = selt->year;
+ elt->hours = selt->hours;elt->minutes = selt->minutes;
+ elt->seconds = selt->seconds;
+ elt->zhours = selt->zhours; elt->zminutes = selt->zminutes;
+ elt->zoccident = selt->zoccident;
+ mx_setdate (LOCAL->buf,elt);
+ sprintf (tmp,"%lu",i);/* delete it from the sysinbox */
+ mail_flag (sysibx,tmp,"\\Deleted",ST_SET);
+ }
+ else { /* failed to snarf */
+ if (fd) { /* did it ever get opened? */
+ close (fd); /* close descriptor */
+ unlink (LOCAL->buf);/* flush this file */
+ }
+ sprintf (tmp,"Message copy to MX mailbox failed: %.80s",
+ s,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ r = 0; /* stop the snarf in its tracks */
+ }
+ }
+ /* update scan time */
+ if (!stat (stream->mailbox,&sbuf)) LOCAL->scantime = sbuf.st_ctime;
+ mail_expunge (sysibx); /* now expunge all those messages */
+ }
+ if (sysibx) mail_close (sysibx);
+ MM_NOCRITICAL (stream); /* release critical */
+ }
+ mx_unlockindex (stream); /* done with index */
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of mailbox size */
+ mail_recent (stream,recent);
+ return T; /* return that we are alive */
+}
+
+/* MX mail check mailbox
+ * Accepts: MAIL stream
+ */
+
+void mx_check (MAILSTREAM *stream)
+{
+ if (mx_ping (stream)) MM_LOG ("Check completed",(long) NIL);
+}
+
+
+/* MX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long mx_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ MESSAGECACHE *elt;
+ unsigned long i = 1;
+ unsigned long n = 0;
+ unsigned long recent = stream->recent;
+ if (ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ mx_lockindex (stream)) { /* lock the index */
+ MM_CRITICAL (stream); /* go critical */
+ while (i <= stream->nmsgs) {/* for each message */
+ elt = mail_elt (stream,i);/* if deleted, need to trash it */
+ if (elt->deleted && (sequence ? elt->sequence : T)) {
+ sprintf (LOCAL->buf,"%s/%lu",stream->mailbox,elt->private.uid);
+ if (unlink (LOCAL->buf)) {/* try to delete the message */
+ sprintf (LOCAL->buf,"Expunge of message %lu failed, aborted: %s",i,
+ strerror (errno));
+ MM_LOG (LOCAL->buf,(long) NIL);
+ break;
+ }
+ /* note uncached */
+ LOCAL->cachedtexts -= ((elt->private.msg.header.text.data ?
+ elt->private.msg.header.text.size : 0) +
+ (elt->private.msg.text.text.data ?
+ elt->private.msg.text.text.size : 0));
+ mail_gc_msg (&elt->private.msg,GC_ENV | GC_TEXTS);
+ if(elt->recent)--recent;/* if recent, note one less recent message */
+ mail_expunged(stream,i);/* notify upper levels */
+ n++; /* count up one more expunged message */
+ }
+ else i++; /* otherwise try next message */
+ }
+ if (n) { /* output the news if any expunged */
+ sprintf (LOCAL->buf,"Expunged %lu messages",n);
+ MM_LOG (LOCAL->buf,(long) NIL);
+ }
+ else MM_LOG ("No messages deleted, so no update needed",(long) NIL);
+ MM_NOCRITICAL (stream); /* release critical */
+ mx_unlockindex (stream); /* finished with index */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ }
+ return ret;
+}
+
+/* MX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if copy successful, else NIL
+ */
+
+long mx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ FDDATA d;
+ STRING st;
+ MESSAGECACHE *elt;
+ MAILSTREAM *astream;
+ struct stat sbuf;
+ int fd;
+ unsigned long i,j,uid,uidv;
+ char *t,tmp[MAILTMPLEN];
+ long ret;
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ /* make sure valid mailbox */
+ if (!mx_valid (mailbox)) switch (errno) {
+ case NIL: /* no error in stat() */
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a MX-format mailbox: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ default: /* some stat() error */
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ }
+ /* copy the messages */
+ if (!(ret = ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))));
+ /* acquire stream to append to */
+ else if (!(astream = mail_open (NIL,mailbox,OP_SILENT))) {
+ MM_LOG ("Can't open copy mailbox",ERROR);
+ ret = NIL;
+ }
+ else {
+ MM_CRITICAL (stream); /* go critical */
+ if (!(ret = mx_lockindex (astream)))
+ MM_LOG ("Message copy failed: unable to lock index",ERROR);
+ else {
+
+ copyuid_t cu = (copyuid_t) mail_parameters (NIL,GET_COPYUID,NIL);
+ SEARCHSET *source = cu ? mail_newsearchset () : NIL;
+ SEARCHSET *dest = cu ? mail_newsearchset () : NIL;
+ for (i = 1,uid = uidv = 0; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ if (ret = ((fd = open (mx_fast_work (stream,elt),O_RDONLY,NIL))
+ >= 0)) {
+ fstat (fd,&sbuf); /* get size of message */
+ d.fd = fd; /* set up file descriptor */
+ d.pos = 0; /* start of file */
+ d.chunk = LOCAL->buf;
+ d.chunksize = CHUNKSIZE;
+ INIT (&st,fd_string,&d,sbuf.st_size);
+ /* init flag string */
+ tmp[0] = tmp[1] = '\0';
+ if (j = elt->user_flags) do
+ if (t = stream->user_flags[find_rightmost_bit (&j)])
+ strcat (strcat (tmp," "),t);
+ while (j);
+ if (elt->seen) strcat (tmp," \\Seen");
+ if (elt->deleted) strcat (tmp," \\Deleted");
+ if (elt->flagged) strcat (tmp," \\Flagged");
+ if (elt->answered) strcat (tmp," \\Answered");
+ if (elt->draft) strcat (tmp," \\Draft");
+ tmp[0] = '('; /* open list */
+ strcat (tmp,")"); /* close list */
+ if (ret = mx_append_msg (astream,tmp,elt,&st,dest)) {
+ /* add to source set if needed */
+ if (source) mail_append_set (source,mail_uid (stream,i));
+ /* delete if doing a move */
+ if (options & CP_MOVE) elt->deleted = T;
+ }
+ }
+ }
+ /* return sets if doing COPYUID */
+ if (cu && ret) (*cu) (stream,mailbox,astream->uid_validity,source,dest);
+ else { /* flush any sets we may have built */
+ mail_free_searchset (&source);
+ mail_free_searchset (&dest);
+ }
+ mx_unlockindex (astream); /* unlock index */
+ }
+ MM_NOCRITICAL (stream);
+ mail_close (astream); /* finished with append stream */
+ }
+ return ret; /* return success */
+}
+
+/* MX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ MESSAGECACHE elt;
+ MAILSTREAM *astream;
+ char *flags,*date,tmp[MAILTMPLEN];
+ STRING *message;
+ long ret = LONGT;
+ /* default stream to prototype */
+ if (!stream) stream = user_flags (&mxproto);
+ /* N.B.: can't use LOCAL->buf for tmp */
+ /* make sure valid mailbox */
+ if (!mx_isvalid (mailbox,tmp)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (!compare_cstring (mailbox,"INBOX")) mx_create (NIL,"INBOX");
+ else {
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* falls through */
+ case 0: /* merely empty file? */
+ break;
+ case EINVAL:
+ sprintf (tmp,"Invalid MX-format mailbox name: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a MX-format mailbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+
+ /* get first message */
+ if (!MM_APPEND (af) (stream,data,&flags,&date,&message)) return NIL;
+ if (!(astream = mail_open (NIL,mailbox,OP_SILENT))) {
+ MM_LOG ("Can't open append mailbox",ERROR);
+ return NIL;
+ }
+ MM_CRITICAL (astream); /* go critical */
+ /* lock the index */
+ if (!(ret = mx_lockindex (astream)))
+ MM_LOG ("Message append failed: unable to lock index",ERROR);
+ else {
+ appenduid_t au = (appenduid_t) mail_parameters (NIL,GET_APPENDUID,NIL);
+ SEARCHSET *dst = au ? mail_newsearchset () : NIL;
+ do {
+ /* guard against zero-length */
+ if (!(ret = SIZE (message)))
+ MM_LOG ("Append of zero-length message",ERROR);
+ else if (date && !(ret = mail_parse_date (&elt,date))) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ MM_LOG (tmp,ERROR);
+ }
+ else ret = mx_append_msg (astream,flags,date ? &elt : NIL,message,dst)&&
+ MM_APPEND (af) (stream,data,&flags,&date,&message);
+ } while (ret && message);
+ /* return sets if doing APPENDUID */
+ if (au && ret) (*au) (mailbox,astream->uid_validity,dst);
+ else mail_free_searchset (&dst);
+ mx_unlockindex (astream); /* unlock index */
+ }
+ MM_NOCRITICAL (astream); /* release critical */
+ mail_close (astream);
+ return ret;
+}
+
+/* MX mail append single message
+ * Accepts: MAIL stream
+ * flags for new message if non-NIL
+ * elt with source date if non-NIL
+ * stringstruct of message text
+ * searchset to place UID
+ * Returns: T if success, NIL if failure
+ */
+
+long mx_append_msg (MAILSTREAM *stream,char *flags,MESSAGECACHE *elt,
+ STRING *st,SEARCHSET *set)
+{
+ char tmp[MAILTMPLEN];
+ int fd;
+ unsigned long uf;
+ long f = mail_parse_flags (stream,flags,&uf);
+ /* make message file name */
+ sprintf (tmp,"%s/%lu",stream->mailbox,++stream->uid_last);
+ if ((fd = open (tmp,O_WRONLY|O_CREAT|O_EXCL,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL))) < 0) {
+ sprintf (tmp,"Can't create append message: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ while (SIZE (st)) { /* copy the file */
+ if (st->cursize && (write (fd,st->curpos,st->cursize) < 0)) {
+ unlink (tmp); /* delete file */
+ close (fd); /* close the file */
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ SETPOS (st,GETPOS (st) + st->cursize);
+ }
+ close (fd); /* close the file */
+ if (elt) mx_setdate (tmp,elt);/* set file date */
+ /* swell the cache */
+ mail_exists (stream,++stream->nmsgs);
+ /* copy flags */
+ mail_append_set (set,(elt = mail_elt (stream,stream->nmsgs))->private.uid =
+ stream->uid_last);
+ if (f&fSEEN) elt->seen = T;
+ if (f&fDELETED) elt->deleted = T;
+ if (f&fFLAGGED) elt->flagged = T;
+ if (f&fANSWERED) elt->answered = T;
+ if (f&fDRAFT) elt->draft = T;
+ elt->user_flags |= uf;
+ return LONGT;
+}
+
+/* Internal routines */
+
+
+/* MX file name selection test
+ * Accepts: candidate directory entry
+ * Returns: T to use file name, NIL to skip it
+ */
+
+int mx_select (struct direct *name)
+{
+ char c;
+ char *s = name->d_name;
+ while (c = *s++) if (!isdigit (c)) return NIL;
+ return T;
+}
+
+
+/* MX file name comparision
+ * Accepts: first candidate directory entry
+ * second candidate directory entry
+ * Returns: negative if d1 < d2, 0 if d1 == d2, postive if d1 > d2
+ */
+
+int mx_numsort (const void *d1,const void *d2)
+{
+ return atoi ((*(struct direct **) d1)->d_name) -
+ atoi ((*(struct direct **) d2)->d_name);
+}
+
+
+/* MX mail build file name
+ * Accepts: destination string
+ * source
+ * Returns: destination
+ */
+
+char *mx_file (char *dst,char *name)
+{
+ char *s;
+ /* empty string if mailboxfile fails */
+ if (!mailboxfile (dst,name)) *dst = '\0';
+ /* driver-selected INBOX */
+ else if (!*dst) mailboxfile (dst,"~/INBOX");
+ /* tie off unnecessary trailing / */
+ else if ((s = strrchr (dst,'/')) && !s[1]) *s = '\0';
+ return dst;
+}
+
+/* MX read and lock index
+ * Accepts: MAIL stream
+ * Returns: T if success, NIL if failure
+ */
+
+long mx_lockindex (MAILSTREAM *stream)
+{
+ unsigned long uf,sf,uid;
+ int k = 0;
+ unsigned long msgno = 1;
+ struct stat sbuf;
+ char *s,*t,*idx,tmp[2*MAILTMPLEN];
+ MESSAGECACHE *elt;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if ((LOCAL->fd < 0) && /* get index file, no-op if already have it */
+ (LOCAL->fd = open (strcat (strcpy (tmp,stream->mailbox),MXINDEXNAME),
+ O_RDWR|O_CREAT,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL)))
+ >= 0) {
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_EX); /* get exclusive lock */
+ (*bn) (BLOCK_NONE,NIL);
+ fstat (LOCAL->fd,&sbuf); /* get size of index */
+ /* slurp index */
+ read (LOCAL->fd,s = idx = (char *) fs_get (sbuf.st_size + 1),sbuf.st_size);
+ idx[sbuf.st_size] = '\0'; /* tie off index */
+ /* parse index */
+ if (sbuf.st_size) while (s && *s) switch (*s) {
+ case 'V': /* UID validity record */
+ stream->uid_validity = strtoul (s+1,&s,16);
+ break;
+ case 'L': /* UID last record */
+ stream->uid_last = strtoul (s+1,&s,16);
+ break;
+ case 'K': /* keyword */
+ /* find end of keyword */
+ if (s = strchr (t = ++s,'\n')) {
+ *s++ = '\0'; /* tie off keyword */
+ /* copy keyword */
+ if ((k < NUSERFLAGS) && !stream->user_flags[k] &&
+ (strlen (t) <= MAXUSERFLAG)) stream->user_flags[k] = cpystr (t);
+ k++; /* one more keyword */
+ }
+ break;
+
+ case 'M': /* message status record */
+ uid = strtoul (s+1,&s,16);/* get UID for this message */
+ if (*s == ';') { /* get user flags */
+ uf = strtoul (s+1,&s,16);
+ if (*s == '.') { /* get system flags */
+ sf = strtoul (s+1,&s,16);
+ while ((msgno <= stream->nmsgs) && (mail_uid (stream,msgno) < uid))
+ msgno++; /* find message number for this UID */
+ if ((msgno <= stream->nmsgs) && (mail_uid (stream,msgno) == uid)) {
+ (elt = mail_elt (stream,msgno))->valid = T;
+ elt->user_flags=uf; /* set user and system flags in elt */
+ if (sf & fSEEN) elt->seen = T;
+ if (sf & fDELETED) elt->deleted = T;
+ if (sf & fFLAGGED) elt->flagged = T;
+ if (sf & fANSWERED) elt->answered = T;
+ if (sf & fDRAFT) elt->draft = T;
+ }
+ break;
+ }
+ }
+ default: /* bad news */
+ sprintf (tmp,"Error in index: %.80s",s);
+ MM_LOG (tmp,ERROR);
+ *s = NIL; /* ignore remainder of index */
+ }
+ else { /* new index */
+ stream->uid_validity = time (0);
+ user_flags (stream); /* init stream with default user flags */
+ }
+ fs_give ((void **) &idx); /* flush index */
+ }
+ return (LOCAL->fd >= 0) ? T : NIL;
+}
+
+/* MX write and unlock index
+ * Accepts: MAIL stream
+ */
+
+#define MXIXBUFLEN 2048
+
+void mx_unlockindex (MAILSTREAM *stream)
+{
+ unsigned long i,j;
+ off_t size = 0;
+ char *s,tmp[MXIXBUFLEN + 64];
+ MESSAGECACHE *elt;
+ if (LOCAL->fd >= 0) {
+ lseek (LOCAL->fd,0,L_SET); /* rewind file */
+ /* write header */
+ sprintf (s = tmp,"V%08lxL%08lx",stream->uid_validity,stream->uid_last);
+ for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i]; ++i)
+ sprintf (s += strlen (s),"K%s\n",stream->user_flags[i]);
+ /* write messages */
+ for (i = 1; i <= stream->nmsgs; i++) {
+ /* filled buffer? */
+ if (((s += strlen (s)) - tmp) > MXIXBUFLEN) {
+ write (LOCAL->fd,tmp,j = s - tmp);
+ size += j;
+ *(s = tmp) = '\0'; /* dump out and restart buffer */
+ }
+ elt = mail_elt (stream,i);
+ sprintf(s,"M%08lx;%08lx.%04x",elt->private.uid,elt->user_flags,(unsigned)
+ ((fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)));
+ }
+ /* write tail end of buffer */
+ if ((s += strlen (s)) != tmp) {
+ write (LOCAL->fd,tmp,j = s - tmp);
+ size += j;
+ }
+ ftruncate (LOCAL->fd,size);
+ flock (LOCAL->fd,LOCK_UN); /* unlock the index */
+ close (LOCAL->fd); /* finished with file */
+ LOCAL->fd = -1; /* no index now */
+ }
+}
+
+/* Set date for message
+ * Accepts: file name
+ * elt containing date
+ */
+
+void mx_setdate (char *file,MESSAGECACHE *elt)
+{
+ time_t tp[2];
+ tp[0] = time (0); /* atime is now */
+ tp[1] = mail_longdate (elt); /* modification time */
+ utime (file,tp); /* set the times */
+}
diff --git a/imap/src/osdep/amiga/news.c b/imap/src/osdep/amiga/news.c
new file mode 100644
index 00000000..4cf5bb70
--- /dev/null
+++ b/imap/src/osdep/amiga/news.c
@@ -0,0 +1,738 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: News routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 4 September 1991
+ * Last Edited: 30 January 2007
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "misc.h"
+#include "newsrc.h"
+#include "fdstring.h"
+
+
+/* news_load_message() flags */
+
+#define NLM_HEADER 0x1 /* load message text */
+#define NLM_TEXT 0x2 /* load message text */
+
+/* NEWS I/O stream local data */
+
+typedef struct news_local {
+ unsigned int dirty : 1; /* disk copy of .newsrc needs updating */
+ char *dir; /* spool directory name */
+ char *name; /* local mailbox name */
+ unsigned char buf[CHUNKSIZE]; /* scratch buffer */
+ unsigned long cachedtexts; /* total size of all cached texts */
+} NEWSLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((NEWSLOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *news_valid (char *name);
+DRIVER *news_isvalid (char *name,char *mbx);
+void *news_parameters (long function,void *value);
+void news_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void news_list (MAILSTREAM *stream,char *ref,char *pat);
+void news_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long news_canonicalize (char *ref,char *pat,char *pattern);
+long news_subscribe (MAILSTREAM *stream,char *mailbox);
+long news_unsubscribe (MAILSTREAM *stream,char *mailbox);
+long news_create (MAILSTREAM *stream,char *mailbox);
+long news_delete (MAILSTREAM *stream,char *mailbox);
+long news_rename (MAILSTREAM *stream,char *old,char *newname);
+MAILSTREAM *news_open (MAILSTREAM *stream);
+int news_select (struct direct *name);
+int news_numsort (const void *d1,const void *d2);
+void news_close (MAILSTREAM *stream,long options);
+void news_fast (MAILSTREAM *stream,char *sequence,long flags);
+void news_flags (MAILSTREAM *stream,char *sequence,long flags);
+void news_load_message (MAILSTREAM *stream,unsigned long msgno,long flags);
+char *news_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long news_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void news_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long news_ping (MAILSTREAM *stream);
+void news_check (MAILSTREAM *stream);
+long news_expunge (MAILSTREAM *stream,char *sequence,long options);
+long news_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long news_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+/* News routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER newsdriver = {
+ "news", /* driver name */
+ /* driver flags */
+ DR_NEWS|DR_READONLY|DR_NOFAST|DR_NAMESPACE|DR_NONEWMAIL|DR_DIRFMT,
+ (DRIVER *) NIL, /* next driver */
+ news_valid, /* mailbox is valid for us */
+ news_parameters, /* manipulate parameters */
+ news_scan, /* scan mailboxes */
+ news_list, /* find mailboxes */
+ news_lsub, /* find subscribed mailboxes */
+ news_subscribe, /* subscribe to mailbox */
+ news_unsubscribe, /* unsubscribe from mailbox */
+ news_create, /* create mailbox */
+ news_delete, /* delete mailbox */
+ news_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ news_open, /* open mailbox */
+ news_close, /* close mailbox */
+ news_fast, /* fetch message "fast" attributes */
+ news_flags, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ news_header, /* fetch message header */
+ news_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ NIL, /* modify flags */
+ news_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ news_ping, /* ping mailbox to see if still alive */
+ news_check, /* check for new messages */
+ news_expunge, /* expunge deleted messages */
+ news_copy, /* copy messages to another mailbox */
+ news_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM newsproto = {&newsdriver};
+
+/* News validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *news_valid (char *name)
+{
+ int fd;
+ char *s,*t,*u;
+ struct stat sbuf;
+ if ((name[0] == '#') && (name[1] == 'n') && (name[2] == 'e') &&
+ (name[3] == 'w') && (name[4] == 's') && (name[5] == '.') &&
+ !strchr (name,'/') &&
+ !stat ((char *) mail_parameters (NIL,GET_NEWSSPOOL,NIL),&sbuf) &&
+ ((fd = open ((char *) mail_parameters (NIL,GET_NEWSACTIVE,NIL),O_RDONLY,
+ NIL)) >= 0)) {
+ fstat (fd,&sbuf); /* get size of active file */
+ /* slurp in active file */
+ read (fd,t = s = (char *) fs_get (sbuf.st_size+1),sbuf.st_size);
+ s[sbuf.st_size] = '\0'; /* tie off file */
+ close (fd); /* flush file */
+ while (*t && (u = strchr (t,' '))) {
+ *u++ = '\0'; /* tie off at end of name */
+ if (!strcmp (name+6,t)) {
+ fs_give ((void **) &s); /* flush data */
+ return &newsdriver;
+ }
+ t = 1 + strchr (u,'\n'); /* next line */
+ }
+ fs_give ((void **) &s); /* flush data */
+ }
+ return NIL; /* return status */
+}
+
+/* News manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *news_parameters (long function,void *value)
+{
+ return (function == GET_NEWSRC) ? env_parameters (function,value) : NIL;
+}
+
+
+/* News scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void news_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ char tmp[MAILTMPLEN];
+ if (news_canonicalize (ref,pat,tmp))
+ mm_log ("Scan not valid for news mailboxes",ERROR);
+}
+
+/* News find list of newsgroups
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void news_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ int fd;
+ int i;
+ char *s,*t,*u,*r,pattern[MAILTMPLEN],name[MAILTMPLEN];
+ struct stat sbuf;
+ if (!pat || !*pat) { /* empty pattern? */
+ if (news_canonicalize (ref,"*",pattern)) {
+ /* tie off name at root */
+ if (s = strchr (pattern,'.')) *++s = '\0';
+ else pattern[0] = '\0';
+ mm_list (stream,'.',pattern,LATT_NOSELECT);
+ }
+ }
+ else if (news_canonicalize (ref,pat,pattern) &&
+ !stat ((char *) mail_parameters (NIL,GET_NEWSSPOOL,NIL),&sbuf) &&
+ ((fd = open ((char *) mail_parameters (NIL,GET_NEWSACTIVE,NIL),
+ O_RDONLY,NIL)) >= 0)) {
+ fstat (fd,&sbuf); /* get file size and read data */
+ read (fd,s = (char *) fs_get (sbuf.st_size + 1),sbuf.st_size);
+ close (fd); /* close file */
+ s[sbuf.st_size] = '\0'; /* tie off string */
+ strcpy (name,"#news."); /* write initial prefix */
+ i = strlen (pattern); /* length of pattern */
+ if (pattern[--i] != '%') i = 0;
+ if (t = strtok_r (s,"\n",&r)) do if (u = strchr (t,' ')) {
+ *u = '\0'; /* tie off at end of name */
+ strcpy (name + 6,t); /* make full form of name */
+ if (pmatch_full (name,pattern,'.')) mm_list (stream,'.',name,NIL);
+ else if (i && (u = strchr (name + i,'.'))) {
+ *u = '\0'; /* tie off at delimiter, see if matches */
+ if (pmatch_full (name,pattern,'.'))
+ mm_list (stream,'.',name,LATT_NOSELECT);
+ }
+ } while (t = strtok_r (NIL,"\n",&r));
+ fs_give ((void **) &s);
+ }
+}
+
+/* News find list of subscribed newsgroups
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void news_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ char pattern[MAILTMPLEN];
+ /* return data from newsrc */
+ if (news_canonicalize (ref,pat,pattern)) newsrc_lsub (stream,pattern);
+}
+
+
+/* News canonicalize newsgroup name
+ * Accepts: reference
+ * pattern
+ * returned single pattern
+ * Returns: T on success, NIL on failure
+ */
+
+long news_canonicalize (char *ref,char *pat,char *pattern)
+{
+ unsigned long i;
+ char *s;
+ if (ref && *ref) { /* have a reference */
+ strcpy (pattern,ref); /* copy reference to pattern */
+ /* # overrides mailbox field in reference */
+ if (*pat == '#') strcpy (pattern,pat);
+ /* pattern starts, reference ends, with . */
+ else if ((*pat == '.') && (pattern[strlen (pattern) - 1] == '.'))
+ strcat (pattern,pat + 1); /* append, omitting one of the period */
+ else strcat (pattern,pat); /* anything else is just appended */
+ }
+ else strcpy (pattern,pat); /* just have basic name */
+ if ((pattern[0] == '#') && (pattern[1] == 'n') && (pattern[2] == 'e') &&
+ (pattern[3] == 'w') && (pattern[4] == 's') && (pattern[5] == '.') &&
+ !strchr (pattern,'/')) { /* count wildcards */
+ for (i = 0, s = pattern; *s; *s++) if ((*s == '*') || (*s == '%')) ++i;
+ /* success if not too many */
+ if (i <= MAXWILDCARDS) return LONGT;
+ MM_LOG ("Excessive wildcards in LIST/LSUB",ERROR);
+ }
+ return NIL;
+}
+
+/* News subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long news_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return news_valid (mailbox) ? newsrc_update (stream,mailbox+6,':') : NIL;
+}
+
+
+/* NEWS unsubscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to delete from subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long news_unsubscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return news_valid (mailbox) ? newsrc_update (stream,mailbox+6,'!') : NIL;
+}
+
+/* News create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long news_create (MAILSTREAM *stream,char *mailbox)
+{
+ return NIL; /* never valid for News */
+}
+
+
+/* News delete mailbox
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long news_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return NIL; /* never valid for News */
+}
+
+
+/* News rename mailbox
+ * Accepts: mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long news_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ return NIL; /* never valid for News */
+}
+
+/* News open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *news_open (MAILSTREAM *stream)
+{
+ long i,nmsgs;
+ char *s,tmp[MAILTMPLEN];
+ struct direct **names = NIL;
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &newsproto;
+ if (stream->local) fatal ("news recycle stream");
+ /* build directory name */
+ sprintf (s = tmp,"%s/%s",(char *) mail_parameters (NIL,GET_NEWSSPOOL,NIL),
+ stream->mailbox + 6);
+ while (s = strchr (s,'.')) *s = '/';
+ /* scan directory */
+ if ((nmsgs = scandir (tmp,&names,news_select,news_numsort)) >= 0) {
+ mail_exists (stream,nmsgs); /* notify upper level that messages exist */
+ stream->local = fs_get (sizeof (NEWSLOCAL));
+ LOCAL->dirty = NIL; /* no update to .newsrc needed yet */
+ LOCAL->dir = cpystr (tmp); /* copy directory name for later */
+ LOCAL->name = cpystr (stream->mailbox + 6);
+ for (i = 0; i < nmsgs; ++i) {
+ stream->uid_last = mail_elt (stream,i+1)->private.uid =
+ atoi (names[i]->d_name);
+ fs_give ((void **) &names[i]);
+ }
+ s = (void *) names; /* stupid language */
+ fs_give ((void **) &s); /* free directory */
+ LOCAL->cachedtexts = 0; /* no cached texts */
+ stream->sequence++; /* bump sequence number */
+ stream->rdonly = stream->perm_deleted = T;
+ /* UIDs are always valid */
+ stream->uid_validity = 0xbeefface;
+ /* read .newsrc entries */
+ mail_recent (stream,newsrc_read (LOCAL->name,stream));
+ /* notify if empty newsgroup */
+ if (!(stream->nmsgs || stream->silent)) {
+ sprintf (tmp,"Newsgroup %s is empty",LOCAL->name);
+ mm_log (tmp,WARN);
+ }
+ }
+ else mm_log ("Unable to scan newsgroup spool directory",ERROR);
+ return LOCAL ? stream : NIL; /* if stream is alive, return to caller */
+}
+
+/* News file name selection test
+ * Accepts: candidate directory entry
+ * Returns: T to use file name, NIL to skip it
+ */
+
+int news_select (struct direct *name)
+{
+ char c;
+ char *s = name->d_name;
+ while (c = *s++) if (!isdigit (c)) return NIL;
+ return T;
+}
+
+
+/* News file name comparision
+ * Accepts: first candidate directory entry
+ * second candidate directory entry
+ * Returns: negative if d1 < d2, 0 if d1 == d2, postive if d1 > d2
+ */
+
+int news_numsort (const void *d1,const void *d2)
+{
+ return atoi ((*(struct direct **) d1)->d_name) -
+ atoi ((*(struct direct **) d2)->d_name);
+}
+
+
+/* News close
+ * Accepts: MAIL stream
+ * option flags
+ */
+
+void news_close (MAILSTREAM *stream,long options)
+{
+ if (LOCAL) { /* only if a file is open */
+ news_check (stream); /* dump final checkpoint */
+ if (LOCAL->dir) fs_give ((void **) &LOCAL->dir);
+ if (LOCAL->name) fs_give ((void **) &LOCAL->name);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* News fetch fast information
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ */
+
+void news_fast (MAILSTREAM *stream,char *sequence,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned long i;
+ /* set up metadata for all messages */
+ if (stream && LOCAL && ((flags & FT_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence &&
+ !(elt->day && elt->rfc822_size)) news_load_message (stream,i,NIL);
+}
+
+
+/* News fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ */
+
+void news_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ unsigned long i;
+ if ((flags & FT_UID) ? /* validate all elts */
+ mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))
+ for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->valid = T;
+}
+
+/* News load message into cache
+ * Accepts: MAIL stream
+ * message #
+ * option flags
+ */
+
+void news_load_message (MAILSTREAM *stream,unsigned long msgno,long flags)
+{
+ unsigned long i,j,nlseen;
+ int fd;
+ unsigned char c,*t;
+ struct stat sbuf;
+ MESSAGECACHE *elt;
+ FDDATA d;
+ STRING bs;
+ elt = mail_elt (stream,msgno);/* get elt */
+ /* build message file name */
+ sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid);
+ /* anything we need not currently cached? */
+ if ((!elt->day || !elt->rfc822_size ||
+ ((flags & NLM_HEADER) && !elt->private.msg.header.text.data) ||
+ ((flags & NLM_TEXT) && !elt->private.msg.text.text.data)) &&
+ ((fd = open (LOCAL->buf,O_RDONLY,NIL)) >= 0)) {
+ fstat (fd,&sbuf); /* get file metadata */
+ d.fd = fd; /* set up file descriptor */
+ d.pos = 0; /* start of file */
+ d.chunk = LOCAL->buf;
+ d.chunksize = CHUNKSIZE;
+ INIT (&bs,fd_string,&d,sbuf.st_size);
+ if (!elt->day) { /* set internaldate to file date */
+ struct tm *tm = gmtime (&sbuf.st_mtime);
+ elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1;
+ elt->year = tm->tm_year + 1900 - BASEYEAR;
+ elt->hours = tm->tm_hour; elt->minutes = tm->tm_min;
+ elt->seconds = tm->tm_sec;
+ elt->zhours = 0; elt->zminutes = 0;
+ }
+
+ if (!elt->rfc822_size) { /* know message size yet? */
+ for (i = 0, j = SIZE (&bs), nlseen = 0; j--; ) switch (SNX (&bs)) {
+ case '\015': /* unlikely carriage return */
+ if (!j || (CHR (&bs) != '\012')) {
+ i++; /* ugh, raw CR */
+ nlseen = NIL;
+ break;
+ }
+ SNX (&bs); /* eat the line feed, drop in */
+ case '\012': /* line feed? */
+ i += 2; /* count a CRLF */
+ /* header size known yet? */
+ if (!elt->private.msg.header.text.size && nlseen) {
+ /* note position in file */
+ elt->private.special.text.size = GETPOS (&bs);
+ /* and CRLF-adjusted size */
+ elt->private.msg.header.text.size = i;
+ }
+ nlseen = T; /* note newline seen */
+ break;
+ default: /* ordinary chararacter */
+ i++;
+ nlseen = NIL;
+ break;
+ }
+ SETPOS (&bs,0); /* restore old position */
+ elt->rfc822_size = i; /* note that we have size now */
+ /* header is entire message if no delimiter */
+ if (!elt->private.msg.header.text.size)
+ elt->private.msg.header.text.size = elt->rfc822_size;
+ /* text is remainder of message */
+ elt->private.msg.text.text.size =
+ elt->rfc822_size - elt->private.msg.header.text.size;
+ }
+
+ /* need to load cache with message data? */
+ if (((flags & NLM_HEADER) && !elt->private.msg.header.text.data) ||
+ ((flags & NLM_TEXT) && !elt->private.msg.text.text.data)) {
+ /* purge cache if too big */
+ if (LOCAL->cachedtexts > max (stream->nmsgs * 4096,2097152)) {
+ /* just can't keep that much */
+ mail_gc (stream,GC_TEXTS);
+ LOCAL->cachedtexts = 0;
+ }
+ if ((flags & NLM_HEADER) && !elt->private.msg.header.text.data) {
+ t = elt->private.msg.header.text.data =
+ (unsigned char *) fs_get (elt->private.msg.header.text.size + 1);
+ LOCAL->cachedtexts += elt->private.msg.header.text.size;
+ /* read in message header */
+ for (i = 0; i <= elt->private.msg.header.text.size; i++)
+ switch (c = SNX (&bs)) {
+ case '\015': /* unlikely carriage return */
+ *t++ = c;
+ if ((CHR (&bs) == '\012')) *t++ = SNX (&bs);
+ break;
+ case '\012': /* line feed? */
+ *t++ = '\015';
+ default:
+ *t++ = c;
+ break;
+ }
+ *t = '\0'; /* tie off string */
+ }
+ if ((flags & NLM_TEXT) && !elt->private.msg.text.text.data) {
+ t = elt->private.msg.text.text.data =
+ (unsigned char *) fs_get (elt->private.msg.text.text.size + 1);
+ SETPOS (&bs,elt->private.msg.header.text.size);
+ LOCAL->cachedtexts += elt->private.msg.text.text.size;
+ /* read in message text */
+ for (i = 0; i <= elt->private.msg.text.text.size; i++)
+ switch (c = SNX (&bs)) {
+ case '\015': /* unlikely carriage return */
+ *t++ = c;
+ if ((CHR (&bs) == '\012')) *t++ = SNX (&bs);
+ break;
+ case '\012': /* line feed? */
+ *t++ = '\015';
+ default:
+ *t++ = c;
+ break;
+ }
+ *t = '\0'; /* tie off string */
+ }
+ }
+ close (fd); /* flush message file */
+ }
+}
+
+/* News fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *news_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags)
+{
+ MESSAGECACHE *elt;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ elt = mail_elt (stream,msgno);/* get elt */
+ if (!elt->private.msg.header.text.data)
+ news_load_message (stream,msgno,NLM_HEADER);
+ *length = elt->private.msg.header.text.size;
+ return (char *) elt->private.msg.header.text.data;
+}
+
+
+/* News fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T on success, NIL on failure
+ */
+
+long news_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mail_elt (stream,msgno);/* get elt */
+ /* snarf message if don't have it yet */
+ if (!elt->private.msg.text.text.data) {
+ news_load_message (stream,msgno,NLM_TEXT);
+ if (!elt->private.msg.text.text.data) return NIL;
+ }
+ if (!(flags & FT_PEEK)) { /* mark as seen */
+ mail_elt (stream,msgno)->seen = T;
+ mm_flags (stream,msgno);
+ }
+ INIT (bs,mail_string,elt->private.msg.text.text.data,
+ elt->private.msg.text.text.size);
+ return T;
+}
+
+/* News per-message modify flag
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void news_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ if (!LOCAL->dirty) { /* only bother checking if not dirty yet */
+ if (elt->valid) { /* if done, see if deleted changed */
+ if (elt->sequence != elt->deleted) LOCAL->dirty = T;
+ elt->sequence = T; /* leave the sequence set */
+ }
+ /* note current setting of deleted flag */
+ else elt->sequence = elt->deleted;
+ }
+}
+
+
+/* News ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long news_ping (MAILSTREAM *stream)
+{
+ return T; /* always alive */
+}
+
+
+/* News check mailbox
+ * Accepts: MAIL stream
+ */
+
+void news_check (MAILSTREAM *stream)
+{
+ /* never do if no updates */
+ if (LOCAL->dirty) newsrc_write (LOCAL->name,stream);
+ LOCAL->dirty = NIL;
+}
+
+
+/* News expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T if success, NIL if failure
+ */
+
+long news_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ if (!stream->silent) mm_log ("Expunge ignored on readonly mailbox",NIL);
+ return LONGT;
+}
+
+/* News copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * option flags
+ * Returns: T if copy successful, else NIL
+ */
+
+long news_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ mm_log ("Copy not valid for News",ERROR);
+ return NIL;
+}
+
+
+/* News append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback function
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long news_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ mm_log ("Append not valid for news",ERROR);
+ return NIL;
+}
diff --git a/imap/src/osdep/amiga/nl_ami.c b/imap/src/osdep/amiga/nl_ami.c
new file mode 100644
index 00000000..b2d5616a
--- /dev/null
+++ b/imap/src/osdep/amiga/nl_ami.c
@@ -0,0 +1,92 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX/VMS newline routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Copy string with CRLF newlines
+ * Accepts: destination string
+ * pointer to size of destination string buffer
+ * source string
+ * length of source string
+ * Returns: length of copied string
+ */
+
+unsigned long strcrlfcpy (unsigned char **dst,unsigned long *dstl,
+ unsigned char *src,unsigned long srcl)
+{
+ long i = srcl * 2,j;
+ unsigned char c,*d = src;
+ if (*dst) { /* candidate destination provided? */
+ /* count NLs if doesn't fit worst-case */
+ if (i > *dstl) for (i = j = srcl; j; --j) if (*d++ == '\012') i++;
+ /* still too small, must reset destination */
+ if (i > *dstl) fs_give ((void **) dst);
+ }
+ /* make a new buffer if needed */
+ if (!*dst) *dst = (char *) fs_get ((*dstl = i) + 1);
+ d = *dst; /* destination string */
+ if (srcl) do { /* main copy loop */
+ if ((c = *src++) < '\016') {
+ /* prepend CR to LF */
+ if (c == '\012') *d++ = '\015';
+ /* unlikely CR */
+ else if ((c == '\015') && (srcl > 1) && (*src == '\012')) {
+ *d++ = c; /* copy the CR */
+ c = *src++; /* grab the LF */
+ --srcl; /* adjust the count */
+ }
+ }
+ *d++ = c; /* copy character */
+ } while (--srcl);
+ *d = '\0'; /* tie off destination */
+ return d - *dst; /* return length */
+}
+
+/* Length of string after strcrlfcpy applied
+ * Accepts: source string
+ * Returns: length of string
+ */
+
+unsigned long strcrlflen (STRING *s)
+{
+ unsigned long pos = GETPOS (s);
+ unsigned long i = SIZE (s);
+ unsigned long j = i;
+ while (j--) switch (SNX (s)) {/* search for newlines */
+ case '\015': /* unlikely carriage return */
+ if (j && (CHR (s) == '\012')) {
+ SNX (s); /* eat the line feed */
+ j--;
+ }
+ break;
+ case '\012': /* line feed? */
+ i++;
+ default: /* ordinary chararacter */
+ break;
+ }
+ SETPOS (s,pos); /* restore old position */
+ return i;
+}
diff --git a/imap/src/osdep/amiga/os_ami.c b/imap/src/osdep/amiga/os_ami.c
new file mode 100644
index 00000000..1ba3ca5f
--- /dev/null
+++ b/imap/src/osdep/amiga/os_ami.c
@@ -0,0 +1,80 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Amiga version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+#define PINE /* to get full DIR description in <dirent.h> */
+#include "tcp_ami.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+#define DIR_SIZE(d) d->d_reclen /* for scandir.c */
+
+#include "fs_ami.c"
+#include "ftl_ami.c"
+#include "nl_ami.c"
+#include "env_ami.c"
+#include "tcp_ami.c"
+#include "log_std.c"
+#include "gr_waitp.c"
+#include "tz_bsd.c"
+#include "scandir.c"
+#include "gethstid.c"
+
+#undef utime
+
+/* Amiga has its own wierd utime() with an incompatible struct utimbuf that
+ * does not match with the traditional time_t [2].
+ */
+
+/* Portable utime() that takes it args like real Unix systems
+ * Accepts: file path
+ * traditional utime() argument
+ * Returns: utime() results
+ */
+
+int portable_utime (char *file,time_t timep[2])
+{
+ struct utimbuf times;
+ times.actime = timep[0]; /* copy the portable values */
+ times.modtime = timep[1];
+ return utime (file,&times); /* now call Amiga's routine */
+}
diff --git a/imap/src/osdep/amiga/os_ami.h b/imap/src/osdep/amiga/os_ami.h
new file mode 100644
index 00000000..293005ca
--- /dev/null
+++ b/imap/src/osdep/amiga/os_ami.h
@@ -0,0 +1,54 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Amiga version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 15 September 2006
+ */
+
+#include <proto/socket.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+/* Different names, equivalent things in BSD and SysV */
+
+#define direct dirent
+#define utime portable_utime
+int portable_utime (char *file,time_t timep[2]);
+
+#include "env_ami.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+
+long gethostid (void);
+typedef int (*select_t) (struct direct *name);
+typedef int (*compar_t) (void *d1,void *d2);
+int scandir (char *dirname,struct direct ***namelist,select_t select,
+ compar_t compar);
+int alphasort (void *d1,void *d2);
diff --git a/imap/src/osdep/amiga/phile.c b/imap/src/osdep/amiga/phile.c
new file mode 100644
index 00000000..ce72d0a9
--- /dev/null
+++ b/imap/src/osdep/amiga/phile.c
@@ -0,0 +1,553 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: File routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 25 August 1993
+ * Last Edited: 9 May 2006
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <signal.h>
+#include "mail.h"
+#include "osdep.h"
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "rfc822.h"
+#include "misc.h"
+#include "dummy.h"
+
+/* Types returned from phile_type() */
+
+#define PTYPEBINARY 0 /* binary data */
+#define PTYPETEXT 1 /* textual data */
+#define PTYPECRTEXT 2 /* textual data with CR */
+#define PTYPE8 4 /* textual 8bit data */
+#define PTYPEISO2022JP 8 /* textual Japanese */
+#define PTYPEISO2022KR 16 /* textual Korean */
+#define PTYPEISO2022CN 32 /* textual Chinese */
+
+
+/* PHILE I/O stream local data */
+
+typedef struct phile_local {
+ ENVELOPE *env; /* file envelope */
+ BODY *body; /* file body */
+ char tmp[MAILTMPLEN]; /* temporary buffer */
+} PHILELOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((PHILELOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *phile_valid (char *name);
+int phile_isvalid (char *name,char *tmp);
+void *phile_parameters (long function,void *value);
+void phile_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void phile_list (MAILSTREAM *stream,char *ref,char *pat);
+void phile_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long phile_create (MAILSTREAM *stream,char *mailbox);
+long phile_delete (MAILSTREAM *stream,char *mailbox);
+long phile_rename (MAILSTREAM *stream,char *old,char *newname);
+long phile_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *phile_open (MAILSTREAM *stream);
+int phile_type (unsigned char *s,unsigned long i,unsigned long *j);
+void phile_close (MAILSTREAM *stream,long options);
+ENVELOPE *phile_structure (MAILSTREAM *stream,unsigned long msgno,BODY **body,
+ long flags);
+char *phile_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long phile_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+long phile_ping (MAILSTREAM *stream);
+void phile_check (MAILSTREAM *stream);
+long phile_expunge (MAILSTREAM *stream,char *sequence,long options);
+long phile_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long phile_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+/* File routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER philedriver = {
+ "phile", /* driver name */
+ /* driver flags */
+ DR_LOCAL|DR_READONLY|DR_NOSTICKY,
+ (DRIVER *) NIL, /* next driver */
+ phile_valid, /* mailbox is valid for us */
+ phile_parameters, /* manipulate parameters */
+ phile_scan, /* scan mailboxes */
+ phile_list, /* list mailboxes */
+ phile_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ dummy_create, /* create mailbox */
+ dummy_delete, /* delete mailbox */
+ dummy_rename, /* rename mailbox */
+ phile_status, /* status of mailbox */
+ phile_open, /* open mailbox */
+ phile_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ phile_structure, /* fetch message envelopes */
+ phile_header, /* fetch message header only */
+ phile_text, /* fetch message body only */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ NIL, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ phile_ping, /* ping mailbox to see if still alive */
+ phile_check, /* check for new messages */
+ phile_expunge, /* expunge deleted messages */
+ phile_copy, /* copy messages to another mailbox */
+ phile_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM phileproto = {&philedriver};
+
+/* File validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *phile_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return phile_isvalid (name,tmp) ? &philedriver : NIL;
+}
+
+
+/* File test for valid mailbox
+ * Accepts: mailbox name
+ * Returns: T if valid, NIL otherwise
+ */
+
+int phile_isvalid (char *name,char *tmp)
+{
+ struct stat sbuf;
+ char *s;
+ /* INBOX never accepted, any other name is */
+ return ((s = mailboxfile (tmp,name)) && *s && !stat (s,&sbuf) &&
+ !(sbuf.st_mode & S_IFDIR) &&
+ /* only allow empty files if no empty proto
+ or if #ftp */
+ (sbuf.st_size || !default_proto (T) ||
+ ((*name == '#') && ((name[1] == 'f') || (name[1] == 'F')) &&
+ ((name[2] == 't') || (name[2] == 'T')) &&
+ ((name[3] == 'p') || (name[3] == 'P')) && (name[4] == '/'))));
+}
+
+/* File manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *phile_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+/* File mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void phile_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* File list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void phile_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* File list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void phile_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+
+/* File status
+ * Accepts: mail stream
+ * mailbox name
+ * status flags
+ * Returns: T on success, NIL on failure
+ */
+
+long phile_status (MAILSTREAM *stream,char *mbx,long flags)
+{
+ char *s,tmp[MAILTMPLEN];
+ MAILSTATUS status;
+ struct stat sbuf;
+ long ret = NIL;
+ if ((s = mailboxfile (tmp,mbx)) && *s && !stat (s,&sbuf)) {
+ status.flags = flags; /* return status values */
+ status.unseen = (stream && mail_elt (stream,1)->seen) ? 0 : 1;
+ status.messages = status.recent = status.uidnext = 1;
+ status.uidvalidity = sbuf.st_mtime;
+ /* pass status to main program */
+ mm_status (stream,mbx,&status);
+ ret = LONGT; /* success */
+ }
+ return ret;
+}
+
+/* File open
+ * Accepts: Stream to open
+ * Returns: Stream on success, NIL on failure
+ */
+
+MAILSTREAM *phile_open (MAILSTREAM *stream)
+{
+ int i,k,fd;
+ unsigned long j,m;
+ char *s,tmp[MAILTMPLEN];
+ struct passwd *pw;
+ struct stat sbuf;
+ struct tm *t;
+ MESSAGECACHE *elt;
+ SIZEDTEXT *buf;
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &phileproto;
+ if (stream->local) fatal ("phile recycle stream");
+ /* open associated file */
+ if (!mailboxfile (tmp,stream->mailbox) || !tmp[0] || stat (tmp,&sbuf) ||
+ (fd = open (tmp,O_RDONLY,NIL)) < 0) {
+ sprintf (tmp,"Unable to open file %s",stream->mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ stream->local = fs_get (sizeof (PHILELOCAL));
+ mail_exists (stream,1); /* make sure upper level knows */
+ mail_recent (stream,1);
+ elt = mail_elt (stream,1); /* instantiate cache element */
+ elt->valid = elt->recent = T; /* mark valid flags */
+ stream->sequence++; /* bump sequence number */
+ stream->rdonly = T; /* make sure upper level knows readonly */
+ /* instantiate a new envelope and body */
+ LOCAL->env = mail_newenvelope ();
+ LOCAL->body = mail_newbody ();
+
+ t = gmtime (&sbuf.st_mtime); /* get UTC time and Julian day */
+ i = t->tm_hour * 60 + t->tm_min;
+ k = t->tm_yday;
+ t = localtime(&sbuf.st_mtime);/* get local time */
+ /* calculate time delta */
+ i = t->tm_hour * 60 + t->tm_min - i;
+ if (k = t->tm_yday - k) i += ((k < 0) == (abs (k) == 1)) ? -24*60 : 24*60;
+ k = abs (i); /* time from UTC either way */
+ elt->hours = t->tm_hour; elt->minutes = t->tm_min; elt->seconds = t->tm_sec;
+ elt->day = t->tm_mday; elt->month = t->tm_mon + 1;
+ elt->year = t->tm_year - (BASEYEAR - 1900);
+ elt->zoccident = (k == i) ? 0 : 1;
+ elt->zhours = k/60;
+ elt->zminutes = k % 60;
+ sprintf (tmp,"%s, %d %s %d %02d:%02d:%02d %c%02d%02d",
+ days[t->tm_wday],t->tm_mday,months[t->tm_mon],t->tm_year+1900,
+ t->tm_hour,t->tm_min,t->tm_sec,elt->zoccident ? '-' : '+',
+ elt->zhours,elt->zminutes);
+ /* set up Date field */
+ LOCAL->env->date = cpystr (tmp);
+
+ /* fill in From field from file owner */
+ LOCAL->env->from = mail_newaddr ();
+ if (pw = getpwuid (sbuf.st_uid)) strcpy (tmp,pw->pw_name);
+ else sprintf (tmp,"User-Number-%ld",(long) sbuf.st_uid);
+ LOCAL->env->from->mailbox = cpystr (tmp);
+ LOCAL->env->from->host = cpystr (mylocalhost ());
+ /* set subject to be mailbox name */
+ LOCAL->env->subject = cpystr (stream->mailbox);
+ /* slurp the data */
+ (buf = &elt->private.special.text)->size = sbuf.st_size;
+ read (fd,buf->data = (unsigned char *) fs_get (buf->size + 1),buf->size);
+ buf->data[buf->size] = '\0';
+ close (fd); /* close the file */
+ /* analyze data type */
+ if (i = phile_type (buf->data,buf->size,&j)) {
+ LOCAL->body->type = TYPETEXT;
+ LOCAL->body->subtype = cpystr ("PLAIN");
+ if (!(i & PTYPECRTEXT)) { /* change Internet newline format as needed */
+ s = (char *) buf->data; /* make copy of UNIX-format string */
+ buf->data = NIL; /* zap the buffer */
+ buf->size = strcrlfcpy (&buf->data,&m,s,buf->size);
+ fs_give ((void **) &s); /* flush original UNIX-format string */
+ }
+ LOCAL->body->parameter = mail_newbody_parameter ();
+ LOCAL->body->parameter->attribute = cpystr ("charset");
+ LOCAL->body->parameter->value =
+ cpystr ((i & PTYPEISO2022JP) ? "ISO-2022-JP" :
+ (i & PTYPEISO2022KR) ? "ISO-2022-KR" :
+ (i & PTYPEISO2022CN) ? "ISO-2022-CN" :
+ (i & PTYPE8) ? "X-UNKNOWN" : "US-ASCII");
+ LOCAL->body->encoding = (i & PTYPE8) ? ENC8BIT : ENC7BIT;
+ LOCAL->body->size.lines = j;
+ }
+ else { /* binary data */
+ LOCAL->body->type = TYPEAPPLICATION;
+ LOCAL->body->subtype = cpystr ("OCTET-STREAM");
+ LOCAL->body->parameter = mail_newbody_parameter ();
+ LOCAL->body->parameter->attribute = cpystr ("name");
+ LOCAL->body->parameter->value =
+ cpystr ((s = (strrchr (stream->mailbox,'/'))) ? s+1 : stream->mailbox);
+ LOCAL->body->encoding = ENCBASE64;
+ buf->data = rfc822_binary (s = (char *) buf->data,buf->size,&buf->size);
+ fs_give ((void **) &s); /* flush originary binary contents */
+ }
+ phile_header (stream,1,&j,NIL);
+ LOCAL->body->size.bytes = LOCAL->body->contents.text.size = buf->size;
+ elt->rfc822_size = j + buf->size;
+ /* only one message ever... */
+ stream->uid_validity = sbuf.st_mtime;
+ stream->uid_last = elt->private.uid = 1;
+ return stream; /* return stream alive to caller */
+}
+
+/* File determine data type
+ * Accepts: data to examine
+ * size of data
+ * pointer to line count return
+ * Returns: PTYPE mask of data type
+ */
+
+int phile_type (unsigned char *s,unsigned long i,unsigned long *j)
+{
+ int ret = PTYPETEXT;
+ char *charvec = "bbbbbbbaaalaacaabbbbbbbbbbbebbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+ *j = 0; /* no lines */
+ /* check type of every character */
+ while (i--) switch (charvec[*s++]) {
+ case 'A':
+ ret |= PTYPE8; /* 8bit character */
+ break;
+ case 'a':
+ break; /* ASCII character */
+ case 'b':
+ return PTYPEBINARY; /* binary byte seen, stop immediately */
+ case 'c':
+ ret |= PTYPECRTEXT; /* CR indicates Internet text */
+ break;
+ case 'e': /* ESC */
+ if (*s == '$') { /* ISO-2022 sequence? */
+ switch (s[1]) {
+ case 'B': case '@': ret |= PTYPEISO2022JP; break;
+ case ')':
+ switch (s[2]) {
+ case 'A': case 'E': case 'G': ret |= PTYPEISO2022CN; break;
+ case 'C': ret |= PTYPEISO2022KR; break;
+ }
+ case '*':
+ switch (s[2]) {
+ case 'H': ret |= PTYPEISO2022CN; break;
+ }
+ case '+':
+ switch (s[2]) {
+ case 'I': case 'J': case 'K': case 'L': case 'M':
+ ret |= PTYPEISO2022CN; break;
+ }
+ }
+ }
+ break;
+ case 'l': /* newline */
+ (*j)++;
+ break;
+ }
+ return ret; /* return type of data */
+}
+
+/* File close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void phile_close (MAILSTREAM *stream,long options)
+{
+ if (LOCAL) { /* only if a file is open */
+ fs_give ((void **) &mail_elt (stream,1)->private.special.text.data);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* File fetch structure
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to return body
+ * option flags
+ * Returns: envelope of this message, body returned in body value
+ *
+ * Fetches the "fast" information as well
+ */
+
+ENVELOPE *phile_structure (MAILSTREAM *stream,unsigned long msgno,BODY **body,
+ long flags)
+{
+ if (body) *body = LOCAL->body;
+ return LOCAL->env; /* return the envelope */
+}
+
+
+/* File fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *phile_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags)
+{
+ rfc822_header (LOCAL->tmp,LOCAL->env,LOCAL->body);
+ *length = strlen (LOCAL->tmp);
+ return LOCAL->tmp;
+}
+
+
+/* File fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T, always
+ */
+
+long phile_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ SIZEDTEXT *buf = &mail_elt (stream,msgno)->private.special.text;
+ if (!(flags &FT_PEEK)) { /* mark message as seen */
+ mail_elt (stream,msgno)->seen = T;
+ mm_flags (stream,msgno);
+ }
+ INIT (bs,mail_string,buf->data,buf->size);
+ return T;
+}
+
+/* File ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+long phile_ping (MAILSTREAM *stream)
+{
+ return T;
+}
+
+/* File check mailbox
+ * Accepts: MAIL stream
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+void phile_check (MAILSTREAM *stream)
+{
+ mm_log ("Check completed",NIL);
+}
+
+/* File expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T if success, NIL if failure
+ */
+
+long phile_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ if (!stream->silent) mm_log ("Expunge ignored on readonly mailbox",NIL);
+ return LONGT;
+}
+
+/* File copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if copy successful, else NIL
+ */
+
+long phile_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ char tmp[MAILTMPLEN];
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (tmp,"Can't copy - file \"%s\" is not in valid mailbox format",
+ stream->mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+}
+
+
+/* File append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback function
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long phile_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ char tmp[MAILTMPLEN],file[MAILTMPLEN];
+ char *s = mailboxfile (file,mailbox);
+ if (s && *s)
+ sprintf (tmp,"Can't append - not in valid mailbox format: %.80s",s);
+ else sprintf (tmp,"Can't append - invalid name: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+}
diff --git a/imap/src/osdep/amiga/pmatch.c b/imap/src/osdep/amiga/pmatch.c
new file mode 100644
index 00000000..a7539e50
--- /dev/null
+++ b/imap/src/osdep/amiga/pmatch.c
@@ -0,0 +1,89 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: IMAP Wildcard Matching Routines (case-dependent)
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 June 2000
+ * Last Edited: 30 August 2006
+ */
+
+/* Wildcard pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if pattern matches base, else NIL
+ */
+
+long pmatch_full (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ /* % at end, OK if no inferiors */
+ if (!pat[1]) return (delim && strchr (s,delim)) ? NIL : T;
+ /* scan remainder of string until delimiter */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while ((*s != delim) && *s++);
+ break;
+ case '*': /* match 0 or more characters */
+ if (!pat[1]) return T; /* * at end, unconditional match */
+ /* scan remainder of string */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while (*s++);
+ break;
+ case '\0': /* end of pattern */
+ return *s ? NIL : T; /* success if also end of base */
+ default: /* match this character */
+ return (*pat == *s) ? pmatch_full (s+1,pat+1,delim) : NIL;
+ }
+ return NIL;
+}
+
+/* Directory pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if base is a matching directory of pattern, else NIL
+ */
+
+long dmatch (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ if (!*s) return T; /* end of base means have a subset match */
+ if (!*++pat) return NIL; /* % at end, no inferiors permitted */
+ /* scan remainder of string until delimiter */
+ do if (dmatch (s,pat,delim)) return T;
+ while ((*s != delim) && *s++);
+ if (*s && !s[1]) return T; /* ends with delimiter, must be subset */
+ return dmatch (s,pat,delim);/* do new scan */
+ case '*': /* match 0 or more characters */
+ return T; /* unconditional match */
+ case '\0': /* end of pattern */
+ break;
+ default: /* match this character */
+ if (*s) return (*pat == *s) ? dmatch (s+1,pat+1,delim) : NIL;
+ /* end of base, return if at delimiter */
+ else if (*pat == delim) return T;
+ break;
+ }
+ return NIL;
+}
diff --git a/imap/src/osdep/amiga/pseudo.c b/imap/src/osdep/amiga/pseudo.c
new file mode 100644
index 00000000..1aae8a53
--- /dev/null
+++ b/imap/src/osdep/amiga/pseudo.c
@@ -0,0 +1,36 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Pseudo Header Strings
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 September 1996
+ * Last Edited: 30 August 2006
+ */
+
+/* Local sites may wish to alter this text */
+
+char *pseudo_from = "MAILER-DAEMON";
+char *pseudo_name = "Mail System Internal Data";
+char *pseudo_subject = "DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA";
+char *pseudo_msg =
+ "This text is part of the internal format of your mail folder, and is not\na real message. It is created automatically by the mail system software.\nIf deleted, important folder data will be lost, and it will be re-created\nwith the data reset to initial values."
+ ;
diff --git a/imap/src/osdep/amiga/pseudo.h b/imap/src/osdep/amiga/pseudo.h
new file mode 100644
index 00000000..c9c07628
--- /dev/null
+++ b/imap/src/osdep/amiga/pseudo.h
@@ -0,0 +1,30 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Pseudo Header Strings
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 September 1996
+ * Last Edited: 30 August 2006
+ */
+
+
+extern char *pseudo_from,*pseudo_name,*pseudo_subject,*pseudo_msg;
diff --git a/imap/src/osdep/amiga/scandir.c b/imap/src/osdep/amiga/scandir.c
new file mode 100644
index 00000000..878343e8
--- /dev/null
+++ b/imap/src/osdep/amiga/scandir.c
@@ -0,0 +1,81 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Scan directories
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 15 September 2006
+ */
+
+/* Emulator for BSD scandir() call
+ * Accepts: directory name
+ * destination pointer of names array
+ * selection function
+ * comparison function
+ * Returns: number of elements in the array or -1 if error
+ */
+
+int scandir (char *dirname,struct direct ***namelist,select_t select,
+ compar_t compar)
+{
+ struct direct *p,*d,**names;
+ int nitems;
+ struct stat stb;
+ long nlmax;
+ DIR *dirp = opendir (dirname);/* open directory and get status poop */
+ if ((!dirp) || (fstat (dirp->dd_fd,&stb) < 0)) return -1;
+ nlmax = stb.st_size / 24; /* guesstimate at number of files */
+ names = (struct direct **) fs_get (nlmax * sizeof (struct direct *));
+ nitems = 0; /* initially none found */
+ while (d = readdir (dirp)) { /* read directory item */
+ /* matches select criterion? */
+ if (select && !(*select) (d)) continue;
+ /* get size of direct record for this file */
+ p = (struct direct *) fs_get (DIR_SIZE (d));
+ p->d_ino = d->d_ino; /* copy the poop */
+ strcpy (p->d_name,d->d_name);
+ if (++nitems >= nlmax) { /* if out of space, try bigger guesstimate */
+ void *s = (void *) names; /* stupid language */
+ nlmax *= 2; /* double it */
+ fs_resize ((void **) &s,nlmax * sizeof (struct direct *));
+ names = (struct direct **) s;
+ }
+ names[nitems - 1] = p; /* store this file there */
+ }
+ closedir (dirp); /* done with directory */
+ /* sort if necessary */
+ if (nitems && compar) qsort (names,nitems,sizeof (struct direct *),compar);
+ *namelist = names; /* return directory */
+ return nitems; /* and size */
+}
+
+/* Alphabetic file name comparision
+ * Accepts: first candidate directory entry
+ * second candidate directory entry
+ * Returns: negative if d1 < d2, 0 if d1 == d2, postive if d1 > d2
+ */
+
+int alphasort (void *d1,void *d2)
+{
+ return strcmp ((*(struct direct **) d1)->d_name,
+ (*(struct direct **) d2)->d_name);
+}
diff --git a/imap/src/osdep/amiga/ssl_none.c b/imap/src/osdep/amiga/ssl_none.c
new file mode 100644
index 00000000..e4dedda7
--- /dev/null
+++ b/imap/src/osdep/amiga/ssl_none.c
@@ -0,0 +1,141 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy (no SSL) authentication/encryption module
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 7 February 2001
+ * Last Edited: 30 August 2006
+ */
+
+/* Init server for SSL
+ * Accepts: server name
+ */
+
+void ssl_server_init (char *server)
+{
+ syslog (LOG_ERR,"This server does not support SSL");
+ exit (1); /* punt this program too */
+}
+
+
+/* Start TLS
+ * Accepts: /etc/services service name
+ * Returns: cpystr'd error string if TLS failed, else NIL for success
+ */
+
+char *ssl_start_tls (char *server)
+{
+ return cpystr ("This server does not support TLS");
+}
+
+/* Get character
+ * Returns: character or EOF
+ */
+
+int PBIN (void)
+{
+ return getchar ();
+}
+
+
+/* Get string
+ * Accepts: destination string pointer
+ * number of bytes available
+ * Returns: destination string pointer or NIL if EOF
+ */
+
+char *PSIN (char *s,int n)
+{
+ return fgets (s,n,stdin);
+}
+
+
+/* Get record
+ * Accepts: destination string pointer
+ * number of bytes to read
+ * Returns: T if success, NIL otherwise
+ */
+
+long PSINR (char *s,unsigned long n)
+{
+ unsigned long i;
+ while (n && ((i = fread (s,1,n,stdin)) || (errno == EINTR))) s += i,n -= i;
+ return n ? NIL : LONGT;
+}
+
+
+/* Wait for input
+ * Accepts: timeout in seconds
+ * Returns: T if have input on stdin, else NIL
+ */
+
+long INWAIT (long seconds)
+{
+ return server_input_wait (seconds);
+}
+
+/* Put character
+ * Accepts: character
+ * Returns: character written or EOF
+ */
+
+int PBOUT (int c)
+{
+ return putchar (c);
+}
+
+
+/* Put string
+ * Accepts: source string pointer
+ * Returns: 0 or EOF if error
+ */
+
+int PSOUT (char *s)
+{
+ return fputs (s,stdout);
+}
+
+
+/* Put record
+ * Accepts: source sized text
+ * Returns: 0 or EOF if error
+ */
+
+int PSOUTR (SIZEDTEXT *s)
+{
+ unsigned char *t;
+ unsigned long i,j;
+ for (t = s->data,i = s->size;
+ (i && ((j = fwrite (t,1,i,stdout)) || (errno == EINTR)));
+ t += j,i -= j);
+ return i ? EOF : NIL;
+}
+
+
+/* Flush output
+ * Returns: 0 or EOF if error
+ */
+
+int PFLUSH (void)
+{
+ return fflush (stdout);
+}
diff --git a/imap/src/osdep/amiga/tcp_ami.c b/imap/src/osdep/amiga/tcp_ami.c
new file mode 100644
index 00000000..974717c0
--- /dev/null
+++ b/imap/src/osdep/amiga/tcp_ami.c
@@ -0,0 +1,797 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Amiga TCP/IP routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 13 January 2008
+ */
+
+#undef write /* don't use redefined write() */
+
+static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */
+static long ttmo_open = 0; /* TCP timeouts, in seconds */
+static long ttmo_read = 0;
+static long ttmo_write = 0;
+static long allowreversedns = T;/* allow reverse DNS lookup */
+static long tcpdebug = NIL; /* extra TCP debugging telemetry */
+
+extern long maxposint; /* get this from write.c */
+
+/* Local function prototypes */
+
+int tcp_socket_open (struct sockaddr_in *sin,char *tmp,int *ctr,char *hst,
+ unsigned long port);
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd);
+long tcp_abort (TCPSTREAM *stream);
+char *tcp_name (struct sockaddr_in *sin,long flag);
+char *tcp_name_valid (char *s);
+
+/* TCP/IP manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tcp_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_TIMEOUT:
+ tmoh = (tcptimeout_t) value;
+ case GET_TIMEOUT:
+ ret = (void *) tmoh;
+ break;
+ case SET_OPENTIMEOUT:
+ ttmo_open = (long) value;
+ case GET_OPENTIMEOUT:
+ ret = (void *) ttmo_open;
+ break;
+ case SET_READTIMEOUT:
+ ttmo_read = (long) value;
+ case GET_READTIMEOUT:
+ ret = (void *) ttmo_read;
+ break;
+ case SET_WRITETIMEOUT:
+ ttmo_write = (long) value;
+ case GET_WRITETIMEOUT:
+ ret = (void *) ttmo_write;
+ break;
+ case SET_ALLOWREVERSEDNS:
+ allowreversedns = (long) value;
+ case GET_ALLOWREVERSEDNS:
+ ret = (void *) allowreversedns;
+ break;
+ case SET_TCPDEBUG:
+ tcpdebug = (long) value;
+ case GET_TCPDEBUG:
+ ret = (void *) tcpdebug;
+ break;
+ }
+ return ret;
+}
+
+/* TCP/IP open
+ * Accepts: host name
+ * contact service name
+ * contact port number and optional silent flag
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream = NIL;
+ int i;
+ int sock = -1;
+ int ctr = 0;
+ int silent = (port & NET_SILENT) ? T : NIL;
+ int *ctrp = (port & NET_NOOPENTIMEOUT) ? NIL : &ctr;
+ char *s;
+ struct sockaddr_in sin;
+ struct hostent *he;
+ char hostname[MAILTMPLEN];
+ char tmp[MAILTMPLEN];
+ struct servent *sv = NIL;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ void *data;
+ port &= 0xffff; /* erase flags */
+ /* lookup service */
+ if (service && (sv = getservbyname (service,"tcp")))
+ port = ntohs (sin.sin_port = sv->s_port);
+ /* copy port number in network format */
+ else sin.sin_port = htons (port);
+ /* The domain literal form is used (rather than simply the dotted decimal
+ as with other Amiga programs) because it has to be a valid "host name"
+ in mailsystem terminology. */
+ /* look like domain literal? */
+ if (host[0] == '[' && host[(strlen (host))-1] == ']') {
+ strcpy (hostname,host+1); /* yes, copy number part */
+ hostname[(strlen (hostname))-1] = '\0';
+ if ((sin.sin_addr.s_addr = inet_addr (hostname)) == -1)
+ sprintf (tmp,"Bad format domain-literal: %.80s",host);
+ else {
+ sin.sin_family = AF_INET; /* family is always Internet */
+ strcpy (hostname,host); /* hostname is user's argument */
+ (*bn) (BLOCK_TCPOPEN,NIL);
+ /* get an open socket for this system */
+ sock = tcp_socket_open (&sin,tmp,ctrp,hostname,port);
+ (*bn) (BLOCK_NONE,NIL);
+ }
+ }
+
+ else { /* lookup host name */
+ if (tcpdebug) {
+ sprintf (tmp,"DNS resolution %.80s",host);
+ mm_log (tmp,TCPDEBUG);
+ }
+ (*bn) (BLOCK_DNSLOOKUP,NIL);/* quell alarms */
+ data = (*bn) (BLOCK_SENSITIVE,NIL);
+ if (!(he = gethostbyname (lcase (strcpy (hostname,host)))))
+ sprintf (tmp,"No such host as %.80s",host);
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ (*bn) (BLOCK_NONE,NIL);
+ if (he) { /* DNS resolution won? */
+ if (tcpdebug) mm_log ("DNS resolution done",TCPDEBUG);
+ /* copy address type */
+ sin.sin_family = he->h_addrtype;
+ /* copy host name */
+ strcpy (hostname,he->h_name);
+#ifdef HOST_NOT_FOUND /* muliple addresses only on DNS systems */
+ for (sock = -1,i = 0; (sock < 0) && (s = he->h_addr_list[i]); i++) {
+ if (i && !silent) mm_log (tmp,WARN);
+ memcpy (&sin.sin_addr,s,he->h_length);
+ (*bn) (BLOCK_TCPOPEN,NIL);
+ sock = tcp_socket_open (&sin,tmp,ctrp,hostname,port);
+ (*bn) (BLOCK_NONE,NIL);
+ }
+#else /* the one true address then */
+ memcpy (&sin.sin_addr,he->h_addr,he->h_length);
+ (*bn) (BLOCK_TCPOPEN,NIL);
+ sock = tcp_socket_open (&sin,tmp,ctrp,hostname,port);
+ (*bn) (BLOCK_NONE,NIL);
+#endif
+ }
+ }
+ if (sock >= 0) { /* won */
+ stream = (TCPSTREAM *) memset (fs_get (sizeof (TCPSTREAM)),0,
+ sizeof (TCPSTREAM));
+ stream->port = port; /* port number */
+ /* init sockets */
+ stream->tcpsi = stream->tcpso = sock;
+ /* stash in the snuck-in byte */
+ if (stream->ictr = ctr) *(stream->iptr = stream->ibuf) = tmp[0];
+ /* copy official host name */
+ stream->host = cpystr (hostname);
+ if (tcpdebug) mm_log ("Stream open and ready for read",TCPDEBUG);
+ }
+ else if (!silent) mm_log (tmp,ERROR);
+ return stream; /* return success */
+}
+
+/* Open a TCP socket
+ * Accepts: Internet socket address block
+ * scratch buffer
+ * pointer to "first byte read in" storage or NIL
+ * host name for error message
+ * port number for error message
+ * Returns: socket if success, else -1 with error string in scratch buffer
+ */
+
+int tcp_socket_open (struct sockaddr_in *sin,char *tmp,int *ctr,char *hst,
+ unsigned long port)
+{
+ int i,ti,sock,flgs;
+ time_t now;
+ struct protoent *pt = getprotobyname ("tcp");
+ fd_set fds,efds;
+ struct timeval tmo;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ void *data = (*bn) (BLOCK_SENSITIVE,NIL);
+ sprintf (tmp,"Trying IP address [%s]",inet_ntoa (sin->sin_addr));
+ mm_log (tmp,NIL);
+ /* make a socket */
+ if ((sock = socket (sin->sin_family,SOCK_STREAM,pt ? pt->p_proto : 0)) < 0) {
+ sprintf (tmp,"Unable to create TCP socket: %s",strerror (errno));
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ return -1;
+ }
+ else if (sock >= FD_SETSIZE) {/* unselectable sockets are useless */
+ sprintf (tmp,"Unable to create selectable TCP socket (%d >= %d)",
+ sock,FD_SETSIZE);
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ close (sock);
+ errno = EMFILE;
+ return -1;
+ }
+ flgs = fcntl (sock,F_GETFL,0);/* get current socket flags */
+ /* set non-blocking if want open timeout */
+ if (ctr) fcntl (sock,F_SETFL,flgs | FNDELAY);
+ /* open connection */
+ while ((i = connect (sock,(struct sockaddr *) sin,
+ sizeof (struct sockaddr_in))) < 0 && (errno == EINTR));
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ if (i < 0) switch (errno) { /* failed? */
+ case EAGAIN: /* DG brain damage */
+ case EINPROGRESS: /* what we expect to happen */
+ case EALREADY: /* or another form of it */
+ case EISCONN: /* restart after interrupt? */
+ case EADDRINUSE: /* restart after interrupt? */
+ break; /* well, not really, it was interrupted */
+ default:
+ sprintf (tmp,"Can't connect to %.80s,%lu: %s",hst,port,strerror (errno));
+ close (sock); /* flush socket */
+ return -1;
+ }
+
+ if (ctr) { /* want open timeout */
+ now = time (0); /* open timeout */
+ ti = ttmo_open ? now + ttmo_open : 0;
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ FD_SET (sock,&fds); /* block for error or readable */
+ FD_SET (sock,&efds);
+ do { /* block under timeout */
+ tmo.tv_sec = ti ? ti - now : 0;
+ i = select (sock+1,&fds,0,&efds,ti ? &tmo : 0);
+ now = time (0); /* fake timeout if interrupt & time expired */
+ if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
+ } while ((i < 0) && (errno == EINTR));
+ if (i > 0) { /* success, make sure really connected */
+ fcntl (sock,F_SETFL,flgs);/* restore blocking status */
+ /* This used to be a zero-byte read(), but that crashes Solaris */
+ /* get socket status */
+ while (((i = *ctr = read (sock,tmp,1)) < 0) && (errno == EINTR));
+ }
+ if (i <= 0) { /* timeout or error? */
+ i = i ? errno : ETIMEDOUT;/* determine error code */
+ close (sock); /* flush socket */
+ errno = i; /* return error code */
+ sprintf (tmp,"Connection failed to %.80s,%lu: %s",hst,port,
+ strerror (errno));
+ return -1;
+ }
+ }
+ return sock; /* return the socket */
+}
+
+/* TCP/IP authenticated open
+ * Accepts: host name
+ * service name
+ * returned user name buffer
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL; /* disabled */
+}
+
+/* TCP receive line
+ * Accepts: TCP stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *tcp_getline (TCPSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = tcp_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = tcp_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* TCP receive line or partial line
+ * Accepts: TCP stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!tcp_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!tcp_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* TCP/IP receive buffer
+ * Accepts: TCP/IP stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *s)
+{
+ unsigned long n;
+ /* make sure socket still alive */
+ if (stream->tcpsi < 0) return NIL;
+ /* can transfer bytes from buffer? */
+ if (n = min (size,stream->ictr)) {
+ memcpy (s,stream->iptr,n); /* yes, slurp as much as we can from it */
+ s += n; /* update pointer */
+ stream->iptr +=n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -=n;
+ }
+ if (size) {
+ int i;
+ fd_set fds,efds;
+ struct timeval tmo;
+ time_t t = time (0);
+ blocknotify_t bn=(blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ (*bn) (BLOCK_TCPREAD,NIL);
+ while (size > 0) { /* until request satisfied */
+ time_t tl = time (0);
+ time_t now = tl;
+ time_t ti = ttmo_read ? now + ttmo_read : 0;
+ if (tcpdebug) mm_log ("Reading TCP buffer",TCPDEBUG);
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ FD_SET (stream->tcpsi,&fds);
+ FD_SET (stream->tcpsi,&efds);
+ errno = NIL; /* block and read */
+ do { /* block under timeout */
+ tmo.tv_sec = ti ? ti - now : 0;
+ i = select (stream->tcpsi+1,&fds,0,&efds,ti ? &tmo : 0);
+ now = time (0); /* fake timeout if interrupt & time expired */
+ if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
+ } while ((i < 0) && (errno == EINTR));
+ if (i > 0) { /* select says there's data to read? */
+ while (((i = read (stream->tcpsi,s,(int) min (maxposint,size))) < 0) &&
+ (errno == EINTR));
+ if (i < 1) return tcp_abort (stream);
+ s += i; /* point at new place to write */
+ size -= i; /* reduce byte count */
+ if (tcpdebug) mm_log ("Successfully read TCP buffer",TCPDEBUG);
+ }
+ else if (i || !tmoh || !(*tmoh) (now - t,now - tl))
+ return tcp_abort (stream);
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ }
+ *s = '\0'; /* tie off string */
+ return T;
+}
+
+/* TCP/IP receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getdata (TCPSTREAM *stream)
+{
+ int i;
+ fd_set fds,efds;
+ struct timeval tmo;
+ time_t t = time (0);
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (stream->tcpsi < 0) return NIL;
+ (*bn) (BLOCK_TCPREAD,NIL);
+ while (stream->ictr < 1) { /* if nothing in the buffer */
+ time_t tl = time (0); /* start of request */
+ time_t now = tl;
+ time_t ti = ttmo_read ? now + ttmo_read : 0;
+ if (tcpdebug) mm_log ("Reading TCP data",TCPDEBUG);
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ FD_SET (stream->tcpsi,&fds);/* set bit in selection vector */
+ FD_SET(stream->tcpsi,&efds);/* set bit in error selection vector */
+ errno = NIL; /* block and read */
+ do { /* block under timeout */
+ tmo.tv_sec = ti ? ti - now : 0;
+ i = select (stream->tcpsi+1,&fds,0,&efds,ti ? &tmo : 0);
+ now = time (0); /* fake timeout if interrupt & time expired */
+ if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
+ } while ((i < 0) && (errno == EINTR));
+ if (i > 0) { /* got data? */
+ while (((i = read (stream->tcpsi,stream->ibuf,BUFLEN)) < 0) &&
+ (errno == EINTR));
+ if (i < 1) return tcp_abort (stream);
+ stream->iptr = stream->ibuf;/* point at TCP buffer */
+ stream->ictr = i; /* set new byte count */
+ if (tcpdebug) mm_log ("Successfully read TCP data",TCPDEBUG);
+ }
+ else if (i || !tmoh || !(*tmoh) (now - t,now - tl))
+ return tcp_abort (stream);/* error or timeout no-continue */
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return T;
+}
+
+/* TCP/IP send string as record
+ * Accepts: TCP/IP stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long tcp_soutr (TCPSTREAM *stream,char *string)
+{
+ return tcp_sout (stream,string,(unsigned long) strlen (string));
+}
+
+
+/* TCP/IP send string
+ * Accepts: TCP/IP stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
+{
+ int i;
+ fd_set fds,efds;
+ struct timeval tmo;
+ time_t t = time (0);
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (stream->tcpso < 0) return NIL;
+ (*bn) (BLOCK_TCPWRITE,NIL);
+ while (size > 0) { /* until request satisfied */
+ time_t tl = time (0); /* start of request */
+ time_t now = tl;
+ time_t ti = ttmo_write ? now + ttmo_write : 0;
+ if (tcpdebug) mm_log ("Writing to TCP",TCPDEBUG);
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ FD_SET (stream->tcpso,&fds);/* set bit in selection vector */
+ FD_SET(stream->tcpso,&efds);/* set bit in error selection vector */
+ errno = NIL; /* block and write */
+ do { /* block under timeout */
+ tmo.tv_sec = ti ? ti - now : 0;
+ i = select (stream->tcpso+1,0,&fds,&efds,ti ? &tmo : 0);
+ now = time (0); /* fake timeout if interrupt & time expired */
+ if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
+ } while ((i < 0) && (errno == EINTR));
+ if (i > 0) { /* OK to send data? */
+ while (((i = write (stream->tcpso,string,size)) < 0) &&(errno == EINTR));
+ if (i < 0) return tcp_abort (stream);
+ size -= i; /* how much we sent */
+ string += i;
+ if (tcpdebug) mm_log ("successfully wrote to TCP",TCPDEBUG);
+ }
+ else if (i || !tmoh || !(*tmoh) (now - t,now - tl))
+ return tcp_abort (stream);/* error or timeout no-continue */
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return T; /* all done */
+}
+
+/* TCP/IP close
+ * Accepts: TCP/IP stream
+ */
+
+void tcp_close (TCPSTREAM *stream)
+{
+ tcp_abort (stream); /* nuke the stream */
+ /* flush host names */
+ if (stream->host) fs_give ((void **) &stream->host);
+ if (stream->remotehost) fs_give ((void **) &stream->remotehost);
+ if (stream->localhost) fs_give ((void **) &stream->localhost);
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+
+/* TCP/IP abort stream
+ * Accepts: TCP/IP stream
+ * Returns: NIL always
+ */
+
+long tcp_abort (TCPSTREAM *stream)
+{
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (stream->tcpsi >= 0) { /* no-op if no socket */
+ (*bn) (BLOCK_TCPCLOSE,NIL);
+ close (stream->tcpsi); /* nuke the socket */
+ if (stream->tcpsi != stream->tcpso) close (stream->tcpso);
+ stream->tcpsi = stream->tcpso = -1;
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return NIL;
+}
+
+/* TCP/IP get host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_host (TCPSTREAM *stream)
+{
+ return stream->host; /* use tcp_remotehost() if want guarantees */
+}
+
+
+/* TCP/IP get remote host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_remotehost (TCPSTREAM *stream)
+{
+ if (!stream->remotehost) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ stream->remotehost = /* get socket's peer name */
+ (getpeername (stream->tcpsi,(struct sockaddr *) &sin,(void *) &sinlen) ||
+ (sin.sin_family != AF_INET)) ?
+ cpystr (stream->host) : tcp_name (&sin,NIL);
+ }
+ return stream->remotehost;
+}
+
+
+/* TCP/IP return port for this stream
+ * Accepts: TCP/IP stream
+ * Returns: port number for this stream
+ */
+
+unsigned long tcp_port (TCPSTREAM *stream)
+{
+ return stream->port; /* return port number */
+}
+
+
+/* TCP/IP get local host name
+ * Accepts: TCP/IP stream
+ * Returns: local host name
+ */
+
+char *tcp_localhost (TCPSTREAM *stream)
+{
+ if (!stream->localhost) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ stream->localhost = /* get socket's name */
+ ((stream->port & 0xffff000) ||
+ getsockname (stream->tcpsi,(struct sockaddr *) &sin,(void *) &sinlen) ||
+ (sin.sin_family != AF_INET)) ?
+ cpystr (mylocalhost ()) : tcp_name (&sin,NIL);
+ }
+ return stream->localhost; /* return local host name */
+}
+
+/* TCP/IP get client host address (server calls only)
+ * Returns: client host address
+ */
+
+static char *myClientAddr = NIL;
+
+char *tcp_clientaddr ()
+{
+ if (!myClientAddr) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ myClientAddr = /* get stdin's peer name */
+ cpystr (getpeername (0,(struct sockaddr *) &sin,(void *) &sinlen) ?
+ "UNKNOWN" : ((sin.sin_family == AF_INET) ?
+ inet_ntoa (sin.sin_addr) : "NON-IPv4"));
+ }
+ return myClientAddr;
+}
+
+
+/* TCP/IP get client host name (server calls only)
+ * Returns: client host name
+ */
+
+static char *myClientHost = NIL;
+
+char *tcp_clienthost ()
+{
+ if (!myClientHost) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ myClientHost = /* get stdin's peer name */
+ getpeername (0,(struct sockaddr *) &sin,(void *) &sinlen) ?
+ cpystr ("UNKNOWN") : ((sin.sin_family == AF_INET) ?
+ tcp_name (&sin,T) : cpystr ("NON-IPv4"));
+ }
+ return myClientHost;
+}
+
+/* TCP/IP get server host address (server calls only)
+ * Returns: server host address
+ */
+
+static char *myServerAddr = NIL;
+
+char *tcp_serveraddr ()
+{
+ if (!myServerAddr) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ myServerAddr = /* get stdin's peer name */
+ cpystr (getsockname (0,(struct sockaddr *) &sin,(void *) &sinlen) ?
+ "UNKNOWN" : ((sin.sin_family == AF_INET) ?
+ inet_ntoa (sin.sin_addr) : "NON-IPv4"));
+ }
+ return myServerAddr;
+}
+
+
+/* TCP/IP get server host name (server calls only)
+ * Returns: server host name
+ */
+
+static char *myServerHost = NIL;
+static long myServerPort = -1;
+
+char *tcp_serverhost ()
+{
+ if (!myServerHost) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ /* get stdin's name */
+ if (getsockname (0,(struct sockaddr *) &sin,(void *) &sinlen) ||
+ (sin.sin_family != AF_INET)) myServerHost = cpystr (mylocalhost ());
+ else {
+ myServerHost = tcp_name (&sin,NIL);
+ myServerPort = ntohs (sin.sin_port);
+ }
+ }
+ return myServerHost;
+}
+
+
+/* TCP/IP get server port number (server calls only)
+ * Returns: server port number
+ */
+
+long tcp_serverport ()
+{
+ if (!myServerHost) tcp_serverhost ();
+ return myServerPort;
+}
+
+/* TCP/IP return canonical form of host name
+ * Accepts: host name
+ * Returns: canonical form of host name
+ */
+
+char *tcp_canonical (char *name)
+{
+ char *ret,host[MAILTMPLEN];
+ struct hostent *he;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ void *data;
+ /* look like domain literal? */
+ if (name[0] == '[' && name[strlen (name) - 1] == ']') return name;
+ (*bn) (BLOCK_DNSLOOKUP,NIL); /* quell alarms */
+ data = (*bn) (BLOCK_SENSITIVE,NIL);
+ if (tcpdebug) {
+ sprintf (host,"DNS canonicalization %.80s",name);
+ mm_log (host,TCPDEBUG);
+ }
+ /* note that Amiga requires lowercase! */
+ ret = (he = gethostbyname (lcase (strcpy (host,name)))) ?
+ (char *) he->h_name : name;
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ (*bn) (BLOCK_NONE,NIL); /* alarms OK now */
+ if (tcpdebug) mm_log ("DNS canonicalization done",TCPDEBUG);
+ return ret;
+}
+
+
+/* TCP/IP return name from socket
+ * Accepts: socket
+ * verbose flag
+ * Returns: cpystr name
+ */
+
+char *tcp_name (struct sockaddr_in *sin,long flag)
+{
+ char *ret,*t,adr[MAILTMPLEN],tmp[MAILTMPLEN];
+ sprintf (ret = adr,"[%.80s]",inet_ntoa (sin->sin_addr));
+ if (allowreversedns) {
+ struct hostent *he;
+ blocknotify_t bn = (blocknotify_t)mail_parameters(NIL,GET_BLOCKNOTIFY,NIL);
+ void *data;
+ if (tcpdebug) {
+ sprintf (tmp,"Reverse DNS resolution %s",adr);
+ mm_log (tmp,TCPDEBUG);
+ }
+ (*bn) (BLOCK_DNSLOOKUP,NIL);/* quell alarms */
+ data = (*bn) (BLOCK_SENSITIVE,NIL);
+ /* translate address to name */
+ if (t = tcp_name_valid ((he = gethostbyaddr ((char *) &sin->sin_addr,
+ sizeof (struct in_addr),
+ sin->sin_family)) ?
+ (char *) he->h_name : NIL)) {
+ /* produce verbose form if needed */
+ if (flag) sprintf (ret = tmp,"%s %s",t,adr);
+ else ret = t;
+ }
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ (*bn) (BLOCK_NONE,NIL); /* alarms OK now */
+ if (tcpdebug) mm_log ("Reverse DNS resolution done",TCPDEBUG);
+ }
+ return cpystr (ret);
+}
+
+
+/* Validate name
+ * Accepts: domain name
+ * Returns: T if valid, NIL otherwise
+ */
+
+char *tcp_name_valid (char *s)
+{
+ int c;
+ char *ret,*tail;
+ /* must be non-empty and not too long */
+ if ((ret = (s && *s) ? s : NIL) && (tail = ret + NETMAXHOST)) {
+ /* must be alnum, dot, or hyphen */
+ while ((c = *s++) && (s <= tail) &&
+ (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) ||
+ ((c >= '0') && (c <= '9')) || (c == '-') || (c == '.')));
+ if (c) ret = NIL;
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/amiga/tcp_ami.h b/imap/src/osdep/amiga/tcp_ami.h
new file mode 100644
index 00000000..d4b29797
--- /dev/null
+++ b/imap/src/osdep/amiga/tcp_ami.h
@@ -0,0 +1,48 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX TCP/IP routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* TCP input buffer */
+
+#define BUFLEN 8192
+
+
+/* TCP I/O stream */
+
+#define TCPSTREAM struct tcp_stream
+TCPSTREAM {
+ char *host; /* host name */
+ unsigned long port; /* port number */
+ char *localhost; /* local host name */
+ char *remotehost; /* remote host name */
+ int tcpsi; /* input socket */
+ int tcpso; /* output socket */
+ int ictr; /* input counter */
+ char *iptr; /* input pointer */
+ char ibuf[BUFLEN]; /* input buffer */
+};
diff --git a/imap/src/osdep/amiga/tenex.c b/imap/src/osdep/amiga/tenex.c
new file mode 100644
index 00000000..eee61fba
--- /dev/null
+++ b/imap/src/osdep/amiga/tenex.c
@@ -0,0 +1,1470 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Tenex mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 22 May 1990
+ * Last Edited: 11 October 2007
+ */
+
+
+/* FILE TIME SEMANTICS
+ *
+ * The atime is the last read time of the file.
+ * The mtime is the last flags update time of the file.
+ * The ctime is the last write time of the file.
+ *
+ * TEXT SIZE SEMANTICS
+ *
+ * Most of the text sizes are in internal (LF-only) form, except for the
+ * msg.text size. Beware.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <sys/stat.h>
+#include "misc.h"
+#include "dummy.h"
+
+/* TENEX I/O stream local data */
+
+typedef struct tenex_local {
+ unsigned int shouldcheck: 1; /* if ping should do a check instead */
+ unsigned int mustcheck: 1; /* if ping must do a check instead */
+ int fd; /* file descriptor for I/O */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ time_t lastsnarf; /* local snarf time */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ unsigned long uid; /* current text uid */
+ SIZEDTEXT text; /* current text */
+} TENEXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((TENEXLOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *tenex_valid (char *name);
+int tenex_isvalid (char *name,char *tmp);
+void *tenex_parameters (long function,void *value);
+void tenex_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void tenex_list (MAILSTREAM *stream,char *ref,char *pat);
+void tenex_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long tenex_create (MAILSTREAM *stream,char *mailbox);
+long tenex_delete (MAILSTREAM *stream,char *mailbox);
+long tenex_rename (MAILSTREAM *stream,char *old,char *newname);
+long tenex_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *tenex_open (MAILSTREAM *stream);
+void tenex_close (MAILSTREAM *stream,long options);
+void tenex_fast (MAILSTREAM *stream,char *sequence,long flags);
+void tenex_flags (MAILSTREAM *stream,char *sequence,long flags);
+char *tenex_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long tenex_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void tenex_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+void tenex_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long tenex_ping (MAILSTREAM *stream);
+void tenex_check (MAILSTREAM *stream);
+void tenex_snarf (MAILSTREAM *stream);
+long tenex_expunge (MAILSTREAM *stream,char *sequence,long options);
+long tenex_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long tenex_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+unsigned long tenex_size (MAILSTREAM *stream,unsigned long m);
+char *tenex_file (char *dst,char *name);
+long tenex_parse (MAILSTREAM *stream);
+MESSAGECACHE *tenex_elt (MAILSTREAM *stream,unsigned long msgno);
+void tenex_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt);
+void tenex_update_status (MAILSTREAM *stream,unsigned long msgno,
+ long syncflag);
+unsigned long tenex_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size);
+
+/* Tenex mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER tenexdriver = {
+ "tenex", /* driver name */
+ DR_LOCAL|DR_MAIL|DR_NOSTICKY|DR_LOCKING,
+ /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ tenex_valid, /* mailbox is valid for us */
+ tenex_parameters, /* manipulate parameters */
+ tenex_scan, /* scan mailboxes */
+ tenex_list, /* list mailboxes */
+ tenex_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ dummy_create, /* create mailbox */
+ tenex_delete, /* delete mailbox */
+ tenex_rename, /* rename mailbox */
+ tenex_status, /* status of mailbox */
+ tenex_open, /* open mailbox */
+ tenex_close, /* close mailbox */
+ tenex_fast, /* fetch message "fast" attributes */
+ tenex_flags, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ tenex_header, /* fetch message header */
+ tenex_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ tenex_flag, /* modify flags */
+ tenex_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ tenex_ping, /* ping mailbox to see if still alive */
+ tenex_check, /* check for new messages */
+ tenex_expunge, /* expunge deleted messages */
+ tenex_copy, /* copy messages to another mailbox */
+ tenex_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM tenexproto = {&tenexdriver};
+
+/* Tenex mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *tenex_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return tenex_isvalid (name,tmp) ? &tenexdriver : NIL;
+}
+
+
+/* Tenex mail test for valid mailbox
+ * Accepts: mailbox name
+ * Returns: T if valid, NIL otherwise
+ */
+
+int tenex_isvalid (char *name,char *tmp)
+{
+ int fd;
+ int ret = NIL;
+ char *s,file[MAILTMPLEN];
+ struct stat sbuf;
+ time_t tp[2];
+ errno = EINVAL; /* assume invalid argument */
+ /* if file, get its status */
+ if ((s = tenex_file (file,name)) && !stat (s,&sbuf)) {
+ if (!sbuf.st_size) { /* allow empty file if INBOX */
+ if ((s = mailboxfile (tmp,name)) && !*s) ret = T;
+ else errno = 0; /* empty file */
+ }
+ else if ((fd = open (file,O_RDONLY,NIL)) >= 0) {
+ memset (tmp,'\0',MAILTMPLEN);
+ if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\012')) &&
+ (s[-1] != '\015')) { /* valid format? */
+ *s = '\0'; /* tie off header */
+ /* must begin with dd-mmm-yy" */
+ ret = (((tmp[2] == '-' && tmp[6] == '-') ||
+ (tmp[1] == '-' && tmp[5] == '-')) &&
+ (s = strchr (tmp+18,',')) && strchr (s+2,';')) ? T : NIL;
+ }
+ else errno = -1; /* bogus format */
+ close (fd); /* close the file */
+ /* \Marked status? */
+ if (sbuf.st_ctime > sbuf.st_atime) {
+ tp[0] = sbuf.st_atime; /* preserve atime and mtime */
+ tp[1] = sbuf.st_mtime;
+ utime (file,tp); /* set the times */
+ }
+ }
+ }
+ /* in case INBOX but not tenex format */
+ else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1;
+ return ret; /* return what we should */
+}
+
+/* Tenex manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tenex_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = tenex_file ((char *) value,"INBOX");
+ break;
+ }
+ return ret;
+}
+
+
+/* Tenex mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void tenex_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* Tenex mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void tenex_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* Tenex mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void tenex_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* Tenex mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long tenex_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return tenex_rename (stream,mailbox,NIL);
+}
+
+
+/* Tenex mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long tenex_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = T;
+ char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ int fd,ld;
+ struct stat sbuf;
+ if (!tenex_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'/')) && !s[1])))) {
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ else if ((fd = open (file,O_RDWR,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get exclusive parse/append permission */
+ if ((ld = lockfd (fd,lock,LOCK_EX)) < 0) {
+ MM_LOG ("Unable to lock rename mailbox",ERROR);
+ return NIL;
+ }
+ /* lock out other users */
+ if (flock (fd,LOCK_EX|LOCK_NB)) {
+ close (fd); /* couldn't lock, give up on it then */
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+ MM_LOG (tmp,ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return NIL;
+ }
+
+ if (newname) { /* want rename? */
+ if (s = strrchr (tmp,'/')) {/* found superior to destination name? */
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,tmp,get_dir_protection (newname)))
+ ret = NIL;
+ else *s = c; /* restore full name */
+ }
+ /* rename the file */
+ if (ret && rename (file,tmp)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ else if (unlink (file)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* close the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ /* recreate file if renamed INBOX */
+ if (ret && !compare_cstring (old,"INBOX")) dummy_create (NIL,"mail.txt");
+ return ret; /* return success */
+}
+
+/* Tenex Mail status
+ * Accepts: mail stream
+ * mailbox name
+ * status flags
+ * Returns: T on success, NIL on failure
+ */
+
+long tenex_status (MAILSTREAM *stream,char *mbx,long flags)
+{
+ MAILSTATUS status;
+ unsigned long i;
+ MAILSTREAM *tstream = NIL;
+ MAILSTREAM *systream = NIL;
+ /* make temporary stream (unless this mbx) */
+ if (!stream && !(stream = tstream =
+ mail_open (NIL,mbx,OP_READONLY|OP_SILENT))) return NIL;
+ status.flags = flags; /* return status values */
+ status.messages = stream->nmsgs;
+ status.recent = stream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++)
+ if (!mail_elt (stream,i)->seen) status.unseen++;
+ status.uidnext = stream->uid_last + 1;
+ status.uidvalidity = stream->uid_validity;
+ /* calculate post-snarf results */
+ if (!status.recent && stream->inbox &&
+ (systream = mail_open (NIL,sysinbox (),OP_READONLY|OP_SILENT))) {
+ status.messages += systream->nmsgs;
+ status.recent += systream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1; i <= systream->nmsgs; i++)
+ if (!mail_elt (systream,i)->seen) status.unseen++;
+ /* kludge but probably good enough */
+ status.uidnext += systream->nmsgs;
+ }
+ MM_STATUS(stream,mbx,&status);/* pass status to main program */
+ if (tstream) mail_close (tstream);
+ if (systream) mail_close (systream);
+ return T; /* success */
+}
+
+/* Tenex mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *tenex_open (MAILSTREAM *stream)
+{
+ int fd,ld;
+ char tmp[MAILTMPLEN];
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return user_flags (&tenexproto);
+ if (stream->local) fatal ("tenex recycle stream");
+ user_flags (stream); /* set up user flags */
+ /* canonicalize the mailbox name */
+ if (!tenex_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ MM_LOG (tmp,ERROR);
+ }
+ if (stream->rdonly ||
+ (fd = open (tmp,O_RDWR,NIL)) < 0) {
+ if ((fd = open (tmp,O_RDONLY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ else if (!stream->rdonly) { /* got it, but readonly */
+ MM_LOG ("Can't get write access to mailbox, access is readonly",WARN);
+ stream->rdonly = T;
+ }
+ }
+ stream->local = fs_get (sizeof (TENEXLOCAL));
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ LOCAL->text.data = (unsigned char *) fs_get (CHUNKSIZE);
+ LOCAL->text.size = CHUNKSIZE - 1;
+
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ LOCAL->fd = fd; /* bind the file */
+ /* flush old name */
+ fs_give ((void **) &stream->mailbox);
+ /* save canonical name */
+ stream->mailbox = cpystr (tmp);
+ /* get shared parse permission */
+ if ((ld = lockfd (fd,tmp,LOCK_SH)) < 0) {
+ MM_LOG ("Unable to lock open mailbox",ERROR);
+ return NIL;
+ }
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH); /* lock the file */
+ (*bn) (BLOCK_NONE,NIL);
+ unlockfd (ld,tmp); /* release shared parse permission */
+ LOCAL->filesize = 0; /* initialize parsed file size */
+ /* time not set up yet */
+ LOCAL->lastsnarf = LOCAL->filetime = 0;
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ stream->sequence++; /* bump sequence number */
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ if (tenex_ping (stream) && !stream->nmsgs)
+ MM_LOG ("Mailbox is empty",(long) NIL);
+ if (!LOCAL) return NIL; /* failure if stream died */
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft =
+ stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ return stream; /* return stream to caller */
+}
+
+/* Tenex mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void tenex_close (MAILSTREAM *stream,long options)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ if (options & CL_EXPUNGE) tenex_expunge (stream,NIL,NIL);
+ stream->silent = silent; /* restore previous status */
+ flock (LOCAL->fd,LOCK_UN); /* unlock local file */
+ close (LOCAL->fd); /* close the local file */
+ /* free local text buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* Tenex mail fetch fast data
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ */
+
+void tenex_fast (MAILSTREAM *stream,char *sequence,long flags)
+{
+ STRING bs;
+ MESSAGECACHE *elt;
+ unsigned long i;
+ if (stream && LOCAL &&
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ if (!elt->rfc822_size) { /* have header size yet? */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.special.text.size,L_SET);
+ /* resize bigbuf if necessary */
+ if (LOCAL->buflen < elt->private.msg.full.text.size) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buflen = elt->private.msg.full.text.size;
+ LOCAL->buf = (char *) fs_get (LOCAL->buflen + 1);
+ }
+ /* tie off string */
+ LOCAL->buf[elt->private.msg.full.text.size] = '\0';
+ /* read in the message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.full.text.size);
+ INIT (&bs,mail_string,(void *) LOCAL->buf,
+ elt->private.msg.full.text.size);
+ /* calculate its CRLF size */
+ elt->rfc822_size = strcrlflen (&bs);
+ }
+ tenex_elt (stream,i); /* get current flags from file */
+ }
+}
+
+
+/* Tenex mail fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ * Sniffs at file to get flags
+ */
+
+void tenex_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ unsigned long i;
+ if (stream && LOCAL &&
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if (mail_elt (stream,i)->sequence) tenex_elt (stream,i);
+}
+
+/* TENEX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *tenex_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags)
+{
+ char *s;
+ unsigned long i;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ /* get to header position */
+ lseek (LOCAL->fd,tenex_hdrpos (stream,msgno,&i),L_SET);
+ if (flags & FT_INTERNAL) {
+ if (i > LOCAL->buflen) { /* resize if not enough space */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get (LOCAL->buflen = i + 1);
+ }
+ /* slurp the data */
+ read (LOCAL->fd,LOCAL->buf,*length = i);
+ }
+ else {
+ s = (char *) fs_get (i + 1);/* get readin buffer */
+ s[i] = '\0'; /* tie off string */
+ read (LOCAL->fd,s,i); /* slurp the data */
+ /* make CRLF copy of string */
+ *length = strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,s,i);
+ fs_give ((void **) &s); /* free readin buffer */
+ }
+ return (char *) LOCAL->buf;
+}
+
+/* TENEX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T, always
+ */
+
+long tenex_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ char *s;
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ /* get message status */
+ elt = tenex_elt (stream,msgno);
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ elt->seen = T; /* mark message as seen */
+ /* recalculate status */
+ tenex_update_status (stream,msgno,T);
+ MM_FLAGS (stream,msgno);
+ }
+ if (flags & FT_INTERNAL) { /* if internal representation wanted */
+ /* find header position */
+ i = tenex_hdrpos (stream,msgno,&j);
+ if (i > LOCAL->buflen) { /* resize if not enough space */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get (LOCAL->buflen = i + 1);
+ }
+ /* go to text position */
+ lseek (LOCAL->fd,i + j,L_SET);
+ /* slurp the data */
+ read (LOCAL->fd,LOCAL->buf,i);
+ /* set up stringstruct for internal */
+ INIT (bs,mail_string,LOCAL->buf,i);
+ }
+ else { /* normal form, previous text cached? */
+ if (elt->private.uid == LOCAL->uid)
+ i = elt->private.msg.text.text.size;
+ else { /* not cached, cache it now */
+ LOCAL->uid = elt->private.uid;
+ /* find header position */
+ i = tenex_hdrpos (stream,msgno,&j);
+ /* go to text position */
+ lseek (LOCAL->fd,i + j,L_SET);
+ s = (char *) fs_get ((i = tenex_size (stream,msgno) - j) + 1);
+ s[i] = '\0'; /* tie off string */
+ read (LOCAL->fd,s,i); /* slurp the data */
+ /* make CRLF copy of string */
+ i = elt->private.msg.text.text.size =
+ strcrlfcpy (&LOCAL->text.data,&LOCAL->text.size,s,i);
+ fs_give ((void **) &s); /* free readin buffer */
+ }
+ /* set up stringstruct */
+ INIT (bs,mail_string,LOCAL->text.data,i);
+ }
+ return T; /* success */
+}
+
+/* Tenex mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ */
+
+void tenex_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ time_t tp[2];
+ struct stat sbuf;
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* make sure read comes after all that */
+ utime (stream->mailbox,tp);
+ }
+}
+
+
+/* Tenex mail per-message modify flags
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void tenex_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ struct stat sbuf;
+ /* maybe need to do a checkpoint? */
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ LOCAL->filetime = 0; /* don't do this test for any other messages */
+ }
+ /* recalculate status */
+ tenex_update_status (stream,elt->msgno,NIL);
+}
+
+/* Tenex mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream still alive, NIL if not
+ */
+
+long tenex_ping (MAILSTREAM *stream)
+{
+ unsigned long i = 1;
+ long r = T;
+ int ld;
+ char lock[MAILTMPLEN];
+ struct stat sbuf;
+ if (stream && LOCAL) { /* only if stream already open */
+ fstat (LOCAL->fd,&sbuf); /* get current file poop */
+ if (LOCAL->filetime && !(LOCAL->mustcheck || LOCAL->shouldcheck) &&
+ (LOCAL->filetime < sbuf.st_mtime)) LOCAL->shouldcheck = T;
+ /* check for changed message status */
+ if (LOCAL->mustcheck || LOCAL->shouldcheck) {
+ LOCAL->filetime = sbuf.st_mtime;
+ if (LOCAL->shouldcheck) /* babble when we do this unilaterally */
+ MM_NOTIFY (stream,"[CHECK] Checking for flag updates",NIL);
+ while (i <= stream->nmsgs) tenex_elt (stream,i++);
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ }
+ /* get shared parse/append permission */
+ if ((sbuf.st_size != LOCAL->filesize) &&
+ ((ld = lockfd (LOCAL->fd,lock,LOCK_SH)) >= 0)) {
+ /* parse resulting mailbox */
+ r = (tenex_parse (stream)) ? T : NIL;
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ if (LOCAL) { /* stream must still be alive */
+ /* snarf if this is a read-write inbox */
+ if (stream->inbox && !stream->rdonly) {
+ tenex_snarf (stream);
+ fstat (LOCAL->fd,&sbuf);/* see if file changed now */
+ if ((sbuf.st_size != LOCAL->filesize) &&
+ ((ld = lockfd (LOCAL->fd,lock,LOCK_SH)) >= 0)) {
+ /* parse resulting mailbox */
+ r = (tenex_parse (stream)) ? T : NIL;
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ }
+ }
+ }
+ return r; /* return result of the parse */
+}
+
+
+/* Tenex mail check mailbox (reparses status too)
+ * Accepts: MAIL stream
+ */
+
+void tenex_check (MAILSTREAM *stream)
+{
+ /* mark that a check is desired */
+ if (LOCAL) LOCAL->mustcheck = T;
+ if (tenex_ping (stream)) MM_LOG ("Check completed",(long) NIL);
+}
+
+/* Tenex mail snarf messages from system inbox
+ * Accepts: MAIL stream
+ */
+
+void tenex_snarf (MAILSTREAM *stream)
+{
+ unsigned long i = 0;
+ unsigned long j,r,hdrlen,txtlen;
+ struct stat sbuf;
+ char *hdr,*txt,lock[MAILTMPLEN],tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ MAILSTREAM *sysibx = NIL;
+ int ld;
+ /* give up if can't get exclusive permission */
+ if ((time (0) >= (LOCAL->lastsnarf +
+ (long) mail_parameters (NIL,GET_SNARFINTERVAL,NIL))) &&
+ strcmp (sysinbox (),stream->mailbox) &&
+ ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) >= 0)) {
+ MM_CRITICAL (stream); /* go critical */
+ /* sizes match and anything in sysinbox? */
+ if (!stat (sysinbox (),&sbuf) && sbuf.st_size &&
+ !fstat (LOCAL->fd,&sbuf) && (sbuf.st_size == LOCAL->filesize) &&
+ (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) &&
+ (!sysibx->rdonly) && (r = sysibx->nmsgs)) {
+ /* yes, go to end of file in our mailbox */
+ lseek (LOCAL->fd,sbuf.st_size,L_SET);
+ /* for each message in sysibx mailbox */
+ while (r && (++i <= sysibx->nmsgs)) {
+ /* snarf message from system INBOX */
+ hdr = cpystr (mail_fetchheader_full(sysibx,i,NIL,&hdrlen,FT_INTERNAL));
+ txt = mail_fetchtext_full (sysibx,i,&txtlen,FT_INTERNAL|FT_PEEK);
+ /* if have a message */
+ if (j = hdrlen + txtlen) {
+ /* calculate header line */
+ mail_date (LOCAL->buf,elt = mail_elt (sysibx,i));
+ sprintf (LOCAL->buf + strlen (LOCAL->buf),
+ ",%lu;0000000000%02o\n",j,(unsigned)
+ ((fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)));
+ /* copy message */
+ if ((write (LOCAL->fd,LOCAL->buf,strlen (LOCAL->buf)) < 0) ||
+ (write (LOCAL->fd,hdr,hdrlen) < 0) ||
+ (write (LOCAL->fd,txt,txtlen) < 0)) r = 0;
+ }
+ fs_give ((void **) &hdr);
+ }
+
+ /* make sure all the updates take */
+ if (fsync (LOCAL->fd)) r = 0;
+ if (r) { /* delete all the messages we copied */
+ if (r == 1) strcpy (tmp,"1");
+ else sprintf (tmp,"1:%lu",r);
+ mail_flag (sysibx,tmp,"\\Deleted",ST_SET);
+ mail_expunge (sysibx); /* now expunge all those messages */
+ }
+ else {
+ sprintf (LOCAL->buf,"Can't copy new mail: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,WARN);
+ ftruncate (LOCAL->fd,sbuf.st_size);
+ }
+ fstat (LOCAL->fd,&sbuf); /* yes, get current file size */
+ LOCAL->filetime = sbuf.st_mtime;
+ }
+ if (sysibx) mail_close (sysibx);
+ MM_NOCRITICAL (stream); /* release critical */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ LOCAL->lastsnarf = time (0);/* note time of last snarf */
+ }
+}
+
+/* Tenex mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long tenex_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ time_t tp[2];
+ struct stat sbuf;
+ off_t pos = 0;
+ int ld;
+ unsigned long i = 1;
+ unsigned long j,k,m,recent;
+ unsigned long n = 0;
+ unsigned long delta = 0;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (!(ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ tenex_ping (stream))); /* parse sequence if given, ping stream */
+ else if (stream->rdonly) MM_LOG ("Expunge ignored on readonly mailbox",WARN);
+ else {
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ }
+ /* The cretins who designed flock() created a window of vulnerability in
+ * upgrading locks from shared to exclusive or downgrading from exclusive
+ * to shared. Rather than maintain the lock at shared status at a minimum,
+ * flock() actually *releases* the former lock. Obviously they never talked
+ * to any database guys. Fortunately, we have the parse/append permission
+ * lock. If we require this lock before going exclusive on the mailbox,
+ * another process can not sneak in and steal the exclusive mailbox lock on
+ * us, because it will block on trying to get parse/append permission first.
+ */
+ /* get exclusive parse/append permission */
+ if ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) < 0)
+ MM_LOG ("Unable to lock expunge mailbox",ERROR);
+ /* make sure see any newly-arrived messages */
+ else if (!tenex_parse (stream));
+ /* get exclusive access */
+ else if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH);/* recover previous lock */
+ (*bn) (BLOCK_NONE,NIL);
+ MM_LOG ("Can't expunge because mailbox is in use by another process",
+ ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+
+ else {
+ MM_CRITICAL (stream); /* go critical */
+ recent = stream->recent; /* get recent now that pinged and locked */
+ /* for each message */
+ while (i <= stream->nmsgs) {
+ /* get cache element */
+ elt = tenex_elt (stream,i);
+ /* number of bytes to smash or preserve */
+ k = elt->private.special.text.size + tenex_size (stream,i);
+ /* if need to expunge this message */
+ if (elt->deleted && (sequence ? elt->sequence : T)) {
+ /* if recent, note one less recent message */
+ if (elt->recent) --recent;
+ delta += k; /* number of bytes to delete */
+ /* notify upper levels */
+ mail_expunged (stream,i);
+ n++; /* count up one more expunged message */
+ }
+ else if (i++ && delta) {/* preserved message */
+ /* first byte to preserve */
+ j = elt->private.special.offset;
+ do { /* read from source position */
+ m = min (k,LOCAL->buflen);
+ lseek (LOCAL->fd,j,L_SET);
+ read (LOCAL->fd,LOCAL->buf,m);
+ pos = j - delta; /* write to destination position */
+ lseek (LOCAL->fd,pos,L_SET);
+ while (T) {
+ lseek (LOCAL->fd,pos,L_SET);
+ if (write (LOCAL->fd,LOCAL->buf,m) > 0) break;
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+ pos += m; /* new position */
+ j += m; /* next chunk, perhaps */
+ } while (k -= m); /* until done */
+ /* note the new address of this text */
+ elt->private.special.offset -= delta;
+ }
+ /* preserved but no deleted messages */
+ else pos = elt->private.special.offset + k;
+ }
+
+ if (n) { /* truncate file after last message */
+ if (pos != (LOCAL->filesize -= delta)) {
+ sprintf (LOCAL->buf,
+ "Calculated size mismatch %lu != %lu, delta = %lu",
+ (unsigned long) pos,(unsigned long) LOCAL->filesize,delta);
+ MM_LOG (LOCAL->buf,WARN);
+ LOCAL->filesize = pos;/* fix it then */
+ }
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ sprintf (LOCAL->buf,"Expunged %lu messages",n);
+ /* output the news */
+ MM_LOG (LOCAL->buf,(long) NIL);
+ }
+ else MM_LOG ("No messages deleted, so no update needed",(long) NIL);
+ fsync (LOCAL->fd); /* force disk update */
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* reset atime to now */
+ utime (stream->mailbox,tp);
+ MM_NOCRITICAL (stream); /* release critical */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH);/* allow sharers again */
+ (*bn) (BLOCK_NONE,NIL);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+ }
+ return LONGT;
+}
+
+/* Tenex mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if success, NIL if failed
+ */
+
+long tenex_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ time_t tp[2];
+ MESSAGECACHE *elt;
+ unsigned long i,j,k;
+ long ret = LONGT;
+ int fd,ld;
+ char file[MAILTMPLEN],lock[MAILTMPLEN];
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ /* make sure valid mailbox */
+ if (!tenex_isvalid (mailbox,LOCAL->buf)) switch (errno) {
+ case ENOENT: /* no such file? */
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid Tenex-format mailbox name: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a Tenex-format mailbox: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* got file? */
+ if ((fd = open (tenex_file(file,mailbox),O_RDWR,NIL)) < 0) {
+ sprintf (LOCAL->buf,"Unable to open copy mailbox: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ MM_CRITICAL (stream); /* go critical */
+ /* get exclusive parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockfd (fd,lock,LOCK_EX)) < 0)) {
+ MM_LOG ("Unable to lock copy mailbox",ERROR);
+ MM_NOCRITICAL (stream);
+ return NIL;
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
+
+ /* for each requested message */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ /* number of bytes to copy */
+ k = elt->private.special.text.size + tenex_size (stream,i);
+ do { /* read from source position */
+ j = min (k,LOCAL->buflen);
+ read (LOCAL->fd,LOCAL->buf,j);
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ } while (ret && (k -= j));/* until done */
+ }
+ /* make sure all the updates take */
+ if (!(ret && (ret = !fsync (fd)))) {
+ sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ }
+ if (ret) tp[0] = time (0) - 1;/* set atime to now-1 if successful copy */
+ /* else preserve \Marked status */
+ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0);
+ tp[1] = sbuf.st_mtime; /* preserve mtime */
+ utime (file,tp); /* set the times */
+ close (fd); /* close the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ MM_NOCRITICAL (stream); /* release critical */
+ /* delete all requested messages */
+ if (ret && (options & CP_MOVE)) {
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = tenex_elt (stream,i))->sequence) {
+ elt->deleted = T; /* mark message deleted */
+ /* recalculate status */
+ tenex_update_status (stream,i,NIL);
+ }
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* make sure atime remains greater */
+ utime (stream->mailbox,tp);
+ }
+ }
+ if (ret && mail_parameters (NIL,GET_COPYUID,NIL))
+ MM_LOG ("Can not return meaningful COPYUID with this mailbox format",WARN);
+ return ret;
+}
+
+/* Tenex mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long tenex_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd,ld,c;
+ char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ time_t tp[2];
+ FILE *df;
+ MESSAGECACHE elt;
+ long f;
+ unsigned long i,j,uf,size;
+ STRING *message;
+ long ret = LONGT;
+ /* default stream to prototype */
+ if (!stream) stream = user_flags (&tenexproto);
+ /* make sure valid mailbox */
+ if (!tenex_isvalid (mailbox,tmp)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (!compare_cstring (mailbox,"INBOX")) dummy_create (NIL,"mail.txt");
+ else {
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* falls through */
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid TENEX-format mailbox name: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a TENEX-format mailbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get first message */
+ if (!MM_APPEND (af) (stream,data,&flags,&date,&message)) return NIL;
+
+ /* open destination mailbox */
+ if (((fd = open (tenex_file (file,mailbox),O_WRONLY|O_APPEND,NIL)) < 0) ||
+ !(df = fdopen (fd,"ab"))) {
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockfd (fd,lock,LOCK_EX)) < 0)) {
+ MM_LOG ("Unable to lock append mailbox",ERROR);
+ close (fd);
+ return NIL;
+ }
+ MM_CRITICAL (stream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ errno = 0;
+ do { /* parse flags */
+ if (!SIZE (message)) { /* guard against zero-length */
+ MM_LOG ("Append of zero-length message",ERROR);
+ ret = NIL;
+ break;
+ }
+ f = mail_parse_flags (stream,flags,&i);
+ /* reverse bits (dontcha wish we had CIRC?) */
+ for (uf = 0; i; uf |= 1 << (29 - find_rightmost_bit (&i)));
+ if (date) { /* parse date if given */
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* mark failure */
+ break;
+ }
+ mail_date (tmp,&elt); /* write preseved date */
+ }
+ else internal_date (tmp); /* get current date in IMAP format */
+ i = GETPOS (message); /* remember current position */
+ for (j = SIZE (message), size = 0; j; --j)
+ if (SNX (message) != '\015') ++size;
+ SETPOS (message,i); /* restore position */
+ /* write header */
+ if (fprintf (df,"%s,%lu;%010lo%02lo\n",tmp,size,uf,(unsigned long) f) < 0)
+ ret = NIL;
+ else { /* write message */
+ while (size) if ((c = 0xff & SNX (message)) != '\015') {
+ if (putc (c,df) != EOF) --size;
+ else break;
+ }
+ /* get next message */
+ if (size || !MM_APPEND (af) (stream,data,&flags,&date,&message))
+ ret = NIL;
+ }
+ } while (ret && message);
+ /* if error... */
+ if (!ret || (fflush (df) == EOF)) {
+ ftruncate (fd,sbuf.st_size);/* revert file */
+ close (fd); /* make sure fclose() doesn't corrupt us */
+ if (errno) {
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ }
+ ret = NIL;
+ }
+ if (ret) tp[0] = time (0) - 1;/* set atime to now-1 if successful copy */
+ /* else preserve \Marked status */
+ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0);
+ tp[1] = sbuf.st_mtime; /* preserve mtime */
+ utime (file,tp); /* set the times */
+ fclose (df); /* close the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ MM_NOCRITICAL (stream); /* release critical */
+ if (ret && mail_parameters (NIL,GET_APPENDUID,NIL))
+ MM_LOG ("Can not return meaningful APPENDUID with this mailbox format",
+ WARN);
+ return ret;
+}
+
+/* Internal routines */
+
+
+/* Tenex mail return internal message size in bytes
+ * Accepts: MAIL stream
+ * message #
+ * Returns: internal size of message
+ */
+
+unsigned long tenex_size (MAILSTREAM *stream,unsigned long m)
+{
+ MESSAGECACHE *elt = mail_elt (stream,m);
+ return ((m < stream->nmsgs) ? mail_elt (stream,m+1)->private.special.offset :
+ LOCAL->filesize) -
+ (elt->private.special.offset + elt->private.special.text.size);
+}
+
+
+/* Tenex mail generate file string
+ * Accepts: temporary buffer to write into
+ * mailbox name string
+ * Returns: local file string or NIL if failure
+ */
+
+char *tenex_file (char *dst,char *name)
+{
+ char tmp[MAILTMPLEN];
+ char *s = mailboxfile (dst,name);
+ /* return our standard inbox */
+ return (s && !*s) ? mailboxfile (dst,tenex_isvalid ("~/INBOX",tmp) ?
+ "~/INBOX" : "mail.txt") : s;
+}
+
+/* Tenex mail parse mailbox
+ * Accepts: MAIL stream
+ * Returns: T if parse OK
+ * NIL if failure, stream aborted
+ */
+
+long tenex_parse (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = NIL;
+ unsigned char c,*s,*t,*x;
+ char tmp[MAILTMPLEN];
+ unsigned long i,j;
+ long curpos = LOCAL->filesize;
+ long nmsgs = stream->nmsgs;
+ long recent = stream->recent;
+ short added = NIL;
+ short silent = stream->silent;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ if (sbuf.st_size < curpos) { /* sanity check */
+ sprintf (tmp,"Mailbox shrank from %lu to %lu!",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size);
+ MM_LOG (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ stream->silent = T; /* don't pass up exists events yet */
+ while (sbuf.st_size - curpos){/* while there is stuff to parse */
+ /* get to that position in the file */
+ lseek (LOCAL->fd,curpos,L_SET);
+ if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
+ sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size,
+ i ? strerror (errno) : "no data read");
+ MM_LOG (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ LOCAL->buf[i] = '\0'; /* tie off buffer just in case */
+ if (!(s = strchr (LOCAL->buf,'\012'))) {
+ sprintf (tmp,"Unable to find newline at %lu in %lu bytes, text: %s",
+ (unsigned long) curpos,i,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ *s = '\0'; /* tie off header line */
+ i = (s + 1) - LOCAL->buf; /* note start of text offset */
+ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
+ sprintf (tmp,"Unable to parse internal header at %lu: %s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ *s++ = '\0'; *t++ = '\0'; /* tie off fields */
+
+ added = T; /* note that a new message was added */
+ /* swell the cache */
+ mail_exists (stream,++nmsgs);
+ /* instantiate an elt for this message */
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ elt->private.uid = ++stream->uid_last;
+ /* note file offset of header */
+ elt->private.special.offset = curpos;
+ /* in case error */
+ elt->private.special.text.size = 0;
+ /* header size not known yet */
+ elt->private.msg.header.text.size = 0;
+ x = s; /* parse the header components */
+ if (mail_parse_date (elt,LOCAL->buf) &&
+ (elt->private.msg.full.text.size = strtoul (s,(char **) &s,10)) &&
+ (!(s && *s)) && isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) &&
+ isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) &&
+ isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) &&
+ isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12])
+ elt->private.special.text.size = i;
+ else { /* oops */
+ sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s",
+ curpos,(char *) LOCAL->buf,(char *) x,(char *) t);
+ MM_LOG (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ /* make sure didn't run off end of file */
+ if ((curpos += (elt->private.msg.full.text.size + i)) > sbuf.st_size) {
+ sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)",
+ elt->private.special.offset,(unsigned long) curpos,
+ (unsigned long) sbuf.st_size);
+ MM_LOG (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ c = t[10]; /* remember first system flags byte */
+ t[10] = '\0'; /* tie off flags */
+ j = strtoul (t,NIL,8); /* get user flags value */
+ t[10] = c; /* restore first system flags byte */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ /* calculate system flags */
+ if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T;
+ if (j & fDELETED) elt->deleted = T;
+ if (j & fFLAGGED) elt->flagged = T;
+ if (j & fANSWERED) elt->answered = T;
+ if (j & fDRAFT) elt->draft = T;
+ if (!(j & fOLD)) { /* newly arrived message? */
+ elt->recent = T;
+ recent++; /* count up a new recent message */
+ /* mark it as old */
+ tenex_update_status (stream,nmsgs,NIL);
+ }
+ }
+ fsync (LOCAL->fd); /* make sure all the fOLD flags take */
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */
+ LOCAL->filetime = sbuf.st_mtime;
+ if (added && !stream->rdonly){/* make sure atime updated */
+ time_t tp[2];
+ tp[0] = time (0);
+ tp[1] = LOCAL->filetime;
+ utime (stream->mailbox,tp);
+ }
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
+ mail_recent (stream,recent); /* and of change in recent messages */
+ return LONGT; /* return the winnage */
+}
+
+/* Tenex get cache element with status updating from file
+ * Accepts: MAIL stream
+ * message number
+ * Returns: cache element
+ */
+
+MESSAGECACHE *tenex_elt (MAILSTREAM *stream,unsigned long msgno)
+{
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ struct { /* old flags */
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged;
+ old.answered = elt->answered; old.draft = elt->draft;
+ old.user_flags = elt->user_flags;
+ tenex_read_flags (stream,elt);
+ if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
+ (old.flagged != elt->flagged) || (old.answered != elt->answered) ||
+ (old.draft != elt->draft) || (old.user_flags != elt->user_flags))
+ MM_FLAGS (stream,msgno); /* let top level know */
+ return elt;
+}
+
+/* Tenex read flags from file
+ * Accepts: MAIL stream
+ * Returns: cache element
+ */
+
+void tenex_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ unsigned long i,j;
+ /* noop if readonly and have valid flags */
+ if (stream->rdonly && elt->valid) return;
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 13,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,12) < 0) {
+ sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ /* calculate system flags */
+ i = (((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0');
+ elt->seen = i & fSEEN ? T : NIL; elt->deleted = i & fDELETED ? T : NIL;
+ elt->flagged = i & fFLAGGED ? T : NIL;
+ elt->answered = i & fANSWERED ? T : NIL; elt->draft = i & fDRAFT ? T : NIL;
+ LOCAL->buf[10] = '\0'; /* tie off flags */
+ j = strtoul(LOCAL->buf,NIL,8);/* get user flags value */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ elt->valid = T; /* have valid flags now */
+}
+
+/* Tenex update status string
+ * Accepts: MAIL stream
+ * message number
+ * flag saying whether or not to sync
+ */
+
+void tenex_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag)
+{
+ time_t tp[2];
+ struct stat sbuf;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ unsigned long j,k = 0;
+ /* readonly */
+ if (stream->rdonly || !elt->valid) tenex_read_flags (stream,elt);
+ else { /* readwrite */
+ j = elt->user_flags; /* get user flags */
+ /* reverse bits (dontcha wish we had CIRC?) */
+ while (j) k |= 1 << (29 - find_rightmost_bit (&j));
+ /* print new flag string */
+ sprintf (LOCAL->buf,"%010lo%02o",k,(unsigned)
+ (fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)));
+ /* get to that place in the file */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 13,L_SET);
+ /* write new flags */
+ write (LOCAL->fd,LOCAL->buf,12);
+ if (syncflag) { /* sync if requested */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* make sure read is later */
+ utime (stream->mailbox,tp);
+ }
+ }
+}
+
+/* Tenex locate header for a message
+ * Accepts: MAIL stream
+ * message number
+ * pointer to returned header size
+ * Returns: position of header in file
+ */
+
+unsigned long tenex_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size)
+{
+ unsigned long siz;
+ long i = 0;
+ char c = '\0';
+ char *s = NIL;
+ MESSAGECACHE *elt = tenex_elt (stream,msgno);
+ unsigned long ret = elt->private.special.offset +
+ elt->private.special.text.size;
+ unsigned long msiz = tenex_size (stream,msgno);
+ /* is header size known? */
+ if (!(*size = elt->private.msg.header.text.size)) {
+ lseek (LOCAL->fd,ret,L_SET);/* get to header position */
+ /* search message for LF LF */
+ for (siz = 0; siz < msiz; siz++) {
+ if (--i <= 0) /* read another buffer as necessary */
+ read (LOCAL->fd,s = LOCAL->buf,i = min (msiz-siz,(long) MAILTMPLEN));
+ /* two newline sequence? */
+ if ((c == '\012') && (*s == '\012')) {
+ /* yes, note for later */
+ elt->private.msg.header.text.size = (*size = siz + 1);
+
+ return ret; /* return to caller */
+ }
+ else c = *s++; /* next character */
+ }
+ /* header consumes entire message */
+ elt->private.msg.header.text.size = *size = msiz;
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/amiga/tz_bsd.c b/imap/src/osdep/amiga/tz_bsd.c
new file mode 100644
index 00000000..75ee624c
--- /dev/null
+++ b/imap/src/osdep/amiga/tz_bsd.c
@@ -0,0 +1,38 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: BSD-style Time Zone String
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 30 August 1994
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Append local timezone name
+ * Accepts: destination string
+ */
+
+void rfc822_timezone (char *s,void *t)
+{
+ /* append timezone from tm struct */
+ sprintf (s + strlen (s)," (%.50s)",((struct tm *) t)->tm_zone);
+}
diff --git a/imap/src/osdep/amiga/unix.c b/imap/src/osdep/amiga/unix.c
new file mode 100644
index 00000000..be3c437b
--- /dev/null
+++ b/imap/src/osdep/amiga/unix.c
@@ -0,0 +1,2708 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX mail routines
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@Washington.EDU
+ *
+ * Date: 20 December 1989
+ * Last Edited: 27 March 2008
+ */
+
+
+/* DEDICATION
+ *
+ * This file is dedicated to my dog, Unix, also known as Yun-chan and
+ * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix
+ * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after
+ * a two-month bout with cirrhosis of the liver.
+ *
+ * He was a dear friend, and I miss him terribly.
+ *
+ * Lift a leg, Yunie. Luv ya forever!!!!
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <signal.h>
+#include "mail.h"
+#include "osdep.h"
+#include <time.h>
+#include <sys/stat.h>
+#include "unix.h"
+#include "pseudo.h"
+#include "fdstring.h"
+#include "misc.h"
+#include "dummy.h"
+
+/* UNIX I/O stream local data */
+
+typedef struct unix_local {
+ unsigned int dirty : 1; /* disk copy needs updating */
+ unsigned int ddirty : 1; /* double-dirty, ping becomes checkpoint */
+ unsigned int pseudo : 1; /* uses a pseudo message */
+ unsigned int appending : 1; /* don't mark new messages as old */
+ int fd; /* mailbox file descriptor */
+ int ld; /* lock file descriptor */
+ char *lname; /* lock file name */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ time_t lastsnarf; /* last snarf time (for mbox driver) */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ unsigned long uid; /* current text uid */
+ SIZEDTEXT text; /* current text */
+ unsigned long textlen; /* current text length */
+ char *line; /* returned line */
+ char *linebuf; /* line readin buffer */
+ unsigned long linebuflen; /* current line readin buffer length */
+} UNIXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((UNIXLOCAL *) stream->local)
+
+
+/* UNIX protected file structure */
+
+typedef struct unix_file {
+ MAILSTREAM *stream; /* current stream */
+ off_t curpos; /* current file position */
+ off_t protect; /* protected position */
+ off_t filepos; /* current last written file position */
+ char *buf; /* overflow buffer */
+ size_t buflen; /* current overflow buffer length */
+ char *bufpos; /* current buffer position */
+} UNIXFILE;
+
+/* Function prototypes */
+
+DRIVER *unix_valid (char *name);
+long unix_isvalid_fd (int fd);
+void *unix_parameters (long function,void *value);
+void unix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void unix_list (MAILSTREAM *stream,char *ref,char *pat);
+void unix_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long unix_create (MAILSTREAM *stream,char *mailbox);
+long unix_delete (MAILSTREAM *stream,char *mailbox);
+long unix_rename (MAILSTREAM *stream,char *old,char *newname);
+MAILSTREAM *unix_open (MAILSTREAM *stream);
+void unix_close (MAILSTREAM *stream,long options);
+char *unix_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long unix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt,
+ unsigned long *length,long flags);
+void unix_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long unix_ping (MAILSTREAM *stream);
+void unix_check (MAILSTREAM *stream);
+long unix_expunge (MAILSTREAM *stream,char *sequence,long options);
+long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long unix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+int unix_collect_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
+ STRING *msg);
+int unix_append_msgs (MAILSTREAM *stream,FILE *sf,FILE *df,SEARCHSET *set);
+
+void unix_abort (MAILSTREAM *stream);
+char *unix_file (char *dst,char *name);
+int unix_lock (char *file,int flags,int mode,DOTLOCK *lock,int op);
+void unix_unlock (int fd,MAILSTREAM *stream,DOTLOCK *lock);
+int unix_parse (MAILSTREAM *stream,DOTLOCK *lock,int op);
+char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size);
+unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr);
+unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt,
+ unsigned long uid,long flag);
+long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,DOTLOCK *lock,
+ long flags);
+long unix_extend (MAILSTREAM *stream,unsigned long size);
+void unix_write (UNIXFILE *f,char *s,unsigned long i);
+void unix_phys_write (UNIXFILE *f,char *buf,size_t size);
+
+/* mbox mail routines */
+
+/* Function prototypes */
+
+DRIVER *mbox_valid (char *name);
+long mbox_create (MAILSTREAM *stream,char *mailbox);
+long mbox_delete (MAILSTREAM *stream,char *mailbox);
+long mbox_rename (MAILSTREAM *stream,char *old,char *newname);
+long mbox_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *mbox_open (MAILSTREAM *stream);
+long mbox_ping (MAILSTREAM *stream);
+void mbox_check (MAILSTREAM *stream);
+long mbox_expunge (MAILSTREAM *stream,char *sequence,long options);
+long mbox_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+
+/* UNIX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER unixdriver = {
+ "unix", /* driver name */
+ /* driver flags */
+ DR_LOCAL|DR_MAIL|DR_LOCKING|DR_NONEWMAILRONLY|DR_XPOINT,
+ (DRIVER *) NIL, /* next driver */
+ unix_valid, /* mailbox is valid for us */
+ unix_parameters, /* manipulate parameters */
+ unix_scan, /* scan mailboxes */
+ unix_list, /* list mailboxes */
+ unix_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ unix_create, /* create mailbox */
+ unix_delete, /* delete mailbox */
+ unix_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ unix_open, /* open mailbox */
+ unix_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ unix_header, /* fetch message header */
+ unix_text, /* fetch message text */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ NIL, /* modify flags */
+ unix_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ unix_ping, /* ping mailbox to see if still alive */
+ unix_check, /* check for new messages */
+ unix_expunge, /* expunge deleted messages */
+ unix_copy, /* copy messages to another mailbox */
+ unix_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM unixproto = {&unixdriver};
+
+ /* driver parameters */
+static long unix_fromwidget = T;
+
+/* UNIX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *unix_valid (char *name)
+{
+ int fd;
+ DRIVER *ret = NIL;
+ char *t,file[MAILTMPLEN];
+ struct stat sbuf;
+ time_t tp[2];
+ errno = EINVAL; /* assume invalid argument */
+ /* must be non-empty file */
+ if ((t = dummy_file (file,name)) && !stat (t,&sbuf)) {
+ if (!sbuf.st_size)errno = 0;/* empty file */
+ else if ((fd = open (file,O_RDONLY,NIL)) >= 0) {
+ /* OK if mailbox format good */
+ if (unix_isvalid_fd (fd)) ret = &unixdriver;
+ else errno = -1; /* invalid format */
+ close (fd); /* close the file */
+ /* \Marked status? */
+ if ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) {
+ tp[0] = sbuf.st_atime; /* yes, preserve atime and mtime */
+ tp[1] = sbuf.st_mtime;
+ utime (file,tp); /* set the times */
+ }
+ }
+ }
+ return ret; /* return what we should */
+}
+
+/* UNIX mail test for valid mailbox
+ * Accepts: file descriptor
+ * scratch buffer
+ * Returns: T if valid, NIL otherwise
+ */
+
+long unix_isvalid_fd (int fd)
+{
+ int zn;
+ int ret = NIL;
+ char tmp[MAILTMPLEN],*s,*t,c = '\n';
+ memset (tmp,'\0',MAILTMPLEN);
+ if (read (fd,tmp,MAILTMPLEN-1) >= 0) {
+ for (s = tmp; (*s == '\r') || (*s == '\n') || (*s == ' ') || (*s == '\t');)
+ c = *s++;
+ if (c == '\n') VALID (s,t,ret,zn);
+ }
+ return ret; /* return what we should */
+}
+
+
+/* UNIX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *unix_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = dummy_file ((char *) value,"INBOX");
+ break;
+ case SET_FROMWIDGET:
+ unix_fromwidget = (long) value;
+ case GET_FROMWIDGET:
+ ret = (void *) unix_fromwidget;
+ break;
+ }
+ return ret;
+}
+
+/* UNIX mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void unix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* UNIX mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void unix_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* UNIX mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void unix_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* UNIX mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long unix_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,mbx[MAILTMPLEN],tmp[MAILTMPLEN];
+ long ret = NIL;
+ int i,fd;
+ time_t ti = time (0);
+ if (!(s = dummy_file (mbx,mailbox))) {
+ sprintf (tmp,"Can't create %.80s: invalid name",mailbox);
+ MM_LOG (tmp,ERROR);
+ }
+ /* create underlying file */
+ else if (dummy_create_path (stream,s,get_dir_protection (mailbox))) {
+ /* done if dir-only or whiner */
+ if (((s = strrchr (s,'/')) && !s[1]) ||
+ mail_parameters (NIL,GET_USERHASNOLIFE,NIL)) ret = T;
+ else if ((fd = open (mbx,O_WRONLY,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL))) < 0) {
+ sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ else { /* initialize header */
+ memset (tmp,'\0',MAILTMPLEN);
+ sprintf (tmp,"From %s %sDate: ",pseudo_from,ctime (&ti));
+ rfc822_fixed_date (s = tmp + strlen (tmp));
+ /* write the pseudo-header */
+ sprintf (s += strlen (s),
+ "\nFrom: %s <%s@%s>\nSubject: %s\nX-IMAP: %010lu 0000000000",
+ pseudo_name,pseudo_from,mylocalhost (),pseudo_subject,
+ (unsigned long) ti);
+ for (i = 0; i < NUSERFLAGS; ++i) if (default_user_flag (i))
+ sprintf (s += strlen (s)," %s",default_user_flag (i));
+ sprintf (s += strlen (s),"\nStatus: RO\n\n%s\n\n",pseudo_msg);
+ if (write (fd,tmp,strlen (tmp)) > 0) ret = T;
+ else {
+ sprintf (tmp,"Can't initialize mailbox node %.80s: %s",mbx,
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ close (fd); /* close file */
+ }
+ }
+ /* set proper protections */
+ return ret ? set_mbx_protections (mailbox,mbx) : NIL;
+}
+
+/* UNIX mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long unix_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return unix_rename (stream,mailbox,NIL);
+}
+
+
+/* UNIX mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long unix_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = NIL;
+ char c,*s = NIL;
+ char tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ DOTLOCK lockx;
+ int fd,ld;
+ long i;
+ struct stat sbuf;
+ MM_CRITICAL (stream); /* get the c-client lock */
+ if (!dummy_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'/')) && !s[1]))))
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ /* lock out other c-clients */
+ else if ((ld = lockname (lock,file,LOCK_EX|LOCK_NB,&i)) < 0)
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+
+ else {
+ if ((fd = unix_lock (file,O_RDWR,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL),
+ &lockx,LOCK_EX)) < 0)
+ sprintf (tmp,"Can't lock mailbox %.80s: %s",old,strerror (errno));
+ else {
+ if (newname) { /* want rename? */
+ /* found superior to destination name? */
+ if (s = strrchr (s,'/')) {
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,tmp,get_dir_protection (newname))) {
+ unix_unlock (fd,NIL,&lockx);
+ unix_unlock (ld,NIL,NIL);
+ unlink (lock);
+ MM_NOCRITICAL (stream);
+ return ret; /* return success or failure */
+ }
+ *s = c; /* restore full name */
+ }
+ if (rename (file,tmp))
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ else ret = T; /* set success */
+ }
+ else if (unlink (file))
+ sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
+ else ret = T; /* set success */
+ unix_unlock (fd,NIL,&lockx);
+ }
+ unix_unlock (ld,NIL,NIL); /* flush the lock */
+ unlink (lock);
+ }
+ MM_NOCRITICAL (stream); /* no longer critical */
+ if (!ret) MM_LOG (tmp,ERROR); /* log error */
+ return ret; /* return success or failure */
+}
+
+/* UNIX mail open
+ * Accepts: Stream to open
+ * Returns: Stream on success, NIL on failure
+ */
+
+MAILSTREAM *unix_open (MAILSTREAM *stream)
+{
+ long i;
+ int fd;
+ char tmp[MAILTMPLEN];
+ DOTLOCK lock;
+ long retry;
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return user_flags (&unixproto);
+ retry = stream->silent ? 1 : KODRETRY;
+ if (stream->local) fatal ("unix recycle stream");
+ stream->local = memset (fs_get (sizeof (UNIXLOCAL)),0,sizeof (UNIXLOCAL));
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ /* canonicalize the stream mailbox name */
+ if (!dummy_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* flush old name */
+ fs_give ((void **) &stream->mailbox);
+ /* save canonical name */
+ stream->mailbox = cpystr (tmp);
+ LOCAL->fd = LOCAL->ld = -1; /* no file or state locking yet */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ LOCAL->text.data = (unsigned char *) fs_get (CHUNKSIZE);
+ LOCAL->text.size = CHUNKSIZE - 1;
+ LOCAL->linebuf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->linebuflen = CHUNKSIZE - 1;
+ stream->sequence++; /* bump sequence number */
+
+ /* make lock for read/write access */
+ if (!stream->rdonly) while (retry) {
+ /* try to lock file */
+ if ((fd = lockname (tmp,stream->mailbox,LOCK_EX|LOCK_NB,&i)) < 0) {
+ /* suppressing kiss-of-death? */
+ if (stream->nokod) retry = 0;
+ /* no, first time through? */
+ else if (retry-- == KODRETRY) {
+ /* learned other guy's PID and can signal? */
+ if (i && !kill ((int) i,SIGUSR2)) {
+ sprintf (tmp,"Trying to get mailbox lock from process %ld",i);
+ MM_LOG (tmp,WARN);
+ }
+ else retry = 0; /* give up */
+ }
+ if (!stream->silent) { /* nothing if silent stream */
+ if (retry) sleep (1); /* wait a second before trying again */
+ else MM_LOG ("Mailbox is open by another process, access is readonly",
+ WARN);
+ }
+ }
+ else { /* got the lock, nobody else can alter state */
+ LOCAL->ld = fd; /* note lock's fd and name */
+ LOCAL->lname = cpystr (tmp);
+ /* make sure mode OK (don't use fchmod()) */
+ chmod (LOCAL->lname,(long) mail_parameters (NIL,GET_LOCKPROTECTION,NIL));
+ if (stream->silent) i = 0;/* silent streams won't accept KOD */
+ else { /* note our PID in the lock */
+ sprintf (tmp,"%d",getpid ());
+ write (fd,tmp,(i = strlen (tmp))+1);
+ }
+ ftruncate (fd,i); /* make sure tied off */
+ fsync (fd); /* make sure it's available */
+ retry = 0; /* no more need to try */
+ }
+ }
+
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ /* will we be able to get write access? */
+ if ((LOCAL->ld >= 0) && access (stream->mailbox,W_OK) && (errno == EACCES)) {
+ MM_LOG ("Can't get write access to mailbox, access is readonly",WARN);
+ flock (LOCAL->ld,LOCK_UN); /* release the lock */
+ close (LOCAL->ld); /* close the lock file */
+ LOCAL->ld = -1; /* no more lock fd */
+ unlink (LOCAL->lname); /* delete it */
+ }
+ /* reset UID validity */
+ stream->uid_validity = stream->uid_last = 0;
+ if (stream->silent && !stream->rdonly && (LOCAL->ld < 0))
+ unix_abort (stream); /* abort if can't get RW silent stream */
+ /* parse mailbox */
+ else if (unix_parse (stream,&lock,LOCK_SH)) {
+ unix_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream);
+ MM_NOCRITICAL (stream); /* done with critical */
+ }
+ if (!LOCAL) return NIL; /* failure if stream died */
+ /* make sure upper level knows readonly */
+ stream->rdonly = (LOCAL->ld < 0);
+ /* notify about empty mailbox */
+ if (!(stream->nmsgs || stream->silent)) MM_LOG ("Mailbox is empty",NIL);
+ if (!stream->rdonly) { /* flags stick if readwrite */
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft = T;
+ if (!stream->uid_nosticky) {/* users with lives get permanent keywords */
+ stream->perm_user_flags = 0xffffffff;
+ /* and maybe can create them too! */
+ stream->kwd_create = stream->user_flags[NUSERFLAGS-1] ? NIL : T;
+ }
+ }
+ return stream; /* return stream alive to caller */
+}
+
+
+/* UNIX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void unix_close (MAILSTREAM *stream,long options)
+{
+ int silent = stream->silent;
+ stream->silent = T; /* go silent */
+ /* expunge if requested */
+ if (options & CL_EXPUNGE) unix_expunge (stream,NIL,NIL);
+ /* else dump final checkpoint */
+ else if (LOCAL->dirty) unix_check (stream);
+ stream->silent = silent; /* restore old silence state */
+ unix_abort (stream); /* now punt the file and local data */
+}
+
+/* UNIX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+ /* lines to filter from header */
+static STRINGLIST *unix_hlines = NIL;
+
+char *unix_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned char *s,*t,*tl;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ elt = mail_elt (stream,msgno);/* get cache */
+ if (!unix_hlines) { /* once only code */
+ STRINGLIST *lines = unix_hlines = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "Status"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-Status"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-Keywords"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-UID"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-IMAP"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-IMAPbase"));
+ }
+ /* go to header position */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.msg.header.offset,L_SET);
+
+ if (flags & FT_INTERNAL) { /* initial data OK? */
+ if (elt->private.msg.header.text.size > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
+ elt->private.msg.header.text.size) + 1);
+ }
+ /* read message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.header.text.size);
+ /* got text, tie off string */
+ LOCAL->buf[*length = elt->private.msg.header.text.size] = '\0';
+ /* squeeze out CRs (in case from PC) */
+ for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t < tl; t++)
+ if (*t != '\r') *s++ = *t;
+ *s = '\0';
+ *length = s - LOCAL->buf; /* adjust length */
+ }
+ else { /* need to make a CRLF version */
+ read (LOCAL->fd,s = (char *) fs_get (elt->private.msg.header.text.size+1),
+ elt->private.msg.header.text.size);
+ /* tie off string, and convert to CRLF */
+ s[elt->private.msg.header.text.size] = '\0';
+ *length = strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,s,
+ elt->private.msg.header.text.size);
+ fs_give ((void **) &s); /* free readin buffer */
+ /* squeeze out spurious CRs */
+ for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t < tl; t++)
+ if ((*t != '\r') || (t[1] == '\n')) *s++ = *t;
+ *s = '\0';
+ *length = s - LOCAL->buf; /* adjust length */
+ }
+ *length = mail_filter (LOCAL->buf,*length,unix_hlines,FT_NOT);
+ return (char *) LOCAL->buf; /* return processed copy */
+}
+
+/* UNIX mail fetch message text
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T on success, NIL if failure
+ */
+
+long unix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ char *s;
+ unsigned long i;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mail_elt (stream,msgno);/* get cache element */
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ /* mark message seen and dirty */
+ elt->seen = elt->private.dirty = LOCAL->dirty = T;
+ MM_FLAGS (stream,msgno);
+ }
+ s = unix_text_work (stream,elt,&i,flags);
+ INIT (bs,mail_string,s,i); /* set up stringstruct */
+ return T; /* success */
+}
+
+/* UNIX mail fetch message text worker routine
+ * Accepts: MAIL stream
+ * message cache element
+ * pointer to returned header text length
+ * option flags
+ */
+
+char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt,
+ unsigned long *length,long flags)
+{
+ FDDATA d;
+ STRING bs;
+ unsigned char c,*s,*t,*tl,tmp[CHUNKSIZE];
+ /* go to text position */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.msg.text.offset,L_SET);
+ if (flags & FT_INTERNAL) { /* initial data OK? */
+ if (elt->private.msg.text.text.size > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
+ elt->private.msg.text.text.size) + 1);
+ }
+ /* read message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.text.text.size);
+ /* got text, tie off string */
+ LOCAL->buf[*length = elt->private.msg.text.text.size] = '\0';
+ /* squeeze out CRs (in case from PC) */
+ for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t < tl; t++)
+ if (*t != '\r') *s++ = *t;
+ *s = '\0';
+ *length = s - LOCAL->buf; /* adjust length */
+ return (char *) LOCAL->buf;
+ }
+
+ /* have it cached already? */
+ if (elt->private.uid != LOCAL->uid) {
+ /* not cached, cache it now */
+ LOCAL->uid = elt->private.uid;
+ /* is buffer big enough? */
+ if (elt->rfc822_size > LOCAL->text.size) {
+ /* excessively conservative, but the right thing is too hard to do */
+ fs_give ((void **) &LOCAL->text.data);
+ LOCAL->text.data = (unsigned char *)
+ fs_get ((LOCAL->text.size = elt->rfc822_size) + 1);
+ }
+ d.fd = LOCAL->fd; /* yes, set up file descriptor */
+ d.pos = elt->private.special.offset + elt->private.msg.text.offset;
+ d.chunk = tmp; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE; /* file chunk size */
+ INIT (&bs,fd_string,&d,elt->private.msg.text.text.size);
+ for (s = (char *) LOCAL->text.data; SIZE (&bs);) switch (c = SNX (&bs)) {
+ case '\r': /* carriage return seen */
+ break;
+ case '\n':
+ *s++ = '\r'; /* insert a CR */
+ default:
+ *s++ = c; /* copy characters */
+ }
+ *s = '\0'; /* tie off buffer */
+ /* calculate length of cached data */
+ LOCAL->textlen = s - LOCAL->text.data;
+ }
+ *length = LOCAL->textlen; /* return from cache */
+ return (char *) LOCAL->text.data;
+}
+
+/* UNIX per-message modify flag
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void unix_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ /* only after finishing */
+ if (elt->valid) elt->private.dirty = LOCAL->dirty = T;
+}
+
+
+/* UNIX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long unix_ping (MAILSTREAM *stream)
+{
+ DOTLOCK lock;
+ struct stat sbuf;
+ long reparse;
+ /* big no-op if not readwrite */
+ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock) {
+ if (stream->rdonly) { /* does he want to give up readwrite? */
+ /* checkpoint if we changed something */
+ if (LOCAL->dirty) unix_check (stream);
+ flock (LOCAL->ld,LOCK_UN);/* release readwrite lock */
+ close (LOCAL->ld); /* close the readwrite lock file */
+ LOCAL->ld = -1; /* no more readwrite lock fd */
+ unlink (LOCAL->lname); /* delete the readwrite lock file */
+ }
+ else { /* see if need to reparse */
+ if (!(reparse = (long) mail_parameters (NIL,GET_NETFSSTATBUG,NIL))) {
+ /* get current mailbox size */
+ if (LOCAL->fd >= 0) fstat (LOCAL->fd,&sbuf);
+ else if (stat (stream->mailbox,&sbuf)) {
+ sprintf (LOCAL->buf,"Mailbox stat failed, aborted: %s",
+ strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ unix_abort (stream);
+ return NIL;
+ }
+ reparse = (sbuf.st_size != LOCAL->filesize);
+ }
+ /* parse if mailbox changed */
+ if ((LOCAL->ddirty || reparse) && unix_parse (stream,&lock,LOCK_EX)) {
+ /* force checkpoint if double-dirty */
+ if (LOCAL->ddirty) unix_rewrite (stream,NIL,&lock,NIL);
+ /* unlock mailbox */
+ else unix_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream); /* and stream */
+ MM_NOCRITICAL (stream); /* done with critical */
+ }
+ }
+ }
+ return LOCAL ? LONGT : NIL; /* return if still alive */
+}
+
+/* UNIX mail check mailbox
+ * Accepts: MAIL stream
+ */
+
+void unix_check (MAILSTREAM *stream)
+{
+ DOTLOCK lock;
+ /* parse and lock mailbox */
+ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock &&
+ unix_parse (stream,&lock,LOCK_EX)) {
+ /* any unsaved changes? */
+ if (LOCAL->dirty && unix_rewrite (stream,NIL,&lock,NIL)) {
+ if (!stream->silent) MM_LOG ("Checkpoint completed",NIL);
+ }
+ /* no checkpoint needed, just unlock */
+ else unix_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream); /* unlock the stream */
+ MM_NOCRITICAL (stream); /* done with critical */
+ }
+}
+
+
+/* UNIX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long unix_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ unsigned long i;
+ DOTLOCK lock;
+ char *msg = NIL;
+ /* parse and lock mailbox */
+ if (ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ LOCAL && (LOCAL->ld >= 0) && !stream->lock &&
+ unix_parse (stream,&lock,LOCK_EX)) {
+ /* check expunged messages if not dirty */
+ for (i = 1; !LOCAL->dirty && (i <= stream->nmsgs); i++) {
+ MESSAGECACHE *elt = mail_elt (stream,i);
+ if (mail_elt (stream,i)->deleted) LOCAL->dirty = T;
+ }
+ if (!LOCAL->dirty) { /* not dirty and no expunged messages */
+ unix_unlock (LOCAL->fd,stream,&lock);
+ msg = "No messages deleted, so no update needed";
+ }
+ else if (unix_rewrite (stream,&i,&lock,sequence ? LONGT : NIL)) {
+ if (i) sprintf (msg = LOCAL->buf,"Expunged %lu messages",i);
+ else msg = "Mailbox checkpointed, but no messages expunged";
+ }
+ /* rewrite failed */
+ else unix_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream); /* unlock the stream */
+ MM_NOCRITICAL (stream); /* done with critical */
+ if (msg && !stream->silent) MM_LOG (msg,NIL);
+ }
+ else if (!stream->silent) MM_LOG("Expunge ignored on readonly mailbox",WARN);
+ return ret;
+}
+
+/* UNIX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if copy successful, else NIL
+ */
+
+long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ int fd;
+ char *s,file[MAILTMPLEN];
+ DOTLOCK lock;
+ time_t tp[2];
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ long ret = T;
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ copyuid_t cu = (copyuid_t) (mail_parameters (NIL,GET_USERHASNOLIFE,NIL) ?
+ NIL : mail_parameters (NIL,GET_COPYUID,NIL));
+ SEARCHSET *source = cu ? mail_newsearchset () : NIL;
+ SEARCHSET *dest = cu ? mail_newsearchset () : NIL;
+ MAILSTREAM *tstream = NIL;
+ DRIVER *d;
+ for (d = (DRIVER *) mail_parameters (NIL,GET_DRIVERS,NIL);
+ (d && strcmp (d->name,"mbox") && !(d->flags & DR_DISABLE));
+ d = d->next); /* see if mbox driver active */
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* make sure destination is valid */
+ if (!((d && mbox_valid (mailbox) && (mailbox = "mbox")) ||
+ unix_valid (mailbox) || !errno))
+ switch (errno) {
+ case ENOENT: /* no such file? */
+ if (compare_cstring (mailbox,"INBOX")) {
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ }
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ unix_create (NIL,"INBOX");/* create empty INBOX */
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid UNIX-format mailbox name: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a UNIX-format mailbox: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+
+ /* try to open rewrite for UIDPLUS */
+ if ((tstream = mail_open_work (&unixdriver,NIL,mailbox,
+ OP_SILENT|OP_NOKOD)) && tstream->rdonly)
+ tstream = mail_close (tstream);
+ if (cu && !tstream) { /* wanted a COPYUID? */
+ sprintf (LOCAL->buf,"Unable to write-open mailbox for COPYUID: %.80s",
+ mailbox);
+ MM_LOG (LOCAL->buf,WARN);
+ cu = NIL; /* don't try to do COPYUID */
+ }
+ LOCAL->buf[0] = '\0';
+ MM_CRITICAL (stream); /* go critical */
+ if ((fd = unix_lock (dummy_file (file,mailbox),O_WRONLY|O_APPEND,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL),
+ &lock,LOCK_EX)) < 0) {
+ MM_NOCRITICAL (stream); /* done with critical */
+ sprintf (LOCAL->buf,"Can't open destination mailbox: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);/* log the error */
+ return NIL; /* failed */
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ /* write all requested messages to mailbox */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size);
+ if (write (fd,LOCAL->buf,elt->private.special.text.size) < 0) ret = NIL;
+ else { /* internal header succeeded */
+ s = unix_header (stream,i,&j,FT_INTERNAL);
+ /* header size, sans trailing newline */
+ if (j && (s[j - 2] == '\n')) j--;
+ if (write (fd,s,j) < 0) ret = NIL;
+ else { /* message header succeeded */
+ j = tstream ? /* write UIDPLUS data if have readwrite */
+ unix_xstatus (stream,LOCAL->buf,elt,++(tstream->uid_last),LONGT) :
+ unix_xstatus (stream,LOCAL->buf,elt,NIL,NIL);
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ else { /* message status succeeded */
+ s = unix_text_work (stream,elt,&j,FT_INTERNAL);
+ if ((write (fd,s,j) < 0) || (write (fd,"\n",1) < 0)) ret = NIL;
+ else if (cu) { /* need to pass back new UID? */
+ mail_append_set (source,mail_uid (stream,i));
+ mail_append_set (dest,tstream->uid_last);
+ }
+ }
+ }
+ }
+ }
+
+ if (!ret || fsync (fd)) { /* force out the update */
+ sprintf (LOCAL->buf,"Message copy failed: %s",strerror (errno));
+ ftruncate (fd,sbuf.st_size);
+ ret = NIL;
+ }
+ /* force UIDVALIDITY assignment now */
+ if (tstream && !tstream->uid_validity) tstream->uid_validity = time (0);
+ /* return sets if doing COPYUID */
+ if (cu && ret) (*cu) (stream,mailbox,tstream->uid_validity,source,dest);
+ else { /* flush any sets we may have built */
+ mail_free_searchset (&source);
+ mail_free_searchset (&dest);
+ }
+ tp[1] = time (0); /* set mtime to now */
+ if (ret) tp[0] = tp[1] - 1; /* set atime to now-1 if successful copy */
+ else tp[0] = /* else preserve \Marked status */
+ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ?
+ sbuf.st_atime : tp[1];
+ utime (file,tp); /* set the times */
+ unix_unlock (fd,NIL,&lock); /* unlock and close mailbox */
+ if (tstream) { /* update last UID if we can */
+ UNIXLOCAL *local = (UNIXLOCAL *) tstream->local;
+ local->dirty = T; /* do a rewrite */
+ local->appending = T; /* but not at the cost of marking as old */
+ tstream = mail_close (tstream);
+ }
+ /* log the error */
+ if (!ret) MM_LOG (LOCAL->buf,ERROR);
+ /* delete if requested message */
+ else if (options & CP_MOVE) for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence)
+ elt->deleted = elt->private.dirty = LOCAL->dirty = T;
+ MM_NOCRITICAL (stream); /* release critical */
+ return ret;
+}
+
+/* UNIX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+#define BUFLEN 8*MAILTMPLEN
+
+long unix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd;
+ unsigned long i;
+ char *flags,*date,buf[BUFLEN],tmp[MAILTMPLEN],file[MAILTMPLEN];
+ time_t tp[2];
+ FILE *sf,*df;
+ MESSAGECACHE elt;
+ DOTLOCK lock;
+ STRING *message;
+ unsigned long uidlocation = 0;
+ appenduid_t au = (appenduid_t)
+ (mail_parameters (NIL,GET_USERHASNOLIFE,NIL) ? NIL :
+ mail_parameters (NIL,GET_APPENDUID,NIL));
+ SEARCHSET *dst = au ? mail_newsearchset () : NIL;
+ long ret = LONGT;
+ MAILSTREAM *tstream = NIL;
+ if (!stream) { /* stream specified? */
+ stream = &unixproto; /* no, default stream to prototype */
+ for (i = 0; i < NUSERFLAGS && stream->user_flags[i]; ++i)
+ fs_give ((void **) &stream->user_flags[i]);
+ }
+ if (!unix_valid (mailbox)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (compare_cstring (mailbox,"INBOX")) {
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ unix_create (NIL,"INBOX"); /* create empty INBOX */
+ case 0: /* merely empty file? */
+ tstream = stream;
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid UNIX-format mailbox name: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a UNIX-format mailbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get sniffing stream for keywords */
+ else if (!(tstream = mail_open (NIL,mailbox,
+ OP_READONLY|OP_SILENT|OP_NOKOD|OP_SNIFF))) {
+ sprintf (tmp,"Unable to examine mailbox for APPEND: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+
+ /* get first message */
+ if (!MM_APPEND (af) (tstream,data,&flags,&date,&message)) return NIL;
+ if (!(sf = tmpfile ())) { /* must have scratch file */
+ sprintf (tmp,".%lx.%lx",(unsigned long) time (0),(unsigned long)getpid ());
+ if (!stat (tmp,&sbuf) || !(sf = fopen (tmp,"wb+"))) {
+ sprintf (tmp,"Unable to create scratch file: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ unlink (tmp);
+ }
+ do { /* parse date */
+ if (!date) rfc822_date (date = tmp);
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ MM_LOG (tmp,ERROR);
+ }
+ else { /* user wants to suppress time zones? */
+ if (mail_parameters (NIL,GET_NOTIMEZONES,NIL)) {
+ time_t when = mail_longdate (&elt);
+ date = ctime (&when); /* use traditional date */
+ }
+ /* use POSIX-style date */
+ else date = mail_cdate (tmp,&elt);
+ if (!SIZE (message)) MM_LOG ("Append of zero-length message",ERROR);
+ else if (!unix_collect_msg (tstream,sf,flags,date,message)) {
+ sprintf (tmp,"Error writing scratch file: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ }
+ /* get next message */
+ else if (MM_APPEND (af) (tstream,data,&flags,&date,&message)) continue;
+ }
+ fclose (sf); /* punt scratch file */
+ return NIL; /* give up */
+ } while (message); /* until no more messages */
+ if (fflush (sf)) {
+ sprintf (tmp,"Error finishing scratch file: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ fclose (sf); /* punt scratch file */
+ return NIL; /* give up */
+ }
+ i = ftell (sf); /* size of scratch file */
+ /* close sniffing stream */
+ if (tstream != stream) tstream = mail_close (tstream);
+
+ MM_CRITICAL (stream); /* go critical */
+ /* try to open readwrite for UIDPLUS */
+ if ((tstream = mail_open_work (&unixdriver,NIL,mailbox,
+ OP_SILENT|OP_NOKOD)) && tstream->rdonly)
+ tstream = mail_close (tstream);
+ if (au && !tstream) { /* wanted an APPENDUID? */
+ sprintf (tmp,"Unable to re-open mailbox for APPENDUID: %.80s",mailbox);
+ MM_LOG (tmp,WARN);
+ au = NIL;
+ }
+ if (((fd = unix_lock (dummy_file (file,mailbox),O_WRONLY|O_APPEND,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL),
+ &lock,LOCK_EX)) < 0) ||
+ !(df = fdopen (fd,"ab"))) {
+ MM_NOCRITICAL (stream); /* done with critical */
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ rewind (sf);
+ tp[1] = time (0); /* set mtime to now */
+ /* write all messages */
+ if (!unix_append_msgs (tstream,sf,df,au ? dst : NIL) ||
+ (fflush (df) == EOF) || fsync (fd)) {
+ sprintf (buf,"Message append failed: %s",strerror (errno));
+ MM_LOG (buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ tp[0] = /* preserve \Marked status */
+ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ?
+ sbuf.st_atime : tp[1];
+ ret = NIL; /* return error */
+ }
+ else tp[0] = tp[1] - 1; /* set atime to now-1 if successful copy */
+ utime (file,tp); /* set the times */
+ fclose (sf); /* done with scratch file */
+ /* force UIDVALIDITY assignment now */
+ if (tstream && !tstream->uid_validity) tstream->uid_validity = time (0);
+ /* return sets if doing APPENDUID */
+ if (au && ret) (*au) (mailbox,tstream->uid_validity,dst);
+ else mail_free_searchset (&dst);
+ unix_unlock (fd,NIL,&lock); /* unlock and close mailbox */
+ fclose (df); /* note that unix_unlock() released the fd */
+ if (tstream) { /* update last UID if we can */
+ UNIXLOCAL *local = (UNIXLOCAL *) tstream->local;
+ local->dirty = T; /* do a rewrite */
+ local->appending = T; /* but not at the cost of marking as old */
+ tstream = mail_close (tstream);
+ }
+ MM_NOCRITICAL (stream); /* release critical */
+ return ret;
+}
+
+/* Collect and write single message to append scratch file
+ * Accepts: MAIL stream
+ * scratch file
+ * flags
+ * date
+ * message stringstruct
+ * Returns: NIL if write error, else T
+ */
+
+int unix_collect_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
+ STRING *msg)
+{
+ unsigned char *s,*t;
+ unsigned long uf;
+ long f = mail_parse_flags (stream,flags,&uf);
+ /* write metadata, note date ends with NL */
+ if (fprintf (sf,"%ld %lu %s",f,SIZE (msg) + 1,date) < 0) return NIL;
+ while (uf) /* write user flags */
+ if ((s = stream->user_flags[find_rightmost_bit (&uf)]) &&
+ (fprintf (sf," %s",s) < 0)) return NIL;
+ if (putc ('\n',sf) == EOF) return NIL;
+ while (SIZE (msg)) { /* copy text to scratch file */
+ for (s = (unsigned char *) msg->curpos, t = s + msg->cursize; s < t; ++s)
+ if (!*s) *s = 0x80; /* disallow NUL */
+ /* write buffered text */
+ if (fwrite (msg->curpos,1,msg->cursize,sf) == msg->cursize)
+ SETPOS (msg,GETPOS (msg) + msg->cursize);
+ else return NIL; /* failed */
+ }
+ /* write trailing newline and return */
+ return (putc ('\n',sf) == EOF) ? NIL : T;
+}
+
+/* Append messages from scratch file to mailbox
+ * Accepts: MAIL stream
+ * source file
+ * destination file
+ * uidset to update if non-NIL
+ * Returns: T if success, NIL if failure
+ */
+
+int unix_append_msgs (MAILSTREAM *stream,FILE *sf,FILE *df,SEARCHSET *set)
+{
+ int ti,zn,c;
+ long f;
+ unsigned long i,j;
+ char *x,tmp[MAILTMPLEN];
+ int hdrp = T;
+ /* get message metadata line */
+ while (fgets (tmp,MAILTMPLEN,sf)) {
+ if (!(isdigit (tmp[0]) && strchr (tmp,'\n'))) return NIL;
+ f = strtol (tmp,&x,10); /* get flags */
+ if (!((*x++ == ' ') && isdigit (*x))) return NIL;
+ i = strtoul (x,&x,10); /* get message size */
+ if ((*x++ != ' ') || /* build initial header */
+ (fprintf (df,"From %s@%s %sStatus: ",myusername(),mylocalhost(),x)<0)||
+ (f&fSEEN && (putc ('R',df) == EOF)) ||
+ (fputs ("\nX-Status: ",df) == EOF) ||
+ (f&fDELETED && (putc ('D',df) == EOF)) ||
+ (f&fFLAGGED && (putc ('F',df) == EOF)) ||
+ (f&fANSWERED && (putc ('A',df) == EOF)) ||
+ (f&fDRAFT && (putc ('T',df) == EOF)) ||
+ (fputs ("\nX-Keywords:",df) == EOF)) return NIL;
+ /* copy keywords */
+ while ((c = getc (sf)) != '\n') switch (c) {
+ case EOF:
+ return NIL;
+ default:
+ if (putc (c,df) == EOF) return NIL;
+ }
+ if ((putc ('\n',df) == EOF) ||
+ (set && (fprintf (df,"X-UID: %lu\n",++(stream->uid_last)) < 0)))
+ return NIL;
+
+ for (c = '\n'; i && fgets (tmp,MAILTMPLEN,sf); c = tmp[j-1]) {
+ /* get read line length */
+ if (i < (j = strlen (tmp))) fatal ("unix_append_msgs overrun");
+ i -= j; /* number of bytes left */
+ /* squish out CRs (note also copies NUL) */
+ for (x = tmp; x = strchr (x,'\r'); --j) memmove (x,x+1,j-(x-tmp));
+ if (!j) continue; /* do nothing if line emptied */
+ /* start of line? */
+ if ((c == '\n')) switch (tmp[0]) {
+ case 'F': /* possible "From " (case counts here) */
+ if ((j > 4) && (tmp[0] == 'F') && (tmp[1] == 'r') && (tmp[2] == 'o') &&
+ (tmp[3] == 'm') && (tmp[4] == ' ')) {
+ if (!unix_fromwidget) {
+ VALID (tmp,x,ti,zn);/* conditional, only write widget if */
+ if (!ti) break; /* it looks like a valid header */
+ } /* write the widget */
+ if (putc ('>',df) == EOF) return NIL;
+ }
+ break;
+ case 'S': case 's': /* possible "Status:" */
+ if (hdrp && (j > 6) && ((tmp[1] == 't') || (tmp[1] == 'T')) &&
+ ((tmp[2] == 'a') || (tmp[2] == 'A')) &&
+ ((tmp[3] == 't') || (tmp[3] == 'T')) &&
+ ((tmp[4] == 'u') || (tmp[4] == 'U')) &&
+ ((tmp[5] == 's') || (tmp[5] == 'S')) && (tmp[6] == ':') &&
+ (fputs ("X-Original-",df) == EOF)) return NIL;
+ break;
+ case 'X': case 'x': /* possible X-??? header */
+ if (hdrp && (tmp[1] == '-') &&
+ /* possible X-UID: */
+ (((j > 5) && ((tmp[2] == 'U') || (tmp[2] == 'u')) &&
+ ((tmp[3] == 'I') || (tmp[3] == 'i')) &&
+ ((tmp[4] == 'D') || (tmp[4] == 'd')) && (tmp[5] == ':')) ||
+ /* possible X-IMAP: */
+ ((j > 6) && ((tmp[2] == 'I') || (tmp[2] == 'i')) &&
+ ((tmp[3] == 'M') || (tmp[3] == 'm')) &&
+ ((tmp[4] == 'A') || (tmp[4] == 'a')) &&
+ ((tmp[5] == 'P') || (tmp[5] == 'p')) &&
+ ((tmp[6] == ':') ||
+ /* or X-IMAPbase: */
+ ((j > 10) && ((tmp[6] == 'b') || (tmp[6] == 'B')) &&
+ ((tmp[7] == 'a') || (tmp[7] == 'A')) &&
+ ((tmp[8] == 's') || (tmp[8] == 'S')) &&
+ ((tmp[9] == 'e') || (tmp[9] == 'E')) && (tmp[10] == ':')))) ||
+ /* possible X-Status: */
+ ((j > 8) && ((tmp[2] == 'S') || (tmp[2] == 's')) &&
+ ((tmp[3] == 't') || (tmp[3] == 'T')) &&
+ ((tmp[4] == 'a') || (tmp[4] == 'A')) &&
+ ((tmp[5] == 't') || (tmp[5] == 'T')) &&
+ ((tmp[6] == 'u') || (tmp[6] == 'U')) &&
+ ((tmp[7] == 's') || (tmp[7] == 'S')) && (tmp[8] == ':')) ||
+ /* possible X-Keywords: */
+ ((j > 10) && ((tmp[2] == 'K') || (tmp[2] == 'k')) &&
+ ((tmp[3] == 'e') || (tmp[3] == 'E')) &&
+ ((tmp[4] == 'y') || (tmp[4] == 'Y')) &&
+ ((tmp[5] == 'w') || (tmp[5] == 'W')) &&
+ ((tmp[6] == 'o') || (tmp[6] == 'O')) &&
+ ((tmp[7] == 'r') || (tmp[7] == 'R')) &&
+ ((tmp[8] == 'd') || (tmp[8] == 'D')) &&
+ ((tmp[9] == 's') || (tmp[9] == 'S')) && (tmp[10] == ':'))) &&
+ (fputs ("X-Original-",df) == EOF)) return NIL;
+ case '\n': /* blank line */
+ hdrp = NIL;
+ break;
+ default: /* nothing to do */
+ break;
+ }
+ /* just write the line */
+ if (fwrite (tmp,1,j,df) != j) return NIL;
+ }
+ if (i) return NIL; /* didn't read entire message */
+ /* update set */
+ if (stream) mail_append_set (set,stream->uid_last);
+ }
+ return T;
+}
+
+/* Internal routines */
+
+
+/* UNIX mail abort stream
+ * Accepts: MAIL stream
+ */
+
+void unix_abort (MAILSTREAM *stream)
+{
+ if (LOCAL) { /* only if a file is open */
+ if (LOCAL->fd >= 0) close (LOCAL->fd);
+ if (LOCAL->ld >= 0) { /* have a mailbox lock? */
+ flock (LOCAL->ld,LOCK_UN);/* yes, release the lock */
+ close (LOCAL->ld); /* close the lock file */
+ unlink (LOCAL->lname); /* and delete it */
+ }
+ if (LOCAL->lname) fs_give ((void **) &LOCAL->lname);
+ /* free local text buffers */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data);
+ if (LOCAL->linebuf) fs_give ((void **) &LOCAL->linebuf);
+ if (LOCAL->line) fs_give ((void **) &LOCAL->line);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* UNIX open and lock mailbox
+ * Accepts: file name to open/lock
+ * file open mode
+ * destination buffer for lock file name
+ * type of locking operation (LOCK_SH or LOCK_EX)
+ */
+
+int unix_lock (char *file,int flags,int mode,DOTLOCK *lock,int op)
+{
+ int fd;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ (*bn) (BLOCK_FILELOCK,NIL);
+ /* try locking the easy way */
+ if (dotlock_lock (file,lock,-1)) {
+ /* got dotlock file, easy open */
+ if ((fd = open (file,flags,mode)) >= 0) flock (fd,op);
+ else dotlock_unlock (lock); /* open failed, free the dotlock */
+ }
+ /* no dot lock file, open file now */
+ else if ((fd = open (file,flags,mode)) >= 0) {
+ /* try paranoid way to make a dot lock file */
+ if (dotlock_lock (file,lock,fd)) {
+ close (fd); /* get fresh fd in case of timing race */
+ if ((fd = open (file,flags,mode)) >= 0) flock (fd,op);
+ /* open failed, free the dotlock */
+ else dotlock_unlock (lock);
+ }
+ else flock (fd,op); /* paranoid way failed, just flock() it */
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return fd;
+}
+
+/* UNIX unlock and close mailbox
+ * Accepts: file descriptor
+ * (optional) mailbox stream to check atime/mtime
+ * (optional) lock file name
+ */
+
+void unix_unlock (int fd,MAILSTREAM *stream,DOTLOCK *lock)
+{
+ if (stream) { /* need to muck with times? */
+ struct stat sbuf;
+ time_t tp[2];
+ time_t now = time (0);
+ fstat (fd,&sbuf); /* get file times */
+ if (LOCAL->ld >= 0) { /* yes, readwrite session? */
+ tp[0] = now; /* set atime to now */
+ /* set mtime to (now - 1) if necessary */
+ tp[1] = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1;
+ }
+ else if (stream->recent) { /* readonly with recent messages */
+ if ((sbuf.st_atime >= sbuf.st_mtime) ||
+ (sbuf.st_atime >= sbuf.st_ctime))
+ /* keep past mtime, whack back atime */
+ tp[0] = (tp[1] = (sbuf.st_mtime < now) ? sbuf.st_mtime : now) - 1;
+ else now = 0; /* no time change needed */
+ }
+ /* readonly with no recent messages */
+ else if ((sbuf.st_atime < sbuf.st_mtime) ||
+ (sbuf.st_atime < sbuf.st_ctime)) {
+ tp[0] = now; /* set atime to now */
+ /* set mtime to (now - 1) if necessary */
+ tp[1] = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1;
+ }
+ else now = 0; /* no time change needed */
+ /* set the times, note change */
+ if (now && !utime (stream->mailbox,tp)) LOCAL->filetime = tp[1];
+ }
+ flock (fd,LOCK_UN); /* release flock'ers */
+ if (!stream) close (fd); /* close the file if no stream */
+ dotlock_unlock (lock); /* flush the lock file if any */
+}
+
+/* UNIX mail parse and lock mailbox
+ * Accepts: MAIL stream
+ * space to write lock file name
+ * type of locking operation
+ * Returns: T if parse OK, critical & mailbox is locked shared; NIL if failure
+ */
+
+int unix_parse (MAILSTREAM *stream,DOTLOCK *lock,int op)
+{
+ int zn;
+ unsigned long i,j,k,m;
+ unsigned char c,*s,*t,*u,tmp[MAILTMPLEN],date[30];
+ int ti = 0,retain = T;
+ unsigned long nmsgs = stream->nmsgs;
+ unsigned long prevuid = nmsgs ? mail_elt (stream,nmsgs)->private.uid : 0;
+ unsigned long recent = stream->recent;
+ unsigned long oldnmsgs = stream->nmsgs;
+ short silent = stream->silent;
+ short pseudoseen = NIL;
+ struct stat sbuf;
+ STRING bs;
+ FDDATA d;
+ MESSAGECACHE *elt;
+ mail_lock (stream); /* guard against recursion or pingers */
+ /* toss out previous descriptor */
+ if (LOCAL->fd >= 0) close (LOCAL->fd);
+ MM_CRITICAL (stream); /* open and lock mailbox (shared OK) */
+ if ((LOCAL->fd = unix_lock (stream->mailbox,(LOCAL->ld >= 0) ?
+ O_RDWR : O_RDONLY,
+ (long)mail_parameters(NIL,GET_MBXPROTECTION,NIL),
+ lock,op)) < 0) {
+ sprintf (tmp,"Mailbox open failed, aborted: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ unix_abort (stream);
+ mail_unlock (stream);
+ MM_NOCRITICAL (stream); /* done with critical */
+ return NIL;
+ }
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ /* validate change in size */
+ if (sbuf.st_size < LOCAL->filesize) {
+ sprintf (tmp,"Mailbox shrank from %lu to %lu bytes, aborted",
+ (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size);
+ MM_LOG (tmp,ERROR); /* this is pretty bad */
+ unix_unlock (LOCAL->fd,stream,lock);
+ unix_abort (stream);
+ mail_unlock (stream);
+ MM_NOCRITICAL (stream); /* done with critical */
+ return NIL;
+ }
+
+ /* new data? */
+ else if (i = sbuf.st_size - LOCAL->filesize) {
+ d.fd = LOCAL->fd; /* yes, set up file descriptor */
+ d.pos = LOCAL->filesize; /* get to that position in the file */
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE; /* file chunk size */
+ INIT (&bs,fd_string,&d,i); /* initialize stringstruct */
+ /* skip leading whitespace for broken MTAs */
+ while (((c = CHR (&bs)) == '\n') || (c == '\r') ||
+ (c == ' ') || (c == '\t')) SNX (&bs);
+ if (SIZE (&bs)) { /* read new data */
+ /* remember internal header position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ s = unix_mbxline (stream,&bs,&i);
+ t = NIL,zn = 0;
+ if (i) VALID (s,t,ti,zn); /* see if valid From line */
+ if (!ti) { /* someone pulled the rug from under us */
+ sprintf (tmp,"Unexpected changes to mailbox (try restarting): %.20s",
+ (char *) s);
+ MM_LOG (tmp,ERROR);
+ unix_unlock (LOCAL->fd,stream,lock);
+ unix_abort (stream);
+ mail_unlock (stream);
+ /* done with critical */
+ MM_NOCRITICAL (stream);
+ return NIL;
+ }
+ stream->silent = T; /* quell main program new message events */
+ do { /* found a message */
+ /* instantiate first new message */
+ mail_exists (stream,++nmsgs);
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ recent++; /* assume recent by default */
+ elt->recent = T;
+ /* note position/size of internal header */
+ elt->private.special.offset = j;
+ elt->private.msg.header.offset = elt->private.special.text.size = i;
+
+ /* generate plausible IMAPish date string */
+ date[2] = date[6] = date[20] = '-'; date[11] = ' ';
+ date[14] = date[17] = ':';
+ /* dd */
+ date[0] = t[ti - 2]; date[1] = t[ti - 1];
+ /* mmm */
+ date[3] = t[ti - 6]; date[4] = t[ti - 5]; date[5] = t[ti - 4];
+ /* hh */
+ date[12] = t[ti + 1]; date[13] = t[ti + 2];
+ /* mm */
+ date[15] = t[ti + 4]; date[16] = t[ti + 5];
+ if (t[ti += 6] == ':') {/* ss */
+ date[18] = t[++ti]; date[19] = t[++ti];
+ ti++; /* move to space */
+ }
+ else date[18] = date[19] = '0';
+ /* yy -- advance over timezone if necessary */
+ if (zn == ti) ti += (((t[zn+1] == '+') || (t[zn+1] == '-')) ? 6 : 4);
+ date[7] = t[ti + 1]; date[8] = t[ti + 2];
+ date[9] = t[ti + 3]; date[10] = t[ti + 4];
+ /* zzz */
+ t = zn ? (t + zn + 1) : (unsigned char *) "LCL";
+ date[21] = *t++; date[22] = *t++; date[23] = *t++;
+ if ((date[21] != '+') && (date[21] != '-')) date[24] = '\0';
+ else { /* numeric time zone */
+ date[24] = *t++; date[25] = *t++;
+ date[26] = '\0'; date[20] = ' ';
+ }
+ /* set internal date */
+ if (!mail_parse_date (elt,date)) {
+ sprintf (tmp,"Unable to parse internal date: %s",(char *) date);
+ MM_LOG (tmp,WARN);
+ }
+
+ do { /* look for message body */
+ s = t = unix_mbxline (stream,&bs,&i);
+ if (i) switch (*s) { /* check header lines */
+ case 'X': /* possible X-???: line */
+ if (s[1] == '-') { /* must be immediately followed by hyphen */
+ /* X-Status: becomes Status: in S case */
+ if (s[2] == 'S' && s[3] == 't' && s[4] == 'a' && s[5] == 't' &&
+ s[6] == 'u' && s[7] == 's' && s[8] == ':') s += 2;
+ /* possible X-Keywords */
+ else if (s[2] == 'K' && s[3] == 'e' && s[4] == 'y' &&
+ s[5] == 'w' && s[6] == 'o' && s[7] == 'r' &&
+ s[8] == 'd' && s[9] == 's' && s[10] == ':') {
+ SIZEDTEXT uf;
+ retain = NIL; /* don't retain continuation */
+ s += 11; /* flush leading whitespace */
+ while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n'))){
+ while (*s == ' ') s++;
+ /* find end of keyword */
+ if (!(u = strpbrk (s," \n\r"))) u = s + strlen (s);
+ /* got a keyword? */
+ if ((k = (u - s)) && (k <= MAXUSERFLAG)) {
+ uf.data = (unsigned char *) s;
+ uf.size = k;
+ for (j = 0; (j < NUSERFLAGS) && stream->user_flags[j]; ++j)
+ if (!compare_csizedtext (stream->user_flags[j],&uf)) {
+ elt->user_flags |= ((long) 1) << j;
+ break;
+ }
+ }
+ s = u; /* advance to next keyword */
+ }
+ break;
+ }
+
+ /* possible X-IMAP */
+ else if ((s[2] == 'I') && (s[3] == 'M') && (s[4] == 'A') &&
+ (s[5] == 'P') && ((m = (s[6] == ':')) ||
+ ((s[6] == 'b') && (s[7] == 'a') &&
+ (s[8] == 's') && (s[9] == 'e') &&
+ (s[10] == ':')))) {
+ retain = NIL; /* don't retain continuation */
+ if ((nmsgs == 1) && !stream->uid_validity) {
+ /* advance to data */
+ s += m ? 7 : 11;
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ j = 0; /* slurp UID validity */
+ /* found a digit? */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ /* must have valid UID validity and UID last */
+ if (j && isdigit (*s)) {
+ /* pseudo-header seen if X-IMAP */
+ if (m) pseudoseen = LOCAL->pseudo = T;
+ /* save UID validity */
+ stream->uid_validity = j;
+ j = 0; /* slurp UID last */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* save UID last */
+ stream->uid_last = j;
+ /* process keywords */
+ for (j = 0; (*s != '\n') && ((*s != '\r')||(s[1] != '\n'));
+ s = u,j++) {
+ /* flush leading whitespace */
+ while (*s == ' ') s++;
+ u = strpbrk (s," \n\r");
+ /* got a keyword? */
+ if ((j < NUSERFLAGS) && (k = (u - s)) &&
+ (k <= MAXUSERFLAG)) {
+ if (stream->user_flags[j])
+ fs_give ((void **) &stream->user_flags[j]);
+ stream->user_flags[j] = (char *) fs_get (k + 1);
+ strncpy (stream->user_flags[j],s,k);
+ stream->user_flags[j][k] = '\0';
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ /* possible X-UID */
+ else if (s[2] == 'U' && s[3] == 'I' && s[4] == 'D' &&
+ s[5] == ':') {
+ retain = NIL; /* don't retain continuation */
+ /* only believe if have a UID validity */
+ if (stream->uid_validity && ((nmsgs > 1) || !pseudoseen)) {
+ s += 6; /* advance to UID value */
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ j = 0;
+ /* found a digit? */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* flush remainder of line */
+ while (*s != '\n') s++;
+ /* make sure not duplicated */
+ if (elt->private.uid)
+ sprintf (tmp,"Message %lu UID %lu already has UID %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,elt->private.uid);
+ /* make sure UID doesn't go backwards */
+ else if (j <= prevuid)
+ sprintf (tmp,"Message %lu UID %lu less than %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,prevuid + 1);
+#if 0 /* this is currently broken by UIDPLUS */
+ /* or skip by mailbox's recorded last */
+ else if (j > stream->uid_last)
+ sprintf (tmp,"Message %lu UID %lu greater than last %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,stream->uid_last);
+#endif
+ else { /* normal UID case */
+ prevuid = elt->private.uid = j;
+#if 1 /* temporary kludge for UIDPLUS */
+ if (prevuid > stream->uid_last) {
+ stream->uid_last = prevuid;
+ LOCAL->ddirty = LOCAL->dirty = T;
+ }
+#endif
+ break; /* exit this cruft */
+ }
+ MM_LOG (tmp,WARN);
+ /* invalidate UID validity */
+ stream->uid_validity = 0;
+ elt->private.uid = 0;
+ }
+ break;
+ }
+ }
+ /* otherwise fall into S case */
+
+ case 'S': /* possible Status: line */
+ if (s[0] == 'S' && s[1] == 't' && s[2] == 'a' && s[3] == 't' &&
+ s[4] == 'u' && s[5] == 's' && s[6] == ':') {
+ retain = NIL; /* don't retain continuation */
+ s += 6; /* advance to status flags */
+ do switch (*s++) {/* parse flags */
+ case 'R': /* message read */
+ elt->seen = T;
+ break;
+ case 'O': /* message old */
+ if (elt->recent) {
+ elt->recent = NIL;
+ recent--; /* it really wasn't recent */
+ }
+ break;
+ case 'D': /* message deleted */
+ elt->deleted = T;
+ break;
+ case 'F': /* message flagged */
+ elt->flagged = T;
+ break;
+ case 'A': /* message answered */
+ elt->answered = T;
+ break;
+ case 'T': /* message is a draft */
+ elt->draft = T;
+ break;
+ default: /* some other crap */
+ break;
+ } while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n')));
+ break; /* all done */
+ }
+ /* otherwise fall into default case */
+
+ default: /* ordinary header line */
+ if ((*s == 'S') || (*s == 's') ||
+ (((*s == 'X') || (*s == 'x')) && (s[1] == '-'))) {
+ unsigned char *e,*v;
+ /* must match what mail_filter() does */
+ for (u = s,v = tmp,e = u + min (i,MAILTMPLEN - 1);
+ (u < e) && ((c = (*u ? *u : (*u = ' '))) != ':') &&
+ ((c > ' ') || ((c != ' ') && (c != '\t') &&
+ (c != '\r') && (c != '\n')));
+ *v++ = *u++);
+ *v = '\0'; /* tie off */
+ /* matches internal header? */
+ if (!compare_cstring (tmp,"STATUS") ||
+ !compare_cstring (tmp,"X-STATUS") ||
+ !compare_cstring (tmp,"X-KEYWORDS") ||
+ !compare_cstring (tmp,"X-UID") ||
+ !compare_cstring (tmp,"X-IMAP") ||
+ !compare_cstring (tmp,"X-IMAPBASE")) {
+ char err[MAILTMPLEN];
+ sprintf (err,"Discarding bogus %s header in message %lu",
+ (char *) tmp,elt->msgno);
+ MM_LOG (err,WARN);
+ retain = NIL; /* don't retain continuation */
+ break; /* different case or something */
+ }
+ }
+ /* retain or non-continuation? */
+ if (retain || ((*s != ' ') && (*s != '\t'))) {
+ retain = T; /* retaining continuation now */
+ /* line length in LF format newline */
+ for (j = k = 0; j < i; ++j) if (s[j] != '\r') ++k;
+ /* "internal" header size */
+ elt->private.spare.data += k;
+ /* message size */
+ elt->rfc822_size += k + 1;
+ }
+ else {
+ char err[MAILTMPLEN];
+ sprintf (err,"Discarding bogus continuation in msg %lu: %.80s",
+ elt->msgno,(char *) s);
+ if (u = strpbrk (err,"\r\n")) *u = '\0';
+ MM_LOG (err,WARN);
+ break; /* different case or something */
+ }
+ break;
+ }
+ } while (i && (*t != '\n') && ((*t != '\r') || (t[1] != '\n')));
+ /* "internal" header sans trailing newline */
+ if (i) elt->private.spare.data--;
+ /* assign a UID if none found */
+ if (((nmsgs > 1) || !pseudoseen) && !elt->private.uid) {
+ prevuid = elt->private.uid = ++stream->uid_last;
+ elt->private.dirty = T;
+ LOCAL->ddirty = T; /* force update */
+ }
+ else elt->private.dirty = elt->recent;
+
+ /* note size of header, location of text */
+ elt->private.msg.header.text.size =
+ (elt->private.msg.text.offset =
+ (LOCAL->filesize + GETPOS (&bs)) - elt->private.special.offset) -
+ elt->private.special.text.size;
+ k = m = 0; /* no previous line size yet */
+ /* note current position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ if (i) do { /* look for next message */
+ s = unix_mbxline (stream,&bs,&i);
+ if (i) { /* got new data? */
+ VALID (s,t,ti,zn); /* yes, parse line */
+ if (!ti) { /* not a header line, add it to message */
+ elt->rfc822_size += i;
+ for (j = 0; j < i; ++j) switch (s[j]) {
+ case '\r': /* squeeze out CRs */
+ elt->rfc822_size -= 1;
+ break;
+ case '\n': /* LF becomes CRLF */
+ elt->rfc822_size += 1;
+ break;
+ default:
+ break;
+ }
+ if ((i == 1) && (*s == '\n')) {
+ k = 2;
+ m = 1;
+ }
+ else if ((i == 2) && (*s == '\r') && (s[1] == '\n'))
+ k = m = 2;
+ else k = m = 0; /* file does not end with newline! */
+ /* update current position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ }
+ }
+ } while (i && !ti); /* until found a header */
+ elt->private.msg.text.text.size = j -
+ (elt->private.special.offset + elt->private.msg.text.offset);
+ /* flush ending blank line */
+ elt->private.msg.text.text.size -= m;
+ elt->rfc822_size -= k;
+ /* until end of buffer */
+ } while (!stream->sniff && i);
+ if (pseudoseen) { /* flush pseudo-message if present */
+ /* decrement recent count */
+ if (mail_elt (stream,1)->recent) recent--;
+ /* and the exists count */
+ mail_exists (stream,nmsgs--);
+ mail_expunged(stream,1);/* fake an expunge of that message */
+ }
+ /* need to start a new UID validity? */
+ if (!stream->uid_validity) {
+ stream->uid_validity = time (0);
+ /* in case a whiner with no life */
+ if (mail_parameters (NIL,GET_USERHASNOLIFE,NIL))
+ stream->uid_nosticky = T;
+ else if (nmsgs) { /* don't bother if empty file */
+ /* make dirty to restart UID epoch */
+ LOCAL->ddirty = LOCAL->dirty = T;
+ /* need to rewrite msg 1 if not pseudo */
+ if (!LOCAL->pseudo) mail_elt (stream,1)->private.dirty = T;
+ MM_LOG ("Assigning new unique identifiers to all messages",NIL);
+ }
+ }
+ stream->nmsgs = oldnmsgs; /* whack it back down */
+ stream->silent = silent; /* restore old silent setting */
+ /* notify upper level of new mailbox sizes */
+ mail_exists (stream,nmsgs);
+ mail_recent (stream,recent);
+ /* mark dirty so O flags are set */
+ if (recent) LOCAL->dirty = T;
+ }
+ }
+ /* no change, don't babble if never got time */
+ else if (LOCAL->filetime && LOCAL->filetime != sbuf.st_mtime)
+ MM_LOG ("New mailbox modification time but apparently no changes",WARN);
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ LOCAL->filetime = sbuf.st_mtime;
+ return T; /* return the winnage */
+}
+
+/* UNIX read line from mailbox
+ * Accepts: mail stream
+ * stringstruct
+ * pointer to line size
+ * Returns: pointer to input line
+ */
+
+char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size)
+{
+ unsigned long i,j,k,m;
+ char *s,*t,*te;
+ char *ret = "";
+ /* flush old buffer */
+ if (LOCAL->line) fs_give ((void **) &LOCAL->line);
+ /* if buffer needs refreshing */
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ if (SIZE (bs)) { /* find newline */
+ /* end of fast scan */
+ te = (t = (s = bs->curpos) + bs->cursize) - 12;
+ while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) {
+ --s; /* back up */
+ break; /* exit loop */
+ }
+ /* final character-at-a-time scan */
+ while ((s < t) && (*s != '\n')) ++s;
+ /* difficult case if line spans buffer */
+ if ((i = s - bs->curpos) == bs->cursize) {
+ /* have space in line buffer? */
+ if (i > LOCAL->linebuflen) {
+ fs_give ((void **) &LOCAL->linebuf);
+ LOCAL->linebuf = (char *) fs_get (LOCAL->linebuflen = i);
+ }
+ /* remember what we have so far */
+ memcpy (LOCAL->linebuf,bs->curpos,i);
+ /* load next buffer */
+ SETPOS (bs,k = GETPOS (bs) + i);
+ /* end of fast scan */
+ te = (t = (s = bs->curpos) + bs->cursize) - 12;
+ /* fast scan in overlap buffer */
+ while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) {
+ --s; /* back up */
+ break; /* exit loop */
+ }
+
+ /* final character-at-a-time scan */
+ while ((s < t) && (*s != '\n')) ++s;
+ /* huge line? */
+ if ((j = s - bs->curpos) == bs->cursize) {
+ SETPOS (bs,GETPOS (bs) + j);
+ /* look for end of line (s-l-o-w!!) */
+ for (m = SIZE (bs); m && (SNX (bs) != '\n'); --m,++j);
+ SETPOS (bs,k); /* go back to where it started */
+ }
+ /* got size of data, make buffer for return */
+ ret = LOCAL->line = (char *) fs_get (i + j + 2);
+ /* copy first chunk */
+ memcpy (ret,LOCAL->linebuf,i);
+ while (j) { /* copy remainder */
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ memcpy (ret + i,bs->curpos,k = min (j,bs->cursize));
+ i += k; /* account for this much read in */
+ j -= k;
+ bs->curpos += k; /* increment new position */
+ bs->cursize -= k; /* eat that many bytes */
+ }
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ /* read newline at end */
+ if (SIZE (bs)) ret[i++] = SNX (bs);
+ ret[i] = '\0'; /* makes debugging easier */
+ }
+ else { /* this is easy */
+ ret = bs->curpos; /* string it at this position */
+ bs->curpos += ++i; /* increment new position */
+ bs->cursize -= i; /* eat that many bytes */
+ }
+ *size = i; /* return that to user */
+ }
+ else *size = 0; /* end of data, return empty */
+ return ret;
+}
+
+/* UNIX make pseudo-header
+ * Accepts: MAIL stream
+ * buffer to write pseudo-header
+ * Returns: length of pseudo-header
+ */
+
+unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr)
+{
+ int i;
+ char *s,tmp[MAILTMPLEN];
+ time_t now = time (0);
+ rfc822_fixed_date (tmp);
+ sprintf (hdr,"From %s %.24s\nDate: %s\nFrom: %s <%s@%.80s>\nSubject: %s\nMessage-ID: <%lu@%.80s>\nX-IMAP: %010lu %010lu",
+ pseudo_from,ctime (&now),
+ tmp,pseudo_name,pseudo_from,mylocalhost (),pseudo_subject,
+ (unsigned long) now,mylocalhost (),stream->uid_validity,
+ stream->uid_last);
+ for (s = hdr + strlen (hdr),i = 0; i < NUSERFLAGS; ++i)
+ if (stream->user_flags[i])
+ sprintf (s += strlen (s)," %s",stream->user_flags[i]);
+ sprintf (s += strlen (s),"\nStatus: RO\n\n%s\n\n",pseudo_msg);
+ return strlen (hdr); /* return header length */
+}
+
+/* UNIX make status string
+ * Accepts: MAIL stream
+ * destination string to write
+ * message cache entry
+ * UID to write if non-zero (else use elt->private.uid)
+ * non-zero flag to write UID (.LT. 0 to write UID base info too)
+ * Returns: length of string
+ */
+
+unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt,
+ unsigned long uid,long flag)
+{
+ char *t,stack[64];
+ char *s = status;
+ unsigned long n;
+ int pad = 50;
+ int sticky = uid ? T : !stream->uid_nosticky;
+ /* This used to use sprintf(), but thanks to certain cretinous C libraries
+ with horribly slow implementations of sprintf() I had to change it to this
+ mess. At least it should be fast. */
+ if ((flag < 0) && sticky) { /* need to write X-IMAPbase: header? */
+ *s++ = 'X'; *s++ = '-'; *s++ = 'I'; *s++ = 'M'; *s++ = 'A'; *s++ = 'P';
+ *s++ = 'b'; *s++ = 'a'; *s++ = 's'; *s++ = 'e'; *s++ = ':'; *s++ = ' ';
+ t = stack;
+ n = stream->uid_validity; /* push UID validity digits on the stack */
+ do *t++ = (char) (n % 10) + '0';
+ while (n /= 10);
+ /* pop UID validity digits from stack */
+ while (t > stack) *s++ = *--t;
+ *s++ = ' ';
+ n = stream->uid_last; /* push UID last digits on the stack */
+ do *t++ = (char) (n % 10) + '0';
+ while (n /= 10);
+ /* pop UID last digits from stack */
+ while (t > stack) *s++ = *--t;
+ for (n = 0; n < NUSERFLAGS; ++n) if (t = stream->user_flags[n])
+ for (*s++ = ' '; *t; *s++ = *t++);
+ *s++ = '\n';
+ pad += 30; /* increased padding if have IMAPbase */
+ }
+ *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't'; *s++ = 'u'; *s++ = 's';
+ *s++ = ':'; *s++ = ' ';
+ if (elt->seen) *s++ = 'R';
+ /* only write O if have a UID */
+ if (flag && (!elt->recent || !LOCAL->appending)) *s++ = 'O';
+ *s++ = '\n';
+ *s++ = 'X'; *s++ = '-'; *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't';
+ *s++ = 'u'; *s++ = 's'; *s++ = ':'; *s++ = ' ';
+ if (elt->deleted) *s++ = 'D';
+ if (elt->flagged) *s++ = 'F';
+ if (elt->answered) *s++ = 'A';
+ if (elt->draft) *s++ = 'T';
+ *s++ = '\n';
+
+ if (sticky) { /* only do this if UIDs sticky */
+ *s++ = 'X'; *s++ = '-'; *s++ = 'K'; *s++ = 'e'; *s++ = 'y'; *s++ = 'w';
+ *s++ = 'o'; *s++ = 'r'; *s++ = 'd'; *s++ = 's'; *s++ = ':';
+ if (n = elt->user_flags) do {
+ *s++ = ' ';
+ for (t = stream->user_flags[find_rightmost_bit (&n)]; *t; *s++ = *t++);
+ } while (n);
+ n = s - status; /* get size of stuff so far */
+ /* pad X-Keywords to make size constant */
+ if (n < pad) for (n = pad - n; n > 0; --n) *s++ = ' ';
+ *s++ = '\n';
+ if (flag) { /* want to include UID? */
+ t = stack;
+ /* push UID digits on the stack */
+ n = uid ? uid : elt->private.uid;
+ do *t++ = (char) (n % 10) + '0';
+ while (n /= 10);
+ *s++ = 'X'; *s++ = '-'; *s++ = 'U'; *s++ = 'I'; *s++ = 'D'; *s++ = ':';
+ *s++ = ' ';
+ /* pop UID from stack */
+ while (t > stack) *s++ = *--t;
+ *s++ = '\n';
+ }
+ }
+ *s++ = '\n'; *s = '\0'; /* end of extended message status */
+ return s - status; /* return size of resulting string */
+}
+
+/* Rewrite mailbox file
+ * Accepts: MAIL stream, must be critical and locked
+ * return pointer to number of expunged messages if want expunge
+ * lock file name
+ * expunge sequence, not deleted flag
+ * Returns: T if success and mailbox unlocked, NIL if failure
+ */
+
+#define OVERFLOWBUFLEN 8192 /* initial overflow buffer length */
+
+long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,DOTLOCK *lock,
+ long flags)
+{
+ MESSAGECACHE *elt;
+ UNIXFILE f;
+ char *s;
+ time_t tp[2];
+ long ret,flag;
+ unsigned long i,j;
+ unsigned long recent = stream->recent;
+ unsigned long size = LOCAL->pseudo ? unix_pseudo (stream,LOCAL->buf) : 0;
+ if (nexp) *nexp = 0; /* initially nothing expunged */
+ /* calculate size of mailbox after rewrite */
+ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs; i++) {
+ elt = mail_elt (stream,i); /* get cache */
+ if (!(nexp && elt->deleted && (flags ? elt->sequence : T))) {
+ /* add RFC822 size of this message */
+ size += elt->private.special.text.size + elt->private.spare.data +
+ unix_xstatus (stream,LOCAL->buf,elt,NIL,flag) +
+ elt->private.msg.text.text.size + 1;
+ flag = 1; /* only count X-IMAPbase once */
+ }
+ }
+ /* no messages, has a life, and no pseudo */
+ if (!size && !mail_parameters (NIL,GET_USERHASNOLIFE,NIL)) {
+ LOCAL->pseudo = T; /* so make a pseudo-message now */
+ size = unix_pseudo (stream,LOCAL->buf);
+ }
+ /* extend the file as necessary */
+ if (ret = unix_extend (stream,size)) {
+ /* Set up buffered I/O file structure
+ * curpos current position being written through buffering
+ * filepos current position being written physically to the disk
+ * bufpos current position being written in the buffer
+ * protect current maximum position that can be written to the disk
+ * before buffering is forced
+ * The code tries to buffer so that that disk is written in multiples of
+ * OVERBLOWBUFLEN bytes.
+ */
+ f.stream = stream; /* note mail stream */
+ f.curpos = f.filepos = 0; /* start of file */
+ f.protect = stream->nmsgs ? /* initial protection pointer */
+ mail_elt (stream,1)->private.special.offset : 8192;
+ f.bufpos = f.buf = (char *) fs_get (f.buflen = OVERFLOWBUFLEN);
+
+ if (LOCAL->pseudo) /* update pseudo-header */
+ unix_write (&f,LOCAL->buf,unix_pseudo (stream,LOCAL->buf));
+ /* loop through all messages */
+ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs;) {
+ elt = mail_elt (stream,i);/* get cache */
+ /* expunge this message? */
+ if (nexp && elt->deleted && (flags ? elt->sequence : T)) {
+ /* one less recent message */
+ if (elt->recent) --recent;
+ mail_expunged(stream,i);/* notify upper levels */
+ ++*nexp; /* count up one more expunged message */
+ }
+ else { /* preserve this message */
+ i++; /* advance to next message */
+ if ((flag < 0) || /* need to rewrite message? */
+ elt->private.dirty || (f.curpos != elt->private.special.offset) ||
+ (elt->private.msg.header.text.size !=
+ (elt->private.spare.data +
+ unix_xstatus (stream,LOCAL->buf,elt,NIL,flag)))) {
+ unsigned long newoffset = f.curpos;
+ /* yes, seek to internal header */
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size);
+ /* see if need to squeeze out a CR */
+ if (LOCAL->buf[elt->private.special.text.size - 2] == '\r') {
+ LOCAL->buf[--elt->private.special.text.size - 1] = '\n';
+ --size; /* squeezed out a CR from PC */
+ }
+ /* protection pointer moves to RFC822 header */
+ f.protect = elt->private.special.offset +
+ elt->private.msg.header.offset;
+ /* write internal header */
+ unix_write (&f,LOCAL->buf,elt->private.special.text.size);
+ /* get RFC822 header */
+ s = unix_header (stream,elt->msgno,&j,FT_INTERNAL);
+ /* in case this got decremented */
+ elt->private.msg.header.offset = elt->private.special.text.size;
+ /* header size, sans trailing newline */
+ if ((j < 2) || (s[j - 2] == '\n')) j--;
+ /* this can happen if CRs were squeezed */
+ if (j < elt->private.spare.data) {
+ /* so fix up counts */
+ size -= elt->private.spare.data - j;
+ elt->private.spare.data = j;
+ }
+ else if (j != elt->private.spare.data)
+ fatal ("header size inconsistent");
+ /* protection pointer moves to RFC822 text */
+ f.protect = elt->private.special.offset +
+ elt->private.msg.text.offset;
+ unix_write (&f,s,j); /* write RFC822 header */
+ /* write status and UID */
+ unix_write (&f,LOCAL->buf,
+ j = unix_xstatus (stream,LOCAL->buf,elt,NIL,flag));
+ flag = 1; /* only write X-IMAPbase once */
+ /* new file header size */
+ elt->private.msg.header.text.size = elt->private.spare.data + j;
+
+ /* did text move? */
+ if (f.curpos != f.protect) {
+ /* get message text */
+ s = unix_text_work (stream,elt,&j,FT_INTERNAL);
+ /* this can happen if CRs were squeezed */
+ if (j < elt->private.msg.text.text.size) {
+ /* so fix up counts */
+ size -= elt->private.msg.text.text.size - j;
+ elt->private.msg.text.text.size = j;
+ }
+ /* can't happen it says here */
+ else if (j > elt->private.msg.text.text.size)
+ fatal ("text size inconsistent");
+ /* new text offset, status/UID may change it */
+ elt->private.msg.text.offset = f.curpos - newoffset;
+ /* protection pointer moves to next message */
+ f.protect = (i <= stream->nmsgs) ?
+ mail_elt (stream,i)->private.special.offset : (f.curpos + j + 1);
+ unix_write (&f,s,j);/* write text */
+ /* write trailing newline */
+ unix_write (&f,"\n",1);
+ }
+ else { /* tie off header and status */
+ unix_write (&f,NIL,NIL);
+ /* protection pointer moves to next message */
+ f.protect = (i <= stream->nmsgs) ?
+ mail_elt (stream,i)->private.special.offset : size;
+ /* locate end of message text */
+ j = f.filepos + elt->private.msg.text.text.size;
+ /* trailing newline already there? */
+ if (f.protect == (j + 1)) f.curpos = f.filepos = f.protect;
+ else { /* trailing newline missing, write it */
+ f.curpos = f.filepos = j;
+ unix_write (&f,"\n",1);
+ }
+ }
+ /* new internal header offset */
+ elt->private.special.offset = newoffset;
+ elt->private.dirty =NIL;/* message is now clean */
+ }
+ else { /* no need to rewrite this message */
+ /* tie off previous message if needed */
+ unix_write (&f,NIL,NIL);
+ /* protection pointer moves to next message */
+ f.protect = (i <= stream->nmsgs) ?
+ mail_elt (stream,i)->private.special.offset : size;
+ /* locate end of message text */
+ j = f.filepos + elt->private.special.text.size +
+ elt->private.msg.header.text.size +
+ elt->private.msg.text.text.size;
+ /* trailing newline already there? */
+ if (f.protect == (j + 1)) f.curpos = f.filepos = f.protect;
+ else { /* trailing newline missing, write it */
+ f.curpos = f.filepos = j;
+ unix_write (&f,"\n",1);
+ }
+ }
+ }
+ }
+
+ unix_write (&f,NIL,NIL); /* tie off final message */
+ if (size != f.filepos) fatal ("file size inconsistent");
+ fs_give ((void **) &f.buf); /* free buffer */
+ /* make sure tied off */
+ ftruncate (LOCAL->fd,LOCAL->filesize = size);
+ fsync (LOCAL->fd); /* make sure the updates take */
+ if (size && (flag < 0)) fatal ("lost UID base information");
+ /* no longer dirty */
+ LOCAL->ddirty = LOCAL->dirty = NIL;
+ /* notify upper level of new mailbox sizes */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ /* set atime to now, mtime a second earlier */
+ tp[1] = (tp[0] = time (0)) - 1;
+ /* set the times, note change */
+ if (!utime (stream->mailbox,tp)) LOCAL->filetime = tp[1];
+ close (LOCAL->fd); /* close and reopen file */
+ if ((LOCAL->fd = open (stream->mailbox,O_RDWR,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL)))
+ < 0) {
+ sprintf (LOCAL->buf,"Mailbox open failed, aborted: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ unix_abort (stream);
+ }
+ dotlock_unlock (lock); /* flush the lock file */
+ }
+ return ret; /* return state from algorithm */
+}
+
+/* Extend UNIX mailbox file
+ * Accepts: MAIL stream
+ * new desired size
+ * Return: T if success, else NIL
+ */
+
+long unix_extend (MAILSTREAM *stream,unsigned long size)
+{
+ unsigned long i = (size > LOCAL->filesize) ? size - LOCAL->filesize : 0;
+ if (i) { /* does the mailbox need to grow? */
+ if (i > LOCAL->buflen) { /* make sure have enough space */
+ /* this user won the lottery all right */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1);
+ }
+ memset (LOCAL->buf,'\0',i); /* get a block of nulls */
+ while (T) { /* until write successful or punt */
+ lseek (LOCAL->fd,LOCAL->filesize,L_SET);
+ if ((write (LOCAL->fd,LOCAL->buf,i) >= 0) && !fsync (LOCAL->fd)) break;
+ else {
+ long e = errno; /* note error before doing ftruncate */
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ if (MM_DISKERROR (stream,e,NIL)) {
+ fsync (LOCAL->fd); /* user chose to punt */
+ sprintf (LOCAL->buf,"Unable to extend mailbox: %s",strerror (e));
+ if (!stream->silent) MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ }
+ }
+ }
+ return LONGT;
+}
+
+/* Write data to buffered file
+ * Accepts: buffered file pointer
+ * file data or NIL to indicate "flush buffer"
+ * date size (ignored for "flush buffer")
+ * Does not return until success
+ */
+
+void unix_write (UNIXFILE *f,char *buf,unsigned long size)
+{
+ unsigned long i,j,k;
+ if (buf) { /* doing buffered write? */
+ i = f->bufpos - f->buf; /* yes, get size of current buffer data */
+ /* yes, have space in current buffer chunk? */
+ if (j = i ? ((f->buflen - i) % OVERFLOWBUFLEN) : f->buflen) {
+ /* yes, fill up buffer as much as we can */
+ memcpy (f->bufpos,buf,k = min (j,size));
+ f->bufpos += k; /* new buffer position */
+ f->curpos += k; /* new current position */
+ if (j -= k) return; /* all done if still have buffer free space */
+ buf += k; /* full, get new unwritten data pointer */
+ size -= k; /* new data size */
+ i += k; /* new buffer data size */
+ }
+ /* This chunk of the buffer is full. See if can make some space by
+ * writing to the disk, if there's enough unprotected space to do so.
+ * Try to fill out any unaligned chunk, along with any subsequent full
+ * chunks that will fit in unprotected space.
+ */
+ /* any unprotected space we can write to? */
+ if (j = min (i,f->protect - f->filepos)) {
+ /* yes, filepos not at chunk boundary? */
+ if ((k = f->filepos % OVERFLOWBUFLEN) && ((k = OVERFLOWBUFLEN - k) < j))
+ j -= k; /* yes, and can write out partial chunk */
+ else k = 0; /* no partial chunk to write */
+ /* if at least a chunk free, write that too */
+ if (j > OVERFLOWBUFLEN) k += j - (j % OVERFLOWBUFLEN);
+ if (k) { /* write data if there is anything we can */
+ unix_phys_write (f,f->buf,k);
+ /* slide buffer */
+ if (i -= k) memmove (f->buf,f->buf + k,i);
+ f->bufpos = f->buf + i; /* new end of buffer */
+ }
+ }
+
+ /* Have flushed the buffer as best as possible. All done if no more
+ * data to write. Otherwise, if the buffer is empty AND if the unwritten
+ * data is larger than a chunk AND the unprotected space is also larger
+ * than a chunk, then write as many chunks as we can directly from the
+ * data. Buffer the rest, expanding the buffer as needed.
+ */
+ if (size) { /* have more data that we need to buffer? */
+ /* can write any of it to disk instead? */
+ if ((f->bufpos == f->buf) &&
+ ((j = min (f->protect - f->filepos,size)) > OVERFLOWBUFLEN)) {
+ /* write as much as we can right now */
+ unix_phys_write (f,buf,j -= (j % OVERFLOWBUFLEN));
+ buf += j; /* new data pointer */
+ size -= j; /* new data size */
+ f->curpos += j; /* advance current pointer */
+ }
+ if (size) { /* still have data that we need to buffer? */
+ /* yes, need to expand the buffer? */
+ if ((i = ((f->bufpos + size) - f->buf)) > f->buflen) {
+ /* note current position in buffer */
+ j = f->bufpos - f->buf;
+ i += OVERFLOWBUFLEN; /* yes, grow another chunk */
+ fs_resize ((void **) &f->buf,f->buflen = i - (i % OVERFLOWBUFLEN));
+ /* in case buffer relocated */
+ f->bufpos = f->buf + j;
+ }
+ /* buffer remaining data */
+ memcpy (f->bufpos,buf,size);
+ f->bufpos += size; /* new end of buffer */
+ f->curpos += size; /* advance current pointer */
+ }
+ }
+ }
+ else { /* flush buffer to disk */
+ unix_phys_write (f,f->buf,i = f->bufpos - f->buf);
+ f->bufpos = f->buf; /* reset buffer */
+ /* update positions */
+ f->curpos = f->protect = f->filepos;
+ }
+}
+
+/* Physical disk write
+ * Accepts: buffered file pointer
+ * buffer address
+ * buffer size
+ * Does not return until success
+ */
+
+void unix_phys_write (UNIXFILE *f,char *buf,size_t size)
+{
+ MAILSTREAM *stream = f->stream;
+ /* write data at desired position */
+ while (size && ((lseek (LOCAL->fd,f->filepos,L_SET) < 0) ||
+ (write (LOCAL->fd,buf,size) < 0))) {
+ int e;
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Unable to write to mailbox: %s",strerror (e = errno));
+ MM_LOG (tmp,ERROR);
+ MM_DISKERROR (NIL,e,T); /* serious problem, must retry */
+ }
+ f->filepos += size; /* update file position */
+}
+
+/* MBOX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mboxdriver = {
+ "mbox", /* driver name */
+ /* driver flags */
+ DR_LOCAL|DR_MAIL|DR_LOCKING|DR_NONEWMAILRONLY,
+ (DRIVER *) NIL, /* next driver */
+ mbox_valid, /* mailbox is valid for us */
+ unix_parameters, /* manipulate parameters */
+ unix_scan, /* scan mailboxes */
+ unix_list, /* find mailboxes */
+ unix_lsub, /* find subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ mbox_create, /* create mailbox */
+ mbox_delete, /* delete mailbox */
+ mbox_rename, /* rename mailbox */
+ mbox_status, /* status of mailbox */
+ mbox_open, /* open mailbox */
+ unix_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message structure */
+ unix_header, /* fetch message header */
+ unix_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ NIL, /* modify flags */
+ unix_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mbox_ping, /* ping mailbox to see if still alive */
+ mbox_check, /* check for new messages */
+ mbox_expunge, /* expunge deleted messages */
+ unix_copy, /* copy messages to another mailbox */
+ mbox_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mboxproto = {&mboxdriver};
+
+/* MBOX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mbox_valid (char *name)
+{
+ /* only INBOX, mbox must exist */
+ if (!compare_cstring (name,"INBOX") && (unix_valid ("mbox") || !errno) &&
+ (unix_valid (sysinbox()) || !errno || (errno == ENOENT)))
+ return &mboxdriver;
+ return NIL; /* can't win (yet, anyway) */
+}
+
+/* MBOX mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mbox_create (MAILSTREAM *stream,char *mailbox)
+{
+ char tmp[MAILTMPLEN];
+ if (!compare_cstring (mailbox,"INBOX")) return unix_create (NIL,"mbox");
+ sprintf (tmp,"Can't create non-INBOX name as mbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+}
+
+
+/* MBOX mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mbox_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return mbox_rename (stream,mailbox,NIL);
+}
+
+
+/* MBOX mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long mbox_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ char tmp[MAILTMPLEN];
+ long ret = unix_rename (stream,"~/mbox",newname);
+ /* recreate file if renamed INBOX */
+ if (ret) unix_create (NIL,"mbox");
+ else MM_LOG (tmp,ERROR); /* log error */
+ return ret; /* return success */
+}
+
+/* MBOX Mail status
+ * Accepts: mail stream
+ * mailbox name
+ * status flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mbox_status (MAILSTREAM *stream,char *mbx,long flags)
+{
+ MAILSTATUS status;
+ unsigned long i;
+ MAILSTREAM *tstream = NIL;
+ MAILSTREAM *systream = NIL;
+ /* make temporary stream (unless this mbx) */
+ if (!stream && !(stream = tstream =
+ mail_open (NIL,mbx,OP_READONLY|OP_SILENT))) return NIL;
+ status.flags = flags; /* return status values */
+ status.messages = stream->nmsgs;
+ status.recent = stream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++)
+ if (!mail_elt (stream,i)->seen) status.unseen++;
+ status.uidnext = stream->uid_last + 1;
+ status.uidvalidity = stream->uid_validity;
+ if (!status.recent && /* calculate post-snarf results */
+ (systream = mail_open (NIL,sysinbox (),OP_READONLY|OP_SILENT))) {
+ status.messages += systream->nmsgs;
+ status.recent += systream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1; i <= systream->nmsgs; i++)
+ if (!mail_elt (systream,i)->seen) status.unseen++;
+ /* kludge but probably good enough */
+ status.uidnext += systream->nmsgs;
+ }
+ MM_STATUS(stream,mbx,&status);/* pass status to main program */
+ if (tstream) mail_close (tstream);
+ if (systream) mail_close (systream);
+ return T; /* success */
+}
+
+/* MBOX mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mbox_open (MAILSTREAM *stream)
+{
+ unsigned long i = 1;
+ unsigned long recent = 0;
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &mboxproto;
+ /* change mailbox file name */
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr ("mbox");
+ /* open mailbox, snarf new mail */
+ if (!(unix_open (stream) && mbox_ping (stream))) return NIL;
+ stream->inbox = T; /* mark that this is an INBOX */
+ /* notify upper level of mailbox sizes */
+ mail_exists (stream,stream->nmsgs);
+ while (i <= stream->nmsgs) if (mail_elt (stream,i++)->recent) ++recent;
+ mail_recent (stream,recent); /* including recent messages */
+ return stream;
+}
+
+/* MBOX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+static int snarfed = 0; /* number of snarfs */
+
+long mbox_ping (MAILSTREAM *stream)
+{
+ int sfd;
+ unsigned long size;
+ struct stat sbuf;
+ char *s;
+ DOTLOCK lock,lockx;
+ /* time to try snarf and sysinbox non-empty? */
+ if (LOCAL && !stream->rdonly && !stream->lock &&
+ (time (0) >= (LOCAL->lastsnarf +
+ (long) mail_parameters (NIL,GET_SNARFINTERVAL,NIL))) &&
+ !stat (sysinbox (),&sbuf) && sbuf.st_size) {
+ MM_CRITICAL (stream); /* yes, go critical */
+ /* open and lock sysinbox */
+ if ((sfd = unix_lock (sysinbox (),O_RDWR,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL),
+ &lockx,LOCK_EX)) >= 0) {
+ /* locked sysinbox in good format? */
+ if (fstat (sfd,&sbuf) || !(size = sbuf.st_size) ||
+ !unix_isvalid_fd (sfd)) {
+ sprintf (LOCAL->buf,"Mail drop %s is not in standard Unix format",
+ sysinbox ());
+ MM_LOG (LOCAL->buf,ERROR);
+ }
+ /* sysinbox good, parse and excl-lock mbox */
+ else if (unix_parse (stream,&lock,LOCK_EX)) {
+ lseek (sfd,0,L_SET); /* read entire sysinbox into memory */
+ read (sfd,s = (char *) fs_get (size + 1),size);
+ s[size] = '\0'; /* tie it off */
+ /* append to end of mbox */
+ lseek (LOCAL->fd,LOCAL->filesize,L_SET);
+
+ /* copy to mbox */
+ if ((write (LOCAL->fd,s,size) < 0) || fsync (LOCAL->fd)) {
+ sprintf (LOCAL->buf,"New mail move failed: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,WARN);
+ /* revert mbox to previous size */
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ }
+ /* sysinbox better not have changed */
+ else if (fstat (sfd,&sbuf) || (size != sbuf.st_size)) {
+ sprintf (LOCAL->buf,"Mail drop %s lock failure, old=%lu now=%lu",
+ sysinbox (),size,(unsigned long) sbuf.st_size);
+ MM_LOG (LOCAL->buf,ERROR);
+ /* revert mbox to previous size */
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ /* Believe it or not, a Singaporean government system actually had
+ * symlinks from /var/mail/user to ~user/mbox. To compound this
+ * error, they used an SVR4 system; BSD and OSF locks would have
+ * prevented it but not SVR4 locks.
+ */
+ if (!fstat (sfd,&sbuf) && (size == sbuf.st_size))
+ syslog (LOG_ALERT,"File %s and %s are the same file!",
+ sysinbox (),stream->mailbox);
+ }
+ else { /* data copied OK */
+ ftruncate (sfd,0); /* truncate sysinbox to zero bytes */
+ if (!snarfed++) { /* have we snarfed before? */
+ /* syslog if server, else user log */
+ sprintf (LOCAL->buf,"Moved %lu bytes of new mail to %s from %s",
+ size,stream->mailbox,sysinbox ());
+ if (strcmp ((char *) mail_parameters (NIL,GET_SERVICENAME,NIL),
+ "unknown"))
+ syslog (LOG_INFO,"%s host= %s",LOCAL->buf,tcp_clienthost ());
+ else MM_LOG (LOCAL->buf,WARN);
+ }
+ }
+ /* done with sysinbox text */
+ fs_give ((void **) &s);
+ /* all done with mbox */
+ unix_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream); /* unlock the stream */
+ MM_NOCRITICAL (stream); /* done with critical */
+ }
+ /* all done with sysinbox */
+ unix_unlock (sfd,NIL,&lockx);
+ }
+ MM_NOCRITICAL (stream); /* done with critical */
+ LOCAL->lastsnarf = time (0);/* note time of last snarf */
+ }
+ return unix_ping (stream); /* do the unix routine now */
+}
+
+/* MBOX mail check mailbox
+ * Accepts: MAIL stream
+ */
+
+void mbox_check (MAILSTREAM *stream)
+{
+ /* do local ping, then do unix routine */
+ if (mbox_ping (stream)) unix_check (stream);
+}
+
+
+/* MBOX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long mbox_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret = unix_expunge (stream,sequence,options);
+ mbox_ping (stream); /* do local ping */
+ return ret;
+}
+
+
+/* MBOX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mbox_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ char tmp[MAILTMPLEN];
+ if (mbox_valid (mailbox)) return unix_append (stream,"mbox",af,data);
+ sprintf (tmp,"Can't append to that name: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+}
diff --git a/imap/src/osdep/amiga/unix.h b/imap/src/osdep/amiga/unix.h
new file mode 100644
index 00000000..76cde8b5
--- /dev/null
+++ b/imap/src/osdep/amiga/unix.h
@@ -0,0 +1,219 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX mail routines, Amiga version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 20 December 1989
+ * Last Edited: 30 August 2006
+ */
+
+
+/* DEDICATION
+ *
+ * This file is dedicated to my dog, Unix, also known as Yun-chan and
+ * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix
+ * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after
+ * a two-month bout with cirrhosis of the liver.
+ *
+ * He was a dear friend, and I miss him terribly.
+ *
+ * Lift a leg, Yunie. Luv ya forever!!!!
+ */
+
+/* Validate line
+ * Accepts: pointer to candidate string to validate as a From header
+ * return pointer to end of date/time field
+ * return pointer to offset from t of time (hours of ``mmm dd hh:mm'')
+ * return pointer to offset from t of time zone (if non-zero)
+ * Returns: t,ti,zn set if valid From string, else ti is NIL
+ */
+
+#define VALID(s,x,ti,zn) { \
+ int remote = 0; \
+ ti = 0; \
+ if ((*s == 'F') && (s[1] == 'r') && (s[2] == 'o') && (s[3] == 'm') && \
+ (s[4] == ' ')) { \
+ for (x = s + 5; *x && *x != '\012'; x++); \
+ if (*x) { \
+ if (x[-1] == '\015') --x; \
+ if (x - s >= 41) { \
+ for (zn = -1; x[zn] != ' '; zn--); \
+ if ((x[zn-1] == 'm') && (x[zn-2] == 'o') && (x[zn-3] == 'r') && \
+ (x[zn-4] == 'f') && (x[zn-5] == ' ') && (x[zn-6] == 'e') && \
+ (x[zn-7] == 't') && (x[zn-8] == 'o') && (x[zn-9] == 'm') && \
+ (x[zn-10] == 'e') && (x[zn-11] == 'r') && (x[zn-12] == ' '))\
+ { \
+ while (x[zn-13] == ' ') zn--; \
+ x += zn - 12; \
+ remote = 1; \
+ } \
+ } \
+ if (x - s >= 27) { \
+ if (x[-5] == ' ') { \
+ if (x[-8] == ':') zn = 0,ti = -5; \
+ else if (x[-9] == ' ') ti = zn = -9; \
+ else if ((x[-11] == ' ') && ((x[-10]=='+') || (x[-10]=='-'))) \
+ ti = zn = -11; \
+ } \
+ else if (x[-4] == ' ') { \
+ if (x[-9] == ' ') zn = -4,ti = -9; \
+ else if ( (x[-13] == ' ') && (x[-16] == ' ') \
+ && (x[-20] ==' ') && \
+ ( ((x[-22] == ' ') && (x[-23] == ',')) || \
+ ((x[-23] == ' ') && (x[-24] == ',')) ) ) { \
+ char weekday[4]={0,}, month[4]={0,}, time[11]={0,}; \
+ char tzone[4]={0,}; \
+ char realtime[80]; \
+ int day,year,start=-26; \
+ if (x[-23] == ' ') x--; \
+ sscanf(&x[start],"%3c, %d %s %d %s %s", \
+ weekday,&day,month,&year,time,tzone); \
+ sprintf(realtime,"%s %s %2d %s %d %s", \
+ weekday,month,day,time, \
+ ( (year < 100) ? year+1900 : year),tzone); \
+ if (remote) \
+ strcat(realtime," remote from "); \
+ else \
+ strcat(realtime,"\n"); \
+ strncpy(&x[start],realtime,strlen(realtime)); \
+ zn = -2; \
+ ti = -7; \
+ } \
+ } \
+ else if (x[-6] == ' ') { \
+ if ((x[-11] == ' ') && ((x[-5] == '+') || (x[-5] == '-'))) \
+ zn = -6,ti = -11; \
+ } \
+ else if (x[-9] == ' ') { \
+ if ( ( (x[-12] == ' ') && (x[-16] == ' ') && \
+ ( ((x[-18] == ' ') && (x[-19] == ',') ) || \
+ ((x[-19] == ' ') && (x[-20] == ',')) ) \
+ || \
+ ((x[-14] == ' ') && (x[-18] == ' ') && \
+ ( ((x[-20] == ' ') && (x[-21] == ',') ) || \
+ ((x[-21] == ' ') && (x[-22] == ',')) ) ) ) ) { \
+ char weekday[4]={0,}, month[4]={0,},time[11]={0,}; \
+ int day,year,start=-24; \
+ char realtime[80]; \
+ if (x[-12] == ' ') x++; \
+ if (x[-19] == ' ') x++; \
+ sscanf(&x[start],"%3c, %d %3c %d %s",weekday, \
+ &day,month,&year,time); \
+ sprintf(realtime,"%s %s %2d %s %d",weekday,month,day,time,\
+ ( (year < 100) ? year+1900 : year)); \
+ if (remote) \
+ strcat(realtime," remote from "); \
+ else \
+ strcat(realtime,"\n"); \
+ strncpy(&x[start],realtime,strlen(realtime)); \
+ ti=-5; \
+ zn=0; \
+ } \
+ } \
+ if (ti && !((x[ti - 3] == ':') && \
+ (x[ti -= ((x[ti - 6] == ':') ? 9 : 6)] == ' ') && \
+ (x[ti - 3] == ' ') && (x[ti - 7] == ' ') && \
+ (x[ti - 11] == ' '))) ti = 0; \
+ } \
+ } \
+ } \
+}
+
+/* You are not expected to understand this macro, but read the next page if
+ * you are not faint of heart.
+ *
+ * Known formats to the VALID macro are:
+ * From user Wed Dec 2 05:53 1992
+ * BSD From user Wed Dec 2 05:53:22 1992
+ * SysV From user Wed Dec 2 05:53 PST 1992
+ * rn From user Wed Dec 2 05:53:22 PST 1992
+ * From user Wed Dec 2 05:53 -0700 1992
+ * emacs From user Wed Dec 2 05:53:22 -0700 1992
+ * From user Wed Dec 2 05:53 1992 PST
+ * From user Wed Dec 2 05:53:22 1992 PST
+ * From user Wed Dec 2 05:53 1992 -0700
+ * Solaris From user Wed Dec 2 05:53:22 1992 -0700
+ *
+ * Amiga From user Wed, 6 Dec 92 05:53:22 who did this !!!
+ * CHANGED in place to
+ * From user Wed Dec 2 05:53:22 1992
+ *
+ * Plus all of the above with `` remote from xxx'' after it. Thank you very
+ * much, smail and Solaris, for making my life considerably more complicated.
+ */
+
+/*
+ * What? You want to understand the VALID macro anyway? Alright, since you
+ * insist. Actually, it isn't really all that difficult, provided that you
+ * take it step by step.
+ *
+ * Line 1 Initializes the return ti value to failure (0);
+ * Lines 2-3 Validates that the 1st-5th characters are ``From ''.
+ * Lines 4-6 Validates that there is an end of line and points x at it.
+ * Lines 7-14 First checks to see if the line is at least 41 characters long.
+ * If so, it scans backwards to find the rightmost space. From
+ * that point, it scans backwards to see if the string matches
+ * `` remote from''. If so, it sets x to point to the space at
+ * the start of the string.
+ * Line 15 Makes sure that there are at least 27 characters in the line.
+ * Lines 16-21 Checks if the date/time ends with the year (there is a space
+ * five characters back). If there is a colon three characters
+ * further back, there is no timezone field, so zn is set to 0
+ * and ti is set in front of the year. Otherwise, there must
+ * either to be a space four characters back for a three-letter
+ * timezone, or a space six characters back followed by a + or -
+ * for a numeric timezone; in either case, zn and ti become the
+ * offset of the space immediately before it.
+ * Lines 22-24 Are the failure case for line 14. If there is a space four
+ * characters back, it is a three-letter timezone; there must be a
+ * space for the year nine characters back. zn is the zone
+ * offset; ti is the offset of the space.
+ * Lines 25-28 Are the failure case for line 20. If there is a space six
+ * characters back, it is a numeric timezone; there must be a
+ * space eleven characters back and a + or - five characters back.
+ * zn is the zone offset; ti is the offset of the space.
+ * Line 29-32 If ti is valid, make sure that the string before ti is of the
+ * form www mmm dd hh:mm or www mmm dd hh:mm:ss, otherwise
+ * invalidate ti. There must be a colon three characters back
+ * and a space six or nine characters back (depending upon
+ * whether or not the character six characters back is a colon).
+ * There must be a space three characters further back (in front
+ * of the day), one seven characters back (in front of the month),
+ * and one eleven characters back (in front of the day of week).
+ * ti is set to be the offset of the space before the time.
+ *
+ * Why a macro? It gets invoked a *lot* in a tight loop. On some of the
+ * newer pipelined machines it is faster being open-coded than it would be if
+ * subroutines are called.
+ *
+ * Why does it scan backwards from the end of the line, instead of doing the
+ * much easier forward scan? There is no deterministic way to parse the
+ * ``user'' field, because it may contain unquoted spaces! Yes, I tested it to
+ * see if unquoted spaces were possible. They are, and I've encountered enough
+ * evil mail to be totally unwilling to trust that ``it will never happen''.
+ */
+
+/* Build parameters */
+
+#define KODRETRY 15 /* kiss-of-death retry in seconds */
+#define LOCKTIMEOUT 5 /* lock timeout in minutes */
+#define CHUNK 16384 /* read-in chunk size */
diff --git a/imap/src/osdep/amiga/write.c b/imap/src/osdep/amiga/write.c
new file mode 100644
index 00000000..c7854815
--- /dev/null
+++ b/imap/src/osdep/amiga/write.c
@@ -0,0 +1,59 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Write data, treating partial writes as an error
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 May 1995
+ * Last Edited: 30 August 2006
+ */
+
+/* The whole purpose of this unfortunate routine is to deal with DOS and
+ * certain cretinous versions of UNIX which decided that the "bytes actually
+ * written" return value from write() gave them license to use that for things
+ * that are really errors, such as disk quota exceeded, maximum file size
+ * exceeded, disk full, etc.
+ *
+ * BSD won't screw us this way on the local filesystem, but who knows what
+ * some NFS-mounted filesystem will do.
+ */
+
+#undef write
+
+/* Write data to file
+ * Accepts: file descriptor
+ * I/O vector structure
+ * number of vectors in structure
+ * Returns: number of bytes written if successful, -1 if failure
+ */
+
+long maxposint = (long)((((unsigned long) 1) << ((sizeof(int) * 8) - 1)) - 1);
+
+long safe_write (int fd,char *buf,long nbytes)
+{
+ long i,j;
+ if (nbytes > 0) for (i = nbytes; i; i -= j,buf += j) {
+ while (((j = write (fd,buf,(int) min (maxposint,i))) < 0) &&
+ (errno == EINTR));
+ if (j < 0) return j;
+ }
+ return nbytes;
+}
diff --git a/imap/src/osdep/dos/bezrkdos.c b/imap/src/osdep/dos/bezrkdos.c
new file mode 100644
index 00000000..81617380
--- /dev/null
+++ b/imap/src/osdep/dos/bezrkdos.c
@@ -0,0 +1,901 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Berkeley mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 24 June 1992
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Dedication:
+ * This file is dedicated with affection to those Merry Marvels of Musical
+ * Madness . . .
+ * -> The Incomparable Leland Stanford Junior University Marching Band <-
+ * who entertain, awaken, and outrage Stanford fans in the fact of repeated
+ * losing seasons and shattered Rose Bowl dreams [Cardinal just don't have
+ * HUSKY FEVER!!!].
+ *
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include "mail.h"
+#include "osdep.h"
+#include <time.h>
+#include <sys\stat.h>
+#include <dos.h>
+#include "rfc822.h"
+#include "dummy.h"
+#include "misc.h"
+#include "fdstring.h"
+
+/* Berkeley I/O stream local data */
+
+typedef struct bezerk_local {
+ int fd; /* file descriptor for I/O */
+ off_t filesize; /* file size parsed */
+ char *buf; /* temporary buffer */
+} BEZERKLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((BEZERKLOCAL *) stream->local)
+
+/* Function prototypes */
+
+DRIVER *bezerk_valid (char *name);
+long bezerk_isvalid (char *name,char *tmp);
+int bezerk_valid_line (char *s,char **rx,int *rzn);
+void *bezerk_parameters (long function,void *value);
+void bezerk_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void bezerk_list (MAILSTREAM *stream,char *ref,char *pat);
+void bezerk_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long bezerk_create (MAILSTREAM *stream,char *mailbox);
+long bezerk_delete (MAILSTREAM *stream,char *mailbox);
+long bezerk_rename (MAILSTREAM *stream,char *old,char *newname);
+MAILSTREAM *bezerk_open (MAILSTREAM *stream);
+void bezerk_close (MAILSTREAM *stream,long options);
+char *bezerk_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long bezerk_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,
+ long flags);
+long bezerk_ping (MAILSTREAM *stream);
+void bezerk_check (MAILSTREAM *stream);
+long bezerk_expunge (MAILSTREAM *stream,char *sequence,long options);
+long bezerk_copy (MAILSTREAM *stream,char *sequence,char *mailbox,
+ long options);
+long bezerk_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+int bezerk_append_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
+ STRING *msg);
+void bezerk_gc (MAILSTREAM *stream,long gcflags);
+char *bezerk_file (char *dst,char *name);
+long bezerk_badname (char *tmp,char *s);
+long bezerk_parse (MAILSTREAM *stream);
+unsigned long bezerk_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size);
+
+/* Berkeley mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER bezerkdriver = {
+ "bezerk", /* driver name */
+ /* driver flags */
+ DR_LOCAL|DR_MAIL|DR_LOWMEM|DR_CRLF|DR_NOSTICKY,
+ (DRIVER *) NIL, /* next driver */
+ bezerk_valid, /* mailbox is valid for us */
+ bezerk_parameters, /* manipulate parameters */
+ bezerk_scan, /* scan mailboxes */
+ bezerk_list, /* list mailboxes */
+ bezerk_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ bezerk_create, /* create mailbox */
+ bezerk_delete, /* delete mailbox */
+ bezerk_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ bezerk_open, /* open mailbox */
+ bezerk_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ bezerk_header, /* fetch message header */
+ bezerk_text, /* fetch message text */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ NIL, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ bezerk_ping, /* ping mailbox to see if still alive */
+ bezerk_check, /* check for new messages */
+ bezerk_expunge, /* expunge deleted messages */
+ bezerk_copy, /* copy messages to another mailbox */
+ bezerk_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM bezerkproto = {&bezerkdriver};
+
+/* Berkeley mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *bezerk_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return bezerk_isvalid (name,tmp) ? &bezerkdriver : (DRIVER *) NIL;
+}
+
+
+/* Berkeley mail test for valid mailbox
+ * Accepts: mailbox name
+ * Returns: T if valid, NIL otherwise
+ */
+
+long bezerk_isvalid (char *name,char *tmp)
+{
+ int fd;
+ long ret = NIL;
+ struct stat sbuf;
+ errno = EINVAL; /* assume invalid argument */
+ /* if file, get its status */
+ if ((*name != '{') && mailboxfile (tmp,name) && !stat (tmp,&sbuf)) {
+ if (!sbuf.st_size)errno = 0;/* empty file */
+ else if ((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) >= 0) {
+ memset (tmp,'\0',MAILTMPLEN);
+ errno = -1; /* in case bezerk_valid_line fails */
+ if (read (fd,tmp,MAILTMPLEN-1) >= 0)
+ ret = bezerk_valid_line (tmp,NIL,NIL);
+ close (fd); /* close the file */
+ }
+ }
+ /* in case INBOX but not bezerk format */
+ else if ((errno == ENOENT) && ((name[0] == 'I') || (name[0] == 'i')) &&
+ ((name[1] == 'N') || (name[1] == 'n')) &&
+ ((name[2] == 'B') || (name[2] == 'b')) &&
+ ((name[3] == 'O') || (name[3] == 'o')) &&
+ ((name[4] == 'X') || (name[4] == 'x')) && !name[5]) errno = -1;
+ return ret; /* return what we should */
+}
+
+/* Validate line
+ * Accepts: pointer to candidate string to validate as a From header
+ * return pointer to end of date/time field
+ * return pointer to offset from t of time (hours of ``mmm dd hh:mm'')
+ * return pointer to offset from t of time zone (if non-zero)
+ * Returns: t,ti,zn set if valid From string, else ti is NIL
+ */
+
+int bezerk_valid_line (char *s,char **rx,int *rzn)
+{
+ char *x;
+ int zn;
+ int ti = 0;
+ /* line must begin with "From " */
+ if ((*s != 'F') || (s[1] != 'r') || (s[2] != 'o') || (s[3] != 'm') ||
+ (s[4] != ' ')) return NIL;
+ /* find end of line */
+ for (x = s + 5; *x && *x != '\012'; x++);
+ if (!x) return NIL; /* end of line not found */
+ if (x[-1] == '\015') x--; /* ignore CR */
+ if ((x - s < 27)) return NIL; /* line too short */
+ if (x - s >= 41) { /* possible search for " remote from " */
+ for (zn = -1; x[zn] != ' '; zn--);
+ if ((x[zn-1] == 'm') && (x[zn-2] == 'o') && (x[zn-3] == 'r') &&
+ (x[zn-4] == 'f') && (x[zn-5] == ' ') && (x[zn-6] == 'e') &&
+ (x[zn-7] == 't') && (x[zn-8] == 'o') && (x[zn-9] == 'm') &&
+ (x[zn-10] == 'e') && (x[zn-11] == 'r') && (x[zn-12] == ' '))
+ x += zn - 12;
+ }
+ if (x[-5] == ' ') { /* ends with year? */
+ /* no timezone? */
+ if (x[-8] == ':') zn = 0,ti = -5;
+ /* three letter timezone? */
+ else if (x[-9] == ' ') ti = zn = -9;
+ /* numeric timezone? */
+ else if ((x[-11]==' ') && ((x[-10]=='+') || (x[-10]=='-'))) ti = zn = -11;
+ }
+ else if (x[-4] == ' ') { /* no year and three leter timezone? */
+ if (x[-9] == ' ') zn = -4,ti = -9;
+ }
+ else if (x[-6] == ' ') { /* no year and numeric timezone? */
+ if ((x[-11] == ' ') && ((x[-5] == '+') || (x[-5] == '-')))
+ zn = -6,ti = -11;
+ }
+ /* time must be www mmm dd hh:mm[:ss] */
+ if (ti && !((x[ti - 3] == ':') &&
+ (x[ti -= ((x[ti - 6] == ':') ? 9 : 6)] == ' ') &&
+ (x[ti - 3] == ' ') && (x[ti - 7] == ' ') &&
+ (x[ti - 11] == ' '))) return NIL;
+ if (rx) *rx = x; /* set return values */
+ if (rzn) *rzn = zn;
+ return ti;
+}
+
+/* Berkeley manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *bezerk_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+
+/* Berkeley mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void bezerk_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* Berkeley mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void bezerk_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (stream,ref,pat);
+}
+
+
+/* Berkeley mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void bezerk_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (stream,ref,pat);
+}
+
+/* Berkeley mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long bezerk_create (MAILSTREAM *stream,char *mailbox)
+{
+ return dummy_create (stream,mailbox);
+}
+
+
+/* Berkeley mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long bezerk_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return dummy_delete (stream,mailbox);
+}
+
+
+/* Berkeley mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long bezerk_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ return dummy_rename (stream,old,newname);
+}
+
+/* Berkeley mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *bezerk_open (MAILSTREAM *stream)
+{
+ long i;
+ int fd;
+ char *s;
+ char tmp[MAILTMPLEN];
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &bezerkproto;
+ if (stream->local) fatal ("bezerk recycle stream");
+ if (!mailboxfile (tmp,stream->mailbox))
+ return (MAILSTREAM *) bezerk_badname (tmp,stream->mailbox);
+ if (((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) < 0)) {
+ sprintf (tmp,"Can't open mailbox: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ stream->rdonly = T; /* this driver is readonly */
+ stream->local = fs_get (sizeof (BEZERKLOCAL));
+ /* canonicalize the stream mailbox name */
+ fs_give ((void **) &stream->mailbox);
+ if (s = strchr ((s = strrchr (tmp,'\\')) ? s : tmp,'.')) *s = '\0';
+ stream->mailbox = cpystr (tmp);
+ LOCAL->fd = fd; /* note the file */
+ LOCAL->filesize = 0; /* initialize parsed file size */
+ LOCAL->buf = NIL; /* initially no local buffer */
+ stream->sequence++; /* bump sequence number */
+ stream->uid_validity = time (0);
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ if (!bezerk_ping (stream)) return NIL;
+ if (!stream->nmsgs) mm_log ("Mailbox is empty",(long) NIL);
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft = NIL;
+ stream->perm_user_flags = NIL;
+ return stream; /* return stream to caller */
+}
+
+/* Berkeley mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void bezerk_close (MAILSTREAM *stream,long options)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T;
+ if (options & CL_EXPUNGE) bezerk_expunge (stream,NIL,NIL);
+ close (LOCAL->fd); /* close the local file */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* Berkeley mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *bezerk_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags)
+{
+ char tmp[MAILTMPLEN];
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ /* get to header position */
+ lseek (LOCAL->fd,bezerk_hdrpos (stream,msgno,length),L_SET);
+ /* is buffer big enough? */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((size_t) *length + 1);
+ LOCAL->buf[*length] = '\0'; /* tie off string */
+ /* slurp the data */
+ read (LOCAL->fd,LOCAL->buf,(size_t) *length);
+ return LOCAL->buf;
+}
+
+
+/* Berkeley mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: T, always
+ */
+
+long bezerk_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ MESSAGECACHE *elt;
+ FDDATA d;
+ unsigned long hdrsize,hdrpos;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mail_elt (stream,msgno);/* if message not seen */
+ /* mark message as seen */
+ if (elt->seen && !(flags & FT_PEEK)) {
+ elt->seen = T;
+ mm_flags (stream,msgno);
+ }
+ /* get location of text data */
+ hdrpos = bezerk_hdrpos (stream,msgno,&hdrsize);
+ d.fd = LOCAL->fd; /* set initial stringstruct */
+ d.pos = hdrpos + hdrsize;
+ /* flush old buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ d.chunk = LOCAL->buf = (char *) fs_get ((size_t) d.chunksize = CHUNKSIZE);
+ INIT (bs,fd_string,(void *) &d,elt->rfc822_size - hdrsize);
+ return T; /* success */
+}
+
+/* Berkeley mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream still alive, NIL if not
+ */
+
+long bezerk_ping (MAILSTREAM *stream)
+{
+ /* punt if stream no longer alive */
+ if (!(stream && LOCAL)) return NIL;
+ /* parse mailbox, punt if parse dies */
+ return (bezerk_parse (stream)) ? T : NIL;
+}
+
+
+/* Berkeley mail check mailbox (reparses status too)
+ * Accepts: MAIL stream
+ */
+
+void bezerk_check (MAILSTREAM *stream)
+{
+ unsigned long i = 1;
+ if (bezerk_ping (stream)) { /* ping mailbox */
+ /* get new message status */
+ while (i <= stream->nmsgs) mail_elt (stream,i++);
+ mm_log ("Check completed",(long) NIL);
+ }
+}
+
+/* Berkeley mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long bezerk_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ if (!stream->silent) mm_log ("Expunge ignored on readonly mailbox",WARN);
+ return LONGT;
+}
+
+/* Berkeley mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if success, NIL if failed
+ */
+
+long bezerk_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ char tmp[MAILTMPLEN];
+ struct stat sbuf;
+ MESSAGECACHE *elt;
+ unsigned long i,j,k;
+ int fd;
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* make sure valid mailbox */
+ if (!bezerk_isvalid (mailbox,tmp) && errno) {
+ if (errno == ENOENT)
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",
+ (long) NIL);
+ else if (pc) return (*pc) (stream,sequence,mailbox,options);
+ else if (mailboxfile (tmp,mailbox)) {
+ sprintf (tmp,"Not a Bezerk-format mailbox: %s",mailbox);
+ mm_log (tmp,ERROR);
+ }
+ else bezerk_badname (tmp,mailbox);
+ return NIL;
+ }
+ /* open the destination */
+ if (!mailboxfile (tmp,mailbox) ||
+ (fd = open (tmp,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,
+ S_IREAD|S_IWRITE)) < 0) {
+ sprintf (tmp,"Unable to open copy mailbox: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+
+ mm_critical (stream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ /* for each requested message */
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,SEEK_SET);
+ /* number of bytes to copy */
+ j = elt->private.msg.full.offset + elt->rfc822_size;
+ do { /* read from source position */
+ k = min (j,(unsigned long) MAILTMPLEN);
+ read (LOCAL->fd,tmp,(unsigned int) k);
+ if (write (fd,tmp,(unsigned int) k) < 0) {
+ sprintf (tmp,"Unable to write message: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ chsize (fd,sbuf.st_size);
+ close (fd); /* punt */
+ mm_nocritical (stream);
+ return NIL;
+ }
+ } while (j -= k); /* until done */
+ }
+ close (fd); /* close the file */
+ mm_nocritical (stream); /* release critical */
+ /* delete all requested messages */
+ if (options & CP_MOVE) for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) elt->deleted = T;
+ if (mail_parameters (NIL,GET_COPYUID,NIL))
+ mm_log ("Can not return meaningful COPYUID with this mailbox format",WARN);
+ return T;
+}
+
+/* Berkeley mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+#define BUFLEN MAILTMPLEN
+
+long bezerk_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd;
+ unsigned long i,j;
+ char *flags,*date,buf[BUFLEN],tmp[MAILTMPLEN],file[MAILTMPLEN];
+ FILE *sf,*df;
+ MESSAGECACHE elt;
+ STRING *message;
+ long ret = LONGT;
+ /* default stream to prototype */
+ if (!stream) stream = &bezerkproto;
+ /* make sure valid mailbox */
+ if (!bezerk_isvalid (mailbox,tmp) && errno) {
+ if (errno == ENOENT) {
+ if (((mailbox[0] == 'I') || (mailbox[0] == 'i')) &&
+ ((mailbox[1] == 'N') || (mailbox[1] == 'n')) &&
+ ((mailbox[2] == 'B') || (mailbox[2] == 'b')) &&
+ ((mailbox[3] == 'O') || (mailbox[3] == 'o')) &&
+ ((mailbox[4] == 'X') || (mailbox[4] == 'x')) && !mailbox[5])
+ bezerk_create (NIL,"INBOX");
+ else {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ }
+ else if (mailboxfile (tmp,mailbox)) {
+ sprintf (tmp,"Not a Bezerk-format mailbox: %.80ss",mailbox);
+ mm_log (tmp,ERROR);
+ }
+ else bezerk_badname (tmp,mailbox);
+ return NIL;
+ }
+ tzset (); /* initialize timezone stuff */
+ /* get first message */
+ if (!(*af) (stream,data,&flags,&date,&message)) return NIL;
+ if (!(sf = tmpfile ())) { /* must have scratch file */
+ sprintf (tmp,"Unable to create scratch file: %.80s",strerror (errno));
+ mm_log (tmp,ERROR);
+ }
+
+ do { /* parse date */
+ if (!date) rfc822_date (date = tmp);
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ mm_log (tmp,ERROR);
+ }
+ else { /* user wants to suppress time zones? */
+ if (mail_parameters (NIL,GET_NOTIMEZONES,NIL)) {
+ time_t when = mail_longdate (&elt);
+ date = ctime (&when); /* use traditional date */
+ }
+ /* use POSIX-style date */
+ else date = mail_cdate (tmp,&elt);
+ if (!SIZE (message)) mm_log ("Append of zero-length message",ERROR);
+ else if (!bezerk_append_msg (stream,sf,flags,date,message)) {
+ sprintf (tmp,"Error writing scratch file: %.80s",strerror (errno));
+ mm_log (tmp,ERROR);
+ }
+ /* get next message */
+ else if ((*af) (stream,data,&flags,&date,&message)) continue;
+ }
+ fclose (sf); /* punt scratch file */
+ return NIL; /* give up */
+ } while (message); /* until no more messages */
+ if (fflush (sf) || fstat (fileno (sf),&sbuf)) {
+ sprintf (tmp,"Error finishing scratch file: %.80s",strerror (errno));
+ mm_log (tmp,ERROR);
+ fclose (sf); /* punt scratch file */
+ return NIL; /* give up */
+ }
+ i = sbuf.st_size; /* size of scratch file */
+
+ mm_critical (stream); /* go critical */
+ /* open the destination */
+ if (!mailboxfile (tmp,mailbox) ||
+ ((fd = open (tmp,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,
+ S_IREAD|S_IWRITE)) < 0) ||
+ !(df = fdopen (fd,"ab"))) {
+ mm_nocritical (stream); /* done with critical */
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ while (i) /* until written all bytes */
+ if ((j = fread (buf,1,min ((long) BUFLEN,i),sf)) &&
+ (fwrite (buf,1,j,df) == j)) i -= j;
+ fclose (sf); /* done with scratch file */
+ /* make sure append wins */
+ if (i || (fflush (df) == EOF)) {
+ chsize (fd,sbuf.st_size); /* revert file */
+ close (fd); /* make sure fclose() doesn't corrupt us */
+ sprintf (buf,"Message append failed: %s",strerror (errno));
+ mm_log (buf,ERROR);
+ ret = NIL; /* return error */
+ }
+ fclose (df);
+ mm_nocritical (stream); /* release critical */
+ if (ret && mail_parameters (NIL,GET_APPENDUID,NIL))
+ mm_log ("Can not return meaningful APPENDUID with this mailbox format",
+ WARN);
+ return ret;
+}
+
+/* Write single message to append scratch file
+ * Accepts: MAIL stream
+ * scratch file
+ * flags
+ * message stringstruct
+ * Returns: NIL if write error, else T
+ */
+
+int bezerk_append_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
+ STRING *msg)
+{
+ int c;
+ unsigned long i,uf;
+ char tmp[MAILTMPLEN];
+ long f = mail_parse_flags (stream,flags,&uf);
+ /* build initial header */
+ if ((fprintf (sf,"From %s@%s %sStatus: ",
+ myusername (),mylocalhost (),date) < 0) ||
+ (f&fSEEN && (putc ('R',sf) == EOF)) ||
+ (fputs ("\nX-Status: ",sf) == EOF) ||
+ (f&fDELETED && (putc ('D',sf) == EOF)) ||
+ (f&fFLAGGED && (putc ('F',sf) == EOF)) ||
+ (f&fANSWERED && (putc ('A',sf) == EOF)) ||
+ (f&fDRAFT && (putc ('T',sf) == EOF)) ||
+ (fputs ("\nX-Keywords:",sf) == EOF)) return NIL;
+ while (uf) /* write user flags */
+ if (fprintf (sf," %s",stream->user_flags[find_rightmost_bit (&uf)]) < 0)
+ return NIL;
+ /* tie off flags */
+ if (putc ('\n',sf) == EOF) return NIL;
+ while (SIZE (msg)) { /* copy text to scratch file */
+ /* possible delimiter if line starts with F */
+ if ((c = 0xff & SNX (msg)) == 'F') {
+ /* copy line to buffer */
+ for (i = 1,tmp[0] = c; SIZE (msg) && (c != '\n') && (i < MAILTMPLEN);)
+ if (((c = 0xff & SNX (msg)) != '\r') || !(SIZE (msg)) ||
+ (CHR (msg) != '\n')) tmp[i++] = c;
+ if ((i > 4) && (tmp[1] == 'r') && (tmp[2] == 'o') && (tmp[3] == 'm') &&
+ (tmp[4] == ' ')) { /* possible "From " line? */
+ /* yes, see if need to write a widget */
+ if (((c != '\n') || bezerk_valid_line (tmp,NIL,NIL)) &&
+ (putc ('>',sf) == EOF)) return NIL;
+ }
+ /* write buffered text */
+ if (fwrite (tmp,1,i,sf) != i) return NIL;
+ if (c == '\n') continue; /* all done if got a complete line */
+ }
+ /* copy line, toss out CR from CRLF */
+ do if (((c == '\r') && SIZE (msg) && ((c = 0xff & SNX (msg)) != '\n') &&
+ (putc ('\r',sf) == EOF)) || (putc (c,sf) == EOF)) return NIL;
+ while ((c != '\n') && SIZE (msg) && ((c = 0xff & SNX (msg)) ? c : T));
+ }
+ /* write trailing newline and return */
+ return (putc ('\n',sf) == EOF) ? NIL : T;
+}
+
+
+/* Return bad file name error message
+ * Accepts: temporary buffer
+ * file name
+ * Returns: long NIL always
+ */
+
+long bezerk_badname (char *tmp,char *s)
+{
+ sprintf (tmp,"Invalid mailbox name: %s",s);
+ mm_log (tmp,ERROR);
+ return (long) NIL;
+}
+
+/* Parse mailbox
+ * Accepts: MAIL stream
+ * Returns: T if parse OK
+ * NIL if failure, stream aborted
+ */
+
+long bezerk_parse (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt;
+ char *s,*t,tmp[MAILTMPLEN + 1],*db,datemsg[100];
+ long i;
+ int j,ti,zn;
+ long curpos = LOCAL->filesize;
+ long nmsgs = stream->nmsgs;
+ long recent = stream->recent;
+ short silent = stream->silent;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ if (sbuf.st_size < curpos) { /* sanity check */
+ sprintf (tmp,"Mailbox shrank from %ld to %ld!",curpos,sbuf.st_size);
+ mm_log (tmp,ERROR);
+ bezerk_close (stream,NIL);
+ return NIL;
+ }
+ stream->silent = T; /* don't pass up mm_exists() events yet */
+ db = datemsg + strlen (strcpy (datemsg,"Unparsable date: "));
+ /* while there is data to read */
+ while (i = sbuf.st_size - curpos){
+ /* get to that position in the file */
+ lseek (LOCAL->fd,curpos,SEEK_SET);
+ /* read first buffer's worth */
+ read (LOCAL->fd,tmp,j = (int) min (i,(long) MAILTMPLEN));
+ tmp[j] = '\0'; /* tie off buffer */
+ if (!(ti = bezerk_valid_line (tmp,&t,&zn))) {
+ mm_log ("Mailbox format invalidated (consult an expert), aborted",ERROR);
+ bezerk_close (stream,NIL);
+ return NIL;
+ }
+
+ /* swell the cache */
+ mail_exists (stream,++nmsgs);
+ /* instantiate an elt for this message */
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ elt->private.uid = ++stream->uid_last;
+ /* note file offset of header */
+ elt->private.special.offset = curpos;
+ /* note offset of message */
+ elt->private.msg.full.offset =
+ (s = ((*t == '\015') ? (t + 2) : (t + 1))) - tmp;
+ /* generate plausable IMAPish date string */
+ db[2] = db[6] = db[20] = '-'; db[11] = ' '; db[14] = db[17] = ':';
+ /* dd */
+ db[0] = t[ti - 2]; db[1] = t[ti - 1];
+ /* mmm */
+ db[3] = t[ti - 6]; db[4] = t[ti - 5]; db[5] = t[ti - 4];
+ /* hh */
+ db[12] = t[ti + 1]; db[13] = t[ti + 2];
+ /* mm */
+ db[15] = t[ti + 4]; db[16] = t[ti + 5];
+ if (t[ti += 6] == ':') { /* ss if present */
+ db[18] = t[++ti]; db[19] = t[++ti];
+ ti++; /* move to space */
+ }
+ else db[18] = db[19] = '0'; /* assume 0 seconds */
+ /* yy -- advance over timezone if necessary */
+ if (++zn == ++ti) ti += (((t[zn] == '+') || (t[zn] == '-')) ? 6 : 4);
+ db[7] = t[ti]; db[8] = t[ti + 1]; db[9] = t[ti + 2]; db[10] = t[ti + 3];
+ t = zn ? (t + zn) : "LCL"; /* zzz */
+ db[21] = *t++; db[22] = *t++; db[23] = *t++;
+ if ((db[21] != '+') && (db[21] != '-')) db[24] = '\0';
+ else { /* numeric time zone */
+ db[20] = ' '; db[24] = *t++; db[25] = *t++; db[26] = '\0';
+ }
+ /* set internal date */
+ if (!mail_parse_date (elt,db)) mm_log (datemsg,WARN);
+
+ curpos += s - tmp; /* advance position after header */
+ t = strchr (s,'\012'); /* start of next line */
+ /* find start of next message */
+ while (!(bezerk_valid_line (s,NIL,NIL))) {
+ if (t) { /* have next line? */
+ t++; /* advance to new line */
+ curpos += t - s; /* update position and size */
+ elt->rfc822_size += ((t - s) + ((t[-2] == '\015') ? 0 : 1));
+ s = t; /* move to next line */
+ t = strchr (s,'\012');
+ }
+ else { /* try next buffer */
+ j = strlen (s); /* length of unread data in buffer */
+ if ((i = sbuf.st_size - curpos) && (i != j)) {
+ /* get to that position in the file */
+ lseek (LOCAL->fd,curpos,SEEK_SET);
+ /* read another buffer's worth */
+ read (LOCAL->fd,s = tmp,j = (int) min (i,(long) MAILTMPLEN));
+ tmp[j] = '\0'; /* tie off buffer */
+ if (!(t = strchr (s,'\012'))) fatal ("Line too long in mailbox");
+ }
+ else {
+ curpos += j; /* last bit of data */
+ elt->rfc822_size += j;
+ break;
+ }
+ }
+ }
+ }
+ /* update parsed file size */
+ LOCAL->filesize = sbuf.st_size;
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
+ mail_recent (stream,recent); /* and of change in recent messages */
+ return T; /* return the winnage */
+}
+
+/* Berkeley locate header for a message
+ * Accepts: MAIL stream
+ * message number
+ * pointer to returned header size
+ * Returns: position of header in file
+ */
+
+unsigned long bezerk_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size)
+{
+ long siz;
+ size_t i = 0;
+ char c = '\0';
+ char *s;
+ char tmp[MAILTMPLEN];
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ long pos = elt->private.special.offset + elt->private.msg.full.offset;
+ /* is size known? */
+ if (!(*size = elt->private.msg.header.text.size)) {
+ /* get to header position */
+ lseek (LOCAL->fd,pos,SEEK_SET);
+ /* search message for CRLF CRLF */
+ for (siz = 1; siz <= elt->rfc822_size; siz++) {
+ if (!i && /* buffer empty? */
+ (read (LOCAL->fd,s = tmp,
+ i = (size_t) min(elt->rfc822_size-siz,(long)MAILTMPLEN))<= 0))
+ return pos;
+ else i--;
+ /* two newline sequence? */
+ if ((c == '\012') && (*s == '\012')) {
+ /* yes, note for later */
+ elt->private.msg.header.text.size = (*size = siz);
+ return pos; /* return to caller */
+ }
+ else if ((c == '\012') && (*s == '\015')) {
+ /* yes, note for later */
+ elt->private.msg.header.text.size = (*size = siz + 1);
+ return pos; /* return to caller */
+ }
+ else c = *s++; /* next character */
+ }
+ }
+ return pos; /* have position */
+}
diff --git a/imap/src/osdep/dos/drivers.bat b/imap/src/osdep/dos/drivers.bat
new file mode 100644
index 00000000..0964f537
--- /dev/null
+++ b/imap/src/osdep/dos/drivers.bat
@@ -0,0 +1,33 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Driver Linkage Generator for DOS/NT
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 11 October 1989
+REM Last Edited:30 August 2006
+
+REM Erase old driver linkage
+IF EXIST LINKAGE.* DEL LINKAGE.*
+
+REM Now define the new list
+FOR %%D IN (%1 %2 %3 %4 %5 %6 %7 %8 %9) DO CALL DRIVRAUX %%D
+
+EXIT 0
diff --git a/imap/src/osdep/dos/drivraux.bat b/imap/src/osdep/dos/drivraux.bat
new file mode 100644
index 00000000..a0b354cd
--- /dev/null
+++ b/imap/src/osdep/dos/drivraux.bat
@@ -0,0 +1,28 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Driver Linkage Generator auxillary for DOS
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 11 October 1989
+REM Last Edited:30 August 2006
+
+ECHO extern DRIVER %1driver; >> LINKAGE.H
+ECHO mail_link (&%1driver); /* link in the %1 driver */ >> LINKAGE.C
diff --git a/imap/src/osdep/dos/dummy.h b/imap/src/osdep/dos/dummy.h
new file mode 100644
index 00000000..32650e06
--- /dev/null
+++ b/imap/src/osdep/dos/dummy.h
@@ -0,0 +1,43 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 9 May 1991
+ * Last Edited: 30 August 2006
+ */
+
+/* Exported function prototypes */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat);
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long scan_contents (DRIVER *dtb,char *name,char *contents,
+ unsigned long csiz,unsigned long fsiz);
+long dummy_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz);
+long dummy_create (MAILSTREAM *stream,char *mailbox);
+long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode);
+long dummy_delete (MAILSTREAM *stream,char *mailbox);
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname);
+char *dummy_file (char *dst,char *name);
+long dummy_canonicalize (char *tmp,char *ref,char *pat);
diff --git a/imap/src/osdep/dos/dummydos.c b/imap/src/osdep/dos/dummydos.c
new file mode 100644
index 00000000..4bf147b8
--- /dev/null
+++ b/imap/src/osdep/dos/dummydos.c
@@ -0,0 +1,689 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines for DOS
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 24 May 1993
+ * Last Edited: 30 August 2006
+ */
+
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include "mail.h"
+#include "osdep.h"
+#include <sys\stat.h>
+#include <dos.h>
+#include "dummy.h"
+#include "misc.h"
+
+/* Function prototypes */
+
+DRIVER *dummy_valid (char *name);
+void *dummy_parameters (long function,void *value);
+void dummy_list_work (MAILSTREAM *stream,char *dir,char *pat,char *contents,
+ long level);
+long dummy_listed (MAILSTREAM *stream,char delimiter,char *name,
+ long attributes,char *contents);
+long dummy_subscribe (MAILSTREAM *stream,char *mailbox);
+MAILSTREAM *dummy_open (MAILSTREAM *stream);
+void dummy_close (MAILSTREAM *stream,long options);
+long dummy_ping (MAILSTREAM *stream);
+void dummy_check (MAILSTREAM *stream);
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options);
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+long dummy_badname (char *tmp,char *s);
+
+/* Dummy routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER dummydriver = {
+ "dummy", /* driver name */
+ DR_LOCAL|DR_MAIL, /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ dummy_valid, /* mailbox is valid for us */
+ dummy_parameters, /* manipulate parameters */
+ dummy_scan, /* scan mailboxes */
+ dummy_list, /* list mailboxes */
+ dummy_lsub, /* list subscribed mailboxes */
+ dummy_subscribe, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ dummy_create, /* create mailbox */
+ dummy_delete, /* delete mailbox */
+ dummy_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ dummy_open, /* open mailbox */
+ dummy_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message structure */
+ NIL, /* fetch header */
+ NIL, /* fetch text */
+ NIL, /* fetch message data */
+ NIL, /* unique identifier */
+ NIL, /* message number from UID */
+ NIL, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ dummy_ping, /* ping mailbox to see if still alive */
+ dummy_check, /* check for new messages */
+ dummy_expunge, /* expunge deleted messages */
+ dummy_copy, /* copy messages to another mailbox */
+ dummy_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+
+ /* prototype stream */
+MAILSTREAM dummyproto = {&dummydriver};
+
+ /* driver parameters */
+static char *file_extension = NIL;
+
+/* Dummy validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *dummy_valid (char *name)
+{
+ char *s,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* must be valid local mailbox */
+ return (name && *name && (*name != '{') &&
+ (s = mailboxfile (tmp,name)) && (!*s || !stat (s,&sbuf))) ?
+ &dummydriver : NIL;
+}
+
+
+/* Dummy manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *dummy_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_EXTENSION:
+ if (file_extension) fs_give ((void **) &file_extension);
+ if (*(char *) value) file_extension = cpystr ((char *) value);
+ case GET_EXTENSION:
+ ret = (void *) file_extension;
+ }
+ return ret;
+}
+
+/* Dummy scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+#define LISTTMPLEN 128
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ char *s,test[LISTTMPLEN],file[LISTTMPLEN];
+ long i = 0;
+ if (!pat || !*pat) { /* empty pattern? */
+ if (dummy_canonicalize (test,ref,"*")) {
+ /* tie off name at root */
+ if (s = strchr (test,'\\')) *++s = '\0';
+ else test[0] = '\0';
+ dummy_listed (stream,'\\',test,LATT_NOINFERIORS,NIL);
+ }
+ }
+ /* get canonical form of name */
+ else if (dummy_canonicalize (test,ref,pat)) {
+ /* found any wildcards? */
+ if (s = strpbrk (test,"%*")) {
+ /* yes, copy name up to that point */
+ strncpy (file,test,(size_t) (i = s - test));
+ file[i] = '\0'; /* tie off */
+ }
+ else strcpy (file,test); /* use just that name then */
+ /* find directory name */
+ if (s = strrchr (file,'\\')) {
+ *++s = '\0'; /* found, tie off at that point */
+ s = file;
+ }
+ /* silly case */
+ else if (file[0] == '#') s = file;
+ /* do the work */
+ dummy_list_work (stream,s,test,contents,0);
+ if (pmatch ("INBOX",test)) /* always an INBOX */
+ dummy_listed (stream,NIL,"INBOX",LATT_NOINFERIORS,contents);
+ }
+}
+
+/* Dummy list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ dummy_scan (stream,ref,pat,NIL);
+}
+
+
+/* Dummy list subscribed mailboxes
+ * Accepts: mail stream
+ * pattern to search
+ */
+
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ void *sdb = NIL;
+ char *s,*t,test[MAILTMPLEN];
+ int showuppers = pat[strlen (pat) - 1] == '%';
+ /* get canonical form of name */
+ if (dummy_canonicalize (test,ref,pat) && (s = sm_read (&sdb))) do
+ if (*s != '{') {
+ if (pmatch_full (s,test,'\\')) {
+ if (pmatch (s,"INBOX")) mm_lsub (stream,NIL,s,LATT_NOINFERIORS);
+ else mm_lsub (stream,'\\',s,NIL);
+ }
+ else while (showuppers && (t = strrchr (s,'\\'))) {
+ *t = '\0'; /* tie off the name */
+ if (pmatch_full (s,test,'\\')) mm_lsub (stream,'\\',s,LATT_NOSELECT);
+ }
+ }
+ while (s = sm_read (&sdb)); /* until no more subscriptions */
+}
+
+
+/* Dummy subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* must be valid local mailbox */
+ if ((s = mailboxfile (tmp,mailbox)) && *s && !stat (s,&sbuf) &&
+ ((sbuf.st_mode & S_IFMT) == S_IFREG)) return sm_subscribe (mailbox);
+ sprintf (tmp,"Can't subscribe %s: not a mailbox",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+}
+
+/* Dummy list mailboxes worker routine
+ * Accepts: mail stream
+ * directory name to search
+ * search pattern
+ * string to scan
+ * search level
+ */
+
+void dummy_list_work (MAILSTREAM *stream,char *dir,char *pat,char *contents,
+ long level)
+{
+ struct find_t f;
+ struct stat sbuf;
+ char *s,tmp[LISTTMPLEN],tmpx[LISTTMPLEN];
+ char *base = (dir && (dir[0] == '\\')) ? NIL : myhomedir ();
+ /* build name */
+ if (base) sprintf (tmpx,"%s\\",base);
+ else tmpx[0] = '\0';
+ if (dir) strcat (tmpx,dir);
+ /* punt if bogus name */
+ if (!mailboxfile (tmp,tmpx)) return;
+ /* make directory wildcard */
+ strcat (tmp,(tmp[strlen (tmp) -1] == '\\') ? "*." : "\\*.");
+ strcat (tmp,file_extension ? file_extension : "*");
+ /* do nothing if can't open directory */
+ if (!_dos_findfirst (tmp,_A_NORMAL|_A_SUBDIR,&f)) {
+ /* list it if at top-level */
+ if (!level && dir && pmatch_full (dir,pat,'\\'))
+ dummy_listed (stream,'\\',dir,LATT_NOSELECT,contents);
+ /* scan directory */
+ if (tmpx[strlen (tmpx) - 1] == '\\') do if (*f.name != '.') {
+ if (base) sprintf (tmpx,"%s\\",base);
+ else tmpx[0] = '\0';
+ if (dir) sprintf (tmpx + strlen (tmpx),"%s%s",dir,f.name);
+ else strcat (tmpx,f.name);
+ if (mailboxfile (tmp,tmpx) && !stat (tmp,&sbuf)) {
+ /* suppress extension */
+ if (file_extension && (s = strchr (f.name,'.'))) *s = '\0';
+ /* now make name we'd return */
+ if (dir) sprintf (tmp,"%s%s",dir,f.name);
+ else strcpy (tmp,f.name);
+ /* only interested in file type */
+ switch (sbuf.st_mode & S_IFMT) {
+ case S_IFDIR: /* directory? */
+ if (pmatch_full (tmp,pat,'\\')) {
+ dummy_listed (stream,'\\',tmp,LATT_NOSELECT,contents);
+ strcat (tmp,"\\"); /* set up for dmatch call */
+ }
+ /* try again with trailing / */
+ else if (pmatch_full (strcat (tmp,"\\"),pat,'\\'))
+ dummy_listed (stream,'\\',tmp,LATT_NOSELECT,contents);
+ if (dmatch (tmp,pat,'\\') &&
+ (level < (long) mail_parameters (NIL,GET_LISTMAXLEVEL,NIL)))
+ dummy_list_work (stream,tmp,pat,contents,level+1);
+ break;
+ case S_IFREG: /* ordinary name */
+ if (pmatch_full (tmp,pat,'\\') && !pmatch ("INBOX",tmp))
+ dummy_listed (stream,'\\',tmp,LATT_NOINFERIORS,contents);
+ break;
+ }
+ }
+ }
+ while (!_dos_findnext (&f));
+ }
+}
+
+/* Mailbox found
+ * Accepts: hierarchy delimiter
+ * mailbox name
+ * attributes
+ * contents to search before calling mm_list()
+ * Returns: T, always
+ */
+
+#define BUFSIZE MAILTMPLEN
+
+long dummy_listed (MAILSTREAM *stream,char delimiter,char *name,
+ long attributes,char *contents)
+{
+ struct stat sbuf;
+ int fd;
+ size_t csiz,ssiz,bsiz;
+ char *buf,tmp[MAILTMPLEN];
+ if (contents) { /* want to search contents? */
+ /* forget it if can't select or open */
+ if ((attributes & LATT_NOSELECT) || !(csiz = strlen (contents)) ||
+ !mailboxfile (tmp,name) || stat (tmp,&sbuf) || (csiz > sbuf.st_size) ||
+ ((fd = open (tmp,O_RDONLY,NIL)) < 0)) return T;
+ /* get buffer including slop */
+ buf = (char *) fs_get (BUFSIZE + (ssiz = 4 * ((csiz / 4) + 1)) + 1);
+ memset (buf,'\0',ssiz); /* no slop area the first time */
+ while (sbuf.st_size) { /* until end of file */
+ read (fd,buf+ssiz,bsiz = min (sbuf.st_size,BUFSIZE));
+ if (search ((unsigned char *) buf,bsiz+ssiz,
+ (unsigned char *) contents,csiz)) break;
+ memcpy (buf,buf+BUFSIZE,ssiz);
+ sbuf.st_size -= bsiz; /* note that we read that much */
+ }
+ fs_give ((void **) &buf); /* flush buffer */
+ close (fd); /* finished with file */
+ if (!sbuf.st_size) return T;/* not found */
+ }
+ /* notify main program */
+ mm_list (stream,delimiter,name,attributes);
+ return T;
+}
+
+/* Dummy create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_create (MAILSTREAM *stream,char *mailbox)
+{
+ char tmp[MAILTMPLEN];
+ return (compare_cstring (mailbox,"INBOX") && mailboxfile (tmp,mailbox)) ?
+ dummy_create_path (stream,tmp,NIL) : dummy_badname (tmp,mailbox);
+}
+
+
+/* Dummy create path
+ * Accepts: mail stream
+ * path name to create
+ * directory mode
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode)
+{
+ struct stat sbuf;
+ char c,*s,tmp[MAILTMPLEN];
+ int fd;
+ long ret = NIL;
+ char *t = strrchr (path,'\\');
+ char *pt = (path[1] == ':') ? path + 2 : path;
+ int wantdir = t && !t[1];
+ if (wantdir) *t = '\0'; /* flush trailing delimiter for directory */
+ /* found superior to this name? */
+ if ((s = strrchr (pt,'\\')) && (s != pt)) {
+ strncpy (tmp,path,(size_t) (s - path));
+ tmp[s - path] = '\0'; /* make directory name for stat */
+ c = *++s; /* tie off in case need to recurse */
+ *s = '\0';
+ /* name doesn't exist, create it */
+ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,path,dirmode)) return NIL;
+ *s = c; /* restore full name */
+ }
+ if (wantdir) { /* want to create directory? */
+ ret = !mkdir (path);
+ *t = '\\'; /* restore directory delimiter */
+ }
+ /* create file */
+ else if ((fd = open (path,O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE)) >= 0)
+ ret = !close (fd); /* close file */
+ if (!ret) { /* error? */
+ sprintf (tmp,"Can't create mailbox node %s: %s",path,strerror (errno));
+ mm_log (tmp,ERROR);
+ }
+ return ret; /* return status */
+}
+
+/* Dummy delete mailbox
+ * Accepts: mail stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_delete (MAILSTREAM *stream,char *mailbox)
+{
+ struct stat sbuf;
+ char *s,tmp[MAILTMPLEN];
+ if (!mailboxfile (tmp,mailbox)) return dummy_badname (tmp,mailbox);
+ /* no trailing \ */
+ if ((s = strrchr (tmp,'\\')) && !s[1]) *s = '\0';
+ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) == S_IFDIR) ?
+ rmdir (tmp) : unlink (tmp)) {
+ sprintf (tmp,"Can't delete mailbox %s: %s",mailbox,strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ return T; /* return success */
+}
+
+
+/* Mail rename mailbox
+ * Accepts: mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ struct stat sbuf;
+ char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN];
+ /* make file name */
+ if (!mailboxfile (file,old)) return dummy_badname (tmp,old);
+ /* no trailing \ allowed */
+ if (!(s = mailboxfile (tmp,newname)) || ((s = strrchr (s,'\\')) && !s[1]))
+ return dummy_badname (tmp,newname);
+ if (s) { /* found superior to destination name? */
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (file,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create (stream,file)) return NIL;
+ *s = c; /* restore full name */
+ }
+ if (rename (file,tmp)) {
+ sprintf (tmp,"Can't rename mailbox %s to %s: %s",old,newname,
+ strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ return LONGT; /* return success */
+}
+
+/* Dummy open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *dummy_open (MAILSTREAM *stream)
+{
+ char tmp[MAILTMPLEN];
+ struct stat sbuf;
+ int fd = -1;
+ /* OP_PROTOTYPE call or silence */
+ if (!stream || stream->silent) return NIL;
+ if (!mailboxfile (tmp,stream->mailbox))
+ sprintf (tmp,"Can't open this name: %.80s",stream->mailbox);
+ else if (compare_cstring (stream->mailbox,"INBOX") &&
+ ((fd = open (tmp,O_RDONLY,NIL)) < 0))
+ sprintf (tmp,"%s: %s",strerror (errno),stream->mailbox);
+ else {
+ if (fd >= 0) { /* if got a file */
+ fstat (fd,&sbuf); /* sniff at its size */
+ close (fd);
+ if (sbuf.st_size) sprintf (tmp,"Not a mailbox: %s",stream->mailbox);
+ else fd = -1; /* a-OK */
+ }
+ if (fd < 0) { /* no file, right? */
+ if (!stream->silent) { /* only if silence not requested */
+ /* say there are 0 messages */
+ mail_exists (stream,(long) 0);
+ mail_recent (stream,(long) 0);
+ stream->uid_validity = time (0);
+ }
+ stream->inbox = T; /* note that it's an INBOX */
+ return stream; /* return success */
+ }
+ }
+ mm_log (tmp,stream->silent ? WARN: ERROR);
+ return NIL; /* always fails */
+}
+
+
+/* Dummy close
+ * Accepts: MAIL stream
+ * options
+ */
+
+void dummy_close (MAILSTREAM *stream,long options)
+{
+ /* return silently */
+}
+
+/* Dummy ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+long dummy_ping (MAILSTREAM *stream)
+{
+ MAILSTREAM *test;
+ /* time to do another test? */
+ if (time (0) >= ((time_t) (stream->gensym + 30))) {
+ /* has mailbox format changed? */
+ if ((test = mail_open (NIL,stream->mailbox,OP_PROTOTYPE)) &&
+ (test->dtb != stream->dtb) &&
+ (test = mail_open (NIL,stream->mailbox,NIL))) {
+ /* preserve some resources */
+ test->original_mailbox = stream->original_mailbox;
+ stream->original_mailbox = NIL;
+ test->sparep = stream->sparep;
+ stream->sparep = NIL;
+ test->sequence = stream->sequence;
+ mail_close ((MAILSTREAM *) /* flush resources used by dummy stream */
+ memcpy (fs_get (sizeof (MAILSTREAM)),stream,
+ sizeof (MAILSTREAM)));
+ /* swap the streams */
+ memcpy (stream,test,sizeof (MAILSTREAM));
+ fs_give ((void **) &test);/* flush test now that copied */
+ /* make sure application knows */
+ mail_exists (stream,stream->recent = stream->nmsgs);
+ }
+ /* still hasn't changed */
+ else stream->gensym = time (0);
+ }
+ return T;
+}
+
+
+/* Dummy check mailbox
+ * Accepts: MAIL stream
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+void dummy_check (MAILSTREAM *stream)
+{
+ dummy_ping (stream); /* invoke ping */
+}
+
+
+/* Dummy expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ return LONGT;
+}
+
+/* Dummy copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * options
+ * Returns: T if copy successful, else NIL
+ */
+
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) fatal ("Impossible dummy_copy");
+ return NIL;
+}
+
+
+/* Dummy append message string
+ * Accepts: mail stream
+ * destination mailbox
+ * append callback function
+ * data for callback
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd = -1;
+ int e;
+ char tmp[MAILTMPLEN];
+ MAILSTREAM *ts = default_proto (T);
+ if (compare_cstring (mailbox,"INBOX") && mailboxfile (tmp,mailbox) &&
+ ((fd = open (tmp,O_RDONLY,NIL)) < 0)) {
+ if ((e = errno) == ENOENT) /* failed, was it no such file? */
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",
+ (long) NIL);
+ sprintf (tmp,"%s: %s",strerror (e),mailbox);
+ mm_log (tmp,ERROR); /* pass up error */
+ return NIL; /* always fails */
+ }
+ if (fd >= 0) { /* found file? */
+ fstat (fd,&sbuf); /* get its size */
+ close (fd); /* toss out the fd */
+ if (sbuf.st_size) ts = NIL; /* non-empty file? */
+ }
+ if (ts) return (*ts->dtb->append) (stream,mailbox,af,data);
+ sprintf (tmp,"Indeterminate mailbox format: %s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+}
+
+/* Return bad file name error message
+ * Accepts: temporary buffer
+ * file name
+ * Returns: long NIL always
+ */
+
+long dummy_badname (char *tmp,char *s)
+{
+ sprintf (tmp,"Invalid mailbox name: %s",s);
+ mm_log (tmp,ERROR);
+ return (long) NIL;
+}
+
+
+/* Dummy canonicalize name
+ * Accepts: buffer to write name
+ * reference
+ * pattern
+ * Returns: T if success, NIL if failure
+ */
+
+long dummy_canonicalize (char *tmp,char *ref,char *pat)
+{
+ unsigned long i;
+ char *s,dev[4];
+ /* initially no device */
+ dev[0] = dev[1] = dev[2] = dev[3] = '\0';
+ if (ref) switch (*ref) { /* preliminary reference check */
+ case '{': /* remote names not allowed */
+ return NIL; /* disallowed */
+ case '\0': /* empty reference string */
+ break;
+ default: /* all other names */
+ if (ref[1] == ':') { /* start with device name? */
+ dev[0] = *ref++; dev[1] = *ref++;
+ }
+ break;
+ }
+ if (pat[1] == ':') { /* device name in pattern? */
+ dev[0] = *pat++; dev[1] = *pat++;
+ ref = NIL; /* ignore reference */
+ }
+ switch (*pat) {
+ case '#': /* namespace names */
+ if (mailboxfile (tmp,pat)) strcpy (tmp,pat);
+ else return NIL; /* unknown namespace */
+ break;
+ case '{': /* remote names not allowed */
+ return NIL;
+ case '\\': /* rooted name */
+ ref = NIL; /* ignore reference */
+ break;
+ }
+ /* make sure device names are rooted */
+ if (dev[0] && (*(ref ? ref : pat) != '\\')) dev[2] = '\\';
+ /* build name */
+ sprintf (tmp,"%s%s%s",dev,ref ? ref : "",pat);
+ ucase (tmp); /* force upper case */
+ /* count wildcards */
+ for (i = 0, s = tmp; *s; *s++) if ((*s == '*') || (*s == '%')) ++i;
+ if (i > MAXWILDCARDS) { /* ridiculous wildcarding? */
+ MM_LOG ("Excessive wildcards in LIST/LSUB",ERROR);
+ return NIL;
+ }
+ return T;
+}
diff --git a/imap/src/osdep/dos/env_dos.c b/imap/src/osdep/dos/env_dos.c
new file mode 100644
index 00000000..152dd322
--- /dev/null
+++ b/imap/src/osdep/dos/env_dos.c
@@ -0,0 +1,300 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: DOS environment routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+static char *myLocalHost = NIL; /* local host name */
+static char *myClientAddr = NIL;/* client host address */
+static char *myClientHost = NIL;/* client host name */
+static char *myServerAddr = NIL;/* server host address */
+static char *myServerHost = NIL;/* server host name */
+static char *myHomeDir = NIL; /* home directory name */
+static char *myNewsrc = NIL; /* newsrc file name */
+static long list_max_level = 5; /* maximum level of list recursion */
+static short no822tztext = NIL; /* disable RFC [2]822 timezone text */
+ /* home namespace */
+static NAMESPACE nshome = {"",'\\',NIL,NIL};
+ /* namespace list */
+static NAMESPACE *nslist[3] = {&nshome,NIL,NIL};
+
+#include "write.c" /* include safe writing routines */
+#include "pmatch.c" /* include wildcard pattern matcher */
+
+
+/* Dummy definitions to prevent errors */
+
+#define server_login(user,pass,authuser,argc,argv) NIL
+#define authserver_login(user,authuser,argc,argv) NIL
+#define myusername() ""
+#define MD5ENABLE "\\.nosuch.."
+
+
+/* Get all authenticators */
+
+#include "auths.c"
+
+/* Environment manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *env_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_NAMESPACE:
+ ret = (void *) nslist;
+ break;
+ case SET_HOMEDIR:
+ myHomeDir = cpystr ((char *) value);
+ case GET_HOMEDIR:
+ ret = (void *) myHomeDir;
+ break;
+ case SET_LOCALHOST:
+ myLocalHost = cpystr ((char *) value);
+ case GET_LOCALHOST:
+ ret = (void *) myLocalHost;
+ break;
+ case SET_NEWSRC:
+ if (myNewsrc) fs_give ((void **) &myNewsrc);
+ myNewsrc = cpystr ((char *) value);
+ case GET_NEWSRC:
+ if (!myNewsrc) { /* set news file name if not defined */
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"%s\\NEWSRC",myhomedir ());
+ myNewsrc = cpystr (tmp);
+ }
+ ret = (void *) myNewsrc;
+ break;
+ case SET_LISTMAXLEVEL:
+ list_max_level = (long) value;
+ case GET_LISTMAXLEVEL:
+ ret = (void *) list_max_level;
+ break;
+ case SET_DISABLE822TZTEXT:
+ no822tztext = value ? T : NIL;
+ case GET_DISABLE822TZTEXT:
+ ret = (void *) (no822tztext ? VOIDT : NIL);
+ break;
+ }
+ return ret;
+}
+
+/* Write current time
+ * Accepts: destination string
+ * optional format of day-of-week prefix
+ * format of date and time
+ * flag whether to append symbolic timezone
+ */
+
+static void do_date (char *date,char *prefix,char *fmt,int suffix)
+{
+ time_t tn = time (0);
+ struct tm *t = gmtime (&tn);
+ int zone = t->tm_hour * 60 + t->tm_min;
+ int julian = t->tm_yday;
+ t = localtime (&tn); /* get local time now */
+ /* minus UTC minutes since midnight */
+ zone = t->tm_hour * 60 + t->tm_min - zone;
+ /* julian can be one of:
+ * 36x local time is December 31, UTC is January 1, offset -24 hours
+ * 1 local time is 1 day ahead of UTC, offset +24 hours
+ * 0 local time is same day as UTC, no offset
+ * -1 local time is 1 day behind UTC, offset -24 hours
+ * -36x local time is January 1, UTC is December 31, offset +24 hours
+ */
+ if (julian = t->tm_yday -julian)
+ zone += ((julian < 0) == (abs (julian) == 1)) ? -24*60 : 24*60;
+ if (prefix) { /* want day of week? */
+ sprintf (date,prefix,days[t->tm_wday]);
+ date += strlen (date); /* make next sprintf append */
+ }
+ /* output the date */
+ sprintf (date,fmt,t->tm_mday,months[t->tm_mon],t->tm_year+1900,
+ t->tm_hour,t->tm_min,t->tm_sec,zone/60,abs (zone) % 60);
+ if (suffix) { /* append timezone suffix if desired */
+ tzset (); /* get timezone from TZ environment stuff */
+ sprintf (date + strlen (date)," (%.50s)",
+ tzname[daylight ? (((struct tm *) t)->tm_isdst > 0) : 0]);
+ }
+}
+
+
+/* Write current time in RFC 822 format
+ * Accepts: destination string
+ */
+
+void rfc822_date (char *date)
+{
+ do_date (date,"%s, ","%d %s %d %02d:%02d:%02d %+03d%02d",
+ no822tztext ? NIL : T);
+}
+
+
+/* Write current time in internal format
+ * Accepts: destination string
+ */
+
+void internal_date (char *date)
+{
+ do_date (date,NIL,"%02d-%s-%d %02d:%02d:%02d %+03d%02d",NIL);
+}
+
+/* Return my home directory name
+ * Returns: my home directory name
+ */
+
+char *myhomedir ()
+{
+ int i;
+ char *s;
+ if (!myHomeDir) { /* get home directory name if not yet known */
+ i = strlen (myHomeDir = cpystr ((s = getenv ("HOME")) ? s : ""));
+ if (i && ((myHomeDir[i-1] == '\\') || (myHomeDir[i-1]=='/')))
+ myHomeDir[i-1] = '\0'; /* tie off trailing directory delimiter */
+ }
+ return myHomeDir;
+}
+
+
+/* Return mailbox file name
+ * Accepts: destination buffer
+ * mailbox name
+ * Returns: file name
+ */
+
+char *mailboxfile (char *dst,char *name)
+{
+ char *s;
+ char *ext = (char *) mail_parameters (NIL,GET_EXTENSION,NIL);
+ /* forbid extraneous extensions */
+ if ((s = strchr ((s = strrchr (name,'\\')) ? s : name,'.')) &&
+ ((ext = (char *) mail_parameters (NIL,GET_EXTENSION,NIL)) ||
+ strchr (s+1,'.'))) return NIL;
+ /* absolute path name? */
+ if ((*name == '\\') || (name[1] == ':')) strcpy (dst,name);
+ else sprintf (dst,"%s\\%s",myhomedir (),name);
+ if (ext) sprintf (dst + strlen (dst),".%s",ext);
+ return ucase (dst);
+}
+
+
+/* Determine default prototype stream to user
+ * Accepts: type (NIL for create, T for append)
+ * Returns: default prototype stream
+ */
+
+MAILSTREAM *default_proto (long type)
+{
+ extern MAILSTREAM DEFAULTPROTO;
+ return &DEFAULTPROTO; /* return default driver's prototype */
+}
+
+/* Global data */
+
+static unsigned rndm = 0; /* initial `random' number */
+
+
+/* Return random number
+ */
+
+long random ()
+{
+ if (!rndm) srand (rndm = (unsigned) time (0L));
+ return (long) rand ();
+}
+
+/* Default mailgets routine on DOS
+ * Accepts: readin function pointer
+ * stream to use
+ * number of bytes
+ * identifier data
+ * Returns: string read in, truncated if necessary
+ *
+ * This is a sample mailgets routine. It simply truncates any data larger
+ * than 63K. On most systems, you generally don't use a mailgets
+ * routine at all, but on DOS it's required to prevent the application from
+ * crashing.
+ */
+
+static char *dos_gets_buf = NIL;
+
+char *dos_default_gets (readfn_t f,void *stream,unsigned long size,
+ GETS_DATA *md)
+{
+ readprogress_t *rp = mail_parameters (NIL,GET_READPROGRESS,NIL);
+ char *ret,tmp[MAILTMPLEN+1];
+ unsigned long i,j,dsc,rdi = 0;
+ unsigned long dos_max = 63 * 1024;
+ if (!dos_gets_buf) /* one-time initialization */
+ dos_gets_buf = (char *) fs_get ((size_t) dos_max + 1);
+ ret = (md->flags & MG_COPY) ?
+ ((char *) fs_get ((size_t) size + 1)) : dos_gets_buf;
+ if (size > dos_max) {
+ sprintf (tmp,"Mailbox %s, %s %lu[%.80s], %lu octets truncated to %ld",
+ md->stream->mailbox,(md->flags & MG_UID) ? "UID" : "#",
+ md->msgno,md->what,size,(long) dos_max);
+ mm_log (tmp,WARN); /* warn user */
+ dsc = size - dos_max; /* number of bytes to discard */
+ size = dos_max; /* maximum length string we can read */
+ }
+ else dsc = 0; /* nothing to discard */
+ dos_gets_buf[size] = '\0'; /* tie off string */
+ if (rp) for (i = size; j = min ((long) MAILTMPLEN,(long) i); i -= j) {
+ (*f) (stream,j,ret + rdi);
+ (*rp) (md,rdi += j);
+ }
+ else (*f) (stream,size,dos_gets_buf);
+ /* toss out everything after that */
+ for (i = dsc; j = min ((long) MAILTMPLEN,(long) i); i -= j) {
+ (*f) (stream,j,tmp);
+ if (rp) (*rp) (md,rdi += j);
+ }
+ return ret;
+}
+
+/* Emulator for BSD syslog() routine
+ * Accepts: priority
+ * message
+ * parameters
+ */
+
+void syslog (int priority,const char *message,...)
+{
+}
+
+
+/* Emulator for BSD openlog() routine
+ * Accepts: identity
+ * options
+ * facility
+ */
+
+void openlog (const char *ident,int logopt,int facility)
+{
+}
diff --git a/imap/src/osdep/dos/env_dos.h b/imap/src/osdep/dos/env_dos.h
new file mode 100644
index 00000000..bb0e8c0a
--- /dev/null
+++ b/imap/src/osdep/dos/env_dos.h
@@ -0,0 +1,68 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: DOS environment routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+#define SUBSCRIPTIONFILE(t) sprintf (t,"%s/MAILBOX.LST",myhomedir ())
+#define SUBSCRIPTIONTEMP(t) sprintf (t,"%s/MAILBOX.TMP",myhomedir ())
+
+#define L_SET SEEK_SET
+
+/* Function prototypes */
+
+#include "env.h"
+
+char *dos_default_gets (readfn_t f,void *stream,unsigned long size,
+ GETS_DATA *md);
+long safe_write (int fd,char *buf,long nbytes);
+long random ();
+#if _MSC_VER < 700
+#define getpid random
+#endif
+
+
+/* syslog() emulation */
+
+#define LOG_MAIL (2<<3) /* mail system */
+#define LOG_DAEMON (3<<3) /* system daemons */
+#define LOG_AUTH (4<<3) /* security/authorization messages */
+#define LOG_EMERG 0 /* system is unusable */
+#define LOG_ALERT 1 /* action must be taken immediately */
+#define LOG_CRIT 2 /* critical conditions */
+#define LOG_ERR 3 /* error conditions */
+#define LOG_WARNING 4 /* warning conditions */
+#define LOG_NOTICE 5 /* normal but signification condition */
+#define LOG_INFO 6 /* informational */
+#define LOG_DEBUG 7 /* debug-level messages */
+#define LOG_PID 0x01 /* log the pid with each message */
+#define LOG_CONS 0x02 /* log on the console if errors in sending */
+#define LOG_ODELAY 0x04 /* delay open until syslog() is called */
+#define LOG_NDELAY 0x08 /* don't delay open */
+#define LOG_NOWAIT 0x10 /* if forking to log on console, don't wait() */
+
+void openlog (const char *ident,int logopt,int facility);
+void syslog (int priority,const char *message,...);
diff --git a/imap/src/osdep/dos/fdstring.c b/imap/src/osdep/dos/fdstring.c
new file mode 100644
index 00000000..7a491f7d
--- /dev/null
+++ b/imap/src/osdep/dos/fdstring.c
@@ -0,0 +1,99 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: File descriptor string routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 April 1997
+ * Last Edited: 4 April 2007
+ */
+
+#include "mail.h"
+#include "osdep.h"
+#include "misc.h"
+#include "fdstring.h"
+
+/* String driver for fd stringstructs */
+
+static void fd_string_init (STRING *s,void *data,unsigned long size);
+static char fd_string_next (STRING *s);
+static void fd_string_setpos (STRING *s,unsigned long i);
+
+STRINGDRIVER fd_string = {
+ fd_string_init, /* initialize string structure */
+ fd_string_next, /* get next byte in string structure */
+ fd_string_setpos /* set position in string structure */
+};
+
+
+/* Initialize string structure for fd stringstruct
+ * Accepts: string structure
+ * pointer to string
+ * size of string
+ */
+
+static void fd_string_init (STRING *s,void *data,unsigned long size)
+{
+ FDDATA *d = (FDDATA *) data;
+ /* note fd */
+ s->data = (void *) (unsigned long) d->fd;
+ s->data1 = d->pos; /* note file offset */
+ s->size = size; /* note size */
+ s->curpos = s->chunk = d->chunk;
+ s->chunksize = (unsigned long) d->chunksize;
+ s->offset = 0; /* initial position */
+ /* and size of data */
+ s->cursize = min (s->chunksize,size);
+ /* move to that position in the file */
+ lseek (d->fd,d->pos,L_SET);
+ read (d->fd,s->chunk,(size_t) s->cursize);
+}
+
+/* Get next character from fd stringstruct
+ * Accepts: string structure
+ * Returns: character, string structure chunk refreshed
+ */
+
+static char fd_string_next (STRING *s)
+{
+ char c = *s->curpos++; /* get next byte */
+ SETPOS (s,GETPOS (s)); /* move to next chunk */
+ return c; /* return the byte */
+}
+
+
+/* Set string pointer position for fd stringstruct
+ * Accepts: string structure
+ * new position
+ */
+
+static void fd_string_setpos (STRING *s,unsigned long i)
+{
+ if (i > s->size) i = s->size; /* don't permit setting beyond EOF */
+ s->offset = i; /* set new offset */
+ s->curpos = s->chunk; /* reset position */
+ /* set size of data */
+ if (s->cursize = min (s->chunksize,SIZE (s))) {
+ /* move to that position in the file */
+ lseek ((long) s->data,s->data1 + s->offset,L_SET);
+ read ((long) s->data,s->curpos,(size_t) s->cursize);
+ }
+}
diff --git a/imap/src/osdep/dos/fdstring.h b/imap/src/osdep/dos/fdstring.h
new file mode 100644
index 00000000..d0a021bf
--- /dev/null
+++ b/imap/src/osdep/dos/fdstring.h
@@ -0,0 +1,39 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: File descriptor string routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 April 1997
+ * Last Edited: 30 August 2006
+ */
+
+/* Driver-dependent data passed to init method */
+
+typedef struct fd_data {
+ int fd; /* file descriptor */
+ unsigned long pos; /* initial position */
+ char *chunk; /* I/O buffer chunk */
+ unsigned long chunksize; /* I/O buffer chunk length */
+} FDDATA;
+
+
+extern STRINGDRIVER fd_string;
diff --git a/imap/src/osdep/dos/fs_dos.c b/imap/src/osdep/dos/fs_dos.c
new file mode 100644
index 00000000..03908328
--- /dev/null
+++ b/imap/src/osdep/dos/fs_dos.c
@@ -0,0 +1,62 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Free storage management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Get a block of free storage
+ * Accepts: size of desired block
+ * Returns: free storage block
+ */
+
+void *fs_get (size_t size)
+{
+ void *block = malloc (size ? size : (size_t) 1);
+ if (!block) fatal ("Out of memory");
+ return (block);
+}
+
+
+/* Resize a block of free storage
+ * Accepts: ** pointer to current block
+ * new size
+ */
+
+void fs_resize (void **block,size_t size)
+{
+ if (!(*block = realloc (*block,size ? size : (size_t) 1)))
+ fatal ("Can't resize memory");
+}
+
+
+/* Return a block of free storage
+ * Accepts: ** pointer to free storage block
+ */
+
+void fs_give (void **block)
+{
+ free (*block);
+ *block = NIL;
+}
diff --git a/imap/src/osdep/dos/ftl_dos.c b/imap/src/osdep/dos/ftl_dos.c
new file mode 100644
index 00000000..9e65ef55
--- /dev/null
+++ b/imap/src/osdep/dos/ftl_dos.c
@@ -0,0 +1,38 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: DOS/VMS/TOPS-20 crash management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Report a fatal error
+ * Accepts: string to output
+ */
+
+void fatal (char *string)
+{
+ mm_fatal (string); /* pass up the string */
+ abort (); /* die horribly */
+}
diff --git a/imap/src/osdep/dos/makefile b/imap/src/osdep/dos/makefile
new file mode 100644
index 00000000..40cf2276
--- /dev/null
+++ b/imap/src/osdep/dos/makefile
@@ -0,0 +1,98 @@
+# ========================================================================
+# Copyright 1988-2007 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: Portable C client makefile -- MS-DOS version
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 11 May 1989
+# Last Edited: 23 May 2007
+
+
+OS = wsk
+EXTRAAUTHENTICATORS=
+DEFAULTAUTHENTICATORS= ext md5 pla log
+EXTRADRIVERS =
+DRIVERS = imap nntp pop3 mtx bezerk
+DEFAULTDRIVER = mtx
+CFLAGS= -AL /DCHUNKSIZE=4096 -nologo $(EXTRACFLAGS)
+CC = cl
+
+all: mtest.exe
+
+.c.obj:
+ $(CC) -c $(CFLAGS) $*.c
+
+osdep.h: os_$(OS).h
+ copy os_$(OS).h osdep.h
+ drivers $(EXTRADRIVERS) $(DRIVERS) dummy
+ mkauths $(EXTRAAUTHENTICATORS) $(DEFAULTAUTHENTICATORS)
+ ECHO #define DEFAULTPROTO $(DEFAULTDRIVER)proto >> LINKAGE.H
+ ECHO if (!mail_parameters (NIL,GET_GETS)) >> LINKAGE.C
+ ECHO mail_parameters (NIL,SET_GETS,(void *) dos_default_gets); >> LINKAGE.C
+ ECHO mail_versioncheck (CCLIENTVERSION); >> LINKAGE.C
+
+mtest.obj: mail.h smtp.h misc.h osdep.h mtest.c
+
+mail.obj: mail.h misc.h osdep.h mail.c
+
+misc.obj: mail.h misc.h misc.c
+
+fdstring.obj: mail.h misc.h osdep.h fdstring.h fdstring.c
+
+flstring.obj: mail.h misc.h osdep.h flstring.h flstring.c
+
+netmsg.obj: mail.h misc.h netmsg.h osdep.h netmsg.c
+
+newsrc.obj: mail.h misc.h newsrc.h osdep.h newsrc.c
+
+rfc822.obj: mail.h rfc822.h misc.h rfc822.c
+
+smanager.obj: mail.h misc.h smanager.c
+
+utf8.obj: mail.h misc.h osdep.h utf8.h
+
+utf8aux.obj: mail.h misc.h osdep.h utf8.h
+
+imap4r1.obj: mail.h imap4r1.h misc.h osdep.h imap4r1.c
+
+nntp.obj: mail.h nntp.h smtp.h rfc822.h misc.h osdep.h nntp.c
+
+pop3.obj: mail.h rfc822.h misc.h osdep.h pop3.c
+
+smtp.obj: mail.h smtp.h rfc822.h misc.h osdep.h smtp.c
+
+os_$(OS).obj: mail.h osdep.h env_dos.h fs.h ftl.h nl.h tcp.h \
+ os_$(OS).c fs_dos.c ftl_dos.c nl_dos.c env_dos.c pmatch.c write.c
+
+mtxdos.obj: mail.h misc.h osdep.h mtxdos.c
+
+bezrkdos.obj: mail.h misc.h osdep.h bezrkdos.c
+
+dummydos.obj: mail.h dummy.h misc.h osdep.h dummydos.c
+
+cclient.lib: mail.obj misc.obj fdstring.obj flstring.obj netmsg.obj \
+ newsrc.obj rfc822.obj smanager.obj utf8.obj utf8aux.obj \
+ imap4r1.obj nntp.obj pop3.obj smtp.obj os_$(OS).obj \
+ mtxdos.obj bezrkdos.obj dummydos.obj
+ erase cclient.lib
+ lib cclient +mail+misc+fdstring+flstring+netmsg+newsrc+rfc822+smanager+utf8+utf8aux+imap4r1+nntp+pop3+smtp+os_$(OS)+mtxdos+bezrkdos+dummydos;
+
+mtest.exe: cclient.lib mtest.obj
+ mtest$(OS)
diff --git a/imap/src/osdep/dos/mkautaux.bat b/imap/src/osdep/dos/mkautaux.bat
new file mode 100644
index 00000000..5d24d83b
--- /dev/null
+++ b/imap/src/osdep/dos/mkautaux.bat
@@ -0,0 +1,29 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Authenticator Linkage Generator auxillary for DOS
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 7 December 1995
+REM Last Edited:30 August 2006
+
+ECHO extern AUTHENTICATOR auth_%1; >> LINKAGE.H
+ECHO auth_link (&auth_%1); /* link in the %1 authenticator */ >> LINKAGE.C
+ECHO #include "auth_%1.c" >> AUTHS.C
diff --git a/imap/src/osdep/dos/mkauths.bat b/imap/src/osdep/dos/mkauths.bat
new file mode 100644
index 00000000..d8c5e360
--- /dev/null
+++ b/imap/src/osdep/dos/mkauths.bat
@@ -0,0 +1,33 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Authenticator Linkage Generator for DOS and Windows
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 6 December 1995
+REM Last Edited:30 August 2006
+
+REM Erase old authenticators list
+IF EXIST AUTHS.C DEL AUTHS.C
+
+REM Now define the new list
+FOR %%D IN (%1 %2 %3 %4 %5 %6 %7 %8 %9) DO CALL MKAUTAUX %%D
+
+EXIT 0
diff --git a/imap/src/osdep/dos/mtestdbw.bat b/imap/src/osdep/dos/mtestdbw.bat
new file mode 100644
index 00000000..95452a01
--- /dev/null
+++ b/imap/src/osdep/dos/mtestdbw.bat
@@ -0,0 +1,27 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Portable C client makefile -- MS-DOS B&W link
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 26 June 1994
+REM Last Edited:30 August 2006
+
+link /NOI /stack:32767 mtest.obj,mtest.exe,,cclient.lib llbwtcp.lib llibce.lib
diff --git a/imap/src/osdep/dos/mtestdnf.bat b/imap/src/osdep/dos/mtestdnf.bat
new file mode 100644
index 00000000..451bdda3
--- /dev/null
+++ b/imap/src/osdep/dos/mtestdnf.bat
@@ -0,0 +1,27 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Portable C client makefile -- MS-DOS PC-NFS link
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 26 June 1994
+REM Last Edited:30 August 2006
+
+link /NOI /stack:32767 mtest.obj,mtest.exe,,cclient.lib ltklib.lib
diff --git a/imap/src/osdep/dos/mtestdnv.bat b/imap/src/osdep/dos/mtestdnv.bat
new file mode 100644
index 00000000..2b444e18
--- /dev/null
+++ b/imap/src/osdep/dos/mtestdnv.bat
@@ -0,0 +1,27 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Portable C client makefile -- MS-DOS Novell link
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 26 June 1994
+REM Last Edited:30 August 2006
+
+link /NOI /stack:32767 mtest.obj,mtest.exe,,cclient.lib llibsock.lib
diff --git a/imap/src/osdep/dos/mtestdpc.bat b/imap/src/osdep/dos/mtestdpc.bat
new file mode 100644
index 00000000..ca131181
--- /dev/null
+++ b/imap/src/osdep/dos/mtestdpc.bat
@@ -0,0 +1,27 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Portable C client makefile -- MS-DOS PC/TCP link
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 26 June 1994
+REM Last Edited:30 August 2006
+
+link /NOI /stack:32767 mtest.obj,mtest.exe,,cclient.lib lsocket.lib lnetlib.lib lpc.lib lconfig.lib llibce.lib;
diff --git a/imap/src/osdep/dos/mtestdwa.bat b/imap/src/osdep/dos/mtestdwa.bat
new file mode 100644
index 00000000..3d26146f
--- /dev/null
+++ b/imap/src/osdep/dos/mtestdwa.bat
@@ -0,0 +1,27 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Portable C client makefile -- MS-DOS Waterloo link
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 26 June 1994
+REM Last Edited:30 August 2006
+
+link /NOI /stack:32767 mtest.obj,mtest.exe,,cclient.lib wattcplg.lib
diff --git a/imap/src/osdep/dos/mtestwsk.bat b/imap/src/osdep/dos/mtestwsk.bat
new file mode 100644
index 00000000..5d8922e3
--- /dev/null
+++ b/imap/src/osdep/dos/mtestwsk.bat
@@ -0,0 +1,27 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Portable C client makefile -- MS-DOS Winsock link
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 26 June 1994
+REM Last Edited:30 August 2006
+
+link /NOD:llibce mtest.obj,mtest.exe,,cclient.lib winsock.lib llibcewq.lib libw.lib, mtest
diff --git a/imap/src/osdep/dos/mtxdos.c b/imap/src/osdep/dos/mtxdos.c
new file mode 100644
index 00000000..ad969267
--- /dev/null
+++ b/imap/src/osdep/dos/mtxdos.c
@@ -0,0 +1,875 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MTX mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 24 June 1992
+ * Last Edited: 30 August 2006
+ */
+
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include "mail.h"
+#include "osdep.h"
+#include <time.h>
+#include <sys\stat.h>
+#include <dos.h>
+#include "rfc822.h"
+#include "dummy.h"
+#include "misc.h"
+#include "fdstring.h"
+
+/* MTX I/O stream local data */
+
+typedef struct mtx_local {
+ int fd; /* file descriptor for I/O */
+ off_t filesize; /* file size parsed */
+ unsigned char *buf; /* temporary buffer */
+} MTXLOCAL;
+
+
+/* Drive-dependent data passed to init method */
+
+typedef struct mtx_data {
+ int fd; /* file data */
+ unsigned long pos; /* initial position */
+} MTXDATA;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MTXLOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *mtx_valid (char *name);
+long mtx_isvalid (char *name,char *tmp);
+void *mtx_parameters (long function,void *value);
+void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void mtx_list (MAILSTREAM *stream,char *ref,char *pat);
+void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long mtx_create (MAILSTREAM *stream,char *mailbox);
+long mtx_delete (MAILSTREAM *stream,char *mailbox);
+long mtx_rename (MAILSTREAM *stream,char *old,char *newname);
+MAILSTREAM *mtx_open (MAILSTREAM *stream);
+void mtx_close (MAILSTREAM *stream,long options);
+char *mtx_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long mtx_ping (MAILSTREAM *stream);
+void mtx_check (MAILSTREAM *stream);
+long mtx_expunge (MAILSTREAM *stream,char *sequence,long options);
+long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+char *mtx_file (char *dst,char *name);
+long mtx_badname (char *tmp,char *s);
+long mtx_parse (MAILSTREAM *stream);
+void mtx_update_status (MAILSTREAM *stream,unsigned long msgno);
+unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size);
+
+/* MTX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mtxdriver = {
+ "mtx", /* driver name */
+ /* driver flags */
+ DR_LOCAL|DR_MAIL|DR_LOWMEM|DR_CRLF|DR_NOSTICKY,
+ (DRIVER *) NIL, /* next driver */
+ mtx_valid, /* mailbox is valid for us */
+ mtx_parameters, /* manipulate parameters */
+ mtx_scan, /* scan mailboxes */
+ mtx_list, /* list mailboxes */
+ mtx_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ mtx_create, /* create mailbox */
+ mtx_delete, /* delete mailbox */
+ mtx_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ mtx_open, /* open mailbox */
+ mtx_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mtx_header, /* fetch message header */
+ mtx_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ NIL, /* modify flags */
+ mtx_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mtx_ping, /* ping mailbox to see if still alive */
+ mtx_check, /* check for new messages */
+ mtx_expunge, /* expunge deleted messages */
+ mtx_copy, /* copy messages to another mailbox */
+ mtx_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mtxproto = {&mtxdriver};
+
+/* MTX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mtx_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return mtx_isvalid (name,tmp) ? &mtxdriver : (DRIVER *) NIL;
+}
+
+
+/* MTX mail test for valid mailbox
+ * Accepts: mailbox name
+ * Returns: T if valid, NIL otherwise
+ */
+
+long mtx_isvalid (char *name,char *tmp)
+{
+ int fd;
+ long ret = NIL;
+ char *s;
+ struct stat sbuf;
+ errno = EINVAL; /* assume invalid argument */
+ /* if file, get its status */
+ if ((*name != '{') && mailboxfile (tmp,name) && !stat (tmp,&sbuf)) {
+ if (!sbuf.st_size)errno = 0;/* empty file */
+ else if ((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) >= 0) {
+ memset (tmp,'\0',MAILTMPLEN);
+ if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\015')) &&
+ (s[1] == '\012')) { /* valid format? */
+ *s = '\0'; /* tie off header */
+ /* must begin with dd-mmm-yy" */
+ ret = (((tmp[2] == '-' && tmp[6] == '-') ||
+ (tmp[1] == '-' && tmp[5] == '-')) &&
+ (s = strchr (tmp+18,',')) && strchr (s+2,';')) ? T : NIL;
+ }
+ else errno = -1; /* bogus format */
+ close (fd); /* close the file */
+ }
+ }
+ /* in case INBOX but not mtx format */
+ else if ((errno == ENOENT) && ((name[0] == 'I') || (name[0] == 'i')) &&
+ ((name[1] == 'N') || (name[1] == 'n')) &&
+ ((name[2] == 'B') || (name[2] == 'b')) &&
+ ((name[3] == 'O') || (name[3] == 'o')) &&
+ ((name[4] == 'X') || (name[4] == 'x')) && !name[5]) errno = -1;
+ return ret; /* return what we should */
+}
+
+
+/* MTX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mtx_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+/* MTX mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* MTX mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mtx_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (stream,ref,pat);
+}
+
+
+/* MTX mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (stream,ref,pat);
+}
+
+/* MTX mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mtx_create (MAILSTREAM *stream,char *mailbox)
+{
+ return dummy_create (stream,mailbox);
+}
+
+
+/* MTX mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mtx_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return dummy_delete (stream,mailbox);
+}
+
+
+/* MTX mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long mtx_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ return dummy_rename (stream,old,newname);
+}
+
+/* MTX mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mtx_open (MAILSTREAM *stream)
+{
+ long i;
+ int fd;
+ char *s;
+ char tmp[MAILTMPLEN];
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &mtxproto;
+ if (stream->local) fatal ("mtx recycle stream");
+ if (!mailboxfile (tmp,stream->mailbox))
+ return (MAILSTREAM *) mtx_badname (tmp,stream->mailbox);
+ /* open, possibly creating INBOX */
+ if (((fd = open (tmp,O_BINARY|(stream->rdonly ? O_RDONLY:O_RDWR),NIL)) < 0)&&
+ (compare_cstring (stream->mailbox,"INBOX") ||
+ ((fd = open (tmp,O_BINARY|O_RDWR|O_CREAT|O_EXCL,S_IREAD|S_IWRITE))<0))){
+ sprintf (tmp,"Can't open mailbox: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ stream->local = fs_get (sizeof (MTXLOCAL));
+ /* canonicalize the stream mailbox name */
+ fs_give ((void **) &stream->mailbox);
+ if (s = strchr ((s = strrchr (tmp,'\\')) ? s : tmp,'.')) *s = '\0';
+ stream->mailbox = cpystr (tmp);
+ LOCAL->fd = fd; /* note the file */
+ LOCAL->filesize = 0; /* initialize parsed file size */
+ LOCAL->buf = NIL; /* initially no local buffer */
+ stream->sequence++; /* bump sequence number */
+ stream->uid_validity = time (0);
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ if (!mtx_ping (stream)) return NIL;
+ if (!stream->nmsgs) mm_log ("Mailbox is empty",(long) NIL);
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft =
+ stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ return stream; /* return stream to caller */
+}
+
+/* MTX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mtx_close (MAILSTREAM *stream,long options)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T;
+ if (options & CL_EXPUNGE) mtx_expunge (stream,NIL,NIL);
+ close (LOCAL->fd); /* close the local file */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* MTX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *mtx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags)
+{
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ /* get to header position */
+ lseek (LOCAL->fd,mtx_hdrpos (stream,msgno,length),L_SET);
+ /* is buffer big enough? */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((size_t) *length + 1);
+ LOCAL->buf[*length] = '\0'; /* tie off string */
+ /* slurp the data */
+ read (LOCAL->fd,LOCAL->buf,(size_t) *length);
+ return LOCAL->buf;
+}
+
+
+/* MTX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: T, always
+ */
+
+long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ MESSAGECACHE *elt;
+ FDDATA d;
+ unsigned long hdrsize,hdrpos;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mail_elt (stream,msgno);/* if message not seen */
+ if (elt->seen && !(flags & FT_PEEK)) {
+ elt->seen = T; /* mark message as seen */
+ /* recalculate status */
+ mtx_update_status (stream,msgno);
+ mm_flags (stream,msgno);
+ }
+ /* get location of text data */
+ hdrpos = mtx_hdrpos (stream,msgno,&hdrsize);
+ d.fd = LOCAL->fd; /* set initial stringstruct */
+ d.pos = hdrpos + hdrsize;
+ /* flush old buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ d.chunk = LOCAL->buf = (char *) fs_get ((size_t) d.chunksize = CHUNKSIZE);
+ INIT (bs,fd_string,(void *) &d,elt->rfc822_size - hdrsize);
+ return T; /* success */
+}
+
+/* MTX mail per-message modify flags
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ /* recalculate status */
+ mtx_update_status (stream,elt->msgno);
+}
+
+
+/* MTX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream still alive, NIL if not
+ */
+
+long mtx_ping (MAILSTREAM *stream)
+{
+ /* punt if stream no longer alive */
+ if (!(stream && LOCAL)) return NIL;
+ /* parse mailbox, punt if parse dies */
+ return (mtx_parse (stream)) ? T : NIL;
+}
+
+
+/* MTX mail check mailbox (reparses status too)
+ * Accepts: MAIL stream
+ */
+
+void mtx_check (MAILSTREAM *stream)
+{
+ unsigned long i = 1;
+ if (mtx_ping (stream)) { /* ping mailbox */
+ /* get new message status */
+ while (i <= stream->nmsgs) mail_elt (stream,i++);
+ mm_log ("Check completed",(long) NIL);
+ }
+}
+
+/* MTX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long mtx_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ unsigned long i = 1;
+ unsigned long j,k,m,recent;
+ unsigned long n = 0;
+ unsigned long delta = 0;
+ MESSAGECACHE *elt;
+ char tmp[MAILTMPLEN];
+ if (!(ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ mtx_ping (stream))); /* parse sequence if given, ping stream */
+ else if (stream->rdonly) mm_log ("Expunge ignored on readonly mailbox",WARN);
+ else {
+ mm_critical (stream); /* go critical */
+ recent = stream->recent; /* get recent now that pinged */
+ while (i <= stream->nmsgs) {/* for each message */
+ elt = mail_elt (stream,i);/* get cache element */
+ /* number of bytes to smash or preserve */
+ k = elt->private.special.text.size + elt->rfc822_size;
+ /* if need to expunge this message */
+ if (elt->deleted && (sequence ? elt->sequence : T)) {
+ /* if recent, note one less recent message */
+ if (elt->recent) --recent;
+ delta += k; /* number of bytes to delete */
+ /* notify upper levels */
+ mail_expunged (stream,i);
+ n++; /* count up one more deleted message */
+ }
+ else if (i++ && delta) { /* preserved message */
+ /* first byte to preserve */
+ j = elt->private.special.offset;
+ do { /* read from source position */
+ m = min (k,(unsigned long) MAILTMPLEN);
+ lseek (LOCAL->fd,j,SEEK_SET);
+ read (LOCAL->fd,tmp,(size_t) m);
+ /* write to destination position */
+ lseek (LOCAL->fd,j - delta,SEEK_SET);
+ write (LOCAL->fd,tmp,(size_t) m);
+ j += m; /* next chunk, perhaps */
+ } while (k -= m); /* until done */
+ elt->private.special.offset -= delta;
+ }
+ }
+ if (n) { /* truncate file after last message */
+ chsize (LOCAL->fd,LOCAL->filesize -= delta);
+ sprintf (tmp,"Expunged %ld messages",n);
+ mm_log (tmp,(long) NIL); /* output the news */
+ }
+ else mm_log ("No messages deleted, so no update needed",(long) NIL);
+ mm_nocritical (stream); /* release critical */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ }
+ return ret;
+}
+
+/* MTX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if success, NIL if failed
+ */
+
+long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ char tmp[MAILTMPLEN];
+ struct stat sbuf;
+ MESSAGECACHE *elt;
+ unsigned long i,j,k;
+ long ret = LONGT;
+ int fd;
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ if (!mtx_isvalid (mailbox,tmp)) switch (errno) {
+ case ENOENT: /* no such file? */
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",
+ (long) NIL);
+ return NIL;
+ case 0: /* merely empty file? */
+ break;
+ case EINVAL: /* name is bogus */
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ return mtx_badname (tmp,mailbox);
+ default: /* file exists, but not valid format */
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (tmp,"Not a MTX-format mailbox: %s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* open the destination */
+ if (!mailboxfile (tmp,mailbox) ||
+ (fd = open (tmp,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,
+ S_IREAD|S_IWRITE)) < 0) {
+ sprintf (tmp,"Unable to open copy mailbox: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+
+ mm_critical (stream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ /* for each requested message */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,SEEK_SET);
+ /* number of bytes to copy */
+ k = elt->private.special.text.size + elt->rfc822_size;
+ do { /* read from source position */
+ j = min (k,(long) MAILTMPLEN);
+ read (LOCAL->fd,tmp,(size_t) j);
+ if (write (fd,tmp,(size_t) j) < 0) {
+ sprintf (tmp,"Unable to write message: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ chsize (fd,sbuf.st_size);
+ j = k;
+ ret = NIL; /* note error */
+ break;
+ }
+ } while (k -= j); /* until done */
+ }
+ close (fd); /* close the file */
+ mm_nocritical (stream); /* release critical */
+ /* delete all requested messages */
+ if (ret && (options & CP_MOVE)) for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ elt->deleted = T; /* mark message deleted */
+ /* recalculate status */
+ mtx_update_status (stream,i);
+ }
+ if (ret && mail_parameters (NIL,GET_COPYUID,NIL))
+ mm_log ("Can not return meaningful COPYUID with this mailbox format",WARN);
+ return ret;
+}
+
+/* MTX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd,ld,c;
+ char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN];
+ FILE *df;
+ MESSAGECACHE elt;
+ long f;
+ unsigned long i,uf;
+ STRING *message;
+ long ret = LONGT;
+ /* default stream to prototype */
+ if (!stream) stream = &mtxproto;
+ /* make sure valid mailbox */
+ if (!mtx_isvalid (mailbox,tmp)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (((mailbox[0] == 'I') || (mailbox[0] == 'i')) &&
+ ((mailbox[1] == 'N') || (mailbox[1] == 'n')) &&
+ ((mailbox[2] == 'B') || (mailbox[2] == 'b')) &&
+ ((mailbox[3] == 'O') || (mailbox[3] == 'o')) &&
+ ((mailbox[4] == 'X') || (mailbox[4] == 'x')) && !mailbox[5])
+ dummy_create (NIL,"INBOX.MTX");
+ else {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* falls through */
+ case 0: /* merely empty file? */
+ break;
+ case EINVAL:
+ return mtx_badname (tmp,mailbox);
+ default:
+ sprintf (tmp,"Not a MTX-format mailbox: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get first message */
+ if (!(*af) (stream,data,&flags,&date,&message)) return NIL;
+ /* open destination mailbox */
+ if (!mailboxfile (file,mailbox) ||
+ ((fd = open (file,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,
+ S_IREAD|S_IWRITE)) < 0) || !(df = fdopen (fd,"ab"))) {
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ mm_critical (stream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+
+ errno = 0;
+ do { /* parse flags */
+ if (!SIZE (message)) { /* guard against zero-length */
+ mm_log ("Append of zero-length message",ERROR);
+ ret = NIL;
+ break;
+ }
+ f = mail_parse_flags (stream,flags,&i);
+ /* reverse bits (dontcha wish we had CIRC?) */
+ for (uf = 0; i; uf |= 1 << (29 - find_rightmost_bit (&i)));
+ if (date) { /* parse date if given */
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ mm_log (tmp,ERROR);
+ ret = NIL; /* mark failure */
+ break;
+ }
+ mail_date (tmp,&elt); /* write preseved date */
+ }
+ else internal_date (tmp); /* get current date in IMAP format */
+ /* write header */
+ if (fprintf (df,"%s,%lu;%010lo%02lo\015\012",tmp,i = SIZE (message),uf,
+ (unsigned long) f) < 0) ret = NIL;
+ else { /* write message */
+ if (i) do c = 0xff & SNX (message);
+ while ((putc (c,df) != EOF) && --i);
+ /* get next message */
+ if (i || !(*af) (stream,data,&flags,&date,&message)) ret = NIL;
+ }
+ } while (ret && message);
+ /* revert file if failure */
+ if (!ret || (fflush (df) == EOF)) {
+ chsize (fd,sbuf.st_size); /* revert file */
+ close (fd); /* make sure fclose() doesn't corrupt us */
+ if (errno) {
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ }
+ ret = NIL;
+ }
+ fclose (df); /* close the file */
+ mm_nocritical (stream); /* release critical */
+ if (ret && mail_parameters (NIL,GET_APPENDUID,NIL))
+ mm_log ("Can not return meaningful APPENDUID with this mailbox format",
+ WARN);
+ return ret;
+}
+
+
+/* Return bad file name error message
+ * Accepts: temporary buffer
+ * file name
+ * Returns: long NIL always
+ */
+
+long mtx_badname (char *tmp,char *s)
+{
+ sprintf (tmp,"Invalid mailbox name: %s",s);
+ mm_log (tmp,ERROR);
+ return (long) NIL;
+}
+
+/* Parse mailbox
+ * Accepts: MAIL stream
+ * Returns: T if parse OK
+ * NIL if failure, stream aborted
+ */
+
+long mtx_parse (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = NIL;
+ unsigned char *s,*t,*x,lbuf[65];
+ char tmp[MAILTMPLEN];
+ long i;
+ long curpos = LOCAL->filesize;
+ long nmsgs = stream->nmsgs;
+ long recent = stream->recent;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ if (sbuf.st_size < curpos) { /* sanity check */
+ sprintf (tmp,"Mailbox shrank from %ld to %ld!",curpos,sbuf.st_size);
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ /* while there is stuff to parse */
+ while (i = sbuf.st_size - curpos) {
+ /* get to that position in the file */
+ lseek (LOCAL->fd,curpos,SEEK_SET);
+ if ((i = read (LOCAL->fd,lbuf,64)) <= 0) {
+ sprintf (tmp,"Unable to read internal header at %ld, size = %ld: %s",
+ curpos,sbuf.st_size,i ? strerror (errno) : "no data read");
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ lbuf[i] = '\0'; /* tie off buffer just in case */
+ if (!((s = strchr (lbuf,'\015')) && (s[1] == '\012'))) {
+ sprintf (tmp,"Unable to find end of line at %ld in %ld bytes, text: %s",
+ curpos,i,(char *) lbuf);
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ *s = '\0'; /* tie off header line */
+ i = (s + 2) - lbuf; /* note start of text offset */
+ if (!((s = strchr (lbuf,',')) && (t = strchr (s+1,';')))) {
+ sprintf (tmp,"Unable to parse internal header at %ld: %s",curpos,
+ (char *) lbuf);
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+
+ *s++ = '\0'; *t++ = '\0'; /* tie off fields */
+ /* intantiate an elt for this message */
+ (elt = mail_elt (stream,++nmsgs))->valid = T;
+ elt->private.uid = ++stream->uid_last;
+ /* note file offset of header */
+ elt->private.special.offset = curpos;
+ /* as well as offset from header of message */
+ elt->private.special.text.size = i;
+ /* header size not known yet */
+ elt->private.msg.header.text.size = 0;
+ /* parse the header components */
+ if (!(mail_parse_date (elt,lbuf) &&
+ (elt->rfc822_size = strtol (x = s,(char **) &s,10)) && (!(s && *s))&&
+ isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) &&
+ isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) &&
+ isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) &&
+ isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12])) {
+ sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s",
+ curpos,(char *) lbuf,(char *) x,(char *) t);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ /* update current position to next header */
+ curpos += i + elt->rfc822_size;
+ /* calculate system flags */
+ if ((i = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T;
+ if (i & fDELETED) elt->deleted = T;
+ if (i & fFLAGGED) elt->flagged = T;
+ if (i & fANSWERED) elt->answered = T;
+ if (i & fDRAFT) elt->draft = T;
+ if (curpos > sbuf.st_size) {
+ sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)",
+ elt->private.special.offset,curpos,sbuf.st_size);
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ }
+ /* update parsed file size */
+ LOCAL->filesize = sbuf.st_size;
+ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
+ mail_recent (stream,recent); /* and of change in recent messages */
+ return T; /* return the winnage */
+}
+
+/* Update status string
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mtx_update_status (MAILSTREAM *stream,unsigned long msgno)
+{
+ char tmp[MAILTMPLEN];
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ unsigned long j,k = 0;
+ /* not if readonly you don't */
+ if (stream->rdonly || !elt->valid) return;
+ j = elt->user_flags; /* get user flags */
+ /* reverse bits (dontcha wish we had CIRC?) */
+ while (j) k |= 1 << 29 - find_rightmost_bit (&j);
+ sprintf (tmp,"%010lo%02o",k, /* print new flag string */
+ fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft));
+ /* get to that place in the file */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 14,SEEK_SET);
+ write (LOCAL->fd,tmp,12); /* write new flags */
+}
+
+/* MTX locate header for a message
+ * Accepts: MAIL stream
+ * message number
+ * pointer to returned header size
+ * Returns: position of header in file
+ */
+
+unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size)
+{
+ unsigned long siz;
+ size_t i = 0;
+ int q = 0;
+ char *s,tmp[MAILTMPLEN];
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ long pos = elt->private.special.offset + elt->private.special.text.size;
+ /* is header size known? */
+ if (!(*size = elt->private.msg.header.text.size)) {
+ /* get to header position */
+ lseek (LOCAL->fd,pos,SEEK_SET);
+ /* search message for CRLF CRLF */
+ for (siz = 1; siz <= elt->rfc822_size; siz++) {
+ if (!i && /* buffer empty? */
+ (read (LOCAL->fd,s = tmp,
+ i = (size_t) min(elt->rfc822_size-siz,(long)MAILTMPLEN))<= 0))
+ return pos;
+ else i--;
+ switch (q) { /* sniff at buffer */
+ case 0: /* first character */
+ q = (*s++ == '\015') ? 1 : 0;
+ break;
+ case 1: /* second character */
+ q = (*s++ == '\012') ? 2 : 0;
+ break;
+ case 2: /* third character */
+ q = (*s++ == '\015') ? 3 : 0;
+ break;
+ case 3: /* fourth character */
+ if (*s++ == '\012') { /* have the sequence? */
+ /* yes, note for later */
+ elt->private.msg.header.text.size = (*size = siz);
+ return pos; /* return to caller */
+ }
+ q = 0; /* lost... */
+ break;
+ }
+ }
+ }
+ return pos; /* have position */
+}
diff --git a/imap/src/osdep/dos/nl_dos.c b/imap/src/osdep/dos/nl_dos.c
new file mode 100644
index 00000000..47cb7f0a
--- /dev/null
+++ b/imap/src/osdep/dos/nl_dos.c
@@ -0,0 +1,61 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Windows/TOPS-20 newline routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Copy string with CRLF newlines
+ * Accepts: destination string
+ * pointer to size of destination string buffer
+ * source string
+ * length of source string
+ * Returns: length of copied string
+ */
+
+unsigned long strcrlfcpy (unsigned char **dst,unsigned long *dstl,
+ unsigned char *src,unsigned long srcl)
+{
+ /* flush destination buffer if too small */
+ if (*dst && (srcl > *dstl)) fs_give ((void **) dst);
+ if (!*dst) { /* make a new buffer if needed */
+ *dst = (char *) fs_get ((size_t) (*dstl = srcl) + 1);
+ if (dstl) *dstl = srcl; /* return new buffer length to main program */
+ }
+ /* copy strings */
+ if (srcl) memcpy (*dst,src,(size_t) srcl);
+ *(*dst + srcl) = '\0'; /* tie off destination */
+ return srcl; /* return length */
+}
+
+
+/* Length of string after strcrlfcpy applied
+ * Accepts: source string
+ * Returns: length of string
+ */
+
+unsigned long strcrlflen (STRING *s)
+{
+ return SIZE (s); /* no-brainer on DOS! */
+}
diff --git a/imap/src/osdep/dos/os_dbw.c b/imap/src/osdep/dos/os_dbw.c
new file mode 100644
index 00000000..e28e1ab0
--- /dev/null
+++ b/imap/src/osdep/dos/os_dbw.c
@@ -0,0 +1,91 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- MS-DOS (B&W) version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+/* Private function prototypes */
+
+#include "tcp_dos.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys\stat.h>
+#include <sys\timeb.h>
+#include "misc.h"
+#include "stdlib.h"
+#include "bwtcp.h"
+
+
+#include "fs_dos.c"
+#include "ftl_dos.c"
+#include "nl_dos.c"
+#include "env_dos.c"
+#undef write
+#define read soread
+#define write sowrite
+#define close soclose
+#include "tcp_dos.c"
+
+
+/* Return my local host name
+ * Returns: my local host name
+ */
+
+char *mylocalhost (void)
+{
+ char *s;
+ if (!myLocalHost) { /* known yet? */
+ /* get local host name from DISPLAY env var */
+ if (!((s = getenv ("DISPLAY")) || (s = getenv ("display")))) {
+ mm_log ("Environment variable 'DISPLAY' is not set", ERROR);
+ s = "random-pc";
+ }
+ myLocalHost = cpystr (s);
+ }
+ return myLocalHost;
+}
+
+
+/* Look up host address
+ * Accepts: pointer to pointer to host name
+ * socket address block
+ * Returns: non-zero with host address in socket, official host name in host;
+ * else NIL
+ */
+
+long lookuphost (char **host,struct sockaddr_in *sin)
+{
+ char *s = *host; /* in case of error */
+ sin->sin_addr.s_addr = rhost (host);
+ if (sin->sin_addr.s_addr == -1) {
+ *host = s; /* error, restore old host name */
+ return NIL;
+ }
+ *host = cpystr (*host); /* make permanent copy of name */
+ return T; /* success */
+}
diff --git a/imap/src/osdep/dos/os_dbw.h b/imap/src/osdep/dos/os_dbw.h
new file mode 100644
index 00000000..f22cae6f
--- /dev/null
+++ b/imap/src/osdep/dos/os_dbw.h
@@ -0,0 +1,43 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- DOS (B&W/Novell) version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+#define INADEQUATE_MEMORY
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys\types.h>
+#include <io.h>
+
+#define gethostid clock
+
+#include "env_dos.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/dos/os_dnf.c b/imap/src/osdep/dos/os_dnf.c
new file mode 100644
index 00000000..dcc3ea64
--- /dev/null
+++ b/imap/src/osdep/dos/os_dnf.c
@@ -0,0 +1,95 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- MS-DOS (PC-NFS) version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+/* Private function prototypes */
+
+#include "tcp_dos.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys\stat.h>
+#include <sys\timeb.h>
+#include <sys\nfs_time.h>
+#include <sys\tk_types.h>
+#include <sys\socket.h>
+#include <netinet\in.h>
+#include <tk_errno.h>
+#include <sys\uio.h>
+#include <netdb.h>
+#include "misc.h"
+
+
+#include "fs_dos.c"
+#include "ftl_dos.c"
+#include "nl_dos.c"
+#include "env_dos.c"
+#undef write
+#include "tcp_dos.c"
+
+
+/* Return my local host name
+ * Returns: my local host name
+ */
+
+char *mylocalhost (void)
+{
+ if (!myLocalHost) { /* known yet? */
+ char *s,tmp[MAILTMPLEN];
+ unsigned long myip;
+ /* see if known host name */
+ if (!gethostname (tmp,MAILTMPLEN-1)) s = tmp;
+ /* no, try host address */
+ else if (get_myipaddr ((char *) &myip))
+ sprintf (s = tmp,"[%s]",inet_ntoa (myip));
+ else s = "random-pc"; /* say what? */
+ myLocalHost = cpystr (s); /* record for subsequent use */
+ }
+ return myLocalHost;
+}
+
+
+/* Look up host address
+ * Accepts: pointer to pointer to host name
+ * socket address block
+ * Returns: non-zero with host address in socket, official host name in host;
+ * else NIL
+ */
+
+long lookuphost (char **host,struct sockaddr_in *sin)
+{
+ long ret = -1;
+ char tmp[MAILTMPLEN];
+ struct hostent *hn = gethostbyname (lcase (strcpy (tmp,*host)));
+ if (!hn) return NIL; /* got a host name? */
+ *host = cpystr (hn->h_name); /* set official name */
+ /* copy host addresses */
+ memcpy (&sin->sin_addr,hn->h_addr,hn->h_length);
+ return T;
+}
diff --git a/imap/src/osdep/dos/os_dnf.h b/imap/src/osdep/dos/os_dnf.h
new file mode 100644
index 00000000..3a832217
--- /dev/null
+++ b/imap/src/osdep/dos/os_dnf.h
@@ -0,0 +1,44 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- DOS (PC-NFS) version
+ *
+ * Author: Mike Seibel from Novell version by Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MikeS@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+#define INADEQUATE_MEMORY
+
+#include <tklib.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys\types.h>
+#include <io.h>
+
+#define gethostid clock
+
+#include "env_dos.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/dos/os_dnv.c b/imap/src/osdep/dos/os_dnv.c
new file mode 100644
index 00000000..e0341764
--- /dev/null
+++ b/imap/src/osdep/dos/os_dnv.c
@@ -0,0 +1,95 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- MS-DOS (Novell) version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+/* Private function prototypes */
+
+#include "tcp_dos.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys\stat.h>
+#include <sys\timeb.h>
+#include <sys\socket.h>
+#include <netinet\in.h>
+#include <netdb.h>
+#include "misc.h"
+
+
+#include "fs_dos.c"
+#include "ftl_dos.c"
+#include "nl_dos.c"
+#include "env_dos.c"
+#undef write
+#define read soread
+#define write sowrite
+#define close soclose
+#include "tcp_dos.c"
+
+
+/* Return my local host name
+ * Returns: my local host name
+ */
+
+char *mylocalhost (void)
+{
+ if (!myLocalHost) { /* known yet? */
+ char *s,tmp[MAILTMPLEN];
+ struct hostent *he;
+ struct in_addr in;
+ /* could we get local id? */
+ if ((in.s_addr = getmyipaddr ()) != -1) {
+ if (he = gethostbyaddr ((char *) &in.s_addr,4,PF_INET)) s = he->h_name;
+ else sprintf (s = tmp,"[%s]",inet_ntoa (in));
+ }
+ else s = "random-pc"; /* say what? */
+ myLocalHost = cpystr (s); /* record for subsequent use */
+ }
+ return myLocalHost;
+}
+
+
+/* Look up host address
+ * Accepts: pointer to pointer to host name
+ * socket address block
+ * Returns: non-zero with host address in socket, official host name in host;
+ * else NIL
+ */
+
+long lookuphost (char **host,struct sockaddr_in *sin)
+{
+ char *s = *host; /* in case of error */
+ sin->sin_addr.s_addr = rhost (host);
+ if (sin->sin_addr.s_addr == -1) {
+ *host = s; /* error, restore old host name */
+ return NIL;
+ }
+ *host = cpystr (*host); /* make permanent copy of name */
+ return T; /* success */
+}
diff --git a/imap/src/osdep/dos/os_dnv.h b/imap/src/osdep/dos/os_dnv.h
new file mode 100644
index 00000000..f22cae6f
--- /dev/null
+++ b/imap/src/osdep/dos/os_dnv.h
@@ -0,0 +1,43 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- DOS (B&W/Novell) version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+#define INADEQUATE_MEMORY
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys\types.h>
+#include <io.h>
+
+#define gethostid clock
+
+#include "env_dos.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/dos/os_dpc.c b/imap/src/osdep/dos/os_dpc.c
new file mode 100644
index 00000000..eece19d7
--- /dev/null
+++ b/imap/src/osdep/dos/os_dpc.c
@@ -0,0 +1,102 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- MS-DOS (PC/TCP) version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+/* Private function prototypes */
+
+#include "tcp_dos.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys\stat.h>
+#include <sys\timeb.h>
+#include <4bsddefs.h>
+#include <sys\socket.h>
+#include <errno.h>
+#include <arpa\inet.h>
+#include <netinet\in.h>
+#include <netdb.h>
+#include "misc.h"
+
+
+#include "fs_dos.c"
+#include "ftl_dos.c"
+#include "nl_dos.c"
+#include "env_dos.c"
+#undef write
+#include "tcp_dos.c"
+
+
+/* Return my local host name
+ * Returns: my local host name
+ */
+
+char *mylocalhost (void)
+{
+ if (!myLocalHost) { /* known yet */
+ char *s,tmp[MAILTMPLEN];
+ long myip;
+ /* see if known host name */
+ if (!gethostname (tmp,MAILTMPLEN-1)) s = tmp;
+ /* no, try IP address */
+ else if (myip = gethostid ()) {
+ struct in_addr in;
+ in.s_addr = myip;
+ sprintf (s = tmp,"[%s]",inet_ntoa (in));
+ }
+ /* older kernel, look harder. */
+ else if (getconf ("ifcust","ip-address",tmp+1,MAILTMPLEN-2)) {
+ *(s = tmp) = '['; /* wrap the brackets around it */
+ strcat (tmp,"]");
+ }
+ else s = "random-pc"; /* say what? */
+ myLocalHost = cpystr (s); /* record for subsequent use */
+ }
+ return myLocalHost;
+}
+
+
+/* Look up host address
+ * Accepts: pointer to pointer to host name
+ * socket address block
+ * Returns: non-zero with host address in socket, official host name in host;
+ * else NIL
+ */
+
+long lookuphost (char **host,struct sockaddr_in *sin)
+{
+ long ret = -1;
+ char tmp[MAILTMPLEN];
+ struct hostent *hn = gethostbyname (lcase (strcpy (tmp,*host)));
+ if (!hn) return NIL; /* got a host name? */
+ *host = cpystr (hn->h_name); /* set official name */
+ /* copy host addresses */
+ memcpy (&sin->sin_addr,hn->h_addr,hn->h_length);
+ return T;
+}
diff --git a/imap/src/osdep/dos/os_dpc.h b/imap/src/osdep/dos/os_dpc.h
new file mode 100644
index 00000000..5de3e637
--- /dev/null
+++ b/imap/src/osdep/dos/os_dpc.h
@@ -0,0 +1,41 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- DOS (PC/TCP) version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+#define INADEQUATE_MEMORY
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys\types.h>
+#include <io.h>
+
+#include "env_dos.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/dos/os_dwa.c b/imap/src/osdep/dos/os_dwa.c
new file mode 100644
index 00000000..36e5b147
--- /dev/null
+++ b/imap/src/osdep/dos/os_dwa.c
@@ -0,0 +1,72 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- DOS (Waterloo) version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+#include <tcp.h> /* must be before TCPSTREAM definition */
+#include "tcp_dwa.h" /* must be before osdep include our tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys\stat.h>
+#include <sys\timeb.h>
+#include "misc.h"
+
+/* Undo compatibility definition */
+
+#undef tcp_open
+
+#include "fs_dos.c"
+#include "ftl_dos.c"
+#include "nl_dos.c"
+#include "env_dos.c"
+#include "tcp_dwa.c"
+
+
+/* Return my local host name
+ * Returns: my local host name
+ */
+
+char *mylocalhost (void)
+{
+ if (!myLocalHost) {
+ char *s,hname[32],tmp[MAILTMPLEN];
+ long myip;
+
+ if (!sock_initted++) sock_init();
+ tcp_cbrk (0x01); /* turn off ctrl-break catching */
+ /*
+ * haven't discovered a way to find out the local host's
+ * name with wattcp yet.
+ */
+ if (myip = gethostid ()) sprintf (s = tmp,"[%s]",inet_ntoa (hname,myip));
+ else s = "random-pc";
+ myLocalHost = cpystr (s);
+ }
+ return myLocalHost;
+}
diff --git a/imap/src/osdep/dos/os_dwa.h b/imap/src/osdep/dos/os_dwa.h
new file mode 100644
index 00000000..f70fa31b
--- /dev/null
+++ b/imap/src/osdep/dos/os_dwa.h
@@ -0,0 +1,43 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- DOS (Waterloo) version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+#define INADEQUATE_MEMORY
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys\types.h>
+#include <io.h>
+
+#define tcp_open TCP_open
+
+#include "env_dos.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/dos/os_wsk.c b/imap/src/osdep/dos/os_wsk.c
new file mode 100644
index 00000000..8627ef89
--- /dev/null
+++ b/imap/src/osdep/dos/os_wsk.c
@@ -0,0 +1,45 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Winsock version
+ *
+ * Author: Mike Seibel from Unix version by Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MikeS@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_wsk.h" /* must be before osdep includes tcp.h */
+#undef ERROR /* quell conflicting def warning */
+#include "mail.h"
+#include "osdep.h"
+#include <time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys\stat.h>
+#include <sys\timeb.h>
+#include "misc.h"
+
+
+#include "fs_dos.c"
+#include "ftl_dos.c"
+#include "nl_dos.c"
+#include "env_dos.c"
+#include "tcp_wsk.c"
diff --git a/imap/src/osdep/dos/os_wsk.h b/imap/src/osdep/dos/os_wsk.h
new file mode 100644
index 00000000..10213cef
--- /dev/null
+++ b/imap/src/osdep/dos/os_wsk.h
@@ -0,0 +1,48 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- 16-bit Winsock version
+ *
+ * Author: Mike Seibel from Novell version by Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MikeS@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+#define INADEQUATE_MEMORY
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys\types.h>
+#include <io.h>
+
+#define gethostid clock
+#define WSA_VERSION ((1 << 8) | 1)
+
+#include "env_dos.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+
+
+#undef noErr
+#undef MAC
diff --git a/imap/src/osdep/dos/pmatch.c b/imap/src/osdep/dos/pmatch.c
new file mode 100644
index 00000000..95a0bb86
--- /dev/null
+++ b/imap/src/osdep/dos/pmatch.c
@@ -0,0 +1,89 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: IMAP Wildcard Matching Routines (case-independent)
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 June 2000
+ * Last Edited: 30 August 2006
+ */
+
+/* Wildcard pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if pattern matches base, else NIL
+ */
+
+long pmatch_full (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ /* % at end, OK if no inferiors */
+ if (!pat[1]) return (delim && strchr (s,delim)) ? NIL : T;
+ /* scan remainder of string until delimiter */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while ((*s != delim) && *s++);
+ break;
+ case '*': /* match 0 or more characters */
+ if (!pat[1]) return T; /* * at end, unconditional match */
+ /* scan remainder of string */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while (*s++);
+ break;
+ case '\0': /* end of pattern */
+ return *s ? NIL : T; /* success if also end of base */
+ default: /* match this character */
+ return compare_uchar (*pat,*s) ? NIL : pmatch_full (s+1,pat+1,delim);
+ }
+ return NIL;
+}
+
+/* Directory pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if base is a matching directory of pattern, else NIL
+ */
+
+long dmatch (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ if (!*s) return T; /* end of base means have a subset match */
+ if (!*++pat) return NIL; /* % at end, no inferiors permitted */
+ /* scan remainder of string until delimiter */
+ do if (dmatch (s,pat,delim)) return T;
+ while ((*s != delim) && *s++);
+ if (*s && !s[1]) return T; /* ends with delimiter, must be subset */
+ return dmatch (s,pat,delim);/* do new scan */
+ case '*': /* match 0 or more characters */
+ return T; /* unconditional match */
+ case '\0': /* end of pattern */
+ break;
+ default: /* match this character */
+ if (*s) return compare_uchar (*pat,*s) ? NIL : dmatch (s+1,pat+1,delim);
+ /* end of base, return if at delimiter */
+ else if (*pat == delim) return T;
+ break;
+ }
+ return NIL;
+}
diff --git a/imap/src/osdep/dos/tcp_dos.c b/imap/src/osdep/dos/tcp_dos.c
new file mode 100644
index 00000000..062f260c
--- /dev/null
+++ b/imap/src/osdep/dos/tcp_dos.c
@@ -0,0 +1,434 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MS-DOS TCP/IP routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 13 January 2008
+ */
+
+static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */
+static long ttmo_read = 0; /* TCP timeouts, in seconds */
+static long ttmo_write = 0;
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd);
+
+/* TCP/IP manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tcp_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_TIMEOUT:
+ tmoh = (tcptimeout_t) value;
+ case GET_TIMEOUT:
+ ret = (void *) tmoh;
+ break;
+ case SET_READTIMEOUT:
+ ttmo_read = (long) value;
+ case GET_READTIMEOUT:
+ ret = (void *) ttmo_read;
+ break;
+ case SET_WRITETIMEOUT:
+ ttmo_write = (long) value;
+ case GET_WRITETIMEOUT:
+ ret = (void *) ttmo_write;
+ break;
+ }
+ return ret;
+}
+
+/* TCP/IP open
+ * Accepts: host name
+ * contact service name
+ * contact port number
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream = NIL;
+ struct sockaddr_in sin;
+ int sock;
+ char *s,tmp[MAILTMPLEN];
+ port &= 0xffff; /* erase flags */
+ /* The domain literal form is used (rather than simply the dotted decimal
+ as with other Unix programs) because it has to be a valid "host name"
+ in mailsystem terminology. */
+ sin.sin_family = AF_INET; /* family is always Internet */
+ /* look like domain literal? */
+ if (host[0] == '[' && host[(strlen (host))-1] == ']') {
+ strcpy (tmp,host+1); /* yes, copy number part */
+ tmp[strlen (tmp)-1] = '\0';
+ if ((sin.sin_addr.s_addr = inet_addr (tmp)) == -1) {
+ sprintf (tmp,"Bad format domain-literal: %.80s",host);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ }
+ /* look up host name */
+ else if (!lookuphost (&host,&sin)) {
+ sprintf (tmp,"Host not found: %s",host);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+
+ /* copy port number in network format */
+ if (!(sin.sin_port = htons (port))) fatal ("Bad port argument to tcp_open");
+ /* get a TCP stream */
+ if ((sock = socket (sin.sin_family,SOCK_STREAM,0)) < 0) {
+ sprintf (tmp,"Unable to create TCP socket (%d)",errno);
+ mm_log (tmp,ERROR);
+ fs_give ((void **) &host);
+ return NIL;
+ }
+#if 0
+ /* needed? */
+ else if (sock >= FD_SETSIZE) {/* unselectable sockets are useless */
+ sprintf (tmp,"Unable to create selectable TCP socket (%d >= %d)",
+ sock,FD_SETSIZE);
+ close (sock);
+ errno = ENOBUFS; /* just in case */
+ return NIL;
+ }
+#endif
+ /* open connection */
+ if (connect (sock,(struct sockaddr *) &sin,sizeof (sin)) < 0) {
+ switch (errno) { /* analyze error */
+ case ECONNREFUSED:
+ s = "Refused";
+ break;
+ case ENOBUFS:
+ s = "Insufficient system resources";
+ break;
+ case ETIMEDOUT:
+ s = "Timed out";
+ break;
+ default:
+ s = "Unknown error";
+ break;
+ }
+ sprintf (tmp,"Can't connect to %.80s,%ld: %s (%d)",host,port,s,errno);
+ mm_log (tmp,ERROR);
+ close (sock);
+ fs_give ((void **) &host);
+ return NIL;
+ }
+ /* create TCP/IP stream */
+ stream = (TCPSTREAM *) fs_get (sizeof (TCPSTREAM));
+ stream->host = host; /* official host name */
+ stream->localhost = cpystr (mylocalhost ());
+ stream->port = port; /* port number */
+ stream->tcps = sock; /* init socket */
+ stream->ictr = 0; /* init input counter */
+ return stream; /* return success */
+}
+
+/* TCP/IP authenticated open
+ * Accepts: NETMBX specifier
+ * service name
+ * returned user name buffer
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL; /* always NIL on DOS */
+}
+
+/* TCP receive line
+ * Accepts: TCP stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *tcp_getline (TCPSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = tcp_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = tcp_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* TCP receive line or partial line
+ * Accepts: TCP stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!tcp_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!tcp_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* TCP/IP receive buffer
+ * Accepts: TCP/IP stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer)
+{
+ unsigned long n;
+ char *bufptr = buffer;
+ while (size > 0) { /* until request satisfied */
+ if (!tcp_getdata (stream)) return NIL;
+ n = min (size,stream->ictr);/* number of bytes to transfer */
+ /* do the copy */
+ memcpy (bufptr,stream->iptr,n);
+ bufptr += n; /* update pointer */
+ stream->iptr +=n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -=n;
+ }
+ bufptr[0] = '\0'; /* tie off string */
+ return T;
+}
+
+/* TCP/IP receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getdata (TCPSTREAM *stream)
+{
+ int i;
+ fd_set fds,efds;
+ struct timeval tmo;
+ time_t t = time (0);
+ if (stream->tcps < 0) return NIL;
+ while (stream->ictr < 1) { /* if nothing in the buffer */
+ time_t tl = time (0); /* start of request */
+ tmo.tv_sec = ttmo_read; /* read timeout */
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ FD_SET (stream->tcps,&fds);/* set bit in selection vector */
+ FD_SET(stream->tcps,&efds);/* set bit in error selection vector */
+ errno = NIL; /* block and read */
+ while (((i = select (stream->tcps+1,&fds,0,&efds,ttmo_read ? &tmo : 0))<0)
+ && (errno == EINTR));
+ if (!i) { /* timeout? */
+ time_t tc = time (0);
+ if (tmoh && ((*tmoh) (tc - t,tc - tl))) continue;
+ else return tcp_abort (stream);
+ }
+ else if (i < 0) return tcp_abort (stream);
+ while (((i = read (stream->tcps,stream->ibuf,BUFLEN)) < 0) &&
+ (errno == EINTR));
+ if (i < 1) return tcp_abort (stream);
+ stream->iptr = stream->ibuf;/* point at TCP buffer */
+ stream->ictr = i; /* set new byte count */
+ }
+ return T;
+}
+
+/* TCP/IP send string as record
+ * Accepts: TCP/IP stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long tcp_soutr (TCPSTREAM *stream,char *string)
+{
+ return tcp_sout (stream,string,(unsigned long) strlen (string));
+}
+
+
+/* TCP/IP send string
+ * Accepts: TCP/IP stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
+{
+ int i;
+ fd_set fds;
+ struct timeval tmo;
+ time_t t = time (0);
+ if (stream->tcps < 0) return NIL;
+ while (size > 0) { /* until request satisfied */
+ time_t tl = time (0); /* start of request */
+ tmo.tv_sec = ttmo_write; /* write timeout */
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_SET (stream->tcps,&fds);/* set bit in selection vector */
+ errno = NIL; /* block and write */
+ while (((i = select (stream->tcps+1,0,&fds,0,ttmo_write ? &tmo : 0)) < 0)
+ && (errno == EINTR));
+ if (!i) { /* timeout? */
+ time_t tc = time (0);
+ if (tmoh && ((*tmoh) (tc - t,tc - tl))) continue;
+ else return tcp_abort (stream);
+ }
+ else if (i < 0) return tcp_abort (stream);
+ while (((i = write (stream->tcps,string,size)) < 0) && (errno == EINTR));
+ if (i < 0) return tcp_abort (stream);
+ size -= i; /* how much we sent */
+ string += i;
+ }
+ return T; /* all done */
+}
+
+/* TCP/IP close
+ * Accepts: TCP/IP stream
+ */
+
+void tcp_close (TCPSTREAM *stream)
+{
+ tcp_abort (stream); /* nuke the socket */
+ /* flush host names */
+ fs_give ((void **) &stream->host);
+ fs_give ((void **) &stream->localhost);
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+
+/* TCP/IP abort stream
+ * Accepts: TCP/IP stream
+ * Returns: NIL always
+ */
+
+long tcp_abort (TCPSTREAM *stream)
+{
+ if (stream->tcps >= 0) close (stream->tcps);
+ stream->tcps = -1;
+ return NIL;
+}
+
+/* TCP/IP get host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_host (TCPSTREAM *stream)
+{
+ return stream->host; /* return host name */
+}
+
+
+/* TCP/IP get remote host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_remotehost (TCPSTREAM *stream)
+{
+ return stream->host; /* all we can do for now */
+}
+
+
+/* TCP/IP return port for this stream
+ * Accepts: TCP/IP stream
+ * Returns: port number for this stream
+ */
+
+unsigned long tcp_port (TCPSTREAM *stream)
+{
+ return stream->port; /* return port number */
+}
+
+
+/* TCP/IP get local host name
+ * Accepts: TCP/IP stream
+ * Returns: local host name
+ */
+
+char *tcp_localhost (TCPSTREAM *stream)
+{
+ return stream->localhost; /* return local host name */
+}
+
+
+/* TCP/IP return canonical form of host name
+ * Accepts: host name
+ * Returns: canonical form of host name
+ */
+
+char *tcp_canonical (char *name)
+{
+ return name;
+}
+
+
+/* TCP/IP get client host name (server calls only)
+ * Returns: client host name
+ */
+
+char *tcp_clienthost ()
+{
+ return "UNKNOWN";
+}
diff --git a/imap/src/osdep/dos/tcp_dos.h b/imap/src/osdep/dos/tcp_dos.h
new file mode 100644
index 00000000..2933a5ff
--- /dev/null
+++ b/imap/src/osdep/dos/tcp_dos.h
@@ -0,0 +1,51 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: OS2 routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+/* TCP input buffer -- must be large enough to prevent overflow */
+
+#define BUFLEN 8192
+
+
+/* TCP I/O stream (must be before osdep.h is included) */
+
+#define TCPSTREAM struct tcp_stream
+TCPSTREAM {
+ char *host; /* host name */
+ unsigned long port; /* port number */
+ char *localhost; /* local host name */
+ int tcps; /* tcp socket */
+ long ictr; /* input counter */
+ char *iptr; /* input pointer */
+ char ibuf[BUFLEN]; /* input buffer */
+};
+
+
+/* Local function prototypes */
+
+long lookuphost (char **host,struct sockaddr_in *sin);
+long tcp_abort (TCPSTREAM *stream);
diff --git a/imap/src/osdep/dos/tcp_dwa.c b/imap/src/osdep/dos/tcp_dwa.c
new file mode 100644
index 00000000..4275f27c
--- /dev/null
+++ b/imap/src/osdep/dos/tcp_dwa.c
@@ -0,0 +1,344 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Waterloo DOS TCP/IP routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 13 January 2008
+ */
+
+
+/* Global data */
+
+short sock_initted = 0; /* global so others using net can see it */
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd);
+
+/* TCP/IP manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tcp_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+/* TCP/IP open
+ * Accepts: host name
+ * contact service name
+ * contact port number
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *TCP_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream = NIL;
+ tcp_Socket *sock;
+ char *s,tmp[MAILTMPLEN];
+ unsigned long adr,i,j,k,l;
+ port &= 0xffff; /* erase flags */
+ /* initialize if first time here */
+ if (!sock_initted++) sock_init();
+ /* The domain literal form is used (rather than simply the dotted decimal
+ as with other Unix programs) because it has to be a valid "host name"
+ in mailsystem terminology. */
+ /* look like domain literal? */
+ if (host[0] == '[' && host[strlen (host)-1] == ']') {
+ if (((i = strtoul (s = host+1,&s,10)) <= 255) && *s++ == '.' &&
+ ((j = strtoul (s,&s,10)) <= 255) && *s++ == '.' &&
+ ((k = strtoul (s,&s,10)) <= 255) && *s++ == '.' &&
+ ((l = strtoul (s,&s,10)) <= 255) && *s++ == ']' && !*s)
+ adr = (i << 24) + (j << 16) + (k << 8) + l;
+ else {
+ sprintf (tmp,"Bad format domain-literal: %.80s",host);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ }
+ else { /* lookup host name */
+ if (!(adr = resolve (host))) {
+ sprintf (tmp,"Host not found: %s",host);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ }
+
+ /* OK to instantiate socket now */
+ sock = (tcp_Socket *) fs_get (sizeof (tcp_Socket));
+ /* open connection */
+ if (!tcp_open (sock,(word) 0,adr,(word) port,NULL)) {
+ sprintf (tmp,"Can't connect to %.80s,%ld",host,port);
+ mm_log (tmp,ERROR);
+ fs_give ((void **) &sock);
+ return NIL;
+ }
+ /* create TCP/IP stream */
+ stream = (TCPSTREAM *) fs_get (sizeof (TCPSTREAM));
+ stream->host = cpystr (host); /* official host name */
+ stream->localhost = cpystr (mylocalhost ());
+ stream->port = port; /* port number */
+ stream->tcps = sock; /* init socket */
+ stream->ictr = 0; /* init input counter */
+ return stream; /* return success */
+}
+
+/* TCP/IP authenticated open
+ * Accepts: NETMBX specifier
+ * service name
+ * returned user name buffer
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL; /* always NIL on DOS */
+}
+
+/* TCP receive line
+ * Accepts: TCP stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *tcp_getline (TCPSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = tcp_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = tcp_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* TCP receive line or partial line
+ * Accepts: TCP stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!tcp_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!tcp_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* TCP/IP receive buffer
+ * Accepts: TCP/IP stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer)
+{
+ unsigned long n;
+ char *bufptr = buffer;
+ while (size > 0) { /* until request satisfied */
+ if (!tcp_getdata (stream)) return NIL;
+ n = min (size,stream->ictr);/* number of bytes to transfer */
+ /* do the copy */
+ memcpy (bufptr,stream->iptr,(size_t) n);
+ bufptr += n; /* update pointer */
+ stream->iptr +=n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -=n;
+ }
+ bufptr[0] = '\0'; /* tie off string */
+ return T;
+}
+
+
+/* TCP/IP receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getdata (TCPSTREAM *stream)
+{
+ int status;
+ if (!stream->tcps) return NIL;/* no-no nuked socket */
+ while (stream->ictr < 1) { /* if buffer empty, block for input and read */
+ if (!_ip_delay1 (stream->tcps,600,NULL,&status))
+ stream->ictr = sock_fastread (stream->tcps,
+ stream->iptr = stream->ibuf,BUFLEN);
+ else if (status == 1) { /* nuke the socket if closed */
+ sock_close (stream->tcps);
+ fs_give ((void **) &stream->tcps);
+ return NIL;
+ }
+ }
+ return T;
+}
+
+/* TCP/IP send string as record
+ * Accepts: TCP/IP stream
+ * Returns: T if success else NIL
+ */
+
+long tcp_soutr (TCPSTREAM *stream,char *string)
+{
+ /* output the cruft */
+ sock_puts (stream->tcps,string);
+ return T; /* all done */
+}
+
+
+/* TCP/IP send string
+ * Accepts: TCP/IP stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
+{
+ sock_write (stream->tcps,string,(int) size);
+ return T;
+}
+
+
+/* TCP/IP close
+ * Accepts: TCP/IP stream
+ */
+
+void tcp_close (TCPSTREAM *stream)
+{
+ if (stream->tcps){ /* nuke the socket */
+ sock_close (stream->tcps);
+ _ip_delay2 (stream->tcps,0,NULL,NULL);
+ }
+ fs_give ((void **) &stream->tcps);
+ /* flush host names */
+ fs_give ((void **) &stream->host);
+ fs_give ((void **) &stream->localhost);
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+/* TCP/IP get host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_host (TCPSTREAM *stream)
+{
+ return stream->host; /* return host name */
+}
+
+
+/* TCP/IP get remote host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_remotehost (TCPSTREAM *stream)
+{
+ return stream->host; /* return host name */
+}
+
+
+/* TCP/IP return port for this stream
+ * Accepts: TCP/IP stream
+ * Returns: port number for this stream
+ */
+
+unsigned long tcp_port (TCPSTREAM *stream)
+{
+ return stream->port; /* return port number */
+}
+
+
+/* TCP/IP get local host name
+ * Accepts: TCP/IP stream
+ * Returns: local host name
+ */
+
+char *tcp_localhost (TCPSTREAM *stream)
+{
+ return stream->localhost; /* return local host name */
+}
+
+
+/* TCP/IP return canonical form of host name
+ * Accepts: host name
+ * Returns: canonical form of host name
+ */
+
+char *tcp_canonical (char *name)
+{
+ return name;
+}
+
+
+/* TCP/IP get client host name (server calls only)
+ * Returns: client host name
+ */
+
+char *tcp_clienthost ()
+{
+ return "UNKNOWN";
+}
diff --git a/imap/src/osdep/dos/tcp_dwa.h b/imap/src/osdep/dos/tcp_dwa.h
new file mode 100644
index 00000000..360eea85
--- /dev/null
+++ b/imap/src/osdep/dos/tcp_dwa.h
@@ -0,0 +1,45 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Waterloo DOS TCP/IP routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+/* TCP input buffer -- must be large enough to prevent overflow */
+
+#define BUFLEN 8192
+
+
+/* TCP I/O stream (must be before osdep.h is included) */
+
+#define TCPSTREAM struct tcp_stream
+TCPSTREAM {
+ char *host; /* host name */
+ unsigned long port; /* port number */
+ char *localhost; /* local host name */
+ tcp_Socket *tcps; /* tcp socket */
+ long ictr; /* input counter */
+ char *iptr; /* input pointer */
+ char ibuf[BUFLEN]; /* input buffer */
+};
diff --git a/imap/src/osdep/dos/tcp_wsk.c b/imap/src/osdep/dos/tcp_wsk.c
new file mode 100644
index 00000000..90e206a9
--- /dev/null
+++ b/imap/src/osdep/dos/tcp_wsk.c
@@ -0,0 +1,818 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Winsock TCP/IP routines
+ *
+ * Author: Mark Crispin from Mike Seibel's Winsock code
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 13 January 2008
+ */
+
+
+#define TCPMAXSEND 32768
+
+/* Private functions */
+
+int tcp_socket_open (struct sockaddr_in *sin,char *tmp,char *hst,
+ unsigned long port);
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd);
+long tcp_abort (TCPSTREAM *stream);
+long tcp_close_socket (SOCKET *sock);
+char *tcp_name (struct sockaddr_in *sin,long flag);
+char *tcp_name_valid (char *s);
+
+
+/* Private data */
+
+int wsa_initted = 0; /* init ? */
+static int wsa_sock_open = 0; /* keep track of open sockets */
+static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */
+static long ttmo_read = 0; /* TCP timeouts, in seconds */
+static long ttmo_write = 0;
+static long allowreversedns = T;/* allow reverse DNS lookup */
+static long tcpdebug = NIL; /* extra TCP debugging telemetry */
+
+/* TCP/IP manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tcp_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_TIMEOUT:
+ tmoh = (tcptimeout_t) value;
+ case GET_TIMEOUT:
+ ret = (void *) tmoh;
+ break;
+ case SET_READTIMEOUT:
+ ttmo_read = (long) value;
+ case GET_READTIMEOUT:
+ ret = (void *) ttmo_read;
+ break;
+ case SET_WRITETIMEOUT:
+ ttmo_write = (long) value;
+ case GET_WRITETIMEOUT:
+ ret = (void *) ttmo_write;
+ break;
+ case SET_ALLOWREVERSEDNS:
+ allowreversedns = (long) value;
+ case GET_ALLOWREVERSEDNS:
+ ret = (void *) allowreversedns;
+ break;
+ case SET_TCPDEBUG:
+ tcpdebug = (long) value;
+ case GET_TCPDEBUG:
+ ret = (void *) tcpdebug;
+ break;
+ }
+ return ret;
+}
+
+/* TCP/IP open
+ * Accepts: host name
+ * contact service name
+ * contact port number and optional silent flag
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream = NIL;
+ int i;
+ SOCKET sock = INVALID_SOCKET;
+ int silent = (port & NET_SILENT) ? T : NIL;
+ char *s;
+ struct sockaddr_in sin;
+ struct hostent *he;
+ char hostname[MAILTMPLEN];
+ char tmp[MAILTMPLEN];
+ struct servent *sv = NIL;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (!wsa_initted++) { /* init Windows Sockets */
+ WSADATA wsock;
+ if (i = (int) WSAStartup (WSA_VERSION,&wsock)) {
+ wsa_initted = 0; /* in case we try again */
+ sprintf (tmp,"Unable to start Windows Sockets (%d)",i);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ }
+ port &= 0xffff; /* erase flags */
+ /* lookup service */
+ if (service && (sv = getservbyname (service,"tcp")))
+ port = ntohs (sin.sin_port = sv->s_port);
+ /* copy port number in network format */
+ else sin.sin_port = htons ((u_short) port);
+ /* The domain literal form is used (rather than simply the dotted decimal
+ as with other Windows programs) because it has to be a valid "host name"
+ in mailsystem terminology. */
+ sin.sin_family = AF_INET; /* family is always Internet */
+ /* look like domain literal? */
+ if (host[0] == '[' && host[(strlen (host))-1] == ']') {
+ strcpy (tmp,host+1); /* yes, copy number part */
+ tmp[strlen (tmp)-1] = '\0';
+ if ((sin.sin_addr.s_addr = inet_addr (tmp)) == INADDR_NONE) {
+ sprintf (tmp,"Bad format domain-literal: %.80s",host);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ else {
+ sin.sin_family = AF_INET; /* family is always Internet */
+ strcpy (hostname,host);
+ (*bn) (BLOCK_TCPOPEN,NIL);
+ sock = tcp_socket_open (&sin,tmp,hostname,port);
+ (*bn) (BLOCK_NONE,NIL);
+ }
+ }
+
+ else { /* lookup host name */
+ if (tcpdebug) {
+ sprintf (tmp,"DNS resolution %.80s",host);
+ mm_log (tmp,TCPDEBUG);
+ }
+ (*bn) (BLOCK_DNSLOOKUP,NIL);/* look up name */
+ if (!(he = gethostbyname (lcase (strcpy (tmp,host)))))
+ sprintf (tmp,"Host not found (#%d): %s",WSAGetLastError(),host);
+ (*bn) (BLOCK_NONE,NIL);
+ if (he) { /* DNS resolution won? */
+ if (tcpdebug) mm_log ("DNS resolution done",TCPDEBUG);
+ /* copy address type */
+ sin.sin_family = he->h_addrtype;
+ /* copy host name */
+ strcpy (hostname,he->h_name);
+ wsa_sock_open++; /* prevent tcp_close_socket() from freeing in
+ loop */
+ for (i = 0; (sock == INVALID_SOCKET) && (s = he->h_addr_list[i]); i++) {
+ if (i && !silent) mm_log (tmp,WARN);
+ memcpy (&sin.sin_addr,s,he->h_length);
+ (*bn) (BLOCK_TCPOPEN,NIL);
+ sock = tcp_socket_open (&sin,tmp,hostname,port);
+ (*bn) (BLOCK_NONE,NIL);
+ }
+ wsa_sock_open--; /* undo protection */
+ }
+ }
+ if (sock == INVALID_SOCKET) { /* error? */
+ if (!silent) mm_log (tmp,ERROR);
+ tcp_close_socket (&sock); /* do possible cleanup action */
+ }
+ else { /* got a socket, create TCP/IP stream */
+ stream = (TCPSTREAM *) memset (fs_get (sizeof (TCPSTREAM)),0,
+ sizeof (TCPSTREAM));
+ stream->port = port; /* port number */
+ /* init socket */
+ stream->tcpsi = stream->tcpso = sock;
+ stream->ictr = 0; /* init input counter */
+ /* copy official host name */
+ stream->host = cpystr (hostname);
+ if (tcpdebug) mm_log ("Stream open and ready for read",TCPDEBUG);
+ }
+ return stream; /* return success */
+}
+
+/* Open a TCP socket
+ * Accepts: Internet socket address block
+ * scratch buffer
+ * host name for error message
+ * port number for error message
+ * Returns: socket if success, else -1 with error string in scratch buffer
+ */
+
+int tcp_socket_open (struct sockaddr_in *sin,char *tmp,char *hst,
+ unsigned long port)
+{
+ int sock;
+ char *s;
+ sprintf (tmp,"Trying IP address [%s]",inet_ntoa (sin->sin_addr));
+ mm_log (tmp,NIL);
+ /* get a TCP stream */
+ if ((sock = socket (sin->sin_family,SOCK_STREAM,0)) == INVALID_SOCKET) {
+ sprintf (tmp,"Unable to create TCP socket (%d)",WSAGetLastError());
+ return -1;
+ }
+ wsa_sock_open++; /* count this socket as open */
+ /* open connection */
+ if (connect (sock,(struct sockaddr *) sin,sizeof (struct sockaddr_in)) ==
+ SOCKET_ERROR) {
+ switch (WSAGetLastError ()){/* analyze error */
+ case WSAECONNREFUSED:
+ s = "Refused";
+ break;
+ case WSAENOBUFS:
+ s = "Insufficient system resources";
+ break;
+ case WSAETIMEDOUT:
+ s = "Timed out";
+ break;
+ case WSAEHOSTUNREACH:
+ s = "Host unreachable";
+ break;
+ default:
+ s = "Unknown error";
+ break;
+ }
+ sprintf (tmp,"Can't connect to %.80s,%ld: %s (%d)",hst,port,s,
+ WSAGetLastError ());
+ tcp_close_socket (&sock); /* flush socket */
+ sock = INVALID_SOCKET;
+ }
+ return sock; /* return the socket */
+}
+
+/* TCP/IP authenticated open
+ * Accepts: NETMBX specifier
+ * service name
+ * returned user name buffer
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL; /* always NIL on Windows */
+}
+
+/* TCP receive line
+ * Accepts: TCP stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *tcp_getline (TCPSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = tcp_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = tcp_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* TCP receive line or partial line
+ * Accepts: TCP stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!tcp_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!tcp_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* TCP/IP receive buffer
+ * Accepts: TCP/IP stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *s)
+{
+ unsigned long n;
+ /* make sure socket still alive */
+ if (stream->tcpsi == INVALID_SOCKET) return NIL;
+ /* can transfer bytes from buffer? */
+ if (n = min (size,stream->ictr)) {
+ memcpy (s,stream->iptr,n); /* yes, slurp as much as we can from it */
+ s += n; /* update pointer */
+ stream->iptr +=n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -=n;
+ }
+ if (size) {
+ int i;
+ fd_set fds;
+ struct timeval tmo;
+ time_t tc,t = time (0);
+ blocknotify_t bn=(blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ (*bn) (BLOCK_TCPREAD,NIL);
+ while (size > 0) { /* until request satisfied */
+ time_t tl = time (0);
+ if (tcpdebug) mm_log ("Reading TCP buffer",TCPDEBUG);
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_SET (stream->tcpsi,&fds);/* set bit in selection vector */
+ tmo.tv_sec = ttmo_read;
+ tmo.tv_usec = 0;
+ /* block and read */
+ switch ((stream->tcpsi == stream->tcpso) ?
+ select (stream->tcpsi+1,&fds,0,0,
+ ttmo_read ? &tmo : (struct timeval *) 0) : 1) {
+ case SOCKET_ERROR: /* error */
+ if (WSAGetLastError () != WSAEINTR) return tcp_abort (stream);
+ break;
+ case 0: /* timeout */
+ tc = time (0);
+ if (tmoh && ((*tmoh) (tc - t,tc - tl))) break;
+ return tcp_abort (stream);
+ default:
+ if (stream->tcpsi == stream->tcpso)
+ while (((i = recv (stream->tcpsi,s,(int) min (maxposint,size),0)) ==
+ SOCKET_ERROR) && (WSAGetLastError () == WSAEINTR));
+ else while (((i = read (stream->tcpsi,s,(int) min (maxposint,size))) <
+ 0) && (errno == EINTR));
+ switch (i) {
+ case SOCKET_ERROR: /* error */
+ case 0: /* no data read */
+ return tcp_abort (stream);
+ default:
+ s += i; /* point at new place to write */
+ size -= i; /* reduce byte count */
+ if (tcpdebug) mm_log ("Successfully read TCP buffer",TCPDEBUG);
+ }
+ }
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ }
+ *s = '\0'; /* tie off string */
+ return T;
+}
+
+/* TCP/IP receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getdata (TCPSTREAM *stream)
+{
+ struct timeval tmo;
+ int i;
+ fd_set fds;
+ time_t tc,t = time (0);
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ FD_ZERO (&fds); /* initialize selection vector */
+ if (stream->tcpsi == INVALID_SOCKET) return NIL;
+ (*bn) (BLOCK_TCPREAD,NIL);
+ tmo.tv_sec = ttmo_read;
+ tmo.tv_usec = 0;
+ while (stream->ictr < 1) { /* if nothing in the buffer */
+ time_t tl = time (0);
+ if (tcpdebug) mm_log ("Reading TCP data",TCPDEBUG);
+ FD_SET (stream->tcpsi,&fds);/* set bit in selection vector */
+ /* block and read */
+ switch ((stream->tcpsi == stream->tcpso) ?
+ select (stream->tcpsi+1,&fds,0,0,
+ ttmo_read ? &tmo : (struct timeval *) 0) : 1) {
+ case SOCKET_ERROR: /* error */
+ if (WSAGetLastError () != WSAEINTR) return tcp_abort (stream);
+ break;
+ case 0: /* timeout */
+ tc = time (0);
+ if (tmoh && ((*tmoh) (tc - t,tc - tl))) break;
+ return tcp_abort (stream);
+ default:
+ if (stream->tcpsi == stream->tcpso)
+ while (((i = recv (stream->tcpsi,stream->ibuf,BUFLEN,0)) ==
+ SOCKET_ERROR) && (WSAGetLastError () == WSAEINTR));
+ else while (((i = read (stream->tcpsi,stream->ibuf,BUFLEN)) < 0) &&
+ (errno == EINTR));
+ switch (i) {
+ case SOCKET_ERROR: /* error */
+ case 0: /* no data read */
+ return tcp_abort (stream);
+ default:
+ stream->ictr = i; /* set new byte count */
+ /* point at TCP buffer */
+ stream->iptr = stream->ibuf;
+ if (tcpdebug) mm_log ("Successfully read TCP data",TCPDEBUG);
+ }
+ }
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return T;
+}
+
+/* TCP/IP send string as record
+ * Accepts: TCP/IP stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long tcp_soutr (TCPSTREAM *stream,char *string)
+{
+ return tcp_sout (stream,string,(unsigned long) strlen (string));
+}
+
+
+/* TCP/IP send string
+ * Accepts: TCP/IP stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
+{
+ int i;
+ struct timeval tmo;
+ fd_set fds;
+ time_t tc,t = time (0);
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ tmo.tv_sec = ttmo_write;
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ if (stream->tcpso == INVALID_SOCKET) return NIL;
+ (*bn) (BLOCK_TCPWRITE,NIL);
+ while (size > 0) { /* until request satisfied */
+ time_t tl = time (0);
+ if (tcpdebug) mm_log ("Writing to TCP",TCPDEBUG);
+ FD_SET (stream->tcpso,&fds);/* set bit in selection vector */
+ /* block and write */
+ switch ((stream->tcpsi == stream->tcpso) ?
+ select (stream->tcpso+1,NULL,&fds,NULL,
+ tmo.tv_sec ? &tmo : (struct timeval *) 0) : 1) {
+ case SOCKET_ERROR: /* error */
+ if (WSAGetLastError () != WSAEINTR) return tcp_abort (stream);
+ break;
+ case 0: /* timeout */
+ tc = time (0);
+ if (tmoh && ((*tmoh) (tc - t,tc - tl))) break;
+ return tcp_abort (stream);
+ default:
+ if (stream->tcpsi == stream->tcpso)
+ while (((i = send (stream->tcpso,string,
+ (int) min (size,TCPMAXSEND),0)) == SOCKET_ERROR) &&
+ (WSAGetLastError () == WSAEINTR));
+ else while (((i = write (stream->tcpso,string,
+ min (size,TCPMAXSEND))) < 0) &&
+ (errno == EINTR));
+ if (i == SOCKET_ERROR) return tcp_abort (stream);
+ size -= i; /* count this size */
+ if (tcpdebug) mm_log ("successfully wrote to TCP",TCPDEBUG);
+ string += i;
+ }
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return T; /* all done */
+}
+
+
+/* TCP/IP close
+ * Accepts: TCP/IP stream
+ */
+
+void tcp_close (TCPSTREAM *stream)
+{
+ tcp_abort (stream); /* nuke the sockets */
+ /* flush host names */
+ if (stream->host) fs_give ((void **) &stream->host);
+ if (stream->remotehost) fs_give ((void **) &stream->remotehost);
+ if (stream->localhost) fs_give ((void **) &stream->localhost);
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+
+/* TCP/IP abort sockets
+ * Accepts: TCP/IP stream
+ * Returns: NIL, always
+ */
+
+long tcp_abort (TCPSTREAM *stream)
+{
+ if (stream->tcpsi != stream->tcpso) tcp_close_socket (&stream->tcpso);
+ else stream->tcpso = INVALID_SOCKET;
+ return tcp_close_socket (&stream->tcpsi);
+}
+
+
+/* TCP/IP abort stream
+ * Accepts: WinSock socket
+ * Returns: NIL, always
+ */
+
+long tcp_close_socket (SOCKET *sock)
+{
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ /* something to close? */
+ if (sock && (*sock != INVALID_SOCKET)) {
+ (*bn) (BLOCK_TCPCLOSE,NIL);
+ closesocket (*sock); /* WinSock socket close */
+ *sock = INVALID_SOCKET;
+ (*bn) (BLOCK_NONE,NIL);
+ wsa_sock_open--; /* drop this socket */
+ }
+ /* no more open streams? */
+ if (wsa_initted && !wsa_sock_open) {
+ mm_log ("Winsock cleanup",NIL);
+ wsa_initted = 0; /* no more sockets, so... */
+ WSACleanup (); /* free up resources until needed */
+ }
+ return NIL;
+}
+
+/* TCP/IP get host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_host (TCPSTREAM *stream)
+{
+ return stream->host; /* use tcp_remotehost() if want guarantees */
+}
+
+
+/* TCP/IP get remote host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_remotehost (TCPSTREAM *stream)
+{
+ if (!stream->remotehost) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ stream->remotehost = /* get socket's peer name */
+ ((getpeername (stream->tcpsi,(struct sockaddr *) &sin,&sinlen) ==
+ SOCKET_ERROR) || (sinlen <= 0)) ?
+ cpystr (stream->host) : tcp_name (&sin,NIL);
+ }
+ return stream->remotehost;
+}
+
+
+/* TCP/IP return port for this stream
+ * Accepts: TCP/IP stream
+ * Returns: port number for this stream
+ */
+
+unsigned long tcp_port (TCPSTREAM *stream)
+{
+ return stream->port; /* return port number */
+}
+
+
+/* TCP/IP get local host name
+ * Accepts: TCP/IP stream
+ * Returns: local host name
+ */
+
+char *tcp_localhost (TCPSTREAM *stream)
+{
+ if (!stream->localhost) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ stream->localhost = /* get socket's name */
+ ((stream->port & 0xffff000) ||
+ ((getsockname (stream->tcpsi,(struct sockaddr *) &sin,&sinlen) ==
+ SOCKET_ERROR) || (sinlen <= 0))) ?
+ cpystr (mylocalhost ()) : tcp_name (&sin,NIL);
+ }
+ return stream->localhost; /* return local host name */
+}
+
+/* TCP/IP get client host address (server calls only)
+ * Returns: client host address
+ */
+
+char *tcp_clientaddr ()
+{
+ if (!myClientAddr) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ myClientAddr = /* get stdin's peer name */
+ ((getpeername (0,(struct sockaddr *) &sin,&sinlen) == SOCKET_ERROR) ||
+ (sinlen <= 0)) ? cpystr ("UNKNOWN") : cpystr (inet_ntoa (sin.sin_addr));
+ }
+ return myClientAddr;
+}
+
+
+/* TCP/IP get client host name (server calls only)
+ * Returns: client host name
+ */
+
+char *tcp_clienthost ()
+{
+ if (!myClientHost) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ myClientHost = /* get stdin's peer name */
+ ((getpeername (0,(struct sockaddr *) &sin,&sinlen) == SOCKET_ERROR) ||
+ (sinlen <= 0)) ? cpystr ("UNKNOWN") : tcp_name (&sin,T);
+ }
+ return myClientHost;
+}
+
+/* TCP/IP get server host address (server calls only)
+ * Returns: server host address
+ */
+
+char *tcp_serveraddr ()
+{
+ if (!myServerAddr) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ myServerAddr = /* get stdin's peer name */
+ ((getsockname (0,(struct sockaddr *) &sin,&sinlen) == SOCKET_ERROR) ||
+ (sinlen <= 0)) ? cpystr ("UNKNOWN") : cpystr (inet_ntoa (sin.sin_addr));
+ }
+ return myServerAddr;
+}
+
+
+/* TCP/IP get server host name (server calls only)
+ * Returns: server host name
+ */
+
+static long myServerPort = -1;
+
+char *tcp_serverhost ()
+{
+ if (!myServerHost) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ if (!wsa_initted++) { /* init Windows Sockets */
+ WSADATA wsock;
+ if (WSAStartup (WSA_VERSION,&wsock)) {
+ wsa_initted = 0;
+ return "random-pc"; /* try again later? */
+ }
+ }
+ /* get stdin's name */
+ if ((getsockname (0,(struct sockaddr *) &sin,&sinlen) == SOCKET_ERROR) ||
+ (sinlen <= 0)) myServerHost = cpystr (mylocalhost ());
+ else {
+ myServerHost = tcp_name (&sin,NIL);
+ myServerPort = ntohs (sin.sin_port);
+ }
+ }
+ return myServerHost;
+}
+
+
+/* TCP/IP get server port number (server calls only)
+ * Returns: server port number
+ */
+
+long tcp_serverport ()
+{
+ if (!myServerHost) tcp_serverhost ();
+ return myServerPort;
+}
+
+/* TCP/IP return canonical form of host name
+ * Accepts: host name
+ * Returns: canonical form of host name
+ */
+
+char *tcp_canonical (char *name)
+{
+ char *ret,host[MAILTMPLEN];
+ struct hostent *he;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ /* look like domain literal? */
+ if (name[0] == '[' && name[strlen (name) - 1] == ']') return name;
+ (*bn) (BLOCK_DNSLOOKUP,NIL);
+ if (tcpdebug) {
+ sprintf (host,"DNS canonicalization %.80s",name);
+ mm_log (host,TCPDEBUG);
+ }
+ /* note that NT requires lowercase! */
+ ret = (he = gethostbyname (lcase (strcpy (host,name)))) ? he->h_name : name;
+ (*bn) (BLOCK_NONE,NIL);
+ if (tcpdebug) mm_log ("DNS canonicalization done",TCPDEBUG);
+ return ret;
+}
+
+
+/* TCP/IP return name from socket
+ * Accepts: socket
+ * verbose flag
+ * Returns: cpystr name
+ */
+
+char *tcp_name (struct sockaddr_in *sin,long flag)
+{
+ char *ret,*t,adr[MAILTMPLEN],tmp[MAILTMPLEN];
+ sprintf (ret = adr,"[%.80s]",inet_ntoa (sin->sin_addr));
+ if (allowreversedns) {
+ struct hostent *he;
+ blocknotify_t bn = (blocknotify_t)mail_parameters(NIL,GET_BLOCKNOTIFY,NIL);
+ void *data;
+ if (tcpdebug) {
+ sprintf (tmp,"Reverse DNS resolution %s",adr);
+ mm_log (tmp,TCPDEBUG);
+ }
+ (*bn) (BLOCK_DNSLOOKUP,NIL);/* quell alarms */
+ data = (*bn) (BLOCK_SENSITIVE,NIL);
+ /* translate address to name */
+ if (t = tcp_name_valid ((he = gethostbyaddr ((char *) &sin->sin_addr,
+ sizeof (struct in_addr),
+ sin->sin_family)) ?
+ (char *) he->h_name : NIL)) {
+ /* produce verbose form if needed */
+ if (flag) sprintf (ret = tmp,"%s %s",t,adr);
+ else ret = t;
+ }
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ (*bn) (BLOCK_NONE,NIL); /* alarms OK now */
+ if (tcpdebug) mm_log ("Reverse DNS resolution done",TCPDEBUG);
+ }
+ return cpystr (ret);
+}
+
+/* Return my local host name
+ * Returns: my local host name
+ */
+
+char *mylocalhost (void)
+{
+ if (!myLocalHost) {
+ char tmp[MAILTMPLEN];
+ if (!wsa_initted++) { /* init Windows Sockets */
+ WSADATA wsock;
+ if (WSAStartup (WSA_VERSION,&wsock)) {
+ wsa_initted = 0;
+ return "random-pc"; /* try again later? */
+ }
+ }
+ myLocalHost = cpystr ((gethostname (tmp,MAILTMPLEN-1) == SOCKET_ERROR) ?
+ "random-pc" : tcp_canonical (tmp));
+ }
+ return myLocalHost;
+}
+
+
+/* Validate name
+ * Accepts: domain name
+ * Returns: T if valid, NIL otherwise
+ */
+
+char *tcp_name_valid (char *s)
+{
+ int c;
+ char *ret,*tail;
+ /* must be non-empty and not too long */
+ if ((ret = (s && *s) ? s : NIL) && (tail = ret + NETMAXHOST)) {
+ /* must be alnum, dot, or hyphen */
+ while ((c = *s++) && (s <= tail) &&
+ (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) ||
+ ((c >= '0') && (c <= '9')) || (c == '-') || (c == '.')));
+ if (c) ret = NIL;
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/dos/tcp_wsk.h b/imap/src/osdep/dos/tcp_wsk.h
new file mode 100644
index 00000000..6e8f9af7
--- /dev/null
+++ b/imap/src/osdep/dos/tcp_wsk.h
@@ -0,0 +1,50 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Winsock TCP/IP routines
+ *
+ * Author: Mike Seibel from Unix version by Mark Crispin
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+/* TCP input buffer -- must be large enough to prevent overflow */
+
+#define BUFLEN 16384 /* 32768 causes stdin read() to barf */
+
+#include <windows.h>
+#define _INC_WINDOWS
+#include <winsock.h>
+
+
+/* TCP I/O stream (must be before osdep.h is included) */
+
+#define TCPSTREAM struct tcp_stream
+TCPSTREAM {
+ char *host; /* host name */
+ char *remotehost; /* remote host name */
+ unsigned long port; /* port number */
+ char *localhost; /* local host name */
+ SOCKET tcpsi; /* tcp socket */
+ SOCKET tcpso; /* tcp socket */
+ long ictr; /* input counter */
+ char *iptr; /* input pointer */
+ char ibuf[BUFLEN]; /* input buffer */
+};
diff --git a/imap/src/osdep/dos/write.c b/imap/src/osdep/dos/write.c
new file mode 100644
index 00000000..c7854815
--- /dev/null
+++ b/imap/src/osdep/dos/write.c
@@ -0,0 +1,59 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Write data, treating partial writes as an error
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 May 1995
+ * Last Edited: 30 August 2006
+ */
+
+/* The whole purpose of this unfortunate routine is to deal with DOS and
+ * certain cretinous versions of UNIX which decided that the "bytes actually
+ * written" return value from write() gave them license to use that for things
+ * that are really errors, such as disk quota exceeded, maximum file size
+ * exceeded, disk full, etc.
+ *
+ * BSD won't screw us this way on the local filesystem, but who knows what
+ * some NFS-mounted filesystem will do.
+ */
+
+#undef write
+
+/* Write data to file
+ * Accepts: file descriptor
+ * I/O vector structure
+ * number of vectors in structure
+ * Returns: number of bytes written if successful, -1 if failure
+ */
+
+long maxposint = (long)((((unsigned long) 1) << ((sizeof(int) * 8) - 1)) - 1);
+
+long safe_write (int fd,char *buf,long nbytes)
+{
+ long i,j;
+ if (nbytes > 0) for (i = nbytes; i; i -= j,buf += j) {
+ while (((j = write (fd,buf,(int) min (maxposint,i))) < 0) &&
+ (errno == EINTR));
+ if (j < 0) return j;
+ }
+ return nbytes;
+}
diff --git a/imap/src/osdep/mac/dummy.h b/imap/src/osdep/mac/dummy.h
new file mode 100644
index 00000000..32650e06
--- /dev/null
+++ b/imap/src/osdep/mac/dummy.h
@@ -0,0 +1,43 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 9 May 1991
+ * Last Edited: 30 August 2006
+ */
+
+/* Exported function prototypes */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat);
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long scan_contents (DRIVER *dtb,char *name,char *contents,
+ unsigned long csiz,unsigned long fsiz);
+long dummy_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz);
+long dummy_create (MAILSTREAM *stream,char *mailbox);
+long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode);
+long dummy_delete (MAILSTREAM *stream,char *mailbox);
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname);
+char *dummy_file (char *dst,char *name);
+long dummy_canonicalize (char *tmp,char *ref,char *pat);
diff --git a/imap/src/osdep/mac/dummymac.c b/imap/src/osdep/mac/dummymac.c
new file mode 100644
index 00000000..a0d6d0d2
--- /dev/null
+++ b/imap/src/osdep/mac/dummymac.c
@@ -0,0 +1,295 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines for Mac
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 24 May 1993
+ * Last Edited: 30 August 2006
+ */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include "mail.h"
+#include "osdep.h"
+#include "dummy.h"
+#include "misc.h"
+
+/* Function prototypes */
+
+DRIVER *dummy_valid (char *name);
+void *dummy_parameters (long function,void *value);
+MAILSTREAM *dummy_open (MAILSTREAM *stream);
+void dummy_close (MAILSTREAM *stream,long options);
+long dummy_ping (MAILSTREAM *stream);
+void dummy_check (MAILSTREAM *stream);
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options);
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+/* Dummy routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER dummydriver = {
+ "dummy", /* driver name */
+ DR_LOCAL|DR_MAIL, /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ dummy_valid, /* mailbox is valid for us */
+ dummy_parameters, /* manipulate parameters */
+ dummy_scan, /* scan mailboxes */
+ dummy_list, /* list mailboxes */
+ dummy_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ dummy_create, /* create mailbox */
+ dummy_delete, /* delete mailbox */
+ dummy_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ dummy_open, /* open mailbox */
+ dummy_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message structure */
+ NIL, /* fetch header */
+ NIL, /* fetch text */
+ NIL, /* fetch message data */
+ NIL, /* unique identifier */
+ NIL, /* message number from UID */
+ NIL, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ dummy_ping, /* ping mailbox to see if still alive */
+ dummy_check, /* check for new messages */
+ dummy_expunge, /* expunge deleted messages */
+ dummy_copy, /* copy messages to another mailbox */
+ dummy_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+
+ /* prototype stream */
+MAILSTREAM dummyproto = {&dummydriver};
+
+/* Dummy validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *dummy_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ /* must be valid local mailbox */
+ return (name && *name && (*name != '{') && !compare_cstring (name,"INBOX")) ?
+ &dummydriver : NIL;
+}
+
+
+/* Dummy manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *dummy_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+/* Dummy scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ /* return silently */
+}
+
+
+/* Dummy list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ /* return silently */
+}
+
+
+/* Dummy list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ /* return silently */
+}
+
+/* Dummy create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * driver type to use
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_create (MAILSTREAM *stream,char *mailbox)
+{
+ return NIL; /* always fails */
+}
+
+
+/* Dummy delete mailbox
+ * Accepts: mail stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return NIL; /* always fails */
+}
+
+
+/* Mail rename mailbox
+ * Accepts: mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ return NIL; /* always fails */
+}
+
+/* Dummy open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *dummy_open (MAILSTREAM *stream)
+{
+ char tmp[MAILTMPLEN];
+ /* OP_PROTOTYPE call or silence */
+ if (!stream || stream->silent) return NIL;
+ if (compare_cstring (stream->mailbox,"INBOX")) {
+ sprintf (tmp,"Not a mailbox: %s",stream->mailbox);
+ mm_log (tmp,ERROR);
+ return NIL; /* always fails */
+ }
+ if (!stream->silent) { /* only if silence not requested */
+ mail_exists (stream,0); /* say there are 0 messages */
+ mail_recent (stream,0);
+ stream->uid_validity = time (0);
+ }
+ stream->inbox = T; /* note that it's an INBOX */
+ return stream; /* return success */
+}
+
+
+/* Dummy close
+ * Accepts: MAIL stream
+ * options
+ */
+
+void dummy_close (MAILSTREAM *stream,long options)
+{
+ /* return silently */
+}
+
+/* Dummy ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+long dummy_ping (MAILSTREAM *stream)
+{
+ return T;
+}
+
+
+/* Dummy check mailbox
+ * Accepts: MAIL stream
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+void dummy_check (MAILSTREAM *stream)
+{
+ dummy_ping (stream); /* invoke ping */
+}
+
+
+/* Dummy expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ return LONGT;
+}
+
+/* Dummy copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * options
+ * Returns: T if copy successful, else NIL
+ */
+
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) fatal ("Impossible dummy_copy");
+ return NIL;
+}
+
+
+/* Dummy append message string
+ * Accepts: mail stream
+ * destination mailbox
+ * append callback function
+ * data for callback
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Can't append to %s",mailbox);
+ mm_log (tmp,ERROR); /* pass up error */
+ return NIL; /* always fails */
+}
diff --git a/imap/src/osdep/mac/env_mac.c b/imap/src/osdep/mac/env_mac.c
new file mode 100644
index 00000000..d0a88df7
--- /dev/null
+++ b/imap/src/osdep/mac/env_mac.c
@@ -0,0 +1,236 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Mac environment routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 January 1992
+ * Last Edited: 30 August 2006
+ */
+
+
+static char *myHomeDir = NIL; /* home directory name */
+static char *myLocalHost = NIL; /* local host name */
+static char *myNewsrc = NIL; /* newsrc file name */
+
+#include "pmatch.c" /* include wildcard pattern matcher */
+
+/* Environment manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *env_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ char tmp[MAILTMPLEN];
+ switch ((int) function) {
+ case SET_HOMEDIR:
+ if (myHomeDir) fs_give ((void **) &myHomeDir);
+ myHomeDir = cpystr ((char *) value);
+ case GET_HOMEDIR:
+ /* set home directory if not defined */
+ if (!myHomeDir) myHomeDir = cpystr ("");
+ ret = (void *) myHomeDir;
+ break;
+ case SET_LOCALHOST:
+ myLocalHost = cpystr ((char *) value);
+ case GET_LOCALHOST:
+ ret = (void *) myLocalHost ? myLocalHost : "random-mac";
+ break;
+ case SET_NEWSRC:
+ if (myNewsrc) fs_give ((void **) &myNewsrc);
+ myNewsrc = cpystr ((char *) value);
+ case GET_NEWSRC:
+ if (!myNewsrc) { /* set news file name if not defined */
+ sprintf (tmp,"%s:News State",myhomedir ());
+ myNewsrc = cpystr (tmp);
+ }
+ ret = (void *) myNewsrc;
+ break;
+ }
+ return ret;
+}
+
+/* Write current time
+ * Accepts: destination string
+ * format of date and time
+ *
+ * This depends upon the ReadLocation() call in System 7 and the
+ * user properly setting his location/timezone in the Map control
+ * panel.
+ * Nothing is done about the gmtFlags.dlsDelta byte yet, since I
+ * don't know how it's supposed to work.
+ */
+
+static void do_date (char *date,char *fmt)
+{
+ long tz,tzm;
+ time_t ti = time (0);
+ struct tm *t = localtime (&ti);
+ MachineLocation loc;
+ ReadLocation (&loc); /* get location/timezone poop */
+ /* get sign-extended time zone */
+ tz = (loc.gmtFlags.gmtDelta & 0x00ffffff) |
+ ((loc.gmtFlags.gmtDelta & 0x00800000) ? 0xff000000 : 0);
+ tz /= 60; /* get timezone in minutes */
+ tzm = tz % 60; /* get minutes from the hour */
+ /* output time */
+ strftime (date,MAILTMPLEN,fmt,t);
+ /* now output time zone */
+ sprintf (date += strlen (date),"%+03ld%02ld",tz/60,tzm >= 0 ? tzm : -tzm);
+}
+
+
+/* Write current time in RFC 822 format
+ * Accepts: destination string
+ */
+
+void rfc822_date (char *date)
+{
+ do_date (date,"%a, %d %b %Y %H:%M:%S ");
+}
+
+
+/* Write current time in internal format
+ * Accepts: destination string
+ */
+
+void internal_date (char *date)
+{
+ do_date (date,"%2d-%b-%Y %H:%M:%S ");
+}
+
+/* Return my local host name
+ * Returns: my local host name
+ */
+
+char *mylocalhost (void)
+{
+ return (char *) mail_parameters (NIL,GET_LOCALHOST,NIL);
+}
+
+
+/* Return my home directory name
+ * Returns: my home directory name
+ */
+
+char *myhomedir ()
+{
+ return (char *) mail_parameters (NIL,GET_HOMEDIR,NIL);
+}
+
+
+/* Determine default prototype stream to user
+ * Accepts: type (NIL for create, T for append)
+ * Returns: default prototype stream
+ */
+
+MAILSTREAM *default_proto (long type)
+{
+ extern MAILSTREAM dummyproto;
+ return &dummyproto; /* return default driver's prototype */
+}
+
+/* Block until event satisfied
+ * Called as: while (wait_condition && wait ());
+ * Returns T if OK, NIL if user wants to abort
+ *
+ * Allows user to run a desk accessory, select a different window, or go
+ * to another application while waiting for the event to finish. COMMAND/.
+ * will abort the wait.
+ * Assumes the Apple menu has the apple character as its first character,
+ * and that the main program has disabled all other menus.
+ */
+
+long wait ()
+{
+ EventRecord event;
+ WindowPtr window;
+ MenuInfo **m;
+ long r;
+ Str255 tmp;
+ /* wait for an event */
+ WaitNextEvent (everyEvent,&event,(long) 6,NIL);
+ switch (event.what) { /* got one -- what is it? */
+ case mouseDown: /* mouse clicked */
+ switch (FindWindow (event.where,&window)) {
+ case inMenuBar: /* menu bar item? */
+ /* yes, interesting event? */
+ if (r = MenuSelect (event.where)) {
+ /* round-about test for Apple menu */
+ if ((*(m = GetMHandle (HiWord (r))))->menuData[1] == appleMark) {
+ /* get desk accessory name */
+ GetItem (m,LoWord (r),tmp);
+ OpenDeskAcc (tmp); /* fire it up */
+ SetPort (window); /* put us back at our window */
+ }
+ else SysBeep (60); /* the fool forgot to disable it! */
+ }
+ HiliteMenu (0); /* unhighlight it */
+ break;
+ case inContent: /* some window was selected */
+ if (window != FrontWindow ()) SelectWindow (window);
+ break;
+ default: /* ignore all others */
+ break;
+ }
+ break;
+ case keyDown: /* key hit - if COMMAND/. then punt */
+ if ((event.modifiers & cmdKey) && (event.message & charCodeMask) == '.')
+ return NIL;
+ break;
+ default: /* ignore all others */
+ break;
+ }
+ return T; /* try wait test again */
+}
+
+/* Return random number
+ */
+
+long random ()
+{
+ return (long) rand () << 16 + rand ();
+}
+
+
+/* Emulator for BSD syslog() routine
+ * Accepts: priority
+ * message
+ * parameters
+ */
+
+void syslog (int priority,const char *message,...)
+{
+}
+
+
+/* Emulator for BSD openlog() routine
+ * Accepts: identity
+ * options
+ * facility
+ */
+
+void openlog (const char *ident,int logopt,int facility)
+{
+}
diff --git a/imap/src/osdep/mac/env_mac.h b/imap/src/osdep/mac/env_mac.h
new file mode 100644
index 00000000..4a5a8c97
--- /dev/null
+++ b/imap/src/osdep/mac/env_mac.h
@@ -0,0 +1,58 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Macintosh environment routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 25 May 1995
+ * Last Edited: 30 August 2006
+ */
+
+
+#define SUBSCRIPTIONFILE(t) sprintf (t,"%s:Mailbox List",myhomedir ())
+#define SUBSCRIPTIONTEMP(t) sprintf (t,"%s:Mailbox List Temp",myhomedir ())
+
+/* Function prototypes */
+
+#include "env.h"
+
+
+/* syslog() emulation */
+
+#define LOG_MAIL (2<<3) /* mail system */
+#define LOG_DAEMON (3<<3) /* system daemons */
+#define LOG_AUTH (4<<3) /* security/authorization messages */
+#define LOG_EMERG 0 /* system is unusable */
+#define LOG_ALERT 1 /* action must be taken immediately */
+#define LOG_CRIT 2 /* critical conditions */
+#define LOG_ERR 3 /* error conditions */
+#define LOG_WARNING 4 /* warning conditions */
+#define LOG_NOTICE 5 /* normal but signification condition */
+#define LOG_INFO 6 /* informational */
+#define LOG_DEBUG 7 /* debug-level messages */
+#define LOG_PID 0x01 /* log the pid with each message */
+#define LOG_CONS 0x02 /* log on the console if errors in sending */
+#define LOG_ODELAY 0x04 /* delay open until syslog() is called */
+#define LOG_NDELAY 0x08 /* don't delay open */
+#define LOG_NOWAIT 0x10 /* if forking to log on console, don't wait() */
+
+void openlog (const char *ident,int logopt,int facility);
+void syslog (int priority,const char *message,...);
diff --git a/imap/src/osdep/mac/fs_mac.c b/imap/src/osdep/mac/fs_mac.c
new file mode 100644
index 00000000..03908328
--- /dev/null
+++ b/imap/src/osdep/mac/fs_mac.c
@@ -0,0 +1,62 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Free storage management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Get a block of free storage
+ * Accepts: size of desired block
+ * Returns: free storage block
+ */
+
+void *fs_get (size_t size)
+{
+ void *block = malloc (size ? size : (size_t) 1);
+ if (!block) fatal ("Out of memory");
+ return (block);
+}
+
+
+/* Resize a block of free storage
+ * Accepts: ** pointer to current block
+ * new size
+ */
+
+void fs_resize (void **block,size_t size)
+{
+ if (!(*block = realloc (*block,size ? size : (size_t) 1)))
+ fatal ("Can't resize memory");
+}
+
+
+/* Return a block of free storage
+ * Accepts: ** pointer to free storage block
+ */
+
+void fs_give (void **block)
+{
+ free (*block);
+ *block = NIL;
+}
diff --git a/imap/src/osdep/mac/ftl_mac.c b/imap/src/osdep/mac/ftl_mac.c
new file mode 100644
index 00000000..782381b8
--- /dev/null
+++ b/imap/src/osdep/mac/ftl_mac.c
@@ -0,0 +1,39 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Mac crash management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 January 1992
+ * Last Edited: 30 August 2006
+ */
+
+/* Report a fatal error
+ * Accepts: string to output
+ */
+
+void fatal (char *string)
+{
+ mm_fatal (string); /* pass up the string */
+ /* nuke the resolver */
+ if (resolveropen) CloseResolver ();
+ abort (); /* die horribly */
+}
diff --git a/imap/src/osdep/mac/linkage.c b/imap/src/osdep/mac/linkage.c
new file mode 100644
index 00000000..14490a0c
--- /dev/null
+++ b/imap/src/osdep/mac/linkage.c
@@ -0,0 +1,37 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Default driver linkage
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 13 June 1995
+ * Last Edited: 23 May 2007
+ */
+
+ mail_link (&imapdriver); /* link in the imap driver */
+ mail_link (&nntpdriver); /* link in the nntp driver */
+ mail_link (&pop3driver); /* link in the pop3 driver */
+ mail_link (&dummydriver); /* link in the dummy driver */
+ auth_link (&auth_ext); /* link in the ext authenticator */
+ auth_link (&auth_md5); /* link in the md5 authenticator */
+ auth_link (&auth_pla); /* link in the plain authenticator */
+ auth_link (&auth_log); /* link in the log authenticator */
+ mail_versioncheck (CCLIENTVERSION); /* validate version */
diff --git a/imap/src/osdep/mac/linkage.h b/imap/src/osdep/mac/linkage.h
new file mode 100644
index 00000000..7e0eccfa
--- /dev/null
+++ b/imap/src/osdep/mac/linkage.h
@@ -0,0 +1,36 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Default driver linkage
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 13 June 1995
+ * Last Edited: 30 August 2006
+ */
+
+extern DRIVER imapdriver;
+extern DRIVER nntpdriver;
+extern DRIVER pop3driver;
+extern DRIVER dummydriver;
+extern AUTHENTICATOR auth_ext;
+extern AUTHENTICATOR auth_log;
+extern AUTHENTICATOR auth_md5;
+extern AUTHENTICATOR auth_pla;
diff --git a/imap/src/osdep/mac/mtest.sit.hqx b/imap/src/osdep/mac/mtest.sit.hqx
new file mode 100644
index 00000000..4089c44f
--- /dev/null
+++ b/imap/src/osdep/mac/mtest.sit.hqx
@@ -0,0 +1,171 @@
+(This file must be converted with BinHex 4.0)
+:#@edCA0d,R0TG!"6594%8dP8)3#3""l`!!!"4[4p8dP8)3!#!!!Hm(*-BA8#bJ#
+3!aB"#Jd!$'edCA0d,VNZFR0bB`#3%j@,!*!8"h-!N!6rN!4bFh*M8P0&4!%!TFk
+UMDPEIVJ!!"f3!!#3"JEY!*!%`hJ!N!KIY3c!q+l(4rK4`['9YjP(D"GY*FH1(-X
+cXLBR0(,Fbb2([#i[,dqCK"rKGS3@iEFb3JQeji4&VeZ[1Tp``PGTPS4GCi46F[`
+)*dr*J*-T2h+-c2N46VMfC-$*Vi46-Z(N'*P4`L5F(#G2#5GiGT&MK"*'9LD!erG
+CjLAcHUq9(@&*RN@fj0Ne))``F[c)XC9`B,`kD)!("P!!B+%+X!"Ij9MdF'6TDbk
+eDS@rkBHYZjA")jZhqKjc,5mCd(FTrHqIGj@qeG-@lI&rjBp%)kdK&fVILa2,2T2
+)Nrd4Q1G"rm8Y@pe%8-SkIQSGk2H@qIe3dIJcJrAH1R9c&i2J#HpTbZ[82H$e3hN
+Gr+r1$kAiHS*IpmchqmF6`GH$qi)l!%)YfjZ9XS-II05ZY0Hd[$N205VQN@SCP1m
+)Y64Y8k#VBqraRH@Q)09I"'0XA!G`I`Z`"YrDNrJL[SBi1UackpVJ'9h60qKDB,'
+ZI9HZ+l((G5@JIUk!US1Z-MDXaLiE+K4V$)T"`eF&6AH$4eI`"F3!(Y2Kj)ABCH6
+#b6&ffHN-mSr[[a($&e6dMmla&Iiep%rHVrQhHi#(0JaJe+#i-PJ&%!J"P1Cl!ep
+YQ2T$jSEJi2Z0c+hDh!RbcmB"'fT`%@-'kLB+!r6'c4USMFh0fb68Y(llUk)VPLr
+Ed,hR3+5c[D2(piA[kG@V9rTHlZTmZbd5lH`ji1X1qelC&HhSl'V[kHkL2X*kMAQ
+aAlcJMXYmY&+R(%SV"d9HaYKaMM2'05VCQ-Q6Z-8I6HNNK$jrP@'C#fhdRG$`*Iq
+%&@Cib6p[RSd[23pm09LaTDQaQHE,TKFhEZ)8(AZN&j&$@cB%&dm`1'D23KiA`i4
+k2MCcJkB$!`hclN6Fi$P,$&'1lZ1NTbALF5'11FV9NaR+!fJ,m`$QQX5KhSi!*8i
+0H"P%&KpL"M!d&Sm2'A1eSI*J(ND+Nh2H4CbM[(`bCJHJ,N8-+K58K!`YM1)PBC(
+MF0C+`KM%5qDK'9kBG#fF$!KI)Lr"'SQFp+A[c%U3!1a*+dC(4bhbb%J@6`TE[35
+MM(Nm(X4XR"M-a)90**KqZ"l(V4#iN!!lHpDXM)NM`EB,Cr%6K!Nq4h*KmA)pB*I
+)`FD3!b4&J$+3!2'`Cjcj!bJL9'E`T&l56UjPreMAX3FQ,GJZd#hK9K%451'Ll4J
+[Q!K6EaUrTfIlcI)Grdj,Q,L8F($6rf6,&2hjQq2Ql%alc$FQUj8Y-F8G'4R*miZ
+6r#N"Nhme6(qhkM0Gm4bIGkdBkR++CrL5,4D&DElPi!UIMbP,)&-ICUi63Q#+l`6
+Mr0435I-cJc8P-0hc+6l8CL,C!fPlFU@j0YHYP5HlVH6iFMqck@,jbDiKPVai*iE
+U$Ik%rBbr0$p6)-ka&c4LTeHf'r90kr2kc1PIL8)!1m$[-EHZbBe6E*MC$6#jJ*S
+D5ADDE#fUXfa6S4$,$R1GEqqVFra(qSj!BB$kcE6AAIeme%lY-+NSX$"!#a3%h`&
+Q*k"SP1XGG*8[q[)kriiqr[N`q606mMGrMr6Ym@A(IIpC!`re#`28larr"3,URqc
+[!JVpml$rlr*6*KrShqr[f3f'4hk$ZAhbP(CPb2rrCL3-m3pHa&&p!ZrP1,mSX("
+(RI3Qr550fhJiM2F2G!"KiYhmHX("5F#`F3Cii$"NqG''q!Q&BH'NM`)*!fm-NMJ
+G#iK#50`+6KKLT8)))B%,06GHI$PV%q'LL"-iAR[``!-0+5$5!(j#))h,X24N"1M
+Z3b$mh!-#ETlA5jqQAj-I-'p$,*iGIjfpb3C00LpePpdq'U&fJp!9LaP)F'!L&(#
+B-5QFhja-i,Ii3Z#f[f4jF[adr42@M*(aS%(r(cJ$V%XHH6)M*j%XNB0M090iPKm
+LA!c@+El%Er(aKNG'k+Uq'6Iie,4jIhRpArhcH[pGRlRkjh#kDmVcVp8l0@+3!*-
+C-3dda3h982@pIFIkIZcpjA`I1q0HjqjcpETeZPLFAMG*`Fd9K!Ihi0Vp`bA5LG*
+hUZq8VJ`VcUpGcBK0cMcR"h&$E,bKHRr0IYFEPf+ABZGi0Kklp*kV`G9iZ[%FBMH
+rb60J'$JdYQJ98C5$i*'hHLqX$cj("j*GmUV[S,cU1p,C'LAqPjfYZeGLrMhLP&p
+!I"@iP2Q)8riXB-@A8,d'p(lS*8`T3Z`S(+*+ZVBKKRHdK,Ph`N#[r#[[UN(HZi,
+RmL$f+@NM9SlBFD(YENEXX0#SAS2B+U&4(82X+D&4I3Da#U&48BGBJp#BAipB3'K
+82B2B3PQ$$a&l6QJ8DBJY%4T&Rb$Q&4UHaBLY&4UP5a&E)65mYBK9#BhL5X4@#Sf
+5"BMjK)Dk#,(5*lDrdp8@m8ADSYhl)UeYr`%0!!GYG'9cG#kj3)#D#J&2H#J!&c6
++!*!$J!#3"!&EEhJT,!#3%4B!N!MrN!438Np+5d&)6!%!UKPEcE$CcD)!!(*+!*!
+'&`d!N!3HUJ#3#1[c$J#9qCRFXlGVCEICCSIR+lY0&eVRAMZqXX[00PXS2r@Df1A
+KkHA*"VNBTjER*iZH[pFm&Z8ZE"qcfblm[',l@,%Y@BlrBZI+'dRENNhB`TG6`U+
+%rm+qcS9IXHh##19fLpl##EGFH-DfXHfjAGJq`Xl0#&Z"UN(Pee&qXHelQ@6(IQ(
+lb#E,Nrd4YNmb@CI$RfG![9r(Q-!B#jFc&X+DS8F3ArE,60Kl5)j0,',#3EXF'lA
+Ff"lcGF6XpqJ3VDZ[VCVRG&G6"[,RPeil*ApGbRl6q$rjPKb*9Y(1D'@k1jk56ib
+Eb(5E,A,-M0L$VA*XbY0-Yr-@1CBlLqRfhkE85@)CT[d855D6KeK',QTi*XDBC34
+L5e$cI81&5RrqMB2Q!HmIb'5XY(jK24d42+cfld3SK%*fYiD6QclP6#EAG(r)aeP
+A9eeCik`c3H*Pl-%"%f1(rd'SBd9bYr%[Ke#EaC*,Y$c-`"ZCB(aPl&BS5Mh,BLD
+N0[#0c'FQ[EfTSphSF9G+mm35REeC$@ABQb1@KQ[EQd)a(q9fL4i("NpNfN2KQ-m
+SrQ-S(2)pT,1(,a$YBE%N(&VZLmCm8CdpQL8R8qpEN@Q2GN5E4pEl1X,aD,-2+*'
+J,iSDk,LB&@mhVGUh%i%T`9"c@lc&*aE90$@hqTTDI0'15bkj3XE)'FC`$f"%"*l
+1P"DI2aMbLE&Sh#I+hk8AU)Im6@dGbUP#l9$8Yd)"kNG#BZUa&")[D50+5NM)&9b
+(+Q,e$K1L)[VIr25C($[MAIaX"HGhq)+1&j!!e[2jJj+Bi9hm,'b0jr&KUjKP@[a
+prP3lUX&jI')FUX(jqP([8`qIabF--RE1iir'PSMXGrLk$$B+fa(Rm6-0[2G([SX
+r"YZFmrJ'bKlZfl[ie([83qIV4le(06c22e6lh(IaUAI([SY2[8XIeB'afAb9Ir$
+Df*FU$RcS#1"L*RJ,Ea'm-fRG)RJrZ&A`AVj,m,SFJVIm#X%lcb9ikpb#ep-PH+p
+#E1P2"'r!,RL[ALpi`bm+hXK8V%Z`hSce%F(lBEhJM4F*hQXl"Hp(eQ,G*(MAbRj
+Q$[XD'T2@ZpNUa&HaZ`@Ri-$UI+HX[rmZrkFkaRE&f&5@+e`QI%6A4AYGekUNX",
+a$f$r%H%$&&q992!JbRG&K)XDRZXL%808*$F5I@jPT8B8j'B8aRlIb[FAeAq55dH
+1L1MQrZMBlqI#N6heG3RbIm0h"PIj6mS9SQrVVEIZf)'ii2IIHL[fZKNcr(lX)9e
+Qc0LaJqPIj*p+"@Tqh$jQE2+q8I`V51f,88$DMjNaBmC[idIcNIFrX['[3-eISDr
+,(liA2GB#PGG%M!Y$j0I99(,S4!lH0"+9j69jk$RX1"[J-k&q&K)M!KJPmk2bp!&
+6A(i)A$f%aJ9#mk**M)h2!k&-a+NY%$!d(f"'9ic!cqF$K"F86-V-6b31L6b5G`0
+1M&'($4C$r*(IEi'!bf%jZAp2)MJRAj%!m+V!I53X6@!NNPIi-M$VJ*EN'9#9N!"
+a&YKjY(!C*kkY+8YVm&1V[[Q*Em&G4"8%*T5f,[#88kD'H-`rfpl-l%cSlTfdA9f
+"JHN(J%eSm*4@)PpM4k`P',DM)A3i[eTGJ6&"`DPF8NCpe"&Fi@Z-%4"P0#'KVZJ
+[*D-5cj9JHF(B(,XfiL-JBI-6ph@T+dUX9+M'@9P0&@T[#VEaI(3iraee4Fp0i$M
+,UZ%S8Bqf"80A0`@dM*j99r5IR0'bfSC5UT%ah0(LLr"D$Z!BeBaU'aTVU*EKMXE
+fTQDHHdChlq40kSUZ9+[Z+8@0""1kSbfiP#2TZlpQQDfZk&!&D9'*Qh46eMA0V8e
+4Z8%STi[9&If[j942(BHFSX&33-Rq5%lCFLGG'%BMSCk#N4UTJi!'FlG!4h'FNPU
+T&$KMB#kf"'2"F+LT$GjI0$b-H3($G!YKeP3ZTYbbDi+G[TDDF![[Um`"V"&D(HU
+G[+-pdDB)cfQ`AL29cPmJ9B,h"%-m&1`FlT@F&1Fi2G6j(E%QQ8Nbd5%AUL[i8-(
+a90C32aYL`ACH(TD"2Rj'AF'05MqA53XEUIGmSH9U3qZ"%&GA'!MMC-i"%Nd8C3+
+*ea'RPkXVE!)&TEc"6VT5lqI&(N)CUk&iURNZrKLR,d,jQ,S#3d'4UZ9F3M,(%FV
+(eC9GU+*i5ZTi,V&QcP[%Ek[9&GfJ-QCP3`PRc'!(&4QFM[2Ve*@09rUKYEkmK,M
+I&28hchBiC+i'`RTe"FjV+U%9HSK`lP&ApKk`"H%%+aYUUF2-`Bj`iqcCPhd3LN,
+1kAje"C+FIA"HE5A2rZT`F(CM&%M03dM[I4dTVL&p3ehC*"A*8pR!1c`@l'LFj5M
+NkEqTJp$a'"p6ZV5kJEM'l'p6q*CA,Zf!&T,VPY)iT)9HdcLY3aT(IdlMC*l91)C
+c'SI8d"Q0SkQK8aT(8d1R0)ljV-E*rSh'XIa+iea`9Z-S@ZL8aY'dd%Q0SfQKNaT
+Re$Q0-rUdaY'8d!Q0Ne*#ac@1TS41D"a0#Ch31"HHd$LTbPMIi(A9bcQQ#EB&8Yl
+)!#XVHU$IbG$d`+Y1aS"Zd*h9$4RRG-22[C'MZL&GXcGbh(&BhDmZMP6LeqTL3!Z
+3!,Vi$jq8e-8C,D#ULhHdJ+BZ6QN"FK[He!*(0&$+EAM94dkhicBF8D$pEJ-T8*C
+5S1Q2e-9E@Z!"ZL1#F*R4@q-TDk#qP#q[J"K)kc#&J8b@U#Yh(T@-+R"h"VJc'!$
+*DASST808jr%G6d*c(NrT%10C(8*+i)`1k9F#*cd*c@NiT5kbckN,bePeFF&[e-@
+)AkN,43fF8KHD'MMT5@KUi+4U'(e@0B`jTaSd2A"#0D6d`((9S1Q"%kT"d`-R9)2
+e,GA`5krK4R90H3d004jd,$UX28B*bA-BIkkZQ-03F#4*aJQ&9*b"MU8C$*NYbQT
+ULE0UI1hKk,9DjLPPmpjMbNE12-AUkJ6'+kcqpJ6'YQ$+$aQS@ATZYc@P3c3rC%L
+(r,IEQY)KQJ*kABHNU[JH051TE"%j+UD3!1mDA*QLCE4'AE8j0#"j1,H'I,(f$TR
+G"QG*hRYLPQ63&Cjdc"@QUC)hAH(GfKcDXY)&0930FdZm[4fha2bA-j`f@JNpVDk
+D%P*jhFKjr98(HI`aII2fr1Uc#2q-&rQ-E(I[HlDS+e!8$X09@*c$fS,Y`4Ke#8I
+Ulf&00mJp,+Jp$*ErT@iB8#+N'hkQ4)5[k"mS9eIS"Xk+eVdecK+`M$!'9)Mf+JQ
+hYiG$(T9*G32-C$M0T,SlD`pZ89GYbZ3(5TiQ18m!03haSqNSRfY91+SPIXR0U5Q
+6YlNj@mf*lLqMR*b45*[2dp4fp@Yk3R(dDKX@8%@cX)m&fhK@`PHZkhT%AD%LE(+
+218Y,FEHFN!$ME'Q*qMSk&VFe8G2)+KhG1%eGY5Q6[@J4Z9lBZf5pqEU[A&V@!08
+R'%Tp(Ea83afESpCerS*+F*f3!$dr(QbqZLADG-fV26#+L30B)c8XMkq6#j!!`6i
+BVH*95KlUQC'9S9LEHPrD-(Z3!0I-mFS@P[(jKV,P[K"RaHK3GA09T0+bKF4K@D@
+qjF(AFaUVpN&jC6ARM2*JQi`c@0YaDNB0C498Uk`'Ak!kh06bDNBADVPA1MP5DE#
+T,4`BcXQU)LfUj'bBY5JBDJPI-maBip8Z"'m5BjM"Ql&SQ&1'EiLab(e4r'*T!9@
+daKH+$fG2p`$)c&DfQ1G%69S'0ZBj(I1JbqVVZ3GG&Sf'Sj688EHD2'LjRq4+'FY
+4+CRC"R$)JjElU84@k3h0d@#%FMqDd@5Y6V@eR&NpiA#EaTH$[5F1e65EDRS-kb+
+e!Z@9Lk('K+cbB'G08ka9eA[2U5Xi@0&Yi&,jaS,QL+Tr"cfA+FFm&rZ3!1FbpCc
+RmVkcRN[H1FrPMmjj,[RR2*GTjcbALpr`A!Ca,MRVG8ar`qXBp$$HIml$X*rf-!T
+1HaL&Tcf-5dpl')l6dfdc6Xc1lPCA0[2Rr8$cE!1GFjQ+9+i`Lepe,BiLcG+3!$c
+92#G8qdK1+IllJ)SN9FXjK63JM[4[kSUEPBjaVj*6bVhjS-U8cJ8HkL*M8cc@fN%
+ia*3TR$QUHk[JQ!QR%3VYpI'A)`l1pDRjPlVDZKR8AC&`C)CfadP+"j!!krZD$QJ
+HdJ%d!hK'"kLhMlbM!l6E4dlT!2*pcmcjTLD#hjRce5D#6bQ'IZIhT',)1Z[5QRr
+MdQUZc5qFS04%i0Y1N!!b$haUcPHE"cijjjY@jrD4JFN,ZRhNcCRmkp8e0Imb`1R
+UY%JkJ[QAe&4,rrc,bDQ@LHG[5,P[VHBk(kfEiMUREjK9'H!ZcA0qaF91KcHhR&j
+P9L@P4XJl2U0'IMkYm[EG*cHQ[)k"1HDd$kSK6Hq%@qTZ!XhV',UE)$h"V1raQ`6
+)D98bT,FH8-F&fjXL-k1A+[HTT'R3&cbMY,bZa!Te6EN54eRpIqj6qBA#+(a$B@b
+kmKXdUc'1pqCiTp4!rVUHliGR2&681hq`KVK@4[e"U962kp`5LYV[HFYMj6a!DSC
+ij9rP'9A'Ce5*@SlRqcTbPSVXk,m8rjAkMQrm6UUq-NFa2A%8CQ+2ZcSI5qQZS6X
+AdR2l1[pjBfBUCdee$GqiFP`hV$QL'`CQ9G)6h,apG+SfrI$0fkN*0-f[16@"4MF
+3r1m%fMVY[L5icNlFfm$-(AK4$hLChK'3!+EJh[idd'qB`$I%#2,M6%akN!"U#)p
+,qVqSJrqI(QlqArf8"brKC8[r3[T,rV)Hf6b-Jjj%IZ`a#hm3'K2Um0BX["Z+ap$
+2Zr*CM(8*&HaQi4RX+i@2#jm4RXC,X&B,EKjG,r`9R4"fim`DS3+ReJZI%'l$UGh
+#-cLcFIM-TiHaEdlPTV`c5hk9dfmr[*XU$F&(6`f,+$&b4N3QcX9E+,[hCEiSl`d
+kC6p1f6Z8IDZbAk[X0bRlEmVlV&*j6hbLj(GfRl83'pkeaclq0#PATlRUVaq4HPJ
+VeXlT4ELUF,(8Bj3UqT)2jUh(lZYjeq&NPp56FEI8Bc)JpM,@ql(QZ3,ZcQ65(@C
+kpb*h*a0XFj+ET*l-Uk5[@CRBcYUNQlVhimaY!401ZHQ8&*pr`(a9K%NpqJF$KUU
+E!VT%T43ACjXlr51Q&bfTPFUN(N1[D2%DmSV&5jL)r0ejA9*mHP'Tdq2%fE[&DPY
+RPNR8*qbSdM+F,mblERS4&(8$6Yq#q-5mp8#V,kY'I+eB,HS$&UP(pe8pNCl&[er
+U-mk8&LDIG"l)bV(94X5%25,X(,9N91p@CamE(FP-f"GX3%9ehAmTA5A&q4[r@%@
+Im5Gj@m`Va8jcjprbPfi'6$McI0iGE&c"E(1AAlGiJa4I[-(p[H5"+`VBE+Uaehc
+PKJ*6paI&bBRL3L1kjr&!@dAIiH[ceSSjJHGXa9@ImYX6da+9diY+H+dbR`j8%lC
+D-IDN9V%H`aT8F+@iiV!l%F56bSaqID)P-3eRRVH&fF`rA9G3b@C9ljkFp'bSQLf
+m2k$V6NVab-MZKaG[%0'EPMVTS(p%SBJDe!If*#i'ETIpXe)FrB-B'd&p`k-6dBP
+cM$Pi)*G$l04p+@,CHG2Q,ET0IK[U[MIa2V(1jVCC[@ER(QD+#GGCGfkIrh"brde
+VGklckk5iM5AXK#-+LDQfZYUANP[&@YFGE25P3I$4Z-4BejIG3@B4DlYIGPK4J4H
+R,RjXfdbVc4Uk)Q#VG`C'BmeEk-69h&p&K$&8a*'I41eeKakYef)lqQ1pMfj+(UD
+kk6BkTM&,94!c+k,(Y5bRhZA[M$4h2icp!F)$`Xfph@b%%VZ6BKCAmP"JDAXV-24
+dVZ6(9Yhpmrdl,%ecLr`2iT6"[)aGQ$cXZ'LadlELd-lH6ffXkXqpYIF6F[C+I+B
+D4bbImP2MkPl*Ear2l`ANPT%mj,MS3j4EV2G6[F$B@(@C-hPijVh5Kj%91N4iTZc
+(9[EBYfGD!`jdbG6!L(UR2eI19pMMY5U4(Zm)*E,G#fi@RNA''p'0P-(M9$&aa8m
+2Lh0qHVMU$V"@[L1Ij3GX8r1Cp8TAb3pCcU0lQ2ka+af(jKDjaSrH`bjh(1VH0pr
+ThiXH,qEm8qarN!$Rd)-FHU,*'10j6,JrRehZcjf&E0`2"#bl2REA(8c[dp[biF+
+QN!"h!IF[a,T)cXDZjpF9ER[q4ZEUBSAEN8pGP$QXK9CGmDVNppDK0m5CSlbMEG1
+BIZ0DacDUKQ-lVm4$bEhH@@*1BS&BI"64%T31-Ph!1K1Sk)&4lNJ1)e6TS'dhF2H
+*FjKC,2EVQJ`5[E#4&4BPl[YmZFe8@(6(ClpFIYIDJ!&m2pdV`R[3#`8qLYUmSa(
+9+9'V9irGT*+qj(EE@LQHZ%q+lcq!$Pr"G+DY,%2k-`E@!mFAPG5@PXePh3QaX3k
+m,jUNH2k+[SffkNr[qZlU*lBpZS[(9f`ra)TY-ke@a0EdhXc2q*8cTSdpIF[i#Bp
+b)UHhTmr$6lL8%hS$[9BQKaJQq6Q0lp$ab5eH2AD2%qZJXaSGeTR@cBXA2CcXUh)
+&$N[la%2+Q9[kcr3TCe`Kmdh1GX21BZFqTL[-SE13!+i&6&rJC(TE!6-90%,EIDm
+)0Q1Hi(CprB&mGV(J,R[S'lY+h5kADqZfR@ZUGl-Lff4NrJVUT9"jl$-i[rkYmbY
+&i56#p*2R,DN-UVU@6DaD[Fa5fP@k'LG&l1+P8+jX($m4aeNc2fR!UEJG,mj-*Zr
+UDMEB)0fPq1BEPPUkYVQ,(Fa9l#k@iRIGX(5#RFISP,[L#ZDZq15f3U1l`Q'8iY3
+4ckjpGKe3$R4YfhU$)f2l9RZ'Zq+4VHk+hKYmTSeVPZSTjiCLlh-ICD3)`4@'Z8E
+`a'451AR&L'GhIc&[XTLGRe&Im9*&IF8FShIF!-VDqJV$GAMQhq%&'`TBpjF+*d2
+pCIUI)Tljjl,E+[`6aBa%-E,iR$J'D%X3fCMI)-j,6)X)k"IplC0%%mkjmYdDaLa
+aRTMKY3jNDDZ[%)Y&G-1U``8QU,cU*lBQl0fIRrqYj*1hIMf[QZFbVkS-UR-+H""
+pjLMUhPI5Gc!(RE3CXCF4ZaHa,b$f)m4Z41`@a2iGX5rdDqBF-Cq0#jLaQaM)PIT
+B$$J4i1`(6Jca&X5p5Mb+Z!I9#+2A-4H!Phl1BFZK6)X'P1Pd)2UT'YLl+([Xje"
+&X(G3"E#rQ$,#hLBf5[&#iIEbUHk)jIC*K8+LS[#3!(52q$"f"m9[bQGYVFb8Q&`
+SL*@*#SPH*-BrdXA*r3'p@1bk+AAU$prJ+hU&X5pX[UJ04bEU2XP'ZCd0j"eZ*#m
+%qlqCaqq1CTX@9GH5ZhFh`M30XVe"RY0mK03(jKUH*(m%QFdK*`6lFR*!X+p[Z,+
+'iPH95189,&0B*NpR#(H9ZJKIZ,r848kam%eb)A$qm9,A!Z$S*TAMdQVBIhjH,+V
+Cf"I`*%GkTjmP&c&i6"6,f)mBhYe(XFaka1!98Xc`AF4!9K6,rL&HEiKhr9'XaBT
+c5)ZK9LdVF@iPjNB`#p&b(@+GF!U3!(2,HX3LQ2j!R9YZ4Lb'qBkPL(d'X6E8'Vh
+5mJA%FKKHG*M,@Zj%E)HFAmL!'0L'amb)i9Q$2$B#X30+E$4Lqr*I3PE--!@a2EB
+rTZG''UC4RA1R`,YMKKUFHkCM*Hi9CKB"XDFQ2B@EFTRK`iKpQePi[Dp(,5Nb&l&
+1a(*B&krhpi'a5ijPASAB6UEMHBp$V"$+Rf)6%)0DjV&*r!@-Q6aQ3mc%$$b@KaM
+F(Kkl'$',NSGG',YRVj+(!qHQ+RR-3LaAb@-1BK192+j!6&6bF!(lN!"F'`YHeEK
+RIfEle8fYEB2[A-fZ#erMLpD9d*0B"pl(UMkP0I8+9U()@"I&&&Md@Mh0K'A94F1
+aF$0Z,XqLDlNaF1$Vb#S4Ui0,Si#Ji5H4$`Vm2`!5FJ!!!3#3!`%8!*!$&!#3!c)
+U9*!(+LVX!!!U+P53#Z`!!#T8N!-*EA4PFh3ZFfPd!J#3!e0*9%46593K!3!!T!)
+-!*!$!3!!8dP84&0*9#%"!!#N!J`!N"+`fEpN!!!Hm!!!!8C892q3"953!q`!N!4
+8N!Er9*!&l!!!+P53"Iq3!e53!q`!N!-U9*!+l1`!N!-Ul&53"qcX!*!&l!$X9*!
+$l*!$!*!+l*!$!*!,!3#3!rq3#J#3#"!!+!!@!48#!3#3"J-!N!-"!*!$!43!N!-
+8!*!$-J&20H`KbJ#3!a`!-J!!8f9dC`#3!`S!!2rr!*!%!8pi(2'-:
diff --git a/imap/src/osdep/mac/nl_mac.c b/imap/src/osdep/mac/nl_mac.c
new file mode 100644
index 00000000..6c42cd08
--- /dev/null
+++ b/imap/src/osdep/mac/nl_mac.c
@@ -0,0 +1,74 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Mac newline routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 January 1992
+ * Last Edited: 30 August 2006
+ */
+
+/* Copy string with CRLF newlines
+ * Accepts: destination string
+ * pointer to size of destination string buffer
+ * source string
+ * length of source string
+ * Returns: length of copied string
+ */
+
+unsigned long strcrlfcpy (unsigned char **dst,unsigned long *dstl,
+ unsigned char *src,unsigned long srcl)
+{
+ long i,j;
+ unsigned char c,*d = src;
+ if (*dst) { /* destination provided? */
+ if ((i = srcl * 2) > *dstl) /* calculate worst-case situation */
+ for (i = j = srcl; j; --j) if (*d++ == '\015') i++;
+ /* flush destination buffer if too small */
+ if (i > *dstl) fs_give ((void **) dst);
+ }
+ /* make a new buffer if needed */
+ if (!*dst) *dst = (char *) fs_get ((*dstl = i) + 1);
+ d = *dst; /* destination string */
+ if (srcl) do { /* copy string */
+ c = *d++ = *src++; /* copy character */
+ /* append line feed to bare CR */
+ if ((c == '\015') && (*src != '\012')) *d++ = '\012';
+ } while (--srcl);
+ *d = '\0'; /* tie off destination */
+ return d - *dst; /* return length */
+}
+
+
+/* Length of string after strcrlfcpy applied
+ * Accepts: source string
+ * Returns: length of string
+ */
+
+unsigned long strcrlflen (STRING *s)
+{
+ unsigned long pos = GETPOS (s);
+ unsigned long i = SIZE (s);
+ unsigned long j = i;
+ while (j--) if ((SNX (s) == '\015') && ((CHR (s) != '\012') || !j)) i++;
+ SETPOS (s,pos); /* restore old position */
+ return i;
+}
diff --git a/imap/src/osdep/mac/os_mac.c b/imap/src/osdep/mac/os_mac.c
new file mode 100644
index 00000000..156eb5c3
--- /dev/null
+++ b/imap/src/osdep/mac/os_mac.c
@@ -0,0 +1,82 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Macintosh version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 January 1992
+ * Last Edited: 30 August 2006
+ */
+
+
+/* This is a totally new operating-system dependent module for the Macintosh,
+ * written using THINK C on my Mac PowerBook-100 in my free time.
+ * Unlike earlier efforts, this version requires no external TCP library. It
+ * also takes advantage of the Map panel in System 7 for the timezone.
+ */
+
+/* PPC cretins broke the MachineLocation struct */
+
+#define gmtFlags u
+
+#include <limits.h>
+#include <time.h>
+#include <stdio.h>
+#include <fcntl.h>
+#define tcp_port MacTCP_port
+#include <MacTCPCommonTypes.h>
+#include <AddressXlation.h>
+#include <TCPPB.h>
+#include <Desk.h>
+#include <Devices.h>
+#include <Errors.h>
+#include <Files.h>
+#include <Fonts.h>
+#include <Menus.h>
+#include <Script.h>
+#include <ToolUtils.h>
+#include <Windows.h>
+#undef tcp_port
+
+#include "tcp_mac.h" /* must be before osdep.h */
+#include "mail.h"
+#include "osdep.h"
+#include "misc.h"
+
+static short TCPdriver = 0; /* MacTCP's reference number */
+short resolveropen = 0; /* TCP's resolver open */
+
+
+#include "env_mac.c"
+#include "fs_mac.c"
+#include "ftl_mac.c"
+#include "nl_mac.c"
+#include "tcp_mac.c"
+
+#define open(a,b,c) open (a,b)
+#define server_login(user,pass,authuser,argc,argv) NIL
+#define authserver_login(user,authuser,argc,argv) NIL
+#define myusername() "" /* dummy definition to prevent build errors */
+#define MD5ENABLE ""
+
+#include "auth_md5.c"
+#include "auth_pla.c"
+#include "auth_log.c"
diff --git a/imap/src/osdep/mac/os_mac.h b/imap/src/osdep/mac/os_mac.h
new file mode 100644
index 00000000..5cdad642
--- /dev/null
+++ b/imap/src/osdep/mac/os_mac.h
@@ -0,0 +1,69 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Macintosh version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 January 1992
+ * Last Edited: 30 August 2006
+ */
+
+
+/* This is a totally new operating-system dependent module for the Macintosh,
+ * written using THINK C on my Mac PowerBook-100 in my free time.
+ * Unlike earlier efforts, this version requires no external TCP library. It
+ * also takes advantage of the Map panel in System 7 for the timezone.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <types.h>
+#include <unix.h>
+#ifndef noErr
+#include <Desk.h>
+#include <Devices.h>
+#include <Errors.h>
+#include <Events.h>
+#include <Fonts.h>
+#include <Memory.h>
+#include <Menus.h>
+#include <ToolUtils.h>
+#include <Windows.h>
+#endif
+
+#define L_SET SEEK_SET
+#define L_INCR SEEK_CUR
+#define L_XTND SEEK_END
+
+extern short resolveropen; /* make this global so caller can sniff */
+
+#include "env_mac.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+
+#define TCPDRIVER "\p.IPP"
+
+#define gethostid clock
+
+long wait (void);
+long random (void);
diff --git a/imap/src/osdep/mac/osdep.h b/imap/src/osdep/mac/osdep.h
new file mode 100644
index 00000000..9755c565
--- /dev/null
+++ b/imap/src/osdep/mac/osdep.h
@@ -0,0 +1,29 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Macintosh version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 13 June 1995
+ * Last Edited: 30 August 2006
+ */
+
+#include "os_mac.h"
diff --git a/imap/src/osdep/mac/pmatch.c b/imap/src/osdep/mac/pmatch.c
new file mode 100644
index 00000000..95a0bb86
--- /dev/null
+++ b/imap/src/osdep/mac/pmatch.c
@@ -0,0 +1,89 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: IMAP Wildcard Matching Routines (case-independent)
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 June 2000
+ * Last Edited: 30 August 2006
+ */
+
+/* Wildcard pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if pattern matches base, else NIL
+ */
+
+long pmatch_full (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ /* % at end, OK if no inferiors */
+ if (!pat[1]) return (delim && strchr (s,delim)) ? NIL : T;
+ /* scan remainder of string until delimiter */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while ((*s != delim) && *s++);
+ break;
+ case '*': /* match 0 or more characters */
+ if (!pat[1]) return T; /* * at end, unconditional match */
+ /* scan remainder of string */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while (*s++);
+ break;
+ case '\0': /* end of pattern */
+ return *s ? NIL : T; /* success if also end of base */
+ default: /* match this character */
+ return compare_uchar (*pat,*s) ? NIL : pmatch_full (s+1,pat+1,delim);
+ }
+ return NIL;
+}
+
+/* Directory pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if base is a matching directory of pattern, else NIL
+ */
+
+long dmatch (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ if (!*s) return T; /* end of base means have a subset match */
+ if (!*++pat) return NIL; /* % at end, no inferiors permitted */
+ /* scan remainder of string until delimiter */
+ do if (dmatch (s,pat,delim)) return T;
+ while ((*s != delim) && *s++);
+ if (*s && !s[1]) return T; /* ends with delimiter, must be subset */
+ return dmatch (s,pat,delim);/* do new scan */
+ case '*': /* match 0 or more characters */
+ return T; /* unconditional match */
+ case '\0': /* end of pattern */
+ break;
+ default: /* match this character */
+ if (*s) return compare_uchar (*pat,*s) ? NIL : dmatch (s+1,pat+1,delim);
+ /* end of base, return if at delimiter */
+ else if (*pat == delim) return T;
+ break;
+ }
+ return NIL;
+}
diff --git a/imap/src/osdep/mac/tcp_mac.c b/imap/src/osdep/mac/tcp_mac.c
new file mode 100644
index 00000000..8d39d4f3
--- /dev/null
+++ b/imap/src/osdep/mac/tcp_mac.c
@@ -0,0 +1,557 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Macintosh TCP/IP routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 January 1992
+ * Last Edited: 13 January 2008
+ */
+
+
+/* This is a totally new operating-system dependent module for the Macintosh,
+ * written using THINK C on my Mac PowerBook-100 in my free time.
+ * Unlike earlier efforts, this version requires no external TCP library. It
+ * also takes advantage of the Map panel in System 7 for the timezone.
+ */
+
+static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */
+static long ttmo_open = 75; /* TCP timeouts, in seconds */
+static long ttmo_read = 0;
+static long ttmo_write = 0;
+static long ttmo_close = 0;
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd);
+
+/* TCP/IP manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tcp_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_TIMEOUT:
+ tmoh = (tcptimeout_t) value;
+ case GET_TIMEOUT:
+ ret = (void *) tmoh;
+ break;
+ case SET_OPENTIMEOUT:
+ ttmo_open = (long) value;
+ case GET_OPENTIMEOUT:
+ ret = (void *) ttmo_open;
+ break;
+ case SET_READTIMEOUT:
+ ttmo_read = (long) value;
+ case GET_READTIMEOUT:
+ ret = (void *) ttmo_read;
+ break;
+ case SET_WRITETIMEOUT:
+ ttmo_write = (long) value;
+ case GET_WRITETIMEOUT:
+ ret = (void *) ttmo_write;
+ break;
+ case SET_CLOSETIMEOUT:
+ ttmo_close = (long) value;
+ case GET_CLOSETIMEOUT:
+ ret = (void *) ttmo_close;
+ break;
+ }
+ return ret;
+}
+
+/* TCP/IP open
+ * Accepts: host name
+ * contact service name
+ * contact port number
+ * Returns: TCP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream;
+ struct hostInfo hst;
+ struct TCPCreatePB *createpb;
+ struct TCPOpenPB *openpb;
+ char *s;
+ unsigned long i,j,k,l;
+ char tmp[MAILTMPLEN];
+ port &= 0xffff; /* erase flags */
+ /* init MacTCP */
+ if (!TCPdriver && OpenDriver (TCPDRIVER,&TCPdriver)) {
+ mm_log ("Can't init MacTCP",ERROR);
+ return NIL;
+ }
+ if (!resolveropen && OpenResolver (NIL)) {
+ mm_log ("Can't init domain resolver",ERROR);
+ return NIL;
+ }
+ resolveropen = T; /* note resolver open now */
+ /* domain literal? */
+ if (host[0] == '[' && host[strlen (host)-1] == ']') {
+ if (((i = strtoul (s = host+1,&s,10)) <= 255) && *s++ == '.' &&
+ ((j = strtoul (s,&s,10)) <= 255) && *s++ == '.' &&
+ ((k = strtoul (s,&s,10)) <= 255) && *s++ == '.' &&
+ ((l = strtoul (s,&s,10)) <= 255) && *s++ == ']' && !*s) {
+ hst.addr[0] = (i << 24) + (j << 16) + (k << 8) + l;
+ hst.addr[1] = 0; /* only one address to try! */
+ sprintf (hst.cname,"[%ld.%ld.%ld.%ld]",i,j,k,l);
+ }
+ else {
+ sprintf (tmp,"Bad format domain-literal: %.80s",host);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ }
+
+ else { /* look up host name */
+ if (!tcp_dns_upp) tcp_dns_upp = NewResultProc (tcp_dns_result);
+ if (StrToAddr (host,&hst,tcp_dns_upp,NIL)) {
+ while (hst.rtnCode == cacheFault && wait ());
+ /* kludge around MacTCP bug */
+ if (hst.rtnCode == outOfMemory) {
+ mm_log ("Re-initializing domain resolver",WARN);
+ CloseResolver (); /* bop it on the head and try again */
+ OpenResolver (NIL); /* note this will leak 12K */
+ StrToAddr (host,&hst,tcp_dns_upp,NIL);
+ while (hst.rtnCode == cacheFault && wait ());
+ }
+ if (hst.rtnCode) { /* still have error status? */
+ switch (hst.rtnCode) { /* analyze return */
+ case nameSyntaxErr:
+ s = "Syntax error in name";
+ break;
+ case noResultProc:
+ s = "No result procedure";
+ break;
+ case noNameServer:
+ s = "No name server found";
+ break;
+ case authNameErr:
+ s = "Host does not exist";
+ break;
+ case noAnsErr:
+ s = "No name servers responding";
+ break;
+ case dnrErr:
+ s = "Name server returned an error";
+ break;
+ case outOfMemory:
+ s = "Not enough memory to resolve name";
+ break;
+ case notOpenErr:
+ s = "Driver not open";
+ break;
+ default:
+ s = NIL;
+ break;
+ }
+ if (s) sprintf (tmp,"%s: %.80s",s,host);
+ else sprintf (tmp,"Unknown resolver error (%ld): %.80s",
+ hst.rtnCode,host);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ }
+ }
+
+ /* create local TCP/IP stream */
+ stream = (TCPSTREAM *) fs_get (sizeof (TCPSTREAM));
+ stream->ictr = 0; /* initialize input */
+ stream->pb.ioCRefNum = TCPdriver;
+ createpb = &stream->pb.csParam.create;
+ openpb = &stream->pb.csParam.open;
+ stream->pb.csCode = TCPCreate;/* create a TCP stream */
+ /* set up buffer for TCP */
+ createpb->rcvBuffLen = 4*BUFLEN;
+ createpb->rcvBuff = fs_get (createpb->rcvBuffLen);
+ createpb->notifyProc = NIL; /* no special notify procedure */
+ createpb->userDataPtr = NIL;
+ if (PBControlSync ((ParmBlkPtr) &stream->pb))
+ fatal ("Can't create TCP stream");
+ /* open TCP connection */
+ stream->pb.csCode = TCPActiveOpen;
+ openpb->ulpTimeoutValue = (int) ttmo_open;
+ openpb->ulpTimeoutAction = T;
+ openpb->validityFlags = timeoutValue|timeoutAction;
+ /* remote host (should try all) */
+ openpb->remoteHost = hst.addr[0];
+ openpb->remotePort = port; /* caller specified remote port */
+ openpb->localPort = 0; /* generate a local port */
+ openpb->tosFlags = 0; /* no special TOS */
+ openpb->precedence = 0; /* no special precedence */
+ openpb->dontFrag = 0; /* allow fragmentation */
+ openpb->timeToLive = 255; /* standards say 60, UNIX uses 255 */
+ openpb->security = 0; /* no special security */
+ openpb->optionCnt = 0; /* no IP options */
+ openpb->options[0] = 0;
+ openpb->userDataPtr = NIL; /* no special data pointer */
+ PBControlAsync ((ParmBlkPtr) &stream->pb);
+ while (stream->pb.ioResult == inProgress && wait ());
+ if (stream->pb.ioResult) { /* got back error status? */
+ sprintf (tmp,"Can't connect to %.80s,%ld",hst.cname,port);
+ mm_log (tmp,ERROR);
+ /* nuke the buffer */
+ stream->pb.csCode = TCPRelease;
+ createpb->userDataPtr = NIL;
+ if (PBControlSync ((ParmBlkPtr) &stream->pb)) fatal ("TCPRelease lossage");
+ /* free its buffer */
+ fs_give ((void **) &createpb->rcvBuff);
+ fs_give ((void **) &stream);/* and the local stream */
+ return NIL;
+ }
+
+ /* copy host names for later use */
+ stream->host = cpystr (hst.cname);
+ /* tie off trailing dot */
+ stream->host[strlen (stream->host) - 1] = '\0';
+ /* the open gave us our address */
+ i = (openpb->localHost >> 24) & 0xff;
+ j = (openpb->localHost >> 16) & 0xff;
+ k = (openpb->localHost >> 8) & 0xff;
+ l = openpb->localHost & 0xff;
+ sprintf (tmp,"[%ld.%ld.%ld.%ld]",i,j,k,l);
+ stream->localhost = cpystr (tmp);
+ if (!myLocalHost) myLocalHost = cpystr (tmp);
+ stream->port = port; /* copy port number */
+ return stream;
+}
+
+
+/* Called when have return from DNS
+ * Accepts: host info pointer
+ * user data pointer
+ */
+
+ResultUPP tcp_dns_upp = NIL;
+
+pascal void tcp_dns_result (struct hostInfo *hostInfoPtr,char *userDataPtr)
+{
+ /* dummy routine */
+}
+
+/* TCP/IP authenticated open
+ * Accepts: NETMBX specifier
+ * service name
+ * returned user name buffer
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL; /* no authenticated opens on Mac */
+}
+
+/* TCP receive line
+ * Accepts: TCP stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *tcp_getline (TCPSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = tcp_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = tcp_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* TCP receive line or partial line
+ * Accepts: TCP stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!tcp_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!tcp_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* TCP/IP receive buffer
+ * Accepts: TCP/IP stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer)
+{
+ unsigned long n;
+ char *bufptr = buffer;
+ while (size > 0) { /* until request satisfied */
+ if (!tcp_getdata (stream)) return NIL;
+ n = min (size,stream->ictr);/* number of bytes to transfer */
+ /* do the copy */
+ memcpy (bufptr,stream->iptr,n);
+ bufptr += n; /* update pointer */
+ stream->iptr +=n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -=n;
+ }
+ bufptr[0] = '\0'; /* tie off string */
+ return T;
+}
+
+
+/* TCP/IP receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getdata (TCPSTREAM *stream)
+{
+ time_t t = time (0);
+ struct TCPReceivePB *receivepb = &stream->pb.csParam.receive;
+ struct TCPAbortPB *abortpb = &stream->pb.csParam.abort;
+ while (stream->ictr < 1) { /* if nothing in the buffer */
+ time_t tl = time (0);
+ stream->pb.csCode = TCPRcv; /* receive TCP data */
+ receivepb->commandTimeoutValue = (int) ttmo_read;
+ receivepb->rcvBuff = stream->ibuf;
+ receivepb->rcvBuffLen = BUFLEN;
+ receivepb->secondTimeStamp = 0;
+ receivepb->userDataPtr = NIL;
+ PBControlAsync ((ParmBlkPtr) &stream->pb);
+ while (stream->pb.ioResult == inProgress && wait ());
+ if (stream->pb.ioResult) { /* punt if got an error */
+ time_t tc = time (0);
+ if ((stream->pb.ioResult == commandTimeout) && tmoh &&
+ ((*tmoh) (tc - t,tc - tl, stream->host))) continue;
+ /* nuke connection */
+ stream->pb.csCode = TCPAbort;
+ abortpb->userDataPtr = NIL;
+ PBControlSync ((ParmBlkPtr) &stream->pb);
+ return NIL;
+ }
+ stream->iptr = stream->ibuf;/* point at TCP buffer */
+ stream->ictr = receivepb->rcvBuffLen;
+ }
+ return T;
+}
+
+/* TCP/IP send string as record
+ * Accepts: TCP/IP stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long tcp_soutr (TCPSTREAM *stream,char *string)
+{
+ return tcp_sout (stream,string,(unsigned long) strlen (string));
+}
+
+
+/* TCP/IP send string
+ * Accepts: TCP/IP stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
+{
+ struct TCPSendPB *sendpb = &stream->pb.csParam.send;
+ struct TCPAbortPB *abortpb = &stream->pb.csParam.abort;
+ struct {
+ unsigned short length;
+ Ptr buffer;
+ unsigned short trailer;
+ } wds;
+ while (wds.length = (size > (unsigned long) 32768) ? 32768 : size) {
+ wds.buffer = string; /* buffer */
+ wds.trailer = 0; /* tie off buffer */
+ size -= wds.length; /* this many words will be output */
+ string += wds.length;
+ stream->pb.csCode = TCPSend;/* send TCP data */
+ sendpb->ulpTimeoutValue = (int) ttmo_write;
+ sendpb->ulpTimeoutAction = 0;
+ sendpb->validityFlags = timeoutValue|timeoutAction;
+ sendpb->pushFlag = T; /* send the data now */
+ sendpb->urgentFlag = NIL; /* non-urgent data */
+ sendpb->wdsPtr = (Ptr) &wds;
+ sendpb->userDataPtr = NIL;
+ PBControlAsync ((ParmBlkPtr) &stream->pb);
+ while (stream->pb.ioResult == inProgress && wait ());
+ if (stream->pb.ioResult) { /* punt if got an error */
+ /* nuke connection */
+ stream->pb.csCode =TCPAbort;
+ abortpb->userDataPtr = NIL;
+ PBControlSync ((ParmBlkPtr) &stream->pb);
+ return NIL;
+ }
+ }
+ return T; /* success */
+}
+
+/* TCP/IP close
+ * Accepts: TCP/IP stream
+ */
+
+void tcp_close (TCPSTREAM *stream)
+{
+ struct TCPClosePB *closepb = &stream->pb.csParam.close;
+ struct TCPCreatePB *createpb = &stream->pb.csParam.create;
+ stream->pb.csCode = TCPClose; /* close TCP stream */
+ closepb->ulpTimeoutValue = (int) ttmo_close;
+ closepb->ulpTimeoutAction = 0;
+ closepb->validityFlags = timeoutValue|timeoutAction;
+ closepb->userDataPtr = NIL;
+ PBControlAsync ((ParmBlkPtr) &stream->pb);
+ while (stream->pb.ioResult == inProgress && wait ());
+ stream->pb.csCode =TCPRelease;/* flush the buffers */
+ createpb->userDataPtr = NIL;
+ if (PBControlSync ((ParmBlkPtr) &stream->pb)) fatal ("TCPRelease lossage");
+ /* free its buffer */
+ fs_give ((void **) &createpb->rcvBuff);
+ /* flush host names */
+ fs_give ((void **) &stream->host);
+ fs_give ((void **) &stream->localhost);
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+/* TCP/IP return host for this stream
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_host (TCPSTREAM *stream)
+{
+ return stream->host; /* return host name */
+}
+
+
+/* TCP/IP return remote host for this stream
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_remotehost (TCPSTREAM *stream)
+{
+ return stream->host; /* return host name */
+}
+
+
+/* TCP/IP return port for this stream
+ * Accepts: TCP/IP stream
+ * Returns: port number for this stream
+ */
+
+unsigned long tcp_port (TCPSTREAM *stream)
+{
+ return stream->port; /* return port number */
+}
+
+
+/* TCP/IP return local host for this stream
+ * Accepts: TCP/IP stream
+ * Returns: local host name for this stream
+ */
+
+char *tcp_localhost (TCPSTREAM *stream)
+{
+ return stream->localhost; /* return local host name */
+}
+
+/* TCP/IP return canonical form of host name
+ * Accepts: host name
+ * Returns: canonical form of host name
+ */
+
+char *tcp_canonical (char *name)
+{
+ int i;
+ struct hostInfo hst;
+ /* look like domain literal? */
+ if (name[0] == '[' && name[i = (strlen (name))-1] == ']') return name;
+ if (StrToAddr (name,&hst,tcp_dns_upp,NIL)) {
+ while (hst.rtnCode == cacheFault && wait ());
+ /* kludge around MacTCP bug */
+ if (hst.rtnCode == outOfMemory) {
+ mm_log ("Re-initializing domain resolver",WARN);
+ CloseResolver (); /* bop it on the head and try again */
+ OpenResolver (NIL); /* note this will leak 12K */
+ StrToAddr (name,&hst,tcp_dns_upp,NIL);
+ while (hst.rtnCode == cacheFault && wait ());
+ }
+ /* still have error status? */
+ if (hst.rtnCode) return name;
+ }
+ return hst.cname; /* success */
+}
+
+
+/* TCP/IP get client host name (server calls only)
+ * Returns: client host name
+ */
+
+char *tcp_clienthost ()
+{
+ return "UNKNOWN";
+}
diff --git a/imap/src/osdep/mac/tcp_mac.h b/imap/src/osdep/mac/tcp_mac.h
new file mode 100644
index 00000000..7e9b4a72
--- /dev/null
+++ b/imap/src/osdep/mac/tcp_mac.h
@@ -0,0 +1,49 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Macintosh TCP/IP routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 January 1992
+ * Last Edited: 30 August 2006
+ */
+
+
+/* TCP input buffer */
+
+#define BUFLEN (size_t) 8192 /* TCP input buffer */
+
+
+/* TCP I/O stream */
+
+#define TCPSTREAM struct tcp_stream
+TCPSTREAM {
+ char *host; /* host name */
+ unsigned long port; /* port number */
+ char *localhost; /* local host name */
+ struct TCPiopb pb; /* MacTCP parameter block */
+ long ictr; /* input counter */
+ char *iptr; /* input pointer */
+ char ibuf[BUFLEN]; /* input buffer */
+};
+
+extern ResultUPP tcp_dns_upp;
+pascal void tcp_dns_result (struct hostInfo *hostInfoPtr,char *userDataPtr);
diff --git a/imap/src/osdep/nt/drivers.bat b/imap/src/osdep/nt/drivers.bat
new file mode 100755
index 00000000..0964f537
--- /dev/null
+++ b/imap/src/osdep/nt/drivers.bat
@@ -0,0 +1,33 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Driver Linkage Generator for DOS/NT
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 11 October 1989
+REM Last Edited:30 August 2006
+
+REM Erase old driver linkage
+IF EXIST LINKAGE.* DEL LINKAGE.*
+
+REM Now define the new list
+FOR %%D IN (%1 %2 %3 %4 %5 %6 %7 %8 %9) DO CALL DRIVRAUX %%D
+
+EXIT 0
diff --git a/imap/src/osdep/nt/drivraux.bat b/imap/src/osdep/nt/drivraux.bat
new file mode 100755
index 00000000..30649a78
--- /dev/null
+++ b/imap/src/osdep/nt/drivraux.bat
@@ -0,0 +1,30 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Driver Linkage Generator auxillary for NT/Win9x
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 11 October 1989
+REM Last Edited:30 August 2006
+
+ECHO extern DRIVER %1driver; >> LINKAGE.H
+REM Note the introduction of the caret to quote the ampersand in NT
+if "%OS%" == "Windows_NT" ECHO mail_link (^&%1driver); /* link in the %1 driver */ >> LINKAGE.C
+if "%OS%" == "" ECHO mail_link (&%1driver); /* link in the %1 driver */ >> LINKAGE.C
diff --git a/imap/src/osdep/nt/dummy.h b/imap/src/osdep/nt/dummy.h
new file mode 100644
index 00000000..32650e06
--- /dev/null
+++ b/imap/src/osdep/nt/dummy.h
@@ -0,0 +1,43 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 9 May 1991
+ * Last Edited: 30 August 2006
+ */
+
+/* Exported function prototypes */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat);
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long scan_contents (DRIVER *dtb,char *name,char *contents,
+ unsigned long csiz,unsigned long fsiz);
+long dummy_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz);
+long dummy_create (MAILSTREAM *stream,char *mailbox);
+long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode);
+long dummy_delete (MAILSTREAM *stream,char *mailbox);
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname);
+char *dummy_file (char *dst,char *name);
+long dummy_canonicalize (char *tmp,char *ref,char *pat);
diff --git a/imap/src/osdep/nt/dummynt.c b/imap/src/osdep/nt/dummynt.c
new file mode 100644
index 00000000..b44d271f
--- /dev/null
+++ b/imap/src/osdep/nt/dummynt.c
@@ -0,0 +1,724 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines for NT
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 24 May 1993
+ * Last Edited: 1 June 2007
+ */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <direct.h>
+#include "mail.h"
+#include "osdep.h"
+#include <sys\stat.h>
+#include <dos.h>
+#include "dummy.h"
+#include "misc.h"
+
+/* Function prototypes */
+
+DRIVER *dummy_valid (char *name);
+void *dummy_parameters (long function,void *value);
+void dummy_list_work (MAILSTREAM *stream,char *dir,char *pat,char *contents,
+ long level);
+long dummy_listed (MAILSTREAM *stream,char delimiter,char *name,
+ long attributes,char *contents);
+long dummy_subscribe (MAILSTREAM *stream,char *mailbox);
+MAILSTREAM *dummy_open (MAILSTREAM *stream);
+void dummy_close (MAILSTREAM *stream,long options);
+long dummy_ping (MAILSTREAM *stream);
+void dummy_check (MAILSTREAM *stream);
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options);
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+/* Dummy routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER dummydriver = {
+ "dummy", /* driver name */
+ DR_LOCAL|DR_MAIL, /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ dummy_valid, /* mailbox is valid for us */
+ dummy_parameters, /* manipulate parameters */
+ dummy_scan, /* scan mailboxes */
+ dummy_list, /* list mailboxes */
+ dummy_lsub, /* list subscribed mailboxes */
+ dummy_subscribe, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ dummy_create, /* create mailbox */
+ dummy_delete, /* delete mailbox */
+ dummy_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ dummy_open, /* open mailbox */
+ dummy_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message structure */
+ NIL, /* fetch header */
+ NIL, /* fetch text */
+ NIL, /* fetch message data */
+ NIL, /* unique identifier */
+ NIL, /* message number from UID */
+ NIL, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ dummy_ping, /* ping mailbox to see if still alive */
+ dummy_check, /* check for new messages */
+ dummy_expunge, /* expunge deleted messages */
+ dummy_copy, /* copy messages to another mailbox */
+ dummy_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+
+ /* prototype stream */
+MAILSTREAM dummyproto = {&dummydriver};
+
+/* Dummy validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *dummy_valid (char *name)
+{
+ char *s,*t,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* must be valid local mailbox */
+ if (name && *name && (*name != '{') && (s = mailboxfile (tmp,name))) {
+ /* indeterminate INBOX */
+ if (!*s) return &dummydriver;
+ /* remove trailing \ */
+ if ((t = strrchr (s,'\\')) && !t[1]) *t = '\0';
+ if (!stat (s,&sbuf)) switch (sbuf.st_mode & S_IFMT) {
+ case S_IFREG: /* file */
+ case S_IFDIR: /* future use */
+ return &dummydriver;
+ }
+ }
+ return NIL;
+}
+
+
+/* Dummy manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *dummy_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+/* Dummy scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ char *s,test[MAILTMPLEN],file[MAILTMPLEN];
+ long i = 0;
+ if (!pat || !*pat) { /* empty pattern? */
+ if (dummy_canonicalize (test,ref,"*")) {
+ /* tie off name at root */
+ if (s = strchr (test,'\\')) *++s = '\0';
+ else test[0] = '\0';
+ dummy_listed (stream,'\\',test,LATT_NOSELECT,NIL);
+ }
+ }
+ /* get canonical form of name */
+ else if (dummy_canonicalize (test,ref,pat)) {
+ /* found any wildcards? */
+ if (s = strpbrk (test,"%*")) {
+ /* yes, copy name up to that point */
+ strncpy (file,test,(size_t) (i = s - test));
+ file[i] = '\0'; /* tie off */
+ }
+ else strcpy (file,test); /* use just that name then */
+ /* find directory name */
+ if (s = strrchr (file,'\\')) {
+ *++s = '\0'; /* found, tie off at that point */
+ s = file;
+ }
+ /* silly case */
+ else if (file[0] == '#') s = file;
+ /* do the work */
+ dummy_list_work (stream,s,test,contents,0);
+ if (pmatch ("INBOX",test)) /* always an INBOX */
+ dummy_listed (stream,NIL,"INBOX",LATT_NOINFERIORS,contents);
+ }
+}
+
+/* Dummy list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ dummy_scan (stream,ref,pat,NIL);
+}
+
+
+/* Dummy list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ void *sdb = NIL;
+ char *s,*t,test[MAILTMPLEN];
+ int showuppers = pat[strlen (pat) - 1] == '%';
+ /* get canonical form of name */
+ if (dummy_canonicalize (test,ref,pat) && (s = sm_read (&sdb))) do
+ if (*s != '{') {
+ if (pmatch_full (s,test,'\\')) {
+ if (pmatch (s,"INBOX")) mm_lsub (stream,NIL,s,LATT_NOINFERIORS);
+ else mm_lsub (stream,'\\',s,NIL);
+ }
+ else while (showuppers && (t = strrchr (s,'\\'))) {
+ *t = '\0'; /* tie off the name */
+ if (pmatch_full (s,test,'\\')) mm_lsub (stream,'\\',s,LATT_NOSELECT);
+ }
+ }
+ while (s = sm_read (&sdb)); /* until no more subscriptions */
+}
+
+
+/* Dummy subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* must be valid local mailbox */
+ if ((s = mailboxfile (tmp,mailbox)) && *s && !stat (s,&sbuf) &&
+ ((sbuf.st_mode & S_IFMT) == S_IFREG)) return sm_subscribe (mailbox);
+ sprintf (tmp,"Can't subscribe %.80s: not a mailbox",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+}
+
+/* Dummy list mailboxes worker routine
+ * Accepts: mail stream
+ * directory name to search
+ * search pattern
+ * string to scan
+ * search level
+ */
+
+void dummy_list_work (MAILSTREAM *stream,char *dir,char *pat,char *contents,
+ long level)
+{
+ struct _finddata_t f;
+ struct stat sbuf;
+ long fhandle;
+ char tmp[MAILTMPLEN];
+ size_t len = 0;
+ /* punt if bogus name */
+ if (!mailboxdir (tmp,dir,NIL)) return;
+ /* make directory wildcard */
+ strcat (tmp,(tmp[strlen (tmp) -1] == '\\') ? "*.*" : "\\*.*");
+ /* do nothing if can't open directory */
+ if ((fhandle = _findfirst (tmp,&f)) >= 0) {
+ /* list it if at top-level */
+ if (!level && dir && pmatch_full (dir,pat,'\\'))
+ dummy_listed (stream,'\\',dir,LATT_NOSELECT,contents);
+ /* scan directory */
+ if (!dir || dir[(len = strlen (dir)) - 1] == '\\') do
+ if (((f.name[0] != '.') ||
+ (f.name[1] && ((f.name[1] != '.') || f.name[2]))) &&
+ ((len + strlen (f.name)) <= NETMAXMBX)) {
+ /* see if name is useful */
+ if (dir) sprintf (tmp,"%s%s",dir,f.name);
+ else strcpy (tmp,f.name);
+ /* make sure useful and can get info */
+ if ((pmatch_full (tmp,pat,'\\') ||
+ pmatch_full (strcat (tmp,"\\"),pat,'\\') ||
+ dmatch (tmp,pat,'\\')) &&
+ mailboxdir (tmp,dir,f.name) && tmp[0] && !stat (tmp,&sbuf)) {
+ /* now make name we'd return */
+ if (dir) sprintf (tmp,"%s%s",dir,f.name);
+ else strcpy (tmp,f.name);
+ /* only interested in file type */
+ switch (sbuf.st_mode & S_IFMT) {
+ case S_IFDIR: /* directory? */
+ if (pmatch_full (tmp,pat,'\\')) {
+ if (!dummy_listed (stream,'\\',tmp,LATT_NOSELECT,contents))break;
+ strcat (tmp,"\\");/* set up for dmatch call */
+ }
+ /* try again with trailing \ */
+ else if (pmatch_full (strcat (tmp,"\\"),pat,'\\') &&
+ !dummy_listed (stream,'\\',tmp,LATT_NOSELECT,contents))
+ break;
+ if (dmatch (tmp,pat,'\\') &&
+ (level < (long) mail_parameters (NIL,GET_LISTMAXLEVEL,NIL)))
+ dummy_list_work (stream,tmp,pat,contents,level+1);
+ break;
+ case S_IFREG: /* ordinary name */
+ if (pmatch_full (tmp,pat,'\\') && !pmatch ("INBOX",tmp))
+ dummy_listed (stream,'\\',tmp,LATT_NOINFERIORS,contents);
+ break;
+ }
+ }
+ }
+ while (!_findnext (fhandle,&f));
+ _findclose(fhandle);
+ }
+}
+
+/* Mailbox found
+ * Accepts: hierarchy delimiter
+ * mailbox name
+ * attributes
+ * contents to search before calling mm_list()
+ * Returns: T, always
+ */
+
+#define BUFSIZE 4*MAILTMPLEN
+
+long dummy_listed (MAILSTREAM *stream,char delimiter,char *name,
+ long attributes,char *contents)
+{
+ struct stat sbuf;
+ struct _finddata_t f;
+ int fd,nochild;
+ long fhandle,csiz,ssiz,bsiz;
+ char *s,*buf,tmp[MAILTMPLEN];
+ /* if not \NoInferiors */
+ if (!(attributes & LATT_NOINFERIORS) && mailboxdir (tmp,name,NIL) &&
+ strcat (tmp,(tmp[strlen (tmp) -1] == '\\') ? "*.*" : "\\*.*") &&
+ ((fhandle = _findfirst (tmp,&f)) >= 0)) {
+ nochild = T;
+ do if ((f.name[0] != '.') || (f.name[1] && ((f.name[1] != '.') ||
+ f.name[2]))) nochild = NIL;
+ while (nochild && !_findnext (fhandle,&f));
+ attributes |= nochild ? LATT_HASNOCHILDREN : LATT_HASCHILDREN;
+ _findclose (fhandle); /* all done, flush directory */
+ }
+ if (contents) { /* want to search contents? */
+ /* forget it if can't select or open */
+ if ((attributes & LATT_NOSELECT) || !(csiz = strlen (contents)) ||
+ !(s = dummy_file (tmp,name)) || stat (s,&sbuf) ||
+ (csiz > sbuf.st_size) || ((fd = open (tmp,O_RDONLY,NIL)) < 0))
+ return T;
+ /* get buffer including slop */
+ buf = (char *) fs_get (BUFSIZE + (ssiz = 4 * ((csiz / 4) + 1)) + 1);
+ memset (buf,'\0',ssiz); /* no slop area the first time */
+ while (sbuf.st_size) { /* until end of file */
+ read (fd,buf+ssiz,bsiz = min (sbuf.st_size,BUFSIZE));
+ if (search ((unsigned char *) buf,bsiz+ssiz,
+ (unsigned char *) contents,csiz)) break;
+ memcpy (buf,buf+BUFSIZE,ssiz);
+ sbuf.st_size -= bsiz; /* note that we read that much */
+ }
+ fs_give ((void **) &buf); /* flush buffer */
+ close (fd); /* finished with file */
+ if (!sbuf.st_size) return T;/* not found */
+ }
+ /* notify main program */
+ mm_list (stream,delimiter,name,attributes);
+ return T;
+}
+
+/* Dummy create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_create (MAILSTREAM *stream,char *mailbox)
+{
+ char tmp[MAILTMPLEN];
+ if (compare_cstring (mailbox,"INBOX") && dummy_file (tmp,mailbox))
+ return dummy_create_path (stream,tmp,NIL);
+ sprintf (tmp,"Can't create %.80s: invalid name",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+}
+
+
+/* Dummy create path
+ * Accepts: mail stream
+ * path name to create
+ * directory mode
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode)
+{
+ struct stat sbuf;
+ char c,*s,tmp[MAILTMPLEN];
+ int fd;
+ long ret = NIL;
+ char *t = strrchr (path,'\\');
+ char *pt = (path[1] == ':') ? path + 2 : path;
+ int wantdir = t && !t[1];
+ if (wantdir) *t = '\0'; /* flush trailing delimiter for directory */
+ /* found superior to this name? */
+ if ((s = strrchr (pt,'\\')) && (s != pt)) {
+ strncpy (tmp,path,(size_t) (s - path));
+ tmp[s - path] = '\0'; /* make directory name for stat */
+ c = *++s; /* tie off in case need to recurse */
+ *s = '\0';
+ /* name doesn't exist, create it */
+ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,path,dirmode)) return NIL;
+ *s = c; /* restore full name */
+ }
+ if (wantdir) { /* want to create directory? */
+ ret = !mkdir (path);
+ *t = '\\'; /* restore directory delimiter */
+ }
+ /* create file */
+ else if ((fd = open (path,O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE)) >= 0)
+ ret = !close (fd); /* close file */
+ if (!ret) { /* error? */
+ sprintf (tmp,"Can't create mailbox node %.80s: %.80s",path,
+ strerror (errno));
+ mm_log (tmp,ERROR);
+ }
+ return ret; /* return status */
+}
+
+/* Dummy delete mailbox
+ * Accepts: mail stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_delete (MAILSTREAM *stream,char *mailbox)
+{
+ struct stat sbuf;
+ char *s,tmp[MAILTMPLEN];
+ if (!(s = dummy_file (tmp,mailbox))) {
+ sprintf (tmp,"Can't delete - invalid name: %.80s",s);
+ mm_log (tmp,ERROR);
+ }
+ /* no trailing \ */
+ if ((s = strrchr (tmp,'\\')) && !s[1]) *s = '\0';
+ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) == S_IFDIR) ?
+ rmdir (tmp) : unlink (tmp)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %.80s",mailbox,strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ return T; /* return success */
+}
+
+
+/* Mail rename mailbox
+ * Accepts: mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ struct stat sbuf;
+ char c,*s,tmp[MAILTMPLEN],mbx[MAILTMPLEN],oldname[MAILTMPLEN];
+ long ret = NIL;
+ /* no trailing \ allowed */
+ if (!dummy_file (oldname,old) || !(s = dummy_file (mbx,newname)) ||
+ stat (oldname,&sbuf) || ((s = strrchr (s,'\\')) && !s[1] &&
+ ((sbuf.st_mode & S_IFMT) != S_IFDIR))) {
+ sprintf (mbx,"Can't rename %.80s to %.80s: invalid name",old,newname);
+ mm_log (mbx,ERROR);
+ return NIL;
+ }
+ if (s) { /* found a directory delimiter? */
+ if (!s[1]) *s = '\0'; /* ignore trailing delimiter */
+ /* found superior to destination name? */
+ else if ((s != mbx) && ((mbx[1] != ':') || (s != mbx + 2))) {
+ c = s[1]; /* remember character after delimiter */
+ *s = s[1] = '\0'; /* tie off name at delimiter */
+ /* name doesn't exist, create it */
+ if (stat (mbx,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) {
+ *s = '\\'; /* restore delimiter */
+ if (!dummy_create (stream,mbx)) return NIL;
+ }
+ else *s = '\\'; /* restore delimiter */
+ s[1] = c; /* restore character after delimiter */
+ }
+ }
+ /* rename of non-ex INBOX creates dest */
+ if (!compare_cstring (old,"INBOX") && stat (oldname,&sbuf))
+ return dummy_create (NIL,mbx);
+ if (rename (oldname,mbx)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %.80s",old,newname,
+ strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ return LONGT; /* return success */
+}
+
+/* Dummy open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *dummy_open (MAILSTREAM *stream)
+{
+ int fd;
+ char err[MAILTMPLEN],tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* OP_PROTOTYPE call */
+ if (!stream) return &dummyproto;
+ err[0] = '\0'; /* no error message yet */
+ /* can we open the file? */
+ if (!dummy_file (tmp,stream->mailbox))
+ sprintf (err,"Can't open this name: %.80s",stream->mailbox);
+ else if ((fd = open (tmp,O_RDONLY,NIL)) < 0) {
+ /* no, error unless INBOX */
+ if (compare_cstring (stream->mailbox,"INBOX"))
+ sprintf (err,"%.80s: %.80s",strerror (errno),stream->mailbox);
+ }
+ else { /* file had better be empty then */
+ fstat (fd,&sbuf); /* sniff at its size */
+ close (fd);
+ if (sbuf.st_size) /* bogus format if non-empty */
+ sprintf (err,"%.80s (file %.80s) is not in valid mailbox format",
+ stream->mailbox,tmp);
+ }
+ if (err[0]) { /* if an error happened */
+ mm_log (err,stream->silent ? WARN : ERROR);
+ return NIL;
+ }
+ else if (!stream->silent) { /* only if silence not requested */
+ mail_exists (stream,0); /* say there are 0 messages */
+ mail_recent (stream,0); /* and certainly no recent ones! */
+ stream->uid_validity = (unsigned long) time (0);
+ }
+ stream->inbox = T; /* note that it's an INBOX */
+ return stream; /* return success */
+}
+
+
+/* Dummy close
+ * Accepts: MAIL stream
+ * options
+ */
+
+void dummy_close (MAILSTREAM *stream,long options)
+{
+ /* return silently */
+}
+
+/* Dummy ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long dummy_ping (MAILSTREAM *stream)
+{
+ MAILSTREAM *test;
+ /* time to do another test? */
+ if (time (0) >= ((time_t) (stream->gensym + 30))) {
+ /* has mailbox format changed? */
+ if ((test = mail_open (NIL,stream->mailbox,OP_PROTOTYPE)) &&
+ (test->dtb != stream->dtb) &&
+ (test = mail_open (NIL,stream->mailbox,NIL))) {
+ /* preserve some resources */
+ test->original_mailbox = stream->original_mailbox;
+ stream->original_mailbox = NIL;
+ test->sparep = stream->sparep;
+ stream->sparep = NIL;
+ test->sequence = stream->sequence;
+ mail_close ((MAILSTREAM *) /* flush resources used by dummy stream */
+ memcpy (fs_get (sizeof (MAILSTREAM)),stream,
+ sizeof (MAILSTREAM)));
+ /* swap the streams */
+ memcpy (stream,test,sizeof (MAILSTREAM));
+ fs_give ((void **) &test);/* flush test now that copied */
+ /* make sure application knows */
+ mail_exists (stream,stream->recent = stream->nmsgs);
+ }
+ /* still hasn't changed */
+ else stream->gensym = (unsigned long) time (0);
+ }
+ return T;
+}
+
+
+/* Dummy check mailbox
+ * Accepts: MAIL stream
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+void dummy_check (MAILSTREAM *stream)
+{
+ dummy_ping (stream); /* invoke ping */
+}
+
+
+/* Dummy expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ return LONGT;
+}
+
+/* Dummy copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * options
+ * Returns: T if copy successful, else NIL
+ */
+
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) fatal ("Impossible dummy_copy");
+ return NIL;
+}
+
+
+/* Dummy append message string
+ * Accepts: mail stream
+ * destination mailbox
+ * append callback function
+ * data for callback
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd = -1;
+ int e;
+ char tmp[MAILTMPLEN];
+ MAILSTREAM *ts = default_proto (T);
+ if (compare_cstring (mailbox,"INBOX") && dummy_file (tmp,mailbox) &&
+ ((fd = open (tmp,O_RDONLY,NIL)) < 0)) {
+ if ((e = errno) == ENOENT) /* failed, was it no such file? */
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",
+ (long) NIL);
+ sprintf (tmp,"%.80s: %.80s",strerror (e),mailbox);
+ mm_log (tmp,ERROR); /* pass up error */
+ return NIL; /* always fails */
+ }
+ if (fd >= 0) { /* found file? */
+ fstat (fd,&sbuf); /* get its size */
+ close (fd); /* toss out the fd */
+ if (sbuf.st_size) ts = NIL; /* non-empty file? */
+ }
+ if (ts) return (*ts->dtb->append) (stream,mailbox,af,data);
+ sprintf (tmp,"Indeterminate mailbox format: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+}
+
+/* Dummy mail generate file string
+ * Accepts: temporary buffer to write into
+ * mailbox name string
+ * Returns: local file string or NIL if failure
+ */
+
+char *dummy_file (char *dst,char *name)
+{
+ char *s = mailboxfile (dst,name);
+ /* return our standard inbox */
+ return (s && !*s) ? strcpy (dst,sysinbox ()) : s;
+}
+
+
+/* Dummy canonicalize name
+ * Accepts: buffer to write name
+ * reference
+ * pattern
+ * Returns: T if success, NIL if failure
+ */
+
+long dummy_canonicalize (char *tmp,char *ref,char *pat)
+{
+ unsigned long i;
+ char *s,dev[4];
+ /* initially no device */
+ dev[0] = dev[1] = dev[2] = dev[3] = '\0';
+ if (ref) switch (*ref) { /* preliminary reference check */
+ case '{': /* remote names not allowed */
+ return NIL; /* disallowed */
+ case '\0': /* empty reference string */
+ break;
+ default: /* all other names */
+ if (ref[1] == ':') { /* start with device name? */
+ dev[0] = *ref++; dev[1] = *ref++;
+ }
+ break;
+ }
+ if (pat[1] == ':') { /* device name in pattern? */
+ dev[0] = *pat++; dev[1] = *pat++;
+ ref = NIL; /* ignore reference */
+ }
+ switch (*pat) {
+ case '#': /* namespace names */
+ if (mailboxfile (tmp,pat)) strcpy (tmp,pat);
+ else return NIL; /* unknown namespace */
+ break;
+ case '{': /* remote names not allowed */
+ return NIL;
+ case '\\': /* rooted name */
+ ref = NIL; /* ignore reference */
+ break;
+ }
+ /* make sure device names are rooted */
+ if (dev[0] && (*(ref ? ref : pat) != '\\')) dev[2] = '\\';
+ /* build name */
+ sprintf (tmp,"%s%s%s",dev,ref ? ref : "",pat);
+ ucase (tmp); /* force upper case */
+ /* count wildcards */
+ for (i = 0, s = tmp; *s; *s++) if ((*s == '*') || (*s == '%')) ++i;
+ if (i > MAXWILDCARDS) { /* ridiculous wildcarding? */
+ MM_LOG ("Excessive wildcards in LIST/LSUB",ERROR);
+ return NIL;
+ }
+ return T;
+}
diff --git a/imap/src/osdep/nt/env_nt.c b/imap/src/osdep/nt/env_nt.c
new file mode 100644
index 00000000..18bc2369
--- /dev/null
+++ b/imap/src/osdep/nt/env_nt.c
@@ -0,0 +1,774 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: NT environment routines
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 15 February 2008
+ */
+
+static char *myUserName = NIL; /* user name */
+static char *myLocalHost = NIL; /* local host name */
+static char *myHomeDir = NIL; /* home directory name */
+static char *myNewsrc = NIL; /* newsrc file name */
+static char *sysInbox = NIL; /* system inbox name */
+static long list_max_level = 5; /* maximum level of list recursion */
+ /* block environment init */
+static short block_env_init = NIL;
+static short no822tztext = NIL; /* disable RFC [2]822 timezone text */
+ /* home namespace */
+static NAMESPACE nshome = {"",'\\',NIL,NIL};
+ /* UNIX other user namespace */
+static NAMESPACE nsother = {"#user.",'\\',NIL,NIL};
+ /* namespace list */
+static NAMESPACE *nslist[3] = {&nshome,&nsother,NIL};
+static long alarm_countdown = 0;/* alarm count down */
+static void (*alarm_rang) (); /* alarm interrupt function */
+static unsigned int rndm = 0; /* initial `random' number */
+static int server_nli = 0; /* server and not logged in */
+static int logtry = 3; /* number of login tries */
+ /* block notification */
+static blocknotify_t mailblocknotify = mm_blocknotify;
+ /* callback to get username */
+static userprompt_t mailusername = NIL;
+static long is_nt = -1; /* T if NT, NIL if not NT, -1 unknown */
+static HINSTANCE netapi = NIL;
+typedef NET_API_STATUS (CALLBACK *GETINFO) (LPCWSTR,LPCWSTR,DWORD,LPBYTE *);
+static GETINFO getinfo = NIL;
+
+#include "write.c" /* include safe writing routines */
+#include "pmatch.c" /* include wildcard pattern matcher */
+
+
+/* Get all authenticators */
+
+#include "auths.c"
+
+/* Environment manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *env_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_NAMESPACE:
+ ret = (void *) nslist;
+ break;
+ case SET_USERPROMPT :
+ mailusername = (userprompt_t) value;
+ case GET_USERPROMPT :
+ ret = (void *) mailusername;
+ break;
+ case SET_HOMEDIR:
+ if (myHomeDir) fs_give ((void **) &myHomeDir);
+ myHomeDir = cpystr ((char *) value);
+ case GET_HOMEDIR:
+ ret = (void *) myHomeDir;
+ break;
+ case SET_LOCALHOST:
+ myLocalHost = cpystr ((char *) value);
+ case GET_LOCALHOST:
+ if (myLocalHost) fs_give ((void **) &myLocalHost);
+ ret = (void *) myLocalHost;
+ break;
+ case SET_NEWSRC:
+ if (myNewsrc) fs_give ((void **) &myNewsrc);
+ myNewsrc = cpystr ((char *) value);
+ case GET_NEWSRC:
+ if (!myNewsrc) { /* set news file name if not defined */
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"%s\\NEWSRC",myhomedir ());
+ myNewsrc = cpystr (tmp);
+ }
+ ret = (void *) myNewsrc;
+ break;
+ case SET_SYSINBOX:
+ if (sysInbox) fs_give ((void **) &sysInbox);
+ sysInbox = cpystr ((char *) value);
+ case GET_SYSINBOX:
+ ret = (void *) sysInbox;
+ break;
+ case SET_LISTMAXLEVEL:
+ list_max_level = (long) value;
+ case GET_LISTMAXLEVEL:
+ ret = (void *) list_max_level;
+ break;
+ case SET_DISABLE822TZTEXT:
+ no822tztext = value ? T : NIL;
+ case GET_DISABLE822TZTEXT:
+ ret = (void *) (no822tztext ? VOIDT : NIL);
+ break;
+ case SET_BLOCKENVINIT:
+ block_env_init = value ? T : NIL;
+ case GET_BLOCKENVINIT:
+ ret = (void *) (block_env_init ? VOIDT : NIL);
+ break;
+ case SET_BLOCKNOTIFY:
+ mailblocknotify = (blocknotify_t) value;
+ case GET_BLOCKNOTIFY:
+ ret = (void *) mailblocknotify;
+ break;
+ }
+ return ret;
+}
+
+/* Write current time
+ * Accepts: destination string
+ * optional format of day-of-week prefix
+ * format of date and time
+ * flag whether to append symbolic timezone
+ */
+
+static void do_date (char *date,char *prefix,char *fmt,int suffix)
+{
+ time_t tn = time (0);
+ struct tm *t = gmtime (&tn);
+ int zone = t->tm_hour * 60 + t->tm_min;
+ int julian = t->tm_yday;
+ t = localtime (&tn); /* get local time now */
+ /* minus UTC minutes since midnight */
+ zone = t->tm_hour * 60 + t->tm_min - zone;
+ /* julian can be one of:
+ * 36x local time is December 31, UTC is January 1, offset -24 hours
+ * 1 local time is 1 day ahead of UTC, offset +24 hours
+ * 0 local time is same day as UTC, no offset
+ * -1 local time is 1 day behind UTC, offset -24 hours
+ * -36x local time is January 1, UTC is December 31, offset +24 hours
+ */
+ if (julian = t->tm_yday -julian)
+ zone += ((julian < 0) == (abs (julian) == 1)) ? -24*60 : 24*60;
+ if (prefix) { /* want day of week? */
+ sprintf (date,prefix,days[t->tm_wday]);
+ date += strlen (date); /* make next sprintf append */
+ }
+ /* output the date */
+ sprintf (date,fmt,t->tm_mday,months[t->tm_mon],t->tm_year+1900,
+ t->tm_hour,t->tm_min,t->tm_sec,zone/60,abs (zone) % 60);
+ if (suffix) { /* append timezone suffix if desired */
+ char *tz;
+ tzset (); /* get timezone from TZ environment stuff */
+ tz = tzname[daylight ? (((struct tm *) t)->tm_isdst > 0) : 0];
+ if (tz && tz[0]) {
+ char *s;
+ for (s = tz; *s; s++) if (*s & 0x80) return;
+ sprintf (date + strlen (date)," (%.50s)",tz);
+ }
+ }
+}
+
+
+/* Write current time in RFC 822 format
+ * Accepts: destination string
+ */
+
+void rfc822_date (char *date)
+{
+ do_date (date,"%s, ","%d %s %d %02d:%02d:%02d %+03d%02d",
+ no822tztext ? NIL : T);
+}
+
+
+/* Write current time in fixed-width RFC 822 format
+ * Accepts: destination string
+ */
+
+void rfc822_fixed_date (char *date)
+{
+ do_date (date,NIL,"%02d %s %4d %02d:%02d:%02d %+03d%02d",NIL);
+}
+
+
+/* Write current time in internal format
+ * Accepts: destination string
+ */
+
+void internal_date (char *date)
+{
+ do_date (date,NIL,"%02d-%s-%d %02d:%02d:%02d %+03d%02d",NIL);
+}
+
+/* Return random number
+ */
+
+long random (void)
+{
+ if (!rndm) srand (rndm = (unsigned) time (0L));
+ return (long) rand ();
+}
+
+
+/* Set alarm timer
+ * Accepts: new value
+ * Returns: old alarm value
+ */
+
+long alarm (long seconds)
+{
+ long ret = alarm_countdown;
+ alarm_countdown = seconds;
+ return ret;
+}
+
+
+/* The clock ticked
+ */
+
+void CALLBACK clock_ticked (UINT IDEvent,UINT uReserved,DWORD dwUser,
+ DWORD dwReserved1,DWORD dwReserved2)
+{
+ if (alarm_rang && !--alarm_countdown) (*alarm_rang) ();
+}
+
+/* Initialize server
+ * Accepts: server name for syslog or NIL
+ * /etc/services service name or NIL
+ * alternate /etc/services service name or NIL
+ * clock interrupt handler
+ * kiss-of-death interrupt handler
+ * hangup interrupt handler
+ * termination interrupt handler
+ */
+
+void server_init (char *server,char *service,char *sslservice,
+ void *clkint,void *kodint,void *hupint,void *trmint,
+ void *staint)
+{
+ if (!check_nt ()) {
+ if (!auth_md5.server) fatal ("Can't run on Windows without MD5 database");
+ server_nli = T; /* Windows server not logged in */
+ }
+ /* only do this if for init call */
+ if (server && service && sslservice) {
+ long port;
+ struct servent *sv;
+ /* set server name in syslog */
+ openlog (server,LOG_PID,LOG_MAIL);
+ fclose (stderr); /* possibly save a process ID */
+ /* Use SSL if SSL service, or if server starts with "s" and not service */
+ if (((port = tcp_serverport ()) >= 0)) {
+ if ((sv = getservbyname (service,"tcp")) && (port == ntohs (sv->s_port)))
+ syslog (LOG_DEBUG,"%s service init from %s",service,tcp_clientaddr ());
+ else if ((sv = getservbyname (sslservice,"tcp")) &&
+ (port == ntohs (sv->s_port))) {
+ syslog (LOG_DEBUG,"%s SSL service init from %s",sslservice,
+ tcp_clientaddr ());
+ ssl_server_init (server);
+ }
+ else { /* not service or SSL service port */
+ syslog (LOG_DEBUG,"port %ld service init from %s",port,
+ tcp_clientaddr ());
+ if (*server == 's') ssl_server_init (server);
+ }
+ }
+ /* make sure stdout does binary */
+ setmode (fileno (stdin),O_BINARY);
+ setmode (fileno (stdout),O_BINARY);
+ }
+ alarm_rang = clkint; /* note the clock interrupt */
+ timeBeginPeriod (1000); /* set the timer interval */
+ timeSetEvent (1000,1000,clock_ticked,NIL,TIME_PERIODIC);
+}
+
+
+/* Wait for stdin input
+ * Accepts: timeout in seconds
+ * Returns: T if have input on stdin, else NIL
+ */
+
+long server_input_wait (long seconds)
+{
+ fd_set rfd,efd;
+ struct timeval tmo;
+ FD_ZERO (&rfd);
+ FD_ZERO (&efd);
+ FD_SET (0,&rfd);
+ FD_SET (0,&efd);
+ tmo.tv_sec = seconds; tmo.tv_usec = 0;
+ return select (1,&rfd,0,&efd,&tmo) ? LONGT : NIL;
+}
+
+/* Server log in
+ * Accepts: user name string
+ * password string
+ * authenticating user name string
+ * argument count
+ * argument vector
+ * Returns: T if password validated, NIL otherwise
+ */
+
+static int gotprivs = NIL; /* once-only flag to grab privileges */
+
+long server_login (char *user,char *pass,char *authuser,int argc,char *argv[])
+{
+ HANDLE hdl;
+ LUID tcbpriv;
+ TOKEN_PRIVILEGES tkp;
+ char *s;
+ /* need to get privileges? */
+ if (!gotprivs++ && check_nt ()) {
+ /* hack for inetlisn */
+ if (argc >= 2) myClientHost = argv[1];
+ /* get process token and TCB priv value */
+ if (!(OpenProcessToken (GetCurrentProcess (),
+ TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,&hdl) &&
+ LookupPrivilegeValue ((LPSTR) NIL,SE_TCB_NAME,&tcbpriv)))
+ return NIL;
+ tkp.PrivilegeCount = 1; /* want to enable this privilege */
+ tkp.Privileges[0].Luid = tcbpriv;
+ tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ /* enable it */
+ AdjustTokenPrivileges (hdl,NIL,&tkp,sizeof (TOKEN_PRIVILEGES),
+ (PTOKEN_PRIVILEGES) NIL,(PDWORD) NIL);
+ /* make sure it won */
+ if (GetLastError() != ERROR_SUCCESS) return NIL;
+ }
+
+ /* cretins still haven't given up */
+ if ((strlen (user) >= MAILTMPLEN) ||
+ (authuser && (strlen (authuser) >= MAILTMPLEN)))
+ syslog (LOG_ALERT,"SYSTEM BREAK-IN ATTEMPT, host=%.80s",tcp_clienthost ());
+ else if (logtry > 0) { /* still have available logins? */
+ /* authentication user not supported */
+ if (authuser && *authuser && compare_cstring (authuser,user))
+ mm_log ("Authentication id must match authorization id",ERROR);
+ if (check_nt ()) { /* NT: authserver_login() call not supported */
+ if (!pass) mm_log ("Unsupported authentication mechanism",ERROR);
+ else if (( /* try to login and impersonate the guy */
+#ifdef LOGIN32_LOGON_NETWORK
+ LogonUser (user,".",pass,LOGON32_LOGON_NETWORK,
+ LOGON32_PROVIDER_DEFAULT,&hdl) ||
+#endif
+ LogonUser (user,".",pass,LOGON32_LOGON_INTERACTIVE,
+ LOGON32_PROVIDER_DEFAULT,&hdl) ||
+ LogonUser (user,".",pass,LOGON32_LOGON_BATCH,
+ LOGON32_PROVIDER_DEFAULT,&hdl) ||
+ LogonUser (user,".",pass,LOGON32_LOGON_SERVICE,
+ LOGON32_PROVIDER_DEFAULT,&hdl)) &&
+ ImpersonateLoggedOnUser (hdl)) return env_init (user,NIL);
+ }
+ else { /* Win9x: done if from authserver_login() */
+ if (!pass) server_nli = NIL;
+ /* otherwise check MD5 database */
+ else if (s = auth_md5_pwd (user)) {
+ /* change NLI state based on pwd match */
+ server_nli = strcmp (s,pass);
+ memset (s,0,strlen (s));/* erase sensitive information */
+ fs_give ((void **) &s); /* flush erased password */
+ }
+ /* success if no longer NLI */
+ if (!server_nli) return env_init (user,NIL);
+ }
+ }
+ s = (logtry-- > 0) ? "Login failure" : "Excessive login attempts";
+ /* note the failure in the syslog */
+ syslog (LOG_INFO,"%s user=%.80s host=%.80s",s,user,tcp_clienthost ());
+ sleep (3); /* slow down possible cracker */
+ return NIL;
+}
+
+/* Authenticated server log in
+ * Accepts: user name string
+ * authentication user name string
+ * argument count
+ * argument vector
+ * Returns: T if password validated, NIL otherwise
+ */
+
+long authserver_login (char *user,char *authuser,int argc,char *argv[])
+{
+ return server_login (user,NIL,authuser,argc,argv);
+}
+
+
+/* Log in as anonymous daemon
+ * Accepts: argument count
+ * argument vector
+ * Returns: T if successful, NIL if error
+ */
+
+long anonymous_login (int argc,char *argv[])
+{
+ return server_login ("Guest",NIL,NIL,argc,argv);
+}
+
+
+/* Initialize environment
+ * Accepts: user name
+ * home directory, or NIL to use default
+ * Returns: T, always
+ */
+
+long env_init (char *user,char *home)
+{
+ /* don't init if blocked */
+ if (block_env_init) return LONGT;
+ if (myUserName) fatal ("env_init called twice!");
+ myUserName = cpystr (user); /* remember user name */
+ if (!myHomeDir) /* only if home directory not set up yet */
+ myHomeDir = (home && *home) ? cpystr (home) : win_homedir (user);
+ return T;
+}
+
+/* Check if NT
+ * Returns: T if NT, NIL if Win9x
+ */
+
+int check_nt (void)
+{
+ if (is_nt < 0) { /* not yet set up? */
+ OSVERSIONINFO ver;
+ ver.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
+ GetVersionEx (&ver);
+ is_nt = (ver.dwPlatformId == VER_PLATFORM_WIN32_NT) ? T : NIL;
+ }
+ return is_nt;
+}
+
+
+/* Return Windows home directory
+ * Accepts: user name
+ * Returns: home directory
+ */
+
+char *win_homedir (char *user)
+{
+ char *s,*t,tmp[MAILTMPLEN];
+ PUSER_INFO_1 ui;
+ /* Win9x default */
+ if (!check_nt ()) sprintf (tmp,"%s\\My Documents",defaultDrive ());
+ /* get from user info on NT */
+ else if ((netapi || (netapi = LoadLibrary ("netapi32.dll"))) &&
+ (getinfo ||
+ (getinfo = (GETINFO) GetProcAddress (netapi,"NetUserGetInfo"))) &&
+ MultiByteToWideChar (CP_ACP,0,user,strlen (user) + 1,
+ (WCHAR *) tmp,MAILTMPLEN) &&
+ !(*getinfo) (NIL,(LPWSTR) &tmp,1,(LPBYTE *) &ui) &&
+ WideCharToMultiByte (CP_ACP,0,ui->usri1_home_dir,-1,
+ tmp,MAILTMPLEN,NIL,NIL) && tmp[0]) {
+ /* make sure doesn't end with delimiter */
+ if ((*(s = tmp + strlen (tmp) - 1) == '\\') || (*s == '/')) *s = '\0';
+ }
+ /* no home dir, found Win2K user profile? */
+ else if ((s = getenv ("USERPROFILE")) && (t = strrchr (s,'\\'))) {
+ strncpy (tmp,s,t-s); /* copy up to user name */
+ sprintf (tmp+(t-s),"\\%.100s\\My Documents",user);
+ }
+ /* last resort NT default */
+ else sprintf (tmp,"%s\\users\\default",defaultDrive ());
+ return cpystr (tmp);
+}
+
+
+/* Return default drive
+ * Returns: default drive
+ */
+
+static char *defaultDrive (void)
+{
+ char *s = getenv ("SystemDrive");
+ return (s && *s) ? s : "C:";
+}
+
+/* Return my user name
+ * Accepts: pointer to optional flags
+ * Returns: my user name
+ */
+
+char *myusername_full (unsigned long *flags)
+{
+ UCHAR usr[MAILTMPLEN];
+ DWORD len = MAILTMPLEN;
+ char *user,*path,*d,*p,pth[MAILTMPLEN];
+ char *ret = "SYSTEM";
+ /* get user name if don't have it yet */
+ if (!myUserName && !server_nli &&
+ /* use callback, else logon name */
+ ((mailusername && (user = (char *) (*mailusername) ())) ||
+ (GetUserName (usr,&len) && _stricmp (user = (char *) usr,"SYSTEM")))) {
+ if (block_env_init) { /* don't env_init if blocked */
+ if (flags) *flags = MU_LOGGEDIN;
+ return user;
+ }
+ /* try HOMEPATH, then HOME */
+ if (p = getenv ("HOMEPATH"))
+ sprintf (path = pth,"%s%s",
+ (d = getenv ("HOMEDRIVE")) ? d : defaultDrive (),p);
+ else if (!(path = getenv ("HOME")))
+ sprintf (path = pth,"%s\\My Documents",defaultDrive ());
+ /* make sure doesn't end with delimiter */
+ if ((*(p = path + strlen (path) -1) == '\\') || (*p == '/')) *p = '\0';
+ env_init (user,path); /* initialize environment */
+ }
+ if (myUserName) { /* logged in? */
+ if (flags) /* Guest is an anonymous user */
+ *flags = _stricmp (myUserName,"Guest") ? MU_LOGGEDIN : MU_ANONYMOUS;
+ ret = myUserName; /* return user name */
+ }
+ else if (flags) *flags = MU_NOTLOGGEDIN;
+ return ret;
+}
+
+/* Return my local host name
+ * Returns: my local host name
+ */
+
+char *mylocalhost (void)
+{
+ if (!myLocalHost) {
+ char tmp[MAILTMPLEN];
+ if (!wsa_initted++) { /* init Windows Sockets */
+ WSADATA wsock;
+ if (WSAStartup (WINSOCK_VERSION,&wsock)) {
+ wsa_initted = 0;
+ return "random-pc"; /* try again later? */
+ }
+ }
+ myLocalHost = cpystr ((gethostname (tmp,MAILTMPLEN-1) == SOCKET_ERROR) ?
+ "random-pc" : tcp_canonical (tmp));
+ }
+ return myLocalHost;
+}
+
+/* Return my home directory name
+ * Returns: my home directory name
+ */
+
+char *myhomedir ()
+{
+ if (!myHomeDir) myusername ();/* initialize if first time */
+ return myHomeDir ? myHomeDir : "";
+}
+
+
+/* Return system standard INBOX
+ * Accepts: buffer string
+ */
+
+char *sysinbox ()
+{
+ char tmp[MAILTMPLEN];
+ if (!sysInbox) { /* initialize if first time */
+ if (check_nt ()) sprintf (tmp,MAILFILE,myUserName);
+ else sprintf (tmp,"%s\\INBOX",myhomedir ());
+ sysInbox = cpystr (tmp); /* system inbox is from mail spool */
+ }
+ return sysInbox;
+}
+
+
+/* Return mailbox directory name
+ * Accepts: destination buffer
+ * directory prefix
+ * name in directory
+ * Returns: file name or NIL if error
+ */
+
+char *mailboxdir (char *dst,char *dir,char *name)
+{
+ char tmp[MAILTMPLEN];
+ if (dir || name) { /* if either argument provided */
+ if (dir) {
+ if (strlen (dir) > NETMAXMBX) return NIL;
+ strcpy (tmp,dir); /* write directory prefix */
+ }
+ else tmp[0] = '\0'; /* otherwise null string */
+ if (name) {
+ if (strlen (name) > NETMAXMBX) return NIL;
+ strcat (tmp,name); /* write name in directory */
+ }
+ /* validate name, return its name */
+ if (!mailboxfile (dst,tmp)) return NIL;
+ }
+ else strcpy (dst,myhomedir());/* no arguments, wants home directory */
+ return dst; /* return the name */
+}
+
+/* Return mailbox file name
+ * Accepts: destination buffer
+ * mailbox name
+ * Returns: file name or empty string for driver-selected INBOX or NIL if error
+ */
+
+char *mailboxfile (char *dst,char *name)
+{
+ char homedev[3];
+ char *dir = myhomedir ();
+ if (dir[0] && isalpha (dir[0]) && (dir[1] == ':')) {
+ homedev[0] = dir[0]; /* copy home device */
+ homedev[1] = dir[1];
+ homedev[2] = '\0';
+ }
+ else homedev[0] = '\0'; /* ??no home device?? */
+ *dst = '\0'; /* default to empty string */
+ /* check for INBOX */
+ if (!compare_cstring (name,"INBOX"));
+ /* reject names with / */
+ else if (strchr (name,'/')) dst = NIL;
+ else switch (*name) {
+ case '#': /* namespace names */
+ if (((name[1] == 'u') || (name[1] == 'U')) &&
+ ((name[2] == 's') || (name[2] == 'S')) &&
+ ((name[3] == 'e') || (name[3] == 'E')) &&
+ ((name[4] == 'r') || (name[4] == 'R')) && (name[5] == '.')) {
+ /* copy user name to destination buffer */
+ for (dir = dst,name += 6; *name && (*name != '\\'); *dir++ = *name++);
+ *dir++ = '\0'; /* tie off user name */
+ /* look up homedir for user name */
+ if (dir = win_homedir (dst)) {
+ /* build resulting name */
+ sprintf (dst,"%s\\%s",dir,name);
+ fs_give ((void **) &dir);
+ }
+ else dst = NIL;
+ }
+ else dst = NIL; /* unknown namespace name */
+ break;
+ case '\\': /* absolute path on default drive? */
+ sprintf (dst,"%s%s",homedev,name);
+ break;
+ default: /* any other name */
+ if (name[1] == ':') { /* some other drive? */
+ if (name[2] == '\\') strcpy (dst,name);
+ else sprintf (dst,"%c:\\%s",name[0],name+2);
+ }
+ /* build home-directory relative name */
+ else sprintf (dst,"%s\\%s",dir,name);
+ }
+ return dst; /* return it */
+}
+
+/* Lock file name
+ * Accepts: return buffer for file name
+ * file name
+ * locking to be placed on file if non-NIL
+ * Returns: file descriptor of lock or -1 if error
+ */
+
+int lockname (char *lock,char *fname,int op)
+{
+ int ld;
+ char c,*s;
+ /* Win2K and Win98 have TEMP under windir */
+ if (!((s = lockdir (lock,getenv ("windir"),"TEMP")) ||
+ /* NT4, NT3.x and Win95 use one of these */
+ (s = lockdir (lock,getenv ("TEMP"),NIL)) ||
+ (s = lockdir (lock,getenv ("TMP"),NIL)) ||
+ (s = lockdir (lock,getenv ("TMPDIR"),NIL)) ||
+ /* try one of these */
+ (s = lockdir (lock,defaultDrive (),"WINNT\\TEMP")) ||
+ (s = lockdir (lock,defaultDrive (),"WINDOWS\\TEMP")) ||
+ /* C:\TEMP is last resort */
+ (s = lockdir (lock,defaultDrive (),"TEMP")))) {
+ mm_log ("Unable to find temporary directory",ERROR);
+ return -1;
+ }
+ /* generate file name */
+ while (c = *fname++) switch (c) {
+ case '/': case '\\': case ':':
+ *s++ = '!'; /* convert bad chars to ! */
+ break;
+ default:
+ *s++ = c;
+ break;
+ }
+ *s++ = c; /* tie off name */
+ /* get the lock */
+ if (((ld = open (lock,O_BINARY|O_RDWR|O_CREAT,S_IREAD|S_IWRITE)) >= 0) && op)
+ flock (ld,op); /* apply locking function */
+ return ld; /* return locking file descriptor */
+}
+
+/* Build lock directory, check to see if it exists
+ * Accepts: return buffer for lock directory
+ * first part of possible name
+ * optional second part
+ * Returns: pointer to end of buffer if buffer has a good name, else NIL
+ */
+
+char *lockdir (char *lock,char *first,char *last)
+{
+ struct stat sbuf;
+ char c,*s;
+ if (first && *first) { /* first part must be non-NIL */
+ /* copy first part */
+ for (s = lock; c = *first++; *s++ = (c == '/') ? '\\' : c);
+ if (last && *last) { /* copy last part if specified */
+ /* write trailing \ in case not in first */
+ if (s[-1] != '\\') *s++ = '\\';
+ while (c = *last++) *s++ = (c == '/') ? '\\' : c;
+ }
+ if (s[-1] == '\\') --s; /* delete trailing \ if any */
+ *s = s[1] = '\0'; /* tie off name at this point */
+ if (!stat (lock,&sbuf)) { /* does the name exist? */
+ *s++ = '\\'; /* yes, reinstall trailing \ */
+ return s; /* return the name */
+ }
+ }
+ return NIL; /* failed */
+}
+
+
+/* Unlock file descriptor
+ * Accepts: file descriptor
+ * lock file name from lockfd()
+ */
+
+void unlockfd (int fd,char *lock)
+{
+ flock (fd,LOCK_UN); /* unlock it */
+ close (fd); /* close it */
+}
+
+
+/* Determine default prototype stream to user
+ * Accepts: type (NIL for create, T for append)
+ * Returns: default prototype stream
+ */
+
+MAILSTREAM *default_proto (long type)
+{
+ extern MAILSTREAM CREATEPROTO,APPENDPROTO;
+ return type ? &APPENDPROTO : &CREATEPROTO;
+}
+
+/* Default block notify routine
+ * Accepts: reason for calling
+ * data
+ * Returns: data
+ */
+
+void *mm_blocknotify (int reason,void *data)
+{
+ void *ret = data;
+ switch (reason) {
+ case BLOCK_SENSITIVE: /* entering sensitive code */
+ ret = (void *) alarm (0);
+ break;
+ case BLOCK_NONSENSITIVE: /* exiting sensitive code */
+ if ((unsigned int) data) alarm ((unsigned int) data);
+ break;
+ default: /* ignore all other reasons */
+ break;
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/nt/env_nt.h b/imap/src/osdep/nt/env_nt.h
new file mode 100644
index 00000000..95575246
--- /dev/null
+++ b/imap/src/osdep/nt/env_nt.h
@@ -0,0 +1,68 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: NT environment routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+#define SUBSCRIPTIONFILE(t) sprintf (t,"%s\\MAILBOX.LST",myhomedir ())
+#define SUBSCRIPTIONTEMP(t) sprintf (t,"%s\\MAILBOX.TMP",myhomedir ())
+
+/* Note: the \CRAM-MD5.PWD file only works on DOS-based Windows (Win9x/Me)
+ * and not on NT-based Windows (WinNT/2K/XP). If installed on NT-based
+ * Windows, servers will advertise CRAM-MD5 authentication but it will
+ * never succeed.
+ */
+
+#define MD5ENABLE "\\cram-md5.pwd"
+
+#define L_SET SEEK_SET
+
+/* Function prototypes */
+
+#include "env.h"
+
+void rfc822_fixed_date (char *date);
+long env_init (char *user,char *home);
+int check_nt (void);
+char *win_homedir (char *user);
+static char *defaultDrive (void);
+char *myusername_full (unsigned long *flags);
+#define MU_LOGGEDIN 0
+#define MU_NOTLOGGEDIN 1
+#define MU_ANONYMOUS 2
+#define myusername() \
+ myusername_full (NIL)
+char *sysinbox ();
+char *mailboxdir (char *dst,char *dir,char *name);
+int lockname (char *lock,char *fname,int op);
+char *lockdir (char *lock,char *first,char *last);
+void unlockfd (int fd,char *lock);
+long safe_write (int fd,char *buf,long nbytes);
+void *mm_blocknotify (int reason,void *data);
+long random ();
+#if _MSC_VER < 700
+#define getpid random
+#endif
diff --git a/imap/src/osdep/nt/fdstring.c b/imap/src/osdep/nt/fdstring.c
new file mode 100644
index 00000000..7a491f7d
--- /dev/null
+++ b/imap/src/osdep/nt/fdstring.c
@@ -0,0 +1,99 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: File descriptor string routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 April 1997
+ * Last Edited: 4 April 2007
+ */
+
+#include "mail.h"
+#include "osdep.h"
+#include "misc.h"
+#include "fdstring.h"
+
+/* String driver for fd stringstructs */
+
+static void fd_string_init (STRING *s,void *data,unsigned long size);
+static char fd_string_next (STRING *s);
+static void fd_string_setpos (STRING *s,unsigned long i);
+
+STRINGDRIVER fd_string = {
+ fd_string_init, /* initialize string structure */
+ fd_string_next, /* get next byte in string structure */
+ fd_string_setpos /* set position in string structure */
+};
+
+
+/* Initialize string structure for fd stringstruct
+ * Accepts: string structure
+ * pointer to string
+ * size of string
+ */
+
+static void fd_string_init (STRING *s,void *data,unsigned long size)
+{
+ FDDATA *d = (FDDATA *) data;
+ /* note fd */
+ s->data = (void *) (unsigned long) d->fd;
+ s->data1 = d->pos; /* note file offset */
+ s->size = size; /* note size */
+ s->curpos = s->chunk = d->chunk;
+ s->chunksize = (unsigned long) d->chunksize;
+ s->offset = 0; /* initial position */
+ /* and size of data */
+ s->cursize = min (s->chunksize,size);
+ /* move to that position in the file */
+ lseek (d->fd,d->pos,L_SET);
+ read (d->fd,s->chunk,(size_t) s->cursize);
+}
+
+/* Get next character from fd stringstruct
+ * Accepts: string structure
+ * Returns: character, string structure chunk refreshed
+ */
+
+static char fd_string_next (STRING *s)
+{
+ char c = *s->curpos++; /* get next byte */
+ SETPOS (s,GETPOS (s)); /* move to next chunk */
+ return c; /* return the byte */
+}
+
+
+/* Set string pointer position for fd stringstruct
+ * Accepts: string structure
+ * new position
+ */
+
+static void fd_string_setpos (STRING *s,unsigned long i)
+{
+ if (i > s->size) i = s->size; /* don't permit setting beyond EOF */
+ s->offset = i; /* set new offset */
+ s->curpos = s->chunk; /* reset position */
+ /* set size of data */
+ if (s->cursize = min (s->chunksize,SIZE (s))) {
+ /* move to that position in the file */
+ lseek ((long) s->data,s->data1 + s->offset,L_SET);
+ read ((long) s->data,s->curpos,(size_t) s->cursize);
+ }
+}
diff --git a/imap/src/osdep/nt/fdstring.h b/imap/src/osdep/nt/fdstring.h
new file mode 100644
index 00000000..d0a021bf
--- /dev/null
+++ b/imap/src/osdep/nt/fdstring.h
@@ -0,0 +1,39 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: File descriptor string routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 April 1997
+ * Last Edited: 30 August 2006
+ */
+
+/* Driver-dependent data passed to init method */
+
+typedef struct fd_data {
+ int fd; /* file descriptor */
+ unsigned long pos; /* initial position */
+ char *chunk; /* I/O buffer chunk */
+ unsigned long chunksize; /* I/O buffer chunk length */
+} FDDATA;
+
+
+extern STRINGDRIVER fd_string;
diff --git a/imap/src/osdep/nt/fs_nt.c b/imap/src/osdep/nt/fs_nt.c
new file mode 100644
index 00000000..03908328
--- /dev/null
+++ b/imap/src/osdep/nt/fs_nt.c
@@ -0,0 +1,62 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Free storage management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Get a block of free storage
+ * Accepts: size of desired block
+ * Returns: free storage block
+ */
+
+void *fs_get (size_t size)
+{
+ void *block = malloc (size ? size : (size_t) 1);
+ if (!block) fatal ("Out of memory");
+ return (block);
+}
+
+
+/* Resize a block of free storage
+ * Accepts: ** pointer to current block
+ * new size
+ */
+
+void fs_resize (void **block,size_t size)
+{
+ if (!(*block = realloc (*block,size ? size : (size_t) 1)))
+ fatal ("Can't resize memory");
+}
+
+
+/* Return a block of free storage
+ * Accepts: ** pointer to free storage block
+ */
+
+void fs_give (void **block)
+{
+ free (*block);
+ *block = NIL;
+}
diff --git a/imap/src/osdep/nt/ftl_nt.c b/imap/src/osdep/nt/ftl_nt.c
new file mode 100644
index 00000000..9e65ef55
--- /dev/null
+++ b/imap/src/osdep/nt/ftl_nt.c
@@ -0,0 +1,38 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: DOS/VMS/TOPS-20 crash management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Report a fatal error
+ * Accepts: string to output
+ */
+
+void fatal (char *string)
+{
+ mm_fatal (string); /* pass up the string */
+ abort (); /* die horribly */
+}
diff --git a/imap/src/osdep/nt/ip4_nt.c b/imap/src/osdep/nt/ip4_nt.c
new file mode 100644
index 00000000..23d399e1
--- /dev/null
+++ b/imap/src/osdep/nt/ip4_nt.c
@@ -0,0 +1,184 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX IPv4 routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 18 December 2003
+ * Last Edited: 30 August 2006
+ */
+
+#define SADRLEN sizeof (struct sockaddr)
+
+#define SADR4(sadr) ((struct sockaddr_in *) sadr)
+#define SADR4LEN sizeof (struct sockaddr_in)
+#define SADR4ADR(sadr) SADR4 (sadr)->sin_addr
+#define ADR4LEN sizeof (struct in_addr)
+#define SADR4PORT(sadr) SADR4 (sadr)->sin_port
+
+
+/* IP abstraction layer */
+
+char *ip_sockaddrtostring (struct sockaddr *sadr);
+long ip_sockaddrtoport (struct sockaddr *sadr);
+void *ip_stringtoaddr (char *text,size_t *len,int *family);
+struct sockaddr *ip_newsockaddr (size_t *len);
+struct sockaddr *ip_sockaddr (int family,void *adr,size_t adrlen,
+ unsigned short port,size_t *len);
+char *ip_sockaddrtoname (struct sockaddr *sadr);
+void *ip_nametoaddr (char *name,size_t *len,int *family,char **canonical,
+ void **next);
+
+/* Return IP address string from socket address
+ * Accepts: socket address
+ * Returns: IP address as name string
+ */
+
+char *ip_sockaddrtostring (struct sockaddr *sadr)
+{
+ return (sadr->sa_family == PF_INET) ?
+ inet_ntoa (SADR4ADR (sadr)) : "NON-IPv4";
+}
+
+
+/* Return port from socket address
+ * Accepts: socket address
+ * Returns: port number or -1 if can't determine it
+ */
+
+long ip_sockaddrtoport (struct sockaddr *sadr)
+{
+ return (sadr->sa_family == PF_INET) ? ntohs (SADR4PORT (sadr)) : -1;
+}
+
+
+/* Return IP address from string
+ * Accepts: name string
+ * pointer to returned length
+ * pointer to returned address family
+ * Returns: address if valid, length and family updated, or NIL
+ */
+
+void *ip_stringtoaddr (char *text,size_t *len,int *family)
+{
+ unsigned long adr;
+ struct in_addr *ret;
+ /* get address */
+ if ((adr = inet_addr (text)) == -1) ret = NIL;
+ else { /* make in_addr */
+ ret = (struct in_addr *) fs_get (*len = ADR4LEN);
+ *family = AF_INET; /* IPv4 */
+ ret->s_addr = adr; /* set address */
+ }
+ return (void *) ret;
+}
+
+/* Create a maximum-size socket address
+ * Accepts: pointer to return maximum socket address length
+ * Returns: new, empty socket address of maximum size
+ */
+
+struct sockaddr *ip_newsockaddr (size_t *len)
+{
+ return (struct sockaddr *) memset (fs_get (SADRLEN),0,*len = SADRLEN);
+}
+
+
+/* Stuff a socket address
+ * Accepts: address family
+ * IPv4 address
+ * length of address (always 4 in IPv4)
+ * port number
+ * pointer to return socket address length
+ * Returns: socket address or NIL if error
+ */
+
+struct sockaddr *ip_sockaddr (int family,void *adr,size_t adrlen,
+ unsigned short port,size_t *len)
+{
+ struct sockaddr *sadr = ip_newsockaddr (len);
+ switch (family) { /* build socket address based upon family */
+ case AF_INET: /* IPv4 */
+ sadr->sa_family = PF_INET;
+ /* copy host address */
+ memcpy (&SADR4ADR (sadr),adr,adrlen);
+ /* copy port number in network format */
+ SADR4PORT (sadr) = htons (port);
+ *len = SADR4LEN;
+ break;
+ default: /* non-IP?? */
+ sadr->sa_family = PF_UNSPEC;
+ break;
+ }
+ return sadr;
+}
+
+/* Return name from socket address
+ * Accepts: socket address
+ * Returns: canonical name for that address or NIL if none
+ */
+
+char *ip_sockaddrtoname (struct sockaddr *sadr)
+{
+ struct hostent *he;
+ return ((sadr->sa_family == PF_INET) &&
+ (he = gethostbyaddr ((char *) &SADR4ADR (sadr),ADR4LEN,AF_INET))) ?
+ (char *) he->h_name : NIL;
+}
+
+
+/* Return address from name
+ * Accepts: name or NIL to return next address
+ * pointer to previous/returned length
+ * pointer to previous/returned address family
+ * pointer to previous/returned canonical name
+ * pointer to previous/return state for next-address calls
+ * Returns: address with length/family/canonical updated if needed, or NIL
+ */
+
+void *ip_nametoaddr (char *name,size_t *len,int *family,char **canonical,
+ void **next)
+{
+ char **adl,tmp[MAILTMPLEN];
+ struct hostent *he;
+ if (name) { /* first lookup? */
+ /* yes, do case-independent lookup */
+ if ((strlen (name) < MAILTMPLEN) &&
+ (he = gethostbyname (lcase (strcpy (tmp,name))))) {
+ adl = he->h_addr_list;
+ if (len) *len = he->h_length;
+ if (family) *family = he->h_addrtype;
+ if (canonical) *canonical = (char *) he->h_name;
+ if (next) *next = (void *) adl;
+ }
+ else { /* error */
+ adl = NIL;
+ if (len) *len = 0;
+ if (family) *family = 0;
+ if (canonical) *canonical = NIL;
+ if (next) *next = NIL;
+ }
+ }
+ /* return next in series */
+ else if (next && (adl = (char **) *next)) *next = ++adl;
+ else adl = NIL; /* failure */
+ return adl ? (void *) *adl : NIL;
+}
diff --git a/imap/src/osdep/nt/ip6_nt.c b/imap/src/osdep/nt/ip6_nt.c
new file mode 100644
index 00000000..c15dfbc0
--- /dev/null
+++ b/imap/src/osdep/nt/ip6_nt.c
@@ -0,0 +1,288 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX IPv6 routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 18 December 2003
+ * Last Edited: 30 August 2006
+ */
+
+
+/*
+ * There is some amazingly bad design in IPv6 sockets.
+ *
+ * Supposedly, the new getnameinfo() and getaddrinfo() functions create an
+ * abstraction that is not dependent upon IPv4 or IPv6. However, the
+ * definition of getnameinfo() requires that the caller pass the length of
+ * the sockaddr instead of deriving it from sa_family. The man page says
+ * that there's an sa_len member in the sockaddr, but actually there isn't.
+ * This means that any caller to getnameinfo() and getaddrinfo() has to know
+ * the size for the protocol family used by that sockaddr.
+ *
+ * The new sockaddr_in6 is bigger than the generic sockaddr (which is what
+ * connect(), accept(), bind(), getpeername(), getsockname(), etc. expect).
+ * Rather than increase the size of sockaddr, there's a new sockaddr_storage
+ * which is only usable for allocating space.
+ */
+
+#define SADRLEN sizeof (struct sockaddr_storage)
+
+#define SADR4(sadr) ((struct sockaddr_in *) sadr)
+#define SADR4LEN sizeof (struct sockaddr_in)
+#define SADR4ADR(sadr) SADR4 (sadr)->sin_addr
+#define ADR4LEN sizeof (struct in_addr)
+#define SADR4PORT(sadr) SADR4 (sadr)->sin_port
+
+#define SADR6(sadr) ((struct sockaddr_in6 *) sadr)
+#define SADR6LEN sizeof (struct sockaddr_in6)
+#define SADR6ADR(sadr) SADR6 (sadr)->sin6_addr
+#define ADR6LEN sizeof (struct in6_addr)
+#define SADR6PORT(sadr) SADR6 (sadr)->sin6_port
+
+
+/* IP abstraction layer */
+
+char *ip_sockaddrtostring (struct sockaddr *sadr);
+long ip_sockaddrtoport (struct sockaddr *sadr);
+void *ip_stringtoaddr (char *text,size_t *len,int *family);
+struct sockaddr *ip_newsockaddr (size_t *len);
+struct sockaddr *ip_sockaddr (int family,void *adr,size_t adrlen,
+ unsigned short port,size_t *len);
+char *ip_sockaddrtoname (struct sockaddr *sadr);
+void *ip_nametoaddr (char *name,size_t *len,int *family,char **canonical,
+ void **next);
+
+/* Return IP address string from socket address
+ * Accepts: socket address
+ * Returns: IP address as name string
+ */
+
+char *ip_sockaddrtostring (struct sockaddr *sadr)
+{
+ static char tmp[NI_MAXHOST];
+ switch (sadr->sa_family) {
+ case PF_INET: /* IPv4 */
+ if (!getnameinfo (sadr,SADR4LEN,tmp,NI_MAXHOST,NIL,NIL,NI_NUMERICHOST))
+ return tmp;
+ break;
+ case PF_INET6: /* IPv6 */
+ if (!getnameinfo (sadr,SADR6LEN,tmp,NI_MAXHOST,NIL,NIL,NI_NUMERICHOST))
+ return tmp;
+ break;
+ }
+ return "NON-IP";
+}
+
+
+/* Return port from socket address
+ * Accepts: socket address
+ * Returns: port number or -1 if can't determine it
+ */
+
+long ip_sockaddrtoport (struct sockaddr *sadr)
+{
+ switch (sadr->sa_family) {
+ case PF_INET:
+ return ntohs (SADR4PORT (sadr));
+ case PF_INET6:
+ return ntohs (SADR6PORT (sadr));
+ }
+ return -1;
+}
+
+/* Return IP address from string
+ * Accepts: name string
+ * pointer to returned length
+ * pointer to returned address family
+ * Returns: address if valid, length and family updated, or NIL
+ */
+
+void *ip_stringtoaddr (char *text,size_t *len,int *family)
+
+{
+ char tmp[MAILTMPLEN];
+ static struct addrinfo *hints;
+ struct addrinfo *ai;
+ void *adr = NIL;
+ if (!hints) { /* hints set up yet? */
+ hints = (struct addrinfo *) /* one-time setup */
+ memset (fs_get (sizeof (struct addrinfo)),0,sizeof (struct addrinfo));
+ hints->ai_family = AF_UNSPEC;/* allow any address family */
+ hints->ai_socktype = SOCK_STREAM;
+ /* numeric name only */
+ hints->ai_flags = AI_NUMERICHOST;
+ }
+ /* case-independent lookup */
+ if (text && (strlen (text) < MAILTMPLEN) &&
+ (!getaddrinfo (lcase (strcpy (tmp,text)),NIL,hints,&ai))) {
+ switch (*family = ai->ai_family) {
+ case AF_INET: /* IPv4 */
+ adr = fs_get (*len = ADR4LEN);
+ memcpy (adr,(void *) &SADR4ADR (ai->ai_addr),*len);
+ break;
+ case AF_INET6: /* IPv6 */
+ adr = fs_get (*len = ADR6LEN);
+ memcpy (adr,(void *) &SADR6ADR (ai->ai_addr),*len);
+ break;
+ }
+ freeaddrinfo (ai); /* free addrinfo */
+ }
+ return adr;
+}
+
+/* Create a maximum-size socket address
+ * Accepts: pointer to return maximum socket address length
+ * Returns: new, empty socket address of maximum size
+ */
+
+struct sockaddr *ip_newsockaddr (size_t *len)
+{
+ return (struct sockaddr *) memset (fs_get (SADRLEN),0,*len = SADRLEN);
+}
+
+
+/* Stuff a socket address
+ * Accepts: address family
+ * IPv4 address
+ * length of address
+ * port number
+ * pointer to return socket address length
+ * Returns: socket address
+ */
+
+struct sockaddr *ip_sockaddr (int family,void *adr,size_t adrlen,
+ unsigned short port,size_t *len)
+{
+ struct sockaddr *sadr = ip_newsockaddr (len);
+ switch (family) { /* build socket address based upon family */
+ case AF_INET: /* IPv4 */
+ sadr->sa_family = PF_INET;
+ /* copy host address */
+ memcpy (&SADR4ADR (sadr),adr,adrlen);
+ /* copy port number in network format */
+ SADR4PORT (sadr) = htons (port);
+ *len = SADR4LEN;
+ break;
+ case AF_INET6: /* IPv6 */
+ sadr->sa_family = PF_INET6;
+ /* copy host address */
+ memcpy (&SADR6ADR (sadr),adr,adrlen);
+ /* copy port number in network format */
+ SADR6PORT (sadr) = htons (port);
+ *len = SADR6LEN;
+ break;
+ default: /* non-IP?? */
+ sadr->sa_family = PF_UNSPEC;
+ break;
+ }
+ return sadr;
+}
+
+/* Return name from socket address
+ * Accepts: socket address
+ * Returns: canonical name for that address or NIL if none
+ */
+
+char *ip_sockaddrtoname (struct sockaddr *sadr)
+{
+ static char tmp[NI_MAXHOST];
+ switch (sadr->sa_family) {
+ case PF_INET: /* IPv4 */
+ if (!getnameinfo (sadr,SADR4LEN,tmp,NI_MAXHOST,NIL,NIL,NI_NAMEREQD))
+ return tmp;
+ break;
+ case PF_INET6: /* IPv6 */
+ if (!getnameinfo (sadr,SADR6LEN,tmp,NI_MAXHOST,NIL,NIL,NI_NAMEREQD))
+ return tmp;
+ break;
+ }
+ return NIL;
+}
+
+/* Return address from name
+ * Accepts: name or NIL to return next address
+ * pointer to previous/returned length
+ * pointer to previous/returned address family
+ * pointer to previous/returned canonical name
+ * pointer to previous/return state for next-address calls
+ * Returns: address with length/family/canonical updated if needed, or NIL
+ */
+
+void *ip_nametoaddr (char *name,size_t *len,int *family,char **canonical,
+ void **next)
+{
+ struct addrinfo *cur = NIL;
+ static struct addrinfo *hints;
+ static struct addrinfo *ai = NIL;
+ static char lcname[MAILTMPLEN];
+ if (!hints) { /* hints set up yet? */
+ hints = (struct addrinfo *) /* one-time setup */
+ memset (fs_get (sizeof (struct addrinfo)),0,sizeof (struct addrinfo));
+ /* allow any address family */
+ hints->ai_family = AF_UNSPEC;
+ hints->ai_socktype = SOCK_STREAM;
+ /* need canonical name */
+ hints->ai_flags = AI_CANONNAME;
+ }
+ if (name) { /* name supplied? */
+ if (ai) {
+ freeaddrinfo (ai); /* free old addrinfo */
+ ai = NIL;
+ }
+ /* case-independent lookup */
+ if ((strlen (name) < MAILTMPLEN) &&
+ (!getaddrinfo (lcase (strcpy (lcname,name)),NIL,hints,&ai))) {
+ cur = ai; /* current block */
+ if (canonical) /* set canonical name */
+ *canonical = cur->ai_canonname ? cur->ai_canonname : lcname;
+ /* remember as next block */
+ if (next) *next = (void *) ai;
+ }
+ else { /* error */
+ cur = NIL;
+ if (len) *len = 0;
+ if (family) *family = 0;
+ if (canonical) *canonical = NIL;
+ if (next) *next = NIL;
+ }
+ }
+ /* return next in series */
+ else if (next && (cur = ((struct addrinfo *) *next)->ai_next)) {
+ *next = cur; /* set as last address */
+ /* set canonical in case changed */
+ if (canonical && cur->ai_canonname) *canonical = cur->ai_canonname;
+ }
+
+ if (cur) { /* got data? */
+ if (family) *family = cur->ai_family;
+ switch (cur->ai_family) {
+ case AF_INET:
+ if (len) *len = ADR4LEN;
+ return (void *) &SADR4ADR (cur->ai_addr);
+ case AF_INET6:
+ if (len) *len = ADR6LEN;
+ return (void *) &SADR6ADR (cur->ai_addr);
+ }
+ }
+ if (len) *len = 0; /* error return */
+ return NIL;
+}
diff --git a/imap/src/osdep/nt/kerb_mit.c b/imap/src/osdep/nt/kerb_mit.c
new file mode 100644
index 00000000..57ede524
--- /dev/null
+++ b/imap/src/osdep/nt/kerb_mit.c
@@ -0,0 +1,74 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MIT Kerberos routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 4 March 2003
+ * Last Edited: 30 August 2006
+ */
+
+#define PROTOTYPE(x) x
+#include <gssapi/gssapi_generic.h>
+#include <gssapi/gssapi_krb5.h>
+
+
+long kerberos_server_valid (void);
+long kerberos_try_kinit (OM_uint32 error);
+char *kerberos_login (char *user,char *authuser,int argc,char *argv[]);
+
+/* Kerberos server valid check
+ * Returns: T if have keytab, NIL otherwise
+ */
+
+long kerberos_server_valid ()
+{
+ return NIL;
+}
+
+
+/* Kerberos check for missing or expired credentials
+ * Returns: T if should suggest running kinit, NIL otherwise
+ */
+
+long kerberos_try_kinit (OM_uint32 error)
+{
+ switch (error) {
+ case KRB5KRB_AP_ERR_TKT_EXPIRED:
+ case KRB5_FCC_NOFILE: /* MIT */
+ case KRB5_CC_NOTFOUND: /* Heimdal */
+ return LONGT;
+ }
+ return NIL;
+}
+
+/* Kerberos server log in
+ * Accepts: authorization ID as user name
+ * authentication ID as Kerberos principal
+ * argument count
+ * argument vector
+ * Returns: logged in user name if logged in, NIL otherwise
+ */
+
+char *kerberos_login (char *user,char *authuser,int argc,char *argv[])
+{
+ return NIL;
+}
diff --git a/imap/src/osdep/nt/kerb_w2k.c b/imap/src/osdep/nt/kerb_w2k.c
new file mode 100644
index 00000000..38d0ce30
--- /dev/null
+++ b/imap/src/osdep/nt/kerb_w2k.c
@@ -0,0 +1,699 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: GSSAPI Kerberos Shim 5 for Windows 2000/XP IMAP Toolkit
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 6 March 2000
+ * Last Edited: 30 August 2006
+ */
+
+/* The purpose of this module is to be a shim, so that the auth_gss.c module
+ * (written for MIT Kerberos) will compile, link, and run with SSPI Kerberos
+ * on Windows 2000 systems.
+ * There is no attempt whatsoever to make this be a complete implementation
+ * of GSSAPI. A number of shortcuts were taken that a real GSSAPI
+ * implementation for SSPI can't do.
+ * Nor is there any attempt to make the types identical with MIT Kerberos;
+ * you can't link this library with object files compiled with the MIT
+ * Kerberos .h files.
+ */
+
+
+/* GSSAPI generic definitions */
+
+
+#define SECURITY_WIN32
+#include <security.h>
+
+
+/* GSSAPI types for which we use SSPI equivalent types */
+
+typedef ULONG OM_uint32;
+typedef PCredHandle gss_cred_id_t;
+typedef ULONG gss_cred_usage_t;
+typedef PCtxtHandle gss_ctx_id_t;
+typedef SEC_CHAR * gss_name_t;
+typedef ULONG gss_qop_t;
+
+
+/* Major status codes */
+
+#define GSS_S_COMPLETE SEC_E_OK
+#define GSS_S_BAD_MECH SEC_E_SECPKG_NOT_FOUND
+#define GSS_S_CONTINUE_NEEDED SEC_I_CONTINUE_NEEDED
+#define GSS_S_CREDENTIALS_EXPIRED SEC_E_CERT_EXPIRED
+#define GSS_S_FAILURE SEC_E_INTERNAL_ERROR
+#define GSS_S_NO_CRED SEC_E_NO_CREDENTIALS
+#define GSS_S_NO_CONTEXT SEC_E_INVALID_HANDLE
+
+
+/* Flag bits for context-level services */
+
+#define GSS_C_DELEG_FLAG ISC_REQ_DELEGATE
+#define GSS_C_MUTUAL_FLAG ISC_REQ_MUTUAL_AUTH
+#define GSS_C_REPLAY_FLAG ISC_REQ_REPLAY_DETECT
+#define GSS_C_SEQUENCE_FLAG ISC_REQ_SEQUENCE_DETECT
+#define GSS_C_CONF_FLAG ISC_REQ_CONFIDENTIALITY
+#define GSS_C_INTEG_FLAG ISC_REQ_INTEGRITY
+
+
+/* Credential usage options */
+
+#define GSS_C_BOTH SECPKG_CRED_BOTH
+#define GSS_C_INITIATE SECPKG_CRED_OUTBOUND
+#define GSS_C_ACCEPT SECPKG_CRED_INBOUND
+
+
+/* Major status codes defined by shim */
+
+#define GSS_S_BAD_BINDINGS 100
+#define GSS_S_BAD_NAME 101
+#define GSS_S_BAD_NAMETYPE 102
+#define GSS_S_BAD_STATUS 103
+
+/* GSSAPI types as used in GSSAPI */
+
+
+/* Buffer */
+
+typedef struct gss_buffer_desc_struct {
+ size_t length;
+ void *value;
+} gss_buffer_desc,*gss_buffer_t;
+
+
+/* Object identifier */
+
+typedef struct gss_OID_desc_struct {
+ OM_uint32 length;
+ void *elements;
+} gss_OID_desc,*gss_OID;
+
+typedef struct gss_OID_set_desc_struct {
+ size_t count;
+ gss_OID elements;
+} gss_OID_set_desc,*gss_OID_set;
+
+
+/* Unused, but needed in prototypes */
+
+typedef void * gss_channel_bindings_t;
+
+
+/* Default constants */
+
+#define GSS_C_EMPTY_BUFFER {0,NIL}
+#define GSS_C_NO_BUFFER ((gss_buffer_t) NIL)
+#define GSS_C_NO_OID ((gss_OID) NIL)
+#define GSS_C_NO_CONTEXT ((gss_ctx_id_t) NIL)
+#define GSS_C_NO_CREDENTIAL ((gss_cred_id_t) NIL)
+#define GSS_C_NO_CHANNEL_BINDINGS ((gss_channel_bindings_t) NIL)
+#define GSS_C_QOP_DEFAULT NIL
+
+
+/* Status code types for gss_display_status */
+
+#define GSS_C_GSS_CODE 1
+#define GSS_C_MECH_CODE 2
+
+
+/* GSSAPI constants */
+
+const gss_OID gss_nt_service_name;
+#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
+const gss_OID gss_mech_krb5;
+const gss_OID_set gss_mech_set_krb5;
+
+/* GSSAPI prototypes */
+
+
+OM_uint32 gss_accept_sec_context (OM_uint32 *minor_status,
+ gss_ctx_id_t *context_handle,
+ gss_cred_id_t acceptor_cred_handle,
+ gss_buffer_t input_token_buffer,
+ gss_channel_bindings_t input_chan_bindings,
+ gss_name_t *src_name,gss_OID *mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 *ret_flags,OM_uint32 *time_rec,
+ gss_cred_id_t *delegated_cred_handle);
+OM_uint32 gss_acquire_cred (OM_uint32 *minor_status,gss_name_t desired_name,
+ OM_uint32 time_req,gss_OID_set desired_mechs,
+ gss_cred_usage_t cred_usage,
+ gss_cred_id_t *output_cred_handle,
+ gss_OID_set *actual_mechs,OM_uint32 *time_rec);
+OM_uint32 gss_delete_sec_context (OM_uint32 *minor_status,
+ gss_ctx_id_t *context_handle,
+ gss_buffer_t output_token);
+OM_uint32 gss_display_name (OM_uint32 *minor_status,gss_name_t input_name,
+ gss_buffer_t output_name_buffer,
+ gss_OID *output_name_type);
+OM_uint32 gss_display_status (OM_uint32 *minor_status,OM_uint32 status_value,
+ int status_type,gss_OID mech_type,
+ OM_uint32 *message_context,
+ gss_buffer_t status_string);
+OM_uint32 gss_import_name (OM_uint32 *minor_status,
+ gss_buffer_t input_name_buffer,
+ gss_OID input_name_type,gss_name_t *output_name);
+OM_uint32 gss_init_sec_context (OM_uint32 *minor_status,
+ gss_cred_id_t claimant_cred_handle,
+ gss_ctx_id_t *context_handle,
+ gss_name_t target_name,gss_OID mech_type,
+ OM_uint32 req_flags,OM_uint32 time_req,
+ gss_channel_bindings_t input_chan_bindings,
+ gss_buffer_t input_token,
+ gss_OID *actual_mech_type,
+ gss_buffer_t output_token,OM_uint32 *ret_flags,
+ OM_uint32 *time_rec);
+OM_uint32 gss_release_buffer (OM_uint32 *minor_status,gss_buffer_t buffer);
+OM_uint32 gss_release_cred (OM_uint32 *minor_status,gss_cred_id_t *cred_handle);
+OM_uint32 gss_release_name (OM_uint32 *minor_status,gss_name_t *input_name);
+OM_uint32 gss_wrap (OM_uint32 *minor_status,gss_ctx_id_t context_handle,
+ int conf_req_flag,gss_qop_t qop_req,
+ gss_buffer_t input_message_buffer,int *conf_state,
+ gss_buffer_t output_message_buffer);
+OM_uint32 gss_unwrap (OM_uint32 *minor_status,gss_ctx_id_t context_handle,
+ gss_buffer_t input_message_buffer,
+ gss_buffer_t output_message_buffer,int *conf_state,
+ gss_qop_t *qop_state);
+
+/* Kerberos definitions */
+
+long kerberos_server_valid (void);
+long kerberos_try_kinit (OM_uint32 error);
+char *kerberos_login (char *user,char *authuser,int argc,char *argv[]);
+
+
+#define STRING WINSTRING /* conflict with mail.h */
+#include <NTSecAPI.h>
+
+/* GSSAPI build-in object identifiers */
+
+static gss_OID_desc oids[] = { /* stupid C language makes this necessary */
+ {10,"\052\206\110\206\367\022\001\002\001\004"},
+ {9,"\052\206\110\206\367\022\001\002\002"}
+};
+
+ /* stupid C language ditto */
+static gss_OID_set_desc oidsets[] = {
+ {1,(gss_OID) oids+1}
+};
+
+ /* these are the real OIDs */
+const gss_OID gss_nt_service_name = oids+0;
+const gss_OID gss_mech_krb5 = oids+1;
+const gss_OID_set gss_mech_set_krb5 = oidsets+0;
+
+
+/* Other globals */
+
+ /* substitute for GSS_C_NO_CREDENTIAL */
+static gss_cred_id_t gss_default_cred = NIL;
+
+/* GSSAPI import name (convert to full service principal name)
+ * Accepts: pointer to return minor status
+ * buffer containining input name
+ * type of input name
+ * pointer to return output internal name
+ * Returns: major status, always
+ */
+
+OM_uint32 gss_import_name (OM_uint32 *minor_status,
+ gss_buffer_t input_name_buffer,
+ gss_OID input_name_type,gss_name_t *output_name)
+{
+ OM_uint32 major_status = GSS_S_COMPLETE;
+ TimeStamp expiry;
+ static CredHandle gss_cred;
+ char *s,tmp[MAILTMPLEN];
+ *minor_status = 0; /* never any minor status */
+ if (!gss_default_cred) { /* default credentials set up yet? */
+ if (AcquireCredentialsHandle/* no, acquire them now */
+ (NIL,MICROSOFT_KERBEROS_NAME_A,SECPKG_CRED_OUTBOUND,NIL,NIL,NIL,NIL,
+ &gss_cred,&expiry) != SEC_E_OK) return GSS_S_FAILURE;
+ /* have default credentials now */
+ gss_default_cred = &gss_cred;
+ }
+ /* must be the gss_nt_service_name format */
+ if (input_name_type != gss_nt_service_name)
+ major_status = GSS_S_BAD_NAMETYPE;
+ /* name must be of sane length */
+ else if (input_name_buffer->length > (MAILTMPLEN/2))
+ major_status = GSS_S_BAD_NAME;
+ else { /* copy name */
+ memcpy (tmp,input_name_buffer->value,input_name_buffer->length);
+ tmp[input_name_buffer->length] = '\0';
+ if (s = strchr (tmp,'@')) { /* find service/host/delimiter */
+ *s = '/'; /* convert to full service principal name */
+ *output_name = cpystr (tmp);
+ }
+ else major_status = GSS_S_BAD_NAME;
+ }
+ return major_status;
+}
+
+/* GSSAPI Initialize security context
+ * Accepts: pointer to return minor status
+ * claimant credential handle
+ * context (NIL means "none assigned yet")
+ * desired principal
+ * desired mechanisms
+ * required context attributes
+ * desired lifetime
+ * input channel bindings
+ * input token buffer
+ * pointer to return mechanism type
+ * buffer to return output token
+ * pointer to return flags
+ * pointer to return context lifetime
+ * Returns: major status, always
+ */
+
+OM_uint32 gss_init_sec_context (OM_uint32 *minor_status,
+ gss_cred_id_t claimant_cred_handle,
+ gss_ctx_id_t *context_handle,
+ gss_name_t target_name,gss_OID mech_type,
+ OM_uint32 req_flags,OM_uint32 time_req,
+ gss_channel_bindings_t input_chan_bindings,
+ gss_buffer_t input_token,
+ gss_OID *actual_mech_type,
+ gss_buffer_t output_token,OM_uint32 *ret_flags,
+ OM_uint32 *time_rec)
+{
+ OM_uint32 i;
+ OM_uint32 major_status;
+ TimeStamp expiry;
+ SecBuffer ibuf[1],obuf[1];
+ SecBufferDesc ibufs,obufs;
+ *minor_status = 0; /* never any minor status */
+ /* error if non-default time requested */
+ if (time_req) return GSS_S_FAILURE;
+ if (mech_type && memcmp (mech_type,gss_mech_krb5,sizeof (gss_OID)))
+ return GSS_S_BAD_MECH;
+ /* ditto if any channel bindings */
+ if (input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS)
+ return GSS_S_BAD_BINDINGS;
+
+ /* apply default credential if necessary */
+ if (claimant_cred_handle == GSS_C_NO_CREDENTIAL)
+ claimant_cred_handle = gss_default_cred;
+ /* create output buffer storage as needed */
+ req_flags |= ISC_REQ_ALLOCATE_MEMORY;
+ /* make output buffer */
+ obuf[0].BufferType = SECBUFFER_TOKEN;
+ obuf[0].cbBuffer = 0; obuf[0].pvBuffer = NIL;
+ /* output buffer descriptor */
+ obufs.ulVersion = SECBUFFER_VERSION;
+ obufs.cBuffers = 1;
+ obufs.pBuffers = obuf;
+ /* first time caller? */
+ if (*context_handle == GSS_C_NO_CONTEXT) {
+ /* yes, set up output context handle */
+ PCtxtHandle ctx = (PCtxtHandle) fs_get (sizeof (CtxtHandle));
+ major_status = InitializeSecurityContext (claimant_cred_handle,NIL,
+ target_name,req_flags,0,
+ SECURITY_NETWORK_DREP,NIL,0,ctx,
+ &obufs,
+ ret_flags ? ret_flags : &i,
+ &expiry);
+ *context_handle = ctx; /* return updated context */
+ }
+ else { /* no, make SSPI buffer from GSSAPI buffer */
+ ibuf[0].BufferType = obuf[0].BufferType = SECBUFFER_TOKEN;
+ ibuf[0].cbBuffer = input_token->length;
+ ibuf[0].pvBuffer = input_token->value;
+ /* input buffer descriptor */
+ ibufs.ulVersion = SECBUFFER_VERSION;
+ ibufs.cBuffers = 1;
+ ibufs.pBuffers = ibuf;
+ major_status = InitializeSecurityContext (claimant_cred_handle,
+ *context_handle,target_name,
+ req_flags,0,
+ SECURITY_NETWORK_DREP,&ibufs,0,
+ *context_handle,&obufs,
+ ret_flags ? ret_flags : &i,
+ &expiry);
+ }
+ /* return output */
+ output_token->value = obuf[0].pvBuffer;
+ output_token->length = obuf[0].cbBuffer;
+ /* in case client wanted lifetime returned */
+ if (time_rec) *time_rec = expiry.LowPart;
+ return major_status;
+}
+
+/* GSSAPI display status text
+ * Accepts: pointer to return minor status
+ * status to display
+ * status type
+ * message context for continuation
+ * buffer to write status string
+ * Returns: major status, always
+ */
+
+OM_uint32 gss_display_status (OM_uint32 *minor_status,OM_uint32 status_value,
+ int status_type,gss_OID mech_type,
+ OM_uint32 *message_context,
+ gss_buffer_t status_string)
+{
+ char *s,tmp[MAILTMPLEN];
+ *minor_status = 0; /* never any minor status */
+ if (*message_context) return GSS_S_FAILURE;
+ switch (status_type) { /* what type of status code? */
+ case GSS_C_GSS_CODE: /* major_status */
+ switch (status_value) { /* analyze status value */
+ case GSS_S_FAILURE:
+ s = "Unspecified failure"; break;
+ case GSS_S_CREDENTIALS_EXPIRED:
+ s = "Credentials expired"; break;
+ case GSS_S_BAD_BINDINGS:
+ s = "Bad bindings"; break;
+ case GSS_S_BAD_MECH:
+ s = "Bad mechanism type"; break;
+ case GSS_S_BAD_NAME:
+ s = "Bad name"; break;
+ case GSS_S_BAD_NAMETYPE:
+ s = "Bad name type"; break;
+ case GSS_S_BAD_STATUS:
+ s = "Bad status"; break;
+ case GSS_S_NO_CONTEXT:
+ s = "Invalid context handle"; break;
+ case GSS_S_NO_CRED:
+ s = "Unable to authenticate to Kerberos service";
+ mail_parameters (NIL,DISABLE_AUTHENTICATOR,"GSSAPI");
+ break;
+ case SEC_E_NO_AUTHENTICATING_AUTHORITY:
+ s = "No authenticating authority"; break;
+ case SEC_E_TARGET_UNKNOWN:
+ s = "Destination server unknown to Kerberos service"; break;
+ default:
+ sprintf (s = tmp,"SSPI code %lx",status_value);
+ }
+ break;
+ case GSS_C_MECH_CODE: /* minor status - drop into default */
+ default:
+ return GSS_S_BAD_STATUS; /* bad status type */
+ }
+ /* return status string */
+ status_string->length = strlen (status_string->value = cpystr (s));
+ return GSS_S_COMPLETE;
+}
+
+/* GSSAPI delete security context
+ * Accepts: pointer to return minor status
+ * context to delete
+ * output context token
+ * Returns: major status, always
+ */
+
+OM_uint32 gss_delete_sec_context (OM_uint32 *minor_status,
+ gss_ctx_id_t *context_handle,
+ gss_buffer_t output_token)
+{
+ OM_uint32 major_status;
+ *minor_status = 0; /* never any minor status */
+ /* output token not supported */
+ major_status = output_token ? GSS_S_FAILURE :
+ DeleteSecurityContext (*context_handle);
+ fs_give ((void **) context_handle);
+ return major_status;
+}
+
+
+/* GSSAPI release buffer
+ * Accepts: pointer to return minor status
+ * buffer to release
+ * Returns: GSS_S_COMPLETE, always
+ */
+
+OM_uint32 gss_release_buffer (OM_uint32 *minor_status,gss_buffer_t buffer)
+{
+ *minor_status = 0; /* never any minor status */
+ fs_give (&buffer->value);
+ return GSS_S_COMPLETE;
+}
+
+
+/* GSSAPI release name
+ * Accepts: pointer to return minor status
+ * pointer to name to release
+ * Returns: GSS_S_COMPLETE, always
+ */
+
+OM_uint32 gss_release_name (OM_uint32 *minor_status,gss_name_t *input_name)
+{
+ *minor_status = 0; /* never any minor status */
+ fs_give (input_name);
+ return GSS_S_COMPLETE;
+}
+
+/* GSSAPI wrap data
+ * Accepts: pointer to return minor status
+ * context handle
+ * requested confidentiality
+ * requested quality of protection
+ * input message buffer
+ * pointer to return confidentiality state
+ * output message buffer
+ * Returns: major status, always
+ */
+
+OM_uint32 gss_wrap (OM_uint32 *minor_status,gss_ctx_id_t context_handle,
+ int conf_req_flag,gss_qop_t qop_req,
+ gss_buffer_t input_message_buffer,int *conf_state,
+ gss_buffer_t output_message_buffer)
+{
+ OM_uint32 major_status;
+ SecBuffer buf[3];
+ SecBufferDesc bufs;
+ SecPkgContext_Sizes sizes;
+ *minor_status = NIL; /* never any minor status */
+ *conf_state = conf_req_flag; /* same as requested */
+ if ((major_status = /* get trailer and padding sizes */
+ QueryContextAttributes (context_handle,SECPKG_ATTR_SIZES,&sizes)) ==
+ SEC_E_OK) {
+ /* create big enough output buffer */
+ output_message_buffer->value =
+ fs_get (sizes.cbSecurityTrailer + input_message_buffer->length +
+ sizes.cbBlockSize);
+ /* MSDN claims that for EncryptMessage() in Kerberos, you need an
+ * uninitialized SECBUFFER_STREAM_HEADER; a SECBUFFER_DATA that "contains
+ * the message to be encrypted. The message is encrypted in place,
+ * overwriting the original contents of its buffer"; an uninitialized
+ * SECBUFFER_STREAM_TRAILER, and an uninitialized SECBUFFER_EMPTY. I've
+ * never been able to get it to work that way.
+ */
+ bufs.cBuffers = 3; /* set up buffer descriptor */
+ bufs.pBuffers = buf;
+ bufs.ulVersion = SECBUFFER_VERSION;
+ buf[0].BufferType = SECBUFFER_TOKEN;
+ buf[0].pvBuffer = output_message_buffer->value;
+ buf[0].cbBuffer = sizes.cbSecurityTrailer;
+ /* I/O buffer */
+ buf[1].BufferType = SECBUFFER_DATA;
+ buf[1].pvBuffer = ((char *) buf[0].pvBuffer) + buf[0].cbBuffer;
+ buf[1].cbBuffer = input_message_buffer->length;
+ memcpy (buf[1].pvBuffer,input_message_buffer->value,buf[1].cbBuffer);
+ buf[2].BufferType = SECBUFFER_PADDING;
+ buf[2].pvBuffer = ((char *) buf[1].pvBuffer) + buf[1].cbBuffer;
+ buf[2].cbBuffer = sizes.cbBlockSize;
+ if ((major_status = EncryptMessage (context_handle,qop_req,&bufs,0)) ==
+ GSS_S_COMPLETE) {
+ /* slide data as necessary (how annoying!) */
+ unsigned long i = sizes.cbSecurityTrailer - buf[0].cbBuffer;
+ if (i) buf[1].pvBuffer =
+ memmove (((char *) buf[0].pvBuffer) + buf[0].cbBuffer,
+ buf[1].pvBuffer,buf[1].cbBuffer);
+ if (i += (input_message_buffer->length - buf[1].cbBuffer))
+ buf[1].pvBuffer = memmove (((char *)buf[1].pvBuffer) + buf[1].cbBuffer,
+ buf[2].pvBuffer,buf[2].cbBuffer);
+ output_message_buffer->length = buf[0].cbBuffer + buf[1].cbBuffer +
+ buf[2].cbBuffer;
+ }
+ else fs_give (&output_message_buffer->value);
+ }
+ return major_status; /* return status */
+}
+
+/* GSSAPI unwrap data
+ * Accepts: pointer to return minor status
+ * context handle
+ * input message buffer
+ * output message buffer
+ * pointer to return confidentiality state
+ * pointer to return quality of protection
+ * Returns: major status, always
+ */
+
+OM_uint32 gss_unwrap (OM_uint32 *minor_status,gss_ctx_id_t context_handle,
+ gss_buffer_t input_message_buffer,
+ gss_buffer_t output_message_buffer,int *conf_state,
+ gss_qop_t *qop_state)
+{
+ OM_uint32 major_status;
+ SecBuffer buf[2];
+ SecBufferDesc bufs;
+ *minor_status = NIL; /* never any minor status */
+ *conf_state = NIL; /* or confidentiality state */
+ /* MSDN implies that all that is needed for DecryptMessage() in Kerberos
+ * is a single SECBUFFER_DATA which "contains the encrypted message. The
+ * encrypted message is decrypted in place, overwriting the original
+ * contents of its buffer." I've never been able to get it to work without
+ * using a SECBUFFER_STREAM for input and an uninitialized SECBUFFER_DATA
+ * for output.
+ * It *does* overwrite the input buffer, but not at the same point; e.g.
+ * with an input pointer of 0xa140a8 and size of 53, the output ends up
+ * at 0xa140d5 and size of 4.
+ */
+ bufs.cBuffers = 2; /* set up buffer descriptor */
+ bufs.pBuffers = buf;
+ bufs.ulVersion = SECBUFFER_VERSION;
+ /* input buffer */
+ buf[0].BufferType = SECBUFFER_STREAM;
+ buf[0].pvBuffer = input_message_buffer->value;
+ buf[0].cbBuffer = input_message_buffer->length;
+ /* output buffer */
+ buf[1].BufferType = SECBUFFER_DATA;
+ buf[1].pvBuffer = NIL;
+ buf[1].cbBuffer = 0;
+ /* decrypt and copy to output buffer */
+ if ((major_status = DecryptMessage (context_handle,&bufs,0,qop_state)) ==
+ SEC_E_OK)
+ memcpy (output_message_buffer->value = fs_get (buf[1].cbBuffer),
+ buf[1].pvBuffer,output_message_buffer->length = buf[1].cbBuffer);
+ return major_status; /* return status */
+}
+
+/* From here on are server-only functions, currently unused */
+
+
+/* GSSAPI acquire credentials
+ * Accepts: pointer to return minor status
+ * desired principal
+ * desired lifetime
+ * desired mechanisms
+ * credentials usage
+ * pointer to return credentials handle
+ * pointer to return mechanisms
+ * pointer to return lifetime
+ * Returns: GSS_S_FAILURE, always
+ */
+
+OM_uint32 gss_acquire_cred (OM_uint32 *minor_status,gss_name_t desired_name,
+ OM_uint32 time_req,gss_OID_set desired_mechs,
+ gss_cred_usage_t cred_usage,
+ gss_cred_id_t *output_cred_handle,
+ gss_OID_set *actual_mechs,OM_uint32 *time_rec)
+{
+ *minor_status = 0; /* never any minor status */
+ return GSS_S_FAILURE; /* server only */
+}
+
+
+/* GSSAPI release credentials
+ * Accepts: pointer to return minor status
+ * credentials handle to free
+ * Returns: GSS_S_COMPLETE, always
+ */
+
+OM_uint32 gss_release_cred (OM_uint32 *minor_status,gss_cred_id_t *cred_handle)
+{
+ *minor_status = 0; /* never any minor status */
+ return GSS_S_FAILURE; /* server only */
+}
+
+/* GSSAPI Accept security context
+ * Accepts: pointer to return minor status
+ * context
+ * acceptor credentials
+ * input token buffer
+ * input channel bindings
+ * pointer to return source name
+ * pointer to return mechanism type
+ * buffer to return output token
+ * pointer to return flags
+ * pointer to return context lifetime
+ * pointer to return delegated credentials
+ * Returns: GSS_S_FAILURE, always
+ */
+
+OM_uint32 gss_accept_sec_context (OM_uint32 *minor_status,
+ gss_ctx_id_t *context_handle,
+ gss_cred_id_t acceptor_cred_handle,
+ gss_buffer_t input_token_buffer,
+ gss_channel_bindings_t input_chan_bindings,
+ gss_name_t *src_name,gss_OID *mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 *ret_flags,OM_uint32 *time_rec,
+ gss_cred_id_t *delegated_cred_handle)
+{
+ *minor_status = 0; /* never any minor status */
+ return GSS_S_FAILURE; /* server only */
+}
+
+
+/* GSSAPI return printable name
+ * Accepts: pointer to return minor status
+ * internal name
+ * buffer to return output name
+ * output name type
+ * Returns: GSS_S_FAILURE, always
+ */
+
+OM_uint32 gss_display_name (OM_uint32 *minor_status,gss_name_t input_name,
+ gss_buffer_t output_name_buffer,
+ gss_OID *output_name_type)
+{
+ *minor_status = 0; /* never any minor status */
+ return GSS_S_FAILURE; /* server only */
+}
+
+/* Kerberos server valid check
+ * Returns: T if have keytab, NIL otherwise
+ */
+
+long kerberos_server_valid ()
+{
+ return NIL;
+}
+
+
+/* Kerberos check for missing or expired credentials
+ * Returns: T if should suggest running kinit, NIL otherwise
+ */
+
+long kerberos_try_kinit (OM_uint32 error)
+{
+ return NIL;
+}
+
+/* Kerberos server log in
+ * Accepts: authorization ID as user name
+ * authentication ID as Kerberos principal
+ * argument count
+ * argument vector
+ * Returns: logged in user name if logged in, NIL otherwise
+ */
+
+char *kerberos_login (char *user,char *authuser,int argc,char *argv[])
+{
+ return NIL;
+}
diff --git a/imap/src/osdep/nt/mailfile.h b/imap/src/osdep/nt/mailfile.h
new file mode 100644
index 00000000..056e66b7
--- /dev/null
+++ b/imap/src/osdep/nt/mailfile.h
@@ -0,0 +1,29 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Mail Spool file name
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 8 February 1996
+ * Last Edited: 30 August 2006
+ */
+
+#define MAILFILE "C:\\WINSMTP\\%s.MBX"
diff --git a/imap/src/osdep/nt/makefile.nt b/imap/src/osdep/nt/makefile.nt
new file mode 100644
index 00000000..0ea96e5a
--- /dev/null
+++ b/imap/src/osdep/nt/makefile.nt
@@ -0,0 +1,118 @@
+# ========================================================================
+# Copyright 1988-2007 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+# Program: Portable C client makefile -- NT version
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 11 May 1989
+# Last Edited: 23 May 2007
+
+
+EXTRAAUTHENTICATORS =
+EXTRADRIVERS =
+EXTRACFLAGS =
+AUTHENTICATORS = ext md5 pla log
+DRIVERS = imap nntp pop3 mbx mtx tenex unix
+CREATEDRIVER = mbx
+APPENDDRIVER = unix
+OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0400
+VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE
+CFLAGS = /MT /W3 /Ox /DCHUNKSIZE=65536 $(OSCOMPAT) $(VSCOMPAT) -nologo /I.. $(EXTRACFLAGS)
+CC = cl
+CCLIENTLIB = cclient.lib
+
+all: $(CCLIENTLIB)
+
+.c.obj:
+ $(CC) -c $(CFLAGS) $*.c
+
+osdep.h: os_nt.h
+ copy os_nt.h osdep.h
+ drivers $(EXTRADRIVERS) $(DRIVERS) dummy
+ setproto $(CREATEDRIVER) $(APPENDDRIVER)
+ echo ssl_onceonlyinit (); >> linkage.c
+ mkauths $(EXTRAAUTHENTICATORS) $(AUTHENTICATORS)
+ echo mail_versioncheck (CCLIENTVERSION); >> linkage.c
+
+ip_nt.c: ip4_nt.c
+ copy ip4_nt.c ip_nt.c
+
+mail.obj: mail.h misc.h osdep.h mail.c
+
+misc.obj: mail.h misc.h misc.c
+
+fdstring.obj: mail.h misc.h osdep.h fdstring.h fdstring.c
+
+flstring.obj: mail.h misc.h osdep.h flstring.h flstring.c
+
+netmsg.obj: mail.h misc.h netmsg.h osdep.h netmsg.c
+
+newsrc.obj: mail.h misc.h newsrc.h osdep.h newsrc.c
+
+rfc822.obj: mail.h rfc822.h misc.h rfc822.c
+
+smanager.obj: mail.h misc.h smanager.c
+
+utf8.obj: mail.h misc.h osdep.h utf8.h
+
+utf8aux.obj: mail.h misc.h osdep.h utf8.h
+
+imap4r1.obj: mail.h imap4r1.h misc.h osdep.h imap4r1.c
+
+nntp.obj: mail.h nntp.h smtp.h rfc822.h misc.h osdep.h nntp.c
+
+pop3.obj: mail.h rfc822.h misc.h osdep.h pop3.c
+
+smtp.obj: mail.h smtp.h rfc822.h misc.h osdep.h smtp.c
+
+os_nt.obj: mail.h osdep.h env_nt.h fs.h ftl.h nl.h tcp.h tcp_nt.h yunchan.h \
+ os_nt.c fs_nt.c ftl_nt.c nl_nt.c env_nt.c ssl_nt.c ssl_none.c \
+ ip_nt.c tcp_nt.c yunchan.c pmatch.c write.c \
+ mailfile.h auth_md5.c auth_pla.c auth_log.c
+
+mbxnt.obj: mail.h misc.h osdep.h mbxnt.c
+
+mtxnt.obj: mail.h misc.h osdep.h mtxnt.c
+
+tenexnt.obj: mail.h misc.h osdep.h tenexnt.c
+
+unixnt.obj: mail.h unixnt.h pseudo.h misc.h osdep.h unixnt.c
+
+dummynt.obj: mail.h dummy.h misc.h osdep.h dummynt.c
+
+pseudo.obj: pseudo.h
+
+$(CCLIENTLIB): mail.obj misc.obj fdstring.obj flstring.obj netmsg.obj \
+ newsrc.obj rfc822.obj smanager.obj utf8.obj utf8aux.obj \
+ imap4r1.obj nntp.obj pop3.obj smtp.obj os_nt.obj \
+ mbxnt.obj mtxnt.obj tenexnt.obj unixnt.obj dummynt.obj pseudo.obj
+ if exist $(CCLIENTLIB) del $(CCLIENTLIB)
+ LIB /NOLOGO /OUT:cclient.lib \
+ mail.obj misc.obj fdstring.obj flstring.obj netmsg.obj \
+ newsrc.obj rfc822.obj smanager.obj utf8.obj utf8aux.obj \
+ imap4r1.obj nntp.obj pop3.obj smtp.obj os_nt.obj \
+ mbxnt.obj mtxnt.obj tenexnt.obj unixnt.obj dummynt.obj pseudo.obj
+
+clean:
+ del *.lib *.obj linkage.* osdep.* ip_nt.c auths.c *.exe *.exp || rem
+
+# A monument to a hack of long ago and far away...
+
+love:
+ @echo not war?
diff --git a/imap/src/osdep/nt/makefile.ntk b/imap/src/osdep/nt/makefile.ntk
new file mode 100644
index 00000000..e383e0f8
--- /dev/null
+++ b/imap/src/osdep/nt/makefile.ntk
@@ -0,0 +1,118 @@
+# ========================================================================
+# Copyright 1988-2007 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+# Program: Portable C client makefile -- NT version + Kerberos
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 11 May 1989
+# Last Edited: 23 May 2007
+
+
+EXTRAAUTHENTICATORS =
+EXTRADRIVERS =
+EXTRACFLAGS =
+AUTHENTICATORS = ext gss md5 pla log
+DRIVERS = imap nntp pop3 mbx mtx tenex unix
+CREATEDRIVER = mbx
+APPENDDRIVER = unix
+OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0400 /I\k5\include
+VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE
+CFLAGS = /MT /W3 /Ox /DCHUNKSIZE=65536 $(OSCOMPAT) $(VSCOMPAT) -nologo /I.. $(EXTRACFLAGS)
+CC = cl
+CCLIENTLIB = cclient.lib
+
+all: $(CCLIENTLIB)
+
+.c.obj:
+ $(CC) -c $(CFLAGS) $*.c
+
+osdep.h: os_nt.h
+ copy os_nt.h osdep.h
+ drivers $(EXTRADRIVERS) $(DRIVERS) dummy
+ setproto $(CREATEDRIVER) $(APPENDDRIVER)
+ echo ssl_onceonlyinit (); >> linkage.c
+ mkauths $(EXTRAAUTHENTICATORS) $(AUTHENTICATORS)
+ echo mail_versioncheck (CCLIENTVERSION); >> linkage.c
+
+ip_nt.c: ip4_nt.c
+ copy ip4_nt.c ip_nt.c
+
+mail.obj: mail.h misc.h osdep.h mail.c
+
+misc.obj: mail.h misc.h misc.c
+
+fdstring.obj: mail.h misc.h osdep.h fdstring.h fdstring.c
+
+flstring.obj: mail.h misc.h osdep.h flstring.h flstring.c
+
+netmsg.obj: mail.h misc.h netmsg.h osdep.h netmsg.c
+
+newsrc.obj: mail.h misc.h newsrc.h osdep.h newsrc.c
+
+rfc822.obj: mail.h rfc822.h misc.h rfc822.c
+
+smanager.obj: mail.h misc.h smanager.c
+
+utf8.obj: mail.h misc.h osdep.h utf8.h
+
+utf8aux.obj: mail.h misc.h osdep.h utf8.h
+
+imap4r1.obj: mail.h imap4r1.h misc.h osdep.h imap4r1.c
+
+nntp.obj: mail.h nntp.h smtp.h rfc822.h misc.h osdep.h nntp.c
+
+pop3.obj: mail.h rfc822.h misc.h osdep.h pop3.c
+
+smtp.obj: mail.h smtp.h rfc822.h misc.h osdep.h smtp.c
+
+os_ntk.obj: mail.h osdep.h env_nt.h fs.h ftl.h nl.h tcp.h tcp_nt.h yunchan.h \
+ os_ntk.c fs_nt.c ftl_nt.c nl_nt.c env_nt.c ssl_nt.c ssl_none.c \
+ ip_nt.c tcp_nt.c yunchan.c pmatch.c write.c \
+ mailfile.h auth_gss.c auth_md5.c auth_pla.c auth_log.c kerb_mit.c
+
+mbxnt.obj: mail.h misc.h osdep.h mbxnt.c
+
+mtxnt.obj: mail.h misc.h osdep.h mtxnt.c
+
+tenexnt.obj: mail.h misc.h osdep.h tenexnt.c
+
+unixnt.obj: mail.h unixnt.h pseudo.h misc.h osdep.h unixnt.c
+
+dummynt.obj: mail.h dummy.h misc.h osdep.h dummynt.c
+
+pseudo.obj: pseudo.h
+
+$(CCLIENTLIB): mail.obj misc.obj fdstring.obj flstring.obj netmsg.obj \
+ newsrc.obj rfc822.obj smanager.obj utf8.obj utf8aux.obj \
+ imap4r1.obj nntp.obj pop3.obj smtp.obj os_ntk.obj \
+ mbxnt.obj mtxnt.obj tenexnt.obj unixnt.obj dummynt.obj pseudo.obj
+ if exist $(CCLIENTLIB) del $(CCLIENTLIB)
+ LIB /NOLOGO /OUT:cclient.lib \
+ mail.obj misc.obj fdstring.obj flstring.obj netmsg.obj \
+ newsrc.obj rfc822.obj smanager.obj utf8.obj utf8aux.obj \
+ imap4r1.obj nntp.obj pop3.obj smtp.obj os_ntk.obj \
+ mbxnt.obj mtxnt.obj tenexnt.obj unixnt.obj dummynt.obj pseudo.obj
+
+clean:
+ del *.lib *.obj linkage.* osdep.* ip_nt.c auths.c *.exe *.exp || rem
+
+# A monument to a hack of long ago and far away...
+
+love:
+ @echo not war?
diff --git a/imap/src/osdep/nt/makefile.w2k b/imap/src/osdep/nt/makefile.w2k
new file mode 100644
index 00000000..a3d62ad6
--- /dev/null
+++ b/imap/src/osdep/nt/makefile.w2k
@@ -0,0 +1,119 @@
+# ========================================================================
+# Copyright 1988-2007 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+# Program: Portable C client makefile -- Windows 2000/XP version
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 11 May 1989
+# Last Edited: 23 May 2007
+
+
+IP=6
+EXTRAAUTHENTICATORS =
+EXTRADRIVERS =
+EXTRACFLAGS =
+AUTHENTICATORS = ext gss md5 pla log
+DRIVERS = imap nntp pop3 mbx mtx tenex unix
+CREATEDRIVER = mbx
+APPENDDRIVER = unix
+OSCOMPAT = /DWIN32
+VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE
+CFLAGS = /MT /W3 /Ox /DCHUNKSIZE=65536 $(OSCOMPAT) $(VSCOMPAT) -nologo /I.. $(EXTRACFLAGS)
+CC = cl
+CCLIENTLIB = cclient.lib
+
+all: $(CCLIENTLIB)
+
+.c.obj:
+ $(CC) -c $(CFLAGS) $*.c
+
+osdep.h: os_nt.h
+ copy os_nt.h osdep.h
+ drivers $(EXTRADRIVERS) $(DRIVERS) dummy
+ setproto $(CREATEDRIVER) $(APPENDDRIVER)
+ echo ssl_onceonlyinit (); >> linkage.c
+ mkauths $(EXTRAAUTHENTICATORS) $(AUTHENTICATORS)
+ echo mail_versioncheck (CCLIENTVERSION); >> linkage.c
+
+ip_nt.c: ip$(IP)_nt.c
+ copy ip$(IP)_nt.c ip_nt.c
+
+mail.obj: mail.h misc.h osdep.h mail.c
+
+misc.obj: mail.h misc.h misc.c
+
+fdstring.obj: mail.h misc.h osdep.h fdstring.h fdstring.c
+
+flstring.obj: mail.h misc.h osdep.h flstring.h flstring.c
+
+netmsg.obj: mail.h misc.h netmsg.h osdep.h netmsg.c
+
+newsrc.obj: mail.h misc.h newsrc.h osdep.h newsrc.c
+
+rfc822.obj: mail.h rfc822.h misc.h rfc822.c
+
+smanager.obj: mail.h misc.h smanager.c
+
+utf8.obj: mail.h misc.h osdep.h utf8.h
+
+utf8aux.obj: mail.h misc.h osdep.h utf8.h
+
+imap4r1.obj: mail.h imap4r1.h misc.h osdep.h imap4r1.c
+
+nntp.obj: mail.h nntp.h smtp.h rfc822.h misc.h osdep.h nntp.c
+
+pop3.obj: mail.h rfc822.h misc.h osdep.h pop3.c
+
+smtp.obj: mail.h smtp.h rfc822.h misc.h osdep.h smtp.c
+
+os_w2k.obj: mail.h osdep.h env_nt.h fs.h ftl.h nl.h tcp.h tcp_nt.h yunchan.h \
+ os_w2k.c fs_nt.c ftl_nt.c nl_nt.c env_nt.c ssl_w2k.c ssl_none.c \
+ ip_nt.c tcp_nt.c yunchan.c pmatch.c write.c \
+ mailfile.h auth_gss.c auth_md5.c auth_pla.c auth_log.c kerb_w2k.c
+
+mbxnt.obj: mail.h misc.h osdep.h mbxnt.c
+
+mtxnt.obj: mail.h misc.h osdep.h mtxnt.c
+
+tenexnt.obj: mail.h misc.h osdep.h tenexnt.c
+
+unixnt.obj: mail.h unixnt.h pseudo.h misc.h osdep.h unixnt.c
+
+dummynt.obj: mail.h dummy.h misc.h osdep.h dummynt.c
+
+pseudo.obj: pseudo.h
+
+$(CCLIENTLIB): mail.obj misc.obj fdstring.obj flstring.obj netmsg.obj \
+ newsrc.obj rfc822.obj smanager.obj utf8.obj utf8aux.obj \
+ imap4r1.obj nntp.obj pop3.obj smtp.obj os_w2k.obj \
+ mbxnt.obj mtxnt.obj tenexnt.obj unixnt.obj dummynt.obj pseudo.obj
+ if exist $(CCLIENTLIB) del $(CCLIENTLIB)
+ LIB /NOLOGO /OUT:cclient.lib \
+ mail.obj misc.obj fdstring.obj flstring.obj netmsg.obj \
+ newsrc.obj rfc822.obj smanager.obj utf8.obj utf8aux.obj \
+ imap4r1.obj nntp.obj pop3.obj smtp.obj os_w2k.obj \
+ mbxnt.obj mtxnt.obj tenexnt.obj unixnt.obj dummynt.obj pseudo.obj
+
+clean:
+ del *.lib *.obj linkage.* osdep.* ip_nt.c auths.c *.exe *.exp || rem
+
+# A monument to a hack of long ago and far away...
+
+love:
+ @echo not war?
diff --git a/imap/src/osdep/nt/mbxnt.c b/imap/src/osdep/nt/mbxnt.c
new file mode 100644
index 00000000..bb3d4cc0
--- /dev/null
+++ b/imap/src/osdep/nt/mbxnt.c
@@ -0,0 +1,1694 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MBX mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 October 1995
+ * Last Edited: 28 September 2007
+ */
+
+
+/* FILE TIME SEMANTICS
+ *
+ * The atime is the last read time of the file.
+ * The mtime is the last flags update time of the file.
+ * The ctime is the last write time of the file.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/utime.h>
+#include "misc.h"
+#include "dummy.h"
+#include "fdstring.h"
+
+
+/* Build parameters */
+
+#define HDRSIZE 2048
+
+/* MBX I/O stream local data */
+
+typedef struct mbx_local {
+ unsigned int flagcheck: 1; /* if ping should sweep for flags */
+ unsigned int expok: 1; /* if expunging OK in ping */
+ unsigned int expunged : 1; /* if one or more expunged messages */
+ int fd; /* file descriptor for I/O */
+ int ld; /* lock file descriptor */
+ int ffuserflag; /* first free user flag */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ time_t lastsnarf; /* last snarf time */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ char lock[MAILTMPLEN]; /* buffer to write lock name */
+} MBXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MBXLOCAL *) stream->local)
+
+/* Function prototypes */
+
+DRIVER *mbx_valid (char *name);
+int mbx_isvalid (MAILSTREAM **stream,char *name,char *file,int *ld,char *lock,
+ long flags);
+void *mbx_parameters (long function,void *value);
+void mbx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void mbx_list (MAILSTREAM *stream,char *ref,char *pat);
+void mbx_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long mbx_create (MAILSTREAM *stream,char *mailbox);
+long mbx_delete (MAILSTREAM *stream,char *mailbox);
+long mbx_rename (MAILSTREAM *stream,char *old,char *newname);
+long mbx_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *mbx_open (MAILSTREAM *stream);
+void mbx_close (MAILSTREAM *stream,long options);
+void mbx_abort (MAILSTREAM *stream);
+void mbx_flags (MAILSTREAM *stream,char *sequence,long flags);
+char *mbx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags);
+long mbx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void mbx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+void mbx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long mbx_ping (MAILSTREAM *stream);
+void mbx_check (MAILSTREAM *stream);
+long mbx_expunge (MAILSTREAM *stream,char *sequence,long options);
+void mbx_snarf (MAILSTREAM *stream);
+long mbx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long mbx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+long mbx_parse (MAILSTREAM *stream);
+MESSAGECACHE *mbx_elt (MAILSTREAM *stream,unsigned long msgno,long expok);
+unsigned long mbx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt);
+void mbx_update_header (MAILSTREAM *stream);
+void mbx_update_status (MAILSTREAM *stream,unsigned long msgno,long flags);
+unsigned long mbx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size,char **hdr);
+unsigned long mbx_rewrite (MAILSTREAM *stream,unsigned long *reclaimed,
+ long flags);
+long mbx_flaglock (MAILSTREAM *stream);
+
+/* MBX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mbxdriver = {
+ "mbx", /* driver name */
+ DR_LOCAL|DR_MAIL|DR_CRLF|DR_LOCKING,
+ /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ mbx_valid, /* mailbox is valid for us */
+ mbx_parameters, /* manipulate parameters */
+ mbx_scan, /* scan mailboxes */
+ mbx_list, /* list mailboxes */
+ mbx_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ mbx_create, /* create mailbox */
+ mbx_delete, /* delete mailbox */
+ mbx_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ mbx_open, /* open mailbox */
+ mbx_close, /* close mailbox */
+ mbx_flags, /* fetch message "fast" attributes */
+ mbx_flags, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mbx_header, /* fetch message header */
+ mbx_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ mbx_flag, /* modify flags */
+ mbx_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mbx_ping, /* ping mailbox to see if still alive */
+ mbx_check, /* check for new messages */
+ mbx_expunge, /* expunge deleted messages */
+ mbx_copy, /* copy messages to another mailbox */
+ mbx_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mbxproto = {&mbxdriver};
+
+/* MBX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mbx_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ int fd = mbx_isvalid (NIL,name,tmp,NIL,NIL,NIL);
+ if (fd < 0) return NIL;
+ close (fd); /* don't need the fd now */
+ return &mbxdriver;
+}
+
+
+/* MBX mail test for valid mailbox
+ * Accepts: returned stream with valid mailbox keywords
+ * mailbox name
+ * buffer to write file name
+ * returned lock fd
+ * returned lock name
+ * RW flags or NIL for readonly
+ * Returns: file descriptor if valid, NIL otherwise
+ */
+
+#define MBXISVALIDNOUID 0x1 /* RW, don't do UID action */
+#define MBXISVALIDUID 0x2 /* RW, do UID action */
+
+int mbx_isvalid (MAILSTREAM **stream,char *name,char *file,int *ld,char *lock,
+ long flags)
+{
+ int fd,upd;
+ int ret = -1;
+ unsigned long i;
+ long j,k;
+ off_t pos;
+ char c,*s,*t,hdr[HDRSIZE];
+ struct stat sbuf;
+ struct utimbuf times;
+ int error = EINVAL; /* assume invalid argument */
+ if (ld) *ld = -1; /* initially no lock */
+ /* if file, get its status */
+ if ((s = dummy_file (file,name)) && !stat (s,&sbuf) &&
+ ((sbuf.st_mode & S_IFMT) == S_IFREG) &&
+ ((fd = open (file,(flags ? O_RDWR : O_RDONLY)|O_BINARY,NIL)) >= 0)) {
+ error = -1; /* assume bogus format */
+ if (((((j = read (fd,hdr,HDRSIZE)) == HDRSIZE) && (hdr[0] == '*')) ||
+ /* locked, set byte 0 to "*", read rest */
+ ((j < 0) && (lseek (fd,1,L_SET) == 1) &&
+ (read (fd,hdr+1,HDRSIZE-1) == (HDRSIZE-1)) && (hdr[0] = '*'))) &&
+ (hdr[1] == 'm') && (hdr[2] == 'b') && (hdr[3] == 'x') &&
+ (hdr[4] == '*') && (hdr[5] == '\015') && (hdr[6] == '\012') &&
+ isxdigit (hdr[7]) && isxdigit (hdr[8]) && isxdigit (hdr[9]) &&
+ isxdigit (hdr[10]) && isxdigit (hdr[11]) && isxdigit (hdr[12]) &&
+ isxdigit (hdr[13]) && isxdigit (hdr[14]) && isxdigit (c = hdr[15]) &&
+ isxdigit (hdr[16]) && isxdigit (hdr[17]) && isxdigit (hdr[18]) &&
+ isxdigit (hdr[19]) && isxdigit (hdr[20]) && isxdigit (hdr[21]) &&
+ isxdigit (hdr[22]) && (hdr[23] == '\015') && (hdr[24] == '\012')) {
+ ret = fd; /* mbx format */
+
+ if (stream) { /* lock if making a mini-stream */
+ if (flock (fd,LOCK_SH) ||
+ (flags && ((*ld = lockname (lock,file,LOCK_EX)) < 0))) ret = -1;
+ /* reread data now that locked */
+ else if (lseek (fd,0,L_SET) ||
+ (read (fd,hdr+1,HDRSIZE-1) != (HDRSIZE-1))) ret = -1;
+ else {
+ *stream = (MAILSTREAM *) memset (fs_get (sizeof (MAILSTREAM)),0,
+ sizeof (MAILSTREAM));
+ hdr[15] = '\0'; /* tie off UIDVALIDITY */
+ (*stream)->uid_validity = strtoul (hdr+7,NIL,16);
+ hdr[15] = c; /* now get UIDLAST */
+ (*stream)->uid_last = strtoul (hdr+15,NIL,16);
+ /* parse user flags */
+ for (i = 0, s = hdr + 25;
+ (i < NUSERFLAGS) && (t = strchr (s,'\015')) && (t - s);
+ i++, s = t + 2) {
+ *t = '\0'; /* tie off flag */
+ if (strlen (s) <= MAXUSERFLAG)
+ (*stream)->user_flags[i] = cpystr (s);
+ }
+ /* make sure have true UIDLAST */
+ if (flags & MBXISVALIDUID) {
+ for (upd = NIL,pos = 2048, k = 0; pos < sbuf.st_size;
+ pos += (j + k)) {
+ /* read header for this message */
+ lseek (fd,pos,L_SET);
+ if ((j = read (fd,hdr,64)) >= 0) {
+ hdr[j] = '\0';
+ if ((s = strchr (hdr,'\015')) && (s[1] == '\012')) {
+ *s = '\0';
+ k = s + 2 - hdr;
+ if ((s = strchr (hdr,',')) && (j = strtol (s+1,&s,10)) &&
+ (*s == ';') && (s = strchr (s+1,'-'))) {
+ /* get UID if there is any */
+ i = strtoul (++s,&t,16);
+ if (!*t && (t == (s + 8)) && (i <= (*stream)->uid_last)) {
+ if (!i) {
+ lseek (fd,pos + s - hdr,L_SET);
+ sprintf (hdr,"%08lx",++(*stream)->uid_last);
+ write (fd,hdr,8);
+ upd = T;
+ }
+ continue;
+ }
+ }
+ }
+ ret = -1; /* error, give up */
+ *stream = mail_close (*stream);
+ pos = sbuf.st_size + 1;
+ j = k = 0;
+ }
+ }
+
+ if (upd) { /* need to update hdr with new UIDLAST? */
+ lseek (fd,15,L_SET);
+ sprintf (hdr,"%08lx",(*stream)->uid_last);
+ write (fd,hdr,8);
+ }
+ }
+ }
+ }
+ }
+ if (ret != fd) close (fd); /* close the file */
+ else lseek (fd,0,L_SET); /* else rewind to start */
+ /* \Marked status? */
+ if (sbuf.st_ctime > sbuf.st_atime) {
+ /* preserve atime and mtime */
+ times.actime = sbuf.st_atime;
+ times.modtime = sbuf.st_mtime;
+ utime (file,&times); /* set the times */
+ }
+ }
+ /* in case INBOX but not mbx format */
+ else if (((error = errno) == ENOENT) && !compare_cstring (name,"INBOX"))
+ error = -1;
+ if ((ret < 0) && ld && (*ld >= 0)) {
+ unlockfd (*ld,lock);
+ *ld = -1;
+ }
+ errno = error; /* return as last error */
+ return ret; /* return what we should */
+}
+
+/* MBX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mbx_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_ONETIMEEXPUNGEATPING:
+ if (value) ((MBXLOCAL *) ((MAILSTREAM *) value)->local)->expok = T;
+ case GET_ONETIMEEXPUNGEATPING:
+ if (value) ret = (void *)
+ (((MBXLOCAL *) ((MAILSTREAM *) value)->local)->expok ? VOIDT : NIL);
+ break;
+ }
+ return ret;
+}
+
+
+/* MBX mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mbx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* MBX mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mbx_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* MBX mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mbx_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* MBX mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,mbx[MAILTMPLEN],tmp[HDRSIZE];
+ long ret = NIL;
+ int i,fd;
+ if (!(s = dummy_file (mbx,mailbox))) {
+ sprintf (mbx,"Can't create %.80s: invalid name",mailbox);
+ mm_log (mbx,ERROR);
+ }
+ /* create underlying file */
+ else if (dummy_create (stream,s)) {
+ /* done if made directory */
+ if ((s = strrchr (s,'\\')) && !s[1]) return T;
+ if ((fd = open (mbx,O_WRONLY|O_BINARY,NIL)) < 0) {
+ sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno));
+ mm_log (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ else {
+ memset (tmp,'\0',HDRSIZE);/* initialize header */
+ sprintf (s = tmp,"*mbx*\015\012%08lx00000000\015\012",
+ (unsigned long) time (0));
+ for (i = 0; i < NUSERFLAGS; ++i)
+ sprintf (s += strlen (s),"%s\015\012",
+ (stream && stream->user_flags[i]) ? stream->user_flags[i] :
+ "");
+ if (write (fd,tmp,HDRSIZE) != HDRSIZE) {
+ sprintf (tmp,"Can't initialize mailbox node %.80s: %s",
+ mbx,strerror (errno));
+ mm_log (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ else ret = T; /* success */
+ close (fd); /* close file */
+ }
+ }
+ return ret;
+}
+
+
+/* MBX mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return mbx_rename (stream,mailbox,NIL);
+}
+
+/* MBX mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = LONGT;
+ char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ int fd,ld;
+ struct stat sbuf;
+ if (!dummy_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'\\')) && !s[1])))) {
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ else if ((fd = open (file,O_RDWR|O_BINARY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get parse/append permission */
+ if ((ld = lockname (lock,file,LOCK_EX)) < 0) {
+ mm_log ("Unable to lock rename mailbox",ERROR);
+ return NIL;
+ }
+ /* lock out other users */
+ if (flock (fd,LOCK_EX|LOCK_NB)) {
+ close (fd); /* couldn't lock, give up on it then */
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+ mm_log (tmp,ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return NIL;
+ }
+
+ if (newname) { /* want rename? */
+ /* found superior to destination name? */
+ if ((s = strrchr (tmp,'\\')) && (s != tmp) &&
+ ((tmp[1] != ':') || (s != tmp + 2))) {
+ c = s[1]; /* remember character after delimiter */
+ *s = s[1] = '\0'; /* tie off name at delimiter */
+ /* name doesn't exist, create it */
+ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) {
+ *s = '\\'; /* restore delimiter */
+ if (!dummy_create (stream,tmp)) ret = NIL;
+ }
+ else *s = '\\'; /* restore delimiter */
+ s[1] = c; /* restore character after delimiter */
+ }
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* pacify NTFS */
+ /* rename the file */
+ if (ret && rename (file,tmp)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ else {
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* pacify NTFS */
+ if (unlink (file)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ /* recreate file if renamed INBOX */
+ if (ret && !compare_cstring (old,"INBOX")) mbx_create (NIL,"INBOX");
+ return ret; /* return success */
+}
+
+/* MBX mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mbx_open (MAILSTREAM *stream)
+{
+ int fd,ld;
+ short silent;
+ char tmp[MAILTMPLEN];
+ if (!stream) return &mbxproto;/* return prototype for OP_PROTOTYPE call */
+ if (stream->local) fatal ("mbx recycle stream");
+ /* canonicalize the mailbox name */
+ if (!dummy_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ mm_log (tmp,ERROR);
+ }
+ if (stream->rdonly ||
+ (fd = open (tmp,O_RDWR|O_BINARY,NIL)) < 0) {
+ if ((fd = open (tmp,O_RDONLY|O_BINARY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ else if (!stream->rdonly) { /* got it, but readonly */
+ mm_log ("Can't get write access to mailbox, access is readonly",WARN);
+ stream->rdonly = T;
+ }
+ }
+
+ stream->local = memset (fs_get (sizeof (MBXLOCAL)),NIL,sizeof (MBXLOCAL));
+ LOCAL->fd = fd; /* bind the file */
+ LOCAL->ld = -1; /* no flaglock */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ /* get parse/append permission */
+ if ((ld = lockname (tmp,stream->mailbox,LOCK_EX)) < 0) {
+ mm_log ("Unable to lock open mailbox",ERROR);
+ return NIL;
+ }
+ flock (LOCAL->fd,LOCK_SH); /* lock the file */
+ unlockfd (ld,tmp); /* release shared parse permission */
+ LOCAL->filesize = HDRSIZE; /* initialize parsed file size */
+ LOCAL->filetime = 0; /* time not set up yet */
+ LOCAL->expok = LOCAL->flagcheck = NIL;
+ stream->sequence++; /* bump sequence number */
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ silent = stream->silent; /* defer events */
+ stream->silent = T;
+ if (mbx_ping (stream) && !stream->nmsgs)
+ mm_log ("Mailbox is empty",(long) NIL);
+ stream->silent = silent; /* now notify upper level */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,stream->recent);
+ if (!LOCAL) return NIL; /* failure if stream died */
+ stream->perm_seen = stream->perm_deleted = stream->perm_flagged =
+ stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ stream->kwd_create = (stream->user_flags[NUSERFLAGS-1] || stream->rdonly) ?
+ NIL : T; /* can we create new user flags? */
+ return stream; /* return stream to caller */
+}
+
+/* MBX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mbx_close (MAILSTREAM *stream,long options)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ /* do an expunge if requested */
+ if (options & CL_EXPUNGE) mbx_expunge (stream,NIL,NIL);
+ else { /* otherwise do a checkpoint to purge */
+ LOCAL->expok = T; /* possible expunged messages */
+ mbx_ping (stream);
+ }
+ stream->silent = silent; /* restore previous status */
+ mbx_abort (stream);
+ }
+}
+
+
+/* MBX mail abort stream
+ * Accepts: MAIL stream
+ */
+
+void mbx_abort (MAILSTREAM *stream)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ flock (LOCAL->fd,LOCK_UN); /* unlock local file */
+ close (LOCAL->fd); /* close the local file */
+ /* free local text buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+
+/* MBX mail fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ * Sniffs at file to see if some other process changed the flags
+ */
+
+void mbx_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned long i;
+ if (mbx_ping (stream) && /* ping mailbox, get new status for messages */
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence && !elt->valid)
+ mbx_elt (stream,i,NIL);
+}
+
+/* MBX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *mbx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags)
+{
+ unsigned long i;
+ char *s;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ /* get header position, possibly header */
+ i = mbx_hdrpos (stream,msgno,length,&s);
+ if (!s) { /* mbx_hdrpos() returned header? */
+ lseek (LOCAL->fd,i,L_SET); /* no, get to header position */
+ /* is buffer big enough? */
+ if (*length > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = *length) + 1);
+ }
+ /* slurp the data */
+ read (LOCAL->fd,s = LOCAL->buf,*length);
+ }
+ s[*length] = '\0'; /* tie off string */
+ return s;
+}
+
+/* MBX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ FDDATA d;
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ /* get message status */
+ elt = mbx_elt (stream,msgno,NIL);
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen && mbx_flaglock (stream)) {
+ elt->seen = T; /* mark message as seen */
+ /* recalculate status */
+ mbx_update_status (stream,msgno,NIL);
+ mm_flags (stream,msgno);
+ /* update flags */
+ mbx_flag (stream,NIL,NIL,NIL);
+ }
+ if (!LOCAL) return NIL; /* mbx_flaglock() could have aborted */
+ /* find header position */
+ i = mbx_hdrpos (stream,msgno,&j,NIL);
+ d.fd = LOCAL->fd; /* set up file descriptor */
+ d.pos = i + j;
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE;
+ INIT (bs,fd_string,&d,elt->rfc822_size - j);
+ return LONGT; /* success */
+}
+
+/* MBX mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ * Unlocks flag lock
+ */
+
+void mbx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ struct utimbuf times;
+ struct stat sbuf;
+ /* make sure the update takes */
+ if (!stream->rdonly && LOCAL && (LOCAL->fd >= 0) && (LOCAL->ld >= 0)) {
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ /* update header */
+ if ((LOCAL->ffuserflag < NUSERFLAGS) &&
+ stream->user_flags[LOCAL->ffuserflag]) mbx_update_header (stream);
+ times.actime = time (0); /* make sure read comes after all that */
+ utime (stream->mailbox,&times);
+ }
+ if (LOCAL->ld >= 0) { /* unlock now */
+ unlockfd (LOCAL->ld,LOCAL->lock);
+ LOCAL->ld = -1;
+ }
+}
+
+
+/* MBX mail per-message modify flags
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void mbx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ if (mbx_flaglock (stream)) mbx_update_status (stream,elt->msgno,NIL);
+}
+
+/* MBX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream still alive, NIL if not
+ */
+
+long mbx_ping (MAILSTREAM *stream)
+{
+ unsigned long i,pos;
+ long ret = NIL;
+ int ld;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ struct stat sbuf;
+ if (stream && LOCAL) { /* only if stream already open */
+ ret = LONGT; /* assume OK */
+ fstat (LOCAL->fd,&sbuf); /* get current file poop */
+ /* allow expunge if permitted at ping */
+ if (mail_parameters (NIL,GET_EXPUNGEATPING,NIL)) LOCAL->expok = T;
+ /* if external modification */
+ if (LOCAL->filetime && (LOCAL->filetime < sbuf.st_mtime))
+ LOCAL->flagcheck = T; /* upgrade to flag checking */
+ /* new mail or flagcheck handling needed? */
+ if (((sbuf.st_size - LOCAL->filesize) || LOCAL->flagcheck ||
+ !stream->nmsgs) &&
+ ((ld = lockname (lock,stream->mailbox,LOCK_EX)) >= 0)) {
+ if (!LOCAL->flagcheck) ret = mbx_parse (stream);
+ /* sweep mailbox for changed message status */
+ else if (ret = mbx_parse (stream)) {
+ unsigned long recent = 0;
+ LOCAL->filetime = sbuf.st_mtime;
+ for (i = 1; i <= stream->nmsgs; )
+ if (elt = mbx_elt (stream,i,LOCAL->expok)) {
+ if (elt->recent) ++recent;
+ ++i;
+ }
+ mail_recent (stream,recent);
+ LOCAL->flagcheck = NIL; /* got all the updates */
+ }
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ if (ret) { /* must still be alive */
+ if (!LOCAL->expunged) /* look for holes if none known yet */
+ for (i = 1, pos = HDRSIZE;
+ !LOCAL->expunged && (i <= stream->nmsgs);
+ i++, pos += elt->private.special.text.size + elt->rfc822_size)
+ if ((elt = mail_elt (stream,i))->private.special.offset != pos)
+ LOCAL->expunged = T;/* found a hole */
+ /* burp any holes */
+ if (LOCAL->expunged && !stream->rdonly) {
+ if (mbx_rewrite (stream,&i,NIL)) fatal ("expunge on check");
+ if (i) { /* any space reclaimed? */
+ LOCAL->expunged = NIL;/* no more pending expunge */
+ sprintf (LOCAL->buf,"Reclaimed %lu bytes of expunged space",i);
+ mm_log (LOCAL->buf,(long) NIL);
+ }
+ }
+ LOCAL->expok = NIL; /* no more expok */
+ }
+ }
+ return ret; /* return result of the parse */
+}
+
+/* MBX mail check mailbox (reparses status too)
+ * Accepts: MAIL stream
+ */
+
+void mbx_check (MAILSTREAM *stream)
+{
+ if (LOCAL) LOCAL->expok = T; /* mark that a check is desired */
+ if (mbx_ping (stream)) mm_log ("Check completed",(long) NIL);
+}
+
+
+/* MBX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T if success, NIL if failure
+ */
+
+long mbx_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ unsigned long nexp,reclaimed;
+ if (ret = sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) {
+ if (!mbx_ping (stream)); /* do nothing if stream dead */
+ else if (stream->rdonly) /* won't do on readonly files! */
+ mm_log ("Expunge ignored on readonly mailbox",WARN);
+ /* if expunged any messages */
+ else if (nexp = mbx_rewrite (stream,&reclaimed,sequence ? -1 : 1)) {
+ sprintf (LOCAL->buf,"Expunged %lu messages",nexp);
+ mm_log (LOCAL->buf,(long) NIL);
+ }
+ else if (reclaimed) { /* or if any prior expunged space reclaimed */
+ sprintf (LOCAL->buf,"Reclaimed %lu bytes of expunged space",reclaimed);
+ mm_log (LOCAL->buf,(long) NIL);
+ }
+ else mm_log ("No messages deleted, so no update needed",(long) NIL);
+ }
+ return ret;
+}
+
+/* MBX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if success, NIL if failed
+ */
+
+long mbx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ struct utimbuf times;
+ MESSAGECACHE *elt;
+ unsigned long i,j,k,m;
+ long ret = LONGT;
+ int fd,ld;
+ char *s,*t,file[MAILTMPLEN],lock[MAILTMPLEN];
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ copyuid_t cu = (copyuid_t) mail_parameters (NIL,GET_COPYUID,NIL);
+ SEARCHSET *source = cu ? mail_newsearchset () : NIL;
+ SEARCHSET *dest = cu ? mail_newsearchset () : NIL;
+ MAILSTREAM *dstream = NIL;
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* make sure valid mailbox */
+ if ((fd = mbx_isvalid (&dstream,mailbox,file,&ld,lock,
+ cu ? MBXISVALIDUID : MBXISVALIDNOUID)) < 0)
+ switch (errno) {
+ case ENOENT: /* no such file? */
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid MBX-format mailbox name: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a MBX-format mailbox: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ /* got file? */
+ if ((fd = open (dummy_file (file,mailbox),O_RDWR|O_CREAT|O_BINARY,
+ S_IREAD|S_IWRITE)) < 0) {
+ sprintf (LOCAL->buf,"Unable to open copy mailbox: %s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ mm_critical (stream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
+
+ /* for each requested message */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.special.text.size,L_SET);
+ mail_date(LOCAL->buf,elt);/* build target header */
+ /* get target keyword mask */
+ for (j = elt->user_flags, k = 0; j; )
+ if (s = stream->user_flags[find_rightmost_bit (&j)])
+ for (m = 0; (m < NUSERFLAGS) && (t = dstream->user_flags[m]); m++)
+ if (!compare_cstring (s,t) && (k |= 1 << m)) break;
+ sprintf (LOCAL->buf+strlen(LOCAL->buf),",%lu;%08lx%04x-%08lx\015\012",
+ elt->rfc822_size,k,(unsigned)
+ ((fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)),cu ? ++dstream->uid_last : 0);
+ /* write target header */
+ if (ret = (write (fd,LOCAL->buf,strlen (LOCAL->buf)) > 0)) {
+ for (k = elt->rfc822_size; ret && (j = min (k,LOCAL->buflen)); k -= j){
+ read (LOCAL->fd,LOCAL->buf,j);
+ ret = write (fd,LOCAL->buf,j) >= 0;
+ }
+ if (cu) { /* need to pass back new UID? */
+ mail_append_set (source,mail_uid (stream,i));
+ mail_append_set (dest,dstream->uid_last);
+ }
+ }
+ }
+
+ /* make sure all the updates take */
+ if (!(ret && (ret = !fsync (fd)))) {
+ sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ }
+ if (cu && ret) { /* return sets if doing COPYUID */
+ (*cu) (stream,mailbox,dstream->uid_validity,source,dest);
+ lseek (fd,15,L_SET); /* update UIDLAST */
+ sprintf (LOCAL->buf,"%08lx",dstream->uid_last);
+ write (fd,LOCAL->buf,8);
+ }
+ else { /* flush any sets we may have built */
+ mail_free_searchset (&source);
+ mail_free_searchset (&dest);
+ }
+ /* set atime to now-1 if successful copy */
+ if (ret) times.actime = time (0) - 1;
+ /* else preserved \Marked status */
+ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ?
+ sbuf.st_atime : time (0);
+ times.modtime = sbuf.st_mtime;/* preserve mtime */
+ utime (file,&times); /* set the times */
+ close (fd); /* close the file */
+ mm_nocritical (stream); /* release critical */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ /* delete all requested messages */
+ if (ret && (options & CP_MOVE) && mbx_flaglock (stream)) {
+ for (i = 1; i <= stream->nmsgs; i++) if (mail_elt (stream,i)->sequence) {
+ /* mark message deleted */
+ mbx_elt (stream,i,NIL)->deleted = T;
+ /* recalculate status */
+ mbx_update_status (stream,i,NIL);
+ }
+ /* update flags */
+ mbx_flag (stream,NIL,NIL,NIL);
+ }
+ if (dstream != stream) mail_close (dstream);
+ return ret;
+}
+
+/* MBX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mbx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd,ld;
+ char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ struct utimbuf times;
+ FILE *df;
+ MESSAGECACHE elt;
+ long f;
+ unsigned long i,uf;
+ STRING *message;
+ long ret = NIL;
+ MAILSTREAM *dstream = NIL;
+ appenduid_t au = (appenduid_t) mail_parameters (NIL,GET_APPENDUID,NIL);
+ SEARCHSET *dst = au ? mail_newsearchset () : NIL;
+ /* make sure valid mailbox */
+ /* make sure valid mailbox */
+ if ((fd = mbx_isvalid (&dstream,mailbox,file,&ld,lock,
+ au ? MBXISVALIDUID : MBXISVALIDNOUID)) < 0)
+ switch (errno) {
+ case ENOENT: /* no such file? */
+ if (compare_cstring (mailbox,"INBOX")) {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* can create INBOX here */
+ mbx_create (dstream = stream ? stream : &mbxproto,"INBOX");
+ if ((fd = mbx_isvalid (&dstream,mailbox,file,&ld,lock,
+ au ? MBXISVALIDUID : MBXISVALIDNOUID)) < 0)
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid MBX-format mailbox name: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a MBX-format mailbox: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+
+ /* get first message */
+ if (!(*af) (dstream,data,&flags,&date,&message)) close (fd);
+ else if (!(df = fdopen (fd,"r+b"))) {
+ MM_LOG ("Unable to reopen append mailbox",ERROR);
+ close (fd);
+ }
+ else {
+ mm_critical (dstream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ fseek (df,sbuf.st_size,SEEK_SET);
+ errno = 0;
+ for (ret = LONGT; ret && message; ) {
+ if (!SIZE (message)) { /* guard against zero-length */
+ mm_log ("Append of zero-length message",ERROR);
+ ret = NIL;
+ break;
+ }
+ f = mail_parse_flags (dstream,flags,&uf);
+ if (date) { /* parse date if given */
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ mm_log (tmp,ERROR);
+ ret = NIL; /* mark failure */
+ break;
+ }
+ mail_date (tmp,&elt); /* write preseved date */
+ }
+ else internal_date (tmp); /* get current date in IMAP format */
+ /* write header */
+ if (fprintf (df,"%s,%lu;%08lx%04lx-%08lx\015\012",tmp,i = SIZE (message),
+ uf,(unsigned long) f,au ? ++dstream->uid_last : 0) < 0)
+ ret = NIL;
+ else { /* write message */
+ size_t j;
+ if (!message->cursize) SETPOS (message,GETPOS (message));
+ while (i && (j = fwrite (message->curpos,1,message->cursize,df))) {
+ i -= j;
+ SETPOS (message,GETPOS (message) + j);
+ }
+ /* get next message */
+ if (i || !(*af) (dstream,data,&flags,&date,&message)) ret = NIL;
+ else if (au) mail_append_set (dst,dstream->uid_last);
+ }
+ }
+
+ /* if error... */
+ if (!ret || (fflush (df) == EOF)) {
+ /* revert file */
+ ftruncate (fd,sbuf.st_size);
+ close (fd); /* make sure fclose() doesn't corrupt us */
+ if (errno) {
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ }
+ ret = NIL;
+ }
+ if (au && ret) { /* return sets if doing APPENDUID */
+ (*au) (mailbox,dstream->uid_validity,dst);
+ fseek (df,15,SEEK_SET); /* update UIDLAST */
+ fprintf (df,"%08lx",dstream->uid_last);
+ }
+ else mail_free_searchset (&dst);
+ if (ret) times.actime = time (0) - 1;
+ /* else preserve \Marked status */
+ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ?
+ sbuf.st_atime : time (0);
+ /* preserve mtime */
+ times.modtime = sbuf.st_mtime;
+ utime (file,&times); /* set the times */
+ fclose (df); /* close the file */
+ mm_nocritical (dstream); /* release critical */
+ }
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ if (dstream != stream) mail_close (dstream);
+ return ret;
+}
+
+/* Internal routines */
+
+
+/* MBX mail parse mailbox
+ * Accepts: MAIL stream
+ * Returns: T if parse OK
+ * NIL if failure, stream aborted
+ */
+
+long mbx_parse (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = NIL;
+ unsigned char c,*s,*t,*x;
+ char tmp[MAILTMPLEN];
+ unsigned long i,j,k,m;
+ off_t curpos = LOCAL->filesize;
+ unsigned long nmsgs = stream->nmsgs;
+ unsigned long recent = stream->recent;
+ unsigned long lastuid = 0;
+ short dirty = NIL;
+ short added = NIL;
+ short silent = stream->silent;
+ short uidwarn = T;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ if (sbuf.st_size < curpos) { /* sanity check */
+ sprintf (tmp,"Mailbox shrank from %lu to %lu!",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ lseek (LOCAL->fd,0,L_SET); /* rewind file */
+ /* read internal header */
+ read (LOCAL->fd,LOCAL->buf,HDRSIZE);
+ LOCAL->buf[HDRSIZE] = '\0'; /* tie off header */
+ c = LOCAL->buf[15]; /* save first character of last UID */
+ LOCAL->buf[15] = '\0';
+ /* parse UID validity */
+ stream->uid_validity = strtoul (LOCAL->buf + 7,NIL,16);
+ LOCAL->buf[15] = c; /* restore first character of last UID */
+ /* parse last UID */
+ i = strtoul (LOCAL->buf + 15,NIL,16);
+ stream->uid_last = stream->rdonly ? max (i,stream->uid_last) : i;
+ /* parse user flags */
+ for (i = 0, s = LOCAL->buf + 25;
+ (i < NUSERFLAGS) && (t = strchr (s,'\015')) && (t - s);
+ i++, s = t + 2) {
+ *t = '\0'; /* tie off flag */
+ if (!stream->user_flags[i] && (strlen (s) <= MAXUSERFLAG))
+ stream->user_flags[i] = cpystr (s);
+ }
+ LOCAL->ffuserflag = (int) i; /* first free user flag */
+
+ stream->silent = T; /* don't pass up mm_exists() events yet */
+ while (sbuf.st_size - curpos){/* while there is stuff to parse */
+ /* get to that position in the file */
+ lseek (LOCAL->fd,curpos,L_SET);
+ if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
+ sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size,
+ i ? strerror (errno) : "no data read");
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ LOCAL->buf[i] = '\0'; /* tie off buffer just in case */
+ if (!((s = strchr (LOCAL->buf,'\015')) && (s[1] == '\012'))) {
+ sprintf (tmp,"Unable to find CRLF at %lu in %lu bytes, text: %.80s",
+ (unsigned long) curpos,i,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ *s = '\0'; /* tie off header line */
+ i = (s + 2) - LOCAL->buf; /* note start of text offset */
+ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
+ sprintf (tmp,"Unable to parse internal header at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ if (!(isxdigit (t[1]) && isxdigit (t[2]) && isxdigit (t[3]) &&
+ isxdigit (t[4]) && isxdigit (t[5]) && isxdigit (t[6]) &&
+ isxdigit (t[7]) && isxdigit (t[8]) && isxdigit (t[9]) &&
+ isxdigit (t[10]) && isxdigit (t[11]) && isxdigit (t[12]))) {
+ sprintf (tmp,"Unable to parse message flags at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ if ((t[13] != '-') || t[22] ||
+ !(isxdigit (t[14]) && isxdigit (t[15]) && isxdigit (t[16]) &&
+ isxdigit (t[17]) && isxdigit (t[18]) && isxdigit (t[19]) &&
+ isxdigit (t[20]) && isxdigit (t[21]))) {
+ sprintf (tmp,"Unable to parse message UID at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+
+ *s++ = '\0'; *t++ = '\0'; /* break up fields */
+ /* get message size */
+ if (!(j = strtoul (s,(char **) &x,10)) && (!(x && *x))) {
+ sprintf (tmp,"Unable to parse message size at %lu: %.80s,%.80s;%.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf,(char *) s,
+ (char *) t);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ /* make sure didn't run off end of file */
+ if (((off_t) (curpos + i + j)) > sbuf.st_size) {
+ sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)",
+ (unsigned long) curpos,(unsigned long) (curpos + i + j),
+ (unsigned long) sbuf.st_size);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ /* parse UID */
+ if ((m = strtoul (t+13,NIL,16)) &&
+ ((m <= lastuid) || (m > stream->uid_last))) {
+ if (uidwarn) {
+ sprintf (tmp,"Invalid UID %08lx in message %lu, rebuilding UIDs",
+ m,nmsgs+1);
+ mm_log (tmp,WARN);
+ uidwarn = NIL;
+ /* restart UID validity */
+ stream->uid_validity = (unsigned long) time (0);
+ }
+ m = 0; /* lose this UID */
+ dirty = T; /* mark dirty, set new lastuid */
+ stream->uid_last = lastuid;
+ }
+
+ t[12] = '\0'; /* parse system flags */
+ if ((k = strtoul (t+8,NIL,16)) & fEXPUNGED) {
+ if (m) lastuid = m; /* expunge message, update last UID seen */
+ else { /* no UID assigned? */
+ lastuid = ++stream->uid_last;
+ dirty = T;
+ }
+ }
+ else { /* not expunged, swell the cache */
+ added = T; /* note that a new message was added */
+ mail_exists (stream,++nmsgs);
+ /* instantiate an elt for this message */
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ /* parse the date */
+ if (!mail_parse_date (elt,LOCAL->buf)) {
+ sprintf (tmp,"Unable to parse message date at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ /* note file offset of header */
+ elt->private.special.offset = curpos;
+ /* and internal header size */
+ elt->private.special.text.size = i;
+ /* header size not known yet */
+ elt->private.msg.header.text.size = 0;
+ elt->rfc822_size = j; /* note message size */
+ /* calculate system flags */
+ if (k & fSEEN) elt->seen = T;
+ if (k & fDELETED) elt->deleted = T;
+ if (k & fFLAGGED) elt->flagged = T;
+ if (k & fANSWERED) elt->answered = T;
+ if (k & fDRAFT) elt->draft = T;
+ t[8] = '\0'; /* get user flags value */
+ elt->user_flags = strtoul (t,NIL,16);
+ /* UID already assigned? */
+ if (!(elt->private.uid = m) || !(k & fOLD)) {
+ elt->recent = T; /* no, mark as recent */
+ ++recent; /* count up a new recent message */
+ dirty = T; /* and must rewrite header */
+ /* assign new UID */
+ if (!elt->private.uid) elt->private.uid = ++stream->uid_last;
+ mbx_update_status (stream,elt->msgno,NIL);
+ }
+ /* update last parsed UID */
+ lastuid = elt->private.uid;
+ }
+ curpos += i + j; /* update position */
+ }
+
+ if (dirty && !stream->rdonly){/* update header */
+ mbx_update_header (stream);
+ fsync (LOCAL->fd); /* make sure all the UID updates take */
+ }
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */
+ LOCAL->filetime = sbuf.st_mtime;
+ if (added && !stream->rdonly){/* make sure atime updated */
+ struct utimbuf times;
+ times.actime = time (0);
+ times.modtime = LOCAL->filetime;
+ utime (stream->mailbox,&times);
+ }
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
+ mail_recent (stream,recent); /* and of change in recent messages */
+ return LONGT; /* return the winnage */
+}
+
+/* MBX get cache element with status updating from file
+ * Accepts: MAIL stream
+ * message number
+ * expunge OK flag
+ * Returns: cache element
+ */
+
+MESSAGECACHE *mbx_elt (MAILSTREAM *stream,unsigned long msgno,long expok)
+{
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ struct { /* old flags */
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged;
+ old.answered = elt->answered; old.draft = elt->draft;
+ old.user_flags = elt->user_flags;
+ /* get new flags */
+ if (mbx_read_flags (stream,elt) && expok) {
+ mail_expunged (stream,elt->msgno);
+ return NIL; /* return this message was expunged */
+ }
+ if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
+ (old.flagged != elt->flagged) || (old.answered != elt->answered) ||
+ (old.draft != elt->draft) || (old.user_flags != elt->user_flags))
+ mm_flags (stream,msgno); /* let top level know */
+ return elt;
+}
+
+/* MBX read flags from file
+ * Accepts: MAIL stream
+ * cache element
+ * Returns: non-NIL if message expunged
+ */
+
+unsigned long mbx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ unsigned long i;
+ struct stat sbuf;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ /* make sure file size is good */
+ if (sbuf.st_size < LOCAL->filesize) {
+ sprintf (LOCAL->buf,"Mailbox shrank from %lu to %lu in flag read!",
+ (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size);
+ fatal (LOCAL->buf);
+ }
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 24,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,14) < 0) {
+ sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ if ((LOCAL->buf[0] != ';') || (LOCAL->buf[13] != '-')) {
+ LOCAL->buf[14] = '\0'; /* tie off buffer for error message */
+ sprintf (LOCAL->buf+50,"Invalid flags for message %lu (%lu %lu): %s",
+ elt->msgno,elt->private.special.offset,
+ elt->private.special.text.size,(char *) LOCAL->buf);
+ fatal (LOCAL->buf+50);
+ }
+ LOCAL->buf[13] = '\0'; /* tie off buffer */
+ /* calculate system flags */
+ i = strtoul (LOCAL->buf+9,NIL,16);
+ elt->seen = i & fSEEN ? T : NIL;
+ elt->deleted = i & fDELETED ? T : NIL;
+ elt->flagged = i & fFLAGGED ? T : NIL;
+ elt->answered = i & fANSWERED ? T : NIL;
+ elt->draft = i & fDRAFT ? T : NIL;
+ LOCAL->expunged |= i & fEXPUNGED ? T : NIL;
+ LOCAL->buf[9] = '\0'; /* tie off flags */
+ /* get user flags value */
+ elt->user_flags = strtoul (LOCAL->buf+1,NIL,16);
+ elt->valid = T; /* have valid flags now */
+ return i & fEXPUNGED;
+}
+
+/* MBX update header
+ * Accepts: MAIL stream
+ */
+
+#define NTKLUDGEOFFSET 7
+
+void mbx_update_header (MAILSTREAM *stream)
+{
+ int i;
+ char *s = LOCAL->buf;
+ memset (s,'\0',HDRSIZE); /* initialize header */
+ sprintf (s,"*mbx*\015\012%08lx%08lx\015\012",
+ stream->uid_validity,stream->uid_last);
+ for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i]; ++i)
+ sprintf (s += strlen (s),"%s\015\012",stream->user_flags[i]);
+ LOCAL->ffuserflag = i; /* first free user flag */
+ /* can we create more user flags? */
+ stream->kwd_create = (i < NUSERFLAGS) ? T : NIL;
+ /* write reserved lines */
+ while (i++ < NUSERFLAGS) strcat (s,"\015\012");
+ while (T) { /* rewind file */
+ lseek (LOCAL->fd,NTKLUDGEOFFSET,L_SET);
+ /* write new header */
+ if (write (LOCAL->fd,LOCAL->buf + NTKLUDGEOFFSET,
+ HDRSIZE - NTKLUDGEOFFSET) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+}
+
+/* MBX update status string
+ * Accepts: MAIL stream
+ * message number
+ * flags
+ */
+
+void mbx_update_status (MAILSTREAM *stream,unsigned long msgno,long flags)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ /* readonly */
+ if (stream->rdonly || !elt->valid) mbx_read_flags (stream,elt);
+ else { /* readwrite */
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ /* make sure file size is good */
+ if (sbuf.st_size < LOCAL->filesize) {
+ sprintf (LOCAL->buf,"Mailbox shrank from %lu to %lu in flag update!",
+ (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size);
+ fatal (LOCAL->buf);
+ }
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 24,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,14) < 0) {
+ sprintf (LOCAL->buf,"Unable to read old status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ if ((LOCAL->buf[0] != ';') || (LOCAL->buf[13] != '-')) {
+ LOCAL->buf[14] = '\0'; /* tie off buffer for error message */
+ sprintf (LOCAL->buf+50,"Invalid flags for message %lu (%lu %lu): %s",
+ elt->msgno,elt->private.special.offset,
+ elt->private.special.text.size,(char *) LOCAL->buf);
+ fatal (LOCAL->buf+50);
+ }
+ /* print new flag string */
+ sprintf (LOCAL->buf,"%08lx%04x-%08lx",elt->user_flags,(unsigned)
+ (((elt->deleted && flags) ?
+ fEXPUNGED : (strtoul (LOCAL->buf+9,NIL,16)) & fEXPUNGED) +
+ (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft) + fOLD),elt->private.uid);
+ while (T) { /* get to that place in the file */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 23,L_SET);
+ /* write new flags and UID */
+ if (write (LOCAL->fd,LOCAL->buf,21) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+ }
+}
+
+/* MBX locate header for a message
+ * Accepts: MAIL stream
+ * message number
+ * pointer to returned header size
+ * pointer to possible returned header
+ * Returns: position of header in file
+ */
+
+#define HDRBUFLEN 16384 /* good enough for most headers */
+#define SLOP 4 /* CR LF CR LF */
+
+unsigned long mbx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size,char **hdr)
+{
+ unsigned long siz,done;
+ long i;
+ unsigned char *s,*t,*te;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ unsigned long ret = elt->private.special.offset +
+ elt->private.special.text.size;
+ if (hdr) *hdr = NIL; /* assume no header returned */
+ /* is header size known? */
+ if (*size = elt->private.msg.header.text.size) return ret;
+ /* paranoia check */
+ if (LOCAL->buflen < (HDRBUFLEN + SLOP))
+ fatal ("LOCAL->buf smaller than HDRBUFLEN");
+ lseek (LOCAL->fd,ret,L_SET); /* get to header position */
+ /* read HDRBUFLEN chunks with 4 byte slop */
+ for (done = siz = 0, s = LOCAL->buf;
+ (i = min ((long) (elt->rfc822_size - done),(long) HDRBUFLEN)) &&
+ (read (LOCAL->fd,s,i) == i);
+ done += i, siz += (t - LOCAL->buf) - SLOP, s = LOCAL->buf + SLOP) {
+ te = (t = s + i) - 12; /* calculate end of fast scan */
+ /* fast scan for CR */
+ for (s = LOCAL->buf; s < te;)
+ if (((*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') ||
+ (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') ||
+ (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') ||
+ (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015')) &&
+ (*s == '\012') && (*++s == '\015') && (*++s == '\012')) {
+ *size = elt->private.msg.header.text.size = siz + (++s - LOCAL->buf);
+ if (hdr) *hdr = LOCAL->buf;
+ return ret;
+ }
+ for (te = t - 3; (s < te);) /* final character-at-a-time scan */
+ if ((*s++ == '\015') && (*s == '\012') && (*++s == '\015') &&
+ (*++s == '\012')) {
+ *size = elt->private.msg.header.text.size = siz + (++s - LOCAL->buf);
+ if (hdr) *hdr = LOCAL->buf;
+ return ret;
+ }
+ if (i <= SLOP) break; /* end of data */
+ /* slide over last 4 bytes */
+ memmove (LOCAL->buf,t - SLOP,SLOP);
+ hdr = NIL; /* can't return header this way */
+ }
+ /* not found: header consumes entire message */
+ elt->private.msg.header.text.size = *size = elt->rfc822_size;
+ if (hdr) *hdr = LOCAL->buf; /* possibly return header too */
+ return ret;
+}
+
+/* MBX mail rewrite mailbox
+ * Accepts: MAIL stream
+ * pointer to return reclaimed size
+ * flags (0 = no expunge, 1 = expunge deleted, -1 = expunge sequence)
+ * Returns: number of expunged messages
+ */
+
+unsigned long mbx_rewrite (MAILSTREAM *stream,unsigned long *reclaimed,
+ long flags)
+{
+ struct utimbuf times;
+ struct stat sbuf;
+ off_t pos,ppos;
+ int ld;
+ unsigned long i,j,k,m,delta;
+ unsigned long n = *reclaimed = 0;
+ unsigned long recent = 0;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ /* get parse/append permission */
+ if ((ld = lockname (lock,stream->mailbox,LOCK_EX)) < 0) {
+ mm_log ("Unable to lock expunge mailbox",ERROR);
+ return 0;
+ }
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime && !LOCAL->flagcheck &&
+ (LOCAL->filetime < sbuf.st_mtime)) LOCAL->flagcheck = T;
+ if (!mbx_parse (stream)) { /* make sure see any newly-arrived messages */
+ unlockfd (ld,lock); /* failed?? */
+ return 0;
+ }
+ if (LOCAL->flagcheck) { /* sweep flags if need flagcheck */
+ LOCAL->filetime = sbuf.st_mtime;
+ for (i = 1; i <= stream->nmsgs; ++i) mbx_elt (stream,i,NIL);
+ LOCAL->flagcheck = NIL;
+ }
+
+ /* get exclusive access */
+ if (!flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
+ mm_critical (stream); /* go critical */
+ for (i = 1,delta = 0,pos = ppos = HDRSIZE; i <= stream->nmsgs; ) {
+ /* note if message not at predicted location */
+ if (m = (elt = mbx_elt (stream,i,NIL))->private.special.offset - ppos) {
+ ppos = elt->private.special.offset;
+ *reclaimed += m; /* note reclaimed message space */
+ delta += m; /* and as expunge delta */
+ }
+ /* number of bytes to smash or preserve */
+ ppos += (k = elt->private.special.text.size + elt->rfc822_size);
+ /* if need to expunge this message*/
+ if (flags && elt->deleted && ((flags > 0) || elt->sequence)) {
+ delta += k; /* number of bytes to delete */
+ mail_expunged(stream,i);/* notify upper levels */
+ n++; /* count up one more expunged message */
+ }
+ else { /* preserved message */
+ i++; /* count this message */
+ if (elt->recent) ++recent;
+ if (delta) { /* moved, note first byte to preserve */
+ j = elt->private.special.offset;
+ do { /* read from source position */
+ m = min (k,LOCAL->buflen);
+ lseek (LOCAL->fd,j,L_SET);
+ read (LOCAL->fd,LOCAL->buf,m);
+ pos = j - delta; /* write to destination position */
+ while (T) {
+ lseek (LOCAL->fd,pos,L_SET);
+ if (write (LOCAL->fd,LOCAL->buf,m) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+ pos += m; /* new position */
+ j += m; /* next chunk, perhaps */
+ } while (k -= m); /* until done */
+ /* note the new address of this text */
+ elt->private.special.offset -= delta;
+ }
+ /* preserved but no deleted messages yet */
+ else pos = elt->private.special.offset + k;
+ }
+ }
+ /* deltaed file size match position? */
+ if (m = (LOCAL->filesize -= delta) - pos) {
+ *reclaimed += m; /* probably an fEXPUNGED msg */
+ LOCAL->filesize = pos; /* set correct size */
+ }
+ /* truncate file after last message */
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ fsync (LOCAL->fd); /* force disk update */
+ mm_nocritical (stream); /* release critical */
+ flock (LOCAL->fd,LOCK_SH); /* allow sharers again */
+ }
+
+ else { /* can't get exclusive */
+ flock (LOCAL->fd,LOCK_SH); /* recover previous shared mailbox lock */
+ /* do hide-expunge when shared */
+ if (flags) for (i = 1; i <= stream->nmsgs; ) {
+ if (elt = mbx_elt (stream,i,T)) {
+ /* make the message invisible */
+ if (elt->deleted && ((flags > 0) || elt->sequence)) {
+ mbx_update_status (stream,elt->msgno,LONGT);
+ /* notify upper levels */
+ mail_expunged (stream,i);
+ n++; /* count up one more expunged message */
+ }
+ else {
+ i++; /* preserved message */
+ if (elt->recent) ++recent;
+ }
+ }
+ else n++; /* count up one more expunged message */
+ }
+ fsync (LOCAL->fd); /* force disk update */
+ }
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* reset atime to now */
+ utime (stream->mailbox,&times);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ return n; /* return number of expunged messages */
+}
+
+/* MBX mail lock for flag updating
+ * Accepts: stream
+ * Returns: T if successful, NIL if failure
+ */
+
+long mbx_flaglock (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ unsigned long i;
+ int ld;
+ char lock[MAILTMPLEN];
+ /* no-op if readonly or already locked */
+ if (!stream->rdonly && LOCAL && (LOCAL->fd >= 0) && (LOCAL->ld < 0)) {
+ /* lock now */
+ if ((ld = lockname (lock,stream->mailbox,LOCK_EX)) < 0) return NIL;
+ if (!LOCAL->flagcheck) { /* don't do this if flagcheck already needed */
+ if (LOCAL->filetime) { /* know previous time? */
+ fstat (LOCAL->fd,&sbuf);/* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->flagcheck = T;
+ LOCAL->filetime = 0; /* don't do this test for any other messages */
+ }
+ if (!mbx_parse (stream)) {/* parse mailbox */
+ unlockfd (ld,lock); /* shouldn't happen */
+ return NIL;
+ }
+ if (LOCAL->flagcheck) /* invalidate cache if flagcheck */
+ for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->valid = NIL;
+ }
+ LOCAL->ld = ld; /* copy to stream for subsequent calls */
+ memcpy (LOCAL->lock,lock,MAILTMPLEN);
+ }
+ return LONGT;
+}
diff --git a/imap/src/osdep/nt/mkautaux.bat b/imap/src/osdep/nt/mkautaux.bat
new file mode 100755
index 00000000..c65022d2
--- /dev/null
+++ b/imap/src/osdep/nt/mkautaux.bat
@@ -0,0 +1,31 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Authenticator Linkage Generator auxillary for NT/Win9x
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 6 December 1995
+REM Last Edited:30 August 2006
+
+ECHO extern AUTHENTICATOR auth_%1; >> LINKAGE.H
+REM Note the introduction of the caret to quote the ampersand in NT
+if "%OS%" == "Windows_NT" ECHO auth_link (^&auth_%1); /* link in the %1 authenticator */ >> LINKAGE.C
+if "%OS%" == "" ECHO auth_link (&auth_%1); /* link in the %1 authenticator */ >> LINKAGE.C
+ECHO #include "auth_%1.c" >> AUTHS.C
diff --git a/imap/src/osdep/nt/mkauths.bat b/imap/src/osdep/nt/mkauths.bat
new file mode 100755
index 00000000..d8c5e360
--- /dev/null
+++ b/imap/src/osdep/nt/mkauths.bat
@@ -0,0 +1,33 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Authenticator Linkage Generator for DOS and Windows
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 6 December 1995
+REM Last Edited:30 August 2006
+
+REM Erase old authenticators list
+IF EXIST AUTHS.C DEL AUTHS.C
+
+REM Now define the new list
+FOR %%D IN (%1 %2 %3 %4 %5 %6 %7 %8 %9) DO CALL MKAUTAUX %%D
+
+EXIT 0
diff --git a/imap/src/osdep/nt/mtxnt.c b/imap/src/osdep/nt/mtxnt.c
new file mode 100644
index 00000000..13f61586
--- /dev/null
+++ b/imap/src/osdep/nt/mtxnt.c
@@ -0,0 +1,1232 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MTX mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 22 May 1990
+ * Last Edited: 15 June 2007
+ */
+
+
+/* FILE TIME SEMANTICS
+ *
+ * The atime is the last read time of the file.
+ * The mtime is the last flags update time of the file.
+ * The ctime is the last write time of the file.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/utime.h>
+#include "misc.h"
+#include "dummy.h"
+#include "fdstring.h"
+
+/* MTX I/O stream local data */
+
+typedef struct mtx_local {
+ unsigned int shouldcheck: 1; /* if ping should do a check instead */
+ unsigned int mustcheck: 1; /* if ping must do a check instead */
+ int fd; /* file descriptor for I/O */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ time_t lastsnarf; /* last snarf time */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+} MTXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MTXLOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *mtx_valid (char *name);
+int mtx_isvalid (char *name,char *file);
+void *mtx_parameters (long function,void *value);
+void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void mtx_list (MAILSTREAM *stream,char *ref,char *pat);
+void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long mtx_create (MAILSTREAM *stream,char *mailbox);
+long mtx_delete (MAILSTREAM *stream,char *mailbox);
+long mtx_rename (MAILSTREAM *stream,char *old,char *newname);
+long mtx_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *mtx_open (MAILSTREAM *stream);
+void mtx_close (MAILSTREAM *stream,long options);
+void mtx_flags (MAILSTREAM *stream,char *sequence,long flags);
+char *mtx_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void mtx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long mtx_ping (MAILSTREAM *stream);
+void mtx_check (MAILSTREAM *stream);
+void mtx_snarf (MAILSTREAM *stream);
+long mtx_expunge (MAILSTREAM *stream,char *sequence,long options);
+long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+long mtx_parse (MAILSTREAM *stream);
+MESSAGECACHE *mtx_elt (MAILSTREAM *stream,unsigned long msgno);
+void mtx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt);
+void mtx_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag);
+unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size);
+
+
+/* MTX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mtxdriver = {
+ "mtx", /* driver name */
+ /* driver flags */
+ DR_LOCAL|DR_MAIL|DR_CRLF|DR_NOSTICKY,
+ (DRIVER *) NIL, /* next driver */
+ mtx_valid, /* mailbox is valid for us */
+ mtx_parameters, /* manipulate parameters */
+ mtx_scan, /* scan mailboxes */
+ mtx_list, /* list mailboxes */
+ mtx_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ mtx_create, /* create mailbox */
+ mtx_delete, /* delete mailbox */
+ mtx_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ mtx_open, /* open mailbox */
+ mtx_close, /* close mailbox */
+ mtx_flags, /* fetch message "fast" attributes */
+ mtx_flags, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mtx_header, /* fetch message header */
+ mtx_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ mtx_flag, /* modify flags */
+ mtx_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mtx_ping, /* ping mailbox to see if still alive */
+ mtx_check, /* check for new messages */
+ mtx_expunge, /* expunge deleted messages */
+ mtx_copy, /* copy messages to another mailbox */
+ mtx_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mtxproto = {&mtxdriver};
+
+/* MTX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mtx_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return mtx_isvalid (name,tmp) ? &mtxdriver : NIL;
+}
+
+
+/* MTX mail test for valid mailbox
+ * Accepts: mailbox name
+ * buffer to return file name
+ * Returns: T if valid, NIL otherwise
+ */
+
+int mtx_isvalid (char *name,char *file)
+{
+ int fd;
+ int ret = NIL;
+ char *s,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ struct utimbuf times;
+ errno = EINVAL; /* assume invalid argument */
+ /* if file, get its status */
+ if ((s = dummy_file (file,name)) && !stat (s,&sbuf) &&
+ ((sbuf.st_mode & S_IFMT) == S_IFREG)) {
+ if (!sbuf.st_size)errno = 0;/* empty file */
+ else if ((fd = open (file,O_BINARY|O_RDONLY,NIL)) >= 0) {
+ memset (tmp,'\0',MAILTMPLEN);
+ if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\015')) &&
+ (s[1] == '\012')) { /* valid format? */
+ *s = '\0'; /* tie off header */
+ /* must begin with dd-mmm-yy" */
+ ret = (((tmp[2] == '-' && tmp[6] == '-') ||
+ (tmp[1] == '-' && tmp[5] == '-')) &&
+ (s = strchr (tmp+18,',')) && strchr (s+2,';')) ? T : NIL;
+ }
+ else errno = -1; /* bogus format */
+ close (fd); /* close the file */
+ /* \Marked status? */
+ if (sbuf.st_ctime > sbuf.st_atime) {
+ /* preserve atime and mtime */
+ times.actime = sbuf.st_atime;
+ times.modtime = sbuf.st_mtime;
+ utime (file,&times); /* set the times */
+ }
+ }
+ }
+ /* in case INBOX but not mtx format */
+ else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1;
+ return ret; /* return what we should */
+}
+
+
+/* MTX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mtx_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+/* MTX mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* MTX mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mtx_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* MTX mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* MTX mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mtx_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,mbx[MAILTMPLEN];
+ if (s = dummy_file (mbx,mailbox)) return dummy_create (stream,s);
+ sprintf (mbx,"Can't create %.80s: invalid name",mailbox);
+ mm_log (mbx,ERROR);
+ return NIL;
+}
+
+
+/* MTX mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mtx_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return mtx_rename (stream,mailbox,NIL);
+}
+
+/* MTX mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long mtx_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = LONGT;
+ char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ int fd,ld;
+ struct stat sbuf;
+ if (!dummy_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'\\')) && !s[1])))) {
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ if ((fd = open (file,O_BINARY|O_RDWR,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get exclusive parse/append permission */
+ if ((ld = lockname (lock,file,LOCK_EX)) < 0) {
+ mm_log ("Unable to lock rename mailbox",ERROR);
+ return NIL;
+ }
+ /* lock out other users */
+ if (flock (fd,LOCK_EX|LOCK_NB)) {
+ close (fd); /* couldn't lock, give up on it then */
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+ mm_log (tmp,ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return NIL;
+ }
+
+ if (newname) { /* want rename? */
+ /* found superior to destination name? */
+ if ((s = strrchr (tmp,'\\')) && (s != tmp) &&
+ ((tmp[1] != ':') || (s != tmp + 2))) {
+ c = s[1]; /* remember character after delimiter */
+ *s = s[1] = '\0'; /* tie off name at delimiter */
+ /* name doesn't exist, create it */
+ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) {
+ *s = '\\'; /* restore delimiter */
+ if (!dummy_create (stream,tmp)) ret = NIL;
+ }
+ else *s = '\\'; /* restore delimiter */
+ s[1] = c; /* restore character after delimiter */
+ }
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* pacify NTFS */
+ /* rename the file */
+ if (ret && rename (file,tmp)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ else {
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* pacify NTFS */
+ if (unlink (file)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %.80s",old,strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return ret; /* return success */
+}
+
+/* MTX mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mtx_open (MAILSTREAM *stream)
+{
+ int fd,ld;
+ char tmp[MAILTMPLEN];
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &mtxproto;
+ if (stream->local) fatal ("mtx recycle stream");
+ /* canonicalize the mailbox name */
+ if (!dummy_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ mm_log (tmp,ERROR);
+ }
+ if (stream->rdonly ||
+ (fd = open (tmp,O_BINARY|O_RDWR,NIL)) < 0) {
+ if ((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox: %.80s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ else if (!stream->rdonly) { /* got it, but readonly */
+ mm_log ("Can't get write access to mailbox, access is readonly",WARN);
+ stream->rdonly = T;
+ }
+ }
+ stream->local = fs_get (sizeof (MTXLOCAL));
+ LOCAL->fd = fd; /* bind the file */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ /* get shared parse permission */
+ if ((ld = lockname (tmp,stream->mailbox,LOCK_SH)) < 0) {
+ mm_log ("Unable to lock open mailbox",ERROR);
+ return NIL;
+ }
+ flock (LOCAL->fd,LOCK_SH); /* lock the file */
+ unlockfd (ld,tmp); /* release shared parse permission */
+ LOCAL->filesize = 0; /* initialize parsed file size */
+ LOCAL->filetime = 0; /* time not set up yet */
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ stream->sequence++; /* bump sequence number */
+ stream->uid_validity = (unsigned long) time (0);
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ if (mtx_ping (stream) && !stream->nmsgs)
+ mm_log ("Mailbox is empty",(long) NIL);
+ if (!LOCAL) return NIL; /* failure if stream died */
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft =
+ stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ return stream; /* return stream to caller */
+}
+
+/* MTX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mtx_close (MAILSTREAM *stream,long options)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ if (options & CL_EXPUNGE) mtx_expunge (stream,NIL,NIL);
+ stream->silent = silent; /* restore previous status */
+ flock (LOCAL->fd,LOCK_UN); /* unlock local file */
+ close (LOCAL->fd); /* close the local file */
+ /* free local text buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+
+/* MTX mail fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ * Sniffs at file to see if some other process changed the flags
+ */
+
+void mtx_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ unsigned long i;
+ if (mtx_ping (stream) && /* ping mailbox, get new status for messages */
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if (mail_elt (stream,i)->sequence) mtx_elt (stream,i);
+}
+
+/* MTX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *mtx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags)
+{
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ /* get to header position */
+ lseek (LOCAL->fd,mtx_hdrpos (stream,msgno,length),L_SET);
+ /* is buffer big enough? */
+ if (*length > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = *length) + 1);
+ }
+ LOCAL->buf[*length] = '\0'; /* tie off string */
+ /* slurp the data */
+ read (LOCAL->fd,LOCAL->buf,*length);
+ return LOCAL->buf;
+}
+
+/* MTX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: T, always
+ */
+
+long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ FDDATA d;
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mtx_elt (stream,msgno); /* get message status */
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ elt->seen = T; /* mark message as seen */
+ /* recalculate status */
+ mtx_update_status (stream,msgno,NIL);
+ mm_flags (stream,msgno);
+ }
+ /* find header position */
+ i = mtx_hdrpos (stream,msgno,&j);
+ d.fd = LOCAL->fd; /* set up file descriptor */
+ d.pos = i + j;
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE;
+ INIT (bs,fd_string,&d,elt->rfc822_size - j);
+ return T; /* success */
+}
+
+/* MTX mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ */
+
+void mtx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ struct utimbuf times;
+ struct stat sbuf;
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* make sure read comes after all that */
+ utime (stream->mailbox,&times);
+ }
+}
+
+
+/* MTX mail per-message modify flags
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ struct stat sbuf;
+ /* maybe need to do a checkpoint? */
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ LOCAL->filetime = 0; /* don't do this test for any other messages */
+ }
+ /* recalculate status */
+ mtx_update_status (stream,elt->msgno,NIL);
+}
+
+/* MTX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream still alive, NIL if not
+ */
+
+long mtx_ping (MAILSTREAM *stream)
+{
+ unsigned long i = 1;
+ long r = T;
+ int ld;
+ char lock[MAILTMPLEN];
+ struct stat sbuf;
+ if (stream && LOCAL) { /* only if stream already open */
+ fstat (LOCAL->fd,&sbuf); /* get current file poop */
+ if (LOCAL->filetime && !(LOCAL->mustcheck || LOCAL->shouldcheck) &&
+ (LOCAL->filetime < sbuf.st_mtime)) LOCAL->shouldcheck = T;
+ /* check for changed message status */
+ if (LOCAL->mustcheck || LOCAL->shouldcheck) {
+ LOCAL->filetime = sbuf.st_mtime;
+ if (LOCAL->shouldcheck) /* babble when we do this unilaterally */
+ mm_notify (stream,"[CHECK] Checking for flag updates",NIL);
+ while (i <= stream->nmsgs) mtx_elt (stream,i++);
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ }
+ /* get shared parse/append permission */
+ if ((sbuf.st_size != LOCAL->filesize) &&
+ ((ld = lockname (lock,stream->mailbox,LOCK_SH)) >= 0)) {
+ /* parse resulting mailbox */
+ r = (mtx_parse (stream)) ? T : NIL;
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ }
+ return r; /* return result of the parse */
+}
+
+
+/* MTX mail check mailbox (reparses status too)
+ * Accepts: MAIL stream
+ */
+
+void mtx_check (MAILSTREAM *stream)
+{
+ /* mark that a check is desired */
+ if (LOCAL) LOCAL->mustcheck = T;
+ if (mtx_ping (stream)) mm_log ("Check completed",(long) NIL);
+}
+
+/* MTX mail expunge mailbox
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long mtx_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ struct utimbuf times;
+ struct stat sbuf;
+ off_t pos = 0;
+ int ld;
+ unsigned long i = 1;
+ unsigned long j,k,m,recent;
+ unsigned long n = 0;
+ unsigned long delta = 0;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ if (!(ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ mtx_ping (stream))); /* parse sequence if given, ping stream */
+ else if (stream->rdonly) mm_log ("Expunge ignored on readonly mailbox",WARN);
+ else {
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ }
+ /* get exclusive parse/append permission */
+ if ((ld = lockname (lock,stream->mailbox,LOCK_EX)) < 0)
+ mm_log ("Unable to lock expunge mailbox",ERROR);
+ /* make sure see any newly-arrived messages */
+ else if (!mtx_parse (stream));
+ /* get exclusive access */
+ else if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
+ flock (LOCAL->fd,LOCK_SH);/* recover previous lock */
+ mm_log ("Can't expunge because mailbox is in use by another process",
+ ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+
+ else {
+ mm_critical (stream); /* go critical */
+ recent = stream->recent; /* get recent now that pinged and locked */
+ /* for each message */
+ while (i <= stream->nmsgs) {
+ /* get cache element */
+ elt = mtx_elt (stream,i);
+ /* number of bytes to smash or preserve */
+ k = elt->private.special.text.size + elt->rfc822_size;
+ /* if need to expunge this message */
+ if (elt->deleted && (sequence ? elt->sequence : T)) {
+ /* if recent, note one less recent message */
+ if (elt->recent) --recent;
+ delta += k; /* number of bytes to delete */
+ /* notify upper levels */
+ mail_expunged (stream,i);
+ n++; /* count up one more expunged message */
+ }
+ else if (i++ && delta) {/* preserved message */
+ /* first byte to preserve */
+ j = elt->private.special.offset;
+ do { /* read from source position */
+ m = min (k,LOCAL->buflen);
+ lseek (LOCAL->fd,j,L_SET);
+ read (LOCAL->fd,LOCAL->buf,m);
+ pos = j - delta; /* write to destination position */
+ while (T) {
+ lseek (LOCAL->fd,pos,L_SET);
+ if (write (LOCAL->fd,LOCAL->buf,m) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+ pos += m; /* new position */
+ j += m; /* next chunk, perhaps */
+ } while (k -= m); /* until done */
+ /* note the new address of this text */
+ elt->private.special.offset -= delta;
+ }
+ /* preserved but no deleted messages */
+ else pos = elt->private.special.offset + k;
+ }
+ if (n) { /* truncate file after last message */
+ if (pos != (LOCAL->filesize -= delta)) {
+ sprintf (LOCAL->buf,
+ "Calculated size mismatch %lu != %lu, delta = %lu",
+ (unsigned long) pos,(unsigned long) LOCAL->filesize,delta);
+ mm_log (LOCAL->buf,WARN);
+ LOCAL->filesize = pos;/* fix it then */
+ }
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ sprintf (LOCAL->buf,"Expunged %lu messages",n);
+ /* output the news */
+ mm_log (LOCAL->buf,(long) NIL);
+ }
+ else mm_log ("No messages deleted, so no update needed",(long) NIL);
+ fsync (LOCAL->fd); /* force disk update */
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* reset atime to now */
+ utime (stream->mailbox,&times);
+ mm_nocritical (stream); /* release critical */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ flock (LOCAL->fd,LOCK_SH);/* allow sharers again */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+ }
+ return ret;
+}
+
+/* MTX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if success, NIL if failed
+ */
+
+long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ struct utimbuf times;
+ MESSAGECACHE *elt;
+ unsigned long i,j,k;
+ long ret = LONGT;
+ int fd,ld;
+ char file[MAILTMPLEN],lock[MAILTMPLEN];
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ /* make sure valid mailbox */
+ if (!mtx_isvalid (mailbox,file)) switch (errno) {
+ case ENOENT: /* no such file? */
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid MTX-format mailbox name: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a MTX-format mailbox: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* got file? */
+ if ((fd = open (file,O_BINARY|O_RDWR|O_CREAT,S_IREAD|S_IWRITE)) < 0) {
+ sprintf (LOCAL->buf,"Unable to open copy mailbox: %.80s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ mm_critical (stream); /* go critical */
+ /* get exclusive parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockname (lock,file,LOCK_EX)) < 0)) {
+ mm_log ("Unable to lock copy mailbox",ERROR);
+ mm_nocritical (stream);
+ return NIL;
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
+
+ /* for each requested message */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ /* number of bytes to copy */
+ k = elt->private.special.text.size + elt->rfc822_size;
+ do { /* read from source position */
+ j = min (k,LOCAL->buflen);
+ read (LOCAL->fd,LOCAL->buf,j);
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ } while (ret && (k -= j));/* until done */
+ }
+ /* make sure all the updates take */
+ if (!(ret && (ret = !fsync (fd)))) {
+ sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ }
+ /* set atime to now-1 if successful copy */
+ if (ret) times.actime = time (0) - 1;
+ /* else preserved \Marked status */
+ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ?
+ sbuf.st_atime : time (0);
+ times.modtime = sbuf.st_mtime;/* preserve mtime */
+ utime (file,&times); /* set the times */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ close (fd); /* close the file */
+ mm_nocritical (stream); /* release critical */
+ /* delete all requested messages */
+ if (ret && (options & CP_MOVE)) {
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mtx_elt (stream,i))->sequence) {
+ elt->deleted = T; /* mark message deleted */
+ /* recalculate status */
+ mtx_update_status (stream,i,NIL);
+ }
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* make sure atime remains greater */
+ utime (stream->mailbox,&times);
+ }
+ }
+ if (ret && mail_parameters (NIL,GET_COPYUID,NIL))
+ mm_log ("Can not return meaningful COPYUID with this mailbox format",WARN);
+ return ret;
+}
+
+/* MTX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd,ld,c;
+ char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ struct utimbuf times;
+ FILE *df;
+ MESSAGECACHE elt;
+ long f;
+ unsigned long i,uf;
+ STRING *message;
+ long ret = LONGT;
+ /* default stream to prototype */
+ if (!stream) stream = &mtxproto;
+ /* make sure valid mailbox */
+ if (!mtx_isvalid (mailbox,file)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (!compare_cstring (mailbox,"INBOX")) mtx_create (NIL,"INBOX");
+ else {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* falls through */
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid MTX-format mailbox name: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a MTX-format mailbox: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get first message */
+ if (!(*af) (stream,data,&flags,&date,&message)) return NIL;
+
+ /* open destination mailbox */
+ if (((fd = open (file,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE))
+ < 0) || !(df = fdopen (fd,"ab"))) {
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockname (lock,file,LOCK_EX)) < 0)) {
+ mm_log ("Unable to lock append mailbox",ERROR);
+ close (fd);
+ return NIL;
+ }
+ mm_critical (stream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ errno = 0;
+ do { /* parse flags */
+ if (!SIZE (message)) { /* guard against zero-length */
+ mm_log ("Append of zero-length message",ERROR);
+ ret = NIL;
+ break;
+ }
+ f = mail_parse_flags (stream,flags,&i);
+ /* reverse bits (dontcha wish we had CIRC?) */
+ for (uf = 0; i; uf |= 1 << (29 - find_rightmost_bit (&i)));
+ if (date) { /* parse date if given */
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ mm_log (tmp,ERROR);
+ ret = NIL; /* mark failure */
+ break;
+ }
+ mail_date (tmp,&elt); /* write preseved date */
+ }
+ else internal_date (tmp); /* get current date in IMAP format */
+ /* write header */
+ if (fprintf (df,"%s,%lu;%010lo%02lo\015\012",tmp,i = SIZE (message),uf,
+ (unsigned long) f) < 0) ret = NIL;
+ else { /* write message */
+ if (i) do c = 0xff & SNX (message);
+ while ((putc (c,df) != EOF) && --i);
+ /* get next message */
+ if (i || !(*af) (stream,data,&flags,&date,&message)) ret = NIL;
+ }
+ } while (ret && message);
+ /* if error... */
+ if (!ret || (fflush (df) == EOF)) {
+ ftruncate (fd,sbuf.st_size);/* revert file */
+ close (fd); /* make sure fclose() doesn't corrupt us */
+ if (errno) {
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ }
+ ret = NIL;
+ }
+ if (ret) times.actime = time (0) - 1;
+ /* else preserved \Marked status */
+ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ?
+ sbuf.st_atime : time (0);
+ times.modtime = sbuf.st_mtime;/* preserve mtime */
+ utime (file,&times); /* set the times */
+ fclose (df); /* close the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ mm_nocritical (stream); /* release critical */
+ if (ret && mail_parameters (NIL,GET_APPENDUID,NIL))
+ mm_log ("Can not return meaningful APPENDUID with this mailbox format",
+ WARN);
+ return ret;
+}
+
+/* Internal routines */
+
+
+/* MTX mail parse mailbox
+ * Accepts: MAIL stream
+ * Returns: T if parse OK
+ * NIL if failure, stream aborted
+ */
+
+long mtx_parse (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = NIL;
+ unsigned char c,*s,*t,*x;
+ char tmp[MAILTMPLEN];
+ unsigned long i,j;
+ long curpos = LOCAL->filesize;
+ long nmsgs = stream->nmsgs;
+ long recent = stream->recent;
+ short added = NIL;
+ short silent = stream->silent;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ if (sbuf.st_size < curpos) { /* sanity check */
+ sprintf (tmp,"Mailbox shrank from %ld to %ld!",curpos,sbuf.st_size);
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ stream->silent = T; /* don't pass up mm_exists() events yet */
+ while (sbuf.st_size - curpos){/* while there is stuff to parse */
+ /* get to that position in the file */
+ lseek (LOCAL->fd,curpos,L_SET);
+ if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
+ sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size,
+ i ? strerror (errno) : "no data read");
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ LOCAL->buf[i] = '\0'; /* tie off buffer just in case */
+ if (!((s = strchr (LOCAL->buf,'\015')) && (s[1] == '\012'))) {
+ sprintf (tmp,"Unable to find CRLF at %lu in %lu bytes, text: %s",
+ (unsigned long) curpos,i,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ *s = '\0'; /* tie off header line */
+ i = (s + 2) - LOCAL->buf; /* note start of text offset */
+ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
+ sprintf (tmp,"Unable to parse internal header at %lu: %s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ *s++ = '\0'; *t++ = '\0'; /* tie off fields */
+
+ added = T; /* note that a new message was added */
+ /* swell the cache */
+ mail_exists (stream,++nmsgs);
+ /* instantiate an elt for this message */
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ elt->private.uid = ++stream->uid_last;
+ /* note file offset of header */
+ elt->private.special.offset = curpos;
+ /* in case error */
+ elt->private.special.text.size = 0;
+ /* header size not known yet */
+ elt->private.msg.header.text.size = 0;
+ x = s; /* parse the header components */
+ if (mail_parse_date (elt,LOCAL->buf) &&
+ (elt->rfc822_size = strtoul (s,(char **) &s,10)) && (!(s && *s)) &&
+ isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) &&
+ isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) &&
+ isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) &&
+ isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12])
+ elt->private.special.text.size = i;
+ else { /* oops */
+ sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s",
+ curpos,(char *) LOCAL->buf,(char *) x,(char *) t);
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ /* make sure didn't run off end of file */
+ if ((curpos += (elt->rfc822_size + i)) > sbuf.st_size) {
+ sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)",
+ elt->private.special.offset,(unsigned long) curpos,
+ (unsigned long) sbuf.st_size);
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ c = t[10]; /* remember first system flags byte */
+ t[10] = '\0'; /* tie off flags */
+ j = strtoul (t,NIL,8); /* get user flags value */
+ t[10] = c; /* restore first system flags byte */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ /* calculate system flags */
+ if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T;
+ if (j & fDELETED) elt->deleted = T;
+ if (j & fFLAGGED) elt->flagged = T;
+ if (j & fANSWERED) elt->answered = T;
+ if (j & fDRAFT) elt->draft = T;
+ if (!(j & fOLD)) { /* newly arrived message? */
+ elt->recent = T;
+ recent++; /* count up a new recent message */
+ /* mark it as old */
+ mtx_update_status (stream,nmsgs,NIL);
+ }
+ }
+ fsync (LOCAL->fd); /* make sure all the fOLD flags take */
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */
+ LOCAL->filetime = sbuf.st_mtime;
+ if (added && !stream->rdonly){/* make sure atime updated */
+ struct utimbuf times;
+ times.actime = time (0);
+ times.modtime = LOCAL->filetime;
+ utime (stream->mailbox,&times);
+ }
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
+ mail_recent (stream,recent); /* and of change in recent messages */
+ return LONGT; /* return the winnage */
+}
+
+/* MTX get cache element with status updating from file
+ * Accepts: MAIL stream
+ * message number
+ * Returns: cache element
+ */
+
+MESSAGECACHE *mtx_elt (MAILSTREAM *stream,unsigned long msgno)
+{
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ struct { /* old flags */
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged;
+ old.answered = elt->answered; old.draft = elt->draft;
+ old.user_flags = elt->user_flags;
+ mtx_read_flags (stream,elt);
+ if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
+ (old.flagged != elt->flagged) || (old.answered != elt->answered) ||
+ (old.draft != elt->draft) || (old.user_flags != elt->user_flags))
+ mm_flags (stream,msgno); /* let top level know */
+ return elt;
+}
+
+/* MTX read flags from file
+ * Accepts: MAIL stream
+ * Returns: cache element
+ */
+
+void mtx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ unsigned long i,j;
+ /* noop if readonly and have valid flags */
+ if (stream->rdonly && elt->valid) return;
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 14,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,12) < 0) {
+ sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ /* calculate system flags */
+ i = (((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0');
+ elt->seen = i & fSEEN ? T : NIL; elt->deleted = i & fDELETED ? T : NIL;
+ elt->flagged = i & fFLAGGED ? T : NIL;
+ elt->answered = i & fANSWERED ? T : NIL; elt->draft = i & fDRAFT ? T : NIL;
+ LOCAL->buf[10] = '\0'; /* tie off flags */
+ j = strtoul(LOCAL->buf,NIL,8);/* get user flags value */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ elt->valid = T; /* have valid flags now */
+}
+
+/* MTX update status string
+ * Accepts: MAIL stream
+ * message number
+ * flag saying whether or not to sync
+ */
+
+void mtx_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag)
+{
+ struct utimbuf times;
+ struct stat sbuf;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ unsigned long j,k = 0;
+ /* readonly */
+ if (stream->rdonly || !elt->valid) mtx_read_flags (stream,elt);
+ else { /* readwrite */
+ j = elt->user_flags; /* get user flags */
+ /* reverse bits (dontcha wish we had CIRC?) */
+ while (j) k |= 1 << (29 - find_rightmost_bit (&j));
+ /* print new flag string */
+ sprintf (LOCAL->buf,"%010lo%02o",k,(unsigned)
+ (fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)));
+ while (T) { /* get to that place in the file */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 14,L_SET);
+ /* write new flags */
+ if (write (LOCAL->fd,LOCAL->buf,12) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+ if (syncflag) { /* sync if requested */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* make sure read is later */
+ utime (stream->mailbox,&times);
+ }
+ }
+}
+
+/* MTX locate header for a message
+ * Accepts: MAIL stream
+ * message number
+ * pointer to returned header size
+ * Returns: position of header in file
+ */
+
+unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size)
+{
+ unsigned long siz;
+ long i = 0;
+ int q = 0;
+ char *s,tmp[MAILTMPLEN];
+ MESSAGECACHE *elt = mtx_elt (stream,msgno);
+ unsigned long ret = elt->private.special.offset +
+ elt->private.special.text.size;
+ /* is header size known? */
+ if (!(*size = elt->private.msg.header.text.size)) {
+ lseek (LOCAL->fd,ret,L_SET);/* get to header position */
+ /* search message for CRLF CRLF */
+ for (siz = 1,s = tmp; siz <= elt->rfc822_size; siz++) {
+ /* read another buffer as necessary */
+ if ((--i <= 0) && /* buffer empty? */
+ (read (LOCAL->fd,s = tmp,
+ i = min (elt->rfc822_size - siz,(long) MAILTMPLEN)) < 0))
+ return ret; /* I/O error? */
+ switch (q) { /* sniff at buffer */
+ case 0: /* first character */
+ q = (*s++ == '\015') ? 1 : 0;
+ break;
+ case 1: /* second character */
+ q = (*s++ == '\012') ? 2 : 0;
+ break;
+ case 2: /* third character */
+ q = (*s++ == '\015') ? 3 : 0;
+ break;
+ case 3: /* fourth character */
+ if (*s++ == '\012') { /* have the sequence? */
+ /* yes, note for later */
+ elt->private.msg.header.text.size = *size = siz;
+ return ret;
+ }
+ q = 0; /* lost... */
+ break;
+ }
+ }
+ /* header consumes entire message */
+ elt->private.msg.header.text.size = *size = elt->rfc822_size;
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/nt/nl_nt.c b/imap/src/osdep/nt/nl_nt.c
new file mode 100644
index 00000000..47cb7f0a
--- /dev/null
+++ b/imap/src/osdep/nt/nl_nt.c
@@ -0,0 +1,61 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Windows/TOPS-20 newline routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Copy string with CRLF newlines
+ * Accepts: destination string
+ * pointer to size of destination string buffer
+ * source string
+ * length of source string
+ * Returns: length of copied string
+ */
+
+unsigned long strcrlfcpy (unsigned char **dst,unsigned long *dstl,
+ unsigned char *src,unsigned long srcl)
+{
+ /* flush destination buffer if too small */
+ if (*dst && (srcl > *dstl)) fs_give ((void **) dst);
+ if (!*dst) { /* make a new buffer if needed */
+ *dst = (char *) fs_get ((size_t) (*dstl = srcl) + 1);
+ if (dstl) *dstl = srcl; /* return new buffer length to main program */
+ }
+ /* copy strings */
+ if (srcl) memcpy (*dst,src,(size_t) srcl);
+ *(*dst + srcl) = '\0'; /* tie off destination */
+ return srcl; /* return length */
+}
+
+
+/* Length of string after strcrlfcpy applied
+ * Accepts: source string
+ * Returns: length of string
+ */
+
+unsigned long strcrlflen (STRING *s)
+{
+ return SIZE (s); /* no-brainer on DOS! */
+}
diff --git a/imap/src/osdep/nt/os_nt.c b/imap/src/osdep/nt/os_nt.c
new file mode 100644
index 00000000..0f6fea89
--- /dev/null
+++ b/imap/src/osdep/nt/os_nt.c
@@ -0,0 +1,48 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- NT version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_nt.h" /* must be before osdep includes tcp.h */
+#undef ERROR /* quell conflicting def warning */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+#include <sys\timeb.h>
+#include <fcntl.h>
+#include <sys\stat.h>
+#include "misc.h"
+#include "mailfile.h"
+
+#include "fs_nt.c"
+#include "ftl_nt.c"
+#include "nl_nt.c"
+#include "yunchan.c"
+#include "tcp_nt.c" /* must be before env_nt.c */
+#include "env_nt.c"
+#include "ssl_nt.c"
diff --git a/imap/src/osdep/nt/os_nt.h b/imap/src/osdep/nt/os_nt.h
new file mode 100644
index 00000000..650bb782
--- /dev/null
+++ b/imap/src/osdep/nt/os_nt.h
@@ -0,0 +1,60 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- NT version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 27 April 2007
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys\types.h>
+#include <time.h>
+#include <io.h>
+#include <conio.h>
+#include <process.h>
+#undef ERROR /* quell conflicting defintion warning */
+#include <windows.h>
+#include <lm.h>
+#undef ERROR
+#define ERROR (long) 2 /* must match mail.h */
+
+#if _MSC_VER >= 1400
+#define strtok_r strtok_s /* for some reason they called it this */
+#else
+/* strtok() is actually MT-safe in MSVC. Why is it that Microsoft can do
+ * their CRT right, but GNU, Sun, etc. can't?
+ */
+#define strtok_r(a,b,c) strtok(*(c) = a,b)
+#endif
+
+#include "env_nt.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "yunchan.h"
+
+#undef noErr
+#undef MAC
diff --git a/imap/src/osdep/nt/os_ntk.c b/imap/src/osdep/nt/os_ntk.c
new file mode 100644
index 00000000..87c69123
--- /dev/null
+++ b/imap/src/osdep/nt/os_ntk.c
@@ -0,0 +1,51 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- NT version + Kerberos
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_nt.h" /* must be before osdep includes tcp.h */
+#undef ERROR /* quell conflicting def warning */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+#include <sys\timeb.h>
+#include <fcntl.h>
+#include <sys\stat.h>
+#include "misc.h"
+#include "mailfile.h"
+
+#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
+
+#include "fs_nt.c"
+#include "ftl_nt.c"
+#include "nl_nt.c"
+#include "yunchan.c"
+#include "kerb_mit.c"
+#include "tcp_nt.c" /* must be before env_nt.c */
+#include "env_nt.c"
+#include "ssl_nt.c"
diff --git a/imap/src/osdep/nt/os_old.c b/imap/src/osdep/nt/os_old.c
new file mode 100644
index 00000000..5cd6c0c2
--- /dev/null
+++ b/imap/src/osdep/nt/os_old.c
@@ -0,0 +1,48 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- NT version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 21 December 2007
+ */
+
+#include "tcp_nt.h" /* must be before osdep includes tcp.h */
+#undef ERROR /* quell conflicting def warning */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+#include <sys\timeb.h>
+#include <fcntl.h>
+#include <sys\stat.h>
+#include "misc.h"
+#include "mailfile.h"
+
+#include "fs_nt.c"
+#include "ftl_nt.c"
+#include "nl_nt.c"
+#include "yunchan.c"
+#include "tcp_nt.c" /* must be before env_nt.c */
+#include "env_nt.c"
+#include "ssl_old.c"
diff --git a/imap/src/osdep/nt/os_w2k.c b/imap/src/osdep/nt/os_w2k.c
new file mode 100644
index 00000000..c7af4faa
--- /dev/null
+++ b/imap/src/osdep/nt/os_w2k.c
@@ -0,0 +1,49 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Windows 2000 version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_nt.h" /* must be before osdep includes tcp.h */
+#undef ERROR /* quell conflicting def warning */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+#include <sys\timeb.h>
+#include <fcntl.h>
+#include <sys\stat.h>
+#include "misc.h"
+#include "mailfile.h"
+
+#include "fs_nt.c"
+#include "ftl_nt.c"
+#include "nl_nt.c"
+#include "yunchan.c"
+#include "kerb_w2k.c"
+#include "tcp_nt.c" /* must be before env_nt.c */
+#include "env_nt.c"
+#include "ssl_w2k.c"
diff --git a/imap/src/osdep/nt/pmatch.c b/imap/src/osdep/nt/pmatch.c
new file mode 100644
index 00000000..95a0bb86
--- /dev/null
+++ b/imap/src/osdep/nt/pmatch.c
@@ -0,0 +1,89 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: IMAP Wildcard Matching Routines (case-independent)
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 June 2000
+ * Last Edited: 30 August 2006
+ */
+
+/* Wildcard pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if pattern matches base, else NIL
+ */
+
+long pmatch_full (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ /* % at end, OK if no inferiors */
+ if (!pat[1]) return (delim && strchr (s,delim)) ? NIL : T;
+ /* scan remainder of string until delimiter */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while ((*s != delim) && *s++);
+ break;
+ case '*': /* match 0 or more characters */
+ if (!pat[1]) return T; /* * at end, unconditional match */
+ /* scan remainder of string */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while (*s++);
+ break;
+ case '\0': /* end of pattern */
+ return *s ? NIL : T; /* success if also end of base */
+ default: /* match this character */
+ return compare_uchar (*pat,*s) ? NIL : pmatch_full (s+1,pat+1,delim);
+ }
+ return NIL;
+}
+
+/* Directory pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if base is a matching directory of pattern, else NIL
+ */
+
+long dmatch (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ if (!*s) return T; /* end of base means have a subset match */
+ if (!*++pat) return NIL; /* % at end, no inferiors permitted */
+ /* scan remainder of string until delimiter */
+ do if (dmatch (s,pat,delim)) return T;
+ while ((*s != delim) && *s++);
+ if (*s && !s[1]) return T; /* ends with delimiter, must be subset */
+ return dmatch (s,pat,delim);/* do new scan */
+ case '*': /* match 0 or more characters */
+ return T; /* unconditional match */
+ case '\0': /* end of pattern */
+ break;
+ default: /* match this character */
+ if (*s) return compare_uchar (*pat,*s) ? NIL : dmatch (s+1,pat+1,delim);
+ /* end of base, return if at delimiter */
+ else if (*pat == delim) return T;
+ break;
+ }
+ return NIL;
+}
diff --git a/imap/src/osdep/nt/pseudo.c b/imap/src/osdep/nt/pseudo.c
new file mode 100644
index 00000000..1aae8a53
--- /dev/null
+++ b/imap/src/osdep/nt/pseudo.c
@@ -0,0 +1,36 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Pseudo Header Strings
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 September 1996
+ * Last Edited: 30 August 2006
+ */
+
+/* Local sites may wish to alter this text */
+
+char *pseudo_from = "MAILER-DAEMON";
+char *pseudo_name = "Mail System Internal Data";
+char *pseudo_subject = "DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA";
+char *pseudo_msg =
+ "This text is part of the internal format of your mail folder, and is not\na real message. It is created automatically by the mail system software.\nIf deleted, important folder data will be lost, and it will be re-created\nwith the data reset to initial values."
+ ;
diff --git a/imap/src/osdep/nt/pseudo.h b/imap/src/osdep/nt/pseudo.h
new file mode 100644
index 00000000..c9c07628
--- /dev/null
+++ b/imap/src/osdep/nt/pseudo.h
@@ -0,0 +1,30 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Pseudo Header Strings
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 September 1996
+ * Last Edited: 30 August 2006
+ */
+
+
+extern char *pseudo_from,*pseudo_name,*pseudo_subject,*pseudo_msg;
diff --git a/imap/src/osdep/nt/setproto.bat b/imap/src/osdep/nt/setproto.bat
new file mode 100755
index 00000000..ce7cb1ef
--- /dev/null
+++ b/imap/src/osdep/nt/setproto.bat
@@ -0,0 +1,29 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Set default prototype for DOS/NT
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 9 October 1995
+REM Last Edited: 30 August 2006
+
+REM Set the default drivers
+ECHO #define CREATEPROTO %1proto >> LINKAGE.H
+ECHO #define APPENDPROTO %2proto >> LINKAGE.H
diff --git a/imap/src/osdep/nt/ssl_none.c b/imap/src/osdep/nt/ssl_none.c
new file mode 100644
index 00000000..e4dedda7
--- /dev/null
+++ b/imap/src/osdep/nt/ssl_none.c
@@ -0,0 +1,141 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy (no SSL) authentication/encryption module
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 7 February 2001
+ * Last Edited: 30 August 2006
+ */
+
+/* Init server for SSL
+ * Accepts: server name
+ */
+
+void ssl_server_init (char *server)
+{
+ syslog (LOG_ERR,"This server does not support SSL");
+ exit (1); /* punt this program too */
+}
+
+
+/* Start TLS
+ * Accepts: /etc/services service name
+ * Returns: cpystr'd error string if TLS failed, else NIL for success
+ */
+
+char *ssl_start_tls (char *server)
+{
+ return cpystr ("This server does not support TLS");
+}
+
+/* Get character
+ * Returns: character or EOF
+ */
+
+int PBIN (void)
+{
+ return getchar ();
+}
+
+
+/* Get string
+ * Accepts: destination string pointer
+ * number of bytes available
+ * Returns: destination string pointer or NIL if EOF
+ */
+
+char *PSIN (char *s,int n)
+{
+ return fgets (s,n,stdin);
+}
+
+
+/* Get record
+ * Accepts: destination string pointer
+ * number of bytes to read
+ * Returns: T if success, NIL otherwise
+ */
+
+long PSINR (char *s,unsigned long n)
+{
+ unsigned long i;
+ while (n && ((i = fread (s,1,n,stdin)) || (errno == EINTR))) s += i,n -= i;
+ return n ? NIL : LONGT;
+}
+
+
+/* Wait for input
+ * Accepts: timeout in seconds
+ * Returns: T if have input on stdin, else NIL
+ */
+
+long INWAIT (long seconds)
+{
+ return server_input_wait (seconds);
+}
+
+/* Put character
+ * Accepts: character
+ * Returns: character written or EOF
+ */
+
+int PBOUT (int c)
+{
+ return putchar (c);
+}
+
+
+/* Put string
+ * Accepts: source string pointer
+ * Returns: 0 or EOF if error
+ */
+
+int PSOUT (char *s)
+{
+ return fputs (s,stdout);
+}
+
+
+/* Put record
+ * Accepts: source sized text
+ * Returns: 0 or EOF if error
+ */
+
+int PSOUTR (SIZEDTEXT *s)
+{
+ unsigned char *t;
+ unsigned long i,j;
+ for (t = s->data,i = s->size;
+ (i && ((j = fwrite (t,1,i,stdout)) || (errno == EINTR)));
+ t += j,i -= j);
+ return i ? EOF : NIL;
+}
+
+
+/* Flush output
+ * Returns: 0 or EOF if error
+ */
+
+int PFLUSH (void)
+{
+ return fflush (stdout);
+}
diff --git a/imap/src/osdep/nt/ssl_nt.c b/imap/src/osdep/nt/ssl_nt.c
new file mode 100644
index 00000000..c7efa486
--- /dev/null
+++ b/imap/src/osdep/nt/ssl_nt.c
@@ -0,0 +1,721 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: SSL authentication/encryption module for Windows 9x and NT
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 22 September 1998
+ * Last Edited: 13 January 2008
+ */
+
+#define SECURITY_WIN32
+#include <sspi.h>
+#include <schannel.h>
+
+
+#define SSLBUFLEN 8192
+
+
+/* SSL I/O stream */
+
+typedef struct ssl_stream {
+ TCPSTREAM *tcpstream; /* TCP stream */
+ CredHandle cred; /* SSL credentials */
+ CtxtHandle context; /* SSL context */
+ /* stream encryption sizes */
+ SecPkgContext_StreamSizes sizes;
+ size_t bufsize;
+ int ictr; /* input counter */
+ char *iptr; /* input pointer */
+ int iextractr; /* extra input counter */
+ char *iextraptr; /* extra input pointer */
+ char *ibuf; /* input buffer */
+ char *obuf; /* output buffer */
+} SSLSTREAM;
+
+#include "sslio.h"
+
+
+/* Function prototypes */
+
+static SSLSTREAM *ssl_start(TCPSTREAM *tstream,char *host,unsigned long flags);
+static char *ssl_analyze_status (SECURITY_STATUS err,char *buf);
+static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
+ long *contd);
+static long ssl_abort (SSLSTREAM *stream);
+
+/* Secure Sockets Layer network driver dispatch */
+
+static struct ssl_driver ssldriver = {
+ ssl_open, /* open connection */
+ ssl_aopen, /* open preauthenticated connection */
+ ssl_getline, /* get a line */
+ ssl_getbuffer, /* get a buffer */
+ ssl_soutr, /* output pushed data */
+ ssl_sout, /* output string */
+ ssl_close, /* close connection */
+ ssl_host, /* return host name */
+ ssl_remotehost, /* return remote host name */
+ ssl_port, /* return port number */
+ ssl_localhost /* return local host name */
+};
+
+ /* security function table */
+static SecurityFunctionTable *sft = NIL;
+static unsigned long ssltsz = 0;/* SSL maximum token length */
+
+
+/* Define crypt32.dll stuff here in case a pre-IE5 Win9x system */
+
+typedef DWORD (CALLBACK *CNTS) (DWORD,PCERT_NAME_BLOB,DWORD,LPSTR,DWORD);
+typedef BOOL (CALLBACK *CGCC) (HCERTCHAINENGINE,PCCERT_CONTEXT,LPFILETIME,
+ HCERTSTORE,PCERT_CHAIN_PARA,DWORD,LPVOID,
+ PCCERT_CHAIN_CONTEXT *);
+typedef BOOL (CALLBACK *CVCCP) (LPCSTR,PCCERT_CHAIN_CONTEXT,
+ PCERT_CHAIN_POLICY_PARA,
+ PCERT_CHAIN_POLICY_STATUS);
+typedef VOID (CALLBACK *CFCC) (PCCERT_CHAIN_CONTEXT);
+typedef BOOL (CALLBACK *CFCCX) (PCCERT_CONTEXT);
+
+static CNTS certNameToStr = NIL;
+static CGCC certGetCertificateChain = NIL;
+static CVCCP certVerifyCertificateChainPolicy = NIL;
+static CFCC certFreeCertificateChain = NIL;
+static CFCCX certFreeCertificateContext = NIL;
+
+/* One-time SSL initialization */
+
+static int sslonceonly = 0;
+
+void ssl_onceonlyinit (void)
+{
+ if (!sslonceonly++) { /* only need to call it once */
+ HINSTANCE lib;
+ FARPROC pi;
+ ULONG np;
+ SecPkgInfo *pp;
+ int i;
+ /* get security library */
+ if (((lib = LoadLibrary ("schannel.dll")) ||
+ (lib = LoadLibrary ("security.dll"))) &&
+ (pi = GetProcAddress (lib,SECURITY_ENTRYPOINT)) &&
+ (sft = (SecurityFunctionTable *) pi ()) &&
+ !(sft->EnumerateSecurityPackages (&np,&pp))) {
+ /* look for an SSL package */
+ for (i = 0; (i < (int) np); i++) if (!strcmp (pp[i].Name,UNISP_NAME)) {
+ /* note maximum token size and name */
+ ssltsz = pp[i].cbMaxToken;
+ /* apply runtime linkage */
+ mail_parameters (NIL,SET_SSLDRIVER,(void *) &ssldriver);
+ mail_parameters (NIL,SET_SSLSTART,(void *) ssl_start);
+ if ((lib = LoadLibrary ("crypt32.dll")) &&
+ (certGetCertificateChain = (CGCC)
+ GetProcAddress (lib,"CertGetCertificateChain")) &&
+ (certVerifyCertificateChainPolicy = (CVCCP)
+ GetProcAddress (lib,"CertVerifyCertificateChainPolicy")) &&
+ (certFreeCertificateChain = (CFCC)
+ GetProcAddress (lib,"CertFreeCertificateChain")) &&
+ (certFreeCertificateContext = (CFCCX)
+ GetProcAddress (lib,"CertFreeCertificateContext")))
+ certNameToStr = (CNTS) GetProcAddress (lib,"CertNameToStrA");
+ return; /* all done */
+ }
+ }
+ }
+}
+
+/* SSL open
+ * Accepts: host name
+ * contact service name
+ * contact port number
+ * Returns: SSL stream if success else NIL
+ */
+
+SSLSTREAM *ssl_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream = tcp_open (host,service,port);
+ return stream ? ssl_start (stream,host,port) : NIL;
+}
+
+
+/* SSL authenticated open
+ * Accepts: host name
+ * service name
+ * returned user name buffer
+ * Returns: SSL stream if success else NIL
+ */
+
+SSLSTREAM *ssl_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL; /* don't use this mechanism with SSL */
+}
+
+/* Start SSL/TLS negotiations
+ * Accepts: open TCP stream of session
+ * user's host name
+ * flags
+ * Returns: SSL stream if success else NIL
+ */
+
+static SSLSTREAM *ssl_start (TCPSTREAM *tstream,char *host,unsigned long flags)
+{
+ SECURITY_STATUS e;
+ ULONG a;
+ TimeStamp t;
+ SecBuffer ibuf[2],obuf[1];
+ SecBufferDesc ibufs,obufs;
+ SCHANNEL_CRED tlscred;
+ CERT_CONTEXT *cert = NIL;
+ CERT_CHAIN_PARA chparam;
+ CERT_CHAIN_CONTEXT *chain;
+ SSL_EXTRA_CERT_CHAIN_POLICY_PARA policy;
+ CERT_CHAIN_POLICY_PARA polparam;
+ CERT_CHAIN_POLICY_STATUS status;
+ char tmp[MAILTMPLEN],certname[256];
+ char *reason = NIL;
+ ULONG req = ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT |
+ ISC_REQ_CONFIDENTIALITY | ISC_REQ_USE_SESSION_KEY |
+ ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM | ISC_REQ_EXTENDED_ERROR |
+ ISC_REQ_MANUAL_CRED_VALIDATION;
+ LPSTR usage[] = {
+ szOID_PKIX_KP_SERVER_AUTH,
+ szOID_SERVER_GATED_CRYPTO,
+ szOID_SGC_NETSCAPE
+ };
+ PWSTR whost = NIL;
+ char *buf = (char *) fs_get (ssltsz);
+ unsigned long size = 0;
+ sslcertificatequery_t scq =
+ (sslcertificatequery_t) mail_parameters (NIL,GET_SSLCERTIFICATEQUERY,NIL);
+ sslfailure_t sf = (sslfailure_t) mail_parameters (NIL,GET_SSLFAILURE,NIL);
+ SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
+ sizeof (SSLSTREAM));
+ stream->tcpstream = tstream; /* bind TCP stream */
+ /* initialize TLS credential */
+ memset (&tlscred,0,sizeof (SCHANNEL_CRED));
+ tlscred.dwVersion = SCHANNEL_CRED_VERSION;
+ tlscred.grbitEnabledProtocols = SP_PROT_TLS1;
+
+ /* acquire credentials */
+ if (sft->AcquireCredentialsHandle
+ (NIL,UNISP_NAME,SECPKG_CRED_OUTBOUND,NIL,(flags & NET_TLSCLIENT) ?
+ &tlscred : NIL,NIL,NIL,&stream->cred,&t)
+ != SEC_E_OK) reason = "Acquire credentials handle failed";
+ else while (!reason) { /* negotiate security context */
+ /* initialize buffers */
+ ibuf[0].cbBuffer = size; ibuf[0].pvBuffer = buf;
+ ibuf[1].cbBuffer = 0; ibuf[1].pvBuffer = NIL;
+ obuf[0].cbBuffer = 0; obuf[0].pvBuffer = NIL;
+ ibuf[0].BufferType = obuf[0].BufferType = SECBUFFER_TOKEN;
+ ibuf[1].BufferType = SECBUFFER_EMPTY;
+ /* initialize buffer descriptors */
+ ibufs.ulVersion = obufs.ulVersion = SECBUFFER_VERSION;
+ ibufs.cBuffers = 2; obufs.cBuffers = 1;
+ ibufs.pBuffers = ibuf; obufs.pBuffers = obuf;
+ /* negotiate security */
+ e = sft->InitializeSecurityContext
+ (&stream->cred,size ? &stream->context : NIL,host,req,0,
+ SECURITY_NETWORK_DREP,size? &ibufs:NIL,0,&stream->context,&obufs,&a,&t);
+ /* have an output buffer we need to send? */
+ if (obuf[0].pvBuffer && obuf[0].cbBuffer) {
+ if (!tcp_sout (stream->tcpstream,obuf[0].pvBuffer,obuf[0].cbBuffer))
+ reason = "Unexpected TCP output disconnect";
+ /* free the buffer */
+ sft->FreeContextBuffer (obuf[0].pvBuffer);
+ }
+ if (!reason) switch (e) { /* negotiation state */
+ case SEC_I_INCOMPLETE_CREDENTIALS:
+ break; /* server wants client auth */
+ case SEC_I_CONTINUE_NEEDED:
+ if (size) { /* continue, read any data? */
+ /* yes, anything regurgiated back to us? */
+ if (ibuf[1].BufferType == SECBUFFER_EXTRA) {
+ /* yes, set this as the new data */
+ memmove (buf,buf + size - ibuf[1].cbBuffer,ibuf[1].cbBuffer);
+ size = ibuf[1].cbBuffer;
+ break;
+ }
+ size = 0; /* otherwise, read more stuff from server */
+ }
+ case SEC_E_INCOMPLETE_MESSAGE:
+ /* need to read more data from server */
+ if (!tcp_getdata (stream->tcpstream))
+ reason = "Unexpected TCP input disconnect";
+ else {
+ memcpy (buf+size,stream->tcpstream->iptr,stream->tcpstream->ictr);
+ size += stream->tcpstream->ictr;
+ /* empty it from TCP's buffers */
+ stream->tcpstream->iptr += stream->tcpstream->ictr;
+ stream->tcpstream->ictr = 0;
+ }
+ break;
+
+ case SEC_E_OK: /* success, any data to be regurgitated? */
+ if (ibuf[1].BufferType == SECBUFFER_EXTRA) {
+ /* yes, set this as the new data */
+ memmove (stream->tcpstream->iptr = stream->tcpstream->ibuf,
+ buf + size - ibuf[1].cbBuffer,ibuf[1].cbBuffer);
+ stream->tcpstream->ictr = ibuf[1].cbBuffer;
+ }
+ if (certNameToStr && !(flags & NET_NOVALIDATECERT)) {
+ /* need validation, make wchar of host */
+ if (!((size = MultiByteToWideChar (CP_ACP,0,host,-1,NIL,0)) &&
+ (whost = (PWSTR) fs_get (size*sizeof (WCHAR))) &&
+ MultiByteToWideChar (CP_ACP,0,host,-1,whost,size)))
+ fatal ("Can't make wchar of host name!");
+ /* get certificate */
+ if ((sft->QueryContextAttributes
+ (&stream->context,SECPKG_ATTR_REMOTE_CERT_CONTEXT,&cert) !=
+ SEC_E_OK) || !cert) {
+ reason = "*Unable to get certificate";
+ strcpy (certname,"<no certificate>");
+ }
+ else { /* get certificate subject name */
+ (*certNameToStr) (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ &cert->pCertInfo->Subject,CERT_X500_NAME_STR,
+ certname,255);
+ /* build certificate chain */
+ memset (&chparam,0,sizeof (chparam));
+ chparam.cbSize = sizeof (chparam);
+ chparam.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
+ chparam.RequestedUsage.Usage.rgpszUsageIdentifier = usage;
+ chparam.RequestedUsage.Usage.cUsageIdentifier =
+ sizeof (usage) / sizeof (LPSTR);
+ if (!(*certGetCertificateChain)
+ (NIL,cert,NIL,cert->hCertStore,&chparam,NIL,NIL,&chain))
+ reason = ssl_analyze_status (GetLastError (),tmp);
+ else { /* validate certificate chain */
+ memset (&policy,0,sizeof (SSL_EXTRA_CERT_CHAIN_POLICY_PARA));
+ policy.cbStruct = sizeof (SSL_EXTRA_CERT_CHAIN_POLICY_PARA);
+ policy.dwAuthType = AUTHTYPE_SERVER;
+ policy.fdwChecks = NIL;
+ policy.pwszServerName = whost;
+ memset (&polparam,0,sizeof (polparam));
+ polparam.cbSize = sizeof (polparam);
+ polparam.pvExtraPolicyPara = &policy;
+ memset (&status,0,sizeof (status));
+ status.cbSize = sizeof (status);
+ if (!(*certVerifyCertificateChainPolicy)
+ (CERT_CHAIN_POLICY_SSL,chain,&polparam,&status))
+ reason = ssl_analyze_status (GetLastError (),tmp);
+ else if (status.dwError)
+ reason = ssl_analyze_status (status.dwError,tmp);
+ (*certFreeCertificateChain) (chain);
+ }
+ (*certFreeCertificateContext) (cert);
+ }
+ if (whost) fs_give ((void **) &whost);
+
+ if (reason) { /* got an error? */
+ /* application callback */
+ if (scq) reason = (*scq) ((*reason == '*') ? reason + 1 : reason,
+ host,certname) ? NIL : "";
+ else if (*certname) { /* error message to return via mm_log() */
+ sprintf (buf,"*%.128s: %.255s",
+ (*reason == '*') ? reason + 1 : reason,certname);
+ reason = buf;
+ }
+ }
+ }
+ if (reason ||
+ (reason = ssl_analyze_status
+ (sft->QueryContextAttributes
+ (&stream->context,SECPKG_ATTR_STREAM_SIZES,&stream->sizes),buf)))
+ break; /* error in certificate or getting sizes */
+ fs_give ((void **) &buf); /* flush temporary buffer */
+ /* make maximum-sized buffers */
+ stream->bufsize = stream->sizes.cbHeader +
+ stream->sizes.cbMaximumMessage + stream->sizes.cbTrailer;
+ if (stream->sizes.cbMaximumMessage < SSLBUFLEN)
+ fatal ("cbMaximumMessage is less than SSLBUFLEN!");
+ else if (stream->sizes.cbMaximumMessage < 16384) {
+ sprintf (tmp,"WINDOWS BUG: cbMaximumMessage = %ld, should be 16384",
+ (long) stream->sizes.cbMaximumMessage);
+ mm_log (tmp,NIL);
+ }
+ stream->ibuf = (char *) fs_get (stream->bufsize);
+ stream->obuf = (char *) fs_get (stream->bufsize);
+ return stream;
+ default:
+ reason = ssl_analyze_status (e,buf);
+ }
+ }
+ ssl_close (stream); /* failed to do SSL */
+ stream = NIL; /* no stream returned */
+ switch (*reason) { /* analyze reason */
+ case '*': /* certificate failure */
+ ++reason; /* skip over certificate failure indication */
+ /* pass to error callback */
+ if (sf) (*sf) (host,reason,flags);
+ else { /* no error callback, build error message */
+ sprintf (tmp,"Certificate failure for %.80s: %.512s",host,reason);
+ mm_log (tmp,ERROR);
+ }
+ case '\0': /* user answered no to certificate callback */
+ if (flags & NET_TRYSSL) /* return dummy stream to stop tryssl */
+ stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
+ sizeof (SSLSTREAM));
+ break;
+ default: /* non-certificate failure */
+ if (flags & NET_TRYSSL); /* no error output if tryssl */
+ /* pass to error callback */
+ else if (sf) (*sf) (host,reason,flags);
+ else { /* no error callback, build error message */
+ sprintf (tmp,"TLS/SSL failure for %.80s: %.512s",host,reason);
+ mm_log (tmp,ERROR);
+ }
+ break;
+ }
+ fs_give ((void **) &buf); /* flush temporary buffer */
+ return stream;
+}
+
+/* Generate error text from SSL error code
+ * Accepts: SSL status
+ * scratch buffer
+ * Returns: text if error status, else NIL
+ */
+
+static char *ssl_analyze_status (SECURITY_STATUS err,char *buf)
+{
+ switch (err) {
+ case SEC_E_OK: /* no error */
+ case SEC_I_CONTINUE_NEEDED:
+ case SEC_I_INCOMPLETE_CREDENTIALS:
+ case SEC_E_INCOMPLETE_MESSAGE:
+ return NIL;
+ case SEC_E_NO_AUTHENTICATING_AUTHORITY:
+ mm_log ("unexpected SEC_E_NO_AUTHENTICATING_AUTHORITY",NIL);
+ return "*No authority could be contacted for authentication";
+ case SEC_E_WRONG_PRINCIPAL:
+ mm_log ("unexpected SEC_E_WRONG_PRINCIPAL",NIL);
+ case CERT_E_CN_NO_MATCH:
+ return "*Server name does not match certificate";
+ case SEC_E_UNTRUSTED_ROOT:
+ mm_log ("unexpected SEC_E_UNTRUSTED_ROOT",NIL);
+ case CERT_E_UNTRUSTEDROOT:
+ return "*Self-signed certificate or untrusted authority";
+ case SEC_E_CERT_EXPIRED:
+ mm_log ("unexpected SEC_E_CERT_EXPIRED",NIL);
+ case CERT_E_EXPIRED:
+ return "*Certificate has expired";
+ case CERT_E_REVOKED:
+ return "*Certificate revoked";
+ case SEC_E_INVALID_TOKEN:
+ return "Invalid token, probably not an SSL server";
+ case SEC_E_UNSUPPORTED_FUNCTION:
+ return "SSL not supported on this machine - upgrade your system software";
+ }
+ sprintf (buf,"Unexpected SSPI or certificate error %lx - report this",err);
+ return buf;
+}
+
+/* SSL receive line
+ * Accepts: SSL stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *ssl_getline (SSLSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = ssl_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = ssl_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* SSL receive line or partial line
+ * Accepts: SSL stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!ssl_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!ssl_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* SSL receive buffer
+ * Accepts: SSL stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long ssl_getbuffer (SSLSTREAM *stream,unsigned long size,char *buffer)
+{
+ unsigned long n;
+ while (size > 0) { /* until request satisfied */
+ if (!ssl_getdata (stream)) return NIL;
+ n = min (size,stream->ictr);/* number of bytes to transfer */
+ /* do the copy */
+ memcpy (buffer,stream->iptr,n);
+ buffer += n; /* update pointer */
+ stream->iptr += n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -= n;
+ }
+ buffer[0] = '\0'; /* tie off string */
+ return T;
+}
+
+/* SSL receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long ssl_getdata (SSLSTREAM *stream)
+{
+ while (stream->ictr < 1) { /* decrypted buffer empty? */
+ SECURITY_STATUS status;
+ SecBuffer buf[4];
+ SecBufferDesc msg;
+ size_t i;
+ size_t n = 0; /* initially no bytes to decrypt */
+ do { /* yes, make sure have data from TCP */
+ if (stream->iextractr) { /* have previous unread data? */
+ memcpy (stream->ibuf + n,stream->iextraptr,stream->iextractr);
+ n += stream->iextractr; /* update number of bytes read */
+ stream->iextractr = 0; /* no more extra data */
+ }
+ else { /* read from TCP */
+ if (!tcp_getdata (stream->tcpstream)) return ssl_abort (stream);
+ /* maximum amount of data to copy */
+ if (!(i = min (stream->bufsize - n,stream->tcpstream->ictr)))
+ fatal ("incomplete SecBuffer exceeds maximum buffer size");
+ /* do the copy */
+ memcpy (stream->ibuf + n,stream->tcpstream->iptr,i);
+ stream->tcpstream->iptr += i;
+ stream->tcpstream->ictr -= i;
+ n += i; /* update number of bytes to decrypt */
+ }
+ buf[0].cbBuffer = n; /* first SecBuffer gets data */
+ buf[0].pvBuffer = stream->ibuf;
+ buf[0].BufferType = SECBUFFER_DATA;
+ /* subsequent ones are for spares */
+ buf[1].BufferType = buf[2].BufferType = buf[3].BufferType =
+ SECBUFFER_EMPTY;
+ msg.ulVersion = SECBUFFER_VERSION;
+ msg.cBuffers = 4; /* number of SecBuffers */
+ msg.pBuffers = buf; /* first SecBuffer */
+
+ } while ((status = ((DECRYPT_MESSAGE_FN) sft->Reserved4)
+ (&stream->context,&msg,0,NIL)) == SEC_E_INCOMPLETE_MESSAGE);
+ switch (status) {
+ case SEC_E_OK: /* won */
+ case SEC_I_RENEGOTIATE: /* won but lost it after this buffer */
+ /* hunt for a buffer */
+ for (i = 0; (i < 4) && (buf[i].BufferType != SECBUFFER_DATA) ; i++);
+ if (i < 4) { /* found a buffer? */
+ /* yes, set up pointer and counter */
+ stream->iptr = buf[i].pvBuffer;
+ stream->ictr = buf[i].cbBuffer;
+ /* any unprocessed data? */
+ while (++i < 4) if (buf[i].BufferType == SECBUFFER_EXTRA) {
+ /* yes, note for next time around */
+ stream->iextraptr = buf[i].pvBuffer;
+ stream->iextractr = buf[i].cbBuffer;
+ }
+ }
+ break;
+ default: /* anything else means we've lost */
+ return ssl_abort (stream);
+ }
+ }
+ return LONGT;
+}
+
+/* SSL send string as record
+ * Accepts: SSL stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long ssl_soutr (SSLSTREAM *stream,char *string)
+{
+ return ssl_sout (stream,string,(unsigned long) strlen (string));
+}
+
+
+/* SSL send string
+ * Accepts: SSL stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long ssl_sout (SSLSTREAM *stream,char *string,unsigned long size)
+{
+ SecBuffer buf[4];
+ SecBufferDesc msg;
+ char *s;
+ size_t n;
+ if (!stream->tcpstream) return NIL;
+ /* until request satisfied */
+ for (s = stream->ibuf,n = 0; size;) {
+ /* header */
+ buf[0].BufferType = SECBUFFER_STREAM_HEADER;
+ memset (buf[0].pvBuffer = stream->obuf,0,
+ buf[0].cbBuffer = stream->sizes.cbHeader);
+ /* message (up to maximum size) */
+ buf[1].BufferType = SECBUFFER_DATA;
+ memcpy (buf[1].pvBuffer = stream->obuf + stream->sizes.cbHeader,string,
+ buf[1].cbBuffer = min (size,SSLBUFLEN));
+ /* trailer */
+ buf[2].BufferType = SECBUFFER_STREAM_TRAILER;
+ memset (buf[2].pvBuffer = ((char *) buf[1].pvBuffer) + buf[1].cbBuffer,0,
+ buf[2].cbBuffer = stream->sizes.cbTrailer);
+ /* spare */
+ buf[3].BufferType = SECBUFFER_EMPTY;
+ msg.ulVersion = SECBUFFER_VERSION;
+ msg.cBuffers = 4; /* number of SecBuffers */
+ msg.pBuffers = buf; /* first SecBuffer */
+ string += buf[1].cbBuffer;
+ size -= buf[1].cbBuffer; /* this many bytes processed */
+ /* encrypt and send message */
+ if ((((ENCRYPT_MESSAGE_FN) sft->Reserved3)
+ (&stream->context,0,&msg,NIL) != SEC_E_OK) ||
+ !tcp_sout (stream->tcpstream,stream->obuf,
+ buf[0].cbBuffer + buf[1].cbBuffer + buf[2].cbBuffer))
+ return ssl_abort (stream);/* encryption or sending failed */
+ }
+ return LONGT;
+}
+
+/* SSL close
+ * Accepts: SSL stream
+ */
+
+void ssl_close (SSLSTREAM *stream)
+{
+ ssl_abort (stream); /* nuke the stream */
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+
+/* SSL abort stream
+ * Accepts: SSL stream
+ * Returns: NIL always
+ */
+
+static long ssl_abort (SSLSTREAM *stream)
+{
+ if (stream->tcpstream) { /* close TCP stream */
+ sft->DeleteSecurityContext (&stream->context);
+ sft->FreeCredentialHandle (&stream->cred);
+ tcp_close (stream->tcpstream);
+ stream->tcpstream = NIL;
+ }
+ if (stream->ibuf) fs_give ((void **) &stream->ibuf);
+ if (stream->obuf) fs_give ((void **) &stream->obuf);
+ return NIL;
+}
+
+/* SSL get host name
+ * Accepts: SSL stream
+ * Returns: host name for this stream
+ */
+
+char *ssl_host (SSLSTREAM *stream)
+{
+ return tcp_host (stream->tcpstream);
+}
+
+
+/* SSL get remote host name
+ * Accepts: SSL stream
+ * Returns: host name for this stream
+ */
+
+char *ssl_remotehost (SSLSTREAM *stream)
+{
+ return tcp_remotehost (stream->tcpstream);
+}
+
+
+/* SSL return port for this stream
+ * Accepts: SSL stream
+ * Returns: port number for this stream
+ */
+
+unsigned long ssl_port (SSLSTREAM *stream)
+{
+ return tcp_port (stream->tcpstream);
+}
+
+
+/* SSL get local host name
+ * Accepts: SSL stream
+ * Returns: local host name
+ */
+
+char *ssl_localhost (SSLSTREAM *stream)
+{
+ return tcp_localhost (stream->tcpstream);
+}
+
+#include "ssl_none.c" /* currently no server support */
diff --git a/imap/src/osdep/nt/ssl_old.c b/imap/src/osdep/nt/ssl_old.c
new file mode 100644
index 00000000..121bd02e
--- /dev/null
+++ b/imap/src/osdep/nt/ssl_old.c
@@ -0,0 +1,625 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: SSL authentication/encryption module for Windows 9x and NT
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 22 September 1998
+ * Last Edited: 13 January 2008
+ */
+
+#define SECURITY_WIN32
+#include <sspi.h>
+#if(_WIN32_WINNT < 0x0400)
+typedef unsigned int ALG_ID;
+#else
+#include <wincrypt.h>
+ALGIDDEF
+#endif
+#include <schnlsp.h>
+#include <issperr.h>
+
+ /* in case a binary runs on Windows 2000 */
+#ifndef ISC_REQ_MANUAL_CRED_VALIDATION
+#define ISC_REQ_MANUAL_CRED_VALIDATION 0x00080000
+#endif
+#ifndef SEC_E_UNTRUSTED_ROOT
+#define SEC_E_UNTRUSTED_ROOT ((HRESULT) 0x80090325L)
+#endif
+#ifndef SEC_E_CERT_EXPIRED
+#define SEC_E_CERT_EXPIRED ((HRESULT) 0x80090328L)
+#endif
+
+
+#define SSLBUFLEN 8192
+
+
+/* SSL I/O stream */
+
+typedef struct ssl_stream {
+ TCPSTREAM *tcpstream; /* TCP stream */
+ CredHandle cred; /* SSL credentials */
+ CtxtHandle context; /* SSL context */
+ /* stream encryption sizes */
+ SecPkgContext_StreamSizes sizes;
+ size_t bufsize;
+ int ictr; /* input counter */
+ char *iptr; /* input pointer */
+ int iextractr; /* extra input counter */
+ char *iextraptr; /* extra input pointer */
+ char *ibuf; /* input buffer */
+ char *obuf; /* output buffer */
+} SSLSTREAM;
+
+#include "sslio.h"
+
+
+/* Function prototypes */
+
+static SSLSTREAM *ssl_start(TCPSTREAM *tstream,char *host,unsigned long flags);
+static char *ssl_analyze_status (SECURITY_STATUS err,char *buf);
+static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
+ long *contd);
+static long ssl_abort (SSLSTREAM *stream);
+
+/* Secure Sockets Layer network driver dispatch */
+
+static struct ssl_driver ssldriver = {
+ ssl_open, /* open connection */
+ ssl_aopen, /* open preauthenticated connection */
+ ssl_getline, /* get a line */
+ ssl_getbuffer, /* get a buffer */
+ ssl_soutr, /* output pushed data */
+ ssl_sout, /* output string */
+ ssl_close, /* close connection */
+ ssl_host, /* return host name */
+ ssl_remotehost, /* return remote host name */
+ ssl_port, /* return port number */
+ ssl_localhost /* return local host name */
+};
+
+ /* security function table */
+static SecurityFunctionTable *sft = NIL;
+static unsigned long ssltsz = 0;/* SSL maximum token length */
+
+/* One-time SSL initialization */
+
+static int sslonceonly = 0;
+
+void ssl_onceonlyinit (void)
+{
+ if (!sslonceonly++) { /* only need to call it once */
+ HINSTANCE lib;
+ FARPROC pi;
+ ULONG np;
+ SecPkgInfo *pp;
+ int i;
+ /* get security library */
+ if (((lib = LoadLibrary ("schannel.dll")) ||
+ (lib = LoadLibrary ("security.dll"))) &&
+ (pi = GetProcAddress (lib,SECURITY_ENTRYPOINT)) &&
+ (sft = (SecurityFunctionTable *) pi ()) &&
+ !(sft->EnumerateSecurityPackages (&np,&pp))) {
+ /* look for an SSL package */
+ for (i = 0; (i < (int) np); i++) if (!strcmp (pp[i].Name,UNISP_NAME)) {
+ /* note maximum token size and name */
+ ssltsz = pp[i].cbMaxToken;
+ /* apply runtime linkage */
+ mail_parameters (NIL,SET_SSLDRIVER,(void *) &ssldriver);
+ mail_parameters (NIL,SET_SSLSTART,(void *) ssl_start);
+ return; /* all done */
+ }
+ }
+ }
+}
+
+/* SSL open
+ * Accepts: host name
+ * contact service name
+ * contact port number
+ * Returns: SSL stream if success else NIL
+ */
+
+SSLSTREAM *ssl_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream = tcp_open (host,service,port);
+ return stream ? ssl_start (stream,host,port) : NIL;
+}
+
+
+/* SSL authenticated open
+ * Accepts: host name
+ * service name
+ * returned user name buffer
+ * Returns: SSL stream if success else NIL
+ */
+
+SSLSTREAM *ssl_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL; /* don't use this mechanism with SSL */
+}
+
+/* Start SSL/TLS negotiations
+ * Accepts: open TCP stream of session
+ * user's host name
+ * flags
+ * Returns: SSL stream if success else NIL
+ */
+
+static SSLSTREAM *ssl_start (TCPSTREAM *tstream,char *host,unsigned long flags)
+{
+ SECURITY_STATUS e;
+ ULONG a;
+ TimeStamp t;
+ SecBuffer ibuf[2],obuf[1];
+ SecBufferDesc ibufs,obufs;
+ char tmp[MAILTMPLEN];
+ char *reason = NIL;
+ ULONG req = ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT |
+ ISC_REQ_CONFIDENTIALITY | ISC_REQ_USE_SESSION_KEY |
+ ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM | ISC_REQ_EXTENDED_ERROR +
+ ((flags & NET_NOVALIDATECERT) ? ISC_REQ_MANUAL_CRED_VALIDATION :
+ ISC_REQ_MUTUAL_AUTH);
+ SCHANNEL_CRED tlscred;
+ char *buf = (char *) fs_get (ssltsz);
+ unsigned long size = 0;
+ sslfailure_t sf = (sslfailure_t) mail_parameters (NIL,GET_SSLFAILURE,NIL);
+ SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
+ sizeof (SSLSTREAM));
+ stream->tcpstream = tstream; /* bind TCP stream */
+ /* initialize TLS credential */
+ memset (&tlscred,0,sizeof (SCHANNEL_CRED));
+ tlscred.dwVersion = SCHANNEL_CRED_VERSION;
+ tlscred.grbitEnabledProtocols = SP_PROT_TLS1;
+
+ /* acquire credentials */
+ if (sft->AcquireCredentialsHandle
+ (NIL,UNISP_NAME,SECPKG_CRED_OUTBOUND,NIL,(flags & NET_TLSCLIENT) ?
+ &tlscred : NIL,NIL,NIL,&stream->cred,&t)
+ != SEC_E_OK) reason = "Acquire credentials handle failed";
+ else while (!reason) { /* negotiate security context */
+ /* initialize buffers */
+ ibuf[0].cbBuffer = size; ibuf[0].pvBuffer = buf;
+ ibuf[1].cbBuffer = 0; ibuf[1].pvBuffer = NIL;
+ obuf[0].cbBuffer = 0; obuf[0].pvBuffer = NIL;
+ ibuf[0].BufferType = obuf[0].BufferType = SECBUFFER_TOKEN;
+ ibuf[1].BufferType = SECBUFFER_EMPTY;
+ /* initialize buffer descriptors */
+ ibufs.ulVersion = obufs.ulVersion = SECBUFFER_VERSION;
+ ibufs.cBuffers = 2; obufs.cBuffers = 1;
+ ibufs.pBuffers = ibuf; obufs.pBuffers = obuf;
+ /* negotiate security */
+ e = sft->InitializeSecurityContext
+ (&stream->cred,size ? &stream->context : NIL,host,req,0,
+ SECURITY_NETWORK_DREP,size? &ibufs:NIL,0,&stream->context,&obufs,&a,&t);
+ /* have an output buffer we need to send? */
+ if (obuf[0].pvBuffer && obuf[0].cbBuffer) {
+ if (!tcp_sout (stream->tcpstream,obuf[0].pvBuffer,obuf[0].cbBuffer))
+ reason = "Unexpected TCP output disconnect";
+ /* free the buffer */
+ sft->FreeContextBuffer (obuf[0].pvBuffer);
+ }
+ if (!reason) switch (e) { /* negotiation state */
+ case SEC_I_INCOMPLETE_CREDENTIALS:
+ break; /* server wants client auth */
+ case SEC_I_CONTINUE_NEEDED:
+ if (size) { /* continue, read any data? */
+ /* yes, anything regurgiated back to us? */
+ if (ibuf[1].BufferType == SECBUFFER_EXTRA) {
+ /* yes, set this as the new data */
+ memmove (buf,buf + size - ibuf[1].cbBuffer,ibuf[1].cbBuffer);
+ size = ibuf[1].cbBuffer;
+ break;
+ }
+ size = 0; /* otherwise, read more stuff from server */
+ }
+ case SEC_E_INCOMPLETE_MESSAGE:
+ /* need to read more data from server */
+ if (!tcp_getdata (stream->tcpstream))
+ reason = "Unexpected TCP input disconnect";
+ else {
+ memcpy (buf+size,stream->tcpstream->iptr,stream->tcpstream->ictr);
+ size += stream->tcpstream->ictr;
+ /* empty it from TCP's buffers */
+ stream->tcpstream->iptr += stream->tcpstream->ictr;
+ stream->tcpstream->ictr = 0;
+ }
+ break;
+
+ case SEC_E_OK: /* success, any data to be regurgitated? */
+ if (ibuf[1].BufferType == SECBUFFER_EXTRA) {
+ /* yes, set this as the new data */
+ memmove (stream->tcpstream->iptr = stream->tcpstream->ibuf,
+ buf + size - ibuf[1].cbBuffer,ibuf[1].cbBuffer);
+ stream->tcpstream->ictr = ibuf[1].cbBuffer;
+ }
+ if (reason = ssl_analyze_status
+ (sft->QueryContextAttributes
+ (&stream->context,SECPKG_ATTR_STREAM_SIZES,&stream->sizes),buf))
+ break; /* error getting sizes */
+ fs_give ((void **) &buf); /* flush temporary buffer */
+ /* make maximum-sized buffers */
+ stream->bufsize = stream->sizes.cbHeader +
+ stream->sizes.cbMaximumMessage + stream->sizes.cbTrailer;
+ if (stream->sizes.cbMaximumMessage < SSLBUFLEN)
+ fatal ("cbMaximumMessage is less than SSLBUFLEN!");
+ else if (stream->sizes.cbMaximumMessage < 16384) {
+ sprintf (tmp,"WINDOWS BUG: cbMaximumMessage = %ld, should be 16384",
+ (long) stream->sizes.cbMaximumMessage);
+ mm_log (tmp,NIL);
+ }
+ stream->ibuf = (char *) fs_get (stream->bufsize);
+ stream->obuf = (char *) fs_get (stream->bufsize);
+ return stream;
+ default:
+ reason = ssl_analyze_status (e,buf);
+ }
+ }
+ ssl_close (stream); /* failed to do SSL */
+ stream = NIL; /* no stream returned */
+ fs_give ((void **) &buf); /* flush temporary buffer */
+ switch (*reason) { /* analyze reason */
+ case '*': /* certificate failure */
+ ++reason; /* skip over certificate failure indication */
+ /* pass to error callback */
+ if (sf) (*sf) (host,reason,flags);
+ else { /* no error callback, build error message */
+ sprintf (tmp,"Certificate failure for %.80s: %.512s",host,reason);
+ mm_log (tmp,ERROR);
+ }
+ case '\0': /* user answered no to certificate callback */
+ if (flags & NET_TRYSSL) /* return dummy stream to stop tryssl */
+ stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
+ sizeof (SSLSTREAM));
+ break;
+ default: /* non-certificate failure */
+ if (flags & NET_TRYSSL); /* no error output if tryssl */
+ /* pass to error callback */
+ else if (sf) (*sf) (host,reason,flags);
+ else { /* no error callback, build error message */
+ sprintf (tmp,"TLS/SSL failure for %.80s: %.512s",host,reason);
+ mm_log (tmp,ERROR);
+ }
+ break;
+ }
+ return stream;
+}
+
+/* Generate error text from SSL error code
+ * Accepts: SSL status
+ * scratch buffer
+ * Returns: text if error status, else NIL
+ */
+
+static char *ssl_analyze_status (SECURITY_STATUS err,char *buf)
+{
+ switch (err) {
+ case SEC_E_OK: /* no error */
+ case SEC_I_CONTINUE_NEEDED:
+ case SEC_I_INCOMPLETE_CREDENTIALS:
+ case SEC_E_INCOMPLETE_MESSAGE:
+ return NIL;
+ case SEC_E_NO_AUTHENTICATING_AUTHORITY:
+ return "*No authority could be contacted for authentication";
+ case SEC_E_WRONG_PRINCIPAL:
+ return "*Server name does not match certificate";
+ case SEC_E_UNTRUSTED_ROOT:
+ return "*Self-signed certificate or untrusted authority";
+ case SEC_E_CERT_EXPIRED:
+ return "*Certificate has expired";
+ case SEC_E_INVALID_TOKEN:
+ return "Invalid token, probably not an SSL server";
+ case SEC_E_UNSUPPORTED_FUNCTION:
+ return "SSL not supported on this machine - upgrade your system software";
+ }
+ sprintf (buf,"Unexpected SChannel error %lx - report this",err);
+ return buf;
+}
+
+/* SSL receive line
+ * Accepts: SSL stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *ssl_getline (SSLSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = ssl_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = ssl_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* SSL receive line or partial line
+ * Accepts: SSL stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!ssl_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!ssl_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* SSL receive buffer
+ * Accepts: SSL stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long ssl_getbuffer (SSLSTREAM *stream,unsigned long size,char *buffer)
+{
+ unsigned long n;
+ while (size > 0) { /* until request satisfied */
+ if (!ssl_getdata (stream)) return NIL;
+ n = min (size,stream->ictr);/* number of bytes to transfer */
+ /* do the copy */
+ memcpy (buffer,stream->iptr,n);
+ buffer += n; /* update pointer */
+ stream->iptr += n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -= n;
+ }
+ buffer[0] = '\0'; /* tie off string */
+ return T;
+}
+
+/* SSL receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long ssl_getdata (SSLSTREAM *stream)
+{
+ while (stream->ictr < 1) { /* decrypted buffer empty? */
+ SECURITY_STATUS status;
+ SecBuffer buf[4];
+ SecBufferDesc msg;
+ size_t i;
+ size_t n = 0; /* initially no bytes to decrypt */
+ do { /* yes, make sure have data from TCP */
+ if (stream->iextractr) { /* have previous unread data? */
+ memcpy (stream->ibuf + n,stream->iextraptr,stream->iextractr);
+ n += stream->iextractr; /* update number of bytes read */
+ stream->iextractr = 0; /* no more extra data */
+ }
+ else { /* read from TCP */
+ if (!tcp_getdata (stream->tcpstream)) return ssl_abort (stream);
+ /* maximum amount of data to copy */
+ if (!(i = min (stream->bufsize - n,stream->tcpstream->ictr)))
+ fatal ("incomplete SecBuffer exceeds maximum buffer size");
+ /* do the copy */
+ memcpy (stream->ibuf + n,stream->tcpstream->iptr,i);
+ stream->tcpstream->iptr += i;
+ stream->tcpstream->ictr -= i;
+ n += i; /* update number of bytes to decrypt */
+ }
+ buf[0].cbBuffer = n; /* first SecBuffer gets data */
+ buf[0].pvBuffer = stream->ibuf;
+ buf[0].BufferType = SECBUFFER_DATA;
+ /* subsequent ones are for spares */
+ buf[1].BufferType = buf[2].BufferType = buf[3].BufferType =
+ SECBUFFER_EMPTY;
+ msg.ulVersion = SECBUFFER_VERSION;
+ msg.cBuffers = 4; /* number of SecBuffers */
+ msg.pBuffers = buf; /* first SecBuffer */
+
+ } while ((status = ((DECRYPT_MESSAGE_FN) sft->Reserved4)
+ (&stream->context,&msg,0,NIL)) == SEC_E_INCOMPLETE_MESSAGE);
+ switch (status) {
+ case SEC_E_OK: /* won */
+ case SEC_I_RENEGOTIATE: /* won but lost it after this buffer */
+ /* hunt for a buffer */
+ for (i = 0; (i < 4) && (buf[i].BufferType != SECBUFFER_DATA) ; i++);
+ if (i < 4) { /* found a buffer? */
+ /* yes, set up pointer and counter */
+ stream->iptr = buf[i].pvBuffer;
+ stream->ictr = buf[i].cbBuffer;
+ /* any unprocessed data? */
+ while (++i < 4) if (buf[i].BufferType == SECBUFFER_EXTRA) {
+ /* yes, note for next time around */
+ stream->iextraptr = buf[i].pvBuffer;
+ stream->iextractr = buf[i].cbBuffer;
+ }
+ }
+ break;
+ default: /* anything else means we've lost */
+ return ssl_abort (stream);
+ }
+ }
+ return LONGT;
+}
+
+/* SSL send string as record
+ * Accepts: SSL stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long ssl_soutr (SSLSTREAM *stream,char *string)
+{
+ return ssl_sout (stream,string,(unsigned long) strlen (string));
+}
+
+
+/* SSL send string
+ * Accepts: SSL stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long ssl_sout (SSLSTREAM *stream,char *string,unsigned long size)
+{
+ SecBuffer buf[4];
+ SecBufferDesc msg;
+ char *s = stream->ibuf;
+ size_t n = 0;
+ while (size) { /* until satisfied request */
+ /* header */
+ buf[0].BufferType = SECBUFFER_STREAM_HEADER;
+ memset (buf[0].pvBuffer = stream->obuf,0,
+ buf[0].cbBuffer = stream->sizes.cbHeader);
+ /* message (up to maximum size) */
+ buf[1].BufferType = SECBUFFER_DATA;
+ memcpy (buf[1].pvBuffer = stream->obuf + stream->sizes.cbHeader,string,
+ buf[1].cbBuffer = min (size,SSLBUFLEN));
+ /* trailer */
+ buf[2].BufferType = SECBUFFER_STREAM_TRAILER;
+ memset (buf[2].pvBuffer = ((char *) buf[1].pvBuffer) + buf[1].cbBuffer,0,
+ buf[2].cbBuffer = stream->sizes.cbTrailer);
+ /* spare */
+ buf[3].BufferType = SECBUFFER_EMPTY;
+ msg.ulVersion = SECBUFFER_VERSION;
+ msg.cBuffers = 4; /* number of SecBuffers */
+ msg.pBuffers = buf; /* first SecBuffer */
+ string += buf[1].cbBuffer;
+ size -= buf[1].cbBuffer; /* this many bytes processed */
+ /* encrypt and send message */
+ if ((((ENCRYPT_MESSAGE_FN) sft->Reserved3)
+ (&stream->context,0,&msg,NIL) != SEC_E_OK) ||
+ !tcp_sout (stream->tcpstream,stream->obuf,
+ buf[0].cbBuffer + buf[1].cbBuffer + buf[2].cbBuffer))
+ return ssl_abort (stream);/* encryption or sending failed */
+ }
+ return LONGT;
+}
+
+/* SSL close
+ * Accepts: SSL stream
+ */
+
+void ssl_close (SSLSTREAM *stream)
+{
+ ssl_abort (stream); /* nuke the stream */
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+
+/* SSL abort stream
+ * Accepts: SSL stream
+ * Returns: NIL always
+ */
+
+static long ssl_abort (SSLSTREAM *stream)
+{
+ if (stream->tcpstream) { /* close TCP stream */
+ sft->DeleteSecurityContext (&stream->context);
+ sft->FreeCredentialHandle (&stream->cred);
+ tcp_close (stream->tcpstream);
+ stream->tcpstream = NIL;
+ }
+ if (stream->ibuf) fs_give ((void **) &stream->ibuf);
+ if (stream->obuf) fs_give ((void **) &stream->obuf);
+ return NIL;
+}
+
+/* SSL get host name
+ * Accepts: SSL stream
+ * Returns: host name for this stream
+ */
+
+char *ssl_host (SSLSTREAM *stream)
+{
+ return tcp_host (stream->tcpstream);
+}
+
+
+/* SSL get remote host name
+ * Accepts: SSL stream
+ * Returns: host name for this stream
+ */
+
+char *ssl_remotehost (SSLSTREAM *stream)
+{
+ return tcp_remotehost (stream->tcpstream);
+}
+
+
+/* SSL return port for this stream
+ * Accepts: SSL stream
+ * Returns: port number for this stream
+ */
+
+unsigned long ssl_port (SSLSTREAM *stream)
+{
+ return tcp_port (stream->tcpstream);
+}
+
+
+/* SSL get local host name
+ * Accepts: SSL stream
+ * Returns: local host name
+ */
+
+char *ssl_localhost (SSLSTREAM *stream)
+{
+ return tcp_localhost (stream->tcpstream);
+}
+
+#include "ssl_none.c" /* currently no server support */
diff --git a/imap/src/osdep/nt/ssl_w2k.c b/imap/src/osdep/nt/ssl_w2k.c
new file mode 100644
index 00000000..f5d8d1f5
--- /dev/null
+++ b/imap/src/osdep/nt/ssl_w2k.c
@@ -0,0 +1,683 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: SSL authentication/encryption module for Windows 2000
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 22 September 1998
+ * Last Edited: 13 January 2008
+ */
+
+#define SECURITY_WIN32
+#include <sspi.h>
+#include <schannel.h>
+
+
+#define SSLBUFLEN 8192
+
+
+/* SSL I/O stream */
+
+typedef struct ssl_stream {
+ TCPSTREAM *tcpstream; /* TCP stream */
+ CredHandle cred; /* SSL credentials */
+ CtxtHandle context; /* SSL context */
+ /* stream encryption sizes */
+ SecPkgContext_StreamSizes sizes;
+ size_t bufsize;
+ int ictr; /* input counter */
+ char *iptr; /* input pointer */
+ int iextractr; /* extra input counter */
+ char *iextraptr; /* extra input pointer */
+ char *ibuf; /* input buffer */
+ char *obuf; /* output buffer */
+} SSLSTREAM;
+
+#include "sslio.h"
+
+
+/* Function prototypes */
+
+static SSLSTREAM *ssl_start(TCPSTREAM *tstream,char *host,unsigned long flags);
+static char *ssl_analyze_status (SECURITY_STATUS err,char *buf);
+static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
+ long *contd);
+static long ssl_abort (SSLSTREAM *stream);
+
+/* Secure Sockets Layer network driver dispatch */
+
+static struct ssl_driver ssldriver = {
+ ssl_open, /* open connection */
+ ssl_aopen, /* open preauthenticated connection */
+ ssl_getline, /* get a line */
+ ssl_getbuffer, /* get a buffer */
+ ssl_soutr, /* output pushed data */
+ ssl_sout, /* output string */
+ ssl_close, /* close connection */
+ ssl_host, /* return host name */
+ ssl_remotehost, /* return remote host name */
+ ssl_port, /* return port number */
+ ssl_localhost /* return local host name */
+};
+
+static unsigned long ssltsz = 0;/* SSL maximum token length */
+
+/* One-time SSL initialization */
+
+static int sslonceonly = 0;
+
+void ssl_onceonlyinit (void)
+{
+ if (!sslonceonly++) { /* only need to call it once */
+ ULONG np;
+ SecPkgInfo *pp;
+ int i;
+ /* get security library */
+ if (!EnumerateSecurityPackages (&np,&pp)) {
+ /* look for an SSL package */
+ for (i = 0; (i < (int) np); i++) if (!strcmp (pp[i].Name,UNISP_NAME)) {
+ /* note maximum token size and name */
+ ssltsz = pp[i].cbMaxToken;
+ /* apply runtime linkage */
+ mail_parameters (NIL,SET_SSLDRIVER,(void *) &ssldriver);
+ mail_parameters (NIL,SET_SSLSTART,(void *) ssl_start);
+ return; /* all done */
+ }
+ }
+ }
+}
+
+/* SSL open
+ * Accepts: host name
+ * contact service name
+ * contact port number
+ * Returns: SSL stream if success else NIL
+ */
+
+SSLSTREAM *ssl_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream = tcp_open (host,service,port);
+ return stream ? ssl_start (stream,host,port) : NIL;
+}
+
+
+/* SSL authenticated open
+ * Accepts: host name
+ * service name
+ * returned user name buffer
+ * Returns: SSL stream if success else NIL
+ */
+
+SSLSTREAM *ssl_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL; /* don't use this mechanism with SSL */
+}
+
+/* Start SSL/TLS negotiations
+ * Accepts: open TCP stream of session
+ * user's host name
+ * flags
+ * Returns: SSL stream if success else NIL
+ */
+
+static SSLSTREAM *ssl_start (TCPSTREAM *tstream,char *host,unsigned long flags)
+{
+ SECURITY_STATUS e;
+ ULONG a;
+ TimeStamp t;
+ SecBuffer ibuf[2],obuf[1];
+ SecBufferDesc ibufs,obufs;
+ SCHANNEL_CRED tlscred;
+ CERT_CONTEXT *cert = NIL;
+ CERT_CHAIN_PARA chparam;
+ CERT_CHAIN_CONTEXT *chain;
+ SSL_EXTRA_CERT_CHAIN_POLICY_PARA policy;
+ CERT_CHAIN_POLICY_PARA polparam;
+ CERT_CHAIN_POLICY_STATUS status;
+ char tmp[MAILTMPLEN],certname[256];
+ char *reason = NIL;
+ ULONG req = ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT |
+ ISC_REQ_CONFIDENTIALITY | ISC_REQ_USE_SESSION_KEY |
+ ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM | ISC_REQ_EXTENDED_ERROR |
+ ISC_REQ_MANUAL_CRED_VALIDATION;
+ LPSTR usage[] = {
+ szOID_PKIX_KP_SERVER_AUTH,
+ szOID_SERVER_GATED_CRYPTO,
+ szOID_SGC_NETSCAPE
+ };
+ PWSTR whost = NIL;
+ char *buf = (char *) fs_get (ssltsz);
+ unsigned long size = 0;
+ sslcertificatequery_t scq =
+ (sslcertificatequery_t) mail_parameters (NIL,GET_SSLCERTIFICATEQUERY,NIL);
+ sslfailure_t sf = (sslfailure_t) mail_parameters (NIL,GET_SSLFAILURE,NIL);
+ SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
+ sizeof (SSLSTREAM));
+ stream->tcpstream = tstream; /* bind TCP stream */
+ /* initialize TLS credential */
+ memset (&tlscred,0,sizeof (SCHANNEL_CRED));
+ tlscred.dwVersion = SCHANNEL_CRED_VERSION;
+ tlscred.grbitEnabledProtocols = SP_PROT_TLS1;
+
+ /* acquire credentials */
+ if (AcquireCredentialsHandle
+ (NIL,UNISP_NAME,SECPKG_CRED_OUTBOUND,NIL,(flags & NET_TLSCLIENT) ?
+ &tlscred : NIL,NIL,NIL,&stream->cred,&t)
+ != SEC_E_OK) reason = "Acquire credentials handle failed";
+ else while (!reason) { /* negotiate security context */
+ /* initialize buffers */
+ ibuf[0].cbBuffer = size; ibuf[0].pvBuffer = buf;
+ ibuf[1].cbBuffer = 0; ibuf[1].pvBuffer = NIL;
+ obuf[0].cbBuffer = 0; obuf[0].pvBuffer = NIL;
+ ibuf[0].BufferType = obuf[0].BufferType = SECBUFFER_TOKEN;
+ ibuf[1].BufferType = SECBUFFER_EMPTY;
+ /* initialize buffer descriptors */
+ ibufs.ulVersion = obufs.ulVersion = SECBUFFER_VERSION;
+ ibufs.cBuffers = 2; obufs.cBuffers = 1;
+ ibufs.pBuffers = ibuf; obufs.pBuffers = obuf;
+ /* negotiate security */
+ e = InitializeSecurityContext
+ (&stream->cred,size ? &stream->context : NIL,host,req,0,
+ SECURITY_NETWORK_DREP,size? &ibufs:NIL,0,&stream->context,&obufs,&a,&t);
+ /* have an output buffer we need to send? */
+ if (obuf[0].pvBuffer && obuf[0].cbBuffer) {
+ if (!tcp_sout (stream->tcpstream,obuf[0].pvBuffer,obuf[0].cbBuffer))
+ reason = "Unexpected TCP output disconnect";
+ /* free the buffer */
+ FreeContextBuffer (obuf[0].pvBuffer);
+ }
+ if (!reason) switch (e) { /* negotiation state */
+ case SEC_I_INCOMPLETE_CREDENTIALS:
+ break; /* server wants client auth */
+ case SEC_I_CONTINUE_NEEDED:
+ if (size) { /* continue, read any data? */
+ /* yes, anything regurgiated back to us? */
+ if (ibuf[1].BufferType == SECBUFFER_EXTRA) {
+ /* yes, set this as the new data */
+ memmove (buf,buf + size - ibuf[1].cbBuffer,ibuf[1].cbBuffer);
+ size = ibuf[1].cbBuffer;
+ break;
+ }
+ size = 0; /* otherwise, read more stuff from server */
+ }
+ case SEC_E_INCOMPLETE_MESSAGE:
+ /* need to read more data from server */
+ if (!tcp_getdata (stream->tcpstream))
+ reason = "Unexpected TCP input disconnect";
+ else {
+ memcpy (buf+size,stream->tcpstream->iptr,stream->tcpstream->ictr);
+ size += stream->tcpstream->ictr;
+ /* empty it from TCP's buffers */
+ stream->tcpstream->iptr += stream->tcpstream->ictr;
+ stream->tcpstream->ictr = 0;
+ }
+ break;
+
+ case SEC_E_OK: /* success, any data to be regurgitated? */
+ if (ibuf[1].BufferType == SECBUFFER_EXTRA) {
+ /* yes, set this as the new data */
+ memmove (stream->tcpstream->iptr = stream->tcpstream->ibuf,
+ buf + size - ibuf[1].cbBuffer,ibuf[1].cbBuffer);
+ stream->tcpstream->ictr = ibuf[1].cbBuffer;
+ }
+ if (!(flags & NET_NOVALIDATECERT)) {
+ /* need validation, make wchar of host */
+ if (!((size = MultiByteToWideChar (CP_ACP,0,host,-1,NIL,0)) &&
+ (whost = (PWSTR) fs_get (size*sizeof (WCHAR))) &&
+ MultiByteToWideChar (CP_ACP,0,host,-1,whost,size)))
+ fatal ("Can't make wchar of host name!");
+ /* get certificate */
+ if ((QueryContextAttributes
+ (&stream->context,SECPKG_ATTR_REMOTE_CERT_CONTEXT,&cert) !=
+ SEC_E_OK) || !cert) {
+ reason = "*Unable to get certificate";
+ strcpy (certname,"<no certificate>");
+ }
+ else { /* get certificate subject name */
+ CertNameToStr (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ &cert->pCertInfo->Subject,CERT_X500_NAME_STR,
+ certname,255);
+ /* build certificate chain */
+ memset (&chparam,0,sizeof (chparam));
+ chparam.cbSize = sizeof (chparam);
+ chparam.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
+ chparam.RequestedUsage.Usage.rgpszUsageIdentifier = usage;
+ chparam.RequestedUsage.Usage.cUsageIdentifier =
+ sizeof (usage) / sizeof (LPSTR);
+ if (!CertGetCertificateChain
+ (NIL,cert,NIL,cert->hCertStore,&chparam,NIL,NIL,&chain))
+ reason = ssl_analyze_status (GetLastError (),tmp);
+ else { /* validate certificate chain */
+ memset (&policy,0,sizeof (SSL_EXTRA_CERT_CHAIN_POLICY_PARA));
+ policy.cbStruct = sizeof (SSL_EXTRA_CERT_CHAIN_POLICY_PARA);
+ policy.dwAuthType = AUTHTYPE_SERVER;
+ policy.fdwChecks = NIL;
+ policy.pwszServerName = whost;
+ memset (&polparam,0,sizeof (polparam));
+ polparam.cbSize = sizeof (polparam);
+ polparam.pvExtraPolicyPara = &policy;
+ memset (&status,0,sizeof (status));
+ status.cbSize = sizeof (status);
+ if (!CertVerifyCertificateChainPolicy
+ (CERT_CHAIN_POLICY_SSL,chain,&polparam,&status))
+ reason = ssl_analyze_status (GetLastError (),tmp);
+ else if (status.dwError)
+ reason = ssl_analyze_status (status.dwError,tmp);
+ CertFreeCertificateChain (chain);
+ }
+ CertFreeCertificateContext (cert);
+ }
+ if (whost) fs_give ((void **) &whost);
+
+ if (reason) { /* got an error? */
+ /* application callback */
+ if (scq) reason = (*scq) ((*reason == '*') ? reason + 1 : reason,
+ host,certname) ? NIL : "";
+ else if (*certname) { /* error message to return via mm_log() */
+ sprintf (buf,"*%.128s: %.255s",
+ (*reason == '*') ? reason + 1 : reason,certname);
+ reason = buf;
+ }
+ }
+ }
+ if (reason ||
+ (reason = ssl_analyze_status
+ (QueryContextAttributes
+ (&stream->context,SECPKG_ATTR_STREAM_SIZES,&stream->sizes),buf)))
+ break; /* error in certificate or getting sizes */
+ fs_give ((void **) &buf); /* flush temporary buffer */
+ /* make maximum-sized buffers */
+ stream->bufsize = stream->sizes.cbHeader +
+ stream->sizes.cbMaximumMessage + stream->sizes.cbTrailer;
+ if (stream->sizes.cbMaximumMessage < SSLBUFLEN)
+ fatal ("cbMaximumMessage is less than SSLBUFLEN!");
+ else if (stream->sizes.cbMaximumMessage < 16384) {
+ sprintf (tmp,"WINDOWS BUG: cbMaximumMessage = %ld, should be 16384",
+ (long) stream->sizes.cbMaximumMessage);
+ mm_log (tmp,NIL);
+ }
+ stream->ibuf = (char *) fs_get (stream->bufsize);
+ stream->obuf = (char *) fs_get (stream->bufsize);
+ return stream;
+ default:
+ reason = ssl_analyze_status (e,buf);
+ }
+ }
+ ssl_close (stream); /* failed to do SSL */
+ stream = NIL; /* no stream returned */
+ switch (*reason) { /* analyze reason */
+ case '*': /* certificate failure */
+ ++reason; /* skip over certificate failure indication */
+ /* pass to error callback */
+ if (sf) (*sf) (host,reason,flags);
+ else { /* no error callback, build error message */
+ sprintf (tmp,"Certificate failure for %.80s: %.512s",host,reason);
+ mm_log (tmp,ERROR);
+ }
+ case '\0': /* user answered no to certificate callback */
+ if (flags & NET_TRYSSL) /* return dummy stream to stop tryssl */
+ stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
+ sizeof (SSLSTREAM));
+ break;
+ default: /* non-certificate failure */
+ if (flags & NET_TRYSSL); /* no error output if tryssl */
+ /* pass to error callback */
+ else if (sf) (*sf) (host,reason,flags);
+ else { /* no error callback, build error message */
+ sprintf (tmp,"TLS/SSL failure for %.80s: %.512s",host,reason);
+ mm_log (tmp,ERROR);
+ }
+ break;
+ }
+ fs_give ((void **) &buf); /* flush temporary buffer */
+ return stream;
+}
+
+/* Generate error text from SSL error code
+ * Accepts: SSL status
+ * scratch buffer
+ * Returns: text if error status, else NIL
+ */
+
+static char *ssl_analyze_status (SECURITY_STATUS err,char *buf)
+{
+ switch (err) {
+ case SEC_E_OK: /* no error */
+ case SEC_I_CONTINUE_NEEDED:
+ case SEC_I_INCOMPLETE_CREDENTIALS:
+ case SEC_E_INCOMPLETE_MESSAGE:
+ return NIL;
+ case SEC_E_NO_AUTHENTICATING_AUTHORITY:
+ mm_log ("unexpected SEC_E_NO_AUTHENTICATING_AUTHORITY",NIL);
+ return "*No authority could be contacted for authentication";
+ case SEC_E_WRONG_PRINCIPAL:
+ mm_log ("unexpected SEC_E_WRONG_PRINCIPAL",NIL);
+ case CERT_E_CN_NO_MATCH:
+ return "*Server name does not match certificate";
+ case SEC_E_UNTRUSTED_ROOT:
+ mm_log ("unexpected SEC_E_UNTRUSTED_ROOT",NIL);
+ case CERT_E_UNTRUSTEDROOT:
+ return "*Self-signed certificate or untrusted authority";
+ case SEC_E_CERT_EXPIRED:
+ mm_log ("unexpected SEC_E_CERT_EXPIRED",NIL);
+ case CERT_E_EXPIRED:
+ return "*Certificate has expired";
+ case CERT_E_REVOKED:
+ return "*Certificate revoked";
+ case SEC_E_INVALID_TOKEN:
+ return "Invalid token, probably not an SSL server";
+ case SEC_E_UNSUPPORTED_FUNCTION:
+ return "SSL not supported on this machine - upgrade your system software";
+ }
+ sprintf (buf,"Unexpected SSPI or certificate error %lx - report this",err);
+ return buf;
+}
+
+/* SSL receive line
+ * Accepts: SSL stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *ssl_getline (SSLSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = ssl_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = ssl_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* SSL receive line or partial line
+ * Accepts: SSL stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!ssl_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!ssl_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* SSL receive buffer
+ * Accepts: SSL stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long ssl_getbuffer (SSLSTREAM *stream,unsigned long size,char *buffer)
+{
+ unsigned long n;
+ while (size > 0) { /* until request satisfied */
+ if (!ssl_getdata (stream)) return NIL;
+ n = min (size,stream->ictr);/* number of bytes to transfer */
+ /* do the copy */
+ memcpy (buffer,stream->iptr,n);
+ buffer += n; /* update pointer */
+ stream->iptr += n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -= n;
+ }
+ buffer[0] = '\0'; /* tie off string */
+ return T;
+}
+
+/* SSL receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long ssl_getdata (SSLSTREAM *stream)
+{
+ while (stream->ictr < 1) { /* decrypted buffer empty? */
+ SECURITY_STATUS status;
+ SecBuffer buf[4];
+ SecBufferDesc msg;
+ size_t i;
+ size_t n = 0; /* initially no bytes to decrypt */
+ do { /* yes, make sure have data from TCP */
+ if (stream->iextractr) { /* have previous unread data? */
+ memcpy (stream->ibuf + n,stream->iextraptr,stream->iextractr);
+ n += stream->iextractr; /* update number of bytes read */
+ stream->iextractr = 0; /* no more extra data */
+ }
+ else { /* read from TCP */
+ if (!tcp_getdata (stream->tcpstream)) return ssl_abort (stream);
+ /* maximum amount of data to copy */
+ if (!(i = min (stream->bufsize - n,stream->tcpstream->ictr)))
+ fatal ("incomplete SecBuffer exceeds maximum buffer size");
+ /* do the copy */
+ memcpy (stream->ibuf + n,stream->tcpstream->iptr,i);
+ stream->tcpstream->iptr += i;
+ stream->tcpstream->ictr -= i;
+ n += i; /* update number of bytes to decrypt */
+ }
+ buf[0].cbBuffer = n; /* first SecBuffer gets data */
+ buf[0].pvBuffer = stream->ibuf;
+ buf[0].BufferType = SECBUFFER_DATA;
+ /* subsequent ones are for spares */
+ buf[1].BufferType = buf[2].BufferType = buf[3].BufferType =
+ SECBUFFER_EMPTY;
+ msg.ulVersion = SECBUFFER_VERSION;
+ msg.cBuffers = 4; /* number of SecBuffers */
+ msg.pBuffers = buf; /* first SecBuffer */
+ } while ((status = DecryptMessage
+ (&stream->context,&msg,0,NIL)) == SEC_E_INCOMPLETE_MESSAGE);
+ switch (status) {
+ case SEC_E_OK: /* won */
+ case SEC_I_RENEGOTIATE: /* won but lost it after this buffer */
+ /* hunt for a buffer */
+ for (i = 0; (i < 4) && (buf[i].BufferType != SECBUFFER_DATA) ; i++);
+ if (i < 4) { /* found a buffer? */
+ /* yes, set up pointer and counter */
+ stream->iptr = buf[i].pvBuffer;
+ stream->ictr = buf[i].cbBuffer;
+ /* any unprocessed data? */
+ while (++i < 4) if (buf[i].BufferType == SECBUFFER_EXTRA) {
+ /* yes, note for next time around */
+ stream->iextraptr = buf[i].pvBuffer;
+ stream->iextractr = buf[i].cbBuffer;
+ }
+ }
+ break;
+ default: /* anything else means we've lost */
+ return ssl_abort (stream);
+ }
+ }
+ return LONGT;
+}
+
+/* SSL send string as record
+ * Accepts: SSL stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long ssl_soutr (SSLSTREAM *stream,char *string)
+{
+ return ssl_sout (stream,string,(unsigned long) strlen (string));
+}
+
+
+/* SSL send string
+ * Accepts: SSL stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long ssl_sout (SSLSTREAM *stream,char *string,unsigned long size)
+{
+ SecBuffer buf[4];
+ SecBufferDesc msg;
+ char *s;
+ size_t n;
+ if (!stream->tcpstream) return NIL;
+ /* until request satisfied */
+ for (s = stream->ibuf,n = 0; size;) {
+ /* header */
+ buf[0].BufferType = SECBUFFER_STREAM_HEADER;
+ memset (buf[0].pvBuffer = stream->obuf,0,
+ buf[0].cbBuffer = stream->sizes.cbHeader);
+ /* message (up to maximum size) */
+ buf[1].BufferType = SECBUFFER_DATA;
+ memcpy (buf[1].pvBuffer = stream->obuf + stream->sizes.cbHeader,string,
+ buf[1].cbBuffer = min (size,SSLBUFLEN));
+ /* trailer */
+ buf[2].BufferType = SECBUFFER_STREAM_TRAILER;
+ memset (buf[2].pvBuffer = ((char *) buf[1].pvBuffer) + buf[1].cbBuffer,0,
+ buf[2].cbBuffer = stream->sizes.cbTrailer);
+ /* spare */
+ buf[3].BufferType = SECBUFFER_EMPTY;
+ msg.ulVersion = SECBUFFER_VERSION;
+ msg.cBuffers = 4; /* number of SecBuffers */
+ msg.pBuffers = buf; /* first SecBuffer */
+ string += buf[1].cbBuffer;
+ size -= buf[1].cbBuffer; /* this many bytes processed */
+ /* encrypt and send message */
+ if ((EncryptMessage
+ (&stream->context,0,&msg,NIL) != SEC_E_OK) ||
+ !tcp_sout (stream->tcpstream,stream->obuf,
+ buf[0].cbBuffer + buf[1].cbBuffer + buf[2].cbBuffer))
+ return ssl_abort (stream);/* encryption or sending failed */
+ }
+ return LONGT;
+}
+
+/* SSL close
+ * Accepts: SSL stream
+ */
+
+void ssl_close (SSLSTREAM *stream)
+{
+ ssl_abort (stream); /* nuke the stream */
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+
+/* SSL abort stream
+ * Accepts: SSL stream
+ * Returns: NIL always
+ */
+
+static long ssl_abort (SSLSTREAM *stream)
+{
+ if (stream->tcpstream) { /* close TCP stream */
+ DeleteSecurityContext (&stream->context);
+ FreeCredentialHandle (&stream->cred);
+ tcp_close (stream->tcpstream);
+ stream->tcpstream = NIL;
+ }
+ if (stream->ibuf) fs_give ((void **) &stream->ibuf);
+ if (stream->obuf) fs_give ((void **) &stream->obuf);
+ return NIL;
+}
+
+/* SSL get host name
+ * Accepts: SSL stream
+ * Returns: host name for this stream
+ */
+
+char *ssl_host (SSLSTREAM *stream)
+{
+ return tcp_host (stream->tcpstream);
+}
+
+
+/* SSL get remote host name
+ * Accepts: SSL stream
+ * Returns: host name for this stream
+ */
+
+char *ssl_remotehost (SSLSTREAM *stream)
+{
+ return tcp_remotehost (stream->tcpstream);
+}
+
+
+/* SSL return port for this stream
+ * Accepts: SSL stream
+ * Returns: port number for this stream
+ */
+
+unsigned long ssl_port (SSLSTREAM *stream)
+{
+ return tcp_port (stream->tcpstream);
+}
+
+
+/* SSL get local host name
+ * Accepts: SSL stream
+ * Returns: local host name
+ */
+
+char *ssl_localhost (SSLSTREAM *stream)
+{
+ return tcp_localhost (stream->tcpstream);
+}
+
+#include "ssl_none.c" /* currently no server support */
diff --git a/imap/src/osdep/nt/tcp_nt.c b/imap/src/osdep/nt/tcp_nt.c
new file mode 100644
index 00000000..a6d735dc
--- /dev/null
+++ b/imap/src/osdep/nt/tcp_nt.c
@@ -0,0 +1,916 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Winsock TCP/IP routines
+ *
+ * Author: Mark Crispin from Mike Seibel's Winsock code
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 13 January 2007
+ */
+
+#include "ip_nt.c"
+
+
+#define TCPMAXSEND 32768
+
+/* Private functions */
+
+int tcp_socket_open (int family,void *adr,size_t adrlen,unsigned short port,
+ char *tmp,char *hst);
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd);
+long tcp_abort (TCPSTREAM *stream);
+long tcp_close_socket (SOCKET *sock);
+char *tcp_name (struct sockaddr *sadr,long flag);
+char *tcp_name_valid (char *s);
+
+
+/* Private data */
+
+int wsa_initted = 0; /* init ? */
+static int wsa_sock_open = 0; /* keep track of open sockets */
+static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */
+static long ttmo_open = 0; /* TCP timeouts, in seconds */
+static long ttmo_read = 0;
+static long ttmo_write = 0;
+static long allowreversedns = T;/* allow reverse DNS lookup */
+static long tcpdebug = NIL; /* extra TCP debugging telemetry */
+static char *myClientAddr = NIL;/* client host address */
+static char *myClientHost = NIL;/* client host name */
+static long myClientPort = -1; /* client port */
+static char *myServerAddr = NIL;/* server host address */
+static char *myServerHost = NIL;/* server host name */
+static long myServerPort = -1; /* server port */
+
+extern long maxposint; /* get this from write.c */
+
+/* TCP/IP manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tcp_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_TIMEOUT:
+ tmoh = (tcptimeout_t) value;
+ case GET_TIMEOUT:
+ ret = (void *) tmoh;
+ break;
+ case SET_OPENTIMEOUT:
+ ttmo_open = (long) value ? (long) value : (long) WSA_INFINITE;
+ case GET_OPENTIMEOUT:
+ ret = (void *) ttmo_open;
+ break;
+ case SET_READTIMEOUT:
+ ttmo_read = (long) value;
+ case GET_READTIMEOUT:
+ ret = (void *) ttmo_read;
+ break;
+ case SET_WRITETIMEOUT:
+ ttmo_write = (long) value;
+ case GET_WRITETIMEOUT:
+ ret = (void *) ttmo_write;
+ break;
+ case SET_ALLOWREVERSEDNS:
+ allowreversedns = (long) value;
+ case GET_ALLOWREVERSEDNS:
+ ret = (void *) allowreversedns;
+ break;
+ case SET_TCPDEBUG:
+ tcpdebug = (long) value;
+ case GET_TCPDEBUG:
+ ret = (void *) tcpdebug;
+ break;
+ }
+ return ret;
+}
+
+/* TCP/IP open
+ * Accepts: host name
+ * contact service name
+ * contact port number and optional silent flag
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream = NIL;
+ int i,family;
+ SOCKET sock = INVALID_SOCKET;
+ int silent = (port & NET_SILENT) ? T : NIL;
+ char *s,*hostname,tmp[MAILTMPLEN];
+ void *adr,*next;
+ size_t adrlen;
+ struct servent *sv = NIL;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (!wsa_initted++) { /* init Windows Sockets */
+ WSADATA wsock;
+ if (i = (int) WSAStartup (WINSOCK_VERSION,&wsock)) {
+ wsa_initted = 0; /* in case we try again */
+ sprintf (tmp,"Unable to start Windows Sockets (%d)",i);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ }
+ port &= 0xffff; /* erase flags */
+ /* lookup service */
+ if (service && (sv = getservbyname (service,"tcp")))
+ port = ntohs (sv->s_port);
+ /* The domain literal form is used (rather than simply the dotted decimal
+ as with other Windows programs) because it has to be a valid "host name"
+ in mailsystem terminology. */
+ /* look like domain literal? */
+ if (host[0] == '[' && host[(strlen (host))-1] == ']') {
+ strcpy (tmp,host+1); /* yes, copy number part */
+ tmp[strlen (tmp)-1] = '\0';
+ if (adr = ip_stringtoaddr (tmp,&adrlen,&family)) {
+ (*bn) (BLOCK_TCPOPEN,NIL);
+ sock = tcp_socket_open (family,adr,adrlen,(unsigned short) port,tmp,
+ hostname = host);
+ (*bn) (BLOCK_NONE,NIL);
+ fs_give ((void **) &adr);
+ }
+ else sprintf (tmp,"Bad format domain-literal: %.80s",host);
+ }
+
+ else { /* lookup host name */
+ if (tcpdebug) {
+ sprintf (tmp,"DNS resolution %.80s",host);
+ mm_log (tmp,TCPDEBUG);
+ }
+ (*bn) (BLOCK_DNSLOOKUP,NIL);/* look up name */
+ if (!(s = ip_nametoaddr (host,&adrlen,&family,&hostname,&next)))
+ sprintf (tmp,"Host not found (#%d): %s",WSAGetLastError (),host);
+ (*bn) (BLOCK_NONE,NIL);
+ if (s) { /* DNS resolution won? */
+ if (tcpdebug) mm_log ("DNS resolution done",TCPDEBUG);
+ wsa_sock_open++; /* prevent tcp_close_socket() from freeing in
+ loop */
+ do {
+ (*bn) (BLOCK_TCPOPEN,NIL);
+ if (((sock = tcp_socket_open (family,s,adrlen,(unsigned short) port,
+ tmp,hostname)) == INVALID_SOCKET) &&
+ (s = ip_nametoaddr (NIL,&adrlen,&family,&hostname,&next)) &&
+ !silent) mm_log (tmp,WARN);
+ (*bn) (BLOCK_NONE,NIL);
+ } while ((sock == INVALID_SOCKET) && s);
+ wsa_sock_open--; /* undo protection */
+ }
+ }
+ if (sock == INVALID_SOCKET) { /* do possible cleanup action */
+ if (!silent) mm_log (tmp,ERROR);
+ tcp_close_socket (&sock);
+ }
+ else { /* got a socket, create TCP/IP stream */
+ stream = (TCPSTREAM *) memset (fs_get (sizeof (TCPSTREAM)),0,
+ sizeof (TCPSTREAM));
+ stream->port = port; /* port number */
+ /* init socket */
+ stream->tcpsi = stream->tcpso = sock;
+ stream->ictr = 0; /* init input counter */
+ /* copy official host name */
+ stream->host = cpystr (hostname);
+ if (tcpdebug) mm_log ("Stream open and ready for read",TCPDEBUG);
+ }
+ return stream; /* return success */
+}
+
+/* Open a TCP socket
+ * Accepts: protocol family
+ * address to connect to
+ * address length
+ * port
+ * scratch buffer
+ * host name
+ * Returns: socket if success, else SOCKET_ERROR with error string in scratch
+ */
+
+int tcp_socket_open (int family,void *adr,size_t adrlen,unsigned short port,
+ char *tmp,char *hst)
+{
+ int sock,err;
+ char *s,errmsg[100];
+ size_t len;
+ DWORD eo;
+ WSAEVENT event;
+ WSANETWORKEVENTS events;
+ unsigned long cmd = 0;
+ struct protoent *pt = getprotobyname ("tcp");
+ struct sockaddr *sadr = ip_sockaddr (family,adr,adrlen,port,&len);
+ sprintf (tmp,"Trying IP address [%s]",ip_sockaddrtostring (sadr));
+ mm_log (tmp,NIL);
+ /* get a TCP stream */
+ if ((sock = socket (sadr->sa_family,SOCK_STREAM,pt ? pt->p_proto : 0)) ==
+ INVALID_SOCKET)
+ sprintf (tmp,"Unable to create TCP socket (%d)",WSAGetLastError ());
+ else {
+ /* On Windows, FD_SETSIZE is the number of descriptors which can be
+ * held in an fd_set, as opposed to the maximum descriptor value on UNIX.
+ * Similarly, an fd_set in Windows is a vector of descriptor values, as
+ * opposed to a bitmask of set fds on UNIX. Thus, an fd_set can hold up
+ * to FD_SETSIZE values which can be larger than FD_SETSIZE, and the test
+ * that is used on UNIX is unnecessary here.
+ */
+ wsa_sock_open++; /* count this socket as open */
+ /* set socket nonblocking */
+ if (ttmo_open) WSAEventSelect (sock,event = WSACreateEvent (),FD_CONNECT);
+ else event = 0; /* no event */
+ /* open connection */
+ err = (connect (sock,sadr,len) == SOCKET_ERROR) ? WSAGetLastError () : NIL;
+ /* if timer in effect, wait for event */
+ if (event) while (err == WSAEWOULDBLOCK)
+ switch (eo = WSAWaitForMultipleEvents (1,&event,T,ttmo_open*1000,NIL)) {
+ case WSA_WAIT_EVENT_0: /* got an event? */
+ err = (WSAEnumNetworkEvents (sock,event,&events) == SOCKET_ERROR) ?
+ WSAGetLastError () : events.iErrorCode[FD_CONNECT_BIT];
+ break;
+ case WSA_WAIT_IO_COMPLETION:
+ break; /* fAlertable is NIL so shouldn't happen */
+ default: /* all other conditions */
+ err = eo; /* error from WSAWaitForMultipleEvents() */
+ break;
+ }
+
+ switch (err) { /* analyze result from connect and wait */
+ case 0: /* got a connection */
+ s = NIL;
+ if (event) { /* unset blocking mode */
+ WSAEventSelect (sock,event,NIL);
+ if (ioctlsocket (sock,FIONBIO,&cmd) == SOCKET_ERROR)
+ sprintf (s = errmsg,"Can't set blocking mode (%d)",
+ WSAGetLastError ());
+ }
+ break;
+ case WSAECONNREFUSED:
+ s = "Refused";
+ break;
+ case WSAENOBUFS:
+ s = "Insufficient system resources";
+ break;
+ case WSA_WAIT_TIMEOUT: case WSAETIMEDOUT:
+ s = "Timed out";
+ break;
+ case WSAEHOSTUNREACH:
+ s = "Host unreachable";
+ break;
+ default: /* horrible error 69 */
+ sprintf (s = errmsg,"Unknown error (%d)",err);
+ break;
+ }
+ /* flush event */
+ if (event) WSACloseEvent (event);
+ if (s) { /* got an error? */
+ sprintf (tmp,"Can't connect to %.80s,%ld: %.80s",hst,port,s);
+ tcp_close_socket (&sock); /* flush socket */
+ sock = INVALID_SOCKET;
+ }
+ }
+ fs_give ((void **) &sadr); /* and socket address */
+ return sock; /* return the socket */
+}
+
+/* TCP/IP authenticated open
+ * Accepts: NETMBX specifier
+ * service name
+ * returned user name buffer
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL; /* always NIL on Windows */
+}
+
+/* TCP receive line
+ * Accepts: TCP stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *tcp_getline (TCPSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = tcp_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = tcp_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* TCP receive line or partial line
+ * Accepts: TCP stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!tcp_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!tcp_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* TCP/IP receive buffer
+ * Accepts: TCP/IP stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *s)
+{
+ unsigned long n;
+ /* make sure socket still alive */
+ if (stream->tcpsi == INVALID_SOCKET) return NIL;
+ /* can transfer bytes from buffer? */
+ if (n = min (size,stream->ictr)) {
+ memcpy (s,stream->iptr,n); /* yes, slurp as much as we can from it */
+ s += n; /* update pointer */
+ stream->iptr +=n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -=n;
+ }
+ if (size) {
+ int i;
+ fd_set fds,efds;
+ struct timeval tmo;
+ time_t t = time (0);
+ blocknotify_t bn=(blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ (*bn) (BLOCK_TCPREAD,NIL);
+ while (size > 0) { /* until request satisfied */
+ if (tcpdebug) mm_log ("Reading TCP buffer",TCPDEBUG);
+ /* simple case if not a socket */
+ if (stream->tcpsi != stream->tcpso)
+ while (((i = read (stream->tcpsi,s,(int) min (maxposint,size))) < 0) &&
+ (errno == EINTR));
+ else { /* socket case */
+ time_t tl = time (0);
+ time_t now = tl;
+ time_t ti = ttmo_read ? now + ttmo_read : 0;
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ /* set bit in selection vectors */
+ FD_SET (stream->tcpsi,&fds);
+ FD_SET (stream->tcpsi,&efds);
+ errno = NIL; /* initially no error */
+ do { /* block under timeout */
+ tmo.tv_sec = (long) (ti ? ti - now : 0);
+ i = select (stream->tcpsi+1,&fds,NIL,&efds,ti ? &tmo : NIL);
+ now = time (0); /* fake timeout if interrupt & time expired */
+ if ((i < 0) && ((errno = WSAGetLastError ()) == WSAEINTR) && ti &&
+ (ti <= now)) i = 0;
+ } while ((i < 0) && (errno == WSAEINTR));
+ /* success from select, read what we can */
+ if (i > 0) while (((i = recv (stream->tcpsi,s,
+ (int) min (maxposint,size),0)) ==
+ SOCKET_ERROR) &&
+ ((errno = WSAGetLastError ()) == WSAEINTR));
+ else if (!i) { /* timeout, ignore if told to resume */
+ if (tmoh && (*tmoh) ((long) (now - t),(long) (now - tl),
+ stream->host))
+ continue;
+ /* otherwise punt */
+ if (tcpdebug) mm_log ("TCP buffer read timeout",TCPDEBUG);
+ return tcp_abort (stream);
+ }
+ }
+ if (i <= 0) { /* error seen? */
+ if (tcpdebug) {
+ char tmp[MAILTMPLEN];
+ if (i) sprintf (s = tmp,"TCP buffer read I/O error %d",errno);
+ else s = "TCP buffer read end of file";
+ mm_log (s,TCPDEBUG);
+ }
+ return tcp_abort (stream);
+ }
+ s += i; /* point at new place to write */
+ size -= i; /* reduce byte count */
+ if (tcpdebug) mm_log ("Successfully read TCP buffer",TCPDEBUG);
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ }
+ *s = '\0'; /* tie off string */
+ return LONGT;
+}
+
+/* TCP/IP receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getdata (TCPSTREAM *stream)
+{
+ int i;
+ fd_set fds,efds;
+ struct timeval tmo;
+ time_t t = time (0);
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (stream->tcpsi == INVALID_SOCKET) return NIL;
+ (*bn) (BLOCK_TCPREAD,NIL);
+ while (stream->ictr < 1) { /* if nothing in the buffer */
+ if (tcpdebug) mm_log ("Reading TCP data",TCPDEBUG);
+ /* simple case if not a socket */
+ if (stream->tcpsi != stream->tcpso)
+ while (((i = read (stream->tcpsi,stream->ibuf,BUFLEN)) < 0) &&
+ (errno == EINTR));
+ else {
+ time_t tl = time (0);
+ time_t now = tl;
+ time_t ti = ttmo_read ? now + ttmo_read : 0;
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ /* set bit in selection vectors */
+ FD_SET (stream->tcpsi,&fds);
+ FD_SET (stream->tcpsi,&efds);
+ errno = NIL; /* initially no error */
+ do { /* block under timeout */
+ tmo.tv_sec = (long) (ti ? ti - now : 0);
+ i = select (stream->tcpsi+1,&fds,NIL,&efds,ti ? &tmo : NIL);
+ now = time (0); /* fake timeout if interrupt & time expired */
+ if ((i < 0) && ((errno = WSAGetLastError ()) == WSAEINTR) && ti &&
+ (ti <= now)) i = 0;
+ } while ((i < 0) && (errno == WSAEINTR));
+ /* success from select, read what we can */
+ if (i > 0) while (((i = recv (stream->tcpsi,stream->ibuf,BUFLEN,0)) ==
+ SOCKET_ERROR) &&
+ ((errno = WSAGetLastError ()) == WSAEINTR));
+ else if (!i) { /* timeout, ignore if told to resume */
+ if (tmoh && (*tmoh) ((long) (now - t),(long) (now - tl), stream->host))
+ continue;
+ /* otherwise punt */
+ if (tcpdebug) mm_log ("TCP data read timeout",TCPDEBUG);
+ return tcp_abort (stream);
+ }
+ }
+ if (i <= 0) { /* error seen? */
+ if (tcpdebug) {
+ char *s,tmp[MAILTMPLEN];
+ if (i) sprintf (s = tmp,"TCP data read I/O error %d",errno);
+ else s = "TCP data read end of file";
+ mm_log (tmp,TCPDEBUG);
+ }
+ return tcp_abort (stream);
+ }
+ stream->iptr = stream->ibuf;/* point at TCP buffer */
+ stream->ictr = i; /* set new byte count */
+ if (tcpdebug) mm_log ("Successfully read TCP data",TCPDEBUG);
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return T;
+}
+
+/* TCP/IP send string as record
+ * Accepts: TCP/IP stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long tcp_soutr (TCPSTREAM *stream,char *string)
+{
+ return tcp_sout (stream,string,(unsigned long) strlen (string));
+}
+
+
+/* TCP/IP send string
+ * Accepts: TCP/IP stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
+{
+ int i;
+ struct timeval tmo;
+ fd_set fds,efds;
+ time_t t = time (0);
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ tmo.tv_sec = ttmo_write;
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ if (stream->tcpso == INVALID_SOCKET) return NIL;
+ (*bn) (BLOCK_TCPWRITE,NIL);
+ while (size > 0) { /* until request satisfied */
+ if (tcpdebug) mm_log ("Writing to TCP",TCPDEBUG);
+ /* simple case if not a socket */
+ if (stream->tcpsi != stream->tcpso)
+ while (((i = write (stream->tcpso,string,min (size,TCPMAXSEND))) < 0) &&
+ (errno == EINTR));
+ else {
+ time_t tl = time (0); /* start of request */
+ time_t now = tl;
+ time_t ti = ttmo_write ? now + ttmo_write : 0;
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ /* set bit in selection vectors */
+ FD_SET (stream->tcpso,&fds);
+ FD_SET(stream->tcpso,&efds);
+ errno = NIL; /* block and write */
+ do { /* block under timeout */
+ tmo.tv_sec = (long) (ti ? ti - now : 0);
+ i = select (stream->tcpso+1,NIL,&fds,&efds,ti ? &tmo : NIL);
+ now = time (0); /* fake timeout if interrupt & time expired */
+ if ((i < 0) && ((errno = WSAGetLastError ()) == WSAEINTR) && ti &&
+ (ti <= now)) i = 0;
+ } while ((i < 0) && (errno == WSAEINTR));
+ /* OK to send data? */
+ if (i > 0) while (((i = send (stream->tcpso,string,
+ (int) min (size,TCPMAXSEND),0)) ==
+ SOCKET_ERROR) &&
+ ((errno = WSAGetLastError ()) == WSAEINTR));
+ else if (!i) { /* timeout, ignore if told to resume */
+ if (tmoh && (*tmoh) ((long) (now - t),(long) (now - tl), stream->host))
+ continue;
+ /* otherwise punt */
+ if (tcpdebug) mm_log ("TCP write timeout",TCPDEBUG);
+ return tcp_abort (stream);
+ }
+ }
+ if (i <= 0) { /* error seen? */
+ if (tcpdebug) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"TCP write I/O error %d",errno);
+ mm_log (tmp,TCPDEBUG);
+ }
+ return tcp_abort (stream);
+ }
+ string += i; /* how much we sent */
+ size -= i; /* count this size */
+ if (tcpdebug) mm_log ("successfully wrote to TCP",TCPDEBUG);
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return T; /* all done */
+}
+
+/* TCP/IP close
+ * Accepts: TCP/IP stream
+ */
+
+void tcp_close (TCPSTREAM *stream)
+{
+ tcp_abort (stream); /* nuke the sockets */
+ /* flush host names */
+ if (stream->host) fs_give ((void **) &stream->host);
+ if (stream->remotehost) fs_give ((void **) &stream->remotehost);
+ if (stream->localhost) fs_give ((void **) &stream->localhost);
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+
+/* TCP/IP abort sockets
+ * Accepts: TCP/IP stream
+ * Returns: NIL, always
+ */
+
+long tcp_abort (TCPSTREAM *stream)
+{
+ if (stream->tcpsi != stream->tcpso) tcp_close_socket (&stream->tcpso);
+ else stream->tcpso = INVALID_SOCKET;
+ return tcp_close_socket (&stream->tcpsi);
+}
+
+
+/* TCP/IP abort stream
+ * Accepts: WinSock socket
+ * Returns: NIL, always
+ */
+
+long tcp_close_socket (SOCKET *sock)
+{
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ /* something to close? */
+ if (sock && (*sock != INVALID_SOCKET)) {
+ (*bn) (BLOCK_TCPCLOSE,NIL);
+ closesocket (*sock); /* WinSock socket close */
+ *sock = INVALID_SOCKET;
+ (*bn) (BLOCK_NONE,NIL);
+ wsa_sock_open--; /* drop this socket */
+ }
+ /* no more open streams? */
+ if (wsa_initted && !wsa_sock_open) {
+ mm_log ("Winsock cleanup",NIL);
+ wsa_initted = 0; /* no more sockets, so... */
+ WSACleanup (); /* free up resources until needed */
+ }
+ return NIL;
+}
+
+/* TCP/IP get host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_host (TCPSTREAM *stream)
+{
+ return stream->host; /* use tcp_remotehost() if want guarantees */
+}
+
+
+/* TCP/IP get remote host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_remotehost (TCPSTREAM *stream)
+{
+ if (!stream->remotehost) {
+ size_t sadrlen;
+ struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
+ stream->remotehost = /* get socket's peer name */
+ ((getpeername (stream->tcpsi,sadr,&sadrlen) == SOCKET_ERROR) ||
+ (sadrlen <= 0)) ? cpystr (stream->host) : tcp_name (sadr,NIL);
+ fs_give ((void **) &sadr);
+ }
+ return stream->remotehost;
+}
+
+
+/* TCP/IP return port for this stream
+ * Accepts: TCP/IP stream
+ * Returns: port number for this stream
+ */
+
+unsigned long tcp_port (TCPSTREAM *stream)
+{
+ return stream->port; /* return port number */
+}
+
+
+/* TCP/IP get local host name
+ * Accepts: TCP/IP stream
+ * Returns: local host name
+ */
+
+char *tcp_localhost (TCPSTREAM *stream)
+{
+ if (!stream->localhost) {
+ size_t sadrlen;
+ struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
+ stream->localhost = /* get socket's name */
+ ((stream->port & 0xffff000) ||
+ ((getsockname (stream->tcpsi,sadr,&sadrlen) == SOCKET_ERROR) ||
+ (sadrlen <= 0))) ? cpystr (mylocalhost ()) : tcp_name (sadr,NIL);
+ fs_give ((void **) &sadr);
+ }
+ return stream->localhost; /* return local host name */
+}
+
+/* TCP/IP get client host address (server calls only)
+ * Returns: client host address
+ */
+
+char *tcp_clientaddr ()
+{
+ if (!myClientAddr) {
+ size_t sadrlen;
+ struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
+ if ((getpeername (0,sadr,(void *) &sadrlen) == SOCKET_ERROR) ||
+ (sadrlen <= 0)) myClientAddr = cpystr ("UNKNOWN");
+ else { /* get stdin's peer name */
+ myClientAddr = cpystr (ip_sockaddrtostring (sadr));
+ if (myClientPort < 0) myClientPort = ip_sockaddrtoport (sadr);
+ }
+ fs_give ((void **) &sadr);
+ }
+ return myClientAddr;
+}
+
+
+/* TCP/IP get client host name (server calls only)
+ * Returns: client host name
+ */
+
+char *tcp_clienthost ()
+{
+ if (!myClientHost) {
+ size_t sadrlen;
+ struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
+ if ((getpeername (0,sadr,(void *) &sadrlen) == SOCKET_ERROR) ||
+ (sadrlen <= 0)) myClientHost = cpystr ("UNKNOWN");
+ else { /* get stdin's peer name */
+ myClientHost = tcp_name (sadr,T);
+ if (!myClientAddr) myClientAddr = cpystr (ip_sockaddrtostring (sadr));
+ if (myClientPort < 0) myClientPort = ip_sockaddrtoport (sadr);
+ }
+ fs_give ((void **) &sadr);
+ }
+ return myClientHost;
+}
+
+
+/* TCP/IP get client port number (server calls only)
+ * Returns: client port number
+ */
+
+long tcp_clientport ()
+{
+ if (!myClientHost && !myClientAddr) tcp_clientaddr ();
+ return myClientPort;
+}
+
+/* TCP/IP get server host address (server calls only)
+ * Returns: server host address
+ */
+
+char *tcp_serveraddr ()
+{
+ if (!myServerAddr) {
+ size_t sadrlen;
+ struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
+ if (!wsa_initted++) { /* init Windows Sockets */
+ WSADATA wsock;
+ if (WSAStartup (WINSOCK_VERSION,&wsock)) {
+ wsa_initted = 0;
+ return "random-pc"; /* try again later? */
+ }
+ }
+ if ((getsockname (0,sadr,(void *) &sadrlen) == SOCKET_ERROR) ||
+ (sadrlen <= 0)) myServerAddr = cpystr ("UNKNOWN");
+ else { /* get stdin's name */
+ myServerAddr = cpystr (ip_sockaddrtostring (sadr));
+ if (myServerPort < 0) myServerPort = ip_sockaddrtoport (sadr);
+ }
+ fs_give ((void **) &sadr);
+ }
+ return myServerAddr;
+}
+
+
+/* TCP/IP get server host name (server calls only)
+ * Returns: server host name
+ */
+
+char *tcp_serverhost ()
+{
+ if (!myServerHost) { /* once-only */
+ size_t sadrlen;
+ struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
+ if (!wsa_initted++) { /* init Windows Sockets */
+ WSADATA wsock;
+ if (WSAStartup (WINSOCK_VERSION,&wsock)) {
+ wsa_initted = 0;
+ return "random-pc"; /* try again later? */
+ }
+ }
+ /* get stdin's name */
+ if ((getsockname (0,sadr,(void *) &sadrlen) == SOCKET_ERROR) ||
+ (sadrlen <= 0)) myServerHost = cpystr (mylocalhost ());
+ else { /* get stdin's name */
+ myServerHost = tcp_name (sadr,NIL);
+ if (!myServerAddr) myServerAddr = cpystr (ip_sockaddrtostring (sadr));
+ if (myServerPort < 0) myServerPort = ip_sockaddrtoport (sadr);
+ }
+ fs_give ((void **) &sadr);
+ }
+ return myServerHost;
+}
+
+
+/* TCP/IP get server port number (server calls only)
+ * Returns: server port number
+ */
+
+long tcp_serverport ()
+{
+ if (!myServerHost && !myServerAddr) tcp_serveraddr ();
+ return myServerPort;
+}
+
+/* TCP/IP return canonical form of host name
+ * Accepts: host name
+ * Returns: canonical form of host name
+ */
+
+char *tcp_canonical (char *name)
+{
+ char *ret,host[MAILTMPLEN];
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ /* look like domain literal? */
+ if (name[0] == '[' && name[strlen (name) - 1] == ']') return name;
+ (*bn) (BLOCK_DNSLOOKUP,NIL);
+ if (tcpdebug) {
+ sprintf (host,"DNS canonicalization %.80s",name);
+ mm_log (host,TCPDEBUG);
+ }
+ /* get canonical name */
+ if (!ip_nametoaddr (name,NIL,NIL,&ret,NIL)) ret = name;
+ (*bn) (BLOCK_NONE,NIL); /* alarms OK now */
+ if (tcpdebug) mm_log ("DNS canonicalization done",TCPDEBUG);
+ return ret;
+}
+
+
+/* TCP/IP return name from socket
+ * Accepts: socket
+ * verbose flag
+ * Returns: cpystr name
+ */
+
+char *tcp_name (struct sockaddr *sadr,long flag)
+{
+ char *ret,*t,adr[MAILTMPLEN],tmp[MAILTMPLEN];
+ sprintf (ret = adr,"[%.80s]",ip_sockaddrtostring (sadr));
+ if (allowreversedns) {
+ blocknotify_t bn = (blocknotify_t)mail_parameters(NIL,GET_BLOCKNOTIFY,NIL);
+ if (tcpdebug) {
+ sprintf (tmp,"Reverse DNS resolution %s",adr);
+ mm_log (tmp,TCPDEBUG);
+ }
+ (*bn) (BLOCK_DNSLOOKUP,NIL);/* quell alarms */
+ /* translate address to name */
+ if (t = tcp_name_valid (ip_sockaddrtoname (sadr))) {
+ /* produce verbose form if needed */
+ if (flag) sprintf (ret = tmp,"%s %s",t,adr);
+ else ret = t;
+ }
+ (*bn) (BLOCK_NONE,NIL); /* alarms OK now */
+ if (tcpdebug) mm_log ("Reverse DNS resolution done",TCPDEBUG);
+ }
+ return cpystr (ret);
+}
+
+/* Validate name
+ * Accepts: domain name
+ * Returns: T if valid, NIL otherwise
+ */
+
+char *tcp_name_valid (char *s)
+{
+ int c;
+ char *ret,*tail;
+ /* must be non-empty and not too long */
+ if ((ret = (s && *s) ? s : NIL) && (tail = ret + NETMAXHOST)) {
+ /* must be alnum, dot, or hyphen */
+ while ((c = *s++) && (s <= tail) &&
+ (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) ||
+ ((c >= '0') && (c <= '9')) || (c == '-') || (c == '.')));
+ if (c) ret = NIL;
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/nt/tcp_nt.h b/imap/src/osdep/nt/tcp_nt.h
new file mode 100644
index 00000000..284cb226
--- /dev/null
+++ b/imap/src/osdep/nt/tcp_nt.h
@@ -0,0 +1,51 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Winsock TCP/IP routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+/* TCP input buffer -- must be large enough to prevent overflow */
+
+#define BUFLEN 16384 /* 32768 causes stdin read() to barf */
+
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#undef ERROR /* quell conflicting definition diagnostic */
+
+
+/* TCP I/O stream (must be before osdep.h is included) */
+
+#define TCPSTREAM struct tcp_stream
+TCPSTREAM {
+ char *host; /* host name */
+ char *remotehost; /* remote host name */
+ unsigned long port; /* port number */
+ char *localhost; /* local host name */
+ SOCKET tcpsi; /* tcp socket */
+ SOCKET tcpso; /* tcp socket */
+ long ictr; /* input counter */
+ char *iptr; /* input pointer */
+ char ibuf[BUFLEN]; /* input buffer */
+};
diff --git a/imap/src/osdep/nt/tenexnt.c b/imap/src/osdep/nt/tenexnt.c
new file mode 100644
index 00000000..4805b7c5
--- /dev/null
+++ b/imap/src/osdep/nt/tenexnt.c
@@ -0,0 +1,1310 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Tenex mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 22 May 1990
+ * Last Edited: 18 June 2007
+ */
+
+
+/* FILE TIME SEMANTICS
+ *
+ * The atime is the last read time of the file.
+ * The mtime is the last flags update time of the file.
+ * The ctime is the last write time of the file.
+ *
+ * TEXT SIZE SEMANTICS
+ *
+ * Most of the text sizes are in internal (LF-only) form, except for the
+ * msg.text size. Beware.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/utime.h>
+#include "misc.h"
+#include "dummy.h"
+
+/* TENEX I/O stream local data */
+
+typedef struct tenex_local {
+ unsigned int shouldcheck: 1; /* if ping should do a check instead */
+ unsigned int mustcheck: 1; /* if ping must do a check instead */
+ int fd; /* file descriptor for I/O */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ time_t lastsnarf; /* local snarf time */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ unsigned long uid; /* current text uid */
+ SIZEDTEXT text; /* current text */
+} TENEXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((TENEXLOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *tenex_valid (char *name);
+int tenex_isvalid (char *name,char *file);
+void *tenex_parameters (long function,void *value);
+void tenex_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void tenex_list (MAILSTREAM *stream,char *ref,char *pat);
+void tenex_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long tenex_create (MAILSTREAM *stream,char *mailbox);
+long tenex_delete (MAILSTREAM *stream,char *mailbox);
+long tenex_rename (MAILSTREAM *stream,char *old,char *newname);
+long tenex_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *tenex_open (MAILSTREAM *stream);
+void tenex_close (MAILSTREAM *stream,long options);
+void tenex_fast (MAILSTREAM *stream,char *sequence,long flags);
+void tenex_flags (MAILSTREAM *stream,char *sequence,long flags);
+char *tenex_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long tenex_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void tenex_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+void tenex_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long tenex_ping (MAILSTREAM *stream);
+void tenex_check (MAILSTREAM *stream);
+void tenex_snarf (MAILSTREAM *stream);
+long tenex_expunge (MAILSTREAM *stream,char *sequence,long options);
+long tenex_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long tenex_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+unsigned long tenex_size (MAILSTREAM *stream,unsigned long m);
+long tenex_parse (MAILSTREAM *stream);
+MESSAGECACHE *tenex_elt (MAILSTREAM *stream,unsigned long msgno);
+void tenex_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt);
+void tenex_update_status (MAILSTREAM *stream,unsigned long msgno,
+ long syncflag);
+unsigned long tenex_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size);
+
+
+/* Tenex mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER tenexdriver = {
+ "tenex", /* driver name */
+ DR_LOCAL|DR_MAIL, /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ tenex_valid, /* mailbox is valid for us */
+ tenex_parameters, /* manipulate parameters */
+ tenex_scan, /* scan mailboxes */
+ tenex_list, /* list mailboxes */
+ tenex_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ tenex_create, /* create mailbox */
+ tenex_delete, /* delete mailbox */
+ tenex_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ tenex_open, /* open mailbox */
+ tenex_close, /* close mailbox */
+ tenex_flags, /* fetch message "fast" attributes */
+ tenex_flags, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ tenex_header, /* fetch message header */
+ tenex_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ tenex_flag, /* modify flags */
+ tenex_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ tenex_ping, /* ping mailbox to see if still alive */
+ tenex_check, /* check for new messages */
+ tenex_expunge, /* expunge deleted messages */
+ tenex_copy, /* copy messages to another mailbox */
+ tenex_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM tenexproto = {&tenexdriver};
+
+/* Tenex mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *tenex_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return tenex_isvalid (name,tmp) ? &tenexdriver : NIL;
+}
+
+
+/* Tenex mail test for valid mailbox
+ * Accepts: mailbox name
+ * buffer to return file name
+ * Returns: T if valid, NIL otherwise
+ */
+
+int tenex_isvalid (char *name,char *file)
+{
+ int fd;
+ int ret = NIL;
+ char *s,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ struct utimbuf times;
+ errno = EINVAL; /* assume invalid argument */
+ /* if file, get its status */
+ if ((s = dummy_file (file,name)) && !stat (s,&sbuf) &&
+ ((sbuf.st_mode & S_IFMT) == S_IFREG)) {
+ if (!sbuf.st_size)errno = 0;/* empty file */
+ else if ((fd = open (file,O_BINARY|O_RDONLY,NIL)) >= 0) {
+ memset (tmp,'\0',MAILTMPLEN);
+ if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\012')) &&
+ (s[-1] != '\015')) { /* valid format? */
+ *s = '\0'; /* tie off header */
+ /* must begin with dd-mmm-yy" */
+ ret = (((tmp[2] == '-' && tmp[6] == '-') ||
+ (tmp[1] == '-' && tmp[5] == '-')) &&
+ (s = strchr (tmp+18,',')) && strchr (s+2,';')) ? T : NIL;
+ }
+ else errno = -1; /* bogus format */
+ close (fd); /* close the file */
+ /* \Marked status? */
+ if (sbuf.st_ctime > sbuf.st_atime) {
+ /* preserve atime and mtime */
+ times.actime = sbuf.st_atime;
+ times.modtime = sbuf.st_mtime;
+ utime (file,&times); /* set the times */
+ }
+ }
+ }
+ /* in case INBOX but not tenex format */
+ else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1;
+ return ret; /* return what we should */
+}
+
+
+/* Tenex manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tenex_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+/* Tenex mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void tenex_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* Tenex mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void tenex_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* Tenex mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void tenex_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* Tenex mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long tenex_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,mbx[MAILTMPLEN];
+ if (s = dummy_file (mbx,mailbox)) return dummy_create (stream,s);
+ sprintf (mbx,"Can't create %.80s: invalid name",mailbox);
+ mm_log (mbx,ERROR);
+ return NIL;
+}
+
+
+/* Tenex mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long tenex_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return tenex_rename (stream,mailbox,NIL);
+}
+
+/* Tenex mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long tenex_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = LONGT;
+ char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ int fd,ld;
+ struct stat sbuf;
+ if (!dummy_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'\\')) && !s[1])))) {
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ else if ((fd = open (file,O_BINARY|O_RDWR,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get exclusive parse/append permission */
+ if ((ld = lockname (lock,file,LOCK_EX)) < 0) {
+ mm_log ("Unable to lock rename mailbox",ERROR);
+ return NIL;
+ }
+ /* lock out other users */
+ if (flock (fd,LOCK_EX|LOCK_NB)) {
+ close (fd); /* couldn't lock, give up on it then */
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+ mm_log (tmp,ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return NIL;
+ }
+
+ if (newname) { /* want rename? */
+ /* found superior to destination name? */
+ if ((s = strrchr (tmp,'\\')) && (s != tmp) &&
+ ((tmp[1] != ':') || (s != tmp + 2))) {
+ c = s[1]; /* remember character after delimiter */
+ *s = s[1] = '\0'; /* tie off name at delimiter */
+ /* name doesn't exist, create it */
+ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) {
+ *s = '\\'; /* restore delimiter */
+ if (!dummy_create (stream,tmp)) ret = NIL;
+ }
+ else *s = '\\'; /* restore delimiter */
+ s[1] = c; /* restore character after delimiter */
+ }
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* pacify NTFS */
+ /* rename the file */
+ if (ret && rename (file,tmp)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ else {
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* pacify NTFS */
+ if (unlink (file)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %.80s",old,strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return ret; /* return success */
+}
+
+/* Tenex mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *tenex_open (MAILSTREAM *stream)
+{
+ int fd,ld;
+ char tmp[MAILTMPLEN];
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &tenexproto;
+ if (stream->local) fatal ("tenex recycle stream");
+ /* canonicalize the mailbox name */
+ if (!dummy_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ mm_log (tmp,ERROR);
+ }
+ if (stream->rdonly ||
+ (fd = open (tmp,O_BINARY|O_RDWR,NIL)) < 0) {
+ if ((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox: %.80s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ else if (!stream->rdonly) { /* got it, but readonly */
+ mm_log ("Can't get write access to mailbox, access is readonly",WARN);
+ stream->rdonly = T;
+ }
+ }
+ stream->local = fs_get (sizeof (TENEXLOCAL));
+ LOCAL->fd = fd; /* bind the file */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ LOCAL->text.data = (unsigned char *) fs_get (CHUNKSIZE);
+ LOCAL->text.size = CHUNKSIZE - 1;
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ /* get shared parse permission */
+ if ((ld = lockname (tmp,stream->mailbox,LOCK_SH)) < 0) {
+ mm_log ("Unable to lock open mailbox",ERROR);
+ return NIL;
+ }
+ flock (LOCAL->fd,LOCK_SH); /* lock the file */
+ unlockfd (ld,tmp); /* release shared parse permission */
+ LOCAL->filesize = 0; /* initialize parsed file size */
+ LOCAL->filetime = 0; /* time not set up yet */
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ stream->sequence++; /* bump sequence number */
+ stream->uid_validity = (unsigned long) time (0);
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ if (tenex_ping (stream) && !stream->nmsgs)
+ mm_log ("Mailbox is empty",(long) NIL);
+ if (!LOCAL) return NIL; /* failure if stream died */
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft =
+ stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ return stream; /* return stream to caller */
+}
+
+/* Tenex mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void tenex_close (MAILSTREAM *stream,long options)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ if (options & CL_EXPUNGE) tenex_expunge (stream,NIL,NIL);
+ stream->silent = silent; /* restore previous status */
+ flock (LOCAL->fd,LOCK_UN); /* unlock local file */
+ close (LOCAL->fd); /* close the local file */
+ /* free local text buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* Tenex mail fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ * Sniffs at file to get flags
+ */
+
+void tenex_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ STRING bs;
+ MESSAGECACHE *elt;
+ unsigned long i;
+ if (stream && LOCAL &&
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ if (!elt->rfc822_size) { /* have header size yet? */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.special.text.size,L_SET);
+ /* resize bigbuf if necessary */
+ if (LOCAL->buflen < elt->private.msg.full.text.size) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buflen = elt->private.msg.full.text.size;
+ LOCAL->buf = (char *) fs_get (LOCAL->buflen + 1);
+ }
+ /* tie off string */
+ LOCAL->buf[elt->private.msg.full.text.size] = '\0';
+ /* read in the message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.full.text.size);
+ INIT (&bs,mail_string,(void *) LOCAL->buf,
+ elt->private.msg.full.text.size);
+ /* calculate its CRLF size */
+ elt->rfc822_size = unix_crlflen (&bs);
+ }
+ tenex_elt (stream,i); /* get current flags from file */
+ }
+}
+
+/* TENEX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *tenex_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags)
+{
+ char *s;
+ unsigned long i;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ /* get to header position */
+ lseek (LOCAL->fd,tenex_hdrpos (stream,msgno,&i),L_SET);
+ if (flags & FT_INTERNAL) {
+ if (i > LOCAL->buflen) { /* resize if not enough space */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get (LOCAL->buflen = i + 1);
+ }
+ /* slurp the data */
+ read (LOCAL->fd,LOCAL->buf,*length = i);
+ }
+ else {
+ s = (char *) fs_get (i + 1);/* get readin buffer */
+ s[i] = '\0'; /* tie off string */
+ read (LOCAL->fd,s,i); /* slurp the data */
+ /* make CRLF copy of string */
+ *length = unix_crlfcpy (&LOCAL->buf,&LOCAL->buflen,s,i);
+ fs_give ((void **) &s); /* free readin buffer */
+ }
+ return LOCAL->buf;
+}
+
+/* TENEX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T, always
+ */
+
+long tenex_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ char *s;
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ /* get message status */
+ elt = tenex_elt (stream,msgno);
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ elt->seen = T; /* mark message as seen */
+ /* recalculate status */
+ tenex_update_status (stream,msgno,T);
+ mm_flags (stream,msgno);
+ }
+ if (flags & FT_INTERNAL) { /* if internal representation wanted */
+ /* find header position */
+ i = tenex_hdrpos (stream,msgno,&j);
+ if (i > LOCAL->buflen) { /* resize if not enough space */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get (LOCAL->buflen = i + 1);
+ }
+ /* go to text position */
+ lseek (LOCAL->fd,i + j,L_SET);
+ /* slurp the data */
+ if (read (LOCAL->fd,LOCAL->buf,i) != (long) i) return NIL;
+ /* set up stringstruct for internal */
+ INIT (bs,mail_string,LOCAL->buf,i);
+ }
+ else { /* normal form, previous text cached? */
+ if (elt->private.uid == LOCAL->uid)
+ i = elt->private.msg.text.text.size;
+ else { /* not cached, cache it now */
+ LOCAL->uid = elt->private.uid;
+ /* find header position */
+ i = tenex_hdrpos (stream,msgno,&j);
+ /* go to text position */
+ lseek (LOCAL->fd,i + j,L_SET);
+ s = (char *) fs_get ((i = tenex_size (stream,msgno) - j) + 1);
+ s[i] = '\0'; /* tie off string */
+ read (LOCAL->fd,s,i); /* slurp the data */
+ /* make CRLF copy of string */
+ i = elt->private.msg.text.text.size =
+ strcrlfcpy (&LOCAL->text.data,&LOCAL->text.size,s,i);
+ fs_give ((void **) &s); /* free readin buffer */
+ }
+ /* set up stringstruct */
+ INIT (bs,mail_string,LOCAL->text.data,i);
+ }
+ return T; /* success */
+}
+
+/* Tenex mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ */
+
+void tenex_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ struct utimbuf times;
+ struct stat sbuf;
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* make sure read comes after all that */
+ utime (stream->mailbox,&times);
+ }
+}
+
+
+/* Tenex mail per-message modify flags
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void tenex_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ struct stat sbuf;
+ /* maybe need to do a checkpoint? */
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ LOCAL->filetime = 0; /* don't do this test for any other messages */
+ }
+ /* recalculate status */
+ tenex_update_status (stream,elt->msgno,NIL);
+}
+
+/* Tenex mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream still alive, NIL if not
+ */
+
+long tenex_ping (MAILSTREAM *stream)
+{
+ unsigned long i = 1;
+ long r = T;
+ int ld;
+ char lock[MAILTMPLEN];
+ struct stat sbuf;
+ if (stream && LOCAL) { /* only if stream already open */
+ fstat (LOCAL->fd,&sbuf); /* get current file poop */
+ if (LOCAL->filetime && !(LOCAL->mustcheck || LOCAL->shouldcheck) &&
+ (LOCAL->filetime < sbuf.st_mtime)) LOCAL->shouldcheck = T;
+ /* check for changed message status */
+ if (LOCAL->mustcheck || LOCAL->shouldcheck) {
+ LOCAL->filetime = sbuf.st_mtime;
+ if (LOCAL->shouldcheck) /* babble when we do this unilaterally */
+ mm_notify (stream,"[CHECK] Checking for flag updates",NIL);
+ while (i <= stream->nmsgs) tenex_elt (stream,i++);
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ }
+ /* get shared parse/append permission */
+ if ((sbuf.st_size != LOCAL->filesize) &&
+ ((ld = lockname (lock,stream->mailbox,LOCK_SH)) >= 0)) {
+ /* parse resulting mailbox */
+ r = (tenex_parse (stream)) ? T : NIL;
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ }
+ return r; /* return result of the parse */
+}
+
+
+/* Tenex mail check mailbox (reparses status too)
+ * Accepts: MAIL stream
+ */
+
+void tenex_check (MAILSTREAM *stream)
+{
+ /* mark that a check is desired */
+ if (LOCAL) LOCAL->mustcheck = T;
+ if (tenex_ping (stream)) mm_log ("Check completed",(long) NIL);
+}
+
+/* Tenex mail expunge mailbox
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long tenex_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ struct utimbuf times;
+ struct stat sbuf;
+ off_t pos = 0;
+ int ld;
+ unsigned long i = 1;
+ unsigned long j,k,m,recent;
+ unsigned long n = 0;
+ unsigned long delta = 0;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ if (!(ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ tenex_ping (stream))); /* parse sequence if given, ping stream */
+ else if (stream->rdonly) mm_log ("Expunge ignored on readonly mailbox",WARN);
+ else {
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ }
+ /* get exclusive access */
+ if ((ld = lockname (lock,stream->mailbox,LOCK_EX)) < 0)
+ mm_log ("Unable to lock expunge mailbox",ERROR);
+ /* make sure see any newly-arrived messages */
+ else if (!tenex_parse (stream));
+ /* get exclusive access */
+ else if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
+ flock (LOCAL->fd,LOCK_SH);/* recover previous lock */
+ mm_log ("Can't expunge because mailbox is in use by another process",
+ ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+
+ else {
+ mm_critical (stream); /* go critical */
+ recent = stream->recent; /* get recent now that pinged and locked */
+ /* for each message */
+ while (i <= stream->nmsgs) {
+ /* get cache element */
+ elt = tenex_elt (stream,i);
+ /* number of bytes to smash or preserve */
+ k = elt->private.special.text.size + tenex_size (stream,i);
+ /* if need to expunge this message */
+ if (elt->deleted && (sequence ? elt->sequence : T)) {
+ /* if recent, note one less recent message */
+ if (elt->recent) --recent;
+ delta += k; /* number of bytes to delete */
+ /* notify upper levels */
+ mail_expunged (stream,i);
+ n++; /* count up one more expunged message */
+ }
+ else if (i++ && delta) {/* preserved message */
+ /* first byte to preserve */
+ j = elt->private.special.offset;
+ do { /* read from source position */
+ m = min (k,LOCAL->buflen);
+ lseek (LOCAL->fd,j,L_SET);
+ read (LOCAL->fd,LOCAL->buf,m);
+ pos = j - delta; /* write to destination position */
+ while (T) {
+ lseek (LOCAL->fd,pos,L_SET);
+ if (write (LOCAL->fd,LOCAL->buf,m) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+ pos += m; /* new position */
+ j += m; /* next chunk, perhaps */
+ } while (k -= m); /* until done */
+ /* note the new address of this text */
+ elt->private.special.offset -= delta;
+ }
+ /* preserved but no deleted messages */
+ else pos = elt->private.special.offset + k;
+ }
+
+ if (n) { /* truncate file after last message */
+ if (pos != (LOCAL->filesize -= delta)) {
+ sprintf (LOCAL->buf,
+ "Calculated size mismatch %lu != %lu, delta = %lu",
+ (unsigned long) pos,(unsigned long) LOCAL->filesize,delta);
+ mm_log (LOCAL->buf,WARN);
+ LOCAL->filesize = pos;/* fix it then */
+ }
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ sprintf (LOCAL->buf,"Expunged %lu messages",n);
+ /* output the news */
+ mm_log (LOCAL->buf,(long) NIL);
+ }
+ else mm_log ("No messages deleted, so no update needed",(long) NIL);
+ fsync (LOCAL->fd); /* force disk update */
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* reset atime to now */
+ utime (stream->mailbox,&times);
+ mm_nocritical (stream); /* release critical */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ flock (LOCAL->fd,LOCK_SH);/* allow sharers again */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+ }
+ return ret;
+}
+
+/* Tenex mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if success, NIL if failed
+ */
+
+long tenex_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ struct utimbuf times;
+ MESSAGECACHE *elt;
+ unsigned long i,j,k;
+ long ret = LONGT;
+ int fd,ld;
+ char file[MAILTMPLEN],lock[MAILTMPLEN];
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ /* make sure valid mailbox */
+ if (!tenex_isvalid (mailbox,file)) switch (errno) {
+ case ENOENT: /* no such file? */
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid Tenex-format mailbox name: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a Tenex-format mailbox: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* got file? */
+ if ((fd = open (file,O_BINARY|O_RDWR|O_CREAT,S_IREAD|S_IWRITE)) < 0) {
+ sprintf (LOCAL->buf,"Unable to open copy mailbox: %.80s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ mm_critical (stream); /* go critical */
+ /* get exclusive parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockname (lock,file,LOCK_EX)) < 0)) {
+ mm_log ("Unable to lock copy mailbox",ERROR);
+ mm_nocritical (stream);
+ return NIL;
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
+
+ /* for each requested message */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ /* number of bytes to copy */
+ k = elt->private.special.text.size + tenex_size (stream,i);
+ do { /* read from source position */
+ j = min (k,LOCAL->buflen);
+ read (LOCAL->fd,LOCAL->buf,j);
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ } while (ret && (k -= j));/* until done */
+ }
+ /* delete all requested messages */
+ if (ret && (options & CP_MOVE)) {
+ sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ }
+ /* set atime to now-1 if successful copy */
+ if (ret) times.actime = time (0) - 1;
+ /* else preserved \Marked status */
+ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ?
+ sbuf.st_atime : time (0);
+ times.modtime = sbuf.st_mtime;/* preserve mtime */
+ utime (file,&times); /* set the times */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ close (fd); /* close the file */
+ mm_nocritical (stream); /* release critical */
+ /* delete all requested messages */
+ if (ret && (options & CP_MOVE)) {
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = tenex_elt (stream,i))->sequence) {
+ elt->deleted = T; /* mark message deleted */
+ /* recalculate status */
+ tenex_update_status (stream,i,NIL);
+ }
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* make sure atime remains greater */
+ utime (stream->mailbox,&times);
+ }
+ }
+ if (ret && mail_parameters (NIL,GET_COPYUID,NIL))
+ mm_log ("Can not return meaningful COPYUID with this mailbox format",WARN);
+ return ret;
+}
+
+/* Tenex mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long tenex_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd,ld,c;
+ char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ struct utimbuf times;
+ FILE *df;
+ MESSAGECACHE elt;
+ long f;
+ unsigned long i,j,uf,size;
+ STRING *message;
+ long ret = LONGT;
+ /* default stream to prototype */
+ if (!stream) stream = &tenexproto;
+ /* make sure valid mailbox */
+ if (!tenex_isvalid (mailbox,file)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (!compare_cstring (mailbox,"INBOX")) tenex_create (NIL,"INBOX");
+ else {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* falls through */
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid TENEX-format mailbox name: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a TENEX-format mailbox: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get first message */
+ if (!(*af) (stream,data,&flags,&date,&message)) return NIL;
+
+ /* open destination mailbox */
+ if (((fd = open (file,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE))
+ < 0) || !(df = fdopen (fd,"ab"))) {
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockname (lock,file,LOCK_EX)) < 0)) {
+ mm_log ("Unable to lock append mailbox",ERROR);
+ close (fd);
+ return NIL;
+ }
+ mm_critical (stream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ errno = 0;
+ do { /* parse flags */
+ if (!SIZE (message)) { /* guard against zero-length */
+ mm_log ("Append of zero-length message",ERROR);
+ ret = NIL;
+ break;
+ }
+ f = mail_parse_flags (stream,flags,&i);
+ /* reverse bits (dontcha wish we had CIRC?) */
+ for (uf = 0; i; uf |= 1 << (29 - find_rightmost_bit (&i)));
+ if (date) { /* parse date if given */
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ mm_log (tmp,ERROR);
+ ret = NIL; /* mark failure */
+ break;
+ }
+ mail_date (tmp,&elt); /* write preseved date */
+ }
+ else internal_date (tmp); /* get current date in IMAP format */
+ i = GETPOS (message); /* remember current position */
+ for (j = SIZE (message), size = 0; j; --j)
+ if (SNX (message) != '\015') ++size;
+ SETPOS (message,i); /* restore position */
+ /* write header */
+ if (fprintf (df,"%s,%lu;%010lo%02lo\n",tmp,size,uf,(unsigned long) f) < 0)
+ ret = NIL;
+ else { /* write message */
+ while (size) if ((c = 0xff & SNX (message)) != '\015') {
+ if (putc (c,df) != EOF) --size;
+ else break;
+ }
+ /* get next message */
+ if (size || !(*af) (stream,data,&flags,&date,&message)) ret = NIL;
+ }
+ } while (ret && message);
+ /* if error... */
+ if (!ret || (fflush (df) == EOF)) {
+ ftruncate (fd,sbuf.st_size);/* revert file */
+ close (fd); /* make sure fclose() doesn't corrupt us */
+ if (errno) {
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ }
+ ret = NIL;
+ }
+ if (ret) times.actime = time (0) - 1;
+ /* else preserved \Marked status */
+ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ?
+ sbuf.st_atime : time (0);
+ times.modtime = sbuf.st_mtime;/* preserve mtime */
+ utime (file,&times); /* set the times */
+ fclose (df); /* close the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ mm_nocritical (stream); /* release critical */
+ if (ret && mail_parameters (NIL,GET_APPENDUID,NIL))
+ mm_log ("Can not return meaningful APPENDUID with this mailbox format",
+ WARN);
+ return ret;
+}
+
+/* Internal routines */
+
+
+/* Tenex mail return internal message size in bytes
+ * Accepts: MAIL stream
+ * message #
+ * Returns: internal size of message
+ */
+
+unsigned long tenex_size (MAILSTREAM *stream,unsigned long m)
+{
+ MESSAGECACHE *elt = mail_elt (stream,m);
+ return ((m < stream->nmsgs) ? mail_elt (stream,m+1)->private.special.offset :
+ LOCAL->filesize) -
+ (elt->private.special.offset + elt->private.special.text.size);
+}
+
+/* Tenex mail parse mailbox
+ * Accepts: MAIL stream
+ * Returns: T if parse OK
+ * NIL if failure, stream aborted
+ */
+
+long tenex_parse (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = NIL;
+ unsigned char c,*s,*t,*x;
+ char tmp[MAILTMPLEN];
+ unsigned long i,j;
+ long curpos = LOCAL->filesize;
+ long nmsgs = stream->nmsgs;
+ long recent = stream->recent;
+ short added = NIL;
+ short silent = stream->silent;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ if (sbuf.st_size < curpos) { /* sanity check */
+ sprintf (tmp,"Mailbox shrank from %ld to %ld!",curpos,sbuf.st_size);
+ mm_log (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ stream->silent = T; /* don't pass up mm_exists() events yet */
+ while (sbuf.st_size - curpos){/* while there is stuff to parse */
+ /* get to that position in the file */
+ lseek (LOCAL->fd,curpos,L_SET);
+ if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
+ sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size,
+ i ? strerror (errno) : "no data read");
+ mm_log (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ LOCAL->buf[i] = '\0'; /* tie off buffer just in case */
+ if (!(s = strchr (LOCAL->buf,'\012'))) {
+ sprintf (tmp,"Unable to find newline at %lu in %lu bytes, text: %s",
+ (unsigned long) curpos,i,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ *s = '\0'; /* tie off header line */
+ i = (s + 1) - LOCAL->buf; /* note start of text offset */
+ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
+ sprintf (tmp,"Unable to parse internal header at %lu: %s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ *s++ = '\0'; *t++ = '\0'; /* tie off fields */
+
+ added = T; /* note that a new message was added */
+ /* swell the cache */
+ mail_exists (stream,++nmsgs);
+ /* instantiate an elt for this message */
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ elt->private.uid = ++stream->uid_last;
+ /* note file offset of header */
+ elt->private.special.offset = curpos;
+ /* in case error */
+ elt->private.special.text.size = 0;
+ /* header size not known yet */
+ elt->private.msg.header.text.size = 0;
+ x = s; /* parse the header components */
+ if (mail_parse_date (elt,LOCAL->buf) &&
+ (elt->private.msg.full.text.size = strtoul (s,(char **) &s,10)) &&
+ (!(s && *s)) && isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) &&
+ isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) &&
+ isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) &&
+ isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12])
+ elt->private.special.text.size = i;
+ else { /* oops */
+ sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s",
+ curpos,(char *) LOCAL->buf,(char *) x,(char *) t);
+ mm_log (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ /* make sure didn't run off end of file */
+ if ((curpos += (elt->private.msg.full.text.size + i)) > sbuf.st_size) {
+ sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)",
+ elt->private.special.offset,(unsigned long) curpos,
+ (unsigned long) sbuf.st_size);
+ mm_log (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ c = t[10]; /* remember first system flags byte */
+ t[10] = '\0'; /* tie off flags */
+ j = strtoul (t,NIL,8); /* get user flags value */
+ t[10] = c; /* restore first system flags byte */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ /* calculate system flags */
+ if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T;
+ if (j & fDELETED) elt->deleted = T;
+ if (j & fFLAGGED) elt->flagged = T;
+ if (j & fANSWERED) elt->answered = T;
+ if (j & fDRAFT) elt->draft = T;
+ if (!(j & fOLD)) { /* newly arrived message? */
+ elt->recent = T;
+ recent++; /* count up a new recent message */
+ /* mark it as old */
+ tenex_update_status (stream,nmsgs,NIL);
+ }
+ }
+ fsync (LOCAL->fd); /* make sure all the fOLD flags take */
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */
+ LOCAL->filetime = sbuf.st_mtime;
+ if (added && !stream->rdonly){/* make sure atime updated */
+ struct utimbuf times;
+ times.actime = time (0);
+ times.modtime = LOCAL->filetime;
+ utime (stream->mailbox,&times);
+ }
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
+ mail_recent (stream,recent); /* and of change in recent messages */
+ return LONGT; /* return the winnage */
+}
+
+/* Tenex get cache element with status updating from file
+ * Accepts: MAIL stream
+ * message number
+ * Returns: cache element
+ */
+
+MESSAGECACHE *tenex_elt (MAILSTREAM *stream,unsigned long msgno)
+{
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ struct { /* old flags */
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged;
+ old.answered = elt->answered; old.draft = elt->draft;
+ old.user_flags = elt->user_flags;
+ tenex_read_flags (stream,elt);
+ if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
+ (old.flagged != elt->flagged) || (old.answered != elt->answered) ||
+ (old.draft != elt->draft) || (old.user_flags != elt->user_flags))
+ mm_flags (stream,msgno); /* let top level know */
+ return elt;
+}
+
+
+/* Tenex read flags from file
+ * Accepts: MAIL stream
+ * Returns: cache element
+ */
+
+void tenex_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ unsigned long i,j;
+ /* noop if readonly and have valid flags */
+ if (stream->rdonly && elt->valid) return;
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 13,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,12) < 0) {
+ sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ /* calculate system flags */
+ i = (((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0');
+ elt->seen = i & fSEEN ? T : NIL; elt->deleted = i & fDELETED ? T : NIL;
+ elt->flagged = i & fFLAGGED ? T : NIL;
+ elt->answered = i & fANSWERED ? T : NIL; elt->draft = i & fDRAFT ? T : NIL;
+ LOCAL->buf[10] = '\0'; /* tie off flags */
+ j = strtoul(LOCAL->buf,NIL,8);/* get user flags value */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ elt->valid = T; /* have valid flags now */
+}
+
+/* Tenex update status string
+ * Accepts: MAIL stream
+ * message number
+ * flag saying whether or not to sync
+ */
+
+void tenex_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag)
+{
+ struct utimbuf times;
+ struct stat sbuf;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ unsigned long j,k = 0;
+ /* readonly */
+ if (stream->rdonly || !elt->valid) tenex_read_flags (stream,elt);
+ else { /* readwrite */
+ j = elt->user_flags; /* get user flags */
+ /* reverse bits (dontcha wish we had CIRC?) */
+ while (j) k |= 1 << (29 - find_rightmost_bit (&j));
+ /* print new flag string */
+ sprintf (LOCAL->buf,"%010lo%02o",k,(unsigned)
+ (fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)));
+ while (T) { /* get to that place in the file */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 13,L_SET);
+ /* write new flags */
+ if (write (LOCAL->fd,LOCAL->buf,12) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+ if (syncflag) { /* sync if requested */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* make sure read is later */
+ utime (stream->mailbox,&times);
+ }
+ }
+}
+
+/* Tenex locate header for a message
+ * Accepts: MAIL stream
+ * message number
+ * pointer to returned header size
+ * Returns: position of header in file
+ */
+
+unsigned long tenex_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size)
+{
+ unsigned long siz;
+ long i = 0;
+ char c = '\0';
+ char *s = NIL;
+ MESSAGECACHE *elt = tenex_elt (stream,msgno);
+ unsigned long ret = elt->private.special.offset +
+ elt->private.special.text.size;
+ unsigned long msiz = tenex_size (stream,msgno);
+ /* is header size known? */
+ if (!(*size = elt->private.msg.header.text.size)) {
+ lseek (LOCAL->fd,ret,L_SET);/* get to header position */
+ /* search message for LF LF */
+ for (siz = 0; siz < msiz; siz++) {
+ if (--i <= 0) /* read another buffer as necessary */
+ read (LOCAL->fd,s = LOCAL->buf,i = min (msiz-siz,(long) MAILTMPLEN));
+ /* two newline sequence? */
+ if ((c == '\012') && (*s == '\012')) {
+ /* yes, note for later */
+ elt->private.msg.header.text.size = (*size = siz + 1);
+ return ret; /* return to caller */
+ }
+ else c = *s++; /* next character */
+ }
+ /* header consumes entire message */
+ elt->private.msg.header.text.size = *size = msiz;
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/nt/unixnt.c b/imap/src/osdep/nt/unixnt.c
new file mode 100644
index 00000000..2e92d39a
--- /dev/null
+++ b/imap/src/osdep/nt/unixnt.c
@@ -0,0 +1,2297 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX mail routines
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 20 December 1989
+ * Last Edited: 27 March 2008
+ */
+
+
+/* DEDICATION
+ *
+ * This file is dedicated to my dog, Unix, also known as Yun-chan and
+ * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix
+ * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after
+ * a two-month bout with cirrhosis of the liver.
+ *
+ * He was a dear friend, and I miss him terribly.
+ *
+ * Lift a leg, Yunie. Luv ya forever!!!!
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <time.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/utime.h>
+#include "unixnt.h"
+#include "pseudo.h"
+#include "fdstring.h"
+#include "misc.h"
+#include "dummy.h"
+
+/* UNIX I/O stream local data */
+
+typedef struct unix_local {
+ unsigned int dirty : 1; /* disk copy needs updating */
+ unsigned int ddirty : 1; /* double-dirty, ping becomes checkpoint */
+ unsigned int pseudo : 1; /* uses a pseudo message */
+ unsigned int appending : 1; /* don't mark new messages as old */
+ int fd; /* mailbox file descriptor */
+ int ld; /* lock file descriptor */
+ char *lname; /* lock file name */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ unsigned long uid; /* current text uid */
+ SIZEDTEXT text; /* current text */
+ unsigned long textlen; /* current text length */
+ char *line; /* returned line */
+ char *linebuf; /* line readin buffer */
+ unsigned long linebuflen; /* current line readin buffer length */
+} UNIXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((UNIXLOCAL *) stream->local)
+
+
+/* UNIX protected file structure */
+
+typedef struct unix_file {
+ MAILSTREAM *stream; /* current stream */
+ off_t curpos; /* current file position */
+ off_t protect; /* protected position */
+ off_t filepos; /* current last written file position */
+ char *buf; /* overflow buffer */
+ size_t buflen; /* current overflow buffer length */
+ char *bufpos; /* current buffer position */
+} UNIXFILE;
+
+/* Function prototypes */
+
+DRIVER *unix_valid (char *name);
+void *unix_parameters (long function,void *value);
+void unix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void unix_list (MAILSTREAM *stream,char *ref,char *pat);
+void unix_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long unix_create (MAILSTREAM *stream,char *mailbox);
+long unix_delete (MAILSTREAM *stream,char *mailbox);
+long unix_rename (MAILSTREAM *stream,char *old,char *newname);
+MAILSTREAM *unix_open (MAILSTREAM *stream);
+void unix_close (MAILSTREAM *stream,long options);
+char *unix_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long unix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt,
+ unsigned long *length,long flags);
+void unix_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long unix_ping (MAILSTREAM *stream);
+void unix_check (MAILSTREAM *stream);
+long unix_expunge (MAILSTREAM *stream,char *sequence,long options);
+long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long unix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+int unix_collect_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
+ STRING *msg);
+int unix_append_msgs (MAILSTREAM *stream,FILE *sf,FILE *df,SEARCHSET *set);
+
+void unix_abort (MAILSTREAM *stream);
+char *unix_file (char *dst,char *name);
+int unix_lock (char *file,int flags,int mode,char *lock,int op);
+void unix_unlock (int fd,MAILSTREAM *stream,char *lock);
+int unix_parse (MAILSTREAM *stream,char *lock,int op);
+char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size);
+unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr);
+unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt,
+ unsigned long uid,long flag);
+long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,char *lock,
+ long flags);
+long unix_extend (MAILSTREAM *stream,unsigned long size);
+void unix_write (UNIXFILE *f,char *s,unsigned long i);
+void unix_phys_write (UNIXFILE *f,char *buf,size_t size);
+
+/* UNIX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER unixdriver = {
+ "unix", /* driver name */
+ /* driver flags */
+ DR_LOCAL|DR_MAIL|DR_NONEWMAILRONLY|DR_XPOINT,
+ (DRIVER *) NIL, /* next driver */
+ unix_valid, /* mailbox is valid for us */
+ unix_parameters, /* manipulate parameters */
+ unix_scan, /* scan mailboxes */
+ unix_list, /* list mailboxes */
+ unix_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ unix_create, /* create mailbox */
+ unix_delete, /* delete mailbox */
+ unix_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ unix_open, /* open mailbox */
+ unix_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ unix_header, /* fetch message header */
+ unix_text, /* fetch message text */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ NIL, /* modify flags */
+ unix_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ unix_ping, /* ping mailbox to see if still alive */
+ unix_check, /* check for new messages */
+ unix_expunge, /* expunge deleted messages */
+ unix_copy, /* copy messages to another mailbox */
+ unix_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM unixproto = {&unixdriver};
+
+ /* driver parameters */
+static long unix_fromwidget = T;
+
+/* UNIX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *unix_valid (char *name)
+{
+ int fd;
+ DRIVER *ret = NIL;
+ int c,r;
+ char tmp[MAILTMPLEN],file[MAILTMPLEN],*s,*t;
+ struct stat sbuf;
+ struct utimbuf times;
+ errno = EINVAL; /* assume invalid argument */
+ /* must be non-empty file */
+ if ((t = dummy_file (file,name)) && !stat (t,&sbuf) &&
+ ((sbuf.st_mode & S_IFMT) == S_IFREG)) {
+ if (!sbuf.st_size)errno = 0;/* empty file */
+ else if ((fd = open (file,O_BINARY|O_RDONLY,NIL)) >= 0) {
+ memset (tmp,'\0',MAILTMPLEN);
+ if (read (fd,tmp,MAILTMPLEN-1) <= 0) errno = -1;
+ else { /* ignore leading whitespace */
+ for (s = tmp,c = '\n';
+ (*s == '\r') || (*s == '\n') || (*s == ' ') || (*s == '\t');
+ c = *s++);
+ if (c == '\n') { /* at start of a line? */
+ VALID (s,t,r,c); /* yes, validate format */
+ if (r) ret = &unixdriver;
+ else errno = -1; /* invalid format */
+ }
+ }
+ close (fd); /* close the file */
+ /* \Marked status? */
+ if ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) {
+ /* yes, preserve atime and mtime */
+ times.actime = sbuf.st_atime;
+ times.modtime = sbuf.st_mtime;
+ utime (file,&times); /* set the times */
+ }
+ }
+ }
+ return ret; /* return what we should */
+}
+/* UNIX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *unix_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_FROMWIDGET:
+ unix_fromwidget = (long) value;
+ case GET_FROMWIDGET:
+ ret = (void *) unix_fromwidget;
+ break;
+ }
+ return ret;
+}
+
+/* UNIX mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void unix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* UNIX mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void unix_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* UNIX mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void unix_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* UNIX mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long unix_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,mbx[MAILTMPLEN],tmp[MAILTMPLEN];
+ long ret = NIL;
+ int fd;
+ time_t ti = time (0);
+ if (!(s = dummy_file (mbx,mailbox))) {
+ sprintf (tmp,"Can't create %.80s: invalid name",mailbox);
+ mm_log (tmp,ERROR);
+ }
+ /* create underlying file */
+ else if (dummy_create_path (stream,s,NIL)) {
+ if ((s = strrchr (s,'\\')) && !s[1]) ret = T;
+ if ((fd = open (mbx,O_WRONLY|O_BINARY,NIL)) < 0) {
+ sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno));
+ mm_log (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ else { /* initialize header */
+ memset (tmp,'\0',MAILTMPLEN);
+ sprintf (tmp,"From %s %s",pseudo_from,ctime (&ti));
+ if (s = strpbrk (tmp,"\r\n")) *s = '\0';
+ strcat (tmp,"\r\nDate: ");
+ rfc822_fixed_date (s = tmp + strlen (tmp));
+ sprintf (s += strlen (s), /* write the pseudo-header */
+ "\r\nFrom: %s <%s@%s>\r\nSubject: %s\r\nX-IMAP: %010lu 0000000000\r\nStatus: RO\r\n\r\n%s\r\n\r\n",
+ pseudo_name,pseudo_from,mylocalhost (),pseudo_subject,
+ (unsigned long) ti,pseudo_msg);
+ if (write (fd,tmp,strlen (tmp)) > 0) {
+ close (fd); /* close file */
+ ret = T;
+ }
+ else {
+ sprintf (tmp,"Can't initialize mailbox node %.80s: %s",mbx,
+ strerror (errno));
+ mm_log (tmp,ERROR);
+ close (fd); /* close file before unlinking */
+ unlink (mbx); /* delete the file */
+ }
+ }
+ }
+ return ret;
+}
+
+/* UNIX mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long unix_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return unix_rename (stream,mailbox,NIL);
+}
+
+
+/* UNIX mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long unix_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = NIL;
+ char c,*s = NIL;
+ char tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN],lockx[MAILTMPLEN];
+ int fd,ld;
+ struct stat sbuf;
+ mm_critical (stream); /* get the c-client lock */
+ if (!dummy_file (file,old) ||
+ (newname && (!(s = dummy_file (tmp,newname)) ||
+ ((s = strrchr (s,'\\')) && !s[1]))))
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ else if ((ld = lockname (lock,file,NIL)) < 0)
+ sprintf (tmp,"Can't get lock for mailbox %.80s",old);
+
+ else { /* lock out other c-clients */
+ if (flock (ld,LOCK_EX|LOCK_NB)) {
+ close (ld); /* couldn't lock, give up on it then */
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+ }
+ /* lock out non c-client applications */
+ else if ((fd = unix_lock (file,O_BINARY|O_RDWR,S_IREAD|S_IWRITE,lockx,
+ LOCK_EX)) < 0)
+ sprintf (tmp,"Can't lock mailbox %.80s: %s",old,strerror (errno));
+ else {
+ unix_unlock(fd,NIL,lockx);/* pacify evil NTFS */
+ if (newname) { /* want rename? */
+ /* found superior to destination name? */
+ if ((s = strrchr (tmp,'\\')) && (s != tmp) &&
+ ((tmp[1] != ':') || (s != tmp + 2))) {
+ c = s[1]; /* remember character after delimiter */
+ *s = s[1] = '\0'; /* tie off name at delimiter */
+ /* name doesn't exist, create it */
+ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) {
+ *s = '\\'; /* restore delimiter */
+ if (!dummy_create (stream,newname)) {
+ flock (ld,LOCK_UN);
+ close (ld); /* close c-client lock */
+ unlink (lock); /* and delete it */
+ mm_nocritical (stream);
+ return NIL; /* couldn't create superior */
+ }
+ }
+ else *s = '\\'; /* restore delimiter */
+ s[1] = c; /* restore character after delimiter */
+ }
+ if (rename (file,tmp))
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ else ret = T; /* set success */
+ }
+ else if (unlink (file)) /* want delete */
+ sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
+ else ret = T; /* set success */
+ flock (ld,LOCK_UN); /* release c-client lock */
+ close (ld); /* close c-client lock */
+ unlink (lock); /* and delete it */
+ }
+ }
+ mm_nocritical (stream); /* no longer critical */
+ if (!ret) mm_log (tmp,ERROR); /* log error */
+ return ret; /* return success or failure */
+}
+
+/* UNIX mail open
+ * Accepts: Stream to open
+ * Returns: Stream on success, NIL on failure
+ */
+
+MAILSTREAM *unix_open (MAILSTREAM *stream)
+{
+ int fd;
+ char tmp[MAILTMPLEN];
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &unixproto;
+ if (stream->local) fatal ("unix recycle stream");
+ stream->local = memset (fs_get (sizeof (UNIXLOCAL)),0,sizeof (UNIXLOCAL));
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ /* canonicalize the stream mailbox name */
+ if (!dummy_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* flush old name */
+ fs_give ((void **) &stream->mailbox);
+ /* save canonical name */
+ stream->mailbox = cpystr (tmp);
+ LOCAL->fd = LOCAL->ld = -1; /* no file or state locking yet */
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = CHUNKSIZE) + 1);
+ LOCAL->text.data = (unsigned char *) fs_get (CHUNKSIZE);
+ LOCAL->text.size = CHUNKSIZE - 1;
+ LOCAL->linebuf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->linebuflen = CHUNKSIZE - 1;
+ stream->sequence++; /* bump sequence number */
+ if (!stream->rdonly) { /* make lock for read/write access */
+ if ((fd = lockname (tmp,stream->mailbox,NIL)) < 0)
+ mm_log ("Can't open mailbox lock, access is readonly",WARN);
+ /* can get the lock? */
+ else if (flock (fd,LOCK_EX|LOCK_NB)) {
+ if (!stream->silent)
+ mm_log ("Mailbox is open by another process, access is readonly",WARN);
+ close (fd);
+ }
+ else { /* got the lock, nobody else can alter state */
+ LOCAL->ld = fd; /* note lock's fd and name */
+ LOCAL->lname = cpystr (tmp);
+ }
+ }
+
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ /* will we be able to get write access? */
+ if ((LOCAL->ld >= 0) && access (stream->mailbox,02) && (errno == EACCES)) {
+ mm_log ("Can't get write access to mailbox, access is readonly",WARN);
+ flock (LOCAL->ld,LOCK_UN); /* release the lock */
+ close (LOCAL->ld); /* close the lock file */
+ LOCAL->ld = -1; /* no more lock fd */
+ unlink (LOCAL->lname); /* delete it */
+ }
+ /* reset UID validity */
+ stream->uid_validity = stream->uid_last = 0;
+ if (stream->silent && !stream->rdonly && (LOCAL->ld < 0))
+ unix_abort (stream); /* abort if can't get RW silent stream */
+ /* parse mailbox */
+ else if (unix_parse (stream,tmp,LOCK_SH)) {
+ unix_unlock (LOCAL->fd,stream,tmp);
+ mail_unlock (stream);
+ mm_nocritical (stream); /* done with critical */
+ }
+ if (!LOCAL) return NIL; /* failure if stream died */
+ /* make sure upper level knows readonly */
+ stream->rdonly = (LOCAL->ld < 0);
+ /* notify about empty mailbox */
+ if (!(stream->nmsgs || stream->silent)) mm_log ("Mailbox is empty",NIL);
+ if (!stream->rdonly) { /* flags stick if readwrite */
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft = T;
+ /* have permanent keywords */
+ stream->perm_user_flags = 0xffffffff;
+ /* and maybe can create them too */
+ stream->kwd_create = stream->user_flags[NUSERFLAGS-1] ? NIL : T;
+ }
+ return stream; /* return stream alive to caller */
+}
+
+
+/* UNIX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void unix_close (MAILSTREAM *stream,long options)
+{
+ int silent = stream->silent;
+ stream->silent = T; /* go silent */
+ /* expunge if requested */
+ if (options & CL_EXPUNGE) unix_expunge (stream,NIL,NIL);
+ /* else dump final checkpoint */
+ else if (LOCAL->dirty) unix_check (stream);
+ stream->silent = silent; /* restore old silence state */
+ unix_abort (stream); /* now punt the file and local data */
+}
+
+/* UNIX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+ /* lines to filter from header */
+static STRINGLIST *unix_hlines = NIL;
+
+char *unix_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned char *s;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ elt = mail_elt (stream,msgno);/* get cache */
+ if (!unix_hlines) { /* once only code */
+ STRINGLIST *lines = unix_hlines = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "Status"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-Status"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-Keywords"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-UID"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-IMAP"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-IMAPbase"));
+ }
+ /* go to header position */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.msg.header.offset,L_SET);
+
+ if (flags & FT_INTERNAL) { /* initial data OK? */
+ if (elt->private.msg.header.text.size > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
+ elt->private.msg.header.text.size) + 1);
+ }
+ /* read message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.header.text.size);
+ /* got text, tie off string */
+ LOCAL->buf[*length = elt->private.msg.header.text.size] = '\0';
+ }
+ else { /* need to make a CRLF version */
+ read (LOCAL->fd,s = (char *) fs_get (elt->private.msg.header.text.size+1),
+ elt->private.msg.header.text.size);
+ /* tie off string, and convert to CRLF */
+ s[elt->private.msg.header.text.size] = '\0';
+ *length = unix_crlfcpy (&LOCAL->buf,&LOCAL->buflen,s,
+ elt->private.msg.header.text.size);
+ fs_give ((void **) &s); /* free readin buffer */
+ }
+ *length = mail_filter (LOCAL->buf,*length,unix_hlines,FT_NOT);
+ return LOCAL->buf; /* return processed copy */
+}
+
+/* UNIX mail fetch message text
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T on success, NIL if failure
+ */
+
+long unix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ char *s;
+ unsigned long i;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mail_elt (stream,msgno);/* get cache element */
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ /* mark message seen and dirty */
+ elt->seen = elt->private.dirty = LOCAL->dirty = T;
+ mm_flags (stream,msgno);
+ }
+ s = unix_text_work (stream,elt,&i,flags);
+ INIT (bs,mail_string,s,i); /* set up stringstruct */
+ return T; /* success */
+}
+
+/* UNIX mail fetch message text worker routine
+ * Accepts: MAIL stream
+ * message cache element
+ * pointer to returned header text length
+ * option flags
+ */
+
+char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt,
+ unsigned long *length,long flags)
+{
+ FDDATA d;
+ STRING bs;
+ unsigned char c,*s,tmp[CHUNKSIZE];
+ /* go to text position */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.msg.text.offset,L_SET);
+ if (flags & FT_INTERNAL) { /* initial data OK? */
+ if (elt->private.msg.text.text.size > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
+ elt->private.msg.text.text.size) + 1);
+ }
+ /* read message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.text.text.size);
+ /* got text, tie off string */
+ LOCAL->buf[*length = elt->private.msg.text.text.size] = '\0';
+ return LOCAL->buf;
+ }
+ /* have it cached already? */
+ if (elt->private.uid != LOCAL->uid) {
+ /* not cached, cache it now */
+ LOCAL->uid = elt->private.uid;
+ /* is buffer big enough? */
+ if (elt->rfc822_size > LOCAL->text.size) {
+ /* excessively conservative, but the right thing is too hard to do */
+ fs_give ((void **) &LOCAL->text.data);
+ LOCAL->text.data = (unsigned char *)
+ fs_get ((LOCAL->text.size = elt->rfc822_size) + 1);
+ }
+ d.fd = LOCAL->fd; /* yes, set up file descriptor */
+ d.pos = elt->private.special.offset + elt->private.msg.text.offset;
+ d.chunk = tmp; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE; /* file chunk size */
+ INIT (&bs,fd_string,&d,elt->private.msg.text.text.size);
+ for (s = (char *) LOCAL->text.data; SIZE (&bs);) switch (c = SNX (&bs)) {
+ case '\r': /* carriage return seen */
+ *s++ = c; /* copy it and any succeeding LF */
+ if (SIZE (&bs) && (CHR (&bs) == '\n')) *s++ = SNX (&bs);
+ break;
+ case '\n':
+ *s++ = '\r'; /* insert a CR */
+ default:
+ *s++ = c; /* copy characters */
+ }
+ *s = '\0'; /* tie off buffer */
+ /* calculate length of cached data */
+ LOCAL->textlen = s - LOCAL->text.data;
+ }
+ *length = LOCAL->textlen; /* return from cache */
+ return (char *) LOCAL->text.data;
+}
+
+/* UNIX per-message modify flag
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void unix_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ /* only after finishing */
+ if (elt->valid) elt->private.dirty = LOCAL->dirty = T;
+}
+
+
+/* UNIX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long unix_ping (MAILSTREAM *stream)
+{
+ char lock[MAILTMPLEN];
+ struct stat sbuf;
+ /* big no-op if not readwrite */
+ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock) {
+ if (stream->rdonly) { /* does he want to give up readwrite? */
+ /* checkpoint if we changed something */
+ if (LOCAL->dirty) unix_check (stream);
+ flock (LOCAL->ld,LOCK_UN);/* release readwrite lock */
+ close (LOCAL->ld); /* close the readwrite lock file */
+ LOCAL->ld = -1; /* no more readwrite lock fd */
+ unlink (LOCAL->lname); /* delete the readwrite lock file */
+ }
+ else { /* get current mailbox size */
+ if (LOCAL->fd >= 0) fstat (LOCAL->fd,&sbuf);
+ else if (stat (stream->mailbox,&sbuf)) {
+ sprintf (LOCAL->buf,"Mailbox stat failed, aborted: %s",
+ strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ unix_abort (stream);
+ return NIL;
+ }
+ /* parse if mailbox changed */
+ if ((LOCAL->ddirty || (sbuf.st_size != LOCAL->filesize)) &&
+ unix_parse (stream,lock,LOCK_EX)) {
+ /* force checkpoint if double-dirty */
+ if (LOCAL->ddirty) unix_rewrite (stream,NIL,lock,NIL);
+ /* unlock mailbox */
+ else unix_unlock (LOCAL->fd,stream,lock);
+ mail_unlock (stream); /* and stream */
+ mm_nocritical (stream); /* done with critical */
+ }
+ }
+ }
+ return LOCAL ? LONGT : NIL; /* return if still alive */
+}
+
+/* UNIX mail check mailbox
+ * Accepts: MAIL stream
+ */
+
+void unix_check (MAILSTREAM *stream)
+{
+ char lock[MAILTMPLEN];
+ /* parse and lock mailbox */
+ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock &&
+ unix_parse (stream,lock,LOCK_EX)) {
+ /* any unsaved changes? */
+ if (LOCAL->dirty && unix_rewrite (stream,NIL,lock,NIL)) {
+ if (!stream->silent) mm_log ("Checkpoint completed",NIL);
+ }
+ /* no checkpoint needed, just unlock */
+ else unix_unlock (LOCAL->fd,stream,lock);
+ mail_unlock (stream); /* unlock the stream */
+ mm_nocritical (stream); /* done with critical */
+ }
+}
+
+
+/* UNIX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long unix_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ unsigned long i;
+ char lock[MAILTMPLEN];
+ char *msg = NIL;
+ /* parse and lock mailbox */
+ if (ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ LOCAL && (LOCAL->ld >= 0) && !stream->lock &&
+ unix_parse (stream,lock,LOCK_EX)) {
+ /* check expunged messages if not dirty */
+ for (i = 1; !LOCAL->dirty && (i <= stream->nmsgs); i++) {
+ MESSAGECACHE *elt = mail_elt (stream,i);
+ if (mail_elt (stream,i)->deleted) LOCAL->dirty = T;
+ }
+ if (!LOCAL->dirty) { /* not dirty and no expunged messages */
+ unix_unlock (LOCAL->fd,stream,lock);
+ msg = "No messages deleted, so no update needed";
+ }
+ else if (unix_rewrite (stream,&i,lock,sequence ? LONGT : NIL)) {
+ if (i) sprintf (msg = LOCAL->buf,"Expunged %lu messages",i);
+ else msg = "Mailbox checkpointed, but no messages expunged";
+ }
+ /* rewrite failed */
+ else unix_unlock (LOCAL->fd,stream,lock);
+ mail_unlock (stream); /* unlock the stream */
+ mm_nocritical (stream); /* done with critical */
+ if (msg && !stream->silent) mm_log (msg,NIL);
+ }
+ else if (!stream->silent) mm_log("Expunge ignored on readonly mailbox",WARN);
+ return ret;
+}
+
+/* UNIX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if copy successful, else NIL
+ */
+
+long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ int fd;
+ char *s,file[MAILTMPLEN],lock[MAILTMPLEN];
+ struct utimbuf times;
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ long ret = T;
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ copyuid_t cu = (copyuid_t) (mail_parameters (NIL,GET_USERHASNOLIFE,NIL) ?
+ NIL : mail_parameters (NIL,GET_COPYUID,NIL));
+ SEARCHSET *source = cu ? mail_newsearchset () : NIL;
+ SEARCHSET *dest = cu ? mail_newsearchset () : NIL;
+ MAILSTREAM *tstream = NIL;
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* make sure destination is valid */
+ if (!(unix_valid (mailbox) || !errno))
+ switch (errno) {
+ case ENOENT: /* no such file? */
+ if (compare_cstring (mailbox,"INBOX")) {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ }
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ unix_create (NIL,"INBOX");/* create empty INBOX */
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid UNIX-format mailbox name: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a UNIX-format mailbox: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+
+ /* try to open rewrite for UIDPLUS */
+ if ((tstream = mail_open_work (&unixdriver,NIL,mailbox,
+ OP_SILENT|OP_NOKOD)) && tstream->rdonly)
+ tstream = mail_close (tstream);
+ if (cu && !tstream) { /* wanted a COPYUID? */
+ sprintf (LOCAL->buf,"Unable to write-open mailbox for COPYUID: %.80s",
+ mailbox);
+ MM_LOG (LOCAL->buf,WARN);
+ cu = NIL; /* don't try to do COPYUID */
+ }
+ LOCAL->buf[0] = '\0';
+ mm_critical (stream); /* go critical */
+ if ((fd = unix_lock (dummy_file (file,mailbox),
+ O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE,
+ lock,LOCK_EX)) < 0) {
+ mm_nocritical (stream); /* done with critical */
+ sprintf (LOCAL->buf,"Can't open destination mailbox: %s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR); /* log the error */
+ return NIL; /* failed */
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ /* write all requested messages to mailbox */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size);
+ if (LOCAL->buf[(j = elt->private.special.text.size) - 2] != '\r') {
+ LOCAL->buf[j - 1] = '\r';
+ LOCAL->buf[j++] = '\n';
+ }
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ else { /* internal header succeeded */
+ s = unix_header (stream,i,&j,NIL);
+ /* header size, sans trailing newline */
+ if (j && (s[j - 4] == '\r')) j -= 2;
+ if (write (fd,s,j) < 0) ret = NIL;
+ else { /* message header succeeded */
+ j = tstream ? /* write UIDPLUS data if have readwrite */
+ unix_xstatus (stream,LOCAL->buf,elt,++(tstream->uid_last),LONGT) :
+ unix_xstatus (stream,LOCAL->buf,elt,NIL,NIL);
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ else { /* message status succeeded */
+ s = unix_text_work (stream,elt,&j,NIL);
+ if ((write (fd,s,j) < 0) || (write (fd,"\r\n",2) < 0))
+ ret = NIL;
+ else if (cu) { /* need to pass back new UID? */
+ mail_append_set (source,mail_uid (stream,i));
+ mail_append_set (dest,tstream->uid_last);
+ }
+ }
+ }
+ }
+ }
+
+ if (!ret || fsync (fd)) { /* force out the update */
+ sprintf (LOCAL->buf,"Message copy failed: %s",strerror (errno));
+ ftruncate (fd,sbuf.st_size);
+ ret = NIL;
+ }
+ /* force UIDVALIDITY assignment now */
+ if (tstream && !tstream->uid_validity)
+ tstream->uid_validity = (unsigned long) time (0);
+ /* return sets if doing COPYUID */
+ if (cu && ret) (*cu) (stream,mailbox,tstream->uid_validity,source,dest);
+ else { /* flush any sets we may have built */
+ mail_free_searchset (&source);
+ mail_free_searchset (&dest);
+ }
+ times.modtime = time (0); /* set mtime to now */
+ /* set atime to now-1 if successful copy */
+ if (ret) times.actime = times.modtime - 1;
+
+ else times.actime = /* else preserve \Marked status */
+ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ?
+ sbuf.st_atime : times.modtime;
+ utime (file,&times); /* set the times */
+ unix_unlock (fd,NIL,lock); /* unlock and close mailbox */
+ if (tstream) { /* update last UID if we can */
+ UNIXLOCAL * local = (UNIXLOCAL *) tstream->local;
+ local->dirty = T; /* do a rewrite */
+ local->appending = T; /* but not at the cost of marking as old */
+ tstream = mail_close (tstream);
+ }
+ /* log the error */
+ if (!ret) mm_log (LOCAL->buf,ERROR);
+ /* delete if requested message */
+ else if (options & CP_MOVE) for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence)
+ elt->deleted = elt->private.dirty = LOCAL->dirty = T;
+ mm_nocritical (stream); /* release critical */
+ return ret;
+}
+
+/* UNIX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+#define BUFLEN 8*MAILTMPLEN
+
+long unix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd;
+ unsigned long i;
+ char *flags,*date,buf[BUFLEN],tmp[MAILTMPLEN],file[MAILTMPLEN],
+ lock[MAILTMPLEN];
+ struct utimbuf times;
+ FILE *sf,*df;
+ MESSAGECACHE elt;
+ STRING *message;
+ unsigned long uidlocation = 0;
+ appenduid_t au = (appenduid_t)
+ (mail_parameters (NIL,GET_USERHASNOLIFE,NIL) ? NIL :
+ mail_parameters (NIL,GET_APPENDUID,NIL));
+ SEARCHSET *dst = au ? mail_newsearchset () : NIL;
+ long ret = LONGT;
+ MAILSTREAM *tstream = NIL;
+ if (!stream) { /* stream specified? */
+ stream = &unixproto; /* no, default stream to prototype */
+ for (i = 0; i < NUSERFLAGS && stream->user_flags[i]; ++i)
+ fs_give ((void **) &stream->user_flags[i]);
+ }
+ if (!unix_valid (mailbox)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (!compare_cstring (mailbox,"INBOX")) {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ unix_create (NIL,"INBOX"); /* create empty INBOX */
+ case 0: /* merely empty file? */
+ tstream = stream;
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid UNIX-format mailbox name: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a UNIX-format mailbox: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get sniffing stream for keywords */
+ else if (!(tstream = mail_open (NIL,mailbox,
+ OP_READONLY|OP_SILENT|OP_NOKOD|OP_SNIFF))) {
+ sprintf (tmp,"Unable to examine mailbox for APPEND: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+
+ /* get first message */
+ if (!(*af) (tstream,data,&flags,&date,&message)) return NIL;
+ if (!(sf = tmpfile ())) { /* must have scratch file */
+ sprintf (tmp,".%lx.%lx",(unsigned long) time (0),(unsigned long)getpid ());
+ if (!stat (tmp,&sbuf) || !(sf = fopen (tmp,"wb+"))) {
+ sprintf (tmp,"Unable to create scratch file: %.80s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ unlink (tmp);
+ }
+ do { /* parse date */
+ if (!date) rfc822_date (date = tmp);
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ mm_log (tmp,ERROR);
+ }
+ else { /* user wants to suppress time zones? */
+ if (mail_parameters (NIL,GET_NOTIMEZONES,NIL)) {
+ time_t when = mail_longdate (&elt);
+ date = ctime (&when); /* use traditional date */
+ }
+ /* use POSIX-style date */
+ else date = mail_cdate (tmp,&elt);
+ if (!SIZE (message)) mm_log ("Append of zero-length message",ERROR);
+ else if (!unix_collect_msg (tstream,sf,flags,date,message)) {
+ sprintf (tmp,"Error writing scratch file: %.80s",strerror (errno));
+ mm_log (tmp,ERROR);
+ }
+ /* get next message */
+ else if ((*af) (tstream,data,&flags,&date,&message)) continue;
+ }
+ fclose (sf); /* punt scratch file */
+ return NIL; /* give up */
+ } while (message); /* until no more messages */
+ if (fflush (sf)) {
+ sprintf (tmp,"Error finishing scratch file: %.80s",strerror (errno));
+ mm_log (tmp,ERROR);
+ fclose (sf); /* punt scratch file */
+ return NIL; /* give up */
+ }
+ i = ftell (sf); /* size of scratch file */
+
+ /* close sniffing stream */
+ if (tstream != stream) tstream = mail_close (tstream);
+ mm_critical (stream); /* go critical */
+ /* try to open readwrite for UIDPLUS */
+ if ((tstream = mail_open_work (&unixdriver,NIL,mailbox,
+ OP_SILENT|OP_NOKOD)) && tstream->rdonly)
+ tstream = mail_close (tstream);
+ if (au && !tstream) { /* wanted an APPENDUID? */
+ sprintf (tmp,"Unable to re-open mailbox for APPENDUID: %.80s",mailbox);
+ MM_LOG (tmp,WARN);
+ au = NIL;
+ }
+ if (((fd = unix_lock (dummy_file (file,mailbox),
+ O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE,
+ lock,LOCK_EX)) < 0) || !(df = fdopen (fd,"ab"))) {
+ mm_nocritical (stream); /* done with critical */
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ rewind (sf);
+ times.modtime = time (0); /* set mtime to now */
+ /* write all messages */
+ if (!unix_append_msgs (tstream,sf,df,au ? dst : NIL) ||
+ (fflush (df) == EOF) || fsync (fd)) {
+ sprintf (buf,"Message append failed: %s",strerror (errno));
+ mm_log (buf,ERROR);
+ ftruncate (fd,sbuf.st_size);/* revert file */
+ times.actime = /* preserve \Marked status */
+ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ?
+ sbuf.st_atime : times.modtime;
+ ret = NIL; /* return error */
+ }
+ /* set atime to now-1 if successful copy */
+ else times.actime = times.modtime - 1;
+ utime (file,&times); /* set the times */
+ fclose (sf); /* done with scratch file */
+ /* force UIDVALIDITY assignment now */
+ if (tstream && !tstream->uid_validity)
+ tstream->uid_validity = (unsigned long) time (0);
+ /* return sets if doing APPENDUID */
+ if (au && ret) (*au) (mailbox,tstream->uid_validity,dst);
+ else mail_free_searchset (&dst);
+ flock (fd,LOCK_UN); /* unlock mailbox (can't use unix_unlock() */
+ if (lock && *lock) unlink (lock);
+ fclose (df); /* close mailbox */
+ if (tstream) { /* update last UID if we can */
+ UNIXLOCAL * local = (UNIXLOCAL *) tstream->local;
+ local->dirty = T; /* do a rewrite */
+ local->appending = T; /* but not at the cost of marking as old */
+ tstream = mail_close (tstream);
+ }
+ mm_nocritical (stream); /* release critical */
+ return ret;
+}
+
+/* Collect and write single message to append scratch file
+ * Accepts: MAIL stream
+ * scratch file
+ * flags
+ * date
+ * message stringstruct
+ * Returns: NIL if write error, else T
+ */
+
+int unix_collect_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
+ STRING *msg)
+{
+ unsigned char *s,*t;
+ unsigned long uf;
+ long f = mail_parse_flags (stream,flags,&uf);
+ /* write metadata */
+ if (fprintf (sf,"%ld %lu ",f,SIZE (msg) + 2) < 0) return NIL;
+ for (s = date; *s; *s++) switch (*s) {
+ default:
+ if (putc (*s,sf) == EOF) return NIL;
+ case '\r': case '\n':
+ break;
+ }
+ if (fputs ("\r\n",sf) == EOF) return NIL;
+ while (uf) /* write user flags */
+ if ((s = stream->user_flags[find_rightmost_bit (&uf)]) &&
+ (fprintf (sf," %s",s) < 0)) return NIL;
+ if (fputs ("\r\n",sf) == EOF) return NIL;
+ while (SIZE (msg)) { /* copy text to scratch file */
+ for (s = (unsigned char *) msg->curpos, t = s + msg->cursize; s < t; ++s)
+ if (!*s) *s = 0x80; /* disallow NUL */
+ /* write buffered text */
+ if (fwrite (msg->curpos,1,msg->cursize,sf) == msg->cursize)
+ SETPOS (msg,GETPOS (msg) + msg->cursize);
+ else return NIL; /* failed */
+ }
+ /* write trailing CRLF and return */
+ return (fputs ("\r\n",sf) == EOF) ? NIL : T;
+}
+
+/* Append messages from scratch file to mailbox
+ * Accepts: MAIL stream
+ * source file
+ * destination file
+ * uidset to update if non-NIL
+ * Returns: T if success, NIL if failure
+ */
+
+int unix_append_msgs (MAILSTREAM *stream,FILE *sf,FILE *df,SEARCHSET *set)
+{
+ int ti,zn,c;
+ long f;
+ unsigned long i,j;
+ char *x,tmp[MAILTMPLEN];
+ int hdrp = T;
+ /* get message metadata line */
+ while (fgets (tmp,MAILTMPLEN,sf)) {
+ if (!(isdigit (tmp[0]) && strchr (tmp,'\n'))) return NIL;
+ f = strtol (tmp,&x,10); /* get flags */
+ if (!((*x++ == ' ') && isdigit (*x))) return NIL;
+ i = strtoul (x,&x,10); /* get message size */
+ if ((*x++ != ' ') || /* build initial header */
+ (fprintf (df,"From %s@%s %sStatus: ",myusername(),mylocalhost(),x)<0)||
+ (f&fSEEN && (putc ('R',df) == EOF)) ||
+ (fputs ("\r\nX-Status: ",df) == EOF) ||
+ (f&fDELETED && (putc ('D',df) == EOF)) ||
+ (f&fFLAGGED && (putc ('F',df) == EOF)) ||
+ (f&fANSWERED && (putc ('A',df) == EOF)) ||
+ (f&fDRAFT && (putc ('T',df) == EOF)) ||
+ (fputs ("\r\nX-Keywords:",df) == EOF)) return NIL;
+ /* copy keywords */
+ while ((c = getc (sf)) != '\n') switch (c) {
+ case EOF:
+ return NIL;
+ default:
+ if (putc (c,df) == EOF) return NIL;
+ }
+ if ((putc ('\n',df) == EOF) ||
+ (set && (fprintf (df,"X-UID: %lu\r\n",++(stream->uid_last)) < 0)))
+ return NIL;
+
+ for (c = '\n'; i && fgets (tmp,MAILTMPLEN,sf); c = tmp[j-1]) {
+ /* get read line length */
+ if (i < (j = strlen (tmp))) fatal ("unix_append_msgs overrun");
+ i -= j; /* number of bytes left */
+ if (!j) continue; /* do nothing if line emptied */
+ /* complete line? */
+ if ((c == '\n')) switch (tmp[0]) {
+ case 'F': /* possible "From " (case counts here) */
+ if ((j > 4) && (tmp[0] == 'F') && (tmp[1] == 'r') && (tmp[2] == 'o') &&
+ (tmp[3] == 'm') && (tmp[4] == ' ')) {
+ if (!unix_fromwidget) {
+ VALID (tmp,x,ti,zn);/* conditional, only write widget if */
+ if (!ti) break; /* it looks like a valid header */
+ } /* write the widget */
+ if (putc ('>',df) == EOF) return NIL;
+ }
+ break;
+ case 'S': case 's': /* possible "Status:" */
+ if (hdrp && (j > 6) && ((tmp[1] == 't') || (tmp[1] == 'T')) &&
+ ((tmp[2] == 'a') || (tmp[2] == 'A')) &&
+ ((tmp[3] == 't') || (tmp[3] == 'T')) &&
+ ((tmp[4] == 'u') || (tmp[4] == 'U')) &&
+ ((tmp[5] == 's') || (tmp[5] == 'S')) && (tmp[6] == ':') &&
+ (fputs ("X-Original-",df) == EOF)) return NIL;
+ break;
+ case 'X': case 'x': /* possible X-??? header */
+ if (hdrp && (tmp[1] == '-') &&
+ /* possible X-UID: */
+ (((j > 5) && ((tmp[2] == 'U') || (tmp[2] == 'u')) &&
+ ((tmp[3] == 'I') || (tmp[3] == 'i')) &&
+ ((tmp[4] == 'D') || (tmp[4] == 'd')) && (tmp[5] == ':')) ||
+ /* possible X-IMAP: */
+ ((j > 6) && ((tmp[2] == 'I') || (tmp[2] == 'i')) &&
+ ((tmp[3] == 'M') || (tmp[3] == 'm')) &&
+ ((tmp[4] == 'A') || (tmp[4] == 'a')) &&
+ ((tmp[5] == 'P') || (tmp[5] == 'p')) &&
+ ((tmp[6] == ':') ||
+ /* or X-IMAPbase: */
+ ((j > 10) && ((tmp[6] == 'b') || (tmp[6] == 'B')) &&
+ ((tmp[7] == 'a') || (tmp[7] == 'A')) &&
+ ((tmp[8] == 's') || (tmp[8] == 'S')) &&
+ ((tmp[9] == 'e') || (tmp[9] == 'E')) && (tmp[10] == ':')))) ||
+ /* possible X-Status: */
+ ((j > 8) && ((tmp[2] == 'S') || (tmp[2] == 's')) &&
+ ((tmp[3] == 't') || (tmp[3] == 'T')) &&
+ ((tmp[4] == 'a') || (tmp[4] == 'A')) &&
+ ((tmp[5] == 't') || (tmp[5] == 'T')) &&
+ ((tmp[6] == 'u') || (tmp[6] == 'U')) &&
+ ((tmp[7] == 's') || (tmp[7] == 'S')) && (tmp[8] == ':')) ||
+ /* possible X-Keywords: */
+ ((j > 10) && ((tmp[2] == 'K') || (tmp[2] == 'k')) &&
+ ((tmp[3] == 'e') || (tmp[3] == 'E')) &&
+ ((tmp[4] == 'y') || (tmp[4] == 'Y')) &&
+ ((tmp[5] == 'w') || (tmp[5] == 'W')) &&
+ ((tmp[6] == 'o') || (tmp[6] == 'O')) &&
+ ((tmp[7] == 'r') || (tmp[7] == 'R')) &&
+ ((tmp[8] == 'd') || (tmp[8] == 'D')) &&
+ ((tmp[9] == 's') || (tmp[9] == 'S')) && (tmp[10] == ':'))) &&
+ (fputs ("X-Original-",df) == EOF)) return NIL;
+ break;
+ case '\n': /* blank line */
+ hdrp = NIL;
+ break;
+ default: /* nothing to do */
+ break;
+ }
+ /* just write the line */
+ if (fwrite (tmp,1,j,df) != j) return NIL;
+ }
+ if (i) return NIL; /* didn't read entire message */
+ /* update set */
+ if (stream) mail_append_set (set,stream->uid_last);
+ }
+ return T;
+}
+
+/* Internal routines */
+
+
+/* UNIX mail abort stream
+ * Accepts: MAIL stream
+ */
+
+void unix_abort (MAILSTREAM *stream)
+{
+ if (LOCAL) { /* only if a file is open */
+ if (LOCAL->fd >= 0) close (LOCAL->fd);
+ if (LOCAL->ld >= 0) { /* have a mailbox lock? */
+ flock (LOCAL->ld,LOCK_UN);/* yes, release the lock */
+ close (LOCAL->ld); /* close the lock file */
+ unlink (LOCAL->lname); /* and delete it */
+ }
+ if (LOCAL->lname) fs_give ((void **) &LOCAL->lname);
+ /* free local text buffers */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data);
+ if (LOCAL->linebuf) fs_give ((void **) &LOCAL->linebuf);
+ if (LOCAL->line) fs_give ((void **) &LOCAL->line);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* UNIX open and lock mailbox
+ * Accepts: file name to open/lock
+ * file open mode
+ * destination buffer for lock file name
+ * type of locking operation (LOCK_SH or LOCK_EX)
+ */
+
+int unix_lock (char *file,int flags,int mode,char *lock,int op)
+{
+ int fd,ld,j;
+ int i = LOCKTIMEOUT * 60 - 1;
+ char tmp[MAILTMPLEN];
+ time_t t;
+ struct stat sb;
+ sprintf (lock,"%s.lock",file);/* build lock filename */
+ do { /* until OK or out of tries */
+ t = time (0); /* get the time now */
+ /* try to get the lock */
+ if ((ld = open(lock,O_BINARY|O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE))>=0)
+ close (ld); /* got it, close the lock file! */
+ else if (errno != EEXIST) { /* miscellaneous error */
+ sprintf (tmp,"Error creating %.80s: %s",lock,strerror (errno));
+ if (!(i%15)) mm_log (tmp,WARN);
+ }
+ /* lock exists, still active? */
+ else if (!stat (lock,&sb) && (t > sb.st_ctime + LOCKTIMEOUT * 60) &&
+ ((ld = open(lock,O_BINARY|O_WRONLY|O_CREAT,S_IREAD|S_IWRITE))>=0))
+ close (ld); /* got timed-out lock file */
+ else { /* active lock, try again */
+ if (!(i%15)) {
+ sprintf (tmp,"Mailbox %.80s is locked, will override in %d seconds...",
+ file,i);
+ mm_log (tmp,WARN);
+ }
+ sleep (1); /* wait a second before next retry */
+ }
+ } while (*lock && ld < 0 && i--);
+ /* open file */
+ if ((fd = open (file,flags,mode)) >= 0) flock (fd,op);
+ else { /* open failed */
+ j = errno; /* preserve error code */
+ if (*lock) unlink (lock); /* flush the lock file if any */
+ errno = j; /* restore error code */
+ }
+ return fd;
+}
+
+/* UNIX unlock and close mailbox
+ * Accepts: file descriptor
+ * (optional) mailbox stream to check atime/mtime
+ * (optional) lock file name
+ */
+
+void unix_unlock (int fd,MAILSTREAM *stream,char *lock)
+{
+ if (stream) { /* need to muck with times? */
+ struct stat sbuf;
+ struct utimbuf times;
+ time_t now = time (0);
+ fstat (fd,&sbuf); /* get file times */
+ if (LOCAL->ld >= 0) { /* yes, readwrite session? */
+ times.actime = now; /* set atime to now */
+ /* set mtime to (now - 1) if necessary */
+ times.modtime = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1;
+ }
+ else if (stream->recent) { /* readonly with recent messages */
+ if ((sbuf.st_atime >= sbuf.st_mtime) ||
+ (sbuf.st_atime >= sbuf.st_ctime))
+ /* keep past mtime, whack back atime */
+ times.actime = (times.modtime = (sbuf.st_mtime < now) ?
+ sbuf.st_mtime : now) - 1;
+ else now = 0; /* no time change needed */
+ }
+ /* readonly with no recent messages */
+ else if ((sbuf.st_atime < sbuf.st_mtime) ||
+ (sbuf.st_atime < sbuf.st_ctime)) {
+ times.actime = now; /* set atime to now */
+ /* set mtime to (now - 1) if necessary */
+ times.modtime = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1;
+ }
+ else now = 0; /* no time change needed */
+ /* set the times, note change */
+ if (now && !utime (stream->mailbox,&times))
+ LOCAL->filetime = times.modtime;
+ }
+ flock (fd,LOCK_UN); /* release flock'ers */
+ if (!stream) close (fd); /* close the file if no stream */
+ /* flush the lock file if any */
+ if (lock && *lock) unlink (lock);
+}
+
+/* UNIX mail parse and lock mailbox
+ * Accepts: MAIL stream
+ * space to write lock file name
+ * type of locking operation
+ * Returns: T if parse OK, critical & mailbox is locked shared; NIL if failure
+ */
+
+int unix_parse (MAILSTREAM *stream,char *lock,int op)
+{
+ int zn;
+ unsigned long i,j,k,m;
+ unsigned char c,*s,*t,*u,tmp[MAILTMPLEN],date[30];
+ int ti = 0,retain = T;
+ unsigned long nmsgs = stream->nmsgs;
+ unsigned long prevuid = nmsgs ? mail_elt (stream,nmsgs)->private.uid : 0;
+ unsigned long recent = stream->recent;
+ unsigned long oldnmsgs = stream->nmsgs;
+ short silent = stream->silent;
+ short pseudoseen = NIL;
+ struct stat sbuf;
+ STRING bs;
+ FDDATA d;
+ MESSAGECACHE *elt;
+ mail_lock (stream); /* guard against recursion or pingers */
+ /* toss out previous descriptor */
+ if (LOCAL->fd >= 0) close (LOCAL->fd);
+ mm_critical (stream); /* open and lock mailbox (shared OK) */
+ if ((LOCAL->fd = unix_lock (stream->mailbox,
+ O_BINARY + ((LOCAL->ld >= 0) ? O_RDWR:O_RDONLY),
+ NIL,lock,op)) < 0) {
+ sprintf (tmp,"Mailbox open failed, aborted: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ unix_abort (stream);
+ mail_unlock (stream);
+ mm_nocritical (stream); /* done with critical */
+ return NIL;
+ }
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ /* validate change in size */
+ if (sbuf.st_size < LOCAL->filesize) {
+ sprintf (tmp,"Mailbox shrank from %lu to %lu bytes, aborted",
+ (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size);
+ mm_log (tmp,ERROR); /* this is pretty bad */
+ unix_unlock (LOCAL->fd,stream,lock);
+ unix_abort (stream);
+ mail_unlock (stream);
+ mm_nocritical (stream); /* done with critical */
+ return NIL;
+ }
+
+ /* new data? */
+ else if (i = sbuf.st_size - LOCAL->filesize) {
+ d.fd = LOCAL->fd; /* yes, set up file descriptor */
+ d.pos = LOCAL->filesize; /* get to that position in the file */
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE; /* file chunk size */
+ INIT (&bs,fd_string,&d,i); /* initialize stringstruct */
+ /* skip leading whitespace for broken MTAs */
+ while (((c = CHR (&bs)) == '\n') || (c == '\r') ||
+ (c == ' ') || (c == '\t')) SNX (&bs);
+ if (SIZE (&bs)) { /* read new data */
+ /* remember internal header position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ s = unix_mbxline (stream,&bs,&i);
+ t = NIL,zn = 0;
+ if (i) VALID (s,t,ti,zn); /* see if valid From line */
+ if (!ti) { /* someone pulled the rug from under us */
+ sprintf (tmp,"Unexpected changes to mailbox (try restarting): %.20s",
+ (char *) s);
+ mm_log (tmp,ERROR);
+ unix_unlock (LOCAL->fd,stream,lock);
+ unix_abort (stream);
+ mail_unlock (stream);
+ mm_nocritical (stream); /* done with critical */
+ return NIL;
+ }
+ stream->silent = T; /* quell main program new message events */
+ do { /* found a message */
+ /* instantiate first new message */
+ mail_exists (stream,++nmsgs);
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ recent++; /* assume recent by default */
+ elt->recent = T;
+ /* note position/size of internal header */
+ elt->private.special.offset = j;
+ elt->private.msg.header.offset = elt->private.special.text.size = i;
+
+ /* generate plausible IMAPish date string */
+ date[2] = date[6] = date[20] = '-'; date[11] = ' ';
+ date[14] = date[17] = ':';
+ /* dd */
+ date[0] = t[ti - 2]; date[1] = t[ti - 1];
+ /* mmm */
+ date[3] = t[ti - 6]; date[4] = t[ti - 5]; date[5] = t[ti - 4];
+ /* hh */
+ date[12] = t[ti + 1]; date[13] = t[ti + 2];
+ /* mm */
+ date[15] = t[ti + 4]; date[16] = t[ti + 5];
+ if (t[ti += 6] == ':') {/* ss */
+ date[18] = t[++ti]; date[19] = t[++ti];
+ ti++; /* move to space */
+ }
+ else date[18] = date[19] = '0';
+ /* yy -- advance over timezone if necessary */
+ if (zn == ti) ti += (((t[zn+1] == '+') || (t[zn+1] == '-')) ? 6 : 4);
+ date[7] = t[ti + 1]; date[8] = t[ti + 2];
+ date[9] = t[ti + 3]; date[10] = t[ti + 4];
+ /* zzz */
+ t = zn ? (t + zn + 1) : (unsigned char *) "LCL";
+ date[21] = *t++; date[22] = *t++; date[23] = *t++;
+ if ((date[21] != '+') && (date[21] != '-')) date[24] = '\0';
+ else { /* numeric time zone */
+ date[24] = *t++; date[25] = *t++;
+ date[26] = '\0'; date[20] = ' ';
+ }
+ /* set internal date */
+ if (!mail_parse_date (elt,date)) {
+ sprintf (tmp,"Unable to parse internal date: %s",(char *) date);
+ mm_log (tmp,WARN);
+ }
+
+ do { /* look for message body */
+ s = t = unix_mbxline (stream,&bs,&i);
+ if (i) switch (*s) { /* check header lines */
+ case 'X': /* possible X-???: line */
+ if (s[1] == '-') { /* must be immediately followed by hyphen */
+ /* X-Status: becomes Status: in S case */
+ if (s[2] == 'S' && s[3] == 't' && s[4] == 'a' && s[5] == 't' &&
+ s[6] == 'u' && s[7] == 's' && s[8] == ':') s += 2;
+ /* possible X-Keywords */
+ else if (s[2] == 'K' && s[3] == 'e' && s[4] == 'y' &&
+ s[5] == 'w' && s[6] == 'o' && s[7] == 'r' &&
+ s[8] == 'd' && s[9] == 's' && s[10] == ':') {
+ SIZEDTEXT uf;
+ retain = NIL; /* don't retain continuation */
+ s += 11; /* flush leading whitespace */
+ while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n'))){
+ while (*s == ' ') s++;
+ /* find end of keyword */
+ if (!(u = strpbrk (s," \n\r"))) u = s + strlen (s);
+ /* got a keyword? */
+ if ((k = (u - s)) && (k <= MAXUSERFLAG)) {
+ uf.data = (unsigned char *) s;
+ uf.size = k;
+ for (j = 0; (j < NUSERFLAGS) && stream->user_flags[j]; ++j)
+ if (!compare_csizedtext (stream->user_flags[j],&uf)) {
+ elt->user_flags |= ((long) 1) << j;
+ break;
+ }
+ }
+ s = u; /* advance to next keyword */
+ }
+ break;
+ }
+
+ /* possible X-IMAP */
+ else if ((s[2] == 'I') && (s[3] == 'M') && (s[4] == 'A') &&
+ (s[5] == 'P') && ((m = (s[6] == ':')) ||
+ ((s[6] == 'b') && (s[7] == 'a') &&
+ (s[8] == 's') && (s[9] == 'e') &&
+ (s[10] == ':')))) {
+ retain = NIL; /* don't retain continuation */
+ if ((nmsgs == 1) && !stream->uid_validity) {
+ /* advance to data */
+ s += m ? 7 : 11;
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ j = 0; /* slurp UID validity */
+ /* found a digit? */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ /* must have valid UID validity and UID last */
+ if (j && isdigit (*s)) {
+ /* pseudo-header seen if X-IMAP */
+ if (m) pseudoseen = LOCAL->pseudo = T;
+ /* save UID validity */
+ stream->uid_validity = j;
+ j = 0; /* slurp UID last */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* save UID last */
+ stream->uid_last = j;
+ /* process keywords */
+ for (j = 0; (*s != '\n') && ((*s != '\r')||(s[1] != '\n'));
+ s = u,j++) {
+ /* flush leading whitespace */
+ while (*s == ' ') s++;
+ u = strpbrk (s," \n\r");
+ /* got a keyword? */
+ if ((j < NUSERFLAGS) && (k = (u - s)) &&
+ (k <= MAXUSERFLAG)) {
+ if (stream->user_flags[j])
+ fs_give ((void **) &stream->user_flags[j]);
+ stream->user_flags[j] = (char *) fs_get (k + 1);
+ strncpy (stream->user_flags[j],s,k);
+ stream->user_flags[j][k] = '\0';
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ /* possible X-UID */
+ else if (s[2] == 'U' && s[3] == 'I' && s[4] == 'D' &&
+ s[5] == ':') {
+ retain = NIL; /* don't retain continuation */
+ /* only believe if have a UID validity */
+ if (stream->uid_validity && ((nmsgs > 1) || !pseudoseen)) {
+ s += 6; /* advance to UID value */
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ j = 0;
+ /* found a digit? */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* flush remainder of line */
+ while (*s != '\n') s++;
+ /* make sure not duplicated */
+ if (elt->private.uid)
+ sprintf (tmp,"Message %lu UID %lu already has UID %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,elt->private.uid);
+ /* make sure UID doesn't go backwards */
+ else if (j <= prevuid)
+ sprintf (tmp,"Message %lu UID %lu less than %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,prevuid + 1);
+#if 0 /* this is currently broken by UIDPLUS */
+ /* or skip by mailbox's recorded last */
+ else if (j > stream->uid_last)
+ sprintf (tmp,"Message %lu UID %lu greater than last %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,stream->uid_last);
+#endif
+ else { /* normal UID case */
+ prevuid = elt->private.uid = j;
+#if 1 /* temporary kludge for UIDPLUS */
+ if (prevuid > stream->uid_last) {
+ stream->uid_last = prevuid;
+ LOCAL->ddirty = LOCAL->dirty = T;
+ }
+#endif
+ break; /* exit this cruft */
+ }
+ mm_log (tmp,WARN);
+ /* invalidate UID validity */
+ stream->uid_validity = 0;
+ elt->private.uid = 0;
+ }
+ break;
+ }
+ }
+ /* otherwise fall into S case */
+
+ case 'S': /* possible Status: line */
+ if (s[0] == 'S' && s[1] == 't' && s[2] == 'a' && s[3] == 't' &&
+ s[4] == 'u' && s[5] == 's' && s[6] == ':') {
+ retain = NIL; /* don't retain continuation */
+ s += 6; /* advance to status flags */
+ do switch (*s++) {/* parse flags */
+ case 'R': /* message read */
+ elt->seen = T;
+ break;
+ case 'O': /* message old */
+ if (elt->recent) {
+ elt->recent = NIL;
+ recent--; /* it really wasn't recent */
+ }
+ break;
+ case 'D': /* message deleted */
+ elt->deleted = T;
+ break;
+ case 'F': /* message flagged */
+ elt->flagged = T;
+ break;
+ case 'A': /* message answered */
+ elt->answered = T;
+ break;
+ case 'T': /* message is a draft */
+ elt->draft = T;
+ break;
+ default: /* some other crap */
+ break;
+ } while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n')));
+ break; /* all done */
+ }
+ /* otherwise fall into default case */
+
+ default: /* ordinary header line */
+ if ((*s == 'S') || (*s == 's') ||
+ (((*s == 'X') || (*s == 'x')) && (s[1] == '-'))) {
+ unsigned char *e,*v;
+ /* must match what mail_filter() does */
+ for (u = s,v = tmp,e = u + min (i,MAILTMPLEN - 1);
+ (u < e) && ((c = (*u ? *u : (*u = ' '))) != ':') &&
+ ((c > ' ') || ((c != ' ') && (c != '\t') &&
+ (c != '\r') && (c != '\n')));
+ *v++ = *u++);
+ *v = '\0'; /* tie off */
+ /* matches internal header? */
+ if (!compare_cstring (tmp,"STATUS") ||
+ !compare_cstring (tmp,"X-STATUS") ||
+ !compare_cstring (tmp,"X-KEYWORDS") ||
+ !compare_cstring (tmp,"X-UID") ||
+ !compare_cstring (tmp,"X-IMAP") ||
+ !compare_cstring (tmp,"X-IMAPBASE")) {
+ char err[MAILTMPLEN];
+ sprintf (err,"Discarding bogus %s header in message %lu",
+ (char *) tmp,elt->msgno);
+ mm_log (err,WARN);
+ retain = NIL; /* don't retain continuation */
+ break; /* different case or something */
+ }
+ }
+ /* retain or non-continuation? */
+ if (retain || ((*s != ' ') && (*s != '\t'))) {
+ retain = T; /* retaining continuation now */
+ /* line length in CRLF format newline */
+ k = i + (((i < 2) || (s[i - 2] != '\r')) ? 1 : 0);
+ /* header size */
+ elt->rfc822_size = elt->private.spare.data += k;
+ }
+ else {
+ char err[MAILTMPLEN];
+ sprintf (err,"Discarding bogus continuation in msg %lu: %.80s",
+ elt->msgno,(char *) s);
+ if (u = strpbrk (err,"\r\n")) *u = '\0';
+ mm_log (err,WARN);
+ break; /* different case or something */
+ }
+ break;
+ }
+ } while (i && (*t != '\n') && ((*t != '\r') || (t[1] != '\n')));
+ /* "internal" header sans trailing newline */
+ if (i) elt->private.spare.data -= 2;
+ /* assign a UID if none found */
+ if (((nmsgs > 1) || !pseudoseen) && !elt->private.uid) {
+ prevuid = elt->private.uid = ++stream->uid_last;
+ elt->private.dirty = T;
+ LOCAL->ddirty = T; /* force update */
+ }
+ else elt->private.dirty = elt->recent;
+
+ /* note size of header, location of text */
+ elt->private.msg.header.text.size =
+ (elt->private.msg.text.offset =
+ (LOCAL->filesize + GETPOS (&bs)) - elt->private.special.offset) -
+ elt->private.special.text.size;
+ k = m = 0; /* no previous line size yet */
+ /* note current position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ if (i) do { /* look for next message */
+ s = unix_mbxline (stream,&bs,&i);
+ if (i) { /* got new data? */
+ VALID (s,t,ti,zn); /* yes, parse line */
+ if (!ti) { /* not a header line, add it to message */
+ if (s[i - 1] == '\n')
+ elt->rfc822_size +=
+ k = i + (m = (((i < 2) || s[i - 2] != '\r') ? 1 : 0));
+ else { /* file does not end with newline! */
+ elt->rfc822_size += i;
+ k = m = 0;
+ }
+ /* update current position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ }
+ }
+ } while (i && !ti); /* until found a header */
+ elt->private.msg.text.text.size = j -
+ (elt->private.special.offset + elt->private.msg.text.offset);
+ if (k == 2) { /* last line was blank? */
+ elt->private.msg.text.text.size -= (m ? 1 : 2);
+ elt->rfc822_size -= 2;
+ }
+ /* until end of buffer */
+ } while (!stream->sniff && i);
+ if (pseudoseen) { /* flush pseudo-message if present */
+ /* decrement recent count */
+ if (mail_elt (stream,1)->recent) recent--;
+ /* and the exists count */
+ mail_exists (stream,nmsgs--);
+ mail_expunged(stream,1);/* fake an expunge of that message */
+ }
+ /* need to start a new UID validity? */
+ if (!stream->uid_validity) {
+ stream->uid_validity = (unsigned long) time (0);
+ if (nmsgs) { /* don't bother if empty file */
+ /* make dirty to restart UID epoch */
+ LOCAL->ddirty = LOCAL->dirty = T;
+ /* need to rewrite msg 1 if not pseudo */
+ if (!LOCAL->pseudo) mail_elt (stream,1)->private.dirty = T;
+ mm_log ("Assigning new unique identifiers to all messages",NIL);
+ }
+ }
+ stream->nmsgs = oldnmsgs; /* whack it back down */
+ stream->silent = silent; /* restore old silent setting */
+ /* notify upper level of new mailbox sizes */
+ mail_exists (stream,nmsgs);
+ mail_recent (stream,recent);
+ /* mark dirty so O flags are set */
+ if (recent) LOCAL->dirty = T;
+ }
+ }
+ /* no change, don't babble if never got time */
+ else if (LOCAL->filetime && LOCAL->filetime != sbuf.st_mtime)
+ mm_log ("New mailbox modification time but apparently no changes",WARN);
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ LOCAL->filetime = sbuf.st_mtime;
+ return T; /* return the winnage */
+}
+
+/* UNIX read line from mailbox
+ * Accepts: mail stream
+ * stringstruct
+ * pointer to line size
+ * Returns: pointer to input line
+ */
+
+char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size)
+{
+ unsigned long i,j,k,m;
+ char *s,*t,*te;
+ char *ret = "";
+ /* flush old buffer */
+ if (LOCAL->line) fs_give ((void **) &LOCAL->line);
+ /* if buffer needs refreshing */
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ if (SIZE (bs)) { /* find newline */
+ /* end of fast scan */
+ te = (t = (s = bs->curpos) + bs->cursize) - 12;
+ while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) {
+ --s; /* back up */
+ break; /* exit loop */
+ }
+ /* final character-at-a-time scan */
+ while ((s < t) && (*s != '\n')) ++s;
+ /* difficult case if line spans buffer */
+ if ((i = s - bs->curpos) == bs->cursize) {
+ /* have space in line buffer? */
+ if (i > LOCAL->linebuflen) {
+ fs_give ((void **) &LOCAL->linebuf);
+ LOCAL->linebuf = (char *) fs_get (LOCAL->linebuflen = i);
+ }
+ /* remember what we have so far */
+ memcpy (LOCAL->linebuf,bs->curpos,i);
+ /* load next buffer */
+ SETPOS (bs,k = GETPOS (bs) + i);
+ /* end of fast scan */
+ te = (t = (s = bs->curpos) + bs->cursize) - 12;
+ /* fast scan in overlap buffer */
+ while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) {
+ --s; /* back up */
+ break; /* exit loop */
+ }
+
+ /* final character-at-a-time scan */
+ while ((s < t) && (*s != '\n')) ++s;
+ /* huge line? */
+ if ((j = s - bs->curpos) == bs->cursize) {
+ SETPOS (bs,GETPOS (bs) + j);
+ /* look for end of line (s-l-o-w!!) */
+ for (m = SIZE (bs); m && (SNX (bs) != '\n'); --m,++j);
+ SETPOS (bs,k); /* go back to where it started */
+ }
+ /* got size of data, make buffer for return */
+ ret = LOCAL->line = (char *) fs_get (i + j + 2);
+ /* copy first chunk */
+ memcpy (ret,LOCAL->linebuf,i);
+ while (j) { /* copy remainder */
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ memcpy (ret + i,bs->curpos,k = min (j,bs->cursize));
+ i += k; /* account for this much read in */
+ j -= k;
+ bs->curpos += k; /* increment new position */
+ bs->cursize -= k; /* eat that many bytes */
+ }
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ /* read newline at end */
+ if (SIZE (bs)) ret[i++] = SNX (bs);
+ ret[i] = '\0'; /* makes debugging easier */
+ }
+ else { /* this is easy */
+ ret = bs->curpos; /* string it at this position */
+ bs->curpos += ++i; /* increment new position */
+ bs->cursize -= i; /* eat that many bytes */
+ }
+ *size = i; /* return that to user */
+ }
+ else *size = 0; /* end of data, return empty */
+ return ret;
+}
+
+/* UNIX make pseudo-header
+ * Accepts: MAIL stream
+ * buffer to write pseudo-header
+ * Returns: length of pseudo-header
+ */
+
+unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr)
+{
+ int i;
+ char *s,*t,tmp[MAILTMPLEN];
+ time_t now = time(0);
+ rfc822_fixed_date (tmp);
+ sprintf (hdr,"From %s %.24s\r\nDate: %s\r\nFrom: %s <%s@%.80s>\r\nSubject: %s\r\nMessage-ID: <%lu@%.80s>\r\nX-IMAP: %010ld %010ld",
+ pseudo_from,ctime (&now),
+ tmp,pseudo_name,pseudo_from,mylocalhost (),pseudo_subject,
+ (unsigned long) now,mylocalhost (),stream->uid_validity,
+ stream->uid_last);
+ for (t = hdr + strlen (hdr),i = 0; i < NUSERFLAGS; ++i)
+ if (stream->user_flags[i])
+ sprintf (t += strlen (t)," %s",stream->user_flags[i]);
+ strcpy (t += strlen (t),"\r\nStatus: RO\r\n\r\n");
+ for (s = pseudo_msg,t += strlen (t); *s; *t++ = *s++)
+ if (*s == '\n') *t++ = '\r';
+ *t++ = '\r'; *t++ = '\n'; *t++ = '\r'; *t++ = '\n';
+ *t = '\0'; /* tie off pseudo header */
+ return t - hdr; /* return length of pseudo header */
+}
+
+/* UNIX make status string
+ * Accepts: MAIL stream
+ * destination string to write
+ * message cache entry
+ * UID to write if non-zero (else use elt->private.uid)
+ * non-zero flag to write UID (.LT. 0 to write UID base info too)
+ * Returns: length of string
+ */
+
+unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt,
+ unsigned long uid,long flag)
+{
+ char *t,stack[64];
+ char *s = status;
+ unsigned long n;
+ unsigned long pad = 50;
+ /* This used to use sprintf(), but thanks to certain cretinous C libraries
+ with horribly slow implementations of sprintf() I had to change it to this
+ mess. At least it should be fast. */
+ *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't'; *s++ = 'u'; *s++ = 's';
+ *s++ = ':'; *s++ = ' ';
+ if (elt->seen) *s++ = 'R';
+ /* only write O if have a UID */
+ if (flag && (!elt->recent || LOCAL->appending)) *s++ = 'O';
+ *s++ = '\r'; *s++ = '\n';
+ *s++ = 'X'; *s++ = '-'; *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't';
+ *s++ = 'u'; *s++ = 's'; *s++ = ':'; *s++ = ' ';
+ if (elt->deleted) *s++ = 'D';
+ if (elt->flagged) *s++ = 'F';
+ if (elt->answered) *s++ = 'A';
+ if (elt->draft) *s++ = 'T';
+ *s++ = '\r'; *s++ = '\n';
+
+ *s++ = 'X'; *s++ = '-'; *s++ = 'K'; *s++ = 'e'; *s++ = 'y'; *s++ = 'w';
+ *s++ = 'o'; *s++ = 'r'; *s++ = 'd'; *s++ = 's'; *s++ = ':';
+ if (n = elt->user_flags) do {
+ *s++ = ' ';
+ for (t = stream->user_flags[find_rightmost_bit (&n)]; *t; *s++ = *t++);
+ } while (n);
+ n = s - status; /* get size of stuff so far */
+ /* pad X-Keywords to make size constant */
+ if (n < pad) for (n = pad - n; n > 0; --n) *s++ = ' ';
+ *s++ = '\r'; *s++ = '\n';
+ if (flag) { /* want to include UID? */
+ t = stack;
+ /* push UID digits on the stack */
+ n = uid ? uid : elt->private.uid;
+ do *t++ = (char) (n % 10) + '0';
+ while (n /= 10);
+ *s++ = 'X'; *s++ = '-'; *s++ = 'U'; *s++ = 'I'; *s++ = 'D'; *s++ = ':';
+ *s++ = ' ';
+ /* pop UID from stack */
+ while (t > stack) *s++ = *--t;
+ *s++ = '\r'; *s++ = '\n';
+ }
+ /* end of extended message status */
+ *s++ = '\r'; *s++ = '\n'; *s = '\0';
+ return s - status; /* return size of resulting string */
+}
+
+/* Rewrite mailbox file
+ * Accepts: MAIL stream, must be critical and locked
+ * return pointer to number of expunged messages if want expunge
+ * lock file name
+ * expunge sequence, not deleted flag
+ * Returns: T if success and mailbox unlocked, NIL if failure
+ */
+
+#define OVERFLOWBUFLEN 8192 /* initial overflow buffer length */
+
+long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,char *lock,
+ long flags)
+{
+ MESSAGECACHE *elt;
+ UNIXFILE f;
+ char *s;
+ struct utimbuf times;
+ long ret,flag;
+ unsigned long i,j;
+ unsigned long recent = stream->recent;
+ unsigned long size = LOCAL->pseudo ? unix_pseudo (stream,LOCAL->buf) : 0;
+ if (nexp) *nexp = 0; /* initially nothing expunged */
+ /* calculate size of mailbox after rewrite */
+ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs; i++) {
+ elt = mail_elt (stream,i); /* get cache */
+ if (!(nexp && elt->deleted && (flags ? elt->sequence : T))) {
+ /* add RFC822 size of this message */
+ size += elt->private.special.text.size + elt->private.spare.data +
+ unix_xstatus (stream,LOCAL->buf,elt,NIL,flag) +
+ elt->private.msg.text.text.size + 2;
+ flag = 1; /* only count X-IMAPbase once */
+ }
+ }
+ if (!size) { /* no messages and no pseudo, make one now */
+ size = unix_pseudo (stream,LOCAL->buf);
+ LOCAL->pseudo = T;
+ }
+ /* extend the file as necessary */
+ if (ret = unix_extend (stream,size)) {
+ /* Set up buffered I/O file structure
+ * curpos current position being written through buffering
+ * filepos current position being written physically to the disk
+ * bufpos current position being written in the buffer
+ * protect current maximum position that can be written to the disk
+ * before buffering is forced
+ * The code tries to buffer so that that disk is written in multiples of
+ * OVERBLOWBUFLEN bytes.
+ */
+ f.stream = stream; /* note mail stream */
+ f.curpos = f.filepos = 0; /* start of file */
+ f.protect = stream->nmsgs ? /* initial protection pointer */
+ mail_elt (stream,1)->private.special.offset : 8192;
+ f.bufpos = f.buf = (char *) fs_get (f.buflen = OVERFLOWBUFLEN);
+
+ if (LOCAL->pseudo) /* update pseudo-header */
+ unix_write (&f,LOCAL->buf,unix_pseudo (stream,LOCAL->buf));
+ /* loop through all messages */
+ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs;) {
+ elt = mail_elt (stream,i);/* get cache */
+ /* expunge this message? */
+ if (nexp && elt->deleted && (flags ? elt->sequence : T)) {
+ /* one less recent message */
+ if (elt->recent) --recent;
+ mail_expunged(stream,i);/* notify upper levels */
+ ++*nexp; /* count up one more expunged message */
+ }
+ else { /* preserve this message */
+ i++; /* advance to next message */
+ if ((flag < 0) || /* need to rewrite message? */
+ elt->private.dirty ||
+ (((unsigned long) f.curpos) != elt->private.special.offset) ||
+ (elt->private.msg.header.text.size !=
+ (elt->private.spare.data +
+ unix_xstatus (stream,LOCAL->buf,elt,NIL,flag)))) {
+ unsigned long newoffset = f.curpos;
+ /* yes, seek to internal header */
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size);
+ /* protection pointer moves to RFC822 header */
+ f.protect = elt->private.special.offset +
+ elt->private.msg.header.offset;
+ /* write internal header */
+ unix_write (&f,LOCAL->buf,elt->private.special.text.size);
+ /* get RFC822 header */
+ s = unix_header (stream,elt->msgno,&j,NIL);
+ /* in case this got decremented */
+ elt->private.msg.header.offset = elt->private.special.text.size;
+ /* header size, sans trailing newline */
+ if ((j < 4) || (s[j - 4] == '\r')) j -= 2;
+ if (j != elt->private.spare.data) fatal ("header size inconsistent");
+ /* protection pointer moves to RFC822 text */
+ f.protect = elt->private.special.offset +
+ elt->private.msg.text.offset;
+ unix_write (&f,s,j); /* write RFC822 header */
+ /* write status and UID */
+ unix_write (&f,LOCAL->buf,
+ j = unix_xstatus (stream,LOCAL->buf,elt,NIL,flag));
+ flag = 1; /* only write X-IMAPbase once */
+ /* new file header size */
+ elt->private.msg.header.text.size = elt->private.spare.data + j;
+
+ /* did text move? */
+ if (f.curpos != f.protect) {
+ /* get message text */
+ s = unix_text_work (stream,elt,&j,FT_INTERNAL);
+ /* can't happen it says here */
+ if (j > elt->private.msg.text.text.size)
+ fatal ("text size inconsistent");
+ /* new text offset, status/UID may change it */
+ elt->private.msg.text.offset = f.curpos - newoffset;
+ /* protection pointer moves to next message */
+ f.protect = (i <= stream->nmsgs) ?
+ mail_elt (stream,i)->private.special.offset : (f.curpos + j + 2);
+ unix_write (&f,s,j);/* write text */
+ /* write trailing newline */
+ unix_write (&f,"\r\n",2);
+ }
+ else { /* tie off header and status */
+ unix_write (&f,NIL,NIL);
+ /* protection pointer moves to next message */
+ f.protect = (i <= stream->nmsgs) ?
+ mail_elt (stream,i)->private.special.offset : size;
+ /* locate end of message text */
+ j = f.filepos + elt->private.msg.text.text.size;
+ /* trailing newline already there? */
+ if (f.protect == (off_t) (j + 2)) f.curpos = f.filepos = f.protect;
+ else { /* trailing newline missing, write it */
+ f.curpos = f.filepos = j;
+ unix_write (&f,"\r\n",2);
+ }
+ }
+ /* new internal header offset */
+ elt->private.special.offset = newoffset;
+ elt->private.dirty =NIL;/* message is now clean */
+ }
+ else { /* no need to rewrite this message */
+ /* tie off previous message if needed */
+ unix_write (&f,NIL,NIL);
+ /* protection pointer moves to next message */
+ f.protect = (i <= stream->nmsgs) ?
+ mail_elt (stream,i)->private.special.offset : size;
+ /* locate end of message text */
+ j = f.filepos + elt->private.special.text.size +
+ elt->private.msg.header.text.size +
+ elt->private.msg.text.text.size;
+ /* trailing newline already there? */
+ if (f.protect == (off_t) (j + 2)) f.curpos = f.filepos = f.protect;
+ else { /* trailing newline missing, write it */
+ f.curpos = f.filepos = j;
+ unix_write (&f,"\r\n",2);
+ }
+ }
+ }
+ }
+
+ unix_write (&f,NIL,NIL); /* tie off final message */
+ if (size != ((unsigned long) f.filepos)) fatal ("file size inconsistent");
+ fs_give ((void **) &f.buf); /* free buffer */
+ /* make sure tied off */
+ ftruncate (LOCAL->fd,LOCAL->filesize = size);
+ fsync (LOCAL->fd); /* make sure the updates take */
+ if (size && (flag < 0)) fatal ("lost UID base information");
+ /* no longer dirty */
+ LOCAL->ddirty = LOCAL->dirty = NIL;
+ /* notify upper level of new mailbox sizes */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ /* set atime to now, mtime a second earlier */
+ times.modtime = (times.actime = time (0)) -1;
+ /* set the times, note change */
+ if (!utime (stream->mailbox,&times)) LOCAL->filetime = times.modtime;
+ /* flush the lock file */
+ unix_unlock (LOCAL->fd,stream,lock);
+ }
+ return ret; /* return state from algorithm */
+}
+
+/* Extend UNIX mailbox file
+ * Accepts: MAIL stream
+ * new desired size
+ * Return: T if success, else NIL
+ */
+
+long unix_extend (MAILSTREAM *stream,unsigned long size)
+{
+ unsigned long i = (size > ((unsigned long) LOCAL->filesize)) ?
+ size - ((unsigned long) LOCAL->filesize) : 0;
+ if (i) { /* does the mailbox need to grow? */
+ if (i > LOCAL->buflen) { /* make sure have enough space */
+ /* this user won the lottery all right */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1);
+ }
+ memset (LOCAL->buf,'\0',i); /* get a block of nulls */
+ while (T) { /* until write successful or punt */
+ lseek (LOCAL->fd,LOCAL->filesize,L_SET);
+ if ((write (LOCAL->fd,LOCAL->buf,i) >= 0) && !fsync (LOCAL->fd)) break;
+ else {
+ long e = errno; /* note error before doing ftruncate */
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ if (mm_diskerror (stream,e,NIL)) {
+ fsync (LOCAL->fd); /* user chose to punt */
+ sprintf (LOCAL->buf,"Unable to extend mailbox: %s",strerror (e));
+ if (!stream->silent) mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ }
+ }
+ }
+ return LONGT;
+}
+
+/* Write data to buffered file
+ * Accepts: buffered file pointer
+ * file data or NIL to indicate "flush buffer"
+ * date size (ignored for "flush buffer")
+ * Does not return until success
+ */
+
+void unix_write (UNIXFILE *f,char *buf,unsigned long size)
+{
+ unsigned long i,j,k;
+ if (buf) { /* doing buffered write? */
+ i = f->bufpos - f->buf; /* yes, get size of current buffer data */
+ /* yes, have space in current buffer chunk? */
+ if (j = i ? ((f->buflen - i) % OVERFLOWBUFLEN) : f->buflen) {
+ /* yes, fill up buffer as much as we can */
+ memcpy (f->bufpos,buf,k = min (j,size));
+ f->bufpos += k; /* new buffer position */
+ f->curpos += k; /* new current position */
+ if (j -= k) return; /* all done if still have buffer free space */
+ buf += k; /* full, get new unwritten data pointer */
+ size -= k; /* new data size */
+ i += k; /* new buffer data size */
+ }
+ /* This chunk of the buffer is full. See if can make some space by
+ * writing to the disk, if there's enough unprotected space to do so.
+ * Try to fill out any unaligned chunk, along with any subsequent full
+ * chunks that will fit in unprotected space.
+ */
+ /* any unprotected space we can write to? */
+ if (j = min (i,f->protect - f->filepos)) {
+ /* yes, filepos not at chunk boundary? */
+ if ((k = f->filepos % OVERFLOWBUFLEN) && ((k = OVERFLOWBUFLEN - k) < j))
+ j -= k; /* yes, and can write out partial chunk */
+ else k = 0; /* no partial chunk to write */
+ /* if at least a chunk free, write that too */
+ if (j > OVERFLOWBUFLEN) k += j - (j % OVERFLOWBUFLEN);
+ if (k) { /* write data if there is anything we can */
+ unix_phys_write (f,f->buf,k);
+ /* slide buffer */
+ if (i -= k) memmove (f->buf,f->buf + k,i);
+ f->bufpos = f->buf + i; /* new end of buffer */
+ }
+ }
+
+ /* Have flushed the buffer as best as possible. All done if no more
+ * data to write. Otherwise, if the buffer is empty AND if the unwritten
+ * data is larger than a chunk AND the unprotected space is also larger
+ * than a chunk, then write as many chunks as we can directly from the
+ * data. Buffer the rest, expanding the buffer as needed.
+ */
+ if (size) { /* have more data that we need to buffer? */
+ /* can write any of it to disk instead? */
+ if ((f->bufpos == f->buf) &&
+ ((j = min (f->protect - f->filepos,size)) > OVERFLOWBUFLEN)) {
+ /* write as much as we can right now */
+ unix_phys_write (f,buf,j -= (j % OVERFLOWBUFLEN));
+ buf += j; /* new data pointer */
+ size -= j; /* new data size */
+ f->curpos += j; /* advance current pointer */
+ }
+ if (size) { /* still have data that we need to buffer? */
+ /* yes, need to expand the buffer? */
+ if ((i = ((f->bufpos + size) - f->buf)) > f->buflen) {
+ /* note current position in buffer */
+ j = f->bufpos - f->buf;
+ i += OVERFLOWBUFLEN; /* yes, grow another chunk */
+ fs_resize ((void **) &f->buf,f->buflen = i - (i % OVERFLOWBUFLEN));
+ /* in case buffer relocated */
+ f->bufpos = f->buf + j;
+ }
+ /* buffer remaining data */
+ memcpy (f->bufpos,buf,size);
+ f->bufpos += size; /* new end of buffer */
+ f->curpos += size; /* advance current pointer */
+ }
+ }
+ }
+ else { /* flush buffer to disk */
+ unix_phys_write (f,f->buf,i = f->bufpos - f->buf);
+ f->bufpos = f->buf; /* reset buffer */
+ /* update positions */
+ f->curpos = f->protect = f->filepos;
+ }
+}
+
+/* Physical disk write
+ * Accepts: buffered file pointer
+ * buffer address
+ * buffer size
+ * Does not return until success
+ */
+
+void unix_phys_write (UNIXFILE *f,char *buf,size_t size)
+{
+ MAILSTREAM *stream = f->stream;
+ /* write data at desired position */
+ while (size && ((lseek (LOCAL->fd,f->filepos,L_SET) < 0) ||
+ (write (LOCAL->fd,buf,size) < 0))) {
+ int e;
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Unable to write to mailbox: %s",strerror (e = errno));
+ mm_log (tmp,ERROR);
+ mm_diskerror (NIL,e,T); /* serious problem, must retry */
+ }
+ f->filepos += size; /* update file position */
+}
diff --git a/imap/src/osdep/nt/unixnt.h b/imap/src/osdep/nt/unixnt.h
new file mode 100644
index 00000000..1305184f
--- /dev/null
+++ b/imap/src/osdep/nt/unixnt.h
@@ -0,0 +1,161 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 20 December 1989
+ * Last Edited: 30 August 2006
+ */
+
+
+/* DEDICATION
+ *
+ * This file is dedicated to my dog, Unix, also known as Yun-chan and
+ * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix
+ * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after
+ * a two-month bout with cirrhosis of the liver.
+ *
+ * He was a dear friend, and I miss him terribly.
+ *
+ * Lift a leg, Yunie. Luv ya forever!!!!
+ */
+
+/* Validate line
+ * Accepts: pointer to candidate string to validate as a From header
+ * return pointer to end of date/time field
+ * return pointer to offset from t of time (hours of ``mmm dd hh:mm'')
+ * return pointer to offset from t of time zone (if non-zero)
+ * Returns: t,ti,zn set if valid From string, else ti is NIL
+ */
+
+#define VALID(s,x,ti,zn) { \
+ ti = 0; \
+ if ((*s == 'F') && (s[1] == 'r') && (s[2] == 'o') && (s[3] == 'm') && \
+ (s[4] == ' ')) { \
+ for (x = s + 5; *x && *x != '\012'; x++); \
+ if (*x) { \
+ if (x[-1] == '\015') --x; \
+ if (x - s >= 41) { \
+ for (zn = -1; x[zn] != ' '; zn--); \
+ if ((x[zn-1] == 'm') && (x[zn-2] == 'o') && (x[zn-3] == 'r') && \
+ (x[zn-4] == 'f') && (x[zn-5] == ' ') && (x[zn-6] == 'e') && \
+ (x[zn-7] == 't') && (x[zn-8] == 'o') && (x[zn-9] == 'm') && \
+ (x[zn-10] == 'e') && (x[zn-11] == 'r') && (x[zn-12] == ' '))\
+ x += zn - 12; \
+ } \
+ if (x - s >= 27) { \
+ if (x[-5] == ' ') { \
+ if (x[-8] == ':') zn = 0,ti = -5; \
+ else if (x[-9] == ' ') ti = zn = -9; \
+ else if ((x[-11] == ' ') && ((x[-10]=='+') || (x[-10]=='-'))) \
+ ti = zn = -11; \
+ } \
+ else if (x[-4] == ' ') { \
+ if (x[-9] == ' ') zn = -4,ti = -9; \
+ } \
+ else if (x[-6] == ' ') { \
+ if ((x[-11] == ' ') && ((x[-5] == '+') || (x[-5] == '-'))) \
+ zn = -6,ti = -11; \
+ } \
+ if (ti && !((x[ti - 3] == ':') && \
+ (x[ti -= ((x[ti - 6] == ':') ? 9 : 6)] == ' ') && \
+ (x[ti - 3] == ' ') && (x[ti - 7] == ' ') && \
+ (x[ti - 11] == ' '))) ti = 0; \
+ } \
+ } \
+ } \
+}
+
+/* You are not expected to understand this macro, but read the next page if
+ * you are not faint of heart.
+ *
+ * Known formats to the VALID macro are:
+ * From user Wed Dec 2 05:53 1992
+ * BSD From user Wed Dec 2 05:53:22 1992
+ * SysV From user Wed Dec 2 05:53 PST 1992
+ * rn From user Wed Dec 2 05:53:22 PST 1992
+ * From user Wed Dec 2 05:53 -0700 1992
+ * emacs From user Wed Dec 2 05:53:22 -0700 1992
+ * From user Wed Dec 2 05:53 1992 PST
+ * From user Wed Dec 2 05:53:22 1992 PST
+ * From user Wed Dec 2 05:53 1992 -0700
+ * Solaris From user Wed Dec 2 05:53:22 1992 -0700
+ *
+ * Plus all of the above with `` remote from xxx'' after it. Thank you very
+ * much, smail and Solaris, for making my life considerably more complicated.
+ */
+
+/*
+ * What? You want to understand the VALID macro anyway? Alright, since you
+ * insist. Actually, it isn't really all that difficult, provided that you
+ * take it step by step.
+ *
+ * Line 1 Initializes the return ti value to failure (0);
+ * Lines 2-3 Validates that the 1st-5th characters are ``From ''.
+ * Lines 4-6 Validates that there is an end of line and points x at it.
+ * Lines 7-14 First checks to see if the line is at least 41 characters long.
+ * If so, it scans backwards to find the rightmost space. From
+ * that point, it scans backwards to see if the string matches
+ * `` remote from''. If so, it sets x to point to the space at
+ * the start of the string.
+ * Line 15 Makes sure that there are at least 27 characters in the line.
+ * Lines 16-21 Checks if the date/time ends with the year (there is a space
+ * five characters back). If there is a colon three characters
+ * further back, there is no timezone field, so zn is set to 0
+ * and ti is set in front of the year. Otherwise, there must
+ * either to be a space four characters back for a three-letter
+ * timezone, or a space six characters back followed by a + or -
+ * for a numeric timezone; in either case, zn and ti become the
+ * offset of the space immediately before it.
+ * Lines 22-24 Are the failure case for line 14. If there is a space four
+ * characters back, it is a three-letter timezone; there must be a
+ * space for the year nine characters back. zn is the zone
+ * offset; ti is the offset of the space.
+ * Lines 25-28 Are the failure case for line 20. If there is a space six
+ * characters back, it is a numeric timezone; there must be a
+ * space eleven characters back and a + or - five characters back.
+ * zn is the zone offset; ti is the offset of the space.
+ * Line 29-32 If ti is valid, make sure that the string before ti is of the
+ * form www mmm dd hh:mm or www mmm dd hh:mm:ss, otherwise
+ * invalidate ti. There must be a colon three characters back
+ * and a space six or nine characters back (depending upon
+ * whether or not the character six characters back is a colon).
+ * There must be a space three characters further back (in front
+ * of the day), one seven characters back (in front of the month),
+ * and one eleven characters back (in front of the day of week).
+ * ti is set to be the offset of the space before the time.
+ *
+ * Why a macro? It gets invoked a *lot* in a tight loop. On some of the
+ * newer pipelined machines it is faster being open-coded than it would be if
+ * subroutines are called.
+ *
+ * Why does it scan backwards from the end of the line, instead of doing the
+ * much easier forward scan? There is no deterministic way to parse the
+ * ``user'' field, because it may contain unquoted spaces! Yes, I tested it to
+ * see if unquoted spaces were possible. They are, and I've encountered enough
+ * evil mail to be totally unwilling to trust that ``it will never happen''.
+ */
+
+/* Build parameters */
+
+#define KODRETRY 15 /* kiss-of-death retry in seconds */
+#define LOCKTIMEOUT 5 /* lock timeout in minutes */
diff --git a/imap/src/osdep/nt/write.c b/imap/src/osdep/nt/write.c
new file mode 100644
index 00000000..c7854815
--- /dev/null
+++ b/imap/src/osdep/nt/write.c
@@ -0,0 +1,59 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Write data, treating partial writes as an error
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 May 1995
+ * Last Edited: 30 August 2006
+ */
+
+/* The whole purpose of this unfortunate routine is to deal with DOS and
+ * certain cretinous versions of UNIX which decided that the "bytes actually
+ * written" return value from write() gave them license to use that for things
+ * that are really errors, such as disk quota exceeded, maximum file size
+ * exceeded, disk full, etc.
+ *
+ * BSD won't screw us this way on the local filesystem, but who knows what
+ * some NFS-mounted filesystem will do.
+ */
+
+#undef write
+
+/* Write data to file
+ * Accepts: file descriptor
+ * I/O vector structure
+ * number of vectors in structure
+ * Returns: number of bytes written if successful, -1 if failure
+ */
+
+long maxposint = (long)((((unsigned long) 1) << ((sizeof(int) * 8) - 1)) - 1);
+
+long safe_write (int fd,char *buf,long nbytes)
+{
+ long i,j;
+ if (nbytes > 0) for (i = nbytes; i; i -= j,buf += j) {
+ while (((j = write (fd,buf,(int) min (maxposint,i))) < 0) &&
+ (errno == EINTR));
+ if (j < 0) return j;
+ }
+ return nbytes;
+}
diff --git a/imap/src/osdep/nt/yunchan.c b/imap/src/osdep/nt/yunchan.c
new file mode 100644
index 00000000..8c1f6905
--- /dev/null
+++ b/imap/src/osdep/nt/yunchan.c
@@ -0,0 +1,286 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Unix compatibility routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 14 September 1996
+ * Last Edited: 30 August 2006
+ */
+
+
+/* DEDICATION
+ *
+ * This file is dedicated to my dog, Unix, also known as Yun-chan and
+ * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix
+ * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after
+ * a two-month bout with cirrhosis of the liver.
+ *
+ * He was a dear friend, and I miss him terribly.
+ *
+ * Lift a leg, Yunie. Luv ya forever!!!!
+ */
+
+/* Emulator for BSD flock() call
+ * Accepts: file descriptor
+ * operation bitmask
+ * Returns: 0 if successful, -1 if failure
+ */
+
+/* Our friends in Redmond have decided that you can not write to any segment
+ * which has a shared lock. This screws up the shared-write mailbox drivers
+ * (mbx, mtx, and tenex). As a workaround, we'll only lock the first byte of
+ * the file, meaning that you can't write that byte shared.
+ * This behavior seems to be new as of NT 4.0.
+ */
+
+int flock (int fd,int op)
+{
+ HANDLE hdl = (HANDLE) _get_osfhandle (fd);
+ DWORD flags = (op & LOCK_NB) ? LOCKFILE_FAIL_IMMEDIATELY : 0;
+ OVERLAPPED offset = {NIL,NIL,0,0,NIL};
+ int ret = -1;
+ blocknotify_t bn = (blocknotify_t)
+ ((op & LOCK_NB) ? NIL : mail_parameters (NIL,GET_BLOCKNOTIFY,NIL));
+ if (hdl < 0) errno = EBADF; /* error in file descriptor */
+ else switch (op & ~LOCK_NB) { /* translate to LockFileEx() op */
+ case LOCK_EX: /* exclusive */
+ flags |= LOCKFILE_EXCLUSIVE_LOCK;
+ case LOCK_SH: /* shared */
+ if (!check_nt ()) return 0; /* always succeeds if not NT */
+ if (bn) (*bn) (BLOCK_FILELOCK,NIL);
+ /* bug for bug compatible with Unix */
+ UnlockFileEx (hdl,NIL,1,0,&offset);
+ /* lock the file as requested */
+ if (LockFileEx (hdl,flags,NIL,1,0,&offset)) ret = 0;
+ if (bn) (*bn) (BLOCK_NONE,NIL);
+ /* if failed */
+ if (ret) errno = (op & LOCK_NB) ? EAGAIN : EBADF;
+ break;
+ case LOCK_UN: /* unlock */
+ if (check_nt ()) UnlockFileEx (hdl,NIL,1,0,&offset);
+ ret = 0; /* always succeeds */
+ default: /* default */
+ errno = EINVAL; /* bad call */
+ break;
+ }
+ return ret;
+}
+
+/* Local storage */
+
+static char *loghdr; /* log file header string */
+static HANDLE loghdl = NIL; /* handle of event source */
+
+/* Emulator for BSD syslog() routine
+ * Accepts: priority
+ * message
+ * parameters
+ */
+
+void syslog (int priority,const char *message,...)
+{
+ va_list args;
+ LPTSTR strs[2];
+ char tmp[MAILTMPLEN]; /* callers must be careful not to pop this */
+ unsigned short etype;
+ if (!check_nt ()) return; /* no-op on non-NT system */
+ /* default event source */
+ if (!loghdl) openlog ("c-client",LOG_PID,LOG_MAIL);
+ switch (priority) { /* translate UNIX type into NT type */
+ case LOG_ALERT:
+ etype = EVENTLOG_ERROR_TYPE;
+ break;
+ case LOG_INFO:
+ etype = EVENTLOG_INFORMATION_TYPE;
+ break;
+ default:
+ etype = EVENTLOG_WARNING_TYPE;
+ }
+ va_start (args,message); /* initialize vararg mechanism */
+ vsprintf (tmp,message,args); /* build message */
+ strs[0] = loghdr; /* write header */
+ strs[1] = tmp; /* then the message */
+ /* report the event */
+ ReportEvent (loghdl,etype,(unsigned short) priority,2000,NIL,2,0,strs,NIL);
+ va_end (args);
+}
+
+
+/* Emulator for BSD openlog() routine
+ * Accepts: identity
+ * options
+ * facility
+ */
+
+void openlog (const char *ident,int logopt,int facility)
+{
+ char tmp[MAILTMPLEN];
+ if (!check_nt ()) return; /* no-op on non-NT system */
+ if (loghdl) fatal ("Duplicate openlog()!");
+ loghdl = RegisterEventSource (NIL,ident);
+ sprintf (tmp,(logopt & LOG_PID) ? "%s[%d]" : "%s",ident,getpid ());
+ loghdr = cpystr (tmp); /* save header for later */
+}
+
+/* Copy Unix string with CRLF newlines
+ * Accepts: destination string
+ * pointer to size of destination string buffer
+ * source string
+ * length of source string
+ * Returns: length of copied string
+ */
+
+unsigned long unix_crlfcpy (char **dst,unsigned long *dstl,char *src,
+ unsigned long srcl)
+{
+ unsigned long i,j;
+ char *d = src;
+ /* count number of LF's in source string(s) */
+ for (i = srcl,j = 0; j < srcl; j++) if (*d++ == '\012') i++;
+ /* flush destination buffer if too small */
+ if (*dst && (i > *dstl)) fs_give ((void **) dst);
+ if (!*dst) { /* make a new buffer if needed */
+ *dst = (char *) fs_get ((*dstl = i) + 1);
+ if (dstl) *dstl = i; /* return new buffer length to main program */
+ }
+ d = *dst; /* destination string */
+ /* copy strings, inserting CR's before LF's */
+ while (srcl--) switch (*src) {
+ case '\015': /* unlikely carriage return */
+ *d++ = *src++; /* copy it and any succeeding linefeed */
+ if (srcl && *src == '\012') {
+ *d++ = *src++;
+ srcl--;
+ }
+ break;
+ case '\012': /* line feed? */
+ *d++ ='\015'; /* yes, prepend a CR, drop into default case */
+ default: /* ordinary chararacter */
+ *d++ = *src++; /* just copy character */
+ break;
+ }
+ *d = '\0'; /* tie off destination */
+ return d - *dst; /* return length */
+}
+
+/* Length of Unix string after unix_crlfcpy applied
+ * Accepts: source string
+ * Returns: length of string
+ */
+
+unsigned long unix_crlflen (STRING *s)
+{
+ unsigned long pos = GETPOS (s);
+ unsigned long i = SIZE (s);
+ unsigned long j = i;
+ while (j--) switch (SNX (s)) {/* search for newlines */
+ case '\015': /* unlikely carriage return */
+ if (j && (CHR (s) == '\012')) {
+ SNX (s); /* eat the line feed */
+ j--;
+ }
+ break;
+ case '\012': /* line feed? */
+ i++;
+ default: /* ordinary chararacter */
+ break;
+ }
+ SETPOS (s,pos); /* restore old position */
+ return i;
+}
+
+/* Undoubtably, I'm going to regret these two routines in the future. I
+ * regret them now. Their purpose is to work around two problems in the
+ * VC++ 6.0 C library:
+ * (1) tmpfile() creates the file in the current directory instead of a
+ * temporary directory
+ * (2) tmpfile() and fclose() think that on NT systems, it works to unlink
+ * the file while it's still open, so there's no need for the _tmpfname
+ * hook at fclose(). Unfortunately, that doesn't work in Win2K.
+ * I would be delighted to have a better alternative.
+ */
+
+#undef fclose /* use the real fclose() in close_file() */
+
+/* Substitute for Microsoft's tmpfile() that uses the real temporary directory
+ * Returns: FILE structure if success, NIL if failure
+ */
+
+FILE *create_tempfile (void)
+{
+ FILE *ret = NIL;
+ char *s = _tempnam (getenv ("TEMP"),"msg");
+ if (s) { /* if got temporary name... */
+ /* open file, and stash name on _tmpfname */
+ if (ret = fopen (s,"w+b")) ret->_tmpfname = s;
+ else fs_give ((void **) &s);/* flush temporary string */
+ }
+ return ret;
+}
+
+
+/* Substitute for Microsoft's fclose() that always flushes _tmpfname
+ * Returns: FILE structure if success, NIL if failure
+ */
+
+int close_file (FILE *stream)
+{
+ int ret;
+ char *s = stream->_tmpfname;
+ stream->_tmpfname = NIL; /* just in case fclose() tries to delete it */
+ ret = fclose (stream); /* close the file */
+ if (s) { /* was there a _tmpfname? */
+ unlink (s); /* yup, delete it */
+ fs_give ((void **) &s); /* and flush the name */
+ }
+ return ret;
+}
+
+/* Get password from console
+ * Accepts: prompt
+ * Returns: password
+ */
+
+#define PWDLEN 128 /* used by Linux */
+
+char *getpass (const char *prompt)
+{
+ static char pwd[PWDLEN];
+ int ch,i,done;
+ fputs (prompt,stderr); /* output prompt */
+ for (i = done = 0; !done; ) switch (ch = _getch()) {
+ case 0x03: /* CTRL/C stops program */
+ _exit (1);
+ case '\b': /* BACKSPACE erase previous character */
+ if (i) pwd[--i] = '\0';
+ break;
+ case '\n': case '\r': /* CR or LF terminates string */
+ done = 1;
+ break;
+ default: /* any other character is a pwd char */
+ if (i < (PWDLEN - 1)) pwd[i++] = ch;
+ break;
+ }
+ pwd[i] = '\0'; /* tie off string with null */
+ putchar ('\n'); /* echo newline */
+ return pwd;
+}
diff --git a/imap/src/osdep/nt/yunchan.h b/imap/src/osdep/nt/yunchan.h
new file mode 100644
index 00000000..25d60568
--- /dev/null
+++ b/imap/src/osdep/nt/yunchan.h
@@ -0,0 +1,86 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Unix compatibility routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 14 September 1996
+ * Last Edited: 30 August 2006
+ */
+
+
+/* DEDICATION
+ *
+ * This file is dedicated to my dog, Unix, also known as Yun-chan and
+ * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix
+ * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after
+ * a two-month bout with cirrhosis of the liver.
+ *
+ * He was a dear friend, and I miss him terribly.
+ *
+ * Lift a leg, Yunie. Luv ya forever!!!!
+ */
+
+/* For flock() emulation */
+
+#define LOCK_SH 1
+#define LOCK_EX 2
+#define LOCK_NB 4
+#define LOCK_UN 8
+
+
+/* syslog() emulation */
+
+#define LOG_MAIL (2<<3) /* mail system */
+#define LOG_DAEMON (3<<3) /* system daemons */
+#define LOG_AUTH (4<<3) /* security/authorization messages */
+#define LOG_EMERG 0 /* system is unusable */
+#define LOG_ALERT 1 /* action must be taken immediately */
+#define LOG_CRIT 2 /* critical conditions */
+#define LOG_ERR 3 /* error conditions */
+#define LOG_WARNING 4 /* warning conditions */
+#define LOG_NOTICE 5 /* normal but signification condition */
+#define LOG_INFO 6 /* informational */
+#define LOG_DEBUG 7 /* debug-level messages */
+#define LOG_PID 0x01 /* log the pid with each message */
+#define LOG_CONS 0x02 /* log on the console if errors in sending */
+#define LOG_ODELAY 0x04 /* delay open until syslog() is called */
+#define LOG_NDELAY 0x08 /* don't delay open */
+#define LOG_NOWAIT 0x10 /* if forking to log on console, don't wait() */
+
+#define tmpfile create_tempfile
+#define fclose close_file
+#define fsync _commit
+#define ftruncate chsize
+#define gethostid clock
+#define sleep(x) Sleep (1000 * x)
+
+
+long alarm (long seconds);
+int flock (int fd,int op);
+void openlog (const char *ident,int logopt,int facility);
+void syslog (int priority,const char *message,...);
+unsigned long unix_crlfcpy (char **dst,unsigned long *dstl,char *src,
+ unsigned long srcl);
+unsigned long unix_crlflen (STRING *s);
+FILE *create_tempfile (void);
+int close_file (FILE *stream);
+char *getpass (const char *prompt);
diff --git a/imap/src/osdep/os2/auths.cmd b/imap/src/osdep/os2/auths.cmd
new file mode 100644
index 00000000..639f0807
--- /dev/null
+++ b/imap/src/osdep/os2/auths.cmd
@@ -0,0 +1,48 @@
+/* rexx */
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+/*
+ * Program: Authenticator Linkage Generator for OS/2
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 June 1999
+ * Last Edited: 30 August 2006
+ */
+'@echo off'
+/* Erase old authenticators list */
+'if exist auths.c del auths.c'
+parse arg args
+n=words(args)
+a_file='auths.c'
+c_file='linkage.c'
+h_file='linkage.h'
+call stream a_file, 'C', 'open write'
+call stream c_file, 'C', 'open write'
+call stream h_file, 'C', 'open write'
+do i=1 to n
+ arg=word(args,i)
+ call lineout a_file, '#include "auth_'arg'.c"'
+ call lineout h_file, 'extern AUTHENTICATOR auth_'arg';'
+ call lineout c_file, ' auth_link (&auth_'arg'); /* link in the 'arg' authenticator */'
+ end
+call stream h_file, 'C', 'close'
+call stream c_file, 'C', 'close'
+call stream a_file, 'C', 'close'
+exit 0
diff --git a/imap/src/osdep/os2/drivers.cmd b/imap/src/osdep/os2/drivers.cmd
new file mode 100644
index 00000000..8508834b
--- /dev/null
+++ b/imap/src/osdep/os2/drivers.cmd
@@ -0,0 +1,45 @@
+/* rexx */
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+/*
+ * Program: Authenticator Linkage Generator for OS/2
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 June 1999
+ * Last Edited: 30 August 2006
+ */
+'@echo off'
+/* Erase old authenticators list */
+'if exist linkage.h del linkage.h'
+'if exist linkage.c del linkage.c'
+parse arg args
+n=words(args)
+c_file='linkage.c'
+h_file='linkage.h'
+call stream c_file, 'C', 'open write'
+call stream h_file, 'C', 'open write'
+do i=1 to n
+ arg=word(args,i)
+ call lineout h_file, 'extern DRIVER 'arg'driver;'
+ call lineout c_file, ' mail_link (&'arg'driver); /* link in the 'arg' driver */'
+ end
+call stream h_file, 'C', 'close'
+call stream c_file, 'C', 'close'
+exit 0
diff --git a/imap/src/osdep/os2/dummy.h b/imap/src/osdep/os2/dummy.h
new file mode 100644
index 00000000..32650e06
--- /dev/null
+++ b/imap/src/osdep/os2/dummy.h
@@ -0,0 +1,43 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 9 May 1991
+ * Last Edited: 30 August 2006
+ */
+
+/* Exported function prototypes */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat);
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long scan_contents (DRIVER *dtb,char *name,char *contents,
+ unsigned long csiz,unsigned long fsiz);
+long dummy_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz);
+long dummy_create (MAILSTREAM *stream,char *mailbox);
+long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode);
+long dummy_delete (MAILSTREAM *stream,char *mailbox);
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname);
+char *dummy_file (char *dst,char *name);
+long dummy_canonicalize (char *tmp,char *ref,char *pat);
diff --git a/imap/src/osdep/os2/dummyos2.c b/imap/src/osdep/os2/dummyos2.c
new file mode 100644
index 00000000..f54999fc
--- /dev/null
+++ b/imap/src/osdep/os2/dummyos2.c
@@ -0,0 +1,714 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines for OS2
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 24 May 1993
+ * Last Edited: 30 August 2006
+ */
+
+/* Thanks to Nicholas Sheppard for the original version */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <pwd.h>
+#include <sys/stat.h>
+#include <io.h>
+#include <fcntl.h>
+#include <os2.h>
+#undef ADDRESS
+#include "mail.h"
+#include "osdep.h"
+#include "misc.h"
+#include "dummy.h"
+
+/* Function prototypes */
+
+DRIVER *dummy_valid (char *name);
+void *dummy_parameters (long function,void *value);
+void dummy_list_work (MAILSTREAM *stream,char *dir,char *pat,char *contents,
+ long level);
+long dummy_listed (MAILSTREAM *stream,char delimiter,char *name,
+ long attributes,char *contents);
+long dummy_subscribe (MAILSTREAM *stream,char *mailbox);
+MAILSTREAM *dummy_open (MAILSTREAM *stream);
+void dummy_close (MAILSTREAM *stream,long options);
+long dummy_ping (MAILSTREAM *stream);
+void dummy_check (MAILSTREAM *stream);
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options);
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+/* Dummy routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER dummydriver = {
+ "dummy", /* driver name */
+ DR_LOCAL|DR_MAIL, /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ dummy_valid, /* mailbox is valid for us */
+ dummy_parameters, /* manipulate parameters */
+ dummy_scan, /* scan mailboxes */
+ dummy_list, /* list mailboxes */
+ dummy_lsub, /* list subscribed mailboxes */
+ dummy_subscribe, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ dummy_create, /* create mailbox */
+ dummy_delete, /* delete mailbox */
+ dummy_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ dummy_open, /* open mailbox */
+ dummy_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message structure */
+ NIL, /* fetch header */
+ NIL, /* fetch text */
+ NIL, /* fetch message data */
+ NIL, /* unique identifier */
+ NIL, /* message number from UID */
+ NIL, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ dummy_ping, /* ping mailbox to see if still alive */
+ dummy_check, /* check for new messages */
+ dummy_expunge, /* expunge deleted messages */
+ dummy_copy, /* copy messages to another mailbox */
+ dummy_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+
+ /* prototype stream */
+MAILSTREAM dummyproto = {&dummydriver};
+
+/* Dummy validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *dummy_valid (char *name)
+{
+ char *s,*t,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* must be valid local mailbox */
+ if (name && *name && (*name != '{') && (s = mailboxfile (tmp,name))) {
+ /* indeterminate INBOX */
+ if (!*s) return &dummydriver;
+ /* remove trailing \ */
+ if ((t = strrchr (s,'\\')) && !t[1]) *t = '\0';
+ if (!stat (s,&sbuf)) switch (sbuf.st_mode & S_IFMT) {
+ case S_IFREG: /* file */
+ case S_IFDIR: /* future use */
+ return &dummydriver;
+ }
+ }
+ return NIL;
+}
+
+
+/* Dummy manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *dummy_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+/* Dummy scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ char *s,test[MAILTMPLEN],file[MAILTMPLEN];
+ long i = 0;
+ if (!pat || !*pat) { /* empty pattern? */
+ if (dummy_canonicalize (test,ref,"*")) {
+ /* tie off name at root */
+ if (s = strchr (test,'\\')) *++s = '\0';
+ else test[0] = '\0';
+ dummy_listed (stream,'\\',test,LATT_NOSELECT,NIL);
+ }
+ }
+ /* get canonical form of name */
+ else if (dummy_canonicalize (test,ref,pat)) {
+ /* found any wildcards? */
+ if (s = strpbrk (test,"%*")) {
+ /* yes, copy name up to that point */
+ strncpy (file,test,(size_t) (i = s - test));
+ file[i] = '\0'; /* tie off */
+ }
+ else strcpy (file,test); /* use just that name then */
+ /* find directory name */
+ if (s = strrchr (file,'\\')) {
+ *++s = '\0'; /* found, tie off at that point */
+ s = file;
+ }
+ /* silly case */
+ else if (file[0] == '#') s = file;
+ /* do the work */
+ dummy_list_work (stream,s,test,contents,0);
+ if (pmatch ("INBOX",test)) /* always an INBOX */
+ dummy_listed (stream,NIL,"INBOX",LATT_NOINFERIORS,contents);
+ }
+}
+
+/* Dummy list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ dummy_scan (stream,ref,pat,NIL);
+}
+
+
+/* Dummy list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ void *sdb = NIL;
+ char *s,*t,test[MAILTMPLEN];
+ int showuppers = pat[strlen (pat) - 1] == '%';
+ /* get canonical form of name */
+ if (dummy_canonicalize (test,ref,pat) && (s = sm_read (&sdb))) do
+ if (*s != '{') {
+ if (pmatch_full (s,test,'\\')) {
+ if (pmatch (s,"INBOX")) mm_lsub (stream,NIL,s,LATT_NOINFERIORS);
+ else mm_lsub (stream,'\\',s,NIL);
+ }
+ else while (showuppers && (t = strrchr (s,'\\'))) {
+ *t = '\0'; /* tie off the name */
+ if (pmatch_full (s,test,'\\')) mm_lsub (stream,'\\',s,LATT_NOSELECT);
+ }
+ }
+ while (s = sm_read (&sdb)); /* until no more subscriptions */
+}
+
+
+/* Dummy subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* must be valid local mailbox */
+ if ((s = mailboxfile (tmp,mailbox)) && *s && !stat (s,&sbuf) &&
+ ((sbuf.st_mode & S_IFMT) == S_IFREG)) return sm_subscribe (mailbox);
+ sprintf (tmp,"Can't subscribe %.80s: not a mailbox",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+}
+
+/* Dummy list mailboxes worker routine
+ * Accepts: mail stream
+ * directory name to search
+ * search pattern
+ * string to scan
+ * search level
+ */
+
+void dummy_list_work (MAILSTREAM *stream,char *dir,char *pat,char *contents,
+ long level)
+{
+ unsigned long i = 1;
+ FILEFINDBUF3 f;
+ HDIR hd = HDIR_CREATE;
+ struct stat sbuf;
+ char tmp[MAILTMPLEN];
+ /* punt if bogus name */
+ if (!mailboxdir (tmp,dir,NIL)) return;
+ /* make directory wildcard */
+ strcat (tmp,(tmp[strlen (tmp) -1] == '\\') ? "*.*" : "\\*.*");
+ /* do nothing if can't open directory */
+ if (!DosFindFirst (tmp,&hd,FILE_NORMAL,&f,sizeof (f),&i,FIL_STANDARD)) {
+ /* list it if not at top-level */
+ if (!level && dir && pmatch_full (dir,pat,'\\'))
+ dummy_listed (stream,'\\',dir,LATT_NOSELECT,contents);
+ /* scan directory */
+ if (!dir || dir[strlen (dir) -1] == '\\') do {
+ if (((f.name[0] != '.') ||
+ (f.name[1] && ((f.name[1] != '.') || f.name[2]))) &&
+ (strlen (f.name) <= NETMAXMBX)) {
+ /* see if name is useful */
+ if (dir) sprintf (tmp,"%s%s",dir,f.name);
+ else strcpy (tmp,f.name);
+ /* make sure useful and can get info */
+ if ((pmatch_full (tmp,pat,'\\') ||
+ pmatch_full (strcat (tmp,"\\"),pat,'\\') ||
+ dmatch (tmp,pat,'\\')) &&
+ mailboxdir (tmp,dir,f.name) && tmp[0] && !stat (tmp,&sbuf)) {
+ /* now make name we'd return */
+ if (dir) sprintf (tmp,"%s%s",dir,f.name);
+ else strcpy (tmp,f.name);
+ /* only interested in file type */
+ switch (sbuf.st_mode & S_IFMT) {
+ case S_IFDIR: /* directory? */
+ if (pmatch_full (tmp,pat,'\\')) {
+ if (!dummy_listed (stream,'\\',tmp,LATT_NOSELECT,contents))break;
+ strcat (tmp,"\\");/* set up for dmatch call */
+ }
+ /* try again with trailing \ */
+ else if (pmatch_full (strcat (tmp,"\\"),pat,'\\') &&
+ !dummy_listed (stream,'\\',tmp,LATT_NOSELECT,contents))
+ break;
+ if (dmatch (tmp,pat,'\\') &&
+ (level < (long) mail_parameters (NIL,GET_LISTMAXLEVEL,NIL)))
+ dummy_list_work (stream,tmp,pat,contents,level+1);
+ break;
+ case S_IFREG: /* ordinary name */
+ if (pmatch_full (tmp,pat,'\\') && !pmatch ("INBOX",tmp))
+ dummy_listed (stream,'\\',tmp,LATT_NOINFERIORS,contents);
+ break;
+ }
+ }
+ }
+ i = 1;
+ } while (!DosFindNext (h,&f,sizeof (f),&i));
+ }
+}
+
+/* Mailbox found
+ * Accepts: hierarchy delimiter
+ * mailbox name
+ * attributes
+ * contents to search before calling mm_list()
+ * Returns: T, always
+ */
+
+#define BUFSIZE 4*MAILTMPLEN
+
+long dummy_listed (MAILSTREAM *stream,char delimiter,char *name,
+ long attributes,char *contents)
+{
+ struct stat sbuf;
+ int fd;
+ long csiz,ssiz,bsiz;
+ char *s,*buf,tmp[MAILTMPLEN];
+ if (contents) { /* want to search contents? */
+ /* forget it if can't select or open */
+ if ((attributes & LATT_NOSELECT) || !(csiz = strlen (contents)) ||
+ !(s = dummy_file (tmp,name)) || stat (s,&sbuf) ||
+ (csiz > sbuf.st_size) || ((fd = open (tmp,O_RDONLY,NIL)) < 0))
+ return T;
+ /* get buffer including slop */
+ buf = (char *) fs_get (BUFSIZE + (ssiz = 4 * ((csiz / 4) + 1)) + 1);
+ memset (buf,'\0',ssiz); /* no slop area the first time */
+ while (sbuf.st_size) { /* until end of file */
+ read (fd,buf+ssiz,bsiz = min (sbuf.st_size,BUFSIZE));
+ if (search ((unsigned char *) buf,bsiz+ssiz,
+ (unsigned char *) contents,csiz)) break;
+ memcpy (buf,buf+BUFSIZE,ssiz);
+ sbuf.st_size -= bsiz; /* note that we read that much */
+ }
+ fs_give ((void **) &buf); /* flush buffer */
+ close (fd); /* finished with file */
+ if (!sbuf.st_size) return T;/* not found */
+ }
+ /* notify main program */
+ mm_list (stream,delimiter,name,attributes);
+ return T;
+}
+
+/* Dummy create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_create (MAILSTREAM *stream,char *mailbox)
+{
+ char tmp[MAILTMPLEN];
+ if (compare_cstring (mailbox,"INBOX") && dummy_file (tmp,mailbox))
+ return dummy_create_path (stream,tmp,NIL);
+ sprintf (tmp,"Can't create %.80s: invalid name",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+}
+
+
+/* Dummy create path
+ * Accepts: mail stream
+ * path name to create
+ * directory mode
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode)
+{
+ struct stat sbuf;
+ char c,*s,tmp[MAILTMPLEN];
+ int fd;
+ long ret = NIL;
+ char *t = strrchr (path,'\\');
+ char *pt = (path[1] == ':') ? path + 2 : path;
+ int wantdir = t && !t[1];
+ if (wantdir) *t = '\0'; /* flush trailing delimiter for directory */
+ /* found superior to this name? */
+ if ((s = strrchr (pt,'\\')) && (s != pt)) {
+ strncpy (tmp,path,(size_t) (s - path));
+ tmp[s - path] = '\0'; /* make directory name for stat */
+ c = *++s; /* tie off in case need to recurse */
+ *s = '\0';
+ /* name doesn't exist, create it */
+ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,path,dirmode)) return NIL;
+ *s = c; /* restore full name */
+ }
+ if (wantdir) { /* want to create directory? */
+ ret = !mkdir (path);
+ *t = '\\'; /* restore directory delimiter */
+ }
+ /* create file */
+ else if ((fd = open (path,O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE)) >= 0)
+ ret = !close (fd); /* close file */
+ if (!ret) { /* error? */
+ sprintf (tmp,"Can't create mailbox node %.80s: %.80s",path,
+ strerror (errno));
+ mm_log (tmp,ERROR);
+ }
+ return ret; /* return status */
+}
+
+/* Dummy delete mailbox
+ * Accepts: mail stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_delete (MAILSTREAM *stream,char *mailbox)
+{
+ struct stat sbuf;
+ char *s,tmp[MAILTMPLEN];
+ if (!(s = dummy_file (tmp,mailbox))) {
+ sprintf (tmp,"Can't delete - invalid name: %.80s",s);
+ mm_log (tmp,ERROR);
+ }
+ /* no trailing \ */
+ if ((s = strrchr (tmp,'\\')) && !s[1]) *s = '\0';
+ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) == S_IFDIR) ?
+ rmdir (tmp) : unlink (tmp)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %.80s",mailbox,strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ return T; /* return success */
+}
+
+
+/* Mail rename mailbox
+ * Accepts: mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ struct stat sbuf;
+ char c,*s,tmp[MAILTMPLEN],mbx[MAILTMPLEN],oldname[MAILTMPLEN];
+ long ret = NIL;
+ /* no trailing \ allowed */
+ if (!dummy_file (oldname,old) || !(s = dummy_file (mbx,newname)) ||
+ ((s = strrchr (s,'\\')) && !s[1])) {
+ sprintf (mbx,"Can't rename %.80s to %.80s: invalid name",old,newname);
+ mm_log (mbx,ERROR);
+ return NIL;
+ }
+ /* found superior to destination name? */
+ if (s && (s != mbx) && ((mbx[1] != ':') || (s != mbx + 2))) {
+ c = s[1]; /* remember character after delimiter */
+ *s = s[1] = '\0'; /* tie off name at delimiter */
+ /* name doesn't exist, create it */
+ if (stat (mbx,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) {
+ *s = '\\'; /* restore delimiter */
+ if (!dummy_create (stream,mbx)) return NIL;
+ }
+ else *s = '\\'; /* restore delimiter */
+ s[1] = c; /* restore character after delimiter */
+ }
+ /* rename of non-ex INBOX creates dest */
+ if (!compare_cstring (old,"INBOX") && stat (oldname,&sbuf))
+ return dummy_create (NIL,mbx);
+ if (rename (oldname,mbx)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %.80s",old,newname,
+ strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ return LONGT; /* return success */
+}
+
+/* Dummy open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *dummy_open (MAILSTREAM *stream)
+{
+ int fd;
+ char err[MAILTMPLEN],tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* OP_PROTOTYPE call */
+ if (!stream) return &dummyproto;
+ err[0] = '\0'; /* no error message yet */
+ /* can we open the file? */
+ if (!dummy_file (tmp,stream->mailbox))
+ sprintf (err,"Can't open this name: %.80s",stream->mailbox);
+ else if ((fd = open (tmp,O_RDONLY,NIL)) < 0) {
+ /* no, error unless INBOX */
+ if (compare_cstring (stream->mailbox,"INBOX"))
+ sprintf (err,"%.80s: %.80s",strerror (errno),stream->mailbox);
+ }
+ else { /* file had better be empty then */
+ fstat (fd,&sbuf); /* sniff at its size */
+ close (fd);
+ if (sbuf.st_size) /* bogus format if non-empty */
+ sprintf (err,"%.80s (file %.80s) is not in valid mailbox format",
+ stream->mailbox,tmp);
+ }
+ if (err[0]) { /* if an error happened */
+ mm_log (err,stream->silent ? WARN : ERROR);
+ return NIL;
+ }
+ else if (!stream->silent) { /* only if silence not requested */
+ mail_exists (stream,0); /* say there are 0 messages */
+ mail_recent (stream,0); /* and certainly no recent ones! */
+ stream->uid_validity = time (0);
+ }
+ stream->inbox = T; /* note that it's an INBOX */
+ return stream; /* return success */
+}
+
+
+/* Dummy close
+ * Accepts: MAIL stream
+ * options
+ */
+
+void dummy_close (MAILSTREAM *stream,long options)
+{
+ /* return silently */
+}
+
+/* Dummy ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long dummy_ping (MAILSTREAM *stream)
+{
+ MAILSTREAM *test;
+ /* time to do another test? */
+ if (time (0) >= ((time_t) (stream->gensym + 30))) {
+ /* has mailbox format changed? */
+ if ((test = mail_open (NIL,stream->mailbox,OP_PROTOTYPE)) &&
+ (test->dtb != stream->dtb) &&
+ (test = mail_open (NIL,stream->mailbox,NIL))) {
+ /* preserve some resources */
+ test->original_mailbox = stream->original_mailbox;
+ stream->original_mailbox = NIL;
+ test->sparep = stream->sparep;
+ stream->sparep = NIL;
+ test->sequence = stream->sequence;
+ mail_close ((MAILSTREAM *) /* flush resources used by dummy stream */
+ memcpy (fs_get (sizeof (MAILSTREAM)),stream,
+ sizeof (MAILSTREAM)));
+ /* swap the streams */
+ memcpy (stream,test,sizeof (MAILSTREAM));
+ fs_give ((void **) &test);/* flush test now that copied */
+ /* make sure application knows */
+ mail_exists (stream,stream->recent = stream->nmsgs);
+ }
+ /* still hasn't changed */
+ else stream->gensym = time (0);
+ }
+ return T;
+}
+
+
+/* Dummy check mailbox
+ * Accepts: MAIL stream
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+void dummy_check (MAILSTREAM *stream)
+{
+ dummy_ping (stream); /* invoke ping */
+}
+
+
+/* Dummy expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ return LONGT;
+}
+
+/* Dummy copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * options
+ * Returns: T if copy successful, else NIL
+ */
+
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) fatal ("Impossible dummy_copy");
+ return NIL;
+}
+
+
+/* Dummy append message string
+ * Accepts: mail stream
+ * destination mailbox
+ * append callback function
+ * data for callback
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd = -1;
+ int e;
+ char tmp[MAILTMPLEN];
+ MAILSTREAM *ts = default_proto (T);
+ if (compare_cstring (mailbox,"INBOX") && dummy_file (tmp,mailbox) &&
+ ((fd = open (tmp,O_RDONLY,NIL)) < 0)) {
+ if ((e = errno) == ENOENT) /* failed, was it no such file? */
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",
+ (long) NIL);
+ sprintf (tmp,"%.80s: %.80s",strerror (e),mailbox);
+ mm_log (tmp,ERROR); /* pass up error */
+ return NIL; /* always fails */
+ }
+ if (fd >= 0) { /* found file? */
+ fstat (fd,&sbuf); /* get its size */
+ close (fd); /* toss out the fd */
+ if (sbuf.st_size) ts = NIL; /* non-empty file? */
+ }
+ if (ts) return (*ts->dtb->append) (stream,mailbox,af,data);
+ sprintf (tmp,"Indeterminate mailbox format: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+}
+
+/* Dummy mail generate file string
+ * Accepts: temporary buffer to write into
+ * mailbox name string
+ * Returns: local file string or NIL if failure
+ */
+
+char *dummy_file (char *dst,char *name)
+{
+ char *s = mailboxfile (dst,name);
+ /* return our standard inbox */
+ return (s && !*s) ? strcpy (dst,sysinbox ()) : s;
+}
+
+
+/* Dummy canonicalize name
+ * Accepts: buffer to write name
+ * reference
+ * pattern
+ * Returns: T if success, NIL if failure
+ */
+
+long dummy_canonicalize (char *tmp,char *ref,char *pat)
+{
+ unsigned long i;
+ char *s,dev[4];
+ /* initially no device */
+ dev[0] = dev[1] = dev[2] = dev[3] = '\0';
+ if (ref) switch (*ref) { /* preliminary reference check */
+ case '{': /* remote names not allowed */
+ return NIL; /* disallowed */
+ case '\0': /* empty reference string */
+ break;
+ default: /* all other names */
+ if (ref[1] == ':') { /* start with device name? */
+ dev[0] = *ref++; dev[1] = *ref++;
+ }
+ break;
+ }
+ if (pat[1] == ':') { /* device name in pattern? */
+ dev[0] = *pat++; dev[1] = *pat++;
+ ref = NIL; /* ignore reference */
+ }
+ switch (*pat) {
+ case '#': /* namespace names */
+ if (mailboxfile (tmp,pat)) strcpy (tmp,pat);
+ else return NIL; /* unknown namespace */
+ break;
+ case '{': /* remote names not allowed */
+ return NIL;
+ case '\\': /* rooted name */
+ ref = NIL; /* ignore reference */
+ break;
+ }
+ /* make sure device names are rooted */
+ if (dev[0] && (*(ref ? ref : pat) != '\\')) dev[2] = '\\';
+ /* build name */
+ sprintf (tmp,"%s%s%s",dev,ref ? ref : "",pat);
+ ucase (tmp); /* force upper case */
+ /* count wildcards */
+ for (i = 0, s = tmp; *s; *s++) if ((*s == '*') || (*s == '%')) ++i;
+ if (i > MAXWILDCARDS) { /* ridiculous wildcarding? */
+ MM_LOG ("Excessive wildcards in LIST/LSUB",ERROR);
+ return NIL;
+ }
+ return T;
+}
diff --git a/imap/src/osdep/os2/env_os2.c b/imap/src/osdep/os2/env_os2.c
new file mode 100644
index 00000000..3c679f5d
--- /dev/null
+++ b/imap/src/osdep/os2/env_os2.c
@@ -0,0 +1,318 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: OS/2 environment routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+static char *myLocalHost = NIL; /* local host name */
+static char *myHomeDir = NIL; /* home directory name */
+static char *myNewsrc = NIL; /* newsrc file name */
+static short no822tztext = NIL; /* disable RFC [2]822 timezone text */
+
+#include "write.c" /* include safe writing routines */
+#include "pmatch.c" /* include wildcard pattern matcher */
+
+
+/* Get all authenticators */
+
+#include "auths.c"
+
+/* Environment manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *env_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_HOMEDIR:
+ myHomeDir = cpystr ((char *) value);
+ case GET_HOMEDIR:
+ ret = (void *) myHomeDir;
+ break;
+ case SET_LOCALHOST:
+ myLocalHost = cpystr ((char *) value);
+ case GET_LOCALHOST:
+ ret = (void *) myLocalHost;
+ break;
+ case SET_NEWSRC:
+ if (myNewsrc) fs_give ((void **) &myNewsrc);
+ myNewsrc = cpystr ((char *) value);
+ case GET_NEWSRC:
+ if (!myNewsrc) { /* set news file name if not defined */
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"%s\\newsrc",myhomedir ());
+ myNewsrc = cpystr (tmp);
+ }
+ ret = (void *) myNewsrc;
+ break;
+ case SET_DISABLE822TZTEXT:
+ no822tztext = value ? T : NIL;
+ case GET_DISABLE822TZTEXT:
+ ret = (void *) (no822tztext ? VOIDT : NIL);
+ break;
+ }
+ return ret;
+}
+
+/* Write current time
+ * Accepts: destination string
+ * optional format of day-of-week prefix
+ * format of date and time
+ * flag whether to append symbolic timezone
+ */
+
+static void do_date (char *date,char *prefix,char *fmt,int suffix)
+{
+ time_t tn = time (0);
+ struct tm *t = gmtime (&tn);
+ int zone = t->tm_hour * 60 + t->tm_min;
+ int julian = t->tm_yday;
+ t = localtime (&tn); /* get local time now */
+ /* minus UTC minutes since midnight */
+ zone = t->tm_hour * 60 + t->tm_min - zone;
+ /* julian can be one of:
+ * 36x local time is December 31, UTC is January 1, offset -24 hours
+ * 1 local time is 1 day ahead of UTC, offset +24 hours
+ * 0 local time is same day as UTC, no offset
+ * -1 local time is 1 day behind UTC, offset -24 hours
+ * -36x local time is January 1, UTC is December 31, offset +24 hours
+ */
+ if (julian = t->tm_yday -julian)
+ zone += ((julian < 0) == (abs (julian) == 1)) ? -24*60 : 24*60;
+ if (prefix) { /* want day of week? */
+ sprintf (date,prefix,days[t->tm_wday]);
+ date += strlen (date); /* make next sprintf append */
+ }
+ /* output the date */
+ sprintf (date,fmt,t->tm_mday,months[t->tm_mon],t->tm_year+1900,
+ t->tm_hour,t->tm_min,t->tm_sec,zone/60,abs (zone) % 60);
+ if (suffix) { /* append timezone suffix if desired */
+ char *tz;
+ tzset (); /* get timezone from TZ environment stuff */
+ tz = tzname[daylight ? (((struct tm *) t)->tm_isdst > 0) : 0];
+ if (tz && tz[0]) {
+ char *s;
+ for (s = tz; *s; s++) if (*s & 0x80) return;
+ sprintf (date + strlen (date)," (%.50s)",tz);
+ }
+ }
+}
+
+
+/* Write current time in RFC 822 format
+ * Accepts: destination string
+ */
+
+void rfc822_date (char *date)
+{
+ do_date (date,"%s, ","%d %s %d %02d:%02d:%02d %+03d%02d",
+ no822tztext ? NIL : T);
+}
+
+
+/* Write current time in fixed-width RFC 822 format
+ * Accepts: destination string
+ */
+
+void rfc822_fixed_date (char *date)
+{
+ do_date (date,NIL,"%02d %s %4d %02d:%02d:%02d %+03d%02d",NIL);
+}
+
+
+/* Write current time in internal format
+ * Accepts: destination string
+ */
+
+void internal_date (char *date)
+{
+ do_date (date,NIL,"%02d-%s-%d %02d:%02d:%02d %+03d%02d",NIL);
+}
+
+/* Return my home directory name
+ * Returns: my home directory name
+ */
+
+char *myhomedir ()
+{
+ if (!myHomeDir) { /* get home directory name if not yet known */
+ char *s;
+ if ((s = getenv ("PINEHOME")) || (s = getenv ("HOME")) ||
+ (s = getenv ("ETC"))) {
+ myHomeDir = cpystr (s);
+ while (s = strchr (myHomeDir,'/')) *s = '\\';
+ if ((s = strrchr (myHomeDir,'\\')) && !s[1]) *s = '\0';
+ }
+ else myHomeDir = cpystr ("");
+ }
+ return myHomeDir;
+}
+
+
+/* Return mailbox file name
+ * Accepts: destination buffer
+ * mailbox name
+ * Returns: file name
+ */
+
+char *mailboxfile (char *dst,char *name)
+{
+ char *s;
+ char *ext = (char *) mail_parameters (NIL,GET_EXTENSION,NIL);
+ /* forbid extraneous extensions */
+ if ((s = strchr ((s = strrchr (name,'\\')) ? s : name,'.')) &&
+ ((ext = (char *) mail_parameters (NIL,GET_EXTENSION,NIL)) ||
+ strchr (s+1,'.'))) return NIL;
+ /* absolute path name? */
+ if ((*name == '\\') || (name[1] == ':')) strcpy (dst,name);
+ else sprintf (dst,"%s\\%s",myhomedir (),name);
+ if (ext) sprintf (dst + strlen (dst),".%s",ext);
+ return dst;
+}
+
+/* Lock file name
+ * Accepts: return buffer for file name
+ * file name
+ * locking to be placed on file if non-NIL
+ * Returns: file descriptor of lock or -1 if error
+ */
+
+int lockname (char *lock,char *fname,int op)
+{
+ int ld;
+ char c,*s;
+ if (!((s = lockdir (lock,getenv ("TEMP"),NIL)) ||
+ (s = lockdir (lock,getenv ("TMP"),NIL)) ||
+ (s = lockdir (lock,getenv ("TMPDIR"),NIL)) ||
+ /* C:\TEMP is last resort */
+ (s = lockdir (lock,defaultDrive (),"TEMP")))) {
+ mm_log ("Unable to find temporary directory",ERROR);
+ return -1;
+ }
+ /* generate file name */
+ while (c = *fname++) switch (c) {
+ case '/': case '\\': case ':':
+ *s++ = '!'; /* convert bad chars to ! */
+ break;
+ default:
+ *s++ = c;
+ break;
+ }
+ *s++ = c; /* tie off name */
+ /* get the lock */
+ if (((ld = open (lock,O_BINARY|O_RDWR|O_CREAT,S_IREAD|S_IWRITE)) >= 0) && op)
+ flock (ld,op); /* apply locking function */
+ return ld; /* return locking file descriptor */
+}
+
+/* Build lock directory, check to see if it exists
+ * Accepts: return buffer for lock directory
+ * first part of possible name
+ * optional second part
+ * Returns: pointer to end of buffer if buffer has a good name, else NIL
+ */
+
+char *lockdir (char *lock,char *first,char *last)
+{
+ struct stat sbuf;
+ char c,*s;
+ if (first && *first) { /* first part must be non-NIL */
+ /* copy first part */
+ for (s = lock; *first; c = *s++ = *first++);
+ if (last && *last) { /* copy last part if specified */
+ /* write trailing \ in case not in first */
+ if (c != '\\') *s++ = '\\';
+ while (*last) c = *s++ = *last++;
+ }
+ if (c == '\\') --s; /* delete trailing \ if any */
+ *s = '\0'; /* tie off name at this point */
+ return stat (lock,&sbuf) ? NIL : s;
+ }
+ return NIL; /* failed */
+}
+
+
+/* Unlock file descriptor
+ * Accepts: file descriptor
+ * lock file name from lockfd()
+ */
+
+void unlockfd (int fd,char *lock)
+{
+ flock (fd,LOCK_UN); /* unlock it */
+ close (fd); /* close it */
+}
+
+
+/* Determine default prototype stream to user
+ * Accepts: type (NIL for create, T for append)
+ * Returns: default prototype stream
+ */
+
+MAILSTREAM *default_proto (long type)
+{
+ extern MAILSTREAM DEFAULTPROTO;
+ return &DEFAULTPROTO; /* return default driver's prototype */
+}
+
+/* Global data */
+
+static unsigned rndm = 0; /* initial `random' number */
+
+
+/* Return random number
+ */
+
+long random ()
+{
+ if (!rndm) srand (rndm = (unsigned) time (0L));
+ return (long) rand ();
+}
+
+
+/* Emulator for BSD syslog() routine
+ * Accepts: priority
+ * message
+ * parameters
+ */
+
+void syslog (int priority,const char *message,...)
+{
+}
+
+
+/* Emulator for BSD openlog() routine
+ * Accepts: identity
+ * options
+ * facility
+ */
+
+void openlog (const char *ident,int logopt,int facility)
+{
+}
diff --git a/imap/src/osdep/os2/env_os2.h b/imap/src/osdep/os2/env_os2.h
new file mode 100644
index 00000000..de86da44
--- /dev/null
+++ b/imap/src/osdep/os2/env_os2.h
@@ -0,0 +1,62 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: OS/2 environment routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 14 March 1996
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Function prototypes */
+
+#include "env.h"
+
+long random (void);
+
+
+/* syslog() emulation */
+
+#define LOG_MAIL (2<<3) /* mail system */
+#define LOG_DAEMON (3<<3) /* system daemons */
+#define LOG_AUTH (4<<3) /* security/authorization messages */
+#define LOG_EMERG 0 /* system is unusable */
+#define LOG_ALERT 1 /* action must be taken immediately */
+#define LOG_CRIT 2 /* critical conditions */
+#define LOG_ERR 3 /* error conditions */
+#define LOG_WARNING 4 /* warning conditions */
+#define LOG_NOTICE 5 /* normal but signification condition */
+#define LOG_INFO 6 /* informational */
+#define LOG_DEBUG 7 /* debug-level messages */
+#define LOG_PID 0x01 /* log the pid with each message */
+#define LOG_CONS 0x02 /* log on the console if errors in sending */
+#define LOG_ODELAY 0x04 /* delay open until syslog() is called */
+#define LOG_NDELAY 0x08 /* don't delay open */
+#define LOG_NOWAIT 0x10 /* if forking to log on console, don't wait() */
+
+void openlog (const char *ident,int logopt,int facility);
+void syslog (int priority,const char *message,...);
+
+void rfc822_fixed_date (char *date);
+int lockname (char *lock,char *fname,int op);
+char *lockdir (char *lock,char *first,char *last);
+void unlockfd (int fd,char *lock);
diff --git a/imap/src/osdep/os2/fs_os2.c b/imap/src/osdep/os2/fs_os2.c
new file mode 100644
index 00000000..03908328
--- /dev/null
+++ b/imap/src/osdep/os2/fs_os2.c
@@ -0,0 +1,62 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Free storage management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Get a block of free storage
+ * Accepts: size of desired block
+ * Returns: free storage block
+ */
+
+void *fs_get (size_t size)
+{
+ void *block = malloc (size ? size : (size_t) 1);
+ if (!block) fatal ("Out of memory");
+ return (block);
+}
+
+
+/* Resize a block of free storage
+ * Accepts: ** pointer to current block
+ * new size
+ */
+
+void fs_resize (void **block,size_t size)
+{
+ if (!(*block = realloc (*block,size ? size : (size_t) 1)))
+ fatal ("Can't resize memory");
+}
+
+
+/* Return a block of free storage
+ * Accepts: ** pointer to free storage block
+ */
+
+void fs_give (void **block)
+{
+ free (*block);
+ *block = NIL;
+}
diff --git a/imap/src/osdep/os2/ftl_os2.c b/imap/src/osdep/os2/ftl_os2.c
new file mode 100644
index 00000000..9e65ef55
--- /dev/null
+++ b/imap/src/osdep/os2/ftl_os2.c
@@ -0,0 +1,38 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: DOS/VMS/TOPS-20 crash management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Report a fatal error
+ * Accepts: string to output
+ */
+
+void fatal (char *string)
+{
+ mm_fatal (string); /* pass up the string */
+ abort (); /* die horribly */
+}
diff --git a/imap/src/osdep/os2/makefile.os2 b/imap/src/osdep/os2/makefile.os2
new file mode 100644
index 00000000..a2211e91
--- /dev/null
+++ b/imap/src/osdep/os2/makefile.os2
@@ -0,0 +1,109 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+# Program: Portable C client makefile -- OS/2 version
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 11 May 1989
+# Last Edited: 30 August 2006
+
+
+EXTRAAUTHENTICATORS =
+EXTRADRIVERS =
+EXTRACFLAGS =
+DEFAULTAUTHENTICATORS = ext md5 pla log
+DRIVERS = imap nntp pop3 mbx mtx tenex unix
+DEFAULTDRIVER = mbx
+CFLAGS = -DOMF -DCHUNKSIZE=65536 -O2 -Zomf $(EXTRACFLAGS)
+CC = gcc
+CCLIENTLIB = cclient.lib
+
+all: $(CCLIENTLIB)
+
+.c.obj:
+ $(CC) $(CFLAGS) -o $@ -c $*.c
+
+osdep.h: os_os2.h
+ copy os_os2.h osdep.h
+ drivers.cmd $(EXTRADRIVERS) $(DRIVERS) dummy
+ auths.cmd $(EXTRAAUTHENTICATORS) $(DEFAULTAUTHENTICATORS)
+ setproto.cmd $(DEFAULTDRIVER)
+
+mail.obj: mail.h misc.h osdep.h mail.c
+
+misc.obj: mail.h misc.h misc.c
+
+fdstring.obj: mail.h misc.h osdep.h fdstring.h fdstring.c
+
+flstring.obj: mail.h misc.h osdep.h flstring.h flstring.c
+
+netmsg.obj: mail.h misc.h netmsg.h osdep.h netmsg.c
+
+newsrc.obj: mail.h misc.h newsrc.h osdep.h newsrc.c
+
+rfc822.obj: mail.h rfc822.h misc.h rfc822.c
+
+smanager.obj: mail.h misc.h smanager.c
+
+utf8.obj: mail.h misc.h osdep.h utf8.h
+
+utf8aux.obj: mail.h misc.h osdep.h utf8.h
+
+imap4r1.obj: mail.h imap4r1.h misc.h osdep.h imap4r1.c
+
+nntp.obj: mail.h nntp.h smtp.h rfc822.h misc.h osdep.h nntp.c
+
+pop3.obj: mail.h rfc822.h misc.h osdep.h pop3.c
+
+smtp.obj: mail.h smtp.h rfc822.h misc.h osdep.h smtp.c
+
+os_os2.obj: mail.h osdep.h env_os2.h fs.h ftl.h nl.h tcp.h tcp_os2.h \
+ os_os2.c fs_os2.c ftl_os2.c nl_os2.c env_os2.c tcp_os2.c \
+ mailfile.h auth_md5.c auth_log.c pmatch.c write.c
+
+mbxnt.obj: mail.h misc.h osdep.h mbxnt.c
+
+mtxnt.obj: mail.h misc.h osdep.h mtxnt.c
+
+tenexnt.obj: mail.h misc.h osdep.h tenexnt.c
+
+unixnt.obj: mail.h unixnt.h pseudo.h misc.h osdep.h unixnt.c
+
+dummyos2.obj: mail.h dummy.h misc.h osdep.h dummyos2.c
+
+pseudo.obj: pseudo.h
+
+$(CCLIENTLIB): mail.obj misc.obj fdstring.obj flstring.obj netmsg.obj \
+ newsrc.obj rfc822.obj smanager.obj utf8.obj utf8aux.obj \
+ imap4r1.obj nntp.obj pop3.obj smtp.obj os_os2.obj \
+ mbxnt.obj mtxnt.obj tenexnt.obj unixnt.obj dummynt.obj pseudo.obj
+ del $(CCLIENTLIB)
+ LIB /NOLOGO /OUT:$(CCLIENTLIB) \
+ mail.obj misc.obj fdstring.obj flstring.obj netmsg.obj \
+ newsrc.obj rfc822.obj smanager.obj utf8.obj utf8aux.obj \
+ imap4r1.obj nntp.obj pop3.obj smtp.obj os_os2.obj \
+ mbxnt.obj mtxnt.obj tenexnt.obj unixnt.obj dummynt.obj pseudo.obj
+
+clean:
+ del *.lib *.obj linkage.* osdep.* auths.c *.exe *.exp
+
+# A monument to a hack of long ago and far away...
+
+love:
+ @echo not war?
diff --git a/imap/src/osdep/os2/mbxnt.c b/imap/src/osdep/os2/mbxnt.c
new file mode 100644
index 00000000..bb3d4cc0
--- /dev/null
+++ b/imap/src/osdep/os2/mbxnt.c
@@ -0,0 +1,1694 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MBX mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 October 1995
+ * Last Edited: 28 September 2007
+ */
+
+
+/* FILE TIME SEMANTICS
+ *
+ * The atime is the last read time of the file.
+ * The mtime is the last flags update time of the file.
+ * The ctime is the last write time of the file.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/utime.h>
+#include "misc.h"
+#include "dummy.h"
+#include "fdstring.h"
+
+
+/* Build parameters */
+
+#define HDRSIZE 2048
+
+/* MBX I/O stream local data */
+
+typedef struct mbx_local {
+ unsigned int flagcheck: 1; /* if ping should sweep for flags */
+ unsigned int expok: 1; /* if expunging OK in ping */
+ unsigned int expunged : 1; /* if one or more expunged messages */
+ int fd; /* file descriptor for I/O */
+ int ld; /* lock file descriptor */
+ int ffuserflag; /* first free user flag */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ time_t lastsnarf; /* last snarf time */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ char lock[MAILTMPLEN]; /* buffer to write lock name */
+} MBXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MBXLOCAL *) stream->local)
+
+/* Function prototypes */
+
+DRIVER *mbx_valid (char *name);
+int mbx_isvalid (MAILSTREAM **stream,char *name,char *file,int *ld,char *lock,
+ long flags);
+void *mbx_parameters (long function,void *value);
+void mbx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void mbx_list (MAILSTREAM *stream,char *ref,char *pat);
+void mbx_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long mbx_create (MAILSTREAM *stream,char *mailbox);
+long mbx_delete (MAILSTREAM *stream,char *mailbox);
+long mbx_rename (MAILSTREAM *stream,char *old,char *newname);
+long mbx_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *mbx_open (MAILSTREAM *stream);
+void mbx_close (MAILSTREAM *stream,long options);
+void mbx_abort (MAILSTREAM *stream);
+void mbx_flags (MAILSTREAM *stream,char *sequence,long flags);
+char *mbx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags);
+long mbx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void mbx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+void mbx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long mbx_ping (MAILSTREAM *stream);
+void mbx_check (MAILSTREAM *stream);
+long mbx_expunge (MAILSTREAM *stream,char *sequence,long options);
+void mbx_snarf (MAILSTREAM *stream);
+long mbx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long mbx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+long mbx_parse (MAILSTREAM *stream);
+MESSAGECACHE *mbx_elt (MAILSTREAM *stream,unsigned long msgno,long expok);
+unsigned long mbx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt);
+void mbx_update_header (MAILSTREAM *stream);
+void mbx_update_status (MAILSTREAM *stream,unsigned long msgno,long flags);
+unsigned long mbx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size,char **hdr);
+unsigned long mbx_rewrite (MAILSTREAM *stream,unsigned long *reclaimed,
+ long flags);
+long mbx_flaglock (MAILSTREAM *stream);
+
+/* MBX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mbxdriver = {
+ "mbx", /* driver name */
+ DR_LOCAL|DR_MAIL|DR_CRLF|DR_LOCKING,
+ /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ mbx_valid, /* mailbox is valid for us */
+ mbx_parameters, /* manipulate parameters */
+ mbx_scan, /* scan mailboxes */
+ mbx_list, /* list mailboxes */
+ mbx_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ mbx_create, /* create mailbox */
+ mbx_delete, /* delete mailbox */
+ mbx_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ mbx_open, /* open mailbox */
+ mbx_close, /* close mailbox */
+ mbx_flags, /* fetch message "fast" attributes */
+ mbx_flags, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mbx_header, /* fetch message header */
+ mbx_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ mbx_flag, /* modify flags */
+ mbx_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mbx_ping, /* ping mailbox to see if still alive */
+ mbx_check, /* check for new messages */
+ mbx_expunge, /* expunge deleted messages */
+ mbx_copy, /* copy messages to another mailbox */
+ mbx_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mbxproto = {&mbxdriver};
+
+/* MBX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mbx_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ int fd = mbx_isvalid (NIL,name,tmp,NIL,NIL,NIL);
+ if (fd < 0) return NIL;
+ close (fd); /* don't need the fd now */
+ return &mbxdriver;
+}
+
+
+/* MBX mail test for valid mailbox
+ * Accepts: returned stream with valid mailbox keywords
+ * mailbox name
+ * buffer to write file name
+ * returned lock fd
+ * returned lock name
+ * RW flags or NIL for readonly
+ * Returns: file descriptor if valid, NIL otherwise
+ */
+
+#define MBXISVALIDNOUID 0x1 /* RW, don't do UID action */
+#define MBXISVALIDUID 0x2 /* RW, do UID action */
+
+int mbx_isvalid (MAILSTREAM **stream,char *name,char *file,int *ld,char *lock,
+ long flags)
+{
+ int fd,upd;
+ int ret = -1;
+ unsigned long i;
+ long j,k;
+ off_t pos;
+ char c,*s,*t,hdr[HDRSIZE];
+ struct stat sbuf;
+ struct utimbuf times;
+ int error = EINVAL; /* assume invalid argument */
+ if (ld) *ld = -1; /* initially no lock */
+ /* if file, get its status */
+ if ((s = dummy_file (file,name)) && !stat (s,&sbuf) &&
+ ((sbuf.st_mode & S_IFMT) == S_IFREG) &&
+ ((fd = open (file,(flags ? O_RDWR : O_RDONLY)|O_BINARY,NIL)) >= 0)) {
+ error = -1; /* assume bogus format */
+ if (((((j = read (fd,hdr,HDRSIZE)) == HDRSIZE) && (hdr[0] == '*')) ||
+ /* locked, set byte 0 to "*", read rest */
+ ((j < 0) && (lseek (fd,1,L_SET) == 1) &&
+ (read (fd,hdr+1,HDRSIZE-1) == (HDRSIZE-1)) && (hdr[0] = '*'))) &&
+ (hdr[1] == 'm') && (hdr[2] == 'b') && (hdr[3] == 'x') &&
+ (hdr[4] == '*') && (hdr[5] == '\015') && (hdr[6] == '\012') &&
+ isxdigit (hdr[7]) && isxdigit (hdr[8]) && isxdigit (hdr[9]) &&
+ isxdigit (hdr[10]) && isxdigit (hdr[11]) && isxdigit (hdr[12]) &&
+ isxdigit (hdr[13]) && isxdigit (hdr[14]) && isxdigit (c = hdr[15]) &&
+ isxdigit (hdr[16]) && isxdigit (hdr[17]) && isxdigit (hdr[18]) &&
+ isxdigit (hdr[19]) && isxdigit (hdr[20]) && isxdigit (hdr[21]) &&
+ isxdigit (hdr[22]) && (hdr[23] == '\015') && (hdr[24] == '\012')) {
+ ret = fd; /* mbx format */
+
+ if (stream) { /* lock if making a mini-stream */
+ if (flock (fd,LOCK_SH) ||
+ (flags && ((*ld = lockname (lock,file,LOCK_EX)) < 0))) ret = -1;
+ /* reread data now that locked */
+ else if (lseek (fd,0,L_SET) ||
+ (read (fd,hdr+1,HDRSIZE-1) != (HDRSIZE-1))) ret = -1;
+ else {
+ *stream = (MAILSTREAM *) memset (fs_get (sizeof (MAILSTREAM)),0,
+ sizeof (MAILSTREAM));
+ hdr[15] = '\0'; /* tie off UIDVALIDITY */
+ (*stream)->uid_validity = strtoul (hdr+7,NIL,16);
+ hdr[15] = c; /* now get UIDLAST */
+ (*stream)->uid_last = strtoul (hdr+15,NIL,16);
+ /* parse user flags */
+ for (i = 0, s = hdr + 25;
+ (i < NUSERFLAGS) && (t = strchr (s,'\015')) && (t - s);
+ i++, s = t + 2) {
+ *t = '\0'; /* tie off flag */
+ if (strlen (s) <= MAXUSERFLAG)
+ (*stream)->user_flags[i] = cpystr (s);
+ }
+ /* make sure have true UIDLAST */
+ if (flags & MBXISVALIDUID) {
+ for (upd = NIL,pos = 2048, k = 0; pos < sbuf.st_size;
+ pos += (j + k)) {
+ /* read header for this message */
+ lseek (fd,pos,L_SET);
+ if ((j = read (fd,hdr,64)) >= 0) {
+ hdr[j] = '\0';
+ if ((s = strchr (hdr,'\015')) && (s[1] == '\012')) {
+ *s = '\0';
+ k = s + 2 - hdr;
+ if ((s = strchr (hdr,',')) && (j = strtol (s+1,&s,10)) &&
+ (*s == ';') && (s = strchr (s+1,'-'))) {
+ /* get UID if there is any */
+ i = strtoul (++s,&t,16);
+ if (!*t && (t == (s + 8)) && (i <= (*stream)->uid_last)) {
+ if (!i) {
+ lseek (fd,pos + s - hdr,L_SET);
+ sprintf (hdr,"%08lx",++(*stream)->uid_last);
+ write (fd,hdr,8);
+ upd = T;
+ }
+ continue;
+ }
+ }
+ }
+ ret = -1; /* error, give up */
+ *stream = mail_close (*stream);
+ pos = sbuf.st_size + 1;
+ j = k = 0;
+ }
+ }
+
+ if (upd) { /* need to update hdr with new UIDLAST? */
+ lseek (fd,15,L_SET);
+ sprintf (hdr,"%08lx",(*stream)->uid_last);
+ write (fd,hdr,8);
+ }
+ }
+ }
+ }
+ }
+ if (ret != fd) close (fd); /* close the file */
+ else lseek (fd,0,L_SET); /* else rewind to start */
+ /* \Marked status? */
+ if (sbuf.st_ctime > sbuf.st_atime) {
+ /* preserve atime and mtime */
+ times.actime = sbuf.st_atime;
+ times.modtime = sbuf.st_mtime;
+ utime (file,&times); /* set the times */
+ }
+ }
+ /* in case INBOX but not mbx format */
+ else if (((error = errno) == ENOENT) && !compare_cstring (name,"INBOX"))
+ error = -1;
+ if ((ret < 0) && ld && (*ld >= 0)) {
+ unlockfd (*ld,lock);
+ *ld = -1;
+ }
+ errno = error; /* return as last error */
+ return ret; /* return what we should */
+}
+
+/* MBX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mbx_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_ONETIMEEXPUNGEATPING:
+ if (value) ((MBXLOCAL *) ((MAILSTREAM *) value)->local)->expok = T;
+ case GET_ONETIMEEXPUNGEATPING:
+ if (value) ret = (void *)
+ (((MBXLOCAL *) ((MAILSTREAM *) value)->local)->expok ? VOIDT : NIL);
+ break;
+ }
+ return ret;
+}
+
+
+/* MBX mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mbx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* MBX mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mbx_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* MBX mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mbx_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* MBX mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,mbx[MAILTMPLEN],tmp[HDRSIZE];
+ long ret = NIL;
+ int i,fd;
+ if (!(s = dummy_file (mbx,mailbox))) {
+ sprintf (mbx,"Can't create %.80s: invalid name",mailbox);
+ mm_log (mbx,ERROR);
+ }
+ /* create underlying file */
+ else if (dummy_create (stream,s)) {
+ /* done if made directory */
+ if ((s = strrchr (s,'\\')) && !s[1]) return T;
+ if ((fd = open (mbx,O_WRONLY|O_BINARY,NIL)) < 0) {
+ sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno));
+ mm_log (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ else {
+ memset (tmp,'\0',HDRSIZE);/* initialize header */
+ sprintf (s = tmp,"*mbx*\015\012%08lx00000000\015\012",
+ (unsigned long) time (0));
+ for (i = 0; i < NUSERFLAGS; ++i)
+ sprintf (s += strlen (s),"%s\015\012",
+ (stream && stream->user_flags[i]) ? stream->user_flags[i] :
+ "");
+ if (write (fd,tmp,HDRSIZE) != HDRSIZE) {
+ sprintf (tmp,"Can't initialize mailbox node %.80s: %s",
+ mbx,strerror (errno));
+ mm_log (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ else ret = T; /* success */
+ close (fd); /* close file */
+ }
+ }
+ return ret;
+}
+
+
+/* MBX mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return mbx_rename (stream,mailbox,NIL);
+}
+
+/* MBX mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = LONGT;
+ char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ int fd,ld;
+ struct stat sbuf;
+ if (!dummy_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'\\')) && !s[1])))) {
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ else if ((fd = open (file,O_RDWR|O_BINARY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get parse/append permission */
+ if ((ld = lockname (lock,file,LOCK_EX)) < 0) {
+ mm_log ("Unable to lock rename mailbox",ERROR);
+ return NIL;
+ }
+ /* lock out other users */
+ if (flock (fd,LOCK_EX|LOCK_NB)) {
+ close (fd); /* couldn't lock, give up on it then */
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+ mm_log (tmp,ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return NIL;
+ }
+
+ if (newname) { /* want rename? */
+ /* found superior to destination name? */
+ if ((s = strrchr (tmp,'\\')) && (s != tmp) &&
+ ((tmp[1] != ':') || (s != tmp + 2))) {
+ c = s[1]; /* remember character after delimiter */
+ *s = s[1] = '\0'; /* tie off name at delimiter */
+ /* name doesn't exist, create it */
+ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) {
+ *s = '\\'; /* restore delimiter */
+ if (!dummy_create (stream,tmp)) ret = NIL;
+ }
+ else *s = '\\'; /* restore delimiter */
+ s[1] = c; /* restore character after delimiter */
+ }
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* pacify NTFS */
+ /* rename the file */
+ if (ret && rename (file,tmp)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ else {
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* pacify NTFS */
+ if (unlink (file)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ /* recreate file if renamed INBOX */
+ if (ret && !compare_cstring (old,"INBOX")) mbx_create (NIL,"INBOX");
+ return ret; /* return success */
+}
+
+/* MBX mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mbx_open (MAILSTREAM *stream)
+{
+ int fd,ld;
+ short silent;
+ char tmp[MAILTMPLEN];
+ if (!stream) return &mbxproto;/* return prototype for OP_PROTOTYPE call */
+ if (stream->local) fatal ("mbx recycle stream");
+ /* canonicalize the mailbox name */
+ if (!dummy_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ mm_log (tmp,ERROR);
+ }
+ if (stream->rdonly ||
+ (fd = open (tmp,O_RDWR|O_BINARY,NIL)) < 0) {
+ if ((fd = open (tmp,O_RDONLY|O_BINARY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ else if (!stream->rdonly) { /* got it, but readonly */
+ mm_log ("Can't get write access to mailbox, access is readonly",WARN);
+ stream->rdonly = T;
+ }
+ }
+
+ stream->local = memset (fs_get (sizeof (MBXLOCAL)),NIL,sizeof (MBXLOCAL));
+ LOCAL->fd = fd; /* bind the file */
+ LOCAL->ld = -1; /* no flaglock */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ /* get parse/append permission */
+ if ((ld = lockname (tmp,stream->mailbox,LOCK_EX)) < 0) {
+ mm_log ("Unable to lock open mailbox",ERROR);
+ return NIL;
+ }
+ flock (LOCAL->fd,LOCK_SH); /* lock the file */
+ unlockfd (ld,tmp); /* release shared parse permission */
+ LOCAL->filesize = HDRSIZE; /* initialize parsed file size */
+ LOCAL->filetime = 0; /* time not set up yet */
+ LOCAL->expok = LOCAL->flagcheck = NIL;
+ stream->sequence++; /* bump sequence number */
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ silent = stream->silent; /* defer events */
+ stream->silent = T;
+ if (mbx_ping (stream) && !stream->nmsgs)
+ mm_log ("Mailbox is empty",(long) NIL);
+ stream->silent = silent; /* now notify upper level */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,stream->recent);
+ if (!LOCAL) return NIL; /* failure if stream died */
+ stream->perm_seen = stream->perm_deleted = stream->perm_flagged =
+ stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ stream->kwd_create = (stream->user_flags[NUSERFLAGS-1] || stream->rdonly) ?
+ NIL : T; /* can we create new user flags? */
+ return stream; /* return stream to caller */
+}
+
+/* MBX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mbx_close (MAILSTREAM *stream,long options)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ /* do an expunge if requested */
+ if (options & CL_EXPUNGE) mbx_expunge (stream,NIL,NIL);
+ else { /* otherwise do a checkpoint to purge */
+ LOCAL->expok = T; /* possible expunged messages */
+ mbx_ping (stream);
+ }
+ stream->silent = silent; /* restore previous status */
+ mbx_abort (stream);
+ }
+}
+
+
+/* MBX mail abort stream
+ * Accepts: MAIL stream
+ */
+
+void mbx_abort (MAILSTREAM *stream)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ flock (LOCAL->fd,LOCK_UN); /* unlock local file */
+ close (LOCAL->fd); /* close the local file */
+ /* free local text buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+
+/* MBX mail fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ * Sniffs at file to see if some other process changed the flags
+ */
+
+void mbx_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned long i;
+ if (mbx_ping (stream) && /* ping mailbox, get new status for messages */
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence && !elt->valid)
+ mbx_elt (stream,i,NIL);
+}
+
+/* MBX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *mbx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags)
+{
+ unsigned long i;
+ char *s;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ /* get header position, possibly header */
+ i = mbx_hdrpos (stream,msgno,length,&s);
+ if (!s) { /* mbx_hdrpos() returned header? */
+ lseek (LOCAL->fd,i,L_SET); /* no, get to header position */
+ /* is buffer big enough? */
+ if (*length > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = *length) + 1);
+ }
+ /* slurp the data */
+ read (LOCAL->fd,s = LOCAL->buf,*length);
+ }
+ s[*length] = '\0'; /* tie off string */
+ return s;
+}
+
+/* MBX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ FDDATA d;
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ /* get message status */
+ elt = mbx_elt (stream,msgno,NIL);
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen && mbx_flaglock (stream)) {
+ elt->seen = T; /* mark message as seen */
+ /* recalculate status */
+ mbx_update_status (stream,msgno,NIL);
+ mm_flags (stream,msgno);
+ /* update flags */
+ mbx_flag (stream,NIL,NIL,NIL);
+ }
+ if (!LOCAL) return NIL; /* mbx_flaglock() could have aborted */
+ /* find header position */
+ i = mbx_hdrpos (stream,msgno,&j,NIL);
+ d.fd = LOCAL->fd; /* set up file descriptor */
+ d.pos = i + j;
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE;
+ INIT (bs,fd_string,&d,elt->rfc822_size - j);
+ return LONGT; /* success */
+}
+
+/* MBX mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ * Unlocks flag lock
+ */
+
+void mbx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ struct utimbuf times;
+ struct stat sbuf;
+ /* make sure the update takes */
+ if (!stream->rdonly && LOCAL && (LOCAL->fd >= 0) && (LOCAL->ld >= 0)) {
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ /* update header */
+ if ((LOCAL->ffuserflag < NUSERFLAGS) &&
+ stream->user_flags[LOCAL->ffuserflag]) mbx_update_header (stream);
+ times.actime = time (0); /* make sure read comes after all that */
+ utime (stream->mailbox,&times);
+ }
+ if (LOCAL->ld >= 0) { /* unlock now */
+ unlockfd (LOCAL->ld,LOCAL->lock);
+ LOCAL->ld = -1;
+ }
+}
+
+
+/* MBX mail per-message modify flags
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void mbx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ if (mbx_flaglock (stream)) mbx_update_status (stream,elt->msgno,NIL);
+}
+
+/* MBX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream still alive, NIL if not
+ */
+
+long mbx_ping (MAILSTREAM *stream)
+{
+ unsigned long i,pos;
+ long ret = NIL;
+ int ld;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ struct stat sbuf;
+ if (stream && LOCAL) { /* only if stream already open */
+ ret = LONGT; /* assume OK */
+ fstat (LOCAL->fd,&sbuf); /* get current file poop */
+ /* allow expunge if permitted at ping */
+ if (mail_parameters (NIL,GET_EXPUNGEATPING,NIL)) LOCAL->expok = T;
+ /* if external modification */
+ if (LOCAL->filetime && (LOCAL->filetime < sbuf.st_mtime))
+ LOCAL->flagcheck = T; /* upgrade to flag checking */
+ /* new mail or flagcheck handling needed? */
+ if (((sbuf.st_size - LOCAL->filesize) || LOCAL->flagcheck ||
+ !stream->nmsgs) &&
+ ((ld = lockname (lock,stream->mailbox,LOCK_EX)) >= 0)) {
+ if (!LOCAL->flagcheck) ret = mbx_parse (stream);
+ /* sweep mailbox for changed message status */
+ else if (ret = mbx_parse (stream)) {
+ unsigned long recent = 0;
+ LOCAL->filetime = sbuf.st_mtime;
+ for (i = 1; i <= stream->nmsgs; )
+ if (elt = mbx_elt (stream,i,LOCAL->expok)) {
+ if (elt->recent) ++recent;
+ ++i;
+ }
+ mail_recent (stream,recent);
+ LOCAL->flagcheck = NIL; /* got all the updates */
+ }
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ if (ret) { /* must still be alive */
+ if (!LOCAL->expunged) /* look for holes if none known yet */
+ for (i = 1, pos = HDRSIZE;
+ !LOCAL->expunged && (i <= stream->nmsgs);
+ i++, pos += elt->private.special.text.size + elt->rfc822_size)
+ if ((elt = mail_elt (stream,i))->private.special.offset != pos)
+ LOCAL->expunged = T;/* found a hole */
+ /* burp any holes */
+ if (LOCAL->expunged && !stream->rdonly) {
+ if (mbx_rewrite (stream,&i,NIL)) fatal ("expunge on check");
+ if (i) { /* any space reclaimed? */
+ LOCAL->expunged = NIL;/* no more pending expunge */
+ sprintf (LOCAL->buf,"Reclaimed %lu bytes of expunged space",i);
+ mm_log (LOCAL->buf,(long) NIL);
+ }
+ }
+ LOCAL->expok = NIL; /* no more expok */
+ }
+ }
+ return ret; /* return result of the parse */
+}
+
+/* MBX mail check mailbox (reparses status too)
+ * Accepts: MAIL stream
+ */
+
+void mbx_check (MAILSTREAM *stream)
+{
+ if (LOCAL) LOCAL->expok = T; /* mark that a check is desired */
+ if (mbx_ping (stream)) mm_log ("Check completed",(long) NIL);
+}
+
+
+/* MBX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T if success, NIL if failure
+ */
+
+long mbx_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ unsigned long nexp,reclaimed;
+ if (ret = sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) {
+ if (!mbx_ping (stream)); /* do nothing if stream dead */
+ else if (stream->rdonly) /* won't do on readonly files! */
+ mm_log ("Expunge ignored on readonly mailbox",WARN);
+ /* if expunged any messages */
+ else if (nexp = mbx_rewrite (stream,&reclaimed,sequence ? -1 : 1)) {
+ sprintf (LOCAL->buf,"Expunged %lu messages",nexp);
+ mm_log (LOCAL->buf,(long) NIL);
+ }
+ else if (reclaimed) { /* or if any prior expunged space reclaimed */
+ sprintf (LOCAL->buf,"Reclaimed %lu bytes of expunged space",reclaimed);
+ mm_log (LOCAL->buf,(long) NIL);
+ }
+ else mm_log ("No messages deleted, so no update needed",(long) NIL);
+ }
+ return ret;
+}
+
+/* MBX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if success, NIL if failed
+ */
+
+long mbx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ struct utimbuf times;
+ MESSAGECACHE *elt;
+ unsigned long i,j,k,m;
+ long ret = LONGT;
+ int fd,ld;
+ char *s,*t,file[MAILTMPLEN],lock[MAILTMPLEN];
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ copyuid_t cu = (copyuid_t) mail_parameters (NIL,GET_COPYUID,NIL);
+ SEARCHSET *source = cu ? mail_newsearchset () : NIL;
+ SEARCHSET *dest = cu ? mail_newsearchset () : NIL;
+ MAILSTREAM *dstream = NIL;
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* make sure valid mailbox */
+ if ((fd = mbx_isvalid (&dstream,mailbox,file,&ld,lock,
+ cu ? MBXISVALIDUID : MBXISVALIDNOUID)) < 0)
+ switch (errno) {
+ case ENOENT: /* no such file? */
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid MBX-format mailbox name: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a MBX-format mailbox: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ /* got file? */
+ if ((fd = open (dummy_file (file,mailbox),O_RDWR|O_CREAT|O_BINARY,
+ S_IREAD|S_IWRITE)) < 0) {
+ sprintf (LOCAL->buf,"Unable to open copy mailbox: %s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ mm_critical (stream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
+
+ /* for each requested message */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.special.text.size,L_SET);
+ mail_date(LOCAL->buf,elt);/* build target header */
+ /* get target keyword mask */
+ for (j = elt->user_flags, k = 0; j; )
+ if (s = stream->user_flags[find_rightmost_bit (&j)])
+ for (m = 0; (m < NUSERFLAGS) && (t = dstream->user_flags[m]); m++)
+ if (!compare_cstring (s,t) && (k |= 1 << m)) break;
+ sprintf (LOCAL->buf+strlen(LOCAL->buf),",%lu;%08lx%04x-%08lx\015\012",
+ elt->rfc822_size,k,(unsigned)
+ ((fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)),cu ? ++dstream->uid_last : 0);
+ /* write target header */
+ if (ret = (write (fd,LOCAL->buf,strlen (LOCAL->buf)) > 0)) {
+ for (k = elt->rfc822_size; ret && (j = min (k,LOCAL->buflen)); k -= j){
+ read (LOCAL->fd,LOCAL->buf,j);
+ ret = write (fd,LOCAL->buf,j) >= 0;
+ }
+ if (cu) { /* need to pass back new UID? */
+ mail_append_set (source,mail_uid (stream,i));
+ mail_append_set (dest,dstream->uid_last);
+ }
+ }
+ }
+
+ /* make sure all the updates take */
+ if (!(ret && (ret = !fsync (fd)))) {
+ sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ }
+ if (cu && ret) { /* return sets if doing COPYUID */
+ (*cu) (stream,mailbox,dstream->uid_validity,source,dest);
+ lseek (fd,15,L_SET); /* update UIDLAST */
+ sprintf (LOCAL->buf,"%08lx",dstream->uid_last);
+ write (fd,LOCAL->buf,8);
+ }
+ else { /* flush any sets we may have built */
+ mail_free_searchset (&source);
+ mail_free_searchset (&dest);
+ }
+ /* set atime to now-1 if successful copy */
+ if (ret) times.actime = time (0) - 1;
+ /* else preserved \Marked status */
+ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ?
+ sbuf.st_atime : time (0);
+ times.modtime = sbuf.st_mtime;/* preserve mtime */
+ utime (file,&times); /* set the times */
+ close (fd); /* close the file */
+ mm_nocritical (stream); /* release critical */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ /* delete all requested messages */
+ if (ret && (options & CP_MOVE) && mbx_flaglock (stream)) {
+ for (i = 1; i <= stream->nmsgs; i++) if (mail_elt (stream,i)->sequence) {
+ /* mark message deleted */
+ mbx_elt (stream,i,NIL)->deleted = T;
+ /* recalculate status */
+ mbx_update_status (stream,i,NIL);
+ }
+ /* update flags */
+ mbx_flag (stream,NIL,NIL,NIL);
+ }
+ if (dstream != stream) mail_close (dstream);
+ return ret;
+}
+
+/* MBX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mbx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd,ld;
+ char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ struct utimbuf times;
+ FILE *df;
+ MESSAGECACHE elt;
+ long f;
+ unsigned long i,uf;
+ STRING *message;
+ long ret = NIL;
+ MAILSTREAM *dstream = NIL;
+ appenduid_t au = (appenduid_t) mail_parameters (NIL,GET_APPENDUID,NIL);
+ SEARCHSET *dst = au ? mail_newsearchset () : NIL;
+ /* make sure valid mailbox */
+ /* make sure valid mailbox */
+ if ((fd = mbx_isvalid (&dstream,mailbox,file,&ld,lock,
+ au ? MBXISVALIDUID : MBXISVALIDNOUID)) < 0)
+ switch (errno) {
+ case ENOENT: /* no such file? */
+ if (compare_cstring (mailbox,"INBOX")) {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* can create INBOX here */
+ mbx_create (dstream = stream ? stream : &mbxproto,"INBOX");
+ if ((fd = mbx_isvalid (&dstream,mailbox,file,&ld,lock,
+ au ? MBXISVALIDUID : MBXISVALIDNOUID)) < 0)
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid MBX-format mailbox name: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a MBX-format mailbox: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+
+ /* get first message */
+ if (!(*af) (dstream,data,&flags,&date,&message)) close (fd);
+ else if (!(df = fdopen (fd,"r+b"))) {
+ MM_LOG ("Unable to reopen append mailbox",ERROR);
+ close (fd);
+ }
+ else {
+ mm_critical (dstream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ fseek (df,sbuf.st_size,SEEK_SET);
+ errno = 0;
+ for (ret = LONGT; ret && message; ) {
+ if (!SIZE (message)) { /* guard against zero-length */
+ mm_log ("Append of zero-length message",ERROR);
+ ret = NIL;
+ break;
+ }
+ f = mail_parse_flags (dstream,flags,&uf);
+ if (date) { /* parse date if given */
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ mm_log (tmp,ERROR);
+ ret = NIL; /* mark failure */
+ break;
+ }
+ mail_date (tmp,&elt); /* write preseved date */
+ }
+ else internal_date (tmp); /* get current date in IMAP format */
+ /* write header */
+ if (fprintf (df,"%s,%lu;%08lx%04lx-%08lx\015\012",tmp,i = SIZE (message),
+ uf,(unsigned long) f,au ? ++dstream->uid_last : 0) < 0)
+ ret = NIL;
+ else { /* write message */
+ size_t j;
+ if (!message->cursize) SETPOS (message,GETPOS (message));
+ while (i && (j = fwrite (message->curpos,1,message->cursize,df))) {
+ i -= j;
+ SETPOS (message,GETPOS (message) + j);
+ }
+ /* get next message */
+ if (i || !(*af) (dstream,data,&flags,&date,&message)) ret = NIL;
+ else if (au) mail_append_set (dst,dstream->uid_last);
+ }
+ }
+
+ /* if error... */
+ if (!ret || (fflush (df) == EOF)) {
+ /* revert file */
+ ftruncate (fd,sbuf.st_size);
+ close (fd); /* make sure fclose() doesn't corrupt us */
+ if (errno) {
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ }
+ ret = NIL;
+ }
+ if (au && ret) { /* return sets if doing APPENDUID */
+ (*au) (mailbox,dstream->uid_validity,dst);
+ fseek (df,15,SEEK_SET); /* update UIDLAST */
+ fprintf (df,"%08lx",dstream->uid_last);
+ }
+ else mail_free_searchset (&dst);
+ if (ret) times.actime = time (0) - 1;
+ /* else preserve \Marked status */
+ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ?
+ sbuf.st_atime : time (0);
+ /* preserve mtime */
+ times.modtime = sbuf.st_mtime;
+ utime (file,&times); /* set the times */
+ fclose (df); /* close the file */
+ mm_nocritical (dstream); /* release critical */
+ }
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ if (dstream != stream) mail_close (dstream);
+ return ret;
+}
+
+/* Internal routines */
+
+
+/* MBX mail parse mailbox
+ * Accepts: MAIL stream
+ * Returns: T if parse OK
+ * NIL if failure, stream aborted
+ */
+
+long mbx_parse (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = NIL;
+ unsigned char c,*s,*t,*x;
+ char tmp[MAILTMPLEN];
+ unsigned long i,j,k,m;
+ off_t curpos = LOCAL->filesize;
+ unsigned long nmsgs = stream->nmsgs;
+ unsigned long recent = stream->recent;
+ unsigned long lastuid = 0;
+ short dirty = NIL;
+ short added = NIL;
+ short silent = stream->silent;
+ short uidwarn = T;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ if (sbuf.st_size < curpos) { /* sanity check */
+ sprintf (tmp,"Mailbox shrank from %lu to %lu!",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ lseek (LOCAL->fd,0,L_SET); /* rewind file */
+ /* read internal header */
+ read (LOCAL->fd,LOCAL->buf,HDRSIZE);
+ LOCAL->buf[HDRSIZE] = '\0'; /* tie off header */
+ c = LOCAL->buf[15]; /* save first character of last UID */
+ LOCAL->buf[15] = '\0';
+ /* parse UID validity */
+ stream->uid_validity = strtoul (LOCAL->buf + 7,NIL,16);
+ LOCAL->buf[15] = c; /* restore first character of last UID */
+ /* parse last UID */
+ i = strtoul (LOCAL->buf + 15,NIL,16);
+ stream->uid_last = stream->rdonly ? max (i,stream->uid_last) : i;
+ /* parse user flags */
+ for (i = 0, s = LOCAL->buf + 25;
+ (i < NUSERFLAGS) && (t = strchr (s,'\015')) && (t - s);
+ i++, s = t + 2) {
+ *t = '\0'; /* tie off flag */
+ if (!stream->user_flags[i] && (strlen (s) <= MAXUSERFLAG))
+ stream->user_flags[i] = cpystr (s);
+ }
+ LOCAL->ffuserflag = (int) i; /* first free user flag */
+
+ stream->silent = T; /* don't pass up mm_exists() events yet */
+ while (sbuf.st_size - curpos){/* while there is stuff to parse */
+ /* get to that position in the file */
+ lseek (LOCAL->fd,curpos,L_SET);
+ if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
+ sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size,
+ i ? strerror (errno) : "no data read");
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ LOCAL->buf[i] = '\0'; /* tie off buffer just in case */
+ if (!((s = strchr (LOCAL->buf,'\015')) && (s[1] == '\012'))) {
+ sprintf (tmp,"Unable to find CRLF at %lu in %lu bytes, text: %.80s",
+ (unsigned long) curpos,i,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ *s = '\0'; /* tie off header line */
+ i = (s + 2) - LOCAL->buf; /* note start of text offset */
+ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
+ sprintf (tmp,"Unable to parse internal header at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ if (!(isxdigit (t[1]) && isxdigit (t[2]) && isxdigit (t[3]) &&
+ isxdigit (t[4]) && isxdigit (t[5]) && isxdigit (t[6]) &&
+ isxdigit (t[7]) && isxdigit (t[8]) && isxdigit (t[9]) &&
+ isxdigit (t[10]) && isxdigit (t[11]) && isxdigit (t[12]))) {
+ sprintf (tmp,"Unable to parse message flags at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ if ((t[13] != '-') || t[22] ||
+ !(isxdigit (t[14]) && isxdigit (t[15]) && isxdigit (t[16]) &&
+ isxdigit (t[17]) && isxdigit (t[18]) && isxdigit (t[19]) &&
+ isxdigit (t[20]) && isxdigit (t[21]))) {
+ sprintf (tmp,"Unable to parse message UID at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+
+ *s++ = '\0'; *t++ = '\0'; /* break up fields */
+ /* get message size */
+ if (!(j = strtoul (s,(char **) &x,10)) && (!(x && *x))) {
+ sprintf (tmp,"Unable to parse message size at %lu: %.80s,%.80s;%.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf,(char *) s,
+ (char *) t);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ /* make sure didn't run off end of file */
+ if (((off_t) (curpos + i + j)) > sbuf.st_size) {
+ sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)",
+ (unsigned long) curpos,(unsigned long) (curpos + i + j),
+ (unsigned long) sbuf.st_size);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ /* parse UID */
+ if ((m = strtoul (t+13,NIL,16)) &&
+ ((m <= lastuid) || (m > stream->uid_last))) {
+ if (uidwarn) {
+ sprintf (tmp,"Invalid UID %08lx in message %lu, rebuilding UIDs",
+ m,nmsgs+1);
+ mm_log (tmp,WARN);
+ uidwarn = NIL;
+ /* restart UID validity */
+ stream->uid_validity = (unsigned long) time (0);
+ }
+ m = 0; /* lose this UID */
+ dirty = T; /* mark dirty, set new lastuid */
+ stream->uid_last = lastuid;
+ }
+
+ t[12] = '\0'; /* parse system flags */
+ if ((k = strtoul (t+8,NIL,16)) & fEXPUNGED) {
+ if (m) lastuid = m; /* expunge message, update last UID seen */
+ else { /* no UID assigned? */
+ lastuid = ++stream->uid_last;
+ dirty = T;
+ }
+ }
+ else { /* not expunged, swell the cache */
+ added = T; /* note that a new message was added */
+ mail_exists (stream,++nmsgs);
+ /* instantiate an elt for this message */
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ /* parse the date */
+ if (!mail_parse_date (elt,LOCAL->buf)) {
+ sprintf (tmp,"Unable to parse message date at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ /* note file offset of header */
+ elt->private.special.offset = curpos;
+ /* and internal header size */
+ elt->private.special.text.size = i;
+ /* header size not known yet */
+ elt->private.msg.header.text.size = 0;
+ elt->rfc822_size = j; /* note message size */
+ /* calculate system flags */
+ if (k & fSEEN) elt->seen = T;
+ if (k & fDELETED) elt->deleted = T;
+ if (k & fFLAGGED) elt->flagged = T;
+ if (k & fANSWERED) elt->answered = T;
+ if (k & fDRAFT) elt->draft = T;
+ t[8] = '\0'; /* get user flags value */
+ elt->user_flags = strtoul (t,NIL,16);
+ /* UID already assigned? */
+ if (!(elt->private.uid = m) || !(k & fOLD)) {
+ elt->recent = T; /* no, mark as recent */
+ ++recent; /* count up a new recent message */
+ dirty = T; /* and must rewrite header */
+ /* assign new UID */
+ if (!elt->private.uid) elt->private.uid = ++stream->uid_last;
+ mbx_update_status (stream,elt->msgno,NIL);
+ }
+ /* update last parsed UID */
+ lastuid = elt->private.uid;
+ }
+ curpos += i + j; /* update position */
+ }
+
+ if (dirty && !stream->rdonly){/* update header */
+ mbx_update_header (stream);
+ fsync (LOCAL->fd); /* make sure all the UID updates take */
+ }
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */
+ LOCAL->filetime = sbuf.st_mtime;
+ if (added && !stream->rdonly){/* make sure atime updated */
+ struct utimbuf times;
+ times.actime = time (0);
+ times.modtime = LOCAL->filetime;
+ utime (stream->mailbox,&times);
+ }
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
+ mail_recent (stream,recent); /* and of change in recent messages */
+ return LONGT; /* return the winnage */
+}
+
+/* MBX get cache element with status updating from file
+ * Accepts: MAIL stream
+ * message number
+ * expunge OK flag
+ * Returns: cache element
+ */
+
+MESSAGECACHE *mbx_elt (MAILSTREAM *stream,unsigned long msgno,long expok)
+{
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ struct { /* old flags */
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged;
+ old.answered = elt->answered; old.draft = elt->draft;
+ old.user_flags = elt->user_flags;
+ /* get new flags */
+ if (mbx_read_flags (stream,elt) && expok) {
+ mail_expunged (stream,elt->msgno);
+ return NIL; /* return this message was expunged */
+ }
+ if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
+ (old.flagged != elt->flagged) || (old.answered != elt->answered) ||
+ (old.draft != elt->draft) || (old.user_flags != elt->user_flags))
+ mm_flags (stream,msgno); /* let top level know */
+ return elt;
+}
+
+/* MBX read flags from file
+ * Accepts: MAIL stream
+ * cache element
+ * Returns: non-NIL if message expunged
+ */
+
+unsigned long mbx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ unsigned long i;
+ struct stat sbuf;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ /* make sure file size is good */
+ if (sbuf.st_size < LOCAL->filesize) {
+ sprintf (LOCAL->buf,"Mailbox shrank from %lu to %lu in flag read!",
+ (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size);
+ fatal (LOCAL->buf);
+ }
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 24,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,14) < 0) {
+ sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ if ((LOCAL->buf[0] != ';') || (LOCAL->buf[13] != '-')) {
+ LOCAL->buf[14] = '\0'; /* tie off buffer for error message */
+ sprintf (LOCAL->buf+50,"Invalid flags for message %lu (%lu %lu): %s",
+ elt->msgno,elt->private.special.offset,
+ elt->private.special.text.size,(char *) LOCAL->buf);
+ fatal (LOCAL->buf+50);
+ }
+ LOCAL->buf[13] = '\0'; /* tie off buffer */
+ /* calculate system flags */
+ i = strtoul (LOCAL->buf+9,NIL,16);
+ elt->seen = i & fSEEN ? T : NIL;
+ elt->deleted = i & fDELETED ? T : NIL;
+ elt->flagged = i & fFLAGGED ? T : NIL;
+ elt->answered = i & fANSWERED ? T : NIL;
+ elt->draft = i & fDRAFT ? T : NIL;
+ LOCAL->expunged |= i & fEXPUNGED ? T : NIL;
+ LOCAL->buf[9] = '\0'; /* tie off flags */
+ /* get user flags value */
+ elt->user_flags = strtoul (LOCAL->buf+1,NIL,16);
+ elt->valid = T; /* have valid flags now */
+ return i & fEXPUNGED;
+}
+
+/* MBX update header
+ * Accepts: MAIL stream
+ */
+
+#define NTKLUDGEOFFSET 7
+
+void mbx_update_header (MAILSTREAM *stream)
+{
+ int i;
+ char *s = LOCAL->buf;
+ memset (s,'\0',HDRSIZE); /* initialize header */
+ sprintf (s,"*mbx*\015\012%08lx%08lx\015\012",
+ stream->uid_validity,stream->uid_last);
+ for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i]; ++i)
+ sprintf (s += strlen (s),"%s\015\012",stream->user_flags[i]);
+ LOCAL->ffuserflag = i; /* first free user flag */
+ /* can we create more user flags? */
+ stream->kwd_create = (i < NUSERFLAGS) ? T : NIL;
+ /* write reserved lines */
+ while (i++ < NUSERFLAGS) strcat (s,"\015\012");
+ while (T) { /* rewind file */
+ lseek (LOCAL->fd,NTKLUDGEOFFSET,L_SET);
+ /* write new header */
+ if (write (LOCAL->fd,LOCAL->buf + NTKLUDGEOFFSET,
+ HDRSIZE - NTKLUDGEOFFSET) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+}
+
+/* MBX update status string
+ * Accepts: MAIL stream
+ * message number
+ * flags
+ */
+
+void mbx_update_status (MAILSTREAM *stream,unsigned long msgno,long flags)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ /* readonly */
+ if (stream->rdonly || !elt->valid) mbx_read_flags (stream,elt);
+ else { /* readwrite */
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ /* make sure file size is good */
+ if (sbuf.st_size < LOCAL->filesize) {
+ sprintf (LOCAL->buf,"Mailbox shrank from %lu to %lu in flag update!",
+ (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size);
+ fatal (LOCAL->buf);
+ }
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 24,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,14) < 0) {
+ sprintf (LOCAL->buf,"Unable to read old status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ if ((LOCAL->buf[0] != ';') || (LOCAL->buf[13] != '-')) {
+ LOCAL->buf[14] = '\0'; /* tie off buffer for error message */
+ sprintf (LOCAL->buf+50,"Invalid flags for message %lu (%lu %lu): %s",
+ elt->msgno,elt->private.special.offset,
+ elt->private.special.text.size,(char *) LOCAL->buf);
+ fatal (LOCAL->buf+50);
+ }
+ /* print new flag string */
+ sprintf (LOCAL->buf,"%08lx%04x-%08lx",elt->user_flags,(unsigned)
+ (((elt->deleted && flags) ?
+ fEXPUNGED : (strtoul (LOCAL->buf+9,NIL,16)) & fEXPUNGED) +
+ (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft) + fOLD),elt->private.uid);
+ while (T) { /* get to that place in the file */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 23,L_SET);
+ /* write new flags and UID */
+ if (write (LOCAL->fd,LOCAL->buf,21) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+ }
+}
+
+/* MBX locate header for a message
+ * Accepts: MAIL stream
+ * message number
+ * pointer to returned header size
+ * pointer to possible returned header
+ * Returns: position of header in file
+ */
+
+#define HDRBUFLEN 16384 /* good enough for most headers */
+#define SLOP 4 /* CR LF CR LF */
+
+unsigned long mbx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size,char **hdr)
+{
+ unsigned long siz,done;
+ long i;
+ unsigned char *s,*t,*te;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ unsigned long ret = elt->private.special.offset +
+ elt->private.special.text.size;
+ if (hdr) *hdr = NIL; /* assume no header returned */
+ /* is header size known? */
+ if (*size = elt->private.msg.header.text.size) return ret;
+ /* paranoia check */
+ if (LOCAL->buflen < (HDRBUFLEN + SLOP))
+ fatal ("LOCAL->buf smaller than HDRBUFLEN");
+ lseek (LOCAL->fd,ret,L_SET); /* get to header position */
+ /* read HDRBUFLEN chunks with 4 byte slop */
+ for (done = siz = 0, s = LOCAL->buf;
+ (i = min ((long) (elt->rfc822_size - done),(long) HDRBUFLEN)) &&
+ (read (LOCAL->fd,s,i) == i);
+ done += i, siz += (t - LOCAL->buf) - SLOP, s = LOCAL->buf + SLOP) {
+ te = (t = s + i) - 12; /* calculate end of fast scan */
+ /* fast scan for CR */
+ for (s = LOCAL->buf; s < te;)
+ if (((*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') ||
+ (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') ||
+ (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') ||
+ (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015')) &&
+ (*s == '\012') && (*++s == '\015') && (*++s == '\012')) {
+ *size = elt->private.msg.header.text.size = siz + (++s - LOCAL->buf);
+ if (hdr) *hdr = LOCAL->buf;
+ return ret;
+ }
+ for (te = t - 3; (s < te);) /* final character-at-a-time scan */
+ if ((*s++ == '\015') && (*s == '\012') && (*++s == '\015') &&
+ (*++s == '\012')) {
+ *size = elt->private.msg.header.text.size = siz + (++s - LOCAL->buf);
+ if (hdr) *hdr = LOCAL->buf;
+ return ret;
+ }
+ if (i <= SLOP) break; /* end of data */
+ /* slide over last 4 bytes */
+ memmove (LOCAL->buf,t - SLOP,SLOP);
+ hdr = NIL; /* can't return header this way */
+ }
+ /* not found: header consumes entire message */
+ elt->private.msg.header.text.size = *size = elt->rfc822_size;
+ if (hdr) *hdr = LOCAL->buf; /* possibly return header too */
+ return ret;
+}
+
+/* MBX mail rewrite mailbox
+ * Accepts: MAIL stream
+ * pointer to return reclaimed size
+ * flags (0 = no expunge, 1 = expunge deleted, -1 = expunge sequence)
+ * Returns: number of expunged messages
+ */
+
+unsigned long mbx_rewrite (MAILSTREAM *stream,unsigned long *reclaimed,
+ long flags)
+{
+ struct utimbuf times;
+ struct stat sbuf;
+ off_t pos,ppos;
+ int ld;
+ unsigned long i,j,k,m,delta;
+ unsigned long n = *reclaimed = 0;
+ unsigned long recent = 0;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ /* get parse/append permission */
+ if ((ld = lockname (lock,stream->mailbox,LOCK_EX)) < 0) {
+ mm_log ("Unable to lock expunge mailbox",ERROR);
+ return 0;
+ }
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime && !LOCAL->flagcheck &&
+ (LOCAL->filetime < sbuf.st_mtime)) LOCAL->flagcheck = T;
+ if (!mbx_parse (stream)) { /* make sure see any newly-arrived messages */
+ unlockfd (ld,lock); /* failed?? */
+ return 0;
+ }
+ if (LOCAL->flagcheck) { /* sweep flags if need flagcheck */
+ LOCAL->filetime = sbuf.st_mtime;
+ for (i = 1; i <= stream->nmsgs; ++i) mbx_elt (stream,i,NIL);
+ LOCAL->flagcheck = NIL;
+ }
+
+ /* get exclusive access */
+ if (!flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
+ mm_critical (stream); /* go critical */
+ for (i = 1,delta = 0,pos = ppos = HDRSIZE; i <= stream->nmsgs; ) {
+ /* note if message not at predicted location */
+ if (m = (elt = mbx_elt (stream,i,NIL))->private.special.offset - ppos) {
+ ppos = elt->private.special.offset;
+ *reclaimed += m; /* note reclaimed message space */
+ delta += m; /* and as expunge delta */
+ }
+ /* number of bytes to smash or preserve */
+ ppos += (k = elt->private.special.text.size + elt->rfc822_size);
+ /* if need to expunge this message*/
+ if (flags && elt->deleted && ((flags > 0) || elt->sequence)) {
+ delta += k; /* number of bytes to delete */
+ mail_expunged(stream,i);/* notify upper levels */
+ n++; /* count up one more expunged message */
+ }
+ else { /* preserved message */
+ i++; /* count this message */
+ if (elt->recent) ++recent;
+ if (delta) { /* moved, note first byte to preserve */
+ j = elt->private.special.offset;
+ do { /* read from source position */
+ m = min (k,LOCAL->buflen);
+ lseek (LOCAL->fd,j,L_SET);
+ read (LOCAL->fd,LOCAL->buf,m);
+ pos = j - delta; /* write to destination position */
+ while (T) {
+ lseek (LOCAL->fd,pos,L_SET);
+ if (write (LOCAL->fd,LOCAL->buf,m) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+ pos += m; /* new position */
+ j += m; /* next chunk, perhaps */
+ } while (k -= m); /* until done */
+ /* note the new address of this text */
+ elt->private.special.offset -= delta;
+ }
+ /* preserved but no deleted messages yet */
+ else pos = elt->private.special.offset + k;
+ }
+ }
+ /* deltaed file size match position? */
+ if (m = (LOCAL->filesize -= delta) - pos) {
+ *reclaimed += m; /* probably an fEXPUNGED msg */
+ LOCAL->filesize = pos; /* set correct size */
+ }
+ /* truncate file after last message */
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ fsync (LOCAL->fd); /* force disk update */
+ mm_nocritical (stream); /* release critical */
+ flock (LOCAL->fd,LOCK_SH); /* allow sharers again */
+ }
+
+ else { /* can't get exclusive */
+ flock (LOCAL->fd,LOCK_SH); /* recover previous shared mailbox lock */
+ /* do hide-expunge when shared */
+ if (flags) for (i = 1; i <= stream->nmsgs; ) {
+ if (elt = mbx_elt (stream,i,T)) {
+ /* make the message invisible */
+ if (elt->deleted && ((flags > 0) || elt->sequence)) {
+ mbx_update_status (stream,elt->msgno,LONGT);
+ /* notify upper levels */
+ mail_expunged (stream,i);
+ n++; /* count up one more expunged message */
+ }
+ else {
+ i++; /* preserved message */
+ if (elt->recent) ++recent;
+ }
+ }
+ else n++; /* count up one more expunged message */
+ }
+ fsync (LOCAL->fd); /* force disk update */
+ }
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* reset atime to now */
+ utime (stream->mailbox,&times);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ return n; /* return number of expunged messages */
+}
+
+/* MBX mail lock for flag updating
+ * Accepts: stream
+ * Returns: T if successful, NIL if failure
+ */
+
+long mbx_flaglock (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ unsigned long i;
+ int ld;
+ char lock[MAILTMPLEN];
+ /* no-op if readonly or already locked */
+ if (!stream->rdonly && LOCAL && (LOCAL->fd >= 0) && (LOCAL->ld < 0)) {
+ /* lock now */
+ if ((ld = lockname (lock,stream->mailbox,LOCK_EX)) < 0) return NIL;
+ if (!LOCAL->flagcheck) { /* don't do this if flagcheck already needed */
+ if (LOCAL->filetime) { /* know previous time? */
+ fstat (LOCAL->fd,&sbuf);/* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->flagcheck = T;
+ LOCAL->filetime = 0; /* don't do this test for any other messages */
+ }
+ if (!mbx_parse (stream)) {/* parse mailbox */
+ unlockfd (ld,lock); /* shouldn't happen */
+ return NIL;
+ }
+ if (LOCAL->flagcheck) /* invalidate cache if flagcheck */
+ for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->valid = NIL;
+ }
+ LOCAL->ld = ld; /* copy to stream for subsequent calls */
+ memcpy (LOCAL->lock,lock,MAILTMPLEN);
+ }
+ return LONGT;
+}
diff --git a/imap/src/osdep/os2/mtxnt.c b/imap/src/osdep/os2/mtxnt.c
new file mode 100644
index 00000000..13f61586
--- /dev/null
+++ b/imap/src/osdep/os2/mtxnt.c
@@ -0,0 +1,1232 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MTX mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 22 May 1990
+ * Last Edited: 15 June 2007
+ */
+
+
+/* FILE TIME SEMANTICS
+ *
+ * The atime is the last read time of the file.
+ * The mtime is the last flags update time of the file.
+ * The ctime is the last write time of the file.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/utime.h>
+#include "misc.h"
+#include "dummy.h"
+#include "fdstring.h"
+
+/* MTX I/O stream local data */
+
+typedef struct mtx_local {
+ unsigned int shouldcheck: 1; /* if ping should do a check instead */
+ unsigned int mustcheck: 1; /* if ping must do a check instead */
+ int fd; /* file descriptor for I/O */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ time_t lastsnarf; /* last snarf time */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+} MTXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MTXLOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *mtx_valid (char *name);
+int mtx_isvalid (char *name,char *file);
+void *mtx_parameters (long function,void *value);
+void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void mtx_list (MAILSTREAM *stream,char *ref,char *pat);
+void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long mtx_create (MAILSTREAM *stream,char *mailbox);
+long mtx_delete (MAILSTREAM *stream,char *mailbox);
+long mtx_rename (MAILSTREAM *stream,char *old,char *newname);
+long mtx_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *mtx_open (MAILSTREAM *stream);
+void mtx_close (MAILSTREAM *stream,long options);
+void mtx_flags (MAILSTREAM *stream,char *sequence,long flags);
+char *mtx_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void mtx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long mtx_ping (MAILSTREAM *stream);
+void mtx_check (MAILSTREAM *stream);
+void mtx_snarf (MAILSTREAM *stream);
+long mtx_expunge (MAILSTREAM *stream,char *sequence,long options);
+long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+long mtx_parse (MAILSTREAM *stream);
+MESSAGECACHE *mtx_elt (MAILSTREAM *stream,unsigned long msgno);
+void mtx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt);
+void mtx_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag);
+unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size);
+
+
+/* MTX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mtxdriver = {
+ "mtx", /* driver name */
+ /* driver flags */
+ DR_LOCAL|DR_MAIL|DR_CRLF|DR_NOSTICKY,
+ (DRIVER *) NIL, /* next driver */
+ mtx_valid, /* mailbox is valid for us */
+ mtx_parameters, /* manipulate parameters */
+ mtx_scan, /* scan mailboxes */
+ mtx_list, /* list mailboxes */
+ mtx_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ mtx_create, /* create mailbox */
+ mtx_delete, /* delete mailbox */
+ mtx_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ mtx_open, /* open mailbox */
+ mtx_close, /* close mailbox */
+ mtx_flags, /* fetch message "fast" attributes */
+ mtx_flags, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mtx_header, /* fetch message header */
+ mtx_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ mtx_flag, /* modify flags */
+ mtx_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mtx_ping, /* ping mailbox to see if still alive */
+ mtx_check, /* check for new messages */
+ mtx_expunge, /* expunge deleted messages */
+ mtx_copy, /* copy messages to another mailbox */
+ mtx_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mtxproto = {&mtxdriver};
+
+/* MTX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mtx_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return mtx_isvalid (name,tmp) ? &mtxdriver : NIL;
+}
+
+
+/* MTX mail test for valid mailbox
+ * Accepts: mailbox name
+ * buffer to return file name
+ * Returns: T if valid, NIL otherwise
+ */
+
+int mtx_isvalid (char *name,char *file)
+{
+ int fd;
+ int ret = NIL;
+ char *s,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ struct utimbuf times;
+ errno = EINVAL; /* assume invalid argument */
+ /* if file, get its status */
+ if ((s = dummy_file (file,name)) && !stat (s,&sbuf) &&
+ ((sbuf.st_mode & S_IFMT) == S_IFREG)) {
+ if (!sbuf.st_size)errno = 0;/* empty file */
+ else if ((fd = open (file,O_BINARY|O_RDONLY,NIL)) >= 0) {
+ memset (tmp,'\0',MAILTMPLEN);
+ if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\015')) &&
+ (s[1] == '\012')) { /* valid format? */
+ *s = '\0'; /* tie off header */
+ /* must begin with dd-mmm-yy" */
+ ret = (((tmp[2] == '-' && tmp[6] == '-') ||
+ (tmp[1] == '-' && tmp[5] == '-')) &&
+ (s = strchr (tmp+18,',')) && strchr (s+2,';')) ? T : NIL;
+ }
+ else errno = -1; /* bogus format */
+ close (fd); /* close the file */
+ /* \Marked status? */
+ if (sbuf.st_ctime > sbuf.st_atime) {
+ /* preserve atime and mtime */
+ times.actime = sbuf.st_atime;
+ times.modtime = sbuf.st_mtime;
+ utime (file,&times); /* set the times */
+ }
+ }
+ }
+ /* in case INBOX but not mtx format */
+ else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1;
+ return ret; /* return what we should */
+}
+
+
+/* MTX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mtx_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+/* MTX mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* MTX mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mtx_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* MTX mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* MTX mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mtx_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,mbx[MAILTMPLEN];
+ if (s = dummy_file (mbx,mailbox)) return dummy_create (stream,s);
+ sprintf (mbx,"Can't create %.80s: invalid name",mailbox);
+ mm_log (mbx,ERROR);
+ return NIL;
+}
+
+
+/* MTX mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mtx_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return mtx_rename (stream,mailbox,NIL);
+}
+
+/* MTX mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long mtx_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = LONGT;
+ char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ int fd,ld;
+ struct stat sbuf;
+ if (!dummy_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'\\')) && !s[1])))) {
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ if ((fd = open (file,O_BINARY|O_RDWR,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get exclusive parse/append permission */
+ if ((ld = lockname (lock,file,LOCK_EX)) < 0) {
+ mm_log ("Unable to lock rename mailbox",ERROR);
+ return NIL;
+ }
+ /* lock out other users */
+ if (flock (fd,LOCK_EX|LOCK_NB)) {
+ close (fd); /* couldn't lock, give up on it then */
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+ mm_log (tmp,ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return NIL;
+ }
+
+ if (newname) { /* want rename? */
+ /* found superior to destination name? */
+ if ((s = strrchr (tmp,'\\')) && (s != tmp) &&
+ ((tmp[1] != ':') || (s != tmp + 2))) {
+ c = s[1]; /* remember character after delimiter */
+ *s = s[1] = '\0'; /* tie off name at delimiter */
+ /* name doesn't exist, create it */
+ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) {
+ *s = '\\'; /* restore delimiter */
+ if (!dummy_create (stream,tmp)) ret = NIL;
+ }
+ else *s = '\\'; /* restore delimiter */
+ s[1] = c; /* restore character after delimiter */
+ }
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* pacify NTFS */
+ /* rename the file */
+ if (ret && rename (file,tmp)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ else {
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* pacify NTFS */
+ if (unlink (file)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %.80s",old,strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return ret; /* return success */
+}
+
+/* MTX mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mtx_open (MAILSTREAM *stream)
+{
+ int fd,ld;
+ char tmp[MAILTMPLEN];
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &mtxproto;
+ if (stream->local) fatal ("mtx recycle stream");
+ /* canonicalize the mailbox name */
+ if (!dummy_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ mm_log (tmp,ERROR);
+ }
+ if (stream->rdonly ||
+ (fd = open (tmp,O_BINARY|O_RDWR,NIL)) < 0) {
+ if ((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox: %.80s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ else if (!stream->rdonly) { /* got it, but readonly */
+ mm_log ("Can't get write access to mailbox, access is readonly",WARN);
+ stream->rdonly = T;
+ }
+ }
+ stream->local = fs_get (sizeof (MTXLOCAL));
+ LOCAL->fd = fd; /* bind the file */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ /* get shared parse permission */
+ if ((ld = lockname (tmp,stream->mailbox,LOCK_SH)) < 0) {
+ mm_log ("Unable to lock open mailbox",ERROR);
+ return NIL;
+ }
+ flock (LOCAL->fd,LOCK_SH); /* lock the file */
+ unlockfd (ld,tmp); /* release shared parse permission */
+ LOCAL->filesize = 0; /* initialize parsed file size */
+ LOCAL->filetime = 0; /* time not set up yet */
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ stream->sequence++; /* bump sequence number */
+ stream->uid_validity = (unsigned long) time (0);
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ if (mtx_ping (stream) && !stream->nmsgs)
+ mm_log ("Mailbox is empty",(long) NIL);
+ if (!LOCAL) return NIL; /* failure if stream died */
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft =
+ stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ return stream; /* return stream to caller */
+}
+
+/* MTX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mtx_close (MAILSTREAM *stream,long options)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ if (options & CL_EXPUNGE) mtx_expunge (stream,NIL,NIL);
+ stream->silent = silent; /* restore previous status */
+ flock (LOCAL->fd,LOCK_UN); /* unlock local file */
+ close (LOCAL->fd); /* close the local file */
+ /* free local text buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+
+/* MTX mail fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ * Sniffs at file to see if some other process changed the flags
+ */
+
+void mtx_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ unsigned long i;
+ if (mtx_ping (stream) && /* ping mailbox, get new status for messages */
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if (mail_elt (stream,i)->sequence) mtx_elt (stream,i);
+}
+
+/* MTX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *mtx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags)
+{
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ /* get to header position */
+ lseek (LOCAL->fd,mtx_hdrpos (stream,msgno,length),L_SET);
+ /* is buffer big enough? */
+ if (*length > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = *length) + 1);
+ }
+ LOCAL->buf[*length] = '\0'; /* tie off string */
+ /* slurp the data */
+ read (LOCAL->fd,LOCAL->buf,*length);
+ return LOCAL->buf;
+}
+
+/* MTX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: T, always
+ */
+
+long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ FDDATA d;
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mtx_elt (stream,msgno); /* get message status */
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ elt->seen = T; /* mark message as seen */
+ /* recalculate status */
+ mtx_update_status (stream,msgno,NIL);
+ mm_flags (stream,msgno);
+ }
+ /* find header position */
+ i = mtx_hdrpos (stream,msgno,&j);
+ d.fd = LOCAL->fd; /* set up file descriptor */
+ d.pos = i + j;
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE;
+ INIT (bs,fd_string,&d,elt->rfc822_size - j);
+ return T; /* success */
+}
+
+/* MTX mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ */
+
+void mtx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ struct utimbuf times;
+ struct stat sbuf;
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* make sure read comes after all that */
+ utime (stream->mailbox,&times);
+ }
+}
+
+
+/* MTX mail per-message modify flags
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ struct stat sbuf;
+ /* maybe need to do a checkpoint? */
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ LOCAL->filetime = 0; /* don't do this test for any other messages */
+ }
+ /* recalculate status */
+ mtx_update_status (stream,elt->msgno,NIL);
+}
+
+/* MTX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream still alive, NIL if not
+ */
+
+long mtx_ping (MAILSTREAM *stream)
+{
+ unsigned long i = 1;
+ long r = T;
+ int ld;
+ char lock[MAILTMPLEN];
+ struct stat sbuf;
+ if (stream && LOCAL) { /* only if stream already open */
+ fstat (LOCAL->fd,&sbuf); /* get current file poop */
+ if (LOCAL->filetime && !(LOCAL->mustcheck || LOCAL->shouldcheck) &&
+ (LOCAL->filetime < sbuf.st_mtime)) LOCAL->shouldcheck = T;
+ /* check for changed message status */
+ if (LOCAL->mustcheck || LOCAL->shouldcheck) {
+ LOCAL->filetime = sbuf.st_mtime;
+ if (LOCAL->shouldcheck) /* babble when we do this unilaterally */
+ mm_notify (stream,"[CHECK] Checking for flag updates",NIL);
+ while (i <= stream->nmsgs) mtx_elt (stream,i++);
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ }
+ /* get shared parse/append permission */
+ if ((sbuf.st_size != LOCAL->filesize) &&
+ ((ld = lockname (lock,stream->mailbox,LOCK_SH)) >= 0)) {
+ /* parse resulting mailbox */
+ r = (mtx_parse (stream)) ? T : NIL;
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ }
+ return r; /* return result of the parse */
+}
+
+
+/* MTX mail check mailbox (reparses status too)
+ * Accepts: MAIL stream
+ */
+
+void mtx_check (MAILSTREAM *stream)
+{
+ /* mark that a check is desired */
+ if (LOCAL) LOCAL->mustcheck = T;
+ if (mtx_ping (stream)) mm_log ("Check completed",(long) NIL);
+}
+
+/* MTX mail expunge mailbox
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long mtx_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ struct utimbuf times;
+ struct stat sbuf;
+ off_t pos = 0;
+ int ld;
+ unsigned long i = 1;
+ unsigned long j,k,m,recent;
+ unsigned long n = 0;
+ unsigned long delta = 0;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ if (!(ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ mtx_ping (stream))); /* parse sequence if given, ping stream */
+ else if (stream->rdonly) mm_log ("Expunge ignored on readonly mailbox",WARN);
+ else {
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ }
+ /* get exclusive parse/append permission */
+ if ((ld = lockname (lock,stream->mailbox,LOCK_EX)) < 0)
+ mm_log ("Unable to lock expunge mailbox",ERROR);
+ /* make sure see any newly-arrived messages */
+ else if (!mtx_parse (stream));
+ /* get exclusive access */
+ else if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
+ flock (LOCAL->fd,LOCK_SH);/* recover previous lock */
+ mm_log ("Can't expunge because mailbox is in use by another process",
+ ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+
+ else {
+ mm_critical (stream); /* go critical */
+ recent = stream->recent; /* get recent now that pinged and locked */
+ /* for each message */
+ while (i <= stream->nmsgs) {
+ /* get cache element */
+ elt = mtx_elt (stream,i);
+ /* number of bytes to smash or preserve */
+ k = elt->private.special.text.size + elt->rfc822_size;
+ /* if need to expunge this message */
+ if (elt->deleted && (sequence ? elt->sequence : T)) {
+ /* if recent, note one less recent message */
+ if (elt->recent) --recent;
+ delta += k; /* number of bytes to delete */
+ /* notify upper levels */
+ mail_expunged (stream,i);
+ n++; /* count up one more expunged message */
+ }
+ else if (i++ && delta) {/* preserved message */
+ /* first byte to preserve */
+ j = elt->private.special.offset;
+ do { /* read from source position */
+ m = min (k,LOCAL->buflen);
+ lseek (LOCAL->fd,j,L_SET);
+ read (LOCAL->fd,LOCAL->buf,m);
+ pos = j - delta; /* write to destination position */
+ while (T) {
+ lseek (LOCAL->fd,pos,L_SET);
+ if (write (LOCAL->fd,LOCAL->buf,m) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+ pos += m; /* new position */
+ j += m; /* next chunk, perhaps */
+ } while (k -= m); /* until done */
+ /* note the new address of this text */
+ elt->private.special.offset -= delta;
+ }
+ /* preserved but no deleted messages */
+ else pos = elt->private.special.offset + k;
+ }
+ if (n) { /* truncate file after last message */
+ if (pos != (LOCAL->filesize -= delta)) {
+ sprintf (LOCAL->buf,
+ "Calculated size mismatch %lu != %lu, delta = %lu",
+ (unsigned long) pos,(unsigned long) LOCAL->filesize,delta);
+ mm_log (LOCAL->buf,WARN);
+ LOCAL->filesize = pos;/* fix it then */
+ }
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ sprintf (LOCAL->buf,"Expunged %lu messages",n);
+ /* output the news */
+ mm_log (LOCAL->buf,(long) NIL);
+ }
+ else mm_log ("No messages deleted, so no update needed",(long) NIL);
+ fsync (LOCAL->fd); /* force disk update */
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* reset atime to now */
+ utime (stream->mailbox,&times);
+ mm_nocritical (stream); /* release critical */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ flock (LOCAL->fd,LOCK_SH);/* allow sharers again */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+ }
+ return ret;
+}
+
+/* MTX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if success, NIL if failed
+ */
+
+long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ struct utimbuf times;
+ MESSAGECACHE *elt;
+ unsigned long i,j,k;
+ long ret = LONGT;
+ int fd,ld;
+ char file[MAILTMPLEN],lock[MAILTMPLEN];
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ /* make sure valid mailbox */
+ if (!mtx_isvalid (mailbox,file)) switch (errno) {
+ case ENOENT: /* no such file? */
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid MTX-format mailbox name: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a MTX-format mailbox: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* got file? */
+ if ((fd = open (file,O_BINARY|O_RDWR|O_CREAT,S_IREAD|S_IWRITE)) < 0) {
+ sprintf (LOCAL->buf,"Unable to open copy mailbox: %.80s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ mm_critical (stream); /* go critical */
+ /* get exclusive parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockname (lock,file,LOCK_EX)) < 0)) {
+ mm_log ("Unable to lock copy mailbox",ERROR);
+ mm_nocritical (stream);
+ return NIL;
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
+
+ /* for each requested message */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ /* number of bytes to copy */
+ k = elt->private.special.text.size + elt->rfc822_size;
+ do { /* read from source position */
+ j = min (k,LOCAL->buflen);
+ read (LOCAL->fd,LOCAL->buf,j);
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ } while (ret && (k -= j));/* until done */
+ }
+ /* make sure all the updates take */
+ if (!(ret && (ret = !fsync (fd)))) {
+ sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ }
+ /* set atime to now-1 if successful copy */
+ if (ret) times.actime = time (0) - 1;
+ /* else preserved \Marked status */
+ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ?
+ sbuf.st_atime : time (0);
+ times.modtime = sbuf.st_mtime;/* preserve mtime */
+ utime (file,&times); /* set the times */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ close (fd); /* close the file */
+ mm_nocritical (stream); /* release critical */
+ /* delete all requested messages */
+ if (ret && (options & CP_MOVE)) {
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mtx_elt (stream,i))->sequence) {
+ elt->deleted = T; /* mark message deleted */
+ /* recalculate status */
+ mtx_update_status (stream,i,NIL);
+ }
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* make sure atime remains greater */
+ utime (stream->mailbox,&times);
+ }
+ }
+ if (ret && mail_parameters (NIL,GET_COPYUID,NIL))
+ mm_log ("Can not return meaningful COPYUID with this mailbox format",WARN);
+ return ret;
+}
+
+/* MTX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd,ld,c;
+ char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ struct utimbuf times;
+ FILE *df;
+ MESSAGECACHE elt;
+ long f;
+ unsigned long i,uf;
+ STRING *message;
+ long ret = LONGT;
+ /* default stream to prototype */
+ if (!stream) stream = &mtxproto;
+ /* make sure valid mailbox */
+ if (!mtx_isvalid (mailbox,file)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (!compare_cstring (mailbox,"INBOX")) mtx_create (NIL,"INBOX");
+ else {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* falls through */
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid MTX-format mailbox name: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a MTX-format mailbox: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get first message */
+ if (!(*af) (stream,data,&flags,&date,&message)) return NIL;
+
+ /* open destination mailbox */
+ if (((fd = open (file,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE))
+ < 0) || !(df = fdopen (fd,"ab"))) {
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockname (lock,file,LOCK_EX)) < 0)) {
+ mm_log ("Unable to lock append mailbox",ERROR);
+ close (fd);
+ return NIL;
+ }
+ mm_critical (stream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ errno = 0;
+ do { /* parse flags */
+ if (!SIZE (message)) { /* guard against zero-length */
+ mm_log ("Append of zero-length message",ERROR);
+ ret = NIL;
+ break;
+ }
+ f = mail_parse_flags (stream,flags,&i);
+ /* reverse bits (dontcha wish we had CIRC?) */
+ for (uf = 0; i; uf |= 1 << (29 - find_rightmost_bit (&i)));
+ if (date) { /* parse date if given */
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ mm_log (tmp,ERROR);
+ ret = NIL; /* mark failure */
+ break;
+ }
+ mail_date (tmp,&elt); /* write preseved date */
+ }
+ else internal_date (tmp); /* get current date in IMAP format */
+ /* write header */
+ if (fprintf (df,"%s,%lu;%010lo%02lo\015\012",tmp,i = SIZE (message),uf,
+ (unsigned long) f) < 0) ret = NIL;
+ else { /* write message */
+ if (i) do c = 0xff & SNX (message);
+ while ((putc (c,df) != EOF) && --i);
+ /* get next message */
+ if (i || !(*af) (stream,data,&flags,&date,&message)) ret = NIL;
+ }
+ } while (ret && message);
+ /* if error... */
+ if (!ret || (fflush (df) == EOF)) {
+ ftruncate (fd,sbuf.st_size);/* revert file */
+ close (fd); /* make sure fclose() doesn't corrupt us */
+ if (errno) {
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ }
+ ret = NIL;
+ }
+ if (ret) times.actime = time (0) - 1;
+ /* else preserved \Marked status */
+ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ?
+ sbuf.st_atime : time (0);
+ times.modtime = sbuf.st_mtime;/* preserve mtime */
+ utime (file,&times); /* set the times */
+ fclose (df); /* close the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ mm_nocritical (stream); /* release critical */
+ if (ret && mail_parameters (NIL,GET_APPENDUID,NIL))
+ mm_log ("Can not return meaningful APPENDUID with this mailbox format",
+ WARN);
+ return ret;
+}
+
+/* Internal routines */
+
+
+/* MTX mail parse mailbox
+ * Accepts: MAIL stream
+ * Returns: T if parse OK
+ * NIL if failure, stream aborted
+ */
+
+long mtx_parse (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = NIL;
+ unsigned char c,*s,*t,*x;
+ char tmp[MAILTMPLEN];
+ unsigned long i,j;
+ long curpos = LOCAL->filesize;
+ long nmsgs = stream->nmsgs;
+ long recent = stream->recent;
+ short added = NIL;
+ short silent = stream->silent;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ if (sbuf.st_size < curpos) { /* sanity check */
+ sprintf (tmp,"Mailbox shrank from %ld to %ld!",curpos,sbuf.st_size);
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ stream->silent = T; /* don't pass up mm_exists() events yet */
+ while (sbuf.st_size - curpos){/* while there is stuff to parse */
+ /* get to that position in the file */
+ lseek (LOCAL->fd,curpos,L_SET);
+ if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
+ sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size,
+ i ? strerror (errno) : "no data read");
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ LOCAL->buf[i] = '\0'; /* tie off buffer just in case */
+ if (!((s = strchr (LOCAL->buf,'\015')) && (s[1] == '\012'))) {
+ sprintf (tmp,"Unable to find CRLF at %lu in %lu bytes, text: %s",
+ (unsigned long) curpos,i,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ *s = '\0'; /* tie off header line */
+ i = (s + 2) - LOCAL->buf; /* note start of text offset */
+ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
+ sprintf (tmp,"Unable to parse internal header at %lu: %s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ *s++ = '\0'; *t++ = '\0'; /* tie off fields */
+
+ added = T; /* note that a new message was added */
+ /* swell the cache */
+ mail_exists (stream,++nmsgs);
+ /* instantiate an elt for this message */
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ elt->private.uid = ++stream->uid_last;
+ /* note file offset of header */
+ elt->private.special.offset = curpos;
+ /* in case error */
+ elt->private.special.text.size = 0;
+ /* header size not known yet */
+ elt->private.msg.header.text.size = 0;
+ x = s; /* parse the header components */
+ if (mail_parse_date (elt,LOCAL->buf) &&
+ (elt->rfc822_size = strtoul (s,(char **) &s,10)) && (!(s && *s)) &&
+ isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) &&
+ isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) &&
+ isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) &&
+ isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12])
+ elt->private.special.text.size = i;
+ else { /* oops */
+ sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s",
+ curpos,(char *) LOCAL->buf,(char *) x,(char *) t);
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ /* make sure didn't run off end of file */
+ if ((curpos += (elt->rfc822_size + i)) > sbuf.st_size) {
+ sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)",
+ elt->private.special.offset,(unsigned long) curpos,
+ (unsigned long) sbuf.st_size);
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ c = t[10]; /* remember first system flags byte */
+ t[10] = '\0'; /* tie off flags */
+ j = strtoul (t,NIL,8); /* get user flags value */
+ t[10] = c; /* restore first system flags byte */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ /* calculate system flags */
+ if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T;
+ if (j & fDELETED) elt->deleted = T;
+ if (j & fFLAGGED) elt->flagged = T;
+ if (j & fANSWERED) elt->answered = T;
+ if (j & fDRAFT) elt->draft = T;
+ if (!(j & fOLD)) { /* newly arrived message? */
+ elt->recent = T;
+ recent++; /* count up a new recent message */
+ /* mark it as old */
+ mtx_update_status (stream,nmsgs,NIL);
+ }
+ }
+ fsync (LOCAL->fd); /* make sure all the fOLD flags take */
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */
+ LOCAL->filetime = sbuf.st_mtime;
+ if (added && !stream->rdonly){/* make sure atime updated */
+ struct utimbuf times;
+ times.actime = time (0);
+ times.modtime = LOCAL->filetime;
+ utime (stream->mailbox,&times);
+ }
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
+ mail_recent (stream,recent); /* and of change in recent messages */
+ return LONGT; /* return the winnage */
+}
+
+/* MTX get cache element with status updating from file
+ * Accepts: MAIL stream
+ * message number
+ * Returns: cache element
+ */
+
+MESSAGECACHE *mtx_elt (MAILSTREAM *stream,unsigned long msgno)
+{
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ struct { /* old flags */
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged;
+ old.answered = elt->answered; old.draft = elt->draft;
+ old.user_flags = elt->user_flags;
+ mtx_read_flags (stream,elt);
+ if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
+ (old.flagged != elt->flagged) || (old.answered != elt->answered) ||
+ (old.draft != elt->draft) || (old.user_flags != elt->user_flags))
+ mm_flags (stream,msgno); /* let top level know */
+ return elt;
+}
+
+/* MTX read flags from file
+ * Accepts: MAIL stream
+ * Returns: cache element
+ */
+
+void mtx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ unsigned long i,j;
+ /* noop if readonly and have valid flags */
+ if (stream->rdonly && elt->valid) return;
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 14,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,12) < 0) {
+ sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ /* calculate system flags */
+ i = (((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0');
+ elt->seen = i & fSEEN ? T : NIL; elt->deleted = i & fDELETED ? T : NIL;
+ elt->flagged = i & fFLAGGED ? T : NIL;
+ elt->answered = i & fANSWERED ? T : NIL; elt->draft = i & fDRAFT ? T : NIL;
+ LOCAL->buf[10] = '\0'; /* tie off flags */
+ j = strtoul(LOCAL->buf,NIL,8);/* get user flags value */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ elt->valid = T; /* have valid flags now */
+}
+
+/* MTX update status string
+ * Accepts: MAIL stream
+ * message number
+ * flag saying whether or not to sync
+ */
+
+void mtx_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag)
+{
+ struct utimbuf times;
+ struct stat sbuf;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ unsigned long j,k = 0;
+ /* readonly */
+ if (stream->rdonly || !elt->valid) mtx_read_flags (stream,elt);
+ else { /* readwrite */
+ j = elt->user_flags; /* get user flags */
+ /* reverse bits (dontcha wish we had CIRC?) */
+ while (j) k |= 1 << (29 - find_rightmost_bit (&j));
+ /* print new flag string */
+ sprintf (LOCAL->buf,"%010lo%02o",k,(unsigned)
+ (fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)));
+ while (T) { /* get to that place in the file */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 14,L_SET);
+ /* write new flags */
+ if (write (LOCAL->fd,LOCAL->buf,12) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+ if (syncflag) { /* sync if requested */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* make sure read is later */
+ utime (stream->mailbox,&times);
+ }
+ }
+}
+
+/* MTX locate header for a message
+ * Accepts: MAIL stream
+ * message number
+ * pointer to returned header size
+ * Returns: position of header in file
+ */
+
+unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size)
+{
+ unsigned long siz;
+ long i = 0;
+ int q = 0;
+ char *s,tmp[MAILTMPLEN];
+ MESSAGECACHE *elt = mtx_elt (stream,msgno);
+ unsigned long ret = elt->private.special.offset +
+ elt->private.special.text.size;
+ /* is header size known? */
+ if (!(*size = elt->private.msg.header.text.size)) {
+ lseek (LOCAL->fd,ret,L_SET);/* get to header position */
+ /* search message for CRLF CRLF */
+ for (siz = 1,s = tmp; siz <= elt->rfc822_size; siz++) {
+ /* read another buffer as necessary */
+ if ((--i <= 0) && /* buffer empty? */
+ (read (LOCAL->fd,s = tmp,
+ i = min (elt->rfc822_size - siz,(long) MAILTMPLEN)) < 0))
+ return ret; /* I/O error? */
+ switch (q) { /* sniff at buffer */
+ case 0: /* first character */
+ q = (*s++ == '\015') ? 1 : 0;
+ break;
+ case 1: /* second character */
+ q = (*s++ == '\012') ? 2 : 0;
+ break;
+ case 2: /* third character */
+ q = (*s++ == '\015') ? 3 : 0;
+ break;
+ case 3: /* fourth character */
+ if (*s++ == '\012') { /* have the sequence? */
+ /* yes, note for later */
+ elt->private.msg.header.text.size = *size = siz;
+ return ret;
+ }
+ q = 0; /* lost... */
+ break;
+ }
+ }
+ /* header consumes entire message */
+ elt->private.msg.header.text.size = *size = elt->rfc822_size;
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/os2/nl_os2.c b/imap/src/osdep/os2/nl_os2.c
new file mode 100644
index 00000000..47cb7f0a
--- /dev/null
+++ b/imap/src/osdep/os2/nl_os2.c
@@ -0,0 +1,61 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Windows/TOPS-20 newline routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Copy string with CRLF newlines
+ * Accepts: destination string
+ * pointer to size of destination string buffer
+ * source string
+ * length of source string
+ * Returns: length of copied string
+ */
+
+unsigned long strcrlfcpy (unsigned char **dst,unsigned long *dstl,
+ unsigned char *src,unsigned long srcl)
+{
+ /* flush destination buffer if too small */
+ if (*dst && (srcl > *dstl)) fs_give ((void **) dst);
+ if (!*dst) { /* make a new buffer if needed */
+ *dst = (char *) fs_get ((size_t) (*dstl = srcl) + 1);
+ if (dstl) *dstl = srcl; /* return new buffer length to main program */
+ }
+ /* copy strings */
+ if (srcl) memcpy (*dst,src,(size_t) srcl);
+ *(*dst + srcl) = '\0'; /* tie off destination */
+ return srcl; /* return length */
+}
+
+
+/* Length of string after strcrlfcpy applied
+ * Accepts: source string
+ * Returns: length of string
+ */
+
+unsigned long strcrlflen (STRING *s)
+{
+ return SIZE (s); /* no-brainer on DOS! */
+}
diff --git a/imap/src/osdep/os2/os_os2.c b/imap/src/osdep/os2/os_os2.c
new file mode 100644
index 00000000..9c6f2f5f
--- /dev/null
+++ b/imap/src/osdep/os2/os_os2.c
@@ -0,0 +1,100 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- OS/2 emx version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 14 March 1996
+ * Last Edited: 30 August 2006
+ */
+
+#include <stdio.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "tcp_os2.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include "misc.h"
+#include "fs_os2.c"
+#include "ftl_os2.c"
+#include "nl_os2.c"
+#include "env_os2.c"
+#include "tcp_os2.c"
+
+/* Return my local host name
+ * Returns: my local host name
+ */
+
+char *mylocalhost (void)
+{
+ if (!myLocalHost) { /* known yet? */
+ char *s,tmp[MAILTMPLEN];
+ struct hostent *he;
+ /* could we get local id? */
+ gethostname (tmp,MAILTMPLEN-1);
+ if (he = gethostbyname (lcase (tmp))) {
+ if (he->h_name) s = he->h_name;
+ else sprintf (s = tmp,"[%i.%i.%i.%i]",he->h_addr[0],he->h_addr[1],
+ he->h_addr[2],he->h_addr[3]);
+ }
+ else s = "random-pc";
+ myLocalHost = cpystr (s); /* record for subsequent use */
+ }
+ return myLocalHost;
+}
+
+
+/* Look up host address
+ * Accepts: pointer to pointer to host name
+ * socket address block
+ * Returns: non-zero with host address in socket, official host name in host;
+ * else NIL
+ */
+
+long lookuphost (char **host,struct sockaddr_in *sin)
+{
+ long ret = -1;
+ char tmp[MAILTMPLEN];
+ struct hostent *hn = gethostbyname (lcase (strcpy (tmp,*host)));
+ if (!hn) return NIL; /* got a host name? */
+ *host = cpystr (hn->h_name); /* set official name */
+ /* copy host addresses */
+ memcpy (&sin->sin_addr,hn->h_addr,hn->h_length);
+ return T;
+}
+
+
+/*
+ * Emulator for BSD syslog() routine.
+ */
+
+void syslog (int priority,const char *message,...)
+{
+}
diff --git a/imap/src/osdep/os2/os_os2.h b/imap/src/osdep/os2/os_os2.h
new file mode 100644
index 00000000..0dd0a62b
--- /dev/null
+++ b/imap/src/osdep/os2/os_os2.h
@@ -0,0 +1,38 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- OS/2 emx version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 14 March 1996
+ * Last Edited: 30 August 2006
+ */
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include "env_os2.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/os2/pmatch.c b/imap/src/osdep/os2/pmatch.c
new file mode 100644
index 00000000..95a0bb86
--- /dev/null
+++ b/imap/src/osdep/os2/pmatch.c
@@ -0,0 +1,89 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: IMAP Wildcard Matching Routines (case-independent)
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 June 2000
+ * Last Edited: 30 August 2006
+ */
+
+/* Wildcard pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if pattern matches base, else NIL
+ */
+
+long pmatch_full (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ /* % at end, OK if no inferiors */
+ if (!pat[1]) return (delim && strchr (s,delim)) ? NIL : T;
+ /* scan remainder of string until delimiter */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while ((*s != delim) && *s++);
+ break;
+ case '*': /* match 0 or more characters */
+ if (!pat[1]) return T; /* * at end, unconditional match */
+ /* scan remainder of string */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while (*s++);
+ break;
+ case '\0': /* end of pattern */
+ return *s ? NIL : T; /* success if also end of base */
+ default: /* match this character */
+ return compare_uchar (*pat,*s) ? NIL : pmatch_full (s+1,pat+1,delim);
+ }
+ return NIL;
+}
+
+/* Directory pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if base is a matching directory of pattern, else NIL
+ */
+
+long dmatch (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ if (!*s) return T; /* end of base means have a subset match */
+ if (!*++pat) return NIL; /* % at end, no inferiors permitted */
+ /* scan remainder of string until delimiter */
+ do if (dmatch (s,pat,delim)) return T;
+ while ((*s != delim) && *s++);
+ if (*s && !s[1]) return T; /* ends with delimiter, must be subset */
+ return dmatch (s,pat,delim);/* do new scan */
+ case '*': /* match 0 or more characters */
+ return T; /* unconditional match */
+ case '\0': /* end of pattern */
+ break;
+ default: /* match this character */
+ if (*s) return compare_uchar (*pat,*s) ? NIL : dmatch (s+1,pat+1,delim);
+ /* end of base, return if at delimiter */
+ else if (*pat == delim) return T;
+ break;
+ }
+ return NIL;
+}
diff --git a/imap/src/osdep/os2/pseudo.c b/imap/src/osdep/os2/pseudo.c
new file mode 100644
index 00000000..1aae8a53
--- /dev/null
+++ b/imap/src/osdep/os2/pseudo.c
@@ -0,0 +1,36 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Pseudo Header Strings
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 September 1996
+ * Last Edited: 30 August 2006
+ */
+
+/* Local sites may wish to alter this text */
+
+char *pseudo_from = "MAILER-DAEMON";
+char *pseudo_name = "Mail System Internal Data";
+char *pseudo_subject = "DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA";
+char *pseudo_msg =
+ "This text is part of the internal format of your mail folder, and is not\na real message. It is created automatically by the mail system software.\nIf deleted, important folder data will be lost, and it will be re-created\nwith the data reset to initial values."
+ ;
diff --git a/imap/src/osdep/os2/pseudo.h b/imap/src/osdep/os2/pseudo.h
new file mode 100644
index 00000000..c9c07628
--- /dev/null
+++ b/imap/src/osdep/os2/pseudo.h
@@ -0,0 +1,30 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Pseudo Header Strings
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 September 1996
+ * Last Edited: 30 August 2006
+ */
+
+
+extern char *pseudo_from,*pseudo_name,*pseudo_subject,*pseudo_msg;
diff --git a/imap/src/osdep/os2/setproto.cmd b/imap/src/osdep/os2/setproto.cmd
new file mode 100644
index 00000000..5b9d6eef
--- /dev/null
+++ b/imap/src/osdep/os2/setproto.cmd
@@ -0,0 +1,34 @@
+/* rexx */
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+/*
+ * Program: Set default prototype for OS/2
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 June 1999
+ * Last Edited: 30 August 2006
+ */
+'@echo off'
+parse arg default args
+h_file='linkage.h'
+call stream h_file, 'C', 'open write'
+call lineout h_file, '#define DEFAULTPROTO' default'proto'
+call stream h_file, 'C', 'close'
+exit 0
diff --git a/imap/src/osdep/os2/tcp_os2.c b/imap/src/osdep/os2/tcp_os2.c
new file mode 100644
index 00000000..062f260c
--- /dev/null
+++ b/imap/src/osdep/os2/tcp_os2.c
@@ -0,0 +1,434 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MS-DOS TCP/IP routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 13 January 2008
+ */
+
+static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */
+static long ttmo_read = 0; /* TCP timeouts, in seconds */
+static long ttmo_write = 0;
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd);
+
+/* TCP/IP manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tcp_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_TIMEOUT:
+ tmoh = (tcptimeout_t) value;
+ case GET_TIMEOUT:
+ ret = (void *) tmoh;
+ break;
+ case SET_READTIMEOUT:
+ ttmo_read = (long) value;
+ case GET_READTIMEOUT:
+ ret = (void *) ttmo_read;
+ break;
+ case SET_WRITETIMEOUT:
+ ttmo_write = (long) value;
+ case GET_WRITETIMEOUT:
+ ret = (void *) ttmo_write;
+ break;
+ }
+ return ret;
+}
+
+/* TCP/IP open
+ * Accepts: host name
+ * contact service name
+ * contact port number
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream = NIL;
+ struct sockaddr_in sin;
+ int sock;
+ char *s,tmp[MAILTMPLEN];
+ port &= 0xffff; /* erase flags */
+ /* The domain literal form is used (rather than simply the dotted decimal
+ as with other Unix programs) because it has to be a valid "host name"
+ in mailsystem terminology. */
+ sin.sin_family = AF_INET; /* family is always Internet */
+ /* look like domain literal? */
+ if (host[0] == '[' && host[(strlen (host))-1] == ']') {
+ strcpy (tmp,host+1); /* yes, copy number part */
+ tmp[strlen (tmp)-1] = '\0';
+ if ((sin.sin_addr.s_addr = inet_addr (tmp)) == -1) {
+ sprintf (tmp,"Bad format domain-literal: %.80s",host);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ }
+ /* look up host name */
+ else if (!lookuphost (&host,&sin)) {
+ sprintf (tmp,"Host not found: %s",host);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+
+ /* copy port number in network format */
+ if (!(sin.sin_port = htons (port))) fatal ("Bad port argument to tcp_open");
+ /* get a TCP stream */
+ if ((sock = socket (sin.sin_family,SOCK_STREAM,0)) < 0) {
+ sprintf (tmp,"Unable to create TCP socket (%d)",errno);
+ mm_log (tmp,ERROR);
+ fs_give ((void **) &host);
+ return NIL;
+ }
+#if 0
+ /* needed? */
+ else if (sock >= FD_SETSIZE) {/* unselectable sockets are useless */
+ sprintf (tmp,"Unable to create selectable TCP socket (%d >= %d)",
+ sock,FD_SETSIZE);
+ close (sock);
+ errno = ENOBUFS; /* just in case */
+ return NIL;
+ }
+#endif
+ /* open connection */
+ if (connect (sock,(struct sockaddr *) &sin,sizeof (sin)) < 0) {
+ switch (errno) { /* analyze error */
+ case ECONNREFUSED:
+ s = "Refused";
+ break;
+ case ENOBUFS:
+ s = "Insufficient system resources";
+ break;
+ case ETIMEDOUT:
+ s = "Timed out";
+ break;
+ default:
+ s = "Unknown error";
+ break;
+ }
+ sprintf (tmp,"Can't connect to %.80s,%ld: %s (%d)",host,port,s,errno);
+ mm_log (tmp,ERROR);
+ close (sock);
+ fs_give ((void **) &host);
+ return NIL;
+ }
+ /* create TCP/IP stream */
+ stream = (TCPSTREAM *) fs_get (sizeof (TCPSTREAM));
+ stream->host = host; /* official host name */
+ stream->localhost = cpystr (mylocalhost ());
+ stream->port = port; /* port number */
+ stream->tcps = sock; /* init socket */
+ stream->ictr = 0; /* init input counter */
+ return stream; /* return success */
+}
+
+/* TCP/IP authenticated open
+ * Accepts: NETMBX specifier
+ * service name
+ * returned user name buffer
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL; /* always NIL on DOS */
+}
+
+/* TCP receive line
+ * Accepts: TCP stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *tcp_getline (TCPSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = tcp_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = tcp_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* TCP receive line or partial line
+ * Accepts: TCP stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!tcp_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!tcp_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* TCP/IP receive buffer
+ * Accepts: TCP/IP stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer)
+{
+ unsigned long n;
+ char *bufptr = buffer;
+ while (size > 0) { /* until request satisfied */
+ if (!tcp_getdata (stream)) return NIL;
+ n = min (size,stream->ictr);/* number of bytes to transfer */
+ /* do the copy */
+ memcpy (bufptr,stream->iptr,n);
+ bufptr += n; /* update pointer */
+ stream->iptr +=n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -=n;
+ }
+ bufptr[0] = '\0'; /* tie off string */
+ return T;
+}
+
+/* TCP/IP receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getdata (TCPSTREAM *stream)
+{
+ int i;
+ fd_set fds,efds;
+ struct timeval tmo;
+ time_t t = time (0);
+ if (stream->tcps < 0) return NIL;
+ while (stream->ictr < 1) { /* if nothing in the buffer */
+ time_t tl = time (0); /* start of request */
+ tmo.tv_sec = ttmo_read; /* read timeout */
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ FD_SET (stream->tcps,&fds);/* set bit in selection vector */
+ FD_SET(stream->tcps,&efds);/* set bit in error selection vector */
+ errno = NIL; /* block and read */
+ while (((i = select (stream->tcps+1,&fds,0,&efds,ttmo_read ? &tmo : 0))<0)
+ && (errno == EINTR));
+ if (!i) { /* timeout? */
+ time_t tc = time (0);
+ if (tmoh && ((*tmoh) (tc - t,tc - tl))) continue;
+ else return tcp_abort (stream);
+ }
+ else if (i < 0) return tcp_abort (stream);
+ while (((i = read (stream->tcps,stream->ibuf,BUFLEN)) < 0) &&
+ (errno == EINTR));
+ if (i < 1) return tcp_abort (stream);
+ stream->iptr = stream->ibuf;/* point at TCP buffer */
+ stream->ictr = i; /* set new byte count */
+ }
+ return T;
+}
+
+/* TCP/IP send string as record
+ * Accepts: TCP/IP stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long tcp_soutr (TCPSTREAM *stream,char *string)
+{
+ return tcp_sout (stream,string,(unsigned long) strlen (string));
+}
+
+
+/* TCP/IP send string
+ * Accepts: TCP/IP stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
+{
+ int i;
+ fd_set fds;
+ struct timeval tmo;
+ time_t t = time (0);
+ if (stream->tcps < 0) return NIL;
+ while (size > 0) { /* until request satisfied */
+ time_t tl = time (0); /* start of request */
+ tmo.tv_sec = ttmo_write; /* write timeout */
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_SET (stream->tcps,&fds);/* set bit in selection vector */
+ errno = NIL; /* block and write */
+ while (((i = select (stream->tcps+1,0,&fds,0,ttmo_write ? &tmo : 0)) < 0)
+ && (errno == EINTR));
+ if (!i) { /* timeout? */
+ time_t tc = time (0);
+ if (tmoh && ((*tmoh) (tc - t,tc - tl))) continue;
+ else return tcp_abort (stream);
+ }
+ else if (i < 0) return tcp_abort (stream);
+ while (((i = write (stream->tcps,string,size)) < 0) && (errno == EINTR));
+ if (i < 0) return tcp_abort (stream);
+ size -= i; /* how much we sent */
+ string += i;
+ }
+ return T; /* all done */
+}
+
+/* TCP/IP close
+ * Accepts: TCP/IP stream
+ */
+
+void tcp_close (TCPSTREAM *stream)
+{
+ tcp_abort (stream); /* nuke the socket */
+ /* flush host names */
+ fs_give ((void **) &stream->host);
+ fs_give ((void **) &stream->localhost);
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+
+/* TCP/IP abort stream
+ * Accepts: TCP/IP stream
+ * Returns: NIL always
+ */
+
+long tcp_abort (TCPSTREAM *stream)
+{
+ if (stream->tcps >= 0) close (stream->tcps);
+ stream->tcps = -1;
+ return NIL;
+}
+
+/* TCP/IP get host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_host (TCPSTREAM *stream)
+{
+ return stream->host; /* return host name */
+}
+
+
+/* TCP/IP get remote host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_remotehost (TCPSTREAM *stream)
+{
+ return stream->host; /* all we can do for now */
+}
+
+
+/* TCP/IP return port for this stream
+ * Accepts: TCP/IP stream
+ * Returns: port number for this stream
+ */
+
+unsigned long tcp_port (TCPSTREAM *stream)
+{
+ return stream->port; /* return port number */
+}
+
+
+/* TCP/IP get local host name
+ * Accepts: TCP/IP stream
+ * Returns: local host name
+ */
+
+char *tcp_localhost (TCPSTREAM *stream)
+{
+ return stream->localhost; /* return local host name */
+}
+
+
+/* TCP/IP return canonical form of host name
+ * Accepts: host name
+ * Returns: canonical form of host name
+ */
+
+char *tcp_canonical (char *name)
+{
+ return name;
+}
+
+
+/* TCP/IP get client host name (server calls only)
+ * Returns: client host name
+ */
+
+char *tcp_clienthost ()
+{
+ return "UNKNOWN";
+}
diff --git a/imap/src/osdep/os2/tcp_os2.h b/imap/src/osdep/os2/tcp_os2.h
new file mode 100644
index 00000000..2933a5ff
--- /dev/null
+++ b/imap/src/osdep/os2/tcp_os2.h
@@ -0,0 +1,51 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: OS2 routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+/* TCP input buffer -- must be large enough to prevent overflow */
+
+#define BUFLEN 8192
+
+
+/* TCP I/O stream (must be before osdep.h is included) */
+
+#define TCPSTREAM struct tcp_stream
+TCPSTREAM {
+ char *host; /* host name */
+ unsigned long port; /* port number */
+ char *localhost; /* local host name */
+ int tcps; /* tcp socket */
+ long ictr; /* input counter */
+ char *iptr; /* input pointer */
+ char ibuf[BUFLEN]; /* input buffer */
+};
+
+
+/* Local function prototypes */
+
+long lookuphost (char **host,struct sockaddr_in *sin);
+long tcp_abort (TCPSTREAM *stream);
diff --git a/imap/src/osdep/os2/tenexnt.c b/imap/src/osdep/os2/tenexnt.c
new file mode 100644
index 00000000..4805b7c5
--- /dev/null
+++ b/imap/src/osdep/os2/tenexnt.c
@@ -0,0 +1,1310 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Tenex mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 22 May 1990
+ * Last Edited: 18 June 2007
+ */
+
+
+/* FILE TIME SEMANTICS
+ *
+ * The atime is the last read time of the file.
+ * The mtime is the last flags update time of the file.
+ * The ctime is the last write time of the file.
+ *
+ * TEXT SIZE SEMANTICS
+ *
+ * Most of the text sizes are in internal (LF-only) form, except for the
+ * msg.text size. Beware.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/utime.h>
+#include "misc.h"
+#include "dummy.h"
+
+/* TENEX I/O stream local data */
+
+typedef struct tenex_local {
+ unsigned int shouldcheck: 1; /* if ping should do a check instead */
+ unsigned int mustcheck: 1; /* if ping must do a check instead */
+ int fd; /* file descriptor for I/O */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ time_t lastsnarf; /* local snarf time */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ unsigned long uid; /* current text uid */
+ SIZEDTEXT text; /* current text */
+} TENEXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((TENEXLOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *tenex_valid (char *name);
+int tenex_isvalid (char *name,char *file);
+void *tenex_parameters (long function,void *value);
+void tenex_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void tenex_list (MAILSTREAM *stream,char *ref,char *pat);
+void tenex_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long tenex_create (MAILSTREAM *stream,char *mailbox);
+long tenex_delete (MAILSTREAM *stream,char *mailbox);
+long tenex_rename (MAILSTREAM *stream,char *old,char *newname);
+long tenex_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *tenex_open (MAILSTREAM *stream);
+void tenex_close (MAILSTREAM *stream,long options);
+void tenex_fast (MAILSTREAM *stream,char *sequence,long flags);
+void tenex_flags (MAILSTREAM *stream,char *sequence,long flags);
+char *tenex_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long tenex_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void tenex_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+void tenex_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long tenex_ping (MAILSTREAM *stream);
+void tenex_check (MAILSTREAM *stream);
+void tenex_snarf (MAILSTREAM *stream);
+long tenex_expunge (MAILSTREAM *stream,char *sequence,long options);
+long tenex_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long tenex_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+unsigned long tenex_size (MAILSTREAM *stream,unsigned long m);
+long tenex_parse (MAILSTREAM *stream);
+MESSAGECACHE *tenex_elt (MAILSTREAM *stream,unsigned long msgno);
+void tenex_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt);
+void tenex_update_status (MAILSTREAM *stream,unsigned long msgno,
+ long syncflag);
+unsigned long tenex_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size);
+
+
+/* Tenex mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER tenexdriver = {
+ "tenex", /* driver name */
+ DR_LOCAL|DR_MAIL, /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ tenex_valid, /* mailbox is valid for us */
+ tenex_parameters, /* manipulate parameters */
+ tenex_scan, /* scan mailboxes */
+ tenex_list, /* list mailboxes */
+ tenex_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ tenex_create, /* create mailbox */
+ tenex_delete, /* delete mailbox */
+ tenex_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ tenex_open, /* open mailbox */
+ tenex_close, /* close mailbox */
+ tenex_flags, /* fetch message "fast" attributes */
+ tenex_flags, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ tenex_header, /* fetch message header */
+ tenex_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ tenex_flag, /* modify flags */
+ tenex_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ tenex_ping, /* ping mailbox to see if still alive */
+ tenex_check, /* check for new messages */
+ tenex_expunge, /* expunge deleted messages */
+ tenex_copy, /* copy messages to another mailbox */
+ tenex_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM tenexproto = {&tenexdriver};
+
+/* Tenex mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *tenex_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return tenex_isvalid (name,tmp) ? &tenexdriver : NIL;
+}
+
+
+/* Tenex mail test for valid mailbox
+ * Accepts: mailbox name
+ * buffer to return file name
+ * Returns: T if valid, NIL otherwise
+ */
+
+int tenex_isvalid (char *name,char *file)
+{
+ int fd;
+ int ret = NIL;
+ char *s,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ struct utimbuf times;
+ errno = EINVAL; /* assume invalid argument */
+ /* if file, get its status */
+ if ((s = dummy_file (file,name)) && !stat (s,&sbuf) &&
+ ((sbuf.st_mode & S_IFMT) == S_IFREG)) {
+ if (!sbuf.st_size)errno = 0;/* empty file */
+ else if ((fd = open (file,O_BINARY|O_RDONLY,NIL)) >= 0) {
+ memset (tmp,'\0',MAILTMPLEN);
+ if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\012')) &&
+ (s[-1] != '\015')) { /* valid format? */
+ *s = '\0'; /* tie off header */
+ /* must begin with dd-mmm-yy" */
+ ret = (((tmp[2] == '-' && tmp[6] == '-') ||
+ (tmp[1] == '-' && tmp[5] == '-')) &&
+ (s = strchr (tmp+18,',')) && strchr (s+2,';')) ? T : NIL;
+ }
+ else errno = -1; /* bogus format */
+ close (fd); /* close the file */
+ /* \Marked status? */
+ if (sbuf.st_ctime > sbuf.st_atime) {
+ /* preserve atime and mtime */
+ times.actime = sbuf.st_atime;
+ times.modtime = sbuf.st_mtime;
+ utime (file,&times); /* set the times */
+ }
+ }
+ }
+ /* in case INBOX but not tenex format */
+ else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1;
+ return ret; /* return what we should */
+}
+
+
+/* Tenex manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tenex_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+/* Tenex mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void tenex_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* Tenex mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void tenex_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* Tenex mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void tenex_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* Tenex mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long tenex_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,mbx[MAILTMPLEN];
+ if (s = dummy_file (mbx,mailbox)) return dummy_create (stream,s);
+ sprintf (mbx,"Can't create %.80s: invalid name",mailbox);
+ mm_log (mbx,ERROR);
+ return NIL;
+}
+
+
+/* Tenex mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long tenex_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return tenex_rename (stream,mailbox,NIL);
+}
+
+/* Tenex mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long tenex_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = LONGT;
+ char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ int fd,ld;
+ struct stat sbuf;
+ if (!dummy_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'\\')) && !s[1])))) {
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ else if ((fd = open (file,O_BINARY|O_RDWR,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get exclusive parse/append permission */
+ if ((ld = lockname (lock,file,LOCK_EX)) < 0) {
+ mm_log ("Unable to lock rename mailbox",ERROR);
+ return NIL;
+ }
+ /* lock out other users */
+ if (flock (fd,LOCK_EX|LOCK_NB)) {
+ close (fd); /* couldn't lock, give up on it then */
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+ mm_log (tmp,ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return NIL;
+ }
+
+ if (newname) { /* want rename? */
+ /* found superior to destination name? */
+ if ((s = strrchr (tmp,'\\')) && (s != tmp) &&
+ ((tmp[1] != ':') || (s != tmp + 2))) {
+ c = s[1]; /* remember character after delimiter */
+ *s = s[1] = '\0'; /* tie off name at delimiter */
+ /* name doesn't exist, create it */
+ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) {
+ *s = '\\'; /* restore delimiter */
+ if (!dummy_create (stream,tmp)) ret = NIL;
+ }
+ else *s = '\\'; /* restore delimiter */
+ s[1] = c; /* restore character after delimiter */
+ }
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* pacify NTFS */
+ /* rename the file */
+ if (ret && rename (file,tmp)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ else {
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* pacify NTFS */
+ if (unlink (file)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %.80s",old,strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return ret; /* return success */
+}
+
+/* Tenex mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *tenex_open (MAILSTREAM *stream)
+{
+ int fd,ld;
+ char tmp[MAILTMPLEN];
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &tenexproto;
+ if (stream->local) fatal ("tenex recycle stream");
+ /* canonicalize the mailbox name */
+ if (!dummy_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ mm_log (tmp,ERROR);
+ }
+ if (stream->rdonly ||
+ (fd = open (tmp,O_BINARY|O_RDWR,NIL)) < 0) {
+ if ((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox: %.80s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ else if (!stream->rdonly) { /* got it, but readonly */
+ mm_log ("Can't get write access to mailbox, access is readonly",WARN);
+ stream->rdonly = T;
+ }
+ }
+ stream->local = fs_get (sizeof (TENEXLOCAL));
+ LOCAL->fd = fd; /* bind the file */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ LOCAL->text.data = (unsigned char *) fs_get (CHUNKSIZE);
+ LOCAL->text.size = CHUNKSIZE - 1;
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ /* get shared parse permission */
+ if ((ld = lockname (tmp,stream->mailbox,LOCK_SH)) < 0) {
+ mm_log ("Unable to lock open mailbox",ERROR);
+ return NIL;
+ }
+ flock (LOCAL->fd,LOCK_SH); /* lock the file */
+ unlockfd (ld,tmp); /* release shared parse permission */
+ LOCAL->filesize = 0; /* initialize parsed file size */
+ LOCAL->filetime = 0; /* time not set up yet */
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ stream->sequence++; /* bump sequence number */
+ stream->uid_validity = (unsigned long) time (0);
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ if (tenex_ping (stream) && !stream->nmsgs)
+ mm_log ("Mailbox is empty",(long) NIL);
+ if (!LOCAL) return NIL; /* failure if stream died */
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft =
+ stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ return stream; /* return stream to caller */
+}
+
+/* Tenex mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void tenex_close (MAILSTREAM *stream,long options)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ if (options & CL_EXPUNGE) tenex_expunge (stream,NIL,NIL);
+ stream->silent = silent; /* restore previous status */
+ flock (LOCAL->fd,LOCK_UN); /* unlock local file */
+ close (LOCAL->fd); /* close the local file */
+ /* free local text buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* Tenex mail fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ * Sniffs at file to get flags
+ */
+
+void tenex_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ STRING bs;
+ MESSAGECACHE *elt;
+ unsigned long i;
+ if (stream && LOCAL &&
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ if (!elt->rfc822_size) { /* have header size yet? */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.special.text.size,L_SET);
+ /* resize bigbuf if necessary */
+ if (LOCAL->buflen < elt->private.msg.full.text.size) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buflen = elt->private.msg.full.text.size;
+ LOCAL->buf = (char *) fs_get (LOCAL->buflen + 1);
+ }
+ /* tie off string */
+ LOCAL->buf[elt->private.msg.full.text.size] = '\0';
+ /* read in the message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.full.text.size);
+ INIT (&bs,mail_string,(void *) LOCAL->buf,
+ elt->private.msg.full.text.size);
+ /* calculate its CRLF size */
+ elt->rfc822_size = unix_crlflen (&bs);
+ }
+ tenex_elt (stream,i); /* get current flags from file */
+ }
+}
+
+/* TENEX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *tenex_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags)
+{
+ char *s;
+ unsigned long i;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ /* get to header position */
+ lseek (LOCAL->fd,tenex_hdrpos (stream,msgno,&i),L_SET);
+ if (flags & FT_INTERNAL) {
+ if (i > LOCAL->buflen) { /* resize if not enough space */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get (LOCAL->buflen = i + 1);
+ }
+ /* slurp the data */
+ read (LOCAL->fd,LOCAL->buf,*length = i);
+ }
+ else {
+ s = (char *) fs_get (i + 1);/* get readin buffer */
+ s[i] = '\0'; /* tie off string */
+ read (LOCAL->fd,s,i); /* slurp the data */
+ /* make CRLF copy of string */
+ *length = unix_crlfcpy (&LOCAL->buf,&LOCAL->buflen,s,i);
+ fs_give ((void **) &s); /* free readin buffer */
+ }
+ return LOCAL->buf;
+}
+
+/* TENEX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T, always
+ */
+
+long tenex_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ char *s;
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ /* get message status */
+ elt = tenex_elt (stream,msgno);
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ elt->seen = T; /* mark message as seen */
+ /* recalculate status */
+ tenex_update_status (stream,msgno,T);
+ mm_flags (stream,msgno);
+ }
+ if (flags & FT_INTERNAL) { /* if internal representation wanted */
+ /* find header position */
+ i = tenex_hdrpos (stream,msgno,&j);
+ if (i > LOCAL->buflen) { /* resize if not enough space */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get (LOCAL->buflen = i + 1);
+ }
+ /* go to text position */
+ lseek (LOCAL->fd,i + j,L_SET);
+ /* slurp the data */
+ if (read (LOCAL->fd,LOCAL->buf,i) != (long) i) return NIL;
+ /* set up stringstruct for internal */
+ INIT (bs,mail_string,LOCAL->buf,i);
+ }
+ else { /* normal form, previous text cached? */
+ if (elt->private.uid == LOCAL->uid)
+ i = elt->private.msg.text.text.size;
+ else { /* not cached, cache it now */
+ LOCAL->uid = elt->private.uid;
+ /* find header position */
+ i = tenex_hdrpos (stream,msgno,&j);
+ /* go to text position */
+ lseek (LOCAL->fd,i + j,L_SET);
+ s = (char *) fs_get ((i = tenex_size (stream,msgno) - j) + 1);
+ s[i] = '\0'; /* tie off string */
+ read (LOCAL->fd,s,i); /* slurp the data */
+ /* make CRLF copy of string */
+ i = elt->private.msg.text.text.size =
+ strcrlfcpy (&LOCAL->text.data,&LOCAL->text.size,s,i);
+ fs_give ((void **) &s); /* free readin buffer */
+ }
+ /* set up stringstruct */
+ INIT (bs,mail_string,LOCAL->text.data,i);
+ }
+ return T; /* success */
+}
+
+/* Tenex mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ */
+
+void tenex_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ struct utimbuf times;
+ struct stat sbuf;
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* make sure read comes after all that */
+ utime (stream->mailbox,&times);
+ }
+}
+
+
+/* Tenex mail per-message modify flags
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void tenex_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ struct stat sbuf;
+ /* maybe need to do a checkpoint? */
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ LOCAL->filetime = 0; /* don't do this test for any other messages */
+ }
+ /* recalculate status */
+ tenex_update_status (stream,elt->msgno,NIL);
+}
+
+/* Tenex mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream still alive, NIL if not
+ */
+
+long tenex_ping (MAILSTREAM *stream)
+{
+ unsigned long i = 1;
+ long r = T;
+ int ld;
+ char lock[MAILTMPLEN];
+ struct stat sbuf;
+ if (stream && LOCAL) { /* only if stream already open */
+ fstat (LOCAL->fd,&sbuf); /* get current file poop */
+ if (LOCAL->filetime && !(LOCAL->mustcheck || LOCAL->shouldcheck) &&
+ (LOCAL->filetime < sbuf.st_mtime)) LOCAL->shouldcheck = T;
+ /* check for changed message status */
+ if (LOCAL->mustcheck || LOCAL->shouldcheck) {
+ LOCAL->filetime = sbuf.st_mtime;
+ if (LOCAL->shouldcheck) /* babble when we do this unilaterally */
+ mm_notify (stream,"[CHECK] Checking for flag updates",NIL);
+ while (i <= stream->nmsgs) tenex_elt (stream,i++);
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ }
+ /* get shared parse/append permission */
+ if ((sbuf.st_size != LOCAL->filesize) &&
+ ((ld = lockname (lock,stream->mailbox,LOCK_SH)) >= 0)) {
+ /* parse resulting mailbox */
+ r = (tenex_parse (stream)) ? T : NIL;
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ }
+ return r; /* return result of the parse */
+}
+
+
+/* Tenex mail check mailbox (reparses status too)
+ * Accepts: MAIL stream
+ */
+
+void tenex_check (MAILSTREAM *stream)
+{
+ /* mark that a check is desired */
+ if (LOCAL) LOCAL->mustcheck = T;
+ if (tenex_ping (stream)) mm_log ("Check completed",(long) NIL);
+}
+
+/* Tenex mail expunge mailbox
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long tenex_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ struct utimbuf times;
+ struct stat sbuf;
+ off_t pos = 0;
+ int ld;
+ unsigned long i = 1;
+ unsigned long j,k,m,recent;
+ unsigned long n = 0;
+ unsigned long delta = 0;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ if (!(ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ tenex_ping (stream))); /* parse sequence if given, ping stream */
+ else if (stream->rdonly) mm_log ("Expunge ignored on readonly mailbox",WARN);
+ else {
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ }
+ /* get exclusive access */
+ if ((ld = lockname (lock,stream->mailbox,LOCK_EX)) < 0)
+ mm_log ("Unable to lock expunge mailbox",ERROR);
+ /* make sure see any newly-arrived messages */
+ else if (!tenex_parse (stream));
+ /* get exclusive access */
+ else if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
+ flock (LOCAL->fd,LOCK_SH);/* recover previous lock */
+ mm_log ("Can't expunge because mailbox is in use by another process",
+ ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+
+ else {
+ mm_critical (stream); /* go critical */
+ recent = stream->recent; /* get recent now that pinged and locked */
+ /* for each message */
+ while (i <= stream->nmsgs) {
+ /* get cache element */
+ elt = tenex_elt (stream,i);
+ /* number of bytes to smash or preserve */
+ k = elt->private.special.text.size + tenex_size (stream,i);
+ /* if need to expunge this message */
+ if (elt->deleted && (sequence ? elt->sequence : T)) {
+ /* if recent, note one less recent message */
+ if (elt->recent) --recent;
+ delta += k; /* number of bytes to delete */
+ /* notify upper levels */
+ mail_expunged (stream,i);
+ n++; /* count up one more expunged message */
+ }
+ else if (i++ && delta) {/* preserved message */
+ /* first byte to preserve */
+ j = elt->private.special.offset;
+ do { /* read from source position */
+ m = min (k,LOCAL->buflen);
+ lseek (LOCAL->fd,j,L_SET);
+ read (LOCAL->fd,LOCAL->buf,m);
+ pos = j - delta; /* write to destination position */
+ while (T) {
+ lseek (LOCAL->fd,pos,L_SET);
+ if (write (LOCAL->fd,LOCAL->buf,m) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+ pos += m; /* new position */
+ j += m; /* next chunk, perhaps */
+ } while (k -= m); /* until done */
+ /* note the new address of this text */
+ elt->private.special.offset -= delta;
+ }
+ /* preserved but no deleted messages */
+ else pos = elt->private.special.offset + k;
+ }
+
+ if (n) { /* truncate file after last message */
+ if (pos != (LOCAL->filesize -= delta)) {
+ sprintf (LOCAL->buf,
+ "Calculated size mismatch %lu != %lu, delta = %lu",
+ (unsigned long) pos,(unsigned long) LOCAL->filesize,delta);
+ mm_log (LOCAL->buf,WARN);
+ LOCAL->filesize = pos;/* fix it then */
+ }
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ sprintf (LOCAL->buf,"Expunged %lu messages",n);
+ /* output the news */
+ mm_log (LOCAL->buf,(long) NIL);
+ }
+ else mm_log ("No messages deleted, so no update needed",(long) NIL);
+ fsync (LOCAL->fd); /* force disk update */
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* reset atime to now */
+ utime (stream->mailbox,&times);
+ mm_nocritical (stream); /* release critical */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ flock (LOCAL->fd,LOCK_SH);/* allow sharers again */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+ }
+ return ret;
+}
+
+/* Tenex mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if success, NIL if failed
+ */
+
+long tenex_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ struct utimbuf times;
+ MESSAGECACHE *elt;
+ unsigned long i,j,k;
+ long ret = LONGT;
+ int fd,ld;
+ char file[MAILTMPLEN],lock[MAILTMPLEN];
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ /* make sure valid mailbox */
+ if (!tenex_isvalid (mailbox,file)) switch (errno) {
+ case ENOENT: /* no such file? */
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid Tenex-format mailbox name: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a Tenex-format mailbox: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* got file? */
+ if ((fd = open (file,O_BINARY|O_RDWR|O_CREAT,S_IREAD|S_IWRITE)) < 0) {
+ sprintf (LOCAL->buf,"Unable to open copy mailbox: %.80s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ mm_critical (stream); /* go critical */
+ /* get exclusive parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockname (lock,file,LOCK_EX)) < 0)) {
+ mm_log ("Unable to lock copy mailbox",ERROR);
+ mm_nocritical (stream);
+ return NIL;
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
+
+ /* for each requested message */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ /* number of bytes to copy */
+ k = elt->private.special.text.size + tenex_size (stream,i);
+ do { /* read from source position */
+ j = min (k,LOCAL->buflen);
+ read (LOCAL->fd,LOCAL->buf,j);
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ } while (ret && (k -= j));/* until done */
+ }
+ /* delete all requested messages */
+ if (ret && (options & CP_MOVE)) {
+ sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ }
+ /* set atime to now-1 if successful copy */
+ if (ret) times.actime = time (0) - 1;
+ /* else preserved \Marked status */
+ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ?
+ sbuf.st_atime : time (0);
+ times.modtime = sbuf.st_mtime;/* preserve mtime */
+ utime (file,&times); /* set the times */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ close (fd); /* close the file */
+ mm_nocritical (stream); /* release critical */
+ /* delete all requested messages */
+ if (ret && (options & CP_MOVE)) {
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = tenex_elt (stream,i))->sequence) {
+ elt->deleted = T; /* mark message deleted */
+ /* recalculate status */
+ tenex_update_status (stream,i,NIL);
+ }
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* make sure atime remains greater */
+ utime (stream->mailbox,&times);
+ }
+ }
+ if (ret && mail_parameters (NIL,GET_COPYUID,NIL))
+ mm_log ("Can not return meaningful COPYUID with this mailbox format",WARN);
+ return ret;
+}
+
+/* Tenex mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long tenex_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd,ld,c;
+ char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ struct utimbuf times;
+ FILE *df;
+ MESSAGECACHE elt;
+ long f;
+ unsigned long i,j,uf,size;
+ STRING *message;
+ long ret = LONGT;
+ /* default stream to prototype */
+ if (!stream) stream = &tenexproto;
+ /* make sure valid mailbox */
+ if (!tenex_isvalid (mailbox,file)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (!compare_cstring (mailbox,"INBOX")) tenex_create (NIL,"INBOX");
+ else {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* falls through */
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid TENEX-format mailbox name: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a TENEX-format mailbox: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get first message */
+ if (!(*af) (stream,data,&flags,&date,&message)) return NIL;
+
+ /* open destination mailbox */
+ if (((fd = open (file,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE))
+ < 0) || !(df = fdopen (fd,"ab"))) {
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockname (lock,file,LOCK_EX)) < 0)) {
+ mm_log ("Unable to lock append mailbox",ERROR);
+ close (fd);
+ return NIL;
+ }
+ mm_critical (stream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ errno = 0;
+ do { /* parse flags */
+ if (!SIZE (message)) { /* guard against zero-length */
+ mm_log ("Append of zero-length message",ERROR);
+ ret = NIL;
+ break;
+ }
+ f = mail_parse_flags (stream,flags,&i);
+ /* reverse bits (dontcha wish we had CIRC?) */
+ for (uf = 0; i; uf |= 1 << (29 - find_rightmost_bit (&i)));
+ if (date) { /* parse date if given */
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ mm_log (tmp,ERROR);
+ ret = NIL; /* mark failure */
+ break;
+ }
+ mail_date (tmp,&elt); /* write preseved date */
+ }
+ else internal_date (tmp); /* get current date in IMAP format */
+ i = GETPOS (message); /* remember current position */
+ for (j = SIZE (message), size = 0; j; --j)
+ if (SNX (message) != '\015') ++size;
+ SETPOS (message,i); /* restore position */
+ /* write header */
+ if (fprintf (df,"%s,%lu;%010lo%02lo\n",tmp,size,uf,(unsigned long) f) < 0)
+ ret = NIL;
+ else { /* write message */
+ while (size) if ((c = 0xff & SNX (message)) != '\015') {
+ if (putc (c,df) != EOF) --size;
+ else break;
+ }
+ /* get next message */
+ if (size || !(*af) (stream,data,&flags,&date,&message)) ret = NIL;
+ }
+ } while (ret && message);
+ /* if error... */
+ if (!ret || (fflush (df) == EOF)) {
+ ftruncate (fd,sbuf.st_size);/* revert file */
+ close (fd); /* make sure fclose() doesn't corrupt us */
+ if (errno) {
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ }
+ ret = NIL;
+ }
+ if (ret) times.actime = time (0) - 1;
+ /* else preserved \Marked status */
+ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ?
+ sbuf.st_atime : time (0);
+ times.modtime = sbuf.st_mtime;/* preserve mtime */
+ utime (file,&times); /* set the times */
+ fclose (df); /* close the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ mm_nocritical (stream); /* release critical */
+ if (ret && mail_parameters (NIL,GET_APPENDUID,NIL))
+ mm_log ("Can not return meaningful APPENDUID with this mailbox format",
+ WARN);
+ return ret;
+}
+
+/* Internal routines */
+
+
+/* Tenex mail return internal message size in bytes
+ * Accepts: MAIL stream
+ * message #
+ * Returns: internal size of message
+ */
+
+unsigned long tenex_size (MAILSTREAM *stream,unsigned long m)
+{
+ MESSAGECACHE *elt = mail_elt (stream,m);
+ return ((m < stream->nmsgs) ? mail_elt (stream,m+1)->private.special.offset :
+ LOCAL->filesize) -
+ (elt->private.special.offset + elt->private.special.text.size);
+}
+
+/* Tenex mail parse mailbox
+ * Accepts: MAIL stream
+ * Returns: T if parse OK
+ * NIL if failure, stream aborted
+ */
+
+long tenex_parse (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = NIL;
+ unsigned char c,*s,*t,*x;
+ char tmp[MAILTMPLEN];
+ unsigned long i,j;
+ long curpos = LOCAL->filesize;
+ long nmsgs = stream->nmsgs;
+ long recent = stream->recent;
+ short added = NIL;
+ short silent = stream->silent;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ if (sbuf.st_size < curpos) { /* sanity check */
+ sprintf (tmp,"Mailbox shrank from %ld to %ld!",curpos,sbuf.st_size);
+ mm_log (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ stream->silent = T; /* don't pass up mm_exists() events yet */
+ while (sbuf.st_size - curpos){/* while there is stuff to parse */
+ /* get to that position in the file */
+ lseek (LOCAL->fd,curpos,L_SET);
+ if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
+ sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size,
+ i ? strerror (errno) : "no data read");
+ mm_log (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ LOCAL->buf[i] = '\0'; /* tie off buffer just in case */
+ if (!(s = strchr (LOCAL->buf,'\012'))) {
+ sprintf (tmp,"Unable to find newline at %lu in %lu bytes, text: %s",
+ (unsigned long) curpos,i,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ *s = '\0'; /* tie off header line */
+ i = (s + 1) - LOCAL->buf; /* note start of text offset */
+ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
+ sprintf (tmp,"Unable to parse internal header at %lu: %s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ *s++ = '\0'; *t++ = '\0'; /* tie off fields */
+
+ added = T; /* note that a new message was added */
+ /* swell the cache */
+ mail_exists (stream,++nmsgs);
+ /* instantiate an elt for this message */
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ elt->private.uid = ++stream->uid_last;
+ /* note file offset of header */
+ elt->private.special.offset = curpos;
+ /* in case error */
+ elt->private.special.text.size = 0;
+ /* header size not known yet */
+ elt->private.msg.header.text.size = 0;
+ x = s; /* parse the header components */
+ if (mail_parse_date (elt,LOCAL->buf) &&
+ (elt->private.msg.full.text.size = strtoul (s,(char **) &s,10)) &&
+ (!(s && *s)) && isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) &&
+ isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) &&
+ isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) &&
+ isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12])
+ elt->private.special.text.size = i;
+ else { /* oops */
+ sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s",
+ curpos,(char *) LOCAL->buf,(char *) x,(char *) t);
+ mm_log (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ /* make sure didn't run off end of file */
+ if ((curpos += (elt->private.msg.full.text.size + i)) > sbuf.st_size) {
+ sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)",
+ elt->private.special.offset,(unsigned long) curpos,
+ (unsigned long) sbuf.st_size);
+ mm_log (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ c = t[10]; /* remember first system flags byte */
+ t[10] = '\0'; /* tie off flags */
+ j = strtoul (t,NIL,8); /* get user flags value */
+ t[10] = c; /* restore first system flags byte */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ /* calculate system flags */
+ if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T;
+ if (j & fDELETED) elt->deleted = T;
+ if (j & fFLAGGED) elt->flagged = T;
+ if (j & fANSWERED) elt->answered = T;
+ if (j & fDRAFT) elt->draft = T;
+ if (!(j & fOLD)) { /* newly arrived message? */
+ elt->recent = T;
+ recent++; /* count up a new recent message */
+ /* mark it as old */
+ tenex_update_status (stream,nmsgs,NIL);
+ }
+ }
+ fsync (LOCAL->fd); /* make sure all the fOLD flags take */
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */
+ LOCAL->filetime = sbuf.st_mtime;
+ if (added && !stream->rdonly){/* make sure atime updated */
+ struct utimbuf times;
+ times.actime = time (0);
+ times.modtime = LOCAL->filetime;
+ utime (stream->mailbox,&times);
+ }
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
+ mail_recent (stream,recent); /* and of change in recent messages */
+ return LONGT; /* return the winnage */
+}
+
+/* Tenex get cache element with status updating from file
+ * Accepts: MAIL stream
+ * message number
+ * Returns: cache element
+ */
+
+MESSAGECACHE *tenex_elt (MAILSTREAM *stream,unsigned long msgno)
+{
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ struct { /* old flags */
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged;
+ old.answered = elt->answered; old.draft = elt->draft;
+ old.user_flags = elt->user_flags;
+ tenex_read_flags (stream,elt);
+ if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
+ (old.flagged != elt->flagged) || (old.answered != elt->answered) ||
+ (old.draft != elt->draft) || (old.user_flags != elt->user_flags))
+ mm_flags (stream,msgno); /* let top level know */
+ return elt;
+}
+
+
+/* Tenex read flags from file
+ * Accepts: MAIL stream
+ * Returns: cache element
+ */
+
+void tenex_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ unsigned long i,j;
+ /* noop if readonly and have valid flags */
+ if (stream->rdonly && elt->valid) return;
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 13,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,12) < 0) {
+ sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ /* calculate system flags */
+ i = (((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0');
+ elt->seen = i & fSEEN ? T : NIL; elt->deleted = i & fDELETED ? T : NIL;
+ elt->flagged = i & fFLAGGED ? T : NIL;
+ elt->answered = i & fANSWERED ? T : NIL; elt->draft = i & fDRAFT ? T : NIL;
+ LOCAL->buf[10] = '\0'; /* tie off flags */
+ j = strtoul(LOCAL->buf,NIL,8);/* get user flags value */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ elt->valid = T; /* have valid flags now */
+}
+
+/* Tenex update status string
+ * Accepts: MAIL stream
+ * message number
+ * flag saying whether or not to sync
+ */
+
+void tenex_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag)
+{
+ struct utimbuf times;
+ struct stat sbuf;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ unsigned long j,k = 0;
+ /* readonly */
+ if (stream->rdonly || !elt->valid) tenex_read_flags (stream,elt);
+ else { /* readwrite */
+ j = elt->user_flags; /* get user flags */
+ /* reverse bits (dontcha wish we had CIRC?) */
+ while (j) k |= 1 << (29 - find_rightmost_bit (&j));
+ /* print new flag string */
+ sprintf (LOCAL->buf,"%010lo%02o",k,(unsigned)
+ (fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)));
+ while (T) { /* get to that place in the file */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 13,L_SET);
+ /* write new flags */
+ if (write (LOCAL->fd,LOCAL->buf,12) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+ if (syncflag) { /* sync if requested */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* make sure read is later */
+ utime (stream->mailbox,&times);
+ }
+ }
+}
+
+/* Tenex locate header for a message
+ * Accepts: MAIL stream
+ * message number
+ * pointer to returned header size
+ * Returns: position of header in file
+ */
+
+unsigned long tenex_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size)
+{
+ unsigned long siz;
+ long i = 0;
+ char c = '\0';
+ char *s = NIL;
+ MESSAGECACHE *elt = tenex_elt (stream,msgno);
+ unsigned long ret = elt->private.special.offset +
+ elt->private.special.text.size;
+ unsigned long msiz = tenex_size (stream,msgno);
+ /* is header size known? */
+ if (!(*size = elt->private.msg.header.text.size)) {
+ lseek (LOCAL->fd,ret,L_SET);/* get to header position */
+ /* search message for LF LF */
+ for (siz = 0; siz < msiz; siz++) {
+ if (--i <= 0) /* read another buffer as necessary */
+ read (LOCAL->fd,s = LOCAL->buf,i = min (msiz-siz,(long) MAILTMPLEN));
+ /* two newline sequence? */
+ if ((c == '\012') && (*s == '\012')) {
+ /* yes, note for later */
+ elt->private.msg.header.text.size = (*size = siz + 1);
+ return ret; /* return to caller */
+ }
+ else c = *s++; /* next character */
+ }
+ /* header consumes entire message */
+ elt->private.msg.header.text.size = *size = msiz;
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/os2/unixnt.c b/imap/src/osdep/os2/unixnt.c
new file mode 100644
index 00000000..2e92d39a
--- /dev/null
+++ b/imap/src/osdep/os2/unixnt.c
@@ -0,0 +1,2297 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX mail routines
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 20 December 1989
+ * Last Edited: 27 March 2008
+ */
+
+
+/* DEDICATION
+ *
+ * This file is dedicated to my dog, Unix, also known as Yun-chan and
+ * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix
+ * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after
+ * a two-month bout with cirrhosis of the liver.
+ *
+ * He was a dear friend, and I miss him terribly.
+ *
+ * Lift a leg, Yunie. Luv ya forever!!!!
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <time.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/utime.h>
+#include "unixnt.h"
+#include "pseudo.h"
+#include "fdstring.h"
+#include "misc.h"
+#include "dummy.h"
+
+/* UNIX I/O stream local data */
+
+typedef struct unix_local {
+ unsigned int dirty : 1; /* disk copy needs updating */
+ unsigned int ddirty : 1; /* double-dirty, ping becomes checkpoint */
+ unsigned int pseudo : 1; /* uses a pseudo message */
+ unsigned int appending : 1; /* don't mark new messages as old */
+ int fd; /* mailbox file descriptor */
+ int ld; /* lock file descriptor */
+ char *lname; /* lock file name */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ unsigned long uid; /* current text uid */
+ SIZEDTEXT text; /* current text */
+ unsigned long textlen; /* current text length */
+ char *line; /* returned line */
+ char *linebuf; /* line readin buffer */
+ unsigned long linebuflen; /* current line readin buffer length */
+} UNIXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((UNIXLOCAL *) stream->local)
+
+
+/* UNIX protected file structure */
+
+typedef struct unix_file {
+ MAILSTREAM *stream; /* current stream */
+ off_t curpos; /* current file position */
+ off_t protect; /* protected position */
+ off_t filepos; /* current last written file position */
+ char *buf; /* overflow buffer */
+ size_t buflen; /* current overflow buffer length */
+ char *bufpos; /* current buffer position */
+} UNIXFILE;
+
+/* Function prototypes */
+
+DRIVER *unix_valid (char *name);
+void *unix_parameters (long function,void *value);
+void unix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void unix_list (MAILSTREAM *stream,char *ref,char *pat);
+void unix_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long unix_create (MAILSTREAM *stream,char *mailbox);
+long unix_delete (MAILSTREAM *stream,char *mailbox);
+long unix_rename (MAILSTREAM *stream,char *old,char *newname);
+MAILSTREAM *unix_open (MAILSTREAM *stream);
+void unix_close (MAILSTREAM *stream,long options);
+char *unix_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long unix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt,
+ unsigned long *length,long flags);
+void unix_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long unix_ping (MAILSTREAM *stream);
+void unix_check (MAILSTREAM *stream);
+long unix_expunge (MAILSTREAM *stream,char *sequence,long options);
+long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long unix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+int unix_collect_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
+ STRING *msg);
+int unix_append_msgs (MAILSTREAM *stream,FILE *sf,FILE *df,SEARCHSET *set);
+
+void unix_abort (MAILSTREAM *stream);
+char *unix_file (char *dst,char *name);
+int unix_lock (char *file,int flags,int mode,char *lock,int op);
+void unix_unlock (int fd,MAILSTREAM *stream,char *lock);
+int unix_parse (MAILSTREAM *stream,char *lock,int op);
+char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size);
+unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr);
+unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt,
+ unsigned long uid,long flag);
+long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,char *lock,
+ long flags);
+long unix_extend (MAILSTREAM *stream,unsigned long size);
+void unix_write (UNIXFILE *f,char *s,unsigned long i);
+void unix_phys_write (UNIXFILE *f,char *buf,size_t size);
+
+/* UNIX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER unixdriver = {
+ "unix", /* driver name */
+ /* driver flags */
+ DR_LOCAL|DR_MAIL|DR_NONEWMAILRONLY|DR_XPOINT,
+ (DRIVER *) NIL, /* next driver */
+ unix_valid, /* mailbox is valid for us */
+ unix_parameters, /* manipulate parameters */
+ unix_scan, /* scan mailboxes */
+ unix_list, /* list mailboxes */
+ unix_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ unix_create, /* create mailbox */
+ unix_delete, /* delete mailbox */
+ unix_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ unix_open, /* open mailbox */
+ unix_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ unix_header, /* fetch message header */
+ unix_text, /* fetch message text */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ NIL, /* modify flags */
+ unix_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ unix_ping, /* ping mailbox to see if still alive */
+ unix_check, /* check for new messages */
+ unix_expunge, /* expunge deleted messages */
+ unix_copy, /* copy messages to another mailbox */
+ unix_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM unixproto = {&unixdriver};
+
+ /* driver parameters */
+static long unix_fromwidget = T;
+
+/* UNIX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *unix_valid (char *name)
+{
+ int fd;
+ DRIVER *ret = NIL;
+ int c,r;
+ char tmp[MAILTMPLEN],file[MAILTMPLEN],*s,*t;
+ struct stat sbuf;
+ struct utimbuf times;
+ errno = EINVAL; /* assume invalid argument */
+ /* must be non-empty file */
+ if ((t = dummy_file (file,name)) && !stat (t,&sbuf) &&
+ ((sbuf.st_mode & S_IFMT) == S_IFREG)) {
+ if (!sbuf.st_size)errno = 0;/* empty file */
+ else if ((fd = open (file,O_BINARY|O_RDONLY,NIL)) >= 0) {
+ memset (tmp,'\0',MAILTMPLEN);
+ if (read (fd,tmp,MAILTMPLEN-1) <= 0) errno = -1;
+ else { /* ignore leading whitespace */
+ for (s = tmp,c = '\n';
+ (*s == '\r') || (*s == '\n') || (*s == ' ') || (*s == '\t');
+ c = *s++);
+ if (c == '\n') { /* at start of a line? */
+ VALID (s,t,r,c); /* yes, validate format */
+ if (r) ret = &unixdriver;
+ else errno = -1; /* invalid format */
+ }
+ }
+ close (fd); /* close the file */
+ /* \Marked status? */
+ if ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) {
+ /* yes, preserve atime and mtime */
+ times.actime = sbuf.st_atime;
+ times.modtime = sbuf.st_mtime;
+ utime (file,&times); /* set the times */
+ }
+ }
+ }
+ return ret; /* return what we should */
+}
+/* UNIX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *unix_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_FROMWIDGET:
+ unix_fromwidget = (long) value;
+ case GET_FROMWIDGET:
+ ret = (void *) unix_fromwidget;
+ break;
+ }
+ return ret;
+}
+
+/* UNIX mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void unix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* UNIX mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void unix_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* UNIX mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void unix_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* UNIX mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long unix_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,mbx[MAILTMPLEN],tmp[MAILTMPLEN];
+ long ret = NIL;
+ int fd;
+ time_t ti = time (0);
+ if (!(s = dummy_file (mbx,mailbox))) {
+ sprintf (tmp,"Can't create %.80s: invalid name",mailbox);
+ mm_log (tmp,ERROR);
+ }
+ /* create underlying file */
+ else if (dummy_create_path (stream,s,NIL)) {
+ if ((s = strrchr (s,'\\')) && !s[1]) ret = T;
+ if ((fd = open (mbx,O_WRONLY|O_BINARY,NIL)) < 0) {
+ sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno));
+ mm_log (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ else { /* initialize header */
+ memset (tmp,'\0',MAILTMPLEN);
+ sprintf (tmp,"From %s %s",pseudo_from,ctime (&ti));
+ if (s = strpbrk (tmp,"\r\n")) *s = '\0';
+ strcat (tmp,"\r\nDate: ");
+ rfc822_fixed_date (s = tmp + strlen (tmp));
+ sprintf (s += strlen (s), /* write the pseudo-header */
+ "\r\nFrom: %s <%s@%s>\r\nSubject: %s\r\nX-IMAP: %010lu 0000000000\r\nStatus: RO\r\n\r\n%s\r\n\r\n",
+ pseudo_name,pseudo_from,mylocalhost (),pseudo_subject,
+ (unsigned long) ti,pseudo_msg);
+ if (write (fd,tmp,strlen (tmp)) > 0) {
+ close (fd); /* close file */
+ ret = T;
+ }
+ else {
+ sprintf (tmp,"Can't initialize mailbox node %.80s: %s",mbx,
+ strerror (errno));
+ mm_log (tmp,ERROR);
+ close (fd); /* close file before unlinking */
+ unlink (mbx); /* delete the file */
+ }
+ }
+ }
+ return ret;
+}
+
+/* UNIX mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long unix_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return unix_rename (stream,mailbox,NIL);
+}
+
+
+/* UNIX mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long unix_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = NIL;
+ char c,*s = NIL;
+ char tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN],lockx[MAILTMPLEN];
+ int fd,ld;
+ struct stat sbuf;
+ mm_critical (stream); /* get the c-client lock */
+ if (!dummy_file (file,old) ||
+ (newname && (!(s = dummy_file (tmp,newname)) ||
+ ((s = strrchr (s,'\\')) && !s[1]))))
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ else if ((ld = lockname (lock,file,NIL)) < 0)
+ sprintf (tmp,"Can't get lock for mailbox %.80s",old);
+
+ else { /* lock out other c-clients */
+ if (flock (ld,LOCK_EX|LOCK_NB)) {
+ close (ld); /* couldn't lock, give up on it then */
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+ }
+ /* lock out non c-client applications */
+ else if ((fd = unix_lock (file,O_BINARY|O_RDWR,S_IREAD|S_IWRITE,lockx,
+ LOCK_EX)) < 0)
+ sprintf (tmp,"Can't lock mailbox %.80s: %s",old,strerror (errno));
+ else {
+ unix_unlock(fd,NIL,lockx);/* pacify evil NTFS */
+ if (newname) { /* want rename? */
+ /* found superior to destination name? */
+ if ((s = strrchr (tmp,'\\')) && (s != tmp) &&
+ ((tmp[1] != ':') || (s != tmp + 2))) {
+ c = s[1]; /* remember character after delimiter */
+ *s = s[1] = '\0'; /* tie off name at delimiter */
+ /* name doesn't exist, create it */
+ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) {
+ *s = '\\'; /* restore delimiter */
+ if (!dummy_create (stream,newname)) {
+ flock (ld,LOCK_UN);
+ close (ld); /* close c-client lock */
+ unlink (lock); /* and delete it */
+ mm_nocritical (stream);
+ return NIL; /* couldn't create superior */
+ }
+ }
+ else *s = '\\'; /* restore delimiter */
+ s[1] = c; /* restore character after delimiter */
+ }
+ if (rename (file,tmp))
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ else ret = T; /* set success */
+ }
+ else if (unlink (file)) /* want delete */
+ sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
+ else ret = T; /* set success */
+ flock (ld,LOCK_UN); /* release c-client lock */
+ close (ld); /* close c-client lock */
+ unlink (lock); /* and delete it */
+ }
+ }
+ mm_nocritical (stream); /* no longer critical */
+ if (!ret) mm_log (tmp,ERROR); /* log error */
+ return ret; /* return success or failure */
+}
+
+/* UNIX mail open
+ * Accepts: Stream to open
+ * Returns: Stream on success, NIL on failure
+ */
+
+MAILSTREAM *unix_open (MAILSTREAM *stream)
+{
+ int fd;
+ char tmp[MAILTMPLEN];
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &unixproto;
+ if (stream->local) fatal ("unix recycle stream");
+ stream->local = memset (fs_get (sizeof (UNIXLOCAL)),0,sizeof (UNIXLOCAL));
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ /* canonicalize the stream mailbox name */
+ if (!dummy_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* flush old name */
+ fs_give ((void **) &stream->mailbox);
+ /* save canonical name */
+ stream->mailbox = cpystr (tmp);
+ LOCAL->fd = LOCAL->ld = -1; /* no file or state locking yet */
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = CHUNKSIZE) + 1);
+ LOCAL->text.data = (unsigned char *) fs_get (CHUNKSIZE);
+ LOCAL->text.size = CHUNKSIZE - 1;
+ LOCAL->linebuf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->linebuflen = CHUNKSIZE - 1;
+ stream->sequence++; /* bump sequence number */
+ if (!stream->rdonly) { /* make lock for read/write access */
+ if ((fd = lockname (tmp,stream->mailbox,NIL)) < 0)
+ mm_log ("Can't open mailbox lock, access is readonly",WARN);
+ /* can get the lock? */
+ else if (flock (fd,LOCK_EX|LOCK_NB)) {
+ if (!stream->silent)
+ mm_log ("Mailbox is open by another process, access is readonly",WARN);
+ close (fd);
+ }
+ else { /* got the lock, nobody else can alter state */
+ LOCAL->ld = fd; /* note lock's fd and name */
+ LOCAL->lname = cpystr (tmp);
+ }
+ }
+
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ /* will we be able to get write access? */
+ if ((LOCAL->ld >= 0) && access (stream->mailbox,02) && (errno == EACCES)) {
+ mm_log ("Can't get write access to mailbox, access is readonly",WARN);
+ flock (LOCAL->ld,LOCK_UN); /* release the lock */
+ close (LOCAL->ld); /* close the lock file */
+ LOCAL->ld = -1; /* no more lock fd */
+ unlink (LOCAL->lname); /* delete it */
+ }
+ /* reset UID validity */
+ stream->uid_validity = stream->uid_last = 0;
+ if (stream->silent && !stream->rdonly && (LOCAL->ld < 0))
+ unix_abort (stream); /* abort if can't get RW silent stream */
+ /* parse mailbox */
+ else if (unix_parse (stream,tmp,LOCK_SH)) {
+ unix_unlock (LOCAL->fd,stream,tmp);
+ mail_unlock (stream);
+ mm_nocritical (stream); /* done with critical */
+ }
+ if (!LOCAL) return NIL; /* failure if stream died */
+ /* make sure upper level knows readonly */
+ stream->rdonly = (LOCAL->ld < 0);
+ /* notify about empty mailbox */
+ if (!(stream->nmsgs || stream->silent)) mm_log ("Mailbox is empty",NIL);
+ if (!stream->rdonly) { /* flags stick if readwrite */
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft = T;
+ /* have permanent keywords */
+ stream->perm_user_flags = 0xffffffff;
+ /* and maybe can create them too */
+ stream->kwd_create = stream->user_flags[NUSERFLAGS-1] ? NIL : T;
+ }
+ return stream; /* return stream alive to caller */
+}
+
+
+/* UNIX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void unix_close (MAILSTREAM *stream,long options)
+{
+ int silent = stream->silent;
+ stream->silent = T; /* go silent */
+ /* expunge if requested */
+ if (options & CL_EXPUNGE) unix_expunge (stream,NIL,NIL);
+ /* else dump final checkpoint */
+ else if (LOCAL->dirty) unix_check (stream);
+ stream->silent = silent; /* restore old silence state */
+ unix_abort (stream); /* now punt the file and local data */
+}
+
+/* UNIX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+ /* lines to filter from header */
+static STRINGLIST *unix_hlines = NIL;
+
+char *unix_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned char *s;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ elt = mail_elt (stream,msgno);/* get cache */
+ if (!unix_hlines) { /* once only code */
+ STRINGLIST *lines = unix_hlines = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "Status"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-Status"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-Keywords"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-UID"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-IMAP"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-IMAPbase"));
+ }
+ /* go to header position */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.msg.header.offset,L_SET);
+
+ if (flags & FT_INTERNAL) { /* initial data OK? */
+ if (elt->private.msg.header.text.size > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
+ elt->private.msg.header.text.size) + 1);
+ }
+ /* read message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.header.text.size);
+ /* got text, tie off string */
+ LOCAL->buf[*length = elt->private.msg.header.text.size] = '\0';
+ }
+ else { /* need to make a CRLF version */
+ read (LOCAL->fd,s = (char *) fs_get (elt->private.msg.header.text.size+1),
+ elt->private.msg.header.text.size);
+ /* tie off string, and convert to CRLF */
+ s[elt->private.msg.header.text.size] = '\0';
+ *length = unix_crlfcpy (&LOCAL->buf,&LOCAL->buflen,s,
+ elt->private.msg.header.text.size);
+ fs_give ((void **) &s); /* free readin buffer */
+ }
+ *length = mail_filter (LOCAL->buf,*length,unix_hlines,FT_NOT);
+ return LOCAL->buf; /* return processed copy */
+}
+
+/* UNIX mail fetch message text
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T on success, NIL if failure
+ */
+
+long unix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ char *s;
+ unsigned long i;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mail_elt (stream,msgno);/* get cache element */
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ /* mark message seen and dirty */
+ elt->seen = elt->private.dirty = LOCAL->dirty = T;
+ mm_flags (stream,msgno);
+ }
+ s = unix_text_work (stream,elt,&i,flags);
+ INIT (bs,mail_string,s,i); /* set up stringstruct */
+ return T; /* success */
+}
+
+/* UNIX mail fetch message text worker routine
+ * Accepts: MAIL stream
+ * message cache element
+ * pointer to returned header text length
+ * option flags
+ */
+
+char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt,
+ unsigned long *length,long flags)
+{
+ FDDATA d;
+ STRING bs;
+ unsigned char c,*s,tmp[CHUNKSIZE];
+ /* go to text position */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.msg.text.offset,L_SET);
+ if (flags & FT_INTERNAL) { /* initial data OK? */
+ if (elt->private.msg.text.text.size > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
+ elt->private.msg.text.text.size) + 1);
+ }
+ /* read message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.text.text.size);
+ /* got text, tie off string */
+ LOCAL->buf[*length = elt->private.msg.text.text.size] = '\0';
+ return LOCAL->buf;
+ }
+ /* have it cached already? */
+ if (elt->private.uid != LOCAL->uid) {
+ /* not cached, cache it now */
+ LOCAL->uid = elt->private.uid;
+ /* is buffer big enough? */
+ if (elt->rfc822_size > LOCAL->text.size) {
+ /* excessively conservative, but the right thing is too hard to do */
+ fs_give ((void **) &LOCAL->text.data);
+ LOCAL->text.data = (unsigned char *)
+ fs_get ((LOCAL->text.size = elt->rfc822_size) + 1);
+ }
+ d.fd = LOCAL->fd; /* yes, set up file descriptor */
+ d.pos = elt->private.special.offset + elt->private.msg.text.offset;
+ d.chunk = tmp; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE; /* file chunk size */
+ INIT (&bs,fd_string,&d,elt->private.msg.text.text.size);
+ for (s = (char *) LOCAL->text.data; SIZE (&bs);) switch (c = SNX (&bs)) {
+ case '\r': /* carriage return seen */
+ *s++ = c; /* copy it and any succeeding LF */
+ if (SIZE (&bs) && (CHR (&bs) == '\n')) *s++ = SNX (&bs);
+ break;
+ case '\n':
+ *s++ = '\r'; /* insert a CR */
+ default:
+ *s++ = c; /* copy characters */
+ }
+ *s = '\0'; /* tie off buffer */
+ /* calculate length of cached data */
+ LOCAL->textlen = s - LOCAL->text.data;
+ }
+ *length = LOCAL->textlen; /* return from cache */
+ return (char *) LOCAL->text.data;
+}
+
+/* UNIX per-message modify flag
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void unix_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ /* only after finishing */
+ if (elt->valid) elt->private.dirty = LOCAL->dirty = T;
+}
+
+
+/* UNIX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long unix_ping (MAILSTREAM *stream)
+{
+ char lock[MAILTMPLEN];
+ struct stat sbuf;
+ /* big no-op if not readwrite */
+ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock) {
+ if (stream->rdonly) { /* does he want to give up readwrite? */
+ /* checkpoint if we changed something */
+ if (LOCAL->dirty) unix_check (stream);
+ flock (LOCAL->ld,LOCK_UN);/* release readwrite lock */
+ close (LOCAL->ld); /* close the readwrite lock file */
+ LOCAL->ld = -1; /* no more readwrite lock fd */
+ unlink (LOCAL->lname); /* delete the readwrite lock file */
+ }
+ else { /* get current mailbox size */
+ if (LOCAL->fd >= 0) fstat (LOCAL->fd,&sbuf);
+ else if (stat (stream->mailbox,&sbuf)) {
+ sprintf (LOCAL->buf,"Mailbox stat failed, aborted: %s",
+ strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ unix_abort (stream);
+ return NIL;
+ }
+ /* parse if mailbox changed */
+ if ((LOCAL->ddirty || (sbuf.st_size != LOCAL->filesize)) &&
+ unix_parse (stream,lock,LOCK_EX)) {
+ /* force checkpoint if double-dirty */
+ if (LOCAL->ddirty) unix_rewrite (stream,NIL,lock,NIL);
+ /* unlock mailbox */
+ else unix_unlock (LOCAL->fd,stream,lock);
+ mail_unlock (stream); /* and stream */
+ mm_nocritical (stream); /* done with critical */
+ }
+ }
+ }
+ return LOCAL ? LONGT : NIL; /* return if still alive */
+}
+
+/* UNIX mail check mailbox
+ * Accepts: MAIL stream
+ */
+
+void unix_check (MAILSTREAM *stream)
+{
+ char lock[MAILTMPLEN];
+ /* parse and lock mailbox */
+ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock &&
+ unix_parse (stream,lock,LOCK_EX)) {
+ /* any unsaved changes? */
+ if (LOCAL->dirty && unix_rewrite (stream,NIL,lock,NIL)) {
+ if (!stream->silent) mm_log ("Checkpoint completed",NIL);
+ }
+ /* no checkpoint needed, just unlock */
+ else unix_unlock (LOCAL->fd,stream,lock);
+ mail_unlock (stream); /* unlock the stream */
+ mm_nocritical (stream); /* done with critical */
+ }
+}
+
+
+/* UNIX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long unix_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ unsigned long i;
+ char lock[MAILTMPLEN];
+ char *msg = NIL;
+ /* parse and lock mailbox */
+ if (ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ LOCAL && (LOCAL->ld >= 0) && !stream->lock &&
+ unix_parse (stream,lock,LOCK_EX)) {
+ /* check expunged messages if not dirty */
+ for (i = 1; !LOCAL->dirty && (i <= stream->nmsgs); i++) {
+ MESSAGECACHE *elt = mail_elt (stream,i);
+ if (mail_elt (stream,i)->deleted) LOCAL->dirty = T;
+ }
+ if (!LOCAL->dirty) { /* not dirty and no expunged messages */
+ unix_unlock (LOCAL->fd,stream,lock);
+ msg = "No messages deleted, so no update needed";
+ }
+ else if (unix_rewrite (stream,&i,lock,sequence ? LONGT : NIL)) {
+ if (i) sprintf (msg = LOCAL->buf,"Expunged %lu messages",i);
+ else msg = "Mailbox checkpointed, but no messages expunged";
+ }
+ /* rewrite failed */
+ else unix_unlock (LOCAL->fd,stream,lock);
+ mail_unlock (stream); /* unlock the stream */
+ mm_nocritical (stream); /* done with critical */
+ if (msg && !stream->silent) mm_log (msg,NIL);
+ }
+ else if (!stream->silent) mm_log("Expunge ignored on readonly mailbox",WARN);
+ return ret;
+}
+
+/* UNIX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if copy successful, else NIL
+ */
+
+long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ int fd;
+ char *s,file[MAILTMPLEN],lock[MAILTMPLEN];
+ struct utimbuf times;
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ long ret = T;
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ copyuid_t cu = (copyuid_t) (mail_parameters (NIL,GET_USERHASNOLIFE,NIL) ?
+ NIL : mail_parameters (NIL,GET_COPYUID,NIL));
+ SEARCHSET *source = cu ? mail_newsearchset () : NIL;
+ SEARCHSET *dest = cu ? mail_newsearchset () : NIL;
+ MAILSTREAM *tstream = NIL;
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* make sure destination is valid */
+ if (!(unix_valid (mailbox) || !errno))
+ switch (errno) {
+ case ENOENT: /* no such file? */
+ if (compare_cstring (mailbox,"INBOX")) {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ }
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ unix_create (NIL,"INBOX");/* create empty INBOX */
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid UNIX-format mailbox name: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a UNIX-format mailbox: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+
+ /* try to open rewrite for UIDPLUS */
+ if ((tstream = mail_open_work (&unixdriver,NIL,mailbox,
+ OP_SILENT|OP_NOKOD)) && tstream->rdonly)
+ tstream = mail_close (tstream);
+ if (cu && !tstream) { /* wanted a COPYUID? */
+ sprintf (LOCAL->buf,"Unable to write-open mailbox for COPYUID: %.80s",
+ mailbox);
+ MM_LOG (LOCAL->buf,WARN);
+ cu = NIL; /* don't try to do COPYUID */
+ }
+ LOCAL->buf[0] = '\0';
+ mm_critical (stream); /* go critical */
+ if ((fd = unix_lock (dummy_file (file,mailbox),
+ O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE,
+ lock,LOCK_EX)) < 0) {
+ mm_nocritical (stream); /* done with critical */
+ sprintf (LOCAL->buf,"Can't open destination mailbox: %s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR); /* log the error */
+ return NIL; /* failed */
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ /* write all requested messages to mailbox */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size);
+ if (LOCAL->buf[(j = elt->private.special.text.size) - 2] != '\r') {
+ LOCAL->buf[j - 1] = '\r';
+ LOCAL->buf[j++] = '\n';
+ }
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ else { /* internal header succeeded */
+ s = unix_header (stream,i,&j,NIL);
+ /* header size, sans trailing newline */
+ if (j && (s[j - 4] == '\r')) j -= 2;
+ if (write (fd,s,j) < 0) ret = NIL;
+ else { /* message header succeeded */
+ j = tstream ? /* write UIDPLUS data if have readwrite */
+ unix_xstatus (stream,LOCAL->buf,elt,++(tstream->uid_last),LONGT) :
+ unix_xstatus (stream,LOCAL->buf,elt,NIL,NIL);
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ else { /* message status succeeded */
+ s = unix_text_work (stream,elt,&j,NIL);
+ if ((write (fd,s,j) < 0) || (write (fd,"\r\n",2) < 0))
+ ret = NIL;
+ else if (cu) { /* need to pass back new UID? */
+ mail_append_set (source,mail_uid (stream,i));
+ mail_append_set (dest,tstream->uid_last);
+ }
+ }
+ }
+ }
+ }
+
+ if (!ret || fsync (fd)) { /* force out the update */
+ sprintf (LOCAL->buf,"Message copy failed: %s",strerror (errno));
+ ftruncate (fd,sbuf.st_size);
+ ret = NIL;
+ }
+ /* force UIDVALIDITY assignment now */
+ if (tstream && !tstream->uid_validity)
+ tstream->uid_validity = (unsigned long) time (0);
+ /* return sets if doing COPYUID */
+ if (cu && ret) (*cu) (stream,mailbox,tstream->uid_validity,source,dest);
+ else { /* flush any sets we may have built */
+ mail_free_searchset (&source);
+ mail_free_searchset (&dest);
+ }
+ times.modtime = time (0); /* set mtime to now */
+ /* set atime to now-1 if successful copy */
+ if (ret) times.actime = times.modtime - 1;
+
+ else times.actime = /* else preserve \Marked status */
+ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ?
+ sbuf.st_atime : times.modtime;
+ utime (file,&times); /* set the times */
+ unix_unlock (fd,NIL,lock); /* unlock and close mailbox */
+ if (tstream) { /* update last UID if we can */
+ UNIXLOCAL * local = (UNIXLOCAL *) tstream->local;
+ local->dirty = T; /* do a rewrite */
+ local->appending = T; /* but not at the cost of marking as old */
+ tstream = mail_close (tstream);
+ }
+ /* log the error */
+ if (!ret) mm_log (LOCAL->buf,ERROR);
+ /* delete if requested message */
+ else if (options & CP_MOVE) for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence)
+ elt->deleted = elt->private.dirty = LOCAL->dirty = T;
+ mm_nocritical (stream); /* release critical */
+ return ret;
+}
+
+/* UNIX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+#define BUFLEN 8*MAILTMPLEN
+
+long unix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd;
+ unsigned long i;
+ char *flags,*date,buf[BUFLEN],tmp[MAILTMPLEN],file[MAILTMPLEN],
+ lock[MAILTMPLEN];
+ struct utimbuf times;
+ FILE *sf,*df;
+ MESSAGECACHE elt;
+ STRING *message;
+ unsigned long uidlocation = 0;
+ appenduid_t au = (appenduid_t)
+ (mail_parameters (NIL,GET_USERHASNOLIFE,NIL) ? NIL :
+ mail_parameters (NIL,GET_APPENDUID,NIL));
+ SEARCHSET *dst = au ? mail_newsearchset () : NIL;
+ long ret = LONGT;
+ MAILSTREAM *tstream = NIL;
+ if (!stream) { /* stream specified? */
+ stream = &unixproto; /* no, default stream to prototype */
+ for (i = 0; i < NUSERFLAGS && stream->user_flags[i]; ++i)
+ fs_give ((void **) &stream->user_flags[i]);
+ }
+ if (!unix_valid (mailbox)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (!compare_cstring (mailbox,"INBOX")) {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ unix_create (NIL,"INBOX"); /* create empty INBOX */
+ case 0: /* merely empty file? */
+ tstream = stream;
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid UNIX-format mailbox name: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a UNIX-format mailbox: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get sniffing stream for keywords */
+ else if (!(tstream = mail_open (NIL,mailbox,
+ OP_READONLY|OP_SILENT|OP_NOKOD|OP_SNIFF))) {
+ sprintf (tmp,"Unable to examine mailbox for APPEND: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+
+ /* get first message */
+ if (!(*af) (tstream,data,&flags,&date,&message)) return NIL;
+ if (!(sf = tmpfile ())) { /* must have scratch file */
+ sprintf (tmp,".%lx.%lx",(unsigned long) time (0),(unsigned long)getpid ());
+ if (!stat (tmp,&sbuf) || !(sf = fopen (tmp,"wb+"))) {
+ sprintf (tmp,"Unable to create scratch file: %.80s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ unlink (tmp);
+ }
+ do { /* parse date */
+ if (!date) rfc822_date (date = tmp);
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ mm_log (tmp,ERROR);
+ }
+ else { /* user wants to suppress time zones? */
+ if (mail_parameters (NIL,GET_NOTIMEZONES,NIL)) {
+ time_t when = mail_longdate (&elt);
+ date = ctime (&when); /* use traditional date */
+ }
+ /* use POSIX-style date */
+ else date = mail_cdate (tmp,&elt);
+ if (!SIZE (message)) mm_log ("Append of zero-length message",ERROR);
+ else if (!unix_collect_msg (tstream,sf,flags,date,message)) {
+ sprintf (tmp,"Error writing scratch file: %.80s",strerror (errno));
+ mm_log (tmp,ERROR);
+ }
+ /* get next message */
+ else if ((*af) (tstream,data,&flags,&date,&message)) continue;
+ }
+ fclose (sf); /* punt scratch file */
+ return NIL; /* give up */
+ } while (message); /* until no more messages */
+ if (fflush (sf)) {
+ sprintf (tmp,"Error finishing scratch file: %.80s",strerror (errno));
+ mm_log (tmp,ERROR);
+ fclose (sf); /* punt scratch file */
+ return NIL; /* give up */
+ }
+ i = ftell (sf); /* size of scratch file */
+
+ /* close sniffing stream */
+ if (tstream != stream) tstream = mail_close (tstream);
+ mm_critical (stream); /* go critical */
+ /* try to open readwrite for UIDPLUS */
+ if ((tstream = mail_open_work (&unixdriver,NIL,mailbox,
+ OP_SILENT|OP_NOKOD)) && tstream->rdonly)
+ tstream = mail_close (tstream);
+ if (au && !tstream) { /* wanted an APPENDUID? */
+ sprintf (tmp,"Unable to re-open mailbox for APPENDUID: %.80s",mailbox);
+ MM_LOG (tmp,WARN);
+ au = NIL;
+ }
+ if (((fd = unix_lock (dummy_file (file,mailbox),
+ O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE,
+ lock,LOCK_EX)) < 0) || !(df = fdopen (fd,"ab"))) {
+ mm_nocritical (stream); /* done with critical */
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ rewind (sf);
+ times.modtime = time (0); /* set mtime to now */
+ /* write all messages */
+ if (!unix_append_msgs (tstream,sf,df,au ? dst : NIL) ||
+ (fflush (df) == EOF) || fsync (fd)) {
+ sprintf (buf,"Message append failed: %s",strerror (errno));
+ mm_log (buf,ERROR);
+ ftruncate (fd,sbuf.st_size);/* revert file */
+ times.actime = /* preserve \Marked status */
+ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ?
+ sbuf.st_atime : times.modtime;
+ ret = NIL; /* return error */
+ }
+ /* set atime to now-1 if successful copy */
+ else times.actime = times.modtime - 1;
+ utime (file,&times); /* set the times */
+ fclose (sf); /* done with scratch file */
+ /* force UIDVALIDITY assignment now */
+ if (tstream && !tstream->uid_validity)
+ tstream->uid_validity = (unsigned long) time (0);
+ /* return sets if doing APPENDUID */
+ if (au && ret) (*au) (mailbox,tstream->uid_validity,dst);
+ else mail_free_searchset (&dst);
+ flock (fd,LOCK_UN); /* unlock mailbox (can't use unix_unlock() */
+ if (lock && *lock) unlink (lock);
+ fclose (df); /* close mailbox */
+ if (tstream) { /* update last UID if we can */
+ UNIXLOCAL * local = (UNIXLOCAL *) tstream->local;
+ local->dirty = T; /* do a rewrite */
+ local->appending = T; /* but not at the cost of marking as old */
+ tstream = mail_close (tstream);
+ }
+ mm_nocritical (stream); /* release critical */
+ return ret;
+}
+
+/* Collect and write single message to append scratch file
+ * Accepts: MAIL stream
+ * scratch file
+ * flags
+ * date
+ * message stringstruct
+ * Returns: NIL if write error, else T
+ */
+
+int unix_collect_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
+ STRING *msg)
+{
+ unsigned char *s,*t;
+ unsigned long uf;
+ long f = mail_parse_flags (stream,flags,&uf);
+ /* write metadata */
+ if (fprintf (sf,"%ld %lu ",f,SIZE (msg) + 2) < 0) return NIL;
+ for (s = date; *s; *s++) switch (*s) {
+ default:
+ if (putc (*s,sf) == EOF) return NIL;
+ case '\r': case '\n':
+ break;
+ }
+ if (fputs ("\r\n",sf) == EOF) return NIL;
+ while (uf) /* write user flags */
+ if ((s = stream->user_flags[find_rightmost_bit (&uf)]) &&
+ (fprintf (sf," %s",s) < 0)) return NIL;
+ if (fputs ("\r\n",sf) == EOF) return NIL;
+ while (SIZE (msg)) { /* copy text to scratch file */
+ for (s = (unsigned char *) msg->curpos, t = s + msg->cursize; s < t; ++s)
+ if (!*s) *s = 0x80; /* disallow NUL */
+ /* write buffered text */
+ if (fwrite (msg->curpos,1,msg->cursize,sf) == msg->cursize)
+ SETPOS (msg,GETPOS (msg) + msg->cursize);
+ else return NIL; /* failed */
+ }
+ /* write trailing CRLF and return */
+ return (fputs ("\r\n",sf) == EOF) ? NIL : T;
+}
+
+/* Append messages from scratch file to mailbox
+ * Accepts: MAIL stream
+ * source file
+ * destination file
+ * uidset to update if non-NIL
+ * Returns: T if success, NIL if failure
+ */
+
+int unix_append_msgs (MAILSTREAM *stream,FILE *sf,FILE *df,SEARCHSET *set)
+{
+ int ti,zn,c;
+ long f;
+ unsigned long i,j;
+ char *x,tmp[MAILTMPLEN];
+ int hdrp = T;
+ /* get message metadata line */
+ while (fgets (tmp,MAILTMPLEN,sf)) {
+ if (!(isdigit (tmp[0]) && strchr (tmp,'\n'))) return NIL;
+ f = strtol (tmp,&x,10); /* get flags */
+ if (!((*x++ == ' ') && isdigit (*x))) return NIL;
+ i = strtoul (x,&x,10); /* get message size */
+ if ((*x++ != ' ') || /* build initial header */
+ (fprintf (df,"From %s@%s %sStatus: ",myusername(),mylocalhost(),x)<0)||
+ (f&fSEEN && (putc ('R',df) == EOF)) ||
+ (fputs ("\r\nX-Status: ",df) == EOF) ||
+ (f&fDELETED && (putc ('D',df) == EOF)) ||
+ (f&fFLAGGED && (putc ('F',df) == EOF)) ||
+ (f&fANSWERED && (putc ('A',df) == EOF)) ||
+ (f&fDRAFT && (putc ('T',df) == EOF)) ||
+ (fputs ("\r\nX-Keywords:",df) == EOF)) return NIL;
+ /* copy keywords */
+ while ((c = getc (sf)) != '\n') switch (c) {
+ case EOF:
+ return NIL;
+ default:
+ if (putc (c,df) == EOF) return NIL;
+ }
+ if ((putc ('\n',df) == EOF) ||
+ (set && (fprintf (df,"X-UID: %lu\r\n",++(stream->uid_last)) < 0)))
+ return NIL;
+
+ for (c = '\n'; i && fgets (tmp,MAILTMPLEN,sf); c = tmp[j-1]) {
+ /* get read line length */
+ if (i < (j = strlen (tmp))) fatal ("unix_append_msgs overrun");
+ i -= j; /* number of bytes left */
+ if (!j) continue; /* do nothing if line emptied */
+ /* complete line? */
+ if ((c == '\n')) switch (tmp[0]) {
+ case 'F': /* possible "From " (case counts here) */
+ if ((j > 4) && (tmp[0] == 'F') && (tmp[1] == 'r') && (tmp[2] == 'o') &&
+ (tmp[3] == 'm') && (tmp[4] == ' ')) {
+ if (!unix_fromwidget) {
+ VALID (tmp,x,ti,zn);/* conditional, only write widget if */
+ if (!ti) break; /* it looks like a valid header */
+ } /* write the widget */
+ if (putc ('>',df) == EOF) return NIL;
+ }
+ break;
+ case 'S': case 's': /* possible "Status:" */
+ if (hdrp && (j > 6) && ((tmp[1] == 't') || (tmp[1] == 'T')) &&
+ ((tmp[2] == 'a') || (tmp[2] == 'A')) &&
+ ((tmp[3] == 't') || (tmp[3] == 'T')) &&
+ ((tmp[4] == 'u') || (tmp[4] == 'U')) &&
+ ((tmp[5] == 's') || (tmp[5] == 'S')) && (tmp[6] == ':') &&
+ (fputs ("X-Original-",df) == EOF)) return NIL;
+ break;
+ case 'X': case 'x': /* possible X-??? header */
+ if (hdrp && (tmp[1] == '-') &&
+ /* possible X-UID: */
+ (((j > 5) && ((tmp[2] == 'U') || (tmp[2] == 'u')) &&
+ ((tmp[3] == 'I') || (tmp[3] == 'i')) &&
+ ((tmp[4] == 'D') || (tmp[4] == 'd')) && (tmp[5] == ':')) ||
+ /* possible X-IMAP: */
+ ((j > 6) && ((tmp[2] == 'I') || (tmp[2] == 'i')) &&
+ ((tmp[3] == 'M') || (tmp[3] == 'm')) &&
+ ((tmp[4] == 'A') || (tmp[4] == 'a')) &&
+ ((tmp[5] == 'P') || (tmp[5] == 'p')) &&
+ ((tmp[6] == ':') ||
+ /* or X-IMAPbase: */
+ ((j > 10) && ((tmp[6] == 'b') || (tmp[6] == 'B')) &&
+ ((tmp[7] == 'a') || (tmp[7] == 'A')) &&
+ ((tmp[8] == 's') || (tmp[8] == 'S')) &&
+ ((tmp[9] == 'e') || (tmp[9] == 'E')) && (tmp[10] == ':')))) ||
+ /* possible X-Status: */
+ ((j > 8) && ((tmp[2] == 'S') || (tmp[2] == 's')) &&
+ ((tmp[3] == 't') || (tmp[3] == 'T')) &&
+ ((tmp[4] == 'a') || (tmp[4] == 'A')) &&
+ ((tmp[5] == 't') || (tmp[5] == 'T')) &&
+ ((tmp[6] == 'u') || (tmp[6] == 'U')) &&
+ ((tmp[7] == 's') || (tmp[7] == 'S')) && (tmp[8] == ':')) ||
+ /* possible X-Keywords: */
+ ((j > 10) && ((tmp[2] == 'K') || (tmp[2] == 'k')) &&
+ ((tmp[3] == 'e') || (tmp[3] == 'E')) &&
+ ((tmp[4] == 'y') || (tmp[4] == 'Y')) &&
+ ((tmp[5] == 'w') || (tmp[5] == 'W')) &&
+ ((tmp[6] == 'o') || (tmp[6] == 'O')) &&
+ ((tmp[7] == 'r') || (tmp[7] == 'R')) &&
+ ((tmp[8] == 'd') || (tmp[8] == 'D')) &&
+ ((tmp[9] == 's') || (tmp[9] == 'S')) && (tmp[10] == ':'))) &&
+ (fputs ("X-Original-",df) == EOF)) return NIL;
+ break;
+ case '\n': /* blank line */
+ hdrp = NIL;
+ break;
+ default: /* nothing to do */
+ break;
+ }
+ /* just write the line */
+ if (fwrite (tmp,1,j,df) != j) return NIL;
+ }
+ if (i) return NIL; /* didn't read entire message */
+ /* update set */
+ if (stream) mail_append_set (set,stream->uid_last);
+ }
+ return T;
+}
+
+/* Internal routines */
+
+
+/* UNIX mail abort stream
+ * Accepts: MAIL stream
+ */
+
+void unix_abort (MAILSTREAM *stream)
+{
+ if (LOCAL) { /* only if a file is open */
+ if (LOCAL->fd >= 0) close (LOCAL->fd);
+ if (LOCAL->ld >= 0) { /* have a mailbox lock? */
+ flock (LOCAL->ld,LOCK_UN);/* yes, release the lock */
+ close (LOCAL->ld); /* close the lock file */
+ unlink (LOCAL->lname); /* and delete it */
+ }
+ if (LOCAL->lname) fs_give ((void **) &LOCAL->lname);
+ /* free local text buffers */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data);
+ if (LOCAL->linebuf) fs_give ((void **) &LOCAL->linebuf);
+ if (LOCAL->line) fs_give ((void **) &LOCAL->line);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* UNIX open and lock mailbox
+ * Accepts: file name to open/lock
+ * file open mode
+ * destination buffer for lock file name
+ * type of locking operation (LOCK_SH or LOCK_EX)
+ */
+
+int unix_lock (char *file,int flags,int mode,char *lock,int op)
+{
+ int fd,ld,j;
+ int i = LOCKTIMEOUT * 60 - 1;
+ char tmp[MAILTMPLEN];
+ time_t t;
+ struct stat sb;
+ sprintf (lock,"%s.lock",file);/* build lock filename */
+ do { /* until OK or out of tries */
+ t = time (0); /* get the time now */
+ /* try to get the lock */
+ if ((ld = open(lock,O_BINARY|O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE))>=0)
+ close (ld); /* got it, close the lock file! */
+ else if (errno != EEXIST) { /* miscellaneous error */
+ sprintf (tmp,"Error creating %.80s: %s",lock,strerror (errno));
+ if (!(i%15)) mm_log (tmp,WARN);
+ }
+ /* lock exists, still active? */
+ else if (!stat (lock,&sb) && (t > sb.st_ctime + LOCKTIMEOUT * 60) &&
+ ((ld = open(lock,O_BINARY|O_WRONLY|O_CREAT,S_IREAD|S_IWRITE))>=0))
+ close (ld); /* got timed-out lock file */
+ else { /* active lock, try again */
+ if (!(i%15)) {
+ sprintf (tmp,"Mailbox %.80s is locked, will override in %d seconds...",
+ file,i);
+ mm_log (tmp,WARN);
+ }
+ sleep (1); /* wait a second before next retry */
+ }
+ } while (*lock && ld < 0 && i--);
+ /* open file */
+ if ((fd = open (file,flags,mode)) >= 0) flock (fd,op);
+ else { /* open failed */
+ j = errno; /* preserve error code */
+ if (*lock) unlink (lock); /* flush the lock file if any */
+ errno = j; /* restore error code */
+ }
+ return fd;
+}
+
+/* UNIX unlock and close mailbox
+ * Accepts: file descriptor
+ * (optional) mailbox stream to check atime/mtime
+ * (optional) lock file name
+ */
+
+void unix_unlock (int fd,MAILSTREAM *stream,char *lock)
+{
+ if (stream) { /* need to muck with times? */
+ struct stat sbuf;
+ struct utimbuf times;
+ time_t now = time (0);
+ fstat (fd,&sbuf); /* get file times */
+ if (LOCAL->ld >= 0) { /* yes, readwrite session? */
+ times.actime = now; /* set atime to now */
+ /* set mtime to (now - 1) if necessary */
+ times.modtime = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1;
+ }
+ else if (stream->recent) { /* readonly with recent messages */
+ if ((sbuf.st_atime >= sbuf.st_mtime) ||
+ (sbuf.st_atime >= sbuf.st_ctime))
+ /* keep past mtime, whack back atime */
+ times.actime = (times.modtime = (sbuf.st_mtime < now) ?
+ sbuf.st_mtime : now) - 1;
+ else now = 0; /* no time change needed */
+ }
+ /* readonly with no recent messages */
+ else if ((sbuf.st_atime < sbuf.st_mtime) ||
+ (sbuf.st_atime < sbuf.st_ctime)) {
+ times.actime = now; /* set atime to now */
+ /* set mtime to (now - 1) if necessary */
+ times.modtime = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1;
+ }
+ else now = 0; /* no time change needed */
+ /* set the times, note change */
+ if (now && !utime (stream->mailbox,&times))
+ LOCAL->filetime = times.modtime;
+ }
+ flock (fd,LOCK_UN); /* release flock'ers */
+ if (!stream) close (fd); /* close the file if no stream */
+ /* flush the lock file if any */
+ if (lock && *lock) unlink (lock);
+}
+
+/* UNIX mail parse and lock mailbox
+ * Accepts: MAIL stream
+ * space to write lock file name
+ * type of locking operation
+ * Returns: T if parse OK, critical & mailbox is locked shared; NIL if failure
+ */
+
+int unix_parse (MAILSTREAM *stream,char *lock,int op)
+{
+ int zn;
+ unsigned long i,j,k,m;
+ unsigned char c,*s,*t,*u,tmp[MAILTMPLEN],date[30];
+ int ti = 0,retain = T;
+ unsigned long nmsgs = stream->nmsgs;
+ unsigned long prevuid = nmsgs ? mail_elt (stream,nmsgs)->private.uid : 0;
+ unsigned long recent = stream->recent;
+ unsigned long oldnmsgs = stream->nmsgs;
+ short silent = stream->silent;
+ short pseudoseen = NIL;
+ struct stat sbuf;
+ STRING bs;
+ FDDATA d;
+ MESSAGECACHE *elt;
+ mail_lock (stream); /* guard against recursion or pingers */
+ /* toss out previous descriptor */
+ if (LOCAL->fd >= 0) close (LOCAL->fd);
+ mm_critical (stream); /* open and lock mailbox (shared OK) */
+ if ((LOCAL->fd = unix_lock (stream->mailbox,
+ O_BINARY + ((LOCAL->ld >= 0) ? O_RDWR:O_RDONLY),
+ NIL,lock,op)) < 0) {
+ sprintf (tmp,"Mailbox open failed, aborted: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ unix_abort (stream);
+ mail_unlock (stream);
+ mm_nocritical (stream); /* done with critical */
+ return NIL;
+ }
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ /* validate change in size */
+ if (sbuf.st_size < LOCAL->filesize) {
+ sprintf (tmp,"Mailbox shrank from %lu to %lu bytes, aborted",
+ (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size);
+ mm_log (tmp,ERROR); /* this is pretty bad */
+ unix_unlock (LOCAL->fd,stream,lock);
+ unix_abort (stream);
+ mail_unlock (stream);
+ mm_nocritical (stream); /* done with critical */
+ return NIL;
+ }
+
+ /* new data? */
+ else if (i = sbuf.st_size - LOCAL->filesize) {
+ d.fd = LOCAL->fd; /* yes, set up file descriptor */
+ d.pos = LOCAL->filesize; /* get to that position in the file */
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE; /* file chunk size */
+ INIT (&bs,fd_string,&d,i); /* initialize stringstruct */
+ /* skip leading whitespace for broken MTAs */
+ while (((c = CHR (&bs)) == '\n') || (c == '\r') ||
+ (c == ' ') || (c == '\t')) SNX (&bs);
+ if (SIZE (&bs)) { /* read new data */
+ /* remember internal header position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ s = unix_mbxline (stream,&bs,&i);
+ t = NIL,zn = 0;
+ if (i) VALID (s,t,ti,zn); /* see if valid From line */
+ if (!ti) { /* someone pulled the rug from under us */
+ sprintf (tmp,"Unexpected changes to mailbox (try restarting): %.20s",
+ (char *) s);
+ mm_log (tmp,ERROR);
+ unix_unlock (LOCAL->fd,stream,lock);
+ unix_abort (stream);
+ mail_unlock (stream);
+ mm_nocritical (stream); /* done with critical */
+ return NIL;
+ }
+ stream->silent = T; /* quell main program new message events */
+ do { /* found a message */
+ /* instantiate first new message */
+ mail_exists (stream,++nmsgs);
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ recent++; /* assume recent by default */
+ elt->recent = T;
+ /* note position/size of internal header */
+ elt->private.special.offset = j;
+ elt->private.msg.header.offset = elt->private.special.text.size = i;
+
+ /* generate plausible IMAPish date string */
+ date[2] = date[6] = date[20] = '-'; date[11] = ' ';
+ date[14] = date[17] = ':';
+ /* dd */
+ date[0] = t[ti - 2]; date[1] = t[ti - 1];
+ /* mmm */
+ date[3] = t[ti - 6]; date[4] = t[ti - 5]; date[5] = t[ti - 4];
+ /* hh */
+ date[12] = t[ti + 1]; date[13] = t[ti + 2];
+ /* mm */
+ date[15] = t[ti + 4]; date[16] = t[ti + 5];
+ if (t[ti += 6] == ':') {/* ss */
+ date[18] = t[++ti]; date[19] = t[++ti];
+ ti++; /* move to space */
+ }
+ else date[18] = date[19] = '0';
+ /* yy -- advance over timezone if necessary */
+ if (zn == ti) ti += (((t[zn+1] == '+') || (t[zn+1] == '-')) ? 6 : 4);
+ date[7] = t[ti + 1]; date[8] = t[ti + 2];
+ date[9] = t[ti + 3]; date[10] = t[ti + 4];
+ /* zzz */
+ t = zn ? (t + zn + 1) : (unsigned char *) "LCL";
+ date[21] = *t++; date[22] = *t++; date[23] = *t++;
+ if ((date[21] != '+') && (date[21] != '-')) date[24] = '\0';
+ else { /* numeric time zone */
+ date[24] = *t++; date[25] = *t++;
+ date[26] = '\0'; date[20] = ' ';
+ }
+ /* set internal date */
+ if (!mail_parse_date (elt,date)) {
+ sprintf (tmp,"Unable to parse internal date: %s",(char *) date);
+ mm_log (tmp,WARN);
+ }
+
+ do { /* look for message body */
+ s = t = unix_mbxline (stream,&bs,&i);
+ if (i) switch (*s) { /* check header lines */
+ case 'X': /* possible X-???: line */
+ if (s[1] == '-') { /* must be immediately followed by hyphen */
+ /* X-Status: becomes Status: in S case */
+ if (s[2] == 'S' && s[3] == 't' && s[4] == 'a' && s[5] == 't' &&
+ s[6] == 'u' && s[7] == 's' && s[8] == ':') s += 2;
+ /* possible X-Keywords */
+ else if (s[2] == 'K' && s[3] == 'e' && s[4] == 'y' &&
+ s[5] == 'w' && s[6] == 'o' && s[7] == 'r' &&
+ s[8] == 'd' && s[9] == 's' && s[10] == ':') {
+ SIZEDTEXT uf;
+ retain = NIL; /* don't retain continuation */
+ s += 11; /* flush leading whitespace */
+ while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n'))){
+ while (*s == ' ') s++;
+ /* find end of keyword */
+ if (!(u = strpbrk (s," \n\r"))) u = s + strlen (s);
+ /* got a keyword? */
+ if ((k = (u - s)) && (k <= MAXUSERFLAG)) {
+ uf.data = (unsigned char *) s;
+ uf.size = k;
+ for (j = 0; (j < NUSERFLAGS) && stream->user_flags[j]; ++j)
+ if (!compare_csizedtext (stream->user_flags[j],&uf)) {
+ elt->user_flags |= ((long) 1) << j;
+ break;
+ }
+ }
+ s = u; /* advance to next keyword */
+ }
+ break;
+ }
+
+ /* possible X-IMAP */
+ else if ((s[2] == 'I') && (s[3] == 'M') && (s[4] == 'A') &&
+ (s[5] == 'P') && ((m = (s[6] == ':')) ||
+ ((s[6] == 'b') && (s[7] == 'a') &&
+ (s[8] == 's') && (s[9] == 'e') &&
+ (s[10] == ':')))) {
+ retain = NIL; /* don't retain continuation */
+ if ((nmsgs == 1) && !stream->uid_validity) {
+ /* advance to data */
+ s += m ? 7 : 11;
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ j = 0; /* slurp UID validity */
+ /* found a digit? */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ /* must have valid UID validity and UID last */
+ if (j && isdigit (*s)) {
+ /* pseudo-header seen if X-IMAP */
+ if (m) pseudoseen = LOCAL->pseudo = T;
+ /* save UID validity */
+ stream->uid_validity = j;
+ j = 0; /* slurp UID last */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* save UID last */
+ stream->uid_last = j;
+ /* process keywords */
+ for (j = 0; (*s != '\n') && ((*s != '\r')||(s[1] != '\n'));
+ s = u,j++) {
+ /* flush leading whitespace */
+ while (*s == ' ') s++;
+ u = strpbrk (s," \n\r");
+ /* got a keyword? */
+ if ((j < NUSERFLAGS) && (k = (u - s)) &&
+ (k <= MAXUSERFLAG)) {
+ if (stream->user_flags[j])
+ fs_give ((void **) &stream->user_flags[j]);
+ stream->user_flags[j] = (char *) fs_get (k + 1);
+ strncpy (stream->user_flags[j],s,k);
+ stream->user_flags[j][k] = '\0';
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ /* possible X-UID */
+ else if (s[2] == 'U' && s[3] == 'I' && s[4] == 'D' &&
+ s[5] == ':') {
+ retain = NIL; /* don't retain continuation */
+ /* only believe if have a UID validity */
+ if (stream->uid_validity && ((nmsgs > 1) || !pseudoseen)) {
+ s += 6; /* advance to UID value */
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ j = 0;
+ /* found a digit? */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* flush remainder of line */
+ while (*s != '\n') s++;
+ /* make sure not duplicated */
+ if (elt->private.uid)
+ sprintf (tmp,"Message %lu UID %lu already has UID %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,elt->private.uid);
+ /* make sure UID doesn't go backwards */
+ else if (j <= prevuid)
+ sprintf (tmp,"Message %lu UID %lu less than %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,prevuid + 1);
+#if 0 /* this is currently broken by UIDPLUS */
+ /* or skip by mailbox's recorded last */
+ else if (j > stream->uid_last)
+ sprintf (tmp,"Message %lu UID %lu greater than last %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,stream->uid_last);
+#endif
+ else { /* normal UID case */
+ prevuid = elt->private.uid = j;
+#if 1 /* temporary kludge for UIDPLUS */
+ if (prevuid > stream->uid_last) {
+ stream->uid_last = prevuid;
+ LOCAL->ddirty = LOCAL->dirty = T;
+ }
+#endif
+ break; /* exit this cruft */
+ }
+ mm_log (tmp,WARN);
+ /* invalidate UID validity */
+ stream->uid_validity = 0;
+ elt->private.uid = 0;
+ }
+ break;
+ }
+ }
+ /* otherwise fall into S case */
+
+ case 'S': /* possible Status: line */
+ if (s[0] == 'S' && s[1] == 't' && s[2] == 'a' && s[3] == 't' &&
+ s[4] == 'u' && s[5] == 's' && s[6] == ':') {
+ retain = NIL; /* don't retain continuation */
+ s += 6; /* advance to status flags */
+ do switch (*s++) {/* parse flags */
+ case 'R': /* message read */
+ elt->seen = T;
+ break;
+ case 'O': /* message old */
+ if (elt->recent) {
+ elt->recent = NIL;
+ recent--; /* it really wasn't recent */
+ }
+ break;
+ case 'D': /* message deleted */
+ elt->deleted = T;
+ break;
+ case 'F': /* message flagged */
+ elt->flagged = T;
+ break;
+ case 'A': /* message answered */
+ elt->answered = T;
+ break;
+ case 'T': /* message is a draft */
+ elt->draft = T;
+ break;
+ default: /* some other crap */
+ break;
+ } while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n')));
+ break; /* all done */
+ }
+ /* otherwise fall into default case */
+
+ default: /* ordinary header line */
+ if ((*s == 'S') || (*s == 's') ||
+ (((*s == 'X') || (*s == 'x')) && (s[1] == '-'))) {
+ unsigned char *e,*v;
+ /* must match what mail_filter() does */
+ for (u = s,v = tmp,e = u + min (i,MAILTMPLEN - 1);
+ (u < e) && ((c = (*u ? *u : (*u = ' '))) != ':') &&
+ ((c > ' ') || ((c != ' ') && (c != '\t') &&
+ (c != '\r') && (c != '\n')));
+ *v++ = *u++);
+ *v = '\0'; /* tie off */
+ /* matches internal header? */
+ if (!compare_cstring (tmp,"STATUS") ||
+ !compare_cstring (tmp,"X-STATUS") ||
+ !compare_cstring (tmp,"X-KEYWORDS") ||
+ !compare_cstring (tmp,"X-UID") ||
+ !compare_cstring (tmp,"X-IMAP") ||
+ !compare_cstring (tmp,"X-IMAPBASE")) {
+ char err[MAILTMPLEN];
+ sprintf (err,"Discarding bogus %s header in message %lu",
+ (char *) tmp,elt->msgno);
+ mm_log (err,WARN);
+ retain = NIL; /* don't retain continuation */
+ break; /* different case or something */
+ }
+ }
+ /* retain or non-continuation? */
+ if (retain || ((*s != ' ') && (*s != '\t'))) {
+ retain = T; /* retaining continuation now */
+ /* line length in CRLF format newline */
+ k = i + (((i < 2) || (s[i - 2] != '\r')) ? 1 : 0);
+ /* header size */
+ elt->rfc822_size = elt->private.spare.data += k;
+ }
+ else {
+ char err[MAILTMPLEN];
+ sprintf (err,"Discarding bogus continuation in msg %lu: %.80s",
+ elt->msgno,(char *) s);
+ if (u = strpbrk (err,"\r\n")) *u = '\0';
+ mm_log (err,WARN);
+ break; /* different case or something */
+ }
+ break;
+ }
+ } while (i && (*t != '\n') && ((*t != '\r') || (t[1] != '\n')));
+ /* "internal" header sans trailing newline */
+ if (i) elt->private.spare.data -= 2;
+ /* assign a UID if none found */
+ if (((nmsgs > 1) || !pseudoseen) && !elt->private.uid) {
+ prevuid = elt->private.uid = ++stream->uid_last;
+ elt->private.dirty = T;
+ LOCAL->ddirty = T; /* force update */
+ }
+ else elt->private.dirty = elt->recent;
+
+ /* note size of header, location of text */
+ elt->private.msg.header.text.size =
+ (elt->private.msg.text.offset =
+ (LOCAL->filesize + GETPOS (&bs)) - elt->private.special.offset) -
+ elt->private.special.text.size;
+ k = m = 0; /* no previous line size yet */
+ /* note current position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ if (i) do { /* look for next message */
+ s = unix_mbxline (stream,&bs,&i);
+ if (i) { /* got new data? */
+ VALID (s,t,ti,zn); /* yes, parse line */
+ if (!ti) { /* not a header line, add it to message */
+ if (s[i - 1] == '\n')
+ elt->rfc822_size +=
+ k = i + (m = (((i < 2) || s[i - 2] != '\r') ? 1 : 0));
+ else { /* file does not end with newline! */
+ elt->rfc822_size += i;
+ k = m = 0;
+ }
+ /* update current position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ }
+ }
+ } while (i && !ti); /* until found a header */
+ elt->private.msg.text.text.size = j -
+ (elt->private.special.offset + elt->private.msg.text.offset);
+ if (k == 2) { /* last line was blank? */
+ elt->private.msg.text.text.size -= (m ? 1 : 2);
+ elt->rfc822_size -= 2;
+ }
+ /* until end of buffer */
+ } while (!stream->sniff && i);
+ if (pseudoseen) { /* flush pseudo-message if present */
+ /* decrement recent count */
+ if (mail_elt (stream,1)->recent) recent--;
+ /* and the exists count */
+ mail_exists (stream,nmsgs--);
+ mail_expunged(stream,1);/* fake an expunge of that message */
+ }
+ /* need to start a new UID validity? */
+ if (!stream->uid_validity) {
+ stream->uid_validity = (unsigned long) time (0);
+ if (nmsgs) { /* don't bother if empty file */
+ /* make dirty to restart UID epoch */
+ LOCAL->ddirty = LOCAL->dirty = T;
+ /* need to rewrite msg 1 if not pseudo */
+ if (!LOCAL->pseudo) mail_elt (stream,1)->private.dirty = T;
+ mm_log ("Assigning new unique identifiers to all messages",NIL);
+ }
+ }
+ stream->nmsgs = oldnmsgs; /* whack it back down */
+ stream->silent = silent; /* restore old silent setting */
+ /* notify upper level of new mailbox sizes */
+ mail_exists (stream,nmsgs);
+ mail_recent (stream,recent);
+ /* mark dirty so O flags are set */
+ if (recent) LOCAL->dirty = T;
+ }
+ }
+ /* no change, don't babble if never got time */
+ else if (LOCAL->filetime && LOCAL->filetime != sbuf.st_mtime)
+ mm_log ("New mailbox modification time but apparently no changes",WARN);
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ LOCAL->filetime = sbuf.st_mtime;
+ return T; /* return the winnage */
+}
+
+/* UNIX read line from mailbox
+ * Accepts: mail stream
+ * stringstruct
+ * pointer to line size
+ * Returns: pointer to input line
+ */
+
+char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size)
+{
+ unsigned long i,j,k,m;
+ char *s,*t,*te;
+ char *ret = "";
+ /* flush old buffer */
+ if (LOCAL->line) fs_give ((void **) &LOCAL->line);
+ /* if buffer needs refreshing */
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ if (SIZE (bs)) { /* find newline */
+ /* end of fast scan */
+ te = (t = (s = bs->curpos) + bs->cursize) - 12;
+ while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) {
+ --s; /* back up */
+ break; /* exit loop */
+ }
+ /* final character-at-a-time scan */
+ while ((s < t) && (*s != '\n')) ++s;
+ /* difficult case if line spans buffer */
+ if ((i = s - bs->curpos) == bs->cursize) {
+ /* have space in line buffer? */
+ if (i > LOCAL->linebuflen) {
+ fs_give ((void **) &LOCAL->linebuf);
+ LOCAL->linebuf = (char *) fs_get (LOCAL->linebuflen = i);
+ }
+ /* remember what we have so far */
+ memcpy (LOCAL->linebuf,bs->curpos,i);
+ /* load next buffer */
+ SETPOS (bs,k = GETPOS (bs) + i);
+ /* end of fast scan */
+ te = (t = (s = bs->curpos) + bs->cursize) - 12;
+ /* fast scan in overlap buffer */
+ while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) {
+ --s; /* back up */
+ break; /* exit loop */
+ }
+
+ /* final character-at-a-time scan */
+ while ((s < t) && (*s != '\n')) ++s;
+ /* huge line? */
+ if ((j = s - bs->curpos) == bs->cursize) {
+ SETPOS (bs,GETPOS (bs) + j);
+ /* look for end of line (s-l-o-w!!) */
+ for (m = SIZE (bs); m && (SNX (bs) != '\n'); --m,++j);
+ SETPOS (bs,k); /* go back to where it started */
+ }
+ /* got size of data, make buffer for return */
+ ret = LOCAL->line = (char *) fs_get (i + j + 2);
+ /* copy first chunk */
+ memcpy (ret,LOCAL->linebuf,i);
+ while (j) { /* copy remainder */
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ memcpy (ret + i,bs->curpos,k = min (j,bs->cursize));
+ i += k; /* account for this much read in */
+ j -= k;
+ bs->curpos += k; /* increment new position */
+ bs->cursize -= k; /* eat that many bytes */
+ }
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ /* read newline at end */
+ if (SIZE (bs)) ret[i++] = SNX (bs);
+ ret[i] = '\0'; /* makes debugging easier */
+ }
+ else { /* this is easy */
+ ret = bs->curpos; /* string it at this position */
+ bs->curpos += ++i; /* increment new position */
+ bs->cursize -= i; /* eat that many bytes */
+ }
+ *size = i; /* return that to user */
+ }
+ else *size = 0; /* end of data, return empty */
+ return ret;
+}
+
+/* UNIX make pseudo-header
+ * Accepts: MAIL stream
+ * buffer to write pseudo-header
+ * Returns: length of pseudo-header
+ */
+
+unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr)
+{
+ int i;
+ char *s,*t,tmp[MAILTMPLEN];
+ time_t now = time(0);
+ rfc822_fixed_date (tmp);
+ sprintf (hdr,"From %s %.24s\r\nDate: %s\r\nFrom: %s <%s@%.80s>\r\nSubject: %s\r\nMessage-ID: <%lu@%.80s>\r\nX-IMAP: %010ld %010ld",
+ pseudo_from,ctime (&now),
+ tmp,pseudo_name,pseudo_from,mylocalhost (),pseudo_subject,
+ (unsigned long) now,mylocalhost (),stream->uid_validity,
+ stream->uid_last);
+ for (t = hdr + strlen (hdr),i = 0; i < NUSERFLAGS; ++i)
+ if (stream->user_flags[i])
+ sprintf (t += strlen (t)," %s",stream->user_flags[i]);
+ strcpy (t += strlen (t),"\r\nStatus: RO\r\n\r\n");
+ for (s = pseudo_msg,t += strlen (t); *s; *t++ = *s++)
+ if (*s == '\n') *t++ = '\r';
+ *t++ = '\r'; *t++ = '\n'; *t++ = '\r'; *t++ = '\n';
+ *t = '\0'; /* tie off pseudo header */
+ return t - hdr; /* return length of pseudo header */
+}
+
+/* UNIX make status string
+ * Accepts: MAIL stream
+ * destination string to write
+ * message cache entry
+ * UID to write if non-zero (else use elt->private.uid)
+ * non-zero flag to write UID (.LT. 0 to write UID base info too)
+ * Returns: length of string
+ */
+
+unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt,
+ unsigned long uid,long flag)
+{
+ char *t,stack[64];
+ char *s = status;
+ unsigned long n;
+ unsigned long pad = 50;
+ /* This used to use sprintf(), but thanks to certain cretinous C libraries
+ with horribly slow implementations of sprintf() I had to change it to this
+ mess. At least it should be fast. */
+ *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't'; *s++ = 'u'; *s++ = 's';
+ *s++ = ':'; *s++ = ' ';
+ if (elt->seen) *s++ = 'R';
+ /* only write O if have a UID */
+ if (flag && (!elt->recent || LOCAL->appending)) *s++ = 'O';
+ *s++ = '\r'; *s++ = '\n';
+ *s++ = 'X'; *s++ = '-'; *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't';
+ *s++ = 'u'; *s++ = 's'; *s++ = ':'; *s++ = ' ';
+ if (elt->deleted) *s++ = 'D';
+ if (elt->flagged) *s++ = 'F';
+ if (elt->answered) *s++ = 'A';
+ if (elt->draft) *s++ = 'T';
+ *s++ = '\r'; *s++ = '\n';
+
+ *s++ = 'X'; *s++ = '-'; *s++ = 'K'; *s++ = 'e'; *s++ = 'y'; *s++ = 'w';
+ *s++ = 'o'; *s++ = 'r'; *s++ = 'd'; *s++ = 's'; *s++ = ':';
+ if (n = elt->user_flags) do {
+ *s++ = ' ';
+ for (t = stream->user_flags[find_rightmost_bit (&n)]; *t; *s++ = *t++);
+ } while (n);
+ n = s - status; /* get size of stuff so far */
+ /* pad X-Keywords to make size constant */
+ if (n < pad) for (n = pad - n; n > 0; --n) *s++ = ' ';
+ *s++ = '\r'; *s++ = '\n';
+ if (flag) { /* want to include UID? */
+ t = stack;
+ /* push UID digits on the stack */
+ n = uid ? uid : elt->private.uid;
+ do *t++ = (char) (n % 10) + '0';
+ while (n /= 10);
+ *s++ = 'X'; *s++ = '-'; *s++ = 'U'; *s++ = 'I'; *s++ = 'D'; *s++ = ':';
+ *s++ = ' ';
+ /* pop UID from stack */
+ while (t > stack) *s++ = *--t;
+ *s++ = '\r'; *s++ = '\n';
+ }
+ /* end of extended message status */
+ *s++ = '\r'; *s++ = '\n'; *s = '\0';
+ return s - status; /* return size of resulting string */
+}
+
+/* Rewrite mailbox file
+ * Accepts: MAIL stream, must be critical and locked
+ * return pointer to number of expunged messages if want expunge
+ * lock file name
+ * expunge sequence, not deleted flag
+ * Returns: T if success and mailbox unlocked, NIL if failure
+ */
+
+#define OVERFLOWBUFLEN 8192 /* initial overflow buffer length */
+
+long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,char *lock,
+ long flags)
+{
+ MESSAGECACHE *elt;
+ UNIXFILE f;
+ char *s;
+ struct utimbuf times;
+ long ret,flag;
+ unsigned long i,j;
+ unsigned long recent = stream->recent;
+ unsigned long size = LOCAL->pseudo ? unix_pseudo (stream,LOCAL->buf) : 0;
+ if (nexp) *nexp = 0; /* initially nothing expunged */
+ /* calculate size of mailbox after rewrite */
+ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs; i++) {
+ elt = mail_elt (stream,i); /* get cache */
+ if (!(nexp && elt->deleted && (flags ? elt->sequence : T))) {
+ /* add RFC822 size of this message */
+ size += elt->private.special.text.size + elt->private.spare.data +
+ unix_xstatus (stream,LOCAL->buf,elt,NIL,flag) +
+ elt->private.msg.text.text.size + 2;
+ flag = 1; /* only count X-IMAPbase once */
+ }
+ }
+ if (!size) { /* no messages and no pseudo, make one now */
+ size = unix_pseudo (stream,LOCAL->buf);
+ LOCAL->pseudo = T;
+ }
+ /* extend the file as necessary */
+ if (ret = unix_extend (stream,size)) {
+ /* Set up buffered I/O file structure
+ * curpos current position being written through buffering
+ * filepos current position being written physically to the disk
+ * bufpos current position being written in the buffer
+ * protect current maximum position that can be written to the disk
+ * before buffering is forced
+ * The code tries to buffer so that that disk is written in multiples of
+ * OVERBLOWBUFLEN bytes.
+ */
+ f.stream = stream; /* note mail stream */
+ f.curpos = f.filepos = 0; /* start of file */
+ f.protect = stream->nmsgs ? /* initial protection pointer */
+ mail_elt (stream,1)->private.special.offset : 8192;
+ f.bufpos = f.buf = (char *) fs_get (f.buflen = OVERFLOWBUFLEN);
+
+ if (LOCAL->pseudo) /* update pseudo-header */
+ unix_write (&f,LOCAL->buf,unix_pseudo (stream,LOCAL->buf));
+ /* loop through all messages */
+ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs;) {
+ elt = mail_elt (stream,i);/* get cache */
+ /* expunge this message? */
+ if (nexp && elt->deleted && (flags ? elt->sequence : T)) {
+ /* one less recent message */
+ if (elt->recent) --recent;
+ mail_expunged(stream,i);/* notify upper levels */
+ ++*nexp; /* count up one more expunged message */
+ }
+ else { /* preserve this message */
+ i++; /* advance to next message */
+ if ((flag < 0) || /* need to rewrite message? */
+ elt->private.dirty ||
+ (((unsigned long) f.curpos) != elt->private.special.offset) ||
+ (elt->private.msg.header.text.size !=
+ (elt->private.spare.data +
+ unix_xstatus (stream,LOCAL->buf,elt,NIL,flag)))) {
+ unsigned long newoffset = f.curpos;
+ /* yes, seek to internal header */
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size);
+ /* protection pointer moves to RFC822 header */
+ f.protect = elt->private.special.offset +
+ elt->private.msg.header.offset;
+ /* write internal header */
+ unix_write (&f,LOCAL->buf,elt->private.special.text.size);
+ /* get RFC822 header */
+ s = unix_header (stream,elt->msgno,&j,NIL);
+ /* in case this got decremented */
+ elt->private.msg.header.offset = elt->private.special.text.size;
+ /* header size, sans trailing newline */
+ if ((j < 4) || (s[j - 4] == '\r')) j -= 2;
+ if (j != elt->private.spare.data) fatal ("header size inconsistent");
+ /* protection pointer moves to RFC822 text */
+ f.protect = elt->private.special.offset +
+ elt->private.msg.text.offset;
+ unix_write (&f,s,j); /* write RFC822 header */
+ /* write status and UID */
+ unix_write (&f,LOCAL->buf,
+ j = unix_xstatus (stream,LOCAL->buf,elt,NIL,flag));
+ flag = 1; /* only write X-IMAPbase once */
+ /* new file header size */
+ elt->private.msg.header.text.size = elt->private.spare.data + j;
+
+ /* did text move? */
+ if (f.curpos != f.protect) {
+ /* get message text */
+ s = unix_text_work (stream,elt,&j,FT_INTERNAL);
+ /* can't happen it says here */
+ if (j > elt->private.msg.text.text.size)
+ fatal ("text size inconsistent");
+ /* new text offset, status/UID may change it */
+ elt->private.msg.text.offset = f.curpos - newoffset;
+ /* protection pointer moves to next message */
+ f.protect = (i <= stream->nmsgs) ?
+ mail_elt (stream,i)->private.special.offset : (f.curpos + j + 2);
+ unix_write (&f,s,j);/* write text */
+ /* write trailing newline */
+ unix_write (&f,"\r\n",2);
+ }
+ else { /* tie off header and status */
+ unix_write (&f,NIL,NIL);
+ /* protection pointer moves to next message */
+ f.protect = (i <= stream->nmsgs) ?
+ mail_elt (stream,i)->private.special.offset : size;
+ /* locate end of message text */
+ j = f.filepos + elt->private.msg.text.text.size;
+ /* trailing newline already there? */
+ if (f.protect == (off_t) (j + 2)) f.curpos = f.filepos = f.protect;
+ else { /* trailing newline missing, write it */
+ f.curpos = f.filepos = j;
+ unix_write (&f,"\r\n",2);
+ }
+ }
+ /* new internal header offset */
+ elt->private.special.offset = newoffset;
+ elt->private.dirty =NIL;/* message is now clean */
+ }
+ else { /* no need to rewrite this message */
+ /* tie off previous message if needed */
+ unix_write (&f,NIL,NIL);
+ /* protection pointer moves to next message */
+ f.protect = (i <= stream->nmsgs) ?
+ mail_elt (stream,i)->private.special.offset : size;
+ /* locate end of message text */
+ j = f.filepos + elt->private.special.text.size +
+ elt->private.msg.header.text.size +
+ elt->private.msg.text.text.size;
+ /* trailing newline already there? */
+ if (f.protect == (off_t) (j + 2)) f.curpos = f.filepos = f.protect;
+ else { /* trailing newline missing, write it */
+ f.curpos = f.filepos = j;
+ unix_write (&f,"\r\n",2);
+ }
+ }
+ }
+ }
+
+ unix_write (&f,NIL,NIL); /* tie off final message */
+ if (size != ((unsigned long) f.filepos)) fatal ("file size inconsistent");
+ fs_give ((void **) &f.buf); /* free buffer */
+ /* make sure tied off */
+ ftruncate (LOCAL->fd,LOCAL->filesize = size);
+ fsync (LOCAL->fd); /* make sure the updates take */
+ if (size && (flag < 0)) fatal ("lost UID base information");
+ /* no longer dirty */
+ LOCAL->ddirty = LOCAL->dirty = NIL;
+ /* notify upper level of new mailbox sizes */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ /* set atime to now, mtime a second earlier */
+ times.modtime = (times.actime = time (0)) -1;
+ /* set the times, note change */
+ if (!utime (stream->mailbox,&times)) LOCAL->filetime = times.modtime;
+ /* flush the lock file */
+ unix_unlock (LOCAL->fd,stream,lock);
+ }
+ return ret; /* return state from algorithm */
+}
+
+/* Extend UNIX mailbox file
+ * Accepts: MAIL stream
+ * new desired size
+ * Return: T if success, else NIL
+ */
+
+long unix_extend (MAILSTREAM *stream,unsigned long size)
+{
+ unsigned long i = (size > ((unsigned long) LOCAL->filesize)) ?
+ size - ((unsigned long) LOCAL->filesize) : 0;
+ if (i) { /* does the mailbox need to grow? */
+ if (i > LOCAL->buflen) { /* make sure have enough space */
+ /* this user won the lottery all right */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1);
+ }
+ memset (LOCAL->buf,'\0',i); /* get a block of nulls */
+ while (T) { /* until write successful or punt */
+ lseek (LOCAL->fd,LOCAL->filesize,L_SET);
+ if ((write (LOCAL->fd,LOCAL->buf,i) >= 0) && !fsync (LOCAL->fd)) break;
+ else {
+ long e = errno; /* note error before doing ftruncate */
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ if (mm_diskerror (stream,e,NIL)) {
+ fsync (LOCAL->fd); /* user chose to punt */
+ sprintf (LOCAL->buf,"Unable to extend mailbox: %s",strerror (e));
+ if (!stream->silent) mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ }
+ }
+ }
+ return LONGT;
+}
+
+/* Write data to buffered file
+ * Accepts: buffered file pointer
+ * file data or NIL to indicate "flush buffer"
+ * date size (ignored for "flush buffer")
+ * Does not return until success
+ */
+
+void unix_write (UNIXFILE *f,char *buf,unsigned long size)
+{
+ unsigned long i,j,k;
+ if (buf) { /* doing buffered write? */
+ i = f->bufpos - f->buf; /* yes, get size of current buffer data */
+ /* yes, have space in current buffer chunk? */
+ if (j = i ? ((f->buflen - i) % OVERFLOWBUFLEN) : f->buflen) {
+ /* yes, fill up buffer as much as we can */
+ memcpy (f->bufpos,buf,k = min (j,size));
+ f->bufpos += k; /* new buffer position */
+ f->curpos += k; /* new current position */
+ if (j -= k) return; /* all done if still have buffer free space */
+ buf += k; /* full, get new unwritten data pointer */
+ size -= k; /* new data size */
+ i += k; /* new buffer data size */
+ }
+ /* This chunk of the buffer is full. See if can make some space by
+ * writing to the disk, if there's enough unprotected space to do so.
+ * Try to fill out any unaligned chunk, along with any subsequent full
+ * chunks that will fit in unprotected space.
+ */
+ /* any unprotected space we can write to? */
+ if (j = min (i,f->protect - f->filepos)) {
+ /* yes, filepos not at chunk boundary? */
+ if ((k = f->filepos % OVERFLOWBUFLEN) && ((k = OVERFLOWBUFLEN - k) < j))
+ j -= k; /* yes, and can write out partial chunk */
+ else k = 0; /* no partial chunk to write */
+ /* if at least a chunk free, write that too */
+ if (j > OVERFLOWBUFLEN) k += j - (j % OVERFLOWBUFLEN);
+ if (k) { /* write data if there is anything we can */
+ unix_phys_write (f,f->buf,k);
+ /* slide buffer */
+ if (i -= k) memmove (f->buf,f->buf + k,i);
+ f->bufpos = f->buf + i; /* new end of buffer */
+ }
+ }
+
+ /* Have flushed the buffer as best as possible. All done if no more
+ * data to write. Otherwise, if the buffer is empty AND if the unwritten
+ * data is larger than a chunk AND the unprotected space is also larger
+ * than a chunk, then write as many chunks as we can directly from the
+ * data. Buffer the rest, expanding the buffer as needed.
+ */
+ if (size) { /* have more data that we need to buffer? */
+ /* can write any of it to disk instead? */
+ if ((f->bufpos == f->buf) &&
+ ((j = min (f->protect - f->filepos,size)) > OVERFLOWBUFLEN)) {
+ /* write as much as we can right now */
+ unix_phys_write (f,buf,j -= (j % OVERFLOWBUFLEN));
+ buf += j; /* new data pointer */
+ size -= j; /* new data size */
+ f->curpos += j; /* advance current pointer */
+ }
+ if (size) { /* still have data that we need to buffer? */
+ /* yes, need to expand the buffer? */
+ if ((i = ((f->bufpos + size) - f->buf)) > f->buflen) {
+ /* note current position in buffer */
+ j = f->bufpos - f->buf;
+ i += OVERFLOWBUFLEN; /* yes, grow another chunk */
+ fs_resize ((void **) &f->buf,f->buflen = i - (i % OVERFLOWBUFLEN));
+ /* in case buffer relocated */
+ f->bufpos = f->buf + j;
+ }
+ /* buffer remaining data */
+ memcpy (f->bufpos,buf,size);
+ f->bufpos += size; /* new end of buffer */
+ f->curpos += size; /* advance current pointer */
+ }
+ }
+ }
+ else { /* flush buffer to disk */
+ unix_phys_write (f,f->buf,i = f->bufpos - f->buf);
+ f->bufpos = f->buf; /* reset buffer */
+ /* update positions */
+ f->curpos = f->protect = f->filepos;
+ }
+}
+
+/* Physical disk write
+ * Accepts: buffered file pointer
+ * buffer address
+ * buffer size
+ * Does not return until success
+ */
+
+void unix_phys_write (UNIXFILE *f,char *buf,size_t size)
+{
+ MAILSTREAM *stream = f->stream;
+ /* write data at desired position */
+ while (size && ((lseek (LOCAL->fd,f->filepos,L_SET) < 0) ||
+ (write (LOCAL->fd,buf,size) < 0))) {
+ int e;
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Unable to write to mailbox: %s",strerror (e = errno));
+ mm_log (tmp,ERROR);
+ mm_diskerror (NIL,e,T); /* serious problem, must retry */
+ }
+ f->filepos += size; /* update file position */
+}
diff --git a/imap/src/osdep/os2/unixnt.h b/imap/src/osdep/os2/unixnt.h
new file mode 100644
index 00000000..1305184f
--- /dev/null
+++ b/imap/src/osdep/os2/unixnt.h
@@ -0,0 +1,161 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 20 December 1989
+ * Last Edited: 30 August 2006
+ */
+
+
+/* DEDICATION
+ *
+ * This file is dedicated to my dog, Unix, also known as Yun-chan and
+ * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix
+ * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after
+ * a two-month bout with cirrhosis of the liver.
+ *
+ * He was a dear friend, and I miss him terribly.
+ *
+ * Lift a leg, Yunie. Luv ya forever!!!!
+ */
+
+/* Validate line
+ * Accepts: pointer to candidate string to validate as a From header
+ * return pointer to end of date/time field
+ * return pointer to offset from t of time (hours of ``mmm dd hh:mm'')
+ * return pointer to offset from t of time zone (if non-zero)
+ * Returns: t,ti,zn set if valid From string, else ti is NIL
+ */
+
+#define VALID(s,x,ti,zn) { \
+ ti = 0; \
+ if ((*s == 'F') && (s[1] == 'r') && (s[2] == 'o') && (s[3] == 'm') && \
+ (s[4] == ' ')) { \
+ for (x = s + 5; *x && *x != '\012'; x++); \
+ if (*x) { \
+ if (x[-1] == '\015') --x; \
+ if (x - s >= 41) { \
+ for (zn = -1; x[zn] != ' '; zn--); \
+ if ((x[zn-1] == 'm') && (x[zn-2] == 'o') && (x[zn-3] == 'r') && \
+ (x[zn-4] == 'f') && (x[zn-5] == ' ') && (x[zn-6] == 'e') && \
+ (x[zn-7] == 't') && (x[zn-8] == 'o') && (x[zn-9] == 'm') && \
+ (x[zn-10] == 'e') && (x[zn-11] == 'r') && (x[zn-12] == ' '))\
+ x += zn - 12; \
+ } \
+ if (x - s >= 27) { \
+ if (x[-5] == ' ') { \
+ if (x[-8] == ':') zn = 0,ti = -5; \
+ else if (x[-9] == ' ') ti = zn = -9; \
+ else if ((x[-11] == ' ') && ((x[-10]=='+') || (x[-10]=='-'))) \
+ ti = zn = -11; \
+ } \
+ else if (x[-4] == ' ') { \
+ if (x[-9] == ' ') zn = -4,ti = -9; \
+ } \
+ else if (x[-6] == ' ') { \
+ if ((x[-11] == ' ') && ((x[-5] == '+') || (x[-5] == '-'))) \
+ zn = -6,ti = -11; \
+ } \
+ if (ti && !((x[ti - 3] == ':') && \
+ (x[ti -= ((x[ti - 6] == ':') ? 9 : 6)] == ' ') && \
+ (x[ti - 3] == ' ') && (x[ti - 7] == ' ') && \
+ (x[ti - 11] == ' '))) ti = 0; \
+ } \
+ } \
+ } \
+}
+
+/* You are not expected to understand this macro, but read the next page if
+ * you are not faint of heart.
+ *
+ * Known formats to the VALID macro are:
+ * From user Wed Dec 2 05:53 1992
+ * BSD From user Wed Dec 2 05:53:22 1992
+ * SysV From user Wed Dec 2 05:53 PST 1992
+ * rn From user Wed Dec 2 05:53:22 PST 1992
+ * From user Wed Dec 2 05:53 -0700 1992
+ * emacs From user Wed Dec 2 05:53:22 -0700 1992
+ * From user Wed Dec 2 05:53 1992 PST
+ * From user Wed Dec 2 05:53:22 1992 PST
+ * From user Wed Dec 2 05:53 1992 -0700
+ * Solaris From user Wed Dec 2 05:53:22 1992 -0700
+ *
+ * Plus all of the above with `` remote from xxx'' after it. Thank you very
+ * much, smail and Solaris, for making my life considerably more complicated.
+ */
+
+/*
+ * What? You want to understand the VALID macro anyway? Alright, since you
+ * insist. Actually, it isn't really all that difficult, provided that you
+ * take it step by step.
+ *
+ * Line 1 Initializes the return ti value to failure (0);
+ * Lines 2-3 Validates that the 1st-5th characters are ``From ''.
+ * Lines 4-6 Validates that there is an end of line and points x at it.
+ * Lines 7-14 First checks to see if the line is at least 41 characters long.
+ * If so, it scans backwards to find the rightmost space. From
+ * that point, it scans backwards to see if the string matches
+ * `` remote from''. If so, it sets x to point to the space at
+ * the start of the string.
+ * Line 15 Makes sure that there are at least 27 characters in the line.
+ * Lines 16-21 Checks if the date/time ends with the year (there is a space
+ * five characters back). If there is a colon three characters
+ * further back, there is no timezone field, so zn is set to 0
+ * and ti is set in front of the year. Otherwise, there must
+ * either to be a space four characters back for a three-letter
+ * timezone, or a space six characters back followed by a + or -
+ * for a numeric timezone; in either case, zn and ti become the
+ * offset of the space immediately before it.
+ * Lines 22-24 Are the failure case for line 14. If there is a space four
+ * characters back, it is a three-letter timezone; there must be a
+ * space for the year nine characters back. zn is the zone
+ * offset; ti is the offset of the space.
+ * Lines 25-28 Are the failure case for line 20. If there is a space six
+ * characters back, it is a numeric timezone; there must be a
+ * space eleven characters back and a + or - five characters back.
+ * zn is the zone offset; ti is the offset of the space.
+ * Line 29-32 If ti is valid, make sure that the string before ti is of the
+ * form www mmm dd hh:mm or www mmm dd hh:mm:ss, otherwise
+ * invalidate ti. There must be a colon three characters back
+ * and a space six or nine characters back (depending upon
+ * whether or not the character six characters back is a colon).
+ * There must be a space three characters further back (in front
+ * of the day), one seven characters back (in front of the month),
+ * and one eleven characters back (in front of the day of week).
+ * ti is set to be the offset of the space before the time.
+ *
+ * Why a macro? It gets invoked a *lot* in a tight loop. On some of the
+ * newer pipelined machines it is faster being open-coded than it would be if
+ * subroutines are called.
+ *
+ * Why does it scan backwards from the end of the line, instead of doing the
+ * much easier forward scan? There is no deterministic way to parse the
+ * ``user'' field, because it may contain unquoted spaces! Yes, I tested it to
+ * see if unquoted spaces were possible. They are, and I've encountered enough
+ * evil mail to be totally unwilling to trust that ``it will never happen''.
+ */
+
+/* Build parameters */
+
+#define KODRETRY 15 /* kiss-of-death retry in seconds */
+#define LOCKTIMEOUT 5 /* lock timeout in minutes */
diff --git a/imap/src/osdep/os2/write.c b/imap/src/osdep/os2/write.c
new file mode 100644
index 00000000..c7854815
--- /dev/null
+++ b/imap/src/osdep/os2/write.c
@@ -0,0 +1,59 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Write data, treating partial writes as an error
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 May 1995
+ * Last Edited: 30 August 2006
+ */
+
+/* The whole purpose of this unfortunate routine is to deal with DOS and
+ * certain cretinous versions of UNIX which decided that the "bytes actually
+ * written" return value from write() gave them license to use that for things
+ * that are really errors, such as disk quota exceeded, maximum file size
+ * exceeded, disk full, etc.
+ *
+ * BSD won't screw us this way on the local filesystem, but who knows what
+ * some NFS-mounted filesystem will do.
+ */
+
+#undef write
+
+/* Write data to file
+ * Accepts: file descriptor
+ * I/O vector structure
+ * number of vectors in structure
+ * Returns: number of bytes written if successful, -1 if failure
+ */
+
+long maxposint = (long)((((unsigned long) 1) << ((sizeof(int) * 8) - 1)) - 1);
+
+long safe_write (int fd,char *buf,long nbytes)
+{
+ long i,j;
+ if (nbytes > 0) for (i = nbytes; i; i -= j,buf += j) {
+ while (((j = write (fd,buf,(int) min (maxposint,i))) < 0) &&
+ (errno == EINTR));
+ if (j < 0) return j;
+ }
+ return nbytes;
+}
diff --git a/imap/src/osdep/tops-20/build.ctl b/imap/src/osdep/tops-20/build.ctl
new file mode 100644
index 00000000..45a13450
--- /dev/null
+++ b/imap/src/osdep/tops-20/build.ctl
@@ -0,0 +1,63 @@
+! ========================================================================
+! Copyright 1988-2006 University of Washington
+!
+! Licensed under the Apache License, Version 2.0 (the "License");
+! you may not use this file except in compliance with the License.
+! You may obtain a copy of the License at
+!
+! http://www.apache.org/licenses/LICENSE-2.0
+!
+!
+! ========================================================================
+
+! Program: Portable C client build for TOPS-20
+!
+! Author: Mark Crispin
+! Networks and Distributed Computing
+! Computing & Communications
+! University of Washington
+! Administration Building, AG-44
+! Seattle, WA 98195
+! Internet: MRC@CAC.Washington.EDU
+!
+! Date: 11 May 1989
+! Last Edited: 30 August 2006
+
+!
+! Set environment
+@DEFINE SYS: PS:<KCC-6>,SYS:
+!
+! Build c-client library
+!
+@COPY OS_T20.* OSDEP.*
+@CCX -c -m -w=note MAIL.C DUMMYT20.C IMAP4R1.C SMTP.C NNTP.C POP3.C RFC822.C MISC.C OSDEP.C FLSTRING.C SMANAGER.C NEWSRC.C NETMSG.C UTF8.C UTF8AUX.C
+!
+! Build c-client library file. Should use MAKLIB, but MAKLIB barfs on MAIL.REL
+!
+@DELETE CCLINT.REL
+@APPEND MAIL.REL,DUMMYT20.REL,IMAP4R1.REL,SMTP.REL,NNTP.REL,POP3.REL,RFC822.REL,MISC.REL,OSDEP.REL,FLSTRING.REL,SMANAGER.REL,NEWSRC.REL,NETMSG.REL,UTF8.REL,UTF8AUX.REL CCLINT.REL
+!
+! Build MTEST library test program
+!
+@CCX -c -m -w=note MTEST.C
+@LINK
+*/SET:.HIGH.:200000
+*C:LIBCKX.REL
+*MTEST.REL
+*CCLINT.REL
+*MTEST/SAVE
+*/GO
+!
+! Build MAILUTIL
+!
+@CCX -c -m -w=note MAILUTIL.C
+@RENAME MAILUTIL.REL MUTIL.REL
+@LINK
+*/SET:.HIGH.:200000
+*C:LIBCKX.REL
+*MUTIL.REL
+*CCLINT.REL
+*MUTIL/SAVE
+*/GO
+@RESET
+@RENAME MUTIL.EXE MAILUTIL.EXE
diff --git a/imap/src/osdep/tops-20/dummy.h b/imap/src/osdep/tops-20/dummy.h
new file mode 100644
index 00000000..32650e06
--- /dev/null
+++ b/imap/src/osdep/tops-20/dummy.h
@@ -0,0 +1,43 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 9 May 1991
+ * Last Edited: 30 August 2006
+ */
+
+/* Exported function prototypes */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat);
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long scan_contents (DRIVER *dtb,char *name,char *contents,
+ unsigned long csiz,unsigned long fsiz);
+long dummy_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz);
+long dummy_create (MAILSTREAM *stream,char *mailbox);
+long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode);
+long dummy_delete (MAILSTREAM *stream,char *mailbox);
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname);
+char *dummy_file (char *dst,char *name);
+long dummy_canonicalize (char *tmp,char *ref,char *pat);
diff --git a/imap/src/osdep/tops-20/dummyt20.c b/imap/src/osdep/tops-20/dummyt20.c
new file mode 100644
index 00000000..1117ad45
--- /dev/null
+++ b/imap/src/osdep/tops-20/dummyt20.c
@@ -0,0 +1,294 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines for TOPS-20
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 13 June 1995
+ * Last Edited: 30 August 2006
+ */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include "mail.h"
+#include "osdep.h"
+#include "dummy.h"
+#include "misc.h"
+
+/* Function prototypes */
+
+DRIVER *dummy_valid (char *name);
+void *dummy_parameters (long function,void *value);
+MAILSTREAM *dummy_open (MAILSTREAM *stream);
+void dummy_close (MAILSTREAM *stream,long options);
+long dummy_ping (MAILSTREAM *stream);
+void dummy_check (MAILSTREAM *stream);
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options);
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+/* Dummy routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER dummydriver = {
+ "dummy", /* driver name */
+ DR_LOCAL|DR_MAIL, /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ dummy_valid, /* mailbox is valid for us */
+ dummy_parameters, /* manipulate parameters */
+ dummy_scan, /* scan mailboxes */
+ dummy_list, /* list mailboxes */
+ dummy_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ dummy_create, /* create mailbox */
+ dummy_delete, /* delete mailbox */
+ dummy_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ dummy_open, /* open mailbox */
+ dummy_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message structure */
+ NIL, /* fetch header */
+ NIL, /* fetch text */
+ NIL, /* fetch message data */
+ NIL, /* unique identifier */
+ NIL, /* message number from UID */
+ NIL, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ dummy_ping, /* ping mailbox to see if still alive */
+ dummy_check, /* check for new messages */
+ dummy_expunge, /* expunge deleted messages */
+ dummy_copy, /* copy messages to another mailbox */
+ dummy_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+
+ /* prototype stream */
+MAILSTREAM dummyproto = {&dummydriver};
+
+/* Dummy validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *dummy_valid (char *name)
+{
+ /* must be valid local mailbox */
+ return (name && *name && (*name != '{') && !compare_cstring (name,"INBOX")) ?
+ &dummydriver : NIL;
+}
+
+
+/* Dummy manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *dummy_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+/* Dummy scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ /* return silently */
+}
+
+
+/* Dummy list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ /* return silently */
+}
+
+
+/* Dummy list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ /* return silently */
+}
+
+/* Dummy create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * driver type to use
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_create (MAILSTREAM *stream,char *mailbox)
+{
+ return NIL; /* always fails */
+}
+
+
+/* Dummy delete mailbox
+ * Accepts: mail stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return NIL; /* always fails */
+}
+
+
+/* Mail rename mailbox
+ * Accepts: mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ return NIL; /* always fails */
+}
+
+/* Dummy open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *dummy_open (MAILSTREAM *stream)
+{
+ char tmp[MAILTMPLEN];
+ /* OP_PROTOTYPE call or silence */
+ if (!stream || stream->silent) return NIL;
+ if (compare_cstring (stream->mailbox,"INBOX")) {
+ sprintf (tmp,"Not a mailbox: %s",stream->mailbox);
+ mm_log (tmp,ERROR);
+ return NIL; /* always fails */
+ }
+ if (!stream->silent) { /* only if silence not requested */
+ mail_exists (stream,0); /* say there are 0 messages */
+ mail_recent (stream,0);
+ stream->uid_validity = time (0);
+ }
+ stream->inbox = T; /* note that it's an INBOX */
+ return stream; /* return success */
+}
+
+
+/* Dummy close
+ * Accepts: MAIL stream
+ * options
+ */
+
+void dummy_close (MAILSTREAM *stream,long options)
+{
+ /* return silently */
+}
+
+/* Dummy ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+long dummy_ping (MAILSTREAM *stream)
+{
+ return T;
+}
+
+
+/* Dummy check mailbox
+ * Accepts: MAIL stream
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+void dummy_check (MAILSTREAM *stream)
+{
+ dummy_ping (stream); /* invoke ping */
+}
+
+
+/* Dummy expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ return LONGT;
+}
+
+/* Dummy copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * options
+ * Returns: T if copy successful, else NIL
+ */
+
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) fatal ("Impossible dummy_copy");
+ return NIL;
+}
+
+
+/* Dummy append message string
+ * Accepts: mail stream
+ * destination mailbox
+ * append callback function
+ * data for callback
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Can't append to %s",mailbox);
+ mm_log (tmp,ERROR); /* pass up error */
+ return NIL; /* always fails */
+}
diff --git a/imap/src/osdep/tops-20/env_t20.c b/imap/src/osdep/tops-20/env_t20.c
new file mode 100644
index 00000000..38403454
--- /dev/null
+++ b/imap/src/osdep/tops-20/env_t20.c
@@ -0,0 +1,226 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Environment routines -- TOPS-20 version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Dedication:
+ * This file is dedicated with affection to the TOPS-20 operating system, which
+ * set standards for user and programmer friendliness that have still not been
+ * equaled by more `modern' operating systems.
+ * Wasureru mon ka!!!!
+ */
+
+/* c-client environment parameters */
+
+static char *myUserName = NIL; /* user name */
+static char *myHomeDir = NIL; /* home directory name */
+static char *myLocalHost = NIL; /* local host name */
+static char *myNewsrc = NIL; /* newsrc file name */
+static short no822tztext = NIL; /* disable RFC [2]822 timezone text */
+
+
+#include "pmatch.c" /* include wildcard pattern matcher */
+
+/* Environment manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *env_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_USERNAME:
+ if (myUserName) fs_give ((void **) &myUserName);
+ myUserName = cpystr ((char *) value);
+ case GET_USERNAME:
+ ret = (void *) myUserName;
+ break;
+ case SET_HOMEDIR:
+ if (myHomeDir) fs_give ((void **) &myHomeDir);
+ myHomeDir = cpystr ((char *) value);
+ case GET_HOMEDIR:
+ ret = (void *) myHomeDir;
+ break;
+ case SET_LOCALHOST:
+ if (myLocalHost) fs_give ((void **) &myLocalHost);
+ myLocalHost = cpystr ((char *) value);
+ case GET_LOCALHOST:
+ ret = (void *) myLocalHost;
+ break;
+ case SET_NEWSRC:
+ if (myNewsrc) fs_give ((void **) &myNewsrc);
+ myNewsrc = cpystr ((char *) value);
+ case GET_NEWSRC:
+ ret = (void *) myNewsrc;
+ break;
+ case SET_DISABLE822TZTEXT:
+ no822tztext = value ? T : NIL;
+ case GET_DISABLE822TZTEXT:
+ ret = (void *) (no822tztext ? VOIDT : NIL);
+ break;
+ }
+ return ret;
+}
+
+/* Write current time in RFC 822 format
+ * Accepts: destination string
+ */
+
+void rfc822_date (char *date)
+{
+ char *s;
+ int argblk[4];
+ argblk[1] = (int) (date-1);
+ argblk[2] = -1; /* time now */
+ argblk[3] = OT_822; /* want RFC [2]822 format */
+ jsys (ODTIM,argblk);
+ /* suppress time zone text if desired */
+ if (no822tztext && (s = strstr (date," ("))) *s = NIL;
+}
+
+
+/* Write current time in internal format
+ * Accepts: destination string
+ */
+
+void internal_date (char *date)
+{
+ int argblk[5];
+ argblk[1] = (int) (date-1);
+ argblk[2] = -1; /* time now */
+ argblk[3] = OT_4YR; /* output in 4-digit year format */
+ jsys (ODTIM,argblk);
+ argblk[2] = ' '; /* delimit with space */
+ jsys (BOUT,argblk);
+ argblk[2] = -1; /* time now */
+ argblk[4] = 0; /* no flags */
+ jsys (ODCNV,argblk); /* get time zone */
+ argblk[2] = ((argblk[4] & 077000000) >> 18) * -100;
+ /* add an hour if summer time */
+ if (argblk[4] & IC_ADS) argblk[2] += 100;
+ argblk[3] = 0340005000012;
+ jsys (NOUT,argblk);
+}
+
+/* Return my user name
+ * Accepts: pointer to optional flags
+ * Returns: my user name
+ */
+
+char *myusername_full (unsigned long *flags)
+{
+ if (!myUserName) { /* get user name if don't have it yet */
+ char tmp[MAILTMPLEN];
+ int argblk[5],i;
+ jsys (GJINF,argblk); /* get job poop */
+ if (!(i = argblk[1])) { /* remember user number */
+ if (flags) *flags = MU_NOTLOGGEDIN;
+ return "SYSTEM"; /* not logged in */
+ }
+ argblk[1] = (int) (tmp-1); /* destination */
+ argblk[2] = i; /* user number */
+ jsys (DIRST,argblk); /* get user name string */
+ myUserName = cpystr (tmp); /* copy user name */
+ argblk[1] = 0; /* no flags */
+ argblk[2] = i; /* user number */
+ argblk[3] = 0; /* no stepping */
+ jsys (RCDIR,argblk); /* get home directory */
+ argblk[1] = (int) (tmp-1); /* destination */
+ argblk[2] = argblk[3]; /* home directory number */
+ jsys (DIRST,argblk); /* get home directory string */
+ myHomeDir = cpystr (tmp); /* copy home directory */
+ if (!myNewsrc) { /* set news file name if not defined */
+ sprintf (tmp,"%sNEWSRC",myhomedir ());
+ myNewsrc = cpystr (tmp);
+ }
+ if (flags) *flags = MU_LOGGEDIN;
+ }
+ return myUserName;
+}
+
+/* Return my local host name
+ * Returns: my local host name
+ */
+
+char *mylocalhost ()
+{
+ if (!myLocalHost) { /* initialize if first time */
+ char tmp[MAILTMPLEN];
+ int argblk[5];
+ argblk[1] = _GTHNS; /* convert number to string */
+ argblk[2] = (int) (tmp-1);
+ argblk[3] = -1; /* want local host */
+ if (!jsys (GTHST,argblk)) strcpy (tmp,"LOCAL");
+ myLocalHost = cpystr (tmp);
+ }
+ return myLocalHost;
+}
+
+
+/* Return my home directory name
+ * Returns: my home directory name
+ */
+
+char *myhomedir ()
+{
+ if (!myHomeDir) myusername ();/* initialize if first time */
+ return myHomeDir ? myHomeDir : "";
+}
+
+
+/* Determine default prototype stream to user
+ * Accepts: type (NIL for create, T for append)
+ * Returns: default prototype stream
+ */
+
+MAILSTREAM *default_proto (long type)
+{
+ return NIL; /* no default prototype */
+}
+
+/* Emulator for BSD syslog() routine
+ * Accepts: priority
+ * message
+ * parameters
+ */
+
+void syslog (int priority,const char *message,...)
+{
+}
+
+
+/* Emulator for BSD openlog() routine
+ * Accepts: identity
+ * options
+ * facility
+ */
+
+void openlog (const char *ident,int logopt,int facility)
+{
+}
diff --git a/imap/src/osdep/tops-20/env_t20.h b/imap/src/osdep/tops-20/env_t20.h
new file mode 100644
index 00000000..3031f4ed
--- /dev/null
+++ b/imap/src/osdep/tops-20/env_t20.h
@@ -0,0 +1,73 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: TOPS-20 environment routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 25 May 1995
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Dedication:
+ * This file is dedicated with affection to the TOPS-20 operating system, which
+ * set standards for user and programmer friendliness that have still not been
+ * equaled by more `modern' operating systems.
+ * Wasureru mon ka!!!!
+ */
+
+
+#define SUBSCRIPTIONFILE(t) sprintf (t,"%s\\SUBSCRIPTIONS.TXT",myhomedir ())
+#define SUBSCRIPTIONTEMP(t) sprintf (t,"%s\\SUBSCRIPTIONS.TMP",myhomedir ())
+
+/* Function prototypes */
+
+#include "env.h"
+
+char *myusername_full (unsigned long *flags);
+#define MU_LOGGEDIN 0
+#define MU_NOTLOGGEDIN 1
+#define MU_ANONYMOUS 2
+#define myusername() \
+ myusername_full (NIL)
+
+
+/* syslog() emulation */
+
+#define LOG_MAIL (2<<3) /* mail system */
+#define LOG_DAEMON (3<<3) /* system daemons */
+#define LOG_AUTH (4<<3) /* security/authorization messages */
+#define LOG_EMERG 0 /* system is unusable */
+#define LOG_ALERT 1 /* action must be taken immediately */
+#define LOG_CRIT 2 /* critical conditions */
+#define LOG_ERR 3 /* error conditions */
+#define LOG_WARNING 4 /* warning conditions */
+#define LOG_NOTICE 5 /* normal but signification condition */
+#define LOG_INFO 6 /* informational */
+#define LOG_DEBUG 7 /* debug-level messages */
+#define LOG_PID 0x01 /* log the pid with each message */
+#define LOG_CONS 0x02 /* log on the console if errors in sending */
+#define LOG_ODELAY 0x04 /* delay open until syslog() is called */
+#define LOG_NDELAY 0x08 /* don't delay open */
+#define LOG_NOWAIT 0x10 /* if forking to log on console, don't wait() */
+
+void openlog (const char *ident,int logopt,int facility);
+void syslog (int priority,const char *message,...);
diff --git a/imap/src/osdep/tops-20/fs_t20.c b/imap/src/osdep/tops-20/fs_t20.c
new file mode 100644
index 00000000..03908328
--- /dev/null
+++ b/imap/src/osdep/tops-20/fs_t20.c
@@ -0,0 +1,62 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Free storage management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Get a block of free storage
+ * Accepts: size of desired block
+ * Returns: free storage block
+ */
+
+void *fs_get (size_t size)
+{
+ void *block = malloc (size ? size : (size_t) 1);
+ if (!block) fatal ("Out of memory");
+ return (block);
+}
+
+
+/* Resize a block of free storage
+ * Accepts: ** pointer to current block
+ * new size
+ */
+
+void fs_resize (void **block,size_t size)
+{
+ if (!(*block = realloc (*block,size ? size : (size_t) 1)))
+ fatal ("Can't resize memory");
+}
+
+
+/* Return a block of free storage
+ * Accepts: ** pointer to free storage block
+ */
+
+void fs_give (void **block)
+{
+ free (*block);
+ *block = NIL;
+}
diff --git a/imap/src/osdep/tops-20/ftl_t20.c b/imap/src/osdep/tops-20/ftl_t20.c
new file mode 100644
index 00000000..9e65ef55
--- /dev/null
+++ b/imap/src/osdep/tops-20/ftl_t20.c
@@ -0,0 +1,38 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: DOS/VMS/TOPS-20 crash management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Report a fatal error
+ * Accepts: string to output
+ */
+
+void fatal (char *string)
+{
+ mm_fatal (string); /* pass up the string */
+ abort (); /* die horribly */
+}
diff --git a/imap/src/osdep/tops-20/linkage.c b/imap/src/osdep/tops-20/linkage.c
new file mode 100644
index 00000000..14490a0c
--- /dev/null
+++ b/imap/src/osdep/tops-20/linkage.c
@@ -0,0 +1,37 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Default driver linkage
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 13 June 1995
+ * Last Edited: 23 May 2007
+ */
+
+ mail_link (&imapdriver); /* link in the imap driver */
+ mail_link (&nntpdriver); /* link in the nntp driver */
+ mail_link (&pop3driver); /* link in the pop3 driver */
+ mail_link (&dummydriver); /* link in the dummy driver */
+ auth_link (&auth_ext); /* link in the ext authenticator */
+ auth_link (&auth_md5); /* link in the md5 authenticator */
+ auth_link (&auth_pla); /* link in the plain authenticator */
+ auth_link (&auth_log); /* link in the log authenticator */
+ mail_versioncheck (CCLIENTVERSION); /* validate version */
diff --git a/imap/src/osdep/tops-20/linkage.h b/imap/src/osdep/tops-20/linkage.h
new file mode 100644
index 00000000..7e0eccfa
--- /dev/null
+++ b/imap/src/osdep/tops-20/linkage.h
@@ -0,0 +1,36 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Default driver linkage
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 13 June 1995
+ * Last Edited: 30 August 2006
+ */
+
+extern DRIVER imapdriver;
+extern DRIVER nntpdriver;
+extern DRIVER pop3driver;
+extern DRIVER dummydriver;
+extern AUTHENTICATOR auth_ext;
+extern AUTHENTICATOR auth_log;
+extern AUTHENTICATOR auth_md5;
+extern AUTHENTICATOR auth_pla;
diff --git a/imap/src/osdep/tops-20/log_t20.c b/imap/src/osdep/tops-20/log_t20.c
new file mode 100644
index 00000000..1c744d7f
--- /dev/null
+++ b/imap/src/osdep/tops-20/log_t20.c
@@ -0,0 +1,80 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: TOPS-20 server login
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Dedication:
+ * This file is dedicated with affection to the TOPS-20 operating system, which
+ * set standards for user and programmer friendliness that have still not been
+ * equaled by more `modern' operating systems.
+ * Wasureru mon ka!!!!
+ */
+
+/* Server log in
+ * Accepts: user name string
+ * password string
+ * authenticating user name string
+ * argument count
+ * argument vector
+ * Returns: T if password validated, NIL otherwise
+ */
+
+long server_login (char *user,char *pass,char *authuser,int argc,char *argv[])
+{
+ int uid;
+ int argblk[5];
+ if (authuser && *authuser) { /* not available */
+ syslog (LOG_NOTICE|LOG_AUTH,
+ "Login %s failed: invalid authentication ID %s host=%.80s",
+ user,authuser,tcp_clienthost ());
+ sleep (3);
+ return NIL;
+ }
+ argblk[1] = RC_EMO; /* require exact match */
+ argblk[2] = (int) (user-1); /* user name */
+ argblk[3] = 0; /* no stepping */
+ if (!jsys (RCUSR,argblk)) return NIL;
+ uid = argblk[1] = argblk[3]; /* user number */
+ argblk[2] = (int) (pass-1); /* password */
+ argblk[3] = 0; /* no special account */
+ if (!jsys (LOGIN,argblk)) return NIL;
+ return T;
+}
+
+
+/* Authenticated server log in
+ * Accepts: user name string
+ * authenticating user name string
+ * argument count
+ * argument vector
+ * Returns: T if password validated, NIL otherwise
+ */
+
+long authserver_login (char *user,char *authuser,int argc,char *argv[])
+{
+ return NIL; /* how to implement this on TOPS-20??? */
+}
diff --git a/imap/src/osdep/tops-20/nl_t20.c b/imap/src/osdep/tops-20/nl_t20.c
new file mode 100644
index 00000000..47cb7f0a
--- /dev/null
+++ b/imap/src/osdep/tops-20/nl_t20.c
@@ -0,0 +1,61 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Windows/TOPS-20 newline routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Copy string with CRLF newlines
+ * Accepts: destination string
+ * pointer to size of destination string buffer
+ * source string
+ * length of source string
+ * Returns: length of copied string
+ */
+
+unsigned long strcrlfcpy (unsigned char **dst,unsigned long *dstl,
+ unsigned char *src,unsigned long srcl)
+{
+ /* flush destination buffer if too small */
+ if (*dst && (srcl > *dstl)) fs_give ((void **) dst);
+ if (!*dst) { /* make a new buffer if needed */
+ *dst = (char *) fs_get ((size_t) (*dstl = srcl) + 1);
+ if (dstl) *dstl = srcl; /* return new buffer length to main program */
+ }
+ /* copy strings */
+ if (srcl) memcpy (*dst,src,(size_t) srcl);
+ *(*dst + srcl) = '\0'; /* tie off destination */
+ return srcl; /* return length */
+}
+
+
+/* Length of string after strcrlfcpy applied
+ * Accepts: source string
+ * Returns: length of string
+ */
+
+unsigned long strcrlflen (STRING *s)
+{
+ return SIZE (s); /* no-brainer on DOS! */
+}
diff --git a/imap/src/osdep/tops-20/os_t20.c b/imap/src/osdep/tops-20/os_t20.c
new file mode 100644
index 00000000..de0479df
--- /dev/null
+++ b/imap/src/osdep/tops-20/os_t20.c
@@ -0,0 +1,106 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- TOPS-20 version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Dedication:
+ * This file is dedicated with affection to the TOPS-20 operating system, which
+ * set standards for user and programmer friendliness that have still not been
+ * equaled by more `modern' operating systems.
+ * Wasureru mon ka!!!!
+ */
+
+#include "mail.h"
+#include <jsys.h> /* must be before tcp_t20.h */
+#include "tcp_t20.h" /* must be before osdep include tcp.h */
+#include <time.h>
+#include "osdep.h"
+#include <sys/time.h>
+#include "misc.h"
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <ctype.h>
+
+#include "fs_t20.c"
+#include "ftl_t20.c"
+#include "nl_t20.c"
+#include "env_t20.c"
+#include "tcp_t20.c"
+#include "log_t20.c"
+
+#define MD5ENABLE "PS:<SYSTEM>CRAM-MD5.PWD"
+#include "auth_md5.c"
+#include "auth_ext.c"
+#include "auth_pla.c"
+#include "auth_log.c"
+
+/* Emulator for UNIX gethostid() call
+ * Returns: host id
+ */
+
+long gethostid ()
+{
+ int argblk[5];
+#ifndef _APRID
+#define _APRID 28
+#endif
+ argblk[1] = _APRID;
+ jsys (GETAB,argblk);
+ return (long) argblk[1];
+}
+
+
+/* Emulator for UNIX getpass() call
+ * Accepts: prompt
+ * Returns: password
+ */
+
+#define PWDLEN 128 /* used by Linux */
+
+char *getpass (const char *prompt)
+{
+ char *s;
+ static char pwd[PWDLEN];
+ int argblk[5],mode;
+ argblk[1] = (int) (prompt-1); /* prompt user */
+ jsys (PSOUT,argblk);
+ argblk[1] = _PRIIN; /* get current TTY mode */
+ jsys (RFMOD,argblk);
+ mode = argblk[2]; /* save for later */
+ argblk[2] &= ~06000;
+ jsys (SFMOD,argblk);
+ jsys (STPAR,argblk);
+ fgets (pwd,PWDLEN-1,stdin);
+ pwd[PWDLEN-1] = '\0';
+ if (s = strchr (pwd,'\n')) *s = '\0';
+ putchar ('\n');
+ argblk[2] = mode;
+ jsys (SFMOD,argblk);
+ jsys (STPAR,argblk);
+ return pwd;
+}
diff --git a/imap/src/osdep/tops-20/os_t20.h b/imap/src/osdep/tops-20/os_t20.h
new file mode 100644
index 00000000..4c397243
--- /dev/null
+++ b/imap/src/osdep/tops-20/os_t20.h
@@ -0,0 +1,52 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- TOPS-20 version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 January 2007
+ */
+
+
+/* Dedication:
+ * This file is dedicated with affection to the TOPS-20 operating system, which
+ * set standards for user and programmer friendliness that have still not been
+ * equaled by more `modern' operating systems.
+ * Wasureru mon ka!!!!
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <unistd.h>
+
+#include "env_t20.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+
+long gethostid (void);
+char *getpass (const char *prompt);
+
+#define strtok_r(a,b,c) strtok(a,b)
diff --git a/imap/src/osdep/tops-20/pmatch.c b/imap/src/osdep/tops-20/pmatch.c
new file mode 100644
index 00000000..95a0bb86
--- /dev/null
+++ b/imap/src/osdep/tops-20/pmatch.c
@@ -0,0 +1,89 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: IMAP Wildcard Matching Routines (case-independent)
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 June 2000
+ * Last Edited: 30 August 2006
+ */
+
+/* Wildcard pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if pattern matches base, else NIL
+ */
+
+long pmatch_full (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ /* % at end, OK if no inferiors */
+ if (!pat[1]) return (delim && strchr (s,delim)) ? NIL : T;
+ /* scan remainder of string until delimiter */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while ((*s != delim) && *s++);
+ break;
+ case '*': /* match 0 or more characters */
+ if (!pat[1]) return T; /* * at end, unconditional match */
+ /* scan remainder of string */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while (*s++);
+ break;
+ case '\0': /* end of pattern */
+ return *s ? NIL : T; /* success if also end of base */
+ default: /* match this character */
+ return compare_uchar (*pat,*s) ? NIL : pmatch_full (s+1,pat+1,delim);
+ }
+ return NIL;
+}
+
+/* Directory pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if base is a matching directory of pattern, else NIL
+ */
+
+long dmatch (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ if (!*s) return T; /* end of base means have a subset match */
+ if (!*++pat) return NIL; /* % at end, no inferiors permitted */
+ /* scan remainder of string until delimiter */
+ do if (dmatch (s,pat,delim)) return T;
+ while ((*s != delim) && *s++);
+ if (*s && !s[1]) return T; /* ends with delimiter, must be subset */
+ return dmatch (s,pat,delim);/* do new scan */
+ case '*': /* match 0 or more characters */
+ return T; /* unconditional match */
+ case '\0': /* end of pattern */
+ break;
+ default: /* match this character */
+ if (*s) return compare_uchar (*pat,*s) ? NIL : dmatch (s+1,pat+1,delim);
+ /* end of base, return if at delimiter */
+ else if (*pat == delim) return T;
+ break;
+ }
+ return NIL;
+}
diff --git a/imap/src/osdep/tops-20/shortsym.h b/imap/src/osdep/tops-20/shortsym.h
new file mode 100644
index 00000000..2bd54649
--- /dev/null
+++ b/imap/src/osdep/tops-20/shortsym.h
@@ -0,0 +1,608 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Definitions for compilers with 6-letter symbol limits
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 24 May 1995
+ * Last Edited: 1 January 2008
+ */
+
+#define auth_link a_link
+#define auth_log a_log
+#define auth_login_client al_cli
+#define auth_login_server al_ser
+#define auth_ext a_ext
+#define auth_external_client ae_cli
+#define auth_external_server ae_ser
+#define auth_md5 a_md5
+#define auth_md5_valid a5_val
+#define auth_md5_client a5_cli
+#define auth_md5_server a5_ser
+#define auth_pla a_pla
+#define auth_plain_client ap_cli
+#define auth_plain_server ap_ser
+#define authenticate a_auth
+#define authserver_login a_serv
+#define body_encodings bencds
+#define body_types btypes
+#define compare_csizedtext cm_szt
+#define compare_cstring cm_str
+#define compare_uchar cm_uch
+#define compare_ulong cm_uln
+#define default_proto d_prot
+#define dummy_append d_appn
+#define dummy_canonicalize d_cano
+#define dummy_check d_chck
+#define dummy_close d_clos
+#define dummy_copy d_copy
+#define dummy_create d_crea
+#define dummy_create_path d_crep
+#define dummy_delete d_del
+#define dummy_expunge d_exp
+#define dummy_file d_fil
+#define dummy_list d_list
+#define dummy_list_work d_lstw
+#define dummy_listed d_lstd
+#define dummy_lsub d_lsub
+#define dummy_open d_open
+#define dummy_parameters d_parm
+#define dummy_ping d_ping
+#define dummy_rename d_ren
+#define dummy_scan d_scan
+#define dummy_search d_srch
+#define dummy_subscribe d_subs
+#define dummy_valid d_val
+#define env_parameters e_parm
+#define fatal fatal
+#define file_string fl_str
+#define file_string_init fl_ini
+#define file_string_next fl_nxt
+#define file_string_setpos fl_sps
+#define fs_get f_get
+#define fs_give f_give
+#define fs_resize f_rsiz
+#define hash_create h_crea
+#define hash_destory h_dest
+#define hash_index h_indx
+#define hash_lookup h_lkup
+#define hash_add h_add
+#define hash_lookup_and_add h_lad
+#define imap_OK i_OK
+#define imap_acl_work i_aclw
+#define imap_append i_appn
+#define imap_append_single i_apps
+#define imap_anon i_anon
+#define imap_auth i_auth
+#define imap_cache i_cach
+#define imap_cap i_cap
+#define imap_capability i_capa
+#define imap_challenge i_chln
+#define imap_check i_chck
+#define imap_close i_clos
+#define imap_copy i_copy
+#define imap_create i_crea
+#define imap_delete i_del
+#define imap_deleteacl i_dacl
+#define imap_expunge i_expn
+#define imap_fake i_fake
+#define imap_fast i_fast
+#define imap_fetch i_fetc
+#define imap_flag i_flag
+#define imap_flags i_flgs
+#define imap_gc i_gc
+#define imap_gc_body ig_bdy
+#define imap_getacl i_gacl
+#define imap_getquota i_gqot
+#define imap_getquotaroot i_gqtr
+#define imap_host i_host
+#define imap_list i_list
+#define imap_listrights i_lrgh
+#define imap_list_work il_wrk
+#define imap_login i_logn
+#define imap_lsub i_lsub
+#define imap_manage i_man
+#define imap_msgdata i_msgd
+#define imap_msgno i_msgn
+#define imap_myrights i_mrgh
+#define imap_open i_open
+#define imap_parameters i_parm
+#define imap_parse_address ip_adr
+#define imap_parse_adrlist ip_adl
+#define imap_parse_astring ip_ast
+#define imap_parse_body ip_bdy
+#define imap_parse_body_parameter ipb_pa
+#define imap_parse_body_structure ipb_st
+#define imap_parse_capabilities ip_cap
+#define imap_parse_disposition ip_dsp
+#define imap_parse_envelope ip_env
+#define imap_parse_extension ip_ext
+#define imap_parse_flags ip_flg
+#define imap_parse_header ip_hdr
+#define imap_parse_language ip_lng
+#define imap_parse_namespace ip_nam
+#define imap_parse_reply ip_rep
+#define imap_parse_response ip_rsp
+#define imap_parse_string ip_str
+#define imap_parse_stringlist ip_stl
+#define imap_parse_thread ip_thr
+#define imap_parse_unsolicited ip_uns
+#define imap_parse_user_flag ipu_fl
+#define imap_ping i_ping
+#define imap_reform_sequence i_rfrs
+#define imap_rename i_ren
+#define imap_reply i_rep
+#define imap_response i_rspn
+#define imap_scan i_scan
+#define imap_search i_srch
+#define imap_send i_send
+#define imap_send_astring is_ast
+#define imap_send_literal is_lit
+#define imap_send_sdate iss_da
+#define imap_send_slist iss_sl
+#define imap_send_spgm iss_pg
+#define imap_send_spgm_trim iss_pt
+#define imap_send_sset iss_st
+#define imap_send_sset_work iss_sw
+#define imap_setacl i_sacl
+#define imap_setquota i_sqot
+#define imap_sort i_sort
+#define imap_sout i_sout
+#define imap_soutr i_sotr
+#define imap_status i_stat
+#define imap_structure i_stru
+#define imap_subscribe i_sub
+#define imap_thread i_thrd
+#define imap_thread_work i_thrw
+#define imap_uid i_uid
+#define imap_unsubscribe i_uns
+#define imap_valid i_val
+#define internal_date in_dat
+#define mail_append_full m_appn
+#define mail_append_multiple m_appm
+#define mail_append_set m_apps
+#define mail_auth m_auth
+#define mail_body m_body
+#define mail_cdate m_cdat
+#define mail_check m_chck
+#define mail_close_full m_clos
+#define mail_copy_full m_copy
+#define mail_create m_crea
+#define mail_criteria m_crit
+#define mail_criteria_date mc_dat
+#define mail_criteria_string mc_str
+#define mail_date m_date
+#define mail_debug m_dbug
+#define mail_delete m_del
+#define mail_dlog m_dlog
+#define mail_elt m_elt
+#define mail_exists m_exst
+#define mail_expunge_full m_expn
+#define mail_expunged m_expd
+#define mail_fetch_body fs_bdy
+#define mail_fetch_fast mf_fst
+#define mail_fetch_flags mf_flg
+#define mail_fetch_header mf_hdr
+#define mail_fetch_message mf_msg
+#define mail_fetch_mime mf_mim
+#define mail_fetch_overview mf_ovr
+#define mail_fetch_overview_sequence mf_ovs
+#define mail_fetch_overview_default mf_ovd
+#define mail_fetch_structure mf_str
+#define mail_fetch_text mf_txt
+#define mail_fetch_text_return mf_txr
+#define mail_fetch_string_return mf_tsr
+#define mail_fetchfrom mf_frm
+#define mail_fetchsubject mf_sub
+#define mail_filter m_filt
+#define mail_flag m_flag
+#define mail_free_acl mr_acl
+#define mail_free_address mr_add
+#define mail_free_body mr_bdy
+#define mail_free_body_data mrb_da
+#define mail_free_body_parameter mrb_pr
+#define mail_free_body_part mrb_pt
+#define mail_free_cache mr_cac
+#define mail_free_elt mr_elt
+#define mail_free_envelope mr_env
+#define mail_free_handle mr_han
+#define mail_free_namespace mr_nsp
+#define mail_free_quotalist mr_qtl
+#define mail_free_searchheader mrs_hd
+#define mail_free_searchor mrs_or
+#define mail_free_searchpgm mrs_pg
+#define mail_free_searchpgmlist mrs_pl
+#define mail_free_searchset mrs_st
+#define mail_free_sortpgm mr_spg
+#define mail_free_stringlist mr_sls
+#define mail_free_threadnode mr_thn
+#define mail_gc m_gc
+#define mail_gc_msg m_gcm
+#define mail_gc_body m_gcb
+#define mail_initbody m_ibdy
+#define mail_link m_link
+#define mail_list m_list
+#define mail_lock m_lock
+#define mail_longdate ml_lda
+#define mail_lookup_auth m_laut
+#define mail_lookup_auth_name m_latn
+#define mail_lsub m_lsub
+#define mail_makehandle m_mhdl
+#define mail_match_lines m_mlns
+#define mail_msgno m_msgn
+#define mail_newacl mn_acl
+#define mail_newaddr mn_add
+#define mail_newbody mn_bdy
+#define mail_newbody_parameter mnb_pr
+#define mail_newbody_part mnb_pt
+#define mail_newbody_message_part mnb_mp
+#define mail_new_cache_elt mn_elt
+#define mail_newenvelope mn_env
+#define mail_newmsg mn_msg
+#define mail_newquotalist mn_qtl
+#define mail_newsearchheader mns_hd
+#define mail_newsearchor mns_or
+#define mail_newsearchpgm mns_pg
+#define mail_newsearchpgmlist mns_pl
+#define mail_newsearchset mns_st
+#define mail_newsortpgm mn_spg
+#define mail_newstringlist mn_sls
+#define mail_newthreadnode mn_thr
+#define mail_nodebug m_ndbg
+#define mail_open m_open
+#define mail_parameters m_parm
+#define mail_parse_date mp_dat
+#define mail_parse_flags mp_flg
+#define mail_parse_set mp_set
+#define mail_partial_body mpt_bd
+#define mail_partial_text mpt_tx
+#define mail_ping m_ping
+#define mail_read m_read
+#define mail_recent m_rcent
+#define mail_rename m_ren
+#define mail_scan m_scan
+#define mail_search_addr ms_adr
+#define mail_search_body ms_bdy
+#define mail_search_default ms_def
+#define mail_search_full m_srch
+#define mail_search_gets ms_gts
+#define mail_search_header ms_hdr
+#define mail_search_header_text ms_hdt
+#define mail_search_keyword ms_key
+#define mail_search_msg ms_msg
+#define mail_search_string ms_str
+#define mail_search_string_work ms_stw
+#define mail_search_text ms_txt
+#define mail_sequence m_seq
+#define mail_shortdate m_shtd
+#define mail_skip_fwd msk_fw
+#define mail_skip_re msk_re
+#define mail_sort ml_srt
+#define mail_sort_cache ms_csh
+#define mail_sort_compare ms_cmp
+#define mail_sort_loadcache ms_lcs
+#define mail_sort_msgs ms_mgs
+#define mail_status m_stat
+#define mail_status_default m_stad
+#define mail_stream m_strm
+#define mail_string m_strg
+#define mail_string_init mt_ini
+#define mail_string_next mt_nxt
+#define mail_string_setpos mt_sps
+#define mail_strip_subject mst_sb
+#define mail_strip_subject_wsp mst_ws
+#define mail_strip_subject_blob mst_bl
+#define mail_subscribe m_sub
+#define mail_thread m_thr
+#define mail_threadlist mt_lst
+#define mail_thread_c2node mt_c2n
+#define mail_thread_check_child mt_ckc
+#define mail_thread_compare_date mtc_da
+#define mail_thread_loadcache mt_ldc
+#define mail_thread_msgs mt_mgs
+#define mail_thread_orderedsubject mt_osb
+#define mail_thread_parse_msgid mtp_mi
+#define mail_thread_parse_references mtp_rf
+#define mail_thread_prune_dummy mt_prd
+#define mail_thread_references mt_ref
+#define mail_thread_sort mt_srt
+#define mail_uid m_uid
+#define mail_uid_sequence mu_seq
+#define mail_unlock m_unl
+#define mail_unsubscribe m_uns
+#define mail_usable_network_stream m_usns
+#define mail_utf7_valid m_ut7v
+#define mail_valid m_val
+#define mail_valid_net mv_net
+#define mail_valid_net_parse mvn_pr
+#define mail_valid_net_parse_work mvn_pw
+#define mail_versioncheck m_vers
+#define mailboxfile mbxfil
+#define md5_init m5_ini
+#define md5_update m5_upd
+#define md5_final m5_fin
+#define mime2_decode m2_dec
+#define mime2_text m2_txt
+#define mime2_token m2_tok
+#define mm_cache mm_cac
+#define mm_critical mm_crt
+#define mm_diskerror mm_dse
+#define mm_dlog mm_dlg
+#define mm_exists mm_exs
+#define mm_expunged mm_exp
+#define mm_fatal mm_ftl
+#define mm_flags mm_flg
+#define mm_list mm_lst
+#define mm_log mm_log
+#define mm_login mm_lgi
+#define mm_lsub mm_lsb
+#define mm_mailbox mm_mbx
+#define mm_nocritical mm_ncr
+#define mm_notify mm_not
+#define mm_searched mm_src
+#define myhomedir myhome
+#define mylocalhost myhost
+#define myusername_full myuser
+#define net_aopen nt_aop
+#define net_close nt_cls
+#define net_getbuffer nt_gtb
+#define net_getdata nt_gtd
+#define net_getline nt_gtl
+#define net_host nt_hst
+#define net_localhost nt_lhs
+#define net_open nt_opn
+#define net_port nt_prt
+#define net_sout nt_sot
+#define net_soutr nt_str
+#define netmsg_read nm_rea
+#define netmsg_slurp nm_slr
+#define netmsg_slurp_text nm_slt
+#define newsrc_check_uid nsc_ui
+#define newsrc_create ns_crea
+#define newsrc_error ns_err
+#define newsrc_lsub ns_lsub
+#define newsrc_newmessages ns_nms
+#define newsrc_newstate ns_nst
+#define newsrc_read ns_rea
+#define newsrc_status ns_sta
+#define newsrc_update ns_upd
+#define newsrc_write ns_wri
+#define newsrc_write_error ns_wer
+#define nntp_append n_appn
+#define nntp_canonicalize n_cano
+#define nntp_check n_chck
+#define nntp_close n_clos
+#define nntp_copy n_copy
+#define nntp_create n_crea
+#define nntp_delete n_del
+#define nntp_expunge n_expn
+#define nntp_fake n_fake
+#define nntp_fetchfast nf_fst
+#define nntp_fetchflags nf_flg
+#define nntp_fetchmessage nf_msg
+#define nntp_flagmsg n_fmsg
+#define nntp_gc n_gc
+#define nntp_getmap n_gmap
+#define nntp_header n_head
+#define nntp_isvalid n_isvl
+#define nntp_list n_list
+#define nntp_lsub n_lsub
+#define nntp_mail n_mail
+#define nntp_mclose n_mcls
+#define nntp_mopen n_mopn
+#define nntp_open_full n_open
+#define nntp_over n_ovr
+#define nntp_overview n_over
+#define nntp_parameters n_parm
+#define nntp_parsestructure n_pars
+#define nntp_parse_overview n_povr
+#define nntp_ping n_ping
+#define nntp_rename n_ren
+#define nntp_reply n_repl
+#define nntp_scan n_scan
+#define nntp_search n_srch
+#define nntp_search_msg ns_msg
+#define nntp_send n_send
+#define nntp_send_auth ns_aut
+#define nntp_send_auth_work ns_atw
+#define nntp_send_work n_sndw
+#define nntp_sort n_sort
+#define nntp_sort_loadcache ns_lcs
+#define nntp_soutr n_sout
+#define nntp_status n_stat
+#define nntp_subscribe n_sub
+#define nntp_text n_text
+#define nntp_text_slurp nt_slp
+#define nntp_thread n_thrd
+#define nntp_unsubscribe n_uns
+#define nntp_valid n_val
+#define pop3_append p_appn
+#define pop3_auth p_auth
+#define pop3_cache p_cach
+#define pop3_challenge p_chal
+#define pop3_check p_chck
+#define pop3_close p_clos
+#define pop3_copy p_copy
+#define pop3_create p_crea
+#define pop3_delete p_del
+#define pop3_expunge p_exp
+#define pop3_fake p_fake
+#define pop3_fetchfast pf_fst
+#define pop3_fetchflags pf_flg
+#define pop3_fetchmessage pf_msg
+#define pop3_gc p_gc
+#define pop3_list p_list
+#define pop3_lsub p_lsub
+#define pop3_open p_open
+#define pop3_parameters p_parm
+#define pop3_parsestructure p_pars
+#define pop3_ping p_ping
+#define pop3_rename p_ren
+#define pop3_reply p_rep
+#define pop3_response p_resp
+#define pop3_scan p_scan
+#define pop3_send p_send
+#define pop3_send_num ps_num
+#define pop3_status p_stat
+#define pop3_subscribe p_sub
+#define pop3_unsubscribe p_uns
+#define pop3_valid p_val
+#define rfc822_8bit r
+#define rfc822_address r_addr
+#define rfc822_address_line ra_lin
+#define rfc822_base64 r_b64
+#define rfc822_binary r_bin
+#define rfc822_cat r_cat
+#define rfc822_contents r_cont
+#define rfc822_cpy r_cpy
+#define rfc822_cpy_adr rc_adr
+#define rfc822_date r_date
+#define rfc822_default_subtype rd_sub
+#define rfc822_encode_body_7bit reb_7b
+#define rfc822_encode_body_8bit reb_8b
+#define rfc822_header r_head
+#define rfc822_header_line rh_lin
+#define rfc822_output r_out
+#define rfc822_output_address ro_adr
+#define rfc822_output_address_line roa_ln
+#define rfc822_output_address_list roa_li
+#define rfc822_output_body ro_bdy
+#define rfc822_output_body_header rob_hd
+#define rfc822_output_full ro_ful
+#define rfc822_output_flush ro_flu
+#define rfc822_output_header ro_hdr
+#define rfc822_output_header_line roh_ln
+#define rfc822_output_cat ro_cat
+#define rfc822_output_parameter ro_par
+#define rfc822_output_stringlist ro_stl
+#define rfc822_output_text ro_txt
+#define rfc822_parse_address rp_adr
+#define rfc822_parse_addrspec rp_ads
+#define rfc822_parse_adrlist rp_adl
+#define rfc822_parse_content rp_cnt
+#define rfc822_parse_content_header rpc_hd
+#define rfc822_parse_group rp_grp
+#define rfc822_parse_mailbox rp_mbx
+#define rfc822_parse_msg_full rp_msg
+#define rfc822_parse_parameter rp_par
+#define rfc822_parse_phrase rp_phr
+#define rfc822_parse_routeaddr rp_rte
+#define rfc822_parse_word rp_wrd
+#define rfc822_phraseonly r_poly
+#define rfc822_qprint r_qpnt
+#define rfc822_quote r_quot
+#define rfc822_skip_comment rs_cmt
+#define rfc822_skipws rs_ws
+#define rfc822_timezone r_tz
+#define rfc822_write_address_full rw_adr
+#define rfc822_write_body_header rwbh_8
+#define server_input_wait s_iwat
+#define server_login s_log
+#define server_init s_init
+#define sm_read sm_rd
+#define sm_subscribe sm_sub
+#define sm_unsubscribe sm_uns
+#define smtp_auth s_auth
+#define smtp_challenge s_chal
+#define smtp_close s_clos
+#define smtp_ehlo s_ehlo
+#define smtp_fake s_fake
+#define smtp_mail s_mail
+#define smtp_open_full s_open
+#define smtp_rcpt s_rcpt
+#define smtp_reply s_repl
+#define smtp_response s_resp
+#define smtp_send s_send
+#define smtp_send_auth ss_aut
+#define smtp_send_auth_work ss_atw
+#define smtp_send_work ss_wrk
+#define smtp_soutr s_str
+#define strcrlfcpy sc_cpy
+#define strcrlflen sc_len
+#define tcp_aopen t_aopn
+#define tcp_canonical t_cnon
+#define tcp_clientaddr t_cadr
+#define tcp_clienthost t_chst
+#define tcp_clientport t_cprt
+#define tcp_close t_clos
+#define tcp_getbuffer tg_buf
+#define tcp_getdata tg_dat
+#define tcp_getline tg_lin
+#define tcp_host t_host
+#define tcp_localhost t_lhst
+#define tcp_open t_open
+#define tcp_parameters t_parameters
+#define tcp_port t_port
+#define tcp_remotehost t_rhst
+#define tcp_serveraddr t_sadr
+#define tcp_serverhost t_shst
+#define tcp_serverport t_sprt
+#define tcp_sout t_sout
+#define tcp_soutr t_str
+#define textcpy txcopy
+#define textcpystring txcpst
+#define textcpyoffstring txcpos
+#define ucs4_cs_get u4_csg
+#define ucs4_decompose u4_dcm
+#define ucs4_decompose_recursive u4_dcr
+#define ucs4_rmapbuf u4r_bf
+#define ucs4_rmaplen u4r_ln
+#define ucs4_rmaptext u4r_tx
+#define ucs4_titlecase u4_tcs
+#define ucs4_width u4_wid
+#define utf8_badcharset u8_bcs
+#define utf8_charset u8_chs
+#define utf8_cstext u8_cst
+#define utf8_cstocstext u8_cct
+#define utf8_from_mutf7 u8fmu7
+#define utf8_get u8_get
+#define utf8_get_raw u8_gtr
+#define utf8_iso2022text u8_i22
+#define utf8_mime2text u8_mi2
+#define utf8_put u8_put
+#define utf8_rmap u8_rmp
+#define utf8_rmap_cs u8r_cs
+#define utf8_rmap_gen u8r_gn
+#define utf8_rmapsize u8r_sz
+#define utf8_rmaptext u8r_tx
+#define utf8_script u8_scr
+#define utf8_searchpgm u8_spg
+#define utf8_size u8_siz
+#define utf8_stringlist u8_lst
+#define utf8_text u8_txt
+#define utf8_text_2022 u8t_22
+#define utf8_text_8859_1 u8t_we
+#define utf8_text_1byte0 u8t_10
+#define utf8_text_1byte u8t_1b
+#define utf8_text_1byte8 u8t_18
+#define utf8_text_cs ut8_cs
+#define utf8_text_euc u8t_eu
+#define utf8_text_dbyte u8t_db
+#define utf8_text_dbyte2 u8t_d2
+#define utf8_text_sjis u8t_sj
+#define utf8_text_ucs2 u8t_u2
+#define utf8_text_ucs4 ut8_u4
+#define utf8_text_utf7 ut8_u7
+#define utf8_text_utf8 ut8_u8
+#define utf8_text_utf16 ut8_16
+#define utf8_to_mutf7 u8tmu7
+#define utf8_validate u8_val
+#define utf8_textwidth u8_twd
diff --git a/imap/src/osdep/tops-20/tcp_t20.c b/imap/src/osdep/tops-20/tcp_t20.c
new file mode 100644
index 00000000..7b2a2580
--- /dev/null
+++ b/imap/src/osdep/tops-20/tcp_t20.c
@@ -0,0 +1,365 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: TOPS-20 TCP/IP routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 13 January 2008
+ */
+
+
+/* Dedication:
+ * This file is dedicated with affection to the TOPS-20 operating system, which
+ * set standards for user and programmer friendliness that have still not been
+ * equaled by more `modern' operating systems.
+ * Wasureru mon ka!!!!
+ */
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd);
+
+/* TCP/IP manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tcp_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+/* TCP/IP open
+ * Accepts: host name
+ * contact service name
+ * contact port number
+ * Returns: TCP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
+{
+ char *s,tmp[MAILTMPLEN];
+ TCPSTREAM *stream = NIL;
+ int argblk[5],jfn;
+ unsigned long i,j,k,l;
+ char file[MAILTMPLEN];
+ port &= 0xffff; /* erase flags */
+ /* domain literal? */
+ if (host[0] == '[' && host[strlen (host)-1] == ']') {
+ if (((i = strtoul (s = host+1,&s,10)) <= 255) && *s++ == '.' &&
+ ((j = strtoul (s,&s,10)) <= 255) && *s++ == '.' &&
+ ((k = strtoul (s,&s,10)) <= 255) && *s++ == '.' &&
+ ((l = strtoul (s,&s,10)) <= 255) && *s++ == ']' && !*s) {
+ argblk[3] = (i << 24) + (j << 16) + (k << 8) + l;
+ sprintf (tmp,"[%lu.%lu.%lu.%lu]",i,j,k,l);
+ }
+ else {
+ sprintf (tmp,"Bad format domain-literal: %.80s",host);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ }
+ else { /* host name */
+ argblk[1] = _GTHPN; /* get IP address and primary name */
+ argblk[2] = (int) (host-1); /* pointer to host */
+ argblk[4] = (int) (tmp-1);
+ if (!jsys (GTHST,argblk)) { /* first try DEC's domain way */
+ argblk[1] = _GTHPN; /* get IP address and primary name */
+ argblk[2] = (int) (host-1);
+ argblk[4] = (int) (tmp-1);
+ if (!jsys (GTDOM,argblk)){/* try the CHIVES domain way */
+ argblk[1] = _GTHSN; /* failed, do the host table then */
+ if (!jsys (GTHST,argblk)) {
+ sprintf (tmp,"No such host as %s",host);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ argblk[1] = _GTHNS; /* convert number to string */
+ argblk[2] = (int) (tmp-1);
+ /* get the official name */
+ if (!jsys (GTHST,argblk)) strcpy (tmp,host);
+ }
+ }
+ }
+
+ sprintf (file,"TCP:.%o-%d;PERSIST:30;CONNECTION:ACTIVE",argblk[3],port);
+ argblk[1] = GJ_SHT; /* short form GTJFN% */
+ argblk[2] = (int) (file-1); /* pointer to file name */
+ /* get JFN for TCP: file */
+ if (!jsys (GTJFN,argblk)) fatal ("Unable to create TCP JFN");
+ jfn = argblk[1]; /* note JFN for later */
+ /* want 8-bit bidirectional I/O */
+ argblk[2] = OF_RD|OF_WR|(FLD (8,monsym("OF%BSZ")));
+ if (!jsys (OPENF,argblk)) {
+ sprintf (file,"Can't connect to %s,%d server",tmp,port);
+ mm_log (file,ERROR);
+ return NIL;
+ }
+ /* create TCP/IP stream */
+ stream = (TCPSTREAM *) fs_get (sizeof (TCPSTREAM));
+ stream->host = cpystr (tmp); /* copy official host name */
+ argblk[1] = _GTHNS; /* convert number to string */
+ argblk[2] = (int) (tmp-1);
+ argblk[3] = -1; /* want local host */
+ if (!jsys (GTHST,argblk)) strcpy (tmp,"LOCAL");
+ stream->localhost = cpystr (tmp);
+ stream->port = port; /* save port number */
+ stream->jfn = jfn; /* init JFN */
+ return stream;
+}
+
+/* TCP/IP authenticated open
+ * Accepts: NETMBX specifier
+ * service name
+ * returned user name buffer
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL;
+}
+
+/* TCP receive line
+ * Accepts: TCP stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *tcp_getline (TCPSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = tcp_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = tcp_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* TCP receive line or partial line
+ * Accepts: TCP stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ int argblk[5];
+ unsigned long n,m;
+ char *ret,*stp,*st;
+ *contd = NIL; /* assume no continuation */
+ argblk[1] = stream->jfn; /* read from TCP */
+ /* pointer to buffer */
+ argblk[2] = (int) (stream->ibuf - 1);
+ argblk[3] = BUFLEN; /* max number of bytes to read */
+ argblk[4] = '\012'; /* terminate on LF */
+ if (!jsys (SIN,argblk)) return NIL;
+ n = BUFLEN - argblk[3]; /* number of bytes read */
+ /* got a complete line? */
+ if ((stream->ibuf[n - 2] == '\015') && (stream->ibuf[n - 1] == '\012')) {
+ memcpy ((ret = (char *) fs_get (n)),stream->ibuf,*size = n - 2);
+ ret[n - 2] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ /* copy partial string */
+ memcpy ((ret = (char *) fs_get (n)),stream->ibuf,*size = n);
+ /* special case of newline broken by buffer */
+ if ((stream->ibuf[n - 1] == '\015') && jsys (BIN,argblk) &&
+ (argblk[2] == '\012')) { /* was it? */
+ ret[n - 1] = '\0'; /* tie off string with null */
+ }
+ /* otherwise back up */
+ else if (!jsys (BKJFN,argblk)) fs_give ((void **) &ret);
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* TCP/IP receive buffer
+ * Accepts: TCP/IP stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer)
+{
+ int argblk[5];
+ argblk[1] = stream->jfn; /* read from TCP */
+ argblk[2] = (int) (buffer-1); /* pointer to buffer */
+ argblk[3] = -size; /* number of bytes to read */
+ if (!jsys (SIN,argblk)) return NIL;
+ buffer[size] = '\0'; /* tie off text */
+ return T;
+}
+
+
+/* TCP/IP send string as record
+ * Accepts: TCP/IP stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long tcp_soutr (TCPSTREAM *stream,char *string)
+{
+ int argblk[5];
+ argblk[1] = stream->jfn; /* write to TCP */
+ argblk[2] = (int) (string-1); /* pointer to buffer */
+ argblk[3] = 0; /* write until NUL */
+ if (!jsys (SOUTR,argblk)) return NIL;
+ return T;
+}
+
+
+/* TCP/IP send string
+ * Accepts: TCP/IP stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
+{
+ int argblk[5];
+ argblk[1] = stream->jfn; /* write to TCP */
+ argblk[2] = (int) (string-1); /* pointer to buffer */
+ argblk[3] = -size; /* write this many bytes */
+ if (!jsys (SOUTR,argblk)) return NIL;
+ return T;
+}
+
+/* TCP/IP close
+ * Accepts: TCP/IP stream
+ */
+
+void tcp_close (TCPSTREAM *stream)
+{
+ int argblk[5];
+ argblk[1] = stream->jfn; /* close TCP */
+ jsys (CLOSF,argblk);
+ /* flush host names */
+ fs_give ((void **) &stream->host);
+ fs_give ((void **) &stream->localhost);
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+
+/* TCP/IP return host for this stream
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_host (TCPSTREAM *stream)
+{
+ return stream->host; /* return host name */
+}
+
+
+/* TCP/IP return remote host for this stream
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_remotehost (TCPSTREAM *stream)
+{
+ return stream->host; /* return host name */
+}
+
+
+/* TCP/IP return port for this stream
+ * Accepts: TCP/IP stream
+ * Returns: port number for this stream
+ */
+
+unsigned long tcp_port (TCPSTREAM *stream)
+{
+ return stream->port; /* return port number */
+}
+
+
+/* TCP/IP return local host for this stream
+ * Accepts: TCP/IP stream
+ * Returns: local host name for this stream
+ */
+
+char *tcp_localhost (TCPSTREAM *stream)
+{
+ return stream->localhost; /* return local host name */
+}
+
+/* TCP/IP return canonical form of host name
+ * Accepts: host name
+ * Returns: canonical form of host name
+ */
+
+char *tcp_canonical (char *name)
+{
+ int argblk[5];
+ static char tmp[MAILTMPLEN];
+ /* look like domain literal? */
+ if (name[0] == '[' && name[strlen (name) - 1] == ']') return name;
+ argblk[1] = _GTHPN; /* get IP address and primary name */
+ argblk[2] = (int) (name-1); /* pointer to host */
+ argblk[4] = (int) (tmp-1); /* pointer to return destination */
+ if (!jsys (GTHST,argblk)) { /* first try DEC's domain way */
+ argblk[1] = _GTHPN; /* get IP address and primary name */
+ argblk[2] = (int) (name-1);
+ argblk[4] = (int) (tmp-1);
+ if (!jsys (GTDOM,argblk)) { /* try the CHIVES domain way */
+ argblk[1] = _GTHSN; /* failed, do the host table then */
+ if (!jsys (GTHST,argblk)) return name;
+ argblk[1] = _GTHNS; /* convert number to string */
+ argblk[2] = (int) (tmp-1);
+ /* get the official name */
+ if (!jsys (GTHST,argblk)) return name;
+ }
+ }
+ return tmp;
+}
+
+
+/* TCP/IP get client host name (server calls only)
+ * Returns: client host name
+ */
+
+char *tcp_clienthost ()
+{
+ return "UNKNOWN";
+}
diff --git a/imap/src/osdep/tops-20/tcp_t20.h b/imap/src/osdep/tops-20/tcp_t20.h
new file mode 100644
index 00000000..291465cf
--- /dev/null
+++ b/imap/src/osdep/tops-20/tcp_t20.h
@@ -0,0 +1,65 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: TOPS-20 TCP/IP routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Dedication:
+ * This file is dedicated with affection to the TOPS-20 operating system, which
+ * set standards for user and programmer friendliness that have still not been
+ * equaled by more `modern' operating systems.
+ * Wasureru mon ka!!!!
+ */
+
+/* TCP input buffer */
+
+#define BUFLEN 8192
+
+/* TCP I/O stream (must be before osdep.h is included) */
+
+#define TCPSTREAM struct tcp_stream
+TCPSTREAM {
+ char *host; /* host name */
+ unsigned long port; /* port number */
+ char *localhost; /* local host name */
+ int jfn; /* jfn for connection */
+ char ibuf[BUFLEN]; /* input buffer */
+};
+
+
+/* All of these should be in JSYS.H, but just in case... */
+
+#ifndef _GTHPN
+#define _GTHPN 12
+#endif
+
+#ifndef _GTDPN
+#define _GTDPN 12
+#endif
+
+#ifndef GTDOM
+#define GTDOM (FLD (1,JSYS_CLASS) | 501
+#endif
diff --git a/imap/src/osdep/unix/Makefile b/imap/src/osdep/unix/Makefile
new file mode 100644
index 00000000..78913acc
--- /dev/null
+++ b/imap/src/osdep/unix/Makefile
@@ -0,0 +1,1078 @@
+# ========================================================================
+# Copyright 1988-2007 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+# Program: C client makefile
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 11 May 1989
+# Last Edited: 17 December 2007
+
+
+# Command line build parameters
+
+EXTRAAUTHENTICATORS=
+EXTRADRIVERS=mbox
+PASSWDTYPE=std
+SSLTYPE=nopwd
+IP=4
+
+
+# The optimization level here for GCC ports is set here for a reason. It's
+# to get you to read this text.
+# The general concensus seems to be that -O2 is the one to use.
+# Over the years, I've been told to use many different settings, including -O6.
+# In recent versions of GCC [as of 2/2005], -O6 generates bad code that, among
+# other ill effects, causes infinite loops.
+# -O3 seems to be safe, but empirical observation from our local expert
+# indicates that in some (many?) cases -O3 code runs slower than -O2.
+
+GCCOPTLEVEL= -O2
+
+
+# Try to have some consistency in GCC builds. We want optimization, but we
+# also want to be able to debug.
+
+GCCCFLAGS= -g $(GCCOPTLEVEL) -pipe -fno-omit-frame-pointer
+GCC4CFLAGS= $(GCCCFLAGS) -Wno-pointer-sign
+
+
+# Extended flags needed for SSL. You may need to modify.
+
+SSLDIR=/usr/local/ssl
+SSLCERTS=$(SSLDIR)/certs
+SSLKEYS=$(SSLCERTS)
+SSLINCLUDE=$(SSLDIR)/include
+SSLLIB=$(SSLDIR)/lib
+
+SSLCRYPTO=-lcrypto
+
+# Older versions of MIT Kerberos also have a libcrypto. If so, you may need
+# to use this instead
+#SSLCRYPTO=$(SSLLIB)/libcrypto.a
+
+# RSA Security Inc. released the RSA public key encryption algorithm into
+# the public domain on September 6, 2000. There is no longer any need to
+# use RSAREF.
+SSLRSA= # -lRSAglue -lrsaref
+
+SSLCFLAGS= -I$(SSLINCLUDE) -I$(SSLINCLUDE)/openssl\
+ -DSSL_CERT_DIRECTORY=\"$(SSLCERTS)\" -DSSL_KEY_DIRECTORY=\"$(SSLKEYS)\"
+SSLLDFLAGS= -L$(SSLLIB) -lssl $(SSLCRYPTO) $(SSLRSA)
+
+
+# Extended flags needed for non-standard passwd types. You may need to modify.
+
+AFSDIR=/usr/afsws
+AFSCFLAGS=-I$(AFSDIR)/include
+AFSLIB=$(AFSDIR)/lib
+AFSLDFLAGS=-L$(AFSLIB)/afs -L$(AFSLIB) -L$(AFSDIR)/domestic/lib\
+ -lkauth -lprot -lubik -lauth -lrxkad -lrx -llwp -ldes -lcom_err\
+ $(AFSLIB)/afs/util.a -laudit -lsys
+# AFSLDFLAGS may also need -L/usr/ucblib -lucb
+DCECFLAGS= -DDCE_MINIMAL -DPASSWD_OVERRIDE=\"/opt/pop3/passwd/passwd\"
+DCELDFLAGS= -ldce
+PAMLDFLAGS= -lpam -ldl
+
+
+# Build parameters normally set by the individual port
+
+CHECKPW=std
+LOGINPW=std
+SIGTYPE=bsd
+CRXTYPE=std
+ACTIVEFILE=/usr/lib/news/active
+SPOOLDIR=/usr/spool
+MAILSPOOL=$(SPOOLDIR)/mail
+NEWSSPOOL=$(SPOOLDIR)/news
+RSHPATH=/usr/ucb/rsh
+MD5PWD=/etc/cram-md5.pwd
+# Tries one of the test alternatives below if not specified.
+LOCKPGM=
+# Test alternatives if LOCKPGM not specified
+LOCKPGM1=/usr/libexec/mlock
+LOCKPGM2=/usr/sbin/mlock
+LOCKPGM3=/etc/mlock
+
+
+# Default formats for creating new mailboxes and for empty mailboxes in the
+# default namespace; must be set to the associated driver's prototype.
+#
+# The CREATEPROTO is the default format for new mailbox creation.
+# The EMPTYPROTO is the default format for handling zero-byte files.
+#
+# Normally, this is set by the individual port.
+#
+# NOTE: namespace formats (e.g. mh and news) can not be set as a default format
+# since they do not exist in the default namespace. Also, it is meaningless to
+# set certain other formats (e.g. mbx, mx, and mix) as the EMPTYPROTO since
+# these formats can never be empty files.
+
+CREATEPROTO=unixproto
+EMPTYPROTO=unixproto
+
+
+# Commands possibly overriden by the individual port
+
+ARRC=ar rc
+CC=cc
+LN=ln -s
+RANLIB=ranlib
+
+
+# Standard distribution build parameters
+
+DEFAULTAUTHENTICATORS=ext md5 pla log
+#
+# mh needs to be after any other directory format drivers (such as mx or mix)
+# since otherwise mh will seize any directory that is under the mh path.
+# However, mh needs to be before any sysinbox formats (such as mmdf or unix)
+# since otherwise INBOX won't work correctly when mh_allow_inbox is set.
+#
+DEFAULTDRIVERS=imap nntp pop3 mix mx mbx tenex mtx mh mmdf unix news phile
+CHUNKSIZE=65536
+
+# Normally no need to change any of these
+
+ARCHIVE=c-client.a
+BINARIES=osdep.o mail.o misc.o newsrc.o smanager.o utf8.o utf8aux.o siglocal.o \
+ dummy.o pseudo.o netmsg.o flstring.o fdstring.o \
+ rfc822.o nntp.o smtp.o imap4r1.o pop3.o \
+ unix.o mbx.o mmdf.o tenex.o mtx.o news.o phile.o mh.o mx.o mix.o
+CFLAGS=-g
+
+CAT=cat
+MAKE=make
+MV=mv
+RM=rm -rf
+SH=sh
+
+
+# Primary build command
+
+BUILD=$(MAKE) build EXTRACFLAGS='$(EXTRACFLAGS)'\
+ EXTRALDFLAGS='$(EXTRALDFLAGS)'\
+ EXTRADRIVERS='$(EXTRADRIVERS)' EXTRAAUTHENTICATORS='$(EXTRAAUTHENTICATORS)'\
+ PASSWDTYPE=$(PASSWDTYPE) SSLTYPE=$(SSLTYPE) IP=$(IP)
+
+
+# Here if no make argument established
+
+missing: osdep.h
+ $(MAKE) all `$(CAT) SPECIALS`
+
+osdep.h:
+ @echo You must specify what type of system
+ @false
+
+
+# Current ports
+
+a32: # AIX 3.2 for RS/6000
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=psx CRXTYPE=nfs \
+ SPOOLDIR=/var/spool \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="-g -Dunix=1 -D_BSD" \
+ BASELDFLAGS="-lbsd"
+
+a41: # AIX 4.1 for RS/6000
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=psx CHECKPW=a41 CRXTYPE=nfs \
+ SPOOLDIR=/var/spool \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="-g -Dunix=1 -D_BSD -qro -qroconst" \
+ BASELDFLAGS="-ls"
+
+a52: # Attempt at AIX 5.2
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=psx CHECKPW=a41 CRXTYPE=nfs \
+ SPOOLDIR=/var/spool \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="-g -Dunix=1 -D_BSD -qro -qroconst" \
+ BASELDFLAGS="-ls"
+
+aix: # AIX/370
+ @echo You are building for AIX on an S/370 class machine
+ @echo If you want AIX on an RS/6000 you need to use a32 or a41 instead!
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ CRXTYPE=nfs \
+ BASECFLAGS="-g" \
+ BASELDFLAGS="-lbsd"
+
+aos: # AOS for RT
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ CRXTYPE=nfs \
+ BASECFLAGS="-g -Dconst="
+
+art: # AIX 2.2.1 for RT
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=sv4 \
+ SPOOLDIR=/var \
+ ACTIVEFILE=/usr/local/news/control/active \
+ RSHPATH=/bin/rsh \
+ BASECFLAGS="-g -Dconst= -Dvoid=char" \
+ RANLIB=true
+
+asv: # Altos SVR4
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=sv4 LOGINPW=old \
+ ACTIVEFILE=/usr/spool/news/active \
+ RSHPATH=/usr/bin/rcmd \
+ BASECFLAGS="-Dconst= -DSIGSTOP=SIGKILL" \
+ BASELDFLAGS="-lsocket -lrpc -lgen -lcrypt -lxenix" \
+ RANLIB=true
+
+aux: # A/UX
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ CRXTYPE=nfs \
+ MAILSPOOL=/usr/mail \
+ BASECFLAGS="-g -B/usr/lib/big/ -Dvoid=char -Dconst=" \
+ RANLIB=true ARRC="ar -rc"
+
+bs3: # BSD/i386 3.0 or higher
+ $(BUILD) `$(CAT) SPECIALS` OS=bsi \
+ CHECKPW=bsi LOGINPW=bsi CRXTYPE=nfs \
+ SPOOLDIR=/var NEWSSPOOL=/var/news/spool \
+ ACTIVEFILE=/var/news/etc/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="$(GCCCFLAGS)" CC=shlicc
+
+bsd: # BSD UNIX
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ CRXTYPE=nfs \
+ BASECFLAGS="-g -Dconst="
+
+bsf: # FreeBSD
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=psx CRXTYPE=nfs \
+ SPOOLDIR=/var \
+ ACTIVEFILE=/usr/local/news/lib/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="$(GCCCFLAGS)" \
+ BASELDFLAGS="-lcrypt"
+
+bsi: # BSD/i386
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ CRXTYPE=nfs \
+ SPOOLDIR=/var NEWSSPOOL=/var/news/spool \
+ ACTIVEFILE=/var/news/etc/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="$(GCCCFLAGS)"
+
+bso: # OpenBSD
+ $(BUILD) `$(CAT) SPECIALS` OS=bsi \
+ SIGTYPE=psx CRXTYPE=nfs \
+ SPOOLDIR=/var \
+ ACTIVEFILE=/usr/local/news/lib/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="$(GCCCFLAGS)"
+
+cvx: # Convex
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ CRXTYPE=nfs \
+ BASECFLAGS="-O -ext -Dconst="
+
+cyg: # Cygwin - note that most local file drivers don't work!!
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ DEFAULTDRIVERS="imap nntp pop3 mbx unix phile" \
+ SIGTYPE=psx CHECKPW=cyg LOGINPW=cyg CRXTYPE=std \
+ SPOOLDIR=/var \
+ ACTIVEFILE=/usr/local/news/lib/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="$(GCCCFLAGS)" \
+ BASELDFLAGS="-lcrypt" \
+ CC=gcc
+
+d-g: # Data General DG/UX
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=sv4 CRXTYPE=nfs \
+ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \
+ ACTIVEFILE=/local/news/active \
+ RSHPATH=/usr/bin/remsh \
+ BASECFLAGS="-g -Dconst=" \
+ BASELDFLAGS="-lnsl -lsocket" \
+ RANLIB=true
+
+d54: # Data General DG/UX 5.4
+ $(BUILD) `$(CAT) SPECIALS` OS=d-g \
+ SIGTYPE=sv4 CRXTYPE=nfs \
+ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \
+ ACTIVEFILE=/local/news/active \
+ RSHPATH=/usr/bin/remsh \
+ BASECFLAGS="-g -Dconst=" \
+ BASELDFLAGS="-lnsl -lsocket" \
+ RANLIB=true
+
+dpx: # Bull DPX/2
+ $(BUILD) `$(CAT) SPECIALS` OS=sv4 \
+ SIGTYPE=sv4 CHECKPW=sv4 LOGINPW=sv4 \
+ RSHPATH=/usr/bin/remsh \
+ BASECFLAGS="-Dconst= -DSYSTEM5 -DSHORT_IDENT" \
+ BASELDFLAGS="-linet" \
+ RANLIB=true LN=ln
+
+drs: # ICL DRS/NX
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=sv4 CHECKPW=sv4 LOGINPW=sv4 CRXTYPE=nfs \
+ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \
+ ACTIVEFILE=/var/lib/news/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="-O" \
+ BASELDFLAGS="-lsocket -lgen" \
+ RANLIB=true
+
+do4: # Apollo Domain/OS sr10.4
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ CRXTYPE=nfs \
+ BASECFLAGS="-A systype,bsd4.3 -D_APOLLO_SOURCE" \
+ RANLIB=true
+
+dyn: # Dynix
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ CRXTYPE=nfs \
+ BASECFLAGS="-g -Dconst="
+
+epx: # EP/IX
+ $(BUILD) `$(CAT) SPECIALS` OS=sv4 \
+ SIGTYPE=sv4 CHECKPW=sv4 LOGINPW=sv4 \
+ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \
+ ACTIVEFILE=/usr/share/news/active \
+ RSHPATH=/usr/net/rsh \
+ BASECFLAGS="-g -systype svr4" \
+ BASELDFLAGS="-lsocket -lnsl -lgen" \
+ RANLIB=true
+
+ga4: # GCC AIX 4.1 for RS/6000
+ $(BUILD) `$(CAT) SPECIALS` OS=a41 \
+ SIGTYPE=psx CHECKPW=a41 CRXTYPE=nfs \
+ SPOOLDIR=/var/spool \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="-g -Dunix=1 -D_BSD" \
+ BASELDFLAGS="-ls"
+
+gas: # GCC Altos SVR4
+ $(BUILD) `$(CAT) SPECIALS` OS=asv \
+ SIGTYPE=sv4 LOGINPW=old \
+ ACTIVEFILE=/usr/spool/news/active \
+ RSHPATH=/usr/bin/rcmd \
+ BASECFLAGS="-g -O -DALTOS_SYSTEM_V -DSIGSTOP=SIGKILL" \
+ BASELDFLAGS="-lsocket -lrpc -lgen -lcrypt -lxenix" \
+ RANLIB=true CC=gcc
+
+gh9: # GCC HP-UX 9.x
+ $(BUILD) `$(CAT) SPECIALS` OS=hpp \
+ SIGTYPE=psx CRXTYPE=nfs \
+ MAILSPOOL=/usr/mail \
+ RSHPATH=/usr/bin/remsh \
+ BASECFLAGS="$(GCCCFLAGS)" \
+ RANLIB=true CC=gcc
+
+ghp: # GCC HP-UX 10.x
+ $(BUILD) `$(CAT) SPECIALS` OS=hpp \
+ SIGTYPE=psx CRXTYPE=nfs \
+ SPOOLDIR=/var \
+ ACTIVEFILE=/var/news/active \
+ RSHPATH=/usr/bin/remsh \
+ BASECFLAGS="$(GCCCFLAGS)" \
+ RANLIB=true CC=gcc
+
+ghs: # GCC HP-UX with Trusted Computer Base
+ $(BUILD) `$(CAT) SPECIALS` OS=shp \
+ SIGTYPE=psx CHECKPW=sec CRXTYPE=nfs \
+ SPOOLDIR=/var \
+ ACTIVEFILE=/var/news/active \
+ RSHPATH=/usr/bin/remsh \
+ BASECFLAGS="$(GCCCFLAGS)" \
+ BASELDFLAGS="-lnet -lV3 -lsec" \
+ RANLIB=true CC=gcc
+
+go5: # GCC 2.7.1 (95q4) SCO Open Server 5.0.x
+ $(BUILD) `$(CAT) SPECIALS` OS=sc5 \
+ SIGTYPE=psx CHECKPW=sec LOGINPW=sec \
+ CREATEPROTO=mmdfproto EMPTYPROTO=mmdfproto \
+ SPOOLDIR=/var/spool \
+ ACTIVEFILE=/var/lib/news/active \
+ RSHPATH=/usr/bin/rcmd \
+ BASECFLAGS="$(GCCCFLAGS) -I/usr/include -L/lib" \
+ BASELDFLAGS="-lsocket -lprot -lx -ltinfo -lm" \
+ RANLIB=true CC=gcc
+
+gsc: # Santa Cruz Operation
+ $(BUILD) `$(CAT) SPECIALS` OS=sco \
+ SIGTYPE=sv4 CHECKPW=sec LOGINPW=sec \
+ CREATEPROTO=mmdfproto EMPTYPROTO=mmdfproto \
+ RSHPATH=/usr/bin/rcmd \
+ BASECFLAGS="$(GCCCFLAGS)" \
+ BASELDFLAGS="-lsocket -lprot -lcrypt_i -lx -los" \
+ RANLIB=true LN=ln CC=gcc
+
+gsg: # GCC Silicon Graphics
+ $(BUILD) `$(CAT) SPECIALS` OS=sgi \
+ SIGTYPE=sv4 CRXTYPE=nfs \
+ MAILSPOOL=/usr/mail \
+ RSHPATH=/usr/bsd/rsh \
+ BASECFLAGS="$(GCCCFLAGS)" \
+ RANLIB=true CC=gcc
+
+gso: os_sol.h # GCC Solaris
+ $(BUILD) `$(CAT) SPECIALS` OS=sol \
+ SIGTYPE=psx CHECKPW=psx CRXTYPE=nfs \
+ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \
+ ACTIVEFILE=/usr/share/news/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="$(GCCCFLAGS)" \
+ BASELDFLAGS="-lsocket -lnsl -lgen" \
+ RANLIB=true CC=gcc
+
+gsu: # GCC SUN-OS
+ $(BUILD) `$(CAT) SPECIALS` OS=sun \
+ CRXTYPE=nfs \
+ BASECFLAGS="$(GCCCFLAGS)" \
+ BASELDFLAGS="-ldl" \
+ CC=gcc
+
+gul: # GCC Ultrix
+ $(BUILD) `$(CAT) SPECIALS` OS=ult \
+ SIGTYPE=psx CHECKPW=ult CRXTYPE=nfs \
+ BASECFLAGS="$(GCCCFLAGS)" \
+ BASELDFLAGS="-lauth -lc" \
+ CC=gcc
+
+h11: # HP-UX 11i
+ $(BUILD) `$(CAT) SPECIALS` OS=hpp \
+ SIGTYPE=psx CRXTYPE=nfs \
+ SPOOLDIR=/var \
+ ACTIVEFILE=/var/news/active \
+ RSHPATH=/usr/bin/remsh \
+ BASECFLAGS="-g -Ae" \
+ RANLIB=true
+
+hpp: # HP-UX 9.x
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=psx CRXTYPE=nfs \
+ MAILSPOOL=/usr/mail \
+ RSHPATH=/usr/bin/remsh \
+ BASECFLAGS="-g -Aa -D_HPUX_SOURCE" \
+ BASELDFLAGS="-lnet -lV3" \
+ RANLIB=true
+
+hpx: # HP-UX 10.x
+ $(BUILD) `$(CAT) SPECIALS` OS=hpp \
+ SIGTYPE=psx CRXTYPE=nfs \
+ SPOOLDIR=/var \
+ ACTIVEFILE=/var/news/active \
+ RSHPATH=/usr/bin/remsh \
+ BASECFLAGS="-g -Ae" \
+ BASELDFLAGS="-lnet -lV3" \
+ RANLIB=true
+
+isc: # Interactive
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=sv4 CHECKPW=sv4 LOGINPW=sv4 \
+ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \
+ ACTIVEFILE=/var/spool/news/active \
+ BASECFLAGS="-Xp -D_SYSV3" \
+ BASELDFLAGS="-linet -lnsl_s -lgen -lx -lsec -liberty" \
+ RANLIB=true
+
+lnp: # Linux Pluggable Authentication modules
+ $(BUILD) `$(CAT) SPECIALS` OS=slx \
+ SIGTYPE=psx CHECKPW=pam CRXTYPE=nfs \
+ SPOOLDIR=/var/spool \
+ ACTIVEFILE=/var/lib/news/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="$(GCCCFLAGS)" \
+ BASELDFLAGS="$(PAMLDFLAGS)"
+
+lnx: # Linux non-shadow passwords
+ @echo You are building for traditional Linux *without* shadow
+ @echo passwords and with the crypt function in the C library.
+ @echo If your system has shadow passwords, or if crypt is not
+ @echo in the C library, you must use slx, sl4, or sl5 instead!
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=psx CRXTYPE=nfs \
+ SPOOLDIR=/var/spool \
+ ACTIVEFILE=/var/lib/news/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="$(GCCCFLAGS)"
+
+lyn: # LynxOS
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ CRXTYPE=nfs \
+ RSHPATH=/bin/rsh \
+ BASECFLAGS="$(GCCCFLAGS)" \
+ BASELDFLAGS=-lbsd \
+ CC=gcc
+
+mct: # MachTen - CRXTYPE=nfs doesn't work (at least not on 2.2)
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SPOOLDIR=/var/spool \
+ BASECFLAGS="$(GCCCFLAGS)"
+
+mnt: # Mint
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=psx CRXTYPE=nfs \
+ SPOOLDIR=/var/spool \
+ ACTIVEFILE=/var/lib/news/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="$(GCCCFLAGS)"
+
+neb: # NetBSD
+ $(BUILD) `$(CAT) SPECIALS` OS=bsi \
+ CRXTYPE=nfs \
+ SPOOLDIR=/var \
+ ACTIVEFILE=/var/db/news/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="$(GCCCFLAGS)" \
+ BASELDFLAGS="-lcrypt"
+
+nec: # NEC UX
+ $(BUILD) `$(CAT) SPECIALS` OS=sv4 \
+ SIGTYPE=sv4 CHECKPW=sv4 \
+ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \
+ ACTIVEFILE=/var/news/lib/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="-g -Kopt=2 -KOlimit=2000" \
+ BASELDFLAGS="-lsocket -lnsl -lgen" \
+ RANLIB=true CC=/usr/abiccs/bin/cc
+
+nto: # QNX Neutrino RTP
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ CRXTYPE=nfs \
+ SPOOLDIR=/var/spool \
+ ACTIVEFILE=/var/lib/news/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="-g -O"
+
+nxt: # NEXTSTEP
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ CRXTYPE=nfs \
+ BASECFLAGS="$(GCCCFLAGS)"
+
+nx3: # NEXTSTEP 3.0 single threaded
+ $(BUILD) `$(CAT) SPECIALS` OS=nxt \
+ CRXTYPE=nfs \
+ BASECFLAGS="$(GCCCFLAGS)"
+ echo "void malloc_singlethreaded (void);" >> linkage.h
+ echo " malloc_singlethreaded ();" >> linkage.c
+
+osf: # OSF/1
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=psx CRXTYPE=nfs \
+ SPOOLDIR=/var/spool \
+ BASECFLAGS="-g3 -w -O2 -Olimit 1500"
+
+# Note: sia_become_user() used by LOGINPW=os4 doesn't seem to work right. The
+# user doesn't get proper file access, and the process can't be killed.
+
+os4: # OSF/1 (Digital UNIX) 4
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=psx CHECKPW=os4 LOGINPW=os4 CRXTYPE=nfs \
+ SPOOLDIR=/var/spool \
+ BASECFLAGS="-g3 -w -std0 -O2"
+
+osx: # Mac OS X
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ CRXTYPE=nfs \
+ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="$(GCC4CFLAGS)"
+
+ptx: # PTX
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=psx CHECKPW=svo LOGINPW=sv4 CRXTYPE=nfs \
+ MAILSPOOL=/usr/mail \
+ RSHPATH=/usr/bin/resh \
+ BASECFLAGS="-Wc,-O3 -Wc,-seq -Dprivate=PRIVATE" \
+ BASELDFLAGS="-lseq -lsec -lsocket -linet -lnsl -lgen" \
+ RANLIB=true
+
+pyr: # Pyramid
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ CRXTYPE=nfs \
+ BASECFLAGS="-g -Dconst="
+
+qnx: # QNX
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ CHECKPW=psx LOGINPW=old \
+ RSHPATH=/usr/ucb/rsh \
+ BASECFLAGS="-Otax -g -Dunix=1 -D_POSIX_SOURCE" \
+ BASELDFLAGS="-g -N128k -llogin -lsocket -lunix"
+
+s40: # SUN-OS 4.0
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ CRXTYPE=nfs \
+ BASECFLAGS="-g -Dconst="
+
+sc5: # SCO Open Server 5.0
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=psx CHECKPW=sec LOGINPW=sec \
+ CREATEPROTO=mmdfproto EMPTYPROTO=mmdfproto \
+ SPOOLDIR=/var/spool \
+ ACTIVEFILE=/var/lib/news/active \
+ RSHPATH=/usr/bin/rcmd \
+ BASECFLAGS="-O3 -s -belf" \
+ BASELDFLAGS="-lsocket -lprot -lx -ltinfo -lm" \
+ RANLIB=true
+
+sco: # Santa Cruz Operation
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=sv4 CHECKPW=sec LOGINPW=sec \
+ CREATEPROTO=mmdfproto EMPTYPROTO=mmdfproto \
+ RSHPATH=/usr/bin/rcmd \
+ BASECFLAGS="-O3" \
+ BASELDFLAGS="-lsocket -lprot -lcrypt_i -lx -los" \
+ RANLIB=true LN=ln
+
+# Note: setting _POSIX_SOURCE doesn't seem to build it as of SGI IRIX 5.3
+
+sgi: # Silicon Graphics
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=sv4 CRXTYPE=nfs \
+ MAILSPOOL=/usr/mail \
+ RSHPATH=/usr/bsd/rsh \
+ BASECFLAGS="-g3 -O2 -Olimit 8192" \
+ RANLIB=true
+
+sg6: # Silicon Graphics, IRIX 6.5
+ MAKEFLAGS= $(BUILD) `$(CAT) SPECIALS` OS=sgi \
+ SIGTYPE=sv4 CRXTYPE=nfs \
+ MAILSPOOL=/usr/mail \
+ RSHPATH=/usr/bsd/rsh \
+ BASECFLAGS="-g3 -O2 -OPT:Olimit=0 -woff 1110,1116" \
+ RANLIB=true
+
+# Note: Mark Kaesling says that setluid() isn't in HP-UX with SecureWare.
+
+shp: # HP-UX with Trusted Computer Base
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=psx CHECKPW=sec CRXTYPE=nfs \
+ SPOOLDIR=/var \
+ ACTIVEFILE=/var/news/active \
+ RSHPATH=/usr/bin/remsh \
+ BASECFLAGS="-g -Ae" \
+ BASELDFLAGS="-lnet -lV3 -lsec" \
+ RANLIB=true
+
+slx: # Secure Linux
+ @echo You are building for libc6/glibc versions of Secure Linux
+ @echo If you want libc5 versions you must use sl5 instead!
+ @echo If you want libc4 versions you must use sl4 instead!
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=psx CHECKPW=psx CRXTYPE=nfs \
+ SPOOLDIR=/var/spool \
+ ACTIVEFILE=/var/lib/news/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="$(GCCCFLAGS)" \
+ BASELDFLAGS="-lcrypt"
+
+sl4: # Secure Linux using libc4
+ @echo You are building for libc4 versions of Secure Linux
+ @echo If you want libc6/glibc versions you must use slx instead!
+ @echo If you want libc5 versions you must use sl5 instead!
+ $(BUILD) `$(CAT) SPECIALS` OS=slx \
+ SIGTYPE=psx CHECKPW=psx CRXTYPE=nfs \
+ SPOOLDIR=/var/spool \
+ ACTIVEFILE=/var/lib/news/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="$(GCCCFLAGS)" \
+ BASELDFLAGS="-lshadow"
+
+sl5: # Secure Linux using libc5
+ @echo You are building for libc5 versions of Secure Linux
+ @echo If you want libc6/glibc versions you must use slx instead!
+ @echo If you want libc4 versions you must use sl4 instead!
+ $(BUILD) `$(CAT) SPECIALS` OS=slx \
+ SIGTYPE=psx CHECKPW=psx CRXTYPE=nfs \
+ SPOOLDIR=/var/spool \
+ ACTIVEFILE=/var/lib/news/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="$(GCCCFLAGS)"
+
+snx: # Siemens Nixdorf SINIX and Reliant UNIX
+ $(BUILD) `$(CAT) SPECIALS` OS=sv4 \
+ SIGTYPE=psx CHECKPW=sv4 \
+ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \
+ ACTIVEFILE=/usr/share/news/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="-g -D_SYS_CLOCK_H -Dconst=" \
+ BASELDFLAGS="-lsocket -lnsl -lgen" \
+ RANLIB=true
+
+# Sorry about the -w, but the cretinous SUN Workshop Pro C compiler barfs on
+# implicit casts between char and unsigned char.
+
+soc: os_sol.h # Solaris with cc
+ $(BUILD) `$(CAT) SPECIALS` OS=sol \
+ SIGTYPE=psx CHECKPW=psx CRXTYPE=nfs \
+ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \
+ ACTIVEFILE=/usr/share/news/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="-g -O -w" \
+ BASELDFLAGS="-lsocket -lnsl -lgen" \
+ RANLIB=true CC=/opt/SUNWspro/bin/cc
+
+
+# Note: It is a long and disgusting story about why cc is set to ucbcc. You
+# need to invoke the C compiler so that it links with the SVR4 libraries and
+# not the BSD libraries, otherwise readdir() will return the wrong information.
+# Of all the names in the most common path, ucbcc is the only name to be found
+# (on /usr/ccs/bin) that points to a suitable compiler. cc is likely to be
+# /usr/ucb/cc which is absolutely not the compiler that you want. The real
+# SVR4 cc is probably something like /opt/SUNWspro/bin/cc which is rarely in
+# anyone's path.
+#
+# ucbcc is probably a link to acc, e.g. /opt/SUNWspro/SC4.0/bin/acc, and is
+# the UCB C compiler using the SVR4 libraries.
+#
+# If ucbcc isn't on your system, then punt on the SUN C compiler and use gcc
+# instead (the gso port instead of the sol port).
+#
+# If, in spite of all the above warnings, you choose to use the "soc" port
+# instead of the "sol" port, be sure to check the behavior of the LIST command
+# in imapd. Also, note that the "soc" port uses -O. If you want to use the
+# real SVR4 compiler, you must use -O. If it works to compile with -O2, then
+# cc is probably using the UCB compiler with BSD libraries, and will not build
+# a good binary
+#
+# To recap:
+# 1) The sol port is designed to be built using the UCB compiler using the
+# SVR4 libraries. This compiler is "ucbcc", which is lunk to acc. You
+# use -O2 as one of the CFLAGS.
+# 2) If you build the sol port with the UCB compiler using the BSD libraries,
+# you will get no error messages but you will get bad binaries (the most
+# obvious symptom is dropping the first two characters return filenames
+# from the imapd LIST command. This compiler also uses -O2, and is very
+# often what the user gets from "cc". BEWARE!!!
+# 3) If you build the sol port with the real SVR4 compiler, which is often
+# hidden away or unavailable on many systems, then you will get errors
+# from -O2 and you need to change that to -O. But you will get a good
+# binary. However, you should try it with -O2 first, to make sure that
+# you got this compiler and not the UCB compiler using BSD libraries.
+
+sol: os_sol.h # Solaris
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=psx CHECKPW=psx CRXTYPE=nfs \
+ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \
+ ACTIVEFILE=/usr/share/news/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="-g -O2" \
+ BASELDFLAGS="-lsocket -lnsl -lgen" \
+ RANLIB=true CC=ucbcc
+
+sos: # Secure OSF/1
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=psx CHECKPW=sce LOGINPW=sec CRXTYPE=nfs \
+ BASECFLAGS="-g3 -w -O2 -Olimit 1500" \
+ BASELDFLAGS="-lsecurity -laud"
+
+ssn: # Secure SUN-OS
+ $(BUILD) `$(CAT) SPECIALS` OS=sun \
+ CHECKPW=ssn CRXTYPE=nfs \
+ BASECFLAGS="-g -Dconst=" \
+ BASELDFLAGS="-ldl"
+
+sua: # Windows Vista Subsystem for UNIX Applications
+ $(BUILD) `$(CAT) SPECIALS` OS=sua \
+ SIGTYPE=psx CRXTYPE=nfs LOGINPW=old \
+ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \
+ ACTIVEFILE=/var/lib/news/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="-g -O2" \
+ BASELDFLAGS="-lcrypt"
+
+sun: # SUN-OS
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ CRXTYPE=nfs \
+ BASECFLAGS="-g -Dconst=" \
+ BASELDFLAGS="-ldl"
+
+sv2: # SVR2
+ @echo You are being *very* optimistic!
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=sv4 LOGINPW=old \
+ MAILSPOOL=/usr/mail \
+ RSHPATH=/usr/bin/remsh \
+ BASECFLAGS="-Dconst= -DSYSTEM5 -DSHORT_IDENT -I/usr/ethernet/include" \
+ BASELDFLAGS="-lnet" \
+ RANLIB=true LN=ln
+
+sv4: # SVR4
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=sv4 CHECKPW=sv4 LOGINPW=sv4 \
+ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \
+ ACTIVEFILE=/usr/share/news/active \
+ RSHPATH=/usr/bin/resh \
+ BASECFLAGS="-g -Dconst=" \
+ BASELDFLAGS="-lsocket -lnsl -lgen" \
+ RANLIB=true
+
+ult: # Ultrix
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ SIGTYPE=psx CHECKPW=ult CRXTYPE=nfs \
+ BASECFLAGS="-g3 -O2 -Olimit 1500 -Dconst=" \
+ BASELDFLAGS="-lauth -lc"
+
+uw2: # UnixWare SVR4.2
+ $(BUILD) `$(CAT) SPECIALS` OS=sv4 \
+ SIGTYPE=sv4 CHECKPW=sv4 \
+ SPOOLDIR=/var/spool MAILSPOOL=/var/mail \
+ ACTIVEFILE=/var/news/lib/active \
+ RSHPATH=/usr/bin/rsh \
+ BASECFLAGS="-g" \
+ BASELDFLAGS="-lsocket -lnsl -lgen" \
+ RANLIB=true
+
+vul: # VAX Ultrix
+ $(BUILD) `$(CAT) SPECIALS` OS=ult \
+ SIGTYPE=psx CHECKPW=ult CRXTYPE=nfs \
+ BASECFLAGS="-O2 -Dconst=" \
+ BASELDFLAGS="-lauth -lc"
+
+vu2: # VAX Ultrix 2.3, etc.
+ $(BUILD) `$(CAT) SPECIALS` OS=$@ \
+ CRXTYPE=nfs \
+ BASECFLAGS="-O2 -Dconst= -Dvoid=char"
+
+
+# Build it!
+
+build: clean once $(ARCHIVE)
+
+all: $(ARCHIVE)
+
+$(ARCHIVE): $(BINARIES)
+ sh -c '$(RM) $(ARCHIVE) || true'
+ @$(CAT) ARCHIVE
+ @$(SH) ARCHIVE
+
+.c.o:
+ `$(CAT) CCTYPE` -c `$(CAT) CFLAGS` $*.c
+
+
+# Cleanup
+
+clean:
+ sh -c '$(RM) auths.c crexcl.c ip_unix.c linkage.[ch] siglocal.c osdep*.[ch] *.o ARCHIVE *FLAGS *TYPE $(ARCHIVE) || true'
+
+
+# Dependencies
+
+dummy.o: mail.h misc.h osdep.h dummy.h
+fdstring.o: mail.h misc.h osdep.h fdstring.h
+flstring.o: mail.h misc.h osdep.h flstring.h
+imap4r1.o: mail.h misc.h osdep.h imap4r1.h rfc822.h
+mail.o: mail.h misc.h osdep.h rfc822.h linkage.h
+mbx.o: mail.h misc.h osdep.h dummy.h
+mh.o: mail.h misc.h osdep.h dummy.h
+mix.o: mail.h misc.h osdep.h dummy.h
+mx.o: mail.h misc.h osdep.h dummy.h
+misc.o: mail.h misc.h osdep.h
+mmdf.o: mail.h misc.h osdep.h pseudo.h dummy.h
+mtx.o: mail.h misc.h osdep.h dummy.h
+netmsg.o: mail.h misc.h osdep.h netmsg.h
+news.o: mail.h misc.h osdep.h
+newsrc.o: mail.h misc.h osdep.h newsrc.h
+nntp.o: mail.h misc.h osdep.h netmsg.h smtp.h nntp.h rfc822.h
+phile.o: mail.h misc.h osdep.h rfc822.h dummy.h
+pseudo.o: pseudo.h
+pop3.o: mail.h misc.h osdep.h rfc822.h
+smanager.o: mail.h misc.h osdep.h
+smtp.o: mail.h misc.h osdep.h smtp.h rfc822.h
+rfc822.o: mail.h misc.h osdep.h rfc822.h
+tenex.o: mail.h misc.h osdep.h dummy.h
+unix.o: mail.h misc.h osdep.h unix.h pseudo.h dummy.h
+utf8.o: mail.h misc.h osdep.h utf8.h tmap.c widths.c
+utf8aux.o: mail.h misc.h osdep.h utf8.h
+
+
+# OS-dependent
+
+osdep.o:mail.h misc.h env.h fs.h ftl.h nl.h tcp.h \
+ osdep.h env_unix.h tcp_unix.h \
+ osdep.c env_unix.c fs_unix.c ftl_unix.c nl_unix.c tcp_unix.c ip_unix.c\
+ auths.c crexcl.c flockcyg.c flocklnx.c flocksim.c fsync.c \
+ gethstid.c getspnam.c \
+ gr_wait.c gr_wait4.c gr_waitp.c \
+ kerb_mit.c \
+ auth_ext.c auth_gss.c auth_log.c auth_md5.c auth_pla.c \
+ pmatch.c scandir.c setpgrp.c strerror.c truncate.c write.c \
+ memmove.c memmove2.c memset.c \
+ tz_bsd.c tz_nul.c tz_sv4.c \
+ write.c sslstdio.c \
+ strerror.c strpbrk.c strstr.c strtok.c strtoul.c \
+ OSCFLAGS
+ @echo Building OS-dependent module
+ @echo If you get No such file error messages for files x509.h, ssl.h,
+ @echo pem.h, buffer.h, bio.h, and crypto.h, that means that OpenSSL
+ @echo is not installed on your system. Either install OpenSSL first
+ @echo or build with command: make `$(CAT) OSTYPE` SSLTYPE=none
+ `$(CAT) CCTYPE` -c `$(CAT) CFLAGS` `$(CAT) OSCFLAGS` -c osdep.c
+
+osdep.c: osdepbas.c osdepckp.c osdeplog.c osdepssl.c
+ $(CAT) osdepbas.c osdepckp.c osdeplog.c osdepssl.c > osdep.c
+
+osdepbas.c:
+ @echo osdepbas.c not found...try make clean and new make
+ @false
+
+osdepckp.c:
+ @echo osdepckp.c not found...try make clean and new make
+ @false
+
+osdeplog.c:
+ @echo osdeplog.c not found...try make clean and new make
+ @false
+
+osdepssl.c:
+ @echo osdepssl.c not found...try make clean and new make
+ @false
+
+siglocal.c:
+ @echo siglocal.c not found...try make clean and new make
+ @false
+
+crexcl.c:
+ @echo crexcl.c not found...do make clean and new make
+ @false
+
+ip_unix.c:
+ @echo ip_unix.c not found...do make clean and new make
+ @false
+
+os_sol.h:
+ sh -c 'if [ -f /lib/libc.a ]; then (strings /lib/libc.a | grep getpassphrase > /dev/null) && $(LN) os_soln.h os_sol.h || $(LN) os_solo.h os_sol.h ; else $(LN) os_soln.h os_sol.h ; fi'
+
+
+# Once-only environment setup
+
+once: onceenv ckp$(PASSWDTYPE) ssl$(SSLTYPE) osdep.c
+
+onceenv:
+ @echo Once-only environment setup...
+ echo $(CC) > CCTYPE
+ echo $(BASECFLAGS) '$(EXTRACFLAGS)' -DCHUNKSIZE=$(CHUNKSIZE) > CFLAGS
+ echo -DCREATEPROTO=$(CREATEPROTO) -DEMPTYPROTO=$(EMPTYPROTO) \
+ -DMD5ENABLE=\"$(MD5PWD)\" -DMAILSPOOL=\"$(MAILSPOOL)\" \
+ -DANONYMOUSHOME=\"$(MAILSPOOL)/anonymous\" \
+ -DACTIVEFILE=\"$(ACTIVEFILE)\" -DNEWSSPOOL=\"$(NEWSSPOOL)\" \
+ -DRSHPATH=\"$(RSHPATH)\" -DLOCKPGM=\"$(LOCKPGM)\" \
+ -DLOCKPGM1=\"$(LOCKPGM1)\" -DLOCKPGM2=\"$(LOCKPGM2)\" \
+ -DLOCKPGM3=\"$(LOCKPGM3)\" > OSCFLAGS
+ echo $(BASELDFLAGS) $(EXTRALDFLAGS) > LDFLAGS
+ echo "$(ARRC) $(ARCHIVE) $(BINARIES);$(RANLIB) $(ARCHIVE)" > ARCHIVE
+ echo $(OS) > OSTYPE
+ ./drivers $(EXTRADRIVERS) $(DEFAULTDRIVERS) dummy
+ ./mkauths $(EXTRAAUTHENTICATORS) $(DEFAULTAUTHENTICATORS)
+ echo " mail_versioncheck (CCLIENTVERSION);" >> linkage.c
+ $(LN) os_$(OS).h osdep.h
+ $(LN) os_$(OS).c osdepbas.c
+ $(LN) log_$(LOGINPW).c osdeplog.c
+ $(LN) sig_$(SIGTYPE).c siglocal.c
+ $(LN) crx_$(CRXTYPE).c crexcl.c
+ $(LN) ip$(IP)_unix.c ip_unix.c
+ sh -c '(test $(OS) = sc5 -o $(OS) = sco -o ! -f /usr/include/sys/statvfs.h) && echo -DNOFSTATVFS >> OSCFLAGS || fgrep statvfs64 /usr/include/sys/statvfs.h > /dev/null || echo -DNOFSTATVFS64 >> OSCFLAGS'
+
+
+# Password checkers
+
+ckpafs: # AFS
+ @echo AFS password authentication
+ echo $(AFSCFLAGS) >> OSCFLAGS
+# echo $(AFSLDFLAGS) >> LDFLAGS
+# Note: Steve Roseman says that AFS libraries have to be lunk before SSL
+ echo $(AFSLDFLAGS) `$(CAT) LDFLAGS` > LDFLAGS.tmp
+ mv LDFLAGS.tmp LDFLAGS
+ $(LN) ckp_afs.c osdepckp.c
+
+ckpdce: # DCE
+ @echo DCE password authentication
+ echo $(DCECFLAGS) >> OSCFLAGS
+ echo $(DCELDFLAGS) >> LDFLAGS
+ $(LN) ckp_dce.c osdepckp.c
+
+ckpgss: # Kerberos V (must have gss EXTRAAUTHENTICATOR as well)
+ @echo Kerberos V password authentication
+ $(LN) ckp_gss.c osdepckp.c
+
+ckpnul: # NUL authenticator (disables all plaintext authentication)
+ @echo Plaintext authentication prohibited
+ echo " mail_parameters (NIL,SET_DISABLEPLAINTEXT,(void *) 1);" >> linkage.c
+ $(LN) ckp_nul.c osdepckp.c
+
+ckppam: # Pluggable Authentication Modules authenticator
+ @echo PAM password authentication
+ echo $(PAMLDFLAGS) >> LDFLAGS
+ $(LN) ckp_pam.c osdepckp.c
+
+ckppmb: # Broken (e.g. SUN) Pluggable Authentication Modules authenticator
+ @echo Broken PAM password authentication
+ echo $(PAMLDFLAGS) >> LDFLAGS
+ $(LN) ckp_pmb.c osdepckp.c
+
+ckpstd: # Port standard
+ @echo Standard password authentication
+ $(LN) ckp_$(CHECKPW).c osdepckp.c
+
+ckptwo: # Something plus standard
+ @echo $(CHECKPWALT) password authentication first, then standard
+ $(CAT) ckp_1st.c ckp_$(CHECKPWALT).c ckp_2nd.c ckp_$(CHECKPW).c \
+ ckp_3rd.c > osdepckp.c
+
+
+# SSL support
+
+sslnone:# No SSL
+ @echo Building without SSL support
+ $(LN) ssl_none.c osdepssl.c
+
+sslnopwd: sslunix snopwd
+
+sslunix.nopwd: sslnopwd
+
+sslsco.nopwd: sslsco snopwd
+
+sslunix: sbasic sldunix
+
+sslsco: sbasic sldsco
+
+sbasic: # UNIX OpenSSL
+ @echo Building with SSL
+ $(LN) ssl_unix.c osdepssl.c
+ echo $(SSLCFLAGS) >> OSCFLAGS
+ echo " ssl_onceonlyinit ();" >> linkage.c
+
+snopwd: # Plaintext disable
+ @echo Building with SSL and plaintext passwords disabled unless SSL/TLS
+ echo " mail_parameters (NIL,SET_DISABLEPLAINTEXT,(void *) 2);" >> linkage.c
+
+sldunix:# Normal UNIX SSL load flags
+ echo $(SSLLDFLAGS) >> LDFLAGS
+
+
+sldsco: # SCO SSL load flags
+# Note: Tim Rice says that SSL has to be lunk before other libraries on SCO.
+ echo $(SSLLDFLAGS) `cat LDFLAGS` > LDFLAGS.tmp
+ mv LDFLAGS.tmp LDFLAGS
+
+
+# A monument to a hack of long ago and far away...
+
+love:
+ @echo not war?
diff --git a/imap/src/osdep/unix/Makefile.gss b/imap/src/osdep/unix/Makefile.gss
new file mode 100644
index 00000000..440647e9
--- /dev/null
+++ b/imap/src/osdep/unix/Makefile.gss
@@ -0,0 +1,39 @@
+# ========================================================================
+# Copyright 1988-2007 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+# Program: GSSAPI makefile
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 11 May 1989
+# Last Edited: 4 April 2007
+
+
+# Extended flags needed for additional authenticators. You may need to modify.
+
+GSSDIR=/usr/local
+GSSINCLUDE=$(GSSDIR)/include
+GSSLIB=$(GSSDIR)/lib
+GSSCFLAGS= -I$(GSSINCLUDE) -DGSS_C_NT_HOSTBASED_SERVICE=gss_nt_service_name -DKRB5_DEPRECATED=1
+GSSOLDLDFLAGS= -L$(GSSLIB) -lgssapi_krb5 -lkrb5 -lcrypto -lcom_err
+GSSNEWLDFLAGS= -L$(GSSLIB) -lgssapi_krb5 -lkrb5 -lk5crypto -lcom_err
+
+gss: # GSSAPI Kerberos V flags
+ echo $(GSSCFLAGS) >> OSCFLAGS
+ sh -c '(test -f $(GSSLIB)/libk5crypto.a) && echo $(GSSNEWLDFLAGS) || echo $(GSSOLDLDFLAGS)' >> LDFLAGS
+ echo "#include \"kerb_mit.c\"" >> auths.c
diff --git a/imap/src/osdep/unix/ckp_1st.c b/imap/src/osdep/unix/ckp_1st.c
new file mode 100644
index 00000000..d7769b9d
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_1st.c
@@ -0,0 +1,53 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dual check password part 1
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+struct passwd *checkpw_alt(struct passwd *pw,char *pass,int argc,char *argv[]);
+struct passwd *checkpw_std(struct passwd *pw,char *pass,int argc,char *argv[]);
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ struct passwd *ret;
+ /* in case first checker smashes it */
+ char *user = cpystr (pw->pw_name);
+ if (!(ret = checkpw_alt (pw,pass,argc,argv)))
+ ret = checkpw_std (getpwnam (user),pass,argc,argv);
+ fs_give ((void **) &user);
+ return ret;
+}
+
+/* Redefine alt checker's routine name */
+
+#define checkpw checkpw_alt
diff --git a/imap/src/osdep/unix/ckp_2nd.c b/imap/src/osdep/unix/ckp_2nd.c
new file mode 100644
index 00000000..5207b5d6
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_2nd.c
@@ -0,0 +1,36 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dual check password part 2
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Undefine routine name */
+
+#undef checkpw
+
+/* Redefine std checker's routine name */
+
+#define checkpw checkpw_std
diff --git a/imap/src/osdep/unix/ckp_3rd.c b/imap/src/osdep/unix/ckp_3rd.c
new file mode 100644
index 00000000..c441771a
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_3rd.c
@@ -0,0 +1,32 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dual check password part 3
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Undefine routine name */
+
+#undef checkpw
diff --git a/imap/src/osdep/unix/ckp_a41.c b/imap/src/osdep/unix/ckp_a41.c
new file mode 100644
index 00000000..3018c24c
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_a41.c
@@ -0,0 +1,52 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: AIX 4.1 check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include <login.h>
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ int reenter = 0;
+ char *msg = NIL;
+ char *user = cpystr (pw->pw_name);
+ /* validate password */
+ struct passwd *ret = (pw && !loginrestrictions (user,S_RLOGIN,NIL,&msg) &&
+ !authenticate (user,pass,&reenter,&msg)) ?
+ getpwnam (user) : NIL;
+ /* clean up any message returned */
+ if (msg) fs_give ((void **) &msg);
+ if (user) fs_give ((void **) &user);
+ return ret;
+}
diff --git a/imap/src/osdep/unix/ckp_afs.c b/imap/src/osdep/unix/ckp_afs.c
new file mode 100644
index 00000000..2f3fa6ba
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_afs.c
@@ -0,0 +1,71 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: AFS check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* AFS cleanup
+ * Accepts: data
+ */
+
+void checkpw_cleanup (void *data)
+{
+ ktc_ForgetAllTokens ();
+}
+
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+#undef INIT
+#define min AFS_MIN
+#define max AFS_MAX
+#include <afs/param.h>
+#include <afs/kautils.h>
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ char *reason;
+ /* faster validation for POP servers */
+ if (!strcmp ((char *) mail_parameters (NIL,GET_SERVICENAME,NIL),"pop")) {
+ struct ktc_encryptionKey key;
+ struct ktc_token token;
+ /* just check the password */
+ ka_StringToKey (pass,NIL,&key);
+ if (ka_GetAdminToken (pw->pw_name,"","",&key,600,&token,1)) return NIL;
+ }
+ /* check password and get AFS token */
+ else if (ka_UserAuthenticateGeneral
+ (KA_USERAUTH_VERSION + KA_USERAUTH_DOSETPAG,pw->pw_name,NIL,NIL,
+ pass,0,0,0,&reason)) return NIL;
+ /* arm hook to delete credentials */
+ mail_parameters (NIL,SET_LOGOUTHOOK,(void *) checkpw_cleanup);
+ return pw;
+}
diff --git a/imap/src/osdep/unix/ckp_bsi.c b/imap/src/osdep/unix/ckp_bsi.c
new file mode 100644
index 00000000..128f23ab
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_bsi.c
@@ -0,0 +1,50 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: BSI check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+#include <login_cap.h>
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ char *s,tmp[MAILTMPLEN];
+ login_cap_t *lc;
+ /* make service name */
+ sprintf (tmp,"auth-%s",(char *) mail_parameters (NIL,GET_SERVICENAME,NIL));
+ /* validate password */
+ return ((lc = login_getclass (pw->pw_class)) &&
+ (s = login_getstyle (lc,NIL,tmp)) &&
+ (auth_response (pw->pw_name,lc->lc_class,s,"response",NIL,"",pass)
+ > 0)) ? pw : NIL;
+}
diff --git a/imap/src/osdep/unix/ckp_cyg.c b/imap/src/osdep/unix/ckp_cyg.c
new file mode 100644
index 00000000..28e07394
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_cyg.c
@@ -0,0 +1,64 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Cygwin check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* This module is against my better judgement. If you want to run an imapd
+ * or ipop[23]d you should use the native NT or W2K ports intead of this
+ * Cygwin port. There is no surety that this module works right or will work
+ * right in the future.
+ */
+
+#undef ERROR
+#define NOCRYPT
+#include <windows.h>
+#include <sys/cygwin.h>
+
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+static char *cyg_user = NIL;
+static HANDLE cyg_hdl = NIL;
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ /* flush last pw-checked user */
+ if (cyg_user) fs_give ((void **) &cyg_user);
+ /* forbid if UID 0 or SYSTEM uid */
+ if (!pw->pw_uid || (pw->pw_uid == SYSTEMUID) ||
+ ((cyg_hdl = cygwin_logon_user (pw,pass)) == INVALID_HANDLE_VALUE))
+ return NIL; /* bad UID or password */
+ /* remember user for this handle */
+ cyg_user = cpystr (pw->pw_name);
+ return pw;
+}
+
diff --git a/imap/src/osdep/unix/ckp_dce.c b/imap/src/osdep/unix/ckp_dce.c
new file mode 100644
index 00000000..2a3aed0e
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_dce.c
@@ -0,0 +1,83 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: DCE check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+#include <dce/rpc.h>
+#include <dce/sec_login.h>
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ sec_passwd_rec_t pwr;
+ sec_login_handle_t lhdl;
+ boolean32 rstpwd;
+ sec_login_auth_src_t asrc;
+ error_status_t status;
+ FILE *fd;
+ /* easy case */
+ if (pw->pw_passwd && pw->pw_passwd[0] && pw->pw_passwd[1] &&
+ !strcmp (pw->pw_passwd,(char *) crypt (pass,pw->pw_passwd))) return pw;
+ /* try DCE password cache file */
+ if (fd = fopen (PASSWD_OVERRIDE,"r")) {
+ char *usr = cpystr (pw->pw_name);
+ while ((pw = fgetpwent (fd)) && strcmp (usr,pw->pw_name));
+ fclose (fd); /* finished with cache file */
+ /* validate cached password */
+ if (pw && pw->pw_passwd && pw->pw_passwd[0] && pw->pw_passwd[1] &&
+ !strcmp (pw->pw_passwd,(char *) crypt (pass,pw->pw_passwd))) {
+ fs_give ((void **) &usr);
+ return pw;
+ }
+ if (!pw) pw = getpwnam (usr);
+ fs_give ((void **) &usr);
+ }
+ if (pw) { /* try S-L-O-W DCE... */
+ sec_login_setup_identity ((unsigned_char_p_t) pw->pw_name,
+ sec_login_no_flags,&lhdl,&status);
+ if (status == error_status_ok) {
+ pwr.key.tagged_union.plain = (idl_char *) pass;
+ pwr.key.key_type = sec_passwd_plain;
+ pwr.pepper = NIL;
+ pwr.version_number = sec_passwd_c_version_none;
+ /* validate password with login context */
+ sec_login_validate_identity (lhdl,&pwr,&rstpwd,&asrc,&status);
+ if (!rstpwd && (asrc == sec_login_auth_src_network) &&
+ (status == error_status_ok)) {
+ sec_login_purge_context (&lhdl,&status);
+ if (status == error_status_ok) return pw;
+ }
+ }
+ }
+ return NIL; /* password validation failed */
+}
diff --git a/imap/src/osdep/unix/ckp_gss.c b/imap/src/osdep/unix/ckp_gss.c
new file mode 100644
index 00000000..0ef9388b
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_gss.c
@@ -0,0 +1,90 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Kerberos 5 check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 11 October 2007
+ */
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ char svrnam[MAILTMPLEN],cltnam[MAILTMPLEN];
+ krb5_context ctx;
+ krb5_timestamp now;
+ krb5_principal service;
+ krb5_ccache ccache;
+ krb5_error_code code;
+ krb5_creds *crd = (krb5_creds *) memset (fs_get (sizeof (krb5_creds)),0,
+ sizeof (krb5_creds));
+ struct passwd *ret = NIL;
+ if (*pass) { /* only if password non-empty */
+ /* make service name */
+ sprintf (svrnam,"%.80s@%.512s",
+ (char *) mail_parameters (NIL,GET_SERVICENAME,NIL),
+ tcp_serverhost ());
+ /* make client name with principal */
+ sprintf (cltnam,"%.80s/%.80s",pw->pw_name,
+ (char *) mail_parameters (NIL,GET_SERVICENAME,NIL));
+ /* get a context */
+ if (!krb5_init_context (&ctx)) {
+ /* get time, client and server principals */
+ if (!krb5_timeofday (ctx,&now) &&
+ /* Normally, kerb_cp_svr_name (defined/set in env_unix.c) is NIL, so
+ * only the user name is used as a client principal. A few sites want
+ * to have separate client principals for different services, but many
+ * other sites vehemently object...
+ */
+ !krb5_parse_name (ctx,kerb_cp_svr_name ? cltnam : pw->pw_name,
+ &crd->client) &&
+ !krb5_parse_name (ctx,svrnam,&service) &&
+ !krb5_build_principal_ext(ctx,&crd->server,
+ krb5_princ_realm (ctx,crd->client)->length,
+ krb5_princ_realm (ctx,crd->client)->data,
+ KRB5_TGS_NAME_SIZE,KRB5_TGS_NAME,
+ krb5_princ_realm (ctx,crd->client)->length,
+ krb5_princ_realm (ctx,crd->client)->data,
+ 0)) {
+ /* expire in 3 minutes */
+ crd->times.endtime = now + (3 * 60);
+ if (krb5_cc_resolve (ctx,"MEMORY:pwk",&ccache) ||
+ krb5_cc_initialize (ctx,ccache,crd->client)) ccache = 0;
+ if (!krb5_get_in_tkt_with_password (ctx,NIL,NIL,NIL,NIL,pass,ccache,
+ crd,0) &&
+ !krb5_verify_init_creds (ctx,crd,service,0,ccache ? &ccache : 0,0))
+ ret = pw;
+ krb5_free_creds (ctx,crd);/* flush creds and service principal */
+ krb5_free_principal (ctx,service);
+ }
+ krb5_free_context (ctx); /* don't need context any more */
+ }
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/unix/ckp_nul.c b/imap/src/osdep/unix/ckp_nul.c
new file mode 100644
index 00000000..153ee0ed
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_nul.c
@@ -0,0 +1,40 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Null check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 23 July 1998
+ * Last Edited: 30 August 2006
+ */
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ return NIL; /* always fails */
+}
diff --git a/imap/src/osdep/unix/ckp_os4.c b/imap/src/osdep/unix/ckp_os4.c
new file mode 100644
index 00000000..765bb146
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_os4.c
@@ -0,0 +1,78 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: OSF/1 (Digital UNIX) 4 check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Dummy collection routine
+ * Accepts: how long to wait for user
+ * how to run parameter collection
+ * title
+ * number of prompts
+ * prompts
+ * Returns: collection status
+ *
+ * Because Spider Boardman, who wrote SIA, says that it's needed for buggy SIA
+ * mechanisms, that's why.
+ */
+
+static int checkpw_collect (int timeout,int rendition,uchar_t *title,
+ int nprompts,prompt_t *prompts)
+{
+ switch (rendition) {
+ case SIAONELINER: case SIAINFO: case SIAWARNING: return SIACOLSUCCESS;
+ }
+ return SIACOLABORT; /* another else is bogus */
+}
+
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ int i;
+ char *s;
+ char *name = cpystr (pw->pw_name);
+ char *host = cpystr (tcp_clienthost ());
+ struct passwd *ret = NIL;
+ /* tie off address */
+ if (s = strchr (host,' ')) *s = '\0';
+ if (*host == '[') { /* convert [a.b.c.d] to a.b.c.d */
+ memmove (host,host+1,i = strlen (host + 2));
+ host[i] = '\0';
+ }
+ /* validate password */
+ if (sia_validate_user (checkpw_collect,argc,argv,host,name,NIL,NIL,NIL,pass)
+ == SIASUCCESS) ret = getpwnam (name);
+ fs_give ((void **) &name);
+ fs_give ((void **) &host);
+ return ret;
+}
diff --git a/imap/src/osdep/unix/ckp_pam.c b/imap/src/osdep/unix/ckp_pam.c
new file mode 100644
index 00000000..60c6c1f7
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_pam.c
@@ -0,0 +1,136 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Pluggable Authentication Modules login services
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 31 August 2006
+ */
+
+
+#ifdef MAC_OSX_KLUDGE /* why can't Apple be compatible? */
+#include <pam/pam_appl.h>
+#else
+#include <security/pam_appl.h>
+#endif
+
+struct checkpw_cred {
+ char *uname; /* user name */
+ char *pass; /* password */
+};
+
+/* PAM conversation function
+ * Accepts: number of messages
+ * vector of messages
+ * pointer to response return
+ * application data
+ * Returns: PAM_SUCCESS if OK, response vector filled in, else PAM_CONV_ERR
+ */
+
+static int checkpw_conv (int num_msg,const struct pam_message **msg,
+ struct pam_response **resp,void *appdata_ptr)
+{
+ int i;
+ struct checkpw_cred *cred = (struct checkpw_cred *) appdata_ptr;
+ struct pam_response *reply = fs_get (sizeof (struct pam_response) * num_msg);
+ for (i = 0; i < num_msg; i++) switch (msg[i]->msg_style) {
+ case PAM_PROMPT_ECHO_ON: /* assume want user name */
+ reply[i].resp_retcode = PAM_SUCCESS;
+ reply[i].resp = cpystr (cred->uname);
+ break;
+ case PAM_PROMPT_ECHO_OFF: /* assume want password */
+ reply[i].resp_retcode = PAM_SUCCESS;
+ reply[i].resp = cpystr (cred->pass);
+ break;
+ case PAM_TEXT_INFO:
+ case PAM_ERROR_MSG:
+ reply[i].resp_retcode = PAM_SUCCESS;
+ reply[i].resp = NULL;
+ break;
+ default: /* unknown message style */
+ fs_give ((void **) &reply);
+ return PAM_CONV_ERR;
+ }
+ *resp = reply;
+ return PAM_SUCCESS;
+}
+
+
+/* PAM cleanup
+ * Accepts: handle
+ */
+
+static void checkpw_cleanup (pam_handle_t *hdl)
+{
+#if 0 /* see checkpw() for why this is #if 0 */
+ pam_close_session (hdl,NIL); /* close session [uw]tmp */
+#endif
+ pam_setcred (hdl,PAM_DELETE_CRED);
+ pam_end (hdl,PAM_SUCCESS);
+}
+
+/* Server log in
+ * Accepts: user name string
+ * password string
+ * Returns: T if password validated, NIL otherwise
+ */
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ pam_handle_t *hdl;
+ struct pam_conv conv;
+ struct checkpw_cred cred;
+ char *name = cpystr (pw->pw_name);
+ conv.conv = &checkpw_conv;
+ conv.appdata_ptr = &cred;
+ cred.uname = name;
+ cred.pass = pass;
+ if (pw = ((pam_start ((char *) mail_parameters (NIL,GET_SERVICENAME,NIL),
+ pw->pw_name,&conv,&hdl) == PAM_SUCCESS) &&
+ (pam_set_item (hdl,PAM_RHOST,tcp_clientaddr ()) == PAM_SUCCESS) &&
+ (pam_authenticate (hdl,NIL) == PAM_SUCCESS) &&
+ (pam_acct_mgmt (hdl,NIL) == PAM_SUCCESS) &&
+ (pam_setcred (hdl,PAM_ESTABLISH_CRED) == PAM_SUCCESS)) ?
+ getpwnam (name) : NIL) {
+#if 0
+ /*
+ * Some people have reported that this causes a SEGV in strncpy() from
+ * pam_unix.so.1
+ */
+ /*
+ * This pam_open_session() call is inconsistant with how we handle other
+ * platforms, where we don't write [uw]tmp records. However, unlike our
+ * code on other platforms, pam_acct_mgmt() will check those records for
+ * inactivity and deny the authentication.
+ */
+ pam_open_session (hdl,NIL); /* make sure account doesn't go inactive */
+#endif
+ /* arm hook to delete credentials */
+ mail_parameters (NIL,SET_LOGOUTHOOK,(void *) checkpw_cleanup);
+ mail_parameters (NIL,SET_LOGOUTDATA,(void *) hdl);
+ }
+ else checkpw_cleanup (hdl); /* clean up */
+ fs_give ((void **) &name);
+ /* reset log facility in case PAM broke it */
+ if (myServerName) openlog (myServerName,LOG_PID,syslog_facility);
+ return pw;
+}
diff --git a/imap/src/osdep/unix/ckp_pmb.c b/imap/src/osdep/unix/ckp_pmb.c
new file mode 100644
index 00000000..c8a935cd
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_pmb.c
@@ -0,0 +1,128 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Pluggable Authentication Modules login services, buggy systems
+ * (use this instead of ckp_pam.c on Solaris)
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 31 August 2006
+ */
+
+
+#include <security/pam_appl.h>
+
+static char *pam_uname; /* user name */
+static char *pam_pass; /* password */
+
+/* PAM conversation function
+ * Accepts: number of messages
+ * vector of messages
+ * pointer to response return
+ * application data
+ * Returns: PAM_SUCCESS if OK, response vector filled in, else PAM_CONV_ERR
+ */
+
+static int checkpw_conv (int num_msg,const struct pam_message **msg,
+ struct pam_response **resp,void *appdata_ptr)
+{
+ int i;
+ struct pam_response *reply = fs_get (sizeof (struct pam_response) * num_msg);
+ for (i = 0; i < num_msg; i++) switch (msg[i]->msg_style) {
+ case PAM_PROMPT_ECHO_ON: /* assume want user name */
+ reply[i].resp_retcode = PAM_SUCCESS;
+ reply[i].resp = cpystr (pam_uname);
+ break;
+ case PAM_PROMPT_ECHO_OFF: /* assume want password */
+ reply[i].resp_retcode = PAM_SUCCESS;
+ reply[i].resp = cpystr (pam_pass);
+ break;
+ case PAM_TEXT_INFO:
+ case PAM_ERROR_MSG:
+ reply[i].resp_retcode = PAM_SUCCESS;
+ reply[i].resp = NULL;
+ break;
+ default: /* unknown message style */
+ fs_give ((void **) &reply);
+ return PAM_CONV_ERR;
+ }
+ *resp = reply;
+ return PAM_SUCCESS;
+}
+
+
+/* PAM cleanup
+ * Accepts: handle
+ */
+
+static void checkpw_cleanup (pam_handle_t *hdl)
+{
+#if 0 /* see checkpw() for why this is #if 0 */
+ pam_close_session (hdl,NIL); /* close session [uw]tmp */
+#endif
+ pam_setcred (hdl,PAM_DELETE_CRED);
+ pam_end (hdl,PAM_SUCCESS);
+}
+
+/* Server log in
+ * Accepts: user name string
+ * password string
+ * Returns: T if password validated, NIL otherwise
+ */
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ pam_handle_t *hdl;
+ struct pam_conv conv;
+ char *name = cpystr (pw->pw_name);
+ conv.conv = &checkpw_conv;
+ pam_uname = pw->pw_name;
+ pam_pass = pass;
+ if (pw = ((pam_start ((char *) mail_parameters (NIL,GET_SERVICENAME,NIL),
+ pw->pw_name,&conv,&hdl) == PAM_SUCCESS) &&
+ (pam_set_item (hdl,PAM_RHOST,tcp_clientaddr ()) == PAM_SUCCESS) &&
+ (pam_authenticate (hdl,NIL) == PAM_SUCCESS) &&
+ (pam_acct_mgmt (hdl,NIL) == PAM_SUCCESS) &&
+ (pam_setcred (hdl,PAM_ESTABLISH_CRED) == PAM_SUCCESS)) ?
+ getpwnam (name) : NIL) {
+#if 0
+ /*
+ * Some people have reported that this causes a SEGV in strncpy() from
+ * pam_unix.so.1
+ */
+ /*
+ * This pam_open_session() call is inconsistant with how we handle other
+ * platforms, where we don't write [uw]tmp records. However, unlike our
+ * code on other platforms, pam_acct_mgmt() will check those records for
+ * inactivity and deny the authentication.
+ */
+ pam_open_session (hdl,NIL); /* make sure account doesn't go inactive */
+#endif
+ /* arm hook to delete credentials */
+ mail_parameters (NIL,SET_LOGOUTHOOK,(void *) checkpw_cleanup);
+ mail_parameters (NIL,SET_LOGOUTDATA,(void *) hdl);
+ }
+ else checkpw_cleanup (hdl); /* clean up */
+ fs_give ((void **) &name);
+ /* reset log facility in case PAM broke it */
+ if (myServerName) openlog (myServerName,LOG_PID,syslog_facility);
+ return pw;
+}
diff --git a/imap/src/osdep/unix/ckp_psx.c b/imap/src/osdep/unix/ckp_psx.c
new file mode 100644
index 00000000..d8ebdaad
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_psx.c
@@ -0,0 +1,101 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: POSIX check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ char tmp[MAILTMPLEN];
+ struct spwd *sp = NIL;
+ time_t left;
+ time_t now = time (0);
+ struct tm *t = gmtime (&now);
+ int zone = t->tm_hour * 60 + t->tm_min;
+ int julian = t->tm_yday;
+ t = localtime (&now); /* get local time now */
+ /* minus UTC minutes since midnight */
+ zone = t->tm_hour * 60 + t->tm_min - zone;
+ /* julian can be one of:
+ * 36x local time is December 31, UTC is January 1, offset -24 hours
+ * 1 local time is 1 day ahead of UTC, offset +24 hours
+ * 0 local time is same day as UTC, no offset
+ * -1 local time is 1 day behind UTC, offset -24 hours
+ * -36x local time is January 1, UTC is December 31, offset +24 hours
+ */
+ if (julian = t->tm_yday -julian)
+ zone += ((julian < 0) == (abs (julian) == 1)) ? -24*60 : 24*60;
+ /* days since 1/1/1970 local time */
+ now = ((now /60) + zone) / (60*24);
+ /* non-shadow authentication */
+ if (!pw->pw_passwd || !pw->pw_passwd[0] || !pw->pw_passwd[1] ||
+ strcmp (pw->pw_passwd,(char *) crypt (pass,pw->pw_passwd))) {
+ /* As far as I've been able to determine, here is how the expiration
+ * fields in the shadow authentication data work:
+ * lstchg last password change date if non-negative. If zero, the
+ * user can not log in without changing password.
+ * max number of days a password is valid if positive
+ * warn number of days of password expiration warning
+ * expire date account expires if positive
+ * inact number of days an accout can be inactive (not checked!)
+ * The expiration day is the *last* day that the password or account
+ * is valid.
+ */
+ /* shadow authentication */
+ if ((sp = getspnam (pw->pw_name)) && sp->sp_lstchg &&
+ ((sp->sp_lstchg < 0) || (sp->sp_max <= 0) ||
+ ((sp->sp_lstchg + sp->sp_max) >= now)) &&
+ ((sp->sp_expire <= 0) || (sp->sp_expire >= now)) &&
+ sp->sp_pwdp && sp->sp_pwdp[0] && sp->sp_pwdp[1] &&
+ !strcmp (sp->sp_pwdp,(char *) crypt (pass,sp->sp_pwdp))) {
+ if ((sp->sp_lstchg > 0) && (sp->sp_max > 0) &&
+ ((left = (sp->sp_lstchg + sp->sp_max) - now) <= sp->sp_warn)) {
+ if (left) {
+ sprintf (tmp,"[ALERT] Password expires in %ld day(s)",(long) left);
+ mm_notify (NIL,tmp,NIL);
+ }
+ else mm_notify (NIL,"[ALERT] Password expires today!",WARN);
+ }
+ if ((sp->sp_expire > 0) && ((left = sp->sp_expire - now) < 28)) {
+ if (left) {
+ sprintf (tmp,"[ALERT] Account expires in %ld day(s)",(long) left);
+ mm_notify (NIL,tmp,NIL);
+ }
+ else mm_notify (NIL,"[ALERT] Account expires today!",WARN);
+ }
+ endspent (); /* don't need shadow password data any more */
+ }
+ else pw = NIL; /* password failed */
+ }
+ return pw;
+}
diff --git a/imap/src/osdep/unix/ckp_sce.c b/imap/src/osdep/unix/ckp_sce.c
new file mode 100644
index 00000000..cff95dda
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_sce.c
@@ -0,0 +1,51 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: SecureWare check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ struct es_passwd *pp;
+ set_auth_parameters (argc,argv);
+ if ((pw->pw_passwd && pw->pw_passwd[0] && pw->pw_passwd[1] &&
+ !strcmp (pw->pw_passwd,(char *) crypt (pass,pw->pw_passwd))) ||
+ ((pp = getespwnam (pw->pw_name)) && !pp->ufld->fd_lock &&
+ !pp->ufld->fd_psw_chg_reqd &&
+ pp->ufld->fd_encrypt && pp->ufld->fd_encrypt[0] &&
+ pp->ufld->fd_encrypt[1] &&
+ !strcmp (pp->ufld->fd_encrypt,bigcrypt (pass,pp->ufld->fd_encrypt))))
+ endprpwent (); /* don't need shadow password data any more */
+ else pw = NIL; /* password failed */
+ return pw;
+}
diff --git a/imap/src/osdep/unix/ckp_sec.c b/imap/src/osdep/unix/ckp_sec.c
new file mode 100644
index 00000000..feddaaf3
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_sec.c
@@ -0,0 +1,50 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: SecureWare check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ struct pr_passwd *pp;
+ set_auth_parameters (argc,argv);
+ if ((pw->pw_passwd && pw->pw_passwd[0] && pw->pw_passwd[1] &&
+ !strcmp (pw->pw_passwd,(char *) crypt (pass,pw->pw_passwd))) ||
+ ((pp = getprpwnam (pw->pw_name)) && !pp->ufld.fd_lock &&
+ pp->ufld.fd_encrypt && pp->ufld.fd_encrypt[0] &&
+ pp->ufld.fd_encrypt[1] &&
+ !strcmp (pp->ufld.fd_encrypt,bigcrypt (pass,pp->ufld.fd_encrypt))))
+ endprpwent (); /* don't need shadow password data any more */
+ else pw = NIL; /* password failed */
+ return pw;
+}
diff --git a/imap/src/osdep/unix/ckp_ssn.c b/imap/src/osdep/unix/ckp_ssn.c
new file mode 100644
index 00000000..edd7d632
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_ssn.c
@@ -0,0 +1,56 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Secure SUN-OS check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include <sys/label.h>
+#include <sys/audit.h>
+#include <pwdadj.h>
+
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ struct passwd_adjunct *pa;
+ char *user = cpystr (pw->pw_name);
+ /* validate user and password */
+ struct passwd *ret =
+ ((pw->pw_passwd && pw->pw_passwd[0] && pw->pw_passwd[1] &&
+ !strcmp (pw->pw_passwd,(char *) crypt (pass,pw->pw_passwd))) ||
+ ((pa = getpwanam (pw->pw_name)) &&
+ pa->pwa_passwd && pa->pwa_passwd[0] && pa->pwa_passwd[1] &&
+ !strcmp (pa->pwa_passwd,(char *) crypt (pass,pa->pwa_passwd)))) ?
+ getpwnam (user) : NIL;
+ if (user) fs_give ((void **) &user);
+ return ret;
+}
diff --git a/imap/src/osdep/unix/ckp_std.c b/imap/src/osdep/unix/ckp_std.c
new file mode 100644
index 00000000..36255cc1
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_std.c
@@ -0,0 +1,42 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Standard check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ return (pw->pw_passwd && pw->pw_passwd[0] && pw->pw_passwd[1] &&
+ !strcmp (pw->pw_passwd,(char *) crypt (pass,pw->pw_passwd))) ?
+ pw : NIL;
+}
diff --git a/imap/src/osdep/unix/ckp_sv4.c b/imap/src/osdep/unix/ckp_sv4.c
new file mode 100644
index 00000000..e4b8ab3b
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_sv4.c
@@ -0,0 +1,90 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: SVR4 check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ char tmp[MAILTMPLEN];
+ struct spwd *sp = NIL;
+ time_t left;
+ time_t now = time (0);
+ struct tm *t = gmtime (&now);
+ int zone = t->tm_hour * 60 + t->tm_min;
+ int julian = t->tm_yday;
+ t = localtime (&now); /* get local time now */
+ /* minus UTC minutes since midnight */
+ zone = t->tm_hour * 60 + t->tm_min - zone;
+ /* julian can be one of:
+ * 36x local time is December 31, UTC is January 1, offset -24 hours
+ * 1 local time is 1 day ahead of UTC, offset +24 hours
+ * 0 local time is same day as UTC, no offset
+ * -1 local time is 1 day behind UTC, offset -24 hours
+ * -36x local time is January 1, UTC is December 31, offset +24 hours
+ */
+ if (julian = t->tm_yday -julian)
+ zone += ((julian < 0) == (abs (julian) == 1)) ? -24*60 : 24*60;
+ /* days since 1/1/1970 local time */
+ now = ((now /60) + zone) / (60*24);
+ /* non-shadow authentication */
+ if (!pw->pw_passwd || !pw->pw_passwd[0] || !pw->pw_passwd[1] ||
+ strcmp (pw->pw_passwd,(char *) crypt (pass,pw->pw_passwd))) {
+ /* As far as I've been able to determine, here is how the expiration
+ * fields in the shadow authentication data work:
+ * lstchg last password change date if non-negative. If zero, the
+ * user can not log in without changing password.
+ * max number of days a password is valid if positive
+ * warn number of days of password expiration warning
+ * The expiration day is the *last* day that the password is valid.
+ */
+ /* shadow authentication */
+ if ((sp = getspnam (pw->pw_name)) && sp->sp_lstchg &&
+ ((sp->sp_lstchg < 0) || (sp->sp_max <= 0) ||
+ ((sp->sp_lstchg + sp->sp_max) >= now)) &&
+ sp->sp_pwdp && sp->sp_pwdp[0] && sp->sp_pwdp[1] &&
+ !strcmp (sp->sp_pwdp,(char *) crypt (pass,sp->sp_pwdp))) {
+ if ((sp->sp_lstchg > 0) && (sp->sp_max > 0) &&
+ ((left = (sp->sp_lstchg + sp->sp_max) - now) <= sp->sp_warn)) {
+ if (left) {
+ sprintf (tmp,"[ALERT] Password expires in %ld day(s)",(long) left);
+ mm_notify (NIL,tmp,NIL);
+ }
+ else mm_notify (NIL,"[ALERT] Password expires today!",WARN);
+ }
+ endspent (); /* don't need shadow password data any more */
+ }
+ else pw = NIL; /* password failed */
+ }
+ return pw;
+}
diff --git a/imap/src/osdep/unix/ckp_svo.c b/imap/src/osdep/unix/ckp_svo.c
new file mode 100644
index 00000000..ffb19a64
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_svo.c
@@ -0,0 +1,89 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Old SVR4 check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ char tmp[MAILTMPLEN];
+ struct spwd *sp = NIL;
+ time_t left;
+ time_t now = time (0);
+ struct tm *t = gmtime (&now);
+ int zone = t->tm_hour * 60 + t->tm_min;
+ int julian = t->tm_yday;
+ t = localtime (&now); /* get local time now */
+ /* minus UTC minutes since midnight */
+ zone = t->tm_hour * 60 + t->tm_min - zone;
+ /* julian can be one of:
+ * 36x local time is December 31, UTC is January 1, offset -24 hours
+ * 1 local time is 1 day ahead of UTC, offset +24 hours
+ * 0 local time is same day as UTC, no offset
+ * -1 local time is 1 day behind UTC, offset -24 hours
+ * -36x local time is January 1, UTC is December 31, offset +24 hours
+ */
+ if (julian = t->tm_yday -julian)
+ zone += ((julian < 0) == (abs (julian) == 1)) ? -24*60 : 24*60;
+ /* days since 1/1/1970 local time */
+ now = ((now /60) + zone) / (60*24);
+ /* non-shadow authentication */
+ if (!pw->pw_passwd || !pw->pw_passwd[0] || !pw->pw_passwd[1] ||
+ strcmp (pw->pw_passwd,(char *) crypt (pass,pw->pw_passwd))) {
+ /* As far as I've been able to determine, here is how the expiration
+ * fields in the shadow authentication data work:
+ * lstchg last password change date if non-negative. If zero, the
+ * user can not log in without changing password.
+ * max number of days a password is valid if positive
+ * The expiration day is the *last* day that the password is valid.
+ */
+ /* shadow authentication */
+ if ((sp = getspnam (pw->pw_name)) && sp->sp_lstchg &&
+ ((sp->sp_lstchg < 0) || (sp->sp_max <= 0) ||
+ ((sp->sp_lstchg + sp->sp_max) >= now)) &&
+ sp->sp_pwdp && sp->sp_pwdp[0] && sp->sp_pwdp[1] &&
+ !strcmp (sp->sp_pwdp,(char *) crypt (pass,sp->sp_pwdp))) {
+ if ((sp->sp_lstchg > 0) && (sp->sp_max > 0) &&
+ ((left = (sp->sp_lstchg + sp->sp_max) - now) <= 28)) {
+ if (left) {
+ sprintf (tmp,"[ALERT] Password expires in %ld day(s)",(long) left);
+ mm_notify (NIL,tmp,NIL);
+ }
+ else mm_notify (NIL,"[ALERT] Password expires today!",WARN);
+ }
+ endspent (); /* don't need shadow password data any more */
+ }
+ else pw = NIL; /* password failed */
+ }
+ return pw;
+}
diff --git a/imap/src/osdep/unix/ckp_ult.c b/imap/src/osdep/unix/ckp_ult.c
new file mode 100644
index 00000000..94e3b0b6
--- /dev/null
+++ b/imap/src/osdep/unix/ckp_ult.c
@@ -0,0 +1,40 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: ULTRIX check password
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Check password
+ * Accepts: login passwd struct
+ * password string
+ * argument count
+ * argument vector
+ * Returns: passwd struct if password validated, NIL otherwise
+ */
+
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[])
+{
+ return (authenticate_user (pw,pass,NIL) >= 0) ? pw : NIL;
+}
diff --git a/imap/src/osdep/unix/crx_nfs.c b/imap/src/osdep/unix/crx_nfs.c
new file mode 100644
index 00000000..7e60e635
--- /dev/null
+++ b/imap/src/osdep/unix/crx_nfs.c
@@ -0,0 +1,79 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Exclusive create of a file, NFS kludge version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 17 December 1999
+ * Last Edited: 30 August 2006
+ */
+
+
+/* SUN-OS had an NFS, As kludgy as an albatross;
+ * And everywhere that it was installed, It was a total loss.
+ * -- MRC 9/25/91
+ */
+
+/* Exclusive create of a file
+ * Accepts: file name
+ * Return: T if success, NIL if failed, -1 if retry
+ */
+
+long crexcl (char *name)
+{
+ long ret = -1;
+ int i;
+ char hitch[MAILTMPLEN];
+ struct stat sb;
+ int mask = umask (0);
+ /* build hitching post file name */
+ sprintf (hitch,"%s.%lu.%d.",name,(unsigned long) time (0),getpid ());
+ i = strlen (hitch); /* append local host name */
+ gethostname (hitch + i,(MAILTMPLEN - i) - 1);
+ /* try to get hitching-post file */
+ if ((i = open (hitch,O_WRONLY|O_CREAT|O_EXCL,(int) shlock_mode)) >= 0) {
+ close (i); /* close the hitching-post */
+ /* Note: link() may return an error even if it actually succeeded. So we
+ * always check for success via the link count, and ignore the error if
+ * the link count is right.
+ */
+ /* tie hitching-post to lock */
+ i = link (hitch,name) ? errno : 0;
+ /* success if link count now 2 */
+ if (!stat (hitch,&sb) && (sb.st_nlink == 2)) ret = LONGT;
+ else if (i == EPERM) { /* links not allowed? */
+ /* Probably a FAT filesystem on Linux. It can't be NFS, so try creating
+ * the lock file directly.
+ */
+ if ((i = open (name,O_WRONLY|O_CREAT|O_EXCL,(int) shlock_mode)) >= 0){
+ close (i); /* close the file */
+ ret = LONGT; /* success */
+ }
+ /* fail unless error is EEXIST */
+ else if (errno != EEXIST) ret = NIL;
+ }
+ unlink (hitch); /* flush hitching post */
+ }
+ /* fail unless error is EEXIST */
+ else if (errno != EEXIST) ret = NIL;
+ umask (mask); /* restore previous mask */
+ return ret;
+}
diff --git a/imap/src/osdep/unix/crx_std.c b/imap/src/osdep/unix/crx_std.c
new file mode 100644
index 00000000..633137b8
--- /dev/null
+++ b/imap/src/osdep/unix/crx_std.c
@@ -0,0 +1,45 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Exclusive create of a file
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 17 December 1999
+ * Last Edited: 30 August 2006
+ */
+
+/* Exclusive create of a file
+ * Accepts: file name
+ * Return: T if success, NIL if failed, -1 if retry
+ */
+
+long crexcl (char *name)
+{
+ int i;
+ int mask = umask (0);
+ long ret = LONGT;
+ /* try to get the lock */
+ if ((i = open (name,O_WRONLY|O_CREAT|O_EXCL,(int) shlock_mode)) < 0)
+ ret = (errno == EEXIST) ? -1 : NIL;
+ else close (i); /* made the file, now close it */
+ umask (mask); /* restore previous mask */
+ return LONGT; /* success */
+}
diff --git a/imap/src/osdep/unix/drivers b/imap/src/osdep/unix/drivers
new file mode 100755
index 00000000..bd49365f
--- /dev/null
+++ b/imap/src/osdep/unix/drivers
@@ -0,0 +1,36 @@
+#!/bin/sh
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+# Program: Driver Linkage Generator
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 11 October 1989
+# Last Edited: 30 August 2006
+
+
+# Erase old driver linkage
+rm -f linkage.[ch]
+
+# Now define the new list
+for driver
+ do
+ echo "extern DRIVER "$driver"driver;" >> linkage.h
+ echo " mail_link (&"$driver"driver); /* link in the $driver driver */" | cat >> linkage.c
+done
diff --git a/imap/src/osdep/unix/dummy.c b/imap/src/osdep/unix/dummy.c
new file mode 100644
index 00000000..b003a0ba
--- /dev/null
+++ b/imap/src/osdep/unix/dummy.c
@@ -0,0 +1,809 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 9 May 1991
+ * Last Edited: 1 June 2007
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <pwd.h>
+#include <sys/stat.h>
+#include "dummy.h"
+#include "misc.h"
+
+/* Function prototypes */
+
+DRIVER *dummy_valid (char *name);
+void *dummy_parameters (long function,void *value);
+void dummy_list_work (MAILSTREAM *stream,char *dir,char *pat,char *contents,
+ long level);
+long dummy_listed (MAILSTREAM *stream,char delimiter,char *name,
+ long attributes,char *contents);
+long dummy_subscribe (MAILSTREAM *stream,char *mailbox);
+MAILSTREAM *dummy_open (MAILSTREAM *stream);
+void dummy_close (MAILSTREAM *stream,long options);
+long dummy_ping (MAILSTREAM *stream);
+void dummy_check (MAILSTREAM *stream);
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options);
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+/* Dummy routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER dummydriver = {
+ "dummy", /* driver name */
+ DR_LOCAL|DR_MAIL, /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ dummy_valid, /* mailbox is valid for us */
+ dummy_parameters, /* manipulate parameters */
+ dummy_scan, /* scan mailboxes */
+ dummy_list, /* list mailboxes */
+ dummy_lsub, /* list subscribed mailboxes */
+ dummy_subscribe, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ dummy_create, /* create mailbox */
+ dummy_delete, /* delete mailbox */
+ dummy_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ dummy_open, /* open mailbox */
+ dummy_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message structure */
+ NIL, /* fetch header */
+ NIL, /* fetch text */
+ NIL, /* fetch message data */
+ NIL, /* unique identifier */
+ NIL, /* message number from UID */
+ NIL, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ dummy_ping, /* ping mailbox to see if still alive */
+ dummy_check, /* check for new messages */
+ dummy_expunge, /* expunge deleted messages */
+ dummy_copy, /* copy messages to another mailbox */
+ dummy_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM dummyproto = {&dummydriver};
+
+/* Dummy validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *dummy_valid (char *name)
+{
+ char *s,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* must be valid local mailbox */
+ if (name && *name && (*name != '{') && (s = mailboxfile (tmp,name))) {
+ /* indeterminate clearbox INBOX */
+ if (!*s) return &dummydriver;
+ else if (!stat (s,&sbuf)) switch (sbuf.st_mode & S_IFMT) {
+ case S_IFREG:
+ case S_IFDIR:
+ return &dummydriver;
+ }
+ /* blackbox INBOX does not exist yet */
+ else if (!compare_cstring (name,"INBOX")) return &dummydriver;
+ }
+ return NIL;
+}
+
+
+/* Dummy manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *dummy_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = dummy_file ((char *) value,"INBOX");
+ break;
+ }
+ return ret;
+}
+
+/* Dummy scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ DRIVER *drivers;
+ char *s,test[MAILTMPLEN],file[MAILTMPLEN];
+ long i;
+ if (!pat || !*pat) { /* empty pattern? */
+ if (dummy_canonicalize (test,ref,"*")) {
+ /* tie off name at root */
+ if (s = strchr (test,'/')) *++s = '\0';
+ else test[0] = '\0';
+ dummy_listed (stream,'/',test,LATT_NOSELECT,NIL);
+ }
+ }
+ /* get canonical form of name */
+ else if (dummy_canonicalize (test,ref,pat)) {
+ /* found any wildcards? */
+ if (s = strpbrk (test,"%*")) {
+ /* yes, copy name up to that point */
+ strncpy (file,test,i = s - test);
+ file[i] = '\0'; /* tie off */
+ }
+ else strcpy (file,test); /* use just that name then */
+ if (s = strrchr (file,'/')){/* find directory name */
+ *++s = '\0'; /* found, tie off at that point */
+ s = file;
+ }
+ /* silly case */
+ else if ((file[0] == '~') || (file[0] == '#')) s = file;
+ /* do the work */
+ dummy_list_work (stream,s,test,contents,0);
+ /* always an INBOX */
+ if (pmatch ("INBOX",ucase (test))) {
+ /* done if have a dirfmt INBOX */
+ for (drivers = (DRIVER *) mail_parameters (NIL,GET_DRIVERS,NIL);
+ drivers && !(!(drivers->flags & DR_DISABLE) &&
+ (drivers->flags & DR_DIRFMT) &&
+ (*drivers->valid) ("INBOX")); drivers = drivers->next);
+ /* list INBOX appropriately */
+ dummy_listed (stream,drivers ? '/' : NIL,"INBOX",
+ drivers ? NIL : LATT_NOINFERIORS,contents);
+ }
+ }
+}
+
+
+/* Dummy list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ dummy_scan (stream,ref,pat,NIL);
+}
+
+/* Dummy list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ void *sdb = NIL;
+ char *s,*t,test[MAILTMPLEN],tmp[MAILTMPLEN];
+ int showuppers = pat[strlen (pat) - 1] == '%';
+ /* get canonical form of name */
+ if (dummy_canonicalize (test,ref,pat) && (s = sm_read (&sdb))) do
+ if (*s != '{') {
+ if (!compare_cstring (s,"INBOX") &&
+ pmatch ("INBOX",ucase (strcpy (tmp,test))))
+ mm_lsub (stream,NIL,s,LATT_NOINFERIORS);
+ else if (pmatch_full (s,test,'/')) mm_lsub (stream,'/',s,NIL);
+ else while (showuppers && (t = strrchr (s,'/'))) {
+ *t = '\0'; /* tie off the name */
+ if (pmatch_full (s,test,'/')) mm_lsub (stream,'/',s,LATT_NOSELECT);
+ }
+ }
+ while (s = sm_read (&sdb)); /* until no more subscriptions */
+}
+
+
+/* Dummy subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* must be valid local mailbox */
+ if ((s = mailboxfile (tmp,mailbox)) && *s && !stat (s,&sbuf))
+ switch (sbuf.st_mode & S_IFMT) {
+ case S_IFDIR: /* allow but snarl */
+ sprintf (tmp,"CLIENT BUG DETECTED: subscribe of non-mailbox directory %.80s",
+ mailbox);
+ MM_NOTIFY (stream,tmp,WARN);
+ case S_IFREG:
+ return sm_subscribe (mailbox);
+ }
+ sprintf (tmp,"Can't subscribe %.80s: not a mailbox",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+}
+
+/* Dummy list mailboxes worker routine
+ * Accepts: mail stream
+ * directory name to search
+ * search pattern
+ * string to scan
+ * search level
+ */
+
+void dummy_list_work (MAILSTREAM *stream,char *dir,char *pat,char *contents,
+ long level)
+{
+ DRIVER *drivers;
+ dirfmttest_t dt;
+ DIR *dp;
+ struct direct *d;
+ struct stat sbuf;
+ char tmp[MAILTMPLEN],path[MAILTMPLEN];
+ size_t len = 0;
+ /* punt if bogus name */
+ if (!mailboxdir (tmp,dir,NIL)) return;
+ if (dp = opendir (tmp)) { /* do nothing if can't open directory */
+ /* see if a non-namespace directory format */
+ for (drivers = (DRIVER *) mail_parameters (NIL,GET_DRIVERS,NIL), dt = NIL;
+ dir && !dt && drivers; drivers = drivers->next)
+ if (!(drivers->flags & DR_DISABLE) && (drivers->flags & DR_DIRFMT) &&
+ (*drivers->valid) (dir))
+ dt = mail_parameters ((*drivers->open) (NIL),GET_DIRFMTTEST,NIL);
+ /* list it if at top-level */
+ if (!level && dir && pmatch_full (dir,pat,'/') && !pmatch (dir,"INBOX"))
+ dummy_listed (stream,'/',dir,dt ? NIL : LATT_NOSELECT,contents);
+
+ /* scan directory, ignore . and .. */
+ if (!dir || dir[(len = strlen (dir)) - 1] == '/') while (d = readdir (dp))
+ if ((!(dt && (*dt) (d->d_name))) &&
+ ((d->d_name[0] != '.') ||
+ (((long) mail_parameters (NIL,GET_HIDEDOTFILES,NIL)) ? NIL :
+ (d->d_name[1] && (((d->d_name[1] != '.') || d->d_name[2]))))) &&
+ ((len + strlen (d->d_name)) <= NETMAXMBX)) {
+ /* see if name is useful */
+ if (dir) sprintf (tmp,"%s%s",dir,d->d_name);
+ else strcpy (tmp,d->d_name);
+ /* make sure useful and can get info */
+ if ((pmatch_full (strcpy (path,tmp),pat,'/') ||
+ pmatch_full (strcat (path,"/"),pat,'/') ||
+ dmatch (path,pat,'/')) &&
+ mailboxdir (path,dir,"x") && (len = strlen (path)) &&
+ strcpy (path+len-1,d->d_name) && !stat (path,&sbuf)) {
+ /* only interested in file type */
+ switch (sbuf.st_mode & S_IFMT) {
+ case S_IFDIR: /* directory? */
+ /* form with trailing / */
+ sprintf (path,"%s/",tmp);
+ /* skip listing if INBOX */
+ if (!pmatch (tmp,"INBOX")) {
+ if (pmatch_full (tmp,pat,'/')) {
+ if (!dummy_listed (stream,'/',tmp,LATT_NOSELECT,contents))
+ break;
+ }
+ /* try again with trailing / */
+ else if (pmatch_full (path,pat,'/') &&
+ !dummy_listed (stream,'/',path,LATT_NOSELECT,contents))
+ break;
+ }
+ if (dmatch (path,pat,'/') &&
+ (level < (long) mail_parameters (NIL,GET_LISTMAXLEVEL,NIL)))
+ dummy_list_work (stream,path,pat,contents,level+1);
+ break;
+ case S_IFREG: /* ordinary name */
+ /* Must use ctime for systems that don't update mtime properly */
+ if (pmatch_full (tmp,pat,'/') && compare_cstring (tmp,"INBOX"))
+ dummy_listed (stream,'/',tmp,LATT_NOINFERIORS +
+ ((sbuf.st_size && (sbuf.st_atime < sbuf.st_ctime))?
+ LATT_MARKED : LATT_UNMARKED),contents);
+ break;
+ }
+ }
+ }
+ closedir (dp); /* all done, flush directory */
+ }
+}
+
+/* Scan file for contents
+ * Accepts: driver to use
+ * file name
+ * desired contents
+ * length of contents
+ * size of file
+ * Returns: NIL if contents not found, T if found
+ */
+
+long scan_contents (DRIVER *dtb,char *name,char *contents,
+ unsigned long csiz,unsigned long fsiz)
+{
+ scancontents_t sc = dtb ?
+ (scancontents_t) (*dtb->parameters) (GET_SCANCONTENTS,NIL) : NIL;
+ return (*(sc ? sc : dummy_scan_contents)) (name,contents,csiz,fsiz);
+}
+
+
+/* Scan file for contents
+ * Accepts: file name
+ * desired contents
+ * length of contents
+ * size of file
+ * Returns: NIL if contents not found, T if found
+ */
+
+#define BUFSIZE 4*MAILTMPLEN
+
+long dummy_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz)
+{
+ int fd;
+ unsigned long ssiz,bsiz;
+ char *buf;
+ /* forget it if can't select or open */
+ if ((fd = open (name,O_RDONLY,NIL)) >= 0) {
+ /* get buffer including slop */
+ buf = (char *) fs_get (BUFSIZE + (ssiz = 4 * ((csiz / 4) + 1)) + 1);
+ memset (buf,'\0',ssiz); /* no slop area the first time */
+ while (fsiz) { /* until end of file */
+ read (fd,buf+ssiz,bsiz = min (fsiz,BUFSIZE));
+ if (search ((unsigned char *) buf,bsiz+ssiz,
+ (unsigned char *) contents,csiz)) break;
+ memcpy (buf,buf+BUFSIZE,ssiz);
+ fsiz -= bsiz; /* note that we read that much */
+ }
+ fs_give ((void **) &buf); /* flush buffer */
+ close (fd); /* finished with file */
+ if (fsiz) return T; /* found */
+ }
+ return NIL; /* not found */
+}
+
+/* Mailbox found
+ * Accepts: MAIL stream
+ * hierarchy delimiter
+ * mailbox name
+ * attributes
+ * contents to search before calling mm_list()
+ * Returns: NIL if should abort hierarchy search, else T (currently always)
+ */
+
+long dummy_listed (MAILSTREAM *stream,char delimiter,char *name,
+ long attributes,char *contents)
+{
+ DRIVER *d;
+ DIR *dp;
+ struct direct *dr;
+ dirfmttest_t dt;
+ unsigned long csiz;
+ struct stat sbuf;
+ int nochild;
+ char *s,tmp[MAILTMPLEN];
+ if (!(attributes & LATT_NOINFERIORS) && mailboxdir (tmp,name,NIL) &&
+ (dp = opendir (tmp))) { /* if not \NoInferiors */
+ /* locate dirfmttest if any */
+ for (d = (DRIVER *) mail_parameters (NIL,GET_DRIVERS,NIL), dt = NIL;
+ !dt && d; d = d->next)
+ if (!(d->flags & DR_DISABLE) && (d->flags & DR_DIRFMT) &&
+ (*d->valid) (name))
+ dt = mail_parameters ((*d->open) (NIL),GET_DIRFMTTEST,NIL);
+ /* scan directory for children */
+ for (nochild = T; nochild && (dr = readdir (dp)); )
+ if ((!(dt && (*dt) (dr->d_name))) &&
+ ((dr->d_name[0] != '.') ||
+ (((long) mail_parameters (NIL,GET_HIDEDOTFILES,NIL)) ? NIL :
+ (dr->d_name[1] && ((dr->d_name[1] != '.') || dr->d_name[2])))))
+ nochild = NIL;
+ attributes |= nochild ? LATT_HASNOCHILDREN : LATT_HASCHILDREN;
+ closedir (dp); /* all done, flush directory */
+ }
+ d = NIL; /* don't \NoSelect dir if it has a driver */
+ if ((attributes & LATT_NOSELECT) && (d = mail_valid (NIL,name,NIL)) &&
+ (d != &dummydriver)) attributes &= ~LATT_NOSELECT;
+ if (!contents || /* notify main program */
+ (!(attributes & LATT_NOSELECT) && (csiz = strlen (contents)) &&
+ (s = mailboxfile (tmp,name)) &&
+ (*s || (s = mail_parameters (NIL,GET_INBOXPATH,tmp))) &&
+ !stat (s,&sbuf) && (d || (csiz <= sbuf.st_size)) &&
+ SAFE_SCAN_CONTENTS (d,tmp,contents,csiz,sbuf.st_size)))
+ mm_list (stream,delimiter,name,attributes);
+ return T;
+}
+
+/* Dummy create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,tmp[MAILTMPLEN];
+ long ret = NIL;
+ /* validate name */
+ if (!(compare_cstring (mailbox,"INBOX") && (s = dummy_file (tmp,mailbox)))) {
+ sprintf (tmp,"Can't create %.80s: invalid name",mailbox);
+ MM_LOG (tmp,ERROR);
+ }
+ /* create the name, done if made directory */
+ else if ((ret = dummy_create_path (stream,tmp,get_dir_protection(mailbox)))&&
+ (s = strrchr (s,'/')) && !s[1]) return T;
+ return ret ? set_mbx_protections (mailbox,tmp) : NIL;
+}
+
+/* Dummy create path
+ * Accepts: mail stream
+ * path name to create
+ * directory mode
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode)
+{
+ struct stat sbuf;
+ char c,*s,tmp[MAILTMPLEN];
+ int fd;
+ long ret = NIL;
+ char *t = strrchr (path,'/');
+ int wantdir = t && !t[1];
+ int mask = umask (0);
+ if (wantdir) *t = '\0'; /* flush trailing delimiter for directory */
+ if (s = strrchr (path,'/')) { /* found superior to this name? */
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (path,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,path,dirmode)) {
+ umask (mask); /* restore mask */
+ return NIL;
+ }
+ *s = c; /* restore full name */
+ }
+ if (wantdir) { /* want to create directory? */
+ ret = !mkdir (path,(int) dirmode);
+ *t = '/'; /* restore directory delimiter */
+ }
+ /* create file */
+ else if ((fd = open (path,O_WRONLY|O_CREAT|O_EXCL,
+ (long) mail_parameters(NIL,GET_MBXPROTECTION,NIL))) >=0)
+ ret = !close (fd);
+ if (!ret) { /* error? */
+ sprintf (tmp,"Can't create mailbox node %.80s: %.80s",path,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ }
+ umask (mask); /* restore mask */
+ return ret; /* return status */
+}
+
+/* Dummy delete mailbox
+ * Accepts: mail stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_delete (MAILSTREAM *stream,char *mailbox)
+{
+ struct stat sbuf;
+ char *s,tmp[MAILTMPLEN];
+ if (!(s = dummy_file (tmp,mailbox))) {
+ sprintf (tmp,"Can't delete - invalid name: %.80s",s);
+ MM_LOG (tmp,ERROR);
+ }
+ /* no trailing / (workaround BSD kernel bug) */
+ if ((s = strrchr (tmp,'/')) && !s[1]) *s = '\0';
+ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) == S_IFDIR) ?
+ rmdir (tmp) : unlink (tmp)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %.80s",mailbox,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ return T; /* return success */
+}
+
+/* Mail rename mailbox
+ * Accepts: mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ struct stat sbuf;
+ char c,*s,tmp[MAILTMPLEN],mbx[MAILTMPLEN],oldname[MAILTMPLEN];
+ /* no trailing / allowed */
+ if (!dummy_file (oldname,old) || !(s = dummy_file (mbx,newname)) ||
+ stat (oldname,&sbuf) || ((s = strrchr (s,'/')) && !s[1] &&
+ ((sbuf.st_mode & S_IFMT) != S_IFDIR))) {
+ sprintf (mbx,"Can't rename %.80s to %.80s: invalid name",old,newname);
+ MM_LOG (mbx,ERROR);
+ return NIL;
+ }
+ if (s) { /* found a directory delimiter? */
+ if (!s[1]) *s = '\0'; /* ignore trailing delimiter */
+ else { /* found superior to destination name? */
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (mbx,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create (stream,mbx)) return NIL;
+ *s = c; /* restore full name */
+ }
+ }
+ /* rename of non-ex INBOX creates dest */
+ if (!compare_cstring (old,"INBOX") && stat (oldname,&sbuf))
+ return dummy_create (NIL,mbx);
+ if (rename (oldname,mbx)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %.80s",old,newname,
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ return T; /* return success */
+}
+
+/* Dummy open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *dummy_open (MAILSTREAM *stream)
+{
+ int fd;
+ char err[MAILTMPLEN],tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* OP_PROTOTYPE call */
+ if (!stream) return &dummyproto;
+ err[0] = '\0'; /* no error message yet */
+ /* can we open the file? */
+ if (!dummy_file (tmp,stream->mailbox))
+ sprintf (err,"Can't open this name: %.80s",stream->mailbox);
+ else if ((fd = open (tmp,O_RDONLY,NIL)) < 0) {
+ /* no, error unless INBOX */
+ if (compare_cstring (stream->mailbox,"INBOX"))
+ sprintf (err,"%.80s: %.80s",strerror (errno),stream->mailbox);
+ }
+ else { /* file had better be empty then */
+ fstat (fd,&sbuf); /* sniff at its size */
+ close (fd);
+ if ((sbuf.st_mode & S_IFMT) != S_IFREG)
+ sprintf (err,"Can't open %.80s: not a selectable mailbox",
+ stream->mailbox);
+ else if (sbuf.st_size) /* bogus format if non-empty */
+ sprintf (err,"Can't open %.80s (file %.80s): not in valid mailbox format",
+ stream->mailbox,tmp);
+ }
+ if (err[0]) { /* if an error happened */
+ MM_LOG (err,stream->silent ? WARN : ERROR);
+ return NIL;
+ }
+ else if (!stream->silent) { /* only if silence not requested */
+ mail_exists (stream,0); /* say there are 0 messages */
+ mail_recent (stream,0); /* and certainly no recent ones! */
+ stream->uid_validity = time (0);
+ }
+ stream->inbox = T; /* note that it's an INBOX */
+ return stream; /* return success */
+}
+
+
+/* Dummy close
+ * Accepts: MAIL stream
+ * options
+ */
+
+void dummy_close (MAILSTREAM *stream,long options)
+{
+ /* return silently */
+}
+
+/* Dummy ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long dummy_ping (MAILSTREAM *stream)
+{
+ MAILSTREAM *test;
+ if (time (0) >= /* time to do another test? */
+ ((time_t) (stream->gensym +
+ (long) mail_parameters (NIL,GET_SNARFINTERVAL,NIL)))) {
+ /* has mailbox format changed? */
+ if ((test = mail_open (NIL,stream->mailbox,OP_PROTOTYPE)) &&
+ (test->dtb != stream->dtb) &&
+ (test = mail_open (NIL,stream->mailbox,NIL))) {
+ /* preserve some resources */
+ test->original_mailbox = stream->original_mailbox;
+ stream->original_mailbox = NIL;
+ test->sparep = stream->sparep;
+ stream->sparep = NIL;
+ test->sequence = stream->sequence;
+ mail_close ((MAILSTREAM *) /* flush resources used by dummy stream */
+ memcpy (fs_get (sizeof (MAILSTREAM)),stream,
+ sizeof (MAILSTREAM)));
+ /* swap the streams */
+ memcpy (stream,test,sizeof (MAILSTREAM));
+ fs_give ((void **) &test);/* flush test now that copied */
+ /* make sure application knows */
+ mail_exists (stream,stream->recent = stream->nmsgs);
+ }
+ /* still hasn't changed */
+ else stream->gensym = time (0);
+ }
+ return T;
+}
+
+
+/* Dummy check mailbox
+ * Accepts: MAIL stream
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+void dummy_check (MAILSTREAM *stream)
+{
+ dummy_ping (stream); /* invoke ping */
+}
+
+
+/* Dummy expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ return LONGT;
+}
+
+/* Dummy copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * options
+ * Returns: T if copy successful, else NIL
+ */
+
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) fatal ("Impossible dummy_copy");
+ return NIL;
+}
+
+
+/* Dummy append message string
+ * Accepts: mail stream
+ * destination mailbox
+ * append callback function
+ * data for callback
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd = -1;
+ int e;
+ char tmp[MAILTMPLEN];
+ MAILSTREAM *ts = default_proto (T);
+ /* append to INBOX? */
+ if (!compare_cstring (mailbox,"INBOX")) {
+ /* yes, if no empty proto try creating */
+ if (!ts && !(*(ts = default_proto (NIL))->dtb->create) (ts,"INBOX"))
+ ts = NIL;
+ }
+ else if (dummy_file (tmp,mailbox) && ((fd = open (tmp,O_RDONLY,NIL)) < 0)) {
+ if ((e = errno) == ENOENT) /* failed, was it no such file? */
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ sprintf (tmp,"%.80s: %.80s",strerror (e),mailbox);
+ MM_LOG (tmp,ERROR); /* pass up error */
+ return NIL; /* always fails */
+ }
+ else if (fd >= 0) { /* found file? */
+ fstat (fd,&sbuf); /* get its size */
+ close (fd); /* toss out the fd */
+ if (sbuf.st_size) ts = NIL; /* non-empty file? */
+ }
+ if (ts) return (*ts->dtb->append) (stream,mailbox,af,data);
+ sprintf (tmp,"Indeterminate mailbox format: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+}
+
+/* Dummy mail generate file string
+ * Accepts: temporary buffer to write into
+ * mailbox name string
+ * Returns: local file string or NIL if failure
+ */
+
+char *dummy_file (char *dst,char *name)
+{
+ char *s = mailboxfile (dst,name);
+ /* return our standard inbox */
+ return (s && !*s) ? strcpy (dst,sysinbox ()) : s;
+}
+
+
+/* Dummy canonicalize name
+ * Accepts: buffer to write name
+ * reference
+ * pattern
+ * Returns: T if success, NIL if failure
+ */
+
+long dummy_canonicalize (char *tmp,char *ref,char *pat)
+{
+ unsigned long i;
+ char *s;
+ if (ref) { /* preliminary reference check */
+ if (*ref == '{') return NIL;/* remote reference not allowed */
+ else if (!*ref) ref = NIL; /* treat empty reference as no reference */
+ }
+ switch (*pat) {
+ case '#': /* namespace name */
+ if (mailboxfile (tmp,pat)) strcpy (tmp,pat);
+ else return NIL; /* unknown namespace */
+ break;
+ case '{': /* remote names not allowed */
+ return NIL;
+ case '/': /* rooted name */
+ case '~': /* home directory name */
+ if (!ref || (*ref != '#')) {/* non-namespace reference? */
+ strcpy (tmp,pat); /* yes, ignore */
+ break;
+ }
+ /* fall through */
+ default: /* apply reference for all other names */
+ if (!ref) strcpy (tmp,pat); /* just copy if no namespace */
+ else if ((*ref != '#') || mailboxfile (tmp,ref)) {
+ /* wants root of name? */
+ if (*pat == '/') strcpy (strchr (strcpy (tmp,ref),'/'),pat);
+ /* otherwise just append */
+ else sprintf (tmp,"%s%s",ref,pat);
+ }
+ else return NIL; /* unknown namespace */
+ }
+ /* count wildcards */
+ for (i = 0, s = tmp; *s; *s++) if ((*s == '*') || (*s == '%')) ++i;
+ if (i > MAXWILDCARDS) { /* ridiculous wildcarding? */
+ MM_LOG ("Excessive wildcards in LIST/LSUB",ERROR);
+ return NIL;
+ }
+ return T;
+}
diff --git a/imap/src/osdep/unix/dummy.h b/imap/src/osdep/unix/dummy.h
new file mode 100644
index 00000000..32650e06
--- /dev/null
+++ b/imap/src/osdep/unix/dummy.h
@@ -0,0 +1,43 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 9 May 1991
+ * Last Edited: 30 August 2006
+ */
+
+/* Exported function prototypes */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat);
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long scan_contents (DRIVER *dtb,char *name,char *contents,
+ unsigned long csiz,unsigned long fsiz);
+long dummy_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz);
+long dummy_create (MAILSTREAM *stream,char *mailbox);
+long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode);
+long dummy_delete (MAILSTREAM *stream,char *mailbox);
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname);
+char *dummy_file (char *dst,char *name);
+long dummy_canonicalize (char *tmp,char *ref,char *pat);
diff --git a/imap/src/osdep/unix/env_unix.c b/imap/src/osdep/unix/env_unix.c
new file mode 100644
index 00000000..6b2c4471
--- /dev/null
+++ b/imap/src/osdep/unix/env_unix.c
@@ -0,0 +1,1847 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX environment routines
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 23 February 2009
+ */
+
+#include <grp.h>
+#include <signal.h>
+#include <sys/wait.h>
+
+
+/* in case stat.h is ancient */
+
+#ifndef S_IRUSR
+#define S_IRUSR S_IREAD
+#endif
+#ifndef S_IWUSR
+#define S_IWUSR S_IWRITE
+#endif
+#ifndef S_IXUSR
+#define S_IXUSR S_IEXEC
+#endif
+#ifndef S_IRGRP
+#define S_IRGRP (S_IREAD >> 3)
+#endif
+#ifndef S_IWGRP
+#define S_IWGRP (S_IWRITE >> 3)
+#endif
+#ifndef S_IXGRP
+#define S_IXGRP (S_IEXEC >> 3)
+#endif
+#ifndef S_IROTH
+#define S_IROTH (S_IREAD >> 6)
+#endif
+#ifndef S_IWOTH
+#define S_IWOTH (S_IWRITE >> 6)
+#endif
+#ifndef S_IXOTH
+#define S_IXOTH (S_IEXEC >> 6)
+#endif
+
+/* c-client environment parameters */
+
+static char *myUserName = NIL; /* user name */
+static char *myHomeDir = NIL; /* home directory name */
+static char *myServerName = NIL;/* server name */
+static char *myLocalHost = NIL; /* local host name */
+static char *myNewsrc = NIL; /* newsrc file name */
+static char *mailsubdir = NIL; /* mailbox subdirectory name */
+static char *sysInbox = NIL; /* system inbox name */
+static char *newsActive = NIL; /* news active file */
+static char *newsSpool = NIL; /* news spool */
+static char *blackBoxDir = NIL; /* black box directory name */
+ /* black box default home directory */
+static char *blackBoxDefaultHome = NIL;
+static char *sslCApath = NIL; /* non-standard CA path */
+static short anonymous = NIL; /* is anonymous */
+static short blackBox = NIL; /* is a black box */
+static short closedBox = NIL; /* is a closed box (uses chroot() jail) */
+static short restrictBox = NIL; /* is a restricted box */
+static short has_no_life = NIL; /* is a cretin with no life */
+ /* block environment init */
+static short block_env_init = NIL;
+static short hideDotFiles = NIL;/* hide files whose names start with . */
+ /* advertise filesystem root */
+static short advertisetheworld = NIL;
+ /* only advertise own mailboxes and #shared */
+static short limitedadvertise = NIL;
+ /* disable automatic shared namespaces */
+static short noautomaticsharedns = NIL;
+static short no822tztext = NIL; /* disable RFC [2]822 timezone text */
+ /* client principals include service name */
+static short kerb_cp_svr_name = NIL;
+static long locktimeout = 5; /* default lock timeout in minutes */
+ /* default prototypes */
+static MAILSTREAM *createProto = NIL;
+static MAILSTREAM *appendProto = NIL;
+ /* default user flags */
+static char *userFlags[NUSERFLAGS] = {NIL};
+static NAMESPACE *nslist[3]; /* namespace list */
+static int logtry = 3; /* number of server login tries */
+ /* block notification */
+static blocknotify_t mailblocknotify = mm_blocknotify;
+ /* logout function */
+static logouthook_t maillogouthook = NIL;
+ /* logout data */
+static void *maillogoutdata = NIL;
+ /* allow user config files */
+static short allowuserconfig = NIL;
+ /* 1 = disable plaintext, 2 = if not SSL */
+static long disablePlaintext = NIL;
+static long list_max_level = 20;/* maximum level of list recursion */
+ /* facility for syslog */
+static int syslog_facility = LOG_MAIL;
+
+/* Path of the privileged system lock program (mlock). Normally set by
+ * logic test.
+ */
+
+static char *lockpgm = LOCKPGM;
+
+/* Directory used for shared locks. MUST be the same for all users of the
+ * system, and MUST be protected 1777. /var/tmp may be preferable on some
+ * systems.
+ */
+
+static const char *tmpdir = "/tmp";
+
+/* Do not change shlock_mode. Doing so can cause mailbox corruption and
+ * denial of service. It also defeats the entire purpose of the shared
+ * lock mechanism. The right way to avoid shared locks is to set up a
+ * closed box (see the closedBox setting).
+ */
+
+ /* shared lock mode */
+static const int shlock_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
+
+
+/* It is STRONGLY recommended that you do not change dotlock_mode. Doing so
+ * can cause denial of service with old dot-lock files left lying around.
+ * However, since dot-locks are only used with traditional UNIX and MMDF
+ * formats which are not normally shared, it is much less harmful to tamper
+ * with this than with shlock_mode.
+ */
+
+ /* dot-lock mode */
+static long dotlock_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
+
+/* File/directory access and protection policies */
+
+/* Unlike shlock_mode, the ????_protection modes are intended to be fully
+ * customizable according to site policy. The values here are recommended
+ * settings, based upon the documented purposes of the namespaces.
+ */
+
+ /* user space - only owner can read/write */
+static char *myMailboxDir = NIL;/* user space directory name */
+ /* default file protection */
+static long mbx_protection = S_IRUSR|S_IWUSR;
+ /* default directory protection */
+static long dir_protection = S_IRUSR|S_IWUSR|S_IXUSR;
+
+ /* user space for user "anonymous" */
+ /* anonymous home directory */
+static char *anonymousHome = NIL;
+
+ /* #ftp - everybody can read, only owner can write */
+static char *ftpHome = NIL; /* ftp export home directory */
+ /* default ftp file protection */
+static long ftp_protection = S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH;
+static long ftp_dir_protection =/* default ftp directory protection */
+ S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
+
+ /* #public - everybody can read/write */
+static char *publicHome = NIL; /* public home directory */
+static long public_protection = /* default public file protection */
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
+ /* default public directory protection */
+static long public_dir_protection =
+ S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH;
+
+ /* #shared/ - owner and group members can read/write */
+static char *sharedHome = NIL; /* shared home directory */
+ /* default shared file protection */
+static long shared_protection = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP;
+ /* default shared directory protection */
+static long shared_dir_protection =
+ S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP;
+
+/* OS bug workarounds - should be avoided at all cost */
+
+
+/* Don't set fcntlhangbug unless you really have to, since it risks mailbox
+ * corruption. The flocksim.c mechanism is designed to detect NFS access
+ * and no-op in that cases only, so this flag should be unnecessary.
+ */
+
+static short fcntlhangbug = NIL;/* flock() emulator using fcntl() is a no-op */
+
+
+/* Don't set netfsstatbug unless you really have to, since it dramatically
+ * slows down traditional UNIX and MMDF mailbox performance.
+ */
+
+static short netfsstatbug = NIL;/* compensate for broken stat() on network
+ * filesystems (AFS and old NFS)
+ */
+
+
+/* Note: setting disableLockWarning means that you assert that the
+ * so-modified copy of this software will NEVER be used:
+ * 1) in conjunction with any software which expects .lock files
+ * 2) to access NFS-mounted files and directories
+ *
+ * Unless both of these conditions apply, then do not set this flag.
+ * Instead, read the FAQ (item 7.10) and either use 1777 protection
+ * on the mail spool, or install mlock.
+ *
+ * In addition, by setting this flag you also agree that you are fully
+ * legally and morally responsible when (not if) mail files are damaged
+ * as the result of your choice.
+ *
+ * The mlock tool exists for a reason. Use it.
+ */
+ /* disable warning if can't make .lock file */
+static short disableLockWarning = NIL;
+
+/* UNIX Namespaces */
+
+ /* personal mh namespace */
+static NAMESPACE nsmhf = {"#mh/",'/',NIL,NIL};
+static NAMESPACE nsmh = {"#mhinbox",NIL,NIL,&nsmhf};
+ /* home namespace */
+static NAMESPACE nshome = {"",'/',NIL,&nsmh};
+ /* UNIX other user namespace */
+static NAMESPACE nsunixother = {"~",'/',NIL,NIL};
+ /* black box other user namespace */
+static NAMESPACE nsblackother = {"/",'/',NIL,NIL};
+ /* public (anonymous OK) namespace */
+static NAMESPACE nspublic = {"#public/",'/',NIL,NIL};
+ /* netnews namespace */
+static NAMESPACE nsnews = {"#news.",'.',NIL,&nspublic};
+ /* FTP export namespace */
+static NAMESPACE nsftp = {"#ftp/",'/',NIL,&nsnews};
+ /* shared (no anonymous) namespace */
+static NAMESPACE nsshared = {"#shared/",'/',NIL,&nsftp};
+ /* world namespace */
+static NAMESPACE nsworld = {"/",'/',NIL,&nsshared};
+ /* only shared and public namespaces */
+static NAMESPACE nslimited = {"#shared/",'/',NIL,&nspublic};
+
+
+
+#include "write.c" /* include safe writing routines */
+#include "crexcl.c" /* include exclusive create */
+#include "pmatch.c" /* include wildcard pattern matcher */
+
+/* Get all authenticators */
+
+#include "auths.c"
+
+/* Environment manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *env_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_NAMESPACE:
+ ret = (void *) nslist;
+ break;
+ case SET_USERNAME:
+ if (myUserName) fs_give ((void **) &myUserName);
+ myUserName = cpystr ((char *) value);
+ case GET_USERNAME:
+ ret = (void *) myUserName;
+ break;
+ case SET_HOMEDIR:
+ if (myHomeDir) fs_give ((void **) &myHomeDir);
+ myHomeDir = cpystr ((char *) value);
+ case GET_HOMEDIR:
+ ret = (void *) myHomeDir;
+ break;
+ case SET_LOCALHOST:
+ if (myLocalHost) fs_give ((void **) &myLocalHost);
+ myLocalHost = cpystr ((char *) value);
+ case GET_LOCALHOST:
+ ret = (void *) myLocalHost;
+ break;
+ case SET_NEWSRC:
+ if (myNewsrc) fs_give ((void **) &myNewsrc);
+ myNewsrc = cpystr ((char *) value);
+ case GET_NEWSRC:
+ ret = (void *) myNewsrc;
+ break;
+ case SET_NEWSACTIVE:
+ if (newsActive) fs_give ((void **) &newsActive);
+ newsActive = cpystr ((char *) value);
+ case GET_NEWSACTIVE:
+ ret = (void *) newsActive;
+ break;
+ case SET_NEWSSPOOL:
+ if (newsSpool) fs_give ((void **) &newsSpool);
+ newsSpool = cpystr ((char *) value);
+ case GET_NEWSSPOOL:
+ ret = (void *) newsSpool;
+ break;
+
+ case SET_ANONYMOUSHOME:
+ if (anonymousHome) fs_give ((void **) &anonymousHome);
+ anonymousHome = cpystr ((char *) value);
+ case GET_ANONYMOUSHOME:
+ if (!anonymousHome) anonymousHome = cpystr (ANONYMOUSHOME);
+ ret = (void *) anonymousHome;
+ break;
+ case SET_FTPHOME:
+ if (ftpHome) fs_give ((void **) &ftpHome);
+ ftpHome = cpystr ((char *) value);
+ case GET_FTPHOME:
+ ret = (void *) ftpHome;
+ break;
+ case SET_PUBLICHOME:
+ if (publicHome) fs_give ((void **) &publicHome);
+ publicHome = cpystr ((char *) value);
+ case GET_PUBLICHOME:
+ ret = (void *) publicHome;
+ break;
+ case SET_SHAREDHOME:
+ if (sharedHome) fs_give ((void **) &sharedHome);
+ sharedHome = cpystr ((char *) value);
+ case GET_SHAREDHOME:
+ ret = (void *) sharedHome;
+ break;
+ case SET_SYSINBOX:
+ if (sysInbox) fs_give ((void **) &sysInbox);
+ sysInbox = cpystr ((char *) value);
+ case GET_SYSINBOX:
+ ret = (void *) sysInbox;
+ break;
+ case SET_SSLCAPATH: /* this can be set null */
+ if (sslCApath) fs_give ((void **) &sslCApath);
+ sslCApath = value ? cpystr ((char *) value) : value;
+ break;
+ case GET_SSLCAPATH:
+ ret = (void *) sslCApath;
+ break;
+ case SET_LISTMAXLEVEL:
+ list_max_level = (long) value;
+ case GET_LISTMAXLEVEL:
+ ret = (void *) list_max_level;
+ break;
+
+ case SET_MBXPROTECTION:
+ mbx_protection = (long) value;
+ case GET_MBXPROTECTION:
+ ret = (void *) mbx_protection;
+ break;
+ case SET_DIRPROTECTION:
+ dir_protection = (long) value;
+ case GET_DIRPROTECTION:
+ ret = (void *) dir_protection;
+ break;
+ case SET_LOCKPROTECTION:
+ dotlock_mode = (long) value;
+ case GET_LOCKPROTECTION:
+ ret = (void *) dotlock_mode;
+ break;
+ case SET_FTPPROTECTION:
+ ftp_protection = (long) value;
+ case GET_FTPPROTECTION:
+ ret = (void *) ftp_protection;
+ break;
+ case SET_PUBLICPROTECTION:
+ public_protection = (long) value;
+ case GET_PUBLICPROTECTION:
+ ret = (void *) public_protection;
+ break;
+ case SET_SHAREDPROTECTION:
+ shared_protection = (long) value;
+ case GET_SHAREDPROTECTION:
+ ret = (void *) shared_protection;
+ break;
+ case SET_FTPDIRPROTECTION:
+ ftp_dir_protection = (long) value;
+ case GET_FTPDIRPROTECTION:
+ ret = (void *) ftp_dir_protection;
+ break;
+ case SET_PUBLICDIRPROTECTION:
+ public_dir_protection = (long) value;
+ case GET_PUBLICDIRPROTECTION:
+ ret = (void *) public_dir_protection;
+ break;
+ case SET_SHAREDDIRPROTECTION:
+ shared_dir_protection = (long) value;
+ case GET_SHAREDDIRPROTECTION:
+ ret = (void *) shared_dir_protection;
+ break;
+
+ case SET_LOCKTIMEOUT:
+ locktimeout = (long) value;
+ case GET_LOCKTIMEOUT:
+ ret = (void *) locktimeout;
+ break;
+ case SET_DISABLEFCNTLLOCK:
+ fcntlhangbug = value ? T : NIL;
+ case GET_DISABLEFCNTLLOCK:
+ ret = (void *) (fcntlhangbug ? VOIDT : NIL);
+ break;
+ case SET_LOCKEACCESERROR:
+ disableLockWarning = value ? NIL : T;
+ case GET_LOCKEACCESERROR:
+ ret = (void *) (disableLockWarning ? NIL : VOIDT);
+ break;
+ case SET_HIDEDOTFILES:
+ hideDotFiles = value ? T : NIL;
+ case GET_HIDEDOTFILES:
+ ret = (void *) (hideDotFiles ? VOIDT : NIL);
+ break;
+ case SET_DISABLEPLAINTEXT:
+ disablePlaintext = (long) value;
+ case GET_DISABLEPLAINTEXT:
+ ret = (void *) disablePlaintext;
+ break;
+ case SET_CHROOTSERVER:
+ closedBox = value ? T : NIL;
+ case GET_CHROOTSERVER:
+ ret = (void *) (closedBox ? VOIDT : NIL);
+ break;
+ case SET_ADVERTISETHEWORLD:
+ advertisetheworld = value ? T : NIL;
+ case GET_ADVERTISETHEWORLD:
+ ret = (void *) (advertisetheworld ? VOIDT : NIL);
+ break;
+ case SET_LIMITEDADVERTISE:
+ limitedadvertise = value ? T : NIL;
+ case GET_LIMITEDADVERTISE:
+ ret = (void *) (limitedadvertise ? VOIDT : NIL);
+ break;
+ case SET_DISABLEAUTOSHAREDNS:
+ noautomaticsharedns = value ? T : NIL;
+ case GET_DISABLEAUTOSHAREDNS:
+ ret = (void *) (noautomaticsharedns ? VOIDT : NIL);
+ break;
+ case SET_DISABLE822TZTEXT:
+ no822tztext = value ? T : NIL;
+ case GET_DISABLE822TZTEXT:
+ ret = (void *) (no822tztext ? VOIDT : NIL);
+ break;
+
+ case SET_USERHASNOLIFE:
+ has_no_life = value ? T : NIL;
+ case GET_USERHASNOLIFE:
+ ret = (void *) (has_no_life ? VOIDT : NIL);
+ break;
+ case SET_KERBEROS_CP_SVR_NAME:
+ kerb_cp_svr_name = value ? T : NIL;
+ case GET_KERBEROS_CP_SVR_NAME:
+ ret = (void *) (kerb_cp_svr_name ? VOIDT : NIL);
+ break;
+ case SET_NETFSSTATBUG:
+ netfsstatbug = value ? T : NIL;
+ case GET_NETFSSTATBUG:
+ ret = (void *) (netfsstatbug ? VOIDT : NIL);
+ break;
+ case SET_BLOCKENVINIT:
+ block_env_init = value ? T : NIL;
+ case GET_BLOCKENVINIT:
+ ret = (void *) (block_env_init ? VOIDT : NIL);
+ break;
+ case SET_BLOCKNOTIFY:
+ mailblocknotify = (blocknotify_t) value;
+ case GET_BLOCKNOTIFY:
+ ret = (void *) mailblocknotify;
+ break;
+ case SET_LOGOUTHOOK:
+ maillogouthook = (logouthook_t) value;
+ case GET_LOGOUTHOOK:
+ ret = maillogouthook;
+ break;
+ case SET_LOGOUTDATA:
+ maillogoutdata = (void *) value;
+ case GET_LOGOUTDATA:
+ ret = maillogoutdata;
+ }
+ return ret;
+}
+
+/* Write current time
+ * Accepts: destination string
+ * optional format of day-of-week prefix
+ * format of date and time
+ * flag whether to append symbolic timezone
+ */
+
+static void do_date (char *date,char *prefix,char *fmt,int suffix)
+{
+ time_t tn = time (0);
+ struct tm *t = gmtime (&tn);
+ int zone = t->tm_hour * 60 + t->tm_min;
+ int julian = t->tm_yday;
+ t = localtime (&tn); /* get local time now */
+ /* minus UTC minutes since midnight */
+ zone = t->tm_hour * 60 + t->tm_min - zone;
+ /* julian can be one of:
+ * 36x local time is December 31, UTC is January 1, offset -24 hours
+ * 1 local time is 1 day ahead of UTC, offset +24 hours
+ * 0 local time is same day as UTC, no offset
+ * -1 local time is 1 day behind UTC, offset -24 hours
+ * -36x local time is January 1, UTC is December 31, offset +24 hours
+ */
+ if (julian = t->tm_yday -julian)
+ zone += ((julian < 0) == (abs (julian) == 1)) ? -24*60 : 24*60;
+ if (prefix) { /* want day of week? */
+ sprintf (date,prefix,days[t->tm_wday]);
+ date += strlen (date); /* make next sprintf append */
+ }
+ /* output the date */
+ sprintf (date,fmt,t->tm_mday,months[t->tm_mon],t->tm_year+1900,
+ t->tm_hour,t->tm_min,t->tm_sec,zone/60,abs (zone) % 60);
+ /* append timezone suffix if desired */
+ if (suffix) rfc822_timezone (date,(void *) t);
+}
+
+/* Write current time in RFC 822 format
+ * Accepts: destination string
+ */
+
+void rfc822_date (char *date)
+{
+ do_date (date,"%s, ","%d %s %d %02d:%02d:%02d %+03d%02d",
+ no822tztext ? NIL : T);
+}
+
+
+/* Write current time in fixed-width RFC 822 format
+ * Accepts: destination string
+ */
+
+void rfc822_fixed_date (char *date)
+{
+ do_date (date,NIL,"%02d %s %4d %02d:%02d:%02d %+03d%02d",NIL);
+}
+
+
+/* Write current time in internal format
+ * Accepts: destination string
+ */
+
+void internal_date (char *date)
+{
+ do_date (date,NIL,"%02d-%s-%d %02d:%02d:%02d %+03d%02d",NIL);
+}
+
+/* Initialize server
+ * Accepts: server name for syslog or NIL
+ * /etc/services service name or NIL
+ * alternate /etc/services service name or NIL
+ * clock interrupt handler
+ * kiss-of-death interrupt handler
+ * hangup interrupt handler
+ * termination interrupt handler
+ */
+
+void server_init (char *server,char *service,char *sslservice,
+ void *clkint,void *kodint,void *hupint,void *trmint,
+ void *staint)
+{
+ int onceonly = server && service && sslservice;
+ if (onceonly) { /* set server name in syslog */
+ int mask;
+ openlog (myServerName = cpystr (server),LOG_PID,syslog_facility);
+ fclose (stderr); /* possibly save a process ID */
+ dorc (NIL,NIL); /* do systemwide configuration */
+ switch (mask = umask (022)){/* check old umask */
+ case 0: /* definitely unreasonable */
+ case 022: /* don't need to change it */
+ break;
+ default: /* already was a reasonable value */
+ umask (mask); /* so change it back */
+ }
+ }
+ arm_signal (SIGALRM,clkint); /* prepare for clock interrupt */
+ arm_signal (SIGUSR2,kodint); /* prepare for Kiss Of Death */
+ arm_signal (SIGHUP,hupint); /* prepare for hangup */
+ arm_signal (SIGPIPE,hupint); /* alternative hangup */
+ arm_signal (SIGTERM,trmint); /* prepare for termination */
+ /* status dump */
+ if (staint) arm_signal (SIGUSR1,staint);
+ if (onceonly) { /* set up network and maybe SSL */
+ long port;
+ struct servent *sv;
+ /* Use SSL if SSL service, or if server starts with "s" and not service */
+ if (((port = tcp_serverport ()) >= 0)) {
+ if ((sv = getservbyname (service,"tcp")) && (port == ntohs (sv->s_port)))
+ syslog (LOG_DEBUG,"%s service init from %s",service,tcp_clientaddr ());
+ else if ((sv = getservbyname (sslservice,"tcp")) &&
+ (port == ntohs (sv->s_port))) {
+ syslog (LOG_DEBUG,"%s SSL service init from %s",sslservice,
+ tcp_clientaddr ());
+ ssl_server_init (server);
+ }
+ else { /* not service or SSL service port */
+ syslog (LOG_DEBUG,"port %ld service init from %s",port,
+ tcp_clientaddr ());
+ if (*server == 's') ssl_server_init (server);
+ }
+ }
+ }
+}
+
+/* Wait for stdin input
+ * Accepts: timeout in seconds
+ * Returns: T if have input on stdin, else NIL
+ */
+
+long server_input_wait (long seconds)
+{
+ fd_set rfd,efd;
+ struct timeval tmo;
+ FD_ZERO (&rfd);
+ FD_ZERO (&efd);
+ FD_SET (0,&rfd);
+ FD_SET (0,&efd);
+ tmo.tv_sec = seconds; tmo.tv_usec = 0;
+ return select (1,&rfd,0,&efd,&tmo) ? LONGT : NIL;
+}
+
+/* Return UNIX password entry for user name
+ * Accepts: user name string
+ * Returns: password entry
+ *
+ * Tries all-lowercase form of user name if given user name fails
+ */
+
+static struct passwd *pwuser (unsigned char *user)
+{
+ unsigned char *s;
+ struct passwd *pw = getpwnam (user);
+ if (!pw) { /* failed, see if any uppercase characters */
+ for (s = user; *s && ((*s < 'A') || (*s > 'Z')); s++);
+ if (*s) { /* yes, try all lowercase form */
+ pw = getpwnam (s = lcase (cpystr (user)));
+ fs_give ((void **) &s);
+ }
+ }
+ return pw;
+}
+
+
+/* Validate password for user name
+ * Accepts: user name string
+ * password string
+ * argument count
+ * argument vector
+ * Returns: password entry if validated
+ *
+ * Tries password+1 if password fails and starts with space
+ */
+
+static struct passwd *valpwd (char *user,char *pwd,int argc,char *argv[])
+{
+ char *s;
+ struct passwd *pw;
+ struct passwd *ret = NIL;
+ if (auth_md5.server) { /* using CRAM-MD5 authentication? */
+ if (s = auth_md5_pwd (user)) {
+ if (!strcmp (s,pwd) || ((*pwd == ' ') && pwd[1] && !strcmp (s,pwd+1)))
+ ret = pwuser (user); /* validated, get passwd entry for user */
+ memset (s,0,strlen (s)); /* erase sensitive information */
+ fs_give ((void **) &s);
+ }
+ }
+ else if (pw = pwuser (user)) {/* can get user? */
+ s = cpystr (pw->pw_name); /* copy returned name in case we need it */
+ if (*pwd && !(ret = checkpw (pw,pwd,argc,argv)) &&
+ (*pwd == ' ') && pwd[1] && (ret = pwuser (s)))
+ ret = checkpw (pw,pwd+1,argc,argv);
+ fs_give ((void **) &s); /* don't need copy of name any more */
+ }
+ return ret;
+}
+
+/* Server log in
+ * Accepts: user name string
+ * password string
+ * authenticating user name string
+ * argument count
+ * argument vector
+ * Returns: T if password validated, NIL otherwise
+ */
+
+long server_login (char *user,char *pwd,char *authuser,int argc,char *argv[])
+{
+ struct passwd *pw = NIL;
+ int level = LOG_NOTICE;
+ char *err = "failed";
+ /* cretins still haven't given up */
+ if ((strlen (user) >= NETMAXUSER) ||
+ (authuser && (strlen (authuser) >= NETMAXUSER))) {
+ level = LOG_ALERT; /* escalate this alert */
+ err = "SYSTEM BREAK-IN ATTEMPT";
+ logtry = 0; /* render this session useless */
+ }
+ else if (logtry-- <= 0) err = "excessive login failures";
+ else if (disablePlaintext) err = "disabled";
+ else if (!(authuser && *authuser)) pw = valpwd (user,pwd,argc,argv);
+ else if (valpwd (authuser,pwd,argc,argv)) pw = pwuser (user);
+ if (pw && pw_login (pw,authuser,pw->pw_name,NIL,argc,argv)) return T;
+ syslog (level|LOG_AUTH,"Login %s user=%.64s auth=%.64s host=%.80s",err,
+ user,(authuser && *authuser) ? authuser : user,tcp_clienthost ());
+ sleep (3); /* slow down possible cracker */
+ return NIL;
+}
+
+/* Authenticated server log in
+ * Accepts: user name string
+ * authenticating user name string
+ * argument count
+ * argument vector
+ * Returns: T if password validated, NIL otherwise
+ */
+
+long authserver_login (char *user,char *authuser,int argc,char *argv[])
+{
+ return pw_login (pwuser (user),authuser,user,NIL,argc,argv);
+}
+
+
+/* Log in as anonymous daemon
+ * Accepts: argument count
+ * argument vector
+ * Returns: T if successful, NIL if error
+ */
+
+long anonymous_login (int argc,char *argv[])
+{
+ /* log in Mr. A. N. Onymous */
+ return pw_login (getpwnam (ANONYMOUSUSER),NIL,NIL,
+ (char *) mail_parameters (NIL,GET_ANONYMOUSHOME,NIL),
+ argc,argv);
+}
+
+/* Finish log in and environment initialization
+ * Accepts: passwd struct for loginpw()
+ * optional authentication user name
+ * user name (NIL for anonymous)
+ * home directory (NIL to use directory from passwd struct)
+ * argument count
+ * argument vector
+ * Returns: T if successful, NIL if error
+ */
+
+long pw_login (struct passwd *pw,char *auser,char *user,char *home,int argc,
+ char *argv[])
+{
+ struct group *gr;
+ char **t;
+ long ret = NIL;
+ if (pw && pw->pw_uid) { /* must have passwd struct for non-UID 0 */
+ /* make safe copies of user and home */
+ if (user) user = cpystr (pw->pw_name);
+ home = cpystr (home ? home : pw->pw_dir);
+ /* authorization ID .NE. authentication ID? */
+ if (user && auser && *auser && compare_cstring (auser,user)) {
+ /* scan list of mail administrators */
+ if ((gr = getgrnam (ADMINGROUP)) && (t = gr->gr_mem)) while (*t && !ret)
+ if (!compare_cstring (auser,*t++))
+ ret = pw_login (pw,NIL,user,home,argc,argv);
+ syslog (LOG_NOTICE|LOG_AUTH,"%s %.80s override of user=%.80s host=%.80s",
+ ret ? "Admin" : "Failed",auser,user,tcp_clienthost ());
+ }
+ else if (closedBox) { /* paranoid site, lock out other directories */
+ if (chdir (home) || chroot (home))
+ syslog (LOG_NOTICE|LOG_AUTH,
+ "Login %s failed: unable to set chroot=%.80s host=%.80s",
+ pw->pw_name,home,tcp_clienthost ());
+ else if (loginpw (pw,argc,argv)) ret = env_init (user,NIL);
+ else fatal ("Login failed after chroot");
+ }
+ /* normal login */
+ else if (((pw->pw_uid == geteuid ()) || loginpw (pw,argc,argv)) &&
+ (ret = env_init (user,home))) chdir (myhomedir ());
+ fs_give ((void **) &home); /* clean up */
+ if (user) fs_give ((void **) &user);
+ }
+ endpwent (); /* in case shadow passwords in pw data */
+ return ret; /* return status */
+}
+
+/* Initialize environment
+ * Accepts: user name (NIL for anonymous)
+ * home directory name
+ * Returns: T, always
+ */
+
+long env_init (char *user,char *home)
+{
+ extern MAILSTREAM CREATEPROTO;
+ extern MAILSTREAM EMPTYPROTO;
+ struct passwd *pw;
+ struct stat sbuf;
+ char tmp[MAILTMPLEN];
+ /* don't init if blocked */
+ if (block_env_init) return LONGT;
+ if (myUserName) fatal ("env_init called twice!");
+ /* initially nothing in namespace list */
+ nslist[0] = nslist[1] = nslist[2] = NIL;
+ /* myUserName must be set before dorc() call */
+ myUserName = cpystr (user ? user : ANONYMOUSUSER);
+ /* force default prototypes to be set */
+ if (!createProto) createProto = &CREATEPROTO;
+ if (!appendProto) appendProto = &EMPTYPROTO;
+ dorc (NIL,NIL); /* do systemwide configuration */
+ if (!home) { /* closed box server */
+ /* standard user can only reference home */
+ if (user) nslist[0] = &nshome;
+ else { /* anonymous user */
+ nslist[0] = &nsblackother; /* set root */
+ anonymous = T; /* flag as anonymous */
+ }
+ myHomeDir = cpystr (""); /* home directory is root */
+ sysInbox = cpystr ("INBOX");/* make system INBOX */
+ }
+ else { /* open or black box */
+ closedBox = NIL; /* definitely not a closed box */
+ if (user) { /* remember user name and home directory */
+ if (blackBoxDir) { /* build black box directory name */
+ sprintf (tmp,"%s/%s",blackBoxDir,myUserName);
+ /* must exist */
+ if (!((!stat (home = tmp,&sbuf) && (sbuf.st_mode & S_IFDIR)) ||
+ (blackBoxDefaultHome &&
+ !stat (home = blackBoxDefaultHome,&sbuf) &&
+ (sbuf.st_mode & S_IFDIR)))) fatal ("no home");
+ sysInbox = (char *) fs_get (strlen (home) + 7);
+ /* set system INBOX */
+ sprintf (sysInbox,"%s/INBOX",home);
+ blackBox = T; /* mark that it's a black box */
+ /* mbox meaningless if black box */
+ mail_parameters (NIL,DISABLE_DRIVER,(void *) "mbox");
+ }
+ nslist[0] = &nshome; /* home namespace */
+ /* limited advertise namespaces */
+ if (limitedadvertise) nslist[2] = &nslimited;
+ else if (blackBox) { /* black box namespaces */
+ nslist[1] = &nsblackother;
+ nslist[2] = &nsshared;
+ }
+ else { /* open box namespaces */
+ nslist[1] = &nsunixother;
+ nslist[2] = advertisetheworld ? &nsworld : &nsshared;
+ }
+ }
+ else {
+ nslist[2] = &nsftp; /* anonymous user */
+ sprintf (tmp,"%s/INBOX",
+ home = (char *) mail_parameters (NIL,GET_ANONYMOUSHOME,NIL));
+ sysInbox = cpystr (tmp); /* make system INBOX */
+ anonymous = T; /* flag as anonymous */
+ }
+ myHomeDir = cpystr (home); /* set home directory */
+ }
+
+ if (allowuserconfig) { /* allow user config files */
+ dorc (strcat (strcpy (tmp,myHomeDir),"/.mminit"),T);
+ dorc (strcat (strcpy (tmp,myHomeDir),"/.imaprc"),NIL);
+ }
+ if (!closedBox && !noautomaticsharedns) {
+ /* #ftp namespace */
+ if (!ftpHome && (pw = getpwnam ("ftp"))) ftpHome = cpystr (pw->pw_dir);
+ /* #public namespace */
+ if (!publicHome && (pw = getpwnam ("imappublic")))
+ publicHome = cpystr (pw->pw_dir);
+ /* #shared namespace */
+ if (!anonymous && !sharedHome && (pw = getpwnam ("imapshared")))
+ sharedHome = cpystr (pw->pw_dir);
+ }
+ if (!myLocalHost) mylocalhost ();
+ if (!myNewsrc) myNewsrc = cpystr(strcat (strcpy (tmp,myHomeDir),"/.newsrc"));
+ if (!newsActive) newsActive = cpystr (ACTIVEFILE);
+ if (!newsSpool) newsSpool = cpystr (NEWSSPOOL);
+ /* re-do open action to get flags */
+ (*createProto->dtb->open) (NIL);
+ endpwent (); /* close pw database */
+ return T;
+}
+
+/* Return my user name
+ * Accepts: pointer to optional flags
+ * Returns: my user name
+ */
+
+char *myusername_full (unsigned long *flags)
+{
+ struct passwd *pw;
+ struct stat sbuf;
+ char *s;
+ unsigned long euid;
+ char *ret = UNLOGGEDUSER;
+ /* no user name yet and not root? */
+ if (!myUserName && (euid = geteuid ())) {
+ /* yes, look up getlogin() user name or EUID */
+ if (((s = (char *) getlogin ()) && *s && (strlen (s) < NETMAXUSER) &&
+ (pw = getpwnam (s)) && (pw->pw_uid == euid)) ||
+ (pw = getpwuid (euid))) {
+ if (block_env_init) { /* don't env_init if blocked */
+ if (flags) *flags = MU_LOGGEDIN;
+ return pw->pw_name;
+ }
+ env_init (pw->pw_name,
+ ((s = getenv ("HOME")) && *s && (strlen (s) < NETMAXMBX) &&
+ !stat (s,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) ?
+ s : pw->pw_dir);
+ }
+ else fatal ("Unable to look up user name");
+ }
+ if (myUserName) { /* logged in? */
+ if (flags) *flags = anonymous ? MU_ANONYMOUS : MU_LOGGEDIN;
+ ret = myUserName; /* return user name */
+ }
+ else if (flags) *flags = MU_NOTLOGGEDIN;
+ return ret;
+}
+
+
+/* Return my local host name
+ * Returns: my local host name
+ */
+
+char *mylocalhost ()
+{
+ if (!myLocalHost) {
+ char *s,tmp[MAILTMPLEN];
+ char *t = "unknown";
+ tmp[0] = tmp[MAILTMPLEN-1] = '\0';
+ if (!gethostname (tmp,MAILTMPLEN-1) && tmp[0]) {
+ /* sanity check of name */
+ for (s = tmp; (*s > 0x20) && (*s < 0x7f); ++s);
+ if (!*s) t = tcp_canonical (tmp);
+ }
+ myLocalHost = cpystr (t);
+ }
+ return myLocalHost;
+}
+
+/* Return my home directory name
+ * Returns: my home directory name
+ */
+
+char *myhomedir ()
+{
+ if (!myHomeDir) myusername ();/* initialize if first time */
+ return myHomeDir ? myHomeDir : "";
+}
+
+
+/* Return my home mailbox name
+ * Returns: my home directory name
+ */
+
+static char *mymailboxdir ()
+{
+ char *home = myhomedir ();
+ /* initialize if first time */
+ if (!myMailboxDir && myHomeDir) {
+ if (mailsubdir) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"%s/%s",home,mailsubdir);
+ myMailboxDir = cpystr (tmp);/* use pre-defined subdirectory of home */
+ }
+ else myMailboxDir = cpystr (home);
+ }
+ return myMailboxDir ? myMailboxDir : "";
+}
+
+
+/* Return system standard INBOX
+ * Accepts: buffer string
+ */
+
+char *sysinbox ()
+{
+ char tmp[MAILTMPLEN];
+ if (!sysInbox) { /* initialize if first time */
+ sprintf (tmp,"%s/%s",MAILSPOOL,myusername ());
+ sysInbox = cpystr (tmp); /* system inbox is from mail spool */
+ }
+ return sysInbox;
+}
+
+/* Return mailbox directory name
+ * Accepts: destination buffer
+ * directory prefix
+ * name in directory
+ * Returns: file name or NIL if error
+ */
+
+char *mailboxdir (char *dst,char *dir,char *name)
+{
+ char tmp[MAILTMPLEN];
+ if (dir || name) { /* if either argument provided */
+ if (dir) {
+ if (strlen (dir) > NETMAXMBX) return NIL;
+ strcpy (tmp,dir); /* write directory prefix */
+ }
+ else tmp[0] = '\0'; /* otherwise null string */
+ if (name) {
+ if (strlen (name) > NETMAXMBX) return NIL;
+ strcat (tmp,name); /* write name in directory */
+ }
+ /* validate name, return its name */
+ if (!mailboxfile (dst,tmp)) return NIL;
+ }
+ /* no arguments, wants mailbox directory */
+ else strcpy (dst,mymailboxdir ());
+ return dst; /* return the name */
+}
+
+/* Return mailbox file name
+ * Accepts: destination buffer
+ * mailbox name
+ * Returns: file name or empty string for driver-selected INBOX or NIL if error
+ */
+
+char *mailboxfile (char *dst,char *name)
+{
+ struct passwd *pw;
+ char *s;
+ if (!name || !*name || (*name == '{') || (strlen (name) > NETMAXMBX) ||
+ ((anonymous || blackBox || restrictBox || (*name == '#')) &&
+ (strstr (name,"..") || strstr (name,"//") || strstr (name,"/~"))))
+ dst = NIL; /* invalid name */
+ else switch (*name) { /* determine mailbox type based upon name */
+ case '#': /* namespace name */
+ /* #ftp/ namespace */
+ if (((name[1] == 'f') || (name[1] == 'F')) &&
+ ((name[2] == 't') || (name[2] == 'T')) &&
+ ((name[3] == 'p') || (name[3] == 'P')) &&
+ (name[4] == '/') && ftpHome) sprintf (dst,"%s/%s",ftpHome,name+5);
+ /* #public/ and #shared/ namespaces */
+ else if ((((name[1] == 'p') || (name[1] == 'P')) &&
+ ((name[2] == 'u') || (name[2] == 'U')) &&
+ ((name[3] == 'b') || (name[3] == 'B')) &&
+ ((name[4] == 'l') || (name[4] == 'L')) &&
+ ((name[5] == 'i') || (name[5] == 'I')) &&
+ ((name[6] == 'c') || (name[6] == 'C')) &&
+ (name[7] == '/') && (s = publicHome)) ||
+ (!anonymous && ((name[1] == 's') || (name[1] == 'S')) &&
+ ((name[2] == 'h') || (name[2] == 'H')) &&
+ ((name[3] == 'a') || (name[3] == 'A')) &&
+ ((name[4] == 'r') || (name[4] == 'R')) &&
+ ((name[5] == 'e') || (name[5] == 'E')) &&
+ ((name[6] == 'd') || (name[6] == 'D')) &&
+ (name[7] == '/') && (s = sharedHome)))
+ sprintf (dst,"%s/%s",s,compare_cstring (name+8,"INBOX") ?
+ name+8 : "INBOX");
+ else dst = NIL; /* unknown namespace */
+ break;
+
+ case '/': /* root access */
+ if (anonymous) dst = NIL; /* anonymous forbidden to do this */
+ else if (blackBox) { /* other user access if blackbox */
+ if (restrictBox & RESTRICTOTHERUSER) dst = NIL;
+ /* see if other user INBOX */
+ else if ((s = strchr (name+1,'/')) && !compare_cstring (s+1,"INBOX")) {
+ *s = '\0'; /* temporarily tie off string */
+ sprintf (dst,"%s/%s/INBOX",blackBoxDir,name+1);
+ *s = '/'; /* in case caller cares */
+ }
+ else sprintf (dst,"%s/%s",blackBoxDir,name+1);
+ }
+ else if ((restrictBox & RESTRICTROOT) && strcmp (name,sysinbox ()))
+ dst = NIL; /* restricted and not access to sysinbox */
+ else strcpy (dst,name); /* unrestricted, copy root name */
+ break;
+ case '~': /* other user access */
+ /* bad syntax or anonymous can't win */
+ if (!*++name || anonymous) dst = NIL;
+ /* ~/ equivalent to ordinary name */
+ else if (*name == '/') sprintf (dst,"%s/%s",mymailboxdir (),name+1);
+ /* other user forbidden if closed/restricted */
+ else if (closedBox || (restrictBox & RESTRICTOTHERUSER)) dst = NIL;
+ else if (blackBox) { /* black box form of other user */
+ /* see if other user INBOX */
+ if ((s = strchr (name,'/')) && compare_cstring (s+1,"INBOX")) {
+ *s = '\0'; /* temporarily tie off string */
+ sprintf (dst,"%s/%s/INBOX",blackBoxDir,name);
+ *s = '/'; /* in case caller cares */
+ }
+ else sprintf (dst,"%s/%s",blackBoxDir,name);
+ }
+ else { /* clear box other user */
+ /* copy user name */
+ for (s = dst; *name && (*name != '/'); *s++ = *name++);
+ *s++ = '\0'; /* tie off user name, look up in passwd file */
+ if ((pw = getpwnam (dst)) && pw->pw_dir) {
+ if (*name) name++; /* skip past the slash */
+ /* canonicalize case of INBOX */
+ if (!compare_cstring (name,"INBOX")) name = "INBOX";
+ /* remove trailing / from directory */
+ if ((s = strrchr (pw->pw_dir,'/')) && !s[1]) *s = '\0';
+ /* don't allow ~root/ if restricted root */
+ if ((restrictBox & RESTRICTROOT) && !*pw->pw_dir) dst = NIL;
+ /* build final name w/ subdir if needed */
+ else if (mailsubdir) sprintf (dst,"%s/%s/%s",pw->pw_dir,mailsubdir,name);
+ else sprintf (dst,"%s/%s",pw->pw_dir,name);
+ }
+ else dst = NIL; /* no such user */
+ }
+ break;
+
+ case 'I': case 'i': /* possible INBOX */
+ if (!compare_cstring (name+1,"NBOX")) {
+ /* if restricted, use INBOX in mailbox dir */
+ if (anonymous || blackBox || closedBox)
+ sprintf (dst,"%s/INBOX",mymailboxdir ());
+ else *dst = '\0'; /* otherwise driver selects the name */
+ break;
+ }
+ /* drop into to ordinary name case */
+ default: /* ordinary name is easy */
+ sprintf (dst,"%s/%s",mymailboxdir (),name);
+ break;
+ }
+ return dst; /* return final name */
+}
+
+/* Dot-lock file locker
+ * Accepts: file name to lock
+ * destination buffer for lock file name
+ * open file description on file name to lock
+ * Returns: T if success, NIL if failure
+ */
+
+long dotlock_lock (char *file,DOTLOCK *base,int fd)
+{
+ int i = locktimeout * 60;
+ int j,mask,retry,pi[2],po[2];
+ char *s,tmp[MAILTMPLEN];
+ struct stat sb;
+ /* flush absurd file name */
+ if (strlen (file) > 512) return NIL;
+ /* build lock filename */
+ sprintf (base->lock,"%s.lock",file);
+ /* assume no pipe */
+ base->pipei = base->pipeo = -1;
+ do { /* make sure not symlink */
+ if (!(j = chk_notsymlink (base->lock,&sb))) return NIL;
+ /* time out if file older than 5 minutes */
+ if ((j > 0) && ((time (0)) >= (sb.st_ctime + locktimeout * 60))) i = 0;
+ /* try to create the lock */
+ switch (retry = crexcl (base->lock)) {
+ case -1: /* OK to retry */
+ if (!(i%15)) { /* time to notify? */
+ sprintf (tmp,"Mailbox %.80s is locked, will override in %d seconds...",
+ file,i);
+ MM_LOG (tmp,WARN);
+ }
+ sleep (1); /* wait 1 second before next try */
+ break;
+ case NIL: /* failure, can't retry */
+ i = 0;
+ break;
+ case T: /* success, make sure others can break lock */
+ chmod (base->lock,(int) dotlock_mode);
+ return LONGT;
+ }
+ } while (i--); /* until out of retries */
+ if (retry < 0) { /* still returning retry after locktimeout? */
+ if (!(j = chk_notsymlink (base->lock,&sb))) return NIL;
+ if ((j > 0) && ((time (0)) < (sb.st_ctime + locktimeout * 60))) {
+ sprintf (tmp,"Mailbox vulnerable - seizing %ld second old lock",
+ (long) (time (0) - sb.st_ctime));
+ MM_LOG (tmp,WARN);
+ }
+ mask = umask (0); /* want our lock protection */
+ unlink (base->lock); /* try to remove the old file */
+ /* seize the lock */
+ if ((i = open (base->lock,O_WRONLY|O_CREAT,(int) dotlock_mode)) >= 0) {
+ close (i); /* don't need descriptor any more */
+ sprintf (tmp,"Mailbox %.80s lock overridden",file);
+ MM_LOG (tmp,NIL);
+ chmod (base->lock,(int) dotlock_mode);
+ umask (mask); /* restore old umask */
+ return LONGT;
+ }
+ umask (mask); /* restore old umask */
+ }
+
+ if (fd >= 0) switch (errno) {
+ case EACCES: /* protection failure? */
+ MM_CRITICAL (NIL); /* go critical */
+ if (closedBox || !lockpgm); /* can't do on closed box or disabled */
+ else if ((*lockpgm && stat (lockpgm,&sb)) ||
+ (!*lockpgm && stat (lockpgm = LOCKPGM1,&sb) &&
+ stat (lockpgm = LOCKPGM2,&sb) && stat (lockpgm = LOCKPGM3,&sb)))
+ lockpgm = NIL; /* disable if can't find lockpgm */
+ else if (pipe (pi) >= 0) { /* make command pipes */
+ long cf;
+ char *argv[4],arg[20];
+ /* if input pipes usable create output pipes */
+ if ((pi[0] < FD_SETSIZE) && (pi[1] < FD_SETSIZE) && (pipe (po) >= 0)) {
+ /* make sure output pipes are usable */
+ if ((po[0] >= FD_SETSIZE) || (po[1] >= FD_SETSIZE));
+ /* all is good, make inferior process */
+ else if (!(j = fork ())) {
+ if (!fork ()) { /* make grandchild so it's inherited by init */
+ /* prepare argument vector */
+ sprintf (arg,"%d",fd);
+ argv[0] = lockpgm; argv[1] = arg;
+ argv[2] = file; argv[3] = NIL;
+ /* set parent's I/O to my O/I */
+ dup2 (pi[1],1); dup2 (pi[1],2); dup2 (po[0],0);
+ /* close all unnecessary descriptors */
+ for (cf = max (20,max (max (pi[0],pi[1]),max(po[0],po[1])));
+ cf >= 3; --cf) if (cf != fd) close (cf);
+ /* be our own process group */
+ setpgrp (0,getpid ());
+ /* now run it */
+ _exit (execv (argv[0],argv));
+ }
+ _exit (1); /* child is done */
+ }
+ else if (j > 0) { /* parent process */
+ fd_set rfd;
+ struct timeval tmo;
+ FD_ZERO (&rfd);
+ FD_SET (pi[0],&rfd);
+ tmo.tv_sec = locktimeout * 60;
+ grim_pid_reap (j,NIL);/* reap child; grandchild now owned by init */
+ /* read response from locking program */
+ if (select (pi[0]+1,&rfd,0,0,&tmo) &&
+ (read (pi[0],tmp,1) == 1) && (tmp[0] == '+')) {
+ /* success, record pipes */
+ base->pipei = pi[0]; base->pipeo = po[1];
+ /* close child's side of the pipes */
+ close (pi[1]); close (po[0]);
+ MM_NOCRITICAL (NIL);/* no longer critical */
+ return LONGT;
+ }
+ }
+ close (po[0]); close (po[1]);
+ }
+ close (pi[0]); close (pi[1]);
+ }
+
+ MM_NOCRITICAL (NIL); /* no longer critical */
+ /* find directory/file delimiter */
+ if (s = strrchr (base->lock,'/')) {
+ *s = '\0'; /* tie off at directory */
+ sprintf(tmp, /* generate default message */
+ "Mailbox vulnerable - directory %.80s must have 1777 protection",
+ base->lock);
+ /* definitely not 1777 if can't stat */
+ mask = stat (base->lock,&sb) ? 0 : (sb.st_mode & 1777);
+ *s = '/'; /* restore lock name */
+ if (mask != 1777) { /* default warning if not 1777 */
+ if (!disableLockWarning) MM_LOG (tmp,WARN);
+ break;
+ }
+ }
+ default:
+ sprintf (tmp,"Mailbox vulnerable - error creating %.80s: %s",
+ base->lock,strerror (errno));
+ if (!disableLockWarning) MM_LOG (tmp,WARN);
+ break;
+ }
+ base->lock[0] = '\0'; /* don't use lock files */
+ return NIL;
+}
+
+/* Dot-lock file unlocker
+ * Accepts: lock file name
+ * Returns: T if success, NIL if failure
+ */
+
+long dotlock_unlock (DOTLOCK *base)
+{
+ long ret = LONGT;
+ if (base && base->lock[0]) {
+ if (base->pipei >= 0) { /* if running through a pipe unlocker */
+ ret = (write (base->pipeo,"+",1) == 1);
+ /* nuke the pipes */
+ close (base->pipei); close (base->pipeo);
+ }
+ else ret = !unlink (base->lock);
+ }
+ return ret;
+}
+
+/* Lock file name
+ * Accepts: scratch buffer
+ * file name
+ * type of locking operation (LOCK_SH or LOCK_EX)
+ * pointer to return PID of locker
+ * Returns: file descriptor of lock or negative if error
+ */
+
+int lockname (char *lock,char *fname,int op,long *pid)
+{
+ struct stat sbuf;
+ *pid = 0; /* no locker PID */
+ return stat (fname,&sbuf) ? -1 : lock_work (lock,&sbuf,op,pid);
+}
+
+
+/* Lock file descriptor
+ * Accepts: file descriptor
+ * lock file name buffer
+ * type of locking operation (LOCK_SH or LOCK_EX)
+ * Returns: file descriptor of lock or negative if error
+ */
+
+int lockfd (int fd,char *lock,int op)
+{
+ struct stat sbuf;
+ return fstat (fd,&sbuf) ? -1 : lock_work (lock,&sbuf,op,NIL);
+}
+
+/* Lock file name worker
+ * Accepts: lock file name
+ * pointer to stat() buffer
+ * type of locking operation (LOCK_SH or LOCK_EX)
+ * pointer to return PID of locker
+ * Returns: file descriptor of lock or negative if error
+ */
+
+int lock_work (char *lock,void *sb,int op,long *pid)
+{
+ struct stat lsb,fsb;
+ struct stat *sbuf = (struct stat *) sb;
+ char tmp[MAILTMPLEN];
+ long i;
+ int fd;
+ int mask = umask (0);
+ if (pid) *pid = 0; /* initialize return PID */
+ /* make temporary lock file name */
+ sprintf (lock,"%s/.%lx.%lx",closedBox ? "" : tmpdir,
+ (unsigned long) sbuf->st_dev,(unsigned long) sbuf->st_ino);
+ while (T) { /* until get a good lock */
+ do switch ((int) chk_notsymlink (lock,&lsb)) {
+ case 1: /* exists just once */
+ if (((fd = open (lock,O_RDWR,shlock_mode)) >= 0) ||
+ (errno != ENOENT) || (chk_notsymlink (lock,&lsb) >= 0)) break;
+ case -1: /* name doesn't exist */
+ fd = open (lock,O_RDWR|O_CREAT|O_EXCL,shlock_mode);
+ break;
+ default: /* multiple hard links */
+ MM_LOG ("hard link to lock name",ERROR);
+ syslog (LOG_CRIT,"SECURITY PROBLEM: hard link to lock name: %.80s",lock);
+ case 0: /* symlink (already did syslog) */
+ umask (mask); /* restore old mask */
+ return -1; /* fail: no lock file */
+ } while ((fd < 0) && (errno == EEXIST));
+ if (fd < 0) { /* failed to get file descriptor */
+ syslog (LOG_INFO,"Mailbox lock file %s open failure: %s",lock,
+ strerror (errno));
+ if (!closedBox) { /* more explicit snarl for bad configuration */
+ if (stat (tmpdir,&lsb))
+ syslog (LOG_CRIT,"SYSTEM ERROR: no %s: %s",tmpdir,strerror (errno));
+ else if ((lsb.st_mode & 01777) != 01777) {
+ sprintf (tmp,"Can't lock for write: %.80s must have 1777 protection",
+ tmpdir);
+ MM_LOG (tmp,WARN);
+ }
+ }
+ umask (mask); /* restore old mask */
+ return -1; /* fail: can't open lock file */
+ }
+
+ /* non-blocking form */
+ if (op & LOCK_NB) i = flock (fd,op);
+ else { /* blocking form */
+ (*mailblocknotify) (BLOCK_FILELOCK,NIL);
+ i = flock (fd,op);
+ (*mailblocknotify) (BLOCK_NONE,NIL);
+ }
+ if (i) { /* failed, get other process' PID */
+ if (pid && !fstat (fd,&fsb) && (i = min (fsb.st_size,MAILTMPLEN-1)) &&
+ (read (fd,tmp,i) == i) && !(tmp[i] = 0) && ((i = atol (tmp)) > 0))
+ *pid = i;
+ close (fd); /* failed, give up on lock */
+ umask (mask); /* restore old mask */
+ return -1; /* fail: can't lock */
+ }
+ /* make sure this lock is good for us */
+ if (!lstat (lock,&lsb) && ((lsb.st_mode & S_IFMT) != S_IFLNK) &&
+ !fstat (fd,&fsb) && (lsb.st_dev == fsb.st_dev) &&
+ (lsb.st_ino == fsb.st_ino) && (fsb.st_nlink == 1)) break;
+ close (fd); /* lock not right, drop fd and try again */
+ }
+ chmod (lock,shlock_mode); /* make sure mode OK (don't use fchmod()) */
+ umask (mask); /* restore old mask */
+ return fd; /* success */
+}
+
+/* Check to make sure not a symlink
+ * Accepts: file name
+ * stat buffer
+ * Returns: -1 if doesn't exist, NIL if symlink, else number of hard links
+ */
+
+long chk_notsymlink (char *name,void *sb)
+{
+ struct stat *sbuf = (struct stat *) sb;
+ /* name exists? */
+ if (lstat (name,sbuf)) return -1;
+ /* forbid symbolic link */
+ if ((sbuf->st_mode & S_IFMT) == S_IFLNK) {
+ MM_LOG ("symbolic link on lock name",ERROR);
+ syslog (LOG_CRIT,"SECURITY PROBLEM: symbolic link on lock name: %.80s",
+ name);
+ return NIL;
+ }
+ return (long) sbuf->st_nlink; /* return number of hard links */
+}
+
+
+/* Unlock file descriptor
+ * Accepts: file descriptor
+ * lock file name from lockfd()
+ */
+
+void unlockfd (int fd,char *lock)
+{
+ /* delete the file if no sharers */
+ if (!flock (fd,LOCK_EX|LOCK_NB)) unlink (lock);
+ flock (fd,LOCK_UN); /* unlock it */
+ close (fd); /* close it */
+}
+
+/* Set proper file protection for mailbox
+ * Accepts: mailbox name
+ * actual file path name
+ * Returns: T, always
+ */
+
+long set_mbx_protections (char *mailbox,char *path)
+{
+ struct stat sbuf;
+ int mode = (int) mbx_protection;
+ if (*mailbox == '#') { /* possible namespace? */
+ if (((mailbox[1] == 'f') || (mailbox[1] == 'F')) &&
+ ((mailbox[2] == 't') || (mailbox[2] == 'T')) &&
+ ((mailbox[3] == 'p') || (mailbox[3] == 'P')) &&
+ (mailbox[4] == '/')) mode = (int) ftp_protection;
+ else if (((mailbox[1] == 'p') || (mailbox[1] == 'P')) &&
+ ((mailbox[2] == 'u') || (mailbox[2] == 'U')) &&
+ ((mailbox[3] == 'b') || (mailbox[3] == 'B')) &&
+ ((mailbox[4] == 'l') || (mailbox[4] == 'L')) &&
+ ((mailbox[5] == 'i') || (mailbox[5] == 'I')) &&
+ ((mailbox[6] == 'c') || (mailbox[6] == 'C')) &&
+ (mailbox[7] == '/')) mode = (int) public_protection;
+ else if (((mailbox[1] == 's') || (mailbox[1] == 'S')) &&
+ ((mailbox[2] == 'h') || (mailbox[2] == 'H')) &&
+ ((mailbox[3] == 'a') || (mailbox[3] == 'A')) &&
+ ((mailbox[4] == 'r') || (mailbox[4] == 'R')) &&
+ ((mailbox[5] == 'e') || (mailbox[5] == 'E')) &&
+ ((mailbox[6] == 'd') || (mailbox[6] == 'D')) &&
+ (mailbox[7] == '/')) mode = (int) shared_protection;
+ }
+ /* if a directory */
+ if (!stat (path,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) {
+ /* set owner search if allow read or write */
+ if (mode & 0600) mode |= 0100;
+ if (mode & 060) mode |= 010;/* set group search if allow read or write */
+ if (mode & 06) mode |= 01; /* set world search if allow read or write */
+ /* preserve directory SGID bit */
+ if (sbuf.st_mode & S_ISGID) mode |= S_ISGID;
+ }
+ chmod (path,mode); /* set the new protection, ignore failure */
+ return LONGT;
+}
+
+/* Get proper directory protection
+ * Accepts: mailbox name
+ * Returns: directory mode, always
+ */
+
+long get_dir_protection (char *mailbox)
+{
+ if (*mailbox == '#') { /* possible namespace? */
+ if (((mailbox[1] == 'f') || (mailbox[1] == 'F')) &&
+ ((mailbox[2] == 't') || (mailbox[2] == 'T')) &&
+ ((mailbox[3] == 'p') || (mailbox[3] == 'P')) &&
+ (mailbox[4] == '/')) return ftp_dir_protection;
+ else if (((mailbox[1] == 'p') || (mailbox[1] == 'P')) &&
+ ((mailbox[2] == 'u') || (mailbox[2] == 'U')) &&
+ ((mailbox[3] == 'b') || (mailbox[3] == 'B')) &&
+ ((mailbox[4] == 'l') || (mailbox[4] == 'L')) &&
+ ((mailbox[5] == 'i') || (mailbox[5] == 'I')) &&
+ ((mailbox[6] == 'c') || (mailbox[6] == 'C')) &&
+ (mailbox[7] == '/')) return public_dir_protection;
+ else if (((mailbox[1] == 's') || (mailbox[1] == 'S')) &&
+ ((mailbox[2] == 'h') || (mailbox[2] == 'H')) &&
+ ((mailbox[3] == 'a') || (mailbox[3] == 'A')) &&
+ ((mailbox[4] == 'r') || (mailbox[4] == 'R')) &&
+ ((mailbox[5] == 'e') || (mailbox[5] == 'E')) &&
+ ((mailbox[6] == 'd') || (mailbox[6] == 'D')) &&
+ (mailbox[7] == '/')) return shared_dir_protection;
+ }
+ return dir_protection;
+}
+
+/* Determine default prototype stream to user
+ * Accepts: type (NIL for create, T for append)
+ * Returns: default prototype stream
+ */
+
+MAILSTREAM *default_proto (long type)
+{
+ myusername (); /* make sure initialized */
+ /* return default driver's prototype */
+ return type ? appendProto : createProto;
+}
+
+
+/* Set up user flags for stream
+ * Accepts: MAIL stream
+ * Returns: MAIL stream with user flags set up
+ */
+
+MAILSTREAM *user_flags (MAILSTREAM *stream)
+{
+ int i;
+ myusername (); /* make sure initialized */
+ for (i = 0; i < NUSERFLAGS && userFlags[i]; ++i)
+ if (!stream->user_flags[i]) stream->user_flags[i] = cpystr (userFlags[i]);
+ return stream;
+}
+
+
+/* Return nth user flag
+ * Accepts: user flag number
+ * Returns: flag
+ */
+
+char *default_user_flag (unsigned long i)
+{
+ myusername (); /* make sure initialized */
+ return userFlags[i];
+}
+
+/* Process rc file
+ * Accepts: file name
+ * .mminit flag
+ * Don't use this feature.
+ */
+
+void dorc (char *file,long flag)
+{
+ int i;
+ char *s,*t,*k,*r,tmp[MAILTMPLEN],tmpx[MAILTMPLEN];
+ extern MAILSTREAM CREATEPROTO;
+ extern MAILSTREAM EMPTYPROTO;
+ DRIVER *d;
+ FILE *f;
+ if ((f = fopen (file ? file : SYSCONFIG,"r")) &&
+ (s = fgets (tmp,MAILTMPLEN,f)) && (t = strchr (s,'\n'))) do {
+ *t++ = '\0'; /* tie off line, find second space */
+ if ((k = strchr (s,' ')) && (k = strchr (++k,' '))) {
+ *k++ = '\0'; /* tie off two words */
+ if (!compare_cstring (s,"set keywords") && !userFlags[0]) {
+ /* yes, get first keyword */
+ k = strtok_r (k,", ",&r);
+ /* copy keyword list */
+ for (i = 0; k && i < NUSERFLAGS; ++i) if (strlen (k) <= MAXUSERFLAG) {
+ if (userFlags[i]) fs_give ((void **) &userFlags[i]);
+ userFlags[i] = cpystr (k);
+ k = strtok_r (NIL,", ",&r);
+ }
+ if (flag) break; /* found "set keywords" in .mminit */
+ }
+
+ else if (!flag) { /* none of these valid in .mminit */
+ if (myUserName) { /* only valid if logged in */
+ if (!compare_cstring (s,"set new-mailbox-format") ||
+ !compare_cstring (s,"set new-folder-format")) {
+ if (!compare_cstring (k,"same-as-inbox")) {
+ if (d = mail_valid (NIL,"INBOX",NIL)) {
+ if (!compare_cstring (d->name,"mbox"))
+ d = (DRIVER *) mail_parameters (NIL,GET_DRIVER,
+ (void *) "unix");
+ else if (!compare_cstring (d->name,"dummy")) d = NIL;
+ }
+ createProto = d ? ((*d->open) (NIL)) : &CREATEPROTO;
+ }
+ else if (!compare_cstring (k,"system-standard"))
+ createProto = &CREATEPROTO;
+ else { /* canonicalize mbox to unix */
+ if (!compare_cstring (k,"mbox")) k = "unix";
+ /* see if a driver name */
+ if (d = (DRIVER *) mail_parameters (NIL,GET_DRIVER,(void *) k))
+ createProto = (*d->open) (NIL);
+ else { /* duh... */
+ sprintf (tmpx,"Unknown new mailbox format in %s: %s",
+ file ? file : SYSCONFIG,k);
+ MM_LOG (tmpx,WARN);
+ }
+ }
+ }
+ if (!compare_cstring (s,"set empty-mailbox-format") ||
+ !compare_cstring (s,"set empty-folder-format")) {
+ if (!compare_cstring (k,"invalid")) appendProto = NIL;
+ else if (!compare_cstring (k,"same-as-inbox"))
+ appendProto = ((d = mail_valid (NIL,"INBOX",NIL)) &&
+ compare_cstring (d->name,"dummy")) ?
+ ((*d->open) (NIL)) : &EMPTYPROTO;
+ else if (!compare_cstring (k,"system-standard"))
+ appendProto = &EMPTYPROTO;
+ else { /* see if a driver name */
+ for (d = (DRIVER *) mail_parameters (NIL,GET_DRIVERS,NIL);
+ d && compare_cstring (d->name,k); d = d->next);
+ if (d) appendProto = (*d->open) (NIL);
+ else { /* duh... */
+ sprintf (tmpx,"Unknown empty mailbox format in %s: %s",
+ file ? file : SYSCONFIG,k);
+ MM_LOG (tmpx,WARN);
+ }
+ }
+ }
+ }
+
+ if (!compare_cstring (s,"set local-host")) {
+ fs_give ((void **) &myLocalHost);
+ myLocalHost = cpystr (k);
+ }
+ else if (!compare_cstring (s,"set news-active-file")) {
+ fs_give ((void **) &newsActive);
+ newsActive = cpystr (k);
+ }
+ else if (!compare_cstring (s,"set news-spool-directory")) {
+ fs_give ((void **) &newsSpool);
+ newsSpool = cpystr (k);
+ }
+ else if (!compare_cstring (s,"set mh-path"))
+ mail_parameters (NIL,SET_MHPATH,(void *) k);
+ else if (!compare_cstring (s,"set mh-allow-inbox"))
+ mail_parameters (NIL,SET_MHALLOWINBOX,(void *) atol (k));
+ else if (!compare_cstring (s,"set news-state-file")) {
+ fs_give ((void **) &myNewsrc);
+ myNewsrc = cpystr (k);
+ }
+ else if (!compare_cstring (s,"set ftp-export-directory")) {
+ fs_give ((void **) &ftpHome);
+ ftpHome = cpystr (k);
+ }
+ else if (!compare_cstring (s,"set public-home-directory")) {
+ fs_give ((void **) &publicHome);
+ publicHome = cpystr (k);
+ }
+ else if (!compare_cstring (s,"set shared-home-directory")) {
+ fs_give ((void **) &sharedHome);
+ sharedHome = cpystr (k);
+ }
+ else if (!compare_cstring (s,"set system-inbox")) {
+ fs_give ((void **) &sysInbox);
+ sysInbox = cpystr (k);
+ }
+ else if (!compare_cstring (s,"set mail-subdirectory")) {
+ fs_give ((void **) &mailsubdir);
+ mailsubdir = cpystr (k);
+ }
+ else if (!compare_cstring (s,"set from-widget"))
+ mail_parameters (NIL,SET_FROMWIDGET,
+ compare_cstring (k,"header-only") ?
+ VOIDT : NIL);
+
+ else if (!compare_cstring (s,"set rsh-command"))
+ mail_parameters (NIL,SET_RSHCOMMAND,(void *) k);
+ else if (!compare_cstring (s,"set rsh-path"))
+ mail_parameters (NIL,SET_RSHPATH,(void *) k);
+ else if (!compare_cstring (s,"set ssh-command"))
+ mail_parameters (NIL,SET_SSHCOMMAND,(void *) k);
+ else if (!compare_cstring (s,"set ssh-path"))
+ mail_parameters (NIL,SET_SSHPATH,(void *) k);
+ else if (!compare_cstring (s,"set tcp-open-timeout"))
+ mail_parameters (NIL,SET_OPENTIMEOUT,(void *) atol (k));
+ else if (!compare_cstring (s,"set tcp-read-timeout"))
+ mail_parameters (NIL,SET_READTIMEOUT,(void *) atol (k));
+ else if (!compare_cstring (s,"set tcp-write-timeout"))
+ mail_parameters (NIL,SET_WRITETIMEOUT,(void *) atol (k));
+ else if (!compare_cstring (s,"set rsh-timeout"))
+ mail_parameters (NIL,SET_RSHTIMEOUT,(void *) atol (k));
+ else if (!compare_cstring (s,"set ssh-timeout"))
+ mail_parameters (NIL,SET_SSHTIMEOUT,(void *) atol (k));
+ else if (!compare_cstring (s,"set maximum-login-trials"))
+ mail_parameters (NIL,SET_MAXLOGINTRIALS,(void *) atol (k));
+ else if (!compare_cstring (s,"set lookahead"))
+ mail_parameters (NIL,SET_LOOKAHEAD,(void *) atol (k));
+ else if (!compare_cstring (s,"set prefetch"))
+ mail_parameters (NIL,SET_PREFETCH,(void *) atol (k));
+ else if (!compare_cstring (s,"set close-on-error"))
+ mail_parameters (NIL,SET_CLOSEONERROR,(void *) atol (k));
+ else if (!compare_cstring (s,"set imap-port"))
+ mail_parameters (NIL,SET_IMAPPORT,(void *) atol (k));
+ else if (!compare_cstring (s,"set pop3-port"))
+ mail_parameters (NIL,SET_POP3PORT,(void *) atol (k));
+ else if (!compare_cstring (s,"set uid-lookahead"))
+ mail_parameters (NIL,SET_UIDLOOKAHEAD,(void *) atol (k));
+ else if (!compare_cstring (s,"set try-ssl-first"))
+ mail_parameters (NIL,SET_TRYSSLFIRST,(void *) atol (k));
+
+ else if (!compare_cstring (s,"set mailbox-protection"))
+ mbx_protection = atol (k);
+ else if (!compare_cstring (s,"set directory-protection"))
+ dir_protection = atol (k);
+ else if (!compare_cstring (s,"set lock-protection"))
+ dotlock_mode = atol (k);
+ else if (!compare_cstring (s,"set ftp-protection"))
+ ftp_protection = atol (k);
+ else if (!compare_cstring (s,"set public-protection"))
+ public_protection = atol (k);
+ else if (!compare_cstring (s,"set shared-protection"))
+ shared_protection = atol (k);
+ else if (!compare_cstring (s,"set ftp-directory-protection"))
+ ftp_dir_protection = atol (k);
+ else if (!compare_cstring (s,"set public-directory-protection"))
+ public_dir_protection = atol (k);
+ else if (!compare_cstring (s,"set shared-directory-protection"))
+ shared_dir_protection = atol (k);
+ else if (!compare_cstring (s,"set dot-lock-file-timeout"))
+ locktimeout = atoi (k);
+ else if (!compare_cstring (s,"set disable-fcntl-locking"))
+ fcntlhangbug = atoi (k);
+ else if (!compare_cstring (s,"set disable-lock-warning"))
+ disableLockWarning = atoi (k);
+ else if (!compare_cstring (s,"set disable-unix-UIDs-and-keywords"))
+ has_no_life = atoi (k);
+ else if (!compare_cstring (s,"set hide-dot-files"))
+ hideDotFiles = atoi (k);
+ else if (!compare_cstring (s,"set list-maximum-level"))
+ list_max_level = atol (k);
+ else if (!compare_cstring (s,"set trust-dns"))
+ mail_parameters (NIL,SET_TRUSTDNS,(void *) atol (k));
+ else if (!compare_cstring (s,"set sasl-uses-ptr-name"))
+ mail_parameters (NIL,SET_SASLUSESPTRNAME,(void *) atol (k));
+ else if (!compare_cstring (s,"set network-filesystem-stat-bug"))
+ netfsstatbug = atoi (k);
+ else if (!compare_cstring (s,"set nntp-range"))
+ mail_parameters (NIL,SET_NNTPRANGE,(void *) atol (k));
+
+ else if (!file) { /* only allowed in system init */
+ if (!compare_cstring (s,"set black-box-directory") &&
+ !blackBoxDir) blackBoxDir = cpystr (k);
+ else if (!compare_cstring(s,"set black-box-default-home-directory")&&
+ blackBoxDir && !blackBoxDefaultHome)
+ blackBoxDefaultHome = cpystr (k);
+ else if (!compare_cstring (s,"set anonymous-home-directory") &&
+ !anonymousHome) anonymousHome = cpystr (k);
+ /* It's tempting to allow setting the CA path
+ * in a user init. However, that opens up a
+ * vector of attack big enough to drive a
+ * truck through... Resist the temptation.
+ */
+ else if (!compare_cstring (s,"set CA-certificate-path"))
+ sslCApath = cpystr (k);
+ else if (!compare_cstring (s,"set disable-plaintext"))
+ disablePlaintext = atoi (k);
+ else if (!compare_cstring (s,"set allowed-login-attempts"))
+ logtry = atoi (k);
+ else if (!compare_cstring (s,"set chroot-server"))
+ closedBox = atoi (k);
+ else if (!compare_cstring (s,"set restrict-mailbox-access"))
+ for (k = strtok_r (k,", ",&r); k; k = strtok_r (NIL,", ",&r)) {
+ if (!compare_cstring (k,"root")) restrictBox |= RESTRICTROOT;
+ else if (!compare_cstring (k,"otherusers"))
+ restrictBox |= RESTRICTOTHERUSER;
+ else if (!compare_cstring (k,"all")) restrictBox = -1;
+ }
+ else if (!compare_cstring (s,"set advertise-the-world"))
+ advertisetheworld = atoi (k);
+ else if (!compare_cstring (s,"set limited-advertise"))
+ limitedadvertise = atoi (k);
+ else if (!compare_cstring
+ (s,"set disable-automatic-shared-namespaces"))
+ noautomaticsharedns = atoi (k);
+ else if (!compare_cstring (s,"set allow-user-config"))
+ allowuserconfig = atoi (k);
+ else if (!compare_cstring (s,"set allow-reverse-dns"))
+ mail_parameters (NIL,SET_ALLOWREVERSEDNS,(void *) atol (k));
+ else if (!compare_cstring (s,"set k5-cp-uses-service-name"))
+ kerb_cp_svr_name = atoi (k);
+ /* must appear in file after any
+ * "set disable-plaintext" command! */
+ else if (!compare_cstring (s,"set plaintext-allowed-clients")) {
+ for (k = strtok_r (k,", ",&r); k && !tcp_isclienthost (k);
+ k = strtok_r (NIL,", ",&r));
+ if (k) disablePlaintext = 0;
+ }
+ }
+ }
+ }
+ } while ((s = fgets (tmp,MAILTMPLEN,f)) && (t = strchr (s,'\n')));
+ if (f) fclose (f); /* flush the file */
+}
+
+/* INBOX create function for tmail/dmail use only
+ * Accepts: mail stream
+ * path name buffer, preloaded with driver-dependent path
+ * Returns: T on success, NIL on failure
+ *
+ * This routine is evil and a truly incredible kludge. It is private for
+ * tmail/dmail and is not supported for any other application.
+ */
+
+long path_create (MAILSTREAM *stream,char *path)
+{
+ long ret;
+ short rsave = restrictBox;
+ restrictBox = NIL; /* can't restrict */
+ if (blackBox) { /* if black box */
+ /* toss out driver dependent names */
+ sprintf (path,"%s/INBOX",mymailboxdir ());
+ blackBox = NIL; /* well that's evil - evil is going on */
+ ret = mail_create (stream,path);
+ blackBox = T; /* restore the box */
+ }
+ /* easy thing otherwise */
+ else ret = mail_create (stream,path);
+ restrictBox = rsave; /* restore restrictions */
+ return ret;
+}
+
+/* Default block notify routine
+ * Accepts: reason for calling
+ * data
+ * Returns: data
+ */
+
+void *mm_blocknotify (int reason,void *data)
+{
+ void *ret = data;
+ switch (reason) {
+ case BLOCK_SENSITIVE: /* entering sensitive code */
+ ret = (void *) (unsigned long) alarm (0);
+ break;
+ case BLOCK_NONSENSITIVE: /* exiting sensitive code */
+ if ((unsigned long) data) alarm ((unsigned long) data);
+ break;
+ default: /* ignore all other reasons */
+ break;
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/unix/env_unix.h b/imap/src/osdep/unix/env_unix.h
new file mode 100644
index 00000000..365e5128
--- /dev/null
+++ b/imap/src/osdep/unix/env_unix.h
@@ -0,0 +1,95 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX environment routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+typedef struct dotlock_base {
+ char lock[MAILTMPLEN];
+ int pipei;
+ int pipeo;
+} DOTLOCK;
+
+
+/* Bits that can be set in restrictBox */
+
+#define RESTRICTROOT 0x1 /* restricted box doesn't allow root */
+#define RESTRICTOTHERUSER 0x2 /* restricted box doesn't allow other user */
+
+/* Subscription definitions for UNIX */
+
+#define SUBSCRIPTIONFILE(t) sprintf (t,"%s/.mailboxlist",myhomedir ())
+#define SUBSCRIPTIONTEMP(t) sprintf (t,"%s/.mlbxlsttmp",myhomedir ())
+
+
+/* dorc() options */
+
+#define SYSCONFIG "/etc/c-client.cf"
+
+
+/* Special users */
+
+#define ANONYMOUSUSER "nobody" /* anonymous user */
+#define UNLOGGEDUSER "root" /* unlogged-in user */
+#define ADMINGROUP "mailadm" /* mail administrator group */
+
+/* Function prototypes */
+
+#include "env.h"
+
+void rfc822_fixed_date (char *date);
+long env_init (char *user,char *home);
+char *myusername_full (unsigned long *flags);
+#define MU_LOGGEDIN 0
+#define MU_NOTLOGGEDIN 1
+#define MU_ANONYMOUS 2
+#define myusername() \
+ myusername_full (NIL)
+char *sysinbox ();
+char *mailboxdir (char *dst,char *dir,char *name);
+long dotlock_lock (char *file,DOTLOCK *base,int fd);
+long dotlock_unlock (DOTLOCK *base);
+int lockname (char *lock,char *fname,int op,long *pid);
+int lockfd (int fd,char *lock,int op);
+int lock_work (char *lock,void *sbuf,int op,long *pid);
+long chk_notsymlink (char *name,void *sbuf);
+void unlockfd (int fd,char *lock);
+long set_mbx_protections (char *mailbox,char *path);
+long get_dir_protection (char *mailbox);
+MAILSTREAM *user_flags (MAILSTREAM *stream);
+char *default_user_flag (unsigned long i);
+void dorc (char *file,long flag);
+long path_create (MAILSTREAM *stream,char *mailbox);
+void grim_pid_reap_status (int pid,int killreq,void *status);
+#define grim_pid_reap(pid,killreq) \
+ grim_pid_reap_status (pid,killreq,NIL)
+long safe_write (int fd,char *buf,long nbytes);
+void *arm_signal (int sig,void *action);
+struct passwd *checkpw (struct passwd *pw,char *pass,int argc,char *argv[]);
+long loginpw (struct passwd *pw,int argc,char *argv[]);
+long pw_login (struct passwd *pw,char *auser,char *user,char *home,int argc,
+ char *argv[]);
+void *mm_blocknotify (int reason,void *data);
diff --git a/imap/src/osdep/unix/fdstring.c b/imap/src/osdep/unix/fdstring.c
new file mode 100644
index 00000000..7a491f7d
--- /dev/null
+++ b/imap/src/osdep/unix/fdstring.c
@@ -0,0 +1,99 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: File descriptor string routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 April 1997
+ * Last Edited: 4 April 2007
+ */
+
+#include "mail.h"
+#include "osdep.h"
+#include "misc.h"
+#include "fdstring.h"
+
+/* String driver for fd stringstructs */
+
+static void fd_string_init (STRING *s,void *data,unsigned long size);
+static char fd_string_next (STRING *s);
+static void fd_string_setpos (STRING *s,unsigned long i);
+
+STRINGDRIVER fd_string = {
+ fd_string_init, /* initialize string structure */
+ fd_string_next, /* get next byte in string structure */
+ fd_string_setpos /* set position in string structure */
+};
+
+
+/* Initialize string structure for fd stringstruct
+ * Accepts: string structure
+ * pointer to string
+ * size of string
+ */
+
+static void fd_string_init (STRING *s,void *data,unsigned long size)
+{
+ FDDATA *d = (FDDATA *) data;
+ /* note fd */
+ s->data = (void *) (unsigned long) d->fd;
+ s->data1 = d->pos; /* note file offset */
+ s->size = size; /* note size */
+ s->curpos = s->chunk = d->chunk;
+ s->chunksize = (unsigned long) d->chunksize;
+ s->offset = 0; /* initial position */
+ /* and size of data */
+ s->cursize = min (s->chunksize,size);
+ /* move to that position in the file */
+ lseek (d->fd,d->pos,L_SET);
+ read (d->fd,s->chunk,(size_t) s->cursize);
+}
+
+/* Get next character from fd stringstruct
+ * Accepts: string structure
+ * Returns: character, string structure chunk refreshed
+ */
+
+static char fd_string_next (STRING *s)
+{
+ char c = *s->curpos++; /* get next byte */
+ SETPOS (s,GETPOS (s)); /* move to next chunk */
+ return c; /* return the byte */
+}
+
+
+/* Set string pointer position for fd stringstruct
+ * Accepts: string structure
+ * new position
+ */
+
+static void fd_string_setpos (STRING *s,unsigned long i)
+{
+ if (i > s->size) i = s->size; /* don't permit setting beyond EOF */
+ s->offset = i; /* set new offset */
+ s->curpos = s->chunk; /* reset position */
+ /* set size of data */
+ if (s->cursize = min (s->chunksize,SIZE (s))) {
+ /* move to that position in the file */
+ lseek ((long) s->data,s->data1 + s->offset,L_SET);
+ read ((long) s->data,s->curpos,(size_t) s->cursize);
+ }
+}
diff --git a/imap/src/osdep/unix/fdstring.h b/imap/src/osdep/unix/fdstring.h
new file mode 100644
index 00000000..d0a021bf
--- /dev/null
+++ b/imap/src/osdep/unix/fdstring.h
@@ -0,0 +1,39 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: File descriptor string routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 April 1997
+ * Last Edited: 30 August 2006
+ */
+
+/* Driver-dependent data passed to init method */
+
+typedef struct fd_data {
+ int fd; /* file descriptor */
+ unsigned long pos; /* initial position */
+ char *chunk; /* I/O buffer chunk */
+ unsigned long chunksize; /* I/O buffer chunk length */
+} FDDATA;
+
+
+extern STRINGDRIVER fd_string;
diff --git a/imap/src/osdep/unix/flockcyg.c b/imap/src/osdep/unix/flockcyg.c
new file mode 100644
index 00000000..46281829
--- /dev/null
+++ b/imap/src/osdep/unix/flockcyg.c
@@ -0,0 +1,92 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: flock emulation via fcntl() locking
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 April 2001
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Cygwin does not seem to have the design flaw in fcntl() locking that
+ * most other systems do (see flocksim.c for details). If some cretin
+ * decides to implement that design flaw, then Cygwin will have to use
+ * flocksim. Also, we don't test NFS either.
+ *
+ * However, Cygwin does have the Windows misfeature (introduced in NT 4.0)
+ * that you can not write to any segment which has a shared lock, and you
+ * can't lock a zero-byte segment either. This screws up the shared-write
+ * mailbox drivers (mbx, mtx, mx, and tenex). As a workaround, we'll only
+ * lock the first byte of the file, meaning that you can't write that byte
+ * shared. It's been suggested to lock the maximum off_t type, but that
+ * risks having a future version of Windows (or Cygwin) deciding that this
+ * also means "no lock".
+ */
+
+#undef flock /* name is used as a struct for fcntl */
+
+/* Emulator for flock() call
+ * Accepts: file descriptor
+ * operation bitmask
+ * Returns: 0 if successful, -1 if failure under BSD conditions
+ */
+
+int flocksim (int fd,int op)
+{
+ char tmp[MAILTMPLEN];
+ int logged = 0;
+ struct flock fl;
+ /* lock one bytes at byte 0 */
+ fl.l_whence = SEEK_SET; fl.l_start = 0; fl.l_len = 1;
+ fl.l_pid = getpid (); /* shouldn't be necessary */
+ switch (op & ~LOCK_NB) { /* translate to fcntl() operation */
+ case LOCK_EX: /* exclusive */
+ fl.l_type = F_WRLCK;
+ break;
+ case LOCK_SH: /* shared */
+ fl.l_type = F_RDLCK;
+ break;
+ case LOCK_UN: /* unlock */
+ fl.l_type = F_UNLCK;
+ break;
+ default: /* default */
+ errno = EINVAL;
+ return -1;
+ }
+ while (fcntl (fd,(op & LOCK_NB) ? F_SETLK : F_SETLKW,&fl))
+ if (errno != EINTR) {
+ /* Can't use switch here because these error codes may resolve to the
+ * same value on some systems.
+ */
+ if ((errno != EWOULDBLOCK) && (errno != EAGAIN) && (errno != EACCES)) {
+ sprintf (tmp,"Unexpected file locking failure: %s",strerror (errno));
+ /* give the user a warning of what happened */
+ MM_NOTIFY (NIL,tmp,WARN);
+ if (!logged++) syslog (LOG_ERR,"%s",tmp);
+ if (op & LOCK_NB) return -1;
+ sleep (5); /* slow things down for loops */
+ }
+ /* return failure for non-blocking lock */
+ else if (op & LOCK_NB) return -1;
+ }
+ return 0; /* success */
+}
diff --git a/imap/src/osdep/unix/flockcyg.h b/imap/src/osdep/unix/flockcyg.h
new file mode 100644
index 00000000..0bc53623
--- /dev/null
+++ b/imap/src/osdep/unix/flockcyg.h
@@ -0,0 +1,50 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: flock() emulation via fcntl() locking
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 April 2001
+ * Last Edited: 30 August 2006
+ */
+
+/* Cygwin does not seem to have the design flaw in fcntl() locking that
+ * most other systems do (see flocksim.c for details). If some cretin
+ * decides to implement that design flaw, then Cygwin will have to use
+ * flocksim. Also, we don't test NFS either
+ */
+
+
+#define flock flocksim /* use ours instead of theirs */
+
+#undef LOCK_SH
+#define LOCK_SH 1 /* shared lock */
+#undef LOCK_EX
+#define LOCK_EX 2 /* exclusive lock */
+#undef LOCK_NB
+#define LOCK_NB 4 /* no blocking */
+#undef LOCK_UN
+#define LOCK_UN 8 /* unlock */
+
+
+/* Function prototypes */
+
+int flocksim (int fd,int operation);
diff --git a/imap/src/osdep/unix/flocklnx.c b/imap/src/osdep/unix/flocklnx.c
new file mode 100644
index 00000000..ca0112ac
--- /dev/null
+++ b/imap/src/osdep/unix/flocklnx.c
@@ -0,0 +1,76 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Safe File Lock for Linux
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 20 April 2005
+ * Last Edited: 30 August 2006
+ */
+
+#undef flock
+
+#include <sys/vfs.h>
+#ifndef NFS_SUPER_MAGIC
+#define NFS_SUPER_MAGIC 0x6969
+#endif
+
+int safe_flock (int fd,int op)
+{
+ struct statfs sfbuf;
+ char tmp[MAILTMPLEN];
+ int e;
+ int logged = 0;
+ /* Check for NFS because Linux 2.6 broke flock() on NFS. Instead of being
+ * a no-op, flock() on NFS now returns ENOLCK. Read
+ * https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=123415
+ * for the gruesome details.
+ */
+ /* check filesystem type */
+ while ((e = fstatfs (fd,&sfbuf)) && (errno == EINTR));
+ if (!e) switch (sfbuf.f_type) {
+ case NFS_SUPER_MAGIC: /* always a fast no-op on NFS */
+ break;
+ default: /* allow on other filesystem types */
+ /* do the lock */
+ while (flock (fd,op)) switch (errno) {
+ case EINTR: /* interrupt */
+ break;
+ case ENOLCK: /* lock table is full */
+ sprintf (tmp,"File locking failure: %s",strerror (errno));
+ mm_log (tmp,WARN); /* give the user a warning of what happened */
+ if (!logged++) syslog (LOG_ERR,tmp);
+ /* return failure if non-blocking lock */
+ if (op & LOCK_NB) return -1;
+ sleep (5); /* slow down in case it loops */
+ break;
+ case EWOULDBLOCK: /* file is locked, LOCK_NB should be set */
+ if (op & LOCK_NB) return -1;
+ case EBADF: /* not valid open file descriptor */
+ case EINVAL: /* invalid operator */
+ default: /* other error code? */
+ sprintf (tmp,"Unexpected file locking failure: %s",strerror (errno));
+ fatal (tmp);
+ }
+ break;
+ }
+ return 0; /* success */
+}
diff --git a/imap/src/osdep/unix/flocksim.c b/imap/src/osdep/unix/flocksim.c
new file mode 100644
index 00000000..82f07837
--- /dev/null
+++ b/imap/src/osdep/unix/flocksim.c
@@ -0,0 +1,920 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: flock emulation via fcntl() locking
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 April 2001
+ * Last Edited: 11 October 2007
+ */
+
+#undef flock /* name is used as a struct for fcntl */
+
+#ifndef NOFSTATVFS /* thank you, SUN. NOT! */
+# ifndef NOFSTATVFS64
+# ifndef _LARGEFILE64_SOURCE
+# define _LARGEFILE64_SOURCE
+# endif /* _LARGEFILE64_SOURCE */
+# endif /* NOFSTATVFFS64 */
+#include <sys/statvfs.h>
+#endif /* NOFSTATVFS */
+
+#ifndef NSIG /* don't know if this can happen */
+#define NSIG 32 /* a common maximum */
+#endif
+
+/* Emulator for flock() call
+ * Accepts: file descriptor
+ * operation bitmask
+ * Returns: 0 if successful, -1 if failure under BSD conditions
+ */
+
+int flocksim (int fd,int op)
+{
+ char tmp[MAILTMPLEN];
+ int logged = 0;
+ struct stat sbuf;
+ struct ustat usbuf;
+ struct flock fl;
+ /* lock zero bytes at byte 0 */
+ fl.l_whence = SEEK_SET; fl.l_start = fl.l_len = 0;
+ fl.l_pid = getpid (); /* shouldn't be necessary */
+ switch (op & ~LOCK_NB) { /* translate to fcntl() operation */
+ case LOCK_EX: /* exclusive */
+ fl.l_type = F_WRLCK;
+ break;
+ case LOCK_SH: /* shared */
+ fl.l_type = F_RDLCK;
+ break;
+ case LOCK_UN: /* unlock */
+ fl.l_type = F_UNLCK;
+ break;
+ default: /* default */
+ errno = EINVAL;
+ return -1;
+ }
+ /* always return success if disabled */
+ if (mail_parameters (NIL,GET_DISABLEFCNTLLOCK,NIL)) return 0;
+
+ /* Make fcntl() locking of NFS files be a no-op the way it is with flock()
+ * on BSD. This is because the rpc.statd/rpc.lockd daemons don't work very
+ * well and cause cluster-wide hangs if you exercise them at all. The
+ * result of this is that you lose the ability to detect shared mail_open()
+ * on NFS-mounted files. If you are wise, you'll use IMAP instead of NFS
+ * for mail files.
+ *
+ * Sun alleges that it doesn't matter, and that they have fixed all the
+ * rpc.statd/rpc.lockd bugs. As of October 2006, that is still false.
+ *
+ * We need three tests for three major historical variants in SVR4:
+ * 1) In NFSv2, ustat() would return -1 in f_tinode for NFS.
+ * 2) When fstatvfs() was introduced with NFSv3, ustat() was "fixed".
+ * 3) When 64-bit filesystems were introduced, fstatvfs() would return
+ * EOVERFLOW; you have to use fstatvfs64() even though you don't care
+ * about any of the affected values.
+ *
+ * We can't use fstatfs() because fstatfs():
+ * . is documented as being deprecated in SVR4.
+ * . has inconsistent calling conventions (there are two additional int
+ * arguments on Solaris and I don't know what they do).
+ * . returns inconsistent statfs structs. On Solaris, the file system type
+ * is a short called f_fstyp. On AIX, it's an int called f_type that is
+ * documented as always being 0!
+ *
+ * For what it's worth, here's the scoop on fstatfs() elsewhere:
+ *
+ * On Linux, the file system type is a long called f_type that has a file
+ * system type code. A different module (flocklnx.c) uses this because
+ * some knothead "improved" flock() to return ENOLCK on NFS files instead
+ * of being a successful no-op. This "improvement" apparently has been
+ * reverted, but not before it got to many systems in the field.
+ *
+ * On BSD, it's a short called either f_otype or f_type that is documented
+ * as always being zero. Fortunately, BSD has flock() the way it's supposed
+ * to be, and none of this nonsense is necessary.
+ */
+ if (!fstat (fd,&sbuf)) { /* no hope of working if can't fstat()! */
+ /* Any base type that begins with "nfs" or "afs" is considered to be a
+ * network filesystem.
+ */
+#ifndef NOFSTATVFS
+ struct statvfs vsbuf;
+#ifndef NOFSTATVFS64
+ struct statvfs64 vsbuf64;
+ if (!fstatvfs64 (fd,&vsbuf64) && (vsbuf64.f_basetype[1] == 'f') &&
+ (vsbuf64.f_basetype[2] == 's') &&
+ ((vsbuf64.f_basetype[0] == 'n') || (vsbuf64.f_basetype[0] == 'a')))
+ return 0;
+#endif /* NOFSTATVFS64 */
+ if (!fstatvfs (fd,&vsbuf) && (vsbuf.f_basetype[1] == 'f') &&
+ (vsbuf.f_basetype[2] == 's') &&
+ ((vsbuf.f_basetype[0] == 'n') || (vsbuf.f_basetype[0] == 'a')))
+ return 0;
+#endif /* NOFSTATVFS */
+ if (!ustat (sbuf.st_dev,&usbuf) && !++usbuf.f_tinode) return 0;
+ }
+
+ /* do the lock */
+ while (fcntl (fd,(op & LOCK_NB) ? F_SETLK : F_SETLKW,&fl))
+ if (errno != EINTR) {
+ /* Can't use switch here because these error codes may resolve to the
+ * same value on some systems.
+ */
+ if ((errno != EWOULDBLOCK) && (errno != EAGAIN) && (errno != EACCES)) {
+ sprintf (tmp,"Unexpected file locking failure: %.100s",
+ strerror (errno));
+ /* give the user a warning of what happened */
+ MM_NOTIFY (NIL,tmp,WARN);
+ if (!logged++) syslog (LOG_ERR,"%s",tmp);
+ if (op & LOCK_NB) return -1;
+ sleep (5); /* slow things down for loops */
+ }
+ /* return failure for non-blocking lock */
+ else if (op & LOCK_NB) return -1;
+ }
+ return 0; /* success */
+}
+
+/* Master/slave procedures for safe fcntl() locking.
+ *
+ * The purpose of this nonsense is to work around a bad bug in fcntl()
+ * locking. The cretins who designed it decided that a close() should
+ * release any locks made by that process on the file opened on that
+ * file descriptor. Never mind that the lock wasn't made on that file
+ * descriptor, but rather on some other file descriptor.
+ *
+ * This bug is on every implementation of fcntl() locking that I have
+ * tested. Fortunately, on BSD systems, OSF/1, and Linux, we can use the
+ * flock() system call which doesn't have this bug.
+ *
+ * Note that OSF/1, Linux, and some BSD systems have both broken fcntl()
+ * locking and the working flock() locking.
+ *
+ * The program below can be used to demonstrate this problem. Be sure to
+ * let it run long enough for all the sleep() calls to finish.
+ */
+
+#if 0
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/file.h>
+
+main ()
+{
+ struct flock fl;
+ int fd,fd2;
+ char *file = "a.a";
+ if ((fd = creat (file,0666)) < 0)
+ perror ("TEST FAILED: can't create test file"),_exit (errno);
+ close (fd);
+ if (fork ()) { /* parent */
+ if ((fd = open (file,O_RDWR,0)) < 0) abort();
+ /* lock applies to entire file */
+ fl.l_whence = fl.l_start = fl.l_len = 0;
+ fl.l_pid = getpid (); /* shouldn't be necessary */
+ fl.l_type = F_RDLCK;
+ if (fcntl (fd,F_SETLKW,&fl) == -1) abort ();
+ sleep (5);
+ if ((fd2 = open (file,O_RDWR,0)) < 0) abort ();
+ sleep (1);
+ puts ("parent test ready -- will hang here if locking works correctly");
+ close (fd2);
+ wait (0);
+ puts ("OS BUG: child terminated");
+ _exit (0);
+ }
+ else { /* child */
+ sleep (2);
+ if ((fd = open (file,O_RDWR,0666)) < 0) abort ();
+ puts ("child test ready -- child will hang if no bug");
+ /* lock applies to entire file */
+ fl.l_whence = fl.l_start = fl.l_len = 0;
+ fl.l_pid = getpid (); /* shouldn't be necessary */
+ fl.l_type = F_WRLCK;
+ if (fcntl (fd,F_SETLKW,&fl) == -1) abort ();
+ puts ("OS BUG: child got lock");
+ }
+}
+#endif
+
+/* Beware of systems such as AIX which offer flock() as a compatibility
+ * function that is just a jacket into fcntl() locking. The program below
+ * is a variant of the program above, only using flock(). It can be used
+ * to test to see if your system has real flock() or just a jacket into
+ * fcntl().
+ *
+ * Be sure to let it run long enough for all the sleep() calls to finish.
+ * If the program hangs, then flock() works and you can dispense with the
+ * use of this module (you lucky person!).
+ */
+
+#if 0
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/file.h>
+
+main ()
+{
+ int fd,fd2;
+ char *file = "a.a";
+ if ((fd = creat (file,0666)) < 0)
+ perror ("TEST FAILED: can't create test file"),_exit (errno);
+ close (fd);
+ if (fork ()) { /* parent */
+ if ((fd = open (file,O_RDWR,0)) < 0) abort();
+ if (flock (fd,LOCK_SH) == -1) abort ();
+ sleep (5);
+ if ((fd2 = open (file,O_RDWR,0)) < 0) abort ();
+ sleep (1);
+ puts ("parent test ready -- will hang here if flock() works correctly");
+ close (fd2);
+ wait (0);
+ puts ("OS BUG: child terminated");
+ _exit (0);
+ }
+ else { /* child */
+ sleep (2);
+ if ((fd = open (file,O_RDWR,0666)) < 0) abort ();
+ puts ("child test ready -- child will hang if no bug");
+ if (flock (fd,LOCK_EX) == -1) abort ();
+ puts ("OS BUG: child got lock");
+ }
+}
+#endif
+
+/* Master/slave details
+ *
+ * On broken systems, we invoke an inferior fork to execute any driver
+ * dispatches which are likely to tickle this bug; specifically, any
+ * dispatch which may fiddle with a mailbox that is already selected. As
+ * of this writing, these are: delete, rename, status, scan, copy, and append.
+ *
+ * Delete and rename are pretty marginal, yet there are certain clients
+ * (e.g. Outlook Express) that really want to delete or rename the selected
+ * mailbox. The same is true of status, but there are people (such as the
+ * authors of Entourage) who don't understand why status of the selected
+ * mailbox is bad news.
+ *
+ * However, in copy and append it is reasonable to do this to a selected
+ * mailbox. Although scanning the selected mailbox isn't particularly
+ * sensible, it's hard to avoid due to wildcards.
+ *
+ * It is still possible for an application to trigger the bug by doing
+ * mail_open() on the same mailbox twice. Don't do it.
+ *
+ * Once the slave is invoked, the master only has to read events from the
+ * slave's output (see below for these events) and translate these events
+ * to the appropriate c-client callback. When end of file occurs on the pipe,
+ * the master reads the slave's exit status and uses that as the function
+ * return. The append master is slightly more complicated because it has to
+ * send data back to the slave (see below).
+ *
+ * The slave takes callback events from the driver which otherwise would
+ * pass to the main program. Only those events which a slave can actually
+ * encounter are covered here; for example mm_searched() and mm_list() are
+ * not covered since a slave never does the operations that trigger these.
+ * Certain other events (mm_exists(), mm_expunged(), mm_flags()) are discarded
+ * by the slave since the master will generate these events for itself.
+ *
+ * The other events cause the slave to write a newline-terminated string to
+ * its output. The first character of string indicates the event: S for
+ * mm_status(), N for mm_notify(), L for mm_log(), C for mm_critical(), X for
+ * mm_nocritical(), D for mm_diskerror(), F for mm_fatal(), and "A" for append
+ * argument callback. Most of these events also carry data, which carried as
+ * text space-delimited in the string.
+ *
+ * Append argument callback requires the master to provide the slave with
+ * data in the slave's input. The first thing that the master provides is
+ * either a "+" (master has data for the slave) or a "-" (master has no data).
+ * If the master has data, it will then send the flags, internal date, and
+ * message text, each as <text octet count><SPACE><text>.
+ */
+
+/* It should be alright for lockslavep to be a global, since it will always
+ * be zero in the master (which is where threads would be). The slave won't
+ * ever thread, since any driver which threads in its methods probably can't
+ * use fcntl() locking so won't have DR_LOCKING in its driver flags
+ *
+ * lockslavep can not be a static, since it's used by the dispatch macros.
+ */
+
+int lockslavep = 0; /* non-zero means slave process for locking */
+static int lockproxycopy = 0; /* non-zero means redo copy as proxy */
+FILE *slavein = NIL; /* slave input */
+FILE *slaveout = NIL; /* slave output */
+
+
+/* Common master
+ * Accepts: permitted stream
+ * append callback (append calls only, else NIL)
+ * data for callback (append calls only, else NIL)
+ * Returns: (master) T if slave succeeded, NIL if slave failed
+ * (slave) NIL always, with lockslavep non-NIL
+ */
+
+static long master (MAILSTREAM *stream,append_t af,void *data)
+{
+ MAILSTREAM *st;
+ MAILSTATUS status;
+ STRING *message;
+ FILE *pi,*po;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ long ret = NIL;
+ unsigned long i,j;
+ int c,pid,pipei[2],pipeo[2];
+ char *s,*t,event[MAILTMPLEN],tmp[MAILTMPLEN];
+ lockproxycopy = NIL; /* not doing a lock proxycopy */
+ /* make pipe from slave */
+ if (pipe (pipei) < 0) mm_log ("Can't create input pipe",ERROR);
+ else if (pipe (pipeo) < 0) {
+ mm_log ("Can't create output pipe",ERROR);
+ close (pipei[0]); close (pipei[1]);
+ }
+ else if ((pid = fork ()) < 0) {/* make slave */
+ mm_log ("Can't create execution process",ERROR);
+ close (pipei[0]); close (pipei[1]);
+ close (pipeo[0]); close (pipeo[1]);
+ }
+ else if (lockslavep = !pid) { /* are we slave or master? */
+ alarm (0); /* slave doesn't have alarms or signals */
+ for (c = 0; c < NSIG; c++) signal (c,SIG_DFL);
+ if (!(slavein = fdopen (pipeo[0],"r")) ||
+ !(slaveout = fdopen (pipei[1],"w")))
+ fatal ("Can't do slave pipe buffered I/O");
+ close (pipei[0]); /* close parent's side of the pipes */
+ close (pipeo[1]);
+ }
+
+ else { /* master process */
+ void *blockdata = (*bn) (BLOCK_SENSITIVE,NIL);
+ close (pipei[1]); /* close slave's side of the pipes */
+ close (pipeo[0]);
+ if (!(pi = fdopen (pipei[0],"r")) || !(po = fdopen (pipeo[1],"w")))
+ fatal ("Can't do master pipe buffered I/O");
+ /* do slave events until EOF */
+ /* read event */
+ while (fgets (event,MAILTMPLEN-1,pi)) {
+ if (!(s = strchr (event,'\n'))) {
+ sprintf (tmp,"Execution process event string too long: %.500s",event);
+ fatal (tmp);
+ }
+ *s = '\0'; /* tie off event at end of line */
+ switch (event[0]) { /* analyze event */
+ case 'A': /* append callback */
+ if ((*af) (NIL,data,&s,&t,&message)) {
+ if (i = message ? SIZE (message) : 0) {
+ if (!s) s = ""; /* default values */
+ if (!t) t = "";
+ }
+ else s = t = ""; /* no flags or date if no message */
+ errno = NIL; /* reset last error */
+ /* build response */
+ if (fprintf (po,"+%lu %s%lu %s%lu ",strlen (s),s,strlen (t),t,i) < 0)
+ fatal ("Failed to pipe append command");
+ /* write message text */
+ if (i) do if (putc (c = 0xff & SNX (message),po) == EOF) {
+ sprintf (tmp,"Failed to pipe %lu bytes (of %lu), last=%u: %.100s",
+ i,message->size,c,strerror (errno));
+ fatal (tmp);
+ } while (--i);
+ }
+ else putc ('-',po); /* append error */
+ fflush (po);
+ break;
+ case '&': /* slave wants a proxycopy? */
+ lockproxycopy = T;
+ break;
+
+ case 'L': /* mm_log() */
+ i = strtoul (event+1,&s,10);
+ if (!s || (*s++ != ' ')) {
+ sprintf (tmp,"Invalid log event arguments: %.500s",event);
+ fatal (tmp);
+ }
+ mm_log (s,i);
+ break;
+ case 'N': /* mm_notify() */
+ st = (MAILSTREAM *) strtoul (event+1,&s,16);
+ if (s && (*s++ == ' ')) {
+ i = strtoul (s,&s,10);/* get severity */
+ if (s && (*s++ == ' ')) {
+ mm_notify ((st == stream) ? stream : NIL,s,i);
+ break;
+ }
+ }
+ sprintf (tmp,"Invalid notify event arguments: %.500s",event);
+ fatal (tmp);
+
+ case 'S': /* mm_status() */
+ st = (MAILSTREAM *) strtoul (event+1,&s,16);
+ if (s && (*s++ == ' ')) {
+ status.flags = strtoul (s,&s,10);
+ if (s && (*s++ == ' ')) {
+ status.messages = strtoul (s,&s,10);
+ if (s && (*s++ == ' ')) {
+ status.recent = strtoul (s,&s,10);
+ if (s && (*s++ == ' ')) {
+ status.unseen = strtoul (s,&s,10);
+ if (s && (*s++ == ' ')) {
+ status.uidnext = strtoul (s,&s,10);
+ if (s && (*s++ == ' ')) {
+ status.uidvalidity = strtoul (s,&s,10);
+ if (s && (*s++ == ' ')) {
+ mm_status ((st == stream) ? stream : NIL,s,&status);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ sprintf (tmp,"Invalid status event arguments: %.500s",event);
+ fatal (tmp);
+ case 'C': /* mm_critical() */
+ st = (MAILSTREAM *) strtoul (event+1,&s,16);
+ mm_critical ((st == stream) ? stream : NIL);
+ break;
+ case 'X': /* mm_nocritical() */
+ st = (MAILSTREAM *) strtoul (event+1,&s,16);
+ mm_nocritical ((st == stream) ? stream : NIL);
+ break;
+
+ case 'D': /* mm_diskerror() */
+ st = (MAILSTREAM *) strtoul (event+1,&s,16);
+ if (s && (*s++ == ' ')) {
+ i = strtoul (s,&s,10);
+ if (s && (*s++ == ' ')) {
+ j = (long) strtoul (s,NIL,10);
+ if (st == stream) /* let's hope it's on usable stream */
+ putc (mm_diskerror (stream,(long) i,j) ? '+' : '-',po);
+ else if (j) { /* serious diskerror on slave-created stream */
+ mm_log ("Retrying disk write to avoid mailbox corruption!",WARN);
+ sleep (5); /* give some time for it to clear up */
+ putc ('-',po); /* don't abort */
+ }
+ else { /* recoverable on slave-created stream */
+ mm_log ("Error on disk write",ERROR);
+ putc ('+',po); /* so abort it */
+ }
+ fflush (po); /* force it out either way */
+ break;
+ }
+ }
+ sprintf (tmp,"Invalid diskerror event arguments: %.500s",event);
+ fatal (tmp);
+ case 'F': /* mm_fatal() */
+ mm_fatal (event+1);
+ break;
+ default: /* random lossage */
+ sprintf (tmp,"Unknown event from execution process: %.500s",event);
+ fatal (tmp);
+ }
+ }
+ fclose (pi); fclose (po); /* done with the pipes */
+ /* get slave status */
+ grim_pid_reap_status (pid,NIL,&ret);
+ if (ret & 0177) { /* signal or stopped */
+ sprintf (tmp,"Execution process terminated abnormally (%lx)",ret);
+ mm_log (tmp,ERROR);
+ ret = NIL;
+ }
+ else ret >>= 8; /* return exit code */
+ (*bn) (BLOCK_NONSENSITIVE,blockdata);
+ }
+ return ret; /* return status */
+}
+
+/* Safe driver calls */
+
+
+/* Safely delete mailbox
+ * Accepts: driver to call under slave
+ * MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long safe_delete (DRIVER *dtb,MAILSTREAM *stream,char *mbx)
+{
+ long ret = master (stream,NIL,NIL);
+ if (lockslavep) exit ((*dtb->mbxdel) (stream,mbx));
+ return ret;
+}
+
+
+/* Safely rename mailbox
+ * Accepts: driver to call under slave
+ * MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long safe_rename (DRIVER *dtb,MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = master (stream,NIL,NIL);
+ if (lockslavep) exit ((*dtb->mbxren) (stream,old,newname));
+ return ret;
+}
+
+
+/* Safely get status of mailbox
+ * Accepts: driver to call under slave
+ * MAIL stream
+ * mailbox name
+ * status flags
+ * Returns: T on success, NIL on failure
+ */
+
+long safe_status (DRIVER *dtb,MAILSTREAM *stream,char *mbx,long flags)
+{
+ long ret = master (stream,NIL,NIL);
+ if (lockslavep) exit ((*dtb->status) (stream,mbx,flags));
+ return ret;
+}
+
+
+/* Scan file for contents
+ * Accepts: driver to call under slave
+ * file name
+ * desired contents
+ * length of contents
+ * length of file
+ * Returns: NIL if contents not found, T if found
+ */
+
+long safe_scan_contents (DRIVER *dtb,char *name,char *contents,
+ unsigned long csiz,unsigned long fsiz)
+{
+ long ret = master (NIL,NIL,NIL);
+ if (lockslavep) exit (scan_contents (dtb,name,contents,csiz,fsiz));
+ return ret;
+}
+
+/* Safely copy message to mailbox
+ * Accepts: driver to call under slave
+ * MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if success, NIL if failed
+ */
+
+long safe_copy (DRIVER *dtb,MAILSTREAM *stream,char *seq,char *mbx,long flags)
+{
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ long ret = master (stream,NIL,NIL);
+ if (lockslavep) {
+ /* don't do proxycopy in slave */
+ if (pc) mail_parameters (stream,SET_MAILPROXYCOPY,(void *) slaveproxycopy);
+ exit ((*dtb->copy) (stream,seq,mbx,flags));
+ }
+ /* do any proxycopy in master */
+ if (lockproxycopy && pc) return (*pc) (stream,seq,mbx,flags);
+ return ret;
+}
+
+
+/* Append package for slave */
+
+typedef struct append_data {
+ int first; /* flag indicating first message */
+ char *flags; /* message flags */
+ char *date; /* message date */
+ char *msg; /* message text */
+ STRING message; /* message stringstruct */
+} APPENDDATA;
+
+
+/* Safely append message to mailbox
+ * Accepts: driver to call under slave
+ * MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long safe_append (DRIVER *dtb,MAILSTREAM *stream,char *mbx,append_t af,
+ void *data)
+{
+ long ret = master (stream,af,data);
+ if (lockslavep) {
+ APPENDDATA ad;
+ ad.first = T; /* initialize initial append package */
+ ad.flags = ad.date = ad.msg = NIL;
+ exit ((*dtb->append) (stream,mbx,slave_append,&ad));
+ }
+ return ret;
+}
+
+/* Slave callbacks */
+
+
+/* Message exists (i.e. there are that many messages in the mailbox)
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void slave_exists (MAILSTREAM *stream,unsigned long number)
+{
+ /* this event never passed by slaves */
+}
+
+
+/* Message expunged
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void slave_expunged (MAILSTREAM *stream,unsigned long number)
+{
+ /* this event never passed by slaves */
+}
+
+
+/* Message status changed
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void slave_flags (MAILSTREAM *stream,unsigned long number)
+{
+ /* this event never passed by slaves */
+}
+
+/* Mailbox status
+ * Accepts: MAIL stream
+ * mailbox name
+ * mailbox status
+ */
+
+void slave_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
+{
+ int i,c;
+ fprintf (slaveout,"S%lx %lu %lu %lu %lu %lu %lu ",
+ (unsigned long) stream,status->flags,status->messages,status->recent,
+ status->unseen,status->uidnext,status->uidvalidity,mailbox);
+ /* yow! are we paranoid enough yet? */
+ for (i = 0; (i < 500) && (c = *mailbox++); ++i) switch (c) {
+ case '\r': case '\n': /* newline in a mailbox name? */
+ c = ' ';
+ default:
+ putc (c,slaveout);
+ }
+ putc ('\n',slaveout);
+ fflush (slaveout);
+}
+
+/* Notification event
+ * Accepts: MAIL stream
+ * string to log
+ * error flag
+ */
+
+void slave_notify (MAILSTREAM *stream,char *string,long errflg)
+{
+ int i,c;
+ fprintf (slaveout,"N%lx %lu ",(unsigned long) stream,errflg);
+ /* prevent more than 500 bytes */
+ for (i = 0; (i < 500) && (c = *string++); ++i) switch (c) {
+ case '\r': case '\n': /* or embedded newline */
+ c = ' ';
+ default:
+ putc (c,slaveout);
+ }
+ putc ('\n',slaveout);
+ fflush (slaveout);
+}
+
+
+/* Log an event for the user to see
+ * Accepts: string to log
+ * error flag
+ */
+
+void slave_log (char *string,long errflg)
+{
+ int i,c;
+ fprintf (slaveout,"L%lu ",errflg);
+ /* prevent more than 500 bytes */
+ for (i = 0; (i < 500) && (c = *string++); ++i) switch (c) {
+ case '\r': case '\n': /* or embedded newline */
+ c = ' ';
+ default:
+ putc (c,slaveout);
+ }
+ putc ('\n',slaveout);
+ fflush (slaveout);
+}
+
+/* About to enter critical code
+ * Accepts: stream
+ */
+
+void slave_critical (MAILSTREAM *stream)
+{
+ fprintf (slaveout,"C%lx\n",(unsigned long) stream);
+ fflush (slaveout);
+}
+
+
+/* About to exit critical code
+ * Accepts: stream
+ */
+
+void slave_nocritical (MAILSTREAM *stream)
+{
+ fprintf (slaveout,"X%lx\n",(unsigned long) stream);
+ fflush (slaveout);
+}
+
+/* Disk error found
+ * Accepts: stream
+ * system error code
+ * flag indicating that mailbox may be clobbered
+ * Returns: abort flag
+ */
+
+long slave_diskerror (MAILSTREAM *stream,long errcode,long serious)
+{
+ char tmp[MAILTMPLEN];
+ int c;
+ long ret = NIL;
+ fprintf (slaveout,"D%lx %lu %lu\n",(unsigned long) stream,errcode,serious);
+ fflush (slaveout);
+ switch (c = getc (slavein)) {
+ case EOF: /* pipe broken */
+ slave_fatal ("Pipe broken reading diskerror response");
+ case '+': /* user wants to abort */
+ ret = LONGT;
+ case '-': /* no abort */
+ break;
+ default:
+ sprintf (tmp,"Unknown master response for diskerror: %c",c);
+ slave_fatal (tmp);
+ }
+ return ret;
+}
+
+
+/* Log a fatal error event
+ * Accepts: string to log
+ * Does not return
+ */
+
+void slave_fatal (char *string)
+{
+ int i,c;
+ syslog (LOG_ALERT,"IMAP toolkit slave process crash: %.500s",string);
+ putc ('F',slaveout);
+ /* prevent more than 500 bytes */
+ for (i = 0; (i < 500) && (c = *string++); ++i) switch (c) {
+ case '\r': case '\n': /* newline in a mailbox name? */
+ c = ' ';
+ default:
+ putc (c,slaveout);
+ }
+ putc ('\n',slaveout);
+ fflush (slaveout);
+ abort (); /* die */
+}
+
+/* Append read buffer
+ * Accepts: number of bytes to read
+ * error message if fails
+ * Returns: read-in string
+ */
+
+static char *slave_append_read (unsigned long n,char *error)
+{
+#if 0
+ unsigned long i;
+#endif
+ int c;
+ char *t,tmp[MAILTMPLEN];
+ char *s = (char *) fs_get (n + 1);
+ s[n] = '\0';
+#if 0
+ /* This doesn't work on Solaris with GCC. I think that it's a C library
+ * bug, since the problem only shows up if the application does fread()
+ * on some other file
+ */
+ for (t = s; n && ((i = fread (t,1,n,slavein)); t += i,n -= i);
+#else
+ for (t = s; n && ((c = getc (slavein)) != EOF); *t++ = c,--n);
+#endif
+ if (n) {
+ sprintf(tmp,"Pipe broken reading %.100s with %lu bytes remaining",error,n);
+ slave_fatal (tmp);
+ }
+ return s;
+}
+
+/* Append message callback
+ * Accepts: MAIL stream
+ * append data package
+ * pointer to return initial flags
+ * pointer to return message internal date
+ * pointer to return stringstruct of message or NIL to stop
+ * Returns: T if success (have message or stop), NIL if error
+ */
+
+long slave_append (MAILSTREAM *stream,void *data,char **flags,char **date,
+ STRING **message)
+{
+ char tmp[MAILTMPLEN];
+ unsigned long n;
+ int c;
+ APPENDDATA *ad = (APPENDDATA *) data;
+ /* flush text of previous message */
+ if (ad->flags) fs_give ((void **) &ad->flags);
+ if (ad->date) fs_give ((void **) &ad->date);
+ if (ad->msg) fs_give ((void **) &ad->msg);
+ *flags = *date = NIL; /* assume no flags or date */
+ fputs ("A\n",slaveout); /* tell master we're doing append callback */
+ fflush (slaveout);
+ switch (c = getc (slavein)) { /* what did master say? */
+ case '+': /* have message, get size of flags */
+ for (n = 0; isdigit (c = getc (slavein)); n *= 10, n += (c - '0'));
+ if (c != ' ') {
+ if (c == EOF) sprintf (tmp,"Pipe broken after flag size %lu",n);
+ sprintf (tmp,"Missing delimiter after flag size %lu: %c",n,c);
+ slave_fatal (tmp);
+ }
+ if (n) *flags = ad->flags = slave_append_read (n,"flags");
+ /* get size of date */
+ for (n = 0; isdigit (c = getc (slavein)); n *= 10, n += (c - '0'));
+ if (c != ' ') {
+ if (c == EOF) sprintf (tmp,"Pipe broken after date size %lu",n);
+ else sprintf (tmp,"Missing delimiter after date size %lu: %c",n,c);
+ slave_fatal (tmp);
+ }
+ if (n) *date = ad->date = slave_append_read (n,"date");
+ /* get size of message */
+ for (n = 0; isdigit (c = getc (slavein)); n *= 10, n += (c - '0'));
+ if (c != ' ') {
+ if (c == EOF) sprintf (tmp,"Pipe broken after message size %lu",n);
+ sprintf (tmp,"Missing delimiter after message size %lu: %c",n,c);
+ slave_fatal (tmp);
+ }
+ if (n) { /* make buffer for message */
+ ad->msg = slave_append_read (n,"message");
+ /* initialize stringstruct */
+ INIT (&ad->message,mail_string,(void *) ad->msg,n);
+ ad->first = NIL; /* no longer first message */
+ *message = &ad->message; /* return message */
+ }
+ else *message = NIL; /* empty message */
+ return LONGT;
+ case '-': /* error */
+ *message = NIL; /* set stop */
+ break;
+ case EOF: /* end of file */
+ slave_fatal ("Pipe broken reading append response");
+ default: /* unknown event */
+ sprintf (tmp,"Unknown master response for append: %c",c);
+ slave_fatal (tmp);
+ }
+ return NIL; /* return failure */
+}
+
+/* Proxy copy across mailbox formats
+ * Accepts: mail stream
+ * sequence to copy on this stream
+ * destination mailbox
+ * option flags
+ * Returns: T if success, else NIL
+ */
+
+long slaveproxycopy (MAILSTREAM *stream,char *sequence,char *mailbox,
+ long options)
+{
+ fputs ("&\n",slaveout); /* redo copy as append */
+ fflush (slaveout);
+ return NIL; /* failure for now */
+}
diff --git a/imap/src/osdep/unix/flocksim.h b/imap/src/osdep/unix/flocksim.h
new file mode 100644
index 00000000..719eba46
--- /dev/null
+++ b/imap/src/osdep/unix/flocksim.h
@@ -0,0 +1,117 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: flock() emulation via fcntl() locking
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 April 2001
+ * Last Edited: 20 December 2006
+ */
+
+
+#include "dummy.h" /* get scan_contents() prototype */
+
+#define flock flocksim /* use ours instead of theirs */
+
+#undef LOCK_SH
+#define LOCK_SH 1 /* shared lock */
+#undef LOCK_EX
+#define LOCK_EX 2 /* exclusive lock */
+#undef LOCK_NB
+#define LOCK_NB 4 /* no blocking */
+#undef LOCK_UN
+#define LOCK_UN 8 /* unlock */
+
+/* Safe locking definitions */
+
+extern int lockslavep; /* non-zero means slave process */
+
+#undef SAFE_DELETE
+#define SAFE_DELETE(dtb,stream,mbx) (dtb->flags & DR_LOCKING) ? \
+ safe_delete (dtb,stream,mbx) : (*dtb->mbxdel) (stream,mbx)
+#undef SAFE_RENAME
+#define SAFE_RENAME(dtb,stream,old,newname) (dtb->flags & DR_LOCKING) ? \
+ safe_rename (dtb,stream,old,newname) : (*dtb->mbxren) (stream,old,newname)
+#undef SAFE_STATUS
+#define SAFE_STATUS(dtb,stream,mbx,bits) (dtb->flags & DR_LOCKING) ? \
+ safe_status (dtb,stream,mbx,bits) : (*dtb->status) (stream,mbx,bits)
+#undef SAFE_SCAN_CONTENTS
+#define SAFE_SCAN_CONTENTS(dtb,name,contents,csiz,fsiz) \
+ (!dtb || (dtb->flags & DR_LOCKING)) ? \
+ safe_scan_contents (dtb,name,contents,csiz,fsiz) : \
+ scan_contents (dtb,name,contents,csiz,fsiz)
+#undef SAFE_COPY
+#define SAFE_COPY(dtb,stream,seq,mbx,bits) (dtb->flags & DR_LOCKING) ? \
+ safe_copy (dtb,stream,seq,mbx,bits) : (*dtb->copy) (stream,seq,mbx,bits)
+#undef SAFE_APPEND
+#define SAFE_APPEND(dtb,stream,mbx,af,data) (dtb->flags & DR_LOCKING) ? \
+ safe_append (dtb,stream,mbx,af,data) : (*dtb->append) (stream,mbx,af,data)
+
+#undef MM_EXISTS
+#define MM_EXISTS (lockslavep ? slave_exists : mm_exists)
+#undef MM_EXPUNGED
+#define MM_EXPUNGED (lockslavep ? slave_expunged : mm_expunged)
+#undef MM_FLAGS
+#define MM_FLAGS (lockslavep ? slave_flags : mm_flags)
+#undef MM_NOTIFY
+#define MM_NOTIFY (lockslavep ? slave_notify : mm_notify)
+#undef MM_STATUS
+#define MM_STATUS (lockslavep ? slave_status : mm_status)
+#undef MM_LOG
+#define MM_LOG (lockslavep ? slave_log : mm_log)
+#undef MM_CRITICAL
+#define MM_CRITICAL (lockslavep ? slave_critical : mm_critical)
+#undef MM_NOCRITICAL
+#define MM_NOCRITICAL (lockslavep ? slave_nocritical : mm_nocritical)
+#undef MM_DISKERROR
+#define MM_DISKERROR (lockslavep ? slave_diskerror : mm_diskerror)
+#undef MM_FATAL
+#define MM_FATAL (lockslavep ? slave_fatal : mm_fatal)
+#undef MM_APPEND
+#define MM_APPEND(af) (lockslavep ? slave_append : (*af))
+
+/* Function prototypes */
+
+int flocksim (int fd,int operation);
+
+long safe_delete (DRIVER *dtb,MAILSTREAM *stream,char *mbx);
+long safe_rename (DRIVER *dtb,MAILSTREAM *stream,char *old,char *newname);
+long safe_status (DRIVER *dtb,MAILSTREAM *stream,char *mbx,long flags);
+long safe_scan_contents (DRIVER *dtb,char *name,char *contents,
+ unsigned long csiz,unsigned long fsiz);
+long safe_copy (DRIVER *dtb,MAILSTREAM *stream,char *seq,char *mbx,long flags);
+long safe_append (DRIVER *dtb,MAILSTREAM *stream,char *mbx,append_t af,
+ void *data);
+
+void slave_exists (MAILSTREAM *stream,unsigned long number);
+void slave_expunged (MAILSTREAM *stream,unsigned long number);
+void slave_flags (MAILSTREAM *stream,unsigned long number);
+void slave_notify (MAILSTREAM *stream,char *string,long errflg);
+void slave_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status);
+void slave_log (char *string,long errflg);
+void slave_critical (MAILSTREAM *stream);
+void slave_nocritical (MAILSTREAM *stream);
+long slave_diskerror (MAILSTREAM *stream,long errcode,long serious);
+void slave_fatal (char *string);
+long slave_append (MAILSTREAM *stream,void *data,char **flags,char **date,
+ STRING **message);
+long slaveproxycopy (MAILSTREAM *stream,char *sequence,char *mailbox,
+ long options);
diff --git a/imap/src/osdep/unix/fs_unix.c b/imap/src/osdep/unix/fs_unix.c
new file mode 100644
index 00000000..b433e1b4
--- /dev/null
+++ b/imap/src/osdep/unix/fs_unix.c
@@ -0,0 +1,71 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Free storage management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Get a block of free storage
+ * Accepts: size of desired block
+ * Returns: free storage block
+ */
+
+void *fs_get (size_t size)
+{
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ void *data = (*bn) (BLOCK_SENSITIVE,NIL);
+ void *block = malloc (size ? size : (size_t) 1);
+ if (!block) fatal ("Out of memory");
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ return (block);
+}
+
+
+/* Resize a block of free storage
+ * Accepts: ** pointer to current block
+ * new size
+ */
+
+void fs_resize (void **block,size_t size)
+{
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ void *data = (*bn) (BLOCK_SENSITIVE,NIL);
+ if (!(*block = realloc (*block,size ? size : (size_t) 1)))
+ fatal ("Can't resize memory");
+ (*bn) (BLOCK_NONSENSITIVE,data);
+}
+
+
+/* Return a block of free storage
+ * Accepts: ** pointer to free storage block
+ */
+
+void fs_give (void **block)
+{
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ void *data = (*bn) (BLOCK_SENSITIVE,NIL);
+ free (*block);
+ *block = NIL;
+ (*bn) (BLOCK_NONSENSITIVE,data);
+}
diff --git a/imap/src/osdep/unix/fsync.c b/imap/src/osdep/unix/fsync.c
new file mode 100644
index 00000000..7a63b0f8
--- /dev/null
+++ b/imap/src/osdep/unix/fsync.c
@@ -0,0 +1,39 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: File sync emulator
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 May 1995
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Emulator for BSD fsync() call
+ * Accepts: file name
+ * Returns: 0 if successful, -1 if failure
+ */
+
+int fsync (int fd)
+{
+ sync ();
+ return 0;
+}
diff --git a/imap/src/osdep/unix/ftl_unix.c b/imap/src/osdep/unix/ftl_unix.c
new file mode 100644
index 00000000..e927360d
--- /dev/null
+++ b/imap/src/osdep/unix/ftl_unix.c
@@ -0,0 +1,39 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX crash management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Report a fatal error
+ * Accepts: string to output
+ */
+
+void fatal (char *string)
+{
+ MM_FATAL (string); /* pass up the string */
+ syslog (LOG_ALERT,"IMAP toolkit crash: %.100s",string);
+ abort (); /* die horribly */
+}
diff --git a/imap/src/osdep/unix/gethstid.c b/imap/src/osdep/unix/gethstid.c
new file mode 100644
index 00000000..01516976
--- /dev/null
+++ b/imap/src/osdep/unix/gethstid.c
@@ -0,0 +1,38 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Get host ID emulator
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 May 1995
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Emulator for BSD gethostid() call
+ * Returns: unique identifier for this machine
+ */
+
+long gethostid (void)
+{
+ /* No gethostid() here, so just fake it and hope things turn out okay. */
+ return 0xdeadface;
+}
diff --git a/imap/src/osdep/unix/getspnam.c b/imap/src/osdep/unix/getspnam.c
new file mode 100644
index 00000000..cc01daf8
--- /dev/null
+++ b/imap/src/osdep/unix/getspnam.c
@@ -0,0 +1,65 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: 64-bit getsockname()/getpeername() emulator
+ *
+ * Author: Mark Crispin from code contributed by Chris Ross
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 5 November 2004
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Jacket into getpeername()
+ * Accepts: socket
+ * pointer to socket address
+ * void pointer to len
+ * Returns: 0 if success, -1 if error
+ */
+
+int Getpeername (int s,struct sockaddr *name,size_t *namelen)
+{
+ int ret;
+ socklen_t len = (socklen_t) *namelen;
+ ret = getpeername (s,name,&len);
+ *namelen = (size_t) len;
+ return ret;
+}
+
+
+/* Jacket into getsockname()
+ * Accepts: socket
+ * pointer to socket address
+ * void pointer to len
+ * Returns: 0 if success, -1 if error
+ */
+
+int Getsockname (int s,struct sockaddr *name,size_t *namelen)
+{
+ int ret;
+ socklen_t len = (socklen_t) *namelen;
+ ret = getsockname (s,name,&len);
+ *namelen = (size_t) len;
+ return ret;
+}
+
+
+#define getpeername Getpeername
+#define getsockname Getsockname
diff --git a/imap/src/osdep/unix/gr_wait.c b/imap/src/osdep/unix/gr_wait.c
new file mode 100644
index 00000000..626c57e8
--- /dev/null
+++ b/imap/src/osdep/unix/gr_wait.c
@@ -0,0 +1,46 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX Grim PID Reaper -- wait() version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 30 November 1993
+ * Last Edited: 30 August 2006
+ */
+
+/* Grim PID reaper
+ * Accepts: process ID
+ * kill request flag
+ * status return value
+ */
+
+void grim_pid_reap_status (int pid,int killreq,void *status)
+{
+ int r;
+ if (killreq) {
+ kill (pid,SIGHUP); /* kill if not already dead */
+ alarm (10); /* in case we get hosed */
+ while (((r = wait (NIL)) != pid) &&
+ ((r > 0) || ((errno != ECHILD) && (errno != EINTR))));
+ alarm (0); /* cancel the alarm */
+ }
+ else while (((r = wait (status)) != pid) && ((r > 0) || (errno != ECHILD)));
+}
diff --git a/imap/src/osdep/unix/gr_wait4.c b/imap/src/osdep/unix/gr_wait4.c
new file mode 100644
index 00000000..f394b84c
--- /dev/null
+++ b/imap/src/osdep/unix/gr_wait4.c
@@ -0,0 +1,39 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX Grim PID Reaper -- wait4() version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 30 November 1993
+ * Last Edited: 30 August 2006
+ */
+
+/* Grim PID reaper
+ * Accepts: process ID
+ * kill request flag
+ * status return value
+ */
+
+void grim_pid_reap_status (int pid,int killreq,void *status)
+{
+ if (killreq) kill(pid,SIGHUP);/* kill if not already dead */
+ while ((wait4 (pid,status,NIL,NIL) < 0) && (errno != ECHILD));
+}
diff --git a/imap/src/osdep/unix/gr_waitp.c b/imap/src/osdep/unix/gr_waitp.c
new file mode 100644
index 00000000..8b230f9d
--- /dev/null
+++ b/imap/src/osdep/unix/gr_waitp.c
@@ -0,0 +1,39 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX Grim PID Reaper -- waitpid() version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 30 November 1993
+ * Last Edited: 30 August 2006
+ */
+
+/* Grim PID reaper
+ * Accepts: process ID
+ * kill request flag
+ * status return value
+ */
+
+void grim_pid_reap_status (int pid,int killreq,void *status)
+{
+ if (killreq) kill(pid,SIGHUP);/* kill if not already dead */
+ while ((waitpid (pid,status,NIL) < 0) && (errno != ECHILD));
+}
diff --git a/imap/src/osdep/unix/ip4_unix.c b/imap/src/osdep/unix/ip4_unix.c
new file mode 100644
index 00000000..23d399e1
--- /dev/null
+++ b/imap/src/osdep/unix/ip4_unix.c
@@ -0,0 +1,184 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX IPv4 routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 18 December 2003
+ * Last Edited: 30 August 2006
+ */
+
+#define SADRLEN sizeof (struct sockaddr)
+
+#define SADR4(sadr) ((struct sockaddr_in *) sadr)
+#define SADR4LEN sizeof (struct sockaddr_in)
+#define SADR4ADR(sadr) SADR4 (sadr)->sin_addr
+#define ADR4LEN sizeof (struct in_addr)
+#define SADR4PORT(sadr) SADR4 (sadr)->sin_port
+
+
+/* IP abstraction layer */
+
+char *ip_sockaddrtostring (struct sockaddr *sadr);
+long ip_sockaddrtoport (struct sockaddr *sadr);
+void *ip_stringtoaddr (char *text,size_t *len,int *family);
+struct sockaddr *ip_newsockaddr (size_t *len);
+struct sockaddr *ip_sockaddr (int family,void *adr,size_t adrlen,
+ unsigned short port,size_t *len);
+char *ip_sockaddrtoname (struct sockaddr *sadr);
+void *ip_nametoaddr (char *name,size_t *len,int *family,char **canonical,
+ void **next);
+
+/* Return IP address string from socket address
+ * Accepts: socket address
+ * Returns: IP address as name string
+ */
+
+char *ip_sockaddrtostring (struct sockaddr *sadr)
+{
+ return (sadr->sa_family == PF_INET) ?
+ inet_ntoa (SADR4ADR (sadr)) : "NON-IPv4";
+}
+
+
+/* Return port from socket address
+ * Accepts: socket address
+ * Returns: port number or -1 if can't determine it
+ */
+
+long ip_sockaddrtoport (struct sockaddr *sadr)
+{
+ return (sadr->sa_family == PF_INET) ? ntohs (SADR4PORT (sadr)) : -1;
+}
+
+
+/* Return IP address from string
+ * Accepts: name string
+ * pointer to returned length
+ * pointer to returned address family
+ * Returns: address if valid, length and family updated, or NIL
+ */
+
+void *ip_stringtoaddr (char *text,size_t *len,int *family)
+{
+ unsigned long adr;
+ struct in_addr *ret;
+ /* get address */
+ if ((adr = inet_addr (text)) == -1) ret = NIL;
+ else { /* make in_addr */
+ ret = (struct in_addr *) fs_get (*len = ADR4LEN);
+ *family = AF_INET; /* IPv4 */
+ ret->s_addr = adr; /* set address */
+ }
+ return (void *) ret;
+}
+
+/* Create a maximum-size socket address
+ * Accepts: pointer to return maximum socket address length
+ * Returns: new, empty socket address of maximum size
+ */
+
+struct sockaddr *ip_newsockaddr (size_t *len)
+{
+ return (struct sockaddr *) memset (fs_get (SADRLEN),0,*len = SADRLEN);
+}
+
+
+/* Stuff a socket address
+ * Accepts: address family
+ * IPv4 address
+ * length of address (always 4 in IPv4)
+ * port number
+ * pointer to return socket address length
+ * Returns: socket address or NIL if error
+ */
+
+struct sockaddr *ip_sockaddr (int family,void *adr,size_t adrlen,
+ unsigned short port,size_t *len)
+{
+ struct sockaddr *sadr = ip_newsockaddr (len);
+ switch (family) { /* build socket address based upon family */
+ case AF_INET: /* IPv4 */
+ sadr->sa_family = PF_INET;
+ /* copy host address */
+ memcpy (&SADR4ADR (sadr),adr,adrlen);
+ /* copy port number in network format */
+ SADR4PORT (sadr) = htons (port);
+ *len = SADR4LEN;
+ break;
+ default: /* non-IP?? */
+ sadr->sa_family = PF_UNSPEC;
+ break;
+ }
+ return sadr;
+}
+
+/* Return name from socket address
+ * Accepts: socket address
+ * Returns: canonical name for that address or NIL if none
+ */
+
+char *ip_sockaddrtoname (struct sockaddr *sadr)
+{
+ struct hostent *he;
+ return ((sadr->sa_family == PF_INET) &&
+ (he = gethostbyaddr ((char *) &SADR4ADR (sadr),ADR4LEN,AF_INET))) ?
+ (char *) he->h_name : NIL;
+}
+
+
+/* Return address from name
+ * Accepts: name or NIL to return next address
+ * pointer to previous/returned length
+ * pointer to previous/returned address family
+ * pointer to previous/returned canonical name
+ * pointer to previous/return state for next-address calls
+ * Returns: address with length/family/canonical updated if needed, or NIL
+ */
+
+void *ip_nametoaddr (char *name,size_t *len,int *family,char **canonical,
+ void **next)
+{
+ char **adl,tmp[MAILTMPLEN];
+ struct hostent *he;
+ if (name) { /* first lookup? */
+ /* yes, do case-independent lookup */
+ if ((strlen (name) < MAILTMPLEN) &&
+ (he = gethostbyname (lcase (strcpy (tmp,name))))) {
+ adl = he->h_addr_list;
+ if (len) *len = he->h_length;
+ if (family) *family = he->h_addrtype;
+ if (canonical) *canonical = (char *) he->h_name;
+ if (next) *next = (void *) adl;
+ }
+ else { /* error */
+ adl = NIL;
+ if (len) *len = 0;
+ if (family) *family = 0;
+ if (canonical) *canonical = NIL;
+ if (next) *next = NIL;
+ }
+ }
+ /* return next in series */
+ else if (next && (adl = (char **) *next)) *next = ++adl;
+ else adl = NIL; /* failure */
+ return adl ? (void *) *adl : NIL;
+}
diff --git a/imap/src/osdep/unix/ip6_unix.c b/imap/src/osdep/unix/ip6_unix.c
new file mode 100644
index 00000000..c15dfbc0
--- /dev/null
+++ b/imap/src/osdep/unix/ip6_unix.c
@@ -0,0 +1,288 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX IPv6 routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 18 December 2003
+ * Last Edited: 30 August 2006
+ */
+
+
+/*
+ * There is some amazingly bad design in IPv6 sockets.
+ *
+ * Supposedly, the new getnameinfo() and getaddrinfo() functions create an
+ * abstraction that is not dependent upon IPv4 or IPv6. However, the
+ * definition of getnameinfo() requires that the caller pass the length of
+ * the sockaddr instead of deriving it from sa_family. The man page says
+ * that there's an sa_len member in the sockaddr, but actually there isn't.
+ * This means that any caller to getnameinfo() and getaddrinfo() has to know
+ * the size for the protocol family used by that sockaddr.
+ *
+ * The new sockaddr_in6 is bigger than the generic sockaddr (which is what
+ * connect(), accept(), bind(), getpeername(), getsockname(), etc. expect).
+ * Rather than increase the size of sockaddr, there's a new sockaddr_storage
+ * which is only usable for allocating space.
+ */
+
+#define SADRLEN sizeof (struct sockaddr_storage)
+
+#define SADR4(sadr) ((struct sockaddr_in *) sadr)
+#define SADR4LEN sizeof (struct sockaddr_in)
+#define SADR4ADR(sadr) SADR4 (sadr)->sin_addr
+#define ADR4LEN sizeof (struct in_addr)
+#define SADR4PORT(sadr) SADR4 (sadr)->sin_port
+
+#define SADR6(sadr) ((struct sockaddr_in6 *) sadr)
+#define SADR6LEN sizeof (struct sockaddr_in6)
+#define SADR6ADR(sadr) SADR6 (sadr)->sin6_addr
+#define ADR6LEN sizeof (struct in6_addr)
+#define SADR6PORT(sadr) SADR6 (sadr)->sin6_port
+
+
+/* IP abstraction layer */
+
+char *ip_sockaddrtostring (struct sockaddr *sadr);
+long ip_sockaddrtoport (struct sockaddr *sadr);
+void *ip_stringtoaddr (char *text,size_t *len,int *family);
+struct sockaddr *ip_newsockaddr (size_t *len);
+struct sockaddr *ip_sockaddr (int family,void *adr,size_t adrlen,
+ unsigned short port,size_t *len);
+char *ip_sockaddrtoname (struct sockaddr *sadr);
+void *ip_nametoaddr (char *name,size_t *len,int *family,char **canonical,
+ void **next);
+
+/* Return IP address string from socket address
+ * Accepts: socket address
+ * Returns: IP address as name string
+ */
+
+char *ip_sockaddrtostring (struct sockaddr *sadr)
+{
+ static char tmp[NI_MAXHOST];
+ switch (sadr->sa_family) {
+ case PF_INET: /* IPv4 */
+ if (!getnameinfo (sadr,SADR4LEN,tmp,NI_MAXHOST,NIL,NIL,NI_NUMERICHOST))
+ return tmp;
+ break;
+ case PF_INET6: /* IPv6 */
+ if (!getnameinfo (sadr,SADR6LEN,tmp,NI_MAXHOST,NIL,NIL,NI_NUMERICHOST))
+ return tmp;
+ break;
+ }
+ return "NON-IP";
+}
+
+
+/* Return port from socket address
+ * Accepts: socket address
+ * Returns: port number or -1 if can't determine it
+ */
+
+long ip_sockaddrtoport (struct sockaddr *sadr)
+{
+ switch (sadr->sa_family) {
+ case PF_INET:
+ return ntohs (SADR4PORT (sadr));
+ case PF_INET6:
+ return ntohs (SADR6PORT (sadr));
+ }
+ return -1;
+}
+
+/* Return IP address from string
+ * Accepts: name string
+ * pointer to returned length
+ * pointer to returned address family
+ * Returns: address if valid, length and family updated, or NIL
+ */
+
+void *ip_stringtoaddr (char *text,size_t *len,int *family)
+
+{
+ char tmp[MAILTMPLEN];
+ static struct addrinfo *hints;
+ struct addrinfo *ai;
+ void *adr = NIL;
+ if (!hints) { /* hints set up yet? */
+ hints = (struct addrinfo *) /* one-time setup */
+ memset (fs_get (sizeof (struct addrinfo)),0,sizeof (struct addrinfo));
+ hints->ai_family = AF_UNSPEC;/* allow any address family */
+ hints->ai_socktype = SOCK_STREAM;
+ /* numeric name only */
+ hints->ai_flags = AI_NUMERICHOST;
+ }
+ /* case-independent lookup */
+ if (text && (strlen (text) < MAILTMPLEN) &&
+ (!getaddrinfo (lcase (strcpy (tmp,text)),NIL,hints,&ai))) {
+ switch (*family = ai->ai_family) {
+ case AF_INET: /* IPv4 */
+ adr = fs_get (*len = ADR4LEN);
+ memcpy (adr,(void *) &SADR4ADR (ai->ai_addr),*len);
+ break;
+ case AF_INET6: /* IPv6 */
+ adr = fs_get (*len = ADR6LEN);
+ memcpy (adr,(void *) &SADR6ADR (ai->ai_addr),*len);
+ break;
+ }
+ freeaddrinfo (ai); /* free addrinfo */
+ }
+ return adr;
+}
+
+/* Create a maximum-size socket address
+ * Accepts: pointer to return maximum socket address length
+ * Returns: new, empty socket address of maximum size
+ */
+
+struct sockaddr *ip_newsockaddr (size_t *len)
+{
+ return (struct sockaddr *) memset (fs_get (SADRLEN),0,*len = SADRLEN);
+}
+
+
+/* Stuff a socket address
+ * Accepts: address family
+ * IPv4 address
+ * length of address
+ * port number
+ * pointer to return socket address length
+ * Returns: socket address
+ */
+
+struct sockaddr *ip_sockaddr (int family,void *adr,size_t adrlen,
+ unsigned short port,size_t *len)
+{
+ struct sockaddr *sadr = ip_newsockaddr (len);
+ switch (family) { /* build socket address based upon family */
+ case AF_INET: /* IPv4 */
+ sadr->sa_family = PF_INET;
+ /* copy host address */
+ memcpy (&SADR4ADR (sadr),adr,adrlen);
+ /* copy port number in network format */
+ SADR4PORT (sadr) = htons (port);
+ *len = SADR4LEN;
+ break;
+ case AF_INET6: /* IPv6 */
+ sadr->sa_family = PF_INET6;
+ /* copy host address */
+ memcpy (&SADR6ADR (sadr),adr,adrlen);
+ /* copy port number in network format */
+ SADR6PORT (sadr) = htons (port);
+ *len = SADR6LEN;
+ break;
+ default: /* non-IP?? */
+ sadr->sa_family = PF_UNSPEC;
+ break;
+ }
+ return sadr;
+}
+
+/* Return name from socket address
+ * Accepts: socket address
+ * Returns: canonical name for that address or NIL if none
+ */
+
+char *ip_sockaddrtoname (struct sockaddr *sadr)
+{
+ static char tmp[NI_MAXHOST];
+ switch (sadr->sa_family) {
+ case PF_INET: /* IPv4 */
+ if (!getnameinfo (sadr,SADR4LEN,tmp,NI_MAXHOST,NIL,NIL,NI_NAMEREQD))
+ return tmp;
+ break;
+ case PF_INET6: /* IPv6 */
+ if (!getnameinfo (sadr,SADR6LEN,tmp,NI_MAXHOST,NIL,NIL,NI_NAMEREQD))
+ return tmp;
+ break;
+ }
+ return NIL;
+}
+
+/* Return address from name
+ * Accepts: name or NIL to return next address
+ * pointer to previous/returned length
+ * pointer to previous/returned address family
+ * pointer to previous/returned canonical name
+ * pointer to previous/return state for next-address calls
+ * Returns: address with length/family/canonical updated if needed, or NIL
+ */
+
+void *ip_nametoaddr (char *name,size_t *len,int *family,char **canonical,
+ void **next)
+{
+ struct addrinfo *cur = NIL;
+ static struct addrinfo *hints;
+ static struct addrinfo *ai = NIL;
+ static char lcname[MAILTMPLEN];
+ if (!hints) { /* hints set up yet? */
+ hints = (struct addrinfo *) /* one-time setup */
+ memset (fs_get (sizeof (struct addrinfo)),0,sizeof (struct addrinfo));
+ /* allow any address family */
+ hints->ai_family = AF_UNSPEC;
+ hints->ai_socktype = SOCK_STREAM;
+ /* need canonical name */
+ hints->ai_flags = AI_CANONNAME;
+ }
+ if (name) { /* name supplied? */
+ if (ai) {
+ freeaddrinfo (ai); /* free old addrinfo */
+ ai = NIL;
+ }
+ /* case-independent lookup */
+ if ((strlen (name) < MAILTMPLEN) &&
+ (!getaddrinfo (lcase (strcpy (lcname,name)),NIL,hints,&ai))) {
+ cur = ai; /* current block */
+ if (canonical) /* set canonical name */
+ *canonical = cur->ai_canonname ? cur->ai_canonname : lcname;
+ /* remember as next block */
+ if (next) *next = (void *) ai;
+ }
+ else { /* error */
+ cur = NIL;
+ if (len) *len = 0;
+ if (family) *family = 0;
+ if (canonical) *canonical = NIL;
+ if (next) *next = NIL;
+ }
+ }
+ /* return next in series */
+ else if (next && (cur = ((struct addrinfo *) *next)->ai_next)) {
+ *next = cur; /* set as last address */
+ /* set canonical in case changed */
+ if (canonical && cur->ai_canonname) *canonical = cur->ai_canonname;
+ }
+
+ if (cur) { /* got data? */
+ if (family) *family = cur->ai_family;
+ switch (cur->ai_family) {
+ case AF_INET:
+ if (len) *len = ADR4LEN;
+ return (void *) &SADR4ADR (cur->ai_addr);
+ case AF_INET6:
+ if (len) *len = ADR6LEN;
+ return (void *) &SADR6ADR (cur->ai_addr);
+ }
+ }
+ if (len) *len = 0; /* error return */
+ return NIL;
+}
diff --git a/imap/src/osdep/unix/ipo_unix.c b/imap/src/osdep/unix/ipo_unix.c
new file mode 100644
index 00000000..665fb2e8
--- /dev/null
+++ b/imap/src/osdep/unix/ipo_unix.c
@@ -0,0 +1,181 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX IPv4 old routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 18 December 2003
+ * Last Edited: 30 August 2006
+ */
+
+#define SADRLEN sizeof (struct sockaddr)
+
+#define SADR4(sadr) ((struct sockaddr_in *) sadr)
+#define SADR4LEN sizeof (struct sockaddr_in)
+#define SADR4ADR(sadr) SADR4 (sadr)->sin_addr
+#define ADR4LEN sizeof (struct in_addr)
+#define SADR4PORT(sadr) SADR4 (sadr)->sin_port
+
+
+/* IP abstraction layer */
+
+char *ip_sockaddrtostring (struct sockaddr *sadr);
+long ip_sockaddrtoport (struct sockaddr *sadr);
+void *ip_stringtoaddr (char *text,size_t *len,int *family);
+struct sockaddr *ip_newsockaddr (size_t *len);
+struct sockaddr *ip_sockaddr (int family,void *adr,size_t adrlen,
+ unsigned short port,size_t *len);
+char *ip_sockaddrtoname (struct sockaddr *sadr);
+void *ip_nametoaddr (char *name,size_t *len,int *family,char **canonical,
+ void **next);
+
+/* Return IP address string from socket address
+ * Accepts: socket address
+ * Returns: IP address as name string
+ */
+
+char *ip_sockaddrtostring (struct sockaddr *sadr)
+{
+ return (sadr->sa_family == PF_INET) ?
+ inet_ntoa (SADR4ADR (sadr)) : "NON-IPv4";
+}
+
+
+/* Return port from socket address
+ * Accepts: socket address
+ * Returns: port number or -1 if can't determine it
+ */
+
+long ip_sockaddrtoport (struct sockaddr *sadr)
+{
+ return (sadr->sa_family == PF_INET) ? ntohs (SADR4PORT (sadr)) : -1;
+}
+
+
+/* Return IP address from string
+ * Accepts: name string
+ * pointer to returned length
+ * pointer to returned address family
+ * Returns: address if valid, length and family updated, or NIL
+ */
+
+void *ip_stringtoaddr (char *text,size_t *len,int *family)
+{
+ unsigned long adr;
+ struct in_addr *ret;
+ /* get address */
+ if ((adr = inet_addr (text)) == -1) ret = NIL;
+ else { /* make in_addr */
+ ret = (struct in_addr *) fs_get (*len = ADR4LEN);
+ *family = AF_INET; /* IPv4 */
+ ret->s_addr = adr; /* set address */
+ }
+ return (void *) ret;
+}
+
+/* Create a maximum-size socket address
+ * Accepts: pointer to return maximum socket address length
+ * Returns: new, empty socket address of maximum size
+ */
+
+struct sockaddr *ip_newsockaddr (size_t *len)
+{
+ return (struct sockaddr *) memset (fs_get (SADRLEN),0,*len = SADRLEN);
+}
+
+
+/* Stuff a socket address
+ * Accepts: address family
+ * IPv4 address
+ * length of address (always 4 in IPv4)
+ * port number
+ * pointer to return socket address length
+ * Returns: socket address or NIL if error
+ */
+
+struct sockaddr *ip_sockaddr (int family,void *adr,size_t adrlen,
+ unsigned short port,size_t *len)
+{
+ struct sockaddr *sadr = ip_newsockaddr (len);
+ switch (family) { /* build socket address based upon family */
+ case AF_INET: /* IPv4 */
+ sadr->sa_family = PF_INET;
+ /* copy host address */
+ memcpy (&SADR4ADR (sadr),adr,adrlen);
+ /* copy port number in network format */
+ SADR4PORT (sadr) = htons (port);
+ *len = SADR4LEN;
+ break;
+ default: /* non-IP?? */
+ sadr->sa_family = PF_UNSPEC;
+ break;
+ }
+ return sadr;
+}
+
+/* Return name from socket address
+ * Accepts: socket address
+ * Returns: canonical name for that address or NIL if none
+ */
+
+char *ip_sockaddrtoname (struct sockaddr *sadr)
+{
+ struct hostent *he;
+ return ((sadr->sa_family == PF_INET) &&
+ (he = gethostbyaddr ((char *) &SADR4ADR (sadr),ADR4LEN,AF_INET))) ?
+ (char *) he->h_name : NIL;
+}
+
+
+/* Return address from name
+ * Accepts: name or NIL to return next address
+ * pointer to previous/returned length
+ * pointer to previous/returned address family
+ * pointer to previous/returned canonical name
+ * pointer to previous/return state for next-address calls
+ * Returns: address with length/family/canonical updated if needed, or NIL
+ */
+
+void *ip_nametoaddr (char *name,size_t *len,int *family,char **canonical,
+ void **next)
+{
+ char tmp[MAILTMPLEN];
+ struct hostent *he;
+ void *ret = NIL;
+ if (name) { /* first lookup? */
+ /* yes, do case-independent lookup */
+ if ((strlen (name) < MAILTMPLEN) &&
+ (he = gethostbyname (lcase (strcpy (tmp,name))))) {
+ if (len) *len = he->h_length;
+ if (family) *family = he->h_addrtype;
+ if (canonical) *canonical = (char *) he->h_name;
+ if (next) *next = 0;
+ ret = he->h_addr; /* set result to this one and only block */
+ }
+ else { /* error */
+ if (len) *len = 0;
+ if (family) *family = 0;
+ if (canonical) *canonical = NIL;
+ if (next) *next = NIL;
+ }
+ }
+ return ret; /* return result */
+}
diff --git a/imap/src/osdep/unix/kerb_mit.c b/imap/src/osdep/unix/kerb_mit.c
new file mode 100644
index 00000000..82e6c936
--- /dev/null
+++ b/imap/src/osdep/unix/kerb_mit.c
@@ -0,0 +1,111 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MIT Kerberos routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 4 March 2003
+ * Last Edited: 30 August 2006
+ */
+
+#define PROTOTYPE(x) x
+#include <gssapi/gssapi_generic.h>
+#include <gssapi/gssapi_krb5.h>
+
+
+long kerberos_server_valid (void);
+long kerberos_try_kinit (OM_uint32 error);
+char *kerberos_login (char *user,char *authuser,int argc,char *argv[]);
+
+/* Kerberos server valid check
+ * Returns: T if have keytab, NIL otherwise
+ *
+ * Note that this routine will probably return T only if the process is root.
+ * This is alright since the server is probably still root at this point.
+ */
+
+long kerberos_server_valid ()
+{
+ krb5_context ctx;
+ krb5_keytab kt;
+ krb5_kt_cursor csr;
+ long ret = NIL;
+ /* make a context */
+ if (!krb5_init_context (&ctx)) {
+ /* get default keytab */
+ if (!krb5_kt_default (ctx,&kt)) {
+ /* can do server if have good keytab */
+ if (!krb5_kt_start_seq_get (ctx,kt,&csr) &&
+ !krb5_kt_end_seq_get (ctx,kt,&csr)) ret = LONGT;
+ krb5_kt_close (ctx,kt); /* finished with keytab */
+ }
+ krb5_free_context (ctx); /* finished with context */
+ }
+ return ret;
+}
+
+
+/* Kerberos check for missing or expired credentials
+ * Returns: T if should suggest running kinit, NIL otherwise
+ */
+
+long kerberos_try_kinit (OM_uint32 error)
+{
+ switch (error) {
+ case KRB5KRB_AP_ERR_TKT_EXPIRED:
+ case KRB5_FCC_NOFILE: /* MIT */
+ case KRB5_CC_NOTFOUND: /* Heimdal */
+ return LONGT;
+ }
+ return NIL;
+}
+
+/* Kerberos server log in
+ * Accepts: authorization ID as user name
+ * authentication ID as Kerberos principal
+ * argument count
+ * argument vector
+ * Returns: logged in user name if logged in, NIL otherwise
+ */
+
+char *kerberos_login (char *user,char *authuser,int argc,char *argv[])
+{
+ krb5_context ctx;
+ krb5_principal prnc;
+ char kuser[NETMAXUSER];
+ char *ret = NIL;
+ /* make a context */
+ if (!krb5_init_context (&ctx)) {
+ /* build principal */
+ if (!krb5_parse_name (ctx,authuser,&prnc)) {
+ /* can get local name for this principal? */
+ if (!krb5_aname_to_localname (ctx,prnc,NETMAXUSER-1,kuser)) {
+ /* yes, local name permitted login as user? */
+ if (authserver_login (user,kuser,argc,argv) ||
+ authserver_login (lcase (user),kuser,argc,argv))
+ ret = myusername (); /* yes, return user name */
+ }
+ krb5_free_principal (ctx,prnc);
+ }
+ krb5_free_context (ctx); /* finished with context */
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/unix/log_bsi.c b/imap/src/osdep/unix/log_bsi.c
new file mode 100644
index 00000000..5db7415d
--- /dev/null
+++ b/imap/src/osdep/unix/log_bsi.c
@@ -0,0 +1,55 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: BSI login
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Log in
+ * Accepts: login passwd struct
+ * argument count
+ * argument vector
+ * Returns: T if success, NIL otherwise
+ */
+
+long loginpw (struct passwd *pw,int argc,char *argv[])
+{
+ long ret = NIL;
+ uid_t euid = geteuid ();
+ login_cap_t *lc = login_getclass (pw->pw_class);
+ /* have class and can become user? */
+ if (lc && !seteuid (pw->pw_uid)) {
+ /* ask for approval */
+ if (auth_approve (lc,pw->pw_name,
+ (char *) mail_parameters (NIL,GET_SERVICENAME,NIL)) <= 0)
+ seteuid (euid); /* not approved, restore root euid */
+ else { /* approved */
+ seteuid (euid); /* restore former root euid first */
+ setsid (); /* ensure we are session leader */
+ /* log the guy in */
+ ret = !setusercontext (lc,pw,pw->pw_uid,LOGIN_SETALL&~LOGIN_SETPATH);
+ }
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/unix/log_cyg.c b/imap/src/osdep/unix/log_cyg.c
new file mode 100644
index 00000000..b60a4a7d
--- /dev/null
+++ b/imap/src/osdep/unix/log_cyg.c
@@ -0,0 +1,46 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Cygwin login
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Log in
+ * Accepts: login passwd struct
+ * argument count
+ * argument vector
+ * Returns: T if success, NIL otherwise
+ */
+
+long loginpw (struct passwd *pw,int argc,char *argv[])
+{
+ uid_t uid = pw->pw_uid;
+ /* must be same user name as last checkpw() */
+ if (!(cyg_user && !strcmp (pw->pw_name,cyg_user))) return NIL;
+ /* do the ImpersonateLoggedOnUser() */
+ cygwin_set_impersonation_token (cyg_hdl);
+
+ return !(setgid (pw->pw_gid) || initgroups (cyg_user,pw->pw_gid) ||
+ setuid (uid));
+}
diff --git a/imap/src/osdep/unix/log_old.c b/imap/src/osdep/unix/log_old.c
new file mode 100644
index 00000000..2cb10529
--- /dev/null
+++ b/imap/src/osdep/unix/log_old.c
@@ -0,0 +1,39 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Standard login for very old UNIX systems
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Log in
+ * Accepts: login passwd struct
+ * argument count
+ * argument vector
+ * Returns: T if success, NIL otherwise
+ */
+
+long loginpw (struct passwd *pw,int argc,char *argv[])
+{
+ return !(setgid (pw->pw_gid) || setuid (pw->pw_uid));
+}
diff --git a/imap/src/osdep/unix/log_os4.c b/imap/src/osdep/unix/log_os4.c
new file mode 100644
index 00000000..dec704e5
--- /dev/null
+++ b/imap/src/osdep/unix/log_os4.c
@@ -0,0 +1,58 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: OSF/1 (Digital UNIX) 4 login
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Log in
+ * Accepts: login passwd struct
+ * argument count
+ * argument vector
+ * Returns: T if success, NIL otherwise
+ */
+
+long loginpw (struct passwd *pw,int argc,char *argv[])
+{
+ int i;
+ char *s;
+ char *name = cpystr (pw->pw_name);
+ char *host = cpystr (tcp_clienthost ());
+ uid_t uid = pw->pw_uid;
+ long ret = NIL;
+ /* tie off address */
+ if (s = strchr (host,' ')) *s = '\0';
+ if (*host == '[') { /* convert [a.b.c.d] to a.b.c.d */
+ memmove (host,host+1,i = strlen (host + 2));
+ host[i] = '\0';
+ }
+ if (sia_become_user (checkpw_collect,argc,argv,host,name,NIL,NIL,NIL,NIL,
+ SIA_BEU_REALLOGIN|SIA_BEU_OKROOTDIR) != SIASUCCESS)
+ setreuid (0,0); /* make sure have root again */
+ /* probable success, complete login */
+ else ret = (!setreuid (0,0) && !setuid (uid));
+ fs_give ((void **) &name);
+ fs_give ((void **) &host);
+ return ret;
+}
diff --git a/imap/src/osdep/unix/log_sec.c b/imap/src/osdep/unix/log_sec.c
new file mode 100644
index 00000000..f2641896
--- /dev/null
+++ b/imap/src/osdep/unix/log_sec.c
@@ -0,0 +1,44 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: SecureWare login
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Log in
+ * Accepts: login passwd struct
+ * argument count
+ * argument vector
+ * Returns: T if success, NIL otherwise
+ */
+
+long loginpw (struct passwd *pw,int argc,char *argv[])
+{
+ uid_t uid = pw->pw_uid;
+ char *name = cpystr (pw->pw_name);
+ long ret = !(setluid (uid) || setgid (pw->pw_gid) ||
+ initgroups (name,pw->pw_gid) || setuid (uid));
+ fs_give ((void **) &name);
+ return ret;
+}
diff --git a/imap/src/osdep/unix/log_std.c b/imap/src/osdep/unix/log_std.c
new file mode 100644
index 00000000..a36fa88f
--- /dev/null
+++ b/imap/src/osdep/unix/log_std.c
@@ -0,0 +1,44 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Standard login
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Log in
+ * Accepts: login passwd struct
+ * argument count
+ * argument vector
+ * Returns: T if success, NIL otherwise
+ */
+
+long loginpw (struct passwd *pw,int argc,char *argv[])
+{
+ uid_t uid = pw->pw_uid;
+ char *name = cpystr (pw->pw_name);
+ long ret = !(setgid (pw->pw_gid) || initgroups (name,pw->pw_gid) ||
+ setuid (uid));
+ fs_give ((void **) &name);
+ return ret;
+}
diff --git a/imap/src/osdep/unix/log_sv4.c b/imap/src/osdep/unix/log_sv4.c
new file mode 100644
index 00000000..4738ce12
--- /dev/null
+++ b/imap/src/osdep/unix/log_sv4.c
@@ -0,0 +1,43 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: SVR4 login
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Log in
+ * Accepts: login passwd struct
+ * argument count
+ * argument vector
+ * Returns: T if success, NIL otherwise
+ */
+
+long loginpw (struct passwd *pw,int argc,char *argv[])
+{
+ uid_t uid = pw->pw_uid;
+ char *name = cpystr (pw->pw_name);
+ long ret = !(setgid (pw->pw_gid) || initgroups (name) || setuid (uid));
+ fs_give ((void **) &name);
+ return ret;
+}
diff --git a/imap/src/osdep/unix/mbx.c b/imap/src/osdep/unix/mbx.c
new file mode 100644
index 00000000..1ece5d8d
--- /dev/null
+++ b/imap/src/osdep/unix/mbx.c
@@ -0,0 +1,1855 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MBX mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 October 1995
+ * Last Edited: 11 October 2007
+ */
+
+
+/* FILE TIME SEMANTICS
+ *
+ * The atime is the last read time of the file.
+ * The mtime is the last flags update time of the file.
+ * The ctime is the last write time of the file.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "misc.h"
+#include "dummy.h"
+#include "fdstring.h"
+
+
+/* Build parameters */
+
+#define HDRSIZE 2048
+
+
+/* Kludge to make Cygwin happy */
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+/* MBX I/O stream local data */
+
+typedef struct mbx_local {
+ unsigned int flagcheck: 1; /* if ping should sweep for flags */
+ unsigned int expok: 1; /* if expunging OK in ping */
+ unsigned int expunged : 1; /* if one or more expunged messages */
+ int fd; /* file descriptor for I/O */
+ int ld; /* lock file descriptor */
+ int ffuserflag; /* first free user flag */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ time_t lastsnarf; /* last snarf time */
+ unsigned long lastpid; /* PID of last writer */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ char lock[MAILTMPLEN]; /* buffer to write lock name */
+} MBXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MBXLOCAL *) stream->local)
+
+/* Function prototypes */
+
+DRIVER *mbx_valid (char *name);
+int mbx_isvalid (MAILSTREAM **stream,char *name,char *tmp,int *ld,char *lock,
+ long flags);
+void *mbx_parameters (long function,void *value);
+void mbx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void mbx_list (MAILSTREAM *stream,char *ref,char *pat);
+void mbx_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long mbx_create (MAILSTREAM *stream,char *mailbox);
+long mbx_delete (MAILSTREAM *stream,char *mailbox);
+long mbx_rename (MAILSTREAM *stream,char *old,char *newname);
+long mbx_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *mbx_open (MAILSTREAM *stream);
+void mbx_close (MAILSTREAM *stream,long options);
+void mbx_abort (MAILSTREAM *stream);
+void mbx_flags (MAILSTREAM *stream,char *sequence,long flags);
+char *mbx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags);
+long mbx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void mbx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+void mbx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long mbx_ping (MAILSTREAM *stream);
+void mbx_check (MAILSTREAM *stream);
+long mbx_expunge (MAILSTREAM *stream,char *sequence,long options);
+void mbx_snarf (MAILSTREAM *stream);
+long mbx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long mbx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+char *mbx_file (char *dst,char *name);
+long mbx_parse (MAILSTREAM *stream);
+MESSAGECACHE *mbx_elt (MAILSTREAM *stream,unsigned long msgno,long expok);
+unsigned long mbx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt);
+void mbx_update_header (MAILSTREAM *stream);
+void mbx_update_status (MAILSTREAM *stream,unsigned long msgno,long flags);
+unsigned long mbx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size,char **hdr);
+unsigned long mbx_rewrite (MAILSTREAM *stream,unsigned long *reclaimed,
+ long flags);
+long mbx_flaglock (MAILSTREAM *stream);
+
+/* MBX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mbxdriver = {
+ "mbx", /* driver name */
+ DR_LOCAL|DR_MAIL|DR_CRLF|DR_LOCKING,
+ /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ mbx_valid, /* mailbox is valid for us */
+ mbx_parameters, /* manipulate parameters */
+ mbx_scan, /* scan mailboxes */
+ mbx_list, /* list mailboxes */
+ mbx_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ mbx_create, /* create mailbox */
+ mbx_delete, /* delete mailbox */
+ mbx_rename, /* rename mailbox */
+ mbx_status, /* status of mailbox */
+ mbx_open, /* open mailbox */
+ mbx_close, /* close mailbox */
+ mbx_flags, /* fetch message "fast" attributes */
+ mbx_flags, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mbx_header, /* fetch message header */
+ mbx_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ mbx_flag, /* modify flags */
+ mbx_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mbx_ping, /* ping mailbox to see if still alive */
+ mbx_check, /* check for new messages */
+ mbx_expunge, /* expunge deleted messages */
+ mbx_copy, /* copy messages to another mailbox */
+ mbx_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mbxproto = {&mbxdriver};
+
+/* MBX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mbx_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ int fd = mbx_isvalid (NIL,name,tmp,NIL,NIL,NIL);
+ if (fd < 0) return NIL;
+ close (fd); /* don't need the fd now */
+ return &mbxdriver;
+}
+
+
+/* MBX mail test for valid mailbox
+ * Accepts: returned stream with valid mailbox keywords
+ * mailbox name
+ * scratch buffer
+ * returned lock fd
+ * returned lock name
+ * RW flags or NIL for readonly
+ * Returns: file descriptor if valid, NIL otherwise
+ */
+
+#define MBXISVALIDNOUID 0x1 /* RW, don't do UID action */
+#define MBXISVALIDUID 0x2 /* RW, do UID action */
+
+int mbx_isvalid (MAILSTREAM **stream,char *name,char *tmp,int *ld,char *lock,
+ long flags)
+{
+ int fd,upd;
+ int ret = -1;
+ unsigned long i;
+ long j,k;
+ off_t pos;
+ char c,*s,*t,hdr[HDRSIZE];
+ struct stat sbuf;
+ time_t tp[2];
+ int error = EINVAL; /* assume invalid argument */
+ if (ld) *ld = -1; /* initially no lock */
+ if ((s = mbx_file (tmp,name)) && !stat (s,&sbuf) &&
+ ((fd = open (tmp,(flags ? O_RDWR : O_RDONLY)|O_BINARY,NIL)) >= 0)) {
+ error = -1; /* bogus format */
+ /* I love cretinous C compilers -- don't you? */
+ if (read (fd,hdr,HDRSIZE) == HDRSIZE)
+ if ((hdr[0] == '*') && (hdr[1] == 'm') && (hdr[2] == 'b') &&
+ (hdr[3] == 'x') && (hdr[4] == '*') && (hdr[5] == '\015') &&
+ (hdr[6] == '\012') && isxdigit (hdr[7]) && isxdigit (hdr[8]))
+ if (isxdigit (hdr[9]) && isxdigit (hdr[10]) && isxdigit (hdr[11]) &&
+ isxdigit (hdr[12]) && isxdigit (hdr[13]) && isxdigit (hdr[14]) &&
+ isxdigit (c = hdr[15]) && isxdigit (hdr[16]))
+ if (isxdigit (hdr[17]) && isxdigit (hdr[18]) &&
+ isxdigit (hdr[19]) && isxdigit (hdr[20]) &&
+ isxdigit (hdr[21]) && isxdigit (hdr[22]) &&
+ (hdr[23] == '\015') && (hdr[24] == '\012')) {
+ ret = fd; /* mbx format */
+
+ if (stream) { /* lock if making mini-stream */
+ if (flock (fd,LOCK_SH) ||
+ (flags && ((*ld = lockfd (fd,lock,LOCK_EX)) < 0))) ret = -1;
+ /* reread data now that locked */
+ else if (lseek (fd,0,L_SET) ||
+ (read (fd,hdr,HDRSIZE) != HDRSIZE)) ret = -1;
+ else {
+ *stream = (MAILSTREAM *) memset (fs_get (sizeof (MAILSTREAM)),
+ 0,sizeof (MAILSTREAM));
+ hdr[15] = '\0'; /* tie off UIDVALIDITY */
+ (*stream)->uid_validity = strtoul (hdr+7,NIL,16);
+ hdr[15] = c; /* now get UIDLAST */
+ (*stream)->uid_last = strtoul (hdr+15,NIL,16);
+ /* parse user flags */
+ for (i = 0, s = hdr + 25;
+ (i < NUSERFLAGS) && (t = strchr (s,'\015')) && (t - s);
+ i++, s = t + 2) {
+ *t = '\0'; /* tie off flag */
+ if (strlen (s) <= MAXUSERFLAG)
+ (*stream)->user_flags[i] = cpystr (s);
+ }
+ /* make sure have true UIDLAST */
+ if (flags & MBXISVALIDUID) {
+ for (upd = NIL,pos = 2048, k = 0; pos < sbuf.st_size;
+ pos += (j + k)) {
+ /* read header for this message */
+ lseek (fd,pos,L_SET);
+ if ((j = read (fd,hdr,64)) >= 0) {
+ hdr[j] = '\0';
+ if ((s = strchr (hdr,'\015')) && (s[1] == '\012')) {
+ *s = '\0';
+ k = s + 2 - hdr;
+ if ((s = strchr (hdr,',')) &&
+ (j = strtol (s+1,&s,10)) && (*s == ';') &&
+ (s = strchr (s+1,'-'))) {
+ /* get UID if there is any */
+ i = strtoul (++s,&t,16);
+ if (!*t && (t == (s + 8)) &&
+ (i <= (*stream)->uid_last)) {
+ if (!i) {
+ lseek (fd,pos + s - hdr,L_SET);
+ sprintf (hdr,"%08lx",++(*stream)->uid_last);
+ write (fd,hdr,8);
+ upd = T;
+ }
+ continue;
+ }
+ }
+ }
+ ret = -1; /* error, give up */
+ *stream = mail_close (*stream);
+ pos = sbuf.st_size + 1;
+ j = k = 0;
+ }
+ }
+
+ if (upd) { /* need to update hdr with new UIDLAST? */
+ lseek (fd,15,L_SET);
+ sprintf (hdr,"%08lx",(*stream)->uid_last);
+ write (fd,hdr,8);
+ }
+ }
+ }
+ }
+ }
+ if (ret != fd) close (fd); /* close the file */
+ else lseek (fd,0,L_SET); /* else rewind to start */
+ /* \Marked status? */
+ if (sbuf.st_ctime > sbuf.st_atime) {
+ tp[0] = sbuf.st_atime; /* preserve atime and mtime */
+ tp[1] = sbuf.st_mtime;
+ utime (tmp,tp); /* set the times */
+ }
+ }
+ /* in case INBOX but not mbx format */
+ else if (((error = errno) == ENOENT) && !compare_cstring (name,"INBOX"))
+ error = -1;
+ if ((ret < 0) && ld && (*ld >= 0)) {
+ unlockfd (*ld,lock);
+ *ld = -1;
+ }
+ errno = error; /* return as last error */
+ return ret; /* return what we should */
+}
+
+/* MBX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mbx_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = mbx_file ((char *) value,"INBOX");
+ break;
+ case SET_ONETIMEEXPUNGEATPING:
+ if (value) ((MBXLOCAL *) ((MAILSTREAM *) value)->local)->expok = T;
+ case GET_ONETIMEEXPUNGEATPING:
+ if (value) ret = (void *)
+ (((MBXLOCAL *) ((MAILSTREAM *) value)->local)->expok ? VOIDT : NIL);
+ break;
+ }
+ return ret;
+}
+
+
+/* MBX mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mbx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* MBX mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mbx_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* MBX mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mbx_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* MBX mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,*t,mbx[MAILTMPLEN],tmp[HDRSIZE];
+ long ret = NIL;
+ int i,fd;
+ if (!(s = mbx_file (mbx,mailbox))) {
+ sprintf (mbx,"Can't create %.80s: invalid name",mailbox);
+ MM_LOG (mbx,ERROR);
+ }
+ /* create underlying file */
+ else if (dummy_create_path (stream,s,get_dir_protection (mailbox))) {
+ /* done if made directory */
+ if ((s = strrchr (s,'/')) && !s[1]) return T;
+ if ((fd = open (mbx,O_WRONLY|O_BINARY,NIL)) < 0) {
+ sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ else {
+ memset (tmp,'\0',HDRSIZE);/* initialize header */
+ sprintf (s = tmp,"*mbx*\015\012%08lx00000000\015\012",
+ (unsigned long) time (0));
+ for (i = 0; i < NUSERFLAGS; ++i) {
+ t = (stream && stream->user_flags[i]) ? stream->user_flags[i] :
+ ((t = default_user_flag (i)) ? t : "");
+ sprintf (s += strlen (s),"%s\015\012",t);
+ }
+ if (write (fd,tmp,HDRSIZE) != HDRSIZE) {
+ sprintf (tmp,"Can't initialize mailbox node %.80s: %s",
+ mbx,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ else ret = T; /* success */
+ close (fd); /* close file */
+ }
+ }
+ /* set proper protections */
+ return ret ? set_mbx_protections (mailbox,mbx) : NIL;
+}
+
+
+/* MBX mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return mbx_rename (stream,mailbox,NIL);
+}
+
+/* MBX mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = LONGT;
+ char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ int fd,ld;
+ struct stat sbuf;
+ if (!mbx_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'/')) && !s[1])))) {
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ else if ((fd = open (file,O_RDWR|O_BINARY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get parse/append permission */
+ if ((ld = lockfd (fd,lock,LOCK_EX)) < 0) {
+ MM_LOG ("Unable to lock rename mailbox",ERROR);
+ return NIL;
+ }
+ /* lock out other users */
+ if (flock (fd,LOCK_EX|LOCK_NB)) {
+ close (fd); /* couldn't lock, give up on it then */
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+ MM_LOG (tmp,ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return NIL;
+ }
+
+ if (newname) { /* want rename? */
+ /* found superior to destination name? */
+ if (s = strrchr (tmp,'/')) {
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* superior name doesn't exist, create it */
+ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,tmp,get_dir_protection (newname)))
+ ret = NIL;
+ else *s = c; /* restore full name */
+ }
+ /* rename the file */
+ if (ret && rename (file,tmp)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ else if (unlink (file)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ flock (fd,LOCK_UN); /* release lock on the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ close (fd); /* close the file */
+ /* recreate file if renamed INBOX */
+ if (ret && !compare_cstring (old,"INBOX")) mbx_create (NIL,"INBOX");
+ return ret; /* return success */
+}
+
+/* MBX Mail status
+ * Accepts: mail stream
+ * mailbox name
+ * status flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_status (MAILSTREAM *stream,char *mbx,long flags)
+{
+ MAILSTATUS status;
+ unsigned long i;
+ MAILSTREAM *tstream = NIL;
+ MAILSTREAM *systream = NIL;
+ /* make temporary stream (unless this mbx) */
+ if (!stream && !(stream = tstream =
+ mail_open (NIL,mbx,OP_READONLY|OP_SILENT)))
+ return NIL;
+ status.flags = flags; /* return status values */
+ status.messages = stream->nmsgs;
+ status.recent = stream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++)
+ if (!mail_elt (stream,i)->seen) status.unseen++;
+ status.uidnext = stream->uid_last + 1;
+ status.uidvalidity = stream->uid_validity;
+ /* calculate post-snarf results */
+ if (!status.recent && stream->inbox &&
+ (systream = mail_open (NIL,sysinbox (),OP_READONLY|OP_SILENT))) {
+ status.messages += systream->nmsgs;
+ status.recent += systream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1; i <= systream->nmsgs; i++)
+ if (!mail_elt (systream,i)->seen) status.unseen++;
+ /* kludge but probably good enough */
+ status.uidnext += systream->nmsgs;
+ }
+ MM_STATUS(stream,mbx,&status);/* pass status to main program */
+ if (tstream) mail_close (tstream);
+ if (systream) mail_close (systream);
+ return T; /* success */
+}
+
+/* MBX mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mbx_open (MAILSTREAM *stream)
+{
+ int fd,ld;
+ short silent;
+ char tmp[MAILTMPLEN];
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return user_flags (&mbxproto);
+ if (stream->local) fatal ("mbx recycle stream");
+ /* canonicalize the mailbox name */
+ if (!mbx_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ MM_LOG (tmp,ERROR);
+ }
+ if (stream->rdonly ||
+ (fd = open (tmp,O_RDWR|O_BINARY,NIL)) < 0) {
+ if ((fd = open (tmp,O_RDONLY|O_BINARY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ else if (!stream->rdonly) { /* got it, but readonly */
+ MM_LOG ("Can't get write access to mailbox, access is readonly",WARN);
+ stream->rdonly = T;
+ }
+ }
+
+ stream->local = memset (fs_get (sizeof (MBXLOCAL)),NIL,sizeof (MBXLOCAL));
+ LOCAL->fd = fd; /* bind the file */
+ LOCAL->ld = -1; /* no flaglock */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ /* get parse/append permission */
+ if ((ld = lockfd (LOCAL->fd,tmp,LOCK_EX)) < 0) {
+ MM_LOG ("Unable to lock open mailbox",ERROR);
+ return NIL;
+ }
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH); /* lock the file */
+ (*bn) (BLOCK_NONE,NIL);
+ unlockfd (ld,tmp); /* release shared parse permission */
+ LOCAL->filesize = HDRSIZE; /* initialize parsed file size */
+ /* time not set up yet */
+ LOCAL->lastsnarf = LOCAL->filetime = 0;
+ LOCAL->expok = LOCAL->flagcheck = NIL;
+ stream->sequence++; /* bump sequence number */
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ silent = stream->silent; /* defer events */
+ stream->silent = T;
+ if (mbx_ping (stream) && !stream->nmsgs)
+ MM_LOG ("Mailbox is empty",(long) NIL);
+ stream->silent = silent; /* now notify upper level */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,stream->recent);
+ if (!LOCAL) return NIL; /* failure if stream died */
+ stream->perm_seen = stream->perm_deleted = stream->perm_flagged =
+ stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ stream->kwd_create = (stream->user_flags[NUSERFLAGS-1] || stream->rdonly) ?
+ NIL : T; /* can we create new user flags? */
+ return stream; /* return stream to caller */
+}
+
+/* MBX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mbx_close (MAILSTREAM *stream,long options)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ /* do an expunge if requested */
+ if (options & CL_EXPUNGE) mbx_expunge (stream,NIL,NIL);
+ else { /* otherwise do a checkpoint to purge */
+ LOCAL->expok = T; /* possible expunged messages */
+ mbx_ping (stream);
+ }
+ stream->silent = silent; /* restore previous status */
+ mbx_abort (stream);
+ }
+}
+
+
+/* MBX mail abort stream
+ * Accepts: MAIL stream
+ */
+
+void mbx_abort (MAILSTREAM *stream)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ flock (LOCAL->fd,LOCK_UN); /* unlock local file */
+ close (LOCAL->fd); /* close the local file */
+ /* free local text buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+
+/* MBX mail fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ * Sniffs at file to see if some other process changed the flags
+ */
+
+void mbx_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned long i;
+ if (mbx_ping (stream) && /* ping mailbox, get new status for messages */
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence && !elt->valid)
+ mbx_elt (stream,i,NIL);
+}
+
+/* MBX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *mbx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags)
+{
+ unsigned long i;
+ char *s;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ /* get header position, possibly header */
+ i = mbx_hdrpos (stream,msgno,length,&s);
+ if (!s) { /* mbx_hdrpos() returned header? */
+ lseek (LOCAL->fd,i,L_SET); /* no, get to header position */
+ /* is buffer big enough? */
+ if (*length > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = *length) + 1);
+ }
+ /* slurp the data */
+ read (LOCAL->fd,s = LOCAL->buf,*length);
+ }
+ s[*length] = '\0'; /* tie off string */
+ return s;
+}
+
+/* MBX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ FDDATA d;
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ /* get message status */
+ elt = mbx_elt (stream,msgno,NIL);
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen && mbx_flaglock (stream)) {
+ elt->seen = T; /* mark message as seen */
+ /* recalculate status */
+ mbx_update_status (stream,msgno,NIL);
+ MM_FLAGS (stream,msgno);
+ /* update flags */
+ mbx_flag (stream,NIL,NIL,NIL);
+ }
+ if (!LOCAL) return NIL; /* mbx_flaglock() could have aborted */
+ /* find header position */
+ i = mbx_hdrpos (stream,msgno,&j,NIL);
+ d.fd = LOCAL->fd; /* set up file descriptor */
+ d.pos = i + j;
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE;
+ INIT (bs,fd_string,&d,elt->rfc822_size - j);
+ return LONGT; /* success */
+}
+
+/* MBX mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ * Unlocks flag lock
+ */
+
+void mbx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ time_t tp[2];
+ struct stat sbuf;
+ unsigned long oldpid = LOCAL->lastpid;
+ /* make sure the update takes */
+ if (!stream->rdonly && LOCAL && (LOCAL->fd >= 0) && (LOCAL->ld >= 0)) {
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ /* we are the last flag updater */
+ LOCAL->lastpid = (unsigned long) getpid ();
+ /* update header if needed */
+ if (((LOCAL->ffuserflag < NUSERFLAGS) &&
+ stream->user_flags[LOCAL->ffuserflag]) || (oldpid != LOCAL->lastpid))
+ mbx_update_header (stream);
+ tp[0] = time (0); /* make sure read comes after all that */
+ utime (stream->mailbox,tp);
+ }
+ if (LOCAL->ld >= 0) { /* unlock now */
+ unlockfd (LOCAL->ld,LOCAL->lock);
+ LOCAL->ld = -1;
+ }
+}
+
+
+/* MBX mail per-message modify flags
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void mbx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ if (mbx_flaglock (stream)) mbx_update_status (stream,elt->msgno,NIL);
+}
+
+/* MBX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream still alive, NIL if not
+ */
+
+long mbx_ping (MAILSTREAM *stream)
+{
+ unsigned long i,pos;
+ long ret = NIL;
+ int ld;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ struct stat sbuf;
+ if (stream && LOCAL) { /* only if stream already open */
+ int snarf = stream->inbox && !stream->rdonly;
+ ret = LONGT; /* assume OK */
+ fstat (LOCAL->fd,&sbuf); /* get current file poop */
+ /* allow expunge if permitted at ping */
+ if (mail_parameters (NIL,GET_EXPUNGEATPING,NIL)) LOCAL->expok = T;
+ /* if external modification */
+ if (LOCAL->filetime && (LOCAL->filetime < sbuf.st_mtime))
+ LOCAL->flagcheck = T; /* upgrade to flag checking */
+ /* new mail or flagcheck handling needed? */
+ if (((sbuf.st_size - LOCAL->filesize) || LOCAL->flagcheck ||
+ !stream->nmsgs || snarf) &&
+ ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) >= 0)) {
+ /* reparse header if not flagchecking */
+ if (!LOCAL->flagcheck) ret = mbx_parse (stream);
+ /* sweep mailbox for changed message status */
+ else if (ret = mbx_parse (stream)) {
+ unsigned long recent = 0;
+ LOCAL->filetime = sbuf.st_mtime;
+ for (i = 1; i <= stream->nmsgs; )
+ if (elt = mbx_elt (stream,i,LOCAL->expok)) {
+ if (elt->recent) ++recent;
+ ++i;
+ }
+ mail_recent (stream,recent);
+ LOCAL->flagcheck = NIL; /* got all the updates */
+ }
+ /* always reparse header at least */
+ if (ret && snarf) { /* snarf new messages if still OK */
+ mbx_snarf (stream);
+ /* parse snarfed messages */
+ ret = mbx_parse (stream);
+ }
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ if (ret) { /* must still be alive */
+ if (!LOCAL->expunged) /* look for holes if none known yet */
+ for (i = 1, pos = HDRSIZE;
+ !LOCAL->expunged && (i <= stream->nmsgs);
+ i++, pos += elt->private.special.text.size + elt->rfc822_size)
+ if ((elt = mail_elt (stream,i))->private.special.offset != pos)
+ LOCAL->expunged = T;/* found a hole */
+ /* burp any holes */
+ if (LOCAL->expunged && !stream->rdonly) {
+ if (mbx_rewrite (stream,&i,NIL)) fatal ("expunge on check");
+ if (i) { /* any space reclaimed? */
+ LOCAL->expunged = NIL;/* no more pending expunge */
+ sprintf (LOCAL->buf,"Reclaimed %lu bytes of expunged space",i);
+ MM_LOG (LOCAL->buf,(long) NIL);
+ }
+ }
+ LOCAL->expok = NIL; /* no more expok */
+ }
+ }
+ return ret; /* return result of the parse */
+}
+
+/* MBX mail check mailbox (reparses status too)
+ * Accepts: MAIL stream
+ */
+
+void mbx_check (MAILSTREAM *stream)
+{
+ if (LOCAL) LOCAL->expok = T; /* mark that a check is desired */
+ if (mbx_ping (stream)) MM_LOG ("Check completed",(long) NIL);
+}
+
+
+/* MBX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T if success, NIL if failure
+ */
+
+long mbx_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ unsigned long nexp,reclaimed;
+ if (ret = sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) {
+ if (!mbx_ping (stream)); /* do nothing if stream dead */
+ else if (stream->rdonly) /* won't do on readonly files! */
+ MM_LOG ("Expunge ignored on readonly mailbox",WARN);
+ /* if expunged any messages */
+ else if (nexp = mbx_rewrite (stream,&reclaimed,sequence ? -1 : 1)) {
+ sprintf (LOCAL->buf,"Expunged %lu messages",nexp);
+ MM_LOG (LOCAL->buf,(long) NIL);
+ }
+ else if (reclaimed) { /* or if any prior expunged space reclaimed */
+ sprintf (LOCAL->buf,"Reclaimed %lu bytes of expunged space",reclaimed);
+ MM_LOG (LOCAL->buf,(long) NIL);
+ }
+ else MM_LOG ("No messages deleted, so no update needed",(long) NIL);
+ }
+ return ret;
+}
+
+/* MBX mail snarf messages from system inbox
+ * Accepts: MAIL stream, already locked
+ */
+
+void mbx_snarf (MAILSTREAM *stream)
+{
+ unsigned long i = 0;
+ unsigned long j,r,hdrlen,txtlen;
+ struct stat sbuf;
+ char *hdr,*txt,tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ MAILSTREAM *sysibx = NIL;
+ /* give up if can't get exclusive permission */
+ if ((time (0) >= (LOCAL->lastsnarf +
+ (long) mail_parameters (NIL,GET_SNARFINTERVAL,NIL))) &&
+ strcmp (sysinbox (),stream->mailbox)) {
+ MM_CRITICAL (stream); /* go critical */
+ /* sizes match and anything in sysinbox? */
+ if (!stat (sysinbox (),&sbuf) && sbuf.st_size &&
+ !fstat (LOCAL->fd,&sbuf) && (sbuf.st_size == LOCAL->filesize) &&
+ (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) &&
+ (!sysibx->rdonly) && (r = sysibx->nmsgs)) {
+ /* yes, go to end of file in our mailbox */
+ lseek (LOCAL->fd,sbuf.st_size,L_SET);
+ /* for each message in sysibx mailbox */
+ while (r && (++i <= sysibx->nmsgs)) {
+ /* snarf message from system INBOX */
+ hdr = cpystr (mail_fetchheader_full (sysibx,i,NIL,&hdrlen,NIL));
+ txt = mail_fetchtext_full (sysibx,i,&txtlen,FT_PEEK);
+ /* if have a message */
+ if (j = hdrlen + txtlen) {
+ /* build header line */
+ mail_date (LOCAL->buf,elt = mail_elt (sysibx,i));
+ sprintf (LOCAL->buf + strlen (LOCAL->buf),
+ ",%lu;00000000%04x-00000000\015\012",j,(unsigned)
+ ((fSEEN * elt->seen) +
+ (fDELETED * elt->deleted) + (fFLAGGED * elt->flagged) +
+ (fANSWERED * elt->answered) + (fDRAFT * elt->draft)));
+ /* copy message */
+ if ((write (LOCAL->fd,LOCAL->buf,strlen (LOCAL->buf)) < 0) ||
+ (write (LOCAL->fd,hdr,hdrlen) < 0) ||
+ (write (LOCAL->fd,txt,txtlen) < 0)) r = 0;
+ }
+ fs_give ((void **) &hdr);
+ }
+
+ /* make sure all the updates take */
+ if (fsync (LOCAL->fd)) r = 0;
+ if (r) { /* delete all the messages we copied */
+ if (r == 1) strcpy (tmp,"1");
+ else sprintf (tmp,"1:%lu",r);
+ mail_setflag (sysibx,tmp,"\\Deleted");
+ mail_expunge (sysibx); /* now expunge all those messages */
+ }
+ else {
+ sprintf (LOCAL->buf,"Can't copy new mail: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,WARN);
+ ftruncate (LOCAL->fd,sbuf.st_size);
+ }
+ fstat (LOCAL->fd,&sbuf); /* yes, get current file size */
+ LOCAL->filetime = sbuf.st_mtime;
+ }
+ if (sysibx) mail_close (sysibx);
+ MM_NOCRITICAL (stream); /* release critical */
+ LOCAL->lastsnarf = time (0);/* note time of last snarf */
+ }
+}
+
+/* MBX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if success, NIL if failed
+ */
+
+long mbx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ time_t tp[2];
+ MESSAGECACHE *elt;
+ unsigned long i,j,k,m;
+ long ret = LONGT;
+ int fd,ld;
+ char *s,*t,file[MAILTMPLEN],lock[MAILTMPLEN];
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ copyuid_t cu = (copyuid_t) mail_parameters (NIL,GET_COPYUID,NIL);
+ SEARCHSET *source = cu ? mail_newsearchset () : NIL;
+ SEARCHSET *dest = cu ? mail_newsearchset () : NIL;
+ MAILSTREAM *dstream = NIL;
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* make sure valid mailbox */
+ if ((fd = mbx_isvalid (&dstream,mailbox,file,&ld,lock,
+ cu ? MBXISVALIDUID : MBXISVALIDNOUID)) < 0)
+ switch (errno) {
+ case ENOENT: /* no such file? */
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid MBX-format mailbox name: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a MBX-format mailbox: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ MM_CRITICAL (stream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
+
+ /* for each requested message */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.special.text.size,L_SET);
+ mail_date(LOCAL->buf,elt);/* build target header */
+ /* get target keyword mask */
+ for (j = elt->user_flags, k = 0; j; )
+ if (s = stream->user_flags[find_rightmost_bit (&j)])
+ for (m = 0; (m < NUSERFLAGS) && (t = dstream->user_flags[m]); m++)
+ if (!compare_cstring (s,t) && (k |= 1 << m)) break;
+ sprintf (LOCAL->buf+strlen(LOCAL->buf),",%lu;%08lx%04x-%08lx\015\012",
+ elt->rfc822_size,k,(unsigned)
+ ((fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)),cu ? ++dstream->uid_last : 0);
+ /* write target header */
+ if (ret = (write (fd,LOCAL->buf,strlen (LOCAL->buf)) > 0)) {
+ for (k = elt->rfc822_size; ret && (j = min (k,LOCAL->buflen)); k -= j){
+ read (LOCAL->fd,LOCAL->buf,j);
+ ret = write (fd,LOCAL->buf,j) >= 0;
+ }
+ if (cu) { /* need to pass back new UID? */
+ mail_append_set (source,mail_uid (stream,i));
+ mail_append_set (dest,dstream->uid_last);
+ }
+ }
+ }
+
+ /* make sure all the updates take */
+ if (!(ret && (ret = !fsync (fd)))) {
+ sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ }
+ if (cu && ret) { /* return sets if doing COPYUID */
+ (*cu) (stream,mailbox,dstream->uid_validity,source,dest);
+ lseek (fd,15,L_SET); /* update UIDLAST */
+ sprintf (LOCAL->buf,"%08lx",dstream->uid_last);
+ write (fd,LOCAL->buf,8);
+ }
+ else { /* flush any sets we may have built */
+ mail_free_searchset (&source);
+ mail_free_searchset (&dest);
+ }
+ if (ret) tp[0] = time (0) - 1;/* set atime to now-1 if successful copy */
+ /* else preserve \Marked status */
+ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0);
+ tp[1] = sbuf.st_mtime; /* preserve mtime */
+ utime (file,tp); /* set the times */
+ close (fd); /* close the file */
+ MM_NOCRITICAL (stream); /* release critical */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ /* delete all requested messages */
+ if (ret && (options & CP_MOVE) && mbx_flaglock (stream)) {
+ for (i = 1; i <= stream->nmsgs; i++) if (mail_elt (stream,i)->sequence) {
+ /* mark message deleted */
+ mbx_elt (stream,i,NIL)->deleted = T;
+ /* recalculate status */
+ mbx_update_status (stream,i,NIL);
+ }
+ /* update flags */
+ mbx_flag (stream,NIL,NIL,NIL);
+ }
+ if (dstream != stream) mail_close (dstream);
+ return ret;
+}
+
+/* MBX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mbx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd,ld;
+ char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ time_t tp[2];
+ FILE *df;
+ MESSAGECACHE elt;
+ long f;
+ unsigned long i,uf;
+ STRING *message;
+ long ret = NIL;
+ MAILSTREAM *dstream = NIL;
+ appenduid_t au = (appenduid_t) mail_parameters (NIL,GET_APPENDUID,NIL);
+ SEARCHSET *dst = au ? mail_newsearchset () : NIL;
+ /* make sure valid mailbox */
+ if ((fd = mbx_isvalid (&dstream,mailbox,file,&ld,lock,
+ au ? MBXISVALIDUID : MBXISVALIDNOUID)) < 0)
+ switch (errno) {
+ case ENOENT: /* no such file? */
+ if (compare_cstring (mailbox,"INBOX")) {
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* can create INBOX here */
+ mbx_create (dstream = stream ? stream : user_flags (&mbxproto),"INBOX");
+ if ((fd = mbx_isvalid (&dstream,mailbox,file,&ld,lock,
+ au ? MBXISVALIDUID : MBXISVALIDNOUID)) >= 0)
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid MBX-format mailbox name: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a MBX-format mailbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+
+ /* get first message */
+ if (!MM_APPEND (af) (dstream,data,&flags,&date,&message)) close (fd);
+ else if (!(df = fdopen (fd,"r+b"))) {
+ MM_LOG ("Unable to reopen append mailbox",ERROR);
+ close (fd);
+ }
+ else {
+ MM_CRITICAL (dstream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ fseek (df,sbuf.st_size,SEEK_SET);
+ errno = 0;
+ for (ret = LONGT; ret && message; ) {
+ if (!SIZE (message)) { /* guard against zero-length */
+ MM_LOG ("Append of zero-length message",ERROR);
+ ret = NIL;
+ break;
+ }
+ f = mail_parse_flags (dstream,flags,&uf);
+ if (date) { /* parse date if given */
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* mark failure */
+ break;
+ }
+ mail_date (tmp,&elt); /* write preseved date */
+ }
+ else internal_date (tmp); /* get current date in IMAP format */
+ /* write header */
+ if (fprintf (df,"%s,%lu;%08lx%04lx-%08lx\015\012",tmp,i = SIZE (message),
+ uf,(unsigned long) f,au ? ++dstream->uid_last : 0) < 0)
+ ret = NIL;
+ else { /* write message */
+ size_t j;
+ if (!message->cursize) SETPOS (message,GETPOS (message));
+ while (i && (j = fwrite (message->curpos,1,message->cursize,df))) {
+ i -= j;
+ SETPOS (message,GETPOS (message) + j);
+ }
+ /* get next message */
+ if (i || !MM_APPEND (af) (dstream,data,&flags,&date,&message))
+ ret = NIL;
+ else if (au) mail_append_set (dst,dstream->uid_last);
+ }
+ }
+
+ /* if error... */
+ if (!ret || (fflush (df) == EOF)) {
+ /* revert file */
+ ftruncate (fd,sbuf.st_size);
+ close (fd); /* make sure fclose() doesn't corrupt us */
+ if (errno) {
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ }
+ ret = NIL;
+ }
+ if (au && ret) { /* return sets if doing APPENDUID */
+ (*au) (mailbox,dstream->uid_validity,dst);
+ fseek (df,15,SEEK_SET); /* update UIDLAST */
+ fprintf (df,"%08lx",dstream->uid_last);
+ }
+ else mail_free_searchset (&dst);
+ /* set atime to now-1 if successful copy */
+ if (ret) tp[0] = time (0) - 1;
+ /* else preserve \Marked status */
+ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0);
+ tp[1] = sbuf.st_mtime; /* preserve mtime */
+ utime (file,tp); /* set the times */
+ fclose (df); /* close the file */
+ MM_NOCRITICAL (dstream); /* release critical */
+ }
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ if (dstream != stream) mail_close (dstream);
+ return ret;
+}
+
+/* Internal routines */
+
+
+/* MBX mail generate file string
+ * Accepts: temporary buffer to write into
+ * mailbox name string
+ * Returns: local file string or NIL if failure
+ */
+
+char *mbx_file (char *dst,char *name)
+{
+ char *s = mailboxfile (dst,name);
+ return (s && !*s) ? mailboxfile (dst,"~/INBOX") : s;
+}
+
+/* MBX mail parse mailbox
+ * Accepts: MAIL stream
+ * Returns: T if parse OK
+ * NIL if failure, stream aborted
+ */
+
+long mbx_parse (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = NIL;
+ unsigned char c,*s,*t,*x;
+ char tmp[MAILTMPLEN];
+ unsigned long i,j,k,m;
+ off_t curpos = LOCAL->filesize;
+ unsigned long nmsgs = stream->nmsgs;
+ unsigned long recent = stream->recent;
+ unsigned long lastuid = 0;
+ short dirty = NIL;
+ short added = NIL;
+ short silent = stream->silent;
+ short uidwarn = T;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ if (sbuf.st_size < curpos) { /* sanity check */
+ sprintf (tmp,"Mailbox shrank from %lu to %lu!",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size);
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ lseek (LOCAL->fd,0,L_SET); /* rewind file */
+ /* read internal header */
+ read (LOCAL->fd,LOCAL->buf,HDRSIZE);
+ LOCAL->buf[HDRSIZE] = '\0'; /* tie off header */
+ c = LOCAL->buf[15]; /* save first character of last UID */
+ LOCAL->buf[15] = '\0';
+ /* parse UID validity */
+ stream->uid_validity = strtoul (LOCAL->buf + 7,NIL,16);
+ LOCAL->buf[15] = c; /* restore first character of last UID */
+ /* parse last UID */
+ i = strtoul (LOCAL->buf + 15,NIL,16);
+ stream->uid_last = stream->rdonly ? max (i,stream->uid_last) : i;
+ /* parse user flags */
+ for (i = 0, s = LOCAL->buf + 25;
+ (i < NUSERFLAGS) && (t = strchr (s,'\015')) && (t - s);
+ i++, s = t + 2) {
+ *t = '\0'; /* tie off flag */
+ if (!stream->user_flags[i] && (strlen (s) <= MAXUSERFLAG))
+ stream->user_flags[i] = cpystr (s);
+ }
+ LOCAL->ffuserflag = (int) i; /* first free user flag */
+
+ /* get current last flag updater PID */
+ i = (isxdigit (LOCAL->buf[HDRSIZE-10]) && isxdigit (LOCAL->buf[HDRSIZE-9]) &&
+ isxdigit (LOCAL->buf[HDRSIZE-8]) && isxdigit (LOCAL->buf[HDRSIZE-7]) &&
+ isxdigit (LOCAL->buf[HDRSIZE-6]) && isxdigit (LOCAL->buf[HDRSIZE-5]) &&
+ isxdigit (LOCAL->buf[HDRSIZE-4]) && isxdigit (LOCAL->buf[HDRSIZE-3]) &&
+ (LOCAL->buf[HDRSIZE-2] == '\015') && (LOCAL->buf[HDRSIZE-1] == '\012'))?
+ strtoul (LOCAL->buf + HDRSIZE - 8,NIL,16) : 0;
+ /* set flagcheck if lastpid changed */
+ if (LOCAL->lastpid && (LOCAL->lastpid != i)) LOCAL->flagcheck = T;
+ LOCAL->lastpid = i; /* set as last PID */
+ stream->silent = T; /* don't pass up exists events yet */
+ while (sbuf.st_size - curpos){/* while there is stuff to parse */
+ /* get to that position in the file */
+ lseek (LOCAL->fd,curpos,L_SET);
+ if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
+ sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size,
+ i ? strerror (errno) : "no data read");
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ LOCAL->buf[i] = '\0'; /* tie off buffer just in case */
+ if (!((s = strchr (LOCAL->buf,'\015')) && (s[1] == '\012'))) {
+ sprintf (tmp,"Unable to find CRLF at %lu in %lu bytes, text: %.80s",
+ (unsigned long) curpos,i,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ *s = '\0'; /* tie off header line */
+ i = (s + 2) - LOCAL->buf; /* note start of text offset */
+ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
+ sprintf (tmp,"Unable to parse internal header at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ if (!(isxdigit (t[1]) && isxdigit (t[2]) && isxdigit (t[3]) &&
+ isxdigit (t[4]) && isxdigit (t[5]) && isxdigit (t[6]) &&
+ isxdigit (t[7]) && isxdigit (t[8]) && isxdigit (t[9]) &&
+ isxdigit (t[10]) && isxdigit (t[11]) && isxdigit (t[12]))) {
+ sprintf (tmp,"Unable to parse message flags at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ if ((t[13] != '-') || t[22] ||
+ !(isxdigit (t[14]) && isxdigit (t[15]) && isxdigit (t[16]) &&
+ isxdigit (t[17]) && isxdigit (t[18]) && isxdigit (t[19]) &&
+ isxdigit (t[20]) && isxdigit (t[21]))) {
+ sprintf (tmp,"Unable to parse message UID at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+
+ *s++ = '\0'; *t++ = '\0'; /* break up fields */
+ /* get message size */
+ if (!(j = strtoul (s,(char **) &x,10)) && (!(x && *x))) {
+ sprintf (tmp,"Unable to parse message size at %lu: %.80s,%.80s;%.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf,(char *) s,
+ (char *) t);
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ /* make sure didn't run off end of file */
+ if (((off_t) (curpos + i + j)) > sbuf.st_size) {
+ sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)",
+ (unsigned long) curpos,(unsigned long) (curpos + i + j),
+ (unsigned long) sbuf.st_size);
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ /* parse UID */
+ if ((m = strtoul (t+13,NIL,16)) &&
+ ((m <= lastuid) || (m > stream->uid_last))) {
+ if (uidwarn) {
+ sprintf (tmp,"Invalid UID %08lx in message %lu, rebuilding UIDs",
+ m,nmsgs+1);
+ MM_LOG (tmp,WARN);
+ uidwarn = NIL;
+ /* restart UID validity */
+ stream->uid_validity = time (0);
+ }
+ m = 0; /* lose this UID */
+ dirty = T; /* mark dirty, set new lastuid */
+ stream->uid_last = lastuid;
+ }
+
+ t[12] = '\0'; /* parse system flags */
+ if ((k = strtoul (t+8,NIL,16)) & fEXPUNGED) {
+ if (m) lastuid = m; /* expunge message, update last UID seen */
+ else { /* no UID assigned? */
+ lastuid = ++stream->uid_last;
+ dirty = T;
+ }
+ }
+ else { /* not expunged, swell the cache */
+ added = T; /* note that a new message was added */
+ mail_exists (stream,++nmsgs);
+ /* instantiate an elt for this message */
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ /* parse the date */
+ if (!mail_parse_date (elt,LOCAL->buf)) {
+ sprintf (tmp,"Unable to parse message date at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ /* note file offset of header */
+ elt->private.special.offset = curpos;
+ /* and internal header size */
+ elt->private.special.text.size = i;
+ /* header size not known yet */
+ elt->private.msg.header.text.size = 0;
+ elt->rfc822_size = j; /* note message size */
+ /* calculate system flags */
+ if (k & fSEEN) elt->seen = T;
+ if (k & fDELETED) elt->deleted = T;
+ if (k & fFLAGGED) elt->flagged = T;
+ if (k & fANSWERED) elt->answered = T;
+ if (k & fDRAFT) elt->draft = T;
+ t[8] = '\0'; /* get user flags value */
+ elt->user_flags = strtoul (t,NIL,16);
+ /* UID already assigned? */
+ if (!(elt->private.uid = m) || !(k & fOLD)) {
+ elt->recent = T; /* no, mark as recent */
+ ++recent; /* count up a new recent message */
+ dirty = T; /* and must rewrite header */
+ /* assign new UID */
+ if (!elt->private.uid) elt->private.uid = ++stream->uid_last;
+ mbx_update_status (stream,elt->msgno,NIL);
+ }
+ /* update last parsed UID */
+ lastuid = elt->private.uid;
+ }
+ curpos += i + j; /* update position */
+ }
+
+ if (dirty && !stream->rdonly){/* update header */
+ mbx_update_header (stream);
+ fsync (LOCAL->fd); /* make sure all the UID updates take */
+ }
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */
+ LOCAL->filetime = sbuf.st_mtime;
+ if (added && !stream->rdonly){/* make sure atime updated */
+ time_t tp[2];
+ tp[0] = time (0);
+ tp[1] = LOCAL->filetime;
+ utime (stream->mailbox,tp);
+ }
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
+ mail_recent (stream,recent); /* and of change in recent messages */
+ return LONGT; /* return the winnage */
+}
+
+/* MBX get cache element with status updating from file
+ * Accepts: MAIL stream
+ * message number
+ * expunge OK flag
+ * Returns: cache element
+ */
+
+MESSAGECACHE *mbx_elt (MAILSTREAM *stream,unsigned long msgno,long expok)
+{
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ struct { /* old flags */
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged;
+ old.answered = elt->answered; old.draft = elt->draft;
+ old.user_flags = elt->user_flags;
+ /* get new flags */
+ if (mbx_read_flags (stream,elt) && expok) {
+ mail_expunged (stream,elt->msgno);
+ return NIL; /* return this message was expunged */
+ }
+ if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
+ (old.flagged != elt->flagged) || (old.answered != elt->answered) ||
+ (old.draft != elt->draft) || (old.user_flags != elt->user_flags))
+ MM_FLAGS (stream,msgno); /* let top level know */
+ return elt;
+}
+
+/* MBX read flags from file
+ * Accepts: MAIL stream
+ * cache element
+ * Returns: non-NIL if message expunged
+ */
+
+unsigned long mbx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ unsigned long i;
+ struct stat sbuf;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ /* make sure file size is good */
+ if (sbuf.st_size < LOCAL->filesize) {
+ sprintf (LOCAL->buf,"Mailbox shrank from %lu to %lu in flag read!",
+ (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size);
+ fatal (LOCAL->buf);
+ }
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 24,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,14) < 0) {
+ sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ if ((LOCAL->buf[0] != ';') || (LOCAL->buf[13] != '-')) {
+ LOCAL->buf[14] = '\0'; /* tie off buffer for error message */
+ sprintf (LOCAL->buf+50,"Invalid flags for message %lu (%lu %lu): %s",
+ elt->msgno,elt->private.special.offset,
+ elt->private.special.text.size,(char *) LOCAL->buf);
+ fatal (LOCAL->buf+50);
+ }
+ LOCAL->buf[13] = '\0'; /* tie off buffer */
+ /* calculate system flags */
+ i = strtoul (LOCAL->buf+9,NIL,16);
+ elt->seen = i & fSEEN ? T : NIL;
+ elt->deleted = i & fDELETED ? T : NIL;
+ elt->flagged = i & fFLAGGED ? T : NIL;
+ elt->answered = i & fANSWERED ? T : NIL;
+ elt->draft = i & fDRAFT ? T : NIL;
+ LOCAL->expunged |= i & fEXPUNGED ? T : NIL;
+ LOCAL->buf[9] = '\0'; /* tie off flags */
+ /* get user flags value */
+ elt->user_flags = strtoul (LOCAL->buf+1,NIL,16);
+ elt->valid = T; /* have valid flags now */
+ return i & fEXPUNGED;
+}
+
+/* MBX update header
+ * Accepts: MAIL stream
+ */
+
+#ifndef CYGKLUDGEOFFSET
+#define CYGKLUDGEOFFSET 0
+#endif
+
+void mbx_update_header (MAILSTREAM *stream)
+{
+ int i;
+ char *s = LOCAL->buf;
+ memset (s,'\0',HDRSIZE); /* initialize header */
+ sprintf (s,"*mbx*\015\012%08lx%08lx\015\012",
+ stream->uid_validity,stream->uid_last);
+ for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i]; ++i)
+ sprintf (s += strlen (s),"%s\015\012",stream->user_flags[i]);
+ LOCAL->ffuserflag = i; /* first free user flag */
+ /* can we create more user flags? */
+ stream->kwd_create = (i < NUSERFLAGS) ? T : NIL;
+ /* write reserved lines */
+ while (i++ < NUSERFLAGS) strcat (s,"\015\012");
+ sprintf (LOCAL->buf + HDRSIZE - 10,"%08lx\015\012",LOCAL->lastpid);
+ while (T) { /* rewind file */
+ lseek (LOCAL->fd,CYGKLUDGEOFFSET,L_SET);
+ /* write new header */
+ if (write (LOCAL->fd,LOCAL->buf + CYGKLUDGEOFFSET,
+ HDRSIZE - CYGKLUDGEOFFSET) > 0) break;
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+}
+
+/* MBX update status string
+ * Accepts: MAIL stream
+ * message number
+ * flags
+ */
+
+void mbx_update_status (MAILSTREAM *stream,unsigned long msgno,long flags)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ /* readonly */
+ if (stream->rdonly || !elt->valid) mbx_read_flags (stream,elt);
+ else { /* readwrite */
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ /* make sure file size is good */
+ if (sbuf.st_size < LOCAL->filesize) {
+ sprintf (LOCAL->buf,"Mailbox shrank from %lu to %lu in flag update!",
+ (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size);
+ fatal (LOCAL->buf);
+ }
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 24,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,14) < 0) {
+ sprintf (LOCAL->buf,"Unable to read old status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ if ((LOCAL->buf[0] != ';') || (LOCAL->buf[13] != '-')) {
+ LOCAL->buf[14] = '\0'; /* tie off buffer for error message */
+ sprintf (LOCAL->buf+50,"Invalid flags for message %lu (%lu %lu): %s",
+ elt->msgno,elt->private.special.offset,
+ elt->private.special.text.size,(char *) LOCAL->buf);
+ fatal (LOCAL->buf+50);
+ }
+ /* print new flag string */
+ sprintf (LOCAL->buf,"%08lx%04x-%08lx",elt->user_flags,(unsigned)
+ (((elt->deleted && flags) ?
+ fEXPUNGED : (strtoul (LOCAL->buf+9,NIL,16)) & fEXPUNGED) +
+ (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft) + fOLD),elt->private.uid);
+ while (T) { /* get to that place in the file */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 23,L_SET);
+ /* write new flags and UID */
+ if (write (LOCAL->fd,LOCAL->buf,21) > 0) break;
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+ }
+}
+
+/* MBX locate header for a message
+ * Accepts: MAIL stream
+ * message number
+ * pointer to returned header size
+ * pointer to possible returned header
+ * Returns: position of header in file
+ */
+
+#define HDRBUFLEN 16384 /* good enough for most headers */
+#define SLOP 4 /* CR LF CR LF */
+
+unsigned long mbx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size,char **hdr)
+{
+ unsigned long siz,done;
+ long i;
+ unsigned char *s,*t,*te;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ unsigned long ret = elt->private.special.offset +
+ elt->private.special.text.size;
+ if (hdr) *hdr = NIL; /* assume no header returned */
+ /* is header size known? */
+ if (*size = elt->private.msg.header.text.size) return ret;
+ /* paranoia check */
+ if (LOCAL->buflen < (HDRBUFLEN + SLOP))
+ fatal ("LOCAL->buf smaller than HDRBUFLEN");
+ lseek (LOCAL->fd,ret,L_SET); /* get to header position */
+ /* read HDRBUFLEN chunks with 4 byte slop */
+ for (done = siz = 0, s = LOCAL->buf;
+ (i = min ((long) (elt->rfc822_size - done),(long) HDRBUFLEN)) &&
+ (read (LOCAL->fd,s,i) == i);
+ done += i, siz += (t - LOCAL->buf) - SLOP, s = LOCAL->buf + SLOP) {
+ te = (t = s + i) - 12; /* calculate end of fast scan */
+ /* fast scan for CR */
+ for (s = LOCAL->buf; s < te;)
+ if (((*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') ||
+ (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') ||
+ (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') ||
+ (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015')) &&
+ (*s == '\012') && (*++s == '\015') && (*++s == '\012')) {
+ *size = elt->private.msg.header.text.size = siz + (++s - LOCAL->buf);
+ if (hdr) *hdr = LOCAL->buf;
+ return ret;
+ }
+ for (te = t - 3; (s < te);) /* final character-at-a-time scan */
+ if ((*s++ == '\015') && (*s == '\012') && (*++s == '\015') &&
+ (*++s == '\012')) {
+ *size = elt->private.msg.header.text.size = siz + (++s - LOCAL->buf);
+ if (hdr) *hdr = LOCAL->buf;
+ return ret;
+ }
+ if (i <= SLOP) break; /* end of data */
+ /* slide over last 4 bytes */
+ memmove (LOCAL->buf,t - SLOP,SLOP);
+ hdr = NIL; /* can't return header this way */
+ }
+ /* not found: header consumes entire message */
+ elt->private.msg.header.text.size = *size = elt->rfc822_size;
+ if (hdr) *hdr = LOCAL->buf; /* possibly return header too */
+ return ret;
+}
+
+/* MBX mail rewrite mailbox
+ * Accepts: MAIL stream
+ * pointer to return reclaimed size
+ * flags (0 = no expunge, 1 = expunge deleted, -1 = expunge sequence)
+ * Returns: number of expunged messages
+ */
+
+unsigned long mbx_rewrite (MAILSTREAM *stream,unsigned long *reclaimed,
+ long flags)
+{
+ time_t tp[2];
+ struct stat sbuf;
+ off_t pos,ppos;
+ int ld;
+ unsigned long i,j,k,m,delta;
+ unsigned long n = *reclaimed = 0;
+ unsigned long recent = 0;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ /* The cretins who designed flock() created a window of vulnerability in
+ * upgrading locks from shared to exclusive or downgrading from exclusive
+ * to shared. Rather than maintain the lock at shared status at a minimum,
+ * flock() actually *releases* the former lock. Obviously they never talked
+ * to any database guys. Fortunately, we have the parse/append permission
+ * lock. If we require this lock before going exclusive on the mailbox,
+ * another process can not sneak in and steal the exclusive mailbox lock on
+ * us, because it will block on trying to get parse/append permission first.
+ */
+ /* get parse/append permission */
+ if ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) < 0) {
+ MM_LOG ("Unable to lock mailbox for rewrite",ERROR);
+ return 0;
+ }
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime && !LOCAL->flagcheck &&
+ (LOCAL->filetime < sbuf.st_mtime)) LOCAL->flagcheck = T;
+ if (!mbx_parse (stream)) { /* make sure see any newly-arrived messages */
+ unlockfd (ld,lock); /* failed?? */
+ return 0;
+ }
+ if (LOCAL->flagcheck) { /* sweep flags if need flagcheck */
+ LOCAL->filetime = sbuf.st_mtime;
+ for (i = 1; i <= stream->nmsgs; ++i) mbx_elt (stream,i,NIL);
+ LOCAL->flagcheck = NIL;
+ }
+
+ /* get exclusive access */
+ if (!flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
+ MM_CRITICAL (stream); /* go critical */
+ for (i = 1,delta = 0,pos = ppos = HDRSIZE; i <= stream->nmsgs; ) {
+ /* note if message not at predicted location */
+ if (m = (elt = mbx_elt (stream,i,NIL))->private.special.offset - ppos) {
+ ppos = elt->private.special.offset;
+ *reclaimed += m; /* note reclaimed message space */
+ delta += m; /* and as expunge delta */
+ }
+ /* number of bytes to smash or preserve */
+ ppos += (k = elt->private.special.text.size + elt->rfc822_size);
+ /* if need to expunge this message*/
+ if (flags && elt->deleted && ((flags > 0) || elt->sequence)) {
+ delta += k; /* number of bytes to delete */
+ mail_expunged(stream,i);/* notify upper levels */
+ n++; /* count up one more expunged message */
+ }
+ else { /* preserved message */
+ i++; /* count this message */
+ if (elt->recent) ++recent;
+ if (delta) { /* moved, note first byte to preserve */
+ j = elt->private.special.offset;
+ do { /* read from source position */
+ m = min (k,LOCAL->buflen);
+ lseek (LOCAL->fd,j,L_SET);
+ read (LOCAL->fd,LOCAL->buf,m);
+ pos = j - delta; /* write to destination position */
+ while (T) {
+ lseek (LOCAL->fd,pos,L_SET);
+ if (write (LOCAL->fd,LOCAL->buf,m) > 0) break;
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+ pos += m; /* new position */
+ j += m; /* next chunk, perhaps */
+ } while (k -= m); /* until done */
+ /* note the new address of this text */
+ elt->private.special.offset -= delta;
+ }
+ /* preserved but no deleted messages yet */
+ else pos = elt->private.special.offset + k;
+ }
+ }
+ /* deltaed file size match position? */
+ if (m = (LOCAL->filesize -= delta) - pos) {
+ *reclaimed += m; /* probably an fEXPUNGED msg */
+ LOCAL->filesize = pos; /* set correct size */
+ }
+ /* truncate file after last message */
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ fsync (LOCAL->fd); /* force disk update */
+ MM_NOCRITICAL (stream); /* release critical */
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH); /* allow sharers again */
+ (*bn) (BLOCK_NONE,NIL);
+ }
+
+ else { /* can't get exclusive */
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH); /* recover previous shared mailbox lock */
+ (*bn) (BLOCK_NONE,NIL);
+ /* do hide-expunge when shared */
+ if (flags) for (i = 1; i <= stream->nmsgs; ) {
+ if (elt = mbx_elt (stream,i,T)) {
+ /* make the message invisible */
+ if (elt->deleted && ((flags > 0) || elt->sequence)) {
+ mbx_update_status (stream,elt->msgno,LONGT);
+ /* notify upper levels */
+ mail_expunged (stream,i);
+ n++; /* count up one more expunged message */
+ }
+ else {
+ i++; /* preserved message */
+ if (elt->recent) ++recent;
+ }
+ }
+ else n++; /* count up one more expunged message */
+ }
+ fsync (LOCAL->fd); /* force disk update */
+ }
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* reset atime to now */
+ utime (stream->mailbox,tp);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ return n; /* return number of expunged messages */
+}
+
+/* MBX mail lock for flag updating
+ * Accepts: stream
+ * Returns: T if successful, NIL if failure
+ */
+
+long mbx_flaglock (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ unsigned long i;
+ int ld;
+ char lock[MAILTMPLEN];
+ /* no-op if readonly or already locked */
+ if (!stream->rdonly && LOCAL && (LOCAL->fd >= 0) && (LOCAL->ld < 0)) {
+ /* lock now */
+ if ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) < 0) return NIL;
+ if (!LOCAL->flagcheck) { /* don't do this if flagcheck already needed */
+ if (LOCAL->filetime) { /* know previous time? */
+ fstat (LOCAL->fd,&sbuf);/* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->flagcheck = T;
+ LOCAL->filetime = 0; /* don't do this test for any other messages */
+ }
+ if (!mbx_parse (stream)) {/* parse mailbox */
+ unlockfd (ld,lock); /* shouldn't happen */
+ return NIL;
+ }
+ if (LOCAL->flagcheck) /* invalidate cache if flagcheck */
+ for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->valid = NIL;
+ }
+ LOCAL->ld = ld; /* copy to stream for subsequent calls */
+ memcpy (LOCAL->lock,lock,MAILTMPLEN);
+ }
+ return LONGT;
+}
diff --git a/imap/src/osdep/unix/mh.c b/imap/src/osdep/unix/mh.c
new file mode 100644
index 00000000..0226b7af
--- /dev/null
+++ b/imap/src/osdep/unix/mh.c
@@ -0,0 +1,1283 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MH mail routines
+ *
+ * Author(s): Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 23 February 1992
+ * Last Edited: 11 October 2007
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "misc.h"
+#include "dummy.h"
+#include "fdstring.h"
+
+
+/* Build parameters */
+
+#define MHINBOX "#mhinbox" /* corresponds to namespace in env_unix.c */
+#define MHINBOXDIR "inbox"
+#define MHPROFILE ".mh_profile"
+#define MHCOMMA ','
+#define MHSEQUENCE ".mh_sequence"
+#define MHSEQUENCES ".mh_sequences"
+#define MHPATH "Mail"
+
+
+/* mh_load_message() flags */
+
+#define MLM_HEADER 0x1 /* load message text */
+#define MLM_TEXT 0x2 /* load message text */
+
+/* MH I/O stream local data */
+
+typedef struct mh_local {
+ char *dir; /* spool directory name */
+ unsigned char buf[CHUNKSIZE]; /* temporary buffer */
+ unsigned long cachedtexts; /* total size of all cached texts */
+ time_t scantime; /* last time directory scanned */
+} MHLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MHLOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *mh_valid (char *name);
+int mh_isvalid (char *name,char *tmp,long synonly);
+int mh_namevalid (char *name);
+char *mh_path (char *tmp);
+void *mh_parameters (long function,void *value);
+long mh_dirfmttest (char *name);
+void mh_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void mh_list (MAILSTREAM *stream,char *ref,char *pat);
+void mh_lsub (MAILSTREAM *stream,char *ref,char *pat);
+void mh_list_work (MAILSTREAM *stream,char *dir,char *pat,long level);
+long mh_subscribe (MAILSTREAM *stream,char *mailbox);
+long mh_unsubscribe (MAILSTREAM *stream,char *mailbox);
+long mh_create (MAILSTREAM *stream,char *mailbox);
+long mh_delete (MAILSTREAM *stream,char *mailbox);
+long mh_rename (MAILSTREAM *stream,char *old,char *newname);
+MAILSTREAM *mh_open (MAILSTREAM *stream);
+void mh_close (MAILSTREAM *stream,long options);
+void mh_fast (MAILSTREAM *stream,char *sequence,long flags);
+void mh_load_message (MAILSTREAM *stream,unsigned long msgno,long flags);
+char *mh_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags);
+long mh_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+long mh_ping (MAILSTREAM *stream);
+void mh_check (MAILSTREAM *stream);
+long mh_expunge (MAILSTREAM *stream,char *sequence,long options);
+long mh_copy (MAILSTREAM *stream,char *sequence,char *mailbox,
+ long options);
+long mh_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+int mh_select (struct direct *name);
+int mh_numsort (const void *d1,const void *d2);
+char *mh_file (char *dst,char *name);
+long mh_canonicalize (char *pattern,char *ref,char *pat);
+void mh_setdate (char *file,MESSAGECACHE *elt);
+
+/* MH mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mhdriver = {
+ "mh", /* driver name */
+ /* driver flags */
+ DR_MAIL|DR_LOCAL|DR_NOFAST|DR_NAMESPACE|DR_NOSTICKY|DR_DIRFMT,
+ (DRIVER *) NIL, /* next driver */
+ mh_valid, /* mailbox is valid for us */
+ mh_parameters, /* manipulate parameters */
+ mh_scan, /* scan mailboxes */
+ mh_list, /* find mailboxes */
+ mh_lsub, /* find subscribed mailboxes */
+ mh_subscribe, /* subscribe to mailbox */
+ mh_unsubscribe, /* unsubscribe from mailbox */
+ mh_create, /* create mailbox */
+ mh_delete, /* delete mailbox */
+ mh_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ mh_open, /* open mailbox */
+ mh_close, /* close mailbox */
+ mh_fast, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mh_header, /* fetch message header */
+ mh_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ NIL, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mh_ping, /* ping mailbox to see if still alive */
+ mh_check, /* check for new messages */
+ mh_expunge, /* expunge deleted messages */
+ mh_copy, /* copy messages to another mailbox */
+ mh_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mhproto = {&mhdriver};
+
+
+static char *mh_profile = NIL; /* holds MH profile */
+static char *mh_pathname = NIL; /* holds MH path name */
+static long mh_once = 0; /* already snarled once */
+static long mh_allow_inbox =NIL;/* allow INBOX as well as MHINBOX */
+
+/* MH mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mh_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return mh_isvalid (name,tmp,T) ? &mhdriver : NIL;
+}
+
+
+/* MH mail test for valid mailbox
+ * Accepts: mailbox name
+ * temporary buffer to use
+ * syntax only test flag
+ * Returns: T if valid, NIL otherwise
+ */
+
+int mh_isvalid (char *name,char *tmp,long synonly)
+{
+ struct stat sbuf;
+ char *s,*t,altname[MAILTMPLEN];
+ unsigned long i;
+ int ret = NIL;
+ errno = NIL; /* zap any error condition */
+ /* mh name? */
+ if ((mh_allow_inbox && !compare_cstring (name,"INBOX")) ||
+ !compare_cstring (name,MHINBOX) ||
+ ((name[0] == '#') && ((name[1] == 'm') || (name[1] == 'M')) &&
+ ((name[2] == 'h') || (name[2] == 'H')) && (name[3] == '/') && name[4])){
+ if (mh_path (tmp)) /* validate name if INBOX or not synonly */
+ ret = (synonly && compare_cstring (name,"INBOX")) ?
+ T : ((stat (mh_file (tmp,name),&sbuf) == 0) &&
+ (sbuf.st_mode & S_IFMT) == S_IFDIR);
+ else if (!mh_once++) { /* only report error once */
+ sprintf (tmp,"%.900s not found, mh format names disabled",mh_profile);
+ mm_log (tmp,WARN);
+ }
+ }
+ /* see if non-NS name within mh hierarchy */
+ else if ((name[0] != '#') && (s = mh_path (tmp)) && (i = strlen (s)) &&
+ (t = mailboxfile (tmp,name)) && !strncmp (t,s,i) &&
+ (tmp[i] == '/') && tmp[i+1]) {
+ sprintf (altname,"#mh%.900s",tmp+i);
+ /* can't do synonly here! */
+ ret = mh_isvalid (altname,tmp,NIL);
+ }
+ else errno = EINVAL; /* bogus name */
+ return ret;
+}
+
+/* MH mail test for valid mailbox
+ * Accepts: mailbox name
+ * Returns: T if valid, NIL otherwise
+ */
+
+int mh_namevalid (char *name)
+{
+ char *s;
+ if (name[0] == '#' && (name[1] == 'm' || name[1] == 'M') &&
+ (name[2] == 'h' || name[2] == 'H') && name[3] == '/')
+ for (s = name; s && *s;) { /* make sure no all-digit nodes */
+ if (isdigit (*s)) s++; /* digit, check this node further... */
+ else if (*s == '/') break;/* all digit node, barf */
+ /* non-digit, skip to next node or return */
+ else if (!((s = strchr (s+1,'/')) && *++s)) return T;
+ }
+ return NIL; /* all numeric or empty node */
+}
+
+/* Return MH path
+ * Accepts: temporary buffer
+ * Returns: MH path or NIL if MH disabled
+ */
+
+char *mh_path (char *tmp)
+{
+ char *s,*t,*v,*r;
+ int fd;
+ struct stat sbuf;
+ if (!mh_profile) { /* build mh_profile and mh_pathname now */
+ sprintf (tmp,"%s/%s",myhomedir (),MHPROFILE);
+ if ((fd = open (mh_profile = cpystr (tmp),O_RDONLY,NIL)) >= 0) {
+ fstat (fd,&sbuf); /* yes, get size and read file */
+ read (fd,(t = (char *) fs_get (sbuf.st_size + 1)),sbuf.st_size);
+ close (fd); /* don't need the file any more */
+ t[sbuf.st_size] = '\0'; /* tie it off */
+ /* parse profile file */
+ for (s = strtok_r (t,"\r\n",&r); s && *s; s = strtok_r (NIL,"\r\n",&r)) {
+ /* found space in line? */
+ if (v = strpbrk (s," \t")) {
+ *v++ = '\0'; /* tie off, is keyword "Path:"? */
+ if (!compare_cstring (s,"Path:")) {
+ /* skip whitespace */
+ while ((*v == ' ') || (*v == '\t')) ++v;
+ /* absolute path? */
+ if (*v == '/') s = v;
+ else sprintf (s = tmp,"%s/%s",myhomedir (),v);
+ /* copy name */
+ mh_pathname = cpystr (s);
+ break; /* don't need to look at rest of file */
+ }
+ }
+ }
+ fs_give ((void **) &t); /* flush profile text */
+ if (!mh_pathname) { /* default path if not in the profile */
+ sprintf (tmp,"%s/%s",myhomedir (),MHPATH);
+ mh_pathname = cpystr (tmp);
+ }
+ }
+ }
+ return mh_pathname;
+}
+
+/* MH manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mh_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = mh_file ((char *) value,"INBOX");
+ break;
+ case GET_DIRFMTTEST:
+ ret = (void *) mh_dirfmttest;
+ break;
+ case SET_MHPROFILE:
+ if (mh_profile) fs_give ((void **) &mh_profile);
+ mh_profile = cpystr ((char *) value);
+ case GET_MHPROFILE:
+ ret = (void *) mh_profile;
+ break;
+ case SET_MHPATH:
+ if (mh_pathname) fs_give ((void **) &mh_pathname);
+ mh_pathname = cpystr ((char *) value);
+ case GET_MHPATH:
+ ret = (void *) mh_pathname;
+ break;
+ case SET_MHALLOWINBOX:
+ mh_allow_inbox = value ? T : NIL;
+ case GET_MHALLOWINBOX:
+ ret = (void *) (mh_allow_inbox ? VOIDT : NIL);
+ }
+ return ret;
+}
+
+
+/* MH test for directory format internal node
+ * Accepts: candidate node name
+ * Returns: T if internal name, NIL otherwise
+ */
+
+long mh_dirfmttest (char *s)
+{
+ int c;
+ /* sequence(s) file is an internal name */
+ if (strcmp (s,MHSEQUENCE) && strcmp (s,MHSEQUENCES)) {
+ if (*s == MHCOMMA) ++s; /* else comma + all numeric name */
+ /* success if all-numeric */
+ while (c = *s++) if (!isdigit (c)) return NIL;
+ }
+ return LONGT;
+}
+
+/* MH scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mh_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ char *s,test[MAILTMPLEN],file[MAILTMPLEN];
+ long i = 0;
+ if (!pat || !*pat) { /* empty pattern? */
+ if (mh_canonicalize (test,ref,"*")) {
+ /* tie off name at root */
+ if (s = strchr (test,'/')) *++s = '\0';
+ else test[0] = '\0';
+ mm_list (stream,'/',test,LATT_NOSELECT);
+ }
+ }
+ /* get canonical form of name */
+ else if (mh_canonicalize (test,ref,pat)) {
+ if (contents) { /* maybe I'll implement this someday */
+ mm_log ("Scan not valid for mh mailboxes",ERROR);
+ return;
+ }
+ if (test[3] == '/') { /* looking down levels? */
+ /* yes, found any wildcards? */
+ if (s = strpbrk (test,"%*")) {
+ /* yes, copy name up to that point */
+ strncpy (file,test+4,i = s - (test+4));
+ file[i] = '\0'; /* tie off */
+ }
+ else strcpy (file,test+4);/* use just that name then */
+ /* find directory name */
+ if (s = strrchr (file,'/')) {
+ *s = '\0'; /* found, tie off at that point */
+ s = file;
+ }
+ /* do the work */
+ mh_list_work (stream,s,test,0);
+ }
+ /* always an INBOX */
+ if (!compare_cstring (test,MHINBOX))
+ mm_list (stream,NIL,MHINBOX,LATT_NOINFERIORS);
+ }
+}
+
+/* MH list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mh_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ mh_scan (stream,ref,pat,NIL);
+}
+
+
+/* MH list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mh_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ void *sdb = NIL;
+ char *s,test[MAILTMPLEN];
+ /* get canonical form of name */
+ if (mh_canonicalize (test,ref,pat) && (s = sm_read (&sdb))) {
+ do if (pmatch_full (s,test,'/')) mm_lsub (stream,'/',s,NIL);
+ while (s = sm_read (&sdb)); /* until no more subscriptions */
+ }
+}
+
+/* MH list mailboxes worker routine
+ * Accepts: mail stream
+ * directory name to search
+ * search pattern
+ * search level
+ */
+
+void mh_list_work (MAILSTREAM *stream,char *dir,char *pat,long level)
+{
+ DIR *dp;
+ struct direct *d;
+ struct stat sbuf;
+ char *cp,*np,curdir[MAILTMPLEN],name[MAILTMPLEN];
+ /* build MH name to search */
+ if (dir) sprintf (name,"#mh/%s/",dir);
+ else strcpy (name,"#mh/");
+ /* make directory name, punt if bogus */
+ if (!mh_file (curdir,name)) return;
+ cp = curdir + strlen (curdir);/* end of directory name */
+ np = name + strlen (name); /* end of MH name */
+ if (dp = opendir (curdir)) { /* open directory */
+ while (d = readdir (dp)) /* scan, ignore . and numeric names */
+ if ((d->d_name[0] != '.') && !mh_select (d)) {
+ strcpy (cp,d->d_name); /* make directory name */
+ if (!stat (curdir,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) {
+ strcpy (np,d->d_name);/* make mh name of directory name */
+ /* yes, an MH name if full match */
+ if (pmatch_full (name,pat,'/')) mm_list (stream,'/',name,NIL);
+ /* check if should recurse */
+ if (dmatch (name,pat,'/') &&
+ (level < (long) mail_parameters (NIL,GET_LISTMAXLEVEL,NIL)))
+ mh_list_work (stream,name+4,pat,level+1);
+ }
+ }
+ closedir (dp); /* all done, flush directory */
+ }
+}
+
+/* MH mail subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long mh_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return sm_subscribe (mailbox);
+}
+
+
+/* MH mail unsubscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to delete from subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long mh_unsubscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return sm_unsubscribe (mailbox);
+}
+
+/* MH mail create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mh_create (MAILSTREAM *stream,char *mailbox)
+{
+ char tmp[MAILTMPLEN];
+ if (!mh_namevalid (mailbox)) /* validate name */
+ sprintf (tmp,"Can't create mailbox %.80s: invalid MH-format name",mailbox);
+ /* must not already exist */
+ else if (mh_isvalid (mailbox,tmp,NIL))
+ sprintf (tmp,"Can't create mailbox %.80s: mailbox already exists",mailbox);
+ else if (!mh_path (tmp)) return NIL;
+ /* try to make it */
+ else if (!(mh_file (tmp,mailbox) &&
+ dummy_create_path (stream,strcat (tmp,"/"),
+ get_dir_protection (mailbox))))
+ sprintf (tmp,"Can't create mailbox %.80s: %s",mailbox,strerror (errno));
+ else return LONGT; /* success */
+ mm_log (tmp,ERROR);
+ return NIL;
+}
+
+/* MH mail delete mailbox
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mh_delete (MAILSTREAM *stream,char *mailbox)
+{
+ DIR *dirp;
+ struct direct *d;
+ int i;
+ char tmp[MAILTMPLEN];
+ /* is mailbox valid? */
+ if (!mh_isvalid (mailbox,tmp,NIL)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: no such mailbox",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get name of directory */
+ i = strlen (mh_file (tmp,mailbox));
+ if (dirp = opendir (tmp)) { /* open directory */
+ tmp[i++] = '/'; /* now apply trailing delimiter */
+ /* massacre all mh owned files */
+ while (d = readdir (dirp)) if (mh_dirfmttest (d->d_name)) {
+ strcpy (tmp + i,d->d_name);
+ unlink (tmp); /* sayonara */
+ }
+ closedir (dirp); /* flush directory */
+ }
+ /* try to remove the directory */
+ if (rmdir (mh_file (tmp,mailbox))) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %s",mailbox,strerror (errno));
+ mm_log (tmp,WARN);
+ }
+ return T; /* return success */
+}
+
+/* MH mail rename mailbox
+ * Accepts: MH mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long mh_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ char c,*s,tmp[MAILTMPLEN],tmp1[MAILTMPLEN];
+ struct stat sbuf;
+ /* old mailbox name must be valid */
+ if (!mh_isvalid (old,tmp,NIL))
+ sprintf (tmp,"Can't rename mailbox %.80s: no such mailbox",old);
+ else if (!mh_namevalid (newname))
+ sprintf (tmp,"Can't rename to mailbox %.80s: invalid MH-format name",
+ newname);
+ /* new mailbox name must not be valid */
+ else if (mh_isvalid (newname,tmp,NIL))
+ sprintf (tmp,"Can't rename to mailbox %.80s: destination already exists",
+ newname);
+ /* success if can rename the directory */
+ else { /* found superior to destination name? */
+ if (s = strrchr (mh_file (tmp1,newname),'/')) {
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (tmp1,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,tmp1,get_dir_protection (newname)))
+ return NIL;
+ *s = c; /* restore full name */
+ }
+ if (!rename (mh_file (tmp,old),tmp1)) return T;
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",
+ old,newname,strerror (errno));
+ }
+ mm_log (tmp,ERROR); /* something failed */
+ return NIL;
+}
+
+/* MH mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mh_open (MAILSTREAM *stream)
+{
+ char tmp[MAILTMPLEN];
+ if (!stream) return &mhproto; /* return prototype for OP_PROTOTYPE call */
+ if (stream->local) fatal ("mh recycle stream");
+ stream->local = fs_get (sizeof (MHLOCAL));
+ /* INBOXness is one of the following:
+ * #mhinbox (case-independent)
+ * #mh/inbox (mh is case-independent, inbox is case-dependent)
+ * INBOX (case-independent
+ */
+ stream->inbox = /* note if an INBOX or not */
+ (!compare_cstring (stream->mailbox,MHINBOX) ||
+ ((stream->mailbox[0] == '#') &&
+ ((stream->mailbox[1] == 'm') || (stream->mailbox[1] == 'M')) &&
+ ((stream->mailbox[2] == 'h') || (stream->mailbox[2] == 'H')) &&
+ (stream->mailbox[3] == '/') && !strcmp (stream->mailbox+4,MHINBOXDIR)) ||
+ !compare_cstring (stream->mailbox,"INBOX")) ? T : NIL;
+ mh_file (tmp,stream->mailbox);/* get directory name */
+ LOCAL->dir = cpystr (tmp); /* copy directory name for later */
+ LOCAL->scantime = 0; /* not scanned yet */
+ LOCAL->cachedtexts = 0; /* no cached texts */
+ stream->sequence++; /* bump sequence number */
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ if (!mh_ping (stream)) return NIL;
+ if (!(stream->nmsgs || stream->silent))
+ mm_log ("Mailbox is empty",(long) NIL);
+ return stream; /* return stream to caller */
+}
+
+/* MH mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mh_close (MAILSTREAM *stream,long options)
+{
+ if (LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ if (options & CL_EXPUNGE) mh_expunge (stream,NIL,NIL);
+ if (LOCAL->dir) fs_give ((void **) &LOCAL->dir);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ stream->silent = silent; /* reset silent state */
+ }
+}
+
+
+/* MH mail fetch fast information
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ */
+
+void mh_fast (MAILSTREAM *stream,char *sequence,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned long i;
+ /* set up metadata for all messages */
+ if (stream && LOCAL && ((flags & FT_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence &&
+ !(elt->day && elt->rfc822_size)) mh_load_message (stream,i,NIL);
+}
+
+/* MH load message into cache
+ * Accepts: MAIL stream
+ * message #
+ * option flags
+ */
+
+void mh_load_message (MAILSTREAM *stream,unsigned long msgno,long flags)
+{
+ unsigned long i,j,nlseen;
+ int fd;
+ unsigned char c,*t;
+ struct stat sbuf;
+ MESSAGECACHE *elt;
+ FDDATA d;
+ STRING bs;
+ elt = mail_elt (stream,msgno);/* get elt */
+ /* build message file name */
+ sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid);
+ /* anything we need not currently cached? */
+ if ((!elt->day || !elt->rfc822_size ||
+ ((flags & MLM_HEADER) && !elt->private.msg.header.text.data) ||
+ ((flags & MLM_TEXT) && !elt->private.msg.text.text.data)) &&
+ ((fd = open (LOCAL->buf,O_RDONLY,NIL)) >= 0)) {
+ fstat (fd,&sbuf); /* get file metadata */
+ d.fd = fd; /* set up file descriptor */
+ d.pos = 0; /* start of file */
+ d.chunk = LOCAL->buf;
+ d.chunksize = CHUNKSIZE;
+ INIT (&bs,fd_string,&d,sbuf.st_size);
+ if (!elt->day) { /* set internaldate to file date */
+ struct tm *tm = gmtime (&sbuf.st_mtime);
+ elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1;
+ elt->year = tm->tm_year + 1900 - BASEYEAR;
+ elt->hours = tm->tm_hour; elt->minutes = tm->tm_min;
+ elt->seconds = tm->tm_sec;
+ elt->zhours = 0; elt->zminutes = 0;
+ }
+
+ if (!elt->rfc822_size) { /* know message size yet? */
+ for (i = 0, j = SIZE (&bs), nlseen = 0; j--; ) switch (SNX (&bs)) {
+ case '\015': /* unlikely carriage return */
+ if (!j || (CHR (&bs) != '\012')) {
+ i++; /* ugh, raw CR */
+ nlseen = NIL;
+ break;
+ }
+ SNX (&bs); /* eat the line feed, drop in */
+ --j;
+ case '\012': /* line feed? */
+ i += 2; /* count a CRLF */
+ /* header size known yet? */
+ if (!elt->private.msg.header.text.size && nlseen) {
+ /* note position in file */
+ elt->private.special.text.size = GETPOS (&bs);
+ /* and CRLF-adjusted size */
+ elt->private.msg.header.text.size = i;
+ }
+ nlseen = T; /* note newline seen */
+ break;
+ default: /* ordinary chararacter */
+ i++;
+ nlseen = NIL;
+ break;
+ }
+ SETPOS (&bs,0); /* restore old position */
+ elt->rfc822_size = i; /* note that we have size now */
+ /* header is entire message if no delimiter */
+ if (!elt->private.msg.header.text.size)
+ elt->private.msg.header.text.size = elt->rfc822_size;
+ /* text is remainder of message */
+ elt->private.msg.text.text.size =
+ elt->rfc822_size - elt->private.msg.header.text.size;
+ }
+ /* need to load cache with message data? */
+ if (((flags & MLM_HEADER) && !elt->private.msg.header.text.data) ||
+ ((flags & MLM_TEXT) && !elt->private.msg.text.text.data)) {
+ /* purge cache if too big */
+ if (LOCAL->cachedtexts > max (stream->nmsgs * 4096,2097152)) {
+ /* just can't keep that much */
+ mail_gc (stream,GC_TEXTS);
+ LOCAL->cachedtexts = 0;
+ }
+
+ if ((flags & MLM_HEADER) && !elt->private.msg.header.text.data) {
+ t = elt->private.msg.header.text.data =
+ (unsigned char *) fs_get (elt->private.msg.header.text.size + 1);
+ LOCAL->cachedtexts += elt->private.msg.header.text.size;
+ /* read in message header */
+ for (i = 0; i < elt->private.msg.header.text.size; i++)
+ switch (c = SNX (&bs)) {
+ case '\015': /* unlikely carriage return */
+ *t++ = c;
+ if ((CHR (&bs) == '\012')) {
+ *t++ = SNX (&bs);
+ i++;
+ }
+ break;
+ case '\012': /* line feed? */
+ *t++ = '\015';
+ i++;
+ default:
+ *t++ = c;
+ break;
+ }
+ *t = '\0'; /* tie off string */
+ if ((t - elt->private.msg.header.text.data) !=
+ elt->private.msg.header.text.size) fatal ("mh hdr size mismatch");
+ }
+ if ((flags & MLM_TEXT) && !elt->private.msg.text.text.data) {
+ t = elt->private.msg.text.text.data =
+ (unsigned char *) fs_get (elt->private.msg.text.text.size + 1);
+ SETPOS (&bs,elt->private.special.text.size);
+ LOCAL->cachedtexts += elt->private.msg.text.text.size;
+ /* read in message text */
+ for (i = 0; i < elt->private.msg.text.text.size; i++)
+ switch (c = SNX (&bs)) {
+ case '\015': /* unlikely carriage return */
+ *t++ = c;
+ if ((CHR (&bs) == '\012')) {
+ *t++ = SNX (&bs);
+ i++;
+ }
+ break;
+ case '\012': /* line feed? */
+ *t++ = '\015';
+ i++;
+ default:
+ *t++ = c;
+ break;
+ }
+ *t = '\0'; /* tie off string */
+ if ((t - elt->private.msg.text.text.data) !=
+ elt->private.msg.text.text.size) fatal ("mh txt size mismatch");
+ }
+ }
+ close (fd); /* flush message file */
+ }
+}
+
+/* MH mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *mh_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags)
+{
+ MESSAGECACHE *elt;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ elt = mail_elt (stream,msgno);/* get elt */
+ if (!elt->private.msg.header.text.data)
+ mh_load_message (stream,msgno,MLM_HEADER);
+ *length = elt->private.msg.header.text.size;
+ return (char *) elt->private.msg.header.text.data;
+}
+
+
+/* MH mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mh_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mail_elt (stream,msgno);/* get elt */
+ /* snarf message if don't have it yet */
+ if (!elt->private.msg.text.text.data) {
+ mh_load_message (stream,msgno,MLM_TEXT);
+ if (!elt->private.msg.text.text.data) return NIL;
+ }
+ if (!(flags & FT_PEEK)) { /* mark as seen */
+ mail_elt (stream,msgno)->seen = T;
+ mm_flags (stream,msgno);
+ }
+ INIT (bs,mail_string,elt->private.msg.text.text.data,
+ elt->private.msg.text.text.size);
+ return T;
+}
+
+/* MH mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long mh_ping (MAILSTREAM *stream)
+{
+ MAILSTREAM *sysibx = NIL;
+ MESSAGECACHE *elt,*selt;
+ struct stat sbuf;
+ char *s,tmp[MAILTMPLEN];
+ int fd;
+ unsigned long i,j,r;
+ unsigned long old = stream->uid_last;
+ long nmsgs = stream->nmsgs;
+ long recent = stream->recent;
+ int silent = stream->silent;
+ if (stat (LOCAL->dir,&sbuf)) {/* directory exists? */
+ if (stream->inbox && /* no, create if INBOX */
+ dummy_create_path (stream,strcat (mh_file (tmp,MHINBOX),"/"),
+ get_dir_protection ("INBOX"))) return T;
+ sprintf (tmp,"Can't open mailbox %.80s: no such mailbox",stream->mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ stream->silent = T; /* don't pass up mm_exists() events yet */
+ if (sbuf.st_ctime != LOCAL->scantime) {
+ struct direct **names = NIL;
+ long nfiles = scandir (LOCAL->dir,&names,mh_select,mh_numsort);
+ if (nfiles < 0) nfiles = 0; /* in case error */
+ /* note scanned now */
+ LOCAL->scantime = sbuf.st_ctime;
+ /* scan directory */
+ for (i = 0; i < nfiles; ++i) {
+ /* if newly seen, add to list */
+ if ((j = atoi (names[i]->d_name)) > old) {
+ mail_exists (stream,++nmsgs);
+ stream->uid_last = (elt = mail_elt (stream,nmsgs))->private.uid = j;
+ elt->valid = T; /* note valid flags */
+ if (old) { /* other than the first pass? */
+ elt->recent = T; /* yup, mark as recent */
+ recent++; /* bump recent count */
+ }
+ else { /* see if already read */
+ sprintf (tmp,"%s/%s",LOCAL->dir,names[i]->d_name);
+ if (!stat (tmp,&sbuf) && (sbuf.st_atime > sbuf.st_mtime))
+ elt->seen = T;
+ }
+ }
+ fs_give ((void **) &names[i]);
+ }
+ /* free directory */
+ if (s = (void *) names) fs_give ((void **) &s);
+ }
+
+ /* if INBOX, snarf from system INBOX */
+ if (stream->inbox && strcmp (sysinbox (),stream->mailbox)) {
+ old = stream->uid_last;
+ mm_critical (stream); /* go critical */
+ /* see if anything in system inbox */
+ if (!stat (sysinbox (),&sbuf) && sbuf.st_size &&
+ (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) &&
+ !sysibx->rdonly && (r = sysibx->nmsgs)) {
+ for (i = 1; i <= r; ++i) {/* for each message in sysinbox mailbox */
+ /* build file name we will use */
+ sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,++old);
+ /* snarf message from Berkeley mailbox */
+ selt = mail_elt (sysibx,i);
+ if (((fd = open (LOCAL->buf,O_WRONLY|O_CREAT|O_EXCL,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL)))
+ >= 0) &&
+ (s = mail_fetchheader_full (sysibx,i,NIL,&j,FT_INTERNAL)) &&
+ (write (fd,s,j) == j) &&
+ (s = mail_fetchtext_full (sysibx,i,&j,FT_INTERNAL|FT_PEEK)) &&
+ (write (fd,s,j) == j) && !fsync (fd) && !close (fd)) {
+ /* swell the cache */
+ mail_exists (stream,++nmsgs);
+ stream->uid_last = /* create new elt, note its file number */
+ (elt = mail_elt (stream,nmsgs))->private.uid = old;
+ recent++; /* bump recent count */
+ /* set up initial flags and date */
+ elt->valid = elt->recent = T;
+ elt->seen = selt->seen;
+ elt->deleted = selt->deleted;
+ elt->flagged = selt->flagged;
+ elt->answered = selt->answered;
+ elt->draft = selt->draft;
+ elt->day = selt->day;elt->month = selt->month;elt->year = selt->year;
+ elt->hours = selt->hours;elt->minutes = selt->minutes;
+ elt->seconds = selt->seconds;
+ elt->zhours = selt->zhours; elt->zminutes = selt->zminutes;
+ elt->zoccident = selt->zoccident;
+ mh_setdate (LOCAL->buf,elt);
+ sprintf (tmp,"%lu",i);/* delete it from the sysinbox */
+ mail_flag (sysibx,tmp,"\\Deleted",ST_SET);
+ }
+
+ else { /* failed to snarf */
+ if (fd) { /* did it ever get opened? */
+ close (fd); /* close descriptor */
+ unlink (LOCAL->buf);/* flush this file */
+ }
+ sprintf (tmp,"Message copy to MH mailbox failed: %.80s",
+ s,strerror (errno));
+ mm_log (tmp,ERROR);
+ r = 0; /* stop the snarf in its tracks */
+ }
+ }
+ /* update scan time */
+ if (!stat (LOCAL->dir,&sbuf)) LOCAL->scantime = sbuf.st_ctime;
+ mail_expunge (sysibx); /* now expunge all those messages */
+ }
+ if (sysibx) mail_close (sysibx);
+ mm_nocritical (stream); /* release critical */
+ }
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of mailbox size */
+ mail_recent (stream,recent);
+ return T; /* return that we are alive */
+}
+
+/* MH mail check mailbox
+ * Accepts: MAIL stream
+ */
+
+void mh_check (MAILSTREAM *stream)
+{
+ /* Perhaps in the future this will preserve flags */
+ if (mh_ping (stream)) mm_log ("Check completed",(long) NIL);
+}
+
+
+/* MH mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long mh_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ MESSAGECACHE *elt;
+ unsigned long i = 1;
+ unsigned long n = 0;
+ unsigned long recent = stream->recent;
+ if (ret = sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) {
+ mm_critical (stream); /* go critical */
+ while (i <= stream->nmsgs) {/* for each message */
+ elt = mail_elt (stream,i);/* if deleted, need to trash it */
+ if (elt->deleted && (sequence ? elt->sequence : T)) {
+ sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid);
+ if (unlink (LOCAL->buf)) {/* try to delete the message */
+ sprintf (LOCAL->buf,"Expunge of message %lu failed, aborted: %s",i,
+ strerror (errno));
+ mm_log (LOCAL->buf,(long) NIL);
+ break;
+ }
+ /* note uncached */
+ LOCAL->cachedtexts -= ((elt->private.msg.header.text.data ?
+ elt->private.msg.header.text.size : 0) +
+ (elt->private.msg.text.text.data ?
+ elt->private.msg.text.text.size : 0));
+ mail_gc_msg (&elt->private.msg,GC_ENV | GC_TEXTS);
+ /* if recent, note one less recent message */
+ if (elt->recent) --recent;
+ /* notify upper levels */
+ mail_expunged (stream,i);
+ n++; /* count up one more expunged message */
+ }
+ else i++; /* otherwise try next message */
+ }
+ if (n) { /* output the news if any expunged */
+ sprintf (LOCAL->buf,"Expunged %lu messages",n);
+ mm_log (LOCAL->buf,(long) NIL);
+ }
+ else mm_log ("No messages deleted, so no update needed",(long) NIL);
+ mm_nocritical (stream); /* release critical */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ }
+ return ret;
+}
+
+/* MH mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if copy successful, else NIL
+ */
+
+long mh_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ FDDATA d;
+ STRING st;
+ MESSAGECACHE *elt;
+ struct stat sbuf;
+ int fd;
+ unsigned long i;
+ char flags[MAILTMPLEN],date[MAILTMPLEN];
+ appenduid_t au = (appenduid_t) mail_parameters (NIL,GET_APPENDUID,NIL);
+ long ret = NIL;
+ /* copy the messages */
+ if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid);
+ if ((fd = open (LOCAL->buf,O_RDONLY,NIL)) < 0) return NIL;
+ fstat (fd,&sbuf); /* get size of message */
+ if (!elt->day) { /* set internaldate to file date if needed */
+ struct tm *tm = gmtime (&sbuf.st_mtime);
+ elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1;
+ elt->year = tm->tm_year + 1900 - BASEYEAR;
+ elt->hours = tm->tm_hour; elt->minutes = tm->tm_min;
+ elt->seconds = tm->tm_sec;
+ elt->zhours = 0; elt->zminutes = 0;
+ }
+ d.fd = fd; /* set up file descriptor */
+ d.pos = 0; /* start of file */
+ d.chunk = LOCAL->buf;
+ d.chunksize = CHUNKSIZE;
+ /* kludge; mh_append would just strip CRs */
+ INIT (&st,fd_string,&d,sbuf.st_size);
+ /* init flag string */
+ flags[0] = flags[1] = '\0';
+ if (elt->seen) strcat (flags," \\Seen");
+ if (elt->deleted) strcat (flags," \\Deleted");
+ if (elt->flagged) strcat (flags," \\Flagged");
+ if (elt->answered) strcat (flags," \\Answered");
+ if (elt->draft) strcat (flags," \\Draft");
+ flags[0] = '('; /* open list */
+ strcat (flags,")"); /* close list */
+ mail_date (date,elt); /* generate internal date */
+ if (au) mail_parameters (NIL,SET_APPENDUID,NIL);
+ if ((ret = mail_append_full (NIL,mailbox,flags,date,&st)) &&
+ (options & CP_MOVE)) elt->deleted = T;
+ if (au) mail_parameters (NIL,SET_APPENDUID,(void *) au);
+ close (fd);
+ }
+ if (ret && mail_parameters (NIL,GET_COPYUID,NIL))
+ mm_log ("Can not return meaningful COPYUID with this mailbox format",WARN);
+ return ret; /* return success */
+}
+
+/* MH mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mh_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct direct **names = NIL;
+ int fd;
+ char c,*flags,*date,*s,tmp[MAILTMPLEN];
+ STRING *message;
+ MESSAGECACHE elt;
+ FILE *df;
+ long i,size,last,nfiles;
+ long ret = LONGT;
+ /* default stream to prototype */
+ if (!stream) stream = &mhproto;
+ /* make sure valid mailbox */
+ if (!mh_isvalid (mailbox,tmp,NIL)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (!((!compare_cstring (mailbox,MHINBOX) ||
+ !compare_cstring (mailbox,"INBOX")) &&
+ (mh_file (tmp,MHINBOX) &&
+ dummy_create_path (stream,strcat (tmp,"/"),
+ get_dir_protection (mailbox))))) {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* falls through */
+ case 0: /* merely empty file? */
+ break;
+ case EINVAL:
+ sprintf (tmp,"Invalid MH-format mailbox name: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a MH-format mailbox: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get first message */
+ if (!(*af) (stream,data,&flags,&date,&message)) return NIL;
+ if ((nfiles = scandir (tmp,&names,mh_select,mh_numsort)) > 0) {
+ /* largest number */
+ last = atoi (names[nfiles-1]->d_name);
+ for (i = 0; i < nfiles; ++i) /* free directory */
+ fs_give ((void **) &names[i]);
+ }
+ else last = 0; /* no messages here yet */
+ if (s = (void *) names) fs_give ((void **) &s);
+
+ mm_critical (stream); /* go critical */
+ do {
+ if (!SIZE (message)) { /* guard against zero-length */
+ mm_log ("Append of zero-length message",ERROR);
+ ret = NIL;
+ break;
+ }
+ if (date) { /* want to preserve date? */
+ /* yes, parse date into an elt */
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ mm_log (tmp,ERROR);
+ ret = NIL;
+ break;
+ }
+ }
+ mh_file (tmp,mailbox); /* build file name we will use */
+ sprintf (tmp + strlen (tmp),"/%ld",++last);
+ if (((fd = open (tmp,O_WRONLY|O_CREAT|O_EXCL,
+ (long)mail_parameters (NIL,GET_MBXPROTECTION,NIL))) < 0)||
+ !(df = fdopen (fd,"ab"))) {
+ sprintf (tmp,"Can't open append message: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL;
+ break;
+ }
+ /* copy the data w/o CR's */
+ for (size = 0,i = SIZE (message); i && ret; --i)
+ if (((c = SNX (message)) != '\015') && (putc (c,df) == EOF)) ret = NIL;
+ /* close the file */
+ if (!ret || fclose (df)) {
+ unlink (tmp); /* delete message */
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL;
+ }
+ if (ret) { /* set the date for this message */
+ if (date) mh_setdate (tmp,&elt);
+ /* get next message */
+ if (!(*af) (stream,data,&flags,&date,&message)) ret = NIL;
+ }
+ } while (ret && message);
+ mm_nocritical (stream); /* release critical */
+ if (ret && mail_parameters (NIL,GET_APPENDUID,NIL))
+ mm_log ("Can not return meaningful APPENDUID with this mailbox format",
+ WARN);
+ return ret;
+}
+
+/* Internal routines */
+
+
+/* MH file name selection test
+ * Accepts: candidate directory entry
+ * Returns: T to use file name, NIL to skip it
+ */
+
+int mh_select (struct direct *name)
+{
+ char c;
+ char *s = name->d_name;
+ while (c = *s++) if (!isdigit (c)) return NIL;
+ return T;
+}
+
+
+/* MH file name comparision
+ * Accepts: first candidate directory entry
+ * second candidate directory entry
+ * Returns: negative if d1 < d2, 0 if d1 == d2, postive if d1 > d2
+ */
+
+int mh_numsort (const void *d1,const void *d2)
+{
+ return atoi ((*(struct direct **) d1)->d_name) -
+ atoi ((*(struct direct **) d2)->d_name);
+}
+
+
+/* MH mail build file name
+ * Accepts: destination string
+ * source
+ * Returns: destination
+ */
+
+char *mh_file (char *dst,char *name)
+{
+ char *s;
+ char *path = mh_path (dst);
+ if (!path) fatal ("No mh path in mh_file()!");
+ /* INBOX becomes "inbox" in the MH path */
+ if (!compare_cstring (name,MHINBOX) || !compare_cstring (name,"INBOX"))
+ sprintf (dst,"%.900s/%.80s",path,MHINBOXDIR);
+ /* #mh names skip past prefix */
+ else if (*name == '#') sprintf (dst,"%.100s/%.900s",path,name + 4);
+ else mailboxfile (dst,name); /* all other names */
+ /* tie off unnecessary trailing / */
+ if ((s = strrchr (dst,'/')) && !s[1] && (s[-1] == '/')) *s = '\0';
+ return dst;
+}
+
+/* MH canonicalize name
+ * Accepts: buffer to write name
+ * reference
+ * pattern
+ * Returns: T if success, NIL if failure
+ */
+
+long mh_canonicalize (char *pattern,char *ref,char *pat)
+{
+ unsigned long i;
+ char *s,tmp[MAILTMPLEN];
+ if (ref && *ref) { /* have a reference */
+ strcpy (pattern,ref); /* copy reference to pattern */
+ /* # overrides mailbox field in reference */
+ if (*pat == '#') strcpy (pattern,pat);
+ /* pattern starts, reference ends, with / */
+ else if ((*pat == '/') && (pattern[strlen (pattern) - 1] == '/'))
+ strcat (pattern,pat + 1); /* append, omitting one of the period */
+ else strcat (pattern,pat); /* anything else is just appended */
+ }
+ else strcpy (pattern,pat); /* just have basic name */
+ if (mh_isvalid (pattern,tmp,T)) {
+ /* count wildcards */
+ for (i = 0, s = pattern; *s; *s++) if ((*s == '*') || (*s == '%')) ++i;
+ /* success if not too many */
+ if (i <= MAXWILDCARDS) return LONGT;
+ mm_log ("Excessive wildcards in LIST/LSUB",ERROR);
+ }
+ return NIL;
+}
+
+/* Set date for message
+ * Accepts: file name
+ * elt containing date
+ */
+
+void mh_setdate (char *file,MESSAGECACHE *elt)
+{
+ time_t tp[2];
+ tp[0] = time (0); /* atime is now */
+ tp[1] = mail_longdate (elt); /* modification time */
+ utime (file,tp); /* set the times */
+}
diff --git a/imap/src/osdep/unix/mix.c b/imap/src/osdep/unix/mix.c
new file mode 100644
index 00000000..fbf4a023
--- /dev/null
+++ b/imap/src/osdep/unix/mix.c
@@ -0,0 +1,2834 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MIX mail routines
+ *
+ * Author(s): Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@Washington.EDU
+ *
+ * Date: 1 March 2006
+ * Last Edited: 7 May 2008
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "misc.h"
+#include "dummy.h"
+#include "fdstring.h"
+
+/* MIX definitions */
+
+#define MEGABYTE (1024*1024)
+
+#define MIXDATAROLL MEGABYTE /* size at which we roll to a new file */
+
+
+/* MIX files */
+
+#define MIXNAME ".mix" /* prefix for all MIX file names */
+#define MIXMETA "meta" /* suffix for metadata */
+#define MIXINDEX "index" /* suffix for index */
+#define MIXSTATUS "status" /* suffix for status */
+#define MIXSORTCACHE "sortcache"/* suffix for sortcache */
+#define METAMAX (MEGABYTE-1) /* maximum metadata file size (sanity check) */
+
+
+/* MIX file formats */
+
+ /* sequence format (all but msg files) */
+#define SEQFMT "S%08lx\015\012"
+ /* metadata file format */
+#define MTAFMT "V%08lx\015\012L%08lx\015\012N%08lx\015\012"
+ /* index file record format */
+#define IXRFMT ":%08lx:%04d%02d%02d%02d%02d%02d%c%02d%02d:%08lx:%08lx:%08lx:%08lx:%08lx:\015\012"
+ /* status file record format */
+#define STRFMT ":%08lx:%08lx:%04x:%08lx:\015\012"
+ /* message file header format */
+#define MSRFMT "%s%08lx:%04d%02d%02d%02d%02d%02d%c%02d%02d:%08lx:\015\012"
+#define MSGTOK ":msg:"
+#define MSGTSZ (sizeof(MSGTOK)-1)
+ /* sortcache file record format */
+#define SCRFMT ":%08lx:%08lx:%08lx:%08lx:%08lx:%c%08lx:%08lx:%08lx:\015\012"
+
+/* MIX I/O stream local data */
+
+typedef struct mix_local {
+ unsigned long curmsg; /* current message file number */
+ unsigned long newmsg; /* current new message file number */
+ time_t lastsnarf; /* last snarf time */
+ int msgfd; /* file description of current msg file */
+ int mfd; /* file descriptor of open metadata */
+ unsigned long metaseq; /* metadata sequence */
+ char *index; /* mailbox index name */
+ unsigned long indexseq; /* index sequence */
+ char *status; /* mailbox status name */
+ unsigned long statusseq; /* status sequence */
+ char *sortcache; /* mailbox sortcache name */
+ unsigned long sortcacheseq; /* sortcache sequence */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ unsigned int expok : 1; /* non-zero if expunge reports OK */
+ unsigned int internal : 1; /* internally opened, do not validate */
+} MIXLOCAL;
+
+
+#define MIXBURP struct mix_burp
+
+MIXBURP {
+ unsigned long fileno; /* message file number */
+ char *name; /* message file name */
+ SEARCHSET *tail; /* tail of ranges */
+ SEARCHSET set; /* set of retained ranges */
+ MIXBURP *next; /* next file to burp */
+};
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MIXLOCAL *) stream->local)
+
+/* Function prototypes */
+
+DRIVER *mix_valid (char *name);
+long mix_isvalid (char *name,char *meta);
+void *mix_parameters (long function,void *value);
+long mix_dirfmttest (char *name);
+void mix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+long mix_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz);
+void mix_list (MAILSTREAM *stream,char *ref,char *pat);
+void mix_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long mix_subscribe (MAILSTREAM *stream,char *mailbox);
+long mix_unsubscribe (MAILSTREAM *stream,char *mailbox);
+long mix_create (MAILSTREAM *stream,char *mailbox);
+long mix_delete (MAILSTREAM *stream,char *mailbox);
+long mix_rename (MAILSTREAM *stream,char *old,char *newname);
+int mix_rselect (struct direct *name);
+MAILSTREAM *mix_open (MAILSTREAM *stream);
+void mix_close (MAILSTREAM *stream,long options);
+void mix_abort (MAILSTREAM *stream);
+char *mix_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags);
+long mix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void mix_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+unsigned long *mix_sort (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
+ SORTPGM *pgm,long flags);
+THREADNODE *mix_thread (MAILSTREAM *stream,char *type,char *charset,
+ SEARCHPGM *spg,long flags);
+long mix_ping (MAILSTREAM *stream);
+void mix_check (MAILSTREAM *stream);
+long mix_expunge (MAILSTREAM *stream,char *sequence,long options);
+int mix_select (struct direct *name);
+int mix_msgfsort (const void *d1,const void *d2);
+long mix_addset (SEARCHSET **set,unsigned long start,unsigned long size);
+long mix_burp (MAILSTREAM *stream,MIXBURP *burp,unsigned long *reclaimed);
+long mix_burp_check (SEARCHSET *set,size_t size,char *file);
+long mix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,
+ long options);
+long mix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+long mix_append_msg (MAILSTREAM *stream,FILE *f,char *flags,MESSAGECACHE *delt,
+ STRING *msg,SEARCHSET *set,unsigned long seq);
+
+FILE *mix_parse (MAILSTREAM *stream,FILE **idxf,long iflags,long sflags);
+char *mix_meta_slurp (MAILSTREAM *stream,unsigned long *seq);
+long mix_meta_update (MAILSTREAM *stream);
+long mix_index_update (MAILSTREAM *stream,FILE *idxf,long flag);
+long mix_status_update (MAILSTREAM *stream,FILE *statf,long flag);
+FILE *mix_data_open (MAILSTREAM *stream,int *fd,long *size,
+ unsigned long newsize);
+FILE *mix_sortcache_open (MAILSTREAM *stream);
+long mix_sortcache_update (MAILSTREAM *stream,FILE **sortcache);
+char *mix_read_record (FILE *f,char *buf,unsigned long buflen,char *type);
+unsigned long mix_read_sequence (FILE *f);
+char *mix_dir (char *dst,char *name);
+char *mix_file (char *dst,char *dir,char *name);
+char *mix_file_data (char *dst,char *dir,unsigned long data);
+unsigned long mix_modseq (unsigned long oldseq);
+
+/* MIX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mixdriver = {
+ "mix", /* driver name */
+ /* driver flags */
+ DR_MAIL|DR_LOCAL|DR_NOFAST|DR_CRLF|DR_LOCKING|DR_DIRFMT|DR_MODSEQ,
+ (DRIVER *) NIL, /* next driver */
+ mix_valid, /* mailbox is valid for us */
+ mix_parameters, /* manipulate parameters */
+ mix_scan, /* scan mailboxes */
+ mix_list, /* find mailboxes */
+ mix_lsub, /* find subscribed mailboxes */
+ mix_subscribe, /* subscribe to mailbox */
+ mix_unsubscribe, /* unsubscribe from mailbox */
+ mix_create, /* create mailbox */
+ mix_delete, /* delete mailbox */
+ mix_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ mix_open, /* open mailbox */
+ mix_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mix_header, /* fetch message header only */
+ mix_text, /* fetch message body only */
+ NIL, /* fetch partial message test */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ mix_flag, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ mix_sort, /* sort messages */
+ mix_thread, /* thread messages */
+ mix_ping, /* ping mailbox to see if still alive */
+ mix_check, /* check for new messages */
+ mix_expunge, /* expunge deleted messages */
+ mix_copy, /* copy messages to another mailbox */
+ mix_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mixproto = {&mixdriver};
+
+/* MIX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mix_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return mix_isvalid (name,tmp) ? &mixdriver : NIL;
+}
+
+
+/* MIX mail test for valid mailbox
+ * Accepts: mailbox name
+ * buffer to return meta name
+ * Returns: T if valid, NIL otherwise, metadata name written in both cases
+ */
+
+long mix_isvalid (char *name,char *meta)
+{
+ char dir[MAILTMPLEN];
+ struct stat sbuf;
+ /* validate name as directory */
+ if (!(errno = ((strlen (name) > NETMAXMBX) ? ENAMETOOLONG : NIL)) &&
+ *mix_dir (dir,name) && mix_file (meta,dir,MIXMETA) &&
+ !stat (dir,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) {
+ /* name is directory; is it mix? */
+ if (!stat (meta,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFREG))
+ return LONGT;
+ else errno = NIL; /* directory but not mix */
+ }
+ return NIL;
+}
+
+/* MIX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mix_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = mailboxfile ((char *) value,"~/INBOX");
+ break;
+ case GET_DIRFMTTEST:
+ ret = (void *) mix_dirfmttest;
+ break;
+ case GET_SCANCONTENTS:
+ ret = (void *) mix_scan_contents;
+ break;
+ case SET_ONETIMEEXPUNGEATPING:
+ if (value) ((MIXLOCAL *) ((MAILSTREAM *) value)->local)->expok = T;
+ case GET_ONETIMEEXPUNGEATPING:
+ if (value) ret = (void *)
+ (((MIXLOCAL *) ((MAILSTREAM *) value)->local)->expok ? VOIDT : NIL);
+ break;
+ }
+ return ret;
+}
+
+
+/* MIX test for directory format internal node
+ * Accepts: candidate node name
+ * Returns: T if internal name, NIL otherwise
+ */
+
+long mix_dirfmttest (char *name)
+{
+ /* belongs to MIX if starts with .mix */
+ return strncmp (name,MIXNAME,sizeof (MIXNAME) - 1) ? NIL : LONGT;
+}
+
+/* MIX mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* MIX scan mailbox for contents
+ * Accepts: mailbox name
+ * desired contents
+ * contents size
+ * file size (ignored)
+ * Returns: NIL if contents not found, T if found
+ */
+
+long mix_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz)
+{
+ long i,nfiles;
+ void *a;
+ char *s;
+ long ret = NIL;
+ size_t namelen = strlen (name);
+ struct stat sbuf;
+ struct direct **names = NIL;
+ if ((nfiles = scandir (name,&names,mix_select,mix_msgfsort)) > 0)
+ for (i = 0; i < nfiles; ++i) {
+ if (!ret) {
+ sprintf (s = (char *) fs_get (namelen + strlen (names[i]->d_name) + 2),
+ "%s/%s",name,names[i]->d_name);
+ if (!stat (s,&sbuf) && (csiz <= sbuf.st_size))
+ ret = dummy_scan_contents (s,contents,csiz,sbuf.st_size);
+ fs_give ((void **) &s);
+ }
+ fs_give ((void **) &names[i]);
+ }
+ /* free directory list */
+ if (a = (void *) names) fs_give ((void **) &a);
+ return ret;
+}
+
+/* MIX list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mix_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* MIX list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mix_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* MIX mail subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long mix_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return sm_subscribe (mailbox);
+}
+
+
+/* MIX mail unsubscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to delete from subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long mix_unsubscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return sm_unsubscribe (mailbox);
+}
+
+/* MIX mail create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mix_create (MAILSTREAM *stream,char *mailbox)
+{
+ DRIVER *test;
+ FILE *f;
+ int c,i;
+ char *t,tmp[MAILTMPLEN],file[MAILTMPLEN];
+ char *s = strrchr (mailbox,'/');
+ unsigned long now = time (NIL);
+ long ret = NIL;
+ /* always create \NoSelect if trailing / */
+ if (s && !s[1]) return dummy_create (stream,mailbox);
+ /* validate name */
+ if (mix_dirfmttest (s ? s + 1 : mailbox))
+ sprintf(tmp,"Can't create mailbox %.80s: invalid MIX-format name",mailbox);
+ /* must not already exist */
+ else if ((test = mail_valid (NIL,mailbox,NIL)) &&
+ strcmp (test->name,"dummy"))
+ sprintf (tmp,"Can't create mailbox %.80s: mailbox already exists",mailbox);
+ /* create directory and metadata */
+ else if (!dummy_create_path (stream,
+ mix_file (file,mix_dir (tmp,mailbox),MIXMETA),
+ get_dir_protection (mailbox)))
+ sprintf (tmp,"Can't create mailbox %.80s: %.80s",mailbox,strerror (errno));
+ else if (!(f = fopen (file,"w")))
+ sprintf (tmp,"Can't re-open metadata %.80s: %.80s",mailbox,
+ strerror (errno));
+ else { /* success, write initial metadata */
+ fprintf (f,SEQFMT,now);
+ fprintf (f,MTAFMT,now,0,now);
+ for (i = 0, c = 'K'; (i < NUSERFLAGS) &&
+ (t = (stream && stream->user_flags[i]) ? stream->user_flags[i] :
+ default_user_flag (i)) && *t; ++i) {
+ putc (c,f); /* write another keyword */
+ fputs (t,f);
+ c = ' '; /* delimiter is now space */
+ }
+ fclose (f);
+ set_mbx_protections (mailbox,file);
+ /* point to suffix */
+ s = file + strlen (file) - (sizeof (MIXMETA) - 1);
+ strcpy (s,MIXINDEX); /* create index */
+ if (!dummy_create_path (stream,file,get_dir_protection (mailbox)))
+ sprintf (tmp,"Can't create mix mailbox index: %.80s",strerror (errno));
+ else {
+ set_mbx_protections (mailbox,file);
+ strcpy (s,MIXSTATUS); /* create status */
+ if (!dummy_create_path (stream,file,get_dir_protection (mailbox)))
+ sprintf (tmp,"Can't create mix mailbox status: %.80s",
+ strerror (errno));
+ else {
+ set_mbx_protections (mailbox,file);
+ sprintf (s,"%08lx",now);/* message file */
+ if (!dummy_create_path (stream,file,get_dir_protection (mailbox)))
+ sprintf (tmp,"Can't create mix mailbox data: %.80s",
+ strerror (errno));
+ else {
+ set_mbx_protections (mailbox,file);
+ ret = LONGT; /* declare success at this point */
+ }
+ }
+ }
+ }
+ if (!ret) MM_LOG (tmp,ERROR); /* some error */
+ return ret;
+}
+
+/* MIX mail delete mailbox
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mix_delete (MAILSTREAM *stream,char *mailbox)
+{
+ DIR *dirp;
+ struct direct *d;
+ int fd = -1;
+ char *s,tmp[MAILTMPLEN];
+ if (!mix_isvalid (mailbox,tmp))
+ sprintf (tmp,"Can't delete mailbox %.80s: no such mailbox",mailbox);
+ else if (((fd = open (tmp,O_RDWR,NIL)) < 0) || flock (fd,LOCK_EX|LOCK_NB))
+ sprintf (tmp,"Can't lock mailbox for delete: %.80s",mailbox);
+ /* delete metadata */
+ else if (unlink (tmp)) sprintf (tmp,"Can't delete mailbox %.80s index: %80s",
+ mailbox,strerror (errno));
+ else {
+ close (fd); /* close descriptor on deleted metadata */
+ /* get directory name */
+ *(s = strrchr (tmp,'/')) = '\0';
+ if (dirp = opendir (tmp)) { /* open directory */
+ *s++ = '/'; /* restore delimiter */
+ /* massacre messages */
+ while (d = readdir (dirp)) if (mix_dirfmttest (d->d_name)) {
+ strcpy (s,d->d_name); /* make path */
+ unlink (tmp); /* sayonara */
+ }
+ closedir (dirp); /* flush directory */
+ *(s = strrchr (tmp,'/')) = '\0';
+ if (rmdir (tmp)) { /* try to remove the directory */
+ sprintf (tmp,"Can't delete name %.80s: %.80s",
+ mailbox,strerror (errno));
+ MM_LOG (tmp,WARN);
+ }
+ }
+ return T; /* always success */
+ }
+ if (fd >= 0) close (fd); /* close any descriptor on metadata */
+ MM_LOG (tmp,ERROR); /* something failed */
+ return NIL;
+}
+
+/* MIX mail rename mailbox
+ * Accepts: MIX mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long mix_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ char c,*s,tmp[MAILTMPLEN],tmp1[MAILTMPLEN];
+ struct stat sbuf;
+ int fd = -1;
+ if (!mix_isvalid (old,tmp))
+ sprintf (tmp,"Can't rename mailbox %.80s: no such mailbox",old);
+ else if (((fd = open (tmp,O_RDWR,NIL)) < 0) || flock (fd,LOCK_EX|LOCK_NB))
+ sprintf (tmp,"Can't lock mailbox for rename: %.80s",old);
+ else if (mix_dirfmttest ((s = strrchr (newname,'/')) ? s + 1 : newname))
+ sprintf (tmp,"Can't rename to mailbox %.80s: invalid MIX-format name",
+ newname);
+ /* new mailbox name must not be valid */
+ else if (mix_isvalid (newname,tmp))
+ sprintf (tmp,"Can't rename to mailbox %.80s: destination already exists",
+ newname);
+ else {
+ mix_dir (tmp,old); /* build old directory name */
+ mix_dir (tmp1,newname); /* and new directory name */
+ /* easy if not INBOX */
+ if (compare_cstring (old,"INBOX")) {
+ /* found superior to destination name? */
+ if (s = strrchr (tmp1,'/')) {
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (tmp1,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,tmp1,get_dir_protection (newname)))
+ return NIL;
+ *s = c; /* restore full name */
+ }
+ if (!rename (tmp,tmp1)) {
+ close (fd); /* close descriptor on metadata */
+ return LONGT;
+ }
+ }
+
+ /* RFC 3501 requires this */
+ else if (dummy_create_path (stream,strcat (tmp1,"/"),
+ get_dir_protection (newname))) {
+ void *a;
+ int i,n,lasterror;
+ char *src,*dst;
+ struct direct **names = NIL;
+ size_t srcl = strlen (tmp);
+ size_t dstl = strlen (tmp1);
+ /* rename each mix file to new directory */
+ for (i = lasterror = 0,n = scandir (tmp,&names,mix_rselect,alphasort);
+ i < n; ++i) {
+ size_t len = strlen (names[i]->d_name);
+ sprintf (src = (char *) fs_get (srcl + len + 2),"%s/%s",
+ tmp,names[i]->d_name);
+ sprintf (dst = (char *) fs_get (dstl + len + 1),"%s%s",
+ tmp1,names[i]->d_name);
+ if (rename (src,dst)) lasterror = errno;
+ fs_give ((void **) &src);
+ fs_give ((void **) &dst);
+ fs_give ((void **) &names[i]);
+ }
+ /* free directory list */
+ if (a = (void *) names) fs_give ((void **) &a);
+ if (lasterror) errno = lasterror;
+ else {
+ close (fd); /* close descriptor on metadata */
+ return mix_create (NIL,"INBOX");
+ }
+ }
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %.80s",
+ old,newname,strerror (errno));
+ }
+ if (fd >= 0) close (fd); /* close any descriptor on metadata */
+ MM_LOG (tmp,ERROR); /* something failed */
+ return NIL;
+}
+
+
+/* MIX test for mix name
+ * Accepts: candidate directory name
+ * Returns: T if mix file name, NIL otherwise
+ */
+
+int mix_rselect (struct direct *name)
+{
+ return mix_dirfmttest (name->d_name);
+}
+
+/* MIX mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mix_open (MAILSTREAM *stream)
+{
+ short silent;
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return user_flags (&mixproto);
+ if (stream->local) fatal ("mix recycle stream");
+ stream->local = memset (fs_get (sizeof (MIXLOCAL)),0,sizeof (MIXLOCAL));
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ /* make temporary buffer */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ /* set stream->mailbox to be directory name */
+ mix_dir (LOCAL->buf,stream->mailbox);
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (LOCAL->buf);
+ LOCAL->msgfd = -1; /* currently no file open */
+ if (!(((!stream->rdonly && /* open metadata file */
+ ((LOCAL->mfd = open (mix_file (LOCAL->buf,stream->mailbox,MIXMETA),
+ O_RDWR,NIL)) >= 0)) ||
+ ((stream->rdonly = T) &&
+ ((LOCAL->mfd = open (mix_file (LOCAL->buf,stream->mailbox,MIXMETA),
+ O_RDONLY,NIL)) >= 0))) &&
+ !flock (LOCAL->mfd,LOCK_SH))) {
+ MM_LOG ("Error opening mix metadata file",ERROR);
+ mix_abort (stream);
+ stream = NIL; /* open fails */
+ }
+ else { /* metadata open, complete open */
+ LOCAL->index = cpystr (mix_file (LOCAL->buf,stream->mailbox,MIXINDEX));
+ LOCAL->status = cpystr (mix_file (LOCAL->buf,stream->mailbox,MIXSTATUS));
+ LOCAL->sortcache = cpystr (mix_file (LOCAL->buf,stream->mailbox,
+ MIXSORTCACHE));
+ stream->sequence++; /* bump sequence number */
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ if (silent = stream->silent) LOCAL->internal = T;
+ stream->silent = T;
+ if (mix_ping (stream)) { /* do initial ping */
+ /* try burping in case we are exclusive */
+ if (!stream->rdonly) mix_expunge (stream,"",NIL);
+ if (!(stream->nmsgs || stream->silent))
+ MM_LOG ("Mailbox is empty",(long) NIL);
+ stream->silent = silent; /* now notify upper level */
+ mail_exists (stream,stream->nmsgs);
+ stream->perm_seen = stream->perm_deleted = stream->perm_flagged =
+ stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ stream->kwd_create = /* can we create new user flags? */
+ (stream->user_flags[NUSERFLAGS-1] || stream->rdonly) ? NIL : T;
+ }
+ else { /* got murdelyzed in ping */
+ mix_abort (stream);
+ stream = NIL;
+ }
+ }
+ return stream; /* return stream to caller */
+}
+
+/* MIX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mix_close (MAILSTREAM *stream,long options)
+{
+ if (LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ /* burp-only or expunge */
+ mix_expunge (stream,(options & CL_EXPUNGE) ? NIL : "",NIL);
+ mix_abort (stream);
+ stream->silent = silent; /* reset silent state */
+ }
+}
+
+
+/* MIX mail abort stream
+ * Accepts: MAIL stream
+ */
+
+void mix_abort (MAILSTREAM *stream)
+{
+ if (LOCAL) { /* only if a file is open */
+ /* close current message file if open */
+ if (LOCAL->msgfd >= 0) close (LOCAL->msgfd);
+ /* close current metadata file if open */
+ if (LOCAL->mfd >= 0) close (LOCAL->mfd);
+ if (LOCAL->index) fs_give ((void **) &LOCAL->index);
+ if (LOCAL->status) fs_give ((void **) &LOCAL->status);
+ if (LOCAL->sortcache) fs_give ((void **) &LOCAL->sortcache);
+ /* free local scratch buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* MIX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *mix_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags)
+{
+ unsigned long i,j,k;
+ int fd;
+ char *s,tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ if (length) *length = 0; /* default return */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ elt = mail_elt (stream,msgno);/* get elt */
+ /* is message in current message file? */
+ if ((LOCAL->msgfd < 0) || (elt->private.spare.data != LOCAL->curmsg)) {
+ if (LOCAL->msgfd >= 0) close (LOCAL->msgfd);
+ if ((LOCAL->msgfd = open (mix_file_data (LOCAL->buf,stream->mailbox,
+ elt->private.spare.data),
+ O_RDONLY,NIL)) < 0) return "";
+ /* got file */
+ LOCAL->curmsg = elt->private.spare.data;
+ }
+ lseek (LOCAL->msgfd,elt->private.special.offset,L_SET);
+ /* size of special data and header */
+ j = elt->private.msg.header.offset + elt->private.msg.header.text.size;
+ if (j > LOCAL->buflen) { /* is buffer big enough? */
+ /* no, make one that is */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = j) + 1);
+ }
+ /* Maybe someday validate internaldate too */
+ /* slurp special data + header, validate */
+ if ((read (LOCAL->msgfd,LOCAL->buf,j) == j) &&
+ !strncmp (LOCAL->buf,MSGTOK,MSGTSZ) &&
+ (elt->private.uid == strtoul ((char *) LOCAL->buf + MSGTSZ,&s,16)) &&
+ (*s++ == ':') && (s = strchr (s,':')) &&
+ (k = strtoul (s+1,&s,16)) && (*s++ == ':') &&
+ (s < (char *) (LOCAL->buf + elt->private.msg.header.offset))) {
+ /* won, set offset and size of message */
+ i = elt->private.msg.header.offset;
+ *length = elt->private.msg.header.text.size;
+ if (k != elt->rfc822_size) {
+ sprintf (tmp,"Inconsistency in mix message size, uid=%lx (%lu != %lu)",
+ elt->private.uid,elt->rfc822_size,k);
+ MM_LOG (tmp,WARN);
+ }
+ }
+ else { /* document the problem */
+ LOCAL->buf[100] = '\0'; /* tie off buffer at no more than 100 octets */
+ /* or at newline, whichever is first */
+ if (s = strpbrk (LOCAL->buf,"\015\012")) *s = '\0';
+ sprintf (tmp,"Error reading mix message header, uid=%lx, s=%.0lx, h=%s",
+ elt->private.uid,elt->rfc822_size,LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ *length = i = j = 0; /* default to empty */
+ }
+ LOCAL->buf[j] = '\0'; /* tie off buffer at the end */
+ return (char *) LOCAL->buf + i;
+}
+
+/* MIX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ unsigned long i;
+ FDDATA d;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mail_elt (stream,msgno);
+ /* is message in current message file? */
+ if ((LOCAL->msgfd < 0) || (elt->private.spare.data != LOCAL->curmsg)) {
+ if (LOCAL->msgfd >= 0) close (LOCAL->msgfd);
+ if ((LOCAL->msgfd = open (mix_file_data (LOCAL->buf,stream->mailbox,
+ elt->private.spare.data),
+ O_RDONLY,NIL)) < 0) return NIL;
+ /* got file */
+ LOCAL->curmsg = elt->private.spare.data;
+ }
+ /* doing non-peek fetch? */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ FILE *idxf; /* yes, process metadata/index/status */
+ FILE *statf = mix_parse (stream,&idxf,NIL,LONGT);
+ elt->seen = T; /* mark as seen */
+ MM_FLAGS (stream,elt->msgno);
+ /* update status file if possible */
+ if (statf && !stream->rdonly) {
+ elt->private.mod = LOCAL->statusseq = mix_modseq (LOCAL->statusseq);
+ mix_status_update (stream,statf,NIL);
+ }
+ if (idxf) fclose (idxf); /* release index and status file */
+ if (statf) fclose (statf);
+ }
+ d.fd = LOCAL->msgfd; /* set up file descriptor */
+ /* offset of message text */
+ d.pos = elt->private.special.offset + elt->private.msg.header.offset +
+ elt->private.msg.header.text.size;
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE; /* chunk size */
+ INIT (bs,fd_string,&d,elt->rfc822_size - elt->private.msg.header.text.size);
+ return T;
+}
+
+/* MIX mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ */
+
+void mix_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned long i,uf,ffkey;
+ long f;
+ short nf;
+ FILE *idxf;
+ FILE *statf = mix_parse (stream,&idxf,NIL,LONGT);
+ unsigned long seq = mix_modseq (LOCAL->statusseq);
+ /* find first free key */
+ for (ffkey = 0; (ffkey < NUSERFLAGS) && stream->user_flags[ffkey]; ++ffkey);
+ /* parse sequence and flags */
+ if (((flags & ST_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) &&
+ ((f = mail_parse_flags (stream,flag,&uf)) || uf)) {
+ /* alter flags */
+ for (i = 1,nf = (flags & ST_SET) ? T : NIL; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ struct { /* old flags */
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.seen = elt->seen; old.deleted = elt->deleted;
+ old.flagged = elt->flagged; old.answered = elt->answered;
+ old.draft = elt->draft; old.user_flags = elt->user_flags;
+ if (f&fSEEN) elt->seen = nf;
+ if (f&fDELETED) elt->deleted = nf;
+ if (f&fFLAGGED) elt->flagged = nf;
+ if (f&fANSWERED) elt->answered = nf;
+ if (f&fDRAFT) elt->draft = nf;
+ /* user flags */
+ if (flags & ST_SET) elt->user_flags |= uf;
+ else elt->user_flags &= ~uf;
+ if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
+ (old.flagged != elt->flagged) ||
+ (old.answered != elt->answered) || (old.draft != elt->draft) ||
+ (old.user_flags != elt->user_flags)) {
+ if (!stream->rdonly) elt->private.mod = LOCAL->statusseq = seq;
+ MM_FLAGS (stream,elt->msgno);
+ }
+ }
+ /* update status file after change */
+ if (statf && (seq == LOCAL->statusseq))
+ mix_status_update (stream,statf,NIL);
+ /* update metadata if created a keyword */
+ if ((ffkey < NUSERFLAGS) && stream->user_flags[ffkey] &&
+ !mix_meta_update (stream))
+ MM_LOG ("Error updating mix metadata after keyword creation",ERROR);
+ }
+ if (statf) fclose (statf); /* release status file if still open */
+ if (idxf) fclose (idxf); /* release index file */
+}
+
+/* MIX mail sort messages
+ * Accepts: mail stream
+ * character set
+ * search program
+ * sort program
+ * option flags
+ * Returns: vector of sorted message sequences or NIL if error
+ */
+
+unsigned long *mix_sort (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
+ SORTPGM *pgm,long flags)
+{
+ unsigned long *ret;
+ FILE *sortcache = mix_sortcache_open (stream);
+ ret = mail_sort_msgs (stream,charset,spg,pgm,flags);
+ mix_sortcache_update (stream,&sortcache);
+ return ret;
+}
+
+
+/* MIX mail thread messages
+ * Accepts: mail stream
+ * thread type
+ * character set
+ * search program
+ * option flags
+ * Returns: thread node tree or NIL if error
+ */
+
+THREADNODE *mix_thread (MAILSTREAM *stream,char *type,char *charset,
+ SEARCHPGM *spg,long flags)
+{
+ THREADNODE *ret;
+ FILE *sortcache = mix_sortcache_open (stream);
+ ret = mail_thread_msgs (stream,type,charset,spg,flags,mail_sort_msgs);
+ mix_sortcache_update (stream,&sortcache);
+ return ret;
+}
+
+/* MIX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+static int snarfing = 0; /* lock against recursive snarfing */
+
+long mix_ping (MAILSTREAM *stream)
+{
+ FILE *idxf,*statf;
+ struct stat sbuf;
+ STRING msg;
+ MESSAGECACHE *elt;
+ int mfd,ifd,sfd;
+ unsigned long i,msglen;
+ char *message,date[MAILTMPLEN],flags[MAILTMPLEN];
+ MAILSTREAM *sysibx = NIL;
+ long ret = NIL;
+ long snarfok = LONGT;
+ /* time to snarf? */
+ if (stream->inbox && !stream->rdonly && !snarfing &&
+ (time (0) >= (LOCAL->lastsnarf +
+ (time_t) mail_parameters (NIL,GET_SNARFINTERVAL,NIL)))) {
+ appenduid_t au = (appenduid_t) mail_parameters (NIL,GET_APPENDUID,NIL);
+ copyuid_t cu = (copyuid_t) mail_parameters (NIL,GET_COPYUID,NIL);
+ MM_CRITICAL (stream); /* go critical */
+ snarfing = T; /* don't recursively snarf */
+ /* disable APPENDUID/COPYUID callbacks */
+ mail_parameters (NIL,SET_APPENDUID,NIL);
+ mail_parameters (NIL,SET_COPYUID,NIL);
+ /* sizes match and anything in sysinbox? */
+ if (!stat (sysinbox (),&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFREG) &&
+ sbuf.st_size && (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) &&
+ !sysibx->rdonly && sysibx->nmsgs) {
+ /* for each message in sysibx mailbox */
+ for (i = 1; snarfok && (i <= sysibx->nmsgs); ++i)
+ if (!(elt = mail_elt (sysibx,i))->deleted &&
+ (message = mail_fetch_message (sysibx,i,&msglen,FT_PEEK)) &&
+ msglen) {
+ mail_date (date,elt); /* make internal date string */
+ /* make flag string */
+ flags[0] = flags[1] = '\0';
+ if (elt->seen) strcat (flags," \\Seen");
+ if (elt->flagged) strcat (flags," \\Flagged");
+ if (elt->answered) strcat (flags," \\Answered");
+ if (elt->draft) strcat (flags," \\Draft");
+ flags[0] = '(';
+ strcat (flags,")");
+ INIT (&msg,mail_string,message,msglen);
+ if (snarfok = mail_append_full (stream,"INBOX",flags,date,&msg)) {
+ char sequence[15];
+ sprintf (sequence,"%lu",i);
+ mail_flag (sysibx,sequence,"\\Deleted",ST_SET);
+ }
+ }
+
+ /* now expunge all those messages */
+ if (snarfok) mail_expunge (sysibx);
+ else {
+ sprintf (LOCAL->buf,"Can't copy new mail at message: %lu",i - 1);
+ MM_LOG (LOCAL->buf,WARN);
+ }
+ }
+ if (sysibx) mail_close (sysibx);
+ /* reenable APPENDUID/COPYUID */
+ mail_parameters (NIL,SET_APPENDUID,(void *) au);
+ mail_parameters (NIL,SET_COPYUID,(void *) cu);
+ snarfing = NIL; /* no longer snarfing */
+ MM_NOCRITICAL (stream); /* release critical */
+ LOCAL->lastsnarf = time (0);/* note time of last snarf */
+ }
+ /* expunging OK if global flag set */
+ if (mail_parameters (NIL,GET_EXPUNGEATPING,NIL)) LOCAL->expok = T;
+ /* process metadata/index/status */
+ if (statf = mix_parse (stream,&idxf,LONGT,
+ (LOCAL->internal ? NIL : LONGT))) {
+ fclose (statf); /* just close the status file */
+ ret = LONGT; /* declare success */
+ }
+ if (idxf) fclose (idxf); /* release index file */
+ LOCAL->expok = NIL; /* expunge no longer OK */
+ if (!ret) mix_abort (stream); /* murdelyze stream if ping fails */
+ return ret;
+}
+
+
+/* MIX mail checkpoint mailbox (burp only)
+ * Accepts: MAIL stream
+ */
+
+void mix_check (MAILSTREAM *stream)
+{
+ if (stream->rdonly) /* won't do on readonly files! */
+ MM_LOG ("Checkpoint ignored on readonly mailbox",NIL);
+ /* do burp-only expunge action */
+ if (mix_expunge (stream,"",NIL)) MM_LOG ("Check completed",(long) NIL);
+}
+
+/* MIX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL, empty string for burp only
+ * expunge options
+ * Returns: T on success, NIL if failure
+ */
+
+long mix_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ FILE *idxf = NIL;
+ FILE *statf = NIL;
+ MESSAGECACHE *elt;
+ int ifd,sfd;
+ long ret;
+ unsigned long i;
+ unsigned long nexp = 0;
+ unsigned long reclaimed = 0;
+ int burponly = (sequence && !*sequence);
+ LOCAL->expok = T; /* expunge during ping is OK */
+ if (!(ret = burponly || !sequence ||
+ ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) || stream->rdonly);
+ /* read index and open status exclusive */
+ else if (statf = mix_parse (stream,&idxf,LONGT,
+ LOCAL->internal ? NIL : LONGT)) {
+ /* expunge unless just burping */
+ if (!burponly) for (i = 1; i <= stream->nmsgs;) {
+ elt = mail_elt (stream,i);/* need to expunge this message? */
+ if (sequence ? elt->sequence : elt->deleted) {
+ ++nexp; /* yes, make it so */
+ mail_expunged (stream,i);
+ }
+ else ++i; /* otherwise advance to next message */
+ }
+
+ /* burp if can get exclusive access */
+ if (!flock (LOCAL->mfd,LOCK_EX|LOCK_NB)) {
+ void *a;
+ struct direct **names = NIL;
+ long nfiles = scandir (stream->mailbox,&names,mix_select,mix_msgfsort);
+ if (nfiles > 0) { /* if have message files */
+ MIXBURP *burp,*cur;
+ /* initialize burp list */
+ for (i = 0, burp = cur = NIL; i < nfiles; ++i) {
+ MIXBURP *nxt = (MIXBURP *) memset (fs_get (sizeof (MIXBURP)),0,
+ sizeof (MIXBURP));
+ /* another file found */
+ if (cur) cur = cur->next = nxt;
+ else cur = burp = nxt;
+ cur->name = names[i]->d_name;
+ cur->fileno = strtoul (cur->name + sizeof (MIXNAME) - 1,NIL,16);
+ cur->tail = &cur->set;
+ fs_give ((void **) &names[i]);
+ }
+ /* now load ranges */
+ for (i = 1, cur = burp; ret && (i <= stream->nmsgs); i++) {
+ /* is this message in current set? */
+ elt = mail_elt (stream,i);
+ if (cur && (elt->private.spare.data != cur->fileno)) {
+ /* restart if necessary */
+ if (elt->private.spare.data < cur->fileno) cur = burp;
+ /* hunt for appropriate mailbox */
+ while (cur && (elt->private.spare.data > cur->fileno))
+ cur = cur->next;
+ /* ought to have found it now... */
+ if (cur && (elt->private.spare.data != cur->fileno)) cur = NIL;
+ }
+ /* if found, add to set */
+ if (cur) ret = mix_addset (&cur->tail,elt->private.special.offset,
+ elt->private.msg.header.offset +
+ elt->rfc822_size);
+ else { /* uh-oh */
+ sprintf (LOCAL->buf,"Can't locate mix message file %.08lx",
+ elt->private.spare.data);
+ MM_LOG (LOCAL->buf,ERROR);
+ ret = NIL;
+ }
+ }
+ if (ret) /* if no errors, burp all files */
+ for (cur = burp; ret && cur; cur = cur->next) {
+ /* if non-empty, burp it */
+ if (cur->set.last) ret = mix_burp (stream,cur,&reclaimed);
+ /* empty, delete it unless new msg file */
+ else if (mix_file_data (LOCAL->buf,stream->mailbox,cur->fileno) &&
+ ((cur->fileno == LOCAL->newmsg) ?
+ truncate (LOCAL->buf,0) : unlink (LOCAL->buf))) {
+ sprintf (LOCAL->buf,
+ "Can't delete empty message file %.80s: %.80s",
+ cur->name,strerror (errno));
+ MM_LOG (LOCAL->buf,WARN);
+ }
+ }
+ }
+ else MM_LOG ("No mix message files found during expunge",WARN);
+ /* free directory list */
+ if (a = (void *) names) fs_give ((void **) &a);
+ }
+
+ /* either way, re-acquire shared lock */
+ if (flock (LOCAL->mfd,LOCK_SH|LOCK_NB))
+ fatal ("Unable to re-acquire metadata shared lock!");
+ /* Do this step even if ret is NIL (meaning some burp problem)! */
+ if (nexp || reclaimed) { /* rewrite index and status if changed */
+ LOCAL->indexseq = mix_modseq (LOCAL->indexseq);
+ if (mix_index_update (stream,idxf,NIL)) {
+ LOCAL->statusseq = mix_modseq (LOCAL->statusseq);
+ /* set failure if update fails */
+ ret = mix_status_update (stream,statf,NIL);
+ }
+ }
+ }
+ if (statf) fclose (statf); /* close status if still open */
+ if (idxf) fclose (idxf); /* close index if still open */
+ LOCAL->expok = NIL; /* cancel expok */
+ if (ret) { /* only if success */
+ char *s = NIL;
+ if (nexp) sprintf (s = LOCAL->buf,"Expunged %lu messages",nexp);
+ else if (reclaimed)
+ sprintf (s=LOCAL->buf,"Reclaimed %lu bytes of expunged space",reclaimed);
+ else if (!burponly)
+ s = stream->rdonly ? "Expunge ignored on readonly mailbox" :
+ "No messages deleted, so no update needed";
+ if (s) MM_LOG (s,(long) NIL);
+ }
+ return ret;
+}
+
+/* MIX test for message file name
+ * Accepts: candidate directory name
+ * Returns: T if message file name, NIL otherwise
+ *
+ * ".mix" with no suffix was used by experimental versions
+ */
+
+int mix_select (struct direct *name)
+{
+ char c,*s;
+ /* make sure name has prefix */
+ if (mix_dirfmttest (name->d_name)) {
+ for (c = *(s = name->d_name + sizeof (MIXNAME) - 1); c && isxdigit (c);
+ c = *s++);
+ if (!c) return T; /* all-hex or no suffix */
+ }
+ return NIL; /* not suffix or non-hex */
+}
+
+
+/* MIX msg file name comparision
+ * Accepts: first candidate directory entry
+ * second candidate directory entry
+ * Returns: -1 if d1 < d2, 0 if d1 == d2, 1 d1 > d2
+ */
+
+int mix_msgfsort (const void *d1,const void *d2)
+{
+ char *n1 = (*(struct direct **) d1)->d_name + sizeof (MIXNAME) - 1;
+ char *n2 = (*(struct direct **) d2)->d_name + sizeof (MIXNAME) - 1;
+ return compare_ulong (*n1 ? strtoul (n1,NIL,16) : 0,
+ *n2 ? strtoul (n2,NIL,16) : 0);
+}
+
+
+/* MIX add a range to a set
+ * Accepts: pointer to set to add
+ * start of set
+ * size of set
+ * Returns: T if success, set updated, NIL otherwise
+ */
+
+long mix_addset (SEARCHSET **set,unsigned long start,unsigned long size)
+{
+ SEARCHSET *s = *set;
+ if (start < s->last) { /* sanity check */
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Backwards-running mix index %lu < %lu",start,s->last);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* range initially empty? */
+ if (!s->last) s->first = start;
+ else if (start > s->last) /* no, start new range if can't append */
+ (*set = s = s->next = mail_newsearchset ())->first = start;
+ s->last = start + size; /* end of current range */
+ return LONGT;
+}
+
+/* MIX burp message file
+ * Accepts: MAIL stream
+ * current burp block for this message
+ * Returns: T if successful, NIL if failed
+ */
+
+static char *staterr = "Error in stat of mix message file %.80s: %.80s";
+static char *truncerr = "Error truncating mix message file %.80s: %.80s";
+
+long mix_burp (MAILSTREAM *stream,MIXBURP *burp,unsigned long *reclaimed)
+{
+ MESSAGECACHE *elt;
+ SEARCHSET *set;
+ struct stat sbuf;
+ off_t rpos,wpos;
+ size_t size,wsize,wpending,written;
+ int fd;
+ FILE *f;
+ void *s;
+ unsigned long i;
+ long ret = NIL;
+ /* build file name */
+ mix_file_data (LOCAL->buf,stream->mailbox,burp->fileno);
+ /* need to burp at start or multiple ranges? */
+ if (!burp->set.first && !burp->set.next) {
+ /* easy case, single range at start of file */
+ if (stat (LOCAL->buf,&sbuf)) {
+ sprintf (LOCAL->buf,staterr,burp->name,strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ }
+ /* is this range sane? */
+ else if (mix_burp_check (&burp->set,sbuf.st_size,LOCAL->buf)) {
+ /* if matches range then no burp needed! */
+ if (burp->set.last == sbuf.st_size) ret = LONGT;
+ /* just need to remove cruft at end */
+ else if (ret = !truncate (LOCAL->buf,burp->set.last))
+ *reclaimed += sbuf.st_size - burp->set.last;
+ else {
+ sprintf (LOCAL->buf,truncerr,burp->name,strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ }
+ }
+ }
+ /* have to do more work, get the file */
+ else if (((fd = open (LOCAL->buf,O_RDWR,NIL)) < 0) ||
+ !(f = fdopen (fd,"r+b"))) {
+ sprintf (LOCAL->buf,"Error opening mix message file %.80s: %.80s",
+ burp->name,strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ if (fd >= 0) close (fd); /* in case fdopen() failure */
+ }
+ else if (fstat (fd,&sbuf)) { /* get file size */
+ sprintf (LOCAL->buf,staterr,burp->name,strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ fclose (f);
+ }
+
+ /* only if sane */
+ else if (mix_burp_check (&burp->set,sbuf.st_size,LOCAL->buf)) {
+ /* make sure each range starts with token */
+ for (set = &burp->set; set; set = set->next)
+ if (fseek (f,set->first,SEEK_SET) ||
+ (fread (LOCAL->buf,1,MSGTSZ,f) != MSGTSZ) ||
+ strncmp (LOCAL->buf,MSGTOK,MSGTSZ)) {
+ sprintf (LOCAL->buf,"Bad message token in mix message file at %lu",
+ set->first);
+ MM_LOG (LOCAL->buf,ERROR);
+ fclose (f);
+ return NIL; /* burp fails for this file */
+ }
+ /* burp out each old message */
+ for (set = &burp->set, wpos = 0; set; set = set->next) {
+ /* move down this range */
+ for (rpos = set->first, size = set->last - set->first;
+ size; size -= wsize) {
+ if (rpos != wpos) { /* data to skip at start? */
+ /* no, slide this buffer down */
+ wsize = min (size,LOCAL->buflen);
+ /* failure is not an option here */
+ while (fseek (f,rpos,SEEK_SET) ||
+ (fread (LOCAL->buf,1,wsize,f) != wsize)) {
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+ /* nor here */
+ while (fseek (f,wpos,SEEK_SET)) {
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+ /* and especially not here */
+ for (s = LOCAL->buf, wpending = wsize; wpending; wpending -= written)
+ if (!(written = fwrite (LOCAL->buf,1,wpending,f))) {
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+ }
+ else wsize = size; /* nothing to skip, say we wrote it all */
+ rpos += wsize; wpos += wsize;
+ }
+ }
+
+ while (fflush (f)) { /* failure also not an option here... */
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+ if (ftruncate (fd,wpos)) { /* flush cruft at end of file */
+ sprintf (LOCAL->buf,truncerr,burp->name,strerror (errno));
+ MM_LOG (LOCAL->buf,WARN);
+ }
+ else *reclaimed += rpos - wpos;
+ ret = !fclose (f); /* close file */
+ /* slide down message positions in index */
+ for (i = 1,rpos = 0; i <= stream->nmsgs; ++i)
+ if ((elt = mail_elt (stream,i))->private.spare.data == burp->fileno) {
+ elt->private.special.offset = rpos;
+ rpos += elt->private.msg.header.offset + elt->rfc822_size;
+ }
+ /* debugging */
+ if (rpos != wpos) fatal ("burp size consistency check!");
+ }
+ return ret;
+}
+
+
+/* MIX burp sanity check to make sure not burping off end of file
+ * Accepts: burp set
+ * file size
+ * file name
+ * Returns: T if sane, NIL if insane
+ */
+
+long mix_burp_check (SEARCHSET *set,size_t size,char *file)
+{
+ do if (set->last > size) { /* sanity check */
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Unexpected short mix message file %.80s %lu < %lu",
+ file,size,set->last);
+ MM_LOG (tmp,ERROR);
+ return NIL; /* don't burp this file at all */
+ } while (set = set->next);
+ return LONGT;
+}
+
+/* MIX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if copy successful, else NIL
+ */
+
+long mix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ FDDATA d;
+ STRING st;
+ char tmp[2*MAILTMPLEN];
+ long ret = mix_isvalid (mailbox,LOCAL->buf);
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ MAILSTREAM *astream = NIL;
+ FILE *idxf = NIL;
+ FILE *msgf = NIL;
+ FILE *statf = NIL;
+ if (!ret) switch (errno) { /* make sure valid mailbox */
+ case NIL: /* no error in stat() */
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (tmp,"Not a MIX-format mailbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ break;
+ default: /* some stat() error */
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ break;
+ }
+ /* get sequence to copy */
+ else if (!(ret = ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))));
+ /* acquire stream to append */
+ else if (ret = ((astream = mail_open (NIL,mailbox,OP_SILENT)) &&
+ !astream->rdonly &&
+ (((MIXLOCAL *) astream->local)->expok = T) &&
+ (statf = mix_parse (astream,&idxf,LONGT,NIL))) ?
+ LONGT : NIL) {
+ int fd;
+ unsigned long i;
+ MESSAGECACHE *elt;
+ unsigned long newsize,hdrsize,size;
+ MIXLOCAL *local = (MIXLOCAL *) astream->local;
+ unsigned long seq = mix_modseq (local->metaseq);
+ /* make sure new modseq fits */
+ if (local->indexseq > seq) seq = local->indexseq + 1;
+ if (local->statusseq > seq) seq = local->statusseq + 1;
+ /* calculate size of per-message header */
+ sprintf (local->buf,MSRFMT,MSGTOK,0,0,0,0,0,0,0,'+',0,0,0);
+ hdrsize = strlen (local->buf);
+
+ MM_CRITICAL (stream); /* go critical */
+ astream->silent = T; /* no events here */
+ /* calculate size that will be added */
+ for (i = 1, newsize = 0; i <= stream->nmsgs; ++i)
+ if ((elt = mail_elt (stream,i))->sequence)
+ newsize += hdrsize + elt->rfc822_size;
+ /* open data file */
+ if (msgf = mix_data_open (astream,&fd,&size,newsize)) {
+ char *t;
+ unsigned long j,uid,uidv;
+ copyuid_t cu = (copyuid_t) mail_parameters (NIL,GET_COPYUID,NIL);
+ SEARCHSET *source = cu ? mail_newsearchset () : NIL;
+ SEARCHSET *dest = cu ? mail_newsearchset () : NIL;
+ for (i = 1,uid = uidv = 0; ret && (i <= stream->nmsgs); ++i)
+ if (((elt = mail_elt (stream,i))->sequence) && elt->rfc822_size) {
+ /* is message in current message file? */
+ if ((LOCAL->msgfd < 0) ||
+ (elt->private.spare.data != LOCAL->curmsg)) {
+ if (LOCAL->msgfd >= 0) close (LOCAL->msgfd);
+ if ((LOCAL->msgfd = open (mix_file_data (LOCAL->buf,
+ stream->mailbox,
+ elt->private.spare.data),
+ O_RDONLY,NIL)) >= 0)
+ LOCAL->curmsg = elt->private.spare.data;
+ }
+ if (LOCAL->msgfd < 0) ret = NIL;
+ else { /* got file */
+ d.fd = LOCAL->msgfd;/* set up file descriptor */
+ /* start of message */
+ d.pos = elt->private.special.offset +
+ elt->private.msg.header.offset;
+ d.chunk = LOCAL->buf;
+ d.chunksize = CHUNKSIZE;
+ INIT (&st,fd_string,&d,elt->rfc822_size);
+ /* init flag string */
+ tmp[0] = tmp[1] = '\0';
+ if (j = elt->user_flags) do
+ if ((t = stream->user_flags[find_rightmost_bit (&j)]) && *t)
+ strcat (strcat (tmp," "),t);
+ while (j);
+ if (elt->seen) strcat (tmp," \\Seen");
+ if (elt->deleted) strcat (tmp," \\Deleted");
+ if (elt->flagged) strcat (tmp," \\Flagged");
+ if (elt->answered) strcat (tmp," \\Answered");
+ if (elt->draft) strcat (tmp," \\Draft");
+ tmp[0] = '('; /* wrap list */
+ strcat (tmp,")");
+ /* if append OK, add to source set */
+ if ((ret = mix_append_msg (astream,msgf,tmp,elt,&st,dest,
+ seq)) && source)
+ mail_append_set (source,mail_uid (stream,i));
+ }
+ }
+
+ /* finish write if success */
+ if (ret && (ret = !fflush (msgf))) {
+ fclose (msgf); /* all good, close the msg file now */
+ /* write new metadata, index, and status */
+ local->metaseq = local->indexseq = local->statusseq = seq;
+ if (ret = (mix_meta_update (astream) &&
+ mix_index_update (astream,idxf,LONGT))) {
+ /* success, delete if doing a move */
+ if (options & CP_MOVE)
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ elt->deleted = T;
+ if (!stream->rdonly) elt->private.mod = LOCAL->statusseq = seq;
+ MM_FLAGS (stream,elt->msgno);
+ }
+ /* done with status file now */
+ mix_status_update (astream,statf,LONGT);
+ /* return sets if doing COPYUID */
+ if (cu) (*cu) (stream,mailbox,astream->uid_validity,source,dest);
+ source = dest = NIL; /* don't free these sets now */
+ }
+ }
+ else { /* error */
+ if (errno) { /* output error message if system call error */
+ sprintf (tmp,"Message copy failed: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ }
+ ftruncate (fd,size); /* revert file */
+ close (fd); /* make sure that fclose doesn't corrupt us */
+ fclose (msgf); /* free the stdio resources */
+ }
+ /* flush any sets remaining */
+ mail_free_searchset (&source);
+ mail_free_searchset (&dest);
+ }
+ else { /* message file open failed */
+ sprintf (tmp,"Error opening copy message file: %.80s",
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL;
+ }
+ MM_NOCRITICAL (stream);
+ }
+ else MM_LOG ("Can't open copy mailbox",ERROR);
+ if (statf) fclose (statf); /* close status if still open */
+ if (idxf) fclose (idxf); /* close index if still open */
+ /* finished with append stream */
+ if (astream) mail_close (astream);
+ return ret; /* return state */
+}
+
+/* MIX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ STRING *message;
+ char *flags,*date,tmp[MAILTMPLEN];
+ /* N.B.: can't use LOCAL->buf for tmp */
+ long ret = mix_isvalid (mailbox,tmp);
+ /* default stream to prototype */
+ if (!stream) stream = user_flags (&mixproto);
+ if (!ret) switch (errno) { /* if not valid mailbox */
+ case ENOENT: /* no such file? */
+ if (ret = compare_cstring (mailbox,"INBOX") ?
+ NIL : mix_create (NIL,"INBOX"))
+ break;
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ break;
+ default:
+ sprintf (tmp,"Not a MIX-format mailbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ break;
+ }
+
+ /* get first message */
+ if (ret && MM_APPEND (af) (stream,data,&flags,&date,&message)) {
+ MAILSTREAM *astream;
+ FILE *idxf = NIL;
+ FILE *msgf = NIL;
+ FILE *statf = NIL;
+ if (ret = ((astream = mail_open (NIL,mailbox,OP_SILENT)) &&
+ !astream->rdonly &&
+ (((MIXLOCAL *) astream->local)->expok = T) &&
+ (statf = mix_parse (astream,&idxf,LONGT,NIL))) ?
+ LONGT : NIL) {
+ int fd;
+ unsigned long size,hdrsize;
+ MESSAGECACHE elt;
+ MIXLOCAL *local = (MIXLOCAL *) astream->local;
+ unsigned long seq = mix_modseq (local->metaseq);
+ /* make sure new modseq fits */
+ if (local->indexseq > seq) seq = local->indexseq + 1;
+ if (local->statusseq > seq) seq = local->statusseq + 1;
+ /* calculate size of per-message header */
+ sprintf (local->buf,MSRFMT,MSGTOK,0,0,0,0,0,0,0,'+',0,0,0);
+ hdrsize = strlen (local->buf);
+ MM_CRITICAL (astream); /* go critical */
+ astream->silent = T; /* no events here */
+ /* open data file */
+ if (msgf = mix_data_open (astream,&fd,&size,hdrsize + SIZE (message))) {
+ appenduid_t au = (appenduid_t) mail_parameters (NIL,GET_APPENDUID,NIL);
+ SEARCHSET *dst = au ? mail_newsearchset () : NIL;
+ while (ret && message) {/* while good to go and have messages */
+ errno = NIL; /* in case one of these causes failure */
+ /* guard against zero-length */
+ if (!(ret = SIZE (message)))
+ MM_LOG ("Append of zero-length message",ERROR);
+ else if (date && !(ret = mail_parse_date (&elt,date))) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ MM_LOG (tmp,ERROR);
+ }
+ else {
+ if (!date) { /* if date not specified, use now */
+ internal_date (tmp);
+ mail_parse_date (&elt,tmp);
+ }
+ ret = mix_append_msg (astream,msgf,flags,&elt,message,dst,seq) &&
+ MM_APPEND (af) (stream,data,&flags,&date,&message);
+ }
+ }
+
+ /* finish write if success */
+ if (ret && (ret = !fflush (msgf))) {
+ fclose (msgf); /* all good, close the msg file now */
+ /* write new metadata, index, and status */
+ local->metaseq = local->indexseq = local->statusseq = seq;
+ if ((ret = (mix_meta_update (astream) &&
+ mix_index_update (astream,idxf,LONGT) &&
+ mix_status_update (astream,statf,LONGT))) && au) {
+ (*au) (mailbox,astream->uid_validity,dst);
+ dst = NIL; /* don't free this set now */
+ }
+ }
+ else { /* failure */
+ if (errno) { /* output error message if system call error */
+ sprintf (tmp,"Message append failed: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ }
+ ftruncate (fd,size); /* revert all writes to file*/
+ close (fd); /* make sure that fclose doesn't corrupt us */
+ fclose (msgf); /* free the stdio resources */
+ }
+ /* flush any set remaining */
+ mail_free_searchset (&dst);
+ }
+ else { /* message file open failed */
+ sprintf (tmp,"Error opening append message file: %.80s",
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL;
+ }
+ MM_NOCRITICAL (astream); /* release critical */
+ }
+ else MM_LOG ("Can't open append mailbox",ERROR);
+ if (statf) fclose (statf); /* close status if still open */
+ if (idxf) fclose (idxf); /* close index if still open */
+ if (astream) mail_close (astream);
+ }
+ return ret;
+}
+
+/* MIX mail append single message
+ * Accepts: MAIL stream
+ * flags for new message if non-NIL
+ * elt with source date if non-NIL
+ * stringstruct of message text
+ * searchset to place UID
+ * modseq of message
+ * Returns: T if success, NIL if failure
+ */
+
+long mix_append_msg (MAILSTREAM *stream,FILE *f,char *flags,MESSAGECACHE *delt,
+ STRING *msg,SEARCHSET *set,unsigned long seq)
+{
+ MESSAGECACHE *elt;
+ int c,cs;
+ unsigned long i,j,k,uf,hoff;
+ long sf;
+ stream->kwd_create = NIL; /* don't copy unknown keywords */
+ sf = mail_parse_flags (stream,flags,&uf);
+ /* swell the cache */
+ mail_exists (stream,++stream->nmsgs);
+ /* assign new UID from metadata */
+ (elt = mail_elt (stream,stream->nmsgs))->private.uid = ++stream->uid_last;
+ elt->private.mod = seq; /* set requested modseq in status */
+ elt->rfc822_size = SIZE (msg);/* copy message size and date to index */
+ elt->year = delt->year; elt->month = delt->month; elt->day = delt->day;
+ elt->hours = delt->hours; elt->minutes = delt->minutes;
+ elt->seconds = delt->seconds; elt->zoccident = delt->zoccident;
+ elt->zhours = delt->zhours; elt->zminutes = delt->zminutes;
+ /*
+ * Do NOT set elt->valid here! mix_status_update() uses it to determine
+ * whether a message should be marked as old.
+ */
+ if (sf&fSEEN) elt->seen = T; /* copy flags to status */
+ if (sf&fDELETED) elt->deleted = T;
+ if (sf&fFLAGGED) elt->flagged = T;
+ if (sf&fANSWERED) elt->answered = T;
+ if (sf&fDRAFT) elt->draft = T;
+ elt->user_flags |= uf;
+ /* message is in new message file */
+ elt->private.spare.data = LOCAL->newmsg;
+
+ /* offset to message internal header */
+ elt->private.special.offset = ftell (f);
+ /* build header for message */
+ fprintf (f,MSRFMT,MSGTOK,elt->private.uid,
+ elt->year + BASEYEAR,elt->month,elt->day,
+ elt->hours,elt->minutes,elt->seconds,
+ elt->zoccident ? '-' : '+',elt->zhours,elt->zminutes,
+ elt->rfc822_size);
+ /* offset to header from internal header */
+ elt->private.msg.header.offset = ftell (f) - elt->private.special.offset;
+ for (cs = 0; SIZE (msg); ) { /* copy message */
+ if (elt->private.msg.header.text.size) {
+ if (msg->cursize) /* blat entire chunk if have it */
+ for (j = msg->cursize; j; j -= k)
+ if (!(k = fwrite (msg->curpos,1,j,f))) return NIL;
+ SETPOS (msg,GETPOS (msg) + msg->cursize);
+ }
+ else { /* still searching for delimiter */
+ c = 0xff & SNX (msg); /* get source character */
+ if (putc (c,f) == EOF) return NIL;
+ switch (cs) { /* decide what to do based on state */
+ case 0: /* previous char ordinary */
+ if (c == '\015') cs = 1;/* advance if CR */
+ break;
+ case 1: /* previous CR, advance if LF */
+ cs = (c == '\012') ? 2 : 0;
+ break;
+ case 2: /* previous CRLF, advance if CR */
+ cs = (c == '\015') ? 3 : 0;
+ break;
+ case 3: /* previous CRLFCR, done if LF */
+ if (c == '\012') elt->private.msg.header.text.size =
+ elt->rfc822_size - SIZE (msg);
+ cs = 0; /* reset mechanism */
+ break;
+ }
+ }
+ }
+ /* if no delimiter, header is entire msg */
+ if (!elt->private.msg.header.text.size)
+ elt->private.msg.header.text.size = elt->rfc822_size;
+ /* add this message to set */
+ mail_append_set (set,elt->private.uid);
+ return LONGT; /* success */
+}
+
+/* MIX mail read metadata, index, and status
+ * Accepts: MAIL stream
+ * returned index file
+ * index file flags (non-NIL if want to add/remove messages)
+ * status file flags (non-NIL if want to update elt->valid and old)
+ * Returns: open status file, or NIL if failure
+ *
+ * Note that this routine can return an open index file even if it fails!
+ */
+
+static char *shortmsg =
+ "message %lu (UID=%.08lx) truncated by %lu byte(s) (%lu < %lu)";
+
+FILE *mix_parse (MAILSTREAM *stream,FILE **idxf,long iflags,long sflags)
+{
+ int fd;
+ unsigned long i;
+ char *s,*t;
+ struct stat sbuf;
+ FILE *statf = NIL;
+ short metarepairneeded = 0;
+ short indexrepairneeded = 0;
+ short silent = stream->silent;
+ *idxf = NIL; /* in case error */
+ /* readonly means no updates */
+ if (stream->rdonly) iflags = sflags = NIL;
+ /* open index file */
+ if ((fd = open (LOCAL->index,iflags ? O_RDWR : O_RDONLY,NIL)) < 0)
+ MM_LOG ("Error opening mix index file",ERROR);
+ /* acquire exclusive access and FILE */
+ else if (!flock (fd,iflags ? LOCK_EX : LOCK_SH) &&
+ !(*idxf = fdopen (fd,iflags ? "r+b" : "rb"))) {
+ MM_LOG ("Error obtaining stream on mix index file",ERROR);
+ flock (fd,LOCK_UN); /* relinquish lock */
+ close (fd);
+ }
+
+ /* slurp metadata */
+ else if (s = mix_meta_slurp (stream,&i)) {
+ unsigned long j = 0; /* non-zero if UIDVALIDITY/UIDLAST changed */
+ if (i != LOCAL->metaseq) { /* metadata changed? */
+ char *t,*k;
+ LOCAL->metaseq = i; /* note new metadata sequence */
+ while (s && *s) { /* parse entire metadata file */
+ /* locate end of line */
+ if (s = strstr (t = s,"\015\012")) {
+ *s = '\0'; /* tie off line */
+ s += 2; /* skip past CRLF */
+ switch (*t++) { /* parse line */
+ case 'V': /* UIDVALIDITY */
+ if (!isxdigit (*t) || !(i = strtoul (t,&t,16))) {
+ MM_LOG ("Error in mix metadata file UIDVALIDITY record",ERROR);
+ return NIL; /* give up */
+ }
+ if (i != stream->uid_validity) j = stream->uid_validity = i;
+ break;
+ case 'L': /* new UIDLAST */
+ if (!isxdigit (*t)) {
+ MM_LOG ("Error in mix metadata file UIDLAST record",ERROR);
+ return NIL; /* give up */
+ }
+ if ((i = strtoul (t,&t,16)) != stream->uid_last)
+ j = stream->uid_last = i;
+ break;
+ case 'N': /* new message file */
+ if (!isxdigit (*t)) {
+ MM_LOG ("Error in mix metadata file new msg record",ERROR);
+ return NIL; /* give up */
+ }
+ if ((i = strtoul (t,&t,16)) != stream->uid_last)
+ LOCAL->newmsg = i;
+ break;
+ case 'K': /* new keyword list */
+ for (i = 0; t && *t && (i < NUSERFLAGS); ++i) {
+ if (t = strchr (k = t,' ')) *t++ = '\0';
+ /* make sure keyword non-empty */
+ if (*k && (strlen (k) <= MAXUSERFLAG)) {
+ /* in case value changes (shouldn't happen) */
+ if (stream->user_flags[i] && strcmp (stream->user_flags[i],k)){
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"flag rename old=%.80s new=%.80s",
+ stream->user_flags[i],k);
+ MM_LOG (tmp,WARN);
+ fs_give ((void **) &stream->user_flags[i]);
+ }
+ if (!stream->user_flags[i]) stream->user_flags[i] = cpystr (k);
+ }
+ else break; /* empty keyword */
+ }
+ if ((i < NUSERFLAGS) && stream->user_flags[i]) {
+ MM_LOG ("Error in mix metadata file keyword record",ERROR);
+ return NIL; /* give up */
+ }
+ else if (i == NUSERFLAGS) stream->kwd_create = NIL;
+ break;
+ }
+ }
+ if (t && *t) { /* junk in line */
+ MM_LOG ("Error in mix metadata record",ERROR);
+ return NIL; /* give up */
+ }
+ }
+ }
+
+ /* get sequence */
+ if (!(i = mix_read_sequence (*idxf)) || (i < LOCAL->indexseq)) {
+ MM_LOG ("Error in mix index file sequence record",ERROR);
+ return NIL; /* give up */
+ }
+ /* sequence changed from last time? */
+ else if (j || (i > LOCAL->indexseq)) {
+ unsigned long uid,nmsgs,curfile,curfilesize,curpos;
+ char *t,*msg,tmp[MAILTMPLEN];
+ /* start with no messages */
+ curfile = curfilesize = curpos = nmsgs = 0;
+ /* update sequence iff expunging OK */
+ if (LOCAL->expok) LOCAL->indexseq = i;
+ /* get first elt */
+ while ((s = mix_read_record (*idxf,LOCAL->buf,LOCAL->buflen,"index")) &&
+ *s)
+ switch (*s) {
+ case ':': /* message record */
+ if (!(isxdigit (*++s) && (uid = strtoul (s,&t,16)))) msg = "UID";
+ else if (!((*t++ == ':') && isdigit (*t) && isdigit (t[1]) &&
+ isdigit (t[2]) && isdigit (t[3]) && isdigit (t[4]) &&
+ isdigit (t[5]) && isdigit (t[6]) && isdigit (t[7]) &&
+ isdigit (t[8]) && isdigit (t[9]) && isdigit (t[10]) &&
+ isdigit (t[11]) && isdigit (t[12]) && isdigit (t[13]) &&
+ ((t[14] == '+') || (t[14] == '-')) &&
+ isdigit (t[15]) && isdigit (t[16]) && isdigit (t[17]) &&
+ isdigit (t[18]))) msg = "internaldate";
+ else if ((*(s = t+19) != ':') || !isxdigit (*++s)) msg = "size";
+ else {
+ unsigned int y = (((*t - '0') * 1000) + ((t[1] - '0') * 100) +
+ ((t[2] - '0') * 10) + t[3] - '0') - BASEYEAR;
+ unsigned int m = ((t[4] - '0') * 10) + t[5] - '0';
+ unsigned int d = ((t[6] - '0') * 10) + t[7] - '0';
+ unsigned int hh = ((t[8] - '0') * 10) + t[9] - '0';
+ unsigned int mm = ((t[10] - '0') * 10) + t[11] - '0';
+ unsigned int ss = ((t[12] - '0') * 10) + t[13] - '0';
+ unsigned int z = (t[14] == '-') ? 1 : 0;
+ unsigned int zh = ((t[15] - '0') * 10) + t[16] - '0';
+ unsigned int zm = ((t[17] - '0') * 10) + t[18] - '0';
+ unsigned long size = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ unsigned long file = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ unsigned long pos = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ unsigned long hpos = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ unsigned long hsiz = strtoul (s,&s,16);
+ if (uid > stream->uid_last) {
+ sprintf (tmp,"mix index invalid UID (%08lx < %08lx)",
+ uid,stream->uid_last);
+ if (stream->rdonly) {
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ strcat (tmp,", repaired");
+ MM_LOG (tmp,WARN);
+ stream->uid_last = uid;
+ metarepairneeded = T;
+ }
+
+ /* ignore expansion values */
+ if (*s++ == ':') {
+ MESSAGECACHE *elt;
+ ++nmsgs; /* this is another mesage */
+ /* within current known range of messages? */
+ while (nmsgs <= stream->nmsgs) {
+ /* yes, get corresponding elt */
+ elt = mail_elt (stream,nmsgs);
+ /* existing message with matching data? */
+ if (uid == elt->private.uid) {
+ /* beware of Dracula's resurrection */
+ if (elt->private.ghost) {
+ sprintf (tmp,"mix index data unexpunged UID: %lx",
+ uid);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* also of static data changing */
+ if ((size != elt->rfc822_size) ||
+ (file != elt->private.spare.data) ||
+ (pos != elt->private.special.offset) ||
+ (hpos != elt->private.msg.header.offset) ||
+ (hsiz != elt->private.msg.header.text.size) ||
+ (y != elt->year) || (m != elt->month) ||
+ (d != elt->day) || (hh != elt->hours) ||
+ (mm != elt->minutes) || (ss != elt->seconds) ||
+ (z != elt->zoccident) || (zh != elt->zhours) ||
+ (zm != elt->zminutes)) {
+ sprintf (tmp,"mix index data mismatch: %lx",uid);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ break;
+ }
+ /* existing msg with lower UID is expunged */
+ else if (uid > elt->private.uid) {
+ if (LOCAL->expok) mail_expunged (stream,nmsgs);
+ else {/* message expunged, but not yet for us */
+ ++nmsgs;
+ elt->private.ghost = T;
+ }
+ }
+ else { /* unexpected message record */
+ sprintf (tmp,"mix index UID mismatch (%lx < %lx)",
+ uid,elt->private.uid);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ }
+
+ /* time to create a new message? */
+ if (nmsgs > stream->nmsgs) {
+ /* defer announcing until later */
+ stream->silent = T;
+ mail_exists (stream,nmsgs);
+ stream->silent = silent;
+ (elt = mail_elt (stream,nmsgs))->recent = T;
+ elt->private.uid = uid; elt->rfc822_size = size;
+ elt->private.spare.data = file;
+ elt->private.special.offset = pos;
+ elt->private.msg.header.offset = hpos;
+ elt->private.msg.header.text.size = hsiz;
+ elt->year = y; elt->month = m; elt->day = d;
+ elt->hours = hh; elt->minutes = mm;
+ elt->seconds = ss; elt->zoccident = z;
+ elt->zhours = zh; elt->zminutes = zm;
+ /* message in same file? */
+ if (curfile == file) {
+ if (pos < curpos) {
+ MESSAGECACHE *plt = mail_elt (stream,elt->msgno-1);
+ /* uh-oh, calculate delta? */
+ i = curpos - pos;
+ sprintf (tmp,shortmsg,plt->msgno,plt->private.uid,
+ i,pos,curpos);
+ /* possible to fix? */
+ if (!stream->rdonly && LOCAL->expok &&
+ (i < plt->rfc822_size)) {
+ plt->rfc822_size -= i;
+ if (plt->rfc822_size <
+ plt->private.msg.header.text.size)
+ plt->private.msg.header.text.size =
+ plt->rfc822_size;
+ strcat (tmp,", repaired");
+ indexrepairneeded = T;
+ }
+ MM_LOG (tmp,WARN);
+ }
+ }
+ else { /* new file, restart */
+ if (stat (mix_file_data (LOCAL->buf,stream->mailbox,
+ curfile = file),&sbuf)) {
+ sprintf (tmp,"Missing mix data file: %.500s",
+ LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ curfile = file;
+ curfilesize = sbuf.st_size;
+ }
+
+ /* position of message in file */
+ curpos = pos + elt->private.msg.header.offset +
+ elt->rfc822_size;
+ /* short file? */
+ if (curfilesize < curpos) {
+ /* uh-oh, calculate delta? */
+ i = curpos - curfilesize;
+ sprintf (tmp,shortmsg,elt->msgno,elt->private.uid,
+ i,curfilesize,curpos);
+ /* possible to fix? */
+ if (!stream->rdonly && LOCAL->expok &&
+ (i < elt->rfc822_size)) {
+ elt->rfc822_size -= i;
+ if (elt->rfc822_size <
+ elt->private.msg.header.text.size)
+ elt->private.msg.header.text.size =
+ elt->rfc822_size;
+ strcat (tmp,", repaired");
+ indexrepairneeded = T;
+ }
+ MM_LOG (tmp,WARN);
+ }
+ }
+ break;
+ }
+ else msg = "expansion";
+ }
+ else msg = "header size";
+ }
+ else msg = "header position";
+ }
+ else msg = "message position";
+ }
+ else msg = "file#";
+ }
+ sprintf (tmp,"Error in %s in mix index file: %.500s",msg,s);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Unknown record in mix index file: %.500s",s);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ if (!s) return NIL; /* barfage from mix_read_record() */
+ /* expunge trailing messages not in index */
+ if (LOCAL->expok) while (nmsgs < stream->nmsgs)
+ mail_expunged (stream,stream->nmsgs);
+ }
+
+ /* repair metadata and index if needed */
+ if ((metarepairneeded ? mix_meta_update (stream) : T) &&
+ (indexrepairneeded ? mix_index_update (stream,*idxf,NIL) : T)) {
+ MESSAGECACHE *elt;
+ int fd;
+ unsigned long uid,uf,sf,mod;
+ char *s;
+ int updatep = NIL;
+ /* open status file */
+ if ((fd = open (LOCAL->status,
+ stream->rdonly ? O_RDONLY : O_RDWR,NIL)) < 0)
+ MM_LOG ("Error opening mix status file",ERROR);
+ /* acquire exclusive access and FILE */
+ else if (!flock (fd,stream->rdonly ? LOCK_SH : LOCK_EX) &&
+ !(statf = fdopen (fd,stream->rdonly ? "rb" : "r+b"))) {
+ MM_LOG ("Error obtaining stream on mix status file",ERROR);
+ flock (fd,LOCK_UN); /* relinquish lock */
+ close (fd);
+ }
+ /* get sequence */
+ else if (!(i = mix_read_sequence (statf)) ||
+ ((i < LOCAL->statusseq) && stream->nmsgs && (i != 1))) {
+ sprintf (LOCAL->buf,
+ "Error in mix status sequence record, i=%lx, seq=%lx",
+ i,LOCAL->statusseq);
+ MM_LOG (LOCAL->buf,ERROR);
+ }
+ /* sequence changed from last time? */
+ else if (i != LOCAL->statusseq) {
+ /* update sequence, get first elt */
+ if (i > LOCAL->statusseq) LOCAL->statusseq = i;
+ if (stream->nmsgs) {
+ elt = mail_elt (stream,i = 1);
+
+ /* read message records */
+ while ((t = s = mix_read_record (statf,LOCAL->buf,LOCAL->buflen,
+ "status")) && *s && (*s++ == ':') &&
+ isxdigit (*s)) {
+ uid = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ uf = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ sf = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ mod = strtoul (s,&s,16);
+ /* ignore expansion values */
+ if (*s++ == ':') {
+ /* need to move ahead to next elt? */
+ while ((uid > elt->private.uid) && (i < stream->nmsgs))
+ elt = mail_elt (stream,++i);
+ /* update elt if altered */
+ if ((uid == elt->private.uid) &&
+ (!elt->valid || (mod != elt->private.mod))) {
+ elt->user_flags = uf;
+ elt->private.mod = mod;
+ elt->seen = (sf & fSEEN) ? T : NIL;
+ elt->deleted = (sf & fDELETED) ? T : NIL;
+ elt->flagged = (sf & fFLAGGED) ? T : NIL;
+ elt->answered = (sf & fANSWERED) ? T : NIL;
+ elt->draft = (sf & fDRAFT) ? T : NIL;
+ /* announce if altered existing message */
+ if (elt->valid) MM_FLAGS (stream,elt->msgno);
+ /* first time, is old message? */
+ else if (sf & fOLD) {
+ /* yes, clear recent and set valid */
+ elt->recent = NIL;
+ elt->valid = T;
+ }
+ /* recent, allowed to update its status? */
+ else if (sflags) {
+ /* yes, set valid and check in status */
+ elt->valid = T;
+ elt->private.mod = mix_modseq (elt->private.mod);
+ updatep = T;
+ }
+ /* leave valid unset and recent if sflags not set */
+ }
+ continue; /* everything looks good */
+ }
+ }
+ }
+ }
+ break; /* error somewhere */
+ }
+
+ if (t && *t) { /* non-null means bogus record */
+ char msg[MAILTMPLEN];
+ sprintf (msg,"Error in mix status file message record%s: %.80s",
+ stream->rdonly ? "" : ", fixing",t);
+ MM_LOG (msg,WARN);
+ /* update it if not readonly */
+ if (!stream->rdonly) updatep = T;
+ }
+ if (updatep) { /* need to update? */
+ LOCAL->statusseq = mix_modseq (LOCAL->statusseq);
+ mix_status_update (stream,statf,LONGT);
+ }
+ }
+ }
+ }
+ }
+ if (statf) { /* still happy? */
+ unsigned long j;
+ stream->silent = silent; /* now notify upper level */
+ mail_exists (stream,stream->nmsgs);
+ for (i = 1, j = 0; i <= stream->nmsgs; ++i)
+ if (mail_elt (stream,i)->recent) ++j;
+ mail_recent (stream,j);
+ }
+ return statf;
+}
+
+/* MIX metadata file routines */
+
+/* MIX read metadata
+ * Accepts: MAIL stream
+ * return pointer for modseq
+ * Returns: pointer to metadata after modseq or NIL if failure
+ */
+
+char *mix_meta_slurp (MAILSTREAM *stream,unsigned long *seq)
+{
+ struct stat sbuf;
+ char *s;
+ char *ret = NIL;
+ if (fstat (LOCAL->mfd,&sbuf))
+ MM_LOG ("Error obtaining size of mix metatdata file",ERROR);
+ if (sbuf.st_size > LOCAL->buflen) {
+ /* should be just a few dozen bytes */
+ if (sbuf.st_size > METAMAX) fatal ("absurd mix metadata file size");
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = sbuf.st_size) + 1);
+ }
+ /* read current metadata file */
+ LOCAL->buf[sbuf.st_size] = '\0';
+ if (lseek (LOCAL->mfd,0,L_SET) ||
+ (read (LOCAL->mfd,s = LOCAL->buf,sbuf.st_size) != sbuf.st_size))
+ MM_LOG ("Error reading mix metadata file",ERROR);
+ else if ((*s != 'S') || !isxdigit (s[1]) ||
+ ((*seq = strtoul (s+1,&s,16)) < LOCAL->metaseq) ||
+ (*s++ != '\015') || (*s++ != '\012'))
+ MM_LOG ("Error in mix metadata file sequence record",ERROR);
+ else ret = s;
+ return ret;
+}
+
+/* MIX update metadata
+ * Accepts: MAIL stream
+ * Returns: T on success, NIL if error
+ *
+ * Index MUST be locked!!
+ */
+
+long mix_meta_update (MAILSTREAM *stream)
+{
+ long ret;
+ /* do nothing if stream readonly */
+ if (stream->rdonly) ret = LONGT;
+ else {
+ unsigned char c,*s,*ss,*t;
+ unsigned long i;
+ /* The worst-case metadata is limited to:
+ * 4 * (1 + 8 + 2) + (NUSERFLAGS * (MAXUSERFLAG + 1))
+ * which comes out to 1994 octets. This is much smaller than the normal
+ * CHUNKSIZE definition of 64K, and CHUNKSIZE is the smallest size of
+ * LOCAL->buf.
+ *
+ * If more stuff gets added to the metadata, or if you change the value
+ * of NUSERFLAGS, MAXUSERFLAG or CHUNKSIZE, be sure to recalculate the
+ * above assertation.
+ */
+ sprintf (LOCAL->buf,SEQFMT,LOCAL->metaseq = mix_modseq (LOCAL->metaseq));
+ sprintf (LOCAL->buf + strlen (LOCAL->buf),MTAFMT,
+ stream->uid_validity,stream->uid_last,LOCAL->newmsg);
+ for (i = 0, c = 'K', s = ss = LOCAL->buf + strlen (LOCAL->buf);
+ (i < NUSERFLAGS) && (t = stream->user_flags[i]); ++i) {
+ if (!*t) fatal ("impossible empty keyword");
+ *s++ = c; /* write delimiter */
+ while (*t) *s++ = *t++; /* write keyword */
+ c = ' '; /* delimiter is now space */
+ }
+ if (s != ss) { /* tie off keywords line */
+ *s++ = '\015'; *s++ = '\012';
+ }
+ /* calculate length of metadata */
+ if ((i = s - LOCAL->buf) > LOCAL->buflen)
+ fatal ("impossible buffer overflow");
+ lseek (LOCAL->mfd,0,L_SET); /* rewind file */
+ /* write new metadata */
+ ret = (write (LOCAL->mfd,LOCAL->buf,i) == i) ? LONGT : NIL;
+ ftruncate (LOCAL->mfd,i); /* and tie off at that point */
+ }
+ return ret;
+}
+
+/* MIX index file routines */
+
+
+/* MIX update index
+ * Accepts: MAIL stream
+ * open FILE
+ * expansion check flag
+ * Returns: T on success, NIL if error
+ */
+
+long mix_index_update (MAILSTREAM *stream,FILE *idxf,long flag)
+{
+ unsigned long i;
+ long ret = LONGT;
+ if (!stream->rdonly) { /* do nothing if stream readonly */
+ if (flag) { /* need to do expansion check? */
+ char tmp[MAILTMPLEN];
+ size_t size;
+ struct stat sbuf;
+ /* calculate file size we need */
+ for (i = 1, size = 0; i <= stream->nmsgs; ++i)
+ if (!mail_elt (stream,i)->private.ghost) ++size;
+ if (size) { /* Winston Smith's first dairy entry */
+ sprintf (tmp,IXRFMT,0,14,4,4,13,0,0,'+',0,0,0,0,0,0,0);
+ size *= strlen (tmp);
+ }
+ /* calculate file size we need */
+ sprintf (tmp,SEQFMT,LOCAL->indexseq);
+ size += strlen (tmp);
+ /* get current file size */
+ if (fstat (fileno (idxf),&sbuf)) {
+ MM_LOG ("Error getting size of mix index file",ERROR);
+ ret = NIL;
+ }
+ /* need to write additional space? */
+ else if (sbuf.st_size < size) {
+ void *buf = fs_get (size -= sbuf.st_size);
+ memset (buf,0,size);
+ if (fseek (idxf,0,SEEK_END) || (fwrite (buf,1,size,idxf) != size) ||
+ fflush (idxf)) {
+ fseek (idxf,sbuf.st_size,SEEK_SET);
+ ftruncate (fileno (idxf),sbuf.st_size);
+ MM_LOG ("Error extending mix index file",ERROR);
+ ret = NIL;
+ }
+ fs_give ((void **) &buf);
+ }
+ }
+
+ if (ret) { /* if still good to go */
+ rewind (idxf); /* let's start at the very beginning */
+ /* write modseq first */
+ fprintf (idxf,SEQFMT,LOCAL->indexseq);
+ /* then write all messages */
+ for (i = 1; ret && (i <= stream->nmsgs); i++) {
+ MESSAGECACHE *elt = mail_elt (stream,i);
+ if (!elt->private.ghost)/* only write living messages */
+ fprintf (idxf,IXRFMT,elt->private.uid,
+ elt->year + BASEYEAR,elt->month,elt->day,
+ elt->hours,elt->minutes,elt->seconds,
+ elt->zoccident ? '-' : '+',elt->zhours,elt->zminutes,
+ elt->rfc822_size,elt->private.spare.data,
+ elt->private.special.offset,
+ elt->private.msg.header.offset,
+ elt->private.msg.header.text.size);
+ if (ferror (idxf)) {
+ MM_LOG ("Error updating mix index file",ERROR);
+ ret = NIL;
+ }
+ }
+ if (fflush (idxf)) {
+ MM_LOG ("Error flushing mix index file",ERROR);
+ ret = NIL;
+ }
+ if (ret) ftruncate (fileno (idxf),ftell (idxf));
+ }
+ }
+ return ret;
+}
+
+/* MIX status file routines */
+
+
+/* MIX update status
+ * Accepts: MAIL stream
+ * pointer to open FILE
+ * expansion check flag
+ * Returns: T on success, NIL if error
+ */
+
+long mix_status_update (MAILSTREAM *stream,FILE *statf,long flag)
+{
+ unsigned long i;
+ char tmp[MAILTMPLEN];
+ long ret = LONGT;
+ if (!stream->rdonly) { /* do nothing if stream readonly */
+ if (flag) { /* need to do expansion check? */
+ char tmp[MAILTMPLEN];
+ size_t size;
+ struct stat sbuf;
+ /* calculate file size we need */
+ for (i = 1, size = 0; i <= stream->nmsgs; ++i)
+ if (!mail_elt (stream,i)->private.ghost) ++size;
+ if (size) { /* number of living messages */
+ sprintf (tmp,STRFMT,0,0,0,0);
+ size *= strlen (tmp);
+ }
+ sprintf (tmp,SEQFMT,LOCAL->statusseq);
+ size += strlen (tmp);
+ /* get current file size */
+ if (fstat (fileno (statf),&sbuf)) {
+ MM_LOG ("Error getting size of mix status file",ERROR);
+ ret = NIL;
+ }
+ /* need to write additional space? */
+ else if (sbuf.st_size < size) {
+ void *buf = fs_get (size -= sbuf.st_size);
+ memset (buf,0,size);
+ if (fseek (statf,0,SEEK_END) || (fwrite (buf,1,size,statf) != size) ||
+ fflush (statf)) {
+ fseek (statf,sbuf.st_size,SEEK_SET);
+ ftruncate (fileno (statf),sbuf.st_size);
+ MM_LOG ("Error extending mix status file",ERROR);
+ ret = NIL;
+ }
+ fs_give ((void **) &buf);
+ }
+ }
+
+ if (ret) { /* if still good to go */
+ rewind (statf); /* let's start at the very beginning */
+ /* write sequence */
+ fprintf (statf,SEQFMT,LOCAL->statusseq);
+ /* write message status records */
+ for (i = 1; ret && (i <= stream->nmsgs); ++i) {
+ MESSAGECACHE *elt = mail_elt (stream,i);
+ /* make sure all messages have a modseq */
+ if (!elt->private.mod) elt->private.mod = LOCAL->statusseq;
+ if (!elt->private.ghost)/* only write living messages */
+ fprintf (statf,STRFMT,elt->private.uid,elt->user_flags,
+ (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft) + (elt->valid ? fOLD : NIL),
+ elt->private.mod);
+ if (ferror (statf)) {
+ sprintf (tmp,"Error updating mix status file: %.80s",
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL;
+ }
+ }
+ if (ret && fflush (statf)) {
+ MM_LOG ("Error flushing mix status file",ERROR);
+ ret = NIL;
+ }
+ if (ret) ftruncate (fileno (statf),ftell (statf));
+ }
+ }
+ return ret;
+}
+
+/* MIX data file routines */
+
+
+/* MIX open data file
+ * Accepts: MAIL stream
+ * pointer to returned fd if success
+ * pointer to returned size if success
+ * size of new data to be added
+ * Returns: open FILE, or NIL if failure
+ *
+ * The curend test assumes that the last message of the mailbox is the furthest
+ * point that the current data file extends, and thus that is all that needs to
+ * be tested for short file prevention.
+ */
+
+FILE *mix_data_open (MAILSTREAM *stream,int *fd,long *size,
+ unsigned long newsize)
+{
+ FILE *msgf = NIL;
+ struct stat sbuf;
+ MESSAGECACHE *elt = stream->nmsgs ? mail_elt (stream,stream->nmsgs) : NIL;
+ unsigned long curend = (elt && (elt->private.spare.data == LOCAL->newmsg)) ?
+ elt->private.special.offset + elt->private.msg.header.offset +
+ elt->rfc822_size : 0;
+ /* allow create if curend 0 */
+ if ((*fd = open (mix_file_data (LOCAL->buf,stream->mailbox,LOCAL->newmsg),
+ O_RDWR | (curend ? NIL : O_CREAT),NIL)) >= 0) {
+ fstat (*fd,&sbuf); /* get current file size */
+ /* can we use this file? */
+ if ((curend <= sbuf.st_size) &&
+ (!sbuf.st_size || ((sbuf.st_size + newsize) <= MIXDATAROLL)))
+ *size = sbuf.st_size; /* yes, return current size */
+ else { /* short file or becoming too long */
+ if (curend > sbuf.st_size) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"short mix message file %.08lx (%ld > %ld), rolling",
+ LOCAL->newmsg,curend,sbuf.st_size);
+ MM_LOG (tmp,WARN); /* shouldn't happen */
+ }
+ close (*fd); /* roll to a new file */
+ while ((*fd = open (mix_file_data
+ (LOCAL->buf,stream->mailbox,
+ LOCAL->newmsg = mix_modseq (LOCAL->newmsg)),
+ O_RDWR | O_CREAT | O_EXCL,sbuf.st_mode)) < 0);
+ *size = 0; /* brand new file */
+ fchmod (*fd,sbuf.st_mode);/* with same mode as previous file */
+ }
+ }
+ if (*fd >= 0) { /* have a data file? */
+ /* yes, get stdio and set position */
+ if (msgf = fdopen (*fd,"r+b")) fseek (msgf,*size,SEEK_SET);
+ else close (*fd); /* fdopen() failed? */
+ }
+ return msgf; /* return results */
+}
+
+/* MIX open sortcache
+ * Accepts: MAIL stream
+ * Returns: open FILE, or NIL if failure or could only get readonly sortcache
+ */
+
+FILE *mix_sortcache_open (MAILSTREAM *stream)
+{
+ int fd,refwd;
+ unsigned long i,uid,sentdate,fromlen,tolen,cclen,subjlen,msgidlen,reflen;
+ char *s,*t,*msg,tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ SORTCACHE *sc;
+ STRINGLIST *sl;
+ struct stat sbuf;
+ int rdonly = NIL;
+ FILE *srtcf = NIL;
+ mailcache_t mc = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL);
+ fstat (LOCAL->mfd,&sbuf);
+ if (!stream->nmsgs); /* do nothing if mailbox empty */
+ /* open sortcache file */
+ else if (((fd = open (LOCAL->sortcache,O_RDWR|O_CREAT,sbuf.st_mode)) < 0) &&
+ !(rdonly = ((fd = open (LOCAL->sortcache,O_RDONLY,NIL)) >= 0)))
+ MM_LOG ("Error opening mix sortcache file",WARN);
+ /* acquire lock and FILE */
+ else if (!flock (fd,rdonly ? LOCK_SH : LOCK_EX) &&
+ !(srtcf = fdopen (fd,rdonly ? "rb" : "r+b"))) {
+ MM_LOG ("Error obtaining stream on mix sortcache file",WARN);
+ flock (fd,LOCK_UN); /* relinquish lock */
+ close (fd);
+ }
+ else if (!(i = mix_read_sequence (srtcf)) || (i < LOCAL->sortcacheseq))
+ MM_LOG ("Error in mix sortcache file sequence record",WARN);
+ /* sequence changed from last time? */
+ else if (i > LOCAL->sortcacheseq) {
+ LOCAL->sortcacheseq = i; /* update sequence */
+ while ((s = t = mix_read_record (srtcf,LOCAL->buf,LOCAL->buflen,
+ "sortcache")) && *s &&
+ (msg = "uid") && (*s++ == ':') && isxdigit (*s)) {
+ uid = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ sentdate = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ fromlen = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ tolen = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ cclen = strtoul (s,&s,16);
+ if ((*s++ == ':') && ((*s == 'R') || (*s == ' ')) &&
+ isxdigit (s[1])) {
+ refwd = (*s++ == 'R') ? T : NIL;
+ subjlen = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ msgidlen = strtoul (s,&s,16);
+ if ((*s++ == ':') && isxdigit (*s)) {
+ reflen = strtoul (s,&s,16);
+ /* ignore expansion values */
+ if (*s++ == ':') {
+
+ if (i = mail_msgno (stream,uid)) {
+ sc = (SORTCACHE *) (*mc) (stream,i,CH_SORTCACHE);
+ sc->size = (elt = mail_elt (stream,i))->rfc822_size;
+ sc->date = sentdate;
+ sc->arrival = elt->day ? mail_longdate (elt) : 1;
+ if (refwd) sc->refwd = T;
+ if (fromlen) {
+ if (sc->from) fseek (srtcf,fromlen + 2,SEEK_CUR);
+ else if ((getc (srtcf) != 'F') ||
+ (fread (sc->from = (char *) fs_get(fromlen),
+ 1,fromlen-1,srtcf) != (fromlen-1))||
+ (sc->from[fromlen-1] = '\0') ||
+ (getc (srtcf) != '\015') ||
+ (getc (srtcf) != '\012')) {
+ msg = "from data";
+ break;
+ }
+ }
+ if (tolen) {
+ if (sc->to) fseek (srtcf,tolen + 2,SEEK_CUR);
+ else if ((getc (srtcf) != 'T') ||
+ (fread (sc->to = (char *) fs_get (tolen),
+ 1,tolen-1,srtcf) != (tolen - 1)) ||
+ (sc->to[tolen-1] = '\0') ||
+ (getc (srtcf) != '\015') ||
+ (getc (srtcf) != '\012')) {
+ msg = "to data";
+ break;
+ }
+ }
+ if (cclen) {
+ if (sc->cc) fseek (srtcf,cclen + 2,SEEK_CUR);
+ else if ((getc (srtcf) != 'C') ||
+ (fread (sc->cc = (char *) fs_get (cclen),
+ 1,cclen-1,srtcf) != (cclen - 1)) ||
+ (sc->cc[cclen-1] = '\0') ||
+ (getc (srtcf) != '\015') ||
+ (getc (srtcf) != '\012')) {
+ msg = "cc data";
+ break;
+ }
+ }
+ if (subjlen) {
+ if (sc->subject) fseek (srtcf,subjlen + 2,SEEK_CUR);
+ else if ((getc (srtcf) != 'S') ||
+ (fread (sc->subject =
+ (char *) fs_get (subjlen),1,
+ subjlen-1,srtcf) != (subjlen-1))||
+ (sc->subject[subjlen-1] = '\0') ||
+ (getc (srtcf) != '\015') ||
+ (getc (srtcf) != '\012')) {
+ msg = "subject data";
+ break;
+ }
+ }
+
+ if (msgidlen) {
+ if (sc->message_id)
+ fseek (srtcf,msgidlen + 2,SEEK_CUR);
+ else if ((getc (srtcf) != 'M') ||
+ (fread (sc->message_id =
+ (char *) fs_get (msgidlen),1,
+ msgidlen-1,srtcf) != (msgidlen-1))||
+ (sc->message_id[msgidlen-1] = '\0') ||
+ (getc (srtcf) != '\015') ||
+ (getc (srtcf) != '\012')) {
+ msg = "message-id data";
+ break;
+ }
+ }
+ if (reflen) {
+ if (sc->references) fseek(srtcf,reflen + 2,SEEK_CUR);
+ /* make sure it fits */
+ else {
+ if (reflen >= LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *)
+ fs_get ((LOCAL->buflen = reflen) + 1);
+ }
+ if ((getc (srtcf) != 'R') ||
+ (fread (LOCAL->buf,1,reflen-1,srtcf) !=
+ (reflen - 1)) ||
+ (LOCAL->buf[reflen-1] = '\0') ||
+ (getc (srtcf) != '\015') ||
+ (getc (srtcf) != '\012')) {
+ msg = "references data";
+ break;
+ }
+ for (s = LOCAL->buf,sl = NIL,
+ sc->references = mail_newstringlist ();
+ s && *s; s += i + 1) {
+ if ((i = strtoul (s,&s,16)) && (*s++ == ':') &&
+ (s[i] == ':')) {
+ if (sl) sl = sl->next = mail_newstringlist();
+ else sl = sc->references;
+ s[i] = '\0';
+ sl->text.data = cpystr (s);
+ sl->text.size = i;
+ }
+ else s = NIL;
+ }
+ if (!s || *s ||
+ (s != ((char *) LOCAL->buf + reflen - 1))) {
+ msg = "references length consistency check";
+ break;
+ }
+ }
+ }
+ }
+
+ /* UID not found, ignore this message */
+ else fseek (srtcf,((fromlen ? fromlen + 2 : 0) +
+ (tolen ? tolen + 2 : 0) +
+ (cclen ? cclen + 2 : 0) +
+ (subjlen ? subjlen + 2 : 0) +
+ (msgidlen ? msgidlen + 2 : 0) +
+ (reflen ? reflen + 2 : 0)),
+ SEEK_CUR);
+ continue;
+ }
+ else msg = "expansion";
+ }
+ else msg = "references";
+ }
+ else msg = "message-id";
+ }
+ else msg = "subject";
+ }
+ else msg = "cc";
+ }
+ else msg = "to";
+ }
+ else msg = "from";
+ }
+ else msg = "sentdate";
+ break; /* error somewhere */
+ }
+ if (!t || *t) { /* error detected? */
+ if (t) { /* non-null means bogus record */
+ sprintf (tmp,"Error in %s in mix sortcache record: %.500s",msg,t);
+ MM_LOG (tmp,WARN);
+ }
+ fclose (srtcf); /* either way, must punt */
+ srtcf = NIL;
+ }
+ }
+ if (rdonly && srtcf) { /* can't update if readonly */
+ unlink (LOCAL->sortcache); /* try deleting it */
+ fclose (srtcf); /* so close it and return as if error */
+ srtcf = NIL;
+ }
+ else fchmod (fd,sbuf.st_mode);
+ return srtcf;
+}
+
+/* MIX update and close sortcache
+ * Accepts: MAIL stream
+ * pointer to open FILE (if FILE is NIL, do nothing)
+ * Returns: T on success, NIL on error
+ */
+
+long mix_sortcache_update (MAILSTREAM *stream,FILE **sortcache)
+{
+ FILE *f = *sortcache;
+ long ret = LONGT;
+ if (f) { /* ignore if no file */
+ unsigned long i,j;
+ mailcache_t mc = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL);
+ for (i = 1; (i <= stream->nmsgs) &&
+ !((SORTCACHE *) (*mc) (stream,i,CH_SORTCACHE))->dirty; ++i);
+ if (i <= stream->nmsgs) { /* only update if some entry is dirty */
+ rewind (f); /* let's start at the very beginning */
+ /* write sequence */
+ fprintf (f,SEQFMT,LOCAL->sortcacheseq = mix_modseq(LOCAL->sortcacheseq));
+ for (i = 1; ret && (i <= stream->nmsgs); ++i) {
+ MESSAGECACHE *elt = mail_elt (stream,i);
+ SORTCACHE *s = (SORTCACHE *) (*mc) (stream,i,CH_SORTCACHE);
+ STRINGLIST *sl;
+ s->dirty = NIL; /* no longer dirty */
+ if (sl = s->references) /* count length of references */
+ for (j = 1; sl && sl->text.data; sl = sl->next)
+ j += 10 + sl->text.size;
+ else j = 0; /* no references yet */
+ fprintf (f,SCRFMT,elt->private.uid,s->date,
+ s->from ? strlen (s->from) + 1 : 0,
+ s->to ? strlen (s->to) + 1 : 0,s->cc ? strlen (s->cc) + 1 : 0,
+ s->refwd ? 'R' : ' ',s->subject ? strlen (s->subject) + 1: 0,
+ s->message_id ? strlen (s->message_id) + 1 : 0,j);
+ if (s->from) fprintf (f,"F%s\015\012",s->from);
+ if (s->to) fprintf (f,"T%s\015\012",s->to);
+ if (s->cc) fprintf (f,"C%s\015\012",s->cc);
+ if (s->subject) fprintf (f,"S%s\015\012",s->subject);
+ if (s->message_id) fprintf (f,"M%s\015\012",s->message_id);
+ if (j) { /* any references to write? */
+ fputc ('R',f); /* yes, do so */
+ for (sl = s->references; sl && sl->text.data; sl = sl->next)
+ fprintf (f,"%08lx:%s:",sl->text.size,sl->text.data);
+ fputs ("\015\012",f);
+ }
+ if (ferror (f)) {
+ MM_LOG ("Error updating mix sortcache file",WARN);
+ ret = NIL;
+ }
+ }
+ if (ret && fflush (f)) {
+ MM_LOG ("Error flushing mix sortcache file",WARN);
+ ret = NIL;
+ }
+ if (ret) ftruncate (fileno (f),ftell (f));
+ }
+ if (fclose (f)) {
+ MM_LOG ("Error closing mix sortcache file",WARN);
+ ret = NIL;
+ }
+ }
+ return ret;
+}
+
+/* MIX generic file routines */
+
+/* MIX read record
+ * Accepts: open FILE
+ * buffer
+ * buffer length
+ * record type
+ * Returns: buffer if success, else NIL (zero-length buffer means EOF)
+ */
+
+char *mix_read_record (FILE *f,char *buf,unsigned long buflen,char *type)
+{
+ char *s,tmp[MAILTMPLEN];
+ /* ensure string tied off */
+ buf[buflen-2] = buf[buflen-1] = '\0';
+ while (fgets (buf,buflen-1,f)) {
+ if (s = strchr (buf,'\012')) {
+ if ((s != buf) && (s[-1] == '\015')) --s;
+ *s = '\0'; /* tie off buffer */
+ if (s != buf) return buf; /* return if non-empty buffer */
+ sprintf (tmp,"Empty mix %s record",type);
+ MM_LOG (tmp,WARN);
+ }
+ else if (buf[buflen-2]) { /* overlong record is bad news */
+ sprintf (tmp,"Oversize mix %s record: %.512s",type,buf);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ else {
+ sprintf (tmp,"Truncated mix %s record: %.512s",type,buf);
+ MM_LOG (tmp,WARN);
+ return buf; /* pass to caller anyway */
+ }
+ }
+ buf[0] = '\0'; /* return empty buffer on EOF */
+ return buf;
+}
+
+/* MIX read sequence record
+ * Accepts: open FILE
+ * Returns: sequence value, or NIL if failure
+ */
+
+unsigned long mix_read_sequence (FILE *f)
+{
+ unsigned long ret;
+ char *s,tmp[MAILTMPLEN];
+ if (!mix_read_record (f,tmp,MAILTMPLEN-1,"sequence")) return NIL;
+ switch (tmp[0]) { /* examine record */
+ case '\0': /* end of file */
+ ret = 1; /* start a new sequence regime */
+ break;
+ case 'S': /* sequence record */
+ if (isxdigit (tmp[1])) { /* must be followed by hex value */
+ ret = strtoul (tmp+1,&s,16);
+ if (!*s) break; /* and nothing more */
+ }
+ /* drop into default case */
+ default: /* anything else is an error */
+ return NIL; /* return error */
+ }
+ return ret;
+}
+
+/* MIX internal routines */
+
+
+/* MIX mail build directory name
+ * Accepts: destination string
+ * source
+ * Returns: destination or empty string if error
+ */
+
+char *mix_dir (char *dst,char *name)
+{
+ char *s;
+ /* empty string if mailboxfile fails */
+ if (!mailboxfile (dst,name)) *dst = '\0';
+ /* driver-selected INBOX */
+ else if (!*dst) mailboxfile (dst,"~/INBOX");
+ /* tie off unnecessary trailing / */
+ else if ((s = strrchr (dst,'/')) && !s[1]) *s = '\0';
+ return dst;
+}
+
+
+/* MIX mail build file name
+ * Accepts: destination string
+ * directory name
+ * file name
+ * Returns: destination
+ */
+
+char *mix_file (char *dst,char *dir,char *name)
+{
+ sprintf (dst,"%.500s/%.80s%.80s",dir,MIXNAME,name);
+ return dst;
+}
+
+
+/* MIX mail build file name from data file number
+ * Accepts: destination string
+ * directory name
+ * data file number
+ * Returns: destination
+ */
+
+char *mix_file_data (char *dst,char *dir,unsigned long data)
+{
+ char tmp[MAILTMPLEN];
+ if (data) sprintf (tmp,"%08lx",data);
+ else tmp[0] = '\0'; /* compatibility with experimental version */
+ return mix_file (dst,dir,tmp);
+}
+
+/* MIX mail get new modseq
+ * Accepts: old modseq
+ * Returns: new modseq value
+ */
+
+unsigned long mix_modseq (unsigned long oldseq)
+{
+ /* normally time now */
+ unsigned long ret = (unsigned long) time (NIL);
+ /* ensure that modseq doesn't go backwards */
+ if (ret <= oldseq) ret = oldseq + 1;
+ return ret;
+}
diff --git a/imap/src/osdep/unix/mkauths b/imap/src/osdep/unix/mkauths
new file mode 100755
index 00000000..8e9cc0cf
--- /dev/null
+++ b/imap/src/osdep/unix/mkauths
@@ -0,0 +1,40 @@
+#!/bin/sh
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+# Program: Authenticator Linkage Generator
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 5 December 1995
+# Last Edited: 30 August 2006
+
+# Erase old authenticators list
+rm -f auths.c
+touch auths.c
+
+# Now define the new list
+for authenticator
+ do
+ if [ -f Makefile."$authenticator" ]; then
+ make -f Makefile."$authenticator" `cat SPECIALS`
+ fi
+ echo "extern AUTHENTICATOR auth_"$authenticator";" >> linkage.h
+ echo " auth_link (&auth_"$authenticator"); /* link in the $authenticator authenticator */" | cat >> linkage.c
+ echo "#include \"auth_"$authenticator".c\"" >> auths.c
+done
diff --git a/imap/src/osdep/unix/mmdf.c b/imap/src/osdep/unix/mmdf.c
new file mode 100644
index 00000000..e962434e
--- /dev/null
+++ b/imap/src/osdep/unix/mmdf.c
@@ -0,0 +1,2549 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MMDF mail routines
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@Washington.EDU
+ *
+ * Date: 20 December 1989
+ * Last Edited: 27 March 2008
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <signal.h>
+#include "mail.h"
+#include "osdep.h"
+#include <time.h>
+#include <sys/stat.h>
+#include "pseudo.h"
+#include "fdstring.h"
+#include "misc.h"
+#include "dummy.h"
+
+/* Supposedly, this page has everything the MMDF driver needs to know about
+ * the MMDF delimiter. By changing these macros, the MMDF driver should
+ * change with it. Note that if you change the length of MMDFHDRTXT you
+ * also need to change the ISMMDF and RETIFMMDFWRD macros to reflect the new
+ * size.
+ */
+
+
+/* Useful MMDF constants */
+
+#define MMDFCHR '\01' /* MMDF character */
+#define MMDFCHRS 0x01010101 /* MMDF header character spread in a word */
+ /* MMDF header text */
+#define MMDFHDRTXT "\01\01\01\01\n"
+ /* length of MMDF header text */
+#define MMDFHDRLEN (sizeof (MMDFHDRTXT) - 1)
+
+
+/* Validate MMDF header
+ * Accepts: pointer to candidate string to validate as an MMDF header
+ * Returns: T if valid; else NIL
+ */
+
+#define ISMMDF(s) \
+ ((*(s) == MMDFCHR) && ((s)[1] == MMDFCHR) && ((s)[2] == MMDFCHR) && \
+ ((s)[3] == MMDFCHR) && ((s)[4] == '\n'))
+
+
+/* Return if a 32-bit word has the start of an MMDF header
+ * Accepts: pointer to word of four bytes to validate as an MMDF header
+ * Returns: pointer to MMDF header, else proceeds
+ */
+
+#define RETIFMMDFWRD(s) { \
+ if (s[3] == MMDFCHR) { \
+ if ((s[4] == MMDFCHR) && (s[5] == MMDFCHR) && (s[6] == MMDFCHR) && \
+ (s[7] == '\n')) return s + 3; \
+ else if (s[2] == MMDFCHR) { \
+ if ((s[4] == MMDFCHR) && (s[5] == MMDFCHR) && (s[6] == '\n')) \
+ return s + 2; \
+ else if (s[1] == MMDFCHR) { \
+ if ((s[4] == MMDFCHR) && (s[5] == '\n')) return s + 1; \
+ else if ((*s == MMDFCHR) && (s[4] == '\n')) return s; \
+ } \
+ } \
+ } \
+}
+
+/* Validate line
+ * Accepts: pointer to candidate string to validate as a From header
+ * return pointer to end of date/time field
+ * return pointer to offset from t of time (hours of ``mmm dd hh:mm'')
+ * return pointer to offset from t of time zone (if non-zero)
+ * Returns: t,ti,zn set if valid From string, else ti is NIL
+ */
+
+#define VALID(s,x,ti,zn) { \
+ ti = 0; \
+ if ((*s == 'F') && (s[1] == 'r') && (s[2] == 'o') && (s[3] == 'm') && \
+ (s[4] == ' ')) { \
+ for (x = s + 5; *x && *x != '\n'; x++); \
+ if (*x) { \
+ if (x - s >= 41) { \
+ for (zn = -1; x[zn] != ' '; zn--); \
+ if ((x[zn-1] == 'm') && (x[zn-2] == 'o') && (x[zn-3] == 'r') && \
+ (x[zn-4] == 'f') && (x[zn-5] == ' ') && (x[zn-6] == 'e') && \
+ (x[zn-7] == 't') && (x[zn-8] == 'o') && (x[zn-9] == 'm') && \
+ (x[zn-10] == 'e') && (x[zn-11] == 'r') && (x[zn-12] == ' '))\
+ x += zn - 12; \
+ } \
+ if (x - s >= 27) { \
+ if (x[-5] == ' ') { \
+ if (x[-8] == ':') zn = 0,ti = -5; \
+ else if (x[-9] == ' ') ti = zn = -9; \
+ else if ((x[-11] == ' ') && ((x[-10]=='+') || (x[-10]=='-'))) \
+ ti = zn = -11; \
+ } \
+ else if (x[-4] == ' ') { \
+ if (x[-9] == ' ') zn = -4,ti = -9; \
+ } \
+ else if (x[-6] == ' ') { \
+ if ((x[-11] == ' ') && ((x[-5] == '+') || (x[-5] == '-'))) \
+ zn = -6,ti = -11; \
+ } \
+ if (ti && !((x[ti - 3] == ':') && \
+ (x[ti -= ((x[ti - 6] == ':') ? 9 : 6)] == ' ') && \
+ (x[ti - 3] == ' ') && (x[ti - 7] == ' ') && \
+ (x[ti - 11] == ' '))) ti = 0; \
+ } \
+ } \
+ } \
+}
+
+/* You are not expected to understand this macro, but read the next page if
+ * you are not faint of heart.
+ *
+ * Known formats to the VALID macro are:
+ * From user Wed Dec 2 05:53 1992
+ * BSD From user Wed Dec 2 05:53:22 1992
+ * SysV From user Wed Dec 2 05:53 PST 1992
+ * rn From user Wed Dec 2 05:53:22 PST 1992
+ * From user Wed Dec 2 05:53 -0700 1992
+ * emacs From user Wed Dec 2 05:53:22 -0700 1992
+ * From user Wed Dec 2 05:53 1992 PST
+ * From user Wed Dec 2 05:53:22 1992 PST
+ * From user Wed Dec 2 05:53 1992 -0700
+ * Solaris From user Wed Dec 2 05:53:22 1992 -0700
+ *
+ * Plus all of the above with `` remote from xxx'' after it. Thank you very
+ * much, smail and Solaris, for making my life considerably more complicated.
+ */
+
+/*
+ * What? You want to understand the VALID macro anyway? Alright, since you
+ * insist. Actually, it isn't really all that difficult, provided that you
+ * take it step by step.
+ *
+ * Line 1 Initializes the return ti value to failure (0);
+ * Lines 2-3 Validates that the 1st-5th characters are ``From ''.
+ * Lines 4-5 Validates that there is an end of line and points x at it.
+ * Lines 6-13 First checks to see if the line is at least 41 characters long.
+ * If so, it scans backwards to find the rightmost space. From
+ * that point, it scans backwards to see if the string matches
+ * `` remote from''. If so, it sets x to point to the space at
+ * the start of the string.
+ * Line 14 Makes sure that there are at least 27 characters in the line.
+ * Lines 15-20 Checks if the date/time ends with the year (there is a space
+ * five characters back). If there is a colon three characters
+ * further back, there is no timezone field, so zn is set to 0
+ * and ti is set in front of the year. Otherwise, there must
+ * either to be a space four characters back for a three-letter
+ * timezone, or a space six characters back followed by a + or -
+ * for a numeric timezone; in either case, zn and ti become the
+ * offset of the space immediately before it.
+ * Lines 21-23 Are the failure case for line 14. If there is a space four
+ * characters back, it is a three-letter timezone; there must be a
+ * space for the year nine characters back. zn is the zone
+ * offset; ti is the offset of the space.
+ * Lines 24-27 Are the failure case for line 20. If there is a space six
+ * characters back, it is a numeric timezone; there must be a
+ * space eleven characters back and a + or - five characters back.
+ * zn is the zone offset; ti is the offset of the space.
+ * Line 28-31 If ti is valid, make sure that the string before ti is of the
+ * form www mmm dd hh:mm or www mmm dd hh:mm:ss, otherwise
+ * invalidate ti. There must be a colon three characters back
+ * and a space six or nine characters back (depending upon
+ * whether or not the character six characters back is a colon).
+ * There must be a space three characters further back (in front
+ * of the day), one seven characters back (in front of the month),
+ * and one eleven characters back (in front of the day of week).
+ * ti is set to be the offset of the space before the time.
+ *
+ * Why a macro? It gets invoked a *lot* in a tight loop. On some of the
+ * newer pipelined machines it is faster being open-coded than it would be if
+ * subroutines are called.
+ *
+ * Why does it scan backwards from the end of the line, instead of doing the
+ * much easier forward scan? There is no deterministic way to parse the
+ * ``user'' field, because it may contain unquoted spaces! Yes, I tested it to
+ * see if unquoted spaces were possible. They are, and I've encountered enough
+ * evil mail to be totally unwilling to trust that ``it will never happen''.
+ */
+
+/* Build parameters */
+
+#define KODRETRY 15 /* kiss-of-death retry in seconds */
+#define LOCKTIMEOUT 5 /* lock timeout in minutes */
+
+
+/* MMDF I/O stream local data */
+
+typedef struct mmdf_local {
+ unsigned int dirty : 1; /* disk copy needs updating */
+ unsigned int ddirty : 1; /* double-dirty, ping becomes checkpoint */
+ unsigned int pseudo : 1; /* uses a pseudo message */
+ unsigned int appending : 1; /* don't mark new messages as old */
+ int fd; /* mailbox file descriptor */
+ int ld; /* lock file descriptor */
+ char *lname; /* lock file name */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ unsigned long uid; /* current text uid */
+ SIZEDTEXT text; /* current text */
+ unsigned long textlen; /* current text length */
+ char *line; /* returned line */
+ char *linebuf; /* line readin buffer */
+ unsigned long linebuflen; /* current line readin buffer length */
+} MMDFLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MMDFLOCAL *) stream->local)
+
+
+/* MMDF protected file structure */
+
+typedef struct mmdf_file {
+ MAILSTREAM *stream; /* current stream */
+ off_t curpos; /* current file position */
+ off_t protect; /* protected position */
+ off_t filepos; /* current last written file position */
+ char *buf; /* overflow buffer */
+ size_t buflen; /* current overflow buffer length */
+ char *bufpos; /* current buffer position */
+} MMDFFILE;
+
+/* Function prototypes */
+
+DRIVER *mmdf_valid (char *name);
+long mmdf_isvalid (char *name,char *tmp);
+long mmdf_isvalid_fd (int fd,char *tmp);
+void *mmdf_parameters (long function,void *value);
+void mmdf_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void mmdf_list (MAILSTREAM *stream,char *ref,char *pat);
+void mmdf_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long mmdf_create (MAILSTREAM *stream,char *mailbox);
+long mmdf_delete (MAILSTREAM *stream,char *mailbox);
+long mmdf_rename (MAILSTREAM *stream,char *old,char *newname);
+MAILSTREAM *mmdf_open (MAILSTREAM *stream);
+void mmdf_close (MAILSTREAM *stream,long options);
+char *mmdf_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long mmdf_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+char *mmdf_text_work (MAILSTREAM *stream,MESSAGECACHE *elt,
+ unsigned long *length,long flags);
+void mmdf_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long mmdf_ping (MAILSTREAM *stream);
+void mmdf_check (MAILSTREAM *stream);
+long mmdf_expunge (MAILSTREAM *stream,char *sequence,long options);
+long mmdf_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long mmdf_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+int mmdf_collect_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
+ STRING *msg);
+int mmdf_append_msgs (MAILSTREAM *stream,FILE *sf,FILE *df,SEARCHSET *set);
+
+void mmdf_abort (MAILSTREAM *stream);
+char *mmdf_file (char *dst,char *name);
+int mmdf_lock (char *file,int flags,int mode,DOTLOCK *lock,int op);
+void mmdf_unlock (int fd,MAILSTREAM *stream,DOTLOCK *lock);
+int mmdf_parse (MAILSTREAM *stream,DOTLOCK *lock,int op);
+char *mmdf_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size);
+unsigned long mmdf_pseudo (MAILSTREAM *stream,char *hdr);
+unsigned long mmdf_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt,
+ unsigned long uid,long flag);
+long mmdf_rewrite (MAILSTREAM *stream,unsigned long *nexp,DOTLOCK *lock,
+ long flags);
+long mmdf_extend (MAILSTREAM *stream,unsigned long size);
+void mmdf_write (MMDFFILE *f,char *s,unsigned long i);
+void mmdf_phys_write (MMDFFILE *f,char *buf,size_t size);
+
+/* MMDF mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mmdfdriver = {
+ "mmdf", /* driver name */
+ /* driver flags */
+ DR_LOCAL|DR_MAIL|DR_LOCKING|DR_NONEWMAILRONLY|DR_XPOINT,
+ (DRIVER *) NIL, /* next driver */
+ mmdf_valid, /* mailbox is valid for us */
+ mmdf_parameters, /* manipulate parameters */
+ mmdf_scan, /* scan mailboxes */
+ mmdf_list, /* list mailboxes */
+ mmdf_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ mmdf_create, /* create mailbox */
+ mmdf_delete, /* delete mailbox */
+ mmdf_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ mmdf_open, /* open mailbox */
+ mmdf_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mmdf_header, /* fetch message header */
+ mmdf_text, /* fetch message text */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ NIL, /* modify flags */
+ mmdf_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mmdf_ping, /* ping mailbox to see if still alive */
+ mmdf_check, /* check for new messages */
+ mmdf_expunge, /* expunge deleted messages */
+ mmdf_copy, /* copy messages to another mailbox */
+ mmdf_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mmdfproto = {&mmdfdriver};
+
+char *mmdfhdr = MMDFHDRTXT; /* MMDF header */
+
+/* MMDF mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mmdf_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return mmdf_isvalid (name,tmp) ? &mmdfdriver : NIL;
+}
+
+
+/* MMDF mail test for valid mailbox name
+ * Accepts: mailbox name
+ * scratch buffer
+ * Returns: T if valid, NIL otherwise
+ */
+
+long mmdf_isvalid (char *name,char *tmp)
+{
+ int fd;
+ int ret = NIL;
+ char *t,file[MAILTMPLEN];
+ struct stat sbuf;
+ time_t tp[2];
+ errno = EINVAL; /* assume invalid argument */
+ /* must be non-empty file */
+ if ((t = dummy_file (file,name)) && !stat (t,&sbuf)) {
+ if (!sbuf.st_size)errno = 0;/* empty file */
+ else if ((fd = open (file,O_RDONLY,NIL)) >= 0) {
+ /* error -1 for invalid format */
+ if (!(ret = mmdf_isvalid_fd (fd,tmp))) errno = -1;
+ close (fd); /* close the file */
+ /* \Marked status? */
+ if ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) {
+ tp[0] = sbuf.st_atime; /* preserve atime and mtime */
+ tp[1] = sbuf.st_mtime;
+ utime (file,tp); /* set the times */
+ }
+ }
+ }
+ return ret; /* return what we should */
+}
+
+/* MMDF mail test for valid mailbox
+ * Accepts: file descriptor
+ * scratch buffer
+ * Returns: T if valid, NIL otherwise
+ */
+
+long mmdf_isvalid_fd (int fd,char *tmp)
+{
+ int ret = NIL;
+ memset (tmp,'\0',MAILTMPLEN);
+ if (read (fd,tmp,MAILTMPLEN-1) >= 0) ret = ISMMDF (tmp) ? T : NIL;
+ return ret; /* return what we should */
+}
+
+
+/* MMDF manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mmdf_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = dummy_file ((char *) value,"INBOX");
+ break;
+ }
+ return ret;
+}
+
+/* MMDF mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mmdf_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* MMDF mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mmdf_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* MMDF mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mmdf_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* MMDF mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mmdf_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,mbx[MAILTMPLEN],tmp[MAILTMPLEN];
+ long ret = NIL;
+ int i,fd;
+ time_t ti = time (0);
+ if (!(s = dummy_file (mbx,mailbox))) {
+ sprintf (tmp,"Can't create %.80s: invalid name",mailbox);
+ MM_LOG (tmp,ERROR);
+ }
+ /* create underlying file */
+ else if (dummy_create_path (stream,s,get_dir_protection (mailbox))) {
+ /* done if dir-only or whiner */
+ if (((s = strrchr (s,'/')) && !s[1]) ||
+ mail_parameters (NIL,GET_USERHASNOLIFE,NIL)) ret = T;
+ else if ((fd = open (mbx,O_WRONLY,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL))) < 0) {
+ sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ else { /* initialize header */
+ memset (tmp,'\0',MAILTMPLEN);
+ sprintf (tmp,"%sFrom %s %sDate: ",mmdfhdr,pseudo_from,ctime (&ti));
+ rfc822_date (s = tmp + strlen (tmp));
+ sprintf (s += strlen (s), /* write the pseudo-header */
+ "\nFrom: %s <%s@%s>\nSubject: %s\nX-IMAP: %010lu 0000000000",
+ pseudo_name,pseudo_from,mylocalhost (),pseudo_subject,
+ (unsigned long) ti);
+ for (i = 0; i < NUSERFLAGS; ++i) if (default_user_flag (i))
+ sprintf (s += strlen (s)," %s",default_user_flag (i));
+ sprintf (s += strlen (s),"\nStatus: RO\n\n%s\n%s",pseudo_msg,mmdfhdr);
+ if (write (fd,tmp,strlen (tmp)) > 0) ret = T;
+ else {
+ sprintf (tmp,"Can't initialize mailbox node %.80s: %s",mbx,
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ close (fd); /* close file */
+ }
+ }
+ /* set proper protections */
+ return ret ? set_mbx_protections (mailbox,mbx) : NIL;
+}
+
+/* MMDF mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mmdf_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return mmdf_rename (stream,mailbox,NIL);
+}
+
+
+/* MMDF mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long mmdf_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = NIL;
+ char c,*s = NIL;
+ char tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ DOTLOCK lockx;
+ int fd,ld;
+ long i;
+ struct stat sbuf;
+ MM_CRITICAL (stream); /* get the c-client lock */
+ if (!dummy_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'/')) && !s[1]))))
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ /* lock out other c-clients */
+ else if ((ld = lockname (lock,file,LOCK_EX|LOCK_NB,&i)) < 0)
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+
+ else {
+ if ((fd = mmdf_lock (file,O_RDWR,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL),
+ &lockx,LOCK_EX)) < 0)
+ sprintf (tmp,"Can't lock mailbox %.80s: %s",old,strerror (errno));
+ else {
+ if (newname) { /* want rename? */
+ /* found superior to destination name? */
+ if (s = strrchr (s,'/')) {
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,tmp,get_dir_protection (newname))) {
+ mmdf_unlock (fd,NIL,&lockx);
+ mmdf_unlock (ld,NIL,NIL);
+ unlink (lock);
+ MM_NOCRITICAL (stream);
+ return ret; /* return success or failure */
+ }
+ *s = c; /* restore full name */
+ }
+ if (rename (file,tmp))
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ else ret = T; /* set success */
+ }
+ else if (unlink (file))
+ sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
+ else ret = T; /* set success */
+ mmdf_unlock (fd,NIL,&lockx);
+ }
+ mmdf_unlock (ld,NIL,NIL); /* flush the lock */
+ unlink (lock);
+ }
+ MM_NOCRITICAL (stream); /* no longer critical */
+ if (!ret) MM_LOG (tmp,ERROR); /* log error */
+ return ret; /* return success or failure */
+}
+
+/* MMDF mail open
+ * Accepts: Stream to open
+ * Returns: Stream on success, NIL on failure
+ */
+
+MAILSTREAM *mmdf_open (MAILSTREAM *stream)
+{
+ long i;
+ int fd;
+ char tmp[MAILTMPLEN];
+ DOTLOCK lock;
+ long retry;
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return user_flags (&mmdfproto);
+ retry = stream->silent ? 1 : KODRETRY;
+ if (stream->local) fatal ("mmdf recycle stream");
+ stream->local = memset (fs_get (sizeof (MMDFLOCAL)),0,sizeof (MMDFLOCAL));
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ /* canonicalize the stream mailbox name */
+ if (!dummy_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* flush old name */
+ fs_give ((void **) &stream->mailbox);
+ /* save canonical name */
+ stream->mailbox = cpystr (tmp);
+ LOCAL->fd = LOCAL->ld = -1; /* no file or state locking yet */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ LOCAL->text.data = (unsigned char *) fs_get (CHUNKSIZE);
+ LOCAL->text.size = CHUNKSIZE - 1;
+ LOCAL->linebuf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->linebuflen = CHUNKSIZE - 1;
+ stream->sequence++; /* bump sequence number */
+
+ /* make lock for read/write access */
+ if (!stream->rdonly) while (retry) {
+ /* try to lock file */
+ if ((fd = lockname (tmp,stream->mailbox,LOCK_EX|LOCK_NB,&i)) < 0) {
+ /* suppressing kiss-of-death? */
+ if (stream->nokod) retry = 0;
+ /* no, first time through? */
+ else if (retry-- == KODRETRY) {
+ /* learned other guy's PID and can signal? */
+ if (i && !kill ((int) i,SIGUSR2)) {
+ sprintf (tmp,"Trying to get mailbox lock from process %ld",i);
+ MM_LOG (tmp,WARN);
+ }
+ else retry = 0; /* give up */
+ }
+ if (!stream->silent) { /* nothing if silent stream */
+ if (retry) sleep (1); /* wait a second before trying again */
+ else MM_LOG ("Mailbox is open by another process, access is readonly",
+ WARN);
+ }
+ }
+ else { /* got the lock, nobody else can alter state */
+ LOCAL->ld = fd; /* note lock's fd and name */
+ LOCAL->lname = cpystr (tmp);
+ /* make sure mode OK (don't use fchmod()) */
+ chmod (LOCAL->lname,(long) mail_parameters (NIL,GET_LOCKPROTECTION,NIL));
+ if (stream->silent) i = 0;/* silent streams won't accept KOD */
+ else { /* note our PID in the lock */
+ sprintf (tmp,"%d",getpid ());
+ write (fd,tmp,(i = strlen (tmp))+1);
+ }
+ ftruncate (fd,i); /* make sure tied off */
+ fsync (fd); /* make sure it's available */
+ retry = 0; /* no more need to try */
+ }
+ }
+
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ /* will we be able to get write access? */
+ if ((LOCAL->ld >= 0) && access (stream->mailbox,W_OK) && (errno == EACCES)) {
+ MM_LOG ("Can't get write access to mailbox, access is readonly",WARN);
+ flock (LOCAL->ld,LOCK_UN); /* release the lock */
+ close (LOCAL->ld); /* close the lock file */
+ LOCAL->ld = -1; /* no more lock fd */
+ unlink (LOCAL->lname); /* delete it */
+ }
+ /* reset UID validity */
+ stream->uid_validity = stream->uid_last = 0;
+ if (stream->silent && !stream->rdonly && (LOCAL->ld < 0))
+ mmdf_abort (stream); /* abort if can't get RW silent stream */
+ /* parse mailbox */
+ else if (mmdf_parse (stream,&lock,LOCK_SH)) {
+ mmdf_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream);
+ MM_NOCRITICAL (stream); /* done with critical */
+ }
+ if (!LOCAL) return NIL; /* failure if stream died */
+ /* make sure upper level knows readonly */
+ stream->rdonly = (LOCAL->ld < 0);
+ /* notify about empty mailbox */
+ if (!(stream->nmsgs || stream->silent)) MM_LOG ("Mailbox is empty",NIL);
+ if (!stream->rdonly) { /* flags stick if readwrite */
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft = T;
+ if (!stream->uid_nosticky) {/* users with lives get permanent keywords */
+ stream->perm_user_flags = 0xffffffff;
+ /* and maybe can create them too! */
+ stream->kwd_create = stream->user_flags[NUSERFLAGS-1] ? NIL : T;
+ }
+ }
+ return stream; /* return stream alive to caller */
+}
+
+
+/* MMDF mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mmdf_close (MAILSTREAM *stream,long options)
+{
+ int silent = stream->silent;
+ stream->silent = T; /* go silent */
+ /* expunge if requested */
+ if (options & CL_EXPUNGE) mmdf_expunge (stream,NIL,NIL);
+ /* else dump final checkpoint */
+ else if (LOCAL->dirty) mmdf_check (stream);
+ stream->silent = silent; /* restore old silence state */
+ mmdf_abort (stream); /* now punt the file and local data */
+}
+
+/* MMDF mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+ /* lines to filter from header */
+static STRINGLIST *mmdf_hlines = NIL;
+
+char *mmdf_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned char *s,*t,*tl;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ elt = mail_elt (stream,msgno);/* get cache */
+ if (!mmdf_hlines) { /* once only code */
+ STRINGLIST *lines = mmdf_hlines = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "Status"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-Status"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-Keywords"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-UID"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-IMAP"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-IMAPbase"));
+ }
+ /* go to header position */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.msg.header.offset,L_SET);
+
+ if (flags & FT_INTERNAL) { /* initial data OK? */
+ if (elt->private.msg.header.text.size > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
+ elt->private.msg.header.text.size) + 1);
+ }
+ /* read message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.header.text.size);
+ /* got text, tie off string */
+ LOCAL->buf[*length = elt->private.msg.header.text.size] = '\0';
+ /* squeeze out CRs (in case from PC) */
+ for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t < tl; t++)
+ if (*t != '\r') *s++ = *t;
+ *s = '\0';
+ *length = s - LOCAL->buf; /* adjust length */
+ }
+ else { /* need to make a CRLF version */
+ read (LOCAL->fd,s = (char *) fs_get (elt->private.msg.header.text.size+1),
+ elt->private.msg.header.text.size);
+ /* tie off string, and convert to CRLF */
+ s[elt->private.msg.header.text.size] = '\0';
+ *length = strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,s,
+ elt->private.msg.header.text.size);
+ fs_give ((void **) &s); /* free readin buffer */
+ /* squeeze out spurious CRs */
+ for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t < tl; t++)
+ if ((*t != '\r') || (t[1] == '\n')) *s++ = *t;
+ *s = '\0';
+ *length = s - LOCAL->buf; /* adjust length */
+ }
+ *length = mail_filter (LOCAL->buf,*length,mmdf_hlines,FT_NOT);
+ return (char *) LOCAL->buf; /* return processed copy */
+}
+
+/* MMDF mail fetch message text
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T on success, NIL if failure
+ */
+
+long mmdf_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ char *s;
+ unsigned long i;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mail_elt (stream,msgno);/* get cache element */
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ /* mark message seen and dirty */
+ elt->seen = elt->private.dirty = LOCAL->dirty = T;
+ MM_FLAGS (stream,msgno);
+ }
+ s = mmdf_text_work (stream,elt,&i,flags);
+ INIT (bs,mail_string,s,i); /* set up stringstruct */
+ return T; /* success */
+}
+
+/* MMDF mail fetch message text worker routine
+ * Accepts: MAIL stream
+ * message cache element
+ * pointer to returned header text length
+ * option flags
+ */
+
+char *mmdf_text_work (MAILSTREAM *stream,MESSAGECACHE *elt,
+ unsigned long *length,long flags)
+{
+ FDDATA d;
+ STRING bs;
+ unsigned char c,*s,*t,*tl,tmp[CHUNKSIZE];
+ /* go to text position */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.msg.text.offset,L_SET);
+ if (flags & FT_INTERNAL) { /* initial data OK? */
+ if (elt->private.msg.text.text.size > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
+ elt->private.msg.text.text.size) + 1);
+ }
+ /* read message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.text.text.size);
+ /* got text, tie off string */
+ LOCAL->buf[*length = elt->private.msg.text.text.size] = '\0';
+ /* squeeze out CRs (in case from PC) */
+ for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t < tl; t++)
+ if (*t != '\r') *s++ = *t;
+ *s = '\0';
+ *length = s - LOCAL->buf; /* adjust length */
+ return (char *) LOCAL->buf;
+ }
+
+ /* have it cached already? */
+ if (elt->private.uid != LOCAL->uid) {
+ /* not cached, cache it now */
+ LOCAL->uid = elt->private.uid;
+ /* is buffer big enough? */
+ if (elt->rfc822_size > LOCAL->text.size) {
+ /* excessively conservative, but the right thing is too hard to do */
+ fs_give ((void **) &LOCAL->text.data);
+ LOCAL->text.data = (unsigned char *)
+ fs_get ((LOCAL->text.size = elt->rfc822_size) + 1);
+ }
+ d.fd = LOCAL->fd; /* yes, set up file descriptor */
+ d.pos = elt->private.special.offset + elt->private.msg.text.offset;
+ d.chunk = tmp; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE; /* file chunk size */
+ INIT (&bs,fd_string,&d,elt->private.msg.text.text.size);
+ for (s = (char *) LOCAL->text.data; SIZE (&bs);) switch (c = SNX (&bs)) {
+ case '\r': /* carriage return seen */
+ break;
+ case '\n':
+ *s++ = '\r'; /* insert a CR */
+ default:
+ *s++ = c; /* copy characters */
+ }
+ *s = '\0'; /* tie off buffer */
+ /* calculate length of cached data */
+ LOCAL->textlen = s - LOCAL->text.data;
+ }
+ *length = LOCAL->textlen; /* return from cache */
+ return (char *) LOCAL->text.data;
+}
+
+/* MMDF per-message modify flag
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void mmdf_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ /* only after finishing */
+ if (elt->valid) elt->private.dirty = LOCAL->dirty = T;
+}
+
+
+/* MMDF mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long mmdf_ping (MAILSTREAM *stream)
+{
+ DOTLOCK lock;
+ struct stat sbuf;
+ long reparse;
+ /* big no-op if not readwrite */
+ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock) {
+ if (stream->rdonly) { /* does he want to give up readwrite? */
+ /* checkpoint if we changed something */
+ if (LOCAL->dirty) mmdf_check (stream);
+ flock (LOCAL->ld,LOCK_UN);/* release readwrite lock */
+ close (LOCAL->ld); /* close the readwrite lock file */
+ LOCAL->ld = -1; /* no more readwrite lock fd */
+ unlink (LOCAL->lname); /* delete the readwrite lock file */
+ }
+ else { /* see if need to reparse */
+ if (!(reparse = (long) mail_parameters (NIL,GET_NETFSSTATBUG,NIL))) {
+ /* get current mailbox size */
+ if (LOCAL->fd >= 0) fstat (LOCAL->fd,&sbuf);
+ else if (stat (stream->mailbox,&sbuf)) {
+ sprintf (LOCAL->buf,"Mailbox stat failed, aborted: %s",
+ strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ mmdf_abort (stream);
+ return NIL;
+ }
+ reparse = (sbuf.st_size != LOCAL->filesize);
+ }
+ /* parse if mailbox changed */
+ if ((LOCAL->ddirty || reparse) && mmdf_parse (stream,&lock,LOCK_EX)) {
+ /* force checkpoint if double-dirty */
+ if (LOCAL->ddirty) mmdf_rewrite (stream,NIL,&lock,NIL);
+ /* unlock mailbox */
+ else mmdf_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream); /* and stream */
+ /* done with critical */
+ MM_NOCRITICAL (stream);
+ }
+ }
+ }
+ return LOCAL ? LONGT : NIL; /* return if still alive */
+}
+
+/* MMDF mail check mailbox
+ * Accepts: MAIL stream
+ */
+
+void mmdf_check (MAILSTREAM *stream)
+{
+ DOTLOCK lock;
+ /* parse and lock mailbox */
+ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock &&
+ mmdf_parse (stream,&lock,LOCK_EX)) {
+ /* any unsaved changes? */
+ if (LOCAL->dirty && mmdf_rewrite (stream,NIL,&lock,NIL)) {
+ if (!stream->silent) MM_LOG ("Checkpoint completed",NIL);
+ }
+ /* no checkpoint needed, just unlock */
+ else mmdf_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream); /* unlock the stream */
+ MM_NOCRITICAL (stream); /* done with critical */
+ }
+}
+
+
+/* MMDF mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long mmdf_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ unsigned long i;
+ DOTLOCK lock;
+ char *msg = NIL;
+ if (ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ LOCAL && (LOCAL->ld >= 0) && !stream->lock &&
+ mmdf_parse (stream,&lock,LOCK_EX)) {
+ /* check expunged messages if not dirty */
+ for (i = 1; !LOCAL->dirty && (i <= stream->nmsgs); i++) {
+ MESSAGECACHE *elt = mail_elt (stream,i);
+ if (mail_elt (stream,i)->deleted) LOCAL->dirty = T;
+ }
+ if (!LOCAL->dirty) { /* not dirty and no expunged messages */
+ mmdf_unlock (LOCAL->fd,stream,&lock);
+ msg = "No messages deleted, so no update needed";
+ }
+ else if (mmdf_rewrite (stream,&i,&lock,sequence ? LONGT : NIL)) {
+ if (i) sprintf (msg = LOCAL->buf,"Expunged %lu messages",i);
+ else msg = "Mailbox checkpointed, but no messages expunged";
+ }
+ /* rewrite failed */
+ else mmdf_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream); /* unlock the stream */
+ MM_NOCRITICAL (stream); /* done with critical */
+ if (msg && !stream->silent) MM_LOG (msg,NIL);
+ }
+ else if (!stream->silent)
+ MM_LOG ("Expunge ignored on readonly mailbox",WARN);
+ return ret;
+}
+
+/* MMDF mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if copy successful, else NIL
+ */
+
+long mmdf_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ int fd;
+ char *s,file[MAILTMPLEN];
+ DOTLOCK lock;
+ time_t tp[2];
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ long ret = T;
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ copyuid_t cu = (copyuid_t) (mail_parameters (NIL,GET_USERHASNOLIFE,NIL) ?
+ NIL : mail_parameters (NIL,GET_COPYUID,NIL));
+ SEARCHSET *source = cu ? mail_newsearchset () : NIL;
+ SEARCHSET *dest = cu ? mail_newsearchset () : NIL;
+ MAILSTREAM *tstream = NIL;
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* make sure destination is valid */
+ if (!(mmdf_valid (mailbox) || !errno))
+ switch (errno) {
+ case ENOENT: /* no such file? */
+ if (compare_cstring (mailbox,"INBOX")) {
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ }
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ mmdf_create (NIL,"INBOX");/* create empty INBOX */
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid MMDF-format mailbox name: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a MMDF-format mailbox: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+
+ /* try to open rewrite for UIDPLUS */
+ if ((tstream = mail_open_work (&mmdfdriver,NIL,mailbox,
+ OP_SILENT|OP_NOKOD)) && tstream->rdonly)
+ tstream = mail_close (tstream);
+ if (cu && !tstream) { /* wanted a COPYUID? */
+ sprintf (LOCAL->buf,"Unable to write-open mailbox for COPYUID: %.80s",
+ mailbox);
+ MM_LOG (LOCAL->buf,WARN);
+ cu = NIL; /* don't try to do COPYUID */
+ }
+ LOCAL->buf[0] = '\0';
+ MM_CRITICAL (stream); /* go critical */
+ if ((fd = mmdf_lock (dummy_file (file,mailbox),O_WRONLY|O_APPEND,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL),
+ &lock,LOCK_EX)) < 0) {
+ MM_NOCRITICAL (stream); /* done with critical */
+ sprintf (LOCAL->buf,"Can't open destination mailbox: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR); /* log the error */
+ return NIL; /* failed */
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ /* write all requested messages to mailbox */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size);
+ if (write (fd,LOCAL->buf,elt->private.special.text.size) < 0) ret = NIL;
+ else { /* internal header succeeded */
+ s = mmdf_header (stream,i,&j,FT_INTERNAL);
+ /* header size, sans trailing newline */
+ if (j && (s[j - 2] == '\n')) j--;
+ if (write (fd,s,j) < 0) ret = NIL;
+ else { /* message header succeeded */
+ j = tstream ? /* write UIDPLUS data if have readwrite */
+ mmdf_xstatus (stream,LOCAL->buf,elt,++(tstream->uid_last),LONGT) :
+ mmdf_xstatus (stream,LOCAL->buf,elt,NIL,NIL);
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ else { /* message status succeeded */
+ s = mmdf_text_work (stream,elt,&j,FT_INTERNAL);
+ if ((write (fd,s,j) < 0) || (write (fd,mmdfhdr,MMDFHDRLEN) < 0))
+ ret = NIL;
+ else if (cu) { /* need to pass back new UID? */
+ mail_append_set (source,mail_uid (stream,i));
+ mail_append_set (dest,tstream->uid_last);
+ }
+ }
+ }
+ }
+ }
+
+ if (!ret || fsync (fd)) { /* force out the update */
+ sprintf (LOCAL->buf,"Message copy failed: %s",strerror (errno));
+ ftruncate (fd,sbuf.st_size);
+ ret = NIL;
+ }
+ /* force UIDVALIDITY assignment now */
+ if (tstream && !tstream->uid_validity) tstream->uid_validity = time (0);
+ /* return sets if doing COPYUID */
+ if (cu && ret) (*cu) (stream,mailbox,tstream->uid_validity,source,dest);
+ else { /* flush any sets we may have built */
+ mail_free_searchset (&source);
+ mail_free_searchset (&dest);
+ }
+ tp[1] = time (0); /* set mtime to now */
+ if (ret) tp[0] = tp[1] - 1; /* set atime to now-1 if successful copy */
+ else tp[0] = /* else preserve \Marked status */
+ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ?
+ sbuf.st_atime : tp[1];
+ utime (file,tp); /* set the times */
+ mmdf_unlock (fd,NIL,&lock); /* unlock and close mailbox */
+ if (tstream) { /* update last UID if we can */
+ MMDFLOCAL *local = (MMDFLOCAL *) tstream->local;
+ local->dirty = T; /* do a rewrite */
+ local->appending = T; /* but not at the cost of marking as old */
+ tstream = mail_close (tstream);
+ }
+ /* log the error */
+ if (!ret) MM_LOG (LOCAL->buf,ERROR);
+ /* delete if requested message */
+ else if (options & CP_MOVE) for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence)
+ elt->deleted = elt->private.dirty = LOCAL->dirty = T;
+ MM_NOCRITICAL (stream); /* release critical */
+ return ret;
+}
+
+/* MMDF mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+#define BUFLEN 8*MAILTMPLEN
+
+long mmdf_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd;
+ unsigned long i;
+ char *flags,*date,buf[BUFLEN],tmp[MAILTMPLEN],file[MAILTMPLEN];
+ time_t tp[2];
+ FILE *sf,*df;
+ MESSAGECACHE elt;
+ DOTLOCK lock;
+ STRING *message;
+ unsigned long uidlocation = 0;
+ appenduid_t au = (appenduid_t)
+ (mail_parameters (NIL,GET_USERHASNOLIFE,NIL) ? NIL :
+ mail_parameters (NIL,GET_APPENDUID,NIL));
+ SEARCHSET *dst = au ? mail_newsearchset () : NIL;
+ long ret = LONGT;
+ MAILSTREAM *tstream = NIL;
+ /* default stream to prototype */
+ if (!stream) { /* stream specified? */
+ stream = &mmdfproto; /* no, default stream to prototype */
+ for (i = 0; i < NUSERFLAGS && stream->user_flags[i]; ++i)
+ fs_give ((void **) &stream->user_flags[i]);
+ }
+ if (!mmdf_valid (mailbox)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (compare_cstring (mailbox,"INBOX")) {
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ mmdf_create (NIL,"INBOX"); /* create empty INBOX */
+ case 0: /* merely empty file? */
+ tstream = stream;
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid MMDF-format mailbox name: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a MMDF-format mailbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get sniffing stream for keywords */
+ else if (!(tstream = mail_open (NIL,mailbox,
+ OP_READONLY|OP_SILENT|OP_NOKOD|OP_SNIFF))) {
+ sprintf (tmp,"Unable to examine mailbox for APPEND: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+
+ /* get first message */
+ if (!MM_APPEND (af) (tstream,data,&flags,&date,&message)) return NIL;
+ if (!(sf = tmpfile ())) { /* must have scratch file */
+ sprintf (tmp,".%lx.%lx",(unsigned long) time (0),(unsigned long)getpid ());
+ if (!stat (tmp,&sbuf) || !(sf = fopen (tmp,"wb+"))) {
+ sprintf (tmp,"Unable to create scratch file: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ unlink (tmp);
+ }
+ do { /* parse date */
+ if (!date) rfc822_date (date = tmp);
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ MM_LOG (tmp,ERROR);
+ }
+ else { /* user wants to suppress time zones? */
+ if (mail_parameters (NIL,GET_NOTIMEZONES,NIL)) {
+ time_t when = mail_longdate (&elt);
+ date = ctime (&when); /* use traditional date */
+ }
+ /* use POSIX-style date */
+ else date = mail_cdate (tmp,&elt);
+ if (!SIZE (message)) MM_LOG ("Append of zero-length message",ERROR);
+ else if (!mmdf_collect_msg (tstream,sf,flags,date,message)) {
+ sprintf (tmp,"Error writing scratch file: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ }
+ /* get next message */
+ else if (MM_APPEND (af) (tstream,data,&flags,&date,&message)) continue;
+ }
+ fclose (sf); /* punt scratch file */
+ return NIL; /* give up */
+ } while (message); /* until no more messages */
+ if (fflush (sf)) {
+ sprintf (tmp,"Error finishing scratch file: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ fclose (sf); /* punt scratch file */
+ return NIL; /* give up */
+ }
+ i = ftell (sf); /* size of scratch file */
+ if (tstream != stream) tstream = mail_close (tstream);
+
+ MM_CRITICAL (stream); /* go critical */
+ /* try to open readwrite for UIDPLUS */
+ if ((tstream = mail_open_work (&mmdfdriver,NIL,mailbox,
+ OP_SILENT|OP_NOKOD)) && tstream->rdonly)
+ tstream = mail_close (tstream);
+ if (au && !tstream) { /* wanted an APPENDUID? */
+ sprintf (tmp,"Unable to re-open mailbox for APPENDUID: %.80s",mailbox);
+ MM_LOG (tmp,WARN);
+ au = NIL;
+ }
+ if (((fd = mmdf_lock (dummy_file (file,mailbox),O_WRONLY|O_APPEND,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL),
+ &lock,LOCK_EX)) < 0) ||
+ !(df = fdopen (fd,"ab"))) {
+ MM_NOCRITICAL (stream); /* done with critical */
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ rewind (sf);
+ tp[1] = time (0); /* set mtime to now */
+ /* write all messages */
+ if (!mmdf_append_msgs (tstream,sf,df,au ? dst : NIL) ||
+ (fflush (df) == EOF) || fsync (fd)) {
+ sprintf (buf,"Message append failed: %s",strerror (errno));
+ MM_LOG (buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ tp[0] = /* preserve \Marked status */
+ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ?
+ sbuf.st_atime : tp[1];
+ ret = NIL; /* return error */
+ }
+ else tp[0] = tp[1] - 1; /* set atime to now-1 if successful copy */
+ utime (file,tp); /* set the times */
+ fclose (sf); /* done with scratch file */
+ /* force UIDVALIDITY assignment now */
+ if (tstream && !tstream->uid_validity) tstream->uid_validity = time (0);
+ /* return sets if doing APPENDUID */
+ if (au && ret) (*au) (mailbox,tstream->uid_validity,dst);
+ else mail_free_searchset (&dst);
+ mmdf_unlock (fd,NIL,&lock); /* unlock and close mailbox */
+ fclose (df);
+ if (tstream) { /* update last UID if we can */
+ MMDFLOCAL *local = (MMDFLOCAL *) tstream->local;
+ local->dirty = T; /* do a rewrite */
+ local->appending = T; /* but not at the cost of marking as old */
+ tstream = mail_close (tstream);
+ }
+ MM_NOCRITICAL (stream); /* release critical */
+ return ret;
+}
+
+/* Collect and write single message to append scratch file
+ * Accepts: MAIL stream
+ * scratch file
+ * flags
+ * date
+ * message stringstruct
+ * Returns: NIL if write error, else T
+ */
+
+int mmdf_collect_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
+ STRING *msg)
+{
+ unsigned char *s,*t;
+ unsigned long uf;
+ long f = mail_parse_flags (stream,flags,&uf);
+ /* write metadata, note date ends with NL */
+ if (fprintf (sf,"%ld %lu %s",f,SIZE (msg) + 1,date) < 0) return NIL;
+ while (uf) /* write user flags */
+ if ((s = stream->user_flags[find_rightmost_bit (&uf)]) &&
+ (fprintf (sf," %s",s) < 0)) return NIL;
+ if (putc ('\n',sf) == EOF) return NIL;
+ while (SIZE (msg)) { /* copy text to scratch file */
+ for (s = (unsigned char *) msg->curpos, t = s + msg->cursize; s < t; ++s)
+ if (!*s) *s = 0x80; /* disallow NUL */
+ /* write buffered text */
+ if (fwrite (msg->curpos,1,msg->cursize,sf) == msg->cursize)
+ SETPOS (msg,GETPOS (msg) + msg->cursize);
+ else return NIL; /* failed */
+ }
+ /* write trailing newline and return */
+ return (putc ('\n',sf) == EOF) ? NIL : T;
+}
+
+/* Append messages from scratch file to mailbox
+ * Accepts: MAIL stream
+ * source file
+ * destination file
+ * uidset to update if non-NIL
+ * Returns: T if success, NIL if failure
+ */
+
+int mmdf_append_msgs (MAILSTREAM *stream,FILE *sf,FILE *df,SEARCHSET *set)
+{
+ int c;
+ long f;
+ unsigned long i,j;
+ char *x,tmp[MAILTMPLEN];
+ int hdrp = T;
+ /* get message metadata line */
+ while (fgets (tmp,MAILTMPLEN,sf)) {
+ if (!(isdigit (tmp[0]) && strchr (tmp,'\n'))) return NIL;
+ f = strtol (tmp,&x,10); /* get flags */
+ if (!((*x++ == ' ') && isdigit (*x))) return NIL;
+ i = strtoul (x,&x,10); /* get message size */
+ if ((*x++ != ' ') || /* build initial header */
+ (fprintf (df,"%sFrom %s@%s %sStatus: ",mmdfhdr,myusername(),
+ mylocalhost(),x) < 0) ||
+ (f&fSEEN && (putc ('R',df) == EOF)) ||
+ (fputs ("\nX-Status: ",df) == EOF) ||
+ (f&fDELETED && (putc ('D',df) == EOF)) ||
+ (f&fFLAGGED && (putc ('F',df) == EOF)) ||
+ (f&fANSWERED && (putc ('A',df) == EOF)) ||
+ (f&fDRAFT && (putc ('T',df) == EOF)) ||
+ (fputs ("\nX-Keywords:",df) == EOF)) return NIL;
+ /* copy keywords */
+ while ((c = getc (sf)) != '\n') switch (c) {
+ case EOF:
+ return NIL;
+ default:
+ if (putc (c,df) == EOF) return NIL;
+ }
+ if ((putc ('\n',df) == EOF) ||
+ (set && (fprintf (df,"X-UID: %lu\n",++(stream->uid_last)) < 0)))
+ return NIL;
+
+ for (c = '\n'; i && fgets (tmp,MAILTMPLEN,sf); c = tmp[j-1]) {
+ /* get read line length */
+ if (i < (j = strlen (tmp))) fatal ("mmdf_append_msgs overrun");
+ i -= j; /* number of bytes left */
+ /* squish out ^A and CRs (note copies NUL) */
+ for (x = tmp; x = strpbrk (x,"\01\r"); --j) memmove (x,x+1,j-(x-tmp));
+ if (!j) continue; /* do nothing if line emptied */
+ /* start of line? */
+ if ((c == '\n')) switch (tmp[0]) {
+ case 'S': case 's': /* possible "Status:" */
+ if (hdrp && (j > 6) && ((tmp[1] == 't') || (tmp[1] == 'T')) &&
+ ((tmp[2] == 'a') || (tmp[2] == 'A')) &&
+ ((tmp[3] == 't') || (tmp[3] == 'T')) &&
+ ((tmp[4] == 'u') || (tmp[4] == 'U')) &&
+ ((tmp[5] == 's') || (tmp[5] == 'S')) && (tmp[6] == ':') &&
+ (fputs ("X-Original-",df) == EOF)) return NIL;
+ break;
+ case 'X': case 'x': /* possible X-??? header */
+ if (hdrp && (tmp[1] == '-') &&
+ /* possible X-UID: */
+ (((j > 5) && ((tmp[2] == 'U') || (tmp[2] == 'u')) &&
+ ((tmp[3] == 'I') || (tmp[3] == 'i')) &&
+ ((tmp[4] == 'D') || (tmp[4] == 'd')) && (tmp[5] == ':')) ||
+ /* possible X-IMAP: */
+ ((j > 6) && ((tmp[2] == 'I') || (tmp[2] == 'i')) &&
+ ((tmp[3] == 'M') || (tmp[3] == 'm')) &&
+ ((tmp[4] == 'A') || (tmp[4] == 'a')) &&
+ ((tmp[5] == 'P') || (tmp[5] == 'p')) &&
+ ((tmp[6] == ':') ||
+ /* or X-IMAPbase: */
+ ((j > 10) && ((tmp[6] == 'b') || (tmp[6] == 'B')) &&
+ ((tmp[7] == 'a') || (tmp[7] == 'A')) &&
+ ((tmp[8] == 's') || (tmp[8] == 'S')) &&
+ ((tmp[9] == 'e') || (tmp[9] == 'E')) && (tmp[10] == ':')))) ||
+ /* possible X-Status: */
+ ((j > 8) && ((tmp[2] == 'S') || (tmp[2] == 's')) &&
+ ((tmp[3] == 't') || (tmp[3] == 'T')) &&
+ ((tmp[4] == 'a') || (tmp[4] == 'A')) &&
+ ((tmp[5] == 't') || (tmp[5] == 'T')) &&
+ ((tmp[6] == 'u') || (tmp[6] == 'U')) &&
+ ((tmp[7] == 's') || (tmp[7] == 'S')) && (tmp[8] == ':')) ||
+ /* possible X-Keywords: */
+ ((j > 10) && ((tmp[2] == 'K') || (tmp[2] == 'k')) &&
+ ((tmp[3] == 'e') || (tmp[3] == 'E')) &&
+ ((tmp[4] == 'y') || (tmp[4] == 'Y')) &&
+ ((tmp[5] == 'w') || (tmp[5] == 'W')) &&
+ ((tmp[6] == 'o') || (tmp[6] == 'O')) &&
+ ((tmp[7] == 'r') || (tmp[7] == 'R')) &&
+ ((tmp[8] == 'd') || (tmp[8] == 'D')) &&
+ ((tmp[9] == 's') || (tmp[9] == 'S')) && (tmp[10] == ':'))) &&
+ (fputs ("X-Original-",df) == EOF)) return NIL;
+ break;
+ case '\n': /* blank line */
+ hdrp = NIL;
+ break;
+ default: /* nothing to do */
+ break;
+ }
+ /* just write the line */
+ if (fwrite (tmp,1,j,df) != j) return NIL;
+ }
+ /* make sure read entire msg & wrote trailer */
+ if (i || (fputs (mmdfhdr,df) == EOF)) return NIL;
+ /* update set */
+ if (stream) mail_append_set (set,stream->uid_last);
+ }
+ return T;
+}
+
+/* Internal routines */
+
+
+/* MMDF mail abort stream
+ * Accepts: MAIL stream
+ */
+
+void mmdf_abort (MAILSTREAM *stream)
+{
+ if (LOCAL) { /* only if a file is open */
+ if (LOCAL->fd >= 0) close (LOCAL->fd);
+ if (LOCAL->ld >= 0) { /* have a mailbox lock? */
+ flock (LOCAL->ld,LOCK_UN);/* yes, release the lock */
+ close (LOCAL->ld); /* close the lock file */
+ unlink (LOCAL->lname); /* and delete it */
+ }
+ if (LOCAL->lname) fs_give ((void **) &LOCAL->lname);
+ /* free local text buffers */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data);
+ if (LOCAL->linebuf) fs_give ((void **) &LOCAL->linebuf);
+ if (LOCAL->line) fs_give ((void **) &LOCAL->line);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* MMDF open and lock mailbox
+ * Accepts: file name to open/lock
+ * file open mode
+ * destination buffer for lock file name
+ * type of locking operation (LOCK_SH or LOCK_EX)
+ */
+
+int mmdf_lock (char *file,int flags,int mode,DOTLOCK *lock,int op)
+{
+ int fd;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ (*bn) (BLOCK_FILELOCK,NIL);
+ /* try locking the easy way */
+ if (dotlock_lock (file,lock,-1)) {
+ /* got dotlock file, easy open */
+ if ((fd = open (file,flags,mode)) >= 0) flock (fd,op);
+ else dotlock_unlock (lock); /* open failed, free the dotlock */
+ }
+ /* no dot lock file, open file now */
+ else if ((fd = open (file,flags,mode)) >= 0) {
+ /* try paranoid way to make a dot lock file */
+ if (dotlock_lock (file,lock,fd)) {
+ close (fd); /* get fresh fd in case of timing race */
+ if ((fd = open (file,flags,mode)) >= 0) flock (fd,op);
+ /* open failed, free the dotlock */
+ else dotlock_unlock (lock);
+ }
+ else flock (fd,op); /* paranoid way failed, just flock() it */
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return fd;
+}
+
+/* MMDF unlock and close mailbox
+ * Accepts: file descriptor
+ * (optional) mailbox stream to check atime/mtime
+ * (optional) lock file name
+ */
+
+void mmdf_unlock (int fd,MAILSTREAM *stream,DOTLOCK *lock)
+{
+ if (stream) { /* need to muck with times? */
+ struct stat sbuf;
+ time_t tp[2];
+ time_t now = time (0);
+ fstat (fd,&sbuf); /* get file times */
+ if (LOCAL->ld >= 0) { /* yes, readwrite session? */
+ tp[0] = now; /* set atime to now */
+ /* set mtime to (now - 1) if necessary */
+ tp[1] = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1;
+ }
+ else if (stream->recent) { /* readonly with recent messages */
+ if ((sbuf.st_atime >= sbuf.st_mtime) ||
+ (sbuf.st_atime >= sbuf.st_ctime))
+ /* keep past mtime, whack back atime */
+ tp[0] = (tp[1] = (sbuf.st_mtime < now) ? sbuf.st_mtime : now) - 1;
+ else now = 0; /* no time change needed */
+ }
+ /* readonly with no recent messages */
+ else if ((sbuf.st_atime < sbuf.st_mtime) ||
+ (sbuf.st_atime < sbuf.st_ctime)) {
+ tp[0] = now; /* set atime to now */
+ /* set mtime to (now - 1) if necessary */
+ tp[1] = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1;
+ }
+ else now = 0; /* no time change needed */
+ /* set the times, note change */
+ if (now && !utime (stream->mailbox,tp)) LOCAL->filetime = tp[1];
+ }
+ flock (fd,LOCK_UN); /* release flock'ers */
+ if (!stream) close (fd); /* close the file if no stream */
+ dotlock_unlock (lock); /* flush the lock file if any */
+}
+
+/* MMDF mail parse and lock mailbox
+ * Accepts: MAIL stream
+ * space to write lock file name
+ * type of locking operation
+ * Returns: T if parse OK, critical & mailbox is locked shared; NIL if failure
+ */
+
+int mmdf_parse (MAILSTREAM *stream,DOTLOCK *lock,int op)
+{
+ int ti,zn,m;
+ unsigned long i,j,k;
+ unsigned char c,*s,*t,*u,tmp[MAILTMPLEN],date[30];
+ int retain = T;
+ unsigned long nmsgs = stream->nmsgs;
+ unsigned long prevuid = nmsgs ? mail_elt (stream,nmsgs)->private.uid : 0;
+ unsigned long recent = stream->recent;
+ unsigned long oldnmsgs = stream->nmsgs;
+ short silent = stream->silent;
+ short pseudoseen = NIL;
+ struct stat sbuf;
+ STRING bs;
+ FDDATA d;
+ MESSAGECACHE *elt;
+ mail_lock (stream); /* guard against recursion or pingers */
+ /* toss out previous descriptor */
+ if (LOCAL->fd >= 0) close (LOCAL->fd);
+ MM_CRITICAL (stream); /* open and lock mailbox (shared OK) */
+ if ((LOCAL->fd = mmdf_lock (stream->mailbox,(LOCAL->ld >= 0) ?
+ O_RDWR : O_RDONLY,
+ (long)mail_parameters(NIL,GET_MBXPROTECTION,NIL),
+ lock,op)) < 0) {
+ sprintf (tmp,"Mailbox open failed, aborted: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ mmdf_abort (stream);
+ mail_unlock (stream);
+ MM_NOCRITICAL (stream); /* done with critical */
+ return NIL;
+ }
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ /* validate change in size */
+ if (sbuf.st_size < LOCAL->filesize) {
+ sprintf (tmp,"Mailbox shrank from %lu to %lu bytes, aborted",
+ (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size);
+ MM_LOG (tmp,ERROR); /* this is pretty bad */
+ mmdf_unlock (LOCAL->fd,stream,lock);
+ mmdf_abort (stream);
+ mail_unlock (stream);
+ MM_NOCRITICAL (stream); /* done with critical */
+ return NIL;
+ }
+
+ /* new data? */
+ else if (i = sbuf.st_size - LOCAL->filesize) {
+ d.fd = LOCAL->fd; /* yes, set up file descriptor */
+ d.pos = LOCAL->filesize; /* get to that position in the file */
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE; /* file chunk size */
+ INIT (&bs,fd_string,&d,i); /* initialize stringstruct */
+ /* skip leading whitespace for broken MTAs */
+ while (((c = CHR (&bs)) == '\n') || (c == '\r') ||
+ (c == ' ') || (c == '\t')) SNX (&bs);
+ if (SIZE (&bs)) { /* read new data */
+ /* remember internal header position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ s = mmdf_mbxline (stream,&bs,&i);
+ stream->silent = T; /* quell main program new message events */
+ do { /* read MMDF header */
+ if (!(i && ISMMDF (s))){/* see if valid MMDF header */
+ sprintf (tmp,"Unexpected changes to mailbox (try restarting): %.20s",
+ (char *) s);
+ /* see if we can back up to a line */
+ if (i && (j > MMDFHDRLEN)) {
+ SETPOS (&bs,j -= MMDFHDRLEN);
+ /* read previous line */
+ s = mmdf_mbxline (stream,&bs,&i);
+ /* kill the error if it looks good */
+ if (i && ISMMDF (s)) tmp[0] = '\0';
+ }
+ if (tmp[0]) {
+ MM_LOG (tmp,ERROR);
+ mmdf_unlock (LOCAL->fd,stream,lock);
+ mmdf_abort (stream);
+ mail_unlock (stream);
+ MM_NOCRITICAL (stream);
+ return NIL;
+ }
+ }
+ /* instantiate first new message */
+ mail_exists (stream,++nmsgs);
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ recent++; /* assume recent by default */
+ elt->recent = T;
+ /* note position/size of internal header */
+ elt->private.special.offset = j;
+ elt->private.special.text.size = i;
+
+ s = mmdf_mbxline (stream,&bs,&i);
+ ti = 0; /* assume not a valid date */
+ zn = 0,t = NIL;
+ if (i) VALID (s,t,ti,zn);
+ if (ti) { /* generate plausible IMAPish date string */
+ /* this is also part of header */
+ elt->private.special.text.size += i;
+ date[2] = date[6] = date[20] = '-'; date[11] = ' ';
+ date[14] = date[17] = ':';
+ /* dd */
+ date[0] = t[ti - 2]; date[1] = t[ti - 1];
+ /* mmm */
+ date[3] = t[ti - 6]; date[4] = t[ti - 5]; date[5] = t[ti - 4];
+ /* hh */
+ date[12] = t[ti + 1]; date[13] = t[ti + 2];
+ /* mm */
+ date[15] = t[ti + 4]; date[16] = t[ti + 5];
+ if (t[ti += 6]==':'){ /* ss */
+ date[18] = t[++ti]; date[19] = t[++ti];
+ ti++; /* move to space */
+ }
+ else date[18] = date[19] = '0';
+ /* yy -- advance over timezone if necessary */
+ if (zn == ti) ti += (((t[zn+1] == '+') || (t[zn+1] == '-')) ? 6 : 4);
+ date[7] = t[ti + 1]; date[8] = t[ti + 2];
+ date[9] = t[ti + 3]; date[10] = t[ti + 4];
+ /* zzz */
+ t = zn ? (t + zn + 1) : (unsigned char *) "LCL";
+ date[21] = *t++; date[22] = *t++; date[23] = *t++;
+ if ((date[21] != '+') && (date[21] != '-')) date[24] = '\0';
+ else { /* numeric time zone */
+ date[24] = *t++; date[25] = *t++;
+ date[26] = '\0'; date[20] = ' ';
+ }
+ /* set internal date */
+ if (!mail_parse_date (elt,date)) {
+ sprintf (tmp,"Unable to parse internal date: %s",(char *) date);
+ MM_LOG (tmp,WARN);
+ }
+ }
+ else { /* make date from file date */
+ struct tm *tm = gmtime (&sbuf.st_mtime);
+ elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1;
+ elt->year = tm->tm_year + 1900 - BASEYEAR;
+ elt->hours = tm->tm_hour; elt->minutes = tm->tm_min;
+ elt->seconds = tm->tm_sec;
+ elt->zhours = 0; elt->zminutes = 0;
+ t = NIL; /* suppress line read */
+ }
+ /* header starts here */
+ elt->private.msg.header.offset = elt->private.special.text.size;
+
+ do { /* look for message body */
+ j = GETPOS (&bs); /* note position before line */
+ if (t) s = t = mmdf_mbxline (stream,&bs,&i);
+ else t = s; /* this line read was suppressed */
+ if (ISMMDF (s)) { /* found terminator in header? */
+ SETPOS (&bs,j); /* oops, back up before line */
+ /* must insert a newline */
+ elt->private.spare.data++;
+ break; /* punt */
+ }
+ /* this line is part of header */
+ elt->private.msg.header.text.size += i;
+ if (i) switch (*s) { /* check header lines */
+ case 'X': /* possible X-???: line */
+ if (s[1] == '-') { /* must be immediately followed by hyphen */
+ /* X-Status: becomes Status: in S case */
+ if (s[2] == 'S' && s[3] == 't' && s[4] == 'a' && s[5] == 't' &&
+ s[6] == 'u' && s[7] == 's' && s[8] == ':') s += 2;
+ /* possible X-Keywords */
+ else if (s[2] == 'K' && s[3] == 'e' && s[4] == 'y' &&
+ s[5] == 'w' && s[6] == 'o' && s[7] == 'r' &&
+ s[8] == 'd' && s[9] == 's' && s[10] == ':') {
+ SIZEDTEXT uf;
+ retain = NIL; /* don't retain continuation */
+ s += 11; /* flush leading whitespace */
+ while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n'))){
+ while (*s == ' ') s++;
+ /* find end of keyword */
+ if (!(u = strpbrk (s," \n\r"))) u = s + strlen (s);
+ /* got a keyword? */
+ if ((k = (u - s)) && (k <= MAXUSERFLAG)) {
+ uf.data = (unsigned char *) s;
+ uf.size = k;
+ for (j = 0; (j < NUSERFLAGS) && stream->user_flags[j]; ++j)
+ if (!compare_csizedtext (stream->user_flags[j],&uf)) {
+ elt->user_flags |= ((long) 1) << j;
+ break;
+ }
+ }
+ s = u; /* advance to next keyword */
+ }
+ break;
+ }
+
+ /* possible X-IMAP */
+ else if ((s[2] == 'I') && (s[3] == 'M') && (s[4] == 'A') &&
+ (s[5] == 'P') && ((m = (s[6] == ':')) ||
+ ((s[6] == 'b') && (s[7] == 'a') &&
+ (s[8] == 's') && (s[9] == 'e') &&
+ (s[10] == ':')))) {
+ retain = NIL; /* don't retain continuation */
+ if ((nmsgs == 1) && !stream->uid_validity) {
+ /* advance to data */
+ s += m ? 7 : 11;
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ j = 0; /* slurp UID validity */
+ /* found a digit? */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ /* must have valid UID validity and UID last */
+ if (j && isdigit (*s)) {
+ /* pseudo-header seen if X-IMAP */
+ if (m) pseudoseen = LOCAL->pseudo = T;
+ /* save UID validity */
+ stream->uid_validity = j;
+ j = 0; /* slurp UID last */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* save UID last */
+ stream->uid_last = j;
+ /* process keywords */
+ for (j = 0; (*s != '\n') && ((*s != '\r')||(s[1] != '\n'));
+ s = u,j++) {
+ /* flush leading whitespace */
+ while (*s == ' ') s++;
+ u = strpbrk (s," \n\r");
+ /* got a keyword? */
+ if ((j < NUSERFLAGS) && (k = (u - s)) &&
+ (k <= MAXUSERFLAG)) {
+ if (stream->user_flags[j])
+ fs_give ((void **) &stream->user_flags[j]);
+ stream->user_flags[j] = (char *) fs_get (k + 1);
+ strncpy (stream->user_flags[j],s,k);
+ stream->user_flags[j][k] = '\0';
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ /* possible X-UID */
+ else if (s[2] == 'U' && s[3] == 'I' && s[4] == 'D' &&
+ s[5] == ':') {
+ retain = NIL; /* don't retain continuation */
+ /* only believe if have a UID validity */
+ if (stream->uid_validity && ((nmsgs > 1) || !pseudoseen)) {
+ s += 6; /* advance to UID value */
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ j = 0;
+ /* found a digit? */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* flush remainder of line */
+ while (*s != '\n') s++;
+ /* make sure not duplicated */
+ if (elt->private.uid)
+ sprintf (tmp,"Message %lu UID %lu already has UID %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,elt->private.uid);
+ /* make sure UID doesn't go backwards */
+ else if (j <= prevuid)
+ sprintf (tmp,"Message %lu UID %lu less than %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,prevuid + 1);
+#if 0 /* this is currently broken by UIDPLUS */
+ /* or skip by mailbox's recorded last */
+ else if (j > stream->uid_last)
+ sprintf (tmp,"Message %lu UID %lu greater than last %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,stream->uid_last);
+#endif
+ else { /* normal UID case */
+ prevuid = elt->private.uid = j;
+#if 1 /* temporary kludge for UIDPLUS */
+ if (prevuid > stream->uid_last) {
+ stream->uid_last = prevuid;
+ LOCAL->ddirty = LOCAL->dirty = T;
+ }
+#endif
+ break; /* exit this cruft */
+ }
+ MM_LOG (tmp,WARN);
+ /* invalidate UID validity */
+ stream->uid_validity = 0;
+ elt->private.uid = 0;
+ }
+ break;
+ }
+ }
+ /* otherwise fall into S case */
+
+ case 'S': /* possible Status: line */
+ if (s[0] == 'S' && s[1] == 't' && s[2] == 'a' && s[3] == 't' &&
+ s[4] == 'u' && s[5] == 's' && s[6] == ':') {
+ retain = NIL; /* don't retain continuation */
+ s += 6; /* advance to status flags */
+ do switch (*s++) {/* parse flags */
+ case 'R': /* message read */
+ elt->seen = T;
+ break;
+ case 'O': /* message old */
+ if (elt->recent) {
+ elt->recent = NIL;
+ recent--; /* it really wasn't recent */
+ }
+ break;
+ case 'D': /* message deleted */
+ elt->deleted = T;
+ break;
+ case 'F': /* message flagged */
+ elt->flagged = T;
+ break;
+ case 'A': /* message answered */
+ elt->answered = T;
+ break;
+ case 'T': /* message is a draft */
+ elt->draft = T;
+ break;
+ default: /* some other crap */
+ break;
+ } while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n')));
+ break; /* all done */
+ }
+ /* otherwise fall into default case */
+
+ default: /* ordinary header line */
+ if ((*s == 'S') || (*s == 's') ||
+ (((*s == 'X') || (*s == 'x')) && (s[1] == '-'))) {
+ unsigned char *e,*v;
+ /* must match what mail_filter() does */
+ for (u = s,v = tmp,e = u + min (i,MAILTMPLEN - 1);
+ (u < e) && ((c = (*u ? *u : (*u = ' '))) != ':') &&
+ ((c > ' ') || ((c != ' ') && (c != '\t') &&
+ (c != '\r') && (c != '\n')));
+ *v++ = *u++);
+ *v = '\0'; /* tie off */
+ /* matches internal header? */
+ if (!compare_cstring (tmp,"STATUS") ||
+ !compare_cstring (tmp,"X-STATUS") ||
+ !compare_cstring (tmp,"X-KEYWORDS") ||
+ !compare_cstring (tmp,"X-UID") ||
+ !compare_cstring (tmp,"X-IMAP") ||
+ !compare_cstring (tmp,"X-IMAPBASE")) {
+ char err[MAILTMPLEN];
+ sprintf (err,"Discarding bogus %s header in message %lu",
+ (char *) tmp,elt->msgno);
+ MM_LOG (err,WARN);
+ retain = NIL; /* don't retain continuation */
+ break; /* different case or something */
+ }
+ }
+ /* retain or non-continuation? */
+ if (retain || ((*s != ' ') && (*s != '\t'))) {
+ retain = T; /* retaining continuation now */
+ /* line length in LF format newline */
+ for (j = k = 0; j < i; ++j) if (s[j] != '\r') ++k;
+ /* "internal" header size */
+ elt->private.spare.data += k;
+ /* message size */
+ elt->rfc822_size += k + 1;
+ }
+ else {
+ char err[MAILTMPLEN];
+ sprintf (err,"Discarding bogus continuation in msg %lu: %.80s",
+ elt->msgno,(char *) s);
+ if (u = strpbrk (err,"\r\n")) *u = '\0';
+ MM_LOG (err,WARN);
+ break; /* different case or something */
+ }
+ break;
+ }
+ } while (i && (*t != '\n') && ((*t != '\r') || (t[1] != '\n')));
+ /* "internal" header sans trailing newline */
+ if (i) elt->private.spare.data--;
+ /* assign a UID if none found */
+ if (((nmsgs > 1) || !pseudoseen) && !elt->private.uid) {
+ prevuid = elt->private.uid = ++stream->uid_last;
+ elt->private.dirty = T;
+ }
+ else elt->private.dirty = elt->recent;
+
+ /* note size of header, location of text */
+ elt->private.msg.header.text.size =
+ (elt->private.msg.text.offset =
+ (LOCAL->filesize + GETPOS (&bs)) - elt->private.special.offset) -
+ elt->private.special.text.size;
+ /* note current position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ if (i) do { /* look for next message */
+ s = mmdf_mbxline (stream,&bs,&i);
+ if (i) { /* got new data? */
+ if (ISMMDF (s)) break;
+ else { /* not a header line, add it to message */
+ elt->rfc822_size += i;
+ for (j = 0; j < i; ++j) switch (s[j]) {
+ case '\r': /* squeeze out CRs */
+ elt->rfc822_size -= 1;
+ break;
+ case '\n': /* LF becomes CRLF */
+ elt->rfc822_size += 1;
+ break;
+ default:
+ break;
+ }
+ /* update current position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ }
+ }
+ } while (i); /* until found a header */
+ elt->private.msg.text.text.size = j -
+ (elt->private.special.offset + elt->private.msg.text.offset);
+ if (i) { /* get next header line */
+ /* remember first internal header position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ s = mmdf_mbxline (stream,&bs,&i);
+ }
+ /* until end of buffer */
+ } while (!stream->sniff && i);
+ if (pseudoseen) { /* flush pseudo-message if present */
+ /* decrement recent count */
+ if (mail_elt (stream,1)->recent) recent--;
+ /* and the exists count */
+ mail_exists (stream,nmsgs--);
+ mail_expunged(stream,1);/* fake an expunge of that message */
+ }
+ /* need to start a new UID validity? */
+ if (!stream->uid_validity) {
+ stream->uid_validity = time (0);
+ /* in case a whiner with no life */
+ if (mail_parameters (NIL,GET_USERHASNOLIFE,NIL))
+ stream->uid_nosticky = T;
+ else if (nmsgs) { /* don't bother if empty file */
+ /* make dirty to restart UID epoch */
+ LOCAL->ddirty = LOCAL->dirty = T;
+ /* need to rewrite msg 1 if not pseudo */
+ if (!LOCAL->pseudo) mail_elt (stream,1)->private.dirty = T;
+ MM_LOG ("Assigning new unique identifiers to all messages",NIL);
+ }
+ }
+ stream->nmsgs = oldnmsgs; /* whack it back down */
+ stream->silent = silent; /* restore old silent setting */
+ /* notify upper level of new mailbox sizes */
+ mail_exists (stream,nmsgs);
+ mail_recent (stream,recent);
+ /* mark dirty so O flags are set */
+ if (recent) LOCAL->dirty = T;
+ }
+ }
+ /* no change, don't babble if never got time */
+ else if (LOCAL->filetime && LOCAL->filetime != sbuf.st_mtime)
+ MM_LOG ("New mailbox modification time but apparently no changes",WARN);
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ LOCAL->filetime = sbuf.st_mtime;
+ return T; /* return the winnage */
+}
+
+/* MMDF read line from mailbox
+ * Accepts: mail stream
+ * stringstruct
+ * pointer to line size
+ * Returns: pointer to input line
+ */
+
+char *mmdf_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size)
+{
+ unsigned long i,j,k,m;
+ char *s,*t,*te;
+ char *ret = "";
+ /* flush old buffer */
+ if (LOCAL->line) fs_give ((void **) &LOCAL->line);
+ /* if buffer needs refreshing */
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ if (SIZE (bs)) { /* find newline */
+ /* end of fast scan */
+ te = (t = (s = bs->curpos) + bs->cursize) - 12;
+ while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) {
+ --s; /* back up */
+ break; /* exit loop */
+ }
+ /* final character-at-a-time scan */
+ while ((s < t) && (*s != '\n')) ++s;
+ /* difficult case if line spans buffer */
+ if ((i = s - bs->curpos) == bs->cursize) {
+ /* have space in line buffer? */
+ if (i > LOCAL->linebuflen) {
+ fs_give ((void **) &LOCAL->linebuf);
+ LOCAL->linebuf = (char *) fs_get (LOCAL->linebuflen = i);
+ }
+ /* remember what we have so far */
+ memcpy (LOCAL->linebuf,bs->curpos,i);
+ /* load next buffer */
+ SETPOS (bs,k = GETPOS (bs) + i);
+ /* end of fast scan */
+ te = (t = (s = bs->curpos) + bs->cursize) - 12;
+ /* fast scan in overlap buffer */
+ while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) {
+ --s; /* back up */
+ break; /* exit loop */
+ }
+
+ /* final character-at-a-time scan */
+ while ((s < t) && (*s != '\n')) ++s;
+ /* huge line? */
+ if ((j = s - bs->curpos) == bs->cursize) {
+ SETPOS (bs,GETPOS (bs) + j);
+ /* look for end of line (s-l-o-w!!) */
+ for (m = SIZE (bs); m && (SNX (bs) != '\n'); --m,++j);
+ SETPOS (bs,k); /* go back to where it started */
+ }
+ /* got size of data, make buffer for return */
+ ret = LOCAL->line = (char *) fs_get (i + j + 2);
+ /* copy first chunk */
+ memcpy (ret,LOCAL->linebuf,i);
+ while (j) { /* copy remainder */
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ memcpy (ret + i,bs->curpos,k = min (j,bs->cursize));
+ i += k; /* account for this much read in */
+ j -= k;
+ bs->curpos += k; /* increment new position */
+ bs->cursize -= k; /* eat that many bytes */
+ }
+ /* read newline at end */
+ if (SIZE (bs)) ret[i++] = SNX (bs);
+ ret[i] = '\0'; /* makes debugging easier */
+ }
+ else { /* this is easy */
+ ret = bs->curpos; /* string it at this position */
+ bs->curpos += ++i; /* increment new position */
+ bs->cursize -= i; /* eat that many bytes */
+ }
+ *size = i; /* return that to user */
+ }
+ else *size = 0; /* end of data, return empty */
+ /* embedded MMDF header at end of line? */
+ if ((*size > sizeof (MMDFHDRTXT)) &&
+ (s = ret + *size - (i = sizeof (MMDFHDRTXT) - 1)) && ISMMDF (s)) {
+ SETPOS (bs,GETPOS (bs) - i);/* back up to start of MMDF header */
+ *size -= i; /* reduce length of line */
+ ret[*size - 1] = '\n'; /* force newline at end */
+ }
+ return ret;
+}
+
+/* MMDF make pseudo-header
+ * Accepts: MAIL stream
+ * buffer to write pseudo-header
+ * Returns: length of pseudo-header
+ */
+
+unsigned long mmdf_pseudo (MAILSTREAM *stream,char *hdr)
+{
+ int i;
+ char *s,tmp[MAILTMPLEN];
+ time_t now = time (0);
+ rfc822_fixed_date (tmp);
+ sprintf (hdr,"%sFrom %s %.24s\nDate: %s\nFrom: %s <%s@%.80s>\nSubject: %s\nMessage-ID: <%lu@%.80s>\nX-IMAP: %010lu %010lu",
+ mmdfhdr,pseudo_from,ctime (&now),
+ tmp,pseudo_name,pseudo_from,mylocalhost (),pseudo_subject,
+ (unsigned long) now,mylocalhost (),stream->uid_validity,
+ stream->uid_last);
+ for (s = hdr + strlen (hdr),i = 0; i < NUSERFLAGS; ++i)
+ if (stream->user_flags[i])
+ sprintf (s += strlen (s)," %s",stream->user_flags[i]);
+ sprintf (s += strlen (s),"\nStatus: RO\n\n%s\n%s",pseudo_msg,mmdfhdr);
+ return strlen (hdr);
+}
+
+/* MMDF make status string
+ * Accepts: MAIL stream
+ * destination string to write
+ * message cache entry
+ * UID to write if non-zero (else use elt->private.uid)
+ * non-zero flag to write UID (.LT. 0 to write UID base info too)
+ * Returns: length of string
+ */
+
+unsigned long mmdf_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt,
+ unsigned long uid,long flag)
+{
+ char *t,stack[64];
+ char *s = status;
+ unsigned long n;
+ int pad = 50;
+ int sticky = uid ? T : !stream->uid_nosticky;
+ /* This used to use sprintf(), but thanks to certain cretinous C libraries
+ with horribly slow implementations of sprintf() I had to change it to this
+ mess. At least it should be fast. */
+ if ((flag < 0) && sticky) { /* need to write X-IMAPbase: header? */
+ *s++ = 'X'; *s++ = '-'; *s++ = 'I'; *s++ = 'M'; *s++ = 'A'; *s++ = 'P';
+ *s++ = 'b'; *s++ = 'a'; *s++ = 's'; *s++ = 'e'; *s++ = ':'; *s++ = ' ';
+ t = stack;
+ n = stream->uid_validity; /* push UID validity digits on the stack */
+ do *t++ = (char) (n % 10) + '0';
+ while (n /= 10);
+ /* pop UID validity digits from stack */
+ while (t > stack) *s++ = *--t;
+ *s++ = ' ';
+ n = stream->uid_last; /* push UID last digits on the stack */
+ do *t++ = (char) (n % 10) + '0';
+ while (n /= 10);
+ /* pop UID last digits from stack */
+ while (t > stack) *s++ = *--t;
+ for (n = 0; n < NUSERFLAGS; ++n) if (t = stream->user_flags[n])
+ for (*s++ = ' '; *t; *s++ = *t++);
+ *s++ = '\n';
+ pad += 30; /* increased padding if have IMAPbase */
+ }
+ *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't'; *s++ = 'u'; *s++ = 's';
+ *s++ = ':'; *s++ = ' ';
+ if (elt->seen) *s++ = 'R';
+ /* only write O if have a UID */
+ if (flag && (!elt->recent || !LOCAL->appending)) *s++ = 'O';
+ *s++ = '\n';
+ *s++ = 'X'; *s++ = '-'; *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't';
+ *s++ = 'u'; *s++ = 's'; *s++ = ':'; *s++ = ' ';
+ if (elt->deleted) *s++ = 'D';
+ if (elt->flagged) *s++ = 'F';
+ if (elt->answered) *s++ = 'A';
+ if (elt->draft) *s++ = 'T';
+ *s++ = '\n';
+
+ if (sticky) { /* only do this if UIDs sticky */
+ *s++ = 'X'; *s++ = '-'; *s++ = 'K'; *s++ = 'e'; *s++ = 'y'; *s++ = 'w';
+ *s++ = 'o'; *s++ = 'r'; *s++ = 'd'; *s++ = 's'; *s++ = ':';
+ if (n = elt->user_flags) do {
+ *s++ = ' ';
+ for (t = stream->user_flags[find_rightmost_bit (&n)]; *t; *s++ = *t++);
+ } while (n);
+ n = s - status; /* get size of stuff so far */
+ /* pad X-Keywords to make size constant */
+ if (n < pad) for (n = pad - n; n > 0; --n) *s++ = ' ';
+ *s++ = '\n';
+ if (flag) { /* want to include UID? */
+ t = stack;
+ /* push UID digits on the stack */
+ n = uid ? uid : elt->private.uid;
+ do *t++ = (char) (n % 10) + '0';
+ while (n /= 10);
+ *s++ = 'X'; *s++ = '-'; *s++ = 'U'; *s++ = 'I'; *s++ = 'D'; *s++ = ':';
+ *s++ = ' ';
+ /* pop UID from stack */
+ while (t > stack) *s++ = *--t;
+ *s++ = '\n';
+ }
+ }
+ *s++ = '\n'; *s = '\0'; /* end of extended message status */
+ return s - status; /* return size of resulting string */
+}
+
+/* Rewrite mailbox file
+ * Accepts: MAIL stream, must be critical and locked
+ * return pointer to number of expunged messages if want expunge
+ * lock file name
+ * expunge sequence, not deleted flag
+ * Returns: T if success and mailbox unlocked, NIL if failure
+ */
+
+#define OVERFLOWBUFLEN 8192 /* initial overflow buffer length */
+
+long mmdf_rewrite (MAILSTREAM *stream,unsigned long *nexp,DOTLOCK *lock,
+ long flags)
+{
+ MESSAGECACHE *elt;
+ MMDFFILE f;
+ char *s;
+ time_t tp[2];
+ long ret,flag;
+ unsigned long i,j;
+ unsigned long recent = stream->recent;
+ unsigned long size = LOCAL->pseudo ? mmdf_pseudo (stream,LOCAL->buf) : 0;
+ if (nexp) *nexp = 0; /* initially nothing expunged */
+ /* calculate size of mailbox after rewrite */
+ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs; i++) {
+ elt = mail_elt (stream,i); /* get cache */
+ if (!(nexp && elt->deleted && (flags ? elt->sequence : T))) {
+ /* add RFC822 size of this message */
+ size += elt->private.special.text.size + elt->private.spare.data +
+ mmdf_xstatus (stream,LOCAL->buf,elt,NIL,flag) +
+ elt->private.msg.text.text.size + MMDFHDRLEN;
+ flag = 1; /* only count X-IMAPbase once */
+ }
+ }
+ /* no messages, has a life, and no pseudo */
+ if (!size && !mail_parameters (NIL,GET_USERHASNOLIFE,NIL)) {
+ LOCAL->pseudo = T; /* so make a pseudo-message now */
+ size = mmdf_pseudo (stream,LOCAL->buf);
+ }
+ /* extend the file as necessary */
+ if (ret = mmdf_extend (stream,size)) {
+ /* Set up buffered I/O file structure
+ * curpos current position being written through buffering
+ * filepos current position being written physically to the disk
+ * bufpos current position being written in the buffer
+ * protect current maximum position that can be written to the disk
+ * before buffering is forced
+ * The code tries to buffer so that that disk is written in multiples of
+ * OVERBLOWBUFLEN bytes.
+ */
+ f.stream = stream; /* note mail stream */
+ f.curpos = f.filepos = 0; /* start of file */
+ f.protect = stream->nmsgs ? /* initial protection pointer */
+ mail_elt (stream,1)->private.special.offset : 8192;
+ f.bufpos = f.buf = (char *) fs_get (f.buflen = OVERFLOWBUFLEN);
+
+ if (LOCAL->pseudo) /* update pseudo-header */
+ mmdf_write (&f,LOCAL->buf,mmdf_pseudo (stream,LOCAL->buf));
+ /* loop through all messages */
+ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs;) {
+ elt = mail_elt (stream,i);/* get cache */
+ /* expunge this message? */
+ if (nexp && elt->deleted && (flags ? elt->sequence : T)) {
+ /* one less recent message */
+ if (elt->recent) --recent;
+ mail_expunged(stream,i);/* notify upper levels */
+ ++*nexp; /* count up one more expunged message */
+ }
+ else { /* preserve this message */
+ i++; /* advance to next message */
+ if ((flag < 0) || /* need to rewrite message? */
+ elt->private.dirty || (f.curpos != elt->private.special.offset) ||
+ (elt->private.msg.header.text.size !=
+ (elt->private.spare.data +
+ mmdf_xstatus (stream,LOCAL->buf,elt,NIL,flag)))) {
+ unsigned long newoffset = f.curpos;
+ /* yes, seek to internal header */
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size);
+ /* see if need to squeeze out a CR */
+ if (LOCAL->buf[elt->private.special.text.size - 2] == '\r') {
+ LOCAL->buf[--elt->private.special.text.size - 1] = '\n';
+ --size; /* squeezed out a CR from PC */
+ }
+ /* protection pointer moves to RFC822 header */
+ f.protect = elt->private.special.offset +
+ elt->private.msg.header.offset;
+ /* write internal header */
+ mmdf_write (&f,LOCAL->buf,elt->private.special.text.size);
+ /* get RFC822 header */
+ s = mmdf_header (stream,elt->msgno,&j,FT_INTERNAL);
+ /* in case this got decremented */
+ elt->private.msg.header.offset = elt->private.special.text.size;
+ /* header size, sans trailing newline */
+ if ((j < 2) || (s[j - 2] == '\n')) j--;
+ /* this can happen if CRs were squeezed */
+ if (j < elt->private.spare.data) {
+ /* so fix up counts */
+ size -= elt->private.spare.data - j;
+ elt->private.spare.data = j;
+ }
+ else if (j != elt->private.spare.data)
+ fatal ("header size inconsistent");
+ /* protection pointer moves to RFC822 text */
+ f.protect = elt->private.special.offset +
+ elt->private.msg.text.offset;
+ mmdf_write (&f,s,j); /* write RFC822 header */
+ /* write status and UID */
+ mmdf_write (&f,LOCAL->buf,
+ j = mmdf_xstatus (stream,LOCAL->buf,elt,NIL,flag));
+ flag = 1; /* only write X-IMAPbase once */
+ /* new file header size */
+ elt->private.msg.header.text.size = elt->private.spare.data + j;
+
+ /* did text move? */
+ if (f.curpos != f.protect) {
+ /* get message text */
+ s = mmdf_text_work (stream,elt,&j,FT_INTERNAL);
+ /* this can happen if CRs were squeezed */
+ if (j < elt->private.msg.text.text.size) {
+ /* so fix up counts */
+ size -= elt->private.msg.text.text.size - j;
+ elt->private.msg.text.text.size = j;
+ }
+ /* can't happen it says here */
+ else if (j > elt->private.msg.text.text.size)
+ fatal ("text size inconsistent");
+ /* new text offset, status/UID may change it */
+ elt->private.msg.text.offset = f.curpos - newoffset;
+ /* protection pointer moves to next message */
+ f.protect = (i <= stream->nmsgs) ?
+ mail_elt (stream,i)->private.special.offset :
+ (f.curpos + j + MMDFHDRLEN);
+ mmdf_write (&f,s,j);/* write text */
+ /* write trailing newline */
+ mmdf_write (&f,mmdfhdr,MMDFHDRLEN);
+ }
+ else { /* tie off header and status */
+ mmdf_write (&f,NIL,NIL);
+ f.curpos = f.protect =/* restart everything at end of message */
+ f.filepos += elt->private.msg.text.text.size + MMDFHDRLEN;
+ }
+ /* new internal header offset */
+ elt->private.special.offset = newoffset;
+ elt->private.dirty =NIL;/* message is now clean */
+ }
+ else { /* no need to rewrite this message */
+ /* tie off previous message if needed */
+ mmdf_write (&f,NIL,NIL);
+ f.curpos = f.protect =/* restart everything at end of message */
+ f.filepos += elt->private.special.text.size +
+ elt->private.msg.header.text.size +
+ elt->private.msg.text.text.size + MMDFHDRLEN;
+ }
+ }
+ }
+
+ mmdf_write (&f,NIL,NIL); /* tie off final message */
+ if (size != f.filepos) fatal ("file size inconsistent");
+ fs_give ((void **) &f.buf); /* free buffer */
+ /* make sure tied off */
+ ftruncate (LOCAL->fd,LOCAL->filesize = size);
+ fsync (LOCAL->fd); /* make sure the updates take */
+ if (size && (flag < 0)) fatal ("lost UID base information");
+ /* no longer dirty */
+ LOCAL->ddirty = LOCAL->dirty = NIL;
+ /* notify upper level of new mailbox sizes */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ /* set atime to now, mtime a second earlier */
+ tp[1] = (tp[0] = time (0)) - 1;
+ /* set the times, note change */
+ if (!utime (stream->mailbox,tp)) LOCAL->filetime = tp[1];
+ close (LOCAL->fd); /* close and reopen file */
+ if ((LOCAL->fd = open (stream->mailbox,O_RDWR,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL)))
+ < 0) {
+ sprintf (LOCAL->buf,"Mailbox open failed, aborted: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ mmdf_abort (stream);
+ }
+ dotlock_unlock (lock); /* flush the lock file */
+ }
+ return ret; /* return state from algorithm */
+}
+
+/* Extend MMDF mailbox file
+ * Accepts: MAIL stream
+ * new desired size
+ * Return: T if success, else NIL
+ */
+
+long mmdf_extend (MAILSTREAM *stream,unsigned long size)
+{
+ unsigned long i = (size > LOCAL->filesize) ? size - LOCAL->filesize : 0;
+ if (i) { /* does the mailbox need to grow? */
+ if (i > LOCAL->buflen) { /* make sure have enough space */
+ /* this user won the lottery all right */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1);
+ }
+ memset (LOCAL->buf,'\0',i); /* get a block of nulls */
+ while (T) { /* until write successful or punt */
+ lseek (LOCAL->fd,LOCAL->filesize,L_SET);
+ if ((write (LOCAL->fd,LOCAL->buf,i) >= 0) && !fsync (LOCAL->fd)) break;
+ else {
+ long e = errno; /* note error before doing ftruncate */
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ if (MM_DISKERROR (stream,e,NIL)) {
+ fsync (LOCAL->fd); /* user chose to punt */
+ sprintf (LOCAL->buf,"Unable to extend mailbox: %s",strerror (e));
+ if (!stream->silent) MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ }
+ }
+ }
+ return LONGT;
+}
+
+/* Write data to buffered file
+ * Accepts: buffered file pointer
+ * file data or NIL to indicate "flush buffer"
+ * date size (ignored for "flush buffer")
+ * Does not return until success
+ */
+
+void mmdf_write (MMDFFILE *f,char *buf,unsigned long size)
+{
+ unsigned long i,j,k;
+ if (buf) { /* doing buffered write? */
+ i = f->bufpos - f->buf; /* yes, get size of current buffer data */
+ /* yes, have space in current buffer chunk? */
+ if (j = i ? ((f->buflen - i) % OVERFLOWBUFLEN) : f->buflen) {
+ /* yes, fill up buffer as much as we can */
+ memcpy (f->bufpos,buf,k = min (j,size));
+ f->bufpos += k; /* new buffer position */
+ f->curpos += k; /* new current position */
+ if (j -= k) return; /* all done if still have buffer free space */
+ buf += k; /* full, get new unwritten data pointer */
+ size -= k; /* new data size */
+ i += k; /* new buffer data size */
+ }
+ /* This chunk of the buffer is full. See if can make some space by
+ * writing to the disk, if there's enough unprotected space to do so.
+ * Try to fill out any unaligned chunk, along with any subsequent full
+ * chunks that will fit in unprotected space.
+ */
+ /* any unprotected space we can write to? */
+ if (j = min (i,f->protect - f->filepos)) {
+ /* yes, filepos not at chunk boundary? */
+ if ((k = f->filepos % OVERFLOWBUFLEN) && ((k = OVERFLOWBUFLEN - k) < j))
+ j -= k; /* yes, and can write out partial chunk */
+ else k = 0; /* no partial chunk to write */
+ /* if at least a chunk free, write that too */
+ if (j > OVERFLOWBUFLEN) k += j - (j % OVERFLOWBUFLEN);
+ if (k) { /* write data if there is anything we can */
+ mmdf_phys_write (f,f->buf,k);
+ /* slide buffer */
+ if (i -= k) memmove (f->buf,f->buf + k,i);
+ f->bufpos = f->buf + i; /* new end of buffer */
+ }
+ }
+
+ /* Have flushed the buffer as best as possible. All done if no more
+ * data to write. Otherwise, if the buffer is empty AND if the unwritten
+ * data is larger than a chunk AND the unprotected space is also larger
+ * than a chunk, then write as many chunks as we can directly from the
+ * data. Buffer the rest, expanding the buffer as needed.
+ */
+ if (size) { /* have more data that we need to buffer? */
+ /* can write any of it to disk instead? */
+ if ((f->bufpos == f->buf) &&
+ ((j = min (f->protect - f->filepos,size)) > OVERFLOWBUFLEN)) {
+ /* write as much as we can right now */
+ mmdf_phys_write (f,buf,j -= (j % OVERFLOWBUFLEN));
+ buf += j; /* new data pointer */
+ size -= j; /* new data size */
+ f->curpos += j; /* advance current pointer */
+ }
+ if (size) { /* still have data that we need to buffer? */
+ /* yes, need to expand the buffer? */
+ if ((i = ((f->bufpos + size) - f->buf)) > f->buflen) {
+ /* note current position in buffer */
+ j = f->bufpos - f->buf;
+ i += OVERFLOWBUFLEN; /* yes, grow another chunk */
+ fs_resize ((void **) &f->buf,f->buflen = i - (i % OVERFLOWBUFLEN));
+ /* in case buffer relocated */
+ f->bufpos = f->buf + j;
+ }
+ /* buffer remaining data */
+ memcpy (f->bufpos,buf,size);
+ f->bufpos += size; /* new end of buffer */
+ f->curpos += size; /* advance current pointer */
+ }
+ }
+ }
+ else { /* flush buffer to disk */
+ mmdf_phys_write (f,f->buf,i = f->bufpos - f->buf);
+ f->bufpos = f->buf; /* reset buffer */
+ /* update positions */
+ f->curpos = f->protect = f->filepos;
+ }
+}
+
+/* Physical disk write
+ * Accepts: buffered file pointer
+ * buffer address
+ * buffer size
+ * Does not return until success
+ */
+
+void mmdf_phys_write (MMDFFILE *f,char *buf,size_t size)
+{
+ MAILSTREAM *stream = f->stream;
+ /* write data at desired position */
+ while (size && ((lseek (LOCAL->fd,f->filepos,L_SET) < 0) ||
+ (write (LOCAL->fd,buf,size) < 0))) {
+ int e;
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Unable to write to mailbox: %s",strerror (e = errno));
+ MM_LOG (tmp,ERROR);
+ MM_DISKERROR (NIL,e,T); /* serious problem, must retry */
+ }
+ f->filepos += size; /* update file position */
+}
diff --git a/imap/src/osdep/unix/mtx.c b/imap/src/osdep/unix/mtx.c
new file mode 100644
index 00000000..8e6f76e8
--- /dev/null
+++ b/imap/src/osdep/unix/mtx.c
@@ -0,0 +1,1371 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MTX mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 22 May 1990
+ * Last Edited: 11 October 2007
+ */
+
+
+/* FILE TIME SEMANTICS
+ *
+ * The atime is the last read time of the file.
+ * The mtime is the last flags update time of the file.
+ * The ctime is the last write time of the file.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "misc.h"
+#include "dummy.h"
+#include "fdstring.h"
+
+/* MTX I/O stream local data */
+
+typedef struct mtx_local {
+ unsigned int shouldcheck: 1; /* if ping should do a check instead */
+ unsigned int mustcheck: 1; /* if ping must do a check instead */
+ int fd; /* file descriptor for I/O */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ time_t lastsnarf; /* last snarf time */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+} MTXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MTXLOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *mtx_valid (char *name);
+int mtx_isvalid (char *name,char *tmp);
+void *mtx_parameters (long function,void *value);
+void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void mtx_list (MAILSTREAM *stream,char *ref,char *pat);
+void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long mtx_create (MAILSTREAM *stream,char *mailbox);
+long mtx_delete (MAILSTREAM *stream,char *mailbox);
+long mtx_rename (MAILSTREAM *stream,char *old,char *newname);
+long mtx_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *mtx_open (MAILSTREAM *stream);
+void mtx_close (MAILSTREAM *stream,long options);
+void mtx_flags (MAILSTREAM *stream,char *sequence,long flags);
+char *mtx_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void mtx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long mtx_ping (MAILSTREAM *stream);
+void mtx_check (MAILSTREAM *stream);
+void mtx_snarf (MAILSTREAM *stream);
+long mtx_expunge (MAILSTREAM *stream,char *sequence,long options);
+long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+char *mtx_file (char *dst,char *name);
+long mtx_parse (MAILSTREAM *stream);
+MESSAGECACHE *mtx_elt (MAILSTREAM *stream,unsigned long msgno);
+void mtx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt);
+void mtx_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag);
+unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size);
+
+/* MTX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mtxdriver = {
+ "mtx", /* driver name */
+ /* driver flags */
+ DR_LOCAL|DR_MAIL|DR_CRLF|DR_NOSTICKY|DR_LOCKING,
+ (DRIVER *) NIL, /* next driver */
+ mtx_valid, /* mailbox is valid for us */
+ mtx_parameters, /* manipulate parameters */
+ mtx_scan, /* scan mailboxes */
+ mtx_list, /* list mailboxes */
+ mtx_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ dummy_create, /* create mailbox */
+ mtx_delete, /* delete mailbox */
+ mtx_rename, /* rename mailbox */
+ mtx_status, /* status of mailbox */
+ mtx_open, /* open mailbox */
+ mtx_close, /* close mailbox */
+ mtx_flags, /* fetch message "fast" attributes */
+ mtx_flags, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mtx_header, /* fetch message header */
+ mtx_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ mtx_flag, /* modify flags */
+ mtx_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mtx_ping, /* ping mailbox to see if still alive */
+ mtx_check, /* check for new messages */
+ mtx_expunge, /* expunge deleted messages */
+ mtx_copy, /* copy messages to another mailbox */
+ mtx_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mtxproto = {&mtxdriver};
+
+/* MTX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mtx_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return mtx_isvalid (name,tmp) ? &mtxdriver : NIL;
+}
+
+
+/* MTX mail test for valid mailbox
+ * Accepts: mailbox name
+ * Returns: T if valid, NIL otherwise
+ */
+
+int mtx_isvalid (char *name,char *tmp)
+{
+ int fd;
+ int ret = NIL;
+ char *s,file[MAILTMPLEN];
+ struct stat sbuf;
+ time_t tp[2];
+ errno = EINVAL; /* assume invalid argument */
+ /* if file, get its status */
+ if ((s = mtx_file (file,name)) && !stat (s,&sbuf)) {
+ if (!sbuf.st_size) { /* allow empty file if INBOX */
+ if ((s = mailboxfile (tmp,name)) && !*s) ret = T;
+ else errno = 0; /* empty file */
+ }
+ else if ((fd = open (file,O_RDONLY,NIL)) >= 0) {
+ memset (tmp,'\0',MAILTMPLEN);
+ if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\015')) &&
+ (s[1] == '\012')) { /* valid format? */
+ *s = '\0'; /* tie off header */
+ /* must begin with dd-mmm-yy" */
+ ret = (((tmp[2] == '-' && tmp[6] == '-') ||
+ (tmp[1] == '-' && tmp[5] == '-')) &&
+ (s = strchr (tmp+18,',')) && strchr (s+2,';')) ? T : NIL;
+ }
+ else errno = -1; /* bogus format */
+ close (fd); /* close the file */
+ /* \Marked status? */
+ if (sbuf.st_ctime > sbuf.st_atime) {
+ tp[0] = sbuf.st_atime; /* preserve atime and mtime */
+ tp[1] = sbuf.st_mtime;
+ utime (file,tp); /* set the times */
+ }
+ }
+ }
+ /* in case INBOX but not mtx format */
+ else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1;
+ return ret; /* return what we should */
+}
+
+/* MTX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mtx_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = mtx_file ((char *) value,"INBOX");
+ break;
+ }
+ return ret;
+}
+
+
+/* MTX mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* MTX mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mtx_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* MTX mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* MTX mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mtx_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return mtx_rename (stream,mailbox,NIL);
+}
+
+
+/* MTX mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long mtx_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = T;
+ char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ int fd,ld;
+ struct stat sbuf;
+ if (!mtx_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'/')) && !s[1])))) {
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ else if ((fd = open (file,O_RDWR,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get exclusive parse/append permission */
+ if ((ld = lockfd (fd,lock,LOCK_EX)) < 0) {
+ MM_LOG ("Unable to lock rename mailbox",ERROR);
+ return NIL;
+ }
+ /* lock out other users */
+ if (flock (fd,LOCK_EX|LOCK_NB)) {
+ close (fd); /* couldn't lock, give up on it then */
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+ MM_LOG (tmp,ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return NIL;
+ }
+
+ if (newname) { /* want rename? */
+ if (s = strrchr (tmp,'/')) {/* found superior to destination name? */
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,tmp,get_dir_protection (newname)))
+ ret = NIL;
+ else *s = c; /* restore full name */
+ }
+ /* rename the file */
+ if (ret && rename (file,tmp)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ else if (unlink (file)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* close the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ /* recreate file if renamed INBOX */
+ if (ret && !compare_cstring (old,"INBOX")) dummy_create (NIL,"INBOX.MTX");
+ return ret; /* return success */
+}
+
+/* Mtx Mail status
+ * Accepts: mail stream
+ * mailbox name
+ * status flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mtx_status (MAILSTREAM *stream,char *mbx,long flags)
+{
+ MAILSTATUS status;
+ unsigned long i;
+ MAILSTREAM *tstream = NIL;
+ MAILSTREAM *systream = NIL;
+ /* make temporary stream (unless this mbx) */
+ if (!stream && !(stream = tstream =
+ mail_open (NIL,mbx,OP_READONLY|OP_SILENT))) return NIL;
+ status.flags = flags; /* return status values */
+ status.messages = stream->nmsgs;
+ status.recent = stream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++)
+ if (!mail_elt (stream,i)->seen) status.unseen++;
+ status.uidnext = stream->uid_last + 1;
+ status.uidvalidity = stream->uid_validity;
+ /* calculate post-snarf results */
+ if (!status.recent && stream->inbox &&
+ (systream = mail_open (NIL,sysinbox (),OP_READONLY|OP_SILENT))) {
+ status.messages += systream->nmsgs;
+ status.recent += systream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1; i <= systream->nmsgs; i++)
+ if (!mail_elt (systream,i)->seen) status.unseen++;
+ /* kludge but probably good enough */
+ status.uidnext += systream->nmsgs;
+ }
+ MM_STATUS(stream,mbx,&status);/* pass status to main program */
+ if (tstream) mail_close (tstream);
+ if (systream) mail_close (systream);
+ return T; /* success */
+}
+
+/* MTX mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mtx_open (MAILSTREAM *stream)
+{
+ int fd,ld;
+ char tmp[MAILTMPLEN];
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return user_flags (&mtxproto);
+ if (stream->local) fatal ("mtx recycle stream");
+ user_flags (stream); /* set up user flags */
+ /* canonicalize the mailbox name */
+ if (!mtx_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ MM_LOG (tmp,ERROR);
+ }
+ if (stream->rdonly ||
+ (fd = open (tmp,O_RDWR,NIL)) < 0) {
+ if ((fd = open (tmp,O_RDONLY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ else if (!stream->rdonly) { /* got it, but readonly */
+ MM_LOG ("Can't get write access to mailbox, access is readonly",WARN);
+ stream->rdonly = T;
+ }
+ }
+ stream->local = fs_get (sizeof (MTXLOCAL));
+ LOCAL->fd = fd; /* bind the file */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ /* get shared parse permission */
+ if ((ld = lockfd (fd,tmp,LOCK_SH)) < 0) {
+ MM_LOG ("Unable to lock open mailbox",ERROR);
+ return NIL;
+ }
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH); /* lock the file */
+ (*bn) (BLOCK_NONE,NIL);
+ unlockfd (ld,tmp); /* release shared parse permission */
+ LOCAL->filesize = 0; /* initialize parsed file size */
+ /* time not set up yet */
+ LOCAL->lastsnarf = LOCAL->filetime = 0;
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ stream->sequence++; /* bump sequence number */
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ if (mtx_ping (stream) && !stream->nmsgs)
+ MM_LOG ("Mailbox is empty",(long) NIL);
+ if (!LOCAL) return NIL; /* failure if stream died */
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft =
+ stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ return stream; /* return stream to caller */
+}
+
+/* MTX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mtx_close (MAILSTREAM *stream,long options)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ if (options & CL_EXPUNGE) mtx_expunge (stream,NIL,NIL);
+ stream->silent = silent; /* restore previous status */
+ flock (LOCAL->fd,LOCK_UN); /* unlock local file */
+ close (LOCAL->fd); /* close the local file */
+ /* free local text buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+
+/* MTX mail fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ * Sniffs at file to see if some other process changed the flags
+ */
+
+void mtx_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ unsigned long i;
+ if (mtx_ping (stream) && /* ping mailbox, get new status for messages */
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if (mail_elt (stream,i)->sequence) mtx_elt (stream,i);
+}
+
+/* MTX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *mtx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags)
+{
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ /* get to header position */
+ lseek (LOCAL->fd,mtx_hdrpos (stream,msgno,length),L_SET);
+ /* is buffer big enough? */
+ if (*length > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = *length) + 1);
+ }
+ LOCAL->buf[*length] = '\0'; /* tie off string */
+ /* slurp the data */
+ read (LOCAL->fd,LOCAL->buf,*length);
+ return (char *) LOCAL->buf;
+}
+
+/* MTX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: T, always
+ */
+
+long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ FDDATA d;
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mtx_elt (stream,msgno); /* get message status */
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ elt->seen = T; /* mark message as seen */
+ /* recalculate status */
+ mtx_update_status (stream,msgno,NIL);
+ MM_FLAGS (stream,msgno);
+ }
+ /* find header position */
+ i = mtx_hdrpos (stream,msgno,&j);
+ d.fd = LOCAL->fd; /* set up file descriptor */
+ d.pos = i + j;
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE;
+ INIT (bs,fd_string,&d,elt->rfc822_size - j);
+ return T; /* success */
+}
+
+/* MTX mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ */
+
+void mtx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ time_t tp[2];
+ struct stat sbuf;
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* make sure read comes after all that */
+ utime (stream->mailbox,tp);
+ }
+}
+
+
+/* MTX mail per-message modify flags
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ struct stat sbuf;
+ /* maybe need to do a checkpoint? */
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ LOCAL->filetime = 0; /* don't do this test for any other messages */
+ }
+ /* recalculate status */
+ mtx_update_status (stream,elt->msgno,NIL);
+}
+
+/* MTX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream still alive, NIL if not
+ */
+
+long mtx_ping (MAILSTREAM *stream)
+{
+ unsigned long i = 1;
+ long r = T;
+ int ld;
+ char lock[MAILTMPLEN];
+ struct stat sbuf;
+ if (stream && LOCAL) { /* only if stream already open */
+ fstat (LOCAL->fd,&sbuf); /* get current file poop */
+ if (LOCAL->filetime && !(LOCAL->mustcheck || LOCAL->shouldcheck) &&
+ (LOCAL->filetime < sbuf.st_mtime)) LOCAL->shouldcheck = T;
+ /* check for changed message status */
+ if (LOCAL->mustcheck || LOCAL->shouldcheck) {
+ LOCAL->filetime = sbuf.st_mtime;
+ if (LOCAL->shouldcheck) /* babble when we do this unilaterally */
+ MM_NOTIFY (stream,"[CHECK] Checking for flag updates",NIL);
+ while (i <= stream->nmsgs) mtx_elt (stream,i++);
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ }
+ /* get shared parse/append permission */
+ if ((sbuf.st_size != LOCAL->filesize) &&
+ ((ld = lockfd (LOCAL->fd,lock,LOCK_SH)) >= 0)) {
+ /* parse resulting mailbox */
+ r = (mtx_parse (stream)) ? T : NIL;
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ if (LOCAL) { /* stream must still be alive */
+ /* snarf if this is a read-write inbox */
+ if (stream->inbox && !stream->rdonly) {
+ mtx_snarf (stream);
+ fstat (LOCAL->fd,&sbuf);/* see if file changed now */
+ if ((sbuf.st_size != LOCAL->filesize) &&
+ ((ld = lockfd (LOCAL->fd,lock,LOCK_SH)) >= 0)) {
+ /* parse resulting mailbox */
+ r = (mtx_parse (stream)) ? T : NIL;
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ }
+ }
+ }
+ return r; /* return result of the parse */
+}
+
+
+/* MTX mail check mailbox (reparses status too)
+ * Accepts: MAIL stream
+ */
+
+void mtx_check (MAILSTREAM *stream)
+{
+ /* mark that a check is desired */
+ if (LOCAL) LOCAL->mustcheck = T;
+ if (mtx_ping (stream)) MM_LOG ("Check completed",(long) NIL);
+}
+
+/* MTX mail snarf messages from system inbox
+ * Accepts: MAIL stream
+ */
+
+void mtx_snarf (MAILSTREAM *stream)
+{
+ unsigned long i = 0;
+ unsigned long j,r,hdrlen,txtlen;
+ struct stat sbuf;
+ char *hdr,*txt,lock[MAILTMPLEN],tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ MAILSTREAM *sysibx = NIL;
+ int ld;
+ /* give up if can't get exclusive permission */
+ if ((time (0) >= (LOCAL->lastsnarf +
+ (long) mail_parameters (NIL,GET_SNARFINTERVAL,NIL))) &&
+ strcmp (sysinbox (),stream->mailbox) &&
+ ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) >= 0)) {
+ MM_CRITICAL (stream); /* go critical */
+ /* sizes match and anything in sysinbox? */
+ if (!stat (sysinbox (),&sbuf) && sbuf.st_size &&
+ !fstat (LOCAL->fd,&sbuf) && (sbuf.st_size == LOCAL->filesize) &&
+ (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) &&
+ (!sysibx->rdonly) && (r = sysibx->nmsgs)) {
+ /* yes, go to end of file in our mailbox */
+ lseek (LOCAL->fd,sbuf.st_size,L_SET);
+ /* for each message in sysibx mailbox */
+ while (r && (++i <= sysibx->nmsgs)) {
+ /* snarf message from system INBOX */
+ hdr = cpystr (mail_fetchheader_full (sysibx,i,NIL,&hdrlen,NIL));
+ txt = mail_fetchtext_full (sysibx,i,&txtlen,FT_PEEK);
+ /* if have a message */
+ if (j = hdrlen + txtlen) {
+ /* calculate header line */
+ mail_date (LOCAL->buf,elt = mail_elt (sysibx,i));
+ sprintf (LOCAL->buf + strlen (LOCAL->buf),
+ ",%lu;0000000000%02o\015\012",j,(unsigned)
+ ((fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)));
+ /* copy message */
+ if ((write (LOCAL->fd,LOCAL->buf,strlen (LOCAL->buf)) < 0) ||
+ (write (LOCAL->fd,hdr,hdrlen) < 0) ||
+ (write (LOCAL->fd,txt,txtlen) < 0)) r = 0;
+ }
+ fs_give ((void **) &hdr);
+ }
+
+ /* make sure all the updates take */
+ if (fsync (LOCAL->fd)) r = 0;
+ if (r) { /* delete all the messages we copied */
+ if (r == 1) strcpy (tmp,"1");
+ else sprintf (tmp,"1:%lu",r);
+ mail_flag (sysibx,tmp,"\\Deleted",ST_SET);
+ mail_expunge (sysibx); /* now expunge all those messages */
+ }
+ else {
+ sprintf (LOCAL->buf,"Can't copy new mail: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,WARN);
+ ftruncate (LOCAL->fd,sbuf.st_size);
+ }
+ fstat (LOCAL->fd,&sbuf); /* yes, get current file size */
+ LOCAL->filetime = sbuf.st_mtime;
+ }
+ if (sysibx) mail_close (sysibx);
+ MM_NOCRITICAL (stream); /* release critical */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ LOCAL->lastsnarf = time (0);/* note time of last snarf */
+ }
+}
+
+/* MTX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long mtx_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ time_t tp[2];
+ struct stat sbuf;
+ off_t pos = 0;
+ int ld;
+ unsigned long i = 1;
+ unsigned long j,k,m,recent;
+ unsigned long n = 0;
+ unsigned long delta = 0;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (!(ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ mtx_ping (stream))); /* parse sequence if given, ping stream */
+ else if (stream->rdonly) MM_LOG ("Expunge ignored on readonly mailbox",WARN);
+ else {
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ }
+ /* The cretins who designed flock() created a window of vulnerability in
+ * upgrading locks from shared to exclusive or downgrading from exclusive
+ * to shared. Rather than maintain the lock at shared status at a minimum,
+ * flock() actually *releases* the former lock. Obviously they never talked
+ * to any database guys. Fortunately, we have the parse/append permission
+ * lock. If we require this lock before going exclusive on the mailbox,
+ * another process can not sneak in and steal the exclusive mailbox lock on
+ * us, because it will block on trying to get parse/append permission first.
+ */
+ /* get exclusive parse/append permission */
+ if ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) < 0)
+ MM_LOG ("Unable to lock expunge mailbox",ERROR);
+ /* make sure see any newly-arrived messages */
+ else if (!mtx_parse (stream));
+ /* get exclusive access */
+ else if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH);/* recover previous lock */
+ (*bn) (BLOCK_NONE,NIL);
+ MM_LOG ("Can't expunge because mailbox is in use by another process",
+ ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+
+ else {
+ MM_CRITICAL (stream); /* go critical */
+ recent = stream->recent; /* get recent now that pinged and locked */
+ /* for each message */
+ while (i <= stream->nmsgs) {
+ /* get cache element */
+ elt = mtx_elt (stream,i);
+ /* number of bytes to smash or preserve */
+ k = elt->private.special.text.size + elt->rfc822_size;
+ /* if need to expunge this message */
+ if (elt->deleted && (sequence ? elt->sequence : T)) {
+ /* if recent, note one less recent message */
+ if (elt->recent) --recent;
+ delta += k; /* number of bytes to delete */
+ /* notify upper levels */
+ mail_expunged (stream,i);
+ n++; /* count up one more expunged message */
+ }
+ else if (i++ && delta) {/* preserved message */
+ /* first byte to preserve */
+ j = elt->private.special.offset;
+ do { /* read from source position */
+ m = min (k,LOCAL->buflen);
+ lseek (LOCAL->fd,j,L_SET);
+ read (LOCAL->fd,LOCAL->buf,m);
+ pos = j - delta; /* write to destination position */
+ lseek (LOCAL->fd,pos,L_SET);
+ while (T) {
+ lseek (LOCAL->fd,pos,L_SET);
+ if (write (LOCAL->fd,LOCAL->buf,m) > 0) break;
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+ pos += m; /* new position */
+ j += m; /* next chunk, perhaps */
+ } while (k -= m); /* until done */
+ /* note the new address of this text */
+ elt->private.special.offset -= delta;
+ }
+ /* preserved but no deleted messages */
+ else pos = elt->private.special.offset + k;
+ }
+ if (n) { /* truncate file after last message */
+ if (pos != (LOCAL->filesize -= delta)) {
+ sprintf (LOCAL->buf,
+ "Calculated size mismatch %lu != %lu, delta = %lu",
+ (unsigned long) pos,(unsigned long) LOCAL->filesize,delta);
+ MM_LOG (LOCAL->buf,WARN);
+ LOCAL->filesize = pos;/* fix it then */
+ }
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ sprintf (LOCAL->buf,"Expunged %lu messages",n);
+ /* output the news */
+ MM_LOG (LOCAL->buf,(long) NIL);
+ }
+ else MM_LOG ("No messages deleted, so no update needed",(long) NIL);
+ fsync (LOCAL->fd); /* force disk update */
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* reset atime to now */
+ utime (stream->mailbox,tp);
+ MM_NOCRITICAL (stream); /* release critical */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH);/* allow sharers again */
+ (*bn) (BLOCK_NONE,NIL);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+ }
+ return ret;
+}
+
+/* MTX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if success, NIL if failed
+ */
+
+long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ time_t tp[2];
+ MESSAGECACHE *elt;
+ unsigned long i,j,k;
+ long ret = LONGT;
+ int fd,ld;
+ char file[MAILTMPLEN],lock[MAILTMPLEN];
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ /* make sure valid mailbox */
+ if (!mtx_isvalid (mailbox,LOCAL->buf)) switch (errno) {
+ case ENOENT: /* no such file? */
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid MTX-format mailbox name: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a MTX-format mailbox: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* got file? */
+ if ((fd = open (mtx_file (file,mailbox),O_RDWR,NIL)) < 0) {
+ sprintf (LOCAL->buf,"Unable to open copy mailbox: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ MM_CRITICAL (stream); /* go critical */
+ /* get exclusive parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockfd (fd,lock,LOCK_EX)) < 0)) {
+ MM_LOG ("Unable to lock copy mailbox",ERROR);
+ MM_NOCRITICAL (stream);
+ return NIL;
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
+
+ /* for each requested message */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ /* number of bytes to copy */
+ k = elt->private.special.text.size + elt->rfc822_size;
+ do { /* read from source position */
+ j = min (k,LOCAL->buflen);
+ read (LOCAL->fd,LOCAL->buf,j);
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ } while (ret && (k -= j));/* until done */
+ }
+ /* make sure all the updates take */
+ if (!(ret && (ret = !fsync (fd)))) {
+ sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ }
+ if (ret) tp[0] = time (0) - 1;/* set atime to now-1 if successful copy */
+ /* else preserve \Marked status */
+ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0);
+ tp[1] = sbuf.st_mtime; /* preserve mtime */
+ utime (file,tp); /* set the times */
+ close (fd); /* close the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ MM_NOCRITICAL (stream); /* release critical */
+ /* delete all requested messages */
+ if (ret && (options & CP_MOVE)) {
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mtx_elt (stream,i))->sequence) {
+ elt->deleted = T; /* mark message deleted */
+ /* recalculate status */
+ mtx_update_status (stream,i,NIL);
+ }
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* make sure atime remains greater */
+ utime (stream->mailbox,tp);
+ }
+ }
+ if (ret && mail_parameters (NIL,GET_COPYUID,NIL))
+ MM_LOG ("Can not return meaningful COPYUID with this mailbox format",WARN);
+ return ret;
+}
+
+/* MTX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd,ld,c;
+ char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ time_t tp[2];
+ FILE *df;
+ MESSAGECACHE elt;
+ long f;
+ unsigned long i,uf;
+ STRING *message;
+ long ret = LONGT;
+ /* default stream to prototype */
+ if (!stream) stream = user_flags (&mtxproto);
+ /* make sure valid mailbox */
+ if (!mtx_isvalid (mailbox,tmp)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (!compare_cstring (mailbox,"INBOX")) dummy_create (NIL,"INBOX.MTX");
+ else {
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* falls through */
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid MTX-format mailbox name: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a MTX-format mailbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get first message */
+ if (!MM_APPEND (af) (stream,data,&flags,&date,&message)) return NIL;
+
+ /* open destination mailbox */
+ if (((fd = open (mtx_file (file,mailbox),O_WRONLY|O_APPEND,NIL)) < 0) ||
+ !(df = fdopen (fd,"ab"))) {
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockfd (fd,lock,LOCK_EX)) < 0)) {
+ MM_LOG ("Unable to lock append mailbox",ERROR);
+ close (fd);
+ return NIL;
+ }
+ MM_CRITICAL (stream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ errno = 0;
+ do { /* parse flags */
+ if (!SIZE (message)) { /* guard against zero-length */
+ MM_LOG ("Append of zero-length message",ERROR);
+ ret = NIL;
+ break;
+ }
+ f = mail_parse_flags (stream,flags,&i);
+ /* reverse bits (dontcha wish we had CIRC?) */
+ for (uf = 0; i; uf |= 1 << (29 - find_rightmost_bit (&i)));
+ if (date) { /* parse date if given */
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* mark failure */
+ break;
+ }
+ mail_date (tmp,&elt); /* write preseved date */
+ }
+ else internal_date (tmp); /* get current date in IMAP format */
+ /* write header */
+ if (fprintf (df,"%s,%lu;%010lo%02lo\015\012",tmp,i = SIZE (message),uf,
+ (unsigned long) f) < 0) ret = NIL;
+ else { /* write message */
+ if (i) do c = 0xff & SNX (message);
+ while ((putc (c,df) != EOF) && --i);
+ /* get next message */
+ if (i || !MM_APPEND (af) (stream,data,&flags,&date,&message)) ret = NIL;
+ }
+ } while (ret && message);
+ /* if error... */
+ if (!ret || (fflush (df) == EOF)) {
+ ftruncate (fd,sbuf.st_size);/* revert file */
+ close (fd); /* make sure fclose() doesn't corrupt us */
+ if (errno) {
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ }
+ ret = NIL;
+ }
+ if (ret) tp[0] = time (0) - 1;/* set atime to now-1 if successful copy */
+ /* else preserve \Marked status */
+ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0);
+ tp[1] = sbuf.st_mtime; /* preserve mtime */
+ utime (file,tp); /* set the times */
+ fclose (df); /* close the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ MM_NOCRITICAL (stream); /* release critical */
+ if (ret && mail_parameters (NIL,GET_APPENDUID,NIL))
+ MM_LOG ("Can not return meaningful APPENDUID with this mailbox format",
+ WARN);
+ return ret;
+}
+
+/* Internal routines */
+
+
+/* MTX mail generate file string
+ * Accepts: temporary buffer to write into
+ * mailbox name string
+ * Returns: local file string or NIL if failure
+ */
+
+char *mtx_file (char *dst,char *name)
+{
+ char tmp[MAILTMPLEN];
+ char *s = mailboxfile (dst,name);
+ /* return our standard inbox */
+ return (s && !*s) ? mailboxfile (dst,mtx_isvalid ("~/INBOX",tmp) ?
+ "~/INBOX" : "INBOX.MTX") : s;
+}
+
+/* MTX mail parse mailbox
+ * Accepts: MAIL stream
+ * Returns: T if parse OK
+ * NIL if failure, stream aborted
+ */
+
+long mtx_parse (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = NIL;
+ unsigned char c,*s,*t,*x;
+ char tmp[MAILTMPLEN];
+ unsigned long i,j;
+ long curpos = LOCAL->filesize;
+ long nmsgs = stream->nmsgs;
+ long recent = stream->recent;
+ short added = NIL;
+ short silent = stream->silent;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ if (sbuf.st_size < curpos) { /* sanity check */
+ sprintf (tmp,"Mailbox shrank from %lu to %lu!",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size);
+ MM_LOG (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ stream->silent = T; /* don't pass up exists events yet */
+ while (sbuf.st_size - curpos){/* while there is stuff to parse */
+ /* get to that position in the file */
+ lseek (LOCAL->fd,curpos,L_SET);
+ if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
+ sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size,
+ i ? strerror (errno) : "no data read");
+ MM_LOG (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ LOCAL->buf[i] = '\0'; /* tie off buffer just in case */
+ if (!((s = strchr (LOCAL->buf,'\015')) && (s[1] == '\012'))) {
+ sprintf (tmp,"Unable to find CRLF at %lu in %lu bytes, text: %s",
+ (unsigned long) curpos,i,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ *s = '\0'; /* tie off header line */
+ i = (s + 2) - LOCAL->buf; /* note start of text offset */
+ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
+ sprintf (tmp,"Unable to parse internal header at %lu: %s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ *s++ = '\0'; *t++ = '\0'; /* tie off fields */
+
+ added = T; /* note that a new message was added */
+ /* swell the cache */
+ mail_exists (stream,++nmsgs);
+ /* instantiate an elt for this message */
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ elt->private.uid = ++stream->uid_last;
+ /* note file offset of header */
+ elt->private.special.offset = curpos;
+ /* in case error */
+ elt->private.special.text.size = 0;
+ /* header size not known yet */
+ elt->private.msg.header.text.size = 0;
+ x = s; /* parse the header components */
+ if (mail_parse_date (elt,LOCAL->buf) &&
+ (elt->rfc822_size = strtoul (s,(char **) &s,10)) && (!(s && *s)) &&
+ isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) &&
+ isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) &&
+ isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) &&
+ isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12])
+ elt->private.special.text.size = i;
+ else { /* oops */
+ sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s",
+ curpos,(char *) LOCAL->buf,(char *) x,(char *) t);
+ MM_LOG (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ /* make sure didn't run off end of file */
+ if ((curpos += (elt->rfc822_size + i)) > sbuf.st_size) {
+ sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)",
+ elt->private.special.offset,(unsigned long) curpos,
+ (unsigned long) sbuf.st_size);
+ MM_LOG (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ c = t[10]; /* remember first system flags byte */
+ t[10] = '\0'; /* tie off flags */
+ j = strtoul (t,NIL,8); /* get user flags value */
+ t[10] = c; /* restore first system flags byte */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ /* calculate system flags */
+ if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T;
+ if (j & fDELETED) elt->deleted = T;
+ if (j & fFLAGGED) elt->flagged = T;
+ if (j & fANSWERED) elt->answered = T;
+ if (j & fDRAFT) elt->draft = T;
+ if (!(j & fOLD)) { /* newly arrived message? */
+ elt->recent = T;
+ recent++; /* count up a new recent message */
+ /* mark it as old */
+ mtx_update_status (stream,nmsgs,NIL);
+ }
+ }
+ fsync (LOCAL->fd); /* make sure all the fOLD flags take */
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */
+ LOCAL->filetime = sbuf.st_mtime;
+ if (added && !stream->rdonly){/* make sure atime updated */
+ time_t tp[2];
+ tp[0] = time (0);
+ tp[1] = LOCAL->filetime;
+ utime (stream->mailbox,tp);
+ }
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
+ mail_recent (stream,recent); /* and of change in recent messages */
+ return LONGT; /* return the winnage */
+}
+
+/* MTX get cache element with status updating from file
+ * Accepts: MAIL stream
+ * message number
+ * Returns: cache element
+ */
+
+MESSAGECACHE *mtx_elt (MAILSTREAM *stream,unsigned long msgno)
+{
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ struct { /* old flags */
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged;
+ old.answered = elt->answered; old.draft = elt->draft;
+ old.user_flags = elt->user_flags;
+ mtx_read_flags (stream,elt);
+ if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
+ (old.flagged != elt->flagged) || (old.answered != elt->answered) ||
+ (old.draft != elt->draft) || (old.user_flags != elt->user_flags))
+ MM_FLAGS (stream,msgno); /* let top level know */
+ return elt;
+}
+
+/* MTX read flags from file
+ * Accepts: MAIL stream
+ * Returns: cache element
+ */
+
+void mtx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ unsigned long i,j;
+ /* noop if readonly and have valid flags */
+ if (stream->rdonly && elt->valid) return;
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 14,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,12) < 0) {
+ sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ /* calculate system flags */
+ i = (((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0');
+ elt->seen = i & fSEEN ? T : NIL; elt->deleted = i & fDELETED ? T : NIL;
+ elt->flagged = i & fFLAGGED ? T : NIL;
+ elt->answered = i & fANSWERED ? T : NIL; elt->draft = i & fDRAFT ? T : NIL;
+ LOCAL->buf[10] = '\0'; /* tie off flags */
+ j = strtoul(LOCAL->buf,NIL,8);/* get user flags value */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ elt->valid = T; /* have valid flags now */
+}
+
+/* MTX update status string
+ * Accepts: MAIL stream
+ * message number
+ * flag saying whether or not to sync
+ */
+
+void mtx_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag)
+{
+ time_t tp[2];
+ struct stat sbuf;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ unsigned long j,k = 0;
+ /* readonly */
+ if (stream->rdonly || !elt->valid) mtx_read_flags (stream,elt);
+ else { /* readwrite */
+ j = elt->user_flags; /* get user flags */
+ /* reverse bits (dontcha wish we had CIRC?) */
+ while (j) k |= 1 << (29 - find_rightmost_bit (&j));
+ /* print new flag string */
+ sprintf (LOCAL->buf,"%010lo%02o",k,(unsigned)
+ (fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)));
+ /* get to that place in the file */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 14,L_SET);
+ /* write new flags */
+ write (LOCAL->fd,LOCAL->buf,12);
+ if (syncflag) { /* sync if requested */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* make sure read is later */
+ utime (stream->mailbox,tp);
+ }
+ }
+}
+
+/* MTX locate header for a message
+ * Accepts: MAIL stream
+ * message number
+ * pointer to returned header size
+ * Returns: position of header in file
+ */
+
+unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size)
+{
+ unsigned long siz;
+ long i = 0;
+ int q = 0;
+ char *s,tmp[MAILTMPLEN];
+ MESSAGECACHE *elt = mtx_elt (stream,msgno);
+ unsigned long ret = elt->private.special.offset +
+ elt->private.special.text.size;
+ /* is header size known? */
+ if (!(*size = elt->private.msg.header.text.size)) {
+ lseek (LOCAL->fd,ret,L_SET);/* get to header position */
+ /* search message for CRLF CRLF */
+ for (siz = 1,s = tmp; siz <= elt->rfc822_size; siz++) {
+ /* read another buffer as necessary */
+ if ((--i <= 0) && /* buffer empty? */
+ (read (LOCAL->fd,s = tmp,
+ i = min (elt->rfc822_size - siz,(long) MAILTMPLEN)) < 0))
+ return ret; /* I/O error? */
+ switch (q) { /* sniff at buffer */
+ case 0: /* first character */
+ q = (*s++ == '\015') ? 1 : 0;
+ break;
+ case 1: /* second character */
+ q = (*s++ == '\012') ? 2 : 0;
+ break;
+ case 2: /* third character */
+ q = (*s++ == '\015') ? 3 : 0;
+ break;
+ case 3: /* fourth character */
+ if (*s++ == '\012') { /* have the sequence? */
+ /* yes, note for later */
+ elt->private.msg.header.text.size = *size = siz;
+ return ret;
+ }
+ q = 0; /* lost... */
+ break;
+ }
+ }
+ /* header consumes entire message */
+ elt->private.msg.header.text.size = *size = elt->rfc822_size;
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/unix/mx.c b/imap/src/osdep/unix/mx.c
new file mode 100644
index 00000000..45495279
--- /dev/null
+++ b/imap/src/osdep/unix/mx.c
@@ -0,0 +1,1287 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: MX mail routines
+ *
+ * Author(s): Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 May 1996
+ * Last Edited: 6 January 2008
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "misc.h"
+#include "dummy.h"
+#include "fdstring.h"
+
+/* Index file */
+
+#define MXINDEXNAME "/.mxindex"
+#define MXINDEX(d,s) strcat (mx_file (d,s),MXINDEXNAME)
+
+
+/* MX I/O stream local data */
+
+typedef struct mx_local {
+ int fd; /* file descriptor of open index */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ unsigned long cachedtexts; /* total size of all cached texts */
+ time_t scantime; /* last time directory scanned */
+} MXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MXLOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *mx_valid (char *name);
+int mx_isvalid (char *name,char *tmp);
+int mx_namevalid (char *name);
+void *mx_parameters (long function,void *value);
+long mx_dirfmttest (char *name);
+void mx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+long mx_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz);
+void mx_list (MAILSTREAM *stream,char *ref,char *pat);
+void mx_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long mx_subscribe (MAILSTREAM *stream,char *mailbox);
+long mx_unsubscribe (MAILSTREAM *stream,char *mailbox);
+long mx_create (MAILSTREAM *stream,char *mailbox);
+long mx_delete (MAILSTREAM *stream,char *mailbox);
+long mx_rename (MAILSTREAM *stream,char *old,char *newname);
+int mx_rename_work (char *src,size_t srcl,char *dst,size_t dstl,char *name);
+MAILSTREAM *mx_open (MAILSTREAM *stream);
+void mx_close (MAILSTREAM *stream,long options);
+void mx_fast (MAILSTREAM *stream,char *sequence,long flags);
+char *mx_fast_work (MAILSTREAM *stream,MESSAGECACHE *elt);
+char *mx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags);
+long mx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void mx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+void mx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long mx_ping (MAILSTREAM *stream);
+void mx_check (MAILSTREAM *stream);
+long mx_expunge (MAILSTREAM *stream,char *sequence,long options);
+long mx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,
+ long options);
+long mx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+long mx_append_msg (MAILSTREAM *stream,char *flags,MESSAGECACHE *elt,
+ STRING *st,SEARCHSET *set);
+
+int mx_select (struct direct *name);
+int mx_numsort (const void *d1,const void *d2);
+char *mx_file (char *dst,char *name);
+long mx_lockindex (MAILSTREAM *stream);
+void mx_unlockindex (MAILSTREAM *stream);
+void mx_setdate (char *file,MESSAGECACHE *elt);
+
+
+/* MX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mxdriver = {
+ "mx", /* driver name */
+ /* driver flags */
+ DR_MAIL|DR_LOCAL|DR_NOFAST|DR_CRLF|DR_LOCKING|DR_DIRFMT,
+ (DRIVER *) NIL, /* next driver */
+ mx_valid, /* mailbox is valid for us */
+ mx_parameters, /* manipulate parameters */
+ mx_scan, /* scan mailboxes */
+ mx_list, /* find mailboxes */
+ mx_lsub, /* find subscribed mailboxes */
+ mx_subscribe, /* subscribe to mailbox */
+ mx_unsubscribe, /* unsubscribe from mailbox */
+ mx_create, /* create mailbox */
+ mx_delete, /* delete mailbox */
+ mx_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ mx_open, /* open mailbox */
+ mx_close, /* close mailbox */
+ mx_fast, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mx_header, /* fetch message header only */
+ mx_text, /* fetch message body only */
+ NIL, /* fetch partial message test */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ mx_flag, /* modify flags */
+ mx_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mx_ping, /* ping mailbox to see if still alive */
+ mx_check, /* check for new messages */
+ mx_expunge, /* expunge deleted messages */
+ mx_copy, /* copy messages to another mailbox */
+ mx_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mxproto = {&mxdriver};
+
+/* MX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mx_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return mx_isvalid (name,tmp) ? &mxdriver : NIL;
+}
+
+
+/* MX mail test for valid mailbox
+ * Accepts: mailbox name
+ * temporary buffer to use
+ * Returns: T if valid, NIL otherwise with errno holding dir stat error
+ */
+
+int mx_isvalid (char *name,char *tmp)
+{
+ struct stat sbuf;
+ errno = NIL; /* zap error */
+ if ((strlen (name) <= NETMAXMBX) && *mx_file (tmp,name) &&
+ !stat (tmp,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) {
+ /* name is directory; is it mx? */
+ if (!stat (MXINDEX (tmp,name),&sbuf) &&
+ ((sbuf.st_mode & S_IFMT) == S_IFREG)) return T;
+ errno = NIL; /* directory but not mx */
+ }
+ else if (!compare_cstring (name,"INBOX")) errno = NIL;
+ return NIL;
+}
+
+
+/* MX mail test for valid mailbox
+ * Accepts: mailbox name
+ * Returns: T if valid, NIL otherwise
+ */
+
+int mx_namevalid (char *name)
+{
+ char *s = (*name == '/') ? name + 1 : name;
+ while (s && *s) { /* make sure valid name */
+ if (isdigit (*s)) s++; /* digit, check this node further... */
+ else if (*s == '/') break; /* all digit node, barf */
+ /* non-digit, skip to next node or return */
+ else if (!((s = strchr (s+1,'/')) && *++s)) return T;
+ }
+ return NIL; /* all numeric or empty node */
+}
+
+/* MX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mx_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = mailboxfile ((char *) value,"~/INBOX");
+ break;
+ case GET_DIRFMTTEST:
+ ret = (void *) mx_dirfmttest;
+ break;
+ case GET_SCANCONTENTS:
+ ret = (void *) mx_scan_contents;
+ break;
+ }
+ return ret;
+}
+
+
+/* MX test for directory format internal node
+ * Accepts: candidate node name
+ * Returns: T if internal name, NIL otherwise
+ */
+
+long mx_dirfmttest (char *name)
+{
+ int c;
+ /* success if index name or all-numberic */
+ if (strcmp (name,MXINDEXNAME+1))
+ while (c = *name++) if (!isdigit (c)) return NIL;
+ return LONGT;
+}
+
+/* MX scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* MX scan mailbox for contents
+ * Accepts: mailbox name
+ * desired contents
+ * contents size
+ * file size (ignored)
+ * Returns: NIL if contents not found, T if found
+ */
+
+long mx_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz)
+{
+ long i,nfiles;
+ void *a;
+ char *s;
+ long ret = NIL;
+ size_t namelen = strlen (name);
+ struct stat sbuf;
+ struct direct **names = NIL;
+ if ((nfiles = scandir (name,&names,mx_select,mx_numsort)) > 0)
+ for (i = 0; i < nfiles; ++i) {
+ if (!ret) {
+ sprintf (s = (char *) fs_get (namelen + strlen (names[i]->d_name) + 2),
+ "%s/%s",name,names[i]->d_name);
+ if (!stat (s,&sbuf) && (csiz <= sbuf.st_size))
+ ret = dummy_scan_contents (s,contents,csiz,sbuf.st_size);
+ fs_give ((void **) &s);
+ }
+ fs_give ((void **) &names[i]);
+ }
+ /* free directory list */
+ if (a = (void *) names) fs_give ((void **) &a);
+ return ret;
+}
+
+/* MX list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mx_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* MX list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mx_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* MX mail subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long mx_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return sm_subscribe (mailbox);
+}
+
+
+/* MX mail unsubscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to delete from subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long mx_unsubscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return sm_unsubscribe (mailbox);
+}
+
+/* MX mail create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mx_create (MAILSTREAM *stream,char *mailbox)
+{
+ DRIVER *test;
+ int fd;
+ char *s,tmp[MAILTMPLEN];
+ int mask = umask (0);
+ long ret = NIL;
+ if (!mx_namevalid (mailbox)) /* validate name */
+ sprintf (tmp,"Can't create mailbox %.80s: invalid MX-format name",mailbox);
+ /* must not already exist */
+ else if ((test = mail_valid (NIL,mailbox,NIL)) &&
+ strcmp (test->name,"dummy"))
+ sprintf (tmp,"Can't create mailbox %.80s: mailbox already exists",mailbox);
+ /* create directory */
+ else if (!dummy_create_path (stream,MXINDEX (tmp,mailbox),
+ get_dir_protection (mailbox)))
+ sprintf (tmp,"Can't create mailbox %.80s: %s",mailbox,strerror (errno));
+ else { /* success */
+ /* set index protection */
+ set_mbx_protections (mailbox,tmp);
+ /* tie off directory name */
+ *(s = strrchr (tmp,'/') + 1) = '\0';
+ /* set directory protection */
+ set_mbx_protections (mailbox,tmp);
+ ret = LONGT;
+ }
+ umask (mask); /* restore mask */
+ if (!ret) MM_LOG (tmp,ERROR); /* some error */
+ return ret;
+}
+
+/* MX mail delete mailbox
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mx_delete (MAILSTREAM *stream,char *mailbox)
+{
+ DIR *dirp;
+ struct direct *d;
+ char *s;
+ char tmp[MAILTMPLEN];
+ if (!mx_isvalid (mailbox,tmp))
+ sprintf (tmp,"Can't delete mailbox %.80s: no such mailbox",mailbox);
+ /* delete index */
+ else if (unlink (MXINDEX (tmp,mailbox)))
+ sprintf (tmp,"Can't delete mailbox %.80s index: %s",
+ mailbox,strerror (errno));
+ else { /* get directory name */
+ *(s = strrchr (tmp,'/')) = '\0';
+ if (dirp = opendir (tmp)) { /* open directory */
+ *s++ = '/'; /* restore delimiter */
+ /* massacre messages */
+ while (d = readdir (dirp)) if (mx_select (d)) {
+ strcpy (s,d->d_name); /* make path */
+ unlink (tmp); /* sayonara */
+ }
+ closedir (dirp); /* flush directory */
+ *(s = strrchr (tmp,'/')) = '\0';
+ if (rmdir (tmp)) { /* try to remove the directory */
+ sprintf (tmp,"Can't delete name %.80s: %s",mailbox,strerror (errno));
+ MM_LOG (tmp,WARN);
+ }
+ }
+ return T; /* always success */
+ }
+ MM_LOG (tmp,ERROR); /* something failed */
+ return NIL;
+}
+
+/* MX mail rename mailbox
+ * Accepts: MX mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long mx_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ char c,*s,tmp[MAILTMPLEN],tmp1[MAILTMPLEN];
+ struct stat sbuf;
+ if (!mx_isvalid (old,tmp))
+ sprintf (tmp,"Can't rename mailbox %.80s: no such mailbox",old);
+ else if (!mx_namevalid (newname))
+ sprintf (tmp,"Can't rename to mailbox %.80s: invalid MX-format name",
+ newname);
+ /* new mailbox name must not be valid */
+ else if (mx_isvalid (newname,tmp))
+ sprintf (tmp,"Can't rename to mailbox %.80s: destination already exists",
+ newname);
+ else {
+ mx_file (tmp,old); /* build old directory name */
+ mx_file (tmp1,newname); /* and new directory name */
+ /* easy if not INBOX */
+ if (compare_cstring (old,"INBOX")) {
+ /* found superior to destination name? */
+ if (s = strrchr (mx_file (tmp1,newname),'/')) {
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (tmp1,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,tmp1,get_dir_protection (newname)))
+ return NIL;
+ *s = c; /* restore full name */
+ }
+ if (!rename (tmp,tmp1)) return LONGT;
+ }
+
+ /* RFC 3501 requires this */
+ else if (dummy_create_path (stream,strcat (tmp1,"/"),
+ get_dir_protection (newname))) {
+ void *a;
+ int i,n,lasterror;
+ struct direct **names = NIL;
+ size_t srcl = strlen (tmp);
+ size_t dstl = strlen (tmp1);
+ /* rename each mx file to new directory */
+ for (i = lasterror = 0,n = scandir (tmp,&names,mx_select,mx_numsort);
+ i < n; ++i) {
+ if (mx_rename_work (tmp,srcl,tmp1,dstl,names[i]->d_name))
+ lasterror = errno;
+ fs_give ((void **) &names[i]);
+ }
+ /* free directory list */
+ if (a = (void *) names) fs_give ((void **) &a);
+ if (lasterror || mx_rename_work (tmp,srcl,tmp1,dstl,MXINDEXNAME+1))
+ errno = lasterror;
+ else return mx_create (NIL,"INBOX");
+ }
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",
+ old,newname,strerror (errno));
+ }
+ MM_LOG (tmp,ERROR); /* something failed */
+ return NIL;
+}
+
+
+/* MX rename worker routine
+ * Accepts: source directory name
+ * source directory name length
+ * destination directory name
+ * destination directory name length
+ * name of node to move
+ * Returns: zero if success, non-zero if error
+ */
+
+int mx_rename_work (char *src,size_t srcl,char *dst,size_t dstl,char *name)
+{
+ int ret;
+ size_t len = strlen (name);
+ char *s = (char *) fs_get (srcl + len + 2);
+ char *d = (char *) fs_get (dstl + len + 1);
+ sprintf (s,"%s/%s",src,name);
+ sprintf (d,"%s%s",dst,name);
+ ret = rename (s,d);
+ fs_give ((void **) &s);
+ fs_give ((void **) &d);
+ return ret;
+}
+
+/* MX mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mx_open (MAILSTREAM *stream)
+{
+ char tmp[MAILTMPLEN];
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return user_flags (&mxproto);
+ if (stream->local) fatal ("mx recycle stream");
+ stream->local = fs_get (sizeof (MXLOCAL));
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ mx_file (tmp,stream->mailbox);/* get directory name */
+ /* canonicalize mailbox name */
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ /* make temporary buffer */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ LOCAL->scantime = 0; /* not scanned yet */
+ LOCAL->fd = -1; /* no index yet */
+ LOCAL->cachedtexts = 0; /* no cached texts */
+ stream->sequence++; /* bump sequence number */
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ if (mx_ping (stream) && !(stream->nmsgs || stream->silent))
+ MM_LOG ("Mailbox is empty",(long) NIL);
+ stream->perm_seen = stream->perm_deleted = stream->perm_flagged =
+ stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ stream->kwd_create = (stream->user_flags[NUSERFLAGS-1] || stream->rdonly) ?
+ NIL : T; /* can we create new user flags? */
+ return stream; /* return stream to caller */
+}
+
+/* MX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mx_close (MAILSTREAM *stream,long options)
+{
+ if (LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ if (options & CL_EXPUNGE) mx_expunge (stream,NIL,NIL);
+ /* free local scratch buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ stream->silent = silent; /* reset silent state */
+ }
+}
+
+/* MX mail fetch fast information
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ */
+
+void mx_fast (MAILSTREAM *stream,char *sequence,long flags)
+{
+ unsigned long i;
+ MESSAGECACHE *elt;
+ if (stream && LOCAL &&
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) mx_fast_work (stream,elt);
+}
+
+
+/* MX mail fetch fast information
+ * Accepts: MAIL stream
+ * message cache element
+ * Returns: name of message file
+ */
+
+char *mx_fast_work (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ struct stat sbuf;
+ struct tm *tm;
+ /* build message file name */
+ sprintf (LOCAL->buf,"%s/%lu",stream->mailbox,elt->private.uid);
+ /* have size yet? */
+ if (!elt->rfc822_size && !stat (LOCAL->buf,&sbuf)) {
+ /* make plausible IMAPish date string */
+ tm = gmtime (&sbuf.st_mtime);
+ elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1;
+ elt->year = tm->tm_year + 1900 - BASEYEAR;
+ elt->hours = tm->tm_hour; elt->minutes = tm->tm_min;
+ elt->seconds = tm->tm_sec;
+ elt->zhours = 0; elt->zminutes = 0; elt->zoccident = 0;
+ elt->rfc822_size = sbuf.st_size;
+ }
+ return (char *) LOCAL->buf; /* return file name */
+}
+
+/* MX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *mx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags)
+{
+ unsigned long i;
+ int fd;
+ MESSAGECACHE *elt;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ elt = mail_elt (stream,msgno);/* get elt */
+ if (!elt->private.msg.header.text.data) {
+ /* purge cache if too big */
+ if (LOCAL->cachedtexts > max (stream->nmsgs * 4096,2097152)) {
+ mail_gc (stream,GC_TEXTS);/* just can't keep that much */
+ LOCAL->cachedtexts = 0;
+ }
+ if ((fd = open (mx_fast_work (stream,elt),O_RDONLY,NIL)) < 0) return "";
+ /* is buffer big enough? */
+ if (elt->rfc822_size > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = elt->rfc822_size) + 1);
+ }
+ /* slurp message */
+ read (fd,LOCAL->buf,elt->rfc822_size);
+ /* tie off file */
+ LOCAL->buf[elt->rfc822_size] = '\0';
+ close (fd); /* flush message file */
+ /* find end of header */
+ if (elt->rfc822_size < 4) i = 0;
+ else for (i = 4; (i < elt->rfc822_size) &&
+ !((LOCAL->buf[i - 4] == '\015') &&
+ (LOCAL->buf[i - 3] == '\012') &&
+ (LOCAL->buf[i - 2] == '\015') &&
+ (LOCAL->buf[i - 1] == '\012')); i++);
+ /* copy header */
+ cpytxt (&elt->private.msg.header.text,LOCAL->buf,i);
+ cpytxt (&elt->private.msg.text.text,LOCAL->buf+i,elt->rfc822_size - i);
+ /* add to cached size */
+ LOCAL->cachedtexts += elt->rfc822_size;
+ }
+ *length = elt->private.msg.header.text.size;
+ return (char *) elt->private.msg.header.text.data;
+}
+
+/* MX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ unsigned long i;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mail_elt (stream,msgno);
+ /* snarf message if don't have it yet */
+ if (!elt->private.msg.text.text.data) {
+ mx_header (stream,msgno,&i,flags);
+ if (!elt->private.msg.text.text.data) return NIL;
+ }
+ /* mark as seen */
+ if (!(flags & FT_PEEK) && mx_lockindex (stream)) {
+ elt->seen = T;
+ mx_unlockindex (stream);
+ MM_FLAGS (stream,msgno);
+ }
+ INIT (bs,mail_string,elt->private.msg.text.text.data,
+ elt->private.msg.text.text.size);
+ return T;
+}
+
+/* MX mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ */
+
+void mx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ mx_unlockindex (stream); /* finished with index */
+}
+
+
+/* MX per-message modify flags
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void mx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ mx_lockindex (stream); /* lock index if not already locked */
+}
+
+/* MX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long mx_ping (MAILSTREAM *stream)
+{
+ MAILSTREAM *sysibx = NIL;
+ MESSAGECACHE *elt,*selt;
+ struct stat sbuf;
+ char *s,tmp[MAILTMPLEN];
+ int fd;
+ unsigned long i,j,r,old;
+ long nmsgs = stream->nmsgs;
+ long recent = stream->recent;
+ int silent = stream->silent;
+ if (stat (stream->mailbox,&sbuf)) return NIL;
+ stream->silent = T; /* don't pass up exists events yet */
+ if (sbuf.st_ctime != LOCAL->scantime) {
+ struct direct **names = NIL;
+ long nfiles = scandir (stream->mailbox,&names,mx_select,mx_numsort);
+ if (nfiles < 0) nfiles = 0; /* in case error */
+ old = stream->uid_last;
+ /* note scanned now */
+ LOCAL->scantime = sbuf.st_ctime;
+ /* scan directory */
+ for (i = 0; i < nfiles; ++i) {
+ /* if newly seen, add to list */
+ if ((j = atoi (names[i]->d_name)) > old) {
+ /* swell the cache */
+ mail_exists (stream,++nmsgs);
+ stream->uid_last = (elt = mail_elt (stream,nmsgs))->private.uid = j;
+ elt->valid = T; /* note valid flags */
+ if (old) { /* other than the first pass? */
+ elt->recent = T; /* yup, mark as recent */
+ recent++; /* bump recent count */
+ }
+ }
+ fs_give ((void **) &names[i]);
+ }
+ /* free directory */
+ if (s = (void *) names) fs_give ((void **) &s);
+ }
+ stream->nmsgs = nmsgs; /* don't upset mail_uid() */
+
+ /* if INBOX, snarf from system INBOX */
+ if (mx_lockindex (stream) && stream->inbox &&
+ !strcmp (sysinbox (),stream->mailbox)) {
+ old = stream->uid_last;
+ MM_CRITICAL (stream); /* go critical */
+ /* see if anything in system inbox */
+ if (!stat (sysinbox (),&sbuf) && sbuf.st_size &&
+ (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) &&
+ !sysibx->rdonly && (r = sysibx->nmsgs)) {
+ for (i = 1; i <= r; ++i) {/* for each message in sysinbox mailbox */
+ /* build file name we will use */
+ sprintf (LOCAL->buf,"%s/%lu",stream->mailbox,++old);
+ /* snarf message from Berkeley mailbox */
+ selt = mail_elt (sysibx,i);
+ if (((fd = open (LOCAL->buf,O_WRONLY|O_CREAT|O_EXCL,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL)))
+ >= 0) &&
+ (s = mail_fetchheader_full (sysibx,i,NIL,&j,FT_PEEK)) &&
+ (write (fd,s,j) == j) &&
+ (s = mail_fetchtext_full (sysibx,i,&j,FT_PEEK)) &&
+ (write (fd,s,j) == j) && !fsync (fd) && !close (fd)) {
+ /* swell the cache */
+ mail_exists (stream,++nmsgs);
+ stream->uid_last = /* create new elt, note its file number */
+ (elt = mail_elt (stream,nmsgs))->private.uid = old;
+ recent++; /* bump recent count */
+ /* set up initial flags and date */
+ elt->valid = elt->recent = T;
+ elt->seen = selt->seen;
+ elt->deleted = selt->deleted;
+ elt->flagged = selt->flagged;
+ elt->answered = selt->answered;
+ elt->draft = selt->draft;
+ elt->day = selt->day;elt->month = selt->month;elt->year = selt->year;
+ elt->hours = selt->hours;elt->minutes = selt->minutes;
+ elt->seconds = selt->seconds;
+ elt->zhours = selt->zhours; elt->zminutes = selt->zminutes;
+ elt->zoccident = selt->zoccident;
+ mx_setdate (LOCAL->buf,elt);
+ sprintf (tmp,"%lu",i);/* delete it from the sysinbox */
+ mail_flag (sysibx,tmp,"\\Deleted",ST_SET);
+ }
+ else { /* failed to snarf */
+ if (fd) { /* did it ever get opened? */
+ close (fd); /* close descriptor */
+ unlink (LOCAL->buf);/* flush this file */
+ }
+ sprintf (tmp,"Message copy to MX mailbox failed: %.80s",
+ s,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ r = 0; /* stop the snarf in its tracks */
+ }
+ }
+ /* update scan time */
+ if (!stat (stream->mailbox,&sbuf)) LOCAL->scantime = sbuf.st_ctime;
+ mail_expunge (sysibx); /* now expunge all those messages */
+ }
+ if (sysibx) mail_close (sysibx);
+ MM_NOCRITICAL (stream); /* release critical */
+ }
+ mx_unlockindex (stream); /* done with index */
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of mailbox size */
+ mail_recent (stream,recent);
+ return T; /* return that we are alive */
+}
+
+/* MX mail check mailbox
+ * Accepts: MAIL stream
+ */
+
+void mx_check (MAILSTREAM *stream)
+{
+ if (mx_ping (stream)) MM_LOG ("Check completed",(long) NIL);
+}
+
+
+/* MX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long mx_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ MESSAGECACHE *elt;
+ unsigned long i = 1;
+ unsigned long n = 0;
+ unsigned long recent = stream->recent;
+ if (ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ mx_lockindex (stream)) { /* lock the index */
+ MM_CRITICAL (stream); /* go critical */
+ while (i <= stream->nmsgs) {/* for each message */
+ elt = mail_elt (stream,i);/* if deleted, need to trash it */
+ if (elt->deleted && (sequence ? elt->sequence : T)) {
+ sprintf (LOCAL->buf,"%s/%lu",stream->mailbox,elt->private.uid);
+ if (unlink (LOCAL->buf)) {/* try to delete the message */
+ sprintf (LOCAL->buf,"Expunge of message %lu failed, aborted: %s",i,
+ strerror (errno));
+ MM_LOG (LOCAL->buf,(long) NIL);
+ break;
+ }
+ /* note uncached */
+ LOCAL->cachedtexts -= ((elt->private.msg.header.text.data ?
+ elt->private.msg.header.text.size : 0) +
+ (elt->private.msg.text.text.data ?
+ elt->private.msg.text.text.size : 0));
+ mail_gc_msg (&elt->private.msg,GC_ENV | GC_TEXTS);
+ if(elt->recent)--recent;/* if recent, note one less recent message */
+ mail_expunged(stream,i);/* notify upper levels */
+ n++; /* count up one more expunged message */
+ }
+ else i++; /* otherwise try next message */
+ }
+ if (n) { /* output the news if any expunged */
+ sprintf (LOCAL->buf,"Expunged %lu messages",n);
+ MM_LOG (LOCAL->buf,(long) NIL);
+ }
+ else MM_LOG ("No messages deleted, so no update needed",(long) NIL);
+ MM_NOCRITICAL (stream); /* release critical */
+ mx_unlockindex (stream); /* finished with index */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ }
+ return ret;
+}
+
+/* MX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if copy successful, else NIL
+ */
+
+long mx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ FDDATA d;
+ STRING st;
+ MESSAGECACHE *elt;
+ MAILSTREAM *astream;
+ struct stat sbuf;
+ int fd;
+ unsigned long i,j,uid,uidv;
+ char *t,tmp[MAILTMPLEN];
+ long ret;
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ /* make sure valid mailbox */
+ if (!mx_valid (mailbox)) switch (errno) {
+ case NIL: /* no error in stat() */
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a MX-format mailbox: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ default: /* some stat() error */
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ }
+ /* copy the messages */
+ if (!(ret = ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))));
+ /* acquire stream to append to */
+ else if (!(astream = mail_open (NIL,mailbox,OP_SILENT))) {
+ MM_LOG ("Can't open copy mailbox",ERROR);
+ ret = NIL;
+ }
+ else {
+ MM_CRITICAL (stream); /* go critical */
+ if (!(ret = mx_lockindex (astream)))
+ MM_LOG ("Message copy failed: unable to lock index",ERROR);
+ else {
+
+ copyuid_t cu = (copyuid_t) mail_parameters (NIL,GET_COPYUID,NIL);
+ SEARCHSET *source = cu ? mail_newsearchset () : NIL;
+ SEARCHSET *dest = cu ? mail_newsearchset () : NIL;
+ for (i = 1,uid = uidv = 0; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ if (ret = ((fd = open (mx_fast_work (stream,elt),O_RDONLY,NIL))
+ >= 0)) {
+ fstat (fd,&sbuf); /* get size of message */
+ d.fd = fd; /* set up file descriptor */
+ d.pos = 0; /* start of file */
+ d.chunk = LOCAL->buf;
+ d.chunksize = CHUNKSIZE;
+ INIT (&st,fd_string,&d,sbuf.st_size);
+ /* init flag string */
+ tmp[0] = tmp[1] = '\0';
+ if (j = elt->user_flags) do
+ if (t = stream->user_flags[find_rightmost_bit (&j)])
+ strcat (strcat (tmp," "),t);
+ while (j);
+ if (elt->seen) strcat (tmp," \\Seen");
+ if (elt->deleted) strcat (tmp," \\Deleted");
+ if (elt->flagged) strcat (tmp," \\Flagged");
+ if (elt->answered) strcat (tmp," \\Answered");
+ if (elt->draft) strcat (tmp," \\Draft");
+ tmp[0] = '('; /* open list */
+ strcat (tmp,")"); /* close list */
+ if (ret = mx_append_msg (astream,tmp,elt,&st,dest)) {
+ /* add to source set if needed */
+ if (source) mail_append_set (source,mail_uid (stream,i));
+ /* delete if doing a move */
+ if (options & CP_MOVE) elt->deleted = T;
+ }
+ }
+ }
+ /* return sets if doing COPYUID */
+ if (cu && ret) (*cu) (stream,mailbox,astream->uid_validity,source,dest);
+ else { /* flush any sets we may have built */
+ mail_free_searchset (&source);
+ mail_free_searchset (&dest);
+ }
+ mx_unlockindex (astream); /* unlock index */
+ }
+ MM_NOCRITICAL (stream);
+ mail_close (astream); /* finished with append stream */
+ }
+ return ret; /* return success */
+}
+
+/* MX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ MESSAGECACHE elt;
+ MAILSTREAM *astream;
+ char *flags,*date,tmp[MAILTMPLEN];
+ STRING *message;
+ long ret = LONGT;
+ /* default stream to prototype */
+ if (!stream) stream = user_flags (&mxproto);
+ /* N.B.: can't use LOCAL->buf for tmp */
+ /* make sure valid mailbox */
+ if (!mx_isvalid (mailbox,tmp)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (!compare_cstring (mailbox,"INBOX")) mx_create (NIL,"INBOX");
+ else {
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* falls through */
+ case 0: /* merely empty file? */
+ break;
+ case EINVAL:
+ sprintf (tmp,"Invalid MX-format mailbox name: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a MX-format mailbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+
+ /* get first message */
+ if (!MM_APPEND (af) (stream,data,&flags,&date,&message)) return NIL;
+ if (!(astream = mail_open (NIL,mailbox,OP_SILENT))) {
+ MM_LOG ("Can't open append mailbox",ERROR);
+ return NIL;
+ }
+ MM_CRITICAL (astream); /* go critical */
+ /* lock the index */
+ if (!(ret = mx_lockindex (astream)))
+ MM_LOG ("Message append failed: unable to lock index",ERROR);
+ else {
+ appenduid_t au = (appenduid_t) mail_parameters (NIL,GET_APPENDUID,NIL);
+ SEARCHSET *dst = au ? mail_newsearchset () : NIL;
+ do {
+ /* guard against zero-length */
+ if (!(ret = SIZE (message)))
+ MM_LOG ("Append of zero-length message",ERROR);
+ else if (date && !(ret = mail_parse_date (&elt,date))) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ MM_LOG (tmp,ERROR);
+ }
+ else ret = mx_append_msg (astream,flags,date ? &elt : NIL,message,dst)&&
+ MM_APPEND (af) (stream,data,&flags,&date,&message);
+ } while (ret && message);
+ /* return sets if doing APPENDUID */
+ if (au && ret) (*au) (mailbox,astream->uid_validity,dst);
+ else mail_free_searchset (&dst);
+ mx_unlockindex (astream); /* unlock index */
+ }
+ MM_NOCRITICAL (astream); /* release critical */
+ mail_close (astream);
+ return ret;
+}
+
+/* MX mail append single message
+ * Accepts: MAIL stream
+ * flags for new message if non-NIL
+ * elt with source date if non-NIL
+ * stringstruct of message text
+ * searchset to place UID
+ * Returns: T if success, NIL if failure
+ */
+
+long mx_append_msg (MAILSTREAM *stream,char *flags,MESSAGECACHE *elt,
+ STRING *st,SEARCHSET *set)
+{
+ char tmp[MAILTMPLEN];
+ int fd;
+ unsigned long uf;
+ long f = mail_parse_flags (stream,flags,&uf);
+ /* make message file name */
+ sprintf (tmp,"%s/%lu",stream->mailbox,++stream->uid_last);
+ if ((fd = open (tmp,O_WRONLY|O_CREAT|O_EXCL,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL))) < 0) {
+ sprintf (tmp,"Can't create append message: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ while (SIZE (st)) { /* copy the file */
+ if (st->cursize && (write (fd,st->curpos,st->cursize) < 0)) {
+ unlink (tmp); /* delete file */
+ close (fd); /* close the file */
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ SETPOS (st,GETPOS (st) + st->cursize);
+ }
+ close (fd); /* close the file */
+ if (elt) mx_setdate (tmp,elt);/* set file date */
+ /* swell the cache */
+ mail_exists (stream,++stream->nmsgs);
+ /* copy flags */
+ mail_append_set (set,(elt = mail_elt (stream,stream->nmsgs))->private.uid =
+ stream->uid_last);
+ if (f&fSEEN) elt->seen = T;
+ if (f&fDELETED) elt->deleted = T;
+ if (f&fFLAGGED) elt->flagged = T;
+ if (f&fANSWERED) elt->answered = T;
+ if (f&fDRAFT) elt->draft = T;
+ elt->user_flags |= uf;
+ return LONGT;
+}
+
+/* Internal routines */
+
+
+/* MX file name selection test
+ * Accepts: candidate directory entry
+ * Returns: T to use file name, NIL to skip it
+ */
+
+int mx_select (struct direct *name)
+{
+ char c;
+ char *s = name->d_name;
+ while (c = *s++) if (!isdigit (c)) return NIL;
+ return T;
+}
+
+
+/* MX file name comparision
+ * Accepts: first candidate directory entry
+ * second candidate directory entry
+ * Returns: negative if d1 < d2, 0 if d1 == d2, postive if d1 > d2
+ */
+
+int mx_numsort (const void *d1,const void *d2)
+{
+ return atoi ((*(struct direct **) d1)->d_name) -
+ atoi ((*(struct direct **) d2)->d_name);
+}
+
+
+/* MX mail build file name
+ * Accepts: destination string
+ * source
+ * Returns: destination
+ */
+
+char *mx_file (char *dst,char *name)
+{
+ char *s;
+ /* empty string if mailboxfile fails */
+ if (!mailboxfile (dst,name)) *dst = '\0';
+ /* driver-selected INBOX */
+ else if (!*dst) mailboxfile (dst,"~/INBOX");
+ /* tie off unnecessary trailing / */
+ else if ((s = strrchr (dst,'/')) && !s[1]) *s = '\0';
+ return dst;
+}
+
+/* MX read and lock index
+ * Accepts: MAIL stream
+ * Returns: T if success, NIL if failure
+ */
+
+long mx_lockindex (MAILSTREAM *stream)
+{
+ unsigned long uf,sf,uid;
+ int k = 0;
+ unsigned long msgno = 1;
+ struct stat sbuf;
+ char *s,*t,*idx,tmp[2*MAILTMPLEN];
+ MESSAGECACHE *elt;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if ((LOCAL->fd < 0) && /* get index file, no-op if already have it */
+ (LOCAL->fd = open (strcat (strcpy (tmp,stream->mailbox),MXINDEXNAME),
+ O_RDWR|O_CREAT,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL)))
+ >= 0) {
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_EX); /* get exclusive lock */
+ (*bn) (BLOCK_NONE,NIL);
+ fstat (LOCAL->fd,&sbuf); /* get size of index */
+ /* slurp index */
+ read (LOCAL->fd,s = idx = (char *) fs_get (sbuf.st_size + 1),sbuf.st_size);
+ idx[sbuf.st_size] = '\0'; /* tie off index */
+ /* parse index */
+ if (sbuf.st_size) while (s && *s) switch (*s) {
+ case 'V': /* UID validity record */
+ stream->uid_validity = strtoul (s+1,&s,16);
+ break;
+ case 'L': /* UID last record */
+ stream->uid_last = strtoul (s+1,&s,16);
+ break;
+ case 'K': /* keyword */
+ /* find end of keyword */
+ if (s = strchr (t = ++s,'\n')) {
+ *s++ = '\0'; /* tie off keyword */
+ /* copy keyword */
+ if ((k < NUSERFLAGS) && !stream->user_flags[k] &&
+ (strlen (t) <= MAXUSERFLAG)) stream->user_flags[k] = cpystr (t);
+ k++; /* one more keyword */
+ }
+ break;
+
+ case 'M': /* message status record */
+ uid = strtoul (s+1,&s,16);/* get UID for this message */
+ if (*s == ';') { /* get user flags */
+ uf = strtoul (s+1,&s,16);
+ if (*s == '.') { /* get system flags */
+ sf = strtoul (s+1,&s,16);
+ while ((msgno <= stream->nmsgs) && (mail_uid (stream,msgno) < uid))
+ msgno++; /* find message number for this UID */
+ if ((msgno <= stream->nmsgs) && (mail_uid (stream,msgno) == uid)) {
+ (elt = mail_elt (stream,msgno))->valid = T;
+ elt->user_flags=uf; /* set user and system flags in elt */
+ if (sf & fSEEN) elt->seen = T;
+ if (sf & fDELETED) elt->deleted = T;
+ if (sf & fFLAGGED) elt->flagged = T;
+ if (sf & fANSWERED) elt->answered = T;
+ if (sf & fDRAFT) elt->draft = T;
+ }
+ break;
+ }
+ }
+ default: /* bad news */
+ sprintf (tmp,"Error in index: %.80s",s);
+ MM_LOG (tmp,ERROR);
+ *s = NIL; /* ignore remainder of index */
+ }
+ else { /* new index */
+ stream->uid_validity = time (0);
+ user_flags (stream); /* init stream with default user flags */
+ }
+ fs_give ((void **) &idx); /* flush index */
+ }
+ return (LOCAL->fd >= 0) ? T : NIL;
+}
+
+/* MX write and unlock index
+ * Accepts: MAIL stream
+ */
+
+#define MXIXBUFLEN 2048
+
+void mx_unlockindex (MAILSTREAM *stream)
+{
+ unsigned long i,j;
+ off_t size = 0;
+ char *s,tmp[MXIXBUFLEN + 64];
+ MESSAGECACHE *elt;
+ if (LOCAL->fd >= 0) {
+ lseek (LOCAL->fd,0,L_SET); /* rewind file */
+ /* write header */
+ sprintf (s = tmp,"V%08lxL%08lx",stream->uid_validity,stream->uid_last);
+ for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i]; ++i)
+ sprintf (s += strlen (s),"K%s\n",stream->user_flags[i]);
+ /* write messages */
+ for (i = 1; i <= stream->nmsgs; i++) {
+ /* filled buffer? */
+ if (((s += strlen (s)) - tmp) > MXIXBUFLEN) {
+ write (LOCAL->fd,tmp,j = s - tmp);
+ size += j;
+ *(s = tmp) = '\0'; /* dump out and restart buffer */
+ }
+ elt = mail_elt (stream,i);
+ sprintf(s,"M%08lx;%08lx.%04x",elt->private.uid,elt->user_flags,(unsigned)
+ ((fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)));
+ }
+ /* write tail end of buffer */
+ if ((s += strlen (s)) != tmp) {
+ write (LOCAL->fd,tmp,j = s - tmp);
+ size += j;
+ }
+ ftruncate (LOCAL->fd,size);
+ flock (LOCAL->fd,LOCK_UN); /* unlock the index */
+ close (LOCAL->fd); /* finished with file */
+ LOCAL->fd = -1; /* no index now */
+ }
+}
+
+/* Set date for message
+ * Accepts: file name
+ * elt containing date
+ */
+
+void mx_setdate (char *file,MESSAGECACHE *elt)
+{
+ time_t tp[2];
+ tp[0] = time (0); /* atime is now */
+ tp[1] = mail_longdate (elt); /* modification time */
+ utime (file,tp); /* set the times */
+}
diff --git a/imap/src/osdep/unix/news.c b/imap/src/osdep/unix/news.c
new file mode 100644
index 00000000..4cf5bb70
--- /dev/null
+++ b/imap/src/osdep/unix/news.c
@@ -0,0 +1,738 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: News routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 4 September 1991
+ * Last Edited: 30 January 2007
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "misc.h"
+#include "newsrc.h"
+#include "fdstring.h"
+
+
+/* news_load_message() flags */
+
+#define NLM_HEADER 0x1 /* load message text */
+#define NLM_TEXT 0x2 /* load message text */
+
+/* NEWS I/O stream local data */
+
+typedef struct news_local {
+ unsigned int dirty : 1; /* disk copy of .newsrc needs updating */
+ char *dir; /* spool directory name */
+ char *name; /* local mailbox name */
+ unsigned char buf[CHUNKSIZE]; /* scratch buffer */
+ unsigned long cachedtexts; /* total size of all cached texts */
+} NEWSLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((NEWSLOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *news_valid (char *name);
+DRIVER *news_isvalid (char *name,char *mbx);
+void *news_parameters (long function,void *value);
+void news_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void news_list (MAILSTREAM *stream,char *ref,char *pat);
+void news_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long news_canonicalize (char *ref,char *pat,char *pattern);
+long news_subscribe (MAILSTREAM *stream,char *mailbox);
+long news_unsubscribe (MAILSTREAM *stream,char *mailbox);
+long news_create (MAILSTREAM *stream,char *mailbox);
+long news_delete (MAILSTREAM *stream,char *mailbox);
+long news_rename (MAILSTREAM *stream,char *old,char *newname);
+MAILSTREAM *news_open (MAILSTREAM *stream);
+int news_select (struct direct *name);
+int news_numsort (const void *d1,const void *d2);
+void news_close (MAILSTREAM *stream,long options);
+void news_fast (MAILSTREAM *stream,char *sequence,long flags);
+void news_flags (MAILSTREAM *stream,char *sequence,long flags);
+void news_load_message (MAILSTREAM *stream,unsigned long msgno,long flags);
+char *news_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long news_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void news_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long news_ping (MAILSTREAM *stream);
+void news_check (MAILSTREAM *stream);
+long news_expunge (MAILSTREAM *stream,char *sequence,long options);
+long news_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long news_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+/* News routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER newsdriver = {
+ "news", /* driver name */
+ /* driver flags */
+ DR_NEWS|DR_READONLY|DR_NOFAST|DR_NAMESPACE|DR_NONEWMAIL|DR_DIRFMT,
+ (DRIVER *) NIL, /* next driver */
+ news_valid, /* mailbox is valid for us */
+ news_parameters, /* manipulate parameters */
+ news_scan, /* scan mailboxes */
+ news_list, /* find mailboxes */
+ news_lsub, /* find subscribed mailboxes */
+ news_subscribe, /* subscribe to mailbox */
+ news_unsubscribe, /* unsubscribe from mailbox */
+ news_create, /* create mailbox */
+ news_delete, /* delete mailbox */
+ news_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ news_open, /* open mailbox */
+ news_close, /* close mailbox */
+ news_fast, /* fetch message "fast" attributes */
+ news_flags, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ news_header, /* fetch message header */
+ news_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ NIL, /* modify flags */
+ news_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ news_ping, /* ping mailbox to see if still alive */
+ news_check, /* check for new messages */
+ news_expunge, /* expunge deleted messages */
+ news_copy, /* copy messages to another mailbox */
+ news_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM newsproto = {&newsdriver};
+
+/* News validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *news_valid (char *name)
+{
+ int fd;
+ char *s,*t,*u;
+ struct stat sbuf;
+ if ((name[0] == '#') && (name[1] == 'n') && (name[2] == 'e') &&
+ (name[3] == 'w') && (name[4] == 's') && (name[5] == '.') &&
+ !strchr (name,'/') &&
+ !stat ((char *) mail_parameters (NIL,GET_NEWSSPOOL,NIL),&sbuf) &&
+ ((fd = open ((char *) mail_parameters (NIL,GET_NEWSACTIVE,NIL),O_RDONLY,
+ NIL)) >= 0)) {
+ fstat (fd,&sbuf); /* get size of active file */
+ /* slurp in active file */
+ read (fd,t = s = (char *) fs_get (sbuf.st_size+1),sbuf.st_size);
+ s[sbuf.st_size] = '\0'; /* tie off file */
+ close (fd); /* flush file */
+ while (*t && (u = strchr (t,' '))) {
+ *u++ = '\0'; /* tie off at end of name */
+ if (!strcmp (name+6,t)) {
+ fs_give ((void **) &s); /* flush data */
+ return &newsdriver;
+ }
+ t = 1 + strchr (u,'\n'); /* next line */
+ }
+ fs_give ((void **) &s); /* flush data */
+ }
+ return NIL; /* return status */
+}
+
+/* News manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *news_parameters (long function,void *value)
+{
+ return (function == GET_NEWSRC) ? env_parameters (function,value) : NIL;
+}
+
+
+/* News scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void news_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ char tmp[MAILTMPLEN];
+ if (news_canonicalize (ref,pat,tmp))
+ mm_log ("Scan not valid for news mailboxes",ERROR);
+}
+
+/* News find list of newsgroups
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void news_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ int fd;
+ int i;
+ char *s,*t,*u,*r,pattern[MAILTMPLEN],name[MAILTMPLEN];
+ struct stat sbuf;
+ if (!pat || !*pat) { /* empty pattern? */
+ if (news_canonicalize (ref,"*",pattern)) {
+ /* tie off name at root */
+ if (s = strchr (pattern,'.')) *++s = '\0';
+ else pattern[0] = '\0';
+ mm_list (stream,'.',pattern,LATT_NOSELECT);
+ }
+ }
+ else if (news_canonicalize (ref,pat,pattern) &&
+ !stat ((char *) mail_parameters (NIL,GET_NEWSSPOOL,NIL),&sbuf) &&
+ ((fd = open ((char *) mail_parameters (NIL,GET_NEWSACTIVE,NIL),
+ O_RDONLY,NIL)) >= 0)) {
+ fstat (fd,&sbuf); /* get file size and read data */
+ read (fd,s = (char *) fs_get (sbuf.st_size + 1),sbuf.st_size);
+ close (fd); /* close file */
+ s[sbuf.st_size] = '\0'; /* tie off string */
+ strcpy (name,"#news."); /* write initial prefix */
+ i = strlen (pattern); /* length of pattern */
+ if (pattern[--i] != '%') i = 0;
+ if (t = strtok_r (s,"\n",&r)) do if (u = strchr (t,' ')) {
+ *u = '\0'; /* tie off at end of name */
+ strcpy (name + 6,t); /* make full form of name */
+ if (pmatch_full (name,pattern,'.')) mm_list (stream,'.',name,NIL);
+ else if (i && (u = strchr (name + i,'.'))) {
+ *u = '\0'; /* tie off at delimiter, see if matches */
+ if (pmatch_full (name,pattern,'.'))
+ mm_list (stream,'.',name,LATT_NOSELECT);
+ }
+ } while (t = strtok_r (NIL,"\n",&r));
+ fs_give ((void **) &s);
+ }
+}
+
+/* News find list of subscribed newsgroups
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void news_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ char pattern[MAILTMPLEN];
+ /* return data from newsrc */
+ if (news_canonicalize (ref,pat,pattern)) newsrc_lsub (stream,pattern);
+}
+
+
+/* News canonicalize newsgroup name
+ * Accepts: reference
+ * pattern
+ * returned single pattern
+ * Returns: T on success, NIL on failure
+ */
+
+long news_canonicalize (char *ref,char *pat,char *pattern)
+{
+ unsigned long i;
+ char *s;
+ if (ref && *ref) { /* have a reference */
+ strcpy (pattern,ref); /* copy reference to pattern */
+ /* # overrides mailbox field in reference */
+ if (*pat == '#') strcpy (pattern,pat);
+ /* pattern starts, reference ends, with . */
+ else if ((*pat == '.') && (pattern[strlen (pattern) - 1] == '.'))
+ strcat (pattern,pat + 1); /* append, omitting one of the period */
+ else strcat (pattern,pat); /* anything else is just appended */
+ }
+ else strcpy (pattern,pat); /* just have basic name */
+ if ((pattern[0] == '#') && (pattern[1] == 'n') && (pattern[2] == 'e') &&
+ (pattern[3] == 'w') && (pattern[4] == 's') && (pattern[5] == '.') &&
+ !strchr (pattern,'/')) { /* count wildcards */
+ for (i = 0, s = pattern; *s; *s++) if ((*s == '*') || (*s == '%')) ++i;
+ /* success if not too many */
+ if (i <= MAXWILDCARDS) return LONGT;
+ MM_LOG ("Excessive wildcards in LIST/LSUB",ERROR);
+ }
+ return NIL;
+}
+
+/* News subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long news_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return news_valid (mailbox) ? newsrc_update (stream,mailbox+6,':') : NIL;
+}
+
+
+/* NEWS unsubscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to delete from subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long news_unsubscribe (MAILSTREAM *stream,char *mailbox)
+{
+ return news_valid (mailbox) ? newsrc_update (stream,mailbox+6,'!') : NIL;
+}
+
+/* News create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long news_create (MAILSTREAM *stream,char *mailbox)
+{
+ return NIL; /* never valid for News */
+}
+
+
+/* News delete mailbox
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long news_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return NIL; /* never valid for News */
+}
+
+
+/* News rename mailbox
+ * Accepts: mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long news_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ return NIL; /* never valid for News */
+}
+
+/* News open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *news_open (MAILSTREAM *stream)
+{
+ long i,nmsgs;
+ char *s,tmp[MAILTMPLEN];
+ struct direct **names = NIL;
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &newsproto;
+ if (stream->local) fatal ("news recycle stream");
+ /* build directory name */
+ sprintf (s = tmp,"%s/%s",(char *) mail_parameters (NIL,GET_NEWSSPOOL,NIL),
+ stream->mailbox + 6);
+ while (s = strchr (s,'.')) *s = '/';
+ /* scan directory */
+ if ((nmsgs = scandir (tmp,&names,news_select,news_numsort)) >= 0) {
+ mail_exists (stream,nmsgs); /* notify upper level that messages exist */
+ stream->local = fs_get (sizeof (NEWSLOCAL));
+ LOCAL->dirty = NIL; /* no update to .newsrc needed yet */
+ LOCAL->dir = cpystr (tmp); /* copy directory name for later */
+ LOCAL->name = cpystr (stream->mailbox + 6);
+ for (i = 0; i < nmsgs; ++i) {
+ stream->uid_last = mail_elt (stream,i+1)->private.uid =
+ atoi (names[i]->d_name);
+ fs_give ((void **) &names[i]);
+ }
+ s = (void *) names; /* stupid language */
+ fs_give ((void **) &s); /* free directory */
+ LOCAL->cachedtexts = 0; /* no cached texts */
+ stream->sequence++; /* bump sequence number */
+ stream->rdonly = stream->perm_deleted = T;
+ /* UIDs are always valid */
+ stream->uid_validity = 0xbeefface;
+ /* read .newsrc entries */
+ mail_recent (stream,newsrc_read (LOCAL->name,stream));
+ /* notify if empty newsgroup */
+ if (!(stream->nmsgs || stream->silent)) {
+ sprintf (tmp,"Newsgroup %s is empty",LOCAL->name);
+ mm_log (tmp,WARN);
+ }
+ }
+ else mm_log ("Unable to scan newsgroup spool directory",ERROR);
+ return LOCAL ? stream : NIL; /* if stream is alive, return to caller */
+}
+
+/* News file name selection test
+ * Accepts: candidate directory entry
+ * Returns: T to use file name, NIL to skip it
+ */
+
+int news_select (struct direct *name)
+{
+ char c;
+ char *s = name->d_name;
+ while (c = *s++) if (!isdigit (c)) return NIL;
+ return T;
+}
+
+
+/* News file name comparision
+ * Accepts: first candidate directory entry
+ * second candidate directory entry
+ * Returns: negative if d1 < d2, 0 if d1 == d2, postive if d1 > d2
+ */
+
+int news_numsort (const void *d1,const void *d2)
+{
+ return atoi ((*(struct direct **) d1)->d_name) -
+ atoi ((*(struct direct **) d2)->d_name);
+}
+
+
+/* News close
+ * Accepts: MAIL stream
+ * option flags
+ */
+
+void news_close (MAILSTREAM *stream,long options)
+{
+ if (LOCAL) { /* only if a file is open */
+ news_check (stream); /* dump final checkpoint */
+ if (LOCAL->dir) fs_give ((void **) &LOCAL->dir);
+ if (LOCAL->name) fs_give ((void **) &LOCAL->name);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* News fetch fast information
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ */
+
+void news_fast (MAILSTREAM *stream,char *sequence,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned long i;
+ /* set up metadata for all messages */
+ if (stream && LOCAL && ((flags & FT_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence &&
+ !(elt->day && elt->rfc822_size)) news_load_message (stream,i,NIL);
+}
+
+
+/* News fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ */
+
+void news_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ unsigned long i;
+ if ((flags & FT_UID) ? /* validate all elts */
+ mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence))
+ for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->valid = T;
+}
+
+/* News load message into cache
+ * Accepts: MAIL stream
+ * message #
+ * option flags
+ */
+
+void news_load_message (MAILSTREAM *stream,unsigned long msgno,long flags)
+{
+ unsigned long i,j,nlseen;
+ int fd;
+ unsigned char c,*t;
+ struct stat sbuf;
+ MESSAGECACHE *elt;
+ FDDATA d;
+ STRING bs;
+ elt = mail_elt (stream,msgno);/* get elt */
+ /* build message file name */
+ sprintf (LOCAL->buf,"%s/%lu",LOCAL->dir,elt->private.uid);
+ /* anything we need not currently cached? */
+ if ((!elt->day || !elt->rfc822_size ||
+ ((flags & NLM_HEADER) && !elt->private.msg.header.text.data) ||
+ ((flags & NLM_TEXT) && !elt->private.msg.text.text.data)) &&
+ ((fd = open (LOCAL->buf,O_RDONLY,NIL)) >= 0)) {
+ fstat (fd,&sbuf); /* get file metadata */
+ d.fd = fd; /* set up file descriptor */
+ d.pos = 0; /* start of file */
+ d.chunk = LOCAL->buf;
+ d.chunksize = CHUNKSIZE;
+ INIT (&bs,fd_string,&d,sbuf.st_size);
+ if (!elt->day) { /* set internaldate to file date */
+ struct tm *tm = gmtime (&sbuf.st_mtime);
+ elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1;
+ elt->year = tm->tm_year + 1900 - BASEYEAR;
+ elt->hours = tm->tm_hour; elt->minutes = tm->tm_min;
+ elt->seconds = tm->tm_sec;
+ elt->zhours = 0; elt->zminutes = 0;
+ }
+
+ if (!elt->rfc822_size) { /* know message size yet? */
+ for (i = 0, j = SIZE (&bs), nlseen = 0; j--; ) switch (SNX (&bs)) {
+ case '\015': /* unlikely carriage return */
+ if (!j || (CHR (&bs) != '\012')) {
+ i++; /* ugh, raw CR */
+ nlseen = NIL;
+ break;
+ }
+ SNX (&bs); /* eat the line feed, drop in */
+ case '\012': /* line feed? */
+ i += 2; /* count a CRLF */
+ /* header size known yet? */
+ if (!elt->private.msg.header.text.size && nlseen) {
+ /* note position in file */
+ elt->private.special.text.size = GETPOS (&bs);
+ /* and CRLF-adjusted size */
+ elt->private.msg.header.text.size = i;
+ }
+ nlseen = T; /* note newline seen */
+ break;
+ default: /* ordinary chararacter */
+ i++;
+ nlseen = NIL;
+ break;
+ }
+ SETPOS (&bs,0); /* restore old position */
+ elt->rfc822_size = i; /* note that we have size now */
+ /* header is entire message if no delimiter */
+ if (!elt->private.msg.header.text.size)
+ elt->private.msg.header.text.size = elt->rfc822_size;
+ /* text is remainder of message */
+ elt->private.msg.text.text.size =
+ elt->rfc822_size - elt->private.msg.header.text.size;
+ }
+
+ /* need to load cache with message data? */
+ if (((flags & NLM_HEADER) && !elt->private.msg.header.text.data) ||
+ ((flags & NLM_TEXT) && !elt->private.msg.text.text.data)) {
+ /* purge cache if too big */
+ if (LOCAL->cachedtexts > max (stream->nmsgs * 4096,2097152)) {
+ /* just can't keep that much */
+ mail_gc (stream,GC_TEXTS);
+ LOCAL->cachedtexts = 0;
+ }
+ if ((flags & NLM_HEADER) && !elt->private.msg.header.text.data) {
+ t = elt->private.msg.header.text.data =
+ (unsigned char *) fs_get (elt->private.msg.header.text.size + 1);
+ LOCAL->cachedtexts += elt->private.msg.header.text.size;
+ /* read in message header */
+ for (i = 0; i <= elt->private.msg.header.text.size; i++)
+ switch (c = SNX (&bs)) {
+ case '\015': /* unlikely carriage return */
+ *t++ = c;
+ if ((CHR (&bs) == '\012')) *t++ = SNX (&bs);
+ break;
+ case '\012': /* line feed? */
+ *t++ = '\015';
+ default:
+ *t++ = c;
+ break;
+ }
+ *t = '\0'; /* tie off string */
+ }
+ if ((flags & NLM_TEXT) && !elt->private.msg.text.text.data) {
+ t = elt->private.msg.text.text.data =
+ (unsigned char *) fs_get (elt->private.msg.text.text.size + 1);
+ SETPOS (&bs,elt->private.msg.header.text.size);
+ LOCAL->cachedtexts += elt->private.msg.text.text.size;
+ /* read in message text */
+ for (i = 0; i <= elt->private.msg.text.text.size; i++)
+ switch (c = SNX (&bs)) {
+ case '\015': /* unlikely carriage return */
+ *t++ = c;
+ if ((CHR (&bs) == '\012')) *t++ = SNX (&bs);
+ break;
+ case '\012': /* line feed? */
+ *t++ = '\015';
+ default:
+ *t++ = c;
+ break;
+ }
+ *t = '\0'; /* tie off string */
+ }
+ }
+ close (fd); /* flush message file */
+ }
+}
+
+/* News fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *news_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags)
+{
+ MESSAGECACHE *elt;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ elt = mail_elt (stream,msgno);/* get elt */
+ if (!elt->private.msg.header.text.data)
+ news_load_message (stream,msgno,NLM_HEADER);
+ *length = elt->private.msg.header.text.size;
+ return (char *) elt->private.msg.header.text.data;
+}
+
+
+/* News fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T on success, NIL on failure
+ */
+
+long news_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mail_elt (stream,msgno);/* get elt */
+ /* snarf message if don't have it yet */
+ if (!elt->private.msg.text.text.data) {
+ news_load_message (stream,msgno,NLM_TEXT);
+ if (!elt->private.msg.text.text.data) return NIL;
+ }
+ if (!(flags & FT_PEEK)) { /* mark as seen */
+ mail_elt (stream,msgno)->seen = T;
+ mm_flags (stream,msgno);
+ }
+ INIT (bs,mail_string,elt->private.msg.text.text.data,
+ elt->private.msg.text.text.size);
+ return T;
+}
+
+/* News per-message modify flag
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void news_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ if (!LOCAL->dirty) { /* only bother checking if not dirty yet */
+ if (elt->valid) { /* if done, see if deleted changed */
+ if (elt->sequence != elt->deleted) LOCAL->dirty = T;
+ elt->sequence = T; /* leave the sequence set */
+ }
+ /* note current setting of deleted flag */
+ else elt->sequence = elt->deleted;
+ }
+}
+
+
+/* News ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long news_ping (MAILSTREAM *stream)
+{
+ return T; /* always alive */
+}
+
+
+/* News check mailbox
+ * Accepts: MAIL stream
+ */
+
+void news_check (MAILSTREAM *stream)
+{
+ /* never do if no updates */
+ if (LOCAL->dirty) newsrc_write (LOCAL->name,stream);
+ LOCAL->dirty = NIL;
+}
+
+
+/* News expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T if success, NIL if failure
+ */
+
+long news_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ if (!stream->silent) mm_log ("Expunge ignored on readonly mailbox",NIL);
+ return LONGT;
+}
+
+/* News copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * option flags
+ * Returns: T if copy successful, else NIL
+ */
+
+long news_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ mm_log ("Copy not valid for News",ERROR);
+ return NIL;
+}
+
+
+/* News append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback function
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long news_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ mm_log ("Append not valid for news",ERROR);
+ return NIL;
+}
diff --git a/imap/src/osdep/unix/nl_unix.c b/imap/src/osdep/unix/nl_unix.c
new file mode 100644
index 00000000..b2d5616a
--- /dev/null
+++ b/imap/src/osdep/unix/nl_unix.c
@@ -0,0 +1,92 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX/VMS newline routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Copy string with CRLF newlines
+ * Accepts: destination string
+ * pointer to size of destination string buffer
+ * source string
+ * length of source string
+ * Returns: length of copied string
+ */
+
+unsigned long strcrlfcpy (unsigned char **dst,unsigned long *dstl,
+ unsigned char *src,unsigned long srcl)
+{
+ long i = srcl * 2,j;
+ unsigned char c,*d = src;
+ if (*dst) { /* candidate destination provided? */
+ /* count NLs if doesn't fit worst-case */
+ if (i > *dstl) for (i = j = srcl; j; --j) if (*d++ == '\012') i++;
+ /* still too small, must reset destination */
+ if (i > *dstl) fs_give ((void **) dst);
+ }
+ /* make a new buffer if needed */
+ if (!*dst) *dst = (char *) fs_get ((*dstl = i) + 1);
+ d = *dst; /* destination string */
+ if (srcl) do { /* main copy loop */
+ if ((c = *src++) < '\016') {
+ /* prepend CR to LF */
+ if (c == '\012') *d++ = '\015';
+ /* unlikely CR */
+ else if ((c == '\015') && (srcl > 1) && (*src == '\012')) {
+ *d++ = c; /* copy the CR */
+ c = *src++; /* grab the LF */
+ --srcl; /* adjust the count */
+ }
+ }
+ *d++ = c; /* copy character */
+ } while (--srcl);
+ *d = '\0'; /* tie off destination */
+ return d - *dst; /* return length */
+}
+
+/* Length of string after strcrlfcpy applied
+ * Accepts: source string
+ * Returns: length of string
+ */
+
+unsigned long strcrlflen (STRING *s)
+{
+ unsigned long pos = GETPOS (s);
+ unsigned long i = SIZE (s);
+ unsigned long j = i;
+ while (j--) switch (SNX (s)) {/* search for newlines */
+ case '\015': /* unlikely carriage return */
+ if (j && (CHR (s) == '\012')) {
+ SNX (s); /* eat the line feed */
+ j--;
+ }
+ break;
+ case '\012': /* line feed? */
+ i++;
+ default: /* ordinary chararacter */
+ break;
+ }
+ SETPOS (s,pos); /* restore old position */
+ return i;
+}
diff --git a/imap/src/osdep/unix/opendir.c b/imap/src/osdep/unix/opendir.c
new file mode 100644
index 00000000..445fd148
--- /dev/null
+++ b/imap/src/osdep/unix/opendir.c
@@ -0,0 +1,79 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Read directories
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 16 December 1993
+ * Last Edited: 30 August 2006
+ */
+
+/* Emulator for BSD opendir() call
+ * Accepts: directory name
+ * Returns: directory structure pointer
+ */
+
+DIR *opendir (char *name)
+{
+ DIR *d = NIL;
+ struct stat sbuf;
+ int fd = open (name,O_RDONLY,NIL);
+ errno = ENOTDIR; /* default error is bogus directory */
+ if ((fd >= 0) && !(fstat (fd,&sbuf)) && ((sbuf.st_mode&S_IFMT) == S_IFDIR)) {
+ d = (DIR *) fs_get (sizeof (DIR));
+ /* initialize structure */
+ d->dd_loc = 0;
+ read (fd,d->dd_buf = (char *) fs_get (sbuf.st_size),
+ d->dd_size = sbuf.st_size);
+ }
+ else if (d) fs_give ((void **) &d);
+ if (fd >= 0) close (fd);
+ return d;
+}
+
+
+/* Emulator for BSD closedir() call
+ * Accepts: directory structure pointer
+ */
+
+int closedir (DIR *d)
+{
+ /* free storage */
+ fs_give ((void **) &(d->dd_buf));
+ fs_give ((void **) &d);
+ return NIL; /* return */
+}
+
+
+/* Emulator for BSD readdir() call
+ * Accepts: directory structure pointer
+ */
+
+struct direct *readdir (DIR *d)
+{
+ /* loop through directory */
+ while (d->dd_loc < d->dd_size) {
+ struct direct *dp = (struct direct *) (d->dd_buf + d->dd_loc);
+ d->dd_loc += sizeof (struct direct);
+ if (dp->d_ino) return dp; /* if have a good entry return it */
+ }
+ return NIL; /* all done */
+}
diff --git a/imap/src/osdep/unix/os_a32.c b/imap/src/osdep/unix/os_a32.c
new file mode 100644
index 00000000..513e92c7
--- /dev/null
+++ b/imap/src/osdep/unix/os_a32.c
@@ -0,0 +1,60 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- AIX 3.2 on RS 6000
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+#include <sys/select.h>
+
+char *crypt (char *key,char *salt);
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "tz_sv4.c"
+#include "flocksim.c"
+#include "utime.c"
diff --git a/imap/src/osdep/unix/os_a32.h b/imap/src/osdep/unix/os_a32.h
new file mode 100644
index 00000000..40dd6fd4
--- /dev/null
+++ b/imap/src/osdep/unix/os_a32.h
@@ -0,0 +1,50 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- AIX on RS6000
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h> /* for struct tm */
+#include <dirent.h>
+#include <fcntl.h>
+#include <utime.h>
+#include <syslog.h>
+#include <sys/file.h>
+#include <ustat.h>
+
+
+#define utime portable_utime
+int portable_utime (char *file,time_t timep[2]);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_a41.c b/imap/src/osdep/unix/os_a41.c
new file mode 100644
index 00000000..ecaff582
--- /dev/null
+++ b/imap/src/osdep/unix/os_a41.c
@@ -0,0 +1,61 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- AIX 4.1 on RS 6000
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+#include <sys/select.h>
+#include <stddef.h> /* needed for authenticate() */
+
+int authenticate (char *UserName,char *Response,int *Reenter,char **Message);
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "tz_sv4.c"
+#include "flocksim.c"
+#include "utime.c"
diff --git a/imap/src/osdep/unix/os_a41.h b/imap/src/osdep/unix/os_a41.h
new file mode 100644
index 00000000..40dd6fd4
--- /dev/null
+++ b/imap/src/osdep/unix/os_a41.h
@@ -0,0 +1,50 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- AIX on RS6000
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h> /* for struct tm */
+#include <dirent.h>
+#include <fcntl.h>
+#include <utime.h>
+#include <syslog.h>
+#include <sys/file.h>
+#include <ustat.h>
+
+
+#define utime portable_utime
+int portable_utime (char *file,time_t timep[2]);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_a52.c b/imap/src/osdep/unix/os_a52.c
new file mode 100644
index 00000000..fed6af78
--- /dev/null
+++ b/imap/src/osdep/unix/os_a52.c
@@ -0,0 +1,63 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- AIX 4.1 on RS 6000
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+#include <sys/select.h>
+#include <stddef.h> /* needed for authenticate() */
+
+int authenticate (char *UserName,char *Response,int *Reenter,char **Message);
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "tz_sv4.c"
+#include "flocksim.c"
+#undef setpgrp
+#include "setpgrp.c"
+#include "utime.c"
diff --git a/imap/src/osdep/unix/os_a52.h b/imap/src/osdep/unix/os_a52.h
new file mode 100644
index 00000000..a8261746
--- /dev/null
+++ b/imap/src/osdep/unix/os_a52.h
@@ -0,0 +1,53 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- AIX on RS6000
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h> /* for struct tm */
+#include <dirent.h>
+#include <fcntl.h>
+#include <utime.h>
+#include <syslog.h>
+#include <sys/file.h>
+#include <ustat.h>
+
+
+#define setpgrp(a,b) Setpgrp(a,b)
+int Setpgrp (int pid,int gid);
+
+#define utime portable_utime
+int portable_utime (char *file,time_t timep[2]);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_aix.c b/imap/src/osdep/unix/os_aix.c
new file mode 100644
index 00000000..c38e5cf9
--- /dev/null
+++ b/imap/src/osdep/unix/os_aix.c
@@ -0,0 +1,64 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- AIX version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+char *crypt (char *key,char *salt);
+
+extern long timezone;
+extern int daylight;
+extern char *tzname[2];
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "memmove.c"
+#include "strerror.c"
+#include "tz_sv4.c"
diff --git a/imap/src/osdep/unix/os_aix.h b/imap/src/osdep/unix/os_aix.h
new file mode 100644
index 00000000..6c2d4340
--- /dev/null
+++ b/imap/src/osdep/unix/os_aix.h
@@ -0,0 +1,47 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- AIX version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+
+#define direct dirent
+
+char *strerror (int n);
+void *memmove (void *s,void *ct,size_t n);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_aos.c b/imap/src/osdep/unix/os_aos.c
new file mode 100644
index 00000000..a916f47b
--- /dev/null
+++ b/imap/src/osdep/unix/os_aos.c
@@ -0,0 +1,63 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- 4.3BSD version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ *
+ * Date: 11 May 1989
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+#define toint(c) ((c)-'0')
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_wait.c"
+#include "memmove.c"
+#include "strerror.c"
+#include "strstr.c"
+#include "strtoul.c"
+#include "tz_bsd.c"
diff --git a/imap/src/osdep/unix/os_aos.h b/imap/src/osdep/unix/os_aos.h
new file mode 100644
index 00000000..b604a475
--- /dev/null
+++ b/imap/src/osdep/unix/os_aos.h
@@ -0,0 +1,50 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- AOS version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+
+char *getenv (char *name);
+char *strstr (char *cs,char *ct);
+char *strerror (int n);
+void *memmove (void *s,void *ct,size_t n);
+unsigned long strtoul (char *s,char **endp,int base);
+void *malloc (size_t byteSize);
+void free (void *ptr);
+void *realloc (void *oldptr,size_t newsize);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_art.c b/imap/src/osdep/unix/os_art.c
new file mode 100644
index 00000000..8fb78028
--- /dev/null
+++ b/imap/src/osdep/unix/os_art.c
@@ -0,0 +1,86 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- AIX/RT version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 April 1992
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <ctype.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+extern int errno;
+#include <pwd.h>
+#include <sys/socket.h>
+#include <time.h>
+#define KERNEL
+#include <sys/time.h>
+#undef KERNEL
+#include "misc.h"
+
+#define DIR_SIZE(d) sizeof (DIR)
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+#define toint(c) ((c)-'0')
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+
+#define NBBY 8 /* number of bits in a byte */
+#define FD_SETSIZE 256
+
+typedef long fd_mask;
+#define NFDBITS (sizeof(fd_mask) * NBBY)
+ /* bits per mask */
+#define howmany(x, y) (((x)+((y)-1))/(y))
+
+typedef struct fd_set {
+ fd_mask fds_bits[howmany(FD_SETSIZE, NFDBITS)];
+} fd_set;
+
+#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= \
+ (1 << ((n) % NFDBITS)))
+#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= \
+ ~(1 << ((n) % NFDBITS)))
+#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & \
+ (1 << ((n) % NFDBITS)))
+#define FD_ZERO(p) bzero((char *)(p), sizeof(*(p)))
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_wait.c"
+#include "flocksim.c"
+#include "memmove2.c"
+#include "strerror.c"
+#include "tz_sv4.c"
diff --git a/imap/src/osdep/unix/os_art.h b/imap/src/osdep/unix/os_art.h
new file mode 100644
index 00000000..9bfd6f86
--- /dev/null
+++ b/imap/src/osdep/unix/os_art.h
@@ -0,0 +1,81 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- AIX/RT version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 April 1992
+ * Last Edited: 15 September 2006
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <memory.h>
+#include <sys/types.h>
+#define direct dirent
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/syslog.h>
+#include <sys/file.h>
+#include <ustat.h>
+
+
+/* Different names between BSD and SVR4 */
+
+#define L_SET SEEK_SET
+#define L_INCR SEEK_CUR
+#define L_XTND SEEK_END
+
+#define random lrand48
+
+#define SIGSTOP SIGQUIT
+
+
+/* For setitimer() emulation */
+
+#define ITIMER_REAL 0
+
+struct passwd *getpwent (void);
+struct passwd *getpwuid (int uid);
+struct passwd *getpwnam (char *name);
+char *getenv (char *name);
+long gethostid (void);
+void *memmove (void *s,void *ct,size_t n);
+char *strstr (char *cs,char *ct);
+char *strerror (int n);
+unsigned long strtoul (char *s,char **endp,int base);
+typedef int (*select_t) (struct direct *name);
+typedef int (*compar_t) (void *d1,void *d2);
+int scandir (char *dirname,struct direct ***namelist,select_t select,
+ compar_t compar);
+int alphasort (void *d1,void *d2);
+void *malloc (size_t byteSize);
+void free (void *ptr);
+void *realloc (void *oldptr,size_t newsize);
+int openlog (ident,logopt,facility);
+int syslog (priority,message,parameters ...);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_asv.c b/imap/src/osdep/unix/os_asv.c
new file mode 100644
index 00000000..bf943b3e
--- /dev/null
+++ b/imap/src/osdep/unix/os_asv.c
@@ -0,0 +1,70 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Altos System V version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 April 1992
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <ctype.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/tiuser.h>
+#include <sys/stropts.h>
+#include <sys/poll.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+#include <pwd.h>
+#include <sys/socket.h>
+#include "misc.h"
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+#define toint(c) ((c)-'0')
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+
+#define DIR_SIZE(d) d->d_reclen
+
+#define pid_t short /* may not be known on all ASV systems */
+
+#include "strstr.c"
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "strerror.c"
+#include "flocksim.c"
+#include "scandir.c"
+#include "strtoul.c"
+#include "tz_sv4.c"
+#include "gethstid.c"
+#include "memmove.c"
+#include "fsync.c"
diff --git a/imap/src/osdep/unix/os_asv.h b/imap/src/osdep/unix/os_asv.h
new file mode 100644
index 00000000..f92f210b
--- /dev/null
+++ b/imap/src/osdep/unix/os_asv.h
@@ -0,0 +1,76 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Altos System V version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 April 1992
+ * Last Edited: 15 September 2006
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/time.h>
+#include <syslog.h>
+#include <sys/file.h>
+#include <ustat.h>
+
+
+/* Different names, equivalent things in BSD and SysV */
+
+#define L_SET SEEK_SET
+#define L_INCR SEEK_CUR
+#define L_XTND SEEK_END
+
+#define direct dirent
+
+#define ftruncate chsize
+#define random lrand48
+
+
+struct passwd *getpwent (void);
+struct passwd *getpwuid (int uid);
+struct passwd *getpwnam (char *name);
+char *getenv (char *name);
+long gethostid (void);
+void *memmove (void *s,void *ct,size_t n);
+char *strstr (char *cs,char *ct);
+char *strerror (int n);
+unsigned long strtoul (char *s,char **endp,int base);
+typedef int (*select_t) (struct direct *name);
+typedef int (*compar_t) (void *d1,void *d2);
+int scandir (char *dirname,struct direct ***namelist,select_t select,
+ compar_t compar);
+int alphasort (void *d1,void *d2);
+void *malloc (size_t byteSize);
+void free (void *ptr);
+void *realloc (void *oldptr,size_t newsize);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_aux.c b/imap/src/osdep/unix/os_aux.c
new file mode 100644
index 00000000..ec5f666b
--- /dev/null
+++ b/imap/src/osdep/unix/os_aux.c
@@ -0,0 +1,62 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- A/UX version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <ctype.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+#include <pwd.h>
+#include "misc.h"
+
+
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+#define toint(c) ((c)-'0')
+
+extern char *sys_errlist[];
+extern int sys_nerr;
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_wait.c"
+#include "flocksim.c"
+#include "strerror.c"
+#include "strtoul.c"
+#include "strpbrk.c" /* the A/UX version is bogus! */
+#include "memmove.c"
+#include "tz_sv4.c"
diff --git a/imap/src/osdep/unix/os_aux.h b/imap/src/osdep/unix/os_aux.h
new file mode 100644
index 00000000..53ec6cc5
--- /dev/null
+++ b/imap/src/osdep/unix/os_aux.h
@@ -0,0 +1,51 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- A/UX version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <time.h>
+#include <sys/file.h>
+#include <fcntl.h>
+#include <syslog.h>
+
+
+extern int errno;
+
+char *strerror (int n);
+unsigned long strtoul (char *s,char **endp,int base);
+void *memmove (void *s,void *ct,size_t n);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_bsd.c b/imap/src/osdep/unix/os_bsd.c
new file mode 100644
index 00000000..a916f47b
--- /dev/null
+++ b/imap/src/osdep/unix/os_bsd.c
@@ -0,0 +1,63 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- 4.3BSD version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ *
+ * Date: 11 May 1989
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+#define toint(c) ((c)-'0')
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_wait.c"
+#include "memmove.c"
+#include "strerror.c"
+#include "strstr.c"
+#include "strtoul.c"
+#include "tz_bsd.c"
diff --git a/imap/src/osdep/unix/os_bsd.h b/imap/src/osdep/unix/os_bsd.h
new file mode 100644
index 00000000..851434af
--- /dev/null
+++ b/imap/src/osdep/unix/os_bsd.h
@@ -0,0 +1,51 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- 4.3BSD version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+#include <machine/endian.h> /* needed for htons() prototypes */
+
+
+char *getenv (char *name);
+char *strstr (char *cs,char *ct);
+char *strerror (int n);
+void *memmove (void *s,void *ct,size_t n);
+unsigned long strtoul (char *s,char **endp,int base);
+void *malloc (size_t byteSize);
+void free (void *ptr);
+void *realloc (void *oldptr,size_t newsize);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_bsf.c b/imap/src/osdep/unix/os_bsf.c
new file mode 100644
index 00000000..afe26e23
--- /dev/null
+++ b/imap/src/osdep/unix/os_bsf.c
@@ -0,0 +1,54 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- BSDI BSD/386 version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "getspnam.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "tz_bsd.c"
diff --git a/imap/src/osdep/unix/os_bsf.h b/imap/src/osdep/unix/os_bsf.h
new file mode 100644
index 00000000..af18d567
--- /dev/null
+++ b/imap/src/osdep/unix/os_bsf.h
@@ -0,0 +1,46 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- FreeBSD version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 5 March 1993
+ * Last Edited: 30 August 2006
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+
+#define direct dirent
+
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_bsi.c b/imap/src/osdep/unix/os_bsi.c
new file mode 100644
index 00000000..afe26e23
--- /dev/null
+++ b/imap/src/osdep/unix/os_bsi.c
@@ -0,0 +1,54 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- BSDI BSD/386 version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "getspnam.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "tz_bsd.c"
diff --git a/imap/src/osdep/unix/os_bsi.h b/imap/src/osdep/unix/os_bsi.h
new file mode 100644
index 00000000..15f371a3
--- /dev/null
+++ b/imap/src/osdep/unix/os_bsi.h
@@ -0,0 +1,43 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- BSDI BSD/386 version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 5 March 1993
+ * Last Edited: 30 August 2006
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_cvx.c b/imap/src/osdep/unix/os_cvx.c
new file mode 100644
index 00000000..10f01904
--- /dev/null
+++ b/imap/src/osdep/unix/os_cvx.c
@@ -0,0 +1,57 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Convex version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ *
+ * Date: 11 May 1989
+ * Last Edited: 16 August 2007
+ */
+
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+#define toint(c) ((c)-'0')
+
+#include <stdio.h>
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_wait.c"
+#include "tz_nul.c"
diff --git a/imap/src/osdep/unix/os_cvx.h b/imap/src/osdep/unix/os_cvx.h
new file mode 100644
index 00000000..cdb0e92f
--- /dev/null
+++ b/imap/src/osdep/unix/os_cvx.h
@@ -0,0 +1,45 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Convex version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <sys/timeb.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+
+void *malloc (size_t byteSize);
+void *realloc (void *oldptr,size_t newsize);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_cyg.c b/imap/src/osdep/unix/os_cyg.c
new file mode 100644
index 00000000..c9cd2f70
--- /dev/null
+++ b/imap/src/osdep/unix/os_cyg.c
@@ -0,0 +1,71 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Cygwin version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include <crypt.h>
+#include "misc.h"
+
+
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+#define toint(c) ((c)-'0')
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_wait.c"
+#include "tz_nul.c"
+#include "flockcyg.c"
+#include "gethstid.c"
+
+
+/* Emulator for geteuid() call
+ * Returns: effective UID
+ */
+
+#undef geteuid
+
+uid_t Geteuid (void)
+{
+ uid_t ret = geteuid ();
+ return (ret == SYSTEMUID) ? 0 : ret;
+}
diff --git a/imap/src/osdep/unix/os_cyg.h b/imap/src/osdep/unix/os_cyg.h
new file mode 100644
index 00000000..061db332
--- /dev/null
+++ b/imap/src/osdep/unix/os_cyg.h
@@ -0,0 +1,71 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Cygwin version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+#include <time.h>
+#include <sys/time.h>
+
+#define direct dirent
+
+
+#define CYGKLUDGEOFFSET 1 /* don't write 1st byte of shared-lock files */
+
+/* Cygwin gets this wrong */
+
+#define setpgrp setpgid
+
+#define SYSTEMUID 18 /* Cygwin returns this for SYSTEM */
+#define geteuid Geteuid
+uid_t Geteuid (void);
+
+/* Now Cygwin has reportedly joined this madness. Use ifndef in case it shares
+ the SVR4 <sys/file.h> silliness too */
+#ifndef L_SET
+#define L_SET SEEK_SET
+#endif
+#ifndef L_INCR
+#define L_INCR SEEK_CUR
+#endif
+#ifndef L_XTND
+#define L_XTND SEEK_END
+#endif
+
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flockcyg.h"
diff --git a/imap/src/osdep/unix/os_d-g.c b/imap/src/osdep/unix/os_d-g.c
new file mode 100644
index 00000000..b34662f7
--- /dev/null
+++ b/imap/src/osdep/unix/os_d-g.c
@@ -0,0 +1,54 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- D-G version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "tz_sv4.c"
+#include "flocksim.c"
+#include "utime.c"
diff --git a/imap/src/osdep/unix/os_d-g.h b/imap/src/osdep/unix/os_d-g.h
new file mode 100644
index 00000000..3d308038
--- /dev/null
+++ b/imap/src/osdep/unix/os_d-g.h
@@ -0,0 +1,56 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- D-G version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/dir.h>
+#include <time.h> /* for struct tm */
+#include <fcntl.h>
+#define _USEC_UTIME_FLAVOR /* break it for compatibility with */
+#include <utime.h> /* the incompatible past */
+#include <syslog.h>
+#include <sys/file.h>
+
+
+/* D-G gets this wrong */
+
+#define setpgrp setpgrp2
+
+
+#define utime portable_utime
+int portable_utime (char *file,time_t timep[2]);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_do4.c b/imap/src/osdep/unix/os_do4.c
new file mode 100644
index 00000000..186fd94e
--- /dev/null
+++ b/imap/src/osdep/unix/os_do4.c
@@ -0,0 +1,58 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Apollo Domain/OS sr10.4
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ *
+ * Date: 11 May 1989
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+#define toint(c) ((c)-'0')
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "tz_sv4.c"
diff --git a/imap/src/osdep/unix/os_do4.h b/imap/src/osdep/unix/os_do4.h
new file mode 100644
index 00000000..196061c9
--- /dev/null
+++ b/imap/src/osdep/unix/os_do4.h
@@ -0,0 +1,47 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Apollo Domain/OS sr10.4
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <sys/dir.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+#ifndef htons
+#include <netinet/in.h> /* needed for htons() prototypes */
+#endif
+
+extern int daylight; /* local timzone uses daylight savings time */
+extern long altzone; /* seconds west of UTC during daylight time */
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_drs.c b/imap/src/osdep/unix/os_drs.c
new file mode 100644
index 00000000..74386af9
--- /dev/null
+++ b/imap/src/osdep/unix/os_drs.c
@@ -0,0 +1,57 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- ICL DRS/NX
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+char *crypt (char *key,char *salt);
+
+#define DIR_SIZE(d) d->d_reclen
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "tz_sv4.c"
+#include "flocksim.c"
+#include "scandir.c"
diff --git a/imap/src/osdep/unix/os_drs.h b/imap/src/osdep/unix/os_drs.h
new file mode 100644
index 00000000..d90755ee
--- /dev/null
+++ b/imap/src/osdep/unix/os_drs.h
@@ -0,0 +1,59 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- ICL DRS/NX version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 15 September 2006
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <time.h> /* for struct tm */
+#include <dirent.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+#include <ustat.h>
+
+
+#define random rand
+#define direct dirent
+
+
+long gethostid (void);
+typedef int (*select_t) (struct direct *name);
+typedef int (*compar_t) (void *d1,void *d2);
+int scandir (char *dirname,struct direct ***namelist,select_t select,
+ compar_t compar);
+int alphasort (void *d1,void *d2);
+
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_dyn.c b/imap/src/osdep/unix/os_dyn.c
new file mode 100644
index 00000000..98a1a548
--- /dev/null
+++ b/imap/src/osdep/unix/os_dyn.c
@@ -0,0 +1,64 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Dynix version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ *
+ * Date: 11 May 1989
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <ctype.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+#define toint(c) ((c)-'0')
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_wait.c"
+#include "memmove.c"
+#include "strerror.c"
+#include "strpbrk.c"
+#include "strstr.c"
+#include "strtoul.c"
+#include "strtok.c"
+#include "tz_nul.c"
diff --git a/imap/src/osdep/unix/os_dyn.h b/imap/src/osdep/unix/os_dyn.h
new file mode 100644
index 00000000..e5426498
--- /dev/null
+++ b/imap/src/osdep/unix/os_dyn.h
@@ -0,0 +1,61 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Dynix version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 January 2007
+ */
+
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+
+typedef unsigned long size_t;
+
+char *strtok (char *s,char *ct);
+char *strtok_r (char *s,char *ct,char **r);
+char *strstr (char *cs,char *ct);
+char *strpbrk (char *cs,char *ct);
+char *strerror (int n);
+void *memmove (void *s,void *ct,size_t n);
+void *memset (void *s,int c,size_t n);
+unsigned long strtoul (char *s,char **endp,int base);
+void *malloc (size_t byteSize);
+void free (void *ptr);
+void *realloc (void *oldptr,size_t newsize);
+
+int errno;
+
+#define memcpy memmove
+#define strchr index
+#define strrchr rindex
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_hpp.c b/imap/src/osdep/unix/os_hpp.c
new file mode 100644
index 00000000..39794973
--- /dev/null
+++ b/imap/src/osdep/unix/os_hpp.c
@@ -0,0 +1,77 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- HP/UX version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 16 August 2007
+ */
+
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+#define toint(c) ((c)-'0')
+
+#include <stdio.h>
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/utsname.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+extern char *sys_errlist[];
+extern int sys_nerr;
+#include <pwd.h>
+#include "misc.h"
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "flocksim.c"
+#include "tz_sv4.c"
+#undef setpgrp
+#include "setpgrp.c"
+#include "utime.c"
+
+/* Emulator for BSD gethostid() call
+ * Returns: a unique identifier for the system.
+ * Even though HP/UX has an undocumented gethostid() system call,
+ * it does not work (at least for non-privileged users).
+ */
+
+long gethostid (void)
+{
+ struct utsname udata;
+ return (uname (&udata)) ? 0xfeedface : atol (udata.__idnumber);
+}
diff --git a/imap/src/osdep/unix/os_hpp.h b/imap/src/osdep/unix/os_hpp.h
new file mode 100644
index 00000000..d51dba53
--- /dev/null
+++ b/imap/src/osdep/unix/os_hpp.h
@@ -0,0 +1,63 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- HP/UX version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 20 December 2006
+ */
+
+#include <string.h>
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <utime.h>
+#include <syslog.h>
+#include <sys/file.h>
+#include <ustat.h>
+
+
+#define direct dirent
+#define random lrand48
+
+
+/* Many versions of SysV get this wrong */
+
+#define setpgrp(a,b) Setpgrp(a,b)
+int Setpgrp (int pid,int gid);
+
+
+#define utime portable_utime
+int portable_utime (char *file,time_t timep[2]);
+
+long gethostid (void);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_isc.c b/imap/src/osdep/unix/os_isc.c
new file mode 100644
index 00000000..1269e69b
--- /dev/null
+++ b/imap/src/osdep/unix/os_isc.c
@@ -0,0 +1,68 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- ISC version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 April 1992
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <ctype.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/tiuser.h>
+#include <sys/stropts.h>
+#include <sys/poll.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+#include <net/errno.h>
+#include <pwd.h>
+#include <shadow.h>
+#include <sys/socket.h>
+#include "misc.h"
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+#define toint(c) ((c)-'0')
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+
+#define DIR_SIZE(d) d->d_reclen
+
+#define pid_t short /* may not be known on all ISC systems */
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "strerror.c"
+#include "flocksim.c"
+#include "scandir.c"
+#include "tz_sv4.c"
+#include "fsync.c"
diff --git a/imap/src/osdep/unix/os_isc.h b/imap/src/osdep/unix/os_isc.h
new file mode 100644
index 00000000..1e7c22eb
--- /dev/null
+++ b/imap/src/osdep/unix/os_isc.h
@@ -0,0 +1,70 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- ISC version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 April 1992
+ * Last Edited: 15 September 2006
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/bsdtypes.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <syslog.h>
+#include <sys/file.h>
+#include <ustat.h>
+
+
+/* Different names, equivalent things in BSD and SysV */
+
+/* L_SET is defined for some strange reason in <sys/file.h> on SVR4. */
+#ifndef L_SET
+#define L_SET SEEK_SET
+#endif
+#define L_INCR SEEK_CUR
+#define L_XTND SEEK_END
+
+#define direct dirent
+
+#define ftruncate chsize
+#define random lrand48
+
+long gethostid (void);
+void *memmove (void *s,void *ct,size_t n);
+typedef int (*select_t) (struct direct *name);
+typedef int (*compar_t) (void *d1,void *d2);
+int scandir (char *dirname,struct direct ***namelist,select_t select,
+ compar_t compar);
+int alphasort (void *d1,void *d2);
+
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_lnx.c b/imap/src/osdep/unix/os_lnx.c
new file mode 100644
index 00000000..03fd17d9
--- /dev/null
+++ b/imap/src/osdep/unix/os_lnx.c
@@ -0,0 +1,54 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- old Linux version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1993
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "tz_sv4.c"
+#include "flocklnx.c"
diff --git a/imap/src/osdep/unix/os_lnx.h b/imap/src/osdep/unix/os_lnx.h
new file mode 100644
index 00000000..b5f39ffa
--- /dev/null
+++ b/imap/src/osdep/unix/os_lnx.h
@@ -0,0 +1,67 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Linux version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 September 1993
+ * Last Edited: 30 August 2006
+ */
+
+/*
+ *** These lines are claimed to be necessary to build on Debian Linux on an
+ *** Alpha.
+ */
+
+#ifndef _XOPEN_SOURCE
+#define _XOPEN_SOURCE 1
+#endif /* _XOPEN_SOURCE */
+#ifndef _BSD_SOURCE
+#define _BSD_SOURCE 1
+#endif /* _BSD_SOURCE */
+
+/* end Debian Linux on Alpha strangeness */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <time.h> /* for struct tm */
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+
+/* Linux gets this wrong */
+
+#define setpgrp setpgid
+
+#define direct dirent
+
+#define flock safe_flock
+
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_lyn.c b/imap/src/osdep/unix/os_lyn.c
new file mode 100644
index 00000000..49871251
--- /dev/null
+++ b/imap/src/osdep/unix/os_lyn.c
@@ -0,0 +1,54 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- LynxOS version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+char *crypt (char *key,char *salt);
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "tz_nul.c"
diff --git a/imap/src/osdep/unix/os_lyn.h b/imap/src/osdep/unix/os_lyn.h
new file mode 100644
index 00000000..bba4a3d6
--- /dev/null
+++ b/imap/src/osdep/unix/os_lyn.h
@@ -0,0 +1,44 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- LynxOS version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 5 March 1993
+ * Last Edited: 30 August 2006
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+#define gethostid clock
+
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_mct.c b/imap/src/osdep/unix/os_mct.c
new file mode 100644
index 00000000..cb62de50
--- /dev/null
+++ b/imap/src/osdep/unix/os_mct.c
@@ -0,0 +1,52 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- MachTen version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 7 December 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_wait4.c"
+#include "tz_bsd.c"
diff --git a/imap/src/osdep/unix/os_mct.h b/imap/src/osdep/unix/os_mct.h
new file mode 100644
index 00000000..f4b98d5e
--- /dev/null
+++ b/imap/src/osdep/unix/os_mct.h
@@ -0,0 +1,44 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- MachTen version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 7 December 2006
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+#define unix 1
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_mnt.c b/imap/src/osdep/unix/os_mnt.c
new file mode 100644
index 00000000..f19a487b
--- /dev/null
+++ b/imap/src/osdep/unix/os_mnt.c
@@ -0,0 +1,53 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Mint version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "tz_nul.c"
diff --git a/imap/src/osdep/unix/os_mnt.h b/imap/src/osdep/unix/os_mnt.h
new file mode 100644
index 00000000..4519f8b7
--- /dev/null
+++ b/imap/src/osdep/unix/os_mnt.h
@@ -0,0 +1,51 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Mint version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 September 1993
+ * Last Edited: 30 August 2006
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+#include <time.h>
+#include <portlib.h>
+
+#define EAGAIN EWOULDBLOCK
+#define FNDELAY O_NDELAY
+
+/* MiNT gets this wrong */
+
+#define setpgrp setpgid
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_nto.c b/imap/src/osdep/unix/os_nto.c
new file mode 100644
index 00000000..36c45b42
--- /dev/null
+++ b/imap/src/osdep/unix/os_nto.c
@@ -0,0 +1,76 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- QNX Neutrino RTP version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1993
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include <shadow.h>
+#include <sys/select.h>
+#include "misc.h"
+
+#define DIR_SIZE(d) d->d_reclen
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_wait.c"
+#include "tz_sv4.c"
+#include "gethstid.c"
+#include "flocksim.c"
+#include "utime.c"
+
+/* QNX local readdir()
+ * Accepts: directory structure
+ * Returns: direct struct or NIL if failed
+ */
+
+#undef readdir
+
+struct direct *Readdir (DIR *dirp)
+{
+ static struct direct dc;
+ struct dirent *de = readdir (dirp);
+ if (!de) return NIL; /* end of data */
+ dc.d_fileno = 0; /* could get from de->stat.st_ino */
+ dc.d_namlen = strlen (strcpy (dc.d_name,de->d_name));
+ dc.d_reclen = sizeof (dc);
+ return &dc;
+}
diff --git a/imap/src/osdep/unix/os_nto.h b/imap/src/osdep/unix/os_nto.h
new file mode 100644
index 00000000..12ea809a
--- /dev/null
+++ b/imap/src/osdep/unix/os_nto.h
@@ -0,0 +1,75 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- QNX Neutrino RTP version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 September 1993
+ * Last Edited: 30 August 2006
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/dir.h>
+#include <sys/types.h>
+#include <time.h>
+#include <utime.h>
+
+
+/* QNX gets these wrong */
+
+#define setpgrp setpgid
+#define readdir Readdir
+#define FNDELAY O_NONBLOCK
+#define d_ino d_fileno
+
+
+/* Different names, equivalent things in BSD and SysV */
+
+#ifndef L_SET
+#define L_SET SEEK_SET
+#endif
+#ifndef L_INCR
+#define L_INCR SEEK_CUR
+#endif
+#ifndef L_XTND
+#define L_XTND SEEK_END
+#endif
+
+
+#define utime portable_utime
+int portable_utime (char *file,time_t timep[2]);
+
+long gethostid (void);
+struct direct *Readdir (DIR *dirp);
+typedef int (*select_t) (struct direct *name);
+typedef int (*compar_t) (void *d1,void *d2);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_nxt.c b/imap/src/osdep/unix/os_nxt.c
new file mode 100644
index 00000000..a3b749f5
--- /dev/null
+++ b/imap/src/osdep/unix/os_nxt.c
@@ -0,0 +1,54 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- NeXT version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_wait4.c"
+#include "tz_bsd.c"
+#include "strtok.c"
diff --git a/imap/src/osdep/unix/os_nxt.h b/imap/src/osdep/unix/os_nxt.h
new file mode 100644
index 00000000..2af89168
--- /dev/null
+++ b/imap/src/osdep/unix/os_nxt.h
@@ -0,0 +1,50 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- NeXT version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 January 2007
+ */
+
+#include <libc.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+/* Use ours instead of theirs */
+
+#define strtok STRTOK
+#define strtok_r STRTOK_R
+
+char *strtok (char *s,char *ct);
+char *strtok_r (char *s,char *ct,char **r);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_os4.c b/imap/src/osdep/unix/os_os4.c
new file mode 100644
index 00000000..42d90404
--- /dev/null
+++ b/imap/src/osdep/unix/os_os4.c
@@ -0,0 +1,57 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- OSF/Digital UNIX/Tru64 4
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+#include <sia.h>
+#include <siad.h>
+#include <ustat.h>
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "gr_waitp.c"
+#include "tcp_unix.c"
+#include "tz_bsd.c"
+#undef flock
+#include "flocksim.c"
diff --git a/imap/src/osdep/unix/os_os4.h b/imap/src/osdep/unix/os_os4.h
new file mode 100644
index 00000000..432e5316
--- /dev/null
+++ b/imap/src/osdep/unix/os_os4.h
@@ -0,0 +1,51 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- OSF/Digital UNIX/Tru64
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <time.h> /* for struct tm */
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+
+/* OSF/1 gets this wrong */
+
+#define setpgrp setpgid
+
+#define direct dirent
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_osf.c b/imap/src/osdep/unix/os_osf.c
new file mode 100644
index 00000000..560a3b59
--- /dev/null
+++ b/imap/src/osdep/unix/os_osf.c
@@ -0,0 +1,55 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- OSF/Digital UNIX/Tru64
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+#include <ustat.h>
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "gr_waitp.c"
+#include "tcp_unix.c"
+#include "tz_bsd.c"
+#undef flock
+#include "flocksim.c"
diff --git a/imap/src/osdep/unix/os_osf.h b/imap/src/osdep/unix/os_osf.h
new file mode 100644
index 00000000..432e5316
--- /dev/null
+++ b/imap/src/osdep/unix/os_osf.h
@@ -0,0 +1,51 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- OSF/Digital UNIX/Tru64
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <time.h> /* for struct tm */
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+
+/* OSF/1 gets this wrong */
+
+#define setpgrp setpgid
+
+#define direct dirent
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_osx.c b/imap/src/osdep/unix/os_osx.c
new file mode 100644
index 00000000..25eb1a7d
--- /dev/null
+++ b/imap/src/osdep/unix/os_osx.c
@@ -0,0 +1,54 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Mac OS X version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#define fork vfork
+#include "getspnam.c"
+#include "tcp_unix.c"
+#include "gr_wait4.c"
+#include "tz_bsd.c"
diff --git a/imap/src/osdep/unix/os_osx.h b/imap/src/osdep/unix/os_osx.h
new file mode 100644
index 00000000..3480e14a
--- /dev/null
+++ b/imap/src/osdep/unix/os_osx.h
@@ -0,0 +1,56 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Mac OS X version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 26 October 2007
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+
+/* Mac OS X gets this wrong as of Leopard */
+
+#define setpgrp setpgid
+
+
+#define unix 1
+
+/* Mac OS X security framework also has checkpw, and this causes
+ * multiple-definition problems when building Alpine.
+ */
+
+#define checkpw Checkpw
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_ptx.c b/imap/src/osdep/unix/os_ptx.c
new file mode 100644
index 00000000..d7f84d7d
--- /dev/null
+++ b/imap/src/osdep/unix/os_ptx.c
@@ -0,0 +1,115 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- PTX version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <ctype.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/tiuser.h>
+#include <sys/stropts.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+#include <pwd.h>
+#include <shadow.h>
+#include <sys/select.h>
+#include "misc.h"
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+#define DIR_SIZE(d) d->d_reclen
+
+#define toint(c) ((c)-'0')
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#define env_init ENV_INIT
+#include "env_unix.c"
+#undef env_init
+#define getpeername Getpeername
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "flocksim.c"
+#include "scandir.c"
+#include "tz_sv4.c"
+#include "utime.c"
+
+/* Jacket around env_init() to work around PTX inetd braindamage */
+
+static char may_need_server_init = T;
+
+long env_init (char *user,char *home)
+{
+ if (may_need_server_init) { /* maybe need to do server init cruft? */
+ may_need_server_init = NIL; /* not any more we don't */
+ if (!getuid ()) { /* if root, we're most likely a server */
+ t_sync (0); /* PTX inetd is stupid, stupid, stupid */
+ ioctl (0,I_PUSH,"tirdwr");/* it needs this cruft, else servers won't */
+ dup2 (0,1); /* work. How obnoxious!!! */
+ }
+ }
+ ENV_INIT (user,home); /* call the real routine */
+}
+
+/* Emulator for BSD gethostid() call
+ * Returns: unique identifier for this machine
+ */
+
+long gethostid (void)
+{
+ struct sockaddr_in sin;
+ int inet = t_open (TLI_TCP, O_RDWR, 0);
+ if (inet < 0) return 0;
+ getmyinaddr (inet,&sin,sizeof (sin));
+ close (inet);
+ return sin.sin_addr.s_addr;
+}
+
+
+/* Replaced version of getpeername() that jackets into getpeerinaddr()
+ * Accepts: file descriptor
+ * pointer to Internet socket addr
+ * length
+ * Returns: zero if success, data in socket addr
+ */
+
+int Getpeername (int s,struct sockaddr *name,int *namelen)
+{
+ return getpeerinaddr (s,(struct sockaddr_in *) name,*namelen);
+}
diff --git a/imap/src/osdep/unix/os_ptx.h b/imap/src/osdep/unix/os_ptx.h
new file mode 100644
index 00000000..db772597
--- /dev/null
+++ b/imap/src/osdep/unix/os_ptx.h
@@ -0,0 +1,72 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- PTX version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 15 September 2006
+ */
+
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <utime.h>
+#include <dirent.h>
+#include <stropts.h> /* needed in daemons */
+#include <syslog.h>
+#include <sys/file.h>
+#include <ustat.h>
+
+
+/* Different names, equivalent things in BSD and SysV */
+
+#define L_SET SEEK_SET
+#define L_INCR SEEK_CUR
+#define L_XTND SEEK_END
+
+#define direct dirent
+#define random lrand48
+
+
+#define utime portable_utime
+int portable_utime (char *file,time_t timep[2]);
+
+long gethostid (void);
+typedef int (*select_t) (struct direct *name);
+typedef int (*compar_t) (void *d1,void *d2);
+int scandir (char *dirname,struct direct ***namelist,select_t select,
+ compar_t compar);
+int alphasort (void *d1,void *d2);
+long ENV_INIT (char *user,char *home);
+
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_pyr.c b/imap/src/osdep/unix/os_pyr.c
new file mode 100644
index 00000000..69c8e7e4
--- /dev/null
+++ b/imap/src/osdep/unix/os_pyr.c
@@ -0,0 +1,61 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Pyramid OSx 4.4c version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+#define toint(c) ((c)-'0')
+
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_wait.c"
+#include "memmove.c"
+#include "memset.c"
+#include "strerror.c"
+#include "strpbrk.c"
+#include "strstr.c"
+#include "strtok.c"
+#include "tz_nul.c"
diff --git a/imap/src/osdep/unix/os_pyr.h b/imap/src/osdep/unix/os_pyr.h
new file mode 100644
index 00000000..c7c7e57a
--- /dev/null
+++ b/imap/src/osdep/unix/os_pyr.h
@@ -0,0 +1,58 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Pyramid OSx 4.4c version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 January 2007
+ */
+
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+
+char *strtok (char *s,char *ct);
+char *strtok_r (char *s,char *ct,char **r);
+char *strstr (char *cs,char *ct);
+char *strpbrk (char *cs,char *ct);
+char *strerror (int n);
+void *memmove (void *s,void *ct,size_t n);
+void *memset (void *s,int c,size_t n);
+void *malloc (size_t byteSize);
+void free (void *ptr);
+void *realloc (void *oldptr,size_t newsize);
+
+int errno;
+
+#define memcpy memmove
+#define strchr index
+#define strrchr rindex
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_qnx.c b/imap/src/osdep/unix/os_qnx.c
new file mode 100644
index 00000000..b1382519
--- /dev/null
+++ b/imap/src/osdep/unix/os_qnx.c
@@ -0,0 +1,77 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- QNX version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1993
+ * Last Edited: 20 December 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include <shadow.h>
+#include <sys/select.h>
+#include "misc.h"
+
+#define DIR_SIZE(d) d->d_reclen
+
+extern char *crypt (const char *pw, const char *salt);
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "tz_sv4.c"
+#include "gethstid.c"
+#include "scandir.c"
+
+/* QNX local readdir()
+ * Accepts: directory structure
+ * Returns: direct struct or NIL if failed
+ */
+
+#undef readdir
+
+struct direct *Readdir (DIR *dirp)
+{
+ static struct direct dc;
+ struct dirent *de = readdir (dirp);
+ if (!de) return NIL; /* end of data */
+ dc.d_fileno = 0; /* could get from de->stat.st_ino */
+ dc.d_namlen = strlen (strcpy (dc.d_name,de->d_name));
+ dc.d_reclen = sizeof (dc);
+ return &dc;
+}
diff --git a/imap/src/osdep/unix/os_qnx.h b/imap/src/osdep/unix/os_qnx.h
new file mode 100644
index 00000000..66ea5e62
--- /dev/null
+++ b/imap/src/osdep/unix/os_qnx.h
@@ -0,0 +1,62 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- QNX version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 September 1993
+ * Last Edited: 20 December 2006
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/file.h>
+#include <sys/dir.h>
+#include <sys/types.h>
+#include </usr/include/unix.h>
+#include <time.h>
+#include <utime.h>
+
+
+/* QNX gets these wrong */
+
+#define setpgrp setpgid
+#define readdir Readdir
+#define FNDELAY O_NONBLOCK
+#define d_ino d_fileno
+
+typedef int (*select_t) (struct direct *name);
+typedef int (*compar_t) (void *d1,void *d2);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+
+long gethostid(void);
+struct direct *Readdir (DIR *dirp);
+
+extern long random (void);
diff --git a/imap/src/osdep/unix/os_s40.c b/imap/src/osdep/unix/os_s40.c
new file mode 100644
index 00000000..3dc2fe63
--- /dev/null
+++ b/imap/src/osdep/unix/os_s40.c
@@ -0,0 +1,64 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- SUN-OS 4.0 version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+#define toint(c) ((c)-'0')
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_wait4.c"
+#include "memmove.c"
+#include "strerror.c"
+#define strstr Strstr /* override SUN's broken version */
+#include "strstr.c"
+#include "strtoul.c"
+#include "tz_bsd.c"
diff --git a/imap/src/osdep/unix/os_s40.h b/imap/src/osdep/unix/os_s40.h
new file mode 100644
index 00000000..a67d923f
--- /dev/null
+++ b/imap/src/osdep/unix/os_s40.h
@@ -0,0 +1,33 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- SUN-OS 4.0 version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+void *malloc (size_t byteSize);
+void free (void *ptr);
+void *realloc (void *oldptr,size_t newsize);
+
+#include "os_sun.h" /* now use regular SUN-OS file */
diff --git a/imap/src/osdep/unix/os_sc5.c b/imap/src/osdep/unix/os_sc5.c
new file mode 100644
index 00000000..11ac69de
--- /dev/null
+++ b/imap/src/osdep/unix/os_sc5.c
@@ -0,0 +1,63 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- SCO Unix version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include <sys/time.h> /* must be before osdep.h */
+#include "mail.h"
+#include <stdio.h>
+#include "osdep.h"
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+#include "misc.h"
+#define SecureWare /* protected subsystem */
+#include <sys/security.h>
+#include <sys/audit.h>
+#include <prot.h>
+#include <pwd.h>
+
+#define DIR_SIZE(d) d->d_reclen
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "flocksim.c"
+#include "scandir.c"
+#include "tz_sv4.c"
+#include "gethstid.c"
+#undef setpgrp
+#include "setpgrp.c"
+#include "rename.c"
+#include "utime.c"
diff --git a/imap/src/osdep/unix/os_sc5.h b/imap/src/osdep/unix/os_sc5.h
new file mode 100644
index 00000000..f81e4961
--- /dev/null
+++ b/imap/src/osdep/unix/os_sc5.h
@@ -0,0 +1,76 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- SCO Unix version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 20 December 2006
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <utime.h>
+#include <dirent.h>
+#include <syslog.h>
+#include <sys/file.h>
+#include <ustat.h>
+
+/* SCO gets this wrong */
+
+#define setpgrp Setpgrp
+int Setpgrp (int pid,int gid);
+
+#define rename Rename
+
+
+/* Different names, equivalent things in BSD and SysV */
+
+#define L_SET SEEK_SET
+#define L_INCR SEEK_CUR
+#define L_XTND SEEK_END
+
+#define direct dirent
+
+#define utime portable_utime
+int portable_utime (char *file,time_t timep[2]);
+
+
+long gethostid (void);
+typedef int (*select_t) (struct direct *name);
+typedef int (*compar_t) (void *d1,void *d2);
+int scandir (char *dirname,struct direct ***namelist,select_t select,
+ compar_t compar);
+int alphasort (void *d1,void *d2);
+int fsync (int fd);
+
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_sco.c b/imap/src/osdep/unix/os_sco.c
new file mode 100644
index 00000000..fb082420
--- /dev/null
+++ b/imap/src/osdep/unix/os_sco.c
@@ -0,0 +1,66 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- SCO Unix version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include <sys/time.h> /* must be before osdep.h */
+#include "mail.h"
+#include <stdio.h>
+#include "osdep.h"
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+#include "misc.h"
+#define SecureWare /* protected subsystem */
+#include <sys/security.h>
+#include <sys/audit.h>
+#include <prot.h>
+#include <pwd.h>
+
+char *bigcrypt (char *key,char *salt);
+
+#define DIR_SIZE(d) d->d_reclen
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "flocksim.c"
+#include "scandir.c"
+#include "tz_sv4.c"
+#include "gethstid.c"
+#include "fsync.c"
+#undef setpgrp
+#include "setpgrp.c"
+#include "rename.c"
+#include "utime.c"
diff --git a/imap/src/osdep/unix/os_sco.h b/imap/src/osdep/unix/os_sco.h
new file mode 100644
index 00000000..6bcd5376
--- /dev/null
+++ b/imap/src/osdep/unix/os_sco.h
@@ -0,0 +1,79 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- SCO Unix version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 20 December 2006
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <utime.h>
+#include <dirent.h>
+#include <syslog.h>
+#include <sys/file.h>
+#include <ustat.h>
+
+/* SCO gets this wrong */
+
+#define setpgrp Setpgrp
+int Setpgrp (int pid,int gid);
+
+#define rename Rename
+
+
+/* Different names, equivalent things in BSD and SysV */
+
+#define L_SET SEEK_SET
+#define L_INCR SEEK_CUR
+#define L_XTND SEEK_END
+
+#define direct dirent
+
+#define utime portable_utime
+int portable_utime (char *file,time_t timep[2]);
+
+#define ftruncate chsize
+#define random rand
+
+
+long gethostid (void);
+typedef int (*select_t) (struct direct *name);
+typedef int (*compar_t) (void *d1,void *d2);
+int scandir (char *dirname,struct direct ***namelist,select_t select,
+ compar_t compar);
+int alphasort (void *d1,void *d2);
+int fsync (int fd);
+
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_sgi.c b/imap/src/osdep/unix/os_sgi.c
new file mode 100644
index 00000000..3e5eb550
--- /dev/null
+++ b/imap/src/osdep/unix/os_sgi.c
@@ -0,0 +1,57 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- SGI version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <bstring.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "tz_nul.c"
+#include "flocksim.c"
+#undef setpgrp
+#include "setpgrp.c"
+#include "utime.c"
diff --git a/imap/src/osdep/unix/os_sgi.h b/imap/src/osdep/unix/os_sgi.h
new file mode 100644
index 00000000..22664858
--- /dev/null
+++ b/imap/src/osdep/unix/os_sgi.h
@@ -0,0 +1,59 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- SGI version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 20 December 2006
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <time.h> /* for struct tm */
+#include <utime.h>
+#include <fcntl.h>
+#include <sys/syslog.h>
+#include <sys/file.h>
+#include <ustat.h>
+
+/* Many versions of SysV get this wrong */
+
+#define setpgrp(a,b) Setpgrp(a,b)
+int Setpgrp (int pid,int gid);
+
+
+#define direct dirent
+
+#define fatal cclient_fatal
+
+#define utime portable_utime
+int portable_utime (char *file,time_t timep[2]);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_shp.c b/imap/src/osdep/unix/os_shp.c
new file mode 100644
index 00000000..eb7e4e80
--- /dev/null
+++ b/imap/src/osdep/unix/os_shp.c
@@ -0,0 +1,79 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- HP/UX version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 16 August 2007
+ */
+
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+#define toint(c) ((c)-'0')
+
+#include <stdio.h>
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/utsname.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+extern char *sys_errlist[];
+extern int sys_nerr;
+#include <pwd.h>
+#include <hpsecurity.h>
+#include <prot.h>
+#include "misc.h"
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "flocksim.c"
+#include "tz_sv4.c"
+#undef setpgrp
+#include "setpgrp.c"
+#include "utime.c"
+
+/* Emulator for BSD gethostid() call
+ * Returns: a unique identifier for the system.
+ * Even though HP/UX has an undocumented gethostid() system call,
+ * it does not work (at least for non-privileged users).
+ */
+
+long gethostid (void)
+{
+ struct utsname udata;
+ return (uname (&udata)) ? 0xfeedface : atol (udata.__idnumber);
+}
diff --git a/imap/src/osdep/unix/os_shp.h b/imap/src/osdep/unix/os_shp.h
new file mode 100644
index 00000000..d51dba53
--- /dev/null
+++ b/imap/src/osdep/unix/os_shp.h
@@ -0,0 +1,63 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- HP/UX version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 20 December 2006
+ */
+
+#include <string.h>
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <utime.h>
+#include <syslog.h>
+#include <sys/file.h>
+#include <ustat.h>
+
+
+#define direct dirent
+#define random lrand48
+
+
+/* Many versions of SysV get this wrong */
+
+#define setpgrp(a,b) Setpgrp(a,b)
+int Setpgrp (int pid,int gid);
+
+
+#define utime portable_utime
+int portable_utime (char *file,time_t timep[2]);
+
+long gethostid (void);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_slx.c b/imap/src/osdep/unix/os_slx.c
new file mode 100644
index 00000000..c94d6322
--- /dev/null
+++ b/imap/src/osdep/unix/os_slx.c
@@ -0,0 +1,56 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- modern Linux version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1993
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include <shadow.h>
+#include "misc.h"
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "getspnam.c" /* has socklen_t in spite of man page?? */
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "tz_sv4.c"
+#include "flocklnx.c"
diff --git a/imap/src/osdep/unix/os_slx.h b/imap/src/osdep/unix/os_slx.h
new file mode 100644
index 00000000..b5f39ffa
--- /dev/null
+++ b/imap/src/osdep/unix/os_slx.h
@@ -0,0 +1,67 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Linux version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 September 1993
+ * Last Edited: 30 August 2006
+ */
+
+/*
+ *** These lines are claimed to be necessary to build on Debian Linux on an
+ *** Alpha.
+ */
+
+#ifndef _XOPEN_SOURCE
+#define _XOPEN_SOURCE 1
+#endif /* _XOPEN_SOURCE */
+#ifndef _BSD_SOURCE
+#define _BSD_SOURCE 1
+#endif /* _BSD_SOURCE */
+
+/* end Debian Linux on Alpha strangeness */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <time.h> /* for struct tm */
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+
+/* Linux gets this wrong */
+
+#define setpgrp setpgid
+
+#define direct dirent
+
+#define flock safe_flock
+
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_sol.c b/imap/src/osdep/unix/os_sol.c
new file mode 100644
index 00000000..7a78ef80
--- /dev/null
+++ b/imap/src/osdep/unix/os_sol.c
@@ -0,0 +1,71 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Solaris version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 April 1992
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <ctype.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/tiuser.h>
+#include <sys/stropts.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+#include <pwd.h>
+#include <shadow.h>
+#include <crypt.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include "misc.h"
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+#define DIR_SIZE(d) d->d_reclen
+
+#define toint(c) ((c)-'0')
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "getspnam.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "flocksim.c"
+#include "scandir.c"
+#include "tz_sv4.c"
+#include "gethstid.c"
+#undef setpgrp
+#include "setpgrp.c"
+#include "utime.c"
diff --git a/imap/src/osdep/unix/os_soln.h b/imap/src/osdep/unix/os_soln.h
new file mode 100644
index 00000000..969dcbbb
--- /dev/null
+++ b/imap/src/osdep/unix/os_soln.h
@@ -0,0 +1,87 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Solaris version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 20 December 2006
+ */
+
+#include <string.h>
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <utime.h>
+#include <syslog.h>
+#include <sys/file.h>
+#include <ustat.h>
+
+
+/* Many versions of SysV get this wrong */
+
+#define setpgrp(a,b) Setpgrp(a,b)
+int Setpgrp (int pid,int gid);
+
+
+/* Different names, equivalent things in BSD and SysV */
+
+/* L_SET is defined for some strange reason in <sys/file.h> on SVR4. */
+#ifndef L_SET
+#define L_SET SEEK_SET
+#endif
+#ifndef L_INCR
+#define L_INCR SEEK_CUR
+#endif
+#ifndef L_XTND
+#define L_XTND SEEK_END
+#endif
+
+#define direct dirent
+#define random lrand48
+
+#define scandir Scandir
+#define alphasort Alphasort
+
+#define getpass getpassphrase
+
+
+#define utime portable_utime
+int portable_utime (char *file,time_t timep[2]);
+
+long gethostid (void);
+typedef int (*select_t) (struct direct *name);
+typedef int (*compar_t) (const void *d1,const void *d2);
+int scandir (char *dirname,struct direct ***namelist,select_t select,
+ compar_t compar);
+int alphasort (void *d1,void *d2);
+
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_solo.h b/imap/src/osdep/unix/os_solo.h
new file mode 100644
index 00000000..c3fe4a84
--- /dev/null
+++ b/imap/src/osdep/unix/os_solo.h
@@ -0,0 +1,84 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Solaris version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 20 December 2006
+ */
+
+#include <string.h>
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <utime.h>
+#include <syslog.h>
+#include <sys/file.h>
+#include <ustat.h>
+
+
+/* Many versions of SysV get this wrong */
+
+#define setpgrp(a,b) Setpgrp(a,b)
+int Setpgrp (int pid,int gid);
+
+
+/* Different names, equivalent things in BSD and SysV */
+
+/* L_SET is defined for some strange reason in <sys/file.h> on SVR4. */
+#ifndef L_SET
+#define L_SET SEEK_SET
+#endif
+#ifndef L_INCR
+#define L_INCR SEEK_CUR
+#endif
+#ifndef L_XTND
+#define L_XTND SEEK_END
+#endif
+
+#define direct dirent
+#define random lrand48
+
+#define scandir Scandir
+#define alphasort Alphasort
+
+#define utime portable_utime
+int portable_utime (char *file,time_t timep[2]);
+
+long gethostid (void);
+typedef int (*select_t) (struct direct *name);
+typedef int (*compar_t) (const void *d1,const void *d2);
+int scandir (char *dirname,struct direct ***namelist,select_t select,
+ compar_t compar);
+int alphasort (void *d1,void *d2);
+
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_sos.c b/imap/src/osdep/unix/os_sos.c
new file mode 100644
index 00000000..8256aafa
--- /dev/null
+++ b/imap/src/osdep/unix/os_sos.c
@@ -0,0 +1,57 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- S OSF/Digital UNIX/Tru64
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+#include <sys/security.h>
+#include <prot.h>
+#include <ustat.h>
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "tz_bsd.c"
+#undef flock
+#include "flocksim.c"
diff --git a/imap/src/osdep/unix/os_sos.h b/imap/src/osdep/unix/os_sos.h
new file mode 100644
index 00000000..432e5316
--- /dev/null
+++ b/imap/src/osdep/unix/os_sos.h
@@ -0,0 +1,51 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- OSF/Digital UNIX/Tru64
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <time.h> /* for struct tm */
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+
+/* OSF/1 gets this wrong */
+
+#define setpgrp setpgid
+
+#define direct dirent
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_sua.c b/imap/src/osdep/unix/os_sua.c
new file mode 100644
index 00000000..dcf9311b
--- /dev/null
+++ b/imap/src/osdep/unix/os_sua.c
@@ -0,0 +1,54 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Windows Vista SUA
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1993
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "tz_sv4.c"
+#include "gethstid.c"
diff --git a/imap/src/osdep/unix/os_sua.h b/imap/src/osdep/unix/os_sua.h
new file mode 100644
index 00000000..f4150d4f
--- /dev/null
+++ b/imap/src/osdep/unix/os_sua.h
@@ -0,0 +1,50 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Windows Vista SUA
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 September 1993
+ * Last Edited: 4 May 2007
+ */
+
+#define _REENTRANT /* for strtok_r() */
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <time.h> /* for struct tm */
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+
+#define setpgrp setpgid
+
+#define direct dirent
+
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_sun.c b/imap/src/osdep/unix/os_sun.c
new file mode 100644
index 00000000..5348ef58
--- /dev/null
+++ b/imap/src/osdep/unix/os_sun.c
@@ -0,0 +1,64 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- SUN-OS version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+#define toint(c) ((c)-'0')
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "memmove.c"
+#include "strerror.c"
+#define strstr Strstr /* override SUN's broken version */
+#include "strstr.c"
+#include "strtoul.c"
+#include "tz_bsd.c"
diff --git a/imap/src/osdep/unix/os_sun.h b/imap/src/osdep/unix/os_sun.h
new file mode 100644
index 00000000..c7d72ace
--- /dev/null
+++ b/imap/src/osdep/unix/os_sun.h
@@ -0,0 +1,51 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- SUN-OS version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+
+#define strstr Strstr /* override system definition */
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+
+char *Strstr (char *cs,char *ct);
+char *strerror (int n);
+unsigned long strtoul (char *s,char **endp,int base);
+#define memcpy memmove
+void *memmove (void *s,void *ct,size_t n);
+void *memset (void *s,int c,size_t n);
diff --git a/imap/src/osdep/unix/os_sv2.c b/imap/src/osdep/unix/os_sv2.c
new file mode 100644
index 00000000..14e3951b
--- /dev/null
+++ b/imap/src/osdep/unix/os_sv2.c
@@ -0,0 +1,128 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- SVR2 version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 April 1992
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <ctype.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+extern int errno;
+#include <pwd.h>
+#include <sys/socket.h>
+#include <time.h>
+#define KERNEL
+#include <sys/time.h>
+#undef KERNEL
+#include "misc.h"
+
+#define DIR_SIZE(d) sizeof (DIR)
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+#define toint(c) ((c)-'0')
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+
+#define NBBY 8 /* number of bits in a byte */
+#define FD_SETSIZE 256
+
+typedef long fd_mask;
+#define NFDBITS (sizeof(fd_mask) * NBBY)
+ /* bits per mask */
+#define howmany(x, y) (((x)+((y)-1))/(y))
+
+typedef struct fd_set {
+ fd_mask fds_bits[howmany(FD_SETSIZE, NFDBITS)];
+} fd_set;
+
+#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= \
+ (1 << ((n) % NFDBITS)))
+#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= \
+ ~(1 << ((n) % NFDBITS)))
+#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & \
+ (1 << ((n) % NFDBITS)))
+#define FD_ZERO(p) bzero((char *)(p), sizeof(*(p)))
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_wait.c"
+#include "flocksim.c"
+#include "opendir.c"
+#include "scandir.c"
+#include "memmove2.c"
+#include "strstr.c"
+#include "strerror.c"
+#include "strtoul.c"
+#include "tz_sv4.c"
+#include "gethstid.c"
+#include "fsync.c"
+#undef setpgrp
+#include "setpgrp.c"
+
+/* Emulator for BSD syslog() routine
+ * Accepts: priority
+ * message
+ * parameters
+ */
+
+int syslog (int priority,char *message,char *parameters)
+{
+ /* nothing here for now */
+}
+
+
+/* Emulator for BSD openlog() routine
+ * Accepts: identity
+ * options
+ * facility
+ */
+
+int openlog (char *ident,int logopt,int facility)
+{
+ /* nothing here for now */
+}
+
+
+/* Emulator for BSD ftruncate() routine
+ * Accepts: file descriptor
+ * length
+ */
+
+int ftruncate (int fd,unsigned long length)
+{
+ return -1; /* gotta figure out how to do this */
+}
diff --git a/imap/src/osdep/unix/os_sv2.h b/imap/src/osdep/unix/os_sv2.h
new file mode 100644
index 00000000..c39904a4
--- /dev/null
+++ b/imap/src/osdep/unix/os_sv2.h
@@ -0,0 +1,120 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- SVR2 version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 April 1992
+ * Last Edited: 20 December 2006
+ */
+
+#include <unistd.h>
+#include <string.h>
+#define char void
+#include <memory.h>
+#undef char
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+#include <ustat.h>
+
+
+/* Many versions of SysV get this wrong */
+
+#define setpgrp(a,b) Setpgrp(a,b)
+int Setpgrp (int pid,int gid);
+
+
+/* Different names between BSD and SVR4 */
+
+#define L_SET SEEK_SET
+#define L_INCR SEEK_CUR
+#define L_XTND SEEK_END
+
+#define lstat stat
+#define random lrand48
+
+#define SIGSTOP SIGQUIT
+
+#define S_IFLNK 0120000
+
+
+/* syslog() emulation */
+
+#define LOG_MAIL (2<<3) /* mail system */
+#define LOG_DAEMON (3<<3) /* system daemons */
+#define LOG_AUTH (4<<3) /* security/authorization messages */
+#define LOG_ALERT 1 /* action must be taken immediately */
+#define LOG_CONS 0x02 /* log on the console if errors in sending */
+#define LOG_ODELAY 0x04 /* delay open until syslog() is called */
+#define LOG_NDELAY 0x08 /* don't delay open */
+#define LOG_NOWAIT 0x10 /* if forking to log on console, don't wait() */
+
+
+/* For setitimer() emulation */
+
+#define ITIMER_REAL 0
+
+
+/* For opendir() emulation */
+
+typedef struct _dirdesc {
+ int dd_fd;
+ long dd_loc;
+ long dd_size;
+ char *dd_buf;
+} DIR;
+
+struct passwd *getpwent (void);
+struct passwd *getpwuid (int uid);
+struct passwd *getpwnam (char *name);
+struct group *getgrnam (char *name);
+
+char *getenv (char *name);
+long gethostid (void);
+void *memmove (void *s,void *ct,size_t n);
+char *strstr (char *cs,char *ct);
+char *strerror (int n);
+unsigned long strtoul (char *s,char **endp,int base);
+DIR *opendir (char * name);
+int closedir (DIR *d);
+struct direct *readdir (DIR *d);
+typedef int (*select_t) (struct direct *name);
+typedef int (*compar_t) (void *d1,void *d2);
+int scandir (char *dirname,struct direct ***namelist,select_t select,
+ compar_t compar);
+int alphasort (void *d1,void *d2);
+int fsync (int fd);
+int openlog (ident,logopt,facility);
+int syslog (priority,message,parameters ...);
+void *malloc (size_t byteSize);
+void free (void *ptr);
+void *realloc (void *oldptr,size_t newsize);
+
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_sv4.c b/imap/src/osdep/unix/os_sv4.c
new file mode 100644
index 00000000..4b8dd12f
--- /dev/null
+++ b/imap/src/osdep/unix/os_sv4.c
@@ -0,0 +1,68 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- SVR4 version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 April 1992
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <ctype.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/tiuser.h>
+#include <sys/stropts.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+#include <pwd.h>
+#include <shadow.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include "misc.h"
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+#define DIR_SIZE(d) d->d_reclen
+
+#define toint(c) ((c)-'0')
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "flocksim.c"
+#include "scandir.c"
+#include "tz_sv4.c"
+#include "gethstid.c"
+#undef setpgrp
+#include "setpgrp.c"
+#include "utime.c"
diff --git a/imap/src/osdep/unix/os_sv4.h b/imap/src/osdep/unix/os_sv4.h
new file mode 100644
index 00000000..a11ad15d
--- /dev/null
+++ b/imap/src/osdep/unix/os_sv4.h
@@ -0,0 +1,78 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- SVR4 version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 April 1992
+ * Last Edited: 20 December 2006
+ */
+
+#include <string.h>
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <utime.h>
+#include <syslog.h>
+#include <sys/file.h>
+#include <ustat.h>
+
+
+/* Many versions of SysV get this wrong */
+
+#define setpgrp(a,b) Setpgrp(a,b)
+int Setpgrp (int pid,int gid);
+
+
+/* Different names, equivalent things in BSD and SysV */
+
+/* L_SET is defined for some strange reason in <sys/file.h> on SVR4. */
+#ifndef L_SET
+#define L_SET SEEK_SET
+#endif
+#define L_INCR SEEK_CUR
+#define L_XTND SEEK_END
+
+#define direct dirent
+#define random lrand48
+
+
+#define utime portable_utime
+int portable_utime (char *file,time_t timep[2]);
+
+long gethostid (void);
+typedef int (*select_t) (struct direct *name);
+typedef int (*compar_t) (void *d1,void *d2);
+int scandir (char *dirname,struct direct ***namelist,select_t select,
+ compar_t compar);
+int alphasort (void *d1,void *d2);
+
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "flocksim.h"
diff --git a/imap/src/osdep/unix/os_ult.c b/imap/src/osdep/unix/os_ult.c
new file mode 100644
index 00000000..28b72ecc
--- /dev/null
+++ b/imap/src/osdep/unix/os_ult.c
@@ -0,0 +1,52 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Ultrix version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ *
+ * Date: 11 May 1989
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_waitp.c"
+#include "tz_bsd.c"
diff --git a/imap/src/osdep/unix/os_ult.h b/imap/src/osdep/unix/os_ult.h
new file mode 100644
index 00000000..3b93f6db
--- /dev/null
+++ b/imap/src/osdep/unix/os_ult.h
@@ -0,0 +1,42 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- Ultrix version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/syslog.h>
+#include <sys/file.h>
+
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/os_vu2.c b/imap/src/osdep/unix/os_vu2.c
new file mode 100644
index 00000000..be574e07
--- /dev/null
+++ b/imap/src/osdep/unix/os_vu2.c
@@ -0,0 +1,82 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- VAX Ultrix 2.3 version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ *
+ * Date: 11 May 1989
+ * Last Edited: 16 August 2007
+ */
+
+#include "tcp_unix.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <pwd.h>
+#include "misc.h"
+
+#define NFDBITS (sizeof(long) * 8)
+
+#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= \
+ (1 << ((n) % NFDBITS)))
+#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= \
+ ~(1 << ((n) % NFDBITS)))
+#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & \
+ (1 << ((n) % NFDBITS)))
+#define FD_ZERO(p) bzero((char *)(p), sizeof(*(p)))
+
+
+/* Old Ultrix has its own wierd inet_addr() that returns a in_addr struct. */
+
+/* Portable inet_addr () that returns a u_long
+ * Accepts: dotted host string
+ * Returns: u_long
+ */
+
+u_long portable_inet_addr (char *hostname)
+{
+ struct in_addr *in = &inet_addr (hostname);
+ return in->s_addr;
+}
+
+
+#define inet_addr portable_inet_addr
+
+#include "fs_unix.c"
+#include "ftl_unix.c"
+#include "nl_unix.c"
+#include "env_unix.c"
+#define fork vfork
+#include "tcp_unix.c"
+#include "gr_wait.c"
+#include "memmove.c"
+#include "strerror.c"
+#include "strstr.c"
+#include "strtoul.c"
+#include "tz_nul.c"
diff --git a/imap/src/osdep/unix/os_vu2.h b/imap/src/osdep/unix/os_vu2.h
new file mode 100644
index 00000000..7721e092
--- /dev/null
+++ b/imap/src/osdep/unix/os_vu2.h
@@ -0,0 +1,79 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- VAX Ultrix 2.3 version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+#include <memory.h>
+#include <sys/types.h>
+#include <sys/dir.h>
+#include <string.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <sys/file.h>
+
+
+/* syslog() emulation */
+
+#define LOG_MAIL (2<<3) /* mail system */
+#define LOG_DAEMON (3<<3) /* system daemons */
+#define LOG_AUTH (4<<3) /* security/authorization messages */
+#define LOG_EMERG 0 /* system is unusable */
+#define LOG_ALERT 1 /* action must be taken immediately */
+#define LOG_CRIT 2 /* critical conditions */
+#define LOG_ERR 3 /* error conditions */
+#define LOG_WARNING 4 /* warning conditions */
+#define LOG_NOTICE 5 /* normal but signification condition */
+#define LOG_INFO 6 /* informational */
+#define LOG_DEBUG 7 /* debug-level messages */
+#define LOG_PID 0x01 /* log the pid with each message */
+#define LOG_CONS 0x02 /* log on the console if errors in sending */
+#define LOG_ODELAY 0x04 /* delay open until syslog() is called */
+#define LOG_NDELAY 0x08 /* don't delay open */
+#define LOG_NOWAIT 0x10 /* if forking to log on console, don't wait() */
+
+
+#define isodigit(c) (((unsigned)(c)>=060)&((unsigned)(c)<=067))
+#define toint(c) ((c)-'0')
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+
+char *getenv (char *name);
+char *strstr (char *cs,char *ct);
+char *strerror (int n);
+void *memmove (void *s,void *ct,size_t n);
+unsigned long strtoul (char *s,char **endp,int base);
+void *malloc (size_t byteSize);
+void free (void *ptr);
+void *realloc (void *oldptr,size_t newsize);
+u_long portable_inet_addr (char *hostname);
+
+#include "env_unix.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
diff --git a/imap/src/osdep/unix/phile.c b/imap/src/osdep/unix/phile.c
new file mode 100644
index 00000000..ce72d0a9
--- /dev/null
+++ b/imap/src/osdep/unix/phile.c
@@ -0,0 +1,553 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: File routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 25 August 1993
+ * Last Edited: 9 May 2006
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <signal.h>
+#include "mail.h"
+#include "osdep.h"
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include "rfc822.h"
+#include "misc.h"
+#include "dummy.h"
+
+/* Types returned from phile_type() */
+
+#define PTYPEBINARY 0 /* binary data */
+#define PTYPETEXT 1 /* textual data */
+#define PTYPECRTEXT 2 /* textual data with CR */
+#define PTYPE8 4 /* textual 8bit data */
+#define PTYPEISO2022JP 8 /* textual Japanese */
+#define PTYPEISO2022KR 16 /* textual Korean */
+#define PTYPEISO2022CN 32 /* textual Chinese */
+
+
+/* PHILE I/O stream local data */
+
+typedef struct phile_local {
+ ENVELOPE *env; /* file envelope */
+ BODY *body; /* file body */
+ char tmp[MAILTMPLEN]; /* temporary buffer */
+} PHILELOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((PHILELOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *phile_valid (char *name);
+int phile_isvalid (char *name,char *tmp);
+void *phile_parameters (long function,void *value);
+void phile_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void phile_list (MAILSTREAM *stream,char *ref,char *pat);
+void phile_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long phile_create (MAILSTREAM *stream,char *mailbox);
+long phile_delete (MAILSTREAM *stream,char *mailbox);
+long phile_rename (MAILSTREAM *stream,char *old,char *newname);
+long phile_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *phile_open (MAILSTREAM *stream);
+int phile_type (unsigned char *s,unsigned long i,unsigned long *j);
+void phile_close (MAILSTREAM *stream,long options);
+ENVELOPE *phile_structure (MAILSTREAM *stream,unsigned long msgno,BODY **body,
+ long flags);
+char *phile_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long phile_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+long phile_ping (MAILSTREAM *stream);
+void phile_check (MAILSTREAM *stream);
+long phile_expunge (MAILSTREAM *stream,char *sequence,long options);
+long phile_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long phile_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+/* File routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER philedriver = {
+ "phile", /* driver name */
+ /* driver flags */
+ DR_LOCAL|DR_READONLY|DR_NOSTICKY,
+ (DRIVER *) NIL, /* next driver */
+ phile_valid, /* mailbox is valid for us */
+ phile_parameters, /* manipulate parameters */
+ phile_scan, /* scan mailboxes */
+ phile_list, /* list mailboxes */
+ phile_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ dummy_create, /* create mailbox */
+ dummy_delete, /* delete mailbox */
+ dummy_rename, /* rename mailbox */
+ phile_status, /* status of mailbox */
+ phile_open, /* open mailbox */
+ phile_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ phile_structure, /* fetch message envelopes */
+ phile_header, /* fetch message header only */
+ phile_text, /* fetch message body only */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ NIL, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ phile_ping, /* ping mailbox to see if still alive */
+ phile_check, /* check for new messages */
+ phile_expunge, /* expunge deleted messages */
+ phile_copy, /* copy messages to another mailbox */
+ phile_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM phileproto = {&philedriver};
+
+/* File validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *phile_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return phile_isvalid (name,tmp) ? &philedriver : NIL;
+}
+
+
+/* File test for valid mailbox
+ * Accepts: mailbox name
+ * Returns: T if valid, NIL otherwise
+ */
+
+int phile_isvalid (char *name,char *tmp)
+{
+ struct stat sbuf;
+ char *s;
+ /* INBOX never accepted, any other name is */
+ return ((s = mailboxfile (tmp,name)) && *s && !stat (s,&sbuf) &&
+ !(sbuf.st_mode & S_IFDIR) &&
+ /* only allow empty files if no empty proto
+ or if #ftp */
+ (sbuf.st_size || !default_proto (T) ||
+ ((*name == '#') && ((name[1] == 'f') || (name[1] == 'F')) &&
+ ((name[2] == 't') || (name[2] == 'T')) &&
+ ((name[3] == 'p') || (name[3] == 'P')) && (name[4] == '/'))));
+}
+
+/* File manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *phile_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+/* File mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void phile_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* File list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void phile_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* File list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void phile_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+
+/* File status
+ * Accepts: mail stream
+ * mailbox name
+ * status flags
+ * Returns: T on success, NIL on failure
+ */
+
+long phile_status (MAILSTREAM *stream,char *mbx,long flags)
+{
+ char *s,tmp[MAILTMPLEN];
+ MAILSTATUS status;
+ struct stat sbuf;
+ long ret = NIL;
+ if ((s = mailboxfile (tmp,mbx)) && *s && !stat (s,&sbuf)) {
+ status.flags = flags; /* return status values */
+ status.unseen = (stream && mail_elt (stream,1)->seen) ? 0 : 1;
+ status.messages = status.recent = status.uidnext = 1;
+ status.uidvalidity = sbuf.st_mtime;
+ /* pass status to main program */
+ mm_status (stream,mbx,&status);
+ ret = LONGT; /* success */
+ }
+ return ret;
+}
+
+/* File open
+ * Accepts: Stream to open
+ * Returns: Stream on success, NIL on failure
+ */
+
+MAILSTREAM *phile_open (MAILSTREAM *stream)
+{
+ int i,k,fd;
+ unsigned long j,m;
+ char *s,tmp[MAILTMPLEN];
+ struct passwd *pw;
+ struct stat sbuf;
+ struct tm *t;
+ MESSAGECACHE *elt;
+ SIZEDTEXT *buf;
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &phileproto;
+ if (stream->local) fatal ("phile recycle stream");
+ /* open associated file */
+ if (!mailboxfile (tmp,stream->mailbox) || !tmp[0] || stat (tmp,&sbuf) ||
+ (fd = open (tmp,O_RDONLY,NIL)) < 0) {
+ sprintf (tmp,"Unable to open file %s",stream->mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ stream->local = fs_get (sizeof (PHILELOCAL));
+ mail_exists (stream,1); /* make sure upper level knows */
+ mail_recent (stream,1);
+ elt = mail_elt (stream,1); /* instantiate cache element */
+ elt->valid = elt->recent = T; /* mark valid flags */
+ stream->sequence++; /* bump sequence number */
+ stream->rdonly = T; /* make sure upper level knows readonly */
+ /* instantiate a new envelope and body */
+ LOCAL->env = mail_newenvelope ();
+ LOCAL->body = mail_newbody ();
+
+ t = gmtime (&sbuf.st_mtime); /* get UTC time and Julian day */
+ i = t->tm_hour * 60 + t->tm_min;
+ k = t->tm_yday;
+ t = localtime(&sbuf.st_mtime);/* get local time */
+ /* calculate time delta */
+ i = t->tm_hour * 60 + t->tm_min - i;
+ if (k = t->tm_yday - k) i += ((k < 0) == (abs (k) == 1)) ? -24*60 : 24*60;
+ k = abs (i); /* time from UTC either way */
+ elt->hours = t->tm_hour; elt->minutes = t->tm_min; elt->seconds = t->tm_sec;
+ elt->day = t->tm_mday; elt->month = t->tm_mon + 1;
+ elt->year = t->tm_year - (BASEYEAR - 1900);
+ elt->zoccident = (k == i) ? 0 : 1;
+ elt->zhours = k/60;
+ elt->zminutes = k % 60;
+ sprintf (tmp,"%s, %d %s %d %02d:%02d:%02d %c%02d%02d",
+ days[t->tm_wday],t->tm_mday,months[t->tm_mon],t->tm_year+1900,
+ t->tm_hour,t->tm_min,t->tm_sec,elt->zoccident ? '-' : '+',
+ elt->zhours,elt->zminutes);
+ /* set up Date field */
+ LOCAL->env->date = cpystr (tmp);
+
+ /* fill in From field from file owner */
+ LOCAL->env->from = mail_newaddr ();
+ if (pw = getpwuid (sbuf.st_uid)) strcpy (tmp,pw->pw_name);
+ else sprintf (tmp,"User-Number-%ld",(long) sbuf.st_uid);
+ LOCAL->env->from->mailbox = cpystr (tmp);
+ LOCAL->env->from->host = cpystr (mylocalhost ());
+ /* set subject to be mailbox name */
+ LOCAL->env->subject = cpystr (stream->mailbox);
+ /* slurp the data */
+ (buf = &elt->private.special.text)->size = sbuf.st_size;
+ read (fd,buf->data = (unsigned char *) fs_get (buf->size + 1),buf->size);
+ buf->data[buf->size] = '\0';
+ close (fd); /* close the file */
+ /* analyze data type */
+ if (i = phile_type (buf->data,buf->size,&j)) {
+ LOCAL->body->type = TYPETEXT;
+ LOCAL->body->subtype = cpystr ("PLAIN");
+ if (!(i & PTYPECRTEXT)) { /* change Internet newline format as needed */
+ s = (char *) buf->data; /* make copy of UNIX-format string */
+ buf->data = NIL; /* zap the buffer */
+ buf->size = strcrlfcpy (&buf->data,&m,s,buf->size);
+ fs_give ((void **) &s); /* flush original UNIX-format string */
+ }
+ LOCAL->body->parameter = mail_newbody_parameter ();
+ LOCAL->body->parameter->attribute = cpystr ("charset");
+ LOCAL->body->parameter->value =
+ cpystr ((i & PTYPEISO2022JP) ? "ISO-2022-JP" :
+ (i & PTYPEISO2022KR) ? "ISO-2022-KR" :
+ (i & PTYPEISO2022CN) ? "ISO-2022-CN" :
+ (i & PTYPE8) ? "X-UNKNOWN" : "US-ASCII");
+ LOCAL->body->encoding = (i & PTYPE8) ? ENC8BIT : ENC7BIT;
+ LOCAL->body->size.lines = j;
+ }
+ else { /* binary data */
+ LOCAL->body->type = TYPEAPPLICATION;
+ LOCAL->body->subtype = cpystr ("OCTET-STREAM");
+ LOCAL->body->parameter = mail_newbody_parameter ();
+ LOCAL->body->parameter->attribute = cpystr ("name");
+ LOCAL->body->parameter->value =
+ cpystr ((s = (strrchr (stream->mailbox,'/'))) ? s+1 : stream->mailbox);
+ LOCAL->body->encoding = ENCBASE64;
+ buf->data = rfc822_binary (s = (char *) buf->data,buf->size,&buf->size);
+ fs_give ((void **) &s); /* flush originary binary contents */
+ }
+ phile_header (stream,1,&j,NIL);
+ LOCAL->body->size.bytes = LOCAL->body->contents.text.size = buf->size;
+ elt->rfc822_size = j + buf->size;
+ /* only one message ever... */
+ stream->uid_validity = sbuf.st_mtime;
+ stream->uid_last = elt->private.uid = 1;
+ return stream; /* return stream alive to caller */
+}
+
+/* File determine data type
+ * Accepts: data to examine
+ * size of data
+ * pointer to line count return
+ * Returns: PTYPE mask of data type
+ */
+
+int phile_type (unsigned char *s,unsigned long i,unsigned long *j)
+{
+ int ret = PTYPETEXT;
+ char *charvec = "bbbbbbbaaalaacaabbbbbbbbbbbebbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+ *j = 0; /* no lines */
+ /* check type of every character */
+ while (i--) switch (charvec[*s++]) {
+ case 'A':
+ ret |= PTYPE8; /* 8bit character */
+ break;
+ case 'a':
+ break; /* ASCII character */
+ case 'b':
+ return PTYPEBINARY; /* binary byte seen, stop immediately */
+ case 'c':
+ ret |= PTYPECRTEXT; /* CR indicates Internet text */
+ break;
+ case 'e': /* ESC */
+ if (*s == '$') { /* ISO-2022 sequence? */
+ switch (s[1]) {
+ case 'B': case '@': ret |= PTYPEISO2022JP; break;
+ case ')':
+ switch (s[2]) {
+ case 'A': case 'E': case 'G': ret |= PTYPEISO2022CN; break;
+ case 'C': ret |= PTYPEISO2022KR; break;
+ }
+ case '*':
+ switch (s[2]) {
+ case 'H': ret |= PTYPEISO2022CN; break;
+ }
+ case '+':
+ switch (s[2]) {
+ case 'I': case 'J': case 'K': case 'L': case 'M':
+ ret |= PTYPEISO2022CN; break;
+ }
+ }
+ }
+ break;
+ case 'l': /* newline */
+ (*j)++;
+ break;
+ }
+ return ret; /* return type of data */
+}
+
+/* File close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void phile_close (MAILSTREAM *stream,long options)
+{
+ if (LOCAL) { /* only if a file is open */
+ fs_give ((void **) &mail_elt (stream,1)->private.special.text.data);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* File fetch structure
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to return body
+ * option flags
+ * Returns: envelope of this message, body returned in body value
+ *
+ * Fetches the "fast" information as well
+ */
+
+ENVELOPE *phile_structure (MAILSTREAM *stream,unsigned long msgno,BODY **body,
+ long flags)
+{
+ if (body) *body = LOCAL->body;
+ return LOCAL->env; /* return the envelope */
+}
+
+
+/* File fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *phile_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags)
+{
+ rfc822_header (LOCAL->tmp,LOCAL->env,LOCAL->body);
+ *length = strlen (LOCAL->tmp);
+ return LOCAL->tmp;
+}
+
+
+/* File fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T, always
+ */
+
+long phile_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ SIZEDTEXT *buf = &mail_elt (stream,msgno)->private.special.text;
+ if (!(flags &FT_PEEK)) { /* mark message as seen */
+ mail_elt (stream,msgno)->seen = T;
+ mm_flags (stream,msgno);
+ }
+ INIT (bs,mail_string,buf->data,buf->size);
+ return T;
+}
+
+/* File ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+long phile_ping (MAILSTREAM *stream)
+{
+ return T;
+}
+
+/* File check mailbox
+ * Accepts: MAIL stream
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+void phile_check (MAILSTREAM *stream)
+{
+ mm_log ("Check completed",NIL);
+}
+
+/* File expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T if success, NIL if failure
+ */
+
+long phile_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ if (!stream->silent) mm_log ("Expunge ignored on readonly mailbox",NIL);
+ return LONGT;
+}
+
+/* File copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if copy successful, else NIL
+ */
+
+long phile_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ char tmp[MAILTMPLEN];
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (tmp,"Can't copy - file \"%s\" is not in valid mailbox format",
+ stream->mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+}
+
+
+/* File append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback function
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long phile_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ char tmp[MAILTMPLEN],file[MAILTMPLEN];
+ char *s = mailboxfile (file,mailbox);
+ if (s && *s)
+ sprintf (tmp,"Can't append - not in valid mailbox format: %.80s",s);
+ else sprintf (tmp,"Can't append - invalid name: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+}
diff --git a/imap/src/osdep/unix/pmatch.c b/imap/src/osdep/unix/pmatch.c
new file mode 100644
index 00000000..a7539e50
--- /dev/null
+++ b/imap/src/osdep/unix/pmatch.c
@@ -0,0 +1,89 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: IMAP Wildcard Matching Routines (case-dependent)
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 June 2000
+ * Last Edited: 30 August 2006
+ */
+
+/* Wildcard pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if pattern matches base, else NIL
+ */
+
+long pmatch_full (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ /* % at end, OK if no inferiors */
+ if (!pat[1]) return (delim && strchr (s,delim)) ? NIL : T;
+ /* scan remainder of string until delimiter */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while ((*s != delim) && *s++);
+ break;
+ case '*': /* match 0 or more characters */
+ if (!pat[1]) return T; /* * at end, unconditional match */
+ /* scan remainder of string */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while (*s++);
+ break;
+ case '\0': /* end of pattern */
+ return *s ? NIL : T; /* success if also end of base */
+ default: /* match this character */
+ return (*pat == *s) ? pmatch_full (s+1,pat+1,delim) : NIL;
+ }
+ return NIL;
+}
+
+/* Directory pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if base is a matching directory of pattern, else NIL
+ */
+
+long dmatch (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ if (!*s) return T; /* end of base means have a subset match */
+ if (!*++pat) return NIL; /* % at end, no inferiors permitted */
+ /* scan remainder of string until delimiter */
+ do if (dmatch (s,pat,delim)) return T;
+ while ((*s != delim) && *s++);
+ if (*s && !s[1]) return T; /* ends with delimiter, must be subset */
+ return dmatch (s,pat,delim);/* do new scan */
+ case '*': /* match 0 or more characters */
+ return T; /* unconditional match */
+ case '\0': /* end of pattern */
+ break;
+ default: /* match this character */
+ if (*s) return (*pat == *s) ? dmatch (s+1,pat+1,delim) : NIL;
+ /* end of base, return if at delimiter */
+ else if (*pat == delim) return T;
+ break;
+ }
+ return NIL;
+}
diff --git a/imap/src/osdep/unix/pseudo.c b/imap/src/osdep/unix/pseudo.c
new file mode 100644
index 00000000..1aae8a53
--- /dev/null
+++ b/imap/src/osdep/unix/pseudo.c
@@ -0,0 +1,36 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Pseudo Header Strings
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 September 1996
+ * Last Edited: 30 August 2006
+ */
+
+/* Local sites may wish to alter this text */
+
+char *pseudo_from = "MAILER-DAEMON";
+char *pseudo_name = "Mail System Internal Data";
+char *pseudo_subject = "DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA";
+char *pseudo_msg =
+ "This text is part of the internal format of your mail folder, and is not\na real message. It is created automatically by the mail system software.\nIf deleted, important folder data will be lost, and it will be re-created\nwith the data reset to initial values."
+ ;
diff --git a/imap/src/osdep/unix/pseudo.h b/imap/src/osdep/unix/pseudo.h
new file mode 100644
index 00000000..c9c07628
--- /dev/null
+++ b/imap/src/osdep/unix/pseudo.h
@@ -0,0 +1,30 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Pseudo Header Strings
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 September 1996
+ * Last Edited: 30 August 2006
+ */
+
+
+extern char *pseudo_from,*pseudo_name,*pseudo_subject,*pseudo_msg;
diff --git a/imap/src/osdep/unix/rename.c b/imap/src/osdep/unix/rename.c
new file mode 100644
index 00000000..6a4c1bda
--- /dev/null
+++ b/imap/src/osdep/unix/rename.c
@@ -0,0 +1,44 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Rename file
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 20 May 1996
+ * Last Edited: 30 August 2006
+ */
+
+/* Emulator for working Unix rename() call
+ * Accepts: old file name
+ * new file name
+ * Returns: 0 if success, -1 if error with error in errno
+ */
+
+int Rename (char *oldname,char *newname)
+{
+ int ret;
+ unlink (newname); /* make sure the old name doesn't exist */
+ /* link to new name, unlink old name */
+ if (!(ret = link (oldname,newname))) unlink (oldname);
+ return ret;
+}
+
+
diff --git a/imap/src/osdep/unix/scandir.c b/imap/src/osdep/unix/scandir.c
new file mode 100644
index 00000000..878343e8
--- /dev/null
+++ b/imap/src/osdep/unix/scandir.c
@@ -0,0 +1,81 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Scan directories
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 15 September 2006
+ */
+
+/* Emulator for BSD scandir() call
+ * Accepts: directory name
+ * destination pointer of names array
+ * selection function
+ * comparison function
+ * Returns: number of elements in the array or -1 if error
+ */
+
+int scandir (char *dirname,struct direct ***namelist,select_t select,
+ compar_t compar)
+{
+ struct direct *p,*d,**names;
+ int nitems;
+ struct stat stb;
+ long nlmax;
+ DIR *dirp = opendir (dirname);/* open directory and get status poop */
+ if ((!dirp) || (fstat (dirp->dd_fd,&stb) < 0)) return -1;
+ nlmax = stb.st_size / 24; /* guesstimate at number of files */
+ names = (struct direct **) fs_get (nlmax * sizeof (struct direct *));
+ nitems = 0; /* initially none found */
+ while (d = readdir (dirp)) { /* read directory item */
+ /* matches select criterion? */
+ if (select && !(*select) (d)) continue;
+ /* get size of direct record for this file */
+ p = (struct direct *) fs_get (DIR_SIZE (d));
+ p->d_ino = d->d_ino; /* copy the poop */
+ strcpy (p->d_name,d->d_name);
+ if (++nitems >= nlmax) { /* if out of space, try bigger guesstimate */
+ void *s = (void *) names; /* stupid language */
+ nlmax *= 2; /* double it */
+ fs_resize ((void **) &s,nlmax * sizeof (struct direct *));
+ names = (struct direct **) s;
+ }
+ names[nitems - 1] = p; /* store this file there */
+ }
+ closedir (dirp); /* done with directory */
+ /* sort if necessary */
+ if (nitems && compar) qsort (names,nitems,sizeof (struct direct *),compar);
+ *namelist = names; /* return directory */
+ return nitems; /* and size */
+}
+
+/* Alphabetic file name comparision
+ * Accepts: first candidate directory entry
+ * second candidate directory entry
+ * Returns: negative if d1 < d2, 0 if d1 == d2, postive if d1 > d2
+ */
+
+int alphasort (void *d1,void *d2)
+{
+ return strcmp ((*(struct direct **) d1)->d_name,
+ (*(struct direct **) d2)->d_name);
+}
diff --git a/imap/src/osdep/unix/setpgrp.c b/imap/src/osdep/unix/setpgrp.c
new file mode 100644
index 00000000..ca957bb2
--- /dev/null
+++ b/imap/src/osdep/unix/setpgrp.c
@@ -0,0 +1,39 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Set process group emulator
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 May 1995
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Emulator for BSD setpgrp() call
+ * Accepts: process ID
+ * group ID
+ * Returns: 0 if successful, -1 if failure
+ */
+
+int Setpgrp (int pid,int gid)
+{
+ return setpgrp ();
+}
diff --git a/imap/src/osdep/unix/sig_bsd.c b/imap/src/osdep/unix/sig_bsd.c
new file mode 100644
index 00000000..c615e1b0
--- /dev/null
+++ b/imap/src/osdep/unix/sig_bsd.c
@@ -0,0 +1,40 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: BSD Signals
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 29 April 1997
+ * Last Edited: 30 August 2006
+ */
+
+#include <signal.h>
+
+/* Arm a signal
+ * Accepts: signal number
+ * desired action
+ * Returns: old action
+ */
+
+void *arm_signal (int sig,void *action)
+{
+ return (void *) signal (sig,action);
+}
diff --git a/imap/src/osdep/unix/sig_psx.c b/imap/src/osdep/unix/sig_psx.c
new file mode 100644
index 00000000..0d592c31
--- /dev/null
+++ b/imap/src/osdep/unix/sig_psx.c
@@ -0,0 +1,51 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: POSIX Signals
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 29 April 1997
+ * Last Edited: 30 August 2006
+ */
+
+#include <signal.h>
+#include <string.h>
+
+#ifndef SA_RESTART
+#define SA_RESTART 0
+#endif
+
+/* Arm a signal
+ * Accepts: signal number
+ * desired action
+ * Returns: old action
+ */
+
+void *arm_signal (int sig,void *action)
+{
+ struct sigaction nact,oact;
+ memset (&nact,0,sizeof (struct sigaction));
+ sigemptyset (&nact.sa_mask); /* no signals blocked */
+ nact.sa_handler = action; /* set signal handler */
+ nact.sa_flags = SA_RESTART; /* needed on Linux, nice on SVR4 */
+ sigaction (sig,&nact,&oact); /* do the signal action */
+ return (void *) oact.sa_handler;
+}
diff --git a/imap/src/osdep/unix/sig_sv4.c b/imap/src/osdep/unix/sig_sv4.c
new file mode 100644
index 00000000..599d0b38
--- /dev/null
+++ b/imap/src/osdep/unix/sig_sv4.c
@@ -0,0 +1,40 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: SVR4 Signals
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 29 April 1997
+ * Last Edited: 30 August 2006
+ */
+
+#include <signal.h>
+
+/* Arm a signal
+ * Accepts: signal number
+ * desired action
+ * Returns: old action
+ */
+
+void *arm_signal (int sig,void *action)
+{
+ return (void *) sigset (sig,action);
+}
diff --git a/imap/src/osdep/unix/ssl_none.c b/imap/src/osdep/unix/ssl_none.c
new file mode 100644
index 00000000..e4dedda7
--- /dev/null
+++ b/imap/src/osdep/unix/ssl_none.c
@@ -0,0 +1,141 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy (no SSL) authentication/encryption module
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 7 February 2001
+ * Last Edited: 30 August 2006
+ */
+
+/* Init server for SSL
+ * Accepts: server name
+ */
+
+void ssl_server_init (char *server)
+{
+ syslog (LOG_ERR,"This server does not support SSL");
+ exit (1); /* punt this program too */
+}
+
+
+/* Start TLS
+ * Accepts: /etc/services service name
+ * Returns: cpystr'd error string if TLS failed, else NIL for success
+ */
+
+char *ssl_start_tls (char *server)
+{
+ return cpystr ("This server does not support TLS");
+}
+
+/* Get character
+ * Returns: character or EOF
+ */
+
+int PBIN (void)
+{
+ return getchar ();
+}
+
+
+/* Get string
+ * Accepts: destination string pointer
+ * number of bytes available
+ * Returns: destination string pointer or NIL if EOF
+ */
+
+char *PSIN (char *s,int n)
+{
+ return fgets (s,n,stdin);
+}
+
+
+/* Get record
+ * Accepts: destination string pointer
+ * number of bytes to read
+ * Returns: T if success, NIL otherwise
+ */
+
+long PSINR (char *s,unsigned long n)
+{
+ unsigned long i;
+ while (n && ((i = fread (s,1,n,stdin)) || (errno == EINTR))) s += i,n -= i;
+ return n ? NIL : LONGT;
+}
+
+
+/* Wait for input
+ * Accepts: timeout in seconds
+ * Returns: T if have input on stdin, else NIL
+ */
+
+long INWAIT (long seconds)
+{
+ return server_input_wait (seconds);
+}
+
+/* Put character
+ * Accepts: character
+ * Returns: character written or EOF
+ */
+
+int PBOUT (int c)
+{
+ return putchar (c);
+}
+
+
+/* Put string
+ * Accepts: source string pointer
+ * Returns: 0 or EOF if error
+ */
+
+int PSOUT (char *s)
+{
+ return fputs (s,stdout);
+}
+
+
+/* Put record
+ * Accepts: source sized text
+ * Returns: 0 or EOF if error
+ */
+
+int PSOUTR (SIZEDTEXT *s)
+{
+ unsigned char *t;
+ unsigned long i,j;
+ for (t = s->data,i = s->size;
+ (i && ((j = fwrite (t,1,i,stdout)) || (errno == EINTR)));
+ t += j,i -= j);
+ return i ? EOF : NIL;
+}
+
+
+/* Flush output
+ * Returns: 0 or EOF if error
+ */
+
+int PFLUSH (void)
+{
+ return fflush (stdout);
+}
diff --git a/imap/src/osdep/unix/ssl_unix.c b/imap/src/osdep/unix/ssl_unix.c
new file mode 100644
index 00000000..5a7d0bc0
--- /dev/null
+++ b/imap/src/osdep/unix/ssl_unix.c
@@ -0,0 +1,821 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: SSL authentication/encryption module
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 22 September 1998
+ * Last Edited: 13 January 2007
+ */
+
+#define crypt ssl_private_crypt
+#include <x509v3.h>
+#include <ssl.h>
+#include <err.h>
+#include <pem.h>
+#include <buffer.h>
+#include <bio.h>
+#include <crypto.h>
+#include <rand.h>
+#undef crypt
+
+#define SSLBUFLEN 8192
+#define SSLCIPHERLIST "ALL:!LOW"
+
+
+/* SSL I/O stream */
+
+typedef struct ssl_stream {
+ TCPSTREAM *tcpstream; /* TCP stream */
+ SSL_CTX *context; /* SSL context */
+ SSL *con; /* SSL connection */
+ int ictr; /* input counter */
+ char *iptr; /* input pointer */
+ char ibuf[SSLBUFLEN]; /* input buffer */
+} SSLSTREAM;
+
+#include "sslio.h"
+
+/* Function prototypes */
+
+static SSLSTREAM *ssl_start(TCPSTREAM *tstream,char *host,unsigned long flags);
+static char *ssl_start_work (SSLSTREAM *stream,char *host,unsigned long flags);
+static int ssl_open_verify (int ok,X509_STORE_CTX *ctx);
+static char *ssl_validate_cert (X509 *cert,char *host);
+static long ssl_compare_hostnames (unsigned char *s,unsigned char *pat);
+static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
+ long *contd);
+static long ssl_abort (SSLSTREAM *stream);
+static RSA *ssl_genkey (SSL *con,int export,int keylength);
+
+
+/* Secure Sockets Layer network driver dispatch */
+
+static struct ssl_driver ssldriver = {
+ ssl_open, /* open connection */
+ ssl_aopen, /* open preauthenticated connection */
+ ssl_getline, /* get a line */
+ ssl_getbuffer, /* get a buffer */
+ ssl_soutr, /* output pushed data */
+ ssl_sout, /* output string */
+ ssl_close, /* close connection */
+ ssl_host, /* return host name */
+ ssl_remotehost, /* return remote host name */
+ ssl_port, /* return port number */
+ ssl_localhost /* return local host name */
+};
+ /* non-NIL if doing SSL primary I/O */
+static SSLSTDIOSTREAM *sslstdio = NIL;
+static char *start_tls = NIL; /* non-NIL if start TLS requested */
+
+/* One-time SSL initialization */
+
+static int sslonceonly = 0;
+
+void ssl_onceonlyinit (void)
+{
+ if (!sslonceonly++) { /* only need to call it once */
+ int fd;
+ char tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* if system doesn't have /dev/urandom */
+ if (stat ("/dev/urandom",&sbuf)) {
+ while ((fd = open (tmpnam (tmp),O_WRONLY|O_CREAT|O_EXCL,0600)) < 0)
+ sleep (1);
+ unlink (tmp); /* don't need the file */
+ fstat (fd,&sbuf); /* get information about the file */
+ close (fd); /* flush descriptor */
+ /* not great but it'll have to do */
+ sprintf (tmp + strlen (tmp),"%.80s%lx%.80s%lx%lx%lx%lx%lx",
+ tcp_serveraddr (),(unsigned long) tcp_serverport (),
+ tcp_clientaddr (),(unsigned long) tcp_clientport (),
+ (unsigned long) sbuf.st_ino,(unsigned long) time (0),
+ (unsigned long) gethostid (),(unsigned long) getpid ());
+ RAND_seed (tmp,strlen (tmp));
+ }
+ /* apply runtime linkage */
+ mail_parameters (NIL,SET_SSLDRIVER,(void *) &ssldriver);
+ mail_parameters (NIL,SET_SSLSTART,(void *) ssl_start);
+ SSL_library_init (); /* add all algorithms */
+ }
+}
+
+/* SSL open
+ * Accepts: host name
+ * contact service name
+ * contact port number
+ * Returns: SSL stream if success else NIL
+ */
+
+SSLSTREAM *ssl_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream = tcp_open (host,service,port);
+ return stream ? ssl_start (stream,host,port) : NIL;
+}
+
+
+/* SSL authenticated open
+ * Accepts: host name
+ * service name
+ * returned user name buffer
+ * Returns: SSL stream if success else NIL
+ */
+
+SSLSTREAM *ssl_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL; /* don't use this mechanism with SSL */
+}
+
+/* Start SSL/TLS negotiations
+ * Accepts: open TCP stream of session
+ * user's host name
+ * flags
+ * Returns: SSL stream if success else NIL
+ */
+
+static SSLSTREAM *ssl_start (TCPSTREAM *tstream,char *host,unsigned long flags)
+{
+ char *reason,tmp[MAILTMPLEN];
+ sslfailure_t sf = (sslfailure_t) mail_parameters (NIL,GET_SSLFAILURE,NIL);
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ void *data = (*bn) (BLOCK_SENSITIVE,NIL);
+ SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
+ sizeof (SSLSTREAM));
+ stream->tcpstream = tstream; /* bind TCP stream */
+ /* do the work */
+ reason = ssl_start_work (stream,host,flags);
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ if (reason) { /* failed? */
+ ssl_close (stream); /* failed to do SSL */
+ stream = NIL; /* no stream returned */
+ switch (*reason) { /* analyze reason */
+ case '*': /* certificate failure */
+ ++reason; /* skip over certificate failure indication */
+ /* pass to error callback */
+ if (sf) (*sf) (host,reason,flags);
+ else { /* no error callback, build error message */
+ sprintf (tmp,"Certificate failure for %.80s: %.512s",host,reason);
+ mm_log (tmp,ERROR);
+ }
+ case '\0': /* user answered no to certificate callback */
+ if (flags & NET_TRYSSL) /* return dummy stream to stop tryssl */
+ stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
+ sizeof (SSLSTREAM));
+ break;
+ default: /* non-certificate failure */
+ if (flags & NET_TRYSSL); /* no error output if tryssl */
+ /* pass to error callback */
+ else if (sf) (*sf) (host,reason,flags);
+ else { /* no error callback, build error message */
+ sprintf (tmp,"TLS/SSL failure for %.80s: %.512s",host,reason);
+ mm_log (tmp,ERROR);
+ }
+ break;
+ }
+ }
+ return stream;
+}
+
+/* Start SSL/TLS negotiations worker routine
+ * Accepts: SSL stream
+ * user's host name
+ * flags
+ * Returns: NIL if success, else error reason
+ */
+
+ /* evil but I had no choice */
+static char *ssl_last_error = NIL;
+static char *ssl_last_host = NIL;
+
+static char *ssl_start_work (SSLSTREAM *stream,char *host,unsigned long flags)
+{
+ BIO *bio;
+ X509 *cert;
+ unsigned long sl,tl;
+ char *s,*t,*err,tmp[MAILTMPLEN];
+ sslcertificatequery_t scq =
+ (sslcertificatequery_t) mail_parameters (NIL,GET_SSLCERTIFICATEQUERY,NIL);
+ sslclientcert_t scc =
+ (sslclientcert_t) mail_parameters (NIL,GET_SSLCLIENTCERT,NIL);
+ sslclientkey_t sck =
+ (sslclientkey_t) mail_parameters (NIL,GET_SSLCLIENTKEY,NIL);
+ if (ssl_last_error) fs_give ((void **) &ssl_last_error);
+ ssl_last_host = host;
+ if (!(stream->context = SSL_CTX_new ((flags & NET_TLSCLIENT) ?
+ TLSv1_client_method () :
+ SSLv23_client_method ())))
+ return "SSL context failed";
+ SSL_CTX_set_options (stream->context,0);
+ /* disable certificate validation? */
+ if (flags & NET_NOVALIDATECERT)
+ SSL_CTX_set_verify (stream->context,SSL_VERIFY_NONE,NIL);
+ else SSL_CTX_set_verify (stream->context,SSL_VERIFY_PEER,ssl_open_verify);
+ /* set default paths to CAs... */
+ SSL_CTX_set_default_verify_paths (stream->context);
+ /* ...unless a non-standard path desired */
+ if (s = (char *) mail_parameters (NIL,GET_SSLCAPATH,NIL))
+ SSL_CTX_load_verify_locations (stream->context,NIL,s);
+ /* want to send client certificate? */
+ if (scc && (s = (*scc) ()) && (sl = strlen (s))) {
+ if (cert = PEM_read_bio_X509 (bio = BIO_new_mem_buf (s,sl),NIL,NIL,NIL)) {
+ SSL_CTX_use_certificate (stream->context,cert);
+ X509_free (cert);
+ }
+ BIO_free (bio);
+ if (!cert) return "SSL client certificate failed";
+ /* want to supply private key? */
+ if ((t = (sck ? (*sck) () : s)) && (tl = strlen (t))) {
+ EVP_PKEY *key;
+ if (key = PEM_read_bio_PrivateKey (bio = BIO_new_mem_buf (t,tl),
+ NIL,NIL,"")) {
+ SSL_CTX_use_PrivateKey (stream->context,key);
+ EVP_PKEY_free (key);
+ }
+ BIO_free (bio);
+ memset (t,0,tl); /* erase key */
+ }
+ if (s != t) memset (s,0,sl);/* erase certificate if different from key */
+ }
+
+ /* create connection */
+ if (!(stream->con = (SSL *) SSL_new (stream->context)))
+ return "SSL connection failed";
+ bio = BIO_new_socket (stream->tcpstream->tcpsi,BIO_NOCLOSE);
+ SSL_set_bio (stream->con,bio,bio);
+ SSL_set_connect_state (stream->con);
+ if (SSL_in_init (stream->con)) SSL_total_renegotiations (stream->con);
+ /* now negotiate SSL */
+ if (SSL_write (stream->con,"",0) < 0)
+ return ssl_last_error ? ssl_last_error : "SSL negotiation failed";
+ /* need to validate host names? */
+ if (!(flags & NET_NOVALIDATECERT) &&
+ (err = ssl_validate_cert (cert = SSL_get_peer_certificate (stream->con),
+ host))) {
+ /* application callback */
+ if (scq) return (*scq) (err,host,cert ? cert->name : "???") ? NIL : "";
+ /* error message to return via mm_log() */
+ sprintf (tmp,"*%.128s: %.255s",err,cert ? cert->name : "???");
+ return ssl_last_error = cpystr (tmp);
+ }
+ return NIL;
+}
+
+/* SSL certificate verification callback
+ * Accepts: error flag
+ * X509 context
+ * Returns: error flag
+ */
+
+static int ssl_open_verify (int ok,X509_STORE_CTX *ctx)
+{
+ char *err,cert[256],tmp[MAILTMPLEN];
+ sslcertificatequery_t scq =
+ (sslcertificatequery_t) mail_parameters (NIL,GET_SSLCERTIFICATEQUERY,NIL);
+ if (!ok) { /* in case failure */
+ err = (char *) X509_verify_cert_error_string
+ (X509_STORE_CTX_get_error (ctx));
+ X509_NAME_oneline (X509_get_subject_name
+ (X509_STORE_CTX_get_current_cert (ctx)),cert,255);
+ if (!scq) { /* mm_log() error message if no callback */
+ sprintf (tmp,"*%.128s: %.255s",err,cert);
+ ssl_last_error = cpystr (tmp);
+ }
+ /* ignore error if application says to */
+ else if ((*scq) (err,ssl_last_host,cert)) ok = T;
+ /* application wants punt */
+ else ssl_last_error = cpystr ("");
+ }
+ return ok;
+}
+
+
+/* SSL validate certificate
+ * Accepts: certificate
+ * host to validate against
+ * Returns: NIL if validated, else string of error message
+ */
+
+static char *ssl_validate_cert (X509 *cert,char *host)
+{
+ int i,n;
+ char *s,*t,*ret;
+ void *ext;
+ GENERAL_NAME *name;
+ /* make sure have a certificate */
+ if (!cert) ret = "No certificate from server";
+ /* and that it has a name */
+ else if (!cert->name) ret = "No name in certificate";
+ /* locate CN */
+ else if (s = strstr (cert->name,"/CN=")) {
+ if (t = strchr (s += 4,'/')) *t = '\0';
+ /* host name matches pattern? */
+ ret = ssl_compare_hostnames (host,s) ? NIL :
+ "Server name does not match certificate";
+ if (t) *t = '/'; /* restore smashed delimiter */
+ /* if mismatch, see if in extensions */
+ if (ret && (ext = X509_get_ext_d2i (cert,NID_subject_alt_name,NIL,NIL)) &&
+ (n = sk_GENERAL_NAME_num (ext)))
+ /* older versions of OpenSSL use "ia5" instead of dNSName */
+ for (i = 0; ret && (i < n); i++)
+ if ((name = sk_GENERAL_NAME_value (ext,i)) &&
+ (name->type = GEN_DNS) && (s = name->d.ia5->data) &&
+ ssl_compare_hostnames (host,s)) ret = NIL;
+ }
+ else ret = "Unable to locate common name in certificate";
+ return ret;
+}
+
+/* Case-independent wildcard pattern match
+ * Accepts: base string
+ * pattern string
+ * Returns: T if pattern matches base, else NIL
+ */
+
+static long ssl_compare_hostnames (unsigned char *s,unsigned char *pat)
+{
+ long ret = NIL;
+ switch (*pat) {
+ case '*': /* wildcard */
+ if (pat[1]) { /* there must be a pattern suffix */
+ /* there is, scan base against it */
+ do if (ssl_compare_hostnames (s,pat+1)) ret = LONGT;
+ while (!ret && (*s != '.') && *s++);
+ }
+ break;
+ case '\0': /* end of pattern */
+ if (!*s) ret = LONGT; /* success if base is also at end */
+ break;
+ default: /* non-wildcard, recurse if match */
+ if (!compare_uchar (*pat,*s)) ret = ssl_compare_hostnames (s+1,pat+1);
+ break;
+ }
+ return ret;
+}
+
+/* SSL receive line
+ * Accepts: SSL stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *ssl_getline (SSLSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = ssl_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = ssl_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* SSL receive line or partial line
+ * Accepts: SSL stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!ssl_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!ssl_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* SSL receive buffer
+ * Accepts: SSL stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long ssl_getbuffer (SSLSTREAM *stream,unsigned long size,char *buffer)
+{
+ unsigned long n;
+ while (size > 0) { /* until request satisfied */
+ if (!ssl_getdata (stream)) return NIL;
+ n = min (size,stream->ictr);/* number of bytes to transfer */
+ /* do the copy */
+ memcpy (buffer,stream->iptr,n);
+ buffer += n; /* update pointer */
+ stream->iptr += n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -= n;
+ }
+ buffer[0] = '\0'; /* tie off string */
+ return T;
+}
+
+/* SSL receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long ssl_getdata (SSLSTREAM *stream)
+{
+ int i,sock;
+ fd_set fds,efds;
+ struct timeval tmo;
+ tcptimeout_t tmoh = (tcptimeout_t) mail_parameters (NIL,GET_TIMEOUT,NIL);
+ long ttmo_read = (long) mail_parameters (NIL,GET_READTIMEOUT,NIL);
+ time_t t = time (0);
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (!stream->con || ((sock = SSL_get_fd (stream->con)) < 0)) return NIL;
+ /* tcp_unix should have prevented this */
+ if (sock >= FD_SETSIZE) fatal ("unselectable socket in ssl_getdata()");
+ (*bn) (BLOCK_TCPREAD,NIL);
+ while (stream->ictr < 1) { /* if nothing in the buffer */
+ time_t tl = time (0); /* start of request */
+ time_t now = tl;
+ int ti = ttmo_read ? now + ttmo_read : 0;
+ if (SSL_pending (stream->con)) i = 1;
+ else {
+ if (tcpdebug) mm_log ("Reading SSL data",TCPDEBUG);
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ FD_SET (sock,&fds); /* set bit in selection vector */
+ FD_SET (sock,&efds); /* set bit in error selection vector */
+ errno = NIL; /* block and read */
+ do { /* block under timeout */
+ tmo.tv_sec = ti ? ti - now : 0;
+ i = select (sock+1,&fds,0,&efds,ti ? &tmo : 0);
+ now = time (0); /* fake timeout if interrupt & time expired */
+ if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
+ } while ((i < 0) && (errno == EINTR));
+ }
+ if (i) { /* non-timeout result from select? */
+ errno = 0; /* just in case */
+ if (i > 0) /* read what we can */
+ while (((i = SSL_read (stream->con,stream->ibuf,SSLBUFLEN)) < 0) &&
+ ((errno == EINTR) ||
+ (SSL_get_error (stream->con,i) == SSL_ERROR_WANT_READ)));
+ if (i <= 0) { /* error seen? */
+ if (tcpdebug) {
+ char *s,tmp[MAILTMPLEN];
+ if (i) sprintf (s = tmp,"SSL data read I/O error %d SSL error %d",
+ errno,SSL_get_error (stream->con,i));
+ else s = "SSL data read end of file";
+ mm_log (s,TCPDEBUG);
+ }
+ return ssl_abort (stream);
+ }
+ stream->iptr = stream->ibuf;/* point at TCP buffer */
+ stream->ictr = i; /* set new byte count */
+ if (tcpdebug) mm_log ("Successfully read SSL data",TCPDEBUG);
+ }
+ /* timeout, punt unless told not to */
+ else if (!tmoh || !(*tmoh) (now - t,now - tl, stream->tcpstream->host)) {
+ if (tcpdebug) mm_log ("SSL data read timeout",TCPDEBUG);
+ return ssl_abort (stream);
+ }
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return T;
+}
+
+/* SSL send string as record
+ * Accepts: SSL stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long ssl_soutr (SSLSTREAM *stream,char *string)
+{
+ return ssl_sout (stream,string,(unsigned long) strlen (string));
+}
+
+
+/* SSL send string
+ * Accepts: SSL stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long ssl_sout (SSLSTREAM *stream,char *string,unsigned long size)
+{
+ long i;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (!stream->con) return NIL;
+ (*bn) (BLOCK_TCPWRITE,NIL);
+ if (tcpdebug) mm_log ("Writing to SSL",TCPDEBUG);
+ /* until request satisfied */
+ for (i = 0; size > 0; string += i,size -= i)
+ /* write as much as we can */
+ if ((i = SSL_write (stream->con,string,(int) min (SSLBUFLEN,size))) < 0) {
+ if (tcpdebug) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"SSL data write I/O error %d SSL error %d",
+ errno,SSL_get_error (stream->con,i));
+ mm_log (tmp,TCPDEBUG);
+ }
+ return ssl_abort (stream);/* write failed */
+ }
+ if (tcpdebug) mm_log ("successfully wrote to TCP",TCPDEBUG);
+ (*bn) (BLOCK_NONE,NIL);
+ return LONGT; /* all done */
+}
+
+/* SSL close
+ * Accepts: SSL stream
+ */
+
+void ssl_close (SSLSTREAM *stream)
+{
+ ssl_abort (stream); /* nuke the stream */
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+
+/* SSL abort stream
+ * Accepts: SSL stream
+ * Returns: NIL always
+ */
+
+static long ssl_abort (SSLSTREAM *stream)
+{
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (stream->con) { /* close SSL connection */
+ SSL_shutdown (stream->con);
+ SSL_free (stream->con);
+ stream->con = NIL;
+ }
+ if (stream->context) { /* clean up context */
+ SSL_CTX_free (stream->context);
+ stream->context = NIL;
+ }
+ if (stream->tcpstream) { /* close TCP stream */
+ tcp_close (stream->tcpstream);
+ stream->tcpstream = NIL;
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return NIL;
+}
+
+/* SSL get host name
+ * Accepts: SSL stream
+ * Returns: host name for this stream
+ */
+
+char *ssl_host (SSLSTREAM *stream)
+{
+ return tcp_host (stream->tcpstream);
+}
+
+
+/* SSL get remote host name
+ * Accepts: SSL stream
+ * Returns: host name for this stream
+ */
+
+char *ssl_remotehost (SSLSTREAM *stream)
+{
+ return tcp_remotehost (stream->tcpstream);
+}
+
+
+/* SSL return port for this stream
+ * Accepts: SSL stream
+ * Returns: port number for this stream
+ */
+
+unsigned long ssl_port (SSLSTREAM *stream)
+{
+ return tcp_port (stream->tcpstream);
+}
+
+
+/* SSL get local host name
+ * Accepts: SSL stream
+ * Returns: local host name
+ */
+
+char *ssl_localhost (SSLSTREAM *stream)
+{
+ return tcp_localhost (stream->tcpstream);
+}
+
+/* Start TLS
+ * Accepts: /etc/services service name
+ * Returns: cpystr'd error string if TLS failed, else NIL for success
+ */
+
+char *ssl_start_tls (char *server)
+{
+ char tmp[MAILTMPLEN];
+ struct stat sbuf;
+ if (sslstdio) return cpystr ("Already in an SSL session");
+ if (start_tls) return cpystr ("TLS already started");
+ if (server) { /* build specific certificate/key file name */
+ sprintf (tmp,"%s/%s-%s.pem",SSL_CERT_DIRECTORY,server,tcp_serveraddr ());
+ if (stat (tmp,&sbuf)) { /* use non-specific name if no specific file */
+ sprintf (tmp,"%s/%s.pem",SSL_CERT_DIRECTORY,server);
+ if (stat (tmp,&sbuf)) return cpystr ("Server certificate not installed");
+ }
+ start_tls = server; /* switch to STARTTLS mode */
+ }
+ return NIL;
+}
+
+/* Init server for SSL
+ * Accepts: server name
+ */
+
+void ssl_server_init (char *server)
+{
+ char cert[MAILTMPLEN],key[MAILTMPLEN];
+ unsigned long i;
+ struct stat sbuf;
+ SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
+ sizeof (SSLSTREAM));
+ ssl_onceonlyinit (); /* make sure algorithms added */
+ ERR_load_crypto_strings ();
+ SSL_load_error_strings ();
+ /* build specific certificate/key file names */
+ sprintf (cert,"%s/%s-%s.pem",SSL_CERT_DIRECTORY,server,tcp_serveraddr ());
+ sprintf (key,"%s/%s-%s.pem",SSL_KEY_DIRECTORY,server,tcp_serveraddr ());
+ /* use non-specific name if no specific cert */
+ if (stat (cert,&sbuf)) sprintf (cert,"%s/%s.pem",SSL_CERT_DIRECTORY,server);
+ if (stat (key,&sbuf)) { /* use non-specific name if no specific key */
+ sprintf (key,"%s/%s.pem",SSL_KEY_DIRECTORY,server);
+ /* use cert file as fallback for key */
+ if (stat (key,&sbuf)) strcpy (key,cert);
+ }
+ /* create context */
+ if (!(stream->context = SSL_CTX_new (start_tls ?
+ TLSv1_server_method () :
+ SSLv23_server_method ())))
+ syslog (LOG_ALERT,"Unable to create SSL context, host=%.80s",
+ tcp_clienthost ());
+ else { /* set context options */
+ SSL_CTX_set_options (stream->context,SSL_OP_ALL);
+ /* set cipher list */
+ if (!SSL_CTX_set_cipher_list (stream->context,SSLCIPHERLIST))
+ syslog (LOG_ALERT,"Unable to set cipher list %.80s, host=%.80s",
+ SSLCIPHERLIST,tcp_clienthost ());
+ /* load certificate */
+ else if (!SSL_CTX_use_certificate_chain_file (stream->context,cert))
+ syslog (LOG_ALERT,"Unable to load certificate from %.80s, host=%.80s",
+ cert,tcp_clienthost ());
+ /* load key */
+ else if (!(SSL_CTX_use_RSAPrivateKey_file (stream->context,key,
+ SSL_FILETYPE_PEM)))
+ syslog (LOG_ALERT,"Unable to load private key from %.80s, host=%.80s",
+ key,tcp_clienthost ());
+
+ else { /* generate key if needed */
+ if (SSL_CTX_need_tmp_RSA (stream->context))
+ SSL_CTX_set_tmp_rsa_callback (stream->context,ssl_genkey);
+ /* create new SSL connection */
+ if (!(stream->con = SSL_new (stream->context)))
+ syslog (LOG_ALERT,"Unable to create SSL connection, host=%.80s",
+ tcp_clienthost ());
+ else { /* set file descriptor */
+ SSL_set_fd (stream->con,0);
+ /* all OK if accepted */
+ if (SSL_accept (stream->con) < 0)
+ syslog (LOG_INFO,"Unable to accept SSL connection, host=%.80s",
+ tcp_clienthost ());
+ else { /* server set up */
+ sslstdio = (SSLSTDIOSTREAM *)
+ memset (fs_get (sizeof(SSLSTDIOSTREAM)),0,sizeof (SSLSTDIOSTREAM));
+ sslstdio->sslstream = stream;
+ /* available space in output buffer */
+ sslstdio->octr = SSLBUFLEN;
+ /* current output buffer pointer */
+ sslstdio->optr = sslstdio->obuf;
+ /* allow plaintext if disable value was 2 */
+ if ((long) mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL) > 1)
+ mail_parameters (NIL,SET_DISABLEPLAINTEXT,NIL);
+ /* unhide PLAIN SASL authenticator */
+ mail_parameters (NIL,UNHIDE_AUTHENTICATOR,"PLAIN");
+ mail_parameters (NIL,UNHIDE_AUTHENTICATOR,"LOGIN");
+ return;
+ }
+ }
+ }
+ }
+ while (i = ERR_get_error ()) /* SSL failure */
+ syslog (LOG_ERR,"SSL error status: %.80s",ERR_error_string (i,NIL));
+ ssl_close (stream); /* punt stream */
+ exit (1); /* punt this program too */
+}
+
+/* Generate one-time key for server
+ * Accepts: SSL connection
+ * export flag
+ * keylength
+ * Returns: generated key, always
+ */
+
+static RSA *ssl_genkey (SSL *con,int export,int keylength)
+{
+ unsigned long i;
+ static RSA *key = NIL;
+ if (!key) { /* if don't have a key already */
+ /* generate key */
+ if (!(key = RSA_generate_key (export ? keylength : 1024,RSA_F4,NIL,NIL))) {
+ syslog (LOG_ALERT,"Unable to generate temp key, host=%.80s",
+ tcp_clienthost ());
+ while (i = ERR_get_error ())
+ syslog (LOG_ALERT,"SSL error status: %s",ERR_error_string (i,NIL));
+ exit (1);
+ }
+ }
+ return key;
+}
+
+/* Wait for stdin input
+ * Accepts: timeout in seconds
+ * Returns: T if have input on stdin, else NIL
+ */
+
+long ssl_server_input_wait (long seconds)
+{
+ int i,sock;
+ fd_set fds,efd;
+ struct timeval tmo;
+ SSLSTREAM *stream;
+ if (!sslstdio) return server_input_wait (seconds);
+ /* input available in buffer */
+ if (((stream = sslstdio->sslstream)->ictr > 0) ||
+ !stream->con || ((sock = SSL_get_fd (stream->con)) < 0)) return LONGT;
+ /* sock ought to be 0 always */
+ if (sock >= FD_SETSIZE) fatal ("unselectable socket in ssl_getdata()");
+ /* input available from SSL */
+ if (SSL_pending (stream->con) &&
+ ((i = SSL_read (stream->con,stream->ibuf,SSLBUFLEN)) > 0)) {
+ stream->iptr = stream->ibuf;/* point at TCP buffer */
+ stream->ictr = i; /* set new byte count */
+ return LONGT;
+ }
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efd); /* initialize selection vector */
+ FD_SET (sock,&fds); /* set bit in selection vector */
+ FD_SET (sock,&efd); /* set bit in selection vector */
+ tmo.tv_sec = seconds; tmo.tv_usec = 0;
+ /* see if input available from the socket */
+ return select (sock+1,&fds,0,&efd,&tmo) ? LONGT : NIL;
+}
+
+#include "sslstdio.c"
diff --git a/imap/src/osdep/unix/sslstdio.c b/imap/src/osdep/unix/sslstdio.c
new file mode 100644
index 00000000..4059a859
--- /dev/null
+++ b/imap/src/osdep/unix/sslstdio.c
@@ -0,0 +1,169 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: SSL standard I/O routines for server use
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 22 September 1998
+ * Last Edited: 30 August 2006
+ */
+
+/* Get character
+ * Returns: character or EOF
+ */
+
+int PBIN (void)
+{
+ if (!sslstdio) return getchar ();
+ if (!ssl_getdata (sslstdio->sslstream)) return EOF;
+ /* one last byte available */
+ sslstdio->sslstream->ictr--;
+ return (int) *(sslstdio->sslstream->iptr)++;
+}
+
+
+/* Get string
+ * Accepts: destination string pointer
+ * number of bytes available
+ * Returns: destination string pointer or NIL if EOF
+ */
+
+char *PSIN (char *s,int n)
+{
+ int i,c;
+ if (start_tls) { /* doing a start TLS? */
+ ssl_server_init (start_tls);/* enter the mode */
+ start_tls = NIL; /* don't do this again */
+ }
+ if (!sslstdio) return fgets (s,n,stdin);
+ for (i = c = 0, n-- ; (c != '\n') && (i < n); sslstdio->sslstream->ictr--) {
+ if ((sslstdio->sslstream->ictr <= 0) && !ssl_getdata (sslstdio->sslstream))
+ return NIL; /* read error */
+ c = s[i++] = *(sslstdio->sslstream->iptr)++;
+ }
+ s[i] = '\0'; /* tie off string */
+ return s;
+}
+
+
+/* Get record
+ * Accepts: destination string pointer
+ * number of bytes to read
+ * Returns: T if success, NIL otherwise
+ */
+
+long PSINR (char *s,unsigned long n)
+{
+ unsigned long i;
+ if (start_tls) { /* doing a start TLS? */
+ ssl_server_init (start_tls);/* enter the mode */
+ start_tls = NIL; /* don't do this again */
+ }
+ if (sslstdio) return ssl_getbuffer (sslstdio->sslstream,n,s);
+ /* non-SSL case */
+ while (n && ((i = fread (s,1,n,stdin)) || (errno == EINTR))) s += i,n -= i;
+ return n ? NIL : LONGT;
+}
+
+
+/* Wait for stdin input
+ * Accepts: timeout in seconds
+ * Returns: T if have input on stdin, else NIL
+ */
+
+long INWAIT (long seconds)
+{
+ return (sslstdio ? ssl_server_input_wait : server_input_wait) (seconds);
+}
+
+/* Put character
+ * Accepts: character
+ * Returns: character written or EOF
+ */
+
+int PBOUT (int c)
+{
+ if (!sslstdio) return putchar (c);
+ /* flush buffer if full */
+ if (!sslstdio->octr && PFLUSH ()) return EOF;
+ sslstdio->octr--; /* count down one character */
+ *sslstdio->optr++ = c; /* write character */
+ return c; /* return that character */
+}
+
+
+/* Put string
+ * Accepts: destination string pointer
+ * Returns: 0 or EOF if error
+ */
+
+int PSOUT (char *s)
+{
+ if (!sslstdio) return fputs (s,stdout);
+ while (*s) { /* flush buffer if full */
+ if (!sslstdio->octr && PFLUSH ()) return EOF;
+ *sslstdio->optr++ = *s++; /* write one more character */
+ sslstdio->octr--; /* count down one character */
+ }
+ return 0; /* success */
+}
+
+/* Put record
+ * Accepts: source sized text
+ * Returns: 0 or EOF if error
+ */
+
+int PSOUTR (SIZEDTEXT *s)
+{
+ unsigned char *t = s->data;
+ unsigned long i = s->size;
+ unsigned long j;
+ if (sslstdio) while (i) { /* until request satisfied */
+ /* flush buffer if full */
+ if (!sslstdio->octr && PFLUSH ()) break;
+ /* blat as big a chucnk as we can */
+ memcpy (sslstdio->optr,t,j = min (i,sslstdio->octr));
+ sslstdio->optr += j; /* account for chunk */
+ sslstdio->octr -= j;
+ t += j;
+ i -= j;
+ }
+ else while (i && ((j = fwrite (t,1,i,stdout)) || (errno == EINTR)))
+ t += j,i -= j;
+ return i ? EOF : NIL;
+}
+
+
+/* Flush output
+ * Returns: 0 or EOF if error
+ */
+
+int PFLUSH (void)
+{
+ if (!sslstdio) return fflush (stdout);
+ /* force out buffer */
+ if (!ssl_sout (sslstdio->sslstream,sslstdio->obuf,
+ SSLBUFLEN - sslstdio->octr)) return EOF;
+ /* renew output buffer */
+ sslstdio->optr = sslstdio->obuf;
+ sslstdio->octr = SSLBUFLEN;
+ return 0; /* success */
+}
diff --git a/imap/src/osdep/unix/strerror.c b/imap/src/osdep/unix/strerror.c
new file mode 100644
index 00000000..3c2e6a14
--- /dev/null
+++ b/imap/src/osdep/unix/strerror.c
@@ -0,0 +1,37 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Error number to string
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Return implementation-defined string corresponding to error
+ * Accepts: error number
+ * Returns: string for that error
+ */
+
+char *strerror (int n)
+{
+ return (n >= 0 && n < sys_nerr) ? sys_errlist[n] : NIL;
+}
diff --git a/imap/src/osdep/unix/tcp_unix.c b/imap/src/osdep/unix/tcp_unix.c
new file mode 100644
index 00000000..127cf2ae
--- /dev/null
+++ b/imap/src/osdep/unix/tcp_unix.c
@@ -0,0 +1,1043 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX TCP/IP routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 13 January 2008
+ */
+
+#include "ip_unix.c"
+
+#undef write /* don't use redefined write() */
+
+static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */
+static long ttmo_open = 0; /* TCP timeouts, in seconds */
+static long ttmo_read = 0;
+static long ttmo_write = 0;
+static long rshtimeout = 15; /* rsh timeout */
+static char *rshcommand = NIL; /* rsh command */
+static char *rshpath = NIL; /* rsh path */
+static long sshtimeout = 15; /* ssh timeout */
+static char *sshcommand = NIL; /* ssh command */
+static char *sshpath = NIL; /* ssh path */
+static long allowreversedns = T;/* allow reverse DNS lookup */
+static long tcpdebug = NIL; /* extra TCP debugging telemetry */
+static char *myClientAddr = NIL;/* client IP address */
+static char *myClientHost = NIL;/* client DNS name */
+static long myClientPort = -1; /* client port number */
+static char *myServerAddr = NIL;/* server IP address */
+static char *myServerHost = NIL;/* server DNS name */
+static long myServerPort = -1; /* server port number */
+
+extern long maxposint; /* get this from write.c */
+
+/* Local function prototypes */
+
+int tcp_socket_open (int family,void *adr,size_t adrlen,unsigned short port,
+ char *tmp,int *ctr,char *hst);
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd);
+long tcp_abort (TCPSTREAM *stream);
+char *tcp_name (struct sockaddr *sadr,long flag);
+char *tcp_name_valid (char *s);
+
+/* TCP/IP manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tcp_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_TIMEOUT:
+ tmoh = (tcptimeout_t) value;
+ case GET_TIMEOUT:
+ ret = (void *) tmoh;
+ break;
+ case SET_OPENTIMEOUT:
+ ttmo_open = (long) value;
+ case GET_OPENTIMEOUT:
+ ret = (void *) ttmo_open;
+ break;
+ case SET_READTIMEOUT:
+ ttmo_read = (long) value;
+ case GET_READTIMEOUT:
+ ret = (void *) ttmo_read;
+ break;
+ case SET_WRITETIMEOUT:
+ ttmo_write = (long) value;
+ case GET_WRITETIMEOUT:
+ ret = (void *) ttmo_write;
+ break;
+ case SET_ALLOWREVERSEDNS:
+ allowreversedns = (long) value;
+ case GET_ALLOWREVERSEDNS:
+ ret = (void *) allowreversedns;
+ break;
+ case SET_TCPDEBUG:
+ tcpdebug = (long) value;
+ case GET_TCPDEBUG:
+ ret = (void *) tcpdebug;
+ break;
+
+ case SET_RSHTIMEOUT:
+ rshtimeout = (long) value;
+ case GET_RSHTIMEOUT:
+ ret = (void *) rshtimeout;
+ break;
+ case SET_RSHCOMMAND:
+ if (rshcommand) fs_give ((void **) &rshcommand);
+ rshcommand = cpystr ((char *) value);
+ case GET_RSHCOMMAND:
+ ret = (void *) rshcommand;
+ break;
+ case SET_RSHPATH:
+ if (rshpath) fs_give ((void **) &rshpath);
+ rshpath = cpystr ((char *) value);
+ case GET_RSHPATH:
+ ret = (void *) rshpath;
+ break;
+ case SET_SSHTIMEOUT:
+ sshtimeout = (long) value;
+ case GET_SSHTIMEOUT:
+ ret = (void *) sshtimeout;
+ break;
+ case SET_SSHCOMMAND:
+ if (sshcommand) fs_give ((void **) &sshcommand);
+ sshcommand = cpystr ((char *) value);
+ case GET_SSHCOMMAND:
+ ret = (void *) sshcommand;
+ break;
+ case SET_SSHPATH:
+ if (sshpath) fs_give ((void **) &sshpath);
+ sshpath = cpystr ((char *) value);
+ case GET_SSHPATH:
+ ret = (void *) sshpath;
+ break;
+ }
+ return ret;
+}
+
+/* TCP/IP open
+ * Accepts: host name
+ * contact service name
+ * contact port number and optional silent flag
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream = NIL;
+ int family;
+ int sock = -1;
+ int ctr = 0;
+ int silent = (port & NET_SILENT) ? T : NIL;
+ int *ctrp = (port & NET_NOOPENTIMEOUT) ? NIL : &ctr;
+ char *s,*hostname,tmp[MAILTMPLEN];
+ void *adr;
+ size_t adrlen;
+ struct servent *sv = NIL;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ void *data,*next;
+ port &= 0xffff; /* erase flags */
+ /* lookup service */
+ if (service && (sv = getservbyname (service,"tcp")))
+ port = ntohs (sv->s_port);
+ /* The domain literal form is used (rather than simply the dotted decimal
+ as with other Unix programs) because it has to be a valid "host name"
+ in mailsystem terminology. */
+ /* look like domain literal? */
+ if (host[0] == '[' && host[(strlen (host))-1] == ']') {
+ strcpy (tmp,host+1); /* yes, copy number part */
+ tmp[(strlen (tmp))-1] = '\0';
+ if (adr = ip_stringtoaddr (tmp,&adrlen,&family)) {
+ (*bn) (BLOCK_TCPOPEN,NIL);
+ /* get an open socket for this system */
+ sock = tcp_socket_open (family,adr,adrlen,port,tmp,ctrp,hostname = host);
+ (*bn) (BLOCK_NONE,NIL);
+ fs_give ((void **) &adr);
+ }
+ else sprintf (tmp,"Bad format domain-literal: %.80s",host);
+ }
+
+ else { /* lookup host name */
+ if (tcpdebug) {
+ sprintf (tmp,"DNS resolution %.80s",host);
+ mm_log (tmp,TCPDEBUG);
+ }
+ (*bn) (BLOCK_DNSLOOKUP,NIL);/* quell alarms */
+ data = (*bn) (BLOCK_SENSITIVE,NIL);
+ if (!(s = ip_nametoaddr (host,&adrlen,&family,&hostname,&next)))
+ sprintf (tmp,"No such host as %.80s",host);
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ (*bn) (BLOCK_NONE,NIL);
+ if (s) { /* DNS resolution won? */
+ if (tcpdebug) mm_log ("DNS resolution done",TCPDEBUG);
+ do {
+ (*bn) (BLOCK_TCPOPEN,NIL);
+ if (((sock = tcp_socket_open (family,s,adrlen,port,tmp,ctrp,
+ hostname)) < 0) &&
+ (s = ip_nametoaddr (NIL,&adrlen,&family,&hostname,&next)) &&
+ !silent) mm_log (tmp,WARN);
+ (*bn) (BLOCK_NONE,NIL);
+ } while ((sock < 0) && s);/* repeat until success or no more addreses */
+ }
+ }
+ if (sock >= 0) { /* won */
+ stream = (TCPSTREAM *) memset (fs_get (sizeof (TCPSTREAM)),0,
+ sizeof (TCPSTREAM));
+ stream->port = port; /* port number */
+ /* init sockets */
+ stream->tcpsi = stream->tcpso = sock;
+ /* stash in the snuck-in byte */
+ if (stream->ictr = ctr) *(stream->iptr = stream->ibuf) = tmp[0];
+ /* copy official host name */
+ stream->host = cpystr (hostname);
+ if (tcpdebug) mm_log ("Stream open and ready for read",TCPDEBUG);
+ }
+ else if (!silent) mm_log (tmp,ERROR);
+ return stream; /* return success */
+}
+
+/* Open a TCP socket
+ * Accepts: protocol family
+ * address to connect to
+ * address length
+ * port
+ * scratch buffer
+ * pointer to "first byte read in" storage or NIL
+ * host name for error message
+ * Returns: socket if success, else -1 with error string in scratch buffer
+ */
+
+int tcp_socket_open (int family,void *adr,size_t adrlen,unsigned short port,
+ char *tmp,int *ctr,char *hst)
+{
+ int i,ti,sock,flgs;
+ size_t len;
+ time_t now;
+ struct protoent *pt = getprotobyname ("tcp");
+ fd_set fds,efds;
+ struct timeval tmo;
+ struct sockaddr *sadr = ip_sockaddr (family,adr,adrlen,port,&len);
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ /* fetid Solaris */
+ void *data = (*bn) (BLOCK_SENSITIVE,NIL);
+ sprintf (tmp,"Trying IP address [%s]",ip_sockaddrtostring (sadr));
+ mm_log (tmp,NIL);
+ /* make a socket */
+ if ((sock = socket (sadr->sa_family,SOCK_STREAM,pt ? pt->p_proto : 0)) < 0) {
+ sprintf (tmp,"Unable to create TCP socket: %s",strerror (errno));
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ }
+ else if (sock >= FD_SETSIZE) {/* unselectable sockets are useless */
+ sprintf (tmp,"Unable to create selectable TCP socket (%d >= %d)",
+ sock,FD_SETSIZE);
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ close (sock);
+ sock = -1;
+ errno = EMFILE;
+ }
+
+ else { /* get current socket flags */
+ flgs = fcntl (sock,F_GETFL,0);
+ /* set non-blocking if want open timeout */
+ if (ctr) fcntl (sock,F_SETFL,flgs | FNDELAY);
+ /* open connection */
+ while ((i = connect (sock,sadr,len)) < 0 && (errno == EINTR));
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ if (i < 0) switch (errno) { /* failed? */
+ case EAGAIN: /* DG brain damage */
+ case EINPROGRESS: /* what we expect to happen */
+ case EALREADY: /* or another form of it */
+ case EISCONN: /* restart after interrupt? */
+ case EADDRINUSE: /* restart after interrupt? */
+ break; /* well, not really, it was interrupted */
+ default:
+ sprintf (tmp,"Can't connect to %.80s,%u: %s",hst,(unsigned int) port,
+ strerror (errno));
+ close (sock); /* flush socket */
+ sock = -1;
+ }
+ if ((sock >= 0) && ctr) { /* want open timeout? */
+ now = time (0); /* open timeout */
+ ti = ttmo_open ? now + ttmo_open : 0;
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ FD_SET (sock,&fds); /* block for error or readable */
+ FD_SET (sock,&efds);
+ do { /* block under timeout */
+ tmo.tv_sec = ti ? ti - now : 0;
+ i = select (sock+1,&fds,NIL,&efds,ti ? &tmo : NIL);
+ now = time (0); /* fake timeout if interrupt & time expired */
+ if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
+ } while ((i < 0) && (errno == EINTR));
+ if (i > 0) { /* success, make sure really connected */
+ /* restore blocking status */
+ fcntl (sock,F_SETFL,flgs);
+ /* This used to be a zero-byte read(), but that crashes Solaris */
+ /* get socket status */
+ while (((i = *ctr = read (sock,tmp,1)) < 0) && (errno == EINTR));
+ }
+ if (i <= 0) { /* timeout or error? */
+ i = i ? errno : ETIMEDOUT;/* determine error code */
+ close (sock); /* flush socket */
+ sock = -1;
+ errno = i; /* return error code */
+ sprintf (tmp,"Connection failed to %.80s,%lu: %s",hst,
+ (unsigned long) port,strerror (errno));
+ }
+ }
+ }
+ fs_give ((void **) &sadr);
+ return sock; /* return the socket */
+}
+
+/* TCP/IP authenticated open
+ * Accepts: host name
+ * service name
+ * returned user name buffer
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+#define MAXARGV 20
+
+TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ TCPSTREAM *stream = NIL;
+ void *adr;
+ char host[MAILTMPLEN],tmp[MAILTMPLEN],*path,*argv[MAXARGV+1],*r;
+ int i,ti,pipei[2],pipeo[2];
+ size_t len;
+ time_t now;
+ struct timeval tmo;
+ fd_set fds,efds;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+#ifdef SSHPATH /* ssh path defined yet? */
+ if (!sshpath) sshpath = cpystr (SSHPATH);
+#endif
+#ifdef RSHPATH /* rsh path defined yet? */
+ if (!rshpath) rshpath = cpystr (RSHPATH);
+#endif
+ if (*service == '*') { /* want ssh? */
+ /* return immediately if ssh disabled */
+ if (!(sshpath && (ti = sshtimeout))) return NIL;
+ /* ssh command prototype defined yet? */
+ if (!sshcommand) sshcommand = cpystr ("%s %s -l %s exec /etc/r%sd");
+ }
+ /* want rsh? */
+ else if (rshpath && (ti = rshtimeout)) {
+ /* rsh command prototype defined yet? */
+ if (!rshcommand) rshcommand = cpystr ("%s %s -l %s exec /etc/r%sd");
+ }
+ else return NIL; /* rsh disabled */
+ /* look like domain literal? */
+ if (mb->host[0] == '[' && mb->host[i = (strlen (mb->host))-1] == ']') {
+ strcpy (host,mb->host+1); /* yes, copy without brackets */
+ host[i-1] = '\0';
+ /* validate domain literal */
+ if (adr = ip_stringtoaddr (host,&len,&i)) fs_give ((void **) &adr);
+ else {
+ sprintf (tmp,"Bad format domain-literal: %.80s",host);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ }
+ else strcpy (host,tcp_canonical (mb->host));
+
+ if (*service == '*') /* build ssh command */
+ sprintf (tmp,sshcommand,sshpath,host,
+ mb->user[0] ? mb->user : myusername (),service + 1);
+ else sprintf (tmp,rshcommand,rshpath,host,
+ mb->user[0] ? mb->user : myusername (),service);
+ if (tcpdebug) {
+ char msg[MAILTMPLEN];
+ sprintf (msg,"Trying %.100s",tmp);
+ mm_log (msg,TCPDEBUG);
+ }
+ /* parse command into argv */
+ for (i = 1,path = argv[0] = strtok_r (tmp," ",&r);
+ (i < MAXARGV) && (argv[i] = strtok_r (NIL," ",&r)); i++);
+ argv[i] = NIL; /* make sure argv tied off */
+ /* make command pipes */
+ if (pipe (pipei) < 0) return NIL;
+ if ((pipei[0] >= FD_SETSIZE) || (pipei[1] >= FD_SETSIZE) ||
+ (pipe (pipeo) < 0)) {
+ close (pipei[0]); close (pipei[1]);
+ return NIL;
+ }
+ (*bn) (BLOCK_TCPOPEN,NIL); /* quell alarm up here for NeXT */
+ if ((pipeo[0] >= FD_SETSIZE) || (pipeo[1] >= FD_SETSIZE) ||
+ ((i = fork ()) < 0)) { /* make inferior process */
+ close (pipei[0]); close (pipei[1]);
+ close (pipeo[0]); close (pipeo[1]);
+ (*bn) (BLOCK_NONE,NIL);
+ return NIL;
+ }
+ if (!i) { /* if child */
+ alarm (0); /* never have alarms in children */
+ if (!fork ()) { /* make grandchild so it's inherited by init */
+ int cf; /* don't alter parent vars in case vfork() */
+ int maxfd = max (20,max (max(pipei[0],pipei[1]),max(pipeo[0],pipeo[1])));
+ dup2 (pipei[1],1); /* parent's input is my output */
+ dup2 (pipei[1],2); /* parent's input is my error output too */
+ dup2 (pipeo[0],0); /* parent's output is my input */
+ /* close all unnecessary descriptors */
+ for (cf = 3; cf <= maxfd; cf++) close (cf);
+ setpgrp (0,getpid ()); /* be our own process group */
+ _exit (execv (path,argv));/* now run it */
+ }
+ _exit (1); /* child is done */
+ }
+ grim_pid_reap (i,NIL); /* reap child; grandchild now owned by init */
+ close (pipei[1]); /* close child's side of the pipes */
+ close (pipeo[0]);
+
+ /* create TCP/IP stream */
+ stream = (TCPSTREAM *) memset (fs_get (sizeof (TCPSTREAM)),0,
+ sizeof (TCPSTREAM));
+ /* copy remote host name from argument */
+ stream->remotehost = cpystr (stream->host = cpystr (host));
+ stream->tcpsi = pipei[0]; /* init sockets */
+ stream->tcpso = pipeo[1];
+ stream->ictr = 0; /* init input counter */
+ stream->port = 0xffffffff; /* no port number */
+ ti += now = time (0); /* open timeout */
+ tmo.tv_usec = 0; /* initialize usec timeout */
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ FD_SET (stream->tcpsi,&fds); /* set bit in selection vector */
+ FD_SET (stream->tcpsi,&efds); /* set bit in error selection vector */
+ FD_SET (stream->tcpso,&efds); /* set bit in error selection vector */
+ do { /* block under timeout */
+ tmo.tv_sec = ti - now;
+ i = select (max (stream->tcpsi,stream->tcpso)+1,&fds,NIL,&efds,&tmo);
+ now = time (0); /* fake timeout if interrupt & time expired */
+ if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
+ } while ((i < 0) && (errno == EINTR));
+ if (i <= 0) { /* timeout or error? */
+ sprintf (tmp,i ? "error in %s to IMAP server" :
+ "%s to IMAP server timed out",(*service == '*') ? "ssh" : "rsh");
+ mm_log (tmp,WARN);
+ tcp_close (stream); /* punt stream */
+ stream = NIL;
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ /* return user name */
+ strcpy (usrbuf,mb->user[0] ? mb->user : myusername ());
+ return stream; /* return success */
+}
+
+/* TCP receive line
+ * Accepts: TCP stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *tcp_getline (TCPSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = tcp_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = tcp_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* TCP receive line or partial line
+ * Accepts: TCP stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!tcp_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!tcp_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* TCP/IP receive buffer
+ * Accepts: TCP/IP stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *s)
+{
+ unsigned long n;
+ /* make sure socket still alive */
+ if (stream->tcpsi < 0) return NIL;
+ /* can transfer bytes from buffer? */
+ if (n = min (size,stream->ictr)) {
+ memcpy (s,stream->iptr,n); /* yes, slurp as much as we can from it */
+ s += n; /* update pointer */
+ stream->iptr +=n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -=n;
+ }
+ if (size) {
+ int i;
+ fd_set fds,efds;
+ struct timeval tmo;
+ time_t t = time (0);
+ blocknotify_t bn=(blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ (*bn) (BLOCK_TCPREAD,NIL);
+ while (size > 0) { /* until request satisfied */
+ time_t tl = time (0);
+ time_t now = tl;
+ time_t ti = ttmo_read ? now + ttmo_read : 0;
+ if (tcpdebug) mm_log ("Reading TCP buffer",TCPDEBUG);
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ /* set bit in selection vectors */
+ FD_SET (stream->tcpsi,&fds);
+ FD_SET (stream->tcpsi,&efds);
+ errno = NIL; /* initially no error */
+ do { /* block under timeout */
+ tmo.tv_sec = ti ? ti - now : 0;
+ i = select (stream->tcpsi+1,&fds,NIL,&efds,ti ? &tmo : NIL);
+ now = time (0); /* fake timeout if interrupt & time expired */
+ if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
+ } while ((i < 0) && (errno == EINTR));
+ if (i) { /* non-timeout result from select? */
+ if (i > 0) /* read what we can */
+ while (((i = read (stream->tcpsi,s,(int) min (maxposint,size))) < 0)
+ && (errno == EINTR));
+ if (i <= 0) { /* error seen? */
+ if (tcpdebug) {
+ char tmp[MAILTMPLEN];
+ if (i) sprintf (s = tmp,"TCP buffer read I/O error %d",errno);
+ else s = "TCP buffer read end of file";
+ mm_log (s,TCPDEBUG);
+ }
+ return tcp_abort (stream);
+ }
+ s += i; /* success, point at new place to write */
+ size -= i; /* reduce byte count */
+ if (tcpdebug) mm_log ("Successfully read TCP buffer",TCPDEBUG);
+ }
+ /* timeout, punt unless told not to */
+ else if (!tmoh || !(*tmoh) (now - t,now - tl, stream->host)) {
+ if (tcpdebug) mm_log ("TCP buffer read timeout",TCPDEBUG);
+ return tcp_abort (stream);
+ }
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ }
+ *s = '\0'; /* tie off string */
+ return LONGT;
+}
+
+/* TCP/IP receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getdata (TCPSTREAM *stream)
+{
+ int i;
+ fd_set fds,efds;
+ struct timeval tmo;
+ time_t t = time (0);
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (stream->tcpsi < 0) return NIL;
+ (*bn) (BLOCK_TCPREAD,NIL);
+ while (stream->ictr < 1) { /* if nothing in the buffer */
+ time_t tl = time (0); /* start of request */
+ time_t now = tl;
+ time_t ti = ttmo_read ? now + ttmo_read : 0;
+ if (tcpdebug) mm_log ("Reading TCP data",TCPDEBUG);
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ FD_SET (stream->tcpsi,&fds);/* set bit in selection vectors */
+ FD_SET (stream->tcpsi,&efds);
+ errno = NIL; /* initially no error */
+ do { /* block under timeout */
+ tmo.tv_sec = ti ? ti - now : 0;
+ i = select (stream->tcpsi+1,&fds,NIL,&efds,ti ? &tmo : NIL);
+ now = time (0); /* fake timeout if interrupt & time expired */
+ if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
+ } while ((i < 0) && (errno == EINTR));
+ if (i) { /* non-timeout result from select? */
+ /* read what we can */
+ if (i > 0) while (((i = read (stream->tcpsi,stream->ibuf,BUFLEN)) < 0) &&
+ (errno == EINTR));
+ if (i <= 0) { /* error seen? */
+ if (tcpdebug) {
+ char *s,tmp[MAILTMPLEN];
+ if (i) sprintf (s = tmp,"TCP data read I/O error %d",errno);
+ else s = "TCP data read end of file";
+ mm_log (s,TCPDEBUG);
+ }
+ return tcp_abort (stream);
+ }
+ stream->ictr = i; /* success, set new count and pointer */
+ stream->iptr = stream->ibuf;
+ if (tcpdebug) mm_log ("Successfully read TCP data",TCPDEBUG);
+ }
+ /* timeout, punt unless told not to */
+ else if (!tmoh || !(*tmoh) (now - t,now - tl, stream->host)) {
+ if (tcpdebug) mm_log ("TCP data read timeout",TCPDEBUG);
+ return tcp_abort (stream);/* error or timeout no-continue */
+ }
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return T;
+}
+
+/* TCP/IP send string as record
+ * Accepts: TCP/IP stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long tcp_soutr (TCPSTREAM *stream,char *string)
+{
+ return tcp_sout (stream,string,(unsigned long) strlen (string));
+}
+
+
+/* TCP/IP send string
+ * Accepts: TCP/IP stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
+{
+ int i;
+ fd_set fds,efds;
+ struct timeval tmo;
+ time_t t = time (0);
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (stream->tcpso < 0) return NIL;
+ (*bn) (BLOCK_TCPWRITE,NIL);
+ while (size > 0) { /* until request satisfied */
+ time_t tl = time (0); /* start of request */
+ time_t now = tl;
+ time_t ti = ttmo_write ? now + ttmo_write : 0;
+ if (tcpdebug) mm_log ("Writing to TCP",TCPDEBUG);
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ FD_SET (stream->tcpso,&fds);/* set bit in selection vector */
+ FD_SET(stream->tcpso,&efds);/* set bit in error selection vector */
+ errno = NIL; /* block and write */
+ do { /* block under timeout */
+ tmo.tv_sec = ti ? ti - now : 0;
+ i = select (stream->tcpso+1,NIL,&fds,&efds,ti ? &tmo : NIL);
+ now = time (0); /* fake timeout if interrupt & time expired */
+ if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
+ } while ((i < 0) && (errno == EINTR));
+ if (i) { /* non-timeout result from select? */
+ /* write what we can */
+ if (i > 0) while (((i = write (stream->tcpso,string,size)) < 0) &&
+ (errno == EINTR));
+ if (i <= 0) { /* error seen? */
+ if (tcpdebug) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"TCP write I/O error %d",errno);
+ mm_log (tmp,TCPDEBUG);
+ }
+ return tcp_abort (stream);
+ }
+ string += i; /* how much we sent */
+ size -= i; /* count this size */
+ if (tcpdebug) mm_log ("successfully wrote to TCP",TCPDEBUG);
+ }
+ /* timeout, punt unless told not to */
+ else if (!tmoh || !(*tmoh) (now - t,now - tl, stream->host)) {
+ if (tcpdebug) mm_log ("TCP write timeout",TCPDEBUG);
+ return tcp_abort (stream);
+ }
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return T; /* all done */
+}
+
+/* TCP/IP close
+ * Accepts: TCP/IP stream
+ */
+
+void tcp_close (TCPSTREAM *stream)
+{
+ tcp_abort (stream); /* nuke the stream */
+ /* flush host names */
+ if (stream->host) fs_give ((void **) &stream->host);
+ if (stream->remotehost) fs_give ((void **) &stream->remotehost);
+ if (stream->localhost) fs_give ((void **) &stream->localhost);
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+
+/* TCP/IP abort stream
+ * Accepts: TCP/IP stream
+ * Returns: NIL always
+ */
+
+long tcp_abort (TCPSTREAM *stream)
+{
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (stream->tcpsi >= 0) { /* no-op if no socket */
+ (*bn) (BLOCK_TCPCLOSE,NIL);
+ close (stream->tcpsi); /* nuke the socket */
+ if (stream->tcpsi != stream->tcpso) close (stream->tcpso);
+ stream->tcpsi = stream->tcpso = -1;
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return NIL;
+}
+
+/* TCP/IP get host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_host (TCPSTREAM *stream)
+{
+ return stream->host; /* use tcp_remotehost() if want guarantees */
+}
+
+
+/* TCP/IP get remote host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_remotehost (TCPSTREAM *stream)
+{
+ if (!stream->remotehost) {
+ size_t sadrlen;
+ struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
+ stream->remotehost = /* get socket's peer name */
+ getpeername (stream->tcpsi,sadr,(void *) &sadrlen) ?
+ cpystr (stream->host) : tcp_name (sadr,NIL);
+ fs_give ((void **) &sadr);
+ }
+ return stream->remotehost;
+}
+
+
+/* TCP/IP return port for this stream
+ * Accepts: TCP/IP stream
+ * Returns: port number for this stream
+ */
+
+unsigned long tcp_port (TCPSTREAM *stream)
+{
+ return stream->port; /* return port number */
+}
+
+
+/* TCP/IP get local host name
+ * Accepts: TCP/IP stream
+ * Returns: local host name
+ */
+
+char *tcp_localhost (TCPSTREAM *stream)
+{
+ if (!stream->localhost) {
+ size_t sadrlen;
+ struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
+ stream->localhost = /* get socket's name */
+ ((stream->port & 0xffff000) ||
+ getsockname (stream->tcpsi,sadr,(void *) &sadrlen)) ?
+ cpystr (mylocalhost ()) : tcp_name (sadr,NIL);
+ fs_give ((void **) &sadr);
+ }
+ return stream->localhost; /* return local host name */
+}
+
+/* TCP/IP get client host address (server calls only)
+ * Returns: client host address
+ */
+
+char *tcp_clientaddr ()
+{
+ if (!myClientAddr) {
+ size_t sadrlen;
+ struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
+ if (getpeername (0,sadr,(void *) &sadrlen))
+ myClientAddr = cpystr ("UNKNOWN");
+ else { /* get stdin's peer name */
+ myClientAddr = cpystr (ip_sockaddrtostring (sadr));
+ if (myClientPort < 0) myClientPort = ip_sockaddrtoport (sadr);
+ }
+ fs_give ((void **) &sadr);
+ }
+ return myClientAddr;
+}
+
+
+/* TCP/IP get client host name (server calls only)
+ * Returns: client host name
+ */
+
+char *tcp_clienthost ()
+{
+ if (!myClientHost) {
+ size_t sadrlen;
+ struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
+ if (getpeername (0,sadr,(void *) &sadrlen)) {
+ char *s,*t,*v,tmp[MAILTMPLEN];
+ if ((s = getenv (t = "SSH_CLIENT")) ||
+ (s = getenv (t = "KRB5REMOTEADDR")) ||
+ (s = getenv (t = "SSH2_CLIENT"))) {
+ if (v = strchr (s,' ')) *v = '\0';
+ sprintf (v = tmp,"%.80s=%.80s",t,s);
+ }
+ else v = "UNKNOWN";
+ myClientHost = cpystr (v);
+ }
+ else { /* get stdin's peer name */
+ myClientHost = tcp_name (sadr,T);
+ if (!myClientAddr) myClientAddr = cpystr (ip_sockaddrtostring (sadr));
+ if (myClientPort < 0) myClientPort = ip_sockaddrtoport (sadr);
+ }
+ fs_give ((void **) &sadr);
+ }
+ return myClientHost;
+}
+
+
+/* TCP/IP get client port number (server calls only)
+ * Returns: client port number
+ */
+
+long tcp_clientport ()
+{
+ if (!myClientHost && !myClientAddr) tcp_clientaddr ();
+ return myClientPort;
+}
+
+/* TCP/IP get server host address (server calls only)
+ * Returns: server host address
+ */
+
+char *tcp_serveraddr ()
+{
+ if (!myServerAddr) {
+ size_t sadrlen;
+ struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
+ if (getsockname (0,sadr,(void *) &sadrlen))
+ myServerAddr = cpystr ("UNKNOWN");
+ else { /* get stdin's name */
+ myServerAddr = cpystr (ip_sockaddrtostring (sadr));
+ if (myServerPort < 0) myServerPort = ip_sockaddrtoport (sadr);
+ }
+ fs_give ((void **) &sadr);
+ }
+ return myServerAddr;
+}
+
+
+/* TCP/IP get server host name (server calls only)
+ * Returns: server host name
+ */
+
+char *tcp_serverhost ()
+{
+ if (!myServerHost) { /* once-only */
+ size_t sadrlen;
+ struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
+ /* get stdin's name */
+ if (getsockname (0,sadr,(void *) &sadrlen))
+ myServerHost = cpystr (mylocalhost ());
+ else { /* get stdin's name */
+ myServerHost = tcp_name (sadr,NIL);
+ if (!myServerAddr) myServerAddr = cpystr (ip_sockaddrtostring (sadr));
+ if (myServerPort < 0) myServerPort = ip_sockaddrtoport (sadr);
+ }
+ fs_give ((void **) &sadr);
+ }
+ return myServerHost;
+}
+
+
+/* TCP/IP get server port number (server calls only)
+ * Returns: server port number
+ */
+
+long tcp_serverport ()
+{
+ if (!myServerHost && !myServerAddr) tcp_serveraddr ();
+ return myServerPort;
+}
+
+/* TCP/IP return canonical form of host name
+ * Accepts: host name
+ * Returns: canonical form of host name
+ */
+
+char *tcp_canonical (char *name)
+{
+ char *ret,host[MAILTMPLEN];
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ void *data;
+ /* look like domain literal? */
+ if (name[0] == '[' && name[strlen (name) - 1] == ']') return name;
+ (*bn) (BLOCK_DNSLOOKUP,NIL); /* quell alarms */
+ data = (*bn) (BLOCK_SENSITIVE,NIL);
+ if (tcpdebug) {
+ sprintf (host,"DNS canonicalization %.80s",name);
+ mm_log (host,TCPDEBUG);
+ }
+ /* get canonical name */
+ if (!ip_nametoaddr (name,NIL,NIL,&ret,NIL)) ret = name;
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ (*bn) (BLOCK_NONE,NIL); /* alarms OK now */
+ if (tcpdebug) mm_log ("DNS canonicalization done",TCPDEBUG);
+ return ret;
+}
+
+/* TCP/IP return name from socket
+ * Accepts: socket
+ * verbose flag
+ * Returns: cpystr name
+ */
+
+char *tcp_name (struct sockaddr *sadr,long flag)
+{
+ char *ret,*t,adr[MAILTMPLEN],tmp[MAILTMPLEN];
+ sprintf (ret = adr,"[%.80s]",ip_sockaddrtostring (sadr));
+ if (allowreversedns) {
+ blocknotify_t bn = (blocknotify_t)mail_parameters(NIL,GET_BLOCKNOTIFY,NIL);
+ void *data;
+ if (tcpdebug) {
+ sprintf (tmp,"Reverse DNS resolution %s",adr);
+ mm_log (tmp,TCPDEBUG);
+ }
+ (*bn) (BLOCK_DNSLOOKUP,NIL);/* quell alarms */
+ data = (*bn) (BLOCK_SENSITIVE,NIL);
+ /* translate address to name */
+ if (t = tcp_name_valid (ip_sockaddrtoname (sadr))) {
+ /* produce verbose form if needed */
+ if (flag) sprintf (ret = tmp,"%s %s",t,adr);
+ else ret = t;
+ }
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ (*bn) (BLOCK_NONE,NIL); /* alarms OK now */
+ if (tcpdebug) mm_log ("Reverse DNS resolution done",TCPDEBUG);
+ }
+ return cpystr (ret);
+}
+
+
+/* TCP/IP validate name
+ * Accepts: domain name
+ * Returns: name if valid, NIL otherwise
+ */
+
+char *tcp_name_valid (char *s)
+{
+ int c;
+ char *ret,*tail;
+ /* must be non-empty and not too long */
+ if ((ret = (s && *s) ? s : NIL) && (tail = ret + NETMAXHOST)) {
+ /* must be alnum, dot, or hyphen */
+ while ((c = *s++) && (s <= tail) &&
+ (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) ||
+ ((c >= '0') && (c <= '9')) || (c == '-') || (c == '.')));
+ if (c) ret = NIL;
+ }
+ return ret;
+}
+
+/* TCP/IP check if client is given host name
+ * Accepts: candidate host name
+ * Returns: T if match, NIL otherwise
+ */
+
+long tcp_isclienthost (char *host)
+{
+ int family;
+ size_t adrlen,sadrlen,len;
+ void *adr,*next;
+ struct sockaddr *sadr;
+ long ret = NIL;
+ /* make sure that myClientAddr is set */
+ if (tcp_clienthost () && myClientAddr)
+ /* get sockaddr of client */
+ for (adr = ip_nametoaddr (host,&adrlen,&family,NIL,&next); adr && !ret;
+ adr = ip_nametoaddr (NIL,&adrlen,&family,NIL,&next)) {
+ /* build sockaddr of given address */
+ sadr = ip_sockaddr (family,adr,adrlen,1,&len);
+ if (!strcmp (myClientAddr,ip_sockaddrtostring (sadr))) ret = LONGT;
+ fs_give ((void **) &sadr); /* done with client sockaddr */
+ }
+ return ret;
+}
+
+/* Following statement must be at end of this module */
+
+#undef fork /* undo any use of vfork() */
diff --git a/imap/src/osdep/unix/tcp_unix.h b/imap/src/osdep/unix/tcp_unix.h
new file mode 100644
index 00000000..d4b29797
--- /dev/null
+++ b/imap/src/osdep/unix/tcp_unix.h
@@ -0,0 +1,48 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX TCP/IP routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* TCP input buffer */
+
+#define BUFLEN 8192
+
+
+/* TCP I/O stream */
+
+#define TCPSTREAM struct tcp_stream
+TCPSTREAM {
+ char *host; /* host name */
+ unsigned long port; /* port number */
+ char *localhost; /* local host name */
+ char *remotehost; /* remote host name */
+ int tcpsi; /* input socket */
+ int tcpso; /* output socket */
+ int ictr; /* input counter */
+ char *iptr; /* input pointer */
+ char ibuf[BUFLEN]; /* input buffer */
+};
diff --git a/imap/src/osdep/unix/tenex.c b/imap/src/osdep/unix/tenex.c
new file mode 100644
index 00000000..eee61fba
--- /dev/null
+++ b/imap/src/osdep/unix/tenex.c
@@ -0,0 +1,1470 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Tenex mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 22 May 1990
+ * Last Edited: 11 October 2007
+ */
+
+
+/* FILE TIME SEMANTICS
+ *
+ * The atime is the last read time of the file.
+ * The mtime is the last flags update time of the file.
+ * The ctime is the last write time of the file.
+ *
+ * TEXT SIZE SEMANTICS
+ *
+ * Most of the text sizes are in internal (LF-only) form, except for the
+ * msg.text size. Beware.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <sys/stat.h>
+#include "misc.h"
+#include "dummy.h"
+
+/* TENEX I/O stream local data */
+
+typedef struct tenex_local {
+ unsigned int shouldcheck: 1; /* if ping should do a check instead */
+ unsigned int mustcheck: 1; /* if ping must do a check instead */
+ int fd; /* file descriptor for I/O */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ time_t lastsnarf; /* local snarf time */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ unsigned long uid; /* current text uid */
+ SIZEDTEXT text; /* current text */
+} TENEXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((TENEXLOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *tenex_valid (char *name);
+int tenex_isvalid (char *name,char *tmp);
+void *tenex_parameters (long function,void *value);
+void tenex_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void tenex_list (MAILSTREAM *stream,char *ref,char *pat);
+void tenex_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long tenex_create (MAILSTREAM *stream,char *mailbox);
+long tenex_delete (MAILSTREAM *stream,char *mailbox);
+long tenex_rename (MAILSTREAM *stream,char *old,char *newname);
+long tenex_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *tenex_open (MAILSTREAM *stream);
+void tenex_close (MAILSTREAM *stream,long options);
+void tenex_fast (MAILSTREAM *stream,char *sequence,long flags);
+void tenex_flags (MAILSTREAM *stream,char *sequence,long flags);
+char *tenex_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long tenex_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void tenex_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+void tenex_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long tenex_ping (MAILSTREAM *stream);
+void tenex_check (MAILSTREAM *stream);
+void tenex_snarf (MAILSTREAM *stream);
+long tenex_expunge (MAILSTREAM *stream,char *sequence,long options);
+long tenex_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long tenex_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+unsigned long tenex_size (MAILSTREAM *stream,unsigned long m);
+char *tenex_file (char *dst,char *name);
+long tenex_parse (MAILSTREAM *stream);
+MESSAGECACHE *tenex_elt (MAILSTREAM *stream,unsigned long msgno);
+void tenex_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt);
+void tenex_update_status (MAILSTREAM *stream,unsigned long msgno,
+ long syncflag);
+unsigned long tenex_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size);
+
+/* Tenex mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER tenexdriver = {
+ "tenex", /* driver name */
+ DR_LOCAL|DR_MAIL|DR_NOSTICKY|DR_LOCKING,
+ /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ tenex_valid, /* mailbox is valid for us */
+ tenex_parameters, /* manipulate parameters */
+ tenex_scan, /* scan mailboxes */
+ tenex_list, /* list mailboxes */
+ tenex_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ dummy_create, /* create mailbox */
+ tenex_delete, /* delete mailbox */
+ tenex_rename, /* rename mailbox */
+ tenex_status, /* status of mailbox */
+ tenex_open, /* open mailbox */
+ tenex_close, /* close mailbox */
+ tenex_fast, /* fetch message "fast" attributes */
+ tenex_flags, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ tenex_header, /* fetch message header */
+ tenex_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ tenex_flag, /* modify flags */
+ tenex_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ tenex_ping, /* ping mailbox to see if still alive */
+ tenex_check, /* check for new messages */
+ tenex_expunge, /* expunge deleted messages */
+ tenex_copy, /* copy messages to another mailbox */
+ tenex_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM tenexproto = {&tenexdriver};
+
+/* Tenex mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *tenex_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return tenex_isvalid (name,tmp) ? &tenexdriver : NIL;
+}
+
+
+/* Tenex mail test for valid mailbox
+ * Accepts: mailbox name
+ * Returns: T if valid, NIL otherwise
+ */
+
+int tenex_isvalid (char *name,char *tmp)
+{
+ int fd;
+ int ret = NIL;
+ char *s,file[MAILTMPLEN];
+ struct stat sbuf;
+ time_t tp[2];
+ errno = EINVAL; /* assume invalid argument */
+ /* if file, get its status */
+ if ((s = tenex_file (file,name)) && !stat (s,&sbuf)) {
+ if (!sbuf.st_size) { /* allow empty file if INBOX */
+ if ((s = mailboxfile (tmp,name)) && !*s) ret = T;
+ else errno = 0; /* empty file */
+ }
+ else if ((fd = open (file,O_RDONLY,NIL)) >= 0) {
+ memset (tmp,'\0',MAILTMPLEN);
+ if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\012')) &&
+ (s[-1] != '\015')) { /* valid format? */
+ *s = '\0'; /* tie off header */
+ /* must begin with dd-mmm-yy" */
+ ret = (((tmp[2] == '-' && tmp[6] == '-') ||
+ (tmp[1] == '-' && tmp[5] == '-')) &&
+ (s = strchr (tmp+18,',')) && strchr (s+2,';')) ? T : NIL;
+ }
+ else errno = -1; /* bogus format */
+ close (fd); /* close the file */
+ /* \Marked status? */
+ if (sbuf.st_ctime > sbuf.st_atime) {
+ tp[0] = sbuf.st_atime; /* preserve atime and mtime */
+ tp[1] = sbuf.st_mtime;
+ utime (file,tp); /* set the times */
+ }
+ }
+ }
+ /* in case INBOX but not tenex format */
+ else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1;
+ return ret; /* return what we should */
+}
+
+/* Tenex manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tenex_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = tenex_file ((char *) value,"INBOX");
+ break;
+ }
+ return ret;
+}
+
+
+/* Tenex mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void tenex_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* Tenex mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void tenex_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* Tenex mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void tenex_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* Tenex mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long tenex_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return tenex_rename (stream,mailbox,NIL);
+}
+
+
+/* Tenex mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long tenex_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = T;
+ char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ int fd,ld;
+ struct stat sbuf;
+ if (!tenex_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'/')) && !s[1])))) {
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ else if ((fd = open (file,O_RDWR,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get exclusive parse/append permission */
+ if ((ld = lockfd (fd,lock,LOCK_EX)) < 0) {
+ MM_LOG ("Unable to lock rename mailbox",ERROR);
+ return NIL;
+ }
+ /* lock out other users */
+ if (flock (fd,LOCK_EX|LOCK_NB)) {
+ close (fd); /* couldn't lock, give up on it then */
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+ MM_LOG (tmp,ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return NIL;
+ }
+
+ if (newname) { /* want rename? */
+ if (s = strrchr (tmp,'/')) {/* found superior to destination name? */
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,tmp,get_dir_protection (newname)))
+ ret = NIL;
+ else *s = c; /* restore full name */
+ }
+ /* rename the file */
+ if (ret && rename (file,tmp)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ else if (unlink (file)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* close the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ /* recreate file if renamed INBOX */
+ if (ret && !compare_cstring (old,"INBOX")) dummy_create (NIL,"mail.txt");
+ return ret; /* return success */
+}
+
+/* Tenex Mail status
+ * Accepts: mail stream
+ * mailbox name
+ * status flags
+ * Returns: T on success, NIL on failure
+ */
+
+long tenex_status (MAILSTREAM *stream,char *mbx,long flags)
+{
+ MAILSTATUS status;
+ unsigned long i;
+ MAILSTREAM *tstream = NIL;
+ MAILSTREAM *systream = NIL;
+ /* make temporary stream (unless this mbx) */
+ if (!stream && !(stream = tstream =
+ mail_open (NIL,mbx,OP_READONLY|OP_SILENT))) return NIL;
+ status.flags = flags; /* return status values */
+ status.messages = stream->nmsgs;
+ status.recent = stream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++)
+ if (!mail_elt (stream,i)->seen) status.unseen++;
+ status.uidnext = stream->uid_last + 1;
+ status.uidvalidity = stream->uid_validity;
+ /* calculate post-snarf results */
+ if (!status.recent && stream->inbox &&
+ (systream = mail_open (NIL,sysinbox (),OP_READONLY|OP_SILENT))) {
+ status.messages += systream->nmsgs;
+ status.recent += systream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1; i <= systream->nmsgs; i++)
+ if (!mail_elt (systream,i)->seen) status.unseen++;
+ /* kludge but probably good enough */
+ status.uidnext += systream->nmsgs;
+ }
+ MM_STATUS(stream,mbx,&status);/* pass status to main program */
+ if (tstream) mail_close (tstream);
+ if (systream) mail_close (systream);
+ return T; /* success */
+}
+
+/* Tenex mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *tenex_open (MAILSTREAM *stream)
+{
+ int fd,ld;
+ char tmp[MAILTMPLEN];
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return user_flags (&tenexproto);
+ if (stream->local) fatal ("tenex recycle stream");
+ user_flags (stream); /* set up user flags */
+ /* canonicalize the mailbox name */
+ if (!tenex_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ MM_LOG (tmp,ERROR);
+ }
+ if (stream->rdonly ||
+ (fd = open (tmp,O_RDWR,NIL)) < 0) {
+ if ((fd = open (tmp,O_RDONLY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ else if (!stream->rdonly) { /* got it, but readonly */
+ MM_LOG ("Can't get write access to mailbox, access is readonly",WARN);
+ stream->rdonly = T;
+ }
+ }
+ stream->local = fs_get (sizeof (TENEXLOCAL));
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ LOCAL->text.data = (unsigned char *) fs_get (CHUNKSIZE);
+ LOCAL->text.size = CHUNKSIZE - 1;
+
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ LOCAL->fd = fd; /* bind the file */
+ /* flush old name */
+ fs_give ((void **) &stream->mailbox);
+ /* save canonical name */
+ stream->mailbox = cpystr (tmp);
+ /* get shared parse permission */
+ if ((ld = lockfd (fd,tmp,LOCK_SH)) < 0) {
+ MM_LOG ("Unable to lock open mailbox",ERROR);
+ return NIL;
+ }
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH); /* lock the file */
+ (*bn) (BLOCK_NONE,NIL);
+ unlockfd (ld,tmp); /* release shared parse permission */
+ LOCAL->filesize = 0; /* initialize parsed file size */
+ /* time not set up yet */
+ LOCAL->lastsnarf = LOCAL->filetime = 0;
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ stream->sequence++; /* bump sequence number */
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ if (tenex_ping (stream) && !stream->nmsgs)
+ MM_LOG ("Mailbox is empty",(long) NIL);
+ if (!LOCAL) return NIL; /* failure if stream died */
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft =
+ stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ return stream; /* return stream to caller */
+}
+
+/* Tenex mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void tenex_close (MAILSTREAM *stream,long options)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ if (options & CL_EXPUNGE) tenex_expunge (stream,NIL,NIL);
+ stream->silent = silent; /* restore previous status */
+ flock (LOCAL->fd,LOCK_UN); /* unlock local file */
+ close (LOCAL->fd); /* close the local file */
+ /* free local text buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* Tenex mail fetch fast data
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ */
+
+void tenex_fast (MAILSTREAM *stream,char *sequence,long flags)
+{
+ STRING bs;
+ MESSAGECACHE *elt;
+ unsigned long i;
+ if (stream && LOCAL &&
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ if (!elt->rfc822_size) { /* have header size yet? */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.special.text.size,L_SET);
+ /* resize bigbuf if necessary */
+ if (LOCAL->buflen < elt->private.msg.full.text.size) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buflen = elt->private.msg.full.text.size;
+ LOCAL->buf = (char *) fs_get (LOCAL->buflen + 1);
+ }
+ /* tie off string */
+ LOCAL->buf[elt->private.msg.full.text.size] = '\0';
+ /* read in the message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.full.text.size);
+ INIT (&bs,mail_string,(void *) LOCAL->buf,
+ elt->private.msg.full.text.size);
+ /* calculate its CRLF size */
+ elt->rfc822_size = strcrlflen (&bs);
+ }
+ tenex_elt (stream,i); /* get current flags from file */
+ }
+}
+
+
+/* Tenex mail fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ * Sniffs at file to get flags
+ */
+
+void tenex_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ unsigned long i;
+ if (stream && LOCAL &&
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if (mail_elt (stream,i)->sequence) tenex_elt (stream,i);
+}
+
+/* TENEX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *tenex_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags)
+{
+ char *s;
+ unsigned long i;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ /* get to header position */
+ lseek (LOCAL->fd,tenex_hdrpos (stream,msgno,&i),L_SET);
+ if (flags & FT_INTERNAL) {
+ if (i > LOCAL->buflen) { /* resize if not enough space */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get (LOCAL->buflen = i + 1);
+ }
+ /* slurp the data */
+ read (LOCAL->fd,LOCAL->buf,*length = i);
+ }
+ else {
+ s = (char *) fs_get (i + 1);/* get readin buffer */
+ s[i] = '\0'; /* tie off string */
+ read (LOCAL->fd,s,i); /* slurp the data */
+ /* make CRLF copy of string */
+ *length = strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,s,i);
+ fs_give ((void **) &s); /* free readin buffer */
+ }
+ return (char *) LOCAL->buf;
+}
+
+/* TENEX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T, always
+ */
+
+long tenex_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ char *s;
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ /* get message status */
+ elt = tenex_elt (stream,msgno);
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ elt->seen = T; /* mark message as seen */
+ /* recalculate status */
+ tenex_update_status (stream,msgno,T);
+ MM_FLAGS (stream,msgno);
+ }
+ if (flags & FT_INTERNAL) { /* if internal representation wanted */
+ /* find header position */
+ i = tenex_hdrpos (stream,msgno,&j);
+ if (i > LOCAL->buflen) { /* resize if not enough space */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get (LOCAL->buflen = i + 1);
+ }
+ /* go to text position */
+ lseek (LOCAL->fd,i + j,L_SET);
+ /* slurp the data */
+ read (LOCAL->fd,LOCAL->buf,i);
+ /* set up stringstruct for internal */
+ INIT (bs,mail_string,LOCAL->buf,i);
+ }
+ else { /* normal form, previous text cached? */
+ if (elt->private.uid == LOCAL->uid)
+ i = elt->private.msg.text.text.size;
+ else { /* not cached, cache it now */
+ LOCAL->uid = elt->private.uid;
+ /* find header position */
+ i = tenex_hdrpos (stream,msgno,&j);
+ /* go to text position */
+ lseek (LOCAL->fd,i + j,L_SET);
+ s = (char *) fs_get ((i = tenex_size (stream,msgno) - j) + 1);
+ s[i] = '\0'; /* tie off string */
+ read (LOCAL->fd,s,i); /* slurp the data */
+ /* make CRLF copy of string */
+ i = elt->private.msg.text.text.size =
+ strcrlfcpy (&LOCAL->text.data,&LOCAL->text.size,s,i);
+ fs_give ((void **) &s); /* free readin buffer */
+ }
+ /* set up stringstruct */
+ INIT (bs,mail_string,LOCAL->text.data,i);
+ }
+ return T; /* success */
+}
+
+/* Tenex mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ */
+
+void tenex_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ time_t tp[2];
+ struct stat sbuf;
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* make sure read comes after all that */
+ utime (stream->mailbox,tp);
+ }
+}
+
+
+/* Tenex mail per-message modify flags
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void tenex_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ struct stat sbuf;
+ /* maybe need to do a checkpoint? */
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ LOCAL->filetime = 0; /* don't do this test for any other messages */
+ }
+ /* recalculate status */
+ tenex_update_status (stream,elt->msgno,NIL);
+}
+
+/* Tenex mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream still alive, NIL if not
+ */
+
+long tenex_ping (MAILSTREAM *stream)
+{
+ unsigned long i = 1;
+ long r = T;
+ int ld;
+ char lock[MAILTMPLEN];
+ struct stat sbuf;
+ if (stream && LOCAL) { /* only if stream already open */
+ fstat (LOCAL->fd,&sbuf); /* get current file poop */
+ if (LOCAL->filetime && !(LOCAL->mustcheck || LOCAL->shouldcheck) &&
+ (LOCAL->filetime < sbuf.st_mtime)) LOCAL->shouldcheck = T;
+ /* check for changed message status */
+ if (LOCAL->mustcheck || LOCAL->shouldcheck) {
+ LOCAL->filetime = sbuf.st_mtime;
+ if (LOCAL->shouldcheck) /* babble when we do this unilaterally */
+ MM_NOTIFY (stream,"[CHECK] Checking for flag updates",NIL);
+ while (i <= stream->nmsgs) tenex_elt (stream,i++);
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ }
+ /* get shared parse/append permission */
+ if ((sbuf.st_size != LOCAL->filesize) &&
+ ((ld = lockfd (LOCAL->fd,lock,LOCK_SH)) >= 0)) {
+ /* parse resulting mailbox */
+ r = (tenex_parse (stream)) ? T : NIL;
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ if (LOCAL) { /* stream must still be alive */
+ /* snarf if this is a read-write inbox */
+ if (stream->inbox && !stream->rdonly) {
+ tenex_snarf (stream);
+ fstat (LOCAL->fd,&sbuf);/* see if file changed now */
+ if ((sbuf.st_size != LOCAL->filesize) &&
+ ((ld = lockfd (LOCAL->fd,lock,LOCK_SH)) >= 0)) {
+ /* parse resulting mailbox */
+ r = (tenex_parse (stream)) ? T : NIL;
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ }
+ }
+ }
+ return r; /* return result of the parse */
+}
+
+
+/* Tenex mail check mailbox (reparses status too)
+ * Accepts: MAIL stream
+ */
+
+void tenex_check (MAILSTREAM *stream)
+{
+ /* mark that a check is desired */
+ if (LOCAL) LOCAL->mustcheck = T;
+ if (tenex_ping (stream)) MM_LOG ("Check completed",(long) NIL);
+}
+
+/* Tenex mail snarf messages from system inbox
+ * Accepts: MAIL stream
+ */
+
+void tenex_snarf (MAILSTREAM *stream)
+{
+ unsigned long i = 0;
+ unsigned long j,r,hdrlen,txtlen;
+ struct stat sbuf;
+ char *hdr,*txt,lock[MAILTMPLEN],tmp[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ MAILSTREAM *sysibx = NIL;
+ int ld;
+ /* give up if can't get exclusive permission */
+ if ((time (0) >= (LOCAL->lastsnarf +
+ (long) mail_parameters (NIL,GET_SNARFINTERVAL,NIL))) &&
+ strcmp (sysinbox (),stream->mailbox) &&
+ ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) >= 0)) {
+ MM_CRITICAL (stream); /* go critical */
+ /* sizes match and anything in sysinbox? */
+ if (!stat (sysinbox (),&sbuf) && sbuf.st_size &&
+ !fstat (LOCAL->fd,&sbuf) && (sbuf.st_size == LOCAL->filesize) &&
+ (sysibx = mail_open (sysibx,sysinbox (),OP_SILENT)) &&
+ (!sysibx->rdonly) && (r = sysibx->nmsgs)) {
+ /* yes, go to end of file in our mailbox */
+ lseek (LOCAL->fd,sbuf.st_size,L_SET);
+ /* for each message in sysibx mailbox */
+ while (r && (++i <= sysibx->nmsgs)) {
+ /* snarf message from system INBOX */
+ hdr = cpystr (mail_fetchheader_full(sysibx,i,NIL,&hdrlen,FT_INTERNAL));
+ txt = mail_fetchtext_full (sysibx,i,&txtlen,FT_INTERNAL|FT_PEEK);
+ /* if have a message */
+ if (j = hdrlen + txtlen) {
+ /* calculate header line */
+ mail_date (LOCAL->buf,elt = mail_elt (sysibx,i));
+ sprintf (LOCAL->buf + strlen (LOCAL->buf),
+ ",%lu;0000000000%02o\n",j,(unsigned)
+ ((fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)));
+ /* copy message */
+ if ((write (LOCAL->fd,LOCAL->buf,strlen (LOCAL->buf)) < 0) ||
+ (write (LOCAL->fd,hdr,hdrlen) < 0) ||
+ (write (LOCAL->fd,txt,txtlen) < 0)) r = 0;
+ }
+ fs_give ((void **) &hdr);
+ }
+
+ /* make sure all the updates take */
+ if (fsync (LOCAL->fd)) r = 0;
+ if (r) { /* delete all the messages we copied */
+ if (r == 1) strcpy (tmp,"1");
+ else sprintf (tmp,"1:%lu",r);
+ mail_flag (sysibx,tmp,"\\Deleted",ST_SET);
+ mail_expunge (sysibx); /* now expunge all those messages */
+ }
+ else {
+ sprintf (LOCAL->buf,"Can't copy new mail: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,WARN);
+ ftruncate (LOCAL->fd,sbuf.st_size);
+ }
+ fstat (LOCAL->fd,&sbuf); /* yes, get current file size */
+ LOCAL->filetime = sbuf.st_mtime;
+ }
+ if (sysibx) mail_close (sysibx);
+ MM_NOCRITICAL (stream); /* release critical */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ LOCAL->lastsnarf = time (0);/* note time of last snarf */
+ }
+}
+
+/* Tenex mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long tenex_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ time_t tp[2];
+ struct stat sbuf;
+ off_t pos = 0;
+ int ld;
+ unsigned long i = 1;
+ unsigned long j,k,m,recent;
+ unsigned long n = 0;
+ unsigned long delta = 0;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (!(ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ tenex_ping (stream))); /* parse sequence if given, ping stream */
+ else if (stream->rdonly) MM_LOG ("Expunge ignored on readonly mailbox",WARN);
+ else {
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ }
+ /* The cretins who designed flock() created a window of vulnerability in
+ * upgrading locks from shared to exclusive or downgrading from exclusive
+ * to shared. Rather than maintain the lock at shared status at a minimum,
+ * flock() actually *releases* the former lock. Obviously they never talked
+ * to any database guys. Fortunately, we have the parse/append permission
+ * lock. If we require this lock before going exclusive on the mailbox,
+ * another process can not sneak in and steal the exclusive mailbox lock on
+ * us, because it will block on trying to get parse/append permission first.
+ */
+ /* get exclusive parse/append permission */
+ if ((ld = lockfd (LOCAL->fd,lock,LOCK_EX)) < 0)
+ MM_LOG ("Unable to lock expunge mailbox",ERROR);
+ /* make sure see any newly-arrived messages */
+ else if (!tenex_parse (stream));
+ /* get exclusive access */
+ else if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH);/* recover previous lock */
+ (*bn) (BLOCK_NONE,NIL);
+ MM_LOG ("Can't expunge because mailbox is in use by another process",
+ ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+
+ else {
+ MM_CRITICAL (stream); /* go critical */
+ recent = stream->recent; /* get recent now that pinged and locked */
+ /* for each message */
+ while (i <= stream->nmsgs) {
+ /* get cache element */
+ elt = tenex_elt (stream,i);
+ /* number of bytes to smash or preserve */
+ k = elt->private.special.text.size + tenex_size (stream,i);
+ /* if need to expunge this message */
+ if (elt->deleted && (sequence ? elt->sequence : T)) {
+ /* if recent, note one less recent message */
+ if (elt->recent) --recent;
+ delta += k; /* number of bytes to delete */
+ /* notify upper levels */
+ mail_expunged (stream,i);
+ n++; /* count up one more expunged message */
+ }
+ else if (i++ && delta) {/* preserved message */
+ /* first byte to preserve */
+ j = elt->private.special.offset;
+ do { /* read from source position */
+ m = min (k,LOCAL->buflen);
+ lseek (LOCAL->fd,j,L_SET);
+ read (LOCAL->fd,LOCAL->buf,m);
+ pos = j - delta; /* write to destination position */
+ lseek (LOCAL->fd,pos,L_SET);
+ while (T) {
+ lseek (LOCAL->fd,pos,L_SET);
+ if (write (LOCAL->fd,LOCAL->buf,m) > 0) break;
+ MM_NOTIFY (stream,strerror (errno),WARN);
+ MM_DISKERROR (stream,errno,T);
+ }
+ pos += m; /* new position */
+ j += m; /* next chunk, perhaps */
+ } while (k -= m); /* until done */
+ /* note the new address of this text */
+ elt->private.special.offset -= delta;
+ }
+ /* preserved but no deleted messages */
+ else pos = elt->private.special.offset + k;
+ }
+
+ if (n) { /* truncate file after last message */
+ if (pos != (LOCAL->filesize -= delta)) {
+ sprintf (LOCAL->buf,
+ "Calculated size mismatch %lu != %lu, delta = %lu",
+ (unsigned long) pos,(unsigned long) LOCAL->filesize,delta);
+ MM_LOG (LOCAL->buf,WARN);
+ LOCAL->filesize = pos;/* fix it then */
+ }
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ sprintf (LOCAL->buf,"Expunged %lu messages",n);
+ /* output the news */
+ MM_LOG (LOCAL->buf,(long) NIL);
+ }
+ else MM_LOG ("No messages deleted, so no update needed",(long) NIL);
+ fsync (LOCAL->fd); /* force disk update */
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* reset atime to now */
+ utime (stream->mailbox,tp);
+ MM_NOCRITICAL (stream); /* release critical */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ (*bn) (BLOCK_FILELOCK,NIL);
+ flock (LOCAL->fd,LOCK_SH);/* allow sharers again */
+ (*bn) (BLOCK_NONE,NIL);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+ }
+ return LONGT;
+}
+
+/* Tenex mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if success, NIL if failed
+ */
+
+long tenex_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ time_t tp[2];
+ MESSAGECACHE *elt;
+ unsigned long i,j,k;
+ long ret = LONGT;
+ int fd,ld;
+ char file[MAILTMPLEN],lock[MAILTMPLEN];
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ /* make sure valid mailbox */
+ if (!tenex_isvalid (mailbox,LOCAL->buf)) switch (errno) {
+ case ENOENT: /* no such file? */
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid Tenex-format mailbox name: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a Tenex-format mailbox: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* got file? */
+ if ((fd = open (tenex_file(file,mailbox),O_RDWR,NIL)) < 0) {
+ sprintf (LOCAL->buf,"Unable to open copy mailbox: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ MM_CRITICAL (stream); /* go critical */
+ /* get exclusive parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockfd (fd,lock,LOCK_EX)) < 0)) {
+ MM_LOG ("Unable to lock copy mailbox",ERROR);
+ MM_NOCRITICAL (stream);
+ return NIL;
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
+
+ /* for each requested message */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ /* number of bytes to copy */
+ k = elt->private.special.text.size + tenex_size (stream,i);
+ do { /* read from source position */
+ j = min (k,LOCAL->buflen);
+ read (LOCAL->fd,LOCAL->buf,j);
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ } while (ret && (k -= j));/* until done */
+ }
+ /* make sure all the updates take */
+ if (!(ret && (ret = !fsync (fd)))) {
+ sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ }
+ if (ret) tp[0] = time (0) - 1;/* set atime to now-1 if successful copy */
+ /* else preserve \Marked status */
+ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0);
+ tp[1] = sbuf.st_mtime; /* preserve mtime */
+ utime (file,tp); /* set the times */
+ close (fd); /* close the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ MM_NOCRITICAL (stream); /* release critical */
+ /* delete all requested messages */
+ if (ret && (options & CP_MOVE)) {
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = tenex_elt (stream,i))->sequence) {
+ elt->deleted = T; /* mark message deleted */
+ /* recalculate status */
+ tenex_update_status (stream,i,NIL);
+ }
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* make sure atime remains greater */
+ utime (stream->mailbox,tp);
+ }
+ }
+ if (ret && mail_parameters (NIL,GET_COPYUID,NIL))
+ MM_LOG ("Can not return meaningful COPYUID with this mailbox format",WARN);
+ return ret;
+}
+
+/* Tenex mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long tenex_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd,ld,c;
+ char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ time_t tp[2];
+ FILE *df;
+ MESSAGECACHE elt;
+ long f;
+ unsigned long i,j,uf,size;
+ STRING *message;
+ long ret = LONGT;
+ /* default stream to prototype */
+ if (!stream) stream = user_flags (&tenexproto);
+ /* make sure valid mailbox */
+ if (!tenex_isvalid (mailbox,tmp)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (!compare_cstring (mailbox,"INBOX")) dummy_create (NIL,"mail.txt");
+ else {
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* falls through */
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid TENEX-format mailbox name: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a TENEX-format mailbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get first message */
+ if (!MM_APPEND (af) (stream,data,&flags,&date,&message)) return NIL;
+
+ /* open destination mailbox */
+ if (((fd = open (tenex_file (file,mailbox),O_WRONLY|O_APPEND,NIL)) < 0) ||
+ !(df = fdopen (fd,"ab"))) {
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockfd (fd,lock,LOCK_EX)) < 0)) {
+ MM_LOG ("Unable to lock append mailbox",ERROR);
+ close (fd);
+ return NIL;
+ }
+ MM_CRITICAL (stream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ errno = 0;
+ do { /* parse flags */
+ if (!SIZE (message)) { /* guard against zero-length */
+ MM_LOG ("Append of zero-length message",ERROR);
+ ret = NIL;
+ break;
+ }
+ f = mail_parse_flags (stream,flags,&i);
+ /* reverse bits (dontcha wish we had CIRC?) */
+ for (uf = 0; i; uf |= 1 << (29 - find_rightmost_bit (&i)));
+ if (date) { /* parse date if given */
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ MM_LOG (tmp,ERROR);
+ ret = NIL; /* mark failure */
+ break;
+ }
+ mail_date (tmp,&elt); /* write preseved date */
+ }
+ else internal_date (tmp); /* get current date in IMAP format */
+ i = GETPOS (message); /* remember current position */
+ for (j = SIZE (message), size = 0; j; --j)
+ if (SNX (message) != '\015') ++size;
+ SETPOS (message,i); /* restore position */
+ /* write header */
+ if (fprintf (df,"%s,%lu;%010lo%02lo\n",tmp,size,uf,(unsigned long) f) < 0)
+ ret = NIL;
+ else { /* write message */
+ while (size) if ((c = 0xff & SNX (message)) != '\015') {
+ if (putc (c,df) != EOF) --size;
+ else break;
+ }
+ /* get next message */
+ if (size || !MM_APPEND (af) (stream,data,&flags,&date,&message))
+ ret = NIL;
+ }
+ } while (ret && message);
+ /* if error... */
+ if (!ret || (fflush (df) == EOF)) {
+ ftruncate (fd,sbuf.st_size);/* revert file */
+ close (fd); /* make sure fclose() doesn't corrupt us */
+ if (errno) {
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ }
+ ret = NIL;
+ }
+ if (ret) tp[0] = time (0) - 1;/* set atime to now-1 if successful copy */
+ /* else preserve \Marked status */
+ else tp[0] = (sbuf.st_ctime > sbuf.st_atime) ? sbuf.st_atime : time(0);
+ tp[1] = sbuf.st_mtime; /* preserve mtime */
+ utime (file,tp); /* set the times */
+ fclose (df); /* close the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ MM_NOCRITICAL (stream); /* release critical */
+ if (ret && mail_parameters (NIL,GET_APPENDUID,NIL))
+ MM_LOG ("Can not return meaningful APPENDUID with this mailbox format",
+ WARN);
+ return ret;
+}
+
+/* Internal routines */
+
+
+/* Tenex mail return internal message size in bytes
+ * Accepts: MAIL stream
+ * message #
+ * Returns: internal size of message
+ */
+
+unsigned long tenex_size (MAILSTREAM *stream,unsigned long m)
+{
+ MESSAGECACHE *elt = mail_elt (stream,m);
+ return ((m < stream->nmsgs) ? mail_elt (stream,m+1)->private.special.offset :
+ LOCAL->filesize) -
+ (elt->private.special.offset + elt->private.special.text.size);
+}
+
+
+/* Tenex mail generate file string
+ * Accepts: temporary buffer to write into
+ * mailbox name string
+ * Returns: local file string or NIL if failure
+ */
+
+char *tenex_file (char *dst,char *name)
+{
+ char tmp[MAILTMPLEN];
+ char *s = mailboxfile (dst,name);
+ /* return our standard inbox */
+ return (s && !*s) ? mailboxfile (dst,tenex_isvalid ("~/INBOX",tmp) ?
+ "~/INBOX" : "mail.txt") : s;
+}
+
+/* Tenex mail parse mailbox
+ * Accepts: MAIL stream
+ * Returns: T if parse OK
+ * NIL if failure, stream aborted
+ */
+
+long tenex_parse (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = NIL;
+ unsigned char c,*s,*t,*x;
+ char tmp[MAILTMPLEN];
+ unsigned long i,j;
+ long curpos = LOCAL->filesize;
+ long nmsgs = stream->nmsgs;
+ long recent = stream->recent;
+ short added = NIL;
+ short silent = stream->silent;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ if (sbuf.st_size < curpos) { /* sanity check */
+ sprintf (tmp,"Mailbox shrank from %lu to %lu!",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size);
+ MM_LOG (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ stream->silent = T; /* don't pass up exists events yet */
+ while (sbuf.st_size - curpos){/* while there is stuff to parse */
+ /* get to that position in the file */
+ lseek (LOCAL->fd,curpos,L_SET);
+ if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
+ sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size,
+ i ? strerror (errno) : "no data read");
+ MM_LOG (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ LOCAL->buf[i] = '\0'; /* tie off buffer just in case */
+ if (!(s = strchr (LOCAL->buf,'\012'))) {
+ sprintf (tmp,"Unable to find newline at %lu in %lu bytes, text: %s",
+ (unsigned long) curpos,i,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ *s = '\0'; /* tie off header line */
+ i = (s + 1) - LOCAL->buf; /* note start of text offset */
+ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
+ sprintf (tmp,"Unable to parse internal header at %lu: %s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ MM_LOG (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ *s++ = '\0'; *t++ = '\0'; /* tie off fields */
+
+ added = T; /* note that a new message was added */
+ /* swell the cache */
+ mail_exists (stream,++nmsgs);
+ /* instantiate an elt for this message */
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ elt->private.uid = ++stream->uid_last;
+ /* note file offset of header */
+ elt->private.special.offset = curpos;
+ /* in case error */
+ elt->private.special.text.size = 0;
+ /* header size not known yet */
+ elt->private.msg.header.text.size = 0;
+ x = s; /* parse the header components */
+ if (mail_parse_date (elt,LOCAL->buf) &&
+ (elt->private.msg.full.text.size = strtoul (s,(char **) &s,10)) &&
+ (!(s && *s)) && isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) &&
+ isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) &&
+ isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) &&
+ isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12])
+ elt->private.special.text.size = i;
+ else { /* oops */
+ sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s",
+ curpos,(char *) LOCAL->buf,(char *) x,(char *) t);
+ MM_LOG (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ /* make sure didn't run off end of file */
+ if ((curpos += (elt->private.msg.full.text.size + i)) > sbuf.st_size) {
+ sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)",
+ elt->private.special.offset,(unsigned long) curpos,
+ (unsigned long) sbuf.st_size);
+ MM_LOG (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ c = t[10]; /* remember first system flags byte */
+ t[10] = '\0'; /* tie off flags */
+ j = strtoul (t,NIL,8); /* get user flags value */
+ t[10] = c; /* restore first system flags byte */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ /* calculate system flags */
+ if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T;
+ if (j & fDELETED) elt->deleted = T;
+ if (j & fFLAGGED) elt->flagged = T;
+ if (j & fANSWERED) elt->answered = T;
+ if (j & fDRAFT) elt->draft = T;
+ if (!(j & fOLD)) { /* newly arrived message? */
+ elt->recent = T;
+ recent++; /* count up a new recent message */
+ /* mark it as old */
+ tenex_update_status (stream,nmsgs,NIL);
+ }
+ }
+ fsync (LOCAL->fd); /* make sure all the fOLD flags take */
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */
+ LOCAL->filetime = sbuf.st_mtime;
+ if (added && !stream->rdonly){/* make sure atime updated */
+ time_t tp[2];
+ tp[0] = time (0);
+ tp[1] = LOCAL->filetime;
+ utime (stream->mailbox,tp);
+ }
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
+ mail_recent (stream,recent); /* and of change in recent messages */
+ return LONGT; /* return the winnage */
+}
+
+/* Tenex get cache element with status updating from file
+ * Accepts: MAIL stream
+ * message number
+ * Returns: cache element
+ */
+
+MESSAGECACHE *tenex_elt (MAILSTREAM *stream,unsigned long msgno)
+{
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ struct { /* old flags */
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged;
+ old.answered = elt->answered; old.draft = elt->draft;
+ old.user_flags = elt->user_flags;
+ tenex_read_flags (stream,elt);
+ if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
+ (old.flagged != elt->flagged) || (old.answered != elt->answered) ||
+ (old.draft != elt->draft) || (old.user_flags != elt->user_flags))
+ MM_FLAGS (stream,msgno); /* let top level know */
+ return elt;
+}
+
+/* Tenex read flags from file
+ * Accepts: MAIL stream
+ * Returns: cache element
+ */
+
+void tenex_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ unsigned long i,j;
+ /* noop if readonly and have valid flags */
+ if (stream->rdonly && elt->valid) return;
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 13,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,12) < 0) {
+ sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ /* calculate system flags */
+ i = (((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0');
+ elt->seen = i & fSEEN ? T : NIL; elt->deleted = i & fDELETED ? T : NIL;
+ elt->flagged = i & fFLAGGED ? T : NIL;
+ elt->answered = i & fANSWERED ? T : NIL; elt->draft = i & fDRAFT ? T : NIL;
+ LOCAL->buf[10] = '\0'; /* tie off flags */
+ j = strtoul(LOCAL->buf,NIL,8);/* get user flags value */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ elt->valid = T; /* have valid flags now */
+}
+
+/* Tenex update status string
+ * Accepts: MAIL stream
+ * message number
+ * flag saying whether or not to sync
+ */
+
+void tenex_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag)
+{
+ time_t tp[2];
+ struct stat sbuf;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ unsigned long j,k = 0;
+ /* readonly */
+ if (stream->rdonly || !elt->valid) tenex_read_flags (stream,elt);
+ else { /* readwrite */
+ j = elt->user_flags; /* get user flags */
+ /* reverse bits (dontcha wish we had CIRC?) */
+ while (j) k |= 1 << (29 - find_rightmost_bit (&j));
+ /* print new flag string */
+ sprintf (LOCAL->buf,"%010lo%02o",k,(unsigned)
+ (fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)));
+ /* get to that place in the file */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 13,L_SET);
+ /* write new flags */
+ write (LOCAL->fd,LOCAL->buf,12);
+ if (syncflag) { /* sync if requested */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ tp[1] = LOCAL->filetime = sbuf.st_mtime;
+ tp[0] = time (0); /* make sure read is later */
+ utime (stream->mailbox,tp);
+ }
+ }
+}
+
+/* Tenex locate header for a message
+ * Accepts: MAIL stream
+ * message number
+ * pointer to returned header size
+ * Returns: position of header in file
+ */
+
+unsigned long tenex_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size)
+{
+ unsigned long siz;
+ long i = 0;
+ char c = '\0';
+ char *s = NIL;
+ MESSAGECACHE *elt = tenex_elt (stream,msgno);
+ unsigned long ret = elt->private.special.offset +
+ elt->private.special.text.size;
+ unsigned long msiz = tenex_size (stream,msgno);
+ /* is header size known? */
+ if (!(*size = elt->private.msg.header.text.size)) {
+ lseek (LOCAL->fd,ret,L_SET);/* get to header position */
+ /* search message for LF LF */
+ for (siz = 0; siz < msiz; siz++) {
+ if (--i <= 0) /* read another buffer as necessary */
+ read (LOCAL->fd,s = LOCAL->buf,i = min (msiz-siz,(long) MAILTMPLEN));
+ /* two newline sequence? */
+ if ((c == '\012') && (*s == '\012')) {
+ /* yes, note for later */
+ elt->private.msg.header.text.size = (*size = siz + 1);
+
+ return ret; /* return to caller */
+ }
+ else c = *s++; /* next character */
+ }
+ /* header consumes entire message */
+ elt->private.msg.header.text.size = *size = msiz;
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/unix/truncate.c b/imap/src/osdep/unix/truncate.c
new file mode 100644
index 00000000..3dde1072
--- /dev/null
+++ b/imap/src/osdep/unix/truncate.c
@@ -0,0 +1,43 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Truncate a file
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 30 June 1994
+ * Last Edited: 30 August 2006
+ */
+
+/* Emulator for ftruncate() call
+ * Accepts: file descriptor
+ * length
+ * Returns: 0 if success, -1 if failure
+ */
+
+int ftruncate (int fd,off_t length)
+{
+ struct flock fb;
+ fb.l_whence = 0;
+ fb.l_len = 0;
+ fb.l_start = length;
+ fb.l_type = F_WRLCK; /* write lock on file space */
+ return fcntl (fd,F_FREESP,&fb);
+}
diff --git a/imap/src/osdep/unix/tz_bsd.c b/imap/src/osdep/unix/tz_bsd.c
new file mode 100644
index 00000000..75ee624c
--- /dev/null
+++ b/imap/src/osdep/unix/tz_bsd.c
@@ -0,0 +1,38 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: BSD-style Time Zone String
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 30 August 1994
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Append local timezone name
+ * Accepts: destination string
+ */
+
+void rfc822_timezone (char *s,void *t)
+{
+ /* append timezone from tm struct */
+ sprintf (s + strlen (s)," (%.50s)",((struct tm *) t)->tm_zone);
+}
diff --git a/imap/src/osdep/unix/tz_nul.c b/imap/src/osdep/unix/tz_nul.c
new file mode 100644
index 00000000..d2dd2895
--- /dev/null
+++ b/imap/src/osdep/unix/tz_nul.c
@@ -0,0 +1,36 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Null Time Zone String (unknown)
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 30 August 1994
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Append local timezone name
+ * Accepts: destination string
+ */
+
+void rfc822_timezone (char *s,void *t)
+{
+}
diff --git a/imap/src/osdep/unix/tz_sv4.c b/imap/src/osdep/unix/tz_sv4.c
new file mode 100644
index 00000000..409adf14
--- /dev/null
+++ b/imap/src/osdep/unix/tz_sv4.c
@@ -0,0 +1,39 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: SVR4-style Time Zone String
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 30 August 1994
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Append local timezone name
+ * Accepts: destination string
+ */
+
+void rfc822_timezone (char *s,void *t)
+{
+ tzset (); /* get timezone from TZ environment stuff */
+ sprintf (s + strlen (s)," (%.50s)",
+ tzname[daylight ? (((struct tm *) t)->tm_isdst > 0) : 0]);
+}
diff --git a/imap/src/osdep/unix/unix.c b/imap/src/osdep/unix/unix.c
new file mode 100644
index 00000000..be3c437b
--- /dev/null
+++ b/imap/src/osdep/unix/unix.c
@@ -0,0 +1,2708 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX mail routines
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@Washington.EDU
+ *
+ * Date: 20 December 1989
+ * Last Edited: 27 March 2008
+ */
+
+
+/* DEDICATION
+ *
+ * This file is dedicated to my dog, Unix, also known as Yun-chan and
+ * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix
+ * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after
+ * a two-month bout with cirrhosis of the liver.
+ *
+ * He was a dear friend, and I miss him terribly.
+ *
+ * Lift a leg, Yunie. Luv ya forever!!!!
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <signal.h>
+#include "mail.h"
+#include "osdep.h"
+#include <time.h>
+#include <sys/stat.h>
+#include "unix.h"
+#include "pseudo.h"
+#include "fdstring.h"
+#include "misc.h"
+#include "dummy.h"
+
+/* UNIX I/O stream local data */
+
+typedef struct unix_local {
+ unsigned int dirty : 1; /* disk copy needs updating */
+ unsigned int ddirty : 1; /* double-dirty, ping becomes checkpoint */
+ unsigned int pseudo : 1; /* uses a pseudo message */
+ unsigned int appending : 1; /* don't mark new messages as old */
+ int fd; /* mailbox file descriptor */
+ int ld; /* lock file descriptor */
+ char *lname; /* lock file name */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ time_t lastsnarf; /* last snarf time (for mbox driver) */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ unsigned long uid; /* current text uid */
+ SIZEDTEXT text; /* current text */
+ unsigned long textlen; /* current text length */
+ char *line; /* returned line */
+ char *linebuf; /* line readin buffer */
+ unsigned long linebuflen; /* current line readin buffer length */
+} UNIXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((UNIXLOCAL *) stream->local)
+
+
+/* UNIX protected file structure */
+
+typedef struct unix_file {
+ MAILSTREAM *stream; /* current stream */
+ off_t curpos; /* current file position */
+ off_t protect; /* protected position */
+ off_t filepos; /* current last written file position */
+ char *buf; /* overflow buffer */
+ size_t buflen; /* current overflow buffer length */
+ char *bufpos; /* current buffer position */
+} UNIXFILE;
+
+/* Function prototypes */
+
+DRIVER *unix_valid (char *name);
+long unix_isvalid_fd (int fd);
+void *unix_parameters (long function,void *value);
+void unix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void unix_list (MAILSTREAM *stream,char *ref,char *pat);
+void unix_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long unix_create (MAILSTREAM *stream,char *mailbox);
+long unix_delete (MAILSTREAM *stream,char *mailbox);
+long unix_rename (MAILSTREAM *stream,char *old,char *newname);
+MAILSTREAM *unix_open (MAILSTREAM *stream);
+void unix_close (MAILSTREAM *stream,long options);
+char *unix_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long unix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt,
+ unsigned long *length,long flags);
+void unix_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long unix_ping (MAILSTREAM *stream);
+void unix_check (MAILSTREAM *stream);
+long unix_expunge (MAILSTREAM *stream,char *sequence,long options);
+long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long unix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+int unix_collect_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
+ STRING *msg);
+int unix_append_msgs (MAILSTREAM *stream,FILE *sf,FILE *df,SEARCHSET *set);
+
+void unix_abort (MAILSTREAM *stream);
+char *unix_file (char *dst,char *name);
+int unix_lock (char *file,int flags,int mode,DOTLOCK *lock,int op);
+void unix_unlock (int fd,MAILSTREAM *stream,DOTLOCK *lock);
+int unix_parse (MAILSTREAM *stream,DOTLOCK *lock,int op);
+char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size);
+unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr);
+unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt,
+ unsigned long uid,long flag);
+long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,DOTLOCK *lock,
+ long flags);
+long unix_extend (MAILSTREAM *stream,unsigned long size);
+void unix_write (UNIXFILE *f,char *s,unsigned long i);
+void unix_phys_write (UNIXFILE *f,char *buf,size_t size);
+
+/* mbox mail routines */
+
+/* Function prototypes */
+
+DRIVER *mbox_valid (char *name);
+long mbox_create (MAILSTREAM *stream,char *mailbox);
+long mbox_delete (MAILSTREAM *stream,char *mailbox);
+long mbox_rename (MAILSTREAM *stream,char *old,char *newname);
+long mbox_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *mbox_open (MAILSTREAM *stream);
+long mbox_ping (MAILSTREAM *stream);
+void mbox_check (MAILSTREAM *stream);
+long mbox_expunge (MAILSTREAM *stream,char *sequence,long options);
+long mbox_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+
+/* UNIX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER unixdriver = {
+ "unix", /* driver name */
+ /* driver flags */
+ DR_LOCAL|DR_MAIL|DR_LOCKING|DR_NONEWMAILRONLY|DR_XPOINT,
+ (DRIVER *) NIL, /* next driver */
+ unix_valid, /* mailbox is valid for us */
+ unix_parameters, /* manipulate parameters */
+ unix_scan, /* scan mailboxes */
+ unix_list, /* list mailboxes */
+ unix_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ unix_create, /* create mailbox */
+ unix_delete, /* delete mailbox */
+ unix_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ unix_open, /* open mailbox */
+ unix_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ unix_header, /* fetch message header */
+ unix_text, /* fetch message text */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ NIL, /* modify flags */
+ unix_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ unix_ping, /* ping mailbox to see if still alive */
+ unix_check, /* check for new messages */
+ unix_expunge, /* expunge deleted messages */
+ unix_copy, /* copy messages to another mailbox */
+ unix_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM unixproto = {&unixdriver};
+
+ /* driver parameters */
+static long unix_fromwidget = T;
+
+/* UNIX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *unix_valid (char *name)
+{
+ int fd;
+ DRIVER *ret = NIL;
+ char *t,file[MAILTMPLEN];
+ struct stat sbuf;
+ time_t tp[2];
+ errno = EINVAL; /* assume invalid argument */
+ /* must be non-empty file */
+ if ((t = dummy_file (file,name)) && !stat (t,&sbuf)) {
+ if (!sbuf.st_size)errno = 0;/* empty file */
+ else if ((fd = open (file,O_RDONLY,NIL)) >= 0) {
+ /* OK if mailbox format good */
+ if (unix_isvalid_fd (fd)) ret = &unixdriver;
+ else errno = -1; /* invalid format */
+ close (fd); /* close the file */
+ /* \Marked status? */
+ if ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) {
+ tp[0] = sbuf.st_atime; /* yes, preserve atime and mtime */
+ tp[1] = sbuf.st_mtime;
+ utime (file,tp); /* set the times */
+ }
+ }
+ }
+ return ret; /* return what we should */
+}
+
+/* UNIX mail test for valid mailbox
+ * Accepts: file descriptor
+ * scratch buffer
+ * Returns: T if valid, NIL otherwise
+ */
+
+long unix_isvalid_fd (int fd)
+{
+ int zn;
+ int ret = NIL;
+ char tmp[MAILTMPLEN],*s,*t,c = '\n';
+ memset (tmp,'\0',MAILTMPLEN);
+ if (read (fd,tmp,MAILTMPLEN-1) >= 0) {
+ for (s = tmp; (*s == '\r') || (*s == '\n') || (*s == ' ') || (*s == '\t');)
+ c = *s++;
+ if (c == '\n') VALID (s,t,ret,zn);
+ }
+ return ret; /* return what we should */
+}
+
+
+/* UNIX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *unix_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_INBOXPATH:
+ if (value) ret = dummy_file ((char *) value,"INBOX");
+ break;
+ case SET_FROMWIDGET:
+ unix_fromwidget = (long) value;
+ case GET_FROMWIDGET:
+ ret = (void *) unix_fromwidget;
+ break;
+ }
+ return ret;
+}
+
+/* UNIX mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void unix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* UNIX mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void unix_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* UNIX mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void unix_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* UNIX mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long unix_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,mbx[MAILTMPLEN],tmp[MAILTMPLEN];
+ long ret = NIL;
+ int i,fd;
+ time_t ti = time (0);
+ if (!(s = dummy_file (mbx,mailbox))) {
+ sprintf (tmp,"Can't create %.80s: invalid name",mailbox);
+ MM_LOG (tmp,ERROR);
+ }
+ /* create underlying file */
+ else if (dummy_create_path (stream,s,get_dir_protection (mailbox))) {
+ /* done if dir-only or whiner */
+ if (((s = strrchr (s,'/')) && !s[1]) ||
+ mail_parameters (NIL,GET_USERHASNOLIFE,NIL)) ret = T;
+ else if ((fd = open (mbx,O_WRONLY,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL))) < 0) {
+ sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno));
+ MM_LOG (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ else { /* initialize header */
+ memset (tmp,'\0',MAILTMPLEN);
+ sprintf (tmp,"From %s %sDate: ",pseudo_from,ctime (&ti));
+ rfc822_fixed_date (s = tmp + strlen (tmp));
+ /* write the pseudo-header */
+ sprintf (s += strlen (s),
+ "\nFrom: %s <%s@%s>\nSubject: %s\nX-IMAP: %010lu 0000000000",
+ pseudo_name,pseudo_from,mylocalhost (),pseudo_subject,
+ (unsigned long) ti);
+ for (i = 0; i < NUSERFLAGS; ++i) if (default_user_flag (i))
+ sprintf (s += strlen (s)," %s",default_user_flag (i));
+ sprintf (s += strlen (s),"\nStatus: RO\n\n%s\n\n",pseudo_msg);
+ if (write (fd,tmp,strlen (tmp)) > 0) ret = T;
+ else {
+ sprintf (tmp,"Can't initialize mailbox node %.80s: %s",mbx,
+ strerror (errno));
+ MM_LOG (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ close (fd); /* close file */
+ }
+ }
+ /* set proper protections */
+ return ret ? set_mbx_protections (mailbox,mbx) : NIL;
+}
+
+/* UNIX mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long unix_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return unix_rename (stream,mailbox,NIL);
+}
+
+
+/* UNIX mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long unix_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = NIL;
+ char c,*s = NIL;
+ char tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ DOTLOCK lockx;
+ int fd,ld;
+ long i;
+ struct stat sbuf;
+ MM_CRITICAL (stream); /* get the c-client lock */
+ if (!dummy_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'/')) && !s[1]))))
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ /* lock out other c-clients */
+ else if ((ld = lockname (lock,file,LOCK_EX|LOCK_NB,&i)) < 0)
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+
+ else {
+ if ((fd = unix_lock (file,O_RDWR,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL),
+ &lockx,LOCK_EX)) < 0)
+ sprintf (tmp,"Can't lock mailbox %.80s: %s",old,strerror (errno));
+ else {
+ if (newname) { /* want rename? */
+ /* found superior to destination name? */
+ if (s = strrchr (s,'/')) {
+ c = *++s; /* remember first character of inferior */
+ *s = '\0'; /* tie off to get just superior */
+ /* name doesn't exist, create it */
+ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,tmp,get_dir_protection (newname))) {
+ unix_unlock (fd,NIL,&lockx);
+ unix_unlock (ld,NIL,NIL);
+ unlink (lock);
+ MM_NOCRITICAL (stream);
+ return ret; /* return success or failure */
+ }
+ *s = c; /* restore full name */
+ }
+ if (rename (file,tmp))
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ else ret = T; /* set success */
+ }
+ else if (unlink (file))
+ sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
+ else ret = T; /* set success */
+ unix_unlock (fd,NIL,&lockx);
+ }
+ unix_unlock (ld,NIL,NIL); /* flush the lock */
+ unlink (lock);
+ }
+ MM_NOCRITICAL (stream); /* no longer critical */
+ if (!ret) MM_LOG (tmp,ERROR); /* log error */
+ return ret; /* return success or failure */
+}
+
+/* UNIX mail open
+ * Accepts: Stream to open
+ * Returns: Stream on success, NIL on failure
+ */
+
+MAILSTREAM *unix_open (MAILSTREAM *stream)
+{
+ long i;
+ int fd;
+ char tmp[MAILTMPLEN];
+ DOTLOCK lock;
+ long retry;
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return user_flags (&unixproto);
+ retry = stream->silent ? 1 : KODRETRY;
+ if (stream->local) fatal ("unix recycle stream");
+ stream->local = memset (fs_get (sizeof (UNIXLOCAL)),0,sizeof (UNIXLOCAL));
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ /* canonicalize the stream mailbox name */
+ if (!dummy_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* flush old name */
+ fs_give ((void **) &stream->mailbox);
+ /* save canonical name */
+ stream->mailbox = cpystr (tmp);
+ LOCAL->fd = LOCAL->ld = -1; /* no file or state locking yet */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ LOCAL->text.data = (unsigned char *) fs_get (CHUNKSIZE);
+ LOCAL->text.size = CHUNKSIZE - 1;
+ LOCAL->linebuf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->linebuflen = CHUNKSIZE - 1;
+ stream->sequence++; /* bump sequence number */
+
+ /* make lock for read/write access */
+ if (!stream->rdonly) while (retry) {
+ /* try to lock file */
+ if ((fd = lockname (tmp,stream->mailbox,LOCK_EX|LOCK_NB,&i)) < 0) {
+ /* suppressing kiss-of-death? */
+ if (stream->nokod) retry = 0;
+ /* no, first time through? */
+ else if (retry-- == KODRETRY) {
+ /* learned other guy's PID and can signal? */
+ if (i && !kill ((int) i,SIGUSR2)) {
+ sprintf (tmp,"Trying to get mailbox lock from process %ld",i);
+ MM_LOG (tmp,WARN);
+ }
+ else retry = 0; /* give up */
+ }
+ if (!stream->silent) { /* nothing if silent stream */
+ if (retry) sleep (1); /* wait a second before trying again */
+ else MM_LOG ("Mailbox is open by another process, access is readonly",
+ WARN);
+ }
+ }
+ else { /* got the lock, nobody else can alter state */
+ LOCAL->ld = fd; /* note lock's fd and name */
+ LOCAL->lname = cpystr (tmp);
+ /* make sure mode OK (don't use fchmod()) */
+ chmod (LOCAL->lname,(long) mail_parameters (NIL,GET_LOCKPROTECTION,NIL));
+ if (stream->silent) i = 0;/* silent streams won't accept KOD */
+ else { /* note our PID in the lock */
+ sprintf (tmp,"%d",getpid ());
+ write (fd,tmp,(i = strlen (tmp))+1);
+ }
+ ftruncate (fd,i); /* make sure tied off */
+ fsync (fd); /* make sure it's available */
+ retry = 0; /* no more need to try */
+ }
+ }
+
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ /* will we be able to get write access? */
+ if ((LOCAL->ld >= 0) && access (stream->mailbox,W_OK) && (errno == EACCES)) {
+ MM_LOG ("Can't get write access to mailbox, access is readonly",WARN);
+ flock (LOCAL->ld,LOCK_UN); /* release the lock */
+ close (LOCAL->ld); /* close the lock file */
+ LOCAL->ld = -1; /* no more lock fd */
+ unlink (LOCAL->lname); /* delete it */
+ }
+ /* reset UID validity */
+ stream->uid_validity = stream->uid_last = 0;
+ if (stream->silent && !stream->rdonly && (LOCAL->ld < 0))
+ unix_abort (stream); /* abort if can't get RW silent stream */
+ /* parse mailbox */
+ else if (unix_parse (stream,&lock,LOCK_SH)) {
+ unix_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream);
+ MM_NOCRITICAL (stream); /* done with critical */
+ }
+ if (!LOCAL) return NIL; /* failure if stream died */
+ /* make sure upper level knows readonly */
+ stream->rdonly = (LOCAL->ld < 0);
+ /* notify about empty mailbox */
+ if (!(stream->nmsgs || stream->silent)) MM_LOG ("Mailbox is empty",NIL);
+ if (!stream->rdonly) { /* flags stick if readwrite */
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft = T;
+ if (!stream->uid_nosticky) {/* users with lives get permanent keywords */
+ stream->perm_user_flags = 0xffffffff;
+ /* and maybe can create them too! */
+ stream->kwd_create = stream->user_flags[NUSERFLAGS-1] ? NIL : T;
+ }
+ }
+ return stream; /* return stream alive to caller */
+}
+
+
+/* UNIX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void unix_close (MAILSTREAM *stream,long options)
+{
+ int silent = stream->silent;
+ stream->silent = T; /* go silent */
+ /* expunge if requested */
+ if (options & CL_EXPUNGE) unix_expunge (stream,NIL,NIL);
+ /* else dump final checkpoint */
+ else if (LOCAL->dirty) unix_check (stream);
+ stream->silent = silent; /* restore old silence state */
+ unix_abort (stream); /* now punt the file and local data */
+}
+
+/* UNIX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+ /* lines to filter from header */
+static STRINGLIST *unix_hlines = NIL;
+
+char *unix_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned char *s,*t,*tl;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ elt = mail_elt (stream,msgno);/* get cache */
+ if (!unix_hlines) { /* once only code */
+ STRINGLIST *lines = unix_hlines = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "Status"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-Status"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-Keywords"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-UID"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-IMAP"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-IMAPbase"));
+ }
+ /* go to header position */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.msg.header.offset,L_SET);
+
+ if (flags & FT_INTERNAL) { /* initial data OK? */
+ if (elt->private.msg.header.text.size > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
+ elt->private.msg.header.text.size) + 1);
+ }
+ /* read message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.header.text.size);
+ /* got text, tie off string */
+ LOCAL->buf[*length = elt->private.msg.header.text.size] = '\0';
+ /* squeeze out CRs (in case from PC) */
+ for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t < tl; t++)
+ if (*t != '\r') *s++ = *t;
+ *s = '\0';
+ *length = s - LOCAL->buf; /* adjust length */
+ }
+ else { /* need to make a CRLF version */
+ read (LOCAL->fd,s = (char *) fs_get (elt->private.msg.header.text.size+1),
+ elt->private.msg.header.text.size);
+ /* tie off string, and convert to CRLF */
+ s[elt->private.msg.header.text.size] = '\0';
+ *length = strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,s,
+ elt->private.msg.header.text.size);
+ fs_give ((void **) &s); /* free readin buffer */
+ /* squeeze out spurious CRs */
+ for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t < tl; t++)
+ if ((*t != '\r') || (t[1] == '\n')) *s++ = *t;
+ *s = '\0';
+ *length = s - LOCAL->buf; /* adjust length */
+ }
+ *length = mail_filter (LOCAL->buf,*length,unix_hlines,FT_NOT);
+ return (char *) LOCAL->buf; /* return processed copy */
+}
+
+/* UNIX mail fetch message text
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T on success, NIL if failure
+ */
+
+long unix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ char *s;
+ unsigned long i;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mail_elt (stream,msgno);/* get cache element */
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ /* mark message seen and dirty */
+ elt->seen = elt->private.dirty = LOCAL->dirty = T;
+ MM_FLAGS (stream,msgno);
+ }
+ s = unix_text_work (stream,elt,&i,flags);
+ INIT (bs,mail_string,s,i); /* set up stringstruct */
+ return T; /* success */
+}
+
+/* UNIX mail fetch message text worker routine
+ * Accepts: MAIL stream
+ * message cache element
+ * pointer to returned header text length
+ * option flags
+ */
+
+char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt,
+ unsigned long *length,long flags)
+{
+ FDDATA d;
+ STRING bs;
+ unsigned char c,*s,*t,*tl,tmp[CHUNKSIZE];
+ /* go to text position */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.msg.text.offset,L_SET);
+ if (flags & FT_INTERNAL) { /* initial data OK? */
+ if (elt->private.msg.text.text.size > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
+ elt->private.msg.text.text.size) + 1);
+ }
+ /* read message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.text.text.size);
+ /* got text, tie off string */
+ LOCAL->buf[*length = elt->private.msg.text.text.size] = '\0';
+ /* squeeze out CRs (in case from PC) */
+ for (s = t = LOCAL->buf,tl = LOCAL->buf + *length; t < tl; t++)
+ if (*t != '\r') *s++ = *t;
+ *s = '\0';
+ *length = s - LOCAL->buf; /* adjust length */
+ return (char *) LOCAL->buf;
+ }
+
+ /* have it cached already? */
+ if (elt->private.uid != LOCAL->uid) {
+ /* not cached, cache it now */
+ LOCAL->uid = elt->private.uid;
+ /* is buffer big enough? */
+ if (elt->rfc822_size > LOCAL->text.size) {
+ /* excessively conservative, but the right thing is too hard to do */
+ fs_give ((void **) &LOCAL->text.data);
+ LOCAL->text.data = (unsigned char *)
+ fs_get ((LOCAL->text.size = elt->rfc822_size) + 1);
+ }
+ d.fd = LOCAL->fd; /* yes, set up file descriptor */
+ d.pos = elt->private.special.offset + elt->private.msg.text.offset;
+ d.chunk = tmp; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE; /* file chunk size */
+ INIT (&bs,fd_string,&d,elt->private.msg.text.text.size);
+ for (s = (char *) LOCAL->text.data; SIZE (&bs);) switch (c = SNX (&bs)) {
+ case '\r': /* carriage return seen */
+ break;
+ case '\n':
+ *s++ = '\r'; /* insert a CR */
+ default:
+ *s++ = c; /* copy characters */
+ }
+ *s = '\0'; /* tie off buffer */
+ /* calculate length of cached data */
+ LOCAL->textlen = s - LOCAL->text.data;
+ }
+ *length = LOCAL->textlen; /* return from cache */
+ return (char *) LOCAL->text.data;
+}
+
+/* UNIX per-message modify flag
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void unix_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ /* only after finishing */
+ if (elt->valid) elt->private.dirty = LOCAL->dirty = T;
+}
+
+
+/* UNIX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long unix_ping (MAILSTREAM *stream)
+{
+ DOTLOCK lock;
+ struct stat sbuf;
+ long reparse;
+ /* big no-op if not readwrite */
+ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock) {
+ if (stream->rdonly) { /* does he want to give up readwrite? */
+ /* checkpoint if we changed something */
+ if (LOCAL->dirty) unix_check (stream);
+ flock (LOCAL->ld,LOCK_UN);/* release readwrite lock */
+ close (LOCAL->ld); /* close the readwrite lock file */
+ LOCAL->ld = -1; /* no more readwrite lock fd */
+ unlink (LOCAL->lname); /* delete the readwrite lock file */
+ }
+ else { /* see if need to reparse */
+ if (!(reparse = (long) mail_parameters (NIL,GET_NETFSSTATBUG,NIL))) {
+ /* get current mailbox size */
+ if (LOCAL->fd >= 0) fstat (LOCAL->fd,&sbuf);
+ else if (stat (stream->mailbox,&sbuf)) {
+ sprintf (LOCAL->buf,"Mailbox stat failed, aborted: %s",
+ strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ unix_abort (stream);
+ return NIL;
+ }
+ reparse = (sbuf.st_size != LOCAL->filesize);
+ }
+ /* parse if mailbox changed */
+ if ((LOCAL->ddirty || reparse) && unix_parse (stream,&lock,LOCK_EX)) {
+ /* force checkpoint if double-dirty */
+ if (LOCAL->ddirty) unix_rewrite (stream,NIL,&lock,NIL);
+ /* unlock mailbox */
+ else unix_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream); /* and stream */
+ MM_NOCRITICAL (stream); /* done with critical */
+ }
+ }
+ }
+ return LOCAL ? LONGT : NIL; /* return if still alive */
+}
+
+/* UNIX mail check mailbox
+ * Accepts: MAIL stream
+ */
+
+void unix_check (MAILSTREAM *stream)
+{
+ DOTLOCK lock;
+ /* parse and lock mailbox */
+ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock &&
+ unix_parse (stream,&lock,LOCK_EX)) {
+ /* any unsaved changes? */
+ if (LOCAL->dirty && unix_rewrite (stream,NIL,&lock,NIL)) {
+ if (!stream->silent) MM_LOG ("Checkpoint completed",NIL);
+ }
+ /* no checkpoint needed, just unlock */
+ else unix_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream); /* unlock the stream */
+ MM_NOCRITICAL (stream); /* done with critical */
+ }
+}
+
+
+/* UNIX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long unix_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ unsigned long i;
+ DOTLOCK lock;
+ char *msg = NIL;
+ /* parse and lock mailbox */
+ if (ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ LOCAL && (LOCAL->ld >= 0) && !stream->lock &&
+ unix_parse (stream,&lock,LOCK_EX)) {
+ /* check expunged messages if not dirty */
+ for (i = 1; !LOCAL->dirty && (i <= stream->nmsgs); i++) {
+ MESSAGECACHE *elt = mail_elt (stream,i);
+ if (mail_elt (stream,i)->deleted) LOCAL->dirty = T;
+ }
+ if (!LOCAL->dirty) { /* not dirty and no expunged messages */
+ unix_unlock (LOCAL->fd,stream,&lock);
+ msg = "No messages deleted, so no update needed";
+ }
+ else if (unix_rewrite (stream,&i,&lock,sequence ? LONGT : NIL)) {
+ if (i) sprintf (msg = LOCAL->buf,"Expunged %lu messages",i);
+ else msg = "Mailbox checkpointed, but no messages expunged";
+ }
+ /* rewrite failed */
+ else unix_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream); /* unlock the stream */
+ MM_NOCRITICAL (stream); /* done with critical */
+ if (msg && !stream->silent) MM_LOG (msg,NIL);
+ }
+ else if (!stream->silent) MM_LOG("Expunge ignored on readonly mailbox",WARN);
+ return ret;
+}
+
+/* UNIX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if copy successful, else NIL
+ */
+
+long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ int fd;
+ char *s,file[MAILTMPLEN];
+ DOTLOCK lock;
+ time_t tp[2];
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ long ret = T;
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ copyuid_t cu = (copyuid_t) (mail_parameters (NIL,GET_USERHASNOLIFE,NIL) ?
+ NIL : mail_parameters (NIL,GET_COPYUID,NIL));
+ SEARCHSET *source = cu ? mail_newsearchset () : NIL;
+ SEARCHSET *dest = cu ? mail_newsearchset () : NIL;
+ MAILSTREAM *tstream = NIL;
+ DRIVER *d;
+ for (d = (DRIVER *) mail_parameters (NIL,GET_DRIVERS,NIL);
+ (d && strcmp (d->name,"mbox") && !(d->flags & DR_DISABLE));
+ d = d->next); /* see if mbox driver active */
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* make sure destination is valid */
+ if (!((d && mbox_valid (mailbox) && (mailbox = "mbox")) ||
+ unix_valid (mailbox) || !errno))
+ switch (errno) {
+ case ENOENT: /* no such file? */
+ if (compare_cstring (mailbox,"INBOX")) {
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ }
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ unix_create (NIL,"INBOX");/* create empty INBOX */
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid UNIX-format mailbox name: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a UNIX-format mailbox: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+
+ /* try to open rewrite for UIDPLUS */
+ if ((tstream = mail_open_work (&unixdriver,NIL,mailbox,
+ OP_SILENT|OP_NOKOD)) && tstream->rdonly)
+ tstream = mail_close (tstream);
+ if (cu && !tstream) { /* wanted a COPYUID? */
+ sprintf (LOCAL->buf,"Unable to write-open mailbox for COPYUID: %.80s",
+ mailbox);
+ MM_LOG (LOCAL->buf,WARN);
+ cu = NIL; /* don't try to do COPYUID */
+ }
+ LOCAL->buf[0] = '\0';
+ MM_CRITICAL (stream); /* go critical */
+ if ((fd = unix_lock (dummy_file (file,mailbox),O_WRONLY|O_APPEND,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL),
+ &lock,LOCK_EX)) < 0) {
+ MM_NOCRITICAL (stream); /* done with critical */
+ sprintf (LOCAL->buf,"Can't open destination mailbox: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);/* log the error */
+ return NIL; /* failed */
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ /* write all requested messages to mailbox */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size);
+ if (write (fd,LOCAL->buf,elt->private.special.text.size) < 0) ret = NIL;
+ else { /* internal header succeeded */
+ s = unix_header (stream,i,&j,FT_INTERNAL);
+ /* header size, sans trailing newline */
+ if (j && (s[j - 2] == '\n')) j--;
+ if (write (fd,s,j) < 0) ret = NIL;
+ else { /* message header succeeded */
+ j = tstream ? /* write UIDPLUS data if have readwrite */
+ unix_xstatus (stream,LOCAL->buf,elt,++(tstream->uid_last),LONGT) :
+ unix_xstatus (stream,LOCAL->buf,elt,NIL,NIL);
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ else { /* message status succeeded */
+ s = unix_text_work (stream,elt,&j,FT_INTERNAL);
+ if ((write (fd,s,j) < 0) || (write (fd,"\n",1) < 0)) ret = NIL;
+ else if (cu) { /* need to pass back new UID? */
+ mail_append_set (source,mail_uid (stream,i));
+ mail_append_set (dest,tstream->uid_last);
+ }
+ }
+ }
+ }
+ }
+
+ if (!ret || fsync (fd)) { /* force out the update */
+ sprintf (LOCAL->buf,"Message copy failed: %s",strerror (errno));
+ ftruncate (fd,sbuf.st_size);
+ ret = NIL;
+ }
+ /* force UIDVALIDITY assignment now */
+ if (tstream && !tstream->uid_validity) tstream->uid_validity = time (0);
+ /* return sets if doing COPYUID */
+ if (cu && ret) (*cu) (stream,mailbox,tstream->uid_validity,source,dest);
+ else { /* flush any sets we may have built */
+ mail_free_searchset (&source);
+ mail_free_searchset (&dest);
+ }
+ tp[1] = time (0); /* set mtime to now */
+ if (ret) tp[0] = tp[1] - 1; /* set atime to now-1 if successful copy */
+ else tp[0] = /* else preserve \Marked status */
+ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ?
+ sbuf.st_atime : tp[1];
+ utime (file,tp); /* set the times */
+ unix_unlock (fd,NIL,&lock); /* unlock and close mailbox */
+ if (tstream) { /* update last UID if we can */
+ UNIXLOCAL *local = (UNIXLOCAL *) tstream->local;
+ local->dirty = T; /* do a rewrite */
+ local->appending = T; /* but not at the cost of marking as old */
+ tstream = mail_close (tstream);
+ }
+ /* log the error */
+ if (!ret) MM_LOG (LOCAL->buf,ERROR);
+ /* delete if requested message */
+ else if (options & CP_MOVE) for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence)
+ elt->deleted = elt->private.dirty = LOCAL->dirty = T;
+ MM_NOCRITICAL (stream); /* release critical */
+ return ret;
+}
+
+/* UNIX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+#define BUFLEN 8*MAILTMPLEN
+
+long unix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd;
+ unsigned long i;
+ char *flags,*date,buf[BUFLEN],tmp[MAILTMPLEN],file[MAILTMPLEN];
+ time_t tp[2];
+ FILE *sf,*df;
+ MESSAGECACHE elt;
+ DOTLOCK lock;
+ STRING *message;
+ unsigned long uidlocation = 0;
+ appenduid_t au = (appenduid_t)
+ (mail_parameters (NIL,GET_USERHASNOLIFE,NIL) ? NIL :
+ mail_parameters (NIL,GET_APPENDUID,NIL));
+ SEARCHSET *dst = au ? mail_newsearchset () : NIL;
+ long ret = LONGT;
+ MAILSTREAM *tstream = NIL;
+ if (!stream) { /* stream specified? */
+ stream = &unixproto; /* no, default stream to prototype */
+ for (i = 0; i < NUSERFLAGS && stream->user_flags[i]; ++i)
+ fs_give ((void **) &stream->user_flags[i]);
+ }
+ if (!unix_valid (mailbox)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (compare_cstring (mailbox,"INBOX")) {
+ MM_NOTIFY (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ unix_create (NIL,"INBOX"); /* create empty INBOX */
+ case 0: /* merely empty file? */
+ tstream = stream;
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid UNIX-format mailbox name: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a UNIX-format mailbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ /* get sniffing stream for keywords */
+ else if (!(tstream = mail_open (NIL,mailbox,
+ OP_READONLY|OP_SILENT|OP_NOKOD|OP_SNIFF))) {
+ sprintf (tmp,"Unable to examine mailbox for APPEND: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+
+ /* get first message */
+ if (!MM_APPEND (af) (tstream,data,&flags,&date,&message)) return NIL;
+ if (!(sf = tmpfile ())) { /* must have scratch file */
+ sprintf (tmp,".%lx.%lx",(unsigned long) time (0),(unsigned long)getpid ());
+ if (!stat (tmp,&sbuf) || !(sf = fopen (tmp,"wb+"))) {
+ sprintf (tmp,"Unable to create scratch file: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ unlink (tmp);
+ }
+ do { /* parse date */
+ if (!date) rfc822_date (date = tmp);
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ MM_LOG (tmp,ERROR);
+ }
+ else { /* user wants to suppress time zones? */
+ if (mail_parameters (NIL,GET_NOTIMEZONES,NIL)) {
+ time_t when = mail_longdate (&elt);
+ date = ctime (&when); /* use traditional date */
+ }
+ /* use POSIX-style date */
+ else date = mail_cdate (tmp,&elt);
+ if (!SIZE (message)) MM_LOG ("Append of zero-length message",ERROR);
+ else if (!unix_collect_msg (tstream,sf,flags,date,message)) {
+ sprintf (tmp,"Error writing scratch file: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ }
+ /* get next message */
+ else if (MM_APPEND (af) (tstream,data,&flags,&date,&message)) continue;
+ }
+ fclose (sf); /* punt scratch file */
+ return NIL; /* give up */
+ } while (message); /* until no more messages */
+ if (fflush (sf)) {
+ sprintf (tmp,"Error finishing scratch file: %.80s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ fclose (sf); /* punt scratch file */
+ return NIL; /* give up */
+ }
+ i = ftell (sf); /* size of scratch file */
+ /* close sniffing stream */
+ if (tstream != stream) tstream = mail_close (tstream);
+
+ MM_CRITICAL (stream); /* go critical */
+ /* try to open readwrite for UIDPLUS */
+ if ((tstream = mail_open_work (&unixdriver,NIL,mailbox,
+ OP_SILENT|OP_NOKOD)) && tstream->rdonly)
+ tstream = mail_close (tstream);
+ if (au && !tstream) { /* wanted an APPENDUID? */
+ sprintf (tmp,"Unable to re-open mailbox for APPENDUID: %.80s",mailbox);
+ MM_LOG (tmp,WARN);
+ au = NIL;
+ }
+ if (((fd = unix_lock (dummy_file (file,mailbox),O_WRONLY|O_APPEND,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL),
+ &lock,LOCK_EX)) < 0) ||
+ !(df = fdopen (fd,"ab"))) {
+ MM_NOCRITICAL (stream); /* done with critical */
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ rewind (sf);
+ tp[1] = time (0); /* set mtime to now */
+ /* write all messages */
+ if (!unix_append_msgs (tstream,sf,df,au ? dst : NIL) ||
+ (fflush (df) == EOF) || fsync (fd)) {
+ sprintf (buf,"Message append failed: %s",strerror (errno));
+ MM_LOG (buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ tp[0] = /* preserve \Marked status */
+ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ?
+ sbuf.st_atime : tp[1];
+ ret = NIL; /* return error */
+ }
+ else tp[0] = tp[1] - 1; /* set atime to now-1 if successful copy */
+ utime (file,tp); /* set the times */
+ fclose (sf); /* done with scratch file */
+ /* force UIDVALIDITY assignment now */
+ if (tstream && !tstream->uid_validity) tstream->uid_validity = time (0);
+ /* return sets if doing APPENDUID */
+ if (au && ret) (*au) (mailbox,tstream->uid_validity,dst);
+ else mail_free_searchset (&dst);
+ unix_unlock (fd,NIL,&lock); /* unlock and close mailbox */
+ fclose (df); /* note that unix_unlock() released the fd */
+ if (tstream) { /* update last UID if we can */
+ UNIXLOCAL *local = (UNIXLOCAL *) tstream->local;
+ local->dirty = T; /* do a rewrite */
+ local->appending = T; /* but not at the cost of marking as old */
+ tstream = mail_close (tstream);
+ }
+ MM_NOCRITICAL (stream); /* release critical */
+ return ret;
+}
+
+/* Collect and write single message to append scratch file
+ * Accepts: MAIL stream
+ * scratch file
+ * flags
+ * date
+ * message stringstruct
+ * Returns: NIL if write error, else T
+ */
+
+int unix_collect_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
+ STRING *msg)
+{
+ unsigned char *s,*t;
+ unsigned long uf;
+ long f = mail_parse_flags (stream,flags,&uf);
+ /* write metadata, note date ends with NL */
+ if (fprintf (sf,"%ld %lu %s",f,SIZE (msg) + 1,date) < 0) return NIL;
+ while (uf) /* write user flags */
+ if ((s = stream->user_flags[find_rightmost_bit (&uf)]) &&
+ (fprintf (sf," %s",s) < 0)) return NIL;
+ if (putc ('\n',sf) == EOF) return NIL;
+ while (SIZE (msg)) { /* copy text to scratch file */
+ for (s = (unsigned char *) msg->curpos, t = s + msg->cursize; s < t; ++s)
+ if (!*s) *s = 0x80; /* disallow NUL */
+ /* write buffered text */
+ if (fwrite (msg->curpos,1,msg->cursize,sf) == msg->cursize)
+ SETPOS (msg,GETPOS (msg) + msg->cursize);
+ else return NIL; /* failed */
+ }
+ /* write trailing newline and return */
+ return (putc ('\n',sf) == EOF) ? NIL : T;
+}
+
+/* Append messages from scratch file to mailbox
+ * Accepts: MAIL stream
+ * source file
+ * destination file
+ * uidset to update if non-NIL
+ * Returns: T if success, NIL if failure
+ */
+
+int unix_append_msgs (MAILSTREAM *stream,FILE *sf,FILE *df,SEARCHSET *set)
+{
+ int ti,zn,c;
+ long f;
+ unsigned long i,j;
+ char *x,tmp[MAILTMPLEN];
+ int hdrp = T;
+ /* get message metadata line */
+ while (fgets (tmp,MAILTMPLEN,sf)) {
+ if (!(isdigit (tmp[0]) && strchr (tmp,'\n'))) return NIL;
+ f = strtol (tmp,&x,10); /* get flags */
+ if (!((*x++ == ' ') && isdigit (*x))) return NIL;
+ i = strtoul (x,&x,10); /* get message size */
+ if ((*x++ != ' ') || /* build initial header */
+ (fprintf (df,"From %s@%s %sStatus: ",myusername(),mylocalhost(),x)<0)||
+ (f&fSEEN && (putc ('R',df) == EOF)) ||
+ (fputs ("\nX-Status: ",df) == EOF) ||
+ (f&fDELETED && (putc ('D',df) == EOF)) ||
+ (f&fFLAGGED && (putc ('F',df) == EOF)) ||
+ (f&fANSWERED && (putc ('A',df) == EOF)) ||
+ (f&fDRAFT && (putc ('T',df) == EOF)) ||
+ (fputs ("\nX-Keywords:",df) == EOF)) return NIL;
+ /* copy keywords */
+ while ((c = getc (sf)) != '\n') switch (c) {
+ case EOF:
+ return NIL;
+ default:
+ if (putc (c,df) == EOF) return NIL;
+ }
+ if ((putc ('\n',df) == EOF) ||
+ (set && (fprintf (df,"X-UID: %lu\n",++(stream->uid_last)) < 0)))
+ return NIL;
+
+ for (c = '\n'; i && fgets (tmp,MAILTMPLEN,sf); c = tmp[j-1]) {
+ /* get read line length */
+ if (i < (j = strlen (tmp))) fatal ("unix_append_msgs overrun");
+ i -= j; /* number of bytes left */
+ /* squish out CRs (note also copies NUL) */
+ for (x = tmp; x = strchr (x,'\r'); --j) memmove (x,x+1,j-(x-tmp));
+ if (!j) continue; /* do nothing if line emptied */
+ /* start of line? */
+ if ((c == '\n')) switch (tmp[0]) {
+ case 'F': /* possible "From " (case counts here) */
+ if ((j > 4) && (tmp[0] == 'F') && (tmp[1] == 'r') && (tmp[2] == 'o') &&
+ (tmp[3] == 'm') && (tmp[4] == ' ')) {
+ if (!unix_fromwidget) {
+ VALID (tmp,x,ti,zn);/* conditional, only write widget if */
+ if (!ti) break; /* it looks like a valid header */
+ } /* write the widget */
+ if (putc ('>',df) == EOF) return NIL;
+ }
+ break;
+ case 'S': case 's': /* possible "Status:" */
+ if (hdrp && (j > 6) && ((tmp[1] == 't') || (tmp[1] == 'T')) &&
+ ((tmp[2] == 'a') || (tmp[2] == 'A')) &&
+ ((tmp[3] == 't') || (tmp[3] == 'T')) &&
+ ((tmp[4] == 'u') || (tmp[4] == 'U')) &&
+ ((tmp[5] == 's') || (tmp[5] == 'S')) && (tmp[6] == ':') &&
+ (fputs ("X-Original-",df) == EOF)) return NIL;
+ break;
+ case 'X': case 'x': /* possible X-??? header */
+ if (hdrp && (tmp[1] == '-') &&
+ /* possible X-UID: */
+ (((j > 5) && ((tmp[2] == 'U') || (tmp[2] == 'u')) &&
+ ((tmp[3] == 'I') || (tmp[3] == 'i')) &&
+ ((tmp[4] == 'D') || (tmp[4] == 'd')) && (tmp[5] == ':')) ||
+ /* possible X-IMAP: */
+ ((j > 6) && ((tmp[2] == 'I') || (tmp[2] == 'i')) &&
+ ((tmp[3] == 'M') || (tmp[3] == 'm')) &&
+ ((tmp[4] == 'A') || (tmp[4] == 'a')) &&
+ ((tmp[5] == 'P') || (tmp[5] == 'p')) &&
+ ((tmp[6] == ':') ||
+ /* or X-IMAPbase: */
+ ((j > 10) && ((tmp[6] == 'b') || (tmp[6] == 'B')) &&
+ ((tmp[7] == 'a') || (tmp[7] == 'A')) &&
+ ((tmp[8] == 's') || (tmp[8] == 'S')) &&
+ ((tmp[9] == 'e') || (tmp[9] == 'E')) && (tmp[10] == ':')))) ||
+ /* possible X-Status: */
+ ((j > 8) && ((tmp[2] == 'S') || (tmp[2] == 's')) &&
+ ((tmp[3] == 't') || (tmp[3] == 'T')) &&
+ ((tmp[4] == 'a') || (tmp[4] == 'A')) &&
+ ((tmp[5] == 't') || (tmp[5] == 'T')) &&
+ ((tmp[6] == 'u') || (tmp[6] == 'U')) &&
+ ((tmp[7] == 's') || (tmp[7] == 'S')) && (tmp[8] == ':')) ||
+ /* possible X-Keywords: */
+ ((j > 10) && ((tmp[2] == 'K') || (tmp[2] == 'k')) &&
+ ((tmp[3] == 'e') || (tmp[3] == 'E')) &&
+ ((tmp[4] == 'y') || (tmp[4] == 'Y')) &&
+ ((tmp[5] == 'w') || (tmp[5] == 'W')) &&
+ ((tmp[6] == 'o') || (tmp[6] == 'O')) &&
+ ((tmp[7] == 'r') || (tmp[7] == 'R')) &&
+ ((tmp[8] == 'd') || (tmp[8] == 'D')) &&
+ ((tmp[9] == 's') || (tmp[9] == 'S')) && (tmp[10] == ':'))) &&
+ (fputs ("X-Original-",df) == EOF)) return NIL;
+ case '\n': /* blank line */
+ hdrp = NIL;
+ break;
+ default: /* nothing to do */
+ break;
+ }
+ /* just write the line */
+ if (fwrite (tmp,1,j,df) != j) return NIL;
+ }
+ if (i) return NIL; /* didn't read entire message */
+ /* update set */
+ if (stream) mail_append_set (set,stream->uid_last);
+ }
+ return T;
+}
+
+/* Internal routines */
+
+
+/* UNIX mail abort stream
+ * Accepts: MAIL stream
+ */
+
+void unix_abort (MAILSTREAM *stream)
+{
+ if (LOCAL) { /* only if a file is open */
+ if (LOCAL->fd >= 0) close (LOCAL->fd);
+ if (LOCAL->ld >= 0) { /* have a mailbox lock? */
+ flock (LOCAL->ld,LOCK_UN);/* yes, release the lock */
+ close (LOCAL->ld); /* close the lock file */
+ unlink (LOCAL->lname); /* and delete it */
+ }
+ if (LOCAL->lname) fs_give ((void **) &LOCAL->lname);
+ /* free local text buffers */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data);
+ if (LOCAL->linebuf) fs_give ((void **) &LOCAL->linebuf);
+ if (LOCAL->line) fs_give ((void **) &LOCAL->line);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* UNIX open and lock mailbox
+ * Accepts: file name to open/lock
+ * file open mode
+ * destination buffer for lock file name
+ * type of locking operation (LOCK_SH or LOCK_EX)
+ */
+
+int unix_lock (char *file,int flags,int mode,DOTLOCK *lock,int op)
+{
+ int fd;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ (*bn) (BLOCK_FILELOCK,NIL);
+ /* try locking the easy way */
+ if (dotlock_lock (file,lock,-1)) {
+ /* got dotlock file, easy open */
+ if ((fd = open (file,flags,mode)) >= 0) flock (fd,op);
+ else dotlock_unlock (lock); /* open failed, free the dotlock */
+ }
+ /* no dot lock file, open file now */
+ else if ((fd = open (file,flags,mode)) >= 0) {
+ /* try paranoid way to make a dot lock file */
+ if (dotlock_lock (file,lock,fd)) {
+ close (fd); /* get fresh fd in case of timing race */
+ if ((fd = open (file,flags,mode)) >= 0) flock (fd,op);
+ /* open failed, free the dotlock */
+ else dotlock_unlock (lock);
+ }
+ else flock (fd,op); /* paranoid way failed, just flock() it */
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return fd;
+}
+
+/* UNIX unlock and close mailbox
+ * Accepts: file descriptor
+ * (optional) mailbox stream to check atime/mtime
+ * (optional) lock file name
+ */
+
+void unix_unlock (int fd,MAILSTREAM *stream,DOTLOCK *lock)
+{
+ if (stream) { /* need to muck with times? */
+ struct stat sbuf;
+ time_t tp[2];
+ time_t now = time (0);
+ fstat (fd,&sbuf); /* get file times */
+ if (LOCAL->ld >= 0) { /* yes, readwrite session? */
+ tp[0] = now; /* set atime to now */
+ /* set mtime to (now - 1) if necessary */
+ tp[1] = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1;
+ }
+ else if (stream->recent) { /* readonly with recent messages */
+ if ((sbuf.st_atime >= sbuf.st_mtime) ||
+ (sbuf.st_atime >= sbuf.st_ctime))
+ /* keep past mtime, whack back atime */
+ tp[0] = (tp[1] = (sbuf.st_mtime < now) ? sbuf.st_mtime : now) - 1;
+ else now = 0; /* no time change needed */
+ }
+ /* readonly with no recent messages */
+ else if ((sbuf.st_atime < sbuf.st_mtime) ||
+ (sbuf.st_atime < sbuf.st_ctime)) {
+ tp[0] = now; /* set atime to now */
+ /* set mtime to (now - 1) if necessary */
+ tp[1] = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1;
+ }
+ else now = 0; /* no time change needed */
+ /* set the times, note change */
+ if (now && !utime (stream->mailbox,tp)) LOCAL->filetime = tp[1];
+ }
+ flock (fd,LOCK_UN); /* release flock'ers */
+ if (!stream) close (fd); /* close the file if no stream */
+ dotlock_unlock (lock); /* flush the lock file if any */
+}
+
+/* UNIX mail parse and lock mailbox
+ * Accepts: MAIL stream
+ * space to write lock file name
+ * type of locking operation
+ * Returns: T if parse OK, critical & mailbox is locked shared; NIL if failure
+ */
+
+int unix_parse (MAILSTREAM *stream,DOTLOCK *lock,int op)
+{
+ int zn;
+ unsigned long i,j,k,m;
+ unsigned char c,*s,*t,*u,tmp[MAILTMPLEN],date[30];
+ int ti = 0,retain = T;
+ unsigned long nmsgs = stream->nmsgs;
+ unsigned long prevuid = nmsgs ? mail_elt (stream,nmsgs)->private.uid : 0;
+ unsigned long recent = stream->recent;
+ unsigned long oldnmsgs = stream->nmsgs;
+ short silent = stream->silent;
+ short pseudoseen = NIL;
+ struct stat sbuf;
+ STRING bs;
+ FDDATA d;
+ MESSAGECACHE *elt;
+ mail_lock (stream); /* guard against recursion or pingers */
+ /* toss out previous descriptor */
+ if (LOCAL->fd >= 0) close (LOCAL->fd);
+ MM_CRITICAL (stream); /* open and lock mailbox (shared OK) */
+ if ((LOCAL->fd = unix_lock (stream->mailbox,(LOCAL->ld >= 0) ?
+ O_RDWR : O_RDONLY,
+ (long)mail_parameters(NIL,GET_MBXPROTECTION,NIL),
+ lock,op)) < 0) {
+ sprintf (tmp,"Mailbox open failed, aborted: %s",strerror (errno));
+ MM_LOG (tmp,ERROR);
+ unix_abort (stream);
+ mail_unlock (stream);
+ MM_NOCRITICAL (stream); /* done with critical */
+ return NIL;
+ }
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ /* validate change in size */
+ if (sbuf.st_size < LOCAL->filesize) {
+ sprintf (tmp,"Mailbox shrank from %lu to %lu bytes, aborted",
+ (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size);
+ MM_LOG (tmp,ERROR); /* this is pretty bad */
+ unix_unlock (LOCAL->fd,stream,lock);
+ unix_abort (stream);
+ mail_unlock (stream);
+ MM_NOCRITICAL (stream); /* done with critical */
+ return NIL;
+ }
+
+ /* new data? */
+ else if (i = sbuf.st_size - LOCAL->filesize) {
+ d.fd = LOCAL->fd; /* yes, set up file descriptor */
+ d.pos = LOCAL->filesize; /* get to that position in the file */
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE; /* file chunk size */
+ INIT (&bs,fd_string,&d,i); /* initialize stringstruct */
+ /* skip leading whitespace for broken MTAs */
+ while (((c = CHR (&bs)) == '\n') || (c == '\r') ||
+ (c == ' ') || (c == '\t')) SNX (&bs);
+ if (SIZE (&bs)) { /* read new data */
+ /* remember internal header position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ s = unix_mbxline (stream,&bs,&i);
+ t = NIL,zn = 0;
+ if (i) VALID (s,t,ti,zn); /* see if valid From line */
+ if (!ti) { /* someone pulled the rug from under us */
+ sprintf (tmp,"Unexpected changes to mailbox (try restarting): %.20s",
+ (char *) s);
+ MM_LOG (tmp,ERROR);
+ unix_unlock (LOCAL->fd,stream,lock);
+ unix_abort (stream);
+ mail_unlock (stream);
+ /* done with critical */
+ MM_NOCRITICAL (stream);
+ return NIL;
+ }
+ stream->silent = T; /* quell main program new message events */
+ do { /* found a message */
+ /* instantiate first new message */
+ mail_exists (stream,++nmsgs);
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ recent++; /* assume recent by default */
+ elt->recent = T;
+ /* note position/size of internal header */
+ elt->private.special.offset = j;
+ elt->private.msg.header.offset = elt->private.special.text.size = i;
+
+ /* generate plausible IMAPish date string */
+ date[2] = date[6] = date[20] = '-'; date[11] = ' ';
+ date[14] = date[17] = ':';
+ /* dd */
+ date[0] = t[ti - 2]; date[1] = t[ti - 1];
+ /* mmm */
+ date[3] = t[ti - 6]; date[4] = t[ti - 5]; date[5] = t[ti - 4];
+ /* hh */
+ date[12] = t[ti + 1]; date[13] = t[ti + 2];
+ /* mm */
+ date[15] = t[ti + 4]; date[16] = t[ti + 5];
+ if (t[ti += 6] == ':') {/* ss */
+ date[18] = t[++ti]; date[19] = t[++ti];
+ ti++; /* move to space */
+ }
+ else date[18] = date[19] = '0';
+ /* yy -- advance over timezone if necessary */
+ if (zn == ti) ti += (((t[zn+1] == '+') || (t[zn+1] == '-')) ? 6 : 4);
+ date[7] = t[ti + 1]; date[8] = t[ti + 2];
+ date[9] = t[ti + 3]; date[10] = t[ti + 4];
+ /* zzz */
+ t = zn ? (t + zn + 1) : (unsigned char *) "LCL";
+ date[21] = *t++; date[22] = *t++; date[23] = *t++;
+ if ((date[21] != '+') && (date[21] != '-')) date[24] = '\0';
+ else { /* numeric time zone */
+ date[24] = *t++; date[25] = *t++;
+ date[26] = '\0'; date[20] = ' ';
+ }
+ /* set internal date */
+ if (!mail_parse_date (elt,date)) {
+ sprintf (tmp,"Unable to parse internal date: %s",(char *) date);
+ MM_LOG (tmp,WARN);
+ }
+
+ do { /* look for message body */
+ s = t = unix_mbxline (stream,&bs,&i);
+ if (i) switch (*s) { /* check header lines */
+ case 'X': /* possible X-???: line */
+ if (s[1] == '-') { /* must be immediately followed by hyphen */
+ /* X-Status: becomes Status: in S case */
+ if (s[2] == 'S' && s[3] == 't' && s[4] == 'a' && s[5] == 't' &&
+ s[6] == 'u' && s[7] == 's' && s[8] == ':') s += 2;
+ /* possible X-Keywords */
+ else if (s[2] == 'K' && s[3] == 'e' && s[4] == 'y' &&
+ s[5] == 'w' && s[6] == 'o' && s[7] == 'r' &&
+ s[8] == 'd' && s[9] == 's' && s[10] == ':') {
+ SIZEDTEXT uf;
+ retain = NIL; /* don't retain continuation */
+ s += 11; /* flush leading whitespace */
+ while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n'))){
+ while (*s == ' ') s++;
+ /* find end of keyword */
+ if (!(u = strpbrk (s," \n\r"))) u = s + strlen (s);
+ /* got a keyword? */
+ if ((k = (u - s)) && (k <= MAXUSERFLAG)) {
+ uf.data = (unsigned char *) s;
+ uf.size = k;
+ for (j = 0; (j < NUSERFLAGS) && stream->user_flags[j]; ++j)
+ if (!compare_csizedtext (stream->user_flags[j],&uf)) {
+ elt->user_flags |= ((long) 1) << j;
+ break;
+ }
+ }
+ s = u; /* advance to next keyword */
+ }
+ break;
+ }
+
+ /* possible X-IMAP */
+ else if ((s[2] == 'I') && (s[3] == 'M') && (s[4] == 'A') &&
+ (s[5] == 'P') && ((m = (s[6] == ':')) ||
+ ((s[6] == 'b') && (s[7] == 'a') &&
+ (s[8] == 's') && (s[9] == 'e') &&
+ (s[10] == ':')))) {
+ retain = NIL; /* don't retain continuation */
+ if ((nmsgs == 1) && !stream->uid_validity) {
+ /* advance to data */
+ s += m ? 7 : 11;
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ j = 0; /* slurp UID validity */
+ /* found a digit? */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ /* must have valid UID validity and UID last */
+ if (j && isdigit (*s)) {
+ /* pseudo-header seen if X-IMAP */
+ if (m) pseudoseen = LOCAL->pseudo = T;
+ /* save UID validity */
+ stream->uid_validity = j;
+ j = 0; /* slurp UID last */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* save UID last */
+ stream->uid_last = j;
+ /* process keywords */
+ for (j = 0; (*s != '\n') && ((*s != '\r')||(s[1] != '\n'));
+ s = u,j++) {
+ /* flush leading whitespace */
+ while (*s == ' ') s++;
+ u = strpbrk (s," \n\r");
+ /* got a keyword? */
+ if ((j < NUSERFLAGS) && (k = (u - s)) &&
+ (k <= MAXUSERFLAG)) {
+ if (stream->user_flags[j])
+ fs_give ((void **) &stream->user_flags[j]);
+ stream->user_flags[j] = (char *) fs_get (k + 1);
+ strncpy (stream->user_flags[j],s,k);
+ stream->user_flags[j][k] = '\0';
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ /* possible X-UID */
+ else if (s[2] == 'U' && s[3] == 'I' && s[4] == 'D' &&
+ s[5] == ':') {
+ retain = NIL; /* don't retain continuation */
+ /* only believe if have a UID validity */
+ if (stream->uid_validity && ((nmsgs > 1) || !pseudoseen)) {
+ s += 6; /* advance to UID value */
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ j = 0;
+ /* found a digit? */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* flush remainder of line */
+ while (*s != '\n') s++;
+ /* make sure not duplicated */
+ if (elt->private.uid)
+ sprintf (tmp,"Message %lu UID %lu already has UID %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,elt->private.uid);
+ /* make sure UID doesn't go backwards */
+ else if (j <= prevuid)
+ sprintf (tmp,"Message %lu UID %lu less than %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,prevuid + 1);
+#if 0 /* this is currently broken by UIDPLUS */
+ /* or skip by mailbox's recorded last */
+ else if (j > stream->uid_last)
+ sprintf (tmp,"Message %lu UID %lu greater than last %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,stream->uid_last);
+#endif
+ else { /* normal UID case */
+ prevuid = elt->private.uid = j;
+#if 1 /* temporary kludge for UIDPLUS */
+ if (prevuid > stream->uid_last) {
+ stream->uid_last = prevuid;
+ LOCAL->ddirty = LOCAL->dirty = T;
+ }
+#endif
+ break; /* exit this cruft */
+ }
+ MM_LOG (tmp,WARN);
+ /* invalidate UID validity */
+ stream->uid_validity = 0;
+ elt->private.uid = 0;
+ }
+ break;
+ }
+ }
+ /* otherwise fall into S case */
+
+ case 'S': /* possible Status: line */
+ if (s[0] == 'S' && s[1] == 't' && s[2] == 'a' && s[3] == 't' &&
+ s[4] == 'u' && s[5] == 's' && s[6] == ':') {
+ retain = NIL; /* don't retain continuation */
+ s += 6; /* advance to status flags */
+ do switch (*s++) {/* parse flags */
+ case 'R': /* message read */
+ elt->seen = T;
+ break;
+ case 'O': /* message old */
+ if (elt->recent) {
+ elt->recent = NIL;
+ recent--; /* it really wasn't recent */
+ }
+ break;
+ case 'D': /* message deleted */
+ elt->deleted = T;
+ break;
+ case 'F': /* message flagged */
+ elt->flagged = T;
+ break;
+ case 'A': /* message answered */
+ elt->answered = T;
+ break;
+ case 'T': /* message is a draft */
+ elt->draft = T;
+ break;
+ default: /* some other crap */
+ break;
+ } while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n')));
+ break; /* all done */
+ }
+ /* otherwise fall into default case */
+
+ default: /* ordinary header line */
+ if ((*s == 'S') || (*s == 's') ||
+ (((*s == 'X') || (*s == 'x')) && (s[1] == '-'))) {
+ unsigned char *e,*v;
+ /* must match what mail_filter() does */
+ for (u = s,v = tmp,e = u + min (i,MAILTMPLEN - 1);
+ (u < e) && ((c = (*u ? *u : (*u = ' '))) != ':') &&
+ ((c > ' ') || ((c != ' ') && (c != '\t') &&
+ (c != '\r') && (c != '\n')));
+ *v++ = *u++);
+ *v = '\0'; /* tie off */
+ /* matches internal header? */
+ if (!compare_cstring (tmp,"STATUS") ||
+ !compare_cstring (tmp,"X-STATUS") ||
+ !compare_cstring (tmp,"X-KEYWORDS") ||
+ !compare_cstring (tmp,"X-UID") ||
+ !compare_cstring (tmp,"X-IMAP") ||
+ !compare_cstring (tmp,"X-IMAPBASE")) {
+ char err[MAILTMPLEN];
+ sprintf (err,"Discarding bogus %s header in message %lu",
+ (char *) tmp,elt->msgno);
+ MM_LOG (err,WARN);
+ retain = NIL; /* don't retain continuation */
+ break; /* different case or something */
+ }
+ }
+ /* retain or non-continuation? */
+ if (retain || ((*s != ' ') && (*s != '\t'))) {
+ retain = T; /* retaining continuation now */
+ /* line length in LF format newline */
+ for (j = k = 0; j < i; ++j) if (s[j] != '\r') ++k;
+ /* "internal" header size */
+ elt->private.spare.data += k;
+ /* message size */
+ elt->rfc822_size += k + 1;
+ }
+ else {
+ char err[MAILTMPLEN];
+ sprintf (err,"Discarding bogus continuation in msg %lu: %.80s",
+ elt->msgno,(char *) s);
+ if (u = strpbrk (err,"\r\n")) *u = '\0';
+ MM_LOG (err,WARN);
+ break; /* different case or something */
+ }
+ break;
+ }
+ } while (i && (*t != '\n') && ((*t != '\r') || (t[1] != '\n')));
+ /* "internal" header sans trailing newline */
+ if (i) elt->private.spare.data--;
+ /* assign a UID if none found */
+ if (((nmsgs > 1) || !pseudoseen) && !elt->private.uid) {
+ prevuid = elt->private.uid = ++stream->uid_last;
+ elt->private.dirty = T;
+ LOCAL->ddirty = T; /* force update */
+ }
+ else elt->private.dirty = elt->recent;
+
+ /* note size of header, location of text */
+ elt->private.msg.header.text.size =
+ (elt->private.msg.text.offset =
+ (LOCAL->filesize + GETPOS (&bs)) - elt->private.special.offset) -
+ elt->private.special.text.size;
+ k = m = 0; /* no previous line size yet */
+ /* note current position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ if (i) do { /* look for next message */
+ s = unix_mbxline (stream,&bs,&i);
+ if (i) { /* got new data? */
+ VALID (s,t,ti,zn); /* yes, parse line */
+ if (!ti) { /* not a header line, add it to message */
+ elt->rfc822_size += i;
+ for (j = 0; j < i; ++j) switch (s[j]) {
+ case '\r': /* squeeze out CRs */
+ elt->rfc822_size -= 1;
+ break;
+ case '\n': /* LF becomes CRLF */
+ elt->rfc822_size += 1;
+ break;
+ default:
+ break;
+ }
+ if ((i == 1) && (*s == '\n')) {
+ k = 2;
+ m = 1;
+ }
+ else if ((i == 2) && (*s == '\r') && (s[1] == '\n'))
+ k = m = 2;
+ else k = m = 0; /* file does not end with newline! */
+ /* update current position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ }
+ }
+ } while (i && !ti); /* until found a header */
+ elt->private.msg.text.text.size = j -
+ (elt->private.special.offset + elt->private.msg.text.offset);
+ /* flush ending blank line */
+ elt->private.msg.text.text.size -= m;
+ elt->rfc822_size -= k;
+ /* until end of buffer */
+ } while (!stream->sniff && i);
+ if (pseudoseen) { /* flush pseudo-message if present */
+ /* decrement recent count */
+ if (mail_elt (stream,1)->recent) recent--;
+ /* and the exists count */
+ mail_exists (stream,nmsgs--);
+ mail_expunged(stream,1);/* fake an expunge of that message */
+ }
+ /* need to start a new UID validity? */
+ if (!stream->uid_validity) {
+ stream->uid_validity = time (0);
+ /* in case a whiner with no life */
+ if (mail_parameters (NIL,GET_USERHASNOLIFE,NIL))
+ stream->uid_nosticky = T;
+ else if (nmsgs) { /* don't bother if empty file */
+ /* make dirty to restart UID epoch */
+ LOCAL->ddirty = LOCAL->dirty = T;
+ /* need to rewrite msg 1 if not pseudo */
+ if (!LOCAL->pseudo) mail_elt (stream,1)->private.dirty = T;
+ MM_LOG ("Assigning new unique identifiers to all messages",NIL);
+ }
+ }
+ stream->nmsgs = oldnmsgs; /* whack it back down */
+ stream->silent = silent; /* restore old silent setting */
+ /* notify upper level of new mailbox sizes */
+ mail_exists (stream,nmsgs);
+ mail_recent (stream,recent);
+ /* mark dirty so O flags are set */
+ if (recent) LOCAL->dirty = T;
+ }
+ }
+ /* no change, don't babble if never got time */
+ else if (LOCAL->filetime && LOCAL->filetime != sbuf.st_mtime)
+ MM_LOG ("New mailbox modification time but apparently no changes",WARN);
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ LOCAL->filetime = sbuf.st_mtime;
+ return T; /* return the winnage */
+}
+
+/* UNIX read line from mailbox
+ * Accepts: mail stream
+ * stringstruct
+ * pointer to line size
+ * Returns: pointer to input line
+ */
+
+char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size)
+{
+ unsigned long i,j,k,m;
+ char *s,*t,*te;
+ char *ret = "";
+ /* flush old buffer */
+ if (LOCAL->line) fs_give ((void **) &LOCAL->line);
+ /* if buffer needs refreshing */
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ if (SIZE (bs)) { /* find newline */
+ /* end of fast scan */
+ te = (t = (s = bs->curpos) + bs->cursize) - 12;
+ while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) {
+ --s; /* back up */
+ break; /* exit loop */
+ }
+ /* final character-at-a-time scan */
+ while ((s < t) && (*s != '\n')) ++s;
+ /* difficult case if line spans buffer */
+ if ((i = s - bs->curpos) == bs->cursize) {
+ /* have space in line buffer? */
+ if (i > LOCAL->linebuflen) {
+ fs_give ((void **) &LOCAL->linebuf);
+ LOCAL->linebuf = (char *) fs_get (LOCAL->linebuflen = i);
+ }
+ /* remember what we have so far */
+ memcpy (LOCAL->linebuf,bs->curpos,i);
+ /* load next buffer */
+ SETPOS (bs,k = GETPOS (bs) + i);
+ /* end of fast scan */
+ te = (t = (s = bs->curpos) + bs->cursize) - 12;
+ /* fast scan in overlap buffer */
+ while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) {
+ --s; /* back up */
+ break; /* exit loop */
+ }
+
+ /* final character-at-a-time scan */
+ while ((s < t) && (*s != '\n')) ++s;
+ /* huge line? */
+ if ((j = s - bs->curpos) == bs->cursize) {
+ SETPOS (bs,GETPOS (bs) + j);
+ /* look for end of line (s-l-o-w!!) */
+ for (m = SIZE (bs); m && (SNX (bs) != '\n'); --m,++j);
+ SETPOS (bs,k); /* go back to where it started */
+ }
+ /* got size of data, make buffer for return */
+ ret = LOCAL->line = (char *) fs_get (i + j + 2);
+ /* copy first chunk */
+ memcpy (ret,LOCAL->linebuf,i);
+ while (j) { /* copy remainder */
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ memcpy (ret + i,bs->curpos,k = min (j,bs->cursize));
+ i += k; /* account for this much read in */
+ j -= k;
+ bs->curpos += k; /* increment new position */
+ bs->cursize -= k; /* eat that many bytes */
+ }
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ /* read newline at end */
+ if (SIZE (bs)) ret[i++] = SNX (bs);
+ ret[i] = '\0'; /* makes debugging easier */
+ }
+ else { /* this is easy */
+ ret = bs->curpos; /* string it at this position */
+ bs->curpos += ++i; /* increment new position */
+ bs->cursize -= i; /* eat that many bytes */
+ }
+ *size = i; /* return that to user */
+ }
+ else *size = 0; /* end of data, return empty */
+ return ret;
+}
+
+/* UNIX make pseudo-header
+ * Accepts: MAIL stream
+ * buffer to write pseudo-header
+ * Returns: length of pseudo-header
+ */
+
+unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr)
+{
+ int i;
+ char *s,tmp[MAILTMPLEN];
+ time_t now = time (0);
+ rfc822_fixed_date (tmp);
+ sprintf (hdr,"From %s %.24s\nDate: %s\nFrom: %s <%s@%.80s>\nSubject: %s\nMessage-ID: <%lu@%.80s>\nX-IMAP: %010lu %010lu",
+ pseudo_from,ctime (&now),
+ tmp,pseudo_name,pseudo_from,mylocalhost (),pseudo_subject,
+ (unsigned long) now,mylocalhost (),stream->uid_validity,
+ stream->uid_last);
+ for (s = hdr + strlen (hdr),i = 0; i < NUSERFLAGS; ++i)
+ if (stream->user_flags[i])
+ sprintf (s += strlen (s)," %s",stream->user_flags[i]);
+ sprintf (s += strlen (s),"\nStatus: RO\n\n%s\n\n",pseudo_msg);
+ return strlen (hdr); /* return header length */
+}
+
+/* UNIX make status string
+ * Accepts: MAIL stream
+ * destination string to write
+ * message cache entry
+ * UID to write if non-zero (else use elt->private.uid)
+ * non-zero flag to write UID (.LT. 0 to write UID base info too)
+ * Returns: length of string
+ */
+
+unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt,
+ unsigned long uid,long flag)
+{
+ char *t,stack[64];
+ char *s = status;
+ unsigned long n;
+ int pad = 50;
+ int sticky = uid ? T : !stream->uid_nosticky;
+ /* This used to use sprintf(), but thanks to certain cretinous C libraries
+ with horribly slow implementations of sprintf() I had to change it to this
+ mess. At least it should be fast. */
+ if ((flag < 0) && sticky) { /* need to write X-IMAPbase: header? */
+ *s++ = 'X'; *s++ = '-'; *s++ = 'I'; *s++ = 'M'; *s++ = 'A'; *s++ = 'P';
+ *s++ = 'b'; *s++ = 'a'; *s++ = 's'; *s++ = 'e'; *s++ = ':'; *s++ = ' ';
+ t = stack;
+ n = stream->uid_validity; /* push UID validity digits on the stack */
+ do *t++ = (char) (n % 10) + '0';
+ while (n /= 10);
+ /* pop UID validity digits from stack */
+ while (t > stack) *s++ = *--t;
+ *s++ = ' ';
+ n = stream->uid_last; /* push UID last digits on the stack */
+ do *t++ = (char) (n % 10) + '0';
+ while (n /= 10);
+ /* pop UID last digits from stack */
+ while (t > stack) *s++ = *--t;
+ for (n = 0; n < NUSERFLAGS; ++n) if (t = stream->user_flags[n])
+ for (*s++ = ' '; *t; *s++ = *t++);
+ *s++ = '\n';
+ pad += 30; /* increased padding if have IMAPbase */
+ }
+ *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't'; *s++ = 'u'; *s++ = 's';
+ *s++ = ':'; *s++ = ' ';
+ if (elt->seen) *s++ = 'R';
+ /* only write O if have a UID */
+ if (flag && (!elt->recent || !LOCAL->appending)) *s++ = 'O';
+ *s++ = '\n';
+ *s++ = 'X'; *s++ = '-'; *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't';
+ *s++ = 'u'; *s++ = 's'; *s++ = ':'; *s++ = ' ';
+ if (elt->deleted) *s++ = 'D';
+ if (elt->flagged) *s++ = 'F';
+ if (elt->answered) *s++ = 'A';
+ if (elt->draft) *s++ = 'T';
+ *s++ = '\n';
+
+ if (sticky) { /* only do this if UIDs sticky */
+ *s++ = 'X'; *s++ = '-'; *s++ = 'K'; *s++ = 'e'; *s++ = 'y'; *s++ = 'w';
+ *s++ = 'o'; *s++ = 'r'; *s++ = 'd'; *s++ = 's'; *s++ = ':';
+ if (n = elt->user_flags) do {
+ *s++ = ' ';
+ for (t = stream->user_flags[find_rightmost_bit (&n)]; *t; *s++ = *t++);
+ } while (n);
+ n = s - status; /* get size of stuff so far */
+ /* pad X-Keywords to make size constant */
+ if (n < pad) for (n = pad - n; n > 0; --n) *s++ = ' ';
+ *s++ = '\n';
+ if (flag) { /* want to include UID? */
+ t = stack;
+ /* push UID digits on the stack */
+ n = uid ? uid : elt->private.uid;
+ do *t++ = (char) (n % 10) + '0';
+ while (n /= 10);
+ *s++ = 'X'; *s++ = '-'; *s++ = 'U'; *s++ = 'I'; *s++ = 'D'; *s++ = ':';
+ *s++ = ' ';
+ /* pop UID from stack */
+ while (t > stack) *s++ = *--t;
+ *s++ = '\n';
+ }
+ }
+ *s++ = '\n'; *s = '\0'; /* end of extended message status */
+ return s - status; /* return size of resulting string */
+}
+
+/* Rewrite mailbox file
+ * Accepts: MAIL stream, must be critical and locked
+ * return pointer to number of expunged messages if want expunge
+ * lock file name
+ * expunge sequence, not deleted flag
+ * Returns: T if success and mailbox unlocked, NIL if failure
+ */
+
+#define OVERFLOWBUFLEN 8192 /* initial overflow buffer length */
+
+long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,DOTLOCK *lock,
+ long flags)
+{
+ MESSAGECACHE *elt;
+ UNIXFILE f;
+ char *s;
+ time_t tp[2];
+ long ret,flag;
+ unsigned long i,j;
+ unsigned long recent = stream->recent;
+ unsigned long size = LOCAL->pseudo ? unix_pseudo (stream,LOCAL->buf) : 0;
+ if (nexp) *nexp = 0; /* initially nothing expunged */
+ /* calculate size of mailbox after rewrite */
+ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs; i++) {
+ elt = mail_elt (stream,i); /* get cache */
+ if (!(nexp && elt->deleted && (flags ? elt->sequence : T))) {
+ /* add RFC822 size of this message */
+ size += elt->private.special.text.size + elt->private.spare.data +
+ unix_xstatus (stream,LOCAL->buf,elt,NIL,flag) +
+ elt->private.msg.text.text.size + 1;
+ flag = 1; /* only count X-IMAPbase once */
+ }
+ }
+ /* no messages, has a life, and no pseudo */
+ if (!size && !mail_parameters (NIL,GET_USERHASNOLIFE,NIL)) {
+ LOCAL->pseudo = T; /* so make a pseudo-message now */
+ size = unix_pseudo (stream,LOCAL->buf);
+ }
+ /* extend the file as necessary */
+ if (ret = unix_extend (stream,size)) {
+ /* Set up buffered I/O file structure
+ * curpos current position being written through buffering
+ * filepos current position being written physically to the disk
+ * bufpos current position being written in the buffer
+ * protect current maximum position that can be written to the disk
+ * before buffering is forced
+ * The code tries to buffer so that that disk is written in multiples of
+ * OVERBLOWBUFLEN bytes.
+ */
+ f.stream = stream; /* note mail stream */
+ f.curpos = f.filepos = 0; /* start of file */
+ f.protect = stream->nmsgs ? /* initial protection pointer */
+ mail_elt (stream,1)->private.special.offset : 8192;
+ f.bufpos = f.buf = (char *) fs_get (f.buflen = OVERFLOWBUFLEN);
+
+ if (LOCAL->pseudo) /* update pseudo-header */
+ unix_write (&f,LOCAL->buf,unix_pseudo (stream,LOCAL->buf));
+ /* loop through all messages */
+ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs;) {
+ elt = mail_elt (stream,i);/* get cache */
+ /* expunge this message? */
+ if (nexp && elt->deleted && (flags ? elt->sequence : T)) {
+ /* one less recent message */
+ if (elt->recent) --recent;
+ mail_expunged(stream,i);/* notify upper levels */
+ ++*nexp; /* count up one more expunged message */
+ }
+ else { /* preserve this message */
+ i++; /* advance to next message */
+ if ((flag < 0) || /* need to rewrite message? */
+ elt->private.dirty || (f.curpos != elt->private.special.offset) ||
+ (elt->private.msg.header.text.size !=
+ (elt->private.spare.data +
+ unix_xstatus (stream,LOCAL->buf,elt,NIL,flag)))) {
+ unsigned long newoffset = f.curpos;
+ /* yes, seek to internal header */
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size);
+ /* see if need to squeeze out a CR */
+ if (LOCAL->buf[elt->private.special.text.size - 2] == '\r') {
+ LOCAL->buf[--elt->private.special.text.size - 1] = '\n';
+ --size; /* squeezed out a CR from PC */
+ }
+ /* protection pointer moves to RFC822 header */
+ f.protect = elt->private.special.offset +
+ elt->private.msg.header.offset;
+ /* write internal header */
+ unix_write (&f,LOCAL->buf,elt->private.special.text.size);
+ /* get RFC822 header */
+ s = unix_header (stream,elt->msgno,&j,FT_INTERNAL);
+ /* in case this got decremented */
+ elt->private.msg.header.offset = elt->private.special.text.size;
+ /* header size, sans trailing newline */
+ if ((j < 2) || (s[j - 2] == '\n')) j--;
+ /* this can happen if CRs were squeezed */
+ if (j < elt->private.spare.data) {
+ /* so fix up counts */
+ size -= elt->private.spare.data - j;
+ elt->private.spare.data = j;
+ }
+ else if (j != elt->private.spare.data)
+ fatal ("header size inconsistent");
+ /* protection pointer moves to RFC822 text */
+ f.protect = elt->private.special.offset +
+ elt->private.msg.text.offset;
+ unix_write (&f,s,j); /* write RFC822 header */
+ /* write status and UID */
+ unix_write (&f,LOCAL->buf,
+ j = unix_xstatus (stream,LOCAL->buf,elt,NIL,flag));
+ flag = 1; /* only write X-IMAPbase once */
+ /* new file header size */
+ elt->private.msg.header.text.size = elt->private.spare.data + j;
+
+ /* did text move? */
+ if (f.curpos != f.protect) {
+ /* get message text */
+ s = unix_text_work (stream,elt,&j,FT_INTERNAL);
+ /* this can happen if CRs were squeezed */
+ if (j < elt->private.msg.text.text.size) {
+ /* so fix up counts */
+ size -= elt->private.msg.text.text.size - j;
+ elt->private.msg.text.text.size = j;
+ }
+ /* can't happen it says here */
+ else if (j > elt->private.msg.text.text.size)
+ fatal ("text size inconsistent");
+ /* new text offset, status/UID may change it */
+ elt->private.msg.text.offset = f.curpos - newoffset;
+ /* protection pointer moves to next message */
+ f.protect = (i <= stream->nmsgs) ?
+ mail_elt (stream,i)->private.special.offset : (f.curpos + j + 1);
+ unix_write (&f,s,j);/* write text */
+ /* write trailing newline */
+ unix_write (&f,"\n",1);
+ }
+ else { /* tie off header and status */
+ unix_write (&f,NIL,NIL);
+ /* protection pointer moves to next message */
+ f.protect = (i <= stream->nmsgs) ?
+ mail_elt (stream,i)->private.special.offset : size;
+ /* locate end of message text */
+ j = f.filepos + elt->private.msg.text.text.size;
+ /* trailing newline already there? */
+ if (f.protect == (j + 1)) f.curpos = f.filepos = f.protect;
+ else { /* trailing newline missing, write it */
+ f.curpos = f.filepos = j;
+ unix_write (&f,"\n",1);
+ }
+ }
+ /* new internal header offset */
+ elt->private.special.offset = newoffset;
+ elt->private.dirty =NIL;/* message is now clean */
+ }
+ else { /* no need to rewrite this message */
+ /* tie off previous message if needed */
+ unix_write (&f,NIL,NIL);
+ /* protection pointer moves to next message */
+ f.protect = (i <= stream->nmsgs) ?
+ mail_elt (stream,i)->private.special.offset : size;
+ /* locate end of message text */
+ j = f.filepos + elt->private.special.text.size +
+ elt->private.msg.header.text.size +
+ elt->private.msg.text.text.size;
+ /* trailing newline already there? */
+ if (f.protect == (j + 1)) f.curpos = f.filepos = f.protect;
+ else { /* trailing newline missing, write it */
+ f.curpos = f.filepos = j;
+ unix_write (&f,"\n",1);
+ }
+ }
+ }
+ }
+
+ unix_write (&f,NIL,NIL); /* tie off final message */
+ if (size != f.filepos) fatal ("file size inconsistent");
+ fs_give ((void **) &f.buf); /* free buffer */
+ /* make sure tied off */
+ ftruncate (LOCAL->fd,LOCAL->filesize = size);
+ fsync (LOCAL->fd); /* make sure the updates take */
+ if (size && (flag < 0)) fatal ("lost UID base information");
+ /* no longer dirty */
+ LOCAL->ddirty = LOCAL->dirty = NIL;
+ /* notify upper level of new mailbox sizes */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ /* set atime to now, mtime a second earlier */
+ tp[1] = (tp[0] = time (0)) - 1;
+ /* set the times, note change */
+ if (!utime (stream->mailbox,tp)) LOCAL->filetime = tp[1];
+ close (LOCAL->fd); /* close and reopen file */
+ if ((LOCAL->fd = open (stream->mailbox,O_RDWR,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL)))
+ < 0) {
+ sprintf (LOCAL->buf,"Mailbox open failed, aborted: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ unix_abort (stream);
+ }
+ dotlock_unlock (lock); /* flush the lock file */
+ }
+ return ret; /* return state from algorithm */
+}
+
+/* Extend UNIX mailbox file
+ * Accepts: MAIL stream
+ * new desired size
+ * Return: T if success, else NIL
+ */
+
+long unix_extend (MAILSTREAM *stream,unsigned long size)
+{
+ unsigned long i = (size > LOCAL->filesize) ? size - LOCAL->filesize : 0;
+ if (i) { /* does the mailbox need to grow? */
+ if (i > LOCAL->buflen) { /* make sure have enough space */
+ /* this user won the lottery all right */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1);
+ }
+ memset (LOCAL->buf,'\0',i); /* get a block of nulls */
+ while (T) { /* until write successful or punt */
+ lseek (LOCAL->fd,LOCAL->filesize,L_SET);
+ if ((write (LOCAL->fd,LOCAL->buf,i) >= 0) && !fsync (LOCAL->fd)) break;
+ else {
+ long e = errno; /* note error before doing ftruncate */
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ if (MM_DISKERROR (stream,e,NIL)) {
+ fsync (LOCAL->fd); /* user chose to punt */
+ sprintf (LOCAL->buf,"Unable to extend mailbox: %s",strerror (e));
+ if (!stream->silent) MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ }
+ }
+ }
+ return LONGT;
+}
+
+/* Write data to buffered file
+ * Accepts: buffered file pointer
+ * file data or NIL to indicate "flush buffer"
+ * date size (ignored for "flush buffer")
+ * Does not return until success
+ */
+
+void unix_write (UNIXFILE *f,char *buf,unsigned long size)
+{
+ unsigned long i,j,k;
+ if (buf) { /* doing buffered write? */
+ i = f->bufpos - f->buf; /* yes, get size of current buffer data */
+ /* yes, have space in current buffer chunk? */
+ if (j = i ? ((f->buflen - i) % OVERFLOWBUFLEN) : f->buflen) {
+ /* yes, fill up buffer as much as we can */
+ memcpy (f->bufpos,buf,k = min (j,size));
+ f->bufpos += k; /* new buffer position */
+ f->curpos += k; /* new current position */
+ if (j -= k) return; /* all done if still have buffer free space */
+ buf += k; /* full, get new unwritten data pointer */
+ size -= k; /* new data size */
+ i += k; /* new buffer data size */
+ }
+ /* This chunk of the buffer is full. See if can make some space by
+ * writing to the disk, if there's enough unprotected space to do so.
+ * Try to fill out any unaligned chunk, along with any subsequent full
+ * chunks that will fit in unprotected space.
+ */
+ /* any unprotected space we can write to? */
+ if (j = min (i,f->protect - f->filepos)) {
+ /* yes, filepos not at chunk boundary? */
+ if ((k = f->filepos % OVERFLOWBUFLEN) && ((k = OVERFLOWBUFLEN - k) < j))
+ j -= k; /* yes, and can write out partial chunk */
+ else k = 0; /* no partial chunk to write */
+ /* if at least a chunk free, write that too */
+ if (j > OVERFLOWBUFLEN) k += j - (j % OVERFLOWBUFLEN);
+ if (k) { /* write data if there is anything we can */
+ unix_phys_write (f,f->buf,k);
+ /* slide buffer */
+ if (i -= k) memmove (f->buf,f->buf + k,i);
+ f->bufpos = f->buf + i; /* new end of buffer */
+ }
+ }
+
+ /* Have flushed the buffer as best as possible. All done if no more
+ * data to write. Otherwise, if the buffer is empty AND if the unwritten
+ * data is larger than a chunk AND the unprotected space is also larger
+ * than a chunk, then write as many chunks as we can directly from the
+ * data. Buffer the rest, expanding the buffer as needed.
+ */
+ if (size) { /* have more data that we need to buffer? */
+ /* can write any of it to disk instead? */
+ if ((f->bufpos == f->buf) &&
+ ((j = min (f->protect - f->filepos,size)) > OVERFLOWBUFLEN)) {
+ /* write as much as we can right now */
+ unix_phys_write (f,buf,j -= (j % OVERFLOWBUFLEN));
+ buf += j; /* new data pointer */
+ size -= j; /* new data size */
+ f->curpos += j; /* advance current pointer */
+ }
+ if (size) { /* still have data that we need to buffer? */
+ /* yes, need to expand the buffer? */
+ if ((i = ((f->bufpos + size) - f->buf)) > f->buflen) {
+ /* note current position in buffer */
+ j = f->bufpos - f->buf;
+ i += OVERFLOWBUFLEN; /* yes, grow another chunk */
+ fs_resize ((void **) &f->buf,f->buflen = i - (i % OVERFLOWBUFLEN));
+ /* in case buffer relocated */
+ f->bufpos = f->buf + j;
+ }
+ /* buffer remaining data */
+ memcpy (f->bufpos,buf,size);
+ f->bufpos += size; /* new end of buffer */
+ f->curpos += size; /* advance current pointer */
+ }
+ }
+ }
+ else { /* flush buffer to disk */
+ unix_phys_write (f,f->buf,i = f->bufpos - f->buf);
+ f->bufpos = f->buf; /* reset buffer */
+ /* update positions */
+ f->curpos = f->protect = f->filepos;
+ }
+}
+
+/* Physical disk write
+ * Accepts: buffered file pointer
+ * buffer address
+ * buffer size
+ * Does not return until success
+ */
+
+void unix_phys_write (UNIXFILE *f,char *buf,size_t size)
+{
+ MAILSTREAM *stream = f->stream;
+ /* write data at desired position */
+ while (size && ((lseek (LOCAL->fd,f->filepos,L_SET) < 0) ||
+ (write (LOCAL->fd,buf,size) < 0))) {
+ int e;
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Unable to write to mailbox: %s",strerror (e = errno));
+ MM_LOG (tmp,ERROR);
+ MM_DISKERROR (NIL,e,T); /* serious problem, must retry */
+ }
+ f->filepos += size; /* update file position */
+}
+
+/* MBOX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mboxdriver = {
+ "mbox", /* driver name */
+ /* driver flags */
+ DR_LOCAL|DR_MAIL|DR_LOCKING|DR_NONEWMAILRONLY,
+ (DRIVER *) NIL, /* next driver */
+ mbox_valid, /* mailbox is valid for us */
+ unix_parameters, /* manipulate parameters */
+ unix_scan, /* scan mailboxes */
+ unix_list, /* find mailboxes */
+ unix_lsub, /* find subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ mbox_create, /* create mailbox */
+ mbox_delete, /* delete mailbox */
+ mbox_rename, /* rename mailbox */
+ mbox_status, /* status of mailbox */
+ mbox_open, /* open mailbox */
+ unix_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message structure */
+ unix_header, /* fetch message header */
+ unix_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ NIL, /* modify flags */
+ unix_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mbox_ping, /* ping mailbox to see if still alive */
+ mbox_check, /* check for new messages */
+ mbox_expunge, /* expunge deleted messages */
+ unix_copy, /* copy messages to another mailbox */
+ mbox_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mboxproto = {&mboxdriver};
+
+/* MBOX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mbox_valid (char *name)
+{
+ /* only INBOX, mbox must exist */
+ if (!compare_cstring (name,"INBOX") && (unix_valid ("mbox") || !errno) &&
+ (unix_valid (sysinbox()) || !errno || (errno == ENOENT)))
+ return &mboxdriver;
+ return NIL; /* can't win (yet, anyway) */
+}
+
+/* MBOX mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mbox_create (MAILSTREAM *stream,char *mailbox)
+{
+ char tmp[MAILTMPLEN];
+ if (!compare_cstring (mailbox,"INBOX")) return unix_create (NIL,"mbox");
+ sprintf (tmp,"Can't create non-INBOX name as mbox: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+}
+
+
+/* MBOX mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mbox_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return mbox_rename (stream,mailbox,NIL);
+}
+
+
+/* MBOX mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long mbox_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ char tmp[MAILTMPLEN];
+ long ret = unix_rename (stream,"~/mbox",newname);
+ /* recreate file if renamed INBOX */
+ if (ret) unix_create (NIL,"mbox");
+ else MM_LOG (tmp,ERROR); /* log error */
+ return ret; /* return success */
+}
+
+/* MBOX Mail status
+ * Accepts: mail stream
+ * mailbox name
+ * status flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mbox_status (MAILSTREAM *stream,char *mbx,long flags)
+{
+ MAILSTATUS status;
+ unsigned long i;
+ MAILSTREAM *tstream = NIL;
+ MAILSTREAM *systream = NIL;
+ /* make temporary stream (unless this mbx) */
+ if (!stream && !(stream = tstream =
+ mail_open (NIL,mbx,OP_READONLY|OP_SILENT))) return NIL;
+ status.flags = flags; /* return status values */
+ status.messages = stream->nmsgs;
+ status.recent = stream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++)
+ if (!mail_elt (stream,i)->seen) status.unseen++;
+ status.uidnext = stream->uid_last + 1;
+ status.uidvalidity = stream->uid_validity;
+ if (!status.recent && /* calculate post-snarf results */
+ (systream = mail_open (NIL,sysinbox (),OP_READONLY|OP_SILENT))) {
+ status.messages += systream->nmsgs;
+ status.recent += systream->recent;
+ if (flags & SA_UNSEEN) /* must search to get unseen messages */
+ for (i = 1; i <= systream->nmsgs; i++)
+ if (!mail_elt (systream,i)->seen) status.unseen++;
+ /* kludge but probably good enough */
+ status.uidnext += systream->nmsgs;
+ }
+ MM_STATUS(stream,mbx,&status);/* pass status to main program */
+ if (tstream) mail_close (tstream);
+ if (systream) mail_close (systream);
+ return T; /* success */
+}
+
+/* MBOX mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mbox_open (MAILSTREAM *stream)
+{
+ unsigned long i = 1;
+ unsigned long recent = 0;
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &mboxproto;
+ /* change mailbox file name */
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr ("mbox");
+ /* open mailbox, snarf new mail */
+ if (!(unix_open (stream) && mbox_ping (stream))) return NIL;
+ stream->inbox = T; /* mark that this is an INBOX */
+ /* notify upper level of mailbox sizes */
+ mail_exists (stream,stream->nmsgs);
+ while (i <= stream->nmsgs) if (mail_elt (stream,i++)->recent) ++recent;
+ mail_recent (stream,recent); /* including recent messages */
+ return stream;
+}
+
+/* MBOX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+static int snarfed = 0; /* number of snarfs */
+
+long mbox_ping (MAILSTREAM *stream)
+{
+ int sfd;
+ unsigned long size;
+ struct stat sbuf;
+ char *s;
+ DOTLOCK lock,lockx;
+ /* time to try snarf and sysinbox non-empty? */
+ if (LOCAL && !stream->rdonly && !stream->lock &&
+ (time (0) >= (LOCAL->lastsnarf +
+ (long) mail_parameters (NIL,GET_SNARFINTERVAL,NIL))) &&
+ !stat (sysinbox (),&sbuf) && sbuf.st_size) {
+ MM_CRITICAL (stream); /* yes, go critical */
+ /* open and lock sysinbox */
+ if ((sfd = unix_lock (sysinbox (),O_RDWR,
+ (long) mail_parameters (NIL,GET_MBXPROTECTION,NIL),
+ &lockx,LOCK_EX)) >= 0) {
+ /* locked sysinbox in good format? */
+ if (fstat (sfd,&sbuf) || !(size = sbuf.st_size) ||
+ !unix_isvalid_fd (sfd)) {
+ sprintf (LOCAL->buf,"Mail drop %s is not in standard Unix format",
+ sysinbox ());
+ MM_LOG (LOCAL->buf,ERROR);
+ }
+ /* sysinbox good, parse and excl-lock mbox */
+ else if (unix_parse (stream,&lock,LOCK_EX)) {
+ lseek (sfd,0,L_SET); /* read entire sysinbox into memory */
+ read (sfd,s = (char *) fs_get (size + 1),size);
+ s[size] = '\0'; /* tie it off */
+ /* append to end of mbox */
+ lseek (LOCAL->fd,LOCAL->filesize,L_SET);
+
+ /* copy to mbox */
+ if ((write (LOCAL->fd,s,size) < 0) || fsync (LOCAL->fd)) {
+ sprintf (LOCAL->buf,"New mail move failed: %s",strerror (errno));
+ MM_LOG (LOCAL->buf,WARN);
+ /* revert mbox to previous size */
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ }
+ /* sysinbox better not have changed */
+ else if (fstat (sfd,&sbuf) || (size != sbuf.st_size)) {
+ sprintf (LOCAL->buf,"Mail drop %s lock failure, old=%lu now=%lu",
+ sysinbox (),size,(unsigned long) sbuf.st_size);
+ MM_LOG (LOCAL->buf,ERROR);
+ /* revert mbox to previous size */
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ /* Believe it or not, a Singaporean government system actually had
+ * symlinks from /var/mail/user to ~user/mbox. To compound this
+ * error, they used an SVR4 system; BSD and OSF locks would have
+ * prevented it but not SVR4 locks.
+ */
+ if (!fstat (sfd,&sbuf) && (size == sbuf.st_size))
+ syslog (LOG_ALERT,"File %s and %s are the same file!",
+ sysinbox (),stream->mailbox);
+ }
+ else { /* data copied OK */
+ ftruncate (sfd,0); /* truncate sysinbox to zero bytes */
+ if (!snarfed++) { /* have we snarfed before? */
+ /* syslog if server, else user log */
+ sprintf (LOCAL->buf,"Moved %lu bytes of new mail to %s from %s",
+ size,stream->mailbox,sysinbox ());
+ if (strcmp ((char *) mail_parameters (NIL,GET_SERVICENAME,NIL),
+ "unknown"))
+ syslog (LOG_INFO,"%s host= %s",LOCAL->buf,tcp_clienthost ());
+ else MM_LOG (LOCAL->buf,WARN);
+ }
+ }
+ /* done with sysinbox text */
+ fs_give ((void **) &s);
+ /* all done with mbox */
+ unix_unlock (LOCAL->fd,stream,&lock);
+ mail_unlock (stream); /* unlock the stream */
+ MM_NOCRITICAL (stream); /* done with critical */
+ }
+ /* all done with sysinbox */
+ unix_unlock (sfd,NIL,&lockx);
+ }
+ MM_NOCRITICAL (stream); /* done with critical */
+ LOCAL->lastsnarf = time (0);/* note time of last snarf */
+ }
+ return unix_ping (stream); /* do the unix routine now */
+}
+
+/* MBOX mail check mailbox
+ * Accepts: MAIL stream
+ */
+
+void mbox_check (MAILSTREAM *stream)
+{
+ /* do local ping, then do unix routine */
+ if (mbox_ping (stream)) unix_check (stream);
+}
+
+
+/* MBOX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long mbox_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret = unix_expunge (stream,sequence,options);
+ mbox_ping (stream); /* do local ping */
+ return ret;
+}
+
+
+/* MBOX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mbox_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ char tmp[MAILTMPLEN];
+ if (mbox_valid (mailbox)) return unix_append (stream,"mbox",af,data);
+ sprintf (tmp,"Can't append to that name: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+}
diff --git a/imap/src/osdep/unix/unix.h b/imap/src/osdep/unix/unix.h
new file mode 100644
index 00000000..1305184f
--- /dev/null
+++ b/imap/src/osdep/unix/unix.h
@@ -0,0 +1,161 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 20 December 1989
+ * Last Edited: 30 August 2006
+ */
+
+
+/* DEDICATION
+ *
+ * This file is dedicated to my dog, Unix, also known as Yun-chan and
+ * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix
+ * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after
+ * a two-month bout with cirrhosis of the liver.
+ *
+ * He was a dear friend, and I miss him terribly.
+ *
+ * Lift a leg, Yunie. Luv ya forever!!!!
+ */
+
+/* Validate line
+ * Accepts: pointer to candidate string to validate as a From header
+ * return pointer to end of date/time field
+ * return pointer to offset from t of time (hours of ``mmm dd hh:mm'')
+ * return pointer to offset from t of time zone (if non-zero)
+ * Returns: t,ti,zn set if valid From string, else ti is NIL
+ */
+
+#define VALID(s,x,ti,zn) { \
+ ti = 0; \
+ if ((*s == 'F') && (s[1] == 'r') && (s[2] == 'o') && (s[3] == 'm') && \
+ (s[4] == ' ')) { \
+ for (x = s + 5; *x && *x != '\012'; x++); \
+ if (*x) { \
+ if (x[-1] == '\015') --x; \
+ if (x - s >= 41) { \
+ for (zn = -1; x[zn] != ' '; zn--); \
+ if ((x[zn-1] == 'm') && (x[zn-2] == 'o') && (x[zn-3] == 'r') && \
+ (x[zn-4] == 'f') && (x[zn-5] == ' ') && (x[zn-6] == 'e') && \
+ (x[zn-7] == 't') && (x[zn-8] == 'o') && (x[zn-9] == 'm') && \
+ (x[zn-10] == 'e') && (x[zn-11] == 'r') && (x[zn-12] == ' '))\
+ x += zn - 12; \
+ } \
+ if (x - s >= 27) { \
+ if (x[-5] == ' ') { \
+ if (x[-8] == ':') zn = 0,ti = -5; \
+ else if (x[-9] == ' ') ti = zn = -9; \
+ else if ((x[-11] == ' ') && ((x[-10]=='+') || (x[-10]=='-'))) \
+ ti = zn = -11; \
+ } \
+ else if (x[-4] == ' ') { \
+ if (x[-9] == ' ') zn = -4,ti = -9; \
+ } \
+ else if (x[-6] == ' ') { \
+ if ((x[-11] == ' ') && ((x[-5] == '+') || (x[-5] == '-'))) \
+ zn = -6,ti = -11; \
+ } \
+ if (ti && !((x[ti - 3] == ':') && \
+ (x[ti -= ((x[ti - 6] == ':') ? 9 : 6)] == ' ') && \
+ (x[ti - 3] == ' ') && (x[ti - 7] == ' ') && \
+ (x[ti - 11] == ' '))) ti = 0; \
+ } \
+ } \
+ } \
+}
+
+/* You are not expected to understand this macro, but read the next page if
+ * you are not faint of heart.
+ *
+ * Known formats to the VALID macro are:
+ * From user Wed Dec 2 05:53 1992
+ * BSD From user Wed Dec 2 05:53:22 1992
+ * SysV From user Wed Dec 2 05:53 PST 1992
+ * rn From user Wed Dec 2 05:53:22 PST 1992
+ * From user Wed Dec 2 05:53 -0700 1992
+ * emacs From user Wed Dec 2 05:53:22 -0700 1992
+ * From user Wed Dec 2 05:53 1992 PST
+ * From user Wed Dec 2 05:53:22 1992 PST
+ * From user Wed Dec 2 05:53 1992 -0700
+ * Solaris From user Wed Dec 2 05:53:22 1992 -0700
+ *
+ * Plus all of the above with `` remote from xxx'' after it. Thank you very
+ * much, smail and Solaris, for making my life considerably more complicated.
+ */
+
+/*
+ * What? You want to understand the VALID macro anyway? Alright, since you
+ * insist. Actually, it isn't really all that difficult, provided that you
+ * take it step by step.
+ *
+ * Line 1 Initializes the return ti value to failure (0);
+ * Lines 2-3 Validates that the 1st-5th characters are ``From ''.
+ * Lines 4-6 Validates that there is an end of line and points x at it.
+ * Lines 7-14 First checks to see if the line is at least 41 characters long.
+ * If so, it scans backwards to find the rightmost space. From
+ * that point, it scans backwards to see if the string matches
+ * `` remote from''. If so, it sets x to point to the space at
+ * the start of the string.
+ * Line 15 Makes sure that there are at least 27 characters in the line.
+ * Lines 16-21 Checks if the date/time ends with the year (there is a space
+ * five characters back). If there is a colon three characters
+ * further back, there is no timezone field, so zn is set to 0
+ * and ti is set in front of the year. Otherwise, there must
+ * either to be a space four characters back for a three-letter
+ * timezone, or a space six characters back followed by a + or -
+ * for a numeric timezone; in either case, zn and ti become the
+ * offset of the space immediately before it.
+ * Lines 22-24 Are the failure case for line 14. If there is a space four
+ * characters back, it is a three-letter timezone; there must be a
+ * space for the year nine characters back. zn is the zone
+ * offset; ti is the offset of the space.
+ * Lines 25-28 Are the failure case for line 20. If there is a space six
+ * characters back, it is a numeric timezone; there must be a
+ * space eleven characters back and a + or - five characters back.
+ * zn is the zone offset; ti is the offset of the space.
+ * Line 29-32 If ti is valid, make sure that the string before ti is of the
+ * form www mmm dd hh:mm or www mmm dd hh:mm:ss, otherwise
+ * invalidate ti. There must be a colon three characters back
+ * and a space six or nine characters back (depending upon
+ * whether or not the character six characters back is a colon).
+ * There must be a space three characters further back (in front
+ * of the day), one seven characters back (in front of the month),
+ * and one eleven characters back (in front of the day of week).
+ * ti is set to be the offset of the space before the time.
+ *
+ * Why a macro? It gets invoked a *lot* in a tight loop. On some of the
+ * newer pipelined machines it is faster being open-coded than it would be if
+ * subroutines are called.
+ *
+ * Why does it scan backwards from the end of the line, instead of doing the
+ * much easier forward scan? There is no deterministic way to parse the
+ * ``user'' field, because it may contain unquoted spaces! Yes, I tested it to
+ * see if unquoted spaces were possible. They are, and I've encountered enough
+ * evil mail to be totally unwilling to trust that ``it will never happen''.
+ */
+
+/* Build parameters */
+
+#define KODRETRY 15 /* kiss-of-death retry in seconds */
+#define LOCKTIMEOUT 5 /* lock timeout in minutes */
diff --git a/imap/src/osdep/unix/utime.c b/imap/src/osdep/unix/utime.c
new file mode 100644
index 00000000..6fef2d36
--- /dev/null
+++ b/imap/src/osdep/unix/utime.c
@@ -0,0 +1,45 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: BSD utime() emulator
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 October 1996
+ * Last Edited: 30 August 2006
+ */
+
+#undef utime
+
+/* Portable utime() that takes it args like real Unix systems
+ * Accepts: file path
+ * traditional utime() argument
+ * Returns: utime() results
+ */
+
+int portable_utime (char *file,time_t timep[2])
+{
+ struct utimbuf times;
+ /* in case there's other cruft there */
+ memset (&times,0,sizeof (struct utimbuf));
+ times.actime = timep[0]; /* copy the portable values */
+ times.modtime = timep[1];
+ return utime (file,&times); /* now call the SVR4 routine */
+}
diff --git a/imap/src/osdep/unix/write.c b/imap/src/osdep/unix/write.c
new file mode 100644
index 00000000..c7854815
--- /dev/null
+++ b/imap/src/osdep/unix/write.c
@@ -0,0 +1,59 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Write data, treating partial writes as an error
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 May 1995
+ * Last Edited: 30 August 2006
+ */
+
+/* The whole purpose of this unfortunate routine is to deal with DOS and
+ * certain cretinous versions of UNIX which decided that the "bytes actually
+ * written" return value from write() gave them license to use that for things
+ * that are really errors, such as disk quota exceeded, maximum file size
+ * exceeded, disk full, etc.
+ *
+ * BSD won't screw us this way on the local filesystem, but who knows what
+ * some NFS-mounted filesystem will do.
+ */
+
+#undef write
+
+/* Write data to file
+ * Accepts: file descriptor
+ * I/O vector structure
+ * number of vectors in structure
+ * Returns: number of bytes written if successful, -1 if failure
+ */
+
+long maxposint = (long)((((unsigned long) 1) << ((sizeof(int) * 8) - 1)) - 1);
+
+long safe_write (int fd,char *buf,long nbytes)
+{
+ long i,j;
+ if (nbytes > 0) for (i = nbytes; i; i -= j,buf += j) {
+ while (((j = write (fd,buf,(int) min (maxposint,i))) < 0) &&
+ (errno == EINTR));
+ if (j < 0) return j;
+ }
+ return nbytes;
+}
diff --git a/imap/src/osdep/vms/build.com b/imap/src/osdep/vms/build.com
new file mode 100644
index 00000000..aedbcc82
--- /dev/null
+++ b/imap/src/osdep/vms/build.com
@@ -0,0 +1,99 @@
+$! ========================================================================
+$! Copyright 1988-2006 University of Washington
+$!
+$! Licensed under the Apache License, Version 2.0 (the "License");
+$! you may not use this file except in compliance with the License.
+$! You may obtain a copy of the License at
+$!
+$! http://www.apache.org/licenses/LICENSE-2.0
+$!
+$!
+$! ========================================================================
+$!
+$! Program: Portable c-client build for VMS
+$!
+$! Author: Mark Crispin
+$! Networks and Distributed Computing
+$! Computing & Communications
+$! University of Washington
+$! Administration Building, AG-44
+$! Seattle, WA 98195
+$! Internet: MRC@CAC.Washington.EDU
+$!
+$! Date: 2 August 1994
+$! Last Edited: 30 August 2006
+$!
+$! Change this to your local timezone. This value is the number of minutes
+$! east of UTC (formerly known as GMT). Sample values: -300 (US east coast),
+$! -480 (US west coast), 540 (Japan), 60 (western Europe).
+$! VAX C's HELP information says that you should be able to use gmtime(), but
+$! it returns 0 for the struct. ftime(), you ask? It, too, returns 0 for a
+$! timezone. Nothing sucks like a VAX!
+$!
+$ CC_TIMEZONE=-480
+$!
+$! CC options
+$!
+$ CC_PREF = "/OPTIMIZE/INCLUDE=[]"
+$ CC_PREF = CC_PREF + "/DEFINE=net_getbuffer=NET_GETBUF"
+$ CC_PREF = CC_PREF + "/DEFINE=LOCALTIMEZONE='CC_TIMEZONE'"
+$!
+$! Determine TCP type
+$!
+$ TCP_TYPE = "VMSN" ! default to none
+$ IF F$LOCATE("MULTINET", P1) .LT. F$LENGTH(P1)
+$ THEN
+$ DEFINE SYS MULTINET_ROOT:[MULTINET.INCLUDE.SYS],sys$library
+$ DEFINE NETINET MULTINET_ROOT:[MULTINET.INCLUDE.NETINET]
+$ DEFINE ARPA MULTINET_ROOT:[MULTINET.INCLUDE.ARPA]
+$ TCP_TYPE = "VMSM" ! Multinet
+$ LINK_OPT = ",LINK_MNT/OPTION"
+$ ENDIF
+$ IF F$LOCATE("NETLIB", P1) .LT. F$LENGTH(P1)
+$ THEN
+$ DEFINE SYS SYS$LIBRARY: ! normal .H location
+$ DEFINE NETINET SYS$LIBRARY:
+$ DEFINE ARPA SYS$LIBRARY:
+$ LINK_OPT = ",LINK_NLB/OPTION"
+$ TCP_TYPE = "VMSL" ! NETLIB
+$ ENDIF
+$ IF TCP_TYPE .EQS. "VMSN"
+$ THEN
+$ DEFINE SYS SYS$LIBRARY: ! normal .H location
+$ DEFINE NETINET SYS$LIBRARY:
+$ DEFINE ARPA SYS$LIBRARY:
+$ LINK_OPT = ""
+$ ENDIF
+$!
+$ COPY TCP_'TCP_TYPE'.C TCP_VMS.C;
+$!
+$ COPY OS_VMS.H OSDEP.H;
+$ SET VERIFY
+$ CC'CC_PREF' MAIL
+$ CC'CC_PREF' IMAP4R1
+$ CC'CC_PREF' SMTP
+$ CC'CC_PREF' NNTP
+$ CC'CC_PREF' POP3
+$ CC'CC_PREF' DUMMYVMS
+$ CC'CC_PREF' RFC822
+$ CC'CC_PREF' MISC
+$ CC'CC_PREF' OS_VMS
+$ CC'CC_PREF' SMANAGER
+$ CC'CC_PREF' FLSTRING
+$ CC'CC_PREF' NEWSRC
+$ CC'CC_PREF' NETMSG
+$ CC'CC_PREF' UTF8
+$ CC'CC_PREF' UTF8AUX
+$ CC'CC_PREF' MTEST
+$ CC'CC_PREF' MAILUTIL
+$!
+$ LINK MTEST,OS_VMS,MAIL,IMAP4R1,SMTP,NNTP,POP3,DUMMYVMS,RFC822,MISC,UTF8,-
+ UTF8AUX,SMANAGER,FLSTRING,NEWSRC,NETMSG,-
+ SYS$INPUT:/OPTION'LINK_OPT',LINK/OPTION
+PSECT=_CTYPE_,NOWRT
+$ LINK MAILUTIL,OS_VMS,MAIL,IMAP4R1,SMTP,NNTP,POP3,DUMMYVMS,RFC822,MISC,UTF8,-
+ UTF8AUX,SMANAGER,FLSTRING,NEWSRC,NETMSG,-
+ SYS$INPUT:/OPTION'LINK_OPT',LINK/OPTION
+PSECT=_CTYPE_,NOWRT
+$ SET NOVERIFY
+$ EXIT
diff --git a/imap/src/osdep/vms/clean.com b/imap/src/osdep/vms/clean.com
new file mode 100644
index 00000000..98df0db5
--- /dev/null
+++ b/imap/src/osdep/vms/clean.com
@@ -0,0 +1,26 @@
+$! ========================================================================
+$! Copyright 1988-2006 University of Washington
+$!
+$! Licensed under the Apache License, Version 2.0 (the "License");
+$! you may not use this file except in compliance with the License.
+$! You may obtain a copy of the License at
+$!
+$! http://www.apache.org/licenses/LICENSE-2.0
+$!
+$!
+$! ========================================================================
+$!
+$! Program: Portable c-client cleanup for VMS
+$!
+$! Author: Mark Crispin
+$! Networks and Distributed Computing
+$! Computing & Communications
+$! University of Washington
+$! Administration Building, AG-44
+$! Seattle, WA 98195
+$! Internet: MRC@CAC.Washington.EDU
+$!
+$! Date: 14 June 1995
+$! Last Edited: 30 August 2006
+$!
+$ DELETE *.OBJ;*,OSDEP.*;*,TCP_VMS.C;*,MTEST.EXE;*,MAILUTIL.EXE;*
diff --git a/imap/src/osdep/vms/dummy.h b/imap/src/osdep/vms/dummy.h
new file mode 100644
index 00000000..32650e06
--- /dev/null
+++ b/imap/src/osdep/vms/dummy.h
@@ -0,0 +1,43 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 9 May 1991
+ * Last Edited: 30 August 2006
+ */
+
+/* Exported function prototypes */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat);
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long scan_contents (DRIVER *dtb,char *name,char *contents,
+ unsigned long csiz,unsigned long fsiz);
+long dummy_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz);
+long dummy_create (MAILSTREAM *stream,char *mailbox);
+long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode);
+long dummy_delete (MAILSTREAM *stream,char *mailbox);
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname);
+char *dummy_file (char *dst,char *name);
+long dummy_canonicalize (char *tmp,char *ref,char *pat);
diff --git a/imap/src/osdep/vms/dummyvms.c b/imap/src/osdep/vms/dummyvms.c
new file mode 100644
index 00000000..cfcb45d0
--- /dev/null
+++ b/imap/src/osdep/vms/dummyvms.c
@@ -0,0 +1,295 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines for VMS
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 24 May 1993
+ * Last Edited: 30 August 2006
+ */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include "mail.h"
+#include "osdep.h"
+#include "dummy.h"
+#include "misc.h"
+
+/* Function prototypes */
+
+DRIVER *dummy_valid (char *name);
+void *dummy_parameters (long function,void *value);
+MAILSTREAM *dummy_open (MAILSTREAM *stream);
+void dummy_close (MAILSTREAM *stream,long options);
+long dummy_ping (MAILSTREAM *stream);
+void dummy_check (MAILSTREAM *stream);
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options);
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+/* Dummy routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER dummydriver = {
+ "dummy", /* driver name */
+ DR_LOCAL|DR_MAIL, /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ dummy_valid, /* mailbox is valid for us */
+ dummy_parameters, /* manipulate parameters */
+ dummy_scan, /* scan mailboxes */
+ dummy_list, /* list mailboxes */
+ dummy_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ dummy_create, /* create mailbox */
+ dummy_delete, /* delete mailbox */
+ dummy_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ dummy_open, /* open mailbox */
+ dummy_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message structure */
+ NIL, /* fetch header */
+ NIL, /* fetch text */
+ NIL, /* fetch message data */
+ NIL, /* unique identifier */
+ NIL, /* message number from UID */
+ NIL, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ dummy_ping, /* ping mailbox to see if still alive */
+ dummy_check, /* check for new messages */
+ dummy_expunge, /* expunge deleted messages */
+ dummy_copy, /* copy messages to another mailbox */
+ dummy_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+
+ /* prototype stream */
+MAILSTREAM dummyproto = {&dummydriver};
+
+/* Dummy validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *dummy_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ /* must be valid local mailbox */
+ return (name && *name && (*name != '{') && !compare_cstring (name,"INBOX")) ?
+ &dummydriver : NIL;
+}
+
+
+/* Dummy manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *dummy_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+/* Dummy scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ /* return silently */
+}
+
+
+/* Dummy list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ /* return silently */
+}
+
+
+/* Dummy list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ /* return silently */
+}
+
+/* Dummy create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * driver type to use
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_create (MAILSTREAM *stream,char *mailbox)
+{
+ return NIL; /* always fails */
+}
+
+
+/* Dummy delete mailbox
+ * Accepts: mail stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return NIL; /* always fails */
+}
+
+
+/* Mail rename mailbox
+ * Accepts: mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ return NIL; /* always fails */
+}
+
+/* Dummy open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *dummy_open (MAILSTREAM *stream)
+{
+ char tmp[MAILTMPLEN];
+ /* OP_PROTOTYPE call or silence */
+ if (!stream || stream->silent) return NIL;
+ if (compare_cstring (stream->mailbox,"INBOX")) {
+ sprintf (tmp,"Not a mailbox: %s",stream->mailbox);
+ mm_log (tmp,ERROR);
+ return NIL; /* always fails */
+ }
+ if (!stream->silent) { /* only if silence not requested */
+ mail_exists (stream,0); /* say there are 0 messages */
+ mail_recent (stream,0);
+ stream->uid_validity = time (0);
+ }
+ stream->inbox = T; /* note that it's an INBOX */
+ return stream; /* return success */
+}
+
+
+/* Dummy close
+ * Accepts: MAIL stream
+ * options
+ */
+
+void dummy_close (MAILSTREAM *stream,long options)
+{
+ /* return silently */
+}
+
+/* Dummy ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+long dummy_ping (MAILSTREAM *stream)
+{
+ return T;
+}
+
+
+/* Dummy check mailbox
+ * Accepts: MAIL stream
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+void dummy_check (MAILSTREAM *stream)
+{
+ dummy_ping (stream); /* invoke ping */
+}
+
+
+/* Dummy expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ return LONGT;
+}
+
+/* Dummy copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * options
+ * Returns: T if copy successful, else NIL
+ */
+
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) fatal ("Impossible dummy_copy");
+ return NIL;
+}
+
+
+/* Dummy append message string
+ * Accepts: mail stream
+ * destination mailbox
+ * append callback function
+ * data for callback
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Can't append to %s",mailbox);
+ mm_log (tmp,ERROR); /* pass up error */
+ return NIL; /* always fails */
+}
diff --git a/imap/src/osdep/vms/env_vms.c b/imap/src/osdep/vms/env_vms.c
new file mode 100644
index 00000000..56b7a223
--- /dev/null
+++ b/imap/src/osdep/vms/env_vms.c
@@ -0,0 +1,174 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: VMS environment routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 2 August 1994
+ * Last Edited: 30 August 2006
+ */
+
+
+static char *myUserName = NIL; /* user name */
+static char *myLocalHost = NIL; /* local host name */
+static char *myHomeDir = NIL; /* home directory name */
+static char *myNewsrc = NIL; /* newsrc file name */
+
+#include "pmatch.c" /* include wildcard pattern matcher */
+
+/* Environment manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *env_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_USERNAME:
+ myUserName = cpystr ((char *) value);
+ case GET_USERNAME:
+ ret = (void *) myUserName;
+ break;
+ case SET_HOMEDIR:
+ myHomeDir = cpystr ((char *) value);
+ case GET_HOMEDIR:
+ ret = (void *) myHomeDir;
+ break;
+ case SET_LOCALHOST:
+ myLocalHost = cpystr ((char *) value);
+ case GET_LOCALHOST:
+ ret = (void *) myLocalHost;
+ break;
+ case SET_NEWSRC:
+ if (myNewsrc) fs_give ((void **) &myNewsrc);
+ myNewsrc = cpystr ((char *) value);
+ case GET_NEWSRC:
+ if (!myNewsrc) { /* set news file name if not defined */
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"%s:.newsrc",myhomedir ());
+ myNewsrc = cpystr (tmp);
+ }
+ ret = (void *) myNewsrc;
+ break;
+ }
+ return ret;
+}
+
+/* Write current time
+ * Accepts: destination string
+ * optional format of day-of-week prefix
+ * format of date and time
+ */
+
+static void do_date (char *date,char *prefix,char *fmt)
+{
+ time_t tn = time (0);
+ struct tm *t = localtime (&tn);
+ int zone = LOCALTIMEZONE + (t->tm_isdst ? 60 : 0);
+ if (prefix) { /* want day of week? */
+ sprintf (date,prefix,days[t->tm_wday]);
+ date += strlen (date); /* make next sprintf append */
+ }
+ /* output the date */
+ sprintf (date,fmt,t->tm_mday,months[t->tm_mon],t->tm_year+1900,
+ t->tm_hour,t->tm_min,t->tm_sec,zone/60,abs (zone) % 60);
+}
+
+
+/* Write current time in RFC 822 format
+ * Accepts: destination string
+ */
+
+void rfc822_date (char *date)
+{
+ do_date (date,"%s, ","%d %s %d %02d:%02d:%02d %+03d%02d");
+}
+
+
+/* Write current time in internal format
+ * Accepts: destination string
+ */
+
+void internal_date (char *date)
+{
+ do_date (date,NIL,"%02d-%s-%d %02d:%02d:%02d %+03d%02d");
+}
+
+/* Return my user name
+ * Returns: my user name
+ */
+
+char *myusername ()
+{
+ struct stat sbuf;
+ char tmp[MAILTMPLEN];
+
+ if (!myUserName) { /* get user name if don't have it yet */
+ myUserName = cpystr (cuserid (NIL));
+ myHomeDir = cpystr ("SYS$LOGIN");
+ }
+ return myUserName;
+}
+
+
+/* Return my home directory name
+ * Returns: my home directory name
+ */
+
+char *myhomedir ()
+{
+ if (!myHomeDir) myusername ();/* initialize if first time */
+ return myHomeDir;
+}
+
+
+/* Determine default prototype stream to user
+ * Accepts: type (NIL for create, T for append)
+ * Returns: default prototype stream
+ */
+
+MAILSTREAM *default_proto (long type)
+{
+ return NIL; /* no default prototype */
+}
+
+/* Emulator for BSD syslog() routine
+ * Accepts: priority
+ * message
+ * parameters
+ */
+
+void syslog (int priority,const char *message,...)
+{
+}
+
+
+/* Emulator for BSD openlog() routine
+ * Accepts: identity
+ * options
+ * facility
+ */
+
+void openlog (const char *ident,int logopt,int facility)
+{
+}
diff --git a/imap/src/osdep/vms/env_vms.h b/imap/src/osdep/vms/env_vms.h
new file mode 100644
index 00000000..59160a06
--- /dev/null
+++ b/imap/src/osdep/vms/env_vms.h
@@ -0,0 +1,60 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: VMS environment routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 2 August 1994
+ * Last Edited: 30 August 2006
+ */
+
+
+#define SUBSCRIPTIONFILE(t) sprintf (t,"%s\\SUBSCRIPTIONS.TXT",myhomedir ())
+#define SUBSCRIPTIONTEMP(t) sprintf (t,"%s\\SUBSCRIPTIONS.TMP",myhomedir ())
+
+/* Function prototypes */
+
+#include "env.h"
+
+char *myusername ();
+
+
+/* syslog() emulation */
+
+#define LOG_MAIL (2<<3) /* mail system */
+#define LOG_DAEMON (3<<3) /* system daemons */
+#define LOG_AUTH (4<<3) /* security/authorization messages */
+#define LOG_EMERG 0 /* system is unusable */
+#define LOG_ALERT 1 /* action must be taken immediately */
+#define LOG_CRIT 2 /* critical conditions */
+#define LOG_ERR 3 /* error conditions */
+#define LOG_WARNING 4 /* warning conditions */
+#define LOG_NOTICE 5 /* normal but signification condition */
+#define LOG_INFO 6 /* informational */
+#define LOG_DEBUG 7 /* debug-level messages */
+#define LOG_PID 0x01 /* log the pid with each message */
+#define LOG_CONS 0x02 /* log on the console if errors in sending */
+#define LOG_ODELAY 0x04 /* delay open until syslog() is called */
+#define LOG_NDELAY 0x08 /* don't delay open */
+#define LOG_NOWAIT 0x10 /* if forking to log on console, don't wait() */
+
+void openlog (const char *ident,int logopt,int facility);
+void syslog (int priority,const char *message,...);
diff --git a/imap/src/osdep/vms/fs_vms.c b/imap/src/osdep/vms/fs_vms.c
new file mode 100644
index 00000000..03908328
--- /dev/null
+++ b/imap/src/osdep/vms/fs_vms.c
@@ -0,0 +1,62 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Free storage management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Get a block of free storage
+ * Accepts: size of desired block
+ * Returns: free storage block
+ */
+
+void *fs_get (size_t size)
+{
+ void *block = malloc (size ? size : (size_t) 1);
+ if (!block) fatal ("Out of memory");
+ return (block);
+}
+
+
+/* Resize a block of free storage
+ * Accepts: ** pointer to current block
+ * new size
+ */
+
+void fs_resize (void **block,size_t size)
+{
+ if (!(*block = realloc (*block,size ? size : (size_t) 1)))
+ fatal ("Can't resize memory");
+}
+
+
+/* Return a block of free storage
+ * Accepts: ** pointer to free storage block
+ */
+
+void fs_give (void **block)
+{
+ free (*block);
+ *block = NIL;
+}
diff --git a/imap/src/osdep/vms/ftl_vms.c b/imap/src/osdep/vms/ftl_vms.c
new file mode 100644
index 00000000..9e65ef55
--- /dev/null
+++ b/imap/src/osdep/vms/ftl_vms.c
@@ -0,0 +1,38 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: DOS/VMS/TOPS-20 crash management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Report a fatal error
+ * Accepts: string to output
+ */
+
+void fatal (char *string)
+{
+ mm_fatal (string); /* pass up the string */
+ abort (); /* die horribly */
+}
diff --git a/imap/src/osdep/vms/link.opt b/imap/src/osdep/vms/link.opt
new file mode 100644
index 00000000..0f454490
--- /dev/null
+++ b/imap/src/osdep/vms/link.opt
@@ -0,0 +1 @@
+SYS$SHARE:VAXCRTL/SHARE
diff --git a/imap/src/osdep/vms/link_mnt.opt b/imap/src/osdep/vms/link_mnt.opt
new file mode 100644
index 00000000..5eb99104
--- /dev/null
+++ b/imap/src/osdep/vms/link_mnt.opt
@@ -0,0 +1 @@
+MULTINET:MULTINET_SOCKET_LIBRARY/SHARE
diff --git a/imap/src/osdep/vms/link_nlb.opt b/imap/src/osdep/vms/link_nlb.opt
new file mode 100644
index 00000000..883d2859
--- /dev/null
+++ b/imap/src/osdep/vms/link_nlb.opt
@@ -0,0 +1 @@
+NETLIB_SHR/SHARE
diff --git a/imap/src/osdep/vms/linkage.c b/imap/src/osdep/vms/linkage.c
new file mode 100644
index 00000000..14490a0c
--- /dev/null
+++ b/imap/src/osdep/vms/linkage.c
@@ -0,0 +1,37 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Default driver linkage
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 13 June 1995
+ * Last Edited: 23 May 2007
+ */
+
+ mail_link (&imapdriver); /* link in the imap driver */
+ mail_link (&nntpdriver); /* link in the nntp driver */
+ mail_link (&pop3driver); /* link in the pop3 driver */
+ mail_link (&dummydriver); /* link in the dummy driver */
+ auth_link (&auth_ext); /* link in the ext authenticator */
+ auth_link (&auth_md5); /* link in the md5 authenticator */
+ auth_link (&auth_pla); /* link in the plain authenticator */
+ auth_link (&auth_log); /* link in the log authenticator */
+ mail_versioncheck (CCLIENTVERSION); /* validate version */
diff --git a/imap/src/osdep/vms/linkage.h b/imap/src/osdep/vms/linkage.h
new file mode 100644
index 00000000..7e0eccfa
--- /dev/null
+++ b/imap/src/osdep/vms/linkage.h
@@ -0,0 +1,36 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Default driver linkage
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 13 June 1995
+ * Last Edited: 30 August 2006
+ */
+
+extern DRIVER imapdriver;
+extern DRIVER nntpdriver;
+extern DRIVER pop3driver;
+extern DRIVER dummydriver;
+extern AUTHENTICATOR auth_ext;
+extern AUTHENTICATOR auth_log;
+extern AUTHENTICATOR auth_md5;
+extern AUTHENTICATOR auth_pla;
diff --git a/imap/src/osdep/vms/nl_vms.c b/imap/src/osdep/vms/nl_vms.c
new file mode 100644
index 00000000..b2d5616a
--- /dev/null
+++ b/imap/src/osdep/vms/nl_vms.c
@@ -0,0 +1,92 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: UNIX/VMS newline routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Copy string with CRLF newlines
+ * Accepts: destination string
+ * pointer to size of destination string buffer
+ * source string
+ * length of source string
+ * Returns: length of copied string
+ */
+
+unsigned long strcrlfcpy (unsigned char **dst,unsigned long *dstl,
+ unsigned char *src,unsigned long srcl)
+{
+ long i = srcl * 2,j;
+ unsigned char c,*d = src;
+ if (*dst) { /* candidate destination provided? */
+ /* count NLs if doesn't fit worst-case */
+ if (i > *dstl) for (i = j = srcl; j; --j) if (*d++ == '\012') i++;
+ /* still too small, must reset destination */
+ if (i > *dstl) fs_give ((void **) dst);
+ }
+ /* make a new buffer if needed */
+ if (!*dst) *dst = (char *) fs_get ((*dstl = i) + 1);
+ d = *dst; /* destination string */
+ if (srcl) do { /* main copy loop */
+ if ((c = *src++) < '\016') {
+ /* prepend CR to LF */
+ if (c == '\012') *d++ = '\015';
+ /* unlikely CR */
+ else if ((c == '\015') && (srcl > 1) && (*src == '\012')) {
+ *d++ = c; /* copy the CR */
+ c = *src++; /* grab the LF */
+ --srcl; /* adjust the count */
+ }
+ }
+ *d++ = c; /* copy character */
+ } while (--srcl);
+ *d = '\0'; /* tie off destination */
+ return d - *dst; /* return length */
+}
+
+/* Length of string after strcrlfcpy applied
+ * Accepts: source string
+ * Returns: length of string
+ */
+
+unsigned long strcrlflen (STRING *s)
+{
+ unsigned long pos = GETPOS (s);
+ unsigned long i = SIZE (s);
+ unsigned long j = i;
+ while (j--) switch (SNX (s)) {/* search for newlines */
+ case '\015': /* unlikely carriage return */
+ if (j && (CHR (s) == '\012')) {
+ SNX (s); /* eat the line feed */
+ j--;
+ }
+ break;
+ case '\012': /* line feed? */
+ i++;
+ default: /* ordinary chararacter */
+ break;
+ }
+ SETPOS (s,pos); /* restore old position */
+ return i;
+}
diff --git a/imap/src/osdep/vms/os_vms.c b/imap/src/osdep/vms/os_vms.c
new file mode 100644
index 00000000..cd333ba9
--- /dev/null
+++ b/imap/src/osdep/vms/os_vms.c
@@ -0,0 +1,76 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- VMS version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 2 August 1994
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_vms.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "misc.h"
+
+
+#include "fs_vms.c"
+#include "ftl_vms.c"
+#include "nl_vms.c"
+#include "env_vms.c"
+#include "tcp_vms.c"
+
+#define server_login(user,pass,authuser,argc,argv) NIL
+#define authserver_login(user,authuser,argc,argv) NIL
+#define myusername() "" /* dummy definition to prevent build errors */
+#define MD5ENABLE ""
+
+#include "auth_md5.c"
+#include "auth_pla.c"
+#include "auth_log.c"
+
+
+/* Emulator for UNIX getpass() call
+ * Accepts: prompt
+ * Returns: password
+ */
+
+#define PWDLEN 128 /* used by Linux */
+
+char *getpass (const char *prompt)
+{
+ char *s;
+ static char pwd[PWDLEN];
+ fputs (prompt,stdout);
+ fgets (pwd,PWDLEN-1,stdin);
+ if (s = strchr (pwd,'\n')) *s = '\0';
+ return pwd;
+}
diff --git a/imap/src/osdep/vms/os_vms.h b/imap/src/osdep/vms/os_vms.h
new file mode 100644
index 00000000..1da72f13
--- /dev/null
+++ b/imap/src/osdep/vms/os_vms.h
@@ -0,0 +1,52 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- VMS version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 2 August 1994
+ * Last Edited: 30 January 2007
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unixio.h>
+#include <file.h>
+#include <stat.h>
+
+#define L_SET SEEK_SET
+#define L_INCR SEEK_CUR
+#define L_XTND SEEK_END
+
+#include "env_vms.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+
+#define gethostid clock
+#define random rand
+#define unlink delete
+
+char *getpass (const char *prompt);
+
+#define strtok_r(a,b,c) strtok(a,b)
diff --git a/imap/src/osdep/vms/pmatch.c b/imap/src/osdep/vms/pmatch.c
new file mode 100644
index 00000000..95a0bb86
--- /dev/null
+++ b/imap/src/osdep/vms/pmatch.c
@@ -0,0 +1,89 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: IMAP Wildcard Matching Routines (case-independent)
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 June 2000
+ * Last Edited: 30 August 2006
+ */
+
+/* Wildcard pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if pattern matches base, else NIL
+ */
+
+long pmatch_full (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ /* % at end, OK if no inferiors */
+ if (!pat[1]) return (delim && strchr (s,delim)) ? NIL : T;
+ /* scan remainder of string until delimiter */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while ((*s != delim) && *s++);
+ break;
+ case '*': /* match 0 or more characters */
+ if (!pat[1]) return T; /* * at end, unconditional match */
+ /* scan remainder of string */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while (*s++);
+ break;
+ case '\0': /* end of pattern */
+ return *s ? NIL : T; /* success if also end of base */
+ default: /* match this character */
+ return compare_uchar (*pat,*s) ? NIL : pmatch_full (s+1,pat+1,delim);
+ }
+ return NIL;
+}
+
+/* Directory pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if base is a matching directory of pattern, else NIL
+ */
+
+long dmatch (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ if (!*s) return T; /* end of base means have a subset match */
+ if (!*++pat) return NIL; /* % at end, no inferiors permitted */
+ /* scan remainder of string until delimiter */
+ do if (dmatch (s,pat,delim)) return T;
+ while ((*s != delim) && *s++);
+ if (*s && !s[1]) return T; /* ends with delimiter, must be subset */
+ return dmatch (s,pat,delim);/* do new scan */
+ case '*': /* match 0 or more characters */
+ return T; /* unconditional match */
+ case '\0': /* end of pattern */
+ break;
+ default: /* match this character */
+ if (*s) return compare_uchar (*pat,*s) ? NIL : dmatch (s+1,pat+1,delim);
+ /* end of base, return if at delimiter */
+ else if (*pat == delim) return T;
+ break;
+ }
+ return NIL;
+}
diff --git a/imap/src/osdep/vms/tcp_vms.h b/imap/src/osdep/vms/tcp_vms.h
new file mode 100644
index 00000000..ff747016
--- /dev/null
+++ b/imap/src/osdep/vms/tcp_vms.h
@@ -0,0 +1,52 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: VMS TCP/IP routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* TCP input buffer */
+
+#define BUFLEN 8192
+
+
+/* TCP I/O stream */
+
+#define TCPSTREAM struct tcp_stream
+TCPSTREAM {
+ char *host; /* host name */
+ unsigned long port; /* port number */
+ char *localhost; /* local host name */
+ int tcpsi; /* input socket */
+ int tcpso; /* output socket */
+ int ictr; /* input counter */
+ char *iptr; /* input pointer */
+ char ibuf[BUFLEN]; /* input buffer */
+};
+
+
+/* Local function prototypes */
+
+long tcp_abort (TCPSTREAM *stream);
diff --git a/imap/src/osdep/vms/tcp_vmsl.c b/imap/src/osdep/vms/tcp_vmsl.c
new file mode 100644
index 00000000..692c9212
--- /dev/null
+++ b/imap/src/osdep/vms/tcp_vmsl.c
@@ -0,0 +1,378 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: VMS TCP/IP routines for Netlib.
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 2 August 1994
+ * Last Edited: 13 January 2008
+ */
+
+/* Thanks to Yehavi Bourvine at The Hebrew University of Jerusalem who
+ contributed the original VMS code */
+
+#include <descrip.h>
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd);
+
+/* TCP/IP manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tcp_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+
+/* TCP/IP open
+ * Accepts: host name
+ * contact service name
+ * contact port number
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream = NIL;
+ unsigned long sock;
+ int status;
+ char tmp[MAILTMPLEN];
+ /* hostname to connect to */
+ struct dsc$descriptor HostDesc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL };
+ port &= 0xffff; /* erase flags */
+ /* assign a local socket */
+ if (!((status = net_assign (&sock)) & 0x1)) {
+ sprintf (tmp,"Unable to assign to net, status=%d",status);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ if (!((status = net_bind (&sock,1)) & 0x1)) {
+ sprintf (tmp,"Unable to create local socket, status=%d",status);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* open connection */
+ HostDesc.dsc$w_length = strlen (host);
+ HostDesc.dsc$a_pointer = host;
+ if (!((status = tcp_connect (&sock,&HostDesc,port)) & 0x1)) {
+ sprintf (tmp,"Can't connect to %.80s,%lu: %s",host,port,strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* create TCP/IP stream */
+ stream = (TCPSTREAM *) fs_get (sizeof (TCPSTREAM));
+ stream->host = cpystr (host); /* copy official host name */
+ /* copy local host name */
+ stream->localhost = cpystr (mylocalhost ());
+ stream->port = port; /* copy port number */
+ /* init sockets */
+ stream->tcpsi = stream->tcpso = sock;
+ stream->ictr = 0; /* init input counter */
+ return stream; /* return success */
+}
+
+/* TCP/IP authenticated open
+ * Accepts: NETMBX specifier
+ * service name
+ * returned user name buffer
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL;
+}
+
+/* TCP receive line
+ * Accepts: TCP stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *tcp_getline (TCPSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = tcp_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = tcp_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* TCP receive line or partial line
+ * Accepts: TCP stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!tcp_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!tcp_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* TCP/IP receive buffer
+ * Accepts: TCP/IP stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer)
+{
+ unsigned long n;
+ char *bufptr = buffer;
+ while (size > 0) { /* until request satisfied */
+ if (!tcp_getdata (stream)) return NIL;
+ n = min (size,stream->ictr);/* number of bytes to transfer */
+ /* do the copy */
+ memcpy (bufptr,stream->iptr,n);
+ bufptr += n; /* update pointer */
+ stream->iptr +=n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -=n;
+ }
+ bufptr[0] = '\0'; /* tie off string */
+ return T;
+}
+
+
+/* TCP/IP receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getdata (TCPSTREAM *stream)
+{
+ char tmp[MAILTMPLEN];
+ int i,status;
+ /* Note: the doc says we need here dynamic descriptor, but we need static
+ * one... */
+ struct dsc$descriptor BufDesc = {BUFLEN,DSC$K_DTYPE_T,DSC$K_CLASS_S,
+ stream->ibuf};
+ static short iosb[4];
+ if (stream->tcpsi < 0) return NIL;
+ while (stream->ictr < 1) { /* if nothing in the buffer */
+ if (!((status = tcp_receive(&(stream->tcpsi), &BufDesc, iosb)) & 0x1)) {
+ sprintf (tmp,"Error reading from TcpIp/NETLIB, status=%d",status);
+ mm_log (tmp,ERROR);
+ return tcp_abort (stream);
+ }
+ if (iosb[1] > BUFLEN) i = BUFLEN;
+ else i = iosb[1];
+ if (i < 1) return tcp_abort (stream);
+ stream->ictr = i; /* set new byte count */
+ stream->iptr = stream->ibuf;/* point at TCP buffer */
+ }
+ return T;
+}
+
+/* TCP/IP send string as record
+ * Accepts: TCP/IP stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long tcp_soutr (TCPSTREAM *stream,char *string)
+{
+ return tcp_sout (stream,string,(unsigned long) strlen (string));
+}
+
+
+/* TCP/IP send string
+ * Accepts: TCP/IP stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
+{
+ int status;
+ struct dsc$descriptor_s BufDesc = {strlen(string),DSC$K_DTYPE_T,
+ DSC$K_CLASS_S,string };
+ /* 2 = Do not add \r\n */
+ return ((status = tcp_send (&(stream->tcpso),&BufDesc,2)) & 0x1) ? T :
+ tcp_abort (stream);
+}
+
+/* TCP/IP close
+ * Accepts: TCP/IP stream
+ */
+
+void tcp_close (TCPSTREAM *stream)
+{
+ tcp_abort (stream); /* nuke the stream */
+ /* flush host names */
+ fs_give ((void **) &stream->host);
+ fs_give ((void **) &stream->localhost);
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+
+/* TCP/IP abort stream
+ * Accepts: TCP/IP stream
+ * Returns: NIL always
+ */
+
+long tcp_abort (TCPSTREAM *stream)
+{
+ if (stream->tcpsi >= 0) { /* no-op if no socket */
+ /* nuke the socket */
+ tcp_disconnect (&(stream->tcpsi));
+ stream->tcpsi = stream->tcpso = -1;
+ }
+ return NIL;
+}
+
+/* TCP/IP get host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_host (TCPSTREAM *stream)
+{
+ return stream->host; /* return host name */
+}
+
+
+/* TCP/IP get remote host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_remotehost (TCPSTREAM *stream)
+{
+ return stream->host; /* return host name */
+}
+
+
+/* TCP/IP return port for this stream
+ * Accepts: TCP/IP stream
+ * Returns: port number for this stream
+ */
+
+unsigned long tcp_port (TCPSTREAM *stream)
+{
+ return stream->port; /* return port number */
+}
+
+
+/* TCP/IP get local host name
+ * Accepts: TCP/IP stream
+ * Returns: local host name
+ */
+
+char *tcp_localhost (TCPSTREAM *stream)
+{
+ return stream->localhost; /* return local host name */
+}
+
+/* Return my local host name
+ * Returns: my local host name
+ */
+
+char *mylocalhost ()
+{
+ int status;
+ char tmp[MAILTMPLEN];
+ if (!myLocalHost) { /* have local host yet? */
+ /* receives local host name */
+ struct dsc$descriptor LocalhostDesc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_D,NULL};
+ if (!((status = net_get_hostname (&LocalhostDesc)) & 0x1)) {
+ sprintf (tmp,"Can't get local hostname, status=%d",status);
+ mm_log (tmp,ERROR);
+ return "UNKNOWN";
+ }
+ strncpy (tmp,LocalhostDesc.dsc$a_pointer,LocalhostDesc.dsc$w_length);
+ tmp[LocalhostDesc.dsc$w_length] = '\0';
+ str$free1_dx (&LocalhostDesc);
+ myLocalHost = cpystr (tmp);
+ }
+ return myLocalHost;
+}
+
+/* TCP/IP return canonical form of host name
+ * Accepts: host name
+ * Returns: canonical form of host name
+ */
+
+char *tcp_canonical (char *name)
+{
+ return name;
+}
+
+
+/* TCP/IP get client host name (server calls only)
+ * Returns: client host name
+ */
+
+char *tcp_clienthost ()
+{
+ return "UNKNOWN";
+}
diff --git a/imap/src/osdep/vms/tcp_vmsm.c b/imap/src/osdep/vms/tcp_vmsm.c
new file mode 100644
index 00000000..bc9d9ca7
--- /dev/null
+++ b/imap/src/osdep/vms/tcp_vmsm.c
@@ -0,0 +1,479 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: VMS TCP/IP routines for Multinet
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 2 August 1994
+ * Last Edited: 13 January 2008
+ */
+
+
+static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */
+static long ttmo_read = 0; /* TCP timeouts, in seconds */
+static long ttmo_write = 0;
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd);
+
+/* TCP/IP manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tcp_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_TIMEOUT:
+ tmoh = (tcptimeout_t) value;
+ case GET_TIMEOUT:
+ ret = (void *) tmoh;
+ break;
+ case SET_READTIMEOUT:
+ ttmo_read = (long) value;
+ case GET_READTIMEOUT:
+ ret = (void *) ttmo_read;
+ break;
+ case SET_WRITETIMEOUT:
+ ttmo_write = (long) value;
+ case GET_WRITETIMEOUT:
+ ret = (void *) ttmo_write;
+ break;
+ }
+ return ret;
+}
+
+/* TCP/IP open
+ * Accepts: host name
+ * contact service name
+ * contact port number
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream = NIL;
+ int sock;
+ char *s;
+ struct sockaddr_in sin;
+ struct hostent *host_name;
+ char hostname[MAILTMPLEN];
+ char tmp[MAILTMPLEN];
+ struct protoent *pt = getprotobyname ("tcp");
+ struct servent *sv = NIL;
+ port &= 0xffff; /* erase flags */
+ if (service) { /* service specified? */
+ if (*service == '*') { /* yes, special alt driver kludge? */
+ sv = getservbyname (service + 1,"tcp");
+ }
+ else sv = getservbyname (service,"tcp");
+ }
+ /* user service name port */
+ if (sv) port = ntohs (sin.sin_port = sv->s_port);
+ /* copy port number in network format */
+ else sin.sin_port = htons (port);
+ /* The domain literal form is used (rather than simply the dotted decimal
+ as with other Unix programs) because it has to be a valid "host name"
+ in mailsystem terminology. */
+ /* look like domain literal? */
+ if (host[0] == '[' && host[(strlen (host))-1] == ']') {
+ strcpy (hostname,host+1); /* yes, copy number part */
+ hostname[(strlen (hostname))-1] = '\0';
+ if ((sin.sin_addr.s_addr = inet_addr (hostname)) != -1) {
+ sin.sin_family = AF_INET; /* family is always Internet */
+ strcpy (hostname,host); /* hostname is user's argument */
+ }
+ else {
+ sprintf (tmp,"Bad format domain-literal: %.80s",host);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ }
+
+ else { /* lookup host name, note that brain-dead Unix
+ requires lowercase! */
+ strcpy (hostname,host); /* in case host is in write-protected memory */
+ if ((host_name = gethostbyname (lcase (hostname)))) {
+ /* copy address type */
+ sin.sin_family = host_name->h_addrtype;
+ /* copy host name */
+ strcpy (hostname,host_name->h_name);
+ /* copy host addresses */
+ memcpy (&sin.sin_addr,host_name->h_addr,host_name->h_length);
+ }
+ else {
+ sprintf (tmp,"No such host as %.80s",host);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ }
+ /* get a TCP stream */
+ if ((sock = socket (sin.sin_family,SOCK_STREAM,pt ? pt->p_proto : 0)) < 0) {
+ sprintf (tmp,"Unable to create TCP socket: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+#if 0
+ /* Maybe this test is necessary. It depends upon how VMS implements
+ * fd_set. UNIX-style fd_set needs it; Windows-style does not.
+ */
+ else if (sock >= FD_SETSIZE) {/* unselectable sockets are useless */
+ sprintf (tmp,"Unable to create selectable TCP socket (%d >= %d)",
+ sock,FD_SETSIZE);
+ close (sock);
+ return NIL;
+ }
+#endif
+ /* open connection */
+ if (connect (sock,(struct sockaddr *)&sin,sizeof (sin)) < 0) {
+ sprintf (tmp,"Can't connect to %.80s,%d: %s",hostname,port,
+ strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* create TCP/IP stream */
+ stream = (TCPSTREAM *) fs_get (sizeof (TCPSTREAM));
+ /* copy official host name */
+ stream->host = cpystr (hostname);
+ /* get local name */
+ gethostname (tmp,MAILTMPLEN-1);
+ stream->localhost = cpystr ((host_name = gethostbyname (tmp)) ?
+ host_name->h_name : tmp);
+ /* init sockets */
+ stream->port = port; /* port number */
+ stream->tcpsi = stream->tcpso = sock;
+ stream->ictr = 0; /* init input counter */
+ return stream; /* return success */
+}
+
+/* TCP/IP authenticated open
+ * Accepts: NETMBX specifier
+ * service name
+ * returned user name buffer
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL;
+}
+
+/* TCP receive line
+ * Accepts: TCP stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *tcp_getline (TCPSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = tcp_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = tcp_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* TCP receive line or partial line
+ * Accepts: TCP stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!tcp_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!tcp_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* TCP/IP receive buffer
+ * Accepts: TCP/IP stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer)
+{
+ unsigned long n;
+ char *bufptr = buffer;
+ while (size > 0) { /* until request satisfied */
+ if (!tcp_getdata (stream)) return NIL;
+ n = min (size,stream->ictr);/* number of bytes to transfer */
+ /* do the copy */
+ memcpy (bufptr,stream->iptr,n);
+ bufptr += n; /* update pointer */
+ stream->iptr +=n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -=n;
+ }
+ bufptr[0] = '\0'; /* tie off string */
+ return T;
+}
+
+/* TCP/IP receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getdata (TCPSTREAM *stream)
+{
+ int i;
+ fd_set fds,efds;
+ struct timeval tmo;
+ time_t t = time (0);
+ if (stream->tcpsi < 0) return NIL;
+ while (stream->ictr < 1) { /* if nothing in the buffer */
+ time_t tl = time (0); /* start of request */
+ tmo.tv_sec = ttmo_read; /* read timeout */
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ FD_SET (stream->tcpsi,&fds);/* set bit in selection vector */
+ FD_SET(stream->tcpsi,&efds);/* set bit in error selection vector */
+ errno = NIL; /* block and read */
+ while (((i = select (getdtablesize (),&fds,0,&efds,ttmo_read ? &tmo:0))<0)
+ && (errno == EINTR));
+ if (!i) { /* timeout? */
+ time_t tc = time (0);
+ if (tmoh && ((*tmoh) (tc - t,tc - tl))) continue;
+ else return tcp_abort (stream);
+ }
+ else if (i < 0) return tcp_abort (stream);
+ while (((i = socket_read (stream->tcpsi,stream->ibuf,BUFLEN)) < 0) &&
+ (errno == EINTR));
+ if (i < 1) return tcp_abort (stream);
+ stream->iptr = stream->ibuf;/* point at TCP buffer */
+ stream->ictr = i; /* set new byte count */
+ }
+ return T;
+}
+
+/* TCP/IP send string as record
+ * Accepts: TCP/IP stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long tcp_soutr (TCPSTREAM *stream,char *string)
+{
+ return tcp_sout (stream,string,(unsigned long) strlen (string));
+}
+
+
+/* TCP/IP send string
+ * Accepts: TCP/IP stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
+{
+ int i;
+ fd_set fds;
+ struct timeval tmo;
+ time_t t = time (0);
+ if (stream->tcpso < 0) return NIL;
+ while (size > 0) { /* until request satisfied */
+ time_t tl = time (0); /* start of request */
+ tmo.tv_sec = ttmo_write; /* write timeout */
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_SET (stream->tcpso,&fds);/* set bit in selection vector */
+ errno = NIL; /* block and write */
+ while (((i = select (getdtablesize (),0,&fds,0,ttmo_write ? &tmo : 0)) < 0)
+ && (errno == EINTR));
+ if (!i) { /* timeout? */
+ time_t tc = time (0);
+ if (tmoh && ((*tmoh) (tc - t,tc - tl))) continue;
+ else return tcp_abort (stream);
+ }
+ else if (i < 0) return tcp_abort (stream);
+ while (((i = socket_write (stream->tcpso,string,size)) < 0) &&
+ (errno == EINTR));
+ if (i < 0) return tcp_abort (stream);
+ size -= i; /* how much we sent */
+ string += i;
+ }
+ return T; /* all done */
+}
+
+/* TCP/IP close
+ * Accepts: TCP/IP stream
+ */
+
+void tcp_close (TCPSTREAM *stream)
+{
+ tcp_abort (stream); /* nuke the stream */
+ /* flush host names */
+ fs_give ((void **) &stream->host);
+ fs_give ((void **) &stream->localhost);
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+
+/* TCP/IP abort stream
+ * Accepts: TCP/IP stream
+ * Returns: NIL always
+ */
+
+long tcp_abort (TCPSTREAM *stream)
+{
+ int i;
+ if (stream->tcpsi >= 0) { /* no-op if no socket */
+ /* nuke the socket */
+ socket_close (stream->tcpsi);
+ if (stream->tcpsi != stream->tcpso) socket_close (stream->tcpso);
+ stream->tcpsi = stream->tcpso = -1;
+ }
+ return NIL;
+}
+
+/* TCP/IP get host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_host (TCPSTREAM *stream)
+{
+ return stream->host; /* return host name */
+}
+
+
+/* TCP/IP get remote host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_remotehost (TCPSTREAM *stream)
+{
+ return stream->host; /* return host name */
+}
+
+
+/* TCP/IP return port for this stream
+ * Accepts: TCP/IP stream
+ * Returns: port number for this stream
+ */
+
+unsigned long tcp_port (TCPSTREAM *stream)
+{
+ return stream->port; /* return port number */
+}
+
+
+/* TCP/IP get local host name
+ * Accepts: TCP/IP stream
+ * Returns: local host name
+ */
+
+char *tcp_localhost (TCPSTREAM *stream)
+{
+ return stream->localhost; /* return local host name */
+}
+
+/* Return my local host name
+ * Returns: my local host name
+ */
+
+char *mylocalhost ()
+{
+ char tmp[MAILTMPLEN];
+ struct hostent *hn;
+ if (!myLocalHost) { /* have local host yet? */
+ gethostname(tmp,MAILTMPLEN);/* get local host name */
+ myLocalHost = cpystr ((hn = gethostbyname (tmp)) ? hn->h_name : tmp);
+ }
+ return myLocalHost;
+}
+
+
+/* TCP/IP return canonical form of host name
+ * Accepts: host name
+ * Returns: canonical form of host name
+ */
+
+char *tcp_canonical (char *name)
+{
+ char host[MAILTMPLEN];
+ struct hostent *he;
+ /* look like domain literal? */
+ if (name[0] == '[' && name[strlen (name) - 1] == ']') return name;
+ /* note that Unix requires lowercase! */
+ else return (he = gethostbyname (lcase (strcpy (host,name)))) ?
+ he->h_name : name;
+}
+
+
+/* TCP/IP get client host name (server calls only)
+ * Returns: client host name
+ */
+
+char *tcp_clienthost ()
+{
+ return "UNKNOWN";
+}
diff --git a/imap/src/osdep/vms/tcp_vmsn.c b/imap/src/osdep/vms/tcp_vmsn.c
new file mode 100644
index 00000000..4648e48d
--- /dev/null
+++ b/imap/src/osdep/vms/tcp_vmsn.c
@@ -0,0 +1,222 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy VMS TCP/IP routines for non-TCP/IP systems
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 2 August 1994
+ * Last Edited: 30 August 2006
+ */
+
+/* TCP/IP manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tcp_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+
+/* TCP/IP open
+ * Accepts: host name
+ * contact service name
+ * contact port number
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
+{
+ char tmp[MAILTMPLEN];
+ port &= 0xffff; /* erase flags */
+ if (port) sprintf (tmp,"Can't connect to %.80s,%d: no TCP",host,port);
+ else sprintf (tmp,"Can't connect to %.80s,%s: no TCP",host,service);
+ mm_log (tmp,ERROR);
+ return NIL;
+}
+
+
+/* TCP/IP authenticated open
+ * Accepts: NETMBX specifier
+ * service name
+ * returned user name buffer
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL;
+}
+
+/* TCP/IP receive line
+ * Accepts: TCP/IP stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *tcp_getline (TCPSTREAM *stream)
+{
+ return NIL;
+}
+
+
+/* TCP/IP receive buffer
+ * Accepts: TCP/IP stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *buffer)
+{
+ return NIL;
+}
+
+
+/* TCP/IP receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getdata (TCPSTREAM *stream)
+{
+ return NIL;
+}
+
+/* TCP/IP send string as record
+ * Accepts: TCP/IP stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long tcp_soutr (TCPSTREAM *stream,char *string)
+{
+ return NIL;
+}
+
+
+/* TCP/IP send string
+ * Accepts: TCP/IP stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
+{
+ return NIL;
+}
+
+
+/* TCP/IP close
+ * Accepts: TCP/IP stream
+ */
+
+void tcp_close (TCPSTREAM *stream)
+{
+}
+
+
+/* TCP/IP abort stream
+ * Accepts: TCP/IP stream
+ * Returns: NIL always
+ */
+
+long tcp_abort (TCPSTREAM *stream)
+{
+ return NIL;
+}
+
+/* TCP/IP get host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_host (TCPSTREAM *stream)
+{
+ return NIL;
+}
+
+
+/* TCP/IP get remote host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_remotehost (TCPSTREAM *stream)
+{
+ return NIL;
+}
+
+
+/* TCP/IP get local host name
+ * Accepts: TCP/IP stream
+ * Returns: local host name
+ */
+
+char *tcp_localhost (TCPSTREAM *stream)
+{
+ return NIL;
+}
+
+
+/* TCP/IP return port for this stream
+ * Accepts: TCP/IP stream
+ * Returns: port number for this stream
+ */
+
+unsigned long tcp_port (TCPSTREAM *stream)
+{
+ return 0xffffffff; /* return port number */
+}
+
+
+/* Return my local host name
+ * Returns: my local host name
+ */
+
+char *mylocalhost ()
+{
+ /* have local host yet? */
+ if (!myLocalHost) myLocalHost = cpystr (getenv ("SYS$NODE"));
+ return myLocalHost;
+}
+
+/* TCP/IP return canonical form of host name
+ * Accepts: host name
+ * Returns: canonical form of host name
+ */
+
+char *tcp_canonical (char *name)
+{
+ return name;
+}
+
+
+/* TCP/IP get client host name (server calls only)
+ * Returns: client host name
+ */
+
+char *tcp_clienthost ()
+{
+ return "UNKNOWN";
+}
diff --git a/imap/src/osdep/wce/drivers.bat b/imap/src/osdep/wce/drivers.bat
new file mode 100644
index 00000000..0964f537
--- /dev/null
+++ b/imap/src/osdep/wce/drivers.bat
@@ -0,0 +1,33 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Driver Linkage Generator for DOS/NT
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 11 October 1989
+REM Last Edited:30 August 2006
+
+REM Erase old driver linkage
+IF EXIST LINKAGE.* DEL LINKAGE.*
+
+REM Now define the new list
+FOR %%D IN (%1 %2 %3 %4 %5 %6 %7 %8 %9) DO CALL DRIVRAUX %%D
+
+EXIT 0
diff --git a/imap/src/osdep/wce/drivraux.bat b/imap/src/osdep/wce/drivraux.bat
new file mode 100644
index 00000000..30649a78
--- /dev/null
+++ b/imap/src/osdep/wce/drivraux.bat
@@ -0,0 +1,30 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Driver Linkage Generator auxillary for NT/Win9x
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 11 October 1989
+REM Last Edited:30 August 2006
+
+ECHO extern DRIVER %1driver; >> LINKAGE.H
+REM Note the introduction of the caret to quote the ampersand in NT
+if "%OS%" == "Windows_NT" ECHO mail_link (^&%1driver); /* link in the %1 driver */ >> LINKAGE.C
+if "%OS%" == "" ECHO mail_link (&%1driver); /* link in the %1 driver */ >> LINKAGE.C
diff --git a/imap/src/osdep/wce/dummy.h b/imap/src/osdep/wce/dummy.h
new file mode 100644
index 00000000..32650e06
--- /dev/null
+++ b/imap/src/osdep/wce/dummy.h
@@ -0,0 +1,43 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 9 May 1991
+ * Last Edited: 30 August 2006
+ */
+
+/* Exported function prototypes */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat);
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long scan_contents (DRIVER *dtb,char *name,char *contents,
+ unsigned long csiz,unsigned long fsiz);
+long dummy_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz);
+long dummy_create (MAILSTREAM *stream,char *mailbox);
+long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode);
+long dummy_delete (MAILSTREAM *stream,char *mailbox);
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname);
+char *dummy_file (char *dst,char *name);
+long dummy_canonicalize (char *tmp,char *ref,char *pat);
diff --git a/imap/src/osdep/wce/dummywce.c b/imap/src/osdep/wce/dummywce.c
new file mode 100644
index 00000000..2440be27
--- /dev/null
+++ b/imap/src/osdep/wce/dummywce.c
@@ -0,0 +1,301 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Dummy routines for WCE
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 24 May 1993
+ * Last Edited: 30 August 2006
+ */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <direct.h>
+#include "mail.h"
+#include "osdep.h"
+#include <sys\stat.h>
+#include <dos.h>
+#include "dummy.h"
+#include "misc.h"
+
+/* Function prototypes */
+
+DRIVER *dummy_valid (char *name);
+void *dummy_parameters (long function,void *value);
+MAILSTREAM *dummy_open (MAILSTREAM *stream);
+void dummy_close (MAILSTREAM *stream,long options);
+long dummy_ping (MAILSTREAM *stream);
+void dummy_check (MAILSTREAM *stream);
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options);
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+/* Dummy routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER dummydriver = {
+ "dummy", /* driver name */
+ DR_LOCAL|DR_MAIL, /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ dummy_valid, /* mailbox is valid for us */
+ dummy_parameters, /* manipulate parameters */
+ dummy_scan, /* scan mailboxes */
+ dummy_list, /* list mailboxes */
+ dummy_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ dummy_create, /* create mailbox */
+ dummy_delete, /* delete mailbox */
+ dummy_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ dummy_open, /* open mailbox */
+ dummy_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message structure */
+ NIL, /* fetch header */
+ NIL, /* fetch text */
+ NIL, /* fetch message data */
+ NIL, /* unique identifier */
+ NIL, /* message number from UID */
+ NIL, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ dummy_ping, /* ping mailbox to see if still alive */
+ dummy_check, /* check for new messages */
+ dummy_expunge, /* expunge deleted messages */
+ dummy_copy, /* copy messages to another mailbox */
+ dummy_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+
+ /* prototype stream */
+MAILSTREAM dummyproto = {&dummydriver};
+
+ /* driver parameters */
+static char *file_extension = NIL;
+
+/* Dummy validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *dummy_valid (char *name)
+{
+ char *s,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* must be valid local mailbox */
+ return (name && *name && (*name != '{') &&
+ (s = mailboxfile (tmp,name)) && (!*s || !stat (s,&sbuf))) ?
+ &dummydriver : NIL;
+}
+
+
+/* Dummy manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *dummy_parameters (long function,void *value)
+{
+ return value;
+}
+
+/* Dummy scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ /* return silently */
+}
+
+/* Dummy list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ /* return silently */
+}
+
+
+/* Dummy list subscribed mailboxes
+ * Accepts: mail stream
+ * pattern to search
+ */
+
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ /* return silently */
+}
+
+/* Dummy create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_create (MAILSTREAM *stream,char *mailbox)
+{
+ return NIL; /* always fails */
+}
+
+
+/* Dummy delete mailbox
+ * Accepts: mail stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return NIL; /* always fails */
+}
+
+
+/* Mail rename mailbox
+ * Accepts: mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ return NIL; /* always fails */
+}
+
+/* Dummy open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *dummy_open (MAILSTREAM *stream)
+{
+ char tmp[MAILTMPLEN];
+ /* OP_PROTOTYPE call or silence */
+ if (!stream || stream->silent) return NIL;
+ if (compare_cstring (stream->mailbox,"INBOX")) {
+ sprintf (tmp,"Not a mailbox: %s",stream->mailbox);
+ mm_log (tmp,ERROR);
+ return NIL; /* always fails */
+ }
+ if (!stream->silent) { /* only if silence not requested */
+ mail_exists (stream,0); /* say there are 0 messages */
+ mail_recent (stream,0);
+ stream->uid_validity = time (0);
+ }
+ stream->inbox = T; /* note that it's an INBOX */
+ return stream; /* return success */
+}
+
+
+/* Dummy close
+ * Accepts: MAIL stream
+ * options
+ */
+
+void dummy_close (MAILSTREAM *stream,long options)
+{
+ /* return silently */
+}
+
+/* Dummy ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+long dummy_ping (MAILSTREAM *stream)
+{
+ return T;
+}
+
+
+/* Dummy check mailbox
+ * Accepts: MAIL stream
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+void dummy_check (MAILSTREAM *stream)
+{
+ dummy_ping (stream); /* invoke ping */
+}
+
+
+/* Dummy expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ return LONGT;
+}
+
+/* Dummy copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * options
+ * Returns: T if copy successful, else NIL
+ */
+
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) fatal ("Impossible dummy_copy");
+ return NIL;
+}
+
+
+/* Dummy append message string
+ * Accepts: mail stream
+ * destination mailbox
+ * stringstruct of message to append
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Can't append to %s",mailbox);
+ mm_log (tmp,ERROR); /* pass up error */
+ return NIL; /* always fails */
+}
diff --git a/imap/src/osdep/wce/env_wce.c b/imap/src/osdep/wce/env_wce.c
new file mode 100644
index 00000000..ea2dbd98
--- /dev/null
+++ b/imap/src/osdep/wce/env_wce.c
@@ -0,0 +1,301 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: WCE environment routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+static char *myUserName = NIL; /* user name */
+static char *myLocalHost = NIL; /* local host name */
+static char *myClientHost = NIL;/* client host name */
+static char *myServerHost = NIL;/* server host name */
+static char *myHomeDir = NIL; /* home directory name */
+static char *myNewsrc = NIL; /* newsrc file name */
+static char *sysInbox = NIL; /* system inbox name */
+static long list_max_level = 5; /* maximum level of list recursion */
+static short no822tztext = NIL; /* disable RFC [2]822 timezone text */
+ /* home namespace */
+static NAMESPACE nshome = {"",'\\',NIL,NIL};
+ /* namespace list */
+static NAMESPACE *nslist[3] = {&nshome,NIL,NIL};
+static long alarm_countdown = 0;/* alarm count down */
+static void (*alarm_rang) (); /* alarm interrupt function */
+static unsigned int rndm = 0; /* initial `random' number */
+
+
+/* Dummy definitions to prevent errors */
+
+#define server_login(user,pass,authuser,argc,argv) NIL
+#define authserver_login(user,authuser,argc,argv) NIL
+#define myusername() ""
+#define MD5ENABLE "\\.nosuch.."
+
+#include "pmatch.c" /* include wildcard pattern matcher */
+
+/* Environment manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *env_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case GET_NAMESPACE:
+ ret = (void *) nslist;
+ break;
+ case SET_HOMEDIR:
+ myHomeDir = cpystr ((char *) value);
+ case GET_HOMEDIR:
+ ret = (void *) myHomeDir;
+ break;
+ case SET_LOCALHOST:
+ myLocalHost = cpystr ((char *) value);
+ case GET_LOCALHOST:
+ ret = (void *) myLocalHost;
+ break;
+ case SET_NEWSRC:
+ if (myNewsrc) fs_give ((void **) &myNewsrc);
+ myNewsrc = cpystr ((char *) value);
+ case GET_NEWSRC:
+ if (!myNewsrc) { /* set news file name if not defined */
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"%s\\NEWSRC",myhomedir ());
+ myNewsrc = cpystr (tmp);
+ }
+ ret = (void *) myNewsrc;
+ break;
+ case SET_SYSINBOX:
+ if (sysInbox) fs_give ((void **) &sysInbox);
+ sysInbox = cpystr ((char *) value);
+ case GET_SYSINBOX:
+ ret = (void *) sysInbox;
+ break;
+ case SET_LISTMAXLEVEL:
+ list_max_level = (long) value;
+ case GET_LISTMAXLEVEL:
+ ret = (void *) list_max_level;
+ break;
+ case SET_DISABLE822TZTEXT:
+ no822tztext = value ? T : NIL;
+ case GET_DISABLE822TZTEXT:
+ ret = (void *) (no822tztext ? VOIDT : NIL);
+ break;
+ }
+ return ret;
+}
+
+/* Write current time
+ * Accepts: destination string
+ * optional format of day-of-week prefix
+ * format of date and time
+ * flag whether to append symbolic timezone
+ */
+
+static void do_date (char *date,char *prefix,char *fmt,int suffix)
+{
+ time_t tn = time (0);
+ struct tm *t = gmtime (&tn);
+ int zone = t->tm_hour * 60 + t->tm_min;
+ int julian = t->tm_yday;
+ t = localtime (&tn); /* get local time now */
+ /* minus UTC minutes since midnight */
+ zone = t->tm_hour * 60 + t->tm_min - zone;
+ /* julian can be one of:
+ * 36x local time is December 31, UTC is January 1, offset -24 hours
+ * 1 local time is 1 day ahead of UTC, offset +24 hours
+ * 0 local time is same day as UTC, no offset
+ * -1 local time is 1 day behind UTC, offset -24 hours
+ * -36x local time is January 1, UTC is December 31, offset +24 hours
+ */
+ if (julian = t->tm_yday -julian)
+ zone += ((julian < 0) == (abs (julian) == 1)) ? -24*60 : 24*60;
+ if (prefix) { /* want day of week? */
+ sprintf (date,prefix,days[t->tm_wday]);
+ date += strlen (date); /* make next sprintf append */
+ }
+ /* output the date */
+ sprintf (date,fmt,t->tm_mday,months[t->tm_mon],t->tm_year+1900,
+ t->tm_hour,t->tm_min,t->tm_sec,zone/60,abs (zone) % 60);
+ if (suffix) { /* append timezone suffix if desired */
+ char *tz;
+ tzset (); /* get timezone from TZ environment stuff */
+ tz = tzname[daylight ? (((struct tm *) t)->tm_isdst > 0) : 0];
+ if (tz && tz[0]) sprintf (date + strlen (date)," (%s)",tz);
+ }
+}
+
+
+/* Write current time in RFC 822 format
+ * Accepts: destination string
+ */
+
+void rfc822_date (char *date)
+{
+ do_date (date,"%s, ","%d %s %d %02d:%02d:%02d %+03d%02d",
+ no822tztext ? NIL : T);
+}
+
+
+/* Write current time in internal format
+ * Accepts: destination string
+ */
+
+void internal_date (char *date)
+{
+ do_date (date,NIL,"%02d-%s-%d %02d:%02d:%02d %+03d%02d",NIL);
+}
+
+/* Return random number
+ */
+
+long random ()
+{
+ if (!rndm) srand (rndm = (unsigned) time (0L));
+ return (long) rand ();
+}
+
+/* Return default drive
+ * Returns: default drive
+ */
+
+static char *defaultDrive (void)
+{
+ char *s;
+ return ((s = getenv ("SystemDrive")) && *s) ? s : "C:";
+}
+
+
+/* Return home drive from environment variables
+ * Returns: home drive
+ */
+
+static char *homeDrive (void)
+{
+ char *s;
+ return ((s = getenv ("HOMEDRIVE")) && *s) ? s : defaultDrive ();
+}
+
+
+/* Return home path from environment variables
+ * Accepts: path to write into
+ * Returns: home path or NIL if it can't be determined
+ */
+
+static char *homePath (char *path)
+{
+ int i;
+ char *s;
+ if (!((s = getenv ("HOMEPATH")) && (i = strlen (s)))) return NIL;
+ if (((s[i-1] == '\\') || (s[i-1] == '/'))) s[i-1] = '\0';
+ sprintf (path,"%s%s",homeDrive (),s);
+ return path;
+}
+
+/* Return my home directory name
+ * Returns: my home directory name
+ */
+
+char *myhomedir ()
+{
+ char tmp[MAILTMPLEN];
+ /* initialize if first time */
+ if (!myHomeDir) myHomeDir = homePath (tmp);
+ return myHomeDir ? myHomeDir : homeDrive ();
+}
+
+/* Return system standard INBOX
+ * Accepts: buffer string
+ */
+
+char *sysinbox ()
+{
+ char tmp[MAILTMPLEN];
+ if (!sysInbox) { /* initialize if first time */
+ sprintf (tmp,"%s\\INBOX",myhomedir ());
+ sysInbox = cpystr (tmp); /* system inbox is from mail spool */
+ }
+ return sysInbox;
+}
+
+
+/* Return mailbox file name
+ * Accepts: destination buffer
+ * mailbox name
+ * Returns: file name
+ */
+
+char *mailboxfile (char *dst,char *name)
+{
+ char *dir = myhomedir ();
+ *dst = '\0'; /* default to empty string */
+ if (((name[0] == 'I') || (name[0] == 'i')) &&
+ ((name[1] == 'N') || (name[1] == 'n')) &&
+ ((name[2] == 'B') || (name[2] == 'b')) &&
+ ((name[3] == 'O') || (name[3] == 'o')) &&
+ ((name[4] == 'X') || (name[4] == 'x')) && !name[5]) name = NIL;
+ /* reject namespace names or names with / */
+ if (name && ((*name == '#') || strchr (name,'/'))) return NIL;
+ else if (!name) return dst; /* driver selects the INBOX name */
+ /* absolute path name? */
+ else if ((*name == '\\') || (name[1] == ':')) return strcpy (dst,name);
+ /* build resulting name */
+ sprintf (dst,"%s\\%s",dir,name);
+ return dst; /* return it */
+}
+
+
+/* Determine default prototype stream to user
+ * Accepts: type (NIL for create, T for append)
+ * Returns: default prototype stream
+ */
+
+MAILSTREAM *default_proto (long type)
+{
+ extern MAILSTREAM CREATEPROTO,APPENDPROTO;
+ return type ? &APPENDPROTO : &CREATEPROTO;
+}
+
+/* Emulator for BSD syslog() routine
+ * Accepts: priority
+ * message
+ * parameters
+ */
+
+void syslog (int priority,const char *message,...)
+{
+}
+
+
+/* Emulator for BSD openlog() routine
+ * Accepts: identity
+ * options
+ * facility
+ */
+
+void openlog (const char *ident,int logopt,int facility)
+{
+}
diff --git a/imap/src/osdep/wce/env_wce.h b/imap/src/osdep/wce/env_wce.h
new file mode 100644
index 00000000..e3c6c7f0
--- /dev/null
+++ b/imap/src/osdep/wce/env_wce.h
@@ -0,0 +1,70 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: WCE environment routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+#define SUBSCRIPTIONFILE(t) sprintf (t,"%s\\MAILBOX.LST",myhomedir ())
+#define SUBSCRIPTIONTEMP(t) sprintf (t,"%s\\MAILBOX.TMP",myhomedir ())
+
+#define L_SET SEEK_SET
+
+/* Function prototypes */
+
+#include "env.h"
+
+static char *defaultDrive (void);
+static char *homeDrive (void);
+static char *homePath (char *path);
+char *sysinbox ();
+long random ();
+unsigned long unix_crlfcpy (char **dst,unsigned long *dstl,char *src,
+ unsigned long srcl);
+unsigned long unix_crlflen (STRING *s);
+#define getpid random
+
+
+/* syslog() emulation */
+
+#define LOG_MAIL (2<<3) /* mail system */
+#define LOG_DAEMON (3<<3) /* system daemons */
+#define LOG_AUTH (4<<3) /* security/authorization messages */
+#define LOG_EMERG 0 /* system is unusable */
+#define LOG_ALERT 1 /* action must be taken immediately */
+#define LOG_CRIT 2 /* critical conditions */
+#define LOG_ERR 3 /* error conditions */
+#define LOG_WARNING 4 /* warning conditions */
+#define LOG_NOTICE 5 /* normal but signification condition */
+#define LOG_INFO 6 /* informational */
+#define LOG_DEBUG 7 /* debug-level messages */
+#define LOG_PID 0x01 /* log the pid with each message */
+#define LOG_CONS 0x02 /* log on the console if errors in sending */
+#define LOG_ODELAY 0x04 /* delay open until syslog() is called */
+#define LOG_NDELAY 0x08 /* don't delay open */
+#define LOG_NOWAIT 0x10 /* if forking to log on console, don't wait() */
+
+void openlog (const char *ident,int logopt,int facility);
+void syslog (int priority,const char *message,...);
diff --git a/imap/src/osdep/wce/fs_wce.c b/imap/src/osdep/wce/fs_wce.c
new file mode 100644
index 00000000..03908328
--- /dev/null
+++ b/imap/src/osdep/wce/fs_wce.c
@@ -0,0 +1,62 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Free storage management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Get a block of free storage
+ * Accepts: size of desired block
+ * Returns: free storage block
+ */
+
+void *fs_get (size_t size)
+{
+ void *block = malloc (size ? size : (size_t) 1);
+ if (!block) fatal ("Out of memory");
+ return (block);
+}
+
+
+/* Resize a block of free storage
+ * Accepts: ** pointer to current block
+ * new size
+ */
+
+void fs_resize (void **block,size_t size)
+{
+ if (!(*block = realloc (*block,size ? size : (size_t) 1)))
+ fatal ("Can't resize memory");
+}
+
+
+/* Return a block of free storage
+ * Accepts: ** pointer to free storage block
+ */
+
+void fs_give (void **block)
+{
+ free (*block);
+ *block = NIL;
+}
diff --git a/imap/src/osdep/wce/ftl_wce.c b/imap/src/osdep/wce/ftl_wce.c
new file mode 100644
index 00000000..9e65ef55
--- /dev/null
+++ b/imap/src/osdep/wce/ftl_wce.c
@@ -0,0 +1,38 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: DOS/VMS/TOPS-20 crash management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Report a fatal error
+ * Accepts: string to output
+ */
+
+void fatal (char *string)
+{
+ mm_fatal (string); /* pass up the string */
+ abort (); /* die horribly */
+}
diff --git a/imap/src/osdep/wce/makefile.wce b/imap/src/osdep/wce/makefile.wce
new file mode 100644
index 00000000..cdf8198d
--- /dev/null
+++ b/imap/src/osdep/wce/makefile.wce
@@ -0,0 +1,96 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+# Program: Portable C client makefile -- WCE version
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 11 May 1989
+# Last Edited: 30 August 2006
+
+
+EXTRAAUTHENTICATORS=
+DEFAULTAUTHENTICATORS=ext md5 pla log
+EXTRADRIVERS =
+DRIVERS = imap nntp pop3
+DEFAULTDRIVER = dummy
+CFLAGS= /MT /W3 /D_SH3_ -nologo $(EXTRACFLAGS)
+CC = shcl
+CCLIENTLIB= cclient.lib
+
+all: $(CCLIENTLIB)
+
+.c.obj:
+ $(CC) -c $(CFLAGS) $*.c
+
+osdep.h: os_wce.h
+ copy os_wce.h osdep.h
+ drivers $(EXTRADRIVERS) $(DRIVERS) dummy
+ setproto $(DEFAULTDRIVER) $(DEFAULTDRIVER)
+ mkauths $(EXTRAAUTHENTICATORS) $(DEFAULTAUTHENTICATORS)
+
+mail.obj: mail.h misc.h osdep.h mail.c
+
+misc.obj: mail.h misc.h misc.c
+
+flstring.obj: mail.h misc.h osdep.h flstring.h flstring.c
+
+netmsg.obj: mail.h misc.h netmsg.h osdep.h netmsg.c
+
+newsrc.obj: mail.h misc.h newsrc.h osdep.h newsrc.c
+
+rfc822.obj: mail.h rfc822.h misc.h rfc822.c
+
+smanager.obj: mail.h misc.h smanager.c
+
+utf8.obj: mail.h misc.h osdep.h utf8.h
+
+utf8aux.obj: mail.h misc.h osdep.h utf8.h
+
+imap4r1.obj: mail.h imap4r1.h misc.h osdep.h imap4r1.c
+
+nntp.obj: mail.h nntp.h smtp.h rfc822.h misc.h osdep.h nntp.c
+
+pop3.obj: mail.h pop3.h rfc822.h misc.h osdep.h pop3.c
+
+smtp.obj: mail.h smtp.h rfc822.h misc.h osdep.h smtp.c
+
+os_wce.obj: mail.h osdep.h env_wce.h fs.h ftl.h nl.h tcp.h tcp_wce.h \
+ os_wce.c fs_wce.c ftl_wce.c nl_wce.c env_wce.c tcp_wce.c \
+ auth_md5.c auth_log.c pmatch.c
+
+dummywce.obj: mail.h dummy.h misc.h osdep.h dummywce.c
+
+$(CCLIENTLIB): mail.obj misc.obj flstring.obj netmsg.obj \
+ newsrc.obj rfc822.obj smanager.obj utf8.obj utf8aux.obj \
+ imap4r1.obj nntp.obj pop3.obj smtp.obj os_wce.obj \
+ dummywce.obj
+ erase $(CCLIENTLIB)
+ LIB /NOLOGO /OUT:cclient.lib \
+ mail.obj misc.obj flstring.obj netmsg.obj \
+ newsrc.obj rfc822.obj smanager.obj utf8.obj utf8aux.obj \
+ imap4r1.obj nntp.obj pop3.obj smtp.obj os_wce.obj \
+ dummywce.obj
+
+clean:
+ del *.lib *.obj linkage.* osdep.* auths.c *.exe *.exp || rem
+
+# A monument to a hack of long ago and far away...
+
+love:
+ @echo not war?
diff --git a/imap/src/osdep/wce/mkautaux.bat b/imap/src/osdep/wce/mkautaux.bat
new file mode 100644
index 00000000..c65022d2
--- /dev/null
+++ b/imap/src/osdep/wce/mkautaux.bat
@@ -0,0 +1,31 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Authenticator Linkage Generator auxillary for NT/Win9x
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 6 December 1995
+REM Last Edited:30 August 2006
+
+ECHO extern AUTHENTICATOR auth_%1; >> LINKAGE.H
+REM Note the introduction of the caret to quote the ampersand in NT
+if "%OS%" == "Windows_NT" ECHO auth_link (^&auth_%1); /* link in the %1 authenticator */ >> LINKAGE.C
+if "%OS%" == "" ECHO auth_link (&auth_%1); /* link in the %1 authenticator */ >> LINKAGE.C
+ECHO #include "auth_%1.c" >> AUTHS.C
diff --git a/imap/src/osdep/wce/mkauths.bat b/imap/src/osdep/wce/mkauths.bat
new file mode 100644
index 00000000..d8c5e360
--- /dev/null
+++ b/imap/src/osdep/wce/mkauths.bat
@@ -0,0 +1,33 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Authenticator Linkage Generator for DOS and Windows
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 6 December 1995
+REM Last Edited:30 August 2006
+
+REM Erase old authenticators list
+IF EXIST AUTHS.C DEL AUTHS.C
+
+REM Now define the new list
+FOR %%D IN (%1 %2 %3 %4 %5 %6 %7 %8 %9) DO CALL MKAUTAUX %%D
+
+EXIT 0
diff --git a/imap/src/osdep/wce/nl_wce.c b/imap/src/osdep/wce/nl_wce.c
new file mode 100644
index 00000000..47cb7f0a
--- /dev/null
+++ b/imap/src/osdep/wce/nl_wce.c
@@ -0,0 +1,61 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Windows/TOPS-20 newline routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Copy string with CRLF newlines
+ * Accepts: destination string
+ * pointer to size of destination string buffer
+ * source string
+ * length of source string
+ * Returns: length of copied string
+ */
+
+unsigned long strcrlfcpy (unsigned char **dst,unsigned long *dstl,
+ unsigned char *src,unsigned long srcl)
+{
+ /* flush destination buffer if too small */
+ if (*dst && (srcl > *dstl)) fs_give ((void **) dst);
+ if (!*dst) { /* make a new buffer if needed */
+ *dst = (char *) fs_get ((size_t) (*dstl = srcl) + 1);
+ if (dstl) *dstl = srcl; /* return new buffer length to main program */
+ }
+ /* copy strings */
+ if (srcl) memcpy (*dst,src,(size_t) srcl);
+ *(*dst + srcl) = '\0'; /* tie off destination */
+ return srcl; /* return length */
+}
+
+
+/* Length of string after strcrlfcpy applied
+ * Accepts: source string
+ * Returns: length of string
+ */
+
+unsigned long strcrlflen (STRING *s)
+{
+ return SIZE (s); /* no-brainer on DOS! */
+}
diff --git a/imap/src/osdep/wce/os_wce.c b/imap/src/osdep/wce/os_wce.c
new file mode 100644
index 00000000..7cf3ffe7
--- /dev/null
+++ b/imap/src/osdep/wce/os_wce.c
@@ -0,0 +1,45 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- WCE version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_wce.h" /* must be before osdep includes tcp.h */
+#include "mail.h"
+#include "osdep.h"
+#include <winsock.h>
+#include <stdio.h>
+#include <time.h>
+#include <sys\timeb.h>
+#include <fcntl.h>
+#include <sys\stat.h>
+#include "misc.h"
+
+#include "fs_wce.c"
+#include "ftl_wce.c"
+#include "nl_wce.c"
+#include "env_wce.c"
+#include "tcp_wce.c"
+#include "auths.c"
diff --git a/imap/src/osdep/wce/os_wce.h b/imap/src/osdep/wce/os_wce.h
new file mode 100644
index 00000000..45bd76a1
--- /dev/null
+++ b/imap/src/osdep/wce/os_wce.h
@@ -0,0 +1,53 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Operating-system dependent routines -- WCE version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 30 August 2006
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+ /* missing in string.h */
+_CRTIMP char * __cdecl strpbrk (const char *, const char *);
+_CRTIMP char * __cdecl strrchr(const char *, int);
+#include <sys\types.h>
+#undef ERROR
+#include <windows.h>
+#undef ERROR
+#define ERROR (long) 2 /* must match mail.h */
+#include <time.h>
+#include <io.h>
+
+#define gethostid clock
+#define WSA_VERSION ((1 << 8) | 1)
+
+#include "env_wce.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+
+#undef noErr
+#undef MAC
diff --git a/imap/src/osdep/wce/pmatch.c b/imap/src/osdep/wce/pmatch.c
new file mode 100644
index 00000000..95a0bb86
--- /dev/null
+++ b/imap/src/osdep/wce/pmatch.c
@@ -0,0 +1,89 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: IMAP Wildcard Matching Routines (case-independent)
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 June 2000
+ * Last Edited: 30 August 2006
+ */
+
+/* Wildcard pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if pattern matches base, else NIL
+ */
+
+long pmatch_full (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ /* % at end, OK if no inferiors */
+ if (!pat[1]) return (delim && strchr (s,delim)) ? NIL : T;
+ /* scan remainder of string until delimiter */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while ((*s != delim) && *s++);
+ break;
+ case '*': /* match 0 or more characters */
+ if (!pat[1]) return T; /* * at end, unconditional match */
+ /* scan remainder of string */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while (*s++);
+ break;
+ case '\0': /* end of pattern */
+ return *s ? NIL : T; /* success if also end of base */
+ default: /* match this character */
+ return compare_uchar (*pat,*s) ? NIL : pmatch_full (s+1,pat+1,delim);
+ }
+ return NIL;
+}
+
+/* Directory pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if base is a matching directory of pattern, else NIL
+ */
+
+long dmatch (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ if (!*s) return T; /* end of base means have a subset match */
+ if (!*++pat) return NIL; /* % at end, no inferiors permitted */
+ /* scan remainder of string until delimiter */
+ do if (dmatch (s,pat,delim)) return T;
+ while ((*s != delim) && *s++);
+ if (*s && !s[1]) return T; /* ends with delimiter, must be subset */
+ return dmatch (s,pat,delim);/* do new scan */
+ case '*': /* match 0 or more characters */
+ return T; /* unconditional match */
+ case '\0': /* end of pattern */
+ break;
+ default: /* match this character */
+ if (*s) return compare_uchar (*pat,*s) ? NIL : dmatch (s+1,pat+1,delim);
+ /* end of base, return if at delimiter */
+ else if (*pat == delim) return T;
+ break;
+ }
+ return NIL;
+}
diff --git a/imap/src/osdep/wce/setproto.bat b/imap/src/osdep/wce/setproto.bat
new file mode 100644
index 00000000..ce7cb1ef
--- /dev/null
+++ b/imap/src/osdep/wce/setproto.bat
@@ -0,0 +1,29 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Set default prototype for DOS/NT
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 9 October 1995
+REM Last Edited: 30 August 2006
+
+REM Set the default drivers
+ECHO #define CREATEPROTO %1proto >> LINKAGE.H
+ECHO #define APPENDPROTO %2proto >> LINKAGE.H
diff --git a/imap/src/osdep/wce/tcp_wce.c b/imap/src/osdep/wce/tcp_wce.c
new file mode 100644
index 00000000..90e206a9
--- /dev/null
+++ b/imap/src/osdep/wce/tcp_wce.c
@@ -0,0 +1,818 @@
+/* ========================================================================
+ * Copyright 1988-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Winsock TCP/IP routines
+ *
+ * Author: Mark Crispin from Mike Seibel's Winsock code
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 13 January 2008
+ */
+
+
+#define TCPMAXSEND 32768
+
+/* Private functions */
+
+int tcp_socket_open (struct sockaddr_in *sin,char *tmp,char *hst,
+ unsigned long port);
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd);
+long tcp_abort (TCPSTREAM *stream);
+long tcp_close_socket (SOCKET *sock);
+char *tcp_name (struct sockaddr_in *sin,long flag);
+char *tcp_name_valid (char *s);
+
+
+/* Private data */
+
+int wsa_initted = 0; /* init ? */
+static int wsa_sock_open = 0; /* keep track of open sockets */
+static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */
+static long ttmo_read = 0; /* TCP timeouts, in seconds */
+static long ttmo_write = 0;
+static long allowreversedns = T;/* allow reverse DNS lookup */
+static long tcpdebug = NIL; /* extra TCP debugging telemetry */
+
+/* TCP/IP manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tcp_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_TIMEOUT:
+ tmoh = (tcptimeout_t) value;
+ case GET_TIMEOUT:
+ ret = (void *) tmoh;
+ break;
+ case SET_READTIMEOUT:
+ ttmo_read = (long) value;
+ case GET_READTIMEOUT:
+ ret = (void *) ttmo_read;
+ break;
+ case SET_WRITETIMEOUT:
+ ttmo_write = (long) value;
+ case GET_WRITETIMEOUT:
+ ret = (void *) ttmo_write;
+ break;
+ case SET_ALLOWREVERSEDNS:
+ allowreversedns = (long) value;
+ case GET_ALLOWREVERSEDNS:
+ ret = (void *) allowreversedns;
+ break;
+ case SET_TCPDEBUG:
+ tcpdebug = (long) value;
+ case GET_TCPDEBUG:
+ ret = (void *) tcpdebug;
+ break;
+ }
+ return ret;
+}
+
+/* TCP/IP open
+ * Accepts: host name
+ * contact service name
+ * contact port number and optional silent flag
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream = NIL;
+ int i;
+ SOCKET sock = INVALID_SOCKET;
+ int silent = (port & NET_SILENT) ? T : NIL;
+ char *s;
+ struct sockaddr_in sin;
+ struct hostent *he;
+ char hostname[MAILTMPLEN];
+ char tmp[MAILTMPLEN];
+ struct servent *sv = NIL;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (!wsa_initted++) { /* init Windows Sockets */
+ WSADATA wsock;
+ if (i = (int) WSAStartup (WSA_VERSION,&wsock)) {
+ wsa_initted = 0; /* in case we try again */
+ sprintf (tmp,"Unable to start Windows Sockets (%d)",i);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ }
+ port &= 0xffff; /* erase flags */
+ /* lookup service */
+ if (service && (sv = getservbyname (service,"tcp")))
+ port = ntohs (sin.sin_port = sv->s_port);
+ /* copy port number in network format */
+ else sin.sin_port = htons ((u_short) port);
+ /* The domain literal form is used (rather than simply the dotted decimal
+ as with other Windows programs) because it has to be a valid "host name"
+ in mailsystem terminology. */
+ sin.sin_family = AF_INET; /* family is always Internet */
+ /* look like domain literal? */
+ if (host[0] == '[' && host[(strlen (host))-1] == ']') {
+ strcpy (tmp,host+1); /* yes, copy number part */
+ tmp[strlen (tmp)-1] = '\0';
+ if ((sin.sin_addr.s_addr = inet_addr (tmp)) == INADDR_NONE) {
+ sprintf (tmp,"Bad format domain-literal: %.80s",host);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ else {
+ sin.sin_family = AF_INET; /* family is always Internet */
+ strcpy (hostname,host);
+ (*bn) (BLOCK_TCPOPEN,NIL);
+ sock = tcp_socket_open (&sin,tmp,hostname,port);
+ (*bn) (BLOCK_NONE,NIL);
+ }
+ }
+
+ else { /* lookup host name */
+ if (tcpdebug) {
+ sprintf (tmp,"DNS resolution %.80s",host);
+ mm_log (tmp,TCPDEBUG);
+ }
+ (*bn) (BLOCK_DNSLOOKUP,NIL);/* look up name */
+ if (!(he = gethostbyname (lcase (strcpy (tmp,host)))))
+ sprintf (tmp,"Host not found (#%d): %s",WSAGetLastError(),host);
+ (*bn) (BLOCK_NONE,NIL);
+ if (he) { /* DNS resolution won? */
+ if (tcpdebug) mm_log ("DNS resolution done",TCPDEBUG);
+ /* copy address type */
+ sin.sin_family = he->h_addrtype;
+ /* copy host name */
+ strcpy (hostname,he->h_name);
+ wsa_sock_open++; /* prevent tcp_close_socket() from freeing in
+ loop */
+ for (i = 0; (sock == INVALID_SOCKET) && (s = he->h_addr_list[i]); i++) {
+ if (i && !silent) mm_log (tmp,WARN);
+ memcpy (&sin.sin_addr,s,he->h_length);
+ (*bn) (BLOCK_TCPOPEN,NIL);
+ sock = tcp_socket_open (&sin,tmp,hostname,port);
+ (*bn) (BLOCK_NONE,NIL);
+ }
+ wsa_sock_open--; /* undo protection */
+ }
+ }
+ if (sock == INVALID_SOCKET) { /* error? */
+ if (!silent) mm_log (tmp,ERROR);
+ tcp_close_socket (&sock); /* do possible cleanup action */
+ }
+ else { /* got a socket, create TCP/IP stream */
+ stream = (TCPSTREAM *) memset (fs_get (sizeof (TCPSTREAM)),0,
+ sizeof (TCPSTREAM));
+ stream->port = port; /* port number */
+ /* init socket */
+ stream->tcpsi = stream->tcpso = sock;
+ stream->ictr = 0; /* init input counter */
+ /* copy official host name */
+ stream->host = cpystr (hostname);
+ if (tcpdebug) mm_log ("Stream open and ready for read",TCPDEBUG);
+ }
+ return stream; /* return success */
+}
+
+/* Open a TCP socket
+ * Accepts: Internet socket address block
+ * scratch buffer
+ * host name for error message
+ * port number for error message
+ * Returns: socket if success, else -1 with error string in scratch buffer
+ */
+
+int tcp_socket_open (struct sockaddr_in *sin,char *tmp,char *hst,
+ unsigned long port)
+{
+ int sock;
+ char *s;
+ sprintf (tmp,"Trying IP address [%s]",inet_ntoa (sin->sin_addr));
+ mm_log (tmp,NIL);
+ /* get a TCP stream */
+ if ((sock = socket (sin->sin_family,SOCK_STREAM,0)) == INVALID_SOCKET) {
+ sprintf (tmp,"Unable to create TCP socket (%d)",WSAGetLastError());
+ return -1;
+ }
+ wsa_sock_open++; /* count this socket as open */
+ /* open connection */
+ if (connect (sock,(struct sockaddr *) sin,sizeof (struct sockaddr_in)) ==
+ SOCKET_ERROR) {
+ switch (WSAGetLastError ()){/* analyze error */
+ case WSAECONNREFUSED:
+ s = "Refused";
+ break;
+ case WSAENOBUFS:
+ s = "Insufficient system resources";
+ break;
+ case WSAETIMEDOUT:
+ s = "Timed out";
+ break;
+ case WSAEHOSTUNREACH:
+ s = "Host unreachable";
+ break;
+ default:
+ s = "Unknown error";
+ break;
+ }
+ sprintf (tmp,"Can't connect to %.80s,%ld: %s (%d)",hst,port,s,
+ WSAGetLastError ());
+ tcp_close_socket (&sock); /* flush socket */
+ sock = INVALID_SOCKET;
+ }
+ return sock; /* return the socket */
+}
+
+/* TCP/IP authenticated open
+ * Accepts: NETMBX specifier
+ * service name
+ * returned user name buffer
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL; /* always NIL on Windows */
+}
+
+/* TCP receive line
+ * Accepts: TCP stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *tcp_getline (TCPSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = tcp_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = tcp_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* TCP receive line or partial line
+ * Accepts: TCP stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!tcp_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!tcp_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* TCP/IP receive buffer
+ * Accepts: TCP/IP stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *s)
+{
+ unsigned long n;
+ /* make sure socket still alive */
+ if (stream->tcpsi == INVALID_SOCKET) return NIL;
+ /* can transfer bytes from buffer? */
+ if (n = min (size,stream->ictr)) {
+ memcpy (s,stream->iptr,n); /* yes, slurp as much as we can from it */
+ s += n; /* update pointer */
+ stream->iptr +=n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -=n;
+ }
+ if (size) {
+ int i;
+ fd_set fds;
+ struct timeval tmo;
+ time_t tc,t = time (0);
+ blocknotify_t bn=(blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ (*bn) (BLOCK_TCPREAD,NIL);
+ while (size > 0) { /* until request satisfied */
+ time_t tl = time (0);
+ if (tcpdebug) mm_log ("Reading TCP buffer",TCPDEBUG);
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_SET (stream->tcpsi,&fds);/* set bit in selection vector */
+ tmo.tv_sec = ttmo_read;
+ tmo.tv_usec = 0;
+ /* block and read */
+ switch ((stream->tcpsi == stream->tcpso) ?
+ select (stream->tcpsi+1,&fds,0,0,
+ ttmo_read ? &tmo : (struct timeval *) 0) : 1) {
+ case SOCKET_ERROR: /* error */
+ if (WSAGetLastError () != WSAEINTR) return tcp_abort (stream);
+ break;
+ case 0: /* timeout */
+ tc = time (0);
+ if (tmoh && ((*tmoh) (tc - t,tc - tl))) break;
+ return tcp_abort (stream);
+ default:
+ if (stream->tcpsi == stream->tcpso)
+ while (((i = recv (stream->tcpsi,s,(int) min (maxposint,size),0)) ==
+ SOCKET_ERROR) && (WSAGetLastError () == WSAEINTR));
+ else while (((i = read (stream->tcpsi,s,(int) min (maxposint,size))) <
+ 0) && (errno == EINTR));
+ switch (i) {
+ case SOCKET_ERROR: /* error */
+ case 0: /* no data read */
+ return tcp_abort (stream);
+ default:
+ s += i; /* point at new place to write */
+ size -= i; /* reduce byte count */
+ if (tcpdebug) mm_log ("Successfully read TCP buffer",TCPDEBUG);
+ }
+ }
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ }
+ *s = '\0'; /* tie off string */
+ return T;
+}
+
+/* TCP/IP receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getdata (TCPSTREAM *stream)
+{
+ struct timeval tmo;
+ int i;
+ fd_set fds;
+ time_t tc,t = time (0);
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ FD_ZERO (&fds); /* initialize selection vector */
+ if (stream->tcpsi == INVALID_SOCKET) return NIL;
+ (*bn) (BLOCK_TCPREAD,NIL);
+ tmo.tv_sec = ttmo_read;
+ tmo.tv_usec = 0;
+ while (stream->ictr < 1) { /* if nothing in the buffer */
+ time_t tl = time (0);
+ if (tcpdebug) mm_log ("Reading TCP data",TCPDEBUG);
+ FD_SET (stream->tcpsi,&fds);/* set bit in selection vector */
+ /* block and read */
+ switch ((stream->tcpsi == stream->tcpso) ?
+ select (stream->tcpsi+1,&fds,0,0,
+ ttmo_read ? &tmo : (struct timeval *) 0) : 1) {
+ case SOCKET_ERROR: /* error */
+ if (WSAGetLastError () != WSAEINTR) return tcp_abort (stream);
+ break;
+ case 0: /* timeout */
+ tc = time (0);
+ if (tmoh && ((*tmoh) (tc - t,tc - tl))) break;
+ return tcp_abort (stream);
+ default:
+ if (stream->tcpsi == stream->tcpso)
+ while (((i = recv (stream->tcpsi,stream->ibuf,BUFLEN,0)) ==
+ SOCKET_ERROR) && (WSAGetLastError () == WSAEINTR));
+ else while (((i = read (stream->tcpsi,stream->ibuf,BUFLEN)) < 0) &&
+ (errno == EINTR));
+ switch (i) {
+ case SOCKET_ERROR: /* error */
+ case 0: /* no data read */
+ return tcp_abort (stream);
+ default:
+ stream->ictr = i; /* set new byte count */
+ /* point at TCP buffer */
+ stream->iptr = stream->ibuf;
+ if (tcpdebug) mm_log ("Successfully read TCP data",TCPDEBUG);
+ }
+ }
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return T;
+}
+
+/* TCP/IP send string as record
+ * Accepts: TCP/IP stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long tcp_soutr (TCPSTREAM *stream,char *string)
+{
+ return tcp_sout (stream,string,(unsigned long) strlen (string));
+}
+
+
+/* TCP/IP send string
+ * Accepts: TCP/IP stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
+{
+ int i;
+ struct timeval tmo;
+ fd_set fds;
+ time_t tc,t = time (0);
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ tmo.tv_sec = ttmo_write;
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ if (stream->tcpso == INVALID_SOCKET) return NIL;
+ (*bn) (BLOCK_TCPWRITE,NIL);
+ while (size > 0) { /* until request satisfied */
+ time_t tl = time (0);
+ if (tcpdebug) mm_log ("Writing to TCP",TCPDEBUG);
+ FD_SET (stream->tcpso,&fds);/* set bit in selection vector */
+ /* block and write */
+ switch ((stream->tcpsi == stream->tcpso) ?
+ select (stream->tcpso+1,NULL,&fds,NULL,
+ tmo.tv_sec ? &tmo : (struct timeval *) 0) : 1) {
+ case SOCKET_ERROR: /* error */
+ if (WSAGetLastError () != WSAEINTR) return tcp_abort (stream);
+ break;
+ case 0: /* timeout */
+ tc = time (0);
+ if (tmoh && ((*tmoh) (tc - t,tc - tl))) break;
+ return tcp_abort (stream);
+ default:
+ if (stream->tcpsi == stream->tcpso)
+ while (((i = send (stream->tcpso,string,
+ (int) min (size,TCPMAXSEND),0)) == SOCKET_ERROR) &&
+ (WSAGetLastError () == WSAEINTR));
+ else while (((i = write (stream->tcpso,string,
+ min (size,TCPMAXSEND))) < 0) &&
+ (errno == EINTR));
+ if (i == SOCKET_ERROR) return tcp_abort (stream);
+ size -= i; /* count this size */
+ if (tcpdebug) mm_log ("successfully wrote to TCP",TCPDEBUG);
+ string += i;
+ }
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return T; /* all done */
+}
+
+
+/* TCP/IP close
+ * Accepts: TCP/IP stream
+ */
+
+void tcp_close (TCPSTREAM *stream)
+{
+ tcp_abort (stream); /* nuke the sockets */
+ /* flush host names */
+ if (stream->host) fs_give ((void **) &stream->host);
+ if (stream->remotehost) fs_give ((void **) &stream->remotehost);
+ if (stream->localhost) fs_give ((void **) &stream->localhost);
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+
+/* TCP/IP abort sockets
+ * Accepts: TCP/IP stream
+ * Returns: NIL, always
+ */
+
+long tcp_abort (TCPSTREAM *stream)
+{
+ if (stream->tcpsi != stream->tcpso) tcp_close_socket (&stream->tcpso);
+ else stream->tcpso = INVALID_SOCKET;
+ return tcp_close_socket (&stream->tcpsi);
+}
+
+
+/* TCP/IP abort stream
+ * Accepts: WinSock socket
+ * Returns: NIL, always
+ */
+
+long tcp_close_socket (SOCKET *sock)
+{
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ /* something to close? */
+ if (sock && (*sock != INVALID_SOCKET)) {
+ (*bn) (BLOCK_TCPCLOSE,NIL);
+ closesocket (*sock); /* WinSock socket close */
+ *sock = INVALID_SOCKET;
+ (*bn) (BLOCK_NONE,NIL);
+ wsa_sock_open--; /* drop this socket */
+ }
+ /* no more open streams? */
+ if (wsa_initted && !wsa_sock_open) {
+ mm_log ("Winsock cleanup",NIL);
+ wsa_initted = 0; /* no more sockets, so... */
+ WSACleanup (); /* free up resources until needed */
+ }
+ return NIL;
+}
+
+/* TCP/IP get host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_host (TCPSTREAM *stream)
+{
+ return stream->host; /* use tcp_remotehost() if want guarantees */
+}
+
+
+/* TCP/IP get remote host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_remotehost (TCPSTREAM *stream)
+{
+ if (!stream->remotehost) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ stream->remotehost = /* get socket's peer name */
+ ((getpeername (stream->tcpsi,(struct sockaddr *) &sin,&sinlen) ==
+ SOCKET_ERROR) || (sinlen <= 0)) ?
+ cpystr (stream->host) : tcp_name (&sin,NIL);
+ }
+ return stream->remotehost;
+}
+
+
+/* TCP/IP return port for this stream
+ * Accepts: TCP/IP stream
+ * Returns: port number for this stream
+ */
+
+unsigned long tcp_port (TCPSTREAM *stream)
+{
+ return stream->port; /* return port number */
+}
+
+
+/* TCP/IP get local host name
+ * Accepts: TCP/IP stream
+ * Returns: local host name
+ */
+
+char *tcp_localhost (TCPSTREAM *stream)
+{
+ if (!stream->localhost) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ stream->localhost = /* get socket's name */
+ ((stream->port & 0xffff000) ||
+ ((getsockname (stream->tcpsi,(struct sockaddr *) &sin,&sinlen) ==
+ SOCKET_ERROR) || (sinlen <= 0))) ?
+ cpystr (mylocalhost ()) : tcp_name (&sin,NIL);
+ }
+ return stream->localhost; /* return local host name */
+}
+
+/* TCP/IP get client host address (server calls only)
+ * Returns: client host address
+ */
+
+char *tcp_clientaddr ()
+{
+ if (!myClientAddr) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ myClientAddr = /* get stdin's peer name */
+ ((getpeername (0,(struct sockaddr *) &sin,&sinlen) == SOCKET_ERROR) ||
+ (sinlen <= 0)) ? cpystr ("UNKNOWN") : cpystr (inet_ntoa (sin.sin_addr));
+ }
+ return myClientAddr;
+}
+
+
+/* TCP/IP get client host name (server calls only)
+ * Returns: client host name
+ */
+
+char *tcp_clienthost ()
+{
+ if (!myClientHost) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ myClientHost = /* get stdin's peer name */
+ ((getpeername (0,(struct sockaddr *) &sin,&sinlen) == SOCKET_ERROR) ||
+ (sinlen <= 0)) ? cpystr ("UNKNOWN") : tcp_name (&sin,T);
+ }
+ return myClientHost;
+}
+
+/* TCP/IP get server host address (server calls only)
+ * Returns: server host address
+ */
+
+char *tcp_serveraddr ()
+{
+ if (!myServerAddr) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ myServerAddr = /* get stdin's peer name */
+ ((getsockname (0,(struct sockaddr *) &sin,&sinlen) == SOCKET_ERROR) ||
+ (sinlen <= 0)) ? cpystr ("UNKNOWN") : cpystr (inet_ntoa (sin.sin_addr));
+ }
+ return myServerAddr;
+}
+
+
+/* TCP/IP get server host name (server calls only)
+ * Returns: server host name
+ */
+
+static long myServerPort = -1;
+
+char *tcp_serverhost ()
+{
+ if (!myServerHost) {
+ struct sockaddr_in sin;
+ int sinlen = sizeof (struct sockaddr_in);
+ if (!wsa_initted++) { /* init Windows Sockets */
+ WSADATA wsock;
+ if (WSAStartup (WSA_VERSION,&wsock)) {
+ wsa_initted = 0;
+ return "random-pc"; /* try again later? */
+ }
+ }
+ /* get stdin's name */
+ if ((getsockname (0,(struct sockaddr *) &sin,&sinlen) == SOCKET_ERROR) ||
+ (sinlen <= 0)) myServerHost = cpystr (mylocalhost ());
+ else {
+ myServerHost = tcp_name (&sin,NIL);
+ myServerPort = ntohs (sin.sin_port);
+ }
+ }
+ return myServerHost;
+}
+
+
+/* TCP/IP get server port number (server calls only)
+ * Returns: server port number
+ */
+
+long tcp_serverport ()
+{
+ if (!myServerHost) tcp_serverhost ();
+ return myServerPort;
+}
+
+/* TCP/IP return canonical form of host name
+ * Accepts: host name
+ * Returns: canonical form of host name
+ */
+
+char *tcp_canonical (char *name)
+{
+ char *ret,host[MAILTMPLEN];
+ struct hostent *he;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ /* look like domain literal? */
+ if (name[0] == '[' && name[strlen (name) - 1] == ']') return name;
+ (*bn) (BLOCK_DNSLOOKUP,NIL);
+ if (tcpdebug) {
+ sprintf (host,"DNS canonicalization %.80s",name);
+ mm_log (host,TCPDEBUG);
+ }
+ /* note that NT requires lowercase! */
+ ret = (he = gethostbyname (lcase (strcpy (host,name)))) ? he->h_name : name;
+ (*bn) (BLOCK_NONE,NIL);
+ if (tcpdebug) mm_log ("DNS canonicalization done",TCPDEBUG);
+ return ret;
+}
+
+
+/* TCP/IP return name from socket
+ * Accepts: socket
+ * verbose flag
+ * Returns: cpystr name
+ */
+
+char *tcp_name (struct sockaddr_in *sin,long flag)
+{
+ char *ret,*t,adr[MAILTMPLEN],tmp[MAILTMPLEN];
+ sprintf (ret = adr,"[%.80s]",inet_ntoa (sin->sin_addr));
+ if (allowreversedns) {
+ struct hostent *he;
+ blocknotify_t bn = (blocknotify_t)mail_parameters(NIL,GET_BLOCKNOTIFY,NIL);
+ void *data;
+ if (tcpdebug) {
+ sprintf (tmp,"Reverse DNS resolution %s",adr);
+ mm_log (tmp,TCPDEBUG);
+ }
+ (*bn) (BLOCK_DNSLOOKUP,NIL);/* quell alarms */
+ data = (*bn) (BLOCK_SENSITIVE,NIL);
+ /* translate address to name */
+ if (t = tcp_name_valid ((he = gethostbyaddr ((char *) &sin->sin_addr,
+ sizeof (struct in_addr),
+ sin->sin_family)) ?
+ (char *) he->h_name : NIL)) {
+ /* produce verbose form if needed */
+ if (flag) sprintf (ret = tmp,"%s %s",t,adr);
+ else ret = t;
+ }
+ (*bn) (BLOCK_NONSENSITIVE,data);
+ (*bn) (BLOCK_NONE,NIL); /* alarms OK now */
+ if (tcpdebug) mm_log ("Reverse DNS resolution done",TCPDEBUG);
+ }
+ return cpystr (ret);
+}
+
+/* Return my local host name
+ * Returns: my local host name
+ */
+
+char *mylocalhost (void)
+{
+ if (!myLocalHost) {
+ char tmp[MAILTMPLEN];
+ if (!wsa_initted++) { /* init Windows Sockets */
+ WSADATA wsock;
+ if (WSAStartup (WSA_VERSION,&wsock)) {
+ wsa_initted = 0;
+ return "random-pc"; /* try again later? */
+ }
+ }
+ myLocalHost = cpystr ((gethostname (tmp,MAILTMPLEN-1) == SOCKET_ERROR) ?
+ "random-pc" : tcp_canonical (tmp));
+ }
+ return myLocalHost;
+}
+
+
+/* Validate name
+ * Accepts: domain name
+ * Returns: T if valid, NIL otherwise
+ */
+
+char *tcp_name_valid (char *s)
+{
+ int c;
+ char *ret,*tail;
+ /* must be non-empty and not too long */
+ if ((ret = (s && *s) ? s : NIL) && (tail = ret + NETMAXHOST)) {
+ /* must be alnum, dot, or hyphen */
+ while ((c = *s++) && (s <= tail) &&
+ (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) ||
+ ((c >= '0') && (c <= '9')) || (c == '-') || (c == '.')));
+ if (c) ret = NIL;
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/wce/tcp_wce.h b/imap/src/osdep/wce/tcp_wce.h
new file mode 100644
index 00000000..4019af10
--- /dev/null
+++ b/imap/src/osdep/wce/tcp_wce.h
@@ -0,0 +1,46 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Winsock TCP/IP routines
+ *
+ * Author: Mike Seibel from Unix version by Mark Crispin
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+/* TCP input buffer -- must be large enough to prevent overflow */
+
+#define BUFLEN 8192
+
+
+/* TCP I/O stream (must be before osdep.h is included) */
+
+#define TCPSTREAM struct tcp_stream
+TCPSTREAM {
+ char *host; /* host name */
+ char *remotehost; /* remote host name */
+ unsigned long port; /* port number */
+ char *localhost; /* local host name */
+ int tcpsi; /* input tcp socket */
+ int tcpso; /* output tcp socket */
+ long ictr; /* input counter */
+ char *iptr; /* input pointer */
+ char ibuf[BUFLEN]; /* input buffer */
+};
diff --git a/imap/src/tmail/Makefile b/imap/src/tmail/Makefile
new file mode 100644
index 00000000..ce221430
--- /dev/null
+++ b/imap/src/tmail/Makefile
@@ -0,0 +1,53 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+
+# Program: tmail Makefile
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 5 April 1993
+# Last Edited: 10 September 2007
+
+
+C = ../c-client
+CCLIENTLIB = $C/c-client.a
+SHELL = /bin/sh
+
+# Get local definitions from c-client directory
+
+CC = `cat $C/CCTYPE`
+CFLAGS = -I$C `cat $C/CFLAGS`
+LDFLAGS = $(CCLIENTLIB) `cat $C/LDFLAGS`
+
+tmail: $(CCLIENTLIB) tmail.o tquota.o
+ $(CC) $(CFLAGS) -o tmail tmail.o tquota.o $(LDFLAGS)
+
+tmail.o: $C/mail.h $C/misc.h $C/osdep.h tquota.h
+
+tquota.o: tquota.h
+
+$(CCLIENTLIB):
+ cd $C;make
+
+clean:
+ rm -f *.o tmail
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo 'not war?'
diff --git a/imap/src/tmail/tmail.1 b/imap/src/tmail/tmail.1
new file mode 100644
index 00000000..a34d4bb9
--- /dev/null
+++ b/imap/src/tmail/tmail.1
@@ -0,0 +1,205 @@
+.ig
+ * ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+..
+.TH TMAIL 1 "September 27, 2007"
+.SH NAME
+tmail \- Mail Delivery Module
+.nh
+.SH SYNOPSIS
+.B tmail
+.I [-b format] [\-D] [-f from_name] [\-I inbox_specifier] user[+folder] ...
+.SH DESCRIPTION
+.I tmail
+delivers mail to a user's INBOX or a designated folder.
+.I tmail
+may be configured as a drop-in replacement for
+.IR binmail (1),
+.IR mail.local (1)
+or any program intended for use for mail delivery by a mail delivery program
+such as
+.IR sendmail (8).
+.PP
+.I tmail
+is intended to be used for direct delivery by the mailer daemon;
+.IR dmail (1)
+is the preferred tool for user applications, e.g. a mail delivery
+filter such as
+.IR procmail (1) .
+If
+.I tmail
+is used for a user application,
+then the calling program must be aware of the restrictions noted below.
+.PP
+When
+.I tmail
+exits, it returns exit status values to enable the mail delivery program
+to determine whether a message was delivered successfully or had a
+temporary (requeue for later delivery) or permanent (return to sender)
+failure.
+.PP
+If the
+.I +folder
+extension is included in the user argument,
+.I tmail
+will attempt to deliver to the designated folder. If the folder does not
+exist or the extension is not included, the message is delivered to the
+user's INBOX.
+If delivery is to INBOX and no INBOX currently exists,
+.I tmail
+will create a new INBOX, using the \fB-I\fR or \fB-b\fR flag if specified.
+.I tmail
+recognizes the format of an existing INBOX or folder, and appends the new
+message in that format.
+.PP
+The \fB-b\fR flag specifies a format to create INBOX if INBOX does not
+already exist. This flag requires privileges, and can not be used with
+\fB-I\fR. The argument is
+a format name such as mix, mbx, etc.
+.PP
+The \fB-D\fR flag specifies debugging; this enables additional message
+telemetry.
+.PP
+The \fB-f\fR or \fB-r\fR flag is used by
+the mail delivery program to specify a Return-Path. The header
+.br
+ Return-Path: <\fIfrom_name\fR>
+.br
+is prepended to the message before delivery.
+.PP
+The \fB-I\fR flag is used by the mail delivery program
+to specify an alternative INBOX name. This flag requires privileges,
+and can not be used with \fB-b\fR. This affects the location and format
+of INBOX. If specified, it should be in one of three forms:
+.sp
+The first form of argument to \fB-I\fR is the string "INBOX", which
+means to write to the system default inbox using the system default
+mailbox format. These system defaults are defined when the c-client
+library is built.
+.sp
+The second form of argument to \fB-I\fR is a delivery specification,
+consisting of "#driver.", a c-client mailbox format driver name, "/",
+and a file name. This will write to the specified file in the
+specified format. For example, #driver.mbx/INBOX will write to file
+"INBOX" in the home directory in mbx format; and
+#driver.unix/mail/incoming will write to file "incoming" in the
+user's "mail" subdirectory in unix (default UNIX) format.
+.sp
+The third form of argument to \fB-I\fR is any other name. Normally,
+this will write to the specified file on the user's home directory in
+the specified format. However, certain names are special. These are:
+.PP
+.nf
+ value equivalant to
+ ----- -------------
+ INBOX.MTX #driver.mtx/INBOX.MTX
+ mbox #driver.unix/mbox
+ mail.txt #driver.tenex/mail.txt
+.fi
+.PP
+If \fB-I\fR is not specified, the default action is \fB-I INBOX\fR.
+.PP
+If multiple recipients are specified on the command line,
+.I tmail
+spawns one child process per recipient to perform actual delivery. This
+way of calling
+.I tmail
+is not recommended; see below under
+.IR RESTRICTIONS .
+.SH INSTALLATION
+If
+.I tmail
+is to be used for mail delivery from the mail delivery program, it
+.I must
+be installed setuid root.
+.sp
+If sendmail is the mail delivery program,
+.I tmail
+is invoked from sendmail.cf. Look for the "Mlocal" line, and substitute
+the path name for the
+.I tmail
+binary in place of /bin/mail, /usr/lib/mail.local, etc. You should also
+add the flag to invoke
+.I tmail
+with CRLF style newlines; this is usually done with E=\\r\\n in the Mlocal
+line.
+.sp
+Here is an example of an Mlocal line in sendmail version 8:
+.sp
+.nf
+Mlocal, P=/usr/local/etc/tmail, F=lsDFMAw5:/|@qPrn+,
+ S=10/30, R=20/40, E=\\r\\n, T=DNS/RFC822/X-Unix,
+ A=tmail $u
+.fi
+.PP
+If
+.I tmail
+is to be called with the \fB-I\fR flag, it must be invoked with both
+real and effective UID root. Many sendmail configurations invoke the
+local mailer as the sending user when that user is local, which
+will prevent \fB-b\fR or \fB-I\fR from working.
+.SH SECURITY CONSIDERATIONS
+If
+.I tmail
+is invoked by an ordinary user, the Received: header line will
+indicate the name or UID of the user that invoked it.
+.PP
+Ordinary users are not permitted to use the \fB-b\fR or \fB-I\fR flag since
+otherwise a user could create any file on another user's directory.
+.PP
+.I tmail
+can deliver mail to home directories. In addition,
+.I tmail
+can be used to deliver mail to other mail folders in a home directory
+or an inferior directory of a home directory.
+.SH RESTRICTIONS
+The calling program should invoke
+.I tmail
+with CRLF newlines, otherwise
+.I tmail
+will complain in syslog.
+.PP
+Absolute pathnames and
+.I ~user
+specifications are not permitted in
+.I +folder
+extensions.
+.PP
+Ordinary users are not permitted to use the \fB-I\fR flag.
+.PP
+IMAP4 namespace names are not yet supported in
+.I +folder
+extensions.
+.PP
+It is not possible to use
+.I tmail
+to deliver to
+.IR mh (1)
+format mailboxes.
+.PP
+If delivery to multiple users is specified and delivery to any single user
+fails, the entire delivery will be reported as having failed, even though
+delivery to other users may have succeeded. If
+.I tmail
+is used for mail delivery from
+.IR sendmail (8),
+a separate tmail invocation should be done for each user. Otherwise a
+delivery failure for a single user in a message going to multiple users
+will cause multiple deliveries to all the other users every time
+.IR sendmail (8),
+retries.
+.SH AUTHOR
+Mark Crispin, MRC@CAC.Washington.EDU
+.SH "SEE ALSO"
+binmail(1)
+.br
+sendmail(8)
diff --git a/imap/src/tmail/tmail.c b/imap/src/tmail/tmail.c
new file mode 100644
index 00000000..ed5fc589
--- /dev/null
+++ b/imap/src/tmail/tmail.c
@@ -0,0 +1,800 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Mail Delivery Module
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 5 April 1993
+ * Last Edited: 30 October 2008
+ */
+
+#include <stdio.h>
+#include <pwd.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <sysexits.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include "c-client.h"
+#include "tquota.h"
+
+
+/* Globals */
+
+char *version = "22"; /* tmail edit version */
+int debug = NIL; /* debugging (don't fork) */
+int trycreate = NIL; /* flag saying gotta create before appending */
+int critical = NIL; /* flag saying in critical code */
+char *sender = NIL; /* message origin */
+char *inbox = NIL; /* inbox file */
+long precedence = 0; /* delivery precedence - used by quota hook */
+DRIVER *format = NIL; /* desired format */
+
+
+/* Function prototypes */
+
+void file_string_init (STRING *s,void *data,unsigned long size);
+char file_string_next (STRING *s);
+void file_string_setpos (STRING *s,unsigned long i);
+int main (int argc,char *argv[]);
+int deliver (FILE *f,unsigned long msglen,char *user);
+long ibxpath (MAILSTREAM *ds,char **mailbox,char *path);
+int deliver_safely (MAILSTREAM *prt,STRING *st,char *mailbox,char *path,
+ uid_t uid,char *tmp);
+int delivery_unsafe (char *path,uid_t uid,struct stat *sbuf,char *tmp);
+int fail (char *string,int code);
+char *getusername (char *s,char **t);
+
+
+/* File string driver for file stringstructs */
+
+STRINGDRIVER file_string = {
+ file_string_init, /* initialize string structure */
+ file_string_next, /* get next byte in string structure */
+ file_string_setpos /* set position in string structure */
+};
+
+
+/* Cache buffer for file stringstructs */
+
+#define CHUNKLEN 16384
+char chunk[CHUNKLEN];
+
+/* Initialize file string structure for file stringstruct
+ * Accepts: string structure
+ * pointer to string
+ * size of string
+ */
+
+void file_string_init (STRING *s,void *data,unsigned long size)
+{
+ s->data = data; /* note fd */
+ s->size = size; /* note size */
+ s->chunk = chunk;
+ s->chunksize = (unsigned long) CHUNKLEN;
+ SETPOS (s,0); /* set initial position */
+}
+
+
+/* Get next character from file stringstruct
+ * Accepts: string structure
+ * Returns: character, string structure chunk refreshed
+ */
+
+char file_string_next (STRING *s)
+{
+ char c = *s->curpos++; /* get next byte */
+ SETPOS (s,GETPOS (s)); /* move to next chunk */
+ return c; /* return the byte */
+}
+
+
+/* Set string pointer position for file stringstruct
+ * Accepts: string structure
+ * new position
+ */
+
+void file_string_setpos (STRING *s,unsigned long i)
+{
+ if (i > s->size) i = s->size; /* don't permit setting beyond EOF */
+ s->offset = i; /* set new offset */
+ s->curpos = s->chunk; /* reset position */
+ /* set size of data */
+ if (s->cursize = min (s->chunksize,SIZE (s))) {
+ /* move to that position in the file */
+ fseek ((FILE *) s->data,s->offset,SEEK_SET);
+ fread (s->curpos,sizeof (char),(unsigned int) s->cursize,(FILE *) s->data);
+ }
+}
+
+/* Main program */
+
+int main (int argc,char *argv[])
+{
+ FILE *f = NIL;
+ int pid,c,ret = 0;
+ unsigned long msglen,status = 0;
+ char *s,tmp[MAILTMPLEN];
+ uid_t ruid = getuid ();
+ struct passwd *pwd;
+ openlog ("tmail",LOG_PID,LOG_MAIL);
+#include "linkage.c"
+ /* make sure have some arguments */
+ if (--argc < 1) _exit (fail ("usage: tmail [-D] user[+folder]",EX_USAGE));
+ /* process all flags */
+ while (argc && (*(s = *++argv)) == '-') {
+ argc--; /* gobble this argument */
+ switch (s[1]) { /* what is this flag? */
+ case 'D': /* debug */
+ debug = T; /* don't fork */
+ break;
+ case 'I': /* inbox specifier */
+ if (inbox || format) _exit (fail ("duplicate -b or -I",EX_USAGE));
+ if (argc--) inbox = cpystr (*++argv);
+ else _exit (fail ("missing argument to -I",EX_USAGE));
+ break;
+ case 'f': /* new name for this flag */
+ case 'r': /* flag giving return path */
+ if (sender) _exit (fail ("duplicate -f or -r",EX_USAGE));
+ if (argc--) sender = cpystr (*++argv);
+ else _exit (fail ("missing argument to -f or -r",EX_USAGE));
+ break;
+ case 'b': /* create INBOX in this format */
+ if (inbox || format) _exit (fail ("duplicate -b or -I",EX_USAGE));
+ if (!argc--) _exit (fail ("missing argument to -b",EX_USAGE));
+ if (!(format = mail_parameters (NIL,GET_DRIVER,*++argv)))
+ _exit (fail ("unknown format to -b",EX_USAGE));
+ else if (!(format->flags & DR_LOCAL) ||
+ !compare_cstring (format->name,"dummy"))
+ _exit (fail ("invalid format to -b",EX_USAGE));
+ break;
+ /* following flags are undocumented */
+ case 'p': /* precedence for quota */
+ if (s[2] && ((s[2] == '-') || isdigit (s[2]))) precedence = atol (s + 2);
+ else if (argc-- && ((*(s = *++argv) == '-') || isdigit (*s)))
+ precedence = atol (s);
+ else _exit (fail ("missing argument to -p",EX_USAGE));
+ break;
+ case 'd': /* obsolete flag meaning multiple users */
+ break; /* ignore silently */
+ /* -s has been deprecated and replaced by the -s and -k flags in dmail.
+ * dmail's -k flag does what -s once did in tmail; dmail's -s flag
+ * takes no argument and just sets \Seen. Flag setting is more properly
+ * done in dmail which runs as the user and is clearly at the user's
+ * behest. Since tmail runs privileged, -s would have to be disabled
+ * unless the caller is also privileged.
+ */
+ case 's': /* obsolete flag meaning delivery flags */
+ if (!argc--) /* takes an argument */
+ _exit (fail ("missing argument to deprecated flag",EX_USAGE));
+ syslog (LOG_INFO,"tmail called with deprecated flag: -s %.200s",*++argv);
+ break;
+ default: /* anything else */
+ _exit (fail ("unknown switch",EX_USAGE));
+ }
+ }
+
+ if (!argc) ret = fail ("no recipients",EX_USAGE);
+ else if (!(f = tmpfile ())) ret = fail ("can't make temp file",EX_TEMPFAIL);
+ else { /* build delivery headers */
+ if (sender) fprintf (f,"Return-Path: <%s>\015\012",sender);
+ /* start Received line: */
+ fprintf (f,"Received: via tmail-%s.%s",CCLIENTVERSION,version);
+ /* not root or daemon? */
+ if (ruid && !((pwd = getpwnam ("daemon")) && (ruid == pwd->pw_uid))) {
+ pwd = getpwuid (ruid); /* get unprivileged user's information */
+ if (inbox || format) {
+ if (pwd) sprintf (tmp,"user %.80s",pwd->pw_name);
+ else sprintf (tmp,"UID %ld",(long) ruid);
+ strcat (tmp," is not privileged to use -b or -I");
+ _exit (fail (tmp,EX_USAGE));
+ }
+ fputs (" (invoked by ",f);
+ if (pwd) fprintf (f,"user %s",pwd->pw_name);
+ else fprintf (f,"UID %ld",(long) ruid);
+ fputs (")",f);
+ }
+ /* write "for" if single recipient */
+ if (argc == 1) fprintf (f," for %s",*argv);
+ fputs ("; ",f);
+ rfc822_date (tmp);
+ fputs (tmp,f);
+ fputs ("\015\012",f);
+ /* copy text from standard input */
+ if (!fgets (tmp,MAILTMPLEN-1,stdin) || !(s = strchr (tmp,'\n')) ||
+ (s == tmp) || s[1]) _exit (fail ("bad first message line",EX_USAGE));
+ if (s[-1] == '\015') { /* nuke leading "From " line */
+ if ((tmp[0] != 'F') || (tmp[1] != 'r') || (tmp[2] != 'o') ||
+ (tmp[3] != 'm') || (tmp[4] != ' ')) fputs (tmp,f);
+ while ((c = getchar ()) != EOF) putc (c,f);
+ }
+ else {
+ mm_log ("tmail called with LF-only newlines",WARN);
+ if ((tmp[0] != 'F') || (tmp[1] != 'r') || (tmp[2] != 'o') ||
+ (tmp[3] != 'm') || (tmp[4] != ' ')) {
+ *s++ = '\015'; /* overwrite NL with CRLF */
+ *s++ = '\012';
+ *s = '\0'; /* tie off string */
+ fputs (tmp,f); /* write line */
+ }
+ /* copy text from standard input */
+ while ((c = getchar ()) != EOF) {
+ /* add CR if needed */
+ if (c == '\012') putc ('\015',f);
+ putc (c,f);
+ }
+ }
+ msglen = ftell (f); /* size of message */
+ fflush (f); /* make sure all changes written out */
+
+ if (ferror (f)) ret = fail ("error writing temp file",EX_TEMPFAIL);
+ else if (!msglen) ret = fail ("empty message",EX_TEMPFAIL);
+ /* single delivery */
+ else if (argc == 1) ret = deliver (f,msglen,*argv);
+ else do { /* multiple delivery uses daughter forks */
+ if ((pid = fork ()) < 0) ret = fail (strerror (errno),EX_OSERR);
+ else if (pid) { /* mother process */
+ grim_pid_reap_status (pid,NIL,(void *) status);
+ /* normal termination? */
+ if (!ret) ret = (status & 0xff) ? EX_SOFTWARE : (status & 0xff00) >> 8;
+ }
+ /* daughter process */
+ else _exit (deliver (f,msglen,*argv));
+ } while (--argc && *argv++);
+ mm_dlog (ret ? "error in delivery" : "all recipients delivered");
+ }
+ if (f) fclose (f); /* all done with temporary file */
+ _exit (ret); /* normal exit */
+ return 0; /* stupid gcc */
+}
+
+/* Deliver message to recipient list
+ * Accepts: file description of message temporary file
+ * size of message temporary file in bytes
+ * recipient name
+ * Returns: NIL if success, else error code
+ */
+
+int deliver (FILE *f,unsigned long msglen,char *user)
+{
+ MAILSTREAM *ds = NIL;
+ char *s,*t,*mailbox,tmp[MAILTMPLEN],path[MAILTMPLEN];
+ struct passwd *pwd;
+ STRING st;
+ struct stat sbuf;
+ uid_t duid;
+ uid_t euid = geteuid ();
+ /* get user record */
+ if (!(pwd = getpwnam (getusername (user,&mailbox)))) {
+ sprintf (tmp,"no such user as %.80s",user);
+ return fail (tmp,EX_NOUSER);
+ }
+ /* absurd is absurd */
+ if (mailbox && (strlen (mailbox) > 256))
+ return fail ("absurd folder name",EX_NOUSER);
+ /* big security hole if this is allowed */
+ if (!(duid = pwd->pw_uid)) return fail ("mail to root prohibited",EX_NOUSER);
+ /* log in as user if different than euid */
+ if ((duid != euid) && !loginpw (pwd,1,&user)) {
+ sprintf (tmp,"unable to log in UID %ld from UID %ld",
+ (long) duid,(long) euid);
+ return fail (tmp,EX_NOUSER);
+ }
+ /* can't use pwd after this point */
+ env_init (pwd->pw_name,pwd->pw_dir);
+ sprintf (tmp,"delivering to %.80s+%.80s",user,mailbox ? mailbox : "INBOX");
+ mm_dlog (tmp);
+ /* prepare stringstruct */
+ INIT (&st,file_string,(void *) f,msglen);
+ if (mailbox) { /* non-INBOX name */
+ switch (mailbox[0]) { /* make sure a valid name */
+ default: /* other names, try to deliver if not INBOX */
+ if (!strstr (mailbox,"..") && !strstr (mailbox,"//") &&
+ !strstr (mailbox,"/~") && mailboxfile (path,mailbox) && path[0] &&
+ !deliver_safely (NIL,&st,mailbox,path,duid,tmp)) return NIL;
+ case '%': case '*': /* wildcards not valid */
+ case '#': /* namespace name not valid */
+ case '/': /* absolute path names not valid */
+ case '~': /* user names not valid */
+ sprintf (tmp,"invalid mailbox name %.80s+%.80s",user,mailbox);
+ mm_log (tmp,WARN);
+ break;
+ }
+ mm_dlog ("retrying delivery to INBOX");
+ SETPOS (&st,0); /* rewind stringstruct just in case */
+ }
+
+ /* -I specified and not "-I INBOX"? */
+ if (inbox && !(((inbox[0] == 'I') || (inbox[0] == 'i')) &&
+ ((inbox[1] == 'N') || (inbox[1] == 'n')) &&
+ ((inbox[2] == 'B') || (inbox[2] == 'b')) &&
+ ((inbox[3] == 'O') || (inbox[3] == 'o')) &&
+ ((inbox[4] == 'X') || (inbox[4] == 'x')) && !inbox[5])) {
+ DRIVER *dv = NIL;
+ /* "-I #driver.xxx/name"? */
+ if ((*inbox == '#') && ((inbox[1] == 'd') || (inbox[1] == 'D')) &&
+ ((inbox[2] == 'r') || (inbox[2] == 'R')) &&
+ ((inbox[3] == 'i') || (inbox[3] == 'I')) &&
+ ((inbox[4] == 'v') || (inbox[4] == 'V')) &&
+ ((inbox[5] == 'e') || (inbox[5] == 'E')) &&
+ ((inbox[6] == 'r') || (inbox[6] == 'R')) && (inbox[7] == '.') &&
+ (s = strchr (inbox+8,'/'))) {
+ *s = '\0'; /* temporarily tie off driver name */
+ if (!((dv = mail_parameters (NIL,GET_DRIVER,(void *) (inbox+8))) &&
+ (mailboxfile (path,s[1] ? s + 1 : "&&&&&") == path) &&
+ (s[1] || ((t = strstr (path,"&&&&&")) && strcpy (t,"INBOX"))))) {
+ path[0] = '\0'; /* bad -I argument, no path resolved */
+ sprintf (tmp,"Unable to resolve driver in %.80s, -I ignored",inbox);
+ mm_log (tmp,WARN);
+ }
+ *s = '/'; /* restore delimiter */
+ }
+ /* resolve "-I other" specification */
+ else if (mailboxfile (path,inbox) && path[0]) {
+ /* resolution succeeded, impute driver */
+ if (!strcmp (inbox,"mail.txt"))
+ dv = mail_parameters (NIL,GET_DRIVER,(void *) "tenex");
+ else if (!strcmp (inbox,"INBOX.MTX"))
+ dv = mail_parameters (NIL,GET_DRIVER,(void *) "mtx");
+ else if (!strcmp (inbox,"mbox"))
+ dv = mail_parameters (NIL,GET_DRIVER,(void *) "unix");
+ }
+ else { /* bad -I argument */
+ path[0] = '\0'; /* no path resolved */
+ sprintf (tmp,"Unable to resolve %.80s, -I ignored",inbox);
+ mm_log (tmp,WARN);
+ }
+ if (*path) { /* -I successfully resolved a path? */
+ MAILSTREAM dpr;
+ dpr.dtb = dv;
+ if (dv) ds = &dpr;
+ /* supplicate to the Evil One if necessary */
+ if (lstat (path,&sbuf) && !path_create (ds,path)) {
+ /* the Evil One rejected the plea */
+ sprintf (tmp,"Unable to create %.80s, -I ignored",path);
+ mm_log (tmp,WARN);
+ }
+ /* now attempt delivery */
+ else return deliver_safely (ds,&st,inbox,path,duid,tmp);
+ }
+ }
+
+ /* no -I, resolve "INBOX" into path */
+ if (mailboxfile (path,mailbox = "INBOX") && !path[0]) {
+ /* clear box, get generic INBOX prototype */
+ if (!(ds = mail_open (NIL,"INBOX",OP_PROTOTYPE)))
+ fatal ("no INBOX prototype");
+ /* standard system driver? */
+ if (!strcmp (ds->dtb->name,"unix") || !strcmp (ds->dtb->name,"mmdf")) {
+ strcpy (path,sysinbox ());/* use system INBOX */
+ if (!lstat (path,&sbuf)) /* deliver to existing system INBOX */
+ return deliver_safely (ds,&st,mailbox,path,duid,tmp);
+ }
+ else { /* other driver, try ~/INBOX */
+ if ((mailboxfile (path,"&&&&&") == path) &&
+ (s = strstr (path,"&&&&&")) && strcpy (s,"INBOX") &&
+ !lstat (path,&sbuf)){ /* deliver to existing ~/INBOX */
+ sprintf (tmp,"#driver.%s/INBOX",ds->dtb->name);
+ return deliver_safely (ds,&st,cpystr (tmp),path,duid,tmp);
+ }
+ }
+ /* not dummy, deliver to driver imputed path */
+ if (strcmp (ds->dtb->name,"dummy"))
+ return (ibxpath (ds,&mailbox,path) && !lstat (path,&sbuf)) ?
+ deliver_safely (ds,&st,mailbox,path,duid,tmp) :
+ fail ("unable to resolve INBOX path",EX_CANTCREAT);
+ /* dummy, empty imputed append path exist? */
+ if (ibxpath (ds = default_proto (T),&mailbox,path) &&
+ !lstat (path,&sbuf) && !sbuf.st_size)
+ return deliver_safely (ds,&st,mailbox,path,duid,tmp);
+ /* impute path that we will create */
+ if (!ibxpath (ds = format ? (format->open) (NIL) : default_proto (NIL),
+ &mailbox,path))
+ return fail ("unable to resolve INBOX",EX_CANTCREAT);
+ }
+ /* black box, must create, get create proto */
+ else if (lstat (path,&sbuf)) ds = default_proto (NIL);
+ else { /* black box, existing file */
+ /* empty file, get append prototype */
+ if (!sbuf.st_size) ds = default_proto (T);
+ /* non-empty, get prototype from its data */
+ else if (!(ds = mail_open (NIL,"INBOX",OP_PROTOTYPE)))
+ fatal ("no INBOX prototype");
+ /* error if unknown format */
+ if (!strcmp (ds->dtb->name,"phile"))
+ return fail ("unknown format INBOX",EX_UNAVAILABLE);
+ /* otherwise can deliver to it */
+ return deliver_safely (ds,&st,mailbox,path,duid,tmp);
+ }
+ sprintf (tmp,"attempting to create mailbox %.80s path %.80s",mailbox,path);
+ mm_dlog (tmp);
+ /* supplicate to the Evil One */
+ if (!path_create (ds,path)) return fail ("can't create INBOX",EX_CANTCREAT);
+ sprintf (tmp,"created %.80s",path);
+ mm_dlog (tmp);
+ /* deliver the message */
+ return deliver_safely (ds,&st,mailbox,path,duid,tmp);
+}
+
+/* Resolve INBOX from driver prototype into mailbox name and filesystem path
+ * Accepts: driver prototype
+ * pointer to mailbox name string pointer
+ * buffer to return mailbox path
+ * Returns: T if success, NIL if error
+ */
+
+long ibxpath (MAILSTREAM *ds,char **mailbox,char *path)
+{
+ char *s,tmp[MAILTMPLEN];
+ long ret = T;
+ if (!ds) ret = NIL;
+ else if (!strcmp (ds->dtb->name,"unix") || !strcmp (ds->dtb->name,"mmdf"))
+ strcpy (path,sysinbox ()); /* use system INBOX for unix and MMDF */
+ else if (!strcmp (ds->dtb->name,"tenex"))
+ ret = (mailboxfile (path,"mail.txt") == path) ? T : NIL;
+ else if (!strcmp (ds->dtb->name,"mtx"))
+ ret = (mailboxfile (path,"INBOX.MTX") == path) ? T : NIL;
+ else if (!strcmp (ds->dtb->name,"mbox"))
+ ret = (mailboxfile (path,"mbox") == path) ? T : NIL;
+ /* better not be a namespace driver */
+ else if (ds->dtb->flags & DR_NAMESPACE) return NIL;
+ /* INBOX in home directory */
+ else ret = ((mailboxfile (path,"&&&&&") == path) &&
+ (s = strstr (path,"&&&&&")) && strcpy (s,"INBOX")) ? T : NIL;
+ if (ret) { /* don't bother if lossage */
+ sprintf (tmp,"#driver.%s/INBOX",ds->dtb->name);
+ *mailbox = cpystr (tmp); /* name of INBOX in this namespace */
+ }
+ return ret;
+}
+
+/* Deliver safely
+ * Accepts: prototype stream to force mailbox format
+ * stringstruct of message temporary file
+ * mailbox name
+ * filesystem path name
+ * user id
+ * scratch buffer for messages
+ * Returns: NIL if success, else error code
+ */
+
+int deliver_safely (MAILSTREAM *prt,STRING *st,char *mailbox,char *path,
+ uid_t uid,char *tmp)
+{
+ struct stat sbuf;
+ int i = delivery_unsafe (path,uid,&sbuf,tmp);
+ if (i) return i; /* give up now if delivery unsafe */
+ /* directory, not file */
+ if ((sbuf.st_mode & S_IFMT) == S_IFDIR) {
+ if (sbuf.st_mode & 0001) { /* listable directories may be worrisome */
+ sprintf (tmp,"WARNING: directory %.80s is listable",path);
+ mm_log (tmp,WARN);
+ }
+ }
+ else { /* file, not directory */
+ if (sbuf.st_nlink != 1) { /* multiple links may be worrisome */
+ sprintf (tmp,"WARNING: multiple links to file %.80s",path);
+ mm_log (tmp,WARN);
+ }
+ if (sbuf.st_mode & 0111) { /* executable files may be worrisome */
+ sprintf (tmp,"WARNING: file %.80s is executable",path);
+ mm_log (tmp,WARN);
+ }
+ }
+ if (sbuf.st_mode & 0002) { /* public-write files may be worrisome */
+ sprintf (tmp,"WARNING: file %.80s is publicly-writable",path);
+ mm_log (tmp,WARN);
+ }
+ if (sbuf.st_mode & 0004) { /* public-write files may be worrisome */
+ sprintf (tmp,"WARNING: file %.80s is publicly-readable",path);
+ mm_log (tmp,WARN);
+ }
+ /* check site-written quota procedure */
+ if (!tmail_quota (st,path,uid,tmp,sender,precedence)) return fail (tmp,-1);
+ /* so far, so good */
+ sprintf (tmp,"%s appending to %.80s (%s %.80s)",
+ prt ? prt->dtb->name : "default",mailbox,
+ ((sbuf.st_mode & S_IFMT) == S_IFDIR) ? "directory" : "file",path);
+ mm_dlog (tmp);
+ /* do the append now! */
+ if (!mail_append (prt,mailbox,st)) {
+ sprintf (tmp,"message delivery failed to %.80s",path);
+ return fail (tmp,EX_CANTCREAT);
+ }
+ /* note success */
+ sprintf (tmp,"delivered to %.80s",path);
+ mm_log (tmp,NIL);
+ /* make sure nothing evil this way comes */
+ return delivery_unsafe (path,uid,&sbuf,tmp);
+}
+
+/* Verify that delivery is safe
+ * Accepts: path name
+ * user id
+ * stat buffer
+ * scratch buffer for messages
+ * Returns: NIL if delivery is safe, error code if unsafe
+ */
+
+int delivery_unsafe (char *path,uid_t uid,struct stat *sbuf,char *tmp)
+{
+ u_short type;
+ sprintf (tmp,"Verifying safe delivery to %.80s by UID %ld",path,(long) uid);
+ mm_dlog (tmp);
+ /* prepare message just in case */
+ sprintf (tmp,"delivery to %.80s unsafe: ",path);
+ /* unsafe if can't get its status */
+ if (lstat (path,sbuf)) strcat (tmp,strerror (errno));
+ else if (sbuf->st_uid != uid) /* unsafe if UID does not match */
+ sprintf (tmp + strlen (tmp),"uid mismatch (%ld != %ld)",
+ (long) sbuf->st_uid,(long) uid);
+ /* check file type */
+ else switch (sbuf->st_mode & S_IFMT) {
+ case S_IFDIR: /* directory is always OK */
+ return NIL;
+ case S_IFREG: /* file is unsafe if setuid */
+ if (sbuf->st_mode & S_ISUID) strcat (tmp,"setuid file");
+ /* or setgid */
+ else if (sbuf->st_mode & S_ISGID) strcat (tmp,"setgid file");
+ else return NIL; /* otherwise safe */
+ break;
+ case S_IFCHR: strcat (tmp,"character special"); break;
+ case S_IFBLK: strcat (tmp,"block special"); break;
+ case S_IFLNK: strcat (tmp,"symbolic link"); break;
+ case S_IFSOCK: strcat (tmp,"socket"); break;
+ default:
+ sprintf (tmp + strlen (tmp),"file type %07o",(unsigned int) type);
+ }
+ return fail (tmp,EX_CANTCREAT);
+}
+
+/* Report an error
+ * Accepts: string to output
+ */
+
+int fail (char *string,int code)
+{
+ mm_log (string,ERROR); /* pass up the string */
+ switch (code) {
+#if T
+ case EX_USAGE:
+ case EX_OSERR:
+ case EX_SOFTWARE:
+ case EX_NOUSER:
+ case EX_CANTCREAT:
+ case EX_UNAVAILABLE:
+ code = EX_TEMPFAIL; /* coerce these to TEMPFAIL */
+#endif
+ break;
+ case -1: /* quota failure... */
+ code = EX_CANTCREAT; /* ...really returns this code */
+ break;
+ default:
+ break;
+ }
+ return code; /* error code to return */
+}
+
+
+/* Get user name from username+mailbox specifier
+ * Accepts: username/mailbox specifier
+ * pointer to return location for mailbox specifier
+ * Returns: user name, mailbox specifier value NIL if INBOX, patches out +
+ */
+
+char *getusername (char *s,char **t)
+{
+ if (*t = strchr (s,'+')) { /* have a mailbox specifier? */
+ *(*t)++ = '\0'; /* yes, tie off user name */
+ /* user+ and user+INBOX same as user */
+ if (!**t || !compare_cstring ((unsigned char *) *t,"INBOX")) *t = NIL;
+ }
+ return s; /* return user name */
+}
+
+/* Co-routines from MAIL library */
+
+
+/* Message matches a search
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_searched (MAILSTREAM *stream,unsigned long msgno)
+{
+ fatal ("mm_searched() call");
+}
+
+
+/* Message exists (i.e. there are that many messages in the mailbox)
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_exists (MAILSTREAM *stream,unsigned long number)
+{
+ fatal ("mm_exists() call");
+}
+
+
+/* Message expunged
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_expunged (MAILSTREAM *stream,unsigned long number)
+{
+ fatal ("mm_expunged() call");
+}
+
+
+/* Message flags update seen
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_flags (MAILSTREAM *stream,unsigned long number)
+{
+}
+
+/* Mailbox found
+ * Accepts: MAIL stream
+ * delimiter
+ * mailbox name
+ * mailbox attributes
+ */
+
+void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes)
+{
+ fatal ("mm_list() call");
+}
+
+
+/* Subscribed mailbox found
+ * Accepts: MAIL stream
+ * delimiter
+ * mailbox name
+ * mailbox attributes
+ */
+
+void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes)
+{
+ fatal ("mm_lsub() call");
+}
+
+
+/* Mailbox status
+ * Accepts: MAIL stream
+ * mailbox name
+ * mailbox status
+ */
+
+void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
+{
+ fatal ("mm_status() call");
+}
+
+/* Notification event
+ * Accepts: MAIL stream
+ * string to log
+ * error flag
+ */
+
+void mm_notify (MAILSTREAM *stream,char *string,long errflg)
+{
+ char tmp[MAILTMPLEN];
+ tmp[11] = '\0'; /* see if TRYCREATE */
+ if (!strcmp (ucase (strncpy (tmp,string,11)),"[TRYCREATE]")) trycreate = T;
+ mm_log (string,errflg); /* just do mm_log action */
+}
+
+
+/* Log an event for the user to see
+ * Accepts: string to log
+ * error flag
+ */
+
+void mm_log (char *string,long errflg)
+{
+ if (trycreate)mm_dlog(string);/* debug logging only if trycreate in effect */
+ else { /* ordinary logging */
+ fprintf (stderr,"%s\n",string);
+ switch (errflg) {
+ case NIL: /* no error */
+ syslog (LOG_INFO,"%s",string);
+ break;
+ case PARSE: /* parsing problem */
+ case WARN: /* warning */
+ syslog (LOG_WARNING,"%s",string);
+ break;
+ case ERROR: /* error */
+ default:
+ syslog (LOG_ERR,"%s",string);
+ break;
+ }
+ }
+}
+
+
+/* Log an event to debugging telemetry
+ * Accepts: string to log
+ */
+
+void mm_dlog (char *string)
+{
+ if (debug) fprintf (stderr,"%s\n",string);
+ syslog (LOG_DEBUG,"%s",string);
+}
+
+/* Get user name and password for this host
+ * Accepts: parse of network mailbox name
+ * where to return user name
+ * where to return password
+ * trial count
+ */
+
+void mm_login (NETMBX *mb,char *username,char *password,long trial)
+{
+ fatal ("mm_login() call");
+}
+
+
+/* About to enter critical code
+ * Accepts: stream
+ */
+
+void mm_critical (MAILSTREAM *stream)
+{
+ critical = T; /* note in critical code */
+}
+
+
+/* About to exit critical code
+ * Accepts: stream
+ */
+
+void mm_nocritical (MAILSTREAM *stream)
+{
+ critical = NIL; /* note not in critical code */
+}
+
+
+/* Disk error found
+ * Accepts: stream
+ * system error code
+ * flag indicating that mailbox may be clobbered
+ * Returns: T if user wants to abort
+ */
+
+long mm_diskerror (MAILSTREAM *stream,long errcode,long serious)
+{
+ return T;
+}
+
+
+/* Log a fatal error event
+ * Accepts: string to log
+ */
+
+void mm_fatal (char *string)
+{
+ printf ("?%s\n",string); /* shouldn't happen normally */
+}
diff --git a/imap/src/tmail/tquota.c b/imap/src/tmail/tquota.c
new file mode 100644
index 00000000..16552eb2
--- /dev/null
+++ b/imap/src/tmail/tquota.c
@@ -0,0 +1,45 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Mail Delivery Module Quota Hook
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 September 2007
+ * Last Edited: 10 September 2007
+ */
+
+#include "c-client.h"
+
+/* Site-written routine to validate delivery per quota and policy
+ * Accepts: stringstruct of message temporary file
+ * filesystem path
+ * recipient user id
+ * return path
+ * buffer to write error message
+ * precedence setting
+ * Returns: T if can deliver, or NIL if quota issue and must bounce
+ */
+
+long tmail_quota (STRING *msg,char *path,uid_t uid,char *tmp,char *sender,
+ long precedence)
+{
+ return LONGT; /* dummy success return */
+}
diff --git a/imap/src/tmail/tquota.h b/imap/src/tmail/tquota.h
new file mode 100644
index 00000000..1cd11c01
--- /dev/null
+++ b/imap/src/tmail/tquota.h
@@ -0,0 +1,32 @@
+/* ========================================================================
+ * Copyright 1988-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Mail Delivery Module Quota Hook
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 10 September 2007
+ * Last Edited: 10 September 2007
+ */
+
+/* Function prototypes */
+
+long tmail_quota (STRING *msg,char *path,uid_t uid,char *tmp,char *sender,
+ long precedence);
diff --git a/imap/tools/Makefile b/imap/tools/Makefile
new file mode 100644
index 00000000..f2db51f9
--- /dev/null
+++ b/imap/tools/Makefile
@@ -0,0 +1,36 @@
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+# Program: Tools Makefile
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+
+
+CC=cc
+RM=rm -f
+
+
+uahelper:
+ $(CC) -o uahelper uahelper.c $(LDFLAGS)
+
+clean:
+ sh -c '$(RM) *.o uahelper || true'
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo 'not war?'
diff --git a/imap/tools/an b/imap/tools/an
new file mode 100755
index 00000000..dcaca97d
--- /dev/null
+++ b/imap/tools/an
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+# ========================================================================
+# Copyright 1988-2007 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+BASE=`pwd`
+
+if [ ! -d $3 ]; then
+ mkdir $3
+fi
+
+for i in $2/Makefile* ; do
+ if [ -f $i ] ; then
+ $1 "$BASE/$i" "$BASE/$3"
+ fi
+done
+
+if [ -f $2/drivers ]; then
+ $1 "$BASE/$2/drivers" "$BASE/$3"
+fi
+
+if [ -f $2/mkauths ]; then
+ $1 "$BASE/$2/mkauths" "$BASE/$3"
+fi
+
+cd $2
+
+for j in *.[ch]; do
+ $1 "$BASE/$2/$j" "$BASE/$3"
+done
+
+exit 0
diff --git a/imap/tools/ua b/imap/tools/ua
new file mode 100755
index 00000000..a188b56c
--- /dev/null
+++ b/imap/tools/ua
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+# ========================================================================
+# Copyright 1988-2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+#
+# ========================================================================
+
+BASE=`pwd`
+
+if [ ! -f tools/uahelper ]; then
+ (cd tools;make)
+fi
+
+if [ ! -d $3 ]; then
+ mkdir $3
+fi
+
+for i in $2/Makefile* ; do
+ if [ -f $i ] ; then
+ $1 "$BASE/$i" "$BASE/$3"
+ fi
+done
+
+if [ -f $2/drivers ]; then
+ $1 "$BASE/$2/drivers" "$BASE/$3"
+fi
+
+if [ -f $2/mkauths ]; then
+ $1 "$BASE/$2/mkauths" "$BASE/$3"
+fi
+
+cd $2
+
+for j in *.[ch]; do
+ "$BASE/tools/uahelper" < $j > "$BASE/$3/$j"
+done
+
+exit 0
diff --git a/imap/tools/uahelper.c b/imap/tools/uahelper.c
new file mode 100644
index 00000000..114fd687
--- /dev/null
+++ b/imap/tools/uahelper.c
@@ -0,0 +1,262 @@
+/* ========================================================================
+ * Copyright 1988-2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: unANSIify
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * 4545 15th Ave NE
+ * Seattle, WA 98105-4527
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 June 1995
+ * Last Edited: 30 August 2006
+ */
+
+
+/* This program is designed to make traditional C sources out of my source
+ * files which use ANSI syntax. This program depends heavily upon knowledge
+ * of the way I write code, and is not a general purpose tool. I hope that
+ * someday someone will provide a general purpose tool...
+ */
+
+#include <stdio.h>
+
+#define LINELENGTH 1000
+
+
+/* Find first non-whitespace
+ * Accepts: string
+ * Returns: 0 if no non-whitespace found, or pointer to non-whitespace
+ */
+
+char *fndnws (s)
+ char *s;
+{
+ while ((*s == ' ') || (*s == '\t'))
+ if ((*s++ == '\n') || !*s) return (char *) 0;
+ return s;
+}
+
+
+/* Find first whitespace
+ * Accepts: string
+ * Returns: 0 if no whitespace found, or pointer to whitespace
+ */
+
+char *fndws (s)
+ char *s;
+{
+ while ((*s != ' ') && (*s != '\t'))
+ if ((*s++ == '\n') || !*s) return (char *) 0;
+ return s;
+}
+
+
+/* Find end of commend
+ * Accepts: string
+ * Returns: -1 if end of comment found, else 0
+ */
+
+int fndcmt (s)
+ char *s;
+{
+ while (*s && (*s != '\n'))
+ if ((*s++ == '*') && (*s == '/')) return -1;
+ return 0;
+}
+
+/* Output a line
+ * Accepts: string
+ */
+
+void poot (s)
+ char *s;
+{
+ if (s) fputs (s,stdout);
+}
+
+
+/* Skip prefix
+ * Accepts: string
+ * Returns: updated string
+ */
+
+char *skipfx (s)
+ char *s;
+{
+ char c,*t = s,*tt;
+ /* skip leading whitespace too */
+ while ((*s == ' ') || (*s == '\t')) *s++;
+ if (s != t) {
+ c = *s; /* save character */
+ *s = '\0'; /* tie off prefix */
+ poot (t); /* output prefix */
+ *s = c; /* restore character */
+ }
+ /* a typedef? */
+ if (((s[0] == 't') && (s[1] == 'y') && (s[2] == 'p') && (s[3] == 'e') &&
+ (s[4] == 'd') && (s[5] == 'e') && (s[6] == 'f')) &&
+ (t = fndws (s)) && (t = fndnws (t))) {
+ if ((t[0] == 'u') && (t[1] == 'n') && (t[2] == 's') && (t[3] == 'i') &&
+ (t[4] == 'g') && (t[5] == 'n') && (t[6] == 'e') && (t[7] == 'd') &&
+ (tt = fndws (t+7)) && (tt = fndnws (tt))) t = tt;
+ c = *t; /* save character */
+ *t = '\0'; /* tie off prefix */
+ poot (s); /* output prefix */
+ *t = c; /* restore character */
+ s = t; /* new string pointer */
+ }
+ /* static with known prefix */
+ else if (((s[0] == 's') && (s[1] == 't') && (s[2] == 'a') &&
+ (s[3] == 't') && (s[4] == 'i') && (s[5] == 'c') &&
+ (s[6] == ' ')) &&
+ (((s[7] == 'u') && (s[8] == 'n') && (s[9] == 's') &&
+ (s[10] == 'i') && (s[11] == 'g') && (s[12] == 'n') &&
+ (s[13] == 'e') && (s[14] == 'd')) ||
+ ((s[7] == 's') && (s[8] == 't') && (s[9] == 'r') &&
+ (s[10] == 'u') && (s[11] == 'c') && (s[12] == 't')) ||
+ ((s[7] == 'd') && (s[8] == 'o')) ||
+ ((s[9] == 'e') && (s[10] == 'l') && (s[11] == 's') &&
+ (s[12] == 'e'))) &&
+ (t = fndws (s)) && (t = fndnws (t)) &&
+ (t = fndws (t)) && (t = fndnws (t))) {
+ c = *t; /* save character */
+ *t = '\0'; /* tie off prefix */
+ poot (s); /* output prefix */
+ *t = c; /* restore character */
+ s = t; /* new string pointer */
+ }
+ /* one of the known prefixes? */
+ else if ((((s[0] == 'u') && (s[1] == 'n') && (s[2] == 's') &&
+ (s[3] == 'i') && (s[4] == 'g') && (s[5] == 'n') &&
+ (s[6] == 'e') && (s[7] == 'd')) ||
+ ((s[0] == 's') && (s[1] == 't') && (s[2] == 'r') &&
+ (s[3] == 'u') && (s[4] == 'c') && (s[5] == 't')) ||
+ ((s[0] == 's') && (s[1] == 't') && (s[2] == 'a') &&
+ (s[3] == 't') && (s[4] == 'i') && (s[5] == 'c')) ||
+ ((s[0] == 'd') && (s[1] == 'o')) ||
+ ((s[0] == 'e') && (s[1] == 'l') && (s[2] == 's') &&
+ (s[3] == 'e'))) &&
+ (t = fndws (s)) && (t = fndnws (t))) {
+ c = *t; /* save character */
+ *t = '\0'; /* tie off prefix */
+ poot (s); /* output prefix */
+ *t = c; /* restore character */
+ s = t; /* new string pointer */
+ }
+ /* may look like a proto, but isn't */
+ else if ((s[0] == 'r') && (s[1] == 'e') && (s[2] == 't') && (s[3] == 'u') &&
+ (s[4] == 'r') && (s[5] == 'n')) {
+ poot (s);
+ return 0;
+ }
+ return s;
+}
+
+/* UnANSI a line
+ * Accepts: string
+ */
+
+void unansi (s)
+ char *s;
+{
+ char c,*t = s,*u,*v;
+ while (t[1] && (t[1] != '\n')) t++;
+ switch (*t) {
+ case ',': /* continued on next line? */
+ /* slurp remainder of line */
+ fgets (t + 1,LINELENGTH - (t + 1 - s),stdin);
+ unansi (s); /* try again */
+ break;
+ case ';': /* function prototype? */
+ /* yes, tie it off */
+ *(fndws (fndnws (fndws (fndnws (s))))) = '\0';
+ printf ("%s ();\n",s); /* and output non-ANSI form */
+ break;
+ case ')':
+ *t = '\0'; /* tie off args */
+ if (*(t = fndnws (fndws (fndnws (fndws (fndnws (s)))))) == '(') {
+ *t++ = '\0'; /* tie off */
+ while ((*t == ' ') || (*t == '\t')) t++;
+ if ((t[0] == 'v') && (t[1] == 'o') && (t[2] == 'i') && (t[3] == 'd') &&
+ !t[4]) *t = '\0'; /* make void be same as null */
+ printf ("%s(",s); /* output start of function */
+ s = t;
+ while (*s) { /* for each argument */
+ while ((*s == ' ') || (*s == '\t')) s++;
+ for (u = v = s; (*u && (*u != ',') && (*u != '[')); u++)
+ if ((*u == ' ') || (*u == '\t')) v = u;
+ c = *u; /* remember delimiter */
+ *u = '\0'; /* tie off argument name */
+ while (*++v == '*'); /* remove leading pointer indication */
+ fputs (v,stdout); /* write variable name */
+ *(s = u) = c; /* restore delimiter */
+ while (*s && (*s != ',')) *s++;
+ if (*s) fputc (*s++,stdout);
+ }
+ puts (")"); /* end of function */
+ while (*t) { /* for each argument */
+ fputs (" ",stdout);
+ while ((*t == ' ') || (*t == '\t')) t++;
+ while (*t && (*t != ',')) fputc (*t++,stdout);
+ puts (";");
+ if (*t == ',') t++;
+ }
+ }
+ else printf ("%s)",s); /* say what?? */
+ break;
+ default: /* doesn't look like a function */
+ poot (s);
+ }
+}
+
+main ()
+{
+ char *s,*t,line[LINELENGTH];
+ int c;
+ while (s = fgets (line,LINELENGTH,stdin)) switch (line[0]) {
+ case '/': /* comment */
+ if ((s[1] != '*') || fndcmt (s+2)) poot (line);
+ else do poot (line);
+ while (!fndcmt (line) && (s = fgets (line,LINELENGTH,stdin)));
+ break;
+ case '{': /* open function */
+ case '}': /* close function */
+ case '\f': /* formfeed */
+ case '\n': /* newline */
+ case '#': /* preprocessor command */
+ poot (line);
+ break;
+ case '\t': /* whitespace */
+ case ' ':
+ /* look like function arg def in structure? */
+ if ((t = skipfx (line)) && (s = fndws (t)) && (s = fndnws (s)) &&
+ (((*s == '(') && (s[1] == '*')) ||
+ ((*s == '*') && (s[1] == '(') && (s[2] == '*'))) &&
+ (s = fndws (s)) && (s[-1] == ')') && (s = fndnws (s)) && (*s == '('))
+ unansi (t);
+ else poot (t);
+ break;
+ default: /* begins with anything else */
+ /* look like function proto or def? */
+ if ((t = skipfx (line)) && (s = fndws (t)) && (s = fndnws (s)) &&
+ (s = fndws (s)) && (s = fndnws (s)) && (*s == '('))
+ unansi (t);
+ else poot (t);
+ break;
+ }
+}
diff --git a/include/config.h.in b/include/config.h.in
new file mode 100644
index 00000000..dadc4341
--- /dev/null
+++ b/include/config.h.in
@@ -0,0 +1,649 @@
+/* include/config.h.in. Generated from configure.ac by autoheader. */
+
+/* Default configuration value */
+#undef ANSI_PRINTER
+
+/* Use Apple OS X key chain for credential caching */
+#undef APPLEKEYCHAIN
+
+/* Enable background posting support */
+#undef BACKGROUND_POST
+
+/* Default configuration value */
+#undef CHECK_POINT_FREQ
+
+/* Default configuration value */
+#undef CHECK_POINT_TIME
+
+/* File name separator as character constant */
+#undef C_FILESEP
+
+/* Default configuration value */
+#undef DEADLETTER
+
+/* Compile in debugging */
+#undef DEBUG
+
+/* Default configuration value */
+#undef DEBUGFILE
+
+/* Display debug messages in journal */
+#undef DEBUGJOURNAL
+
+/* Default configuration value */
+#undef DEFAULT_COLUMNS_ON_TERMINAL
+
+/* Default configuration value */
+#undef DEFAULT_DEBUG
+
+/* Default configuration value */
+#undef DEFAULT_LINES_ON_TERMINAL
+
+/* Default configuration value */
+#undef DEFAULT_SAVE
+
+/* Default configuration value */
+#undef DF_AB_SORT_RULE
+
+/* Default configuration value */
+#undef DF_ADDRESSBOOK
+
+/* Default configuration value */
+#undef DF_CACERT_DIR
+
+/* Default configuration value */
+#undef DF_DEFAULT_FCC
+
+/* Default configuration value */
+#undef DF_DEFAULT_PRINTER
+
+/* Default configuration value */
+#undef DF_ELM_STYLE_SAVE
+
+/* Default configuration value */
+#undef DF_FCC_RULE
+
+/* Default configuration value */
+#undef DF_FILLCOL
+
+/* Default configuration value */
+#undef DF_FLD_SORT_RULE
+
+/* Default configuration value */
+#undef DF_HEADER_IN_REPLY
+
+/* Default configuration value */
+#undef DF_KBLOCK_PASSWD_COUNT
+
+/* Default configuration value */
+#undef DF_LOCAL_ADDRESS
+
+/* Default configuration value */
+#undef DF_LOCAL_FULLNAME
+
+/* Default configuration value */
+#undef DF_MAILCHECK
+
+/* Default configuration value */
+#undef DF_MAIL_DIRECTORY
+
+/* Default configuration value */
+#undef DF_MARGIN
+
+/* Default configuration value */
+#undef DF_OLD_STYLE_REPLY
+
+/* Default configuration value */
+#undef DF_OVERLAP
+
+/* Default configuration value */
+#undef DF_PRIVATEKEY_DIR
+
+/* Default configuration value */
+#undef DF_PUBLICCERT_DIR
+
+/* Default configuration value */
+#undef DF_REMOTE_ABOOK_HISTORY
+
+/* Default configuration value */
+#undef DF_SAVED_MSG_NAME_RULE
+
+/* Default configuration value */
+#undef DF_SAVE_BY_SENDER
+
+/* Default configuration value */
+#undef DF_SIGNATURE_FILE
+
+/* Default configuration value */
+#undef DF_SORT_KEY
+
+/* set default value of ssh command string (usually "%s %s -l %s exec
+ /etc/r%sd") */
+#undef DF_SSHCMD
+
+/* set default value of ssh command path (defining should cause ssh to be
+ preferred to rsh) */
+#undef DF_SSHPATH
+
+/* Default configuration value */
+#undef DF_STANDARD_PRINTER
+
+/* Default configuration value */
+#undef DF_USE_ONLY_DOMAIN_NAME
+
+/* Interactive, filewise spell checker */
+#undef DF_VAR_SPELLER
+
+/* Define enable dmalloc debugging */
+#undef ENABLE_DMALLOC
+
+/* Enable LDAP query support */
+#undef ENABLE_LDAP
+
+/* Define to 1 if translation of program messages to the user's native
+ language is requested. */
+#undef ENABLE_NLS
+
+/* Enable From address encoding in sent messages */
+#undef ENCODE_FROMS
+
+/* Default configuration value */
+#undef FORWARDED_FLAG
+
+/* Define to 1 if `TIOCGWINSZ' requires <sys/ioctl.h>. */
+#undef GWINSZ_IN_SYS_IOCTL
+
+/* Define if systems uses old BSD-style terminal control */
+#undef HAS_SGTTY
+
+/* Define if systems uses termcap terminal database */
+#undef HAS_TERMCAP
+
+/* Define if systems uses terminfo terminal database */
+#undef HAS_TERMINFO
+
+/* Define if systems uses termio terminal control */
+#undef HAS_TERMIO
+
+/* Define if systems uses termios terminal control */
+#undef HAS_TERMIOS
+
+/* Define to 1 if you have the <assert.h> header file. */
+#undef HAVE_ASSERT_H
+
+/* Define to 1 if you have the MacOS X function CFLocaleCopyCurrent in the
+ CoreFoundation framework. */
+#undef HAVE_CFLOCALECOPYCURRENT
+
+/* Define to 1 if you have the MacOS X function CFPreferencesCopyAppValue in
+ the CoreFoundation framework. */
+#undef HAVE_CFPREFERENCESCOPYAPPVALUE
+
+/* Define to 1 if you have the `chown' function. */
+#undef HAVE_CHOWN
+
+/* Define to 1 if you have the <ctype.h> header file. */
+#undef HAVE_CTYPE_H
+
+/* Define if the GNU dcgettext() function is already present or preinstalled.
+ */
+#undef HAVE_DCGETTEXT
+
+/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.
+ */
+#undef HAVE_DIRENT_H
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
+/* Define to 1 if you have the <errno.h> header file. */
+#undef HAVE_ERRNO_H
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#undef HAVE_FCNTL_H
+
+/* Define to 1 if you have the `fork' function. */
+#undef HAVE_FORK
+
+/* Define to 1 if you have the `fsync' function. */
+#undef HAVE_FSYNC
+
+/* Define to 1 if you have the `getpwnam' function. */
+#undef HAVE_GETPWNAM
+
+/* Define to 1 if you have the `getpwuid' function. */
+#undef HAVE_GETPWUID
+
+/* Define if the GNU gettext() function is already present or preinstalled. */
+#undef HAVE_GETTEXT
+
+/* Define to 1 if you have the `gettimeofday' function. */
+#undef HAVE_GETTIMEOFDAY
+
+/* Define to 1 if you have the `getuid' function. */
+#undef HAVE_GETUID
+
+/* Define if you have the iconv() function. */
+#undef HAVE_ICONV
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the <langinfo.h> header file. */
+#undef HAVE_LANGINFO_H
+
+/* Define to 1 if you have the <limits.h> header file. */
+#undef HAVE_LIMITS_H
+
+/* Define to 1 if you have the `listen' function. */
+#undef HAVE_LISTEN
+
+/* Define to 1 if you have the <locale.h> header file. */
+#undef HAVE_LOCALE_H
+
+/* Define to 1 if you have the `mbstowcs' function. */
+#undef HAVE_MBSTOWCS
+
+/* Define to 1 if you have the `memcpy' function. */
+#undef HAVE_MEMCPY
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define if system supports subsecond, non-alarm sleep */
+#undef HAVE_NANOSLEEP
+
+/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
+#undef HAVE_NDIR_H
+
+/* Define to 1 if you have the <netdb.h> header file. */
+#undef HAVE_NETDB_H
+
+/* Define to 1 if you have the `pclose' function. */
+#undef HAVE_PCLOSE
+
+/* Define to 1 if you have the `poll' function. */
+#undef HAVE_POLL
+
+/* Define to 1 if you have the `popen' function. */
+#undef HAVE_POPEN
+
+/* System has pthread support */
+#undef HAVE_PTHREAD
+
+/* Define to 1 if you have the <pthread.h> header file. */
+#undef HAVE_PTHREAD_H
+
+/* Define to 1 if you have the `putenv' function. */
+#undef HAVE_PUTENV
+
+/* Define to 1 if you have the <pwd.h> header file. */
+#undef HAVE_PWD_H
+
+/* Define to 1 if you have the `qsort' function. */
+#undef HAVE_QSORT
+
+/* Define to 1 if you have the `read' function. */
+#undef HAVE_READ
+
+/* Regular expression header file exists */
+#undef HAVE_REGEX_H
+
+/* Define to 1 if you have the `rename' function. */
+#undef HAVE_RENAME
+
+/* Define to 1 if you have the `select' function. */
+#undef HAVE_SELECT
+
+/* Define to 1 if you have the `setenv' function. */
+#undef HAVE_SETENV
+
+/* Define to 1 if you have the `setjmp' function. */
+#undef HAVE_SETJMP
+
+/* Define to 1 if you have the <setjmp.h> header file. */
+#undef HAVE_SETJMP_H
+
+/* Define to 1 if you have the `sigaction' function. */
+#undef HAVE_SIGACTION
+
+/* Define to 1 if you have the `sigaddset' function. */
+#undef HAVE_SIGADDSET
+
+/* Define to 1 if you have the `sigemptyset' function. */
+#undef HAVE_SIGEMPTYSET
+
+/* Define to 1 if you have the `signal' function. */
+#undef HAVE_SIGNAL
+
+/* Define to 1 if you have the <signal.h> header file. */
+#undef HAVE_SIGNAL_H
+
+/* Define to 1 if you have the `sigprocmask' function. */
+#undef HAVE_SIGPROCMASK
+
+/* Define to 1 if you have the `sigrelse' function. */
+#undef HAVE_SIGRELSE
+
+/* Define to 1 if you have the `sigset' function. */
+#undef HAVE_SIGSET
+
+/* Define to 1 if you have the `srandom' function. */
+#undef HAVE_SRANDOM
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the `strchr' function. */
+#undef HAVE_STRCHR
+
+/* Define to 1 if you have the `strcoll' function and it is properly defined.
+ */
+#undef HAVE_STRCOLL
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the <stropts.h> header file. */
+#undef HAVE_STROPTS_H
+
+/* Define to 1 if you have the `strtol' function. */
+#undef HAVE_STRTOL
+
+/* Define to 1 if you have the `strtoul' function. */
+#undef HAVE_STRTOUL
+
+/* Define if system supplies syslog() logging */
+#undef HAVE_SYSLOG
+
+/* Define to 1 if you have the <syslog.h> header file. */
+#undef HAVE_SYSLOG_H
+
+/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.
+ */
+#undef HAVE_SYS_DIR_H
+
+/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'.
+ */
+#undef HAVE_SYS_NDIR_H
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#undef HAVE_SYS_PARAM_H
+
+/* Define to 1 if you have the <sys/poll.h> header file. */
+#undef HAVE_SYS_POLL_H
+
+/* Define to 1 if you have the <sys/select.h> header file. */
+#undef HAVE_SYS_SELECT_H
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#undef HAVE_SYS_SOCKET_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/syslog.h> header file. */
+#undef HAVE_SYS_SYSLOG_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <sys/uio.h> header file. */
+#undef HAVE_SYS_UIO_H
+
+/* Define to 1 if you have the <sys/un.h> header file. */
+#undef HAVE_SYS_UN_H
+
+/* Define to 1 if you have the <sys/utime.h> header file. */
+#undef HAVE_SYS_UTIME_H
+
+/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */
+#undef HAVE_SYS_WAIT_H
+
+/* Define to 1 if you have the `tmpfile' function. */
+#undef HAVE_TMPFILE
+
+/* Define to 1 if you have the `truncate' function. */
+#undef HAVE_TRUNCATE
+
+/* Define to 1 if you have the `uname' function. */
+#undef HAVE_UNAME
+
+/* Define to 1 if the system has the type `union wait'. */
+#undef HAVE_UNION_WAIT
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to 1 if you have the <utime.h> header file. */
+#undef HAVE_UTIME_H
+
+/* Define to 1 if you have the `vfork' function. */
+#undef HAVE_VFORK
+
+/* Define to 1 if you have the <vfork.h> header file. */
+#undef HAVE_VFORK_H
+
+/* Define to 1 if you have the `wait' function. */
+#undef HAVE_WAIT
+
+/* Define to 1 if you have the `wait4' function. */
+#undef HAVE_WAIT4
+
+/* Define to 1 if you have the `waitpid' function. */
+#undef HAVE_WAITPID
+
+/* Define to 1 if you have the <wchar.h> header file. */
+#undef HAVE_WCHAR_H
+
+/* Define to 1 if you have the `wcrtomb' function. */
+#undef HAVE_WCRTOMB
+
+/* Define to 1 if you have the `wcwidth' function. */
+#undef HAVE_WCWIDTH
+
+/* Define to 1 if `fork' works. */
+#undef HAVE_WORKING_FORK
+
+/* Define to 1 if `vfork' works. */
+#undef HAVE_WORKING_VFORK
+
+/* Default configuration value */
+#undef INBOX_NAME
+
+/* Default configuration value */
+#undef INTERRUPTED_MAIL
+
+/* Enable keyboard lock support */
+#undef KEYBOARD_LOCK
+
+/* Define if you use OpenLDAP 2.3.x deprecated functions */
+#undef LDAP_DEPRECATED
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+ */
+#undef LT_OBJDIR
+
+/* Path to local inboxes for pico newmail check */
+#undef MAILDIR
+
+/* Default configuration value */
+#undef MAX_FILLCOL
+
+/* Default configuration value */
+#undef MAX_SCREEN_COLS
+
+/* Default configuration value */
+#undef MAX_SCREEN_ROWS
+
+/* File mode used to set readonly access */
+#undef MODE_READONLY
+
+/* Compile in mouse support */
+#undef MOUSE
+
+/* Disallow users changing their From address */
+#undef NEVER_ALLOW_CHANGING_FROM
+
+/* Default configuration value */
+#undef NUMDEBUGFILES
+
+/* OSX TARGET */
+#undef OSX_TARGET
+
+/* Name of package */
+#undef PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Password cache file (NOT secure. NOT recommended) */
+#undef PASSFILE
+
+/* Program users use to change their password */
+#undef PASSWD_PROG
+
+/* Define if system supports POSIX signal interface */
+#undef POSIX_SIGNALS
+
+/* Default configuration value */
+#undef POSTPONED_MAIL
+
+/* Default configuration value */
+#undef POSTPONED_MSGS
+
+/* Define to necessary symbol if this constant uses a non-standard name on
+ your system. */
+#undef PTHREAD_CREATE_JOINABLE
+
+/* Include support for UW Pubcookie Web Authentication */
+#undef PUBCOOKIE
+
+/* Define as the return type of signal handlers (`int' or `void'). */
+#undef RETSIGTYPE
+
+/* Define to the type of arg 1 for `select'. */
+#undef SELECT_TYPE_ARG1
+
+/* Define to the type of args 2, 3 and 4 for `select'. */
+#undef SELECT_TYPE_ARG234
+
+/* Define to the type of arg 5 for `select'. */
+#undef SELECT_TYPE_ARG5
+
+/* Local mail submission agent */
+#undef SENDMAIL
+
+/* Local MSA flags for SMTP on stdin/stdout */
+#undef SENDMAILFLAGS
+
+/* Posting agent to use when no nntp-servers defined */
+#undef SENDNEWS
+
+/* The size of `unsigned int', as computed by sizeof. */
+#undef SIZEOF_UNSIGNED_INT
+
+/* The size of `unsigned long', as computed by sizeof. */
+#undef SIZEOF_UNSIGNED_LONG
+
+/* The size of `unsigned short', as computed by sizeof. */
+#undef SIZEOF_UNSIGNED_SHORT
+
+/* Enable S/MIME code */
+#undef SMIME
+
+/* Directory where S/MIME CACerts are located */
+#undef SMIME_SSLCERTS
+
+/* Simple spell checker: reads stdin, emits misspellings on stdout */
+#undef SPELLER
+
+/* Define to 1 if the `S_IS*' macros in <sys/stat.h> do not work properly. */
+#undef STAT_MACROS_BROKEN
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* System pinerc */
+#undef SYSTEM_PINERC
+
+/* System fixed pinerc */
+#undef SYSTEM_PINERC_FIXED
+
+/* Pine-Centric Host Specifier */
+#undef SYSTYPE
+
+/* Define if system supports SYSV signal interface */
+#undef SYSV_SIGNALS
+
+/* File name separator as string constant */
+#undef S_FILESEP
+
+/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
+#undef TIME_WITH_SYS_TIME
+
+/* Define to 1 if your <sys/time.h> declares `struct tm'. */
+#undef TM_IN_SYS_TIME
+
+/* Default configuration value */
+#undef TRASH_FOLDER
+
+/* System defined unsigned 16 bit integer */
+#undef UINT16
+
+/* System defined unsigned 32 bit integer */
+#undef UINT32
+
+/* Compile in quota check on startup */
+#undef USE_QUOTAS
+
+/* Version number of package */
+#undef VERSION
+
+/* Windows is just too different */
+#undef _WINDOWS
+
+/* Enable extended pthread features on Solaris */
+#undef __EXTENSIONS__
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#undef gid_t
+
+/* Define to `int' if <sys/types.h> does not define. */
+#undef mode_t
+
+/* Define to `int' if <sys/types.h> does not define. */
+#undef pid_t
+
+/* qsort compare function argument type */
+#undef qsort_t
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+#undef size_t
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#undef uid_t
+
+/* Define as `fork' if `vfork' does not work. */
+#undef vfork
diff --git a/include/config.wnt.h b/include/config.wnt.h
new file mode 100644
index 00000000..c025971b
--- /dev/null
+++ b/include/config.wnt.h
@@ -0,0 +1,581 @@
+/* include/config.h.in. Generated from configure.ac by autoheader. */
+
+/* Default configuration value */
+#define ANSI_PRINTER "attached-to-ansi"
+
+/* Enable background posting support */
+#undef BACKGROUND_POST
+
+/* Default configuration value */
+#define CHECK_POINT_FREQ 12
+
+/* Default configuration value */
+#define CHECK_POINT_TIME 420
+
+/* File name separator as character constant */
+#define C_FILESEP '\\'
+/* Avoid OSX Conflict */
+/* #undef Comment */
+
+/* Default configuration value */
+#define DEADLETTER "deadletr"
+
+/* Compile in debugging */
+#define DEBUG
+
+/* Default configuration value */
+#define DEBUGFILE "pinedebg.txt"
+
+/* Display debug messages in journal */
+#define DEBUGJOURNAL
+
+/* Default configuration value */
+#define DEFAULT_COLUMNS_ON_TERMINAL 80
+
+/* Default configuration value */
+#define DEFAULT_DEBUG 2
+
+/* Default configuration value */
+#define DEFAULT_LINES_ON_TERMINAL 25
+
+/* Default configuration value */
+#define DEFAULT_SAVE "savemail"
+
+/* Default configuration value */
+#define DF_AB_SORT_RULE "fullname-with-lists-last"
+
+/* Default configuration value */
+#define DF_ADDRESSBOOK "addrbook"
+
+/* Default configuration value */
+#define DF_DEFAULT_FCC "sentmail"
+
+/* Default configuration value */
+#define DF_DEFAULT_PRINTER ANSI_PRINTER
+
+/* Default configuration value */
+#define DF_ELM_STYLE_SAVE "no"
+
+/* Default configuration value */
+#define DF_FCC_RULE "default-fcc"
+
+/* Default configuration value */
+#define DF_FILLCOL "74"
+
+/* Default configuration value */
+#define DF_FLD_SORT_RULE "alphabetical"
+
+/* Default configuration value */
+#define DF_HEADER_IN_REPLY "no"
+
+/* Default configuration value */
+#define DF_KBLOCK_PASSWD_COUNT "1"
+
+/* Default configuration value */
+#define DF_LOCAL_ADDRESS "postmaster"
+
+/* Default configuration value */
+#define DF_LOCAL_FULLNAME "Local Support"
+
+/* Default configuration value */
+#define DF_MAILCHECK "150"
+
+/* Default configuration value */
+#define DF_MAIL_DIRECTORY "mail"
+
+/* Default configuration value */
+#define DF_MARGIN "0"
+
+/* Default configuration value */
+#define DF_OLD_STYLE_REPLY "no"
+
+/* Default configuration value */
+#define DF_OVERLAP "2"
+
+#define DF_PINEDIR "\\pine"
+
+/* Default configuration value */
+#define DF_REMOTE_ABOOK_HISTORY "3"
+
+/* Default configuration value */
+#define DF_SAVED_MSG_NAME_RULE "default-folder"
+
+/* Default configuration value */
+#define DF_SAVE_BY_SENDER "no"
+
+/* Default configuration value */
+#define DF_SIGNATURE_FILE "pine.sig"
+
+/* Default configuration value */
+#define DF_SORT_KEY "arrival"
+
+/* Default configuration value */
+#define DF_STANDARD_PRINTER "lpr"
+
+/* Default configuration value */
+#define DF_USE_ONLY_DOMAIN_NAME "no"
+
+/* Define enable dmalloc debugging */
+/* #undef ENABLE_DMALLOC */
+
+/* Enable LDAP query support - the build command defines this */
+/* #define ENABLE_LDAP */
+
+/* Define to 1 if translation of program messages to the user's native
+ language is requested. */
+/* #define ENABLE_NLS 1 */ /* What should this be? - jpf */
+
+/* Enable From address encoding in sent messages */
+/* #undef ENCODE_FROMS */
+
+#define FORWARDED_FLAG "$Forwarded"
+
+/* Avoid OSX Conflict */
+/* #undef Fixed */
+
+/* Define to 1 if `TIOCGWINSZ' requires <sys/ioctl.h>. */
+/* #undef GWINSZ_IN_SYS_IOCTL */
+
+/* Define if systems uses old BSD-style terminal control */
+/* #undef HAS_SGTTY */
+
+/* Define if systems uses termcap terminal database */
+/* #undef HAS_TERMCAP */
+
+/* Define if systems uses terminfo terminal database */
+/* #undef HAS_TERMINFO */
+
+/* Define if systems uses termio terminal control */
+/* #undef HAS_TERMIO */
+
+/* Define if systems uses termios terminal control */
+/* #undef HAS_TERMIOS */
+
+/* Define to 1 if you have the `bind' function. */
+/* #undef HAVE_BIND */
+
+/* Define to 1 if you have the MacOS X function CFLocaleCopyCurrent in the
+ CoreFoundation framework. */
+/* #undef HAVE_CFLOCALECOPYCURRENT */
+
+/* Define to 1 if you have the MacOS X function CFPreferencesCopyAppValue in
+ the CoreFoundation framework. */
+/* #undef HAVE_CFPREFERENCESCOPYAPPVALUE */
+
+/* Define to 1 if you have the `chown' function. */
+/* #undef HAVE_CHOWN */
+
+/* Define if the GNU dcgettext() function is already present or preinstalled.
+ */
+/* #undef HAVE_DCGETTEXT */
+
+/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.
+ */
+/* #undef HAVE_DIRENT_H */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+/* #define HAVE_DLFCN_H */
+
+/* Define to 1 if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the `fork' function. */
+/* #define HAVE_FORK */
+
+/* Define to 1 if you have the `fsync' function. */
+/* #define HAVE_FSYNC */
+
+/* Define to 1 if you have the `getpwnam' function. */
+/* #define HAVE_GETPWNAM */
+
+/* Define to 1 if you have the `getpwuid' function. */
+/* #define HAVE_GETPWUID */
+
+/* Define if the GNU gettext() function is already present or preinstalled. */
+/* #define HAVE_GETTEXT */
+
+/* Define to 1 if you have the `gettimeofday' function. */
+/* #define HAVE_GETTIMEOFDAY */
+
+/* Define to 1 if you have the `getuid' function. */
+/* #define HAVE_GETUID */
+
+/* Define if you have the iconv() function. */
+/* #define HAVE_ICONV */
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+/* #define HAVE_INTTYPES_H */
+
+/* Define to 1 if you have the `dmalloc' library (-ldmalloc). */
+/* #define HAVE_LIBDMALLOC */
+
+/* Define to 1 if you have the `tcl' library (-ltcl). */
+/* #define HAVE_LIBTCL */
+
+/* Define to 1 if you have the <limits.h> header file. */
+#define HAVE_LIMITS_H 1
+
+/* Define to 1 if you have the `listen' function. */
+#define HAVE_LISTEN 1
+
+/* Define to 1 if you have the <locale.h> header file. */
+#define HAVE_LOCALE_H 1
+
+/* Define to 1 if you have the `mbstowcs' function. */
+#define HAVE_MBSTOWCS 1
+
+/* Define to 1 if you have the `memcpy' function. */
+#define HAVE_MEMCPY 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
+/* #define HAVE_NDIR_H */
+
+/* Define to 1 if you have the <netdb.h> header file. */
+/* #define HAVE_NETDB_H */
+
+/* Define to 1 if you have the <pwd.h> header file. */
+/* #define HAVE_PWD_H */
+
+/* Define to 1 if you have the `pclose' function. */
+/* #define HAVE_PCLOSE 1 */
+
+/* Define to 1 if you have the `poll' function. */
+/* #define HAVE_POLL */
+
+/* Define to 1 if you have the `popen' function. */
+/* #define HAVE_POPEN 1 */
+
+/* Define to 1 if you have the `qsort' function. */
+#define HAVE_QSORT 1
+
+/* Define to 1 if you have the `read' function. */
+#define HAVE_READ 1
+
+/* Define to 1 if you have the `rename' function. */
+/* #define HAVE_RENAME */
+
+/* Define to 1 if you have the `select' function. */
+#define HAVE_SELECT 1
+
+/* Define to 1 if you have the `setjmp' function. */
+#define HAVE_SETJMP 1
+
+/* Define to 1 if you have the <setjmp.h> header file. */
+#define HAVE_SETJMP_H 1
+
+/* Define to 1 if you have the `sigaction' function. */
+/* #define HAVE_SIGACTION */
+
+/* Define to 1 if you have the `sigaddset' function. */
+/* #define HAVE_SIGADDSET */
+
+/* Define to 1 if you have the `sigemptyset' function. */
+/* #define HAVE_SIGEMPTYSET */
+
+/* Define to 1 if you have the `signal' function. */
+#define HAVE_SIGNAL 1
+
+/* Define to 1 if you have the <signal.h> header file. */
+#define HAVE_SIGNAL_H 1
+
+/* Define to 1 if you have the `sigprocmask' function. */
+/* #define HAVE_SIGPROCMASK */
+
+/* Define to 1 if you have the `sigrelse' function. */
+/* #define HAVE_SIGRELSE */
+
+/* Define to 1 if you have the `sigset' function. */
+/* #define HAVE_SIGSET */
+
+/* Define to 1 if you have the `srandom' function. */
+/* #define HAVE_SRANDOM */
+
+/* Define to 1 if you have the <stdint.h> header file. */
+/* #define HAVE_STDINT_H */
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the `strchr' function. */
+#define HAVE_STRCHR 1
+
+/* Define to 1 if you have the `strcoll' function and it is properly defined.
+ */
+#define HAVE_STRCOLL 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+/* #define HAVE_STRINGS_H */
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <stropts.h> header file. */
+/* #define HAVE_STROPTS_H */
+
+/* Define to 1 if you have the `strtol' function. */
+#define HAVE_STRTOL 1
+
+/* Define to 1 if you have the `strtoul' function. */
+#define HAVE_STRTOUL 1
+
+/* Define to 1 if you have the <syslog.h> header file. */
+/* #define HAVE_SYSLOG_H */
+
+/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.
+ */
+/* #define HAVE_SYS_DIR_H */
+
+/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'.
+ */
+/* #define HAVE_SYS_NDIR_H */
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+/* #define HAVE_SYS_PARAM_H */
+
+/* Define to 1 if you have the <sys/poll.h> header file. */
+/* #define HAVE_SYS_POLL_H */
+
+/* Define to 1 if you have the <sys/select.h> header file. */
+/* #define HAVE_SYS_SELECT_H */
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+/* #define HAVE_SYS_SOCKET_H */
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <sys/un.h> header file. */
+/* #define HAVE_SYS_UN_H */
+
+#define HAVE_SYS_UTIME_H 1
+
+/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */
+/* #define HAVE_SYS_WAIT_H */
+
+/* Define to 1 if you have the <term.h> header file. */
+/* #define HAVE_TERM_H */
+
+/* Define to 1 if you have the `tmpfile' function. */
+#define HAVE_TMPFILE 1
+
+/* Define to 1 if you have the `truncate' function. */
+/* #define HAVE_TRUNCATE */
+
+/* Define to 1 if you have the `uname' function. */
+/* #define HAVE_UNAME */
+
+/* Define to 1 if the system has the type `union wait'. */
+/* #define HAVE_UNION_WAIT */
+
+/* Define to 1 if you have the <unistd.h> header file. */
+/* #define HAVE_UNISTD_H */
+
+/* Define to 1 if you have the `vfork' function. */
+/* #define HAVE_VFORK */
+
+/* Define to 1 if you have the <vfork.h> header file. */
+/* #define HAVE_VFORK_H */
+
+/* Define to 1 if you have the `wait' function. */
+/* #define HAVE_WAIT */
+
+/* Define to 1 if you have the `wait4' function. */
+/* #define HAVE_WAIT4 */
+
+/* Define to 1 if you have the `waitpid' function. */
+/* #define HAVE_WAITPID */
+
+/* Define to 1 if you have the <wchar.h> header file. */
+#define HAVE_WCHAR_H 1
+
+/* Define to 1 if `fork' works. */
+/* #define HAVE_WORKING_FORK */
+
+/* Define to 1 if `vfork' works. */
+/* #define HAVE_WORKING_VFORK */
+
+/* Always 1 because we use regex/regex.h */
+#define HAVE_REGEX_H 1
+
+/* Avoid OSX Conflict */
+/* #undef Handle */
+
+/* Default configuration value */
+#define INBOX_NAME "INBOX"
+
+/* Default configuration value */
+#define INTERRUPTED_MAIL "intruptd"
+
+/* Path to local inboxes for pico newmail check */
+#undef MAILDIR
+
+/* Default configuration value */
+#define MAX_FILLCOL 80
+
+/* Default configuration value */
+#define MAX_SCREEN_COLS 500
+
+/* Default configuration value */
+#define MAX_SCREEN_ROWS 200
+
+/* File mode used to set readonly access */
+#undef MODE_READONLY
+
+/* Compile in mouse support */
+#define MOUSE
+/* Windows has this set */
+#define EX_MOUSE
+
+/* Disallow users changing their From address */
+/* #undef NEVER_ALLOW_CHANGING_FROM */
+
+/* Disable keyboard lock support */
+/* #undef NO_KEYBOARD_LOCK */
+
+/* Default configuration value */
+#define NUMDEBUGFILES 4
+
+/* Name of package */
+#define PACKAGE "alpine"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "alpine-contact@u.washington.edu"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "alpine"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "alpine 2.10"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "alpine"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "2.10"
+
+/* Program users use to change their password */
+/* #undef PASSWD_PROG */
+
+/* Define if system supports POSIX signal interface */
+/* #define POSIX_SIGNALS */
+
+/* Default configuration value */
+#define POSTPONED_MAIL "postpone"
+
+/* Default configuration value */
+#define POSTPONED_MSGS "postpond"
+
+/* Default Trash folder for Web Alpine */
+#define TRASH_FOLDER "Trash"
+
+/* Define as the return type of signal handlers (`int' or `void'). */
+#define RETSIGTYPE void
+
+/* Define to the type of arg 1 for `select'. */
+#define SELECT_TYPE_ARG1 int
+
+/* Define to the type of args 2, 3 and 4 for `select'. */
+#define SELECT_TYPE_ARG234 (fd_set *)
+
+/* Define to the type of arg 5 for `select'. */
+#define SELECT_TYPE_ARG5 (struct timeval *)
+
+/* Local mail submission agent */
+/* #define SENDMAIL */
+
+/* Local MSA flags for SMTP on stdin/stdout */
+/* #define SENDMAILFLAGS */
+
+/* Posting agent to use when no nntp-servers defined */
+/* #define SENDNEWS */
+
+/* The size of a `unsigned int', as computed by sizeof. */
+/* #define SIZEOF_UNSIGNED_INT */
+
+/* The size of a `unsigned long', as computed by sizeof. */
+/* #define SIZEOF_UNSIGNED_LONG */
+
+/* The size of a `unsigned short', as computed by sizeof. */
+/* #define SIZEOF_UNSIGNED_SHORT */
+
+/* Program pico uses to check spelling */
+#define SPELLER
+
+/* Define to 1 if the `S_IS*' macros in <sys/stat.h> do not work properly. */
+/* #define STAT_MACROS_BROKEN */
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Default configuration value */
+#define SYSTEM_PINERC "pinerc"
+
+/* Default configuration value */
+/* #define SYSTEM_PINERC_FIXED */
+
+/* Pine-Centric Host Specifier */
+#define SYSTYPE "WNT"
+
+/* Define if system supports SYSV signal interface */
+/* #define SYSV_SIGNALS */
+
+/* File name separator as string constant */
+#define S_FILESEP "\\"
+
+/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
+/* #define TIME_WITH_SYS_TIME */
+
+/* Define to 1 if your <sys/time.h> declares `struct tm'. */
+/* #define TM_IN_SYS_TIME */
+
+/* System defined unsigned 16 bit integer */
+/* #define UINT16 uint16_t */
+/* #define UINT16 unsigned int */
+
+/* System defined unsigned 32 bit integer */
+/* #define UINT32 uint32_t */
+/* #define UINT32 unsigned long */
+
+/* Compile in quota check on startup */
+/* #define USE_QUOTAS */
+
+/* Enable internal utf8 handling */
+#define UTF8_INTERNAL
+
+/* Version number of package */
+#define VERSION "2.10"
+
+/* Windows is just too different */
+#ifndef _WINDOWS
+#define _WINDOWS
+#endif
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #define gid_t */
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #define mode_t */
+
+/* Define to `int' if <sys/types.h> does not define. */
+#define pid_t int
+
+/* qsort compare function argument type */
+#define qsort_t void
+
+/* Define to `unsigned' if <sys/types.h> does not define. */
+/* #define size_t */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #define uid_t */
+
+/* Define as `fork' if `vfork' does not work. */
+/* #define vfork */
diff --git a/include/general.h b/include/general.h
new file mode 100644
index 00000000..500a5e74
--- /dev/null
+++ b/include/general.h
@@ -0,0 +1,134 @@
+/*----------------------------------------------------------------------
+ $Id: general.h 764 2007-10-23 23:44:49Z hubert@u.washington.edu $
+ ----------------------------------------------------------------------*/
+
+/* ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+*/
+
+
+#ifndef _GENERAL_INCLUDED
+#define _GENERAL_INCLUDED
+
+
+#include "system.h"
+
+/*
+ * Generally useful definitions and constants
+ */
+
+
+/* Might as well be consistant */
+#undef FALSE
+#define FALSE 0 /* False, no, bad, etc. */
+#undef TRUE
+#define TRUE 1 /* True, yes, good, etc. */
+#define ABORT 2 /* Death, ^G, abort, etc. */
+
+
+#undef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#undef MAX
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+
+
+#ifdef O_BINARY
+#define STDIO_READ "rb"
+#define STDIO_APPEND "a+b"
+#else /* !O_BINARY */
+#define O_BINARY 0
+#define STDIO_READ "r"
+#define STDIO_APPEND "a+"
+#endif /* !O_BINARY */
+
+#ifndef O_TEXT
+#define O_TEXT 0
+#endif /* !O_TEXT */
+
+#ifndef _O_WTEXT
+#define _O_WTEXT 0
+#endif /* !O_WTEXT */
+
+#ifndef _O_U8TEXT
+#define _O_U8TEXT 0
+#endif /* !O_U8TEXT */
+
+
+/* Various character constants */
+#define BACKSPACE '\b' /* backspace character */
+#define TAB '\t' /* tab character */
+#define RETURN '\r' /* carriage return char */
+#define LINE_FEED '\n' /* line feed character */
+#define FORMFEED '\f' /* form feed (^L) char */
+#define COMMA ',' /* comma character */
+#define SPACE ' ' /* space character */
+#define ESCAPE '\033' /* the escape */
+#define BELL '\007' /* the bell */
+#define LPAREN '(' /* left parenthesis */
+#define RPAREN ')' /* right parenthesis */
+#define BSLASH '\\' /* back slash */
+#define QUOTE '"' /* double quote char */
+#define DEL '\177' /* delete */
+
+/*
+ * These help with isspace when dealing with UCS-4 characters.
+ * 0x3000 == Ideographic Space (as wide as a CJK character cell)
+ * 0x2002 == En Space
+ * 0x2003 == Em Space
+ */
+#define SPECIAL_SPACE(c) ((c) == 0x3000 || (c) == 0x2002 || (c) == 0x2003)
+
+
+
+
+/* Longest foldername we ever expect */
+#define MAXFOLDER (128)
+
+
+/* Various maximum field lengths, probably shouldn't be changed. */
+#define MAX_FULLNAME (100)
+#define MAX_NICKNAME (80)
+#define MAX_ADDRESS (500)
+#define MAX_NEW_LIST (500) /* Max addrs to be added when creating list */
+#define MAX_SEARCH (100) /* Longest string to search for */
+#define MAX_ADDR_EXPN (1000) /* Longest expanded addr */
+#define MAX_ADDR_FIELD (10000) /* Longest fully-expanded addr field */
+
+/*
+ * Input timeout fudge factor
+ */
+#define IDLE_TIMEOUT (8)
+#define FUDGE (30) /* better be at least 20 */
+
+
+/*
+ * We use a 32 bit unsigned int to carry UCS-4 characters.
+ * C-client uses unsigned long for this, so we have to do
+ * some minor conversion at that interface. UCS-4 characters
+ * are really only 21 bits. We do use the extra space for
+ * defining some special values that a character might have.
+ * In particular, the set of values like KEY_UP, KEY_RESIZE,
+ * NO_OP_IDLE, F12, and so on are 32 bit quantities that don't
+ * interfere with the actual character values. They are also
+ * all positive values with the most significant bit set to 0,
+ * so a 32 bit signed integer could hold them all.
+ */
+typedef UINT32 UCS;
+
+/*
+ * The type of an IMAP UID, which is a 32-bit unsigned int.
+ * This could be UINT32 instead of unsigned long but we use
+ * unsigned long because that's what c-client uses.
+ */
+typedef unsigned long imapuid_t;
+
+
+#endif /* _GENERAL_INCLUDED */
diff --git a/include/system.h b/include/system.h
new file mode 100644
index 00000000..636eb82b
--- /dev/null
+++ b/include/system.h
@@ -0,0 +1,392 @@
+/*----------------------------------------------------------------------
+ $Id: system.h 776 2007-10-24 23:14:26Z hubert@u.washington.edu $
+ ----------------------------------------------------------------------*/
+
+/* ========================================================================
+ * Copyright 2006-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+*/
+
+/*
+ * System-Specific includes, macros and prototypes based on config.h definitions
+ */
+
+
+#ifndef _SYSTEM_INCLUDED
+#define _SYSTEM_INCLUDED
+
+#ifndef _WINDOWS
+#include "config.h"
+#else
+#include "config.wnt.h"
+#endif
+
+#include <stdio.h>
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifdef HAVE_CTYPE_H
+# include <ctype.h>
+#endif
+
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+
+# if HAVE_STRING_H
+# include <string.h>
+# endif
+
+# if HAVE_STRINGS_H
+# include <strings.h>
+# endif
+
+# if HAVE_WCHAR_H
+# include <wchar.h>
+# endif
+
+# include <stdarg.h>
+
+#else
+void *malloc (size_t);
+void *realloc (void *, size_t);
+void free (void *);
+size_t strlen (const char *);
+int strcmp (const char *, const char *);
+char *strcpy (char *, const char *);
+char *strcat (char *, const char *);
+char *strtok (char *, const char *);
+# ifndef HAVE_STRCHR
+# warn "No strchr(); trying to use index() instead."
+# define strchr index
+# define strrchr rindex
+# endif
+# ifndef HAVE_STRDUP
+# error "C library missing strdup()!"
+# endif
+char *strdup (const char *);
+char *strstr (const char *, const char *);
+# ifdef HAVE_MEMORY_H
+# include <memory.h>
+# else
+# ifdef HAVE_MEMCPY
+void *memcpy(void *, const void *, size_t);
+void *memmove(void *, const void *, size_t);
+void *memset(void *, int, size_t);
+# else
+# define memcpy(d, s, n) bcopy ((s), (d), (n))
+# define memmove(d, s, n) bcopy ((s), (d), (n))
+# define memset(d, c, n) bzero ((d), (n))
+# endif
+# endif
+#endif
+
+/* Set System Collator */
+#if HAVE_STRCOLL
+# define collator strcoll
+#else
+# define collator strucmp
+#endif
+
+#define PREREQ_FOR_SYS_TRANSLATION (HAVE_WCHAR_H && HAVE_WCRTOMB && HAVE_WCWIDTH && HAVE_MBSTOWCS && HAVE_LANGINFO_H && defined(CODESET) && !defined(_WINDOWS))
+
+/* System uin32 definition */
+#if HAVE_STDINT_H
+# include <stdint.h>
+#endif
+
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#if HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+/* set MAXPATH based on what's available */
+
+#if HAVE_LIMITS_H
+# include <limits.h>
+#endif
+
+#ifdef PATH_MAX
+# define MAXPATH PATH_MAX
+#else
+# if HAVE_SYS_PARAM_H
+# include <sys/param.h>
+# endif
+# if MAXPATHLEN
+# define MAXPATH MAXPATHLEN
+# else
+# define MAXPATH (512)
+# endif
+#endif
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+
+#if HAVE_UTIME_H
+# include <utime.h>
+#elif HAVE_SYS_UTIME_H
+# include <sys/utime.h>
+#endif
+
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#if defined(O_NONBLOCK)
+# define NON_BLOCKING_IO O_NONBLOCK
+#elif defined(FNDELAY)
+# define NON_BLOCKING_IO FNDELAY
+#endif
+
+#if HAVE_PWD_H
+# include <pwd.h>
+#endif
+
+#if HAVE_SIGNAL_H
+# include <signal.h>
+#endif
+
+#if HAVE_SETJMP_H
+# include <setjmp.h>
+#endif
+
+#if HAS_TERMIOS
+# include <termios.h>
+#endif
+
+#if HAS_TERMIO
+# include <termio.h>
+#endif
+
+#if HAS_SGTTY
+# include <sgtty.h>
+#endif
+
+#if HAVE_NETDB_H
+# include <netdb.h>
+#endif
+
+#if GWINSZ_IN_SYS_IOCTL
+# include <sys/ioctl.h>
+#endif
+
+#if HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#if HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
+#if HAVE_SYS_UN_H
+#include <sys/un.h>
+#else
+struct sockaddr_un {
+ short sun_family; /* AF_UNIX */
+ char sun_path[108]; /* path name (gag) */
+};
+#endif
+
+#if HAVE_PTHREAD_H
+# include <pthread.h>
+#endif
+
+#if HAVE_ASSERT_H
+# include <assert.h>
+#endif
+
+#if HAVE_LOCALE_H
+# include <locale.h>
+#endif
+
+#if HAVE_LANGINFO_H
+# include <langinfo.h>
+#endif
+
+#if HAVE_REGEX_H
+# include <regex.h>
+#endif
+
+#ifdef ENABLE_NLS
+
+# include <libintl.h>
+
+# define _(String) gettext (String)
+# define N_(String) String
+
+#else /* !ENABLE_NLS */
+
+# define _(String) String
+# define N_(String) String
+
+#endif /* !ENABLE_NLS */
+
+
+#ifdef ENABLE_LDAP
+
+# include <lber.h>
+# include <ldap.h>
+
+# ifndef LDAPAPI
+# if defined(LDAP_API_VERSION) /* draft-ietf-ldapext-ldap-c-api-04 */
+# define LDAPAPI LDAP_API_VERSION
+# elif defined(LDAP_OPT_SIZELIMIT)
+# define LDAPAPI 15 /* Netscape SDK */
+# elif defined(LDAP_BEGIN_DECL)
+# define LDAPAPI 11 /* OpenLDAP 1.x */
+# else /* older version */
+# define LDAPAPI 10 /* Umich */
+# endif
+
+# ifndef LDAP_OPT_ON
+# define LDAP_OPT_ON ((void *)1)
+# endif
+# ifndef LDAP_OPT_OFF
+# define LDAP_OPT_OFF ((void *)0)
+# endif
+# ifndef LDAP_OPT_SIZELIMIT
+# define LDAP_OPT_SIZELIMIT 1134 /* we're hacking now! */
+# endif
+# ifndef LDAP_OPT_TIMELIMIT
+# define LDAP_OPT_TIMELIMIT 1135
+# endif
+# ifndef LDAP_OPT_PROTOCOL_VERSION
+# define LDAP_OPT_PROTOCOL_VERSION 1136
+# endif
+
+# ifndef LDAP_MSG_ONE
+# define LDAP_MSG_ONE (0x00)
+# define LDAP_MSG_ALL (0x01)
+# define LDAP_MSG_RECEIVED (0x02)
+# endif
+# endif
+#endif /* ENABLE_LDAP */
+
+
+#if defined(DOS) || defined(OS2)
+# define NEWLINE "\r\n" /* local EOL convention... */
+#else
+# define NEWLINE "\n" /* necessary for gf_* funcs */
+#endif
+
+#if OSX_TARGET
+# include <Carbon/Carbon.h>
+# include <ApplicationServices/ApplicationServices.h>
+
+/* Avoid OSX Conflicts */
+#define Fixed PineFixed
+#define Handle PineHandle
+#define Comment PineComment
+#define normal PineNormal
+#endif
+
+#if HAVE_SYSLOG_H
+# include <syslog.h>
+#elif HAVE_SYS_SYSLOG_H
+# include <sys/syslog.h>
+#endif
+
+#ifdef ENABLE_DMALLOC
+# include <dmalloc.h>
+#endif
+
+#ifdef _WINDOWS
+
+/*
+ * Defining these turns generic data types and function calls
+ * into their unicode (wide) versions. Not defining it will
+ * break the code, so leave it defined.
+ */
+#ifndef _UNICODE
+# define _UNICODE
+#endif
+#ifndef UNICODE
+# define UNICODE
+#endif
+
+#define _WIN32_WINNT WINVER
+
+/*-------- Standard windows defines and then our window module defines. */
+#include <dos.h>
+#include <direct.h>
+#include <search.h>
+#undef CTRL
+#include <io.h>
+#include <windows.h>
+#include <commdlg.h>
+#include <cderr.h>
+#include <winsock.h>
+#include <shellapi.h>
+#include <sys/timeb.h>
+#include <tchar.h>
+#include <wchar.h>
+#include <assert.h>
+
+#if defined(WINVER) && WINVER >= 0x0501
+# define WINCRED 1 /* WINCRED will work */
+#else
+# define WINCRED 0 /* too old for WINCRED to work */
+#endif
+
+#undef ERROR
+
+typedef int mode_t;
+typedef int uid_t;
+typedef int gid_t;
+
+#undef snprintf
+#define snprintf _snprintf
+#undef CM_NONE
+
+#undef utimbuf
+#define utimbuf _utimbuf
+
+#endif /* _WINDOWS */
+
+
+#if defined(PASSFILE) && defined(APPLEKEYCHAIN)
+# error "Cant define both PASSFILE and APPLEKEYCHAIN"
+#endif
+#if defined(PASSFILE) && defined(WINCRED)
+# error "Cant define both PASSFILE and WINCRED"
+#endif
+#if defined(APPLEKEYCHAIN) && defined(WINCRED)
+# error "Cant define both APPLEKEYCHAIN and WINCRED"
+#endif
+
+#if defined(PASSFILE) || defined(APPLEKEYCHAIN) || defined(WINCRED)
+# define LOCAL_PASSWD_CACHE
+#endif
+
+#endif /* _SYSTEM_INCLUDED */
diff --git a/install-sh b/install-sh
new file mode 100755
index 00000000..6781b987
--- /dev/null
+++ b/install-sh
@@ -0,0 +1,520 @@
+#!/bin/sh
+# install - install a program, script, or datafile
+
+scriptversion=2009-04-28.21; # UTC
+
+# This originates from X11R5 (mit/util/scripts/install.sh), which was
+# later released in X11R6 (xc/config/util/install.sh) with the
+# following copyright and license.
+#
+# Copyright (C) 1994 X Consortium
+#
+# 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
+# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
+# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# Except as contained in this notice, the name of the X Consortium shall not
+# be used in advertising or otherwise to promote the sale, use or other deal-
+# ings in this Software without prior written authorization from the X Consor-
+# tium.
+#
+#
+# FSF changes to this file are in the public domain.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+
+nl='
+'
+IFS=" "" $nl"
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit=${DOITPROG-}
+if test -z "$doit"; then
+ doit_exec=exec
+else
+ doit_exec=$doit
+fi
+
+# Put in absolute file names if you don't have them in your path;
+# or use environment vars.
+
+chgrpprog=${CHGRPPROG-chgrp}
+chmodprog=${CHMODPROG-chmod}
+chownprog=${CHOWNPROG-chown}
+cmpprog=${CMPPROG-cmp}
+cpprog=${CPPROG-cp}
+mkdirprog=${MKDIRPROG-mkdir}
+mvprog=${MVPROG-mv}
+rmprog=${RMPROG-rm}
+stripprog=${STRIPPROG-strip}
+
+posix_glob='?'
+initialize_posix_glob='
+ test "$posix_glob" != "?" || {
+ if (set -f) 2>/dev/null; then
+ posix_glob=
+ else
+ posix_glob=:
+ fi
+ }
+'
+
+posix_mkdir=
+
+# Desired mode of installed file.
+mode=0755
+
+chgrpcmd=
+chmodcmd=$chmodprog
+chowncmd=
+mvcmd=$mvprog
+rmcmd="$rmprog -f"
+stripcmd=
+
+src=
+dst=
+dir_arg=
+dst_arg=
+
+copy_on_change=false
+no_target_directory=
+
+usage="\
+Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
+ or: $0 [OPTION]... SRCFILES... DIRECTORY
+ or: $0 [OPTION]... -t DIRECTORY SRCFILES...
+ or: $0 [OPTION]... -d DIRECTORIES...
+
+In the 1st form, copy SRCFILE to DSTFILE.
+In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
+In the 4th, create DIRECTORIES.
+
+Options:
+ --help display this help and exit.
+ --version display version info and exit.
+
+ -c (ignored)
+ -C install only if different (preserve the last data modification time)
+ -d create directories instead of installing files.
+ -g GROUP $chgrpprog installed files to GROUP.
+ -m MODE $chmodprog installed files to MODE.
+ -o USER $chownprog installed files to USER.
+ -s $stripprog installed files.
+ -t DIRECTORY install into DIRECTORY.
+ -T report an error if DSTFILE is a directory.
+
+Environment variables override the default commands:
+ CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
+ RMPROG STRIPPROG
+"
+
+while test $# -ne 0; do
+ case $1 in
+ -c) ;;
+
+ -C) copy_on_change=true;;
+
+ -d) dir_arg=true;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift;;
+
+ --help) echo "$usage"; exit $?;;
+
+ -m) mode=$2
+ case $mode in
+ *' '* | *' '* | *'
+'* | *'*'* | *'?'* | *'['*)
+ echo "$0: invalid mode: $mode" >&2
+ exit 1;;
+ esac
+ shift;;
+
+ -o) chowncmd="$chownprog $2"
+ shift;;
+
+ -s) stripcmd=$stripprog;;
+
+ -t) dst_arg=$2
+ shift;;
+
+ -T) no_target_directory=true;;
+
+ --version) echo "$0 $scriptversion"; exit $?;;
+
+ --) shift
+ break;;
+
+ -*) echo "$0: invalid option: $1" >&2
+ exit 1;;
+
+ *) break;;
+ esac
+ shift
+done
+
+if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
+ # When -d is used, all remaining arguments are directories to create.
+ # When -t is used, the destination is already specified.
+ # Otherwise, the last argument is the destination. Remove it from $@.
+ for arg
+ do
+ if test -n "$dst_arg"; then
+ # $@ is not empty: it contains at least $arg.
+ set fnord "$@" "$dst_arg"
+ shift # fnord
+ fi
+ shift # arg
+ dst_arg=$arg
+ done
+fi
+
+if test $# -eq 0; then
+ if test -z "$dir_arg"; then
+ echo "$0: no input file specified." >&2
+ exit 1
+ fi
+ # It's OK to call `install-sh -d' without argument.
+ # This can happen when creating conditional directories.
+ exit 0
+fi
+
+if test -z "$dir_arg"; then
+ trap '(exit $?); exit' 1 2 13 15
+
+ # Set umask so as not to create temps with too-generous modes.
+ # However, 'strip' requires both read and write access to temps.
+ case $mode in
+ # Optimize common cases.
+ *644) cp_umask=133;;
+ *755) cp_umask=22;;
+
+ *[0-7])
+ if test -z "$stripcmd"; then
+ u_plus_rw=
+ else
+ u_plus_rw='% 200'
+ fi
+ cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
+ *)
+ if test -z "$stripcmd"; then
+ u_plus_rw=
+ else
+ u_plus_rw=,u+rw
+ fi
+ cp_umask=$mode$u_plus_rw;;
+ esac
+fi
+
+for src
+do
+ # Protect names starting with `-'.
+ case $src in
+ -*) src=./$src;;
+ esac
+
+ if test -n "$dir_arg"; then
+ dst=$src
+ dstdir=$dst
+ test -d "$dstdir"
+ dstdir_status=$?
+ else
+
+ # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
+ # might cause directories to be created, which would be especially bad
+ # if $src (and thus $dsttmp) contains '*'.
+ if test ! -f "$src" && test ! -d "$src"; then
+ echo "$0: $src does not exist." >&2
+ exit 1
+ fi
+
+ if test -z "$dst_arg"; then
+ echo "$0: no destination specified." >&2
+ exit 1
+ fi
+
+ dst=$dst_arg
+ # Protect names starting with `-'.
+ case $dst in
+ -*) dst=./$dst;;
+ esac
+
+ # If destination is a directory, append the input filename; won't work
+ # if double slashes aren't ignored.
+ if test -d "$dst"; then
+ if test -n "$no_target_directory"; then
+ echo "$0: $dst_arg: Is a directory" >&2
+ exit 1
+ fi
+ dstdir=$dst
+ dst=$dstdir/`basename "$src"`
+ dstdir_status=0
+ else
+ # Prefer dirname, but fall back on a substitute if dirname fails.
+ dstdir=`
+ (dirname "$dst") 2>/dev/null ||
+ expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$dst" : 'X\(//\)[^/]' \| \
+ X"$dst" : 'X\(//\)$' \| \
+ X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
+ echo X"$dst" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'
+ `
+
+ test -d "$dstdir"
+ dstdir_status=$?
+ fi
+ fi
+
+ obsolete_mkdir_used=false
+
+ if test $dstdir_status != 0; then
+ case $posix_mkdir in
+ '')
+ # Create intermediate dirs using mode 755 as modified by the umask.
+ # This is like FreeBSD 'install' as of 1997-10-28.
+ umask=`umask`
+ case $stripcmd.$umask in
+ # Optimize common cases.
+ *[2367][2367]) mkdir_umask=$umask;;
+ .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
+
+ *[0-7])
+ mkdir_umask=`expr $umask + 22 \
+ - $umask % 100 % 40 + $umask % 20 \
+ - $umask % 10 % 4 + $umask % 2
+ `;;
+ *) mkdir_umask=$umask,go-w;;
+ esac
+
+ # With -d, create the new directory with the user-specified mode.
+ # Otherwise, rely on $mkdir_umask.
+ if test -n "$dir_arg"; then
+ mkdir_mode=-m$mode
+ else
+ mkdir_mode=
+ fi
+
+ posix_mkdir=false
+ case $umask in
+ *[123567][0-7][0-7])
+ # POSIX mkdir -p sets u+wx bits regardless of umask, which
+ # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
+ ;;
+ *)
+ tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
+ trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
+
+ if (umask $mkdir_umask &&
+ exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
+ then
+ if test -z "$dir_arg" || {
+ # Check for POSIX incompatibilities with -m.
+ # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
+ # other-writeable bit of parent directory when it shouldn't.
+ # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
+ ls_ld_tmpdir=`ls -ld "$tmpdir"`
+ case $ls_ld_tmpdir in
+ d????-?r-*) different_mode=700;;
+ d????-?--*) different_mode=755;;
+ *) false;;
+ esac &&
+ $mkdirprog -m$different_mode -p -- "$tmpdir" && {
+ ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
+ test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
+ }
+ }
+ then posix_mkdir=:
+ fi
+ rmdir "$tmpdir/d" "$tmpdir"
+ else
+ # Remove any dirs left behind by ancient mkdir implementations.
+ rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
+ fi
+ trap '' 0;;
+ esac;;
+ esac
+
+ if
+ $posix_mkdir && (
+ umask $mkdir_umask &&
+ $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
+ )
+ then :
+ else
+
+ # The umask is ridiculous, or mkdir does not conform to POSIX,
+ # or it failed possibly due to a race condition. Create the
+ # directory the slow way, step by step, checking for races as we go.
+
+ case $dstdir in
+ /*) prefix='/';;
+ -*) prefix='./';;
+ *) prefix='';;
+ esac
+
+ eval "$initialize_posix_glob"
+
+ oIFS=$IFS
+ IFS=/
+ $posix_glob set -f
+ set fnord $dstdir
+ shift
+ $posix_glob set +f
+ IFS=$oIFS
+
+ prefixes=
+
+ for d
+ do
+ test -z "$d" && continue
+
+ prefix=$prefix$d
+ if test -d "$prefix"; then
+ prefixes=
+ else
+ if $posix_mkdir; then
+ (umask=$mkdir_umask &&
+ $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
+ # Don't fail if two instances are running concurrently.
+ test -d "$prefix" || exit 1
+ else
+ case $prefix in
+ *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
+ *) qprefix=$prefix;;
+ esac
+ prefixes="$prefixes '$qprefix'"
+ fi
+ fi
+ prefix=$prefix/
+ done
+
+ if test -n "$prefixes"; then
+ # Don't fail if two instances are running concurrently.
+ (umask $mkdir_umask &&
+ eval "\$doit_exec \$mkdirprog $prefixes") ||
+ test -d "$dstdir" || exit 1
+ obsolete_mkdir_used=true
+ fi
+ fi
+ fi
+
+ if test -n "$dir_arg"; then
+ { test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
+ { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
+ { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
+ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
+ else
+
+ # Make a couple of temp file names in the proper directory.
+ dsttmp=$dstdir/_inst.$$_
+ rmtmp=$dstdir/_rm.$$_
+
+ # Trap to clean up those temp files at exit.
+ trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
+
+ # Copy the file name to the temp name.
+ (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
+
+ # and set any options; do chmod last to preserve setuid bits.
+ #
+ # If any of these fail, we abort the whole thing. If we want to
+ # ignore errors from any of these, just make sure not to ignore
+ # errors from the above "$doit $cpprog $src $dsttmp" command.
+ #
+ { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
+ { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
+ { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
+ { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
+
+ # If -C, don't bother to copy if it wouldn't change the file.
+ if $copy_on_change &&
+ old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
+ new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
+
+ eval "$initialize_posix_glob" &&
+ $posix_glob set -f &&
+ set X $old && old=:$2:$4:$5:$6 &&
+ set X $new && new=:$2:$4:$5:$6 &&
+ $posix_glob set +f &&
+
+ test "$old" = "$new" &&
+ $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
+ then
+ rm -f "$dsttmp"
+ else
+ # Rename the file to the real destination.
+ $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
+
+ # The rename failed, perhaps because mv can't rename something else
+ # to itself, or perhaps because mv is so ancient that it does not
+ # support -f.
+ {
+ # Now remove or move aside any old file at destination location.
+ # We try this two ways since rm can't unlink itself on some
+ # systems and the destination file might be busy for other
+ # reasons. In this case, the final cleanup might fail but the new
+ # file should still install successfully.
+ {
+ test ! -f "$dst" ||
+ $doit $rmcmd -f "$dst" 2>/dev/null ||
+ { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
+ { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
+ } ||
+ { echo "$0: cannot unlink or rename $dst" >&2
+ (exit 1); exit 1
+ }
+ } &&
+
+ # Now rename the file to the real destination.
+ $doit $mvcmd "$dsttmp" "$dst"
+ }
+ fi || exit 1
+
+ trap '' 0
+ fi
+done
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/ldap/binaries/debug/ldap32.dll b/ldap/binaries/debug/ldap32.dll
new file mode 100755
index 00000000..e9a734b2
--- /dev/null
+++ b/ldap/binaries/debug/ldap32.dll
Binary files differ
diff --git a/ldap/binaries/debug/ldap32.lib b/ldap/binaries/debug/ldap32.lib
new file mode 100755
index 00000000..e5e1231e
--- /dev/null
+++ b/ldap/binaries/debug/ldap32.lib
Binary files differ
diff --git a/ldap/binaries/debug/libldap.dll b/ldap/binaries/debug/libldap.dll
new file mode 100755
index 00000000..52688f37
--- /dev/null
+++ b/ldap/binaries/debug/libldap.dll
Binary files differ
diff --git a/ldap/binaries/debug/libldap.lib b/ldap/binaries/debug/libldap.lib
new file mode 100755
index 00000000..8ae6890a
--- /dev/null
+++ b/ldap/binaries/debug/libldap.lib
Binary files differ
diff --git a/ldap/binaries/debug/ltest.exe b/ldap/binaries/debug/ltest.exe
new file mode 100755
index 00000000..1d7cd6ca
--- /dev/null
+++ b/ldap/binaries/debug/ltest.exe
Binary files differ
diff --git a/ldap/binaries/debug/ltest32.exe b/ldap/binaries/debug/ltest32.exe
new file mode 100755
index 00000000..09a1ac72
--- /dev/null
+++ b/ldap/binaries/debug/ltest32.exe
Binary files differ
diff --git a/ldap/binaries/release/ldap32.dll b/ldap/binaries/release/ldap32.dll
new file mode 100755
index 00000000..2284f9f8
--- /dev/null
+++ b/ldap/binaries/release/ldap32.dll
Binary files differ
diff --git a/ldap/binaries/release/ldap32.lib b/ldap/binaries/release/ldap32.lib
new file mode 100755
index 00000000..506dedd3
--- /dev/null
+++ b/ldap/binaries/release/ldap32.lib
Binary files differ
diff --git a/ldap/binaries/release/libldap.dll b/ldap/binaries/release/libldap.dll
new file mode 100755
index 00000000..16cb6ade
--- /dev/null
+++ b/ldap/binaries/release/libldap.dll
Binary files differ
diff --git a/ldap/binaries/release/libldap.lib b/ldap/binaries/release/libldap.lib
new file mode 100755
index 00000000..8ae6890a
--- /dev/null
+++ b/ldap/binaries/release/libldap.lib
Binary files differ
diff --git a/ldap/disptmpl.cfg b/ldap/disptmpl.cfg
new file mode 100755
index 00000000..f08e3b27
--- /dev/null
+++ b/ldap/disptmpl.cfg
@@ -0,0 +1,677 @@
+##########################################################################
+# LDAP display templates
+##########################################################################
+
+#
+# Version must be 1
+#
+Version 1
+
+##########################################################################
+# U-M Person template
+##########################################################################
+#
+# template name and plural name come first
+"U-M Person"
+"U-M People"
+
+# name of the icon that is associated with this template
+"person icon"
+
+# blank-separated list of template options ("" for none)
+# addable - end-user should be allowed to add these types of entries
+# modrdn - end-user can change the name of these entries
+# altview - this template is referred to in another template's
+# "linkact" item
+"addable"
+
+#
+# objectclass list
+umichPerson person
+END
+
+#
+# name of attribute to authenticate as ("" means auth as this entry)
+""
+
+#
+# default attribute name to use when forming RDN of a new entry
+#
+cn
+
+#
+# default location when adding new entries (DN; "" means no default)
+"o=University of Michigan, c=US"
+
+#
+# rules used to define default values for new entries
+END
+
+#
+#
+# list of items for display
+# each line is either:
+# item (type) (attribute) (attr name) (extra args...)
+# to define an item or
+# samerow
+# to keep the next item on the same row as the previous
+#
+# valid types are:
+# cis - case ignore string
+# mls - multiline string
+# dn -
+# mail - case ignore string that contains an RFC822 mail address
+# bool - boolean value
+# jpeg - inlined JPEG image
+# jpegbtn - JPEG image button
+# fax - inlined Fax image
+# faxbtn - Fax image button
+# audiobtn - audio button
+# time - time value
+# date - time value displayed as a date only
+# url - labeled URL for links to items in WWW
+# searchact - search action
+# linkact - link to another template
+#
+# valid options (comma separated list appended to the type) are:
+# ro - attribute is read only; don't let user edit it
+# sort - order the values of this attribute
+# 1val - disallow entry of multiple values
+# required - this attribute should have at least one value
+# hide - don't show this item if attribute has no values
+# hideiffalse - hide item if value is FALSE (for type 'bool' only)
+#
+item jpegbtn "View Photo" jpegPhoto "Next Photo"
+item audiobtn "Play Sound" audio
+item cis,ro,sort "Also Known As" cn
+item mail "E-Mail Address" mail
+item cis "Work Phone" telephoneNumber
+item cis "Fax Number" facsimileTelephoneNumber
+item cis "Pager Number" pager
+item mls "Work Address" postalAddress
+item cis,sort "Title" title
+item cis,ro "Uniqname" uid
+item mls "Description" multiLineDescription
+item cis "Home Phone" homePhone
+item mls "Home Address" homePostalAddress
+item url "More Info (URL)" labeledURL
+item dn,sort "See Also" seeAlso
+item cis "Favorite Beverage" drink
+item cis "Notice" notice
+item bool,hideiffalse "On Vacation" onVacation
+item mls,1val "Vacation Message" vacationMessage
+item bool,hideiffalse "Do Not Allow Updates" noBatchUpdates
+item time,ro "Last Modified" lastModifiedTime
+item dn,ro "Modified By" lastModifiedBy
+item searchact "Find Groups Added To" "" "-dnt" "(&(objectclass=rfc822mailgroup)(member=%v))" "multiLineDescription" "Description" ""
+item searchact "List Owned Groups" "" "-dnt" "(&(objectclass=rfc822mailgroup)(owner=%v))" "title" "Title" ""
+item linkact "Other Addresses" "" "other addresses"
+END
+
+
+##########################################################################
+# Person template
+##########################################################################
+"Person"
+"People"
+"person icon"
+
+# template options
+addable
+#
+# objectclass list
+person
+END
+# name of attribute to authenticate as
+""
+
+# default attribute name to use when forming RDN of a new entry
+#
+cn
+
+# default location when adding new entries
+""
+
+#
+# rules used to define default values for new entries
+END
+
+#
+# list of items for display
+item jpegbtn "View Photo" jpegPhoto "Next Photo"
+item audiobtn "Play Sound" audio
+item cis,sort "Also Known As" cn
+item cis,sort "Title" title
+item mls "Work Address" postalAddress
+item cis "Work Phone" telephoneNumber
+item cis "Fax Number" facsimileTelephoneNumber
+item cis "Pager Number" pager
+item mls "Home Address" homePostalAddress
+item cis "Home Phone" homePhone
+item cis "User ID" uid
+item mail "E-Mail Address" mail
+item cis "Description" description
+item cis "Favorite Beverage" drink
+item dn,sort "See Also" seeAlso
+item time,ro "Last Modified" lastModifiedTime
+item dn,ro "Modified By" lastModifiedBy
+END
+
+
+##########################################################################
+# Group template
+##########################################################################
+"Group"
+"Groups"
+"group icon"
+
+# template options
+addable modrdn
+
+# objectclass list
+rfc822MailGroup
+END
+
+# name of attribute to authenticate as
+"owner"
+
+# default attribute name to use when forming RDN of a new entry
+#
+cn
+
+# default location when adding new entries
+"ou=User Groups, ou=Groups, o=University of Michigan, c=US"
+
+#
+# rules used to define default values for new entries
+constant "associatedDomain" "umich.edu"
+constant "joinable" "FALSE"
+addersdn "owner"
+addersdn "member"
+addersdn "errorsTo"
+addersdn "requestsTo"
+END
+
+#
+#
+# list of items for display
+# each line is either:
+# item (type) (attribute) (attr name) (extra args...)
+# to define an item or
+# samerow
+#
+# list of items for display
+item cis,sort "Also Known As" cn
+item mls "Description" multiLineDescription
+item cis "Phone Number" telephoneNumber
+item cis "Fax Number" facsimileTelephoneNumber
+item mls "Address" postalAddress
+item dn,required,sort "Owner" owner
+item url "More Info (URL)" labeledURL
+item dn,sort "See Also" seeAlso
+item dn,sort "Errors To" errorsTo
+item dn,sort "Requests To" requestsTo
+item cis "Associated Domain" associatedDomain
+item cis "Moderator" moderator
+item bool "Suppress 'No E-Mail Address' Errors" suppressNoEmailError
+item bool "Others May Join" joinable
+item dn,sort "X.500 Members" member
+item mail,sort "E-Mail Errors To" rfc822ErrorsTo
+item mail,sort "E-Mail Requests To" rfc822RequestsTo
+item mail,sort "E-Mail Members" mail
+item cis "Notice" notice
+item time,ro "Last Modified" lastModifiedTime
+item dn,ro "Modified By" lastModifiedBy
+item searchact "Subscribers" "" "-dnt" "memberOfGroup=%v" "title" "Title" "joinable"
+item verifyact "Verify Members" "member" "mail" "E-Mail Address"
+END
+
+##########################################################################
+# Organization template
+##########################################################################
+"Organization"
+"Organizations"
+"organization icon"
+
+# template options
+""
+
+# objectclass list
+organization
+END
+
+# name of attribute to authenticate as
+""
+
+# default attribute name to use when forming RDN of a new entry
+o
+
+# default location when adding new entries
+""
+
+# rules used to define default values for new entries
+constant "o" "foo"
+END
+
+#
+#
+# list of items for display
+# each line is either:
+# item (type) (attribute) (attr name) (extra args...)
+# to define an item or
+# samerow
+#
+# list of items for display
+item cis,sort "Name" o
+item cis "Location" l
+item mls "Address" postalAddress
+item cis "Phone Number" telephoneNumber
+item cis "Fax Number" facsimileTelephoneNumber
+item cis "Description" description
+item dn,sort "See Also" seeAlso
+item time,ro "Last Modified" lastModifiedTime
+item dn,ro "Modified By" lastModifiedBy
+END
+
+
+##########################################################################
+# Service template
+##########################################################################
+"Service"
+"Services"
+"service icon"
+
+# template options
+"addable"
+
+# objectclass list
+service
+END
+
+# name of attribute to authenticate as
+"owner"
+
+# default attribute name to use when forming RDN of a new entry
+cn
+
+# default location when adding new entries
+"ou=Services, o=University of Michigan, c=US"
+
+# rules used to define default values for new entries
+addersdn "owner"
+END
+
+#
+#
+# list of items for display
+# each line is either:
+# item (type) (attribute) (attr name) (extra args...)
+# to define an item or
+# samerow
+#
+# list of items for display
+item jpegbtn "View Photo" jpegPhoto
+item cis,sort "Name" cn
+item mls "Description" multilineDescription
+item cis "Provider" provider
+item cis,sort "Service Area" serviceArea
+item mail "E-mail Address" mail
+item cis "Phone" telephoneNumber
+item cis "Fax Number" facsimileTelephoneNumber
+item mls "Postal Address" postalAddress
+item cis "Hours" hoursOfOperation
+item url "More Info (URL)" labeledURL
+item dn,sort "Depends On" dependentUpon
+item dn,sort "See Also" seeAlso
+item cis,sort "Platform" platform
+item cis,sort "Product" product
+item cis,sort "Keywords" keywords
+item cis "FCE Rating" serviceRating
+item date "Date Rated" ratingTime
+item mls "Rating Description" ratingDescription
+item time,ro "Last Modified" lastModifiedTime
+item dn,ro "Modified By" lastModifiedBy
+item dn,required,sort "Owner" owner
+END
+
+
+##########################################################################
+# Organizational Role template
+##########################################################################
+"Organizational Role"
+"Organizational Roles"
+"person icon"
+
+# template options
+""
+
+# objectclass list
+organizationalRole
+END
+
+# name of attribute to authenticate as
+""
+
+# default attribute name to use when forming RDN of a new entry
+cn
+
+# default location when adding new entries
+""
+
+# rules used to define default values for new entries
+END
+
+#
+#
+# list of items for display
+# each line is either:
+# item (type) (attribute) (attr name) (extra args...)
+# to define an item or
+# samerow
+#
+# list of items for display
+item cis,sort "Name" cn
+item cis "Description" description
+item dn "Role Occupant" roleOccupant
+item dn,sort "See Also" seeAlso
+item time,ro "Last Modified" lastModifiedTime
+item dn,ro "Modified By" lastModifiedBy
+END
+
+
+##########################################################################
+# Organizational Unit template
+##########################################################################
+"Organizational Unit"
+"Organizational Units"
+"organization icon"
+
+# template options
+""
+
+# objectclass list
+organizationalUnit
+END
+
+# name of attribute to authenticate as
+""
+
+# default attribute name to use when forming RDN of a new entry
+cn
+
+# default location when adding new entries
+""
+
+# rules used to define default values for new entries
+END
+
+# Item list
+item cis "Organization Unit Name" ou
+item cis "Title" title
+item time,ro "Last Modified" lastModifiedTime
+item dn,ro "Modified By" lastModifiedBy
+END
+
+
+
+##########################################################################
+# Application Entity template
+##########################################################################
+"Application Entity"
+"Application Entities"
+"application icon"
+
+# template options
+""
+
+# objectclass list
+applicationEntity
+END
+
+# name of attribute to authenticate as
+""
+
+# default attribute name to use when forming RDN of a new entry
+cn
+
+# default location when adding new entries
+""
+
+# rules used to define default values for new entries
+END
+
+# Item list
+item cis,sort "Name" cn
+item cis "Location" l
+item cis "Description" description
+item time,ro "Last Modified" lastModifiedTime
+item dn,ro "Modified By" lastModifiedBy
+END
+
+##########################################################################
+# Document template
+##########################################################################
+"Document"
+"Documents"
+"document icon"
+
+# template options
+""
+
+# objectclass list
+document
+umichDocument
+END
+
+# name of attribute to authenticate as
+""
+
+# default attribute name to use when forming RDN of a new entry
+cn
+
+# default location when adding new entries
+""
+
+# rules used to define default values for new entries
+END
+
+#
+# Item list
+item cis "Document ID" documentIdentifier
+item cis "Title" documentTitle
+item cis "Series Title" documentSeriesTitle
+item cis "Version" documentVersion
+item cis,sort "Service Area" serviceArea
+item mls "Abstract" multiLineAbstract
+item url "More Info (URL)" labeledURL
+item dn,sort "Availability" documentAvailable
+item dn,sort "See Also" seeAlso
+item cis,sort "Platform" platform
+item cis,sort "Product" product
+item cis,sort "Keyword" keywords
+item dn,sort "Author" documentAuthor
+item time,ro "Last Modified" lastModifiedTime
+item dn,ro "Modified By" lastModifiedBy
+item dn,required "Owner" owner
+END
+
+##########################################################################
+# Document description template
+##########################################################################
+"DocumentDescription"
+"DocumentDescriptions"
+"document description icon"
+
+# template options
+""
+
+# objectclass list
+documentDescription
+END
+
+# name of attribute to authenticate as
+""
+
+# default attribute name to use when forming RDN of a new entry
+cn
+
+# default location when adding new entries
+""
+
+# rules used to define default values for new entries
+END
+
+#
+# Item list
+item mls "Description" multilineDescription
+item url "More Info (URL)" labeledURL
+item time,ro "Last Modified" lastModifiedTime
+item dn,ro "Modified By" lastModifiedBy
+item dn,required "Owner" owner
+END
+
+##########################################################################
+# Image template
+##########################################################################
+"Image"
+"Images"
+"image icon"
+
+# template options
+""
+
+# objectclass list
+image
+END
+
+# name of attribute to authenticate as
+""
+
+# default attribute name to use when forming RDN of a new entry
+cn
+
+# default location when adding new entries
+""
+
+# rules used to define default values for new entries
+END
+
+#
+# Item list
+item cis "Name" cn
+item mls "Description" multilineDescription
+item jpegbtn "View Photo(s)" jpegPhoto
+item cis "Citation" citation
+item cis "Copyright" copyright
+item cis "Keywords" keywords
+item time,ro "Last Modified" lastModifiedTime
+item dn,ro "Modified By" lastModifiedBy
+item dn,required "Owner" owner
+END
+
+
+
+##########################################################################
+# Country template
+##########################################################################
+"Country"
+"Countries"
+"country icon"
+
+# template options
+""
+
+# objectclass list
+friendlyCountry
+END
+
+# name of attribute to authenticate as
+""
+
+# default attribute name to use when forming RDN of a new entry
+c
+
+# default location when adding new entries
+""
+
+# rules used to define default values for new entries
+END
+
+# Item list
+item cis "Country Name" co
+item cis "Country Code" c
+item cis "Description" description
+item time,ro "Last Modified" lastModifiedTime
+item dn,ro "Modified By" lastModifiedBy
+END
+
+##########################################################################
+# Locality template
+##########################################################################
+"Locality"
+"Localities"
+"locality icon"
+
+# template options
+""
+
+# objectclass list
+locality
+END
+
+# name of attribute to authenticate as
+""
+
+# default attribute name to use when forming RDN of a new entry
+l
+
+# default location when adding new entries
+""
+
+# rules used to define default values for new entries
+END
+
+#
+# Item list
+item cis "Name" l
+item time,ro "Last Modified" lastModifiedTime
+item dn,ro "Modified By" lastModifiedBy
+END
+
+
+##########################################################################
+# "Other Addresses" template
+##########################################################################
+"Others Addresses"
+"Other Addresses"
+"other addr icon"
+
+# template options
+"altview"
+
+# objectclass list
+END
+
+# name of attribute to authenticate as
+""
+
+# default attribute name to use when forming RDN of a new entry
+""
+
+# default location when adding new entries
+""
+
+# rules used to define default values for new entries
+END
+
+# Item list
+item cis "Street Address" streetAddress
+item cis "Locality" l
+item cis "State or Province" st
+item cis "Postal Code" postalCode
+item cis,hide "X.400 Address" mhsORAddresses
+item cis,hide "X.400 Address" textEncodedORAddress
+Item cis "Other Mailbox" otherMailbox
+item time,ro "Last Modified" lastModifiedTime
+item dn,ro "Modified By" lastModifiedBy
+END
diff --git a/ldap/inckit/disptmpl.h b/ldap/inckit/disptmpl.h
new file mode 100755
index 00000000..d6e0a359
--- /dev/null
+++ b/ldap/inckit/disptmpl.h
@@ -0,0 +1,330 @@
+/*
+ * Copyright (c) 1993, 1994 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ *
+ * disptmpl.h: display template library defines
+ * 7 March 1994 by Mark C Smith
+ */
+
+#ifndef _DISPTMPL_H
+#define _DISPTMPL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define LDAP_TEMPLATE_VERSION 1
+
+/*
+ * general types of items (confined to most significant byte)
+ */
+#define LDAP_SYN_TYPE_TEXT 0x01000000L
+#define LDAP_SYN_TYPE_IMAGE 0x02000000L
+#define LDAP_SYN_TYPE_BOOLEAN 0x04000000L
+#define LDAP_SYN_TYPE_BUTTON 0x08000000L
+#define LDAP_SYN_TYPE_ACTION 0x10000000L
+
+
+/*
+ * syntax options (confined to second most significant byte)
+ */
+#define LDAP_SYN_OPT_DEFER 0x00010000L
+
+
+/*
+ * display template item syntax ids (defined by common agreement)
+ * these are the valid values for the ti_syntaxid of the tmplitem
+ * struct (defined below). A general type is encoded in the
+ * most-significant 8 bits, and some options are encoded in the next
+ * 8 bits. The lower 16 bits are reserved for the distinct types.
+ */
+#define LDAP_SYN_CASEIGNORESTR ( 1 | LDAP_SYN_TYPE_TEXT )
+#define LDAP_SYN_MULTILINESTR ( 2 | LDAP_SYN_TYPE_TEXT )
+#define LDAP_SYN_DN ( 3 | LDAP_SYN_TYPE_TEXT )
+#define LDAP_SYN_BOOLEAN ( 4 | LDAP_SYN_TYPE_BOOLEAN )
+#define LDAP_SYN_JPEGIMAGE ( 5 | LDAP_SYN_TYPE_IMAGE )
+#define LDAP_SYN_JPEGBUTTON ( 6 | LDAP_SYN_TYPE_BUTTON | LDAP_SYN_OPT_DEFER )
+#define LDAP_SYN_FAXIMAGE ( 7 | LDAP_SYN_TYPE_IMAGE )
+#define LDAP_SYN_FAXBUTTON ( 8 | LDAP_SYN_TYPE_BUTTON | LDAP_SYN_OPT_DEFER )
+#define LDAP_SYN_AUDIOBUTTON ( 9 | LDAP_SYN_TYPE_BUTTON | LDAP_SYN_OPT_DEFER )
+#define LDAP_SYN_TIME ( 10 | LDAP_SYN_TYPE_TEXT )
+#define LDAP_SYN_DATE ( 11 | LDAP_SYN_TYPE_TEXT )
+#define LDAP_SYN_LABELEDURL ( 12 | LDAP_SYN_TYPE_TEXT )
+#define LDAP_SYN_SEARCHACTION ( 13 | LDAP_SYN_TYPE_ACTION )
+#define LDAP_SYN_LINKACTION ( 14 | LDAP_SYN_TYPE_ACTION )
+#define LDAP_SYN_ADDDNACTION ( 15 | LDAP_SYN_TYPE_ACTION )
+#define LDAP_SYN_VERIFYDNACTION ( 16 | LDAP_SYN_TYPE_ACTION )
+#define LDAP_SYN_RFC822ADDR ( 17 | LDAP_SYN_TYPE_TEXT )
+
+
+/*
+ * handy macros
+ */
+#define LDAP_GET_SYN_TYPE( syid ) ((syid) & 0xFF000000L )
+#define LDAP_GET_SYN_OPTIONS( syid ) ((syid) & 0x00FF0000L )
+
+
+/*
+ * display options for output routines (used by entry2text and friends)
+ */
+/*
+ * use calculated label width (based on length of longest label in
+ * template) instead of contant width
+ */
+#define LDAP_DISP_OPT_AUTOLABELWIDTH 0x00000001L
+#define LDAP_DISP_OPT_HTMLBODYONLY 0x00000002L
+
+/*
+ * perform search actions (applies to ldap_entry2text_search only)
+ */
+#define LDAP_DISP_OPT_DOSEARCHACTIONS 0x00000002L
+
+/*
+ * include additional info. relevant to "non leaf" entries only
+ * used by ldap_entry2html and ldap_entry2html_search to include "Browse"
+ * and "Move Up" HREFs
+ */
+#define LDAP_DISP_OPT_NONLEAF 0x00000004L
+
+
+/*
+ * display template item options (may not apply to all types)
+ * if this bit is set in ti_options, it applies.
+ */
+#define LDAP_DITEM_OPT_READONLY 0x00000001L
+#define LDAP_DITEM_OPT_SORTVALUES 0x00000002L
+#define LDAP_DITEM_OPT_SINGLEVALUED 0x00000004L
+#define LDAP_DITEM_OPT_HIDEIFEMPTY 0x00000008L
+#define LDAP_DITEM_OPT_VALUEREQUIRED 0x00000010L
+#define LDAP_DITEM_OPT_HIDEIFFALSE 0x00000020L /* booleans only */
+
+
+
+/*
+ * display template item structure
+ */
+struct ldap_tmplitem {
+ unsigned long ti_syntaxid;
+ unsigned long ti_options;
+ char *ti_attrname;
+ char *ti_label;
+ char **ti_args;
+ struct ldap_tmplitem *ti_next_in_row;
+ struct ldap_tmplitem *ti_next_in_col;
+ void *ti_appdata;
+};
+
+
+#define NULLTMPLITEM ((struct ldap_tmplitem *)0)
+
+#define LDAP_SET_TMPLITEM_APPDATA( ti, datap ) \
+ (ti)->ti_appdata = (void *)(datap)
+
+#define LDAP_GET_TMPLITEM_APPDATA( ti, type ) \
+ (type)((ti)->ti_appdata)
+
+#define LDAP_IS_TMPLITEM_OPTION_SET( ti, option ) \
+ (((ti)->ti_options & option ) != 0 )
+
+
+/*
+ * object class array structure
+ */
+struct ldap_oclist {
+ char **oc_objclasses;
+ struct ldap_oclist *oc_next;
+};
+
+#define NULLOCLIST ((struct ldap_oclist *)0)
+
+
+/*
+ * add defaults list
+ */
+struct ldap_adddeflist {
+ int ad_source;
+#define LDAP_ADSRC_CONSTANTVALUE 1
+#define LDAP_ADSRC_ADDERSDN 2
+ char *ad_attrname;
+ char *ad_value;
+ struct ldap_adddeflist *ad_next;
+};
+
+#define NULLADLIST ((struct ldap_adddeflist *)0)
+
+
+/*
+ * display template global options
+ * if this bit is set in dt_options, it applies.
+ */
+/*
+ * users should be allowed to try to add objects of these entries
+ */
+#define LDAP_DTMPL_OPT_ADDABLE 0x00000001L
+
+/*
+ * users should be allowed to do "modify RDN" operation of these entries
+ */
+#define LDAP_DTMPL_OPT_ALLOWMODRDN 0x00000002L
+
+/*
+ * this template is an alternate view, not a primary view
+ */
+#define LDAP_DTMPL_OPT_ALTVIEW 0x00000004L
+
+
+/*
+ * display template structure
+ */
+struct ldap_disptmpl {
+ char *dt_name;
+ char *dt_pluralname;
+ char *dt_iconname;
+ unsigned long dt_options;
+ char *dt_authattrname;
+ char *dt_defrdnattrname;
+ char *dt_defaddlocation;
+ struct ldap_oclist *dt_oclist;
+ struct ldap_adddeflist *dt_adddeflist;
+ struct ldap_tmplitem *dt_items;
+ void *dt_appdata;
+ struct ldap_disptmpl *dt_next;
+};
+
+#define NULLDISPTMPL ((struct ldap_disptmpl *)0)
+
+#define LDAP_SET_DISPTMPL_APPDATA( dt, datap ) \
+ (dt)->dt_appdata = (void *)(datap)
+
+#define LDAP_GET_DISPTMPL_APPDATA( dt, type ) \
+ (type)((dt)->dt_appdata)
+
+#define LDAP_IS_DISPTMPL_OPTION_SET( dt, option ) \
+ (((dt)->dt_options & option ) != 0 )
+
+#define LDAP_TMPL_ERR_VERSION 1
+#define LDAP_TMPL_ERR_MEM 2
+#define LDAP_TMPL_ERR_SYNTAX 3
+#define LDAP_TMPL_ERR_FILE 4
+
+/*
+ * buffer size needed for entry2text and vals2text
+ */
+#define LDAP_DTMPL_BUFSIZ 8192
+
+
+#ifndef NEEDPROTOS
+
+typedef int (*writeptype)();
+
+int ldap_init_templates();
+int ldap_init_templates_buf();
+void ldap_free_templates();
+struct ldap_disptmpl *ldap_first_disptmpl();
+struct ldap_disptmpl *ldap_next_disptmpl();
+struct ldap_disptmpl *ldap_name2template();
+struct ldap_disptmpl *ldap_oc2template();
+char **ldap_tmplattrs();
+struct ldap_tmplitem *ldap_first_tmplrow();
+struct ldap_tmplitem *ldap_next_tmplrow();
+struct ldap_tmplitem *ldap_first_tmplcol();
+struct ldap_tmplitem *ldap_next_tmplcol();
+int ldap_entry2text_search();
+int ldap_entry2text();
+int ldap_vals2text();
+int ldap_entry2html_search();
+int ldap_entry2html();
+int ldap_vals2html();
+
+#else /* !NEEDPROTOS */
+
+typedef int (*writeptype)( void *writeparm, char *p, int len );
+
+LDAPFUNCDECL int
+ldap_init_templates( char *file, struct ldap_disptmpl **tmpllistp );
+
+LDAPFUNCDECL int
+ldap_init_templates_buf( char *buf, long buflen,
+ struct ldap_disptmpl **tmpllistp );
+
+LDAPFUNCDECL void
+ldap_free_templates( struct ldap_disptmpl *tmpllist );
+
+LDAPFUNCDECL struct ldap_disptmpl *
+ldap_first_disptmpl( struct ldap_disptmpl *tmpllist );
+
+LDAPFUNCDECL struct ldap_disptmpl *
+ldap_next_disptmpl( struct ldap_disptmpl *tmpllist,
+ struct ldap_disptmpl *tmpl );
+
+LDAPFUNCDECL struct ldap_disptmpl *
+ldap_name2template( char *name, struct ldap_disptmpl *tmpllist );
+
+LDAPFUNCDECL struct ldap_disptmpl *
+ldap_oc2template( char **oclist, struct ldap_disptmpl *tmpllist );
+
+LDAPFUNCDECL char **
+ldap_tmplattrs( struct ldap_disptmpl *tmpl, char **includeattrs, int exclude,
+ unsigned long syntaxmask );
+
+LDAPFUNCDECL struct ldap_tmplitem *
+ldap_first_tmplrow( struct ldap_disptmpl *tmpl );
+
+LDAPFUNCDECL struct ldap_tmplitem *
+ldap_next_tmplrow( struct ldap_disptmpl *tmpl, struct ldap_tmplitem *row );
+
+LDAPFUNCDECL struct ldap_tmplitem *
+ldap_first_tmplcol( struct ldap_disptmpl *tmpl, struct ldap_tmplitem *row );
+
+LDAPFUNCDECL struct ldap_tmplitem *
+ldap_next_tmplcol( struct ldap_disptmpl *tmpl, struct ldap_tmplitem *row,
+ struct ldap_tmplitem *col );
+
+LDAPFUNCDECL int
+ldap_entry2text( LDAP *ld, char *buf, LDAPMessage *entry,
+ struct ldap_disptmpl *tmpl, char **defattrs, char ***defvals,
+ writeptype writeproc, void *writeparm, char *eol, int rdncount,
+ unsigned long opts );
+
+LDAPFUNCDECL int
+ldap_vals2text( LDAP *ld, char *buf, char **vals, char *label, int labelwidth,
+ unsigned long syntaxid, writeptype writeproc, void *writeparm,
+ char *eol, int rdncount );
+
+LDAPFUNCDECL int
+ldap_entry2text_search( LDAP *ld, char *dn, char *base, LDAPMessage *entry,
+ struct ldap_disptmpl *tmpllist, char **defattrs, char ***defvals,
+ writeptype writeproc, void *writeparm, char *eol, int rdncount,
+ unsigned long opts );
+
+LDAPFUNCDECL int
+ldap_entry2html( LDAP *ld, char *buf, LDAPMessage *entry,
+ struct ldap_disptmpl *tmpl, char **defattrs, char ***defvals,
+ writeptype writeproc, void *writeparm, char *eol, int rdncount,
+ unsigned long opts, char *urlprefix, char *base );
+
+LDAPFUNCDECL int
+ldap_vals2html( LDAP *ld, char *buf, char **vals, char *label, int labelwidth,
+ unsigned long syntaxid, writeptype writeproc, void *writeparm,
+ char *eol, int rdncount, char *urlprefix );
+
+LDAPFUNCDECL int
+ldap_entry2html_search( LDAP *ld, char *dn, char *base, LDAPMessage *entry,
+ struct ldap_disptmpl *tmpllist, char **defattrs, char ***defvals,
+ writeptype writeproc, void *writeparm, char *eol, int rdncount,
+ unsigned long opts, char *urlprefix );
+#endif /* !NEEDPROTOS */
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _DISPTMPL_H */
diff --git a/ldap/inckit/lber.h b/ldap/inckit/lber.h
new file mode 100755
index 00000000..9c5289f1
--- /dev/null
+++ b/ldap/inckit/lber.h
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#ifndef _LBER_H
+#define _LBER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if !defined( NEEDPROTOS ) && defined(__STDC__)
+#define NEEDPROTOS 1
+#endif
+
+/* BER classes and mask */
+#define LBER_CLASS_UNIVERSAL 0x00
+#define LBER_CLASS_APPLICATION 0x40
+#define LBER_CLASS_CONTEXT 0x80
+#define LBER_CLASS_PRIVATE 0xc0
+#define LBER_CLASS_MASK 0xc0
+
+/* BER encoding type and mask */
+#define LBER_PRIMITIVE 0x00
+#define LBER_CONSTRUCTED 0x20
+#define LBER_ENCODING_MASK 0x20
+
+#define LBER_BIG_TAG_MASK 0x1f
+#define LBER_MORE_TAG_MASK 0x80
+
+/*
+ * Note that LBER_ERROR and LBER_DEFAULT are values that can never appear
+ * as valid BER tags, and so it is safe to use them to report errors. In
+ * fact, any tag for which the following is true is invalid:
+ * (( tag & 0x00000080 ) != 0 ) && (( tag & 0xFFFFFF00 ) != 0 )
+ */
+#define LBER_ERROR 0xffffffffL
+#define LBER_DEFAULT 0xffffffffL
+
+/* general BER types we know about */
+#define LBER_BOOLEAN 0x01L
+#define LBER_INTEGER 0x02L
+#define LBER_BITSTRING 0x03L
+#define LBER_OCTETSTRING 0x04L
+#define LBER_NULL 0x05L
+#define LBER_ENUMERATED 0x0aL
+#define LBER_SEQUENCE 0x30L /* constructed */
+#define LBER_SET 0x31L /* constructed */
+
+#define OLD_LBER_SEQUENCE 0x10L /* w/o constructed bit - broken */
+#define OLD_LBER_SET 0x11L /* w/o constructed bit - broken */
+
+#ifdef NEEDPROTOS
+typedef int (*BERTranslateProc)( char **bufp, unsigned long *buflenp,
+ int free_input );
+#else /* NEEDPROTOS */
+typedef int (*BERTranslateProc)();
+#endif /* NEEDPROTOS */
+
+typedef struct berelement {
+ char *ber_buf;
+ char *ber_ptr;
+ char *ber_end;
+ struct seqorset *ber_sos;
+ unsigned long ber_tag;
+ unsigned long ber_len;
+ int ber_usertag;
+ char ber_options;
+#define LBER_USE_DER 0x01
+#define LBER_USE_INDEFINITE_LEN 0x02
+#define LBER_TRANSLATE_STRINGS 0x04
+ char *ber_rwptr;
+ BERTranslateProc ber_encode_translate_proc;
+ BERTranslateProc ber_decode_translate_proc;
+} BerElement;
+#define NULLBER ((BerElement *) 0)
+
+typedef struct sockbuf {
+#ifndef MACOS
+ int sb_sd;
+#else /* MACOS */
+ void *sb_sd;
+#endif /* MACOS */
+ BerElement sb_ber;
+
+ int sb_naddr; /* > 0 implies using CLDAP (UDP) */
+ void *sb_useaddr; /* pointer to sockaddr to use next */
+ void *sb_fromaddr; /* pointer to message source sockaddr */
+ void **sb_addrs; /* actually an array of pointers to
+ sockaddrs */
+
+ int sb_options; /* to support copying ber elements */
+#define LBER_TO_FILE 0x01 /* to a file referenced by sb_fd */
+#define LBER_TO_FILE_ONLY 0x02 /* only write to file, not network */
+#define LBER_MAX_INCOMING_SIZE 0x04 /* impose limit on incoming stuff */
+#define LBER_NO_READ_AHEAD 0x08 /* read only as much as requested */
+ int sb_fd;
+ long sb_max_incoming;
+} Sockbuf;
+#define READBUFSIZ 8192
+
+typedef struct seqorset {
+ BerElement *sos_ber;
+ unsigned long sos_clen;
+ unsigned long sos_tag;
+ char *sos_first;
+ char *sos_ptr;
+ struct seqorset *sos_next;
+} Seqorset;
+#define NULLSEQORSET ((Seqorset *) 0)
+
+/* structure for returning a sequence of octet strings + length */
+struct berval {
+ unsigned long bv_len;
+ char *bv_val;
+};
+
+#ifndef NEEDPROTOS
+extern BerElement *ber_alloc();
+extern BerElement *der_alloc();
+extern BerElement *ber_alloc_t();
+extern BerElement *ber_dup();
+extern int lber_debug;
+extern void ber_bvfree();
+extern void ber_bvecfree();
+extern struct berval *ber_bvdup();
+extern void ber_dump();
+extern void ber_sos_dump();
+extern void lber_bprint();
+extern void ber_reset();
+extern void ber_init();
+#else /* NEEDPROTOS */
+#if defined(WINSOCK)
+#include "proto-lb.h"
+#else
+#include "proto-lber.h"
+#endif
+#endif /* NEEDPROTOS */
+
+#if !defined(__alpha) || defined(VMS)
+
+#define LBER_HTONL( l ) htonl( l )
+#define LBER_NTOHL( l ) ntohl( l )
+
+#else /* __alpha */
+/*
+ * htonl and ntohl on the DEC Alpha under OSF 1 seem to only swap the
+ * lower-order 32-bits of a (64-bit) long, so we define correct versions
+ * here.
+ */
+#define LBER_HTONL( l ) (((long)htonl( (l) & 0x00000000FFFFFFFF )) << 32 \
+ | htonl( ( (l) & 0xFFFFFFFF00000000 ) >> 32 ))
+
+#define LBER_NTOHL( l ) (((long)ntohl( (l) & 0x00000000FFFFFFFF )) << 32 \
+ | ntohl( ( (l) & 0xFFFFFFFF00000000 ) >> 32 ))
+#endif /* __alpha */
+
+
+/*
+ * SAFEMEMCPY is an overlap-safe copy from s to d of n bytes
+ */
+#ifdef MACOS
+#define SAFEMEMCPY( d, s, n ) BlockMoveData( (Ptr)s, (Ptr)d, n )
+#else /* MACOS */
+#ifdef sunos4
+#define SAFEMEMCPY( d, s, n ) bcopy( s, d, n )
+#else /* sunos4 */
+#define SAFEMEMCPY( d, s, n ) memmove( d, s, n )
+#endif /* sunos4 */
+#endif /* MACOS */
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _LBER_H */
diff --git a/ldap/inckit/ldap.h b/ldap/inckit/ldap.h
new file mode 100755
index 00000000..de3e217c
--- /dev/null
+++ b/ldap/inckit/ldap.h
@@ -0,0 +1,592 @@
+/*
+ * Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#ifndef _LDAP_H
+#define _LDAP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#ifdef WINSOCK
+#include "msdos.h"
+#include <winsock.h>
+#endif
+
+#if !defined( NEEDPROTOS ) && defined(__STDC__)
+#define NEEDPROTOS 1
+#endif
+
+#define LDAP_PORT 389
+#define LDAP_VERSION1 1
+#define LDAP_VERSION2 2
+#define LDAP_VERSION LDAP_VERSION2
+
+#define COMPAT20
+#define COMPAT30
+#if defined(COMPAT20) || defined(COMPAT30)
+#define COMPAT
+#endif
+
+#define LDAP_MAX_ATTR_LEN 100
+
+/* debugging stuff */
+#ifdef LDAP_DEBUG
+extern int ldap_debug;
+#ifdef LDAP_SYSLOG
+extern int ldap_syslog;
+extern int ldap_syslog_level;
+#endif
+#define LDAP_DEBUG_TRACE 0x001
+#define LDAP_DEBUG_PACKETS 0x002
+#define LDAP_DEBUG_ARGS 0x004
+#define LDAP_DEBUG_CONNS 0x008
+#define LDAP_DEBUG_BER 0x010
+#define LDAP_DEBUG_FILTER 0x020
+#define LDAP_DEBUG_CONFIG 0x040
+#define LDAP_DEBUG_ACL 0x080
+#define LDAP_DEBUG_STATS 0x100
+#define LDAP_DEBUG_STATS2 0x200
+#define LDAP_DEBUG_SHELL 0x400
+#define LDAP_DEBUG_PARSE 0x800
+#define LDAP_DEBUG_ANY 0xffff
+
+#ifdef LDAP_SYSLOG
+#define Debug( level, fmt, arg1, arg2, arg3 ) \
+ { \
+ if ( ldap_debug & level ) \
+ fprintf( stderr, fmt, arg1, arg2, arg3 ); \
+ if ( ldap_syslog & level ) \
+ syslog( ldap_syslog_level, fmt, arg1, arg2, arg3 ); \
+ }
+#else /* LDAP_SYSLOG */
+#ifndef WINSOCK
+#define Debug( level, fmt, arg1, arg2, arg3 ) \
+ if ( ldap_debug & level ) \
+ fprintf( stderr, fmt, arg1, arg2, arg3 );
+#else /* !WINSOCK */
+extern void Debug( int level, char* fmt, ... );
+#endif /* !WINSOCK */
+#endif /* LDAP_SYSLOG */
+#else /* LDAP_DEBUG */
+#define Debug( level, fmt, arg1, arg2, arg3 )
+#endif /* LDAP_DEBUG */
+
+/*
+ * specific LDAP instantiations of BER types we know about
+ */
+
+/* general stuff */
+#define LDAP_TAG_MESSAGE 0x30L /* tag is 16 + constructed bit */
+#define OLD_LDAP_TAG_MESSAGE 0x10L /* forgot the constructed bit */
+#define LDAP_TAG_MSGID 0x02L
+
+/* possible operations a client can invoke */
+#define LDAP_REQ_BIND 0x60L /* application + constructed */
+#define LDAP_REQ_UNBIND 0x42L /* application + primitive */
+#define LDAP_REQ_SEARCH 0x63L /* application + constructed */
+#define LDAP_REQ_MODIFY 0x66L /* application + constructed */
+#define LDAP_REQ_ADD 0x68L /* application + constructed */
+#define LDAP_REQ_DELETE 0x4aL /* application + primitive */
+#define LDAP_REQ_MODRDN 0x6cL /* application + constructed */
+#define LDAP_REQ_COMPARE 0x6eL /* application + constructed */
+#define LDAP_REQ_ABANDON 0x50L /* application + primitive */
+
+/* version 3.0 compatibility stuff */
+#define LDAP_REQ_UNBIND_30 0x62L
+#define LDAP_REQ_DELETE_30 0x6aL
+#define LDAP_REQ_ABANDON_30 0x70L
+
+/*
+ * old broken stuff for backwards compatibility - forgot application tag
+ * and constructed/primitive bit
+ */
+#define OLD_LDAP_REQ_BIND 0x00L
+#define OLD_LDAP_REQ_UNBIND 0x02L
+#define OLD_LDAP_REQ_SEARCH 0x03L
+#define OLD_LDAP_REQ_MODIFY 0x06L
+#define OLD_LDAP_REQ_ADD 0x08L
+#define OLD_LDAP_REQ_DELETE 0x0aL
+#define OLD_LDAP_REQ_MODRDN 0x0cL
+#define OLD_LDAP_REQ_COMPARE 0x0eL
+#define OLD_LDAP_REQ_ABANDON 0x10L
+
+/* possible result types a server can return */
+#define LDAP_RES_BIND 0x61L /* application + constructed */
+#define LDAP_RES_SEARCH_ENTRY 0x64L /* application + constructed */
+#define LDAP_RES_SEARCH_RESULT 0x65L /* application + constructed */
+#define LDAP_RES_MODIFY 0x67L /* application + constructed */
+#define LDAP_RES_ADD 0x69L /* application + constructed */
+#define LDAP_RES_DELETE 0x6bL /* application + constructed */
+#define LDAP_RES_MODRDN 0x6dL /* application + constructed */
+#define LDAP_RES_COMPARE 0x6fL /* application + constructed */
+#define LDAP_RES_ANY (-1L)
+
+/* old broken stuff for backwards compatibility */
+#define OLD_LDAP_RES_BIND 0x01L
+#define OLD_LDAP_RES_SEARCH_ENTRY 0x04L
+#define OLD_LDAP_RES_SEARCH_RESULT 0x05L
+#define OLD_LDAP_RES_MODIFY 0x07L
+#define OLD_LDAP_RES_ADD 0x09L
+#define OLD_LDAP_RES_DELETE 0x0bL
+#define OLD_LDAP_RES_MODRDN 0x0dL
+#define OLD_LDAP_RES_COMPARE 0x0fL
+
+/* authentication methods available */
+#define LDAP_AUTH_NONE 0x00L /* no authentication */
+#define LDAP_AUTH_SIMPLE 0x80L /* context specific + primitive */
+#define LDAP_AUTH_KRBV4 0xffL /* means do both of the following */
+#define LDAP_AUTH_KRBV41 0x81L /* context specific + primitive */
+#define LDAP_AUTH_KRBV42 0x82L /* context specific + primitive */
+
+/* 3.0 compatibility auth methods */
+#define LDAP_AUTH_SIMPLE_30 0xa0L /* context specific + constructed */
+#define LDAP_AUTH_KRBV41_30 0xa1L /* context specific + constructed */
+#define LDAP_AUTH_KRBV42_30 0xa2L /* context specific + constructed */
+
+/* old broken stuff */
+#define OLD_LDAP_AUTH_SIMPLE 0x00L
+#define OLD_LDAP_AUTH_KRBV4 0x01L
+#define OLD_LDAP_AUTH_KRBV42 0x02L
+
+/* filter types */
+#define LDAP_FILTER_AND 0xa0L /* context specific + constructed */
+#define LDAP_FILTER_OR 0xa1L /* context specific + constructed */
+#define LDAP_FILTER_NOT 0xa2L /* context specific + constructed */
+#define LDAP_FILTER_EQUALITY 0xa3L /* context specific + constructed */
+#define LDAP_FILTER_SUBSTRINGS 0xa4L /* context specific + constructed */
+#define LDAP_FILTER_GE 0xa5L /* context specific + constructed */
+#define LDAP_FILTER_LE 0xa6L /* context specific + constructed */
+#define LDAP_FILTER_PRESENT 0x87L /* context specific + primitive */
+#define LDAP_FILTER_APPROX 0xa8L /* context specific + constructed */
+
+/* 3.0 compatibility filter types */
+#define LDAP_FILTER_PRESENT_30 0xa7L /* context specific + constructed */
+
+/* old broken stuff */
+#define OLD_LDAP_FILTER_AND 0x00L
+#define OLD_LDAP_FILTER_OR 0x01L
+#define OLD_LDAP_FILTER_NOT 0x02L
+#define OLD_LDAP_FILTER_EQUALITY 0x03L
+#define OLD_LDAP_FILTER_SUBSTRINGS 0x04L
+#define OLD_LDAP_FILTER_GE 0x05L
+#define OLD_LDAP_FILTER_LE 0x06L
+#define OLD_LDAP_FILTER_PRESENT 0x07L
+#define OLD_LDAP_FILTER_APPROX 0x08L
+
+/* substring filter component types */
+#define LDAP_SUBSTRING_INITIAL 0x80L /* context specific */
+#define LDAP_SUBSTRING_ANY 0x81L /* context specific */
+#define LDAP_SUBSTRING_FINAL 0x82L /* context specific */
+
+/* 3.0 compatibility substring filter component types */
+#define LDAP_SUBSTRING_INITIAL_30 0xa0L /* context specific */
+#define LDAP_SUBSTRING_ANY_30 0xa1L /* context specific */
+#define LDAP_SUBSTRING_FINAL_30 0xa2L /* context specific */
+
+/* old broken stuff */
+#define OLD_LDAP_SUBSTRING_INITIAL 0x00L
+#define OLD_LDAP_SUBSTRING_ANY 0x01L
+#define OLD_LDAP_SUBSTRING_FINAL 0x02L
+
+/* search scopes */
+#define LDAP_SCOPE_BASE 0x00
+#define LDAP_SCOPE_ONELEVEL 0x01
+#define LDAP_SCOPE_SUBTREE 0x02
+
+/* for modifications */
+typedef struct ldapmod {
+ int mod_op;
+#define LDAP_MOD_ADD 0x00
+#define LDAP_MOD_DELETE 0x01
+#define LDAP_MOD_REPLACE 0x02
+#define LDAP_MOD_BVALUES 0x80
+ char *mod_type;
+ union {
+ char **modv_strvals;
+ struct berval **modv_bvals;
+ } mod_vals;
+#define mod_values mod_vals.modv_strvals
+#define mod_bvalues mod_vals.modv_bvals
+ struct ldapmod *mod_next;
+} LDAPMod;
+
+/*
+ * possible error codes we can return
+ */
+
+#define LDAP_SUCCESS 0x00
+#define LDAP_OPERATIONS_ERROR 0x01
+#define LDAP_PROTOCOL_ERROR 0x02
+#define LDAP_TIMELIMIT_EXCEEDED 0x03
+#define LDAP_SIZELIMIT_EXCEEDED 0x04
+#define LDAP_COMPARE_FALSE 0x05
+#define LDAP_COMPARE_TRUE 0x06
+#define LDAP_STRONG_AUTH_NOT_SUPPORTED 0x07
+#define LDAP_STRONG_AUTH_REQUIRED 0x08
+#define LDAP_PARTIAL_RESULTS 0x09
+
+#define LDAP_NO_SUCH_ATTRIBUTE 0x10
+#define LDAP_UNDEFINED_TYPE 0x11
+#define LDAP_INAPPROPRIATE_MATCHING 0x12
+#define LDAP_CONSTRAINT_VIOLATION 0x13
+#define LDAP_TYPE_OR_VALUE_EXISTS 0x14
+#define LDAP_INVALID_SYNTAX 0x15
+
+#define LDAP_NO_SUCH_OBJECT 0x20
+#define LDAP_ALIAS_PROBLEM 0x21
+#define LDAP_INVALID_DN_SYNTAX 0x22
+#define LDAP_IS_LEAF 0x23
+#define LDAP_ALIAS_DEREF_PROBLEM 0x24
+
+#define NAME_ERROR(n) ((n & 0xf0) == 0x20)
+
+#define LDAP_INAPPROPRIATE_AUTH 0x30
+#define LDAP_INVALID_CREDENTIALS 0x31
+#define LDAP_INSUFFICIENT_ACCESS 0x32
+#define LDAP_BUSY 0x33
+#define LDAP_UNAVAILABLE 0x34
+#define LDAP_UNWILLING_TO_PERFORM 0x35
+#define LDAP_LOOP_DETECT 0x36
+
+#define LDAP_NAMING_VIOLATION 0x40
+#define LDAP_OBJECT_CLASS_VIOLATION 0x41
+#define LDAP_NOT_ALLOWED_ON_NONLEAF 0x42
+#define LDAP_NOT_ALLOWED_ON_RDN 0x43
+#define LDAP_ALREADY_EXISTS 0x44
+#define LDAP_NO_OBJECT_CLASS_MODS 0x45
+#define LDAP_RESULTS_TOO_LARGE 0x46
+
+#define LDAP_OTHER 0x50
+#define LDAP_SERVER_DOWN 0x51
+#define LDAP_LOCAL_ERROR 0x52
+#define LDAP_ENCODING_ERROR 0x53
+#define LDAP_DECODING_ERROR 0x54
+#define LDAP_TIMEOUT 0x55
+#define LDAP_AUTH_UNKNOWN 0x56
+#define LDAP_FILTER_ERROR 0x57
+#define LDAP_USER_CANCELLED 0x58
+#define LDAP_PARAM_ERROR 0x59
+#define LDAP_NO_MEMORY 0x5a
+
+
+/* default limit on nesting of referrals */
+#define LDAP_DEFAULT_REFHOPLIMIT 5
+
+/*
+ * This structure represents both ldap messages and ldap responses.
+ * These are really the same, except in the case of search responses,
+ * where a response has multiple messages.
+ */
+
+typedef struct ldapmsg {
+ int lm_msgid; /* the message id */
+ int lm_msgtype; /* the message type */
+ BerElement *lm_ber; /* the ber encoded message contents */
+ struct ldapmsg *lm_chain; /* for search - next msg in the resp */
+ struct ldapmsg *lm_next; /* next response */
+ unsigned long lm_time; /* used to maintain cache */
+} LDAPMessage;
+#define NULLMSG ((LDAPMessage *) NULL)
+
+
+#ifdef LDAP_REFERRALS
+/*
+ * structure for tracking LDAP server host, ports, DNs, etc.
+ */
+typedef struct ldap_server {
+ char *lsrv_host;
+ char *lsrv_dn; /* if NULL, use default */
+ int lsrv_port;
+ struct ldap_server *lsrv_next;
+} LDAPServer;
+
+
+/*
+ * structure for representing an LDAP server connection
+ */
+typedef struct ldap_conn {
+ Sockbuf *lconn_sb;
+ int lconn_refcnt;
+ unsigned long lconn_lastused; /* time */
+ int lconn_status;
+#define LDAP_CONNST_NEEDSOCKET 1
+#define LDAP_CONNST_CONNECTING 2
+#define LDAP_CONNST_CONNECTED 3
+ LDAPServer *lconn_server;
+ char *lconn_krbinstance;
+ struct ldap_conn *lconn_next;
+} LDAPConn;
+
+
+/*
+ * structure used to track outstanding requests
+ */
+typedef struct ldapreq {
+ int lr_msgid; /* the message id */
+ int lr_status; /* status of request */
+#define LDAP_REQST_INPROGRESS 1
+#define LDAP_REQST_CHASINGREFS 2
+#define LDAP_REQST_NOTCONNECTED 3
+#define LDAP_REQST_WRITING 4
+ int lr_outrefcnt; /* count of outstanding referrals */
+ int lr_origid; /* original request's message id */
+ int lr_parentcnt; /* count of parent requests */
+ int lr_res_msgtype; /* result message type */
+ int lr_res_errno; /* result LDAP errno */
+ char *lr_res_error; /* result error string */
+ char *lr_res_matched;/* result matched DN string */
+ BerElement *lr_ber; /* ber encoded request contents */
+ LDAPConn *lr_conn; /* connection used to send request */
+ struct ldapreq *lr_parent; /* request that spawned this referral */
+ struct ldapreq *lr_refnext; /* next referral spawned */
+ struct ldapreq *lr_prev; /* previous request */
+ struct ldapreq *lr_next; /* next request */
+} LDAPRequest;
+#endif /* LDAP_REFERRALS */
+
+
+/*
+ * structure for client cache
+ */
+#define LDAP_CACHE_BUCKETS 31 /* cache hash table size */
+typedef struct ldapcache {
+ LDAPMessage *lc_buckets[LDAP_CACHE_BUCKETS];/* hash table */
+ LDAPMessage *lc_requests; /* unfulfilled reqs */
+ long lc_timeout; /* request timeout */
+ long lc_maxmem; /* memory to use */
+ long lc_memused; /* memory in use */
+ int lc_enabled; /* enabled? */
+ unsigned long lc_options; /* options */
+#define LDAP_CACHE_OPT_CACHENOERRS 0x00000001
+#define LDAP_CACHE_OPT_CACHEALLERRS 0x00000002
+} LDAPCache;
+#define NULLLDCACHE ((LDAPCache *)NULL)
+
+/*
+ * structures for ldap getfilter routines
+ */
+
+typedef struct ldap_filt_info {
+ char *lfi_filter;
+ char *lfi_desc;
+ int lfi_scope; /* LDAP_SCOPE_BASE, etc */
+ int lfi_isexact; /* exact match filter? */
+ struct ldap_filt_info *lfi_next;
+} LDAPFiltInfo;
+
+typedef struct ldap_filt_list {
+ char *lfl_tag;
+ char *lfl_pattern;
+ char *lfl_delims;
+ LDAPFiltInfo *lfl_ilist;
+ struct ldap_filt_list *lfl_next;
+} LDAPFiltList;
+
+
+#define LDAP_FILT_MAXSIZ 1024
+
+typedef struct ldap_filt_desc {
+ LDAPFiltList *lfd_filtlist;
+ LDAPFiltInfo *lfd_curfip;
+ LDAPFiltInfo lfd_retfi;
+ char lfd_filter[ LDAP_FILT_MAXSIZ ];
+ char *lfd_curval;
+ char *lfd_curvalcopy;
+ char **lfd_curvalwords;
+ char *lfd_filtprefix;
+ char *lfd_filtsuffix;
+} LDAPFiltDesc;
+
+
+/*
+ * structure representing an ldap connection
+ */
+
+typedef struct ldap {
+ Sockbuf ld_sb; /* socket descriptor & buffer */
+ char *ld_host;
+ int ld_version;
+ char ld_lberoptions;
+ int ld_deref;
+#define LDAP_DEREF_NEVER 0
+#define LDAP_DEREF_SEARCHING 1
+#define LDAP_DEREF_FINDING 2
+#define LDAP_DEREF_ALWAYS 3
+
+ int ld_timelimit;
+ int ld_sizelimit;
+#define LDAP_NO_LIMIT 0
+
+ LDAPFiltDesc *ld_filtd; /* from getfilter for ufn searches */
+ char *ld_ufnprefix; /* for incomplete ufn's */
+
+ int ld_errno;
+ char *ld_error;
+ char *ld_matched;
+ int ld_msgid;
+
+ /* do not mess with these */
+#ifdef LDAP_REFERRALS
+ LDAPRequest *ld_requests; /* list of outstanding requests */
+#else /* LDAP_REFERRALS */
+ LDAPMessage *ld_requests; /* list of outstanding requests */
+#endif /* LDAP_REFERRALS */
+ LDAPMessage *ld_responses; /* list of outstanding responses */
+ int *ld_abandoned; /* array of abandoned requests */
+ char ld_attrbuffer[LDAP_MAX_ATTR_LEN];
+ LDAPCache *ld_cache; /* non-null if cache is initialized */
+ char *ld_cldapdn; /* DN used in connectionless search */
+
+ /* it is OK to change these next four values directly */
+ int ld_cldaptries; /* connectionless search retry count */
+ int ld_cldaptimeout;/* time between retries */
+ int ld_refhoplimit; /* limit on referral nesting */
+ unsigned long ld_options; /* boolean options */
+#ifdef LDAP_DNS
+#define LDAP_OPT_DNS 0x00000001 /* use DN & DNS */
+#endif /* LDAP_DNS */
+#ifdef LDAP_REFERRALS
+#define LDAP_OPT_REFERRALS 0x00000002 /* chase referrals */
+#endif /* LDAP_REFERRALS */
+#define LDAP_OPT_RESTART 0x00000004 /* restart if EINTR occurs */
+
+ /* do not mess with the rest though */
+ char *ld_defhost; /* full name of default server */
+ int ld_defport; /* port of default server */
+ BERTranslateProc ld_lber_encode_translate_proc;
+ BERTranslateProc ld_lber_decode_translate_proc;
+#ifdef LDAP_REFERRALS
+ LDAPConn *ld_defconn; /* default connection */
+ LDAPConn *ld_conns; /* list of server connections */
+ void *ld_selectinfo; /* platform specifics for select */
+ int (*ld_rebindproc)( struct ldap *ld, char **dnp,
+ char **passwdp, int *authmethodp, int freeit );
+ /* routine to get info needed for re-bind */
+#endif /* LDAP_REFERRALS */
+} LDAP;
+
+
+/*
+ * structure for ldap friendly mapping routines
+ */
+
+typedef struct friendly {
+ char *f_unfriendly;
+ char *f_friendly;
+} FriendlyMap;
+
+
+/*
+ * handy macro to check whether LDAP struct is set up for CLDAP or not
+ */
+#define LDAP_IS_CLDAP( ld ) ( ld->ld_sb.sb_naddr > 0 )
+
+
+/*
+ * types for ldap URL handling
+ */
+typedef struct ldap_url_desc {
+ char *lud_host;
+ int lud_port;
+ char *lud_dn;
+ char **lud_attrs;
+ int lud_scope;
+ char *lud_filter;
+ char *lud_string; /* for internal use only */
+} LDAPURLDesc;
+#define NULLLDAPURLDESC ((LDAPURLDesc *)NULL)
+
+#define LDAP_URL_ERR_NOTLDAP 1 /* URL doesn't begin with "ldap://" */
+#define LDAP_URL_ERR_NODN 2 /* URL has no DN (required) */
+#define LDAP_URL_ERR_BADSCOPE 3 /* URL scope string is invalid */
+#define LDAP_URL_ERR_MEM 4 /* can't allocate memory space */
+
+
+#ifndef NEEDPROTOS
+extern LDAP *ldap_open();
+extern LDAP *ldap_init();
+#ifdef STR_TRANSLATION
+extern void ldap_set_string_translators();
+#ifdef LDAP_CHARSET_8859
+extern int ldap_t61_to_8859();
+extern int ldap_8859_to_t61();
+#endif /* LDAP_CHARSET_8859 */
+#endif /* STR_TRANSLATION */
+extern LDAPMessage *ldap_first_entry();
+extern LDAPMessage *ldap_next_entry();
+extern char *ldap_get_dn();
+extern char *ldap_dn2ufn();
+extern char **ldap_explode_dn();
+extern char *ldap_first_attribute();
+extern char *ldap_next_attribute();
+extern char **ldap_get_values();
+extern struct berval **ldap_get_values_len();
+extern void ldap_value_free();
+extern void ldap_value_free_len();
+extern int ldap_count_values();
+extern int ldap_count_values_len();
+extern char *ldap_err2string();
+extern void ldap_getfilter_free();
+extern LDAPFiltDesc *ldap_init_getfilter();
+extern LDAPFiltDesc *ldap_init_getfilter_buf();
+extern LDAPFiltInfo *ldap_getfirstfilter();
+extern LDAPFiltInfo *ldap_getnextfilter();
+extern void ldap_setfilteraffixes();
+extern void ldap_build_filter();
+extern void ldap_flush_cache();
+extern void ldap_set_cache_options();
+extern void ldap_uncache_entry();
+extern void ldap_uncache_request();
+extern char *ldap_friendly_name();
+extern void ldap_free_friendlymap();
+extern LDAP *cldap_open();
+extern void cldap_setretryinfo();
+extern void cldap_close();
+extern LDAPFiltDesc *ldap_ufn_setfilter();
+extern int ldap_ufn_timeout();
+extern int ldap_sort_entries();
+extern int ldap_sort_values();
+extern int ldap_sort_strcasecmp();
+void ldap_free_urldesc();
+void ldap_set_rebind_proc();
+void ldap_enable_translation();
+
+
+#if defined(ultrix) || defined(VMS) || defined( nextstep )
+extern char *strdup();
+#endif
+
+#else /* NEEDPROTOS */
+#if !defined(MACOS) && !defined(DOS) && !defined(_WIN32) && !defined(WINSOCK)
+#include <sys/time.h>
+#endif
+#if defined(WINSOCK)
+#include "proto-ld.h"
+#else
+#include "proto-ldap.h"
+#endif
+
+#ifdef VMS
+extern char *strdup( const char *s );
+#endif
+#if defined(ultrix) || defined( nextstep )
+extern char *strdup();
+#endif
+
+#endif /* NEEDPROTOS */
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _LDAP_H */
diff --git a/ldap/inckit/msdos.h b/ldap/inckit/msdos.h
new file mode 100755
index 00000000..81a0de4d
--- /dev/null
+++ b/ldap/inckit/msdos.h
@@ -0,0 +1,133 @@
+/* wsa.h */
+/*
+ * Copyright (c) 1993 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#ifndef _MSDOS_H
+#define _MSDOS_H
+
+/*
+ * NOTE: This file should be included via ldap.h. Many symbols are
+ * defined here that are needed BEFORE anything else is included.
+ * Be careful !!!
+ */
+/*
+ * The following are defined within the Integrated Development Environment
+ * of Microsoft's Visual C++ Compiler (v1.52c)
+ * (Options/Project/Compiler/Preprocessor/Symbols and Macros to Define)
+ * But there's a (buffer length) limit to how long this list can be, so
+ * I'm doing the rest here in msdos.h
+ * WINSOCK, DOS, NEEDPROTOS, NO_USERINTERFACE
+ */
+/*
+ * MIT's krb.h doesn't use the symbols provided by Microsoft.
+ * It needs __MSDOS__ and WINDOWS. Normally _WINDOWS is provided by MS
+ * but it's based on having the prolog/epilog optimization switches set
+ * in a way that we don't set them. So define it manually.
+ *
+ * kbind.c needs __MSDOS__ for krb.h to include osconf.h
+ * which includes conf-pc.h which defines byte order and such
+ */
+#define __MSDOS__
+/*
+ * conf-pc.h wants WINDOWS rather than _WINDOWS which Microsoft provides
+ */
+#define WINDOWS
+
+/*
+ * Where two of the config files live in the windows environment
+ * There are two others also; ldfriend.cfg, & srchpref.cfg
+ * These names are different that the unix names due to 8.3 rule
+ */
+#define FILTERFILE "ldfilter.cfg"
+#define TEMPLATEFILE "disptmpl.cfg"
+/*
+ * These are not automatically defined for us even though we're a DLL. They
+ * are triggered by prolog/epilog configuration options that we don't use.
+ * But be careful not to redefine them for other apps that include this file.
+ */
+#ifndef _WINDLL
+/*
+ * Needed by wshelper.h
+ */
+#define _WINDLL
+#endif
+
+#ifndef _WINDOWS
+/*
+ * Needed by authlib.h via kerberos.c via AUTHMAN
+ */
+#define _WINDOWS 1
+#endif
+
+/*
+ * KERBEROS must be defined as a preprocessor symbol in the compiler.
+ * It's too late to define it in this file.
+ */
+
+/*
+ * AUTHMAN - Use Authlib.dll as a higher level interface to krbv4win.dll
+ * (kerberos). If defined, get_kerberosv4_credentials in kerberos.c is
+ * used and authlib.dll (and krbv4win.dll) are dynamically loaded and used.
+ * If AUTHMAN is not defined, the get_kerberosv4_credentials in
+ * kbind.c works just fine, but requires the presence of krbv4win.dll at
+ * load time.
+ */
+/* don't want to be dependent on authman
+ * #define AUTHMAN
+ */
+
+/*
+ * define WSHELPER if you want wsockip.c to use rgethostbyaddr() (in
+ * WSHELPER.DLL) rather than gethostbyaddr(). You might want this if your
+ * gethostbyaddr() returns the WRONG host name and you want to use
+ * kerberos authentication (need host name to form service ticket
+ * request). Most won't want kerberos, and of those, there might actually
+ * be some vendors who really do the lookup rather than use cached info
+ * from gethostbyname() calls.
+ */
+#define WSHELPER
+/*
+ * The new slapd stuff
+ */
+#define LDAP_REFERRALS
+/*
+ * LDAP character string translation routines
+ * I compiled and tested these and they seemed to work.
+ * The thing to test with is:
+ * cn=Charset Test Entry, ou=SWITCHdirectory, o=SWITCH, c=CH
+ *
+ * I'm disabling it for release.
+#define STR_TRANSLATION
+#define LDAP_CHARSET_8859 88591
+#define LDAP_DEFAULT_CHARSET LDAP_CHARSET_8859
+ */
+
+
+#ifdef _DEBUG
+#define LDAP_DEBUG
+#endif
+#include <winsock.h>
+
+
+#include <string.h>
+#include <malloc.h>
+#ifndef _WIN32
+#define memcpy( a, b, n ) _fmemcpy( a, b, n )
+#define strcpy( a, b ) _fstrcpy( a, b )
+#define strchr( a, c ) _fstrchr( a, c )
+#endif /* !_WIN32 */
+#define strcasecmp(a,b) stricmp(a,b)
+#define strncasecmp(a,b,len) strnicmp(a,b,len)
+
+#endif /* _MSDOS_H */
+
+
diff --git a/ldap/inckit/proto-lb.h b/ldap/inckit/proto-lb.h
new file mode 100755
index 00000000..600328d5
--- /dev/null
+++ b/ldap/inckit/proto-lb.h
@@ -0,0 +1,102 @@
+/*
+ * lber-proto.h
+ * function prototypes for lber library
+ */
+
+#ifdef LDAP_DEBUG
+extern int lber_debug;
+#endif
+
+#ifndef LDAPFUNCDECL
+#ifdef _WIN32
+#define LDAPFUNCDECL __declspec( dllexport )
+#else /* _WIN32 */
+#define LDAPFUNCDECL
+#endif /* _WIN32 */
+#endif /* LDAPFUNCDECL */
+
+/*
+ * in bprint.c:
+ */
+LDAPFUNCDECL void lber_bprint( char *data, int len );
+
+/*
+ * in decode.c:
+ */
+LDAPFUNCDECL unsigned long ber_get_tag( BerElement *ber );
+LDAPFUNCDECL unsigned long ber_skip_tag( BerElement *ber, unsigned long *len );
+LDAPFUNCDECL unsigned long ber_peek_tag( BerElement *ber, unsigned long *len );
+LDAPFUNCDECL unsigned long ber_get_int( BerElement *ber, long *num );
+LDAPFUNCDECL unsigned long ber_get_stringb( BerElement *ber, char *buf,
+ unsigned long *len );
+LDAPFUNCDECL unsigned long ber_get_stringa( BerElement *ber, char **buf );
+LDAPFUNCDECL unsigned long ber_get_stringal( BerElement *ber, struct berval **bv );
+LDAPFUNCDECL unsigned long ber_get_bitstringa( BerElement *ber, char **buf,
+ unsigned long *len );
+LDAPFUNCDECL unsigned long ber_get_null( BerElement *ber );
+LDAPFUNCDECL unsigned long ber_get_boolean( BerElement *ber, int *boolval );
+LDAPFUNCDECL unsigned long ber_first_element( BerElement *ber, unsigned long *len,
+ char **last );
+LDAPFUNCDECL unsigned long ber_next_element( BerElement *ber, unsigned long *len,
+ char *last );
+#if defined( MACOS ) || defined( BC31 ) || defined( _WIN32 )
+LDAPFUNCDECL unsigned long ber_scanf( BerElement *ber, char *fmt, ... );
+#else
+LDAPFUNCDECL unsigned long ber_scanf();
+#endif
+LDAPFUNCDECL void ber_bvfree( struct berval *bv );
+LDAPFUNCDECL void ber_bvecfree( struct berval **bv );
+LDAPFUNCDECL struct berval *ber_bvdup( struct berval *bv );
+#ifdef STR_TRANSLATION
+LDAPFUNCDECL void ber_set_string_translators( BerElement *ber,
+ BERTranslateProc encode_proc, BERTranslateProc decode_proc );
+#endif /* STR_TRANSLATION */
+
+/*
+ * in encode.c
+ */
+LDAPFUNCDECL int ber_put_enum( BerElement *ber, long num, unsigned long tag );
+LDAPFUNCDECL int ber_put_int( BerElement *ber, long num, unsigned long tag );
+LDAPFUNCDECL int ber_put_ostring( BerElement *ber, char *str, unsigned long len,
+ unsigned long tag );
+LDAPFUNCDECL int ber_put_string( BerElement *ber, char *str, unsigned long tag );
+LDAPFUNCDECL int ber_put_bitstring( BerElement *ber, char *str,
+ unsigned long bitlen, unsigned long tag );
+LDAPFUNCDECL int ber_put_null( BerElement *ber, unsigned long tag );
+LDAPFUNCDECL int ber_put_boolean( BerElement *ber, int boolval,
+ unsigned long tag );
+LDAPFUNCDECL int ber_start_seq( BerElement *ber, unsigned long tag );
+LDAPFUNCDECL int ber_start_set( BerElement *ber, unsigned long tag );
+LDAPFUNCDECL int ber_put_seq( BerElement *ber );
+LDAPFUNCDECL int ber_put_set( BerElement *ber );
+#if defined( MACOS ) || defined( BC31 ) || defined( _WIN32 )
+LDAPFUNCDECL int ber_printf( BerElement *ber, char *fmt, ... );
+#else
+LDAPFUNCDECL int ber_printf();
+#endif
+
+/*
+ * in io.c:
+ */
+LDAPFUNCDECL long ber_read( BerElement *ber, char *buf, unsigned long len );
+LDAPFUNCDECL long ber_write( BerElement *ber, char *buf, unsigned long len,
+ int nosos );
+LDAPFUNCDECL void ber_free( BerElement *ber, int freebuf );
+LDAPFUNCDECL int ber_flush( Sockbuf *sb, BerElement *ber, int freeit );
+LDAPFUNCDECL BerElement *ber_alloc( void );
+LDAPFUNCDECL BerElement *der_alloc( void );
+LDAPFUNCDECL BerElement *ber_alloc_t( int options );
+LDAPFUNCDECL BerElement *ber_dup( BerElement *ber );
+LDAPFUNCDECL void ber_dump( BerElement *ber, int inout );
+LDAPFUNCDECL void ber_sos_dump( Seqorset *sos );
+LDAPFUNCDECL unsigned long ber_get_next( Sockbuf *sb, unsigned long *len,
+ BerElement *ber );
+LDAPFUNCDECL void ber_init( BerElement *ber, int options );
+LDAPFUNCDECL void ber_reset( BerElement *ber, int was_writing );
+
+#ifdef NEEDGETOPT
+/*
+ * in getopt.c
+ */
+int getopt( int nargc, char **nargv, char *ostr );
+#endif /* NEEDGETOPT */
diff --git a/ldap/inckit/proto-ld.h b/ldap/inckit/proto-ld.h
new file mode 100755
index 00000000..1ccb8317
--- /dev/null
+++ b/ldap/inckit/proto-ld.h
@@ -0,0 +1,276 @@
+/*
+ * proto-ldap.h
+ * function prototypes for ldap library
+ */
+
+
+#ifndef LDAPFUNCDECL
+#ifdef _WIN32
+#define LDAPFUNCDECL __declspec( dllexport )
+#else /* _WIN32 */
+#define LDAPFUNCDECL
+#endif /* _WIN32 */
+#endif /* LDAPFUNCDECL */
+
+
+/*
+ * in abandon.c:
+ */
+LDAPFUNCDECL int ldap_abandon( LDAP *ld, int msgid );
+
+/*
+ * in add.c:
+ */
+LDAPFUNCDECL int ldap_add( LDAP *ld, char *dn, LDAPMod **attrs );
+LDAPFUNCDECL int ldap_add_s( LDAP *ld, char *dn, LDAPMod **attrs );
+
+/*
+ * in bind.c:
+ */
+LDAPFUNCDECL int ldap_bind( LDAP *ld, char *who, char *passwd, int authmethod );
+LDAPFUNCDECL int ldap_bind_s( LDAP *ld, char *who, char *cred, int method );
+#ifdef LDAP_REFERRALS
+LDAPFUNCDECL void ldap_set_rebind_proc( LDAP *ld, int (*rebindproc)( LDAP *ld,
+ char **dnp, char **passwdp, int *authmethodp, int freeit ));
+#endif /* LDAP_REFERRALS */
+
+/*
+ * in sbind.c:
+ */
+LDAPFUNCDECL int ldap_simple_bind( LDAP *ld, char *who, char *passwd );
+LDAPFUNCDECL int ldap_simple_bind_s( LDAP *ld, char *who, char *passwd );
+
+/*
+ * in kbind.c:
+ */
+LDAPFUNCDECL int ldap_kerberos_bind_s( LDAP *ld, char *who );
+LDAPFUNCDECL int ldap_kerberos_bind1( LDAP *ld, char *who );
+LDAPFUNCDECL int ldap_kerberos_bind1_s( LDAP *ld, char *who );
+LDAPFUNCDECL int ldap_kerberos_bind2( LDAP *ld, char *who );
+LDAPFUNCDECL int ldap_kerberos_bind2_s( LDAP *ld, char *who );
+
+
+#ifndef NO_CACHE
+/*
+ * in cache.c
+ */
+LDAPFUNCDECL int ldap_enable_cache( LDAP *ld, long timeout, long maxmem );
+LDAPFUNCDECL void ldap_disable_cache( LDAP *ld );
+LDAPFUNCDECL void ldap_set_cache_options( LDAP *ld, unsigned long opts );
+LDAPFUNCDECL void ldap_destroy_cache( LDAP *ld );
+LDAPFUNCDECL void ldap_flush_cache( LDAP *ld );
+LDAPFUNCDECL void ldap_uncache_entry( LDAP *ld, char *dn );
+LDAPFUNCDECL void ldap_uncache_request( LDAP *ld, int msgid );
+#endif /* !NO_CACHE */
+
+/*
+ * in compare.c:
+ */
+LDAPFUNCDECL int ldap_compare( LDAP *ld, char *dn, char *attr, char *value );
+LDAPFUNCDECL int ldap_compare_s( LDAP *ld, char *dn, char *attr, char *value );
+
+/*
+ * in delete.c:
+ */
+LDAPFUNCDECL int ldap_delete( LDAP *ld, char *dn );
+LDAPFUNCDECL int ldap_delete_s( LDAP *ld, char *dn );
+
+/*
+ * in error.c:
+ */
+LDAPFUNCDECL int ldap_result2error( LDAP *ld, LDAPMessage *r, int freeit );
+LDAPFUNCDECL char *ldap_err2string( int err );
+LDAPFUNCDECL void ldap_perror( LDAP *ld, char *s );
+
+/*
+ * in modify.c:
+ */
+LDAPFUNCDECL int ldap_modify( LDAP *ld, char *dn, LDAPMod **mods );
+LDAPFUNCDECL int ldap_modify_s( LDAP *ld, char *dn, LDAPMod **mods );
+
+/*
+ * in modrdn.c:
+ */
+LDAPFUNCDECL int ldap_modrdn( LDAP *ld, char *dn, char *newrdn );
+LDAPFUNCDECL int ldap_modrdn_s( LDAP *ld, char *dn, char *newrdn );
+LDAPFUNCDECL int ldap_modrdn2( LDAP *ld, char *dn, char *newrdn,
+ int deleteoldrdn );
+LDAPFUNCDECL int ldap_modrdn2_s( LDAP *ld, char *dn, char *newrdn,
+ int deleteoldrdn);
+
+/*
+ * in open.c:
+ */
+LDAPFUNCDECL LDAP *ldap_open( char *host, int port );
+LDAPFUNCDECL LDAP *ldap_init( char *defhost, int defport );
+
+/*
+ * in getentry.c:
+ */
+LDAPFUNCDECL LDAPMessage *ldap_first_entry( LDAP *ld, LDAPMessage *chain );
+LDAPFUNCDECL LDAPMessage *ldap_next_entry( LDAP *ld, LDAPMessage *entry );
+LDAPFUNCDECL int ldap_count_entries( LDAP *ld, LDAPMessage *chain );
+
+/*
+ * in addentry.c
+ */
+LDAPFUNCDECL LDAPMessage *ldap_delete_result_entry( LDAPMessage **list,
+ LDAPMessage *e );
+LDAPFUNCDECL void ldap_add_result_entry( LDAPMessage **list, LDAPMessage *e );
+
+/*
+ * in getdn.c
+ */
+LDAPFUNCDECL char *ldap_get_dn( LDAP *ld, LDAPMessage *entry );
+LDAPFUNCDECL char *ldap_dn2ufn( char *dn );
+LDAPFUNCDECL char **ldap_explode_dn( char *dn, int notypes );
+LDAPFUNCDECL char **ldap_explode_dns( char *dn );
+LDAPFUNCDECL int ldap_is_dns_dn( char *dn );
+
+/*
+ * in getattr.c
+ */
+LDAPFUNCDECL char *ldap_first_attribute( LDAP *ld, LDAPMessage *entry,
+ BerElement **ber );
+LDAPFUNCDECL char *ldap_next_attribute( LDAP *ld, LDAPMessage *entry,
+ BerElement *ber );
+
+/*
+ * in getvalues.c
+ */
+LDAPFUNCDECL char **ldap_get_values( LDAP *ld, LDAPMessage *entry, char *target );
+LDAPFUNCDECL struct berval **ldap_get_values_len( LDAP *ld, LDAPMessage *entry,
+ char *target );
+LDAPFUNCDECL int ldap_count_values( char **vals );
+LDAPFUNCDECL int ldap_count_values_len( struct berval **vals );
+LDAPFUNCDECL void ldap_value_free( char **vals );
+LDAPFUNCDECL void ldap_value_free_len( struct berval **vals );
+
+/*
+ * in result.c:
+ */
+LDAPFUNCDECL int ldap_result( LDAP *ld, int msgid, int all,
+ struct timeval *timeout, LDAPMessage **result );
+LDAPFUNCDECL int ldap_msgfree( LDAPMessage *lm );
+LDAPFUNCDECL int ldap_msgdelete( LDAP *ld, int msgid );
+
+/*
+ * in search.c:
+ */
+LDAPFUNCDECL int ldap_search( LDAP *ld, char *base, int scope, char *filter,
+ char **attrs, int attrsonly );
+LDAPFUNCDECL int ldap_search_s( LDAP *ld, char *base, int scope, char *filter,
+ char **attrs, int attrsonly, LDAPMessage **res );
+LDAPFUNCDECL int ldap_search_st( LDAP *ld, char *base, int scope, char *filter,
+ char **attrs, int attrsonly, struct timeval *timeout, LDAPMessage **res );
+
+/*
+ * in ufn.c
+ */
+LDAPFUNCDECL int ldap_ufn_search_c( LDAP *ld, char *ufn, char **attrs,
+ int attrsonly, LDAPMessage **res, int (*cancelproc)( void *cl ),
+ void *cancelparm );
+LDAPFUNCDECL int ldap_ufn_search_ct( LDAP *ld, char *ufn, char **attrs,
+ int attrsonly, LDAPMessage **res, int (*cancelproc)( void *cl ),
+ void *cancelparm, char *tag1, char *tag2, char *tag3 );
+LDAPFUNCDECL int ldap_ufn_search_s( LDAP *ld, char *ufn, char **attrs,
+ int attrsonly, LDAPMessage **res );
+LDAPFUNCDECL LDAPFiltDesc *ldap_ufn_setfilter( LDAP *ld, char *fname );
+LDAPFUNCDECL void ldap_ufn_setprefix( LDAP *ld, char *prefix );
+LDAPFUNCDECL int ldap_ufn_timeout( void *tvparam );
+
+
+/*
+ * in unbind.c
+ */
+LDAPFUNCDECL int ldap_unbind( LDAP *ld );
+LDAPFUNCDECL int ldap_unbind_s( LDAP *ld );
+
+
+/*
+ * in getfilter.c
+ */
+LDAPFUNCDECL LDAPFiltDesc *ldap_init_getfilter( char *fname );
+LDAPFUNCDECL LDAPFiltDesc *ldap_init_getfilter_buf( char *buf, long buflen );
+LDAPFUNCDECL LDAPFiltInfo *ldap_getfirstfilter( LDAPFiltDesc *lfdp, char *tagpat,
+ char *value );
+LDAPFUNCDECL LDAPFiltInfo *ldap_getnextfilter( LDAPFiltDesc *lfdp );
+LDAPFUNCDECL void ldap_setfilteraffixes( LDAPFiltDesc *lfdp, char *prefix, char *suffix );
+LDAPFUNCDECL void ldap_build_filter( char *buf, unsigned long buflen,
+ char *pattern, char *prefix, char *suffix, char *attr,
+ char *value, char **valwords );
+
+/*
+ * in free.c
+ */
+LDAPFUNCDECL void ldap_getfilter_free( LDAPFiltDesc *lfdp );
+LDAPFUNCDECL void ldap_mods_free( LDAPMod **mods, int freemods );
+
+/*
+ * in friendly.c
+ */
+LDAPFUNCDECL char *ldap_friendly_name( char *filename, char *uname,
+ FriendlyMap **map );
+LDAPFUNCDECL void ldap_free_friendlymap( FriendlyMap **map );
+
+
+/*
+ * in cldap.c
+ */
+LDAPFUNCDECL LDAP *cldap_open( char *host, int port );
+LDAPFUNCDECL void cldap_close( LDAP *ld );
+LDAPFUNCDECL int cldap_search_s( LDAP *ld, char *base, int scope, char *filter,
+ char **attrs, int attrsonly, LDAPMessage **res, char *logdn );
+LDAPFUNCDECL void cldap_setretryinfo( LDAP *ld, int tries, int timeout );
+
+
+/*
+ * in sort.c
+ */
+LDAPFUNCDECL int ldap_sort_entries( LDAP *ld, LDAPMessage **chain, char *attr,
+ int (*cmp)() );
+LDAPFUNCDECL int ldap_sort_values( LDAP *ld, char **vals, int (*cmp)() );
+LDAPFUNCDECL int ldap_sort_strcasecmp( char **a, char **b );
+
+
+/*
+ * in url.c
+ */
+LDAPFUNCDECL int ldap_is_ldap_url( char *url );
+LDAPFUNCDECL int ldap_url_parse( char *url, LDAPURLDesc **ludpp );
+LDAPFUNCDECL void ldap_free_urldesc( LDAPURLDesc *ludp );
+LDAPFUNCDECL int ldap_url_search( LDAP *ld, char *url, int attrsonly );
+LDAPFUNCDECL int ldap_url_search_s( LDAP *ld, char *url, int attrsonly,
+ LDAPMessage **res );
+LDAPFUNCDECL int ldap_url_search_st( LDAP *ld, char *url, int attrsonly,
+ struct timeval *timeout, LDAPMessage **res );
+
+
+/*
+ * in charset.c
+ */
+#ifdef STR_TRANSLATION
+LDAPFUNCDECL void ldap_set_string_translators( LDAP *ld,
+ BERTranslateProc encode_proc, BERTranslateProc decode_proc );
+LDAPFUNCDECL int ldap_translate_from_t61( LDAP *ld, char **bufp,
+ unsigned long *lenp, int free_input );
+LDAPFUNCDECL int ldap_translate_to_t61( LDAP *ld, char **bufp,
+ unsigned long *lenp, int free_input );
+LDAPFUNCDECL void ldap_enable_translation( LDAP *ld, LDAPMessage *entry,
+ int enable );
+
+#ifdef LDAP_CHARSET_8859
+LDAPFUNCDECL int ldap_t61_to_8859( char **bufp, unsigned long *buflenp,
+ int free_input );
+LDAPFUNCDECL int ldap_8859_to_t61( char **bufp, unsigned long *buflenp,
+ int free_input );
+#endif /* LDAP_CHARSET_8859 */
+#endif /* STR_TRANSLATION */
+
+
+#ifdef WINSOCK
+/*
+ * in msdos/winsock/wsa.c
+ */
+LDAPFUNCDECL void ldap_memfree( void *p );
+#endif /* WINSOCK */
diff --git a/ldap/inckit/srchpref.h b/ldap/inckit/srchpref.h
new file mode 100755
index 00000000..fe8a422c
--- /dev/null
+++ b/ldap/inckit/srchpref.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 1993, 1994 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ *
+ * searchpref.h: display template library defines
+ * 16 May 1994 by Gordon Good
+ */
+
+
+#ifndef _SRCHPREF_H
+#define _SRCHPREF_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+struct ldap_searchattr {
+ char *sa_attrlabel;
+ char *sa_attr;
+ /* max 32 matchtypes for now */
+ unsigned long sa_matchtypebitmap;
+ char *sa_selectattr;
+ char *sa_selecttext;
+ struct ldap_searchattr *sa_next;
+};
+
+struct ldap_searchmatch {
+ char *sm_matchprompt;
+ char *sm_filter;
+ struct ldap_searchmatch *sm_next;
+};
+
+struct ldap_searchobj {
+ char *so_objtypeprompt;
+ unsigned long so_options;
+ char *so_prompt;
+ short so_defaultscope;
+ char *so_filterprefix;
+ char *so_filtertag;
+ char *so_defaultselectattr;
+ char *so_defaultselecttext;
+ struct ldap_searchattr *so_salist;
+ struct ldap_searchmatch *so_smlist;
+ struct ldap_searchobj *so_next;
+};
+
+#define NULLSEARCHOBJ ((struct ldap_searchobj *)0)
+
+/*
+ * global search object options
+ */
+#define LDAP_SEARCHOBJ_OPT_INTERNAL 0x00000001
+
+#define LDAP_IS_SEARCHOBJ_OPTION_SET( so, option ) \
+ (((so)->so_options & option ) != 0 )
+
+#define LDAP_SEARCHPREF_VERSION_ZERO 0
+#define LDAP_SEARCHPREF_VERSION 1
+
+#define LDAP_SEARCHPREF_ERR_VERSION 1
+#define LDAP_SEARCHPREF_ERR_MEM 2
+#define LDAP_SEARCHPREF_ERR_SYNTAX 3
+#define LDAP_SEARCHPREF_ERR_FILE 4
+
+
+#ifndef NEEDPROTOS
+int ldap_init_searchprefs();
+int ldap_init_searchprefs_buf();
+void ldap_free_searchprefs();
+struct ldap_searchobj *ldap_first_searchobj();
+struct ldap_searchobj *ldap_next_searchobj();
+
+#else /* !NEEDPROTOS */
+
+LDAPFUNCDECL int
+ldap_init_searchprefs( char *file, struct ldap_searchobj **solistp );
+
+LDAPFUNCDECL int
+ldap_init_searchprefs_buf( char *buf, long buflen,
+ struct ldap_searchobj **solistp );
+
+LDAPFUNCDECL void
+ldap_free_searchprefs( struct ldap_searchobj *solist );
+
+LDAPFUNCDECL struct ldap_searchobj *
+ldap_first_searchobj( struct ldap_searchobj *solist );
+
+LDAPFUNCDECL struct ldap_searchobj *
+ldap_next_searchobj( struct ldap_searchobj *sollist,
+ struct ldap_searchobj *so );
+
+#endif /* !NEEDPROTOS */
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _SRCHPREF_H */
diff --git a/ldap/kbind.c b/ldap/kbind.c
new file mode 100755
index 00000000..ff6279c9
--- /dev/null
+++ b/ldap/kbind.c
@@ -0,0 +1,368 @@
+/*
+ * Copyright (c) 1993 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * kbind.c
+ */
+
+#ifndef lint
+static char copyright[] = "@(#) Copyright (c) 1993 Regents of the University of Michigan.\nAll rights reserved.\n";
+#endif
+
+#ifdef KERBEROS
+
+#include <stdio.h>
+#include <string.h>
+
+#ifdef MACOS
+#include <stdlib.h>
+#include "macos.h"
+#else /* MACOS */
+#ifdef DOS
+#include "msdos.h"
+#endif /* DOS */
+#include <krb.h>
+#include <stdlib.h>
+#if !defined(DOS) && !defined( _WIN32 )
+#include <sys/types.h>
+#endif /* !DOS && !_WIN32 */
+#include <sys/time.h>
+#include <sys/socket.h>
+#endif /* MACOS */
+
+#include "lber.h"
+#include "ldap.h"
+#include "ldap-int.h"
+
+
+
+/*
+ * ldap_kerberos_bind1 - initiate a bind to the ldap server using
+ * kerberos authentication. The dn is supplied. It is assumed the user
+ * already has a valid ticket granting ticket. The msgid of the
+ * request is returned on success (suitable for passing to ldap_result()),
+ * -1 is returned if there's trouble.
+ *
+ * Example:
+ * ldap_kerberos_bind1( ld, "cn=manager, o=university of michigan, c=us" )
+ */
+int
+ldap_kerberos_bind1( LDAP *ld, char *dn )
+{
+ BerElement *ber;
+ char *cred;
+ int rc, credlen;
+ char *get_kerberosv4_credentials();
+#ifdef STR_TRANSLATION
+ int str_translation_on;
+#endif /* STR_TRANSLATION */
+
+ /*
+ * The bind request looks like this:
+ * BindRequest ::= SEQUENCE {
+ * version INTEGER,
+ * name DistinguishedName,
+ * authentication CHOICE {
+ * krbv42ldap [1] OCTET STRING
+ * krbv42dsa [2] OCTET STRING
+ * }
+ * }
+ * all wrapped up in an LDAPMessage sequence.
+ */
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_kerberos_bind1\n", 0, 0, 0 );
+
+ if ( dn == NULL )
+ dn = "";
+
+ if ( (cred = get_kerberosv4_credentials( ld, dn, "ldapserver",
+ &credlen )) == NULL ) {
+ return( -1 ); /* ld_errno should already be set */
+ }
+
+ /* create a message to send */
+ if ( (ber = alloc_ber_with_options( ld )) == NULLBER ) {
+ free( cred );
+ return( -1 );
+ }
+
+#ifdef STR_TRANSLATION
+ if (( str_translation_on = (( ber->ber_options &
+ LBER_TRANSLATE_STRINGS ) != 0 ))) { /* turn translation off */
+ ber->ber_options &= ~LBER_TRANSLATE_STRINGS;
+ }
+#endif /* STR_TRANSLATION */
+
+ /* fill it in */
+ rc = ber_printf( ber, "{it{isto}}", ++ld->ld_msgid, LDAP_REQ_BIND,
+ ld->ld_version, dn, LDAP_AUTH_KRBV41, cred, credlen );
+
+#ifdef STR_TRANSLATION
+ if ( str_translation_on ) { /* restore translation */
+ ber->ber_options |= LBER_TRANSLATE_STRINGS;
+ }
+#endif /* STR_TRANSLATION */
+
+ if ( rc == -1 ) {
+ free( cred );
+ ber_free( ber, 1 );
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ return( -1 );
+ }
+
+ free( cred );
+
+#ifndef NO_CACHE
+ if ( ld->ld_cache != NULL ) {
+ ldap_flush_cache( ld );
+ }
+#endif /* !NO_CACHE */
+
+ /* send the message */
+ return ( send_initial_request( ld, LDAP_REQ_BIND, dn, ber ));
+}
+
+int
+ldap_kerberos_bind1_s( LDAP *ld, char *dn )
+{
+ int msgid;
+ LDAPMessage *res;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_kerberos_bind1_s\n", 0, 0, 0 );
+
+ /* initiate the bind */
+ if ( (msgid = ldap_kerberos_bind1( ld, dn )) == -1 )
+ return( ld->ld_errno );
+
+ /* wait for a result */
+ if ( ldap_result( ld, ld->ld_msgid, 1, (struct timeval *) 0, &res )
+ == -1 ) {
+ return( ld->ld_errno ); /* ldap_result sets ld_errno */
+ }
+
+ return( ldap_result2error( ld, res, 1 ) );
+}
+
+/*
+ * ldap_kerberos_bind2 - initiate a bind to the X.500 server using
+ * kerberos authentication. The dn is supplied. It is assumed the user
+ * already has a valid ticket granting ticket. The msgid of the
+ * request is returned on success (suitable for passing to ldap_result()),
+ * -1 is returned if there's trouble.
+ *
+ * Example:
+ * ldap_kerberos_bind2( ld, "cn=manager, o=university of michigan, c=us" )
+ */
+int
+ldap_kerberos_bind2( LDAP *ld, char *dn )
+{
+ BerElement *ber;
+ char *cred;
+ int rc, credlen;
+ char *get_kerberosv4_credentials();
+#ifdef STR_TRANSLATION
+ int str_translation_on;
+#endif /* STR_TRANSLATION */
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_kerberos_bind2\n", 0, 0, 0 );
+
+ if ( dn == NULL )
+ dn = "";
+
+ if ( (cred = get_kerberosv4_credentials( ld, dn, "x500dsa", &credlen ))
+ == NULL ) {
+ return( -1 ); /* ld_errno should already be set */
+ }
+
+ /* create a message to send */
+ if ( (ber = alloc_ber_with_options( ld )) == NULLBER ) {
+ free( cred );
+ return( -1 );
+ }
+
+#ifdef STR_TRANSLATION
+ if (( str_translation_on = (( ber->ber_options &
+ LBER_TRANSLATE_STRINGS ) != 0 ))) { /* turn translation off */
+ ber->ber_options &= ~LBER_TRANSLATE_STRINGS;
+ }
+#endif /* STR_TRANSLATION */
+
+ /* fill it in */
+ rc = ber_printf( ber, "{it{isto}}", ++ld->ld_msgid, LDAP_REQ_BIND,
+ ld->ld_version, dn, LDAP_AUTH_KRBV42, cred, credlen );
+
+
+#ifdef STR_TRANSLATION
+ if ( str_translation_on ) { /* restore translation */
+ ber->ber_options |= LBER_TRANSLATE_STRINGS;
+ }
+#endif /* STR_TRANSLATION */
+
+ free( cred );
+
+ if ( rc == -1 ) {
+ ber_free( ber, 1 );
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ return( -1 );
+ }
+
+ /* send the message */
+ return ( send_initial_request( ld, LDAP_REQ_BIND, dn, ber ));
+}
+
+/* synchronous bind to DSA using kerberos */
+int
+ldap_kerberos_bind2_s( LDAP *ld, char *dn )
+{
+ int msgid;
+ LDAPMessage *res;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_kerberos_bind2_s\n", 0, 0, 0 );
+
+ /* initiate the bind */
+ if ( (msgid = ldap_kerberos_bind2( ld, dn )) == -1 )
+ return( ld->ld_errno );
+
+ /* wait for a result */
+ if ( ldap_result( ld, ld->ld_msgid, 1, (struct timeval *) 0, &res )
+ == -1 ) {
+ return( ld->ld_errno ); /* ldap_result sets ld_errno */
+ }
+
+ return( ldap_result2error( ld, res, 1 ) );
+}
+
+/* synchronous bind to ldap and DSA using kerberos */
+int
+ldap_kerberos_bind_s( LDAP *ld, char *dn )
+{
+ int err;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_kerberos_bind_s\n", 0, 0, 0 );
+
+ if ( (err = ldap_kerberos_bind1_s( ld, dn )) != LDAP_SUCCESS )
+ return( err );
+
+ return( ldap_kerberos_bind2_s( ld, dn ) );
+}
+
+
+#ifndef AUTHMAN
+#ifdef WINDOWS
+#define FreeWindowsLibrary() FreeLibrary(instKrbv4DLL);instKrbv4DLL = (HINSTANCE)NULL;
+#else /* WINDOWS */
+#define FreeWindowsLibrary() /* do nothing */
+#endif /* WINDOWS */
+/*
+ * get_kerberosv4_credentials - obtain kerberos v4 credentials for ldap.
+ * The dn of the entry to which to bind is supplied. It's assumed the
+ * user already has a tgt.
+ */
+
+char *
+get_kerberosv4_credentials( LDAP *ld, char *who, char *service, int *len )
+{
+ KTEXT_ST ktxt;
+ int err;
+ char realm[REALM_SZ], *cred, *krbinstance;
+#ifdef WINDOWS
+ typedef int (_cdecl* pfn_krb_get_tf_realm) (char FAR *,char FAR *);
+ typedef int (PASCAL* pfn_krb_mk_req) (KTEXT,LPSTR,LPSTR,LPSTR,long);
+ typedef char * (_cdecl* pfn_tkt_string) ();
+
+ static HINSTANCE instKrbv4DLL = (HINSTANCE)NULL;
+ pfn_krb_get_tf_realm fptr_krb_get_tf_realm = NULL;
+ pfn_krb_mk_req fptr_krb_mk_req = NULL;
+ pfn_tkt_string fptr_tkt_string = NULL;
+ char* p_tkt_string = NULL;
+ KTEXT pKt = &ktxt;
+#endif
+
+ Debug( LDAP_DEBUG_TRACE, "get_kerberosv4_credentials\n", 0, 0, 0 );
+
+#ifdef WINDOWS
+ /*
+ * The goal is to gracefully survive the absence of krbv4win.dll
+ * and thus wshelper.dll. User's won't be able to use kerberos,
+ * but they shouldn't see a message box everytime libldap.dll loads.
+ */
+ if ( !instKrbv4DLL ) {
+ unsigned int prevMode = SetErrorMode( SEM_NOOPENFILEERRORBOX ); // don't whine at user if you can't find it
+ instKrbv4DLL = LoadLibrary("Krbv4win.DLL");
+ SetErrorMode( prevMode );
+
+ if ( instKrbv4DLL < HINSTANCE_ERROR ) { // can't find authlib
+ ld->ld_errno = LDAP_AUTH_UNKNOWN;
+ return( NULL );
+ }
+
+ fptr_krb_get_tf_realm = (pfn_krb_get_tf_realm)GetProcAddress( instKrbv4DLL, "_krb_get_tf_realm" );
+ fptr_krb_mk_req = (pfn_krb_mk_req)GetProcAddress( instKrbv4DLL, "krb_mk_req" );
+ fptr_tkt_string = (pfn_tkt_string)GetProcAddress( instKrbv4DLL, "_tkt_string" );
+
+ // verify that we found all the routines we need
+ if (!(fptr_krb_mk_req && fptr_krb_get_tf_realm && fptr_tkt_string)) {
+ FreeWindowsLibrary();
+ ld->ld_errno = LDAP_AUTH_UNKNOWN;
+ return( NULL );
+ }
+
+ }
+ p_tkt_string = (fptr_tkt_string)( );
+ if ( (err = (fptr_krb_get_tf_realm)( p_tkt_string, realm )) != KSUCCESS ) {
+#else /* WINDOWS */
+ if ( (err = krb_get_tf_realm( tkt_string(), realm )) != KSUCCESS ) {
+#endif /* WINDOWS */
+#ifndef NO_USERINTERFACE
+ fprintf( stderr, "krb_get_tf_realm failed (%s)\n",
+ krb_err_txt[err] );
+#endif /* NO_USERINTERFACE */
+ ld->ld_errno = LDAP_INVALID_CREDENTIALS;
+ FreeWindowsLibrary();
+ return( NULL );
+ }
+
+#ifdef LDAP_REFERRALS
+ krbinstance = ld->ld_defconn->lconn_krbinstance;
+#else /* LDAP_REFERRALS */
+ krbinstance = ld->ld_host;
+#endif /* LDAP_REFERRALS */
+
+#ifdef WINDOWS
+ if ( !krbinstance ) { // if we don't know name of service host, no chance for service tickets
+ FreeWindowsLibrary();
+ ld->ld_errno = LDAP_LOCAL_ERROR;
+ WSASetLastError(WSANO_ADDRESS);
+ return( NULL );
+ }
+#endif /* WINDOWS */
+
+#ifdef WINDOWS
+ if ( (err = (fptr_krb_mk_req)( pKt, service, krbinstance, realm, 0 ))
+#else /* WINDOWS */
+ if ( (err = krb_mk_req( &ktxt, service, krbinstance, realm, 0 ))
+#endif /* WINDOWS */
+ != KSUCCESS ) {
+#ifndef NO_USERINTERFACE
+ fprintf( stderr, "krb_mk_req failed (%s)\n", krb_err_txt[err] );
+#endif /* NO_USERINTERFACE */
+ ld->ld_errno = LDAP_INVALID_CREDENTIALS;
+ FreeWindowsLibrary();
+ return( NULL );
+ }
+
+ if ( ( cred = malloc( ktxt.length )) == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ FreeWindowsLibrary();
+ return( NULL );
+ }
+
+ *len = ktxt.length;
+ memcpy( cred, ktxt.dat, ktxt.length );
+
+ FreeWindowsLibrary();
+ return( cred );
+}
+
+#endif /* !AUTHMAN */
+#endif /* KERBEROS */
diff --git a/ldap/ldap32.gid b/ldap/ldap32.gid
new file mode 100644
index 00000000..8f35a869
--- /dev/null
+++ b/ldap/ldap32.gid
Binary files differ
diff --git a/ldap/ldap32.hlp b/ldap/ldap32.hlp
new file mode 100755
index 00000000..f9de5446
--- /dev/null
+++ b/ldap/ldap32.hlp
Binary files differ
diff --git a/ldap/ldfilter.cfg b/ldap/ldfilter.cfg
new file mode 100755
index 00000000..c602f7d2
--- /dev/null
+++ b/ldap/ldfilter.cfg
@@ -0,0 +1,105 @@
+# ldap filter file
+#
+# lines like this that start with # or empty lines are ignored
+#
+# syntax:
+#
+# <tag>
+# <pattern1> <delimiters> <filter1-1> <desc1-1> [<scope>]
+# <filter1-2> <desc1-2> [<scope>]
+#
+# <pattern2> <delimiters> <filter2-1> <desc2-1> [<scope>] ...
+#
+# The "desc" should describe the filter and it should correctly complete
+# both of the following phrases:
+#
+# One <desc> match was found for...
+# Three <desc> matches were found for...
+#
+# The scope is optional, and should be one of:
+# "base"
+# "onelevel"
+# "subtree"
+# if it is included.
+#
+
+"finger and ud and go500 and go500gw subtree and web500gw subtree and rp500 and rcpt500 and ufn last"
+ "=" " " "%v" "arbitrary filter"
+
+ "^[0-9][0-9-]*$" " " "(telephoneNumber=*%v)" "phone number"
+
+ "@" " " "(mail=%v)" "email address"
+ "(mail=%v*)" "start of email address"
+
+ "^.[. _].*" ". _" "(cn=%v1* %v2-)" "first initial"
+
+ ".*[. _].$" ". _" "(cn=%v1-*)" "last initial"
+
+ "[. _]" ". _" "(|(sn=%v1-)(cn=%v1-))" "exact"
+ "(|(sn~=%v1-)(cn~=%v1-))" "approximate"
+
+ ".*" ". " "(|(cn=%v1)(sn=%v1)(uid=%v1))" "exact"
+ "(|(cn~=%v1)(sn~=%v1))" "approximate"
+
+"go500gw onelevel and web500gw onelevel and ufn first and ufn intermediate"
+ "=" " " "%v" "arbitrary filter"
+
+ "^..$" " " "(|(o=%v)(c=%v)(l=%v)(co=%v))" "exact"
+ "(|(o~=%v)(c~=%v)(l~=%v)(co~=%v))" "approximate"
+
+ " " " " "(|(o=%v)(l=%v)(co=%v)(ou=%v))" "exact"
+ "(|(o~=%v)(l~=%v)(co~=%v)(ou~=%v))" "approximate"
+
+ "\." " " "(associatedDomain=%v)" "exact"
+
+ ".*" " " "(|(o=%v)(l=%v)(co=%v)(ou=%v))" "exact"
+ "(|(o~=%v)(l~=%v)(co~=%v)(ou~=%v))" "approximate"
+
+
+#
+# xax500
+#
+
+"xax500"
+ "=" " " "(%v)" "arbitrary filter"
+
+ "^[0-9][0-9-]*$" " " "(telephoneNumber=*%v)" "phone number"
+
+ "@" " " "(mail=%v)" "email address"
+ "(mail=%v*)" "start of email address"
+
+ "^.[. _].*" ". _" "(cn=%v1* %v2-)" "first initial"
+
+ ".*[. _].$" ". _" "(cn=%v1-*)" "last initial"
+
+ "[. _]" ". _" "(|(sn=%v1-)(cn=%v1-))" "exact"
+ "(|(sn~=%v1-)(cn~=%v1-))" "approximate"
+
+ ".*" ". " "(|(cn=%v1)(sn=%v1)(uid=%v1))" "exact"
+ "(|(cn=%v1)(sn~=%v1))" "approximate"
+
+
+"xax500-auth"
+ "=" " " "(%v)" "arbitrary filter"
+
+ "^[0-9][0-9-]*$" " " "(telephoneNumber=*%v)" "phone number"
+
+ "@" " " "(mail=%v)" "email address"
+ "(mail=%v*)" "start of email address"
+
+ "^.[. _].*" ". _" "(cn=%v1* %v2-)" "first initial"
+
+ ".*[. _].$" ". _" "(cn=%v1-*)" "last initial"
+
+ "[. _]" ". _" "(|(sn=%v1-)(cn=%v1-))" "exact"
+ "(|(sn~=%v1-)(cn~=%v1-))" "approximate"
+
+ ".*" ". " "(|(cn=%v1)(sn=%v1)(uid=%v1))" "exact"
+ "(|(cn=%v1)(sn~=%v1))" "approximate"
+
+"list500"
+ "[. _]" ". _" "(|(sn=%v1-)(cn=%v1-))" "exact"
+ "(|(sn~=%v1-)(cn~=%v1-))" "approximate"
+
+ ".*" ". " "(|(cn=%v1)(sn=%v1)(uid=%v1))" "exact"
+ "(|(cn~=%v1)(sn~=%v1))" "approximate"
diff --git a/ldap/ldfriend.cfg b/ldap/ldfriend.cfg
new file mode 100755
index 00000000..3605090c
--- /dev/null
+++ b/ldap/ldfriend.cfg
@@ -0,0 +1,242 @@
+AD Andorra
+AE United Arab Emirates
+AF Afghanistan
+AG Antigua and Barbuda
+AI Anguilla
+AL Albania
+AM Armenia
+AN Netherlands Antilles
+AO Angola
+AQ Antarctica
+AR Argentina
+AS American Samoa
+AT Austria
+AU Australia
+AW Aruba
+AZ Azerbaijan
+BA Bosnia and Herzegowina
+BB Barbados
+BD Bangladesh
+BE Belgium
+BF Burkina Faso
+BG Bulgaria
+BH Bahrain
+BI Burundi
+BJ Benin
+BM Bermuda
+BN Brunei Darussalam
+BO Bolivia
+BR Brazil
+BS Bahamas
+BT Bhutan
+BV Bouvet Island
+BW Botswana
+BY Belarus
+BZ Belize
+CA Canada
+CC Cocos (Keeling) Islands
+CF Central African Republic
+CG Congo
+CH Switzerland
+CI Cote d'Ivoire
+CK Cook Islands
+CL Chile
+CM Cameroon
+CN China
+CO Colombia
+CR Costa Rica
+CS Former Czechoslovakia
+CU Cuba
+CV Cape Verde
+CX Christmas Island
+CY Cyprus
+CZ Czech Republic
+DE Germany
+DJ Djibouti
+DK Denmark
+DM Dominica
+DO Dominican Republic
+DZ Algeria
+EC Ecuador
+EE Estonia
+EG Egypt
+EH Western Sahara
+ER Eritrea
+ES Spain
+ET Ethiopia
+FI Finland
+FJ Fiji
+FK Falkland Islands (Malvinas)
+FM Micronesia
+FO Faroe Islands
+FR France
+FX France, Metropolitan
+GA Gabon
+GB United Kingdom
+GD Grenada
+GE Georgia
+GF French Guiana
+GH Ghana
+GI Gibraltar
+GL Greenland
+GM Gambia
+GN Guinea
+GP Guadeloupe
+GQ Equatorial Guinea
+GR Greece
+GS South Georgia and the South Sandwich Islands
+GT Guatemala
+GU Guam
+GW Guinea-Bissau
+GY Guyana
+HK Hong Kong
+HM Heard and McDonald Islands
+HN Honduras
+HR Croatia
+HT Haiti
+HU Hungary
+ID Indonesia
+IE Ireland
+IL Israel
+IN India
+IO British Indian Ocean Territory
+IQ Iraq
+IR Iran
+IS Iceland
+IT Italy
+JM Jamaica
+JO Jordan
+JP Japan
+KE Kenya
+KG Kyrgyzstan
+KH Cambodia
+KI Kiribati
+KM Comoros
+KN Saint Kitts and Nevis
+KP Korea, Democratic People's Republic of
+KR Korea, Republic of
+KW Kuwait
+KY Cayman Islands
+KZ Kazakhstan
+LA Laos
+LB Lebanon
+LC Saint Lucia
+LI Liechtenstein
+LK Sri Lanka
+LR Liberia
+LS Lesotho
+LT Lithuania
+LU Luxembourg
+LV Latvia
+LY Libya
+MA Morocco
+MC Monaco
+MD Moldova
+MG Madagascar
+MH Marshall Islands
+MK Macedonia
+ML Mali
+MM Myanmar
+MN Mongolia
+MO Macau
+MP Northern Mariana Islands
+MQ Martinique
+MR Mauritania
+MS Montserrat
+MT Malta
+MU Mauritius
+MV Maldives
+MW Malawi
+MX Mexico
+MY Malaysia
+MZ Mozambique
+NA Namibia
+NC New Caledonia
+NE Niger
+NF Norfolk Island
+NG Nigeria
+NI Nicaragua
+NL Netherlands
+NO Norway
+NP Nepal
+NR Nauru
+NU Niue
+NZ New Zealand
+OM Oman
+PA Panama
+PE Peru
+PF French Polynesia
+PG Papua New Guinea
+PH Philippines
+PK Pakistan
+PL Poland
+PM St. Pierre and Miquelon
+PN Pitcairn
+PR Puerto Rico
+PT Portugal
+PW Palau
+PY Paraguay
+QA Qatar
+RE Reunion
+RO Romania
+RU Russian Federation
+RW Rwanda
+SA Saudi Arabia
+SB Solomon Islands
+SC Seychelles
+SD Sudan
+SE Sweden
+SG Singapore
+SH St. Helena
+SI Slovenia
+SJ Svalbard and Jan Mayen Islands
+SK Slovakia (Slovak Republic)
+SL Sierra Leone
+SM San Marino
+SN Senegal
+SO Somalia
+SR Suriname
+ST Sao Tome and Principe
+SU Former Soviet Union
+SV El Salvador
+SY Syria
+SZ Swaziland
+TC Turks and Caicos Islands
+TD Chad
+TF French Southern Territories
+TG Togo
+TH Thailand
+TJ Tajikistan
+TK Tokelau
+TM Turkmenistan
+TN Tunisia
+TO Tonga
+TP East Timor
+TR Turkey
+TT Trinidad and Tobago
+TV Tuvalu
+TW Taiwan
+TZ Tanzania
+UA Ukraine
+UG Uganda
+UK United Kingdom
+UM United States Minor Outlying Islands
+US United States of America
+UY Uruguay
+UZ Uzbekistan
+VA Vatican City State (Holy See)
+VC Saint Vincent and the Grenadines
+VE Venezuela
+VG Virgin Islands (British)
+VI Virgin Islands (U.S.)
+VN Viet Nam
+VU Vanuatu
+WF Wallis and Futuna Islands
+WS Samoa
+YE Yemen
+YT Mayotte
+YU Yugoslavia
+ZA South Africa
+ZM Zambia
+ZR Zaire
+ZW Zimbabwe
diff --git a/ldap/lib b/ldap/lib
new file mode 120000
index 00000000..b545aebb
--- /dev/null
+++ b/ldap/lib
@@ -0,0 +1 @@
+binaries/release \ No newline at end of file
diff --git a/ldap/libldap.hlp b/ldap/libldap.hlp
new file mode 100755
index 00000000..981ef717
--- /dev/null
+++ b/ldap/libldap.hlp
Binary files differ
diff --git a/ldap/libldap.mak b/ldap/libldap.mak
new file mode 100755
index 00000000..6e9a2b98
--- /dev/null
+++ b/ldap/libldap.mak
@@ -0,0 +1,667 @@
+# Microsoft Visual C++ generated build script - Do not modify
+
+PROJ = LIBLDAP
+DEBUG = 0
+PROGTYPE = 1
+CALLER = c:\tmp\ltest.exe
+ARGS =
+DLLS =
+D_RCDEFINES = /d_DEBUG
+R_RCDEFINES = /dNDEBUG
+ORIGIN = MSVC
+ORIGIN_VER = 1.00
+PROJPATH = C:\SRC\LDAP\LIBRAR~1\LIBLDAP\
+USEMFC = 0
+CC = cl
+CPP = cl
+CXX = cl
+CCREATEPCHFLAG =
+CPPCREATEPCHFLAG =
+CUSEPCHFLAG =
+CPPUSEPCHFLAG =
+FIRSTC = ABANDON.C
+FIRSTCPP =
+RC = rc
+CFLAGS_D_WDLL = /nologo /G2 /W3 /Gf /Zi /ALu /Od /D "_DEBUG" /D "WINSOCK" /D "DOS" /D "NEEDPROTOS" /D "NO_USERINTERFACE" /D "KERBEROS" /FR /Fd"LIBLDAP.PDB"
+CFLAGS_R_WDLL = /nologo /f- /G3 /W3 /Gf /ALu /Od /D "NDEBUG" /D "WINSOCK" /D "DOS" /D "NEEDPROTOS" /D "NO_USERINTERFACE" /D "KERBEROS"
+LFLAGS_D_WDLL = /NOLOGO /NOD /NOE /PACKC:61440 /ALIGN:16 /ONERROR:NOEXE /CO /MAP:FULL
+LFLAGS_R_WDLL = /NOLOGO /NOD /NOE /PACKC:61440 /ALIGN:16 /ONERROR:NOEXE /MAP:FULL
+LIBS_D_WDLL = oldnames libw ldllcew commdlg.lib olecli.lib olesvr.lib shell.lib
+LIBS_R_WDLL = oldnames libw ldllcew commdlg.lib olecli.lib olesvr.lib shell.lib
+RCFLAGS = /nologo
+RESFLAGS = /nologo
+RUNFLAGS =
+DEFFILE = LIBLDAP.DEF
+OBJS_EXT =
+LIBS_EXT = WINSOCK.LIB
+!if "$(DEBUG)" == "1"
+CFLAGS = $(CFLAGS_D_WDLL)
+LFLAGS = $(LFLAGS_D_WDLL)
+LIBS = $(LIBS_D_WDLL)
+MAPFILE = nul
+RCDEFINES = $(D_RCDEFINES)
+!else
+CFLAGS = $(CFLAGS_R_WDLL)
+LFLAGS = $(LFLAGS_R_WDLL)
+LIBS = $(LIBS_R_WDLL)
+MAPFILE = nul
+RCDEFINES = $(R_RCDEFINES)
+!endif
+!if [if exist MSVC.BND del MSVC.BND]
+!endif
+SBRS = ABANDON.SBR \
+ ADD.SBR \
+ BIND.SBR \
+ CACHE.SBR \
+ COMPARE.SBR \
+ DELETE.SBR \
+ ERROR.SBR \
+ GETFILTE.SBR \
+ REGEX.SBR \
+ MODIFY.SBR \
+ MODRDN.SBR \
+ GETDN.SBR \
+ GETENTRY.SBR \
+ GETATTR.SBR \
+ GETVALUE.SBR \
+ ADDENTRY.SBR \
+ RESULT.SBR \
+ SEARCH.SBR \
+ UFN.SBR \
+ DECODE.SBR \
+ ENCODE.SBR \
+ IO.SBR \
+ MSDOS.SBR \
+ SBIND.SBR \
+ UNBIND.SBR \
+ KBIND.SBR \
+ FRIENDLY.SBR \
+ DISPTMPL.SBR \
+ DSPARSE.SBR \
+ FREE.SBR \
+ SORT.SBR \
+ SRCHPREF.SBR \
+ TMPLOUT.SBR \
+ REQUEST.SBR \
+ WSOCKIP.SBR \
+ OPEN.SBR \
+ CHARSET.SBR \
+ URL.SBR
+
+
+WINSOCK_DEP =
+
+ABANDON_DEP = c:\src\ldap\include\sys/socket.h \
+ c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\ldap.h \
+ c:\src\ldap\include\proto-ld.h \
+ c:\src\ldap\librar~1\libldap\ldap-int.h
+
+
+ADD_DEP = c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\sys/socket.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\ldap.h \
+ c:\src\ldap\include\proto-ld.h \
+ c:\src\ldap\librar~1\libldap\ldap-int.h
+
+
+BIND_DEP = c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\sys/socket.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\ldap.h \
+ c:\src\ldap\include\proto-ld.h
+
+
+CACHE_DEP = c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\sys/socket.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\ldap.h \
+ c:\src\ldap\include\proto-ld.h \
+ c:\src\ldap\librar~1\libldap\ldap-int.h
+
+
+COMPARE_DEP = c:\src\ldap\include\sys/socket.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\ldap.h \
+ c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\proto-ld.h \
+ c:\src\ldap\librar~1\libldap\ldap-int.h
+
+
+DELETE_DEP = c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\sys/socket.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\ldap.h \
+ c:\src\ldap\include\proto-ld.h \
+ c:\src\ldap\librar~1\libldap\ldap-int.h
+
+
+ERROR_DEP = c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\sys/socket.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\ldap.h \
+ c:\src\ldap\include\proto-ld.h
+
+
+GETFILTE_DEP = c:\src\ldap\include\regex.h \
+ c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\sys/file.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\ldap.h \
+ c:\src\ldap\include\proto-ld.h
+
+
+REGEX_DEP = c:\src\ldap\include\portable.h \
+ c:\src\ldap\include\regex.h
+
+
+MODIFY_DEP = c:\src\ldap\include\sys/socket.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\ldap.h \
+ c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\proto-ld.h \
+ c:\src\ldap\librar~1\libldap\ldap-int.h
+
+
+MODRDN_DEP = c:\src\ldap\include\sys/socket.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\ldap.h \
+ c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\proto-ld.h \
+ c:\src\ldap\librar~1\libldap\ldap-int.h
+
+
+GETDN_DEP = c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\sys/socket.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\ldap.h \
+ c:\src\ldap\include\proto-ld.h
+
+
+GETENTRY_DEP = c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\sys/socket.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\ldap.h \
+ c:\src\ldap\include\proto-ld.h
+
+
+GETATTR_DEP = c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\sys/socket.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\ldap.h \
+ c:\src\ldap\include\proto-ld.h \
+ c:\src\ldap\librar~1\libldap\ldap-int.h
+
+
+GETVALUE_DEP = c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\sys/socket.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\ldap.h \
+ c:\src\ldap\include\proto-ld.h
+
+
+ADDENTRY_DEP = c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\sys/socket.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\ldap.h \
+ c:\src\ldap\include\proto-ld.h
+
+
+RESULT_DEP = c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\sys/socket.h \
+ c:\src\ldap\include\sys/select.h \
+ c:\src\ldap\include\portable.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\ldap.h \
+ c:\src\ldap\include\proto-ld.h \
+ c:\src\ldap\librar~1\libldap\ldap-int.h
+
+
+SEARCH_DEP = c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\sys/socket.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\ldap.h \
+ c:\src\ldap\include\proto-ld.h \
+ c:\src\ldap\librar~1\libldap\ldap-int.h
+
+
+UFN_DEP = c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\sys/socket.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\ldap.h \
+ c:\src\ldap\include\proto-ld.h
+
+
+DECODE_DEP = c:\src\ldap\include\sys/socket.h \
+ c:\src\ldap\include\netinet/in.h \
+ c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h
+
+
+ENCODE_DEP = c:\src\ldap\include\sys/socket.h \
+ c:\src\ldap\include\netinet/in.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h
+
+
+IO_DEP = c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\sys/socket.h \
+ c:\src\ldap\include\netinet/in.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h
+
+
+MSDOS_DEP = c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\ldap.h \
+ c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\proto-ld.h
+
+
+SBIND_DEP = c:\src\ldap\include\sys/socket.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\ldap.h \
+ c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\proto-ld.h \
+ c:\src\ldap\librar~1\libldap\ldap-int.h
+
+
+UNBIND_DEP = c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\sys/socket.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\ldap.h \
+ c:\src\ldap\include\proto-ld.h \
+ c:\src\ldap\librar~1\libldap\ldap-int.h
+
+
+KBIND_DEP = c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\krb.h \
+ c:\src\ldap\include\mit_copy.h \
+ c:\src\ldap\include\conf.h \
+ c:\src\ldap\include\osconf.h \
+ c:\src\ldap\include\conf-pc.h \
+ c:\src\ldap\include\des.h \
+ c:\src\ldap\include\lsh_pwd.h \
+ c:\src\ldap\include\sys/socket.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\ldap.h \
+ c:\src\ldap\include\proto-ld.h \
+ c:\src\ldap\librar~1\libldap\ldap-int.h
+
+
+FRIENDLY_DEP = c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\sys/socket.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\ldap.h \
+ c:\src\ldap\include\proto-ld.h
+
+
+DISPTMPL_DEP = c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\sys/file.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\ldap.h \
+ c:\src\ldap\include\proto-ld.h \
+ c:\src\ldap\include\disptmpl.h
+
+
+DSPARSE_DEP = c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\sys/file.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\ldap.h \
+ c:\src\ldap\include\proto-ld.h
+
+
+FREE_DEP = c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\ldap.h \
+ c:\src\ldap\include\proto-ld.h
+
+
+SORT_DEP = c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\ldap.h \
+ c:\src\ldap\include\proto-ld.h
+
+
+SRCHPREF_DEP = c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\sys/file.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\ldap.h \
+ c:\src\ldap\include\proto-ld.h \
+ c:\src\ldap\include\srchpref.h
+
+
+TMPLOUT_DEP = c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\sys/file.h \
+ c:\src\ldap\include\sys/socket.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\ldap.h \
+ c:\src\ldap\include\proto-ld.h \
+ c:\src\ldap\include\disptmpl.h
+
+
+LIBLDAP_RCDEP =
+
+REQUEST_DEP = c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\sys/socket.h \
+ c:\src\ldap\include\sys/select.h \
+ c:\src\ldap\include\portable.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\ldap.h \
+ c:\src\ldap\include\proto-ld.h \
+ c:\src\ldap\librar~1\libldap\ldap-int.h
+
+
+WSOCKIP_DEP = c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\wshelper.h \
+ c:\src\ldap\include\resolv.h \
+ c:\src\ldap\include\arpa/nameser.h \
+ c:\src\ldap\include\hesiod.h \
+ c:\src\ldap\include\sys/socket.h \
+ c:\src\ldap\include\netinet/in.h \
+ c:\src\ldap\include\netdb.h \
+ c:\src\ldap\include\sys\socket.h \
+ c:\src\ldap\include\sys/select.h \
+ c:\src\ldap\include\portable.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\ldap.h \
+ c:\src\ldap\include\proto-ld.h \
+ c:\src\ldap\include\_sys/filio.h \
+ c:\src\ldap\include\sys/filio.h \
+ c:\src\ldap\include\_sys/ioctl.h \
+ c:\src\ldap\include\sys/ioctl.h
+
+
+OPEN_DEP = c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\sys/socket.h \
+ c:\src\ldap\include\sys/param.h \
+ c:\src\ldap\include\netinet/in.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\ldap.h \
+ c:\src\ldap\include\proto-ld.h \
+ c:\src\ldap\librar~1\libldap\ldap-int.h
+
+
+CHARSET_DEP = c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\sys/socket.h \
+ c:\src\ldap\include\sys/param.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\ldap.h \
+ c:\src\ldap\include\proto-ld.h \
+ c:\src\ldap\librar~1\libldap\ldap-int.h
+
+
+URL_DEP = c:\src\ldap\include\msdos.h \
+ c:\msvc\include\winsock.h \
+ c:\src\ldap\include\sys/socket.h \
+ c:\src\ldap\include\lber.h \
+ c:\src\ldap\include\proto-lb.h \
+ c:\src\ldap\include\ldap.h \
+ c:\src\ldap\include\proto-ld.h \
+ c:\src\ldap\librar~1\libldap\ldap-int.h
+
+
+all: $(PROJ).DLL
+
+ABANDON.OBJ: ABANDON.C $(ABANDON_DEP)
+ $(CC) $(CFLAGS) $(CCREATEPCHFLAG) /c ABANDON.C
+
+ADD.OBJ: ADD.C $(ADD_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c ADD.C
+
+BIND.OBJ: BIND.C $(BIND_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c BIND.C
+
+CACHE.OBJ: CACHE.C $(CACHE_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c CACHE.C
+
+COMPARE.OBJ: COMPARE.C $(COMPARE_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c COMPARE.C
+
+DELETE.OBJ: DELETE.C $(DELETE_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c DELETE.C
+
+ERROR.OBJ: ERROR.C $(ERROR_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c ERROR.C
+
+GETFILTE.OBJ: GETFILTE.C $(GETFILTE_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c GETFILTE.C
+
+REGEX.OBJ: REGEX.C $(REGEX_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c REGEX.C
+
+MODIFY.OBJ: MODIFY.C $(MODIFY_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c MODIFY.C
+
+MODRDN.OBJ: MODRDN.C $(MODRDN_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c MODRDN.C
+
+GETDN.OBJ: GETDN.C $(GETDN_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c GETDN.C
+
+GETENTRY.OBJ: GETENTRY.C $(GETENTRY_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c GETENTRY.C
+
+GETATTR.OBJ: GETATTR.C $(GETATTR_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c GETATTR.C
+
+GETVALUE.OBJ: GETVALUE.C $(GETVALUE_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c GETVALUE.C
+
+ADDENTRY.OBJ: ADDENTRY.C $(ADDENTRY_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c ADDENTRY.C
+
+RESULT.OBJ: RESULT.C $(RESULT_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c RESULT.C
+
+SEARCH.OBJ: SEARCH.C $(SEARCH_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c SEARCH.C
+
+UFN.OBJ: UFN.C $(UFN_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c UFN.C
+
+DECODE.OBJ: ..\LIBLBER\DECODE.C $(DECODE_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c ..\LIBLBER\DECODE.C
+
+ENCODE.OBJ: ..\LIBLBER\ENCODE.C $(ENCODE_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c ..\LIBLBER\ENCODE.C
+
+IO.OBJ: ..\LIBLBER\IO.C $(IO_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c ..\LIBLBER\IO.C
+
+MSDOS.OBJ: MSDOS.C $(MSDOS_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c MSDOS.C
+
+SBIND.OBJ: SBIND.C $(SBIND_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c SBIND.C
+
+UNBIND.OBJ: UNBIND.C $(UNBIND_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c UNBIND.C
+
+KBIND.OBJ: KBIND.C $(KBIND_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c KBIND.C
+
+FRIENDLY.OBJ: FRIENDLY.C $(FRIENDLY_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c FRIENDLY.C
+
+DISPTMPL.OBJ: DISPTMPL.C $(DISPTMPL_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c DISPTMPL.C
+
+DSPARSE.OBJ: DSPARSE.C $(DSPARSE_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c DSPARSE.C
+
+FREE.OBJ: FREE.C $(FREE_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c FREE.C
+
+SORT.OBJ: SORT.C $(SORT_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c SORT.C
+
+SRCHPREF.OBJ: SRCHPREF.C $(SRCHPREF_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c SRCHPREF.C
+
+TMPLOUT.OBJ: TMPLOUT.C $(TMPLOUT_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c TMPLOUT.C
+
+LIBLDAP.RES: LIBLDAP.RC $(LIBLDAP_RCDEP)
+ $(RC) $(RCFLAGS) $(RCDEFINES) -r LIBLDAP.RC
+
+REQUEST.OBJ: REQUEST.C $(REQUEST_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c REQUEST.C
+
+WSOCKIP.OBJ: WSOCKIP.C $(WSOCKIP_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c WSOCKIP.C
+
+OPEN.OBJ: OPEN.C $(OPEN_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c OPEN.C
+
+CHARSET.OBJ: CHARSET.C $(CHARSET_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c CHARSET.C
+
+URL.OBJ: URL.C $(URL_DEP)
+ $(CC) $(CFLAGS) $(CUSEPCHFLAG) /c URL.C
+
+
+$(PROJ).DLL:: LIBLDAP.RES
+
+$(PROJ).DLL:: ABANDON.OBJ ADD.OBJ BIND.OBJ CACHE.OBJ COMPARE.OBJ DELETE.OBJ ERROR.OBJ \
+ GETFILTE.OBJ REGEX.OBJ MODIFY.OBJ MODRDN.OBJ GETDN.OBJ GETENTRY.OBJ GETATTR.OBJ GETVALUE.OBJ \
+ ADDENTRY.OBJ RESULT.OBJ SEARCH.OBJ UFN.OBJ DECODE.OBJ ENCODE.OBJ IO.OBJ MSDOS.OBJ \
+ SBIND.OBJ UNBIND.OBJ KBIND.OBJ FRIENDLY.OBJ DISPTMPL.OBJ DSPARSE.OBJ FREE.OBJ SORT.OBJ \
+ SRCHPREF.OBJ TMPLOUT.OBJ REQUEST.OBJ WSOCKIP.OBJ OPEN.OBJ CHARSET.OBJ URL.OBJ $(OBJS_EXT) $(DEFFILE)
+ echo >NUL @<<$(PROJ).CRF
+ABANDON.OBJ +
+ADD.OBJ +
+BIND.OBJ +
+CACHE.OBJ +
+COMPARE.OBJ +
+DELETE.OBJ +
+ERROR.OBJ +
+GETFILTE.OBJ +
+REGEX.OBJ +
+MODIFY.OBJ +
+MODRDN.OBJ +
+GETDN.OBJ +
+GETENTRY.OBJ +
+GETATTR.OBJ +
+GETVALUE.OBJ +
+ADDENTRY.OBJ +
+RESULT.OBJ +
+SEARCH.OBJ +
+UFN.OBJ +
+DECODE.OBJ +
+ENCODE.OBJ +
+IO.OBJ +
+MSDOS.OBJ +
+SBIND.OBJ +
+UNBIND.OBJ +
+KBIND.OBJ +
+FRIENDLY.OBJ +
+DISPTMPL.OBJ +
+DSPARSE.OBJ +
+FREE.OBJ +
+SORT.OBJ +
+SRCHPREF.OBJ +
+TMPLOUT.OBJ +
+REQUEST.OBJ +
+WSOCKIP.OBJ +
+OPEN.OBJ +
+CHARSET.OBJ +
+URL.OBJ +
+$(OBJS_EXT)
+$(PROJ).DLL
+$(MAPFILE)
+c:\msvc\lib\+
+c:\msvc\mfc\lib\+
+c:\src\lib\+
+WINSOCK.LIB+
+$(LIBS)
+$(DEFFILE);
+<<
+ link $(LFLAGS) @$(PROJ).CRF
+ $(RC) $(RESFLAGS) LIBLDAP.RES $@
+ @copy $(PROJ).CRF MSVC.BND
+ implib /nowep $(PROJ).LIB $(PROJ).DLL
+
+$(PROJ).DLL:: LIBLDAP.RES
+ if not exist MSVC.BND $(RC) $(RESFLAGS) LIBLDAP.RES $@
+
+run: $(PROJ).DLL
+ $(PROJ) $(RUNFLAGS)
+
+
+$(PROJ).BSC: $(SBRS)
+ bscmake @<<
+/o$@ $(SBRS)
+<<
diff --git a/ldap/readme.txt b/ldap/readme.txt
new file mode 100755
index 00000000..c504f4f0
--- /dev/null
+++ b/ldap/readme.txt
@@ -0,0 +1,218 @@
+
+LDAP (Lightweight Directory Access Protocol) API for Windows/Winsock
+
+(Change history is at the bottom of this file.)
+
+The lber and ldap client libraries have been ported to Microsoft Windows
+in the form of Windows Dynamic Link libraries called LIBLDAP.DLL (16Bit)
+and Ldap32.dll (32Bit). The LTest program is also provided in both
+formats.
+
+A Windows Sockets API (version 1.1 conformant) TCP/IP WINSOCK.DLL or
+WSOCK32.DLL is required for the DLL to run.
+
+Our intent is that this "kit" include everything you'll need to make use
+of the ldap client API from your 16Bit or 32Bit application. If you
+find something missing or have a suggestion for improvement, send email
+to the "bug reporting" address at the bottom of this file.
+
+To use this "kit"
+
+ 1) Get to a DOS prompt
+
+ 2) Create the directory you want this to live in (e.g. \ldap)
+ and cd into it. We will refer to that directory simply as
+ "\ldap" from now on, but it could be anywhere and have any name
+ you desire.
+
+ 3) Use "pkunzip -d" to extract the files. The "-d" is NECESSARY to
+ preserve the subdirectories and avoid file name collisions.
+
+ 4) We have included only the files you need to use and test
+ libldap.dll and ldap32.dll. If you want the entire distribution,
+ with source, you can get it from:
+
+ ftp://terminator.rs.itd.umich.edu/ldap/ldap-3.3.tar.Z
+
+The following files are included in this distribution:
+
+ 16Bit binaries and libs
+ BINARIES/DEBUG/LIBLDAP.DLL
+ BINARIES/DEBUG/LIBLDAP.LIB
+ BINARIES/RELEASE/LIBLDAP.DLL
+ BINARIES/RELEASE/LIBLDAP.LIB
+
+ BINARIES/DEBUG/LTEST.EXE
+
+ 32Bit binaries and libs
+ BINARIES/DEBUG/LDAP32.DLL
+ BINARIES/DEBUG/LDAP32.LIB
+ BINARIES/RELEASE/LDAP32.DLL
+ BINARIES/RELEASE/LDAP32.LIB
+
+ BINARIES/DEBUG/LTEST32.EXE
+
+ Include files
+ INCKIT/MSDOS.H
+ INCKIT/LBER.H
+ INCKIT/LDAP.H
+ INCKIT/PROTO-LD.H
+ INCKIT/PROTO-LB.H
+ INCKIT/SRCHPREF.H
+ INCKIT/DISPTMPL.H
+
+ Sample Configuration files
+ SRCHPREF.CFG
+ DISPTMPL.CFG
+ LDFRIEND.CFG
+ LDFILTER.CFG
+
+ Man pages in the form of Windows HLP files
+ LIBLDAP.HLP - old format hlp file
+ LDAP32.HLP - new format hlp file, both have same content
+
+16Bit versions
+
+ Libldap.dll was compiled with KERBEROS, AUTHMAN, WSHELPER, WIN32,
+ _WINDOWS,& LDAP_REFERRALS defined. Even if you do not need kerberos
+ authentication, (see below for more information on kerberos) this
+ dll should work correctly for you.
+
+ LDAP_REFERRALS makes libldap.dll capable of handling referrals
+ returned by a slapd server.
+
+32Bit versions
+
+ The 32Bit version is NOT SAFE for MULTIPLE THREADS at this time.
+ Not more than one thread per application may make use of the
+ ldap routines.
+
+ Ldap32.dll was compiled with LDAP_REFERRALS defined and is capable
+ of handling referrals returned by a slapd server.
+
+
+WRITING APPLICATIONS THAT USE LIBLDAP.DLL or LDAP32.DLL
+
+ All of the normal LDAP and LBER calls documented in the help file
+ should work, except for ldap_perror (this is not supported under
+ Windows since you will want to use an application-defined dialog;
+ you can use ldap_err2string to obtain an error string to display in
+ a message box or dialog).
+
+ The man pages are included in this kit in the form of windows HLP files.
+ The official source man pages are available via the web at:
+
+ http://www.umich.edu/ldap/doc/man/
+
+ Any memory that you obtain as the result of a call to an LIBLDAP.DLL
+ routine should NOT be freed by calling the free() routine in your C
+ library. Instead, use the the new utility routine ldap_memfree or
+ the appropriate ldap ...free routine. This is so the malloc/calloc
+ and free routines all come from the same library (the one in
+ libldap) rather than using libldap's malloc/calloc and the calling
+ program's free. Microsoft's VC++ 4.0 compiler (in debug mode)
+ FORCED me to be compulsive about this for the application I used to
+ test.
+
+ To be friendly under Windows, you should use the asynchronous LDAP
+ calls whenever possible.
+
+ One limitation of the current LIBLDAP.DLL is that each X.500 LDAP
+ result message has to be smaller than 64K bytes. Ldap32.dll does
+ NOT have this limitation.
+
+ To compile the ldap dlls we define the following preprocessor variables.
+
+ WINSOCK, DOS, NEEDPROTOS, NO_USERINTERFACE, KERBEROS
+
+ Presumably you don't need KERBEROS. You may need some/all the others
+ to take the right path through the include files. Also note that a
+ few more preprocessor variables are defined in msdos.h. This means that
+ msdos.h must be included before ldap.h or lber.h.
+
+
+LTest and LTtest32
+
+ The LTest.exe and LTest32.exe programs are test interfaces to libldap
+ and ldap32 respectively. By default they connect to the host
+ "truelies". This host name is contained in a string resource in the
+ exe file. You may easily "customize" this to be the name of whatever
+ server you're using with AppStudio or any Windows resource editor.
+
+Kerberos Information
+
+ Libldap.dll was compiled with KERBEROS, AUTHMAN, WSHELPER, &
+ LDAP_REFERRALS defined. If you do not need kerberos authentication,
+ this dll should still work correctly for you. Libldap.dll
+ dynamically loads and uses the dlls needed for kerberos
+ authentication (Authlib.dll, Krbv4win.dll, & WSHelper.dll). If
+ Libldap.dll is unable to load the needed dlls, execution continues
+ without error, but without kerberos authentication capability.
+
+ AUTHMAN allows libldap.dll to make use of Authlib.dll (which
+ requires KrbV4Win.dll & WSHelper.dll) if they are ALL in the "PATH".
+ If these are not available, kerberos authentication can not succede,
+ but libldap.dll will execute without error.
+
+ WSHELPER means that if WSHelper.dll is in the "PATH", it will be
+ dynamically loaded and used to do the gethostbyaddr() call required
+ for kerberos authentication to work. (This is used because so many
+ vendor implementations of gethostbyaddr return WRONG results. We
+ are working with all vendors we can get to listen to get these
+ implementations fixed.) If WSHelper.dll is not in the "PATH"
+ libldap.dll does not fail to execute correctly.
+
+ Ldap32.dll does NOT have the ability to do kerberos authentication
+ because none of Authlib.dll, krbv4win.dll or wshelper.dll have been
+ ported to 32Bits at this time.
+
+ For further information on using kerberos with the ldap DLLs send
+ email to ldap-support@umich.edu.
+
+BUG REPORTING
+
+ Bug reports should be sent to bug-ldap@umich.edu.
+
+
+Miscellaneous
+
+ Build testing was done on Windows NT workstation 3.51 (build 1057
+ service pack 2) on an NTFS file system (which supports long
+ filenames) using Microsoft Visual C++ 1.52c (16 bit) and Visual C++
+ 4.0 (32 bit).
+
+Change History:
+
+ 2 May 1996
+ o based on LDAP 3.3 source
+ o correct bug that caused error message box about problems
+ wshelper.dll. Made use of krbv4win and thus wshelper
+ dynamic. They will be used if present, if not
+ get_kerberosv4_credentials will set ld->ld_errno =
+ LDAP_AUTH_UNKNOWN and return a NULL pointer.
+ o this required changes to libldap.mak and kbind.c.
+ Since these changed files did not make it into the
+ official tar file, I've included them here.
+
+ 2 Aug 1996
+ o WSAStartup() was not being called before htons was used.
+ WSAStartup() call was moved to correct this problem. This may
+ change the number of calls to WSAStartup() and the pairing of
+ these with WSACleanup();
+ o 32 bit Release binaries of ldap were not built using the DEF file.
+ This caused the ordinals (and the LIB files) to be different.
+ Both 32 bit binaries now use same DEF, same ordinals, LIBs compare.
+
+ 23 Sep 1996 : sgr
+ o libldap.dll uses OutputDebugString to emit debug messages. This
+ was not getting disabled in the Release version. Simple change to
+ msdos.h made LDAP_DEBUG depend on _DEBUG which fixed this problem.
+
+ 17 Oct 1996 : sgr
+ o 4 of the .h files were missing the ^M at EOL that DOS/WIN requires
+ These have been fixed and replaced in the zip file. They were
+ disptmpl.h, ldap.h, proto-ld.h, & proto-lb.h
+
+------------------------------------------------------------------------
+
+README Last updated 17 Oct 1996 by Steve Rothwell
diff --git a/ldap/srchpref.cfg b/ldap/srchpref.cfg
new file mode 100755
index 00000000..74be091d
--- /dev/null
+++ b/ldap/srchpref.cfg
@@ -0,0 +1,153 @@
+# Version should be 1 now
+Version 1
+#
+#
+# Name for this search object
+People
+# options (the only one supported right now is "internal" which means that
+# this search object should not be presented directly to the user)
+# use "" for none
+""
+# Label to place before text box user types in
+"Search For:"
+# Filter prefix to append to all "More Choices" searches
+"(&(objectClass=person)"
+# Tag to use for "Fewer Choices" searches - from ldapfilter.conf file
+"xax500"
+# If a search results in > 1 match, retrieve this attribute to help
+# user disambiguate the entries...
+title
+# ...and label it with this string:
+"Title"
+# Search scope to use when searching
+subtree
+# Follows a list of "More Choices" search options. Format is:
+# Label, attribute, select-bitmap, extra attr display name, extra attr ldap name
+# If last two are null, "Fewer Choices" name/attributes used
+"Common Name" cn 11111 "" ""
+"Surname" sn 11111 "" ""
+"Business Phone" "telephoneNumber" 11101 "" ""
+"E-Mail Address" "mail" 11111 "" ""
+"Uniqname" "uid" 11111 "" ""
+"Title" title 11111 "" ""
+END
+# Match types
+"exactly matches" "(%a=%v))"
+"approximately matches" "(%a~=%v))"
+"starts with" "(%a=%v*))"
+"ends with" "(%a=*%v))"
+"contains" "(%a=*%v*))"
+END
+#
+#
+#
+Groups
+""
+"Search For:"
+"(&(objectClass=rfc822MailGroup)"
+"xax500"
+multilineDescription
+"Description"
+subtree
+"Common Name" cn 11111 "" ""
+"Description" multilineDescription 11101 "" ""
+"Owner" "owner" 00001 "owner" "Owner"
+"X.500 Member" "member" 00001 "" ""
+"E-Mail Member" "mail" 00101 "" ""
+END
+"exactly matches" "(%a=%v))"
+"approximately matches" "(%a~=%v))"
+"starts with" "(%a=%v*))"
+"ends with" "(%a=*%v))"
+"contains" "(%a=*%v*))"
+END
+#
+#
+#
+"Joinable Groups"
+""
+"Search For:"
+"(&(&(objectClass=rfc822MailGroup)(joinable=TRUE))"
+"xax500"
+multilineDescription
+"Description"
+subtree
+"Common Name" cn 11111 "" ""
+"Description" multilineDescription 11101 "" ""
+"Owner" "owner" 00001 "owner" "Owner"
+"X.500 Member" "member" 00001 "" ""
+"E-Mail Member" "mail" 00101 "" ""
+END
+"exactly matches" "(%a=%v))"
+"approximately matches" "(%a~=%v))"
+"starts with" "(%a=%v*))"
+"ends with" "(%a=*%v))"
+"contains" "(%a=*%v*))"
+END
+#
+#
+#
+Services
+""
+"Search For:"
+"(&(objectClass=service)"
+"xax500"
+multilineDescription
+"Description"
+subtree
+"Common Name" cn 11111 "" ""
+"Description" multilineDescription 11101 "" ""
+"Owner" "owner" 00001 "owner" "Owner"
+"Keywords" "keywords" 11111 "" ""
+"Hours" "hoursOfOperation" 11111 "" ""
+END
+"exactly matches" "(%a=%v))"
+"approximately matches" "(%a~=%v))"
+"starts with" "(%a=%v*))"
+"ends with" "(%a=*%v))"
+"contains" "(%a=*%v*))"
+END
+#
+#
+#
+Organizations
+""
+"Search For:"
+"(&(objectClass=organization)"
+"xax500"
+multilineDescription
+"Description"
+subtree
+"Name" organizationName 01111 "" ""
+"Location" localityName 11111 "" ""
+"Phone Number" "telephoneNumber" 10111 "" ""
+"Description" description 10111 "" ""
+END
+"exactly matches" "(%a=%v))"
+"approximately matches" "(%a~=%v))"
+"starts with" "(%a=%v*))"
+"ends with" "(%a=*%v))"
+"contains" "(%a=*%v*))"
+END
+#
+#
+#
+Documents
+""
+"Search For:"
+"(&(objectClass=document)"
+"xax500"
+multilineDescription
+"Description"
+subtree
+"Document Title" cn 11111 "" ""
+"Keyword" "keywords" 11111 "" ""
+"Category" "category" 11111 "" ""
+"Document Number" "documentIdentifier" 11111 "" ""
+END
+"exactly matches" "(%a=%v))"
+"approximately matches" "(%a~=%v))"
+"starts with" "(%a=%v*))"
+"ends with" "(%a=*%v))"
+"contains" "(%a=*%v*))"
+END
diff --git a/ltmain.sh b/ltmain.sh
new file mode 100755
index 00000000..950feee3
--- /dev/null
+++ b/ltmain.sh
@@ -0,0 +1,8406 @@
+# Generated from ltmain.m4sh.
+
+# ltmain.sh (GNU libtool) 2.2.6b
+# Written by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
+
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007 2008 Free Software Foundation, Inc.
+# This is free software; see the source for copying conditions. There is NO
+# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+# GNU Libtool 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.
+#
+# As a special exception to the GNU General Public License,
+# if you distribute this file as part of a program or library that
+# is built using GNU Libtool, you may include this file under the
+# same distribution terms that you use for the rest of that program.
+#
+# GNU Libtool 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 GNU Libtool; see the file COPYING. If not, a copy
+# can be downloaded from http://www.gnu.org/licenses/gpl.html,
+# or obtained by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+# Usage: $progname [OPTION]... [MODE-ARG]...
+#
+# Provide generalized library-building support services.
+#
+# --config show all configuration variables
+# --debug enable verbose shell tracing
+# -n, --dry-run display commands without modifying any files
+# --features display basic configuration information and exit
+# --mode=MODE use operation mode MODE
+# --preserve-dup-deps don't remove duplicate dependency libraries
+# --quiet, --silent don't print informational messages
+# --tag=TAG use configuration variables from tag TAG
+# -v, --verbose print informational messages (default)
+# --version print version information
+# -h, --help print short or long help message
+#
+# MODE must be one of the following:
+#
+# clean remove files from the build directory
+# compile compile a source file into a libtool object
+# execute automatically set library path, then run a program
+# finish complete the installation of libtool libraries
+# install install libraries or executables
+# link create a library or an executable
+# uninstall remove libraries from an installed directory
+#
+# MODE-ARGS vary depending on the MODE.
+# Try `$progname --help --mode=MODE' for a more detailed description of MODE.
+#
+# When reporting a bug, please describe a test case to reproduce it and
+# include the following information:
+#
+# host-triplet: $host
+# shell: $SHELL
+# compiler: $LTCC
+# compiler flags: $LTCFLAGS
+# linker: $LD (gnu? $with_gnu_ld)
+# $progname: (GNU libtool) 2.2.6b
+# automake: $automake_version
+# autoconf: $autoconf_version
+#
+# Report bugs to <bug-libtool@gnu.org>.
+
+PROGRAM=ltmain.sh
+PACKAGE=libtool
+VERSION=2.2.6b
+TIMESTAMP=""
+package_revision=1.3018
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac
+fi
+BIN_SH=xpg4; export BIN_SH # for Tru64
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# NLS nuisances: We save the old values to restore during execute mode.
+# Only set LANG and LC_ALL to C if already set.
+# These must not be set unconditionally because not all systems understand
+# e.g. LANG=C (notably SCO).
+lt_user_locale=
+lt_safe_locale=
+for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES
+do
+ eval "if test \"\${$lt_var+set}\" = set; then
+ save_$lt_var=\$$lt_var
+ $lt_var=C
+ export $lt_var
+ lt_user_locale=\"$lt_var=\\\$save_\$lt_var; \$lt_user_locale\"
+ lt_safe_locale=\"$lt_var=C; \$lt_safe_locale\"
+ fi"
+done
+
+$lt_unset CDPATH
+
+
+
+
+
+: ${CP="cp -f"}
+: ${ECHO="echo"}
+: ${EGREP="/usr/bin/grep -E"}
+: ${FGREP="/usr/bin/grep -F"}
+: ${GREP="/usr/bin/grep"}
+: ${LN_S="ln -s"}
+: ${MAKE="make"}
+: ${MKDIR="mkdir"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+: ${SED="/usr/bin/sed"}
+: ${SHELL="${CONFIG_SHELL-/bin/sh}"}
+: ${Xsed="$SED -e 1s/^X//"}
+
+# Global variables:
+EXIT_SUCCESS=0
+EXIT_FAILURE=1
+EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing.
+EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake.
+
+exit_status=$EXIT_SUCCESS
+
+# Make sure IFS has a sensible default
+lt_nl='
+'
+IFS=" $lt_nl"
+
+dirname="s,/[^/]*$,,"
+basename="s,^.*/,,"
+
+# func_dirname_and_basename file append nondir_replacement
+# perform func_basename and func_dirname in a single function
+# call:
+# dirname: Compute the dirname of FILE. If nonempty,
+# add APPEND to the result, otherwise set result
+# to NONDIR_REPLACEMENT.
+# value returned in "$func_dirname_result"
+# basename: Compute filename of FILE.
+# value retuned in "$func_basename_result"
+# Implementation must be kept synchronized with func_dirname
+# and func_basename. For efficiency, we do not delegate to
+# those functions but instead duplicate the functionality here.
+func_dirname_and_basename ()
+{
+ # Extract subdirectory from the argument.
+ func_dirname_result=`$ECHO "X${1}" | $Xsed -e "$dirname"`
+ if test "X$func_dirname_result" = "X${1}"; then
+ func_dirname_result="${3}"
+ else
+ func_dirname_result="$func_dirname_result${2}"
+ fi
+ func_basename_result=`$ECHO "X${1}" | $Xsed -e "$basename"`
+}
+
+# Generated shell functions inserted here.
+
+# Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh
+# is ksh but when the shell is invoked as "sh" and the current value of
+# the _XPG environment variable is not equal to 1 (one), the special
+# positional parameter $0, within a function call, is the name of the
+# function.
+progpath="$0"
+
+# The name of this program:
+# In the unlikely event $progname began with a '-', it would play havoc with
+# func_echo (imagine progname=-n), so we prepend ./ in that case:
+func_dirname_and_basename "$progpath"
+progname=$func_basename_result
+case $progname in
+ -*) progname=./$progname ;;
+esac
+
+# Make sure we have an absolute path for reexecution:
+case $progpath in
+ [\\/]*|[A-Za-z]:\\*) ;;
+ *[\\/]*)
+ progdir=$func_dirname_result
+ progdir=`cd "$progdir" && pwd`
+ progpath="$progdir/$progname"
+ ;;
+ *)
+ save_IFS="$IFS"
+ IFS=:
+ for progdir in $PATH; do
+ IFS="$save_IFS"
+ test -x "$progdir/$progname" && break
+ done
+ IFS="$save_IFS"
+ test -n "$progdir" || progdir=`pwd`
+ progpath="$progdir/$progname"
+ ;;
+esac
+
+# Sed substitution that helps us do robust quoting. It backslashifies
+# metacharacters that are still active within double-quoted strings.
+Xsed="${SED}"' -e 1s/^X//'
+sed_quote_subst='s/\([`"$\\]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\(["`\\]\)/\\\1/g'
+
+# Re-`\' parameter expansions in output of double_quote_subst that were
+# `\'-ed in input to the same. If an odd number of `\' preceded a '$'
+# in input to double_quote_subst, that '$' was protected from expansion.
+# Since each input `\' is now two `\'s, look for any number of runs of
+# four `\'s followed by two `\'s and then a '$'. `\' that '$'.
+bs='\\'
+bs2='\\\\'
+bs4='\\\\\\\\'
+dollar='\$'
+sed_double_backslash="\
+ s/$bs4/&\\
+/g
+ s/^$bs2$dollar/$bs&/
+ s/\\([^$bs]\\)$bs2$dollar/\\1$bs2$bs$dollar/g
+ s/\n//g"
+
+# Standard options:
+opt_dry_run=false
+opt_help=false
+opt_quiet=false
+opt_verbose=false
+opt_warning=:
+
+# func_echo arg...
+# Echo program name prefixed message, along with the current mode
+# name if it has been set yet.
+func_echo ()
+{
+ $ECHO "$progname${mode+: }$mode: $*"
+}
+
+# func_verbose arg...
+# Echo program name prefixed message in verbose mode only.
+func_verbose ()
+{
+ $opt_verbose && func_echo ${1+"$@"}
+
+ # A bug in bash halts the script if the last line of a function
+ # fails when set -e is in force, so we need another command to
+ # work around that:
+ :
+}
+
+# func_error arg...
+# Echo program name prefixed message to standard error.
+func_error ()
+{
+ $ECHO "$progname${mode+: }$mode: "${1+"$@"} 1>&2
+}
+
+# func_warning arg...
+# Echo program name prefixed warning message to standard error.
+func_warning ()
+{
+ $opt_warning && $ECHO "$progname${mode+: }$mode: warning: "${1+"$@"} 1>&2
+
+ # bash bug again:
+ :
+}
+
+# func_fatal_error arg...
+# Echo program name prefixed message to standard error, and exit.
+func_fatal_error ()
+{
+ func_error ${1+"$@"}
+ exit $EXIT_FAILURE
+}
+
+# func_fatal_help arg...
+# Echo program name prefixed message to standard error, followed by
+# a help hint, and exit.
+func_fatal_help ()
+{
+ func_error ${1+"$@"}
+ func_fatal_error "$help"
+}
+help="Try \`$progname --help' for more information." ## default
+
+
+# func_grep expression filename
+# Check whether EXPRESSION matches any line of FILENAME, without output.
+func_grep ()
+{
+ $GREP "$1" "$2" >/dev/null 2>&1
+}
+
+
+# func_mkdir_p directory-path
+# Make sure the entire path to DIRECTORY-PATH is available.
+func_mkdir_p ()
+{
+ my_directory_path="$1"
+ my_dir_list=
+
+ if test -n "$my_directory_path" && test "$opt_dry_run" != ":"; then
+
+ # Protect directory names starting with `-'
+ case $my_directory_path in
+ -*) my_directory_path="./$my_directory_path" ;;
+ esac
+
+ # While some portion of DIR does not yet exist...
+ while test ! -d "$my_directory_path"; do
+ # ...make a list in topmost first order. Use a colon delimited
+ # list incase some portion of path contains whitespace.
+ my_dir_list="$my_directory_path:$my_dir_list"
+
+ # If the last portion added has no slash in it, the list is done
+ case $my_directory_path in */*) ;; *) break ;; esac
+
+ # ...otherwise throw away the child directory and loop
+ my_directory_path=`$ECHO "X$my_directory_path" | $Xsed -e "$dirname"`
+ done
+ my_dir_list=`$ECHO "X$my_dir_list" | $Xsed -e 's,:*$,,'`
+
+ save_mkdir_p_IFS="$IFS"; IFS=':'
+ for my_dir in $my_dir_list; do
+ IFS="$save_mkdir_p_IFS"
+ # mkdir can fail with a `File exist' error if two processes
+ # try to create one of the directories concurrently. Don't
+ # stop in that case!
+ $MKDIR "$my_dir" 2>/dev/null || :
+ done
+ IFS="$save_mkdir_p_IFS"
+
+ # Bail out if we (or some other process) failed to create a directory.
+ test -d "$my_directory_path" || \
+ func_fatal_error "Failed to create \`$1'"
+ fi
+}
+
+
+# func_mktempdir [string]
+# Make a temporary directory that won't clash with other running
+# libtool processes, and avoids race conditions if possible. If
+# given, STRING is the basename for that directory.
+func_mktempdir ()
+{
+ my_template="${TMPDIR-/tmp}/${1-$progname}"
+
+ if test "$opt_dry_run" = ":"; then
+ # Return a directory name, but don't create it in dry-run mode
+ my_tmpdir="${my_template}-$$"
+ else
+
+ # If mktemp works, use that first and foremost
+ my_tmpdir=`mktemp -d "${my_template}-XXXXXXXX" 2>/dev/null`
+
+ if test ! -d "$my_tmpdir"; then
+ # Failing that, at least try and use $RANDOM to avoid a race
+ my_tmpdir="${my_template}-${RANDOM-0}$$"
+
+ save_mktempdir_umask=`umask`
+ umask 0077
+ $MKDIR "$my_tmpdir"
+ umask $save_mktempdir_umask
+ fi
+
+ # If we're not in dry-run mode, bomb out on failure
+ test -d "$my_tmpdir" || \
+ func_fatal_error "cannot create temporary directory \`$my_tmpdir'"
+ fi
+
+ $ECHO "X$my_tmpdir" | $Xsed
+}
+
+
+# func_quote_for_eval arg
+# Aesthetically quote ARG to be evaled later.
+# This function returns two values: FUNC_QUOTE_FOR_EVAL_RESULT
+# is double-quoted, suitable for a subsequent eval, whereas
+# FUNC_QUOTE_FOR_EVAL_UNQUOTED_RESULT has merely all characters
+# which are still active within double quotes backslashified.
+func_quote_for_eval ()
+{
+ case $1 in
+ *[\\\`\"\$]*)
+ func_quote_for_eval_unquoted_result=`$ECHO "X$1" | $Xsed -e "$sed_quote_subst"` ;;
+ *)
+ func_quote_for_eval_unquoted_result="$1" ;;
+ esac
+
+ case $func_quote_for_eval_unquoted_result in
+ # Double-quote args containing shell metacharacters to delay
+ # word splitting, command substitution and and variable
+ # expansion for a subsequent eval.
+ # Many Bourne shells cannot handle close brackets correctly
+ # in scan sets, so we specify it separately.
+ *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"")
+ func_quote_for_eval_result="\"$func_quote_for_eval_unquoted_result\""
+ ;;
+ *)
+ func_quote_for_eval_result="$func_quote_for_eval_unquoted_result"
+ esac
+}
+
+
+# func_quote_for_expand arg
+# Aesthetically quote ARG to be evaled later; same as above,
+# but do not quote variable references.
+func_quote_for_expand ()
+{
+ case $1 in
+ *[\\\`\"]*)
+ my_arg=`$ECHO "X$1" | $Xsed \
+ -e "$double_quote_subst" -e "$sed_double_backslash"` ;;
+ *)
+ my_arg="$1" ;;
+ esac
+
+ case $my_arg in
+ # Double-quote args containing shell metacharacters to delay
+ # word splitting and command substitution for a subsequent eval.
+ # Many Bourne shells cannot handle close brackets correctly
+ # in scan sets, so we specify it separately.
+ *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"")
+ my_arg="\"$my_arg\""
+ ;;
+ esac
+
+ func_quote_for_expand_result="$my_arg"
+}
+
+
+# func_show_eval cmd [fail_exp]
+# Unless opt_silent is true, then output CMD. Then, if opt_dryrun is
+# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP
+# is given, then evaluate it.
+func_show_eval ()
+{
+ my_cmd="$1"
+ my_fail_exp="${2-:}"
+
+ ${opt_silent-false} || {
+ func_quote_for_expand "$my_cmd"
+ eval "func_echo $func_quote_for_expand_result"
+ }
+
+ if ${opt_dry_run-false}; then :; else
+ eval "$my_cmd"
+ my_status=$?
+ if test "$my_status" -eq 0; then :; else
+ eval "(exit $my_status); $my_fail_exp"
+ fi
+ fi
+}
+
+
+# func_show_eval_locale cmd [fail_exp]
+# Unless opt_silent is true, then output CMD. Then, if opt_dryrun is
+# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP
+# is given, then evaluate it. Use the saved locale for evaluation.
+func_show_eval_locale ()
+{
+ my_cmd="$1"
+ my_fail_exp="${2-:}"
+
+ ${opt_silent-false} || {
+ func_quote_for_expand "$my_cmd"
+ eval "func_echo $func_quote_for_expand_result"
+ }
+
+ if ${opt_dry_run-false}; then :; else
+ eval "$lt_user_locale
+ $my_cmd"
+ my_status=$?
+ eval "$lt_safe_locale"
+ if test "$my_status" -eq 0; then :; else
+ eval "(exit $my_status); $my_fail_exp"
+ fi
+ fi
+}
+
+
+
+
+
+# func_version
+# Echo version message to standard output and exit.
+func_version ()
+{
+ $SED -n '/^# '$PROGRAM' (GNU /,/# warranty; / {
+ s/^# //
+ s/^# *$//
+ s/\((C)\)[ 0-9,-]*\( [1-9][0-9]*\)/\1\2/
+ p
+ }' < "$progpath"
+ exit $?
+}
+
+# func_usage
+# Echo short help message to standard output and exit.
+func_usage ()
+{
+ $SED -n '/^# Usage:/,/# -h/ {
+ s/^# //
+ s/^# *$//
+ s/\$progname/'$progname'/
+ p
+ }' < "$progpath"
+ $ECHO
+ $ECHO "run \`$progname --help | more' for full usage"
+ exit $?
+}
+
+# func_help
+# Echo long help message to standard output and exit.
+func_help ()
+{
+ $SED -n '/^# Usage:/,/# Report bugs to/ {
+ s/^# //
+ s/^# *$//
+ s*\$progname*'$progname'*
+ s*\$host*'"$host"'*
+ s*\$SHELL*'"$SHELL"'*
+ s*\$LTCC*'"$LTCC"'*
+ s*\$LTCFLAGS*'"$LTCFLAGS"'*
+ s*\$LD*'"$LD"'*
+ s/\$with_gnu_ld/'"$with_gnu_ld"'/
+ s/\$automake_version/'"`(automake --version) 2>/dev/null |$SED 1q`"'/
+ s/\$autoconf_version/'"`(autoconf --version) 2>/dev/null |$SED 1q`"'/
+ p
+ }' < "$progpath"
+ exit $?
+}
+
+# func_missing_arg argname
+# Echo program name prefixed message to standard error and set global
+# exit_cmd.
+func_missing_arg ()
+{
+ func_error "missing argument for $1"
+ exit_cmd=exit
+}
+
+exit_cmd=:
+
+
+
+
+
+# Check that we have a working $ECHO.
+if test "X$1" = X--no-reexec; then
+ # Discard the --no-reexec flag, and continue.
+ shift
+elif test "X$1" = X--fallback-echo; then
+ # Avoid inline document here, it may be left over
+ :
+elif test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t'; then
+ # Yippee, $ECHO works!
+ :
+else
+ # Restart under the correct shell, and then maybe $ECHO will work.
+ exec $SHELL "$progpath" --no-reexec ${1+"$@"}
+fi
+
+if test "X$1" = X--fallback-echo; then
+ # used as fallback echo
+ shift
+ cat <<EOF
+$*
+EOF
+ exit $EXIT_SUCCESS
+fi
+
+magic="%%%MAGIC variable%%%"
+magic_exe="%%%MAGIC EXE variable%%%"
+
+# Global variables.
+# $mode is unset
+nonopt=
+execute_dlfiles=
+preserve_args=
+lo2o="s/\\.lo\$/.${objext}/"
+o2lo="s/\\.${objext}\$/.lo/"
+extracted_archives=
+extracted_serial=0
+
+opt_dry_run=false
+opt_duplicate_deps=false
+opt_silent=false
+opt_debug=:
+
+# If this variable is set in any of the actions, the command in it
+# will be execed at the end. This prevents here-documents from being
+# left over by shells.
+exec_cmd=
+
+# func_fatal_configuration arg...
+# Echo program name prefixed message to standard error, followed by
+# a configuration failure hint, and exit.
+func_fatal_configuration ()
+{
+ func_error ${1+"$@"}
+ func_error "See the $PACKAGE documentation for more information."
+ func_fatal_error "Fatal configuration error."
+}
+
+
+# func_config
+# Display the configuration for all the tags in this script.
+func_config ()
+{
+ re_begincf='^# ### BEGIN LIBTOOL'
+ re_endcf='^# ### END LIBTOOL'
+
+ # Default configuration.
+ $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath"
+
+ # Now print the configurations for the tags.
+ for tagname in $taglist; do
+ $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath"
+ done
+
+ exit $?
+}
+
+# func_features
+# Display the features supported by this script.
+func_features ()
+{
+ $ECHO "host: $host"
+ if test "$build_libtool_libs" = yes; then
+ $ECHO "enable shared libraries"
+ else
+ $ECHO "disable shared libraries"
+ fi
+ if test "$build_old_libs" = yes; then
+ $ECHO "enable static libraries"
+ else
+ $ECHO "disable static libraries"
+ fi
+
+ exit $?
+}
+
+# func_enable_tag tagname
+# Verify that TAGNAME is valid, and either flag an error and exit, or
+# enable the TAGNAME tag. We also add TAGNAME to the global $taglist
+# variable here.
+func_enable_tag ()
+{
+ # Global variable:
+ tagname="$1"
+
+ re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$"
+ re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$"
+ sed_extractcf="/$re_begincf/,/$re_endcf/p"
+
+ # Validate tagname.
+ case $tagname in
+ *[!-_A-Za-z0-9,/]*)
+ func_fatal_error "invalid tag name: $tagname"
+ ;;
+ esac
+
+ # Don't test for the "default" C tag, as we know it's
+ # there but not specially marked.
+ case $tagname in
+ CC) ;;
+ *)
+ if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then
+ taglist="$taglist $tagname"
+
+ # Evaluate the configuration. Be careful to quote the path
+ # and the sed script, to avoid splitting on whitespace, but
+ # also don't use non-portable quotes within backquotes within
+ # quotes we have to do it in 2 steps:
+ extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"`
+ eval "$extractedcf"
+ else
+ func_error "ignoring unknown tag $tagname"
+ fi
+ ;;
+ esac
+}
+
+# Parse options once, thoroughly. This comes as soon as possible in
+# the script to make things like `libtool --version' happen quickly.
+{
+
+ # Shorthand for --mode=foo, only valid as the first argument
+ case $1 in
+ clean|clea|cle|cl)
+ shift; set dummy --mode clean ${1+"$@"}; shift
+ ;;
+ compile|compil|compi|comp|com|co|c)
+ shift; set dummy --mode compile ${1+"$@"}; shift
+ ;;
+ execute|execut|execu|exec|exe|ex|e)
+ shift; set dummy --mode execute ${1+"$@"}; shift
+ ;;
+ finish|finis|fini|fin|fi|f)
+ shift; set dummy --mode finish ${1+"$@"}; shift
+ ;;
+ install|instal|insta|inst|ins|in|i)
+ shift; set dummy --mode install ${1+"$@"}; shift
+ ;;
+ link|lin|li|l)
+ shift; set dummy --mode link ${1+"$@"}; shift
+ ;;
+ uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u)
+ shift; set dummy --mode uninstall ${1+"$@"}; shift
+ ;;
+ esac
+
+ # Parse non-mode specific arguments:
+ while test "$#" -gt 0; do
+ opt="$1"
+ shift
+
+ case $opt in
+ --config) func_config ;;
+
+ --debug) preserve_args="$preserve_args $opt"
+ func_echo "enabling shell trace mode"
+ opt_debug='set -x'
+ $opt_debug
+ ;;
+
+ -dlopen) test "$#" -eq 0 && func_missing_arg "$opt" && break
+ execute_dlfiles="$execute_dlfiles $1"
+ shift
+ ;;
+
+ --dry-run | -n) opt_dry_run=: ;;
+ --features) func_features ;;
+ --finish) mode="finish" ;;
+
+ --mode) test "$#" -eq 0 && func_missing_arg "$opt" && break
+ case $1 in
+ # Valid mode arguments:
+ clean) ;;
+ compile) ;;
+ execute) ;;
+ finish) ;;
+ install) ;;
+ link) ;;
+ relink) ;;
+ uninstall) ;;
+
+ # Catch anything else as an error
+ *) func_error "invalid argument for $opt"
+ exit_cmd=exit
+ break
+ ;;
+ esac
+
+ mode="$1"
+ shift
+ ;;
+
+ --preserve-dup-deps)
+ opt_duplicate_deps=: ;;
+
+ --quiet|--silent) preserve_args="$preserve_args $opt"
+ opt_silent=:
+ ;;
+
+ --verbose| -v) preserve_args="$preserve_args $opt"
+ opt_silent=false
+ ;;
+
+ --tag) test "$#" -eq 0 && func_missing_arg "$opt" && break
+ preserve_args="$preserve_args $opt $1"
+ func_enable_tag "$1" # tagname is set here
+ shift
+ ;;
+
+ # Separate optargs to long options:
+ -dlopen=*|--mode=*|--tag=*)
+ func_opt_split "$opt"
+ set dummy "$func_opt_split_opt" "$func_opt_split_arg" ${1+"$@"}
+ shift
+ ;;
+
+ -\?|-h) func_usage ;;
+ --help) opt_help=: ;;
+ --version) func_version ;;
+
+ -*) func_fatal_help "unrecognized option \`$opt'" ;;
+
+ *) nonopt="$opt"
+ break
+ ;;
+ esac
+ done
+
+
+ case $host in
+ *cygwin* | *mingw* | *pw32* | *cegcc*)
+ # don't eliminate duplications in $postdeps and $predeps
+ opt_duplicate_compiler_generated_deps=:
+ ;;
+ *)
+ opt_duplicate_compiler_generated_deps=$opt_duplicate_deps
+ ;;
+ esac
+
+ # Having warned about all mis-specified options, bail out if
+ # anything was wrong.
+ $exit_cmd $EXIT_FAILURE
+}
+
+# func_check_version_match
+# Ensure that we are using m4 macros, and libtool script from the same
+# release of libtool.
+func_check_version_match ()
+{
+ if test "$package_revision" != "$macro_revision"; then
+ if test "$VERSION" != "$macro_version"; then
+ if test -z "$macro_version"; then
+ cat >&2 <<_LT_EOF
+$progname: Version mismatch error. This is $PACKAGE $VERSION, but the
+$progname: definition of this LT_INIT comes from an older release.
+$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION
+$progname: and run autoconf again.
+_LT_EOF
+ else
+ cat >&2 <<_LT_EOF
+$progname: Version mismatch error. This is $PACKAGE $VERSION, but the
+$progname: definition of this LT_INIT comes from $PACKAGE $macro_version.
+$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION
+$progname: and run autoconf again.
+_LT_EOF
+ fi
+ else
+ cat >&2 <<_LT_EOF
+$progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision,
+$progname: but the definition of this LT_INIT comes from revision $macro_revision.
+$progname: You should recreate aclocal.m4 with macros from revision $package_revision
+$progname: of $PACKAGE $VERSION and run autoconf again.
+_LT_EOF
+ fi
+
+ exit $EXIT_MISMATCH
+ fi
+}
+
+
+## ----------- ##
+## Main. ##
+## ----------- ##
+
+$opt_help || {
+ # Sanity checks first:
+ func_check_version_match
+
+ if test "$build_libtool_libs" != yes && test "$build_old_libs" != yes; then
+ func_fatal_configuration "not configured to build any kind of library"
+ fi
+
+ test -z "$mode" && func_fatal_error "error: you must specify a MODE."
+
+
+ # Darwin sucks
+ eval std_shrext=\"$shrext_cmds\"
+
+
+ # Only execute mode is allowed to have -dlopen flags.
+ if test -n "$execute_dlfiles" && test "$mode" != execute; then
+ func_error "unrecognized option \`-dlopen'"
+ $ECHO "$help" 1>&2
+ exit $EXIT_FAILURE
+ fi
+
+ # Change the help message to a mode-specific one.
+ generic_help="$help"
+ help="Try \`$progname --help --mode=$mode' for more information."
+}
+
+
+# func_lalib_p file
+# True iff FILE is a libtool `.la' library or `.lo' object file.
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_lalib_p ()
+{
+ test -f "$1" &&
+ $SED -e 4q "$1" 2>/dev/null \
+ | $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1
+}
+
+# func_lalib_unsafe_p file
+# True iff FILE is a libtool `.la' library or `.lo' object file.
+# This function implements the same check as func_lalib_p without
+# resorting to external programs. To this end, it redirects stdin and
+# closes it afterwards, without saving the original file descriptor.
+# As a safety measure, use it only where a negative result would be
+# fatal anyway. Works if `file' does not exist.
+func_lalib_unsafe_p ()
+{
+ lalib_p=no
+ if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then
+ for lalib_p_l in 1 2 3 4
+ do
+ read lalib_p_line
+ case "$lalib_p_line" in
+ \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;;
+ esac
+ done
+ exec 0<&5 5<&-
+ fi
+ test "$lalib_p" = yes
+}
+
+# func_ltwrapper_script_p file
+# True iff FILE is a libtool wrapper script
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_ltwrapper_script_p ()
+{
+ func_lalib_p "$1"
+}
+
+# func_ltwrapper_executable_p file
+# True iff FILE is a libtool wrapper executable
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_ltwrapper_executable_p ()
+{
+ func_ltwrapper_exec_suffix=
+ case $1 in
+ *.exe) ;;
+ *) func_ltwrapper_exec_suffix=.exe ;;
+ esac
+ $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1
+}
+
+# func_ltwrapper_scriptname file
+# Assumes file is an ltwrapper_executable
+# uses $file to determine the appropriate filename for a
+# temporary ltwrapper_script.
+func_ltwrapper_scriptname ()
+{
+ func_ltwrapper_scriptname_result=""
+ if func_ltwrapper_executable_p "$1"; then
+ func_dirname_and_basename "$1" "" "."
+ func_stripname '' '.exe' "$func_basename_result"
+ func_ltwrapper_scriptname_result="$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper"
+ fi
+}
+
+# func_ltwrapper_p file
+# True iff FILE is a libtool wrapper script or wrapper executable
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_ltwrapper_p ()
+{
+ func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1"
+}
+
+
+# func_execute_cmds commands fail_cmd
+# Execute tilde-delimited COMMANDS.
+# If FAIL_CMD is given, eval that upon failure.
+# FAIL_CMD may read-access the current command in variable CMD!
+func_execute_cmds ()
+{
+ $opt_debug
+ save_ifs=$IFS; IFS='~'
+ for cmd in $1; do
+ IFS=$save_ifs
+ eval cmd=\"$cmd\"
+ func_show_eval "$cmd" "${2-:}"
+ done
+ IFS=$save_ifs
+}
+
+
+# func_source file
+# Source FILE, adding directory component if necessary.
+# Note that it is not necessary on cygwin/mingw to append a dot to
+# FILE even if both FILE and FILE.exe exist: automatic-append-.exe
+# behavior happens only for exec(3), not for open(2)! Also, sourcing
+# `FILE.' does not work on cygwin managed mounts.
+func_source ()
+{
+ $opt_debug
+ case $1 in
+ */* | *\\*) . "$1" ;;
+ *) . "./$1" ;;
+ esac
+}
+
+
+# func_infer_tag arg
+# Infer tagged configuration to use if any are available and
+# if one wasn't chosen via the "--tag" command line option.
+# Only attempt this if the compiler in the base compile
+# command doesn't match the default compiler.
+# arg is usually of the form 'gcc ...'
+func_infer_tag ()
+{
+ $opt_debug
+ if test -n "$available_tags" && test -z "$tagname"; then
+ CC_quoted=
+ for arg in $CC; do
+ func_quote_for_eval "$arg"
+ CC_quoted="$CC_quoted $func_quote_for_eval_result"
+ done
+ case $@ in
+ # Blanks in the command may have been stripped by the calling shell,
+ # but not from the CC environment variable when configure was run.
+ " $CC "* | "$CC "* | " `$ECHO $CC` "* | "`$ECHO $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$ECHO $CC_quoted` "* | "`$ECHO $CC_quoted` "*) ;;
+ # Blanks at the start of $base_compile will cause this to fail
+ # if we don't check for them as well.
+ *)
+ for z in $available_tags; do
+ if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then
+ # Evaluate the configuration.
+ eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`"
+ CC_quoted=
+ for arg in $CC; do
+ # Double-quote args containing other shell metacharacters.
+ func_quote_for_eval "$arg"
+ CC_quoted="$CC_quoted $func_quote_for_eval_result"
+ done
+ case "$@ " in
+ " $CC "* | "$CC "* | " `$ECHO $CC` "* | "`$ECHO $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$ECHO $CC_quoted` "* | "`$ECHO $CC_quoted` "*)
+ # The compiler in the base compile command matches
+ # the one in the tagged configuration.
+ # Assume this is the tagged configuration we want.
+ tagname=$z
+ break
+ ;;
+ esac
+ fi
+ done
+ # If $tagname still isn't set, then no tagged configuration
+ # was found and let the user know that the "--tag" command
+ # line option must be used.
+ if test -z "$tagname"; then
+ func_echo "unable to infer tagged configuration"
+ func_fatal_error "specify a tag with \`--tag'"
+# else
+# func_verbose "using $tagname tagged configuration"
+ fi
+ ;;
+ esac
+ fi
+}
+
+
+
+# func_write_libtool_object output_name pic_name nonpic_name
+# Create a libtool object file (analogous to a ".la" file),
+# but don't create it if we're doing a dry run.
+func_write_libtool_object ()
+{
+ write_libobj=${1}
+ if test "$build_libtool_libs" = yes; then
+ write_lobj=\'${2}\'
+ else
+ write_lobj=none
+ fi
+
+ if test "$build_old_libs" = yes; then
+ write_oldobj=\'${3}\'
+ else
+ write_oldobj=none
+ fi
+
+ $opt_dry_run || {
+ cat >${write_libobj}T <<EOF
+# $write_libobj - a libtool object file
+# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# Name of the PIC object.
+pic_object=$write_lobj
+
+# Name of the non-PIC object
+non_pic_object=$write_oldobj
+
+EOF
+ $MV "${write_libobj}T" "${write_libobj}"
+ }
+}
+
+# func_mode_compile arg...
+func_mode_compile ()
+{
+ $opt_debug
+ # Get the compilation command and the source file.
+ base_compile=
+ srcfile="$nonopt" # always keep a non-empty value in "srcfile"
+ suppress_opt=yes
+ suppress_output=
+ arg_mode=normal
+ libobj=
+ later=
+ pie_flag=
+
+ for arg
+ do
+ case $arg_mode in
+ arg )
+ # do not "continue". Instead, add this to base_compile
+ lastarg="$arg"
+ arg_mode=normal
+ ;;
+
+ target )
+ libobj="$arg"
+ arg_mode=normal
+ continue
+ ;;
+
+ normal )
+ # Accept any command-line options.
+ case $arg in
+ -o)
+ test -n "$libobj" && \
+ func_fatal_error "you cannot specify \`-o' more than once"
+ arg_mode=target
+ continue
+ ;;
+
+ -pie | -fpie | -fPIE)
+ pie_flag="$pie_flag $arg"
+ continue
+ ;;
+
+ -shared | -static | -prefer-pic | -prefer-non-pic)
+ later="$later $arg"
+ continue
+ ;;
+
+ -no-suppress)
+ suppress_opt=no
+ continue
+ ;;
+
+ -Xcompiler)
+ arg_mode=arg # the next one goes into the "base_compile" arg list
+ continue # The current "srcfile" will either be retained or
+ ;; # replaced later. I would guess that would be a bug.
+
+ -Wc,*)
+ func_stripname '-Wc,' '' "$arg"
+ args=$func_stripname_result
+ lastarg=
+ save_ifs="$IFS"; IFS=','
+ for arg in $args; do
+ IFS="$save_ifs"
+ func_quote_for_eval "$arg"
+ lastarg="$lastarg $func_quote_for_eval_result"
+ done
+ IFS="$save_ifs"
+ func_stripname ' ' '' "$lastarg"
+ lastarg=$func_stripname_result
+
+ # Add the arguments to base_compile.
+ base_compile="$base_compile $lastarg"
+ continue
+ ;;
+
+ *)
+ # Accept the current argument as the source file.
+ # The previous "srcfile" becomes the current argument.
+ #
+ lastarg="$srcfile"
+ srcfile="$arg"
+ ;;
+ esac # case $arg
+ ;;
+ esac # case $arg_mode
+
+ # Aesthetically quote the previous argument.
+ func_quote_for_eval "$lastarg"
+ base_compile="$base_compile $func_quote_for_eval_result"
+ done # for arg
+
+ case $arg_mode in
+ arg)
+ func_fatal_error "you must specify an argument for -Xcompile"
+ ;;
+ target)
+ func_fatal_error "you must specify a target with \`-o'"
+ ;;
+ *)
+ # Get the name of the library object.
+ test -z "$libobj" && {
+ func_basename "$srcfile"
+ libobj="$func_basename_result"
+ }
+ ;;
+ esac
+
+ # Recognize several different file suffixes.
+ # If the user specifies -o file.o, it is replaced with file.lo
+ case $libobj in
+ *.[cCFSifmso] | \
+ *.ada | *.adb | *.ads | *.asm | \
+ *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \
+ *.[fF][09]? | *.for | *.java | *.obj | *.sx)
+ func_xform "$libobj"
+ libobj=$func_xform_result
+ ;;
+ esac
+
+ case $libobj in
+ *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;;
+ *)
+ func_fatal_error "cannot determine name of library object from \`$libobj'"
+ ;;
+ esac
+
+ func_infer_tag $base_compile
+
+ for arg in $later; do
+ case $arg in
+ -shared)
+ test "$build_libtool_libs" != yes && \
+ func_fatal_configuration "can not build a shared library"
+ build_old_libs=no
+ continue
+ ;;
+
+ -static)
+ build_libtool_libs=no
+ build_old_libs=yes
+ continue
+ ;;
+
+ -prefer-pic)
+ pic_mode=yes
+ continue
+ ;;
+
+ -prefer-non-pic)
+ pic_mode=no
+ continue
+ ;;
+ esac
+ done
+
+ func_quote_for_eval "$libobj"
+ test "X$libobj" != "X$func_quote_for_eval_result" \
+ && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \
+ && func_warning "libobj name \`$libobj' may not contain shell special characters."
+ func_dirname_and_basename "$obj" "/" ""
+ objname="$func_basename_result"
+ xdir="$func_dirname_result"
+ lobj=${xdir}$objdir/$objname
+
+ test -z "$base_compile" && \
+ func_fatal_help "you must specify a compilation command"
+
+ # Delete any leftover library objects.
+ if test "$build_old_libs" = yes; then
+ removelist="$obj $lobj $libobj ${libobj}T"
+ else
+ removelist="$lobj $libobj ${libobj}T"
+ fi
+
+ # On Cygwin there's no "real" PIC flag so we must build both object types
+ case $host_os in
+ cygwin* | mingw* | pw32* | os2* | cegcc*)
+ pic_mode=default
+ ;;
+ esac
+ if test "$pic_mode" = no && test "$deplibs_check_method" != pass_all; then
+ # non-PIC code in shared libraries is not supported
+ pic_mode=default
+ fi
+
+ # Calculate the filename of the output object if compiler does
+ # not support -o with -c
+ if test "$compiler_c_o" = no; then
+ output_obj=`$ECHO "X$srcfile" | $Xsed -e 's%^.*/%%' -e 's%\.[^.]*$%%'`.${objext}
+ lockfile="$output_obj.lock"
+ else
+ output_obj=
+ need_locks=no
+ lockfile=
+ fi
+
+ # Lock this critical section if it is needed
+ # We use this script file to make the link, it avoids creating a new file
+ if test "$need_locks" = yes; then
+ until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do
+ func_echo "Waiting for $lockfile to be removed"
+ sleep 2
+ done
+ elif test "$need_locks" = warn; then
+ if test -f "$lockfile"; then
+ $ECHO "\
+*** ERROR, $lockfile exists and contains:
+`cat $lockfile 2>/dev/null`
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support \`-c' and \`-o' together. If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+ $opt_dry_run || $RM $removelist
+ exit $EXIT_FAILURE
+ fi
+ removelist="$removelist $output_obj"
+ $ECHO "$srcfile" > "$lockfile"
+ fi
+
+ $opt_dry_run || $RM $removelist
+ removelist="$removelist $lockfile"
+ trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15
+
+ if test -n "$fix_srcfile_path"; then
+ eval srcfile=\"$fix_srcfile_path\"
+ fi
+ func_quote_for_eval "$srcfile"
+ qsrcfile=$func_quote_for_eval_result
+
+ # Only build a PIC object if we are building libtool libraries.
+ if test "$build_libtool_libs" = yes; then
+ # Without this assignment, base_compile gets emptied.
+ fbsd_hideous_sh_bug=$base_compile
+
+ if test "$pic_mode" != no; then
+ command="$base_compile $qsrcfile $pic_flag"
+ else
+ # Don't build PIC code
+ command="$base_compile $qsrcfile"
+ fi
+
+ func_mkdir_p "$xdir$objdir"
+
+ if test -z "$output_obj"; then
+ # Place PIC objects in $objdir
+ command="$command -o $lobj"
+ fi
+
+ func_show_eval_locale "$command" \
+ 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE'
+
+ if test "$need_locks" = warn &&
+ test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then
+ $ECHO "\
+*** ERROR, $lockfile contains:
+`cat $lockfile 2>/dev/null`
+
+but it should contain:
+$srcfile
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support \`-c' and \`-o' together. If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+ $opt_dry_run || $RM $removelist
+ exit $EXIT_FAILURE
+ fi
+
+ # Just move the object if needed, then go on to compile the next one
+ if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then
+ func_show_eval '$MV "$output_obj" "$lobj"' \
+ 'error=$?; $opt_dry_run || $RM $removelist; exit $error'
+ fi
+
+ # Allow error messages only from the first compilation.
+ if test "$suppress_opt" = yes; then
+ suppress_output=' >/dev/null 2>&1'
+ fi
+ fi
+
+ # Only build a position-dependent object if we build old libraries.
+ if test "$build_old_libs" = yes; then
+ if test "$pic_mode" != yes; then
+ # Don't build PIC code
+ command="$base_compile $qsrcfile$pie_flag"
+ else
+ command="$base_compile $qsrcfile $pic_flag"
+ fi
+ if test "$compiler_c_o" = yes; then
+ command="$command -o $obj"
+ fi
+
+ # Suppress compiler output if we already did a PIC compilation.
+ command="$command$suppress_output"
+ func_show_eval_locale "$command" \
+ '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE'
+
+ if test "$need_locks" = warn &&
+ test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then
+ $ECHO "\
+*** ERROR, $lockfile contains:
+`cat $lockfile 2>/dev/null`
+
+but it should contain:
+$srcfile
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support \`-c' and \`-o' together. If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+ $opt_dry_run || $RM $removelist
+ exit $EXIT_FAILURE
+ fi
+
+ # Just move the object if needed
+ if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then
+ func_show_eval '$MV "$output_obj" "$obj"' \
+ 'error=$?; $opt_dry_run || $RM $removelist; exit $error'
+ fi
+ fi
+
+ $opt_dry_run || {
+ func_write_libtool_object "$libobj" "$objdir/$objname" "$objname"
+
+ # Unlock the critical section if it was locked
+ if test "$need_locks" != no; then
+ removelist=$lockfile
+ $RM "$lockfile"
+ fi
+ }
+
+ exit $EXIT_SUCCESS
+}
+
+$opt_help || {
+test "$mode" = compile && func_mode_compile ${1+"$@"}
+}
+
+func_mode_help ()
+{
+ # We need to display help for each of the modes.
+ case $mode in
+ "")
+ # Generic help is extracted from the usage comments
+ # at the start of this file.
+ func_help
+ ;;
+
+ clean)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE...
+
+Remove files from the build directory.
+
+RM is the name of the program to use to delete files associated with each FILE
+(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed
+to RM.
+
+If FILE is a libtool library, object or program, all the files associated
+with it are deleted. Otherwise, only FILE itself is deleted using RM."
+ ;;
+
+ compile)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE
+
+Compile a source file into a libtool library object.
+
+This mode accepts the following additional options:
+
+ -o OUTPUT-FILE set the output file name to OUTPUT-FILE
+ -no-suppress do not suppress compiler output for multiple passes
+ -prefer-pic try to building PIC objects only
+ -prefer-non-pic try to building non-PIC objects only
+ -shared do not build a \`.o' file suitable for static linking
+ -static only build a \`.o' file suitable for static linking
+
+COMPILE-COMMAND is a command to be used in creating a \`standard' object file
+from the given SOURCEFILE.
+
+The output file name is determined by removing the directory component from
+SOURCEFILE, then substituting the C source code suffix \`.c' with the
+library object suffix, \`.lo'."
+ ;;
+
+ execute)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]...
+
+Automatically set library path, then run a program.
+
+This mode accepts the following additional options:
+
+ -dlopen FILE add the directory containing FILE to the library path
+
+This mode sets the library path environment variable according to \`-dlopen'
+flags.
+
+If any of the ARGS are libtool executable wrappers, then they are translated
+into their corresponding uninstalled binary, and any of their required library
+directories are added to the library path.
+
+Then, COMMAND is executed, with ARGS as arguments."
+ ;;
+
+ finish)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=finish [LIBDIR]...
+
+Complete the installation of libtool libraries.
+
+Each LIBDIR is a directory that contains libtool libraries.
+
+The commands that this mode executes may require superuser privileges. Use
+the \`--dry-run' option if you just want to see what would be executed."
+ ;;
+
+ install)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND...
+
+Install executables or libraries.
+
+INSTALL-COMMAND is the installation command. The first component should be
+either the \`install' or \`cp' program.
+
+The following components of INSTALL-COMMAND are treated specially:
+
+ -inst-prefix PREFIX-DIR Use PREFIX-DIR as a staging area for installation
+
+The rest of the components are interpreted as arguments to that command (only
+BSD-compatible install options are recognized)."
+ ;;
+
+ link)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=link LINK-COMMAND...
+
+Link object files or libraries together to form another library, or to
+create an executable program.
+
+LINK-COMMAND is a command using the C compiler that you would use to create
+a program from several object files.
+
+The following components of LINK-COMMAND are treated specially:
+
+ -all-static do not do any dynamic linking at all
+ -avoid-version do not add a version suffix if possible
+ -dlopen FILE \`-dlpreopen' FILE if it cannot be dlopened at runtime
+ -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols
+ -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3)
+ -export-symbols SYMFILE
+ try to export only the symbols listed in SYMFILE
+ -export-symbols-regex REGEX
+ try to export only the symbols matching REGEX
+ -LLIBDIR search LIBDIR for required installed libraries
+ -lNAME OUTPUT-FILE requires the installed library libNAME
+ -module build a library that can dlopened
+ -no-fast-install disable the fast-install mode
+ -no-install link a not-installable executable
+ -no-undefined declare that a library does not refer to external symbols
+ -o OUTPUT-FILE create OUTPUT-FILE from the specified objects
+ -objectlist FILE Use a list of object files found in FILE to specify objects
+ -precious-files-regex REGEX
+ don't remove output files matching REGEX
+ -release RELEASE specify package release information
+ -rpath LIBDIR the created library will eventually be installed in LIBDIR
+ -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries
+ -shared only do dynamic linking of libtool libraries
+ -shrext SUFFIX override the standard shared library file extension
+ -static do not do any dynamic linking of uninstalled libtool libraries
+ -static-libtool-libs
+ do not do any dynamic linking of libtool libraries
+ -version-info CURRENT[:REVISION[:AGE]]
+ specify library version info [each variable defaults to 0]
+ -weak LIBNAME declare that the target provides the LIBNAME interface
+
+All other options (arguments beginning with \`-') are ignored.
+
+Every other argument is treated as a filename. Files ending in \`.la' are
+treated as uninstalled libtool libraries, other files are standard or library
+object files.
+
+If the OUTPUT-FILE ends in \`.la', then a libtool library is created,
+only library objects (\`.lo' files) may be specified, and \`-rpath' is
+required, except when creating a convenience library.
+
+If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created
+using \`ar' and \`ranlib', or on Windows using \`lib'.
+
+If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file
+is created, otherwise an executable program is created."
+ ;;
+
+ uninstall)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE...
+
+Remove libraries from an installation directory.
+
+RM is the name of the program to use to delete files associated with each FILE
+(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed
+to RM.
+
+If FILE is a libtool library, all the files associated with it are deleted.
+Otherwise, only FILE itself is deleted using RM."
+ ;;
+
+ *)
+ func_fatal_help "invalid operation mode \`$mode'"
+ ;;
+ esac
+
+ $ECHO
+ $ECHO "Try \`$progname --help' for more information about other modes."
+
+ exit $?
+}
+
+ # Now that we've collected a possible --mode arg, show help if necessary
+ $opt_help && func_mode_help
+
+
+# func_mode_execute arg...
+func_mode_execute ()
+{
+ $opt_debug
+ # The first argument is the command name.
+ cmd="$nonopt"
+ test -z "$cmd" && \
+ func_fatal_help "you must specify a COMMAND"
+
+ # Handle -dlopen flags immediately.
+ for file in $execute_dlfiles; do
+ test -f "$file" \
+ || func_fatal_help "\`$file' is not a file"
+
+ dir=
+ case $file in
+ *.la)
+ # Check to see that this really is a libtool archive.
+ func_lalib_unsafe_p "$file" \
+ || func_fatal_help "\`$lib' is not a valid libtool archive"
+
+ # Read the libtool library.
+ dlname=
+ library_names=
+ func_source "$file"
+
+ # Skip this library if it cannot be dlopened.
+ if test -z "$dlname"; then
+ # Warn if it was a shared library.
+ test -n "$library_names" && \
+ func_warning "\`$file' was not linked with \`-export-dynamic'"
+ continue
+ fi
+
+ func_dirname "$file" "" "."
+ dir="$func_dirname_result"
+
+ if test -f "$dir/$objdir/$dlname"; then
+ dir="$dir/$objdir"
+ else
+ if test ! -f "$dir/$dlname"; then
+ func_fatal_error "cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'"
+ fi
+ fi
+ ;;
+
+ *.lo)
+ # Just add the directory containing the .lo file.
+ func_dirname "$file" "" "."
+ dir="$func_dirname_result"
+ ;;
+
+ *)
+ func_warning "\`-dlopen' is ignored for non-libtool libraries and objects"
+ continue
+ ;;
+ esac
+
+ # Get the absolute pathname.
+ absdir=`cd "$dir" && pwd`
+ test -n "$absdir" && dir="$absdir"
+
+ # Now add the directory to shlibpath_var.
+ if eval "test -z \"\$$shlibpath_var\""; then
+ eval "$shlibpath_var=\"\$dir\""
+ else
+ eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\""
+ fi
+ done
+
+ # This variable tells wrapper scripts just to set shlibpath_var
+ # rather than running their programs.
+ libtool_execute_magic="$magic"
+
+ # Check if any of the arguments is a wrapper script.
+ args=
+ for file
+ do
+ case $file in
+ -*) ;;
+ *)
+ # Do a test to see if this is really a libtool program.
+ if func_ltwrapper_script_p "$file"; then
+ func_source "$file"
+ # Transform arg to wrapped name.
+ file="$progdir/$program"
+ elif func_ltwrapper_executable_p "$file"; then
+ func_ltwrapper_scriptname "$file"
+ func_source "$func_ltwrapper_scriptname_result"
+ # Transform arg to wrapped name.
+ file="$progdir/$program"
+ fi
+ ;;
+ esac
+ # Quote arguments (to preserve shell metacharacters).
+ func_quote_for_eval "$file"
+ args="$args $func_quote_for_eval_result"
+ done
+
+ if test "X$opt_dry_run" = Xfalse; then
+ if test -n "$shlibpath_var"; then
+ # Export the shlibpath_var.
+ eval "export $shlibpath_var"
+ fi
+
+ # Restore saved environment variables
+ for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES
+ do
+ eval "if test \"\${save_$lt_var+set}\" = set; then
+ $lt_var=\$save_$lt_var; export $lt_var
+ else
+ $lt_unset $lt_var
+ fi"
+ done
+
+ # Now prepare to actually exec the command.
+ exec_cmd="\$cmd$args"
+ else
+ # Display what would be done.
+ if test -n "$shlibpath_var"; then
+ eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\""
+ $ECHO "export $shlibpath_var"
+ fi
+ $ECHO "$cmd$args"
+ exit $EXIT_SUCCESS
+ fi
+}
+
+test "$mode" = execute && func_mode_execute ${1+"$@"}
+
+
+# func_mode_finish arg...
+func_mode_finish ()
+{
+ $opt_debug
+ libdirs="$nonopt"
+ admincmds=
+
+ if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then
+ for dir
+ do
+ libdirs="$libdirs $dir"
+ done
+
+ for libdir in $libdirs; do
+ if test -n "$finish_cmds"; then
+ # Do each command in the finish commands.
+ func_execute_cmds "$finish_cmds" 'admincmds="$admincmds
+'"$cmd"'"'
+ fi
+ if test -n "$finish_eval"; then
+ # Do the single finish_eval.
+ eval cmds=\"$finish_eval\"
+ $opt_dry_run || eval "$cmds" || admincmds="$admincmds
+ $cmds"
+ fi
+ done
+ fi
+
+ # Exit here if they wanted silent mode.
+ $opt_silent && exit $EXIT_SUCCESS
+
+ $ECHO "X----------------------------------------------------------------------" | $Xsed
+ $ECHO "Libraries have been installed in:"
+ for libdir in $libdirs; do
+ $ECHO " $libdir"
+ done
+ $ECHO
+ $ECHO "If you ever happen to want to link against installed libraries"
+ $ECHO "in a given directory, LIBDIR, you must either use libtool, and"
+ $ECHO "specify the full pathname of the library, or use the \`-LLIBDIR'"
+ $ECHO "flag during linking and do at least one of the following:"
+ if test -n "$shlibpath_var"; then
+ $ECHO " - add LIBDIR to the \`$shlibpath_var' environment variable"
+ $ECHO " during execution"
+ fi
+ if test -n "$runpath_var"; then
+ $ECHO " - add LIBDIR to the \`$runpath_var' environment variable"
+ $ECHO " during linking"
+ fi
+ if test -n "$hardcode_libdir_flag_spec"; then
+ libdir=LIBDIR
+ eval flag=\"$hardcode_libdir_flag_spec\"
+
+ $ECHO " - use the \`$flag' linker flag"
+ fi
+ if test -n "$admincmds"; then
+ $ECHO " - have your system administrator run these commands:$admincmds"
+ fi
+ if test -f /etc/ld.so.conf; then
+ $ECHO " - have your system administrator add LIBDIR to \`/etc/ld.so.conf'"
+ fi
+ $ECHO
+
+ $ECHO "See any operating system documentation about shared libraries for"
+ case $host in
+ solaris2.[6789]|solaris2.1[0-9])
+ $ECHO "more information, such as the ld(1), crle(1) and ld.so(8) manual"
+ $ECHO "pages."
+ ;;
+ *)
+ $ECHO "more information, such as the ld(1) and ld.so(8) manual pages."
+ ;;
+ esac
+ $ECHO "X----------------------------------------------------------------------" | $Xsed
+ exit $EXIT_SUCCESS
+}
+
+test "$mode" = finish && func_mode_finish ${1+"$@"}
+
+
+# func_mode_install arg...
+func_mode_install ()
+{
+ $opt_debug
+ # There may be an optional sh(1) argument at the beginning of
+ # install_prog (especially on Windows NT).
+ if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh ||
+ # Allow the use of GNU shtool's install command.
+ $ECHO "X$nonopt" | $GREP shtool >/dev/null; then
+ # Aesthetically quote it.
+ func_quote_for_eval "$nonopt"
+ install_prog="$func_quote_for_eval_result "
+ arg=$1
+ shift
+ else
+ install_prog=
+ arg=$nonopt
+ fi
+
+ # The real first argument should be the name of the installation program.
+ # Aesthetically quote it.
+ func_quote_for_eval "$arg"
+ install_prog="$install_prog$func_quote_for_eval_result"
+
+ # We need to accept at least all the BSD install flags.
+ dest=
+ files=
+ opts=
+ prev=
+ install_type=
+ isdir=no
+ stripme=
+ for arg
+ do
+ if test -n "$dest"; then
+ files="$files $dest"
+ dest=$arg
+ continue
+ fi
+
+ case $arg in
+ -d) isdir=yes ;;
+ -f)
+ case " $install_prog " in
+ *[\\\ /]cp\ *) ;;
+ *) prev=$arg ;;
+ esac
+ ;;
+ -g | -m | -o)
+ prev=$arg
+ ;;
+ -s)
+ stripme=" -s"
+ continue
+ ;;
+ -*)
+ ;;
+ *)
+ # If the previous option needed an argument, then skip it.
+ if test -n "$prev"; then
+ prev=
+ else
+ dest=$arg
+ continue
+ fi
+ ;;
+ esac
+
+ # Aesthetically quote the argument.
+ func_quote_for_eval "$arg"
+ install_prog="$install_prog $func_quote_for_eval_result"
+ done
+
+ test -z "$install_prog" && \
+ func_fatal_help "you must specify an install program"
+
+ test -n "$prev" && \
+ func_fatal_help "the \`$prev' option requires an argument"
+
+ if test -z "$files"; then
+ if test -z "$dest"; then
+ func_fatal_help "no file or destination specified"
+ else
+ func_fatal_help "you must specify a destination"
+ fi
+ fi
+
+ # Strip any trailing slash from the destination.
+ func_stripname '' '/' "$dest"
+ dest=$func_stripname_result
+
+ # Check to see that the destination is a directory.
+ test -d "$dest" && isdir=yes
+ if test "$isdir" = yes; then
+ destdir="$dest"
+ destname=
+ else
+ func_dirname_and_basename "$dest" "" "."
+ destdir="$func_dirname_result"
+ destname="$func_basename_result"
+
+ # Not a directory, so check to see that there is only one file specified.
+ set dummy $files; shift
+ test "$#" -gt 1 && \
+ func_fatal_help "\`$dest' is not a directory"
+ fi
+ case $destdir in
+ [\\/]* | [A-Za-z]:[\\/]*) ;;
+ *)
+ for file in $files; do
+ case $file in
+ *.lo) ;;
+ *)
+ func_fatal_help "\`$destdir' must be an absolute directory name"
+ ;;
+ esac
+ done
+ ;;
+ esac
+
+ # This variable tells wrapper scripts just to set variables rather
+ # than running their programs.
+ libtool_install_magic="$magic"
+
+ staticlibs=
+ future_libdirs=
+ current_libdirs=
+ for file in $files; do
+
+ # Do each installation.
+ case $file in
+ *.$libext)
+ # Do the static libraries later.
+ staticlibs="$staticlibs $file"
+ ;;
+
+ *.la)
+ # Check to see that this really is a libtool archive.
+ func_lalib_unsafe_p "$file" \
+ || func_fatal_help "\`$file' is not a valid libtool archive"
+
+ library_names=
+ old_library=
+ relink_command=
+ func_source "$file"
+
+ # Add the libdir to current_libdirs if it is the destination.
+ if test "X$destdir" = "X$libdir"; then
+ case "$current_libdirs " in
+ *" $libdir "*) ;;
+ *) current_libdirs="$current_libdirs $libdir" ;;
+ esac
+ else
+ # Note the libdir as a future libdir.
+ case "$future_libdirs " in
+ *" $libdir "*) ;;
+ *) future_libdirs="$future_libdirs $libdir" ;;
+ esac
+ fi
+
+ func_dirname "$file" "/" ""
+ dir="$func_dirname_result"
+ dir="$dir$objdir"
+
+ if test -n "$relink_command"; then
+ # Determine the prefix the user has applied to our future dir.
+ inst_prefix_dir=`$ECHO "X$destdir" | $Xsed -e "s%$libdir\$%%"`
+
+ # Don't allow the user to place us outside of our expected
+ # location b/c this prevents finding dependent libraries that
+ # are installed to the same prefix.
+ # At present, this check doesn't affect windows .dll's that
+ # are installed into $libdir/../bin (currently, that works fine)
+ # but it's something to keep an eye on.
+ test "$inst_prefix_dir" = "$destdir" && \
+ func_fatal_error "error: cannot install \`$file' to a directory not ending in $libdir"
+
+ if test -n "$inst_prefix_dir"; then
+ # Stick the inst_prefix_dir data into the link command.
+ relink_command=`$ECHO "X$relink_command" | $Xsed -e "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"`
+ else
+ relink_command=`$ECHO "X$relink_command" | $Xsed -e "s%@inst_prefix_dir@%%"`
+ fi
+
+ func_warning "relinking \`$file'"
+ func_show_eval "$relink_command" \
+ 'func_fatal_error "error: relink \`$file'\'' with the above command before installing it"'
+ fi
+
+ # See the names of the shared library.
+ set dummy $library_names; shift
+ if test -n "$1"; then
+ realname="$1"
+ shift
+
+ srcname="$realname"
+ test -n "$relink_command" && srcname="$realname"T
+
+ # Install the shared library and build the symlinks.
+ func_show_eval "$install_prog $dir/$srcname $destdir/$realname" \
+ 'exit $?'
+ tstripme="$stripme"
+ case $host_os in
+ cygwin* | mingw* | pw32* | cegcc*)
+ case $realname in
+ *.dll.a)
+ tstripme=""
+ ;;
+ esac
+ ;;
+ esac
+ if test -n "$tstripme" && test -n "$striplib"; then
+ func_show_eval "$striplib $destdir/$realname" 'exit $?'
+ fi
+
+ if test "$#" -gt 0; then
+ # Delete the old symlinks, and create new ones.
+ # Try `ln -sf' first, because the `ln' binary might depend on
+ # the symlink we replace! Solaris /bin/ln does not understand -f,
+ # so we also need to try rm && ln -s.
+ for linkname
+ do
+ test "$linkname" != "$realname" \
+ && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })"
+ done
+ fi
+
+ # Do each command in the postinstall commands.
+ lib="$destdir/$realname"
+ func_execute_cmds "$postinstall_cmds" 'exit $?'
+ fi
+
+ # Install the pseudo-library for information purposes.
+ func_basename "$file"
+ name="$func_basename_result"
+ instname="$dir/$name"i
+ func_show_eval "$install_prog $instname $destdir/$name" 'exit $?'
+
+ # Maybe install the static library, too.
+ test -n "$old_library" && staticlibs="$staticlibs $dir/$old_library"
+ ;;
+
+ *.lo)
+ # Install (i.e. copy) a libtool object.
+
+ # Figure out destination file name, if it wasn't already specified.
+ if test -n "$destname"; then
+ destfile="$destdir/$destname"
+ else
+ func_basename "$file"
+ destfile="$func_basename_result"
+ destfile="$destdir/$destfile"
+ fi
+
+ # Deduce the name of the destination old-style object file.
+ case $destfile in
+ *.lo)
+ func_lo2o "$destfile"
+ staticdest=$func_lo2o_result
+ ;;
+ *.$objext)
+ staticdest="$destfile"
+ destfile=
+ ;;
+ *)
+ func_fatal_help "cannot copy a libtool object to \`$destfile'"
+ ;;
+ esac
+
+ # Install the libtool object if requested.
+ test -n "$destfile" && \
+ func_show_eval "$install_prog $file $destfile" 'exit $?'
+
+ # Install the old object if enabled.
+ if test "$build_old_libs" = yes; then
+ # Deduce the name of the old-style object file.
+ func_lo2o "$file"
+ staticobj=$func_lo2o_result
+ func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?'
+ fi
+ exit $EXIT_SUCCESS
+ ;;
+
+ *)
+ # Figure out destination file name, if it wasn't already specified.
+ if test -n "$destname"; then
+ destfile="$destdir/$destname"
+ else
+ func_basename "$file"
+ destfile="$func_basename_result"
+ destfile="$destdir/$destfile"
+ fi
+
+ # If the file is missing, and there is a .exe on the end, strip it
+ # because it is most likely a libtool script we actually want to
+ # install
+ stripped_ext=""
+ case $file in
+ *.exe)
+ if test ! -f "$file"; then
+ func_stripname '' '.exe' "$file"
+ file=$func_stripname_result
+ stripped_ext=".exe"
+ fi
+ ;;
+ esac
+
+ # Do a test to see if this is really a libtool program.
+ case $host in
+ *cygwin* | *mingw*)
+ if func_ltwrapper_executable_p "$file"; then
+ func_ltwrapper_scriptname "$file"
+ wrapper=$func_ltwrapper_scriptname_result
+ else
+ func_stripname '' '.exe' "$file"
+ wrapper=$func_stripname_result
+ fi
+ ;;
+ *)
+ wrapper=$file
+ ;;
+ esac
+ if func_ltwrapper_script_p "$wrapper"; then
+ notinst_deplibs=
+ relink_command=
+
+ func_source "$wrapper"
+
+ # Check the variables that should have been set.
+ test -z "$generated_by_libtool_version" && \
+ func_fatal_error "invalid libtool wrapper script \`$wrapper'"
+
+ finalize=yes
+ for lib in $notinst_deplibs; do
+ # Check to see that each library is installed.
+ libdir=
+ if test -f "$lib"; then
+ func_source "$lib"
+ fi
+ libfile="$libdir/"`$ECHO "X$lib" | $Xsed -e 's%^.*/%%g'` ### testsuite: skip nested quoting test
+ if test -n "$libdir" && test ! -f "$libfile"; then
+ func_warning "\`$lib' has not been installed in \`$libdir'"
+ finalize=no
+ fi
+ done
+
+ relink_command=
+ func_source "$wrapper"
+
+ outputname=
+ if test "$fast_install" = no && test -n "$relink_command"; then
+ $opt_dry_run || {
+ if test "$finalize" = yes; then
+ tmpdir=`func_mktempdir`
+ func_basename "$file$stripped_ext"
+ file="$func_basename_result"
+ outputname="$tmpdir/$file"
+ # Replace the output file specification.
+ relink_command=`$ECHO "X$relink_command" | $Xsed -e 's%@OUTPUT@%'"$outputname"'%g'`
+
+ $opt_silent || {
+ func_quote_for_expand "$relink_command"
+ eval "func_echo $func_quote_for_expand_result"
+ }
+ if eval "$relink_command"; then :
+ else
+ func_error "error: relink \`$file' with the above command before installing it"
+ $opt_dry_run || ${RM}r "$tmpdir"
+ continue
+ fi
+ file="$outputname"
+ else
+ func_warning "cannot relink \`$file'"
+ fi
+ }
+ else
+ # Install the binary that we compiled earlier.
+ file=`$ECHO "X$file$stripped_ext" | $Xsed -e "s%\([^/]*\)$%$objdir/\1%"`
+ fi
+ fi
+
+ # remove .exe since cygwin /usr/bin/install will append another
+ # one anyway
+ case $install_prog,$host in
+ */usr/bin/install*,*cygwin*)
+ case $file:$destfile in
+ *.exe:*.exe)
+ # this is ok
+ ;;
+ *.exe:*)
+ destfile=$destfile.exe
+ ;;
+ *:*.exe)
+ func_stripname '' '.exe' "$destfile"
+ destfile=$func_stripname_result
+ ;;
+ esac
+ ;;
+ esac
+ func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?'
+ $opt_dry_run || if test -n "$outputname"; then
+ ${RM}r "$tmpdir"
+ fi
+ ;;
+ esac
+ done
+
+ for file in $staticlibs; do
+ func_basename "$file"
+ name="$func_basename_result"
+
+ # Set up the ranlib parameters.
+ oldlib="$destdir/$name"
+
+ func_show_eval "$install_prog \$file \$oldlib" 'exit $?'
+
+ if test -n "$stripme" && test -n "$old_striplib"; then
+ func_show_eval "$old_striplib $oldlib" 'exit $?'
+ fi
+
+ # Do each command in the postinstall commands.
+ func_execute_cmds "$old_postinstall_cmds" 'exit $?'
+ done
+
+ test -n "$future_libdirs" && \
+ func_warning "remember to run \`$progname --finish$future_libdirs'"
+
+ if test -n "$current_libdirs"; then
+ # Maybe just do a dry run.
+ $opt_dry_run && current_libdirs=" -n$current_libdirs"
+ exec_cmd='$SHELL $progpath $preserve_args --finish$current_libdirs'
+ else
+ exit $EXIT_SUCCESS
+ fi
+}
+
+test "$mode" = install && func_mode_install ${1+"$@"}
+
+
+# func_generate_dlsyms outputname originator pic_p
+# Extract symbols from dlprefiles and create ${outputname}S.o with
+# a dlpreopen symbol table.
+func_generate_dlsyms ()
+{
+ $opt_debug
+ my_outputname="$1"
+ my_originator="$2"
+ my_pic_p="${3-no}"
+ my_prefix=`$ECHO "$my_originator" | sed 's%[^a-zA-Z0-9]%_%g'`
+ my_dlsyms=
+
+ if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
+ if test -n "$NM" && test -n "$global_symbol_pipe"; then
+ my_dlsyms="${my_outputname}S.c"
+ else
+ func_error "not configured to extract global symbols from dlpreopened files"
+ fi
+ fi
+
+ if test -n "$my_dlsyms"; then
+ case $my_dlsyms in
+ "") ;;
+ *.c)
+ # Discover the nlist of each of the dlfiles.
+ nlist="$output_objdir/${my_outputname}.nm"
+
+ func_show_eval "$RM $nlist ${nlist}S ${nlist}T"
+
+ # Parse the name list into a source file.
+ func_verbose "creating $output_objdir/$my_dlsyms"
+
+ $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\
+/* $my_dlsyms - symbol resolution table for \`$my_outputname' dlsym emulation. */
+/* Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION */
+
+#ifdef __cplusplus
+extern \"C\" {
+#endif
+
+/* External symbol declarations for the compiler. */\
+"
+
+ if test "$dlself" = yes; then
+ func_verbose "generating symbol list for \`$output'"
+
+ $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist"
+
+ # Add our own program objects to the symbol list.
+ progfiles=`$ECHO "X$objs$old_deplibs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP`
+ for progfile in $progfiles; do
+ func_verbose "extracting global C symbols from \`$progfile'"
+ $opt_dry_run || eval "$NM $progfile | $global_symbol_pipe >> '$nlist'"
+ done
+
+ if test -n "$exclude_expsyms"; then
+ $opt_dry_run || {
+ eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T'
+ eval '$MV "$nlist"T "$nlist"'
+ }
+ fi
+
+ if test -n "$export_symbols_regex"; then
+ $opt_dry_run || {
+ eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T'
+ eval '$MV "$nlist"T "$nlist"'
+ }
+ fi
+
+ # Prepare the list of exported symbols
+ if test -z "$export_symbols"; then
+ export_symbols="$output_objdir/$outputname.exp"
+ $opt_dry_run || {
+ $RM $export_symbols
+ eval "${SED} -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"'
+ case $host in
+ *cygwin* | *mingw* | *cegcc* )
+ eval "echo EXPORTS "'> "$output_objdir/$outputname.def"'
+ eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"'
+ ;;
+ esac
+ }
+ else
+ $opt_dry_run || {
+ eval "${SED} -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"'
+ eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T'
+ eval '$MV "$nlist"T "$nlist"'
+ case $host in
+ *cygwin | *mingw* | *cegcc* )
+ eval "echo EXPORTS "'> "$output_objdir/$outputname.def"'
+ eval 'cat "$nlist" >> "$output_objdir/$outputname.def"'
+ ;;
+ esac
+ }
+ fi
+ fi
+
+ for dlprefile in $dlprefiles; do
+ func_verbose "extracting global C symbols from \`$dlprefile'"
+ func_basename "$dlprefile"
+ name="$func_basename_result"
+ $opt_dry_run || {
+ eval '$ECHO ": $name " >> "$nlist"'
+ eval "$NM $dlprefile 2>/dev/null | $global_symbol_pipe >> '$nlist'"
+ }
+ done
+
+ $opt_dry_run || {
+ # Make sure we have at least an empty file.
+ test -f "$nlist" || : > "$nlist"
+
+ if test -n "$exclude_expsyms"; then
+ $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T
+ $MV "$nlist"T "$nlist"
+ fi
+
+ # Try sorting and uniquifying the output.
+ if $GREP -v "^: " < "$nlist" |
+ if sort -k 3 </dev/null >/dev/null 2>&1; then
+ sort -k 3
+ else
+ sort +2
+ fi |
+ uniq > "$nlist"S; then
+ :
+ else
+ $GREP -v "^: " < "$nlist" > "$nlist"S
+ fi
+
+ if test -f "$nlist"S; then
+ eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"'
+ else
+ $ECHO '/* NONE */' >> "$output_objdir/$my_dlsyms"
+ fi
+
+ $ECHO >> "$output_objdir/$my_dlsyms" "\
+
+/* The mapping between symbol names and symbols. */
+typedef struct {
+ const char *name;
+ void *address;
+} lt_dlsymlist;
+"
+ case $host in
+ *cygwin* | *mingw* | *cegcc* )
+ $ECHO >> "$output_objdir/$my_dlsyms" "\
+/* DATA imports from DLLs on WIN32 con't be const, because
+ runtime relocations are performed -- see ld's documentation
+ on pseudo-relocs. */"
+ lt_dlsym_const= ;;
+ *osf5*)
+ echo >> "$output_objdir/$my_dlsyms" "\
+/* This system does not cope well with relocations in const data */"
+ lt_dlsym_const= ;;
+ *)
+ lt_dlsym_const=const ;;
+ esac
+
+ $ECHO >> "$output_objdir/$my_dlsyms" "\
+extern $lt_dlsym_const lt_dlsymlist
+lt_${my_prefix}_LTX_preloaded_symbols[];
+$lt_dlsym_const lt_dlsymlist
+lt_${my_prefix}_LTX_preloaded_symbols[] =
+{\
+ { \"$my_originator\", (void *) 0 },"
+
+ case $need_lib_prefix in
+ no)
+ eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms"
+ ;;
+ *)
+ eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms"
+ ;;
+ esac
+ $ECHO >> "$output_objdir/$my_dlsyms" "\
+ {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+ return lt_${my_prefix}_LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif\
+"
+ } # !$opt_dry_run
+
+ pic_flag_for_symtable=
+ case "$compile_command " in
+ *" -static "*) ;;
+ *)
+ case $host in
+ # compiling the symbol table file with pic_flag works around
+ # a FreeBSD bug that causes programs to crash when -lm is
+ # linked before any other PIC object. But we must not use
+ # pic_flag when linking with -static. The problem exists in
+ # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1.
+ *-*-freebsd2*|*-*-freebsd3.0*|*-*-freebsdelf3.0*)
+ pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;;
+ *-*-hpux*)
+ pic_flag_for_symtable=" $pic_flag" ;;
+ *)
+ if test "X$my_pic_p" != Xno; then
+ pic_flag_for_symtable=" $pic_flag"
+ fi
+ ;;
+ esac
+ ;;
+ esac
+ symtab_cflags=
+ for arg in $LTCFLAGS; do
+ case $arg in
+ -pie | -fpie | -fPIE) ;;
+ *) symtab_cflags="$symtab_cflags $arg" ;;
+ esac
+ done
+
+ # Now compile the dynamic symbol file.
+ func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?'
+
+ # Clean up the generated files.
+ func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T"'
+
+ # Transform the symbol file into the correct name.
+ symfileobj="$output_objdir/${my_outputname}S.$objext"
+ case $host in
+ *cygwin* | *mingw* | *cegcc* )
+ if test -f "$output_objdir/$my_outputname.def"; then
+ compile_command=`$ECHO "X$compile_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"`
+ finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"`
+ else
+ compile_command=`$ECHO "X$compile_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"`
+ finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"`
+ fi
+ ;;
+ *)
+ compile_command=`$ECHO "X$compile_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"`
+ finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"`
+ ;;
+ esac
+ ;;
+ *)
+ func_fatal_error "unknown suffix for \`$my_dlsyms'"
+ ;;
+ esac
+ else
+ # We keep going just in case the user didn't refer to
+ # lt_preloaded_symbols. The linker will fail if global_symbol_pipe
+ # really was required.
+
+ # Nullify the symbol file.
+ compile_command=`$ECHO "X$compile_command" | $Xsed -e "s% @SYMFILE@%%"`
+ finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s% @SYMFILE@%%"`
+ fi
+}
+
+# func_win32_libid arg
+# return the library type of file 'arg'
+#
+# Need a lot of goo to handle *both* DLLs and import libs
+# Has to be a shell function in order to 'eat' the argument
+# that is supplied when $file_magic_command is called.
+func_win32_libid ()
+{
+ $opt_debug
+ win32_libid_type="unknown"
+ win32_fileres=`file -L $1 2>/dev/null`
+ case $win32_fileres in
+ *ar\ archive\ import\ library*) # definitely import
+ win32_libid_type="x86 archive import"
+ ;;
+ *ar\ archive*) # could be an import, or static
+ if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null |
+ $EGREP 'file format pe-i386(.*architecture: i386)?' >/dev/null ; then
+ win32_nmres=`eval $NM -f posix -A $1 |
+ $SED -n -e '
+ 1,100{
+ / I /{
+ s,.*,import,
+ p
+ q
+ }
+ }'`
+ case $win32_nmres in
+ import*) win32_libid_type="x86 archive import";;
+ *) win32_libid_type="x86 archive static";;
+ esac
+ fi
+ ;;
+ *DLL*)
+ win32_libid_type="x86 DLL"
+ ;;
+ *executable*) # but shell scripts are "executable" too...
+ case $win32_fileres in
+ *MS\ Windows\ PE\ Intel*)
+ win32_libid_type="x86 DLL"
+ ;;
+ esac
+ ;;
+ esac
+ $ECHO "$win32_libid_type"
+}
+
+
+
+# func_extract_an_archive dir oldlib
+func_extract_an_archive ()
+{
+ $opt_debug
+ f_ex_an_ar_dir="$1"; shift
+ f_ex_an_ar_oldlib="$1"
+ func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" 'exit $?'
+ if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then
+ :
+ else
+ func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib"
+ fi
+}
+
+
+# func_extract_archives gentop oldlib ...
+func_extract_archives ()
+{
+ $opt_debug
+ my_gentop="$1"; shift
+ my_oldlibs=${1+"$@"}
+ my_oldobjs=""
+ my_xlib=""
+ my_xabs=""
+ my_xdir=""
+
+ for my_xlib in $my_oldlibs; do
+ # Extract the objects.
+ case $my_xlib in
+ [\\/]* | [A-Za-z]:[\\/]*) my_xabs="$my_xlib" ;;
+ *) my_xabs=`pwd`"/$my_xlib" ;;
+ esac
+ func_basename "$my_xlib"
+ my_xlib="$func_basename_result"
+ my_xlib_u=$my_xlib
+ while :; do
+ case " $extracted_archives " in
+ *" $my_xlib_u "*)
+ func_arith $extracted_serial + 1
+ extracted_serial=$func_arith_result
+ my_xlib_u=lt$extracted_serial-$my_xlib ;;
+ *) break ;;
+ esac
+ done
+ extracted_archives="$extracted_archives $my_xlib_u"
+ my_xdir="$my_gentop/$my_xlib_u"
+
+ func_mkdir_p "$my_xdir"
+
+ case $host in
+ *-darwin*)
+ func_verbose "Extracting $my_xabs"
+ # Do not bother doing anything if just a dry run
+ $opt_dry_run || {
+ darwin_orig_dir=`pwd`
+ cd $my_xdir || exit $?
+ darwin_archive=$my_xabs
+ darwin_curdir=`pwd`
+ darwin_base_archive=`basename "$darwin_archive"`
+ darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true`
+ if test -n "$darwin_arches"; then
+ darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'`
+ darwin_arch=
+ func_verbose "$darwin_base_archive has multiple architectures $darwin_arches"
+ for darwin_arch in $darwin_arches ; do
+ func_mkdir_p "unfat-$$/${darwin_base_archive}-${darwin_arch}"
+ $LIPO -thin $darwin_arch -output "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" "${darwin_archive}"
+ cd "unfat-$$/${darwin_base_archive}-${darwin_arch}"
+ func_extract_an_archive "`pwd`" "${darwin_base_archive}"
+ cd "$darwin_curdir"
+ $RM "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}"
+ done # $darwin_arches
+ ## Okay now we've a bunch of thin objects, gotta fatten them up :)
+ darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$basename" | sort -u`
+ darwin_file=
+ darwin_files=
+ for darwin_file in $darwin_filelist; do
+ darwin_files=`find unfat-$$ -name $darwin_file -print | $NL2SP`
+ $LIPO -create -output "$darwin_file" $darwin_files
+ done # $darwin_filelist
+ $RM -rf unfat-$$
+ cd "$darwin_orig_dir"
+ else
+ cd $darwin_orig_dir
+ func_extract_an_archive "$my_xdir" "$my_xabs"
+ fi # $darwin_arches
+ } # !$opt_dry_run
+ ;;
+ *)
+ func_extract_an_archive "$my_xdir" "$my_xabs"
+ ;;
+ esac
+ my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | $NL2SP`
+ done
+
+ func_extract_archives_result="$my_oldobjs"
+}
+
+
+
+# func_emit_wrapper_part1 [arg=no]
+#
+# Emit the first part of a libtool wrapper script on stdout.
+# For more information, see the description associated with
+# func_emit_wrapper(), below.
+func_emit_wrapper_part1 ()
+{
+ func_emit_wrapper_part1_arg1=no
+ if test -n "$1" ; then
+ func_emit_wrapper_part1_arg1=$1
+ fi
+
+ $ECHO "\
+#! $SHELL
+
+# $output - temporary wrapper script for $objdir/$outputname
+# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION
+#
+# The $output program cannot be directly executed until all the libtool
+# libraries that it depends on are installed.
+#
+# This wrapper script should never be moved out of the build directory.
+# If it is, it will not operate correctly.
+
+# Sed substitution that helps us do robust quoting. It backslashifies
+# metacharacters that are still active within double-quoted strings.
+Xsed='${SED} -e 1s/^X//'
+sed_quote_subst='$sed_quote_subst'
+
+# Be Bourne compatible
+if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '\${1+\"\$@\"}'='\"\$@\"'
+ setopt NO_GLOB_SUBST
+else
+ case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac
+fi
+BIN_SH=xpg4; export BIN_SH # for Tru64
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+relink_command=\"$relink_command\"
+
+# This environment variable determines our operation mode.
+if test \"\$libtool_install_magic\" = \"$magic\"; then
+ # install mode needs the following variables:
+ generated_by_libtool_version='$macro_version'
+ notinst_deplibs='$notinst_deplibs'
+else
+ # When we are sourced in execute mode, \$file and \$ECHO are already set.
+ if test \"\$libtool_execute_magic\" != \"$magic\"; then
+ ECHO=\"$qecho\"
+ file=\"\$0\"
+ # Make sure echo works.
+ if test \"X\$1\" = X--no-reexec; then
+ # Discard the --no-reexec flag, and continue.
+ shift
+ elif test \"X\`{ \$ECHO '\t'; } 2>/dev/null\`\" = 'X\t'; then
+ # Yippee, \$ECHO works!
+ :
+ else
+ # Restart under the correct shell, and then maybe \$ECHO will work.
+ exec $SHELL \"\$0\" --no-reexec \${1+\"\$@\"}
+ fi
+ fi\
+"
+ $ECHO "\
+
+ # Find the directory that this script lives in.
+ thisdir=\`\$ECHO \"X\$file\" | \$Xsed -e 's%/[^/]*$%%'\`
+ test \"x\$thisdir\" = \"x\$file\" && thisdir=.
+
+ # Follow symbolic links until we get to the real thisdir.
+ file=\`ls -ld \"\$file\" | ${SED} -n 's/.*-> //p'\`
+ while test -n \"\$file\"; do
+ destdir=\`\$ECHO \"X\$file\" | \$Xsed -e 's%/[^/]*\$%%'\`
+
+ # If there was a directory component, then change thisdir.
+ if test \"x\$destdir\" != \"x\$file\"; then
+ case \"\$destdir\" in
+ [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;;
+ *) thisdir=\"\$thisdir/\$destdir\" ;;
+ esac
+ fi
+
+ file=\`\$ECHO \"X\$file\" | \$Xsed -e 's%^.*/%%'\`
+ file=\`ls -ld \"\$thisdir/\$file\" | ${SED} -n 's/.*-> //p'\`
+ done
+"
+}
+# end: func_emit_wrapper_part1
+
+# func_emit_wrapper_part2 [arg=no]
+#
+# Emit the second part of a libtool wrapper script on stdout.
+# For more information, see the description associated with
+# func_emit_wrapper(), below.
+func_emit_wrapper_part2 ()
+{
+ func_emit_wrapper_part2_arg1=no
+ if test -n "$1" ; then
+ func_emit_wrapper_part2_arg1=$1
+ fi
+
+ $ECHO "\
+
+ # Usually 'no', except on cygwin/mingw when embedded into
+ # the cwrapper.
+ WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_part2_arg1
+ if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then
+ # special case for '.'
+ if test \"\$thisdir\" = \".\"; then
+ thisdir=\`pwd\`
+ fi
+ # remove .libs from thisdir
+ case \"\$thisdir\" in
+ *[\\\\/]$objdir ) thisdir=\`\$ECHO \"X\$thisdir\" | \$Xsed -e 's%[\\\\/][^\\\\/]*$%%'\` ;;
+ $objdir ) thisdir=. ;;
+ esac
+ fi
+
+ # Try to get the absolute directory name.
+ absdir=\`cd \"\$thisdir\" && pwd\`
+ test -n \"\$absdir\" && thisdir=\"\$absdir\"
+"
+
+ if test "$fast_install" = yes; then
+ $ECHO "\
+ program=lt-'$outputname'$exeext
+ progdir=\"\$thisdir/$objdir\"
+
+ if test ! -f \"\$progdir/\$program\" ||
+ { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | ${SED} 1q\`; \\
+ test \"X\$file\" != \"X\$progdir/\$program\"; }; then
+
+ file=\"\$\$-\$program\"
+
+ if test ! -d \"\$progdir\"; then
+ $MKDIR \"\$progdir\"
+ else
+ $RM \"\$progdir/\$file\"
+ fi"
+
+ $ECHO "\
+
+ # relink executable if necessary
+ if test -n \"\$relink_command\"; then
+ if relink_command_output=\`eval \$relink_command 2>&1\`; then :
+ else
+ $ECHO \"\$relink_command_output\" >&2
+ $RM \"\$progdir/\$file\"
+ exit 1
+ fi
+ fi
+
+ $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null ||
+ { $RM \"\$progdir/\$program\";
+ $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; }
+ $RM \"\$progdir/\$file\"
+ fi"
+ else
+ $ECHO "\
+ program='$outputname'
+ progdir=\"\$thisdir/$objdir\"
+"
+ fi
+
+ $ECHO "\
+
+ if test -f \"\$progdir/\$program\"; then"
+
+ # Export our shlibpath_var if we have one.
+ if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then
+ $ECHO "\
+ # Add our own library path to $shlibpath_var
+ $shlibpath_var=\"$temp_rpath\$$shlibpath_var\"
+
+ # Some systems cannot cope with colon-terminated $shlibpath_var
+ # The second colon is a workaround for a bug in BeOS R4 sed
+ $shlibpath_var=\`\$ECHO \"X\$$shlibpath_var\" | \$Xsed -e 's/::*\$//'\`
+
+ export $shlibpath_var
+"
+ fi
+
+ # fixup the dll searchpath if we need to.
+ if test -n "$dllsearchpath"; then
+ $ECHO "\
+ # Add the dll search path components to the executable PATH
+ PATH=$dllsearchpath:\$PATH
+"
+ fi
+
+ $ECHO "\
+ if test \"\$libtool_execute_magic\" != \"$magic\"; then
+ # Run the actual program with our arguments.
+"
+ case $host in
+ # Backslashes separate directories on plain windows
+ *-*-mingw | *-*-os2* | *-cegcc*)
+ $ECHO "\
+ exec \"\$progdir\\\\\$program\" \${1+\"\$@\"}
+"
+ ;;
+
+ *)
+ $ECHO "\
+ exec \"\$progdir/\$program\" \${1+\"\$@\"}
+"
+ ;;
+ esac
+ $ECHO "\
+ \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2
+ exit 1
+ fi
+ else
+ # The program doesn't exist.
+ \$ECHO \"\$0: error: \\\`\$progdir/\$program' does not exist\" 1>&2
+ \$ECHO \"This script is just a wrapper for \$program.\" 1>&2
+ $ECHO \"See the $PACKAGE documentation for more information.\" 1>&2
+ exit 1
+ fi
+fi\
+"
+}
+# end: func_emit_wrapper_part2
+
+
+# func_emit_wrapper [arg=no]
+#
+# Emit a libtool wrapper script on stdout.
+# Don't directly open a file because we may want to
+# incorporate the script contents within a cygwin/mingw
+# wrapper executable. Must ONLY be called from within
+# func_mode_link because it depends on a number of variables
+# set therein.
+#
+# ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR
+# variable will take. If 'yes', then the emitted script
+# will assume that the directory in which it is stored is
+# the $objdir directory. This is a cygwin/mingw-specific
+# behavior.
+func_emit_wrapper ()
+{
+ func_emit_wrapper_arg1=no
+ if test -n "$1" ; then
+ func_emit_wrapper_arg1=$1
+ fi
+
+ # split this up so that func_emit_cwrapperexe_src
+ # can call each part independently.
+ func_emit_wrapper_part1 "${func_emit_wrapper_arg1}"
+ func_emit_wrapper_part2 "${func_emit_wrapper_arg1}"
+}
+
+
+# func_to_host_path arg
+#
+# Convert paths to host format when used with build tools.
+# Intended for use with "native" mingw (where libtool itself
+# is running under the msys shell), or in the following cross-
+# build environments:
+# $build $host
+# mingw (msys) mingw [e.g. native]
+# cygwin mingw
+# *nix + wine mingw
+# where wine is equipped with the `winepath' executable.
+# In the native mingw case, the (msys) shell automatically
+# converts paths for any non-msys applications it launches,
+# but that facility isn't available from inside the cwrapper.
+# Similar accommodations are necessary for $host mingw and
+# $build cygwin. Calling this function does no harm for other
+# $host/$build combinations not listed above.
+#
+# ARG is the path (on $build) that should be converted to
+# the proper representation for $host. The result is stored
+# in $func_to_host_path_result.
+func_to_host_path ()
+{
+ func_to_host_path_result="$1"
+ if test -n "$1" ; then
+ case $host in
+ *mingw* )
+ lt_sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g'
+ case $build in
+ *mingw* ) # actually, msys
+ # awkward: cmd appends spaces to result
+ lt_sed_strip_trailing_spaces="s/[ ]*\$//"
+ func_to_host_path_tmp1=`( cmd //c echo "$1" |\
+ $SED -e "$lt_sed_strip_trailing_spaces" ) 2>/dev/null || echo ""`
+ func_to_host_path_result=`echo "$func_to_host_path_tmp1" |\
+ $SED -e "$lt_sed_naive_backslashify"`
+ ;;
+ *cygwin* )
+ func_to_host_path_tmp1=`cygpath -w "$1"`
+ func_to_host_path_result=`echo "$func_to_host_path_tmp1" |\
+ $SED -e "$lt_sed_naive_backslashify"`
+ ;;
+ * )
+ # Unfortunately, winepath does not exit with a non-zero
+ # error code, so we are forced to check the contents of
+ # stdout. On the other hand, if the command is not
+ # found, the shell will set an exit code of 127 and print
+ # *an error message* to stdout. So we must check for both
+ # error code of zero AND non-empty stdout, which explains
+ # the odd construction:
+ func_to_host_path_tmp1=`winepath -w "$1" 2>/dev/null`
+ if test "$?" -eq 0 && test -n "${func_to_host_path_tmp1}"; then
+ func_to_host_path_result=`echo "$func_to_host_path_tmp1" |\
+ $SED -e "$lt_sed_naive_backslashify"`
+ else
+ # Allow warning below.
+ func_to_host_path_result=""
+ fi
+ ;;
+ esac
+ if test -z "$func_to_host_path_result" ; then
+ func_error "Could not determine host path corresponding to"
+ func_error " '$1'"
+ func_error "Continuing, but uninstalled executables may not work."
+ # Fallback:
+ func_to_host_path_result="$1"
+ fi
+ ;;
+ esac
+ fi
+}
+# end: func_to_host_path
+
+# func_to_host_pathlist arg
+#
+# Convert pathlists to host format when used with build tools.
+# See func_to_host_path(), above. This function supports the
+# following $build/$host combinations (but does no harm for
+# combinations not listed here):
+# $build $host
+# mingw (msys) mingw [e.g. native]
+# cygwin mingw
+# *nix + wine mingw
+#
+# Path separators are also converted from $build format to
+# $host format. If ARG begins or ends with a path separator
+# character, it is preserved (but converted to $host format)
+# on output.
+#
+# ARG is a pathlist (on $build) that should be converted to
+# the proper representation on $host. The result is stored
+# in $func_to_host_pathlist_result.
+func_to_host_pathlist ()
+{
+ func_to_host_pathlist_result="$1"
+ if test -n "$1" ; then
+ case $host in
+ *mingw* )
+ lt_sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g'
+ # Remove leading and trailing path separator characters from
+ # ARG. msys behavior is inconsistent here, cygpath turns them
+ # into '.;' and ';.', and winepath ignores them completely.
+ func_to_host_pathlist_tmp2="$1"
+ # Once set for this call, this variable should not be
+ # reassigned. It is used in tha fallback case.
+ func_to_host_pathlist_tmp1=`echo "$func_to_host_pathlist_tmp2" |\
+ $SED -e 's|^:*||' -e 's|:*$||'`
+ case $build in
+ *mingw* ) # Actually, msys.
+ # Awkward: cmd appends spaces to result.
+ lt_sed_strip_trailing_spaces="s/[ ]*\$//"
+ func_to_host_pathlist_tmp2=`( cmd //c echo "$func_to_host_pathlist_tmp1" |\
+ $SED -e "$lt_sed_strip_trailing_spaces" ) 2>/dev/null || echo ""`
+ func_to_host_pathlist_result=`echo "$func_to_host_pathlist_tmp2" |\
+ $SED -e "$lt_sed_naive_backslashify"`
+ ;;
+ *cygwin* )
+ func_to_host_pathlist_tmp2=`cygpath -w -p "$func_to_host_pathlist_tmp1"`
+ func_to_host_pathlist_result=`echo "$func_to_host_pathlist_tmp2" |\
+ $SED -e "$lt_sed_naive_backslashify"`
+ ;;
+ * )
+ # unfortunately, winepath doesn't convert pathlists
+ func_to_host_pathlist_result=""
+ func_to_host_pathlist_oldIFS=$IFS
+ IFS=:
+ for func_to_host_pathlist_f in $func_to_host_pathlist_tmp1 ; do
+ IFS=$func_to_host_pathlist_oldIFS
+ if test -n "$func_to_host_pathlist_f" ; then
+ func_to_host_path "$func_to_host_pathlist_f"
+ if test -n "$func_to_host_path_result" ; then
+ if test -z "$func_to_host_pathlist_result" ; then
+ func_to_host_pathlist_result="$func_to_host_path_result"
+ else
+ func_to_host_pathlist_result="$func_to_host_pathlist_result;$func_to_host_path_result"
+ fi
+ fi
+ fi
+ IFS=:
+ done
+ IFS=$func_to_host_pathlist_oldIFS
+ ;;
+ esac
+ if test -z "$func_to_host_pathlist_result" ; then
+ func_error "Could not determine the host path(s) corresponding to"
+ func_error " '$1'"
+ func_error "Continuing, but uninstalled executables may not work."
+ # Fallback. This may break if $1 contains DOS-style drive
+ # specifications. The fix is not to complicate the expression
+ # below, but for the user to provide a working wine installation
+ # with winepath so that path translation in the cross-to-mingw
+ # case works properly.
+ lt_replace_pathsep_nix_to_dos="s|:|;|g"
+ func_to_host_pathlist_result=`echo "$func_to_host_pathlist_tmp1" |\
+ $SED -e "$lt_replace_pathsep_nix_to_dos"`
+ fi
+ # Now, add the leading and trailing path separators back
+ case "$1" in
+ :* ) func_to_host_pathlist_result=";$func_to_host_pathlist_result"
+ ;;
+ esac
+ case "$1" in
+ *: ) func_to_host_pathlist_result="$func_to_host_pathlist_result;"
+ ;;
+ esac
+ ;;
+ esac
+ fi
+}
+# end: func_to_host_pathlist
+
+# func_emit_cwrapperexe_src
+# emit the source code for a wrapper executable on stdout
+# Must ONLY be called from within func_mode_link because
+# it depends on a number of variable set therein.
+func_emit_cwrapperexe_src ()
+{
+ cat <<EOF
+
+/* $cwrappersource - temporary wrapper executable for $objdir/$outputname
+ Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION
+
+ The $output program cannot be directly executed until all the libtool
+ libraries that it depends on are installed.
+
+ This wrapper executable should never be moved out of the build directory.
+ If it is, it will not operate correctly.
+
+ Currently, it simply execs the wrapper *script* "$SHELL $output",
+ but could eventually absorb all of the scripts functionality and
+ exec $objdir/$outputname directly.
+*/
+EOF
+ cat <<"EOF"
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef _MSC_VER
+# include <direct.h>
+# include <process.h>
+# include <io.h>
+# define setmode _setmode
+#else
+# include <unistd.h>
+# include <stdint.h>
+# ifdef __CYGWIN__
+# include <io.h>
+# define HAVE_SETENV
+# ifdef __STRICT_ANSI__
+char *realpath (const char *, char *);
+int putenv (char *);
+int setenv (const char *, const char *, int);
+# endif
+# endif
+#endif
+#include <malloc.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#if defined(PATH_MAX)
+# define LT_PATHMAX PATH_MAX
+#elif defined(MAXPATHLEN)
+# define LT_PATHMAX MAXPATHLEN
+#else
+# define LT_PATHMAX 1024
+#endif
+
+#ifndef S_IXOTH
+# define S_IXOTH 0
+#endif
+#ifndef S_IXGRP
+# define S_IXGRP 0
+#endif
+
+#ifdef _MSC_VER
+# define S_IXUSR _S_IEXEC
+# define stat _stat
+# ifndef _INTPTR_T_DEFINED
+# define intptr_t int
+# endif
+#endif
+
+#ifndef DIR_SEPARATOR
+# define DIR_SEPARATOR '/'
+# define PATH_SEPARATOR ':'
+#endif
+
+#if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \
+ defined (__OS2__)
+# define HAVE_DOS_BASED_FILE_SYSTEM
+# define FOPEN_WB "wb"
+# ifndef DIR_SEPARATOR_2
+# define DIR_SEPARATOR_2 '\\'
+# endif
+# ifndef PATH_SEPARATOR_2
+# define PATH_SEPARATOR_2 ';'
+# endif
+#endif
+
+#ifndef DIR_SEPARATOR_2
+# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR)
+#else /* DIR_SEPARATOR_2 */
+# define IS_DIR_SEPARATOR(ch) \
+ (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2))
+#endif /* DIR_SEPARATOR_2 */
+
+#ifndef PATH_SEPARATOR_2
+# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR)
+#else /* PATH_SEPARATOR_2 */
+# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2)
+#endif /* PATH_SEPARATOR_2 */
+
+#ifdef __CYGWIN__
+# define FOPEN_WB "wb"
+#endif
+
+#ifndef FOPEN_WB
+# define FOPEN_WB "w"
+#endif
+#ifndef _O_BINARY
+# define _O_BINARY 0
+#endif
+
+#define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type)))
+#define XFREE(stale) do { \
+ if (stale) { free ((void *) stale); stale = 0; } \
+} while (0)
+
+#undef LTWRAPPER_DEBUGPRINTF
+#if defined DEBUGWRAPPER
+# define LTWRAPPER_DEBUGPRINTF(args) ltwrapper_debugprintf args
+static void
+ltwrapper_debugprintf (const char *fmt, ...)
+{
+ va_list args;
+ va_start (args, fmt);
+ (void) vfprintf (stderr, fmt, args);
+ va_end (args);
+}
+#else
+# define LTWRAPPER_DEBUGPRINTF(args)
+#endif
+
+const char *program_name = NULL;
+
+void *xmalloc (size_t num);
+char *xstrdup (const char *string);
+const char *base_name (const char *name);
+char *find_executable (const char *wrapper);
+char *chase_symlinks (const char *pathspec);
+int make_executable (const char *path);
+int check_executable (const char *path);
+char *strendzap (char *str, const char *pat);
+void lt_fatal (const char *message, ...);
+void lt_setenv (const char *name, const char *value);
+char *lt_extend_str (const char *orig_value, const char *add, int to_end);
+void lt_opt_process_env_set (const char *arg);
+void lt_opt_process_env_prepend (const char *arg);
+void lt_opt_process_env_append (const char *arg);
+int lt_split_name_value (const char *arg, char** name, char** value);
+void lt_update_exe_path (const char *name, const char *value);
+void lt_update_lib_path (const char *name, const char *value);
+
+static const char *script_text_part1 =
+EOF
+
+ func_emit_wrapper_part1 yes |
+ $SED -e 's/\([\\"]\)/\\\1/g' \
+ -e 's/^/ "/' -e 's/$/\\n"/'
+ echo ";"
+ cat <<EOF
+
+static const char *script_text_part2 =
+EOF
+ func_emit_wrapper_part2 yes |
+ $SED -e 's/\([\\"]\)/\\\1/g' \
+ -e 's/^/ "/' -e 's/$/\\n"/'
+ echo ";"
+
+ cat <<EOF
+const char * MAGIC_EXE = "$magic_exe";
+const char * LIB_PATH_VARNAME = "$shlibpath_var";
+EOF
+
+ if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then
+ func_to_host_pathlist "$temp_rpath"
+ cat <<EOF
+const char * LIB_PATH_VALUE = "$func_to_host_pathlist_result";
+EOF
+ else
+ cat <<"EOF"
+const char * LIB_PATH_VALUE = "";
+EOF
+ fi
+
+ if test -n "$dllsearchpath"; then
+ func_to_host_pathlist "$dllsearchpath:"
+ cat <<EOF
+const char * EXE_PATH_VARNAME = "PATH";
+const char * EXE_PATH_VALUE = "$func_to_host_pathlist_result";
+EOF
+ else
+ cat <<"EOF"
+const char * EXE_PATH_VARNAME = "";
+const char * EXE_PATH_VALUE = "";
+EOF
+ fi
+
+ if test "$fast_install" = yes; then
+ cat <<EOF
+const char * TARGET_PROGRAM_NAME = "lt-$outputname"; /* hopefully, no .exe */
+EOF
+ else
+ cat <<EOF
+const char * TARGET_PROGRAM_NAME = "$outputname"; /* hopefully, no .exe */
+EOF
+ fi
+
+
+ cat <<"EOF"
+
+#define LTWRAPPER_OPTION_PREFIX "--lt-"
+#define LTWRAPPER_OPTION_PREFIX_LENGTH 5
+
+static const size_t opt_prefix_len = LTWRAPPER_OPTION_PREFIX_LENGTH;
+static const char *ltwrapper_option_prefix = LTWRAPPER_OPTION_PREFIX;
+
+static const char *dumpscript_opt = LTWRAPPER_OPTION_PREFIX "dump-script";
+
+static const size_t env_set_opt_len = LTWRAPPER_OPTION_PREFIX_LENGTH + 7;
+static const char *env_set_opt = LTWRAPPER_OPTION_PREFIX "env-set";
+ /* argument is putenv-style "foo=bar", value of foo is set to bar */
+
+static const size_t env_prepend_opt_len = LTWRAPPER_OPTION_PREFIX_LENGTH + 11;
+static const char *env_prepend_opt = LTWRAPPER_OPTION_PREFIX "env-prepend";
+ /* argument is putenv-style "foo=bar", new value of foo is bar${foo} */
+
+static const size_t env_append_opt_len = LTWRAPPER_OPTION_PREFIX_LENGTH + 10;
+static const char *env_append_opt = LTWRAPPER_OPTION_PREFIX "env-append";
+ /* argument is putenv-style "foo=bar", new value of foo is ${foo}bar */
+
+int
+main (int argc, char *argv[])
+{
+ char **newargz;
+ int newargc;
+ char *tmp_pathspec;
+ char *actual_cwrapper_path;
+ char *actual_cwrapper_name;
+ char *target_name;
+ char *lt_argv_zero;
+ intptr_t rval = 127;
+
+ int i;
+
+ program_name = (char *) xstrdup (base_name (argv[0]));
+ LTWRAPPER_DEBUGPRINTF (("(main) argv[0] : %s\n", argv[0]));
+ LTWRAPPER_DEBUGPRINTF (("(main) program_name : %s\n", program_name));
+
+ /* very simple arg parsing; don't want to rely on getopt */
+ for (i = 1; i < argc; i++)
+ {
+ if (strcmp (argv[i], dumpscript_opt) == 0)
+ {
+EOF
+ case "$host" in
+ *mingw* | *cygwin* )
+ # make stdout use "unix" line endings
+ echo " setmode(1,_O_BINARY);"
+ ;;
+ esac
+
+ cat <<"EOF"
+ printf ("%s", script_text_part1);
+ printf ("%s", script_text_part2);
+ return 0;
+ }
+ }
+
+ newargz = XMALLOC (char *, argc + 1);
+ tmp_pathspec = find_executable (argv[0]);
+ if (tmp_pathspec == NULL)
+ lt_fatal ("Couldn't find %s", argv[0]);
+ LTWRAPPER_DEBUGPRINTF (("(main) found exe (before symlink chase) at : %s\n",
+ tmp_pathspec));
+
+ actual_cwrapper_path = chase_symlinks (tmp_pathspec);
+ LTWRAPPER_DEBUGPRINTF (("(main) found exe (after symlink chase) at : %s\n",
+ actual_cwrapper_path));
+ XFREE (tmp_pathspec);
+
+ actual_cwrapper_name = xstrdup( base_name (actual_cwrapper_path));
+ strendzap (actual_cwrapper_path, actual_cwrapper_name);
+
+ /* wrapper name transforms */
+ strendzap (actual_cwrapper_name, ".exe");
+ tmp_pathspec = lt_extend_str (actual_cwrapper_name, ".exe", 1);
+ XFREE (actual_cwrapper_name);
+ actual_cwrapper_name = tmp_pathspec;
+ tmp_pathspec = 0;
+
+ /* target_name transforms -- use actual target program name; might have lt- prefix */
+ target_name = xstrdup (base_name (TARGET_PROGRAM_NAME));
+ strendzap (target_name, ".exe");
+ tmp_pathspec = lt_extend_str (target_name, ".exe", 1);
+ XFREE (target_name);
+ target_name = tmp_pathspec;
+ tmp_pathspec = 0;
+
+ LTWRAPPER_DEBUGPRINTF (("(main) libtool target name: %s\n",
+ target_name));
+EOF
+
+ cat <<EOF
+ newargz[0] =
+ XMALLOC (char, (strlen (actual_cwrapper_path) +
+ strlen ("$objdir") + 1 + strlen (actual_cwrapper_name) + 1));
+ strcpy (newargz[0], actual_cwrapper_path);
+ strcat (newargz[0], "$objdir");
+ strcat (newargz[0], "/");
+EOF
+
+ cat <<"EOF"
+ /* stop here, and copy so we don't have to do this twice */
+ tmp_pathspec = xstrdup (newargz[0]);
+
+ /* do NOT want the lt- prefix here, so use actual_cwrapper_name */
+ strcat (newargz[0], actual_cwrapper_name);
+
+ /* DO want the lt- prefix here if it exists, so use target_name */
+ lt_argv_zero = lt_extend_str (tmp_pathspec, target_name, 1);
+ XFREE (tmp_pathspec);
+ tmp_pathspec = NULL;
+EOF
+
+ case $host_os in
+ mingw*)
+ cat <<"EOF"
+ {
+ char* p;
+ while ((p = strchr (newargz[0], '\\')) != NULL)
+ {
+ *p = '/';
+ }
+ while ((p = strchr (lt_argv_zero, '\\')) != NULL)
+ {
+ *p = '/';
+ }
+ }
+EOF
+ ;;
+ esac
+
+ cat <<"EOF"
+ XFREE (target_name);
+ XFREE (actual_cwrapper_path);
+ XFREE (actual_cwrapper_name);
+
+ lt_setenv ("BIN_SH", "xpg4"); /* for Tru64 */
+ lt_setenv ("DUALCASE", "1"); /* for MSK sh */
+ lt_update_lib_path (LIB_PATH_VARNAME, LIB_PATH_VALUE);
+ lt_update_exe_path (EXE_PATH_VARNAME, EXE_PATH_VALUE);
+
+ newargc=0;
+ for (i = 1; i < argc; i++)
+ {
+ if (strncmp (argv[i], env_set_opt, env_set_opt_len) == 0)
+ {
+ if (argv[i][env_set_opt_len] == '=')
+ {
+ const char *p = argv[i] + env_set_opt_len + 1;
+ lt_opt_process_env_set (p);
+ }
+ else if (argv[i][env_set_opt_len] == '\0' && i + 1 < argc)
+ {
+ lt_opt_process_env_set (argv[++i]); /* don't copy */
+ }
+ else
+ lt_fatal ("%s missing required argument", env_set_opt);
+ continue;
+ }
+ if (strncmp (argv[i], env_prepend_opt, env_prepend_opt_len) == 0)
+ {
+ if (argv[i][env_prepend_opt_len] == '=')
+ {
+ const char *p = argv[i] + env_prepend_opt_len + 1;
+ lt_opt_process_env_prepend (p);
+ }
+ else if (argv[i][env_prepend_opt_len] == '\0' && i + 1 < argc)
+ {
+ lt_opt_process_env_prepend (argv[++i]); /* don't copy */
+ }
+ else
+ lt_fatal ("%s missing required argument", env_prepend_opt);
+ continue;
+ }
+ if (strncmp (argv[i], env_append_opt, env_append_opt_len) == 0)
+ {
+ if (argv[i][env_append_opt_len] == '=')
+ {
+ const char *p = argv[i] + env_append_opt_len + 1;
+ lt_opt_process_env_append (p);
+ }
+ else if (argv[i][env_append_opt_len] == '\0' && i + 1 < argc)
+ {
+ lt_opt_process_env_append (argv[++i]); /* don't copy */
+ }
+ else
+ lt_fatal ("%s missing required argument", env_append_opt);
+ continue;
+ }
+ if (strncmp (argv[i], ltwrapper_option_prefix, opt_prefix_len) == 0)
+ {
+ /* however, if there is an option in the LTWRAPPER_OPTION_PREFIX
+ namespace, but it is not one of the ones we know about and
+ have already dealt with, above (inluding dump-script), then
+ report an error. Otherwise, targets might begin to believe
+ they are allowed to use options in the LTWRAPPER_OPTION_PREFIX
+ namespace. The first time any user complains about this, we'll
+ need to make LTWRAPPER_OPTION_PREFIX a configure-time option
+ or a configure.ac-settable value.
+ */
+ lt_fatal ("Unrecognized option in %s namespace: '%s'",
+ ltwrapper_option_prefix, argv[i]);
+ }
+ /* otherwise ... */
+ newargz[++newargc] = xstrdup (argv[i]);
+ }
+ newargz[++newargc] = NULL;
+
+ LTWRAPPER_DEBUGPRINTF (("(main) lt_argv_zero : %s\n", (lt_argv_zero ? lt_argv_zero : "<NULL>")));
+ for (i = 0; i < newargc; i++)
+ {
+ LTWRAPPER_DEBUGPRINTF (("(main) newargz[%d] : %s\n", i, (newargz[i] ? newargz[i] : "<NULL>")));
+ }
+
+EOF
+
+ case $host_os in
+ mingw*)
+ cat <<"EOF"
+ /* execv doesn't actually work on mingw as expected on unix */
+ rval = _spawnv (_P_WAIT, lt_argv_zero, (const char * const *) newargz);
+ if (rval == -1)
+ {
+ /* failed to start process */
+ LTWRAPPER_DEBUGPRINTF (("(main) failed to launch target \"%s\": errno = %d\n", lt_argv_zero, errno));
+ return 127;
+ }
+ return rval;
+EOF
+ ;;
+ *)
+ cat <<"EOF"
+ execv (lt_argv_zero, newargz);
+ return rval; /* =127, but avoids unused variable warning */
+EOF
+ ;;
+ esac
+
+ cat <<"EOF"
+}
+
+void *
+xmalloc (size_t num)
+{
+ void *p = (void *) malloc (num);
+ if (!p)
+ lt_fatal ("Memory exhausted");
+
+ return p;
+}
+
+char *
+xstrdup (const char *string)
+{
+ return string ? strcpy ((char *) xmalloc (strlen (string) + 1),
+ string) : NULL;
+}
+
+const char *
+base_name (const char *name)
+{
+ const char *base;
+
+#if defined (HAVE_DOS_BASED_FILE_SYSTEM)
+ /* Skip over the disk name in MSDOS pathnames. */
+ if (isalpha ((unsigned char) name[0]) && name[1] == ':')
+ name += 2;
+#endif
+
+ for (base = name; *name; name++)
+ if (IS_DIR_SEPARATOR (*name))
+ base = name + 1;
+ return base;
+}
+
+int
+check_executable (const char *path)
+{
+ struct stat st;
+
+ LTWRAPPER_DEBUGPRINTF (("(check_executable) : %s\n",
+ path ? (*path ? path : "EMPTY!") : "NULL!"));
+ if ((!path) || (!*path))
+ return 0;
+
+ if ((stat (path, &st) >= 0)
+ && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
+ return 1;
+ else
+ return 0;
+}
+
+int
+make_executable (const char *path)
+{
+ int rval = 0;
+ struct stat st;
+
+ LTWRAPPER_DEBUGPRINTF (("(make_executable) : %s\n",
+ path ? (*path ? path : "EMPTY!") : "NULL!"));
+ if ((!path) || (!*path))
+ return 0;
+
+ if (stat (path, &st) >= 0)
+ {
+ rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR);
+ }
+ return rval;
+}
+
+/* Searches for the full path of the wrapper. Returns
+ newly allocated full path name if found, NULL otherwise
+ Does not chase symlinks, even on platforms that support them.
+*/
+char *
+find_executable (const char *wrapper)
+{
+ int has_slash = 0;
+ const char *p;
+ const char *p_next;
+ /* static buffer for getcwd */
+ char tmp[LT_PATHMAX + 1];
+ int tmp_len;
+ char *concat_name;
+
+ LTWRAPPER_DEBUGPRINTF (("(find_executable) : %s\n",
+ wrapper ? (*wrapper ? wrapper : "EMPTY!") : "NULL!"));
+
+ if ((wrapper == NULL) || (*wrapper == '\0'))
+ return NULL;
+
+ /* Absolute path? */
+#if defined (HAVE_DOS_BASED_FILE_SYSTEM)
+ if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':')
+ {
+ concat_name = xstrdup (wrapper);
+ if (check_executable (concat_name))
+ return concat_name;
+ XFREE (concat_name);
+ }
+ else
+ {
+#endif
+ if (IS_DIR_SEPARATOR (wrapper[0]))
+ {
+ concat_name = xstrdup (wrapper);
+ if (check_executable (concat_name))
+ return concat_name;
+ XFREE (concat_name);
+ }
+#if defined (HAVE_DOS_BASED_FILE_SYSTEM)
+ }
+#endif
+
+ for (p = wrapper; *p; p++)
+ if (*p == '/')
+ {
+ has_slash = 1;
+ break;
+ }
+ if (!has_slash)
+ {
+ /* no slashes; search PATH */
+ const char *path = getenv ("PATH");
+ if (path != NULL)
+ {
+ for (p = path; *p; p = p_next)
+ {
+ const char *q;
+ size_t p_len;
+ for (q = p; *q; q++)
+ if (IS_PATH_SEPARATOR (*q))
+ break;
+ p_len = q - p;
+ p_next = (*q == '\0' ? q : q + 1);
+ if (p_len == 0)
+ {
+ /* empty path: current directory */
+ if (getcwd (tmp, LT_PATHMAX) == NULL)
+ lt_fatal ("getcwd failed");
+ tmp_len = strlen (tmp);
+ concat_name =
+ XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1);
+ memcpy (concat_name, tmp, tmp_len);
+ concat_name[tmp_len] = '/';
+ strcpy (concat_name + tmp_len + 1, wrapper);
+ }
+ else
+ {
+ concat_name =
+ XMALLOC (char, p_len + 1 + strlen (wrapper) + 1);
+ memcpy (concat_name, p, p_len);
+ concat_name[p_len] = '/';
+ strcpy (concat_name + p_len + 1, wrapper);
+ }
+ if (check_executable (concat_name))
+ return concat_name;
+ XFREE (concat_name);
+ }
+ }
+ /* not found in PATH; assume curdir */
+ }
+ /* Relative path | not found in path: prepend cwd */
+ if (getcwd (tmp, LT_PATHMAX) == NULL)
+ lt_fatal ("getcwd failed");
+ tmp_len = strlen (tmp);
+ concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1);
+ memcpy (concat_name, tmp, tmp_len);
+ concat_name[tmp_len] = '/';
+ strcpy (concat_name + tmp_len + 1, wrapper);
+
+ if (check_executable (concat_name))
+ return concat_name;
+ XFREE (concat_name);
+ return NULL;
+}
+
+char *
+chase_symlinks (const char *pathspec)
+{
+#ifndef S_ISLNK
+ return xstrdup (pathspec);
+#else
+ char buf[LT_PATHMAX];
+ struct stat s;
+ char *tmp_pathspec = xstrdup (pathspec);
+ char *p;
+ int has_symlinks = 0;
+ while (strlen (tmp_pathspec) && !has_symlinks)
+ {
+ LTWRAPPER_DEBUGPRINTF (("checking path component for symlinks: %s\n",
+ tmp_pathspec));
+ if (lstat (tmp_pathspec, &s) == 0)
+ {
+ if (S_ISLNK (s.st_mode) != 0)
+ {
+ has_symlinks = 1;
+ break;
+ }
+
+ /* search backwards for last DIR_SEPARATOR */
+ p = tmp_pathspec + strlen (tmp_pathspec) - 1;
+ while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p)))
+ p--;
+ if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p)))
+ {
+ /* no more DIR_SEPARATORS left */
+ break;
+ }
+ *p = '\0';
+ }
+ else
+ {
+ char *errstr = strerror (errno);
+ lt_fatal ("Error accessing file %s (%s)", tmp_pathspec, errstr);
+ }
+ }
+ XFREE (tmp_pathspec);
+
+ if (!has_symlinks)
+ {
+ return xstrdup (pathspec);
+ }
+
+ tmp_pathspec = realpath (pathspec, buf);
+ if (tmp_pathspec == 0)
+ {
+ lt_fatal ("Could not follow symlinks for %s", pathspec);
+ }
+ return xstrdup (tmp_pathspec);
+#endif
+}
+
+char *
+strendzap (char *str, const char *pat)
+{
+ size_t len, patlen;
+
+ assert (str != NULL);
+ assert (pat != NULL);
+
+ len = strlen (str);
+ patlen = strlen (pat);
+
+ if (patlen <= len)
+ {
+ str += len - patlen;
+ if (strcmp (str, pat) == 0)
+ *str = '\0';
+ }
+ return str;
+}
+
+static void
+lt_error_core (int exit_status, const char *mode,
+ const char *message, va_list ap)
+{
+ fprintf (stderr, "%s: %s: ", program_name, mode);
+ vfprintf (stderr, message, ap);
+ fprintf (stderr, ".\n");
+
+ if (exit_status >= 0)
+ exit (exit_status);
+}
+
+void
+lt_fatal (const char *message, ...)
+{
+ va_list ap;
+ va_start (ap, message);
+ lt_error_core (EXIT_FAILURE, "FATAL", message, ap);
+ va_end (ap);
+}
+
+void
+lt_setenv (const char *name, const char *value)
+{
+ LTWRAPPER_DEBUGPRINTF (("(lt_setenv) setting '%s' to '%s'\n",
+ (name ? name : "<NULL>"),
+ (value ? value : "<NULL>")));
+ {
+#ifdef HAVE_SETENV
+ /* always make a copy, for consistency with !HAVE_SETENV */
+ char *str = xstrdup (value);
+ setenv (name, str, 1);
+#else
+ int len = strlen (name) + 1 + strlen (value) + 1;
+ char *str = XMALLOC (char, len);
+ sprintf (str, "%s=%s", name, value);
+ if (putenv (str) != EXIT_SUCCESS)
+ {
+ XFREE (str);
+ }
+#endif
+ }
+}
+
+char *
+lt_extend_str (const char *orig_value, const char *add, int to_end)
+{
+ char *new_value;
+ if (orig_value && *orig_value)
+ {
+ int orig_value_len = strlen (orig_value);
+ int add_len = strlen (add);
+ new_value = XMALLOC (char, add_len + orig_value_len + 1);
+ if (to_end)
+ {
+ strcpy (new_value, orig_value);
+ strcpy (new_value + orig_value_len, add);
+ }
+ else
+ {
+ strcpy (new_value, add);
+ strcpy (new_value + add_len, orig_value);
+ }
+ }
+ else
+ {
+ new_value = xstrdup (add);
+ }
+ return new_value;
+}
+
+int
+lt_split_name_value (const char *arg, char** name, char** value)
+{
+ const char *p;
+ int len;
+ if (!arg || !*arg)
+ return 1;
+
+ p = strchr (arg, (int)'=');
+
+ if (!p)
+ return 1;
+
+ *value = xstrdup (++p);
+
+ len = strlen (arg) - strlen (*value);
+ *name = XMALLOC (char, len);
+ strncpy (*name, arg, len-1);
+ (*name)[len - 1] = '\0';
+
+ return 0;
+}
+
+void
+lt_opt_process_env_set (const char *arg)
+{
+ char *name = NULL;
+ char *value = NULL;
+
+ if (lt_split_name_value (arg, &name, &value) != 0)
+ {
+ XFREE (name);
+ XFREE (value);
+ lt_fatal ("bad argument for %s: '%s'", env_set_opt, arg);
+ }
+
+ lt_setenv (name, value);
+ XFREE (name);
+ XFREE (value);
+}
+
+void
+lt_opt_process_env_prepend (const char *arg)
+{
+ char *name = NULL;
+ char *value = NULL;
+ char *new_value = NULL;
+
+ if (lt_split_name_value (arg, &name, &value) != 0)
+ {
+ XFREE (name);
+ XFREE (value);
+ lt_fatal ("bad argument for %s: '%s'", env_prepend_opt, arg);
+ }
+
+ new_value = lt_extend_str (getenv (name), value, 0);
+ lt_setenv (name, new_value);
+ XFREE (new_value);
+ XFREE (name);
+ XFREE (value);
+}
+
+void
+lt_opt_process_env_append (const char *arg)
+{
+ char *name = NULL;
+ char *value = NULL;
+ char *new_value = NULL;
+
+ if (lt_split_name_value (arg, &name, &value) != 0)
+ {
+ XFREE (name);
+ XFREE (value);
+ lt_fatal ("bad argument for %s: '%s'", env_append_opt, arg);
+ }
+
+ new_value = lt_extend_str (getenv (name), value, 1);
+ lt_setenv (name, new_value);
+ XFREE (new_value);
+ XFREE (name);
+ XFREE (value);
+}
+
+void
+lt_update_exe_path (const char *name, const char *value)
+{
+ LTWRAPPER_DEBUGPRINTF (("(lt_update_exe_path) modifying '%s' by prepending '%s'\n",
+ (name ? name : "<NULL>"),
+ (value ? value : "<NULL>")));
+
+ if (name && *name && value && *value)
+ {
+ char *new_value = lt_extend_str (getenv (name), value, 0);
+ /* some systems can't cope with a ':'-terminated path #' */
+ int len = strlen (new_value);
+ while (((len = strlen (new_value)) > 0) && IS_PATH_SEPARATOR (new_value[len-1]))
+ {
+ new_value[len-1] = '\0';
+ }
+ lt_setenv (name, new_value);
+ XFREE (new_value);
+ }
+}
+
+void
+lt_update_lib_path (const char *name, const char *value)
+{
+ LTWRAPPER_DEBUGPRINTF (("(lt_update_lib_path) modifying '%s' by prepending '%s'\n",
+ (name ? name : "<NULL>"),
+ (value ? value : "<NULL>")));
+
+ if (name && *name && value && *value)
+ {
+ char *new_value = lt_extend_str (getenv (name), value, 0);
+ lt_setenv (name, new_value);
+ XFREE (new_value);
+ }
+}
+
+
+EOF
+}
+# end: func_emit_cwrapperexe_src
+
+# func_mode_link arg...
+func_mode_link ()
+{
+ $opt_debug
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+ # It is impossible to link a dll without this setting, and
+ # we shouldn't force the makefile maintainer to figure out
+ # which system we are compiling for in order to pass an extra
+ # flag for every libtool invocation.
+ # allow_undefined=no
+
+ # FIXME: Unfortunately, there are problems with the above when trying
+ # to make a dll which has undefined symbols, in which case not
+ # even a static library is built. For now, we need to specify
+ # -no-undefined on the libtool link line when we can be certain
+ # that all symbols are satisfied, otherwise we get a static library.
+ allow_undefined=yes
+ ;;
+ *)
+ allow_undefined=yes
+ ;;
+ esac
+ libtool_args=$nonopt
+ base_compile="$nonopt $@"
+ compile_command=$nonopt
+ finalize_command=$nonopt
+
+ compile_rpath=
+ finalize_rpath=
+ compile_shlibpath=
+ finalize_shlibpath=
+ convenience=
+ old_convenience=
+ deplibs=
+ old_deplibs=
+ compiler_flags=
+ linker_flags=
+ dllsearchpath=
+ lib_search_path=`pwd`
+ inst_prefix_dir=
+ new_inherited_linker_flags=
+
+ avoid_version=no
+ dlfiles=
+ dlprefiles=
+ dlself=no
+ export_dynamic=no
+ export_symbols=
+ export_symbols_regex=
+ generated=
+ libobjs=
+ ltlibs=
+ module=no
+ no_install=no
+ objs=
+ non_pic_objects=
+ precious_files_regex=
+ prefer_static_libs=no
+ preload=no
+ prev=
+ prevarg=
+ release=
+ rpath=
+ xrpath=
+ perm_rpath=
+ temp_rpath=
+ thread_safe=no
+ vinfo=
+ vinfo_number=no
+ weak_libs=
+ single_module="${wl}-single_module"
+ func_infer_tag $base_compile
+
+ # We need to know -static, to get the right output filenames.
+ for arg
+ do
+ case $arg in
+ -shared)
+ test "$build_libtool_libs" != yes && \
+ func_fatal_configuration "can not build a shared library"
+ build_old_libs=no
+ break
+ ;;
+ -all-static | -static | -static-libtool-libs)
+ case $arg in
+ -all-static)
+ if test "$build_libtool_libs" = yes && test -z "$link_static_flag"; then
+ func_warning "complete static linking is impossible in this configuration"
+ fi
+ if test -n "$link_static_flag"; then
+ dlopen_self=$dlopen_self_static
+ fi
+ prefer_static_libs=yes
+ ;;
+ -static)
+ if test -z "$pic_flag" && test -n "$link_static_flag"; then
+ dlopen_self=$dlopen_self_static
+ fi
+ prefer_static_libs=built
+ ;;
+ -static-libtool-libs)
+ if test -z "$pic_flag" && test -n "$link_static_flag"; then
+ dlopen_self=$dlopen_self_static
+ fi
+ prefer_static_libs=yes
+ ;;
+ esac
+ build_libtool_libs=no
+ build_old_libs=yes
+ break
+ ;;
+ esac
+ done
+
+ # See if our shared archives depend on static archives.
+ test -n "$old_archive_from_new_cmds" && build_old_libs=yes
+
+ # Go through the arguments, transforming them on the way.
+ while test "$#" -gt 0; do
+ arg="$1"
+ shift
+ func_quote_for_eval "$arg"
+ qarg=$func_quote_for_eval_unquoted_result
+ func_append libtool_args " $func_quote_for_eval_result"
+
+ # If the previous option needs an argument, assign it.
+ if test -n "$prev"; then
+ case $prev in
+ output)
+ func_append compile_command " @OUTPUT@"
+ func_append finalize_command " @OUTPUT@"
+ ;;
+ esac
+
+ case $prev in
+ dlfiles|dlprefiles)
+ if test "$preload" = no; then
+ # Add the symbol object into the linking commands.
+ func_append compile_command " @SYMFILE@"
+ func_append finalize_command " @SYMFILE@"
+ preload=yes
+ fi
+ case $arg in
+ *.la | *.lo) ;; # We handle these cases below.
+ force)
+ if test "$dlself" = no; then
+ dlself=needless
+ export_dynamic=yes
+ fi
+ prev=
+ continue
+ ;;
+ self)
+ if test "$prev" = dlprefiles; then
+ dlself=yes
+ elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then
+ dlself=yes
+ else
+ dlself=needless
+ export_dynamic=yes
+ fi
+ prev=
+ continue
+ ;;
+ *)
+ if test "$prev" = dlfiles; then
+ dlfiles="$dlfiles $arg"
+ else
+ dlprefiles="$dlprefiles $arg"
+ fi
+ prev=
+ continue
+ ;;
+ esac
+ ;;
+ expsyms)
+ export_symbols="$arg"
+ test -f "$arg" \
+ || func_fatal_error "symbol file \`$arg' does not exist"
+ prev=
+ continue
+ ;;
+ expsyms_regex)
+ export_symbols_regex="$arg"
+ prev=
+ continue
+ ;;
+ framework)
+ case $host in
+ *-*-darwin*)
+ case "$deplibs " in
+ *" $qarg.ltframework "*) ;;
+ *) deplibs="$deplibs $qarg.ltframework" # this is fixed later
+ ;;
+ esac
+ ;;
+ esac
+ prev=
+ continue
+ ;;
+ inst_prefix)
+ inst_prefix_dir="$arg"
+ prev=
+ continue
+ ;;
+ objectlist)
+ if test -f "$arg"; then
+ save_arg=$arg
+ moreargs=
+ for fil in `cat "$save_arg"`
+ do
+# moreargs="$moreargs $fil"
+ arg=$fil
+ # A libtool-controlled object.
+
+ # Check to see that this really is a libtool object.
+ if func_lalib_unsafe_p "$arg"; then
+ pic_object=
+ non_pic_object=
+
+ # Read the .lo file
+ func_source "$arg"
+
+ if test -z "$pic_object" ||
+ test -z "$non_pic_object" ||
+ test "$pic_object" = none &&
+ test "$non_pic_object" = none; then
+ func_fatal_error "cannot find name of object for \`$arg'"
+ fi
+
+ # Extract subdirectory from the argument.
+ func_dirname "$arg" "/" ""
+ xdir="$func_dirname_result"
+
+ if test "$pic_object" != none; then
+ # Prepend the subdirectory the object is found in.
+ pic_object="$xdir$pic_object"
+
+ if test "$prev" = dlfiles; then
+ if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then
+ dlfiles="$dlfiles $pic_object"
+ prev=
+ continue
+ else
+ # If libtool objects are unsupported, then we need to preload.
+ prev=dlprefiles
+ fi
+ fi
+
+ # CHECK ME: I think I busted this. -Ossama
+ if test "$prev" = dlprefiles; then
+ # Preload the old-style object.
+ dlprefiles="$dlprefiles $pic_object"
+ prev=
+ fi
+
+ # A PIC object.
+ func_append libobjs " $pic_object"
+ arg="$pic_object"
+ fi
+
+ # Non-PIC object.
+ if test "$non_pic_object" != none; then
+ # Prepend the subdirectory the object is found in.
+ non_pic_object="$xdir$non_pic_object"
+
+ # A standard non-PIC object
+ func_append non_pic_objects " $non_pic_object"
+ if test -z "$pic_object" || test "$pic_object" = none ; then
+ arg="$non_pic_object"
+ fi
+ else
+ # If the PIC object exists, use it instead.
+ # $xdir was prepended to $pic_object above.
+ non_pic_object="$pic_object"
+ func_append non_pic_objects " $non_pic_object"
+ fi
+ else
+ # Only an error if not doing a dry-run.
+ if $opt_dry_run; then
+ # Extract subdirectory from the argument.
+ func_dirname "$arg" "/" ""
+ xdir="$func_dirname_result"
+
+ func_lo2o "$arg"
+ pic_object=$xdir$objdir/$func_lo2o_result
+ non_pic_object=$xdir$func_lo2o_result
+ func_append libobjs " $pic_object"
+ func_append non_pic_objects " $non_pic_object"
+ else
+ func_fatal_error "\`$arg' is not a valid libtool object"
+ fi
+ fi
+ done
+ else
+ func_fatal_error "link input file \`$arg' does not exist"
+ fi
+ arg=$save_arg
+ prev=
+ continue
+ ;;
+ precious_regex)
+ precious_files_regex="$arg"
+ prev=
+ continue
+ ;;
+ release)
+ release="-$arg"
+ prev=
+ continue
+ ;;
+ rpath | xrpath)
+ # We need an absolute path.
+ case $arg in
+ [\\/]* | [A-Za-z]:[\\/]*) ;;
+ *)
+ func_fatal_error "only absolute run-paths are allowed"
+ ;;
+ esac
+ if test "$prev" = rpath; then
+ case "$rpath " in
+ *" $arg "*) ;;
+ *) rpath="$rpath $arg" ;;
+ esac
+ else
+ case "$xrpath " in
+ *" $arg "*) ;;
+ *) xrpath="$xrpath $arg" ;;
+ esac
+ fi
+ prev=
+ continue
+ ;;
+ shrext)
+ shrext_cmds="$arg"
+ prev=
+ continue
+ ;;
+ weak)
+ weak_libs="$weak_libs $arg"
+ prev=
+ continue
+ ;;
+ xcclinker)
+ linker_flags="$linker_flags $qarg"
+ compiler_flags="$compiler_flags $qarg"
+ prev=
+ func_append compile_command " $qarg"
+ func_append finalize_command " $qarg"
+ continue
+ ;;
+ xcompiler)
+ compiler_flags="$compiler_flags $qarg"
+ prev=
+ func_append compile_command " $qarg"
+ func_append finalize_command " $qarg"
+ continue
+ ;;
+ xlinker)
+ linker_flags="$linker_flags $qarg"
+ compiler_flags="$compiler_flags $wl$qarg"
+ prev=
+ func_append compile_command " $wl$qarg"
+ func_append finalize_command " $wl$qarg"
+ continue
+ ;;
+ *)
+ eval "$prev=\"\$arg\""
+ prev=
+ continue
+ ;;
+ esac
+ fi # test -n "$prev"
+
+ prevarg="$arg"
+
+ case $arg in
+ -all-static)
+ if test -n "$link_static_flag"; then
+ # See comment for -static flag below, for more details.
+ func_append compile_command " $link_static_flag"
+ func_append finalize_command " $link_static_flag"
+ fi
+ continue
+ ;;
+
+ -allow-undefined)
+ # FIXME: remove this flag sometime in the future.
+ func_fatal_error "\`-allow-undefined' must not be used because it is the default"
+ ;;
+
+ -avoid-version)
+ avoid_version=yes
+ continue
+ ;;
+
+ -dlopen)
+ prev=dlfiles
+ continue
+ ;;
+
+ -dlpreopen)
+ prev=dlprefiles
+ continue
+ ;;
+
+ -export-dynamic)
+ export_dynamic=yes
+ continue
+ ;;
+
+ -export-symbols | -export-symbols-regex)
+ if test -n "$export_symbols" || test -n "$export_symbols_regex"; then
+ func_fatal_error "more than one -exported-symbols argument is not allowed"
+ fi
+ if test "X$arg" = "X-export-symbols"; then
+ prev=expsyms
+ else
+ prev=expsyms_regex
+ fi
+ continue
+ ;;
+
+ -framework)
+ prev=framework
+ continue
+ ;;
+
+ -inst-prefix-dir)
+ prev=inst_prefix
+ continue
+ ;;
+
+ # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:*
+ # so, if we see these flags be careful not to treat them like -L
+ -L[A-Z][A-Z]*:*)
+ case $with_gcc/$host in
+ no/*-*-irix* | /*-*-irix*)
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ ;;
+ esac
+ continue
+ ;;
+
+ -L*)
+ func_stripname '-L' '' "$arg"
+ dir=$func_stripname_result
+ if test -z "$dir"; then
+ if test "$#" -gt 0; then
+ func_fatal_error "require no space between \`-L' and \`$1'"
+ else
+ func_fatal_error "need path for \`-L' option"
+ fi
+ fi
+ # We need an absolute path.
+ case $dir in
+ [\\/]* | [A-Za-z]:[\\/]*) ;;
+ *)
+ absdir=`cd "$dir" && pwd`
+ test -z "$absdir" && \
+ func_fatal_error "cannot determine absolute directory name of \`$dir'"
+ dir="$absdir"
+ ;;
+ esac
+ case "$deplibs " in
+ *" -L$dir "*) ;;
+ *)
+ deplibs="$deplibs -L$dir"
+ lib_search_path="$lib_search_path $dir"
+ ;;
+ esac
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+ testbindir=`$ECHO "X$dir" | $Xsed -e 's*/lib$*/bin*'`
+ case :$dllsearchpath: in
+ *":$dir:"*) ;;
+ ::) dllsearchpath=$dir;;
+ *) dllsearchpath="$dllsearchpath:$dir";;
+ esac
+ case :$dllsearchpath: in
+ *":$testbindir:"*) ;;
+ ::) dllsearchpath=$testbindir;;
+ *) dllsearchpath="$dllsearchpath:$testbindir";;
+ esac
+ ;;
+ esac
+ continue
+ ;;
+
+ -l*)
+ if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc*)
+ # These systems don't actually have a C or math library (as such)
+ continue
+ ;;
+ *-*-os2*)
+ # These systems don't actually have a C library (as such)
+ test "X$arg" = "X-lc" && continue
+ ;;
+ *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*)
+ # Do not include libc due to us having libc/libc_r.
+ test "X$arg" = "X-lc" && continue
+ ;;
+ *-*-rhapsody* | *-*-darwin1.[012])
+ # Rhapsody C and math libraries are in the System framework
+ deplibs="$deplibs System.ltframework"
+ continue
+ ;;
+ *-*-sco3.2v5* | *-*-sco5v6*)
+ # Causes problems with __ctype
+ test "X$arg" = "X-lc" && continue
+ ;;
+ *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*)
+ # Compiler inserts libc in the correct place for threads to work
+ test "X$arg" = "X-lc" && continue
+ ;;
+ esac
+ elif test "X$arg" = "X-lc_r"; then
+ case $host in
+ *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*)
+ # Do not include libc_r directly, use -pthread flag.
+ continue
+ ;;
+ esac
+ fi
+ deplibs="$deplibs $arg"
+ continue
+ ;;
+
+ -module)
+ module=yes
+ continue
+ ;;
+
+ # Tru64 UNIX uses -model [arg] to determine the layout of C++
+ # classes, name mangling, and exception handling.
+ # Darwin uses the -arch flag to determine output architecture.
+ -model|-arch|-isysroot)
+ compiler_flags="$compiler_flags $arg"
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ prev=xcompiler
+ continue
+ ;;
+
+ -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe|-threads)
+ compiler_flags="$compiler_flags $arg"
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ case "$new_inherited_linker_flags " in
+ *" $arg "*) ;;
+ * ) new_inherited_linker_flags="$new_inherited_linker_flags $arg" ;;
+ esac
+ continue
+ ;;
+
+ -multi_module)
+ single_module="${wl}-multi_module"
+ continue
+ ;;
+
+ -no-fast-install)
+ fast_install=no
+ continue
+ ;;
+
+ -no-install)
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*)
+ # The PATH hackery in wrapper scripts is required on Windows
+ # and Darwin in order for the loader to find any dlls it needs.
+ func_warning "\`-no-install' is ignored for $host"
+ func_warning "assuming \`-no-fast-install' instead"
+ fast_install=no
+ ;;
+ *) no_install=yes ;;
+ esac
+ continue
+ ;;
+
+ -no-undefined)
+ allow_undefined=no
+ continue
+ ;;
+
+ -objectlist)
+ prev=objectlist
+ continue
+ ;;
+
+ -o) prev=output ;;
+
+ -precious-files-regex)
+ prev=precious_regex
+ continue
+ ;;
+
+ -release)
+ prev=release
+ continue
+ ;;
+
+ -rpath)
+ prev=rpath
+ continue
+ ;;
+
+ -R)
+ prev=xrpath
+ continue
+ ;;
+
+ -R*)
+ func_stripname '-R' '' "$arg"
+ dir=$func_stripname_result
+ # We need an absolute path.
+ case $dir in
+ [\\/]* | [A-Za-z]:[\\/]*) ;;
+ *)
+ func_fatal_error "only absolute run-paths are allowed"
+ ;;
+ esac
+ case "$xrpath " in
+ *" $dir "*) ;;
+ *) xrpath="$xrpath $dir" ;;
+ esac
+ continue
+ ;;
+
+ -shared)
+ # The effects of -shared are defined in a previous loop.
+ continue
+ ;;
+
+ -shrext)
+ prev=shrext
+ continue
+ ;;
+
+ -static | -static-libtool-libs)
+ # The effects of -static are defined in a previous loop.
+ # We used to do the same as -all-static on platforms that
+ # didn't have a PIC flag, but the assumption that the effects
+ # would be equivalent was wrong. It would break on at least
+ # Digital Unix and AIX.
+ continue
+ ;;
+
+ -thread-safe)
+ thread_safe=yes
+ continue
+ ;;
+
+ -version-info)
+ prev=vinfo
+ continue
+ ;;
+
+ -version-number)
+ prev=vinfo
+ vinfo_number=yes
+ continue
+ ;;
+
+ -weak)
+ prev=weak
+ continue
+ ;;
+
+ -Wc,*)
+ func_stripname '-Wc,' '' "$arg"
+ args=$func_stripname_result
+ arg=
+ save_ifs="$IFS"; IFS=','
+ for flag in $args; do
+ IFS="$save_ifs"
+ func_quote_for_eval "$flag"
+ arg="$arg $wl$func_quote_for_eval_result"
+ compiler_flags="$compiler_flags $func_quote_for_eval_result"
+ done
+ IFS="$save_ifs"
+ func_stripname ' ' '' "$arg"
+ arg=$func_stripname_result
+ ;;
+
+ -Wl,*)
+ func_stripname '-Wl,' '' "$arg"
+ args=$func_stripname_result
+ arg=
+ save_ifs="$IFS"; IFS=','
+ for flag in $args; do
+ IFS="$save_ifs"
+ func_quote_for_eval "$flag"
+ arg="$arg $wl$func_quote_for_eval_result"
+ compiler_flags="$compiler_flags $wl$func_quote_for_eval_result"
+ linker_flags="$linker_flags $func_quote_for_eval_result"
+ done
+ IFS="$save_ifs"
+ func_stripname ' ' '' "$arg"
+ arg=$func_stripname_result
+ ;;
+
+ -Xcompiler)
+ prev=xcompiler
+ continue
+ ;;
+
+ -Xlinker)
+ prev=xlinker
+ continue
+ ;;
+
+ -XCClinker)
+ prev=xcclinker
+ continue
+ ;;
+
+ # -msg_* for osf cc
+ -msg_*)
+ func_quote_for_eval "$arg"
+ arg="$func_quote_for_eval_result"
+ ;;
+
+ # -64, -mips[0-9] enable 64-bit mode on the SGI compiler
+ # -r[0-9][0-9]* specifies the processor on the SGI compiler
+ # -xarch=*, -xtarget=* enable 64-bit mode on the Sun compiler
+ # +DA*, +DD* enable 64-bit mode on the HP compiler
+ # -q* pass through compiler args for the IBM compiler
+ # -m*, -t[45]*, -txscale* pass through architecture-specific
+ # compiler args for GCC
+ # -F/path gives path to uninstalled frameworks, gcc on darwin
+ # -p, -pg, --coverage, -fprofile-* pass through profiling flag for GCC
+ # @file GCC response files
+ -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \
+ -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*)
+ func_quote_for_eval "$arg"
+ arg="$func_quote_for_eval_result"
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ compiler_flags="$compiler_flags $arg"
+ continue
+ ;;
+
+ # Some other compiler flag.
+ -* | +*)
+ func_quote_for_eval "$arg"
+ arg="$func_quote_for_eval_result"
+ ;;
+
+ *.$objext)
+ # A standard object.
+ objs="$objs $arg"
+ ;;
+
+ *.lo)
+ # A libtool-controlled object.
+
+ # Check to see that this really is a libtool object.
+ if func_lalib_unsafe_p "$arg"; then
+ pic_object=
+ non_pic_object=
+
+ # Read the .lo file
+ func_source "$arg"
+
+ if test -z "$pic_object" ||
+ test -z "$non_pic_object" ||
+ test "$pic_object" = none &&
+ test "$non_pic_object" = none; then
+ func_fatal_error "cannot find name of object for \`$arg'"
+ fi
+
+ # Extract subdirectory from the argument.
+ func_dirname "$arg" "/" ""
+ xdir="$func_dirname_result"
+
+ if test "$pic_object" != none; then
+ # Prepend the subdirectory the object is found in.
+ pic_object="$xdir$pic_object"
+
+ if test "$prev" = dlfiles; then
+ if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then
+ dlfiles="$dlfiles $pic_object"
+ prev=
+ continue
+ else
+ # If libtool objects are unsupported, then we need to preload.
+ prev=dlprefiles
+ fi
+ fi
+
+ # CHECK ME: I think I busted this. -Ossama
+ if test "$prev" = dlprefiles; then
+ # Preload the old-style object.
+ dlprefiles="$dlprefiles $pic_object"
+ prev=
+ fi
+
+ # A PIC object.
+ func_append libobjs " $pic_object"
+ arg="$pic_object"
+ fi
+
+ # Non-PIC object.
+ if test "$non_pic_object" != none; then
+ # Prepend the subdirectory the object is found in.
+ non_pic_object="$xdir$non_pic_object"
+
+ # A standard non-PIC object
+ func_append non_pic_objects " $non_pic_object"
+ if test -z "$pic_object" || test "$pic_object" = none ; then
+ arg="$non_pic_object"
+ fi
+ else
+ # If the PIC object exists, use it instead.
+ # $xdir was prepended to $pic_object above.
+ non_pic_object="$pic_object"
+ func_append non_pic_objects " $non_pic_object"
+ fi
+ else
+ # Only an error if not doing a dry-run.
+ if $opt_dry_run; then
+ # Extract subdirectory from the argument.
+ func_dirname "$arg" "/" ""
+ xdir="$func_dirname_result"
+
+ func_lo2o "$arg"
+ pic_object=$xdir$objdir/$func_lo2o_result
+ non_pic_object=$xdir$func_lo2o_result
+ func_append libobjs " $pic_object"
+ func_append non_pic_objects " $non_pic_object"
+ else
+ func_fatal_error "\`$arg' is not a valid libtool object"
+ fi
+ fi
+ ;;
+
+ *.$libext)
+ # An archive.
+ deplibs="$deplibs $arg"
+ old_deplibs="$old_deplibs $arg"
+ continue
+ ;;
+
+ *.la)
+ # A libtool-controlled library.
+
+ if test "$prev" = dlfiles; then
+ # This library was specified with -dlopen.
+ dlfiles="$dlfiles $arg"
+ prev=
+ elif test "$prev" = dlprefiles; then
+ # The library was specified with -dlpreopen.
+ dlprefiles="$dlprefiles $arg"
+ prev=
+ else
+ deplibs="$deplibs $arg"
+ fi
+ continue
+ ;;
+
+ # Some other compiler argument.
+ *)
+ # Unknown arguments in both finalize_command and compile_command need
+ # to be aesthetically quoted because they are evaled later.
+ func_quote_for_eval "$arg"
+ arg="$func_quote_for_eval_result"
+ ;;
+ esac # arg
+
+ # Now actually substitute the argument into the commands.
+ if test -n "$arg"; then
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ fi
+ done # argument parsing loop
+
+ test -n "$prev" && \
+ func_fatal_help "the \`$prevarg' option requires an argument"
+
+ if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then
+ eval arg=\"$export_dynamic_flag_spec\"
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ fi
+
+ oldlibs=
+ # calculate the name of the file, without its directory
+ func_basename "$output"
+ outputname="$func_basename_result"
+ libobjs_save="$libobjs"
+
+ if test -n "$shlibpath_var"; then
+ # get the directories listed in $shlibpath_var
+ eval shlib_search_path=\`\$ECHO \"X\${$shlibpath_var}\" \| \$Xsed -e \'s/:/ /g\'\`
+ else
+ shlib_search_path=
+ fi
+ eval sys_lib_search_path=\"$sys_lib_search_path_spec\"
+ eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\"
+
+ func_dirname "$output" "/" ""
+ output_objdir="$func_dirname_result$objdir"
+ # Create the object directory.
+ func_mkdir_p "$output_objdir"
+
+ # Determine the type of output
+ case $output in
+ "")
+ func_fatal_help "you must specify an output file"
+ ;;
+ *.$libext) linkmode=oldlib ;;
+ *.lo | *.$objext) linkmode=obj ;;
+ *.la) linkmode=lib ;;
+ *) linkmode=prog ;; # Anything else should be a program.
+ esac
+
+ specialdeplibs=
+
+ libs=
+ # Find all interdependent deplibs by searching for libraries
+ # that are linked more than once (e.g. -la -lb -la)
+ for deplib in $deplibs; do
+ if $opt_duplicate_deps ; then
+ case "$libs " in
+ *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;;
+ esac
+ fi
+ libs="$libs $deplib"
+ done
+
+ if test "$linkmode" = lib; then
+ libs="$predeps $libs $compiler_lib_search_path $postdeps"
+
+ # Compute libraries that are listed more than once in $predeps
+ # $postdeps and mark them as special (i.e., whose duplicates are
+ # not to be eliminated).
+ pre_post_deps=
+ if $opt_duplicate_compiler_generated_deps; then
+ for pre_post_dep in $predeps $postdeps; do
+ case "$pre_post_deps " in
+ *" $pre_post_dep "*) specialdeplibs="$specialdeplibs $pre_post_deps" ;;
+ esac
+ pre_post_deps="$pre_post_deps $pre_post_dep"
+ done
+ fi
+ pre_post_deps=
+ fi
+
+ deplibs=
+ newdependency_libs=
+ newlib_search_path=
+ need_relink=no # whether we're linking any uninstalled libtool libraries
+ notinst_deplibs= # not-installed libtool libraries
+ notinst_path= # paths that contain not-installed libtool libraries
+
+ case $linkmode in
+ lib)
+ passes="conv dlpreopen link"
+ for file in $dlfiles $dlprefiles; do
+ case $file in
+ *.la) ;;
+ *)
+ func_fatal_help "libraries can \`-dlopen' only libtool libraries: $file"
+ ;;
+ esac
+ done
+ ;;
+ prog)
+ compile_deplibs=
+ finalize_deplibs=
+ alldeplibs=no
+ newdlfiles=
+ newdlprefiles=
+ passes="conv scan dlopen dlpreopen link"
+ ;;
+ *) passes="conv"
+ ;;
+ esac
+
+ for pass in $passes; do
+ # The preopen pass in lib mode reverses $deplibs; put it back here
+ # so that -L comes before libs that need it for instance...
+ if test "$linkmode,$pass" = "lib,link"; then
+ ## FIXME: Find the place where the list is rebuilt in the wrong
+ ## order, and fix it there properly
+ tmp_deplibs=
+ for deplib in $deplibs; do
+ tmp_deplibs="$deplib $tmp_deplibs"
+ done
+ deplibs="$tmp_deplibs"
+ fi
+
+ if test "$linkmode,$pass" = "lib,link" ||
+ test "$linkmode,$pass" = "prog,scan"; then
+ libs="$deplibs"
+ deplibs=
+ fi
+ if test "$linkmode" = prog; then
+ case $pass in
+ dlopen) libs="$dlfiles" ;;
+ dlpreopen) libs="$dlprefiles" ;;
+ link) libs="$deplibs %DEPLIBS% $dependency_libs" ;;
+ esac
+ fi
+ if test "$linkmode,$pass" = "lib,dlpreopen"; then
+ # Collect and forward deplibs of preopened libtool libs
+ for lib in $dlprefiles; do
+ # Ignore non-libtool-libs
+ dependency_libs=
+ case $lib in
+ *.la) func_source "$lib" ;;
+ esac
+
+ # Collect preopened libtool deplibs, except any this library
+ # has declared as weak libs
+ for deplib in $dependency_libs; do
+ deplib_base=`$ECHO "X$deplib" | $Xsed -e "$basename"`
+ case " $weak_libs " in
+ *" $deplib_base "*) ;;
+ *) deplibs="$deplibs $deplib" ;;
+ esac
+ done
+ done
+ libs="$dlprefiles"
+ fi
+ if test "$pass" = dlopen; then
+ # Collect dlpreopened libraries
+ save_deplibs="$deplibs"
+ deplibs=
+ fi
+
+ for deplib in $libs; do
+ lib=
+ found=no
+ case $deplib in
+ -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe|-threads)
+ if test "$linkmode,$pass" = "prog,link"; then
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ else
+ compiler_flags="$compiler_flags $deplib"
+ if test "$linkmode" = lib ; then
+ case "$new_inherited_linker_flags " in
+ *" $deplib "*) ;;
+ * ) new_inherited_linker_flags="$new_inherited_linker_flags $deplib" ;;
+ esac
+ fi
+ fi
+ continue
+ ;;
+ -l*)
+ if test "$linkmode" != lib && test "$linkmode" != prog; then
+ func_warning "\`-l' is ignored for archives/objects"
+ continue
+ fi
+ func_stripname '-l' '' "$deplib"
+ name=$func_stripname_result
+ if test "$linkmode" = lib; then
+ searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path"
+ else
+ searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path"
+ fi
+ for searchdir in $searchdirs; do
+ for search_ext in .la $std_shrext .so .a; do
+ # Search the libtool library
+ lib="$searchdir/lib${name}${search_ext}"
+ if test -f "$lib"; then
+ if test "$search_ext" = ".la"; then
+ found=yes
+ else
+ found=no
+ fi
+ break 2
+ fi
+ done
+ done
+ if test "$found" != yes; then
+ # deplib doesn't seem to be a libtool library
+ if test "$linkmode,$pass" = "prog,link"; then
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ else
+ deplibs="$deplib $deplibs"
+ test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs"
+ fi
+ continue
+ else # deplib is a libtool library
+ # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib,
+ # We need to do some special things here, and not later.
+ if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+ case " $predeps $postdeps " in
+ *" $deplib "*)
+ if func_lalib_p "$lib"; then
+ library_names=
+ old_library=
+ func_source "$lib"
+ for l in $old_library $library_names; do
+ ll="$l"
+ done
+ if test "X$ll" = "X$old_library" ; then # only static version available
+ found=no
+ func_dirname "$lib" "" "."
+ ladir="$func_dirname_result"
+ lib=$ladir/$old_library
+ if test "$linkmode,$pass" = "prog,link"; then
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ else
+ deplibs="$deplib $deplibs"
+ test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs"
+ fi
+ continue
+ fi
+ fi
+ ;;
+ *) ;;
+ esac
+ fi
+ fi
+ ;; # -l
+ *.ltframework)
+ if test "$linkmode,$pass" = "prog,link"; then
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ else
+ deplibs="$deplib $deplibs"
+ if test "$linkmode" = lib ; then
+ case "$new_inherited_linker_flags " in
+ *" $deplib "*) ;;
+ * ) new_inherited_linker_flags="$new_inherited_linker_flags $deplib" ;;
+ esac
+ fi
+ fi
+ continue
+ ;;
+ -L*)
+ case $linkmode in
+ lib)
+ deplibs="$deplib $deplibs"
+ test "$pass" = conv && continue
+ newdependency_libs="$deplib $newdependency_libs"
+ func_stripname '-L' '' "$deplib"
+ newlib_search_path="$newlib_search_path $func_stripname_result"
+ ;;
+ prog)
+ if test "$pass" = conv; then
+ deplibs="$deplib $deplibs"
+ continue
+ fi
+ if test "$pass" = scan; then
+ deplibs="$deplib $deplibs"
+ else
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ fi
+ func_stripname '-L' '' "$deplib"
+ newlib_search_path="$newlib_search_path $func_stripname_result"
+ ;;
+ *)
+ func_warning "\`-L' is ignored for archives/objects"
+ ;;
+ esac # linkmode
+ continue
+ ;; # -L
+ -R*)
+ if test "$pass" = link; then
+ func_stripname '-R' '' "$deplib"
+ dir=$func_stripname_result
+ # Make sure the xrpath contains only unique directories.
+ case "$xrpath " in
+ *" $dir "*) ;;
+ *) xrpath="$xrpath $dir" ;;
+ esac
+ fi
+ deplibs="$deplib $deplibs"
+ continue
+ ;;
+ *.la) lib="$deplib" ;;
+ *.$libext)
+ if test "$pass" = conv; then
+ deplibs="$deplib $deplibs"
+ continue
+ fi
+ case $linkmode in
+ lib)
+ # Linking convenience modules into shared libraries is allowed,
+ # but linking other static libraries is non-portable.
+ case " $dlpreconveniencelibs " in
+ *" $deplib "*) ;;
+ *)
+ valid_a_lib=no
+ case $deplibs_check_method in
+ match_pattern*)
+ set dummy $deplibs_check_method; shift
+ match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
+ if eval "\$ECHO \"X$deplib\"" 2>/dev/null | $Xsed -e 10q \
+ | $EGREP "$match_pattern_regex" > /dev/null; then
+ valid_a_lib=yes
+ fi
+ ;;
+ pass_all)
+ valid_a_lib=yes
+ ;;
+ esac
+ if test "$valid_a_lib" != yes; then
+ $ECHO
+ $ECHO "*** Warning: Trying to link with static lib archive $deplib."
+ $ECHO "*** I have the capability to make that library automatically link in when"
+ $ECHO "*** you link to this library. But I can only do this if you have a"
+ $ECHO "*** shared version of the library, which you do not appear to have"
+ $ECHO "*** because the file extensions .$libext of this argument makes me believe"
+ $ECHO "*** that it is just a static archive that I should not use here."
+ else
+ $ECHO
+ $ECHO "*** Warning: Linking the shared library $output against the"
+ $ECHO "*** static library $deplib is not portable!"
+ deplibs="$deplib $deplibs"
+ fi
+ ;;
+ esac
+ continue
+ ;;
+ prog)
+ if test "$pass" != link; then
+ deplibs="$deplib $deplibs"
+ else
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ fi
+ continue
+ ;;
+ esac # linkmode
+ ;; # *.$libext
+ *.lo | *.$objext)
+ if test "$pass" = conv; then
+ deplibs="$deplib $deplibs"
+ elif test "$linkmode" = prog; then
+ if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then
+ # If there is no dlopen support or we're linking statically,
+ # we need to preload.
+ newdlprefiles="$newdlprefiles $deplib"
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ else
+ newdlfiles="$newdlfiles $deplib"
+ fi
+ fi
+ continue
+ ;;
+ %DEPLIBS%)
+ alldeplibs=yes
+ continue
+ ;;
+ esac # case $deplib
+
+ if test "$found" = yes || test -f "$lib"; then :
+ else
+ func_fatal_error "cannot find the library \`$lib' or unhandled argument \`$deplib'"
+ fi
+
+ # Check to see that this really is a libtool archive.
+ func_lalib_unsafe_p "$lib" \
+ || func_fatal_error "\`$lib' is not a valid libtool archive"
+
+ func_dirname "$lib" "" "."
+ ladir="$func_dirname_result"
+
+ dlname=
+ dlopen=
+ dlpreopen=
+ libdir=
+ library_names=
+ old_library=
+ inherited_linker_flags=
+ # If the library was installed with an old release of libtool,
+ # it will not redefine variables installed, or shouldnotlink
+ installed=yes
+ shouldnotlink=no
+ avoidtemprpath=
+
+
+ # Read the .la file
+ func_source "$lib"
+
+ # Convert "-framework foo" to "foo.ltframework"
+ if test -n "$inherited_linker_flags"; then
+ tmp_inherited_linker_flags=`$ECHO "X$inherited_linker_flags" | $Xsed -e 's/-framework \([^ $]*\)/\1.ltframework/g'`
+ for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do
+ case " $new_inherited_linker_flags " in
+ *" $tmp_inherited_linker_flag "*) ;;
+ *) new_inherited_linker_flags="$new_inherited_linker_flags $tmp_inherited_linker_flag";;
+ esac
+ done
+ fi
+ dependency_libs=`$ECHO "X $dependency_libs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'`
+ if test "$linkmode,$pass" = "lib,link" ||
+ test "$linkmode,$pass" = "prog,scan" ||
+ { test "$linkmode" != prog && test "$linkmode" != lib; }; then
+ test -n "$dlopen" && dlfiles="$dlfiles $dlopen"
+ test -n "$dlpreopen" && dlprefiles="$dlprefiles $dlpreopen"
+ fi
+
+ if test "$pass" = conv; then
+ # Only check for convenience libraries
+ deplibs="$lib $deplibs"
+ if test -z "$libdir"; then
+ if test -z "$old_library"; then
+ func_fatal_error "cannot find name of link library for \`$lib'"
+ fi
+ # It is a libtool convenience library, so add in its objects.
+ convenience="$convenience $ladir/$objdir/$old_library"
+ old_convenience="$old_convenience $ladir/$objdir/$old_library"
+ elif test "$linkmode" != prog && test "$linkmode" != lib; then
+ func_fatal_error "\`$lib' is not a convenience library"
+ fi
+ tmp_libs=
+ for deplib in $dependency_libs; do
+ deplibs="$deplib $deplibs"
+ if $opt_duplicate_deps ; then
+ case "$tmp_libs " in
+ *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;;
+ esac
+ fi
+ tmp_libs="$tmp_libs $deplib"
+ done
+ continue
+ fi # $pass = conv
+
+
+ # Get the name of the library we link against.
+ linklib=
+ for l in $old_library $library_names; do
+ linklib="$l"
+ done
+ if test -z "$linklib"; then
+ func_fatal_error "cannot find name of link library for \`$lib'"
+ fi
+
+ # This library was specified with -dlopen.
+ if test "$pass" = dlopen; then
+ if test -z "$libdir"; then
+ func_fatal_error "cannot -dlopen a convenience library: \`$lib'"
+ fi
+ if test -z "$dlname" ||
+ test "$dlopen_support" != yes ||
+ test "$build_libtool_libs" = no; then
+ # If there is no dlname, no dlopen support or we're linking
+ # statically, we need to preload. We also need to preload any
+ # dependent libraries so libltdl's deplib preloader doesn't
+ # bomb out in the load deplibs phase.
+ dlprefiles="$dlprefiles $lib $dependency_libs"
+ else
+ newdlfiles="$newdlfiles $lib"
+ fi
+ continue
+ fi # $pass = dlopen
+
+ # We need an absolute path.
+ case $ladir in
+ [\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;;
+ *)
+ abs_ladir=`cd "$ladir" && pwd`
+ if test -z "$abs_ladir"; then
+ func_warning "cannot determine absolute directory name of \`$ladir'"
+ func_warning "passing it literally to the linker, although it might fail"
+ abs_ladir="$ladir"
+ fi
+ ;;
+ esac
+ func_basename "$lib"
+ laname="$func_basename_result"
+
+ # Find the relevant object directory and library name.
+ if test "X$installed" = Xyes; then
+ if test ! -f "$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then
+ func_warning "library \`$lib' was moved."
+ dir="$ladir"
+ absdir="$abs_ladir"
+ libdir="$abs_ladir"
+ else
+ dir="$libdir"
+ absdir="$libdir"
+ fi
+ test "X$hardcode_automatic" = Xyes && avoidtemprpath=yes
+ else
+ if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then
+ dir="$ladir"
+ absdir="$abs_ladir"
+ # Remove this search path later
+ notinst_path="$notinst_path $abs_ladir"
+ else
+ dir="$ladir/$objdir"
+ absdir="$abs_ladir/$objdir"
+ # Remove this search path later
+ notinst_path="$notinst_path $abs_ladir"
+ fi
+ fi # $installed = yes
+ func_stripname 'lib' '.la' "$laname"
+ name=$func_stripname_result
+
+ # This library was specified with -dlpreopen.
+ if test "$pass" = dlpreopen; then
+ if test -z "$libdir" && test "$linkmode" = prog; then
+ func_fatal_error "only libraries may -dlpreopen a convenience library: \`$lib'"
+ fi
+ # Prefer using a static library (so that no silly _DYNAMIC symbols
+ # are required to link).
+ if test -n "$old_library"; then
+ newdlprefiles="$newdlprefiles $dir/$old_library"
+ # Keep a list of preopened convenience libraries to check
+ # that they are being used correctly in the link pass.
+ test -z "$libdir" && \
+ dlpreconveniencelibs="$dlpreconveniencelibs $dir/$old_library"
+ # Otherwise, use the dlname, so that lt_dlopen finds it.
+ elif test -n "$dlname"; then
+ newdlprefiles="$newdlprefiles $dir/$dlname"
+ else
+ newdlprefiles="$newdlprefiles $dir/$linklib"
+ fi
+ fi # $pass = dlpreopen
+
+ if test -z "$libdir"; then
+ # Link the convenience library
+ if test "$linkmode" = lib; then
+ deplibs="$dir/$old_library $deplibs"
+ elif test "$linkmode,$pass" = "prog,link"; then
+ compile_deplibs="$dir/$old_library $compile_deplibs"
+ finalize_deplibs="$dir/$old_library $finalize_deplibs"
+ else
+ deplibs="$lib $deplibs" # used for prog,scan pass
+ fi
+ continue
+ fi
+
+
+ if test "$linkmode" = prog && test "$pass" != link; then
+ newlib_search_path="$newlib_search_path $ladir"
+ deplibs="$lib $deplibs"
+
+ linkalldeplibs=no
+ if test "$link_all_deplibs" != no || test -z "$library_names" ||
+ test "$build_libtool_libs" = no; then
+ linkalldeplibs=yes
+ fi
+
+ tmp_libs=
+ for deplib in $dependency_libs; do
+ case $deplib in
+ -L*) func_stripname '-L' '' "$deplib"
+ newlib_search_path="$newlib_search_path $func_stripname_result"
+ ;;
+ esac
+ # Need to link against all dependency_libs?
+ if test "$linkalldeplibs" = yes; then
+ deplibs="$deplib $deplibs"
+ else
+ # Need to hardcode shared library paths
+ # or/and link against static libraries
+ newdependency_libs="$deplib $newdependency_libs"
+ fi
+ if $opt_duplicate_deps ; then
+ case "$tmp_libs " in
+ *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;;
+ esac
+ fi
+ tmp_libs="$tmp_libs $deplib"
+ done # for deplib
+ continue
+ fi # $linkmode = prog...
+
+ if test "$linkmode,$pass" = "prog,link"; then
+ if test -n "$library_names" &&
+ { { test "$prefer_static_libs" = no ||
+ test "$prefer_static_libs,$installed" = "built,yes"; } ||
+ test -z "$old_library"; }; then
+ # We need to hardcode the library path
+ if test -n "$shlibpath_var" && test -z "$avoidtemprpath" ; then
+ # Make sure the rpath contains only unique directories.
+ case "$temp_rpath:" in
+ *"$absdir:"*) ;;
+ *) temp_rpath="$temp_rpath$absdir:" ;;
+ esac
+ fi
+
+ # Hardcode the library path.
+ # Skip directories that are in the system default run-time
+ # search path.
+ case " $sys_lib_dlsearch_path " in
+ *" $absdir "*) ;;
+ *)
+ case "$compile_rpath " in
+ *" $absdir "*) ;;
+ *) compile_rpath="$compile_rpath $absdir"
+ esac
+ ;;
+ esac
+ case " $sys_lib_dlsearch_path " in
+ *" $libdir "*) ;;
+ *)
+ case "$finalize_rpath " in
+ *" $libdir "*) ;;
+ *) finalize_rpath="$finalize_rpath $libdir"
+ esac
+ ;;
+ esac
+ fi # $linkmode,$pass = prog,link...
+
+ if test "$alldeplibs" = yes &&
+ { test "$deplibs_check_method" = pass_all ||
+ { test "$build_libtool_libs" = yes &&
+ test -n "$library_names"; }; }; then
+ # We only need to search for static libraries
+ continue
+ fi
+ fi
+
+ link_static=no # Whether the deplib will be linked statically
+ use_static_libs=$prefer_static_libs
+ if test "$use_static_libs" = built && test "$installed" = yes; then
+ use_static_libs=no
+ fi
+ if test -n "$library_names" &&
+ { test "$use_static_libs" = no || test -z "$old_library"; }; then
+ case $host in
+ *cygwin* | *mingw* | *cegcc*)
+ # No point in relinking DLLs because paths are not encoded
+ notinst_deplibs="$notinst_deplibs $lib"
+ need_relink=no
+ ;;
+ *)
+ if test "$installed" = no; then
+ notinst_deplibs="$notinst_deplibs $lib"
+ need_relink=yes
+ fi
+ ;;
+ esac
+ # This is a shared library
+
+ # Warn about portability, can't link against -module's on some
+ # systems (darwin). Don't bleat about dlopened modules though!
+ dlopenmodule=""
+ for dlpremoduletest in $dlprefiles; do
+ if test "X$dlpremoduletest" = "X$lib"; then
+ dlopenmodule="$dlpremoduletest"
+ break
+ fi
+ done
+ if test -z "$dlopenmodule" && test "$shouldnotlink" = yes && test "$pass" = link; then
+ $ECHO
+ if test "$linkmode" = prog; then
+ $ECHO "*** Warning: Linking the executable $output against the loadable module"
+ else
+ $ECHO "*** Warning: Linking the shared library $output against the loadable module"
+ fi
+ $ECHO "*** $linklib is not portable!"
+ fi
+ if test "$linkmode" = lib &&
+ test "$hardcode_into_libs" = yes; then
+ # Hardcode the library path.
+ # Skip directories that are in the system default run-time
+ # search path.
+ case " $sys_lib_dlsearch_path " in
+ *" $absdir "*) ;;
+ *)
+ case "$compile_rpath " in
+ *" $absdir "*) ;;
+ *) compile_rpath="$compile_rpath $absdir"
+ esac
+ ;;
+ esac
+ case " $sys_lib_dlsearch_path " in
+ *" $libdir "*) ;;
+ *)
+ case "$finalize_rpath " in
+ *" $libdir "*) ;;
+ *) finalize_rpath="$finalize_rpath $libdir"
+ esac
+ ;;
+ esac
+ fi
+
+ if test -n "$old_archive_from_expsyms_cmds"; then
+ # figure out the soname
+ set dummy $library_names
+ shift
+ realname="$1"
+ shift
+ libname=`eval "\\$ECHO \"$libname_spec\""`
+ # use dlname if we got it. it's perfectly good, no?
+ if test -n "$dlname"; then
+ soname="$dlname"
+ elif test -n "$soname_spec"; then
+ # bleh windows
+ case $host in
+ *cygwin* | mingw* | *cegcc*)
+ func_arith $current - $age
+ major=$func_arith_result
+ versuffix="-$major"
+ ;;
+ esac
+ eval soname=\"$soname_spec\"
+ else
+ soname="$realname"
+ fi
+
+ # Make a new name for the extract_expsyms_cmds to use
+ soroot="$soname"
+ func_basename "$soroot"
+ soname="$func_basename_result"
+ func_stripname 'lib' '.dll' "$soname"
+ newlib=libimp-$func_stripname_result.a
+
+ # If the library has no export list, then create one now
+ if test -f "$output_objdir/$soname-def"; then :
+ else
+ func_verbose "extracting exported symbol list from \`$soname'"
+ func_execute_cmds "$extract_expsyms_cmds" 'exit $?'
+ fi
+
+ # Create $newlib
+ if test -f "$output_objdir/$newlib"; then :; else
+ func_verbose "generating import library for \`$soname'"
+ func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?'
+ fi
+ # make sure the library variables are pointing to the new library
+ dir=$output_objdir
+ linklib=$newlib
+ fi # test -n "$old_archive_from_expsyms_cmds"
+
+ if test "$linkmode" = prog || test "$mode" != relink; then
+ add_shlibpath=
+ add_dir=
+ add=
+ lib_linked=yes
+ case $hardcode_action in
+ immediate | unsupported)
+ if test "$hardcode_direct" = no; then
+ add="$dir/$linklib"
+ case $host in
+ *-*-sco3.2v5.0.[024]*) add_dir="-L$dir" ;;
+ *-*-sysv4*uw2*) add_dir="-L$dir" ;;
+ *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \
+ *-*-unixware7*) add_dir="-L$dir" ;;
+ *-*-darwin* )
+ # if the lib is a (non-dlopened) module then we can not
+ # link against it, someone is ignoring the earlier warnings
+ if /usr/bin/file -L $add 2> /dev/null |
+ $GREP ": [^:]* bundle" >/dev/null ; then
+ if test "X$dlopenmodule" != "X$lib"; then
+ $ECHO "*** Warning: lib $linklib is a module, not a shared library"
+ if test -z "$old_library" ; then
+ $ECHO
+ $ECHO "*** And there doesn't seem to be a static archive available"
+ $ECHO "*** The link will probably fail, sorry"
+ else
+ add="$dir/$old_library"
+ fi
+ elif test -n "$old_library"; then
+ add="$dir/$old_library"
+ fi
+ fi
+ esac
+ elif test "$hardcode_minus_L" = no; then
+ case $host in
+ *-*-sunos*) add_shlibpath="$dir" ;;
+ esac
+ add_dir="-L$dir"
+ add="-l$name"
+ elif test "$hardcode_shlibpath_var" = no; then
+ add_shlibpath="$dir"
+ add="-l$name"
+ else
+ lib_linked=no
+ fi
+ ;;
+ relink)
+ if test "$hardcode_direct" = yes &&
+ test "$hardcode_direct_absolute" = no; then
+ add="$dir/$linklib"
+ elif test "$hardcode_minus_L" = yes; then
+ add_dir="-L$dir"
+ # Try looking first in the location we're being installed to.
+ if test -n "$inst_prefix_dir"; then
+ case $libdir in
+ [\\/]*)
+ add_dir="$add_dir -L$inst_prefix_dir$libdir"
+ ;;
+ esac
+ fi
+ add="-l$name"
+ elif test "$hardcode_shlibpath_var" = yes; then
+ add_shlibpath="$dir"
+ add="-l$name"
+ else
+ lib_linked=no
+ fi
+ ;;
+ *) lib_linked=no ;;
+ esac
+
+ if test "$lib_linked" != yes; then
+ func_fatal_configuration "unsupported hardcode properties"
+ fi
+
+ if test -n "$add_shlibpath"; then
+ case :$compile_shlibpath: in
+ *":$add_shlibpath:"*) ;;
+ *) compile_shlibpath="$compile_shlibpath$add_shlibpath:" ;;
+ esac
+ fi
+ if test "$linkmode" = prog; then
+ test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs"
+ test -n "$add" && compile_deplibs="$add $compile_deplibs"
+ else
+ test -n "$add_dir" && deplibs="$add_dir $deplibs"
+ test -n "$add" && deplibs="$add $deplibs"
+ if test "$hardcode_direct" != yes &&
+ test "$hardcode_minus_L" != yes &&
+ test "$hardcode_shlibpath_var" = yes; then
+ case :$finalize_shlibpath: in
+ *":$libdir:"*) ;;
+ *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;;
+ esac
+ fi
+ fi
+ fi
+
+ if test "$linkmode" = prog || test "$mode" = relink; then
+ add_shlibpath=
+ add_dir=
+ add=
+ # Finalize command for both is simple: just hardcode it.
+ if test "$hardcode_direct" = yes &&
+ test "$hardcode_direct_absolute" = no; then
+ add="$libdir/$linklib"
+ elif test "$hardcode_minus_L" = yes; then
+ add_dir="-L$libdir"
+ add="-l$name"
+ elif test "$hardcode_shlibpath_var" = yes; then
+ case :$finalize_shlibpath: in
+ *":$libdir:"*) ;;
+ *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;;
+ esac
+ add="-l$name"
+ elif test "$hardcode_automatic" = yes; then
+ if test -n "$inst_prefix_dir" &&
+ test -f "$inst_prefix_dir$libdir/$linklib" ; then
+ add="$inst_prefix_dir$libdir/$linklib"
+ else
+ add="$libdir/$linklib"
+ fi
+ else
+ # We cannot seem to hardcode it, guess we'll fake it.
+ add_dir="-L$libdir"
+ # Try looking first in the location we're being installed to.
+ if test -n "$inst_prefix_dir"; then
+ case $libdir in
+ [\\/]*)
+ add_dir="$add_dir -L$inst_prefix_dir$libdir"
+ ;;
+ esac
+ fi
+ add="-l$name"
+ fi
+
+ if test "$linkmode" = prog; then
+ test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs"
+ test -n "$add" && finalize_deplibs="$add $finalize_deplibs"
+ else
+ test -n "$add_dir" && deplibs="$add_dir $deplibs"
+ test -n "$add" && deplibs="$add $deplibs"
+ fi
+ fi
+ elif test "$linkmode" = prog; then
+ # Here we assume that one of hardcode_direct or hardcode_minus_L
+ # is not unsupported. This is valid on all known static and
+ # shared platforms.
+ if test "$hardcode_direct" != unsupported; then
+ test -n "$old_library" && linklib="$old_library"
+ compile_deplibs="$dir/$linklib $compile_deplibs"
+ finalize_deplibs="$dir/$linklib $finalize_deplibs"
+ else
+ compile_deplibs="-l$name -L$dir $compile_deplibs"
+ finalize_deplibs="-l$name -L$dir $finalize_deplibs"
+ fi
+ elif test "$build_libtool_libs" = yes; then
+ # Not a shared library
+ if test "$deplibs_check_method" != pass_all; then
+ # We're trying link a shared library against a static one
+ # but the system doesn't support it.
+
+ # Just print a warning and add the library to dependency_libs so
+ # that the program can be linked against the static library.
+ $ECHO
+ $ECHO "*** Warning: This system can not link to static lib archive $lib."
+ $ECHO "*** I have the capability to make that library automatically link in when"
+ $ECHO "*** you link to this library. But I can only do this if you have a"
+ $ECHO "*** shared version of the library, which you do not appear to have."
+ if test "$module" = yes; then
+ $ECHO "*** But as you try to build a module library, libtool will still create "
+ $ECHO "*** a static module, that should work as long as the dlopening application"
+ $ECHO "*** is linked with the -dlopen flag to resolve symbols at runtime."
+ if test -z "$global_symbol_pipe"; then
+ $ECHO
+ $ECHO "*** However, this would only work if libtool was able to extract symbol"
+ $ECHO "*** lists from a program, using \`nm' or equivalent, but libtool could"
+ $ECHO "*** not find such a program. So, this module is probably useless."
+ $ECHO "*** \`nm' from GNU binutils and a full rebuild may help."
+ fi
+ if test "$build_old_libs" = no; then
+ build_libtool_libs=module
+ build_old_libs=yes
+ else
+ build_libtool_libs=no
+ fi
+ fi
+ else
+ deplibs="$dir/$old_library $deplibs"
+ link_static=yes
+ fi
+ fi # link shared/static library?
+
+ if test "$linkmode" = lib; then
+ if test -n "$dependency_libs" &&
+ { test "$hardcode_into_libs" != yes ||
+ test "$build_old_libs" = yes ||
+ test "$link_static" = yes; }; then
+ # Extract -R from dependency_libs
+ temp_deplibs=
+ for libdir in $dependency_libs; do
+ case $libdir in
+ -R*) func_stripname '-R' '' "$libdir"
+ temp_xrpath=$func_stripname_result
+ case " $xrpath " in
+ *" $temp_xrpath "*) ;;
+ *) xrpath="$xrpath $temp_xrpath";;
+ esac;;
+ *) temp_deplibs="$temp_deplibs $libdir";;
+ esac
+ done
+ dependency_libs="$temp_deplibs"
+ fi
+
+ newlib_search_path="$newlib_search_path $absdir"
+ # Link against this library
+ test "$link_static" = no && newdependency_libs="$abs_ladir/$laname $newdependency_libs"
+ # ... and its dependency_libs
+ tmp_libs=
+ for deplib in $dependency_libs; do
+ newdependency_libs="$deplib $newdependency_libs"
+ if $opt_duplicate_deps ; then
+ case "$tmp_libs " in
+ *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;;
+ esac
+ fi
+ tmp_libs="$tmp_libs $deplib"
+ done
+
+ if test "$link_all_deplibs" != no; then
+ # Add the search paths of all dependency libraries
+ for deplib in $dependency_libs; do
+ case $deplib in
+ -L*) path="$deplib" ;;
+ *.la)
+ func_dirname "$deplib" "" "."
+ dir="$func_dirname_result"
+ # We need an absolute path.
+ case $dir in
+ [\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;;
+ *)
+ absdir=`cd "$dir" && pwd`
+ if test -z "$absdir"; then
+ func_warning "cannot determine absolute directory name of \`$dir'"
+ absdir="$dir"
+ fi
+ ;;
+ esac
+ if $GREP "^installed=no" $deplib > /dev/null; then
+ case $host in
+ *-*-darwin*)
+ depdepl=
+ eval deplibrary_names=`${SED} -n -e 's/^library_names=\(.*\)$/\1/p' $deplib`
+ if test -n "$deplibrary_names" ; then
+ for tmp in $deplibrary_names ; do
+ depdepl=$tmp
+ done
+ if test -f "$absdir/$objdir/$depdepl" ; then
+ depdepl="$absdir/$objdir/$depdepl"
+ darwin_install_name=`${OTOOL} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'`
+ if test -z "$darwin_install_name"; then
+ darwin_install_name=`${OTOOL64} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'`
+ fi
+ compiler_flags="$compiler_flags ${wl}-dylib_file ${wl}${darwin_install_name}:${depdepl}"
+ linker_flags="$linker_flags -dylib_file ${darwin_install_name}:${depdepl}"
+ path=
+ fi
+ fi
+ ;;
+ *)
+ path="-L$absdir/$objdir"
+ ;;
+ esac
+ else
+ eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib`
+ test -z "$libdir" && \
+ func_fatal_error "\`$deplib' is not a valid libtool archive"
+ test "$absdir" != "$libdir" && \
+ func_warning "\`$deplib' seems to be moved"
+
+ path="-L$absdir"
+ fi
+ ;;
+ esac
+ case " $deplibs " in
+ *" $path "*) ;;
+ *) deplibs="$path $deplibs" ;;
+ esac
+ done
+ fi # link_all_deplibs != no
+ fi # linkmode = lib
+ done # for deplib in $libs
+ if test "$pass" = link; then
+ if test "$linkmode" = "prog"; then
+ compile_deplibs="$new_inherited_linker_flags $compile_deplibs"
+ finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs"
+ else
+ compiler_flags="$compiler_flags "`$ECHO "X $new_inherited_linker_flags" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'`
+ fi
+ fi
+ dependency_libs="$newdependency_libs"
+ if test "$pass" = dlpreopen; then
+ # Link the dlpreopened libraries before other libraries
+ for deplib in $save_deplibs; do
+ deplibs="$deplib $deplibs"
+ done
+ fi
+ if test "$pass" != dlopen; then
+ if test "$pass" != conv; then
+ # Make sure lib_search_path contains only unique directories.
+ lib_search_path=
+ for dir in $newlib_search_path; do
+ case "$lib_search_path " in
+ *" $dir "*) ;;
+ *) lib_search_path="$lib_search_path $dir" ;;
+ esac
+ done
+ newlib_search_path=
+ fi
+
+ if test "$linkmode,$pass" != "prog,link"; then
+ vars="deplibs"
+ else
+ vars="compile_deplibs finalize_deplibs"
+ fi
+ for var in $vars dependency_libs; do
+ # Add libraries to $var in reverse order
+ eval tmp_libs=\"\$$var\"
+ new_libs=
+ for deplib in $tmp_libs; do
+ # FIXME: Pedantically, this is the right thing to do, so
+ # that some nasty dependency loop isn't accidentally
+ # broken:
+ #new_libs="$deplib $new_libs"
+ # Pragmatically, this seems to cause very few problems in
+ # practice:
+ case $deplib in
+ -L*) new_libs="$deplib $new_libs" ;;
+ -R*) ;;
+ *)
+ # And here is the reason: when a library appears more
+ # than once as an explicit dependence of a library, or
+ # is implicitly linked in more than once by the
+ # compiler, it is considered special, and multiple
+ # occurrences thereof are not removed. Compare this
+ # with having the same library being listed as a
+ # dependency of multiple other libraries: in this case,
+ # we know (pedantically, we assume) the library does not
+ # need to be listed more than once, so we keep only the
+ # last copy. This is not always right, but it is rare
+ # enough that we require users that really mean to play
+ # such unportable linking tricks to link the library
+ # using -Wl,-lname, so that libtool does not consider it
+ # for duplicate removal.
+ case " $specialdeplibs " in
+ *" $deplib "*) new_libs="$deplib $new_libs" ;;
+ *)
+ case " $new_libs " in
+ *" $deplib "*) ;;
+ *) new_libs="$deplib $new_libs" ;;
+ esac
+ ;;
+ esac
+ ;;
+ esac
+ done
+ tmp_libs=
+ for deplib in $new_libs; do
+ case $deplib in
+ -L*)
+ case " $tmp_libs " in
+ *" $deplib "*) ;;
+ *) tmp_libs="$tmp_libs $deplib" ;;
+ esac
+ ;;
+ *) tmp_libs="$tmp_libs $deplib" ;;
+ esac
+ done
+ eval $var=\"$tmp_libs\"
+ done # for var
+ fi
+ # Last step: remove runtime libs from dependency_libs
+ # (they stay in deplibs)
+ tmp_libs=
+ for i in $dependency_libs ; do
+ case " $predeps $postdeps $compiler_lib_search_path " in
+ *" $i "*)
+ i=""
+ ;;
+ esac
+ if test -n "$i" ; then
+ tmp_libs="$tmp_libs $i"
+ fi
+ done
+ dependency_libs=$tmp_libs
+ done # for pass
+ if test "$linkmode" = prog; then
+ dlfiles="$newdlfiles"
+ fi
+ if test "$linkmode" = prog || test "$linkmode" = lib; then
+ dlprefiles="$newdlprefiles"
+ fi
+
+ case $linkmode in
+ oldlib)
+ if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
+ func_warning "\`-dlopen' is ignored for archives"
+ fi
+
+ case " $deplibs" in
+ *\ -l* | *\ -L*)
+ func_warning "\`-l' and \`-L' are ignored for archives" ;;
+ esac
+
+ test -n "$rpath" && \
+ func_warning "\`-rpath' is ignored for archives"
+
+ test -n "$xrpath" && \
+ func_warning "\`-R' is ignored for archives"
+
+ test -n "$vinfo" && \
+ func_warning "\`-version-info/-version-number' is ignored for archives"
+
+ test -n "$release" && \
+ func_warning "\`-release' is ignored for archives"
+
+ test -n "$export_symbols$export_symbols_regex" && \
+ func_warning "\`-export-symbols' is ignored for archives"
+
+ # Now set the variables for building old libraries.
+ build_libtool_libs=no
+ oldlibs="$output"
+ objs="$objs$old_deplibs"
+ ;;
+
+ lib)
+ # Make sure we only generate libraries of the form `libNAME.la'.
+ case $outputname in
+ lib*)
+ func_stripname 'lib' '.la' "$outputname"
+ name=$func_stripname_result
+ eval shared_ext=\"$shrext_cmds\"
+ eval libname=\"$libname_spec\"
+ ;;
+ *)
+ test "$module" = no && \
+ func_fatal_help "libtool library \`$output' must begin with \`lib'"
+
+ if test "$need_lib_prefix" != no; then
+ # Add the "lib" prefix for modules if required
+ func_stripname '' '.la' "$outputname"
+ name=$func_stripname_result
+ eval shared_ext=\"$shrext_cmds\"
+ eval libname=\"$libname_spec\"
+ else
+ func_stripname '' '.la' "$outputname"
+ libname=$func_stripname_result
+ fi
+ ;;
+ esac
+
+ if test -n "$objs"; then
+ if test "$deplibs_check_method" != pass_all; then
+ func_fatal_error "cannot build libtool library \`$output' from non-libtool objects on this host:$objs"
+ else
+ $ECHO
+ $ECHO "*** Warning: Linking the shared library $output against the non-libtool"
+ $ECHO "*** objects $objs is not portable!"
+ libobjs="$libobjs $objs"
+ fi
+ fi
+
+ test "$dlself" != no && \
+ func_warning "\`-dlopen self' is ignored for libtool libraries"
+
+ set dummy $rpath
+ shift
+ test "$#" -gt 1 && \
+ func_warning "ignoring multiple \`-rpath's for a libtool library"
+
+ install_libdir="$1"
+
+ oldlibs=
+ if test -z "$rpath"; then
+ if test "$build_libtool_libs" = yes; then
+ # Building a libtool convenience library.
+ # Some compilers have problems with a `.al' extension so
+ # convenience libraries should have the same extension an
+ # archive normally would.
+ oldlibs="$output_objdir/$libname.$libext $oldlibs"
+ build_libtool_libs=convenience
+ build_old_libs=yes
+ fi
+
+ test -n "$vinfo" && \
+ func_warning "\`-version-info/-version-number' is ignored for convenience libraries"
+
+ test -n "$release" && \
+ func_warning "\`-release' is ignored for convenience libraries"
+ else
+
+ # Parse the version information argument.
+ save_ifs="$IFS"; IFS=':'
+ set dummy $vinfo 0 0 0
+ shift
+ IFS="$save_ifs"
+
+ test -n "$7" && \
+ func_fatal_help "too many parameters to \`-version-info'"
+
+ # convert absolute version numbers to libtool ages
+ # this retains compatibility with .la files and attempts
+ # to make the code below a bit more comprehensible
+
+ case $vinfo_number in
+ yes)
+ number_major="$1"
+ number_minor="$2"
+ number_revision="$3"
+ #
+ # There are really only two kinds -- those that
+ # use the current revision as the major version
+ # and those that subtract age and use age as
+ # a minor version. But, then there is irix
+ # which has an extra 1 added just for fun
+ #
+ case $version_type in
+ darwin|linux|osf|windows|none)
+ func_arith $number_major + $number_minor
+ current=$func_arith_result
+ age="$number_minor"
+ revision="$number_revision"
+ ;;
+ freebsd-aout|freebsd-elf|sunos)
+ current="$number_major"
+ revision="$number_minor"
+ age="0"
+ ;;
+ irix|nonstopux)
+ func_arith $number_major + $number_minor
+ current=$func_arith_result
+ age="$number_minor"
+ revision="$number_minor"
+ lt_irix_increment=no
+ ;;
+ esac
+ ;;
+ no)
+ current="$1"
+ revision="$2"
+ age="$3"
+ ;;
+ esac
+
+ # Check that each of the things are valid numbers.
+ case $current in
+ 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
+ *)
+ func_error "CURRENT \`$current' must be a nonnegative integer"
+ func_fatal_error "\`$vinfo' is not valid version information"
+ ;;
+ esac
+
+ case $revision in
+ 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
+ *)
+ func_error "REVISION \`$revision' must be a nonnegative integer"
+ func_fatal_error "\`$vinfo' is not valid version information"
+ ;;
+ esac
+
+ case $age in
+ 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
+ *)
+ func_error "AGE \`$age' must be a nonnegative integer"
+ func_fatal_error "\`$vinfo' is not valid version information"
+ ;;
+ esac
+
+ if test "$age" -gt "$current"; then
+ func_error "AGE \`$age' is greater than the current interface number \`$current'"
+ func_fatal_error "\`$vinfo' is not valid version information"
+ fi
+
+ # Calculate the version variables.
+ major=
+ versuffix=
+ verstring=
+ case $version_type in
+ none) ;;
+
+ darwin)
+ # Like Linux, but with the current version available in
+ # verstring for coding it into the library header
+ func_arith $current - $age
+ major=.$func_arith_result
+ versuffix="$major.$age.$revision"
+ # Darwin ld doesn't like 0 for these options...
+ func_arith $current + 1
+ minor_current=$func_arith_result
+ xlcverstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision"
+ verstring="-compatibility_version $minor_current -current_version $minor_current.$revision"
+ ;;
+
+ freebsd-aout)
+ major=".$current"
+ versuffix=".$current.$revision";
+ ;;
+
+ freebsd-elf)
+ major=".$current"
+ versuffix=".$current"
+ ;;
+
+ irix | nonstopux)
+ if test "X$lt_irix_increment" = "Xno"; then
+ func_arith $current - $age
+ else
+ func_arith $current - $age + 1
+ fi
+ major=$func_arith_result
+
+ case $version_type in
+ nonstopux) verstring_prefix=nonstopux ;;
+ *) verstring_prefix=sgi ;;
+ esac
+ verstring="$verstring_prefix$major.$revision"
+
+ # Add in all the interfaces that we are compatible with.
+ loop=$revision
+ while test "$loop" -ne 0; do
+ func_arith $revision - $loop
+ iface=$func_arith_result
+ func_arith $loop - 1
+ loop=$func_arith_result
+ verstring="$verstring_prefix$major.$iface:$verstring"
+ done
+
+ # Before this point, $major must not contain `.'.
+ major=.$major
+ versuffix="$major.$revision"
+ ;;
+
+ linux)
+ func_arith $current - $age
+ major=.$func_arith_result
+ versuffix="$major.$age.$revision"
+ ;;
+
+ osf)
+ func_arith $current - $age
+ major=.$func_arith_result
+ versuffix=".$current.$age.$revision"
+ verstring="$current.$age.$revision"
+
+ # Add in all the interfaces that we are compatible with.
+ loop=$age
+ while test "$loop" -ne 0; do
+ func_arith $current - $loop
+ iface=$func_arith_result
+ func_arith $loop - 1
+ loop=$func_arith_result
+ verstring="$verstring:${iface}.0"
+ done
+
+ # Make executables depend on our current version.
+ verstring="$verstring:${current}.0"
+ ;;
+
+ qnx)
+ major=".$current"
+ versuffix=".$current"
+ ;;
+
+ sunos)
+ major=".$current"
+ versuffix=".$current.$revision"
+ ;;
+
+ windows)
+ # Use '-' rather than '.', since we only want one
+ # extension on DOS 8.3 filesystems.
+ func_arith $current - $age
+ major=$func_arith_result
+ versuffix="-$major"
+ ;;
+
+ *)
+ func_fatal_configuration "unknown library version type \`$version_type'"
+ ;;
+ esac
+
+ # Clear the version info if we defaulted, and they specified a release.
+ if test -z "$vinfo" && test -n "$release"; then
+ major=
+ case $version_type in
+ darwin)
+ # we can't check for "0.0" in archive_cmds due to quoting
+ # problems, so we reset it completely
+ verstring=
+ ;;
+ *)
+ verstring="0.0"
+ ;;
+ esac
+ if test "$need_version" = no; then
+ versuffix=
+ else
+ versuffix=".0.0"
+ fi
+ fi
+
+ # Remove version info from name if versioning should be avoided
+ if test "$avoid_version" = yes && test "$need_version" = no; then
+ major=
+ versuffix=
+ verstring=""
+ fi
+
+ # Check to see if the archive will have undefined symbols.
+ if test "$allow_undefined" = yes; then
+ if test "$allow_undefined_flag" = unsupported; then
+ func_warning "undefined symbols not allowed in $host shared libraries"
+ build_libtool_libs=no
+ build_old_libs=yes
+ fi
+ else
+ # Don't allow undefined symbols.
+ allow_undefined_flag="$no_undefined_flag"
+ fi
+
+ fi
+
+ func_generate_dlsyms "$libname" "$libname" "yes"
+ libobjs="$libobjs $symfileobj"
+ test "X$libobjs" = "X " && libobjs=
+
+ if test "$mode" != relink; then
+ # Remove our outputs, but don't remove object files since they
+ # may have been created when compiling PIC objects.
+ removelist=
+ tempremovelist=`$ECHO "$output_objdir/*"`
+ for p in $tempremovelist; do
+ case $p in
+ *.$objext | *.gcno)
+ ;;
+ $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/${libname}${release}.*)
+ if test "X$precious_files_regex" != "X"; then
+ if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1
+ then
+ continue
+ fi
+ fi
+ removelist="$removelist $p"
+ ;;
+ *) ;;
+ esac
+ done
+ test -n "$removelist" && \
+ func_show_eval "${RM}r \$removelist"
+ fi
+
+ # Now set the variables for building old libraries.
+ if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then
+ oldlibs="$oldlibs $output_objdir/$libname.$libext"
+
+ # Transform .lo files to .o files.
+ oldobjs="$objs "`$ECHO "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}'$/d' -e "$lo2o" | $NL2SP`
+ fi
+
+ # Eliminate all temporary directories.
+ #for path in $notinst_path; do
+ # lib_search_path=`$ECHO "X$lib_search_path " | $Xsed -e "s% $path % %g"`
+ # deplibs=`$ECHO "X$deplibs " | $Xsed -e "s% -L$path % %g"`
+ # dependency_libs=`$ECHO "X$dependency_libs " | $Xsed -e "s% -L$path % %g"`
+ #done
+
+ if test -n "$xrpath"; then
+ # If the user specified any rpath flags, then add them.
+ temp_xrpath=
+ for libdir in $xrpath; do
+ temp_xrpath="$temp_xrpath -R$libdir"
+ case "$finalize_rpath " in
+ *" $libdir "*) ;;
+ *) finalize_rpath="$finalize_rpath $libdir" ;;
+ esac
+ done
+ if test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes; then
+ dependency_libs="$temp_xrpath $dependency_libs"
+ fi
+ fi
+
+ # Make sure dlfiles contains only unique files that won't be dlpreopened
+ old_dlfiles="$dlfiles"
+ dlfiles=
+ for lib in $old_dlfiles; do
+ case " $dlprefiles $dlfiles " in
+ *" $lib "*) ;;
+ *) dlfiles="$dlfiles $lib" ;;
+ esac
+ done
+
+ # Make sure dlprefiles contains only unique files
+ old_dlprefiles="$dlprefiles"
+ dlprefiles=
+ for lib in $old_dlprefiles; do
+ case "$dlprefiles " in
+ *" $lib "*) ;;
+ *) dlprefiles="$dlprefiles $lib" ;;
+ esac
+ done
+
+ if test "$build_libtool_libs" = yes; then
+ if test -n "$rpath"; then
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc*)
+ # these systems don't actually have a c library (as such)!
+ ;;
+ *-*-rhapsody* | *-*-darwin1.[012])
+ # Rhapsody C library is in the System framework
+ deplibs="$deplibs System.ltframework"
+ ;;
+ *-*-netbsd*)
+ # Don't link with libc until the a.out ld.so is fixed.
+ ;;
+ *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*)
+ # Do not include libc due to us having libc/libc_r.
+ ;;
+ *-*-sco3.2v5* | *-*-sco5v6*)
+ # Causes problems with __ctype
+ ;;
+ *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*)
+ # Compiler inserts libc in the correct place for threads to work
+ ;;
+ *)
+ # Add libc to deplibs on all other systems if necessary.
+ if test "$build_libtool_need_lc" = "yes"; then
+ deplibs="$deplibs -lc"
+ fi
+ ;;
+ esac
+ fi
+
+ # Transform deplibs into only deplibs that can be linked in shared.
+ name_save=$name
+ libname_save=$libname
+ release_save=$release
+ versuffix_save=$versuffix
+ major_save=$major
+ # I'm not sure if I'm treating the release correctly. I think
+ # release should show up in the -l (ie -lgmp5) so we don't want to
+ # add it in twice. Is that correct?
+ release=""
+ versuffix=""
+ major=""
+ newdeplibs=
+ droppeddeps=no
+ case $deplibs_check_method in
+ pass_all)
+ # Don't check for shared/static. Everything works.
+ # This might be a little naive. We might want to check
+ # whether the library exists or not. But this is on
+ # osf3 & osf4 and I'm not really sure... Just
+ # implementing what was already the behavior.
+ newdeplibs=$deplibs
+ ;;
+ test_compile)
+ # This code stresses the "libraries are programs" paradigm to its
+ # limits. Maybe even breaks it. We compile a program, linking it
+ # against the deplibs as a proxy for the library. Then we can check
+ # whether they linked in statically or dynamically with ldd.
+ $opt_dry_run || $RM conftest.c
+ cat > conftest.c <<EOF
+ int main() { return 0; }
+EOF
+ $opt_dry_run || $RM conftest
+ if $LTCC $LTCFLAGS -o conftest conftest.c $deplibs; then
+ ldd_output=`ldd conftest`
+ for i in $deplibs; do
+ case $i in
+ -l*)
+ func_stripname -l '' "$i"
+ name=$func_stripname_result
+ if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+ case " $predeps $postdeps " in
+ *" $i "*)
+ newdeplibs="$newdeplibs $i"
+ i=""
+ ;;
+ esac
+ fi
+ if test -n "$i" ; then
+ libname=`eval "\\$ECHO \"$libname_spec\""`
+ deplib_matches=`eval "\\$ECHO \"$library_names_spec\""`
+ set dummy $deplib_matches; shift
+ deplib_match=$1
+ if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0 ; then
+ newdeplibs="$newdeplibs $i"
+ else
+ droppeddeps=yes
+ $ECHO
+ $ECHO "*** Warning: dynamic linker does not accept needed library $i."
+ $ECHO "*** I have the capability to make that library automatically link in when"
+ $ECHO "*** you link to this library. But I can only do this if you have a"
+ $ECHO "*** shared version of the library, which I believe you do not have"
+ $ECHO "*** because a test_compile did reveal that the linker did not use it for"
+ $ECHO "*** its dynamic dependency list that programs get resolved with at runtime."
+ fi
+ fi
+ ;;
+ *)
+ newdeplibs="$newdeplibs $i"
+ ;;
+ esac
+ done
+ else
+ # Error occurred in the first compile. Let's try to salvage
+ # the situation: Compile a separate program for each library.
+ for i in $deplibs; do
+ case $i in
+ -l*)
+ func_stripname -l '' "$i"
+ name=$func_stripname_result
+ $opt_dry_run || $RM conftest
+ if $LTCC $LTCFLAGS -o conftest conftest.c $i; then
+ ldd_output=`ldd conftest`
+ if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+ case " $predeps $postdeps " in
+ *" $i "*)
+ newdeplibs="$newdeplibs $i"
+ i=""
+ ;;
+ esac
+ fi
+ if test -n "$i" ; then
+ libname=`eval "\\$ECHO \"$libname_spec\""`
+ deplib_matches=`eval "\\$ECHO \"$library_names_spec\""`
+ set dummy $deplib_matches; shift
+ deplib_match=$1
+ if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0 ; then
+ newdeplibs="$newdeplibs $i"
+ else
+ droppeddeps=yes
+ $ECHO
+ $ECHO "*** Warning: dynamic linker does not accept needed library $i."
+ $ECHO "*** I have the capability to make that library automatically link in when"
+ $ECHO "*** you link to this library. But I can only do this if you have a"
+ $ECHO "*** shared version of the library, which you do not appear to have"
+ $ECHO "*** because a test_compile did reveal that the linker did not use this one"
+ $ECHO "*** as a dynamic dependency that programs can get resolved with at runtime."
+ fi
+ fi
+ else
+ droppeddeps=yes
+ $ECHO
+ $ECHO "*** Warning! Library $i is needed by this library but I was not able to"
+ $ECHO "*** make it link in! You will probably need to install it or some"
+ $ECHO "*** library that it depends on before this library will be fully"
+ $ECHO "*** functional. Installing it before continuing would be even better."
+ fi
+ ;;
+ *)
+ newdeplibs="$newdeplibs $i"
+ ;;
+ esac
+ done
+ fi
+ ;;
+ file_magic*)
+ set dummy $deplibs_check_method; shift
+ file_magic_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
+ for a_deplib in $deplibs; do
+ case $a_deplib in
+ -l*)
+ func_stripname -l '' "$a_deplib"
+ name=$func_stripname_result
+ if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+ case " $predeps $postdeps " in
+ *" $a_deplib "*)
+ newdeplibs="$newdeplibs $a_deplib"
+ a_deplib=""
+ ;;
+ esac
+ fi
+ if test -n "$a_deplib" ; then
+ libname=`eval "\\$ECHO \"$libname_spec\""`
+ for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do
+ potential_libs=`ls $i/$libname[.-]* 2>/dev/null`
+ for potent_lib in $potential_libs; do
+ # Follow soft links.
+ if ls -lLd "$potent_lib" 2>/dev/null |
+ $GREP " -> " >/dev/null; then
+ continue
+ fi
+ # The statement above tries to avoid entering an
+ # endless loop below, in case of cyclic links.
+ # We might still enter an endless loop, since a link
+ # loop can be closed while we follow links,
+ # but so what?
+ potlib="$potent_lib"
+ while test -h "$potlib" 2>/dev/null; do
+ potliblink=`ls -ld $potlib | ${SED} 's/.* -> //'`
+ case $potliblink in
+ [\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";;
+ *) potlib=`$ECHO "X$potlib" | $Xsed -e 's,[^/]*$,,'`"$potliblink";;
+ esac
+ done
+ if eval $file_magic_cmd \"\$potlib\" 2>/dev/null |
+ $SED -e 10q |
+ $EGREP "$file_magic_regex" > /dev/null; then
+ newdeplibs="$newdeplibs $a_deplib"
+ a_deplib=""
+ break 2
+ fi
+ done
+ done
+ fi
+ if test -n "$a_deplib" ; then
+ droppeddeps=yes
+ $ECHO
+ $ECHO "*** Warning: linker path does not have real file for library $a_deplib."
+ $ECHO "*** I have the capability to make that library automatically link in when"
+ $ECHO "*** you link to this library. But I can only do this if you have a"
+ $ECHO "*** shared version of the library, which you do not appear to have"
+ $ECHO "*** because I did check the linker path looking for a file starting"
+ if test -z "$potlib" ; then
+ $ECHO "*** with $libname but no candidates were found. (...for file magic test)"
+ else
+ $ECHO "*** with $libname and none of the candidates passed a file format test"
+ $ECHO "*** using a file magic. Last file checked: $potlib"
+ fi
+ fi
+ ;;
+ *)
+ # Add a -L argument.
+ newdeplibs="$newdeplibs $a_deplib"
+ ;;
+ esac
+ done # Gone through all deplibs.
+ ;;
+ match_pattern*)
+ set dummy $deplibs_check_method; shift
+ match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
+ for a_deplib in $deplibs; do
+ case $a_deplib in
+ -l*)
+ func_stripname -l '' "$a_deplib"
+ name=$func_stripname_result
+ if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+ case " $predeps $postdeps " in
+ *" $a_deplib "*)
+ newdeplibs="$newdeplibs $a_deplib"
+ a_deplib=""
+ ;;
+ esac
+ fi
+ if test -n "$a_deplib" ; then
+ libname=`eval "\\$ECHO \"$libname_spec\""`
+ for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do
+ potential_libs=`ls $i/$libname[.-]* 2>/dev/null`
+ for potent_lib in $potential_libs; do
+ potlib="$potent_lib" # see symlink-check above in file_magic test
+ if eval "\$ECHO \"X$potent_lib\"" 2>/dev/null | $Xsed -e 10q | \
+ $EGREP "$match_pattern_regex" > /dev/null; then
+ newdeplibs="$newdeplibs $a_deplib"
+ a_deplib=""
+ break 2
+ fi
+ done
+ done
+ fi
+ if test -n "$a_deplib" ; then
+ droppeddeps=yes
+ $ECHO
+ $ECHO "*** Warning: linker path does not have real file for library $a_deplib."
+ $ECHO "*** I have the capability to make that library automatically link in when"
+ $ECHO "*** you link to this library. But I can only do this if you have a"
+ $ECHO "*** shared version of the library, which you do not appear to have"
+ $ECHO "*** because I did check the linker path looking for a file starting"
+ if test -z "$potlib" ; then
+ $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)"
+ else
+ $ECHO "*** with $libname and none of the candidates passed a file format test"
+ $ECHO "*** using a regex pattern. Last file checked: $potlib"
+ fi
+ fi
+ ;;
+ *)
+ # Add a -L argument.
+ newdeplibs="$newdeplibs $a_deplib"
+ ;;
+ esac
+ done # Gone through all deplibs.
+ ;;
+ none | unknown | *)
+ newdeplibs=""
+ tmp_deplibs=`$ECHO "X $deplibs" | $Xsed \
+ -e 's/ -lc$//' -e 's/ -[LR][^ ]*//g'`
+ if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+ for i in $predeps $postdeps ; do
+ # can't use Xsed below, because $i might contain '/'
+ tmp_deplibs=`$ECHO "X $tmp_deplibs" | $Xsed -e "s,$i,,"`
+ done
+ fi
+ if $ECHO "X $tmp_deplibs" | $Xsed -e 's/[ ]//g' |
+ $GREP . >/dev/null; then
+ $ECHO
+ if test "X$deplibs_check_method" = "Xnone"; then
+ $ECHO "*** Warning: inter-library dependencies are not supported in this platform."
+ else
+ $ECHO "*** Warning: inter-library dependencies are not known to be supported."
+ fi
+ $ECHO "*** All declared inter-library dependencies are being dropped."
+ droppeddeps=yes
+ fi
+ ;;
+ esac
+ versuffix=$versuffix_save
+ major=$major_save
+ release=$release_save
+ libname=$libname_save
+ name=$name_save
+
+ case $host in
+ *-*-rhapsody* | *-*-darwin1.[012])
+ # On Rhapsody replace the C library with the System framework
+ newdeplibs=`$ECHO "X $newdeplibs" | $Xsed -e 's/ -lc / System.ltframework /'`
+ ;;
+ esac
+
+ if test "$droppeddeps" = yes; then
+ if test "$module" = yes; then
+ $ECHO
+ $ECHO "*** Warning: libtool could not satisfy all declared inter-library"
+ $ECHO "*** dependencies of module $libname. Therefore, libtool will create"
+ $ECHO "*** a static module, that should work as long as the dlopening"
+ $ECHO "*** application is linked with the -dlopen flag."
+ if test -z "$global_symbol_pipe"; then
+ $ECHO
+ $ECHO "*** However, this would only work if libtool was able to extract symbol"
+ $ECHO "*** lists from a program, using \`nm' or equivalent, but libtool could"
+ $ECHO "*** not find such a program. So, this module is probably useless."
+ $ECHO "*** \`nm' from GNU binutils and a full rebuild may help."
+ fi
+ if test "$build_old_libs" = no; then
+ oldlibs="$output_objdir/$libname.$libext"
+ build_libtool_libs=module
+ build_old_libs=yes
+ else
+ build_libtool_libs=no
+ fi
+ else
+ $ECHO "*** The inter-library dependencies that have been dropped here will be"
+ $ECHO "*** automatically added whenever a program is linked with this library"
+ $ECHO "*** or is declared to -dlopen it."
+
+ if test "$allow_undefined" = no; then
+ $ECHO
+ $ECHO "*** Since this library must not contain undefined symbols,"
+ $ECHO "*** because either the platform does not support them or"
+ $ECHO "*** it was explicitly requested with -no-undefined,"
+ $ECHO "*** libtool will only create a static version of it."
+ if test "$build_old_libs" = no; then
+ oldlibs="$output_objdir/$libname.$libext"
+ build_libtool_libs=module
+ build_old_libs=yes
+ else
+ build_libtool_libs=no
+ fi
+ fi
+ fi
+ fi
+ # Done checking deplibs!
+ deplibs=$newdeplibs
+ fi
+ # Time to change all our "foo.ltframework" stuff back to "-framework foo"
+ case $host in
+ *-*-darwin*)
+ newdeplibs=`$ECHO "X $newdeplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'`
+ new_inherited_linker_flags=`$ECHO "X $new_inherited_linker_flags" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'`
+ deplibs=`$ECHO "X $deplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'`
+ ;;
+ esac
+
+ # move library search paths that coincide with paths to not yet
+ # installed libraries to the beginning of the library search list
+ new_libs=
+ for path in $notinst_path; do
+ case " $new_libs " in
+ *" -L$path/$objdir "*) ;;
+ *)
+ case " $deplibs " in
+ *" -L$path/$objdir "*)
+ new_libs="$new_libs -L$path/$objdir" ;;
+ esac
+ ;;
+ esac
+ done
+ for deplib in $deplibs; do
+ case $deplib in
+ -L*)
+ case " $new_libs " in
+ *" $deplib "*) ;;
+ *) new_libs="$new_libs $deplib" ;;
+ esac
+ ;;
+ *) new_libs="$new_libs $deplib" ;;
+ esac
+ done
+ deplibs="$new_libs"
+
+ # All the library-specific variables (install_libdir is set above).
+ library_names=
+ old_library=
+ dlname=
+
+ # Test again, we may have decided not to build it any more
+ if test "$build_libtool_libs" = yes; then
+ if test "$hardcode_into_libs" = yes; then
+ # Hardcode the library paths
+ hardcode_libdirs=
+ dep_rpath=
+ rpath="$finalize_rpath"
+ test "$mode" != relink && rpath="$compile_rpath$rpath"
+ for libdir in $rpath; do
+ if test -n "$hardcode_libdir_flag_spec"; then
+ if test -n "$hardcode_libdir_separator"; then
+ if test -z "$hardcode_libdirs"; then
+ hardcode_libdirs="$libdir"
+ else
+ # Just accumulate the unique libdirs.
+ case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+ *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+ ;;
+ *)
+ hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir"
+ ;;
+ esac
+ fi
+ else
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ dep_rpath="$dep_rpath $flag"
+ fi
+ elif test -n "$runpath_var"; then
+ case "$perm_rpath " in
+ *" $libdir "*) ;;
+ *) perm_rpath="$perm_rpath $libdir" ;;
+ esac
+ fi
+ done
+ # Substitute the hardcoded libdirs into the rpath.
+ if test -n "$hardcode_libdir_separator" &&
+ test -n "$hardcode_libdirs"; then
+ libdir="$hardcode_libdirs"
+ if test -n "$hardcode_libdir_flag_spec_ld"; then
+ eval dep_rpath=\"$hardcode_libdir_flag_spec_ld\"
+ else
+ eval dep_rpath=\"$hardcode_libdir_flag_spec\"
+ fi
+ fi
+ if test -n "$runpath_var" && test -n "$perm_rpath"; then
+ # We should set the runpath_var.
+ rpath=
+ for dir in $perm_rpath; do
+ rpath="$rpath$dir:"
+ done
+ eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var"
+ fi
+ test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs"
+ fi
+
+ shlibpath="$finalize_shlibpath"
+ test "$mode" != relink && shlibpath="$compile_shlibpath$shlibpath"
+ if test -n "$shlibpath"; then
+ eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var"
+ fi
+
+ # Get the real and link names of the library.
+ eval shared_ext=\"$shrext_cmds\"
+ eval library_names=\"$library_names_spec\"
+ set dummy $library_names
+ shift
+ realname="$1"
+ shift
+
+ if test -n "$soname_spec"; then
+ eval soname=\"$soname_spec\"
+ else
+ soname="$realname"
+ fi
+ if test -z "$dlname"; then
+ dlname=$soname
+ fi
+
+ lib="$output_objdir/$realname"
+ linknames=
+ for link
+ do
+ linknames="$linknames $link"
+ done
+
+ # Use standard objects if they are pic
+ test -z "$pic_flag" && libobjs=`$ECHO "X$libobjs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP`
+ test "X$libobjs" = "X " && libobjs=
+
+ delfiles=
+ if test -n "$export_symbols" && test -n "$include_expsyms"; then
+ $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp"
+ export_symbols="$output_objdir/$libname.uexp"
+ delfiles="$delfiles $export_symbols"
+ fi
+
+ orig_export_symbols=
+ case $host_os in
+ cygwin* | mingw* | cegcc*)
+ if test -n "$export_symbols" && test -z "$export_symbols_regex"; then
+ # exporting using user supplied symfile
+ if test "x`$SED 1q $export_symbols`" != xEXPORTS; then
+ # and it's NOT already a .def file. Must figure out
+ # which of the given symbols are data symbols and tag
+ # them as such. So, trigger use of export_symbols_cmds.
+ # export_symbols gets reassigned inside the "prepare
+ # the list of exported symbols" if statement, so the
+ # include_expsyms logic still works.
+ orig_export_symbols="$export_symbols"
+ export_symbols=
+ always_export_symbols=yes
+ fi
+ fi
+ ;;
+ esac
+
+ # Prepare the list of exported symbols
+ if test -z "$export_symbols"; then
+ if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then
+ func_verbose "generating symbol list for \`$libname.la'"
+ export_symbols="$output_objdir/$libname.exp"
+ $opt_dry_run || $RM $export_symbols
+ cmds=$export_symbols_cmds
+ save_ifs="$IFS"; IFS='~'
+ for cmd in $cmds; do
+ IFS="$save_ifs"
+ eval cmd=\"$cmd\"
+ func_len " $cmd"
+ len=$func_len_result
+ if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then
+ func_show_eval "$cmd" 'exit $?'
+ skipped_export=false
+ else
+ # The command line is too long to execute in one step.
+ func_verbose "using reloadable object file for export list..."
+ skipped_export=:
+ # Break out early, otherwise skipped_export may be
+ # set to false by a later but shorter cmd.
+ break
+ fi
+ done
+ IFS="$save_ifs"
+ if test -n "$export_symbols_regex" && test "X$skipped_export" != "X:"; then
+ func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"'
+ func_show_eval '$MV "${export_symbols}T" "$export_symbols"'
+ fi
+ fi
+ fi
+
+ if test -n "$export_symbols" && test -n "$include_expsyms"; then
+ tmp_export_symbols="$export_symbols"
+ test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols"
+ $opt_dry_run || eval '$ECHO "X$include_expsyms" | $Xsed | $SP2NL >> "$tmp_export_symbols"'
+ fi
+
+ if test "X$skipped_export" != "X:" && test -n "$orig_export_symbols"; then
+ # The given exports_symbols file has to be filtered, so filter it.
+ func_verbose "filter symbol list for \`$libname.la' to tag DATA exports"
+ # FIXME: $output_objdir/$libname.filter potentially contains lots of
+ # 's' commands which not all seds can handle. GNU sed should be fine
+ # though. Also, the filter scales superlinearly with the number of
+ # global variables. join(1) would be nice here, but unfortunately
+ # isn't a blessed tool.
+ $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter
+ delfiles="$delfiles $export_symbols $output_objdir/$libname.filter"
+ export_symbols=$output_objdir/$libname.def
+ $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols
+ fi
+
+ tmp_deplibs=
+ for test_deplib in $deplibs; do
+ case " $convenience " in
+ *" $test_deplib "*) ;;
+ *)
+ tmp_deplibs="$tmp_deplibs $test_deplib"
+ ;;
+ esac
+ done
+ deplibs="$tmp_deplibs"
+
+ if test -n "$convenience"; then
+ if test -n "$whole_archive_flag_spec" &&
+ test "$compiler_needs_object" = yes &&
+ test -z "$libobjs"; then
+ # extract the archives, so we have objects to list.
+ # TODO: could optimize this to just extract one archive.
+ whole_archive_flag_spec=
+ fi
+ if test -n "$whole_archive_flag_spec"; then
+ save_libobjs=$libobjs
+ eval libobjs=\"\$libobjs $whole_archive_flag_spec\"
+ test "X$libobjs" = "X " && libobjs=
+ else
+ gentop="$output_objdir/${outputname}x"
+ generated="$generated $gentop"
+
+ func_extract_archives $gentop $convenience
+ libobjs="$libobjs $func_extract_archives_result"
+ test "X$libobjs" = "X " && libobjs=
+ fi
+ fi
+
+ if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then
+ eval flag=\"$thread_safe_flag_spec\"
+ linker_flags="$linker_flags $flag"
+ fi
+
+ # Make a backup of the uninstalled library when relinking
+ if test "$mode" = relink; then
+ $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $?
+ fi
+
+ # Do each of the archive commands.
+ if test "$module" = yes && test -n "$module_cmds" ; then
+ if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then
+ eval test_cmds=\"$module_expsym_cmds\"
+ cmds=$module_expsym_cmds
+ else
+ eval test_cmds=\"$module_cmds\"
+ cmds=$module_cmds
+ fi
+ else
+ if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then
+ eval test_cmds=\"$archive_expsym_cmds\"
+ cmds=$archive_expsym_cmds
+ else
+ eval test_cmds=\"$archive_cmds\"
+ cmds=$archive_cmds
+ fi
+ fi
+
+ if test "X$skipped_export" != "X:" &&
+ func_len " $test_cmds" &&
+ len=$func_len_result &&
+ test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then
+ :
+ else
+ # The command line is too long to link in one step, link piecewise
+ # or, if using GNU ld and skipped_export is not :, use a linker
+ # script.
+
+ # Save the value of $output and $libobjs because we want to
+ # use them later. If we have whole_archive_flag_spec, we
+ # want to use save_libobjs as it was before
+ # whole_archive_flag_spec was expanded, because we can't
+ # assume the linker understands whole_archive_flag_spec.
+ # This may have to be revisited, in case too many
+ # convenience libraries get linked in and end up exceeding
+ # the spec.
+ if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then
+ save_libobjs=$libobjs
+ fi
+ save_output=$output
+ output_la=`$ECHO "X$output" | $Xsed -e "$basename"`
+
+ # Clear the reloadable object creation command queue and
+ # initialize k to one.
+ test_cmds=
+ concat_cmds=
+ objlist=
+ last_robj=
+ k=1
+
+ if test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "$with_gnu_ld" = yes; then
+ output=${output_objdir}/${output_la}.lnkscript
+ func_verbose "creating GNU ld script: $output"
+ $ECHO 'INPUT (' > $output
+ for obj in $save_libobjs
+ do
+ $ECHO "$obj" >> $output
+ done
+ $ECHO ')' >> $output
+ delfiles="$delfiles $output"
+ elif test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "X$file_list_spec" != X; then
+ output=${output_objdir}/${output_la}.lnk
+ func_verbose "creating linker input file list: $output"
+ : > $output
+ set x $save_libobjs
+ shift
+ firstobj=
+ if test "$compiler_needs_object" = yes; then
+ firstobj="$1 "
+ shift
+ fi
+ for obj
+ do
+ $ECHO "$obj" >> $output
+ done
+ delfiles="$delfiles $output"
+ output=$firstobj\"$file_list_spec$output\"
+ else
+ if test -n "$save_libobjs"; then
+ func_verbose "creating reloadable object files..."
+ output=$output_objdir/$output_la-${k}.$objext
+ eval test_cmds=\"$reload_cmds\"
+ func_len " $test_cmds"
+ len0=$func_len_result
+ len=$len0
+
+ # Loop over the list of objects to be linked.
+ for obj in $save_libobjs
+ do
+ func_len " $obj"
+ func_arith $len + $func_len_result
+ len=$func_arith_result
+ if test "X$objlist" = X ||
+ test "$len" -lt "$max_cmd_len"; then
+ func_append objlist " $obj"
+ else
+ # The command $test_cmds is almost too long, add a
+ # command to the queue.
+ if test "$k" -eq 1 ; then
+ # The first file doesn't have a previous command to add.
+ eval concat_cmds=\"$reload_cmds $objlist $last_robj\"
+ else
+ # All subsequent reloadable object files will link in
+ # the last one created.
+ eval concat_cmds=\"\$concat_cmds~$reload_cmds $objlist $last_robj~\$RM $last_robj\"
+ fi
+ last_robj=$output_objdir/$output_la-${k}.$objext
+ func_arith $k + 1
+ k=$func_arith_result
+ output=$output_objdir/$output_la-${k}.$objext
+ objlist=$obj
+ func_len " $last_robj"
+ func_arith $len0 + $func_len_result
+ len=$func_arith_result
+ fi
+ done
+ # Handle the remaining objects by creating one last
+ # reloadable object file. All subsequent reloadable object
+ # files will link in the last one created.
+ test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+ eval concat_cmds=\"\${concat_cmds}$reload_cmds $objlist $last_robj\"
+ if test -n "$last_robj"; then
+ eval concat_cmds=\"\${concat_cmds}~\$RM $last_robj\"
+ fi
+ delfiles="$delfiles $output"
+
+ else
+ output=
+ fi
+
+ if ${skipped_export-false}; then
+ func_verbose "generating symbol list for \`$libname.la'"
+ export_symbols="$output_objdir/$libname.exp"
+ $opt_dry_run || $RM $export_symbols
+ libobjs=$output
+ # Append the command to create the export file.
+ test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+ eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\"
+ if test -n "$last_robj"; then
+ eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\"
+ fi
+ fi
+
+ test -n "$save_libobjs" &&
+ func_verbose "creating a temporary reloadable object file: $output"
+
+ # Loop through the commands generated above and execute them.
+ save_ifs="$IFS"; IFS='~'
+ for cmd in $concat_cmds; do
+ IFS="$save_ifs"
+ $opt_silent || {
+ func_quote_for_expand "$cmd"
+ eval "func_echo $func_quote_for_expand_result"
+ }
+ $opt_dry_run || eval "$cmd" || {
+ lt_exit=$?
+
+ # Restore the uninstalled library and exit
+ if test "$mode" = relink; then
+ ( cd "$output_objdir" && \
+ $RM "${realname}T" && \
+ $MV "${realname}U" "$realname" )
+ fi
+
+ exit $lt_exit
+ }
+ done
+ IFS="$save_ifs"
+
+ if test -n "$export_symbols_regex" && ${skipped_export-false}; then
+ func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"'
+ func_show_eval '$MV "${export_symbols}T" "$export_symbols"'
+ fi
+ fi
+
+ if ${skipped_export-false}; then
+ if test -n "$export_symbols" && test -n "$include_expsyms"; then
+ tmp_export_symbols="$export_symbols"
+ test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols"
+ $opt_dry_run || eval '$ECHO "X$include_expsyms" | $Xsed | $SP2NL >> "$tmp_export_symbols"'
+ fi
+
+ if test -n "$orig_export_symbols"; then
+ # The given exports_symbols file has to be filtered, so filter it.
+ func_verbose "filter symbol list for \`$libname.la' to tag DATA exports"
+ # FIXME: $output_objdir/$libname.filter potentially contains lots of
+ # 's' commands which not all seds can handle. GNU sed should be fine
+ # though. Also, the filter scales superlinearly with the number of
+ # global variables. join(1) would be nice here, but unfortunately
+ # isn't a blessed tool.
+ $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter
+ delfiles="$delfiles $export_symbols $output_objdir/$libname.filter"
+ export_symbols=$output_objdir/$libname.def
+ $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols
+ fi
+ fi
+
+ libobjs=$output
+ # Restore the value of output.
+ output=$save_output
+
+ if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then
+ eval libobjs=\"\$libobjs $whole_archive_flag_spec\"
+ test "X$libobjs" = "X " && libobjs=
+ fi
+ # Expand the library linking commands again to reset the
+ # value of $libobjs for piecewise linking.
+
+ # Do each of the archive commands.
+ if test "$module" = yes && test -n "$module_cmds" ; then
+ if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then
+ cmds=$module_expsym_cmds
+ else
+ cmds=$module_cmds
+ fi
+ else
+ if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then
+ cmds=$archive_expsym_cmds
+ else
+ cmds=$archive_cmds
+ fi
+ fi
+ fi
+
+ if test -n "$delfiles"; then
+ # Append the command to remove temporary files to $cmds.
+ eval cmds=\"\$cmds~\$RM $delfiles\"
+ fi
+
+ # Add any objects from preloaded convenience libraries
+ if test -n "$dlprefiles"; then
+ gentop="$output_objdir/${outputname}x"
+ generated="$generated $gentop"
+
+ func_extract_archives $gentop $dlprefiles
+ libobjs="$libobjs $func_extract_archives_result"
+ test "X$libobjs" = "X " && libobjs=
+ fi
+
+ save_ifs="$IFS"; IFS='~'
+ for cmd in $cmds; do
+ IFS="$save_ifs"
+ eval cmd=\"$cmd\"
+ $opt_silent || {
+ func_quote_for_expand "$cmd"
+ eval "func_echo $func_quote_for_expand_result"
+ }
+ $opt_dry_run || eval "$cmd" || {
+ lt_exit=$?
+
+ # Restore the uninstalled library and exit
+ if test "$mode" = relink; then
+ ( cd "$output_objdir" && \
+ $RM "${realname}T" && \
+ $MV "${realname}U" "$realname" )
+ fi
+
+ exit $lt_exit
+ }
+ done
+ IFS="$save_ifs"
+
+ # Restore the uninstalled library and exit
+ if test "$mode" = relink; then
+ $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $?
+
+ if test -n "$convenience"; then
+ if test -z "$whole_archive_flag_spec"; then
+ func_show_eval '${RM}r "$gentop"'
+ fi
+ fi
+
+ exit $EXIT_SUCCESS
+ fi
+
+ # Create links to the real library.
+ for linkname in $linknames; do
+ if test "$realname" != "$linkname"; then
+ func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?'
+ fi
+ done
+
+ # If -module or -export-dynamic was specified, set the dlname.
+ if test "$module" = yes || test "$export_dynamic" = yes; then
+ # On all known operating systems, these are identical.
+ dlname="$soname"
+ fi
+ fi
+ ;;
+
+ obj)
+ if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
+ func_warning "\`-dlopen' is ignored for objects"
+ fi
+
+ case " $deplibs" in
+ *\ -l* | *\ -L*)
+ func_warning "\`-l' and \`-L' are ignored for objects" ;;
+ esac
+
+ test -n "$rpath" && \
+ func_warning "\`-rpath' is ignored for objects"
+
+ test -n "$xrpath" && \
+ func_warning "\`-R' is ignored for objects"
+
+ test -n "$vinfo" && \
+ func_warning "\`-version-info' is ignored for objects"
+
+ test -n "$release" && \
+ func_warning "\`-release' is ignored for objects"
+
+ case $output in
+ *.lo)
+ test -n "$objs$old_deplibs" && \
+ func_fatal_error "cannot build library object \`$output' from non-libtool objects"
+
+ libobj=$output
+ func_lo2o "$libobj"
+ obj=$func_lo2o_result
+ ;;
+ *)
+ libobj=
+ obj="$output"
+ ;;
+ esac
+
+ # Delete the old objects.
+ $opt_dry_run || $RM $obj $libobj
+
+ # Objects from convenience libraries. This assumes
+ # single-version convenience libraries. Whenever we create
+ # different ones for PIC/non-PIC, this we'll have to duplicate
+ # the extraction.
+ reload_conv_objs=
+ gentop=
+ # reload_cmds runs $LD directly, so let us get rid of
+ # -Wl from whole_archive_flag_spec and hope we can get by with
+ # turning comma into space..
+ wl=
+
+ if test -n "$convenience"; then
+ if test -n "$whole_archive_flag_spec"; then
+ eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\"
+ reload_conv_objs=$reload_objs\ `$ECHO "X$tmp_whole_archive_flags" | $Xsed -e 's|,| |g'`
+ else
+ gentop="$output_objdir/${obj}x"
+ generated="$generated $gentop"
+
+ func_extract_archives $gentop $convenience
+ reload_conv_objs="$reload_objs $func_extract_archives_result"
+ fi
+ fi
+
+ # Create the old-style object.
+ reload_objs="$objs$old_deplibs "`$ECHO "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}$'/d' -e '/\.lib$/d' -e "$lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test
+
+ output="$obj"
+ func_execute_cmds "$reload_cmds" 'exit $?'
+
+ # Exit if we aren't doing a library object file.
+ if test -z "$libobj"; then
+ if test -n "$gentop"; then
+ func_show_eval '${RM}r "$gentop"'
+ fi
+
+ exit $EXIT_SUCCESS
+ fi
+
+ if test "$build_libtool_libs" != yes; then
+ if test -n "$gentop"; then
+ func_show_eval '${RM}r "$gentop"'
+ fi
+
+ # Create an invalid libtool object if no PIC, so that we don't
+ # accidentally link it into a program.
+ # $show "echo timestamp > $libobj"
+ # $opt_dry_run || eval "echo timestamp > $libobj" || exit $?
+ exit $EXIT_SUCCESS
+ fi
+
+ if test -n "$pic_flag" || test "$pic_mode" != default; then
+ # Only do commands if we really have different PIC objects.
+ reload_objs="$libobjs $reload_conv_objs"
+ output="$libobj"
+ func_execute_cmds "$reload_cmds" 'exit $?'
+ fi
+
+ if test -n "$gentop"; then
+ func_show_eval '${RM}r "$gentop"'
+ fi
+
+ exit $EXIT_SUCCESS
+ ;;
+
+ prog)
+ case $host in
+ *cygwin*) func_stripname '' '.exe' "$output"
+ output=$func_stripname_result.exe;;
+ esac
+ test -n "$vinfo" && \
+ func_warning "\`-version-info' is ignored for programs"
+
+ test -n "$release" && \
+ func_warning "\`-release' is ignored for programs"
+
+ test "$preload" = yes \
+ && test "$dlopen_support" = unknown \
+ && test "$dlopen_self" = unknown \
+ && test "$dlopen_self_static" = unknown && \
+ func_warning "\`LT_INIT([dlopen])' not used. Assuming no dlopen support."
+
+ case $host in
+ *-*-rhapsody* | *-*-darwin1.[012])
+ # On Rhapsody replace the C library is the System framework
+ compile_deplibs=`$ECHO "X $compile_deplibs" | $Xsed -e 's/ -lc / System.ltframework /'`
+ finalize_deplibs=`$ECHO "X $finalize_deplibs" | $Xsed -e 's/ -lc / System.ltframework /'`
+ ;;
+ esac
+
+ case $host in
+ *-*-darwin*)
+ # Don't allow lazy linking, it breaks C++ global constructors
+ # But is supposedly fixed on 10.4 or later (yay!).
+ if test "$tagname" = CXX ; then
+ case ${MACOSX_DEPLOYMENT_TARGET-10.0} in
+ 10.[0123])
+ compile_command="$compile_command ${wl}-bind_at_load"
+ finalize_command="$finalize_command ${wl}-bind_at_load"
+ ;;
+ esac
+ fi
+ # Time to change all our "foo.ltframework" stuff back to "-framework foo"
+ compile_deplibs=`$ECHO "X $compile_deplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'`
+ finalize_deplibs=`$ECHO "X $finalize_deplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'`
+ ;;
+ esac
+
+
+ # move library search paths that coincide with paths to not yet
+ # installed libraries to the beginning of the library search list
+ new_libs=
+ for path in $notinst_path; do
+ case " $new_libs " in
+ *" -L$path/$objdir "*) ;;
+ *)
+ case " $compile_deplibs " in
+ *" -L$path/$objdir "*)
+ new_libs="$new_libs -L$path/$objdir" ;;
+ esac
+ ;;
+ esac
+ done
+ for deplib in $compile_deplibs; do
+ case $deplib in
+ -L*)
+ case " $new_libs " in
+ *" $deplib "*) ;;
+ *) new_libs="$new_libs $deplib" ;;
+ esac
+ ;;
+ *) new_libs="$new_libs $deplib" ;;
+ esac
+ done
+ compile_deplibs="$new_libs"
+
+
+ compile_command="$compile_command $compile_deplibs"
+ finalize_command="$finalize_command $finalize_deplibs"
+
+ if test -n "$rpath$xrpath"; then
+ # If the user specified any rpath flags, then add them.
+ for libdir in $rpath $xrpath; do
+ # This is the magic to use -rpath.
+ case "$finalize_rpath " in
+ *" $libdir "*) ;;
+ *) finalize_rpath="$finalize_rpath $libdir" ;;
+ esac
+ done
+ fi
+
+ # Now hardcode the library paths
+ rpath=
+ hardcode_libdirs=
+ for libdir in $compile_rpath $finalize_rpath; do
+ if test -n "$hardcode_libdir_flag_spec"; then
+ if test -n "$hardcode_libdir_separator"; then
+ if test -z "$hardcode_libdirs"; then
+ hardcode_libdirs="$libdir"
+ else
+ # Just accumulate the unique libdirs.
+ case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+ *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+ ;;
+ *)
+ hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir"
+ ;;
+ esac
+ fi
+ else
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ rpath="$rpath $flag"
+ fi
+ elif test -n "$runpath_var"; then
+ case "$perm_rpath " in
+ *" $libdir "*) ;;
+ *) perm_rpath="$perm_rpath $libdir" ;;
+ esac
+ fi
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+ testbindir=`${ECHO} "$libdir" | ${SED} -e 's*/lib$*/bin*'`
+ case :$dllsearchpath: in
+ *":$libdir:"*) ;;
+ ::) dllsearchpath=$libdir;;
+ *) dllsearchpath="$dllsearchpath:$libdir";;
+ esac
+ case :$dllsearchpath: in
+ *":$testbindir:"*) ;;
+ ::) dllsearchpath=$testbindir;;
+ *) dllsearchpath="$dllsearchpath:$testbindir";;
+ esac
+ ;;
+ esac
+ done
+ # Substitute the hardcoded libdirs into the rpath.
+ if test -n "$hardcode_libdir_separator" &&
+ test -n "$hardcode_libdirs"; then
+ libdir="$hardcode_libdirs"
+ eval rpath=\" $hardcode_libdir_flag_spec\"
+ fi
+ compile_rpath="$rpath"
+
+ rpath=
+ hardcode_libdirs=
+ for libdir in $finalize_rpath; do
+ if test -n "$hardcode_libdir_flag_spec"; then
+ if test -n "$hardcode_libdir_separator"; then
+ if test -z "$hardcode_libdirs"; then
+ hardcode_libdirs="$libdir"
+ else
+ # Just accumulate the unique libdirs.
+ case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+ *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+ ;;
+ *)
+ hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir"
+ ;;
+ esac
+ fi
+ else
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ rpath="$rpath $flag"
+ fi
+ elif test -n "$runpath_var"; then
+ case "$finalize_perm_rpath " in
+ *" $libdir "*) ;;
+ *) finalize_perm_rpath="$finalize_perm_rpath $libdir" ;;
+ esac
+ fi
+ done
+ # Substitute the hardcoded libdirs into the rpath.
+ if test -n "$hardcode_libdir_separator" &&
+ test -n "$hardcode_libdirs"; then
+ libdir="$hardcode_libdirs"
+ eval rpath=\" $hardcode_libdir_flag_spec\"
+ fi
+ finalize_rpath="$rpath"
+
+ if test -n "$libobjs" && test "$build_old_libs" = yes; then
+ # Transform all the library objects into standard objects.
+ compile_command=`$ECHO "X$compile_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP`
+ finalize_command=`$ECHO "X$finalize_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP`
+ fi
+
+ func_generate_dlsyms "$outputname" "@PROGRAM@" "no"
+
+ # template prelinking step
+ if test -n "$prelink_cmds"; then
+ func_execute_cmds "$prelink_cmds" 'exit $?'
+ fi
+
+ wrappers_required=yes
+ case $host in
+ *cygwin* | *mingw* )
+ if test "$build_libtool_libs" != yes; then
+ wrappers_required=no
+ fi
+ ;;
+ *cegcc)
+ # Disable wrappers for cegcc, we are cross compiling anyway.
+ wrappers_required=no
+ ;;
+ *)
+ if test "$need_relink" = no || test "$build_libtool_libs" != yes; then
+ wrappers_required=no
+ fi
+ ;;
+ esac
+ if test "$wrappers_required" = no; then
+ # Replace the output file specification.
+ compile_command=`$ECHO "X$compile_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'`
+ link_command="$compile_command$compile_rpath"
+
+ # We have no uninstalled library dependencies, so finalize right now.
+ exit_status=0
+ func_show_eval "$link_command" 'exit_status=$?'
+
+ # Delete the generated files.
+ if test -f "$output_objdir/${outputname}S.${objext}"; then
+ func_show_eval '$RM "$output_objdir/${outputname}S.${objext}"'
+ fi
+
+ exit $exit_status
+ fi
+
+ if test -n "$compile_shlibpath$finalize_shlibpath"; then
+ compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command"
+ fi
+ if test -n "$finalize_shlibpath"; then
+ finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command"
+ fi
+
+ compile_var=
+ finalize_var=
+ if test -n "$runpath_var"; then
+ if test -n "$perm_rpath"; then
+ # We should set the runpath_var.
+ rpath=
+ for dir in $perm_rpath; do
+ rpath="$rpath$dir:"
+ done
+ compile_var="$runpath_var=\"$rpath\$$runpath_var\" "
+ fi
+ if test -n "$finalize_perm_rpath"; then
+ # We should set the runpath_var.
+ rpath=
+ for dir in $finalize_perm_rpath; do
+ rpath="$rpath$dir:"
+ done
+ finalize_var="$runpath_var=\"$rpath\$$runpath_var\" "
+ fi
+ fi
+
+ if test "$no_install" = yes; then
+ # We don't need to create a wrapper script.
+ link_command="$compile_var$compile_command$compile_rpath"
+ # Replace the output file specification.
+ link_command=`$ECHO "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'`
+ # Delete the old output file.
+ $opt_dry_run || $RM $output
+ # Link the executable and exit
+ func_show_eval "$link_command" 'exit $?'
+ exit $EXIT_SUCCESS
+ fi
+
+ if test "$hardcode_action" = relink; then
+ # Fast installation is not supported
+ link_command="$compile_var$compile_command$compile_rpath"
+ relink_command="$finalize_var$finalize_command$finalize_rpath"
+
+ func_warning "this platform does not like uninstalled shared libraries"
+ func_warning "\`$output' will be relinked during installation"
+ else
+ if test "$fast_install" != no; then
+ link_command="$finalize_var$compile_command$finalize_rpath"
+ if test "$fast_install" = yes; then
+ relink_command=`$ECHO "X$compile_var$compile_command$compile_rpath" | $Xsed -e 's%@OUTPUT@%\$progdir/\$file%g'`
+ else
+ # fast_install is set to needless
+ relink_command=
+ fi
+ else
+ link_command="$compile_var$compile_command$compile_rpath"
+ relink_command="$finalize_var$finalize_command$finalize_rpath"
+ fi
+ fi
+
+ # Replace the output file specification.
+ link_command=`$ECHO "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'`
+
+ # Delete the old output files.
+ $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname
+
+ func_show_eval "$link_command" 'exit $?'
+
+ # Now create the wrapper script.
+ func_verbose "creating $output"
+
+ # Quote the relink command for shipping.
+ if test -n "$relink_command"; then
+ # Preserve any variables that may affect compiler behavior
+ for var in $variables_saved_for_relink; do
+ if eval test -z \"\${$var+set}\"; then
+ relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command"
+ elif eval var_value=\$$var; test -z "$var_value"; then
+ relink_command="$var=; export $var; $relink_command"
+ else
+ func_quote_for_eval "$var_value"
+ relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command"
+ fi
+ done
+ relink_command="(cd `pwd`; $relink_command)"
+ relink_command=`$ECHO "X$relink_command" | $Xsed -e "$sed_quote_subst"`
+ fi
+
+ # Quote $ECHO for shipping.
+ if test "X$ECHO" = "X$SHELL $progpath --fallback-echo"; then
+ case $progpath in
+ [\\/]* | [A-Za-z]:[\\/]*) qecho="$SHELL $progpath --fallback-echo";;
+ *) qecho="$SHELL `pwd`/$progpath --fallback-echo";;
+ esac
+ qecho=`$ECHO "X$qecho" | $Xsed -e "$sed_quote_subst"`
+ else
+ qecho=`$ECHO "X$ECHO" | $Xsed -e "$sed_quote_subst"`
+ fi
+
+ # Only actually do things if not in dry run mode.
+ $opt_dry_run || {
+ # win32 will think the script is a binary if it has
+ # a .exe suffix, so we strip it off here.
+ case $output in
+ *.exe) func_stripname '' '.exe' "$output"
+ output=$func_stripname_result ;;
+ esac
+ # test for cygwin because mv fails w/o .exe extensions
+ case $host in
+ *cygwin*)
+ exeext=.exe
+ func_stripname '' '.exe' "$outputname"
+ outputname=$func_stripname_result ;;
+ *) exeext= ;;
+ esac
+ case $host in
+ *cygwin* | *mingw* )
+ func_dirname_and_basename "$output" "" "."
+ output_name=$func_basename_result
+ output_path=$func_dirname_result
+ cwrappersource="$output_path/$objdir/lt-$output_name.c"
+ cwrapper="$output_path/$output_name.exe"
+ $RM $cwrappersource $cwrapper
+ trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15
+
+ func_emit_cwrapperexe_src > $cwrappersource
+
+ # The wrapper executable is built using the $host compiler,
+ # because it contains $host paths and files. If cross-
+ # compiling, it, like the target executable, must be
+ # executed on the $host or under an emulation environment.
+ $opt_dry_run || {
+ $LTCC $LTCFLAGS -o $cwrapper $cwrappersource
+ $STRIP $cwrapper
+ }
+
+ # Now, create the wrapper script for func_source use:
+ func_ltwrapper_scriptname $cwrapper
+ $RM $func_ltwrapper_scriptname_result
+ trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15
+ $opt_dry_run || {
+ # note: this script will not be executed, so do not chmod.
+ if test "x$build" = "x$host" ; then
+ $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result
+ else
+ func_emit_wrapper no > $func_ltwrapper_scriptname_result
+ fi
+ }
+ ;;
+ * )
+ $RM $output
+ trap "$RM $output; exit $EXIT_FAILURE" 1 2 15
+
+ func_emit_wrapper no > $output
+ chmod +x $output
+ ;;
+ esac
+ }
+ exit $EXIT_SUCCESS
+ ;;
+ esac
+
+ # See if we need to build an old-fashioned archive.
+ for oldlib in $oldlibs; do
+
+ if test "$build_libtool_libs" = convenience; then
+ oldobjs="$libobjs_save $symfileobj"
+ addlibs="$convenience"
+ build_libtool_libs=no
+ else
+ if test "$build_libtool_libs" = module; then
+ oldobjs="$libobjs_save"
+ build_libtool_libs=no
+ else
+ oldobjs="$old_deplibs $non_pic_objects"
+ if test "$preload" = yes && test -f "$symfileobj"; then
+ oldobjs="$oldobjs $symfileobj"
+ fi
+ fi
+ addlibs="$old_convenience"
+ fi
+
+ if test -n "$addlibs"; then
+ gentop="$output_objdir/${outputname}x"
+ generated="$generated $gentop"
+
+ func_extract_archives $gentop $addlibs
+ oldobjs="$oldobjs $func_extract_archives_result"
+ fi
+
+ # Do each command in the archive commands.
+ if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then
+ cmds=$old_archive_from_new_cmds
+ else
+
+ # Add any objects from preloaded convenience libraries
+ if test -n "$dlprefiles"; then
+ gentop="$output_objdir/${outputname}x"
+ generated="$generated $gentop"
+
+ func_extract_archives $gentop $dlprefiles
+ oldobjs="$oldobjs $func_extract_archives_result"
+ fi
+
+ # POSIX demands no paths to be encoded in archives. We have
+ # to avoid creating archives with duplicate basenames if we
+ # might have to extract them afterwards, e.g., when creating a
+ # static archive out of a convenience library, or when linking
+ # the entirety of a libtool archive into another (currently
+ # not supported by libtool).
+ if (for obj in $oldobjs
+ do
+ func_basename "$obj"
+ $ECHO "$func_basename_result"
+ done | sort | sort -uc >/dev/null 2>&1); then
+ :
+ else
+ $ECHO "copying selected object files to avoid basename conflicts..."
+ gentop="$output_objdir/${outputname}x"
+ generated="$generated $gentop"
+ func_mkdir_p "$gentop"
+ save_oldobjs=$oldobjs
+ oldobjs=
+ counter=1
+ for obj in $save_oldobjs
+ do
+ func_basename "$obj"
+ objbase="$func_basename_result"
+ case " $oldobjs " in
+ " ") oldobjs=$obj ;;
+ *[\ /]"$objbase "*)
+ while :; do
+ # Make sure we don't pick an alternate name that also
+ # overlaps.
+ newobj=lt$counter-$objbase
+ func_arith $counter + 1
+ counter=$func_arith_result
+ case " $oldobjs " in
+ *[\ /]"$newobj "*) ;;
+ *) if test ! -f "$gentop/$newobj"; then break; fi ;;
+ esac
+ done
+ func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj"
+ oldobjs="$oldobjs $gentop/$newobj"
+ ;;
+ *) oldobjs="$oldobjs $obj" ;;
+ esac
+ done
+ fi
+ eval cmds=\"$old_archive_cmds\"
+
+ func_len " $cmds"
+ len=$func_len_result
+ if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then
+ cmds=$old_archive_cmds
+ else
+ # the command line is too long to link in one step, link in parts
+ func_verbose "using piecewise archive linking..."
+ save_RANLIB=$RANLIB
+ RANLIB=:
+ objlist=
+ concat_cmds=
+ save_oldobjs=$oldobjs
+ oldobjs=
+ # Is there a better way of finding the last object in the list?
+ for obj in $save_oldobjs
+ do
+ last_oldobj=$obj
+ done
+ eval test_cmds=\"$old_archive_cmds\"
+ func_len " $test_cmds"
+ len0=$func_len_result
+ len=$len0
+ for obj in $save_oldobjs
+ do
+ func_len " $obj"
+ func_arith $len + $func_len_result
+ len=$func_arith_result
+ func_append objlist " $obj"
+ if test "$len" -lt "$max_cmd_len"; then
+ :
+ else
+ # the above command should be used before it gets too long
+ oldobjs=$objlist
+ if test "$obj" = "$last_oldobj" ; then
+ RANLIB=$save_RANLIB
+ fi
+ test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+ eval concat_cmds=\"\${concat_cmds}$old_archive_cmds\"
+ objlist=
+ len=$len0
+ fi
+ done
+ RANLIB=$save_RANLIB
+ oldobjs=$objlist
+ if test "X$oldobjs" = "X" ; then
+ eval cmds=\"\$concat_cmds\"
+ else
+ eval cmds=\"\$concat_cmds~\$old_archive_cmds\"
+ fi
+ fi
+ fi
+ func_execute_cmds "$cmds" 'exit $?'
+ done
+
+ test -n "$generated" && \
+ func_show_eval "${RM}r$generated"
+
+ # Now create the libtool archive.
+ case $output in
+ *.la)
+ old_library=
+ test "$build_old_libs" = yes && old_library="$libname.$libext"
+ func_verbose "creating $output"
+
+ # Preserve any variables that may affect compiler behavior
+ for var in $variables_saved_for_relink; do
+ if eval test -z \"\${$var+set}\"; then
+ relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command"
+ elif eval var_value=\$$var; test -z "$var_value"; then
+ relink_command="$var=; export $var; $relink_command"
+ else
+ func_quote_for_eval "$var_value"
+ relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command"
+ fi
+ done
+ # Quote the link command for shipping.
+ relink_command="(cd `pwd`; $SHELL $progpath $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)"
+ relink_command=`$ECHO "X$relink_command" | $Xsed -e "$sed_quote_subst"`
+ if test "$hardcode_automatic" = yes ; then
+ relink_command=
+ fi
+
+ # Only create the output if not a dry run.
+ $opt_dry_run || {
+ for installed in no yes; do
+ if test "$installed" = yes; then
+ if test -z "$install_libdir"; then
+ break
+ fi
+ output="$output_objdir/$outputname"i
+ # Replace all uninstalled libtool libraries with the installed ones
+ newdependency_libs=
+ for deplib in $dependency_libs; do
+ case $deplib in
+ *.la)
+ func_basename "$deplib"
+ name="$func_basename_result"
+ eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib`
+ test -z "$libdir" && \
+ func_fatal_error "\`$deplib' is not a valid libtool archive"
+ newdependency_libs="$newdependency_libs $libdir/$name"
+ ;;
+ *) newdependency_libs="$newdependency_libs $deplib" ;;
+ esac
+ done
+ dependency_libs="$newdependency_libs"
+ newdlfiles=
+
+ for lib in $dlfiles; do
+ case $lib in
+ *.la)
+ func_basename "$lib"
+ name="$func_basename_result"
+ eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib`
+ test -z "$libdir" && \
+ func_fatal_error "\`$lib' is not a valid libtool archive"
+ newdlfiles="$newdlfiles $libdir/$name"
+ ;;
+ *) newdlfiles="$newdlfiles $lib" ;;
+ esac
+ done
+ dlfiles="$newdlfiles"
+ newdlprefiles=
+ for lib in $dlprefiles; do
+ case $lib in
+ *.la)
+ # Only pass preopened files to the pseudo-archive (for
+ # eventual linking with the app. that links it) if we
+ # didn't already link the preopened objects directly into
+ # the library:
+ func_basename "$lib"
+ name="$func_basename_result"
+ eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib`
+ test -z "$libdir" && \
+ func_fatal_error "\`$lib' is not a valid libtool archive"
+ newdlprefiles="$newdlprefiles $libdir/$name"
+ ;;
+ esac
+ done
+ dlprefiles="$newdlprefiles"
+ else
+ newdlfiles=
+ for lib in $dlfiles; do
+ case $lib in
+ [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;;
+ *) abs=`pwd`"/$lib" ;;
+ esac
+ newdlfiles="$newdlfiles $abs"
+ done
+ dlfiles="$newdlfiles"
+ newdlprefiles=
+ for lib in $dlprefiles; do
+ case $lib in
+ [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;;
+ *) abs=`pwd`"/$lib" ;;
+ esac
+ newdlprefiles="$newdlprefiles $abs"
+ done
+ dlprefiles="$newdlprefiles"
+ fi
+ $RM $output
+ # place dlname in correct position for cygwin
+ tdlname=$dlname
+ case $host,$output,$installed,$module,$dlname in
+ *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll) tdlname=../bin/$dlname ;;
+ esac
+ $ECHO > $output "\
+# $outputname - a libtool library file
+# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# The name that we can dlopen(3).
+dlname='$tdlname'
+
+# Names of this library.
+library_names='$library_names'
+
+# The name of the static archive.
+old_library='$old_library'
+
+# Linker flags that can not go in dependency_libs.
+inherited_linker_flags='$new_inherited_linker_flags'
+
+# Libraries that this one depends upon.
+dependency_libs='$dependency_libs'
+
+# Names of additional weak libraries provided by this library
+weak_library_names='$weak_libs'
+
+# Version information for $libname.
+current=$current
+age=$age
+revision=$revision
+
+# Is this an already installed library?
+installed=$installed
+
+# Should we warn about portability when linking against -modules?
+shouldnotlink=$module
+
+# Files to dlopen/dlpreopen
+dlopen='$dlfiles'
+dlpreopen='$dlprefiles'
+
+# Directory that this library needs to be installed in:
+libdir='$install_libdir'"
+ if test "$installed" = no && test "$need_relink" = yes; then
+ $ECHO >> $output "\
+relink_command=\"$relink_command\""
+ fi
+ done
+ }
+
+ # Do a symbolic link so that the libtool archive can be found in
+ # LD_LIBRARY_PATH before the program is installed.
+ func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?'
+ ;;
+ esac
+ exit $EXIT_SUCCESS
+}
+
+{ test "$mode" = link || test "$mode" = relink; } &&
+ func_mode_link ${1+"$@"}
+
+
+# func_mode_uninstall arg...
+func_mode_uninstall ()
+{
+ $opt_debug
+ RM="$nonopt"
+ files=
+ rmforce=
+ exit_status=0
+
+ # This variable tells wrapper scripts just to set variables rather
+ # than running their programs.
+ libtool_install_magic="$magic"
+
+ for arg
+ do
+ case $arg in
+ -f) RM="$RM $arg"; rmforce=yes ;;
+ -*) RM="$RM $arg" ;;
+ *) files="$files $arg" ;;
+ esac
+ done
+
+ test -z "$RM" && \
+ func_fatal_help "you must specify an RM program"
+
+ rmdirs=
+
+ origobjdir="$objdir"
+ for file in $files; do
+ func_dirname "$file" "" "."
+ dir="$func_dirname_result"
+ if test "X$dir" = X.; then
+ objdir="$origobjdir"
+ else
+ objdir="$dir/$origobjdir"
+ fi
+ func_basename "$file"
+ name="$func_basename_result"
+ test "$mode" = uninstall && objdir="$dir"
+
+ # Remember objdir for removal later, being careful to avoid duplicates
+ if test "$mode" = clean; then
+ case " $rmdirs " in
+ *" $objdir "*) ;;
+ *) rmdirs="$rmdirs $objdir" ;;
+ esac
+ fi
+
+ # Don't error if the file doesn't exist and rm -f was used.
+ if { test -L "$file"; } >/dev/null 2>&1 ||
+ { test -h "$file"; } >/dev/null 2>&1 ||
+ test -f "$file"; then
+ :
+ elif test -d "$file"; then
+ exit_status=1
+ continue
+ elif test "$rmforce" = yes; then
+ continue
+ fi
+
+ rmfiles="$file"
+
+ case $name in
+ *.la)
+ # Possibly a libtool archive, so verify it.
+ if func_lalib_p "$file"; then
+ func_source $dir/$name
+
+ # Delete the libtool libraries and symlinks.
+ for n in $library_names; do
+ rmfiles="$rmfiles $objdir/$n"
+ done
+ test -n "$old_library" && rmfiles="$rmfiles $objdir/$old_library"
+
+ case "$mode" in
+ clean)
+ case " $library_names " in
+ # " " in the beginning catches empty $dlname
+ *" $dlname "*) ;;
+ *) rmfiles="$rmfiles $objdir/$dlname" ;;
+ esac
+ test -n "$libdir" && rmfiles="$rmfiles $objdir/$name $objdir/${name}i"
+ ;;
+ uninstall)
+ if test -n "$library_names"; then
+ # Do each command in the postuninstall commands.
+ func_execute_cmds "$postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1'
+ fi
+
+ if test -n "$old_library"; then
+ # Do each command in the old_postuninstall commands.
+ func_execute_cmds "$old_postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1'
+ fi
+ # FIXME: should reinstall the best remaining shared library.
+ ;;
+ esac
+ fi
+ ;;
+
+ *.lo)
+ # Possibly a libtool object, so verify it.
+ if func_lalib_p "$file"; then
+
+ # Read the .lo file
+ func_source $dir/$name
+
+ # Add PIC object to the list of files to remove.
+ if test -n "$pic_object" &&
+ test "$pic_object" != none; then
+ rmfiles="$rmfiles $dir/$pic_object"
+ fi
+
+ # Add non-PIC object to the list of files to remove.
+ if test -n "$non_pic_object" &&
+ test "$non_pic_object" != none; then
+ rmfiles="$rmfiles $dir/$non_pic_object"
+ fi
+ fi
+ ;;
+
+ *)
+ if test "$mode" = clean ; then
+ noexename=$name
+ case $file in
+ *.exe)
+ func_stripname '' '.exe' "$file"
+ file=$func_stripname_result
+ func_stripname '' '.exe' "$name"
+ noexename=$func_stripname_result
+ # $file with .exe has already been added to rmfiles,
+ # add $file without .exe
+ rmfiles="$rmfiles $file"
+ ;;
+ esac
+ # Do a test to see if this is a libtool program.
+ if func_ltwrapper_p "$file"; then
+ if func_ltwrapper_executable_p "$file"; then
+ func_ltwrapper_scriptname "$file"
+ relink_command=
+ func_source $func_ltwrapper_scriptname_result
+ rmfiles="$rmfiles $func_ltwrapper_scriptname_result"
+ else
+ relink_command=
+ func_source $dir/$noexename
+ fi
+
+ # note $name still contains .exe if it was in $file originally
+ # as does the version of $file that was added into $rmfiles
+ rmfiles="$rmfiles $objdir/$name $objdir/${name}S.${objext}"
+ if test "$fast_install" = yes && test -n "$relink_command"; then
+ rmfiles="$rmfiles $objdir/lt-$name"
+ fi
+ if test "X$noexename" != "X$name" ; then
+ rmfiles="$rmfiles $objdir/lt-${noexename}.c"
+ fi
+ fi
+ fi
+ ;;
+ esac
+ func_show_eval "$RM $rmfiles" 'exit_status=1'
+ done
+ objdir="$origobjdir"
+
+ # Try to remove the ${objdir}s in the directories where we deleted files
+ for dir in $rmdirs; do
+ if test -d "$dir"; then
+ func_show_eval "rmdir $dir >/dev/null 2>&1"
+ fi
+ done
+
+ exit $exit_status
+}
+
+{ test "$mode" = uninstall || test "$mode" = clean; } &&
+ func_mode_uninstall ${1+"$@"}
+
+test -z "$mode" && {
+ help="$generic_help"
+ func_fatal_help "you must specify a MODE"
+}
+
+test -z "$exec_cmd" && \
+ func_fatal_help "invalid operation mode \`$mode'"
+
+if test -n "$exec_cmd"; then
+ eval exec "$exec_cmd"
+ exit $EXIT_FAILURE
+fi
+
+exit $exit_status
+
+
+# The TAGs below are defined such that we never get into a situation
+# in which we disable both kinds of libraries. Given conflicting
+# choices, we go for a static library, that is the most portable,
+# since we can't tell whether shared libraries were disabled because
+# the user asked for that or because the platform doesn't support
+# them. This is particularly important on AIX, because we don't
+# support having both static and shared libraries enabled at the same
+# time on that platform, so we default to a shared-only configuration.
+# If a disable-shared tag is given, we'll fallback to a static-only
+# configuration. But we'll never go from static-only to shared-only.
+
+# ### BEGIN LIBTOOL TAG CONFIG: disable-shared
+build_libtool_libs=no
+build_old_libs=yes
+# ### END LIBTOOL TAG CONFIG: disable-shared
+
+# ### BEGIN LIBTOOL TAG CONFIG: disable-static
+build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac`
+# ### END LIBTOOL TAG CONFIG: disable-static
+
+# Local Variables:
+# mode:shell-script
+# sh-indentation:2
+# End:
+# vi:sw=2
+
diff --git a/m4/ChangeLog b/m4/ChangeLog
new file mode 100644
index 00000000..f495d58e
--- /dev/null
+++ b/m4/ChangeLog
@@ -0,0 +1,56 @@
+2007-10-05 gettextize <bug-gnu-gettext@gnu.org>
+
+ * codeset.m4: Upgrade to gettext-0.14.6.
+ * gettext.m4: Upgrade to gettext-0.14.6.
+ * glibc2.m4: New file, from gettext-0.14.6.
+ * glibc21.m4: Upgrade to gettext-0.14.6.
+ * iconv.m4: Upgrade to gettext-0.14.6.
+ * intdiv0.m4: Upgrade to gettext-0.14.6.
+ * intmax.m4: New file, from gettext-0.14.6.
+ * inttypes.m4: Upgrade to gettext-0.14.6.
+ * inttypes_h.m4: Upgrade to gettext-0.14.6.
+ * inttypes-pri.m4: Upgrade to gettext-0.14.6.
+ * isc-posix.m4: Upgrade to gettext-0.14.6.
+ * lcmessage.m4: Upgrade to gettext-0.14.6.
+ * lib-ld.m4: Upgrade to gettext-0.14.6.
+ * lib-link.m4: Upgrade to gettext-0.14.6.
+ * lib-prefix.m4: Upgrade to gettext-0.14.6.
+ * longdouble.m4: New file, from gettext-0.14.6.
+ * longlong.m4: New file, from gettext-0.14.6.
+ * nls.m4: Upgrade to gettext-0.14.6.
+ * po.m4: Upgrade to gettext-0.14.6.
+ * printf-posix.m4: New file, from gettext-0.14.6.
+ * progtest.m4: Upgrade to gettext-0.14.6.
+ * signed.m4: New file, from gettext-0.14.6.
+ * size_max.m4: New file, from gettext-0.14.6.
+ * stdint_h.m4: Upgrade to gettext-0.14.6.
+ * uintmax_t.m4: Upgrade to gettext-0.14.6.
+ * ulonglong.m4: Upgrade to gettext-0.14.6.
+ * wchar_t.m4: New file, from gettext-0.14.6.
+ * wint_t.m4: New file, from gettext-0.14.6.
+ * xsize.m4: New file, from gettext-0.14.6.
+ * Makefile.am (EXTRA_DIST): Add the new files.
+
+2006-07-28 gettextize <bug-gnu-gettext@gnu.org>
+
+ * codeset.m4: New file, from gettext-0.12.1.
+ * gettext.m4: New file, from gettext-0.12.1.
+ * glibc21.m4: New file, from gettext-0.12.1.
+ * iconv.m4: New file, from gettext-0.12.1.
+ * intdiv0.m4: New file, from gettext-0.12.1.
+ * inttypes.m4: New file, from gettext-0.12.1.
+ * inttypes_h.m4: New file, from gettext-0.12.1.
+ * inttypes-pri.m4: New file, from gettext-0.12.1.
+ * isc-posix.m4: New file, from gettext-0.12.1.
+ * lcmessage.m4: New file, from gettext-0.12.1.
+ * lib-ld.m4: New file, from gettext-0.12.1.
+ * lib-link.m4: New file, from gettext-0.12.1.
+ * lib-prefix.m4: New file, from gettext-0.12.1.
+ * nls.m4: New file, from gettext-0.12.1.
+ * po.m4: New file, from gettext-0.12.1.
+ * progtest.m4: New file, from gettext-0.12.1.
+ * stdint_h.m4: New file, from gettext-0.12.1.
+ * uintmax_t.m4: New file, from gettext-0.12.1.
+ * ulonglong.m4: New file, from gettext-0.12.1.
+ * Makefile.am: New file.
+
diff --git a/m4/Makefile.am b/m4/Makefile.am
new file mode 100644
index 00000000..6914e980
--- /dev/null
+++ b/m4/Makefile.am
@@ -0,0 +1 @@
+EXTRA_DIST = glibc2.m4 intmax.m4 longdouble.m4 longlong.m4 printf-posix.m4 signed.m4 size_max.m4 wchar_t.m4 wint_t.m4 xsize.m4 codeset.m4 gettext.m4 glibc21.m4 iconv.m4 intdiv0.m4 inttypes.m4 inttypes_h.m4 inttypes-pri.m4 isc-posix.m4 lcmessage.m4 lib-ld.m4 lib-link.m4 lib-prefix.m4 nls.m4 po.m4 progtest.m4 stdint_h.m4 uintmax_t.m4 ulonglong.m4
diff --git a/m4/Makefile.in b/m4/Makefile.in
new file mode 100644
index 00000000..917ccf5f
--- /dev/null
+++ b/m4/Makefile.in
@@ -0,0 +1,401 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = m4
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in ChangeLog
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/acx_pthread.m4 \
+ $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
+ $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/VERSION \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/include/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+SOURCES =
+DIST_SOURCES =
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_CFLAGS = @AM_CFLAGS@
+AM_LDFLAGS = @AM_LDFLAGS@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CP = @CP@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+C_CLIENT_CFLAGS = @C_CLIENT_CFLAGS@
+C_CLIENT_GCCOPTLEVEL = @C_CLIENT_GCCOPTLEVEL@
+C_CLIENT_LDFLAGS = @C_CLIENT_LDFLAGS@
+C_CLIENT_SPECIALS = @C_CLIENT_SPECIALS@
+C_CLIENT_TARGET = @C_CLIENT_TARGET@
+C_CLIENT_WITH_IPV6 = @C_CLIENT_WITH_IPV6@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISPELLPROG = @ISPELLPROG@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN = @LN@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKE = @MAKE@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NPA_PROG = @NPA_PROG@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+POSUB = @POSUB@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+PWPROG = @PWPROG@
+RANLIB = @RANLIB@
+REGEX_BUILD = @REGEX_BUILD@
+RM = @RM@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPELLPROG = @SPELLPROG@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEB_BINDIR = @WEB_BINDIR@
+WEB_BUILD = @WEB_BUILD@
+WEB_PUBCOOKIE_BUILD = @WEB_PUBCOOKIE_BUILD@
+WEB_PUBCOOKIE_LIB = @WEB_PUBCOOKIE_LIB@
+WEB_PUBCOOKIE_LINK = @WEB_PUBCOOKIE_LINK@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+acx_pthread_config = @acx_pthread_config@
+alpine_interactive_spellcheck = @alpine_interactive_spellcheck@
+alpine_simple_spellcheck = @alpine_simple_spellcheck@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+EXTRA_DIST = glibc2.m4 intmax.m4 longdouble.m4 longlong.m4 printf-posix.m4 signed.m4 size_max.m4 wchar_t.m4 wint_t.m4 xsize.m4 codeset.m4 gettext.m4 glibc21.m4 iconv.m4 intdiv0.m4 inttypes.m4 inttypes_h.m4 inttypes-pri.m4 isc-posix.m4 lcmessage.m4 lib-ld.m4 lib-link.m4 lib-prefix.m4 nls.m4 po.m4 progtest.m4 stdint_h.m4 uintmax_t.m4 ulonglong.m4
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign m4/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign m4/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+tags: TAGS
+TAGS:
+
+ctags: CTAGS
+CTAGS:
+
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ distclean distclean-generic distclean-libtool distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am uninstall uninstall-am
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/m4/acx_pthread.m4 b/m4/acx_pthread.m4
new file mode 100644
index 00000000..e4e91d3c
--- /dev/null
+++ b/m4/acx_pthread.m4
@@ -0,0 +1,242 @@
+dnl @synopsis ACX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
+dnl
+dnl @summary figure out how to build C programs using POSIX threads
+dnl
+dnl This macro figures out how to build C programs using POSIX threads.
+dnl It sets the PTHREAD_LIBS output variable to the threads library and
+dnl linker flags, and the PTHREAD_CFLAGS output variable to any special
+dnl C compiler flags that are needed. (The user can also force certain
+dnl compiler flags/libs to be tested by setting these environment
+dnl variables.)
+dnl
+dnl Also sets PTHREAD_CC to any special C compiler that is needed for
+dnl multi-threaded programs (defaults to the value of CC otherwise).
+dnl (This is necessary on AIX to use the special cc_r compiler alias.)
+dnl
+dnl NOTE: You are assumed to not only compile your program with these
+dnl flags, but also link it with them as well. e.g. you should link
+dnl with $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS
+dnl $LIBS
+dnl
+dnl If you are only building threads programs, you may wish to use
+dnl these variables in your default LIBS, CFLAGS, and CC:
+dnl
+dnl LIBS="$PTHREAD_LIBS $LIBS"
+dnl CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+dnl CC="$PTHREAD_CC"
+dnl
+dnl In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute
+dnl constant has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to
+dnl that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
+dnl
+dnl ACTION-IF-FOUND is a list of shell commands to run if a threads
+dnl library is found, and ACTION-IF-NOT-FOUND is a list of commands to
+dnl run it if it is not found. If ACTION-IF-FOUND is not specified, the
+dnl default action will define HAVE_PTHREAD.
+dnl
+dnl Please let the authors know if this macro fails on any platform, or
+dnl if you have any other suggestions or comments. This macro was based
+dnl on work by SGJ on autoconf scripts for FFTW (www.fftw.org) (with
+dnl help from M. Frigo), as well as ac_pthread and hb_pthread macros
+dnl posted by Alejandro Forero Cuervo to the autoconf macro repository.
+dnl We are also grateful for the helpful feedback of numerous users.
+dnl
+dnl @category InstalledPackages
+dnl @author Steven G. Johnson <stevenj@alum.mit.edu>
+dnl @version 2006-05-29
+dnl @license GPLWithACException
+
+AC_DEFUN([ACX_PTHREAD], [
+AC_REQUIRE([AC_CANONICAL_HOST])
+AC_LANG_SAVE
+AC_LANG_C
+acx_pthread_ok=no
+
+# We used to check for pthread.h first, but this fails if pthread.h
+# requires special compiler flags (e.g. on True64 or Sequent).
+# It gets checked for in the link test anyway.
+
+# First of all, check if the user has set any of the PTHREAD_LIBS,
+# etcetera environment variables, and if threads linking works using
+# them:
+if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+ save_LIBS="$LIBS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
+ AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes)
+ AC_MSG_RESULT($acx_pthread_ok)
+ if test x"$acx_pthread_ok" = xno; then
+ PTHREAD_LIBS=""
+ PTHREAD_CFLAGS=""
+ fi
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+fi
+
+# We must check for the threads library under a number of different
+# names; the ordering is very important because some systems
+# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
+# libraries is broken (non-POSIX).
+
+# Create a list of thread flags to try. Items starting with a "-" are
+# C compiler flags, and other items are library names, except for "none"
+# which indicates that we try without any flags at all, and "pthread-config"
+# which is a program returning the flags for the Pth emulation library.
+
+acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
+
+# The ordering *is* (sometimes) important. Some notes on the
+# individual items follow:
+
+# pthreads: AIX (must check this before -lpthread)
+# none: in case threads are in libc; should be tried before -Kthread and
+# other compiler flags to prevent continual compiler warnings
+# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
+# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
+# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
+# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
+# -pthreads: Solaris/gcc
+# -mthreads: Mingw32/gcc, Lynx/gcc
+# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
+# doesn't hurt to check since this sometimes defines pthreads too;
+# also defines -D_REENTRANT)
+# ... -mt is also the pthreads flag for HP/aCC
+# pthread: Linux, etcetera
+# --thread-safe: KAI C++
+# pthread-config: use pthread-config program (for GNU Pth library)
+
+case "${host_cpu}-${host_os}" in
+ *solaris*)
+
+ # On Solaris (at least, for some versions), libc contains stubbed
+ # (non-functional) versions of the pthreads routines, so link-based
+ # tests will erroneously succeed. (We need to link with -pthreads/-mt/
+ # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
+ # a function called by this macro, so we could check for that, but
+ # who knows whether they'll stub that too in a future libc.) So,
+ # we'll just look for -pthreads and -lpthread first:
+
+ acx_pthread_flags="-pthreads pthread -mt -pthread $acx_pthread_flags"
+ ;;
+esac
+
+if test x"$acx_pthread_ok" = xno; then
+for flag in $acx_pthread_flags; do
+
+ case $flag in
+ none)
+ AC_MSG_CHECKING([whether pthreads work without any flags])
+ ;;
+
+ -*)
+ AC_MSG_CHECKING([whether pthreads work with $flag])
+ PTHREAD_CFLAGS="$flag"
+ ;;
+
+ pthread-config)
+ AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no)
+ if test x"$acx_pthread_config" = xno; then continue; fi
+ PTHREAD_CFLAGS="`pthread-config --cflags`"
+ PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
+ ;;
+
+ *)
+ AC_MSG_CHECKING([for the pthreads library -l$flag])
+ PTHREAD_LIBS="-l$flag"
+ ;;
+ esac
+
+ save_LIBS="$LIBS"
+ save_CFLAGS="$CFLAGS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+ # Check for various functions. We must include pthread.h,
+ # since some functions may be macros. (On the Sequent, we
+ # need a special flag -Kthread to make this header compile.)
+ # We check for pthread_join because it is in -lpthread on IRIX
+ # while pthread_create is in libc. We check for pthread_attr_init
+ # due to DEC craziness with -lpthreads. We check for
+ # pthread_cleanup_push because it is one of the few pthread
+ # functions on Solaris that doesn't have a non-functional libc stub.
+ # We try pthread_create on general principles.
+ AC_TRY_LINK([#include <pthread.h>],
+ [pthread_t th; pthread_join(th, 0);
+ pthread_attr_init(0); pthread_cleanup_push(0, 0);
+ pthread_create(0,0,0,0); pthread_cleanup_pop(0); ],
+ [acx_pthread_ok=yes])
+
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+
+ AC_MSG_RESULT($acx_pthread_ok)
+ if test "x$acx_pthread_ok" = xyes; then
+ break;
+ fi
+
+ PTHREAD_LIBS=""
+ PTHREAD_CFLAGS=""
+done
+fi
+
+# Various other checks:
+if test "x$acx_pthread_ok" = xyes; then
+ save_LIBS="$LIBS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+ # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
+ AC_MSG_CHECKING([for joinable pthread attribute])
+ attr_name=unknown
+ for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
+ AC_TRY_LINK([#include <pthread.h>], [int attr=$attr; return attr;],
+ [attr_name=$attr; break])
+ done
+ AC_MSG_RESULT($attr_name)
+ if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
+ AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
+ [Define to necessary symbol if this constant
+ uses a non-standard name on your system.])
+ fi
+
+ AC_MSG_CHECKING([if more special flags are required for pthreads])
+ flag=no
+ case "${host_cpu}-${host_os}" in
+ *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";;
+ *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
+ esac
+ AC_MSG_RESULT(${flag})
+ if test "x$flag" != xno; then
+ PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
+ fi
+
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+
+ # More AIX lossage: must compile with xlc_r or cc_r
+ if test x"$GCC" != xyes; then
+ AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC})
+ else
+ PTHREAD_CC=$CC
+ fi
+else
+ PTHREAD_CC="$CC"
+fi
+
+AC_SUBST(PTHREAD_LIBS)
+AC_SUBST(PTHREAD_CFLAGS)
+AC_SUBST(PTHREAD_CC)
+
+# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
+if test x"$acx_pthread_ok" = xyes; then
+ ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
+ :
+else
+ acx_pthread_ok=no
+ $2
+fi
+AC_LANG_RESTORE
+])dnl ACX_PTHREAD
diff --git a/m4/codeset.m4 b/m4/codeset.m4
new file mode 100644
index 00000000..223955b4
--- /dev/null
+++ b/m4/codeset.m4
@@ -0,0 +1,21 @@
+# codeset.m4 serial 2 (gettext-0.16)
+dnl Copyright (C) 2000-2002, 2006 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Bruno Haible.
+
+AC_DEFUN([AM_LANGINFO_CODESET],
+[
+ AC_CACHE_CHECK([for nl_langinfo and CODESET], am_cv_langinfo_codeset,
+ [AC_TRY_LINK([#include <langinfo.h>],
+ [char* cs = nl_langinfo(CODESET); return !cs;],
+ am_cv_langinfo_codeset=yes,
+ am_cv_langinfo_codeset=no)
+ ])
+ if test $am_cv_langinfo_codeset = yes; then
+ AC_DEFINE(HAVE_LANGINFO_CODESET, 1,
+ [Define if you have <langinfo.h> and nl_langinfo(CODESET).])
+ fi
+])
diff --git a/m4/gettext.m4 b/m4/gettext.m4
new file mode 100644
index 00000000..91c345e9
--- /dev/null
+++ b/m4/gettext.m4
@@ -0,0 +1,419 @@
+# gettext.m4 serial 59 (gettext-0.16.1)
+dnl Copyright (C) 1995-2006 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+dnl
+dnl This file can can be used in projects which are not available under
+dnl the GNU General Public License or the GNU Library General Public
+dnl License but which still want to provide support for the GNU gettext
+dnl functionality.
+dnl Please note that the actual code of the GNU gettext library is covered
+dnl by the GNU Library General Public License, and the rest of the GNU
+dnl gettext package package is covered by the GNU General Public License.
+dnl They are *not* in the public domain.
+
+dnl Authors:
+dnl Ulrich Drepper <drepper@cygnus.com>, 1995-2000.
+dnl Bruno Haible <haible@clisp.cons.org>, 2000-2006.
+
+dnl Macro to add for using GNU gettext.
+
+dnl Usage: AM_GNU_GETTEXT([INTLSYMBOL], [NEEDSYMBOL], [INTLDIR]).
+dnl INTLSYMBOL can be one of 'external', 'no-libtool', 'use-libtool'. The
+dnl default (if it is not specified or empty) is 'no-libtool'.
+dnl INTLSYMBOL should be 'external' for packages with no intl directory,
+dnl and 'no-libtool' or 'use-libtool' for packages with an intl directory.
+dnl If INTLSYMBOL is 'use-libtool', then a libtool library
+dnl $(top_builddir)/intl/libintl.la will be created (shared and/or static,
+dnl depending on --{enable,disable}-{shared,static} and on the presence of
+dnl AM-DISABLE-SHARED). If INTLSYMBOL is 'no-libtool', a static library
+dnl $(top_builddir)/intl/libintl.a will be created.
+dnl If NEEDSYMBOL is specified and is 'need-ngettext', then GNU gettext
+dnl implementations (in libc or libintl) without the ngettext() function
+dnl will be ignored. If NEEDSYMBOL is specified and is
+dnl 'need-formatstring-macros', then GNU gettext implementations that don't
+dnl support the ISO C 99 <inttypes.h> formatstring macros will be ignored.
+dnl INTLDIR is used to find the intl libraries. If empty,
+dnl the value `$(top_builddir)/intl/' is used.
+dnl
+dnl The result of the configuration is one of three cases:
+dnl 1) GNU gettext, as included in the intl subdirectory, will be compiled
+dnl and used.
+dnl Catalog format: GNU --> install in $(datadir)
+dnl Catalog extension: .mo after installation, .gmo in source tree
+dnl 2) GNU gettext has been found in the system's C library.
+dnl Catalog format: GNU --> install in $(datadir)
+dnl Catalog extension: .mo after installation, .gmo in source tree
+dnl 3) No internationalization, always use English msgid.
+dnl Catalog format: none
+dnl Catalog extension: none
+dnl If INTLSYMBOL is 'external', only cases 2 and 3 can occur.
+dnl The use of .gmo is historical (it was needed to avoid overwriting the
+dnl GNU format catalogs when building on a platform with an X/Open gettext),
+dnl but we keep it in order not to force irrelevant filename changes on the
+dnl maintainers.
+dnl
+AC_DEFUN([AM_GNU_GETTEXT],
+[
+ dnl Argument checking.
+ ifelse([$1], [], , [ifelse([$1], [external], , [ifelse([$1], [no-libtool], , [ifelse([$1], [use-libtool], ,
+ [errprint([ERROR: invalid first argument to AM_GNU_GETTEXT
+])])])])])
+ ifelse([$2], [], , [ifelse([$2], [need-ngettext], , [ifelse([$2], [need-formatstring-macros], ,
+ [errprint([ERROR: invalid second argument to AM_GNU_GETTEXT
+])])])])
+ define([gt_included_intl],
+ ifelse([$1], [external],
+ ifdef([AM_GNU_GETTEXT_][INTL_SUBDIR], [yes], [no]),
+ [yes]))
+ define([gt_libtool_suffix_prefix], ifelse([$1], [use-libtool], [l], []))
+ gt_NEEDS_INIT
+ AM_GNU_GETTEXT_NEED([$2])
+
+ AC_REQUIRE([AM_PO_SUBDIRS])dnl
+ ifelse(gt_included_intl, yes, [
+ AC_REQUIRE([AM_INTL_SUBDIR])dnl
+ ])
+
+ dnl Prerequisites of AC_LIB_LINKFLAGS_BODY.
+ AC_REQUIRE([AC_LIB_PREPARE_PREFIX])
+ AC_REQUIRE([AC_LIB_RPATH])
+
+ dnl Sometimes libintl requires libiconv, so first search for libiconv.
+ dnl Ideally we would do this search only after the
+ dnl if test "$USE_NLS" = "yes"; then
+ dnl if { eval "gt_val=\$$gt_func_gnugettext_libc"; test "$gt_val" != "yes"; }; then
+ dnl tests. But if configure.in invokes AM_ICONV after AM_GNU_GETTEXT
+ dnl the configure script would need to contain the same shell code
+ dnl again, outside any 'if'. There are two solutions:
+ dnl - Invoke AM_ICONV_LINKFLAGS_BODY here, outside any 'if'.
+ dnl - Control the expansions in more detail using AC_PROVIDE_IFELSE.
+ dnl Since AC_PROVIDE_IFELSE is only in autoconf >= 2.52 and not
+ dnl documented, we avoid it.
+ ifelse(gt_included_intl, yes, , [
+ AC_REQUIRE([AM_ICONV_LINKFLAGS_BODY])
+ ])
+
+ dnl Sometimes, on MacOS X, libintl requires linking with CoreFoundation.
+ gt_INTL_MACOSX
+
+ dnl Set USE_NLS.
+ AC_REQUIRE([AM_NLS])
+
+ ifelse(gt_included_intl, yes, [
+ BUILD_INCLUDED_LIBINTL=no
+ USE_INCLUDED_LIBINTL=no
+ ])
+ LIBINTL=
+ LTLIBINTL=
+ POSUB=
+
+ dnl Add a version number to the cache macros.
+ case " $gt_needs " in
+ *" need-formatstring-macros "*) gt_api_version=3 ;;
+ *" need-ngettext "*) gt_api_version=2 ;;
+ *) gt_api_version=1 ;;
+ esac
+ gt_func_gnugettext_libc="gt_cv_func_gnugettext${gt_api_version}_libc"
+ gt_func_gnugettext_libintl="gt_cv_func_gnugettext${gt_api_version}_libintl"
+
+ dnl If we use NLS figure out what method
+ if test "$USE_NLS" = "yes"; then
+ gt_use_preinstalled_gnugettext=no
+ ifelse(gt_included_intl, yes, [
+ AC_MSG_CHECKING([whether included gettext is requested])
+ AC_ARG_WITH(included-gettext,
+ [ --with-included-gettext use the GNU gettext library included here],
+ nls_cv_force_use_gnu_gettext=$withval,
+ nls_cv_force_use_gnu_gettext=no)
+ AC_MSG_RESULT($nls_cv_force_use_gnu_gettext)
+
+ nls_cv_use_gnu_gettext="$nls_cv_force_use_gnu_gettext"
+ if test "$nls_cv_force_use_gnu_gettext" != "yes"; then
+ ])
+ dnl User does not insist on using GNU NLS library. Figure out what
+ dnl to use. If GNU gettext is available we use this. Else we have
+ dnl to fall back to GNU NLS library.
+
+ if test $gt_api_version -ge 3; then
+ gt_revision_test_code='
+#ifndef __GNU_GETTEXT_SUPPORTED_REVISION
+#define __GNU_GETTEXT_SUPPORTED_REVISION(major) ((major) == 0 ? 0 : -1)
+#endif
+changequote(,)dnl
+typedef int array [2 * (__GNU_GETTEXT_SUPPORTED_REVISION(0) >= 1) - 1];
+changequote([,])dnl
+'
+ else
+ gt_revision_test_code=
+ fi
+ if test $gt_api_version -ge 2; then
+ gt_expression_test_code=' + * ngettext ("", "", 0)'
+ else
+ gt_expression_test_code=
+ fi
+
+ AC_CACHE_CHECK([for GNU gettext in libc], [$gt_func_gnugettext_libc],
+ [AC_TRY_LINK([#include <libintl.h>
+$gt_revision_test_code
+extern int _nl_msg_cat_cntr;
+extern int *_nl_domain_bindings;],
+ [bindtextdomain ("", "");
+return * gettext ("")$gt_expression_test_code + _nl_msg_cat_cntr + *_nl_domain_bindings],
+ [eval "$gt_func_gnugettext_libc=yes"],
+ [eval "$gt_func_gnugettext_libc=no"])])
+
+ if { eval "gt_val=\$$gt_func_gnugettext_libc"; test "$gt_val" != "yes"; }; then
+ dnl Sometimes libintl requires libiconv, so first search for libiconv.
+ ifelse(gt_included_intl, yes, , [
+ AM_ICONV_LINK
+ ])
+ dnl Search for libintl and define LIBINTL, LTLIBINTL and INCINTL
+ dnl accordingly. Don't use AC_LIB_LINKFLAGS_BODY([intl],[iconv])
+ dnl because that would add "-liconv" to LIBINTL and LTLIBINTL
+ dnl even if libiconv doesn't exist.
+ AC_LIB_LINKFLAGS_BODY([intl])
+ AC_CACHE_CHECK([for GNU gettext in libintl],
+ [$gt_func_gnugettext_libintl],
+ [gt_save_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $INCINTL"
+ gt_save_LIBS="$LIBS"
+ LIBS="$LIBS $LIBINTL"
+ dnl Now see whether libintl exists and does not depend on libiconv.
+ AC_TRY_LINK([#include <libintl.h>
+$gt_revision_test_code
+extern int _nl_msg_cat_cntr;
+extern
+#ifdef __cplusplus
+"C"
+#endif
+const char *_nl_expand_alias (const char *);],
+ [bindtextdomain ("", "");
+return * gettext ("")$gt_expression_test_code + _nl_msg_cat_cntr + *_nl_expand_alias ("")],
+ [eval "$gt_func_gnugettext_libintl=yes"],
+ [eval "$gt_func_gnugettext_libintl=no"])
+ dnl Now see whether libintl exists and depends on libiconv.
+ if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" != yes; } && test -n "$LIBICONV"; then
+ LIBS="$LIBS $LIBICONV"
+ AC_TRY_LINK([#include <libintl.h>
+$gt_revision_test_code
+extern int _nl_msg_cat_cntr;
+extern
+#ifdef __cplusplus
+"C"
+#endif
+const char *_nl_expand_alias (const char *);],
+ [bindtextdomain ("", "");
+return * gettext ("")$gt_expression_test_code + _nl_msg_cat_cntr + *_nl_expand_alias ("")],
+ [LIBINTL="$LIBINTL $LIBICONV"
+ LTLIBINTL="$LTLIBINTL $LTLIBICONV"
+ eval "$gt_func_gnugettext_libintl=yes"
+ ])
+ fi
+ CPPFLAGS="$gt_save_CPPFLAGS"
+ LIBS="$gt_save_LIBS"])
+ fi
+
+ dnl If an already present or preinstalled GNU gettext() is found,
+ dnl use it. But if this macro is used in GNU gettext, and GNU
+ dnl gettext is already preinstalled in libintl, we update this
+ dnl libintl. (Cf. the install rule in intl/Makefile.in.)
+ if { eval "gt_val=\$$gt_func_gnugettext_libc"; test "$gt_val" = "yes"; } \
+ || { { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = "yes"; } \
+ && test "$PACKAGE" != gettext-runtime \
+ && test "$PACKAGE" != gettext-tools; }; then
+ gt_use_preinstalled_gnugettext=yes
+ else
+ dnl Reset the values set by searching for libintl.
+ LIBINTL=
+ LTLIBINTL=
+ INCINTL=
+ fi
+
+ ifelse(gt_included_intl, yes, [
+ if test "$gt_use_preinstalled_gnugettext" != "yes"; then
+ dnl GNU gettext is not found in the C library.
+ dnl Fall back on included GNU gettext library.
+ nls_cv_use_gnu_gettext=yes
+ fi
+ fi
+
+ if test "$nls_cv_use_gnu_gettext" = "yes"; then
+ dnl Mark actions used to generate GNU NLS library.
+ BUILD_INCLUDED_LIBINTL=yes
+ USE_INCLUDED_LIBINTL=yes
+ LIBINTL="ifelse([$3],[],\${top_builddir}/intl,[$3])/libintl.[]gt_libtool_suffix_prefix[]a $LIBICONV $LIBTHREAD"
+ LTLIBINTL="ifelse([$3],[],\${top_builddir}/intl,[$3])/libintl.[]gt_libtool_suffix_prefix[]a $LTLIBICONV $LTLIBTHREAD"
+ LIBS=`echo " $LIBS " | sed -e 's/ -lintl / /' -e 's/^ //' -e 's/ $//'`
+ fi
+
+ CATOBJEXT=
+ if test "$gt_use_preinstalled_gnugettext" = "yes" \
+ || test "$nls_cv_use_gnu_gettext" = "yes"; then
+ dnl Mark actions to use GNU gettext tools.
+ CATOBJEXT=.gmo
+ fi
+ ])
+
+ if test -n "$INTL_MACOSX_LIBS"; then
+ if test "$gt_use_preinstalled_gnugettext" = "yes" \
+ || test "$nls_cv_use_gnu_gettext" = "yes"; then
+ dnl Some extra flags are needed during linking.
+ LIBINTL="$LIBINTL $INTL_MACOSX_LIBS"
+ LTLIBINTL="$LTLIBINTL $INTL_MACOSX_LIBS"
+ fi
+ fi
+
+ if test "$gt_use_preinstalled_gnugettext" = "yes" \
+ || test "$nls_cv_use_gnu_gettext" = "yes"; then
+ AC_DEFINE(ENABLE_NLS, 1,
+ [Define to 1 if translation of program messages to the user's native language
+ is requested.])
+ else
+ USE_NLS=no
+ fi
+ fi
+
+ AC_MSG_CHECKING([whether to use NLS])
+ AC_MSG_RESULT([$USE_NLS])
+ if test "$USE_NLS" = "yes"; then
+ AC_MSG_CHECKING([where the gettext function comes from])
+ if test "$gt_use_preinstalled_gnugettext" = "yes"; then
+ if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = "yes"; }; then
+ gt_source="external libintl"
+ else
+ gt_source="libc"
+ fi
+ else
+ gt_source="included intl directory"
+ fi
+ AC_MSG_RESULT([$gt_source])
+ fi
+
+ if test "$USE_NLS" = "yes"; then
+
+ if test "$gt_use_preinstalled_gnugettext" = "yes"; then
+ if { eval "gt_val=\$$gt_func_gnugettext_libintl"; test "$gt_val" = "yes"; }; then
+ AC_MSG_CHECKING([how to link with libintl])
+ AC_MSG_RESULT([$LIBINTL])
+ AC_LIB_APPENDTOVAR([CPPFLAGS], [$INCINTL])
+ fi
+
+ dnl For backward compatibility. Some packages may be using this.
+ AC_DEFINE(HAVE_GETTEXT, 1,
+ [Define if the GNU gettext() function is already present or preinstalled.])
+ AC_DEFINE(HAVE_DCGETTEXT, 1,
+ [Define if the GNU dcgettext() function is already present or preinstalled.])
+ fi
+
+ dnl We need to process the po/ directory.
+ POSUB=po
+ fi
+
+ ifelse(gt_included_intl, yes, [
+ dnl If this is used in GNU gettext we have to set BUILD_INCLUDED_LIBINTL
+ dnl to 'yes' because some of the testsuite requires it.
+ if test "$PACKAGE" = gettext-runtime || test "$PACKAGE" = gettext-tools; then
+ BUILD_INCLUDED_LIBINTL=yes
+ fi
+
+ dnl Make all variables we use known to autoconf.
+ AC_SUBST(BUILD_INCLUDED_LIBINTL)
+ AC_SUBST(USE_INCLUDED_LIBINTL)
+ AC_SUBST(CATOBJEXT)
+
+ dnl For backward compatibility. Some configure.ins may be using this.
+ nls_cv_header_intl=
+ nls_cv_header_libgt=
+
+ dnl For backward compatibility. Some Makefiles may be using this.
+ DATADIRNAME=share
+ AC_SUBST(DATADIRNAME)
+
+ dnl For backward compatibility. Some Makefiles may be using this.
+ INSTOBJEXT=.mo
+ AC_SUBST(INSTOBJEXT)
+
+ dnl For backward compatibility. Some Makefiles may be using this.
+ GENCAT=gencat
+ AC_SUBST(GENCAT)
+
+ dnl For backward compatibility. Some Makefiles may be using this.
+ INTLOBJS=
+ if test "$USE_INCLUDED_LIBINTL" = yes; then
+ INTLOBJS="\$(GETTOBJS)"
+ fi
+ AC_SUBST(INTLOBJS)
+
+ dnl Enable libtool support if the surrounding package wishes it.
+ INTL_LIBTOOL_SUFFIX_PREFIX=gt_libtool_suffix_prefix
+ AC_SUBST(INTL_LIBTOOL_SUFFIX_PREFIX)
+ ])
+
+ dnl For backward compatibility. Some Makefiles may be using this.
+ INTLLIBS="$LIBINTL"
+ AC_SUBST(INTLLIBS)
+
+ dnl Make all documented variables known to autoconf.
+ AC_SUBST(LIBINTL)
+ AC_SUBST(LTLIBINTL)
+ AC_SUBST(POSUB)
+])
+
+
+dnl Checks for special options needed on MacOS X.
+dnl Defines INTL_MACOSX_LIBS.
+AC_DEFUN([gt_INTL_MACOSX],
+[
+ dnl Check for API introduced in MacOS X 10.2.
+ AC_CACHE_CHECK([for CFPreferencesCopyAppValue],
+ gt_cv_func_CFPreferencesCopyAppValue,
+ [gt_save_LIBS="$LIBS"
+ LIBS="$LIBS -Wl,-framework -Wl,CoreFoundation"
+ AC_TRY_LINK([#include <CoreFoundation/CFPreferences.h>],
+ [CFPreferencesCopyAppValue(NULL, NULL)],
+ [gt_cv_func_CFPreferencesCopyAppValue=yes],
+ [gt_cv_func_CFPreferencesCopyAppValue=no])
+ LIBS="$gt_save_LIBS"])
+ if test $gt_cv_func_CFPreferencesCopyAppValue = yes; then
+ AC_DEFINE([HAVE_CFPREFERENCESCOPYAPPVALUE], 1,
+ [Define to 1 if you have the MacOS X function CFPreferencesCopyAppValue in the CoreFoundation framework.])
+ fi
+ dnl Check for API introduced in MacOS X 10.3.
+ AC_CACHE_CHECK([for CFLocaleCopyCurrent], gt_cv_func_CFLocaleCopyCurrent,
+ [gt_save_LIBS="$LIBS"
+ LIBS="$LIBS -Wl,-framework -Wl,CoreFoundation"
+ AC_TRY_LINK([#include <CoreFoundation/CFLocale.h>], [CFLocaleCopyCurrent();],
+ [gt_cv_func_CFLocaleCopyCurrent=yes],
+ [gt_cv_func_CFLocaleCopyCurrent=no])
+ LIBS="$gt_save_LIBS"])
+ if test $gt_cv_func_CFLocaleCopyCurrent = yes; then
+ AC_DEFINE([HAVE_CFLOCALECOPYCURRENT], 1,
+ [Define to 1 if you have the MacOS X function CFLocaleCopyCurrent in the CoreFoundation framework.])
+ fi
+ INTL_MACOSX_LIBS=
+ if test $gt_cv_func_CFPreferencesCopyAppValue = yes || test $gt_cv_func_CFLocaleCopyCurrent = yes; then
+ INTL_MACOSX_LIBS="-Wl,-framework -Wl,CoreFoundation"
+ fi
+ AC_SUBST([INTL_MACOSX_LIBS])
+])
+
+
+dnl gt_NEEDS_INIT ensures that the gt_needs variable is initialized.
+m4_define([gt_NEEDS_INIT],
+[
+ m4_divert_text([DEFAULTS], [gt_needs=])
+ m4_define([gt_NEEDS_INIT], [])
+])
+
+
+dnl Usage: AM_GNU_GETTEXT_NEED([NEEDSYMBOL])
+AC_DEFUN([AM_GNU_GETTEXT_NEED],
+[
+ m4_divert_text([INIT_PREPARE], [gt_needs="$gt_needs $1"])
+])
+
+
+dnl Usage: AM_GNU_GETTEXT_VERSION([gettext-version])
+AC_DEFUN([AM_GNU_GETTEXT_VERSION], [])
diff --git a/m4/glibc2.m4 b/m4/glibc2.m4
new file mode 100644
index 00000000..e8f5bfe6
--- /dev/null
+++ b/m4/glibc2.m4
@@ -0,0 +1,30 @@
+# glibc2.m4 serial 1
+dnl Copyright (C) 2000-2002, 2004 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+# Test for the GNU C Library, version 2.0 or newer.
+# From Bruno Haible.
+
+AC_DEFUN([gt_GLIBC2],
+ [
+ AC_CACHE_CHECK(whether we are using the GNU C Library 2 or newer,
+ ac_cv_gnu_library_2,
+ [AC_EGREP_CPP([Lucky GNU user],
+ [
+#include <features.h>
+#ifdef __GNU_LIBRARY__
+ #if (__GLIBC__ >= 2)
+ Lucky GNU user
+ #endif
+#endif
+ ],
+ ac_cv_gnu_library_2=yes,
+ ac_cv_gnu_library_2=no)
+ ]
+ )
+ AC_SUBST(GLIBC2)
+ GLIBC2="$ac_cv_gnu_library_2"
+ ]
+)
diff --git a/m4/glibc21.m4 b/m4/glibc21.m4
new file mode 100644
index 00000000..d95fd986
--- /dev/null
+++ b/m4/glibc21.m4
@@ -0,0 +1,30 @@
+# glibc21.m4 serial 3
+dnl Copyright (C) 2000-2002, 2004 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+# Test for the GNU C Library, version 2.1 or newer.
+# From Bruno Haible.
+
+AC_DEFUN([gl_GLIBC21],
+ [
+ AC_CACHE_CHECK(whether we are using the GNU C Library 2.1 or newer,
+ ac_cv_gnu_library_2_1,
+ [AC_EGREP_CPP([Lucky GNU user],
+ [
+#include <features.h>
+#ifdef __GNU_LIBRARY__
+ #if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1) || (__GLIBC__ > 2)
+ Lucky GNU user
+ #endif
+#endif
+ ],
+ ac_cv_gnu_library_2_1=yes,
+ ac_cv_gnu_library_2_1=no)
+ ]
+ )
+ AC_SUBST(GLIBC21)
+ GLIBC21="$ac_cv_gnu_library_2_1"
+ ]
+)
diff --git a/m4/iconv.m4 b/m4/iconv.m4
new file mode 100644
index 00000000..654c4158
--- /dev/null
+++ b/m4/iconv.m4
@@ -0,0 +1,101 @@
+# iconv.m4 serial AM4 (gettext-0.11.3)
+dnl Copyright (C) 2000-2002 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Bruno Haible.
+
+AC_DEFUN([AM_ICONV_LINKFLAGS_BODY],
+[
+ dnl Prerequisites of AC_LIB_LINKFLAGS_BODY.
+ AC_REQUIRE([AC_LIB_PREPARE_PREFIX])
+ AC_REQUIRE([AC_LIB_RPATH])
+
+ dnl Search for libiconv and define LIBICONV, LTLIBICONV and INCICONV
+ dnl accordingly.
+ AC_LIB_LINKFLAGS_BODY([iconv])
+])
+
+AC_DEFUN([AM_ICONV_LINK],
+[
+ dnl Some systems have iconv in libc, some have it in libiconv (OSF/1 and
+ dnl those with the standalone portable GNU libiconv installed).
+
+ dnl Search for libiconv and define LIBICONV, LTLIBICONV and INCICONV
+ dnl accordingly.
+ AC_REQUIRE([AM_ICONV_LINKFLAGS_BODY])
+
+ dnl Add $INCICONV to CPPFLAGS before performing the following checks,
+ dnl because if the user has installed libiconv and not disabled its use
+ dnl via --without-libiconv-prefix, he wants to use it. The first
+ dnl AC_TRY_LINK will then fail, the second AC_TRY_LINK will succeed.
+ am_save_CPPFLAGS="$CPPFLAGS"
+ AC_LIB_APPENDTOVAR([CPPFLAGS], [$INCICONV])
+
+ AC_CACHE_CHECK(for iconv, am_cv_func_iconv, [
+ am_cv_func_iconv="no, consider installing GNU libiconv"
+ am_cv_lib_iconv=no
+ AC_TRY_LINK([#include <stdlib.h>
+#include <iconv.h>],
+ [iconv_t cd = iconv_open("","");
+ iconv(cd,NULL,NULL,NULL,NULL);
+ iconv_close(cd);],
+ am_cv_func_iconv=yes)
+ if test "$am_cv_func_iconv" != yes; then
+ am_save_LIBS="$LIBS"
+ LIBS="$LIBS $LIBICONV"
+ AC_TRY_LINK([#include <stdlib.h>
+#include <iconv.h>],
+ [iconv_t cd = iconv_open("","");
+ iconv(cd,NULL,NULL,NULL,NULL);
+ iconv_close(cd);],
+ am_cv_lib_iconv=yes
+ am_cv_func_iconv=yes)
+ LIBS="$am_save_LIBS"
+ fi
+ ])
+ if test "$am_cv_func_iconv" = yes; then
+ AC_DEFINE(HAVE_ICONV, 1, [Define if you have the iconv() function.])
+ fi
+ if test "$am_cv_lib_iconv" = yes; then
+ AC_MSG_CHECKING([how to link with libiconv])
+ AC_MSG_RESULT([$LIBICONV])
+ else
+ dnl If $LIBICONV didn't lead to a usable library, we don't need $INCICONV
+ dnl either.
+ CPPFLAGS="$am_save_CPPFLAGS"
+ LIBICONV=
+ LTLIBICONV=
+ fi
+ AC_SUBST(LIBICONV)
+ AC_SUBST(LTLIBICONV)
+])
+
+AC_DEFUN([AM_ICONV],
+[
+ AM_ICONV_LINK
+ if test "$am_cv_func_iconv" = yes; then
+ AC_MSG_CHECKING([for iconv declaration])
+ AC_CACHE_VAL(am_cv_proto_iconv, [
+ AC_TRY_COMPILE([
+#include <stdlib.h>
+#include <iconv.h>
+extern
+#ifdef __cplusplus
+"C"
+#endif
+#if defined(__STDC__) || defined(__cplusplus)
+size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);
+#else
+size_t iconv();
+#endif
+], [], am_cv_proto_iconv_arg1="", am_cv_proto_iconv_arg1="const")
+ am_cv_proto_iconv="extern size_t iconv (iconv_t cd, $am_cv_proto_iconv_arg1 char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);"])
+ am_cv_proto_iconv=`echo "[$]am_cv_proto_iconv" | tr -s ' ' | sed -e 's/( /(/'`
+ AC_MSG_RESULT([$]{ac_t:-
+ }[$]am_cv_proto_iconv)
+ AC_DEFINE_UNQUOTED(ICONV_CONST, $am_cv_proto_iconv_arg1,
+ [Define as const if the declaration of iconv() needs const.])
+ fi
+])
diff --git a/m4/intdiv0.m4 b/m4/intdiv0.m4
new file mode 100644
index 00000000..b8d78176
--- /dev/null
+++ b/m4/intdiv0.m4
@@ -0,0 +1,70 @@
+# intdiv0.m4 serial 1 (gettext-0.11.3)
+dnl Copyright (C) 2002 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Bruno Haible.
+
+AC_DEFUN([gt_INTDIV0],
+[
+ AC_REQUIRE([AC_PROG_CC])dnl
+ AC_REQUIRE([AC_CANONICAL_HOST])dnl
+
+ AC_CACHE_CHECK([whether integer division by zero raises SIGFPE],
+ gt_cv_int_divbyzero_sigfpe,
+ [
+ AC_TRY_RUN([
+#include <stdlib.h>
+#include <signal.h>
+
+static void
+#ifdef __cplusplus
+sigfpe_handler (int sig)
+#else
+sigfpe_handler (sig) int sig;
+#endif
+{
+ /* Exit with code 0 if SIGFPE, with code 1 if any other signal. */
+ exit (sig != SIGFPE);
+}
+
+int x = 1;
+int y = 0;
+int z;
+int nan;
+
+int main ()
+{
+ signal (SIGFPE, sigfpe_handler);
+/* IRIX and AIX (when "xlc -qcheck" is used) yield signal SIGTRAP. */
+#if (defined (__sgi) || defined (_AIX)) && defined (SIGTRAP)
+ signal (SIGTRAP, sigfpe_handler);
+#endif
+/* Linux/SPARC yields signal SIGILL. */
+#if defined (__sparc__) && defined (__linux__)
+ signal (SIGILL, sigfpe_handler);
+#endif
+
+ z = x / y;
+ nan = y / y;
+ exit (1);
+}
+], gt_cv_int_divbyzero_sigfpe=yes, gt_cv_int_divbyzero_sigfpe=no,
+ [
+ # Guess based on the CPU.
+ case "$host_cpu" in
+ alpha* | i[34567]86 | m68k | s390*)
+ gt_cv_int_divbyzero_sigfpe="guessing yes";;
+ *)
+ gt_cv_int_divbyzero_sigfpe="guessing no";;
+ esac
+ ])
+ ])
+ case "$gt_cv_int_divbyzero_sigfpe" in
+ *yes) value=1;;
+ *) value=0;;
+ esac
+ AC_DEFINE_UNQUOTED(INTDIV0_RAISES_SIGFPE, $value,
+ [Define if integer division by zero raises signal SIGFPE.])
+])
diff --git a/m4/intl.m4 b/m4/intl.m4
new file mode 100644
index 00000000..dcefb118
--- /dev/null
+++ b/m4/intl.m4
@@ -0,0 +1,259 @@
+# intl.m4 serial 3 (gettext-0.16)
+dnl Copyright (C) 1995-2006 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+dnl
+dnl This file can can be used in projects which are not available under
+dnl the GNU General Public License or the GNU Library General Public
+dnl License but which still want to provide support for the GNU gettext
+dnl functionality.
+dnl Please note that the actual code of the GNU gettext library is covered
+dnl by the GNU Library General Public License, and the rest of the GNU
+dnl gettext package package is covered by the GNU General Public License.
+dnl They are *not* in the public domain.
+
+dnl Authors:
+dnl Ulrich Drepper <drepper@cygnus.com>, 1995-2000.
+dnl Bruno Haible <haible@clisp.cons.org>, 2000-2006.
+
+AC_PREREQ(2.52)
+
+dnl Checks for all prerequisites of the intl subdirectory,
+dnl except for INTL_LIBTOOL_SUFFIX_PREFIX (and possibly LIBTOOL), INTLOBJS,
+dnl USE_INCLUDED_LIBINTL, BUILD_INCLUDED_LIBINTL.
+AC_DEFUN([AM_INTL_SUBDIR],
+[
+ AC_REQUIRE([AC_PROG_INSTALL])dnl
+ AC_REQUIRE([AM_PROG_MKDIR_P])dnl defined by automake
+ AC_REQUIRE([AC_PROG_CC])dnl
+ AC_REQUIRE([AC_CANONICAL_HOST])dnl
+ AC_REQUIRE([gt_GLIBC2])dnl
+ AC_REQUIRE([AC_PROG_RANLIB])dnl
+ AC_REQUIRE([gl_VISIBILITY])dnl
+ AC_REQUIRE([gt_INTL_SUBDIR_CORE])dnl
+ AC_REQUIRE([AC_TYPE_LONG_LONG_INT])dnl
+ AC_REQUIRE([gt_TYPE_LONGDOUBLE])dnl
+ AC_REQUIRE([gt_TYPE_WCHAR_T])dnl
+ AC_REQUIRE([gt_TYPE_WINT_T])dnl
+ AC_REQUIRE([gl_AC_HEADER_INTTYPES_H])
+ AC_REQUIRE([gt_TYPE_INTMAX_T])
+ AC_REQUIRE([gt_PRINTF_POSIX])
+ AC_REQUIRE([gl_GLIBC21])dnl
+ AC_REQUIRE([gl_XSIZE])dnl
+ AC_REQUIRE([gt_INTL_MACOSX])dnl
+
+ AC_CHECK_TYPE([ptrdiff_t], ,
+ [AC_DEFINE([ptrdiff_t], [long],
+ [Define as the type of the result of subtracting two pointers, if the system doesn't define it.])
+ ])
+ AC_CHECK_HEADERS([stddef.h stdlib.h string.h])
+ AC_CHECK_FUNCS([asprintf fwprintf putenv setenv setlocale snprintf wcslen])
+
+ dnl Use the _snprintf function only if it is declared (because on NetBSD it
+ dnl is defined as a weak alias of snprintf; we prefer to use the latter).
+ gt_CHECK_DECL(_snprintf, [#include <stdio.h>])
+ gt_CHECK_DECL(_snwprintf, [#include <stdio.h>])
+
+ dnl Use the *_unlocked functions only if they are declared.
+ dnl (because some of them were defined without being declared in Solaris
+ dnl 2.5.1 but were removed in Solaris 2.6, whereas we want binaries built
+ dnl on Solaris 2.5.1 to run on Solaris 2.6).
+ dnl Don't use AC_CHECK_DECLS because it isn't supported in autoconf-2.13.
+ gt_CHECK_DECL(getc_unlocked, [#include <stdio.h>])
+
+ case $gt_cv_func_printf_posix in
+ *yes) HAVE_POSIX_PRINTF=1 ;;
+ *) HAVE_POSIX_PRINTF=0 ;;
+ esac
+ AC_SUBST([HAVE_POSIX_PRINTF])
+ if test "$ac_cv_func_asprintf" = yes; then
+ HAVE_ASPRINTF=1
+ else
+ HAVE_ASPRINTF=0
+ fi
+ AC_SUBST([HAVE_ASPRINTF])
+ if test "$ac_cv_func_snprintf" = yes; then
+ HAVE_SNPRINTF=1
+ else
+ HAVE_SNPRINTF=0
+ fi
+ AC_SUBST([HAVE_SNPRINTF])
+ if test "$ac_cv_func_wprintf" = yes; then
+ HAVE_WPRINTF=1
+ else
+ HAVE_WPRINTF=0
+ fi
+ AC_SUBST([HAVE_WPRINTF])
+
+ AM_LANGINFO_CODESET
+ gt_LC_MESSAGES
+
+ dnl Compilation on mingw and Cygwin needs special Makefile rules, because
+ dnl 1. when we install a shared library, we must arrange to export
+ dnl auxiliary pointer variables for every exported variable,
+ dnl 2. when we install a shared library and a static library simultaneously,
+ dnl the include file specifies __declspec(dllimport) and therefore we
+ dnl must arrange to define the auxiliary pointer variables for the
+ dnl exported variables _also_ in the static library.
+ if test "$enable_shared" = yes; then
+ case "$host_os" in
+ cygwin*) is_woe32dll=yes ;;
+ *) is_woe32dll=no ;;
+ esac
+ else
+ is_woe32dll=no
+ fi
+ WOE32DLL=$is_woe32dll
+ AC_SUBST([WOE32DLL])
+
+ dnl Rename some macros and functions used for locking.
+ AH_BOTTOM([
+#define __libc_lock_t gl_lock_t
+#define __libc_lock_define gl_lock_define
+#define __libc_lock_define_initialized gl_lock_define_initialized
+#define __libc_lock_init gl_lock_init
+#define __libc_lock_lock gl_lock_lock
+#define __libc_lock_unlock gl_lock_unlock
+#define __libc_lock_recursive_t gl_recursive_lock_t
+#define __libc_lock_define_recursive gl_recursive_lock_define
+#define __libc_lock_define_initialized_recursive gl_recursive_lock_define_initialized
+#define __libc_lock_init_recursive gl_recursive_lock_init
+#define __libc_lock_lock_recursive gl_recursive_lock_lock
+#define __libc_lock_unlock_recursive gl_recursive_lock_unlock
+#define glthread_in_use libintl_thread_in_use
+#define glthread_lock_init libintl_lock_init
+#define glthread_lock_lock libintl_lock_lock
+#define glthread_lock_unlock libintl_lock_unlock
+#define glthread_lock_destroy libintl_lock_destroy
+#define glthread_rwlock_init libintl_rwlock_init
+#define glthread_rwlock_rdlock libintl_rwlock_rdlock
+#define glthread_rwlock_wrlock libintl_rwlock_wrlock
+#define glthread_rwlock_unlock libintl_rwlock_unlock
+#define glthread_rwlock_destroy libintl_rwlock_destroy
+#define glthread_recursive_lock_init libintl_recursive_lock_init
+#define glthread_recursive_lock_lock libintl_recursive_lock_lock
+#define glthread_recursive_lock_unlock libintl_recursive_lock_unlock
+#define glthread_recursive_lock_destroy libintl_recursive_lock_destroy
+#define glthread_once libintl_once
+#define glthread_once_call libintl_once_call
+#define glthread_once_singlethreaded libintl_once_singlethreaded
+])
+])
+
+
+dnl Checks for the core files of the intl subdirectory:
+dnl dcigettext.c
+dnl eval-plural.h
+dnl explodename.c
+dnl finddomain.c
+dnl gettextP.h
+dnl gmo.h
+dnl hash-string.h hash-string.c
+dnl l10nflist.c
+dnl libgnuintl.h.in (except the *printf stuff)
+dnl loadinfo.h
+dnl loadmsgcat.c
+dnl localealias.c
+dnl log.c
+dnl plural-exp.h plural-exp.c
+dnl plural.y
+dnl Used by libglocale.
+AC_DEFUN([gt_INTL_SUBDIR_CORE],
+[
+ AC_REQUIRE([AC_C_INLINE])dnl
+ AC_REQUIRE([AC_TYPE_SIZE_T])dnl
+ AC_REQUIRE([gl_AC_HEADER_STDINT_H])
+ AC_REQUIRE([AC_FUNC_ALLOCA])dnl
+ AC_REQUIRE([AC_FUNC_MMAP])dnl
+ AC_REQUIRE([gt_INTDIV0])dnl
+ AC_REQUIRE([gl_AC_TYPE_UINTMAX_T])dnl
+ AC_REQUIRE([gt_INTTYPES_PRI])dnl
+ AC_REQUIRE([gl_LOCK])dnl
+
+ AC_TRY_LINK(
+ [int foo (int a) { a = __builtin_expect (a, 10); return a == 10 ? 0 : 1; }],
+ [],
+ [AC_DEFINE([HAVE_BUILTIN_EXPECT], 1,
+ [Define to 1 if the compiler understands __builtin_expect.])])
+
+ AC_CHECK_HEADERS([argz.h inttypes.h limits.h unistd.h sys/param.h])
+ AC_CHECK_FUNCS([getcwd getegid geteuid getgid getuid mempcpy munmap \
+ stpcpy strcasecmp strdup strtoul tsearch argz_count argz_stringify \
+ argz_next __fsetlocking])
+
+ dnl Use the *_unlocked functions only if they are declared.
+ dnl (because some of them were defined without being declared in Solaris
+ dnl 2.5.1 but were removed in Solaris 2.6, whereas we want binaries built
+ dnl on Solaris 2.5.1 to run on Solaris 2.6).
+ dnl Don't use AC_CHECK_DECLS because it isn't supported in autoconf-2.13.
+ gt_CHECK_DECL(feof_unlocked, [#include <stdio.h>])
+ gt_CHECK_DECL(fgets_unlocked, [#include <stdio.h>])
+
+ AM_ICONV
+
+ dnl glibc >= 2.4 has a NL_LOCALE_NAME macro when _GNU_SOURCE is defined,
+ dnl and a _NL_LOCALE_NAME macro always.
+ AC_CACHE_CHECK([for NL_LOCALE_NAME macro], gt_cv_nl_locale_name,
+ [AC_TRY_LINK([#include <langinfo.h>
+#include <locale.h>],
+ [char* cs = nl_langinfo(_NL_LOCALE_NAME(LC_MESSAGES));],
+ gt_cv_nl_locale_name=yes,
+ gt_cv_nl_locale_name=no)
+ ])
+ if test $gt_cv_nl_locale_name = yes; then
+ AC_DEFINE(HAVE_NL_LOCALE_NAME, 1,
+ [Define if you have <langinfo.h> and it defines the NL_LOCALE_NAME macro if _GNU_SOURCE is defined.])
+ fi
+
+ dnl intl/plural.c is generated from intl/plural.y. It requires bison,
+ dnl because plural.y uses bison specific features. It requires at least
+ dnl bison-1.26 because earlier versions generate a plural.c that doesn't
+ dnl compile.
+ dnl bison is only needed for the maintainer (who touches plural.y). But in
+ dnl order to avoid separate Makefiles or --enable-maintainer-mode, we put
+ dnl the rule in general Makefile. Now, some people carelessly touch the
+ dnl files or have a broken "make" program, hence the plural.c rule will
+ dnl sometimes fire. To avoid an error, defines BISON to ":" if it is not
+ dnl present or too old.
+ AC_CHECK_PROGS([INTLBISON], [bison])
+ if test -z "$INTLBISON"; then
+ ac_verc_fail=yes
+ else
+ dnl Found it, now check the version.
+ AC_MSG_CHECKING([version of bison])
+changequote(<<,>>)dnl
+ ac_prog_version=`$INTLBISON --version 2>&1 | sed -n 's/^.*GNU Bison.* \([0-9]*\.[0-9.]*\).*$/\1/p'`
+ case $ac_prog_version in
+ '') ac_prog_version="v. ?.??, bad"; ac_verc_fail=yes;;
+ 1.2[6-9]* | 1.[3-9][0-9]* | [2-9].*)
+changequote([,])dnl
+ ac_prog_version="$ac_prog_version, ok"; ac_verc_fail=no;;
+ *) ac_prog_version="$ac_prog_version, bad"; ac_verc_fail=yes;;
+ esac
+ AC_MSG_RESULT([$ac_prog_version])
+ fi
+ if test $ac_verc_fail = yes; then
+ INTLBISON=:
+ fi
+])
+
+
+dnl gt_CHECK_DECL(FUNC, INCLUDES)
+dnl Check whether a function is declared.
+AC_DEFUN([gt_CHECK_DECL],
+[
+ AC_CACHE_CHECK([whether $1 is declared], ac_cv_have_decl_$1,
+ [AC_TRY_COMPILE([$2], [
+#ifndef $1
+ char *p = (char *) $1;
+#endif
+], ac_cv_have_decl_$1=yes, ac_cv_have_decl_$1=no)])
+ if test $ac_cv_have_decl_$1 = yes; then
+ gt_value=1
+ else
+ gt_value=0
+ fi
+ AC_DEFINE_UNQUOTED([HAVE_DECL_]translit($1, [a-z], [A-Z]), [$gt_value],
+ [Define to 1 if you have the declaration of `$1', and to 0 if you don't.])
+])
diff --git a/m4/intldir.m4 b/m4/intldir.m4
new file mode 100644
index 00000000..7a28843f
--- /dev/null
+++ b/m4/intldir.m4
@@ -0,0 +1,19 @@
+# intldir.m4 serial 1 (gettext-0.16)
+dnl Copyright (C) 2006 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+dnl
+dnl This file can can be used in projects which are not available under
+dnl the GNU General Public License or the GNU Library General Public
+dnl License but which still want to provide support for the GNU gettext
+dnl functionality.
+dnl Please note that the actual code of the GNU gettext library is covered
+dnl by the GNU Library General Public License, and the rest of the GNU
+dnl gettext package package is covered by the GNU General Public License.
+dnl They are *not* in the public domain.
+
+AC_PREREQ(2.52)
+
+dnl Tells the AM_GNU_GETTEXT macro to consider an intl/ directory.
+AC_DEFUN([AM_GNU_GETTEXT_INTL_SUBDIR], [])
diff --git a/m4/intmax.m4 b/m4/intmax.m4
new file mode 100644
index 00000000..ce7a8a49
--- /dev/null
+++ b/m4/intmax.m4
@@ -0,0 +1,33 @@
+# intmax.m4 serial 3 (gettext-0.16)
+dnl Copyright (C) 2002-2005 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Bruno Haible.
+dnl Test whether the system has the 'intmax_t' type, but don't attempt to
+dnl find a replacement if it is lacking.
+
+AC_DEFUN([gt_TYPE_INTMAX_T],
+[
+ AC_REQUIRE([gl_AC_HEADER_INTTYPES_H])
+ AC_REQUIRE([gl_AC_HEADER_STDINT_H])
+ AC_CACHE_CHECK(for intmax_t, gt_cv_c_intmax_t,
+ [AC_TRY_COMPILE([
+#include <stddef.h>
+#include <stdlib.h>
+#if HAVE_STDINT_H_WITH_UINTMAX
+#include <stdint.h>
+#endif
+#if HAVE_INTTYPES_H_WITH_UINTMAX
+#include <inttypes.h>
+#endif
+], [intmax_t x = -1;
+ return !x;],
+ gt_cv_c_intmax_t=yes,
+ gt_cv_c_intmax_t=no)])
+ if test $gt_cv_c_intmax_t = yes; then
+ AC_DEFINE(HAVE_INTMAX_T, 1,
+ [Define if you have the 'intmax_t' type in <stdint.h> or <inttypes.h>.])
+ fi
+])
diff --git a/m4/inttypes-pri.m4 b/m4/inttypes-pri.m4
new file mode 100644
index 00000000..7c7f8940
--- /dev/null
+++ b/m4/inttypes-pri.m4
@@ -0,0 +1,36 @@
+# inttypes-pri.m4 serial 4 (gettext-0.16)
+dnl Copyright (C) 1997-2002, 2006 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Bruno Haible.
+
+AC_PREREQ(2.52)
+
+# Define PRI_MACROS_BROKEN if <inttypes.h> exists and defines the PRI*
+# macros to non-string values. This is the case on AIX 4.3.3.
+
+AC_DEFUN([gt_INTTYPES_PRI],
+[
+ AC_CHECK_HEADERS([inttypes.h])
+ if test $ac_cv_header_inttypes_h = yes; then
+ AC_CACHE_CHECK([whether the inttypes.h PRIxNN macros are broken],
+ gt_cv_inttypes_pri_broken,
+ [
+ AC_TRY_COMPILE([#include <inttypes.h>
+#ifdef PRId32
+char *p = PRId32;
+#endif
+], [], gt_cv_inttypes_pri_broken=no, gt_cv_inttypes_pri_broken=yes)
+ ])
+ fi
+ if test "$gt_cv_inttypes_pri_broken" = yes; then
+ AC_DEFINE_UNQUOTED(PRI_MACROS_BROKEN, 1,
+ [Define if <inttypes.h> exists and defines unusable PRI* macros.])
+ PRI_MACROS_BROKEN=1
+ else
+ PRI_MACROS_BROKEN=0
+ fi
+ AC_SUBST([PRI_MACROS_BROKEN])
+])
diff --git a/m4/inttypes.m4 b/m4/inttypes.m4
new file mode 100644
index 00000000..779bcea0
--- /dev/null
+++ b/m4/inttypes.m4
@@ -0,0 +1,25 @@
+# inttypes.m4 serial 1 (gettext-0.11.4)
+dnl Copyright (C) 1997-2002 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Paul Eggert.
+
+# Define HAVE_INTTYPES_H if <inttypes.h> exists and doesn't clash with
+# <sys/types.h>.
+
+AC_DEFUN([gt_HEADER_INTTYPES_H],
+[
+ AC_CACHE_CHECK([for inttypes.h], gt_cv_header_inttypes_h,
+ [
+ AC_TRY_COMPILE(
+ [#include <sys/types.h>
+#include <inttypes.h>],
+ [], gt_cv_header_inttypes_h=yes, gt_cv_header_inttypes_h=no)
+ ])
+ if test $gt_cv_header_inttypes_h = yes; then
+ AC_DEFINE_UNQUOTED(HAVE_INTTYPES_H, 1,
+ [Define if <inttypes.h> exists and doesn't clash with <sys/types.h>.])
+ fi
+])
diff --git a/m4/inttypes_h.m4 b/m4/inttypes_h.m4
new file mode 100644
index 00000000..edc8ecb2
--- /dev/null
+++ b/m4/inttypes_h.m4
@@ -0,0 +1,26 @@
+# inttypes_h.m4 serial 7
+dnl Copyright (C) 1997-2004, 2006 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Paul Eggert.
+
+# Define HAVE_INTTYPES_H_WITH_UINTMAX if <inttypes.h> exists,
+# doesn't clash with <sys/types.h>, and declares uintmax_t.
+
+AC_DEFUN([gl_AC_HEADER_INTTYPES_H],
+[
+ AC_CACHE_CHECK([for inttypes.h], gl_cv_header_inttypes_h,
+ [AC_TRY_COMPILE(
+ [#include <sys/types.h>
+#include <inttypes.h>],
+ [uintmax_t i = (uintmax_t) -1; return !i;],
+ gl_cv_header_inttypes_h=yes,
+ gl_cv_header_inttypes_h=no)])
+ if test $gl_cv_header_inttypes_h = yes; then
+ AC_DEFINE_UNQUOTED(HAVE_INTTYPES_H_WITH_UINTMAX, 1,
+ [Define if <inttypes.h> exists, doesn't clash with <sys/types.h>,
+ and declares uintmax_t. ])
+ fi
+])
diff --git a/m4/isc-posix.m4 b/m4/isc-posix.m4
new file mode 100644
index 00000000..74dc8f26
--- /dev/null
+++ b/m4/isc-posix.m4
@@ -0,0 +1,24 @@
+# isc-posix.m4 serial 2 (gettext-0.11.2)
+dnl Copyright (C) 1995-2002 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+# This file is not needed with autoconf-2.53 and newer. Remove it in 2005.
+
+# This test replaces the one in autoconf.
+# Currently this macro should have the same name as the autoconf macro
+# because gettext's gettext.m4 (distributed in the automake package)
+# still uses it. Otherwise, the use in gettext.m4 makes autoheader
+# give these diagnostics:
+# configure.in:556: AC_TRY_COMPILE was called before AC_ISC_POSIX
+# configure.in:556: AC_TRY_RUN was called before AC_ISC_POSIX
+
+undefine([AC_ISC_POSIX])
+
+AC_DEFUN([AC_ISC_POSIX],
+ [
+ dnl This test replaces the obsolescent AC_ISC_POSIX kludge.
+ AC_CHECK_LIB(cposix, strerror, [LIBS="$LIBS -lcposix"])
+ ]
+)
diff --git a/m4/lcmessage.m4 b/m4/lcmessage.m4
new file mode 100644
index 00000000..19aa77e4
--- /dev/null
+++ b/m4/lcmessage.m4
@@ -0,0 +1,30 @@
+# lcmessage.m4 serial 4 (gettext-0.14.2)
+dnl Copyright (C) 1995-2002, 2004-2005 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+dnl
+dnl This file can can be used in projects which are not available under
+dnl the GNU General Public License or the GNU Library General Public
+dnl License but which still want to provide support for the GNU gettext
+dnl functionality.
+dnl Please note that the actual code of the GNU gettext library is covered
+dnl by the GNU Library General Public License, and the rest of the GNU
+dnl gettext package package is covered by the GNU General Public License.
+dnl They are *not* in the public domain.
+
+dnl Authors:
+dnl Ulrich Drepper <drepper@cygnus.com>, 1995.
+
+# Check whether LC_MESSAGES is available in <locale.h>.
+
+AC_DEFUN([gt_LC_MESSAGES],
+[
+ AC_CACHE_CHECK([for LC_MESSAGES], gt_cv_val_LC_MESSAGES,
+ [AC_TRY_LINK([#include <locale.h>], [return LC_MESSAGES],
+ gt_cv_val_LC_MESSAGES=yes, gt_cv_val_LC_MESSAGES=no)])
+ if test $gt_cv_val_LC_MESSAGES = yes; then
+ AC_DEFINE(HAVE_LC_MESSAGES, 1,
+ [Define if your <locale.h> file defines LC_MESSAGES.])
+ fi
+])
diff --git a/m4/lib-ld.m4 b/m4/lib-ld.m4
new file mode 100644
index 00000000..96c4e2c3
--- /dev/null
+++ b/m4/lib-ld.m4
@@ -0,0 +1,110 @@
+# lib-ld.m4 serial 3 (gettext-0.13)
+dnl Copyright (C) 1996-2003 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl Subroutines of libtool.m4,
+dnl with replacements s/AC_/AC_LIB/ and s/lt_cv/acl_cv/ to avoid collision
+dnl with libtool.m4.
+
+dnl From libtool-1.4. Sets the variable with_gnu_ld to yes or no.
+AC_DEFUN([AC_LIB_PROG_LD_GNU],
+[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], acl_cv_prog_gnu_ld,
+[# I'd rather use --version here, but apparently some GNU ld's only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+ acl_cv_prog_gnu_ld=yes ;;
+*)
+ acl_cv_prog_gnu_ld=no ;;
+esac])
+with_gnu_ld=$acl_cv_prog_gnu_ld
+])
+
+dnl From libtool-1.4. Sets the variable LD.
+AC_DEFUN([AC_LIB_PROG_LD],
+[AC_ARG_WITH(gnu-ld,
+[ --with-gnu-ld assume the C compiler uses GNU ld [default=no]],
+test "$withval" = no || with_gnu_ld=yes, with_gnu_ld=no)
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_CANONICAL_HOST])dnl
+# Prepare PATH_SEPARATOR.
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+ac_prog=ld
+if test "$GCC" = yes; then
+ # Check if gcc -print-prog-name=ld gives a path.
+ AC_MSG_CHECKING([for ld used by GCC])
+ case $host in
+ *-*-mingw*)
+ # gcc leaves a trailing carriage return which upsets mingw
+ ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+ *)
+ ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+ esac
+ case $ac_prog in
+ # Accept absolute paths.
+ [[\\/]* | [A-Za-z]:[\\/]*)]
+ [re_direlt='/[^/][^/]*/\.\./']
+ # Canonicalize the path of ld
+ ac_prog=`echo $ac_prog| sed 's%\\\\%/%g'`
+ while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do
+ ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"`
+ done
+ test -z "$LD" && LD="$ac_prog"
+ ;;
+ "")
+ # If it fails, then pretend we aren't using GCC.
+ ac_prog=ld
+ ;;
+ *)
+ # If it is relative, then search for the first ld in PATH.
+ with_gnu_ld=unknown
+ ;;
+ esac
+elif test "$with_gnu_ld" = yes; then
+ AC_MSG_CHECKING([for GNU ld])
+else
+ AC_MSG_CHECKING([for non-GNU ld])
+fi
+AC_CACHE_VAL(acl_cv_path_LD,
+[if test -z "$LD"; then
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR-:}"
+ for ac_dir in $PATH; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+ acl_cv_path_LD="$ac_dir/$ac_prog"
+ # Check to see if the program is GNU ld. I'd rather use --version,
+ # but apparently some GNU ld's only accept -v.
+ # Break only if it was the GNU/non-GNU ld that we prefer.
+ case `"$acl_cv_path_LD" -v 2>&1 < /dev/null` in
+ *GNU* | *'with BFD'*)
+ test "$with_gnu_ld" != no && break ;;
+ *)
+ test "$with_gnu_ld" != yes && break ;;
+ esac
+ fi
+ done
+ IFS="$ac_save_ifs"
+else
+ acl_cv_path_LD="$LD" # Let the user override the test with a path.
+fi])
+LD="$acl_cv_path_LD"
+if test -n "$LD"; then
+ AC_MSG_RESULT($LD)
+else
+ AC_MSG_RESULT(no)
+fi
+test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH])
+AC_LIB_PROG_LD_GNU
+])
diff --git a/m4/lib-link.m4 b/m4/lib-link.m4
new file mode 100644
index 00000000..f95b7ba8
--- /dev/null
+++ b/m4/lib-link.m4
@@ -0,0 +1,644 @@
+# lib-link.m4 serial 9 (gettext-0.16)
+dnl Copyright (C) 2001-2006 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Bruno Haible.
+
+AC_PREREQ(2.50)
+
+dnl AC_LIB_LINKFLAGS(name [, dependencies]) searches for libname and
+dnl the libraries corresponding to explicit and implicit dependencies.
+dnl Sets and AC_SUBSTs the LIB${NAME} and LTLIB${NAME} variables and
+dnl augments the CPPFLAGS variable.
+AC_DEFUN([AC_LIB_LINKFLAGS],
+[
+ AC_REQUIRE([AC_LIB_PREPARE_PREFIX])
+ AC_REQUIRE([AC_LIB_RPATH])
+ define([Name],[translit([$1],[./-], [___])])
+ define([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-],
+ [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])])
+ AC_CACHE_CHECK([how to link with lib[]$1], [ac_cv_lib[]Name[]_libs], [
+ AC_LIB_LINKFLAGS_BODY([$1], [$2])
+ ac_cv_lib[]Name[]_libs="$LIB[]NAME"
+ ac_cv_lib[]Name[]_ltlibs="$LTLIB[]NAME"
+ ac_cv_lib[]Name[]_cppflags="$INC[]NAME"
+ ])
+ LIB[]NAME="$ac_cv_lib[]Name[]_libs"
+ LTLIB[]NAME="$ac_cv_lib[]Name[]_ltlibs"
+ INC[]NAME="$ac_cv_lib[]Name[]_cppflags"
+ AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME)
+ AC_SUBST([LIB]NAME)
+ AC_SUBST([LTLIB]NAME)
+ dnl Also set HAVE_LIB[]NAME so that AC_LIB_HAVE_LINKFLAGS can reuse the
+ dnl results of this search when this library appears as a dependency.
+ HAVE_LIB[]NAME=yes
+ undefine([Name])
+ undefine([NAME])
+])
+
+dnl AC_LIB_HAVE_LINKFLAGS(name, dependencies, includes, testcode)
+dnl searches for libname and the libraries corresponding to explicit and
+dnl implicit dependencies, together with the specified include files and
+dnl the ability to compile and link the specified testcode. If found, it
+dnl sets and AC_SUBSTs HAVE_LIB${NAME}=yes and the LIB${NAME} and
+dnl LTLIB${NAME} variables and augments the CPPFLAGS variable, and
+dnl #defines HAVE_LIB${NAME} to 1. Otherwise, it sets and AC_SUBSTs
+dnl HAVE_LIB${NAME}=no and LIB${NAME} and LTLIB${NAME} to empty.
+AC_DEFUN([AC_LIB_HAVE_LINKFLAGS],
+[
+ AC_REQUIRE([AC_LIB_PREPARE_PREFIX])
+ AC_REQUIRE([AC_LIB_RPATH])
+ define([Name],[translit([$1],[./-], [___])])
+ define([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-],
+ [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])])
+
+ dnl Search for lib[]Name and define LIB[]NAME, LTLIB[]NAME and INC[]NAME
+ dnl accordingly.
+ AC_LIB_LINKFLAGS_BODY([$1], [$2])
+
+ dnl Add $INC[]NAME to CPPFLAGS before performing the following checks,
+ dnl because if the user has installed lib[]Name and not disabled its use
+ dnl via --without-lib[]Name-prefix, he wants to use it.
+ ac_save_CPPFLAGS="$CPPFLAGS"
+ AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME)
+
+ AC_CACHE_CHECK([for lib[]$1], [ac_cv_lib[]Name], [
+ ac_save_LIBS="$LIBS"
+ LIBS="$LIBS $LIB[]NAME"
+ AC_TRY_LINK([$3], [$4], [ac_cv_lib[]Name=yes], [ac_cv_lib[]Name=no])
+ LIBS="$ac_save_LIBS"
+ ])
+ if test "$ac_cv_lib[]Name" = yes; then
+ HAVE_LIB[]NAME=yes
+ AC_DEFINE([HAVE_LIB]NAME, 1, [Define if you have the $1 library.])
+ AC_MSG_CHECKING([how to link with lib[]$1])
+ AC_MSG_RESULT([$LIB[]NAME])
+ else
+ HAVE_LIB[]NAME=no
+ dnl If $LIB[]NAME didn't lead to a usable library, we don't need
+ dnl $INC[]NAME either.
+ CPPFLAGS="$ac_save_CPPFLAGS"
+ LIB[]NAME=
+ LTLIB[]NAME=
+ fi
+ AC_SUBST([HAVE_LIB]NAME)
+ AC_SUBST([LIB]NAME)
+ AC_SUBST([LTLIB]NAME)
+ undefine([Name])
+ undefine([NAME])
+])
+
+dnl Determine the platform dependent parameters needed to use rpath:
+dnl libext, shlibext, hardcode_libdir_flag_spec, hardcode_libdir_separator,
+dnl hardcode_direct, hardcode_minus_L.
+AC_DEFUN([AC_LIB_RPATH],
+[
+ dnl Tell automake >= 1.10 to complain if config.rpath is missing.
+ m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([config.rpath])])
+ AC_REQUIRE([AC_PROG_CC]) dnl we use $CC, $GCC, $LDFLAGS
+ AC_REQUIRE([AC_LIB_PROG_LD]) dnl we use $LD, $with_gnu_ld
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl we use $host
+ AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT]) dnl we use $ac_aux_dir
+ AC_CACHE_CHECK([for shared library run path origin], acl_cv_rpath, [
+ CC="$CC" GCC="$GCC" LDFLAGS="$LDFLAGS" LD="$LD" with_gnu_ld="$with_gnu_ld" \
+ ${CONFIG_SHELL-/bin/sh} "$ac_aux_dir/config.rpath" "$host" > conftest.sh
+ . ./conftest.sh
+ rm -f ./conftest.sh
+ acl_cv_rpath=done
+ ])
+ wl="$acl_cv_wl"
+ libext="$acl_cv_libext"
+ shlibext="$acl_cv_shlibext"
+ hardcode_libdir_flag_spec="$acl_cv_hardcode_libdir_flag_spec"
+ hardcode_libdir_separator="$acl_cv_hardcode_libdir_separator"
+ hardcode_direct="$acl_cv_hardcode_direct"
+ hardcode_minus_L="$acl_cv_hardcode_minus_L"
+ dnl Determine whether the user wants rpath handling at all.
+ AC_ARG_ENABLE(rpath,
+ [ --disable-rpath do not hardcode runtime library paths],
+ :, enable_rpath=yes)
+])
+
+dnl AC_LIB_LINKFLAGS_BODY(name [, dependencies]) searches for libname and
+dnl the libraries corresponding to explicit and implicit dependencies.
+dnl Sets the LIB${NAME}, LTLIB${NAME} and INC${NAME} variables.
+AC_DEFUN([AC_LIB_LINKFLAGS_BODY],
+[
+ AC_REQUIRE([AC_LIB_PREPARE_MULTILIB])
+ define([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-],
+ [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])])
+ dnl By default, look in $includedir and $libdir.
+ use_additional=yes
+ AC_LIB_WITH_FINAL_PREFIX([
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+ ])
+ AC_LIB_ARG_WITH([lib$1-prefix],
+[ --with-lib$1-prefix[=DIR] search for lib$1 in DIR/include and DIR/lib
+ --without-lib$1-prefix don't search for lib$1 in includedir and libdir],
+[
+ if test "X$withval" = "Xno"; then
+ use_additional=no
+ else
+ if test "X$withval" = "X"; then
+ AC_LIB_WITH_FINAL_PREFIX([
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+ ])
+ else
+ additional_includedir="$withval/include"
+ additional_libdir="$withval/$acl_libdirstem"
+ fi
+ fi
+])
+ dnl Search the library and its dependencies in $additional_libdir and
+ dnl $LDFLAGS. Using breadth-first-seach.
+ LIB[]NAME=
+ LTLIB[]NAME=
+ INC[]NAME=
+ rpathdirs=
+ ltrpathdirs=
+ names_already_handled=
+ names_next_round='$1 $2'
+ while test -n "$names_next_round"; do
+ names_this_round="$names_next_round"
+ names_next_round=
+ for name in $names_this_round; do
+ already_handled=
+ for n in $names_already_handled; do
+ if test "$n" = "$name"; then
+ already_handled=yes
+ break
+ fi
+ done
+ if test -z "$already_handled"; then
+ names_already_handled="$names_already_handled $name"
+ dnl See if it was already located by an earlier AC_LIB_LINKFLAGS
+ dnl or AC_LIB_HAVE_LINKFLAGS call.
+ uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'`
+ eval value=\"\$HAVE_LIB$uppername\"
+ if test -n "$value"; then
+ if test "$value" = yes; then
+ eval value=\"\$LIB$uppername\"
+ test -z "$value" || LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$value"
+ eval value=\"\$LTLIB$uppername\"
+ test -z "$value" || LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$value"
+ else
+ dnl An earlier call to AC_LIB_HAVE_LINKFLAGS has determined
+ dnl that this library doesn't exist. So just drop it.
+ :
+ fi
+ else
+ dnl Search the library lib$name in $additional_libdir and $LDFLAGS
+ dnl and the already constructed $LIBNAME/$LTLIBNAME.
+ found_dir=
+ found_la=
+ found_so=
+ found_a=
+ if test $use_additional = yes; then
+ if test -n "$shlibext" \
+ && { test -f "$additional_libdir/lib$name.$shlibext" \
+ || { test "$shlibext" = dll \
+ && test -f "$additional_libdir/lib$name.dll.a"; }; }; then
+ found_dir="$additional_libdir"
+ if test -f "$additional_libdir/lib$name.$shlibext"; then
+ found_so="$additional_libdir/lib$name.$shlibext"
+ else
+ found_so="$additional_libdir/lib$name.dll.a"
+ fi
+ if test -f "$additional_libdir/lib$name.la"; then
+ found_la="$additional_libdir/lib$name.la"
+ fi
+ else
+ if test -f "$additional_libdir/lib$name.$libext"; then
+ found_dir="$additional_libdir"
+ found_a="$additional_libdir/lib$name.$libext"
+ if test -f "$additional_libdir/lib$name.la"; then
+ found_la="$additional_libdir/lib$name.la"
+ fi
+ fi
+ fi
+ fi
+ if test "X$found_dir" = "X"; then
+ for x in $LDFLAGS $LTLIB[]NAME; do
+ AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+ case "$x" in
+ -L*)
+ dir=`echo "X$x" | sed -e 's/^X-L//'`
+ if test -n "$shlibext" \
+ && { test -f "$dir/lib$name.$shlibext" \
+ || { test "$shlibext" = dll \
+ && test -f "$dir/lib$name.dll.a"; }; }; then
+ found_dir="$dir"
+ if test -f "$dir/lib$name.$shlibext"; then
+ found_so="$dir/lib$name.$shlibext"
+ else
+ found_so="$dir/lib$name.dll.a"
+ fi
+ if test -f "$dir/lib$name.la"; then
+ found_la="$dir/lib$name.la"
+ fi
+ else
+ if test -f "$dir/lib$name.$libext"; then
+ found_dir="$dir"
+ found_a="$dir/lib$name.$libext"
+ if test -f "$dir/lib$name.la"; then
+ found_la="$dir/lib$name.la"
+ fi
+ fi
+ fi
+ ;;
+ esac
+ if test "X$found_dir" != "X"; then
+ break
+ fi
+ done
+ fi
+ if test "X$found_dir" != "X"; then
+ dnl Found the library.
+ LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$found_dir -l$name"
+ if test "X$found_so" != "X"; then
+ dnl Linking with a shared library. We attempt to hardcode its
+ dnl directory into the executable's runpath, unless it's the
+ dnl standard /usr/lib.
+ if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/$acl_libdirstem"; then
+ dnl No hardcoding is needed.
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so"
+ else
+ dnl Use an explicit option to hardcode DIR into the resulting
+ dnl binary.
+ dnl Potentially add DIR to ltrpathdirs.
+ dnl The ltrpathdirs will be appended to $LTLIBNAME at the end.
+ haveit=
+ for x in $ltrpathdirs; do
+ if test "X$x" = "X$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ ltrpathdirs="$ltrpathdirs $found_dir"
+ fi
+ dnl The hardcoding into $LIBNAME is system dependent.
+ if test "$hardcode_direct" = yes; then
+ dnl Using DIR/libNAME.so during linking hardcodes DIR into the
+ dnl resulting binary.
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so"
+ else
+ if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then
+ dnl Use an explicit option to hardcode DIR into the resulting
+ dnl binary.
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so"
+ dnl Potentially add DIR to rpathdirs.
+ dnl The rpathdirs will be appended to $LIBNAME at the end.
+ haveit=
+ for x in $rpathdirs; do
+ if test "X$x" = "X$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ rpathdirs="$rpathdirs $found_dir"
+ fi
+ else
+ dnl Rely on "-L$found_dir".
+ dnl But don't add it if it's already contained in the LDFLAGS
+ dnl or the already constructed $LIBNAME
+ haveit=
+ for x in $LDFLAGS $LIB[]NAME; do
+ AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+ if test "X$x" = "X-L$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir"
+ fi
+ if test "$hardcode_minus_L" != no; then
+ dnl FIXME: Not sure whether we should use
+ dnl "-L$found_dir -l$name" or "-L$found_dir $found_so"
+ dnl here.
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so"
+ else
+ dnl We cannot use $hardcode_runpath_var and LD_RUN_PATH
+ dnl here, because this doesn't fit in flags passed to the
+ dnl compiler. So give up. No hardcoding. This affects only
+ dnl very old systems.
+ dnl FIXME: Not sure whether we should use
+ dnl "-L$found_dir -l$name" or "-L$found_dir $found_so"
+ dnl here.
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name"
+ fi
+ fi
+ fi
+ fi
+ else
+ if test "X$found_a" != "X"; then
+ dnl Linking with a static library.
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_a"
+ else
+ dnl We shouldn't come here, but anyway it's good to have a
+ dnl fallback.
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir -l$name"
+ fi
+ fi
+ dnl Assume the include files are nearby.
+ additional_includedir=
+ case "$found_dir" in
+ */$acl_libdirstem | */$acl_libdirstem/)
+ basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e "s,/$acl_libdirstem/"'*$,,'`
+ additional_includedir="$basedir/include"
+ ;;
+ esac
+ if test "X$additional_includedir" != "X"; then
+ dnl Potentially add $additional_includedir to $INCNAME.
+ dnl But don't add it
+ dnl 1. if it's the standard /usr/include,
+ dnl 2. if it's /usr/local/include and we are using GCC on Linux,
+ dnl 3. if it's already present in $CPPFLAGS or the already
+ dnl constructed $INCNAME,
+ dnl 4. if it doesn't exist as a directory.
+ if test "X$additional_includedir" != "X/usr/include"; then
+ haveit=
+ if test "X$additional_includedir" = "X/usr/local/include"; then
+ if test -n "$GCC"; then
+ case $host_os in
+ linux* | gnu* | k*bsd*-gnu) haveit=yes;;
+ esac
+ fi
+ fi
+ if test -z "$haveit"; then
+ for x in $CPPFLAGS $INC[]NAME; do
+ AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+ if test "X$x" = "X-I$additional_includedir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_includedir"; then
+ dnl Really add $additional_includedir to $INCNAME.
+ INC[]NAME="${INC[]NAME}${INC[]NAME:+ }-I$additional_includedir"
+ fi
+ fi
+ fi
+ fi
+ fi
+ dnl Look for dependencies.
+ if test -n "$found_la"; then
+ dnl Read the .la file. It defines the variables
+ dnl dlname, library_names, old_library, dependency_libs, current,
+ dnl age, revision, installed, dlopen, dlpreopen, libdir.
+ save_libdir="$libdir"
+ case "$found_la" in
+ */* | *\\*) . "$found_la" ;;
+ *) . "./$found_la" ;;
+ esac
+ libdir="$save_libdir"
+ dnl We use only dependency_libs.
+ for dep in $dependency_libs; do
+ case "$dep" in
+ -L*)
+ additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'`
+ dnl Potentially add $additional_libdir to $LIBNAME and $LTLIBNAME.
+ dnl But don't add it
+ dnl 1. if it's the standard /usr/lib,
+ dnl 2. if it's /usr/local/lib and we are using GCC on Linux,
+ dnl 3. if it's already present in $LDFLAGS or the already
+ dnl constructed $LIBNAME,
+ dnl 4. if it doesn't exist as a directory.
+ if test "X$additional_libdir" != "X/usr/$acl_libdirstem"; then
+ haveit=
+ if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem"; then
+ if test -n "$GCC"; then
+ case $host_os in
+ linux* | gnu* | k*bsd*-gnu) haveit=yes;;
+ esac
+ fi
+ fi
+ if test -z "$haveit"; then
+ haveit=
+ for x in $LDFLAGS $LIB[]NAME; do
+ AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+ if test "X$x" = "X-L$additional_libdir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_libdir"; then
+ dnl Really add $additional_libdir to $LIBNAME.
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$additional_libdir"
+ fi
+ fi
+ haveit=
+ for x in $LDFLAGS $LTLIB[]NAME; do
+ AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+ if test "X$x" = "X-L$additional_libdir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_libdir"; then
+ dnl Really add $additional_libdir to $LTLIBNAME.
+ LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$additional_libdir"
+ fi
+ fi
+ fi
+ fi
+ ;;
+ -R*)
+ dir=`echo "X$dep" | sed -e 's/^X-R//'`
+ if test "$enable_rpath" != no; then
+ dnl Potentially add DIR to rpathdirs.
+ dnl The rpathdirs will be appended to $LIBNAME at the end.
+ haveit=
+ for x in $rpathdirs; do
+ if test "X$x" = "X$dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ rpathdirs="$rpathdirs $dir"
+ fi
+ dnl Potentially add DIR to ltrpathdirs.
+ dnl The ltrpathdirs will be appended to $LTLIBNAME at the end.
+ haveit=
+ for x in $ltrpathdirs; do
+ if test "X$x" = "X$dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ ltrpathdirs="$ltrpathdirs $dir"
+ fi
+ fi
+ ;;
+ -l*)
+ dnl Handle this in the next round.
+ names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'`
+ ;;
+ *.la)
+ dnl Handle this in the next round. Throw away the .la's
+ dnl directory; it is already contained in a preceding -L
+ dnl option.
+ names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'`
+ ;;
+ *)
+ dnl Most likely an immediate library name.
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$dep"
+ LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$dep"
+ ;;
+ esac
+ done
+ fi
+ else
+ dnl Didn't find the library; assume it is in the system directories
+ dnl known to the linker and runtime loader. (All the system
+ dnl directories known to the linker should also be known to the
+ dnl runtime loader, otherwise the system is severely misconfigured.)
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name"
+ LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-l$name"
+ fi
+ fi
+ fi
+ done
+ done
+ if test "X$rpathdirs" != "X"; then
+ if test -n "$hardcode_libdir_separator"; then
+ dnl Weird platform: only the last -rpath option counts, the user must
+ dnl pass all path elements in one option. We can arrange that for a
+ dnl single library, but not when more than one $LIBNAMEs are used.
+ alldirs=
+ for found_dir in $rpathdirs; do
+ alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$found_dir"
+ done
+ dnl Note: hardcode_libdir_flag_spec uses $libdir and $wl.
+ acl_save_libdir="$libdir"
+ libdir="$alldirs"
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ libdir="$acl_save_libdir"
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag"
+ else
+ dnl The -rpath options are cumulative.
+ for found_dir in $rpathdirs; do
+ acl_save_libdir="$libdir"
+ libdir="$found_dir"
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ libdir="$acl_save_libdir"
+ LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag"
+ done
+ fi
+ fi
+ if test "X$ltrpathdirs" != "X"; then
+ dnl When using libtool, the option that works for both libraries and
+ dnl executables is -R. The -R options are cumulative.
+ for found_dir in $ltrpathdirs; do
+ LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-R$found_dir"
+ done
+ fi
+])
+
+dnl AC_LIB_APPENDTOVAR(VAR, CONTENTS) appends the elements of CONTENTS to VAR,
+dnl unless already present in VAR.
+dnl Works only for CPPFLAGS, not for LIB* variables because that sometimes
+dnl contains two or three consecutive elements that belong together.
+AC_DEFUN([AC_LIB_APPENDTOVAR],
+[
+ for element in [$2]; do
+ haveit=
+ for x in $[$1]; do
+ AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+ if test "X$x" = "X$element"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ [$1]="${[$1]}${[$1]:+ }$element"
+ fi
+ done
+])
+
+dnl For those cases where a variable contains several -L and -l options
+dnl referring to unknown libraries and directories, this macro determines the
+dnl necessary additional linker options for the runtime path.
+dnl AC_LIB_LINKFLAGS_FROM_LIBS([LDADDVAR], [LIBSVALUE], [USE-LIBTOOL])
+dnl sets LDADDVAR to linker options needed together with LIBSVALUE.
+dnl If USE-LIBTOOL evaluates to non-empty, linking with libtool is assumed,
+dnl otherwise linking without libtool is assumed.
+AC_DEFUN([AC_LIB_LINKFLAGS_FROM_LIBS],
+[
+ AC_REQUIRE([AC_LIB_RPATH])
+ AC_REQUIRE([AC_LIB_PREPARE_MULTILIB])
+ $1=
+ if test "$enable_rpath" != no; then
+ if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then
+ dnl Use an explicit option to hardcode directories into the resulting
+ dnl binary.
+ rpathdirs=
+ next=
+ for opt in $2; do
+ if test -n "$next"; then
+ dir="$next"
+ dnl No need to hardcode the standard /usr/lib.
+ if test "X$dir" != "X/usr/$acl_libdirstem"; then
+ rpathdirs="$rpathdirs $dir"
+ fi
+ next=
+ else
+ case $opt in
+ -L) next=yes ;;
+ -L*) dir=`echo "X$opt" | sed -e 's,^X-L,,'`
+ dnl No need to hardcode the standard /usr/lib.
+ if test "X$dir" != "X/usr/$acl_libdirstem"; then
+ rpathdirs="$rpathdirs $dir"
+ fi
+ next= ;;
+ *) next= ;;
+ esac
+ fi
+ done
+ if test "X$rpathdirs" != "X"; then
+ if test -n ""$3""; then
+ dnl libtool is used for linking. Use -R options.
+ for dir in $rpathdirs; do
+ $1="${$1}${$1:+ }-R$dir"
+ done
+ else
+ dnl The linker is used for linking directly.
+ if test -n "$hardcode_libdir_separator"; then
+ dnl Weird platform: only the last -rpath option counts, the user
+ dnl must pass all path elements in one option.
+ alldirs=
+ for dir in $rpathdirs; do
+ alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$dir"
+ done
+ acl_save_libdir="$libdir"
+ libdir="$alldirs"
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ libdir="$acl_save_libdir"
+ $1="$flag"
+ else
+ dnl The -rpath options are cumulative.
+ for dir in $rpathdirs; do
+ acl_save_libdir="$libdir"
+ libdir="$dir"
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ libdir="$acl_save_libdir"
+ $1="${$1}${$1:+ }$flag"
+ done
+ fi
+ fi
+ fi
+ fi
+ fi
+ AC_SUBST([$1])
+])
diff --git a/m4/lib-prefix.m4 b/m4/lib-prefix.m4
new file mode 100644
index 00000000..a8684e17
--- /dev/null
+++ b/m4/lib-prefix.m4
@@ -0,0 +1,185 @@
+# lib-prefix.m4 serial 5 (gettext-0.15)
+dnl Copyright (C) 2001-2005 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Bruno Haible.
+
+dnl AC_LIB_ARG_WITH is synonymous to AC_ARG_WITH in autoconf-2.13, and
+dnl similar to AC_ARG_WITH in autoconf 2.52...2.57 except that is doesn't
+dnl require excessive bracketing.
+ifdef([AC_HELP_STRING],
+[AC_DEFUN([AC_LIB_ARG_WITH], [AC_ARG_WITH([$1],[[$2]],[$3],[$4])])],
+[AC_DEFUN([AC_][LIB_ARG_WITH], [AC_ARG_WITH([$1],[$2],[$3],[$4])])])
+
+dnl AC_LIB_PREFIX adds to the CPPFLAGS and LDFLAGS the flags that are needed
+dnl to access previously installed libraries. The basic assumption is that
+dnl a user will want packages to use other packages he previously installed
+dnl with the same --prefix option.
+dnl This macro is not needed if only AC_LIB_LINKFLAGS is used to locate
+dnl libraries, but is otherwise very convenient.
+AC_DEFUN([AC_LIB_PREFIX],
+[
+ AC_BEFORE([$0], [AC_LIB_LINKFLAGS])
+ AC_REQUIRE([AC_PROG_CC])
+ AC_REQUIRE([AC_CANONICAL_HOST])
+ AC_REQUIRE([AC_LIB_PREPARE_MULTILIB])
+ AC_REQUIRE([AC_LIB_PREPARE_PREFIX])
+ dnl By default, look in $includedir and $libdir.
+ use_additional=yes
+ AC_LIB_WITH_FINAL_PREFIX([
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+ ])
+ AC_LIB_ARG_WITH([lib-prefix],
+[ --with-lib-prefix[=DIR] search for libraries in DIR/include and DIR/lib
+ --without-lib-prefix don't search for libraries in includedir and libdir],
+[
+ if test "X$withval" = "Xno"; then
+ use_additional=no
+ else
+ if test "X$withval" = "X"; then
+ AC_LIB_WITH_FINAL_PREFIX([
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+ ])
+ else
+ additional_includedir="$withval/include"
+ additional_libdir="$withval/$acl_libdirstem"
+ fi
+ fi
+])
+ if test $use_additional = yes; then
+ dnl Potentially add $additional_includedir to $CPPFLAGS.
+ dnl But don't add it
+ dnl 1. if it's the standard /usr/include,
+ dnl 2. if it's already present in $CPPFLAGS,
+ dnl 3. if it's /usr/local/include and we are using GCC on Linux,
+ dnl 4. if it doesn't exist as a directory.
+ if test "X$additional_includedir" != "X/usr/include"; then
+ haveit=
+ for x in $CPPFLAGS; do
+ AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+ if test "X$x" = "X-I$additional_includedir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test "X$additional_includedir" = "X/usr/local/include"; then
+ if test -n "$GCC"; then
+ case $host_os in
+ linux* | gnu* | k*bsd*-gnu) haveit=yes;;
+ esac
+ fi
+ fi
+ if test -z "$haveit"; then
+ if test -d "$additional_includedir"; then
+ dnl Really add $additional_includedir to $CPPFLAGS.
+ CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }-I$additional_includedir"
+ fi
+ fi
+ fi
+ fi
+ dnl Potentially add $additional_libdir to $LDFLAGS.
+ dnl But don't add it
+ dnl 1. if it's the standard /usr/lib,
+ dnl 2. if it's already present in $LDFLAGS,
+ dnl 3. if it's /usr/local/lib and we are using GCC on Linux,
+ dnl 4. if it doesn't exist as a directory.
+ if test "X$additional_libdir" != "X/usr/$acl_libdirstem"; then
+ haveit=
+ for x in $LDFLAGS; do
+ AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"])
+ if test "X$x" = "X-L$additional_libdir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test "X$additional_libdir" = "X/usr/local/$acl_libdirstem"; then
+ if test -n "$GCC"; then
+ case $host_os in
+ linux*) haveit=yes;;
+ esac
+ fi
+ fi
+ if test -z "$haveit"; then
+ if test -d "$additional_libdir"; then
+ dnl Really add $additional_libdir to $LDFLAGS.
+ LDFLAGS="${LDFLAGS}${LDFLAGS:+ }-L$additional_libdir"
+ fi
+ fi
+ fi
+ fi
+ fi
+])
+
+dnl AC_LIB_PREPARE_PREFIX creates variables acl_final_prefix,
+dnl acl_final_exec_prefix, containing the values to which $prefix and
+dnl $exec_prefix will expand at the end of the configure script.
+AC_DEFUN([AC_LIB_PREPARE_PREFIX],
+[
+ dnl Unfortunately, prefix and exec_prefix get only finally determined
+ dnl at the end of configure.
+ if test "X$prefix" = "XNONE"; then
+ acl_final_prefix="$ac_default_prefix"
+ else
+ acl_final_prefix="$prefix"
+ fi
+ if test "X$exec_prefix" = "XNONE"; then
+ acl_final_exec_prefix='${prefix}'
+ else
+ acl_final_exec_prefix="$exec_prefix"
+ fi
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ eval acl_final_exec_prefix=\"$acl_final_exec_prefix\"
+ prefix="$acl_save_prefix"
+])
+
+dnl AC_LIB_WITH_FINAL_PREFIX([statement]) evaluates statement, with the
+dnl variables prefix and exec_prefix bound to the values they will have
+dnl at the end of the configure script.
+AC_DEFUN([AC_LIB_WITH_FINAL_PREFIX],
+[
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ $1
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+])
+
+dnl AC_LIB_PREPARE_MULTILIB creates a variable acl_libdirstem, containing
+dnl the basename of the libdir, either "lib" or "lib64".
+AC_DEFUN([AC_LIB_PREPARE_MULTILIB],
+[
+ dnl There is no formal standard regarding lib and lib64. The current
+ dnl practice is that on a system supporting 32-bit and 64-bit instruction
+ dnl sets or ABIs, 64-bit libraries go under $prefix/lib64 and 32-bit
+ dnl libraries go under $prefix/lib. We determine the compiler's default
+ dnl mode by looking at the compiler's library search path. If at least
+ dnl of its elements ends in /lib64 or points to a directory whose absolute
+ dnl pathname ends in /lib64, we assume a 64-bit ABI. Otherwise we use the
+ dnl default, namely "lib".
+ acl_libdirstem=lib
+ searchpath=`(LC_ALL=C $CC -print-search-dirs) 2>/dev/null | sed -n -e 's,^libraries: ,,p' | sed -e 's,^=,,'`
+ if test -n "$searchpath"; then
+ acl_save_IFS="${IFS= }"; IFS=":"
+ for searchdir in $searchpath; do
+ if test -d "$searchdir"; then
+ case "$searchdir" in
+ */lib64/ | */lib64 ) acl_libdirstem=lib64 ;;
+ *) searchdir=`cd "$searchdir" && pwd`
+ case "$searchdir" in
+ */lib64 ) acl_libdirstem=lib64 ;;
+ esac ;;
+ esac
+ fi
+ done
+ IFS="$acl_save_IFS"
+ fi
+])
diff --git a/m4/libtool.m4 b/m4/libtool.m4
new file mode 100644
index 00000000..4ff44c87
--- /dev/null
+++ b/m4/libtool.m4
@@ -0,0 +1,7365 @@
+# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*-
+#
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005,
+# 2006, 2007, 2008 Free Software Foundation, Inc.
+# Written by Gordon Matzigkeit, 1996
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+m4_define([_LT_COPYING], [dnl
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005,
+# 2006, 2007, 2008 Free Software Foundation, Inc.
+# Written by Gordon Matzigkeit, 1996
+#
+# This file is part of GNU Libtool.
+#
+# GNU Libtool 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.
+#
+# As a special exception to the GNU General Public License,
+# if you distribute this file as part of a program or library that
+# is built using GNU Libtool, you may include this file under the
+# same distribution terms that you use for the rest of that program.
+#
+# GNU Libtool 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 GNU Libtool; see the file COPYING. If not, a copy
+# can be downloaded from http://www.gnu.org/licenses/gpl.html, or
+# obtained by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+])
+
+# serial 56 LT_INIT
+
+
+# LT_PREREQ(VERSION)
+# ------------------
+# Complain and exit if this libtool version is less that VERSION.
+m4_defun([LT_PREREQ],
+[m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1,
+ [m4_default([$3],
+ [m4_fatal([Libtool version $1 or higher is required],
+ 63)])],
+ [$2])])
+
+
+# _LT_CHECK_BUILDDIR
+# ------------------
+# Complain if the absolute build directory name contains unusual characters
+m4_defun([_LT_CHECK_BUILDDIR],
+[case `pwd` in
+ *\ * | *\ *)
+ AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;;
+esac
+])
+
+
+# LT_INIT([OPTIONS])
+# ------------------
+AC_DEFUN([LT_INIT],
+[AC_PREREQ([2.58])dnl We use AC_INCLUDES_DEFAULT
+AC_BEFORE([$0], [LT_LANG])dnl
+AC_BEFORE([$0], [LT_OUTPUT])dnl
+AC_BEFORE([$0], [LTDL_INIT])dnl
+m4_require([_LT_CHECK_BUILDDIR])dnl
+
+dnl Autoconf doesn't catch unexpanded LT_ macros by default:
+m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl
+m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl
+dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4
+dnl unless we require an AC_DEFUNed macro:
+AC_REQUIRE([LTOPTIONS_VERSION])dnl
+AC_REQUIRE([LTSUGAR_VERSION])dnl
+AC_REQUIRE([LTVERSION_VERSION])dnl
+AC_REQUIRE([LTOBSOLETE_VERSION])dnl
+m4_require([_LT_PROG_LTMAIN])dnl
+
+dnl Parse OPTIONS
+_LT_SET_OPTIONS([$0], [$1])
+
+# This can be used to rebuild libtool when needed
+LIBTOOL_DEPS="$ltmain"
+
+# Always use our own libtool.
+LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+AC_SUBST(LIBTOOL)dnl
+
+_LT_SETUP
+
+# Only expand once:
+m4_define([LT_INIT])
+])# LT_INIT
+
+# Old names:
+AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT])
+AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_PROG_LIBTOOL], [])
+dnl AC_DEFUN([AM_PROG_LIBTOOL], [])
+
+
+# _LT_CC_BASENAME(CC)
+# -------------------
+# Calculate cc_basename. Skip known compiler wrappers and cross-prefix.
+m4_defun([_LT_CC_BASENAME],
+[for cc_temp in $1""; do
+ case $cc_temp in
+ compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;;
+ distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;;
+ \-*) ;;
+ *) break;;
+ esac
+done
+cc_basename=`$ECHO "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"`
+])
+
+
+# _LT_FILEUTILS_DEFAULTS
+# ----------------------
+# It is okay to use these file commands and assume they have been set
+# sensibly after `m4_require([_LT_FILEUTILS_DEFAULTS])'.
+m4_defun([_LT_FILEUTILS_DEFAULTS],
+[: ${CP="cp -f"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+])# _LT_FILEUTILS_DEFAULTS
+
+
+# _LT_SETUP
+# ---------
+m4_defun([_LT_SETUP],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+_LT_DECL([], [host_alias], [0], [The host system])dnl
+_LT_DECL([], [host], [0])dnl
+_LT_DECL([], [host_os], [0])dnl
+dnl
+_LT_DECL([], [build_alias], [0], [The build system])dnl
+_LT_DECL([], [build], [0])dnl
+_LT_DECL([], [build_os], [0])dnl
+dnl
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([LT_PATH_LD])dnl
+AC_REQUIRE([LT_PATH_NM])dnl
+dnl
+AC_REQUIRE([AC_PROG_LN_S])dnl
+test -z "$LN_S" && LN_S="ln -s"
+_LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl
+dnl
+AC_REQUIRE([LT_CMD_MAX_LEN])dnl
+_LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl
+_LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl
+dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_CHECK_SHELL_FEATURES])dnl
+m4_require([_LT_CMD_RELOAD])dnl
+m4_require([_LT_CHECK_MAGIC_METHOD])dnl
+m4_require([_LT_CMD_OLD_ARCHIVE])dnl
+m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl
+
+_LT_CONFIG_LIBTOOL_INIT([
+# See if we are running on zsh, and set the options which allow our
+# commands through without removal of \ escapes INIT.
+if test -n "\${ZSH_VERSION+set}" ; then
+ setopt NO_GLOB_SUBST
+fi
+])
+if test -n "${ZSH_VERSION+set}" ; then
+ setopt NO_GLOB_SUBST
+fi
+
+_LT_CHECK_OBJDIR
+
+m4_require([_LT_TAG_COMPILER])dnl
+_LT_PROG_ECHO_BACKSLASH
+
+case $host_os in
+aix3*)
+ # AIX sometimes has problems with the GCC collect2 program. For some
+ # reason, if we set the COLLECT_NAMES environment variable, the problems
+ # vanish in a puff of smoke.
+ if test "X${COLLECT_NAMES+set}" != Xset; then
+ COLLECT_NAMES=
+ export COLLECT_NAMES
+ fi
+ ;;
+esac
+
+# Sed substitution that helps us do robust quoting. It backslashifies
+# metacharacters that are still active within double-quoted strings.
+sed_quote_subst='s/\([["`$\\]]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\([["`\\]]\)/\\\1/g'
+
+# Sed substitution to delay expansion of an escaped shell variable in a
+# double_quote_subst'ed string.
+delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
+
+# Sed substitution to delay expansion of an escaped single quote.
+delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g'
+
+# Sed substitution to avoid accidental globbing in evaled expressions
+no_glob_subst='s/\*/\\\*/g'
+
+# Global variables:
+ofile=libtool
+can_build_shared=yes
+
+# All known linkers require a `.a' archive for static linking (except MSVC,
+# which needs '.lib').
+libext=a
+
+with_gnu_ld="$lt_cv_prog_gnu_ld"
+
+old_CC="$CC"
+old_CFLAGS="$CFLAGS"
+
+# Set sane defaults for various variables
+test -z "$CC" && CC=cc
+test -z "$LTCC" && LTCC=$CC
+test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS
+test -z "$LD" && LD=ld
+test -z "$ac_objext" && ac_objext=o
+
+_LT_CC_BASENAME([$compiler])
+
+# Only perform the check for file, if the check method requires it
+test -z "$MAGIC_CMD" && MAGIC_CMD=file
+case $deplibs_check_method in
+file_magic*)
+ if test "$file_magic_cmd" = '$MAGIC_CMD'; then
+ _LT_PATH_MAGIC
+ fi
+ ;;
+esac
+
+# Use C for the default configuration in the libtool script
+LT_SUPPORTED_TAG([CC])
+_LT_LANG_C_CONFIG
+_LT_LANG_DEFAULT_CONFIG
+_LT_CONFIG_COMMANDS
+])# _LT_SETUP
+
+
+# _LT_PROG_LTMAIN
+# ---------------
+# Note that this code is called both from `configure', and `config.status'
+# now that we use AC_CONFIG_COMMANDS to generate libtool. Notably,
+# `config.status' has no value for ac_aux_dir unless we are using Automake,
+# so we pass a copy along to make sure it has a sensible value anyway.
+m4_defun([_LT_PROG_LTMAIN],
+[m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl
+_LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir'])
+ltmain="$ac_aux_dir/ltmain.sh"
+])# _LT_PROG_LTMAIN
+
+
+## ------------------------------------- ##
+## Accumulate code for creating libtool. ##
+## ------------------------------------- ##
+
+# So that we can recreate a full libtool script including additional
+# tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS
+# in macros and then make a single call at the end using the `libtool'
+# label.
+
+
+# _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS])
+# ----------------------------------------
+# Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later.
+m4_define([_LT_CONFIG_LIBTOOL_INIT],
+[m4_ifval([$1],
+ [m4_append([_LT_OUTPUT_LIBTOOL_INIT],
+ [$1
+])])])
+
+# Initialize.
+m4_define([_LT_OUTPUT_LIBTOOL_INIT])
+
+
+# _LT_CONFIG_LIBTOOL([COMMANDS])
+# ------------------------------
+# Register COMMANDS to be passed to AC_CONFIG_COMMANDS later.
+m4_define([_LT_CONFIG_LIBTOOL],
+[m4_ifval([$1],
+ [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS],
+ [$1
+])])])
+
+# Initialize.
+m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS])
+
+
+# _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS])
+# -----------------------------------------------------
+m4_defun([_LT_CONFIG_SAVE_COMMANDS],
+[_LT_CONFIG_LIBTOOL([$1])
+_LT_CONFIG_LIBTOOL_INIT([$2])
+])
+
+
+# _LT_FORMAT_COMMENT([COMMENT])
+# -----------------------------
+# Add leading comment marks to the start of each line, and a trailing
+# full-stop to the whole comment if one is not present already.
+m4_define([_LT_FORMAT_COMMENT],
+[m4_ifval([$1], [
+m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])],
+ [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.])
+)])
+
+
+
+## ------------------------ ##
+## FIXME: Eliminate VARNAME ##
+## ------------------------ ##
+
+
+# _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?])
+# -------------------------------------------------------------------
+# CONFIGNAME is the name given to the value in the libtool script.
+# VARNAME is the (base) name used in the configure script.
+# VALUE may be 0, 1 or 2 for a computed quote escaped value based on
+# VARNAME. Any other value will be used directly.
+m4_define([_LT_DECL],
+[lt_if_append_uniq([lt_decl_varnames], [$2], [, ],
+ [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name],
+ [m4_ifval([$1], [$1], [$2])])
+ lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3])
+ m4_ifval([$4],
+ [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])])
+ lt_dict_add_subkey([lt_decl_dict], [$2],
+ [tagged?], [m4_ifval([$5], [yes], [no])])])
+])
+
+
+# _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION])
+# --------------------------------------------------------
+m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])])
+
+
+# lt_decl_tag_varnames([SEPARATOR], [VARNAME1...])
+# ------------------------------------------------
+m4_define([lt_decl_tag_varnames],
+[_lt_decl_filter([tagged?], [yes], $@)])
+
+
+# _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..])
+# ---------------------------------------------------------
+m4_define([_lt_decl_filter],
+[m4_case([$#],
+ [0], [m4_fatal([$0: too few arguments: $#])],
+ [1], [m4_fatal([$0: too few arguments: $#: $1])],
+ [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)],
+ [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)],
+ [lt_dict_filter([lt_decl_dict], $@)])[]dnl
+])
+
+
+# lt_decl_quote_varnames([SEPARATOR], [VARNAME1...])
+# --------------------------------------------------
+m4_define([lt_decl_quote_varnames],
+[_lt_decl_filter([value], [1], $@)])
+
+
+# lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...])
+# ---------------------------------------------------
+m4_define([lt_decl_dquote_varnames],
+[_lt_decl_filter([value], [2], $@)])
+
+
+# lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...])
+# ---------------------------------------------------
+m4_define([lt_decl_varnames_tagged],
+[m4_assert([$# <= 2])dnl
+_$0(m4_quote(m4_default([$1], [[, ]])),
+ m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]),
+ m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))])
+m4_define([_lt_decl_varnames_tagged],
+[m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])])
+
+
+# lt_decl_all_varnames([SEPARATOR], [VARNAME1...])
+# ------------------------------------------------
+m4_define([lt_decl_all_varnames],
+[_$0(m4_quote(m4_default([$1], [[, ]])),
+ m4_if([$2], [],
+ m4_quote(lt_decl_varnames),
+ m4_quote(m4_shift($@))))[]dnl
+])
+m4_define([_lt_decl_all_varnames],
+[lt_join($@, lt_decl_varnames_tagged([$1],
+ lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl
+])
+
+
+# _LT_CONFIG_STATUS_DECLARE([VARNAME])
+# ------------------------------------
+# Quote a variable value, and forward it to `config.status' so that its
+# declaration there will have the same value as in `configure'. VARNAME
+# must have a single quote delimited value for this to work.
+m4_define([_LT_CONFIG_STATUS_DECLARE],
+[$1='`$ECHO "X$][$1" | $Xsed -e "$delay_single_quote_subst"`'])
+
+
+# _LT_CONFIG_STATUS_DECLARATIONS
+# ------------------------------
+# We delimit libtool config variables with single quotes, so when
+# we write them to config.status, we have to be sure to quote all
+# embedded single quotes properly. In configure, this macro expands
+# each variable declared with _LT_DECL (and _LT_TAGDECL) into:
+#
+# <var>='`$ECHO "X$<var>" | $Xsed -e "$delay_single_quote_subst"`'
+m4_defun([_LT_CONFIG_STATUS_DECLARATIONS],
+[m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames),
+ [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])])
+
+
+# _LT_LIBTOOL_TAGS
+# ----------------
+# Output comment and list of tags supported by the script
+m4_defun([_LT_LIBTOOL_TAGS],
+[_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl
+available_tags="_LT_TAGS"dnl
+])
+
+
+# _LT_LIBTOOL_DECLARE(VARNAME, [TAG])
+# -----------------------------------
+# Extract the dictionary values for VARNAME (optionally with TAG) and
+# expand to a commented shell variable setting:
+#
+# # Some comment about what VAR is for.
+# visible_name=$lt_internal_name
+m4_define([_LT_LIBTOOL_DECLARE],
+[_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1],
+ [description])))[]dnl
+m4_pushdef([_libtool_name],
+ m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl
+m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])),
+ [0], [_libtool_name=[$]$1],
+ [1], [_libtool_name=$lt_[]$1],
+ [2], [_libtool_name=$lt_[]$1],
+ [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl
+m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl
+])
+
+
+# _LT_LIBTOOL_CONFIG_VARS
+# -----------------------
+# Produce commented declarations of non-tagged libtool config variables
+# suitable for insertion in the LIBTOOL CONFIG section of the `libtool'
+# script. Tagged libtool config variables (even for the LIBTOOL CONFIG
+# section) are produced by _LT_LIBTOOL_TAG_VARS.
+m4_defun([_LT_LIBTOOL_CONFIG_VARS],
+[m4_foreach([_lt_var],
+ m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)),
+ [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])])
+
+
+# _LT_LIBTOOL_TAG_VARS(TAG)
+# -------------------------
+m4_define([_LT_LIBTOOL_TAG_VARS],
+[m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames),
+ [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])])
+
+
+# _LT_TAGVAR(VARNAME, [TAGNAME])
+# ------------------------------
+m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])])
+
+
+# _LT_CONFIG_COMMANDS
+# -------------------
+# Send accumulated output to $CONFIG_STATUS. Thanks to the lists of
+# variables for single and double quote escaping we saved from calls
+# to _LT_DECL, we can put quote escaped variables declarations
+# into `config.status', and then the shell code to quote escape them in
+# for loops in `config.status'. Finally, any additional code accumulated
+# from calls to _LT_CONFIG_LIBTOOL_INIT is expanded.
+m4_defun([_LT_CONFIG_COMMANDS],
+[AC_PROVIDE_IFELSE([LT_OUTPUT],
+ dnl If the libtool generation code has been placed in $CONFIG_LT,
+ dnl instead of duplicating it all over again into config.status,
+ dnl then we will have config.status run $CONFIG_LT later, so it
+ dnl needs to know what name is stored there:
+ [AC_CONFIG_COMMANDS([libtool],
+ [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])],
+ dnl If the libtool generation code is destined for config.status,
+ dnl expand the accumulated commands and init code now:
+ [AC_CONFIG_COMMANDS([libtool],
+ [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])])
+])#_LT_CONFIG_COMMANDS
+
+
+# Initialize.
+m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT],
+[
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+sed_quote_subst='$sed_quote_subst'
+double_quote_subst='$double_quote_subst'
+delay_variable_subst='$delay_variable_subst'
+_LT_CONFIG_STATUS_DECLARATIONS
+LTCC='$LTCC'
+LTCFLAGS='$LTCFLAGS'
+compiler='$compiler_DEFAULT'
+
+# Quote evaled strings.
+for var in lt_decl_all_varnames([[ \
+]], lt_decl_quote_varnames); do
+ case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in
+ *[[\\\\\\\`\\"\\\$]]*)
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$sed_quote_subst\\"\\\`\\\\\\""
+ ;;
+ *)
+ eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+ ;;
+ esac
+done
+
+# Double-quote double-evaled strings.
+for var in lt_decl_all_varnames([[ \
+]], lt_decl_dquote_varnames); do
+ case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in
+ *[[\\\\\\\`\\"\\\$]]*)
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\""
+ ;;
+ *)
+ eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+ ;;
+ esac
+done
+
+# Fix-up fallback echo if it was mangled by the above quoting rules.
+case \$lt_ECHO in
+*'\\\[$]0 --fallback-echo"')dnl "
+ lt_ECHO=\`\$ECHO "X\$lt_ECHO" | \$Xsed -e 's/\\\\\\\\\\\\\\\[$]0 --fallback-echo"\[$]/\[$]0 --fallback-echo"/'\`
+ ;;
+esac
+
+_LT_OUTPUT_LIBTOOL_INIT
+])
+
+
+# LT_OUTPUT
+# ---------
+# This macro allows early generation of the libtool script (before
+# AC_OUTPUT is called), incase it is used in configure for compilation
+# tests.
+AC_DEFUN([LT_OUTPUT],
+[: ${CONFIG_LT=./config.lt}
+AC_MSG_NOTICE([creating $CONFIG_LT])
+cat >"$CONFIG_LT" <<_LTEOF
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate a libtool stub with the current configuration.
+
+lt_cl_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_LTEOF
+
+cat >>"$CONFIG_LT" <<\_LTEOF
+AS_SHELL_SANITIZE
+_AS_PREPARE
+
+exec AS_MESSAGE_FD>&1
+exec AS_MESSAGE_LOG_FD>>config.log
+{
+ echo
+ AS_BOX([Running $as_me.])
+} >&AS_MESSAGE_LOG_FD
+
+lt_cl_help="\
+\`$as_me' creates a local libtool stub from the current configuration,
+for use in further configure time tests before the real libtool is
+generated.
+
+Usage: $[0] [[OPTIONS]]
+
+ -h, --help print this help, then exit
+ -V, --version print version number, then exit
+ -q, --quiet do not print progress messages
+ -d, --debug don't remove temporary files
+
+Report bugs to <bug-libtool@gnu.org>."
+
+lt_cl_version="\
+m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl
+m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION])
+configured by $[0], generated by m4_PACKAGE_STRING.
+
+Copyright (C) 2008 Free Software Foundation, Inc.
+This config.lt script is free software; the Free Software Foundation
+gives unlimited permision to copy, distribute and modify it."
+
+while test $[#] != 0
+do
+ case $[1] in
+ --version | --v* | -V )
+ echo "$lt_cl_version"; exit 0 ;;
+ --help | --h* | -h )
+ echo "$lt_cl_help"; exit 0 ;;
+ --debug | --d* | -d )
+ debug=: ;;
+ --quiet | --q* | --silent | --s* | -q )
+ lt_cl_silent=: ;;
+
+ -*) AC_MSG_ERROR([unrecognized option: $[1]
+Try \`$[0] --help' for more information.]) ;;
+
+ *) AC_MSG_ERROR([unrecognized argument: $[1]
+Try \`$[0] --help' for more information.]) ;;
+ esac
+ shift
+done
+
+if $lt_cl_silent; then
+ exec AS_MESSAGE_FD>/dev/null
+fi
+_LTEOF
+
+cat >>"$CONFIG_LT" <<_LTEOF
+_LT_OUTPUT_LIBTOOL_COMMANDS_INIT
+_LTEOF
+
+cat >>"$CONFIG_LT" <<\_LTEOF
+AC_MSG_NOTICE([creating $ofile])
+_LT_OUTPUT_LIBTOOL_COMMANDS
+AS_EXIT(0)
+_LTEOF
+chmod +x "$CONFIG_LT"
+
+# configure is writing to config.log, but config.lt does its own redirection,
+# appending to config.log, which fails on DOS, as config.log is still kept
+# open by configure. Here we exec the FD to /dev/null, effectively closing
+# config.log, so it can be properly (re)opened and appended to by config.lt.
+if test "$no_create" != yes; then
+ lt_cl_success=:
+ test "$silent" = yes &&
+ lt_config_lt_args="$lt_config_lt_args --quiet"
+ exec AS_MESSAGE_LOG_FD>/dev/null
+ $SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false
+ exec AS_MESSAGE_LOG_FD>>config.log
+ $lt_cl_success || AS_EXIT(1)
+fi
+])# LT_OUTPUT
+
+
+# _LT_CONFIG(TAG)
+# ---------------
+# If TAG is the built-in tag, create an initial libtool script with a
+# default configuration from the untagged config vars. Otherwise add code
+# to config.status for appending the configuration named by TAG from the
+# matching tagged config vars.
+m4_defun([_LT_CONFIG],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+_LT_CONFIG_SAVE_COMMANDS([
+ m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl
+ m4_if(_LT_TAG, [C], [
+ # See if we are running on zsh, and set the options which allow our
+ # commands through without removal of \ escapes.
+ if test -n "${ZSH_VERSION+set}" ; then
+ setopt NO_GLOB_SUBST
+ fi
+
+ cfgfile="${ofile}T"
+ trap "$RM \"$cfgfile\"; exit 1" 1 2 15
+ $RM "$cfgfile"
+
+ cat <<_LT_EOF >> "$cfgfile"
+#! $SHELL
+
+# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services.
+# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION
+# NOTE: Changes made to this file will be lost: look at ltmain.sh.
+#
+_LT_COPYING
+_LT_LIBTOOL_TAGS
+
+# ### BEGIN LIBTOOL CONFIG
+_LT_LIBTOOL_CONFIG_VARS
+_LT_LIBTOOL_TAG_VARS
+# ### END LIBTOOL CONFIG
+
+_LT_EOF
+
+ case $host_os in
+ aix3*)
+ cat <<\_LT_EOF >> "$cfgfile"
+# AIX sometimes has problems with the GCC collect2 program. For some
+# reason, if we set the COLLECT_NAMES environment variable, the problems
+# vanish in a puff of smoke.
+if test "X${COLLECT_NAMES+set}" != Xset; then
+ COLLECT_NAMES=
+ export COLLECT_NAMES
+fi
+_LT_EOF
+ ;;
+ esac
+
+ _LT_PROG_LTMAIN
+
+ # We use sed instead of cat because bash on DJGPP gets confused if
+ # if finds mixed CR/LF and LF-only lines. Since sed operates in
+ # text mode, it properly converts lines to CR/LF. This bash problem
+ # is reportedly fixed, but why not run on old versions too?
+ sed '/^# Generated shell functions inserted here/q' "$ltmain" >> "$cfgfile" \
+ || (rm -f "$cfgfile"; exit 1)
+
+ _LT_PROG_XSI_SHELLFNS
+
+ sed -n '/^# Generated shell functions inserted here/,$p' "$ltmain" >> "$cfgfile" \
+ || (rm -f "$cfgfile"; exit 1)
+
+ mv -f "$cfgfile" "$ofile" ||
+ (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
+ chmod +x "$ofile"
+],
+[cat <<_LT_EOF >> "$ofile"
+
+dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded
+dnl in a comment (ie after a #).
+# ### BEGIN LIBTOOL TAG CONFIG: $1
+_LT_LIBTOOL_TAG_VARS(_LT_TAG)
+# ### END LIBTOOL TAG CONFIG: $1
+_LT_EOF
+])dnl /m4_if
+],
+[m4_if([$1], [], [
+ PACKAGE='$PACKAGE'
+ VERSION='$VERSION'
+ TIMESTAMP='$TIMESTAMP'
+ RM='$RM'
+ ofile='$ofile'], [])
+])dnl /_LT_CONFIG_SAVE_COMMANDS
+])# _LT_CONFIG
+
+
+# LT_SUPPORTED_TAG(TAG)
+# ---------------------
+# Trace this macro to discover what tags are supported by the libtool
+# --tag option, using:
+# autoconf --trace 'LT_SUPPORTED_TAG:$1'
+AC_DEFUN([LT_SUPPORTED_TAG], [])
+
+
+# C support is built-in for now
+m4_define([_LT_LANG_C_enabled], [])
+m4_define([_LT_TAGS], [])
+
+
+# LT_LANG(LANG)
+# -------------
+# Enable libtool support for the given language if not already enabled.
+AC_DEFUN([LT_LANG],
+[AC_BEFORE([$0], [LT_OUTPUT])dnl
+m4_case([$1],
+ [C], [_LT_LANG(C)],
+ [C++], [_LT_LANG(CXX)],
+ [Java], [_LT_LANG(GCJ)],
+ [Fortran 77], [_LT_LANG(F77)],
+ [Fortran], [_LT_LANG(FC)],
+ [Windows Resource], [_LT_LANG(RC)],
+ [m4_ifdef([_LT_LANG_]$1[_CONFIG],
+ [_LT_LANG($1)],
+ [m4_fatal([$0: unsupported language: "$1"])])])dnl
+])# LT_LANG
+
+
+# _LT_LANG(LANGNAME)
+# ------------------
+m4_defun([_LT_LANG],
+[m4_ifdef([_LT_LANG_]$1[_enabled], [],
+ [LT_SUPPORTED_TAG([$1])dnl
+ m4_append([_LT_TAGS], [$1 ])dnl
+ m4_define([_LT_LANG_]$1[_enabled], [])dnl
+ _LT_LANG_$1_CONFIG($1)])dnl
+])# _LT_LANG
+
+
+# _LT_LANG_DEFAULT_CONFIG
+# -----------------------
+m4_defun([_LT_LANG_DEFAULT_CONFIG],
+[AC_PROVIDE_IFELSE([AC_PROG_CXX],
+ [LT_LANG(CXX)],
+ [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])])
+
+AC_PROVIDE_IFELSE([AC_PROG_F77],
+ [LT_LANG(F77)],
+ [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])])
+
+AC_PROVIDE_IFELSE([AC_PROG_FC],
+ [LT_LANG(FC)],
+ [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])])
+
+dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal
+dnl pulling things in needlessly.
+AC_PROVIDE_IFELSE([AC_PROG_GCJ],
+ [LT_LANG(GCJ)],
+ [AC_PROVIDE_IFELSE([A][M_PROG_GCJ],
+ [LT_LANG(GCJ)],
+ [AC_PROVIDE_IFELSE([LT_PROG_GCJ],
+ [LT_LANG(GCJ)],
+ [m4_ifdef([AC_PROG_GCJ],
+ [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])])
+ m4_ifdef([A][M_PROG_GCJ],
+ [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])])
+ m4_ifdef([LT_PROG_GCJ],
+ [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])])
+
+AC_PROVIDE_IFELSE([LT_PROG_RC],
+ [LT_LANG(RC)],
+ [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])])
+])# _LT_LANG_DEFAULT_CONFIG
+
+# Obsolete macros:
+AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)])
+AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)])
+AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)])
+AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_CXX], [])
+dnl AC_DEFUN([AC_LIBTOOL_F77], [])
+dnl AC_DEFUN([AC_LIBTOOL_FC], [])
+dnl AC_DEFUN([AC_LIBTOOL_GCJ], [])
+
+
+# _LT_TAG_COMPILER
+# ----------------
+m4_defun([_LT_TAG_COMPILER],
+[AC_REQUIRE([AC_PROG_CC])dnl
+
+_LT_DECL([LTCC], [CC], [1], [A C compiler])dnl
+_LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl
+_LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl
+_LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+])# _LT_TAG_COMPILER
+
+
+# _LT_COMPILER_BOILERPLATE
+# ------------------------
+# Check for compiler boilerplate output or warnings with
+# the simple compiler test code.
+m4_defun([_LT_COMPILER_BOILERPLATE],
+[m4_require([_LT_DECL_SED])dnl
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_compile_test_code" >conftest.$ac_ext
+eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_compiler_boilerplate=`cat conftest.err`
+$RM conftest*
+])# _LT_COMPILER_BOILERPLATE
+
+
+# _LT_LINKER_BOILERPLATE
+# ----------------------
+# Check for linker boilerplate output or warnings with
+# the simple link test code.
+m4_defun([_LT_LINKER_BOILERPLATE],
+[m4_require([_LT_DECL_SED])dnl
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_link_test_code" >conftest.$ac_ext
+eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_linker_boilerplate=`cat conftest.err`
+$RM -r conftest*
+])# _LT_LINKER_BOILERPLATE
+
+# _LT_REQUIRED_DARWIN_CHECKS
+# -------------------------
+m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[
+ case $host_os in
+ rhapsody* | darwin*)
+ AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:])
+ AC_CHECK_TOOL([NMEDIT], [nmedit], [:])
+ AC_CHECK_TOOL([LIPO], [lipo], [:])
+ AC_CHECK_TOOL([OTOOL], [otool], [:])
+ AC_CHECK_TOOL([OTOOL64], [otool64], [:])
+ _LT_DECL([], [DSYMUTIL], [1],
+ [Tool to manipulate archived DWARF debug symbol files on Mac OS X])
+ _LT_DECL([], [NMEDIT], [1],
+ [Tool to change global to local symbols on Mac OS X])
+ _LT_DECL([], [LIPO], [1],
+ [Tool to manipulate fat objects and archives on Mac OS X])
+ _LT_DECL([], [OTOOL], [1],
+ [ldd/readelf like tool for Mach-O binaries on Mac OS X])
+ _LT_DECL([], [OTOOL64], [1],
+ [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4])
+
+ AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod],
+ [lt_cv_apple_cc_single_mod=no
+ if test -z "${LT_MULTI_MODULE}"; then
+ # By default we will add the -single_module flag. You can override
+ # by either setting the environment variable LT_MULTI_MODULE
+ # non-empty at configure time, or by adding -multi_module to the
+ # link flags.
+ rm -rf libconftest.dylib*
+ echo "int foo(void){return 1;}" > conftest.c
+ echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+-dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD
+ $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err
+ _lt_result=$?
+ if test -f libconftest.dylib && test ! -s conftest.err && test $_lt_result = 0; then
+ lt_cv_apple_cc_single_mod=yes
+ else
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ fi
+ rm -rf libconftest.dylib*
+ rm -f conftest.*
+ fi])
+ AC_CACHE_CHECK([for -exported_symbols_list linker flag],
+ [lt_cv_ld_exported_symbols_list],
+ [lt_cv_ld_exported_symbols_list=no
+ save_LDFLAGS=$LDFLAGS
+ echo "_main" > conftest.sym
+ LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym"
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])],
+ [lt_cv_ld_exported_symbols_list=yes],
+ [lt_cv_ld_exported_symbols_list=no])
+ LDFLAGS="$save_LDFLAGS"
+ ])
+ case $host_os in
+ rhapsody* | darwin1.[[012]])
+ _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;;
+ darwin1.*)
+ _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+ darwin*) # darwin 5.x on
+ # if running on 10.5 or later, the deployment target defaults
+ # to the OS version, if on x86, and 10.4, the deployment
+ # target defaults to 10.4. Don't you love it?
+ case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
+ 10.0,*86*-darwin8*|10.0,*-darwin[[91]]*)
+ _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+ 10.[[012]]*)
+ _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+ 10.*)
+ _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+ esac
+ ;;
+ esac
+ if test "$lt_cv_apple_cc_single_mod" = "yes"; then
+ _lt_dar_single_mod='$single_module'
+ fi
+ if test "$lt_cv_ld_exported_symbols_list" = "yes"; then
+ _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym'
+ else
+ _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}'
+ fi
+ if test "$DSYMUTIL" != ":"; then
+ _lt_dsymutil='~$DSYMUTIL $lib || :'
+ else
+ _lt_dsymutil=
+ fi
+ ;;
+ esac
+])
+
+
+# _LT_DARWIN_LINKER_FEATURES
+# --------------------------
+# Checks for linker and compiler features on darwin
+m4_defun([_LT_DARWIN_LINKER_FEATURES],
+[
+ m4_require([_LT_REQUIRED_DARWIN_CHECKS])
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_automatic, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=''
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ _LT_TAGVAR(allow_undefined_flag, $1)="$_lt_dar_allow_undefined"
+ case $cc_basename in
+ ifort*) _lt_dar_can_shared=yes ;;
+ *) _lt_dar_can_shared=$GCC ;;
+ esac
+ if test "$_lt_dar_can_shared" = "yes"; then
+ output_verbose_link_cmd=echo
+ _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}"
+ _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}"
+ _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}"
+ _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}"
+ m4_if([$1], [CXX],
+[ if test "$lt_cv_apple_cc_single_mod" != "yes"; then
+ _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}"
+ _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}"
+ fi
+],[])
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+])
+
+# _LT_SYS_MODULE_PATH_AIX
+# -----------------------
+# Links a minimal program and checks the executable
+# for the system default hardcoded library path. In most cases,
+# this is /usr/lib:/lib, but when the MPI compilers are used
+# the location of the communication and MPI libs are included too.
+# If we don't find anything, use the default library path according
+# to the aix ld manual.
+m4_defun([_LT_SYS_MODULE_PATH_AIX],
+[m4_require([_LT_DECL_SED])dnl
+AC_LINK_IFELSE([AC_LANG_PROGRAM],[
+lt_aix_libpath_sed='
+ /Import File Strings/,/^$/ {
+ /^0/ {
+ s/^0 *\(.*\)$/\1/
+ p
+ }
+ }'
+aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+# Check for a 64-bit object if we didn't find anything.
+if test -z "$aix_libpath"; then
+ aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+fi],[])
+if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
+])# _LT_SYS_MODULE_PATH_AIX
+
+
+# _LT_SHELL_INIT(ARG)
+# -------------------
+m4_define([_LT_SHELL_INIT],
+[ifdef([AC_DIVERSION_NOTICE],
+ [AC_DIVERT_PUSH(AC_DIVERSION_NOTICE)],
+ [AC_DIVERT_PUSH(NOTICE)])
+$1
+AC_DIVERT_POP
+])# _LT_SHELL_INIT
+
+
+# _LT_PROG_ECHO_BACKSLASH
+# -----------------------
+# Add some code to the start of the generated configure script which
+# will find an echo command which doesn't interpret backslashes.
+m4_defun([_LT_PROG_ECHO_BACKSLASH],
+[_LT_SHELL_INIT([
+# Check that we are running under the correct shell.
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+case X$lt_ECHO in
+X*--fallback-echo)
+ # Remove one level of quotation (which was required for Make).
+ ECHO=`echo "$lt_ECHO" | sed 's,\\\\\[$]\\[$]0,'[$]0','`
+ ;;
+esac
+
+ECHO=${lt_ECHO-echo}
+if test "X[$]1" = X--no-reexec; then
+ # Discard the --no-reexec flag, and continue.
+ shift
+elif test "X[$]1" = X--fallback-echo; then
+ # Avoid inline document here, it may be left over
+ :
+elif test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' ; then
+ # Yippee, $ECHO works!
+ :
+else
+ # Restart under the correct shell.
+ exec $SHELL "[$]0" --no-reexec ${1+"[$]@"}
+fi
+
+if test "X[$]1" = X--fallback-echo; then
+ # used as fallback echo
+ shift
+ cat <<_LT_EOF
+[$]*
+_LT_EOF
+ exit 0
+fi
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+if test -z "$lt_ECHO"; then
+ if test "X${echo_test_string+set}" != Xset; then
+ # find a string as large as possible, as long as the shell can cope with it
+ for cmd in 'sed 50q "[$]0"' 'sed 20q "[$]0"' 'sed 10q "[$]0"' 'sed 2q "[$]0"' 'echo test'; do
+ # expected sizes: less than 2Kb, 1Kb, 512 bytes, 16 bytes, ...
+ if { echo_test_string=`eval $cmd`; } 2>/dev/null &&
+ { test "X$echo_test_string" = "X$echo_test_string"; } 2>/dev/null
+ then
+ break
+ fi
+ done
+ fi
+
+ if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ :
+ else
+ # The Solaris, AIX, and Digital Unix default echo programs unquote
+ # backslashes. This makes it impossible to quote backslashes using
+ # echo "$something" | sed 's/\\/\\\\/g'
+ #
+ # So, first we look for a working echo in the user's PATH.
+
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ for dir in $PATH /usr/ucb; do
+ IFS="$lt_save_ifs"
+ if (test -f $dir/echo || test -f $dir/echo$ac_exeext) &&
+ test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`($dir/echo "$echo_test_string") 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ ECHO="$dir/echo"
+ break
+ fi
+ done
+ IFS="$lt_save_ifs"
+
+ if test "X$ECHO" = Xecho; then
+ # We didn't find a better echo, so look for alternatives.
+ if test "X`{ print -r '\t'; } 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`{ print -r "$echo_test_string"; } 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ # This shell has a builtin print -r that does the trick.
+ ECHO='print -r'
+ elif { test -f /bin/ksh || test -f /bin/ksh$ac_exeext; } &&
+ test "X$CONFIG_SHELL" != X/bin/ksh; then
+ # If we have ksh, try running configure again with it.
+ ORIGINAL_CONFIG_SHELL=${CONFIG_SHELL-/bin/sh}
+ export ORIGINAL_CONFIG_SHELL
+ CONFIG_SHELL=/bin/ksh
+ export CONFIG_SHELL
+ exec $CONFIG_SHELL "[$]0" --no-reexec ${1+"[$]@"}
+ else
+ # Try using printf.
+ ECHO='printf %s\n'
+ if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' &&
+ echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ # Cool, printf works
+ :
+ elif echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` &&
+ test "X$echo_testing_string" = 'X\t' &&
+ echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ CONFIG_SHELL=$ORIGINAL_CONFIG_SHELL
+ export CONFIG_SHELL
+ SHELL="$CONFIG_SHELL"
+ export SHELL
+ ECHO="$CONFIG_SHELL [$]0 --fallback-echo"
+ elif echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` &&
+ test "X$echo_testing_string" = 'X\t' &&
+ echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` &&
+ test "X$echo_testing_string" = "X$echo_test_string"; then
+ ECHO="$CONFIG_SHELL [$]0 --fallback-echo"
+ else
+ # maybe with a smaller string...
+ prev=:
+
+ for cmd in 'echo test' 'sed 2q "[$]0"' 'sed 10q "[$]0"' 'sed 20q "[$]0"' 'sed 50q "[$]0"'; do
+ if { test "X$echo_test_string" = "X`eval $cmd`"; } 2>/dev/null
+ then
+ break
+ fi
+ prev="$cmd"
+ done
+
+ if test "$prev" != 'sed 50q "[$]0"'; then
+ echo_test_string=`eval $prev`
+ export echo_test_string
+ exec ${ORIGINAL_CONFIG_SHELL-${CONFIG_SHELL-/bin/sh}} "[$]0" ${1+"[$]@"}
+ else
+ # Oops. We lost completely, so just stick with echo.
+ ECHO=echo
+ fi
+ fi
+ fi
+ fi
+ fi
+fi
+
+# Copy echo and quote the copy suitably for passing to libtool from
+# the Makefile, instead of quoting the original, which is used later.
+lt_ECHO=$ECHO
+if test "X$lt_ECHO" = "X$CONFIG_SHELL [$]0 --fallback-echo"; then
+ lt_ECHO="$CONFIG_SHELL \\\$\[$]0 --fallback-echo"
+fi
+
+AC_SUBST(lt_ECHO)
+])
+_LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts])
+_LT_DECL([], [ECHO], [1],
+ [An echo program that does not interpret backslashes])
+])# _LT_PROG_ECHO_BACKSLASH
+
+
+# _LT_ENABLE_LOCK
+# ---------------
+m4_defun([_LT_ENABLE_LOCK],
+[AC_ARG_ENABLE([libtool-lock],
+ [AS_HELP_STRING([--disable-libtool-lock],
+ [avoid locking (might break parallel builds)])])
+test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes
+
+# Some flags need to be propagated to the compiler or linker for good
+# libtool support.
+case $host in
+ia64-*-hpux*)
+ # Find out which ABI we are using.
+ echo 'int i;' > conftest.$ac_ext
+ if AC_TRY_EVAL(ac_compile); then
+ case `/usr/bin/file conftest.$ac_objext` in
+ *ELF-32*)
+ HPUX_IA64_MODE="32"
+ ;;
+ *ELF-64*)
+ HPUX_IA64_MODE="64"
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+*-*-irix6*)
+ # Find out which ABI we are using.
+ echo '[#]line __oline__ "configure"' > conftest.$ac_ext
+ if AC_TRY_EVAL(ac_compile); then
+ if test "$lt_cv_prog_gnu_ld" = yes; then
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ LD="${LD-ld} -melf32bsmip"
+ ;;
+ *N32*)
+ LD="${LD-ld} -melf32bmipn32"
+ ;;
+ *64-bit*)
+ LD="${LD-ld} -melf64bmip"
+ ;;
+ esac
+ else
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ LD="${LD-ld} -32"
+ ;;
+ *N32*)
+ LD="${LD-ld} -n32"
+ ;;
+ *64-bit*)
+ LD="${LD-ld} -64"
+ ;;
+ esac
+ fi
+ fi
+ rm -rf conftest*
+ ;;
+
+x86_64-*kfreebsd*-gnu|x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*| \
+s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
+ # Find out which ABI we are using.
+ echo 'int i;' > conftest.$ac_ext
+ if AC_TRY_EVAL(ac_compile); then
+ case `/usr/bin/file conftest.o` in
+ *32-bit*)
+ case $host in
+ x86_64-*kfreebsd*-gnu)
+ LD="${LD-ld} -m elf_i386_fbsd"
+ ;;
+ x86_64-*linux*)
+ LD="${LD-ld} -m elf_i386"
+ ;;
+ ppc64-*linux*|powerpc64-*linux*)
+ LD="${LD-ld} -m elf32ppclinux"
+ ;;
+ s390x-*linux*)
+ LD="${LD-ld} -m elf_s390"
+ ;;
+ sparc64-*linux*)
+ LD="${LD-ld} -m elf32_sparc"
+ ;;
+ esac
+ ;;
+ *64-bit*)
+ case $host in
+ x86_64-*kfreebsd*-gnu)
+ LD="${LD-ld} -m elf_x86_64_fbsd"
+ ;;
+ x86_64-*linux*)
+ LD="${LD-ld} -m elf_x86_64"
+ ;;
+ ppc*-*linux*|powerpc*-*linux*)
+ LD="${LD-ld} -m elf64ppc"
+ ;;
+ s390*-*linux*|s390*-*tpf*)
+ LD="${LD-ld} -m elf64_s390"
+ ;;
+ sparc*-*linux*)
+ LD="${LD-ld} -m elf64_sparc"
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+
+*-*-sco3.2v5*)
+ # On SCO OpenServer 5, we need -belf to get full-featured binaries.
+ SAVE_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS -belf"
+ AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf,
+ [AC_LANG_PUSH(C)
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no])
+ AC_LANG_POP])
+ if test x"$lt_cv_cc_needs_belf" != x"yes"; then
+ # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
+ CFLAGS="$SAVE_CFLAGS"
+ fi
+ ;;
+sparc*-*solaris*)
+ # Find out which ABI we are using.
+ echo 'int i;' > conftest.$ac_ext
+ if AC_TRY_EVAL(ac_compile); then
+ case `/usr/bin/file conftest.o` in
+ *64-bit*)
+ case $lt_cv_prog_gnu_ld in
+ yes*) LD="${LD-ld} -m elf64_sparc" ;;
+ *)
+ if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then
+ LD="${LD-ld} -64"
+ fi
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+esac
+
+need_locks="$enable_libtool_lock"
+])# _LT_ENABLE_LOCK
+
+
+# _LT_CMD_OLD_ARCHIVE
+# -------------------
+m4_defun([_LT_CMD_OLD_ARCHIVE],
+[AC_CHECK_TOOL(AR, ar, false)
+test -z "$AR" && AR=ar
+test -z "$AR_FLAGS" && AR_FLAGS=cru
+_LT_DECL([], [AR], [1], [The archiver])
+_LT_DECL([], [AR_FLAGS], [1])
+
+AC_CHECK_TOOL(STRIP, strip, :)
+test -z "$STRIP" && STRIP=:
+_LT_DECL([], [STRIP], [1], [A symbol stripping program])
+
+AC_CHECK_TOOL(RANLIB, ranlib, :)
+test -z "$RANLIB" && RANLIB=:
+_LT_DECL([], [RANLIB], [1],
+ [Commands used to install an old-style archive])
+
+# Determine commands to create old-style static archives.
+old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs'
+old_postinstall_cmds='chmod 644 $oldlib'
+old_postuninstall_cmds=
+
+if test -n "$RANLIB"; then
+ case $host_os in
+ openbsd*)
+ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$oldlib"
+ ;;
+ *)
+ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$oldlib"
+ ;;
+ esac
+ old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib"
+fi
+_LT_DECL([], [old_postinstall_cmds], [2])
+_LT_DECL([], [old_postuninstall_cmds], [2])
+_LT_TAGDECL([], [old_archive_cmds], [2],
+ [Commands used to build an old-style archive])
+])# _LT_CMD_OLD_ARCHIVE
+
+
+# _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS,
+# [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE])
+# ----------------------------------------------------------------
+# Check whether the given compiler option works
+AC_DEFUN([_LT_COMPILER_OPTION],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_SED])dnl
+AC_CACHE_CHECK([$1], [$2],
+ [$2=no
+ m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4])
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+ lt_compiler_flag="$3"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ # The option is referenced via a variable to avoid confusing sed.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD)
+ (eval "$lt_compile" 2>conftest.err)
+ ac_status=$?
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+ if (exit $ac_status) && test -s "$ac_outfile"; then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings other than the usual output.
+ $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+ $2=yes
+ fi
+ fi
+ $RM conftest*
+])
+
+if test x"[$]$2" = xyes; then
+ m4_if([$5], , :, [$5])
+else
+ m4_if([$6], , :, [$6])
+fi
+])# _LT_COMPILER_OPTION
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], [])
+
+
+# _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS,
+# [ACTION-SUCCESS], [ACTION-FAILURE])
+# ----------------------------------------------------
+# Check whether the given linker option works
+AC_DEFUN([_LT_LINKER_OPTION],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_SED])dnl
+AC_CACHE_CHECK([$1], [$2],
+ [$2=no
+ save_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS $3"
+ echo "$lt_simple_link_test_code" > conftest.$ac_ext
+ if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+ # The linker can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ if test -s conftest.err; then
+ # Append any errors to the config.log.
+ cat conftest.err 1>&AS_MESSAGE_LOG_FD
+ $ECHO "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if diff conftest.exp conftest.er2 >/dev/null; then
+ $2=yes
+ fi
+ else
+ $2=yes
+ fi
+ fi
+ $RM -r conftest*
+ LDFLAGS="$save_LDFLAGS"
+])
+
+if test x"[$]$2" = xyes; then
+ m4_if([$4], , :, [$4])
+else
+ m4_if([$5], , :, [$5])
+fi
+])# _LT_LINKER_OPTION
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], [])
+
+
+# LT_CMD_MAX_LEN
+#---------------
+AC_DEFUN([LT_CMD_MAX_LEN],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+# find the maximum length of command line arguments
+AC_MSG_CHECKING([the maximum length of command line arguments])
+AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl
+ i=0
+ teststring="ABCD"
+
+ case $build_os in
+ msdosdjgpp*)
+ # On DJGPP, this test can blow up pretty badly due to problems in libc
+ # (any single argument exceeding 2000 bytes causes a buffer overrun
+ # during glob expansion). Even if it were fixed, the result of this
+ # check would be larger than it should be.
+ lt_cv_sys_max_cmd_len=12288; # 12K is about right
+ ;;
+
+ gnu*)
+ # Under GNU Hurd, this test is not required because there is
+ # no limit to the length of command line arguments.
+ # Libtool will interpret -1 as no limit whatsoever
+ lt_cv_sys_max_cmd_len=-1;
+ ;;
+
+ cygwin* | mingw* | cegcc*)
+ # On Win9x/ME, this test blows up -- it succeeds, but takes
+ # about 5 minutes as the teststring grows exponentially.
+ # Worse, since 9x/ME are not pre-emptively multitasking,
+ # you end up with a "frozen" computer, even though with patience
+ # the test eventually succeeds (with a max line length of 256k).
+ # Instead, let's just punt: use the minimum linelength reported by
+ # all of the supported platforms: 8192 (on NT/2K/XP).
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ amigaos*)
+ # On AmigaOS with pdksh, this test takes hours, literally.
+ # So we just punt and use a minimum line length of 8192.
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ netbsd* | freebsd* | openbsd* | darwin* | dragonfly*)
+ # This has been around since 386BSD, at least. Likely further.
+ if test -x /sbin/sysctl; then
+ lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax`
+ elif test -x /usr/sbin/sysctl; then
+ lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax`
+ else
+ lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs
+ fi
+ # And add a safety zone
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+ ;;
+
+ interix*)
+ # We know the value 262144 and hardcode it with a safety zone (like BSD)
+ lt_cv_sys_max_cmd_len=196608
+ ;;
+
+ osf*)
+ # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure
+ # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not
+ # nice to cause kernel panics so lets avoid the loop below.
+ # First set a reasonable default.
+ lt_cv_sys_max_cmd_len=16384
+ #
+ if test -x /sbin/sysconfig; then
+ case `/sbin/sysconfig -q proc exec_disable_arg_limit` in
+ *1*) lt_cv_sys_max_cmd_len=-1 ;;
+ esac
+ fi
+ ;;
+ sco3.2v5*)
+ lt_cv_sys_max_cmd_len=102400
+ ;;
+ sysv5* | sco5v6* | sysv4.2uw2*)
+ kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null`
+ if test -n "$kargmax"; then
+ lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'`
+ else
+ lt_cv_sys_max_cmd_len=32768
+ fi
+ ;;
+ *)
+ lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null`
+ if test -n "$lt_cv_sys_max_cmd_len"; then
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+ else
+ # Make teststring a little bigger before we do anything with it.
+ # a 1K string should be a reasonable start.
+ for i in 1 2 3 4 5 6 7 8 ; do
+ teststring=$teststring$teststring
+ done
+ SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}}
+ # If test is not a shell built-in, we'll probably end up computing a
+ # maximum length that is only half of the actual maximum length, but
+ # we can't tell.
+ while { test "X"`$SHELL [$]0 --fallback-echo "X$teststring$teststring" 2>/dev/null` \
+ = "XX$teststring$teststring"; } >/dev/null 2>&1 &&
+ test $i != 17 # 1/2 MB should be enough
+ do
+ i=`expr $i + 1`
+ teststring=$teststring$teststring
+ done
+ # Only check the string length outside the loop.
+ lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1`
+ teststring=
+ # Add a significant safety factor because C++ compilers can tack on
+ # massive amounts of additional arguments before passing them to the
+ # linker. It appears as though 1/2 is a usable value.
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2`
+ fi
+ ;;
+ esac
+])
+if test -n $lt_cv_sys_max_cmd_len ; then
+ AC_MSG_RESULT($lt_cv_sys_max_cmd_len)
+else
+ AC_MSG_RESULT(none)
+fi
+max_cmd_len=$lt_cv_sys_max_cmd_len
+_LT_DECL([], [max_cmd_len], [0],
+ [What is the maximum length of a command?])
+])# LT_CMD_MAX_LEN
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], [])
+
+
+# _LT_HEADER_DLFCN
+# ----------------
+m4_defun([_LT_HEADER_DLFCN],
+[AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl
+])# _LT_HEADER_DLFCN
+
+
+# _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE,
+# ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING)
+# ----------------------------------------------------------------
+m4_defun([_LT_TRY_DLOPEN_SELF],
+[m4_require([_LT_HEADER_DLFCN])dnl
+if test "$cross_compiling" = yes; then :
+ [$4]
+else
+ lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+ lt_status=$lt_dlunknown
+ cat > conftest.$ac_ext <<_LT_EOF
+[#line __oline__ "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+# define LT_DLGLOBAL RTLD_GLOBAL
+#else
+# ifdef DL_GLOBAL
+# define LT_DLGLOBAL DL_GLOBAL
+# else
+# define LT_DLGLOBAL 0
+# endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+ find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+# ifdef RTLD_LAZY
+# define LT_DLLAZY_OR_NOW RTLD_LAZY
+# else
+# ifdef DL_LAZY
+# define LT_DLLAZY_OR_NOW DL_LAZY
+# else
+# ifdef RTLD_NOW
+# define LT_DLLAZY_OR_NOW RTLD_NOW
+# else
+# ifdef DL_NOW
+# define LT_DLLAZY_OR_NOW DL_NOW
+# else
+# define LT_DLLAZY_OR_NOW 0
+# endif
+# endif
+# endif
+# endif
+#endif
+
+void fnord() { int i=42;}
+int main ()
+{
+ void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+ int status = $lt_dlunknown;
+
+ if (self)
+ {
+ if (dlsym (self,"fnord")) status = $lt_dlno_uscore;
+ else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+ /* dlclose (self); */
+ }
+ else
+ puts (dlerror ());
+
+ return status;
+}]
+_LT_EOF
+ if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext} 2>/dev/null; then
+ (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null
+ lt_status=$?
+ case x$lt_status in
+ x$lt_dlno_uscore) $1 ;;
+ x$lt_dlneed_uscore) $2 ;;
+ x$lt_dlunknown|x*) $3 ;;
+ esac
+ else :
+ # compilation failed
+ $3
+ fi
+fi
+rm -fr conftest*
+])# _LT_TRY_DLOPEN_SELF
+
+
+# LT_SYS_DLOPEN_SELF
+# ------------------
+AC_DEFUN([LT_SYS_DLOPEN_SELF],
+[m4_require([_LT_HEADER_DLFCN])dnl
+if test "x$enable_dlopen" != xyes; then
+ enable_dlopen=unknown
+ enable_dlopen_self=unknown
+ enable_dlopen_self_static=unknown
+else
+ lt_cv_dlopen=no
+ lt_cv_dlopen_libs=
+
+ case $host_os in
+ beos*)
+ lt_cv_dlopen="load_add_on"
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+ ;;
+
+ mingw* | pw32* | cegcc*)
+ lt_cv_dlopen="LoadLibrary"
+ lt_cv_dlopen_libs=
+ ;;
+
+ cygwin*)
+ lt_cv_dlopen="dlopen"
+ lt_cv_dlopen_libs=
+ ;;
+
+ darwin*)
+ # if libdl is installed we need to link against it
+ AC_CHECK_LIB([dl], [dlopen],
+ [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],[
+ lt_cv_dlopen="dyld"
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+ ])
+ ;;
+
+ *)
+ AC_CHECK_FUNC([shl_load],
+ [lt_cv_dlopen="shl_load"],
+ [AC_CHECK_LIB([dld], [shl_load],
+ [lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"],
+ [AC_CHECK_FUNC([dlopen],
+ [lt_cv_dlopen="dlopen"],
+ [AC_CHECK_LIB([dl], [dlopen],
+ [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],
+ [AC_CHECK_LIB([svld], [dlopen],
+ [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"],
+ [AC_CHECK_LIB([dld], [dld_link],
+ [lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"])
+ ])
+ ])
+ ])
+ ])
+ ])
+ ;;
+ esac
+
+ if test "x$lt_cv_dlopen" != xno; then
+ enable_dlopen=yes
+ else
+ enable_dlopen=no
+ fi
+
+ case $lt_cv_dlopen in
+ dlopen)
+ save_CPPFLAGS="$CPPFLAGS"
+ test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+
+ save_LDFLAGS="$LDFLAGS"
+ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
+
+ save_LIBS="$LIBS"
+ LIBS="$lt_cv_dlopen_libs $LIBS"
+
+ AC_CACHE_CHECK([whether a program can dlopen itself],
+ lt_cv_dlopen_self, [dnl
+ _LT_TRY_DLOPEN_SELF(
+ lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes,
+ lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross)
+ ])
+
+ if test "x$lt_cv_dlopen_self" = xyes; then
+ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\"
+ AC_CACHE_CHECK([whether a statically linked program can dlopen itself],
+ lt_cv_dlopen_self_static, [dnl
+ _LT_TRY_DLOPEN_SELF(
+ lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes,
+ lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross)
+ ])
+ fi
+
+ CPPFLAGS="$save_CPPFLAGS"
+ LDFLAGS="$save_LDFLAGS"
+ LIBS="$save_LIBS"
+ ;;
+ esac
+
+ case $lt_cv_dlopen_self in
+ yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+ *) enable_dlopen_self=unknown ;;
+ esac
+
+ case $lt_cv_dlopen_self_static in
+ yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+ *) enable_dlopen_self_static=unknown ;;
+ esac
+fi
+_LT_DECL([dlopen_support], [enable_dlopen], [0],
+ [Whether dlopen is supported])
+_LT_DECL([dlopen_self], [enable_dlopen_self], [0],
+ [Whether dlopen of programs is supported])
+_LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0],
+ [Whether dlopen of statically linked programs is supported])
+])# LT_SYS_DLOPEN_SELF
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], [])
+
+
+# _LT_COMPILER_C_O([TAGNAME])
+# ---------------------------
+# Check to see if options -c and -o are simultaneously supported by compiler.
+# This macro does not hard code the compiler like AC_PROG_CC_C_O.
+m4_defun([_LT_COMPILER_C_O],
+[m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_TAG_COMPILER])dnl
+AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext],
+ [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)],
+ [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no
+ $RM -r conftest 2>/dev/null
+ mkdir conftest
+ cd conftest
+ mkdir out
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ lt_compiler_flag="-o out/conftest2.$ac_objext"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD)
+ (eval "$lt_compile" 2>out/conftest.err)
+ ac_status=$?
+ cat out/conftest.err >&AS_MESSAGE_LOG_FD
+ echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+ if (exit $ac_status) && test -s out/conftest2.$ac_objext
+ then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp
+ $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+ if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+ _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes
+ fi
+ fi
+ chmod u+w . 2>&AS_MESSAGE_LOG_FD
+ $RM conftest*
+ # SGI C++ compiler will create directory out/ii_files/ for
+ # template instantiation
+ test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+ $RM out/* && rmdir out
+ cd ..
+ $RM -r conftest
+ $RM conftest*
+])
+_LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1],
+ [Does compiler simultaneously support -c and -o options?])
+])# _LT_COMPILER_C_O
+
+
+# _LT_COMPILER_FILE_LOCKS([TAGNAME])
+# ----------------------------------
+# Check to see if we can do hard links to lock some files if needed
+m4_defun([_LT_COMPILER_FILE_LOCKS],
+[m4_require([_LT_ENABLE_LOCK])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+_LT_COMPILER_C_O([$1])
+
+hard_links="nottested"
+if test "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" = no && test "$need_locks" != no; then
+ # do not overwrite the value of need_locks provided by the user
+ AC_MSG_CHECKING([if we can lock with hard links])
+ hard_links=yes
+ $RM conftest*
+ ln conftest.a conftest.b 2>/dev/null && hard_links=no
+ touch conftest.a
+ ln conftest.a conftest.b 2>&5 || hard_links=no
+ ln conftest.a conftest.b 2>/dev/null && hard_links=no
+ AC_MSG_RESULT([$hard_links])
+ if test "$hard_links" = no; then
+ AC_MSG_WARN([`$CC' does not support `-c -o', so `make -j' may be unsafe])
+ need_locks=warn
+ fi
+else
+ need_locks=no
+fi
+_LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?])
+])# _LT_COMPILER_FILE_LOCKS
+
+
+# _LT_CHECK_OBJDIR
+# ----------------
+m4_defun([_LT_CHECK_OBJDIR],
+[AC_CACHE_CHECK([for objdir], [lt_cv_objdir],
+[rm -f .libs 2>/dev/null
+mkdir .libs 2>/dev/null
+if test -d .libs; then
+ lt_cv_objdir=.libs
+else
+ # MS-DOS does not allow filenames that begin with a dot.
+ lt_cv_objdir=_libs
+fi
+rmdir .libs 2>/dev/null])
+objdir=$lt_cv_objdir
+_LT_DECL([], [objdir], [0],
+ [The name of the directory that contains temporary libtool files])dnl
+m4_pattern_allow([LT_OBJDIR])dnl
+AC_DEFINE_UNQUOTED(LT_OBJDIR, "$lt_cv_objdir/",
+ [Define to the sub-directory in which libtool stores uninstalled libraries.])
+])# _LT_CHECK_OBJDIR
+
+
+# _LT_LINKER_HARDCODE_LIBPATH([TAGNAME])
+# --------------------------------------
+# Check hardcoding attributes.
+m4_defun([_LT_LINKER_HARDCODE_LIBPATH],
+[AC_MSG_CHECKING([how to hardcode library paths into programs])
+_LT_TAGVAR(hardcode_action, $1)=
+if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" ||
+ test -n "$_LT_TAGVAR(runpath_var, $1)" ||
+ test "X$_LT_TAGVAR(hardcode_automatic, $1)" = "Xyes" ; then
+
+ # We can hardcode non-existent directories.
+ if test "$_LT_TAGVAR(hardcode_direct, $1)" != no &&
+ # If the only mechanism to avoid hardcoding is shlibpath_var, we
+ # have to relink, otherwise we might link with an installed library
+ # when we should be linking with a yet-to-be-installed one
+ ## test "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" != no &&
+ test "$_LT_TAGVAR(hardcode_minus_L, $1)" != no; then
+ # Linking always hardcodes the temporary library directory.
+ _LT_TAGVAR(hardcode_action, $1)=relink
+ else
+ # We can link without hardcoding, and we can hardcode nonexisting dirs.
+ _LT_TAGVAR(hardcode_action, $1)=immediate
+ fi
+else
+ # We cannot hardcode anything, or else we can only hardcode existing
+ # directories.
+ _LT_TAGVAR(hardcode_action, $1)=unsupported
+fi
+AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)])
+
+if test "$_LT_TAGVAR(hardcode_action, $1)" = relink ||
+ test "$_LT_TAGVAR(inherit_rpath, $1)" = yes; then
+ # Fast installation is not supported
+ enable_fast_install=no
+elif test "$shlibpath_overrides_runpath" = yes ||
+ test "$enable_shared" = no; then
+ # Fast installation is not necessary
+ enable_fast_install=needless
+fi
+_LT_TAGDECL([], [hardcode_action], [0],
+ [How to hardcode a shared library path into an executable])
+])# _LT_LINKER_HARDCODE_LIBPATH
+
+
+# _LT_CMD_STRIPLIB
+# ----------------
+m4_defun([_LT_CMD_STRIPLIB],
+[m4_require([_LT_DECL_EGREP])
+striplib=
+old_striplib=
+AC_MSG_CHECKING([whether stripping libraries is possible])
+if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then
+ test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
+ test -z "$striplib" && striplib="$STRIP --strip-unneeded"
+ AC_MSG_RESULT([yes])
+else
+# FIXME - insert some real tests, host_os isn't really good enough
+ case $host_os in
+ darwin*)
+ if test -n "$STRIP" ; then
+ striplib="$STRIP -x"
+ old_striplib="$STRIP -S"
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ fi
+ ;;
+ *)
+ AC_MSG_RESULT([no])
+ ;;
+ esac
+fi
+_LT_DECL([], [old_striplib], [1], [Commands to strip libraries])
+_LT_DECL([], [striplib], [1])
+])# _LT_CMD_STRIPLIB
+
+
+# _LT_SYS_DYNAMIC_LINKER([TAG])
+# -----------------------------
+# PORTME Fill in your ld.so characteristics
+m4_defun([_LT_SYS_DYNAMIC_LINKER],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_OBJDUMP])dnl
+m4_require([_LT_DECL_SED])dnl
+AC_MSG_CHECKING([dynamic linker characteristics])
+m4_if([$1],
+ [], [
+if test "$GCC" = yes; then
+ case $host_os in
+ darwin*) lt_awk_arg="/^libraries:/,/LR/" ;;
+ *) lt_awk_arg="/^libraries:/" ;;
+ esac
+ lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e "s,=/,/,g"`
+ if $ECHO "$lt_search_path_spec" | $GREP ';' >/dev/null ; then
+ # if the path contains ";" then we assume it to be the separator
+ # otherwise default to the standard path separator (i.e. ":") - it is
+ # assumed that no part of a normal pathname contains ";" but that should
+ # okay in the real world where ";" in dirpaths is itself problematic.
+ lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e 's/;/ /g'`
+ else
+ lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+ fi
+ # Ok, now we have the path, separated by spaces, we can step through it
+ # and add multilib dir if necessary.
+ lt_tmp_lt_search_path_spec=
+ lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null`
+ for lt_sys_path in $lt_search_path_spec; do
+ if test -d "$lt_sys_path/$lt_multi_os_dir"; then
+ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir"
+ else
+ test -d "$lt_sys_path" && \
+ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path"
+ fi
+ done
+ lt_search_path_spec=`$ECHO $lt_tmp_lt_search_path_spec | awk '
+BEGIN {RS=" "; FS="/|\n";} {
+ lt_foo="";
+ lt_count=0;
+ for (lt_i = NF; lt_i > 0; lt_i--) {
+ if ($lt_i != "" && $lt_i != ".") {
+ if ($lt_i == "..") {
+ lt_count++;
+ } else {
+ if (lt_count == 0) {
+ lt_foo="/" $lt_i lt_foo;
+ } else {
+ lt_count--;
+ }
+ }
+ }
+ }
+ if (lt_foo != "") { lt_freq[[lt_foo]]++; }
+ if (lt_freq[[lt_foo]] == 1) { print lt_foo; }
+}'`
+ sys_lib_search_path_spec=`$ECHO $lt_search_path_spec`
+else
+ sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+fi])
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+shrext_cmds=".so"
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+need_lib_prefix=unknown
+hardcode_into_libs=no
+
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+need_version=unknown
+
+case $host_os in
+aix3*)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a'
+ shlibpath_var=LIBPATH
+
+ # AIX 3 has no versioning support, so we append a major version to the name.
+ soname_spec='${libname}${release}${shared_ext}$major'
+ ;;
+
+aix[[4-9]]*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ hardcode_into_libs=yes
+ if test "$host_cpu" = ia64; then
+ # AIX 5 supports IA64
+ library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ else
+ # With GCC up to 2.95.x, collect2 would create an import file
+ # for dependence libraries. The import file would start with
+ # the line `#! .'. This would cause the generated library to
+ # depend on `.', always an invalid library. This was fixed in
+ # development snapshots of GCC prior to 3.0.
+ case $host_os in
+ aix4 | aix4.[[01]] | aix4.[[01]].*)
+ if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
+ echo ' yes '
+ echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then
+ :
+ else
+ can_build_shared=no
+ fi
+ ;;
+ esac
+ # AIX (on Power*) has no versioning support, so currently we can not hardcode correct
+ # soname into executable. Probably we can add versioning support to
+ # collect2, so additional links can be useful in future.
+ if test "$aix_use_runtimelinking" = yes; then
+ # If using run time linking (on AIX 4.2 or later) use lib<name>.so
+ # instead of lib<name>.a to let people know that these are not
+ # typical AIX shared libraries.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ else
+ # We preserve .a as extension for shared libraries through AIX4.2
+ # and later when we are not doing run time linking.
+ library_names_spec='${libname}${release}.a $libname.a'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ fi
+ shlibpath_var=LIBPATH
+ fi
+ ;;
+
+amigaos*)
+ case $host_cpu in
+ powerpc)
+ # Since July 2007 AmigaOS4 officially supports .so libraries.
+ # When compiling the executable, add -use-dynld -Lsobjs: to the compileline.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ ;;
+ m68k)
+ library_names_spec='$libname.ixlibrary $libname.a'
+ # Create ${libname}_ixlibrary.a entries in /sys/libs.
+ finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$ECHO "X$lib" | $Xsed -e '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+ ;;
+ esac
+ ;;
+
+beos*)
+ library_names_spec='${libname}${shared_ext}'
+ dynamic_linker="$host_os ld.so"
+ shlibpath_var=LIBRARY_PATH
+ ;;
+
+bsdi[[45]]*)
+ version_type=linux
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+ sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+ # the default ld.so.conf also contains /usr/contrib/lib and
+ # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+ # libtool to hard-code these into programs
+ ;;
+
+cygwin* | mingw* | pw32* | cegcc*)
+ version_type=windows
+ shrext_cmds=".dll"
+ need_version=no
+ need_lib_prefix=no
+
+ case $GCC,$host_os in
+ yes,cygwin* | yes,mingw* | yes,pw32* | yes,cegcc*)
+ library_names_spec='$libname.dll.a'
+ # DLL is installed to $(libdir)/../bin by postinstall_cmds
+ postinstall_cmds='base_file=`basename \${file}`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~
+ dldir=$destdir/`dirname \$dlpath`~
+ test -d \$dldir || mkdir -p \$dldir~
+ $install_prog $dir/$dlname \$dldir/$dlname~
+ chmod a+x \$dldir/$dlname~
+ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+ eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+ fi'
+ postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+ dlpath=$dir/\$dldll~
+ $RM \$dlpath'
+ shlibpath_overrides_runpath=yes
+
+ case $host_os in
+ cygwin*)
+ # Cygwin DLLs use 'cyg' prefix rather than 'lib'
+ soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
+ sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib"
+ ;;
+ mingw* | cegcc*)
+ # MinGW DLLs use traditional 'lib' prefix
+ soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
+ sys_lib_search_path_spec=`$CC -print-search-dirs | $GREP "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"`
+ if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then
+ # It is most probably a Windows format PATH printed by
+ # mingw gcc, but we are running on Cygwin. Gcc prints its search
+ # path with ; separators, and with drive letters. We can handle the
+ # drive letters (cygwin fileutils understands them), so leave them,
+ # especially as we might pass files found there to a mingw objdump,
+ # which wouldn't understand a cygwinified path. Ahh.
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+ else
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+ fi
+ ;;
+ pw32*)
+ # pw32 DLLs use 'pw' prefix rather than 'lib'
+ library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
+ ;;
+ esac
+ ;;
+
+ *)
+ library_names_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext} $libname.lib'
+ ;;
+ esac
+ dynamic_linker='Win32 ld.exe'
+ # FIXME: first we should search . and the directory the executable is in
+ shlibpath_var=PATH
+ ;;
+
+darwin* | rhapsody*)
+ dynamic_linker="$host_os dyld"
+ version_type=darwin
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext'
+ soname_spec='${libname}${release}${major}$shared_ext'
+ shlibpath_overrides_runpath=yes
+ shlibpath_var=DYLD_LIBRARY_PATH
+ shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'
+m4_if([$1], [],[
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"])
+ sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
+ ;;
+
+dgux*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+freebsd1*)
+ dynamic_linker=no
+ ;;
+
+freebsd* | dragonfly*)
+ # DragonFly does not have aout. When/if they implement a new
+ # versioning mechanism, adjust this.
+ if test -x /usr/bin/objformat; then
+ objformat=`/usr/bin/objformat`
+ else
+ case $host_os in
+ freebsd[[123]]*) objformat=aout ;;
+ *) objformat=elf ;;
+ esac
+ fi
+ version_type=freebsd-$objformat
+ case $version_type in
+ freebsd-elf*)
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+ need_version=no
+ need_lib_prefix=no
+ ;;
+ freebsd-*)
+ library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix'
+ need_version=yes
+ ;;
+ esac
+ shlibpath_var=LD_LIBRARY_PATH
+ case $host_os in
+ freebsd2*)
+ shlibpath_overrides_runpath=yes
+ ;;
+ freebsd3.[[01]]* | freebsdelf3.[[01]]*)
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+ freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \
+ freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1)
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+ *) # from 4.6 on, and DragonFly
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+ esac
+ ;;
+
+gnu*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ hardcode_into_libs=yes
+ ;;
+
+hpux9* | hpux10* | hpux11*)
+ # Give a soname corresponding to the major version so that dld.sl refuses to
+ # link against other versions.
+ version_type=sunos
+ need_lib_prefix=no
+ need_version=no
+ case $host_cpu in
+ ia64*)
+ shrext_cmds='.so'
+ hardcode_into_libs=yes
+ dynamic_linker="$host_os dld.so"
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ if test "X$HPUX_IA64_MODE" = X32; then
+ sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
+ else
+ sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
+ fi
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ ;;
+ hppa*64*)
+ shrext_cmds='.sl'
+ hardcode_into_libs=yes
+ dynamic_linker="$host_os dld.sl"
+ shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
+ shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ ;;
+ *)
+ shrext_cmds='.sl'
+ dynamic_linker="$host_os dld.sl"
+ shlibpath_var=SHLIB_PATH
+ shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ ;;
+ esac
+ # HP-UX runs *really* slowly unless shared libraries are mode 555.
+ postinstall_cmds='chmod 555 $lib'
+ ;;
+
+interix[[3-9]]*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+
+irix5* | irix6* | nonstopux*)
+ case $host_os in
+ nonstopux*) version_type=nonstopux ;;
+ *)
+ if test "$lt_cv_prog_gnu_ld" = yes; then
+ version_type=linux
+ else
+ version_type=irix
+ fi ;;
+ esac
+ need_lib_prefix=no
+ need_version=no
+ soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}'
+ case $host_os in
+ irix5* | nonstopux*)
+ libsuff= shlibsuff=
+ ;;
+ *)
+ case $LD in # libtool.m4 will add one of these switches to LD
+ *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
+ libsuff= shlibsuff= libmagic=32-bit;;
+ *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
+ libsuff=32 shlibsuff=N32 libmagic=N32;;
+ *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
+ libsuff=64 shlibsuff=64 libmagic=64-bit;;
+ *) libsuff= shlibsuff= libmagic=never-match;;
+ esac
+ ;;
+ esac
+ shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+ shlibpath_overrides_runpath=no
+ sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}"
+ sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}"
+ hardcode_into_libs=yes
+ ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux*oldld* | linux*aout* | linux*coff*)
+ dynamic_linker=no
+ ;;
+
+# This must be Linux ELF.
+linux* | k*bsd*-gnu)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ # Some binutils ld are patched to set DT_RUNPATH
+ save_LDFLAGS=$LDFLAGS
+ save_libdir=$libdir
+ eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \
+ LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\""
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])],
+ [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null],
+ [shlibpath_overrides_runpath=yes])])
+ LDFLAGS=$save_LDFLAGS
+ libdir=$save_libdir
+
+ # This implies no fast_install, which is unacceptable.
+ # Some rework will be needed to allow for fast_install
+ # before this can be enabled.
+ hardcode_into_libs=yes
+
+ # Append ld.so.conf contents to the search path
+ if test -f /etc/ld.so.conf; then
+ lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '`
+ sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
+ fi
+
+ # We used to test for /lib/ld.so.1 and disable shared libraries on
+ # powerpc, because MkLinux only supported shared libraries with the
+ # GNU dynamic linker. Since this was broken with cross compilers,
+ # most powerpc-linux boxes support dynamic linking these days and
+ # people can always --disable-shared, the test was removed, and we
+ # assume the GNU/Linux dynamic linker is in use.
+ dynamic_linker='GNU/Linux ld.so'
+ ;;
+
+netbsd*)
+ version_type=sunos
+ need_lib_prefix=no
+ need_version=no
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ dynamic_linker='NetBSD (a.out) ld.so'
+ else
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ dynamic_linker='NetBSD ld.elf_so'
+ fi
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+
+newsos6)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ ;;
+
+*nto* | *qnx*)
+ version_type=qnx
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ dynamic_linker='ldqnx.so'
+ ;;
+
+openbsd*)
+ version_type=sunos
+ sys_lib_dlsearch_path_spec="/usr/lib"
+ need_lib_prefix=no
+ # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs.
+ case $host_os in
+ openbsd3.3 | openbsd3.3.*) need_version=yes ;;
+ *) need_version=no ;;
+ esac
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+ case $host_os in
+ openbsd2.[[89]] | openbsd2.[[89]].*)
+ shlibpath_overrides_runpath=no
+ ;;
+ *)
+ shlibpath_overrides_runpath=yes
+ ;;
+ esac
+ else
+ shlibpath_overrides_runpath=yes
+ fi
+ ;;
+
+os2*)
+ libname_spec='$name'
+ shrext_cmds=".dll"
+ need_lib_prefix=no
+ library_names_spec='$libname${shared_ext} $libname.a'
+ dynamic_linker='OS/2 ld.exe'
+ shlibpath_var=LIBPATH
+ ;;
+
+osf3* | osf4* | osf5*)
+ version_type=osf
+ need_lib_prefix=no
+ need_version=no
+ soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+ sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec"
+ ;;
+
+rdos*)
+ dynamic_linker=no
+ ;;
+
+solaris*)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ # ldd complains unless libraries are executable
+ postinstall_cmds='chmod +x $lib'
+ ;;
+
+sunos4*)
+ version_type=sunos
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ if test "$with_gnu_ld" = yes; then
+ need_lib_prefix=no
+ fi
+ need_version=yes
+ ;;
+
+sysv4 | sysv4.3*)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ case $host_vendor in
+ sni)
+ shlibpath_overrides_runpath=no
+ need_lib_prefix=no
+ runpath_var=LD_RUN_PATH
+ ;;
+ siemens)
+ need_lib_prefix=no
+ ;;
+ motorola)
+ need_lib_prefix=no
+ need_version=no
+ shlibpath_overrides_runpath=no
+ sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+ ;;
+ esac
+ ;;
+
+sysv4*MP*)
+ if test -d /usr/nec ;then
+ version_type=linux
+ library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}'
+ soname_spec='$libname${shared_ext}.$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ fi
+ ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+ version_type=freebsd-elf
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ if test "$with_gnu_ld" = yes; then
+ sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib'
+ else
+ sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'
+ case $host_os in
+ sco3.2v5*)
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /lib"
+ ;;
+ esac
+ fi
+ sys_lib_dlsearch_path_spec='/usr/lib'
+ ;;
+
+tpf*)
+ # TPF is a cross-target only. Preferred cross-host = GNU/Linux.
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+
+uts4*)
+ version_type=linux
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+*)
+ dynamic_linker=no
+ ;;
+esac
+AC_MSG_RESULT([$dynamic_linker])
+test "$dynamic_linker" = no && can_build_shared=no
+
+variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
+if test "$GCC" = yes; then
+ variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
+fi
+
+if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then
+ sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec"
+fi
+if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then
+ sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec"
+fi
+
+_LT_DECL([], [variables_saved_for_relink], [1],
+ [Variables whose values should be saved in libtool wrapper scripts and
+ restored at link time])
+_LT_DECL([], [need_lib_prefix], [0],
+ [Do we need the "lib" prefix for modules?])
+_LT_DECL([], [need_version], [0], [Do we need a version for libraries?])
+_LT_DECL([], [version_type], [0], [Library versioning type])
+_LT_DECL([], [runpath_var], [0], [Shared library runtime path variable])
+_LT_DECL([], [shlibpath_var], [0],[Shared library path variable])
+_LT_DECL([], [shlibpath_overrides_runpath], [0],
+ [Is shlibpath searched before the hard-coded library search path?])
+_LT_DECL([], [libname_spec], [1], [Format of library name prefix])
+_LT_DECL([], [library_names_spec], [1],
+ [[List of archive names. First name is the real one, the rest are links.
+ The last name is the one that the linker finds with -lNAME]])
+_LT_DECL([], [soname_spec], [1],
+ [[The coded name of the library, if different from the real name]])
+_LT_DECL([], [postinstall_cmds], [2],
+ [Command to use after installation of a shared archive])
+_LT_DECL([], [postuninstall_cmds], [2],
+ [Command to use after uninstallation of a shared archive])
+_LT_DECL([], [finish_cmds], [2],
+ [Commands used to finish a libtool library installation in a directory])
+_LT_DECL([], [finish_eval], [1],
+ [[As "finish_cmds", except a single script fragment to be evaled but
+ not shown]])
+_LT_DECL([], [hardcode_into_libs], [0],
+ [Whether we should hardcode library paths into libraries])
+_LT_DECL([], [sys_lib_search_path_spec], [2],
+ [Compile-time system search path for libraries])
+_LT_DECL([], [sys_lib_dlsearch_path_spec], [2],
+ [Run-time system search path for libraries])
+])# _LT_SYS_DYNAMIC_LINKER
+
+
+# _LT_PATH_TOOL_PREFIX(TOOL)
+# --------------------------
+# find a file program which can recognize shared library
+AC_DEFUN([_LT_PATH_TOOL_PREFIX],
+[m4_require([_LT_DECL_EGREP])dnl
+AC_MSG_CHECKING([for $1])
+AC_CACHE_VAL(lt_cv_path_MAGIC_CMD,
+[case $MAGIC_CMD in
+[[\\/*] | ?:[\\/]*])
+ lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+ ;;
+*)
+ lt_save_MAGIC_CMD="$MAGIC_CMD"
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+dnl $ac_dummy forces splitting on constant user-supplied paths.
+dnl POSIX.2 word splitting is done only on the output of word expansions,
+dnl not every word. This closes a longstanding sh security hole.
+ ac_dummy="m4_if([$2], , $PATH, [$2])"
+ for ac_dir in $ac_dummy; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$1; then
+ lt_cv_path_MAGIC_CMD="$ac_dir/$1"
+ if test -n "$file_magic_test_file"; then
+ case $deplibs_check_method in
+ "file_magic "*)
+ file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+ MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+ if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+ $EGREP "$file_magic_regex" > /dev/null; then
+ :
+ else
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such. This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem. Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+ fi ;;
+ esac
+ fi
+ break
+ fi
+ done
+ IFS="$lt_save_ifs"
+ MAGIC_CMD="$lt_save_MAGIC_CMD"
+ ;;
+esac])
+MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+if test -n "$MAGIC_CMD"; then
+ AC_MSG_RESULT($MAGIC_CMD)
+else
+ AC_MSG_RESULT(no)
+fi
+_LT_DECL([], [MAGIC_CMD], [0],
+ [Used to examine libraries when file_magic_cmd begins with "file"])dnl
+])# _LT_PATH_TOOL_PREFIX
+
+# Old name:
+AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], [])
+
+
+# _LT_PATH_MAGIC
+# --------------
+# find a file program which can recognize a shared library
+m4_defun([_LT_PATH_MAGIC],
+[_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH)
+if test -z "$lt_cv_path_MAGIC_CMD"; then
+ if test -n "$ac_tool_prefix"; then
+ _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH)
+ else
+ MAGIC_CMD=:
+ fi
+fi
+])# _LT_PATH_MAGIC
+
+
+# LT_PATH_LD
+# ----------
+# find the pathname to the GNU or non-GNU linker
+AC_DEFUN([LT_PATH_LD],
+[AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_DECL_EGREP])dnl
+
+AC_ARG_WITH([gnu-ld],
+ [AS_HELP_STRING([--with-gnu-ld],
+ [assume the C compiler uses GNU ld @<:@default=no@:>@])],
+ [test "$withval" = no || with_gnu_ld=yes],
+ [with_gnu_ld=no])dnl
+
+ac_prog=ld
+if test "$GCC" = yes; then
+ # Check if gcc -print-prog-name=ld gives a path.
+ AC_MSG_CHECKING([for ld used by $CC])
+ case $host in
+ *-*-mingw*)
+ # gcc leaves a trailing carriage return which upsets mingw
+ ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+ *)
+ ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+ esac
+ case $ac_prog in
+ # Accept absolute paths.
+ [[\\/]]* | ?:[[\\/]]*)
+ re_direlt='/[[^/]][[^/]]*/\.\./'
+ # Canonicalize the pathname of ld
+ ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'`
+ while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
+ ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
+ done
+ test -z "$LD" && LD="$ac_prog"
+ ;;
+ "")
+ # If it fails, then pretend we aren't using GCC.
+ ac_prog=ld
+ ;;
+ *)
+ # If it is relative, then search for the first ld in PATH.
+ with_gnu_ld=unknown
+ ;;
+ esac
+elif test "$with_gnu_ld" = yes; then
+ AC_MSG_CHECKING([for GNU ld])
+else
+ AC_MSG_CHECKING([for non-GNU ld])
+fi
+AC_CACHE_VAL(lt_cv_path_LD,
+[if test -z "$LD"; then
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+ lt_cv_path_LD="$ac_dir/$ac_prog"
+ # Check to see if the program is GNU ld. I'd rather use --version,
+ # but apparently some variants of GNU ld only accept -v.
+ # Break only if it was the GNU/non-GNU ld that we prefer.
+ case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
+ *GNU* | *'with BFD'*)
+ test "$with_gnu_ld" != no && break
+ ;;
+ *)
+ test "$with_gnu_ld" != yes && break
+ ;;
+ esac
+ fi
+ done
+ IFS="$lt_save_ifs"
+else
+ lt_cv_path_LD="$LD" # Let the user override the test with a path.
+fi])
+LD="$lt_cv_path_LD"
+if test -n "$LD"; then
+ AC_MSG_RESULT($LD)
+else
+ AC_MSG_RESULT(no)
+fi
+test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH])
+_LT_PATH_LD_GNU
+AC_SUBST([LD])
+
+_LT_TAGDECL([], [LD], [1], [The linker used to build libraries])
+])# LT_PATH_LD
+
+# Old names:
+AU_ALIAS([AM_PROG_LD], [LT_PATH_LD])
+AU_ALIAS([AC_PROG_LD], [LT_PATH_LD])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_PROG_LD], [])
+dnl AC_DEFUN([AC_PROG_LD], [])
+
+
+# _LT_PATH_LD_GNU
+#- --------------
+m4_defun([_LT_PATH_LD_GNU],
+[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], lt_cv_prog_gnu_ld,
+[# I'd rather use --version here, but apparently some GNU lds only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+ lt_cv_prog_gnu_ld=yes
+ ;;
+*)
+ lt_cv_prog_gnu_ld=no
+ ;;
+esac])
+with_gnu_ld=$lt_cv_prog_gnu_ld
+])# _LT_PATH_LD_GNU
+
+
+# _LT_CMD_RELOAD
+# --------------
+# find reload flag for linker
+# -- PORTME Some linkers may need a different reload flag.
+m4_defun([_LT_CMD_RELOAD],
+[AC_CACHE_CHECK([for $LD option to reload object files],
+ lt_cv_ld_reload_flag,
+ [lt_cv_ld_reload_flag='-r'])
+reload_flag=$lt_cv_ld_reload_flag
+case $reload_flag in
+"" | " "*) ;;
+*) reload_flag=" $reload_flag" ;;
+esac
+reload_cmds='$LD$reload_flag -o $output$reload_objs'
+case $host_os in
+ darwin*)
+ if test "$GCC" = yes; then
+ reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs'
+ else
+ reload_cmds='$LD$reload_flag -o $output$reload_objs'
+ fi
+ ;;
+esac
+_LT_DECL([], [reload_flag], [1], [How to create reloadable object files])dnl
+_LT_DECL([], [reload_cmds], [2])dnl
+])# _LT_CMD_RELOAD
+
+
+# _LT_CHECK_MAGIC_METHOD
+# ----------------------
+# how to check for library dependencies
+# -- PORTME fill in with the dynamic library characteristics
+m4_defun([_LT_CHECK_MAGIC_METHOD],
+[m4_require([_LT_DECL_EGREP])
+m4_require([_LT_DECL_OBJDUMP])
+AC_CACHE_CHECK([how to recognize dependent libraries],
+lt_cv_deplibs_check_method,
+[lt_cv_file_magic_cmd='$MAGIC_CMD'
+lt_cv_file_magic_test_file=
+lt_cv_deplibs_check_method='unknown'
+# Need to set the preceding variable on all platforms that support
+# interlibrary dependencies.
+# 'none' -- dependencies not supported.
+# `unknown' -- same as none, but documents that we really don't know.
+# 'pass_all' -- all dependencies passed with no checks.
+# 'test_compile' -- check by making test program.
+# 'file_magic [[regex]]' -- check by looking for files in library path
+# which responds to the $file_magic_cmd with a given extended regex.
+# If you have `file' or equivalent on your system and you're not sure
+# whether `pass_all' will *always* work, you probably want this one.
+
+case $host_os in
+aix[[4-9]]*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+beos*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+bsdi[[45]]*)
+ lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib)'
+ lt_cv_file_magic_cmd='/usr/bin/file -L'
+ lt_cv_file_magic_test_file=/shlib/libc.so
+ ;;
+
+cygwin*)
+ # func_win32_libid is a shell function defined in ltmain.sh
+ lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+ lt_cv_file_magic_cmd='func_win32_libid'
+ ;;
+
+mingw* | pw32*)
+ # Base MSYS/MinGW do not provide the 'file' command needed by
+ # func_win32_libid shell function, so use a weaker test based on 'objdump',
+ # unless we find 'file', for example because we are cross-compiling.
+ if ( file / ) >/dev/null 2>&1; then
+ lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+ lt_cv_file_magic_cmd='func_win32_libid'
+ else
+ lt_cv_deplibs_check_method='file_magic file format pei*-i386(.*architecture: i386)?'
+ lt_cv_file_magic_cmd='$OBJDUMP -f'
+ fi
+ ;;
+
+cegcc)
+ # use the weaker test based on 'objdump'. See mingw*.
+ lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?'
+ lt_cv_file_magic_cmd='$OBJDUMP -f'
+ ;;
+
+darwin* | rhapsody*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+freebsd* | dragonfly*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+ case $host_cpu in
+ i*86 )
+ # Not sure whether the presence of OpenBSD here was a mistake.
+ # Let's accept both of them until this is cleared up.
+ lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library'
+ lt_cv_file_magic_cmd=/usr/bin/file
+ lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
+ ;;
+ esac
+ else
+ lt_cv_deplibs_check_method=pass_all
+ fi
+ ;;
+
+gnu*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+hpux10.20* | hpux11*)
+ lt_cv_file_magic_cmd=/usr/bin/file
+ case $host_cpu in
+ ia64*)
+ lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64'
+ lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so
+ ;;
+ hppa*64*)
+ [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - PA-RISC [0-9].[0-9]']
+ lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl
+ ;;
+ *)
+ lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]].[[0-9]]) shared library'
+ lt_cv_file_magic_test_file=/usr/lib/libc.sl
+ ;;
+ esac
+ ;;
+
+interix[[3-9]]*)
+ # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here
+ lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$'
+ ;;
+
+irix5* | irix6* | nonstopux*)
+ case $LD in
+ *-32|*"-32 ") libmagic=32-bit;;
+ *-n32|*"-n32 ") libmagic=N32;;
+ *-64|*"-64 ") libmagic=64-bit;;
+ *) libmagic=never-match;;
+ esac
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+# This must be Linux ELF.
+linux* | k*bsd*-gnu)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+ lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$'
+ else
+ lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$'
+ fi
+ ;;
+
+newos6*)
+ lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)'
+ lt_cv_file_magic_cmd=/usr/bin/file
+ lt_cv_file_magic_test_file=/usr/lib/libnls.so
+ ;;
+
+*nto* | *qnx*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+openbsd*)
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+ lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$'
+ else
+ lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$'
+ fi
+ ;;
+
+osf3* | osf4* | osf5*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+rdos*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+solaris*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+sysv4 | sysv4.3*)
+ case $host_vendor in
+ motorola)
+ lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]'
+ lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*`
+ ;;
+ ncr)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ sequent)
+ lt_cv_file_magic_cmd='/bin/file'
+ lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )'
+ ;;
+ sni)
+ lt_cv_file_magic_cmd='/bin/file'
+ lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib"
+ lt_cv_file_magic_test_file=/lib/libc.so
+ ;;
+ siemens)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ pc)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ esac
+ ;;
+
+tpf*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+esac
+])
+file_magic_cmd=$lt_cv_file_magic_cmd
+deplibs_check_method=$lt_cv_deplibs_check_method
+test -z "$deplibs_check_method" && deplibs_check_method=unknown
+
+_LT_DECL([], [deplibs_check_method], [1],
+ [Method to check whether dependent libraries are shared objects])
+_LT_DECL([], [file_magic_cmd], [1],
+ [Command to use when deplibs_check_method == "file_magic"])
+])# _LT_CHECK_MAGIC_METHOD
+
+
+# LT_PATH_NM
+# ----------
+# find the pathname to a BSD- or MS-compatible name lister
+AC_DEFUN([LT_PATH_NM],
+[AC_REQUIRE([AC_PROG_CC])dnl
+AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM,
+[if test -n "$NM"; then
+ # Let the user override the test.
+ lt_cv_path_NM="$NM"
+else
+ lt_nm_to_check="${ac_tool_prefix}nm"
+ if test -n "$ac_tool_prefix" && test "$build" = "$host"; then
+ lt_nm_to_check="$lt_nm_to_check nm"
+ fi
+ for lt_tmp_nm in $lt_nm_to_check; do
+ lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do
+ IFS="$lt_save_ifs"
+ test -z "$ac_dir" && ac_dir=.
+ tmp_nm="$ac_dir/$lt_tmp_nm"
+ if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then
+ # Check to see if the nm accepts a BSD-compat flag.
+ # Adding the `sed 1q' prevents false positives on HP-UX, which says:
+ # nm: unknown option "B" ignored
+ # Tru64's nm complains that /dev/null is an invalid object file
+ case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in
+ */dev/null* | *'Invalid file or object type'*)
+ lt_cv_path_NM="$tmp_nm -B"
+ break
+ ;;
+ *)
+ case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in
+ */dev/null*)
+ lt_cv_path_NM="$tmp_nm -p"
+ break
+ ;;
+ *)
+ lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
+ continue # so that we can try to find one that supports BSD flags
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ done
+ IFS="$lt_save_ifs"
+ done
+ : ${lt_cv_path_NM=no}
+fi])
+if test "$lt_cv_path_NM" != "no"; then
+ NM="$lt_cv_path_NM"
+else
+ # Didn't find any BSD compatible name lister, look for dumpbin.
+ AC_CHECK_TOOLS(DUMPBIN, ["dumpbin -symbols" "link -dump -symbols"], :)
+ AC_SUBST([DUMPBIN])
+ if test "$DUMPBIN" != ":"; then
+ NM="$DUMPBIN"
+ fi
+fi
+test -z "$NM" && NM=nm
+AC_SUBST([NM])
+_LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl
+
+AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface],
+ [lt_cv_nm_interface="BSD nm"
+ echo "int some_variable = 0;" > conftest.$ac_ext
+ (eval echo "\"\$as_me:__oline__: $ac_compile\"" >&AS_MESSAGE_LOG_FD)
+ (eval "$ac_compile" 2>conftest.err)
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ (eval echo "\"\$as_me:__oline__: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD)
+ (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ (eval echo "\"\$as_me:__oline__: output\"" >&AS_MESSAGE_LOG_FD)
+ cat conftest.out >&AS_MESSAGE_LOG_FD
+ if $GREP 'External.*some_variable' conftest.out > /dev/null; then
+ lt_cv_nm_interface="MS dumpbin"
+ fi
+ rm -f conftest*])
+])# LT_PATH_NM
+
+# Old names:
+AU_ALIAS([AM_PROG_NM], [LT_PATH_NM])
+AU_ALIAS([AC_PROG_NM], [LT_PATH_NM])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_PROG_NM], [])
+dnl AC_DEFUN([AC_PROG_NM], [])
+
+
+# LT_LIB_M
+# --------
+# check for math library
+AC_DEFUN([LT_LIB_M],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+LIBM=
+case $host in
+*-*-beos* | *-*-cygwin* | *-*-pw32* | *-*-darwin*)
+ # These system don't have libm, or don't need it
+ ;;
+*-ncr-sysv4.3*)
+ AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw")
+ AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm")
+ ;;
+*)
+ AC_CHECK_LIB(m, cos, LIBM="-lm")
+ ;;
+esac
+AC_SUBST([LIBM])
+])# LT_LIB_M
+
+# Old name:
+AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_CHECK_LIBM], [])
+
+
+# _LT_COMPILER_NO_RTTI([TAGNAME])
+# -------------------------------
+m4_defun([_LT_COMPILER_NO_RTTI],
+[m4_require([_LT_TAG_COMPILER])dnl
+
+_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=
+
+if test "$GCC" = yes; then
+ _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin'
+
+ _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions],
+ lt_cv_prog_compiler_rtti_exceptions,
+ [-fno-rtti -fno-exceptions], [],
+ [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"])
+fi
+_LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1],
+ [Compiler flag to turn off builtin functions])
+])# _LT_COMPILER_NO_RTTI
+
+
+# _LT_CMD_GLOBAL_SYMBOLS
+# ----------------------
+m4_defun([_LT_CMD_GLOBAL_SYMBOLS],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([LT_PATH_NM])dnl
+AC_REQUIRE([LT_PATH_LD])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_TAG_COMPILER])dnl
+
+# Check for command to grab the raw symbol name followed by C symbol from nm.
+AC_MSG_CHECKING([command to parse $NM output from $compiler object])
+AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe],
+[
+# These are sane defaults that work on at least a few old systems.
+# [They come from Ultrix. What could be older than Ultrix?!! ;)]
+
+# Character class describing NM global symbol codes.
+symcode='[[BCDEGRST]]'
+
+# Regexp to match symbols that can be accessed directly from C.
+sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)'
+
+# Define system-specific variables.
+case $host_os in
+aix*)
+ symcode='[[BCDT]]'
+ ;;
+cygwin* | mingw* | pw32* | cegcc*)
+ symcode='[[ABCDGISTW]]'
+ ;;
+hpux*)
+ if test "$host_cpu" = ia64; then
+ symcode='[[ABCDEGRST]]'
+ fi
+ ;;
+irix* | nonstopux*)
+ symcode='[[BCDEGRST]]'
+ ;;
+osf*)
+ symcode='[[BCDEGQRST]]'
+ ;;
+solaris*)
+ symcode='[[BDRT]]'
+ ;;
+sco3.2v5*)
+ symcode='[[DT]]'
+ ;;
+sysv4.2uw2*)
+ symcode='[[DT]]'
+ ;;
+sysv5* | sco5v6* | unixware* | OpenUNIX*)
+ symcode='[[ABDT]]'
+ ;;
+sysv4)
+ symcode='[[DFNSTU]]'
+ ;;
+esac
+
+# If we're using GNU nm, then use its standard symbol codes.
+case `$NM -V 2>&1` in
+*GNU* | *'with BFD'*)
+ symcode='[[ABCDGIRSTW]]' ;;
+esac
+
+# Transform an extracted symbol line into a proper C declaration.
+# Some systems (esp. on ia64) link data and code symbols differently,
+# so use this general approach.
+lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'"
+
+# Transform an extracted symbol line into symbol name and symbol address
+lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p'"
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([[^ ]]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \(lib[[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"lib\2\", (void *) \&\2},/p'"
+
+# Handle CRLF in mingw tool chain
+opt_cr=
+case $build_os in
+mingw*)
+ opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp
+ ;;
+esac
+
+# Try without a prefix underscore, then with it.
+for ac_symprfx in "" "_"; do
+
+ # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol.
+ symxfrm="\\1 $ac_symprfx\\2 \\2"
+
+ # Write the raw and C identifiers.
+ if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+ # Fake it for dumpbin and say T for any non-static function
+ # and D for any global variable.
+ # Also find C++ and __fastcall symbols from MSVC++,
+ # which start with @ or ?.
+ lt_cv_sys_global_symbol_pipe="$AWK ['"\
+" {last_section=section; section=\$ 3};"\
+" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\
+" \$ 0!~/External *\|/{next};"\
+" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\
+" {if(hide[section]) next};"\
+" {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\
+" {split(\$ 0, a, /\||\r/); split(a[2], s)};"\
+" s[1]~/^[@?]/{print s[1], s[1]; next};"\
+" s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\
+" ' prfx=^$ac_symprfx]"
+ else
+ lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'"
+ fi
+
+ # Check to see that the pipe works correctly.
+ pipe_works=no
+
+ rm -f conftest*
+ cat > conftest.$ac_ext <<_LT_EOF
+#ifdef __cplusplus
+extern "C" {
+#endif
+char nm_test_var;
+void nm_test_func(void);
+void nm_test_func(void){}
+#ifdef __cplusplus
+}
+#endif
+int main(){nm_test_var='a';nm_test_func();return(0);}
+_LT_EOF
+
+ if AC_TRY_EVAL(ac_compile); then
+ # Now try to grab the symbols.
+ nlist=conftest.nm
+ if AC_TRY_EVAL(NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist) && test -s "$nlist"; then
+ # Try sorting and uniquifying the output.
+ if sort "$nlist" | uniq > "$nlist"T; then
+ mv -f "$nlist"T "$nlist"
+ else
+ rm -f "$nlist"T
+ fi
+
+ # Make sure that we snagged all the symbols we need.
+ if $GREP ' nm_test_var$' "$nlist" >/dev/null; then
+ if $GREP ' nm_test_func$' "$nlist" >/dev/null; then
+ cat <<_LT_EOF > conftest.$ac_ext
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+_LT_EOF
+ # Now generate the symbol file.
+ eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext'
+
+ cat <<_LT_EOF >> conftest.$ac_ext
+
+/* The mapping between symbol names and symbols. */
+const struct {
+ const char *name;
+ void *address;
+}
+lt__PROGRAM__LTX_preloaded_symbols[[]] =
+{
+ { "@PROGRAM@", (void *) 0 },
+_LT_EOF
+ $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext
+ cat <<\_LT_EOF >> conftest.$ac_ext
+ {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+ return lt__PROGRAM__LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+_LT_EOF
+ # Now try linking the two files.
+ mv conftest.$ac_objext conftstm.$ac_objext
+ lt_save_LIBS="$LIBS"
+ lt_save_CFLAGS="$CFLAGS"
+ LIBS="conftstm.$ac_objext"
+ CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)"
+ if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext}; then
+ pipe_works=yes
+ fi
+ LIBS="$lt_save_LIBS"
+ CFLAGS="$lt_save_CFLAGS"
+ else
+ echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD
+ fi
+ else
+ echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD
+ fi
+ else
+ echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD
+ fi
+ else
+ echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD
+ cat conftest.$ac_ext >&5
+ fi
+ rm -rf conftest* conftst*
+
+ # Do not use the global_symbol_pipe unless it works.
+ if test "$pipe_works" = yes; then
+ break
+ else
+ lt_cv_sys_global_symbol_pipe=
+ fi
+done
+])
+if test -z "$lt_cv_sys_global_symbol_pipe"; then
+ lt_cv_sys_global_symbol_to_cdecl=
+fi
+if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then
+ AC_MSG_RESULT(failed)
+else
+ AC_MSG_RESULT(ok)
+fi
+
+_LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1],
+ [Take the output of nm and produce a listing of raw symbols and C names])
+_LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1],
+ [Transform the output of nm in a proper C declaration])
+_LT_DECL([global_symbol_to_c_name_address],
+ [lt_cv_sys_global_symbol_to_c_name_address], [1],
+ [Transform the output of nm in a C name address pair])
+_LT_DECL([global_symbol_to_c_name_address_lib_prefix],
+ [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1],
+ [Transform the output of nm in a C name address pair when lib prefix is needed])
+]) # _LT_CMD_GLOBAL_SYMBOLS
+
+
+# _LT_COMPILER_PIC([TAGNAME])
+# ---------------------------
+m4_defun([_LT_COMPILER_PIC],
+[m4_require([_LT_TAG_COMPILER])dnl
+_LT_TAGVAR(lt_prog_compiler_wl, $1)=
+_LT_TAGVAR(lt_prog_compiler_pic, $1)=
+_LT_TAGVAR(lt_prog_compiler_static, $1)=
+
+AC_MSG_CHECKING([for $compiler option to produce PIC])
+m4_if([$1], [CXX], [
+ # C++ specific cases for pic, static, wl, etc.
+ if test "$GXX" = yes; then
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+
+ case $host_os in
+ aix*)
+ # All AIX code is PIC.
+ if test "$host_cpu" = ia64; then
+ # AIX 5 now supports IA64 processor
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ m68k)
+ # FIXME: we need at least 68020 code to build shared libraries, but
+ # adding the `-m68020' flag to GCC prevents building anything better,
+ # like `-m68040'.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4'
+ ;;
+ esac
+ ;;
+
+ beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+ # PIC is the default for these OSes.
+ ;;
+ mingw* | cygwin* | os2* | pw32* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ # Although the cygwin gcc ignores -fPIC, still need this for old-style
+ # (--disable-auto-import) libraries
+ m4_if([$1], [GCJ], [],
+ [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+ ;;
+ darwin* | rhapsody*)
+ # PIC is the default on this platform
+ # Common symbols not allowed in MH_DYLIB files
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common'
+ ;;
+ *djgpp*)
+ # DJGPP does not support shared libraries at all
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+ ;;
+ interix[[3-9]]*)
+ # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+ # Instead, we relocate shared libraries at runtime.
+ ;;
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic
+ fi
+ ;;
+ hpux*)
+ # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+ # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag
+ # sets the default TLS model and affects inlining.
+ case $host_cpu in
+ hppa*64*)
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ esac
+ ;;
+ *qnx* | *nto*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ esac
+ else
+ case $host_os in
+ aix[[4-9]]*)
+ # All AIX code is PIC.
+ if test "$host_cpu" = ia64; then
+ # AIX 5 now supports IA64 processor
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ else
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp'
+ fi
+ ;;
+ chorus*)
+ case $cc_basename in
+ cxch68*)
+ # Green Hills C++ Compiler
+ # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a"
+ ;;
+ esac
+ ;;
+ dgux*)
+ case $cc_basename in
+ ec++*)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ ;;
+ ghcx*)
+ # Green Hills C++ Compiler
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ freebsd* | dragonfly*)
+ # FreeBSD uses GNU C++
+ ;;
+ hpux9* | hpux10* | hpux11*)
+ case $cc_basename in
+ CC*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive'
+ if test "$host_cpu" != ia64; then
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+ fi
+ ;;
+ aCC*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive'
+ case $host_cpu in
+ hppa*64*|ia64*)
+ # +Z the default
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+ ;;
+ esac
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ interix*)
+ # This is c89, which is MS Visual C++ (no shared libs)
+ # Anyone wants to do a port?
+ ;;
+ irix5* | irix6* | nonstopux*)
+ case $cc_basename in
+ CC*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ # CC pic flag -KPIC is the default.
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ linux* | k*bsd*-gnu)
+ case $cc_basename in
+ KCC*)
+ # KAI C++ Compiler
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ ecpc* )
+ # old Intel C++ for x86_64 which still supported -KPIC.
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+ ;;
+ icpc* )
+ # Intel C++, used to be incompatible with GCC.
+ # ICC 10 doesn't accept -KPIC any more.
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+ ;;
+ pgCC* | pgcpp*)
+ # Portland Group C++ compiler
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+ cxx*)
+ # Compaq C++
+ # Make sure the PIC flag is empty. It appears that all Alpha
+ # Linux and Compaq Tru64 Unix objects are PIC.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+ xlc* | xlC*)
+ # IBM XL 8.0 on PPC
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink'
+ ;;
+ *)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*)
+ # Sun C++ 5.9
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ lynxos*)
+ ;;
+ m88k*)
+ ;;
+ mvs*)
+ case $cc_basename in
+ cxx*)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ netbsd*)
+ ;;
+ *qnx* | *nto*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+ ;;
+ osf3* | osf4* | osf5*)
+ case $cc_basename in
+ KCC*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,'
+ ;;
+ RCC*)
+ # Rational C++ 2.4.1
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+ ;;
+ cxx*)
+ # Digital/Compaq C++
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ # Make sure the PIC flag is empty. It appears that all Alpha
+ # Linux and Compaq Tru64 Unix objects are PIC.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ psos*)
+ ;;
+ solaris*)
+ case $cc_basename in
+ CC*)
+ # Sun C++ 4.2, 5.x and Centerline C++
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+ ;;
+ gcx*)
+ # Green Hills C++ Compiler
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ sunos4*)
+ case $cc_basename in
+ CC*)
+ # Sun C++ 4.x
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+ lcc*)
+ # Lucid
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+ case $cc_basename in
+ CC*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+ esac
+ ;;
+ tandem*)
+ case $cc_basename in
+ NCC*)
+ # NonStop-UX NCC 3.20
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ vxworks*)
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+ ;;
+ esac
+ fi
+],
+[
+ if test "$GCC" = yes; then
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+
+ case $host_os in
+ aix*)
+ # All AIX code is PIC.
+ if test "$host_cpu" = ia64; then
+ # AIX 5 now supports IA64 processor
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ m68k)
+ # FIXME: we need at least 68020 code to build shared libraries, but
+ # adding the `-m68020' flag to GCC prevents building anything better,
+ # like `-m68040'.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4'
+ ;;
+ esac
+ ;;
+
+ beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+ # PIC is the default for these OSes.
+ ;;
+
+ mingw* | cygwin* | pw32* | os2* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ # Although the cygwin gcc ignores -fPIC, still need this for old-style
+ # (--disable-auto-import) libraries
+ m4_if([$1], [GCJ], [],
+ [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+ ;;
+
+ darwin* | rhapsody*)
+ # PIC is the default on this platform
+ # Common symbols not allowed in MH_DYLIB files
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common'
+ ;;
+
+ hpux*)
+ # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+ # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag
+ # sets the default TLS model and affects inlining.
+ case $host_cpu in
+ hppa*64*)
+ # +Z the default
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ esac
+ ;;
+
+ interix[[3-9]]*)
+ # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+ # Instead, we relocate shared libraries at runtime.
+ ;;
+
+ msdosdjgpp*)
+ # Just because we use GCC doesn't mean we suddenly get shared libraries
+ # on systems that don't support them.
+ _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+ enable_shared=no
+ ;;
+
+ *nto* | *qnx*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic
+ fi
+ ;;
+
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ esac
+ else
+ # PORTME Check for flag to pass linker flags through the system compiler.
+ case $host_os in
+ aix*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ if test "$host_cpu" = ia64; then
+ # AIX 5 now supports IA64 processor
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ else
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp'
+ fi
+ ;;
+
+ mingw* | cygwin* | pw32* | os2* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ m4_if([$1], [GCJ], [],
+ [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+ ;;
+
+ hpux9* | hpux10* | hpux11*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+ # not for PA HP-UX.
+ case $host_cpu in
+ hppa*64*|ia64*)
+ # +Z the default
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+ ;;
+ esac
+ # Is there a better lt_prog_compiler_static that works with the bundled CC?
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive'
+ ;;
+
+ irix5* | irix6* | nonstopux*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ # PIC (with -KPIC) is the default.
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+
+ linux* | k*bsd*-gnu)
+ case $cc_basename in
+ # old Intel for x86_64 which still supported -KPIC.
+ ecc*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+ ;;
+ # icc used to be incompatible with GCC.
+ # ICC 10 doesn't accept -KPIC any more.
+ icc* | ifort*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+ ;;
+ # Lahey Fortran 8.1.
+ lf95*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='--static'
+ ;;
+ pgcc* | pgf77* | pgf90* | pgf95*)
+ # Portland Group compilers (*not* the Pentium gcc compiler,
+ # which looks to be a dead project)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+ ccc*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ # All Alpha code is PIC.
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+ xl*)
+ # IBM XL C 8.0/Fortran 10.1 on PPC
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink'
+ ;;
+ *)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*)
+ # Sun C 5.9
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ ;;
+ *Sun\ F*)
+ # Sun Fortran 8.3 passes all unrecognized flags to the linker
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)=''
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+
+ newsos6)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+
+ *nto* | *qnx*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+ ;;
+
+ osf3* | osf4* | osf5*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ # All OSF/1 code is PIC.
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+
+ rdos*)
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+
+ solaris*)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ case $cc_basename in
+ f77* | f90* | f95*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';;
+ esac
+ ;;
+
+ sunos4*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+
+ sysv4 | sysv4.2uw2* | sysv4.3*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec ;then
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ fi
+ ;;
+
+ sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+
+ unicos*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+ ;;
+
+ uts4*)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+
+ *)
+ _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+ ;;
+ esac
+ fi
+])
+case $host_os in
+ # For platforms which do not support PIC, -DPIC is meaningless:
+ *djgpp*)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])"
+ ;;
+esac
+AC_MSG_RESULT([$_LT_TAGVAR(lt_prog_compiler_pic, $1)])
+_LT_TAGDECL([wl], [lt_prog_compiler_wl], [1],
+ [How to pass a linker flag through the compiler])
+
+#
+# Check to make sure the PIC flag actually works.
+#
+if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then
+ _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works],
+ [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)],
+ [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [],
+ [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in
+ "" | " "*) ;;
+ *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;;
+ esac],
+ [_LT_TAGVAR(lt_prog_compiler_pic, $1)=
+ _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no])
+fi
+_LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1],
+ [Additional compiler flags for building library objects])
+
+#
+# Check to make sure the static flag actually works.
+#
+wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\"
+_LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works],
+ _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1),
+ $lt_tmp_static_flag,
+ [],
+ [_LT_TAGVAR(lt_prog_compiler_static, $1)=])
+_LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1],
+ [Compiler flag to prevent dynamic linking])
+])# _LT_COMPILER_PIC
+
+
+# _LT_LINKER_SHLIBS([TAGNAME])
+# ----------------------------
+# See if the linker supports building shared libraries.
+m4_defun([_LT_LINKER_SHLIBS],
+[AC_REQUIRE([LT_PATH_LD])dnl
+AC_REQUIRE([LT_PATH_NM])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl
+m4_require([_LT_TAG_COMPILER])dnl
+AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries])
+m4_if([$1], [CXX], [
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+ case $host_os in
+ aix[[4-9]]*)
+ # If we're using GNU nm, then we don't want the "-C" option.
+ # -C means demangle to AIX nm, but means don't demangle with GNU nm
+ if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+ else
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+ fi
+ ;;
+ pw32*)
+ _LT_TAGVAR(export_symbols_cmds, $1)="$ltdll_cmds"
+ ;;
+ cygwin* | mingw* | cegcc*)
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;/^.*[[ ]]__nm__/s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols'
+ ;;
+ *)
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+ ;;
+ esac
+ _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*']
+], [
+ runpath_var=
+ _LT_TAGVAR(allow_undefined_flag, $1)=
+ _LT_TAGVAR(always_export_symbols, $1)=no
+ _LT_TAGVAR(archive_cmds, $1)=
+ _LT_TAGVAR(archive_expsym_cmds, $1)=
+ _LT_TAGVAR(compiler_needs_object, $1)=no
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)=
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+ _LT_TAGVAR(hardcode_automatic, $1)=no
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+ _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)=
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=
+ _LT_TAGVAR(hardcode_minus_L, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+ _LT_TAGVAR(inherit_rpath, $1)=no
+ _LT_TAGVAR(link_all_deplibs, $1)=unknown
+ _LT_TAGVAR(module_cmds, $1)=
+ _LT_TAGVAR(module_expsym_cmds, $1)=
+ _LT_TAGVAR(old_archive_from_new_cmds, $1)=
+ _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)=
+ _LT_TAGVAR(thread_safe_flag_spec, $1)=
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=
+ # include_expsyms should be a list of space-separated symbols to be *always*
+ # included in the symbol list
+ _LT_TAGVAR(include_expsyms, $1)=
+ # exclude_expsyms can be an extended regexp of symbols to exclude
+ # it will be wrapped by ` (' and `)$', so one must not match beginning or
+ # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc',
+ # as well as any symbol that contains `d'.
+ _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*']
+ # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
+ # platforms (ab)use it in PIC code, but their linkers get confused if
+ # the symbol is explicitly referenced. Since portable code cannot
+ # rely on this symbol name, it's probably fine to never include it in
+ # preloaded symbol tables.
+ # Exclude shared library initialization/finalization symbols.
+dnl Note also adjust exclude_expsyms for C++ above.
+ extract_expsyms_cmds=
+
+ case $host_os in
+ cygwin* | mingw* | pw32* | cegcc*)
+ # FIXME: the MSVC++ port hasn't been tested in a loooong time
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ if test "$GCC" != yes; then
+ with_gnu_ld=no
+ fi
+ ;;
+ interix*)
+ # we just hope/assume this is gcc and not c89 (= MSVC++)
+ with_gnu_ld=yes
+ ;;
+ openbsd*)
+ with_gnu_ld=no
+ ;;
+ esac
+
+ _LT_TAGVAR(ld_shlibs, $1)=yes
+ if test "$with_gnu_ld" = yes; then
+ # If archive_cmds runs LD, not CC, wlarc should be empty
+ wlarc='${wl}'
+
+ # Set some defaults for GNU ld with shared library support. These
+ # are reset later if shared libraries are not supported. Putting them
+ # here allows them to be overridden if necessary.
+ runpath_var=LD_RUN_PATH
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+ # ancient GNU ld didn't support --whole-archive et. al.
+ if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then
+ _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+ else
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=
+ fi
+ supports_anon_versioning=no
+ case `$LD -v 2>&1` in
+ *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11
+ *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
+ *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
+ *\ 2.11.*) ;; # other 2.11 versions
+ *) supports_anon_versioning=yes ;;
+ esac
+
+ # See if GNU ld supports shared libraries.
+ case $host_os in
+ aix[[3-9]]*)
+ # On AIX/PPC, the GNU linker is very broken
+ if test "$host_cpu" != ia64; then
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the GNU linker, at least up to release 2.9.1, is reported
+*** to be unable to reliably create shared libraries on AIX.
+*** Therefore, libtool is disabling shared libraries support. If you
+*** really care for shared libraries, you may want to modify your PATH
+*** so that a non-GNU linker is found, and then restart.
+
+_LT_EOF
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)=''
+ ;;
+ m68k)
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ ;;
+ esac
+ ;;
+
+ beos*)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+ # support --undefined. This deserves some investigation. FIXME
+ _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ cygwin* | mingw* | pw32* | cegcc*)
+ # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless,
+ # as there is no search path for DLLs.
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ _LT_TAGVAR(always_export_symbols, $1)=no
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols'
+
+ if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ # If the export-symbols file already is a .def file (1st line
+ # is EXPORTS), use it as is; otherwise, prepend...
+ _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+ cp $export_symbols $output_objdir/$soname.def;
+ else
+ echo EXPORTS > $output_objdir/$soname.def;
+ cat $export_symbols >> $output_objdir/$soname.def;
+ fi~
+ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ interix[[3-9]]*)
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+ # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+ # Instead, shared libraries are loaded at an image base (0x10000000 by
+ # default) and relocated if they conflict, which is a slow very memory
+ # consuming and fragmenting process. To avoid this, we pick a random,
+ # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+ # time. Moving up from 0x10000000 also allows more sbrk(2) space.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ ;;
+
+ gnu* | linux* | tpf* | k*bsd*-gnu)
+ tmp_diet=no
+ if test "$host_os" = linux-dietlibc; then
+ case $cc_basename in
+ diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn)
+ esac
+ fi
+ if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \
+ && test "$tmp_diet" = no
+ then
+ tmp_addflag=
+ tmp_sharedflag='-shared'
+ case $cc_basename,$host_cpu in
+ pgcc*) # Portland Group C compiler
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+ tmp_addflag=' $pic_flag'
+ ;;
+ pgf77* | pgf90* | pgf95*) # Portland Group f77 and f90 compilers
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+ tmp_addflag=' $pic_flag -Mnomain' ;;
+ ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64
+ tmp_addflag=' -i_dynamic' ;;
+ efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64
+ tmp_addflag=' -i_dynamic -nofor_main' ;;
+ ifc* | ifort*) # Intel Fortran compiler
+ tmp_addflag=' -nofor_main' ;;
+ lf95*) # Lahey Fortran 8.1
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=
+ tmp_sharedflag='--shared' ;;
+ xl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below)
+ tmp_sharedflag='-qmkshrobj'
+ tmp_addflag= ;;
+ esac
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*) # Sun C 5.9
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+ _LT_TAGVAR(compiler_needs_object, $1)=yes
+ tmp_sharedflag='-G' ;;
+ *Sun\ F*) # Sun Fortran 8.3
+ tmp_sharedflag='-G' ;;
+ esac
+ _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+
+ if test "x$supports_anon_versioning" = xyes; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+ fi
+
+ case $cc_basename in
+ xlf*)
+ # IBM XL Fortran 10.1 on PPC cannot create shared libs itself
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+ _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='-rpath $libdir'
+ _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $compiler_flags -soname $soname -o $lib'
+ if test "x$supports_anon_versioning" = xyes; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $LD -shared $libobjs $deplibs $compiler_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib'
+ fi
+ ;;
+ esac
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
+ wlarc=
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ fi
+ ;;
+
+ solaris*)
+ if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: The releases 2.8.* of the GNU linker cannot reliably
+*** create shared libraries on Solaris systems. Therefore, libtool
+*** is disabling shared libraries support. We urge you to upgrade GNU
+*** binutils to release 2.9.1 or newer. Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+ elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*)
+ case `$LD -v 2>&1` in
+ *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*)
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not
+*** reliably create shared libraries on SCO systems. Therefore, libtool
+*** is disabling shared libraries support. We urge you to upgrade GNU
+*** binutils to release 2.16.91.0.3 or newer. Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+ ;;
+ *)
+ # For security reasons, it is highly recommended that you always
+ # use absolute paths for naming shared libraries, and exclude the
+ # DT_RUNPATH tag from executables and libraries. But doing so
+ # requires that you compile everything twice, which is a pain.
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ esac
+ ;;
+
+ sunos4*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ wlarc=
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ *)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ esac
+
+ if test "$_LT_TAGVAR(ld_shlibs, $1)" = no; then
+ runpath_var=
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)=
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=
+ fi
+ else
+ # PORTME fill in a description of your system's linker (not GNU ld)
+ case $host_os in
+ aix3*)
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ _LT_TAGVAR(always_export_symbols, $1)=yes
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'
+ # Note: this linker hardcodes the directories in LIBPATH if there
+ # are no directories specified by -L.
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then
+ # Neither direct hardcoding nor static linking is supported with a
+ # broken collect2.
+ _LT_TAGVAR(hardcode_direct, $1)=unsupported
+ fi
+ ;;
+
+ aix[[4-9]]*)
+ if test "$host_cpu" = ia64; then
+ # On IA64, the linker does run time linking by default, so we don't
+ # have to do anything special.
+ aix_use_runtimelinking=no
+ exp_sym_flag='-Bexport'
+ no_entry_flag=""
+ else
+ # If we're using GNU nm, then we don't want the "-C" option.
+ # -C means demangle to AIX nm, but means don't demangle with GNU nm
+ if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+ else
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+ fi
+ aix_use_runtimelinking=no
+
+ # Test if we are trying to use run time linking or normal
+ # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+ # need to do runtime linking.
+ case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*)
+ for ld_flag in $LDFLAGS; do
+ if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then
+ aix_use_runtimelinking=yes
+ break
+ fi
+ done
+ ;;
+ esac
+
+ exp_sym_flag='-bexport'
+ no_entry_flag='-bnoentry'
+ fi
+
+ # When large executables or shared objects are built, AIX ld can
+ # have problems creating the table of contents. If linking a library
+ # or program results in "error TOC overflow" add -mminimal-toc to
+ # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not
+ # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+ _LT_TAGVAR(archive_cmds, $1)=''
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ _LT_TAGVAR(file_list_spec, $1)='${wl}-f,'
+
+ if test "$GCC" = yes; then
+ case $host_os in aix4.[[012]]|aix4.[[012]].*)
+ # We only want to do this on AIX 4.2 and lower, the check
+ # below for broken collect2 doesn't work under 4.3+
+ collect2name=`${CC} -print-prog-name=collect2`
+ if test -f "$collect2name" &&
+ strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+ then
+ # We have reworked collect2
+ :
+ else
+ # We have old collect2
+ _LT_TAGVAR(hardcode_direct, $1)=unsupported
+ # It fails to find uninstalled libraries when the uninstalled
+ # path is not listed in the libpath. Setting hardcode_minus_L
+ # to unsupported forces relinking
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=
+ fi
+ ;;
+ esac
+ shared_flag='-shared'
+ if test "$aix_use_runtimelinking" = yes; then
+ shared_flag="$shared_flag "'${wl}-G'
+ fi
+ else
+ # not using gcc
+ if test "$host_cpu" = ia64; then
+ # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+ # chokes on -Wl,-G. The following line is correct:
+ shared_flag='-G'
+ else
+ if test "$aix_use_runtimelinking" = yes; then
+ shared_flag='${wl}-G'
+ else
+ shared_flag='${wl}-bM:SRE'
+ fi
+ fi
+ fi
+
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall'
+ # It seems that -bexpall does not export symbols beginning with
+ # underscore (_), so it is better to generate a list of symbols to export.
+ _LT_TAGVAR(always_export_symbols, $1)=yes
+ if test "$aix_use_runtimelinking" = yes; then
+ # Warning - without using the other runtime loading flags (-brtl),
+ # -berok will link without error, but may produce a broken library.
+ _LT_TAGVAR(allow_undefined_flag, $1)='-berok'
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ _LT_SYS_MODULE_PATH_AIX
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then $ECHO "X${wl}${allow_undefined_flag}" | $Xsed; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+ else
+ if test "$host_cpu" = ia64; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib'
+ _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs"
+ _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols"
+ else
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ _LT_SYS_MODULE_PATH_AIX
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+ # Warning - without using the other run time loading flags,
+ # -berok will link without error, but may produce a broken library.
+ _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok'
+ _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok'
+ # Exported symbols can be pulled into shared objects from archives
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience'
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+ # This is similar to how AIX traditionally builds its shared libraries.
+ _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+ fi
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)=''
+ ;;
+ m68k)
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ ;;
+ esac
+ ;;
+
+ bsdi[[45]]*)
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic
+ ;;
+
+ cygwin* | mingw* | pw32* | cegcc*)
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ # hardcode_libdir_flag_spec is actually meaningless, as there is
+ # no search path for DLLs.
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' '
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ # Tell ltmain to make .lib files, not .a files.
+ libext=lib
+ # Tell ltmain to make .dll files, not .so files.
+ shrext_cmds=".dll"
+ # FIXME: Setting linknames here is a bad hack.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `$ECHO "X$deplibs" | $Xsed -e '\''s/ -lc$//'\''` -link -dll~linknames='
+ # The linker will automatically build a .lib file if we build a DLL.
+ _LT_TAGVAR(old_archive_from_new_cmds, $1)='true'
+ # FIXME: Should let the user specify the lib program.
+ _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs'
+ _LT_TAGVAR(fix_srcfile_path, $1)='`cygpath -w "$srcfile"`'
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+ ;;
+
+ darwin* | rhapsody*)
+ _LT_DARWIN_LINKER_FEATURES($1)
+ ;;
+
+ dgux*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ freebsd1*)
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
+ # support. Future versions do this automatically, but an explicit c++rt0.o
+ # does not break anything, and helps significantly (at the cost of a little
+ # extra space).
+ freebsd2.2*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ # Unfortunately, older versions of FreeBSD 2 do not have this feature.
+ freebsd2*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
+ freebsd* | dragonfly*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ hpux9*)
+ if test "$GCC" = yes; then
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+ fi
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+ ;;
+
+ hpux10*)
+ if test "$GCC" = yes -a "$with_gnu_ld" = no; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+ fi
+ if test "$with_gnu_ld" = no; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='+b $libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ fi
+ ;;
+
+ hpux11*)
+ if test "$GCC" = yes -a "$with_gnu_ld" = no; then
+ case $host_cpu in
+ hppa*64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ ia64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ else
+ case $host_cpu in
+ hppa*64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ ia64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ fi
+ if test "$with_gnu_ld" = no; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ case $host_cpu in
+ hppa*64*|ia64*)
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+ *)
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ ;;
+ esac
+ fi
+ ;;
+
+ irix5* | irix6* | nonstopux*)
+ if test "$GCC" = yes; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ # Try to use the -exported_symbol ld option, if it does not
+ # work, assume that -exports_file does not work either and
+ # implicitly export all symbols.
+ save_LDFLAGS="$LDFLAGS"
+ LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null"
+ AC_LINK_IFELSE(
+ [AC_LANG_SOURCE(
+ [AC_LANG_CASE([C], [[int foo (void) { return 0; }]],
+ [C++], [[int foo (void) { return 0; }]],
+ [Fortran 77], [[
+ subroutine foo
+ end]],
+ [Fortran], [[
+ subroutine foo
+ end]])])], [
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib'
+ ])
+ LDFLAGS="$save_LDFLAGS"
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib'
+ fi
+ _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(inherit_rpath, $1)=yes
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ ;;
+
+ netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF
+ fi
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ newsos6)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ *nto* | *qnx*)
+ ;;
+
+ openbsd*)
+ if test -f /usr/libexec/ld.so; then
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+ else
+ case $host_os in
+ openbsd[[01]].* | openbsd2.[[0-7]] | openbsd2.[[0-7]].*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+ ;;
+ esac
+ fi
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ os2*)
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$ECHO DATA >> $output_objdir/$libname.def~$ECHO " SINGLE NONSHARED" >> $output_objdir/$libname.def~$ECHO EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def'
+ _LT_TAGVAR(old_archive_from_new_cmds, $1)='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def'
+ ;;
+
+ osf3*)
+ if test "$GCC" = yes; then
+ _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ else
+ _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+ fi
+ _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ ;;
+
+ osf4* | osf5*) # as osf3* with the addition of -msym flag
+ if test "$GCC" = yes; then
+ _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ else
+ _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~
+ $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp'
+
+ # Both c and cxx compiler support -rpath directly
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+ fi
+ _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ ;;
+
+ solaris*)
+ _LT_TAGVAR(no_undefined_flag, $1)=' -z defs'
+ if test "$GCC" = yes; then
+ wlarc='${wl}'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -shared ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ else
+ case `$CC -V 2>&1` in
+ *"Compilers 5.0"*)
+ wlarc=''
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp'
+ ;;
+ *)
+ wlarc='${wl}'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ ;;
+ esac
+ fi
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ case $host_os in
+ solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+ *)
+ # The compiler driver will combine and reorder linker options,
+ # but understands `-z linker_flag'. GCC discards it without `$wl',
+ # but is careful enough not to reorder.
+ # Supported since Solaris 2.6 (maybe 2.5.1?)
+ if test "$GCC" = yes; then
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
+ else
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract'
+ fi
+ ;;
+ esac
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ ;;
+
+ sunos4*)
+ if test "x$host_vendor" = xsequent; then
+ # Use $CC to link under sequent, because it throws in some extra .o
+ # files that make .init and .fini sections work.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
+ fi
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ sysv4)
+ case $host_vendor in
+ sni)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true???
+ ;;
+ siemens)
+ ## LD is ld it makes a PLAMLIB
+ ## CC just makes a GrossModule.
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs'
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ ;;
+ motorola)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie
+ ;;
+ esac
+ runpath_var='LD_RUN_PATH'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ sysv4.3*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ runpath_var=LD_RUN_PATH
+ hardcode_runpath_var=yes
+ _LT_TAGVAR(ld_shlibs, $1)=yes
+ fi
+ ;;
+
+ sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*)
+ _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ runpath_var='LD_RUN_PATH'
+
+ if test "$GCC" = yes; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ fi
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6*)
+ # Note: We can NOT use -z defs as we might desire, because we do not
+ # link with -lc, and that would cause any symbols used from libc to
+ # always be unresolved, which means just about no library would
+ # ever link correctly. If we're not using GNU ld we use -z text
+ # though, which does catch some bad symbols but isn't as heavy-handed
+ # as -z defs.
+ _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
+ _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs'
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport'
+ runpath_var='LD_RUN_PATH'
+
+ if test "$GCC" = yes; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ fi
+ ;;
+
+ uts4*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ *)
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+
+ if test x$host_vendor = xsni; then
+ case $host in
+ sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Blargedynsym'
+ ;;
+ esac
+ fi
+ fi
+])
+AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)])
+test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no
+
+_LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld
+
+_LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl
+_LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl
+_LT_DECL([], [extract_expsyms_cmds], [2],
+ [The commands to extract the exported symbol list from a shared archive])
+
+#
+# Do we need to explicitly link libc?
+#
+case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in
+x|xyes)
+ # Assume -lc should be added
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+
+ if test "$enable_shared" = yes && test "$GCC" = yes; then
+ case $_LT_TAGVAR(archive_cmds, $1) in
+ *'~'*)
+ # FIXME: we may have to deal with multi-command sequences.
+ ;;
+ '$CC '*)
+ # Test whether the compiler implicitly links with -lc since on some
+ # systems, -lgcc has to come before -lc. If gcc already passes -lc
+ # to ld, don't add -lc before -lgcc.
+ AC_MSG_CHECKING([whether -lc should be explicitly linked in])
+ $RM conftest*
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ if AC_TRY_EVAL(ac_compile) 2>conftest.err; then
+ soname=conftest
+ lib=conftest
+ libobjs=conftest.$ac_objext
+ deplibs=
+ wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1)
+ pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1)
+ compiler_flags=-v
+ linker_flags=-v
+ verstring=
+ output_objdir=.
+ libname=conftest
+ lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1)
+ _LT_TAGVAR(allow_undefined_flag, $1)=
+ if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1)
+ then
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ else
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+ fi
+ _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag
+ else
+ cat conftest.err 1>&5
+ fi
+ $RM conftest*
+ AC_MSG_RESULT([$_LT_TAGVAR(archive_cmds_need_lc, $1)])
+ ;;
+ esac
+ fi
+ ;;
+esac
+
+_LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0],
+ [Whether or not to add -lc for building shared libraries])
+_LT_TAGDECL([allow_libtool_libs_with_static_runtimes],
+ [enable_shared_with_static_runtimes], [0],
+ [Whether or not to disallow shared libs when runtime libs are static])
+_LT_TAGDECL([], [export_dynamic_flag_spec], [1],
+ [Compiler flag to allow reflexive dlopens])
+_LT_TAGDECL([], [whole_archive_flag_spec], [1],
+ [Compiler flag to generate shared objects directly from archives])
+_LT_TAGDECL([], [compiler_needs_object], [1],
+ [Whether the compiler copes with passing no objects directly])
+_LT_TAGDECL([], [old_archive_from_new_cmds], [2],
+ [Create an old-style archive from a shared archive])
+_LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2],
+ [Create a temporary old-style archive to link instead of a shared archive])
+_LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive])
+_LT_TAGDECL([], [archive_expsym_cmds], [2])
+_LT_TAGDECL([], [module_cmds], [2],
+ [Commands used to build a loadable module if different from building
+ a shared archive.])
+_LT_TAGDECL([], [module_expsym_cmds], [2])
+_LT_TAGDECL([], [with_gnu_ld], [1],
+ [Whether we are building with GNU ld or not])
+_LT_TAGDECL([], [allow_undefined_flag], [1],
+ [Flag that allows shared libraries with undefined symbols to be built])
+_LT_TAGDECL([], [no_undefined_flag], [1],
+ [Flag that enforces no undefined symbols])
+_LT_TAGDECL([], [hardcode_libdir_flag_spec], [1],
+ [Flag to hardcode $libdir into a binary during linking.
+ This must work even if $libdir does not exist])
+_LT_TAGDECL([], [hardcode_libdir_flag_spec_ld], [1],
+ [[If ld is used when linking, flag to hardcode $libdir into a binary
+ during linking. This must work even if $libdir does not exist]])
+_LT_TAGDECL([], [hardcode_libdir_separator], [1],
+ [Whether we need a single "-rpath" flag with a separated argument])
+_LT_TAGDECL([], [hardcode_direct], [0],
+ [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes
+ DIR into the resulting binary])
+_LT_TAGDECL([], [hardcode_direct_absolute], [0],
+ [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes
+ DIR into the resulting binary and the resulting library dependency is
+ "absolute", i.e impossible to change by setting ${shlibpath_var} if the
+ library is relocated])
+_LT_TAGDECL([], [hardcode_minus_L], [0],
+ [Set to "yes" if using the -LDIR flag during linking hardcodes DIR
+ into the resulting binary])
+_LT_TAGDECL([], [hardcode_shlibpath_var], [0],
+ [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
+ into the resulting binary])
+_LT_TAGDECL([], [hardcode_automatic], [0],
+ [Set to "yes" if building a shared library automatically hardcodes DIR
+ into the library and all subsequent libraries and executables linked
+ against it])
+_LT_TAGDECL([], [inherit_rpath], [0],
+ [Set to yes if linker adds runtime paths of dependent libraries
+ to runtime path list])
+_LT_TAGDECL([], [link_all_deplibs], [0],
+ [Whether libtool must link a program against all its dependency libraries])
+_LT_TAGDECL([], [fix_srcfile_path], [1],
+ [Fix the shell variable $srcfile for the compiler])
+_LT_TAGDECL([], [always_export_symbols], [0],
+ [Set to "yes" if exported symbols are required])
+_LT_TAGDECL([], [export_symbols_cmds], [2],
+ [The commands to list exported symbols])
+_LT_TAGDECL([], [exclude_expsyms], [1],
+ [Symbols that should not be listed in the preloaded symbols])
+_LT_TAGDECL([], [include_expsyms], [1],
+ [Symbols that must always be exported])
+_LT_TAGDECL([], [prelink_cmds], [2],
+ [Commands necessary for linking programs (against libraries) with templates])
+_LT_TAGDECL([], [file_list_spec], [1],
+ [Specify filename containing input files])
+dnl FIXME: Not yet implemented
+dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1],
+dnl [Compiler flag to generate thread safe objects])
+])# _LT_LINKER_SHLIBS
+
+
+# _LT_LANG_C_CONFIG([TAG])
+# ------------------------
+# Ensure that the configuration variables for a C compiler are suitably
+# defined. These variables are subsequently used by _LT_CONFIG to write
+# the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_C_CONFIG],
+[m4_require([_LT_DECL_EGREP])dnl
+lt_save_CC="$CC"
+AC_LANG_PUSH(C)
+
+# Source file extension for C test sources.
+ac_ext=c
+
+# Object file extension for compiled C test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="int some_variable = 0;"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='int main(){return(0);}'
+
+_LT_TAG_COMPILER
+# Save the default compiler, since it gets overwritten when the other
+# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP.
+compiler_DEFAULT=$CC
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+## CAVEAT EMPTOR:
+## There is no encapsulation within the following macros, do not change
+## the running order or otherwise move them around unless you know exactly
+## what you are doing...
+if test -n "$compiler"; then
+ _LT_COMPILER_NO_RTTI($1)
+ _LT_COMPILER_PIC($1)
+ _LT_COMPILER_C_O($1)
+ _LT_COMPILER_FILE_LOCKS($1)
+ _LT_LINKER_SHLIBS($1)
+ _LT_SYS_DYNAMIC_LINKER($1)
+ _LT_LINKER_HARDCODE_LIBPATH($1)
+ LT_SYS_DLOPEN_SELF
+ _LT_CMD_STRIPLIB
+
+ # Report which library types will actually be built
+ AC_MSG_CHECKING([if libtool supports shared libraries])
+ AC_MSG_RESULT([$can_build_shared])
+
+ AC_MSG_CHECKING([whether to build shared libraries])
+ test "$can_build_shared" = "no" && enable_shared=no
+
+ # On AIX, shared libraries and static libraries use the same namespace, and
+ # are all built from PIC.
+ case $host_os in
+ aix3*)
+ test "$enable_shared" = yes && enable_static=no
+ if test -n "$RANLIB"; then
+ archive_cmds="$archive_cmds~\$RANLIB \$lib"
+ postinstall_cmds='$RANLIB $lib'
+ fi
+ ;;
+
+ aix[[4-9]]*)
+ if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+ test "$enable_shared" = yes && enable_static=no
+ fi
+ ;;
+ esac
+ AC_MSG_RESULT([$enable_shared])
+
+ AC_MSG_CHECKING([whether to build static libraries])
+ # Make sure either enable_shared or enable_static is yes.
+ test "$enable_shared" = yes || enable_static=yes
+ AC_MSG_RESULT([$enable_static])
+
+ _LT_CONFIG($1)
+fi
+AC_LANG_POP
+CC="$lt_save_CC"
+])# _LT_LANG_C_CONFIG
+
+
+# _LT_PROG_CXX
+# ------------
+# Since AC_PROG_CXX is broken, in that it returns g++ if there is no c++
+# compiler, we have our own version here.
+m4_defun([_LT_PROG_CXX],
+[
+pushdef([AC_MSG_ERROR], [_lt_caught_CXX_error=yes])
+AC_PROG_CXX
+if test -n "$CXX" && ( test "X$CXX" != "Xno" &&
+ ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) ||
+ (test "X$CXX" != "Xg++"))) ; then
+ AC_PROG_CXXCPP
+else
+ _lt_caught_CXX_error=yes
+fi
+popdef([AC_MSG_ERROR])
+])# _LT_PROG_CXX
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([_LT_PROG_CXX], [])
+
+
+# _LT_LANG_CXX_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for a C++ compiler are suitably
+# defined. These variables are subsequently used by _LT_CONFIG to write
+# the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_CXX_CONFIG],
+[AC_REQUIRE([_LT_PROG_CXX])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_EGREP])dnl
+
+AC_LANG_PUSH(C++)
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_TAGVAR(allow_undefined_flag, $1)=
+_LT_TAGVAR(always_export_symbols, $1)=no
+_LT_TAGVAR(archive_expsym_cmds, $1)=
+_LT_TAGVAR(compiler_needs_object, $1)=no
+_LT_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_TAGVAR(hardcode_direct, $1)=no
+_LT_TAGVAR(hardcode_direct_absolute, $1)=no
+_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)=
+_LT_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_TAGVAR(hardcode_minus_L, $1)=no
+_LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+_LT_TAGVAR(hardcode_automatic, $1)=no
+_LT_TAGVAR(inherit_rpath, $1)=no
+_LT_TAGVAR(module_cmds, $1)=
+_LT_TAGVAR(module_expsym_cmds, $1)=
+_LT_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(no_undefined_flag, $1)=
+_LT_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Source file extension for C++ test sources.
+ac_ext=cpp
+
+# Object file extension for compiled C++ test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# No sense in running all these tests if we already determined that
+# the CXX compiler isn't working. Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test "$_lt_caught_CXX_error" != yes; then
+ # Code to be used in simple compile tests
+ lt_simple_compile_test_code="int some_variable = 0;"
+
+ # Code to be used in simple link tests
+ lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }'
+
+ # ltmain only uses $CC for tagged configurations so make sure $CC is set.
+ _LT_TAG_COMPILER
+
+ # save warnings/boilerplate of simple test code
+ _LT_COMPILER_BOILERPLATE
+ _LT_LINKER_BOILERPLATE
+
+ # Allow CC to be a program name with arguments.
+ lt_save_CC=$CC
+ lt_save_LD=$LD
+ lt_save_GCC=$GCC
+ GCC=$GXX
+ lt_save_with_gnu_ld=$with_gnu_ld
+ lt_save_path_LD=$lt_cv_path_LD
+ if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then
+ lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx
+ else
+ $as_unset lt_cv_prog_gnu_ld
+ fi
+ if test -n "${lt_cv_path_LDCXX+set}"; then
+ lt_cv_path_LD=$lt_cv_path_LDCXX
+ else
+ $as_unset lt_cv_path_LD
+ fi
+ test -z "${LDCXX+set}" || LD=$LDCXX
+ CC=${CXX-"c++"}
+ compiler=$CC
+ _LT_TAGVAR(compiler, $1)=$CC
+ _LT_CC_BASENAME([$compiler])
+
+ if test -n "$compiler"; then
+ # We don't want -fno-exception when compiling C++ code, so set the
+ # no_builtin_flag separately
+ if test "$GXX" = yes; then
+ _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin'
+ else
+ _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=
+ fi
+
+ if test "$GXX" = yes; then
+ # Set up default GNU C++ configuration
+
+ LT_PATH_LD
+
+ # Check if GNU C++ uses GNU ld as the underlying linker, since the
+ # archiving commands below assume that GNU ld is being used.
+ if test "$with_gnu_ld" = yes; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+
+ # If archive_cmds runs LD, not CC, wlarc should be empty
+ # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to
+ # investigate it a little bit more. (MM)
+ wlarc='${wl}'
+
+ # ancient GNU ld didn't support --whole-archive et. al.
+ if eval "`$CC -print-prog-name=ld` --help 2>&1" |
+ $GREP 'no-whole-archive' > /dev/null; then
+ _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+ else
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=
+ fi
+ else
+ with_gnu_ld=no
+ wlarc=
+
+ # A generic and very simple default shared library creation
+ # command for GNU C++ for the case where it uses the native
+ # linker, instead of GNU ld. If possible, this setting should
+ # overridden to take advantage of the native linker features on
+ # the platform it is being used on.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib'
+ fi
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"'
+
+ else
+ GXX=no
+ with_gnu_ld=no
+ wlarc=
+ fi
+
+ # PORTME: fill in a description of your system's C++ link characteristics
+ AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries])
+ _LT_TAGVAR(ld_shlibs, $1)=yes
+ case $host_os in
+ aix3*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ aix[[4-9]]*)
+ if test "$host_cpu" = ia64; then
+ # On IA64, the linker does run time linking by default, so we don't
+ # have to do anything special.
+ aix_use_runtimelinking=no
+ exp_sym_flag='-Bexport'
+ no_entry_flag=""
+ else
+ aix_use_runtimelinking=no
+
+ # Test if we are trying to use run time linking or normal
+ # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+ # need to do runtime linking.
+ case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*)
+ for ld_flag in $LDFLAGS; do
+ case $ld_flag in
+ *-brtl*)
+ aix_use_runtimelinking=yes
+ break
+ ;;
+ esac
+ done
+ ;;
+ esac
+
+ exp_sym_flag='-bexport'
+ no_entry_flag='-bnoentry'
+ fi
+
+ # When large executables or shared objects are built, AIX ld can
+ # have problems creating the table of contents. If linking a library
+ # or program results in "error TOC overflow" add -mminimal-toc to
+ # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not
+ # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+ _LT_TAGVAR(archive_cmds, $1)=''
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ _LT_TAGVAR(file_list_spec, $1)='${wl}-f,'
+
+ if test "$GXX" = yes; then
+ case $host_os in aix4.[[012]]|aix4.[[012]].*)
+ # We only want to do this on AIX 4.2 and lower, the check
+ # below for broken collect2 doesn't work under 4.3+
+ collect2name=`${CC} -print-prog-name=collect2`
+ if test -f "$collect2name" &&
+ strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+ then
+ # We have reworked collect2
+ :
+ else
+ # We have old collect2
+ _LT_TAGVAR(hardcode_direct, $1)=unsupported
+ # It fails to find uninstalled libraries when the uninstalled
+ # path is not listed in the libpath. Setting hardcode_minus_L
+ # to unsupported forces relinking
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=
+ fi
+ esac
+ shared_flag='-shared'
+ if test "$aix_use_runtimelinking" = yes; then
+ shared_flag="$shared_flag "'${wl}-G'
+ fi
+ else
+ # not using gcc
+ if test "$host_cpu" = ia64; then
+ # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+ # chokes on -Wl,-G. The following line is correct:
+ shared_flag='-G'
+ else
+ if test "$aix_use_runtimelinking" = yes; then
+ shared_flag='${wl}-G'
+ else
+ shared_flag='${wl}-bM:SRE'
+ fi
+ fi
+ fi
+
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall'
+ # It seems that -bexpall does not export symbols beginning with
+ # underscore (_), so it is better to generate a list of symbols to
+ # export.
+ _LT_TAGVAR(always_export_symbols, $1)=yes
+ if test "$aix_use_runtimelinking" = yes; then
+ # Warning - without using the other runtime loading flags (-brtl),
+ # -berok will link without error, but may produce a broken library.
+ _LT_TAGVAR(allow_undefined_flag, $1)='-berok'
+ # Determine the default libpath from the value encoded in an empty
+ # executable.
+ _LT_SYS_MODULE_PATH_AIX
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then $ECHO "X${wl}${allow_undefined_flag}" | $Xsed; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+ else
+ if test "$host_cpu" = ia64; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib'
+ _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs"
+ _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols"
+ else
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ _LT_SYS_MODULE_PATH_AIX
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+ # Warning - without using the other run time loading flags,
+ # -berok will link without error, but may produce a broken library.
+ _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok'
+ _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok'
+ # Exported symbols can be pulled into shared objects from archives
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience'
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+ # This is similar to how AIX traditionally builds its shared
+ # libraries.
+ _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+ fi
+ fi
+ ;;
+
+ beos*)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+ # support --undefined. This deserves some investigation. FIXME
+ _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ chorus*)
+ case $cc_basename in
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+ ;;
+
+ cygwin* | mingw* | pw32* | cegcc*)
+ # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless,
+ # as there is no search path for DLLs.
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ _LT_TAGVAR(always_export_symbols, $1)=no
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+
+ if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ # If the export-symbols file already is a .def file (1st line
+ # is EXPORTS), use it as is; otherwise, prepend...
+ _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
+ cp $export_symbols $output_objdir/$soname.def;
+ else
+ echo EXPORTS > $output_objdir/$soname.def;
+ cat $export_symbols >> $output_objdir/$soname.def;
+ fi~
+ $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ darwin* | rhapsody*)
+ _LT_DARWIN_LINKER_FEATURES($1)
+ ;;
+
+ dgux*)
+ case $cc_basename in
+ ec++*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ ghcx*)
+ # Green Hills C++ Compiler
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+ ;;
+
+ freebsd[[12]]*)
+ # C++ shared libraries reported to be fairly broken before
+ # switch to ELF
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ freebsd-elf*)
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ ;;
+
+ freebsd* | dragonfly*)
+ # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF
+ # conventions
+ _LT_TAGVAR(ld_shlibs, $1)=yes
+ ;;
+
+ gnu*)
+ ;;
+
+ hpux9*)
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH,
+ # but as the default
+ # location of the library.
+
+ case $cc_basename in
+ CC*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ aCC*)
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed'
+ ;;
+ *)
+ if test "$GXX" = yes; then
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+ else
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ esac
+ ;;
+
+ hpux10*|hpux11*)
+ if test $with_gnu_ld = no; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ case $host_cpu in
+ hppa*64*|ia64*)
+ ;;
+ *)
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+ ;;
+ esac
+ fi
+ case $host_cpu in
+ hppa*64*|ia64*)
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+ *)
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH,
+ # but as the default
+ # location of the library.
+ ;;
+ esac
+
+ case $cc_basename in
+ CC*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ aCC*)
+ case $host_cpu in
+ hppa*64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ ia64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ esac
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed'
+ ;;
+ *)
+ if test "$GXX" = yes; then
+ if test $with_gnu_ld = no; then
+ case $host_cpu in
+ hppa*64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ ia64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ esac
+ fi
+ else
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ esac
+ ;;
+
+ interix[[3-9]]*)
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+ # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+ # Instead, shared libraries are loaded at an image base (0x10000000 by
+ # default) and relocated if they conflict, which is a slow very memory
+ # consuming and fragmenting process. To avoid this, we pick a random,
+ # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+ # time. Moving up from 0x10000000 also allows more sbrk(2) space.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ ;;
+ irix5* | irix6*)
+ case $cc_basename in
+ CC*)
+ # SGI C++
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+
+ # Archives containing C++ object files must be created using
+ # "CC -ar", where "CC" is the IRIX C++ compiler. This is
+ # necessary to make sure instantiated templates are included
+ # in the archive.
+ _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs'
+ ;;
+ *)
+ if test "$GXX" = yes; then
+ if test "$with_gnu_ld" = no; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` -o $lib'
+ fi
+ fi
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ ;;
+ esac
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(inherit_rpath, $1)=yes
+ ;;
+
+ linux* | k*bsd*-gnu)
+ case $cc_basename in
+ KCC*)
+ # Kuck and Associates, Inc. (KAI) C++ Compiler
+
+ # KCC will only create a shared library if the output file
+ # ends with ".so" (or ".sl" for HP-UX), so rename the library
+ # to its proper name (with version) after linking.
+ _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib'
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed'
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+
+ # Archives containing C++ object files must be created using
+ # "CC -Bstatic", where "CC" is the KAI C++ compiler.
+ _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs'
+ ;;
+ icpc* | ecpc* )
+ # Intel C++
+ with_gnu_ld=yes
+ # version 8.0 and above of icpc choke on multiply defined symbols
+ # if we add $predep_objects and $postdep_objects, however 7.1 and
+ # earlier do not add the objects themselves.
+ case `$CC -V 2>&1` in
+ *"Version 7."*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ ;;
+ *) # Version 8.0 or newer
+ tmp_idyn=
+ case $host_cpu in
+ ia64*) tmp_idyn=' -i_dynamic';;
+ esac
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ ;;
+ esac
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive'
+ ;;
+ pgCC* | pgcpp*)
+ # Portland Group C++ compiler
+ case `$CC -V` in
+ *pgCC\ [[1-5]]* | *pgcpp\ [[1-5]]*)
+ _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~
+ compile_command="$compile_command `find $tpldir -name \*.o | $NL2SP`"'
+ _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~
+ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | $NL2SP`~
+ $RANLIB $oldlib'
+ _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
+ ;;
+ *) # Version 6 will use weak symbols
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
+ ;;
+ esac
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+ ;;
+ cxx*)
+ # Compaq C++
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols'
+
+ runpath_var=LD_RUN_PATH
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`$ECHO "X$templist" | $Xsed -e "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed'
+ ;;
+ xl*)
+ # IBM XL 8.0 on PPC, with GNU ld
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ if test "x$supports_anon_versioning" = xyes; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+ fi
+ ;;
+ *)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*)
+ # Sun C++ 5.9
+ _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive'
+ _LT_TAGVAR(compiler_needs_object, $1)=yes
+
+ # Not sure whether something based on
+ # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1
+ # would be better.
+ output_verbose_link_cmd='echo'
+
+ # Archives containing C++ object files must be created using
+ # "CC -xar", where "CC" is the Sun C++ compiler. This is
+ # necessary to make sure instantiated templates are included
+ # in the archive.
+ _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs'
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+
+ lynxos*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ m88k*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ mvs*)
+ case $cc_basename in
+ cxx*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+ ;;
+
+ netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags'
+ wlarc=
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ fi
+ # Workaround some broken pre-1.5 toolchains
+ output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"'
+ ;;
+
+ *nto* | *qnx*)
+ _LT_TAGVAR(ld_shlibs, $1)=yes
+ ;;
+
+ openbsd2*)
+ # C++ shared libraries are fairly broken
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ openbsd*)
+ if test -f /usr/libexec/ld.so; then
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+ if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+ fi
+ output_verbose_link_cmd=echo
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ osf3* | osf4* | osf5*)
+ case $cc_basename in
+ KCC*)
+ # Kuck and Associates, Inc. (KAI) C++ Compiler
+
+ # KCC will only create a shared library if the output file
+ # ends with ".so" (or ".sl" for HP-UX), so rename the library
+ # to its proper name (with version) after linking.
+ _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ # Archives containing C++ object files must be created using
+ # the KAI C++ compiler.
+ case $host in
+ osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;;
+ *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;;
+ esac
+ ;;
+ RCC*)
+ # Rational C++ 2.4.1
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ cxx*)
+ case $host in
+ osf3*)
+ _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && $ECHO "X${wl}-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ ;;
+ *)
+ _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~
+ echo "-hidden">> $lib.exp~
+ $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib~
+ $RM $lib.exp'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+ ;;
+ esac
+
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`$ECHO "X$templist" | $Xsed -e "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed'
+ ;;
+ *)
+ if test "$GXX" = yes && test "$with_gnu_ld" = no; then
+ _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
+ case $host in
+ osf3*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ ;;
+ esac
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"'
+
+ else
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ esac
+ ;;
+
+ psos*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ sunos4*)
+ case $cc_basename in
+ CC*)
+ # Sun C++ 4.x
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ lcc*)
+ # Lucid
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+ ;;
+
+ solaris*)
+ case $cc_basename in
+ CC*)
+ # Sun C++ 4.2, 5.x and Centerline C++
+ _LT_TAGVAR(archive_cmds_need_lc,$1)=yes
+ _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ case $host_os in
+ solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+ *)
+ # The compiler driver will combine and reorder linker options,
+ # but understands `-z linker_flag'.
+ # Supported since Solaris 2.6 (maybe 2.5.1?)
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract'
+ ;;
+ esac
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+
+ output_verbose_link_cmd='echo'
+
+ # Archives containing C++ object files must be created using
+ # "CC -xar", where "CC" is the Sun C++ compiler. This is
+ # necessary to make sure instantiated templates are included
+ # in the archive.
+ _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs'
+ ;;
+ gcx*)
+ # Green Hills C++ Compiler
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+
+ # The C++ compiler must be used to create the archive.
+ _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs'
+ ;;
+ *)
+ # GNU C++ compiler with Solaris linker
+ if test "$GXX" = yes && test "$with_gnu_ld" = no; then
+ _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-z ${wl}defs'
+ if $CC --version | $GREP -v '^2\.7' > /dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -shared -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"'
+ else
+ # g++ 2.7 appears to require `-G' NOT `-shared' on this
+ # platform.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"'
+ fi
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $wl$libdir'
+ case $host_os in
+ solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+ *)
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
+ ;;
+ esac
+ fi
+ ;;
+ esac
+ ;;
+
+ sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*)
+ _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ runpath_var='LD_RUN_PATH'
+
+ case $cc_basename in
+ CC*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6*)
+ # Note: We can NOT use -z defs as we might desire, because we do not
+ # link with -lc, and that would cause any symbols used from libc to
+ # always be unresolved, which means just about no library would
+ # ever link correctly. If we're not using GNU ld we use -z text
+ # though, which does catch some bad symbols but isn't as heavy-handed
+ # as -z defs.
+ _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
+ _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs'
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport'
+ runpath_var='LD_RUN_PATH'
+
+ case $cc_basename in
+ CC*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ ;;
+
+ tandem*)
+ case $cc_basename in
+ NCC*)
+ # NonStop-UX NCC 3.20
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+ ;;
+
+ vxworks*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+
+ AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)])
+ test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no
+
+ _LT_TAGVAR(GCC, $1)="$GXX"
+ _LT_TAGVAR(LD, $1)="$LD"
+
+ ## CAVEAT EMPTOR:
+ ## There is no encapsulation within the following macros, do not change
+ ## the running order or otherwise move them around unless you know exactly
+ ## what you are doing...
+ _LT_SYS_HIDDEN_LIBDEPS($1)
+ _LT_COMPILER_PIC($1)
+ _LT_COMPILER_C_O($1)
+ _LT_COMPILER_FILE_LOCKS($1)
+ _LT_LINKER_SHLIBS($1)
+ _LT_SYS_DYNAMIC_LINKER($1)
+ _LT_LINKER_HARDCODE_LIBPATH($1)
+
+ _LT_CONFIG($1)
+ fi # test -n "$compiler"
+
+ CC=$lt_save_CC
+ LDCXX=$LD
+ LD=$lt_save_LD
+ GCC=$lt_save_GCC
+ with_gnu_ld=$lt_save_with_gnu_ld
+ lt_cv_path_LDCXX=$lt_cv_path_LD
+ lt_cv_path_LD=$lt_save_path_LD
+ lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld
+ lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld
+fi # test "$_lt_caught_CXX_error" != yes
+
+AC_LANG_POP
+])# _LT_LANG_CXX_CONFIG
+
+
+# _LT_SYS_HIDDEN_LIBDEPS([TAGNAME])
+# ---------------------------------
+# Figure out "hidden" library dependencies from verbose
+# compiler output when linking a shared library.
+# Parse the compiler output and extract the necessary
+# objects, libraries and library flags.
+m4_defun([_LT_SYS_HIDDEN_LIBDEPS],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+# Dependencies to place before and after the object being linked:
+_LT_TAGVAR(predep_objects, $1)=
+_LT_TAGVAR(postdep_objects, $1)=
+_LT_TAGVAR(predeps, $1)=
+_LT_TAGVAR(postdeps, $1)=
+_LT_TAGVAR(compiler_lib_search_path, $1)=
+
+dnl we can't use the lt_simple_compile_test_code here,
+dnl because it contains code intended for an executable,
+dnl not a library. It's possible we should let each
+dnl tag define a new lt_????_link_test_code variable,
+dnl but it's only used here...
+m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF
+int a;
+void foo (void) { a = 0; }
+_LT_EOF
+], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF
+class Foo
+{
+public:
+ Foo (void) { a = 0; }
+private:
+ int a;
+};
+_LT_EOF
+], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF
+ subroutine foo
+ implicit none
+ integer*4 a
+ a=0
+ return
+ end
+_LT_EOF
+], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF
+ subroutine foo
+ implicit none
+ integer a
+ a=0
+ return
+ end
+_LT_EOF
+], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF
+public class foo {
+ private int a;
+ public void bar (void) {
+ a = 0;
+ }
+};
+_LT_EOF
+])
+dnl Parse the compiler output and extract the necessary
+dnl objects, libraries and library flags.
+if AC_TRY_EVAL(ac_compile); then
+ # Parse the compiler output and extract the necessary
+ # objects, libraries and library flags.
+
+ # Sentinel used to keep track of whether or not we are before
+ # the conftest object file.
+ pre_test_object_deps_done=no
+
+ for p in `eval "$output_verbose_link_cmd"`; do
+ case $p in
+
+ -L* | -R* | -l*)
+ # Some compilers place space between "-{L,R}" and the path.
+ # Remove the space.
+ if test $p = "-L" ||
+ test $p = "-R"; then
+ prev=$p
+ continue
+ else
+ prev=
+ fi
+
+ if test "$pre_test_object_deps_done" = no; then
+ case $p in
+ -L* | -R*)
+ # Internal compiler library paths should come after those
+ # provided the user. The postdeps already come after the
+ # user supplied libs so there is no need to process them.
+ if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then
+ _LT_TAGVAR(compiler_lib_search_path, $1)="${prev}${p}"
+ else
+ _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} ${prev}${p}"
+ fi
+ ;;
+ # The "-l" case would never come before the object being
+ # linked, so don't bother handling this case.
+ esac
+ else
+ if test -z "$_LT_TAGVAR(postdeps, $1)"; then
+ _LT_TAGVAR(postdeps, $1)="${prev}${p}"
+ else
+ _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} ${prev}${p}"
+ fi
+ fi
+ ;;
+
+ *.$objext)
+ # This assumes that the test object file only shows up
+ # once in the compiler output.
+ if test "$p" = "conftest.$objext"; then
+ pre_test_object_deps_done=yes
+ continue
+ fi
+
+ if test "$pre_test_object_deps_done" = no; then
+ if test -z "$_LT_TAGVAR(predep_objects, $1)"; then
+ _LT_TAGVAR(predep_objects, $1)="$p"
+ else
+ _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p"
+ fi
+ else
+ if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then
+ _LT_TAGVAR(postdep_objects, $1)="$p"
+ else
+ _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p"
+ fi
+ fi
+ ;;
+
+ *) ;; # Ignore the rest.
+
+ esac
+ done
+
+ # Clean up.
+ rm -f a.out a.exe
+else
+ echo "libtool.m4: error: problem compiling $1 test program"
+fi
+
+$RM -f confest.$objext
+
+# PORTME: override above test on systems where it is broken
+m4_if([$1], [CXX],
+[case $host_os in
+interix[[3-9]]*)
+ # Interix 3.5 installs completely hosed .la files for C++, so rather than
+ # hack all around it, let's just trust "g++" to DTRT.
+ _LT_TAGVAR(predep_objects,$1)=
+ _LT_TAGVAR(postdep_objects,$1)=
+ _LT_TAGVAR(postdeps,$1)=
+ ;;
+
+linux*)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*)
+ # Sun C++ 5.9
+
+ # The more standards-conforming stlport4 library is
+ # incompatible with the Cstd library. Avoid specifying
+ # it if it's in CXXFLAGS. Ignore libCrun as
+ # -library=stlport4 depends on it.
+ case " $CXX $CXXFLAGS " in
+ *" -library=stlport4 "*)
+ solaris_use_stlport4=yes
+ ;;
+ esac
+
+ if test "$solaris_use_stlport4" != yes; then
+ _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun'
+ fi
+ ;;
+ esac
+ ;;
+
+solaris*)
+ case $cc_basename in
+ CC*)
+ # The more standards-conforming stlport4 library is
+ # incompatible with the Cstd library. Avoid specifying
+ # it if it's in CXXFLAGS. Ignore libCrun as
+ # -library=stlport4 depends on it.
+ case " $CXX $CXXFLAGS " in
+ *" -library=stlport4 "*)
+ solaris_use_stlport4=yes
+ ;;
+ esac
+
+ # Adding this requires a known-good setup of shared libraries for
+ # Sun compiler versions before 5.6, else PIC objects from an old
+ # archive will be linked into the output, leading to subtle bugs.
+ if test "$solaris_use_stlport4" != yes; then
+ _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun'
+ fi
+ ;;
+ esac
+ ;;
+esac
+])
+
+case " $_LT_TAGVAR(postdeps, $1) " in
+*" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;;
+esac
+ _LT_TAGVAR(compiler_lib_search_dirs, $1)=
+if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then
+ _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | ${SED} -e 's! -L! !g' -e 's!^ !!'`
+fi
+_LT_TAGDECL([], [compiler_lib_search_dirs], [1],
+ [The directories searched by this compiler when creating a shared library])
+_LT_TAGDECL([], [predep_objects], [1],
+ [Dependencies to place before and after the objects being linked to
+ create a shared library])
+_LT_TAGDECL([], [postdep_objects], [1])
+_LT_TAGDECL([], [predeps], [1])
+_LT_TAGDECL([], [postdeps], [1])
+_LT_TAGDECL([], [compiler_lib_search_path], [1],
+ [The library search path used internally by the compiler when linking
+ a shared library])
+])# _LT_SYS_HIDDEN_LIBDEPS
+
+
+# _LT_PROG_F77
+# ------------
+# Since AC_PROG_F77 is broken, in that it returns the empty string
+# if there is no fortran compiler, we have our own version here.
+m4_defun([_LT_PROG_F77],
+[
+pushdef([AC_MSG_ERROR], [_lt_disable_F77=yes])
+AC_PROG_F77
+if test -z "$F77" || test "X$F77" = "Xno"; then
+ _lt_disable_F77=yes
+fi
+popdef([AC_MSG_ERROR])
+])# _LT_PROG_F77
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([_LT_PROG_F77], [])
+
+
+# _LT_LANG_F77_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for a Fortran 77 compiler are
+# suitably defined. These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_F77_CONFIG],
+[AC_REQUIRE([_LT_PROG_F77])dnl
+AC_LANG_PUSH(Fortran 77)
+
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_TAGVAR(allow_undefined_flag, $1)=
+_LT_TAGVAR(always_export_symbols, $1)=no
+_LT_TAGVAR(archive_expsym_cmds, $1)=
+_LT_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_TAGVAR(hardcode_direct, $1)=no
+_LT_TAGVAR(hardcode_direct_absolute, $1)=no
+_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)=
+_LT_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_TAGVAR(hardcode_minus_L, $1)=no
+_LT_TAGVAR(hardcode_automatic, $1)=no
+_LT_TAGVAR(inherit_rpath, $1)=no
+_LT_TAGVAR(module_cmds, $1)=
+_LT_TAGVAR(module_expsym_cmds, $1)=
+_LT_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(no_undefined_flag, $1)=
+_LT_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Source file extension for f77 test sources.
+ac_ext=f
+
+# Object file extension for compiled f77 test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# No sense in running all these tests if we already determined that
+# the F77 compiler isn't working. Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test "$_lt_disable_F77" != yes; then
+ # Code to be used in simple compile tests
+ lt_simple_compile_test_code="\
+ subroutine t
+ return
+ end
+"
+
+ # Code to be used in simple link tests
+ lt_simple_link_test_code="\
+ program t
+ end
+"
+
+ # ltmain only uses $CC for tagged configurations so make sure $CC is set.
+ _LT_TAG_COMPILER
+
+ # save warnings/boilerplate of simple test code
+ _LT_COMPILER_BOILERPLATE
+ _LT_LINKER_BOILERPLATE
+
+ # Allow CC to be a program name with arguments.
+ lt_save_CC="$CC"
+ lt_save_GCC=$GCC
+ CC=${F77-"f77"}
+ compiler=$CC
+ _LT_TAGVAR(compiler, $1)=$CC
+ _LT_CC_BASENAME([$compiler])
+ GCC=$G77
+ if test -n "$compiler"; then
+ AC_MSG_CHECKING([if libtool supports shared libraries])
+ AC_MSG_RESULT([$can_build_shared])
+
+ AC_MSG_CHECKING([whether to build shared libraries])
+ test "$can_build_shared" = "no" && enable_shared=no
+
+ # On AIX, shared libraries and static libraries use the same namespace, and
+ # are all built from PIC.
+ case $host_os in
+ aix3*)
+ test "$enable_shared" = yes && enable_static=no
+ if test -n "$RANLIB"; then
+ archive_cmds="$archive_cmds~\$RANLIB \$lib"
+ postinstall_cmds='$RANLIB $lib'
+ fi
+ ;;
+ aix[[4-9]]*)
+ if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+ test "$enable_shared" = yes && enable_static=no
+ fi
+ ;;
+ esac
+ AC_MSG_RESULT([$enable_shared])
+
+ AC_MSG_CHECKING([whether to build static libraries])
+ # Make sure either enable_shared or enable_static is yes.
+ test "$enable_shared" = yes || enable_static=yes
+ AC_MSG_RESULT([$enable_static])
+
+ _LT_TAGVAR(GCC, $1)="$G77"
+ _LT_TAGVAR(LD, $1)="$LD"
+
+ ## CAVEAT EMPTOR:
+ ## There is no encapsulation within the following macros, do not change
+ ## the running order or otherwise move them around unless you know exactly
+ ## what you are doing...
+ _LT_COMPILER_PIC($1)
+ _LT_COMPILER_C_O($1)
+ _LT_COMPILER_FILE_LOCKS($1)
+ _LT_LINKER_SHLIBS($1)
+ _LT_SYS_DYNAMIC_LINKER($1)
+ _LT_LINKER_HARDCODE_LIBPATH($1)
+
+ _LT_CONFIG($1)
+ fi # test -n "$compiler"
+
+ GCC=$lt_save_GCC
+ CC="$lt_save_CC"
+fi # test "$_lt_disable_F77" != yes
+
+AC_LANG_POP
+])# _LT_LANG_F77_CONFIG
+
+
+# _LT_PROG_FC
+# -----------
+# Since AC_PROG_FC is broken, in that it returns the empty string
+# if there is no fortran compiler, we have our own version here.
+m4_defun([_LT_PROG_FC],
+[
+pushdef([AC_MSG_ERROR], [_lt_disable_FC=yes])
+AC_PROG_FC
+if test -z "$FC" || test "X$FC" = "Xno"; then
+ _lt_disable_FC=yes
+fi
+popdef([AC_MSG_ERROR])
+])# _LT_PROG_FC
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([_LT_PROG_FC], [])
+
+
+# _LT_LANG_FC_CONFIG([TAG])
+# -------------------------
+# Ensure that the configuration variables for a Fortran compiler are
+# suitably defined. These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_FC_CONFIG],
+[AC_REQUIRE([_LT_PROG_FC])dnl
+AC_LANG_PUSH(Fortran)
+
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_TAGVAR(allow_undefined_flag, $1)=
+_LT_TAGVAR(always_export_symbols, $1)=no
+_LT_TAGVAR(archive_expsym_cmds, $1)=
+_LT_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_TAGVAR(hardcode_direct, $1)=no
+_LT_TAGVAR(hardcode_direct_absolute, $1)=no
+_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)=
+_LT_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_TAGVAR(hardcode_minus_L, $1)=no
+_LT_TAGVAR(hardcode_automatic, $1)=no
+_LT_TAGVAR(inherit_rpath, $1)=no
+_LT_TAGVAR(module_cmds, $1)=
+_LT_TAGVAR(module_expsym_cmds, $1)=
+_LT_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(no_undefined_flag, $1)=
+_LT_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Source file extension for fc test sources.
+ac_ext=${ac_fc_srcext-f}
+
+# Object file extension for compiled fc test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# No sense in running all these tests if we already determined that
+# the FC compiler isn't working. Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test "$_lt_disable_FC" != yes; then
+ # Code to be used in simple compile tests
+ lt_simple_compile_test_code="\
+ subroutine t
+ return
+ end
+"
+
+ # Code to be used in simple link tests
+ lt_simple_link_test_code="\
+ program t
+ end
+"
+
+ # ltmain only uses $CC for tagged configurations so make sure $CC is set.
+ _LT_TAG_COMPILER
+
+ # save warnings/boilerplate of simple test code
+ _LT_COMPILER_BOILERPLATE
+ _LT_LINKER_BOILERPLATE
+
+ # Allow CC to be a program name with arguments.
+ lt_save_CC="$CC"
+ lt_save_GCC=$GCC
+ CC=${FC-"f95"}
+ compiler=$CC
+ GCC=$ac_cv_fc_compiler_gnu
+
+ _LT_TAGVAR(compiler, $1)=$CC
+ _LT_CC_BASENAME([$compiler])
+
+ if test -n "$compiler"; then
+ AC_MSG_CHECKING([if libtool supports shared libraries])
+ AC_MSG_RESULT([$can_build_shared])
+
+ AC_MSG_CHECKING([whether to build shared libraries])
+ test "$can_build_shared" = "no" && enable_shared=no
+
+ # On AIX, shared libraries and static libraries use the same namespace, and
+ # are all built from PIC.
+ case $host_os in
+ aix3*)
+ test "$enable_shared" = yes && enable_static=no
+ if test -n "$RANLIB"; then
+ archive_cmds="$archive_cmds~\$RANLIB \$lib"
+ postinstall_cmds='$RANLIB $lib'
+ fi
+ ;;
+ aix[[4-9]]*)
+ if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
+ test "$enable_shared" = yes && enable_static=no
+ fi
+ ;;
+ esac
+ AC_MSG_RESULT([$enable_shared])
+
+ AC_MSG_CHECKING([whether to build static libraries])
+ # Make sure either enable_shared or enable_static is yes.
+ test "$enable_shared" = yes || enable_static=yes
+ AC_MSG_RESULT([$enable_static])
+
+ _LT_TAGVAR(GCC, $1)="$ac_cv_fc_compiler_gnu"
+ _LT_TAGVAR(LD, $1)="$LD"
+
+ ## CAVEAT EMPTOR:
+ ## There is no encapsulation within the following macros, do not change
+ ## the running order or otherwise move them around unless you know exactly
+ ## what you are doing...
+ _LT_SYS_HIDDEN_LIBDEPS($1)
+ _LT_COMPILER_PIC($1)
+ _LT_COMPILER_C_O($1)
+ _LT_COMPILER_FILE_LOCKS($1)
+ _LT_LINKER_SHLIBS($1)
+ _LT_SYS_DYNAMIC_LINKER($1)
+ _LT_LINKER_HARDCODE_LIBPATH($1)
+
+ _LT_CONFIG($1)
+ fi # test -n "$compiler"
+
+ GCC=$lt_save_GCC
+ CC="$lt_save_CC"
+fi # test "$_lt_disable_FC" != yes
+
+AC_LANG_POP
+])# _LT_LANG_FC_CONFIG
+
+
+# _LT_LANG_GCJ_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for the GNU Java Compiler compiler
+# are suitably defined. These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_GCJ_CONFIG],
+[AC_REQUIRE([LT_PROG_GCJ])dnl
+AC_LANG_SAVE
+
+# Source file extension for Java test sources.
+ac_ext=java
+
+# Object file extension for compiled Java test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="class foo {}"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }'
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+_LT_TAG_COMPILER
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+# Allow CC to be a program name with arguments.
+lt_save_CC="$CC"
+lt_save_GCC=$GCC
+GCC=yes
+CC=${GCJ-"gcj"}
+compiler=$CC
+_LT_TAGVAR(compiler, $1)=$CC
+_LT_TAGVAR(LD, $1)="$LD"
+_LT_CC_BASENAME([$compiler])
+
+# GCJ did not exist at the time GCC didn't implicitly link libc in.
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+
+## CAVEAT EMPTOR:
+## There is no encapsulation within the following macros, do not change
+## the running order or otherwise move them around unless you know exactly
+## what you are doing...
+if test -n "$compiler"; then
+ _LT_COMPILER_NO_RTTI($1)
+ _LT_COMPILER_PIC($1)
+ _LT_COMPILER_C_O($1)
+ _LT_COMPILER_FILE_LOCKS($1)
+ _LT_LINKER_SHLIBS($1)
+ _LT_LINKER_HARDCODE_LIBPATH($1)
+
+ _LT_CONFIG($1)
+fi
+
+AC_LANG_RESTORE
+
+GCC=$lt_save_GCC
+CC="$lt_save_CC"
+])# _LT_LANG_GCJ_CONFIG
+
+
+# _LT_LANG_RC_CONFIG([TAG])
+# -------------------------
+# Ensure that the configuration variables for the Windows resource compiler
+# are suitably defined. These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to `libtool'.
+m4_defun([_LT_LANG_RC_CONFIG],
+[AC_REQUIRE([LT_PROG_RC])dnl
+AC_LANG_SAVE
+
+# Source file extension for RC test sources.
+ac_ext=rc
+
+# Object file extension for compiled RC test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }'
+
+# Code to be used in simple link tests
+lt_simple_link_test_code="$lt_simple_compile_test_code"
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+_LT_TAG_COMPILER
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+# Allow CC to be a program name with arguments.
+lt_save_CC="$CC"
+lt_save_GCC=$GCC
+GCC=
+CC=${RC-"windres"}
+compiler=$CC
+_LT_TAGVAR(compiler, $1)=$CC
+_LT_CC_BASENAME([$compiler])
+_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes
+
+if test -n "$compiler"; then
+ :
+ _LT_CONFIG($1)
+fi
+
+GCC=$lt_save_GCC
+AC_LANG_RESTORE
+CC="$lt_save_CC"
+])# _LT_LANG_RC_CONFIG
+
+
+# LT_PROG_GCJ
+# -----------
+AC_DEFUN([LT_PROG_GCJ],
+[m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ],
+ [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ],
+ [AC_CHECK_TOOL(GCJ, gcj,)
+ test "x${GCJFLAGS+set}" = xset || GCJFLAGS="-g -O2"
+ AC_SUBST(GCJFLAGS)])])[]dnl
+])
+
+# Old name:
+AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([LT_AC_PROG_GCJ], [])
+
+
+# LT_PROG_RC
+# ----------
+AC_DEFUN([LT_PROG_RC],
+[AC_CHECK_TOOL(RC, windres,)
+])
+
+# Old name:
+AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([LT_AC_PROG_RC], [])
+
+
+# _LT_DECL_EGREP
+# --------------
+# If we don't have a new enough Autoconf to choose the best grep
+# available, choose the one first in the user's PATH.
+m4_defun([_LT_DECL_EGREP],
+[AC_REQUIRE([AC_PROG_EGREP])dnl
+AC_REQUIRE([AC_PROG_FGREP])dnl
+test -z "$GREP" && GREP=grep
+_LT_DECL([], [GREP], [1], [A grep program that handles long lines])
+_LT_DECL([], [EGREP], [1], [An ERE matcher])
+_LT_DECL([], [FGREP], [1], [A literal string matcher])
+dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too
+AC_SUBST([GREP])
+])
+
+
+# _LT_DECL_OBJDUMP
+# --------------
+# If we don't have a new enough Autoconf to choose the best objdump
+# available, choose the one first in the user's PATH.
+m4_defun([_LT_DECL_OBJDUMP],
+[AC_CHECK_TOOL(OBJDUMP, objdump, false)
+test -z "$OBJDUMP" && OBJDUMP=objdump
+_LT_DECL([], [OBJDUMP], [1], [An object symbol dumper])
+AC_SUBST([OBJDUMP])
+])
+
+
+# _LT_DECL_SED
+# ------------
+# Check for a fully-functional sed program, that truncates
+# as few characters as possible. Prefer GNU sed if found.
+m4_defun([_LT_DECL_SED],
+[AC_PROG_SED
+test -z "$SED" && SED=sed
+Xsed="$SED -e 1s/^X//"
+_LT_DECL([], [SED], [1], [A sed program that does not truncate output])
+_LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"],
+ [Sed that helps us avoid accidentally triggering echo(1) options like -n])
+])# _LT_DECL_SED
+
+m4_ifndef([AC_PROG_SED], [
+############################################################
+# NOTE: This macro has been submitted for inclusion into #
+# GNU Autoconf as AC_PROG_SED. When it is available in #
+# a released version of Autoconf we should remove this #
+# macro and use it instead. #
+############################################################
+
+m4_defun([AC_PROG_SED],
+[AC_MSG_CHECKING([for a sed that does not truncate output])
+AC_CACHE_VAL(lt_cv_path_SED,
+[# Loop through the user's path and test for sed and gsed.
+# Then use that list of sed's as ones to test for truncation.
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for lt_ac_prog in sed gsed; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then
+ lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext"
+ fi
+ done
+ done
+done
+IFS=$as_save_IFS
+lt_ac_max=0
+lt_ac_count=0
+# Add /usr/xpg4/bin/sed as it is typically found on Solaris
+# along with /bin/sed that truncates output.
+for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do
+ test ! -f $lt_ac_sed && continue
+ cat /dev/null > conftest.in
+ lt_ac_count=0
+ echo $ECHO_N "0123456789$ECHO_C" >conftest.in
+ # Check for GNU sed and select it if it is found.
+ if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then
+ lt_cv_path_SED=$lt_ac_sed
+ break
+ fi
+ while true; do
+ cat conftest.in conftest.in >conftest.tmp
+ mv conftest.tmp conftest.in
+ cp conftest.in conftest.nl
+ echo >>conftest.nl
+ $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break
+ cmp -s conftest.out conftest.nl || break
+ # 10000 chars as input seems more than enough
+ test $lt_ac_count -gt 10 && break
+ lt_ac_count=`expr $lt_ac_count + 1`
+ if test $lt_ac_count -gt $lt_ac_max; then
+ lt_ac_max=$lt_ac_count
+ lt_cv_path_SED=$lt_ac_sed
+ fi
+ done
+done
+])
+SED=$lt_cv_path_SED
+AC_SUBST([SED])
+AC_MSG_RESULT([$SED])
+])#AC_PROG_SED
+])#m4_ifndef
+
+# Old name:
+AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([LT_AC_PROG_SED], [])
+
+
+# _LT_CHECK_SHELL_FEATURES
+# ------------------------
+# Find out whether the shell is Bourne or XSI compatible,
+# or has some other useful features.
+m4_defun([_LT_CHECK_SHELL_FEATURES],
+[AC_MSG_CHECKING([whether the shell understands some XSI constructs])
+# Try some XSI features
+xsi_shell=no
+( _lt_dummy="a/b/c"
+ test "${_lt_dummy##*/},${_lt_dummy%/*},"${_lt_dummy%"$_lt_dummy"}, \
+ = c,a/b,, \
+ && eval 'test $(( 1 + 1 )) -eq 2 \
+ && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \
+ && xsi_shell=yes
+AC_MSG_RESULT([$xsi_shell])
+_LT_CONFIG_LIBTOOL_INIT([xsi_shell='$xsi_shell'])
+
+AC_MSG_CHECKING([whether the shell understands "+="])
+lt_shell_append=no
+( foo=bar; set foo baz; eval "$[1]+=\$[2]" && test "$foo" = barbaz ) \
+ >/dev/null 2>&1 \
+ && lt_shell_append=yes
+AC_MSG_RESULT([$lt_shell_append])
+_LT_CONFIG_LIBTOOL_INIT([lt_shell_append='$lt_shell_append'])
+
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ lt_unset=unset
+else
+ lt_unset=false
+fi
+_LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl
+
+# test EBCDIC or ASCII
+case `echo X|tr X '\101'` in
+ A) # ASCII based system
+ # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr
+ lt_SP2NL='tr \040 \012'
+ lt_NL2SP='tr \015\012 \040\040'
+ ;;
+ *) # EBCDIC based system
+ lt_SP2NL='tr \100 \n'
+ lt_NL2SP='tr \r\n \100\100'
+ ;;
+esac
+_LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl
+_LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl
+])# _LT_CHECK_SHELL_FEATURES
+
+
+# _LT_PROG_XSI_SHELLFNS
+# ---------------------
+# Bourne and XSI compatible variants of some useful shell functions.
+m4_defun([_LT_PROG_XSI_SHELLFNS],
+[case $xsi_shell in
+ yes)
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_dirname file append nondir_replacement
+# Compute the dirname of FILE. If nonempty, add APPEND to the result,
+# otherwise set result to NONDIR_REPLACEMENT.
+func_dirname ()
+{
+ case ${1} in
+ */*) func_dirname_result="${1%/*}${2}" ;;
+ * ) func_dirname_result="${3}" ;;
+ esac
+}
+
+# func_basename file
+func_basename ()
+{
+ func_basename_result="${1##*/}"
+}
+
+# func_dirname_and_basename file append nondir_replacement
+# perform func_basename and func_dirname in a single function
+# call:
+# dirname: Compute the dirname of FILE. If nonempty,
+# add APPEND to the result, otherwise set result
+# to NONDIR_REPLACEMENT.
+# value returned in "$func_dirname_result"
+# basename: Compute filename of FILE.
+# value retuned in "$func_basename_result"
+# Implementation must be kept synchronized with func_dirname
+# and func_basename. For efficiency, we do not delegate to
+# those functions but instead duplicate the functionality here.
+func_dirname_and_basename ()
+{
+ case ${1} in
+ */*) func_dirname_result="${1%/*}${2}" ;;
+ * ) func_dirname_result="${3}" ;;
+ esac
+ func_basename_result="${1##*/}"
+}
+
+# func_stripname prefix suffix name
+# strip PREFIX and SUFFIX off of NAME.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+func_stripname ()
+{
+ # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are
+ # positional parameters, so assign one to ordinary parameter first.
+ func_stripname_result=${3}
+ func_stripname_result=${func_stripname_result#"${1}"}
+ func_stripname_result=${func_stripname_result%"${2}"}
+}
+
+# func_opt_split
+func_opt_split ()
+{
+ func_opt_split_opt=${1%%=*}
+ func_opt_split_arg=${1#*=}
+}
+
+# func_lo2o object
+func_lo2o ()
+{
+ case ${1} in
+ *.lo) func_lo2o_result=${1%.lo}.${objext} ;;
+ *) func_lo2o_result=${1} ;;
+ esac
+}
+
+# func_xform libobj-or-source
+func_xform ()
+{
+ func_xform_result=${1%.*}.lo
+}
+
+# func_arith arithmetic-term...
+func_arith ()
+{
+ func_arith_result=$(( $[*] ))
+}
+
+# func_len string
+# STRING may not start with a hyphen.
+func_len ()
+{
+ func_len_result=${#1}
+}
+
+_LT_EOF
+ ;;
+ *) # Bourne compatible functions.
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_dirname file append nondir_replacement
+# Compute the dirname of FILE. If nonempty, add APPEND to the result,
+# otherwise set result to NONDIR_REPLACEMENT.
+func_dirname ()
+{
+ # Extract subdirectory from the argument.
+ func_dirname_result=`$ECHO "X${1}" | $Xsed -e "$dirname"`
+ if test "X$func_dirname_result" = "X${1}"; then
+ func_dirname_result="${3}"
+ else
+ func_dirname_result="$func_dirname_result${2}"
+ fi
+}
+
+# func_basename file
+func_basename ()
+{
+ func_basename_result=`$ECHO "X${1}" | $Xsed -e "$basename"`
+}
+
+dnl func_dirname_and_basename
+dnl A portable version of this function is already defined in general.m4sh
+dnl so there is no need for it here.
+
+# func_stripname prefix suffix name
+# strip PREFIX and SUFFIX off of NAME.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+# func_strip_suffix prefix name
+func_stripname ()
+{
+ case ${2} in
+ .*) func_stripname_result=`$ECHO "X${3}" \
+ | $Xsed -e "s%^${1}%%" -e "s%\\\\${2}\$%%"`;;
+ *) func_stripname_result=`$ECHO "X${3}" \
+ | $Xsed -e "s%^${1}%%" -e "s%${2}\$%%"`;;
+ esac
+}
+
+# sed scripts:
+my_sed_long_opt='1s/^\(-[[^=]]*\)=.*/\1/;q'
+my_sed_long_arg='1s/^-[[^=]]*=//'
+
+# func_opt_split
+func_opt_split ()
+{
+ func_opt_split_opt=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_opt"`
+ func_opt_split_arg=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_arg"`
+}
+
+# func_lo2o object
+func_lo2o ()
+{
+ func_lo2o_result=`$ECHO "X${1}" | $Xsed -e "$lo2o"`
+}
+
+# func_xform libobj-or-source
+func_xform ()
+{
+ func_xform_result=`$ECHO "X${1}" | $Xsed -e 's/\.[[^.]]*$/.lo/'`
+}
+
+# func_arith arithmetic-term...
+func_arith ()
+{
+ func_arith_result=`expr "$[@]"`
+}
+
+# func_len string
+# STRING may not start with a hyphen.
+func_len ()
+{
+ func_len_result=`expr "$[1]" : ".*" 2>/dev/null || echo $max_cmd_len`
+}
+
+_LT_EOF
+esac
+
+case $lt_shell_append in
+ yes)
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_append var value
+# Append VALUE to the end of shell variable VAR.
+func_append ()
+{
+ eval "$[1]+=\$[2]"
+}
+_LT_EOF
+ ;;
+ *)
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_append var value
+# Append VALUE to the end of shell variable VAR.
+func_append ()
+{
+ eval "$[1]=\$$[1]\$[2]"
+}
+
+_LT_EOF
+ ;;
+ esac
+])
diff --git a/m4/lock.m4 b/m4/lock.m4
new file mode 100644
index 00000000..0224f2ff
--- /dev/null
+++ b/m4/lock.m4
@@ -0,0 +1,311 @@
+# lock.m4 serial 6 (gettext-0.16)
+dnl Copyright (C) 2005-2006 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Bruno Haible.
+
+dnl Tests for a multithreading library to be used.
+dnl Defines at most one of the macros USE_POSIX_THREADS, USE_SOLARIS_THREADS,
+dnl USE_PTH_THREADS, USE_WIN32_THREADS
+dnl Sets the variables LIBTHREAD and LTLIBTHREAD to the linker options for use
+dnl in a Makefile (LIBTHREAD for use without libtool, LTLIBTHREAD for use with
+dnl libtool).
+dnl Sets the variables LIBMULTITHREAD and LTLIBMULTITHREAD similarly, for
+dnl programs that really need multithread functionality. The difference
+dnl between LIBTHREAD and LIBMULTITHREAD is that on platforms supporting weak
+dnl symbols, typically LIBTHREAD="" whereas LIBMULTITHREAD="-lpthread".
+dnl Adds to CPPFLAGS the flag -D_REENTRANT or -D_THREAD_SAFE if needed for
+dnl multithread-safe programs.
+
+AC_DEFUN([gl_LOCK_EARLY],
+[
+ AC_REQUIRE([gl_LOCK_EARLY_BODY])
+])
+
+dnl The guts of gl_LOCK_EARLY. Needs to be expanded only once.
+
+AC_DEFUN([gl_LOCK_EARLY_BODY],
+[
+ dnl Ordering constraints: This macro modifies CPPFLAGS in a way that
+ dnl influences the result of the autoconf tests that test for *_unlocked
+ dnl declarations, on AIX 5 at least. Therefore it must come early.
+ AC_BEFORE([$0], [gl_FUNC_GLIBC_UNLOCKED_IO])dnl
+ AC_BEFORE([$0], [gl_ARGP])dnl
+
+ AC_REQUIRE([AC_CANONICAL_HOST])
+ AC_REQUIRE([AC_GNU_SOURCE]) dnl needed for pthread_rwlock_t on glibc systems
+ dnl Check for multithreading.
+ AC_ARG_ENABLE(threads,
+AC_HELP_STRING([--enable-threads={posix|solaris|pth|win32}], [specify multithreading API])
+AC_HELP_STRING([--disable-threads], [build without multithread safety]),
+ [gl_use_threads=$enableval],
+ [case "$host_os" in
+ dnl Disable multithreading by default on OSF/1, because it interferes
+ dnl with fork()/exec(): When msgexec is linked with -lpthread, its child
+ dnl process gets an endless segmentation fault inside execvp().
+ osf*) gl_use_threads=no ;;
+ *) gl_use_threads=yes ;;
+ esac
+ ])
+ if test "$gl_use_threads" = yes || test "$gl_use_threads" = posix; then
+ # For using <pthread.h>:
+ case "$host_os" in
+ osf*)
+ # On OSF/1, the compiler needs the flag -D_REENTRANT so that it
+ # groks <pthread.h>. cc also understands the flag -pthread, but
+ # we don't use it because 1. gcc-2.95 doesn't understand -pthread,
+ # 2. putting a flag into CPPFLAGS that has an effect on the linker
+ # causes the AC_TRY_LINK test below to succeed unexpectedly,
+ # leading to wrong values of LIBTHREAD and LTLIBTHREAD.
+ CPPFLAGS="$CPPFLAGS -D_REENTRANT"
+ ;;
+ esac
+ # Some systems optimize for single-threaded programs by default, and
+ # need special flags to disable these optimizations. For example, the
+ # definition of 'errno' in <errno.h>.
+ case "$host_os" in
+ aix* | freebsd*) CPPFLAGS="$CPPFLAGS -D_THREAD_SAFE" ;;
+ solaris*) CPPFLAGS="$CPPFLAGS -D_REENTRANT" ;;
+ esac
+ fi
+])
+
+dnl The guts of gl_LOCK. Needs to be expanded only once.
+
+AC_DEFUN([gl_LOCK_BODY],
+[
+ AC_REQUIRE([gl_LOCK_EARLY_BODY])
+ gl_threads_api=none
+ LIBTHREAD=
+ LTLIBTHREAD=
+ LIBMULTITHREAD=
+ LTLIBMULTITHREAD=
+ if test "$gl_use_threads" != no; then
+ dnl Check whether the compiler and linker support weak declarations.
+ AC_MSG_CHECKING([whether imported symbols can be declared weak])
+ gl_have_weak=no
+ AC_TRY_LINK([extern void xyzzy ();
+#pragma weak xyzzy], [xyzzy();], [gl_have_weak=yes])
+ AC_MSG_RESULT([$gl_have_weak])
+ if test "$gl_use_threads" = yes || test "$gl_use_threads" = posix; then
+ # On OSF/1, the compiler needs the flag -pthread or -D_REENTRANT so that
+ # it groks <pthread.h>. It's added above, in gl_LOCK_EARLY_BODY.
+ AC_CHECK_HEADER(pthread.h, gl_have_pthread_h=yes, gl_have_pthread_h=no)
+ if test "$gl_have_pthread_h" = yes; then
+ # Other possible tests:
+ # -lpthreads (FSU threads, PCthreads)
+ # -lgthreads
+ gl_have_pthread=
+ # Test whether both pthread_mutex_lock and pthread_mutexattr_init exist
+ # in libc. IRIX 6.5 has the first one in both libc and libpthread, but
+ # the second one only in libpthread, and lock.c needs it.
+ AC_TRY_LINK([#include <pthread.h>],
+ [pthread_mutex_lock((pthread_mutex_t*)0);
+ pthread_mutexattr_init((pthread_mutexattr_t*)0);],
+ [gl_have_pthread=yes])
+ # Test for libpthread by looking for pthread_kill. (Not pthread_self,
+ # since it is defined as a macro on OSF/1.)
+ if test -n "$gl_have_pthread"; then
+ # The program links fine without libpthread. But it may actually
+ # need to link with libpthread in order to create multiple threads.
+ AC_CHECK_LIB(pthread, pthread_kill,
+ [LIBMULTITHREAD=-lpthread LTLIBMULTITHREAD=-lpthread
+ # On Solaris and HP-UX, most pthread functions exist also in libc.
+ # Therefore pthread_in_use() needs to actually try to create a
+ # thread: pthread_create from libc will fail, whereas
+ # pthread_create will actually create a thread.
+ case "$host_os" in
+ solaris* | hpux*)
+ AC_DEFINE([PTHREAD_IN_USE_DETECTION_HARD], 1,
+ [Define if the pthread_in_use() detection is hard.])
+ esac
+ ])
+ else
+ # Some library is needed. Try libpthread and libc_r.
+ AC_CHECK_LIB(pthread, pthread_kill,
+ [gl_have_pthread=yes
+ LIBTHREAD=-lpthread LTLIBTHREAD=-lpthread
+ LIBMULTITHREAD=-lpthread LTLIBMULTITHREAD=-lpthread])
+ if test -z "$gl_have_pthread"; then
+ # For FreeBSD 4.
+ AC_CHECK_LIB(c_r, pthread_kill,
+ [gl_have_pthread=yes
+ LIBTHREAD=-lc_r LTLIBTHREAD=-lc_r
+ LIBMULTITHREAD=-lc_r LTLIBMULTITHREAD=-lc_r])
+ fi
+ fi
+ if test -n "$gl_have_pthread"; then
+ gl_threads_api=posix
+ AC_DEFINE([USE_POSIX_THREADS], 1,
+ [Define if the POSIX multithreading library can be used.])
+ if test -n "$LIBMULTITHREAD" || test -n "$LTLIBMULTITHREAD"; then
+ if test $gl_have_weak = yes; then
+ AC_DEFINE([USE_POSIX_THREADS_WEAK], 1,
+ [Define if references to the POSIX multithreading library should be made weak.])
+ LIBTHREAD=
+ LTLIBTHREAD=
+ fi
+ fi
+ # OSF/1 4.0 and MacOS X 10.1 lack the pthread_rwlock_t type and the
+ # pthread_rwlock_* functions.
+ AC_CHECK_TYPE([pthread_rwlock_t],
+ [AC_DEFINE([HAVE_PTHREAD_RWLOCK], 1,
+ [Define if the POSIX multithreading library has read/write locks.])],
+ [],
+ [#include <pthread.h>])
+ # glibc defines PTHREAD_MUTEX_RECURSIVE as enum, not as a macro.
+ AC_TRY_COMPILE([#include <pthread.h>],
+ [#if __FreeBSD__ == 4
+error "No, in FreeBSD 4.0 recursive mutexes actually don't work."
+#else
+int x = (int)PTHREAD_MUTEX_RECURSIVE;
+return !x;
+#endif],
+ [AC_DEFINE([HAVE_PTHREAD_MUTEX_RECURSIVE], 1,
+ [Define if the <pthread.h> defines PTHREAD_MUTEX_RECURSIVE.])])
+ fi
+ fi
+ fi
+ if test -z "$gl_have_pthread"; then
+ if test "$gl_use_threads" = yes || test "$gl_use_threads" = solaris; then
+ gl_have_solaristhread=
+ gl_save_LIBS="$LIBS"
+ LIBS="$LIBS -lthread"
+ AC_TRY_LINK([#include <thread.h>
+#include <synch.h>],
+ [thr_self();],
+ [gl_have_solaristhread=yes])
+ LIBS="$gl_save_LIBS"
+ if test -n "$gl_have_solaristhread"; then
+ gl_threads_api=solaris
+ LIBTHREAD=-lthread
+ LTLIBTHREAD=-lthread
+ LIBMULTITHREAD="$LIBTHREAD"
+ LTLIBMULTITHREAD="$LTLIBTHREAD"
+ AC_DEFINE([USE_SOLARIS_THREADS], 1,
+ [Define if the old Solaris multithreading library can be used.])
+ if test $gl_have_weak = yes; then
+ AC_DEFINE([USE_SOLARIS_THREADS_WEAK], 1,
+ [Define if references to the old Solaris multithreading library should be made weak.])
+ LIBTHREAD=
+ LTLIBTHREAD=
+ fi
+ fi
+ fi
+ fi
+ if test "$gl_use_threads" = pth; then
+ gl_save_CPPFLAGS="$CPPFLAGS"
+ AC_LIB_LINKFLAGS(pth)
+ gl_have_pth=
+ gl_save_LIBS="$LIBS"
+ LIBS="$LIBS -lpth"
+ AC_TRY_LINK([#include <pth.h>], [pth_self();], gl_have_pth=yes)
+ LIBS="$gl_save_LIBS"
+ if test -n "$gl_have_pth"; then
+ gl_threads_api=pth
+ LIBTHREAD="$LIBPTH"
+ LTLIBTHREAD="$LTLIBPTH"
+ LIBMULTITHREAD="$LIBTHREAD"
+ LTLIBMULTITHREAD="$LTLIBTHREAD"
+ AC_DEFINE([USE_PTH_THREADS], 1,
+ [Define if the GNU Pth multithreading library can be used.])
+ if test -n "$LIBMULTITHREAD" || test -n "$LTLIBMULTITHREAD"; then
+ if test $gl_have_weak = yes; then
+ AC_DEFINE([USE_PTH_THREADS_WEAK], 1,
+ [Define if references to the GNU Pth multithreading library should be made weak.])
+ LIBTHREAD=
+ LTLIBTHREAD=
+ fi
+ fi
+ else
+ CPPFLAGS="$gl_save_CPPFLAGS"
+ fi
+ fi
+ if test -z "$gl_have_pthread"; then
+ if test "$gl_use_threads" = yes || test "$gl_use_threads" = win32; then
+ if { case "$host_os" in
+ mingw*) true;;
+ *) false;;
+ esac
+ }; then
+ gl_threads_api=win32
+ AC_DEFINE([USE_WIN32_THREADS], 1,
+ [Define if the Win32 multithreading API can be used.])
+ fi
+ fi
+ fi
+ fi
+ AC_MSG_CHECKING([for multithread API to use])
+ AC_MSG_RESULT([$gl_threads_api])
+ AC_SUBST(LIBTHREAD)
+ AC_SUBST(LTLIBTHREAD)
+ AC_SUBST(LIBMULTITHREAD)
+ AC_SUBST(LTLIBMULTITHREAD)
+])
+
+AC_DEFUN([gl_LOCK],
+[
+ AC_REQUIRE([gl_LOCK_EARLY])
+ AC_REQUIRE([gl_LOCK_BODY])
+ gl_PREREQ_LOCK
+])
+
+# Prerequisites of lib/lock.c.
+AC_DEFUN([gl_PREREQ_LOCK], [
+ AC_REQUIRE([AC_C_INLINE])
+])
+
+dnl Survey of platforms:
+dnl
+dnl Platform Available Compiler Supports test-lock
+dnl flavours option weak result
+dnl --------------- --------- --------- -------- ---------
+dnl Linux 2.4/glibc posix -lpthread Y OK
+dnl
+dnl GNU Hurd/glibc posix
+dnl
+dnl FreeBSD 5.3 posix -lc_r Y
+dnl posix -lkse ? Y
+dnl posix -lpthread ? Y
+dnl posix -lthr Y
+dnl
+dnl FreeBSD 5.2 posix -lc_r Y
+dnl posix -lkse Y
+dnl posix -lthr Y
+dnl
+dnl FreeBSD 4.0,4.10 posix -lc_r Y OK
+dnl
+dnl NetBSD 1.6 --
+dnl
+dnl OpenBSD 3.4 posix -lpthread Y OK
+dnl
+dnl MacOS X 10.[123] posix -lpthread Y OK
+dnl
+dnl Solaris 7,8,9 posix -lpthread Y Sol 7,8: 0.0; Sol 9: OK
+dnl solaris -lthread Y Sol 7,8: 0.0; Sol 9: OK
+dnl
+dnl HP-UX 11 posix -lpthread N (cc) OK
+dnl Y (gcc)
+dnl
+dnl IRIX 6.5 posix -lpthread Y 0.5
+dnl
+dnl AIX 4.3,5.1 posix -lpthread N AIX 4: 0.5; AIX 5: OK
+dnl
+dnl OSF/1 4.0,5.1 posix -pthread (cc) N OK
+dnl -lpthread (gcc) Y
+dnl
+dnl Cygwin posix -lpthread Y OK
+dnl
+dnl Any of the above pth -lpth 0.0
+dnl
+dnl Mingw win32 N OK
+dnl
+dnl BeOS 5 --
+dnl
+dnl The test-lock result shows what happens if in test-lock.c EXPLICIT_YIELD is
+dnl turned off:
+dnl OK if all three tests terminate OK,
+dnl 0.5 if the first test terminates OK but the second one loops endlessly,
+dnl 0.0 if the first test already loops endlessly.
diff --git a/m4/longdouble.m4 b/m4/longdouble.m4
new file mode 100644
index 00000000..25590f47
--- /dev/null
+++ b/m4/longdouble.m4
@@ -0,0 +1,31 @@
+# longdouble.m4 serial 2 (gettext-0.15)
+dnl Copyright (C) 2002-2003, 2006 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Bruno Haible.
+dnl Test whether the compiler supports the 'long double' type.
+dnl Prerequisite: AC_PROG_CC
+
+dnl This file is only needed in autoconf <= 2.59. Newer versions of autoconf
+dnl have a macro AC_TYPE_LONG_DOUBLE with identical semantics.
+
+AC_DEFUN([gt_TYPE_LONGDOUBLE],
+[
+ AC_CACHE_CHECK([for long double], gt_cv_c_long_double,
+ [if test "$GCC" = yes; then
+ gt_cv_c_long_double=yes
+ else
+ AC_TRY_COMPILE([
+ /* The Stardent Vistra knows sizeof(long double), but does not support it. */
+ long double foo = 0.0;
+ /* On Ultrix 4.3 cc, long double is 4 and double is 8. */
+ int array [2*(sizeof(long double) >= sizeof(double)) - 1];
+ ], ,
+ gt_cv_c_long_double=yes, gt_cv_c_long_double=no)
+ fi])
+ if test $gt_cv_c_long_double = yes; then
+ AC_DEFINE(HAVE_LONG_DOUBLE, 1, [Define if you have the 'long double' type.])
+ fi
+])
diff --git a/m4/longlong.m4 b/m4/longlong.m4
new file mode 100644
index 00000000..3716c09f
--- /dev/null
+++ b/m4/longlong.m4
@@ -0,0 +1,48 @@
+# longlong.m4 serial 8
+dnl Copyright (C) 1999-2006 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Paul Eggert.
+
+# Define HAVE_LONG_LONG_INT if 'long long int' works.
+# This fixes a bug in Autoconf 2.60, but can be removed once we
+# assume 2.61 everywhere.
+
+# Note: If the type 'long long int' exists but is only 32 bits large
+# (as on some very old compilers), AC_TYPE_LONG_LONG_INT will not be
+# defined. In this case you can treat 'long long int' like 'long int'.
+
+AC_DEFUN([AC_TYPE_LONG_LONG_INT],
+[
+ AC_CACHE_CHECK([for long long int], [ac_cv_type_long_long_int],
+ [AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[long long int ll = 9223372036854775807ll;
+ long long int nll = -9223372036854775807LL;
+ typedef int a[((-9223372036854775807LL < 0
+ && 0 < 9223372036854775807ll)
+ ? 1 : -1)];
+ int i = 63;]],
+ [[long long int llmax = 9223372036854775807ll;
+ return ((ll << 63) | (ll >> 63) | (ll < i) | (ll > i)
+ | (llmax / ll) | (llmax % ll));]])],
+ [ac_cv_type_long_long_int=yes],
+ [ac_cv_type_long_long_int=no])])
+ if test $ac_cv_type_long_long_int = yes; then
+ AC_DEFINE([HAVE_LONG_LONG_INT], 1,
+ [Define to 1 if the system has the type `long long int'.])
+ fi
+])
+
+# This macro is obsolescent and should go away soon.
+AC_DEFUN([gl_AC_TYPE_LONG_LONG],
+[
+ AC_REQUIRE([AC_TYPE_LONG_LONG_INT])
+ ac_cv_type_long_long=$ac_cv_type_long_long_int
+ if test $ac_cv_type_long_long = yes; then
+ AC_DEFINE(HAVE_LONG_LONG, 1,
+ [Define if you have the 'long long' type.])
+ fi
+])
diff --git a/m4/ltoptions.m4 b/m4/ltoptions.m4
new file mode 100644
index 00000000..34151a3b
--- /dev/null
+++ b/m4/ltoptions.m4
@@ -0,0 +1,368 @@
+# Helper functions for option handling. -*- Autoconf -*-
+#
+# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
+# Written by Gary V. Vaughan, 2004
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# serial 6 ltoptions.m4
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])])
+
+
+# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME)
+# ------------------------------------------
+m4_define([_LT_MANGLE_OPTION],
+[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])])
+
+
+# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME)
+# ---------------------------------------
+# Set option OPTION-NAME for macro MACRO-NAME, and if there is a
+# matching handler defined, dispatch to it. Other OPTION-NAMEs are
+# saved as a flag.
+m4_define([_LT_SET_OPTION],
+[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl
+m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]),
+ _LT_MANGLE_DEFUN([$1], [$2]),
+ [m4_warning([Unknown $1 option `$2'])])[]dnl
+])
+
+
+# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET])
+# ------------------------------------------------------------
+# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
+m4_define([_LT_IF_OPTION],
+[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])])
+
+
+# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET)
+# -------------------------------------------------------
+# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME
+# are set.
+m4_define([_LT_UNLESS_OPTIONS],
+[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
+ [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option),
+ [m4_define([$0_found])])])[]dnl
+m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3
+])[]dnl
+])
+
+
+# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST)
+# ----------------------------------------
+# OPTION-LIST is a space-separated list of Libtool options associated
+# with MACRO-NAME. If any OPTION has a matching handler declared with
+# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about
+# the unknown option and exit.
+m4_defun([_LT_SET_OPTIONS],
+[# Set options
+m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
+ [_LT_SET_OPTION([$1], _LT_Option)])
+
+m4_if([$1],[LT_INIT],[
+ dnl
+ dnl Simply set some default values (i.e off) if boolean options were not
+ dnl specified:
+ _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no
+ ])
+ _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no
+ ])
+ dnl
+ dnl If no reference was made to various pairs of opposing options, then
+ dnl we run the default mode handler for the pair. For example, if neither
+ dnl `shared' nor `disable-shared' was passed, we enable building of shared
+ dnl archives by default:
+ _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED])
+ _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC])
+ _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC])
+ _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install],
+ [_LT_ENABLE_FAST_INSTALL])
+ ])
+])# _LT_SET_OPTIONS
+
+
+## --------------------------------- ##
+## Macros to handle LT_INIT options. ##
+## --------------------------------- ##
+
+# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME)
+# -----------------------------------------
+m4_define([_LT_MANGLE_DEFUN],
+[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])])
+
+
+# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE)
+# -----------------------------------------------
+m4_define([LT_OPTION_DEFINE],
+[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl
+])# LT_OPTION_DEFINE
+
+
+# dlopen
+# ------
+LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes
+])
+
+AU_DEFUN([AC_LIBTOOL_DLOPEN],
+[_LT_SET_OPTION([LT_INIT], [dlopen])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `dlopen' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], [])
+
+
+# win32-dll
+# ---------
+# Declare package support for building win32 dll's.
+LT_OPTION_DEFINE([LT_INIT], [win32-dll],
+[enable_win32_dll=yes
+
+case $host in
+*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-cegcc*)
+ AC_CHECK_TOOL(AS, as, false)
+ AC_CHECK_TOOL(DLLTOOL, dlltool, false)
+ AC_CHECK_TOOL(OBJDUMP, objdump, false)
+ ;;
+esac
+
+test -z "$AS" && AS=as
+_LT_DECL([], [AS], [0], [Assembler program])dnl
+
+test -z "$DLLTOOL" && DLLTOOL=dlltool
+_LT_DECL([], [DLLTOOL], [0], [DLL creation program])dnl
+
+test -z "$OBJDUMP" && OBJDUMP=objdump
+_LT_DECL([], [OBJDUMP], [0], [Object dumper program])dnl
+])# win32-dll
+
+AU_DEFUN([AC_LIBTOOL_WIN32_DLL],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+_LT_SET_OPTION([LT_INIT], [win32-dll])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `win32-dll' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], [])
+
+
+# _LT_ENABLE_SHARED([DEFAULT])
+# ----------------------------
+# implement the --enable-shared flag, and supports the `shared' and
+# `disable-shared' LT_INIT options.
+# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'.
+m4_define([_LT_ENABLE_SHARED],
+[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([shared],
+ [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@],
+ [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])],
+ [p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_shared=yes ;;
+ no) enable_shared=no ;;
+ *)
+ enable_shared=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ for pkg in $enableval; do
+ IFS="$lt_save_ifs"
+ if test "X$pkg" = "X$p"; then
+ enable_shared=yes
+ fi
+ done
+ IFS="$lt_save_ifs"
+ ;;
+ esac],
+ [enable_shared=]_LT_ENABLE_SHARED_DEFAULT)
+
+ _LT_DECL([build_libtool_libs], [enable_shared], [0],
+ [Whether or not to build shared libraries])
+])# _LT_ENABLE_SHARED
+
+LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])])
+
+# Old names:
+AC_DEFUN([AC_ENABLE_SHARED],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared])
+])
+
+AC_DEFUN([AC_DISABLE_SHARED],
+[_LT_SET_OPTION([LT_INIT], [disable-shared])
+])
+
+AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)])
+AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_ENABLE_SHARED], [])
+dnl AC_DEFUN([AM_DISABLE_SHARED], [])
+
+
+
+# _LT_ENABLE_STATIC([DEFAULT])
+# ----------------------------
+# implement the --enable-static flag, and support the `static' and
+# `disable-static' LT_INIT options.
+# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'.
+m4_define([_LT_ENABLE_STATIC],
+[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([static],
+ [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@],
+ [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])],
+ [p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_static=yes ;;
+ no) enable_static=no ;;
+ *)
+ enable_static=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ for pkg in $enableval; do
+ IFS="$lt_save_ifs"
+ if test "X$pkg" = "X$p"; then
+ enable_static=yes
+ fi
+ done
+ IFS="$lt_save_ifs"
+ ;;
+ esac],
+ [enable_static=]_LT_ENABLE_STATIC_DEFAULT)
+
+ _LT_DECL([build_old_libs], [enable_static], [0],
+ [Whether or not to build static libraries])
+])# _LT_ENABLE_STATIC
+
+LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])])
+
+# Old names:
+AC_DEFUN([AC_ENABLE_STATIC],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static])
+])
+
+AC_DEFUN([AC_DISABLE_STATIC],
+[_LT_SET_OPTION([LT_INIT], [disable-static])
+])
+
+AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)])
+AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_ENABLE_STATIC], [])
+dnl AC_DEFUN([AM_DISABLE_STATIC], [])
+
+
+
+# _LT_ENABLE_FAST_INSTALL([DEFAULT])
+# ----------------------------------
+# implement the --enable-fast-install flag, and support the `fast-install'
+# and `disable-fast-install' LT_INIT options.
+# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'.
+m4_define([_LT_ENABLE_FAST_INSTALL],
+[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([fast-install],
+ [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@],
+ [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])],
+ [p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_fast_install=yes ;;
+ no) enable_fast_install=no ;;
+ *)
+ enable_fast_install=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ for pkg in $enableval; do
+ IFS="$lt_save_ifs"
+ if test "X$pkg" = "X$p"; then
+ enable_fast_install=yes
+ fi
+ done
+ IFS="$lt_save_ifs"
+ ;;
+ esac],
+ [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT)
+
+_LT_DECL([fast_install], [enable_fast_install], [0],
+ [Whether or not to optimize for fast installation])dnl
+])# _LT_ENABLE_FAST_INSTALL
+
+LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])])
+
+# Old names:
+AU_DEFUN([AC_ENABLE_FAST_INSTALL],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you put
+the `fast-install' option into LT_INIT's first parameter.])
+])
+
+AU_DEFUN([AC_DISABLE_FAST_INSTALL],
+[_LT_SET_OPTION([LT_INIT], [disable-fast-install])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you put
+the `disable-fast-install' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], [])
+dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], [])
+
+
+# _LT_WITH_PIC([MODE])
+# --------------------
+# implement the --with-pic flag, and support the `pic-only' and `no-pic'
+# LT_INIT options.
+# MODE is either `yes' or `no'. If omitted, it defaults to `both'.
+m4_define([_LT_WITH_PIC],
+[AC_ARG_WITH([pic],
+ [AS_HELP_STRING([--with-pic],
+ [try to use only PIC/non-PIC objects @<:@default=use both@:>@])],
+ [pic_mode="$withval"],
+ [pic_mode=default])
+
+test -z "$pic_mode" && pic_mode=m4_default([$1], [default])
+
+_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl
+])# _LT_WITH_PIC
+
+LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])])
+LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])])
+
+# Old name:
+AU_DEFUN([AC_LIBTOOL_PICMODE],
+[_LT_SET_OPTION([LT_INIT], [pic-only])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the `pic-only' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_PICMODE], [])
+
+## ----------------- ##
+## LTDL_INIT Options ##
+## ----------------- ##
+
+m4_define([_LTDL_MODE], [])
+LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive],
+ [m4_define([_LTDL_MODE], [nonrecursive])])
+LT_OPTION_DEFINE([LTDL_INIT], [recursive],
+ [m4_define([_LTDL_MODE], [recursive])])
+LT_OPTION_DEFINE([LTDL_INIT], [subproject],
+ [m4_define([_LTDL_MODE], [subproject])])
+
+m4_define([_LTDL_TYPE], [])
+LT_OPTION_DEFINE([LTDL_INIT], [installable],
+ [m4_define([_LTDL_TYPE], [installable])])
+LT_OPTION_DEFINE([LTDL_INIT], [convenience],
+ [m4_define([_LTDL_TYPE], [convenience])])
diff --git a/m4/ltsugar.m4 b/m4/ltsugar.m4
new file mode 100644
index 00000000..9000a057
--- /dev/null
+++ b/m4/ltsugar.m4
@@ -0,0 +1,123 @@
+# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*-
+#
+# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
+# Written by Gary V. Vaughan, 2004
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# serial 6 ltsugar.m4
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])])
+
+
+# lt_join(SEP, ARG1, [ARG2...])
+# -----------------------------
+# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their
+# associated separator.
+# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier
+# versions in m4sugar had bugs.
+m4_define([lt_join],
+[m4_if([$#], [1], [],
+ [$#], [2], [[$2]],
+ [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])])
+m4_define([_lt_join],
+[m4_if([$#$2], [2], [],
+ [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])])
+
+
+# lt_car(LIST)
+# lt_cdr(LIST)
+# ------------
+# Manipulate m4 lists.
+# These macros are necessary as long as will still need to support
+# Autoconf-2.59 which quotes differently.
+m4_define([lt_car], [[$1]])
+m4_define([lt_cdr],
+[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])],
+ [$#], 1, [],
+ [m4_dquote(m4_shift($@))])])
+m4_define([lt_unquote], $1)
+
+
+# lt_append(MACRO-NAME, STRING, [SEPARATOR])
+# ------------------------------------------
+# Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'.
+# Note that neither SEPARATOR nor STRING are expanded; they are appended
+# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked).
+# No SEPARATOR is output if MACRO-NAME was previously undefined (different
+# than defined and empty).
+#
+# This macro is needed until we can rely on Autoconf 2.62, since earlier
+# versions of m4sugar mistakenly expanded SEPARATOR but not STRING.
+m4_define([lt_append],
+[m4_define([$1],
+ m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])])
+
+
+
+# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...])
+# ----------------------------------------------------------
+# Produce a SEP delimited list of all paired combinations of elements of
+# PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list
+# has the form PREFIXmINFIXSUFFIXn.
+# Needed until we can rely on m4_combine added in Autoconf 2.62.
+m4_define([lt_combine],
+[m4_if(m4_eval([$# > 3]), [1],
+ [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl
+[[m4_foreach([_Lt_prefix], [$2],
+ [m4_foreach([_Lt_suffix],
+ ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[,
+ [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])])
+
+
+# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ])
+# -----------------------------------------------------------------------
+# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited
+# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ.
+m4_define([lt_if_append_uniq],
+[m4_ifdef([$1],
+ [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1],
+ [lt_append([$1], [$2], [$3])$4],
+ [$5])],
+ [lt_append([$1], [$2], [$3])$4])])
+
+
+# lt_dict_add(DICT, KEY, VALUE)
+# -----------------------------
+m4_define([lt_dict_add],
+[m4_define([$1($2)], [$3])])
+
+
+# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE)
+# --------------------------------------------
+m4_define([lt_dict_add_subkey],
+[m4_define([$1($2:$3)], [$4])])
+
+
+# lt_dict_fetch(DICT, KEY, [SUBKEY])
+# ----------------------------------
+m4_define([lt_dict_fetch],
+[m4_ifval([$3],
+ m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]),
+ m4_ifdef([$1($2)], [m4_defn([$1($2)])]))])
+
+
+# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE])
+# -----------------------------------------------------------------
+m4_define([lt_if_dict_fetch],
+[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4],
+ [$5],
+ [$6])])
+
+
+# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...])
+# --------------------------------------------------------------
+m4_define([lt_dict_filter],
+[m4_if([$5], [], [],
+ [lt_join(m4_quote(m4_default([$4], [[, ]])),
+ lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]),
+ [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl
+])
diff --git a/m4/ltversion.m4 b/m4/ltversion.m4
new file mode 100644
index 00000000..1da0e8c7
--- /dev/null
+++ b/m4/ltversion.m4
@@ -0,0 +1,23 @@
+# ltversion.m4 -- version numbers -*- Autoconf -*-
+#
+# Copyright (C) 2004 Free Software Foundation, Inc.
+# Written by Scott James Remnant, 2004
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# Generated from ltversion.in.
+
+# serial 3018 ltversion.m4
+# This file is part of GNU Libtool
+
+m4_define([LT_PACKAGE_VERSION], [2.2.6b])
+m4_define([LT_PACKAGE_REVISION], [1.3018])
+
+AC_DEFUN([LTVERSION_VERSION],
+[macro_version='2.2.6b'
+macro_revision='1.3018'
+_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?])
+_LT_DECL(, macro_revision, 0)
+])
diff --git a/m4/lt~obsolete.m4 b/m4/lt~obsolete.m4
new file mode 100644
index 00000000..637bb206
--- /dev/null
+++ b/m4/lt~obsolete.m4
@@ -0,0 +1,92 @@
+# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*-
+#
+# Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc.
+# Written by Scott James Remnant, 2004.
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# serial 4 lt~obsolete.m4
+
+# These exist entirely to fool aclocal when bootstrapping libtool.
+#
+# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN)
+# which have later been changed to m4_define as they aren't part of the
+# exported API, or moved to Autoconf or Automake where they belong.
+#
+# The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN
+# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us
+# using a macro with the same name in our local m4/libtool.m4 it'll
+# pull the old libtool.m4 in (it doesn't see our shiny new m4_define
+# and doesn't know about Autoconf macros at all.)
+#
+# So we provide this file, which has a silly filename so it's always
+# included after everything else. This provides aclocal with the
+# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything
+# because those macros already exist, or will be overwritten later.
+# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6.
+#
+# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here.
+# Yes, that means every name once taken will need to remain here until
+# we give up compatibility with versions before 1.7, at which point
+# we need to keep only those names which we still refer to.
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])])
+
+m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])])
+m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])])
+m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])])
+m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])])
+m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])])
+m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])])
+m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])])
+m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])])
+m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])])
+m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])])
+m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])])
+m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])])
+m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])])
+m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])])
+m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])])
+m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])])
+m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])])
+m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])])
+m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])])
+m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])])
+m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])])
+m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])])
+m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])])
+m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])])
+m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])])
+m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])])
+m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])])
+m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])])
+m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])])
+m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])])
+m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])])
+m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])])
+m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])])
+m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])])
+m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])])
+m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])])
+m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])])
+m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])])
+m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])])
+m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])])
+m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])])
+m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])])
+m4_ifndef([AC_LIBTOOL_RC], [AC_DEFUN([AC_LIBTOOL_RC])])
+m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])])
+m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])])
+m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])])
+m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])])
+m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])])
+m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])])
+m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])])
+m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])])
diff --git a/m4/nls.m4 b/m4/nls.m4
new file mode 100644
index 00000000..7967cc2f
--- /dev/null
+++ b/m4/nls.m4
@@ -0,0 +1,31 @@
+# nls.m4 serial 3 (gettext-0.15)
+dnl Copyright (C) 1995-2003, 2005-2006 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+dnl
+dnl This file can can be used in projects which are not available under
+dnl the GNU General Public License or the GNU Library General Public
+dnl License but which still want to provide support for the GNU gettext
+dnl functionality.
+dnl Please note that the actual code of the GNU gettext library is covered
+dnl by the GNU Library General Public License, and the rest of the GNU
+dnl gettext package package is covered by the GNU General Public License.
+dnl They are *not* in the public domain.
+
+dnl Authors:
+dnl Ulrich Drepper <drepper@cygnus.com>, 1995-2000.
+dnl Bruno Haible <haible@clisp.cons.org>, 2000-2003.
+
+AC_PREREQ(2.50)
+
+AC_DEFUN([AM_NLS],
+[
+ AC_MSG_CHECKING([whether NLS is requested])
+ dnl Default is enabled NLS
+ AC_ARG_ENABLE(nls,
+ [ --disable-nls do not use Native Language Support],
+ USE_NLS=$enableval, USE_NLS=yes)
+ AC_MSG_RESULT($USE_NLS)
+ AC_SUBST(USE_NLS)
+])
diff --git a/m4/po.m4 b/m4/po.m4
new file mode 100644
index 00000000..00133ef3
--- /dev/null
+++ b/m4/po.m4
@@ -0,0 +1,428 @@
+# po.m4 serial 13 (gettext-0.15)
+dnl Copyright (C) 1995-2006 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+dnl
+dnl This file can can be used in projects which are not available under
+dnl the GNU General Public License or the GNU Library General Public
+dnl License but which still want to provide support for the GNU gettext
+dnl functionality.
+dnl Please note that the actual code of the GNU gettext library is covered
+dnl by the GNU Library General Public License, and the rest of the GNU
+dnl gettext package package is covered by the GNU General Public License.
+dnl They are *not* in the public domain.
+
+dnl Authors:
+dnl Ulrich Drepper <drepper@cygnus.com>, 1995-2000.
+dnl Bruno Haible <haible@clisp.cons.org>, 2000-2003.
+
+AC_PREREQ(2.50)
+
+dnl Checks for all prerequisites of the po subdirectory.
+AC_DEFUN([AM_PO_SUBDIRS],
+[
+ AC_REQUIRE([AC_PROG_MAKE_SET])dnl
+ AC_REQUIRE([AC_PROG_INSTALL])dnl
+ AC_REQUIRE([AM_PROG_MKDIR_P])dnl defined by automake
+ AC_REQUIRE([AM_NLS])dnl
+
+ dnl Perform the following tests also if --disable-nls has been given,
+ dnl because they are needed for "make dist" to work.
+
+ dnl Search for GNU msgfmt in the PATH.
+ dnl The first test excludes Solaris msgfmt and early GNU msgfmt versions.
+ dnl The second test excludes FreeBSD msgfmt.
+ AM_PATH_PROG_WITH_TEST(MSGFMT, msgfmt,
+ [$ac_dir/$ac_word --statistics /dev/null >&]AS_MESSAGE_LOG_FD[ 2>&1 &&
+ (if $ac_dir/$ac_word --statistics /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi)],
+ :)
+ AC_PATH_PROG(GMSGFMT, gmsgfmt, $MSGFMT)
+
+ dnl Test whether it is GNU msgfmt >= 0.15.
+changequote(,)dnl
+ case `$MSGFMT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in
+ '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) MSGFMT_015=: ;;
+ *) MSGFMT_015=$MSGFMT ;;
+ esac
+changequote([,])dnl
+ AC_SUBST([MSGFMT_015])
+changequote(,)dnl
+ case `$GMSGFMT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in
+ '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) GMSGFMT_015=: ;;
+ *) GMSGFMT_015=$GMSGFMT ;;
+ esac
+changequote([,])dnl
+ AC_SUBST([GMSGFMT_015])
+
+ dnl Search for GNU xgettext 0.12 or newer in the PATH.
+ dnl The first test excludes Solaris xgettext and early GNU xgettext versions.
+ dnl The second test excludes FreeBSD xgettext.
+ AM_PATH_PROG_WITH_TEST(XGETTEXT, xgettext,
+ [$ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= /dev/null >&]AS_MESSAGE_LOG_FD[ 2>&1 &&
+ (if $ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi)],
+ :)
+ dnl Remove leftover from FreeBSD xgettext call.
+ rm -f messages.po
+
+ dnl Test whether it is GNU xgettext >= 0.15.
+changequote(,)dnl
+ case `$XGETTEXT --version | sed 1q | sed -e 's,^[^0-9]*,,'` in
+ '' | 0.[0-9] | 0.[0-9].* | 0.1[0-4] | 0.1[0-4].*) XGETTEXT_015=: ;;
+ *) XGETTEXT_015=$XGETTEXT ;;
+ esac
+changequote([,])dnl
+ AC_SUBST([XGETTEXT_015])
+
+ dnl Search for GNU msgmerge 0.11 or newer in the PATH.
+ AM_PATH_PROG_WITH_TEST(MSGMERGE, msgmerge,
+ [$ac_dir/$ac_word --update -q /dev/null /dev/null >&]AS_MESSAGE_LOG_FD[ 2>&1], :)
+
+ dnl Installation directories.
+ dnl Autoconf >= 2.60 defines localedir. For older versions of autoconf, we
+ dnl have to define it here, so that it can be used in po/Makefile.
+ test -n "$localedir" || localedir='${datadir}/locale'
+ AC_SUBST([localedir])
+
+ AC_CONFIG_COMMANDS([po-directories], [[
+ for ac_file in $CONFIG_FILES; do
+ # Support "outfile[:infile[:infile...]]"
+ case "$ac_file" in
+ *:*) ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ esac
+ # PO directories have a Makefile.in generated from Makefile.in.in.
+ case "$ac_file" in */Makefile.in)
+ # Adjust a relative srcdir.
+ ac_dir=`echo "$ac_file"|sed 's%/[^/][^/]*$%%'`
+ ac_dir_suffix="/`echo "$ac_dir"|sed 's%^\./%%'`"
+ ac_dots=`echo "$ac_dir_suffix"|sed 's%/[^/]*%../%g'`
+ # In autoconf-2.13 it is called $ac_given_srcdir.
+ # In autoconf-2.50 it is called $srcdir.
+ test -n "$ac_given_srcdir" || ac_given_srcdir="$srcdir"
+ case "$ac_given_srcdir" in
+ .) top_srcdir=`echo $ac_dots|sed 's%/$%%'` ;;
+ /*) top_srcdir="$ac_given_srcdir" ;;
+ *) top_srcdir="$ac_dots$ac_given_srcdir" ;;
+ esac
+ # Treat a directory as a PO directory if and only if it has a
+ # POTFILES.in file. This allows packages to have multiple PO
+ # directories under different names or in different locations.
+ if test -f "$ac_given_srcdir/$ac_dir/POTFILES.in"; then
+ rm -f "$ac_dir/POTFILES"
+ test -n "$as_me" && echo "$as_me: creating $ac_dir/POTFILES" || echo "creating $ac_dir/POTFILES"
+ cat "$ac_given_srcdir/$ac_dir/POTFILES.in" | sed -e "/^#/d" -e "/^[ ]*\$/d" -e "s,.*, $top_srcdir/& \\\\," | sed -e "\$s/\(.*\) \\\\/\1/" > "$ac_dir/POTFILES"
+ POMAKEFILEDEPS="POTFILES.in"
+ # ALL_LINGUAS, POFILES, UPDATEPOFILES, DUMMYPOFILES, GMOFILES depend
+ # on $ac_dir but don't depend on user-specified configuration
+ # parameters.
+ if test -f "$ac_given_srcdir/$ac_dir/LINGUAS"; then
+ # The LINGUAS file contains the set of available languages.
+ if test -n "$OBSOLETE_ALL_LINGUAS"; then
+ test -n "$as_me" && echo "$as_me: setting ALL_LINGUAS in configure.in is obsolete" || echo "setting ALL_LINGUAS in configure.in is obsolete"
+ fi
+ ALL_LINGUAS_=`sed -e "/^#/d" -e "s/#.*//" "$ac_given_srcdir/$ac_dir/LINGUAS"`
+ # Hide the ALL_LINGUAS assigment from automake < 1.5.
+ eval 'ALL_LINGUAS''=$ALL_LINGUAS_'
+ POMAKEFILEDEPS="$POMAKEFILEDEPS LINGUAS"
+ else
+ # The set of available languages was given in configure.in.
+ # Hide the ALL_LINGUAS assigment from automake < 1.5.
+ eval 'ALL_LINGUAS''=$OBSOLETE_ALL_LINGUAS'
+ fi
+ # Compute POFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).po)
+ # Compute UPDATEPOFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(lang).po-update)
+ # Compute DUMMYPOFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(lang).nop)
+ # Compute GMOFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).gmo)
+ case "$ac_given_srcdir" in
+ .) srcdirpre= ;;
+ *) srcdirpre='$(srcdir)/' ;;
+ esac
+ POFILES=
+ UPDATEPOFILES=
+ DUMMYPOFILES=
+ GMOFILES=
+ for lang in $ALL_LINGUAS; do
+ POFILES="$POFILES $srcdirpre$lang.po"
+ UPDATEPOFILES="$UPDATEPOFILES $lang.po-update"
+ DUMMYPOFILES="$DUMMYPOFILES $lang.nop"
+ GMOFILES="$GMOFILES $srcdirpre$lang.gmo"
+ done
+ # CATALOGS depends on both $ac_dir and the user's LINGUAS
+ # environment variable.
+ INST_LINGUAS=
+ if test -n "$ALL_LINGUAS"; then
+ for presentlang in $ALL_LINGUAS; do
+ useit=no
+ if test "%UNSET%" != "$LINGUAS"; then
+ desiredlanguages="$LINGUAS"
+ else
+ desiredlanguages="$ALL_LINGUAS"
+ fi
+ for desiredlang in $desiredlanguages; do
+ # Use the presentlang catalog if desiredlang is
+ # a. equal to presentlang, or
+ # b. a variant of presentlang (because in this case,
+ # presentlang can be used as a fallback for messages
+ # which are not translated in the desiredlang catalog).
+ case "$desiredlang" in
+ "$presentlang"*) useit=yes;;
+ esac
+ done
+ if test $useit = yes; then
+ INST_LINGUAS="$INST_LINGUAS $presentlang"
+ fi
+ done
+ fi
+ CATALOGS=
+ if test -n "$INST_LINGUAS"; then
+ for lang in $INST_LINGUAS; do
+ CATALOGS="$CATALOGS $lang.gmo"
+ done
+ fi
+ test -n "$as_me" && echo "$as_me: creating $ac_dir/Makefile" || echo "creating $ac_dir/Makefile"
+ sed -e "/^POTFILES =/r $ac_dir/POTFILES" -e "/^# Makevars/r $ac_given_srcdir/$ac_dir/Makevars" -e "s|@POFILES@|$POFILES|g" -e "s|@UPDATEPOFILES@|$UPDATEPOFILES|g" -e "s|@DUMMYPOFILES@|$DUMMYPOFILES|g" -e "s|@GMOFILES@|$GMOFILES|g" -e "s|@CATALOGS@|$CATALOGS|g" -e "s|@POMAKEFILEDEPS@|$POMAKEFILEDEPS|g" "$ac_dir/Makefile.in" > "$ac_dir/Makefile"
+ for f in "$ac_given_srcdir/$ac_dir"/Rules-*; do
+ if test -f "$f"; then
+ case "$f" in
+ *.orig | *.bak | *~) ;;
+ *) cat "$f" >> "$ac_dir/Makefile" ;;
+ esac
+ fi
+ done
+ fi
+ ;;
+ esac
+ done]],
+ [# Capture the value of obsolete ALL_LINGUAS because we need it to compute
+ # POFILES, UPDATEPOFILES, DUMMYPOFILES, GMOFILES, CATALOGS. But hide it
+ # from automake < 1.5.
+ eval 'OBSOLETE_ALL_LINGUAS''="$ALL_LINGUAS"'
+ # Capture the value of LINGUAS because we need it to compute CATALOGS.
+ LINGUAS="${LINGUAS-%UNSET%}"
+ ])
+])
+
+dnl Postprocesses a Makefile in a directory containing PO files.
+AC_DEFUN([AM_POSTPROCESS_PO_MAKEFILE],
+[
+ # When this code is run, in config.status, two variables have already been
+ # set:
+ # - OBSOLETE_ALL_LINGUAS is the value of LINGUAS set in configure.in,
+ # - LINGUAS is the value of the environment variable LINGUAS at configure
+ # time.
+
+changequote(,)dnl
+ # Adjust a relative srcdir.
+ ac_dir=`echo "$ac_file"|sed 's%/[^/][^/]*$%%'`
+ ac_dir_suffix="/`echo "$ac_dir"|sed 's%^\./%%'`"
+ ac_dots=`echo "$ac_dir_suffix"|sed 's%/[^/]*%../%g'`
+ # In autoconf-2.13 it is called $ac_given_srcdir.
+ # In autoconf-2.50 it is called $srcdir.
+ test -n "$ac_given_srcdir" || ac_given_srcdir="$srcdir"
+ case "$ac_given_srcdir" in
+ .) top_srcdir=`echo $ac_dots|sed 's%/$%%'` ;;
+ /*) top_srcdir="$ac_given_srcdir" ;;
+ *) top_srcdir="$ac_dots$ac_given_srcdir" ;;
+ esac
+
+ # Find a way to echo strings without interpreting backslash.
+ if test "X`(echo '\t') 2>/dev/null`" = 'X\t'; then
+ gt_echo='echo'
+ else
+ if test "X`(printf '%s\n' '\t') 2>/dev/null`" = 'X\t'; then
+ gt_echo='printf %s\n'
+ else
+ echo_func () {
+ cat <<EOT
+$*
+EOT
+ }
+ gt_echo='echo_func'
+ fi
+ fi
+
+ # A sed script that extracts the value of VARIABLE from a Makefile.
+ sed_x_variable='
+# Test if the hold space is empty.
+x
+s/P/P/
+x
+ta
+# Yes it was empty. Look if we have the expected variable definition.
+/^[ ]*VARIABLE[ ]*=/{
+ # Seen the first line of the variable definition.
+ s/^[ ]*VARIABLE[ ]*=//
+ ba
+}
+bd
+:a
+# Here we are processing a line from the variable definition.
+# Remove comment, more precisely replace it with a space.
+s/#.*$/ /
+# See if the line ends in a backslash.
+tb
+:b
+s/\\$//
+# Print the line, without the trailing backslash.
+p
+tc
+# There was no trailing backslash. The end of the variable definition is
+# reached. Clear the hold space.
+s/^.*$//
+x
+bd
+:c
+# A trailing backslash means that the variable definition continues in the
+# next line. Put a nonempty string into the hold space to indicate this.
+s/^.*$/P/
+x
+:d
+'
+changequote([,])dnl
+
+ # Set POTFILES to the value of the Makefile variable POTFILES.
+ sed_x_POTFILES=`$gt_echo "$sed_x_variable" | sed -e '/^ *#/d' -e 's/VARIABLE/POTFILES/g'`
+ POTFILES=`sed -n -e "$sed_x_POTFILES" < "$ac_file"`
+ # Compute POTFILES_DEPS as
+ # $(foreach file, $(POTFILES), $(top_srcdir)/$(file))
+ POTFILES_DEPS=
+ for file in $POTFILES; do
+ POTFILES_DEPS="$POTFILES_DEPS "'$(top_srcdir)/'"$file"
+ done
+ POMAKEFILEDEPS=""
+
+ if test -n "$OBSOLETE_ALL_LINGUAS"; then
+ test -n "$as_me" && echo "$as_me: setting ALL_LINGUAS in configure.in is obsolete" || echo "setting ALL_LINGUAS in configure.in is obsolete"
+ fi
+ if test -f "$ac_given_srcdir/$ac_dir/LINGUAS"; then
+ # The LINGUAS file contains the set of available languages.
+ ALL_LINGUAS_=`sed -e "/^#/d" -e "s/#.*//" "$ac_given_srcdir/$ac_dir/LINGUAS"`
+ POMAKEFILEDEPS="$POMAKEFILEDEPS LINGUAS"
+ else
+ # Set ALL_LINGUAS to the value of the Makefile variable LINGUAS.
+ sed_x_LINGUAS=`$gt_echo "$sed_x_variable" | sed -e '/^ *#/d' -e 's/VARIABLE/LINGUAS/g'`
+ ALL_LINGUAS_=`sed -n -e "$sed_x_LINGUAS" < "$ac_file"`
+ fi
+ # Hide the ALL_LINGUAS assigment from automake < 1.5.
+ eval 'ALL_LINGUAS''=$ALL_LINGUAS_'
+ # Compute POFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).po)
+ # Compute UPDATEPOFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(lang).po-update)
+ # Compute DUMMYPOFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(lang).nop)
+ # Compute GMOFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).gmo)
+ # Compute PROPERTIESFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(top_srcdir)/$(DOMAIN)_$(lang).properties)
+ # Compute CLASSFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(top_srcdir)/$(DOMAIN)_$(lang).class)
+ # Compute QMFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).qm)
+ # Compute MSGFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(frob $(lang)).msg)
+ # Compute RESOURCESDLLFILES
+ # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(frob $(lang))/$(DOMAIN).resources.dll)
+ case "$ac_given_srcdir" in
+ .) srcdirpre= ;;
+ *) srcdirpre='$(srcdir)/' ;;
+ esac
+ POFILES=
+ UPDATEPOFILES=
+ DUMMYPOFILES=
+ GMOFILES=
+ PROPERTIESFILES=
+ CLASSFILES=
+ QMFILES=
+ MSGFILES=
+ RESOURCESDLLFILES=
+ for lang in $ALL_LINGUAS; do
+ POFILES="$POFILES $srcdirpre$lang.po"
+ UPDATEPOFILES="$UPDATEPOFILES $lang.po-update"
+ DUMMYPOFILES="$DUMMYPOFILES $lang.nop"
+ GMOFILES="$GMOFILES $srcdirpre$lang.gmo"
+ PROPERTIESFILES="$PROPERTIESFILES \$(top_srcdir)/\$(DOMAIN)_$lang.properties"
+ CLASSFILES="$CLASSFILES \$(top_srcdir)/\$(DOMAIN)_$lang.class"
+ QMFILES="$QMFILES $srcdirpre$lang.qm"
+ frobbedlang=`echo $lang | sed -e 's/\..*$//' -e 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/'`
+ MSGFILES="$MSGFILES $srcdirpre$frobbedlang.msg"
+ frobbedlang=`echo $lang | sed -e 's/_/-/g' -e 's/^sr-CS/sr-SP/' -e 's/@latin$/-Latn/' -e 's/@cyrillic$/-Cyrl/' -e 's/^sr-SP$/sr-SP-Latn/' -e 's/^uz-UZ$/uz-UZ-Latn/'`
+ RESOURCESDLLFILES="$RESOURCESDLLFILES $srcdirpre$frobbedlang/\$(DOMAIN).resources.dll"
+ done
+ # CATALOGS depends on both $ac_dir and the user's LINGUAS
+ # environment variable.
+ INST_LINGUAS=
+ if test -n "$ALL_LINGUAS"; then
+ for presentlang in $ALL_LINGUAS; do
+ useit=no
+ if test "%UNSET%" != "$LINGUAS"; then
+ desiredlanguages="$LINGUAS"
+ else
+ desiredlanguages="$ALL_LINGUAS"
+ fi
+ for desiredlang in $desiredlanguages; do
+ # Use the presentlang catalog if desiredlang is
+ # a. equal to presentlang, or
+ # b. a variant of presentlang (because in this case,
+ # presentlang can be used as a fallback for messages
+ # which are not translated in the desiredlang catalog).
+ case "$desiredlang" in
+ "$presentlang"*) useit=yes;;
+ esac
+ done
+ if test $useit = yes; then
+ INST_LINGUAS="$INST_LINGUAS $presentlang"
+ fi
+ done
+ fi
+ CATALOGS=
+ JAVACATALOGS=
+ QTCATALOGS=
+ TCLCATALOGS=
+ CSHARPCATALOGS=
+ if test -n "$INST_LINGUAS"; then
+ for lang in $INST_LINGUAS; do
+ CATALOGS="$CATALOGS $lang.gmo"
+ JAVACATALOGS="$JAVACATALOGS \$(DOMAIN)_$lang.properties"
+ QTCATALOGS="$QTCATALOGS $lang.qm"
+ frobbedlang=`echo $lang | sed -e 's/\..*$//' -e 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/'`
+ TCLCATALOGS="$TCLCATALOGS $frobbedlang.msg"
+ frobbedlang=`echo $lang | sed -e 's/_/-/g' -e 's/^sr-CS/sr-SP/' -e 's/@latin$/-Latn/' -e 's/@cyrillic$/-Cyrl/' -e 's/^sr-SP$/sr-SP-Latn/' -e 's/^uz-UZ$/uz-UZ-Latn/'`
+ CSHARPCATALOGS="$CSHARPCATALOGS $frobbedlang/\$(DOMAIN).resources.dll"
+ done
+ fi
+
+ sed -e "s|@POTFILES_DEPS@|$POTFILES_DEPS|g" -e "s|@POFILES@|$POFILES|g" -e "s|@UPDATEPOFILES@|$UPDATEPOFILES|g" -e "s|@DUMMYPOFILES@|$DUMMYPOFILES|g" -e "s|@GMOFILES@|$GMOFILES|g" -e "s|@PROPERTIESFILES@|$PROPERTIESFILES|g" -e "s|@CLASSFILES@|$CLASSFILES|g" -e "s|@QMFILES@|$QMFILES|g" -e "s|@MSGFILES@|$MSGFILES|g" -e "s|@RESOURCESDLLFILES@|$RESOURCESDLLFILES|g" -e "s|@CATALOGS@|$CATALOGS|g" -e "s|@JAVACATALOGS@|$JAVACATALOGS|g" -e "s|@QTCATALOGS@|$QTCATALOGS|g" -e "s|@TCLCATALOGS@|$TCLCATALOGS|g" -e "s|@CSHARPCATALOGS@|$CSHARPCATALOGS|g" -e 's,^#distdir:,distdir:,' < "$ac_file" > "$ac_file.tmp"
+ if grep -l '@TCLCATALOGS@' "$ac_file" > /dev/null; then
+ # Add dependencies that cannot be formulated as a simple suffix rule.
+ for lang in $ALL_LINGUAS; do
+ frobbedlang=`echo $lang | sed -e 's/\..*$//' -e 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/'`
+ cat >> "$ac_file.tmp" <<EOF
+$frobbedlang.msg: $lang.po
+ @echo "\$(MSGFMT) -c --tcl -d \$(srcdir) -l $lang $srcdirpre$lang.po"; \
+ \$(MSGFMT) -c --tcl -d "\$(srcdir)" -l $lang $srcdirpre$lang.po || { rm -f "\$(srcdir)/$frobbedlang.msg"; exit 1; }
+EOF
+ done
+ fi
+ if grep -l '@CSHARPCATALOGS@' "$ac_file" > /dev/null; then
+ # Add dependencies that cannot be formulated as a simple suffix rule.
+ for lang in $ALL_LINGUAS; do
+ frobbedlang=`echo $lang | sed -e 's/_/-/g' -e 's/^sr-CS/sr-SP/' -e 's/@latin$/-Latn/' -e 's/@cyrillic$/-Cyrl/' -e 's/^sr-SP$/sr-SP-Latn/' -e 's/^uz-UZ$/uz-UZ-Latn/'`
+ cat >> "$ac_file.tmp" <<EOF
+$frobbedlang/\$(DOMAIN).resources.dll: $lang.po
+ @echo "\$(MSGFMT) -c --csharp -d \$(srcdir) -l $lang $srcdirpre$lang.po -r \$(DOMAIN)"; \
+ \$(MSGFMT) -c --csharp -d "\$(srcdir)" -l $lang $srcdirpre$lang.po -r "\$(DOMAIN)" || { rm -f "\$(srcdir)/$frobbedlang.msg"; exit 1; }
+EOF
+ done
+ fi
+ if test -n "$POMAKEFILEDEPS"; then
+ cat >> "$ac_file.tmp" <<EOF
+Makefile: $POMAKEFILEDEPS
+EOF
+ fi
+ mv "$ac_file.tmp" "$ac_file"
+])
diff --git a/m4/printf-posix.m4 b/m4/printf-posix.m4
new file mode 100644
index 00000000..af10170a
--- /dev/null
+++ b/m4/printf-posix.m4
@@ -0,0 +1,44 @@
+# printf-posix.m4 serial 2 (gettext-0.13.1)
+dnl Copyright (C) 2003 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Bruno Haible.
+dnl Test whether the printf() function supports POSIX/XSI format strings with
+dnl positions.
+
+AC_DEFUN([gt_PRINTF_POSIX],
+[
+ AC_REQUIRE([AC_PROG_CC])
+ AC_CACHE_CHECK([whether printf() supports POSIX/XSI format strings],
+ gt_cv_func_printf_posix,
+ [
+ AC_TRY_RUN([
+#include <stdio.h>
+#include <string.h>
+/* The string "%2$d %1$d", with dollar characters protected from the shell's
+ dollar expansion (possibly an autoconf bug). */
+static char format[] = { '%', '2', '$', 'd', ' ', '%', '1', '$', 'd', '\0' };
+static char buf[100];
+int main ()
+{
+ sprintf (buf, format, 33, 55);
+ return (strcmp (buf, "55 33") != 0);
+}], gt_cv_func_printf_posix=yes, gt_cv_func_printf_posix=no,
+ [
+ AC_EGREP_CPP(notposix, [
+#if defined __NetBSD__ || defined _MSC_VER || defined __MINGW32__ || defined __CYGWIN__
+ notposix
+#endif
+ ], gt_cv_func_printf_posix="guessing no",
+ gt_cv_func_printf_posix="guessing yes")
+ ])
+ ])
+ case $gt_cv_func_printf_posix in
+ *yes)
+ AC_DEFINE(HAVE_POSIX_PRINTF, 1,
+ [Define if your printf() function supports format strings with positions.])
+ ;;
+ esac
+])
diff --git a/m4/progtest.m4 b/m4/progtest.m4
new file mode 100644
index 00000000..a56365cd
--- /dev/null
+++ b/m4/progtest.m4
@@ -0,0 +1,92 @@
+# progtest.m4 serial 4 (gettext-0.14.2)
+dnl Copyright (C) 1996-2003, 2005 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+dnl
+dnl This file can can be used in projects which are not available under
+dnl the GNU General Public License or the GNU Library General Public
+dnl License but which still want to provide support for the GNU gettext
+dnl functionality.
+dnl Please note that the actual code of the GNU gettext library is covered
+dnl by the GNU Library General Public License, and the rest of the GNU
+dnl gettext package package is covered by the GNU General Public License.
+dnl They are *not* in the public domain.
+
+dnl Authors:
+dnl Ulrich Drepper <drepper@cygnus.com>, 1996.
+
+AC_PREREQ(2.50)
+
+# Search path for a program which passes the given test.
+
+dnl AM_PATH_PROG_WITH_TEST(VARIABLE, PROG-TO-CHECK-FOR,
+dnl TEST-PERFORMED-ON-FOUND_PROGRAM [, VALUE-IF-NOT-FOUND [, PATH]])
+AC_DEFUN([AM_PATH_PROG_WITH_TEST],
+[
+# Prepare PATH_SEPARATOR.
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+# Find out how to test for executable files. Don't use a zero-byte file,
+# as systems may use methods other than mode bits to determine executability.
+cat >conf$$.file <<_ASEOF
+#! /bin/sh
+exit 0
+_ASEOF
+chmod +x conf$$.file
+if test -x conf$$.file >/dev/null 2>&1; then
+ ac_executable_p="test -x"
+else
+ ac_executable_p="test -f"
+fi
+rm -f conf$$.file
+
+# Extract the first word of "$2", so it can be a program name with args.
+set dummy $2; ac_word=[$]2
+AC_MSG_CHECKING([for $ac_word])
+AC_CACHE_VAL(ac_cv_path_$1,
+[case "[$]$1" in
+ [[\\/]]* | ?:[[\\/]]*)
+ ac_cv_path_$1="[$]$1" # Let the user override the test with a path.
+ ;;
+ *)
+ ac_save_IFS="$IFS"; IFS=$PATH_SEPARATOR
+ for ac_dir in ifelse([$5], , $PATH, [$5]); do
+ IFS="$ac_save_IFS"
+ test -z "$ac_dir" && ac_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $ac_executable_p "$ac_dir/$ac_word$ac_exec_ext"; then
+ echo "$as_me: trying $ac_dir/$ac_word..." >&AS_MESSAGE_LOG_FD
+ if [$3]; then
+ ac_cv_path_$1="$ac_dir/$ac_word$ac_exec_ext"
+ break 2
+ fi
+ fi
+ done
+ done
+ IFS="$ac_save_IFS"
+dnl If no 4th arg is given, leave the cache variable unset,
+dnl so AC_PATH_PROGS will keep looking.
+ifelse([$4], , , [ test -z "[$]ac_cv_path_$1" && ac_cv_path_$1="$4"
+])dnl
+ ;;
+esac])dnl
+$1="$ac_cv_path_$1"
+if test ifelse([$4], , [-n "[$]$1"], ["[$]$1" != "$4"]); then
+ AC_MSG_RESULT([$]$1)
+else
+ AC_MSG_RESULT(no)
+fi
+AC_SUBST($1)dnl
+])
diff --git a/m4/size_max.m4 b/m4/size_max.m4
new file mode 100644
index 00000000..bfba811e
--- /dev/null
+++ b/m4/size_max.m4
@@ -0,0 +1,62 @@
+# size_max.m4 serial 5
+dnl Copyright (C) 2003, 2005-2006 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Bruno Haible.
+
+AC_DEFUN([gl_SIZE_MAX],
+[
+ AC_CHECK_HEADERS(stdint.h)
+ dnl First test whether the system already has SIZE_MAX.
+ AC_MSG_CHECKING([for SIZE_MAX])
+ AC_CACHE_VAL([gl_cv_size_max], [
+ gl_cv_size_max=
+ AC_EGREP_CPP([Found it], [
+#include <limits.h>
+#if HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#ifdef SIZE_MAX
+Found it
+#endif
+], gl_cv_size_max=yes)
+ if test -z "$gl_cv_size_max"; then
+ dnl Define it ourselves. Here we assume that the type 'size_t' is not wider
+ dnl than the type 'unsigned long'. Try hard to find a definition that can
+ dnl be used in a preprocessor #if, i.e. doesn't contain a cast.
+ _AC_COMPUTE_INT([sizeof (size_t) * CHAR_BIT - 1], size_t_bits_minus_1,
+ [#include <stddef.h>
+#include <limits.h>], size_t_bits_minus_1=)
+ _AC_COMPUTE_INT([sizeof (size_t) <= sizeof (unsigned int)], fits_in_uint,
+ [#include <stddef.h>], fits_in_uint=)
+ if test -n "$size_t_bits_minus_1" && test -n "$fits_in_uint"; then
+ if test $fits_in_uint = 1; then
+ dnl Even though SIZE_MAX fits in an unsigned int, it must be of type
+ dnl 'unsigned long' if the type 'size_t' is the same as 'unsigned long'.
+ AC_TRY_COMPILE([#include <stddef.h>
+ extern size_t foo;
+ extern unsigned long foo;
+ ], [], fits_in_uint=0)
+ fi
+ dnl We cannot use 'expr' to simplify this expression, because 'expr'
+ dnl works only with 'long' integers in the host environment, while we
+ dnl might be cross-compiling from a 32-bit platform to a 64-bit platform.
+ if test $fits_in_uint = 1; then
+ gl_cv_size_max="(((1U << $size_t_bits_minus_1) - 1) * 2 + 1)"
+ else
+ gl_cv_size_max="(((1UL << $size_t_bits_minus_1) - 1) * 2 + 1)"
+ fi
+ else
+ dnl Shouldn't happen, but who knows...
+ gl_cv_size_max='((size_t)~(size_t)0)'
+ fi
+ fi
+ ])
+ AC_MSG_RESULT([$gl_cv_size_max])
+ if test "$gl_cv_size_max" != yes; then
+ AC_DEFINE_UNQUOTED([SIZE_MAX], [$gl_cv_size_max],
+ [Define as the maximum value of type 'size_t', if the system doesn't define it.])
+ fi
+])
diff --git a/m4/stdint_h.m4 b/m4/stdint_h.m4
new file mode 100644
index 00000000..db9a8ac4
--- /dev/null
+++ b/m4/stdint_h.m4
@@ -0,0 +1,26 @@
+# stdint_h.m4 serial 6
+dnl Copyright (C) 1997-2004, 2006 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Paul Eggert.
+
+# Define HAVE_STDINT_H_WITH_UINTMAX if <stdint.h> exists,
+# doesn't clash with <sys/types.h>, and declares uintmax_t.
+
+AC_DEFUN([gl_AC_HEADER_STDINT_H],
+[
+ AC_CACHE_CHECK([for stdint.h], gl_cv_header_stdint_h,
+ [AC_TRY_COMPILE(
+ [#include <sys/types.h>
+#include <stdint.h>],
+ [uintmax_t i = (uintmax_t) -1; return !i;],
+ gl_cv_header_stdint_h=yes,
+ gl_cv_header_stdint_h=no)])
+ if test $gl_cv_header_stdint_h = yes; then
+ AC_DEFINE_UNQUOTED(HAVE_STDINT_H_WITH_UINTMAX, 1,
+ [Define if <stdint.h> exists, doesn't clash with <sys/types.h>,
+ and declares uintmax_t. ])
+ fi
+])
diff --git a/m4/uintmax_t.m4 b/m4/uintmax_t.m4
new file mode 100644
index 00000000..bf83ed74
--- /dev/null
+++ b/m4/uintmax_t.m4
@@ -0,0 +1,30 @@
+# uintmax_t.m4 serial 9
+dnl Copyright (C) 1997-2004 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Paul Eggert.
+
+AC_PREREQ(2.13)
+
+# Define uintmax_t to 'unsigned long' or 'unsigned long long'
+# if it is not already defined in <stdint.h> or <inttypes.h>.
+
+AC_DEFUN([gl_AC_TYPE_UINTMAX_T],
+[
+ AC_REQUIRE([gl_AC_HEADER_INTTYPES_H])
+ AC_REQUIRE([gl_AC_HEADER_STDINT_H])
+ if test $gl_cv_header_inttypes_h = no && test $gl_cv_header_stdint_h = no; then
+ AC_REQUIRE([gl_AC_TYPE_UNSIGNED_LONG_LONG])
+ test $ac_cv_type_unsigned_long_long = yes \
+ && ac_type='unsigned long long' \
+ || ac_type='unsigned long'
+ AC_DEFINE_UNQUOTED(uintmax_t, $ac_type,
+ [Define to unsigned long or unsigned long long
+ if <stdint.h> and <inttypes.h> don't define.])
+ else
+ AC_DEFINE(HAVE_UINTMAX_T, 1,
+ [Define if you have the 'uintmax_t' type in <stdint.h> or <inttypes.h>.])
+ fi
+])
diff --git a/m4/ulonglong.m4 b/m4/ulonglong.m4
new file mode 100644
index 00000000..9fae98e3
--- /dev/null
+++ b/m4/ulonglong.m4
@@ -0,0 +1,48 @@
+# ulonglong.m4 serial 6
+dnl Copyright (C) 1999-2006 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Paul Eggert.
+
+# Define HAVE_UNSIGNED_LONG_LONG_INT if 'unsigned long long int' works.
+# This fixes a bug in Autoconf 2.60, but can be removed once we
+# assume 2.61 everywhere.
+
+# Note: If the type 'unsigned long long int' exists but is only 32 bits
+# large (as on some very old compilers), AC_TYPE_UNSIGNED_LONG_LONG_INT
+# will not be defined. In this case you can treat 'unsigned long long int'
+# like 'unsigned long int'.
+
+AC_DEFUN([AC_TYPE_UNSIGNED_LONG_LONG_INT],
+[
+ AC_CACHE_CHECK([for unsigned long long int],
+ [ac_cv_type_unsigned_long_long_int],
+ [AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[unsigned long long int ull = 18446744073709551615ULL;
+ typedef int a[(18446744073709551615ULL <= (unsigned long long int) -1
+ ? 1 : -1)];
+ int i = 63;]],
+ [[unsigned long long int ullmax = 18446744073709551615ull;
+ return (ull << 63 | ull >> 63 | ull << i | ull >> i
+ | ullmax / ull | ullmax % ull);]])],
+ [ac_cv_type_unsigned_long_long_int=yes],
+ [ac_cv_type_unsigned_long_long_int=no])])
+ if test $ac_cv_type_unsigned_long_long_int = yes; then
+ AC_DEFINE([HAVE_UNSIGNED_LONG_LONG_INT], 1,
+ [Define to 1 if the system has the type `unsigned long long int'.])
+ fi
+])
+
+# This macro is obsolescent and should go away soon.
+AC_DEFUN([gl_AC_TYPE_UNSIGNED_LONG_LONG],
+[
+ AC_REQUIRE([AC_TYPE_UNSIGNED_LONG_LONG_INT])
+ ac_cv_type_unsigned_long_long=$ac_cv_type_unsigned_long_long_int
+ if test $ac_cv_type_unsigned_long_long = yes; then
+ AC_DEFINE(HAVE_UNSIGNED_LONG_LONG, 1,
+ [Define if you have the 'unsigned long long' type.])
+ fi
+])
diff --git a/m4/visibility.m4 b/m4/visibility.m4
new file mode 100644
index 00000000..2ff6330a
--- /dev/null
+++ b/m4/visibility.m4
@@ -0,0 +1,52 @@
+# visibility.m4 serial 1 (gettext-0.15)
+dnl Copyright (C) 2005 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Bruno Haible.
+
+dnl Tests whether the compiler supports the command-line option
+dnl -fvisibility=hidden and the function and variable attributes
+dnl __attribute__((__visibility__("hidden"))) and
+dnl __attribute__((__visibility__("default"))).
+dnl Does *not* test for __visibility__("protected") - which has tricky
+dnl semantics (see the 'vismain' test in glibc) and does not exist e.g. on
+dnl MacOS X.
+dnl Does *not* test for __visibility__("internal") - which has processor
+dnl dependent semantics.
+dnl Does *not* test for #pragma GCC visibility push(hidden) - which is
+dnl "really only recommended for legacy code".
+dnl Set the variable CFLAG_VISIBILITY.
+dnl Defines and sets the variable HAVE_VISIBILITY.
+
+AC_DEFUN([gl_VISIBILITY],
+[
+ AC_REQUIRE([AC_PROG_CC])
+ CFLAG_VISIBILITY=
+ HAVE_VISIBILITY=0
+ if test -n "$GCC"; then
+ AC_MSG_CHECKING([for simple visibility declarations])
+ AC_CACHE_VAL(gl_cv_cc_visibility, [
+ gl_save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS -fvisibility=hidden"
+ AC_TRY_COMPILE(
+ [extern __attribute__((__visibility__("hidden"))) int hiddenvar;
+ extern __attribute__((__visibility__("default"))) int exportedvar;
+ extern __attribute__((__visibility__("hidden"))) int hiddenfunc (void);
+ extern __attribute__((__visibility__("default"))) int exportedfunc (void);],
+ [],
+ gl_cv_cc_visibility=yes,
+ gl_cv_cc_visibility=no)
+ CFLAGS="$gl_save_CFLAGS"])
+ AC_MSG_RESULT([$gl_cv_cc_visibility])
+ if test $gl_cv_cc_visibility = yes; then
+ CFLAG_VISIBILITY="-fvisibility=hidden"
+ HAVE_VISIBILITY=1
+ fi
+ fi
+ AC_SUBST([CFLAG_VISIBILITY])
+ AC_SUBST([HAVE_VISIBILITY])
+ AC_DEFINE_UNQUOTED([HAVE_VISIBILITY], [$HAVE_VISIBILITY],
+ [Define to 1 or 0, depending whether the compiler supports simple visibility declarations.])
+])
diff --git a/m4/wchar_t.m4 b/m4/wchar_t.m4
new file mode 100644
index 00000000..cde2129a
--- /dev/null
+++ b/m4/wchar_t.m4
@@ -0,0 +1,20 @@
+# wchar_t.m4 serial 1 (gettext-0.12)
+dnl Copyright (C) 2002-2003 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Bruno Haible.
+dnl Test whether <stddef.h> has the 'wchar_t' type.
+dnl Prerequisite: AC_PROG_CC
+
+AC_DEFUN([gt_TYPE_WCHAR_T],
+[
+ AC_CACHE_CHECK([for wchar_t], gt_cv_c_wchar_t,
+ [AC_TRY_COMPILE([#include <stddef.h>
+ wchar_t foo = (wchar_t)'\0';], ,
+ gt_cv_c_wchar_t=yes, gt_cv_c_wchar_t=no)])
+ if test $gt_cv_c_wchar_t = yes; then
+ AC_DEFINE(HAVE_WCHAR_T, 1, [Define if you have the 'wchar_t' type.])
+ fi
+])
diff --git a/m4/wint_t.m4 b/m4/wint_t.m4
new file mode 100644
index 00000000..b8fff9c8
--- /dev/null
+++ b/m4/wint_t.m4
@@ -0,0 +1,20 @@
+# wint_t.m4 serial 1 (gettext-0.12)
+dnl Copyright (C) 2003 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Bruno Haible.
+dnl Test whether <wchar.h> has the 'wint_t' type.
+dnl Prerequisite: AC_PROG_CC
+
+AC_DEFUN([gt_TYPE_WINT_T],
+[
+ AC_CACHE_CHECK([for wint_t], gt_cv_c_wint_t,
+ [AC_TRY_COMPILE([#include <wchar.h>
+ wint_t foo = (wchar_t)'\0';], ,
+ gt_cv_c_wint_t=yes, gt_cv_c_wint_t=no)])
+ if test $gt_cv_c_wint_t = yes; then
+ AC_DEFINE(HAVE_WINT_T, 1, [Define if you have the 'wint_t' type.])
+ fi
+])
diff --git a/m4/xsize.m4 b/m4/xsize.m4
new file mode 100644
index 00000000..85bb721e
--- /dev/null
+++ b/m4/xsize.m4
@@ -0,0 +1,13 @@
+# xsize.m4 serial 3
+dnl Copyright (C) 2003-2004 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_XSIZE],
+[
+ dnl Prerequisites of lib/xsize.h.
+ AC_REQUIRE([gl_SIZE_MAX])
+ AC_REQUIRE([AC_C_INLINE])
+ AC_CHECK_HEADERS(stdint.h)
+])
diff --git a/mapi/ReadmeMapi32.txt b/mapi/ReadmeMapi32.txt
new file mode 100644
index 00000000..c19e7d43
--- /dev/null
+++ b/mapi/ReadmeMapi32.txt
@@ -0,0 +1,102 @@
+ReadmeMapi32.txt
+January 27, 2009
+University of Washington
+
+NOTE: It is uncertain what remains to be done for pmapi to work with
+alpine. This dll should work with a PC-Pine installation, but in
+the future will be made to work with Alpine. Registry access and
+Unicode support will have to change for that to happen.
+
+ pmapi32 for Alpine 2.10
+
+This distribution comes with two other files, instmapi.exe and
+pmapi32.dll, for use with Aline. It is recommended that these files
+be put in your pine directory (generally C:\Program Files\Alpine).
+
+pmapi32.dll - a Simple MAPI dynamically linked library. pmapi32.dll
+is used by other applications to access your inbox or send mail
+through Alpine.
+
+instmapi.exe - installs mapi32.dll such that registry values are set
+according to the location of pmapi32.dll, and possibly copies
+pmapi32.dll to the system directory if it is run on older systems. It
+will also offer to set Alpine as your default mailer and newsreader
+if it is not already set. The "-silent" option has been added to
+assume that default mailer and newsreader should be set and also to
+suppress a "Successful Completion" alert. This program can be run
+repeatedly without side effects.
+
+instmapi.exe must be run at least once in order for pmapi32.dll to be
+accessible to other applications. As of Alpine 4.30, instmapi.exe
+may or may not need to be run in order for pmapi32.dll to work,
+depending on Windows/Internet Explorer/Microsoft Office versions. To
+manually set Alpine as your default mailer, you must open "Internet
+Options" in your Control Panel and select the "Programs" tab. Under
+"E-mail", select Alpine.
+
+Configuration:
+Currently there is only one configuration option that is specific to
+pmapi32.dll:
+
+pmapi-send-behavior: With the ability to send messages without dialogs
+it was decided that the user might want to be prompted in this case.
+There are three possible values for this variable:
+ always-prompt: Always prompt before sending. This is the default
+ behavior unless set otherwise.
+ always-send: Always send the message without prompting.
+ never-send: Never send the message
+
+pmapi-suppress-dialogs: MAPI Clients may tell pmapi32.dll that they
+would like some dialogs allowing the user to edit the message before
+sending. Since Alpine is not yet designed to properly handle this
+(nor will it be unless there is sufficient demand for it), it may be
+desired to forego the Alpine interaction if the message contains
+enough information for it to be set. Possible values are:
+ no: Never suppress dialogs, this is the default behavior
+ yes: Always try sending it as is. You should be aware of the
+ implications before setting this
+ prompt: Prompt whether or not it should fire up PC-Pine to edit
+ the message
+
+pmapi-strict-no-dialog: Some MAPI clients will request pmapi32 not to
+display any dialogs, even when it is absolutely necessary that a
+dialog be displayed for the purpose of logging in. By default,
+pmapi32 will just display the dialog, but this variable is available
+for those who require a strict adherence of this request. The two
+possible values for this variable are:
+ no: Show login dialogs when necessary, which is the default
+ behavior
+ yes: Suppress dialogs when it is requested by the client. It
+ should generally be safe to set this, as it more accurately
+ reflects the specification, but it is possible that setting
+ this will hide necessary authentication prompts.
+
+
+To change the default setting of these variables, you will need to
+either hand edit the pinerc or set a value in the Windows registry.
+The pinerc setting always takes precedence over the registry setting.
+An example of setting the value pmapi-send-behavior to "always-send"
+in the pinerc would be to add the line:
+pmapi-send-behavior=always-send
+To add it to the registry, you would need to make sure the following
+key exists (from the regedit program):
+ HKEY_CURRENT_USER\Software\University of Washington\PC-Pine\4.0\PmapiOpts
+Then, you would add a string named "pmapi-send-behavior" with the
+value of "always-send"
+
+Environment Variables:
+Some sites may take advantage of environment variables in their
+configurations, setting them via some program that gets executed
+before running PC-Pine. There is a way to mimmick this behavior in
+MAPI. First you would need to create the following key:
+ HKEY_CURRENT_USER\Software\University of Washington\Alpine\1.0\PmapiOpts\Env
+Say there is a variable that contains the string "${LOGNAME}". To set
+this for MAPI, you would add a string "LOGNAME" to the above key, and
+have its value point to whatever the environment variable %LOGNAME%
+would point to.
+
+Troubleshooting: To view debugging information, create a file called
+mapi_debug.txt in your system's %TEMP% directory. Bug reports or
+comments should be sent to pine@cac.washington.edu.
+
+Jeff Franklin <jpf@washington.edu>
diff --git a/mapi/instmapi.c b/mapi/instmapi.c
new file mode 100644
index 00000000..ab79f2f4
--- /dev/null
+++ b/mapi/instmapi.c
@@ -0,0 +1,406 @@
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/* installmapi.c : installs the Alpine version of mapi32.dll
+ * into the directory from which this program is run. The PC-Pine
+ * version is pmapi32.dll, and must be in the same directory as the
+ * program being run. Note that we will run into trouble if we want
+ * to name our dll mapi32.dll, unless we're putting it in the system
+ * directory
+ */
+
+#include <windows.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <io.h>
+#include <string.h>
+#include <direct.h>
+
+#define FIXREG 1
+
+/* this program consists of two parts:
+ * (1) Updating the registry,
+ * most particulary the string of DLLPath in the key
+ * HKLM\Software\Clients\PC-Pine.
+ * (2) Deciding whether or not to copy pmapi32.dll into the system
+ * directory. Starting with Windows 2000, Internet Explorer 5.0
+ * or Outlook 2000, mapi32.dll started to be a stub library, loading
+ * whichever mapi32.dll was defined in the registry (see (1)). We
+ * don't want to overwrite mapi32.dll if it is the stub library, but
+ * if the stub library isn't installed, then we want to install our
+ * own version of mapi32.dll. Since the method for detecting the
+ * stub library isn't very well documented (as of Feb 17, 2000), we
+ * should use a liberal policy of whether or not to copy our mapi
+ * into the system directory. Here are the criteria that determine
+ * that the stub library is correctly installed:
+ * i) Existence of mapistub.dll in the system directory
+ * ii) Existence of the exported function FixMAPI() in mapi32.dll
+ * If either of these two conditions fail, then we'll copy into the
+ * system directory (provided we have correct permissions for it).
+ *
+ * Jeff Franklin
+ * University of Washington
+ */
+
+int check_defaults(int, int);
+int check_url_commands(int);
+
+#define CD_MAILER 1
+#define CD_NEWS 2
+
+#define CUC_MAILTO 1
+#define CUC_NEWS 2
+#define CUC_NNTP 3
+
+int APIENTRY WinMain(HINSTANCE hInstance,
+ HINSTANCE hPrevInstance,
+ LPSTR lpCmdLine,
+ int nCmdShow)
+{
+ char dir[1000], filename[1024], mapifile[1024],
+ buffer[3*1024], buffer2[3*1024];
+ unsigned long bufflen = 3*1024;
+ DWORD dtype;
+ int copy = 0, success = 0, silent = 0;
+ FARPROC proc;
+ HKEY hKey;
+ HINSTANCE hDll;
+ LPSTR p, pp;
+
+ for(pp = p = lpCmdLine; *(p-1); p++){
+ if(*p == ' ' || *p == '\t' || *p == '\0'){
+ if(strncmp("-silent", pp, strlen("-silent")) == 0)
+ silent = 1;
+ if(!*p)
+ break;
+ pp = p + 1;
+ }
+ }
+
+
+ /* This is the first part of writing the registry with our values */
+ _getcwd(dir, 1000);
+ sprintf(filename, "%s%s", dir, dir[strlen(dir)-1] == '\\' ?
+ "pmapi32.dll": "\\pmapi32.dll");
+ if(_access(filename, 00) == -1){
+ sprintf(buffer,
+ "pmapi32.dll for PC-Pine not found in %s. Install not completed.",
+ dir);
+ MessageBox(NULL, buffer, "instmapi.exe",
+ MB_OK|MB_ICONERROR);
+ return 0;
+ }
+
+ if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Clients\\Mail",
+ 0, KEY_READ, &hKey) == ERROR_SUCCESS){
+ RegCloseKey(hKey);
+ if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Clients\\Mail",
+ 0, KEY_ALL_ACCESS, &hKey) != ERROR_SUCCESS){
+ MessageBox(NULL,
+ "Cannot install pmapi32.dll. Please make sure that you have permissions to update your registry.",
+ "instmapi.exe",
+ MB_OK|MB_ICONERROR);
+ return 0;
+ }
+ }
+ else {
+ MessageBox(NULL,
+ "Cannot install pmapi32.dll. Mailer information not found in registry.",
+ "instmapi.exe",
+ MB_OK|MB_ICONERROR);
+ return 0;
+ }
+
+#ifdef FIXREG
+ /*
+ * I want to take this out next release because it is only intended to fix
+ * problems that pine (<=4.43) set in the registry.
+ */
+ if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Clients\\Mail\\PC-Pine\\Protocols\\Mailto",
+ 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS){
+ RegSetValueEx(hKey, "URL Protocol", 0, REG_SZ, "", 1);
+ RegCloseKey(hKey);
+ }
+ if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Clients\\News\\PC-Pine\\Protocols\\news",
+ 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS){
+ RegSetValueEx(hKey, "URL Protocol", 0, REG_SZ, "", 1);
+ RegCloseKey(hKey);
+ }
+ if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Clients\\News\\PC-Pine\\Protocols\\nntp",
+ 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS){
+ RegSetValueEx(hKey, "URL Protocol", 0, REG_SZ, "", 1);
+ RegCloseKey(hKey);
+ }
+ if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Clients\\News\\PC-Pine\\shell\\open\\command",
+ 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS){
+ char *tp;
+
+ buffer[0] = '\0';
+ bufflen = 1024*3;
+ RegQueryValueEx(hKey, "", 0, &dtype, buffer, &bufflen);
+ tp = buffer + strlen(buffer) - strlen(" -url news:%1");
+ if((tp - buffer > 0) && (tp - buffer < (int)bufflen)
+ && (strcmp(tp, " -url news:%1") == 0)){
+ *tp = '\0';
+ RegSetValueEx(hKey, "", 0, dtype, buffer, strlen(buffer));
+ }
+ RegCloseKey(hKey);
+ }
+#endif
+
+ if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Clients\\Mail\\PC-Pine",
+ 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS){
+ if(RegSetValueEx(hKey, "DLLPath", 0, REG_SZ,
+ filename, strlen(filename) + 1) != ERROR_SUCCESS){
+ MessageBox(NULL,
+ "Could not update registry. Install not completed.",
+ "instmapi.exe",
+ MB_OK|MB_ICONERROR);
+ RegCloseKey(hKey);
+ return 0;
+ }
+ else
+ RegCloseKey(hKey);
+ }
+ else{
+ MessageBox(NULL,
+ "No entry found for PC-Pine. Try running PC-Pine, and then run this program",
+ "instmapi.exe",
+ MB_OK|MB_ICONERROR);
+ return 0;
+ }
+ if(check_defaults(CD_MAILER, silent))
+ MessageBox(NULL,
+ "Default mailer could not be set",
+ "instmapi.exe",
+ MB_OK|MB_ICONERROR);
+ if(check_defaults(CD_NEWS, silent))
+ MessageBox(NULL,
+ "Default newsreader could not be set",
+ "instmapi.exe",
+ MB_OK|MB_ICONERROR);
+
+ /* This next part determines whether or not we should write our mapi
+ * into the system directory
+ */
+ if(GetSystemDirectory(dir, 1000)){
+ sprintf(mapifile, "%s%s", dir, dir[strlen(dir)-1] == '\\' ?
+ "mapi32.dll" : "\\mapi32.dll");
+
+ hDll = LoadLibrary(mapifile);
+ if(hDll){
+ if(proc = GetProcAddress(hDll,"GetPCPineVersion")){
+ sprintf(buffer2, "pmapi32.dll exists in %s as mapi32.dll",
+ dir);
+ MessageBox(NULL, buffer2,
+ "instmapi.exe",
+ MB_APPLMODAL | MB_ICONINFORMATION | MB_OK);
+ success = 1;
+ }
+ if(!success){
+ sprintf(buffer, "%s%s", dir, dir[strlen(dir)-1] == '\\' ?
+ "mapistub.dll" : "\\mapistub.dll");
+ if(_access(buffer, 00) != 0)
+ copy = 1;
+ if(!copy){
+ proc = GetProcAddress(hDll, "FixMAPI");
+ if(!proc){
+ copy = 1;
+ }
+ else
+ success = 1;
+ }
+ }
+ FreeLibrary(hDll);
+ }
+ else
+ copy = 1;
+ if(copy){
+ sprintf(buffer2, "%s%s", dir, dir[strlen(dir)-1] == '\\' ?
+ "mapi32x.dll" : "\\mapi32x.dll");
+ CopyFile(mapifile, buffer2, 0);
+ if(CopyFile(filename, mapifile, 0)){
+ sprintf(buffer2, "pmapi32.dll has been copied to %s",
+ mapifile);
+ MessageBox(NULL, buffer2, "instmapi.exe",
+ MB_APPLMODAL | MB_ICONINFORMATION | MB_OK);
+ success = 1;
+ }
+ else{
+ sprintf(buffer2, "pmapi32.dll could not be copied to %s",
+ mapifile);
+ MessageBox(NULL, buffer2, "instmapi.exe",
+ MB_APPLMODAL | MB_ICONINFORMATION | MB_OK);
+ }
+ }
+ }
+
+ if(success && !silent){
+ sprintf(buffer2,
+ "Installation of pmapi32.dll was successfully completed",
+ buffer);
+ MessageBox(NULL, buffer2, "instmapi.exe",
+ MB_APPLMODAL | MB_ICONINFORMATION | MB_OK);
+ }
+ else if(!success){
+ sprintf(buffer2,
+ "Installation of pmapi32.dll may not have successfully completed",
+ buffer);
+ MessageBox(NULL, buffer2, "instmapi.exe",
+ MB_APPLMODAL | MB_ICONINFORMATION | MB_OK);
+ }
+ return 0;
+}
+
+int
+check_defaults(wdef, silent)
+ int wdef, silent;
+{
+ HKEY hKey;
+ DWORD dtype;
+ char buffer[1024*3];
+ unsigned long bufflen = 1024*3;
+ int ret;
+
+ if(wdef != CD_MAILER && wdef != CD_NEWS)
+ return(1);
+ if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, wdef == CD_MAILER
+ ? "SOFTWARE\\Clients\\Mail" : "SOFTWARE\\Clients\\News",
+ 0, KEY_ALL_ACCESS, &hKey) != ERROR_SUCCESS)
+ return(1);
+ bufflen = 1024*3;
+ RegQueryValueEx(hKey, "", 0, &dtype, buffer, &bufflen);
+ if(strcmp(buffer, "PC-Pine") == 0){
+ RegCloseKey(hKey);
+ return(0);
+ }
+ ret = silent ? IDYES
+ : MessageBox (NULL, wdef == CD_MAILER
+ ? "Do you want to make PC-Pine your default mailer?"
+ : "Do you want to make PC-Pine your default newsreader?",
+ "instmapi.exe",
+ MB_APPLMODAL | MB_ICONQUESTION | MB_YESNO);
+ if(ret == IDYES){
+ strcpy(buffer, "PC-Pine");
+ bufflen = strlen(buffer)+1;
+ if(RegSetValueEx(hKey, "", 0,
+ REG_SZ, buffer, bufflen) != ERROR_SUCCESS){
+ RegCloseKey(hKey);
+ return(1);
+ }
+ RegCloseKey(hKey);
+ if(wdef == CD_MAILER){
+ if(check_url_commands(CUC_MAILTO))
+ return(1);
+ }
+ else if(wdef == CD_NEWS){
+ if(check_url_commands(CUC_NEWS)
+ || check_url_commands(CUC_NNTP))
+ return(1);
+ }
+ }
+ return(0);
+}
+
+int
+check_url_commands(wdef)
+ int wdef;
+{
+ HKEY hKey, hKeyCP;
+ DWORD dtype;
+ char buffer[1024*3];
+ unsigned long bufflen = 1024*3;
+
+ if(wdef != CUC_MAILTO && wdef != CUC_NEWS && wdef != CUC_NNTP)
+ return(1);
+ if(RegOpenKeyEx(HKEY_CLASSES_ROOT, wdef == CUC_MAILTO
+ ? "mailto\\shell\\open\\command"
+ : (wdef == CUC_NEWS ? "news\\shell\\open\\command"
+ : "nntp\\shell\\open\\command"),
+ 0, KEY_ALL_ACCESS, &hKey) != ERROR_SUCCESS)
+ return(1);
+ if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, wdef == CUC_MAILTO
+ ? "SOFTWARE\\Clients\\Mail\\PC-Pine\\Protocols\\Mailto\\shell\\open\\command"
+ : (wdef == CUC_NEWS ? "SOFTWARE\\Clients\\News\\PC-Pine\\Protocols\\news\\shell\\open\\command"
+ : "SOFTWARE\\Clients\\News\\PC-Pine\\Protocols\\nntp\\shell\\open\\command"),
+ 0, KEY_ALL_ACCESS, &hKeyCP) != ERROR_SUCCESS){
+ RegCloseKey(hKey);
+ return(1);
+ }
+ buffer[0] = '\0';
+ bufflen = 1024*3;
+ if(RegQueryValueEx(hKeyCP, "", 0, &dtype,
+ buffer, &bufflen) != ERROR_SUCCESS
+ || RegSetValueEx(hKey, "", 0, REG_SZ, buffer,
+ bufflen) != ERROR_SUCCESS){
+ RegCloseKey(hKey);
+ RegCloseKey(hKeyCP);
+ return(1);
+ }
+ RegCloseKey(hKey);
+ RegCloseKey(hKeyCP);
+ if(RegOpenKeyEx(HKEY_CLASSES_ROOT, wdef == CUC_MAILTO
+ ? "mailto\\DefaultIcon"
+ : (wdef == CUC_NEWS ? "news\\DefaultIcon"
+ : "nntp\\DefaultIcon"),
+ 0, KEY_ALL_ACCESS,
+ &hKey) != ERROR_SUCCESS)
+ return(1);
+ if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, wdef == CUC_MAILTO
+ ? "SOFTWARE\\Clients\\Mail\\PC-Pine\\Protocols\\Mailto\\DefaultIcon"
+ : (wdef == CUC_NEWS ? "SOFTWARE\\Clients\\News\\PC-Pine\\Protocols\\news\\DefaultIcon"
+ : "SOFTWARE\\Clients\\News\\PC-Pine\\Protocols\\nntp\\DefaultIcon"),
+ 0, KEY_ALL_ACCESS, &hKeyCP) != ERROR_SUCCESS){
+ RegCloseKey(hKey);
+ return(1);
+ }
+ buffer[0] = '\0';
+ bufflen = 1024*3;
+ if(RegQueryValueEx(hKeyCP, "", 0, &dtype, buffer, &bufflen) != ERROR_SUCCESS
+ || RegSetValueEx(hKey, "", 0, REG_SZ, buffer, bufflen) != ERROR_SUCCESS){
+ RegCloseKey(hKey);
+ RegCloseKey(hKeyCP);
+ return(1);
+ }
+ RegCloseKey(hKey);
+ RegCloseKey(hKeyCP);
+
+ if(RegOpenKeyEx(HKEY_CLASSES_ROOT, wdef == CUC_MAILTO
+ ? "mailto"
+ : (wdef == CUC_NEWS ? "news"
+ : "nntp"),
+ 0, KEY_ALL_ACCESS,
+ &hKey) != ERROR_SUCCESS)
+ return(1);
+ if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, wdef == CUC_MAILTO
+ ? "SOFTWARE\\Clients\\Mail\\PC-Pine\\Protocols\\Mailto"
+ : (wdef == CUC_NEWS ? "SOFTWARE\\Clients\\News\\PC-Pine\\Protocols\\news"
+ : "SOFTWARE\\Clients\\News\\PC-Pine\\Protocols\\nntp"),
+ 0, KEY_ALL_ACCESS, &hKeyCP) != ERROR_SUCCESS){
+ RegCloseKey(hKey);
+ return(1);
+ }
+ buffer[0] = '\0';
+ bufflen = 1024*3;
+ if(RegQueryValueEx(hKeyCP, "URL Protocol", 0, &dtype, buffer, &bufflen) != ERROR_SUCCESS
+ || RegSetValueEx(hKey, "URL Protocol", 0, REG_SZ, buffer, bufflen) != ERROR_SUCCESS){
+ RegCloseKey(hKey);
+ RegCloseKey(hKeyCP);
+ return(1);
+ }
+ RegCloseKey(hKey);
+ RegCloseKey(hKeyCP);
+
+ return(0);
+}
diff --git a/mapi/makefile b/mapi/makefile
new file mode 100755
index 00000000..8031be64
--- /dev/null
+++ b/mapi/makefile
@@ -0,0 +1,72 @@
+#
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+
+# Nmake macros for building Windows 32-Bit apps
+
+all: pmapi32.dll instmapi.exe
+
+clean:
+ del *.obj *.res *.dll *.lib *.exe
+
+CC=cl
+RC=rc
+LINK=link
+
+CBASIC = -nologo -c -W3 -DWIN32 -D_WIN32
+CDEBUG = #-Zi -Od
+LDEBUG = #/debug /debugtype:cv
+CVARSDLL = -MD -D_DLL
+CVARSEXE = -MT
+CFLAGS = $(CBASIC) $(CDEBUG) $(EXTRACFLAGS)
+RCFLAGS = /fo pmapi.res
+DLLENTRY = @12
+DLLLFLAGS = -entry:_DllMainCRTStartup$(DLLENTRY) -dll $(EXTRALDFLAGS)
+GUIFLAGS = -subsystem:windows
+LFLAGS = $(LDEBUG) -nologo /NODEFAULTLIB
+STDLIBS= ..\c-client-dll\cclient.lib winmm.lib crypt32.lib
+LIBS = oldnames.lib kernel32.lib advapi32.lib ws2_32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib
+LIBSDLL = msvcrt.lib $(LIBS) $(EXTRALIBES)
+LIBSEXE = libcmt.lib $(LIBS)
+
+# Update the object files if necessary
+
+pmapi.obj: pmapi.c ..\c-client-dll\mail.h pmapi.h
+ $(CC) $(CFLAGS) $(CVARSDLL) pmapi.c
+smapi.obj: smapi.c ..\c-client-dll\mail.h pmapi.h
+ $(CC) $(CFLAGS) $(CVARSDLL) smapi.c
+rfc1522.obj: rfc1522.c ..\c-client-dll\mail.h pmapi.h
+ $(CC) $(CFLAGS) $(CVARSDLL) rfc1522.c
+
+instmapi.obj: instmapi.c
+ $(CC) $(CFLAGS) instmapi.c
+
+# Update the import library
+
+pmapi.res: pmapi.rc
+ $(RC) $(RCFLAGS) pmapi.rc
+
+# Update the dynamic link library
+
+pmapi32.dll: pmapi.obj smapi.obj rfc1522.obj pmapi.def pmapi.res
+ ..\pico\blddate > bdate.c
+ $(CC) /c $(CFLAGS) bdate.c
+ $(LINK) $(LFLAGS) $(DLLLFLAGS)\
+ -base:0x1C000000 \
+ -out:pmapi32.dll \
+ /DEF:pmapi.def \
+ pmapi.obj smapi.obj rfc1522.obj bdate.obj pmapi.res $(STDLIBS) $(LIBSDLL)
+
+instmapi.exe: instmapi.obj
+ $(LINK) $(LFLAGS) $(GUIFLAGS) -out:instmapi.exe instmapi.obj $(LIBSEXE)
+
+distclean:
diff --git a/mapi/pmapi.c b/mapi/pmapi.c
new file mode 100644
index 00000000..7fa3b455
--- /dev/null
+++ b/mapi/pmapi.c
@@ -0,0 +1,2929 @@
+
+/*
+ * ========================================================================
+ * Copyright 2006-2009 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "pmapi.h"
+
+static int xlate_key;
+
+mapi_global_s *ms_global = NULL;
+#define SIZEOF_20KBUF (20480)
+char tmp_20k_buf[SIZEOF_20KBUF];
+
+void mm_searched (MAILSTREAM *stream,unsigned long number);
+void mm_exists (MAILSTREAM *stream,unsigned long number);
+void mm_expunged (MAILSTREAM *stream,unsigned long number);
+void mm_flags (MAILSTREAM *stream,unsigned long number);
+void mm_notify (MAILSTREAM *stream,char *string,long errflg);
+void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes);
+void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes);
+void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status);
+void mm_log (char *string,long errflg);
+void mm_dlog (char *string);
+int fetch_recursively(BODY *body, long msgno, char *prefix,
+ PART *part, long flags, FLAGS MAPIflags,
+ sessionlist_s *cs);
+LRESULT CALLBACK Login(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
+void mm_login (NETMBX *mb,char *user,char *pwd,long trial);
+void mm_critical (MAILSTREAM *stream);
+void mm_nocritical (MAILSTREAM *stream);
+long mm_diskerror (MAILSTREAM *stream,long errcode,long serious);
+void mm_fatal (char *string);
+lpMapiFileDesc new_mapi_file_desc(int arraysize);
+void free_mapi_file_desc(lpMapiFileDesc lpmfd, int arraysize);
+file_s *new_file_s();
+void free_file_s(file_s *fs);
+void init_prcvars(mapi_global_s *nmg);
+void init_prcfeats(mapi_global_s *nmg);
+void expand_env_vars(mapi_global_s *nmg);
+void init_fcc_folder(mapi_global_s *nmg);
+void init_pmapi_registry_vars(mapi_global_s *nmg);
+void init_pmapi_vars(mapi_global_s *nmg);
+char *copy_remote_to_local(char *pinerc, sessionlist_s *cs);
+int read_pinerc(mapi_global_s *nmg, sessionlist_s *cs,
+ char *prc, char *pineconf, char *pinercex, int depth);
+void free_mapi_global(mapi_global_s *nmg);
+MAILSTREAM *first_open(sessionlist_s *cs);
+int lookup_file_mime_type(char *fn, BODY *body);
+int LookupMIMEFileExt(char *val, char *mime_type, DWORD *vallen);
+int in_passfile(sessionlist_s *cs);
+int set_text_data(BODY *body, char *txt);
+int get_suggested_directory(char *dir);
+int get_suggested_file_ext(char *file_ext, PART *part, DWORD *file_extlen);
+int InitDebug();
+int GetPineData();
+int UnderlineSpace(char *s);
+int GetOutlookVersion();
+char *pmapi_generate_message_id();
+SENDSTREAM *mapi_smtp_open(sessionlist_s *cs, char **servers, long options);
+char *encode_mailto_addr_keyval(lpMapiRecipDesc lpmr);
+char *encode_mailto_keyval(char *key, char* val);
+
+
+void mm_searched (MAILSTREAM *stream,unsigned long number)
+{
+ if(MSDEBUG){
+ fprintf(ms_global->dfd, "IMAP mm_searched: not implemented\r\n");
+ _flushall();
+ }
+}
+
+void mm_exists (MAILSTREAM *stream,unsigned long number)
+{
+ if(MSDEBUG){
+ fprintf(ms_global->dfd, "IMAP mm_exists: not implemented\r\n");
+ _flushall();
+ }
+}
+
+void mm_expunged (MAILSTREAM *stream,unsigned long number)
+{
+ if(MSDEBUG){
+ fprintf(ms_global->dfd, "IMAP mm_expunged: not implemented\r\n");
+ _flushall();
+ }
+}
+
+void mm_flags (MAILSTREAM *stream,unsigned long number)
+{
+ if(MSDEBUG){
+ fprintf(ms_global->dfd, "IMAP mm_flags: number is %d\r\n", number);
+ _flushall();
+ }
+}
+
+void mm_notify (MAILSTREAM *stream,char *string,long errflg)
+{
+ mapi_global_s *nmg;
+
+ nmg = ms_global;
+ if(MSDEBUG){
+ fprintf(nmg->dfd, "IMAP mm_notify:%s %s\r\n",
+ (errflg == NIL ? "" :
+ (errflg == WARN ? " WARN:":
+ (errflg == ERROR ? " ERROR:":
+ (errflg == PARSE ? " PARSE:":" BYE:")))), string);
+ _flushall();
+ }
+}
+
+void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes)
+{
+ if(MSDEBUG){
+ fprintf(ms_global->dfd, "IMAP mm_list: not implemented\r\n");
+ _flushall();
+ }
+}
+
+void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes)
+{
+ if(MSDEBUG){
+ fprintf(ms_global->dfd, "IMAP mm_lsub: not implemented\r\n");
+ _flushall();
+ }
+}
+
+void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
+{
+ if(MSDEBUG){
+ fprintf(ms_global->dfd, "IMAP mm_status: not implemented\r\n");
+ _flushall();
+ }
+}
+
+void mm_log (char *string,long errflg)
+{
+ if(MSDEBUG){
+ fprintf(ms_global->dfd, "IMAP mm_log:%s %s\r\n",
+ (errflg == NIL ? "" :
+ (errflg == WARN ? " WARN:":
+ (errflg == ERROR ? " ERROR:":
+ (errflg == PARSE ? " PARSE:":" BYE:")))), string);
+ _flushall();
+ }
+ if(errflg == ERROR)
+ ErrorBox("ERROR: %s", string);
+}
+
+void mm_dlog (char *string)
+{
+ if(MSDEBUG){
+ time_t now;
+ struct tm *tm_now;
+
+ now = time((time_t *)0);
+ tm_now = localtime(&now);
+
+ fprintf(ms_global->dfd, "IMAP %2.2d:%2.2d:%2.2d %d/%d: %s\r\n",
+ tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec,
+ tm_now->tm_mon+1, tm_now->tm_mday, string);
+ _flushall();
+ }
+}
+
+int fetch_recursively(BODY *body, long msgno, char *prefix, PART *part,
+ long flags, FLAGS MAPIflags, sessionlist_s *cs)
+{
+ FILE *attfd;
+ PART *subpart;
+ mapi_global_s *nmg;
+ int num = 0, tmplen;
+ file_s *tfs, *tfs2;
+ unsigned long declen, numwritten;
+ void *decf;
+ char tmp[64], file_ext[64], filename[1024], dir[1024];
+ DWORD file_extlen = 64;
+ char *tmpatt;
+ unsigned long tmpattlen;
+
+ nmg = ms_global;
+ tmp[0] = '\0';
+ if(body && body->type == TYPEMULTIPART){
+ part = body->nested.part;
+ fetch_recursively(&part->body, msgno, prefix, part,
+ flags, MAPIflags, cs);
+ }
+ else{
+ do{
+ num++;
+ sprintf(tmp, "%s%s%d", prefix, (*prefix ? "." : ""), num);
+ if(part && part->body.type == TYPEMULTIPART){
+ subpart = part->body.nested.part;
+ fetch_recursively(&subpart->body, msgno, tmp,
+ subpart, flags, MAPIflags, cs);
+ }
+ else{
+ tmpatt = mail_fetch_body(cs->open_stream, msgno,
+ tmp, &tmpattlen, flags);
+ if(strcmp(tmp, "1") == 0){
+ if(((part && part->body.type == TYPETEXT) ||
+ (!part && body->type == TYPETEXT)) &&
+ !(MAPIflags & MAPI_BODY_AS_FILE)){
+ if(cs->lpm->lpszNoteText){
+ fs_give((void **)&cs->lpm->lpszNoteText);
+ cs->lpm->lpszNoteText = NULL;
+ }
+ cs->lpm->lpszNoteText = mstrdup(tmpatt);
+ tmpatt = NULL;
+ }
+ else
+ cs->lpm->lpszNoteText = mstrdup("");
+ if(MAPIflags & MAPI_SUPPRESS_ATTACH)
+ return 0;
+ }
+ if(tmpatt && part && part->body.encoding == ENCBASE64){
+ decf = rfc822_base64(tmpatt, tmpattlen, &declen);
+ tmpatt = NULL;
+ }
+ else if(tmpatt && part && part->body.encoding == ENCQUOTEDPRINTABLE){
+ decf = rfc822_qprint(tmpatt, tmpattlen, &declen);
+ tmpatt = NULL;
+ }
+ else{
+ if(tmpatt){
+ decf = mstrdup(tmpatt);
+ declen = tmpattlen;
+ }
+ else decf = NULL;
+ }
+ if(decf){
+ dir[0] = '\0';
+ get_suggested_directory(dir);
+ tmplen = strlen(dir);
+ if(dir[tmplen - 1] != '\\'){
+ dir[tmplen] = '\\';
+ dir[tmplen+1] = '\0';
+ }
+ file_ext[0] = '\0';
+ get_suggested_file_ext(file_ext,part, &file_extlen);
+ do{
+ sprintf(filename, "%smapiapp%d%s", dir, nmg->attach_no,
+ file_ext);
+ nmg->attach_no++;
+ }while (_access(filename, 00) != -1);
+ attfd = fopen(filename, "wb");
+ if(attfd){
+ if(MSDEBUG)
+ fprintf(nmg->dfd,"preparing to write attachment to %s\r\n",
+ filename);
+ numwritten = fwrite(decf, sizeof(char), declen, attfd);
+ fclose(attfd);
+ fs_give((void **)&decf);
+ tfs = new_file_s();
+ tfs->filename = mstrdup(filename);
+ if(!cs->fs)
+ cs->fs = tfs;
+ else{
+ for(tfs2 = cs->fs; tfs2->next; tfs2 = tfs2->next);
+ tfs2->next = tfs;
+ }
+ }
+ else{
+ if(MSDEBUG)
+ fprintf(nmg->dfd,"Failure in opening %s for attachment\r\n",
+ filename);
+ }
+ }
+ }
+ }while(part && (part = part->next));
+ }
+ return 0;
+}
+
+int fetch_structure_and_attachments(long msgno, long flags,
+ FLAGS MAPIflags, sessionlist_s *cs)
+{
+ BODY *body = NULL;
+ ENVELOPE *env;
+ ADDRESS *addr;
+ MESSAGECACHE *elt = NULL;
+ mapi_global_s *nmg;
+ file_s *tfs;
+ int num = 0, restore_seen = 0, file_count = 0, i;
+ unsigned long recips;
+ char tmp[1024]; /* don't know how much space we'll need */
+
+ nmg = ms_global;
+ env = mail_fetch_structure(cs->open_stream, msgno, &body, flags);
+ if(env == NULL || body == NULL){
+ if(MSDEBUG)
+ fprintf(nmg->dfd, "mail_fetch_structure returned %p for env and %p for body\r\n", env, body);
+ return MAPI_E_FAILURE;
+ }
+ if(cs->lpm){
+ if(MSDEBUG)
+ fprintf(nmg->dfd, "global lpm is set when it SHOULDN'T be! Freeing\r\n");
+ if(free_mbuffer(cs->lpm))
+ free_MapiMessage(cs->lpm, 1);
+ cs->lpm = NULL;
+ }
+ cs->lpm = new_MapiMessage(1);
+ if(env->subject) cs->lpm->lpszSubject = mstrdup(env->subject);
+ if(env->date){
+ elt = mail_elt(cs->open_stream, msgno);
+ mail_parse_date(elt, env->date);
+ sprintf(tmp, "%d/%s%d/%s%d %s%d:%s%d",
+ elt->year+BASEYEAR, (elt->month < 10) ? "0": "",
+ elt->month, (elt->day < 10) ? "0":"", elt->day,
+ (elt->hours < 10) ? "0":"", elt->hours,
+ (elt->minutes < 10) ? "0":"", elt->minutes);
+ cs->lpm->lpszDateReceived = mstrdup(tmp);
+ }
+ if(env->from){
+ cs->lpm->lpOriginator = new_MapiRecipDesc(1);
+ if(env->from->personal)
+ cs->lpm->lpOriginator->lpszName = mstrdup(env->from->personal);
+ if(env->from->mailbox && env->from->host){
+ /* don't know if these could ever be empty */
+ sprintf(tmp, "%s@%s", env->from->mailbox, env->from->host);
+ cs->lpm->lpOriginator->lpszAddress = mstrdup(tmp);
+ }
+ cs->lpm->lpOriginator->ulRecipClass = MAPI_ORIG;
+ }
+ if(env->to || env->cc || env->bcc){ /* should always be true */
+ recips = 0;
+ if(env->to){
+ addr = env->to;
+ while(addr){
+ recips++;
+ addr = addr->next;
+ }
+ }
+ if(env->cc){
+ addr = env->cc;
+ while(addr){
+ recips++;
+ addr = addr->next;
+ }
+ }
+ if(env->bcc){
+ addr = env->bcc;
+ while(addr){
+ recips++;
+ addr = addr->next;
+ }
+ }
+ cs->lpm->nRecipCount = recips;
+ cs->lpm->lpRecips = new_MapiRecipDesc(recips);
+ recips = 0;
+ if(env->to){
+ addr = env->to;
+ while(addr){
+ cs->lpm->lpRecips[recips].ulRecipClass = MAPI_TO;
+ if(addr->personal)
+ cs->lpm->lpRecips[recips].lpszName = mstrdup(addr->personal);
+ if(addr->mailbox && addr->host){
+ sprintf(tmp, "%s@%s", addr->mailbox, addr->host);
+ cs->lpm->lpRecips[recips].lpszAddress = mstrdup(tmp);
+ }
+ recips++;
+ addr = addr->next;
+ }
+ }
+ if(env->cc){
+ addr = env->cc;
+ while(addr){
+ cs->lpm->lpRecips[recips].ulRecipClass = MAPI_CC;
+ if(addr->personal)
+ cs->lpm->lpRecips[recips].lpszName = mstrdup(addr->personal);
+ if(addr->mailbox && addr->host){
+ sprintf(tmp, "%s@%s", addr->mailbox, addr->host);
+ cs->lpm->lpRecips[recips].lpszAddress = mstrdup(tmp);
+ }
+ recips++;
+ addr = addr->next;
+ }
+ }
+ if(env->bcc){
+ addr = env->bcc;
+ while(addr){
+ cs->lpm->lpRecips[recips].ulRecipClass = MAPI_BCC;
+ if(addr->personal)
+ cs->lpm->lpRecips[recips].lpszName = mstrdup(addr->personal);
+ if(addr->mailbox && addr->host){
+ sprintf(tmp, "%s@%s", addr->mailbox, addr->host);
+ cs->lpm->lpRecips[recips].lpszAddress = mstrdup(tmp);
+ }
+ recips++;
+ addr = addr->next;
+ }
+ }
+ }
+
+ if(flags & FT_PEEK){
+ /* gotta remember to unflag \Seen if we just want a peek */
+ sprintf(tmp, "%d", msgno);
+ mail_fetch_flags(cs->open_stream, tmp, NIL);
+ elt = mail_elt(cs->open_stream, msgno);
+ if(!elt->seen){
+ if(MSDEBUG)
+ fprintf(nmg->dfd, "Message has not been seen, and a PEEK is requested\r\n");
+ restore_seen = 1;
+ }
+ else if(MSDEBUG)
+ fprintf(nmg->dfd, "Message has already been marked seen\r\n");
+ }
+ if(!(MAPIflags & MAPI_ENVELOPE_ONLY))
+ fetch_recursively(body, msgno, "", NULL, flags, MAPIflags, cs);
+ if(cs->fs){
+ for(tfs = cs->fs; tfs; tfs = tfs->next)
+ file_count++;
+ cs->lpm->lpFiles = new_mapi_file_desc(file_count);
+ for(i = 0, tfs = cs->fs; i < file_count && tfs; i++, tfs = tfs->next){
+ cs->lpm->lpFiles[i].lpszPathName = mstrdup(tfs->filename);
+ }
+ cs->lpm->nFileCount = file_count;
+ free_file_s(cs->fs);
+ cs->fs = NULL;
+ }
+ if(restore_seen){
+ elt = mail_elt(cs->open_stream, msgno);
+ if(!elt->seen && MSDEBUG)
+ fprintf(nmg->dfd, "Fetched body and Message still isn't seen\r\n");
+ else if(MSDEBUG)
+ fprintf(nmg->dfd, "Message has been seen, clearing flag\r\n");
+ if(elt->seen){
+ mail_flag(cs->open_stream, tmp, "\\SEEN", NIL);
+ elt = mail_elt(cs->open_stream, msgno);
+ if(MSDEBUG)
+ fprintf(nmg->dfd, "After calling mail_flag(), elt->seen is %s\r\n",
+ elt->seen ? "SET" : "UNSET");
+ }
+ }
+ return SUCCESS_SUCCESS;
+}
+
+LRESULT CALLBACK Login(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ mapi_global_s *nmg;
+
+ nmg = ms_global;
+
+ switch(message){
+ case WM_INITDIALOG:
+ SetDlgItemText(hDlg, IDC_SERVER, nmg->tmpmbxptr);
+ if(/*nmg->flags.mapi_logon_ui &&*/ in_passfile(nmg->cs)){
+ SetDlgItemText(hDlg, IDC_LOGIN, nmg->cs->dlge.edit1);
+ SetDlgItemText(hDlg, IDC_PASSWORD, nmg->cs->dlge.edit2);
+ }
+ else
+ SetDlgItemText(hDlg, IDC_LOGIN,
+ (*nmg->cs->mb->user) ? nmg->cs->mb->user
+ : nmg->prcvars[USER_ID]->val.p);
+ return TRUE;
+ case WM_COMMAND:
+ if(LOWORD(wParam) == IDOK){
+ nmg->cs->flags.dlg_cancel = 0;
+ GetDlgItemText(hDlg, IDC_LOGIN, nmg->cs->dlge.edit1, EDITLEN);
+ GetDlgItemText(hDlg, IDC_PASSWORD, nmg->cs->dlge.edit2, EDITLEN);
+ EndDialog(hDlg, LOWORD(wParam));
+ return TRUE;
+ }
+ else if(LOWORD(wParam) == IDCANCEL){
+ nmg->cs->flags.dlg_cancel = 1;
+ EndDialog(hDlg, LOWORD(wParam));
+ return TRUE;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+void mm_login (NETMBX *mb,char *user,char *pwd,long trial)
+{
+ mapi_global_s *nmg;
+ pw_cache_s *tpwc, *dpwc;
+ int tmp_set_mbx = 0;
+
+ nmg = ms_global;
+ nmg->cs->mb = mb;
+ if(!nmg->cs->flags.dlg_cancel){
+ for(tpwc = nmg->cs->pwc; tpwc; tpwc = tpwc->next){
+ if(tpwc->validpw && strcmp(tpwc->host, mb->host) == 0)
+ break;
+ }
+ if(tpwc){
+ strcpy(user, tpwc->user);
+ strcpy(pwd, tpwc->pwd);
+ tpwc->validpw = 0;
+ }
+ /* else if(!nmg->cs->flags.check_stream) { */
+ else if(nmg->cs->flags.mapi_logon_ui || !nmg->pmapi_strict_no_dialog) {
+ if(!nmg->tmpmbxptr){
+ tmp_set_mbx = 1;
+ nmg->tmpmbxptr = mb->host;
+ }
+ DialogBox(nmg->mhinst, MAKEINTRESOURCE(IDD_DIALOG1),
+ nmg->cs->mhwnd, (DLGPROC)Login);
+ if(tmp_set_mbx)
+ nmg->tmpmbxptr = NULL;
+ if(!nmg->cs->flags.dlg_cancel){
+ strcpy(user, nmg->cs->dlge.edit1);
+ strcpy(pwd, nmg->cs->dlge.edit2);
+ tpwc = nmg->cs->pwc;
+ while(tpwc){
+ if(tpwc->validpw == 0){
+ dpwc = tpwc;
+ tpwc = tpwc->next;
+ if(dpwc == nmg->cs->pwc)
+ nmg->cs->pwc = dpwc->next;
+ fs_give((void **)&dpwc);
+ }
+ else
+ tpwc = tpwc->next;
+ }
+ if(nmg->cs->pwc == NULL){
+ nmg->cs->pwc = (pw_cache_s *)fs_get(sizeof(pw_cache_s));
+ tpwc = nmg->cs->pwc;
+ }
+ else {
+ for(tpwc = nmg->cs->pwc; tpwc->next; tpwc = tpwc->next);
+ tpwc->next = (pw_cache_s *)fs_get(sizeof(pw_cache_s));
+ tpwc = tpwc->next;
+ }
+ memset(tpwc, 0, sizeof(pw_cache_s));
+ strncpy(tpwc->user, nmg->cs->dlge.edit1, EDITLEN - 1);
+ strncpy(tpwc->pwd, nmg->cs->dlge.edit2, EDITLEN - 1);
+ strncpy(tpwc->host, mb->host, EDITLEN - 1);
+ }
+ }
+ }
+ nmg->cs->mb = NULL;
+}
+
+void mm_critical (MAILSTREAM *stream)
+{
+ if(MSDEBUG){
+ fprintf(ms_global->dfd, "IMAP mm_critical: not implemented\r\n");
+ _flushall();
+ }
+}
+
+void mm_nocritical (MAILSTREAM *stream)
+{
+ if(MSDEBUG){
+ fprintf(ms_global->dfd, "IMAP mm_nocritical: not implemented\r\n");
+ _flushall();
+ }
+}
+
+long mm_diskerror (MAILSTREAM *stream,long errcode,long serious)
+{
+ if(MSDEBUG){
+ fprintf(ms_global->dfd, "IMAP mm_diskerror: not implemented\r\n");
+ _flushall();
+ }
+ return 1;
+}
+
+void mm_fatal (char *string)
+{
+ if(MSDEBUG){
+ fprintf(ms_global->dfd, "IMAP mm_fatal: %s\r\n", string);
+ _flushall();
+ }
+}
+
+sessionlist_s *new_sessionlist()
+{
+ sessionlist_s *cs;
+
+ cs = (sessionlist_s *)fs_get(sizeof(sessionlist_s));
+ memset(cs, 0, sizeof(sessionlist_s));
+ cs->session_number = ms_global->next_session++;
+
+ return cs;
+}
+
+sessionlist_s *free_sessionlist_node(sessionlist_s *cs)
+{
+ sessionlist_s *ts;
+
+ ts = cs->next;
+
+ if(cs->currently_open)
+ fs_give((void **)&cs->currently_open);
+ if(cs->fs)
+ free_file_s(cs->fs);
+ if(cs->lpm){
+ if(free_mbuffer(cs->lpm))
+ free_MapiMessage(cs->lpm, 1);
+ cs->lpm = NULL;
+ }
+ fs_give((void **)&cs);
+
+ return ts;
+}
+
+sessionlist_s *get_session(unsigned long num)
+{
+ mapi_global_s *nmg = ms_global;
+ sessionlist_s *ts;
+
+ ts = nmg->sessionlist;
+ while(ts && ts->session_number != num) ts = ts->next;
+ return ts;
+}
+
+/*
+void *mm_cache (MAILSTREAM *stream,unsigned long msgno,long op){}
+*/
+mapi_global_s *new_mapi_global()
+{
+ mapi_global_s *nmg;
+ int i;
+
+ nmg = (mapi_global_s *)fs_get(sizeof(mapi_global_s));
+ memset(nmg, 0, sizeof(mapi_global_s));
+ for(i=0; i < NUMPRCVARS; i++){
+ nmg->prcvars[i] = (rc_entry_s *)fs_get(sizeof(rc_entry_s));
+ memset(nmg->prcvars[i], 0, sizeof(rc_entry_s));
+ }
+ for(i=0; i < NUMPRCFEATS; i++){
+ nmg->prcfeats[i] = (rc_feat_s *)fs_get(sizeof(rc_feat_s));
+ memset(nmg->prcfeats[i], 0, sizeof(rc_feat_s));
+ }
+ nmg->next_session = 1;
+
+ return nmg;
+}
+
+int InitPineSpecific(sessionlist_s *cs)
+{
+ mapi_global_s *nmg = ms_global;
+
+ if(nmg->inited) return 0;
+ init_prcvars(ms_global);
+ init_prcfeats(ms_global);
+ init_pmapi_registry_vars(ms_global);
+ if(read_pinerc(ms_global, cs, ms_global->pinerc,
+ ms_global->pineconf, ms_global->pinercex, 0) == -1)
+ return -1;
+ expand_env_vars(ms_global);
+ init_fcc_folder(ms_global);
+ msprint1("Fcc folder defined: %s", ms_global->fccfolder);
+ init_pmapi_vars(ms_global);
+ nmg->inited = 1;
+ return 1;
+}
+
+int
+new_mbuffer(void *buf, int arraysize, BufType type)
+{
+ MBUFFER_LIST_S *tlist, *tlist2;
+ mapi_global_s *nmg = ms_global;
+
+ tlist = (MBUFFER_LIST_S *)fs_get(sizeof(MBUFFER_LIST_S));
+ memset(tlist, 0, sizeof(MBUFFER_LIST_S));
+ tlist->buf = buf;
+ tlist->arraysize = arraysize;
+ tlist->type = type;
+
+ if(!nmg->mapi_bufs)
+ nmg->mapi_bufs = tlist;
+ else{
+ for(tlist2 = nmg->mapi_bufs; tlist2->next; tlist2 = tlist2->next);
+ tlist2->next = tlist;
+ }
+
+ return(0);
+}
+
+int
+free_mbuffer(void *buf)
+{
+ MBUFFER_LIST_S *tlist, *pre_tlist = NULL;
+ mapi_global_s *nmg = ms_global;
+ sessionlist_s *session;
+
+ for(tlist = nmg->mapi_bufs; tlist && tlist->buf != buf; pre_tlist = tlist, tlist = tlist->next);
+ if(!tlist){
+ msprint1("ERROR: buf %p not found in list!\r\n", buf);
+ return 1;
+ }
+ if(tlist == nmg->mapi_bufs)
+ nmg->mapi_bufs = tlist->next;
+ else
+ pre_tlist->next = tlist->next;
+ switch (tlist->type) {
+ case RecipDesc:
+ free_MapiRecipDesc(tlist->buf, tlist->arraysize);
+ break;
+ case Message:
+ for(session = nmg->sessionlist; session; session = session->next){
+ if(session->lpm == tlist->buf)
+ session->lpm = NULL;
+ }
+ free_MapiMessage(tlist->buf, tlist->arraysize);
+ break;
+ }
+ fs_give((void **)&tlist);
+ return 0;
+}
+
+lpMapiRecipDesc
+new_MapiRecipDesc(int arraysize)
+{
+ lpMapiRecipDesc lpmrd;
+ mapi_global_s *nmg = ms_global;
+
+ lpmrd = (MapiRecipDesc *)fs_get(arraysize*sizeof(MapiRecipDesc));
+ memset(lpmrd, 0, arraysize*sizeof(MapiRecipDesc));
+ new_mbuffer((void *)lpmrd, arraysize, RecipDesc);
+ return lpmrd;
+}
+
+void
+free_MapiRecipDesc(lpMapiRecipDesc buf, int arraysize)
+{
+ int i;
+
+ for(i = 0; i < arraysize; i++){
+ if(buf[i].lpszName)
+ fs_give((void **)&buf[i].lpszName);
+ if(buf[i].lpszAddress)
+ fs_give((void **)&buf[i].lpszAddress);
+ }
+ fs_give((void **)&buf);
+}
+
+lpMapiMessage
+new_MapiMessage(int arraysize)
+{
+ lpMapiMessage lpmm;
+ mapi_global_s *nmg = ms_global;
+
+ lpmm = (lpMapiMessage)fs_get(arraysize*sizeof(MapiMessage));
+ memset(lpmm, 0, arraysize*sizeof(MapiMessage));
+ new_mbuffer((void *)lpmm, arraysize, Message);
+ return lpmm;
+}
+
+void
+free_MapiMessage(lpMapiMessage buf, int arraysize)
+{
+ int i;
+
+ for(i = 0; i < arraysize; i++){
+ if(buf[i].lpszSubject)
+ fs_give((void **)&buf[i].lpszSubject);
+ if(buf[i].lpszNoteText)
+ fs_give((void **)&buf[i].lpszNoteText);
+ if(buf[i].lpszMessageType)
+ fs_give((void **)&buf[i].lpszMessageType);
+ if(buf[i].lpszDateReceived)
+ fs_give((void **)&buf[i].lpszDateReceived);
+ if(buf[i].lpszConversationID)
+ fs_give((void **)&buf[i].lpszConversationID);
+ if(buf[i].lpOriginator){
+ if(free_mbuffer(buf[i].lpOriginator))
+ free_MapiRecipDesc(buf[i].lpOriginator, 1);
+ }
+ if(buf[i].lpRecips){
+ if(free_mbuffer(buf[i].lpRecips))
+ free_MapiRecipDesc(buf[i].lpRecips, buf[i].nRecipCount);
+ }
+ if(buf[i].lpFiles)
+ free_mapi_file_desc(buf[i].lpFiles, buf[i].nFileCount);
+ }
+ fs_give((void **)&buf);
+}
+
+lpMapiFileDesc new_mapi_file_desc(int arraysize)
+{
+ lpMapiFileDesc lpmfd;
+
+ lpmfd = (MapiFileDesc *)fs_get(arraysize * sizeof (MapiFileDesc));
+ memset(lpmfd, 0, arraysize * sizeof(MapiFileDesc));
+ return lpmfd;
+}
+
+void free_mapi_file_desc(lpMapiFileDesc lpmfd, int arraysize)
+{
+ int i;
+
+ if(lpmfd == NULL) return;
+
+ for(i = 0; i < arraysize; i++){
+ if(lpmfd[i].lpszPathName)
+ fs_give((void **)&lpmfd[i].lpszPathName);
+ if(lpmfd[i].lpszFileName)
+ fs_give((void **)&lpmfd[i].lpszFileName);
+ /* NOTE: if lpFileType gets used, free it here */
+ }
+ fs_give((void **)&lpmfd);
+}
+
+file_s *new_file_s()
+{
+ file_s *tmp_fs;
+
+ tmp_fs = (file_s *)fs_get(sizeof(file_s));
+ memset(tmp_fs, 0, sizeof(file_s));
+ return tmp_fs;
+}
+
+void free_file_s(file_s *fs)
+{
+ if(fs == NULL) return;
+ if(fs->next)
+ free_file_s(fs->next);
+ if(fs->filename)
+ fs_give((void **)&fs->filename);
+ fs_give((void **)&fs);
+}
+
+void
+init_prcvars(mapi_global_s *nmg)
+{
+ int i=0;
+
+ nmg->prcvars[USER_ID]->var = mstrdup("user-id");
+ nmg->prcvars[PERSONAL_NAME]->var = mstrdup("personal-name");
+ nmg->prcvars[USER_DOMAIN]->var = mstrdup("user-domain");
+ nmg->prcvars[SMTP_SERVER]->var = mstrdup("smtp-server");
+ nmg->prcvars[SMTP_SERVER]->islist = 1;
+ nmg->prcvars[INBOX_PATH]->var = mstrdup("inbox-path");
+ nmg->prcvars[FEATURE_LIST]->var = mstrdup("feature-list");
+ nmg->prcvars[FEATURE_LIST]->islist = 1;
+ nmg->prcvars[CHARACTER_SET]->var = mstrdup("character-set");
+ nmg->prcvars[FOLDER_COLLECTIONS]->var = mstrdup("folder-collections");
+ nmg->prcvars[FOLDER_COLLECTIONS]->islist = 1;
+ nmg->prcvars[PMAPI_SEND_BEHAVIOR]->var = mstrdup("pmapi-send-behavior");
+ nmg->prcvars[PMAPI_SEND_BEHAVIOR]->ispmapivar = 1;
+ nmg->prcvars[DEFAULT_FCC]->var = mstrdup("default-fcc");
+ nmg->prcvars[PMAPI_SUPPRESS_DIALOGS]->var = mstrdup("pmapi-suppress-dialogs");
+ nmg->prcvars[PMAPI_SUPPRESS_DIALOGS]->ispmapivar = 1;
+ nmg->prcvars[PMAPI_STRICT_NO_DIALOG]->var = mstrdup("pmapi-strict-no-dialog");
+ nmg->prcvars[PMAPI_STRICT_NO_DIALOG]->ispmapivar = 1;
+}
+
+void
+init_prcfeats(mapi_global_s *nmg)
+{
+ nmg->prcfeats[ENABLE8BIT]->var = mstrdup("enable-8bit-esmtp-negotiation");
+}
+
+void init_fcc_folder(mapi_global_s *nmg)
+{
+ char *fcc, **fc, *desc = NULL, *col = NULL, *tfcc, *p, *p2;
+ int i = 0;
+
+ if(!nmg->prcvars[DEFAULT_FCC]->val.p)
+ nmg->prcvars[DEFAULT_FCC]->val.p = cpystr("sent-mail");
+ fcc = nmg->prcvars[DEFAULT_FCC]->val.p;
+ if(!fcc || !(*fcc)) return;
+ if(strcmp(fcc, "\"\"") == 0) return;
+ if((*fcc == '{') || (isalpha(fcc[0]) && (fcc[1] == ':'))
+ || ((fcc[0] == '\\') && (fcc[1] == '\\'))){
+ nmg->fccfolder = cpystr(fcc);
+ return;
+ }
+ fc = nmg->prcvars[FOLDER_COLLECTIONS]->val.l;
+ if(!fc || !fc[0] || !fc[0][0]) return;
+ get_pair(fc[i], &desc, &col, 0, 0);
+ if(desc)
+ fs_give((void **)&desc);
+ if(!col)
+ return;
+ p = strrchr(col, '[');
+ p2 = strrchr(col, ']');
+ if((p2 < p) || (!p)){
+ if(col)
+ fs_give((void **)&col);
+ return;
+ }
+ tfcc = (char *)fs_get((strlen(col) + strlen(fcc) + 1) * sizeof(char));
+ *p = '\0';
+ p2++;
+ sprintf(tfcc, "%s%s%s", col, fcc, p2);
+ nmg->fccfolder = tfcc;
+ if(col)
+ fs_give((void **)&col);
+}
+
+void init_pmapi_registry_vars(mapi_global_s *nmg)
+{
+ HKEY hKey;
+ BYTE keyData[1024];
+ DWORD keyDataSize = 1024;
+ DWORD keyDataType;
+ int i;
+
+ if(RegOpenKeyEx(HKEY_CURRENT_USER,
+ "Software\\University of Washington\\Alpine\\1.0\\PmapiOpts",
+ 0,
+ KEY_QUERY_VALUE,
+ &hKey) != ERROR_SUCCESS)
+ return;
+
+ for(i = 0; i < NUMPRCVARS; i++){
+ if(nmg->prcvars[i]->ispmapivar && nmg->prcvars[i]->islist == 0){
+ keyDataSize = 1024;
+ if((RegQueryValueEx(hKey, nmg->prcvars[i]->var, 0, &keyDataType,
+ keyData, &keyDataSize) == ERROR_SUCCESS)
+ && keyDataType == REG_SZ){
+ if(nmg->prcvars[i]->val.p)
+ fs_give((void **)&nmg->prcvars[i]->val.p);
+ nmg->prcvars[i]->val.p = mstrdup(keyData);
+ }
+ }
+ }
+
+ RegCloseKey(hKey);
+}
+
+void init_pmapi_vars(mapi_global_s *nmg)
+{
+ char *b;
+
+ if(b = nmg->prcvars[PMAPI_SEND_BEHAVIOR]->val.p){
+ if(_stricmp(b, "always-prompt") == 0)
+ nmg->pmapi_send_behavior = PMSB_ALWAYS_PROMPT;
+ else if(_stricmp(b, "always-send") == 0)
+ nmg->pmapi_send_behavior = PMSB_ALWAYS_SEND;
+ else if(_stricmp(b, "never-send") == 0)
+ nmg->pmapi_send_behavior = PMSB_NEVER_SEND;
+ }
+ else
+ nmg->pmapi_send_behavior = PMSB_ALWAYS_PROMPT;
+ if(b = nmg->prcvars[PMAPI_SUPPRESS_DIALOGS]->val.p){
+ if(_stricmp(b, "yes") == 0)
+ nmg->pmapi_suppress_dialogs = PMSD_YES;
+ else if(_stricmp(b, "prompt") == 0)
+ nmg->pmapi_suppress_dialogs = PMSD_PROMPT;
+ else if(_stricmp(b, "no") == 0)
+ nmg->pmapi_suppress_dialogs = PMSD_NO;
+ }
+ else
+ nmg->pmapi_suppress_dialogs = PMSD_NO;
+ if(b = nmg->prcvars[PMAPI_STRICT_NO_DIALOG]->val.p){
+ if(_stricmp(b, "yes") == 0)
+ nmg->pmapi_strict_no_dialog = 1;
+ }
+ else
+ nmg->pmapi_strict_no_dialog = 0;
+}
+
+char *copy_remote_to_local(char *pinerc, sessionlist_s *cs)
+{
+ mapi_global_s *nmg = ms_global;
+ char *tmptext, dir[1024], filename[1024];
+ unsigned long tmptextlen, i = 0, numwritten;
+ FILE *prcfd;
+
+ if(nmg->cs) return NULL;
+ if(!(cs->open_stream = mapi_mail_open(cs, NULL, pinerc,
+ ms_global->debug ? OP_DEBUG : NIL))){
+ ErrorBox("Couldn't open %s for reading as remote pinerc", pinerc);
+ return NULL;
+ }
+ nmg->cs = NULL;
+ nmg->tmpmbxptr = NULL;
+ tmptext = mail_fetch_body(cs->open_stream, cs->open_stream->nmsgs,
+ "1", &tmptextlen, NIL);
+ dir[0] = '\0';
+ get_suggested_directory(dir);
+ do{
+ sprintf(filename, "%s%smapipinerc%d", dir,
+ dir[strlen(dir)-1] == '\\' ? "" : "\\", i);
+ i++;
+ }while (_access(filename, 00) != -1);
+ if(prcfd = fopen(filename, "wb")){
+ if(MSDEBUG)
+ fprintf(ms_global->dfd,"preparing to write pinerc to %s\r\n",
+ filename);
+ numwritten = fwrite(tmptext, sizeof(char), tmptextlen, prcfd);
+ fclose(prcfd);
+ }
+ else{
+ ErrorBox("Couldn't open temp file %s for writing", filename);
+ mail_close_full(cs->open_stream, NIL);
+ return NULL;
+ }
+ cs->open_stream = mail_close_full(cs->open_stream, NIL);
+ return(mstrdup(filename));
+}
+
+int read_pinerc(mapi_global_s *nmg, sessionlist_s *cs,
+ char *prc, char *pineconf, char *pinercex, int depth)
+{
+ FILE *prcfd;
+ int i, varnum, j, varlen, create_local = 0, k;
+ char line[BUFLEN], *local_pinerc, *p;
+
+ if(nmg == NULL) return -1;
+ if(MSDEBUG){
+ fprintf(nmg->dfd,
+ "read_pinerc called: prc: %s, pineconf: %s, pinercex: %s, depth: %d\r\n",
+ prc ? prc : "NULL", pineconf ? pineconf : "NULL",
+ pinercex ? pinercex : "NULL", depth);
+ }
+ if(pineconf){
+ if(MSDEBUG)
+ fprintf(nmg->dfd, "Recursively calling read_pinerc for pineconf\r\n");
+ read_pinerc(nmg, cs, pineconf, NULL, NULL, 1);
+ }
+ if(prc == NULL){
+ ErrorBox("No value found for %s. Try running Alpine.", "pinerc");
+ DEBUG_WRITE("No value found for %s\r\n","pinerc");
+ _flushall();
+ return -1;
+ }
+ if(*prc == '{'){
+ if(MSDEBUG)
+ fprintf(ms_global->dfd, "REMOTE PINERC: %s\r\n", prc);
+ if(!(local_pinerc = copy_remote_to_local(prc, cs))){
+ if(MSDEBUG)
+ fprintf(nmg->dfd, "Couldn't copy remote pinerc to local\r\n");
+ return -1;
+ }
+ create_local = 1;
+ }
+ else{
+ if(!(local_pinerc = mstrdup(prc))){
+ ErrorBox("Couldn't fs_get for %s","pinerc");
+ return -1;
+ }
+ }
+ if(MSDEBUG)
+ fprintf(nmg->dfd, "Preparing to open local pinerc %s\r\n", local_pinerc);
+ prcfd = fopen(local_pinerc, "r");
+ if(prcfd == NULL){
+ DEBUG_WRITE("Couldn't open %s\r\n","pinerc");
+ _flushall();
+ ErrorBox("Couldn't open %s\r\n","pinerc");
+ if(local_pinerc)
+ fs_give((void **)&local_pinerc);
+ return -1;
+ }
+ DEBUG_WRITE("Opened %s for reading\r\n", "pinerc");
+ for(i = 0; i < NUMPRCVARS && nmg->prcvars[i]->var; i++);
+ varnum = i;
+ while(fgets(line, BUFLEN, prcfd)){
+ j = 0;
+ while(isspace(line[j])) j++;
+ if(line[j] != '#' && line[j] != '\0'){
+ for(i = 0; i < varnum; i ++){
+ varlen = strlen(nmg->prcvars[i]->var);
+ if(_strnicmp(nmg->prcvars[i]->var, line+j, varlen)==0){
+ j += varlen;
+ if(line[j] == '='){
+ /* we found a match in the pinerc */
+ j++;
+ if(nmg->prcvars[i]->islist){
+ while(isspace(line[j])) j++;
+ if(line[j] != '\0'){
+ STRLIST_S *strl = NULL, *tl, *tln;
+
+ if(nmg->prcvars[i]->val.l){
+ for(k = 0; nmg->prcvars[i]->val.l[k]; k++)
+ fs_give((void **)&nmg->prcvars[i]->val.l[k]);
+ fs_give((void **)&nmg->prcvars[i]->val.l);
+ }
+ strl = (STRLIST_S *)fs_get(sizeof(STRLIST_S));
+ memset(strl, 0, sizeof(STRLIST_S));
+ tl = strl;
+ while(line[j]){
+ while(isspace(line[j])) j++;
+ if(p = strchr(line+j, ','))
+ *p = '\0';
+ if(tl != strl || tl->str){
+ tl->next = (STRLIST_S *)fs_get(sizeof(STRLIST_S));
+ tl = tl->next;
+ memset(tl, 0, sizeof(STRLIST_S));
+ }
+ varlen = strlen(line+j);
+ while(isspace((line+j)[varlen - 1])){
+ varlen--;
+ (line+j)[varlen] = '\0';
+ }
+ tl->str = mstrdup(line+j);
+ if(p){
+ j = p - line + 1;
+ while(isspace(line[j])) j++;
+ if(!line[j]){
+ fgets(line, BUFLEN, prcfd);
+ j = 0;
+ }
+ }
+ else
+ break;
+ }
+ for(tl = strl, k = 0; tl; tl = tl->next, k++);
+ nmg->prcvars[i]->val.l = (char **)fs_get((k+1)*sizeof(char *));
+ for(tl = strl, k = 0; tl; tl = tln, k++){
+ nmg->prcvars[i]->val.l[k] = tl->str;
+ tln = tl->next;
+ fs_give((void **)&tl);
+ }
+ nmg->prcvars[i]->val.l[k] = NULL;
+ }
+ }
+ else{
+ while(isspace(line[j])) j++;
+ if(line[j] != '\0'){
+ varlen = strlen(line+j);
+ while(isspace((line+j)[varlen-1])){
+ varlen--;
+ (line+j)[varlen] = '\0';
+ }
+ if(nmg->prcvars[i]->val.p) fs_give((void **)&nmg->prcvars[i]->val.p);
+ nmg->prcvars[i]->val.p = (char *)fs_get((varlen + 1)*sizeof(char));
+ strncpy(nmg->prcvars[i]->val.p, line+j, varlen);
+ nmg->prcvars[i]->val.p[varlen] = '\0';
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if(!depth){
+ if(pinercex){
+ if(MSDEBUG)
+ fprintf(nmg->dfd,
+ "Recursively calling read_pinerc for exceptions\r\n");
+ read_pinerc(nmg, cs, pinercex, NULL, NULL, 1);
+ }
+ }
+ _flushall();
+ fclose(prcfd);
+ for(i = 0; nmg->prcvars[FEATURE_LIST]->val.l
+ && nmg->prcvars[FEATURE_LIST]->val.l[i]; i++){
+ for(j = 0; j < NUMPRCFEATS; j++){
+ if(strcmp(nmg->prcfeats[j]->var, nmg->prcvars[FEATURE_LIST]->val.l[i]) == 0)
+ nmg->prcfeats[j]->is_set = 1;
+ else if((strncmp("no-", nmg->prcvars[FEATURE_LIST]->val.l[i], 3) == 0)
+ && (strcmp(nmg->prcfeats[j]->var,
+ nmg->prcvars[FEATURE_LIST]->val.l[i] + 3) == 0)){
+ nmg->prcfeats[j]->is_set = 0;
+ }
+ }
+ }
+ if(create_local && local_pinerc){
+ if(MSDEBUG)
+ fprintf(nmg->dfd, "Removing %s\r\n", local_pinerc);
+ _unlink(local_pinerc);
+ }
+ if(local_pinerc)
+ fs_give((void **)&local_pinerc);
+ if(MSDEBUG){
+ fprintf(nmg->dfd, "Current pinerc settings:\r\n");
+ for(i = 0; i < varnum; i++){
+ fprintf(nmg->dfd, "%s:", nmg->prcvars[i]->var);
+ if(!nmg->prcvars[i]->islist)
+ fprintf(nmg->dfd, " %s\r\n",
+ nmg->prcvars[i]->val.p ? nmg->prcvars[i]->val.p : " NOT DEFINED");
+ else{
+ if(!nmg->prcvars[i]->val.l)
+ fprintf(nmg->dfd, " NOT DEFINED\r\n");
+ else {
+ for(j = 0; nmg->prcvars[i]->val.l[j]; j++)
+ fprintf(nmg->dfd, "\t%s\r\n",
+ nmg->prcvars[i]->val.l[j]);
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+void expand_env_vars(mapi_global_s *nmg)
+{
+ int i, j, check_reg = 0, islist;
+ DWORD keyDataSize = 1024, keyDataType;
+ char *p1, *p2, *p3, keyData[1024], *newstr, **valstrp;
+ HKEY hKey;
+
+
+ if(RegOpenKeyEx(HKEY_CURRENT_USER,
+ "Software\\University of Washington\\Alpine\\1.0\\PmapiOpts\\Env",
+ 0,
+ KEY_QUERY_VALUE,
+ &hKey) == ERROR_SUCCESS)
+ check_reg = 1;
+ for(i = 0; i < NUMPRCVARS; i++){
+ islist = nmg->prcvars[i]->islist;
+ for(j = 0; islist ? (int)(nmg->prcvars[i]->val.l && nmg->prcvars[i]->val.l[j])
+ : (j < 1); j++){
+ valstrp = islist ? &nmg->prcvars[i]->val.l[j] : &nmg->prcvars[i]->val.p;
+ if(*valstrp == NULL) continue;
+ while((p1 = strstr(*valstrp, "${")) && (p2 = strchr(p1, '}'))){
+ msprint1("%s -> ", *valstrp);
+ *p2 = '\0';
+ p3 = NULL;
+ if((p3 = getenv(p1+2)) && *p3)
+ ;
+ else if(check_reg && (keyDataSize = 1024)
+ && (RegQueryValueEx(hKey, p1+2, 0, &keyDataType,
+ keyData, &keyDataSize) == ERROR_SUCCESS)
+ && keyDataType == REG_SZ)
+ p3 = keyData;
+ newstr = (char *)fs_get(sizeof(char)*(strlen(*valstrp)
+ + strlen(p3 ? p3 : "") + strlen(p2+1) + 1));
+ *p1 = '\0';
+ strcpy(newstr, *valstrp);
+ strcat(newstr, p3 && *p3 ? p3 : "");
+ strcat(newstr, p2 + 1);
+ fs_give((void **)valstrp);
+ *valstrp = newstr;
+ msprint1(" %s\r\n", *valstrp);
+ }
+ }
+ }
+ if(check_reg)
+ RegCloseKey(hKey);
+}
+
+void free_mapi_global(mapi_global_s *nmg)
+{
+ int i, j;
+ sessionlist_s *ts;
+
+ if(nmg->pineExe)
+ fs_give((void **)&nmg->pineExe);
+ if(nmg->pineExeAlt)
+ fs_give((void **)&nmg->pineExeAlt);
+ if(nmg->pinerc)
+ fs_give((void **)&nmg->pinerc);
+ if(nmg->pineconf)
+ fs_give((void **)&nmg->pineconf);
+ if(nmg->pinercex)
+ fs_give((void **)&nmg->pinercex);
+ if(nmg->fccfolder)
+ fs_give((void **)&nmg->fccfolder);
+ if(nmg->attachDir)
+ fs_give((void **)&nmg->attachDir);
+ for(i = 0; i < NUMPRCFEATS; i++){
+ if(nmg->prcfeats[i]->var)
+ fs_give((void **)&nmg->prcfeats[i]->var);
+ fs_give((void **)&nmg->prcfeats[i]);
+ }
+ for(i = 0; i < NUMPRCVARS; i++){
+ if(nmg->prcvars[i]->var)
+ fs_give((void **)&nmg->prcvars[i]->var);
+ if(nmg->prcvars[i]->islist){
+ if(nmg->prcvars[i]->val.l){
+ for(j = 0; nmg->prcvars[i]->val.l[j]; j++)
+ fs_give((void **)&nmg->prcvars[i]->val.l[j]);
+ fs_give((void **)&nmg->prcvars[i]->val.l);
+ }
+ }
+ else {
+ if(nmg->prcvars[i]->val.p)
+ fs_give((void **)&nmg->prcvars[i]->val.p);
+ }
+ fs_give((void **)&nmg->prcvars[i]);
+ }
+ for(ts = nmg->sessionlist; ts;){
+ if(ts->open_stream)
+ ts->open_stream = mail_close_full(ts->open_stream, NIL);
+ ts = free_sessionlist_node(ts);
+ }
+ if(nmg->debugFile)
+ fs_give((void **)&nmg->debugFile);
+ if(nmg->debug && nmg->dfd)
+ fclose(nmg->dfd);
+ nmg->debug = FALSE;
+ fs_give((void **)&nmg);
+}
+
+MAILSTREAM *first_open(sessionlist_s *cs)
+{
+ mapi_global_s *nmg = ms_global;
+ /* cs->mhwnd = (HWND)ulUIParam;
+ * if(flFlags & MAPI_LOGON_UI)
+ * cs->flags.mapi_logon_ui = TRUE;
+ */
+ /* if someone is logging in right now, return failure */
+ if(nmg->cs) return NULL;
+ if(MSDEBUG)
+ fprintf(nmg->dfd, "Opening mailbox for the first time\r\n");
+ if(nmg->prcvars[INBOX_PATH]->val.p == NULL){
+ ErrorBox("No value set for %s!", "inbox");
+ return NULL;
+ }
+ cs->open_stream = mapi_mail_open(cs, cs->open_stream, nmg->prcvars[INBOX_PATH]->val.p,
+ nmg->debug ? OP_DEBUG : NIL);
+ /* cs->flags.mapi_logon_ui = FALSE; */
+ if(cs->open_stream){
+ if(cs->currently_open){
+ fs_give((void **)&cs->currently_open);
+ cs->currently_open = NULL;
+ }
+ cs->currently_open = mstrdup(nmg->prcvars[INBOX_PATH]->val.p);
+ if(nmg->debug)
+ mail_debug(cs->open_stream);
+ if(MSDEBUG){
+ fprintf(nmg->dfd, "returning SUCCESS_SUCCESS\r\n");
+ _flushall();
+ }
+ return cs->open_stream;
+ }
+ else if(cs->flags.dlg_cancel){
+ if(MSDEBUG){
+ fprintf(nmg->dfd, "returning MAPI_E_FAILURE\r\n");
+ _flushall();
+ }
+ return NULL;
+ }
+ else{
+ cs->dlge.edit1[0] = '\0';
+ cs->dlge.edit2[0] = '\0';
+ if(cs->currently_open){
+ fs_give((void **)&cs->currently_open);
+ cs->currently_open = NULL;
+ }
+ if(MSDEBUG){
+ fprintf(nmg->dfd, "returning MAPI_E_FAILURE\r\n");
+ _flushall();
+ }
+ return NULL;
+ }
+}
+
+SENDSTREAM *
+mapi_smtp_open(sessionlist_s *cs, char **servers, long options)
+{
+ mapi_global_s *nmg = ms_global;
+ SENDSTREAM *newstream;
+ pw_cache_s *tpwc, *dpwc;
+
+ nmg->cs = cs;
+ nmg->tmpmbxptr = NULL;
+ nmg->cs->flags.dlg_cancel = 0;
+ newstream = smtp_open(servers, options);
+ nmg->cs = NULL;
+ nmg->tmpmbxptr = NULL;
+
+ if(newstream){ /* if open stream, valid password */
+ for(tpwc = cs->pwc; tpwc; tpwc = tpwc->next)
+ tpwc->validpw = 1;
+ }
+ else{
+ for(tpwc = cs->pwc, dpwc = NULL; tpwc; dpwc = tpwc, tpwc = tpwc->next){
+ if(tpwc->validpw == 0){
+ if(tpwc == cs->pwc)
+ cs->pwc = tpwc->next;
+ else
+ dpwc->next = tpwc->next;
+ fs_give((void **)&tpwc);
+ }
+ }
+ }
+ return(newstream);
+}
+
+MAILSTREAM *
+mapi_mail_open(sessionlist_s *cs, MAILSTREAM *stream, char *name, long options)
+{
+ MAILSTREAM *newstream;
+ mapi_global_s *nmg = ms_global;
+ pw_cache_s *tpwc, *dpwc;
+
+ nmg->cs = cs;
+ nmg->tmpmbxptr = name;
+ nmg->cs->flags.dlg_cancel = 0;
+ newstream = mail_open(stream, name, options);
+ nmg->tmpmbxptr = NULL;
+ nmg->cs = NULL;
+
+ if(newstream){ /* if open stream, valid password */
+ for(tpwc = cs->pwc; tpwc; tpwc = tpwc->next)
+ tpwc->validpw = 1;
+ }
+ else{
+ tpwc = cs->pwc;
+ while(tpwc){
+ if(tpwc->validpw == 0){
+ dpwc = tpwc;
+ tpwc = tpwc->next;
+ if(dpwc == cs->pwc)
+ cs->pwc = dpwc->next;
+ fs_give((void **)&dpwc);
+ }
+ else
+ tpwc = tpwc->next;
+ }
+ }
+
+ return (newstream);
+}
+
+
+MAILSTREAM *check_mailstream(sessionlist_s *cs)
+{
+ mapi_global_s *nmg;
+
+ nmg = ms_global;
+
+ if(!cs->open_stream){
+ return(first_open(cs));
+ }
+ cs->flags.check_stream = TRUE;
+ if(!mail_ping(cs->open_stream)){
+ if(nmg->cs) return NULL;
+ cs->open_stream = mapi_mail_open(cs, cs->open_stream,
+ cs->currently_open ? cs->currently_open :
+ nmg->prcvars[INBOX_PATH]->val.p,
+ nmg->debug ? OP_DEBUG : NIL);
+ if(!cs->open_stream){
+ fs_give((void **)&cs->currently_open);
+ cs->currently_open = NULL;
+ cs->dlge.edit1[0] = '\0';
+ cs->dlge.edit2[0] = '\0';
+ cs->flags.check_stream = FALSE;
+ return NULL;
+ }
+ }
+ cs->flags.check_stream = FALSE;
+ return cs->open_stream;
+}
+
+/* pretty much changes a string to an integer,
+ * but if it is not a valid message number, then 0 is returned
+ */
+unsigned long convert_to_msgno(char *msgid)
+{
+ unsigned long place_holder = 1, msgno = 0;
+ int i, len;
+
+ len = strlen(msgid);
+ for(i = 0; i < len; i++){
+ if(msgid[len-1-i] - '0' < 0 || msgid[len-1-i] - '0' > 9)
+ return 0;
+ msgno += (msgid[len - 1 - i] - '0')*place_holder;
+ place_holder *= 10;
+ }
+
+ return msgno;
+}
+
+/*
+ * Lookup file's mimetype by its file extension
+ * fn - filename
+ * body - body in which to store new type, subtype
+ *
+ * A mime type is ALWAYS set
+ *
+ * Returns 0 if the file extension was found and mimetype was set accordingly
+ * 1 if otherwise
+ */
+int lookup_file_mime_type(char *fn, BODY *body)
+{
+ char *p, subkey[1024], val[1024];
+ DWORD dtype, vallen = 1024;
+ HKEY hKey;
+ int i, rv = 0;
+
+ if(body->subtype)
+ fs_give((void **)&body->subtype);
+ if((p = strrchr(fn, '.')) && p[1]){
+ sprintf(subkey, "%.1020s", p);
+ if(RegOpenKeyEx(HKEY_CLASSES_ROOT, subkey, 0, KEY_READ, &hKey) == ERROR_SUCCESS){
+ if(RegQueryValueEx(hKey, "Content Type", NULL, &dtype, val, &vallen) == ERROR_SUCCESS){
+ RegCloseKey(hKey);
+ if((p = strrchr(val, '/')) && p[1]){
+ *(p++) = '\0';
+ body->subtype = mstrdup(p);
+ for(i=0; (i <= TYPEMAX) && body_types[i] && _stricmp(val, body_types[i]); i++);
+ if(i > TYPEMAX)
+ i = TYPEOTHER;
+ else if(!body_types[i])
+ body_types[i] = mstrdup(val);
+ body->type = i;
+ return 0;
+ }
+ }
+ }
+ }
+ body->type = TYPEAPPLICATION;
+ body->subtype = "octet-stream";
+ return 1;
+}
+
+int LookupMIMEFileExt(char *val, char *mime_type, DWORD *vallen)
+{
+ HKEY hKey;
+ DWORD dtype;
+ LONG rv = !ERROR_SUCCESS;
+ char subkey[1024];
+
+ sprintf(subkey, "MIME\\Database\\Content Type\\%s", mime_type);
+ if(RegOpenKeyEx(HKEY_CLASSES_ROOT, subkey, 0, KEY_READ, &hKey) == ERROR_SUCCESS){
+ rv = RegQueryValueEx(hKey,"extension",NULL, &dtype, val, vallen);
+ RegCloseKey(hKey);
+ }
+
+ return(rv);
+}
+
+/*
+ * xlate_out() - xlate_out the given character
+ */
+char
+xlate_out(c)
+ char c;
+{
+ register int dti;
+ register int xch;
+
+ if((c >= FIRSTCH) && (c <= LASTCH)){
+ xch = c - (dti = xlate_key);
+ xch += (xch < FIRSTCH-TABSZ) ? 2*TABSZ : (xch < FIRSTCH) ? TABSZ : 0;
+ dti = (xch - FIRSTCH) + dti;
+ dti -= (dti >= 2*TABSZ) ? 2*TABSZ : (dti >= TABSZ) ? TABSZ : 0;
+ xlate_key = dti;
+ return(xch);
+ }
+ else
+ return(c);
+}
+
+/* return TRUE if the pwd is found, FALSE if not */
+int in_passfile(sessionlist_s *cs)
+{
+ mapi_global_s *nmg;
+ char *tf, *tp, *ui[4], tmp[1024], *dir;
+ int i, j, n;
+ FILE *tfd;
+
+ nmg = ms_global;
+
+ if(*nmg->pinerc == '{')
+ dir = nmg->pineExe;
+ else
+ dir = nmg->pinerc;
+
+ /* if(nmg->flags.passfile_checked) return FALSE; */
+ if(!(tf = (char *)fs_get(sizeof(char)*(strlen(dir) + strlen("pine.pwd") + 1)))){
+ /* nmg->flags.passfile_checked = TRUE; */
+ return FALSE;
+ }
+ strcpy(tf,dir);
+ if(tp = strrchr(tf, '\\')){
+ tp++;
+ strcpy(tp, "pine.pwd");
+ }
+ else /* don't know when this will ever happen */
+ strcpy(tf, "pine.pwd");
+ if(_access(tf, 00) == 0){
+ if(MSDEBUG)
+ fprintf(nmg->dfd,"found %s for passwords\r\n", tf);
+ if(!(tfd = fopen(tf,"r"))){
+ fs_give((void **)&tf);
+ /* nmg->flags.passfile_checked = TRUE; */
+ return FALSE;
+ }
+ else{
+ for(n = 0; fgets(tmp, 1024, tfd); n++){
+ /*** do any necessary DEcryption here ***/
+ xlate_key = n;
+ for(i = 0; tmp[i]; i++)
+ tmp[i] = xlate_out(tmp[i]);
+
+ if(i && tmp[i-1] == '\n')
+ tmp[i-1] = '\0'; /* blast '\n' */
+
+ ui[0] = ui[1] = ui[2] = ui[3] = NULL;
+ for(i = 0, j = 0; tmp[i] && j < 4; j++){
+ for(ui[j] = &tmp[i]; tmp[i] && tmp[i] != '\t'; i++)
+ ;
+
+ if(tmp[i])
+ tmp[i++] = '\0';
+ }
+
+ if(ui[0] && ui[1] && ui[2]){
+ if(strcmp(ui[2], cs->mb->host) == 0){
+ if((cs->mb->altflag && ui[3] && *ui[3] == '1')
+ || (!cs->mb->altflag && (!ui[3] || (*ui[3] == '0')))){
+ if(strcmp(ui[1], *cs->mb->user ? cs->mb->user
+ : nmg->prcvars[USER_ID]->val.p) == 0){
+ /* winner */
+ strcpy(cs->dlge.edit1, *cs->mb->user ? cs->mb->user
+ : nmg->prcvars[USER_ID]->val.p);
+ strcpy(cs->dlge.edit2, ui[0]);
+ fclose(tfd);
+ fs_give((void **)&tf);
+ /* nmg->flags.passfile_checked = TRUE; */
+ return TRUE;
+ }
+ }
+ }
+ }
+ }
+ fclose(tfd);
+ fs_give((void **)&tf);
+ }
+ }
+ else{
+ fs_give((void **)&tf);
+ /* nmg->flags.passfile_checked = TRUE; */
+ return FALSE;
+ }
+ /* nmg->flags.passfile_checked = TRUE; */
+ return FALSE;
+}
+
+int get_suggested_directory(char *dir)
+{
+ char *tmpdir;
+
+ if(tmpdir = getenv("TEMP")){
+ strcpy(dir, tmpdir);
+ return TRUE;
+ }
+ else if(tmpdir = getenv("TMP")){
+ strcpy(dir, tmpdir);
+ return TRUE;
+ }
+ else if(ms_global && ms_global->attachDir){
+ strcpy(dir, ms_global->attachDir);
+ return TRUE;
+ }
+ else{ /* should NEVER get here */
+ strcpy(dir, "C:\\");
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/* return TRUE if file_ext is modified, FALSE if not */
+int get_suggested_file_ext(char *file_ext, PART *part, DWORD *file_extlen)
+{
+ char mime_type[1024], *tmp_ext;
+ int rv = !ERROR_SUCCESS;
+ PARAMETER *param;
+
+ if(part->body.subtype){
+ sprintf(mime_type, "%s/%s", body_types[part->body.type], part->body.subtype);
+ rv = LookupMIMEFileExt(file_ext, mime_type, file_extlen);
+ }
+ if(rv == ERROR_SUCCESS)
+ return TRUE;
+ else{
+ param = part->body.parameter;
+ while(param && (_stricmp("NAME", param->attribute)))
+ param = param->next;
+ if(!param){
+ if(part->body.type == TYPEMESSAGE){
+ /* don't try to recurse through attached messages yet */
+ strcpy(file_ext, ".txt");
+ return TRUE;
+ }
+ }
+ tmp_ext = strrchr(param->value, (int)'.');
+ if(!tmp_ext) return FALSE;
+ strcpy(file_ext, tmp_ext);
+ }
+ return TRUE;
+}
+
+/* return -1 for failure */
+int InitDebug()
+{
+ char path[1024];
+
+ if(!ms_global){
+ if((ms_global = new_mapi_global()) == NULL) return -1;
+ }
+ /*
+ * if debug file exists, turn on debugging mode
+ */
+ if(ms_global->debug == 1) /* debug file already initialized, somehow */
+ return 1;
+ get_suggested_directory(path);
+ if(path[strlen(path-1)] != '\\')
+ strcat(path, "\\");
+ strcat(path, "mapi_debug.txt");
+ if(_access(path, 00) == 0){
+ ms_global->debug = 1;
+ }
+ else{
+ get_suggested_directory(path);
+ if(path[strlen(path-1)] != '\\')
+ strcat(path, "\\");
+ strcat(path, "mapisend");
+ if(_access(path, 00) == 0){
+ ms_global->debug = 1;
+ }
+ }
+
+ if(ms_global->debug){
+ ms_global->dfd = fopen(path, "wb");
+ if(!ms_global->dfd){
+ ErrorBox("MAPISendMail: debug off: can't open debug file %.200s",
+ path);
+ ms_global->debug = 0; /* can't open the file, turn off debugging */
+ }
+ else if(ms_global->debug == 1){
+ ms_global->debugFile = (char *)fs_get((1+strlen(path))*sizeof(char));
+ strcpy(ms_global->debugFile, path);
+ }
+ }
+
+ if(ms_global->debug && (ms_global->dfd == NULL))
+ ms_global->debug = 0;
+
+ return ms_global->debug;
+}
+
+int GetPineData()
+{
+ HKEY pineKey;
+ BYTE pineKeyData[1024];
+ DWORD pineKeyDataSize;
+ DWORD pineKeyDataType;
+ char *defPath = "c:\\pine\\pine.exe";
+ char *pineExe = strrchr(defPath, '\\')+1;
+ char *freepineExe = NULL;
+ char *defAttachDir = "c:\\tmp";
+ char *penv = NULL;
+
+ /*
+ * get name of and path to pine.exe from registry
+ */
+ if (RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ "SOFTWARE\\University of Washington\\Alpine\\1.0",
+ 0,
+ KEY_QUERY_VALUE,
+ &pineKey) == ERROR_SUCCESS) {
+ pineKeyDataSize = sizeof(pineKeyData);
+ if (RegQueryValueEx(
+ pineKey,
+ "PineEXE",
+ 0,
+ &pineKeyDataType,
+ pineKeyData,
+ &pineKeyDataSize) == ERROR_SUCCESS) {
+ freepineExe = (char *)fs_get((pineKeyDataSize + 1) * sizeof(char));
+ if ((pineExe = freepineExe) != NULL) {
+ strcpy(pineExe, pineKeyData);
+ }
+ else {
+ ErrorBox("MAPISendMail: can't fs_get %d bytes for pineExe",
+ pineKeyDataSize);
+ return 0;
+ }
+ if (MSDEBUG) {
+ fprintf(ms_global->dfd,"pine.exe pineKeyDataSize: %d\r\n", pineKeyDataSize);
+ fprintf(ms_global->dfd,"pine.exe pineKeyData: %s\r\n", pineKeyData);
+ }
+ }
+ pineKeyDataSize = sizeof(pineKeyData);
+ if (RegQueryValueEx(
+ pineKey,
+ "pinedir",
+ 0,
+ &pineKeyDataType,
+ pineKeyData,
+ &pineKeyDataSize) == ERROR_SUCCESS) {
+ ms_global->pineExe = (char *)fs_get(sizeof(char)*(pineKeyDataSize+strlen(pineExe)));
+ if (ms_global->pineExe) {
+ strncpy(ms_global->pineExe, pineKeyData, pineKeyDataSize);
+ strcat(ms_global->pineExe, pineExe);
+ }
+ else {
+ ErrorBox("MAPISendMail: can't fs_get %d bytes for av[0]",
+ pineKeyDataSize);
+ return 0;
+ }
+ if (MSDEBUG) {
+ fprintf(ms_global->dfd,"pine.exe pineKeyDataSize: %d\r\n", pineKeyDataSize);
+ fprintf(ms_global->dfd,"pine.exe pineKeyData: %s\r\n", pineKeyData);
+ }
+ }
+ RegCloseKey(pineKey);
+ }
+ if(!ms_global->pineExe){
+ ms_global->pineExe = (char *)fs_get((1+strlen(defPath))*sizeof(char));
+ if(!ms_global->pineExe){
+ ErrorBox("Couldn't fs_get for %s","pineExe");
+ return 0;
+ }
+ else
+ strcpy(ms_global->pineExe, defPath);
+ }
+
+ if(freepineExe)
+ ms_global->pineExeAlt = freepineExe;
+ else{
+ ms_global->pineExeAlt = (char *)fs_get((strlen(strrchr(defPath, '\\')+1)+1)*sizeof(char));
+ if(!ms_global->pineExeAlt){
+ ErrorBox("Couldn't fs_get for %s","pineExeAlt");
+ return 0;
+ }
+ else
+ strcpy(ms_global->pineExeAlt, strrchr(defPath, '\\')+1);
+ }
+
+ /*
+ * get path to pinerc from registry
+ */
+ if (RegOpenKeyEx(
+ HKEY_CURRENT_USER,
+ "Software\\University of Washington\\Alpine\\1.0",
+ 0,
+ KEY_QUERY_VALUE,
+ &pineKey) == ERROR_SUCCESS) {
+ pineKeyDataSize = sizeof(pineKeyData);
+ if( RegQueryValueEx(
+ pineKey,
+ "PineRC",
+ 0,
+ &pineKeyDataType,
+ pineKeyData,
+ &pineKeyDataSize) == ERROR_SUCCESS) {
+ if(*pineKeyData != '{' || ms_global->pineExe)
+ ms_global->attachDir = (char *)fs_get(sizeof(char)*(*pineKeyData == '{' ?
+ pineKeyDataSize + 1 :
+ strlen(ms_global->pineExe)+1));
+ ms_global->pinerc = (char *)fs_get(pineKeyDataSize);
+ if(ms_global->attachDir){
+ char *p;
+ if(*pineKeyData != '{'){
+ strncpy(ms_global->attachDir, pineKeyData, pineKeyDataSize);
+ ms_global->attachDir[pineKeyDataSize] = '\0';
+ }
+ else
+ strcpy(ms_global->attachDir, ms_global->pineExe);
+ p = strrchr(ms_global->attachDir, '\\');
+ if (p) *p = '\0';
+ }
+ if(ms_global->pinerc)
+ strncpy(ms_global->pinerc, pineKeyData, pineKeyDataSize);
+ else {
+ ErrorBox("MAPISendMail: can't fs_get %d bytes for pinercPath",
+ pineKeyDataSize);
+ return 0;
+ }
+ if (MSDEBUG) {
+ fprintf(ms_global->dfd, "pinerc pineKeyDataSize: %d\r\n", pineKeyDataSize);
+ fprintf(ms_global->dfd, "pinerc pineKeyData: %s\r\n", pineKeyData);
+ fprintf(ms_global->dfd, "attachDir: %s\r\n",
+ ms_global->attachDir ? ms_global->attachDir :
+ "NOT YET DEFINED");
+ }
+ }
+ pineKeyDataSize = sizeof(pineKeyData);
+ if( RegQueryValueEx(
+ pineKey,
+ "PineConf",
+ 0,
+ &pineKeyDataType,
+ pineKeyData,
+ &pineKeyDataSize) == ERROR_SUCCESS){
+ ms_global->pineconf = mstrdup(pineKeyData);
+ msprint1("ms_global->pineconf: %s (due to Registry setting)\r\n", ms_global->pineconf);
+ }
+ RegCloseKey(pineKey);
+ }
+
+ if(ms_global->attachDir == NULL){
+ if(ms_global->attachDir = (char *)fs_get((strlen(defAttachDir)+1)*sizeof(char)))
+ strcpy(ms_global->attachDir, defAttachDir);
+ else
+ ErrorBox("Can't find TEMP directory for %s!","attachments");
+ }
+
+
+ if(penv = getenv("PINERC")){
+ if(ms_global->pinerc)
+ fs_give((void **)&ms_global->pinerc);
+ if(ms_global->pinerc = (char *)fs_get((strlen(penv)+1)*sizeof(char)))
+ strcpy(ms_global->pinerc, penv);
+ else
+ ErrorBox("Couldn't fs_get for %s", "pinerc");
+ }
+ if(penv = getenv("PINECONF")){
+ if(ms_global->pineconf)
+ fs_give((void **)&ms_global->pineconf);
+ if(ms_global->pineconf = (char *)fs_get((strlen(penv)+1)*sizeof(char)))
+ strcpy(ms_global->pineconf, penv);
+ else
+ ErrorBox("Couldn't fs_get for %s", "pineconf");
+ }
+ else{
+
+ }
+ if(penv = getenv("PINERCEX")){
+ if(ms_global->pinercex)
+ fs_give((void **)&ms_global->pinercex);
+ if(ms_global->pinercex = mstrdup(penv))
+ strcpy(ms_global->pinercex, penv);
+ else
+ ErrorBox("Couldn't fs_get for %s", "pinercex");
+ }
+ if(MSDEBUG){
+ fprintf(ms_global->dfd,"ms_global->pineExe: %s\r\n",
+ (ms_global->pineExe) ? ms_global->pineExe : "NULL");
+ fprintf(ms_global->dfd,"ms_global->pineExeAlt: %s\r\n",
+ ms_global->pineExeAlt ? ms_global->pineExeAlt : "NULL");
+ fprintf(ms_global->dfd,"ms_global->attachDir: %s\r\n",
+ ms_global->attachDir ? ms_global->attachDir : "NULL");
+ fprintf(ms_global->dfd,"ms_global->pinerc: %s\r\n",
+ ms_global->pinerc ? ms_global->pinerc : "NULL");
+ fprintf(ms_global->dfd,"ms_global->pineconf: %s\r\n",
+ ms_global->pinerc ? ms_global->pineconf : "NULL");
+ fprintf(ms_global->dfd,"ms_global->pinercex: %s\r\n",
+ ms_global->pinerc ? ms_global->pinercex : "NULL");
+ }
+ return 1;
+}
+
+BOOL APIENTRY DllMain(
+ HANDLE hInst,
+ DWORD ul_reason_being_called,
+ LPVOID lpReserved)
+{
+ switch(ul_reason_being_called){
+ case DLL_THREAD_ATTACH:
+ /* if(ms_global)
+ * return 1;
+ */
+ case DLL_PROCESS_ATTACH:
+ if(!ms_global)
+ ms_global = new_mapi_global();
+ if(!ms_global) return 0;
+ ms_global->attached++;
+ ms_global->mhinst = hInst;
+ if(InitDebug() == -1){
+ ErrorBox("Mapi32.dll could not %s", "initialize");
+ return 0;
+ }
+ if(MSDEBUG && ms_global->attached <= 1){
+ time_t now;
+ struct tm *tm_now;
+ extern char datestamp[], hoststamp[];
+
+ now = time((time_t *)0);
+ tm_now = localtime(&now);
+ fprintf(ms_global->dfd, "pmapi32.dll for Alpine Version 2.10r\n");
+ fprintf(ms_global->dfd, " Build date: %s\r\n", datestamp);
+ fprintf(ms_global->dfd,
+ " please report all bugs to alpine-contact@u.washington.edu\r\n");
+ if(tm_now)
+ fprintf(ms_global->dfd,
+ "Created: %2.2d:%2.2d:%2.2d %d/%d/%d\r\n",
+ tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec,
+ tm_now->tm_mon+1, tm_now->tm_mday, tm_now->tm_year+1900);
+
+ fprintf(ms_global->dfd, "\r\n\r\n");
+ }
+ DEBUG_WRITE("%s called. Debug initialized (in DllMain)\r\n",
+ ul_reason_being_called == DLL_PROCESS_ATTACH ?
+ "DLL_PROCESS_ATTACH":"DLL_THREAD_ATTACH");
+ GetPineData();
+#include "../c-client-dll/linkage.c"
+ break;
+ case DLL_PROCESS_DETACH:
+ case DLL_THREAD_DETACH:
+ DEBUG_WRITE("\r\n%s called\r\n",
+ ul_reason_being_called == DLL_PROCESS_DETACH ?
+ "DLL_PROCESS_DETACH" : "DLL_THREAD_DETACH");
+ ms_global->attached--;
+ /* if(ms_global->open_stream)
+ * ms_global->open_stream = mail_close_full(ms_global->open_stream, NIL);
+ */
+ if(ms_global->attached <= 0 &&
+ ul_reason_being_called == DLL_PROCESS_DETACH){
+ if(MSDEBUG)
+ fprintf(ms_global->dfd,
+ "detaching last thread/process. freeing mapi global struct\r\n");
+ free_mapi_global(ms_global);
+ }
+ break;
+ }
+ return 1;
+}
+
+static char *V="\r\n@(#) Alpine Simple Mapi Library Ver. 1.3\r\n";
+
+int
+UnderlineSpace(char *s)
+{
+ char *p;
+
+ if(p = strrchr(s, '\\'))
+ s = p++;
+
+ for(; *s; s++)
+ if(*s == ' ')
+ *s = '_';
+ return 1;
+}
+
+/*
+ * Given source file name and destination directory, make a binary copy
+ * of the file and return the full name of the copy (mangled as necessary
+ * to avoid conflicts). The return value will be a fs_get'd string
+ */
+char *
+TmpCopy(char *srcFile, int is_pinerc)
+{
+ char *dstName; /* constructed and fs_get'd full output pathname */
+ char *srcTail; /* last component of source pathname */
+ char *srcExt; /* extension, if any, of srcTail */
+ char dstDir[1024];
+ int i, cnt, c, len, spc = 0;
+ FILE *sfd, *dfd;
+
+ if (!srcFile) {
+ ErrorBox("TmpCopy: srcFile is %s", "NULL");
+ return NULL;
+ }
+ if(is_pinerc){
+ len = strlen(srcFile);
+ for(i = 0; i < len; i++){
+ if(srcFile[i] == ' ') spc = 1;
+ }
+ if(spc == 0) return mstrdup(srcFile);
+ }
+
+ get_suggested_directory(dstDir);
+ if (!dstDir) {
+ ErrorBox("TmpCopy: dstDir is %s", "NULL");
+ return NULL;
+ }
+
+ dstName = (char *)fs_get(sizeof(char)*(strlen(srcFile) + 5 +
+ max(strlen(dstDir), strlen(PINERC_FILE))));
+
+ if (dstName == NULL) {
+ ErrorBox("TmpCopy: can't fs_get space %d bytes for dstName",
+ strlen(srcFile)+5+max(strlen(dstDir),strlen(PINERC_FILE)));
+ return NULL;
+ }
+
+ if(!is_pinerc){
+ srcTail = strrchr(srcFile, '\\');
+ if (srcTail)
+ ++srcTail;
+ else
+ srcTail = srcFile;
+
+ srcExt = strrchr(srcTail, '.');
+
+ sfd = fopen(srcFile, "rb");
+ if (sfd == NULL) {
+ ErrorBox("TmpCopy: can't open %.200s for reading", srcFile);
+ fs_give((void **)&dstName);
+ return NULL;
+ }
+
+ i = sprintf(dstName, "%s%s%s", dstDir,
+ dstDir[strlen(dstDir)-1] == '\\' ? "" : "\\",
+ srcTail);
+ UnderlineSpace(dstName);
+ for (cnt = 0; cnt < 1000; ++cnt) {
+ int handle = _open(dstName, _O_CREAT|_O_EXCL , _S_IREAD|_S_IWRITE);
+ if (handle != -1) {
+ if (_close(handle)) /* this shouldn't be able to happen */
+ ErrorBox("TmpCopy: _close of new %.200s failed", dstName);
+ dfd = fopen(dstName, "wb");
+ if (dfd) break;
+ }
+ if (srcExt)
+ sprintf(dstName+i-strlen(srcExt), "%03d%s", cnt, srcExt);
+ else
+ sprintf(dstName+i, "%03d", cnt);
+ }
+ if (dfd == NULL) {
+ ErrorBox("TmpCopy: can't create anything like %.200s", dstName);
+ fclose(sfd);
+ fs_give((void **)&dstName);
+ return NULL;
+ }
+ }
+ else{ /* is_pinerc */
+ i = sprintf(dstName, "%s%s%s", dstDir,
+ dstDir[strlen(dstDir)-1] == '\\' ? "" : "\\",
+ PINERC_FILE);
+ dfd = fopen(dstName, "wb");
+ if(!dfd){
+ ErrorBox("Couldn't create temp %s for pine", "pinerc");
+ fs_give((void **)&dstName);
+ return NULL;
+ }
+ sfd = fopen(srcFile, "rb");
+ if (sfd == NULL) {
+ ErrorBox("TmpCopy: can't open %.200s for reading", srcFile);
+ fclose(dfd);
+ fs_give((void **)&dstName);
+ return NULL;
+ }
+ }
+ c = fgetc(sfd);
+ while(feof(sfd) == 0) {
+ putc(c, dfd);
+ c = fgetc(sfd);
+ }
+ if (ferror(dfd)) {
+ ErrorBox("TmpCopy: write error on %.200s", dstName);
+ fs_give((void **)&dstName);
+ fclose(dfd);
+ return NULL;
+ }
+ if (ferror(sfd)) {
+ ErrorBox("TmpCopy: read error on %.200s", srcFile);
+ fs_give((void **)&dstName);
+ fclose(sfd);
+ return NULL;
+ }
+ if (fclose(sfd)) {
+ ErrorBox("TmpCopy: fclose error on %.200s", srcFile);
+ }
+ if (fclose(dfd)) {
+ ErrorBox("TmpCopy: fclose error on %.200s", dstName);
+ }
+
+ return dstName;
+}
+
+int send_documents(char *files, char sep)
+{
+ int ac, i, tmplen, j;
+ char **av, *tmpfiles, *file, *tmpfree;
+ mapi_global_s *nmg;
+
+ nmg = ms_global;
+ ac = 3;
+ tmplen = strlen(files);
+ tmpfiles = (char *)fs_get(sizeof(char)*(tmplen + 1));
+ strcpy(tmpfiles,files);
+ for(i = 0; i <= tmplen; i++){
+ if(files[i] == sep || files[i] == '\0')
+ ac += 2;
+ }
+ ac += 2; /* just for safe measure */
+ av = (char **)fs_get(ac * sizeof(char *));
+ if(nmg->pinerc){
+ av[1] = mstrdup("-p");
+ /* copy pinerc to temp directory just in case it too
+ * has spaces in its directory
+ */
+ if(tmpfree = TmpCopy(nmg->pinerc, IS_PINERC)){
+ av[2] = quote(tmpfree);
+ fs_give((void **)&tmpfree);
+ }
+ else
+ av[2] = quote(nmg->pinerc);
+ }
+ for(i = 0, j = 3, file = tmpfiles; i <= tmplen; i++){
+ if(tmpfiles[i] == sep || i == tmplen){
+ tmpfiles[i] = '\0';
+ if(i - (file - tmpfiles) > 1){
+ tmpfree = TmpCopy(file, NOT_PINERC);
+ if(tmpfree){
+ av[j++] = mstrdup("-attach_and_delete");
+ av[j++] = quote(tmpfree);
+ fs_give((void **)&tmpfree);
+ }
+ }
+ }
+ }
+ av[j] = NULL;
+ av[0] = quote(nmg->pineExe);
+ if(MSDEBUG){
+ fprintf(ms_global->dfd, "spawning %s (else %s):\r\n",
+ ms_global->pineExe, ms_global->pineExeAlt);
+ fprintf(nmg->dfd, " av:\r\n");
+ for(i = 0; av[i]; i++)
+ fprintf(nmg->dfd, " av[%d]: %s\r\n", i, av[i]);
+ }
+
+ /* clean up quote()'s */
+ if (_spawnvp(_P_NOWAIT, ms_global->pineExe, av) == -1 &&
+ _spawnvp(_P_NOWAIT, ms_global->pineExeAlt, av) == -1){
+ ErrorBox("MAPISendMail: _spawnvp of %s failed", ms_global->pineExe);
+ if(MSDEBUG)
+ fprintf(ms_global->dfd, "_spawnvp %s and %s failed\r\n",
+ ms_global->pineExe,ms_global->pineExeAlt);
+ return(MAPI_E_FAILURE);
+ }
+ for(i = 0; av[i]; i++)
+ fs_give((void **)&av[i]);
+ fs_give((void **)&av);
+ return SUCCESS_SUCCESS;
+}
+
+char *
+message_structure_to_mailto_url(lpMapiMessage lpm)
+{
+ char **keyvals, **keyvalp, *url;
+ int keyvallen;
+ unsigned long i, url_len = 0;
+
+ if(lpm == NULL)
+ return NULL;
+
+ keyvallen = lpm->nRecipCount + 4; /* subject + body + from + recips + NULL */
+ keyvals = (char **)fs_get(keyvallen * sizeof(char *));
+ keyvalp = keyvals;
+
+ for(i = 0; i < lpm->nRecipCount; i++)
+ *keyvalp++ = encode_mailto_addr_keyval(&lpm->lpRecips[i]);
+ if(lpm->lpszSubject)
+ *keyvalp++ = encode_mailto_keyval("subject", lpm->lpszSubject);
+ if(lpm->lpOriginator)
+ *keyvalp++ = encode_mailto_addr_keyval(lpm->lpOriginator);
+ if(lpm->lpszNoteText)
+ *keyvalp++ = encode_mailto_keyval("body", lpm->lpszNoteText);
+ *keyvalp = NULL;
+
+ if(*keyvals == NULL){
+ fs_give((void **)&keyvals);
+ return(NULL);
+ }
+
+ url_len = keyvallen + 10; /* mailto url extra chars */
+ for(keyvalp = keyvals; *keyvalp; keyvalp++)
+ url_len += strlen(*keyvalp);
+
+ url = (char *)fs_get(url_len * sizeof(char));
+ sprintf(url, "mailto:?");
+ for(keyvalp = keyvals; *keyvalp; keyvalp++){
+ strcat(url, *keyvalp);
+ if(*(keyvalp+1))
+ strcat(url, "&");
+ fs_give((void **)&(*keyvalp));
+ }
+ fs_give((void **)&keyvals);
+ return url;
+}
+
+char *
+encode_mailto_addr_keyval(lpMapiRecipDesc lpmr)
+{
+ ADDRESS *adr = NULL;
+ char *addr, *retstr;
+ int use_quotes = 0;
+
+ adr = mapirecip2address(lpmr);
+ addr = (char *)fs_get((size_t)est_size(adr));
+ addr[0] = '\0';
+ rfc822_write_address(addr, adr);
+ mail_free_address(&adr);
+
+ retstr = encode_mailto_keyval(lpmr->ulRecipClass == MAPI_CC ? "cc"
+ : (lpmr->ulRecipClass == MAPI_BCC ? "bcc"
+ : (lpmr->ulRecipClass == MAPI_ORIG ? "from"
+ : "to")),
+ addr);
+ fs_give((void **)&addr);
+ return(retstr);
+}
+
+
+
+/*
+ * Hex conversion aids from alpine.h
+ */
+#define HEX_ARRAY "0123456789ABCDEF"
+#define HEX_CHAR1(C) HEX_ARRAY[((C) & 0xf0) >> 4]
+#define HEX_CHAR2(C) HEX_ARRAY[(C) & 0xf]
+
+/* strings.c macros */
+#define C2XPAIR(C, S) { \
+ *(S)++ = HEX_CHAR1(C); \
+ *(S)++ = HEX_CHAR2(C); \
+ }
+
+#define RFC1738_SAFE "$-_.+" /* "safe" */
+#define RFC1738_EXTRA "!*'()," /* "extra" */
+
+/* adapted from rfc1738_encode_mailto */
+char *
+encode_mailto_keyval(char *key, char* val)
+{
+ char *d, *ret = NULL, *v = val;
+
+ if(key && val){
+ ret = (char *)fs_get(sizeof(char) * (strlen(key) + (3*strlen(val)) + 2));
+ strcpy(ret, key);
+ d = ret + strlen(key);
+ *d++ = '=';
+ while(*v){
+ if(isalnum((unsigned char)*v)
+ || strchr(RFC1738_SAFE, *v)
+ || strchr(RFC1738_EXTRA, *v))
+ *d++ = *v++;
+ else{
+ *d++ = '%';
+ C2XPAIR(*v, d);
+ v++;
+ }
+ }
+ *d = '\0';
+ }
+
+ return(ret);
+}
+
+unsigned long
+send_msg_nodlg(LHANDLE lhSession, ULONG ulUIParam, lpMapiMessage lpMessage,
+ FLAGS flFlags, ULONG ulReserved)
+{
+ sessionlist_s *cs;
+ int tsession = 0, orig_in_recip = 0;
+ unsigned long i, orig_index;
+ ADDRESS *tadr = NULL, *tadr2 = NULL;
+ ENVELOPE *env = NULL;
+ BODY *body = NULL;
+ mapi_global_s *nmg = ms_global;
+ SENDSTREAM *sending_stream = NULL;
+ unsigned long rv;
+ time_t now;
+ struct tm *tm_now;
+ char *p = NULL;
+
+ if(nmg->pmapi_send_behavior == PMSB_NEVER_SEND)
+ return MAPI_E_USER_ABORT;
+ else if(nmg->pmapi_send_behavior == PMSB_ALWAYS_PROMPT){
+ if(MessageBox(NULL, "Really Send Message?", "pmapi32.dll", MB_YESNO|MB_ICONQUESTION) == IDNO)
+ return MAPI_E_USER_ABORT;
+ }
+ if((flFlags & MAPI_NEW_SESSION) || lhSession == 0){
+ cs = new_sessionlist();
+ tsession = 1;
+ }
+ else{
+ cs = get_session(lhSession);
+ if(!cs)
+ return MAPI_E_INVALID_SESSION;
+ }
+ cs->flags.mapi_logon_ui = (flFlags & MAPI_LOGON_UI) || (flFlags & MAPI_DIALOG) ? 1 : 0;
+ if(InitPineSpecific(cs) == -1){
+ rv = MAPI_E_LOGIN_FAILURE;
+ goto smn_cleanup;
+ }
+ msprint("Preparing to Send Message with no dialogs...\r\n");
+ /* Make an envelope */
+ env = (ENVELOPE *)fs_get(sizeof(ENVELOPE));
+ memset(env, 0, sizeof(ENVELOPE));
+ if(lpMessage->lpszSubject){
+ p = rfc1522_encode(tmp_20k_buf, SIZEOF_20KBUF, lpMessage->lpszSubject,
+ nmg->prcvars[CHARACTER_SET]->val.p);
+ env->subject = mstrdup(p);
+ if(MSDEBUG)
+ fprintf(ms_global->dfd, " Subject: %s\r\n", env->subject);
+ }
+ /*
+ * Since it is "DateReceived", I think the right thing to do is ignore it,
+ * since we're sending, not receiving.
+ */
+ rfc822_date(tmp_20k_buf);
+ env->date = mstrdup(tmp_20k_buf);
+ msprint1(" Date: %s\r\n", env->date);
+ env->message_id = pmapi_generate_message_id();
+ msprint1(" Message-Id: %s\r\n", env->message_id);
+
+ for(i = 0; i < lpMessage->nRecipCount; i++){
+ if((&lpMessage->lpRecips[i])->ulRecipClass == MAPI_ORIG){
+ orig_in_recip = 1;
+ orig_index = i;
+ }
+ }
+
+ if(lpMessage->lpOriginator || orig_in_recip){
+ if((env->from = mapirecip2address(lpMessage->lpOriginator
+ ? lpMessage->lpOriginator
+ : (&lpMessage->lpRecips[orig_index])))
+ == NULL){
+ rv = MAPI_E_INVALID_RECIPS;
+ goto smn_cleanup;
+ }
+ if(MSDEBUG){
+ sprintf(tmp_20k_buf, "%.100s <%.100s@%.100s>", env->from->personal ? env->from->personal
+ : "", env->from->mailbox ? env->from->mailbox : "",
+ env->from->host ? env->from->host : "");
+ msprint1("From: %s\r\n", tmp_20k_buf);
+ }
+ }
+ else if(nmg->prcvars[USER_ID]->val.p && nmg->prcvars[USER_DOMAIN]->val.p){
+ /*
+ * judgment call: I guess we'll try to generate the from header if it's not
+ * given to us
+ */
+ env->from = mail_newaddr();
+ if(nmg->prcvars[PERSONAL_NAME]->val.p){
+ p = rfc1522_encode(tmp_20k_buf, SIZEOF_20KBUF, nmg->prcvars[PERSONAL_NAME]->val.p,
+ nmg->prcvars[CHARACTER_SET]->val.p);
+ env->from->personal = mstrdup(p);
+ }
+ env->from->mailbox = mstrdup(nmg->prcvars[USER_ID]->val.p);
+ env->from->host = mstrdup(nmg->prcvars[USER_DOMAIN]->val.p);
+ if(MSDEBUG){
+ sprintf(tmp_20k_buf, "%.100s <%.100s@%.100s>", env->from->personal ? env->from->personal
+ : "", env->from->mailbox ? env->from->mailbox : "",
+ env->from->host ? env->from->host : "");
+ msprint1("From: %s\r\n", tmp_20k_buf);
+ }
+ }
+ for(i = 0; i < lpMessage->nRecipCount; i++){
+ if((tadr = mapirecip2address(&lpMessage->lpRecips[i])) == NULL){
+ rv = MAPI_E_INVALID_RECIPS;
+ goto smn_cleanup;
+ }
+ switch (lpMessage->lpRecips[i].ulRecipClass) {
+ case MAPI_TO:
+ case MAPI_ORIG:
+ if(!env->to)
+ env->to = tadr;
+ else{
+ for(tadr2 = env->to; tadr2->next; tadr2 = tadr2->next);
+ tadr2->next = tadr;
+ }
+ msprint(" To: ");
+ break;
+ case MAPI_CC:
+ if(!env->cc)
+ env->cc = tadr;
+ else{
+ for(tadr2 = env->cc; tadr2->next; tadr2 = tadr2->next);
+ tadr2->next = tadr;
+ }
+ msprint(" Cc: ");
+ break;
+ case MAPI_BCC:
+ if(!env->bcc)
+ env->bcc = tadr;
+ else{
+ for(tadr2 = env->bcc; tadr2->next; tadr2 = tadr2->next);
+ tadr2->next = tadr;
+ }
+ msprint(" Bcc: ");
+ break;
+ default:
+ rv = MAPI_E_INVALID_RECIPS;
+ goto smn_cleanup;
+ break;
+ }
+ if(MSDEBUG){
+ sprintf(tmp_20k_buf, "%.100s <%.100s@%.100s>", tadr->personal ? tadr->personal
+ : "", tadr->mailbox ? tadr->mailbox : "",
+ tadr->host ? tadr->host : "");
+ msprint1("%s\r\n", tmp_20k_buf);
+ }
+ }
+ /* Now we have an envelope, let's make us a body */
+ if(lpMessage->lpszNoteText == NULL)
+ msprint("Empty Message Text\r\n");
+ body = mail_newbody();
+ if(lpMessage->nFileCount){
+ PART *p;
+ struct _stat sbuf;
+ unsigned long fsize, n;
+ FILE *sfd;
+ char c, *fn;
+
+ msprint1("Number of files to be Attached: %d\r\n", (void *)lpMessage->nFileCount);
+ body->type = TYPEMULTIPART;
+ body->nested.part = mail_newbody_part();
+ p = body->nested.part;
+ set_text_data(&p->body, lpMessage->lpszNoteText ? lpMessage->lpszNoteText
+ : "");
+
+ for(i = 0; i < lpMessage->nFileCount; i++){
+ p->next = mail_newbody_part();
+ p = p->next;
+ p->body.encoding = ENCBINARY;
+ if(lpMessage->lpFiles[i].lpszPathName == NULL)
+ return(MAPI_E_FAILURE);
+ if(lpMessage->lpFiles[i].lpszFileName == NULL
+ || lpMessage->lpFiles[i].lpszFileName[0] == '\0'){
+ fn = strrchr(lpMessage->lpFiles[i].lpszPathName, '\\');
+ if(!fn)
+ fn = lpMessage->lpFiles[i].lpszPathName;
+ else
+ fn++;
+ }
+ else
+ fn = lpMessage->lpFiles[i].lpszFileName;
+ if(lookup_file_mime_type(fn, &p->body) && (fn == lpMessage->lpFiles[i].lpszFileName))
+ lookup_file_mime_type(lpMessage->lpFiles[i].lpszPathName, &p->body);
+ msprint1(" Attaching file %s;", lpMessage->lpFiles[i].lpszPathName);
+ if(_stat(lpMessage->lpFiles[i].lpszPathName, &sbuf))
+ return(MAPI_E_FAILURE);
+ fsize = sbuf.st_size;
+ if((sfd = fopen(lpMessage->lpFiles[i].lpszPathName, "rb")) == NULL)
+ return(MAPI_E_FAILURE);
+ p->body.contents.text.data = fs_get((fsize+1)*sizeof(char));
+ n = 0;
+ c = fgetc(sfd);
+ while(feof(sfd) == 0){
+ p->body.contents.text.data[n++] = c;
+ if(n > fsize){
+ fsize += 20000;
+ fs_resize((void **)&p->body.contents.text.data, (fsize+1)*sizeof(char));
+ }
+ c = fgetc(sfd);
+ }
+ fclose(sfd);
+ p->body.contents.text.data[n] = '\0';
+ p->body.contents.text.size = n;
+ msprint1(" File size: %d\r\n", (void *)n);
+ if(fn){
+ p->body.parameter = mail_newbody_parameter();
+ p->body.parameter->attribute = mstrdup("name");
+ p->body.parameter->value = mstrdup(fn);
+
+ p->body.disposition.type = mstrdup("attachment");
+ p->body.disposition.parameter = mail_newbody_parameter();
+ p->body.disposition.parameter->attribute = mstrdup("filename");
+ p->body.disposition.parameter->value = mstrdup(fn);
+ }
+ }
+ }
+ else {
+ set_text_data(body, lpMessage->lpszNoteText ? lpMessage->lpszNoteText
+ : "");
+ msprint1(" Message Body size: %d\r\n", (void *)body->contents.text.size);
+ }
+
+ if(MSDEBUG){
+ now = time((time_t *)0);
+ tm_now = localtime(&now);
+ fprintf(ms_global->dfd, "%2.2d:%2.2d:%2.2d %d/%d/%d ",
+ tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec,
+ tm_now->tm_mon+1, tm_now->tm_mday, tm_now->tm_year+1900);
+ }
+ if(nmg->prcvars[SMTP_SERVER]->val.l && nmg->prcvars[SMTP_SERVER]->val.l[0]
+ && nmg->prcvars[SMTP_SERVER]->val.l[0][0]){
+ if(MSDEBUG){
+ fprintf(ms_global->dfd, "Preparing to open SMTP connection (%s ...)\r\n",
+ nmg->prcvars[SMTP_SERVER]->val.l[0]);
+ _flushall();
+ }
+ sending_stream = mapi_smtp_open(cs, nmg->prcvars[SMTP_SERVER]->val.l,
+ nmg->prcfeats[ENABLE8BIT]->is_set ? SOP_8BITMIME : NIL);
+ }
+ else {
+ rv = MAPI_E_FAILURE;
+ if(MSDEBUG){
+ fprintf(ms_global->dfd, "Error! No SMTP server defined!\r\n");
+ }
+ goto smn_cleanup;
+ }
+ if(!sending_stream){
+ rv = MAPI_E_FAILURE;
+ if(MSDEBUG){
+ fprintf(ms_global->dfd, "Couldn't open SMTP connection!\r\n");
+ }
+ goto smn_cleanup;
+ }
+ if(!smtp_mail(sending_stream, "MAIL", env, body)){
+ if(MSDEBUG){
+ fprintf(ms_global->dfd, "Attempt to Send Failed\r\n");
+ }
+ rv = MAPI_E_FAILURE;
+ }
+ else {
+ if(MSDEBUG){
+ fprintf(ms_global->dfd, "Message SENT!\r\n");
+ }
+ if(!nmg->fccfolder)
+ msprint("No fcc defined\r\n");
+ else { /* Now try to write to fcc */
+ STRBUFFER_S *sb;
+ MAILSTREAM *fccstream;
+ STRING msg;
+
+ msprint1("FCCing to %s\r\n", nmg->fccfolder);
+ sb = (STRBUFFER_S *)fs_get(sizeof(STRBUFFER_S));
+ sb->buf = (char *)fs_get(20000*sizeof(char));
+ sb->cur_bytes = 0;
+ sb->increment = 20000;
+ sb->bufsize = 20000;
+ rfc822_output(tmp_20k_buf, env, body, pmapi_soutr, sb, 1);
+ INIT(&msg, mail_string, (void *)sb->buf, sb->cur_bytes);
+ fccstream = mapi_mail_open(cs, NULL, nmg->fccfolder, ms_global->debug ? OP_DEBUG : NIL);
+ if(fccstream){
+ if(mail_append(fccstream, nmg->fccfolder, &msg) == NIL)
+ msprint1("Fcc to %s failed\r\n", nmg->fccfolder);
+ else
+ msprint1("Fcc to %s SUCCEEDED\r\n", nmg->fccfolder);
+ mail_close(fccstream);
+ }
+ else
+ msprint1("Open of %s failed, abandoning FCC\r\n", nmg->fccfolder);
+ if(sb->buf)
+ fs_give((void **)&sb->buf);
+ if(sb)
+ fs_give((void **)&sb);
+ }
+ if((flFlags & MAPI_LOGON_UI) || (flFlags & MAPI_DIALOG))
+ MessageBox(NULL, "Message SENT!\r\n", "pmapi32.dll", MB_OK|MB_ICONINFORMATION);
+ rv = SUCCESS_SUCCESS;
+ }
+ smtp_close(sending_stream);
+ smn_cleanup:
+ if(env)
+ mail_free_envelope(&env);
+ if(body)
+ mail_free_body(&body);
+ if(tsession)
+ fs_give((void **)&cs);
+ return(rv);
+}
+
+int
+set_text_data(BODY *body, char *txt)
+{
+ char *p;
+ int has_8bit = 0;
+ PARAMETER *pm;
+
+ for(p = txt; *p; p++)
+ if(*p & 0x80)
+ has_8bit++;
+
+ body->contents.text.data = mstrdup(txt);
+ body->contents.text.size = strlen(txt);
+ if(has_8bit){
+ body->encoding = ENC8BIT;
+ if(!body->parameter)
+ pm = body->parameter = mail_newbody_parameter();
+ else {
+ for(pm = body->parameter; pm->next; pm = pm->next);
+ pm->next = mail_newbody_parameter();
+ pm = pm->next;
+ }
+ pm->attribute = mstrdup("charset");
+ if(ms_global->prcvars[CHARACTER_SET]->val.p)
+ pm->value = mstrdup(ms_global->prcvars[CHARACTER_SET]->val.p);
+ else
+ pm->value = mstrdup("X-UNKNOWN");
+ }
+ return 0;
+}
+
+long
+pmapi_soutr(STRBUFFER_S *s, char *str)
+{
+ unsigned long i;
+
+ if(s->cur_bytes >= s->bufsize){
+ fs_resize((void **)&s->buf, s->bufsize+ s->increment);
+ s->bufsize += s->increment;
+ }
+ for(i = 0; str[i]; i++){
+ s->buf[s->cur_bytes++] = str[i];
+ if(s->cur_bytes >= s->bufsize){
+ fs_resize((void **)&s->buf, s->bufsize+ s->increment);
+ s->bufsize += s->increment;
+ }
+ }
+ s->buf[s->cur_bytes] = '\0';
+ return T;
+}
+
+ADDRESS *
+mapirecip2address(lpMapiRecipDesc lpmrd)
+{
+ ADDRESS *adr = NULL;
+ static char *fakedomain = "@", *p;
+ mapi_global_s *nmg = ms_global;
+
+ if(!lpmrd->lpszAddress)
+ return(NULL);
+ rfc822_parse_adrlist(&adr, _strnicmp(lpmrd->lpszAddress, "SMTP:", 5) == 0
+ ? lpmrd->lpszAddress + 5 : lpmrd->lpszAddress,
+ nmg->prcvars[USER_DOMAIN]->val.p
+ ? nmg->prcvars[USER_DOMAIN]->val.p : fakedomain);
+ if(!adr)
+ return(NULL);
+ if(adr->next || adr->error){
+ mail_free_address(&adr);
+ return(NULL);
+ }
+
+ if(lpmrd->lpszName && adr->personal)
+ fs_give((void **)&adr->personal);
+ if(lpmrd->lpszName){
+ p = rfc1522_encode(tmp_20k_buf, SIZEOF_20KBUF, lpmrd->lpszName,
+ nmg->prcvars[CHARACTER_SET]->val.p);
+ adr->personal = mstrdup(p);
+ }
+ return(adr);
+}
+
+/*
+ * given a fs_get'd string, return a newly fs_get'd quoted copy
+ */
+char *
+quote(char *old)
+{
+ char *new, *newp, *oldp;
+ int newSize = strlen(old)*2+3;
+
+ if (!old) return mstrdup(old);
+ if(!strchr(old, ' ')) return mstrdup(old);
+
+ newp = new = (char *)fs_get(sizeof(char)*newSize); /* worst case */
+ if (new == NULL) {
+ ErrorBox("quote: fs_get of %d bytes failed", newSize);
+ return old;
+ }
+
+ *newp++ = '"';
+ for (oldp=old; *oldp; ++oldp) {
+ switch(*oldp) {
+ case '"': *newp++ = '\\'; /* fall through */
+ default : *newp++ = *oldp;
+ }
+ }
+ *newp++ = '"';
+ *newp = '\0';
+ return(new);
+}
+
+
+int GetPCPineVersion(int *major, int *minor, int *minorminor)
+{
+ *major = 4;
+ *minor = 52;
+ *minorminor = 0;
+
+ return 1;
+}
+
+int
+msprint(char *str)
+{
+ if(MSDEBUG)
+ fprintf(ms_global->dfd, "%s", str);
+ _flushall();
+ return 0;
+}
+
+int
+msprint1(char *str, void *arg1)
+{
+ if(MSDEBUG)
+ fprintf(ms_global->dfd, str, arg1);
+ _flushall();
+ return 0;
+}
+
+int
+msprint_message_structure(lpMapiMessage lpm)
+{
+ unsigned long i;
+
+ if(MSDEBUG){
+ msprint1("lpMapiMessage: %p\r\n", lpm);
+ if(!lpm)
+ return 1;
+ msprint1(" ulReserved: %d\r\n", (void *)lpm->ulReserved);
+ msprint1(" lpszSubsect: %s\r\n", lpm->lpszSubject ? lpm->lpszSubject : "(NULL)");
+ msprint1(" lpszNoteText size: %d\r\n", lpm->lpszNoteText
+ ? (void *)strlen(lpm->lpszNoteText) : (void *)0);
+ if(lpm->lpszNoteText)
+ msprint1("\tleading text: %.10s\r\n", lpm->lpszNoteText);
+ msprint1(" lpszMessageType: %s\r\n", lpm->lpszMessageType ? lpm->lpszMessageType : "(NULL)");
+ msprint1(" lpszDateReceived: %s\r\n", lpm->lpszDateReceived ? lpm->lpszDateReceived : "(NULL)");
+ msprint1(" lpszConversationID: %s\r\n", lpm->lpszConversationID ? lpm->lpszConversationID : "(NULL)");
+ msprint1(" flFlags: %d\r\n", (void *)lpm->flFlags);
+ msprint(" Originator:\r\n");
+ msprint_recipient_structure(lpm->lpOriginator, 0);
+ msprint1(" nRecipCount: %d\r\n", (void *)lpm->nRecipCount);
+ for(i = 0; i < lpm->nRecipCount; i++)
+ msprint_recipient_structure(&lpm->lpRecips[i], 1);
+ msprint1(" nFileCount: %d\r\n", (void *)lpm->nFileCount);
+ for(i = 0; i < lpm->nFileCount; i++)
+ msprint_file_structure(&lpm->lpFiles[i]);
+ msprint("\r\n");
+ }
+ return 0;
+}
+
+int
+msprint_recipient_structure(lpMapiRecipDesc lmrd, int mapi_orig_is_unexpected)
+{
+ if(MSDEBUG){
+ msprint1(" lpMapiRecipDesc: %p\r\n", (void *)lmrd);
+ if(lmrd == NULL)
+ return 1;
+ msprint1(" ulReserved: %d\r\n", (void *)lmrd->ulReserved);
+ msprint1(" ulRecipClass: %s\r\n", lmrd->ulRecipClass == MAPI_ORIG ? "MAPI_ORIG"
+ : lmrd->ulRecipClass == MAPI_TO ? "MAPI_TO" : lmrd->ulRecipClass == MAPI_CC
+ ? "MAPI_CC" : "MAPI_BCC");
+ if(mapi_orig_is_unexpected && lmrd->ulRecipClass == MAPI_ORIG){
+ msprint(" # NOTE: it is seemingly strange behavior that a MAPI client would use\r\n # MAPI_ORIG instead of the lpOriginator. This may result in unexpected behavior.\r\n");
+ }
+ msprint1(" lpszName: %s\r\n", lmrd->lpszName ? lmrd->lpszName : "(NULL)");
+ msprint1(" lpszAddress: %s\r\n", lmrd->lpszAddress ? lmrd->lpszAddress : "(NULL)");
+ msprint1(" ulEIDSize: %p\r\n", (void *)lmrd->ulEIDSize);
+ msprint1(" lpEntryID: %p\r\n", (void *)lmrd->lpEntryID);
+ }
+ return 0;
+}
+
+
+int
+msprint_file_structure(lpMapiFileDesc lmfd)
+{
+ if(MSDEBUG){
+ msprint1(" lpMapiFileDesc: %p\r\n", (void *)lmfd);
+ if(lmfd == NULL)
+ return 1;
+ msprint1(" ulReserved: %d\r\n", (void *)lmfd->ulReserved);
+ msprint1(" flFlags: %d\r\n", (void *)lmfd->flFlags);
+ msprint1(" nPosition: %d\r\n", (void *)lmfd->nPosition);
+ msprint1(" lpszPathName: %s\r\n", lmfd->lpszPathName ? lmfd->lpszPathName : "(NULL)");
+ msprint1(" lpszFileName: %s\r\n", lmfd->lpszFileName ? lmfd->lpszFileName : "(NULL)");
+ msprint1(" lpFileType: %p\r\n", (void *)lmfd->lpFileType);
+ }
+ return 0;
+}
+
+
+char *mstrdup(char *old)
+{
+ char *tmp;
+
+ tmp = fs_get((strlen(old)+1) * sizeof(char));
+ strcpy(tmp, old);
+
+ return tmp;
+}
+
+int
+est_size(a)
+ ADDRESS *a;
+{
+ int cnt = 0;
+
+ for(; a; a = a->next){
+
+ /* two times personal for possible quoting */
+ cnt += 2 * (a->personal ? strlen(a->personal) : 0);
+ cnt += (a->mailbox ? strlen(a->mailbox) : 0);
+ cnt += (a->adl ? strlen(a->adl) : 0);
+ cnt += (a->host ? strlen(a->host) : 0);
+
+ /*
+ * add room for:
+ * possible single space between fullname and addr
+ * left and right brackets
+ * @ sign
+ * possible : for route addr
+ * , <space>
+ *
+ * So I really think that adding 7 is enough. Instead, I'll add 10.
+ */
+ cnt += 10;
+ }
+
+ return(max(cnt, 50)); /* just making sure */
+}
+
+void ErrorBoxFunc(char *msg)
+{
+ if(MSDEBUG){
+ fprintf(ms_global->dfd,"ErrorBox: %s\r\n", msg);
+ fclose(ms_global->dfd);
+ ms_global->dfd = fopen(ms_global->debugFile, "ab");
+ if(!ms_global->dfd){
+ MessageBox(NULL, "debug file problems! Debugging turned off.",
+ "mapi32.dll",
+ MB_OK|MB_ICONERROR);
+ ms_global->debug = 0;
+ fs_give((void **)&ms_global->debugFile);
+ ms_global->debugFile = NULL;
+ }
+ }
+ MessageBox(NULL, msg, "mapi32.dll", MB_OK|MB_ICONERROR);
+}
+
+/*----------------------------------------------------------------------
+ This was borrowed from reply.c, and modified
+ Generate a unique message id string.
+
+ Args: ps -- The usual pine structure
+
+ Result: Alloc'd unique string is returned
+
+Uniqueness is gaurenteed by using the host name, process id, date to the
+second and a single unique character
+*----------------------------------------------------------------------*/
+char *
+pmapi_generate_message_id()
+{
+ static short osec = 0, cnt = 0;
+ char *id;
+ time_t now;
+ struct tm *now_x;
+
+ now = time((time_t *)0);
+ now_x = localtime(&now);
+ id = (char *)fs_get(128 * sizeof(char));
+
+ if(now_x->tm_sec == osec){
+ cnt++;
+ }else{
+ cnt = 0;
+ osec = now_x->tm_sec;
+ }
+ sprintf(id,"<Pmapi32.%04d%02d%02d%02d%02d%02d%X.%d@%.50s>",
+ (now_x->tm_year) + 1900, now_x->tm_mon + 1,
+ now_x->tm_mday, now_x->tm_hour, now_x->tm_min, now_x->tm_sec,
+ cnt, getpid(), mylocalhost());
+
+ return(id);
+}
diff --git a/mapi/pmapi.def b/mapi/pmapi.def
new file mode 100644
index 00000000..fb633335
--- /dev/null
+++ b/mapi/pmapi.def
@@ -0,0 +1,17 @@
+LIBRARY pmapi32
+VERSION 4.60
+
+EXPORTS
+ MAPILogon @1
+ MAPILogoff @2
+ MAPIFindNext @3
+ MAPIReadMail @4
+ MAPISendMail @5
+ MAPIAddress @6
+ MAPIDeleteMail @7
+ MAPIDetails @8
+ MAPIFreeBuffer @9
+ MAPIResolveName @10
+ MAPISaveMail @11
+ MAPISendDocuments @12
+ GetPCPineVersion @13
diff --git a/mapi/pmapi.h b/mapi/pmapi.h
new file mode 100644
index 00000000..2a8d28f8
--- /dev/null
+++ b/mapi/pmapi.h
@@ -0,0 +1,244 @@
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "windows.h"
+
+#include "mapi.h"
+#include "io.h"
+#include "fcntl.h"
+#include "sys/stat.h"
+#include "string.h"
+#include "stdio.h"
+#include "errno.h"
+#include "process.h"
+#include "resource.h"
+#include "time.h"
+#undef ERROR
+#include "../c-client-dll/mail.h"
+#include "../c-client-dll/rfc822.h"
+#include "../c-client-dll/osdep.h"
+#include "../c-client-dll/smtp.h"
+#include "../c-client-dll/misc.h"
+#include "ctype.h"
+
+#define USER_ID 0
+#define PERSONAL_NAME 1
+#define USER_DOMAIN 2
+#define SMTP_SERVER 3
+#define INBOX_PATH 4
+#define FEATURE_LIST 5
+#define CHARACTER_SET 6
+#define FOLDER_COLLECTIONS 7
+#define PMAPI_SEND_BEHAVIOR 8
+#define DEFAULT_FCC 9
+#define PMAPI_SUPPRESS_DIALOGS 10
+#define PMAPI_STRICT_NO_DIALOG 11
+#define NUMPRCVARS 12
+#define ENABLE8BIT 0
+#define NUMPRCFEATS 1
+#define PMSB_ALWAYS_PROMPT 0
+#define PMSB_ALWAYS_SEND 1
+#define PMSB_NEVER_SEND 2
+#define PMSD_NO 0
+#define PMSD_YES 1
+#define PMSD_PROMPT 2
+#define errBufSize 300 /* for buffers to sprintf %.200s error messages into */
+#define BUFLEN 1024
+#define EDITLEN 128
+#define MSDEBUG ms_global && ms_global->debug
+#define NOT_PINERC 0
+#define IS_PINERC 1
+#define PINERC_FILE "tmpmapiuwpinerc"
+#define ErrorBox(msg,parm){ \
+ char buf[errBufSize]; \
+ sprintf(buf, msg, parm); \
+ ErrorBoxFunc(buf); \
+ } /* a macro so parm can be a pointer or a value as per % format */
+#define DEBUG_WRITE(msg, parm) { if(MSDEBUG) fprintf(ms_global->dfd,msg,parm);}
+
+#ifdef ANSI
+#define PROTO(args) args
+#else
+#define PROTO(args) ()
+#endif
+
+/* used for pine pwd file */
+#define FIRSTCH 0x20
+#define LASTCH 0x7e
+#define TABSZ (LASTCH - FIRSTCH + 1)
+
+
+typedef struct rc_entry{
+ char *var;
+ union {
+ char *p;
+ char **l;
+ } val;
+ int islist;
+ int ispmapivar;
+} rc_entry_s;
+
+typedef struct rc_feat {
+ char *var;
+ int is_set;
+} rc_feat_s;
+
+typedef struct STRLIST{
+ char *str;
+ struct STRLIST *next;
+} STRLIST_S;
+
+typedef struct file_struct{
+ char *filename;
+ struct file_struct *next;
+} file_s;
+
+typedef enum {RecipDesc, Message} BufType;
+
+typedef struct mbuffer_list{
+ void *buf;
+ int arraysize; /* should always be 1 unless it's one of those silly arrays */
+ BufType type;
+ struct mbuffer_list *next;
+} MBUFFER_LIST_S;
+
+typedef struct strbuffer{
+ char *buf;
+ unsigned long cur_bytes;
+ unsigned long increment;
+ unsigned long bufsize;
+} STRBUFFER_S;
+
+typedef struct dlg_edits{
+ char edit1[EDITLEN];
+ char edit2[EDITLEN];
+} dlg_edits_s;
+
+typedef struct pw_cache {
+ char user[EDITLEN];
+ char pwd[EDITLEN];
+ char host[EDITLEN];
+ int validpw;
+ struct pw_cache *next;
+} pw_cache_s;
+
+typedef struct sessionl{
+ NETMBX *mb;
+ dlg_edits_s dlge;
+ pw_cache_s *pwc;
+ MAILSTREAM *open_stream;
+ char *currently_open;
+ struct {
+ unsigned dlg_cancel:1;
+ unsigned int mapi_logon_ui:1;
+ unsigned int check_stream:1;
+ /* int passfile_checked; */
+ } flags;
+ file_s *fs;
+ lpMapiMessage FAR lpm;
+ HWND mhwnd;
+ unsigned long session_number;
+ struct sessionl *next;
+} sessionlist_s;
+
+typedef struct mapi_global{
+ char *debugFile;
+ int debug;
+ FILE *dfd;
+ char *pineExe;
+ char *pineExeAlt;
+ char *pinerc;
+ char *pineconf;
+ char *pinercex;
+ char *fccfolder;
+ int inited;
+ char *tmpmbxptr;
+ rc_entry_s *prcvars[NUMPRCVARS];
+ rc_feat_s *prcfeats[NUMPRCFEATS];
+ int pmapi_send_behavior;
+ int pmapi_suppress_dialogs;
+ int pmapi_strict_no_dialog;
+ sessionlist_s *sessionlist;
+ sessionlist_s *cs; /* the current session, used for logins */
+ unsigned long next_session;
+ unsigned long attach_no;
+ unsigned long attached;
+ MBUFFER_LIST_S *mapi_bufs;
+ char *attachDir;
+ HINSTANCE mhinst;
+} mapi_global_s;
+
+extern struct mapi_global *ms_global;
+
+void ErrorBoxFunc(char *msg);
+char *quote(char *old);
+char *mstrdup(char *old);
+int msprint(char *str);
+int msprint1(char *str, void *arg1);
+int msprint_message_structure(lpMapiMessage lpm);
+int msprint_recipient_structure(lpMapiRecipDesc lmrd, int mapi_orig_is_unexpected);
+int msprint_file_structure(lpMapiFileDesc lmfd);
+
+int est_size(ADDRESS *a);
+int send_documents(char *files, char sep);
+unsigned long send_msg_nodlg(LHANDLE lhSession, ULONG ulUIParam,
+ lpMapiMessage lpMessage, FLAGS flFlags, ULONG ulReserved);
+ADDRESS *mapirecip2address(lpMapiRecipDesc lpmrd);
+long pmapi_soutr(STRBUFFER_S *s, char *str);
+char *TmpCopy(char *srcFile, int is_pinerc);
+sessionlist_s *new_sessionlist();
+sessionlist_s *free_sessionlist_node(sessionlist_s *cs);
+sessionlist_s *get_session(unsigned long num);
+lpMapiRecipDesc new_MapiRecipDesc(int arraysize);
+void free_MapiRecipDesc(lpMapiRecipDesc buf, int arraysize);
+lpMapiMessage new_MapiMessage(int arraysize);
+void free_MapiMessage(lpMapiMessage buf, int arraysize);
+int new_mbuffer(void *buf, int arraysize, BufType type);
+int free_mbuffer(void *buf);
+int InitPineSpecific(sessionlist_s *cs);
+MAILSTREAM *mapi_mail_open(sessionlist_s *cs, MAILSTREAM *stream, char *name, long options);
+MAILSTREAM *check_mailstream(sessionlist_s *cs);
+unsigned long convert_to_msgno(char *msgid);
+int fetch_structure_and_attachments(long msgno, long flags,
+ FLAGS MAPIflags, sessionlist_s *cs);
+char *message_structure_to_mailto_url(lpMapiMessage lpm);
+ULONG FAR PASCAL MAPISendMail(LHANDLE lhSession, ULONG ulUIParam, lpMapiMessage lpMessage,
+ FLAGS flFlags, ULONG ulReserved);
+ULONG FAR PASCAL MAPILogon(ULONG ulUIParam, LPTSTR lpszProfileName, LPTSTR lpszPassword,
+ FLAGS flFlags, ULONG ulReserved, LPLHANDLE lplhSession);
+ULONG FAR PASCAL MAPILogoff (LHANDLE lhSession, ULONG ulUIParam, FLAGS flFlags, ULONG ulReserved);
+ULONG FAR PASCAL MAPIFindNext (LHANDLE lhSession, ULONG ulUIParam, LPSTR lpszMessageType,
+ LPSTR lpszSeedMessageID, FLAGS flFlags, ULONG ulReserved,
+ LPSTR lpszMessageID);
+ULONG FAR PASCAL MAPIReadMail(LHANDLE lhSession, ULONG ulUIParam, LPSTR lpszMessageID,
+ FLAGS flFlags, ULONG ulReserved, lpMapiMessage FAR *lppMessage);
+ULONG FAR PASCAL MAPIAddress(LHANDLE lhSession, ULONG ulUIParam, LPTSTR lpszCaption,
+ ULONG nEditFields, LPTSTR lpszLabels, ULONG nRecips,
+ lpMapiRecipDesc lpRecips, FLAGS flFlags, ULONG ulReserved,
+ LPULONG lpnNewRecips, lpMapiRecipDesc FAR * lppNewRecips);
+ULONG FAR PASCAL MAPIDeleteMail(LHANDLE lhSession, ULONG ulUIParam, LPTSTR lpszMessageID,
+ FLAGS flFlags, ULONG ulReserved);
+ULONG FAR PASCAL MAPIDetails(LHANDLE lhSession, ULONG ulUIParam, lpMapiRecipDesc lpRecip,
+ FLAGS flFlags, ULONG ulReserved);
+ULONG FAR PASCAL MAPIFreeBuffer(LPVOID pv);
+ULONG FAR PASCAL MAPIResolveName(LHANDLE lhSession, ULONG ulUIParam, LPTSTR lpszName,
+ FLAGS flFlags, ULONG ulReserved, lpMapiRecipDesc FAR * lppRecip);
+ULONG FAR PASCAL MAPISaveMail(LHANDLE lhSession, ULONG ulUIParam, lpMapiMessage lpMessage,
+ FLAGS flFlags, ULONG ulReserved, LPTSTR lpszMessageID);
+ULONG FAR PASCAL MAPISendDocuments(ULONG ulUIParam, LPTSTR lpszDelimChar, LPTSTR lpszFullPaths,
+ LPTSTR lpszFileNames, ULONG ulReserved);
+
+/* rfc1522.c */
+char *rfc1522_encode PROTO((char *, size_t, unsigned char *, char *));
+void get_pair PROTO((char *, char **, char **, int, int));
+void removing_leading_and_trailing_white_space PROTO((char *));
diff --git a/mapi/pmapi.rc b/mapi/pmapi.rc
new file mode 100644
index 00000000..a648cd14
--- /dev/null
+++ b/mapi/pmapi.rc
@@ -0,0 +1,153 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#define APSTUDIO_HIDDEN_SYMBOLS
+#include "windows.h"
+#undef APSTUDIO_HIDDEN_SYMBOLS
+#define IDC_STATIC (-1) // all static controls
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_DIALOG1 DIALOG DISCARDABLE 0, 0, 284, 95
+STYLE DS_SYSMODAL | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Login"
+FONT 8, "MS Sans Serif"
+BEGIN
+ LTEXT "Login: ",IDC_STATIC,20,44,20,8
+ EDITTEXT IDC_LOGIN,45,42,158,12,ES_AUTOHSCROLL
+ LTEXT "Password: ",IDC_STATIC,7,57,36,8
+ EDITTEXT IDC_PASSWORD,45,55,158,12,ES_PASSWORD | ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK",IDOK,7,73,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,57,73,50,14
+ LTEXT "Attempting to open the following folder:",IDC_STATIC,7,
+ 18,131,8
+ LTEXT "Some server here ",IDC_SERVER,7,30,269,8
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE
+BEGIN
+ IDD_DIALOG1, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 276
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 87
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+#ifndef _MAC
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 2,01,0,0
+ PRODUCTVERSION 2,01,0,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x29L
+#else
+ FILEFLAGS 0x28L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "Comments", "alpine info: http://www.washington.edu/alpine\0"
+ VALUE "CompanyName", "University of Washington\0"
+ VALUE "FileDescription", "Simple MAPI DLL for Alpine for Windows\0"
+ VALUE "FileVersion", "2.10\0"
+ VALUE "InternalName", "pmapi32\0"
+ VALUE "LegalCopyright", "Copyright ? 2006-2009\0"
+ VALUE "LegalTrademarks", "Apache License, Version 2.0\0"
+ VALUE "OriginalFilename", "pmapi32.dll\0"
+ VALUE "PrivateBuild", " \0"
+ VALUE "ProductName", "Simple MAPI for Alpine for Windows\0"
+ VALUE "ProductVersion", "2.10\0"
+ VALUE "SpecialBuild", " \0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+#endif // !_MAC
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/mapi/resource.h b/mapi/resource.h
new file mode 100644
index 00000000..072b84c4
--- /dev/null
+++ b/mapi/resource.h
@@ -0,0 +1,19 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by select.rc
+//
+#define IDD_DIALOG1 101
+#define IDC_PASSWORD 1000
+#define IDC_LOGIN 1001
+#define IDC_SERVER 1003
+#define IDC_COMBO2 1005
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 103
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1007
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/mapi/rfc1522.c b/mapi/rfc1522.c
new file mode 100644
index 00000000..f77a8c27
--- /dev/null
+++ b/mapi/rfc1522.c
@@ -0,0 +1,557 @@
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*
+ * rfc1522.c
+ *
+ * right now this is just rfc1522_encode (taken straight out of pine/strings.c,
+ * but if were to become necessary,
+ * it could be made to do rfc1522_decode too, and it already has some strings functions.
+ */
+#include "pmapi.h"
+
+#define RFC1522_INIT "=?"
+#define RFC1522_INIT_L 2
+#define RFC1522_TERM "?="
+#define RFC1522_TERM_L 2
+#define RFC1522_DLIM "?"
+#define RFC1522_DLIM_L 1
+#define RFC1522_MAXW 75
+#define ESPECIALS "()<>@,;:\"/[]?.="
+#define RFC1522_OVERHEAD(S) (RFC1522_INIT_L + RFC1522_TERM_L + \
+ (2 * RFC1522_DLIM_L) + strlen(S) + 1);
+#define RFC1522_ENC_CHAR(C) (((C) & 0x80) || !rfc1522_valtok(C) \
+ || (C) == '_' )
+#define SPACE ' ' /* space character */
+#define ESCAPE '\033' /* the escape */
+#define UNKNOWN_CHARSET "X-UNKNOWN"
+
+/*
+ * Hex conversion aids
+ */
+#define HEX_ARRAY "0123456789ABCDEF"
+#define HEX_CHAR1(C) HEX_ARRAY[((C) & 0xf0) >> 4]
+#define HEX_CHAR2(C) HEX_ARRAY[(C) & 0xf]
+
+#define C2XPAIR(C, S) { \
+ *(S)++ = HEX_CHAR1(C); \
+ *(S)++ = HEX_CHAR2(C); \
+ }
+
+
+int rfc1522_token PROTO((char *, int (*) PROTO((int)), char *,
+ char **));
+int rfc1522_valtok PROTO((int));
+int rfc1522_valenc PROTO((int));
+int rfc1522_valid PROTO((char *, char **, char **, char **,
+ char **));
+char *rfc1522_8bit PROTO((void *, int));
+char *rfc1522_binary PROTO((void *, int));
+unsigned char *rfc1522_encoded_word PROTO((unsigned char *, int, char *));
+char *strindex PROTO((char *, int));
+void sstrcpy PROTO((char **, char *));
+void sstrncpy PROTO((char **, char *, int));
+
+int removing_double_quotes PROTO((char *));
+
+static char *known_escapes[] = {
+ "(B", "(J", "$@", "$B", /* RFC 1468 */
+ "(H",
+ NULL};
+/* different for non-Windows */
+
+int
+match_escapes(esc_seq)
+ char *esc_seq;
+{
+ char **p;
+ int n;
+
+ for(p = known_escapes; *p && strncmp(esc_seq, *p, n = strlen(*p)); p++)
+ ;
+
+ return(*p ? n + 1 : 0);
+}
+
+/*----------------------------------------------------------------------
+ A replacement for strchr or index ...
+
+ Returns a pointer to the first occurrence of the character
+ 'ch' in the specified string or NULL if it doesn't occur
+
+ ....so we don't have to worry if it's there or not. We bring our own.
+If we really care about efficiency and think the local one is more
+efficient the local one can be used, but most of the things that take
+a long time are in the c-client and not in pine.
+ ----*/
+char *
+strindex(buffer, ch)
+ char *buffer;
+ int ch;
+{
+ do
+ if(*buffer == ch)
+ return(buffer);
+ while (*buffer++ != '\0');
+
+ return(NULL);
+}
+
+/*----------------------------------------------------------------------
+ copy the source string onto the destination string returning with
+ the destination string pointer at the end of the destination text
+
+ motivation for this is to avoid twice passing over a string that's
+ being appended to twice (i.e., strcpy(t, x); t += strlen(t))
+ ----*/
+void
+sstrcpy(d, s)
+ char **d;
+ char *s;
+{
+ while((**d = *s++) != '\0')
+ (*d)++;
+}
+
+void
+sstrncpy(d, s, n)
+ char **d;
+ char *s;
+ int n;
+{
+ while(n-- > 0 && (**d = *s++) != '\0')
+ (*d)++;
+}
+
+/*
+ * rfc1522_token - scan the given source line up to the end_str making
+ * sure all subsequent chars are "valid" leaving endp
+ * a the start of the end_str.
+ * Returns: TRUE if we got a valid token, FALSE otherwise
+ */
+int
+rfc1522_token(s, valid, end_str, endp)
+ char *s;
+ int (*valid) PROTO((int));
+ char *end_str;
+ char **endp;
+{
+ while(*s){
+ if((char) *s == *end_str /* test for matching end_str */
+ && ((end_str[1])
+ ? !strncmp((char *)s + 1, end_str + 1, strlen(end_str + 1))
+ : 1)){
+ *endp = s;
+ return(TRUE);
+ }
+
+ if(!(*valid)(*s++)) /* test for valid char */
+ break;
+ }
+
+ return(FALSE);
+}
+
+
+/*
+ * rfc1522_valtok - test for valid character in the RFC 1522 encoded
+ * word's charset and encoding fields.
+ */
+int
+rfc1522_valtok(c)
+ int c;
+{
+ return(!(c == SPACE || iscntrl(c & 0x7f) || strindex(ESPECIALS, c)));
+}
+
+
+/*
+ * rfc1522_valenc - test for valid character in the RFC 1522 encoded
+ * word's encoded-text field.
+ */
+int
+rfc1522_valenc(c)
+ int c;
+{
+ return(!(c == '?' || c == SPACE) && isprint((unsigned char)c));
+}
+
+
+/*
+ * rfc1522_valid - validate the given string as to it's rfc1522-ness
+ */
+int
+rfc1522_valid(s, charset, enc, txt, endp)
+ char *s;
+ char **charset;
+ char **enc;
+ char **txt;
+ char **endp;
+{
+ char *c, *e, *t, *p;
+ int rv;
+
+ rv = rfc1522_token(c = s+RFC1522_INIT_L, rfc1522_valtok, RFC1522_DLIM, &e)
+ && rfc1522_token(++e, rfc1522_valtok, RFC1522_DLIM, &t)
+ && rfc1522_token(++t, rfc1522_valenc, RFC1522_TERM, &p)
+ && p - s <= RFC1522_MAXW;
+
+ if(charset)
+ *charset = c;
+
+ if(enc)
+ *enc = e;
+
+ if(txt)
+ *txt = t;
+
+ if(endp)
+ *endp = p;
+
+ return(rv);
+}
+
+
+/*
+ * rfc1522_encode - encode the given source string ala RFC 1522,
+ * IF NECESSARY, into the given destination buffer.
+ * Don't bother copying if it turns out encoding
+ * isn't necessary.
+ *
+ * Returns: pointer to either the destination buffer containing the
+ * encoded text, or a pointer to the source buffer if we didn't
+ * have to encode anything.
+ */
+char *
+rfc1522_encode(d, len, s, charset)
+ char *d;
+ size_t len; /* length of d */
+ unsigned char *s;
+ char *charset;
+{
+ unsigned char *p, *q;
+ int n;
+
+ if(!s)
+ return((char *) s);
+
+ if(!charset)
+ charset = UNKNOWN_CHARSET;
+
+ /* look for a reason to encode */
+ for(p = s, n = 0; *p; p++)
+ if((*p) & 0x80){
+ n++;
+ }
+ else if(*p == RFC1522_INIT[0]
+ && !strncmp((char *) p, RFC1522_INIT, RFC1522_INIT_L)){
+ if(rfc1522_valid((char *) p, NULL, NULL, NULL, (char **) &q))
+ p = q + RFC1522_TERM_L - 1; /* advance past encoded gunk */
+ }
+ else if(*p == ESCAPE && match_escapes((char *)(p+1))){
+ n++;
+ }
+
+ if(n){ /* found, encoding to do */
+ char *rv = d, *t,
+ enc = (n > (2 * (p - s)) / 3) ? 'B' : 'Q';
+
+ while(*s){
+ if(d-rv < len-1-(RFC1522_INIT_L+2*RFC1522_DLIM_L+1)){
+ sstrcpy(&d, RFC1522_INIT); /* insert intro header, */
+ sstrcpy(&d, charset); /* character set tag, */
+ sstrcpy(&d, RFC1522_DLIM); /* and encoding flavor */
+ *d++ = enc;
+ sstrcpy(&d, RFC1522_DLIM);
+ }
+
+ /*
+ * feed lines to encoder such that they're guaranteed
+ * less than RFC1522_MAXW.
+ */
+ p = rfc1522_encoded_word(s, enc, charset);
+ if(enc == 'B') /* insert encoded data */
+ sstrncpy(&d, t = rfc1522_binary(s, p - s), len-1-(d-rv));
+ else /* 'Q' encoding */
+ sstrncpy(&d, t = rfc1522_8bit(s, p - s), len-1-(d-rv));
+
+ sstrncpy(&d, RFC1522_TERM, len-1-(d-rv)); /* insert terminator */
+ fs_give((void **) &t);
+ if(*p) /* more src string follows */
+ sstrncpy(&d, "\015\012 ", len-1-(d-rv)); /* insert cont. line */
+
+ s = p; /* advance s */
+ }
+
+ rv[len-1] = '\0';
+ return(rv);
+ }
+ else
+ return((char *) s); /* no work for us here */
+}
+
+
+
+/*
+ * rfc1522_encoded_word -- cut given string into max length encoded word
+ *
+ * Return: pointer into 's' such that the encoded 's' is no greater
+ * than RFC1522_MAXW
+ *
+ * NOTE: this line break code is NOT cognizant of any SI/SO
+ * charset requirements nor similar strategies using escape
+ * codes. Hopefully this will matter little and such
+ * representation strategies don't also include 8bit chars.
+ */
+unsigned char *
+rfc1522_encoded_word(s, enc, charset)
+ unsigned char *s;
+ int enc;
+ char *charset;
+{
+ int goal = RFC1522_MAXW - RFC1522_OVERHEAD(charset);
+
+ if(enc == 'B') /* base64 encode */
+ for(goal = ((goal / 4) * 3) - 2; goal && *s; goal--, s++)
+ ;
+ else /* special 'Q' encoding */
+ for(; goal && *s; s++)
+ if((goal -= RFC1522_ENC_CHAR(*s) ? 3 : 1) < 0)
+ break;
+
+ return(s);
+}
+
+
+
+/*
+ * rfc1522_8bit -- apply RFC 1522 'Q' encoding to the given 8bit buffer
+ *
+ * Return: alloc'd buffer containing encoded string
+ */
+char *
+rfc1522_8bit(src, slen)
+ void *src;
+ int slen;
+{
+ char *ret = (char *) fs_get ((size_t) (3*slen + 2));
+ char *d = ret;
+ unsigned char c;
+ unsigned char *s = (unsigned char *) src;
+
+ while (slen--) { /* for each character */
+ if (((c = *s++) == '\015') && (*s == '\012') && slen) {
+ *d++ = '\015'; /* true line break */
+ *d++ = *s++;
+ slen--;
+ }
+ else if(c == SPACE){ /* special encoding case */
+ *d++ = '_';
+ }
+ else if(RFC1522_ENC_CHAR(c)){
+ *d++ = '='; /* quote character */
+ C2XPAIR(c, d);
+ }
+ else
+ *d++ = (char) c; /* ordinary character */
+ }
+
+ *d = '\0'; /* tie off destination */
+ return(ret);
+}
+
+
+/*
+ * rfc1522_binary -- apply RFC 1522 'B' encoding to the given 8bit buffer
+ *
+ * Return: alloc'd buffer containing encoded string
+ */
+char *
+rfc1522_binary (src, srcl)
+ void *src;
+ int srcl;
+{
+ static char *v =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ unsigned char *s = (unsigned char *) src;
+ char *ret, *d;
+
+ d = ret = (char *) fs_get ((size_t) ((((srcl + 2) / 3) * 4) + 1));
+ for (; srcl; s += 3) { /* process tuplets */
+ /* byte 1: high 6 bits (1) */
+ *d++ = v[s[0] >> 2];
+ /* byte 2: low 2 bits (1), high 4 bits (2) */
+ *d++ = v[((s[0] << 4) + (--srcl ? (s[1] >> 4) : 0)) & 0x3f];
+ /* byte 3: low 4 bits (2), high 2 bits (3) */
+ *d++ = srcl ? v[((s[1] << 2) + (--srcl ? (s[2] >> 6) :0)) & 0x3f] :'=';
+ /* byte 4: low 6 bits (3) */
+ *d++ = srcl ? v[s[2] & 0x3f] : '=';
+ if(srcl)
+ srcl--; /* count third character if processed */
+ }
+
+ *d = '\0'; /* tie off string */
+ return(ret); /* return the resulting string */
+}
+
+
+/*
+ * Function to parse the given string into two space-delimited fields
+ * Quotes may be used to surround labels or values with spaces in them.
+ * Backslash negates the special meaning of a quote.
+ * Unescaping of backslashes only happens if the pair member is quoted,
+ * this provides for backwards compatibility.
+ *
+ * Args -- string -- the source string
+ * label -- the first half of the string, a return value
+ * value -- the last half of the string, a return value
+ * firstws -- if set, the halves are delimited by the first unquoted
+ * whitespace, else by the last unquoted whitespace
+ * strip_internal_label_quotes -- unescaped quotes in the middle of the label
+ * are removed. This is useful for vars
+ * like display-filters and url-viewers
+ * which may require quoting of an arg
+ * inside of a _TOKEN_.
+ */
+void
+get_pair(string, label, value, firstws, strip_internal_label_quotes)
+ char *string, **label, **value;
+ int firstws;
+ int strip_internal_label_quotes;
+{
+ char *p, *q, *tmp, *token = NULL;
+ int quoted = 0;
+
+ *label = *value = NULL;
+
+ /*
+ * This for loop just finds the beginning of the value. If firstws
+ * is set, then it begins after the first whitespace. Otherwise, it begins
+ * after the last whitespace. Quoted whitespace doesn't count as
+ * whitespace. If there is no unquoted whitespace, then there is no
+ * label, there's just a value.
+ */
+ for(p = string; p && *p;){
+ if(*p == '"') /* quoted label? */
+ quoted = (quoted) ? 0 : 1;
+
+ if(*p == '\\' && *(p+1) == '"') /* escaped quote? */
+ p++; /* skip it... */
+
+ if(isspace((unsigned char)*p) && !quoted){ /* if space, */
+ while(*++p && isspace((unsigned char)*p)) /* move past it */
+ ;
+
+ if(!firstws || !token)
+ token = p; /* remember start of text */
+ }
+ else
+ p++;
+ }
+
+ if(token){ /* copy label */
+ *label = p = (char *)fs_get(((token - string) + 1) * sizeof(char));
+
+ /* make a copy of the string */
+ tmp = (char *)fs_get(((token - string) + 1) * sizeof(char));
+ strncpy(tmp, string, token - string);
+ tmp[token-string] = '\0';
+
+ removing_leading_and_trailing_white_space(tmp);
+ quoted = removing_double_quotes(tmp);
+
+ for(q = tmp; *q; q++){
+ if(quoted && *q == '\\' && (*(q+1) == '"' || *(q+1) == '\\'))
+ *p++ = *++q;
+ else if(!(strip_internal_label_quotes && *q == '"'))
+ *p++ = *q;
+ }
+
+ *p = '\0'; /* tie off label */
+ fs_give((void **)&tmp);
+ if(*label == '\0')
+ fs_give((void **)label);
+ }
+ else
+ token = string;
+
+ if(token){ /* copy value */
+ *value = p = (char *)fs_get((strlen(token) + 1) * sizeof(char));
+
+ tmp = cpystr(token);
+ removing_leading_and_trailing_white_space(tmp);
+ quoted = removing_double_quotes(tmp);
+
+ for(q = tmp; *q ; q++){
+ if(quoted && *q == '\\' && (*(q+1) == '"' || *(q+1) == '\\'))
+ *p++ = *++q;
+ else
+ *p++ = *q;
+ }
+
+ *p = '\0'; /* tie off value */
+ fs_give((void **)&tmp);
+ }
+}
+
+void
+removing_leading_and_trailing_white_space(string)
+ char *string;
+{
+ register char *p, *q = NULL;
+
+ if(!string)
+ return;
+
+ for(p = string; *p; p++) /* find the first non-blank */
+ if(!isspace((unsigned char)*p)){
+ while(*string = *p++){ /* copy back from there... */
+ q = (!isspace((unsigned char)*string)) ? NULL : (!q) ? string : q;
+ string++;
+ }
+
+ if(q)
+ *q = '\0';
+
+ return;
+ }
+
+ if(*string != '\0')
+ *string = '\0';
+}
+
+/*----------------------------------------------------------------------
+ Remove one set of double quotes surrounding string in place
+ Returns 1 if quotes were removed
+
+ Args: string -- string to remove quotes from
+ ----*/
+int
+removing_double_quotes(string)
+ char *string;
+{
+ register char *p;
+ int ret = 0;
+
+ if(string && string[0] == '"' && string[1] != '\0'){
+ p = string + strlen(string) - 1;
+ if(*p == '"'){
+ ret++;
+ *p = '\0';
+ for(p = string; *p; p++)
+ *p = *(p+1);
+ }
+ }
+
+ return(ret);
+}
diff --git a/mapi/smapi.c b/mapi/smapi.c
new file mode 100644
index 00000000..6beb87a8
--- /dev/null
+++ b/mapi/smapi.c
@@ -0,0 +1,653 @@
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "pmapi.h"
+
+ULONG FAR PASCAL MAPISendMail(
+ LHANDLE lhSession,
+ ULONG ulUIParam,
+ lpMapiMessage lpMessage,
+ FLAGS flFlags,
+ ULONG ulReserved)
+{
+ ULONG i;
+ int ac = 0, rv = 0, can_suppress_dialog = 0, has_recips = 0;
+ char **av, *tmpfree, *url;
+
+ if(MSDEBUG){
+ fprintf(ms_global->dfd, "\r\nIn MAPISendMail\r\n");
+ fprintf(ms_global->dfd, " MAPI_DIALOG is %s set\r\n",
+ flFlags & MAPI_DIALOG ? "" : "NOT");
+ fprintf(ms_global->dfd, " MAPI_LOGON_UI is %s set\r\n",
+ flFlags & MAPI_LOGON_UI ? "" : "NOT");
+ fprintf(ms_global->dfd, " MAPI_NEW_SESSION is %s set\r\n",
+ flFlags & MAPI_NEW_SESSION ? "" : "NOT");
+ fprintf(ms_global->dfd, " session number is %p\r\n", lhSession);
+ _flushall();
+ }
+ msprint_message_structure(lpMessage);
+
+ url = message_structure_to_mailto_url(lpMessage);
+
+ for(i = 0; i < lpMessage->nRecipCount; i++){
+ if((&lpMessage->lpRecips[i])->ulRecipClass != MAPI_ORIG)
+ has_recips = 1;
+ }
+ if((flFlags & MAPI_DIALOG) && has_recips &&
+ (lpMessage->lpszSubject
+ || lpMessage->lpszNoteText
+ || lpMessage->nFileCount)){
+ if(ms_global->pmapi_suppress_dialogs == PMSD_YES)
+ can_suppress_dialog = 1;
+ else if(ms_global->pmapi_suppress_dialogs == PMSD_PROMPT){
+ if(MessageBox(NULL, "Edit Message Before Sending?", "pmapi32.dll", MB_YESNO|MB_ICONQUESTION) == IDNO)
+ can_suppress_dialog = 1;
+ }
+ }
+ if(can_suppress_dialog ||
+ ((flFlags & MAPI_DIALOG) == 0)){
+ rv = send_msg_nodlg(lhSession, ulUIParam, lpMessage, flFlags, ulReserved);
+ return(rv);
+ }
+
+ if (lpMessage == NULL){
+ /* lpMessage->lpFiles == NULL ||
+ lpMessage->lpFiles->lpszPathName == NULL) */
+ /* this old code checked to see that there were attachments */
+ /* I think it should be all right for there not to be attachments */
+
+ ErrorBox("MAPISendMail: %s pointer in lpMessage argument", "NULL");
+ return 0;
+ }
+
+ /*
+ * allocate space for spawn's argv array:
+ * 2 for each of the nFileCount attachments
+ * +4 for the -p pinerc, and av[0] and trailing NULL
+ * +2 for the url
+ */
+ {
+ int avSize = (6+2*lpMessage->nFileCount)*sizeof(*av);
+ av = (char **)fs_get(avSize);
+ if (av == NULL) {
+ ErrorBox("MAPISendMail: fs_get of %d bytes for av failed", (avSize));
+ return MAPI_E_FAILURE;
+ }
+ }
+
+ /*
+ * establish a default path to the pine executable which will
+ * probably be replaced with a value from the registry
+ */
+ if(ms_global && ms_global->pineExe){
+ av[ac++] = quote(ms_global->pineExe);
+ if(!av[0]) {
+ ErrorBox("Cannot fs_get for %s","pine");
+ return MAPI_E_FAILURE;
+ }
+ }
+ else return MAPI_E_FAILURE;
+ if(ms_global->pinerc){
+ av[ac++] = mstrdup("-p");
+ if(tmpfree = TmpCopy(ms_global->pinerc, IS_PINERC)){
+ av[ac++] = quote(tmpfree);
+ fs_give((void **)&tmpfree);
+ }
+ else
+ av[ac++] = quote(ms_global->pinerc);
+ }
+ if(url){
+ av[ac++] = mstrdup("-url");
+ av[ac++] = url;
+ }
+
+ /*
+ * make a temporary copy of each attachment in attachDir,
+ * add the new filename (suitably quoted) to pine's argument list
+ */
+ for (i=0; i < lpMessage->nFileCount; ++i) {
+ char *oldPath = lpMessage->lpFiles[i].lpszPathName;
+ char *newCopy, *tmpfree;
+ tmpfree = TmpCopy(oldPath, NOT_PINERC);
+ if(tmpfree == NULL)
+ return MAPI_E_ATTACHMENT_NOT_FOUND;
+ newCopy = quote(tmpfree);
+ fs_give((void **)&tmpfree);
+ if (newCopy) {
+ av[ac++] = mstrdup("-attach_and_delete");
+ av[ac++] = newCopy;
+ }
+ else{
+ if(MSDEBUG)
+ fprintf(ms_global->dfd, "TmpCopy returned null\r\n");
+ }
+ if (MSDEBUG) {
+ fprintf(ms_global->dfd,"Attachment %d: old path: %s\r\n--\r\n",
+ i,oldPath);
+ fprintf(ms_global->dfd,">>: %s\r\n--\r\n",ms_global->attachDir);
+ fprintf(ms_global->dfd,">>: tmp path: %s\r\n--\r\n",
+ (newCopy?newCopy:"NULL"));
+ }
+ }
+ av[ac++] = NULL;
+
+ if(MSDEBUG) {
+ fprintf(ms_global->dfd, "spawning %s (else %s):\r\n",
+ ms_global->pineExe, ms_global->pineExeAlt);
+ for (i=0; av[i]; ++i)
+ fprintf(ms_global->dfd, "av[%d] = %s\r\n", i, av[i]);
+ }
+
+ _flushall();
+ if (_spawnvp(_P_NOWAIT, ms_global->pineExe, av) == -1 &&
+ _spawnvp(_P_NOWAIT, ms_global->pineExeAlt, av) == -1)
+ {
+ ErrorBox("MAPISendMail: _spawnvp of %s failed", ms_global->pineExe);
+ if(MSDEBUG) fprintf(ms_global->dfd, "_spawnvp %s and %s failed\r\n",
+ ms_global->pineExe,ms_global->pineExeAlt);
+ return(MAPI_E_FAILURE);
+ }
+
+ /*
+ * close and free allocated resources
+ */
+ if (av[0]) fs_give((void **)&av[0]);
+ for (i=1; av[i]; i++) {
+ if (av[i]) fs_give((void **)&av[i]);
+ }
+ fs_give((void **)&av);
+
+ return SUCCESS_SUCCESS;
+}
+
+
+ULONG FAR PASCAL MAPILogon(
+ ULONG ulUIParam,
+ LPTSTR lpszProfileName,
+ LPTSTR lpszPassword,
+ FLAGS flFlags,
+ ULONG ulReserved,
+ LPLHANDLE lplhSession)
+{
+ mapi_global_s *nmg = ms_global;
+ sessionlist_s *cs;
+ sessionlist_s *ts;
+
+ if(MSDEBUG){
+ fprintf(ms_global->dfd, "\r\nIn MAPILogon\r\n");
+ fprintf(ms_global->dfd, " MAPI_FORCE_DOWNLOAD is %s set\r\n",
+ flFlags & MAPI_FORCE_DOWNLOAD ? "" : "NOT");
+ fprintf(ms_global->dfd, " MAPI_NEW_SESSION is %s set\r\n",
+ flFlags & MAPI_NEW_SESSION ? "" : "NOT");
+ fprintf(ms_global->dfd, " MAPI_LOGON_UI is %s set\r\n",
+ flFlags & MAPI_LOGON_UI ? "" : "NOT");
+ fprintf(ms_global->dfd, " MAPI_PASSWORD_UI is %s set\r\n",
+ flFlags & MAPI_PASSWORD_UI ? "" : "NOT");
+ fprintf(ms_global->dfd, " ulUIParam is %p\r\n", ulUIParam);
+ if(lpszProfileName)
+ fprintf(ms_global->dfd, " profile name is %s\r\n", lpszProfileName);
+ fprintf(ms_global->dfd, " lplhSession is %p\r\n", lplhSession);
+ if(lplhSession)
+ fprintf(ms_global->dfd, " session number is %p\r\n", *lplhSession);
+ fclose(ms_global->dfd);
+ ms_global->dfd = fopen(ms_global->debugFile, "ab");
+ if(!ms_global->dfd){
+ ms_global->debug = 0;
+ ErrorBox("Problem with debug file %s! Debugging turned off",
+ ms_global->debugFile);
+ fs_give((void **)&ms_global->debugFile);
+ ms_global->debugFile = NULL;
+
+ }
+ }
+ if(lplhSession == NULL){
+ if(MSDEBUG){
+ fprintf(nmg->dfd,
+ "lplhSession is a NULL pointer, returning MAPI_E_FAILURE\r\n");
+ }
+ return MAPI_E_FAILURE;
+ }
+ if((flFlags & MAPI_NEW_SESSION) || !nmg->sessionlist){
+ cs = new_sessionlist();
+ *lplhSession = cs->session_number;
+
+ if(nmg->sessionlist == NULL)
+ nmg->sessionlist = cs;
+ else{
+ ts = nmg->sessionlist;
+ while(ts->next) ts = ts->next;
+ ts->next = cs;
+ }
+ }
+ else{
+ if(!*lplhSession) *lplhSession = nmg->sessionlist->session_number;
+ else
+ cs = get_session(*lplhSession);
+ if(!cs)
+ return MAPI_E_INVALID_SESSION;
+ }
+ cs->flags.mapi_logon_ui = ((flFlags & MAPI_LOGON_UI) || (flFlags & MAPI_PASSWORD_UI))
+ ? 1 : 0;
+ if((flFlags & MAPI_LOGON_UI) || (flFlags & MAPI_PASSWORD_UI))
+ if(InitPineSpecific(cs) == -1) return MAPI_E_FAILURE;
+
+ if(MSDEBUG){
+ fprintf(nmg->dfd,
+ "lplhSession is returning %p for session handle\r\n", *lplhSession);
+ _flushall();
+ }
+ return SUCCESS_SUCCESS;
+}
+
+ULONG FAR PASCAL MAPILogoff (
+ LHANDLE lhSession,
+ ULONG ulUIParam,
+ FLAGS flFlags,
+ ULONG ulReserved)
+{
+ sessionlist_s *cs;
+
+ if(MSDEBUG){
+ fprintf(ms_global->dfd, "\r\nIn MAPILogoff\r\n");
+ fprintf(ms_global->dfd, " session number is %p\r\n", lhSession);
+ _flushall();
+ }
+
+ cs = get_session(lhSession);
+ if(!cs) return MAPI_E_INVALID_SESSION;
+ if(!cs->open_stream) return SUCCESS_SUCCESS;
+ cs->open_stream = mail_close_full(cs->open_stream, 0);
+ cs->open_stream = NULL;
+ if(cs->currently_open){
+ fs_give((void **)&cs->currently_open);
+ cs->currently_open = NULL;
+ }
+ cs->dlge.edit1[0] = '\0';
+ cs->dlge.edit2[0] = '\0';
+ /* ms_global->flags.passfile_checked = FALSE; */
+ return SUCCESS_SUCCESS;
+}
+
+ULONG FAR PASCAL MAPIFindNext (
+ LHANDLE lhSession,
+ ULONG ulUIParam,
+ LPSTR lpszMessageType,
+ LPSTR lpszSeedMessageID,
+ FLAGS flFlags,
+ ULONG ulReserved,
+ LPSTR lpszMessageID)
+{
+ mapi_global_s *nmg = ms_global;
+ sessionlist_s *cs;
+ char tmp[1024], tmpseq[1024];
+ int msg_found;
+ unsigned long tmp_msgno, i, cur_msg = 0;
+ MESSAGECACHE *telt;
+
+ if(MSDEBUG){
+ fprintf(nmg->dfd, "\r\nMAPIFindNext Called\r\n");
+ fprintf(ms_global->dfd, " MAPI_GUARANTEE_FIFO is %s set\r\n",
+ flFlags & MAPI_GUARANTEE_FIFO ? "" : "NOT");
+ fprintf(ms_global->dfd, " MAPI_LONG_MSGID is %s set\r\n",
+ flFlags & MAPI_LONG_MSGID ? "" : "NOT");
+ fprintf(ms_global->dfd, " MAPI_MAPI_UNREAD_ONLY is %s set\r\n",
+ flFlags & MAPI_UNREAD_ONLY ? "" : "NOT");
+ fprintf(ms_global->dfd, " session number is %p\r\n", lhSession);
+ fprintf(ms_global->dfd, " ulUIParam is %d\r\n", ulUIParam);
+ fprintf(ms_global->dfd, "message type is %s\r\n",
+ lpszMessageType ? lpszMessageType : "NOT SET");
+ fprintf(ms_global->dfd, "seed message id is %s\r\n",
+ lpszSeedMessageID ? lpszSeedMessageID : "NOT SET");
+ _flushall();
+ }
+
+ cs = get_session(lhSession);
+ if(!cs){
+ if(MSDEBUG)
+ fprintf(ms_global->dfd, "Session number %p is invalid\r\n", lhSession);
+ return MAPI_E_INVALID_SESSION;
+ }
+
+ if(InitPineSpecific(cs) == -1) return MAPI_E_FAILURE;
+
+ if(!check_mailstream(cs))
+ return MAPI_E_FAILURE;
+ if(lpszSeedMessageID == NULL || *lpszSeedMessageID == '\0')
+ cur_msg = 0;
+ else{
+ cur_msg = convert_to_msgno(lpszSeedMessageID);
+ }
+ if(flFlags & MAPI_UNREAD_ONLY){
+ if(cur_msg + 1 > cs->open_stream->nmsgs)
+ return MAPI_E_NO_MESSAGES;
+ tmp_msgno = cur_msg+1;
+ msg_found = FALSE;
+ while(!msg_found && tmp_msgno <= cs->open_stream->nmsgs){
+ sprintf(tmp, "%d", tmp_msgno);
+ strcpy(tmpseq, tmp);
+ if(tmp_msgno+1 <= cs->open_stream->nmsgs){
+ sprintf(tmp,":%d", min(cs->open_stream->nmsgs,tmp_msgno+100));
+ strcat(tmpseq, tmp);
+ }
+ mail_fetch_flags(cs->open_stream, tmpseq, NIL);
+ for(i = tmp_msgno;
+ i <= (unsigned long)min(cs->open_stream->nmsgs,tmp_msgno+100);
+ i++){
+ telt = mail_elt(cs->open_stream, i);
+ if(telt->seen == 0){
+ msg_found = TRUE;
+ if(MSDEBUG)
+ fprintf(nmg->dfd, "msgno %d is the next UNREAD message after %d",
+ i, cur_msg);
+ break;
+ }
+ }
+ if(!msg_found){
+ if(i == cs->open_stream->nmsgs){
+ if(MSDEBUG)
+ fprintf(nmg->dfd,"No UNREAD messages found after %d\r\n",
+ cur_msg);
+ return MAPI_E_NO_MESSAGES;
+ }
+ tmp_msgno += 100;
+ }
+ }
+ if(msg_found)
+ cur_msg = i;
+ else return MAPI_E_NO_MESSAGES;
+ }
+ else{
+ if(cur_msg+1 > cs->open_stream->nmsgs)
+ return MAPI_E_NO_MESSAGES;
+ cur_msg++;
+ }
+
+ sprintf(lpszMessageID,"%d", cur_msg);
+
+ if(MSDEBUG)
+ fprintf(nmg->dfd, " Next message found is %d\r\n", cur_msg);
+ return SUCCESS_SUCCESS;
+}
+
+
+ULONG FAR PASCAL MAPIReadMail(
+ LHANDLE lhSession,
+ ULONG ulUIParam,
+ LPSTR lpszMessageID,
+ FLAGS flFlags,
+ ULONG ulReserved,
+ lpMapiMessage FAR *lppMessage)
+{
+ mapi_global_s *nmg = ms_global;
+ unsigned rv;
+ unsigned long msgno = 0, flags;
+ sessionlist_s *cs;
+
+ if(MSDEBUG){
+ fprintf(nmg->dfd, "\r\nIn MAPIReadMail\r\n");
+ fprintf(nmg->dfd, " MAPI_PEEK is %s\r\n",
+ flFlags & MAPI_PEEK ? "TRUE":"FALSE");
+ fprintf(nmg->dfd, " MAPI_BODY_AS_FILE is %s\r\n",
+ flFlags & MAPI_BODY_AS_FILE ? "TRUE":"FALSE");
+ fprintf(nmg->dfd, " MAPI_ENVELOPE_ONLY is %s\r\n",
+ flFlags & MAPI_ENVELOPE_ONLY ? "TRUE":"FALSE");
+ fprintf(nmg->dfd, " MAPI_SUPPRESS_ATTACH is %s\r\n",
+ flFlags & MAPI_SUPPRESS_ATTACH ? "TRUE":"FALSE");
+ fprintf(ms_global->dfd, " session number is %p\r\n", lhSession);
+ _flushall();
+ }
+
+ cs = get_session(lhSession);
+ if(!cs){
+ if(MSDEBUG)
+ fprintf(ms_global->dfd, "Session number %p is invalid\r\n", lhSession);
+ return MAPI_E_INVALID_SESSION;
+ }
+
+ if(InitPineSpecific(cs) == -1) return MAPI_E_FAILURE;
+
+ if(!check_mailstream(cs))
+ return MAPI_E_FAILURE;
+
+ msgno = convert_to_msgno(lpszMessageID);
+
+ if(msgno == 0){
+ if(MSDEBUG)
+ fprintf(nmg->dfd, "Invalid Message ID: %s\r\n", lpszMessageID);
+ return MAPI_E_INVALID_MESSAGE;
+ }
+
+ if(MSDEBUG){
+ fprintf(nmg->dfd, "lpszMessageID: %s, converted msgno: %d\r\n",
+ lpszMessageID, msgno);
+ }
+
+ if(flFlags & MAPI_PEEK)
+ flags = FT_PEEK;
+ else flags = NIL;
+
+ rv = fetch_structure_and_attachments(msgno, flags, flFlags, cs);
+ if(rv == MAPI_E_FAILURE)
+ return rv;
+ else if(rv == SUCCESS_SUCCESS){
+ *lppMessage = cs->lpm;
+ return rv;
+ }
+ else
+ return rv;
+}
+
+ULONG FAR PASCAL MAPIAddress(
+ LHANDLE lhSession,
+ ULONG ulUIParam,
+ LPTSTR lpszCaption,
+ ULONG nEditFields,
+ LPTSTR lpszLabels,
+ ULONG nRecips,
+ lpMapiRecipDesc lpRecips,
+ FLAGS flFlags,
+ ULONG ulReserved,
+ LPULONG lpnNewRecips,
+ lpMapiRecipDesc FAR * lppNewRecips)
+{
+ if(MSDEBUG){
+ fprintf(ms_global->dfd, "\r\nIn MAPIAddress\r\n");
+ fprintf(ms_global->dfd, " MAPI_LOGON_UI is %s set\r\n",
+ flFlags & MAPI_LOGON_UI ? "" : "NOT");
+ fprintf(ms_global->dfd, " MAPI_NEW_SESSION is %s set\r\n",
+ flFlags & MAPI_NEW_SESSION ? "" : "NOT");
+ fprintf(ms_global->dfd, " session number is %p\r\n", lhSession);
+ _flushall();
+ }
+ return MAPI_E_NOT_SUPPORTED;
+}
+
+ULONG FAR PASCAL MAPIDeleteMail(
+ LHANDLE lhSession,
+ ULONG ulUIParam,
+ LPTSTR lpszMessageID,
+ FLAGS flFlags,
+ ULONG ulReserved)
+{
+ if(MSDEBUG){
+ fprintf(ms_global->dfd, "\r\nIn MAPIDeleteMail\r\n");
+ fprintf(ms_global->dfd, " session number is %p\r\n", lhSession);
+ _flushall();
+ }
+ return MAPI_E_NOT_SUPPORTED;
+}
+
+ULONG FAR PASCAL MAPIDetails(
+ LHANDLE lhSession,
+ ULONG ulUIParam,
+ lpMapiRecipDesc lpRecip,
+ FLAGS flFlags,
+ ULONG ulReserved)
+{
+ if(MSDEBUG){
+ fprintf(ms_global->dfd, "\r\nIn MAPIDetails\r\n");
+ fprintf(ms_global->dfd, " MAPI_NO_MODIFY is %s set\r\n",
+ flFlags & MAPI_AB_NOMODIFY ? "" : "NOT");
+ fprintf(ms_global->dfd, " MAPI_LOGON_UI is %s set\r\n",
+ flFlags & MAPI_LOGON_UI ? "" : "NOT");
+ fprintf(ms_global->dfd, " MAPI_NEW_SESSION is %s set\r\n",
+ flFlags & MAPI_NEW_SESSION ? "" : "NOT");
+ fprintf(ms_global->dfd, " session number is %p\r\n", lhSession);
+ _flushall();
+ }
+ return MAPI_E_NOT_SUPPORTED;
+}
+
+ULONG FAR PASCAL MAPIFreeBuffer(
+ LPVOID pv)
+{
+ mapi_global_s *nmg = ms_global;
+
+ if(MSDEBUG){
+ fprintf(nmg->dfd, "\r\nMAPIFreeBuffer called, buffer is %p\r\n", pv);
+ _flushall();
+ }
+ free_mbuffer(pv);
+ return SUCCESS_SUCCESS;
+}
+
+ULONG FAR PASCAL MAPIResolveName(
+ LHANDLE lhSession,
+ ULONG ulUIParam,
+ LPTSTR lpszName,
+ FLAGS flFlags,
+ ULONG ulReserved,
+ lpMapiRecipDesc FAR * lppRecip)
+{
+ mapi_global_s *nmg = ms_global;
+ sessionlist_s *cs, *tsession;
+ static char *fakedomain = "@";
+ ADDRESS *adrlist = NULL;
+ char *adrstr, *tadrstr, *tadrstrbuf;
+
+ if(MSDEBUG){
+ fprintf(ms_global->dfd, "\r\nIn MAPIResolveName\r\n");
+ fprintf(ms_global->dfd, " MAPI_NO_MODIFY is %sset\r\n",
+ flFlags & MAPI_AB_NOMODIFY ? "" : "NOT ");
+ fprintf(ms_global->dfd, " MAPI_DIALOG is %sset\r\n",
+ flFlags & MAPI_DIALOG ? "" : "NOT ");
+ fprintf(ms_global->dfd, " MAPI_LOGON_UI is %sset\r\n",
+ flFlags & MAPI_LOGON_UI ? "" : "NOT ");
+ fprintf(ms_global->dfd, " MAPI_NEW_SESSION is %sset\r\n",
+ flFlags & MAPI_NEW_SESSION ? "" : "NOT ");
+ fprintf(ms_global->dfd, " session number is %p\r\n", lhSession);
+ fprintf(ms_global->dfd, " String to Resolve is %s\r\n", lpszName);
+ _flushall();
+ }
+
+ if((flFlags & MAPI_NEW_SESSION) || lhSession == 0)
+ tsession = cs = new_sessionlist();
+ else {
+ cs = get_session(lhSession);
+ if(!cs){
+ if(MSDEBUG)
+ fprintf(ms_global->dfd, "Session number %p is invalid\r\n", lhSession);
+ return MAPI_E_INVALID_SESSION;
+ }
+ }
+ cs->flags.mapi_logon_ui = (flFlags & MAPI_LOGON_UI) ? 1 : 0;
+ if(InitPineSpecific(cs) == -1) return MAPI_E_FAILURE;
+ tadrstrbuf = tadrstr = mstrdup(lpszName);
+ removing_leading_and_trailing_white_space(tadrstr);
+ if(_strnicmp(tadrstr, "SMTP:", 5) == 0){
+ tadrstr += 5;
+ if(tadrstr[0] == '(' && tadrstr[strlen(tadrstr) - 1] == ')'){
+ tadrstr[strlen(tadrstr)-1] = '\0';
+ tadrstr++;
+ removing_leading_and_trailing_white_space(tadrstr);
+ }
+ }
+ rfc822_parse_adrlist(&adrlist, tadrstr,
+ nmg->prcvars[USER_DOMAIN]->val.p ? nmg->prcvars[USER_DOMAIN]->val.p
+ : fakedomain);
+ fs_give((void **)&tadrstrbuf);
+ if(!adrlist || adrlist->next){ /* I guess there aren't supposed to be multiple addresses, */
+ /* which is pretty lame */
+ mail_free_address(&adrlist);
+ if(tsession)
+ fs_give((void **)&tsession);
+ msprint("Returning MAPI_E_AMBIGUOUS_RECIPIENT\r\n");
+ return MAPI_E_AMBIGUOUS_RECIPIENT;
+ }
+ if(!adrlist->host || *adrlist->host == '@'){
+ mail_free_address(&adrlist);
+ if(tsession)
+ fs_give((void **)&tsession);
+ msprint("Returning MAPI_E_AMBIGUOUS_RECIPIENT\r\n");
+ return MAPI_E_AMBIGUOUS_RECIPIENT;
+ }
+
+ (*lppRecip) = new_MapiRecipDesc(1);
+ if(adrlist->personal){
+ (*lppRecip)->lpszName = mstrdup(adrlist->personal);
+ adrlist->personal = NULL;
+ }
+ adrstr = (char *)fs_get((8 + strlen(adrlist->mailbox) + strlen(adrlist->host)) * sizeof(char));
+ sprintf(adrstr, "SMTP:%s@%s", adrlist->mailbox, adrlist->host);
+ (*lppRecip)->lpszAddress = adrstr;
+
+ /* The spec says it's a recipient, so set the thing to MAPI_TO */
+ (*lppRecip)->ulRecipClass = MAPI_TO;
+
+ mail_free_address(&adrlist);
+ msprint1(" Returning with name: %s\r\n", (*lppRecip)->lpszName ? (*lppRecip)->lpszName : "(no name)");
+ msprint1(" address: %s\r\n", (*lppRecip)->lpszAddress ? (*lppRecip)->lpszAddress : "(no address)");
+ msprint(" ulRecipClass: MAPI_TO\r\n");
+ msprint("Returning SUCCESS_SUCCESS\r\n");
+ return SUCCESS_SUCCESS;
+}
+
+ULONG FAR PASCAL MAPISaveMail(
+ LHANDLE lhSession,
+ ULONG ulUIParam,
+ lpMapiMessage lpMessage,
+ FLAGS flFlags,
+ ULONG ulReserved,
+ LPTSTR lpszMessageID)
+{
+ if(MSDEBUG){
+ fprintf(ms_global->dfd, "\r\nIn MAPISaveMail\r\n");
+ fprintf(ms_global->dfd, " MAPI_LOGON_UI is %s set\r\n",
+ flFlags & MAPI_LOGON_UI ? "" : "NOT");
+ fprintf(ms_global->dfd, " MAPI_LONG_MSGID is %s set\r\n",
+ flFlags & MAPI_LONG_MSGID ? "" : "NOT");
+ fprintf(ms_global->dfd, " MAPI_NEW_SESSION is %s set\r\n",
+ flFlags & MAPI_NEW_SESSION ? "" : "NOT");
+ fprintf(ms_global->dfd, " session number is %p\r\n", lhSession);
+ _flushall();
+ }
+ return MAPI_E_NOT_SUPPORTED;
+}
+
+ULONG FAR PASCAL MAPISendDocuments(
+ ULONG ulUIParam,
+ LPTSTR lpszDelimChar,
+ LPTSTR lpszFullPaths,
+ LPTSTR lpszFileNames,
+ ULONG ulReserved)
+{
+ if(MSDEBUG){
+ fprintf(ms_global->dfd, "\r\nIn MAPISendDocuments\r\n");
+ fprintf(ms_global->dfd, " lpszDelimChar: %c\r\n", *lpszDelimChar);
+ fprintf(ms_global->dfd, " lpszFullPaths: %s\r\n", lpszFullPaths);
+ fprintf(ms_global->dfd, " lpszFileNames: %s\r\n", lpszFileNames);
+ _flushall();
+ }
+
+ return send_documents(lpszFullPaths, *lpszDelimChar);
+}
diff --git a/missing b/missing
new file mode 100755
index 00000000..28055d2a
--- /dev/null
+++ b/missing
@@ -0,0 +1,376 @@
+#! /bin/sh
+# Common stub for a few missing GNU programs while installing.
+
+scriptversion=2009-04-28.21; # UTC
+
+# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006,
+# 2008, 2009 Free Software Foundation, Inc.
+# Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
+
+# 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, see <http://www.gnu.org/licenses/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+if test $# -eq 0; then
+ echo 1>&2 "Try \`$0 --help' for more information"
+ exit 1
+fi
+
+run=:
+sed_output='s/.* --output[ =]\([^ ]*\).*/\1/p'
+sed_minuso='s/.* -o \([^ ]*\).*/\1/p'
+
+# In the cases where this matters, `missing' is being run in the
+# srcdir already.
+if test -f configure.ac; then
+ configure_ac=configure.ac
+else
+ configure_ac=configure.in
+fi
+
+msg="missing on your system"
+
+case $1 in
+--run)
+ # Try to run requested program, and just exit if it succeeds.
+ run=
+ shift
+ "$@" && exit 0
+ # Exit code 63 means version mismatch. This often happens
+ # when the user try to use an ancient version of a tool on
+ # a file that requires a minimum version. In this case we
+ # we should proceed has if the program had been absent, or
+ # if --run hadn't been passed.
+ if test $? = 63; then
+ run=:
+ msg="probably too old"
+ fi
+ ;;
+
+ -h|--h|--he|--hel|--help)
+ echo "\
+$0 [OPTION]... PROGRAM [ARGUMENT]...
+
+Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
+error status if there is no known handling for PROGRAM.
+
+Options:
+ -h, --help display this help and exit
+ -v, --version output version information and exit
+ --run try to run the given command, and emulate it if it fails
+
+Supported PROGRAM values:
+ aclocal touch file \`aclocal.m4'
+ autoconf touch file \`configure'
+ autoheader touch file \`config.h.in'
+ autom4te touch the output file, or create a stub one
+ automake touch all \`Makefile.in' files
+ bison create \`y.tab.[ch]', if possible, from existing .[ch]
+ flex create \`lex.yy.c', if possible, from existing .c
+ help2man touch the output file
+ lex create \`lex.yy.c', if possible, from existing .c
+ makeinfo touch the output file
+ tar try tar, gnutar, gtar, then tar without non-portable flags
+ yacc create \`y.tab.[ch]', if possible, from existing .[ch]
+
+Version suffixes to PROGRAM as well as the prefixes \`gnu-', \`gnu', and
+\`g' are ignored when checking the name.
+
+Send bug reports to <bug-automake@gnu.org>."
+ exit $?
+ ;;
+
+ -v|--v|--ve|--ver|--vers|--versi|--versio|--version)
+ echo "missing $scriptversion (GNU Automake)"
+ exit $?
+ ;;
+
+ -*)
+ echo 1>&2 "$0: Unknown \`$1' option"
+ echo 1>&2 "Try \`$0 --help' for more information"
+ exit 1
+ ;;
+
+esac
+
+# normalize program name to check for.
+program=`echo "$1" | sed '
+ s/^gnu-//; t
+ s/^gnu//; t
+ s/^g//; t'`
+
+# Now exit if we have it, but it failed. Also exit now if we
+# don't have it and --version was passed (most likely to detect
+# the program). This is about non-GNU programs, so use $1 not
+# $program.
+case $1 in
+ lex*|yacc*)
+ # Not GNU programs, they don't have --version.
+ ;;
+
+ tar*)
+ if test -n "$run"; then
+ echo 1>&2 "ERROR: \`tar' requires --run"
+ exit 1
+ elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
+ exit 1
+ fi
+ ;;
+
+ *)
+ if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
+ # We have it, but it failed.
+ exit 1
+ elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
+ # Could not run --version or --help. This is probably someone
+ # running `$TOOL --version' or `$TOOL --help' to check whether
+ # $TOOL exists and not knowing $TOOL uses missing.
+ exit 1
+ fi
+ ;;
+esac
+
+# If it does not exist, or fails to run (possibly an outdated version),
+# try to emulate it.
+case $program in
+ aclocal*)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified \`acinclude.m4' or \`${configure_ac}'. You might want
+ to install the \`Automake' and \`Perl' packages. Grab them from
+ any GNU archive site."
+ touch aclocal.m4
+ ;;
+
+ autoconf*)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified \`${configure_ac}'. You might want to install the
+ \`Autoconf' and \`GNU m4' packages. Grab them from any GNU
+ archive site."
+ touch configure
+ ;;
+
+ autoheader*)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified \`acconfig.h' or \`${configure_ac}'. You might want
+ to install the \`Autoconf' and \`GNU m4' packages. Grab them
+ from any GNU archive site."
+ files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}`
+ test -z "$files" && files="config.h"
+ touch_files=
+ for f in $files; do
+ case $f in
+ *:*) touch_files="$touch_files "`echo "$f" |
+ sed -e 's/^[^:]*://' -e 's/:.*//'`;;
+ *) touch_files="$touch_files $f.in";;
+ esac
+ done
+ touch $touch_files
+ ;;
+
+ automake*)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
+ You might want to install the \`Automake' and \`Perl' packages.
+ Grab them from any GNU archive site."
+ find . -type f -name Makefile.am -print |
+ sed 's/\.am$/.in/' |
+ while read f; do touch "$f"; done
+ ;;
+
+ autom4te*)
+ echo 1>&2 "\
+WARNING: \`$1' is needed, but is $msg.
+ You might have modified some files without having the
+ proper tools for further handling them.
+ You can get \`$1' as part of \`Autoconf' from any GNU
+ archive site."
+
+ file=`echo "$*" | sed -n "$sed_output"`
+ test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
+ if test -f "$file"; then
+ touch $file
+ else
+ test -z "$file" || exec >$file
+ echo "#! /bin/sh"
+ echo "# Created by GNU Automake missing as a replacement of"
+ echo "# $ $@"
+ echo "exit 0"
+ chmod +x $file
+ exit 1
+ fi
+ ;;
+
+ bison*|yacc*)
+ echo 1>&2 "\
+WARNING: \`$1' $msg. You should only need it if
+ you modified a \`.y' file. You may need the \`Bison' package
+ in order for those modifications to take effect. You can get
+ \`Bison' from any GNU archive site."
+ rm -f y.tab.c y.tab.h
+ if test $# -ne 1; then
+ eval LASTARG="\${$#}"
+ case $LASTARG in
+ *.y)
+ SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
+ if test -f "$SRCFILE"; then
+ cp "$SRCFILE" y.tab.c
+ fi
+ SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
+ if test -f "$SRCFILE"; then
+ cp "$SRCFILE" y.tab.h
+ fi
+ ;;
+ esac
+ fi
+ if test ! -f y.tab.h; then
+ echo >y.tab.h
+ fi
+ if test ! -f y.tab.c; then
+ echo 'main() { return 0; }' >y.tab.c
+ fi
+ ;;
+
+ lex*|flex*)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified a \`.l' file. You may need the \`Flex' package
+ in order for those modifications to take effect. You can get
+ \`Flex' from any GNU archive site."
+ rm -f lex.yy.c
+ if test $# -ne 1; then
+ eval LASTARG="\${$#}"
+ case $LASTARG in
+ *.l)
+ SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
+ if test -f "$SRCFILE"; then
+ cp "$SRCFILE" lex.yy.c
+ fi
+ ;;
+ esac
+ fi
+ if test ! -f lex.yy.c; then
+ echo 'main() { return 0; }' >lex.yy.c
+ fi
+ ;;
+
+ help2man*)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified a dependency of a manual page. You may need the
+ \`Help2man' package in order for those modifications to take
+ effect. You can get \`Help2man' from any GNU archive site."
+
+ file=`echo "$*" | sed -n "$sed_output"`
+ test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
+ if test -f "$file"; then
+ touch $file
+ else
+ test -z "$file" || exec >$file
+ echo ".ab help2man is required to generate this page"
+ exit $?
+ fi
+ ;;
+
+ makeinfo*)
+ echo 1>&2 "\
+WARNING: \`$1' is $msg. You should only need it if
+ you modified a \`.texi' or \`.texinfo' file, or any other file
+ indirectly affecting the aspect of the manual. The spurious
+ call might also be the consequence of using a buggy \`make' (AIX,
+ DU, IRIX). You might want to install the \`Texinfo' package or
+ the \`GNU make' package. Grab either from any GNU archive site."
+ # The file to touch is that specified with -o ...
+ file=`echo "$*" | sed -n "$sed_output"`
+ test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
+ if test -z "$file"; then
+ # ... or it is the one specified with @setfilename ...
+ infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
+ file=`sed -n '
+ /^@setfilename/{
+ s/.* \([^ ]*\) *$/\1/
+ p
+ q
+ }' $infile`
+ # ... or it is derived from the source name (dir/f.texi becomes f.info)
+ test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info
+ fi
+ # If the file does not exist, the user really needs makeinfo;
+ # let's fail without touching anything.
+ test -f $file || exit 1
+ touch $file
+ ;;
+
+ tar*)
+ shift
+
+ # We have already tried tar in the generic part.
+ # Look for gnutar/gtar before invocation to avoid ugly error
+ # messages.
+ if (gnutar --version > /dev/null 2>&1); then
+ gnutar "$@" && exit 0
+ fi
+ if (gtar --version > /dev/null 2>&1); then
+ gtar "$@" && exit 0
+ fi
+ firstarg="$1"
+ if shift; then
+ case $firstarg in
+ *o*)
+ firstarg=`echo "$firstarg" | sed s/o//`
+ tar "$firstarg" "$@" && exit 0
+ ;;
+ esac
+ case $firstarg in
+ *h*)
+ firstarg=`echo "$firstarg" | sed s/h//`
+ tar "$firstarg" "$@" && exit 0
+ ;;
+ esac
+ fi
+
+ echo 1>&2 "\
+WARNING: I can't seem to be able to run \`tar' with the given arguments.
+ You may want to install GNU tar or Free paxutils, or check the
+ command line arguments."
+ exit 1
+ ;;
+
+ *)
+ echo 1>&2 "\
+WARNING: \`$1' is needed, and is $msg.
+ You might have modified some files without having the
+ proper tools for further handling them. Check the \`README' file,
+ it often tells you about the needed prerequisites for installing
+ this package. You may also peek at any GNU archive site, in case
+ some other package would contain this missing \`$1' program."
+ exit 1
+ ;;
+esac
+
+exit 0
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/mkinstalldirs b/mkinstalldirs
new file mode 100755
index 00000000..4191a45d
--- /dev/null
+++ b/mkinstalldirs
@@ -0,0 +1,162 @@
+#! /bin/sh
+# mkinstalldirs --- make directory hierarchy
+
+scriptversion=2009-04-28.21; # UTC
+
+# Original author: Noah Friedman <friedman@prep.ai.mit.edu>
+# Created: 1993-05-16
+# Public domain.
+#
+# This file is maintained in Automake, please report
+# bugs to <bug-automake@gnu.org> or send patches to
+# <automake-patches@gnu.org>.
+
+nl='
+'
+IFS=" "" $nl"
+errstatus=0
+dirmode=
+
+usage="\
+Usage: mkinstalldirs [-h] [--help] [--version] [-m MODE] DIR ...
+
+Create each directory DIR (with mode MODE, if specified), including all
+leading file name components.
+
+Report bugs to <bug-automake@gnu.org>."
+
+# process command line arguments
+while test $# -gt 0 ; do
+ case $1 in
+ -h | --help | --h*) # -h for help
+ echo "$usage"
+ exit $?
+ ;;
+ -m) # -m PERM arg
+ shift
+ test $# -eq 0 && { echo "$usage" 1>&2; exit 1; }
+ dirmode=$1
+ shift
+ ;;
+ --version)
+ echo "$0 $scriptversion"
+ exit $?
+ ;;
+ --) # stop option processing
+ shift
+ break
+ ;;
+ -*) # unknown option
+ echo "$usage" 1>&2
+ exit 1
+ ;;
+ *) # first non-opt arg
+ break
+ ;;
+ esac
+done
+
+for file
+do
+ if test -d "$file"; then
+ shift
+ else
+ break
+ fi
+done
+
+case $# in
+ 0) exit 0 ;;
+esac
+
+# Solaris 8's mkdir -p isn't thread-safe. If you mkdir -p a/b and
+# mkdir -p a/c at the same time, both will detect that a is missing,
+# one will create a, then the other will try to create a and die with
+# a "File exists" error. This is a problem when calling mkinstalldirs
+# from a parallel make. We use --version in the probe to restrict
+# ourselves to GNU mkdir, which is thread-safe.
+case $dirmode in
+ '')
+ if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then
+ echo "mkdir -p -- $*"
+ exec mkdir -p -- "$@"
+ else
+ # On NextStep and OpenStep, the `mkdir' command does not
+ # recognize any option. It will interpret all options as
+ # directories to create, and then abort because `.' already
+ # exists.
+ test -d ./-p && rmdir ./-p
+ test -d ./--version && rmdir ./--version
+ fi
+ ;;
+ *)
+ if mkdir -m "$dirmode" -p --version . >/dev/null 2>&1 &&
+ test ! -d ./--version; then
+ echo "mkdir -m $dirmode -p -- $*"
+ exec mkdir -m "$dirmode" -p -- "$@"
+ else
+ # Clean up after NextStep and OpenStep mkdir.
+ for d in ./-m ./-p ./--version "./$dirmode";
+ do
+ test -d $d && rmdir $d
+ done
+ fi
+ ;;
+esac
+
+for file
+do
+ case $file in
+ /*) pathcomp=/ ;;
+ *) pathcomp= ;;
+ esac
+ oIFS=$IFS
+ IFS=/
+ set fnord $file
+ shift
+ IFS=$oIFS
+
+ for d
+ do
+ test "x$d" = x && continue
+
+ pathcomp=$pathcomp$d
+ case $pathcomp in
+ -*) pathcomp=./$pathcomp ;;
+ esac
+
+ if test ! -d "$pathcomp"; then
+ echo "mkdir $pathcomp"
+
+ mkdir "$pathcomp" || lasterr=$?
+
+ if test ! -d "$pathcomp"; then
+ errstatus=$lasterr
+ else
+ if test ! -z "$dirmode"; then
+ echo "chmod $dirmode $pathcomp"
+ lasterr=
+ chmod "$dirmode" "$pathcomp" || lasterr=$?
+
+ if test ! -z "$lasterr"; then
+ errstatus=$lasterr
+ fi
+ fi
+ fi
+ fi
+
+ pathcomp=$pathcomp/
+ done
+done
+
+exit $errstatus
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/packages/README b/packages/README
new file mode 100644
index 00000000..501c7cf7
--- /dev/null
+++ b/packages/README
@@ -0,0 +1,81 @@
+Last changed: 06 Apr 2007
+
+Building packages for Alpine
+
+Files in this directory:
+ alpine.spec - spec file for building RPMs on RedHat and Fedora.
+ It should also work with Mandrake.
+ debian/* - files containing instructions for building .deb files
+ for use with dpkg
+ windows/* - files used to build the Windows Alpine installer. Upon
+ release, we set up directory dist.wnt.d with all of the
+ files to be installed, and we then run the command
+ binstaller wnt 0.98 (or whatever the current version is).
+ These scripts are used with Inno Setup 5.
+ README - this file.
+
+
+Most Linux distributions include a package management system to
+simplify the installing and uninstalling of programs. There are two
+types of these systems commonly used; RPM is the system used by
+default on RedHat, Fedora, Ubuntu, Mandrake, and SuSE, and dpkg
+is a system used by default on Debian and Mac OS X's fink.
+
+
+Why build your own packages?
+
+An RPM and a .deb file are already provided on the Alpine Information
+Center (http://www.washington.edu/alpine). Additionally, many
+distributions roll their own alpine packages. The files provided here
+will allow people to build their own versions of the same package in
+the event that the provided packages fail to install due to
+compatibility issues, or in the case where one would want to have a
+modified alpine package. Unlike the alpine packages provided by the
+various distributions, the packages built with these scripts are built
+directly from source, and will be released concurrently with every
+alpine release. It may be preferable to use the distributions' alpine
+packages, as they do tend to add value which is intended to enhance
+their users' experience.
+
+
+Building RPMs:
+
+For building RPMs, first find the directory where RPMs are built on
+your system. On Redhat 9, this directory is /usr/src/redhat. You can
+change this directory by editing your .rpmmacros file. Also, you need
+not be root to build an rpm. To build the RPM, place the
+alpine-{version}.tar.gz file in the SOURCES directory, and place the
+alpine.spec file contained here in the SPECS directory. Then, you need
+only issue the command:
+
+rpmbuild -ba alpine.spec
+
+The RPM file will be placed in the RPMS directory, and a source RPM
+will be placed in the SRPMS directory. Installing an RPM is done with
+the command:
+
+rpm -ivh alpine-{version}-1.i386.rpm
+
+
+Building .deb files:
+
+For building .deb files, dpkg, the program that builds them, looks in
+the "debian" directory for various control files and build scripts. This
+directory should reside in the top level of the alpine directory, in the
+same directory as the "build" file. This requires copying the
+debian directory contained here to the next level up, which can be
+done via the command:
+
+cp -pr debian ../
+
+Then, from alpine's root directory, you would issue the command:
+
+dpkg-buildpackage
+
+Note that you must be root in order to build .deb packages. After
+running the above command, the .deb will have been built in the
+directory above the alpine root directory, and it would be of the form
+alpine_{version}_i386.deb. You can then install this package by
+running the command:
+
+dpkg -i alpine_{version}_i386.deb
diff --git a/packages/alpine.spec b/packages/alpine.spec
new file mode 100644
index 00000000..152cc820
--- /dev/null
+++ b/packages/alpine.spec
@@ -0,0 +1,70 @@
+Summary: University of Washington Alpine mail user agent
+Name: alpine
+Version: 2.10
+Release: 1
+License: Apache License
+Group: Applications/Mail
+Source: http://patches.freeiz.com/alpine/patches/%{name}-%{version}/%{name}-%{version}.clean.tar.lzma
+URL: http://patches.freeiz.com/alpine/
+Vendor: Patches for Pine and Alpine
+Packager: Eduardo Chappa <chappa@gmx.com>
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot
+
+%description
+Alpine -- an Alternatively Licensed Program for Internet
+News & Email -- is a tool for reading, sending, and managing
+electronic messages. Alpine is the successor to Pine and was
+developed until 2008 by Computing & Communications at the
+University of Washington.
+ Though originally designed for inexperienced email users,
+Alpine supports many advanced features, and an ever-growing number of
+configuration and personal-preference options.
+
+%prep
+%setup -q -n %{name}-%{version}
+
+%build
+touch imap/ip6
+./configure --without-krb5 --with-spellcheck-prog=aspell
+make
+
+%install
+[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT
+install -D -m755 alpine/alpine $RPM_BUILD_ROOT%{_bindir}/alpine
+install -D -m755 pico/pico $RPM_BUILD_ROOT%{_bindir}/pico
+install -D -m755 pico/pilot $RPM_BUILD_ROOT%{_bindir}/pilot
+install -D -m755 alpine/rpload $RPM_BUILD_ROOT%{_bindir}/rpload
+install -D -m755 alpine/rpdump $RPM_BUILD_ROOT%{_bindir}/rpdump
+install -D -m755 imap/mailutil/mailutil $RPM_BUILD_ROOT%{_bindir}/mailutil
+if ! install -D -m2755 -gmail imap/mlock/mlock $RPM_BUILD_ROOT%{_sbindir}/mlock; then
+install -D -m755 imap/mlock/mlock $RPM_BUILD_ROOT%{_sbindir}/mlock
+echo "*** DO NOT FORGET TO DO THE FOLLOWING BY HAND while root:
+*** chgrp mail $RPM_BUILD_ROOT%{_sysconfdir}/mlock
+*** echo chmod 2755 $RPM_BUILD_ROOT%{_sysconfdir}/mlock"
+fi
+install -D -m644 doc/alpine.1 $RPM_BUILD_ROOT%{_mandir}/man1/alpine.1
+install -D -m644 doc/pico.1 $RPM_BUILD_ROOT%{_mandir}/man1/pico.1
+install -D -m644 doc/pilot.1 $RPM_BUILD_ROOT%{_mandir}/man1/pilot.1
+install -D -m644 doc/rpload.1 $RPM_BUILD_ROOT%{_mandir}/man1/rpload.1
+install -D -m644 doc/rpdump.1 $RPM_BUILD_ROOT%{_mandir}/man1/rpdump.1
+install -D -m644 imap/src/mailutil/mailutil.1 $RPM_BUILD_ROOT%{_mandir}/man1/mailutil.1
+
+%clean
+[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT
+
+%files
+%defattr(-,root,root)
+%doc README LICENSE doc/tech-notes.txt
+%{_bindir}/alpine
+%{_bindir}/pico
+%{_bindir}/pilot
+%{_bindir}/rpload
+%{_bindir}/rpdump
+%{_bindir}/mailutil
+%attr(2755, root, mail) %{_sbindir}/mlock
+%{_mandir}/man1/alpine.1*
+%{_mandir}/man1/pico.1*
+%{_mandir}/man1/pilot.1*
+%{_mandir}/man1/rpload.1*
+%{_mandir}/man1/rpdump.1*
+%{_mandir}/man1/mailutil.1*
diff --git a/packages/debian/changelog b/packages/debian/changelog
new file mode 100644
index 00000000..07849e93
--- /dev/null
+++ b/packages/debian/changelog
@@ -0,0 +1,5 @@
+alpine (2.01) unstable; urgency=low
+
+ * University of Washington unmodified Alpine release 2.01 package
+
+ -- Alpine Contact <alpine-contact@u.washington.edu> Tue, 27 January 2009 20:20:02 -0800
diff --git a/packages/debian/control b/packages/debian/control
new file mode 100644
index 00000000..7e94e885
--- /dev/null
+++ b/packages/debian/control
@@ -0,0 +1,22 @@
+Source: alpine
+Section: non-free/mail
+Priority: optional
+Maintainer: Alpine Contact <alpine-contact@u.washington.edu>
+Standards-Version: 3.6.1.0
+Build-Depends: libncurses-dev | libncurses5-dev, libldap-dev | libldap2-dev, libssl-dev, libpam-dev | libpam0g-dev
+
+Package: alpine
+Architecture: any
+Depends: ${shlibs:Depends}, mime-support
+Suggests: exim | mail-transport-agent, spell
+Provides: mail-reader
+Description: Alpine -- an Alternatively Licensed Program for Internet
+ News & Email -- is a tool for reading, sending, and managing
+ electronic messages. Alpine is the successor to Pine and was
+ developed by Computing & Communications at the University of
+ Washington.
+ Though originally designed for inexperienced email users,
+ Alpine supports many advanced features, and an ever-growing number of
+ configuration and personal-preference options.
+ .
+ Homepage: http://www.washington.edu/pine
diff --git a/packages/debian/menu b/packages/debian/menu
new file mode 100644
index 00000000..77ed3fe1
--- /dev/null
+++ b/packages/debian/menu
@@ -0,0 +1,2 @@
+?package(alpine):needs="text" section="Apps/Net" title="Alpine" command="/usr/bin/alpine"
+?package(alpine):needs="text" section="Apps/Editors" title="Pico" command="/usr/bin/pico" \ No newline at end of file
diff --git a/packages/debian/postinst b/packages/debian/postinst
new file mode 100644
index 00000000..6c138e60
--- /dev/null
+++ b/packages/debian/postinst
@@ -0,0 +1,5 @@
+#!/bin/sh
+set -e
+if [ -x /usr/bin/update-menus ]; then
+ update-menus
+fi
diff --git a/packages/debian/postrm b/packages/debian/postrm
new file mode 100644
index 00000000..028ea70e
--- /dev/null
+++ b/packages/debian/postrm
@@ -0,0 +1,13 @@
+#!/bin/sh
+set -e
+if [ -x /usr/bin/update-menus ]; then
+ update-menus
+fi
+
+if [ remove = "$1" ]; then
+ dpkg-divert --quiet --package alpine --remove --rename \
+ --divert /usr/bin/clone-editor-moved /usr/bin/pico
+ dpkg-divert --quiet --package alpine --remove --rename \
+ --divert /usr/share/man/man1/clone-editor-moved.1.gz \
+ /usr/share/man/man1/pico.1.gz
+fi
diff --git a/packages/debian/preinst b/packages/debian/preinst
new file mode 100644
index 00000000..0cb00df1
--- /dev/null
+++ b/packages/debian/preinst
@@ -0,0 +1,9 @@
+#!/bin/sh
+set -e
+if [ install = "$1" ]; then
+ dpkg-divert --quiet --package alpine --add --rename \
+ --divert /usr/bin/clone-editor-moved /usr/bin/pico
+ dpkg-divert --quiet --package alpine --add --rename \
+ --divert /usr/share/man/man1/clone-editor-moved.1.gz \
+ /usr/share/man/man1/pico.1.gz
+fi
diff --git a/packages/debian/rules b/packages/debian/rules
new file mode 100755
index 00000000..d539e4b8
--- /dev/null
+++ b/packages/debian/rules
@@ -0,0 +1,79 @@
+#!/usr/bin/make -f
+
+DEBUG =
+INSTALL_PROGRAM = install
+
+ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS)))
+ DEBUG = -DDEBUG -g
+endif
+
+ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
+ INSTALL_PROGRAM += -s
+endif
+
+build: build-all
+
+build-all:
+ $(checkdir)
+ touch imap/ip6
+ ./configure
+ make
+ touch build-all
+
+clean: checkroot
+ $(checkdir)
+ -rm -f build-all
+ -rm -rf debian/tmp debian/files* core debian/substvars
+
+binary-indep: checkroot build-all
+
+binary-arch: checkroot build-all
+ $(checkdir)
+ -rm -rf debian/tmp
+ install -D -m755 debian/preinst debian/tmp/DEBIAN/preinst
+ install -D -m755 debian/postinst debian/tmp/DEBIAN/postinst
+ install -D -m755 debian/postrm debian/tmp/DEBIAN/postrm
+ install -D -m644 debian/menu debian/tmp/usr/lib/menu/alpine
+
+ $(INSTALL_PROGRAM) -D -m755 alpine/alpine debian/tmp/usr/bin/alpine
+ $(INSTALL_PROGRAM) -D -m755 imap/mlock/mlock debian/tmp/usr/sbin/mlock
+ $(INSTALL_PROGRAM) -D -m755 alpine/rpload debian/tmp/usr/bin/rpload
+ $(INSTALL_PROGRAM) -D -m755 alpine/rpdump debian/tmp/usr/bin/rpdump
+ $(INSTALL_PROGRAM) -D -m755 pico/pico debian/tmp/usr/bin/pico
+ $(INSTALL_PROGRAM) -D -m755 pico/pilot debian/tmp/usr/bin/pilot
+ $(INSTALL_PROGRAM) -D -m755 imap/mailutil/mailutil debian/tmp/usr/bin/mailutil
+
+ install -D -m644 doc/alpine.1 debian/tmp/usr/share/man/man1/alpine.1
+ install -D -m644 doc/rpload.1 debian/tmp/usr/share/man/man1/rpload.1
+ install -D -m644 doc/rpdump.1 debian/tmp/usr/share/man/man1/rpdump.1
+ install -D -m644 doc/pico.1 debian/tmp/usr/share/man/man1/pico.1
+ install -D -m644 doc/pilot.1 debian/tmp/usr/share/man/man1/pilot.1
+ install -D -m644 imap/src/mailutil/mailutil.1 debian/tmp/usr/share/man/man1/mailutil.1
+ install -D -m644 doc/tech-notes.txt debian/tmp/usr/share/doc/alpine/tech-notes.txt
+ install -D -m644 LICENSE debian/tmp/usr/share/doc/alpine/license
+ install -D -m644 README debian/tmp/usr/share/doc/alpine/README
+ install -D -m644 debian/changelog debian/tmp/usr/share/doc/alpine/changelog
+
+ gzip -r9 debian/tmp/usr/share/man
+ gzip -9 debian/tmp/usr/share/doc/alpine/changelog
+ dpkg-shlibdeps alpine/alpine pico/pico pico/pilot imap/mlock/mlock alpine/rpload alpine/rpdump
+ cd debian/tmp && md5sum `find * -type f ! -regex "DEBIAN/.*"` > DEBIAN/md5sums
+ dpkg-gencontrol -isp -palpine
+ chown -R root.root debian/tmp
+ chmod -R go=rX debian/tmp
+ chown root.mail debian/tmp/usr/sbin/mlock
+ chmod 2755 debian/tmp/usr/sbin/mlock
+ dpkg --build debian/tmp ..
+
+define checkdir
+ test -f alpine/alpine.c -a -f debian/rules
+endef
+
+binary: binary-indep binary-arch
+
+checkroot:
+ $(checkdir)
+ test root = "`whoami`"
+
+.PHONY: binary binary-arch binary-indep clean checkroot
+
diff --git a/packages/windows/alpine.iss b/packages/windows/alpine.iss
new file mode 100755
index 00000000..5a00af72
--- /dev/null
+++ b/packages/windows/alpine.iss
@@ -0,0 +1,60 @@
+[Setup]
+AppName=Alpine
+#include "iss.SetupVars.tmp"
+;AppVerName=Alpine 0.98
+AppPublisher=University of Washington
+AppPublisherURL=http://www.washington.edu/alpine/
+AppSupportURL=http://www.washington.edu/alpine/faq/
+AppUpdatesURL=http://www.washington.edu/alpine/getalpine/
+DefaultDirName={pf}\Alpine
+DefaultGroupName=Alpine
+AllowNoIcons=yes
+;SourceDir=E:\CreateInstall
+OutputDir=..\
+;OutputBaseFilename=setup_alpine_0.98
+LicenseFile=legal.txt
+;InfoBeforeFile=install.txt
+UninstallDisplayIcon={app}\alpine.exe
+;UninstallDisplayName=Alpine 0.98
+UninstallFilesDir={app}\uninst_alpine
+Compression=bzip/9
+
+[Tasks]
+Name: "desktopicon"; Description: "Create a &desktop icon"; GroupDescription: "Additional icons:"
+Name: "quicklaunchicon"; Description: "Create a &Quick Launch icon"; GroupDescription: "Additional icons:"; Flags: unchecked
+
+[Files]
+Source: "alpine.exe"; DestDir: "{app}"; Flags: ignoreversion
+Source: "install.txt"; DestDir: "{app}"; Flags: ignoreversion
+Source: "legal.txt"; DestDir: "{app}"; Flags: ignoreversion
+Source: "ldap32.dll"; DestDir: "{app}"; Flags: ignoreversion
+Source: "mailcap.sam"; DestDir: "{app}"; Flags: ignoreversion
+Source: "mimetype.sam"; DestDir: "{app}"; Flags: ignoreversion
+Source: "pico.exe"; DestDir: "{app}"; Flags: ignoreversion
+Source: "pinerc.adv"; DestDir: "{app}"; Flags: ignoreversion
+; NOTE: Don't use "Flags: ignoreversion" on any shared system files
+
+[INI]
+Filename: "{app}\alpine.url"; Section: "InternetShortcut"; Key: "URL"; String: "http://www.washington.edu/alpine/"
+
+[Icons]
+Name: "{group}\Alpine"; Filename: "{app}\alpine.exe"
+Name: "{group}\Pico"; Filename: "{app}\pico.exe"
+Name: "{group}\Alpine on the Web"; Filename: "{app}\alpine.url"
+Name: "{group}\Uninstall Alpine"; Filename: "{uninstallexe}"
+Name: "{userdesktop}\Alpine"; Filename: "{app}\alpine.exe"; Tasks: desktopicon
+Name: "{userdesktop}\Pico"; Filename: "{app}\pico.exe"; Tasks: desktopicon
+Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\Alpine"; Filename: "{app}\alpine.exe"; Tasks: quicklaunchicon
+Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\Pico"; Filename: "{app}\pico.exe"; Tasks: quicklaunchicon
+
+[Run]
+Filename: "{app}\alpine.exe"; Parameters: "-nosplash -install" ; Flags: skipifsilent
+Filename: "{app}\alpine.exe"; Description: "Launch Alpine"; Flags: nowait postinstall skipifsilent
+Filename: "{app}\pico.exe"; Description: "Launch Pico"; Flags: nowait postinstall skipifsilent unchecked
+Filename: "{app}\install.txt"; Description: "View Alpine README"; Flags: postinstall shellexec skipifsilent
+
+[UninstallDelete]
+Type: files; Name: "{app}\alpine.url"
+
+[UninstallRun]
+Filename: "{app}\alpine.exe"; Parameters: "-nosplash -uninstall"
diff --git a/packages/windows/binstaller.bat b/packages/windows/binstaller.bat
new file mode 100644
index 00000000..8f5700a9
--- /dev/null
+++ b/packages/windows/binstaller.bat
@@ -0,0 +1,45 @@
+@echo OFF
+if "%1"=="" goto blankplat
+if "%2"=="" goto blankver
+if "%1"=="wnt" goto goodbuild
+if "%1"=="w2k" goto goodbuild
+echo Unknown build platform: %1 %2
+goto usage
+:blankplat
+echo Must specify build platform (wnt, w2k)!
+goto usage
+:blankver
+echo Must specify build version (eg. 0.98)
+:usage
+echo usage: BINSTALLER plat ver
+echo where plat is wnt or w2k
+echo and ver is version (eg 0.98)
+goto fini
+
+
+:goodbuild
+set plat=%1
+set ver=%2
+set tmpfile=iss.SetupVars.tmp
+echo Building installer for platform %plat% for version %ver%
+echo Assuming existence of dist.%plat%.d!
+echo SourceDir=dist.%plat%.d > %tmpfile%
+goto %plat%
+
+:wnt
+echo AppVerName=Alpine %ver% >> %tmpfile%
+echo OutputBaseFilename=setup_alpine_%ver% >> %tmpfile%
+echo UninstallDisplayName=Alpine %ver% >> %tmpfile%
+goto goodbuildcont
+:w2k
+echo AppVerName=Alpine %ver% (with Windows Kerberos) >> %tmpfile%
+echo OutputBaseFilename=setup_alpine_%ver%_w2k >> %tmpfile%
+echo UninstallDisplayName=Alpine %ver% (with Windows Kerberos) >> %tmpfile%
+goto goodbuildcont
+
+:goodbuildcont
+iscc alpine.iss
+del %tmpfile%
+echo Done.
+
+:fini
diff --git a/pico/Makefile.am b/pico/Makefile.am
new file mode 100644
index 00000000..444eea3b
--- /dev/null
+++ b/pico/Makefile.am
@@ -0,0 +1,37 @@
+## Process this file with automake to produce Makefile.in
+## Use aclocal -I m4; automake
+
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+SUBDIRS = osdep
+
+bin_PROGRAMS = pico pilot
+
+pico_SOURCES = main.c utf8stub.c
+
+pilot_SOURCES = pilot.c utf8stub.c
+
+pico_LDADD = $(LDADD) $(INTLLIBS)
+
+pilot_LDADD = $(LDADD) $(INTLLIBS)
+
+LDADD = ../c-client/utf8.o libpico.a osdep/libpicoosd.a \
+ ../pith/osdep/libpithosd.a ../pith/charconv/libpithcc.a
+
+noinst_LIBRARIES = libpico.a
+
+libpico_a_SOURCES = attach.c basic.c bind.c browse.c buffer.c composer.c display.c file.c \
+ fileio.c line.c pico.c random.c region.c search.c window.c word.c \
+ ebind.h edef.h efunc.h estruct.h headers.h keydefs.h mode.h pico.h
+
+AM_CPPFLAGS = -I@top_srcdir@/include
+
diff --git a/pico/Makefile.in b/pico/Makefile.in
new file mode 100644
index 00000000..a27487ee
--- /dev/null
+++ b/pico/Makefile.in
@@ -0,0 +1,777 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+bin_PROGRAMS = pico$(EXEEXT) pilot$(EXEEXT)
+subdir = pico
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/acx_pthread.m4 \
+ $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
+ $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/VERSION \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/include/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+ARFLAGS = cru
+libpico_a_AR = $(AR) $(ARFLAGS)
+libpico_a_LIBADD =
+am_libpico_a_OBJECTS = attach.$(OBJEXT) basic.$(OBJEXT) bind.$(OBJEXT) \
+ browse.$(OBJEXT) buffer.$(OBJEXT) composer.$(OBJEXT) \
+ display.$(OBJEXT) file.$(OBJEXT) fileio.$(OBJEXT) \
+ line.$(OBJEXT) pico.$(OBJEXT) random.$(OBJEXT) \
+ region.$(OBJEXT) search.$(OBJEXT) window.$(OBJEXT) \
+ word.$(OBJEXT)
+libpico_a_OBJECTS = $(am_libpico_a_OBJECTS)
+am__installdirs = "$(DESTDIR)$(bindir)"
+PROGRAMS = $(bin_PROGRAMS)
+am_pico_OBJECTS = main.$(OBJEXT) utf8stub.$(OBJEXT)
+pico_OBJECTS = $(am_pico_OBJECTS)
+am__DEPENDENCIES_1 =
+pico_DEPENDENCIES = $(LDADD) $(am__DEPENDENCIES_1)
+am_pilot_OBJECTS = pilot.$(OBJEXT) utf8stub.$(OBJEXT)
+pilot_OBJECTS = $(am_pilot_OBJECTS)
+pilot_DEPENDENCIES = $(LDADD) $(am__DEPENDENCIES_1)
+DEFAULT_INCLUDES =
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+SOURCES = $(libpico_a_SOURCES) $(pico_SOURCES) $(pilot_SOURCES)
+DIST_SOURCES = $(libpico_a_SOURCES) $(pico_SOURCES) $(pilot_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
+ html-recursive info-recursive install-data-recursive \
+ install-dvi-recursive install-exec-recursive \
+ install-html-recursive install-info-recursive \
+ install-pdf-recursive install-ps-recursive install-recursive \
+ installcheck-recursive installdirs-recursive pdf-recursive \
+ ps-recursive uninstall-recursive
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \
+ $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \
+ distdir
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_CFLAGS = @AM_CFLAGS@
+AM_LDFLAGS = @AM_LDFLAGS@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CP = @CP@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+C_CLIENT_CFLAGS = @C_CLIENT_CFLAGS@
+C_CLIENT_GCCOPTLEVEL = @C_CLIENT_GCCOPTLEVEL@
+C_CLIENT_LDFLAGS = @C_CLIENT_LDFLAGS@
+C_CLIENT_SPECIALS = @C_CLIENT_SPECIALS@
+C_CLIENT_TARGET = @C_CLIENT_TARGET@
+C_CLIENT_WITH_IPV6 = @C_CLIENT_WITH_IPV6@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISPELLPROG = @ISPELLPROG@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN = @LN@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKE = @MAKE@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NPA_PROG = @NPA_PROG@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+POSUB = @POSUB@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+PWPROG = @PWPROG@
+RANLIB = @RANLIB@
+REGEX_BUILD = @REGEX_BUILD@
+RM = @RM@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPELLPROG = @SPELLPROG@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEB_BINDIR = @WEB_BINDIR@
+WEB_BUILD = @WEB_BUILD@
+WEB_PUBCOOKIE_BUILD = @WEB_PUBCOOKIE_BUILD@
+WEB_PUBCOOKIE_LIB = @WEB_PUBCOOKIE_LIB@
+WEB_PUBCOOKIE_LINK = @WEB_PUBCOOKIE_LINK@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+acx_pthread_config = @acx_pthread_config@
+alpine_interactive_spellcheck = @alpine_interactive_spellcheck@
+alpine_simple_spellcheck = @alpine_simple_spellcheck@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SUBDIRS = osdep
+pico_SOURCES = main.c utf8stub.c
+pilot_SOURCES = pilot.c utf8stub.c
+pico_LDADD = $(LDADD) $(INTLLIBS)
+pilot_LDADD = $(LDADD) $(INTLLIBS)
+LDADD = ../c-client/utf8.o libpico.a osdep/libpicoosd.a \
+ ../pith/osdep/libpithosd.a ../pith/charconv/libpithcc.a
+
+noinst_LIBRARIES = libpico.a
+libpico_a_SOURCES = attach.c basic.c bind.c browse.c buffer.c composer.c display.c file.c \
+ fileio.c line.c pico.c random.c region.c search.c window.c word.c \
+ ebind.h edef.h efunc.h estruct.h headers.h keydefs.h mode.h pico.h
+
+AM_CPPFLAGS = -I@top_srcdir@/include
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign pico/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign pico/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLIBRARIES:
+ -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+libpico.a: $(libpico_a_OBJECTS) $(libpico_a_DEPENDENCIES)
+ -rm -f libpico.a
+ $(libpico_a_AR) libpico.a $(libpico_a_OBJECTS) $(libpico_a_LIBADD)
+ $(RANLIB) libpico.a
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p || test -f $$p1; \
+ then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(bindir)" && rm -f $$files
+
+clean-binPROGRAMS:
+ @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+pico$(EXEEXT): $(pico_OBJECTS) $(pico_DEPENDENCIES)
+ @rm -f pico$(EXEEXT)
+ $(LINK) $(pico_OBJECTS) $(pico_LDADD) $(LIBS)
+pilot$(EXEEXT): $(pilot_OBJECTS) $(pilot_DEPENDENCIES)
+ @rm -f pilot$(EXEEXT)
+ $(LINK) $(pilot_OBJECTS) $(pilot_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/attach.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/basic.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bind.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/browse.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/buffer.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/composer.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/display.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fileio.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/line.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pico.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pilot.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/random.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/region.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/search.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utf8stub.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/window.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/word.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run `make' without going through this Makefile.
+# To change the values of `make' variables: instead of editing Makefiles,
+# (1) if the variable is set in `config.status', edit `config.status'
+# (which will cause the Makefiles to be regenerated when you run `make');
+# (2) otherwise, pass the desired values on the `make' command line.
+$(RECURSIVE_TARGETS):
+ @fail= failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+$(RECURSIVE_CLEAN_TARGETS):
+ @fail= failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ rev=''; for subdir in $$list; do \
+ if test "$$subdir" = "."; then :; else \
+ rev="$$subdir $$rev"; \
+ fi; \
+ done; \
+ rev="$$rev ."; \
+ target=`echo $@ | sed s/-recursive//`; \
+ for subdir in $$rev; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done && test -z "$$fail"
+tags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+ done
+ctags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
+ done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(LIBRARIES) $(PROGRAMS)
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(bindir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-binPROGRAMS clean-generic clean-libtool \
+ clean-noinstLIBRARIES mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am: install-binPROGRAMS
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS
+
+.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) ctags-recursive \
+ install-am install-strip tags-recursive
+
+.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \
+ all all-am check check-am clean clean-binPROGRAMS \
+ clean-generic clean-libtool clean-noinstLIBRARIES ctags \
+ ctags-recursive distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-binPROGRAMS \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs installdirs-am \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags tags-recursive uninstall uninstall-am \
+ uninstall-binPROGRAMS
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pico/attach.c b/pico/attach.c
new file mode 100644
index 00000000..05605f96
--- /dev/null
+++ b/pico/attach.c
@@ -0,0 +1,1515 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: attach.c 1082 2008-06-12 18:39:50Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ *
+ * Program: Routines to support attachments in the Pine composer
+ */
+
+#include "headers.h"
+#include "../pith/charconv/filesys.h"
+#include "../pith/string.h"
+
+#include <math.h>
+
+#ifdef ATTACHMENTS
+
+
+int ParseAttach(struct hdr_line **, int *, char *,
+ size_t, char *, size_t, char *, size_t, int *);
+PATMT *NewAttach(char *, long, char *);
+void ZotAttach(struct pico_atmt *);
+void sinserts(UCS *, int, UCS *, int);
+int AttachUpload(char *, size_t, char *, size_t);
+int AttachCancel(char *);
+
+
+#define HIBIT_WARN "Only ASCII characters allowed in attachment comments"
+
+
+/*
+ * AskAttach - ask for attachment fields and build resulting structure
+ * return pointer to that struct if OK, NULL otherwise
+ */
+int
+AskAttach(char *cmnt, size_t cmntlen, LMLIST **lm)
+{
+ int i, status, fbrv, upload = 0;
+ int fb_flags = FB_READ | FB_ATTACH;
+ off_t attsz = 0;
+ size_t len;
+ char bfn[NLINE];
+ char fn[NLINE], sz[32];
+ LMLIST *new;
+ EML eml;
+
+ i = 2; /* 2 is prompt for file, 1 is prompt for comment */
+ fn[0] = '\0';
+ sz[0] = '\0';
+ cmnt[0] = '\0';
+
+ while(i){
+ if(i == 2){
+ EXTRAKEYS menu_attach[4];
+ int n;
+
+ menu_attach[n = 0].name = "^T";
+ menu_attach[n].label = N_("To Files");
+ menu_attach[n].key = (CTRL|'T');
+
+ if(gmode & MDCMPLT){
+ menu_attach[++n].name = "TAB";
+ menu_attach[n].label = N_("Complete");
+ menu_attach[n].key = (CTRL|'I');
+ }
+
+#if !defined(DOS) && !defined(MAC)
+ if(Pmaster && Pmaster->upload){
+ /*
+ * The Plan: ^R prompts for uploaded file's name which
+ * is passed to the defined upload command when the user
+ * hits Return to confirm the name.
+ * NOTE: this is different than upload into message
+ * text in which case the uploaded name isn't useful so
+ * a temp file is ok (be sure to fix the file mode).
+ */
+ menu_attach[++n].name = "^Y";
+ menu_attach[n].key = (CTRL|'Y');
+ /* TRANSLATORS: Read File is a prompt for the name of
+ a file to be read into the composer. */
+ menu_attach[n].label = upload ? N_("Read File") : N_("RcvUpload");
+ }
+#endif
+
+ menu_attach[++n].name = NULL;
+ KS_OSDATASET(&menu_attach[0], KS_NONE);
+ status = mlreply_utf8(upload ? _("Name to give uploaded attachment: ")
+ /* TRANSLATORS: User is being asked for the name
+ of the file they want to attach to a message. */
+ : _("File to attach: "),
+ fn, sizeof(fn), QNORML, menu_attach);
+ }
+ else
+ /* TRANSLATORS: This is a prompt for a comment about the file
+ they have attached. */
+ status = mlreply_utf8(_("Attachment comment: "), cmnt, cmntlen, QNODQT, NULL);
+
+ switch(status){
+ case HELPCH:
+ if(Pmaster){
+ VARS_TO_SAVE *saved_state;
+
+ saved_state = save_pico_state();
+ (*Pmaster->helper)(Pmaster->attach_help, _("Attach Help"), 1);
+ if(saved_state){
+ restore_pico_state(saved_state);
+ free_pico_state(saved_state);
+ }
+
+ pico_refresh(FALSE, 1);
+ update();
+ continue;
+ }
+ else{
+ eml.s = (i == 2) ? "file" : "comment";
+ emlwrite("No Attachment %s help yet!", &eml);
+ sleep(3);
+ }
+
+ break;
+
+ case (CTRL|'I') :
+ if(i == 2){
+ char *fname, *p;
+ int dirlen;
+
+ bfn[0] = '\0';
+ if(*fn && (p = strrchr(fn, C_FILESEP))){
+ fname = p + 1;
+ dirlen = p - fn;
+ if(p == fn){
+ strncpy(bfn, S_FILESEP, sizeof(bfn));
+ bfn[sizeof(bfn)-1] = '\0';
+ }
+#ifdef DOS
+ else if(fn[0] == C_FILESEP
+ || (isalpha((unsigned char)fn[0])
+ && fn[1] == ':')){
+ if(fn[1] == ':' && p == fn+2)
+ dirlen = fname - fn;
+
+ if(dirlen < sizeof(bfn)){
+ strncpy(bfn, fn, dirlen);
+ bfn[dirlen] = '\0';
+ }
+ }
+#else
+ else if (fn[0] == C_FILESEP || fn[0] == '~'){
+ if(dirlen < sizeof(bfn)){
+ strncpy(bfn, fn, dirlen);
+ bfn[dirlen] = '\0';
+ }
+ }
+#endif
+ else
+ snprintf(bfn, sizeof(bfn), "%s%c%.*s",
+ (gmode & MDCURDIR)
+ ? "."
+ : ((gmode & MDTREE) || opertree[0])
+ ? opertree : gethomedir(NULL),
+ C_FILESEP, p - fn, fn);
+ }
+ else{
+ fname = fn;
+ strncpy(bfn, (gmode & MDCURDIR)
+ ? "."
+ : ((gmode & MDTREE) || opertree[0])
+ ? opertree : gethomedir(NULL), sizeof(bfn));
+ bfn[sizeof(bfn)-1] = '\0';
+ }
+
+ if(!pico_fncomplete(bfn, fname, sizeof(fn)-(fname-fn)))
+ (*term.t_beep)();
+ }
+ else
+ (*term.t_beep)();
+
+ break;
+
+ case (CTRL|'T'):
+ if(i != 2){
+ (*term.t_beep)();
+ break;
+ }
+
+ *bfn = '\0';
+ if(*fn == '\0' || !isdir(fn, NULL, NULL)){
+ strncpy(fn, (gmode & MDCURDIR)
+ ? (browse_dir[0] ? browse_dir : ".")
+ : ((gmode & MDTREE) || opertree[0])
+ ? opertree
+ : (browse_dir[0] ? browse_dir
+ : gethomedir(NULL)), sizeof(fn));
+ fn[sizeof(fn)-1] = '\0';
+ }
+
+ if((fbrv = FileBrowse(fn, sizeof(fn), bfn, sizeof(bfn), sz, sizeof(sz),
+ upload ? fb_flags
+ : fb_flags|FB_LMODEPOS,
+ upload ? NULL
+ : lm)) == 1){
+ if (upload && (strlen(fn)+strlen(S_FILESEP)+strlen(bfn)) < sizeof(fn)){
+ size_t len1, len2;
+
+ len1 = strlen(bfn);
+ len2 = strlen(fn);
+ if((new=(LMLIST *)malloc(sizeof(*new))) == NULL
+ || (new->fname=malloc((len1+1) * sizeof(char))) == NULL
+ || (new->dir=malloc((len2+1) * sizeof(char))) == NULL){
+ emlwrite("\007Can't malloc space for filename", NULL);
+ return(-1);
+ }
+
+ strncpy(new->fname, bfn, len1);
+ new->fname[len1] = '\0';
+ strncpy(new->dir, fn, len2);
+ new->dir[len2] = '\0';
+ strncpy(new->size, sz, sizeof(new->size)-1);
+ new->size[sizeof(new->size)-1] = '\0';
+ new->next = NULL;
+ *lm = new;
+
+ strncat(fn, S_FILESEP, sizeof(fn)-strlen(fn)-1);
+ fn[sizeof(fn)-1] = '\0';
+ strncat(fn, bfn, sizeof(fn)-strlen(fn)-1);
+ fn[sizeof(fn)-1] = '\0';
+ if(!AttachUpload(fn, sizeof(fn), sz, sizeof(sz))){
+ i = 2; /* keep prompting for file */
+ sleep(3); /* problem, show error! */
+ }
+ else{
+ i--; /* go prompt for comment */
+ }
+ }
+ else if(!upload){
+ if(lm && *lm && !(*lm)->next) /* get comment */
+ i--;
+ else{ /* no comments if multiple files */
+ update();
+ return(1);
+ }
+ }
+ else{ /* trouble */
+ *fn = '\0';
+ AttachCancel(fn);
+ pico_refresh(FALSE,1);
+ update();
+ emlwrite("\007File name too BIG, cannot select!", NULL);
+ sleep(3);
+ }
+ }
+ else if (!fbrv)
+ *fn = '\0';
+ else{
+ *fn = '\0';
+ AttachCancel(fn);
+ pico_refresh(FALSE, 1);
+ update();
+ emlwrite("\007File name too big, cannot select!", NULL);
+ sleep(3);
+ }
+
+ /* fall thru to clean up the screen */
+
+ case (CTRL|'L'):
+ pico_refresh(FALSE, 1);
+ update();
+ continue;
+
+#if !defined(DOS) && !defined(MAC)
+ case (CTRL|'Y'): /* upload? */
+ if(i == 2)
+ upload ^= 1; /* flip mode */
+ else
+ (*term.t_beep)();
+
+ break;
+#endif
+
+ case ABORT:
+ return(AttachCancel((upload && i == 1) ? fn : NULL));
+
+ case TRUE: /* some comment */
+ case FALSE: /* No comment */
+ if(i-- == 2){
+ if(upload){
+ fixpath(fn, sizeof(fn)); /* names relative to ~ */
+ status = AttachUpload(fn, sizeof(fn), sz, sizeof(sz));
+ pico_refresh(FALSE, 1);
+ update();
+ if(!status){
+ i = 2; /* keep prompting for file */
+ sleep(3); /* problem, show error! */
+ }
+ }
+ else {
+ if(*fn == '\"' && fn[strlen(fn)-1] == '\"'){
+ int j;
+
+ for(j = 0; (fn[j] = fn[j+1]); j++)
+ ;
+
+ fn[j-1] = '\0';
+ }
+
+ if(fn[0]){
+ if((gmode & MDTREE)
+ && !compresspath(opertree, fn, sizeof(fn))){
+ eml.s = (gmode&MDSCUR) ? _("home directory") : opertree;
+ emlwrite(
+ /* TRANSLATORS: the %s is replaced with the name of a directory */
+ _("Restricted mode allows attachments from %s only: too many ..'s"),
+ &eml);
+ return(0);
+ }
+ else{
+ fixpath(fn, sizeof(fn)); /* names relative to ~ */
+ if((gmode&MDTREE) && !in_oper_tree(fn)){
+ eml.s = (gmode&MDSCUR) ? _("home directory") : opertree;
+ emlwrite(
+ _("\007Restricted mode allows attachments from %s only"), &eml);
+ return(0);
+ }
+ }
+
+ if((status = fexist(fn, "r", &attsz)) != FIOSUC){
+ fioperr(status, fn); /* file DOESN'T exist! */
+ return(0);
+ }
+
+ len = strlen(fn);
+ if((new=(LMLIST *)malloc(sizeof(*new))) == NULL
+ || (new->fname=malloc((len+1)*sizeof(char))) == NULL){
+ emlwrite("\007Can't malloc space for filename", NULL);
+ return(-1);
+ }
+
+ new->dir = NULL;
+ strncpy(new->fname, fn, len);
+ new->fname[len] = '\0';
+ strncpy(new->size, prettysz(attsz), sizeof(new->size));
+ new->size[sizeof(new->size)-1] = '\0';
+ new->next = NULL;
+ *lm = new;
+ }
+ else
+ return(AttachCancel((upload && i == 1) ? fn : NULL));
+ }
+ }
+ else{
+ mlerase();
+ return(1); /* mission accomplished! */
+ }
+
+ break;
+ default:
+ break;
+ }
+ }
+
+ return(0);
+}
+
+
+/*
+ * AttachUpload - Use call back to run the external upload command.
+ */
+int
+AttachUpload(char *fn, size_t fnlen, char *sz, size_t szlen)
+{
+ long l;
+
+ if(gmode&MDSCUR){
+ emlwrite("\007Restricted mode disallows uploaded command", NULL);
+ return(0);
+ }
+
+ if(Pmaster && Pmaster->upload && (*Pmaster->upload)(fn, fnlen, &l)){
+ strncpy(sz, prettysz((off_t)l), szlen);
+ sz[szlen-1] = '\0';
+ return(1);
+ }
+
+ return(0);
+}
+
+
+/*
+ * AttachCancel -
+ */
+int
+AttachCancel(char *fn)
+{
+ emlwrite(_("Attach cancelled"), NULL);
+ if(fn && fn[0])
+ our_unlink(fn); /* blast uploaded file */
+
+ return(0);
+}
+
+
+extern struct headerentry *headents;
+
+/*
+ * SyncAttach - given a pointer to a linked list of attachment structures,
+ * return with that structure sync'd with what's displayed.
+ * delete any attachments in list of structs that's not on
+ * the display, and add any that aren't in list but on display.
+ */
+int
+SyncAttach(void)
+{
+ int offset = 0, /* the offset to begin */
+ rv = 0,
+ ki = 0, /* number of known attmnts */
+ bi = 0, /* build array index */
+ nbld = 0, /* size of build array */
+ na, /* old number of attachmnt */
+ status, i, n = 0;
+ size_t j;
+ char file[NLINE], /* buffers to hold it all */
+ size[32],
+ comment[1024];
+ struct hdr_line *lp; /* current line in header */
+ struct headerentry *entry;
+ PATMT *tp, **knwn = NULL, **bld;
+ EML eml;
+
+ if(Pmaster == NULL)
+ return(-1);
+
+ for(entry = headents; entry->name != NULL; entry++) {
+ if(entry->is_attach)
+ break;
+ }
+
+ for(tp = Pmaster->attachments; tp; tp = tp->next)
+ ki++; /* Count known attachments */
+
+ if(ki){
+ if((knwn = (PATMT **)malloc((ki+1) * (sizeof(PATMT *)))) == NULL){
+ eml.s = comatose(ki + 1);
+ emlwrite("\007Can't allocate space for %s known attachment array entries",
+ &eml);
+ rv = -1;
+ goto exit_early;
+ }
+ for(i=0, tp = Pmaster->attachments; i < ki; i++, tp = tp->next){
+ knwn[i] = tp; /* fill table of */
+ /* known attachments */
+ }
+ }
+
+
+ /*
+ * As a quick hack to avoid too many reallocs, check to see if
+ * there are more header lines than known attachments.
+ */
+ for(lp = entry->hd_text; lp ; lp = lp->next)
+ nbld++; /* count header lines */
+
+ nbld = nbld > ki ? nbld : ki + 1;
+
+ if((bld = (PATMT **)malloc(nbld * (sizeof(PATMT *)))) == NULL){
+ eml.s = comatose(nbld);
+ emlwrite("\007Can't allocate space for %s build array entries", &eml);
+ rv = -1;
+ goto exit_early;
+ }
+
+ lp = entry->hd_text;
+ while(lp != NULL){
+ char fn[NLINE];
+ na = ++n;
+
+ if(bi == nbld){ /* need to grow build array? */
+ if((bld = (PATMT **)realloc(bld, ++nbld * sizeof(PATMT *))) == NULL){
+ eml.s = comatose(nbld);
+ emlwrite("\007Can't resize build array to %s entries ", &eml);
+ rv = -1;
+ goto exit_early;
+ }
+ }
+
+ if((status = ParseAttach(&lp, &offset, file, sizeof(file), size, sizeof(size),
+ comment, sizeof(comment), &na)) != 0)
+ rv = (rv < 0) ? rv : status ; /* remember worst case */
+
+ if(*file == '\0'){
+ if(n != na && na > 0 && na <= ki && (knwn[na-1]->flags & A_FLIT)){
+ bld[bi++] = knwn[na-1];
+ knwn[na-1] = NULL;
+ }
+ continue;
+ }
+
+ if((gmode&MDTREE)
+ && file[0] != '['
+ && (!in_oper_tree(file)
+ || !compresspath(file, fn, sizeof(fn))))
+ /* no attachments outside ~ in secure mode! */
+ continue;
+
+ tp = NULL;
+ for(i = 0; i < ki; i++){ /* already know about it? */
+ /*
+ * this is kind of gruesome. what we want to do is keep track
+ * of literal attachment entries because they may not be
+ * actual files we can access or that the user can readily
+ * access.
+ */
+ if(knwn[i]
+ && ((!(knwn[i]->flags&A_FLIT)
+ && !strcmp(file, knwn[i]->filename))
+ || ((knwn[i]->flags&A_FLIT) && i+1 == na))){
+ tp = knwn[i];
+ knwn[i] = NULL; /* forget we know about it */
+
+ if(status == -1) /* ignore garbage! */
+ break;
+
+ if((tp->flags&A_FLIT) && strcmp(file, tp->filename)){
+ rv = 1;
+ if((j=strlen(file)) > strlen(tp->filename)){
+ if((tp->filename = (char *)realloc(tp->filename,
+ sizeof(char)*(j+1))) == NULL){
+ emlwrite("\007Can't realloc filename space",NULL);
+ rv = -1;
+ goto exit_early;
+ }
+ }
+
+ strncpy(tp->filename, file, j);
+ tp->filename[j] = '\0';
+ }
+ else if(tp->size && strcmp(tp->size, size)){
+ rv = 1;
+ if((j=strlen(size)) > strlen(tp->size)){
+ if((tp->size=(char *)realloc(tp->size,
+ sizeof(char)*(j+1))) == NULL){
+ emlwrite("\007Can't realloc space for size", NULL);
+ rv = -1;
+ goto exit_early;
+ }
+ }
+
+ strncpy(tp->size, size, j);
+ tp->size[j] = '\0';
+ }
+
+ if(strcmp(tp->description, comment)){ /* new comment */
+ rv = 1;
+ if((j=strlen(comment)) > strlen(tp->description)){
+ if((tp->description=(char *)realloc(tp->description,
+ sizeof(char)*(j+1))) == NULL){
+ emlwrite("\007Can't realloc description", NULL);
+ rv = -1;
+ goto exit_early;
+ }
+ }
+
+ strncpy(tp->description, comment, j);
+ tp->description[j] = '\0';
+ }
+ break;
+ }
+ }
+
+ if(tp){
+ bld[bi++] = tp;
+ }
+ else{
+ if(file[0] != '['){
+ if((tp = NewAttach(file, atol(size), comment)) == NULL){
+ rv = -1;
+ goto exit_early;
+ }
+ bld[bi++] = tp;
+ }
+ else break;
+ }
+
+ if(status < 0)
+ tp->flags |= A_ERR; /* turn ON error bit */
+ else
+ tp->flags &= ~(A_ERR); /* turn OFF error bit */
+ }
+
+ if(bi){
+ for(i=0; i < bi-1; i++) /* link together newly built list */
+ bld[i]->next = bld[i+1];
+
+ bld[i]->next = NULL; /* tie it off */
+ Pmaster->attachments = bld[0];
+ }
+ else
+ Pmaster->attachments = NULL;
+
+exit_early:
+ if(knwn){
+ for(i = 0; i < ki; i++){ /* kill old/unused references */
+ if(knwn[i]){
+ ZotAttach(knwn[i]);
+ free((char *) knwn[i]);
+ }
+ }
+ free((void *)knwn);
+ }
+
+ if(bld)
+ free((void *)bld);
+
+ return(rv);
+}
+
+
+/*
+ * ParseAttach - given a header line and an offset into it, return with
+ * the three given fields filled in. Size of fn and cmnt
+ * buffers should be passed in fnlen and cmntlen.
+ * Always updates header fields that have changed or are
+ * fixed. An error advances offset to next attachment.
+ *
+ * returns: 1 if a field changed
+ * 0 nothing changed
+ * -1 on error
+ */
+int
+ParseAttach(struct hdr_line **lp, /* current header line */
+ int *off, /* offset into that line */
+ char *fn, /* return file name field */
+ size_t fnlen, /* fn buffer size */
+ char *sz,
+ size_t szlen,
+ char *cmnt, /* places to return fields */
+ size_t cmntlen,
+ int *no) /* attachment number */
+{
+ int j, status, bod, eod = -1,
+ rv = 0, /* return value */
+ orig_offset,
+ lbln = 0, /* label'd attachment */
+ hibit = 0,
+ quoted = 0,
+ add_quotes = 0,
+ escaped = 0;
+ off_t attsz; /* attachment length */
+ EML eml;
+ UCS c, c_lookahead;
+ UCS tmp[1024], *p, *u, quotechar[2];
+ char ctmp[1024];
+ char *utf8 = NULL;
+ char *lblsz = NULL, /* label'd attchmnt's size */
+ number[8];
+ struct hdr_line *lprev = NULL;
+ enum { /* parse levels */
+ LWS, /* leading white space */
+ NUMB, /* attachment number */
+ WSN, /* white space after number */
+ TAG, /* attachments tag (fname) */
+ WST, /* white space after tag */
+ ASIZE, /* attachments size */
+ SWS, /* white space after size */
+ CMMNT, /* attachment comment */
+ TG} level; /* trailing garbage */
+
+ *fn = *sz = *cmnt = '\0'; /* initialize return strings */
+ p = tmp;
+ orig_offset = bod = *off;
+ quotechar[0] = '\"';
+ quotechar[1] = '\0';
+
+ level = LWS; /* start at beginning */
+ while(*lp != NULL){
+
+ if((c=(*lp)->text[*off]) == '\0'){ /* end of display line */
+ if(level == LWS && bod != *off){
+ (*lp)->text[bod] = '\0';
+ rv = 1;
+ }
+ lprev = *lp;
+ if((*lp = (*lp)->next) != NULL)
+ c = (*lp)->text[*off = bod = 0]; /* reset offset */
+ }
+
+ if(c != '\0'){
+ c_lookahead = (*lp)->text[*off + 1];
+ if(c_lookahead == '\0'){ /* end of display line */
+ if((*lp)->next != NULL)
+ c_lookahead = (*lp)->next->text[0];
+ }
+ }
+
+ switch(level){
+ case LWS: /* skip leading white space */
+ if(c <= 0xff && (isspace((unsigned char)c) || c == ',')){
+ c = ' ';
+ break;
+ }
+
+ if(c == '\0'){
+ if(bod > 0 && *off >= bod && lprev){
+ lprev->text[bod - 1] = '\0';
+ rv = 1;
+ }
+ }
+ else if(*off > bod && *lp){ /* wipe out whitespace */
+ memcpy(&(*lp)->text[bod], &(*lp)->text[*off],
+ ucs4_strlen(&(*lp)->text[*off]) + 1);
+ *off = bod; /* reset pointer */
+ rv = 1;
+ }
+
+ if(c == '\0')
+ break;
+
+ if(c > 0xff || !isdigit((unsigned char)c)){ /* add a number */
+ snprintf(number, sizeof(number), "%d. ", *no);
+ *no = 0; /* no previous number! */
+ u = utf8_to_ucs4_cpystr(number);
+ if(u){
+ sinserts((*lp == NULL) ? &lprev->text[*off]
+ : &(*lp)->text[*off],
+ 0, u, j=ucs4_strlen(u));
+
+ fs_give((void **) &u);
+ }
+
+ *off += j - 1;
+ rv = 1;
+ level = TAG; /* interpret the name */
+ break;
+ }
+ level = NUMB;
+ case NUMB: /* attachment number */
+ if(c == '\0' || c == ','){ /* got to end, no number yet */
+ *p = '\0';
+ snprintf(number, sizeof(number), "%d. ", *no);
+ *no = 0; /* no previous number! */
+ if(c == '\0')
+ *lp = lprev; /* go back and look at prev */
+
+ c = (*lp)->text[*off = orig_offset]; /* reset offset */
+ u = utf8_to_ucs4_cpystr(number);
+ if(u){
+ sinserts((*lp == NULL) ? &lprev->text[*off]
+ : &(*lp)->text[*off],
+ 0, u, j=ucs4_strlen(u));
+
+ fs_give((void **) &u);
+ }
+ *off += j - 1;
+ rv = 1;
+ p = tmp;
+ level = WSN; /* what's next... */
+ break;
+ }
+ else if(c == '.' && c_lookahead <= 0xff && isspace((unsigned char)c_lookahead)){
+ /* finished grabbing number */
+ /* if not space is not number */
+ /*
+ * replace number if it's not right
+ */
+ *p = '\0';
+ snprintf(number, sizeof(number), "%d", *no); /* record the current... */
+ utf8 = ucs4_to_utf8_cpystr(tmp);
+ *no = atoi(utf8); /* and the old place in list */
+ if(strcmp(number, utf8)){
+ if(p-tmp > *off){ /* where to begin replacemnt */
+ UCS uu[1];
+
+ uu[0] = '\0';
+ j = (p-tmp) - *off;
+ sinserts((*lp)->text, *off, uu, 0);
+ u = utf8_to_ucs4_cpystr(number);
+ if(u){
+ sinserts(&lprev->text[ucs4_strlen(lprev->text)-j], j,
+ u, ucs4_strlen(u));
+
+ fs_give((void **) &u);
+ }
+
+ *off = 0;
+ }
+ else{
+ j = (*off) - (p-tmp);
+ u = utf8_to_ucs4_cpystr(number);
+ if(u){
+ sinserts((*lp == NULL) ? &lprev->text[j]
+ : &(*lp)->text[j],
+ p-tmp, u, ucs4_strlen(u));
+
+ fs_give((void **) &u);
+ }
+
+ *off += strlen(number) - (p-tmp);
+ }
+ rv = 1;
+ }
+
+ if(utf8)
+ fs_give((void **) &utf8);
+
+ p = tmp;
+ level = WSN; /* what's next... */
+ }
+ else if(c < '0' || c > '9'){ /* Must be part of tag */
+ snprintf(number, sizeof(number), "%d. ", *no);
+ u = utf8_to_ucs4_cpystr(number);
+ if(u){
+ sinserts((*lp == NULL) ? &lprev->text[*off - (p - tmp)]
+ : &(*lp)->text[*off - (p - tmp)],
+ 0, u, j=ucs4_strlen(u));
+
+ fs_give((void **) &u);
+ }
+
+ *off += j;
+ level = TAG; /* interpret the name */
+ goto process_tag; /* in case already past end of tag */
+ }
+ else
+ *p++ = c;
+
+ break;
+
+ case WSN: /* blast whitespace */
+ if(c <= 0xff && (isspace((unsigned char)c) || c == '\0')){
+ break;
+ }
+ else if(c == '['){ /* labeled attachment */
+ lbln++;
+ }
+ else if(c == ',' || c == ' '){
+ /* TRANSLATORS: Attchmnt is an abbreviation for Attachment and
+ the %s is replaced with the character that is not
+ allowed in the name. */
+ eml.s = (c == ',') ? "," : "space";
+ emlwrite(_("\007Attchmnt: '%s' not allowed in file name"), &eml);
+ rv = -1;
+ level = TG; /* eat rest of garbage */
+ break;
+ }
+ level = TAG;
+
+ case TAG: /* get and check filename */
+ /* or labeled attachment */
+process_tag: /* enclosed in [] */
+ if(c == '\0'
+ || (lbln && c == ']')
+ || (quoted && p != tmp && c == '\"')
+ || (!(lbln || quoted)
+ && (c <= 0xff && ((isspace((unsigned char) c) && c_lookahead == (UCS) '(') || strchr(",(\"", c))))){
+ if(p == tmp){
+ if(c == '\"')
+ quoted++;
+ }
+ else{
+ *p = '\0'; /* got something */
+
+ utf8 = ucs4_to_utf8_cpystr(tmp);
+ if(utf8){
+ if(strlen(utf8) > fnlen)
+ emlwrite("File name too big!",NULL);
+
+ strncpy(fn, utf8, fnlen); /* store file name */
+ fn[fnlen-1] = '\0';
+ fs_give((void **) &utf8);
+ }
+
+ if(!lbln){ /* normal file attachment */
+ if((gmode & MDTREE)
+ && !compresspath(opertree, fn, fnlen)){
+ eml.s = (gmode&MDSCUR) ? _("home directory") : opertree;
+ emlwrite(
+ _("Attachments allowed only from %s: too many ..'s"),
+ &eml);
+ rv = -1;
+ level = TG;
+ break;
+ }
+ else{
+ fixpath(fn, fnlen);
+ if((status=fexist(fn, "r", &attsz)) != FIOSUC){
+ fioperr(status, fn);
+ rv = -1;
+ level = TG; /* munch rest of garbage */
+ break;
+ }
+
+ if((gmode & MDTREE) && !in_oper_tree(fn)){
+ eml.s = (gmode&MDSCUR) ? _("home directory") : opertree;
+ emlwrite(_("\007Attachments allowed only from %s"), &eml);
+ rv = -1;
+ level = TG;
+ break;
+ }
+ }
+
+ utf8 = ucs4_to_utf8_cpystr(tmp);
+
+ if(utf8 && strcmp(fn, utf8)){ /* fn changed: display it */
+ if(*off >= p - tmp){ /* room for it? */
+ u = utf8_to_ucs4_cpystr(fn);
+ if(u){
+ /*
+ * This whole parsing of the attachment line
+ * thing is ad hoc and susceptible to problems,
+ * and this particular part is no exception.
+ * Quote the filename if it contains spaces.
+ */
+ if(add_quotes){
+ sinserts((*lp == NULL) ? &lprev->text[*off - (p-tmp)]
+ : &(*lp)->text[*off - (p-tmp)],
+ 0, quotechar, 1);
+ (*off)++;
+ }
+
+ sinserts((*lp == NULL) ? &lprev->text[*off - (p-tmp)]
+ : &(*lp)->text[*off - (p-tmp)],
+ p-tmp, u, j=ucs4_strlen(u));
+
+ *off += j - (p - tmp); /* advance offset */
+
+ if(add_quotes){
+ sinserts((*lp == NULL) ? &lprev->text[*off]
+ : &(*lp)->text[*off],
+ 0, quotechar, 1);
+ (*off)++;
+ add_quotes = 0;
+ }
+
+ fs_give((void **) &u);
+ }
+
+ rv = 1;
+ }
+ else{
+ emlwrite("\007Attchmnt: Problem displaying real file path", NULL);
+ }
+ }
+
+ if(utf8)
+ fs_give((void **) &utf8);
+ }
+ else{ /* labelled attachment! */
+ /*
+ * should explain about labelled attachments:
+ * these are attachments that came into the composer
+ * with meaningless file names (up to caller of
+ * composer to decide), for example, attachments
+ * being forwarded from another message. here, we
+ * just make sure the size stays what was passed
+ * to us. The user is SOL if they change the label
+ * since, as it is now, after changed, it will
+ * just get dropped from the list of what gets
+ * passed back to the caller.
+ */
+ PATMT *tp;
+
+ if(c != ']'){ /* legit label? */
+ eml.s = fn;
+ emlwrite(_("\007Attchmnt: Expected ']' after \"%s\""),
+ &eml);
+ rv = -1;
+ level = TG;
+ break;
+ }
+
+ strncat(fn, "]", fnlen-strlen(fn)-1);
+ fn[fnlen-1] = '\0';
+
+ /*
+ * This is kind of cheating since otherwise
+ * ParseAttach doesn't know about the attachment
+ * struct. OK if filename's not found as it will
+ * get taken care of later...
+ */
+ tp = Pmaster->attachments; /* caller check Pmaster! */
+ j = 0;
+ while(tp != NULL){
+ if(++j == *no){
+ lblsz = tp->size;
+ break;
+ }
+
+ tp = tp->next;
+ }
+
+ if(tp == NULL){
+ eml.s = fn;
+ emlwrite("\007Attchmnt: Unknown reference: %s", &eml);
+ lblsz = "XXX";
+ }
+ }
+
+ if(add_quotes){
+ sinserts((*lp == NULL) ? &lprev->text[*off - (p-tmp)]
+ : &(*lp)->text[*off - (p-tmp)],
+ 0, quotechar, 1);
+ (*off)++;
+ sinserts((*lp == NULL) ? &lprev->text[*off]
+ : &(*lp)->text[*off],
+ 0, quotechar, 1);
+ (*off)++;
+ add_quotes = 0;
+ }
+
+ p = tmp; /* reset p in tmp */
+ level = WST;
+ }
+
+ if(!lbln && c == '(') /* no space 'tween file, size*/
+ level = ASIZE;
+ else if(c == '\0'
+ || (!(lbln || quoted) && (c == ',' || c == '\"'))){
+ strncpy(sz, (lblsz) ? lblsz : prettysz(attsz), szlen);
+ sz[szlen-1] = '\0';
+
+ snprintf(ctmp, sizeof(ctmp), " (%s) %s", sz, (c == '\"') ? "" : "\"\"");
+ u = utf8_to_ucs4_cpystr(ctmp);
+ if(u){
+ ucs4_strncpy(tmp, u, sizeof(tmp)/sizeof(tmp[0]));
+ tmp[sizeof(tmp)/sizeof(tmp[0]) - 1] = '\0';
+ fs_give((void **) &u);
+ }
+
+ sinserts((*lp == NULL) ? &lprev->text[*off]
+ : &(*lp)->text[*off],
+ 0, tmp, j=ucs4_strlen(tmp));
+ *off += j;
+ rv = 1;
+ level = (c == '\"') ? CMMNT : TG;/* cmnt or eat trash */
+ }
+ }
+ else if(!(lbln || quoted)
+ && (c == ',' || /** c == ' ' || **/ c == '[' || c == ']')){
+ eml.s = c == ',' ? ","
+ : c == ' ' ? "space"
+ : c == '[' ? "[" : "]";
+ emlwrite(_("\007Attchmnt: '%s' not allowed in file name"), &eml);
+ rv = -1; /* bad char in file name */
+ level = TG; /* gobble garbage */
+ }
+ else if(!(lbln || quoted) && (c <= 0xff && isspace((unsigned char) c))){
+ add_quotes++;
+ *p++ = c; /* add char to name */
+ }
+ else
+ *p++ = c; /* add char to name */
+
+ break;
+
+ case WST: /* skip white space */
+ if(c > 0xff || !isspace((unsigned char)c)){
+ /*
+ * whole attachment, comment or done!
+ */
+ if(c == ',' || c == '\0' || c == '\"'){
+ strncpy(sz, (lblsz) ? lblsz : prettysz(attsz), sizeof(sz));
+ sz[sizeof(sz)-1] = '\0';
+
+ snprintf(ctmp, sizeof(ctmp), " (%s) %s", sz, (c == '\"') ? "" : "\"\"");
+ u = utf8_to_ucs4_cpystr(ctmp);
+ if(u){
+ ucs4_strncpy(tmp, u, sizeof(tmp)/sizeof(tmp[0]));
+ tmp[sizeof(tmp)/sizeof(tmp[0]) - 1] = '\0';
+ fs_give((void **) &u);
+ }
+
+ sinserts((*lp == NULL) ? &lprev->text[*off]
+ : &(*lp)->text[*off],
+ 0, tmp, j=ucs4_strlen(tmp));
+ *off += j;
+ rv = 1;
+ level = (c == '\"') ? CMMNT : TG;
+ lbln = 0; /* reset flag */
+ }
+ else if(c == '('){ /* get the size */
+ level = ASIZE;
+ }
+ else{
+ eml.s = fn;
+ emlwrite(_("\007Attchmnt: Expected '(' or '\"' after %s"), &eml);
+ rv = -1; /* bag it all */
+ level = TG;
+ }
+ }
+ break;
+
+ case ASIZE: /* check size */
+ if(c == ')'){ /* finished grabbing size */
+ *p = '\0';
+ /*
+ * replace sizes if they don't match!
+ */
+ utf8 = ucs4_to_utf8_cpystr(tmp);
+ if(utf8){
+ strncpy(sz, utf8, szlen);
+ sz[szlen-1] = '\0';
+ fs_give((void **) &utf8);
+ }
+
+ if(strcmp(sz, (lblsz) ? lblsz : prettysz(attsz))){
+ strncpy(sz, (lblsz) ? lblsz : prettysz(attsz), szlen);
+ sz[szlen-1] = '\0';
+ if(p-tmp > *off){ /* where to begin replacemnt */
+ UCS uu[1];
+
+ uu[0] = '\0';
+ j = (p-tmp) - *off;
+ sinserts((*lp)->text, *off, uu, 0);
+ u = utf8_to_ucs4_cpystr(sz);
+ if(u){
+ sinserts(&lprev->text[ucs4_strlen(lprev->text)-j], j,
+ u, ucs4_strlen(u));
+ fs_give((void **) &u);
+ }
+
+ *off = 0;
+ }
+ else{
+ j = (*off) - (p-tmp);
+ u = utf8_to_ucs4_cpystr(sz);
+ if(u){
+ sinserts((*lp == NULL) ? &lprev->text[j]
+ : &(*lp)->text[j],
+ p-tmp , u, ucs4_strlen(u));
+ *off += ucs4_strlen(u) - (p-tmp);
+ fs_give((void **) &u);
+ }
+ }
+ rv = 1;
+ }
+
+ p = tmp;
+ level = SWS; /* what's next... */
+ }
+ else if(c == '\0' || c == ','){
+ *p = '\0';
+ utf8 = ucs4_to_utf8_cpystr(tmp);
+ eml.s = utf8;
+ emlwrite(_("\007Attchmnt: Size field missing ')': \"%s\""), &eml);
+ if(utf8)
+ fs_give((void **) &utf8);
+
+ rv = -1;
+ level = TG;
+ }
+ else
+ *p++ = c;
+
+ break;
+
+ case SWS: /* skip white space */
+ if(c > 0xff || !isspace((unsigned char)c)){
+ if(c == ','){ /* no description */
+ level = TG; /* munch rest of garbage */
+ lbln = 0; /* reset flag */
+ }
+ else if(c != '\"' && c != '\0'){
+ emlwrite(_("\007Attchmnt: Malformed comment, quotes required"), NULL);
+ rv = -1;
+ level = TG;
+ }
+ else
+ level = CMMNT;
+ }
+ break;
+
+ case CMMNT: /* slurp up comment */
+ if((c == '\"' && !escaped) || c == '\0'){
+ *p = '\0'; /* cap it off */
+ p = tmp; /* reset p */
+ utf8 = ucs4_to_utf8_cpystr(tmp);
+ if(utf8){
+ if(strlen(utf8) > cmntlen)
+ emlwrite("Comment too long!",NULL);
+
+ strncpy(cmnt,utf8,cmntlen-1); /* copy the comment */
+ cmnt[cmntlen-1] = '\0';
+ fs_give((void **) &utf8);
+ if(c == '\0'){
+ emlwrite(_("\007Attchmnt: Closing quote required at end of comment"), NULL);
+ rv = -1;
+ }
+ }
+
+ level = TG; /* prepare for next one */
+ lbln = 0; /* reset flag */
+ }
+ else if(c == '\\' && !escaped){ /* something escaped? */
+ escaped = 1;
+ }
+ else{
+ if(escaped){
+ if(c != '\"') /* we only quote escapes */
+ *p++ = '\\';
+
+ escaped = 0;
+ }
+
+ if(((*p++ = c) & 0x80) && (gmode & MDHBTIGN) && !hibit++)
+ emlwrite(HIBIT_WARN, NULL);
+ }
+
+ break;
+
+ case TG: /* get comma or final EOL */
+ if(eod < 0)
+ eod = *off;
+ if(c > 0xff || !isspace((unsigned char)c)){
+ switch(c){
+ case '\0':
+ if(eod != *off)
+ lprev->text[*off = eod] = '\0';
+ break;
+ case ',':
+ if(eod != *off){
+ memcpy(&(*lp)->text[eod], &(*lp)->text[*off],
+ ucs4_strlen(&(*lp)->text[*off]) + 1);
+ *off = eod;
+ rv = 1;
+ }
+ break;
+ default:
+ if(rv != -1)
+ emlwrite(_("\007Attchmnt: Comma must separate attachments"), NULL);
+ rv = -1;
+ }
+ }
+ break;
+
+ default: /* something's very wrong */
+ emlwrite("\007Attchmnt: Weirdness in ParseAttach", NULL);
+ return(-1); /* just give up */
+ }
+
+ if(c == '\0') /* we're done */
+ break;
+
+ (*off)++;
+
+ /*
+ * not in comment or label name? done.
+ */
+ if(c == ',' && (level != TAG && level != CMMNT && !lbln))
+ break; /* put offset past ',' */
+ }
+
+ return(rv);
+}
+
+
+/*
+ * NewAttach - given a filename (assumed to accessible) and comment, creat
+ */
+PATMT *
+NewAttach(char *f, long l, char *c)
+{
+ PATMT *tp;
+ size_t len;
+
+ if((tp=(PATMT *)malloc(sizeof(PATMT))) == NULL){
+ emlwrite("No memory to add attachment", NULL);
+ return(NULL);
+ }
+ else
+ memset(tp, 0, sizeof(PATMT));
+
+ /* file and size malloc */
+ len = strlen(f);
+ if((tp->filename = (char *) malloc((len+1) * sizeof(char))) == NULL){
+ emlwrite("Can't malloc name for attachment", NULL);
+ free((char *) tp);
+ return(NULL);
+ }
+
+ strncpy(tp->filename, f, len);
+ tp->filename[len] = '\0';
+
+ if(l > -1){
+ len = strlen(prettysz((off_t) l));
+ tp->size = (char *) malloc((len+1) * sizeof(char));
+ if(tp->size == NULL){
+ emlwrite("Can't malloc size for attachment", NULL);
+ free((char *) tp->filename);
+ free((char *) tp);
+ return(NULL);
+ }
+ else{
+ strncpy(tp->size, prettysz((off_t) l), len);
+ tp->size[len] = '\0';
+ }
+ }
+
+ /* description malloc */
+ len = strlen(c);
+ if((tp->description = (char *) malloc((len+1) * sizeof(char))) == NULL){
+ emlwrite("Can't malloc description for attachment", NULL);
+ free((char *) tp->size);
+ free((char *) tp->filename);
+ free((char *) tp);
+ return(NULL);
+ }
+
+ strncpy(tp->description, c, len);
+ tp->description[len] = '\0';
+
+ /* callback to show user the mime type that will be used for attachment */
+ if(Pmaster->mimetype && (*Pmaster->mimetype)(f) > 0){
+ int rv ;
+
+ clearcursor();
+ mlerase();
+ rv = (*Pmaster->showmsg)('x');
+ ttresize();
+ picosigs();
+ if(rv) /* Did showmsg corrupt the screen? */
+ PaintBody(0); /* Yes, repaint it */
+
+ mpresf = 1;
+ }
+
+ return(tp);
+}
+
+
+/*
+ * AttachError - Sniff list of attachments, returning TRUE if there's
+ * any sign of trouble...
+ */
+int
+AttachError(void)
+{
+ PATMT *ap;
+
+ if(!Pmaster)
+ return(0);
+
+ ap = Pmaster->attachments;
+ while(ap){
+ if((ap->flags) & A_ERR)
+ return(1);
+
+ ap = ap->next;
+ }
+
+ return(FALSE);
+}
+
+
+char *
+QuoteAttach(char *fn, size_t fnlen)
+{
+ char *p;
+
+ if(*fn && strpbrk(fn, " \t,(\"")){ /* Quote it? */
+ p = &fn[strlen(fn)];
+ if(p+2-fn < fnlen){
+ *(p+2) = '\0';
+ *(p+1) = '\"';
+
+ do
+ *p = *(p-1);
+ while(--p != fn);
+ *p = '\"';
+ }
+ }
+
+ return(fn);
+}
+
+
+void
+ZotAttach(PATMT *p)
+{
+ if(!p)
+ return;
+
+ if(p->description)
+ free((char *)p->description);
+
+ if(p->filename){
+ if(p->flags & A_TMP)
+ our_unlink(p->filename);
+
+ free((char *)p->filename);
+ }
+
+ if(p->size)
+ free((char *)p->size);
+
+ if(p->id)
+ free((char *)p->id);
+
+ p->next = NULL;
+}
+#endif /* ATTACHMENTS */
+
+
+/*
+ * intag - return TRUE if i is in a column that makes up an
+ * attachment line number
+ */
+int
+intag(UCS *s, int i)
+{
+ UCS *p = s;
+ int n = 0;
+
+ while(*p != '\0' && (p-s) < 5){ /* is there a tag? it */
+ if(n && *p == '.') /* can't be more than 4 */
+ return(i <= p-s); /* chars long! */
+
+ if(*p < '0' || *p > '9')
+ break;
+ else
+ n = (n * 10) + (*p - '0');
+
+ p++;
+ }
+
+ return(FALSE);
+}
+
+
+/*
+ * prettysz - return pointer to string containing nice size description
+ */
+char *
+prettysz(off_t l)
+{
+ static char b[32];
+ long sz, left, right;
+
+ sz = (long) l;
+ b[0] = '\0';
+
+ if(sz < 1000L){
+ snprintf(b, sizeof(b), "%ld B", sz); /* xxx B */
+ }
+ else if(sz < 9950L){
+ left = (sz + 50L) / 1000L;
+ right = ((sz + 50L) - left * 1000L) / 100L;
+ snprintf(b, sizeof(b), "%ld.%ld KB", left, right); /* x.x KB */
+ }
+ else if(sz < 999500L){
+ snprintf(b, sizeof(b), "%ld KB", (sz + 500L) / 1000L); /* xxx KB */
+ }
+ else if(sz < 9950000L){
+ left = (sz + 50000L) / 1000000L;
+ right = ((sz + 50000L) - left * 1000000L) / 100000L;
+ snprintf(b, sizeof(b), "%ld.%ld MB", left, right); /* x.x MB */
+ }
+ else{
+ snprintf(b, sizeof(b), "%ld MB", (sz + 500000L) / 1000000L); /* xxx MB */
+ }
+
+ return(b);
+}
+
+
+/*
+ * sinserts - s insert into another string
+ */
+void
+sinserts(UCS *ds, /* dest string */
+ int dl, /* where to begin insert */
+ UCS *ss, /* source string */
+ int sl) /* length of ss */
+{
+ UCS *dp, *edp; /* pointers into dest. */
+ size_t j; /* jump difference */
+
+ if(sl >= dl){ /* source bigger than dest. */
+ dp = ds + dl; /* shift dest. to make room */
+ if((edp = ucs4_strchr(dp, '\0')) != NULL){
+ j = sl - dl;
+
+ for( ;edp >= dp; edp--)
+ edp[j] = *edp;
+
+ while(sl--)
+ *ds++ = *ss++;
+ }
+ else
+ emlwrite("\007No end of line???", NULL); /* can this happen? */
+ }
+ else{ /* dest is longer, shrink it */
+ j = dl - sl; /* difference in lengths */
+
+ while(sl--) /* copy u onto ds */
+ *ds++ = *ss++;
+
+ if(ucs4_strlen(ds) > j){ /* shuffle the rest left */
+ do
+ *ds = ds[j];
+ while(*ds++ != '\0');
+ }
+ else
+ *ds = '\0';
+ }
+}
diff --git a/pico/basic.c b/pico/basic.c
new file mode 100644
index 00000000..49b04bd6
--- /dev/null
+++ b/pico/basic.c
@@ -0,0 +1,955 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: basic.c 831 2007-11-27 01:04:19Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ *
+ * Program: Cursor manipulation functions
+ */
+
+/*
+ * The routines in this file move the cursor around on the screen. They
+ * compute a new value for the cursor, then adjust ".". The display code
+ * always updates the cursor location, so only moves between lines, or
+ * functions that adjust the top line in the window and invalidate the
+ * framing, are hard.
+ */
+#include "headers.h"
+
+#include "osdep/terminal.h"
+
+
+/*
+ * Move the cursor to the
+ * beginning of the current line.
+ * Trivial.
+ */
+int
+gotobol(int f, int n)
+{
+ curwp->w_doto = 0;
+ return (TRUE);
+}
+
+/*
+ * Move the cursor backwards by "n" characters. If "n" is less than zero call
+ * "forwchar" to actually do the move. Otherwise compute the new cursor
+ * location. Error if you try and move out of the buffer. Set the flag if the
+ * line pointer for dot changes.
+ */
+int
+backchar(int f, int n)
+{
+ register LINE *lp;
+
+ if (n < 0)
+ return (forwchar(f, -n));
+
+ while (n--) {
+ if (curwp->w_doto == 0) {
+ if ((lp=lback(curwp->w_dotp)) == curbp->b_linep){
+ if(Pmaster && Pmaster->headents)
+ /*
+ * go up into editing the mail header if on
+ * the top line and the user hits the left arrow!!!
+ *
+ * if the editor returns anything except -1, the
+ * user requested something special, so let
+ * pico know...
+ */
+ return(HeaderEditor(2, 1));
+ else
+ return (FALSE);
+ }
+
+ curwp->w_dotp = lp;
+ curwp->w_doto = llength(lp);
+ curwp->w_flag |= WFMOVE;
+ } else
+ curwp->w_doto--;
+ }
+
+ return (TRUE);
+}
+
+/*
+ * Move the cursor backwards by "n" characters. If "n" is less than zero call
+ * "forwchar" to actually do the move. Otherwise compute the new cursor
+ * location. Error if you try and move out of the buffer. Set the flag if the
+ * line pointer for dot changes.
+ *
+ * This routine does _not_ do the header editor checks. It is used by
+ * backword() in pico\word.c which gets stuck in a loop trying to go
+ * back if you've jumped into a header.
+ */
+int
+backchar_no_header_editor(int f, int n)
+{
+ register LINE *lp;
+
+ if (n < 0)
+ return (forwchar(f, -n));
+
+ while (n--) {
+ if (curwp->w_doto == 0) {
+ if ((lp=lback(curwp->w_dotp)) == curbp->b_linep){
+ return (FALSE);
+ }
+
+ curwp->w_dotp = lp;
+ curwp->w_doto = llength(lp);
+ curwp->w_flag |= WFMOVE;
+ } else
+ curwp->w_doto--;
+ }
+
+ return (TRUE);
+}
+
+/*
+ * Move the cursor to the end of the current line. Trivial. No errors.
+ */
+int
+gotoeol(int f, int n)
+{
+ curwp->w_doto = llength(curwp->w_dotp);
+ return (TRUE);
+}
+
+
+/*
+ * Move the cursor forwwards by "n" characters. If "n" is less than zero call
+ * "backchar" to actually do the move. Otherwise compute the new cursor
+ * location, and move ".". Error if you try and move off the end of the
+ * buffer. Set the flag if the line pointer for dot changes.
+ */
+int
+forwchar(int f, int n)
+{
+ if (n < 0)
+ return (backchar(f, -n));
+
+ while (n--) {
+ if (curwp->w_doto == llength(curwp->w_dotp)) {
+ if (curwp->w_dotp == curbp->b_linep)
+ return (FALSE);
+
+ curwp->w_dotp = lforw(curwp->w_dotp);
+ curwp->w_doto = 0;
+ curwp->w_flag |= WFMOVE;
+ }
+ else
+ curwp->w_doto++;
+ }
+
+ return (TRUE);
+}
+
+
+/*
+ * move to a particular line.
+ * argument (n) must be a positive integer for
+ * this to actually do anything
+ */
+int
+gotoline(int f, int n)
+{
+ if (n < 1) /* if a bogus argument...then leave */
+ return(FALSE);
+
+ /* first, we go to the start of the buffer */
+ curwp->w_dotp = lforw(curbp->b_linep);
+ curwp->w_doto = 0;
+ return(forwline(f, n-1));
+}
+
+
+/*
+ * Goto the beginning of the buffer. Massive adjustment of dot. This is
+ * considered to be hard motion; it really isn't if the original value of dot
+ * is the same as the new value of dot. Normally bound to "M-<".
+ */
+int
+gotobob(int f, int n)
+{
+ curwp->w_dotp = lforw(curbp->b_linep);
+ curwp->w_doto = 0;
+ curwp->w_flag |= WFHARD;
+ return (TRUE);
+}
+
+
+/*
+ * Move to the end of the buffer. Dot is always put at the end of the file
+ * (ZJ). The standard screen code does most of the hard parts of update.
+ * Bound to "M->".
+ */
+int
+gotoeob(int f, int n)
+{
+ curwp->w_dotp = curbp->b_linep;
+ curwp->w_doto = 0;
+ curwp->w_flag |= WFHARD;
+ return (TRUE);
+}
+
+
+/*
+ * Move forward by full lines. If the number of lines to move is less than
+ * zero, call the backward line function to actually do it. The last command
+ * controls how the goal column is set. Bound to "C-N". No errors are
+ * possible.
+ */
+int
+forwline(int f, int n)
+{
+ register LINE *dlp;
+
+ if (n < 0)
+ return (backline(f, -n));
+
+ if ((lastflag&CFCPCN) == 0) /* Reset goal if last */
+ curgoal = getccol(FALSE); /* not C-P or C-N */
+
+ thisflag |= CFCPCN;
+ dlp = curwp->w_dotp;
+ while (n-- && dlp!=curbp->b_linep)
+ dlp = lforw(dlp);
+
+ curwp->w_dotp = dlp;
+ curwp->w_doto = getgoal(dlp);
+ curwp->w_flag |= WFMOVE;
+ return (TRUE);
+}
+
+
+/*
+ * This function is like "forwline", but goes backwards. The scheme is exactly
+ * the same. Check for arguments that are less than zero and call your
+ * alternate. Figure out the new line and call "movedot" to perform the
+ * motion. No errors are possible. Bound to "C-P".
+ */
+int
+backline(int f, int n)
+{
+ register LINE *dlp;
+
+ if (n < 0)
+ return (forwline(f, -n));
+
+ if(Pmaster && Pmaster->headents){
+ /*
+ * go up into editing the mail header if on the top line
+ * and the user hits the up arrow!!!
+ */
+ if (lback(curwp->w_dotp) == curbp->b_linep)
+ /*
+ * if the editor returns anything except -1 then the user
+ * has requested something special, so let pico know...
+ */
+ return(HeaderEditor(1, 1));
+ }
+
+ if ((lastflag&CFCPCN) == 0) /* Reset goal if the */
+ curgoal = getccol(FALSE); /* last isn't C-P, C-N */
+
+ thisflag |= CFCPCN;
+ dlp = curwp->w_dotp;
+ while (n-- && lback(dlp)!=curbp->b_linep)
+ dlp = lback(dlp);
+
+ curwp->w_dotp = dlp;
+ curwp->w_doto = getgoal(dlp);
+ curwp->w_flag |= WFMOVE;
+ return (TRUE);
+}
+
+
+/*
+ * go back to the begining of the current paragraph
+ * here we look for a <NL><NL> or <NL><TAB> or <NL><SPACE>
+ * combination to delimit the begining of a paragraph
+ */
+int
+gotobop(int f, int n)
+{
+ int quoted, qlen;
+ UCS qstr[NLINE], qstr2[NLINE];
+
+ if (n < 0) /* the other way...*/
+ return(gotoeop(f, -n));
+
+ while (n-- > 0) { /* for each one asked for */
+
+ while(lisblank(curwp->w_dotp)
+ && lback(curwp->w_dotp) != curbp->b_linep){
+ curwp->w_dotp = lback(curwp->w_dotp);
+ curwp->w_doto = 0;
+ }
+
+ /* scan line by line until we come to a line ending with
+ * a <NL><NL> or <NL><TAB> or <NL><SPACE>
+ *
+ * PLUS: if there's a quote string, a quoted-to-non-quoted
+ * line transition.
+ */
+ quoted = glo_quote_str ? quote_match(glo_quote_str, curwp->w_dotp, qstr, NLINE) : 0;
+ qlen = quoted ? ucs4_strlen(qstr) : 0;
+ while(lback(curwp->w_dotp) != curbp->b_linep
+ && llength(lback(curwp->w_dotp)) > qlen
+ && (glo_quote_str
+ ? (quoted == quote_match(glo_quote_str,
+ lback(curwp->w_dotp),
+ qstr2, NLINE)
+ && !ucs4_strcmp(qstr, qstr2))
+ : 1)
+ && lgetc(curwp->w_dotp, qlen).c != TAB
+ && lgetc(curwp->w_dotp, qlen).c != ' ')
+ curwp->w_dotp = lback(curwp->w_dotp);
+
+ if(n){
+ /* keep looking */
+ if(lback(curwp->w_dotp) == curbp->b_linep)
+ break;
+ else
+ curwp->w_dotp = lback(curwp->w_dotp);
+
+ curwp->w_doto = 0;
+ }
+ else{
+ /* leave cursor on first word in para */
+ curwp->w_doto = 0;
+ while(ucs4_isspace(lgetc(curwp->w_dotp, curwp->w_doto).c))
+ if(++curwp->w_doto >= llength(curwp->w_dotp)){
+ curwp->w_doto = 0;
+ curwp->w_dotp = lforw(curwp->w_dotp);
+ if(curwp->w_dotp == curbp->b_linep)
+ break;
+ }
+ }
+ }
+
+ curwp->w_flag |= WFMOVE; /* force screen update */
+ return(TRUE);
+}
+
+
+/*
+ * go forword to the end of the current paragraph
+ * here we look for a <NL><NL> or <NL><TAB> or <NL><SPACE>
+ * combination to delimit the begining of a paragraph
+ */
+int
+gotoeop(int f, int n)
+{
+ int quoted, qlen;
+ UCS qstr[NLINE], qstr2[NLINE];
+
+ if (n < 0) /* the other way...*/
+ return(gotobop(f, -n));
+
+ while (n-- > 0) { /* for each one asked for */
+
+ while(lisblank(curwp->w_dotp)){
+ curwp->w_doto = 0;
+ if((curwp->w_dotp = lforw(curwp->w_dotp)) == curbp->b_linep)
+ break;
+ }
+
+ /* scan line by line until we come to a line ending with
+ * a <NL><NL> or <NL><TAB> or <NL><SPACE>
+ *
+ * PLUS: if there's a quote string, a quoted-to-non-quoted
+ * line transition.
+ */
+ quoted = glo_quote_str
+ ? quote_match(glo_quote_str,
+ curwp->w_dotp, qstr, NLINE) : 0;
+ qlen = quoted ? ucs4_strlen(qstr) : 0;
+
+ while(curwp->w_dotp != curbp->b_linep
+ && llength(lforw(curwp->w_dotp)) > qlen
+ && (glo_quote_str
+ ? (quoted == quote_match(glo_quote_str,
+ lforw(curwp->w_dotp),
+ qstr2, NLINE)
+ && !ucs4_strcmp(qstr, qstr2))
+ : 1)
+ && lgetc(lforw(curwp->w_dotp), qlen).c != TAB
+ && lgetc(lforw(curwp->w_dotp), qlen).c != ' ')
+ curwp->w_dotp = lforw(curwp->w_dotp);
+
+ curwp->w_doto = llength(curwp->w_dotp);
+
+ /* still looking? */
+ if(n){
+ if(curwp->w_dotp == curbp->b_linep)
+ break;
+ else
+ curwp->w_dotp = lforw(curwp->w_dotp);
+
+ curwp->w_doto = 0;
+ }
+ }
+
+ curwp->w_flag |= WFMOVE; /* force screen update */
+ return(curwp->w_dotp != curbp->b_linep);
+}
+
+/*
+ * This routine, given a pointer to a LINE, and the current cursor goal
+ * column, return the best choice for the offset. The offset is returned.
+ * Used by "C-N" and "C-P".
+ */
+int
+getgoal(LINE *dlp)
+{
+ UCS c;
+ register int col;
+ register int newcol;
+ register int dbo;
+
+ col = 0;
+ dbo = 0;
+ while (dbo != llength(dlp)) {
+ c = lgetc(dlp, dbo).c;
+ newcol = col;
+
+ if (c == '\t'){
+ newcol |= 0x07;
+ ++newcol;
+ }
+ else if (ISCONTROL(c)){
+ newcol += 2;
+ }
+ else{
+ int w;
+
+ w = wcellwidth(c);
+ newcol += (w >= 0 ? w : 1);
+ }
+
+ if (newcol > curgoal)
+ break;
+
+ col = newcol;
+ ++dbo;
+ }
+
+ return (dbo);
+}
+
+
+/*
+ * Scroll the display forward (up) n lines.
+ */
+int
+scrollforw(int n, int movedot)
+{
+ register LINE *lp;
+ LINE *lp2;
+ register int nl;
+ int i;
+
+ nl = n;
+ lp = curwp->w_linep;
+ while (n-- && lp!=curbp->b_linep)
+ lp = lforw(lp);
+
+ if (movedot) { /* Move dot to top of page. */
+ curwp->w_dotp = lp;
+ curwp->w_doto = 0;
+ }
+
+ curwp->w_flag |= WFHARD;
+ if(lp == curbp->b_linep)
+ return(TRUE);
+ else
+ curwp->w_linep = lp;
+
+ /*
+ * if the header is open, close it ...
+ */
+ if(Pmaster && Pmaster->headents && ComposerTopLine != COMPOSER_TOP_LINE){
+ n -= ComposerTopLine - COMPOSER_TOP_LINE;
+ ToggleHeader(0);
+ }
+
+ /*
+ * scroll down from the top the same number of lines we've moved
+ * forward
+ */
+ if(TERM_OPTIMIZE)
+ scrollup(curwp, -1, nl-n-1);
+
+ if(!movedot){
+ /* Requested to not move the dot. Look for the dot in the current
+ * window. loop through all lines, stop when at end of window
+ * or endof buffer. If the dot is found, it can stay where it
+ * is, otherwise we do need to move it.
+ */
+ movedot = TRUE;
+ for ( lp2 = lp, i = 0;
+ lp2 != curbp->b_linep && i < curwp->w_ntrows;
+ lp2 = lforw(lp2), ++i) {
+ if (curwp->w_dotp == lp2) {
+ movedot = FALSE;
+ break;
+ }
+ }
+ if (movedot) {
+ /* Dot not found in window. Move to first line of window. */
+ curwp->w_dotp = lp;
+ curwp->w_doto = 0;
+ }
+ }
+
+ return (TRUE);
+}
+
+
+/*
+ * Scroll forward by a specified number of lines, or by a full page if no
+ * argument. Bound to "C-V". The "2" in the arithmetic on the window size is
+ * the overlap; this value is the default overlap value in ITS EMACS. Because
+ * this zaps the top line in the display window, we have to do a hard update.
+ */
+int
+forwpage(int f, int n)
+{
+
+ if (f == FALSE) {
+ n = curwp->w_ntrows - 2; /* Default scroll. */
+ if (n <= 0) /* Forget the overlap */
+ n = 1; /* if tiny window. */
+ } else if (n < 0)
+ return (backpage(f, -n));
+#if CVMVAS
+ else /* Convert from pages */
+ n *= curwp->w_ntrows; /* to lines. */
+#endif
+ return (scrollforw (n, TRUE));
+}
+
+
+/*
+ * Scroll back (down) number of lines.
+ */
+int
+scrollback(int n, int movedot)
+{
+ register LINE *lp, *tp;
+ register int nl;
+ int i;
+
+ if(Pmaster && Pmaster->headents){
+ /*
+ * go up into editing the mail header if on the top line
+ * and the user hits the up arrow!!!
+ */
+ if (lback(curwp->w_dotp) == curbp->b_linep){
+ /*
+ * if the editor returns anything except -1 then the user
+ * has requested something special, so let pico know...
+ */
+ return(HeaderEditor(1, 1));
+ }
+ }
+
+ /*
+ * Count back the number of lines requested.
+ */
+ nl = n;
+ lp = curwp->w_linep;
+ while (n-- && lback(lp)!=curbp->b_linep)
+ lp = lback(lp);
+
+ curwp->w_linep = lp;
+ curwp->w_flag |= WFHARD;
+
+ /*
+ * scroll down from the top the same number of lines we've moved
+ * forward
+ *
+ * This isn't too cool, but it has to be this way so we can
+ * gracefully scroll in the message header
+ */
+ if(Pmaster && Pmaster->headents){
+ if((lback(lp)==curbp->b_linep) && (ComposerTopLine==COMPOSER_TOP_LINE))
+ n -= entry_line(1000, TRUE); /* never more than 1000 headers */
+ if(nl-n-1 < curwp->w_ntrows)
+ if(TERM_OPTIMIZE)
+ scrolldown(curwp, -1, nl-n-1);
+ }
+ else
+ if(TERM_OPTIMIZE)
+ scrolldown(curwp, -1, nl-n-1);
+
+ if(Pmaster && Pmaster->headents){
+ /*
+ * if we're at the top of the page, and the header is closed,
+ * open it ...
+ */
+ if((lback(lp) == curbp->b_linep)
+ && (ComposerTopLine == COMPOSER_TOP_LINE)){
+ ToggleHeader(1);
+ movecursor(ComposerTopLine, 0);
+ }
+ }
+
+ /*
+ * Decide if we move the dot or not. Calculation done AFTER deciding
+ * if we display the header because that will change the number of
+ * lines on the screen.
+ */
+ if (movedot) {
+ /* Dot gets put at top of window. */
+ curwp->w_dotp = curwp->w_linep;
+ curwp->w_doto = 0;
+ }
+ else {
+ /* Requested not to move dot, but we do need to keep in on
+ * the screen. Verify that it is still in the range of lines
+ * visable in the window. Loop from the first line to the
+ * last line, until we reach the end of the buffer or the end
+ * of the window. If we find the dot, then we don't need
+ * to move it. */
+ movedot = TRUE;
+ for ( tp = curwp->w_linep, i = 0;
+ tp != curbp->b_linep && i < curwp->w_ntrows;
+ tp = lforw(tp), ++i) {
+ if (curwp->w_dotp == tp) {
+ movedot = FALSE;
+ break;
+ }
+ }
+ if (movedot) {
+ /* Dot not found in window. Move to last line of window. */
+ curwp->w_dotp = lback (tp);
+ curwp->w_doto = 0;
+ }
+ }
+
+ return (TRUE);
+}
+
+
+
+
+/*
+ * This command is like "forwpage", but it goes backwards. The "2", like
+ * above, is the overlap between the two windows. The value is from the ITS
+ * EMACS manual. Bound to "M-V". We do a hard update for exactly the same
+ * reason.
+ */
+int
+backpage(int f, int n)
+{
+
+ if (f == FALSE) {
+ n = curwp->w_ntrows - 2; /* Default scroll. */
+ if (n <= 0) /* Don't blow up if the */
+ n = 1; /* window is tiny. */
+ } else if (n < 0)
+ return (forwpage(f, -n));
+#if CVMVAS
+ else /* Convert from pages */
+ n *= curwp->w_ntrows; /* to lines. */
+#endif
+ return (scrollback (n, TRUE));
+}
+
+
+int
+scrollupline(int f, int n)
+{
+ return (scrollback (1, FALSE));
+}
+
+
+int
+scrolldownline(int f, int n)
+{
+ return (scrollforw (1, FALSE));
+}
+
+
+
+/*
+ * Scroll to a position.
+ */
+int
+scrollto(int f, int n)
+{
+#ifdef _WINDOWS
+ long scrollLine;
+ LINE *lp;
+ int i;
+
+ scrollLine = mswin_getscrollto ();
+
+ /*
+ * Starting at the first data line in the buffer, step forward
+ * 'scrollLine' lines to find the new top line. It is a circular
+ * list of buffers, so watch for the first line to reappear. if
+ * it does, we have some sort of internal error, abort scroll
+ * operation. Also watch for NULL, just in case.
+ */
+ lp = lforw (curbp->b_linep);
+ for (i = 0; i < scrollLine && lp != curbp->b_linep && lp != NULL; ++i)
+ lp = lforw(lp);
+
+ if (lp == curbp->b_linep || lp == NULL)
+ return (FALSE); /* Whoops! */
+
+
+ /* Set the new top line for the window and flag a redraw. */
+ curwp->w_linep = lp;
+ curwp->w_dotp = lp;
+ curwp->w_doto = 0;
+ curwp->w_flag |= WFHARD;
+
+ if(Pmaster && Pmaster->headents){
+ /*
+ * If we are at the top of the page and header not open, open it.
+ * If we are not at the top of the page and the header is open,
+ * close it.
+ */
+ if((lback(lp) == curbp->b_linep)
+ && (ComposerTopLine == COMPOSER_TOP_LINE)){
+ ToggleHeader(1);
+ movecursor(ComposerTopLine, 0);
+ }
+ else if((lback(lp) != curbp->b_linep)
+ && (ComposerTopLine != COMPOSER_TOP_LINE)){
+ ToggleHeader (0);
+ }
+ }
+#endif
+
+ return (TRUE);
+}
+
+
+
+/*
+ * Set the mark in the current window to the value of "." in the window. No
+ * errors are possible. Bound to "M-.". If told to set an already set mark
+ * unset it.
+ */
+int
+setmark(int f, int n)
+{
+ if(!curwp->w_markp){
+ curwp->w_markp = curwp->w_dotp;
+ curwp->w_marko = curwp->w_doto;
+ if(n)
+ emlwrite("Mark Set", NULL);
+ }
+ else{
+ /* clear inverse chars between here and dot */
+ markregion(0);
+ curwp->w_markp = NULL;
+ if(n)
+ emlwrite("Mark UNset", NULL);
+ }
+
+#ifdef _WINDOWS
+ mswin_allowcopycut(curwp->w_markp ? kremove : NULL);
+#endif
+ return (TRUE);
+}
+
+
+/*
+ * Swap the values of "." and "mark" in the current window. This is pretty
+ * easy, bacause all of the hard work gets done by the standard routine
+ * that moves the mark about. The only possible error is "no mark". Bound to
+ * "C-X C-X".
+ */
+int
+swapmark(int f, int n)
+{
+ register LINE *odotp;
+ register int odoto;
+
+ if (curwp->w_markp == NULL) {
+ if(Pmaster == NULL)
+ emlwrite("No mark in this window", NULL);
+ return (FALSE);
+ }
+
+ odotp = curwp->w_dotp;
+ odoto = curwp->w_doto;
+ curwp->w_dotp = curwp->w_markp;
+ curwp->w_doto = curwp->w_marko;
+ curwp->w_markp = odotp;
+ curwp->w_marko = odoto;
+ curwp->w_flag |= WFMOVE;
+ return (TRUE);
+}
+
+
+/*
+ * Set the mark in the current window to the value of "." in the window. No
+ * errors are possible. Bound to "M-.". If told to set an already set mark
+ * unset it.
+ */
+int
+setimark(int f, int n)
+{
+ curwp->w_imarkp = curwp->w_dotp;
+ curwp->w_imarko = curwp->w_doto;
+ return(TRUE);
+}
+
+
+/*
+ * Swap the values of "." and "mark" in the current window. This is pretty
+ * easy, bacause all of the hard work gets done by the standard routine
+ * that moves the mark about. The only possible error is "no mark". Bound to
+ * "C-X C-X".
+ */
+int
+swapimark(int f, int n)
+{
+ register LINE *odotp;
+ register int odoto;
+
+ if (curwp->w_imarkp == NULL) {
+ if(Pmaster == NULL)
+ emlwrite("Programmer botch! No mark in this window", NULL);
+ return (FALSE);
+ }
+
+ odotp = curwp->w_dotp;
+ odoto = curwp->w_doto;
+ curwp->w_dotp = curwp->w_imarkp;
+ curwp->w_doto = curwp->w_imarko;
+ curwp->w_imarkp = odotp;
+ curwp->w_imarko = odoto;
+ curwp->w_flag |= WFMOVE;
+ return (TRUE);
+}
+
+
+/*
+ * If dot comes before mark, do nothing.
+ * If mark comes before dot, swap them.
+ */
+void
+swap_mark_and_dot_if_mark_comes_first(void)
+{
+ LINE *blp, *flp;
+
+ if(!(curwp && curwp->w_dotp && curwp->w_markp))
+ return;
+
+ if(curwp->w_dotp == curwp->w_markp){ /* they are in the same line */
+ if(curwp->w_doto > curwp->w_marko)
+ swapmark(0,1);
+
+ return;
+ }
+
+ /*
+ * Search forward and backward from dot to see if mark
+ * is less than or greater than dot.
+ */
+ flp = blp = curwp->w_dotp;
+ while(flp != curbp->b_linep || lback(blp) != curbp->b_linep){
+ if(flp != curbp->b_linep){
+ flp = lforw(flp);
+ if(flp == curwp->w_markp) /* dot already less than mark */
+ return;
+ }
+
+ if(lback(blp) != curbp->b_linep){
+ blp = lback(blp);
+ if(blp == curwp->w_markp){
+ swapmark(0, 1);
+ return;
+ }
+ }
+ }
+}
+
+
+#ifdef MOUSE
+
+/*
+ * Handle a mouse down.
+ */
+int
+mousepress(int f, int n)
+{
+ MOUSEPRESS mp;
+ LINE *lp;
+ int i;
+
+
+ mouse_get_last (NULL, &mp);
+
+
+ lp = curwp->w_linep;
+ i = mp.row - ((Pmaster && Pmaster->headents) ? ComposerTopLine : 2);
+ if (i < 0) {
+ if (Pmaster) {
+ /* Clear existing region. */
+ if (curwp->w_markp)
+ setmark(0,1);
+
+ /* Move to top of document before editing header. */
+ curwp->w_dotp = curwp->w_linep;
+ curwp->w_doto = 0;
+ curwp->w_flag |= WFMOVE;
+ update (); /* And update. */
+
+ return (HeaderEditor (1, 1));
+ }
+ }
+ else {
+ while(i-- && lp != curbp->b_linep)
+ lp = lforw(lp);
+
+ curgoal = mp.col;
+ curwp->w_dotp = lp;
+ curwp->w_doto = getgoal(lp);
+ curwp->w_flag |= WFMOVE;
+
+ if(mp.doubleclick)
+ setmark(0, 1);
+ }
+
+ return(FALSE);
+}
+
+
+int
+toggle_xterm_mouse(int f, int n)
+{
+#ifndef _WINDOWS
+ int e;
+
+ (e=mouseexist()) ? end_mouse() : (void) init_mouse();
+ if(e != mouseexist()){
+ mouseexist() ? emlwrite(_("Xterm mouse tracking on!"), NULL)
+ : emlwrite(_("Xterm mouse tracking off!"), NULL);
+ }
+ else if(!e)
+ emlwrite(_("Xterm mouse tracking still off ($DISPLAY variable set?)"), NULL);
+#endif
+ return(TRUE);
+}
+#endif
diff --git a/pico/bind.c b/pico/bind.c
new file mode 100644
index 00000000..83e93627
--- /dev/null
+++ b/pico/bind.c
@@ -0,0 +1,406 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: bind.c 857 2007-12-08 00:49:45Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ *
+ * Program: Key binding routines
+ */
+
+/* This file is for functions having to do with key bindings,
+ descriptions, help commands, and command line execution.
+
+ written 11-feb-86 by Daniel Lawrence
+ */
+
+#include "headers.h"
+
+int arraylen(char **);
+
+/*
+ * help - help function for pico (UW pared down version of uemacs).
+ * this function will intentionally garbage with helpful
+ * tips and then wait for a ' ' to be hit to then update the
+ * screen.
+ */
+
+
+static char *helptext[] = {
+ /* TRANSLATORS: The next several lines go together as help text.
+ Leave the ~ characters where they are, they cause the following
+ character to be printed in boldface as long as the first character
+ of the line is also a ~. */
+ N_(" Pico Help Text"),
+ " ",
+ N_(" Pico is designed to be a simple, easy-to-use text editor with a"),
+ N_(" layout very similar to the Alpine mailer. The status line at the"),
+ N_(" top of the display shows pico's version, the current file being"),
+ N_(" edited and whether or not there are outstanding modifications"),
+ N_(" that have not been saved. The third line from the bottom is used"),
+ N_(" to report informational messages and for additional command input."),
+ N_(" The bottom two lines list the available editing commands."),
+ " ",
+ N_(" Each character typed is automatically inserted into the buffer"),
+ N_(" at the current cursor position. Editing commands and cursor"),
+ N_(" movement (besides arrow keys) are given to pico by typing"),
+ N_(" special control-key sequences. A caret, '^', is used to denote"),
+ N_("~ the control key, sometimes marked \"CTRL\", so the ~C~T~R~L~-~q key"),
+ N_("~ combination is written as ~^~Q."),
+ " ",
+ N_(" The following functions are available in pico (where applicable,"),
+ N_(" corresponding function key commands are in parentheses)."),
+ " ",
+ N_("~ ~^~G (~F~1) Display this help text."),
+ " ",
+ N_("~ ~^~F move Forward a character."),
+ N_("~ ~^~B move Backward a character."),
+ N_("~ ~^~P move to the Previous line."),
+ N_("~ ~^~N move to the Next line."),
+ N_("~ ~^~A move to the beginning of the current line."),
+ N_("~ ~^~E move to the End of the current line."),
+ N_("~ ~^~V (~F~8) move forward a page of text."),
+ N_("~ ~^~Y (~F~7) move backward a page of text."),
+ " ",
+ N_("~ ~^~W (~F~6) Search for (where is) text, neglecting case."),
+ N_("~ ~^~L Refresh the display."),
+ " ",
+ N_("~ ~^~D Delete the character at the cursor position."),
+ N_("~ ~^~^ Mark cursor position as beginning of selected text."),
+ N_(" Note: Setting mark when already set unselects text."),
+ N_("~ ~^~K (~F~9) Cut selected text (displayed in inverse characters)."),
+ N_(" Note: The selected text's boundary on the cursor side"),
+ N_(" ends at the left edge of the cursor. So, with "),
+ N_(" selected text to the left of the cursor, the "),
+ N_(" character under the cursor is not selected."),
+ N_("~ ~^~U (~F~1~0) Uncut (paste) last cut text inserting it at the"),
+ N_(" current cursor position."),
+ N_("~ ~^~I Insert a tab at the current cursor position."),
+ " ",
+ N_("~ ~^~J (~F~4) Format (justify) the current paragraph."),
+ N_(" Note: paragraphs delimited by blank lines or indentation."),
+ N_("~ ~^~T (~F~1~2) To invoke the spelling checker"),
+ N_("~ ~^~C (~F~1~1) Report current cursor position"),
+ " ",
+ N_("~ ~^~R (~F~5) Insert an external file at the current cursor position."),
+ N_("~ ~^~O (~F~3) Output the current buffer to a file, saving it."),
+ N_("~ ~^~X (~F~2) Exit pico, saving buffer."),
+ " ",
+ N_(" End of Help."),
+ " ",
+ NULL
+};
+
+
+/*
+ * arraylen - return the number of bytes in an array of char
+ */
+int
+arraylen(char **array)
+{
+ register int i=0;
+
+ while(array[i++] != NULL) ;
+ return(i);
+}
+
+
+/*
+ * whelp - display help text for the composer and pico
+ */
+int
+whelp(int f, int n)
+{
+ if(term.t_mrow == 0){ /* blank keymenu in effect */
+ if(km_popped == 0){
+ /* cause keymenu display */
+ km_popped = 2;
+ if(!Pmaster)
+ sgarbf = TRUE;
+
+ return(TRUE);
+ }
+ }
+
+ if(Pmaster){
+ VARS_TO_SAVE *saved_state;
+
+ saved_state = save_pico_state();
+ (*Pmaster->helper)(Pmaster->composer_help,
+ Pmaster->headents
+ ? _("Help for the Alpine Composer")
+ : _("Help for Signature Editor"),
+ 1);
+ if(saved_state){
+ restore_pico_state(saved_state);
+ free_pico_state(saved_state);
+ }
+
+ ttresize();
+ picosigs(); /* restore any altered handlers */
+ curwp->w_flag |= WFMODE;
+ if(km_popped) /* this will unpop us */
+ curwp->w_flag |= WFHARD;
+ }
+ else{
+ int mrow_was_zero = 0;
+
+ /* always want keyhelp showing during help */
+ if(term.t_mrow == 0){
+ mrow_was_zero++;
+ term.t_mrow = 2;
+ }
+
+ /* TRANSLATORS: Pico is the name of a program */
+ pico_help(helptext, _("Help for Pico"), 1);
+ /* put it back the way it was */
+ if(mrow_was_zero)
+ term.t_mrow = 0;
+ }
+
+ sgarbf = TRUE;
+ return(FALSE);
+}
+
+static KEYMENU menu_scroll[] = {
+ {NULL, NULL, KS_NONE}, {NULL, NULL, KS_NONE},
+ {NULL, NULL, KS_NONE}, {NULL, NULL, KS_NONE},
+ {NULL, NULL, KS_NONE}, {NULL, NULL, KS_NONE},
+ {"^X", N_("Exit Help"), KS_NONE}, {NULL, NULL, KS_NONE},
+ {NULL, NULL, KS_NONE}, {NULL, NULL, KS_NONE},
+ {NULL, NULL, KS_NONE}, {NULL, NULL, KS_NONE}
+};
+#define PREV_KEY 3
+#define NEXT_KEY 9
+
+
+#define OVERLAP 2 /* displayed page overlap */
+
+/*
+ * scrollw - takes beginning row and ending row to diplay an array
+ * of text lines. returns either 0 if scrolling terminated
+ * normally or the value of a ctrl character typed to end it.
+ *
+ * updates -
+ * 01/11/89 - added stripe call if 1st char is tilde - '~'
+ *
+ */
+int
+wscrollw(int begrow, int endrow, char *utf8textp[], int textlen)
+{
+ register int loffset = 0;
+ register int prevoffset = -1;
+ register int dlines;
+ register int i;
+ register int cont;
+ register int done = 0;
+ register char *buf;
+ UCS c;
+
+ dlines = endrow - begrow - 1;
+ while(!done) {
+ /*
+ * diplay a page loop ...
+ */
+ if(prevoffset != loffset){
+ for(i = 0; i < dlines; i++){
+ movecursor(i + begrow, 0);
+ peeol();
+ if((loffset+i) < textlen){
+ buf = _(&(utf8textp[loffset+i][0]));
+ if(*buf == '~'){
+ buf++;
+ wstripe(begrow+i, 0, buf, '~');
+ }
+ else{
+ pputs_utf8(buf, 0);
+ }
+ }
+ }
+ /*
+ * put up the options prompt
+ */
+ movecursor(begrow + dlines, 0);
+ cont = (loffset+dlines < textlen);
+ if(cont){ /* continue ? */
+ menu_scroll[NEXT_KEY].name = "^V";
+ /* TRANSLATORS: Next Page, a command key label */
+ menu_scroll[NEXT_KEY].label = N_("Next Pg");
+ }
+ else
+ menu_scroll[NEXT_KEY].name = NULL;
+
+ if(loffset){
+ menu_scroll[PREV_KEY].name = "^Y";
+ /* TRANSLATORS: Previous Page */
+ menu_scroll[PREV_KEY].label = N_("Prev Pg");
+ }
+ else
+ menu_scroll[PREV_KEY].name = NULL;
+
+ wkeyhelp(menu_scroll);
+ }
+
+ (*term.t_flush)();
+
+ c = GetKey();
+
+ prevoffset = loffset;
+ switch(c){
+ case (CTRL|'X') : /* quit */
+ case F2 :
+ done = 1;
+ break;
+ case (CTRL|'Y') : /* prev page */
+ case F7 : /* prev page */
+ if((loffset-dlines-OVERLAP) > 0){
+ loffset -= (dlines-OVERLAP);
+ }
+ else{
+ if(loffset != 0){
+ prevoffset = -1;
+ }
+ else{
+ (*term.t_beep)();
+ }
+ loffset = 0;
+ }
+ break;
+ case (CTRL|'V') : /* next page */
+ case F8 :
+ if(cont){
+ loffset += (dlines-OVERLAP);
+ }
+ else{
+ (*term.t_beep)();
+ }
+ break;
+ case '\016' : /* prev-line */
+ case (CTRL|'N') :
+ if(cont)
+ loffset++;
+ else
+ (*term.t_beep)();
+ break;
+ case '\020' : /* prev-line */
+ case (CTRL|'P') :
+ if(loffset > 0)
+ loffset--;
+ else
+ (*term.t_beep)();
+ break;
+ case '\014' : /* refresh */
+ case (CTRL|'L') : /* refresh */
+ modeline(curwp);
+ update();
+ prevoffset = -1;
+ break;
+ case NODATA :
+ break;
+#ifdef notdef
+ /*
+ * We don't handle window resize events correctly when in pico help.
+ * resize_pico() redraws the edit window instead of the help window.
+ * A ^L will redraw the help text. What we'd like is something like
+ * a KEY_RESIZE return from GetKey. If we had that we could exit
+ * wscrollw with a FALSE return value and have that cause us to loop
+ * back into wscrollw with the adjusted size. That would still mean
+ * the edit text would be redrawn first...
+ */
+#endif /* notdef */
+ default :
+ unknown_command(c);
+ break;
+ }
+ }
+
+ return(TRUE);
+}
+
+
+/*
+ * normalize_cmd - given a char and list of function key to command key
+ * mappings, return, depending on gmode, the right command.
+ * The list is an array of (Fkey, command-key) pairs.
+ * sc is the index in the array that means to ignore fkey
+ * vs. command key mapping
+ *
+ * rules: 1. if c not in table (either fkey or command), let it thru
+ * 2. if c matches, but in other mode, punt it
+ */
+UCS
+normalize_cmd(UCS c, UCS list[][2], int sc)
+{
+ int i;
+
+ for(i=0; i < 12; i++){
+ if(c == list[i][(FUNC&c) ? 0 : 1]){ /* in table? */
+ if(i == sc) /* SPECIAL CASE! */
+ return(list[i][1]);
+
+ if(list[i][1] == NODATA) /* no mapping ! */
+ return(c);
+
+ if(((FUNC&c) == FUNC) && !((gmode&MDFKEY) == MDFKEY))
+ return(c); /* not allowed, let caller handle it */
+ else
+ return(list[i][1]); /* never return func keys */
+ }
+ }
+
+ return(c);
+}
+
+
+/*
+ * rebind - replace the first function with the second
+ */
+void
+rebindfunc(int (*a)(int, int), int (*b)(int, int))
+{
+ KEYTAB *kp;
+
+ kp = (Pmaster) ? &keytab[0] : &pkeytab[0];
+
+ while(kp->k_fp != NULL){ /* go thru whole list, and */
+ if(kp->k_fp == a)
+ kp->k_fp = b; /* replace all occurances */
+ kp++;
+ }
+}
+
+
+/*
+ * bindtokey - bind function f to command c
+ */
+int
+bindtokey(UCS c, int (*f)(int, int))
+{
+ KEYTAB *kp, *ktab = (Pmaster) ? &keytab[0] : &pkeytab[0];
+
+ for(kp = ktab; kp->k_fp; kp++)
+ if(kp->k_code == c){
+ kp->k_fp = f; /* set to new function */
+ break;
+ }
+
+ /* not found? create new binding */
+ if(!kp->k_fp && kp < &ktab[NBINDS]){
+ kp->k_code = c; /* assign new code and function */
+ kp->k_fp = f;
+ (++kp)->k_code = 0; /* and null out next slot */
+ kp->k_fp = NULL;
+ }
+
+ return(TRUE);
+}
diff --git a/pico/blddate.c b/pico/blddate.c
new file mode 100644
index 00000000..00f0b558
--- /dev/null
+++ b/pico/blddate.c
@@ -0,0 +1,53 @@
+#include <system.h>
+#include <general.h>
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ struct tm *t;
+ FILE *outfile=stdout;
+ time_t ltime;
+
+ if(argc > 1 && (outfile = fopen(argv[1], "w")) == NULL){
+ /* TRANSLATORS: an error message when trying to create a file */
+ fprintf(stderr, _("can't create '%s'\n"), argv[1]);
+ exit(1);
+ }
+
+ time(&ltime);
+ t = localtime(&ltime);
+ fprintf(outfile,"char datestamp[]=\"%02d-%s-%d\";\n", t->tm_mday,
+ (t->tm_mon == 0) ? "Jan" :
+ (t->tm_mon == 1) ? "Feb" :
+ (t->tm_mon == 2) ? "Mar" :
+ (t->tm_mon == 3) ? "Apr" :
+ (t->tm_mon == 4) ? "May" :
+ (t->tm_mon == 5) ? "Jun" :
+ (t->tm_mon == 6) ? "Jul" :
+ (t->tm_mon == 7) ? "Aug" :
+ (t->tm_mon == 8) ? "Sep" :
+ (t->tm_mon == 9) ? "Oct" :
+ (t->tm_mon == 10) ? "Nov" :
+ (t->tm_mon == 11) ? "Dec" : "UKN",
+ 1900 + t->tm_year);
+
+ fprintf(outfile, "char hoststamp[]=\"random-pc\";\n");
+
+ fclose(outfile);
+
+ exit(0);
+}
diff --git a/pico/browse.c b/pico/browse.c
new file mode 100644
index 00000000..46e01fd0
--- /dev/null
+++ b/pico/browse.c
@@ -0,0 +1,2915 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: browse.c 942 2008-03-04 18:21:33Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ *
+ * Program: Routines to support file browser in pico and Pine composer
+ *
+ *
+ * NOTES:
+ *
+ * Misc. thoughts (mss, 5 Apr 92)
+ *
+ * This is supposed to be just a general purpose browser, equally
+ * callable from either pico or the pine composer. Someday, it could
+ * even be used to "wrap" the unix file business for really novice
+ * users. The stubs are here for renaming, copying, creating directories,
+ * deleting, undeleting (thought is delete just moves files to
+ * ~/.pico.deleted directory or something and undelete offers the
+ * files in there for undeletion: kind of like the mac trashcan).
+ *
+ * Nice side effects
+ *
+ * Since the full path name is always maintained and referencing ".."
+ * stats the path stripped of its trailing name, the unpleasantness of
+ * symbolic links is hidden.
+ *
+ * Fleshed out the file managements stuff (mss, 24 June 92)
+ *
+ *
+ */
+#include "headers.h"
+#include "../c-client/mail.h"
+#include "../c-client/rfc822.h"
+#include "../pith/osdep/collate.h"
+#include "../pith/charconv/filesys.h"
+#include "../pith/conf.h"
+
+#ifndef _WINDOWS
+
+#if defined(bsd) || defined(lnx)
+extern int errno;
+#endif
+
+
+/*
+ * directory cell structure
+ */
+struct fcell {
+ char *fname; /* file name */
+ unsigned mode; /* file's mode */
+ char size[16]; /* file's size in s */
+ struct fcell *next;
+ struct fcell *prev;
+};
+
+
+/*
+ * master browser structure
+ */
+static struct bmaster {
+ struct fcell *head; /* first cell in list */
+ struct fcell *top; /* cell in top left */
+ struct fcell *current; /* currently selected */
+ int longest; /* longest file name (in screen width) */
+ int fpl; /* file names per line */
+ int cpf; /* chars / file / line */
+ int flags;
+ char dname[NLINE]; /* this dir's name (UTF-8) */
+ char *names; /* malloc'd name array (UTF-8) */
+ LMLIST *lm;
+} *gmp; /* global master ptr */
+
+
+/*
+ * title for exported browser display
+ */
+static char *browser_title = NULL;
+
+
+struct bmaster *getfcells(char *, int);
+void PaintCell(int, int, int, struct fcell *, int);
+void PaintBrowser(struct bmaster *, int, int *, int *);
+void BrowserKeys(void);
+void layoutcells(struct bmaster *);
+void percdircells(struct bmaster *);
+int PlaceCell(struct bmaster *, struct fcell *, int *, int *);
+void zotfcells(struct fcell *);
+void zotmaster(struct bmaster **);
+struct fcell *FindCell(struct bmaster *, char *);
+int sisin(char *, char *);
+void p_chdir(struct bmaster *);
+void BrowserAnchor(char *);
+void ClearBrowserScreen(void);
+void BrowserRunChild(char *, char *);
+int fcell_is_selected(struct fcell *, struct bmaster *);
+void add_cell_to_lmlist(struct fcell *, struct bmaster *);
+void del_cell_from_lmlist(struct fcell *, struct bmaster *);
+
+
+static KEYMENU menu_browse[] = {
+ {"?", N_("Get Help"), KS_SCREENHELP}, {NULL, NULL, KS_NONE},
+ {NULL, NULL, KS_NONE}, {"-", N_("Prev Pg"), KS_PREVPAGE},
+ {"D", N_("Delete"), KS_NONE}, {"C",N_("Copy"), KS_NONE},
+ {NULL, NULL, KS_NONE}, {NULL, NULL, KS_NONE},
+ {"W", N_("Where is"), KS_NONE}, {"Spc", N_("Next Pg"), KS_NEXTPAGE},
+ {"R", N_("Rename"), KS_NONE}, {NULL, NULL, KS_NONE}
+};
+#define QUIT_KEY 1
+#define EXEC_KEY 2
+#define GOTO_KEY 6
+#define SELECT_KEY 7
+#define PICO_KEY 11
+
+
+#define DIRWORD "dir"
+#define PARENTDIR "parent"
+#define SELECTWORD "SELECT"
+
+
+/*
+ * Default pager used by the stand-alone file browser.
+ */
+#define BROWSER_PAGER ((gmode & MDFKEY) ? "pine -k -F" : "pine -F")
+
+
+/*
+ * function key mappings for callable browser
+ */
+static UCS bfmappings[2][12][2] = { { { F1, '?'}, /* stand alone */
+ { F2, NODATA }, /* browser function */
+ { F3, 'q'}, /* key mappings... */
+ { F4, 'v'},
+ { F5, 'l'},
+ { F6, 'w'},
+ { F7, '-'},
+ { F8, ' '},
+ { F9, 'd'},
+ { F10, 'r'},
+ { F11, 'c'},
+ { F12, 'e'} },
+ { { F1, '?'}, /* callable browser */
+ { F2, NODATA }, /* function key */
+ { F3, 'e'}, /* mappings... */
+ { F4, 's'},
+ { F5, NODATA },
+ { F6, 'w'},
+ { F7, '-'},
+ { F8, ' '},
+ { F9, 'd'},
+ { F10, 'r'},
+ { F11, 'c'},
+ { F12, 'a'} } };
+
+
+/*
+ * Browser help for pico (pine composer help handed to us by pine)
+ */
+static char *BrowseHelpText[] = {
+/* TRANSLATORS: The next several lines go together. The ~ characters
+ should be left in front of the characters they cause to be bold. */
+N_("Help for Browse Command"),
+" ",
+N_(" Pico's file browser is used to select a file from the"),
+N_(" file system for inclusion in the edited text."),
+" ",
+N_("~ Both directories and files are displayed. Press ~S"),
+N_("~ or ~R~e~t~u~r~n to select a file or directory. When a file"),
+N_(" is selected during the \"Read File\" command, it is"),
+N_(" inserted into edited text. Answering \"yes\" to the"),
+N_(" verification question after a directory is selected causes"),
+N_(" the contents of that directory to be displayed for selection."),
+" ",
+N_(" The file named \"..\" is special, and means the \"parent\""),
+N_(" of the directory being displayed. Select this directory"),
+N_(" to move upward in the directory tree."),
+" ",
+N_("End of Browser Help."),
+" ",
+NULL
+};
+
+/*
+ * Help for standalone browser (pilot)
+ */
+static char *sa_BrowseHelpText[] = {
+/* TRANSLATORS: Some more help text */
+N_("Help for Pilot (PIne's Looker-upper Of Things"),
+" ",
+N_(" Pilot is a simple, display-oriented file system browser based on the"),
+N_("~ Alpine message system composer. As with Alpine, commands are displayed at"),
+N_("~ the bottom of the screen, and context-sensitive help is provided."),
+" ",
+N_("~ Pilot displays the current working directory at the top of the screen."),
+N_("~ The directory's contents are displayed in columns of file name, file"),
+N_("~ size pairs. Names that are directories are indicated by the name"),
+N_("~ ~(~d~i~r~) in place of the file size. The parent of the current working"),
+N_("~ directory is indicated by the file name ~.~. and size of ~(~p~a~r~e~n~t~ ~d~i~r~)."),
+N_("~ File names that are symbolic links to other files are displayed with a"),
+N_("~ file size of ~-~-."),
+" ",
+N_(" The following function keys are available in Pilot:"),
+" ",
+N_("~ ~? Display this help text."),
+N_("~ ~Q Quit Pilot."),
+" ",
+N_("~ ~V View the currently selected file or open the selected directory."),
+N_("~ Note: Pilot invokes ~p~i~n~e ~-~F, or the program defined by the ~P~A~G~E~R"),
+N_("~ environment variable, to view the file."),
+N_("~ ~L Launch an external application program."),
+" ",
+N_("~ ~W Search for a file by name."),
+N_("~ ~- Scroll up one page."),
+N_("~ ~S~p~a~c~e Scroll down one page."),
+N_("~ ~N,~^~F Move forward (right) one column."),
+N_("~ ~P,~^~B Move back (left) one column."),
+N_("~ ~^~N Move down one row."),
+N_("~ ~^~P Move up one row."),
+" ",
+N_("~ ~D Delete the selected file."),
+N_("~ ~R Rename the selected file or directory."),
+N_("~ ~C Copy the selected file."),
+N_("~ ~E Edit the selected file."),
+N_("~ Note: Pilot invokes ~p~i~c~o, or the program defined by the ~E~D~I~T~O~R"),
+N_("~ environment variable, to edit the file."),
+" ",
+N_("End of Pilot Help."),
+NULL
+};
+
+
+
+/*
+ * pico_file_browse - Exported version of FileBrowse below.
+ */
+int
+pico_file_browse(PICO *pdata, char *dir, size_t dirlen, char *fn, size_t fnlen,
+ char *sz, size_t szlen, int flags)
+{
+ int rv;
+ char title_buf[64];
+
+ Pmaster = pdata;
+ gmode = pdata->pine_flags | MDEXTFB;
+ km_popped = 0;
+
+ /* only init screen bufs for display and winch handler */
+ if(!vtinit())
+ return(-1);
+
+ if(Pmaster){
+ term.t_mrow = Pmaster->menu_rows;
+ if(Pmaster->oper_dir)
+ strncpy(opertree, Pmaster->oper_dir, NLINE);
+
+ if(*opertree)
+ fixpath(opertree, sizeof(opertree));
+ }
+
+ /* install any necessary winch signal handler */
+ ttresize();
+
+ snprintf(title_buf, sizeof(title_buf), "%s FILE", pdata->pine_anchor);
+ set_browser_title(title_buf);
+ rv = FileBrowse(dir, dirlen, fn, fnlen, sz, szlen, flags, NULL);
+ set_browser_title(NULL);
+ vttidy(); /* clean up tty handling */
+ zotdisplay(); /* and display structures */
+ Pmaster = NULL; /* and global config structure */
+ return(rv);
+}
+
+
+
+/*
+ * FileBrowse - display contents of given directory dir
+ *
+ * intput:
+ * dir points to initial dir to browse.
+ * dirlen- buffer size of dir
+ * fn initial file name.
+ * fnlen- buffer size of fn
+ *
+ * returns:
+ * dir points to currently selected directory (without
+ * trailing file system delimiter)
+ * fn points to currently selected file
+ * sz points to size of file if ptr passed was non-NULL
+ * flags
+ *
+ * Special dispensation for FB_LMODE. If the caller sets
+ * FB_LMODEPOS, and the caller passes a non-null lmreturn,
+ * then the return values will be in lmreturn instead of
+ * in dir and fn. The caller is responsible for freeing
+ * the contents of lmreturn.
+ *
+ * 1 if a file's been selected
+ * 0 if no files selected
+ * -1 if there were problems
+ */
+int
+FileBrowse(char *dir, size_t dirlen, char *fn, size_t fnlen,
+ char *sz, size_t szlen, int fb_flags, LMLIST **lmreturn)
+{
+ UCS c, new_c;
+ int status, i, j;
+ int row, col, crow, ccol;
+ char *p, *envp, child[NLINE], tmp[NLINE];
+ struct bmaster *mp;
+ struct fcell *tp;
+ EML eml;
+#ifdef MOUSE
+ MOUSEPRESS mousep;
+#endif
+
+ child[0] = '\0';
+
+ if((gmode&MDTREE) && !in_oper_tree(dir)){
+ eml.s = opertree;
+ emlwrite(_("\007Can't read outside of %s in restricted mode"), &eml);
+ sleep(2);
+ return(0);
+ }
+
+ if(gmode&MDGOTO){
+ /* fix up function key mapping table */
+ /* fix up key menu labels */
+ }
+
+ /* build contents of cell structures */
+ if((gmp = getfcells(dir, fb_flags)) == NULL)
+ return(-1);
+
+ tp = NULL;
+ if(fn && *fn){
+ if((tp = FindCell(gmp, fn)) != NULL){
+ gmp->current = tp;
+ PlaceCell(gmp, gmp->current, &row, &col);
+ }
+ }
+
+ /* paint screen */
+ PaintBrowser(gmp, 0, &crow, &ccol);
+
+ while(1){ /* the big loop */
+ if(!(gmode&MDSHOCUR)){
+ crow = term.t_nrow-term.t_mrow;
+ ccol = 0;
+ }
+
+ if(!(gmode&MDSHOCUR))
+ movecursor(crow, ccol);
+ else if(gmp->flags & FB_LMODEPOS){
+ if(gmp->flags & FB_LMODE && gmp->current->mode != FIODIR)
+ movecursor(crow, ccol+1);
+ else
+ movecursor(crow, ccol+4);
+ }
+ else
+ movecursor(crow, ccol);
+
+ if(km_popped){
+ km_popped--;
+ if(km_popped == 0)
+ /* cause bottom three lines to repaint */
+ PaintBrowser(gmp, 0, &crow, &ccol);
+ }
+
+ if(km_popped){ /* temporarily change to cause menu to paint */
+ term.t_mrow = 2;
+ movecursor(term.t_nrow-2, 0); /* clear status line */
+ peeol();
+ BrowserKeys();
+ term.t_mrow = 0;
+ }
+
+ (*term.t_flush)();
+
+#ifdef MOUSE
+ mouse_in_content(KEY_MOUSE, -1, -1, 0, 0);
+ register_mfunc(mouse_in_content,2,0,term.t_nrow-(term.t_mrow+1),
+ term.t_ncol);
+#endif
+ c = GetKey();
+#ifdef MOUSE
+ clear_mfunc(mouse_in_content);
+#endif
+
+ if(Pmaster){
+ if(Pmaster->newmail && (c == NODATA || time_to_check())){
+ if((*Pmaster->newmail)(c == NODATA ? 0 : 2, 1) >= 0){
+ int rv;
+
+ if(km_popped){ /* restore display */
+ km_popped = 0;
+ PaintBrowser(gmp, 0, &crow, &ccol);
+ }
+
+ clearcursor();
+ mlerase();
+ rv = (*Pmaster->showmsg)(c);
+ ttresize();
+ picosigs();
+ if(rv) /* Did showmsg corrupt the display? */
+ PaintBrowser(gmp, 0, &crow, &ccol); /* Yes, repaint */
+
+ mpresf = 1;
+ }
+
+ clearcursor();
+ if(gmp->flags & FB_LMODEPOS){
+ if(gmp->flags & FB_LMODE && gmp->current->mode != FIODIR)
+ movecursor(crow, ccol+1);
+ else
+ movecursor(crow, ccol+4);
+ }
+ else
+ movecursor(crow, ccol);
+ }
+ }
+ else{
+ if(get_input_timeout() && (c == NODATA || time_to_check()))
+ if(pico_new_mail())
+ emlwrite(_("You may possibly have new mail."), NULL);
+ }
+
+ if(km_popped)
+ switch(c){
+ case NODATA:
+ case (CTRL|'L'):
+ km_popped++;
+ break;
+
+ default:
+ /* clear bottom three lines */
+ movecursor(term.t_nrow-2, 0);
+ peeol();
+ movecursor(term.t_nrow-1, 0);
+ peeol();
+ movecursor(term.t_nrow, 0);
+ peeol();
+ break;
+ }
+
+ if(c == NODATA) /* GetKey timed out */
+ continue;
+ else if(Pmaster)
+ (*Pmaster->keybinput)();
+
+
+ if(mpresf){ /* blast old messages */
+ if(mpresf++ > MESSDELAY){ /* every few keystrokes */
+ mlerase();
+ }
+ }
+
+ /* process commands */
+ switch(new_c = normalize_cmd(c,bfmappings[(gmode&MDBRONLY)?0:1],2)){
+
+ case KEY_RIGHT: /* move right */
+ case (CTRL|'@'):
+ case (CTRL|'F'): /* forward */
+ case 'n' :
+ case 'N' :
+ if(gmp->current->next == NULL){
+ (*term.t_beep)();
+ break;
+ }
+
+ PlaceCell(gmp, gmp->current, &row, &col);
+ PaintCell(row, col, gmp->cpf, gmp->current, 0);
+ gmp->current = gmp->current->next;
+ if(PlaceCell(gmp, gmp->current, &row, &col)){
+ PaintBrowser(gmp, 1, &crow, &ccol);
+ }
+ else{
+ PaintCell(row, col, gmp->cpf, gmp->current, 1);
+ crow = row;
+ ccol = col;
+ }
+ break;
+
+ case KEY_LEFT: /* move left */
+ case (CTRL|'B'): /* back */
+ case 'p' :
+ case 'P' :
+ if(gmp->current->prev == NULL){
+ (*term.t_beep)();
+ break;
+ }
+
+ PlaceCell(gmp, gmp->current, &row, &col);
+ PaintCell(row, col, gmp->cpf, gmp->current, 0);
+ gmp->current = gmp->current->prev;
+ if(PlaceCell(gmp, gmp->current, &row, &col)){
+ PaintBrowser(gmp, 1, &crow, &ccol);
+ }
+ else{
+ PaintCell(row, col, gmp->cpf, gmp->current, 1);
+ crow = row;
+ ccol = col;
+ }
+ break;
+
+ case (CTRL|'A'): /* beginning of line */
+ tp = gmp->current;
+ i = col;
+ while(i > 0){
+ i -= gmp->cpf;
+ if(tp->prev != NULL)
+ tp = tp->prev;
+ }
+ PlaceCell(gmp, gmp->current, &row, &col);
+ PaintCell(row, col, gmp->cpf, gmp->current, 0);
+ gmp->current = tp;
+ if(PlaceCell(gmp, tp, &row, &col)){
+ PaintBrowser(gmp, 1, &crow, &ccol);
+ }
+ else{
+ PaintCell(row, col, gmp->cpf, gmp->current, 1);
+ crow = row;
+ ccol = col;
+ }
+ break;
+
+ case (CTRL|'E'): /* end of line */
+ tp = gmp->current;
+ i = col + gmp->cpf;
+ while(i+gmp->cpf <= gmp->cpf * gmp->fpl){
+ i += gmp->cpf;
+ if(tp->next != NULL)
+ tp = tp->next;
+ }
+
+ PlaceCell(gmp, gmp->current, &row, &col);
+ PaintCell(row, col, gmp->cpf, gmp->current, 0);
+ gmp->current = tp;
+ if(PlaceCell(gmp, tp, &row, &col)){
+ PaintBrowser(gmp, 1, &crow, &ccol);
+ }
+ else{
+ PaintCell(row, col, gmp->cpf, gmp->current, 1);
+ crow = row;
+ ccol = col;
+ }
+ break;
+
+ case (CTRL|'V'): /* page forward */
+ case ' ':
+ case KEY_PGDN :
+ case KEY_END :
+ tp = gmp->top;
+ i = term.t_nrow - term.t_mrow - 2;
+
+ while(i-- && tp->next != NULL){
+ j = 0;
+ while(++j <= gmp->fpl && tp->next != NULL)
+ tp = tp->next;
+ }
+
+ if(tp == NULL)
+ continue;
+
+ PlaceCell(gmp, gmp->current, &row, &col);
+ PaintCell(row, col, gmp->cpf, gmp->current, 0);
+ gmp->current = tp;
+ if(PlaceCell(gmp, tp, &row, &col)){
+ PaintBrowser(gmp, 1, &crow, &ccol);
+ }
+ else{
+ PaintCell(row, col, gmp->cpf, gmp->current, 1);
+ crow = row;
+ ccol = col;
+ }
+ break;
+
+ case '-' :
+ case (CTRL|'Y'): /* page backward */
+ case KEY_PGUP :
+ case KEY_HOME :
+ tp = gmp->top;
+ i = term.t_nrow - term.t_mrow - 4;
+ while(i-- && tp != NULL){
+ j = gmp->fpl;
+ while(j-- && tp != NULL)
+ tp = tp->prev;
+ }
+
+ if(tp || (gmp->current != gmp->top)){ /* clear old hilite */
+ PlaceCell(gmp, gmp->current, &row, &col);
+ PaintCell(row, col, gmp->cpf, gmp->current, 0);
+ }
+
+ if(tp) /* new page ! */
+ gmp->current = tp;
+ else if(gmp->current != gmp->top) /* goto top of page */
+ gmp->current = gmp->top;
+ else /* do nothing */
+ continue;
+
+ if(PlaceCell(gmp, gmp->current, &row, &col)){
+ PaintBrowser(gmp, 1, &crow, &ccol);
+ }
+ else{
+ PaintCell(row, col, gmp->cpf, gmp->current, 1);
+ crow = row;
+ ccol = col;
+ }
+
+ break;
+
+ case KEY_DOWN :
+ case (CTRL|'N'): /* next */
+ tp = gmp->current;
+ i = gmp->fpl;
+ while(i--){
+ if(tp->next == NULL){
+ (*term.t_beep)();
+ break;
+ }
+ else
+ tp = tp->next;
+ }
+ if(i != -1) /* can't go down */
+ break;
+
+ PlaceCell(gmp, gmp->current, &row, &col);
+ PaintCell(row, col, gmp->cpf, gmp->current, 0);
+ gmp->current = tp;
+ if(PlaceCell(gmp, tp, &row, &col)){
+ PaintBrowser(gmp, 1, &crow, &ccol);
+ }
+ else{
+ PaintCell(row, col, gmp->cpf, gmp->current, 1);
+ crow = row;
+ ccol = col;
+ }
+ break;
+
+ case KEY_UP :
+ case (CTRL|'P'): /* previous */
+ tp = gmp->current;
+ i = gmp->fpl;
+ while(i-- && tp)
+ tp = tp->prev;
+
+ if(tp == NULL)
+ break;
+
+ PlaceCell(gmp, gmp->current, &row, &col);
+ PaintCell(row, col, gmp->cpf, gmp->current, 0);
+ gmp->current = tp;
+ if(PlaceCell(gmp, tp, &row, &col)){
+ PaintBrowser(gmp, 1, &crow, &ccol);
+ }
+ else{
+ PaintCell(row, col, gmp->cpf, gmp->current, 1);
+ crow = row;
+ ccol = col;
+ }
+ break;
+
+#ifdef MOUSE
+ case KEY_MOUSE:
+ mouse_get_last (NULL, &mousep);
+ if (mousep.doubleclick) {
+ goto Selected;
+ }
+ else {
+ row = mousep.row -= 2; /* Adjust for header*/
+ col = mousep.col;
+ i = row * gmp->fpl + (col / gmp->cpf); /* Count from top */
+ tp = gmp->top; /* start at top. */
+ for (; i > 0 && tp != NULL; --i) /* Count cells. */
+ tp = tp->next;
+ if (tp != NULL) { /* Valid cell? */
+ PlaceCell(gmp, gmp->current, &row, &col);
+ PaintCell(row, col, gmp->cpf, gmp->current, 0);
+ gmp->current = tp;
+ if(PlaceCell(gmp, tp, &row, &col)){
+ PaintBrowser(gmp, 1, &crow, &ccol);
+ }
+ else{
+ PaintCell(row, col, gmp->cpf, gmp->current, 1);
+ crow = row;
+ ccol = col;
+ }
+ }
+ }
+ break;
+#endif
+
+ case 'e': /* exit or edit */
+ case 'E':
+ if(gmode&MDBRONLY){ /* run "pico" */
+ snprintf(child, sizeof(child), "%s%c%s", gmp->dname, C_FILESEP,
+ gmp->current->fname);
+ /* make sure selected isn't a directory or executable */
+ if(!LikelyASCII(child)){
+ emlwrite(_("Can't edit non-text file. Try Launch."), NULL);
+ break;
+ }
+
+ if((envp = (char *) getenv("EDITOR")) != NULL)
+ snprintf(tmp, sizeof(tmp), "%s \'%s\'", envp, child);
+ else
+ snprintf(tmp, sizeof(tmp), "pico%s%s%s \'%s\'",
+ (gmode & MDFKEY) ? " -f" : "",
+ (gmode & MDSHOCUR) ? " -g" : "",
+ (gmode & MDMOUSE) ? " -m" : "",
+ child);
+
+ BrowserRunChild(tmp, gmp->dname); /* spawn pico */
+ PaintBrowser(gmp, 0, &crow, &ccol); /* redraw browser */
+ }
+ else{
+ zotmaster(&gmp);
+ return(0);
+ }
+
+ break;
+
+ case 'q': /* user exits wrong */
+ case 'Q':
+ if(gmode&MDBRONLY){
+ zotmaster(&gmp);
+ return(0);
+ }
+
+ unknown_command(c);
+ break;
+
+ case 'x': /* toggle selection */
+ case 'X':
+ if(!(gmp->flags & FB_LMODE)){
+ if(gmp->flags & FB_LMODEPOS)
+ emlwrite(_("\007Type L command to use ListMode"), NULL);
+ else
+ unknown_command(c);
+
+ break;
+ }
+
+ if(gmp->current->mode == FIODIR){
+ emlwrite(_("\007Can't Set directories"), NULL);
+ break;
+ }
+
+ if(fcell_is_selected(gmp->current, gmp))
+ del_cell_from_lmlist(gmp->current, gmp);
+ else
+ add_cell_to_lmlist(gmp->current, gmp);
+
+ PlaceCell(gmp, gmp->current, &row, &col);
+ PaintCell(row, col, gmp->cpf, gmp->current, 1);
+ break;
+
+ case 'l': /* run Command */
+ case 'L': /* or ListMode */
+ if(gmp->flags & FB_LMODEPOS){
+ if(gmp->flags & FB_LMODE){
+ /*
+ * Unless we make it so you can get out of ListMode
+ * once you're in ListMode, this must be an error.
+ */
+ emlwrite(_("\007Already in ListMode"), NULL);
+ break;
+ }
+ else{
+ gmp->flags |= FB_LMODE;
+ PaintBrowser(gmp, 0, &crow, &ccol);
+ }
+
+ break;
+ }
+
+ if(!(gmode&MDBRONLY)){
+ unknown_command(c);
+ break;
+ }
+
+/* add subcommands to invoke pico and insert selected filename */
+/* perhaps: add subcmd to scroll command history */
+
+ tmp[0] = '\0';
+ i = 0;
+ snprintf(child, sizeof(child), "%s%c%s", gmp->dname, C_FILESEP,
+ gmp->current->fname);
+ while(!i){
+ static EXTRAKEYS opts[] = {
+ {"^X", N_("Add Name"), CTRL|'X', KS_NONE},
+ {NULL, NULL, 0, KS_NONE},
+ };
+
+ status = mlreply_utf8(_("Command to execute: "),
+ tmp, NLINE, QNORML, opts);
+ switch(status){
+ case HELPCH:
+ emlwrite(_("\007No help yet!"), NULL);
+/* remove break and sleep after help text is installed */
+ sleep(3);
+ break;
+ case (CTRL|'X'):
+ strncat(tmp, child, sizeof(tmp)-strlen(tmp)-1);
+ tmp[sizeof(tmp)-1] = '\0';
+ break;
+ case (CTRL|'L'):
+ PaintBrowser(gmp, 0, &crow, &ccol);
+ break;
+ case ABORT:
+ emlwrite(_("Command cancelled"), NULL);
+ i++;
+ break;
+ case FALSE:
+ case TRUE:
+ i++;
+
+ if(tmp[0] == '\0'){
+ emlwrite(_("No command specified"), NULL);
+ break;
+ }
+
+ BrowserRunChild(tmp, gmp->dname);
+ PaintBrowser(gmp, 0, &crow, &ccol);
+ break;
+ default:
+ break;
+ }
+ }
+
+ BrowserKeys();
+ break;
+
+ case 'd': /* delete */
+ case 'D':
+ if(gmp->current->mode == FIODIR){
+/* BUG: if dir is empty it should be deleted */
+ emlwrite(_("\007Can't delete a directory"), NULL);
+ break;
+ }
+
+ if(gmode&MDSCUR){ /* not allowed! */
+ emlwrite(_("Delete not allowed in restricted mode"),NULL);
+ break;
+ }
+
+ snprintf(child, sizeof(child), "%s%c%s", gmp->dname, C_FILESEP,
+ gmp->current->fname);
+
+ i = 0;
+ while(i++ < 2){ /* verify twice!! */
+ if(i == 1){
+ if(fexist(child, "w", (off_t *)NULL) != FIOSUC){
+ strncpy(tmp, _("File is write protected! OVERRIDE"), sizeof(tmp));
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+ else
+ /* TRANSLATORS: This is a question, Delete file <filename> */
+ snprintf(tmp, sizeof(tmp), _("Delete file \"%.*s\""), NLINE - 20, child);
+ }
+ else{
+ strncpy(tmp, _("File CANNOT be UNdeleted! Really delete"), sizeof(tmp));
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+
+ if((status = mlyesno_utf8(tmp, FALSE)) != TRUE){
+ emlwrite((status == ABORT)
+ ? _("Delete Cancelled")
+ : _("File Not Deleted"),
+ NULL);
+ break;
+ }
+ }
+
+ if(status == TRUE){
+ if(our_unlink(child) < 0){
+ eml.s = errstr(errno);
+ emlwrite(_("Delete Failed: %s"), &eml);
+ }
+ else{ /* fix up pointers and redraw */
+ tp = gmp->current;
+ if(tp->next){
+ gmp->current = tp->next;
+ if((tp->next->prev = tp->prev) != NULL)
+ tp->prev->next = tp->next;
+ }
+ else if(tp->prev) {
+ gmp->current = tp->prev;
+ if((tp->prev->next = tp->next) != NULL)
+ tp->next->prev = tp->prev;
+ }
+
+ if(tp == gmp->head)
+ gmp->head = tp->next;
+
+ tp->fname = NULL;
+ tp->next = tp->prev = NULL;
+ if(tp != gmp->current)
+ free((char *) tp);
+
+ if((tp = FindCell(gmp, gmp->current->fname)) != NULL){
+ gmp->current = tp;
+ PlaceCell(gmp, gmp->current, &row, &col);
+ }
+
+ PaintBrowser(gmp, 1, &crow, &ccol);
+ mlerase();
+ }
+ }
+
+ BrowserKeys();
+ break;
+
+ case '?': /* HELP! */
+ case (CTRL|'G'):
+ if(term.t_mrow == 0){
+ if(km_popped == 0){
+ km_popped = 2;
+ break;
+ }
+ }
+
+ if(Pmaster){
+ VARS_TO_SAVE *saved_state;
+
+ saved_state = save_pico_state();
+ (*Pmaster->helper)(Pmaster->browse_help,
+ _("Help for Browsing"), 1);
+ if(saved_state){
+ restore_pico_state(saved_state);
+ free_pico_state(saved_state);
+ }
+ }
+ else if(gmode&MDBRONLY)
+ pico_help(sa_BrowseHelpText, _("Browser Help"), 1);
+ else
+ pico_help(BrowseHelpText, _("Help for Browsing"), 1);
+ /* fall thru to repaint everything */
+
+ case (CTRL|'L'):
+ PaintBrowser(gmp, 0, &crow, &ccol);
+ break;
+
+ case 'g': /* jump to a directory */
+ case 'G':
+
+ if(!(gmode&MDGOTO))
+ goto Default;
+
+ i = 0;
+ child[0] = '\0';
+
+ while(!i){
+
+ /* TRANSLATORS: A prompt asking for a directory to
+ move into */
+ status = mlreply_utf8(_("Directory to go to: "), child, NLINE, QNORML,
+ NULL);
+
+ switch(status){
+ case HELPCH:
+ emlwrite(_("\007No help yet!"), NULL);
+ /* remove break and sleep after help text is installed */
+ sleep(3);
+ break;
+ case (CTRL|'L'):
+ PaintBrowser(gmp, 0, &crow, &ccol);
+ break;
+ case ABORT:
+ emlwrite(_("Goto cancelled"), NULL);
+ i++;
+ break;
+ case FALSE:
+ case TRUE:
+ i++;
+
+ if(*child == '\0'){
+ strncpy(child, gethomedir(NULL), sizeof(child));
+ child[sizeof(child)-1] = '\0';
+ }
+
+ if(!compresspath(gmp->dname, child, sizeof(child))){
+ eml.s = child;
+ emlwrite(_("Invalid Directory: %s"), &eml);
+ break;
+ }
+
+ if((gmode&MDSCUR) && homeless(child)){
+ emlwrite(_("Restricted mode browsing limited to home directory"),NULL);
+ break;
+ }
+
+ if((gmode&MDTREE) && !in_oper_tree(child)){
+ eml.s = opertree;
+ emlwrite(_("\007 Can't go outside of %s in restricted mode"),
+ &eml);
+ break;
+ }
+
+ if(isdir(child, (long *) NULL, NULL)){
+ if((mp = getfcells(child, fb_flags)) == NULL){
+ /* getfcells should explain what happened */
+ i++;
+ break;
+ }
+
+ zotmaster(&gmp);
+ gmp = mp;
+ PaintBrowser(gmp, 0, &crow, &ccol);
+ }
+ else{
+ eml.s = child;
+ emlwrite(_("\007Not a directory: \"%s\""), &eml);
+ }
+
+ break;
+ default:
+ break;
+ }
+ }
+ BrowserKeys();
+ break;
+
+ case 'a': /* Add */
+ case 'A':
+ if(gmode&MDSCUR){ /* not allowed! */
+ emlwrite(_("Add not allowed in restricted mode"),NULL);
+ break;
+ }
+
+ i = 0;
+ child[0] = '\0';
+ /* pass in default filename */
+ if(fn && *fn){
+ strncpy(child, fn, sizeof(child) - 1);
+ child[sizeof(child) - 1] = '\0';
+ }
+
+ while(!i){
+
+ switch(status=mlreply_utf8(_("Name of file to add: "), child, NLINE,
+ QFFILE, NULL)){
+ case HELPCH:
+ emlwrite(_("\007No help yet!"), NULL);
+/* remove break and sleep after help text is installed */
+ sleep(3);
+ break;
+ case (CTRL|'L'):
+ PaintBrowser(gmp, 0, &crow, &ccol);
+ break;
+ case ABORT:
+ emlwrite(_("Add File Cancelled"), NULL);
+ i++;
+ break;
+ case FALSE:
+ /*
+ * Support default filename. A return of 'FALSE' means
+ * 'No change in filename' which is fine if we have
+ * provided a default.
+ * John Berthels <john.berthels@nexor.co.uk>
+ */
+ /* FALLTHROUGH */
+ case TRUE:
+ i++;
+
+ if(child[0] == '\0'){
+ emlwrite(_("No file named. Add Cancelled."), NULL);
+ break;
+ }
+
+ if(!compresspath(gmp->dname, child, sizeof(child))){
+ emlwrite(_("Too many ..'s in name"), NULL);
+ break;
+ }
+
+ if((gmode&MDTREE) && !in_oper_tree(child)){
+ eml.s = opertree;
+ emlwrite(_("\007Restricted mode allows Add in %s only"),
+ &eml);
+ break;
+ }
+
+ if((status = fexist(child, "w", (off_t *)NULL)) == FIOSUC){
+ snprintf(tmp, sizeof(tmp), _("File \"%.*s\" already exists!"),
+ NLINE - 20, child);
+ emlwrite(tmp, NULL);
+ break;
+ }
+ else if(status != FIOFNF){
+ fioperr(status, child);
+ break;
+ }
+
+ if(ffwopen(child, FALSE) != FIOSUC){
+ /* ffwopen should've complained */
+ break;
+ }
+ else{ /* highlight new file */
+ ffclose();
+ eml.s = child;
+ emlwrite(_("Added File \"%s\""), &eml);
+
+ if((p = strrchr(child, C_FILESEP)) == NULL){
+ emlwrite(_("Problems refiguring browser"), NULL);
+ break;
+ }
+
+ *p = '\0';
+ if(p != child){
+ strncpy(tmp, child, sizeof(tmp));
+ tmp[sizeof(tmp)-1] = '\0';
+ j = 0;
+ while((child[j++] = *++p) != '\0')
+ ;
+ }
+ else{
+ strncpy(tmp, S_FILESEP, sizeof(tmp));
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+
+ /*
+ * new file in same dir? if so, refigure files
+ * and redraw...
+ */
+ if(!strcmp(tmp, gmp->dname)){
+ if((mp = getfcells(gmp->dname, fb_flags)) == NULL)
+ /* getfcells should explain what happened */
+ break;
+
+ zotmaster(&gmp);
+ gmp = mp;
+ if((tp = FindCell(gmp, child)) != NULL){
+ gmp->current = tp;
+ PlaceCell(gmp, gmp->current, &row, &col);
+ }
+
+ PaintBrowser(gmp, 1, &crow, &ccol);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ BrowserKeys();
+ break;
+
+ case 'c': /* copy */
+ case 'C':
+ if(gmp->current->mode == FIODIR){
+ emlwrite(_("\007Can't copy a directory"), NULL);
+ break;
+ }
+
+ if(gmode&MDSCUR){ /* not allowed! */
+ emlwrite(_("Copy not allowed in restricted mode"),NULL);
+ break;
+ }
+
+ i = 0;
+ child[0] = '\0';
+
+ while(!i){
+
+ switch(status=mlreply_utf8(_("Name of new copy: "), child, NLINE,
+ QFFILE, NULL)){
+ case HELPCH:
+ emlwrite(_("\007No help yet!"), NULL);
+/* remove break and sleep after help text is installed */
+ sleep(3);
+ break;
+ case (CTRL|'L'):
+ PaintBrowser(gmp, 0, &crow, &ccol);
+ break;
+ case ABORT:
+ emlwrite(_("Make Copy Cancelled"), NULL);
+ i++;
+ break;
+ case FALSE:
+ i++;
+ mlerase();
+ break;
+ case TRUE:
+ i++;
+
+ if(child[0] == '\0'){
+ emlwrite(_("No destination, file not copied"), NULL);
+ break;
+ }
+
+ if(!strcmp(gmp->current->fname, child)){
+ emlwrite(_("\007Can't copy file on to itself!"), NULL);
+ break;
+ }
+
+ if(!compresspath(gmp->dname, child, sizeof(child))){
+ emlwrite(_("Too many ..'s in name"), NULL);
+ break;
+ }
+
+ if((gmode&MDTREE) && !in_oper_tree(child)){
+ eml.s = opertree;
+ emlwrite(_("\007Restricted mode allows Copy in %s only"),
+ &eml);
+ break;
+ }
+
+ if((status = fexist(child, "w", (off_t *)NULL)) == FIOSUC){
+ /* TRANSLATORS: A question: File <filename> exists! Replace? */
+ snprintf(tmp, sizeof(tmp), _("File \"%.*s\" exists! OVERWRITE"),
+ NLINE - 20, child);
+ if((status = mlyesno_utf8(tmp, 0)) != TRUE){
+ emlwrite((status == ABORT)
+ ? _("Make copy cancelled")
+ : _("File Not Renamed"),
+ NULL);
+ break;
+ }
+ }
+ else if(status != FIOFNF){
+ fioperr(status, child);
+ break;
+ }
+
+ snprintf(tmp, sizeof(tmp), "%s%c%s", gmp->dname, C_FILESEP,
+ gmp->current->fname);
+
+ if(copy(tmp, child) < 0){
+ /* copy() will report any error messages */
+ break;
+ }
+ else{ /* highlight new file */
+ eml.s = child;
+ emlwrite(_("File copied to %s"), &eml);
+
+ if((p = strrchr(child, C_FILESEP)) == NULL){
+ emlwrite(_("Problems refiguring browser"), NULL);
+ break;
+ }
+
+ *p = '\0';
+ strncpy(tmp, (p == child) ? S_FILESEP: child, sizeof(tmp));
+ tmp[sizeof(tmp)-1] = '\0';
+
+ /*
+ * new file in same dir? if so, refigure files
+ * and redraw...
+ */
+ if(!strcmp(tmp, gmp->dname)){
+ strncpy(child, gmp->current->fname, sizeof(child));
+ child[sizeof(child)-1] = '\0';
+ if((mp = getfcells(gmp->dname, fb_flags)) == NULL)
+ /* getfcells should explain what happened */
+ break;
+
+ zotmaster(&gmp);
+ gmp = mp;
+ if((tp = FindCell(gmp, child)) != NULL){
+ gmp->current = tp;
+ PlaceCell(gmp, gmp->current, &row, &col);
+ }
+
+ PaintBrowser(gmp, 1, &crow, &ccol);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ BrowserKeys();
+ break;
+
+ case 'r': /* rename */
+ case 'R':
+ i = 0;
+
+ if(!strcmp(gmp->current->fname, "..")){
+ emlwrite(_("\007Can't rename \"..\""), NULL);
+ break;
+ }
+
+ if(gmode&MDSCUR){ /* not allowed! */
+ emlwrite(_("Rename not allowed in restricted mode"),NULL);
+ break;
+ }
+
+ strncpy(child, gmp->current->fname, sizeof(child));
+ child[sizeof(child)-1] = '\0';
+
+ while(!i){
+
+ switch(status=mlreply_utf8(_("Rename file to: "), child, NLINE, QFFILE,
+ NULL)){
+ case HELPCH:
+ emlwrite(_("\007No help yet!"), NULL);
+/* remove break and sleep after help text is installed */
+ sleep(3);
+ break;
+ case (CTRL|'L'):
+ PaintBrowser(gmp, 0, &crow, &ccol);
+ break;
+ case ABORT:
+ emlwrite(_("Rename cancelled"), NULL);
+ i++;
+ break;
+ case FALSE:
+ case TRUE:
+ i++;
+
+ if(child[0] == '\0' || status == FALSE){
+ mlerase();
+ break;
+ }
+
+ if(!compresspath(gmp->dname, child, sizeof(child))){
+ emlwrite(_("Too many ..'s in name"), NULL);
+ break;
+ }
+
+ if((gmode&MDTREE) && !in_oper_tree(child)){
+ eml.s = opertree;
+ emlwrite(_("\007Restricted mode allows Rename in %s only"),
+ &eml);
+ break;
+ }
+
+ status = fexist(child, "w", (off_t *)NULL);
+ if(status == FIOSUC || status == FIOFNF){
+ if(status == FIOSUC){
+ snprintf(tmp, sizeof(tmp), _("File \"%.*s\" exists! OVERWRITE"),
+ NLINE - 20, child);
+
+ if((status = mlyesno_utf8(tmp, FALSE)) != TRUE){
+ emlwrite((status == ABORT)
+ ? _("Rename cancelled")
+ : _("Not Renamed"),
+ NULL);
+ break;
+ }
+ }
+
+ snprintf(tmp, sizeof(tmp), "%s%c%s", gmp->dname, C_FILESEP,
+ gmp->current->fname);
+
+ if(our_rename(tmp, child) < 0){
+ eml.s = errstr(errno);
+ emlwrite(_("Rename Failed: %s"), &eml);
+ }
+ else{
+ if((p = strrchr(child, C_FILESEP)) == NULL){
+ emlwrite(_("Problems refiguring browser"), NULL);
+ break;
+ }
+
+ *p = '\0';
+ strncpy(tmp, (p == child) ? S_FILESEP: child, sizeof(tmp));
+ tmp[sizeof(tmp)-1] = '\0';
+
+ if((mp = getfcells(tmp, fb_flags)) == NULL)
+ /* getfcells should explain what happened */
+ break;
+
+ zotmaster(&gmp);
+ gmp = mp;
+
+ if((tp = FindCell(gmp, ++p)) != NULL){
+ gmp->current = tp;
+ PlaceCell(gmp, gmp->current, &row, &col);
+ }
+
+ PaintBrowser(gmp, 1, &crow, &ccol);
+ mlerase();
+ }
+ }
+ else{
+ fioperr(status, child);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ BrowserKeys();
+ break;
+
+ case 'v': /* stand-alone */
+ case 'V': /* browser "view" */
+ case 's': /* user "select" */
+ case 'S':
+ case (CTRL|'M'):
+ Selected:
+
+ if(((new_c == 'S' || new_c == 's') && (gmode&MDBRONLY))
+ || ((new_c == 'V' || new_c == 'v') && !(gmode&MDBRONLY)))
+ goto Default;
+
+ if(gmp->current->mode == FIODIR){
+ *child = '\0';
+ strncpy(tmp, gmp->dname, sizeof(tmp));
+ tmp[sizeof(tmp)-1] = '\0';
+ p = gmp->current->fname;
+ if(p[0] == '.' && p[1] == '.' && p[2] == '\0'){
+ if((p=strrchr(tmp, C_FILESEP)) != NULL){
+ *p = '\0';
+
+ if((gmode&MDTREE) && !in_oper_tree(tmp)){
+ eml.s = PARENTDIR;
+ emlwrite(
+ _("\007Can't visit %s in restricted mode"),
+ &eml);
+ break;
+ }
+
+ strncpy(child, &p[1], sizeof(child));
+ child[sizeof(child)-1] = '\0';
+
+ if
+#if defined(DOS) || defined(OS2)
+ (p == tmp || (tmp[1] == ':' && tmp[2] == '\0'))
+#else
+ (p == tmp)
+#endif
+ { /* is it root? */
+#if defined(DOS) || defined(OS2)
+ if(*child){
+ strncat(tmp, S_FILESEP, sizeof(tmp)-strlen(tmp)-1);
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+#else
+ if(*child){
+ strncpy(tmp, S_FILESEP, sizeof(tmp));
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+#endif
+ else{
+ emlwrite(_("\007Can't move up a directory"),
+ NULL);
+ break;
+ }
+ }
+ }
+ }
+ else if((fb_flags&FB_SAVE) && p[0] == '.' && p[1] == '\0'){
+ if ((strlen(gmp->dname) < dirlen) &&
+ (strlen(gmp->current->fname) < fnlen)){
+ strncpy(dir, gmp->dname, dirlen);
+ dir[dirlen-1] = '\0';
+ }
+
+ zotmaster(&gmp);
+ return(0); /* just change the directory, still return no selection */
+ }
+ else{
+ if(tmp[1] != '\0'){ /* were in root? */
+ strncat(tmp, S_FILESEP, sizeof(tmp)-strlen(tmp)-1);
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+
+ strncat(tmp, gmp->current->fname, sizeof(tmp)-strlen(tmp));
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+
+ if((mp = getfcells(tmp, fb_flags)) == NULL)
+ /* getfcells should explain what happened */
+ break;
+
+ if(gmp->flags & FB_LMODE){
+ mp->flags |= FB_LMODE;
+ mp->lm = gmp->lm;
+ gmp->lm = NULL;
+ }
+
+ zotmaster(&gmp);
+ gmp = mp;
+ tp = NULL;
+
+ if(*child){
+ if((tp = FindCell(gmp, child)) != NULL){
+ gmp->current = tp;
+ PlaceCell(gmp, gmp->current, &row, &col);
+ }
+ else{
+ eml.s = child;
+ emlwrite(_("\007Problem finding dir \"%s\""), &eml);
+ }
+ }
+
+ PaintBrowser(gmp, 0, &crow, &ccol);
+ if(!*child){
+ char b[100];
+
+ snprintf(b, sizeof(b), "Select/View \" .. %s %s\" to return to previous directory.", PARENTDIR, DIRWORD);
+ emlwrite(b, NULL);
+ }
+
+ break;
+ }
+ else if(gmode&MDBRONLY){
+ snprintf(child, sizeof(child), "%s%c%s", gmp->dname, C_FILESEP,
+ gmp->current->fname);
+
+ if(LikelyASCII(child)){
+ snprintf(tmp, sizeof(tmp), "%s \'%s\'",
+ (envp = (char *) getenv("PAGER"))
+ ? envp : BROWSER_PAGER, child);
+ BrowserRunChild(tmp, gmp->dname);
+ PaintBrowser(gmp, 0, &crow, &ccol);
+ }
+
+ break;
+ }
+ else{ /* just return */
+ if(gmp->flags & FB_LMODEPOS){
+
+ if(!lmreturn){
+ emlwrite("Programming error, called FileBrowse with LMODEPOS but no lmreturn", NULL);
+ zotmaster(&gmp);
+ return(-1);
+ }
+
+ /* user actually used ListMode, the list is finished */
+ if(gmp->flags & FB_LMODE){
+ if(!gmp->lm){
+ (*term.t_beep)();
+ emlwrite(_("No files are selected, use \"X\" to mark files for selection"), NULL);
+ break;
+ }
+
+ *lmreturn = gmp->lm;
+ gmp->lm = NULL;
+ }
+ else{ /* construct an lmreturn for user */
+ LMLIST *new;
+ size_t flen, dlen;
+
+ if((new=(LMLIST *)malloc(sizeof(*new))) == NULL
+ || (new->fname=malloc(gmp->current->fname ? (flen=strlen(gmp->current->fname))+1 : 1)) == NULL
+ || (new->dir=malloc((dlen=strlen(gmp->dname))+1)) == NULL){
+ emlwrite("\007Can't malloc space for filename", NULL);
+ return(-1);
+ }
+
+ strncpy(new->fname,
+ gmp->current->fname ? gmp->current->fname : "", flen);
+ new->fname[flen] = '\0';
+ strncpy(new->dir, gmp->dname, dlen);
+ new->dir[dlen] = '\0';
+ strncpy(new->size, gmp->current->size, sizeof(new->size));
+ new->size[sizeof(new->size)-1] = '\0';
+ new->next = NULL;
+ *lmreturn = new;
+ }
+
+ zotmaster(&gmp);
+ return(1);
+ }
+
+ if ((strlen(gmp->dname) < dirlen) &&
+ (strlen(gmp->current->fname) < fnlen)){
+ strncpy(dir, gmp->dname, dirlen);
+ dir[dirlen-1] = '\0';
+ strncpy(fn, gmp->current->fname, fnlen);
+ fn[fnlen-1] = '\0';
+ }
+ else {
+ zotmaster(&gmp);
+ return(-1);
+ }
+ if(sz != NULL){ /* size uninteresting */
+ strncpy(sz, gmp->current->size, szlen);
+ sz[szlen-1] = '\0';
+ }
+
+ zotmaster (&gmp);
+ return (1);
+ }
+ break;
+
+ case 'w': /* Where is */
+ case 'W':
+ case (CTRL|'W'):
+ i = 0;
+
+ while(!i){
+
+ switch(readpattern(_("File name to find"), FALSE)){
+ case HELPCH:
+ emlwrite(_("\007No help yet!"), NULL);
+/* remove break and sleep after help text is installed */
+ sleep(3);
+ break;
+ case (CTRL|'L'):
+ PaintBrowser(gmp, 0, &crow, &ccol);
+ break;
+ case (CTRL|'Y'): /* first first cell */
+ for(tp = gmp->top; tp->prev; tp = tp->prev)
+ ;
+
+ i++;
+ /* fall thru to repaint */
+ case (CTRL|'V'):
+ if(!i){
+ do{
+ tp = gmp->top;
+ if((i = term.t_nrow - term.t_mrow - 2) <= 0)
+ break;
+
+ while(i-- && tp->next){
+ j = 0;
+ while(++j <= gmp->fpl && tp->next)
+ tp = tp->next;
+ }
+
+ if(i < 0)
+ gmp->top = tp;
+ }
+ while(tp->next);
+
+ emlwrite(_("Searched to end of directory"), NULL);
+ }
+ else
+ emlwrite(_("Searched to start of directory"), NULL);
+
+ if(tp){
+ PlaceCell(gmp, gmp->current, &row, &col);
+ PaintCell(row, col, gmp->cpf, gmp->current, 0);
+ gmp->current = tp;
+ if(PlaceCell(gmp, gmp->current, &row, &col)){
+ PaintBrowser(gmp, 1, &crow, &ccol);
+ }
+ else{
+ PaintCell(row, col, gmp->cpf, gmp->current, 1);
+ crow = row;
+ ccol = col;
+ }
+ }
+
+ i++; /* make sure we jump out */
+ break;
+ case ABORT:
+ emlwrite(_("Whereis cancelled"), NULL);
+ i++;
+ break;
+ case FALSE:
+ mlerase();
+ i++;
+ break;
+ case TRUE:
+ {
+ char *utf8 = NULL;
+
+ if(pat && pat[0])
+ utf8 = ucs4_to_utf8_cpystr(pat);
+
+ if(utf8 && (tp = FindCell(gmp, utf8)) != NULL){
+ PlaceCell(gmp, gmp->current, &row, &col);
+ PaintCell(row, col, gmp->cpf, gmp->current, 0);
+ gmp->current = tp;
+
+ if(PlaceCell(gmp, tp, &row, &col)){ /* top changed */
+ PaintBrowser(gmp, 1, &crow, &ccol);
+ }
+ else{
+ PaintCell(row, col, gmp->cpf, gmp->current, 1);
+ crow = row;
+ ccol = col;
+ }
+ mlerase();
+ }
+ else{
+ eml.s = utf8;
+ emlwrite(_("\"%s\" not found"), &eml);
+ }
+
+ if(utf8)
+ fs_give((void **) &utf8);
+ }
+
+ i++;
+ break;
+ default:
+ break;
+ }
+ }
+
+ BrowserKeys();
+ break;
+
+ case (CTRL|'\\') :
+#if defined MOUSE && !defined(_WINDOWS)
+ toggle_xterm_mouse(0,1);
+#else
+ unknown_command(c);
+#endif
+ break;
+
+ case (CTRL|'Z'):
+ if(gmode&MDSSPD){
+ bktoshell(0, 1);
+ PaintBrowser(gmp, 0, &crow, &ccol);
+ break;
+ } /* fall thru with error! */
+
+ default: /* what? */
+ Default:
+ unknown_command(c);
+
+ case NODATA: /* no op */
+ break;
+ }
+ }
+}
+
+
+/*
+ * getfcells - make a master browser struct and fill it in
+ * return NULL if there's a problem.
+ */
+struct bmaster *
+getfcells(char *dname, int fb_flags)
+{
+ int i, /* various return codes */
+ flength,
+ nentries = 0; /* number of dir ents */
+ off_t attsz;
+ char *np, /* names of files in dir */
+ *dcp, /* to add file to path */
+ *tmpstr,
+ **filtnames, /* array filtered names */
+ errbuf[NLINE];
+ struct fcell *ncp, /* new cell pointer */
+ *tcp; /* trailing cell ptr */
+ struct bmaster *mp;
+ EML eml;
+
+ errbuf[0] = '\0';
+ if((mp=(struct bmaster *)malloc(sizeof(struct bmaster))) == NULL){
+ emlwrite("\007Can't malloc space for master filename cell", NULL);
+ return(NULL);
+ }
+
+ memset(mp, 0, sizeof(*mp));
+
+ if(dname[0] == '.' && dname[1] == '\0'){ /* remember this dir */
+ if(!getcwd(mp->dname, 256))
+ mp->dname[0] = '\0';
+ }
+ else if(dname[0] == '.' && dname[1] == '.' && dname[2] == '\0'){
+ if(!getcwd(mp->dname, 256))
+ mp->dname[0] = '\0';
+ else{
+ if((np = (char *)strrchr(mp->dname, C_FILESEP)) != NULL)
+ if(np != mp->dname)
+ *np = '\0';
+ }
+ }
+ else{
+ strncpy(mp->dname, dname, sizeof(mp->dname));
+ mp->dname[sizeof(mp->dname)-1] = '\0';
+ }
+
+ mp->head = mp->top = NULL;
+ mp->cpf = mp->fpl = 0;
+ mp->longest = 5; /* .. must be labeled! */
+ mp->flags = fb_flags;
+
+ eml.s = mp->dname;
+ emlwrite("Building file list of %s...", &eml);
+
+ if((mp->names = getfnames(mp->dname, NULL, &nentries, errbuf, sizeof(errbuf))) == NULL){
+ free((char *) mp);
+ if(*errbuf)
+ emlwrite(errbuf, NULL);
+
+ return(NULL);
+ }
+
+ /*
+ * this is the fun part. build an array of pointers to the fnames we're
+ * interested in (i.e., do any filtering), then pass that off to be
+ * sorted before building list of cells...
+ *
+ * right now default filtering on ".*" except "..", but this could
+ * easily be made a user option later on...
+ */
+ if((filtnames=(char **)malloc((nentries+1) * sizeof(char *))) == NULL){
+ emlwrite("\007Can't malloc space for name array", NULL);
+ zotmaster(&mp);
+ return(NULL);
+ }
+
+ i = 0; /* index of filt'd array */
+ np = mp->names;
+ while(nentries--){
+ int ii;
+ int width;
+
+ /*
+ * Filter dot files? Always filter ".", never filter "..",
+ * and sometimes fitler ".*"...
+ */
+ if(*np == '.' && (!(*(np+1) == '.' && *(np+2) == '\0')
+ && !(*(np+1) == '\0' && (fb_flags&FB_SAVE)))
+ && (*(np+1) == '\0' || !(gmode & MDDOTSOK))){
+ np += strlen(np) + 1;
+ continue;
+ }
+
+ filtnames[i++] = np;
+
+ ii = (int) strlen(np);
+ if((width = (int) utf8_width(np)) > mp->longest)
+ mp->longest = width; /* remember longest */
+
+ np += ii + 1; /* advance name pointer */
+ }
+
+ nentries = i; /* new # of entries */
+
+ /*
+ * sort files case independently
+ */
+ qsort((qsort_t *)filtnames, (size_t)nentries, sizeof(char *), sstrcasecmp);
+
+ /*
+ * this is so we use absolute path names for stats.
+ * remember: be careful using dname as directory name, and fix mp->dname
+ * when we're done
+ */
+ dcp = (char *) strchr(mp->dname, '\0');
+ if(dcp == mp->dname || dcp[-1] != C_FILESEP){
+ dcp[0] = C_FILESEP;
+ dcp[1] = '\0';
+ }
+ else
+ dcp--;
+
+ i = 0;
+ while(nentries--){ /* stat filtered files */
+ /* get a new cell */
+ if((ncp=(struct fcell *)malloc(sizeof(struct fcell))) == NULL){
+ emlwrite("\007Can't malloc cells for browser!", NULL);
+ zotfcells(mp->head); /* clean up cells */
+ free((char *) filtnames);
+ free((char *) mp);
+ return(NULL); /* bummer. */
+ }
+
+ ncp->next = ncp->prev = NULL;
+
+ if(mp->head == NULL){ /* tie it onto the list */
+ mp->head = mp->top = mp->current = ncp;
+ }
+ else{
+ tcp->next = ncp;
+ ncp->prev = tcp;
+ }
+
+ tcp = ncp;
+
+ /* fill in the new cell */
+ ncp->fname = filtnames[i++];
+
+ /* fill in file's mode */
+ if ((flength = strlen(ncp->fname) + 1 + strlen(dname)) < sizeof(mp->dname)){
+ strncpy(&dcp[1], ncp->fname, sizeof(mp->dname)-(dcp+1-mp->dname)); /* use absolute path! */
+ mp->dname[sizeof(mp->dname)-1] = '\0';
+ tmpstr = mp->dname;
+ }
+ else{
+ if((tmpstr = (char *)malloc((flength+1)*sizeof(char))) == NULL){
+ emlwrite("\007Can't malloc cells for temp buffer!", NULL);
+ zotfcells(mp->head); /* clean up cells */
+ free((char *) filtnames);
+ free((char *) mp);
+ return(NULL); /* bummer. */
+ }
+
+ strncpy(tmpstr, dname, flength);
+ tmpstr[flength] = '\0';
+ tmpstr = strncat(tmpstr, S_FILESEP, flength+1-1-strlen(tmpstr));
+ tmpstr[flength] = '\0';
+ tmpstr = strncat(tmpstr, ncp->fname, flength+1-1-strlen(tmpstr));
+ tmpstr[flength] = '\0';
+ }
+
+ switch(fexist(tmpstr, "t", &attsz)){
+ case FIODIR :
+ ncp->mode = FIODIR;
+ snprintf(ncp->size, sizeof(ncp->size), "(%s%s%s)",
+ (ncp->fname[0] == '.' && ncp->fname[1] == '.'
+ && ncp->fname[2] == '\0') ? PARENTDIR :
+ ((fb_flags&FB_SAVE) && ncp->fname[0] == '.' && ncp->fname[1] == '\0'
+ ? SELECTWORD : ""),
+ (ncp->fname[0] == '.' && ncp->fname[1] == '.'
+ && ncp->fname[2] == '\0') ? " " :
+ ((fb_flags&FB_SAVE) && ncp->fname[0] == '.' && ncp->fname[1] == '\0'
+ ? " " : ""),
+ DIRWORD);
+ break;
+
+ case FIOSYM :
+ ncp->mode = FIOSYM;
+ strncpy(ncp->size, "--", sizeof(ncp->size));
+ ncp->size[sizeof(ncp->size)-1] = '\0';
+ break;
+
+ default :
+ ncp->mode = FIOSUC; /* regular file */
+ strncpy(ncp->size, prettysz(attsz), sizeof(ncp->size));
+ ncp->size[sizeof(ncp->size)-1] = '\0';
+ break;
+ }
+
+ if (flength >= NLINE)
+ free((char *) tmpstr);
+ }
+
+ dcp[(dcp == mp->dname) ? 1 : 0] = '\0'; /* remember to cap dname */
+ free((char *) filtnames); /* 'n blast filt'd array*/
+
+ percdircells(mp);
+ layoutcells(mp);
+ if(strlen(mp->dname) < sizeof(browse_dir)){
+ strncpy(browse_dir, mp->dname, sizeof(browse_dir));
+ browse_dir[sizeof(browse_dir)-1] = '\0';
+ }
+ else
+ browse_dir[0] = '\0';
+
+ return(mp);
+}
+
+
+int
+fcell_is_selected(struct fcell *cell, struct bmaster *mp)
+{
+ LMLIST *lm;
+
+ if(cell && cell->fname){
+ for(lm = mp ? mp->lm : NULL; lm; lm = lm->next){
+ /* directory has to match */
+ if(!((mp->dname[0] == '\0' && (!lm->dir || lm->dir[0] =='\0'))
+ || (mp->dname[0] != '\0' && lm->dir && lm->dir[0] !='\0'
+ && !strcmp(mp->dname, lm->dir))))
+ continue;
+
+ if(lm->fname && !strcmp(cell->fname, lm->fname))
+ return(1);
+ }
+ }
+
+ return(0);
+}
+
+
+/*
+ * Adds a new name to the head of the lmlist
+ */
+void
+add_cell_to_lmlist(struct fcell *cell, struct bmaster *mp)
+{
+ LMLIST *new;
+ size_t flen, dlen;
+
+ if(mp && cell && cell->fname && cell->fname[0]){
+ if((new=(LMLIST *)malloc(sizeof(*new))) == NULL ||
+ (new->fname=malloc(sizeof(char)*((flen=strlen(cell->fname))+1))) == NULL ||
+ (new->dir=malloc(sizeof(char)*((dlen=strlen(mp->dname))+1))) == NULL){
+ emlwrite("\007Can't malloc space for filename", NULL);
+ return;
+ }
+
+ strncpy(new->fname, cell->fname, flen);
+ new->fname[flen] = '\0';
+ strncpy(new->dir, mp->dname, dlen);
+ new->dir[dlen] = '\0';
+ new->size[0] = '\0';
+ if(cell->size[0]){
+ strncpy(new->size, cell->size, sizeof(new->size));
+ new->size[sizeof(new->size)-1] = '\0';
+ }
+
+ new->next = mp->lm;
+ mp->lm = new;
+ }
+}
+
+
+/*
+ * Deletes a name from the lmlist
+ */
+void
+del_cell_from_lmlist(struct fcell *cell, struct bmaster *mp)
+{
+ LMLIST *lm, *lmprev = NULL;
+
+ if(mp && cell && cell->fname && cell->fname[0])
+ for(lm = mp ? mp->lm : NULL; lm; lm = lm->next){
+ if(lm->fname && strcmp(cell->fname, lm->fname) == 0){
+ free((char *) lm->fname);
+ if(lm->dir)
+ free((char *) lm->dir);
+
+ if(lmprev)
+ lmprev->next = lm->next;
+ else
+ mp->lm = lm->next;
+
+ free((char *) lm);
+
+ break;
+ }
+
+ lmprev = lm;
+ }
+}
+
+
+/*
+ * PaintCell - print the given cell at the given location on the display
+ * the format of a printed cell is:
+ *
+ * "<fname> <size> "
+ */
+void
+PaintCell(int row, int col,
+ int sc, /* screen columns available for this cell */
+ struct fcell *cell, int inverted)
+{
+ char buf1[NLINE], buf2[NLINE];
+ char lbuf[5];
+ int need, l_wid, f_wid, f_to_s_wid, s_wid;
+ UCS *ucs;
+
+ if(cell == NULL)
+ return;
+
+ l_wid = (gmp && ((gmp->flags & FB_LMODEPOS) > 4)) ? 4 : 0;
+ f_wid = utf8_width(cell->fname ? cell->fname : "");
+ f_to_s_wid = 1;
+ s_wid = utf8_width(cell->size ? cell->size : "");
+
+ /* the two is the space between cell columns */
+ sc = MIN(sc-2, term.t_ncol-col);
+
+ if(l_wid){
+ if(gmp && gmp->flags & FB_LMODE && cell->mode != FIODIR){
+ /*
+ * We have to figure out here if it is selected or not
+ * and use that to write the X or space.
+ */
+ lbuf[0] = '[';
+ if(fcell_is_selected(cell, gmp))
+ lbuf[1] = 'X';
+ else
+ lbuf[1] = ' ';
+
+ lbuf[2] = ']';
+ lbuf[3] = ' ';
+ }
+ else{
+ lbuf[0] = lbuf[1] = lbuf[2] = lbuf[3] = ' ';
+ }
+
+ lbuf[4] = '\0';
+ }
+
+ sc -= l_wid;
+
+ need = f_wid+f_to_s_wid+s_wid;
+
+ /* space between name and size */
+ if(need < sc)
+ f_to_s_wid += (sc-need);
+
+ /*
+ * If the width isn't enough to handle everything we're just putting a single
+ * space between fname and size and truncating on the right. That means that
+ * the sizes in the right hand column won't line up correctly when there is
+ * a lack of space. Instead, we opt for displaying as much info as possible
+ * in a slightly discordant way.
+ */
+
+ movecursor(row, col);
+ if(l_wid){
+ ucs = utf8_to_ucs4_cpystr(lbuf);
+ if(ucs){
+ pputs(ucs, 0);
+ fs_give((void **) &ucs);
+ }
+ }
+
+ utf8_snprintf(buf1, sizeof(buf1), "%*.*w%*.*w%*.*w",
+ f_wid, f_wid, cell->fname ? cell->fname : "",
+ f_to_s_wid, f_to_s_wid, "",
+ s_wid, s_wid, cell->size ? cell->size : "");
+
+ utf8_snprintf(buf2, sizeof(buf2), "%*.*w", sc, sc, buf1);
+
+ if(inverted)
+ (*term.t_rev)(1);
+
+ ucs = utf8_to_ucs4_cpystr(buf2);
+ if(ucs){
+ pputs(ucs, 0);
+ fs_give((void **) &ucs);
+ }
+
+ if(inverted)
+ (*term.t_rev)(0);
+}
+
+
+/*
+ * PaintBrowse - with the current data, display the browser. if level == 0
+ * paint the whole thing, if level == 1 just paint the cells
+ * themselves
+ */
+void
+PaintBrowser(struct bmaster *mp, int level, int *row, int *col)
+{
+ int i, cl;
+ struct fcell *tp;
+
+ if(!level){
+ ClearBrowserScreen();
+ BrowserAnchor(mp->dname);
+ }
+
+ i = 0;
+ tp = mp->top;
+ cl = COMPOSER_TOP_LINE; /* current display line */
+ while(tp){
+
+ PaintCell(cl, mp->cpf * i, mp->cpf, tp, tp == mp->current);
+
+ if(tp == mp->current){
+ if(row)
+ *row = cl;
+
+ if(col)
+ *col = mp->cpf * i;
+ }
+
+ if(++i >= mp->fpl){
+ i = 0;
+ if(++cl > term.t_nrow-(term.t_mrow+1))
+ break;
+ }
+
+ tp = tp->next;
+ }
+
+ if(level){
+ while(cl <= term.t_nrow - (term.t_mrow+1)){
+ if(!i)
+ movecursor(cl, 0);
+ peeol();
+ movecursor(++cl, 0);
+ }
+ }
+ else{
+ BrowserKeys();
+ }
+}
+
+
+/*
+ * BrowserKeys - just paints the keyhelp at the bottom of the display
+ */
+void
+BrowserKeys(void)
+{
+ menu_browse[QUIT_KEY].name = (gmode&MDBRONLY) ? "Q" : "E";
+ /* TRANSLATORS: Brwsr is an abbreviation for Browser, which is
+ a command used to look through something */
+ menu_browse[QUIT_KEY].label = (gmode&MDBRONLY) ? N_("Quit") : N_("Exit Brwsr");
+ menu_browse[GOTO_KEY].name = (gmode&MDGOTO) ? "G" : NULL;
+ menu_browse[GOTO_KEY].label = (gmode&MDGOTO) ? N_("Goto") : NULL;
+ if(gmode & MDBRONLY){
+ menu_browse[EXEC_KEY].name = "L";
+ menu_browse[EXEC_KEY].label = N_("Launch");
+ menu_browse[SELECT_KEY].name = "V";
+ menu_browse[SELECT_KEY].label = "[" N_("View") "]";
+ menu_browse[PICO_KEY].name = "E";
+ menu_browse[PICO_KEY].label = N_("Edit");
+ }
+ else{
+ menu_browse[SELECT_KEY].name = "S";
+ menu_browse[SELECT_KEY].label = "[" N_("Select") "]";
+ menu_browse[PICO_KEY].name = "A";
+ menu_browse[PICO_KEY].label = N_("Add");
+
+ if(gmp && gmp->flags & FB_LMODEPOS){
+ if(gmp && gmp->flags & FB_LMODE){ /* ListMode is already on */
+ menu_browse[EXEC_KEY].name = "X";
+ menu_browse[EXEC_KEY].label = N_("Set/Unset");
+ }
+ else{ /* ListMode is possible */
+ menu_browse[EXEC_KEY].name = "L";
+ menu_browse[EXEC_KEY].label = N_("ListMode");
+ }
+ }
+ else{ /* No ListMode possible */
+ menu_browse[EXEC_KEY].name = NULL;
+ menu_browse[EXEC_KEY].label = NULL;
+ }
+ }
+
+ wkeyhelp(menu_browse);
+}
+
+
+/*
+ * layoutcells - figure out max length of cell and how many cells can
+ * go on a line of the display
+ */
+void
+layoutcells(struct bmaster *mp)
+{
+ static int wid = -1;
+ /*
+ * Max chars/file. Actually this is max screen cells/file
+ * Longest name + "(parent dir)"
+ */
+ if(wid < 0)
+ wid = MAX(utf8_width(PARENTDIR),utf8_width(SELECTWORD)) + utf8_width(DIRWORD);
+
+ mp->cpf = mp->longest + wid + 3;
+
+ if(mp->flags & FB_LMODEPOS) /* "[X] " */
+ mp->cpf += 4;
+
+ if(gmode & MDONECOL){
+ mp->fpl = 1;
+ }
+ else{
+ int i = 1;
+
+ while(i*mp->cpf - 2 <= term.t_ncol) /* no force... */
+ i++; /* like brute force! */
+
+ mp->fpl = i - 1; /* files per line */
+ }
+
+ if(mp->fpl == 0)
+ mp->fpl = 1;
+}
+
+
+/*
+ * percdircells - bubble all the directory cells to the top of the
+ * list.
+ */
+void
+percdircells(struct bmaster *mp)
+{
+ struct fcell *dirlp, /* dir cell list pointer */
+ *lp, *nlp; /* cell list ptr and next */
+
+ dirlp = NULL;
+ for(lp = mp->head; lp; lp = nlp){
+ nlp = lp->next;
+ if(lp->mode == FIODIR){
+ if(lp->prev) /* clip from list */
+ lp->prev->next = lp->next;
+
+ if(lp->next)
+ lp->next->prev = lp->prev;
+
+ if((lp->prev = dirlp) != NULL){ /* tie it into dir portion */
+ if((lp->next = dirlp->next) != NULL)
+ lp->next->prev = lp;
+
+ dirlp->next = lp;
+ dirlp = lp;
+ }
+ else{
+ if((dirlp = lp) != mp->head)
+ dirlp->next = mp->head;
+
+ if(dirlp->next)
+ dirlp->next->prev = dirlp;
+
+ mp->head = mp->top = mp->current = dirlp;
+ }
+ }
+ }
+}
+
+
+/*
+ * PlaceCell - given a browser master and a cell, return row and col of the display that
+ * it should go on.
+ *
+ * return 1 if mp->top has changed, x,y relative to new page
+ * return 0 if otherwise (same page)
+ * return -1 on error
+ */
+int
+PlaceCell(struct bmaster *mp, struct fcell *cp, int *x, int *y)
+{
+ int cl = COMPOSER_TOP_LINE; /* current line */
+ int ci = 0; /* current index on line */
+ int rv = 0;
+ int secondtry = 0;
+ struct fcell *tp;
+
+ /* will cp fit on screen? */
+ tp = mp->top;
+ while(1){
+ if(tp == cp){ /* bingo! */
+ *x = cl;
+ *y = ci * mp->cpf;
+ break;
+ }
+
+ if((tp = tp->next) == NULL){ /* above top? */
+ if(secondtry++){
+ emlwrite("\007Internal error: can't find fname cell", NULL);
+ return(-1);
+ }
+ else{
+ tp = mp->top = mp->head; /* try from the top! */
+ cl = COMPOSER_TOP_LINE;
+ ci = 0;
+ rv = 1;
+ continue; /* start over! */
+ }
+ }
+
+ if(++ci >= mp->fpl){ /* next line? */
+ ci = 0;
+ if(++cl > term.t_nrow-(term.t_mrow+1)){ /* next page? */
+ ci = mp->fpl; /* tp is at bottom right */
+ while(ci--) /* find new top */
+ tp = tp->prev;
+ mp->top = tp;
+ ci = 0;
+ cl = COMPOSER_TOP_LINE; /* keep checking */
+ rv = 1;
+ }
+ }
+
+ }
+
+ /* not on display! */
+ return(rv);
+}
+
+
+/*
+ * zotfcells - clean up malloc'd cells of file names
+ */
+void
+zotfcells(struct fcell *hp)
+{
+ struct fcell *tp;
+
+ while(hp){
+ tp = hp;
+ hp = hp->next;
+ tp->next = NULL;
+ free((char *) tp);
+ }
+}
+
+
+/*
+ * zotmaster - blast the browser master struct
+ */
+void
+zotmaster(struct bmaster **mp)
+{
+ if(mp && *mp){
+ zotfcells((*mp)->head); /* free cells */
+ zotlmlist((*mp)->lm); /* free lmlist */
+ if((*mp)->names)
+ free((char *)(*mp)->names); /* free file names */
+
+ free((char *)*mp); /* free master */
+ *mp = NULL; /* make double sure */
+ }
+}
+
+
+/*
+ * FindCell - starting from the current cell find the first occurance of
+ * the given string wrapping around if necessary
+ */
+struct fcell *
+FindCell(struct bmaster *mp, char *utf8string)
+{
+ struct fcell *tp, *fp;
+
+ if(*utf8string == '\0')
+ return(NULL);
+
+ fp = NULL;
+ tp = mp->current->next;
+
+ while(tp && !fp){
+ if(sisin(tp->fname, utf8string))
+ fp = tp;
+ else
+ tp = tp->next;
+ }
+
+ tp = mp->head;
+ while(tp != mp->current && !fp){
+ if(sisin(tp->fname, utf8string))
+ fp = tp;
+ else
+ tp = tp->next;
+ }
+
+ return(fp);
+}
+
+
+/*
+ * sisin - case insensitive substring matching function
+ *
+ * We can't really do case-insensitive for non-ascii, so restrict
+ * that to ascii characters. The strings will be utf8.
+ */
+int
+sisin(char *bigstr, char *utf8substr)
+{
+ register int j;
+
+ while(*bigstr){
+ j = 0;
+ while(bigstr[j] == utf8substr[j]
+ || ((unsigned char)bigstr[j] < 0x80 && (unsigned char)utf8substr[j] < 0x80
+ && toupper((unsigned char)bigstr[j]) == toupper((unsigned char)utf8substr[j])))
+ if(utf8substr[++j] == '\0') /* bingo! */
+ return(1);
+
+ bigstr++;
+ }
+
+ return(0);
+}
+
+
+/*
+ * set_browser_title -
+ */
+void
+set_browser_title(char *s)
+{
+ browser_title = s;
+}
+
+
+/*
+ * BrowserAnchor - draw the browser's anchor line.
+ */
+void
+BrowserAnchor(char *utf8dir)
+{
+ char *pdir;
+ char titlebuf[NLINE];
+ char buf[NLINE];
+ char dirbuf[NLINE];
+ char *dots = "...";
+ char *br = "BROWSER";
+ char *dir = "Dir: ";
+ UCS *ucs;
+ int need, extra, avail;
+ int title_wid, b_wid, t_to_b_wid, b_to_d_wid, d_wid, dot_wid, dir_wid, after_dir_wid;
+ COLOR_PAIR *lastc = NULL;
+
+ if(!utf8dir)
+ utf8dir = "";
+
+ pdir = utf8dir;
+
+ if(browser_title)
+ snprintf(titlebuf, sizeof(buf), " %s", browser_title);
+ else if(Pmaster)
+ snprintf(titlebuf, sizeof(buf), " ALPINE %s", Pmaster->pine_version);
+ else
+ snprintf(titlebuf, sizeof(buf), " UW PICO %s", (gmode&MDBRONLY) ? "BROWSER" : version);
+
+ title_wid = utf8_width(titlebuf);
+ t_to_b_wid = 15;
+ b_wid = utf8_width(br);
+ b_to_d_wid = 4;
+ d_wid = utf8_width(dir);
+ dot_wid = 0;
+ dir_wid = utf8_width(pdir);
+
+ need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
+
+ if(need > term.t_ncol){
+ extra = need - term.t_ncol;
+ t_to_b_wid -= MIN(extra, 10);
+ need -= MIN(extra, 10);
+
+ if(need > term.t_ncol){
+ extra = need - term.t_ncol;
+ b_to_d_wid -= MIN(extra, 2);
+ need -= MIN(extra, 2);
+ }
+
+ if(need > term.t_ncol){
+ titlebuf[0] = titlebuf[1] = ' ';
+ titlebuf[2] = '\0';
+ title_wid = utf8_width(titlebuf);
+ t_to_b_wid = 0;
+ b_to_d_wid = 4;
+ need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
+ if(need > term.t_ncol){
+ extra = need - term.t_ncol;
+ b_to_d_wid -= MIN(extra, 2);
+ need -= MIN(extra, 2);
+ }
+
+ need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
+ if(need > term.t_ncol){
+ t_to_b_wid = 0;
+ b_wid = 0;
+ b_to_d_wid = 0;
+ }
+
+ need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
+ if(need > term.t_ncol)
+ d_wid = 0;
+
+ need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
+
+ if(need > term.t_ncol && dir_wid > 0){
+ dot_wid = utf8_width(dots);
+ need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
+ while(need > term.t_ncol && (pdir = strchr(pdir+1, C_FILESEP))){
+ dir_wid = utf8_width(pdir);
+ need = title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid;
+ }
+
+ if(!pdir){ /* adjust other widths to fill up space */
+ avail = term.t_ncol - (title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid);
+ if(avail > 2)
+ utf8_to_width_rhs(dirbuf, utf8dir, sizeof(dirbuf), avail);
+ else
+ dirbuf[0] = '\0';
+
+ pdir = dirbuf;
+ }
+
+ dir_wid = utf8_width(pdir);
+ }
+ }
+ }
+
+ extra = term.t_ncol - (title_wid+t_to_b_wid+b_wid+b_to_d_wid+d_wid+dot_wid+dir_wid);
+
+ if(extra >= 0)
+ after_dir_wid = extra;
+ else
+ after_dir_wid = 0;
+
+ utf8_snprintf(buf, sizeof(buf), "%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w",
+ title_wid, title_wid, titlebuf,
+ t_to_b_wid, t_to_b_wid, "",
+ b_wid, b_wid, br,
+ b_to_d_wid, b_to_d_wid, "",
+ d_wid, d_wid, dir,
+ dot_wid, dot_wid, dots,
+ dir_wid, dir_wid, pdir,
+ after_dir_wid, after_dir_wid, "");
+
+ /* just making sure */
+ utf8_snprintf(titlebuf, sizeof(titlebuf), "%-*.*w", term.t_ncol, term.t_ncol, buf);
+
+ movecursor(0, 0);
+ if(Pmaster && Pmaster->colors && Pmaster->colors->tbcp
+ && pico_is_good_colorpair(Pmaster->colors->tbcp)){
+ lastc = pico_get_cur_color();
+ (void) pico_set_colorp(Pmaster->colors->tbcp, PSC_NONE);
+ }
+ else
+ (*term.t_rev)(1);
+
+ ucs = utf8_to_ucs4_cpystr(titlebuf);
+ if(ucs){
+ pputs(ucs, 0);
+ fs_give((void **) &ucs);
+ }
+
+ if(lastc){
+ (void) pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+ else
+ (*term.t_rev)(0);
+}
+
+
+/*
+ * ResizeBrowser - handle a resize event
+ */
+int
+ResizeBrowser(void)
+{
+ if(gmp){
+ layoutcells(gmp);
+ PaintBrowser(gmp, 0, NULL, NULL);
+ return(1);
+ }
+ else
+ return(0);
+}
+
+
+void
+ClearBrowserScreen(void)
+{
+ int i;
+
+ for(i = 0; i <= term.t_nrow; i++){ /* clear screen */
+ movecursor(i, 0);
+ peeol();
+ }
+}
+
+
+void
+BrowserRunChild(char *child, char *dir)
+{
+ int status;
+ char tmp[NLINE];
+ time_t t_in, t_out;
+
+ ClearBrowserScreen();
+ movecursor(0, 0);
+ (*term.t_close)();
+ if(!isdir(dir, NULL, &t_in))
+ t_in = 0;
+
+ fflush(stdout);
+ status = system(child);
+ (*term.t_open)();
+ if(t_in && isdir(dir, NULL, &t_out) && t_in < t_out){
+ struct bmaster *mp;
+
+ if((mp = getfcells(dir, 0)) != NULL){
+ zotmaster(&gmp);
+ gmp = mp;
+ }
+ /* else getfcells should explain what happened */
+ }
+
+ /* complain about non-zero exit status */
+ if((status >> 8) & 0xff){
+
+ movecursor(term.t_nrow - 1, 0);
+ snprintf(tmp, sizeof(tmp), "[ \"%.30s\" exit with error value: %d ]",
+ child, (status >> 8) & 0xff);
+ pputs_utf8(tmp, 1);
+ movecursor(term.t_nrow, 0);
+ pputs_utf8("[ Hit RETURN to continue ]", 1);
+ fflush(stdout);
+
+ while(GetKey() != (CTRL|'M')){
+ (*term.t_beep)();
+ fflush(stdout);
+ }
+ }
+}
+
+
+/*
+ * imitate pc-pine memory for where we last called the file browser.
+ */
+void
+p_chdir(struct bmaster *mp)
+{
+ if(mp && mp->dname)
+ chdir(mp->dname);
+}
+#else /* _WINDOWS */
+
+
+/*
+ * pico_file_browse - Exported version of FileBrowse below.
+ */
+int
+pico_file_browse(PICO *pdata, char *dir, size_t dirlen, char *fn, size_t fnlen,
+ char *sz, size_t szlen, int flags)
+{
+ return(FileBrowse(dir, dirlen, fn, fnlen, sz, szlen, flags, NULL));
+}
+
+int
+ResizeBrowser (void)
+{
+ return (0);
+}
+
+/*
+ * FileBrowse - Windows version of above function
+ */
+int
+FileBrowse(char *dir, size_t dirlen, char *fn, size_t fnlen,
+ char *sz, size_t szlen, int fb_flags, LMLIST **lmreturn)
+{
+ struct stat sbuf;
+ int rc;
+ char lfn[NLINE];
+
+ if (fb_flags & FB_SAVE){
+ rc = mswin_savefile(dir, dirlen, fn, MIN(dirlen,fnlen));
+ }
+ else{
+ *fn = '\0'; /* No initial file names for
+ * open. */
+ if(fb_flags & FB_LMODEPOS){
+ /*
+ * We're going to allow multiple filenames to be returned so
+ * we don't want to use the passed in fn for that. Instead, make
+ * a bigger space and use that.
+ */
+ char f[20000];
+ size_t flen, dlen;
+
+ memset(f, 0, sizeof(f));
+
+ rc = mswin_multopenfile(dir, dirlen, f, sizeof(f),
+ (fb_flags & FB_ATTACH) ? NULL : "Text Files (*.txt)#*.txt");
+
+ if(rc == 1){
+ LMLIST *lmhead = NULL, *new;
+ char *p;
+
+ /*
+ * Build an LMLIST to return to the caller.
+ */
+ for(p = f; *p; p += strlen(p)+1){
+ flen = strlen(p);
+ dlen = strlen(dir ? dir : "");
+ new = (LMLIST *) fs_get(sizeof(*new));
+ new->fname = (char *) fs_get((flen+1) * sizeof(char));
+ new->dir = (char *) fs_get((dlen+1) * sizeof(char));
+
+ strncpy(new->fname, p, flen);
+ new->fname[flen] = '\0';
+ strncpy(new->dir, dir ? dir : "", dlen);
+ new->dir[dlen] = '\0';
+
+ /* build full path to stat file. */
+ if((strlen(new->dir) + strlen(S_FILESEP) +
+ strlen(new->fname) + 1) < sizeof(lfn)){
+ strncpy(lfn, new->dir, sizeof(lfn));
+ lfn[sizeof(lfn)-1] = '\0';
+ strncat(lfn, S_FILESEP, sizeof(lfn)-strlen(lfn)-1);
+ strncat(lfn, new->fname, sizeof(lfn)-strlen(lfn)-1);
+ lfn[sizeof(lfn)-1] = '\0';
+ if(our_stat(lfn, &sbuf) < 0)
+ strncpy(new->size, "0", 32);
+ else
+ strncpy(new->size, prettysz((off_t)sbuf.st_size), 32);
+
+ new->size[32-1] = '\0';
+ }
+
+ new->next = lmhead;
+ lmhead = new;
+ }
+
+ *lmreturn = lmhead;
+ return(1);
+ }
+ }
+ else
+ rc = mswin_openfile (dir, dirlen, fn, fnlen,
+ (fb_flags & FB_ATTACH) ? NULL : "Text Files (*.txt)#*.txt");
+ }
+
+ if(rc == 1){
+ if(sz != NULL){
+ /* build full path to stat file. */
+ if((strlen(dir) + strlen(S_FILESEP) + strlen(fn) + 1) > NLINE)
+ return -1;
+
+ strncpy(lfn, dir, sizeof(lfn));
+ lfn[sizeof(lfn)-1] = '\0';
+ strncat(lfn, S_FILESEP, sizeof(lfn)-strlen(lfn)-1);
+ lfn[sizeof(lfn)-1] = '\0';
+ strncat(lfn, fn, sizeof(lfn)-strlen(lfn)-1);
+ lfn[sizeof(lfn)-1] = '\0';
+ if(our_stat(lfn, &sbuf) < 0){
+ strncpy(sz, "0", szlen);
+ sz[szlen-1] = '\0';
+ }
+ else{
+ strncpy(sz, prettysz ((off_t)sbuf.st_size), szlen);
+ sz[szlen-1] = '\0';
+ }
+ }
+
+ return(1);
+ }
+
+ return(rc ? -1 : 0);
+}
+
+#endif /* _WINDOWS */
+
+
+/*
+ * LikelyASCII - make a rough guess as to the displayability of the
+ * given file.
+ */
+int
+LikelyASCII(char *file)
+{
+#define LA_TEST_BUF 1024
+#define LA_LINE_LIMIT 300
+ int n, i, line, rv = FALSE;
+ unsigned char buf[LA_TEST_BUF];
+ FILE *fp;
+ EML eml;
+
+ if((fp = our_fopen(file, "rb")) != NULL){
+ clearerr(fp);
+ if((n = fread(buf, sizeof(char), LA_TEST_BUF * sizeof(char), fp)) > 0
+ || !ferror(fp)){
+ /*
+ * If we don't hit any newlines in a reasonable number,
+ * LA_LINE_LIMIT, of characters or the file contains NULLs,
+ * bag out...
+ */
+ rv = TRUE;
+ for(i = line = 0; i < n; i++)
+ if((line = (buf[i] == '\n') ? 0 : line + 1) >= LA_LINE_LIMIT
+ || !buf[i]){
+ rv = FALSE;
+ emlwrite(_("Can't display non-text file. Try \"Launch\"."),
+ NULL);
+ break;
+ }
+ }
+ else{
+ eml.s = file;
+ emlwrite(_("Can't read file: %s"), &eml);
+ }
+
+ fclose(fp);
+ }
+ else{
+ eml.s = file;
+ emlwrite(_("Can't open file: %s"), &eml);
+ }
+
+ return(rv);
+}
+
+
+void
+zotlmlist(LMLIST *lm)
+{
+ LMLIST *tp;
+
+ while(lm){
+ if(lm->fname)
+ free(lm->fname);
+
+ if(lm->dir)
+ free(lm->dir);
+
+ tp = lm;
+ lm = lm->next;
+ tp->next = NULL;
+ free((char *) tp);
+ }
+}
+
+
+/*
+ * time_to_check - checks the current time against the last time called
+ * and returns true if the elapsed time is > below.
+ * Newmail won't necessarily check, but we want to give it
+ * a chance to check or do a keepalive.
+ */
+int
+time_to_check(void)
+{
+ static time_t lasttime = 0L;
+
+ if(!get_input_timeout())
+ return(FALSE);
+
+ if(time((time_t *) 0) - lasttime > (Pmaster ? (time_t)(FUDGE-10) : get_input_timeout())){
+ lasttime = time((time_t *) 0);
+ return(TRUE);
+ }
+ else
+ return(FALSE);
+}
diff --git a/pico/buffer.c b/pico/buffer.c
new file mode 100644
index 00000000..87423d28
--- /dev/null
+++ b/pico/buffer.c
@@ -0,0 +1,354 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: buffer.c 761 2007-10-23 22:35:18Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ *
+ * Program: Buffer management routines
+ */
+
+/*
+ * Buffer management.
+ * Some of the functions are internal,
+ * and some are actually attached to user
+ * keys. Like everyone else, they set hints
+ * for the display system.
+ */
+#include "headers.h"
+
+int sgetline(char **, int *, char *, int);
+
+
+/*
+ * Look through the list of
+ * buffers. Return TRUE if there
+ * are any changed buffers. Buffers
+ * that hold magic internal stuff are
+ * not considered; who cares if the
+ * list of buffer names is hacked.
+ * Return FALSE if no buffers
+ * have been changed.
+ */
+int
+anycb(void)
+{
+ register BUFFER *bp;
+
+ bp = bheadp;
+ while (bp != NULL) {
+ if ((bp->b_flag&BFTEMP)==0 && (bp->b_flag&BFCHG)!=0)
+ return (TRUE);
+ bp = bp->b_bufp;
+ }
+ return (FALSE);
+}
+
+/*
+ * Find a buffer, by name. Return a pointer
+ * to the BUFFER structure associated with it. If
+ * the named buffer is found, but is a TEMP buffer (like
+ * the buffer list) conplain. If the buffer is not found
+ * and the "cflag" is TRUE, create it. The "bflag" is
+ * the settings for the flags in in buffer.
+ */
+BUFFER *
+bfind(char *bname, int cflag, int bflag)
+{
+ register BUFFER *bp;
+ register BUFFER *sb; /* buffer to insert after */
+ register LINE *lp;
+
+ bp = bheadp;
+ while (bp != NULL) {
+ if (strcmp(bname, bp->b_bname) == 0) {
+ if ((bp->b_flag&BFTEMP) != 0) {
+ mlwrite_utf8("Cannot select builtin buffer", NULL);
+ return (NULL);
+ }
+ return (bp);
+ }
+ bp = bp->b_bufp;
+ }
+ if (cflag != FALSE) {
+ if ((bp=(BUFFER *)malloc(sizeof(BUFFER))) == NULL)
+ return (NULL);
+ if ((lp=lalloc(0)) == NULL) {
+ free((char *) bp);
+ return (NULL);
+ }
+ /* find the place in the list to insert this buffer */
+ if (bheadp == NULL || strcmp(bheadp->b_bname, bname) > 0) {
+ /* insert at the begining */
+ bp->b_bufp = bheadp;
+ bheadp = bp;
+ } else {
+ sb = bheadp;
+ while (sb->b_bufp != NULL) {
+ if (strcmp(sb->b_bufp->b_bname, bname) > 0)
+ break;
+ sb = sb->b_bufp;
+ }
+
+ /* and insert it */
+ bp->b_bufp = sb->b_bufp;
+ sb->b_bufp = bp;
+ }
+
+ /* and set up the other buffer fields */
+ bp->b_active = TRUE;
+ bp->b_dotp = lp;
+ bp->b_doto = 0;
+ bp->b_markp = NULL;
+ bp->b_marko = 0;
+ bp->b_flag = bflag;
+ bp->b_mode = gmode;
+ bp->b_nwnd = 0;
+ bp->b_linep = lp;
+ strncpy(bp->b_fname, "", sizeof(bp->b_fname));
+ bp->b_fname[sizeof(bp->b_fname)-1] = '\0';
+ strncpy(bp->b_bname, bname, sizeof(bp->b_bname));
+ bp->b_bname[sizeof(bp->b_bname)-1] = '\0';
+ lp->l_fp = lp;
+ lp->l_bp = lp;
+ }
+ return (bp);
+}
+
+/*
+ * This routine blows away all of the text
+ * in a buffer. If the buffer is marked as changed
+ * then we ask if it is ok to blow it away; this is
+ * to save the user the grief of losing text. The
+ * window chain is nearly always wrong if this gets
+ * called; the caller must arrange for the updates
+ * that are required. Return TRUE if everything
+ * looks good.
+ */
+int
+bclear(BUFFER *bp)
+{
+ register LINE *lp;
+ register int s = FALSE;
+
+ if(Pmaster){
+ if ((bp->b_flag&BFTEMP) == 0 /* Not scratch buffer. */
+ && (bp->b_flag&BFCHG) != 0){ /* Something changed */
+ emlwrite("buffer lines not freed.", NULL);
+ return (s);
+ }
+ }
+ else{
+ if ((bp->b_flag&BFTEMP) == 0 /* Not scratch buffer. */
+ && (bp->b_flag&BFCHG) != 0 /* Something changed */
+ /* TRANSLATORS: A question asking whether to forget about
+ the changes and revert to the unchanged version. */
+ && (s=mlyesno_utf8(_("Discard changes"), -1)) != TRUE){
+ return (s);
+ }
+ }
+
+ bp->b_flag &= ~BFCHG; /* Not changed */
+ while ((lp=lforw(bp->b_linep)) != bp->b_linep)
+ lfree(lp);
+ bp->b_dotp = bp->b_linep; /* Fix "." */
+ bp->b_doto = 0;
+ bp->b_markp = NULL; /* Invalidate "mark" */
+ bp->b_marko = 0;
+ return (TRUE);
+}
+
+
+/*
+ * packbuf - will pack up the main buffer in the buffer provided
+ * to be returned to the program that called pico.
+ * if need be, allocate memory for the new message.
+ * will also free the memory associated with the editor
+ * buffer, by calling zotedit.
+ */
+int
+packbuf(char **buf,
+ int *blen,
+ int lcrlf) /* EOLs are local or CRLF */
+{
+ register int i = 0;
+ register LINE *lp;
+ register int retval = 0;
+ register char *bufp;
+ register char *eobuf;
+
+ if(anycb() != FALSE){
+
+ lp = lforw(curbp->b_linep);
+ do{ /* how many chars? */
+ i += llength(lp);
+ /*
+ * add extra for new lines to be inserted later
+ */
+ i += 2;
+ lp = lforw(lp);
+ }
+ while(lp != curbp->b_linep);
+
+ if(i > *blen){ /* new buffer ? */
+ /*
+ * don't forget to add one for the null terminator!!!
+ */
+ if((bufp = (char *)malloc((i+1)*sizeof(char))) == NULL){
+ zotedit(); /* bag it! */
+ return(COMP_FAILED);
+ }
+ free(*buf);
+ *buf = bufp;
+ *blen = i;
+ }
+ else{
+ bufp = *buf;
+ }
+
+ eobuf = bufp + *blen;
+ lp = lforw(curbp->b_linep); /* First line. */
+ do {
+ for (i = 0; i < llength(lp); i++){ /* copy into buffer */
+ if((bufp+1) < eobuf){
+ *bufp++ = (lp->l_text[i].c & 0xFF);
+ }
+ else{
+ /*
+ * the idea is to malloc enough space for the new
+ * buffer...
+ */
+ *bufp = '\0';
+ zotedit();
+ return(BUF_CHANGED|COMP_FAILED);
+ }
+ }
+ if(lcrlf){
+ *bufp++ = '\n'; /* EOLs use local convention */
+ }
+ else{
+ *bufp++ = 0x0D; /* EOLs use net standard */
+ *bufp++ = 0x0A;
+ }
+ lp = lforw(lp);
+ }
+ while (lp != curbp->b_linep);
+ if(lcrlf)
+ *--bufp = '\0';
+ else
+ *bufp = '\0';
+ retval = BUF_CHANGED;
+ }
+
+ zotedit();
+ return(retval);
+}
+
+
+/*
+ * readbuf - reads in a buffer.
+ */
+void
+readbuf(char **buf)
+{
+ register LINE *lp1;
+ register LINE *lp2;
+ register BUFFER *bp;
+ register WINDOW *wp;
+ register int i;
+ register int s;
+ char *sptr; /* pointer into buffer string */
+ int nbytes;
+ char line[NLINE];
+ CELL ac;
+
+ bp = curbp;
+ bp->b_flag &= ~(BFTEMP|BFCHG);
+ sptr = *buf;
+ ac.a = 0;
+
+ while((s=sgetline(&sptr,&nbytes,line,NLINE)) == FIOSUC || s == FIOLNG){
+
+ if ((lp1=lalloc(nbytes)) == NULL) {
+ s = FIOERR; /* Keep message on the */
+ break; /* display. */
+ }
+ lp2 = lback(curbp->b_linep);
+ lp2->l_fp = lp1;
+ lp1->l_fp = curbp->b_linep;
+ lp1->l_bp = lp2;
+ curbp->b_linep->l_bp = lp1;
+ for (i=0; i<nbytes; ++i){
+ ac.c = line[i];
+ lputc(lp1, i, ac);
+ }
+ }
+
+ for (wp=wheadp; wp!=NULL; wp=wp->w_wndp) {
+ if (wp->w_bufp == curbp) {
+ wheadp->w_linep = lforw(curbp->b_linep);
+ wheadp->w_dotp = lback(curbp->b_linep);
+ wheadp->w_doto = 0;
+ wheadp->w_markp = NULL;
+ wheadp->w_marko = 0;
+ wheadp->w_flag |= WFHARD;
+ }
+ }
+
+ strncpy(bp->b_bname, "main", sizeof(bp->b_bname));
+ bp->b_bname[sizeof(bp->b_bname)-1] = '\0';
+ strncpy(bp->b_fname, "", sizeof(bp->b_fname));
+ bp->b_fname[sizeof(bp->b_fname)-1] = '\0';
+
+ bp->b_dotp = bp->b_linep;
+ bp->b_doto = 0;
+}
+
+
+/*
+ * sgetline - copy characters from ibuf to obuf, ending at the first
+ * newline. return with ibuf pointing to first char after
+ * newline.
+ */
+int
+sgetline(char **ibuf, int *nchars, char *obuf, int blen)
+{
+ register char *len;
+ register char *cbuf = *ibuf;
+ register char *bufp = obuf;
+ register int retval = FIOSUC;
+#define CR '\015'
+#define LF '\012'
+
+ *nchars = 0;
+ if(*cbuf == '\0'){
+ retval = FIOEOF;
+ }
+ else{
+ len = obuf + blen;
+ while (*cbuf != CR && *cbuf != LF && *cbuf != '\0'){
+ if(bufp < len){
+ *bufp++ = *cbuf++;
+ (*nchars)++;
+ }
+ else{
+ *bufp = '\0';
+ retval = FIOLNG;
+ break;
+ }
+ }
+ }
+ *bufp = '\0'; /* end retured line */
+ *ibuf = (*cbuf == CR) ? ++cbuf : cbuf;
+ *ibuf = (*cbuf == LF) ? ++cbuf : cbuf;
+ return(retval);
+}
diff --git a/pico/composer.c b/pico/composer.c
new file mode 100644
index 00000000..c801d385
--- /dev/null
+++ b/pico/composer.c
@@ -0,0 +1,4823 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: composer.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2009 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ *
+ * Program: Pine composer routines
+ *
+ * NOTES:
+ *
+ * - composer.c is the composer for the PINE mail system
+ *
+ * - tabled 01/19/90
+ *
+ * Notes: These routines aren't incorporated yet, because the composer as
+ * a whole still needs development. These are ideas that should
+ * be implemented in later releases of PINE. See the notes in
+ * pico.c concerning what needs to be done ....
+ *
+ * - untabled 01/30/90
+ *
+ * Notes: Installed header line editing, with wrapping and unwrapping,
+ * context sensitive help, and other mail header editing features.
+ *
+ * - finalish code cleanup 07/15/91
+ *
+ * Notes: Killed/yanked header lines use emacs kill buffer.
+ * Arbitrarily large headers are handled gracefully.
+ * All formatting handled by FormatLines.
+ *
+ * - Work done to optimize display painting 06/26/92
+ * Not as clean as it should be, needs more thought
+ *
+ */
+#include "headers.h"
+
+#include "osdep/terminal.h"
+#include "../pith/string.h"
+
+int InitEntryText(char *, struct headerentry *);
+int HeaderOffset(int);
+int HeaderFocus(int, int);
+UCS LineEdit(int, UCS *);
+int header_downline(int, int);
+int header_upline(int);
+int FormatLines(struct hdr_line *, char *, int, int, int);
+int FormatSyncAttach(void);
+UCS *ucs4_strqchr(UCS *, UCS, int *, int);
+int ComposerHelp(int);
+void NewTop(int);
+void display_delimiter(int);
+void zotentry(struct hdr_line *);
+int InvertPrompt(int, int);
+int partial_entries(void);
+int physical_line(struct hdr_line *);
+int strend(UCS *, UCS);
+int KillHeaderLine(struct hdr_line *, int);
+int SaveHeaderLines(void);
+UCS *break_point(UCS *, int, UCS, int *);
+int hldelete(struct hdr_line *);
+int is_blank(int, int, int);
+int zotcomma(UCS *);
+struct hdr_line *first_hline(int *);
+struct hdr_line *first_sel_hline(int *);
+struct hdr_line *next_hline(int *, struct hdr_line *);
+struct hdr_line *next_sel_hline(int *, struct hdr_line *);
+struct hdr_line *prev_hline(int *, struct hdr_line *);
+struct hdr_line *prev_sel_hline(int *, struct hdr_line *);
+struct hdr_line *first_requested_hline(int *);
+void fix_mangle_and_err(int *, char **, char *);
+void mark_sticky(struct headerentry *);
+
+
+/*
+ * definition header field array, structures defined in pico.h
+ */
+struct headerentry *headents;
+
+
+/*
+ * structure that keeps track of the range of header lines that are
+ * to be displayed and other fun stuff about the header
+ */
+struct on_display ods; /* global on_display struct */
+
+/* a pointer to the subject line. This is so that we do not have to compute
+ * the header line in every call. It saves a few CPU cycles
+ */
+
+struct hdr_line *subject_line = NULL;
+
+/*
+ * useful macros
+ */
+#define HALLOC() (struct hdr_line *)malloc(sizeof(struct hdr_line))
+#define LINEWID() (((term.t_ncol - headents[ods.cur_e].prwid) > 0) ? ((unsigned) (term.t_ncol - headents[ods.cur_e].prwid)) : 0)
+#define BOTTOM() (term.t_nrow - term.t_mrow)
+#define FULL_SCR() (BOTTOM() - 3)
+#define HALF_SCR() (FULL_SCR()/2)
+
+#ifdef MOUSE
+/*
+ * Redefine HeaderEditor to install wrapper required for mouse event
+ * handling...
+ */
+#define HeaderEditor HeaderEditorWork
+#endif /* MOUSE */
+
+#define HDR_DELIM "----- Message Text -----"
+
+/*
+ * useful declarations
+ */
+static short delim_ps = 0; /* previous state */
+static short invert_ps = 0; /* previous state */
+
+
+static KEYMENU menu_header[] = {
+ /* TRANSLATORS: command key labels, Send means send the message
+ we are currently working on. */
+ {"^G", N_("Get Help"), KS_SCREENHELP}, {"^X", N_("Send"), KS_SEND},
+ /* TRANSLATORS: Rich Headers is a command to display more headers. It
+ is triggered with the ^R key. PrvPg stands for Previous Page. */
+ {"^R", N_("Rich Hdr"), KS_RICHHDR}, {"^Y", N_("PrvPg/Top"), KS_PREVPAGE},
+ /* TRANSLATORS: Cut Line means remove a line. Postpone means to save
+ a message being composed so that it can be worked on later. */
+ {"^K", N_("Cut Line"), KS_CURPOSITION}, {"^O", N_("Postpone"), KS_POSTPONE},
+ /* TRANSLATORS: Del Char is Delete Character */
+ {"^C", N_("Cancel"), KS_CANCEL}, {"^D", N_("Del Char"), KS_NONE},
+ /* TRANSLATORS: Next Page */
+ {"^J", N_("Attach"), KS_ATTACH}, {"^V", N_("NxtPg/End"), KS_NEXTPAGE},
+ /* TRANSLATORS: Undelete a line that was just deleted */
+ {"^U", N_("UnDel Line"), KS_NONE}, {NULL, NULL}
+};
+#define SEND_KEY 1
+#define RICH_KEY 2
+#define CUT_KEY 4
+#define PONE_KEY 5
+#define DEL_KEY 7
+#define ATT_KEY 8
+#define UDEL_KEY 10
+#define TO_KEY 11
+
+
+/*
+ * function key mappings for header editor
+ */
+static UCS ckm[12][2] = {
+ { F1, (CTRL|'G')},
+ { F2, (CTRL|'C')},
+ { F3, (CTRL|'X')},
+ { F4, (CTRL|'D')},
+ { F5, (CTRL|'R')},
+ { F6, (CTRL|'J')},
+ { F7, 0 },
+ { F8, 0 },
+ { F9, (CTRL|'K')},
+ { F10, (CTRL|'U')},
+ { F11, (CTRL|'O')},
+ { F12, (CTRL|'T')}
+};
+
+
+/*
+ * InitMailHeader - initialize header array, and set beginning editor row
+ * range. The header entry structure should look just like
+ * what is written on the screen, the vector
+ * (entry, line, offset) will describe the current cursor
+ * position in the header.
+ *
+ * Returns: TRUE if special header handling was requested,
+ * FALSE under standard default behavior.
+ */
+int
+InitMailHeader(PICO *mp)
+{
+ char *addrbuf;
+ struct headerentry *he;
+ int rv;
+
+ if(!mp->headents){
+ headents = NULL;
+ return(FALSE);
+ }
+
+ /*
+ * initialize some of on_display structure, others below...
+ */
+ ods.p_ind = 0;
+ ods.p_line = COMPOSER_TOP_LINE;
+ ods.top_l = ods.cur_l = NULL;
+
+ headents = mp->headents;
+ /*--- initialize the fields in the headerent structure ----*/
+ for(he = headents; he->name != NULL; he++){
+ he->hd_text = NULL;
+ he->display_it = he->display_it ? he->display_it : !he->rich_header;
+ if(he->is_attach) {
+ /*--- A lot of work to do since attachments are special ---*/
+ he->maxlen = 0;
+ if(mp->attachments != NULL){
+ char buf[NLINE];
+ int attno = 0;
+ int l1, ofp, ofp1, ofp2; /* OverFlowProtection */
+ size_t addrbuflen = 4 * NLINE; /* malloc()ed size of addrbuf */
+ PATMT *ap = mp->attachments;
+
+ ofp = NLINE - 35;
+
+ addrbuf = (char *)malloc(addrbuflen);
+ addrbuf[0] = '\0';
+ buf[0] = '\0';
+ while(ap){
+ if((l1 = strlen(ap->filename)) <= ofp){
+ ofp1 = l1;
+ ofp2 = ofp - l1;
+ }
+ else{
+ ofp1 = ofp;
+ ofp2 = 0;
+ }
+
+ if(ap->filename){
+ snprintf(buf, sizeof(buf), "%d. %.*s%s %s%s%s\"",
+ ++attno,
+ ofp1,
+ ap->filename,
+ (l1 > ofp) ? "...]" : "",
+ ap->size ? "(" : "",
+ ap->size ? ap->size : "",
+ ap->size ? ") " : "");
+
+ /* append description, escaping quotes */
+ if(ap->description){
+ char *dp = ap->description, *bufp = &buf[strlen(buf)];
+ int escaped = 0;
+
+ do
+ if(*dp == '\"' && !escaped){
+ *bufp++ = '\\';
+ ofp2--;
+ }
+ else if(escaped){
+ *bufp++ = '\\';
+ escaped = 0;
+ }
+ while(--ofp2 > 0 && (*bufp++ = *dp++));
+
+ *bufp = '\0';
+ }
+
+ snprintf(buf + strlen(buf), sizeof(buf)-strlen(buf), "\"%s", ap->next ? "," : "");
+
+ if(strlen(addrbuf) + strlen(buf) >= addrbuflen){
+ addrbuflen += NLINE * 4;
+ if(!(addrbuf = (char *)realloc(addrbuf, addrbuflen))){
+ emlwrite("\007Can't realloc addrbuf to %d bytes",
+ (void *) addrbuflen);
+ return(ABORT);
+ }
+ }
+
+ strncat(addrbuf, buf, addrbuflen-strlen(addrbuf)-1);
+ addrbuf[addrbuflen-1] = '\0';
+ }
+ ap = ap->next;
+ }
+ InitEntryText(addrbuf, he);
+ free((char *)addrbuf);
+ } else {
+ InitEntryText("", he);
+ }
+ he->realaddr = NULL;
+ } else {
+ if(!he->blank)
+ addrbuf = *(he->realaddr);
+ else
+ addrbuf = "";
+
+ InitEntryText(addrbuf, he);
+ }
+ }
+
+ /*
+ * finish initialization and then figure out display layout.
+ * first, look for any field the caller requested we start in,
+ * and set the offset into that field.
+ */
+ if((ods.cur_l = first_requested_hline(&ods.cur_e)) != NULL){
+ ods.top_e = 0; /* init top_e */
+ ods.top_l = first_hline(&ods.top_e);
+ if(!HeaderFocus(ods.cur_e, Pmaster ? Pmaster->edit_offset : 0))
+ HeaderFocus(ods.cur_e, 0);
+
+ rv = TRUE;
+ }
+ else{
+ ods.top_l = first_hline(&ods.cur_e);
+ ods.cur_l = first_sel_hline(&ods.cur_e);
+ ods.top_e = ods.cur_e;
+ rv = 0;
+ }
+
+ UpdateHeader(0);
+ return(rv);
+}
+
+
+
+/*
+ * InitEntryText - Add the given header text into the header entry
+ * line structure.
+ */
+int
+InitEntryText(char *utf8text, struct headerentry *e)
+{
+ struct hdr_line *curline;
+ register int longest;
+
+ /*
+ * get first chunk of memory, and tie it to structure...
+ */
+ if((curline = HALLOC()) == NULL){
+ emlwrite("Unable to make room for full Header.", NULL);
+ return(FALSE);
+ }
+
+ longest = term.t_ncol - e->prwid - 1;
+ curline->text[0] = '\0';
+ curline->next = NULL;
+ curline->prev = NULL;
+ e->hd_text = curline; /* tie it into the list */
+
+ if(FormatLines(curline, utf8text, longest, e->break_on_comma, 0) == -1)
+ return(FALSE);
+ else
+ return(TRUE);
+}
+
+
+
+/*
+ * ResizeHeader - Handle resizing display when SIGWINCH received.
+ *
+ * notes:
+ * works OK, but needs thorough testing
+ *
+ */
+int
+ResizeHeader(void)
+{
+ register struct headerentry *i;
+ register int offset;
+
+ if(!headents)
+ return(TRUE);
+
+ offset = (ComposerEditing) ? HeaderOffset(ods.cur_e) : 0;
+
+ for(i=headents; i->name; i++){ /* format each entry */
+ if(FormatLines(i->hd_text, "", (term.t_ncol - i->prwid),
+ i->break_on_comma, 0) == -1){
+ return(-1);
+ }
+ }
+
+ if(ComposerEditing) /* restart at the top */
+ HeaderFocus(ods.cur_e, offset); /* fix cur_l and p_ind */
+ else {
+ struct hdr_line *l;
+ int e;
+
+ for(i = headents; i->name != NULL; i++); /* Find last line */
+ i--;
+ e = i - headents;
+ l = headents[e].hd_text;
+ if(!headents[e].display_it || headents[e].blank)
+ l = prev_sel_hline(&e, l); /* last selectable line */
+
+ if(!l){
+ e = i - headents;
+ l = headents[e].hd_text;
+ }
+
+ HeaderFocus(e, -1); /* put focus on last line */
+ }
+
+ if(ComposerTopLine != COMPOSER_TOP_LINE)
+ UpdateHeader(0);
+
+ PaintBody(0);
+
+ if(ComposerEditing)
+ movecursor(ods.p_line, ods.p_ind+headents[ods.cur_e].prwid);
+
+ (*term.t_flush)();
+ return(TRUE);
+}
+
+
+
+/*
+ * HeaderOffset - return the character offset into the given header
+ */
+int
+HeaderOffset(int h)
+{
+ register struct hdr_line *l;
+ int i = 0;
+
+ l = headents[h].hd_text;
+
+ while(l != ods.cur_l){
+ i += ucs4_strlen(l->text);
+ l = l->next;
+ }
+ return(i+ods.p_ind);
+}
+
+
+
+/*
+ * HeaderFocus - put the dot at the given offset into the given header
+ */
+int
+HeaderFocus(int h, int offset)
+{
+ register struct hdr_line *l;
+ register int i;
+ int last = 0;
+
+ if(offset == -1) /* focus on last line */
+ last = 1;
+
+ l = headents[h].hd_text;
+ while(1){
+ if(last && l->next == NULL){
+ break;
+ }
+ else{
+ if((i=ucs4_strlen(l->text)) >= offset)
+ break;
+ else
+ offset -= i;
+ }
+ if((l = l->next) == NULL)
+ return(FALSE);
+ }
+
+ ods.cur_l = l;
+ ods.p_len = ucs4_strlen(l->text);
+ ods.p_ind = (last) ? 0 : offset;
+
+ return(TRUE);
+}
+
+
+
+/*
+ * HeaderEditor() - edit the mail header field by field, trapping
+ * important key sequences, hand the hard work off
+ * to LineEdit().
+ * returns:
+ * -3 if we drop out bottom *and* want to process mouse event
+ * -1 if we drop out the bottom
+ * FALSE if editing is cancelled
+ * TRUE if editing is finished
+ */
+int
+HeaderEditor(int f, int n)
+{
+ register int i;
+ UCS ch = 0, lastch;
+ register char *bufp;
+ struct headerentry *h;
+ int cur_e, mangled, retval = -1,
+ hdr_only = (gmode & MDHDRONLY) ? 1 : 0;
+ char *errmss, *err;
+#ifdef MOUSE
+ MOUSEPRESS mp;
+#endif
+
+ if(!headents)
+ return(TRUE); /* nothing to edit! */
+
+ ComposerEditing = TRUE;
+ display_delimiter(0); /* provide feedback */
+
+#ifdef _WINDOWS
+ mswin_setscrollrange (0, 0);
+#endif /* _WINDOWS */
+
+ if(Pmaster)
+ for (subject_line = NULL, i=0; headents[i].name; i++)
+ if(strcmp(headents[i].name, "Subject") == 0)
+ subject_line = headents[i].hd_text;
+
+ /*
+ * Decide where to begin editing. if f == TRUE begin editing
+ * at the bottom. this case results from the cursor backing
+ * into the editor from the bottom. otherwise, the user explicitly
+ * requested editing the header, and we begin at the top.
+ *
+ * further, if f == 1, we moved into the header by hitting the up arrow
+ * in the message text, else if f == 2, we moved into the header by
+ * moving past the left edge of the top line in the message. so, make
+ * the end of the last line of the last entry the current cursor position
+ * lastly, if f == 3, we moved into the header by hitting backpage() on
+ * the top line of the message, so scroll a page back.
+ */
+ if(f){
+ if(f == 2){ /* 2 leaves cursor at end */
+ struct hdr_line *l = ods.cur_l;
+ int e = ods.cur_e;
+
+ /*--- make sure on last field ---*/
+ while((l = next_sel_hline(&e, l)) != NULL)
+ if(headents[ods.cur_e].display_it){
+ ods.cur_l = l;
+ ods.cur_e = e;
+ }
+
+ ods.p_ind = 1000; /* and make sure at EOL */
+ }
+ else{
+ /*
+ * note: assumes that ods.cur_e and ods.cur_l haven't changed
+ * since we left...
+ */
+
+ /* fix postition */
+ if(curwp->w_doto < headents[ods.cur_e].prwid)
+ ods.p_ind = 0;
+ else if(curwp->w_doto < ods.p_ind + headents[ods.cur_e].prwid)
+ ods.p_ind = curwp->w_doto - headents[ods.cur_e].prwid;
+ else
+ ods.p_ind = 1000;
+
+ /* and scroll back if needed */
+ if(f == 3)
+ for(i = 0; header_upline(0) && i <= FULL_SCR(); i++)
+ ;
+ }
+
+ ods.p_line = ComposerTopLine - 2;
+ }
+ /* else just trust what ods contains */
+
+ InvertPrompt(ods.cur_e, TRUE); /* highlight header field */
+ sgarbk = 1;
+
+ do{
+ lastch = ch;
+ if(km_popped){
+ km_popped--;
+ if(km_popped == 0)
+ sgarbk = 1;
+ }
+
+ if(sgarbk){
+ if(km_popped){ /* temporarily change to cause menu to paint */
+ term.t_mrow = 2;
+ curwp->w_ntrows -= 2;
+ movecursor(term.t_nrow-2, 0); /* clear status line, too */
+ peeol();
+ }
+ else if(term.t_mrow == 0)
+ PaintBody(1);
+
+ ShowPrompt(); /* display correct options */
+ sgarbk = 0;
+ if(km_popped){
+ term.t_mrow = 0;
+ curwp->w_ntrows += 2;
+ }
+ }
+
+ ch = LineEdit(!(gmode&MDVIEW), &lastch); /* work on the current line */
+
+ if(km_popped)
+ switch(ch){
+ case NODATA:
+ case (CTRL|'L'):
+ km_popped++;
+ break;
+
+ default:
+ movecursor(term.t_nrow-2, 0);
+ peeol();
+ movecursor(term.t_nrow-1, 0);
+ peeol();
+ movecursor(term.t_nrow, 0);
+ peeol();
+ break;
+ }
+
+ switch (ch){
+ case (CTRL|'R') : /* Toggle header display */
+ if(Pmaster->pine_flags & MDHDRONLY){
+ if(Pmaster->expander){
+ packheader();
+ call_expander();
+ PaintBody(0);
+ break;
+ }
+ else
+ goto bleep;
+ }
+
+ /*---- Are there any headers to expand above us? ---*/
+ for(h = headents; h != &headents[ods.cur_e]; h++)
+ if(h->rich_header)
+ break;
+ if(h->rich_header)
+ InvertPrompt(ods.cur_e, FALSE); /* Yes, don't leave inverted */
+
+ mangled = 0;
+ err = NULL;
+ if(partial_entries()){
+ /*--- Just turned off all rich headers --*/
+ if(headents[ods.cur_e].rich_header){
+ /*-- current header got turned off too --*/
+#ifdef ATTACHMENTS
+ if(headents[ods.cur_e].is_attach){
+ /* verify the attachments */
+ if((i = FormatSyncAttach()) != 0){
+ FormatLines(headents[ods.cur_e].hd_text, "",
+ term.t_ncol - headents[ods.cur_e].prwid,
+ headents[ods.cur_e].break_on_comma, 0);
+ }
+ }
+#endif
+ if(headents[ods.cur_e].builder) /* verify text */
+ i = call_builder(&headents[ods.cur_e], &mangled, &err)>0;
+ /* Check below */
+ for(cur_e =ods.cur_e; headents[cur_e].name!=NULL; cur_e++)
+ if(!headents[cur_e].rich_header)
+ break;
+ if(headents[cur_e].name == NULL) {
+ /* didn't find one, check above */
+ for(cur_e =ods.cur_e; headents[cur_e].name!=NULL;
+ cur_e--)
+ if(!headents[cur_e].rich_header)
+ break;
+
+ }
+ ods.p_ind = 0;
+ ods.cur_e = cur_e;
+ ods.cur_l = headents[ods.cur_e].hd_text;
+ }
+ }
+
+ ods.p_line = 0; /* force update */
+ UpdateHeader(0);
+ PaintHeader(COMPOSER_TOP_LINE, FALSE);
+ PaintBody(1);
+ fix_mangle_and_err(&mangled, &err, headents[ods.cur_e].name);
+ break;
+
+ case (CTRL|'C') : /* bag whole thing ?*/
+ if(abort_composer(1, 0) == TRUE)
+ return(FALSE);
+
+ break;
+
+ case (CTRL|'X') : /* Done. Send it. */
+ i = 0;
+#ifdef ATTACHMENTS
+ if(headents[ods.cur_e].is_attach){
+ /* verify the attachments, and pretty things up in case
+ * we come back to the composer due to error...
+ */
+ if((i = FormatSyncAttach()) != 0){
+ sleep(2); /* give time for error to absorb */
+ FormatLines(headents[ods.cur_e].hd_text, "",
+ term.t_ncol - headents[ods.cur_e].prwid,
+ headents[ods.cur_e].break_on_comma, 0);
+ }
+ }
+ else
+#endif
+ mangled = 0;
+ err = NULL;
+ if(headents[ods.cur_e].builder) /* verify text? */
+ i = call_builder(&headents[ods.cur_e], &mangled, &err);
+
+ if(i < 0){ /* don't leave without a valid addr */
+ fix_mangle_and_err(&mangled, &err, headents[ods.cur_e].name);
+ break;
+ }
+ else if(i > 0){
+ ods.cur_l = headents[ods.cur_e].hd_text; /* attach cur_l */
+ ods.p_ind = 0;
+ ods.p_line = 0; /* force realignment */
+ fix_mangle_and_err(&mangled, &err, headents[ods.cur_e].name);
+ NewTop(0);
+ }
+
+ fix_mangle_and_err(&mangled, &err, headents[ods.cur_e].name);
+
+ if(wquit(1,0) == TRUE)
+ return(TRUE);
+
+ if(i > 0){
+ /*
+ * need to be careful here because pointers might be messed up.
+ * also, could do a better job of finding the right place to
+ * put the dot back (i.e., the addr/list that was expanded).
+ */
+ UpdateHeader(0);
+ PaintHeader(COMPOSER_TOP_LINE, FALSE);
+ PaintBody(1);
+ }
+ break;
+
+ case (CTRL|'Z') : /* Suspend compose */
+ if(gmode&MDSSPD){ /* is it allowed? */
+ bktoshell(0, 1);
+ PaintBody(0);
+ }
+ else
+ unknown_command(ch);
+
+ break;
+
+ case (CTRL|'\\') :
+#if defined MOUSE && !defined(_WINDOWS)
+ toggle_xterm_mouse(0,1);
+#else
+ unknown_command(ch);
+#endif
+ break;
+
+ case (CTRL|'O') : /* Suspend message */
+ if(Pmaster->pine_flags & MDHDRONLY)
+ goto bleep;
+
+ i = 0;
+ mangled = 0;
+ err = NULL;
+ if(headents[ods.cur_e].is_attach){
+ if(FormatSyncAttach() < 0){
+ if(mlyesno_utf8(_("Problem with attachments. Postpone anyway?"),
+ FALSE) != TRUE){
+ if(FormatLines(headents[ods.cur_e].hd_text, "",
+ term.t_ncol - headents[ods.cur_e].prwid,
+ headents[ods.cur_e].break_on_comma, 0) == -1)
+ emlwrite("\007Format lines failed!", NULL);
+ UpdateHeader(0);
+ PaintHeader(COMPOSER_TOP_LINE, FALSE);
+ PaintBody(1);
+ continue;
+ }
+ }
+ }
+ else if(headents[ods.cur_e].builder)
+ i = call_builder(&headents[ods.cur_e], &mangled, &err);
+
+ fix_mangle_and_err(&mangled, &err, headents[ods.cur_e].name);
+
+ if(i < 0) /* don't leave without a valid addr */
+ break;
+
+ suspend_composer(1, 0);
+ return(TRUE);
+
+#ifdef ATTACHMENTS
+ case (CTRL|'J') : /* handle attachments */
+ if(Pmaster->pine_flags & MDHDRONLY)
+ goto bleep;
+
+ { char cmt[NLINE];
+ LMLIST *lm = NULL, *lmp;
+ char buf[NLINE], *bfp;
+ int saved_km_popped;
+ size_t len;
+
+ /*
+ * Attachment questions mess with km_popped and assume
+ * it is zero to start with. If we don't set it to zero
+ * on entry, the message about mime type will be erased
+ * by PaintBody. If we don't reset it when we come back,
+ * the bottom three lines may be messed up.
+ */
+ saved_km_popped = km_popped;
+ km_popped = 0;
+
+ if(AskAttach(cmt, sizeof(cmt), &lm)){
+
+ for(lmp = lm; lmp; lmp = lmp->next){
+ size_t space;
+
+ len = lmp->dir ? strlen(lmp->dir)+1 : 0;
+ len += lmp->fname ? strlen(lmp->fname) : 0;
+
+ if(len+3 > sizeof(buf)){
+ space = len+3;
+ bfp = malloc(space*sizeof(char));
+ if(bfp == NULL){
+ emlwrite("\007Can't malloc space for filename",
+ NULL);
+ continue;
+ }
+ }
+ else{
+ bfp = buf;
+ space = sizeof(buf);
+ }
+
+ if(lmp->dir && lmp->dir[0])
+ snprintf(bfp, space, "%s%c%s", lmp->dir, C_FILESEP,
+ lmp->fname ? lmp->fname : "");
+ else
+ snprintf(bfp, space, "%s", lmp->fname ? lmp->fname : "");
+
+ (void) QuoteAttach(bfp, space);
+
+ (void) AppendAttachment(bfp, lm->size, cmt);
+
+ if(bfp != buf)
+ free(bfp);
+ }
+
+ zotlmlist(lm);
+ }
+
+ km_popped = saved_km_popped;
+ sgarbk = 1; /* clean up prompt */
+ }
+ break;
+#endif
+
+ case (CTRL|'I') : /* tab */
+ if(headents[ods.cur_e].nickcmpl != NULL){
+ char *new_nickname = NULL;
+ UCS *strng;
+ UCS *start = NULL, *end = NULL, *before_start = NULL;
+ UCS *uprefix = NULL, *up1, *up2;
+ char *prefix = NULL, *saveprefix = NULL, *insert = NULL;
+ char *ambig = NULL;
+ int offset, prefixlen, add_a_comma = 0;
+ size_t l, l1, l2;
+ int ambiguity, fallthru = 1;
+
+ strng = ods.cur_l->text;
+ offset = HeaderOffset(ods.cur_e);
+
+ if(ods.p_ind > 0
+ && (start = &strng[ods.p_ind-1])
+ && (!*(start+1)
+ || ucs4_isspace(*(start+1))
+ || *(start+1) == ',')
+ && (*start
+ && !ucs4_isspace(*start)
+ && *start != ',')){
+
+ end = start+1;
+
+ while(start > strng
+ && *(start-1)
+ && *(start-1) != ',')
+ start--;
+
+ while(ucs4_isspace(*start))
+ start++;
+
+ if(*end != ',' && ods.cur_l->next)
+ add_a_comma++;
+
+ /*
+ * Nickname prefix begins with start and ends
+ * with end-1. Replace those characters with
+ * completed nickname.
+ */
+ prefixlen = end-start;
+ uprefix = (UCS *) fs_get((prefixlen+1) * sizeof(UCS));
+ ucs4_strncpy(uprefix, start, prefixlen);
+ uprefix[prefixlen] = '\0';
+ prefix = ucs4_to_utf8_cpystr(uprefix);
+ fs_give((void **) &uprefix);
+
+ /*
+ * Ambiguity == 0 means no matches exist.
+ * 1 means it is ambiguous and the longest
+ * unambiguous prefix beginning with prefix
+ * is in new_nickname.
+ * 2 means it is unambiguous and the answer
+ * is in new_nickname
+ *
+ * The expansion from == 2 doesn't have to have prefix
+ * as a prefix. For example, if the prefix is a prefix
+ * of the full name or a prefix of a nickname the answer
+ * might be the full address instead of the expanded
+ * prefix. In fact, that's probably the expected thing.
+ *
+ * Due to the way the build_abook_tries sets up the tries
+ * it is unfortunately the case that the expanded address
+ * is not entered in any trie, so after you get an
+ * unambiguous expansion if you try TAB again at that
+ * point you'll probably get a no match returned instead
+ * of an unambiguous. So be aware of that and handle
+ * that case ok by falling through to header_downline.
+ */
+ ambiguity = (*(headents[ods.cur_e].nickcmpl))(prefix,
+ &new_nickname, (lastch == ch), 0);
+
+ /*
+ * Don't fall through on no-match TAB unless
+ * it is the second TAB.
+ */
+ if(ambiguity == 0 && lastch != ch){
+ lastch = 0;
+ break;
+ }
+
+ if(ambiguity != 1)
+ lastch = 0;
+
+ if(ambiguity == 0)
+ goto nomore_to_complete;
+
+ if(new_nickname){
+ if(strcmp(new_nickname, prefix)){
+ /*
+ * We're trying to work with the way
+ * FormatLines works. It inserts text at the
+ * beginning of the line we pass in.
+ * So, remove the beginning of the line and
+ * have FormatLines put it back.
+ */
+
+ /* save part before start */
+ fallthru = 0;
+ before_start = strng;
+ uprefix = (UCS *) fs_get((start-before_start+1) * sizeof(UCS));
+ ucs4_strncpy(uprefix, before_start, start-before_start);
+ uprefix[start-before_start] = '\0';
+ saveprefix = ucs4_to_utf8_cpystr(uprefix);
+
+ /* figure out new cursor offset */
+ up1 = utf8_to_ucs4_cpystr(new_nickname);
+ if(up1){
+ offset += (ucs4_strlen(up1) - prefixlen);
+ fs_give((void **) &up1);
+ }
+
+ fs_give((void **) &uprefix);
+
+ /*
+ * Delete everything up to end by
+ * copying characters to start of buffer.
+ */
+ up1 = before_start;
+ up2 = end;
+ for(i = ods.p_len - (end - before_start) + 1; i > 0; i--)
+ *up1++ = *up2++;
+
+ ods.p_len -= (end - before_start);
+
+ if(saveprefix){
+ l1 = strlen(saveprefix);
+ l2 = strlen(new_nickname);
+ l = l1 + l2;
+
+ /* add a comma? */
+ if(add_a_comma && ambiguity == 2){
+ l++;
+ offset++;
+ }
+ else
+ add_a_comma = 0;
+
+ insert = (char *) fs_get((l+1) * sizeof(char));
+
+ /*
+ * Insert is the before start stuff plus the
+ * new nickname, and we're going to let
+ * FormatLines put it together for us.
+ */
+ if(insert){
+ strncpy(insert, saveprefix, l);
+ strncpy(insert+l1, new_nickname, l-l1);
+ if(add_a_comma)
+ insert[l-1] = ',';
+
+ insert[l] = '\0';
+ }
+
+ fs_give((void **) &saveprefix);
+ }
+
+
+ if(insert && FormatLines(ods.cur_l, insert,
+ term.t_ncol - headents[ods.cur_e].prwid,
+ headents[ods.cur_e].break_on_comma,0)==-1){
+ emlwrite("\007Format lines failed!", NULL);
+ }
+
+ if(insert)
+ fs_give((void **) &insert);
+
+ HeaderFocus(ods.cur_e, offset);
+ }
+ else{
+ (*term.t_beep)();
+ }
+
+ ambig = new_nickname;
+ new_nickname = NULL;
+ }
+
+ if(!ambig && prefix){
+ ambig = prefix;
+ prefix = NULL;
+ }
+
+
+ if(ambiguity == 2 && fallthru){
+ if(prefix)
+ fs_give((void **) &prefix);
+
+ if(new_nickname)
+ fs_give((void **) &new_nickname);
+
+ if(ambig)
+ fs_give((void **) &ambig);
+
+ UpdateHeader(0);
+ PaintBody(0);
+ goto nomore_to_complete;
+ }
+
+ UpdateHeader(0);
+ PaintBody(0);
+
+ if(prefix)
+ fs_give((void **) &prefix);
+
+ if(new_nickname)
+ fs_give((void **) &new_nickname);
+
+ if(ambig)
+ fs_give((void **) &ambig);
+ }
+ else{
+ goto nomore_to_complete;
+ }
+
+ break;
+ }
+ else{
+nomore_to_complete:
+ ods.p_ind = 0; /* fall through... */
+ }
+
+ case (CTRL|'N') :
+ case KEY_DOWN :
+ header_downline(!hdr_only, hdr_only);
+ break;
+
+ case (CTRL|'P') :
+ case KEY_UP :
+ header_upline(1);
+ break;
+
+ case (CTRL|'V') : /* down a page */
+ case KEY_PGDN :
+ cur_e = ods.cur_e;
+ if(!next_sel_hline(&cur_e, ods.cur_l)){
+ header_downline(!hdr_only, hdr_only);
+ if(!(gmode & MDHDRONLY))
+ retval = -1; /* tell caller we fell out */
+ }
+ else{
+ int move_down, bot_pline;
+ struct hdr_line *new_cur_l, *line, *next_line, *prev_line;
+
+ move_down = BOTTOM() - 2 - ods.p_line;
+ if(move_down < 0)
+ move_down = 0;
+
+ /*
+ * Count down move_down lines to find the pointer to the line
+ * that we want to become the current line.
+ */
+ new_cur_l = ods.cur_l;
+ cur_e = ods.cur_e;
+ for(i = 0; i < move_down; i++){
+ next_line = next_hline(&cur_e, new_cur_l);
+ if(!next_line)
+ break;
+
+ new_cur_l = next_line;
+ }
+
+ if(headents[cur_e].blank){
+ next_line = next_sel_hline(&cur_e, new_cur_l);
+ if(!next_line)
+ break;
+
+ new_cur_l = next_line;
+ }
+
+ /*
+ * Now call header_downline until we get down to the
+ * new current line, so that the builders all get called.
+ * New_cur_l will remain valid since we won't be calling
+ * a builder for it during this loop.
+ */
+ while(ods.cur_l != new_cur_l && header_downline(0, 0))
+ ;
+
+ /*
+ * Count back up, if we're at the bottom, to find the new
+ * top line.
+ */
+ cur_e = ods.cur_e;
+ if(next_hline(&cur_e, ods.cur_l) == NULL){
+ /*
+ * Cursor stops at bottom of headers, which is where
+ * we are right now. Put as much of headers on
+ * screen as will fit. Count up to figure
+ * out which line is top_l and which p_line cursor is on.
+ */
+ cur_e = ods.cur_e;
+ line = ods.cur_l;
+ /* leave delimiter on screen, too */
+ bot_pline = BOTTOM() - 1 - ((gmode & MDHDRONLY) ? 0 : 1);
+ for(i = COMPOSER_TOP_LINE; i < bot_pline; i++){
+ prev_line = prev_hline(&cur_e, line);
+ if(!prev_line)
+ break;
+
+ line = prev_line;
+ }
+
+ ods.top_l = line;
+ ods.top_e = cur_e;
+ ods.p_line = i;
+
+ }
+ else{
+ ods.top_l = ods.cur_l;
+ ods.top_e = ods.cur_e;
+ /*
+ * We don't want to scroll down further than the
+ * delimiter, so check to see if that is the case.
+ * If it is, we move the p_line down the screen
+ * until the bottom line is where we want it.
+ */
+ bot_pline = BOTTOM() - 1 - ((gmode & MDHDRONLY) ? 0 : 1);
+ cur_e = ods.cur_e;
+ line = ods.cur_l;
+ for(i = bot_pline; i > COMPOSER_TOP_LINE; i--){
+ next_line = next_hline(&cur_e, line);
+ if(!next_line)
+ break;
+
+ line = next_line;
+ }
+
+ /*
+ * i is the desired value of p_line.
+ * If it is greater than COMPOSER_TOP_LINE, then
+ * we need to adjust top_l.
+ */
+ ods.p_line = i;
+ line = ods.top_l;
+ cur_e = ods.top_e;
+ for(; i > COMPOSER_TOP_LINE; i--){
+ prev_line = prev_hline(&cur_e, line);
+ if(!prev_line)
+ break;
+
+ line = prev_line;
+ }
+
+ ods.top_l = line;
+ ods.top_e = cur_e;
+
+ /*
+ * Special case. If p_line is within one of the bottom,
+ * move it to the bottom.
+ */
+ if(ods.p_line == bot_pline - 1){
+ header_downline(0, 0);
+ /* but put these back where we want them */
+ ods.p_line = bot_pline;
+ ods.top_l = line;
+ ods.top_e = cur_e;
+ }
+ }
+
+ UpdateHeader(0);
+ PaintHeader(COMPOSER_TOP_LINE, FALSE);
+ PaintBody(1);
+ }
+
+ break;
+
+ case (CTRL|'Y') : /* up a page */
+ case KEY_PGUP :
+ for(i = 0; header_upline(0) && i <= FULL_SCR(); i++)
+ if(i < 0)
+ break;
+
+ break;
+
+#ifdef MOUSE
+ case KEY_MOUSE:
+ mouse_get_last (NULL, &mp);
+ switch(mp.button){
+ case M_BUTTON_LEFT :
+ if (!mp.doubleclick) {
+ if (mp.row < ods.p_line) {
+ for (i = ods.p_line - mp.row;
+ i > 0 && header_upline(0);
+ --i)
+ ;
+ }
+ else {
+ for (i = mp.row-ods.p_line;
+ i > 0 && header_downline(!hdr_only, 0);
+ --i)
+ ;
+ }
+
+ if((ods.p_ind = mp.col - headents[ods.cur_e].prwid) <= 0)
+ ods.p_ind = 0;
+
+ /* -3 is returned if we drop out bottom
+ * *and* want to process a mousepress. The Headereditor
+ * wrapper should make sense of this return code.
+ */
+ if (ods.p_line >= ComposerTopLine)
+ retval = -3;
+ }
+
+ break;
+
+ case M_BUTTON_RIGHT :
+#ifdef _WINDOWS
+ pico_popup();
+#endif
+ case M_BUTTON_MIDDLE :
+ default : /* NOOP */
+ break;
+ }
+
+ break;
+#endif /* MOUSE */
+
+ case (CTRL|'T') : /* Call field selector */
+ errmss = NULL;
+ if(headents[ods.cur_e].is_attach) {
+ /*--- selector for attachments ----*/
+ char dir[NLINE], fn[NLINE], sz[NLINE];
+ LMLIST *lm = NULL, *lmp;
+
+ strncpy(dir, (gmode & MDCURDIR)
+ ? (browse_dir[0] ? browse_dir : ".")
+ : ((gmode & MDTREE) || opertree[0])
+ ? opertree
+ : (browse_dir[0] ? browse_dir
+ : gethomedir(NULL)), sizeof(dir));
+ dir[sizeof(dir)-1] = '\0';
+ fn[0] = sz[0] = '\0';
+ if(FileBrowse(dir, sizeof(dir), fn, sizeof(fn), sz, sizeof(sz),
+ FB_READ | FB_ATTACH | FB_LMODEPOS, &lm) == 1){
+ char buf[NLINE], *bfp;
+ size_t len;
+
+ for(lmp = lm; lmp; lmp = lmp->next){
+ size_t space;
+
+ len = lmp->dir ? strlen(lmp->dir)+1 : 0;
+ len += lmp->fname ? strlen(lmp->fname) : 0;
+ len += 7;
+ len += strlen(lmp->size);
+
+ if(len+3 > sizeof(buf)){
+ space = len+3;
+ bfp = malloc(space*sizeof(char));
+ if(bfp == NULL){
+ emlwrite("\007Can't malloc space for filename",
+ NULL);
+ continue;
+ }
+ }
+ else{
+ bfp = buf;
+ space = sizeof(buf);
+ }
+
+ if(lmp->dir && lmp->dir[0])
+ snprintf(bfp, space, "%s%c%s", lmp->dir, C_FILESEP,
+ lmp->fname ? lmp->fname : "");
+ else
+ snprintf(bfp, space, "%s", lmp->fname ? lmp->fname : "");
+
+ (void) QuoteAttach(bfp, space);
+
+ snprintf(bfp + strlen(bfp), space-strlen(bfp), " (%s) \"\"%s", lmp->size,
+ (!headents[ods.cur_e].hd_text->text[0]) ? "":",");
+
+ if(FormatLines(headents[ods.cur_e].hd_text, bfp,
+ term.t_ncol - headents[ods.cur_e].prwid,
+ headents[ods.cur_e].break_on_comma,0)==-1){
+ emlwrite("\007Format lines failed!", NULL);
+ }
+
+ if(bfp != buf)
+ free(bfp);
+
+ UpdateHeader(0);
+ }
+
+ zotlmlist(lm);
+ } /* else, nothing of interest */
+ } else if (headents[ods.cur_e].selector != NULL) {
+ VARS_TO_SAVE *saved_state;
+
+ /*---- General selector for non-attachments -----*/
+
+ /*
+ * Since the selector may make a new call back to pico()
+ * we need to save and restore the pico state here.
+ */
+ if((saved_state = save_pico_state()) != NULL){
+ bufp = (*(headents[ods.cur_e].selector))(&errmss);
+ restore_pico_state(saved_state);
+ free_pico_state(saved_state);
+ ttresize(); /* fixup screen bufs */
+ picosigs(); /* restore altered signals */
+ }
+ else{
+ char *s = "Not enough memory";
+ size_t len;
+
+ len = strlen(s);
+ errmss = (char *)malloc((len+1) * sizeof(char));
+ strncpy(errmss, s, len+1);
+ errmss[len] = '\0';
+ bufp = NULL;
+ }
+
+ if(bufp != NULL) {
+ mangled = 0;
+ err = NULL;
+ if(headents[ods.cur_e].break_on_comma) {
+ /*--- Must be an address ---*/
+
+ /*
+ * If current line is empty and there are more
+ * lines that follow, delete the empty lines
+ * before adding the new address.
+ */
+ if(ods.cur_l->text[0] == '\0' && ods.cur_l->next){
+ do {
+ KillHeaderLine(ods.cur_l, 1);
+ ods.p_len = ucs4_strlen(ods.cur_l->text);
+ } while(ods.cur_l->text[0] == '\0' && ods.cur_l->next);
+ }
+
+ ods.p_ind = 0;
+
+ if(ods.cur_l->text[0] != '\0'){
+ struct hdr_line *h, *start_of_addr;
+ int q = 0;
+
+ /* cur is not first line */
+ if(ods.cur_l != headents[ods.cur_e].hd_text){
+ /*
+ * Protect against adding a new entry into
+ * the middle of a long, continued entry.
+ */
+ start_of_addr = NULL; /* cur_l is a good place to be */
+ q = 0;
+ for(h = headents[ods.cur_e].hd_text; h; h = h->next){
+ if(ucs4_strqchr(h->text, ',', &q, -1)){
+ start_of_addr = NULL;
+ q = 0;
+ }
+ else if(start_of_addr == NULL)
+ start_of_addr = h;
+
+ if(h->next == ods.cur_l)
+ break;
+ }
+
+ if(start_of_addr){
+ ods.cur_l = start_of_addr;
+ ods.p_len = ucs4_strlen(ods.cur_l->text);
+ }
+ }
+
+ for(i = ++ods.p_len; i; i--)
+ ods.cur_l->text[i] = ods.cur_l->text[i-1];
+
+ ods.cur_l->text[0] = ',';
+ }
+
+ if(FormatLines(ods.cur_l, bufp,
+ (term.t_ncol-headents[ods.cur_e].prwid),
+ headents[ods.cur_e].break_on_comma, 0) == -1){
+ emlwrite("Problem adding address to header !",
+ NULL);
+ (*term.t_beep)();
+ break;
+ }
+
+ /*
+ * If the "selector" has a "builder" as well, pass
+ * what was just selected thru the builder...
+ */
+ if(headents[ods.cur_e].builder){
+ struct hdr_line *l;
+ int cur_row, top_too = 0;
+
+ for(l = headents[ods.cur_e].hd_text, cur_row = 0;
+ l && l != ods.cur_l;
+ l = l->next, cur_row++)
+ ;
+
+ top_too = headents[ods.cur_e].hd_text == ods.top_l;
+
+ if(call_builder(&headents[ods.cur_e], &mangled,
+ &err) < 0){
+ fix_mangle_and_err(&mangled, &err,
+ headents[ods.cur_e].name);
+ }
+
+ for(ods.cur_l = headents[ods.cur_e].hd_text;
+ ods.cur_l->next && cur_row;
+ ods.cur_l = ods.cur_l->next, cur_row--)
+ ;
+
+ if(top_too)
+ ods.top_l = headents[ods.cur_e].hd_text;
+ }
+
+ UpdateHeader(0);
+ } else {
+ UCS *u;
+
+ u = utf8_to_ucs4_cpystr(bufp);
+ if(u){
+ ucs4_strncpy(headents[ods.cur_e].hd_text->text, u, HLSZ);
+ headents[ods.cur_e].hd_text->text[HLSZ-1] = '\0';
+ fs_give((void **) &u);
+ }
+ }
+
+ free(bufp);
+ /* mark this entry dirty */
+ mark_sticky(&headents[ods.cur_e]);
+ headents[ods.cur_e].dirty = 1;
+ fix_mangle_and_err(&mangled,&err,headents[ods.cur_e].name);
+ }
+ } else {
+ /*----- No selector -----*/
+ (*term.t_beep)();
+ continue;
+ }
+
+ PaintBody(0);
+ if(errmss != NULL) {
+ (*term.t_beep)();
+ emlwrite(errmss, NULL);
+ free(errmss);
+ errmss = NULL;
+ }
+ continue;
+
+ case (CTRL|'G'): /* HELP */
+ if(term.t_mrow == 0){
+ if(km_popped == 0){
+ km_popped = 2;
+ sgarbk = 1; /* bring up menu */
+ break;
+ }
+ }
+
+ if(!ComposerHelp(ods.cur_e))
+ break; /* else, fall through... */
+
+ case (CTRL|'L'): /* redraw requested */
+ PaintBody(0);
+ break;
+
+ case (CTRL|'_'): /* file editor */
+ if(headents[ods.cur_e].fileedit != NULL){
+ struct headerentry *e;
+ struct hdr_line *line;
+ int sz = 0;
+ char *filename = NULL;
+ VARS_TO_SAVE *saved_state;
+
+ /*
+ * Since the fileedit will make a new call back to pico()
+ * we need to save and restore the pico state here.
+ */
+ if((saved_state = save_pico_state()) != NULL){
+ UCS *u;
+
+ e = &headents[ods.cur_e];
+
+ for(line = e->hd_text; line != NULL; line = line->next)
+ sz += ucs4_strlen(line->text);
+
+ u = (UCS *)malloc((sz+1) * sizeof(*u));
+ if(u){
+ u[0] = '\0';
+ for(line = e->hd_text; line != NULL; line = line->next)
+ ucs4_strncat(u, line->text, sz+1-ucs4_strlen(u)-1);
+
+ filename = ucs4_to_utf8_cpystr(u);
+ free(u);
+ }
+
+ errmss = (*(headents[ods.cur_e].fileedit))(filename);
+
+ if(filename)
+ free(filename);
+
+ restore_pico_state(saved_state);
+ free_pico_state(saved_state);
+ ttresize(); /* fixup screen bufs */
+ picosigs(); /* restore altered signals */
+ }
+ else{
+ char *s = "Not enough memory";
+ size_t len;
+
+ len = strlen(s);
+ errmss = (char *)malloc((len+1) * sizeof(char));
+ strncpy(errmss, s, len+1);
+ errmss[len] = '\0';
+ }
+
+ PaintBody(0);
+
+ if(errmss != NULL) {
+ (*term.t_beep)();
+ emlwrite(errmss, NULL);
+ free(errmss);
+ errmss = NULL;
+ }
+
+ continue;
+ }
+ else
+ goto bleep;
+
+ break;
+
+ default : /* huh? */
+bleep:
+ unknown_command(ch);
+
+ case NODATA:
+ break;
+ }
+ }
+ while (ods.p_line < ComposerTopLine);
+
+ display_delimiter(1);
+ curwp->w_flag |= WFMODE;
+ movecursor(currow, curcol);
+ ComposerEditing = FALSE;
+ if (ComposerTopLine == BOTTOM()){
+ UpdateHeader(0);
+ PaintHeader(COMPOSER_TOP_LINE, FALSE);
+ PaintBody(1);
+ }
+
+ return(retval);
+}
+
+
+/*
+ *
+ */
+int
+header_downline(int beyond, int gripe)
+{
+ struct hdr_line *new_l, *l;
+ int new_e, status, fullpaint, len, e, incr = 0;
+
+ /* calculate the next line: physical *and* logical */
+ status = 0;
+ new_e = ods.cur_e;
+ if((new_l = next_sel_hline(&new_e, ods.cur_l)) == NULL && !beyond){
+
+ if(gripe){
+ char xx[81];
+
+ strncpy(xx, "Can't move down. Use ^X to ", sizeof(xx));
+ xx[sizeof(xx)-1] = '\0';
+ strncat(xx, (Pmaster && Pmaster->exit_label)
+ ? Pmaster->exit_label
+ : (gmode & MDHDRONLY)
+ ? "eXit/Save"
+ : (gmode & MDVIEW)
+ ? "eXit"
+ : "Send", sizeof(xx)-strlen(xx)-1);
+ xx[sizeof(xx)-1] = '\0';
+ strncat(xx, ".", sizeof(xx)-strlen(xx)-1);
+ xx[sizeof(xx)-1] = '\0';
+ emlwrite(xx, NULL);
+ }
+
+ return(0);
+ }
+
+ /*
+ * Because of blank header lines the cursor may need to move down
+ * more than one line. Figure out how far.
+ */
+ e = ods.cur_e;
+ l = ods.cur_l;
+ while(l != new_l){
+ if((l = next_hline(&e, l)) != NULL)
+ incr++;
+ else
+ break; /* can't happen */
+ }
+
+ ods.p_line += incr;
+ fullpaint = ods.p_line >= BOTTOM(); /* force full redraw? */
+
+ /* expand what needs expanding */
+ if(new_e != ods.cur_e || !new_l){ /* new (or last) field ! */
+ if(new_l)
+ InvertPrompt(ods.cur_e, FALSE); /* turn off current entry */
+
+ if(headents[ods.cur_e].is_attach) { /* verify data ? */
+ if((status = FormatSyncAttach()) != 0){ /* fixup if 1 or -1 */
+ headents[ods.cur_e].rich_header = 0;
+ if(FormatLines(headents[ods.cur_e].hd_text, "",
+ term.t_ncol-headents[new_e].prwid,
+ headents[ods.cur_e].break_on_comma, 0) == -1)
+ emlwrite("\007Format lines failed!", NULL);
+ }
+ } else if(headents[ods.cur_e].builder) { /* expand addresses */
+ int mangled = 0;
+ char *err = NULL;
+
+ if((status = call_builder(&headents[ods.cur_e], &mangled, &err))>0){
+ struct hdr_line *l; /* fixup ods.cur_l */
+ ods.p_line = 0; /* force top line recalc */
+ for(l = headents[ods.cur_e].hd_text; l; l = l->next)
+ ods.cur_l = l;
+
+ if(new_l) /* if new_l, force validity */
+ new_l = headents[new_e].hd_text;
+
+ NewTop(0); /* get new top_l */
+ }
+ else if(status < 0){ /* bad addr? no leave! */
+ --ods.p_line;
+ fix_mangle_and_err(&mangled, &err, headents[ods.cur_e].name);
+ InvertPrompt(ods.cur_e, TRUE);
+ return(0);
+ }
+
+ fix_mangle_and_err(&mangled, &err, headents[ods.cur_e].name);
+ }
+
+ if(new_l){ /* if one below, turn it on */
+ InvertPrompt(new_e, TRUE);
+ sgarbk = 1; /* paint keymenu too */
+ }
+ }
+
+ if(new_l){ /* fixup new pointers */
+ ods.cur_l = (ods.cur_e != new_e) ? headents[new_e].hd_text : new_l;
+ ods.cur_e = new_e;
+ if(ods.p_ind > (len = ucs4_strlen(ods.cur_l->text)))
+ ods.p_ind = len;
+ }
+
+ if(!new_l || status || fullpaint){ /* handle big screen paint */
+ UpdateHeader(0);
+ PaintHeader(COMPOSER_TOP_LINE, FALSE);
+ PaintBody(1);
+
+ if(!new_l){ /* make sure we're done */
+ ods.p_line = ComposerTopLine;
+ InvertPrompt(ods.cur_e, FALSE); /* turn off current entry */
+ }
+ }
+
+ return(new_l ? 1 : 0);
+}
+
+
+/*
+ *
+ */
+int
+header_upline(int gripe)
+{
+ struct hdr_line *new_l, *l;
+ int new_e, status, fullpaint, len, e, incr = 0;
+ EML eml;
+
+ /* calculate the next line: physical *and* logical */
+ status = 0;
+ new_e = ods.cur_e;
+ if(!(new_l = prev_sel_hline(&new_e, ods.cur_l))){ /* all the way up! */
+ ods.p_line = COMPOSER_TOP_LINE;
+ if(gripe){
+ eml.s = (Pmaster->pine_flags & MDHDRONLY) ? "entry" : "header";
+ emlwrite(_("Can't move beyond top of %s"), &eml);
+ }
+
+ return(0);
+ }
+
+ /*
+ * Because of blank header lines the cursor may need to move up
+ * more than one line. Figure out how far.
+ */
+ e = ods.cur_e;
+ l = ods.cur_l;
+ while(l != new_l){
+ if((l = prev_hline(&e, l)) != NULL)
+ incr++;
+ else
+ break; /* can't happen */
+ }
+
+ ods.p_line -= incr;
+ fullpaint = ods.p_line <= COMPOSER_TOP_LINE;
+
+ if(new_e != ods.cur_e){ /* new field ! */
+ InvertPrompt(ods.cur_e, FALSE);
+ if(headents[ods.cur_e].is_attach){
+ if((status = FormatSyncAttach()) != 0){ /* non-zero ? reformat field */
+ headents[ods.cur_e].rich_header = 0;
+ if(FormatLines(headents[ods.cur_e].hd_text, "",
+ term.t_ncol - headents[ods.cur_e].prwid,
+ headents[ods.cur_e].break_on_comma,0) == -1)
+ emlwrite("\007Format lines failed!", NULL);
+ }
+ }
+ else if(headents[ods.cur_e].builder){
+ int mangled = 0;
+ char *err = NULL;
+
+ if((status = call_builder(&headents[ods.cur_e], &mangled,
+ &err)) >= 0){
+ /* repair new_l */
+ for(new_l = headents[new_e].hd_text;
+ new_l->next;
+ new_l=new_l->next)
+ ;
+
+ /* and cur_l (required in fix_and... */
+ ods.cur_l = new_l;
+ }
+ else{
+ ++ods.p_line;
+ fix_mangle_and_err(&mangled, &err, headents[ods.cur_e].name);
+ InvertPrompt(ods.cur_e, TRUE);
+ return(0);
+ }
+
+ fix_mangle_and_err(&mangled, &err, headents[ods.cur_e].name);
+ }
+
+ InvertPrompt(new_e, TRUE);
+ sgarbk = 1;
+ }
+
+ ods.cur_e = new_e; /* update pointers */
+ ods.cur_l = new_l;
+ if(ods.p_ind > (len = ucs4_strlen(ods.cur_l->text)))
+ ods.p_ind = len;
+
+ if(status > 0 || fullpaint){
+ UpdateHeader(0);
+ PaintHeader(COMPOSER_TOP_LINE, FALSE);
+ PaintBody(1);
+ }
+
+ return(1);
+}
+
+
+/*
+ *
+ */
+int
+AppendAttachment(char *fn, char *sz, char *cmt)
+{
+ int a_e, status, spaces;
+ struct hdr_line *lp;
+ char b[256];
+ UCS *u;
+
+ /*--- Find headerentry that is attachments (only first) --*/
+ for(a_e = 0; headents[a_e].name != NULL; a_e++ )
+ if(headents[a_e].is_attach){
+ /* make sure field stays displayed */
+ headents[a_e].rich_header = 0;
+ headents[a_e].display_it = 1;
+ break;
+ }
+
+ /* append new attachment line */
+ for(lp = headents[a_e].hd_text; lp->next; lp=lp->next)
+ ;
+
+ /* build new attachment line */
+ if(lp->text[0]){ /* adding a line? */
+ UCS comma[2];
+
+ comma[0] = ',';
+ comma[1] = '\0';
+ ucs4_strncat(lp->text, comma, HLSZ-ucs4_strlen(lp->text)-1); /* append delimiter */
+ if((lp->next = HALLOC()) != NULL){ /* allocate new line */
+ lp->next->prev = lp;
+ lp->next->next = NULL;
+ lp = lp->next;
+ }
+ else{
+ emlwrite("\007Can't allocate line for new attachment!", NULL);
+ return(0);
+ }
+ }
+
+
+ spaces = (*fn == '\"') ? 0 : (strpbrk(fn, "(), \t") != NULL);
+ snprintf(b, sizeof(b), "%s%s%s (%s) \"%.*s\"",
+ spaces ? "\"" : "", fn, spaces ? "\"" : "",
+ sz ? sz : "", 80, cmt ? cmt : "");
+ u = utf8_to_ucs4_cpystr(b);
+ if(u){
+ ucs4_strncpy(lp->text, u, HLSZ);
+ lp->text[HLSZ-1] = '\0';
+ fs_give((void **) &u);
+ }
+
+ /* validate the new attachment, and reformat if needed */
+ if((status = SyncAttach()) != 0){
+ EML eml;
+
+ if(status < 0){
+ eml.s = fn;
+ emlwrite("\007Problem attaching: %s", &eml);
+ }
+
+ if(FormatLines(headents[a_e].hd_text, "",
+ term.t_ncol - headents[a_e].prwid,
+ headents[a_e].break_on_comma, 0) == -1){
+ emlwrite("\007Format lines failed!", NULL);
+ return(0);
+ }
+ }
+
+ UpdateHeader(0);
+ PaintHeader(COMPOSER_TOP_LINE, status != 0);
+ PaintBody(1);
+ return(status != 0);
+}
+
+
+/*
+ * LineEdit - Always use insert mode and handle line wrapping
+ *
+ * returns:
+ * Any characters typed in that aren't printable
+ * (i.e. commands)
+ *
+ * notes:
+ * Assume we are guaranteed that there is sufficiently
+ * more buffer space in a line than screen width (just one
+ * less thing to worry about). If you want to change this,
+ * then pputc will have to be taught to check the line buffer
+ * length, and HALLOC() will probably have to become a func.
+ */
+UCS
+LineEdit(int allowedit, UCS *lastch)
+{
+ register struct hdr_line *lp; /* temporary line pointer */
+ register int i;
+ UCS ch = 0;
+ register int status; /* various func's return val */
+ UCS *tbufp; /* temporary buffer pointers */
+ int skipmove = 0;
+ UCS *strng;
+ UCS last_key; /* last keystroke */
+
+ strng = ods.cur_l->text; /* initialize offsets */
+ ods.p_len = MIN(ucs4_strlen(strng), HLSZ);
+ if(ods.p_ind < 0) /* offset within range? */
+ ods.p_ind = 0;
+ else if(ods.p_ind > ods.p_len)
+ ods.p_ind = ods.p_len;
+ else if(ucs4_str_width_ptr_to_ptr(&strng[0], &strng[ods.p_ind]) > LINEWID()){
+ UCS *u;
+
+ u = ucs4_particular_width(strng, LINEWID());
+ ods.p_ind = u - strng;
+ }
+
+ while(1){ /* edit the line... */
+
+ if(Pmaster && subject_line != NULL
+ && ods.cur_l == subject_line
+ && ods.cur_l->text[0] == 0)
+ (*Pmaster->newthread)();
+
+ if(skipmove)
+ skipmove = 0;
+ else
+ HeaderPaintCursor();
+
+ last_key = ch;
+ if(ch && lastch)
+ *lastch = ch;
+
+ (*term.t_flush)(); /* get everything out */
+
+#ifdef MOUSE
+ mouse_in_content(KEY_MOUSE, -1, -1, 0, 0);
+ register_mfunc(mouse_in_content,2,0,term.t_nrow-(term.t_mrow+1),
+ term.t_ncol);
+#endif
+#ifdef _WINDOWS
+ mswin_setdndcallback (composer_file_drop);
+ mswin_mousetrackcallback(pico_cursor);
+#endif
+
+ ch = GetKey();
+
+ if (term.t_nrow < 6 && ch != NODATA){
+ (*term.t_beep)();
+ emlwrite(_("Please make the screen larger."), NULL);
+ continue;
+ }
+
+#ifdef MOUSE
+ clear_mfunc(mouse_in_content);
+#endif
+#ifdef _WINDOWS
+ mswin_cleardndcallback ();
+ mswin_mousetrackcallback(NULL);
+#endif
+
+ switch(ch){
+ case DEL :
+ if(gmode & P_DELRUBS)
+ ch = KEY_DEL;
+
+ default :
+ (*Pmaster->keybinput)();
+ if(!time_to_check())
+ break;
+
+ case NODATA : /* new mail ? */
+ if((*Pmaster->newmail)(ch == NODATA ? 0 : 2, 1) >= 0){
+ int rv;
+
+ if(km_popped){
+ term.t_mrow = 2;
+ curwp->w_ntrows -= 2;
+ }
+
+ clearcursor();
+ mlerase();
+ rv = (*Pmaster->showmsg)(ch);
+ ttresize();
+ picosigs();
+ if(rv) /* Did showmsg corrupt the display? */
+ PaintBody(0); /* Yes, repaint */
+
+ mpresf = 1;
+ if(km_popped){
+ term.t_mrow = 0;
+ curwp->w_ntrows += 2;
+ }
+ }
+
+ clearcursor();
+ movecursor(ods.p_line,
+ ucs4_str_width_ptr_to_ptr(strng, &strng[ods.p_ind])+headents[ods.cur_e].prwid);
+ if(ch == NODATA) /* GetKey timed out */
+ continue;
+
+ break;
+ }
+
+ if(mpresf){ /* blast old messages */
+ if(mpresf++ > NMMESSDELAY){ /* every few keystrokes */
+ mlerase();
+ movecursor(ods.p_line,
+ ucs4_str_width_ptr_to_ptr(strng, &strng[ods.p_ind])+headents[ods.cur_e].prwid);
+ }
+ }
+
+ tbufp = &strng[ods.p_len];
+
+ if(VALID_KEY(ch)){ /* char input */
+ /*
+ * if we are allowing editing, insert the new char
+ * end up leaving tbufp pointing to newly
+ * inserted character in string, and offset to the
+ * index of the character after the inserted ch ...
+ */
+ if(allowedit){
+ if(headents[ods.cur_e].is_attach && intag(strng,ods.p_ind)){
+ emlwrite(_("\007Can't edit attachment number!"), NULL);
+ continue;
+ }
+
+ if(headents[ods.cur_e].single_space){
+ if(ch == ' '
+ && (strng[ods.p_ind]==' ' || strng[ods.p_ind-1]==' '))
+ continue;
+ }
+
+ /*
+ * go ahead and add the character...
+ */
+ if(ods.p_len < HLSZ){
+ tbufp = &strng[++ods.p_len]; /* find the end */
+ do{
+ *tbufp = tbufp[-1];
+ } while(--tbufp > &strng[ods.p_ind]); /* shift right */
+ strng[ods.p_ind++] = ch; /* add char to str */
+ }
+
+ /* mark this entry dirty */
+ mark_sticky(&headents[ods.cur_e]);
+ headents[ods.cur_e].dirty = 1;
+
+ /*
+ * then find out where things fit...
+ */
+ if(ucs4_str_width_ptr_to_ptr(&strng[0], &strng[ods.p_len]) < LINEWID()){
+ CELL c;
+
+ c.c = ch & CELLMASK;
+ c.a = 0;
+ if(pinsert(c)){ /* add char to str */
+ skipmove++; /* must'a been optimal */
+ continue; /* on to the next! */
+ }
+ }
+ else{
+ if((status = FormatLines(ods.cur_l, "", LINEWID(),
+ headents[ods.cur_e].break_on_comma,0)) == -1){
+ (*term.t_beep)();
+ continue;
+ }
+ else{
+ /*
+ * during the format, the dot may have moved
+ * down to the next line...
+ */
+ if(ods.p_ind >= ucs4_strlen(strng)){
+ ods.p_line++;
+ ods.p_ind -= ucs4_strlen(strng);
+ ods.cur_l = ods.cur_l->next;
+ strng = ods.cur_l->text;
+ }
+
+ ods.p_len = ucs4_strlen(strng);
+ }
+
+ UpdateHeader(0);
+ PaintHeader(COMPOSER_TOP_LINE, FALSE);
+ PaintBody(1);
+ continue;
+ }
+ }
+ else{
+ rdonly();
+ continue;
+ }
+ }
+ else { /* interpret ch as a command */
+ switch (ch = normalize_cmd(ch, ckm, 2)) {
+ case (CTRL|KEY_LEFT): /* word skip left */
+ if(ods.p_ind > 0) /* Scoot one char left if possible */
+ ods.p_ind--;
+
+ if(ods.p_ind == 0)
+ {
+ if(ods.p_line != COMPOSER_TOP_LINE)
+ ods.p_ind = 1000; /* put cursor at end of line */
+ return(KEY_UP);
+ }
+
+ while(ods.p_ind > 0 && !ucs4_isalnum(strng[ods.p_ind]))
+ ods.p_ind--; /* skip any whitespace we're in */
+
+ while(ods.p_ind > 0) {
+ /* Bail if the character right before this one is whitespace */
+ if(ods.p_ind > 1 && !ucs4_isalnum(strng[ods.p_ind - 1]))
+ break;
+ ods.p_ind--;
+ }
+ continue;
+
+ case (CTRL|'@') : /* word skip */
+ case (CTRL|KEY_RIGHT):
+ while(ucs4_isalnum(strng[ods.p_ind]))
+ ods.p_ind++; /* skip any text we're in */
+
+ while(strng[ods.p_ind] && !ucs4_isalnum(strng[ods.p_ind]))
+ ods.p_ind++; /* skip any whitespace after it */
+
+ if(strng[ods.p_ind] == '\0'){
+ ods.p_ind = 0; /* end of line, let caller handle it */
+ return(KEY_DOWN);
+ }
+
+ continue;
+
+ case (CTRL|'K') : /* kill line cursor's on */
+ if(!allowedit){
+ rdonly();
+ continue;
+ }
+
+ lp = ods.cur_l;
+ if (!(gmode & MDDTKILL))
+ ods.p_ind = 0;
+
+ if(KillHeaderLine(lp, (last_key == (CTRL|'K')))){
+ if(TERM_OPTIMIZE &&
+ !(ods.cur_l->prev==NULL && ods.cur_l->next==NULL))
+ scrollup(wheadp, ods.p_line, 1);
+
+ if(ods.cur_l->next == NULL)
+ if(zotcomma(ods.cur_l->text)){
+ if(ods.p_ind > 0)
+ ods.p_ind = ucs4_strlen(ods.cur_l->text);
+ }
+
+ i = (ods.p_line == COMPOSER_TOP_LINE);
+ UpdateHeader(0);
+ PaintHeader(COMPOSER_TOP_LINE, TRUE);
+
+ if(km_popped){
+ km_popped--;
+ movecursor(term.t_nrow, 0);
+ peeol();
+ }
+
+ PaintBody(1);
+
+ }
+ strng = ods.cur_l->text;
+ ods.p_len = ucs4_strlen(strng);
+ headents[ods.cur_e].sticky = 0;
+ headents[ods.cur_e].dirty = 1;
+ continue;
+
+ case (CTRL|'U') : /* un-delete deleted lines */
+ if(!allowedit){
+ rdonly();
+ continue;
+ }
+
+ if(SaveHeaderLines()){
+ UpdateHeader(0);
+ PaintHeader(COMPOSER_TOP_LINE, FALSE);
+ if(km_popped){
+ km_popped--;
+ movecursor(term.t_nrow, 0);
+ peeol();
+ }
+
+ PaintBody(1);
+ strng = ods.cur_l->text;
+ ods.p_len = ucs4_strlen(strng);
+ mark_sticky(&headents[ods.cur_e]);
+ headents[ods.cur_e].dirty = 1;
+ }
+ else
+ /* TRANSLATORS: Killing text is deleting it and
+ Unkilling text is undeleting killed text. */
+ emlwrite(_("Problem Unkilling text"), NULL);
+ continue;
+
+ case (CTRL|'F') :
+ case KEY_RIGHT: /* move character right */
+ if(ods.p_ind < ods.p_len){
+ ods.p_ind++;
+ continue;
+ }
+ else if(gmode & MDHDRONLY)
+ continue;
+
+ ods.p_ind = 0;
+ return(KEY_DOWN);
+
+ case (CTRL|'B') :
+ case KEY_LEFT : /* move character left */
+ if(ods.p_ind > 0){
+ ods.p_ind--;
+ continue;
+ }
+ if(ods.p_line != COMPOSER_TOP_LINE)
+ ods.p_ind = 1000; /* put cursor at end of line */
+ return(KEY_UP);
+
+ case (CTRL|'M') : /* goto next field */
+ ods.p_ind = 0;
+ return(KEY_DOWN);
+
+ case KEY_HOME :
+ case (CTRL|'A') : /* goto beginning of line */
+ ods.p_ind = 0;
+ continue;
+
+ case KEY_END :
+ case (CTRL|'E') : /* goto end of line */
+ ods.p_ind = ods.p_len;
+ continue;
+
+ case (CTRL|'D') : /* blast this char */
+ case KEY_DEL :
+ if(!allowedit){
+ rdonly();
+ continue;
+ }
+ else if(ods.p_ind >= ucs4_strlen(strng))
+ continue;
+
+ if(headents[ods.cur_e].is_attach && intag(strng, ods.p_ind)){
+ emlwrite(_("\007Can't edit attachment number!"), NULL);
+ continue;
+ }
+
+ pputc(strng[ods.p_ind++], 0); /* drop through and rubout */
+
+ case DEL : /* blast previous char */
+ case (CTRL|'H') :
+ if(!allowedit){
+ rdonly();
+ continue;
+ }
+
+ if(headents[ods.cur_e].is_attach && intag(strng, ods.p_ind-1)){
+ emlwrite(_("\007Can't edit attachment number!"), NULL);
+ continue;
+ }
+
+ if(ods.p_ind > 0){ /* just shift left one char */
+ ods.p_len--;
+ headents[ods.cur_e].dirty = 1;
+ if(ods.p_len == 0)
+ headents[ods.cur_e].sticky = 0;
+ else
+ mark_sticky(&headents[ods.cur_e]);
+
+ tbufp = &strng[--ods.p_ind];
+ while(*tbufp++ != '\0')
+ tbufp[-1] = *tbufp;
+ tbufp = &strng[ods.p_ind];
+ if(pdel()) /* physical screen delete */
+ skipmove++; /* must'a been optimal */
+ }
+ else{ /* may have work to do */
+ if(ods.cur_l->prev == NULL){
+ (*term.t_beep)(); /* no erase into next field */
+ continue;
+ }
+
+ ods.p_line--;
+ ods.cur_l = ods.cur_l->prev;
+ strng = ods.cur_l->text;
+ if((i=ucs4_strlen(strng)) > 0){
+ strng[i-1] = '\0'; /* erase the character */
+ ods.p_ind = i-1;
+ }
+ else{
+ headents[ods.cur_e].sticky = 0;
+ ods.p_ind = 0;
+ }
+
+ tbufp = &strng[ods.p_ind];
+ }
+
+ if((status = FormatLines(ods.cur_l, "", LINEWID(),
+ headents[ods.cur_e].break_on_comma,0))==-1){
+ (*term.t_beep)();
+ continue;
+ }
+ else{
+ /*
+ * beware, the dot may have moved...
+ */
+ while((ods.p_len=ucs4_strlen(strng)) < ods.p_ind){
+ ods.p_line++;
+ ods.p_ind -= ucs4_strlen(strng);
+ ods.cur_l = ods.cur_l->next;
+ strng = ods.cur_l->text;
+ ods.p_len = ucs4_strlen(strng);
+ tbufp = &strng[ods.p_ind];
+ status = TRUE;
+ }
+
+ if(UpdateHeader(0))
+ status = TRUE;
+
+ PaintHeader(COMPOSER_TOP_LINE, FALSE);
+ if(status == TRUE)
+ PaintBody(1);
+ }
+
+ movecursor(ods.p_line,
+ ucs4_str_width_ptr_to_ptr(strng, &strng[ods.p_ind])+headents[ods.cur_e].prwid);
+
+ if(skipmove)
+ continue;
+
+ break;
+
+ default :
+ return(ch);
+ }
+ }
+
+ while ((tbufp-strng) < HLSZ && *tbufp != '\0') /* synchronizing loop */
+ pputc(*tbufp++, 0);
+
+ if(ucs4_str_width_ptr_to_ptr(&strng[0], &strng[ods.p_len]) < LINEWID())
+ peeol();
+ }
+}
+
+
+void
+HeaderPaintCursor(void)
+{
+ movecursor(ods.p_line, ucs4_str_width_ptr_to_ptr(&ods.cur_l->text[0], &ods.cur_l->text[ods.p_ind])+headents[ods.cur_e].prwid);
+}
+
+
+
+/*
+ * FormatLines - Place the given text at the front of the given line->text
+ * making sure to properly format the line, then check
+ * all lines below for proper format.
+ *
+ * notes:
+ * Not much optimization at all. Right now, it recursively
+ * fixes all remaining lines in the entry. Some speed might
+ * gained if this was built to iteratively scan the lines.
+ *
+ * returns:
+ * -1 on error
+ * FALSE if only this line is changed
+ * TRUE if text below the first line is changed
+ */
+int
+FormatLines(struct hdr_line *h, /* where to begin formatting */
+ char *utf8_instr, /* input string */
+ int maxwid, /* max width of a line */
+ int break_on_comma, /* break lines on commas */
+ int quoted) /* this line inside quotes */
+{
+ int retval = FALSE;
+ int i, l, len;
+ char *utf8;
+ UCS *ostr; /* pointer to output string */
+ UCS *free_istr = NULL;
+ UCS *istr = NULL;
+ UCS *breakp; /* pointer to line break */
+ UCS *bp, *tp; /* temporary pointers */
+ UCS *buf; /* string to add later */
+ struct hdr_line *nlp, *lp;
+
+ ostr = h->text;
+ nlp = h->next;
+ if(utf8_instr)
+ free_istr = istr = utf8_to_ucs4_cpystr(utf8_instr);
+
+ len = ucs4_strlen(istr) + ucs4_strlen(ostr);
+ if((buf = (UCS *) malloc((len+10) * sizeof(*buf))) == NULL){
+ if(free_istr)
+ fs_give((void **) &free_istr);
+
+ return(-1);
+ }
+
+ if(ucs4_str_width(istr) + ucs4_str_width(ostr) >= maxwid){ /* break then fixup below */
+
+ if((l=ucs4_str_width(istr)) < maxwid){ /* room for more */
+
+ if(break_on_comma && (bp = ucs4_strqchr(istr, ',', &quoted, -1))){
+ bp += (bp[1] == ' ') ? 2 : 1;
+ for(tp = bp; *tp && *tp == ' '; tp++)
+ ;
+
+ ucs4_strncpy(buf, tp, len+10);
+ buf[len+10-1] = '\0';
+ ucs4_strncat(buf, ostr, len+10-ucs4_strlen(buf)-1);
+ buf[len+10-1] = '\0';
+
+ for(i = 0; &istr[i] < bp; i++)
+ ostr[i] = istr[i];
+
+ ostr[i] = '\0';
+ retval = TRUE;
+ }
+ else{
+ breakp = break_point(ostr, maxwid-ucs4_str_width(istr),
+ break_on_comma ? ',' : ' ',
+ break_on_comma ? &quoted : NULL);
+
+ if(breakp == ostr){ /* no good breakpoint */
+ if(break_on_comma && *breakp == ','){
+ breakp = ostr + 1;
+ retval = TRUE;
+ }
+ else if(ucs4_strchr(istr,(break_on_comma && !quoted)?',':' ')){
+ ucs4_strncpy(buf, ostr, len+10);
+ buf[len+10-1] = '\0';
+ ucs4_strncpy(ostr, istr, HLSZ);
+ ostr[HLSZ-1] = '\0';
+ }
+ else{ /* istr's broken as we can get it */
+ /*
+ * Break at maxwid - width of istr
+ */
+ breakp = ucs4_particular_width(ostr, maxwid - ucs4_str_width(istr)-1);
+ retval = TRUE;
+ }
+ }
+ else
+ retval = TRUE;
+
+ if(retval){
+ ucs4_strncpy(buf, breakp, len+10); /* save broken line */
+ buf[len+10-1] = '\0';
+ if(breakp == ostr){
+ ucs4_strncpy(ostr, istr, HLSZ); /* simple if no break */
+ ostr[HLSZ-1] = '\0';
+ }
+ else{
+ *breakp = '\0'; /* more work to break it */
+ i = ucs4_strlen(istr);
+ /*
+ * shift ostr i chars
+ */
+ for(bp=breakp; bp >= ostr && i; bp--)
+ *(bp+i) = *bp;
+ for(tp=ostr, bp=istr; *bp != '\0'; tp++, bp++)
+ *tp = *bp; /* then add istr */
+ }
+ }
+ }
+ }
+ /*
+ * Short-circuit the recursion in this case.
+ * No time right now to figure out how to do it better.
+ */
+ else if(l > 2*maxwid){
+ UCS *istrp, *saveostr = NULL;
+
+ retval = TRUE;
+ if(ostr && *ostr)
+ saveostr = ucs4_cpystr(ostr);
+
+ istrp = istr;
+ while(l > 2*maxwid){
+ if(break_on_comma || maxwid == 1){
+ breakp = (!(bp = ucs4_strqchr(istrp, ',', &quoted, maxwid))
+ || ucs4_str_width_ptr_to_ptr(istrp, bp) >= maxwid || maxwid == 1)
+ ? ucs4_particular_width(istrp, maxwid)
+ : bp + ((bp[1] == ' ') ? 2 : 1);
+ }
+ else{
+ breakp = break_point(istrp, maxwid, ' ', NULL);
+
+ if(breakp == istrp) /* no good break point */
+ breakp = ucs4_particular_width(istrp, maxwid-1);
+ }
+
+ for(tp=ostr,bp=istrp; bp < breakp; tp++, bp++)
+ *tp = *bp;
+
+ *tp = '\0';
+ l -= ucs4_str_width_ptr_to_ptr(istrp, breakp);
+ istrp = breakp;
+
+ if((lp = HALLOC()) == NULL){
+ emlwrite("Can't allocate any more lines for header!", NULL);
+ free(buf);
+ if(free_istr)
+ fs_give((void **) &free_istr);
+
+ return(-1);
+ }
+
+ lp->next = h->next;
+ if(h->next)
+ h->next->prev = lp;
+
+ h->next = lp;
+ lp->prev = h;
+ lp->text[0] = '\0';
+ h = h->next;
+ ostr = h->text;
+ }
+
+ /*
+ * Now l is still > maxwid. Do it the recursive way,
+ * like the else clause below. Surely we could fix up the
+ * flow control some here, but this works for now.
+ */
+
+ nlp = h->next;
+ istr = istrp;
+ if(saveostr){
+ ucs4_strncpy(ostr, saveostr, HLSZ);
+ ostr[HLSZ-1] = '\0';
+ fs_give((void **) &saveostr);
+ }
+
+ if(break_on_comma || maxwid == 1){
+ breakp = (!(bp = ucs4_strqchr(istrp, ',', &quoted, maxwid))
+ || ucs4_str_width_ptr_to_ptr(istrp, bp) >= maxwid || maxwid == 1)
+ ? ucs4_particular_width(istrp, maxwid)
+ : bp + ((bp[1] == ' ') ? 2 : 1);
+ }
+ else{
+ breakp = break_point(istrp, maxwid, ' ', NULL);
+
+ if(breakp == istrp) /* no good break point */
+ breakp = ucs4_particular_width(istrp, maxwid-1);
+ }
+
+ ucs4_strncpy(buf, breakp, len+10); /* save broken line */
+ buf[len+10-1] = '\0';
+ ucs4_strncat(buf, ostr, len+10-ucs4_strlen(buf)-1); /* add line that was there */
+ buf[len+10-1] = '\0';
+
+ for(tp=ostr,bp=istr; bp < breakp; tp++, bp++)
+ *tp = *bp;
+
+ *tp = '\0';
+ }
+ else{ /* utf8_instr > maxwid ! */
+ if(break_on_comma || maxwid == 1){
+ breakp = (!(bp = ucs4_strqchr(istr, ',', &quoted, maxwid))
+ || ucs4_str_width_ptr_to_ptr(istr, bp) >= maxwid || maxwid == 1)
+ ? ucs4_particular_width(istr, maxwid)
+ : bp + ((bp[1] == ' ') ? 2 : 1);
+ }
+ else{
+ breakp = break_point(istr, maxwid, ' ', NULL);
+
+ if(breakp == istr) /* no good break point */
+ breakp = ucs4_particular_width(istr, maxwid-1);
+ }
+
+ ucs4_strncpy(buf, breakp, len+10); /* save broken line */
+ buf[len+10-1] = '\0';
+ ucs4_strncat(buf, ostr, len+10-ucs4_strlen(buf)-1); /* add line that was there */
+ buf[len+10-1] = '\0';
+
+ for(tp=ostr,bp=istr; bp < breakp; tp++, bp++)
+ *tp = *bp;
+
+ *tp = '\0';
+ }
+
+ if(nlp == NULL){ /* no place to add below? */
+ if((lp = HALLOC()) == NULL){
+ emlwrite("Can't allocate any more lines for header!", NULL);
+ free(buf);
+ if(free_istr)
+ fs_give((void **) &free_istr);
+
+ return(-1);
+ }
+
+ if(TERM_OPTIMIZE && (i = physical_line(h)) != -1)
+ scrolldown(wheadp, i - 1, 1);
+
+ h->next = lp; /* fix up links */
+ lp->prev = h;
+ lp->next = NULL;
+ lp->text[0] = '\0';
+ nlp = lp;
+ retval = TRUE;
+ }
+ }
+ else{ /* combined width < max */
+ buf[0] = '\0';
+ if(istr && *istr){
+ ucs4_strncpy(buf, istr, len+10); /* insert istr before ostr */
+ buf[len+10-1] = '\0';
+
+ ucs4_strncat(buf, ostr, len+10-ucs4_strlen(buf)-1);
+ buf[len+10-1] = '\0';
+
+ ucs4_strncpy(ostr, buf, HLSZ); /* copy back to ostr */
+ ostr[HLSZ-1] = '\0';
+ }
+
+ *buf = '\0';
+ breakp = NULL;
+
+ if(break_on_comma && (breakp = ucs4_strqchr(ostr, ',', &quoted, -1))){
+ breakp += (breakp[1] == ' ') ? 2 : 1;
+ ucs4_strncpy(buf, breakp, len+10);
+ buf[len+10-1] = '\0';
+ *breakp = '\0';
+
+ if(*buf && !nlp){
+ if((lp = HALLOC()) == NULL){
+ emlwrite("Can't allocate any more lines for header!",NULL);
+ free(buf);
+ if(free_istr)
+ fs_give((void **) &free_istr);
+
+ return(-1);
+ }
+
+ if(TERM_OPTIMIZE && (i = physical_line(h)) != -1)
+ scrolldown(wheadp, i - 1, 1);
+
+ h->next = lp; /* fix up links */
+ lp->prev = h;
+ lp->next = NULL;
+ lp->text[0] = '\0';
+ nlp = lp;
+ retval = TRUE;
+ }
+ }
+
+ if(nlp){
+ if(!*buf && !breakp){
+ if(ucs4_str_width(ostr) + ucs4_str_width(nlp->text) >= maxwid){
+ breakp = break_point(nlp->text, maxwid-ucs4_str_width(ostr),
+ break_on_comma ? ',' : ' ',
+ break_on_comma ? &quoted : NULL);
+
+ if(breakp == nlp->text){ /* commas this line? */
+ for(tp=ostr; *tp && *tp != ' '; tp++)
+ ;
+
+ if(!*tp){ /* no commas, get next best */
+ breakp += maxwid - ucs4_str_width(ostr) - 1;
+ retval = TRUE;
+ }
+ else
+ retval = FALSE;
+ }
+ else
+ retval = TRUE;
+
+ if(retval){ /* only if something to do */
+ for(tp = &ostr[ucs4_strlen(ostr)],bp=nlp->text; bp<breakp;
+ tp++, bp++)
+ *tp = *bp; /* add breakp to this line */
+ *tp = '\0';
+ for(tp=nlp->text, bp=breakp; *bp != '\0'; tp++, bp++)
+ *tp = *bp; /* shift next line to left */
+ *tp = '\0';
+ }
+ }
+ else{
+ ucs4_strncat(ostr, nlp->text, HLSZ-ucs4_strlen(ostr)-1);
+ ostr[HLSZ-1] = '\0';
+
+ if(TERM_OPTIMIZE && (i = physical_line(nlp)) != -1)
+ scrollup(wheadp, i, 1);
+
+ hldelete(nlp);
+
+ if(!(nlp = h->next)){
+ free(buf);
+ if(free_istr)
+ fs_give((void **) &free_istr);
+
+ return(TRUE); /* can't go further */
+ }
+ else
+ retval = TRUE; /* more work to do? */
+ }
+ }
+ }
+ else{
+ free(buf);
+ if(free_istr)
+ fs_give((void **) &free_istr);
+
+ return(FALSE);
+ }
+
+ }
+
+ utf8 = ucs4_to_utf8_cpystr(buf);
+ free(buf);
+ if(free_istr)
+ fs_give((void **) &free_istr);
+
+ if(utf8){
+ int rv;
+
+ i = FormatLines(nlp, utf8, maxwid, break_on_comma, quoted);
+ fs_give((void **) &utf8);
+ switch(i){
+ case -1: /* bubble up worst case */
+ rv = -1;
+ break;
+ case FALSE:
+ if(retval == FALSE){
+ rv = FALSE;
+ break;
+ }
+ default:
+ rv = TRUE;
+ }
+
+ return(rv);
+ }
+ else
+ return(-1);
+}
+
+/*
+ * Format the lines before parsing attachments so we
+ * don't expand a bunch of attachments that we don't
+ * have the buffer space for.
+ */
+int
+FormatSyncAttach(void)
+{
+ FormatLines(headents[ods.cur_e].hd_text, "",
+ term.t_ncol - headents[ods.cur_e].prwid,
+ headents[ods.cur_e].break_on_comma, 0);
+ return(SyncAttach());
+}
+
+
+/*
+ * PaintHeader - do the work of displaying the header from the given
+ * physical screen line the end of the header.
+ *
+ * 17 July 91 - fixed reshow to deal with arbitrarily large headers.
+ */
+void
+PaintHeader(int line, /* physical line on screen */
+ int clear) /* clear before painting */
+{
+ struct hdr_line *lp;
+ int curline;
+ int curindex;
+ int curoffset;
+ UCS buf[NLINE];
+ UCS *bufp;
+ int i, e, w;
+
+ if(clear)
+ pclear(COMPOSER_TOP_LINE, ComposerTopLine-1);
+
+ curline = COMPOSER_TOP_LINE;
+ curindex = curoffset = 0;
+
+ for(lp = ods.top_l, e = ods.top_e; ; curline++){
+ if((curline == line) || ((lp = next_hline(&e, lp)) == NULL))
+ break;
+ }
+
+ while(headents[e].name != NULL){ /* begin to redraw */
+ while(lp != NULL){
+ buf[0] = '\0';
+ if((!lp->prev || curline == COMPOSER_TOP_LINE) && !curoffset){
+ if(InvertPrompt(e, (e == ods.cur_e && ComposerEditing)) == -1
+ && !is_blank(curline, 0, headents[e].prwid)){
+ for(i = 0; i < headents[e].prwid; i++)
+ buf[i] = ' ';
+
+ buf[i] = '\0';
+ }
+ }
+ else if(!is_blank(curline, 0, headents[e].prwid)){
+ for(i = 0; i < headents[e].prwid; i++)
+ buf[i] = ' ';
+
+ buf[i] = '\0';
+ }
+
+ if(*(bufp = buf) != '\0'){ /* need to paint? */
+ movecursor(curline, 0); /* paint the line... */
+ while(*bufp != '\0')
+ pputc(*bufp++, 0);
+ }
+
+ bufp = &(lp->text[curindex]); /* skip chars already there */
+ curoffset += headents[e].prwid;
+ curindex = index_from_col(curline, curoffset);
+ while(pscr(curline, curindex) != NULL &&
+ *bufp == pscr(curline, curindex)->c && *bufp != '\0'){
+ w = wcellwidth(*bufp);
+ curoffset += (w >= 0 ? w : 1);
+ ++bufp;
+ ++curindex;
+ if(curoffset >= term.t_ncol)
+ break;
+ }
+
+ if(*bufp != '\0'){ /* need to move? */
+ movecursor(curline, curoffset);
+ while(*bufp != '\0'){ /* display what's not there */
+ pputc(*bufp, 0);
+ w = wcellwidth(*bufp);
+ curoffset += (w >= 0 ? w : 1);
+ ++bufp;
+ ++curindex;
+ }
+ }
+
+ if(curoffset < term.t_ncol){
+ movecursor(curline, curoffset);
+ peeol();
+ }
+ curline++;
+ curindex = curoffset = 0;
+ if(curline >= BOTTOM())
+ break;
+
+ lp = lp->next;
+ }
+
+ if(curline >= BOTTOM())
+ return; /* don't paint delimiter */
+
+ while(headents[++e].name != NULL)
+ if(headents[e].display_it){
+ lp = headents[e].hd_text;
+ break;
+ }
+ }
+
+ display_delimiter(ComposerEditing ? 0 : 1);
+}
+
+
+
+/*
+ * PaintBody() - generic call to handle repainting everything BUT the
+ * header
+ *
+ * notes:
+ * The header redrawing in a level 0 body paint gets done
+ * in update()
+ */
+void
+PaintBody(int level)
+{
+ curwp->w_flag |= WFHARD; /* make sure framing's right */
+ if(level == 0) /* specify what to update */
+ sgarbf = TRUE;
+
+ update(); /* display message body */
+
+ if(level == 0 && ComposerEditing){
+ mlerase(); /* clear the error line */
+ ShowPrompt();
+ }
+}
+
+
+/*
+ * display_for_send - paint the composer from the top line and return.
+ */
+void
+display_for_send(void)
+{
+ int i = 0;
+ struct hdr_line *l;
+
+ /* if first header line isn't displayed, there's work to do */
+ if(headents && ((l = first_hline(&i)) != ods.top_l
+ || ComposerTopLine == COMPOSER_TOP_LINE
+ || !ods.p_line)){
+ struct on_display orig_ods;
+ int orig_edit = ComposerEditing,
+ orig_ct_line = ComposerTopLine;
+
+ /*
+ * fake that the cursor's in the first header line
+ * and force repaint...
+ */
+ orig_ods = ods;
+ ods.cur_e = i;
+ ods.top_l = ods.cur_l = l;
+ ods.top_e = ods.cur_e;
+ ods.p_line = COMPOSER_TOP_LINE;
+ ComposerEditing = TRUE; /* to fool update() */
+ setimark(FALSE, 1); /* remember where we were */
+ gotobob(FALSE, 1);
+
+ UpdateHeader(0); /* redraw whole enchilada */
+ PaintHeader(COMPOSER_TOP_LINE, TRUE);
+ PaintBody(0);
+
+ ods = orig_ods; /* restore original state */
+ ComposerEditing = orig_edit;
+ ComposerTopLine = curwp->w_toprow = orig_ct_line;
+ curwp->w_ntrows = BOTTOM() - ComposerTopLine;
+ swapimark(FALSE, 1);
+
+ /* in case we don't exit, set up restoring the screen */
+ sgarbf = TRUE; /* force redraw */
+ }
+}
+
+
+/*
+ * ArrangeHeader - set up display parm such that header is reasonably
+ * displayed
+ */
+void
+ArrangeHeader(void)
+{
+ int e;
+ register struct hdr_line *l;
+
+ ods.p_line = ods.p_ind = 0;
+ e = ods.top_e = 0;
+ l = ods.top_l = headents[e].hd_text;
+ while(headents[e+1].name || (l && l->next))
+ if((l = next_sel_hline(&e, l)) != NULL){
+ ods.cur_l = l;
+ ods.cur_e = e;
+ }
+
+ UpdateHeader(1);
+}
+
+
+/*
+ * ComposerHelp() - display mail help in a context sensitive way
+ * based on the level passed ...
+ */
+int
+ComposerHelp(int level)
+{
+ char buf[80];
+ VARS_TO_SAVE *saved_state;
+
+ curwp->w_flag |= WFMODE;
+ sgarbf = TRUE;
+
+ if(level < 0 || !headents[level].name){
+ (*term.t_beep)();
+ emlwrite("Sorry, I can't help you with that.", NULL);
+ sleep(2);
+ return(FALSE);
+ }
+
+ snprintf(buf, sizeof(buf), "Help for %s %.40s Field",
+ (Pmaster->pine_flags & MDHDRONLY) ? "Address Book"
+ : "Composer",
+ headents[level].name);
+ saved_state = save_pico_state();
+ (*Pmaster->helper)(headents[level].help, buf, 1);
+ if(saved_state){
+ restore_pico_state(saved_state);
+ free_pico_state(saved_state);
+ }
+
+ ttresize();
+ picosigs(); /* restore altered handlers */
+ return(TRUE);
+}
+
+
+
+/*
+ * ToggleHeader() - set or unset pico values to the full screen size
+ * painting header if need be.
+ */
+int
+ToggleHeader(int show)
+{
+ /*
+ * check to see if we need to display the header...
+ */
+ if(show){
+ UpdateHeader(0); /* figure bounds */
+ PaintHeader(COMPOSER_TOP_LINE, FALSE); /* draw it */
+ }
+ else{
+ /*
+ * set bounds for no header display
+ */
+ curwp->w_toprow = ComposerTopLine = COMPOSER_TOP_LINE;
+ curwp->w_ntrows = BOTTOM() - ComposerTopLine;
+ }
+ return(TRUE);
+}
+
+
+
+/*
+ * HeaderLen() - return the length in lines of the exposed portion of the
+ * header
+ */
+int
+HeaderLen(void)
+{
+ register struct hdr_line *lp;
+ int e;
+ int i;
+
+ i = 1;
+ lp = ods.top_l;
+ e = ods.top_e;
+ while(lp != NULL){
+ lp = next_hline(&e, lp);
+ i++;
+ }
+ return(i);
+}
+
+
+
+/*
+ * first_hline() - return a pointer to the first displayable header line
+ *
+ * returns:
+ * 1) pointer to first displayable line in header and header
+ * entry, via side effect, that the first line is a part of
+ * 2) NULL if no next line, leaving entry at LASTHDR
+ */
+struct hdr_line *
+first_hline(int *entry)
+{
+ /* init *entry so we're sure to start from the top */
+ for(*entry = 0; headents[*entry].name; (*entry)++)
+ if(headents[*entry].display_it)
+ return(headents[*entry].hd_text);
+
+ *entry = 0;
+ return(NULL); /* this shouldn't happen */
+}
+
+
+/*
+ * first_sel_hline() - return a pointer to the first selectable header line
+ *
+ * returns:
+ * 1) pointer to first selectable line in header and header
+ * entry, via side effect, that the first line is a part of
+ * 2) NULL if no next line, leaving entry at LASTHDR
+ */
+struct hdr_line *
+first_sel_hline(int *entry)
+{
+ /* init *entry so we're sure to start from the top */
+ for(*entry = 0; headents[*entry].name; (*entry)++)
+ if(headents[*entry].display_it && !headents[*entry].blank)
+ return(headents[*entry].hd_text);
+
+ *entry = 0;
+ return(NULL); /* this shouldn't happen */
+}
+
+
+
+/*
+ * next_hline() - return a pointer to the next line structure
+ *
+ * returns:
+ * 1) pointer to next displayable line in header and header
+ * entry, via side effect, that the next line is a part of
+ * 2) NULL if no next line, leaving entry at LASTHDR
+ */
+struct hdr_line *
+next_hline(int *entry, struct hdr_line *line)
+{
+ if(line == NULL)
+ return(NULL);
+
+ if(line->next == NULL){
+ while(headents[++(*entry)].name != NULL){
+ if(headents[*entry].display_it)
+ return(headents[*entry].hd_text);
+ }
+ --(*entry);
+ return(NULL);
+ }
+ else
+ return(line->next);
+}
+
+
+/*
+ * next_sel_hline() - return a pointer to the next selectable line structure
+ *
+ * returns:
+ * 1) pointer to next selectable line in header and header
+ * entry, via side effect, that the next line is a part of
+ * 2) NULL if no next line, leaving entry at LASTHDR
+ */
+struct hdr_line *
+next_sel_hline(int *entry, struct hdr_line *line)
+{
+ if(line == NULL)
+ return(NULL);
+
+ if(line->next == NULL){
+ while(headents[++(*entry)].name != NULL){
+ if(headents[*entry].display_it && !headents[*entry].blank)
+ return(headents[*entry].hd_text);
+ }
+ --(*entry);
+ return(NULL);
+ }
+ else
+ return(line->next);
+}
+
+
+
+/*
+ * prev_hline() - return a pointer to the next line structure back
+ *
+ * returns:
+ * 1) pointer to previous displayable line in header and
+ * the header entry that the next line is a part of
+ * via side effect
+ * 2) NULL if no next line, leaving entry unchanged from
+ * the value it had on entry.
+ */
+struct hdr_line *
+prev_hline(int *entry, struct hdr_line *line)
+{
+ if(line == NULL)
+ return(NULL);
+
+ if(line->prev == NULL){
+ int orig_entry;
+
+ orig_entry = *entry;
+ while(--(*entry) >= 0){
+ if(headents[*entry].display_it){
+ line = headents[*entry].hd_text;
+ while(line->next != NULL)
+ line = line->next;
+ return(line);
+ }
+ }
+
+ *entry = orig_entry;
+ return(NULL);
+ }
+ else
+ return(line->prev);
+}
+
+
+/*
+ * prev_sel_hline() - return a pointer to the previous selectable line
+ *
+ * returns:
+ * 1) pointer to previous selectable line in header and
+ * the header entry that the next line is a part of
+ * via side effect
+ * 2) NULL if no next line, leaving entry unchanged from
+ * the value it had on entry.
+ */
+struct hdr_line *
+prev_sel_hline(int *entry, struct hdr_line *line)
+{
+ if(line == NULL)
+ return(NULL);
+
+ if(line->prev == NULL){
+ int orig_entry;
+
+ orig_entry = *entry;
+ while(--(*entry) >= 0){
+ if(headents[*entry].display_it && !headents[*entry].blank){
+ line = headents[*entry].hd_text;
+ while(line->next != NULL)
+ line = line->next;
+ return(line);
+ }
+ }
+
+ *entry = orig_entry;
+ return(NULL);
+ }
+ else
+ return(line->prev);
+}
+
+
+
+/*
+ * first_requested_hline() - return pointer to first line that pico's caller
+ * asked that we start on.
+ */
+struct hdr_line *
+first_requested_hline(int *ent)
+{
+ int i, reqfield;
+ struct hdr_line *rv = NULL;
+
+ for(reqfield = -1, i = 0; headents[i].name; i++)
+ if(headents[i].start_here){
+ headents[i].start_here = 0; /* clear old setting */
+ if(reqfield < 0){ /* if not already, set up */
+ headents[i].display_it = 1; /* make sure it's shown */
+ *ent = reqfield = i;
+ rv = headents[i].hd_text;
+ }
+ }
+
+ return(rv);
+}
+
+
+
+/*
+ * UpdateHeader() - determines the best range of lines to be displayed
+ * using the global ods value for the current line and the
+ * top line, also sets ComposerTopLine and pico limits
+ *
+ * showtop -- Attempt to show all header lines if they'll fit.
+ *
+ * notes:
+ * This is pretty ugly because it has to keep the current line
+ * on the screen in a reasonable location no matter what.
+ * There are also a couple of rules to follow:
+ * 1) follow paging conventions of pico (ie, half page
+ * scroll)
+ * 2) if more than one page, always display last half when
+ * pline is toward the end of the header
+ *
+ * returns:
+ * TRUE if anything changed (side effects: new p_line, top_l
+ * top_e, and pico parms)
+ * FALSE if nothing changed
+ *
+ */
+int
+UpdateHeader(int showtop)
+{
+ register struct hdr_line *lp;
+ int i, le;
+ int ret = FALSE;
+ int old_top = ComposerTopLine;
+ int old_p = ods.p_line;
+
+ if(ods.p_line < COMPOSER_TOP_LINE ||
+ ((ods.p_line == ComposerTopLine-2) ? 2: 0) + ods.p_line >= BOTTOM()){
+ /* NewTop if cur header line is at bottom of screen or two from */
+ /* the bottom of the screen if cur line is bottom header line */
+ NewTop(showtop); /* get new top_l */
+ ret = TRUE;
+ }
+ else{ /* make sure p_line's OK */
+ i = COMPOSER_TOP_LINE;
+ lp = ods.top_l;
+ le = ods.top_e;
+ while(lp != ods.cur_l){
+ /*
+ * this checks to make sure cur_l is below top_l and that
+ * cur_l is on the screen...
+ */
+ if((lp = next_hline(&le, lp)) == NULL || ++i >= BOTTOM()){
+ NewTop(0);
+ ret = TRUE;
+ break;
+ }
+ }
+ }
+
+ ods.p_line = COMPOSER_TOP_LINE; /* find p_line... */
+ lp = ods.top_l;
+ le = ods.top_e;
+ while(lp && lp != ods.cur_l){
+ lp = next_hline(&le, lp);
+ ods.p_line++;
+ }
+
+ if(!ret)
+ ret = !(ods.p_line == old_p);
+
+ ComposerTopLine = ods.p_line; /* figure top composer line */
+ while(lp && ComposerTopLine <= BOTTOM()){
+ lp = next_hline(&le, lp);
+ ComposerTopLine += (lp) ? 1 : 2; /* allow for delim at end */
+ }
+
+ if(!ret)
+ ret = !(ComposerTopLine == old_top);
+
+ if(wheadp->w_toprow != ComposerTopLine){ /* update pico params... */
+ wheadp->w_toprow = ComposerTopLine;
+ wheadp->w_ntrows = ((i = BOTTOM() - ComposerTopLine) > 0) ? i : 0;
+ ret = TRUE;
+ }
+ return(ret);
+}
+
+
+
+/*
+ * NewTop() - calculate a new top_l based on the cur_l
+ *
+ * showtop -- Attempt to show all the header lines if they'll fit
+ *
+ * returns:
+ * with ods.top_l and top_e pointing at a reasonable line
+ * entry
+ */
+void
+NewTop(int showtop)
+{
+ register struct hdr_line *lp;
+ register int i;
+ int e;
+
+ lp = ods.cur_l;
+ e = ods.cur_e;
+ i = showtop ? FULL_SCR() : HALF_SCR();
+
+ while(lp != NULL && (--i > 0)){
+ ods.top_l = lp;
+ ods.top_e = e;
+ lp = prev_hline(&e, lp);
+ }
+}
+
+
+
+/*
+ * display_delimiter() - just paint the header/message body delimiter with
+ * inverse value specified by state.
+ */
+void
+display_delimiter(int state)
+{
+ UCS *bufp, *buf;
+
+ if(ComposerTopLine - 1 >= BOTTOM()) /* silently forget it */
+ return;
+
+ buf = utf8_to_ucs4_cpystr((gmode & MDHDRONLY) ? "" : HDR_DELIM);
+ if(!buf)
+ return;
+
+ bufp = buf;
+
+ if(state == delim_ps){ /* optimize ? */
+ for(delim_ps = 0; bufp[delim_ps] && pscr(ComposerTopLine-1,delim_ps) != NULL && pscr(ComposerTopLine-1,delim_ps)->c == bufp[delim_ps];delim_ps++)
+ ;
+
+ if(bufp[delim_ps] == '\0' && !(gmode & MDHDRONLY)){
+ delim_ps = state;
+ fs_give((void **) &buf);
+ return; /* already displayed! */
+ }
+ }
+
+ delim_ps = state;
+
+ movecursor(ComposerTopLine - 1, 0);
+ if(state)
+ (*term.t_rev)(1);
+
+ while(*bufp != '\0')
+ pputc(*bufp++, state ? 1 : 0);
+
+ if(state)
+ (*term.t_rev)(0);
+
+ peeol();
+ fs_give((void **) &buf);
+}
+
+
+
+/*
+ * InvertPrompt() - invert the prompt associated with header entry to state
+ * state (true if invert, false otherwise).
+ * returns:
+ * non-zero if nothing done
+ * 0 if prompt inverted successfully
+ *
+ * notes:
+ * come to think of it, this func and the one above could
+ * easily be combined
+ */
+int
+InvertPrompt(int entry, int state)
+{
+ UCS *buf, *bufp;
+ UCS *end;
+ int i;
+
+ buf = utf8_to_ucs4_cpystr(headents[entry].prompt); /* fresh prompt paint */
+ if(!buf)
+ return(-1);
+
+ bufp = buf;
+ if((i = entry_line(entry, FALSE)) == -1){
+ fs_give((void **) &buf);
+ return(-1); /* silently forget it */
+ }
+
+ end = buf + ucs4_strlen(buf);
+
+ /*
+ * Makes sure that the prompt doesn't take up more than prwid of screen space.
+ * The caller should do that, too, in order to make it look right so
+ * this should most likely be a no-op
+ */
+ if(ucs4_str_width_ptr_to_ptr(buf, end) > headents[entry].prwid){
+ end = ucs4_particular_width(buf, headents[entry].prwid);
+ *end = '\0';
+ }
+
+ if(entry < 16 && (invert_ps&(1<<entry))
+ == (state ? 1<<entry : 0)){ /* optimize ? */
+ int j;
+
+ for(j = 0; bufp[j] && pscr(i, j)->c == bufp[j]; j++)
+ ;
+
+ if(bufp[j] == '\0'){
+ if(state)
+ invert_ps |= 1<<entry;
+ else
+ invert_ps &= ~(1<<entry);
+
+ fs_give((void **) &buf);
+ return(0); /* already displayed! */
+ }
+ }
+
+ if(entry < 16){ /* if > 16, cannot be stored in invert_ps */
+ if(state)
+ invert_ps |= 1<<entry;
+ else
+ invert_ps &= ~(1<<entry);
+ }
+
+ movecursor(i, 0);
+ if(state)
+ (*term.t_rev)(1);
+
+ while(*bufp && *(bufp + 1))
+ pputc(*bufp++, 1); /* putc upto last char */
+
+ if(state)
+ (*term.t_rev)(0);
+
+ pputc(*bufp, 0); /* last char not inverted */
+
+ fs_give((void **) &buf);
+
+ return(TRUE);
+}
+
+
+
+/*
+ * partial_entries() - toggle display of the bcc and fcc fields.
+ *
+ * returns:
+ * TRUE if there are partial entries on the display
+ * FALSE otherwise.
+ */
+int
+partial_entries(void)
+{
+ register struct headerentry *h;
+ int is_on;
+
+ /*---- find out status of first rich header ---*/
+ for(h = headents; !h->rich_header && h->name != NULL; h++)
+ ;
+
+ is_on = h->display_it;
+ for(h = headents; h->name != NULL; h++)
+ if(h->rich_header)
+ h->display_it = ! is_on;
+
+ return(is_on);
+}
+
+
+
+/*
+ * entry_line() - return the physical line on the screen associated
+ * with the given header entry field. Note: the field
+ * may span lines, so if the last char is set, return
+ * the appropriate value.
+ *
+ * returns:
+ * 1) physical line number of entry
+ * 2) -1 if entry currently not on display
+ */
+int
+entry_line(int entry, int lastchar)
+{
+ register int p_line = COMPOSER_TOP_LINE;
+ int i;
+ register struct hdr_line *line;
+
+ for(line = ods.top_l, i = ods.top_e;
+ headents && headents[i].name && i <= entry;
+ p_line++){
+ if(p_line >= BOTTOM())
+ break;
+ if(i == entry){
+ if(lastchar){
+ if(line->next == NULL)
+ return(p_line);
+ }
+ else if(line->prev == NULL)
+ return(p_line);
+ else
+ return(-1);
+ }
+ line = next_hline(&i, line);
+ }
+ return(-1);
+}
+
+
+
+/*
+ * physical_line() - return the physical line on the screen associated
+ * with the given header line pointer.
+ *
+ * returns:
+ * 1) physical line number of entry
+ * 2) -1 if entry currently not on display
+ */
+int
+physical_line(struct hdr_line *l)
+{
+ register int p_line = COMPOSER_TOP_LINE;
+ register struct hdr_line *lp;
+ int i;
+
+ for(lp=ods.top_l, i=ods.top_e; headents[i].name && lp != NULL; p_line++){
+ if(p_line >= BOTTOM())
+ break;
+
+ if(lp == l)
+ return(p_line);
+
+ lp = next_hline(&i, lp);
+ }
+ return(-1);
+}
+
+
+
+/*
+ * call_builder() - resolve any nicknames in the address book associated
+ * with the given entry...
+ *
+ * NOTES:
+ *
+ * BEWARE: this function can cause cur_l and top_l to get lost so BE
+ * CAREFUL before and after you call this function!!!
+ *
+ * There could to be something here to resolve cur_l and top_l
+ * reasonably into the new linked list for this entry.
+ *
+ * The reason this would mostly work without it is resolve_niks gets
+ * called for the most part in between fields. Since we're moving
+ * to the beginning or end (i.e. the next/prev pointer in the old
+ * freed cur_l is NULL) of the next entry, we get a new cur_l
+ * pointing at a good line. Then since top_l is based on cur_l in
+ * NewTop() we have pretty much lucked out.
+ *
+ * Where we could get burned is in a canceled exit (ctrl|x). Here
+ * nicknames get resolved into addresses, which invalidates cur_l
+ * and top_l. Since we don't actually leave, we could begin editing
+ * again with bad pointers. This would usually results in a nice
+ * core dump.
+ *
+ * NOTE: The mangled argument is a little strange. It's used on both
+ * input and output. On input, if it is not set, then that tells the
+ * builder not to do anything that might take a long time, like a
+ * white pages lookup. On return, it tells the caller that the screen
+ * and signals may have been mangled so signals should be reset, window
+ * resized, and screen redrawn.
+ *
+ * RETURNS:
+ * > 0 if any names where resolved, otherwise
+ * 0 if not, or
+ * < 0 on error
+ * -1: move to next line
+ * -2: don't move off this line
+ */
+int
+call_builder(struct headerentry *entry, int *mangled, char **err)
+{
+ register int retval = 0;
+ register int i;
+ register struct hdr_line *line;
+ int quoted = 0;
+ int sbuflen;
+ char *sbuf;
+ char *s = NULL;
+ char *tmp;
+ struct headerentry *e;
+ BUILDER_ARG *nextarg, *arg = NULL, *headarg = NULL;
+ VARS_TO_SAVE *saved_state;
+
+ if(!entry->builder)
+ return(0);
+
+ line = entry->hd_text;
+ sbuflen = 0;
+ while(line != NULL){
+ sbuflen += (6*term.t_ncol);
+ line = line->next;
+ }
+
+ if((sbuf=(char *)malloc(sbuflen * sizeof(*sbuf))) == NULL){
+ emlwrite("Can't malloc space to expand address", NULL);
+ return(-1);
+ }
+
+ *sbuf = '\0';
+
+ /*
+ * cat the whole entry into one string...
+ */
+ line = entry->hd_text;
+ while(line != NULL){
+ i = ucs4_strlen(line->text);
+
+ /*
+ * To keep pine address builder happy, addresses should be separated
+ * by ", ". Add this space if needed, otherwise...
+ * (This is some ancient requirement that is no longer needed.)
+ *
+ * If this line is NOT a continuation of the previous line, add
+ * white space for pine's address builder if its not already there...
+ * (This is some ancient requirement that is no longer needed.)
+ *
+ * Also if it's not a continuation (i.e., there's already and addr on
+ * the line), and there's another line below, treat the new line as
+ * an implied comma.
+ * (This should only be done for address-type lines, not for regular
+ * text lines like subjects. Key off of the break_on_comma bit which
+ * should only be set on those that won't mind a comma being added.)
+ */
+ if(entry->break_on_comma){
+ UCS *space, commaspace[3];
+
+ commaspace[0] = ',';
+ commaspace[1] = ' ';
+ commaspace[2] = '\0';
+ space = commaspace+1;
+
+ if(i && line->text[i-1] == ','){
+ ucs4_strncat(line->text, space, HLSZ-i-1); /* help address builder */
+ line->text[HLSZ-1] = '\0';
+ }
+ else if(line->next != NULL && !strend(line->text, ',')){
+ if(ucs4_strqchr(line->text, ',', &quoted, -1)){
+ ucs4_strncat(line->text, commaspace, HLSZ-i-1); /* implied comma */
+ line->text[HLSZ-1] = '\0';
+ }
+ }
+ else if(line->prev != NULL && line->next != NULL){
+ if(ucs4_strchr(line->prev->text, ' ') != NULL
+ && line->text[i-1] != ' '){
+ ucs4_strncat(line->text, space, HLSZ-i-1);
+ line->text[HLSZ-1] = '\0';
+ }
+ }
+ }
+
+ tmp = ucs4_to_utf8_cpystr(line->text);
+ if(tmp){
+ strncat(sbuf, tmp, sbuflen-strlen(sbuf)-1);
+ sbuf[sbuflen-1] = '\0';
+ fs_give((void **) &tmp);
+ }
+
+ line = line->next;
+ }
+
+ if(entry->affected_entry){
+ /* check if any non-sticky affected entries */
+ for(e = entry->affected_entry; e; e = e->next_affected)
+ if(!e->sticky)
+ break;
+
+ /* there is at least one non-sticky so make a list to pass */
+ if(e){
+ for(e = entry->affected_entry; e; e = e->next_affected){
+ if(!arg){
+ headarg = arg = (BUILDER_ARG *)malloc(sizeof(BUILDER_ARG));
+ if(!arg){
+ emlwrite("Can't malloc space for fcc", NULL);
+ return(-1);
+ }
+ else{
+ arg->next = NULL;
+ arg->tptr = NULL;
+ arg->aff = &(e->bldr_private);
+ arg->me = &(entry->bldr_private);
+ }
+ }
+ else{
+ nextarg = (BUILDER_ARG *)malloc(sizeof(BUILDER_ARG));
+ if(!nextarg){
+ emlwrite("Can't malloc space for fcc", NULL);
+ return(-1);
+ }
+ else{
+ nextarg->next = NULL;
+ nextarg->tptr = NULL;
+ nextarg->aff = &(e->bldr_private);
+ nextarg->me = &(entry->bldr_private);
+ arg->next = nextarg;
+ arg = arg->next;
+ }
+ }
+
+ if(!e->sticky){
+ line = e->hd_text;
+ arg->tptr = ucs4_to_utf8_cpystr(line->text);
+ }
+ }
+ }
+ }
+
+ /*
+ * Even if there are no affected entries, we still need the arg
+ * to pass the "me" pointer.
+ */
+ if(!headarg){
+ headarg = (BUILDER_ARG *)malloc(sizeof(BUILDER_ARG));
+ if(!headarg){
+ emlwrite("Can't malloc space", NULL);
+ return(-1);
+ }
+ else{
+ headarg->next = NULL;
+ headarg->tptr = NULL;
+ headarg->aff = NULL;
+ headarg->me = &(entry->bldr_private);
+ }
+ }
+
+ /*
+ * The builder may make a new call back to pico() so we save and
+ * restore the pico state.
+ */
+ saved_state = save_pico_state();
+ retval = (*entry->builder)(sbuf, &s, err, headarg, mangled);
+ if(saved_state){
+ restore_pico_state(saved_state);
+ free_pico_state(saved_state);
+ }
+
+ if(mangled && *mangled & BUILDER_MESSAGE_DISPLAYED){
+ *mangled &= ~ BUILDER_MESSAGE_DISPLAYED;
+ if(mpresf == FALSE)
+ mpresf = TRUE;
+ }
+
+ if(mangled && *mangled & BUILDER_FOOTER_MANGLED){
+ *mangled &= ~ BUILDER_FOOTER_MANGLED;
+ sgarbk = TRUE;
+ pclear(term.t_nrow-1, term.t_nrow);
+ }
+
+ if(retval >= 0){
+ if(strcmp(sbuf, s)){
+ line = entry->hd_text;
+ InitEntryText(s, entry); /* arrange new one */
+ zotentry(line); /* blast old list o'entries */
+ entry->dirty = 1; /* mark it dirty */
+ retval = 1;
+ }
+
+ for(e = entry->affected_entry, arg = headarg;
+ e;
+ e = e->next_affected, arg = arg ? arg->next : NULL){
+ if(!e->sticky){
+ line = e->hd_text;
+ tmp = ucs4_to_utf8_cpystr(line->text);
+ if(strcmp(tmp, arg ? arg->tptr : "")){ /* it changed */
+ /* make sure they see it if changed */
+ e->display_it = 1;
+ InitEntryText(arg ? arg->tptr : "", e);
+ if(line == ods.top_l)
+ ods.top_l = e->hd_text;
+
+ zotentry(line); /* blast old list o'entries */
+ e->dirty = 1; /* mark it dirty */
+ retval = 1;
+ }
+
+ if(tmp)
+ fs_give((void **) &tmp);
+ }
+ }
+ }
+
+ if(s)
+ free(s);
+
+ for(arg = headarg; arg; arg = nextarg){
+ /* Don't free xtra or me, they just point to headerentry data */
+ nextarg = arg->next;
+ if(arg->tptr)
+ free(arg->tptr);
+
+ free(arg);
+ }
+
+ free(sbuf);
+ return(retval);
+}
+
+
+void
+call_expander(void)
+{
+ char **s = NULL;
+ VARS_TO_SAVE *saved_state;
+ int expret;
+
+ if(!Pmaster->expander)
+ return;
+
+ /*
+ * Since expander may make a call back to pico() we need to
+ * save and restore pico state.
+ */
+ if((saved_state = save_pico_state()) != NULL){
+
+ expret = (*Pmaster->expander)(headents, &s);
+
+ restore_pico_state(saved_state);
+ free_pico_state(saved_state);
+ ttresize();
+ picosigs();
+
+ if(expret > 0 && s){
+ char *tbuf, *p;
+ int i, biggest = 100;
+ struct headerentry *e;
+
+ /*
+ * Use tbuf to cat together multiple line entries before comparing.
+ */
+ tbuf = (char *)malloc((biggest+1) * sizeof(*tbuf));
+ for(e = headents, i=0; e->name != NULL; e++,i++){
+ int sz = 0;
+ struct hdr_line *line;
+
+ while(e->name && e->blank)
+ e++;
+
+ if(e->name == NULL)
+ continue;
+
+ for(line = e->hd_text; line != NULL; line = line->next){
+ p = ucs4_to_utf8_cpystr(line->text);
+ if(p){
+ sz += strlen(p);
+ fs_give((void **) &p);
+ }
+ }
+
+ if(sz > biggest){
+ biggest = sz;
+ free(tbuf);
+ tbuf = (char *)malloc((biggest+1) * sizeof(*tbuf));
+ }
+
+ tbuf[0] = '\0';
+ for(line = e->hd_text; line != NULL; line = line->next){
+ p = ucs4_to_utf8_cpystr(line->text);
+ if(p){
+ strncat(tbuf, p, biggest+1-strlen(tbuf)-1);
+ tbuf[biggest] = '\0';
+ fs_give((void **) &p);
+ }
+ }
+
+ if(strcmp(tbuf, s[i])){ /* it changed */
+ struct hdr_line *zline;
+
+ line = zline = e->hd_text;
+ InitEntryText(s[i], e);
+
+ /*
+ * If any of the lines for this entry are current or
+ * top, fix that.
+ */
+ for(; line != NULL; line = line->next){
+ if(line == ods.top_l)
+ ods.top_l = e->hd_text;
+
+ if(line == ods.cur_l)
+ ods.cur_l = e->hd_text;
+ }
+
+ zotentry(zline); /* blast old list o'entries */
+ }
+ }
+
+ free(tbuf);
+ }
+
+ if(s){
+ char **p;
+
+ for(p = s; *p; p++)
+ free(*p);
+
+ free(s);
+ }
+ }
+
+ return;
+}
+
+
+/*
+ * strend - neglecting white space, returns TRUE if c is at the
+ * end of the given line. otherwise FALSE.
+ */
+int
+strend(UCS *s, UCS ch)
+{
+ UCS *b;
+
+ if(s == NULL || *s == '\0')
+ return(FALSE);
+
+ for(b = &s[ucs4_strlen(s)] - 1; *b && ucs4_isspace(*b); b--){
+ if(b == s)
+ return(FALSE);
+ }
+
+ return(*b == ch);
+}
+
+
+/*
+ * ucs4_strqchr - returns pointer to first non-quote-enclosed occurance of ch in
+ * the given string. otherwise NULL.
+ * s -- the string
+ * ch -- the character we're looking for
+ * q -- q tells us if we start out inside quotes on entry and is set
+ * correctly on exit.
+ * m -- max characters we'll check for ch (set to -1 for no max)
+ */
+UCS *
+ucs4_strqchr(UCS *s, UCS ch, int *q, int m)
+{
+ int quoted = (q) ? *q : 0;
+
+ for(; s && *s && m != 0; s++, m--){
+ if(*s == '"'){
+ quoted = !quoted;
+ if(q)
+ *q = quoted;
+ }
+
+ if(!quoted && *s == ch)
+ return(s);
+ }
+
+ return(NULL);
+}
+
+
+/*
+ * KillHeaderLine() - kill a line in the header
+ *
+ * notes:
+ * This is pretty simple. Just using the emacs kill buffer
+ * and its accompanying functions to cut the text from lines.
+ *
+ * returns:
+ * TRUE if hldelete worked
+ * FALSE otherwise
+ */
+int
+KillHeaderLine(struct hdr_line *l, int append)
+{
+ UCS *c;
+ int i = ods.p_ind;
+ int nl = TRUE;
+
+ if(!append)
+ kdelete();
+
+ c = l->text;
+ if (gmode & MDDTKILL){
+ if (c[i] == '\0') /* don't insert a new line after this line*/
+ nl = FALSE;
+ /*put to be deleted part into kill buffer */
+ for (i=ods.p_ind; c[i] != '\0'; i++)
+ kinsert(c[i]);
+ }else{
+ while(*c != '\0') /* splat out the line */
+ kinsert(*c++);
+ }
+
+ if (nl)
+ kinsert('\n'); /* helpful to yank in body */
+
+#ifdef _WINDOWS
+ mswin_killbuftoclip (kremove);
+#endif
+
+ if (gmode & MDDTKILL){
+ if (l->text[0]=='\0'){
+
+ if(l->next && l->prev)
+ ods.cur_l = next_hline(&ods.cur_e, l);
+ else if(l->prev)
+ ods.cur_l = prev_hline(&ods.cur_e, l);
+
+ if(l == ods.top_l)
+ ods.top_l = ods.cur_l;
+
+ return(hldelete(l));
+ }
+ else {
+ l->text[ods.p_ind]='\0'; /* delete part of the line from the cursor */
+ return(TRUE);
+ }
+ }else{
+ if(l->next && l->prev)
+ ods.cur_l = next_hline(&ods.cur_e, l);
+ else if(l->prev)
+ ods.cur_l = prev_hline(&ods.cur_e, l);
+
+ if(l == ods.top_l)
+ ods.top_l = ods.cur_l;
+
+ return(hldelete(l)); /* blast it */
+ }
+}
+
+
+
+/*
+ * SaveHeaderLines() - insert the saved lines in the list before the
+ * current line in the header
+ *
+ * notes:
+ * Once again, just using emacs' kill buffer and its
+ * functions.
+ *
+ * returns:
+ * TRUE if something good happend
+ * FALSE otherwise
+ */
+int
+SaveHeaderLines(void)
+{
+ UCS *buf; /* malloc'd copy of buffer */
+ UCS *bp; /* pointer to above buffer */
+ register unsigned i; /* index */
+ UCS *work_buf, *work_buf_begin;
+ char empty[1];
+ int len, buf_len, work_buf_len, tentative_p_ind = 0;
+ struct hdr_line *travel, *tentative_cur_l = NULL;
+
+ if(ksize()){
+ if((bp = buf = (UCS *) malloc((ksize()+5) * sizeof(*buf))) == NULL){
+ emlwrite("Can't malloc space for saved text", NULL);
+ return(FALSE);
+ }
+ }
+ else
+ return(FALSE);
+
+ for(i=0; i < ksize(); i++)
+ if(kremove(i) != '\n') /* filter out newlines */
+ *bp++ = (UCS) kremove(i);
+
+ *bp = '\0';
+
+ while(--bp >= buf) /* kill trailing white space */
+ if(*bp != ' '){
+ if(ods.cur_l->text[0] != '\0'){
+ if(*bp == '>'){ /* inserting an address */
+ *++bp = ','; /* so add separator */
+ *++bp = '\0';
+ }
+ }
+ else{ /* nothing in field yet */
+ if(*bp == ','){ /* so blast any extra */
+ *bp = '\0'; /* separators */
+ }
+ }
+ break;
+ }
+
+ /* insert new text at the dot position */
+ buf_len = ucs4_strlen(buf);
+ tentative_p_ind = ods.p_ind + buf_len;
+ work_buf_len = ucs4_strlen(ods.cur_l->text) + buf_len;
+ work_buf = (UCS *) malloc((work_buf_len + 1) * sizeof(UCS));
+ if (work_buf == NULL) {
+ emlwrite("Can't malloc space for saved text", NULL);
+ return(FALSE);
+ }
+
+ work_buf[0] = '\0';
+ work_buf_begin = work_buf;
+ i = MIN(ods.p_ind, work_buf_len);
+ ucs4_strncpy(work_buf, ods.cur_l->text, i);
+ work_buf[i] = '\0';
+ ucs4_strncat(work_buf, buf, work_buf_len+1-ucs4_strlen(work_buf)-1);
+ work_buf[work_buf_len] = '\0';
+ ucs4_strncat(work_buf, &ods.cur_l->text[ods.p_ind], work_buf_len+1-ucs4_strlen(work_buf)-1);
+ work_buf[work_buf_len] = '\0';
+ empty[0]='\0';
+ ods.p_ind = 0;
+
+ i = TRUE;
+
+ /* insert text in HLSZ character chunks */
+ while(work_buf_len + ods.p_ind > HLSZ) {
+ ucs4_strncpy(&ods.cur_l->text[ods.p_ind], work_buf, HLSZ-ods.p_ind);
+ work_buf += (HLSZ - ods.p_ind);
+ work_buf_len -= (HLSZ - ods.p_ind);
+
+ if(FormatLines(ods.cur_l, empty, LINEWID(),
+ headents[ods.cur_e].break_on_comma, 0) == -1) {
+ i = FALSE;
+ break;
+ } else {
+ i = TRUE;
+ len = 0;
+ travel = ods.cur_l;
+ while (len < HLSZ){
+ len += ucs4_strlen(travel->text);
+ if (len >= HLSZ)
+ break;
+
+ /*
+ * This comes after the break above because it will
+ * be accounted for in the while loop below.
+ */
+ if(!tentative_cur_l){
+ if(tentative_p_ind <= ucs4_strlen(travel->text))
+ tentative_cur_l = travel;
+ else
+ tentative_p_ind -= ucs4_strlen(travel->text);
+ }
+
+ travel = travel->next;
+ }
+
+ ods.cur_l = travel;
+ ods.p_ind = ucs4_strlen(travel->text) - len + HLSZ;
+ }
+ }
+
+ /* insert the remainder of text */
+ if (i != FALSE && work_buf_len > 0) {
+ ucs4_strncpy(&ods.cur_l->text[ods.p_ind], work_buf, HLSZ-ods.p_ind);
+ ods.cur_l->text[HLSZ-1] = '\0';
+ work_buf = work_buf_begin;
+ free(work_buf);
+
+ if(FormatLines(ods.cur_l, empty, LINEWID(),
+ headents[ods.cur_e].break_on_comma, 0) == -1) {
+ i = FALSE;
+ } else {
+ len = 0;
+ travel = ods.cur_l;
+ while (len < work_buf_len + ods.p_ind){
+ if(!tentative_cur_l){
+ if(tentative_p_ind <= ucs4_strlen(travel->text))
+ tentative_cur_l = travel;
+ else
+ tentative_p_ind -= ucs4_strlen(travel->text);
+ }
+
+ len += ucs4_strlen(travel->text);
+ if (len >= work_buf_len + ods.p_ind)
+ break;
+
+ travel = travel->next;
+ }
+
+ ods.cur_l = travel;
+ ods.p_ind = ucs4_strlen(travel->text) - len + work_buf_len + ods.p_ind;
+ if(tentative_cur_l
+ && tentative_p_ind >= 0
+ && tentative_p_ind <= ucs4_strlen(tentative_cur_l->text)){
+ ods.cur_l = tentative_cur_l;
+ ods.p_ind = tentative_p_ind;
+ }
+ }
+ }
+
+ free(buf);
+ return(i);
+}
+
+
+
+/*
+ * break_point - Break the given line at the most reasonable character breakch
+ * within maxwid max characters.
+ *
+ * returns:
+ * Pointer to the best break point in s, or
+ * Pointer to the beginning of s if no break point found
+ */
+UCS *
+break_point(UCS *line, int maxwid, UCS breakch, int *quotedarg)
+{
+ UCS *bp; /* break point */
+ int quoted;
+
+ /*
+ * Start at maxwid and work back until first opportunity to break.
+ */
+ bp = ucs4_particular_width(line, maxwid);
+
+ /*
+ * Quoted should be set up for the start of line. Since we want
+ * to move to bp and work our way back we need to scan through the
+ * line up to bp setting quoted appropriately.
+ */
+ if(quotedarg)
+ ucs4_strqchr(line, '\0', quotedarg, bp-line);
+
+ quoted = quotedarg ? *quotedarg : 0;
+
+ while(bp != line){
+ if(breakch == ',' && *bp == '"') /* don't break on quoted ',' */
+ quoted = !quoted; /* toggle quoted state */
+
+ if(*bp == breakch && !quoted){
+ if(breakch == ' '){
+ if(ucs4_str_width_ptr_to_ptr(line, bp+1) < maxwid){
+ bp++; /* leave the ' ' */
+ break;
+ }
+ }
+ else{
+ /*
+ * if break char isn't a space, leave a space after
+ * the break char.
+ */
+ if(!(ucs4_str_width_ptr_to_ptr(line, bp+1) >= maxwid
+ || (bp[1] == ' ' && ucs4_str_width_ptr_to_ptr(line, bp+2) >= maxwid))){
+ bp += (bp[1] == ' ') ? 2 : 1;
+ break;
+ }
+ }
+ }
+
+ bp--;
+ }
+
+ if(quotedarg)
+ *quotedarg = quoted;
+
+ return((quoted) ? line : bp);
+}
+
+
+
+
+/*
+ * hldelete() - remove the header line pointed to by l from the linked list
+ * of lines.
+ *
+ * notes:
+ * the case of first line in field is kind of bogus. since
+ * the array of headers has a pointer to the first line, and
+ * i don't want to worry about this too much, i just copied
+ * the line below and removed it rather than the first one
+ * from the list.
+ *
+ * returns:
+ * TRUE if it worked
+ * FALSE otherwise
+ */
+int
+hldelete(struct hdr_line *l)
+{
+ register struct hdr_line *lp;
+
+ if(l == NULL)
+ return(FALSE);
+
+ if(l->next == NULL && l->prev == NULL){ /* only one line in field */
+ l->text[0] = '\0';
+ return(TRUE); /* no free only line in list */
+ }
+ else if(l->next == NULL){ /* last line in field */
+ l->prev->next = NULL;
+ }
+ else if(l->prev == NULL){ /* first line in field */
+ ucs4_strncpy(l->text, l->next->text, HLSZ);
+ l->text[HLSZ-1] = '\0';
+ lp = l->next;
+ if((l->next = lp->next) != NULL)
+ l->next->prev = l;
+ l = lp;
+ }
+ else{ /* some where in field */
+ l->prev->next = l->next;
+ l->next->prev = l->prev;
+ }
+
+ l->next = NULL;
+ l->prev = NULL;
+ free((char *)l);
+ return(TRUE);
+}
+
+
+
+/*
+ * is_blank - returns true if the next n chars from coordinates row, col
+ * on display are spaces
+ */
+int
+is_blank(int row, int col, int n)
+{
+ n += col;
+ for( ;col < n; col++){
+ if(pscr(row, col) == NULL || pscr(row, col)->c != ' ')
+ return(0);
+ }
+ return(1);
+}
+
+
+/*
+ * ShowPrompt - display key help corresponding to the current header entry
+ */
+void
+ShowPrompt(void)
+{
+ if(headents[ods.cur_e].key_label){
+ menu_header[TO_KEY].name = "^T";
+ menu_header[TO_KEY].label = headents[ods.cur_e].key_label;
+ KS_OSDATASET(&menu_header[TO_KEY], KS_OSDATAGET(&headents[ods.cur_e]));
+ }
+ else
+ menu_header[TO_KEY].name = NULL;
+
+ if(Pmaster && Pmaster->exit_label)
+ menu_header[SEND_KEY].label = Pmaster->exit_label;
+ else if(gmode & (MDVIEW | MDHDRONLY))
+ menu_header[SEND_KEY].label = (gmode & MDHDRONLY) ? "eXit/Save" : "eXit";
+ else
+ menu_header[SEND_KEY].label = N_("Send");
+
+ if(gmode & MDVIEW){
+ menu_header[CUT_KEY].name = NULL;
+ menu_header[DEL_KEY].name = NULL;
+ menu_header[UDEL_KEY].name = NULL;
+ }
+ else{
+ menu_header[CUT_KEY].name = "^K";
+ menu_header[DEL_KEY].name = "^D";
+ menu_header[UDEL_KEY].name = "^U";
+ }
+
+ if(Pmaster->ctrlr_label){
+ menu_header[RICH_KEY].label = Pmaster->ctrlr_label;
+ menu_header[RICH_KEY].name = "^R";
+ }
+ else if(gmode & MDHDRONLY){
+ menu_header[RICH_KEY].name = NULL;
+ }
+ else{
+ menu_header[RICH_KEY].label = N_("Rich Hdr");
+ menu_header[RICH_KEY].name = "^R";
+ }
+
+ if(gmode & MDHDRONLY){
+ if(headents[ods.cur_e].fileedit){
+ menu_header[PONE_KEY].name = "^_";
+ menu_header[PONE_KEY].label = N_("Edit File");
+ }
+ else
+ menu_header[PONE_KEY].name = NULL;
+
+ menu_header[ATT_KEY].name = NULL;
+ }
+ else{
+ menu_header[PONE_KEY].name = "^O";
+ menu_header[PONE_KEY].label = N_("Postpone");
+
+ menu_header[ATT_KEY].name = "^J";
+ }
+
+ wkeyhelp(menu_header);
+}
+
+
+/*
+ * packheader - packup all of the header fields for return to caller.
+ * NOTE: all of the header info passed in, including address
+ * of the pointer to each string is contained in the
+ * header entry array "headents".
+ */
+int
+packheader(void)
+{
+ register int i = 0; /* array index */
+ register int count; /* count of chars in a field */
+ register int retval = TRUE;
+ register char *bufp;
+ register struct hdr_line *line;
+ char *p;
+
+ if(!headents)
+ return(TRUE);
+
+ while(headents[i].name != NULL){
+#ifdef ATTACHMENTS
+ /*
+ * attachments are special case, already in struct we pass back
+ */
+ if(headents[i].is_attach){
+ i++;
+ continue;
+ }
+#endif
+
+ if(headents[i].blank){
+ i++;
+ continue;
+ }
+
+ /*
+ * count chars to see if we need a new malloc'd space for our
+ * array.
+ */
+ line = headents[i].hd_text;
+ count = 0;
+ while(line != NULL){
+ /*
+ * add one for possible concatination of a ' ' character ...
+ */
+ p = ucs4_to_utf8_cpystr(line->text);
+ if(p){
+ count += strlen(p);
+ if(p[0] && p[strlen(p)-1] == ',')
+ count++;
+
+ fs_give((void **) &p);
+ }
+
+ line = line->next;
+ }
+
+ line = headents[i].hd_text;
+ if(count <= headents[i].maxlen){
+ *headents[i].realaddr[0] = '\0';
+ }
+ else{
+ /*
+ * don't forget to include space for the null terminator!!!!
+ */
+ if((bufp = (char *)malloc((count+1) * sizeof(char))) != NULL){
+ *bufp = '\0';
+
+ free(*headents[i].realaddr);
+ *headents[i].realaddr = bufp;
+ headents[i].maxlen = count;
+ }
+ else{
+ emlwrite("Can't make room to pack header field.", NULL);
+ retval = FALSE;
+ }
+ }
+
+ if(retval != FALSE){
+ int saw_current_line = 0;
+
+ while(line != NULL){
+
+ /* pass the cursor offset back in Pmaster struct */
+ if(headents[i].start_here && Pmaster && !saw_current_line){
+ if(ods.cur_l == line)
+ saw_current_line++;
+ else
+ Pmaster->edit_offset += ucs4_strlen(line->text);
+ }
+
+ p = ucs4_to_utf8_cpystr(line->text);
+ if(p){
+ strncat(*headents[i].realaddr, p, headents[i].maxlen+1-strlen(*headents[i].realaddr)-1);
+ (*headents[i].realaddr)[headents[i].maxlen] = '\0';
+
+ if(p[0] && p[strlen(p)-1] == ','){
+ strncat(*headents[i].realaddr, " ", headents[i].maxlen+1-strlen(*headents[i].realaddr)-1);
+ (*headents[i].realaddr)[headents[i].maxlen] = '\0';
+ }
+
+ fs_give((void **) &p);
+ }
+
+ line = line->next;
+ }
+ }
+
+ i++;
+ }
+
+ return(retval);
+}
+
+
+
+/*
+ * zotheader - free all malloc'd lines associated with the header structs
+ */
+void
+zotheader(void)
+{
+ register struct headerentry *i;
+
+ for(i = headents; headents && i->name; i++)
+ zotentry(i->hd_text);
+}
+
+
+/*
+ * zotentry - free malloc'd space associated with the given linked list
+ */
+void
+zotentry(struct hdr_line *l)
+{
+ register struct hdr_line *ld, *lf = l;
+
+ while((ld = lf) != NULL){
+ lf = ld->next;
+ ld->next = ld->prev = NULL;
+ free((char *) ld);
+ }
+}
+
+
+
+/*
+ * zotcomma - blast any trailing commas and white space from the end
+ * of the given line
+ */
+int
+zotcomma(UCS *s)
+{
+ UCS *p;
+ int retval = FALSE;
+
+ p = &s[ucs4_strlen(s)];
+ while(--p >= s){
+ if(*p != ' '){
+ if(*p == ','){
+ *p = '\0';
+ retval = TRUE;
+ }
+
+ return(retval);
+ }
+ }
+
+ return(retval);
+}
+
+
+/*
+ * Save the current state of global variables so that we can restore
+ * them later. This is so we can call pico again.
+ * Also have to initialize some variables that normally would be set to
+ * zero on startup.
+ */
+VARS_TO_SAVE *
+save_pico_state(void)
+{
+ VARS_TO_SAVE *ret;
+ extern int vtrow;
+ extern int vtcol;
+ extern int lbound;
+ extern VIDEO **vscreen;
+ extern VIDEO **pscreen;
+ extern int pico_all_done;
+ extern jmp_buf finstate;
+ extern UCS *pico_anchor;
+
+ if((ret = (VARS_TO_SAVE *)malloc(sizeof(VARS_TO_SAVE))) == NULL)
+ return(ret);
+
+ ret->vtrow = vtrow;
+ ret->vtcol = vtcol;
+ ret->lbound = lbound;
+ ret->vscreen = vscreen;
+ ret->pscreen = pscreen;
+ ret->ods = ods;
+ ret->delim_ps = delim_ps;
+ ret->invert_ps = invert_ps;
+ ret->pico_all_done = pico_all_done;
+ memcpy(ret->finstate, finstate, sizeof(jmp_buf));
+ ret->pico_anchor = pico_anchor;
+ ret->Pmaster = Pmaster;
+ ret->fillcol = fillcol;
+ if((ret->pat = (UCS *)malloc(sizeof(UCS) * (ucs4_strlen(pat)+1))) != NULL)
+ ucs4_strncpy(ret->pat, pat, ucs4_strlen(pat)+1);
+
+ ret->ComposerTopLine = ComposerTopLine;
+ ret->ComposerEditing = ComposerEditing;
+ ret->gmode = gmode;
+ ret->alt_speller = alt_speller;
+ ret->quote_str = glo_quote_str;
+ ret->wordseps = glo_wordseps;
+ ret->currow = currow;
+ ret->curcol = curcol;
+ ret->thisflag = thisflag;
+ ret->lastflag = lastflag;
+ ret->curgoal = curgoal;
+ ret->opertree = (char *) malloc(sizeof(char) * (strlen(opertree) + 1));
+ if(ret->opertree != NULL)
+ strncpy(ret->opertree, opertree, strlen(opertree)+1);
+
+ ret->curwp = curwp;
+ ret->wheadp = wheadp;
+ ret->curbp = curbp;
+ ret->bheadp = bheadp;
+ ret->km_popped = km_popped;
+ ret->mrow = term.t_mrow;
+
+ /* Initialize for next pico call */
+ wheadp = NULL;
+ curwp = NULL;
+ bheadp = NULL;
+ curbp = NULL;
+
+ return(ret);
+}
+
+
+void
+restore_pico_state(VARS_TO_SAVE *state)
+{
+ extern int vtrow;
+ extern int vtcol;
+ extern int lbound;
+ extern VIDEO **vscreen;
+ extern VIDEO **pscreen;
+ extern int pico_all_done;
+ extern jmp_buf finstate;
+ extern UCS *pico_anchor;
+
+ clearcursor();
+ vtrow = state->vtrow;
+ vtcol = state->vtcol;
+ lbound = state->lbound;
+ vscreen = state->vscreen;
+ pscreen = state->pscreen;
+ ods = state->ods;
+ delim_ps = state->delim_ps;
+ invert_ps = state->invert_ps;
+ pico_all_done = state->pico_all_done;
+ memcpy(finstate, state->finstate, sizeof(jmp_buf));
+ pico_anchor = state->pico_anchor;
+ Pmaster = state->Pmaster;
+ if(Pmaster)
+ headents = Pmaster->headents;
+
+ fillcol = state->fillcol;
+ if(state->pat)
+ ucs4_strncpy(pat, state->pat, NPAT);
+
+ ComposerTopLine = state->ComposerTopLine;
+ ComposerEditing = state->ComposerEditing;
+ gmode = state->gmode;
+ alt_speller = state->alt_speller;
+ glo_quote_str = state->quote_str;
+ glo_wordseps = state->wordseps;
+ currow = state->currow;
+ curcol = state->curcol;
+ thisflag = state->thisflag;
+ lastflag = state->lastflag;
+ curgoal = state->curgoal;
+ if(state->opertree){
+ strncpy(opertree, state->opertree, sizeof(opertree));
+ opertree[sizeof(opertree)-1] = '\0';
+ }
+
+ curwp = state->curwp;
+ wheadp = state->wheadp;
+ curbp = state->curbp;
+ bheadp = state->bheadp;
+ km_popped = state->km_popped;
+ term.t_mrow = state->mrow;
+}
+
+
+void
+free_pico_state(VARS_TO_SAVE *state)
+{
+ if(state->pat)
+ free(state->pat);
+
+ if(state->opertree)
+ free(state->opertree);
+
+ free(state);
+}
+
+
+/*
+ * Ok to call this twice in a row because it won't do anything the second
+ * time.
+ */
+void
+fix_mangle_and_err(int *mangled, char **errmsg, char *name)
+{
+ if(mangled && *mangled){
+ ttresize();
+ picosigs();
+ PaintBody(0);
+ *mangled = 0;
+ }
+
+ if(errmsg && *errmsg){
+ if(**errmsg){
+ char err[500];
+
+ snprintf(err, sizeof(err), "%s field: %s", name, *errmsg);
+ (*term.t_beep)();
+ emlwrite(err, NULL);
+ }
+ else
+ mlerase();
+
+ free(*errmsg);
+ *errmsg = NULL;
+ }
+}
+
+
+/*
+ * What is this for?
+ * This is so that the To line will be appended to by an Lcc
+ * entry unless the user types in the To line after the Lcc
+ * has already been set.
+ */
+void
+mark_sticky(struct headerentry *h)
+{
+ if(h && (!h->sticky_special || h->bldr_private))
+ h->sticky = 1;
+}
+
+
+#ifdef MOUSE
+#undef HeaderEditor
+
+/*
+ * Wraper function for the real header editor.
+ * Does the important tasks of:
+ * 1) verifying that we _can_ edit the headers.
+ * 2) acting on the result code from the header editor.
+ */
+int
+HeaderEditor(int f, int n)
+{
+ int retval;
+
+
+#ifdef _WINDOWS
+ /* Sometimes we get here from a scroll callback, which
+ * is no good at all because mswin is not ready to process input and
+ * this _headeredit() will never do anything.
+ * Putting this test here was the most general solution I could think
+ * of. */
+ if (!mswin_caninput())
+ return (-1);
+#endif
+
+ retval = HeaderEditorWork(f, n);
+ if (retval == -3) {
+ retval = mousepress(0,0);
+ }
+ return (retval);
+}
+#endif
diff --git a/pico/display.c b/pico/display.c
new file mode 100644
index 00000000..2741be3e
--- /dev/null
+++ b/pico/display.c
@@ -0,0 +1,3306 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: display.c 1025 2008-04-08 22:59:38Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ *
+ * Program: Display functions
+ *
+ */
+
+/*
+ * The functions in this file handle redisplay. There are two halves, the
+ * ones that update the virtual display screen, and the ones that make the
+ * physical display screen the same as the virtual display screen. These
+ * functions use hints that are left in the windows by the commands.
+ *
+ */
+
+#include "../c-client/mail.h"
+#include "../c-client/utf8.h"
+
+#ifdef _WINDOWS
+/* wingdi.h uses ERROR (!) and we aren't using the c-client ERROR so... */
+#undef ERROR
+#endif
+
+#include "headers.h"
+#include "../pith/charconv/filesys.h"
+#include "../pith/charconv/utf8.h"
+
+
+void vtmove(int, int);
+void vtputc(CELL);
+void vteeol(void);
+void updateline(int, CELL *, CELL *, short *);
+void updext(void);
+void mlputi(int, int);
+void pprints(int, int);
+void mlputli(long, int);
+void showCompTitle(void);
+int nlforw(void);
+int dumbroot(int, int);
+int dumblroot(long, int);
+unsigned cellwidth_ptr_to_ptr(CELL *pstart, CELL *pend);
+unsigned vcellwidth_a_to_b(int row, int a, int b);
+#ifdef _WINDOWS
+void pico_config_menu_items (KEYMENU *);
+int update_scroll (void);
+#endif /* _WINDOWS */
+
+
+/*
+ * Standard pico keymenus...
+ */
+static KEYMENU menu_pico[] = {
+ {"^G", N_("Get Help"), KS_SCREENHELP}, {"^O", N_("WriteOut"), KS_SAVEFILE},
+ {"^R", N_("Read File"), KS_READFILE}, {"^Y", N_("Prev Pg"), KS_PREVPAGE},
+ {"^K", N_("Cut Text"), KS_NONE}, {"^C", N_("Cur Pos"), KS_CURPOSITION},
+ {"^X", N_("Exit"), KS_EXIT}, {"^J", N_("Justify"), KS_JUSTIFY},
+ {"^W", N_("Where is"), KS_WHEREIS}, {"^V", N_("Next Pg"), KS_NEXTPAGE},
+ {"^U", NULL, KS_NONE},
+#ifdef SPELLER
+ {"^T", N_("To Spell"), KS_SPELLCHK}
+#else
+ {"^D", N_("Del Char"), KS_NONE}
+#endif
+};
+#define UNCUT_KEY 10
+
+
+static KEYMENU menu_compose[] = {
+ {"^G", N_("Get Help"), KS_SCREENHELP}, {"^X", NULL, KS_SEND},
+ {"^R", N_("Read File"), KS_READFILE}, {"^Y", N_("Prev Pg"), KS_PREVPAGE},
+ {"^K", N_("Cut Text"), KS_NONE}, {"^O", N_("Postpone"), KS_POSTPONE},
+ /* TRANSLATORS: Justify is to reformat a paragraph automatically */
+ {"^C", N_("Cancel"), KS_CANCEL}, {"^J", N_("Justify"), KS_JUSTIFY},
+ {NULL, NULL, KS_NONE}, {"^V", N_("Next Pg"), KS_NEXTPAGE},
+ {"^U", NULL, KS_NONE},
+#ifdef SPELLER
+ {"^T", N_("To Spell"), KS_SPELLCHK}
+#else
+ {"^D", N_("Del Char"), KS_NONE}
+#endif
+};
+#define EXIT_KEY 1
+#define PSTPN_KEY 5
+#define WHERE_KEY 8
+
+
+/*
+ * Definition's for pico's modeline
+ */
+#define PICO_TITLE " UW PICO %s"
+#define PICO_MOD_MSG "Modified"
+#define PICO_NEWBUF_MSG "New Buffer"
+
+#define WFDEBUG 0 /* Window flag debug. */
+
+#define VFCHG 0x0001 /* Changed flag */
+#define VFEXT 0x0002 /* extended (beyond column 80) */
+#define VFREV 0x0004 /* reverse video status */
+#define VFREQ 0x0008 /* reverse video request */
+
+int vtrow = 0; /* Row location of SW cursor */
+int vtcol = 0; /* Column location of SW cursor */
+int vtind = 0; /* Index into row array of SW cursor */
+int ttrow = FARAWAY; /* Row location of HW cursor */
+int ttcol = FARAWAY; /* Column location of HW cursor */
+int lbound = 0; /* leftmost column of current line
+ being displayed */
+
+VIDEO **vscreen; /* Virtual screen. */
+VIDEO **pscreen; /* Physical screen. */
+
+
+/*
+ * Initialize the data structures used by the display code. The edge vectors
+ * used to access the screens are set up. The operating system's terminal I/O
+ * channel is set up. All the other things get initialized at compile time.
+ * The original window has "WFCHG" set, so that it will get completely
+ * redrawn on the first call to "update".
+ */
+int
+vtinit(void)
+{
+ int i, j;
+ VIDEO *vp;
+ CELL ac;
+
+ ac.c = ' ';
+ ac.a = 0;
+
+ if(Pmaster == NULL)
+ vtterminalinfo(gmode & MDTCAPWINS);
+
+ (*term.t_open)();
+
+ (*term.t_rev)(FALSE);
+ vscreen = (VIDEO **) malloc((term.t_nrow+1)*sizeof(VIDEO *));
+ memset(vscreen, 0, (term.t_nrow+1)*sizeof(VIDEO *));
+ if (vscreen == NULL){
+ emlwrite("Allocating memory for virtual display failed.", NULL);
+ return(FALSE);
+ }
+
+ pscreen = (VIDEO **) malloc((term.t_nrow+1)*sizeof(VIDEO *));
+ memset(pscreen, 0, (term.t_nrow+1)*sizeof(VIDEO *));
+ if (pscreen == NULL){
+ free((void *)vscreen);
+ emlwrite("Allocating memory for physical display failed.", NULL);
+ return(FALSE);
+ }
+
+
+ for (i = 0; i <= term.t_nrow; ++i) {
+ vp = (VIDEO *) malloc(sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
+
+ if (vp == NULL){
+ free((void *)vscreen);
+ free((void *)pscreen);
+ emlwrite("Allocating memory for virtual display lines failed.",
+ NULL);
+ return(FALSE);
+ }
+ else
+ for(j = 0; j < term.t_ncol; j++)
+ vp->v_text[j] = ac;
+
+ vp->v_flag = 0;
+ vscreen[i] = vp;
+
+ vp = (VIDEO *) malloc(sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
+
+ if (vp == NULL){
+ free((void *)vscreen[i]);
+ while(--i >= 0){
+ free((void *)vscreen[i]);
+ free((void *)pscreen[i]);
+ }
+
+ free((void *)vscreen);
+ free((void *)pscreen);
+ emlwrite("Allocating memory for physical display lines failed.",
+ NULL);
+ return(FALSE);
+ }
+ else
+ for(j = 0; j < term.t_ncol; j++)
+ vp->v_text[j] = ac;
+
+ vp->v_flag = 0;
+ pscreen[i] = vp;
+ }
+
+ return(TRUE);
+}
+
+int
+vtterminalinfo(int termcap_wins)
+{
+ return((term.t_terminalinfo) ? (*term.t_terminalinfo)(termcap_wins)
+ : (Pmaster ? 0 : TRUE));
+}
+
+
+/*
+ * Clean up the virtual terminal system, in anticipation for a return to the
+ * operating system. Move down to the last line and clear it out (the next
+ * system prompt will be written in the line). Shut down the channel to the
+ * terminal.
+ */
+void
+vttidy(void)
+{
+ movecursor(term.t_nrow-1, 0);
+ peeol();
+ movecursor(term.t_nrow, 0);
+ peeol();
+ (*term.t_close)();
+}
+
+
+/*
+ * Set the virtual cursor to the specified row and column on the virtual
+ * screen. There is no checking for nonsense values; this might be a good
+ * idea during the early stages.
+ */
+void
+vtmove(int row, int col)
+{
+ vtrow = row;
+ vtcol = col;
+
+ if(vtcol < 0)
+ vtind = -1;
+ else if(vtcol == 0)
+ vtind = vtcol;
+ else{
+ /*
+ * This is unused so don't worry about it.
+ */
+ assert(0);
+ }
+}
+
+
+/*
+ * Write a character to the virtual screen. The virtual row and column are
+ * updated. If the line is too long put a "$" in the last column. This routine
+ * only puts printing characters into the virtual terminal buffers. Only
+ * column overflow is checked.
+ */
+void
+vtputc(CELL c)
+{
+ VIDEO *vp;
+ CELL ac;
+ int w;
+
+ vp = vscreen[vtrow];
+ ac.c = ' ';
+ ac.a = c.a;
+
+ if (vtcol >= term.t_ncol) {
+ /*
+ * What's this supposed to be doing? This sets vtcol
+ * to the even multiple of 8 >= vtcol. Why are we doing that?
+ * Must make tab work correctly.
+ * 24 -> 24
+ * 25 -> 32
+ * ...
+ * 31 -> 32
+ * 32 -> 32
+ * 33 -> 40
+ */
+ vtcol = (vtcol + 0x07) & ~0x07;
+ ac.c = '$';
+
+ /*
+ * If we get to here that means that there must be characters
+ * past the right hand edge, so we want to put a $ character
+ * in the last visible character. It would be nice to replace
+ * the last visible character by a double-$ if it is double-width
+ * but we aren't doing that because we'd have to add up the widths
+ * starting at the left hand margin each time through.
+ */
+ if(vtind > 0 && vtind <= term.t_ncol)
+ vp->v_text[vtind-1] = ac;
+ }
+ else if (c.c == '\t') {
+ do {
+ vtputc(ac);
+ }
+ while (((vtcol + (vtrow==currow ? lbound : 0)) & 0x07) != 0 && vtcol < term.t_ncol);
+ }
+ else if (ISCONTROL(c.c)){
+ ac.c = '^';
+ vtputc(ac);
+ ac.c = ((c.c & 0x7f) | 0x40);
+ vtputc(ac);
+ }
+ else{
+
+ /*
+ * Have to worry about what happens if we skip over 0
+ * with a double-width character. There may be a better
+ * place to be setting vtind, or maybe we could make do
+ * without it.
+ */
+
+ w = wcellwidth((UCS) c.c);
+ w = (w >= 0 ? w : 1);
+
+ if(vtcol == 0 || (vtcol < 0 && vtcol + w == 1)){
+ vtind = 0;
+ if(vtcol < 0)
+ vtcol = 0;
+ }
+
+ /*
+ * Double-width character overlaps right edge.
+ * Replace it with a $.
+ */
+ if(vtcol + w > term.t_ncol){
+ ac.c = '$';
+ c = ac;
+ w = 1;
+ }
+
+ if(vtind >= 0 && vtind < term.t_ncol)
+ vp->v_text[vtind++] = c;
+
+ vtcol += w;
+ }
+}
+
+
+/*
+ * Erase from the end of the software cursor to the end of the line on which
+ * the software cursor is located.
+ */
+void
+vteeol(void)
+{
+ register VIDEO *vp;
+ CELL c;
+
+ c.c = ' ';
+ c.a = 0;
+ vp = vscreen[vtrow];
+
+ if(vtind >= 0)
+ while (vtind < term.t_ncol)
+ vp->v_text[vtind++] = c;
+
+ vtcol = term.t_ncol;
+}
+
+
+/*
+ * Make sure that the display is right. This is a three part process. First,
+ * scan through all of the windows looking for dirty ones. Check the framing,
+ * and refresh the screen. Second, make sure that "currow" and "curcol" are
+ * correct for the current window. Third, make the virtual and physical
+ * screens the same.
+ */
+void
+update(void)
+{
+ LINE *lp;
+ WINDOW *wp;
+ VIDEO *vp1;
+ VIDEO *vp2;
+ int i;
+ int j;
+ int scroll = 0;
+ CELL c;
+
+#if TYPEAH
+ if (typahead())
+ return;
+#endif
+
+#ifdef _WINDOWS
+ /* This tells our MS Windows module to not bother updating the
+ * cursor position while a massive screen update is in progress.
+ */
+ mswin_beginupdate ();
+#endif
+
+/*
+ * BUG: setting and unsetting whole region at a time is dumb. fix this.
+ */
+ if(curwp->w_markp){
+ unmarkbuffer();
+ markregion(1);
+ }
+
+ wp = wheadp;
+
+ while (wp != NULL){
+ /* Look at any window with update flags set on. */
+
+ if (wp->w_flag != 0){
+ /* If not force reframe, check the framing. */
+
+ if ((wp->w_flag & WFFORCE) == 0){
+ lp = wp->w_linep;
+
+ for (i = 0; i < wp->w_ntrows; ++i){
+ if (lp == wp->w_dotp)
+ goto out;
+
+ if (lp == wp->w_bufp->b_linep)
+ break;
+
+ lp = lforw(lp);
+ }
+ }
+
+ /* Not acceptable, better compute a new value for the line at the
+ * top of the window. Then set the "WFHARD" flag to force full
+ * redraw.
+ */
+ i = wp->w_force;
+
+ if (i > 0){
+ --i;
+
+ if (i >= wp->w_ntrows)
+ i = wp->w_ntrows-1;
+ }
+ else if (i < 0){
+ i += wp->w_ntrows;
+
+ if (i < 0)
+ i = 0;
+ }
+ else if(TERM_OPTIMIZE){
+ /*
+ * find dotp, if its been moved just above or below the
+ * window, use scrollxxx() to facilitate quick redisplay...
+ */
+ lp = lforw(wp->w_dotp);
+ if(lp != wp->w_dotp){
+ if(lp == wp->w_linep && lp != wp->w_bufp->b_linep){
+ scroll = 1;
+ }
+ else {
+ lp = wp->w_linep;
+ for(j=0;j < wp->w_ntrows; ++j){
+ if(lp != wp->w_bufp->b_linep)
+ lp = lforw(lp);
+ else
+ break;
+ }
+ if(lp == wp->w_dotp && j == wp->w_ntrows)
+ scroll = 2;
+ }
+ }
+ j = i = wp->w_ntrows/2;
+ }
+ else
+ i = wp->w_ntrows/2;
+
+ lp = wp->w_dotp;
+
+ while (i != 0 && lback(lp) != wp->w_bufp->b_linep){
+ --i;
+ lp = lback(lp);
+ }
+
+ /*
+ * this is supposed to speed things up by using tcap sequences
+ * to efficiently scroll the terminal screen. the thinking here
+ * is that its much faster to update pscreen[] than to actually
+ * write the stuff to the screen...
+ */
+ if(TERM_OPTIMIZE){
+ switch(scroll){
+ case 1: /* scroll text down */
+ j = j-i+1; /* add one for dot line */
+ /*
+ * do we scroll down the header as well? Well, only
+ * if we're not editing the header, we've backed up
+ * to the top, and the composer is not being
+ * displayed...
+ */
+ if(Pmaster && Pmaster->headents && !ComposerEditing
+ && (lback(lp) == wp->w_bufp->b_linep)
+ && (ComposerTopLine == COMPOSER_TOP_LINE))
+ j += entry_line(1000, TRUE); /* Never > 1000 headers */
+
+ scrolldown(wp, -1, j);
+ break;
+ case 2: /* scroll text up */
+ j = wp->w_ntrows - (j-i); /* we chose new top line! */
+ if(Pmaster && j){
+ /*
+ * do we scroll down the header as well? Well, only
+ * if we're not editing the header, we've backed up
+ * to the top, and the composer is not being
+ * displayed...
+ */
+ if(!ComposerEditing
+ && (ComposerTopLine != COMPOSER_TOP_LINE))
+ scrollup(wp, COMPOSER_TOP_LINE,
+ j+entry_line(1000, TRUE));
+ else
+ scrollup(wp, -1, j);
+ }
+ else
+ scrollup(wp, -1, j);
+ break;
+ default :
+ break;
+ }
+ }
+
+ wp->w_linep = lp;
+ wp->w_flag |= WFHARD; /* Force full. */
+out:
+ /*
+ * if the line at the top of the page is the top line
+ * in the body, show the header...
+ */
+ if(Pmaster && Pmaster->headents && !ComposerEditing){
+ if(lback(wp->w_linep) == wp->w_bufp->b_linep){
+ if(ComposerTopLine == COMPOSER_TOP_LINE){
+ i = term.t_nrow - 2 - term.t_mrow - HeaderLen();
+ if(i > 0 && nlforw() >= i) { /* room for header ? */
+ if((i = nlforw()/2) == 0 && term.t_nrow&1)
+ i = 1;
+ while(wp->w_linep != wp->w_bufp->b_linep && i--)
+ wp->w_linep = lforw(wp->w_linep);
+
+ }
+ else
+ ToggleHeader(1);
+ }
+ }
+ else{
+ if(ComposerTopLine != COMPOSER_TOP_LINE)
+ ToggleHeader(0); /* hide it ! */
+ }
+ }
+
+ /* Try to use reduced update. Mode line update has its own special
+ * flag. The fast update is used if the only thing to do is within
+ * the line editing.
+ */
+ lp = wp->w_linep;
+ i = wp->w_toprow;
+
+ if ((wp->w_flag & ~WFMODE) == WFEDIT){
+ while (lp != wp->w_dotp){
+ ++i;
+ lp = lforw(lp);
+ }
+
+ vscreen[i]->v_flag |= VFCHG;
+ vtmove(i, 0);
+
+ for (j = 0; j < llength(lp); ++j)
+ vtputc(lgetc(lp, j));
+
+ vteeol();
+ }
+ else if ((wp->w_flag & (WFEDIT | WFHARD)) != 0){
+ while (i < wp->w_toprow+wp->w_ntrows){
+ vscreen[i]->v_flag |= VFCHG;
+ vtmove(i, 0);
+
+ /* if line has been changed */
+ if (lp != wp->w_bufp->b_linep){
+ for (j = 0; j < llength(lp); ++j)
+ vtputc(lgetc(lp, j));
+
+ lp = lforw(lp);
+ }
+
+ vteeol();
+ ++i;
+ }
+ }
+#if ~WFDEBUG
+ if ((wp->w_flag&WFMODE) != 0)
+ modeline(wp);
+
+ wp->w_flag = 0;
+ wp->w_force = 0;
+#endif
+ }
+#if WFDEBUG
+ modeline(wp);
+ wp->w_flag = 0;
+ wp->w_force = 0;
+#endif
+
+ /* and onward to the next window */
+ wp = wp->w_wndp;
+ }
+
+ /* Always recompute the row and column number of the hardware cursor. This
+ * is the only update for simple moves.
+ */
+ lp = curwp->w_linep;
+ currow = curwp->w_toprow;
+
+ while (lp != curwp->w_dotp){
+ ++currow;
+ lp = lforw(lp);
+ }
+
+ curcol = 0;
+ i = 0;
+
+ while (i < curwp->w_doto){
+ c = lgetc(lp, i++);
+
+ if(c.c == '\t'){
+ curcol |= 0x07;
+ ++curcol;
+ }
+ else if(ISCONTROL(c.c)){
+ curcol += 2;
+ }
+ else{
+ int w;
+
+ w = wcellwidth((UCS) c.c);
+ curcol += (w >= 0 ? w : 1);
+ }
+ }
+
+ if (curcol >= term.t_ncol) { /* extended line. */
+ /* flag we are extended and changed */
+ vscreen[currow]->v_flag |= VFEXT | VFCHG;
+ updext(); /* and output extended line */
+ } else
+ lbound = 0; /* not extended line */
+
+ /* make sure no lines need to be de-extended because the cursor is
+ * no longer on them
+ */
+
+ wp = wheadp;
+
+ while (wp != NULL) {
+ lp = wp->w_linep;
+ i = wp->w_toprow;
+
+ while (i < wp->w_toprow + wp->w_ntrows) {
+ if (vscreen[i]->v_flag & VFEXT) {
+ /* always flag extended lines as changed */
+ vscreen[i]->v_flag |= VFCHG;
+ if ((wp != curwp) || (lp != wp->w_dotp) ||
+ (curcol < term.t_ncol)) {
+ vtmove(i, 0);
+ for (j = 0; j < llength(lp); ++j)
+ vtputc(lgetc(lp, j));
+ vteeol();
+
+ /* this line no longer is extended */
+ vscreen[i]->v_flag &= ~VFEXT;
+ }
+ }
+ lp = lforw(lp);
+ ++i;
+ }
+ /* and onward to the next window */
+ wp = wp->w_wndp;
+ }
+
+ /* Special hacking if the screen is garbage. Clear the hardware screen,
+ * and update your copy to agree with it. Set all the virtual screen
+ * change bits, to force a full update.
+ */
+
+ if (sgarbf != FALSE){
+ if(Pmaster){
+ int rv;
+
+ showCompTitle();
+
+ if(ComposerTopLine != COMPOSER_TOP_LINE){
+ UpdateHeader(0); /* arrange things */
+ PaintHeader(COMPOSER_TOP_LINE, TRUE);
+ }
+
+ /*
+ * since we're using only a portion of the screen and only
+ * one buffer, only clear enough screen for the current window
+ * which is to say the *only* window.
+ */
+ for(i=wheadp->w_toprow;i<=term.t_nrow; i++){
+ movecursor(i, 0);
+ peeol();
+ vscreen[i]->v_flag |= VFCHG;
+ }
+ rv = (*Pmaster->showmsg)('X' & 0x1f); /* ctrl-L */
+ ttresize();
+ picosigs(); /* restore altered handlers */
+ if(rv) /* Did showmsg corrupt the display? */
+ PaintBody(0); /* Yes, repaint */
+ movecursor(wheadp->w_toprow, 0);
+ }
+ else{
+ for (i = 0; i < term.t_nrow-term.t_mrow; ++i){
+ vscreen[i]->v_flag |= VFCHG;
+ vp1 = pscreen[i];
+ c.c = ' ';
+ c.a = 0;
+ for (j = 0; j < term.t_ncol; ++j)
+ vp1->v_text[j] = c;
+ }
+
+ movecursor(0, 0); /* Erase the screen. */
+ (*term.t_eeop)();
+
+ }
+
+ sgarbf = FALSE; /* Erase-page clears */
+ mpresf = FALSE; /* the message area. */
+
+ if(Pmaster)
+ modeline(curwp);
+ else
+ sgarbk = TRUE; /* fix the keyhelp as well...*/
+ }
+
+ /* Make sure that the physical and virtual displays agree. Unlike before,
+ * the "updateline" code is only called with a line that has been updated
+ * for sure.
+ */
+ if(Pmaster)
+ i = curwp->w_toprow;
+ else
+ i = 0;
+
+ if (term.t_nrow > term.t_mrow)
+ c.c = term.t_nrow - term.t_mrow;
+ else
+ c.c = 0;
+
+ for (; i < (int)c.c; ++i){
+
+ vp1 = vscreen[i];
+
+ /* for each line that needs to be updated, or that needs its
+ reverse video status changed, call the line updater */
+ j = vp1->v_flag;
+ if (j & VFCHG){
+
+#if TYPEAH
+ if (typahead()){
+#ifdef _WINDOWS
+ mswin_endupdate ();
+#endif
+ return;
+ }
+#endif
+ vp2 = pscreen[i];
+
+ updateline(i, &vp1->v_text[0], &vp2->v_text[0], &vp1->v_flag);
+ }
+ }
+
+ if(Pmaster == NULL){
+
+ if(sgarbk != FALSE){
+ if(term.t_mrow > 0){
+ movecursor(term.t_nrow-1, 0);
+ peeol();
+ movecursor(term.t_nrow, 0);
+ peeol();
+ }
+
+ if(lastflag&CFFILL){
+ /* TRANSLATORS: UnJustify means undo the previous
+ Justify command. */
+ menu_pico[UNCUT_KEY].label = N_("UnJustify");
+ if(!(lastflag&CFFLBF)){
+ emlwrite(_("Can now UnJustify!"), NULL);
+ mpresf = FARAWAY; /* remove this after next keystroke! */
+ }
+ }
+ else
+ menu_pico[UNCUT_KEY].label = N_("UnCut Text");
+
+ wkeyhelp(menu_pico);
+ sgarbk = FALSE;
+ }
+ }
+
+ if(lastflag&CFFLBF){
+ emlwrite(_("Can now UnJustify!"), NULL);
+ mpresf = FARAWAY; /* remove this after next keystroke! */
+ }
+
+ /* Finally, update the hardware cursor and flush out buffers. */
+
+ movecursor(currow, curcol - lbound);
+#ifdef _WINDOWS
+ mswin_endupdate ();
+
+ /*
+ * Update the scroll bars. This function is where curbp->b_linecnt
+ * is really managed. See update_scroll.
+ */
+ update_scroll ();
+#endif
+ (*term.t_flush)();
+}
+
+
+/* updext - update the extended line which the cursor is currently
+ * on at a column greater than the terminal width. The line
+ * will be scrolled right or left to let the user see where
+ * the cursor is
+ */
+void
+updext(void)
+{
+ int rcursor; /* real cursor location */
+ LINE *lp; /* pointer to current line */
+ int j; /* index into line */
+ int w = 0;
+ int ww;
+
+ /*
+ * Calculate what column the real cursor will end up in.
+ * The cursor will be in the rcursor'th column. So if we're
+ * counting columns 0 1 2 3 and rcursor is 8, then rcursor
+ * will be over cell 7.
+ *
+ * What this effectively does is to scroll the screen as we're
+ * moving to the right when the cursor first passes off the
+ * screen's right edge. It would be nice if it did the same
+ * thing coming back to the left. Instead, in order that the
+ * screen's display depends only on the curcol and not on
+ * how we got there, the screen scrolls when we pass the
+ * t_margin column. It's also kind of funky that you can't
+ * see the character under the $ but you can delete it.
+ */
+ rcursor = ((curcol - term.t_ncol) % (term.t_ncol - term.t_margin + 1)) + term.t_margin;
+ lbound = curcol - rcursor + 1;
+
+ /*
+ * Make sure lbound is set so that a double-width character does
+ * not straddle the boundary. If it does, move over one cell.
+ */
+ lp = curwp->w_dotp; /* line to output */
+ for (j=0; j<llength(lp) && w < lbound; ++j){
+ ww = wcellwidth((UCS) lgetc(lp, j).c);
+ w += (ww >= 0 ? ww : 1);
+ }
+
+ if(w > lbound)
+ lbound = w;
+
+
+ /* scan through the line outputing characters to the virtual screen
+ * once we reach the left edge
+ */
+ vtmove(currow, -lbound); /* start scanning offscreen */
+ for (j=0; j<llength(lp); ++j) /* until the end-of-line */
+ vtputc(lgetc(lp, j));
+
+ /* truncate the virtual line */
+ vteeol();
+
+ /* and put a '$' in column 1, may have to adjust curcol */
+ w = wcellwidth((UCS) vscreen[currow]->v_text[0].c);
+ vscreen[currow]->v_text[0].c = '$';
+ vscreen[currow]->v_text[0].a = 0;
+ if(w == 2){
+ /*
+ * We want to put $ in the first two columns so that it
+ * takes up the right amount of space, but that means we
+ * have to scoot the real characters over one slot.
+ */
+ for (j = term.t_ncol-1; j >= 2; --j)
+ vscreen[currow]->v_text[j] = vscreen[currow]->v_text[j-1];
+
+ vscreen[currow]->v_text[1].c = '$';
+ vscreen[currow]->v_text[1].a = 0;
+ }
+}
+
+
+/*
+ * Update a single line. This does not know how to use insert or delete
+ * character sequences; we are using VT52 functionality. Update the physical
+ * row and column variables.
+ */
+void
+updateline(int row, /* row on screen */
+ CELL vline[], /* what we want it to end up as */
+ CELL pline[], /* what it looks like now */
+ short *flags)
+{
+ CELL *cp1, *cp2, *cp3, *cp4, *cp5, *cp6, *cp7;
+ int display = TRUE;
+ int nbflag; /* non-blanks to the right flag? */
+ int cleartoeol = 0;
+
+ if(row < 0 || row > term.t_nrow)
+ return;
+
+ /* set up pointers to virtual and physical lines */
+ cp1 = &vline[0];
+ cp2 = &pline[0];
+ cp3 = &vline[term.t_ncol];
+
+ /* advance past any common chars at the left */
+ while (cp1 != cp3 && cp1[0].c == cp2[0].c && cp1[0].a == cp2[0].a) {
+ ++cp1;
+ ++cp2;
+ }
+
+/* This can still happen, even though we only call this routine on changed
+ * lines. A hard update is always done when a line splits, a massive
+ * change is done, or a buffer is displayed twice. This optimizes out most
+ * of the excess updating. A lot of computes are used, but these tend to
+ * be hard operations that do a lot of update, so I don't really care.
+ */
+ /* if both lines are the same, no update needs to be done */
+ if (cp1 == cp3){
+ *flags &= ~VFCHG; /* mark it clean */
+ return;
+ }
+
+ /* find out if there is a match on the right */
+ nbflag = FALSE;
+ cp3 = &vline[term.t_ncol];
+ cp4 = &pline[term.t_ncol];
+
+ if(cellwidth_ptr_to_ptr(cp1, cp3) == cellwidth_ptr_to_ptr(cp2, cp4))
+ while (cp3[-1].c == cp4[-1].c && cp3[-1].a == cp4[-1].a) {
+ --cp3;
+ --cp4;
+ if (cp3[0].c != ' ' || cp3[0].a != 0) /* Note if any nonblank */
+ nbflag = TRUE; /* in right match. */
+ }
+
+ cp5 = cp3;
+
+ if (nbflag == FALSE && TERM_EOLEXIST) { /* Erase to EOL ? */
+ while (cp5 != cp1 && cp5[-1].c == ' ' && cp5[-1].a == 0)
+ --cp5;
+
+ if (cp3-cp5 <= 3) /* Use only if erase is */
+ cp5 = cp3; /* fewer characters. */
+ }
+
+ /* go to start of differences */
+ movecursor(row, cellwidth_ptr_to_ptr(&vline[0], cp1));
+
+ if (!nbflag) { /* use insert or del char? */
+ cp6 = cp3;
+ cp7 = cp4;
+
+ if(TERM_INSCHAR
+ &&(cp7!=cp2 && cp6[0].c==cp7[-1].c && cp6[0].a==cp7[-1].a)){
+ while (cp7 != cp2 && cp6[0].c==cp7[-1].c && cp6[0].a==cp7[-1].a){
+ --cp7;
+ --cp6;
+ }
+
+ if (cp7==cp2 && cp4-cp2 > 3){
+ int ww;
+
+ (*term.t_rev)(cp1->a); /* set inverse for this char */
+ o_insert((UCS) cp1->c); /* insert the char */
+ ww = wcellwidth((UCS) cp1->c);
+ ttcol += (ww >= 0 ? ww : 1);
+ display = FALSE; /* only do it once!! */
+ }
+ }
+ else if(TERM_DELCHAR && cp3 != cp1 && cp7[0].c == cp6[-1].c
+ && cp7[0].a == cp6[-1].a){
+ while (cp6 != cp1 && cp7[0].c==cp6[-1].c && cp7[0].a==cp6[-1].a){
+ --cp7;
+ --cp6;
+ }
+
+ if (cp6==cp1 && cp5-cp6 > 3){
+ int w;
+
+ w = wcellwidth((UCS) cp7[0].c);
+ w = (w >= 0 ? w : 1);
+ while(w-- > 0) /* in case double-width char */
+ o_delete(); /* delete the char */
+ display = FALSE; /* only do it once!! */
+ }
+ }
+ }
+
+ if(cp1 != cp5 && display){
+ int w1, w2;
+
+ /*
+ * If we need to copy characters from cp1 to cp2 and
+ * we need to display them, then we have to worry about
+ * the characters that we are replacing being of a different
+ * width than the new characters, else the display may be
+ * messed up.
+ *
+ * If the new width (w1) is less than the old width, that means
+ * we will leave behind some old remnants if we aren't careful.
+ * If the new width is larger than the old width, we have to
+ * make sure we draw the characters all the way to the end
+ * in order to get it right. Take advantage of clear to end
+ * of line if we have it.
+ */
+ w1 = cellwidth_ptr_to_ptr(cp1, cp3);
+ w2 = cellwidth_ptr_to_ptr(cp2, cp2 + (cp3-cp1));
+
+ if(w1 < w2 || (nbflag && w1 != w2)){
+ if(TERM_EOLEXIST){
+ if(nbflag){
+ /*
+ * Draw all of the characters starting with cp1
+ * until we get to all spaces, then clear to the end of
+ * line from there. Watch out we don't run over the
+ * right hand edge, which shouldn't happen.
+ *
+ * Set cp5 to the first of the repeating spaces.
+ */
+ cp5 = &vline[term.t_ncol];
+ while (cp5 != cp1 && cp5[-1].c == ' ' && cp5[-1].a == 0)
+ --cp5;
+ }
+
+ /*
+ * In the !nbflag case we want spaces from cp5 on.
+ * Setting cp3 to something different from cp5 triggers
+ * the clear to end of line below.
+ */
+ if(cellwidth_ptr_to_ptr(&vline[0], cp5) < term.t_ncol)
+ cleartoeol++;
+ }
+ else{
+ int w;
+
+ /*
+ * No peeol so draw all the way to the edge whether they
+ * are spaces or not.
+ */
+ cp3 = &vline[0];
+ for(w = 0; w < term.t_ncol; cp3++){
+ int ww;
+
+ ww = wcellwidth((UCS) cp3->c);
+ w += (ww >= 0 ? ww : 1);
+ }
+
+ cp5 = cp3;
+ }
+ }
+ }
+
+ while (cp1 != cp5) { /* Ordinary. */
+ int ww;
+
+ if(display){
+ (*term.t_rev)(cp1->a); /* set inverse for this char */
+ (*term.t_putchar)(cp1->c);
+ }
+
+ ww = wcellwidth((UCS) cp1->c);
+ ttcol += (ww >= 0 ? ww : 1);
+
+ *cp2++ = *cp1++;
+ }
+
+ (*term.t_rev)(0); /* turn off inverse anyway! */
+
+ if (cp5 != cp3 || cleartoeol) { /* Erase. */
+ if(display)
+ peeol();
+ else
+ while (cp1 != cp3)
+ *cp2++ = *cp1++;
+ }
+
+ *flags &= ~VFCHG; /* flag this line is changed */
+}
+
+
+/*
+ * Redisplay the mode line for the window pointed to by the "wp". This is the
+ * only routine that has any idea of how the modeline is formatted. You can
+ * change the modeline format by hacking at this routine. Called by "update"
+ * any time there is a dirty window.
+ */
+void
+modeline(WINDOW *wp)
+{
+ if(Pmaster){
+ if(ComposerEditing)
+ ShowPrompt();
+ else{
+ menu_compose[EXIT_KEY].label = (Pmaster->headents)
+ ? N_("Send") :N_("Exit");
+ menu_compose[PSTPN_KEY].name = (Pmaster->headents)
+ ? "^O" : NULL;
+ menu_compose[PSTPN_KEY].label = (Pmaster->headents)
+ ? N_("Postpone") : NULL;
+ menu_compose[WHERE_KEY].name = (Pmaster->alt_ed) ? "^_" : "^W";
+ menu_compose[WHERE_KEY].label = (Pmaster->alt_ed) ? N_("Alt Edit")
+ : N_("Where is");
+ KS_OSDATASET(&menu_compose[WHERE_KEY],
+ (Pmaster->alt_ed) ? KS_ALTEDITOR : KS_WHEREIS);
+ menu_compose[UNCUT_KEY].label = (thisflag&CFFILL) ? N_("UnJustify")
+ : N_("UnCut Text");
+ wkeyhelp(menu_compose);
+ }
+ }
+ else{
+ BUFFER *bp;
+ char t1[NLINE], t2[NLINE], t3[NLINE], tline[NLINE];
+ int w1, w2, w3, w1_to_2, w2_to_3, w3_to_r;
+ UCS *ucs;
+
+ vtmove(1, 0);
+ vteeol();
+ vscreen[0]->v_flag |= VFCHG; /* Redraw next time. */
+ vtmove(0, 0); /* Seek to right line. */
+
+ snprintf(t1, sizeof(t1), PICO_TITLE, version); /* write version */
+
+ bp = wp->w_bufp;
+ if(bp->b_fname[0]) /* File name? */
+ snprintf(t2, sizeof(t2), "File: %s", bp->b_fname);
+ else{
+ strncpy(t2, PICO_NEWBUF_MSG, sizeof(t2));
+ t2[sizeof(t2)-1] = '\0';
+ }
+
+ if(bp->b_flag&BFCHG){ /* "MOD" if changed. */
+ strncpy(t3, PICO_MOD_MSG, sizeof(t3));
+ t3[sizeof(t3)-1] = '\0';
+ }
+ else
+ t3[0] = '\0';
+
+#define ALLOFTHEM (w1+w1_to_2+w2+w2_to_3+w3+w3_to_r)
+#define ALLBUTSPACE (w1+w2+w3+w3_to_r)
+
+ w1 = utf8_width(t1);
+ w2 = utf8_width(t2);
+ w3 = utf8_width(t3);
+ w1_to_2 = w2_to_3 = 1; /* min values for separation */
+ w3_to_r = 2;
+
+ if(ALLOFTHEM <= term.t_ncol){ /* everything fits */
+ w1_to_2 = (term.t_ncol - ALLBUTSPACE)/2;
+ w2_to_3 = term.t_ncol - (ALLBUTSPACE + w1_to_2);
+ }
+ else{
+ w1 = 2;
+ w1_to_2 = 0;
+ if(ALLOFTHEM <= term.t_ncol)
+ w2_to_3 = term.t_ncol - (ALLBUTSPACE + w1_to_2);
+ else{
+ w1 = w1_to_2 = w3_to_r = 0;
+ if(ALLOFTHEM <= term.t_ncol)
+ w2_to_3 = term.t_ncol - (ALLBUTSPACE + w1_to_2);
+ else{
+ if(bp->b_fname[0]){
+ snprintf(t2, sizeof(t2), "%s", bp->b_fname);
+ w2 = utf8_width(t2);
+ }
+
+ if(ALLOFTHEM <= term.t_ncol)
+ w2_to_3 = term.t_ncol - (ALLBUTSPACE + w1_to_2);
+ else{
+ w2 = 8;
+ if(bp->b_fname[0] && ALLOFTHEM <= term.t_ncol){
+ /* reduce size of file */
+ w2 = term.t_ncol - (ALLOFTHEM - w2);
+ t2[0] = t2[1] = t2[2] = '.';
+ utf8_to_width_rhs(t2+3, bp->b_fname, sizeof(t2)-3, w2-3);
+ w2 = utf8_width(t2);
+ }
+ else
+ w2 = utf8_width(t2);
+
+ if(ALLOFTHEM <= term.t_ncol)
+ w2_to_3 = term.t_ncol - (ALLBUTSPACE + w1_to_2);
+ else{
+ w1 = w1_to_2 = w2 = w2_to_3 = w3_to_r = 0;
+ if(ALLOFTHEM <= term.t_ncol)
+ w2_to_3 = term.t_ncol - ALLBUTSPACE;
+ else
+ w3 = 0;
+ }
+ }
+ }
+ }
+ }
+
+ utf8_snprintf(tline, sizeof(tline),
+ "%*.*w%*.*w%*.*w%*.*w%*.*w%*.*w",
+ w1, w1, t1,
+ w1_to_2, w1_to_2, "",
+ w2, w2, t2,
+ w2_to_3, w2_to_3, "",
+ w3, w3, t3,
+ w3_to_r, w3_to_r, "");
+
+ ucs = NULL;
+ if(utf8_width(tline) <= term.t_ncol)
+ ucs = utf8_to_ucs4_cpystr(tline);
+
+ if(ucs){
+ UCS *ucsp;
+ CELL c;
+
+ c.a = 1;
+ ucsp = ucs;
+ while((c.c = CELLMASK & *ucsp++))
+ vtputc(c);
+
+ fs_give((void **) &ucs);
+ }
+ }
+}
+
+
+
+/*
+ * Send a command to the terminal to move the hardware cursor to row "row"
+ * and column "col". The row and column arguments are origin 0. Optimize out
+ * random calls. Update "ttrow" and "ttcol".
+ */
+void
+movecursor(int row, int col)
+{
+ if (row!=ttrow || col!=ttcol) {
+ ttrow = row;
+ ttcol = col;
+ (*term.t_move)(MIN(MAX(row,0),term.t_nrow), MIN(MAX(col,0),term.t_ncol-1));
+ }
+}
+
+
+/*
+ * Erase any sense we have of the cursor's HW location...
+ */
+void
+clearcursor(void)
+{
+ ttrow = ttcol = FARAWAY;
+}
+
+void
+get_cursor(int *row, int *col)
+{
+ if(row)
+ *row = ttrow;
+ if(col)
+ *col = ttcol;
+}
+
+
+/*
+ * Erase the message line. This is a special routine because the message line
+ * is not considered to be part of the virtual screen. It always works
+ * immediately; the terminal buffer is flushed via a call to the flusher.
+ */
+void
+mlerase(void)
+{
+ if (term.t_nrow < term.t_mrow)
+ return;
+
+ movecursor(term.t_nrow - term.t_mrow, 0);
+ (*term.t_rev)(0);
+ if (TERM_EOLEXIST == TRUE)
+ peeol();
+ else{
+ if(ttrow == term.t_nrow){
+ while(ttcol++ < term.t_ncol-1)
+ (*term.t_putchar)(' ');
+ }
+ else{
+ while(ttcol++ < term.t_ncol) /* track's ttcol */
+ (*term.t_putchar)(' ');
+ }
+ }
+
+ (*term.t_flush)();
+ mpresf = FALSE;
+}
+
+
+int
+mlyesno_utf8(char *utf8prompt, int dflt)
+{
+ int ret;
+ UCS *prompt;
+
+ prompt = utf8_to_ucs4_cpystr(utf8prompt ? utf8prompt : "");
+
+ ret = mlyesno(prompt, dflt);
+
+ if(prompt)
+ fs_give((void **) &prompt);
+
+ return(ret);
+}
+
+
+/*
+ * Ask a yes or no question in the message line. Return either TRUE, FALSE, or
+ * ABORT. The ABORT status is returned if the user bumps out of the question
+ * with a ^G. if d >= 0, d is the default answer returned. Otherwise there
+ * is no default.
+ */
+int
+mlyesno(UCS *prompt, int dflt)
+{
+ int rv;
+ UCS buf[NLINE], lbuf[10];
+ KEYMENU menu_yesno[12];
+ COLOR_PAIR *lastc = NULL;
+
+#ifdef _WINDOWS
+ if (mswin_usedialog ())
+ switch (mswin_yesno (prompt)) {
+ default:
+ case 0: return (ABORT);
+ case 1: return (TRUE);
+ case 2: return (FALSE);
+ }
+#endif
+
+ for(rv = 0; rv < 12; rv++){
+ menu_yesno[rv].name = NULL;
+ KS_OSDATASET(&menu_yesno[rv], KS_NONE);
+ }
+
+ menu_yesno[1].name = "Y";
+ menu_yesno[1].label = (dflt == TRUE) ? "[" N_("Yes") "]" : N_("Yes");
+ menu_yesno[6].name = "^C";
+ menu_yesno[6].label = N_("Cancel");
+ menu_yesno[7].name = "N";
+ menu_yesno[7].label = (dflt == FALSE) ? "[" N_("No") "]" : N_("No");
+ wkeyhelp(menu_yesno); /* paint generic menu */
+ sgarbk = TRUE; /* mark menu dirty */
+ if(Pmaster && curwp)
+ curwp->w_flag |= WFMODE;
+
+ ucs4_strncpy(buf, prompt, NLINE);
+ buf[NLINE-1] = '\0';
+ lbuf[0] = ' '; lbuf[1] = '?'; lbuf[2] = ' '; lbuf[3] = '\0';
+ ucs4_strncat(buf, lbuf, NLINE - ucs4_strlen(buf) - 1);
+ buf[NLINE-1] = '\0';
+ mlwrite(buf, NULL);
+ if(Pmaster && Pmaster->colors && Pmaster->colors->prcp
+ && pico_is_good_colorpair(Pmaster->colors->prcp)){
+ lastc = pico_get_cur_color();
+ (void) pico_set_colorp(Pmaster->colors->prcp, PSC_NONE);
+ }
+ else
+ (*term.t_rev)(1);
+
+ rv = -1;
+ while(1){
+ switch(GetKey()){
+ case (CTRL|'M') : /* default */
+ if(dflt >= 0){
+ pputs_utf8((dflt) ? _("Yes") : _("No"), 1);
+ rv = dflt;
+ }
+ else
+ (*term.t_beep)();
+
+ break;
+
+ case (CTRL|'C') : /* Bail out! */
+ case F2 :
+ pputs_utf8(_("ABORT"), 1);
+ rv = ABORT;
+ break;
+
+ case 'y' :
+ case 'Y' :
+ case F3 :
+ pputs_utf8(_("Yes"), 1);
+ rv = TRUE;
+ break;
+
+ case 'n' :
+ case 'N' :
+ case F4 :
+ pputs_utf8(_("No"), 1);
+ rv = FALSE;
+ break;
+
+ case (CTRL|'G') :
+ if(term.t_mrow == 0 && km_popped == 0){
+ movecursor(term.t_nrow-2, 0);
+ peeol();
+ term.t_mrow = 2;
+ if(lastc){
+ (void) pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+ else
+ (*term.t_rev)(0);
+
+ wkeyhelp(menu_yesno); /* paint generic menu */
+ mlwrite(buf, NULL);
+ if(Pmaster && Pmaster->colors && Pmaster->colors->prcp
+ && pico_is_good_colorpair(Pmaster->colors->prcp)){
+ lastc = pico_get_cur_color();
+ (void) pico_set_colorp(Pmaster->colors->prcp, PSC_NONE);
+ }
+ else
+ (*term.t_rev)(1);
+
+ sgarbk = TRUE; /* mark menu dirty */
+ km_popped++;
+ break;
+ }
+ /* else fall through */
+
+ default:
+ (*term.t_beep)();
+ case NODATA :
+ break;
+ }
+
+ (*term.t_flush)();
+ if(rv != -1){
+ if(lastc){
+ (void) pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+ else
+ (*term.t_rev)(0);
+
+ if(km_popped){
+ term.t_mrow = 0;
+ movecursor(term.t_nrow, 0);
+ peeol();
+ sgarbf = 1;
+ km_popped = 0;
+ }
+
+ return(rv);
+ }
+ }
+}
+
+
+/*
+ * Write a prompt into the message line, then read back a response. Keep
+ * track of the physical position of the cursor. If we are in a keyboard
+ * macro throw the prompt away, and return the remembered response. This
+ * lets macros run at full speed. The reply is always terminated by a carriage
+ * return. Handle erase, kill, and abort keys.
+ */
+int
+mlreply_utf8(char *utf8prompt, char *utf8buf, int nbuf, int flg, EXTRAKEYS *extras)
+{
+ return(mlreplyd_utf8(utf8prompt, utf8buf, nbuf, flg|QDEFLT, extras));
+}
+
+
+int
+mlreply(UCS *prompt, UCS *buf, int nbuf, int flg, EXTRAKEYS *extras)
+{
+ return(mlreplyd(prompt, buf, nbuf, flg|QDEFLT, extras));
+}
+
+
+/*
+ * function key mappings
+ */
+static UCS rfkm[12][2] = {
+ { F1, (CTRL|'G')},
+ { F2, (CTRL|'C')},
+ { F3, 0 },
+ { F4, 0 },
+ { F5, 0 },
+ { F6, 0 },
+ { F7, 0 },
+ { F8, 0 },
+ { F9, 0 },
+ { F10, 0 },
+ { F11, 0 },
+ { F12, 0 }
+};
+
+
+int
+mlreplyd_utf8(char *utf8prompt, char *utf8buf, int nbuf, int flg, EXTRAKEYS *extras)
+{
+ int ret;
+ UCS *b, *buf;
+ char *utf8;
+ UCS *prompt;
+
+ buf = (UCS *) fs_get(nbuf * sizeof(*b));
+ b = utf8_to_ucs4_cpystr(utf8buf);
+ if(b){
+ ucs4_strncpy(buf, b, nbuf);
+ buf[nbuf-1] = '\0';
+ fs_give((void **) &b);
+ }
+
+ prompt = utf8_to_ucs4_cpystr(utf8prompt ? utf8prompt : "");
+
+ ret = mlreplyd(prompt, buf, nbuf, flg, extras);
+
+ utf8 = ucs4_to_utf8_cpystr(buf);
+ if(utf8){
+ strncpy(utf8buf, utf8, nbuf);
+ utf8buf[nbuf-1] = '\0';
+ fs_give((void **) &utf8);
+ }
+
+ if(buf)
+ fs_give((void **) &buf);
+
+ if(prompt)
+ fs_give((void **) &prompt);
+
+ return(ret);
+}
+
+
+void
+writeachar(UCS ucs)
+{
+ pputc(ucs, 0);
+}
+
+
+/*
+ * mlreplyd - write the prompt to the message line along with a default
+ * answer already typed in. Carriage return accepts the
+ * default. answer returned in buf which also holds the initial
+ * default, nbuf is its length, def set means use default value.
+ */
+int
+mlreplyd(UCS *prompt, UCS *buf, int nbuf, int flg, EXTRAKEYS *extras)
+{
+ UCS c; /* current char */
+ UCS *b; /* pointer in buf */
+ int i, j;
+ int plen;
+ int changed = FALSE;
+ int return_val = 0;
+ KEYMENU menu_mlreply[12];
+ UCS extra_v[12];
+ struct display_line dline;
+ COLOR_PAIR *lastc = NULL;
+
+#ifdef _WINDOWS
+ if(mswin_usedialog()){
+ MDlgButton btn_list[12];
+ LPTSTR free_names[12];
+ LPTSTR free_labels[12];
+ int i, j;
+
+ memset(&free_names, 0, sizeof(LPTSTR) * 12);
+ memset(&free_labels, 0, sizeof(LPTSTR) * 12);
+ memset(&btn_list, 0, sizeof (MDlgButton) * 12);
+ j = 0;
+ for(i = 0; extras && extras[i].name != NULL; ++i) {
+ if(extras[i].label[0] != '\0') {
+ if((extras[i].key & CTRL) == CTRL)
+ btn_list[j].ch = (extras[i].key & ~CTRL) - '@';
+ else
+ btn_list[j].ch = extras[i].key;
+
+ btn_list[j].rval = extras[i].key;
+ free_names[j] = utf8_to_lptstr(extras[i].name);
+ btn_list[j].name = free_names[j];
+ free_labels[j] = utf8_to_lptstr(extras[i].label);
+ btn_list[j].label = free_labels[j];
+ j++;
+ }
+ }
+
+ btn_list[j].ch = -1;
+
+ return_val = mswin_dialog(prompt, buf, nbuf, ((flg&QDEFLT) > 0),
+ FALSE, btn_list, NULL, 0);
+
+ if(return_val == 3)
+ return_val = HELPCH;
+
+ for(i = 0; i < 12; i++){
+ if(free_names[i])
+ fs_give((void **) &free_names[i]);
+ if(free_labels[i])
+ fs_give((void **) &free_labels[i]);
+ }
+
+ return(return_val);
+ }
+#endif
+
+ menu_mlreply[0].name = "^G";
+ menu_mlreply[0].label = N_("Get Help");
+ KS_OSDATASET(&menu_mlreply[0], KS_SCREENHELP);
+ for(j = 0, i = 1; i < 6; i++){ /* insert odd extras */
+ menu_mlreply[i].name = NULL;
+ KS_OSDATASET(&menu_mlreply[i], KS_NONE);
+ rfkm[2*i][1] = 0;
+ if(extras){
+ for(; extras[j].name && j != 2*(i-1); j++)
+ ;
+
+ if(extras[j].name){
+ rfkm[2*i][1] = extras[j].key;
+ menu_mlreply[i].name = extras[j].name;
+ menu_mlreply[i].label = extras[j].label;
+ KS_OSDATASET(&menu_mlreply[i], KS_OSDATAGET(&extras[j]));
+ }
+ }
+ }
+
+ menu_mlreply[6].name = "^C";
+ menu_mlreply[6].label = N_("Cancel");
+ KS_OSDATASET(&menu_mlreply[6], KS_NONE);
+ for(j = 0, i = 7; i < 12; i++){ /* insert even extras */
+ menu_mlreply[i].name = NULL;
+ rfkm[2*(i-6)+1][1] = 0;
+ if(extras){
+ for(; extras[j].name && j != (2*(i-6)) - 1; j++)
+ ;
+
+ if(extras[j].name){
+ rfkm[2*(i-6)+1][1] = extras[j].key;
+ menu_mlreply[i].name = extras[j].name;
+ menu_mlreply[i].label = extras[j].label;
+ KS_OSDATASET(&menu_mlreply[i], KS_OSDATAGET(&extras[j]));
+ }
+ }
+ }
+
+ /* set up what to watch for and return values */
+ memset(extra_v, 0, sizeof(extra_v));
+ for(i = 0, j = 0; i < 12 && extras && extras[i].name; i++)
+ extra_v[j++] = extras[i].key;
+
+ plen = mlwrite(prompt, NULL); /* paint prompt */
+
+ if(!(flg&QDEFLT))
+ *buf = '\0';
+
+ dline.vused = ucs4_strlen(buf);
+ dline.dwid = term.t_ncol - plen;
+ dline.row = term.t_nrow - term.t_mrow;
+ dline.col = plen;
+
+ dline.dlen = 2 * dline.dwid + 100;
+
+ dline.dl = (UCS *) fs_get(dline.dlen * sizeof(UCS));
+ dline.olddl = (UCS *) fs_get(dline.dlen * sizeof(UCS));
+ memset(dline.dl, 0, dline.dlen * sizeof(UCS));
+ memset(dline.olddl, 0, dline.dlen * sizeof(UCS));
+
+ dline.movecursor = movecursor;
+ dline.writechar = writeachar;
+
+ dline.vl = buf;
+ dline.vlen = nbuf-1;
+ dline.vbase = 0;
+
+ b = &buf[(flg & QBOBUF) ? 0 : ucs4_strlen(buf)];
+
+ wkeyhelp(menu_mlreply); /* paint generic menu */
+
+ sgarbk = 1; /* mark menu dirty */
+
+ if(Pmaster && Pmaster->colors && Pmaster->colors->prcp
+ && pico_is_good_colorpair(Pmaster->colors->prcp)){
+ lastc = pico_get_cur_color();
+ (void) pico_set_colorp(Pmaster->colors->prcp, PSC_NONE);
+ }
+ else
+ (*term.t_rev)(1);
+
+ for(;;){
+
+ line_paint(b-buf, &dline, NULL);
+ (*term.t_flush)();
+
+#ifdef MOUSE
+ mouse_in_content(KEY_MOUSE, -1, -1, 0x5, 0);
+ register_mfunc(mouse_in_content,
+ term.t_nrow - term.t_mrow, plen,
+ term.t_nrow - term.t_mrow, term.t_ncol-1);
+#endif
+#ifdef _WINDOWS
+ mswin_allowpaste(MSWIN_PASTE_LINE);
+#endif
+ while((c = GetKey()) == NODATA)
+ ;
+
+#ifdef MOUSE
+ clear_mfunc(mouse_in_content);
+#endif
+#ifdef _WINDOWS
+ mswin_allowpaste(MSWIN_PASTE_DISABLE);
+#endif
+
+ switch(c = normalize_cmd(c, rfkm, 1)){
+ case (CTRL|'A') : /* CTRL-A beginning */
+ case KEY_HOME :
+ b = buf;
+ continue;
+
+ case (CTRL|'B') : /* CTRL-B back a char */
+ case KEY_LEFT:
+ if(b <= buf)
+ (*term.t_beep)();
+ else
+ b--;
+
+ continue;
+
+ case (CTRL|'C') : /* CTRL-C abort */
+ pputs_utf8(_("ABORT"), 1);
+ ctrlg(FALSE, 0);
+ return_val = ABORT;
+ goto ret;
+
+ case (CTRL|'E') : /* CTRL-E end of line */
+ case KEY_END :
+ b = &buf[ucs4_strlen(buf)];
+ continue;
+
+ case (CTRL|'F') : /* CTRL-F forward a char*/
+ case KEY_RIGHT :
+ if(*b == '\0')
+ (*term.t_beep)();
+ else
+ b++;
+
+ continue;
+
+ case (CTRL|'G') : /* CTRL-G help */
+ if(term.t_mrow == 0 && km_popped == 0){
+ movecursor(term.t_nrow-2, 0);
+ peeol();
+ sgarbk = 1; /* mark menu dirty */
+ km_popped++;
+ term.t_mrow = 2;
+ if(lastc){
+ (void) pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+ else
+ (*term.t_rev)(0);
+
+ wkeyhelp(menu_mlreply); /* paint generic menu */
+ plen = mlwrite(prompt, NULL); /* paint prompt */
+ if(Pmaster && Pmaster->colors && Pmaster->colors->prcp
+ && pico_is_good_colorpair(Pmaster->colors->prcp)){
+ lastc = pico_get_cur_color();
+ (void) pico_set_colorp(Pmaster->colors->prcp, PSC_NONE);
+ }
+ else
+ (*term.t_rev)(1);
+
+ pputs(buf, 1);
+ break;
+ }
+
+ pputs_utf8(_("HELP"), 1);
+ return_val = HELPCH;
+ goto ret;
+
+ case (CTRL|'H') : /* CTRL-H backspace */
+ case 0x7f : /* rubout */
+ if (b <= buf){
+ (*term.t_beep)();
+ break;
+ }
+ else
+ b--;
+
+ case (CTRL|'D') : /* CTRL-D delete char */
+ case KEY_DEL :
+ if (!*b){
+ (*term.t_beep)();
+ break;
+ }
+
+ changed=TRUE;
+ i = 0;
+ dline.vused--;
+ do /* blat out left char */
+ b[i] = b[i+1];
+ while(b[i++] != '\0');
+ break;
+
+ case (CTRL|'L') : /* CTRL-L redraw */
+ return_val = (CTRL|'L');
+ goto ret;
+
+ case (CTRL|'K') : /* CTRL-K kill line */
+ changed=TRUE;
+ buf[0] = '\0';
+ dline.vused = 0;
+ b = buf;
+ break;
+
+ case F1 : /* sort of same thing */
+ return_val = HELPCH;
+ goto ret;
+
+ case (CTRL|'M') : /* newline */
+ return_val = changed;
+ goto ret;
+
+#ifdef MOUSE
+ case KEY_MOUSE :
+ {
+ MOUSEPRESS mp;
+
+ mouse_get_last (NULL, &mp);
+
+ /* The clicked line have anything special on it? */
+ switch(mp.button){
+ case M_BUTTON_LEFT : /* position cursor */
+ mp.col -= plen; /* normalize column */
+ if(mp.col >= 0 && mp.col <= ucs4_strlen(buf))
+ b = buf + mp.col;
+
+ break;
+
+ case M_BUTTON_RIGHT :
+#ifdef _WINDOWS
+ mswin_allowpaste(MSWIN_PASTE_LINE);
+ mswin_paste_popup();
+ mswin_allowpaste(MSWIN_PASTE_DISABLE);
+ break;
+#endif
+
+ case M_BUTTON_MIDDLE : /* NO-OP for now */
+ default: /* just ignore */
+ break;
+ }
+ }
+
+ continue;
+#endif
+
+ default :
+
+ /* look for match in extra_v */
+ for(i = 0; i < 12; i++)
+ if(c && c == extra_v[i]){
+ return_val = c;
+ goto ret;
+ }
+
+ changed=TRUE;
+
+ if(c & (CTRL | FUNC)){ /* bag ctrl_special chars */
+ (*term.t_beep)();
+ }
+ else{
+ i = ucs4_strlen(b);
+ if(flg&QNODQT){ /* reject double quotes? */
+ if(c == '"'){
+ (*term.t_beep)();
+ continue;
+ }
+ }
+
+ if(dline.vused >= nbuf-1){
+ (*term.t_beep)();
+ continue;
+ }
+
+ do /* blat out left char */
+ b[i+1] = b[i];
+ while(i-- > 0);
+
+ dline.vused++;
+ *b++ = c;
+ }
+ }
+ }
+
+ret:
+ if(lastc){
+ (void) pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+ else
+ (*term.t_rev)(0);
+
+ (*term.t_flush)();
+
+ if(km_popped){
+ term.t_mrow = 0;
+ movecursor(term.t_nrow, 0);
+ peeol();
+ sgarbf = 1;
+ km_popped = 0;
+ }
+
+ if(dline.dl)
+ fs_give((void **) &dline.dl);
+
+ if(dline.olddl)
+ fs_give((void **) &dline.olddl);
+
+ return(return_val);
+}
+
+
+void
+emlwrite(char *utf8message, EML *eml)
+{
+ UCS *message;
+
+ message = utf8_to_ucs4_cpystr(utf8message ? utf8message : "");
+
+ emlwrite_ucs4(message, eml);
+
+ if(message)
+ fs_give((void **) &message);
+}
+
+
+/*
+ * emlwrite() - write the message string to the error half of the screen
+ * center justified. much like mlwrite (which is still used
+ * to paint the line for prompts and such), except it center
+ * the text.
+ */
+void
+emlwrite_ucs4(UCS *message, EML *eml)
+{
+ UCS *bufp, *ap;
+ int width;
+ COLOR_PAIR *lastc = NULL;
+
+ mlerase();
+
+ if(!(message && *message) || term.t_nrow < 2)
+ return; /* nothing to write or no space to write, bag it */
+
+ bufp = message;
+
+ width = ucs4_str_width(message);
+
+ /*
+ * next, figure out where the to move the cursor so the message
+ * comes out centered
+ */
+ if((ap=ucs4_strchr(message, '%')) != NULL){
+ width -= 2;
+ switch(ap[1]){
+ case '%':
+ case 'c':
+ width += (eml && eml->c) ? wcellwidth(eml->c) : 1;
+ break;
+ case 'd':
+ width += dumbroot(eml ? eml->d : 0, 10);
+ break;
+ case 'D':
+ width += dumblroot(eml ? eml->l : 0L, 10);
+ break;
+ case 'o':
+ width += dumbroot(eml ? eml->d : 0, 8);
+ break;
+ case 'x':
+ width += dumbroot(eml ? eml->d : 0, 16);
+ break;
+ case 's': /* string arg is UTF-8 */
+ width += (eml && eml->s) ? utf8_width(eml->s) : 2;
+ break;
+ }
+ }
+
+ if(width+4 <= term.t_ncol)
+ movecursor(term.t_nrow-term.t_mrow, (term.t_ncol - (width + 4))/2);
+ else
+ movecursor(term.t_nrow-term.t_mrow, 0);
+
+ if(Pmaster && Pmaster->colors && Pmaster->colors->stcp
+ && pico_is_good_colorpair(Pmaster->colors->stcp)){
+ lastc = pico_get_cur_color();
+ (void) pico_set_colorp(Pmaster->colors->stcp, PSC_NONE);
+ }
+ else
+ (*term.t_rev)(1);
+
+ pputs_utf8("[ ", 1);
+ while (*bufp != '\0' && ttcol < term.t_ncol-2){
+ if(*bufp == '\007')
+ (*term.t_beep)();
+ else if(*bufp == '%'){
+ switch(*++bufp){
+ case 'c':
+ if(eml && eml->c)
+ pputc(eml->c, 0);
+ else {
+ pputs_utf8("%c", 0);
+ }
+ break;
+ case 'd':
+ mlputi(eml ? eml->d : 0, 10);
+ break;
+ case 'D':
+ mlputli(eml ? eml->l : 0L, 10);
+ break;
+ case 'o':
+ mlputi(eml ? eml->d : 0, 16);
+ break;
+ case 'x':
+ mlputi(eml ? eml->d : 0, 8);
+ break;
+ case 's':
+ pputs_utf8((eml && eml->s) ? eml->s : "%s", 0);
+ break;
+ case '%':
+ default:
+ pputc(*bufp, 0);
+ break;
+ }
+ }
+ else
+ pputc(*bufp, 0);
+ bufp++;
+ }
+
+ pputs_utf8(" ]", 1);
+
+ if(lastc){
+ (void) pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+ else
+ (*term.t_rev)(0);
+
+ (*term.t_flush)();
+
+ mpresf = TRUE;
+}
+
+
+int
+mlwrite_utf8(char *utf8fmt, void *arg)
+{
+ UCS *fmt;
+ int ret;
+
+ fmt = utf8_to_ucs4_cpystr(utf8fmt ? utf8fmt : "");
+ ret = mlwrite(fmt, arg);
+ if(fmt)
+ fs_give((void **) &fmt);
+
+ return(ret);
+}
+
+
+/*
+ * Write a message into the message line. Keep track of the physical cursor
+ * position. A small class of printf like format items is handled. Assumes the
+ * stack grows down; this assumption is made by the "++" in the argument scan
+ * loop. Set the "message line" flag TRUE.
+ */
+int
+mlwrite(UCS *fmt, void *arg)
+{
+ int ret, ww;
+ UCS c;
+ char *ap;
+ COLOR_PAIR *lastc = NULL;
+
+ /*
+ * the idea is to only highlight if there is something to show
+ */
+ mlerase();
+ movecursor(ttrow, 0);
+
+ if(Pmaster && Pmaster->colors && Pmaster->colors->prcp
+ && pico_is_good_colorpair(Pmaster->colors->prcp)){
+ lastc = pico_get_cur_color();
+ (void) pico_set_colorp(Pmaster->colors->prcp, PSC_NONE);
+ }
+ else
+ (*term.t_rev)(1);
+
+ ap = (char *) &arg;
+
+ while ((c = *fmt++) != 0) {
+ if (c != '%') {
+ pputc(c, 1);
+ }
+ else {
+ c = *fmt++;
+ switch (c){
+ case 'd':
+ mlputi(*(int *)ap, 10);
+ ap += sizeof(int);
+ break;
+
+ case 'o':
+ mlputi(*(int *)ap, 8);
+ ap += sizeof(int);
+ break;
+
+ case 'x':
+ mlputi(*(int *)ap, 16);
+ ap += sizeof(int);
+ break;
+
+ case 'D':
+ mlputli(*(long *)ap, 10);
+ ap += sizeof(long);
+ break;
+
+ case 's':
+ pputs_utf8(*(char **)ap, 1);
+ ap += sizeof(char *);
+ break;
+
+ default:
+ pputc(c, 1);
+ ww = wcellwidth(c);
+ ttcol += (ww >= 0 ? ww : 1);
+ }
+ }
+ }
+
+ ret = ttcol;
+ while(ttcol < term.t_ncol)
+ pputc(' ', 0);
+
+ movecursor(term.t_nrow - term.t_mrow, ret);
+
+ if(lastc){
+ (void) pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+ else
+ (*term.t_rev)(0);
+
+ (*term.t_flush)();
+ mpresf = TRUE;
+
+ return(ret);
+}
+
+
+/*
+ * Write out an integer, in the specified radix. Update the physical cursor
+ * position. This will not handle any negative numbers; maybe it should.
+ */
+void
+mlputi(int i, int r)
+{
+ register int q;
+ static char hexdigits[] = "0123456789ABCDEF";
+
+ if (i < 0){
+ i = -i;
+ pputc('-', 1);
+ }
+
+ q = i/r;
+
+ if (q != 0)
+ mlputi(q, r);
+
+ pputc(hexdigits[i%r], 1);
+}
+
+
+/*
+ * do the same except as a long integer.
+ */
+void
+mlputli(long l, int r)
+{
+ register long q;
+
+ if (l < 0){
+ l = -l;
+ pputc('-', 1);
+ }
+
+ q = l/r;
+
+ if (q != 0)
+ mlputli(q, r);
+
+ pputc((int)(l%r)+'0', 1);
+}
+
+
+void
+unknown_command(UCS c)
+{
+ char buf[10], ch, *s;
+ EML eml;
+
+ buf[0] = '\0';
+ s = buf;
+
+ if(!c){
+ /* fall through */
+ }
+ else if(c & CTRL && c >= (CTRL|'@') && c <= (CTRL|'_')){
+ ch = c - (CTRL|'@') + '@';
+ snprintf(s, sizeof(buf), "^%c", ch);
+ }
+ else
+ switch(c){
+ case ' ' : s = "SPACE"; break;
+ case '\033' : s = "ESC"; break;
+ case '\177' : s = "DEL"; break;
+ case ctrl('I') : s = "TAB"; break;
+ case ctrl('J') : s = "LINEFEED"; break;
+ case ctrl('M') : s = "RETURN"; break;
+ case ctrl('Q') : s = "XON"; break;
+ case ctrl('S') : s = "XOFF"; break;
+ case KEY_UP : s = "Up Arrow"; break;
+ case KEY_DOWN : s = "Down Arrow"; break;
+ case KEY_RIGHT : s = "Right Arrow"; break;
+ case KEY_LEFT : s = "Left Arrow"; break;
+ case CTRL|KEY_UP : s = "Ctrl-Up Arrow"; break;
+ case CTRL|KEY_DOWN : s = "Ctrl-Down Arrow"; break;
+ case CTRL|KEY_RIGHT : s = "Ctrl-Right Arrow"; break;
+ case CTRL|KEY_LEFT : s = "Ctrl-Left Arrow"; break;
+ case KEY_PGUP : s = "Prev Page"; break;
+ case KEY_PGDN : s = "Next Page"; break;
+ case KEY_HOME : s = "Home"; break;
+ case KEY_END : s = "End"; break;
+ case KEY_DEL : s = "Delete"; break; /* Not necessary DEL! */
+ case F1 :
+ case F2 :
+ case F3 :
+ case F4 :
+ case F5 :
+ case F6 :
+ case F7 :
+ case F8 :
+ case F9 :
+ case F10 :
+ case F11 :
+ case F12 :
+ snprintf(s, sizeof(buf), "F%ld", (long) (c - PF1 + 1));
+ break;
+
+ default:
+ if(c < CTRL)
+ utf8_put((unsigned char *) s, (unsigned long) c);
+
+ break;
+ }
+
+ eml.s = s;
+ emlwrite("Unknown Command: %s", &eml);
+ (*term.t_beep)();
+}
+
+
+/*
+ * scrolldown - use stuff to efficiently move blocks of text on the
+ * display, and update the pscreen array to reflect those
+ * moves...
+ *
+ * wp is the window to move in
+ * r is the row at which to begin scrolling
+ * n is the number of lines to scrol
+ */
+void
+scrolldown(WINDOW *wp, int r, int n)
+{
+#ifdef TERMCAP
+ register int i;
+ register int l;
+ register VIDEO *vp1;
+ register VIDEO *vp2;
+
+ if(!n)
+ return;
+
+ if(r < 0){
+ r = wp->w_toprow;
+ l = wp->w_ntrows;
+ }
+ else{
+ if(r > wp->w_toprow)
+ vscreen[r-1]->v_flag |= VFCHG;
+ l = wp->w_toprow+wp->w_ntrows-r;
+ }
+
+ o_scrolldown(r, n);
+
+ for(i=l-n-1; i >= 0; i--){
+ vp1 = pscreen[r+i];
+ vp2 = pscreen[r+i+n];
+ memcpy(vp2, vp1, term.t_ncol * sizeof(CELL));
+ }
+ pprints(r+n-1, r);
+ ttrow = FARAWAY;
+ ttcol = FARAWAY;
+#endif /* TERMCAP */
+}
+
+
+/*
+ * scrollup - use tcap stuff to efficiently move blocks of text on the
+ * display, and update the pscreen array to reflect those
+ * moves...
+ */
+void
+scrollup(WINDOW *wp, int r, int n)
+{
+#ifdef TERMCAP
+ register int i;
+ register VIDEO *vp1;
+ register VIDEO *vp2;
+
+ if(!n)
+ return;
+
+ if(r < 0)
+ r = wp->w_toprow;
+
+ o_scrollup(r, n);
+
+ i = 0;
+ while(1){
+ if(Pmaster){
+ if(!(r+i+n < wp->w_toprow+wp->w_ntrows))
+ break;
+ }
+ else{
+ if(!((i < wp->w_ntrows-n)&&(r+i+n < wp->w_toprow+wp->w_ntrows)))
+ break;
+ }
+ vp1 = pscreen[r+i+n];
+ vp2 = pscreen[r+i];
+ memcpy(vp2, vp1, term.t_ncol * sizeof(CELL));
+ i++;
+ }
+ pprints(wp->w_toprow+wp->w_ntrows-n, wp->w_toprow+wp->w_ntrows-1);
+ ttrow = FARAWAY;
+ ttcol = FARAWAY;
+#endif /* TERMCAP */
+}
+
+
+/*
+ * print spaces in the physical screen starting from row abs(n) working in
+ * either the positive or negative direction (depending on sign of n).
+ */
+void
+pprints(int x, int y)
+{
+ register int i;
+ register int j;
+
+ if(x < y){
+ for(i = x;i <= y; ++i){
+ for(j = 0; j < term.t_ncol; j++){
+ pscreen[i]->v_text[j].c = ' ';
+ pscreen[i]->v_text[j].a = 0;
+ }
+ }
+ }
+ else{
+ for(i = x;i >= y; --i){
+ for(j = 0; j < term.t_ncol; j++){
+ pscreen[i]->v_text[j].c = ' ';
+ pscreen[i]->v_text[j].a = 0;
+ }
+ }
+ }
+ ttrow = y;
+ ttcol = 0;
+}
+
+
+/*
+ * doton - return the physical line number that the dot is on in the
+ * current window, and by side effect the number of lines remaining
+ */
+int
+doton(int *r, unsigned *chs)
+{
+ register int i = 0;
+ register LINE *lp = curwp->w_linep;
+ int l = -1;
+
+ assert(r != NULL && chs != NULL);
+
+ *chs = 0;
+ while(i++ < curwp->w_ntrows){
+ if(lp == curwp->w_dotp)
+ l = i-1;
+ lp = lforw(lp);
+ if(lp == curwp->w_bufp->b_linep){
+ i++;
+ break;
+ }
+ if(l >= 0)
+ (*chs) += llength(lp);
+ }
+ *r = i - l - term.t_mrow;
+ return(l+curwp->w_toprow);
+}
+
+
+
+/*
+ * resize_pico - given new window dimensions, allocate new resources
+ */
+int
+resize_pico(int row, int col)
+{
+ int old_nrow, old_ncol;
+ register int i;
+ register VIDEO *vp;
+
+ old_nrow = term.t_nrow;
+ old_ncol = term.t_ncol;
+
+ term.t_nrow = row;
+ term.t_ncol = col;
+
+ if (old_ncol == term.t_ncol && old_nrow == term.t_nrow)
+ return(TRUE);
+
+ if(curwp){
+ curwp->w_toprow = 2;
+ curwp->w_ntrows = term.t_nrow - curwp->w_toprow - term.t_mrow;
+ }
+
+ if(Pmaster){
+ fillcol = Pmaster->fillcolumn;
+ (*Pmaster->resize)();
+ }
+ else if(userfillcol > 0)
+ fillcol = userfillcol;
+ else
+ fillcol = term.t_ncol - 6; /* we control the fill column */
+
+ /*
+ * free unused screen space ...
+ */
+ for(i=term.t_nrow+1; i <= old_nrow; ++i){
+ free((char *) vscreen[i]);
+ free((char *) pscreen[i]);
+ }
+
+ /*
+ * realloc new space for screen ...
+ */
+ if((vscreen=(VIDEO **)realloc(vscreen,(term.t_nrow+1)*sizeof(VIDEO *))) == NULL){
+ if(Pmaster)
+ return(-1);
+ else
+ exit(1);
+ }
+
+ if((pscreen=(VIDEO **)realloc(pscreen,(term.t_nrow+1)*sizeof(VIDEO *))) == NULL){
+ if(Pmaster)
+ return(-1);
+ else
+ exit(1);
+ }
+
+ for (i = 0; i <= term.t_nrow; ++i) {
+ if(i <= old_nrow)
+ vp = (VIDEO *) realloc(vscreen[i], sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
+ else
+ vp = (VIDEO *) malloc(sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
+
+ if (vp == NULL)
+ exit(1);
+ vp->v_flag = VFCHG;
+ vscreen[i] = vp;
+ if(old_ncol < term.t_ncol){ /* don't let any garbage in */
+ vtrow = i;
+ vtcol = (i < old_nrow) ? old_ncol : 0;
+ vteeol();
+ }
+
+ if(i <= old_nrow)
+ vp = (VIDEO *) realloc(pscreen[i], sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
+ else
+ vp = (VIDEO *) malloc(sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
+
+ if (vp == NULL)
+ exit(1);
+
+ vp->v_flag = VFCHG;
+ pscreen[i] = vp;
+ }
+
+ if(!ResizeBrowser()){
+ if(Pmaster && Pmaster->headents){
+ ResizeHeader();
+ }
+ else{
+ curwp->w_flag |= (WFHARD | WFMODE);
+ pico_refresh(0, 1); /* redraw whole enchilada. */
+ update(); /* do it */
+ }
+ }
+
+ return(TRUE);
+}
+
+void
+redraw_pico_for_callback(void)
+{
+ pico_refresh(0, 1);
+ update();
+}
+
+
+/*
+ * showCompTitle - display the anchor line passed in from pine
+ */
+void
+showCompTitle(void)
+{
+ if(Pmaster){
+ UCS *bufp;
+ extern UCS *pico_anchor;
+ COLOR_PAIR *lastc = NULL;
+
+ if((bufp = pico_anchor) == NULL)
+ return;
+
+ movecursor(COMPOSER_TITLE_LINE, 0);
+ if (Pmaster->colors && Pmaster->colors->tbcp &&
+ pico_is_good_colorpair(Pmaster->colors->tbcp)){
+ lastc = pico_get_cur_color();
+ (void)pico_set_colorp(Pmaster->colors->tbcp, PSC_NONE);
+ }
+ else
+ (*term.t_rev)(1);
+
+ while (ttcol < term.t_ncol)
+ if(*bufp != '\0')
+ pputc(*bufp++, 1);
+ else
+ pputc(' ', 1);
+
+ if (lastc){
+ (void)pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+ else
+ (*term.t_rev)(0);
+
+ movecursor(COMPOSER_TITLE_LINE + 1, 0);
+ peeol();
+ }
+}
+
+
+
+/*
+ * zotdisplay - blast malloc'd space created for display maps
+ */
+void
+zotdisplay(void)
+{
+ register int i;
+
+ for (i = 0; i <= term.t_nrow; ++i){ /* free screens */
+ free((char *) vscreen[i]);
+ free((char *) pscreen[i]);
+ }
+
+ free((char *) vscreen);
+ free((char *) pscreen);
+}
+
+
+
+/*
+ * nlforw() - returns the number of lines from the top to the dot
+ */
+int
+nlforw(void)
+{
+ register int i = 0;
+ register LINE *lp = curwp->w_linep;
+
+ while(lp != curwp->w_dotp){
+ lp = lforw(lp);
+ i++;
+ }
+ return(i);
+}
+
+
+
+/*
+ * pputc - output the given char, keep track of it on the physical screen
+ * array, and keep track of the cursor
+ */
+void
+pputc(UCS c, /* char to write */
+ int a) /* and its attribute */
+{
+ int ind, width, printable_ascii = 0;
+
+ /*
+ * This is necessary but not sufficient to allow us to draw. Note that
+ * ttrow runs from 0 to t_nrow (so total number of rows is t_nrow+1)
+ * ttcol runs from 0 to t_ncol-1 (so total number of cols is t_ncol)
+ */
+ if((ttcol >= 0 && ttcol < term.t_ncol) && (ttrow >= 0 && ttrow <= term.t_nrow)){
+
+ /*
+ * Width is the number of screen columns a character will occupy.
+ */
+ if(c < 0x80 && isprint(c)){
+ printable_ascii++;
+ width = 1;
+ }
+ else
+ width = wcellwidth(c);
+
+ if(width < 0)
+ width = 1; /* will be a '?' */
+
+ if(ttcol + width <= term.t_ncol){ /* it fits */
+ /*
+ * Some terminals scroll when you write in the lower right corner
+ * of the screen, so don't write there.
+ */
+ if(!(ttrow == term.t_nrow && ttcol+width == term.t_ncol)){
+ (*term.t_putchar)(c); /* write it */
+ ind = index_from_col(ttrow, ttcol);
+ pscreen[ttrow]->v_text[ind].c = c; /* keep track of it */
+ pscreen[ttrow]->v_text[ind].a = a; /* keep track of it */
+ }
+ }
+ else{
+ /*
+ * Character overlaps right edge of screen. Hopefully the higher
+ * layers will prevent this but we're making sure.
+ *
+ * We may want to do something like writing a space character
+ * into the cells that are on the screen. We'll see.
+ */
+ }
+
+ ttcol = MIN(term.t_ncol, ttcol+width);
+ }
+}
+
+
+/*
+ * pputs - print a string and keep track of the cursor
+ */
+void
+pputs(UCS *s, /* string to write */
+ int a) /* and its attribute */
+{
+ while (*s != '\0')
+ pputc(*s++, a);
+}
+
+
+void
+pputs_utf8(char *s, int a)
+{
+ UCS *ucsstr = NULL;
+
+ if(s && *s){
+ ucsstr = utf8_to_ucs4_cpystr(s);
+ if(ucsstr){
+ pputs(ucsstr, a);
+ fs_give((void **) &ucsstr);
+ }
+ }
+}
+
+
+/*
+ * peeol - physical screen array erase to end of the line. remember to
+ * track the cursor.
+ */
+void
+peeol(void)
+{
+ int i, width = 0, ww;
+ CELL cl;
+
+ if(ttrow < 0 || ttrow > term.t_nrow)
+ return;
+
+ cl.c = ' ';
+ cl.a = 0;
+
+ /*
+ * Don't clear if we think we are sitting past the last column,
+ * that erases the last column if we just wrote it.
+ */
+ if(ttcol < term.t_ncol)
+ (*term.t_eeol)();
+
+ /*
+ * Because the characters are variable width it's a little tricky
+ * to erase the rest of the line. What we do is add up the
+ * widths of the characters until we reach ttcol
+ * then set the rest to the space character.
+ */
+ for(i = 0; i < term.t_ncol && width < ttcol; i++){
+ ww = wcellwidth((UCS) pscreen[ttrow]->v_text[i].c);
+ width += (ww >= 0 ? ww : 1);
+ }
+
+ while(i < term.t_ncol)
+ pscreen[ttrow]->v_text[i++] = cl;
+}
+
+
+/*
+ * pscr - return the character cell on the physical screen map on the
+ * given line, l, and offset, o.
+ */
+CELL *
+pscr(int l, int o)
+{
+ if((l >= 0 && l <= term.t_nrow) && (o >= 0 && o < term.t_ncol))
+ return(&(pscreen[l]->v_text[o]));
+ else
+ return(NULL);
+}
+
+
+/*
+ * pclear() - clear the physical screen from row x through row y (inclusive)
+ * row is zero origin, min row = 0 max row = t_nrow
+ * Clear whole screen -- pclear(0, term.t_nrow)
+ * Clear bottom two rows -- pclear(term.t_nrow-1, term.t_nrow)
+ * Clear bottom three rows -- pclear(term.t_nrow-2, term.t_nrow)
+ */
+void
+pclear(int x, int y)
+{
+ register int i;
+
+ x = MIN(MAX(0, x), term.t_nrow);
+ y = MIN(MAX(0, y), term.t_nrow);
+
+ for(i=x; i <= y; i++){
+ movecursor(i, 0);
+ peeol();
+ }
+}
+
+
+/*
+ * dumbroot - just get close
+ */
+int
+dumbroot(int x, int b)
+{
+ if(x < b)
+ return(1);
+ else
+ return(dumbroot(x/b, b) + 1);
+}
+
+
+/*
+ * dumblroot - just get close
+ */
+int
+dumblroot(long x, int b)
+{
+ if(x < b)
+ return(1);
+ else
+ return(dumblroot(x/b, b) + 1);
+}
+
+
+/*
+ * pinsertc - use optimized insert, fixing physical screen map.
+ * returns true if char written, false otherwise
+ */
+int
+pinsert(CELL c)
+{
+ int i, ind = 0, ww;
+ CELL *p;
+
+ if(ttrow < 0 || ttrow > term.t_nrow)
+ return(0);
+
+ if(o_insert((UCS) c.c)){ /* if we've got it, use it! */
+ p = pscreen[ttrow]->v_text; /* then clean up physical screen */
+
+ ind = index_from_col(ttrow, ttcol);
+
+ for(i = term.t_ncol-1; i > ind; i--)
+ p[i] = p[i-1]; /* shift right */
+
+ p[ind] = c; /* insert new char */
+
+ ww = wcellwidth((UCS) c.c);
+ ttcol += (ww >= 0 ? ww : 1);
+
+ return(1);
+ }
+
+ return(0);
+}
+
+
+/*
+ * pdel - use optimized delete to rub out the current char and
+ * fix the physical screen array.
+ * returns true if optimized the delete, false otherwise
+ */
+int
+pdel(void)
+{
+ int i, ind = 0, w;
+ CELL *p;
+
+ if(ttrow < 0 || ttrow > term.t_nrow)
+ return(0);
+
+ if(TERM_DELCHAR){ /* if we've got it, use it! */
+ p = pscreen[ttrow]->v_text;
+ ind = index_from_col(ttrow, ttcol);
+
+ if(ind > 0){
+ --ind;
+ w = wcellwidth((UCS) p[ind].c);
+ w = (w >= 0 ? w : 1);
+ ttcol -= w;
+
+ for(i = 0; i < w; i++){
+ (*term.t_putchar)('\b'); /* move left a char */
+ o_delete(); /* and delete it */
+ }
+
+ /* then clean up physical screen */
+ for(i=ind; i < term.t_ncol-1; i++)
+ p[i] = p[i+1];
+
+ p[i].c = ' ';
+ p[i].a = 0;
+ }
+
+ return(1);
+ }
+
+ return(0);
+}
+
+
+
+/*
+ * wstripe - write out the given string at the given location, and reverse
+ * video on flagged characters. Does the same thing as pine's
+ * stripe.
+ *
+ * I believe this needs to be fixed to work with non-ascii utf8pmt, but maybe
+ * only if you want to put the tildes before multi-byte chars.
+ */
+void
+wstripe(int line, int column, char *utf8pmt, int key)
+{
+ UCS *ucs4pmt, *u;
+ int i = 0, col = 0;
+ int j = 0;
+ int l, ww;
+ COLOR_PAIR *lastc = NULL;
+ COLOR_PAIR *kncp = NULL;
+ COLOR_PAIR *klcp = NULL;
+
+ if(line < 0 || line > term.t_nrow)
+ return;
+
+ if (Pmaster && Pmaster->colors){
+ if(pico_is_good_colorpair(Pmaster->colors->klcp))
+ klcp = Pmaster->colors->klcp;
+
+ if(klcp && pico_is_good_colorpair(Pmaster->colors->kncp))
+ kncp = Pmaster->colors->kncp;
+
+ lastc = pico_get_cur_color();
+ }
+
+ ucs4pmt = utf8_to_ucs4_cpystr(utf8pmt);
+ l = ucs4_strlen(ucs4pmt);
+ while(1){
+ if(i >= term.t_ncol || col >= term.t_ncol || j >= l)
+ return; /* equal strings */
+
+ if(ucs4pmt[j] == (UCS) key)
+ j++;
+
+ if (pscr(line, i) == NULL)
+ return;
+
+ if(pscr(line, i)->c != ucs4pmt[j]){
+ if(j >= 1 && ucs4pmt[j-1] == (UCS) key)
+ j--;
+ break;
+ }
+
+ ww = wcellwidth((UCS) pscr(line, i)->c);
+ col += (ww >= 0 ? ww : 1);
+ j++;
+ i++;
+ }
+
+ movecursor(line, column+col);
+ if(klcp) (void)pico_set_colorp(klcp, PSC_NONE);
+ u = &ucs4pmt[j];
+ do{
+ if(*u == (UCS) key){
+ u++;
+ if(kncp)
+ (void)pico_set_colorp(kncp, PSC_NONE);
+ else
+ (void)(*term.t_rev)(1);
+
+ pputc(*u, 1);
+ if(kncp)
+ (void)pico_set_colorp(klcp, PSC_NONE);
+ else
+ (void)(*term.t_rev)(0);
+ }
+ else{
+ pputc(*u, 0);
+ }
+ }
+ while(*++u != '\0');
+
+ if(ucs4pmt)
+ fs_give((void **) &ucs4pmt);
+
+ peeol();
+ if (lastc){
+ (void)pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+ (*term.t_flush)();
+}
+
+
+
+/*
+ * wkeyhelp - paint list of possible commands on the bottom
+ * of the display (yet another pine clone)
+ * NOTE: function key mode is handled here since all the labels
+ * are the same...
+ *
+ * The KEYMENU definitions have names and labels defined as UTF-8 strings,
+ * and wstripe expects UTF-8.
+ */
+void
+wkeyhelp(KEYMENU *keymenu)
+{
+ char *obufp, *p, fkey[4];
+ char linebuf[2*NLINE]; /* "2" is for space for invert tokens */
+ int row, slot, tspace, adjusted_tspace, nspace[6], index, n;
+#ifdef MOUSE
+ char nbuf[NLINE];
+#endif
+
+#ifdef _WINDOWS
+ pico_config_menu_items (keymenu);
+#endif
+
+ if(term.t_mrow == 0)
+ return;
+
+ if(term.t_nrow < 1)
+ return;
+
+ /*
+ * Calculate amount of space for the names column by column...
+ */
+ for(index = 0; index < 6; index++)
+ if(!(gmode&MDFKEY)){
+ nspace[index] = (keymenu[index].name)
+ ? utf8_width(keymenu[index].name) : 0;
+ if(keymenu[index+6].name
+ && (n = utf8_width(keymenu[index+6].name)) > nspace[index])
+ nspace[index] = n;
+
+ nspace[index]++;
+ }
+ else
+ nspace[index] = (index < 4) ? 3 : 4;
+
+ tspace = term.t_ncol/6; /* total space for each item */
+
+ /*
+ * Avoid writing in bottom right corner so we won't scroll screens that
+ * scroll when you do that. The way this is setup, we won't do that
+ * unless the number of columns is evenly divisible by 6.
+ */
+ adjusted_tspace = (6 * tspace == term.t_ncol) ? tspace - 1 : tspace;
+
+ index = 0;
+ for(row = 0; row <= 1; row++){
+ linebuf[0] = '\0';
+ obufp = &linebuf[0];
+ for(slot = 0; slot < 6; slot++){
+ if(keymenu[index].name && keymenu[index].label){
+ size_t l;
+ char this_label[200], tmp_label[200];
+
+ if(keymenu[index].label[0] == '[' && keymenu[index].label[(l=strlen(keymenu[index].label))-1] == ']' && l > 2){
+ strncpy(tmp_label, &keymenu[index].label[1], MIN(sizeof(tmp_label),l-2));
+ tmp_label[MIN(sizeof(tmp_label)-1,l-2)] = '\0';
+ snprintf(this_label, sizeof(this_label), "[%s]", _(tmp_label));
+ }
+ else
+ strncpy(this_label, _(keymenu[index].label), sizeof(this_label));
+
+ this_label[sizeof(this_label)-1] = '\0';
+
+ if(gmode&MDFKEY){
+ p = fkey;
+ snprintf(fkey, sizeof(fkey), "F%d", (2 * slot) + row + 1);
+ }
+ else
+ p = keymenu[index].name;
+#ifdef MOUSE
+ snprintf(nbuf, sizeof(nbuf), "%.*s %s", nspace[slot], p, this_label);
+ register_key(index,
+ (gmode&MDFKEY) ? F1 + (2 * slot) + row:
+ (keymenu[index].name[0] == '^')
+ ? (CTRL | keymenu[index].name[1])
+ : (keymenu[index].name[0] == 'S'
+ && !strcmp(keymenu[index].name, "Spc"))
+ ? ' '
+ : keymenu[index].name[0],
+ nbuf, invert_label,
+ term.t_nrow - 1 + row, (slot * tspace),
+ strlen(nbuf),
+ (Pmaster && Pmaster->colors)
+ ? Pmaster->colors->kncp: NULL,
+ (Pmaster && Pmaster->colors)
+ ? Pmaster->colors->klcp: NULL);
+#endif
+
+ n = nspace[slot];
+ while(p && *p && n--){
+ *obufp++ = '~'; /* insert "invert" token */
+ *obufp++ = *p++;
+ }
+
+ while(n-- > 0)
+ *obufp++ = ' ';
+
+ p = this_label;
+ n = ((slot == 5 && row == 1) ? adjusted_tspace
+ : tspace) - nspace[slot];
+ while(p && *p && n-- > 0)
+ *obufp++ = *p++;
+
+ while(n-- > 0)
+ *obufp++ = ' ';
+ }
+ else{
+ n = (slot == 5 && row == 1) ? adjusted_tspace : tspace;
+ while(n--)
+ *obufp++ = ' ';
+
+#ifdef MOUSE
+ register_key(index, NODATA, "", NULL, 0, 0, 0, NULL, NULL);
+#endif
+ }
+
+ *obufp = '\0';
+ index++;
+ }
+
+ wstripe(term.t_nrow - 1 + row, 0, linebuf, '~');
+ }
+}
+
+
+/*
+ * This returns the screen width between pstart (inclusive) and
+ * pend (exclusive) where the pointers point into an array of CELLs.
+ */
+unsigned
+cellwidth_ptr_to_ptr(CELL *pstart, CELL *pend)
+{
+ CELL *p;
+ unsigned width = 0;
+ int ww;
+
+ if(pstart)
+ for(p = pstart; p < pend; p++){
+ ww = wcellwidth((UCS) p->c);
+ width += (ww >= 0 ? ww : 1);
+ }
+
+ return(width);
+}
+
+
+/*
+ * This returns the virtual screen width in row from index a to b (exclusive).
+ */
+unsigned
+vcellwidth_a_to_b(int row, int a, int b)
+{
+ CELL *pstart, *pend;
+ VIDEO *vp;
+
+ if(row < 0 || row > term.t_nrow)
+ return 0;
+
+ if(a >= b)
+ return 0;
+
+ a = MIN(MAX(0, a), term.t_ncol-1);
+ b = MIN(MAX(0, a), term.t_ncol); /* b is past where we stop */
+
+ vp = vscreen[row];
+ pstart = &vp->v_text[a];
+ pend = &vp->v_text[b];
+
+ return(cellwidth_ptr_to_ptr(pstart, pend));
+}
+
+
+/*
+ * This returns the physical screen width in row from index a to b (exclusive).
+ */
+unsigned
+pcellwidth_a_to_b(int row, int a, int b)
+{
+ CELL *pstart, *pend;
+ VIDEO *vp;
+
+ if(row < 0 || row > term.t_nrow)
+ return 0;
+
+ if(a >= b)
+ return 0;
+
+ a = MIN(MAX(0, a), term.t_ncol-1);
+ b = MIN(MAX(0, a), term.t_ncol); /* b is past where we stop */
+
+ vp = pscreen[row];
+ pstart = &vp->v_text[a];
+ pend = &vp->v_text[b];
+
+ return(cellwidth_ptr_to_ptr(pstart, pend));
+}
+
+
+int
+index_from_col(int row, int col)
+{
+ CELL *p_start, *p_end, *p_limit;
+ int w_consumed = 0, w, done = 0;
+
+ if(row < 0 || row > term.t_nrow)
+ return 0;
+
+ p_end = p_start = pscreen[row]->v_text;
+ p_limit = p_start + term.t_ncol;
+
+ if(p_start)
+ while(!done && p_end < p_limit && p_end->c && w_consumed <= col){
+ w = wcellwidth((UCS) p_end->c);
+ w = (w >= 0 ? w : 1);
+ if(w_consumed + w <= col){
+ w_consumed += w;
+ ++p_end;
+ }
+ else
+ ++done;
+ }
+
+ /* MIN and MAX just to be sure */
+ return(MIN(MAX(0, p_end - p_start), term.t_ncol-1));
+}
+
+#ifdef _WINDOWS
+
+void
+pico_config_menu_items (KEYMENU *keymenu)
+{
+ int i;
+ KEYMENU *k;
+ UCS key;
+
+ mswin_menuitemclear ();
+
+ /* keymenu's seem to be hardcoded at 12 entries. */
+ for (i = 0, k = keymenu; i < 12; ++i, ++k) {
+ if (k->name != NULL && k->label != NULL &&
+ k->menuitem != KS_NONE) {
+
+ if (k->name[0] == '^')
+ key = CTRL | k->name[1];
+ else if (strcmp(k->name, "Ret") == 0)
+ key = '\r';
+ else
+ key = k->name[0];
+
+ mswin_menuitemadd (key, k->label, k->menuitem, 0);
+ }
+ }
+}
+
+/*
+ * Update the scroll range and position. (exported)
+ *
+ * This is where curbp->b_linecnt is really managed. With out this function
+ * to count the number of lines when needed curbp->b_linecnt will never
+ * really be correct. BUT, this function is only compiled into the
+ * windows version, so b_linecnt will only ever be right in the windows
+ * version. OK for now because that is the only version that
+ * looks at b_linecnt.
+ */
+int
+update_scroll (void)
+{
+ long scr_pos;
+ long scr_range;
+ LINE *lp;
+ static LINE *last_top_line = NULL;
+ static long last_scroll_pos = -1;
+
+
+ if (ComposerEditing) {
+ /* Editing header - don't allow scroll bars. */
+ mswin_setscrollrange (0, 0);
+ return(0);
+ }
+
+
+ /*
+ * Count the number of lines in the current bufer. Done when:
+ *
+ * when told to recount: curbp->b_linecnt == -1
+ * when the top line changed: curwp->w_linep != last_top_line
+ * when we don't know the scroll pos: last_scroll_pos == -1
+ *
+ * The first line in the list is a "place holder" line and is not
+ * counted. The list is circular, when we return the to place
+ * holder we have reached the end.
+ */
+ if(curbp->b_linecnt == -1 || curwp->w_linep != last_top_line
+ || last_scroll_pos == -1) {
+ scr_range = 0;
+ scr_pos = 0;
+ for (lp = lforw (curbp->b_linep); lp != curbp->b_linep;
+ lp = lforw (lp)) {
+ if (lp == curwp->w_linep)
+ scr_pos = scr_range;
+
+ ++scr_range;
+ }
+
+ curbp->b_linecnt = scr_range;
+ last_scroll_pos = scr_pos;
+ last_top_line = curwp->w_linep;
+ }
+
+ /*
+ * Set new scroll range and position.
+ */
+ mswin_setscrollrange (curwp->w_ntrows - 2, curbp->b_linecnt - 1);
+ mswin_setscrollpos (last_scroll_pos);
+ return (0);
+}
+#endif /* _WINDOWS */
diff --git a/pico/ebind.h b/pico/ebind.h
new file mode 100644
index 00000000..4f1687b4
--- /dev/null
+++ b/pico/ebind.h
@@ -0,0 +1,167 @@
+/*
+ * $Id: ebind.h 807 2007-11-09 01:21:33Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ *
+ * Program: Default key bindings
+ *
+ * NOTES:
+ *
+ * This files describes the key bindings for pico and the pine
+ * composer. The binds are static, (i.e., no way for the user
+ * to change them) so as to keep pico/composer as simple to use
+ * as possible. This, of course, means the number of functions is
+ * greatly reduced, but, then again, this is seen as very desirable.
+ *
+ * There are very limited number of flat ctrl-key bindings left, and
+ * most of them are slated for yet-to-be implemented functions, like
+ * invoking an alternate editor in the composer and necessary funcs
+ * for imlementing attachment handling. We really want to avoid
+ * going to multiple keystroke functions. -mss
+ *
+ */
+
+/* EBIND: Initial default key to function bindings for
+ MicroEMACS 3.2
+
+ written by Dave G. Conroy
+ modified by Steve Wilhite, George Jones
+ greatly modified by Daniel Lawrence
+*/
+
+#ifndef EBIND_H
+#define EBIND_H
+
+
+/*
+ * Command table.
+ * This table is *roughly* in ASCII order, left to right across the
+ * characters of the command. This expains the funny location of the
+ * control-X commands.
+ */
+KEYTAB keytab[NBINDS] = {
+ {KEY_UP, backline},
+ {KEY_DOWN, forwline},
+ {KEY_RIGHT, forwchar},
+ {KEY_LEFT, backchar},
+ {KEY_PGUP, backpage},
+ {KEY_PGDN, forwpage},
+ {KEY_HOME, gotobol},
+ {KEY_END, gotoeol},
+ {KEY_DEL, forwdel},
+#ifdef MOUSE
+ {KEY_MOUSE, mousepress},
+#ifndef _WINDOWS
+ {CTRL|'\\', toggle_xterm_mouse},
+#endif
+#endif
+ {CTRL|'A', gotobol},
+ {CTRL|'B', backchar},
+ {CTRL|'C', abort_composer},
+ {CTRL|'D', forwdel},
+ {CTRL|'E', gotoeol},
+ {CTRL|'F', forwchar},
+ {CTRL|'G', whelp},
+ {CTRL|'H', backdel},
+ {CTRL|'I', tab},
+ {CTRL|'J', fillpara},
+ {CTRL|'K', killregion},
+ {CTRL|'L', pico_refresh},
+ {CTRL|'M', newline},
+ {CTRL|'N', forwline},
+ {CTRL|'O', suspend_composer},
+ {CTRL|'P', backline},
+ {CTRL|'R', insfile},
+#ifdef SPELLER
+ {CTRL|'T', spell},
+#endif /* SPELLER */
+ {CTRL|'U', yank},
+ {CTRL|'V', forwpage},
+ {CTRL|'W', forwsearch},
+ {CTRL|'X', wquit},
+ {CTRL|'Y', backpage},
+#if defined(SIGTSTP) || defined(_WINDOWS)
+ {CTRL|'Z', bktoshell},
+#endif
+ {CTRL|'@', forwword},
+ {CTRL|'^', setmark},
+ {CTRL|'_', alt_editor},
+ {CTRL|KEY_LEFT, backword},
+ {CTRL|KEY_RIGHT,forwword},
+ {CTRL|KEY_HOME, gotobob},
+ {CTRL|KEY_END, gotoeob},
+ {0x7F, backdel},
+ {0, NULL}
+};
+
+
+/*
+ * Command table.
+ * This table is *roughly* in ASCII order, left to right across the
+ * characters of the command. This expains the funny location of the
+ * control-X commands.
+ */
+KEYTAB pkeytab[NBINDS] = {
+ {KEY_UP, backline},
+ {KEY_DOWN, forwline},
+ {KEY_RIGHT, forwchar},
+ {KEY_LEFT, backchar},
+ {KEY_PGUP, backpage},
+ {KEY_PGDN, forwpage},
+ {KEY_HOME, gotobol},
+ {KEY_END, gotoeol},
+ {KEY_DEL, forwdel},
+#ifdef MOUSE
+ {KEY_MOUSE, mousepress},
+#ifndef _WINDOWS
+ {CTRL|'\\', toggle_xterm_mouse},
+#endif
+#endif
+ {CTRL|'A', gotobol},
+ {CTRL|'B', backchar},
+ {CTRL|'C', showcpos},
+ {CTRL|'D', forwdel},
+ {CTRL|'E', gotoeol},
+ {CTRL|'F', forwchar},
+ {CTRL|'G', whelp},
+ {CTRL|'H', backdel},
+ {CTRL|'I', tab},
+ {CTRL|'J', fillpara},
+ {CTRL|'K', killregion},
+ {CTRL|'L', pico_refresh},
+ {CTRL|'M', newline},
+ {CTRL|'N', forwline},
+ {CTRL|'O', filewrite},
+ {CTRL|'P', backline},
+ {CTRL|'R', insfile},
+#ifdef SPELLER
+ {CTRL|'T', spell},
+#endif /* SPELLER */
+ {CTRL|'U', yank},
+ {CTRL|'V', forwpage},
+ {CTRL|'W', forwsearch},
+ {CTRL|'X', wquit},
+ {CTRL|'Y', backpage},
+#if defined(SIGTSTP) || defined(_WINDOWS)
+ {CTRL|'Z', bktoshell},
+#endif
+ {CTRL|'@', forwword},
+ {CTRL|'^', setmark},
+ {CTRL|KEY_LEFT, backword},
+ {CTRL|KEY_RIGHT,forwword},
+ {CTRL|KEY_HOME, gotobob},
+ {CTRL|KEY_END, gotoeob},
+ {0x7F, backdel},
+ {0, NULL}
+};
+
+#endif /* EBIND_H */
diff --git a/pico/edef.h b/pico/edef.h
new file mode 100644
index 00000000..209a4600
--- /dev/null
+++ b/pico/edef.h
@@ -0,0 +1,166 @@
+/*
+ * $Id: edef.h 900 2008-01-05 01:13:26Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ *
+ * Program: Global definitions and initializations
+ */
+
+/* EDEF: Global variable definitions for
+ MicroEMACS 3.2
+
+ written by Dave G. Conroy
+ modified by Steve Wilhite, George Jones
+ greatly modified by Daniel Lawrence
+*/
+
+#ifndef EDEF_H
+#define EDEF_H
+
+#ifdef maindef
+
+/* for MAIN.C */
+
+/* initialized global definitions */
+
+int fillcol = 72; /* Current fill column */
+int userfillcol = -1; /* Fillcol set from cmd line */
+UCS pat[NPAT]; /* Search pattern */
+UCS rpat[NPAT]; /* Replace pattern */
+int sgarbk = TRUE; /* TRUE if keyhelp garbaged */
+int sup_keyhelp = FALSE; /* TRUE if keyhelp is suppressed*/
+int mline_open = FALSE; /* TRUE if message line is open */
+int ComposerTopLine = 2; /* TRUE if message line is open */
+int ComposerEditing = FALSE; /* TRUE if editing the headers */
+char modecode[] = "WCSEVO"; /* letters to represent modes */
+long gmode = MDWRAP|MDREPLACE; /* global editor mode */
+int sgarbf = TRUE; /* TRUE if screen is garbage */
+int mpresf = FALSE; /* TRUE if message in last line */
+int clexec = FALSE; /* command line execution flag */
+char *alt_speller = NULL; /* alt spell checking command */
+int preserve_start_stop = FALSE; /* TRUE if pass ^S/^Q to term */
+UCS *glo_quote_str = NULL; /* points to quote string if set*/
+char *glo_quote_str_orig = NULL;
+int use_system_translation = FALSE;
+char *display_character_set = NULL;
+char *keyboard_character_set = NULL;
+UCS *glo_wordseps = NULL; /* points to word separators if set */
+char *glo_wordseps_orig = NULL;
+
+/* uninitialized global definitions */
+int currow; /* Cursor row */
+int curcol; /* Cursor column */
+int thisflag; /* Flags, this command */
+int lastflag; /* Flags, last command */
+int curgoal; /* Goal for C-P, C-N */
+char opertree[NLINE+1]; /* operate within this tree */
+char browse_dir[NLINE+1]; /* directory of last browse (cwd) */
+WINDOW *curwp; /* Current window */
+BUFFER *curbp; /* Current buffer */
+WINDOW *wheadp; /* Head of list of windows */
+BUFFER *bheadp; /* Head of list of buffers */
+BUFFER *blistp; /* Buffer for C-X C-B */
+
+BUFFER *bfind(char *, int, int); /* Lookup a buffer by name */
+LINE *lalloc(int used); /* Allocate a line */
+int km_popped; /* menu popped up */
+#if defined(HAS_TERMCAP) || defined(HAS_TERMINFO) || defined(VMS)
+KBESC_T *kbesc; /* keyboard esc sequence trie */
+#endif /* HAS_TERMCAP/HAS_TERMINFO/VMS */
+void *input_cs; /* passed to mbtow() via kbseq() */
+
+#else /* maindef */
+
+/* for all the other .C files */
+
+/* initialized global external declarations */
+
+extern int fillcol; /* Fill column */
+extern int userfillcol; /* Fillcol set from cmd line */
+extern UCS pat[]; /* Search pattern */
+extern UCS rpat[]; /* Replace pattern */
+extern int sgarbk;
+extern int sup_keyhelp;
+extern int mline_open; /* Message line is open */
+extern int ComposerTopLine; /* TRUE if message line is open */
+extern int ComposerEditing; /* TRUE if message line is open */
+extern char modecode[]; /* letters to represent modes */
+extern KEYTAB keytab[]; /* key bind to functions table */
+extern KEYTAB pkeytab[]; /* pico's function table */
+extern long gmode; /* global editor mode */
+extern int sgarbf; /* State of screen unknown */
+extern int mpresf; /* Stuff in message line */
+extern int clexec; /* command line execution flag */
+extern char *alt_speller; /* alt spell checking command */
+extern int preserve_start_stop; /* TRUE if pass ^S/^Q to term */
+extern UCS *glo_quote_str; /* points to quote string if set*/
+extern char *glo_quote_str_orig;
+extern int use_system_translation;
+extern char *display_character_set;
+extern char *keyboard_character_set;
+extern UCS *glo_wordseps;
+extern char *glo_wordseps_orig;
+/* initialized global external declarations */
+extern int currow; /* Cursor row */
+extern int curcol; /* Cursor column */
+extern int thisflag; /* Flags, this command */
+extern int lastflag; /* Flags, last command */
+extern int curgoal; /* Goal for C-P, C-N */
+extern char opertree[NLINE+1]; /* operate within this tree */
+extern char browse_dir[NLINE+1]; /* directory of last browse (cwd) */
+extern WINDOW *curwp; /* Current window */
+extern BUFFER *curbp; /* Current buffer */
+extern WINDOW *wheadp; /* Head of list of windows */
+extern BUFFER *bheadp; /* Head of list of buffers */
+extern BUFFER *blistp; /* Buffer for C-X C-B */
+
+extern BUFFER *bfind(char *, int, int); /* Lookup a buffer by name */
+extern LINE *lalloc(int used); /* Allocate a line */
+extern int km_popped; /* menu popped up */
+/*
+ * This is a weird one. It has to be defined differently for pico and for
+ * pine. It seems to need to be defined at startup as opposed to set later.
+ * It doesn't work to set it later in pico. When pico is used with a
+ * screen reader it seems to jump to the cursor every time through the
+ * mswin_charavail() loop in GetKey, and the timeout is this long. So we
+ * just need to set it higher than we do in pine. If we understood this
+ * we would probably see that we don't need any timer at all in pico, but
+ * we don't remember why it is here so we'd better leave it.
+ *
+ * This is defined in .../pico/main.c and in .../alpine/alpine.c.
+ */
+extern int my_timer_period; /* here so can be set */
+#ifdef MOUSE
+extern MENUITEM menuitems[]; /* key labels and functions */
+extern MENUITEM *mfunc; /* single generic function */
+extern mousehandler_t mtrack; /* func used to track the mouse */
+#endif /* MOUSE */
+
+#if defined(HAS_TERMCAP) || defined(HAS_TERMINFO) || defined(VMS)
+extern KBESC_T *kbesc; /* keyboard esc sequence trie */
+#endif /* HAS_TERMCAP/HAS_TERMINFO/VMS */
+extern void *input_cs; /* passed to mbtow() via kbseq() */
+
+#endif /* maindef */
+
+/* terminal table defined only in TERM.C */
+
+#ifndef termdef
+#if defined(VMS) && !defined(__ALPHA)
+globalref
+#else
+extern
+#endif /* VMS */
+ TERM term; /* Terminal information. */
+#endif /* termdef */
+
+#endif /* EDEF_H */
diff --git a/pico/efunc.h b/pico/efunc.h
new file mode 100644
index 00000000..b551dfc7
--- /dev/null
+++ b/pico/efunc.h
@@ -0,0 +1,258 @@
+/*
+ * $Id: efunc.h 807 2007-11-09 01:21:33Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ *
+ * Program: Pine's composer and pico's function declarations
+ */
+
+/* EFUNC.H: MicroEMACS function declarations and names
+
+ This file list all the C code functions used by MicroEMACS
+ and the names to use to bind keys to them. To add functions,
+ declare it here in both the extern function list and the name
+ binding table.
+
+ Update History:
+
+ Daniel Lawrence
+*/
+
+#ifndef EFUNC_H
+#define EFUNC_H
+
+
+/* External function declarations */
+/* attach.c */
+extern int AskAttach(char *, size_t, LMLIST **);
+extern int SyncAttach(void);
+extern int intag(UCS *, int);
+extern char *prettysz(off_t);
+extern int AttachError(void);
+extern char *QuoteAttach(char *, size_t);
+extern int getccol(int);
+
+/* basic.c */
+extern int gotobol(int, int);
+extern int backchar(int, int);
+extern int gotoeol(int, int);
+extern int forwchar(int, int);
+extern int gotoline(int, int);
+extern int gotobob(int, int);
+extern int gotoeob(int, int);
+extern int forwline(int, int);
+extern int backline(int, int);
+extern int gotobop(int, int);
+extern int gotoeop(int, int);
+extern int forwpage(int, int);
+extern int backpage(int, int);
+extern int scrollupline(int, int);
+extern int scrolldownline(int, int);
+extern int scrollto(int, int);
+extern int setmark(int, int);
+extern int swapmark(int, int);
+extern int setimark(int, int);
+extern int swapimark(int, int);
+extern int mousepress(int, int);
+extern int toggle_xterm_mouse(int, int);
+extern void swap_mark_and_dot_if_mark_comes_first(void);
+extern int backchar_no_header_editor(int, int);
+extern int getgoal(struct LINE *);
+
+/* bind.c */
+extern UCS normalize_cmd(UCS c, UCS list[][2], int sc);
+extern int whelp(int, int);
+extern int wscrollw(int, int, char **, int);
+extern int normal(int, int (*)[2], int);
+extern void rebindfunc(int (*)(int, int),int (*)(int, int));
+extern int bindtokey(UCS c, int (*f)(int, int));
+
+/* browse.c */
+extern int FileBrowse(char *, size_t, char *, size_t, char *, size_t, int, LMLIST **);
+extern int ResizeBrowser(void);
+extern void set_browser_title(char *);
+extern void zotlmlist(LMLIST *);
+extern int time_to_check(void);
+extern int LikelyASCII(char *);
+
+/* buffer.c */
+extern int anycb(void);
+extern struct BUFFER *bfind(char *, int, int);
+extern int bclear(struct BUFFER *);
+extern int packbuf(char **, int *, int);
+extern void readbuf(char **);
+
+/* composer.c */
+extern int InitMailHeader(struct pico_struct *);
+extern int ResizeHeader(void);
+extern int HeaderEditor(int, int);
+extern void PaintHeader(int, int);
+extern void ArrangeHeader(void);
+extern int ToggleHeader(int);
+extern int HeaderLen(void);
+extern int UpdateHeader(int);
+extern int entry_line(int, int);
+extern int call_builder(struct headerentry *, int *, char **);
+extern void call_expander(void);
+extern void ShowPrompt(void);
+extern int packheader(void);
+extern void zotheader(void);
+extern void display_for_send(void);
+extern VARS_TO_SAVE *save_pico_state(void);
+extern void restore_pico_state(VARS_TO_SAVE *);
+extern void free_pico_state(VARS_TO_SAVE *);
+extern void HeaderPaintCursor(void);
+extern void PaintBody(int);
+extern int AppendAttachment(char *, char *, char *);
+
+/* display.c */
+extern int vtinit(void);
+extern int vtterminalinfo(int);
+extern void vttidy(void);
+extern void update(void);
+extern void modeline(struct WINDOW *);
+extern void movecursor(int, int);
+extern void clearcursor(void);
+extern void mlerase(void);
+extern int mlyesno_utf8(char *, int);
+extern int mlyesno(UCS *, int);
+extern int mlreply_utf8(char *, char *, int, int, EXTRAKEYS *);
+extern int mlreply(UCS *, UCS *, int, int, EXTRAKEYS *);
+extern int mlreplyd_utf8(char *, char *, int, int, EXTRAKEYS *);
+extern int mlreplyd(UCS *, UCS *, int, int, EXTRAKEYS *);
+extern int mlwrite_utf8(char *, void *);
+extern int mlwrite(UCS *, void *);
+extern void emlwrite(char *, EML *);
+extern void emlwrite_ucs4(UCS *, EML *);
+extern void unknown_command(UCS);
+extern void scrolldown(struct WINDOW *, int, int);
+extern void scrollup(struct WINDOW *, int, int);
+extern int doton(int *, unsigned int *);
+extern int resize_pico(int, int);
+extern void zotdisplay(void);
+extern void pputc(UCS c, int a);
+extern void pputs(UCS *s, int a);
+extern void pputs_utf8(char *s, int a);
+extern void peeol(void);
+extern CELL *pscr(int, int);
+extern void pclear(int, int);
+extern int pinsert(CELL);
+extern int pdel(void);
+extern void wstripe(int, int, char *, int);
+extern void wkeyhelp(KEYMENU *);
+extern void get_cursor(int *, int *);
+extern unsigned vcellwidth_a_to_b(int row, int a, int b);
+extern int index_from_col(int row, int col);
+
+/* file.c */
+extern int fileread(int, int);
+extern int insfile(int, int);
+extern int readin(char *, int, int);
+extern int filewrite(int, int);
+extern int filesave(int, int);
+extern int writeout(char *, int);
+extern char *writetmp(int, char *);
+extern int filename(int, int);
+extern int in_oper_tree(char *);
+extern int ifile(char *);
+
+/* fileio.c */
+extern int ffropen(char *);
+extern int ffputline(CELL *, int);
+extern int ffgetline(UCS *, size_t, size_t *, int);
+
+/* line.c */
+extern struct LINE *lalloc(int used);
+extern void lfree(struct LINE *);
+extern void lchange(int);
+extern int linsert(int n, UCS c);
+extern int geninsert(LINE **dotp, int *doto, LINE *linep, UCS c, int attb, int n, long *lines);
+extern int lnewline(void);
+extern int ldelete(long, int (*)(UCS));
+extern int lisblank(struct LINE *);
+extern void kdelete(void);
+extern int kinsert(UCS);
+extern long kremove(int);
+extern int ksize(void);
+extern void fdelete(void);
+extern int finsert(UCS);
+extern long fremove(int);
+extern void set_last_region_added(REGION *);
+extern REGION *get_last_region_added(void);
+
+/* os.c */
+extern int o_insert(UCS);
+extern int o_delete(void);
+
+/* pico.c */
+extern int pico(struct pico_struct *pm);
+extern void edinit(char *);
+extern int execute(UCS c, int f, int n);
+extern int quickexit(int, int);
+extern int abort_composer(int, int);
+extern int suspend_composer(int, int);
+extern int wquit(int, int);
+extern int ctrlg(int, int);
+extern int rdonly(void);
+extern int pico_help(char **, char *, int);
+extern void zotedit(void);
+#ifdef _WINDOWS
+int composer_file_drop(int, int, char *);
+int pico_cursor(int, long);
+#endif
+
+/* random.c */
+extern int showcpos(int, int);
+extern int tab(int, int);
+extern int newline(int, int);
+extern int forwdel(int, int);
+extern int backdel(int, int);
+extern int killtext(int, int);
+extern int yank(int, int);
+
+/* region.c */
+extern int killregion(int, int);
+extern int deleteregion(int, int);
+extern int markregion(int);
+extern int getregion(REGION *, LINE *, int);
+extern void unmarkbuffer(void);
+
+/* search.c */
+extern int forwsearch(int, int);
+extern int readpattern(char *, int);
+extern int forscan(int *, UCS *, LINE *, int, int);
+extern void chword(UCS *, UCS *);
+
+/* spell.c */
+#ifdef SPELLER
+extern int spell(int, int);
+#endif
+
+/* window.c */
+extern int pico_refresh(int, int);
+extern void redraw_pico_for_callback(void);
+
+/* word.c */
+extern int wrapword(void);
+extern int backword(int, int);
+extern int forwword(int, int);
+extern int fillpara(int, int);
+extern int fillbuf(int, int);
+extern int inword(void);
+extern int quote_match(UCS *, LINE *, UCS *, size_t);
+extern int ucs4_isalnum(UCS);
+extern int ucs4_isalpha(UCS);
+extern int ucs4_isspace(UCS);
+extern int ucs4_ispunct(UCS);
+
+#endif /* EFUNC_H */
diff --git a/pico/estruct.h b/pico/estruct.h
new file mode 100644
index 00000000..80afd537
--- /dev/null
+++ b/pico/estruct.h
@@ -0,0 +1,366 @@
+/*
+ * $Id: estruct.h 767 2007-10-24 00:03:59Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ *
+ * Program: Struct and preprocessor definitions
+ *
+ */
+
+/* ESTRUCT: Structure and preprocesser defined for
+ MicroEMACS 3.6
+
+ written by Dave G. Conroy
+ modified by Steve Wilhite, George Jones
+ greatly modified by Daniel Lawrence
+*/
+
+#ifndef ESTRUCT_H
+#define ESTRUCT_H
+
+/* Configuration options */
+
+#define CVMVAS 1 /* arguments to page forward/back in pages */
+#define NFWORD 1 /* forward word jumps to begining of word */
+#define TYPEAH 0 /* type ahead causes update to be skipped */
+#define REVSTA 1 /* Status line appears in reverse video */
+
+
+/* internal constants */
+
+#define NBINDS 50 /* max # of bound keys */
+#ifdef MAXPATH
+#define NFILEN MAXPATH /* # of bytes, file name */
+#else
+#define NFILEN 200 /* # of bytes, file name */
+#endif
+#define NBUFN 16 /* # of bytes, buffer name */
+#define NLINE 500 /* # of bytes, line */
+#define NSTRING 256 /* # of bytes, string buffers */
+#define NPAT 80 /* # of bytes, pattern */
+#define FARAWAY 1000 /* Huge number */
+#define NLOCKS 100 /* max # of file locks active */
+
+#define AGRAVE 0x60 /* M- prefix, Grave (LK201) */
+#define METACH 0x1B /* M- prefix, Control-[, ESC */
+#define CTMECH 0x1C /* C-M- prefix, Control-\ */
+#define EXITCH 0x1D /* Exit level, Control-] */
+#define CTRLCH 0x1E /* C- prefix, Control-^ */
+#define HELPCH 0x1F /* Help key, Control-_ */
+
+#define QNORML 0x0000 /* Flag meaning no flag ;) */
+#define QFFILE 0x0001 /* Flag buffer for file neme */
+#define QDEFLT 0x0002 /* Flag to use default answer */
+#define QBOBUF 0x0004 /* Start with cursor at BOL */
+#define QNODQT 0x0008 /* Don't accept double quotes */
+
+#define FIOSUC 0 /* File I/O, success. */
+#define FIOFNF 1 /* File I/O, file not found. */
+#define FIOEOF 2 /* File I/O, end of file. */
+#define FIOERR 3 /* File I/O, error. */
+#define FIOLNG 4 /*line longer than allowed len */
+#define FIODIR 5 /* File is a directory */
+#define FIONWT 6 /* File lacks write permission */
+#define FIONRD 7 /* File lacks read permission */
+#define FIONEX 8 /* File lacks exec permission */
+#define FIOSYM 9 /* File is a symbolic link */
+#define FIOPER 10 /* Generic permission denied */
+
+
+#define CFCPCN 0x0001 /* Last command was C-P, C-N */
+#define CFKILL 0x0002 /* Last command was a kill */
+#define CFFILL 0x0004 /* Last command was a fill */
+#define CFSRCH 0x0008 /* Last command was WhereIs */
+#define CFFLBF 0x0010 /* Last cmd was full buf fill */
+#define CFFLPA 0x0020 /* Last cmd was paragraph fill */
+
+/*
+ * macros to help filter character input
+ */
+#define CELLMASK 0xffffff
+#define LOBIT_CHAR(C) ((C) > 0x1f && (C) < 0x7f)
+#define HIBIT_CHAR(C) ((C) > 0x7f && (C) <= CELLMASK)
+#define HIBIT_OK(C) (!(gmode & MDHBTIGN))
+#define VALID_KEY(C) (LOBIT_CHAR(C) || (HIBIT_OK(C) && HIBIT_CHAR(C)))
+#define ctrl(c) ((c) & 0x1f) /* control character mapping */
+
+
+/*
+ * Placeholders for keymenu tags used in some ports (well, only in the
+ * windows port for now) to turn on commands in the Menu Bar.
+ */
+#ifndef KS_OSDATAVAR
+#define KS_OSDATAVAR
+#define KS_OSDATAGET(X)
+#define KS_OSDATASET(X, Y)
+
+#define KS_NONE
+#define KS_VIEW
+#define KS_EXPUNGE
+#define KS_ZOOM
+#define KS_SORT
+#define KS_HDRMODE
+#define KS_MAINMENU
+#define KS_FLDRLIST
+#define KS_FLDRINDEX
+#define KS_COMPOSER
+#define KS_PREVPAGE
+#define KS_PREVMSG
+#define KS_NEXTMSG
+#define KS_ADDRBOOK
+#define KS_WHEREIS
+#define KS_PRINT
+#define KS_REPLY
+#define KS_FORWARD
+#define KS_BOUNCE
+#define KS_DELETE
+#define KS_UNDELETE
+#define KS_FLAG
+#define KS_SAVE
+#define KS_EXPORT
+#define KS_TAKEADDR
+#define KS_SELECT
+#define KS_APPLY
+#define KS_POSTPONE
+#define KS_SEND
+#define KS_CANCEL
+#define KS_ATTACH
+#define KS_TOADDRBOOK
+#define KS_READFILE
+#define KS_JUSTIFY
+#define KS_ALTEDITOR
+#define KS_GENERALHELP
+#define KS_SCREENHELP
+#define KS_EXIT
+#define KS_NEXTPAGE
+#define KS_SAVEFILE
+#define KS_CURPOSITION
+#define KS_GOTOFLDR
+#define KS_JUMPTOMSG
+#define KS_RICHHDR
+#define KS_EXITMODE
+#define KS_REVIEW
+#define KS_KEYMENU
+#define KS_SELECTCUR
+#define KS_UNDO
+#define KS_SPELLCHK
+#endif /* !KS_OSDATAVAR */
+
+
+/*
+ * There is a window structure allocated for every active display window. The
+ * windows are kept in a big list, in top to bottom screen order, with the
+ * listhead at "wheadp". Each window contains its own values of dot and mark.
+ * The flag field contains some bits that are set by commands to guide
+ * redisplay; although this is a bit of a compromise in terms of decoupling,
+ * the full blown redisplay is just too expensive to run for every input
+ * character.
+ */
+typedef struct WINDOW {
+ struct WINDOW *w_wndp; /* Next window */
+ struct BUFFER *w_bufp; /* Buffer displayed in window */
+ struct LINE *w_linep; /* Top line in the window */
+ struct LINE *w_dotp; /* Line containing "." */
+ int w_doto; /* Byte offset for "." */
+ struct LINE *w_markp; /* Line containing "mark" */
+ int w_marko; /* Byte offset for "mark" */
+ struct LINE *w_imarkp; /* INTERNAL Line with "mark" */
+ int w_imarko; /* INTERNAL "mark" byte offset */
+ signed char w_toprow; /* Origin 0 top row of window */
+ signed char w_ntrows; /* # of rows of text in window */
+ char w_force; /* If NZ, forcing row. */
+ char w_flag; /* Flags. */
+} WINDOW;
+
+#define WFFORCE 0x01 /* Window needs forced reframe */
+#define WFMOVE 0x02 /* Movement from line to line */
+#define WFEDIT 0x04 /* Editing within a line */
+#define WFHARD 0x08 /* Better to a full display */
+#define WFMODE 0x10 /* Update mode line. */
+
+/*
+ * Text is kept in buffers. A buffer header, described below, exists for every
+ * buffer in the system. The buffers are kept in a big list, so that commands
+ * that search for a buffer by name can find the buffer header. There is a
+ * safe store for the dot and mark in the header, but this is only valid if
+ * the buffer is not being displayed (that is, if "b_nwnd" is 0). The text for
+ * the buffer is kept in a circularly linked list of lines, with a pointer to
+ * the header line in "b_linep".
+ * Buffers may be "Inactive" which means the files accosiated with them
+ * have not been read in yet. These get read in at "use buffer" time.
+ */
+typedef struct BUFFER {
+ struct BUFFER *b_bufp; /* Link to next BUFFER */
+ struct LINE *b_dotp; /* Link to "." LINE structure */
+ int b_doto; /* Offset of "." in above LINE */
+ struct LINE *b_markp; /* The same as the above two, */
+ int b_marko; /* but for the "mark" */
+ struct LINE *b_linep; /* Link to the header LINE */
+ long b_linecnt; /* Lines in buffer (mswin only) */
+ long b_mode; /* editor mode of this buffer */
+ char b_active; /* window activated flag */
+ char b_nwnd; /* Count of windows on buffer */
+ char b_flag; /* Flags */
+ char b_fname[NFILEN]; /* File name */
+ char b_bname[NBUFN]; /* Buffer name */
+} BUFFER;
+
+#define BFTEMP 0x01 /* Internal temporary buffer */
+#define BFCHG 0x02 /* Changed since last write */
+#define BFWRAPOPEN 0x04 /* Wordwrap should open new line */
+
+
+/*
+ * The starting position of a region, and the size of the region in
+ * characters, is kept in a region structure. Used by the region commands.
+ */
+typedef struct {
+ struct LINE *r_linep; /* Origin LINE address. */
+ int r_offset; /* Origin LINE offset. */
+ long r_size; /* Length in characters. */
+} REGION;
+
+
+/*
+ * character and attribute pair. The basic building block
+ * of the editor. The bitfields may have to be changed to a char
+ * and short if there are problems...
+ */
+typedef struct CELL {
+ unsigned int c : 24; /* Character value in cell */
+ unsigned int a : 8; /* Its attributes */
+} CELL;
+
+/* flags for color_options */
+#define COLOR_ANSI8_OPT 0x01
+#define COLOR_ANSI16_OPT 0x02
+#define COLOR_ANSI256_OPT 0x04
+#define COLOR_TRANS_OPT 0x08
+
+/*
+ * All text is kept in circularly linked lists of "LINE" structures. These
+ * begin at the header line (which is the blank line beyond the end of the
+ * buffer). This line is pointed to by the "BUFFER". Each line contains the
+ * number of bytes in the line (the "used" size), the size of the text array,
+ * and the text. The end of line is not stored as a byte; it's implied. Future
+ * additions will include update hints, and a list of marks into the line.
+ */
+typedef struct LINE {
+ struct LINE *l_fp; /* Link to the next line */
+ struct LINE *l_bp; /* Link to the previous line */
+ int l_size; /* Allocated size */
+ int l_used; /* Used size */
+ CELL l_text[1]; /* A bunch of characters. */
+} LINE;
+
+#define lforw(lp) ((lp)->l_fp)
+#define lback(lp) ((lp)->l_bp)
+#define lgetc(lp, n) ((lp)->l_text[(n)])
+#define lputc(lp, n, c) ((lp)->l_text[(n)]=(c))
+#define llength(lp) ((lp)->l_used)
+
+/*
+ * The editor communicates with the display using a high level interface. A
+ * "TERM" structure holds useful variables, and indirect pointers to routines
+ * that do useful operations. The low level get and put routines are here too.
+ * This lets a terminal, in addition to having non standard commands, have
+ * funny get and put character code too. The calls might get changed to
+ * "termp->t_field" style in the future, to make it possible to run more than
+ * one terminal type.
+ */
+typedef struct {
+ short t_nrow; /* Number of rows - 1. */
+ short t_ncol; /* Number of columns. */
+ short t_margin; /* min margin for extended lines*/
+ short t_mrow; /* Number of rows in menu */
+ int (*t_open)(void); /* Open terminal at the start. */
+ int (*t_terminalinfo)(int); /* Set up terminal info */
+ int (*t_close)(void); /* Close terminal at end. */
+ int (*t_getchar)(int return_on_intr,
+ int (*recorder)(int),
+ void (*bail_handler)(void));
+ /* Get character from keyboard. */
+ int (*t_putchar)(UCS); /* Put character to display. */
+ int (*t_flush)(void); /* Flush output buffers. */
+ int (*t_move)(int, int); /* Move the cursor, origin 0. */
+ int (*t_eeol)(void); /* Erase to end of line. */
+ int (*t_eeop)(void); /* Erase to end of page. */
+ int (*t_beep)(void); /* Beep. */
+ int (*t_rev)(int); /* set reverse video state */
+} TERM;
+
+/* structure for the table of initial key bindings */
+
+typedef struct {
+ UCS k_code; /* Key code */
+ int (*k_fp)(); /* Routine to handle it */
+} KEYTAB;
+
+/* sturcture used for key menu painting */
+
+typedef struct {
+ char *name; /* key to display (UTF-8) */
+ char *label; /* function name key invokes (UTF-8) */
+ KS_OSDATAVAR /* port-specific data */
+} KEYMENU;
+
+typedef struct {
+ char *name; /* key to display */
+ char *label; /* function name key invokes */
+ UCS key; /* what to watch for and return */
+ KS_OSDATAVAR /* port-specific data */
+} EXTRAKEYS;
+
+typedef struct lmlist {
+ char *dir; /* these 3 are all UTF-8 strings */
+ char *fname;
+ char size[32];
+ struct lmlist *next;
+} LMLIST;
+
+
+typedef struct VIDEO {
+ short v_flag; /* Flags */
+ CELL v_text[1]; /* Screen data. */
+} VIDEO;
+
+
+typedef union eml {
+ char *s;
+ int d;
+ long l;
+ UCS c;
+} EML;
+
+typedef struct _file_io_data {
+ FILE *fp; /* stdio stream into file */
+ long flags; /* state flags */
+ char *name; /* pointer to file name */
+} FIOINFO;
+
+#define FIOINFO_READ 0x01
+#define FIOINFO_WRITE 0x02
+
+#define ISCONTROL(C) ((C) < 0x20 || (C) == 0x7F \
+ || ((gmode & P_HICTRL) && ((C) > 0x7F && (C) < 0xA0)))
+
+#ifdef MOUSE
+
+/* Test if mouse position (R, C) is in menu item (X) */
+#define M_ACTIVE(R, C, X) (((unsigned)(R)) >= (X)->tl.r \
+ && ((unsigned)(R)) <= (X)->br.r \
+ && ((unsigned)(C)) >= (X)->tl.c \
+ && ((unsigned)(C)) < (X)->br.c)
+#endif /* MOUSE */
+
+#endif /* ESTRUCT_H */
diff --git a/pico/file.c b/pico/file.c
new file mode 100644
index 00000000..2493d02b
--- /dev/null
+++ b/pico/file.c
@@ -0,0 +1,1090 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: file.c 1082 2008-06-12 18:39:50Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ *
+ * Program: High level file input and output routines
+ *
+ */
+
+/*
+ * The routines in this file
+ * handle the reading and writing of
+ * disk files. All of details about the
+ * reading and writing of the disk are
+ * in "fileio.c".
+ */
+#include "headers.h"
+#include "../pith/charconv/filesys.h"
+
+
+void init_insmsgchar_cbuf(void);
+int insmsgchar(int);
+char *file_split(char *, size_t, char *, int);
+
+/*
+ * Read a file into the current
+ * buffer. This is really easy; all you do it
+ * find the name of the file, and call the standard
+ * "read a file into the current buffer" code.
+ * Bound to "C-X C-R".
+ */
+int
+fileread(int f, int n)
+{
+ register int s;
+ char fname[NFILEN];
+ EML eml;
+
+ if ((s=mlreply_utf8(_("Read file: "), fname, NFILEN, QNORML, NULL)) != TRUE)
+ return(s);
+
+ if(gmode&MDSCUR){
+ emlwrite(_("File reading disabled in secure mode"),NULL);
+ return(0);
+ }
+
+ if (strlen(fname) == 0) {
+ emlwrite(_("No file name entered"),NULL);
+ return(0);
+ }
+
+ if((gmode & MDTREE) && !in_oper_tree(fname)){
+ eml.s = opertree;
+ emlwrite(_("Can't read file from outside of %s"), &eml);
+ return(0);
+ }
+
+ return(readin(fname, TRUE, TRUE));
+}
+
+
+
+static char *inshelptext[] = {
+ /* TRANSLATORS: several lines of help text */
+ N_("Insert File Help Text"),
+ " ",
+ N_(" Type in a file name to have it inserted into your editing"),
+ N_(" buffer between the line that the cursor is currently on"),
+ N_(" and the line directly below it. You may abort this by "),
+ N_("~ typing the ~F~2 (~^~C) key after exiting help."),
+ " ",
+ N_("End of Insert File Help"),
+ " ",
+ NULL
+};
+
+static char *writehelp[] = {
+ /* TRANSLATORS: several lines of help text */
+ N_("Write File Help Text"),
+ " ",
+ N_(" Type in a file name to have it written out, thus saving"),
+ N_(" your buffer, to a file. You can abort this by typing "),
+ N_("~ the ~F~2 (~^~C) key after exiting help."),
+ " ",
+ N_("End of Write File Help"),
+ " ",
+ " ",
+ NULL
+};
+
+
+/*
+ * Insert a file into the current
+ * buffer. This is really easy; all you do it
+ * find the name of the file, and call the standard
+ * "insert a file into the current buffer" code.
+ * Bound to "C-X C-I".
+ */
+int
+insfile(int f, int n)
+{
+ register int s;
+ char fname[NLINE], dir[NLINE];
+ int retval, bye = 0, msg = 0;
+ char prompt[64], *infile;
+ EXTRAKEYS menu_ins[5];
+ EML eml;
+
+ if (curbp->b_mode&MDVIEW) /* don't allow this command if */
+ return(rdonly()); /* we are in read only mode */
+
+ fname[0] = dir[0] = '\0';
+ while(!bye){
+ /* set up keymenu stuff */
+ if(!msg){
+ int last_menu = 0;
+
+ menu_ins[last_menu].name = "^T";
+ menu_ins[last_menu].key = (CTRL|'T');
+ menu_ins[last_menu].label = N_("To Files");
+ KS_OSDATASET(&menu_ins[last_menu], KS_NONE);
+
+ if(Pmaster && Pmaster->msgntext){
+ menu_ins[++last_menu].name = "^W";
+ menu_ins[last_menu].key = (CTRL|'W');
+ /* TRANSLATORS: Insert File is a key label for a command
+ that inserts a file into a message being composed.
+ InsertMsg means Insert Message and it inserts a different
+ message into the message being composed. */
+ menu_ins[last_menu].label = msg ? N_("InsertFile") : N_("InsertMsg");
+ KS_OSDATASET(&menu_ins[last_menu], KS_NONE);
+ }
+
+#if !defined(DOS) && !defined(MAC)
+ if(Pmaster && Pmaster->upload){
+ menu_ins[++last_menu].name = "^Y";
+ menu_ins[last_menu].key = (CTRL|'Y');
+ menu_ins[last_menu].label = "RcvUpload";
+ KS_OSDATASET(&menu_ins[last_menu], KS_NONE);
+ }
+#endif /* !(DOS || MAC) */
+
+ if(gmode & MDCMPLT){
+ menu_ins[++last_menu].name = msg ? "" : "TAB";
+ menu_ins[last_menu].key = (CTRL|'I');
+ menu_ins[last_menu].label = msg ? "" : N_("Complete");
+ KS_OSDATASET(&menu_ins[last_menu], KS_NONE);
+ }
+
+ menu_ins[++last_menu].name = NULL;
+ }
+
+ snprintf(prompt, sizeof(prompt), "%s to insert from %s %s: ",
+ msg ? "Number of message" : "File",
+ (msg || (gmode&MDCURDIR))
+ ? "current"
+ : ((gmode & MDTREE) || opertree[0]) ? opertree : "home",
+ msg ? "folder" : "directory");
+ s = mlreplyd_utf8(prompt, fname, NLINE, QDEFLT, msg ? NULL : menu_ins);
+ /* something to read and it was edited or the default accepted */
+ if(fname[0] && (s == TRUE || s == FALSE)){
+ bye++;
+ if(msg){
+ init_insmsgchar_cbuf();
+ if((*Pmaster->msgntext)(atol(fname), insmsgchar)){
+ eml.s = fname;
+ emlwrite(_("Message %s included"), &eml);
+ }
+ }
+ else{
+ bye++;
+ if(gmode&MDSCUR){
+ emlwrite(_("Can't insert file in restricted mode"),NULL);
+ }
+ else{
+ if((gmode & MDTREE)
+ && !compresspath(opertree, fname, sizeof(fname))){
+ eml.s = opertree;
+ emlwrite(
+ _("Can't insert file from outside of %s: too many ..'s"), &eml);
+ }
+ else{
+ fixpath(fname, sizeof(fname));
+
+ if((gmode & MDTREE) && !in_oper_tree(fname)){
+ eml.s = opertree;
+ emlwrite(_("Can't insert file from outside of %s"), &eml);
+ }
+ else
+ retval = ifile(fname);
+ }
+ }
+ }
+ }
+ else{
+ switch(s){
+ case (CTRL|'I') :
+ {
+ char *fn;
+
+ dir[0] = '\0';
+ fn = file_split(dir, sizeof(dir), fname, 0);
+
+ if(!pico_fncomplete(dir, fn, sizeof(fname)-(fn-fname)))
+ (*term.t_beep)();
+ }
+
+ break;
+ case (CTRL|'W'):
+ msg = !msg; /* toggle what to insert */
+ break;
+ case (CTRL|'T'):
+ if(msg){
+ emlwrite(_("Can't select messages yet!"), NULL);
+ }
+ else{
+ char *fn;
+
+ fn = file_split(dir, sizeof(dir), fname, 1);
+ if(!isdir(dir, NULL, NULL)){
+ strncpy(dir, (gmode&MDCURDIR)
+ ? (browse_dir[0] ? browse_dir : ".")
+ : ((gmode & MDTREE) || opertree[0])
+ ? opertree
+ : (browse_dir[0] ? browse_dir
+ :gethomedir(NULL)), sizeof(dir));
+ dir[sizeof(dir)-1] = '\0';
+ }
+ else{
+ if(*fn){
+ int dirlen;
+
+ dirlen = strlen(dir);
+ if(dirlen && dir[dirlen - 1] != C_FILESEP){
+ strncat(dir, S_FILESEP, sizeof(dir)-strlen(dir)-1);
+ dir[sizeof(dir)-1] = '\0';
+ }
+
+ strncat(dir, fn, sizeof(dir)-strlen(dir)-1);
+ dir[sizeof(dir)-1] = '\0';
+ if(!isdir(dir, NULL, NULL))
+ dir[MIN(dirlen,sizeof(dir)-1)] = '\0';
+ }
+ }
+
+ fname[0] = '\0';
+ if((s = FileBrowse(dir, sizeof(dir), fname, sizeof(fname),
+ NULL, 0, FB_READ, NULL)) == 1){
+ if(gmode&MDSCUR){
+ emlwrite(_("Can't insert in restricted mode"),
+ NULL);
+ sleep(2);
+ }
+ else{
+ size_t len;
+
+ len = strlen(dir)+strlen(S_FILESEP)+strlen(fname);
+ if ((infile = (char *)malloc((len+1)*sizeof(char))) != NULL){
+ strncpy(infile, dir, len);
+ infile[len] = '\0';
+ strncat(infile, S_FILESEP, len+1-1-strlen(infile));
+ infile[len] = '\0';
+ strncat(infile, fname, len+1-1-strlen(infile));
+ infile[len] = '\0';
+ retval = ifile(infile);
+ free((char *) infile);
+ }
+ else {
+ emlwrite("Trouble allocating space for insert!"
+ ,NULL);
+ sleep(3);
+ }
+ }
+ bye++;
+ }
+ else
+ fname[0] = '\0';
+
+ pico_refresh(FALSE, 1);
+ if(s != 1){
+ update(); /* redraw on return */
+ continue;
+ }
+ }
+
+ break;
+
+#if !defined(DOS) && !defined(MAC)
+ case (CTRL|'Y') :
+ if(Pmaster && Pmaster->upload){
+ char tfname[NLINE];
+
+ if(gmode&MDSCUR){
+ emlwrite(
+ _("\007Restricted mode disallows uploaded command"),
+ NULL);
+ return(0);
+ }
+
+ tfname[0] = '\0';
+ retval = (*Pmaster->upload)(tfname, sizeof(tfname), NULL);
+
+ pico_refresh(FALSE, 1);
+ update();
+
+ if(retval){
+ retval = ifile(tfname);
+ bye++;
+ }
+ else
+ sleep(3); /* problem, show error! */
+
+ if(tfname[0]) /* clean up temp file */
+ our_unlink(tfname);
+ }
+ else
+ (*term.t_beep)(); /* what? */
+
+ break;
+#endif /* !(DOS || MAC) */
+ case HELPCH:
+ if(Pmaster){
+ VARS_TO_SAVE *saved_state;
+
+ saved_state = save_pico_state();
+ (*Pmaster->helper)(msg ? Pmaster->ins_m_help
+ : Pmaster->ins_help,
+ _("Help for Insert File"), 1);
+ if(saved_state){
+ restore_pico_state(saved_state);
+ free_pico_state(saved_state);
+ }
+ }
+ else
+ pico_help(inshelptext, _("Help for Insert File"), 1);
+ case (CTRL|'L'):
+ pico_refresh(FALSE, 1);
+ update();
+ continue;
+ default:
+ ctrlg(FALSE, 0);
+ retval = s;
+ bye++;
+ }
+ }
+ }
+ curwp->w_flag |= WFMODE|WFHARD;
+
+ return(retval);
+}
+
+/*
+ * split out the file name from the path.
+ * Copy path into dirbuf, return filename,
+ * which is a pointer into orig_fname.
+ * is_for_browse - use browse dir if possible
+ * don't want to use it for TAB-completion
+ */
+char *
+file_split(char *dirbuf, size_t dirbuflen, char *orig_fname, int is_for_browse)
+{
+ char *p, *fn;
+ size_t dirlen;
+
+ if(*orig_fname && (p = strrchr(orig_fname, C_FILESEP))){
+ fn = p + 1;
+ dirlen = p - orig_fname;
+ if(p == orig_fname){
+ strncpy(dirbuf, S_FILESEP, dirbuflen);
+ dirbuf[dirbuflen-1] = '\0';
+ }
+#ifdef DOS
+ else if(orig_fname[0] == C_FILESEP
+ || (isalpha((unsigned char)orig_fname[0])
+ && orig_fname[1] == ':')){
+ if(orig_fname[1] == ':' && p == orig_fname+2)
+ dirlen = fn - orig_fname;
+
+ dirlen = MIN(dirlen, dirbuflen-1);
+ strncpy(dirbuf, orig_fname, dirlen);
+ dirbuf[dirlen] = '\0';
+ }
+#else
+ else if (orig_fname[0] == C_FILESEP || orig_fname[0] == '~'){
+ dirlen = MIN(dirlen, dirbuflen-1);
+ strncpy(dirbuf, orig_fname, dirlen);
+ dirbuf[dirlen] = '\0';
+ }
+#endif
+ else
+ snprintf(dirbuf, dirbuflen, "%s%c%.*s",
+ (gmode & MDCURDIR)
+ ? ((is_for_browse && browse_dir[0]) ? browse_dir : ".")
+ : ((gmode & MDTREE) || opertree[0])
+ ? opertree
+ : ((is_for_browse && browse_dir[0])
+ ? browse_dir : gethomedir(NULL)),
+ C_FILESEP, p - orig_fname, orig_fname);
+ }
+ else{
+ fn = orig_fname;
+ strncpy(dirbuf, (gmode & MDCURDIR)
+ ? ((is_for_browse && browse_dir[0]) ? browse_dir : ".")
+ : ((gmode & MDTREE) || opertree[0])
+ ? opertree
+ : ((is_for_browse && browse_dir[0])
+ ? browse_dir : gethomedir(NULL)), dirbuflen);
+ dirbuf[dirbuflen-1] = '\0';
+ }
+
+ return fn;
+}
+
+
+static CBUF_S insmsgchar_cb;
+
+void
+init_insmsgchar_cbuf(void)
+{
+ insmsgchar_cb.cbuf[0] = '\0';
+ insmsgchar_cb.cbufp = insmsgchar_cb.cbuf;
+ insmsgchar_cb.cbufend = insmsgchar_cb.cbuf;
+}
+
+
+/*
+ * This is called with a stream of UTF-8 characters.
+ * We change that stream into UCS characters and insert
+ * those characters.
+ */
+int
+insmsgchar(int c)
+{
+ if(c == '\n'){
+ UCS *u;
+
+ lnewline();
+ for(u = glo_quote_str; u && *u; u++)
+ if(!linsert(1, *u))
+ return(0);
+ }
+ else if(c != '\r'){ /* ignore CR (likely CR of CRLF) */
+ int outchars;
+ UCS ucs;
+
+ if((outchars = utf8_to_ucs4_oneatatime(c, &insmsgchar_cb, &ucs, NULL)) != 0)
+ if(!linsert(1, ucs))
+ return(0);
+ }
+
+ return(1);
+}
+
+
+/*
+ * Read file "fname" into the current
+ * buffer, blowing away any text found there. Called
+ * by both the read and find commands. Return the final
+ * status of the read. Also called by the mainline,
+ * to read in a file specified on the command line as
+ * an argument.
+ */
+int
+readin(char fname[], /* name of file to read */
+ int lockfl, /* check for file locks? */
+ int rename) /* don't rename if reading from, say, alt speller */
+{
+ UCS line[NLINE], *linep;
+ long nline;
+ int s, done, newline;
+
+ curbp->b_linecnt = -1; /* Must be recalculated */
+ if ((s = bclear(curbp)) != TRUE) /* Might be old. */
+ return (s);
+
+ if(rename){
+ strncpy(curbp->b_fname, fname, sizeof(curbp->b_fname));
+ curbp->b_fname[sizeof(curbp->b_fname)-1] = '\0';
+ }
+
+ if ((s=ffropen(fname)) != FIOSUC){ /* Hard file open. */
+ if(s == FIOFNF) /* File not found. */
+ emlwrite(_("New file"), NULL);
+ else
+ fioperr(s, fname);
+ }
+ else{
+ size_t charsread = 0;
+
+ emlwrite(_("Reading file"), NULL);
+ nline = 0L;
+ done = newline = 0;
+ while(!done)
+ if((s = ffgetline(line, NLINE, &charsread, 1)) == FIOEOF){
+ char b[200];
+
+ curbp->b_flag &= ~(BFTEMP|BFCHG);
+ gotobob(FALSE, 1);
+ if(nline > 1)
+ snprintf(b, sizeof(b), _("Read %ld lines"), nline);
+ else
+ snprintf(b, sizeof(b), _("Read 1 line"));
+
+ emlwrite(b, NULL);
+
+ break;
+ }
+ else{
+ if(newline){
+ lnewline();
+ newline = 0;
+ }
+
+ switch(s){
+ case FIOSUC :
+ nline++;
+ newline = 1;
+
+ case FIOLNG :
+ for(linep = line; charsread-- > 0; linep++)
+ linsert(1, *linep);
+
+ break;
+
+ default :
+ done++;
+ break;
+ }
+ }
+
+ ffclose(); /* Ignore errors. */
+ }
+
+ return(s != FIOERR && s != FIOFNF); /* true if success */
+}
+
+
+/*
+ * Ask for a file name, and write the
+ * contents of the current buffer to that file.
+ * Update the remembered file name and clear the
+ * buffer changed flag. This handling of file names
+ * is different from the earlier versions, and
+ * is more compatable with Gosling EMACS than
+ * with ITS EMACS. Bound to "C-X C-W".
+ */
+int
+filewrite(int f, int n)
+{
+ register WINDOW *wp;
+ register int s;
+ char fname[NFILEN];
+ char shows[NLINE], origshows[NLINE], *bufp;
+ EXTRAKEYS menu_write[3];
+ EML eml;
+
+ if(curbp->b_fname[0] != 0){
+ strncpy(fname, curbp->b_fname, sizeof(curbp->b_fname));
+ curbp->b_fname[sizeof(curbp->b_fname)-1] = '\0';
+ }
+ else
+ fname[0] = '\0';
+
+ menu_write[0].name = "^T";
+ menu_write[0].label = N_("To Files");
+ menu_write[0].key = (CTRL|'T');
+ menu_write[1].name = "TAB";
+ menu_write[1].label = N_("Complete");
+ menu_write[1].key = (CTRL|'I');
+ menu_write[2].name = NULL;
+ for(;!(gmode & MDTOOL);){
+ /* TRANSLATORS: Asking for name of file to write data into */
+ s = mlreplyd_utf8(_("File Name to write : "), fname, NFILEN,
+ QDEFLT|QFFILE, menu_write);
+
+ switch(s){
+ case FALSE:
+ if(!fname[0]){ /* no file name to write to */
+ ctrlg(FALSE, 0);
+ return(s);
+ }
+ case TRUE:
+ if((gmode & MDTREE) && !compresspath(opertree, fname, sizeof(fname))){
+ eml.s = opertree;
+ emlwrite(_("Can't write outside of %s: too many ..'s"), &eml);
+ sleep(2);
+ continue;
+ }
+ else{
+ fixpath(fname, sizeof(fname)); /* fixup ~ in file name */
+ if((gmode & MDTREE) && !in_oper_tree(fname)){
+ eml.s = opertree;
+ emlwrite(_("Can't write outside of %s"), &eml);
+ sleep(2);
+ continue;
+ }
+ }
+
+ break;
+
+ case (CTRL|'I'):
+ {
+ char *fn, *p, dir[NFILEN];
+ int l = NFILEN;
+
+ dir[0] = '\0';
+ if(*fname && (p = strrchr(fname, C_FILESEP))){
+ fn = p + 1;
+ l -= fn - fname;
+ if(p == fname){
+ strncpy(dir, S_FILESEP, sizeof(dir));
+ dir[sizeof(dir)-1] = '\0';
+ }
+#ifdef DOS
+ else if(fname[0] == C_FILESEP
+ || (isalpha((unsigned char)fname[0])
+ && fname[1] == ':'))
+#else
+ else if (fname[0] == C_FILESEP || fname[0] == '~')
+#endif
+ {
+ strncpy(dir, fname, MIN(p - fname, sizeof(dir)-1));
+ dir[MIN(p-fname, sizeof(dir)-1)] = '\0';
+ }
+ else
+ snprintf(dir, sizeof(dir), "%s%c%.*s",
+ (gmode & MDCURDIR)
+ ? "."
+ : ((gmode & MDTREE) || opertree[0])
+ ? opertree : gethomedir(NULL),
+ C_FILESEP, p - fname, fname);
+ }
+ else{
+ fn = fname;
+ strncpy(dir, (gmode & MDCURDIR)
+ ? "."
+ : ((gmode & MDTREE) || opertree[0])
+ ? opertree : gethomedir(NULL), sizeof(dir));
+ dir[sizeof(dir)-1] = '\0';
+ }
+
+ if(!pico_fncomplete(dir, fn, sizeof(fname)-(fn-fname)))
+ (*term.t_beep)();
+ }
+
+ continue;
+
+ case (CTRL|'T'):
+ /* If we have a file name, break up into path and file name.*/
+ *shows = 0;
+ if(*fname) {
+ if(isdir(fname, NULL, NULL)) {
+ /* fname is a directory. */
+ strncpy(shows, fname, sizeof(shows));
+ shows[sizeof(shows)-1] = '\0';
+ *fname = '\0';
+ }
+ else {
+ /* Find right most separator. */
+ bufp = strrchr (fname, C_FILESEP);
+ if (bufp != NULL) {
+ /* Copy directory part to 'shows', and file
+ * name part to front of 'fname'. */
+ *bufp = '\0';
+ strncpy(shows, fname, sizeof(shows));
+ shows[sizeof(shows)-1] = '\0';
+ strncpy(fname, bufp+1, MIN(strlen(bufp+1)+1, sizeof(fname)));
+ fname[sizeof(fname)-1] = '\0';
+ }
+ }
+ }
+
+ /* If we did not end up with a valid directory, use home. */
+ if (!*shows || !isdir (shows, NULL, NULL)){
+ strncpy(shows, ((gmode & MDTREE) || opertree[0])
+ ? opertree
+ : (browse_dir[0] ? browse_dir
+ : gethomedir(NULL)), sizeof(shows));
+ shows[sizeof(shows)-1] = '\0';
+ }
+
+ strncpy(origshows, shows, sizeof(origshows));
+ origshows[sizeof(origshows)-1] = '\0';
+ if ((s = FileBrowse(shows, sizeof(shows), fname, sizeof(fname), NULL, 0,
+ FB_SAVE, NULL)) == 1) {
+ if (strlen(shows)+strlen(S_FILESEP)+strlen(fname) < NLINE){
+ strncat(shows, S_FILESEP, sizeof(shows)-strlen(shows)-1);
+ shows[sizeof(shows)-1] = '\0';
+ strncat(shows, fname, sizeof(shows)-strlen(shows)-1);
+ shows[sizeof(shows)-1] = '\0';
+ strncpy(fname, shows, sizeof(fname));
+ fname[sizeof(fname)-1] = '\0';
+ }
+ else {
+ emlwrite("Cannot write. File name too long!!",NULL);
+ sleep(3);
+ }
+ }
+ else if (s == 0 && strcmp(shows, origshows)){
+ strncat(shows, S_FILESEP, sizeof(shows)-strlen(shows)-1);
+ shows[sizeof(shows)-1] = '\0';
+ strncat(shows, fname, sizeof(shows)-strlen(shows)-1);
+ shows[sizeof(shows)-1] = '\0';
+ strncpy(fname, shows, sizeof(fname));
+ fname[sizeof(fname)-1] = '\0';
+ }
+ else if (s == -1){
+ emlwrite("Cannot write. File name too long!!",NULL);
+ sleep(3);
+ }
+
+ pico_refresh(FALSE, 1);
+ update();
+ if(s == 1)
+ break;
+ else
+ continue;
+
+ case HELPCH:
+ pico_help(writehelp, "", 1);
+ case (CTRL|'L'):
+ pico_refresh(FALSE, 1);
+ update();
+ continue;
+
+ default:
+ return(s);
+ break;
+ }
+
+ if(strcmp(fname, curbp->b_fname) == 0)
+ break;
+
+ if((s=fexist(fname, "w", (off_t *)NULL)) == FIOSUC){
+ /*exists overwrite? */
+
+ snprintf(shows, sizeof(shows), _("File \"%s\" exists, OVERWRITE"), fname);
+ if((s=mlyesno_utf8(shows, FALSE)) == TRUE)
+ break;
+ }
+ else if(s == FIOFNF){
+ break; /* go write it */
+ }
+ else{ /* some error, can't write */
+ fioperr(s, fname);
+ return(ABORT);
+ }
+ }
+
+ emlwrite("Writing...", NULL);
+
+ if ((s=writeout(fname, 0)) != -1) {
+ if(!(gmode&MDTOOL)){
+ strncpy(curbp->b_fname, fname, sizeof(curbp->b_fname));
+ curbp->b_fname[sizeof(curbp->b_fname)-1] = '\0';
+ curbp->b_flag &= ~BFCHG;
+
+ wp = wheadp; /* Update mode lines. */
+ while (wp != NULL) {
+ if (wp->w_bufp == curbp)
+ if((Pmaster && s == TRUE) || Pmaster == NULL)
+ wp->w_flag |= WFMODE;
+ wp = wp->w_wndp;
+ }
+ }
+
+ if(s > 1){
+ eml.s = comatose(s);
+ emlwrite(_("Wrote %s lines"), &eml);
+ }
+ else
+ emlwrite(_("Wrote 1 line"), NULL);
+ }
+
+ return ((s == -1) ? FALSE : TRUE);
+}
+
+
+/*
+ * Save the contents of the current
+ * buffer in its associatd file. No nothing
+ * if nothing has changed (this may be a bug, not a
+ * feature). Error if there is no remembered file
+ * name for the buffer. Bound to "C-X C-S". May
+ * get called by "C-Z".
+ */
+int
+filesave(int f, int n)
+{
+ register WINDOW *wp;
+ register int s;
+ EML eml;
+
+ if (curbp->b_mode&MDVIEW) /* don't allow this command if */
+ return(rdonly()); /* we are in read only mode */
+ if ((curbp->b_flag&BFCHG) == 0) /* Return, no changes. */
+ return (TRUE);
+ if (curbp->b_fname[0] == 0) { /* Must have a name. */
+ emlwrite(_("No file name"), NULL);
+ sleep(2);
+ return (FALSE);
+ }
+
+ emlwrite("Writing...", NULL);
+ if ((s=writeout(curbp->b_fname, 0)) != -1) {
+ curbp->b_flag &= ~BFCHG;
+ wp = wheadp; /* Update mode lines. */
+ while (wp != NULL) {
+ if (wp->w_bufp == curbp)
+ if(Pmaster == NULL)
+ wp->w_flag |= WFMODE;
+ wp = wp->w_wndp;
+ }
+ if(s > 1){
+ eml.s = comatose(s);
+ emlwrite(_("Wrote %s lines"), &eml);
+ }
+ else
+ emlwrite(_("Wrote 1 line"), NULL);
+ }
+ return (s);
+}
+
+
+/*
+ * This function performs the details of file
+ * writing. Uses the file management routines in the
+ * "fileio.c" package. The number of lines written is
+ * displayed. Sadly, it looks inside a LINE; provide
+ * a macro for this. Most of the grief is error
+ * checking of some sort.
+ *
+ * If the argument readonly is set, the file is created with user read
+ * and write permission only if it doesn't already exist. Note that the
+ * word readonly is misleading. It is user r/w permission only.
+ *
+ * CHANGES: 1 Aug 91: returns number of lines written or -1 on error, MSS
+ */
+int
+writeout(char *fn, int readonly)
+{
+ register int s;
+ register int t;
+ register LINE *lp;
+ register int nline;
+
+ if (!((s = ffwopen(fn, readonly)) == FIOSUC && ffelbowroom()))
+ return (-1); /* Open writes message. */
+
+ lp = lforw(curbp->b_linep); /* First line. */
+ nline = 0; /* Number of lines. */
+ while (lp != curbp->b_linep) {
+ if ((s=ffputline(&lp->l_text[0], llength(lp))) != FIOSUC)
+ break;
+ ++nline;
+ lp = lforw(lp);
+ }
+
+ t = ffclose(); /* ffclose complains if error */
+
+ if(s == FIOSUC)
+ s = t; /* report worst case */
+
+ return ((s == FIOSUC) ? nline : -1);
+}
+
+
+/*
+ * writetmp - write a temporary file for message text, mindful of
+ * access restrictions and included text. If n is true, include
+ * lines that indicated included message text, otw forget them
+ * If dir is non-null, put the temp file in that directory.
+ */
+char *
+writetmp(int n, char *dir)
+{
+ static char fn[NFILEN];
+ register int s;
+ register int t;
+ register LINE *lp;
+ register int nline;
+
+ tmpname(dir, fn);
+
+ /* Open writes message */
+ if (!fn[0] || (s=ffwopen(fn, TRUE)) != FIOSUC){
+ if(fn[0])
+ our_unlink(fn);
+
+ return(NULL);
+ }
+
+ lp = lforw(curbp->b_linep); /* First line. */
+ nline = 0; /* Number of lines. */
+ while (lp != curbp->b_linep) {
+ if(n || (!n && lp->l_text[0].c != '>'))
+ if ((s=ffputline(&lp->l_text[0], llength(lp))) != FIOSUC)
+ break;
+
+ ++nline;
+ lp = lforw(lp);
+ }
+
+ t = ffclose(); /* ffclose complains if error */
+
+ if(s == FIOSUC)
+ s = t; /* remember worst case */
+
+ if (s != FIOSUC){ /* Some sort of error. */
+ our_unlink(fn);
+ return(NULL);
+ }
+
+ return(fn);
+}
+
+
+/*
+ * Insert file "fname" into the current
+ * buffer, Called by insert file command. Return the final
+ * status of the read.
+ */
+int
+ifile(char fname[])
+{
+ UCS line[NLINE], *linep;
+ long nline;
+ int s, done, newline;
+ size_t charsread = 0;
+ EML eml;
+
+ if ((s=ffropen(fname)) != FIOSUC){ /* Hard file open. */
+ fioperr(s, fname);
+ return(FALSE);
+ }
+
+ gotobol(FALSE, 1);
+
+ curbp->b_flag |= BFCHG; /* we have changed */
+ curbp->b_flag &= ~BFTEMP; /* and are not temporary*/
+ curbp->b_linecnt = -1; /* must be recalculated */
+
+ eml.s = fname;
+ emlwrite(_("Inserting %s."), &eml);
+ done = newline = 0;
+ nline = 0L;
+ while(!done)
+ if((s = ffgetline(line, NLINE, &charsread, 1)) == FIOEOF){
+ char b[200];
+
+ if(llength(curwp->w_dotp) > curwp->w_doto)
+ lnewline();
+ else
+ forwchar(FALSE, 1);
+
+ if(nline > 1)
+ snprintf(b, sizeof(b), _("Inserted %ld lines"), nline);
+ else
+ snprintf(b, sizeof(b), _("Inserted 1 line"));
+
+ emlwrite(b, NULL);
+ break;
+ }
+ else{
+ if(newline){
+ lnewline();
+ newline = 0;
+ }
+
+ switch(s){
+ case FIOSUC : /* copy line into buf */
+ nline++;
+ newline = 1;
+
+ case FIOLNG :
+ for(linep = line; charsread-- > 0; linep++)
+ linsert(1, *linep);
+
+ break;
+
+ default :
+ done++;
+ }
+ }
+
+ ffclose(); /* Ignore errors. */
+
+ return(s != FIOERR);
+}
+
+
+/*
+ * pico_fncomplete - pico's function to complete the given file name
+ */
+int
+pico_fncomplete(char *dirarg, char *fn, size_t fnlen)
+{
+ char *p, *dlist, tmp[NLINE], dir[NLINE];
+ int n, i, match = -1;
+#ifdef DOS
+#define FILECMP(x, y) (toupper((unsigned char)(x))\
+ == toupper((unsigned char)(y)))
+#else
+#define FILECMP(x, y) ((x) == (y))
+#endif
+
+ strncpy(dir, dirarg, sizeof(dir));
+ dir[sizeof(dir)-1] = '\0';
+ pfnexpand(dir, sizeof(dir));
+ if(*fn && (dlist = p = getfnames(dir, fn, &n, NULL, 0))){
+ memset(tmp, 0, sizeof(tmp));
+ while(n--){ /* any names in it */
+ for(i = 0; i < sizeof(tmp)-1 && fn[i] && FILECMP(p[i], fn[i]); i++)
+ ;
+
+ if(!fn[i]){ /* match or more? */
+ if(tmp[0]){
+ for(; p[i] && FILECMP(p[i], tmp[i]); i++)
+ ;
+
+ match = !p[i] && !tmp[i];
+ tmp[i] = '\0'; /* longest common string */
+ }
+ else{
+ match = 1; /* may be it!?! */
+ strncpy(tmp, p, sizeof(tmp));
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+ }
+
+ p += strlen(p) + 1;
+ }
+
+ free(dlist);
+ }
+
+ if(match >= 0){
+ strncpy(fn, tmp, fnlen);
+ fn[fnlen-1] = '\0';
+ if(match == 1){
+ if ((strlen(dir)+strlen(S_FILESEP)+strlen(fn)) < sizeof(dir)){
+ strncat(dir, S_FILESEP, sizeof(dir)-strlen(dir)-1);
+ dir[sizeof(dir)-1] = '\0';
+ strncat(dir, fn, sizeof(dir)-strlen(dir)-1);
+ dir[sizeof(dir)-1] = '\0';
+ if(isdir(dir, NULL, NULL)){
+ strncat(fn, S_FILESEP, fnlen-strlen(fn)-1);
+ fn[fnlen-1] = '\0';
+ }
+ }
+ else{
+ emlwrite("File name too BIG!!",0);
+ sleep(3);
+ *fn = '\0';
+ }
+
+ }
+ }
+
+ return(match == 1);
+}
+
+
+/*
+ * in_oper_tree - returns true if file "f" does reside in opertree
+ */
+int
+in_oper_tree(char *f)
+{
+ int end = strlen(opertree);
+
+ return(!strncmp(opertree, f, end)
+ && (opertree[end-1] == '/'
+ || opertree[end-1] == '\\'
+ || f[end] == '\0'
+ || f[end] == '/'
+ || f[end] == '\\'));
+}
diff --git a/pico/fileio.c b/pico/fileio.c
new file mode 100644
index 00000000..5cf124c0
--- /dev/null
+++ b/pico/fileio.c
@@ -0,0 +1,157 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: fileio.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ *
+ * Program: file reading routines
+ *
+ */
+
+/*
+ * The routines in this file read and write ASCII files from the disk. All of
+ * the knowledge about files are here. A better message writing scheme should
+ * be used.
+ */
+#include "headers.h"
+#include "../pith/charconv/filesys.h"
+
+
+#if defined(bsd) || defined(lnx)
+extern int errno;
+#endif
+
+
+
+FIOINFO g_pico_fio;
+
+/*
+ * Open a file for reading.
+ */
+int
+ffropen(char *fn)
+{
+ int status;
+
+ if ((status = fexist(g_pico_fio.name = fn, "r", (off_t *)NULL)) == FIOSUC){
+ g_pico_fio.flags = FIOINFO_READ;
+ if((g_pico_fio.fp = our_fopen(g_pico_fio.name, "r")) == NULL)
+ status = FIOFNF;
+ }
+
+ return (status);
+}
+
+
+/*
+ * Write a line to the already opened file. The "buf" points to the buffer,
+ * and the "nbuf" is its length, less the free newline. Return the status.
+ * Check only at the newline.
+ */
+int
+ffputline(CELL buf[], int nbuf)
+{
+ register int i;
+ EML eml;
+
+ for(i = 0; i < nbuf; ++i)
+ if(write_a_wide_char((UCS) buf[i].c, g_pico_fio.fp) == EOF)
+ break;
+
+ if(i == nbuf)
+ write_a_wide_char((UCS) '\n', g_pico_fio.fp);
+
+ if(ferror(g_pico_fio.fp)){
+ eml.s = errstr(errno);
+ emlwrite("\007Write error: %s", &eml);
+ sleep(5);
+ return FIOERR;
+ }
+
+ return FIOSUC;
+}
+
+
+/*
+ * Read a line from a file, and store the bytes in the supplied buffer. The
+ * "nbuf" is the length of the buffer. Complain about long lines and lines
+ * at the end of the file that don't have a newline present. Check for I/O
+ * errors too. Return status.
+ *
+ * Translate the line from the user's locale charset to UCS-4.
+ */
+int
+ffgetline(UCS buf[], size_t nbuf, size_t *charsreturned, int msg)
+{
+ size_t i;
+ UCS ucs;
+
+ if(charsreturned)
+ *charsreturned = 0;
+
+ i = 0;
+
+ while((ucs = read_a_wide_char(g_pico_fio.fp, input_cs)) != CCONV_EOF && ucs != '\n'){
+ /*
+ * Don't blat the CR should the newline be CRLF and we're
+ * running on a unix system. NOTE: this takes care of itself
+ * under DOS since the non-binary open turns newlines into '\n'.
+ */
+ if(ucs == '\r'){
+ if((ucs = read_a_wide_char(g_pico_fio.fp, input_cs)) == CCONV_EOF || ucs == '\n')
+ break;
+
+ if(i < nbuf-2) /* Bare CR. Insert it and go on... */
+ buf[i++] = '\r'; /* else, we're up a creek */
+ }
+
+ if(i >= nbuf-2){
+ buf[nbuf - 2] = ucs; /* store last char read */
+ buf[nbuf - 1] = '\0'; /* and terminate it */
+ if(charsreturned)
+ *charsreturned = nbuf - 1;
+
+ if(msg)
+ emlwrite("File has long line", NULL);
+
+ return FIOLNG;
+ }
+
+ buf[i++] = ucs;
+ }
+
+ if(ucs == CCONV_EOF){
+ if(ferror(g_pico_fio.fp)){
+ emlwrite("File read error", NULL);
+ if(charsreturned)
+ *charsreturned = i;
+
+ return FIOERR;
+ }
+
+ if(i != 0)
+ emlwrite("File doesn't end with newline. Adding one.", NULL);
+ else{
+ if(charsreturned)
+ *charsreturned = i;
+ return FIOEOF;
+ }
+ }
+
+ buf[i] = '\0';
+
+ if(charsreturned)
+ *charsreturned = i;
+
+ return FIOSUC;
+}
diff --git a/pico/headers.h b/pico/headers.h
new file mode 100644
index 00000000..5c6e8cab
--- /dev/null
+++ b/pico/headers.h
@@ -0,0 +1,68 @@
+/*
+ * $Id: headers.h 764 2007-10-23 23:44:49Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ *
+ *
+ *
+ * headers.h
+ *
+ * The include file to always include that includes a few other things
+ * - includes the most general system files and other pine include files
+ * - declares the global variables
+ */
+
+
+#ifndef _PICO_HEADERS_INCLUDED
+#define _PICO_HEADERS_INCLUDED
+
+
+#include <system.h>
+#include <general.h>
+
+/*----------------------------------------------------------------------
+ Include files
+
+ ----*/
+
+#ifdef _WINDOWS
+#include "osdep/msmenu.h"
+#include "osdep/mswin.h"
+#endif /* _WINDOWS */
+#include "estruct.h"
+#include "mode.h"
+#include "pico.h"
+#include "keydefs.h"
+#include "edef.h"
+#include "efunc.h"
+#include "utf8stub.h"
+
+/* include osdep proto's defn's */
+#include "osdep/altedit.h"
+#include "osdep/chkpoint.h"
+#include "osdep/color.h"
+#include "osdep/filesys.h"
+#include "osdep/getkey.h"
+#include "osdep/mouse.h"
+#include "osdep/newmail.h"
+#include "osdep/popen.h"
+#include "osdep/raw.h"
+#include "osdep/read.h"
+#include "osdep/shell.h"
+#include "osdep/signals.h"
+#include "osdep/spell.h"
+#include "osdep/terminal.h"
+#include "osdep/tty.h"
+
+
+#endif /* _PICO_HEADERS_INCLUDED */
diff --git a/pico/keydefs.h b/pico/keydefs.h
new file mode 100644
index 00000000..335391c0
--- /dev/null
+++ b/pico/keydefs.h
@@ -0,0 +1,164 @@
+/*
+ * $Id: keydefs.h 766 2007-10-23 23:59:00Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PICO_KEYDEFS_INCLUDED
+#define PICO_KEYDEFS_INCLUDED
+
+/*
+ * Key value definitions for pico/pine
+ */
+
+
+/*
+ * Char conversion constants
+ * Make these different values than c-client's U8G_ERROR constants, though I don't
+ * think that is necessary.
+ * These values are not possible as regular UCS-4 values.
+ */
+
+#undef CTRL
+#define CTRL 0x1000000
+#define FUNC 0x2000000
+
+#define KEY_BASE 0x40000000
+#define CCONV_NEEDMORE (KEY_BASE+1) /* need more octets */
+#define CCONV_BADCHAR (KEY_BASE+2) /* can tell it's bad conversion */
+#define CCONV_EOF (KEY_BASE+3)
+
+/*
+ * defs for keypad and function keys...
+ */
+#define KEY_UP (KEY_BASE+0x100)
+#define KEY_DOWN (KEY_BASE+0x101)
+#define KEY_RIGHT (KEY_BASE+0x102)
+#define KEY_LEFT (KEY_BASE+0x103)
+#define KEY_PGUP (KEY_BASE+0x104)
+#define KEY_PGDN (KEY_BASE+0x105)
+#define KEY_HOME (KEY_BASE+0x106)
+#define KEY_END (KEY_BASE+0x107)
+#define KEY_DEL (KEY_BASE+0x108)
+#define BADESC (KEY_BASE+0x109)
+#define KEY_MOUSE (KEY_BASE+0x10a)
+#define KEY_SCRLUPL (KEY_BASE+0x10b)
+#define KEY_SCRLDNL (KEY_BASE+0x10c)
+#define KEY_SCRLTO (KEY_BASE+0x10d)
+#define KEY_XTERM_MOUSE (KEY_BASE+0x10e)
+#define KEY_DOUBLE_ESC (KEY_BASE+0x10f)
+#define KEY_SWALLOW_Z (KEY_BASE+0x110)
+#define KEY_SWAL_UP (KEY_BASE+0x111) /* These four have to be in the same order */
+#define KEY_SWAL_DOWN (KEY_BASE+0x112) /* as KEY_UP, KEY_DOWN, ... */
+#define KEY_SWAL_RIGHT (KEY_BASE+0x113)
+#define KEY_SWAL_LEFT (KEY_BASE+0x114)
+#define KEY_KERMIT (KEY_BASE+0x115)
+#define KEY_JUNK (KEY_BASE+0x116)
+#define KEY_RESIZE (KEY_BASE+0x117)
+#define CTRL_KEY_UP (KEY_BASE+0x118)
+#define CTRL_KEY_DOWN (KEY_BASE+0x119)
+#define CTRL_KEY_RIGHT (KEY_BASE+0x11a)
+#define CTRL_KEY_LEFT (KEY_BASE+0x11b)
+
+/*
+ * Don't think we are using the fact that this is zero anywhere,
+ * but just in case we'll leave it.
+ */
+#define NO_OP_COMMAND 0x0 /* no-op for short timeouts */
+
+#define NO_OP_IDLE (KEY_BASE+0x120) /* no-op for >25 second timeouts */
+#define READY_TO_READ (KEY_BASE+0x121)
+#define BAIL_OUT (KEY_BASE+0x122)
+#define PANIC_NOW (KEY_BASE+0x123)
+#define READ_INTR (KEY_BASE+0x124)
+#define NODATA (KEY_BASE+0x125)
+#define KEY_UTF8 (KEY_BASE+0x126)
+#define KEY_UNKNOWN (KEY_BASE+0x127)
+
+/*
+ * defines for function keys
+ */
+#define F1 (KEY_BASE+FUNC+0) /* Function key one */
+#define F2 (KEY_BASE+FUNC+1) /* Function key two */
+#define F3 (KEY_BASE+FUNC+2) /* Function key three */
+#define F4 (KEY_BASE+FUNC+3) /* Function key four */
+#define F5 (KEY_BASE+FUNC+4) /* Function key five */
+#define F6 (KEY_BASE+FUNC+5) /* Function key six */
+#define F7 (KEY_BASE+FUNC+6) /* Function key seven */
+#define F8 (KEY_BASE+FUNC+7) /* Function key eight */
+#define F9 (KEY_BASE+FUNC+8) /* Function key nine */
+#define F10 (KEY_BASE+FUNC+9) /* Function key ten */
+#define F11 (KEY_BASE+FUNC+0xa) /* Function key eleven */
+#define F12 (KEY_BASE+FUNC+0xb) /* Function key twelve */
+
+/* 1st tier pine function keys */
+#define PF1 F1
+#define PF2 F2
+#define PF3 F3
+#define PF4 F4
+#define PF5 F5
+#define PF6 F6
+#define PF7 F7
+#define PF8 F8
+#define PF9 F9
+#define PF10 F10
+#define PF11 F11
+#define PF12 F12
+
+#define PF2OPF(x) (x + 0x10)
+#define PF2OOPF(x) (x + 0x20)
+#define PF2OOOPF(x) (x + 0x30)
+
+/* 2nd tier pine function keys */
+#define OPF1 PF2OPF(PF1)
+#define OPF2 PF2OPF(PF2)
+#define OPF3 PF2OPF(PF3)
+#define OPF4 PF2OPF(PF4)
+#define OPF5 PF2OPF(PF5)
+#define OPF6 PF2OPF(PF6)
+#define OPF7 PF2OPF(PF7)
+#define OPF8 PF2OPF(PF8)
+#define OPF9 PF2OPF(PF9)
+#define OPF10 PF2OPF(PF10)
+#define OPF11 PF2OPF(PF11)
+#define OPF12 PF2OPF(PF12)
+
+/* 3rd tier pine function keys */
+#define OOPF1 PF2OOPF(PF1)
+#define OOPF2 PF2OOPF(PF2)
+#define OOPF3 PF2OOPF(PF3)
+#define OOPF4 PF2OOPF(PF4)
+#define OOPF5 PF2OOPF(PF5)
+#define OOPF6 PF2OOPF(PF6)
+#define OOPF7 PF2OOPF(PF7)
+#define OOPF8 PF2OOPF(PF8)
+#define OOPF9 PF2OOPF(PF9)
+#define OOPF10 PF2OOPF(PF10)
+#define OOPF11 PF2OOPF(PF11)
+#define OOPF12 PF2OOPF(PF12)
+
+/* 4th tier pine function keys */
+#define OOOPF1 PF2OOOPF(PF1)
+#define OOOPF2 PF2OOOPF(PF2)
+#define OOOPF3 PF2OOOPF(PF3)
+#define OOOPF4 PF2OOOPF(PF4)
+#define OOOPF5 PF2OOOPF(PF5)
+#define OOOPF6 PF2OOOPF(PF6)
+#define OOOPF7 PF2OOOPF(PF7)
+#define OOOPF8 PF2OOOPF(PF8)
+#define OOOPF9 PF2OOOPF(PF9)
+#define OOOPF10 PF2OOOPF(PF10)
+#define OOOPF11 PF2OOOPF(PF11)
+#define OOOPF12 PF2OOOPF(PF12)
+
+#endif /* PICO_KEYDEFS_INCLUDED */
diff --git a/pico/line.c b/pico/line.c
new file mode 100644
index 00000000..e5df3670
--- /dev/null
+++ b/pico/line.c
@@ -0,0 +1,778 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: line.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ *
+ * Program: Line management routines
+ */
+
+/*
+ * The functions in this file are a general set of line management utilities.
+ * They are the only routines that touch the text. They also touch the buffer
+ * and window structures, to make sure that the necessary updating gets done.
+ * There are routines in this file that handle the kill buffer too. It isn't
+ * here for any good reason.
+ *
+ * Note that this code only updates the dot and mark values in the window list.
+ * Since all the code acts on the current window, the buffer that we are
+ * editing must be being displayed, which means that "b_nwnd" is non zero,
+ * which means that the dot and mark values in the buffer headers are nonsense.
+ */
+
+#include "headers.h"
+
+#define NBLOCK 16 /* Line block chunk size */
+#define KBLOCK 1024 /* Kill buffer block size */
+
+
+/*
+ * Struct to manage the kill and justify buffers
+ */
+struct pkchunk {
+ short used; /* # of characters used in this buffer */
+ UCS bufp[KBLOCK]; /* buffer containing text */
+ struct pkchunk *next; /* pointer to next chunk */
+};
+
+static struct pkbuf {
+ long total; /* # of UCS characters used in buffer */
+ struct pkchunk *first; /* first one of these in the chain */
+ struct pkchunk *last; /* last one of these in the chain */
+} *kbufp, *fbufp;
+
+
+void insspace(int, int);
+int ldelnewline(void);
+void pkbufdel(struct pkbuf **);
+void pkchunkdel(struct pkchunk **);
+int pkbufinsert(UCS, struct pkbuf **);
+long pkbufremove(int, struct pkbuf *);
+
+
+/*
+ * This routine allocates a block of memory large enough to hold a LINE
+ * containing "used" characters. The block is always rounded up a bit. Return
+ * a pointer to the new block, or NULL if there isn't any memory left. Print a
+ * message in the message line if no space.
+ */
+LINE *
+lalloc(int used)
+{
+ register LINE *lp;
+ register int size;
+ EML eml;
+
+ if((size = (used+NBLOCK-1) & ~(NBLOCK-1)) > NLINE)
+ size *= 2;
+
+ if (size == 0) /* Assume that an empty */
+ size = NBLOCK; /* line is for type-in. */
+
+ if ((lp = (LINE *) malloc(sizeof(LINE)+(size*sizeof(CELL)))) == NULL) {
+ eml.s = comatose(size);
+ emlwrite("Cannot allocate %s bytes", &eml);
+ return (NULL);
+ }
+
+ lp->l_size = size;
+ lp->l_used = used;
+ return (lp);
+}
+
+/*
+ * Delete line "lp". Fix all of the links that might point at it (they are
+ * moved to offset 0 of the next line. Unlink the line from whatever buffer it
+ * might be in. Release the memory. The buffers are updated too; the magic
+ * conditions described in the above comments don't hold here.
+ */
+void
+lfree(LINE *lp)
+{
+ register BUFFER *bp;
+ register WINDOW *wp;
+
+ wp = wheadp;
+ while (wp != NULL) {
+ if (wp->w_linep == lp)
+ wp->w_linep = lp->l_fp;
+
+ if (wp->w_dotp == lp) {
+ wp->w_dotp = lp->l_fp;
+ wp->w_doto = 0;
+ }
+
+ if (wp->w_markp == lp) {
+ wp->w_markp = lp->l_fp;
+ wp->w_marko = 0;
+ }
+
+ if (wp->w_imarkp == lp) {
+ wp->w_imarkp = lp->l_fp;
+ wp->w_imarko = 0;
+ }
+
+ wp = wp->w_wndp;
+ }
+
+ bp = bheadp;
+ while (bp != NULL) {
+ if (bp->b_nwnd == 0) {
+ if (bp->b_dotp == lp) {
+ bp->b_dotp = lp->l_fp;
+ bp->b_doto = 0;
+ }
+
+ if (bp->b_markp == lp) {
+ bp->b_markp = lp->l_fp;
+ bp->b_marko = 0;
+ }
+ }
+
+ bp = bp->b_bufp;
+ }
+
+ lp->l_bp->l_fp = lp->l_fp;
+ lp->l_fp->l_bp = lp->l_bp;
+ free((char *) lp);
+}
+
+
+/*
+ * This routine gets called when a character is changed in place in the current
+ * buffer. It updates all of the required flags in the buffer and window
+ * system. The flag used is passed as an argument; if the buffer is being
+ * displayed in more than 1 window we change EDIT to HARD. Set MODE if the
+ * mode line needs to be updated (the "*" has to be set).
+ */
+void
+lchange(int flag)
+{
+ register WINDOW *wp;
+
+ if (curbp->b_nwnd != 1) /* Ensure hard. */
+ flag = WFHARD;
+
+ if ((curbp->b_flag&BFCHG) == 0) { /* First change, so */
+ if(Pmaster == NULL)
+ flag |= WFMODE; /* update mode lines. */
+ curbp->b_flag |= BFCHG;
+ }
+
+ wp = wheadp;
+ while (wp != NULL) {
+ if (wp->w_bufp == curbp)
+ wp->w_flag |= flag;
+ wp = wp->w_wndp;
+ }
+}
+
+
+/*
+ * insert spaces forward into text
+ * default flag and numeric argument
+ */
+void
+insspace(int f, int n)
+{
+ linsert(n, ' ');
+ backchar(f, n);
+}
+
+/*
+ * Insert "n" copies of the character "c" at the current location of dot. In
+ * the easy case all that happens is the text is stored in the line. In the
+ * hard case, the line has to be reallocated. When the window list is updated,
+ * take special care; I screwed it up once. You always update dot in the
+ * current window. You update mark, and a dot in another window, if it is
+ * greater than the place where you did the insert. Return TRUE if all is
+ * well, and FALSE on errors.
+ */
+int
+linsert(int n, UCS c)
+{
+ register LINE *dotp;
+ register int doto;
+ register WINDOW *wp;
+
+ if (curbp->b_mode&MDVIEW) /* don't allow this command if */
+ return(rdonly()); /* we are in read only mode */
+
+ dotp = curwp->w_dotp;
+ doto = curwp->w_doto;
+ lchange(WFEDIT);
+
+ if(!geninsert(&(curwp->w_dotp), &(curwp->w_doto), curbp->b_linep,
+ c, (curwp->w_markp) ? 1 : 0, n, &curbp->b_linecnt))
+ return(FALSE);
+
+ wp = wheadp; /* Update windows */
+ while (wp != NULL) {
+ if (wp->w_linep == dotp)
+ wp->w_linep = wp->w_dotp;
+
+ if (wp->w_imarkp == dotp) { /* added for internal mark */
+ wp->w_imarkp = wp->w_dotp;
+ if (wp->w_imarko > doto)
+ wp->w_imarko += n;
+ }
+
+ if (wp->w_markp == dotp) {
+ wp->w_markp = dotp;
+ if (wp->w_marko > doto)
+ wp->w_marko += n;
+ }
+ wp = wp->w_wndp;
+ }
+
+ return (TRUE);
+}
+
+
+/*
+ * geninsert - do the actual work of inserting a character into
+ * the list of lines.
+ */
+int
+geninsert(LINE **dotp, int *doto, LINE *linep, UCS c, int attb, int n, long *lines)
+{
+ register LINE *lp1;
+ register LINE *lp2;
+ register CELL *cp1;
+ register CELL *cp2;
+ CELL ac;
+
+ ac.a = attb;
+ if (*dotp == linep) { /* At the end: special */
+ if (*doto != 0) {
+ emlwrite("Programmer botch: geninsert", NULL);
+ return (FALSE);
+ }
+
+ if ((lp1=lalloc(n)) == NULL) /* Allocate new line */
+ return (FALSE);
+
+ lp2 = (*dotp)->l_bp; /* Previous line */
+ lp2->l_fp = lp1; /* Link in */
+ lp1->l_fp = *dotp;
+ (*dotp)->l_bp = lp1;
+ lp1->l_bp = lp2;
+ *doto = n;
+ *dotp = lp1;
+ ac.c = (c & CELLMASK);
+ cp1 = &(*dotp)->l_text[0];
+ while(n--)
+ *cp1++ = ac;
+
+ if(lines)
+ (*lines)++;
+
+ return (TRUE);
+ }
+
+ if ((*dotp)->l_used+n > (*dotp)->l_size) { /* Hard: reallocate */
+ if ((lp1=lalloc((*dotp)->l_used+n)) == NULL)
+ return (FALSE);
+
+ cp1 = &(*dotp)->l_text[0];
+ cp2 = &lp1->l_text[0];
+ while (cp1 != &(*dotp)->l_text[*doto])
+ *cp2++ = *cp1++;
+
+ cp2 += n;
+ while (cp1 != &(*dotp)->l_text[(*dotp)->l_used])
+ *cp2++ = *cp1++;
+
+ (*dotp)->l_bp->l_fp = lp1;
+ lp1->l_fp = (*dotp)->l_fp;
+ (*dotp)->l_fp->l_bp = lp1;
+ lp1->l_bp = (*dotp)->l_bp;
+
+ /* global may be keeping track of mark/imark */
+ if(wheadp){
+ if (wheadp->w_imarkp == *dotp)
+ wheadp->w_imarkp = lp1;
+
+ if (wheadp->w_markp == *dotp)
+ wheadp->w_markp = lp1;
+ }
+
+ free((char *) (*dotp));
+ *dotp = lp1;
+ } else { /* Easy: in place */
+ (*dotp)->l_used += n;
+ cp2 = &(*dotp)->l_text[(*dotp)->l_used];
+ cp1 = cp2-n;
+ while (cp1 != &(*dotp)->l_text[*doto])
+ *--cp2 = *--cp1;
+ }
+
+ ac.c = (c & CELLMASK);
+ while(n--) /* add the chars */
+ (*dotp)->l_text[(*doto)++] = ac;
+
+ return(TRUE);
+}
+
+
+/*
+ * Insert a newline into the buffer at the current location of dot in the
+ * current window. The funny ass-backwards way it does things is not a botch;
+ * it just makes the last line in the file not a special case. Return TRUE if
+ * everything works out and FALSE on error (memory allocation failure). The
+ * update of dot and mark is a bit easier than in the above case, because the
+ * split forces more updating.
+ */
+int
+lnewline(void)
+{
+ register CELL *cp1;
+ register CELL *cp2;
+ register LINE *lp1;
+ register LINE *lp2;
+ register int doto;
+ register WINDOW *wp;
+
+ if (curbp->b_mode&MDVIEW) /* don't allow this command if */
+ return(rdonly()); /* we are in read only mode */
+
+ lchange(WFHARD);
+ lp1 = curwp->w_dotp; /* Get the address and */
+ doto = curwp->w_doto; /* offset of "." */
+ if ((lp2=lalloc(doto)) == NULL) /* New first half line */
+ return (FALSE);
+
+ cp1 = &lp1->l_text[0]; /* Shuffle text around */
+ cp2 = &lp2->l_text[0];
+ while (cp1 != &lp1->l_text[doto])
+ *cp2++ = *cp1++;
+
+ cp2 = &lp1->l_text[0];
+ while (cp1 != &lp1->l_text[lp1->l_used])
+ *cp2++ = *cp1++;
+
+ lp1->l_used -= doto;
+ lp2->l_bp = lp1->l_bp;
+ lp1->l_bp = lp2;
+ lp2->l_bp->l_fp = lp2;
+ lp2->l_fp = lp1;
+ wp = wheadp; /* Windows */
+ while (wp != NULL) {
+ if (wp->w_linep == lp1)
+ wp->w_linep = lp2;
+
+ if (wp->w_dotp == lp1) {
+ if (wp->w_doto < doto)
+ wp->w_dotp = lp2;
+ else
+ wp->w_doto -= doto;
+ }
+
+ if (wp->w_imarkp == lp1) { /* ADDED for internal mark */
+ if (wp->w_imarko < doto)
+ wp->w_imarkp = lp2;
+ else
+ wp->w_imarko -= doto;
+ }
+
+ if (wp->w_markp == lp1) {
+ if (wp->w_marko < doto)
+ wp->w_markp = lp2;
+ else
+ wp->w_marko -= doto;
+ }
+ wp = wp->w_wndp;
+ }
+
+ /*
+ * Keep track of the number of lines in the buffer.
+ */
+ ++curbp->b_linecnt;
+ return (TRUE);
+}
+
+
+/*
+ * This function deletes "n" characters, starting at dot. It understands how do deal
+ * with end of lines, etc. It returns TRUE if all of the characters were
+ * deleted, and FALSE if they were not (because dot ran into the end of the
+ * buffer. The "preserve" function is used to save what was deleted.
+ */
+int
+ldelete(long n, int (*preserve)(UCS))
+{
+ register CELL *cp1;
+ register CELL *cp2;
+ register LINE *dotp;
+ register int doto;
+ register int chunk;
+ register WINDOW *wp;
+
+ if (curbp->b_mode&MDVIEW) /* don't allow this command if */
+ return(rdonly()); /* we are in read only mode */
+
+ while (n != 0) {
+ dotp = curwp->w_dotp;
+ doto = curwp->w_doto;
+ if (dotp == curbp->b_linep) /* Hit end of buffer. */
+ return (FALSE);
+ chunk = dotp->l_used-doto; /* Size of chunk. */
+ if (chunk > n)
+ chunk = n;
+ if (chunk == 0) { /* End of line, merge. */
+ lchange(WFHARD);
+ if (ldelnewline() == FALSE
+ || (preserve ? (*preserve)('\n') == FALSE : 0))
+ return (FALSE);
+ --n;
+ continue;
+ }
+
+ lchange(WFEDIT);
+ cp1 = &dotp->l_text[doto]; /* Scrunch text. */
+ cp2 = cp1 + chunk;
+ if (preserve) { /* Kill? */
+ while (cp1 != cp2) {
+ if ((*preserve)(cp1->c) == FALSE)
+ return (FALSE);
+ ++cp1;
+ }
+ cp1 = &dotp->l_text[doto];
+ }
+
+ while (cp2 != &dotp->l_text[dotp->l_used])
+ *cp1++ = *cp2++;
+
+ dotp->l_used -= chunk;
+ wp = wheadp; /* Fix windows */
+ while (wp != NULL) {
+ if (wp->w_dotp==dotp && wp->w_doto>=doto) {
+ wp->w_doto -= chunk;
+ if (wp->w_doto < doto)
+ wp->w_doto = doto;
+ }
+
+ if (wp->w_markp==dotp && wp->w_marko>=doto) {
+ wp->w_marko -= chunk;
+ if (wp->w_marko < doto)
+ wp->w_marko = doto;
+ }
+
+ if (wp->w_imarkp==dotp && wp->w_imarko>=doto) {
+ wp->w_imarko -= chunk;
+ if (wp->w_imarko < doto)
+ wp->w_imarko = doto;
+ }
+
+ wp = wp->w_wndp;
+ }
+ n -= chunk;
+ }
+
+#ifdef _WINDOWS
+ if (preserve == kinsert && ksize() > 0)
+ mswin_killbuftoclip (kremove);
+#endif
+
+ return (TRUE);
+}
+
+/*
+ * Delete a newline. Join the current line with the next line. If the next line
+ * is the magic header line always return TRUE; merging the last line with the
+ * header line can be thought of as always being a successful operation, even
+ * if nothing is done, and this makes the kill buffer work "right". Easy cases
+ * can be done by shuffling data around. Hard cases require that lines be moved
+ * about in memory. Return FALSE on error and TRUE if all looks ok. Called by
+ * "ldelete" only.
+ */
+int
+ldelnewline(void)
+{
+ register CELL *cp1;
+ register CELL *cp2;
+ register LINE *lp1;
+ register LINE *lp2;
+ register LINE *lp3;
+ register WINDOW *wp;
+
+ if (curbp->b_mode&MDVIEW) /* don't allow this command if */
+ return(rdonly()); /* we are in read only mode */
+
+ lp1 = curwp->w_dotp;
+ lp2 = lp1->l_fp;
+ if (lp2 == curbp->b_linep) { /* At the buffer end. */
+ if (lp1->l_used == 0) { /* Blank line. */
+ lfree(lp1);
+ --curbp->b_linecnt;
+ }
+
+ return (TRUE);
+ }
+
+ if (lp2->l_used <= lp1->l_size-lp1->l_used) {
+ cp1 = &lp1->l_text[lp1->l_used];
+ cp2 = &lp2->l_text[0];
+ while (cp2 != &lp2->l_text[lp2->l_used])
+ *cp1++ = *cp2++;
+
+ wp = wheadp;
+ while (wp != NULL) {
+ if (wp->w_linep == lp2)
+ wp->w_linep = lp1;
+ if (wp->w_dotp == lp2) {
+ wp->w_dotp = lp1;
+ wp->w_doto += lp1->l_used;
+ }
+ if (wp->w_markp == lp2) {
+ wp->w_markp = lp1;
+ wp->w_marko += lp1->l_used;
+ }
+ if (wp->w_imarkp == lp2) {
+ wp->w_imarkp = lp1;
+ wp->w_imarko += lp1->l_used;
+ }
+ wp = wp->w_wndp;
+ }
+ lp1->l_used += lp2->l_used;
+ lp1->l_fp = lp2->l_fp;
+ lp2->l_fp->l_bp = lp1;
+ free((char *) lp2);
+ --curbp->b_linecnt;
+ return (TRUE);
+ }
+
+ if ((lp3=lalloc(lp1->l_used+lp2->l_used)) == NULL)
+ return (FALSE);
+
+ cp1 = &lp1->l_text[0];
+ cp2 = &lp3->l_text[0];
+ while (cp1 != &lp1->l_text[lp1->l_used])
+ *cp2++ = *cp1++;
+
+ cp1 = &lp2->l_text[0];
+ while (cp1 != &lp2->l_text[lp2->l_used])
+ *cp2++ = *cp1++;
+
+ lp1->l_bp->l_fp = lp3;
+ lp3->l_fp = lp2->l_fp;
+ lp2->l_fp->l_bp = lp3;
+ lp3->l_bp = lp1->l_bp;
+ wp = wheadp;
+ while (wp != NULL) {
+ if (wp->w_linep==lp1 || wp->w_linep==lp2)
+ wp->w_linep = lp3;
+ if (wp->w_dotp == lp1)
+ wp->w_dotp = lp3;
+ else if (wp->w_dotp == lp2) {
+ wp->w_dotp = lp3;
+ wp->w_doto += lp1->l_used;
+ }
+ if (wp->w_markp == lp1)
+ wp->w_markp = lp3;
+ else if (wp->w_markp == lp2) {
+ wp->w_markp = lp3;
+ wp->w_marko += lp1->l_used;
+ }
+ if (wp->w_imarkp == lp1)
+ wp->w_imarkp = lp3;
+ else if (wp->w_imarkp == lp2) {
+ wp->w_imarkp = lp3;
+ wp->w_imarko += lp1->l_used;
+ }
+ wp = wp->w_wndp;
+ }
+
+ free((char *) lp1);
+ free((char *) lp2);
+ --curbp->b_linecnt;
+ return (TRUE);
+}
+
+
+/*
+ * Tell the caller if the given line is blank or not.
+ */
+int
+lisblank(LINE *line)
+{
+ int n = 0;
+ UCS qstr[NLINE];
+
+ n = (glo_quote_str
+ && quote_match(glo_quote_str, line, qstr, NLINE))
+ ? ucs4_strlen(qstr) : 0;
+
+ for(; n < llength(line); n++)
+ if(!ucs4_isspace(lgetc(line, n).c))
+ return(FALSE);
+
+ return(TRUE);
+}
+
+
+/*
+ * Delete all of the text saved in the kill buffer. Called by commands when a
+ * new kill context is being created. The kill buffer array is released, just
+ * in case the buffer has grown to immense size. No errors.
+ */
+void
+kdelete(void)
+{
+ pkbufdel(&kbufp);
+}
+
+void
+fdelete(void)
+{
+ pkbufdel(&fbufp);
+}
+
+void
+pkbufdel(struct pkbuf **buf)
+{
+ if (*buf) {
+ pkchunkdel(&(*buf)->first);
+ free((char *) *buf);
+ *buf = NULL;
+ }
+}
+
+
+void
+pkchunkdel(struct pkchunk **chunk)
+{
+ if(chunk){
+ if((*chunk)->next)
+ pkchunkdel(&(*chunk)->next);
+
+ free((char *) *chunk);
+ *chunk = NULL;
+ }
+}
+
+
+/*
+ * Insert a character to the kill buffer, enlarging the buffer if there isn't
+ * any room. Always grow the buffer in chunks, on the assumption that if you
+ * put something in the kill buffer you are going to put more stuff there too
+ * later. Return TRUE if all is well, and FALSE on errors.
+ */
+int
+kinsert(UCS c)
+{
+ return(pkbufinsert(c, &kbufp));
+}
+
+int
+finsert(UCS c)
+{
+ return(pkbufinsert(c, &fbufp));
+}
+
+int
+pkbufinsert(UCS c, struct pkbuf **buf)
+{
+ if(!*buf){
+ if((*buf = (struct pkbuf *) malloc(sizeof(struct pkbuf))) != NULL)
+ memset(*buf, 0, sizeof(struct pkbuf));
+ else
+ return(FALSE);
+ }
+
+ if((*buf)->total % KBLOCK == 0){
+ struct pkchunk *p = (*buf)->last;
+ if(((*buf)->last = (struct pkchunk *) malloc(sizeof(struct pkchunk))) != NULL){
+ memset((*buf)->last, 0, sizeof(struct pkchunk));
+ if(p)
+ p->next = (*buf)->last;
+ else
+ (*buf)->first = (*buf)->last;
+ }
+ else
+ return(FALSE);
+ }
+
+ (*buf)->last->bufp[(*buf)->last->used++] = c;
+ (*buf)->total++;
+ return (TRUE);
+}
+
+
+/*
+ * These functions get characters from the requested buffer. If the
+ * character index "n" is off the end, it returns "-1". This lets the
+ * caller just scan along until it gets a "-1" back.
+ */
+long
+kremove(int n)
+{
+ return(pkbufremove(n, kbufp));
+}
+
+long
+fremove(int n)
+{
+ return(pkbufremove(n, fbufp));
+}
+
+
+/*
+ * This would be type UCS except that it needs to return -1.
+ */
+long
+pkbufremove(int n, struct pkbuf *buf)
+{
+ if(n >= 0 && buf && n < buf->total){
+ register struct pkchunk *p = buf->first;
+ int block = n / KBLOCK;
+
+ while(block--)
+ if(!(p = p->next))
+ return(-1);
+
+ return(p->bufp[n % KBLOCK]);
+ }
+ else
+ return(-1);
+}
+
+
+/*
+ * This function just returns the current size of the kill buffer
+ */
+int
+ksize(void)
+{
+ return(kbufp ? (int) kbufp->total : 0);
+}
+
+
+static REGION last_region_added;
+
+void
+set_last_region_added(REGION *region)
+{
+ if(region)
+ last_region_added = (*region);
+ else
+ memset(&last_region_added, 0, sizeof(last_region_added));
+}
+
+
+REGION *
+get_last_region_added(void)
+{
+ return(&last_region_added);
+}
diff --git a/pico/main.c b/pico/main.c
new file mode 100644
index 00000000..ac5bc388
--- /dev/null
+++ b/pico/main.c
@@ -0,0 +1,871 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: main.c 1184 2008-12-16 23:52:15Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Main stand-alone Pine Composer routines
+ *
+ *
+ * WEEMACS/PICO NOTES:
+ *
+ * 08 Jan 92 - removed PINE defines to simplify compiling
+ *
+ * 08 Apr 92 - removed PINE stub calls
+ *
+ */
+
+#include "headers.h"
+#include "../c-client/mail.h"
+#include "../c-client/rfc822.h"
+#include "../pith/osdep/collate.h"
+#include "../pith/charconv/filesys.h"
+#include "../pith/charconv/utf8.h"
+#include "../pith/conf.h"
+
+
+/*
+ * Useful internal prototypes
+ */
+#ifdef _WINDOWS
+int pico_file_drop(int, int, char *);
+#endif
+
+/*
+ * this isn't defined in the library, because it's a pine global
+ * which we use for GetKey's timeout
+ */
+int timeoutset = 0;
+
+
+int my_timer_period = (300 * 1000);
+
+/*
+ * function key mappings
+ */
+static UCS fkm[12][2] = {
+ { F1, (CTRL|'G')},
+ { F2, (CTRL|'X')},
+ { F3, (CTRL|'O')},
+ { F4, (CTRL|'J')},
+ { F5, (CTRL|'R')},
+ { F6, (CTRL|'W')},
+ { F7, (CTRL|'Y')},
+ { F8, (CTRL|'V')},
+ { F9, (CTRL|'K')},
+ { F10, (CTRL|'U')},
+ { F11, (CTRL|'C')},
+#ifdef SPELLER
+ { F12, (CTRL|'T')}
+#else
+ { F12, (CTRL|'D')}
+#endif
+};
+
+char *pico_args(int, char **, int *, int *, int *);
+void pico_args_help(void);
+void pico_vers_help(void);
+void pico_display_args_err(char *, char **, int);
+
+/* TRANSLATORS: An error message about an unknown flag (option)
+ on the command line */
+char args_pico_missing_flag[] = N_("unknown flag \"%c\"");
+/* TRANSLATORS: error message about command line */
+char args_pico_missing_arg[] = N_("missing or empty argument to \"%c\" flag");
+char args_pico_missing_num[] = N_("non numeric argument for \"%c\" flag");
+char args_pico_missing_color[] = N_("missing color for \"%s\" flag");
+char args_pico_missing_charset[] = N_("missing character set for \"%s\" flag");
+char args_pico_input_charset[] = N_("input character set \"%s\" is unsupported");
+char args_pico_output_charset[] = N_("output character set \"%s\" is unsupported");
+
+char *args_pico_args[] = {
+/* TRANSLATORS: little help printed out when incorrect arguments
+ are given to pico program. */
+N_("Possible Starting Arguments for Pico editor:"),
+"",
+N_("\tArgument\t\tMeaning"),
+N_("\t -e \t\tComplete - allow file name completion"),
+N_("\t -k \t\tCut - let ^K cut from cursor position to end of line"),
+N_("\t -a \t\tShowDot - show dot files in file browser"),
+N_("\t -j \t\tGoto - allow 'Goto' command in file browser"),
+N_("\t -g \t\tShow - show cursor in file browser"),
+N_("\t -m \t\tMouse - turn on mouse support"),
+N_("\t -x \t\tNoKeyhelp - suppress keyhelp"),
+N_("\t -p \t\tPreserveStartStop - preserve \"start\"(^Q) and \"stop\"(^S) characters"),
+N_("\t -q \t\tTermdefWins - termcap or terminfo takes precedence over defaults"),
+N_("\t -Q <quotestr> \tSet quote string (eg. \"> \") esp. for composing email"),
+N_("\t -d \t\tRebind - let delete key delete current character"),
+N_("\t -f \t\tKeys - force use of function keys"),
+N_("\t -b \t\tReplace - allow search and replace"),
+N_("\t -h \t\tHelp - give this list of options"),
+N_("\t -r[#cols] \tFill - set fill column to #cols columns, default=72"),
+N_("\t -n[#s] \tMail - notify about new mail every #s seconds, default=180"),
+N_("\t -s <speller> \tSpeller - specify alternative speller"),
+N_("\t -t \t\tShutdown - enable special shutdown mode"),
+N_("\t -o <dir>\tOperation - specify the operating directory"),
+N_("\t -z \t\tSuspend - allow use of ^Z suspension"),
+N_("\t -w \t\tNoWrap - turn off word wrap"),
+N_("\t -W <wordseps> \tSet word separators other than whitespace"),
+#ifndef _WINDOWS
+N_("\t -dcs <display_character_set> \tdefault uses LANG or LC_CTYPE from environment"),
+N_("\t -kcs <keyboard_character_set> \tdefaults to display_character_set"),
+N_("\t -syscs\t\tuse system-supplied translation routines"),
+#endif /* ! _WINDOWS */
+#ifdef _WINDOWS
+N_("\t -cnf color \tforeground color"),
+N_("\t -cnb color \tbackground color"),
+N_("\t -crf color \treverse foreground color"),
+N_("\t -crb color \treverse background color"),
+#endif /* _WINDOWS */
+N_("\t +[line#] \tLine - start on line# line, default=1"),
+N_("\t -v \t\tView - view file"),
+N_("\t -no_setlocale_collate\tdo not do setlocale(LC_COLLATE)"),
+N_("\t -version\tPico version number"),
+"",
+N_("\t All arguments may be followed by a file name to display."),
+"",
+NULL
+};
+
+
+/*
+ * main standalone pico routine
+ */
+#ifdef _WINDOWS
+int
+app_main (int argc, char *argv[])
+#else
+int
+main(int argc, char *argv[])
+#endif
+{
+ UCS c;
+ register int f;
+ register int n;
+ register BUFFER *bp;
+ int viewflag = FALSE; /* are we starting in view mode?*/
+ int starton = 0; /* where's dot to begin with? */
+ int setlocale_collate = 1;
+ char bname[NBUFN]; /* buffer name of file to read */
+ char *file_to_edit = NULL;
+ char *display_charmap = NULL, *dc;
+ char *keyboard_charmap = NULL;
+ int use_system = 0;
+ char *err = NULL;
+
+ set_input_timeout(600);
+ Pmaster = NULL; /* turn OFF composer functionality */
+ km_popped = 0;
+
+ /*
+ * Read command line flags before initializing, otherwise, we never
+ * know to init for f_keys...
+ */
+ file_to_edit = pico_args(argc, argv, &starton, &viewflag, &setlocale_collate);
+
+ set_collation(setlocale_collate, 1);
+
+#define cpstr(s) strcpy((char *)fs_get(1+strlen(s)), s)
+
+#ifdef _WINDOWS
+ init_utf8_display(1, NULL);
+#else /* UNIX */
+
+
+ if(display_character_set)
+ display_charmap = cpstr(display_character_set);
+#if HAVE_LANGINFO_H && defined(CODESET)
+ else if((dc = nl_langinfo_codeset_wrapper()) != NULL)
+ display_charmap = cpstr(dc);
+#endif
+
+ if(!display_charmap)
+ display_charmap = cpstr("US-ASCII");
+
+ if(keyboard_character_set)
+ keyboard_charmap = cpstr(keyboard_character_set);
+ else
+ keyboard_charmap = cpstr(display_charmap);
+
+
+ if(use_system_translation){
+#if PREREQ_FOR_SYS_TRANSLATION
+ use_system++;
+ /* This modifies its arguments */
+ if(setup_for_input_output(use_system, &display_charmap, &keyboard_charmap,
+ &input_cs, &err) == -1){
+ fprintf(stderr, "%s\n", err ? err : "trouble with character set");
+ exit(1);
+ }
+ else if(err){
+ fprintf(stderr, "%s\n", err);
+ fs_give((void **) &err);
+ }
+#endif
+ }
+
+ if(!use_system){
+ if(setup_for_input_output(use_system, &display_charmap, &keyboard_charmap,
+ &input_cs, &err) == -1){
+ fprintf(stderr, "%s\n", err ? err : "trouble with character set");
+ exit(1);
+ }
+ else if(err){
+ fprintf(stderr, "%s\n", err);
+ fs_give((void **) &err);
+ }
+ }
+
+ if(keyboard_charmap){
+ set_locale_charmap(keyboard_charmap);
+ free((void *) keyboard_charmap);
+ }
+
+ if(display_charmap)
+ free((void *) display_charmap);
+
+#endif /* UNIX */
+
+ /*
+ * There are a couple arguments that we need to be sure
+ * are converted for internal use.
+ */
+ if(alt_speller)
+ alt_speller = cpstr(fname_to_utf8(alt_speller));
+
+ if(opertree && opertree[0]){
+ strncpy(opertree, fname_to_utf8(opertree), sizeof(opertree));
+ opertree[sizeof(opertree)-1] = '\0';
+ }
+
+ if(glo_quote_str_orig)
+ glo_quote_str = utf8_to_ucs4_cpystr(fname_to_utf8(glo_quote_str_orig));
+
+ if(glo_wordseps_orig)
+ glo_wordseps = utf8_to_ucs4_cpystr(fname_to_utf8(glo_wordseps_orig));
+
+ if(file_to_edit)
+ file_to_edit = cpstr(fname_to_utf8(file_to_edit));
+
+#undef cpstr
+
+#if defined(DOS) || defined(OS2)
+ if(file_to_edit){ /* strip quotes? */
+ int l;
+
+ if(strchr("'\"", file_to_edit[0])
+ && (l = strlen(file_to_edit)) > 1
+ && file_to_edit[l-1] == file_to_edit[0]){
+ file_to_edit[l-1] = '\0'; /* blat trailing quote */
+ file_to_edit++; /* advance past leading quote */
+ }
+ }
+#endif
+
+ if(!vtinit()) /* Displays. */
+ exit(1);
+
+ strncpy(bname, "main", sizeof(bname)); /* default buffer name */
+ bname[sizeof(bname)-1] = '\0';
+ edinit(bname); /* Buffers, windows. */
+
+ update(); /* let the user know we are here */
+
+#ifdef _WINDOWS
+ mswin_setwindow(NULL, NULL, NULL, NULL, NULL, NULL);
+ mswin_showwindow();
+ mswin_showcaret(1); /* turn on for main window */
+ mswin_allowpaste(MSWIN_PASTE_FULL);
+ mswin_setclosetext("Use the ^X command to exit Pico.");
+ mswin_setscrollcallback (pico_scroll_callback);
+#endif
+
+#if defined(USE_TERMCAP) || defined(USE_TERMINFO) || defined(VMS)
+ if(kbesc == NULL){ /* will arrow keys work ? */
+ (*term.t_putchar)('\007');
+ emlwrite("Warning: keypad keys may be non-functional", NULL);
+ }
+#endif /* USE_TERMCAP/USE_TERMINFO/VMS */
+
+ if(file_to_edit){ /* Any file to edit? */
+
+ makename(bname, file_to_edit); /* set up a buffer for this file */
+
+ bp = curbp; /* read in first file */
+ makename(bname, file_to_edit);
+ strncpy(bp->b_bname, bname, sizeof(bp->b_bname));
+ bp->b_bname[sizeof(bp->b_bname)-1] = '\0';
+
+ if(strlen(file_to_edit) >= NFILEN){
+ char buf[128];
+
+ snprintf(buf, sizeof(buf), "Filename \"%.10s...\" too long", file_to_edit);
+ emlwrite(buf, NULL);
+ file_to_edit = NULL;
+ }
+ else{
+ strncpy(bp->b_fname, file_to_edit, sizeof(bp->b_fname));
+ bp->b_fname[sizeof(bp->b_fname)-1] = '\0';
+ if (((gmode&MDTREE) && !in_oper_tree(file_to_edit)) ||
+ readin(file_to_edit, (viewflag==FALSE), TRUE) == ABORT) {
+ if ((gmode&MDTREE) && !in_oper_tree(file_to_edit)){
+ EML eml;
+ eml.s = opertree;
+ emlwrite(_("Can't read file from outside of %s"), &eml);
+ }
+
+ file_to_edit = NULL;
+ }
+ }
+
+ if(!file_to_edit){
+ strncpy(bp->b_bname, "main", sizeof(bp->b_bname));
+ bp->b_bname[sizeof(bp->b_bname)-1] = '\0';
+ strncpy(bp->b_fname, "", sizeof(bp->b_fname));
+ bp->b_fname[sizeof(bp->b_fname)-1] = '\0';
+ }
+
+ bp->b_dotp = bp->b_linep;
+ bp->b_doto = 0;
+
+ if (viewflag) /* set the view mode */
+ bp->b_mode |= MDVIEW;
+ }
+
+ /* setup to process commands */
+ lastflag = 0; /* Fake last flags. */
+ curbp->b_mode |= gmode; /* and set default modes*/
+
+ curwp->w_flag |= WFMODE; /* and force an update */
+
+ if(timeoutset){
+ EML eml;
+
+ eml.s = comatose(get_input_timeout());
+ emlwrite(_("Checking for new mail every %s seconds"), &eml);
+ }
+
+
+ forwline(0, starton - 1); /* move dot to specified line */
+
+ while(1){
+
+ if(km_popped){
+ km_popped--;
+ if(km_popped == 0) /* cause bottom three lines to be repainted */
+ curwp->w_flag |= WFHARD;
+ }
+
+ if(km_popped){ /* temporarily change to cause menu to be painted */
+ term.t_mrow = 2;
+ curwp->w_ntrows -= 2;
+ curwp->w_flag |= WFMODE;
+ movecursor(term.t_nrow-2, 0); /* clear status line, too */
+ peeol();
+ }
+
+ update(); /* Fix up the screen */
+ if(km_popped){
+ term.t_mrow = 0;
+ curwp->w_ntrows += 2;
+ }
+
+#ifdef MOUSE
+#ifdef EX_MOUSE
+ /* New mouse function for real mouse text seletion. */
+ register_mfunc(mouse_in_pico, 2, 0, term.t_nrow - (term.t_mrow + 1),
+ term.t_ncol);
+#else
+ mouse_in_content(KEY_MOUSE, -1, -1, 0, 0);
+ register_mfunc(mouse_in_content, 2, 0, term.t_nrow - (term.t_mrow + 1),
+ term.t_ncol);
+#endif
+#endif
+#ifdef _WINDOWS
+ mswin_setdndcallback (pico_file_drop);
+ mswin_mousetrackcallback(pico_cursor);
+#endif
+ c = GetKey();
+#ifdef MOUSE
+#ifdef EX_MOUSE
+ clear_mfunc(mouse_in_pico);
+#else
+ clear_mfunc(mouse_in_content);
+#endif
+#endif
+#ifdef _WINDOWS
+ mswin_cleardndcallback ();
+ mswin_mousetrackcallback(NULL);
+#endif
+
+ if(timeoutset && (c == NODATA || time_to_check())){
+ if(pico_new_mail())
+ emlwrite(_("You may possibly have new mail."), NULL);
+ }
+
+ if(km_popped)
+ switch(c){
+ case NODATA:
+ case (CTRL|'L'):
+ km_popped++;
+ break;
+
+ default:
+ /* clear bottom three lines */
+ mlerase();
+ break;
+ }
+
+ if(c == NODATA)
+ continue;
+
+ if(mpresf){ /* erase message line? */
+ if(mpresf++ > MESSDELAY)
+ mlerase();
+ }
+
+ f = FALSE;
+ n = 1;
+
+#ifdef MOUSE
+ clear_mfunc(mouse_in_content);
+#endif
+ /* Do it. */
+ execute(normalize_cmd(c, fkm, 1), f, n);
+ }
+}
+
+
+/*
+ * Parse the command line args.
+ *
+ * Args ac
+ * av
+ * starton -- place to return starton value
+ * viewflag -- place to return viewflag value
+ *
+ * Result: command arguments parsed
+ * possible printing of help for command line
+ * various global flags set
+ * returns the name of any file to open, else NULL
+ */
+char *
+pico_args(int ac, char **av, int *starton, int *viewflag, int *setlocale_collate)
+{
+ int c, usage = 0;
+ char *str;
+ char tmp_1k_buf[1000]; /* tmp buf to contain err msgs */
+
+Loop:
+ /* while more arguments with leading - or + */
+ while(--ac > 0 && (**++av == '-' || **av == '+')){
+ if(**av == '+'){
+ if(*++*av)
+ str = *av;
+ else if(--ac)
+ str = *++av;
+ else{
+ snprintf(tmp_1k_buf, sizeof(tmp_1k_buf), _(args_pico_missing_arg), '+');
+ pico_display_args_err(tmp_1k_buf, NULL, 1);
+ usage++;
+ goto Loop;
+ }
+
+ if(!isdigit((unsigned char)str[0])){
+ snprintf(tmp_1k_buf, sizeof(tmp_1k_buf), _(args_pico_missing_num), '+');
+ pico_display_args_err(tmp_1k_buf, NULL, 1);
+ usage++;
+ }
+
+ if(starton)
+ *starton = atoi(str);
+
+ goto Loop;
+ }
+
+ /* while more chars in this argument */
+ else while(*++*av){
+
+ if(strcmp(*av, "version") == 0){
+ pico_vers_help();
+ }
+ else if(strcmp(*av, "no_setlocale_collate") == 0){
+ *setlocale_collate = 0;
+ goto Loop;
+ }
+#ifndef _WINDOWS
+ else if(strcmp(*av, "syscs") == 0){
+ use_system_translation = !use_system_translation;
+ goto Loop;
+ }
+#endif /* ! _WINDOWS */
+#ifdef _WINDOWS
+ else if(strcmp(*av, "cnf") == 0
+ || strcmp(*av, "cnb") == 0
+ || strcmp(*av, "crf") == 0
+ || strcmp(*av, "crb") == 0){
+
+ char *cmd = *av; /* save it to use below */
+
+ if(--ac){
+ str = *++av;
+ if(cmd[1] == 'n'){
+ if(cmd[2] == 'f')
+ pico_nfcolor(str);
+ else if(cmd[2] == 'b')
+ pico_nbcolor(str);
+ }
+ else if(cmd[1] == 'r'){
+ if(cmd[2] == 'f')
+ pico_rfcolor(str);
+ else if(cmd[2] == 'b')
+ pico_rbcolor(str);
+ }
+ }
+ else{
+ snprintf(tmp_1k_buf, sizeof(tmp_1k_buf), _(args_pico_missing_color), cmd);
+ pico_display_args_err(tmp_1k_buf, NULL, 1);
+ usage++;
+ }
+
+ goto Loop;
+ }
+#endif /* _WINDOWS */
+#ifndef _WINDOWS
+ else if(strcmp(*av, "dcs") == 0 || strcmp(*av, "kcs") == 0){
+ char *cmd = *av;
+
+ if(--ac){
+ if(strcmp(*av, "dcs") == 0){
+ display_character_set = *++av;
+ if(!output_charset_is_supported(display_character_set)){
+ snprintf(tmp_1k_buf, sizeof(tmp_1k_buf), _(args_pico_output_charset), display_character_set);
+ pico_display_args_err(tmp_1k_buf, NULL, 1);
+ usage++;
+ }
+ }
+ else{
+ keyboard_character_set = *++av;
+ if(!input_charset_is_supported(keyboard_character_set)){
+ snprintf(tmp_1k_buf, sizeof(tmp_1k_buf), _(args_pico_input_charset), keyboard_character_set);
+ pico_display_args_err(tmp_1k_buf, NULL, 1);
+ usage++;
+ }
+ }
+ }
+ else{
+ snprintf(tmp_1k_buf, sizeof(tmp_1k_buf), _(args_pico_missing_charset), cmd);
+ pico_display_args_err(tmp_1k_buf, NULL, 1);
+ usage++;
+ }
+
+ goto Loop;
+ }
+#endif /* ! _WINDOWS */
+
+ /*
+ * Single char options.
+ */
+ switch(c = **av){
+ /*
+ * These don't take arguments.
+ */
+ case 'a':
+ gmode ^= MDDOTSOK; /* show dot files */
+ break;
+ case 'b':
+#ifdef notdef
+ /*
+ * Make MDREPLACE always on instead
+ * Don't leave this to allow turning off of MDREPLACE because the
+ * polarity of -b will have changed. Don't think anybody wants to
+ * turn it off.
+ */
+ gmode ^= MDREPLACE; /* -b for replace string in where is command */
+#endif
+ break;
+ case 'd': /* -d for rebind delete key */
+ bindtokey(0x7f, forwdel);
+ break;
+ case 'e': /* file name completion */
+ gmode ^= MDCMPLT;
+ break;
+ case 'f': /* -f for function key use */
+ gmode ^= MDFKEY;
+ break;
+ case 'g': /* show-cursor in file browser */
+ gmode ^= MDSHOCUR;
+ break;
+ case 'h':
+ usage++;
+ break;
+ case 'j': /* allow "Goto" in file browser */
+ gmode ^= MDGOTO;
+ break;
+ case 'k': /* kill from dot */
+ gmode ^= MDDTKILL;
+ break;
+ case 'm': /* turn on mouse support */
+ gmode ^= MDMOUSE;
+ break;
+ case 'p':
+ preserve_start_stop = 1;
+ break;
+ case 'q': /* -q for termcap takes precedence */
+ gmode ^= MDTCAPWINS;
+ break;
+ case 't': /* special shutdown mode */
+ gmode ^= MDTOOL;
+ rebindfunc(wquit, quickexit);
+ break;
+ case 'v': /* -v for View File */
+ case 'V':
+ *viewflag = !*viewflag;
+ break; /* break back to inner-while */
+ case 'w': /* -w turn off word wrap */
+ gmode ^= MDWRAP;
+ break;
+ case 'x': /* suppress keyhelp */
+ sup_keyhelp = !sup_keyhelp;
+ break;
+ case 'z': /* -z to suspend */
+ gmode ^= MDSSPD;
+ break;
+
+ /*
+ * These do take arguments.
+ */
+ case 'r': /* set fill column */
+ case 'n': /* -n for new mail notification */
+ case 's' : /* speller */
+ case 'o' : /* operating tree */
+ case 'Q' : /* Quote string */
+ case 'W' : /* Word separators */
+ if(*++*av)
+ str = *av;
+ else if(--ac)
+ str = *++av;
+ else{
+ if(c == 'r')
+ str= "72";
+ else if(c == 'n')
+ str = "180";
+ else{
+ snprintf(tmp_1k_buf, sizeof(tmp_1k_buf), _(args_pico_missing_arg), c);
+ pico_display_args_err(tmp_1k_buf, NULL, 1);
+ usage++;
+ goto Loop;
+ }
+ }
+
+ switch(c){
+ case 's':
+ alt_speller = str;
+ break;
+ case 'o':
+ strncpy(opertree, str, NLINE);
+ gmode ^= MDTREE;
+ break;
+ case 'Q':
+ glo_quote_str_orig = str;
+ break;
+ case 'W':
+ glo_wordseps_orig = str;
+ break;
+
+ /* numeric args */
+ case 'r':
+ case 'n':
+ if(!isdigit((unsigned char)str[0])){
+ snprintf(tmp_1k_buf, sizeof(tmp_1k_buf), _(args_pico_missing_num), c);
+ pico_display_args_err(tmp_1k_buf, NULL, 1);
+ usage++;
+ }
+
+ if(c == 'r'){
+ if((userfillcol = atoi(str)) < 1)
+ userfillcol = 72;
+ }
+ else{
+ timeoutset = 1;
+ set_input_timeout(180);
+ if(set_input_timeout(atoi(str)) < 30)
+ set_input_timeout(180);
+ }
+
+ break;
+ }
+
+ goto Loop;
+
+ default: /* huh? */
+ snprintf(tmp_1k_buf, sizeof(tmp_1k_buf), _(args_pico_missing_flag), c);
+ pico_display_args_err(tmp_1k_buf, NULL, 1);
+ usage++;
+ break;
+ }
+ }
+ }
+
+ if(usage)
+ pico_args_help();
+
+ /* return the first filename for editing */
+ if(ac > 0)
+ return(*av);
+ else
+ return(NULL);
+}
+
+
+#ifdef _WINDOWS
+/*
+ *
+ */
+int
+pico_file_drop(int x, int y, char *filename)
+{
+ /*
+ * if current buffer is unchanged
+ * *or* "new buffer" and no current text
+ */
+ if(((curwp->w_bufp->b_flag & BFCHG) == 0)
+ || (curwp->w_bufp->b_fname[0] == '\0'
+ && curwp->w_bufp->b_linep == lforw(curwp->w_bufp->b_linep)
+ && curwp->w_doto == 0)){
+ register BUFFER *bp = curwp->w_bufp;
+ char bname[NBUFN];
+
+ makename(bname, filename);
+ strncpy(bp->b_bname, bname, sizeof(bp->b_bname));
+ bp->b_bname[sizeof(bp->b_bname)-1] = '\0';
+ strncpy(bp->b_fname, filename, sizeof(bp->b_fname));
+ bp->b_fname[sizeof(bp->b_fname)-1] = '\0';
+ bp->b_flag &= ~BFCHG; /* turn off change bit */
+ if (readin(filename, 1, 1) == ABORT) {
+ strncpy(bp->b_bname, "", sizeof(bp->b_bname));
+ bp->b_bname[sizeof(bp->b_bname)-1] = '\0';
+ strncpy(bp->b_fname, "", sizeof(bp->b_fname));
+ bp->b_fname[sizeof(bp->b_fname)-1] = '\0';
+ }
+
+ bp->b_dotp = bp->b_linep;
+ bp->b_doto = 0;
+ }
+ else{
+ EML eml;
+
+ ifile(filename);
+ curwp->w_flag |= WFHARD;
+ update();
+ eml.s = filename;
+ emlwrite("Inserted dropped file \"%s\"", &eml);
+ }
+
+ curwp->w_flag |= WFHARD;
+ update(); /* restore cursor */
+ return(1);
+}
+#endif
+
+
+/*----------------------------------------------------------------------
+ print a few lines of help for command line arguments
+
+ Args: none
+
+ Result: prints help messages
+ ----------------------------------------------------------------------*/
+void
+pico_args_help(void)
+{
+ char **a;
+ char *pp[2];
+
+ pp[1] = NULL;
+
+ /** print out possible starting arguments... **/
+
+ for(a=args_pico_args; a && *a; a++){
+ pp[0] = _(*a);
+ pico_display_args_err(NULL, pp, 0);
+ }
+
+ exit(1);
+}
+
+
+void
+pico_vers_help(void)
+{
+ char v0[100];
+ char *v[2];
+
+ snprintf(v0, sizeof(v0), "Pico %.50s", version);
+ v[0] = v0;
+ v[1] = NULL;
+
+ pico_display_args_err(NULL, v, 0);
+ exit(1);
+}
+
+
+/*----------------------------------------------------------------------
+ write argument error to the display...
+
+ Args: none
+
+ Result: prints help messages
+ ----------------------------------------------------------------------*/
+void
+pico_display_args_err(char *s, char **a, int err)
+{
+ char errstr[256], *errp;
+ FILE *fp = err ? stderr : stdout;
+#ifdef _WINDOWS
+ char tmp_20k_buf[SIZEOF_20KBUF];
+#endif
+
+
+ if(err && s)
+ snprintf(errp = errstr, sizeof(errstr), _("Argument Error: %.200s"), s);
+ else
+ errp = s;
+
+#ifdef _WINDOWS
+ if(errp)
+ mswin_messagebox(errp, err);
+
+ if(a && *a){
+ strncpy(tmp_20k_buf, *a++, SIZEOF_20KBUF);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ while(a && *a){
+ strncat(tmp_20k_buf, "\n", SIZEOF_20KBUF-strlen(tmp_20k_buf)-1);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ strncat(tmp_20k_buf, *a++, SIZEOF_20KBUF-strlen(tmp_20k_buf)-1);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ }
+
+ mswin_messagebox(tmp_20k_buf, err);
+ }
+#else
+ if(errp)
+ fprintf(fp, "%s\n", errp);
+
+ while(a && *a)
+ fprintf(fp, "%s\n", *a++);
+#endif
+}
diff --git a/pico/makefile.wnt b/pico/makefile.wnt
new file mode 100644
index 00000000..bed9d814
--- /dev/null
+++ b/pico/makefile.wnt
@@ -0,0 +1,71 @@
+# $Id: makefile.wnt 14098 2005-10-03 18:54:13Z jpf@u.washington.edu $
+#
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+#
+#
+# Makefile for WIN NT version of libpico.lib and pico.exe
+#
+#
+CC=cl
+RM=del
+CP=copy
+RC=rc
+
+#includes symbol info for debugging
+CDEBUG= #-Zi -Od
+LDEBUG= /DEBUG /DEBUGTYPE:CV
+
+STDCFLAGS= -I..\include -I..\regex -nologo -MT -DWIN32 -DDOS -D_WINDOWS -DMSC_MALLOC
+
+CFLAGS= $(CDEBUG) $(STDCFLAGS) $(NET) $(EXTRACFLAGS)
+
+LFLAGS= $(LDEBUG) $(EXTRALDFLAGS)
+
+RCFLAGS =
+
+LIBS= oldnames.lib kernel32.lib advapi32.lib ws2_32.lib user32.lib gdi32.lib shell32.lib comdlg32.lib \
+ libpico.lib osdep/libpicoosd.lib osdep/mswin.res \
+ ../c-client/utf8.obj ../pith/osdep/libpithosd.lib ../pith/charconv/libpithcc.lib
+LIBER=lib
+LIBARGS=/nologo /verbose
+
+HFILES= ../include/system.h ../include/general.h \
+ ebind.h edef.h efunc.h estruct.h headers.h keydefs.h mode.h pico.h
+
+OFILES= attach.obj basic.obj bind.obj browse.obj buffer.obj composer.obj \
+ display.obj file.obj fileio.obj line.obj pico.obj random.obj \
+ region.obj search.obj utf8stub.obj window.obj word.obj
+
+all: blddate.exe pico.exe
+
+.c.obj:
+ $(CC) -c $(CFLAGS) "$(MAKEDIR)"\$*.c
+
+$(OFILES): $(HFILES)
+
+libpico.lib: $(OFILES)
+ $(RM) libpico.lib || rem
+ $(LIBER) /out:libpico.lib $(OFILES)
+
+blddate.exe: blddate.c
+ $(CC) $(CFLAGS) blddate.c
+
+pico.exe: main.obj libpico.lib $(OFILES) mswinver.obj osdep/libpicoosd.lib ../pith/osdep/libpithosd.lib ../c-client/utf8.obj ../pith/charconv/libpithcc.lib osdep/mswin.res
+ blddate > bdate.c
+ $(CC) /c $(CFLAGS) bdate.c
+ link /subsystem:windows /entry:wWinMainCRTStartup /out:pico.exe $(LFLAGS) bdate.obj mswinver.obj main.obj $(LIBS)
+
+clean:
+ $(RM) *.lib
+ $(RM) *.obj
+ $(RM) *.exe
diff --git a/pico/mode.h b/pico/mode.h
new file mode 100644
index 00000000..f09494e0
--- /dev/null
+++ b/pico/mode.h
@@ -0,0 +1,50 @@
+/*
+ * $Id: mode.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef MODE_H
+#define MODE_H
+
+
+/*
+ * definitions for various PICO modes
+ */
+#define MDWRAP 0x00000001 /* word wrap */
+#define MDSPELL 0x00000002 /* spell error parcing */
+#define MDEXACT 0x00000004 /* Exact matching for searches */
+#define MDVIEW 0x00000008 /* read-only buffer */
+#define MDFKEY 0x00000010 /* function key mode */
+#define MDSCUR 0x00000020 /* secure (for demo) mode */
+#define MDSSPD 0x00000040 /* suspendable mode */
+#define MDADVN 0x00000080 /* Pico's advanced mode */
+#define MDTOOL 0x00000100 /* "tool" mode (quick exit) */
+#define MDBRONLY 0x00000200 /* indicates standalone browser */
+#define MDCURDIR 0x00000400 /* use current dir for lookups */
+#define MDALTNOW 0x00000800 /* enter alt ed sans hesitation */
+#define MDSPWN 0x00001000 /* spawn subshell for suspend */
+#define MDCMPLT 0x00002000 /* enable file name completion */
+#define MDDTKILL 0x00004000 /* kill from dot to eol */
+#define MDSHOCUR 0x00008000 /* cursor follows hilite in browser */
+#define MDHBTIGN 0x00010000 /* ignore chars with hi bit set */
+#define MDDOTSOK 0x00020000 /* browser displays dot files */
+#define MDEXTFB 0x00040000 /* stand alone File browser */
+#define MDTREE 0x00080000 /* confine to a subtree */
+#define MDMOUSE 0x00100000 /* allow mouse (part. in xterm) */
+#define MDONECOL 0x00200000 /* single column browser */
+#define MDHDRONLY 0x00400000 /* header editing exclusively */
+#define MDGOTO 0x00800000 /* support "Goto" in file browser */
+#define MDREPLACE 0x01000000 /* allow "Replace" in "Where is"*/
+#define MDTCAPWINS 0x02000000 /* Termcap overrides defaults */
+
+#endif /* MODE_H */
diff --git a/pico/msmem.c b/pico/msmem.c
new file mode 100644
index 00000000..4565e5dc
--- /dev/null
+++ b/pico/msmem.c
@@ -0,0 +1,1301 @@
+#ifndef MSC_MALLOC
+
+#define WIN31
+#define STRICT
+
+#include <windows.h>
+#include <toolhelp.h>
+#include <stdio.h>
+
+#include "mswin.h"
+
+
+
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Memory allocation routines.
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+/*
+ * The plan is to allocate small blocks in the local heap and
+ * large blocks in the global heap. The intention is to keep
+ * the number of global allocations to a minimum.
+ *
+ * The boundry between small memory and large memory is
+ * controld by the constant SMALL_MEM_BOUNDRY. Blocks smaller
+ * than SMALL_MEM_BOUNDRY go into the local heap. This should
+ * be set large enough to capture the majority of small memory
+ * blocks in the local heap. But if it is too large, the local
+ * heap will fill up and we will end up allocating a lot of small
+ * blocks in the global heap.
+ *
+ * Unfortunatly, pine seems to need a large stack. With the
+ * stack, some global data, and the heap all cramed in to a 64K
+ * segment we end up with a heap that is smaller than ideal.
+ * This could be improved by reducing the size of the stack, or
+ * by moving the heap to a seperate memory block. I did a little
+ * work on moving the heap, but was not successful. My attepts
+ * can be seen in the function MemATest().
+ *
+ * Currently (7/8/94) I've set the stack to 32K (in pine/makefile.win),
+ * the heap to 12K, and the small memory boundry to 32 bytes.
+ * Statistics on pine memory allocation suggest that it would be better
+ * to have a 25K local heap and a 95 byte small memory boundry.
+ *
+ * Statistics on memory use can be gathered by logging memory debugging
+ * to a file, then running the following awk script:
+ *
+
+ # mem.awk
+ #
+ # Looks through log and find blocks that were allocated but not
+ # freed uses block id numbers, rather than addresses, since this is
+ # more accurate (and since this is what block ids exist!)
+ #
+ # awk may run out of memory if the logfile gets too big. If this
+ # happens, then another strategy will be needed...
+ #
+
+ BEGIN {
+ FS = "[ ()]+";
+
+ b[0] = 16;
+ b[1] = 32;
+ b[2] = 64;
+ b[3] = 96;
+ b[4] = 128;
+ b[5] = 256;
+ b[6] = 512;
+ b[7] = 1024;
+ b[8] = 2048;
+ b[9] = 4096;
+ b[10] = 9600;
+ b[11] = 20000;
+ b[12] = 40000;
+ b[13] = 80000;
+ b[14] = 200000000;
+ b[15] = 0;
+ bcount = 15;
+ for (i = 0; i < bcount; ++i)
+ c[i] = 0;
+
+ maxmem = 0;
+ maxsmallmem = 0;
+
+ allocs = 0;
+ frees = 0;
+ globalallocs = 0;
+ pallocs = 0;
+ pfrees = 0;
+ }
+
+ {
+ #print "one", $1, "two", $2, "three", $3, "four ", $4, "five ", $5;
+ if( $1 == "MemAlloc:" ) {
+ m[$5] = $0;
+
+ ++allocs;
+ if ($9 == 1) ++globalallocs;
+
+ for (i = 0; i < bcount; ++i) {
+ if (b[i] > $7) {
+ ++c[i];
+ break;
+ }
+ }
+ }
+ else if( $1 == "MemFree:" ) {
+ delete m[$5];
+ ++frees;
+ }
+ if( $1 == "PageAlloc:" ) {
+ p[$5] = $0;
+
+ ++pallocs;
+ }
+ else if( $1 == "PageFree:" ) {
+ delete p[$5];
+ ++pfrees;
+ }
+ else if ($1 == "Memory") {
+ if ($6 > maxmem) maxmem = $6;
+ }
+ else if ($1 == "Small") {
+ if ($7 > maxsmallmem) maxsmallmem = $7;
+ }
+ }
+
+
+ END {
+ for( i in m ) {
+ print m[i]
+ }
+ for (i in p) {
+ print p[i]
+ }
+
+ cumulative = 0;
+ for (i = 0; i < bcount; ++i) {
+ cumulative += c[i];
+ printf "%9d : %5d (%5d)\n", b[i], c[i], cumulative;
+ }
+
+ print;
+ print "Max memory use: ", maxmem;
+ print "Max small memory use: ", maxsmallmem;
+ print;
+ print "Local allocations ", allocs - globalallocs;
+ print "Global allocations ", globalallocs;
+ print "Total allocations ", allocs;
+ print "Total memory frees ", frees;
+ print "Blocks not freed ", allocs - frees;
+ print;
+ print "Page allocations ", pallocs;
+ print "Page frees ", pfrees;
+ print "Pages not freed ", pallocs - pfrees;
+ }
+
+ *
+ * Each memory block is assigned a unique id. This is only used
+ * to match allocations with frees in the debug log.
+ */
+
+
+
+/*
+ * SEGHEAP selectes between two different implementations of the memory
+ * management functions. Defined and it uses a sub allocation scheme.
+ * Undefined and it comples a direct allocation scheme.
+ *
+ * The sub allocation scheme is greatly prefered because it greatly reduces
+ * the number of global memory allocations.
+ */
+#define SEGHEAP /* Use the sub allocation scheme. */
+
+
+
+
+#define MEM_DEBUG /* Compile in memory debugging code.*/
+#define MEM_DEBUG_LEVEL 8 /* Pine debug level at which memory
+ * debug messages will be generated.*/
+#ifdef MEM_DEBUG
+static int MemDebugLevel = 0; /* Doing debugging. */
+static FILE *MemDebugFile = NULL; /* File to write to. */
+#endif
+
+
+
+#ifdef DEBUG
+#define LOCAL
+#else
+#define LOCAL static
+#endif
+
+
+#define GET_SEGMENT(x) (0xffff & ((DWORD)(x) >> 16))
+#define GET_OFFSET(x) (0xffff & (DWORD)(x))
+
+
+#undef malloc
+#undef realloc
+#undef free
+
+
+void MemATest (void);
+
+
+
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Standard memory allcation functions.
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+
+void *
+malloc (size_t size)
+{
+ return (MemAlloc (size));
+}
+
+
+void __far *
+_fmalloc (size_t size)
+{
+ return (MemAlloc (size));
+}
+
+
+void __far *
+realloc (void *memblock, size_t size)
+{
+ return (MemRealloc (memblock, size));
+}
+
+
+void __far *
+_frealloc (void *memblock, size_t size)
+{
+ return (MemRealloc (memblock, size));
+}
+
+
+void
+free (void *memblock)
+{
+ MemFree (memblock);
+}
+
+void
+_ffree (void *memblock)
+{
+ MemFree (memblock);
+}
+
+
+/*
+ * Turn on memory debugging and specify file to write to.
+ */
+void
+MemDebug (int debug, FILE *debugFile)
+{
+#ifdef MEM_DEBUG
+ if (debugFile == NULL) {
+ MemDebugLevel = 0;
+ MemDebugFile = NULL;
+ }
+ else {
+ MemDebugLevel = debug;
+ MemDebugFile = debugFile;
+ fprintf (MemDebugFile, "Memory Debuging set on\n");
+ }
+#endif /* MEM_DEBUG */
+}
+
+
+
+
+
+
+
+#ifdef SEGHEAP
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * SEGHEAP Memory allocation routines.
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+/*
+ * This implementation allocates memory in pages then sub alloates from the
+ * pages. This greatly reduces the number of global memory blocks allocated.
+ * This code originally written by Stephen Chung and posted to a Usenet news
+ * group. I've modified them for use in Pine. The author says:
+ *
+ *
+ * Copyright (C) Stephen Chung, 1991-1992. All rights reserved.
+ *
+ * Afterwords
+ * ----------
+ *
+ * Theoretically, you are required to obtain special approval from me (because
+ * I copyrighted these routines) if you want to use them in your programs.
+ * However, I usually don't really care if you are not using these routines in
+ * a commercial, shareware etc. product.
+ *
+ * Any questions and/or bug fixes, please send email to:
+ *
+ * Stephen Chung stephenc@cunixf.cc.columbia.edu
+ *
+ * If it bounces, then try schung@cogsci.Berkeley.EDU
+ *
+ * Have fun!
+ *
+ */
+
+
+/*
+ * The folloing control debugging code for testing out of memory conditions.
+ * there are to test code.
+ *
+ * MEM_ALLOC_LIMT
+ * Setting this to anything other than zero will limit memory allocation
+ * to (rougly) that many bytes.
+ *
+ * MEM_FAIL_SOON
+ * Compiles in a function which will cause the memory allocation to fail
+ * soon after that function is called. this can be used to test
+ * mem alloc failurs in specific code segments.
+ */
+#define MEM_ALLOC_LIMIT 0
+#define MEM_FAIL_SOON 0
+
+
+#if MEM_FAIL_SOON
+static long MemFailSoonLimit = 0;
+#endif
+
+
+
+#define MAGIC 0x42022667
+#define MAGIC2 0x56743296
+
+typedef struct MemoryStruct {
+ long int magic;
+ void far *page;
+ WORD id;
+ MemSize size;
+ BOOL allocated;
+ struct MemoryStruct far *next, far *prev;
+ long int magic2;
+} MEMHEADER;
+
+typedef struct PageHeaderStruct {
+ long int magic;
+ HANDLE handle;
+ WORD id;
+ MemSize size;
+ MemSize used;
+ MemSize overhead;
+ MEMHEADER far *data, far *empty;
+ struct PageHeaderStruct far *next, far *prev;
+ long int magic2;
+} MEMPAGEHEADER;
+
+typedef struct {
+ MEMPAGEHEADER far *pages;
+ int nr_pages;
+} MAINMEMHEADER;
+
+#define PAGESIZE (6 * 1024)
+#define USEABLESIZE (PAGESIZE - sizeof(MEMPAGEHEADER) - sizeof(MEMHEADER))
+
+
+LOCAL MAINMEMHEADER MemHeader = { NULL, 0 };
+LOCAL WORD MemID = 0; /* Keep track of ID. */
+LOCAL WORD PageID = 0;
+LOCAL unsigned long MemInUse = 0; /* Total bytes in use. */
+LOCAL unsigned long MemInUseMax = 0; /* max in use at one time. */
+LOCAL unsigned long PageMemInUse = 0;
+LOCAL unsigned long PageMemInUseMax = 0;
+
+
+
+static MEMPAGEHEADER far *
+AddPage(MemSize n)
+{
+ void far *cp;
+ MEMHEADER far *mp;
+ MEMPAGEHEADER far *p;
+ HANDLE handle = NULL;
+
+
+#if MEM_ALLOC_LIMIT
+ if (n + PageMemInUse > MEM_ALLOC_LIMIT) {
+ MessageBox (NULL, "PageAlloc: Above preset limit, allocation fails",
+ "Out Of Memory", MB_ICONSTOP | MB_OK);
+ return (NULL);
+ }
+#endif
+
+ handle = GlobalAlloc(GHND, n);
+ if (handle == NULL) {
+#ifdef MEM_DEBUG
+ if (MemDebugLevel >= 1)
+ fprintf (MemDebugFile, "***\n*** Out of memory: allocating %d bytes\n***\n", n);
+#endif
+ return (NULL);
+ }
+
+ if (MemHeader.pages == NULL || MemHeader.nr_pages <= 0) {
+ p = MemHeader.pages = (MEMPAGEHEADER far *) GlobalLock(handle);
+ p->prev = NULL;
+ } else {
+ for (p = MemHeader.pages; p->next != NULL; p = p->next);
+ p->next = (MEMPAGEHEADER far *) GlobalLock(handle);
+ p->next->prev = p;
+ p = p->next;
+ }
+
+ p->magic = MAGIC;
+ p->handle = handle;
+ p->next = NULL;
+ p->id = PageID++;
+ p->size = n;
+ p->used = 0;
+ p->overhead = sizeof(MEMPAGEHEADER) + sizeof(MEMHEADER);
+ p->magic2 = MAGIC2;
+
+ cp = ((char far *) p) + sizeof(MEMPAGEHEADER);
+ mp = (MEMHEADER far *) cp;
+
+ p->data = p->empty = mp;
+
+ mp->magic = 0L;
+ mp->magic2 = 0L;
+ mp->allocated = FALSE;
+ mp->page = p;
+ mp->size = p->size - p->overhead;
+ mp->next = mp->prev = NULL;
+
+ MemHeader.nr_pages++;
+
+
+#ifdef MEM_DEBUG
+ if (MemDebugLevel >= MEM_DEBUG_LEVEL) {
+ fprintf (MemDebugFile, "PageAlloc: addr(%lx) id(%u) size(%ld) global(%d)\n",
+ p, p->id, (long)n, 1);
+ fflush (MemDebugFile);
+ }
+#endif /* MEM_DEBUG */
+
+ PageMemInUse += n;
+ if (PageMemInUse > PageMemInUseMax)
+ PageMemInUseMax = PageMemInUse;
+
+
+ return (p);
+}
+
+
+
+static void
+DeletePage (MEMPAGEHEADER far *p)
+{
+#ifdef MEM_DEBUG
+ /* Deubgging info... */
+ if (MemDebugLevel >= 4) {
+ if (PageMemInUse == PageMemInUseMax)
+ fprintf (MemDebugFile, "Page usage is up to %lu\n", PageMemInUseMax);
+ }
+ if (MemDebugLevel >= MEM_DEBUG_LEVEL) {
+ fprintf (MemDebugFile, "PageFree: addr(%lx) id(%u)\n",
+ p, p->id);
+ fflush (MemDebugFile);
+ }
+#endif /* MEM_DEBUG */
+
+ PageMemInUse -= p->size;
+
+
+ if (p->next == NULL && p->prev == NULL) {
+ MemHeader.pages = NULL;
+ MemHeader.nr_pages = 0;
+ GlobalUnlock (p->handle);
+ GlobalFree (p->handle);
+ } else {
+ if (p == MemHeader.pages) MemHeader.pages = p->next;
+ MemHeader.nr_pages--;
+
+ if (p->prev != NULL) p->prev->next = p->next;
+ if (p->next != NULL) p->next->prev = p->prev;
+
+ GlobalUnlock (p->handle);
+ GlobalFree (p->handle);
+ }
+}
+
+
+/*
+ * Segmented heap memory allocation.
+ */
+
+MemPtr
+_MemAlloc (MemSize n, char __far * file, int line)
+{
+ MEMPAGEHEADER far *p;
+ MEMHEADER far *mp;
+ char far *cp;
+
+ if (n >= 65535) {
+ assert (n < 65535);
+ goto AllocFail;
+ }
+
+#if MEM_FAIL_SOON
+ if (MemFailSoonLimit != 0 && MemFailSoonLimit < MemInUse + n) {
+ MessageBox (NULL, "MemAlloc: Told to fail here, allocation fails",
+ "Out Of Memory", MB_ICONSTOP | MB_OK);
+ return (NULL);
+ }
+#endif
+
+ /* Larger than page size? */
+
+ if (n > USEABLESIZE) {
+ p = AddPage(n + sizeof(MEMPAGEHEADER) + sizeof(MEMHEADER));
+ if (p == NULL)
+ goto AllocFail;
+
+ mp = p->data;
+ mp->magic = MAGIC;
+ mp->magic2 = MAGIC2;
+ mp->id = MemID++;
+ mp->allocated = TRUE;
+
+
+ p->used = n;
+ p->empty = NULL;
+
+ cp = ((char far *) mp) + sizeof(MEMHEADER);
+#ifdef MEM_DEBUG
+ if (MemDebugLevel >= MEM_DEBUG_LEVEL) {
+ fprintf (MemDebugFile, "MemAlloc: addr(%lx) id(%u) size(%ld) global(%d)\n",
+ cp, mp->id, (long)n, 0);
+ fflush (MemDebugFile);
+ }
+#endif /* MEM_DEBUG */
+
+ MemInUse += n;
+ if (MemInUse > MemInUseMax)
+ MemInUseMax = MemInUse;
+ return ((MemPtr) cp);
+ }
+
+
+ /* Search for the hole */
+
+ for (p = MemHeader.pages; p != NULL; p = p->next) {
+ /* Scan the chains */
+ if (p->size - p->used - p->overhead <= 0) continue;
+ if (p->empty == NULL) continue;
+
+ for (mp = p->empty; mp != NULL; mp = mp->next) {
+ if (!mp->allocated && mp->size >= n) break;
+ }
+
+ if (mp != NULL) break;
+ }
+
+ /* New page needed? */
+
+ if (p == NULL) {
+ p = AddPage(PAGESIZE);
+ if (p == NULL)
+ goto AllocFail;
+ mp = p->data;
+ }
+
+ /* Do we need to break it up? */
+
+ if (mp->size - n > sizeof(MEMHEADER)) {
+ MEMHEADER far *mp2;
+
+ cp = ((char far *) mp) + n + sizeof(MEMHEADER);
+ mp2 = (MEMHEADER far *) cp;
+
+ mp2->magic = 0L;
+ mp2->magic2 = 0L;
+ mp2->allocated = FALSE;
+ mp2->page = p;
+ mp2->size = mp->size - n - sizeof(MEMHEADER);
+
+ mp2->next = mp->next;
+ mp2->prev = mp;
+ if (mp->next != NULL) mp->next->prev = mp2;
+ mp->next = mp2;
+
+
+ p->overhead += sizeof(MEMHEADER);
+
+ mp->size = n;
+ }
+
+ mp->magic = MAGIC;
+ mp->magic2 = MAGIC2;
+ mp->allocated = TRUE;
+ mp->id = MemID++;
+
+ p->used += n;
+ cp = ((char far *) mp) + sizeof(MEMHEADER);
+
+
+ /* Debugging info... */
+#ifdef MEM_DEBUG
+ if (MemDebugLevel >= MEM_DEBUG_LEVEL) {
+ fprintf (MemDebugFile, "MemAlloc: addr(%lx) id(%u) size(%ld) global(%d)\n",
+ cp, mp->id, (long)n, 0);
+ fflush (MemDebugFile);
+ }
+#endif /* MEM_DEBUG */
+
+ MemInUse += n;
+ if (MemInUse > MemInUseMax)
+ MemInUseMax = MemInUse;
+
+
+ /* Search for the next empty hole */
+
+ for (; mp != NULL; mp = mp->next) {
+ if (!mp->allocated && mp->size > 0) break;
+ }
+
+ p->empty = mp;
+
+ return ((MemPtr) cp);
+
+
+AllocFail:
+#if 0
+ assert (FALSE /* Memory allocation failed! */);*/
+#endif
+ return (NULL);
+}
+
+
+
+/*
+ * Segmented heap memory free.
+ */
+int
+_MemFree (MemPtr vp, char __far *file, int line)
+{
+ MEMPAGEHEADER far *p;
+ MEMHEADER far *mp, far *mp2;
+ char far *cp;
+
+ if (vp == NULL)
+ return (0);
+
+
+ cp = ((char far *) vp) - sizeof(MEMHEADER);
+ mp = (MEMHEADER far *) cp;
+
+ if (mp->magic != MAGIC || mp->magic2 != MAGIC2|| !mp->allocated) {
+ assert (mp->magic == MAGIC);
+ assert (mp->magic2 == MAGIC2);
+ assert (mp->allocated);
+ return (-1);
+ }
+
+#ifdef MEM_DEBUG
+ /* Deubgging info... */
+ if (MemDebugLevel >= 4) {
+ if (MemInUse == MemInUseMax)
+ fprintf (MemDebugFile, "Memory usage is up to %lu\n", MemInUseMax);
+ }
+ if (MemDebugLevel >= MEM_DEBUG_LEVEL) {
+ fprintf (MemDebugFile, "MemFree: addr(%lx) id(%u)\n", vp, mp->id);
+ fflush (MemDebugFile);
+ }
+#endif /* MEM_DEBUG */
+
+ MemInUse -= mp->size;
+
+
+ p = (MEMPAGEHEADER far *) mp->page;
+ p->used -= mp->size;
+
+ mp->magic = 0L;
+ mp->magic2 = 0L;
+ mp->allocated = FALSE;
+
+ /* Merge? */
+
+ mp2 = mp->prev;
+
+ if (mp2 != NULL && !mp2->allocated) {
+ mp2->next = mp->next;
+ if (mp->next != NULL) mp->next->prev = mp2;
+ mp2->size += mp->size + sizeof(MEMHEADER);
+
+ p->overhead -= sizeof(MEMHEADER);
+
+ mp = mp2;
+ }
+
+ mp2 = mp->next;
+
+ if (mp2 != NULL && !mp2->allocated) {
+ mp->next = mp2->next;
+ if (mp2->next != NULL)
+ mp2->next->prev = mp;
+
+ mp->size += mp2->size + sizeof(MEMHEADER);
+
+ p->overhead -= sizeof(MEMHEADER);
+ }
+
+ if (mp->prev == NULL && mp->next == NULL) {
+ DeletePage(p);
+ } else {
+ if (p->empty == NULL || mp < p->empty) p->empty = mp;
+ }
+ return (0);
+}
+
+
+
+MemPtr
+_MemRealloc (MemPtr p, MemSize n, char __far *file, int line)
+{
+ MEMHEADER far *mp;
+ char far *cp;
+
+ if (p != NULL) {
+ /* Block already large enough? */
+ cp = ((char far *) p) - sizeof(MEMHEADER);
+ mp = (MEMHEADER far *) cp;
+
+ if (mp->magic != MAGIC || mp->magic2 != MAGIC2) {
+ assert (mp->magic == MAGIC);
+ assert (mp->magic2 == MAGIC2);
+ return (p);
+ }
+
+ if (mp->size >= n) return (p); /* No need to do anything */
+ }
+ /* Else swap to another block */
+
+ cp = MemAlloc (n);
+ if (cp == NULL)
+ return (NULL);
+
+ if (p != NULL) {
+ _fmemcpy(cp, p, (size_t)((mp->size >= n) ? n : mp->size));
+ MemFree (p);
+ }
+
+ return ((void far *) cp);
+}
+
+
+
+void
+MemFailSoon (MemSize n)
+{
+#if MEM_FAIL_SOON
+ MemFailSoonLimit = MemInUse + n;
+#ifdef MEM_DEBUG
+ if (MemDebugLevel >= 1) {
+ fprintf (MemDebugFile,
+ "MemFailSoon: Fail when allocation increases by %ld (Max %ld)\n",
+ n, MemFailSoonLimit);
+ }
+#endif
+#endif
+}
+
+
+
+
+
+MemSize
+MemBlkSize (MemPtr p)
+{
+ MEMHEADER far *mp;
+ char far *cp;
+
+ if (p == NULL) return (0);
+ cp = ((char far *) p) - sizeof(MEMHEADER);
+
+ mp = (MEMHEADER far *) cp;
+
+ if (mp->magic != MAGIC || mp->magic2 != MAGIC2) {
+ assert (mp->magic == MAGIC);
+ assert (mp->magic2 == MAGIC2);
+ return (0);
+ }
+
+ return (mp->size);
+}
+
+
+#if 0
+MemPtr
+MemDup (void far *p)
+{
+ unsigned int len;
+ void far *p1;
+
+ len = MgetBlkSize (p);
+ p1 = MemAlloc (len);
+ if (p1 != NULL)
+ _fmemcpy(p1, p, len);
+
+ return (p1);
+}
+
+
+void
+MemoryStatistics (long int *allocated, long int *used, long int *overhead)
+{
+ MEMPAGEHEADER far *p;
+
+ *allocated = *used = *overhead = 0L;
+
+ for (p = MemHeader.pages; p != NULL; p = p->next) {
+ *allocated += p->size;
+ *used += p->used;
+ *overhead += p->overhead;
+ }
+}
+#endif
+
+
+void
+MemFreeAll (void)
+{
+ MEMPAGEHEADER far *p, far *p1;
+
+ for (p = MemHeader.pages; p != NULL; ) {
+ p1 = p->next;
+ GlobalUnlock (p->handle);
+ GlobalFree (p->handle);
+ p = p1;
+ }
+
+ MemHeader.pages = NULL;
+ MemHeader.nr_pages = 0;
+}
+
+
+#ifdef MEM_DEBUG
+
+/* For debugging purposes... not very pretty */
+
+void PrintMemoryChains(void)
+{
+ MEMPAGEHEADER far *p;
+ MEMHEADER far *mp;
+ char far *cp;
+ char buffer[100];
+
+ /* Block already large enough? */
+
+
+ for (p = MemHeader.pages; p != NULL; p = p->next) {
+ for (mp = p->data; mp != NULL; mp = mp->next) {
+ fprintf (MemDebugFile, "%Fp | %u | %s", mp, mp->size, mp->allocated ? "Alloc" : "Free");
+ }
+ }
+}
+
+#endif /* DEBUG */
+
+
+
+#else /* !SEGHEAP. Old version, not used. */
+
+
+
+
+
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Direct memory allocation
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+/*
+ * This following implementation allocates large memory blocks directly
+ * from global memory and small memory blocks from the local heap. The
+ * problem with this method is that pine's local heap is quite small
+ * so most of the memory ends up comming from the global heap.
+ */
+
+
+#define GUARD_LOW0 0xbbbb
+#define GUARD_LOW 0x9999
+#define GUARD_HIGH 0xaaaaaaaa
+
+#define SMALL_MEM_BOUNDRY 32
+
+#define HEAP_SIZE 32000
+
+
+/* Memory block header. Placed at beginning of each allocated block. */
+typedef struct { /*size len */
+ WORD guard0; /* 00 - 02 */
+ HGLOBAL handle; /* 02 - 02 */
+ short globalBlk; /* 04 - 02 */
+ MemSize size; /* 06 - 04 */
+ WORD id; /* 0A - 02 */
+ WORD guard1; /* 0C - 02 */
+} MemBlk; /* Total size: 0E */
+
+typedef MemBlk __far * MemBlkPtr;
+
+
+/* Memory high guard tailer. Placed at end of each allocated block. */
+typedef struct {
+ unsigned long guard1;
+} MemHighGuard;
+
+typedef MemHighGuard __far *MemHighGuardPtr;
+
+
+/*
+ * Memory allocation globals.
+ */
+LOCAL WORD MemID = 0; /* Keep track of ID. */
+LOCAL unsigned long MemLocalFails = 0;
+LOCAL BOOL MemLocalFull = FALSE; /* True when local heap full*/
+#ifdef MEM_DEBUG
+LOCAL unsigned long MemInUse = 0; /* Total bytes in use. */
+LOCAL unsigned long MemInUseMax = 0; /* max in use at one time. */
+LOCAL unsigned long SmallMemInUse = 0;
+LOCAL unsigned long SmallMemInUseMax = 0;
+#endif /* MEM_DEBUG */
+
+
+
+/*
+ * Allocate a memory block.
+ * The file and line indicate where we are called from (for debugging)
+ * but in pine these mostly point to a bottel neck routine and are
+ * useless.
+ */
+MemPtr
+_MemAlloc (MemSize size, char __far * file, int line)
+{
+ MemBlkPtr pBlk;
+ MemHighGuardPtr pHG;
+ HGLOBAL hBlk;
+ HLOCAL hLBlk;
+ UINT totalSize;
+ BYTE __far * pb;
+
+ assert (size <= MEM_BLOCK_SIZE_MAX);
+
+
+ /*
+ * Calculate total size we need to allocate.
+ */
+ totalSize = (UINT)size + sizeof (MemBlk) + sizeof (MemHighGuard);
+
+
+ pBlk = NULL;
+
+ /*
+ * If it's a small block and the local heap is not full, try
+ * allocating from the local heap.
+ */
+ if (size <= SMALL_MEM_BOUNDRY && !MemLocalFull) {
+
+ /* Allocate block from local storage. */
+ hLBlk = LocalAlloc (LMEM_MOVEABLE, totalSize);
+ if (hLBlk != NULL) {
+
+ /* Lock block and dereference. */
+ pBlk = (MemBlkPtr) LocalLock (hLBlk);
+ if (pBlk != NULL) {
+ pBlk->handle = hLBlk;
+ pBlk->globalBlk = FALSE;
+ }
+ else
+ LocalFree (hLBlk);
+ }
+ else {
+ ++MemLocalFails;
+ MemLocalFull = TRUE;
+#ifdef MEM_DEBUG
+ if (MemDebugLevel >= MEM_DEBUG_LEVEL)
+ fprintf (MemDebugFile, "Local Memory alloc failed, %lu fails, %lu bytes in use\n",
+ MemLocalFails, SmallMemInUse);
+#endif
+ }
+ }
+
+
+ /*
+ * If it is a large block, or local alloc failed, we allocate from
+ * global space.
+ */
+ if (pBlk == NULL) {
+
+ /* Allocate block from global storage. */
+ hBlk = GlobalAlloc (GMEM_MOVEABLE, totalSize);
+ if (hBlk == NULL)
+ return (NULL);
+
+
+ /* Lock block and dereference. */
+ pBlk = (MemBlkPtr) GlobalLock (hBlk);
+ if (pBlk == NULL) {
+ GlobalFree (hBlk);
+ return (NULL);
+ }
+ pBlk->handle = hBlk;
+ pBlk->globalBlk = TRUE;
+ }
+
+
+
+ /* Fill rest of header. */
+ pBlk->guard0 = GUARD_LOW0;
+ pBlk->size = size;
+ pBlk->id = ++MemID;
+ pBlk->guard1 = GUARD_LOW;
+
+
+ /* Find address that will be returned to caller. */
+ pb = (BYTE __far *) (pBlk + 1);
+
+
+ /* Find high guard and fill. */
+ pHG = (MemHighGuardPtr) (pb + size);
+ pHG->guard1 = GUARD_HIGH;
+
+
+ /* Debugging info... */
+#ifdef MEM_DEBUG
+ if (MemDebugLevel >= MEM_DEBUG_LEVEL) {
+ if( !file ) file = "??";
+ fprintf (MemDebugFile, "MemAlloc: addr(%lx) id(%u) size(%ld) global(%d)\n",
+ pb, pBlk->id, (long)size, pBlk->globalBlk);
+ fflush (MemDebugFile);
+ }
+ MemInUse += totalSize;
+ if (MemInUse > MemInUseMax)
+ MemInUseMax = MemInUse;
+ if (size <= SMALL_MEM_BOUNDRY) {
+ SmallMemInUse += totalSize;
+ if (SmallMemInUse > SmallMemInUseMax)
+ SmallMemInUseMax = SmallMemInUse;
+ }
+#endif /* MEM_DEBUG */
+
+
+ return ((MemPtr) pb);
+}
+
+
+
+
+/*
+ * Free a block.
+ */
+int
+_MemFree (MemPtr block, char __far *file, int line)
+{
+ MemBlkPtr pBlk;
+ MemHighGuardPtr pHG;
+ HGLOBAL hBlk;
+ HLOCAL hLBlk;
+ BOOL brc;
+ UINT totalSize;
+
+ if (block == NULL)
+ return (0);
+
+
+ /* Find header and high guard and check them. */
+ pBlk = ((MemBlkPtr)block) - 1;
+ pHG = (MemHighGuardPtr) ((char __far *)block + pBlk->size);
+
+ totalSize = pBlk->size + sizeof (MemBlk) + sizeof (MemHighGuard);
+
+ /* If these changed them someone wrote where the should not have. */
+ assert (pBlk->guard0 == GUARD_LOW0);
+ assert (pBlk->guard1 == GUARD_LOW);
+ assert (pHG->guard1 == GUARD_HIGH);
+
+
+
+#ifdef MEM_DEBUG
+ /* Deubgging info... */
+ if (MemDebugLevel >= MEM_DEBUG_LEVEL) {
+ if (pBlk->size <= SMALL_MEM_BOUNDRY &&
+ SmallMemInUse == SmallMemInUseMax)
+ fprintf (MemDebugFile, "Small memory usage is up to %lu\n", SmallMemInUseMax);
+ if (MemInUse == MemInUseMax)
+ fprintf (MemDebugFile, "Memory usage is up to %lu\n", MemInUseMax);
+ }
+ MemInUse -= totalSize;
+ if (pBlk->size <= SMALL_MEM_BOUNDRY)
+ SmallMemInUse -= totalSize;
+ if (MemDebugLevel >= MEM_DEBUG_LEVEL) {
+ fprintf (MemDebugFile, "MemFree: addr(%lx) id(%u)\n",
+ block, pBlk->id);
+ fflush (MemDebugFile);
+ }
+#endif /* MEM_DEBUG */
+
+
+
+ /*
+ * Header indicates which block it came from
+ */
+ if (!pBlk->globalBlk) {
+ /* Unlock block */
+ hLBlk = pBlk->handle;
+ brc = LocalUnlock (hLBlk);
+ assert (!brc);
+
+ /* And free block. */
+ hLBlk = LocalFree (hLBlk);
+ assert (hLBlk == NULL);
+ MemLocalFull = FALSE;
+ }
+ else {
+ /* Unlock block */
+ hBlk = pBlk->handle;
+ brc = GlobalUnlock (hBlk);
+ assert (!brc);
+
+ /* And free block. */
+ hBlk = GlobalFree (hBlk);
+ assert (hBlk == NULL);
+ }
+ return (0);
+}
+
+
+
+
+/*
+ * Reallocate a memory block. Simplistic approach.
+ */
+MemPtr
+_MemRealloc (MemPtr block, MemSize size, char __far * file, int line)
+{
+ MemPtr newBlock;
+
+
+ newBlock = MemAlloc (size);
+ if (newBlock == NULL)
+ return (NULL);
+
+ if (block != NULL) {
+ _fmemcpy (newBlock, block , (size_t)MIN (size, MemBlkSize (block)));
+ MemFree (block);
+ }
+
+ return (newBlock);
+}
+
+
+
+/*
+ * Return the size of a memory block
+ */
+MemSize
+MemBlkSize (MemPtr block)
+{
+ MemBlkPtr pBlk;
+
+ if (block == NULL) return (0);
+ pBlk = ((MemBlkPtr)block) - 1;
+ assert (pBlk->guard1 == GUARD_LOW);
+
+ return (pBlk->size);
+}
+
+
+#ifdef MEM_DEBUG
+struct testblock {
+ struct testblock __far * prev;
+ HLOCAL h;
+};
+
+
+
+void
+MemATest (void)
+{
+ void __near *n;
+ struct testblock __far *p;
+ struct testblock __far *pnew;
+ HLOCAL hl;
+ int bcount;
+ UINT segment, start, end;
+ void __far *f;
+ HGLOBAL hg;
+ DWORD dw;
+ LOCALINFO li;
+ UINT DataSeg;
+
+#if 0
+ hg = GlobalAlloc (GMEM_FIXED, HEAP_SIZE); /* Allocate global block */
+ if (hg == NULL)
+ return;
+ f = GlobalLock (hg); /* Lock and get pointer. */
+ if (f == NULL)
+ goto Fail1;
+ segment = (UINT) GET_SEGMENT (f); /* Get segment and offsets. */
+ start = (UINT) GET_OFFSET (f);
+ end = start + HEAP_SIZE - 1;
+ start += 16;
+ if (!LocalInit (segment, start, end)) /* Init it as the local heap*/
+ goto Fail2;
+#endif
+#if 0
+ __asm MOV DataSeg,DS; /* Get current DS. */
+ __asm MOV DS,segment; /* Set DS to new heap. */
+ hl = LocalAlloc (0, SMALL_MEM_BOUNDRY); /* Now allocate something. */
+ __asm MOV DS,DataSeg; /* Restore DS. */
+ if (hl == NULL)
+ return;
+ n = LocalLock (hl); /* Find where it is. */
+ f = (void __far *)n;
+ segment = GET_SEGMENT(f); /* What Segment. */
+ dw = GlobalHandle (segment);
+ hg = (HGLOBAL) (dw & 0xffff);
+ if (hg == NULL)
+ return;
+
+ li.dwSize = sizeof (li); /* What size. */
+ if (!LocalInfo (&li, hg))
+ return;
+
+ dw = GlobalSize (hg);
+ f = GlobalLock (hg);
+ GlobalUnlock (hg);
+
+ LocalUnlock (hl);
+ LocalFree (hl);
+
+
+#endif
+
+
+
+
+ p = NULL;
+ pnew = NULL;
+ bcount = 0;
+
+ do {
+ hl = LocalAlloc (0, SMALL_MEM_BOUNDRY);
+ if (hl != NULL) {
+ ++bcount;
+ n = LocalLock (hl);
+ pnew = (struct testblock __far*) n;
+ pnew->h = hl;
+ pnew->prev = p;
+ p = pnew;
+ }
+ } while (hl != NULL);
+
+
+ if (MemDebugFile != NULL)
+ fprintf (MemDebugFile, "Allocated %d blocks of size %d\n",
+ bcount, SMALL_MEM_BOUNDRY);
+
+ while (p != NULL) {
+ pnew = p->prev;
+ hl = p->h;
+ LocalUnlock (hl);
+ LocalFree (hl);
+ p = pnew;
+ }
+ fflush (MemDebugFile);
+#if 0
+Fail2: GlobalUnlock (hg);
+Fail1: GlobalFree (hg);
+#endif
+ return;
+}
+#endif /* MEM_DEBUG */
+
+#endif /* ifdef SEGHEAP */
+
+#endif /* MSC_MALLOC */
diff --git a/pico/mswin.def b/pico/mswin.def
new file mode 100644
index 00000000..c62d295d
--- /dev/null
+++ b/pico/mswin.def
@@ -0,0 +1,16 @@
+NAME PICO
+EXETYPE WINDOWS
+DESCRIPTION 'Pico for Windows (Character)'
+STUB 'winstub.exe'
+
+CODE PRELOAD DISCARDABLE SHARED
+DATA PRELOAD MULTIPLE
+
+HEAPSIZE 4096
+
+EXPORTS
+ PWNDPROC @1
+ TWWNDPROC @2
+ ABOUTDLGPROC @3
+
+
diff --git a/pico/mswinver.c b/pico/mswinver.c
new file mode 100644
index 00000000..6c169721
--- /dev/null
+++ b/pico/mswinver.c
@@ -0,0 +1,66 @@
+/*
+ * ========================================================================
+ * Copyright 2006-2009 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#define VER_MAJOR 5
+#define VER_MINOR 5
+extern char datestamp[];
+
+
+/*
+ * Return major version number...
+ */
+int
+mswin_majorver()
+{
+ return(VER_MAJOR);
+}
+
+
+/*
+ * Return minor version number...
+ */
+int
+mswin_minorver()
+{
+ return(VER_MINOR);
+}
+
+
+/*
+ * Return compilation number...
+ */
+char *
+mswin_compilation_date()
+{
+ return(datestamp);
+}
+
+
+/*
+ * Return special remarks...
+ */
+char *
+mswin_compilation_remarks()
+{
+ return("");
+}
+
+/*
+ * Return specific windows version...
+ */
+char *
+mswin_specific_winver()
+{
+ return("");
+}
diff --git a/pico/osdep/Makefile.am b/pico/osdep/Makefile.am
new file mode 100644
index 00000000..85cffef3
--- /dev/null
+++ b/pico/osdep/Makefile.am
@@ -0,0 +1,20 @@
+## Process this file with automake to produce Makefile.in
+## Use aclocal -I m4; automake
+
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+noinst_LIBRARIES = libpicoosd.a
+
+libpicoosd_a_SOURCES = altedit.c chkpoint.c color.c filesys.c fsync.c getkey.c mouse.c \
+ newmail.c popen.c raw.c read.c shell.c signals.c spell.c terminal.c truncate.c tty.c
+
+AM_CPPFLAGS = -I@top_builddir@/include -I@top_srcdir@/include
diff --git a/pico/osdep/Makefile.in b/pico/osdep/Makefile.in
new file mode 100644
index 00000000..65619a10
--- /dev/null
+++ b/pico/osdep/Makefile.in
@@ -0,0 +1,549 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = pico/osdep
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/acx_pthread.m4 \
+ $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
+ $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/VERSION \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/include/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+ARFLAGS = cru
+libpicoosd_a_AR = $(AR) $(ARFLAGS)
+libpicoosd_a_LIBADD =
+am_libpicoosd_a_OBJECTS = altedit.$(OBJEXT) chkpoint.$(OBJEXT) \
+ color.$(OBJEXT) filesys.$(OBJEXT) fsync.$(OBJEXT) \
+ getkey.$(OBJEXT) mouse.$(OBJEXT) newmail.$(OBJEXT) \
+ popen.$(OBJEXT) raw.$(OBJEXT) read.$(OBJEXT) shell.$(OBJEXT) \
+ signals.$(OBJEXT) spell.$(OBJEXT) terminal.$(OBJEXT) \
+ truncate.$(OBJEXT) tty.$(OBJEXT)
+libpicoosd_a_OBJECTS = $(am_libpicoosd_a_OBJECTS)
+DEFAULT_INCLUDES =
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+SOURCES = $(libpicoosd_a_SOURCES)
+DIST_SOURCES = $(libpicoosd_a_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_CFLAGS = @AM_CFLAGS@
+AM_LDFLAGS = @AM_LDFLAGS@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CP = @CP@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+C_CLIENT_CFLAGS = @C_CLIENT_CFLAGS@
+C_CLIENT_GCCOPTLEVEL = @C_CLIENT_GCCOPTLEVEL@
+C_CLIENT_LDFLAGS = @C_CLIENT_LDFLAGS@
+C_CLIENT_SPECIALS = @C_CLIENT_SPECIALS@
+C_CLIENT_TARGET = @C_CLIENT_TARGET@
+C_CLIENT_WITH_IPV6 = @C_CLIENT_WITH_IPV6@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISPELLPROG = @ISPELLPROG@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN = @LN@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKE = @MAKE@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NPA_PROG = @NPA_PROG@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+POSUB = @POSUB@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+PWPROG = @PWPROG@
+RANLIB = @RANLIB@
+REGEX_BUILD = @REGEX_BUILD@
+RM = @RM@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPELLPROG = @SPELLPROG@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEB_BINDIR = @WEB_BINDIR@
+WEB_BUILD = @WEB_BUILD@
+WEB_PUBCOOKIE_BUILD = @WEB_PUBCOOKIE_BUILD@
+WEB_PUBCOOKIE_LIB = @WEB_PUBCOOKIE_LIB@
+WEB_PUBCOOKIE_LINK = @WEB_PUBCOOKIE_LINK@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+acx_pthread_config = @acx_pthread_config@
+alpine_interactive_spellcheck = @alpine_interactive_spellcheck@
+alpine_simple_spellcheck = @alpine_simple_spellcheck@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LIBRARIES = libpicoosd.a
+libpicoosd_a_SOURCES = altedit.c chkpoint.c color.c filesys.c fsync.c getkey.c mouse.c \
+ newmail.c popen.c raw.c read.c shell.c signals.c spell.c terminal.c truncate.c tty.c
+
+AM_CPPFLAGS = -I@top_builddir@/include -I@top_srcdir@/include
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign pico/osdep/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign pico/osdep/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLIBRARIES:
+ -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+libpicoosd.a: $(libpicoosd_a_OBJECTS) $(libpicoosd_a_DEPENDENCIES)
+ -rm -f libpicoosd.a
+ $(libpicoosd_a_AR) libpicoosd.a $(libpicoosd_a_OBJECTS) $(libpicoosd_a_LIBADD)
+ $(RANLIB) libpicoosd.a
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/altedit.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chkpoint.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/color.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filesys.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fsync.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getkey.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mouse.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/newmail.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/popen.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/raw.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/read.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/shell.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/signals.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/spell.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/terminal.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/truncate.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tty.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LIBRARIES)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libtool clean-noinstLIBRARIES ctags distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags uninstall uninstall-am
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pico/osdep/altedit.c b/pico/osdep/altedit.c
new file mode 100644
index 00000000..934ed016
--- /dev/null
+++ b/pico/osdep/altedit.c
@@ -0,0 +1,712 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: altedit.c 854 2007-12-07 17:44:43Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#include <system.h>
+#include <general.h>
+
+#include "../../pith/osdep/pipe.h"
+#include "../../pith/osdep/forkwait.h"
+#include "../../pith/charconv/filesys.h"
+
+#include "../estruct.h"
+#include "../mode.h"
+#include "../pico.h"
+#include "../edef.h"
+#include "../efunc.h"
+#include "../keydefs.h"
+#include "../utf8stub.h"
+#include "tty.h"
+#include "filesys.h"
+
+#ifdef _WINDOWS
+#include "mswin.h"
+#endif
+
+#include "altedit.h"
+#ifndef _WINDOWS
+#include "signals.h"
+
+#ifdef SIGCHLD
+static jmp_buf pico_child_state;
+static short pico_child_jmp_ok, pico_child_done;
+#endif /* SIGCHLD */
+
+#endif /* !_WINDOWS */
+
+
+/* internal prototypes */
+#ifndef _WINDOWS
+#ifdef SIGCHLD
+RETSIGTYPE child_handler(int);
+#endif /* SIGCHLD */
+#else /* _WINDOWS */
+int alt_editor_valid(char *, char *, size_t);
+int alt_editor_valid_fp(char *);
+#endif /* _WINDOWS */
+
+
+/*
+ * alt_editor - fork off an alternate editor for mail message composition
+ * if one is configured and passed from pine. If not, only
+ * ask for the editor if advanced user flag is set, and
+ * suggest environment's EDITOR value as default.
+ */
+int
+alt_editor(int f, int n)
+{
+#ifndef _WINDOWS
+ char eb[NLINE]; /* buf holding edit command */
+ char *fn; /* tmp holder for file name */
+ char result[128]; /* result string */
+ char prmpt[128];
+ int i, done = 0, ret = 0, rv, trv;
+ pid_t child, pid;
+ RETSIGTYPE (*ohup)(int);
+ RETSIGTYPE (*oint)(int);
+ RETSIGTYPE (*osize)(int);
+ int status;
+ EML eml;
+
+ eml.s = f ? "speller" : "editor";
+
+ if(gmode&MDSCUR){
+ emlwrite("Alternate %s not available in restricted mode", &eml);
+ return(-1);
+ }
+
+ strncpy(result, "Alternate %s complete.", sizeof(result));
+ result[sizeof(result)-1] = '\0';
+
+ if(f){
+ if(alt_speller){
+ strncpy(eb, alt_speller, sizeof(eb));
+ eb[sizeof(eb)-1] = '\0';
+ }
+ else
+ return(-1);
+ }
+ else if(Pmaster == NULL){
+ return(-1);
+ }
+ else{
+ eb[0] = '\0';
+
+ if(Pmaster->alt_ed){
+ char **lp, *wsp, *path, fname[MAXPATH+1];
+ int c;
+
+ for(lp = Pmaster->alt_ed; *lp && **lp; lp++){
+ if((wsp = strpbrk(*lp, " \t")) != NULL){
+ c = *wsp;
+ *wsp = '\0';
+ }
+
+ if(strchr(*lp, '/')){
+ rv = fexist(*lp, "x", (off_t *)NULL);
+ }
+ else{
+ if(!(path = getenv("PATH")))
+ path = ":/bin:/usr/bin";
+
+ rv = ~FIOSUC;
+ while(rv != FIOSUC && *path && pathcat(fname, &path, *lp))
+ rv = fexist(fname, "x", (off_t *)NULL);
+ }
+
+ if(wsp)
+ *wsp = c;
+
+ if(rv == FIOSUC){
+ strncpy(eb, *lp, sizeof(eb));
+ eb[sizeof(eb)-1] = '\0';
+ break;
+ }
+ }
+ }
+
+ if(!eb[0]){
+ if(!(gmode&MDADVN)){
+ unknown_command(0);
+ return(-1);
+ }
+
+ if(getenv("EDITOR")){
+ strncpy(eb, (char *)getenv("EDITOR"), sizeof(eb));
+ eb[sizeof(eb)-1] = '\0';
+ }
+ else
+ *eb = '\0';
+
+ while(!done){
+ pid = mlreplyd_utf8("Which alternate editor ? ", eb,
+ sizeof(eb), QDEFLT, NULL);
+ switch(pid){
+ case ABORT:
+ curwp->w_flag |= WFMODE;
+ return(-1);
+ case HELPCH:
+ emlwrite("no alternate editor help yet", NULL);
+
+/* take sleep and break out after there's help */
+ sleep(3);
+ break;
+ case (CTRL|'L'):
+ sgarbf = TRUE;
+ update();
+ break;
+ case TRUE:
+ case FALSE: /* does editor exist ? */
+ if(*eb == '\0'){ /* leave silently? */
+ mlerase();
+ curwp->w_flag |= WFMODE;
+ return(-1);
+ }
+
+ done++;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ if((fn=writetmp(1, NULL)) == NULL){ /* get temp file */
+ emlwrite("Problem writing temp file for alt editor", NULL);
+ return(-1);
+ }
+
+ strncat(eb, " ", sizeof(eb)-strlen(eb)-1);
+ eb[sizeof(eb)-1] = '\0';
+ strncat(eb, fn, sizeof(eb)-strlen(eb)-1);
+ eb[sizeof(eb)-1] = '\0';
+
+
+ for(i = 0; i <= ((Pmaster) ? term.t_nrow : term.t_nrow - 1); i++){
+ movecursor(i, 0);
+ if(!i){
+ fputs("Invoking alternate ", stdout);
+ fputs(f ? "speller..." : "editor...", stdout);
+ }
+
+ peeol();
+ }
+
+ (*term.t_flush)();
+ if(Pmaster)
+ (*Pmaster->tty_fix)(0);
+ else
+ vttidy();
+
+#ifdef SIGCHLD
+ if(Pmaster){
+ /*
+ * The idea here is to keep any mail connections our caller
+ * may have open while our child's out running around...
+ */
+ pico_child_done = pico_child_jmp_ok = 0;
+ (void) signal(SIGCHLD, child_handler);
+ }
+#endif
+
+ if((child = fork()) > 0){ /* wait for the child to finish */
+ ohup = signal(SIGHUP, SIG_IGN); /* ignore signals for now */
+ oint = signal(SIGINT, SIG_IGN);
+#if defined(SIGWINCH) && defined(TIOCGWINSZ)
+ osize = signal(SIGWINCH, SIG_IGN);
+#endif
+
+#ifdef SIGCHLD
+ if(Pmaster){
+ while(!pico_child_done){
+ (*Pmaster->newmail)(0, 0);
+ if(!pico_child_done){
+ if(setjmp(pico_child_state) == 0){
+ pico_child_jmp_ok = 1;
+ sleep(600);
+ }
+ else
+ our_sigunblock(SIGCHLD);
+ }
+
+ pico_child_jmp_ok = 0;
+ }
+ }
+#endif
+
+ trv = process_reap(child, &status, PR_NONE);
+
+ signal(SIGHUP, ohup); /* restore signals */
+ signal(SIGINT, oint);
+#if defined(SIGWINCH) && defined(TIOCGWINSZ)
+ signal(SIGWINCH, osize);
+#endif
+
+ /*
+ * Report child's unnatural or unhappy exit...
+ */
+ if(trv > 0 && status == 0){
+ strncpy(result, "Alternate %s done", sizeof(result));
+ result[sizeof(result)-1] = '\0';
+ }
+ else {
+ snprintf(result, sizeof(result), "Alternate %%s terminated abnormally (%d)",
+ (trv == 0) ? status : -1);
+ if(f)
+ ret = -1;
+ else{
+ snprintf(prmpt, sizeof(prmpt), "Alt editor failed, use file %.20s of size %%ld chars anyway", fn);
+ ret = -2;
+ }
+ }
+ }
+ else if(child == 0){ /* spawn editor */
+ signal(SIGHUP, SIG_DFL); /* let editor handle signals */
+ signal(SIGINT, SIG_DFL);
+#if defined(SIGWINCH) && defined(TIOCGWINSZ)
+ signal(SIGWINCH, SIG_DFL);
+#endif
+#ifdef SIGCHLD
+ (void) signal(SIGCHLD, SIG_DFL);
+#endif
+ if(execl("/bin/sh", "sh", "-c", fname_to_locale(eb), (char *) NULL) < 0)
+ exit(-1);
+ }
+ else { /* error! */
+ snprintf(result, sizeof(result), "\007Can't fork %%s: %s", errstr(errno));
+ ret = -1;
+ }
+
+#ifdef SIGCHLD
+ (void) signal(SIGCHLD, SIG_DFL);
+#endif
+
+ if(Pmaster)
+ (*Pmaster->tty_fix)(1);
+
+ /*
+ * replace edited text with new text
+ */
+ curbp->b_flag &= ~BFCHG; /* make sure old text gets blasted */
+
+ if(ret == -2){
+ off_t filesize;
+ char prompt[128];
+
+ rv = fexist(fn, "r", &filesize);
+ if(rv == FIOSUC && filesize > 0){
+ snprintf(prompt, sizeof(prompt), prmpt, (long) filesize);
+ /* clear bottom 3 rows */
+ pclear(term.t_nrow-2, term.t_nrow);
+ i = mlyesno_utf8(prompt, FALSE);
+ if(i == TRUE){
+ ret = 0;
+ strncpy(result, "OK, alternate %s done", sizeof(result));
+ result[sizeof(result)-1] = '\0';
+ }
+ else
+ ret = -1;
+ }
+ else
+ ret = -1;
+ }
+
+ if(ret == 0)
+ readin(fn, 0, 0); /* read new text overwriting old */
+
+ our_unlink(fn); /* blast temp file */
+ curbp->b_flag |= BFCHG; /* mark dirty for packbuf() */
+ ttopen(); /* reset the signals */
+ pico_refresh(0, 1); /* redraw */
+ update();
+ emlwrite(result, &eml);
+ return(ret);
+#else /* _WINDOWS */
+ char eb[2 * PATH_MAX]; /* buf holding edit command */
+ char *fn; /* tmp holder for file name */
+ char errbuf[128];
+ char **lp;
+ int status;
+ int done;
+ int rc;
+
+
+ if(Pmaster == NULL)
+ return(-1);
+
+ if(gmode&MDSCUR){
+ emlwrite("Alternate editor not available in restricted mode", NULL);
+ return(-1);
+ }
+
+ eb[0] = '\0';
+
+ if(Pmaster->alt_ed){
+ char *p;
+
+ for(lp = Pmaster->alt_ed; *lp && **lp; lp++)
+ if(*lp[0] == '\"'){
+ if(p = strchr(*lp + 1, '\"')){
+ *p = '\0';
+ rc = alt_editor_valid((*lp) + 1, eb, sizeof(eb));
+ *p = '\"';
+ if(rc){
+ strncat(eb, p + 1, sizeof(eb)-strlen(eb)-1);
+ eb[sizeof(eb)-1] = '\0';
+ break;
+ }
+ }
+ }
+ else if(p = strchr(*lp, ' ')){
+ for(rc = 0; !rc; ){
+ if(p)
+ *p = '\0';
+
+ rc = alt_editor_valid(*lp, eb, sizeof(eb));
+
+ if(p)
+ *p = ' ';
+
+ if(rc){
+ if(p){
+ strncat(eb, p, sizeof(eb)-strlen(eb)-1);
+ eb[sizeof(eb)-1] = '\0';
+ }
+
+ break;
+ }
+ else if(p)
+ p = strchr(p + 1, ' ');
+ else
+ break;
+ }
+
+ if(rc)
+ break;
+ }
+ else if(alt_editor_valid(*lp, eb, sizeof(eb)))
+ break;
+ }
+
+ if(!eb[0]){
+ if(!(gmode&MDADVN)){
+ unknown_command(0);
+ return(-1);
+ }
+
+ /* Guess which editor they want. */
+ if(getenv("EDITOR")){
+ strncpy(eb, (char *)getenv("EDITOR"), sizeof(eb));
+ eb[sizeof(eb)-1] = '\0';
+ }
+ else
+ *eb = '\0';
+
+ done = FALSE;
+ while(!done){
+ rc = mlreplyd_utf8("Which alternate editor ? ", eb,
+ sizeof(eb),QDEFLT,NULL);
+
+ switch(rc){
+ case ABORT:
+ curwp->w_flag |= WFMODE;
+ return(-1);
+ case HELPCH:
+ emlwrite("no alternate editor help yet", NULL);
+
+/* take sleep and break out after there's help */
+ sleep(3);
+ break;
+ case (CTRL|'L'):
+ sgarbf = TRUE;
+ update();
+ break;
+ case TRUE:
+ case FALSE: /* does editor exist ? */
+ if(*eb == '\0'){ /* leave silently? */
+ mlerase();
+ curwp->w_flag |= WFMODE;
+ return(-1);
+ }
+
+ done++;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if((fn=writetmp(1, NULL)) == NULL){ /* get temp file */
+ emlwrite("Problem writing temp file for alt editor", NULL);
+ return(-1);
+ }
+
+ emlwrite("Waiting for alternate editor to finish...", NULL);
+
+#ifdef ALTED_DOT
+ if(eb[0] == '.' && eb[1] == '\0') {
+ status = mswin_exec_and_wait ("alternate editor", eb, fn, fn,
+ NULL, 0);
+ }
+ else
+#endif /* ALTED_DOT */
+ { /* just here in case ALTED_DOT is defined */
+ strncat(eb, " ", sizeof(eb)-strlen(eb)-1);
+ eb[sizeof(eb)-1] = '\0';
+ strncat(eb, fn, sizeof(eb)-strlen(eb)-1);
+ eb[sizeof(eb)-1] = '\0';
+
+ status = mswin_exec_and_wait ("alternate editor", eb, NULL, NULL,
+ NULL, 0);
+ }
+
+ switch (status) {
+
+ case 0:
+ /*
+ * Success: replace edited text with new text
+ */
+ curbp->b_flag &= ~BFCHG; /* make sure old text gets blasted */
+ readin(fn, 0, 0); /* read new text overwriting old */
+ our_unlink(fn); /* blast temp file */
+ curbp->b_flag |= BFCHG; /* mark dirty for packbuf() */
+ ttopen(); /* reset the signals */
+ pico_refresh(0, 1); /* redraw */
+ return(0);
+
+
+ /*
+ * Possible errors.
+ */
+ case -1:
+ /* Failed to map return from WinExec to a HTASK. */
+ emlwrite("Problem finding alternate editor task handle.", NULL);
+ return (-1);
+
+ case -2:
+ /* User decided to abandon the alternate editor.*/
+ emlwrite("Alternate editor abandoned.", NULL);
+ return (-1);
+
+ default:
+ mswin_exec_err_msg ("alternate editor", status, errbuf, sizeof(errbuf));
+ emlwrite (errbuf, NULL);
+ return (-1);
+ }
+ return (-1);
+#endif /*_WINDOWS */
+}
+
+
+
+#ifndef _WINDOWS
+int
+pathcat(char *buf, char **path, char *file)
+{
+ register int n = 0;
+
+ while(**path && **path != ':'){
+ if(n++ > MAXPATH)
+ return(FALSE);
+
+ *buf++ = *(*path)++;
+ }
+
+ if(n){
+ if(n++ > MAXPATH)
+ return(FALSE);
+
+ *buf++ = '/';
+ }
+
+ while((*buf = *file++) != '\0'){
+ if(n++ > MAXPATH)
+ return(FALSE);
+
+ buf++;
+ }
+
+ if(**path == ':')
+ (*path)++;
+
+ return(TRUE);
+}
+
+
+#ifdef SIGCHLD
+/*
+ * child_handler - handle child status change signal
+ */
+RETSIGTYPE
+child_handler(int sig)
+{
+ pico_child_done = 1;
+ if(pico_child_jmp_ok){
+#ifdef sco
+ /*
+ * Villy Kruse <vek@twincom.nl> says:
+ * This probably only affects older unix systems with a "broken" sleep
+ * function such as AT&T System V rel 3.2 and systems derived from
+ * that version. The problem is that the sleep function on these
+ * versions of unix will set up a signal handler for SIGALRM which
+ * will longjmp into the sleep function when the alarm goes off.
+ * This gives problems if another signal handler longjmps out of the
+ * sleep function, thus making the stack frame for the signal function
+ * invalid, and when the ALARM handler later longjmps back into the
+ * sleep function it does no longer have a valid stack frame.
+ * My sugested fix is to cancel the pending alarm in the SIGCHLD
+ * handler before longjmp'ing. This shouldn't hurt as there
+ * shouldn't be any ALARM pending at this point except possibly from
+ * the sleep call.
+ *
+ *
+ * [Even though it shouldn't hurt, we'll make it sco-specific for now.]
+ *
+ *
+ * The sleep call might have set up a signal handler which would
+ * longjmp back into the sleep code, and that would cause a crash.
+ */
+ signal(SIGALRM, SIG_IGN); /* Cancel signal handeler */
+ alarm(0); /* might longjmp back into sleep */
+#endif
+ longjmp(pico_child_state, 1);
+ }
+}
+#endif /* SIGCHLD */
+
+#else /* _WINDOWS */
+/*
+ * alt_editor_valid -- test the given exe name exists, and if so
+ * return full name in "cmdbuf".
+ */
+int
+alt_editor_valid(char *exe, char *cmdbuf, size_t ncmdbuf)
+{
+
+/****
+ **** This isn't the right way to do this. I believe we
+ **** should be doing all of this in TCHARs starting in
+ **** the alt_editor function instead of trying to convert
+ **** to char * here.
+ ****/
+
+ if(!exe)
+ return(FALSE);
+
+#ifdef ALTED_DOT
+ if(exe[0] == '.' && exe[1] == '\0') {
+ cmdbuf[0] = '.';
+ cmdbuf[1] = '\0';
+ return(TRUE);
+ }
+ else
+#endif /* ALTED_DOT */
+
+ if((isalpha((unsigned char) *exe) && *exe == ':') || strchr(exe, '\\')){
+ char *path;
+
+ if(alt_editor_valid_fp(path = exe)){
+ if(strchr(path, ' '))
+ snprintf(cmdbuf, ncmdbuf, "\"%s\"", path);
+ else{
+ strncpy(cmdbuf, path, ncmdbuf);
+ cmdbuf[ncmdbuf-1] = '\0';
+ }
+
+ return(TRUE);
+ }
+ }
+ else{
+ TCHAR pathbuflpt[PATH_MAX+1];
+ LPTSTR name, exelpt;
+ LPTSTR cmdbuflpt;
+ char *utf8;
+
+ cmdbuflpt = (LPTSTR) MemAlloc(ncmdbuf * sizeof(TCHAR));
+ cmdbuflpt[0] = L'0';
+
+ exelpt = utf8_to_lptstr(exe);
+
+ if(SearchPath(NULL, exelpt, NULL, PATH_MAX+1, pathbuflpt, &name)){
+
+ if(_tcschr(pathbuflpt, L' '))
+ _stprintf_s(cmdbuflpt, ncmdbuf, TEXT("\"%s\""), pathbuflpt);
+ else
+ _stprintf_s(cmdbuflpt, ncmdbuf, TEXT("%s"), pathbuflpt);
+
+ if(exelpt)
+ fs_give((void **) &exelpt);
+
+ utf8 = lptstr_to_utf8(cmdbuflpt);
+ if(utf8){
+ strncpy(cmdbuf, utf8, ncmdbuf);
+ cmdbuf[ncmdbuf-1] = '\0';
+ fs_give((void **) &utf8);
+ }
+
+ if(cmdbuflpt)
+ MemFree((void *) cmdbuflpt);
+
+ return(TRUE);
+ }
+
+ if(exelpt)
+ fs_give((void **) &exelpt);
+
+ if(cmdbuflpt)
+ MemFree((void *) cmdbuflpt);
+ }
+
+ return(FALSE);
+}
+
+
+/*
+ * alt_editor_valid_fp -- test the given full path name exists
+ */
+int
+alt_editor_valid_fp(path)
+ char *path;
+{
+ static char *exts[] = {".exe", ".com", ".bat", NULL};
+ char pathcopy[PATH_MAX + 1], *dot = NULL;
+ int i, j;
+
+ for(i = 0; pathcopy[i] = path[i]; i++)
+ if(path[i] == '.')
+ dot = &path[i];
+
+ if(dot && (!strucmp(dot, ".exe")
+ || !strucmp(dot, ".com") || !strucmp(dot, ".bat"))){
+ if(fexist(path, "x", (off_t *) NULL) == FIOSUC)
+ return(TRUE);
+ }
+ else{
+ for(j = 0; exts[j]; j++){
+ strncpy(&pathcopy[i], exts[j], sizeof(pathcopy)-i);
+ pathcopy[sizeof(pathcopy)-1] = '\0';
+ if(fexist(pathcopy, "x", (off_t *) NULL) == FIOSUC)
+ return(TRUE);
+ }
+ }
+
+ return(FALSE);
+}
+#endif
diff --git a/pico/osdep/altedit.h b/pico/osdep/altedit.h
new file mode 100644
index 00000000..7e592f6f
--- /dev/null
+++ b/pico/osdep/altedit.h
@@ -0,0 +1,29 @@
+/*
+ * $Id: altedit.h 769 2007-10-24 00:15:40Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PICO_OSDEP_ALTEDIT_INCLUDED
+#define PICO_OSDEP_ALTEDIT_INCLUDED
+
+
+
+/* exported prototypes */
+int alt_editor(int, int);
+#ifndef _WINDOWS
+int pathcat(char *, char **, char *);
+#endif
+
+
+#endif /* PICO_OSDEP_ALTEDIT_INCLUDED */
diff --git a/pico/osdep/chkpoint.c b/pico/osdep/chkpoint.c
new file mode 100644
index 00000000..235cf83b
--- /dev/null
+++ b/pico/osdep/chkpoint.c
@@ -0,0 +1,98 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: chkpoint.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#include <system.h>
+#include <general.h>
+
+#include "../estruct.h"
+#include "../mode.h"
+#include "../pico.h"
+#include "../edef.h"
+#include "../efunc.h"
+#include "../keydefs.h"
+#include "filesys.h"
+
+#include "../../pith/charconv/filesys.h"
+
+#include "chkpoint.h"
+
+
+/*
+ * chkptinit -- initialize anything we need to support composer
+ * checkpointing
+ */
+void
+chkptinit(char *file, size_t filelen)
+{
+#ifndef _WINDOWS
+ unsigned pid;
+ char *chp;
+#endif
+
+ if(!file[0]){
+ long gmode_save = gmode;
+
+ if(gmode&MDCURDIR)
+ gmode &= ~MDCURDIR; /* so fixpath will use home dir */
+
+#ifndef _WINDOWS
+ strncpy(file, "#picoXXXXX#", filelen);
+#else
+ strncpy(file, "#picoTM0.txt", filelen);
+#endif
+ file[filelen-1] = '\0';
+ fixpath(file, filelen);
+ gmode = gmode_save;
+ }
+ else{
+ size_t l = strlen(file);
+
+ if(l+2 <= filelen && file[l-1] != C_FILESEP){
+ file[l++] = C_FILESEP;
+ file[l] = '\0';
+ }
+
+#ifndef _WINDOWS
+ strncpy(file + l, "#picoXXXXX#", filelen-l);
+#else
+ strncpy(file + l, "#picoTM0.txt", filelen-l);
+#endif
+ file[filelen-1] = '\0';
+ }
+
+#ifndef _WINDOWS
+ pid = (unsigned)getpid();
+ for(chp = file+strlen(file) - 2; *chp == 'X'; chp--){
+ *chp = (pid % 10) + '0';
+ pid /= 10;
+ }
+#else
+ if(fexist(file, "r", (off_t *)NULL) == FIOSUC){ /* does file exist? */
+ char copy[NLINE];
+
+ strncpy(copy, "#picoTM1.txt", sizeof(copy));
+ copy[sizeof(copy)-1] = '\0';
+ fixpath(copy, sizeof(copy));
+ our_rename(file, copy); /* save so we don't overwrite it */
+ }
+#endif /* _WINDOWS */
+
+ our_unlink(file);
+}
+
diff --git a/pico/osdep/chkpoint.h b/pico/osdep/chkpoint.h
new file mode 100644
index 00000000..66ca1baf
--- /dev/null
+++ b/pico/osdep/chkpoint.h
@@ -0,0 +1,24 @@
+/*
+ * $Id: chkpoint.h 776 2007-10-24 23:14:26Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PICO_OSDEP_CHKPOINT_INCLUDED
+#define PICO_OSDEP_CHKPOINT_INCLUDED
+
+
+/* exported prototypes */
+void chkptinit(char *, size_t);
+
+
+#endif /* PICO_OSDEP_CHKPOINT_INCLUDED */
diff --git a/pico/osdep/color.c b/pico/osdep/color.c
new file mode 100644
index 00000000..5a9d3710
--- /dev/null
+++ b/pico/osdep/color.c
@@ -0,0 +1,1797 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: color.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#include <system.h>
+#include <general.h>
+
+#include "../headers.h"
+
+#include "terminal.h"
+#include "color.h"
+#include "../../pith/osdep/color.h"
+#include "../../pith/osdep/collate.h"
+
+#ifndef _WINDOWS
+
+struct color_name_list {
+ char *name;
+ int namelen;
+ struct color_name_list *next;
+};
+
+struct color_table {
+ struct color_name_list *names;
+ char *rgb;
+ int red, green, blue;
+ int val;
+};
+
+
+/* useful definitions */
+#define ANSI8_COLOR() (color_options & COLOR_ANSI8_OPT)
+#define ANSI16_COLOR() (color_options & COLOR_ANSI16_OPT)
+#define ANSI256_COLOR() (color_options & COLOR_ANSI256_OPT)
+#define ANSI_COLOR() (color_options & (COLOR_ANSI8_OPT | COLOR_ANSI16_OPT | COLOR_ANSI256_OPT))
+/* transparent (default) color is possible */
+#define COL_TRANS() ((color_options & COLOR_TRANS_OPT) ? 1 : 0) /* transparent */
+#define END_PSEUDO_REVERSE "EndInverse"
+#define A_UNKNOWN -1
+
+
+/* local globals */
+static unsigned color_options;
+static COLOR_PAIR *the_rev_color, *the_normal_color;
+static COLOR_PAIR *color_blasted_by_attrs;
+static int pinvstate; /* physical state of inverse (standout) attribute */
+static int pboldstate; /* physical state of bold attribute */
+static int pulstate; /* physical state of underline attribute */
+static int rev_color_state;
+
+static int boldstate; /* should-be state of bold attribute */
+static int ulstate; /* should-be state of underline attribute */
+static int invstate; /* should-be state of Inverse, which could be a color
+ change or an actual setinverse */
+static int _color_inited, _using_color;
+static char *_nfcolor, *_nbcolor, *_rfcolor, *_rbcolor;
+static char *_last_fg_color, *_last_bg_color;
+static int _force_fg_color_change;
+static int _force_bg_color_change;
+
+static struct color_table *color_tbl;
+
+/* external references */
+extern char *_op, *_oc, *_setaf, *_setab, *_setf, *_setb, *_scp;
+extern int _bce, _colors;
+
+
+/* internal prototypes */
+void flip_rev_color(int);
+void flip_bold(int);
+void flip_ul(int);
+void reset_attr_state(void);
+void add_to_color_name_list(struct color_table *t, char *name);
+void free_color_name_list(struct color_name_list **nl);
+
+
+#if HAS_TERMINFO
+extern char *tparm(char *, ...);
+#endif
+extern char *tgoto();
+void tinitcolor(void);
+int tfgcolor(int);
+int tbgcolor(int);
+struct color_table *init_color_table(void);
+void free_color_table(struct color_table **);
+int color_to_val(char *);
+
+
+
+/*
+ * Start or end bold mode
+ *
+ * Result: escape sequence to go into or out of reverse color is output
+ *
+ * (This is only called when there is a reverse color.)
+ *
+ * Arg state = ON set bold
+ * OFF set normal
+ */
+void
+flip_rev_color(int state)
+{
+ if((rev_color_state = state) == TRUE)
+ (void)pico_set_colorp(the_rev_color, PSC_NONE);
+ else
+ pico_set_normal_color();
+}
+
+
+/*
+ * Start or end bold mode
+ *
+ * Result: escape sequence to go into or out of bold is output
+ *
+ * Arg state = ON set bold
+ * OFF set normal
+ */
+void
+flip_bold(int state)
+{
+ extern char *_setbold, *_clearallattr;
+
+ if((pboldstate = state) == TRUE){
+ if(_setbold != NULL)
+ putpad(_setbold);
+ }
+ else{
+ if(_clearallattr != NULL){
+ if(!color_blasted_by_attrs)
+ color_blasted_by_attrs = pico_get_cur_color();
+
+ _force_fg_color_change = _force_bg_color_change = 1;
+ putpad(_clearallattr);
+ pinvstate = state;
+ pulstate = state;
+ rev_color_state = state;
+ }
+ }
+}
+
+
+/*
+ * Start or end inverse mode
+ *
+ * Result: escape sequence to go into or out of inverse is output
+ *
+ * Arg state = ON set inverse
+ * OFF set normal
+ */
+void
+flip_inv(int state)
+{
+ extern char *_setinverse, *_clearinverse;
+
+ if((pinvstate = state) == TRUE){
+ if(_setinverse != NULL)
+ putpad(_setinverse);
+ }
+ else{
+ /*
+ * Unfortunately, some termcap entries configure end standout to
+ * be clear all attributes.
+ */
+ if(_clearinverse != NULL){
+ if(!color_blasted_by_attrs)
+ color_blasted_by_attrs = pico_get_cur_color();
+
+ _force_fg_color_change = _force_bg_color_change = 1;
+ putpad(_clearinverse);
+ pboldstate = (pboldstate == FALSE) ? pboldstate : A_UNKNOWN;
+ pulstate = (pulstate == FALSE) ? pulstate : A_UNKNOWN;
+ rev_color_state = A_UNKNOWN;
+ }
+ }
+}
+
+
+/*
+ * Start or end underline mode
+ *
+ * Result: escape sequence to go into or out of underline is output
+ *
+ * Arg state = ON set underline
+ * OFF set normal
+ */
+void
+flip_ul(int state)
+{
+ extern char *_setunderline, *_clearunderline;
+
+ if((pulstate = state) == TRUE){
+ if(_setunderline != NULL)
+ putpad(_setunderline);
+ }
+ else{
+ /*
+ * Unfortunately, some termcap entries configure end underline to
+ * be clear all attributes.
+ */
+ if(_clearunderline != NULL){
+ if(!color_blasted_by_attrs)
+ color_blasted_by_attrs = pico_get_cur_color();
+
+ _force_fg_color_change = _force_bg_color_change = 1;
+ putpad(_clearunderline);
+ pboldstate = (pboldstate == FALSE) ? pboldstate : A_UNKNOWN;
+ pinvstate = (pinvstate == FALSE) ? pinvstate : A_UNKNOWN;
+ rev_color_state = A_UNKNOWN;
+ }
+ }
+}
+
+
+void
+StartInverse(void)
+{
+ invstate = TRUE;
+ reset_attr_state();
+}
+
+
+void
+EndInverse(void)
+{
+ invstate = FALSE;
+ reset_attr_state();
+}
+
+
+int
+InverseState(void)
+{
+ return(invstate);
+}
+
+
+int
+StartBold(void)
+{
+ extern char *_setbold;
+
+ boldstate = TRUE;
+ reset_attr_state();
+ return(_setbold != NULL);
+}
+
+
+void
+EndBold(void)
+{
+ boldstate = FALSE;
+ reset_attr_state();
+}
+
+
+void
+StartUnderline(void)
+{
+ ulstate = TRUE;
+ reset_attr_state();
+}
+
+
+void
+EndUnderline(void)
+{
+ ulstate = FALSE;
+ reset_attr_state();
+}
+
+
+void
+reset_attr_state(void)
+{
+ /*
+ * If we have to turn some attributes off, do that first since that
+ * may turn off other attributes as a side effect.
+ */
+ if(boldstate == FALSE && pboldstate != boldstate)
+ flip_bold(boldstate);
+
+ if(ulstate == FALSE && pulstate != ulstate)
+ flip_ul(ulstate);
+
+ if(invstate == FALSE){
+ if(pico_get_rev_color()){
+ if(rev_color_state != invstate)
+ flip_rev_color(invstate);
+ }
+ else{
+ if(pinvstate != invstate)
+ flip_inv(invstate);
+ }
+ }
+
+ /*
+ * Now turn everything on that needs turning on.
+ */
+ if(boldstate == TRUE && pboldstate != boldstate)
+ flip_bold(boldstate);
+
+ if(ulstate == TRUE && pulstate != ulstate)
+ flip_ul(ulstate);
+
+ if(invstate == TRUE){
+ if(pico_get_rev_color()){
+ if(rev_color_state != invstate)
+ flip_rev_color(invstate);
+ }
+ else{
+ if(pinvstate != invstate)
+ flip_inv(invstate);
+ }
+ }
+
+ if(color_blasted_by_attrs){
+ (void)pico_set_colorp(color_blasted_by_attrs, PSC_NONE);
+ free_color_pair(&color_blasted_by_attrs);
+ }
+
+}
+
+
+void
+tinitcolor(void)
+{
+ if(_color_inited || panicking())
+ return;
+
+ if(ANSI_COLOR() || (_colors > 0 && ((_setaf && _setab) || (_setf && _setb)
+/**** not sure how to do this yet
+ || _scp
+****/
+ ))){
+ _color_inited = 1;
+ color_tbl = init_color_table();
+
+ if(ANSI_COLOR())
+ putpad("\033[39;49m");
+ else{
+ if(_op)
+ putpad(_op);
+ if(_oc)
+ putpad(_oc);
+ }
+ }
+}
+
+
+#if HAS_TERMCAP
+/*
+ * Treading on thin ice here. Tgoto wasn't designed for this. It takes
+ * arguments column and row. We only use one of them, so we put it in
+ * the row argument. The 0 is just a placeholder.
+ */
+#define tparm(s, c) tgoto(s, 0, c)
+#endif
+
+int
+tfgcolor(int color)
+{
+ if(!_color_inited)
+ tinitcolor();
+
+ if(!_color_inited)
+ return(-1);
+
+ if((ANSI8_COLOR() && (color < 0 || color >= 8+COL_TRANS())) ||
+ (ANSI16_COLOR() && (color < 0 || color >= 16+COL_TRANS())) ||
+ (ANSI256_COLOR() && (color < 0 || color >= 256+COL_TRANS())) ||
+ (!ANSI_COLOR() && (color < 0 || color >= _colors+COL_TRANS())))
+ return(-1);
+
+ if(ANSI_COLOR()){
+ char buf[20];
+
+ if(COL_TRANS() && color == pico_count_in_color_table()-1)
+ snprintf(buf, sizeof(buf), "\033[39m");
+ else if(ANSI256_COLOR())
+ snprintf(buf, sizeof(buf), "\033[38;5;%dm", color);
+ else{
+ if(color < 8)
+ snprintf(buf, sizeof(buf), "\033[3%cm", color + '0');
+ else
+ snprintf(buf, sizeof(buf), "\033[9%cm", (color-8) + '0');
+ }
+
+ putpad(buf);
+ }
+ else if(COL_TRANS() && color == pico_count_in_color_table()-1 && _op){
+ char bg_color_was[MAXCOLORLEN+1];
+
+ bg_color_was[0] = '\0';
+
+ /*
+ * Setting transparent (default) foreground color.
+ * We don't know how to set only the default foreground color,
+ * _op sets both foreground and background. So we need to
+ *
+ * get current background color
+ * set default colors
+ * if (current background was not default) reset it
+ */
+ if(_last_bg_color && strncmp(_last_bg_color, MATCH_TRAN_COLOR, RGBLEN)){
+ strncpy(bg_color_was, _last_bg_color, sizeof(bg_color_was));
+ bg_color_was[sizeof(bg_color_was)-1] = '\0';
+ }
+
+ putpad(_op);
+ if(bg_color_was[0]){
+ _force_bg_color_change = 1;
+ pico_set_bg_color(bg_color_was);
+ }
+ }
+ else if(_setaf)
+ putpad(tparm(_setaf, color));
+ else if(_setf)
+ putpad(tparm(_setf, color));
+ else if(_scp){
+ /* set color pair method */
+ }
+
+ return(0);
+}
+
+
+int
+tbgcolor(int color)
+{
+ if(!_color_inited)
+ tinitcolor();
+
+ if(!_color_inited)
+ return(-1);
+
+ if((ANSI8_COLOR() && (color < 0 || color >= 8+COL_TRANS())) ||
+ (ANSI16_COLOR() && (color < 0 || color >= 16+COL_TRANS())) ||
+ (ANSI256_COLOR() && (color < 0 || color >= 256+COL_TRANS())) ||
+ (!ANSI_COLOR() && (color < 0 || color >= _colors+COL_TRANS())))
+ return(-1);
+
+ if(ANSI_COLOR()){
+ char buf[20];
+
+ if(COL_TRANS() && color == pico_count_in_color_table()-1)
+ snprintf(buf, sizeof(buf), "\033[49m");
+ else if(ANSI256_COLOR())
+ snprintf(buf, sizeof(buf), "\033[48;5;%dm", color);
+ else{
+ if(color < 8)
+ snprintf(buf, sizeof(buf), "\033[4%cm", color + '0');
+ else
+ snprintf(buf, sizeof(buf), "\033[10%cm", (color-8) + '0');
+ }
+
+ putpad(buf);
+ }
+ else if(COL_TRANS() && color == pico_count_in_color_table()-1 && _op){
+ char fg_color_was[MAXCOLORLEN+1];
+
+ fg_color_was[0] = '\0';
+
+ /*
+ * Setting transparent (default) background color.
+ * We don't know how to set only the default background color,
+ * _op sets both foreground and background. So we need to
+ *
+ * get current foreground color
+ * set default colors
+ * if (current foreground was not default) reset it
+ */
+ if(_last_fg_color && strncmp(_last_fg_color, MATCH_TRAN_COLOR, RGBLEN)){
+ strncpy(fg_color_was, _last_fg_color, sizeof(fg_color_was));
+ fg_color_was[sizeof(fg_color_was)-1] = '\0';
+ }
+
+ putpad(_op);
+ if(fg_color_was[0]){
+ _force_fg_color_change = 1;
+ pico_set_fg_color(fg_color_was);
+ }
+ }
+ else if(_setab)
+ putpad(tparm(_setab, color));
+ else if(_setb)
+ putpad(tparm(_setb, color));
+ else if(_scp){
+ /* set color pair method */
+ }
+
+ return(0);
+}
+
+
+
+/*
+ * We're not actually using the RGB value other than as a string which
+ * maps into the color.
+ * In fact, on some systems color 1 and color 4 are swapped, and color 3
+ * and color 6 are swapped. So don't believe the RGB values.
+ * Still, it feels right to have them be the canonical values, so we do that.
+ *
+ * Actually we are using them more now. In color_to_val we map to the closest
+ * color if we don't get an exact match. We ignore values over 255.
+ *
+ * More than one "name" can map to the same "rgb".
+ * More than one "name" can map to the same "val".
+ * The "val" for a "name" and for its "rgb" are the same.
+ */
+struct color_table *
+init_color_table(void)
+{
+ struct color_table *ct = NULL, *t;
+ int i, count;
+ char colorname[12];
+
+ count = pico_count_in_color_table();
+
+ if(count > 0 && count <= 256+COL_TRANS()){
+ int ind, graylevel;
+ struct {
+ char rgb[RGBLEN+1];
+ int red, green, blue;
+ } cube[256];
+
+ ct = (struct color_table *) fs_get((count+1) * sizeof(struct color_table));
+ if(ct)
+ memset(ct, 0, (count+1) * sizeof(struct color_table));
+
+ /*
+ * We boldly assume that 256 colors means xterm 256-color
+ * color cube and grayscale.
+ */
+ if(ANSI_COLOR() && (count == 256+COL_TRANS())){
+ int r, g, b, gray;
+
+ for(r = 0; r < 6; r++)
+ for(g = 0; g < 6; g++)
+ for(b = 0; b < 6; b++){
+ ind = 16 + 36*r + 6*g + b;
+ cube[ind].red = r ? (40*r + 55) : 0;
+ cube[ind].green = g ? (40*g + 55) : 0;
+ cube[ind].blue = b ? (40*b + 55) : 0;
+ snprintf(cube[ind].rgb, sizeof(cube[ind].rgb), "%3.3d,%3.3d,%3.3d",
+ cube[ind].red, cube[ind].green, cube[ind].blue);
+ }
+
+ for(gray = 0; gray < 24; gray++){
+ ind = gray + 232;
+ graylevel = 10*gray + 8;
+ cube[ind].red = graylevel;
+ cube[ind].green = graylevel;
+ cube[ind].blue = graylevel;
+ snprintf(cube[ind].rgb, sizeof(cube[ind].rgb), "%3.3d,%3.3d,%3.3d",
+ graylevel, graylevel, graylevel);
+ }
+ }
+
+ for(i = 0, t = ct; t && i < count; i++, t++){
+ t->val = i;
+
+ switch(i){
+ case COL_BLACK:
+ strncpy(colorname, "black", sizeof(colorname));
+ colorname[sizeof(colorname)-1] = '\0';
+ break;
+ case COL_RED:
+ strncpy(colorname, "red", sizeof(colorname));
+ colorname[sizeof(colorname)-1] = '\0';
+ break;
+ case COL_GREEN:
+ strncpy(colorname, "green", sizeof(colorname));
+ colorname[sizeof(colorname)-1] = '\0';
+ break;
+ case COL_YELLOW:
+ strncpy(colorname, "yellow", sizeof(colorname));
+ colorname[sizeof(colorname)-1] = '\0';
+ break;
+ case COL_BLUE:
+ strncpy(colorname, "blue", sizeof(colorname));
+ colorname[sizeof(colorname)-1] = '\0';
+ break;
+ case COL_MAGENTA:
+ strncpy(colorname, "magenta", sizeof(colorname));
+ colorname[sizeof(colorname)-1] = '\0';
+ break;
+ case COL_CYAN:
+ strncpy(colorname, "cyan", sizeof(colorname));
+ colorname[sizeof(colorname)-1] = '\0';
+ break;
+ case COL_WHITE:
+ strncpy(colorname, "white", sizeof(colorname));
+ colorname[sizeof(colorname)-1] = '\0';
+ break;
+ default:
+ snprintf(colorname, sizeof(colorname), "color%3.3d", i);
+ break;
+ }
+
+ if(COL_TRANS() && i == count-1){
+ strncpy(colorname, MATCH_TRAN_COLOR, sizeof(colorname));
+ colorname[sizeof(colorname)-1] = '\0';
+ }
+
+ add_to_color_name_list(t, colorname);
+
+ if(count == 8+COL_TRANS()){
+ if(COL_TRANS() && i == count-1){
+ t->red = t->green = t->blue = -1;
+ }
+ else
+ switch(i){
+ case COL_BLACK:
+ t->red = t->green = t->blue = 0;
+ add_to_color_name_list(t, "color008");
+ add_to_color_name_list(t, "colordgr");
+ add_to_color_name_list(t, "colormgr");
+ break;
+ case COL_RED:
+ t->red = 255;
+ t->green = t->blue = 0;
+ add_to_color_name_list(t, "color009");
+ break;
+ case COL_GREEN:
+ t->green = 255;
+ t->red = t->blue = 0;
+ add_to_color_name_list(t, "color010");
+ break;
+ case COL_YELLOW:
+ t->red = t->green = 255;
+ t->blue = 0;
+ add_to_color_name_list(t, "color011");
+ break;
+ case COL_BLUE:
+ t->red = t->green = 0;
+ t->blue = 255;
+ add_to_color_name_list(t, "color012");
+ break;
+ case COL_MAGENTA:
+ t->red = t->blue = 255;
+ t->green = 0;
+ add_to_color_name_list(t, "color013");
+ break;
+ case COL_CYAN:
+ t->red = 0;
+ t->green = t->blue = 255;
+ add_to_color_name_list(t, "color014");
+ break;
+ case COL_WHITE:
+ t->red = t->green = t->blue = 255;
+ add_to_color_name_list(t, "color015");
+ add_to_color_name_list(t, "colorlgr");
+ break;
+ }
+ }
+ else if(count == 16+COL_TRANS() || count == 256+COL_TRANS()){
+ if(COL_TRANS() && i == count-1){
+ t->red = t->green = t->blue = -1;
+ }
+ else
+ /*
+ * This set of RGB values seems to come close to describing
+ * what a 16-color xterm gives you.
+ */
+ switch(i){
+ case COL_BLACK:
+ t->red = t->green = t->blue = 0;
+ break;
+ case COL_RED: /* actually dark red */
+ t->red = 174;
+ t->green = t->blue = 0;
+ break;
+ case COL_GREEN: /* actually dark green */
+ t->green = 174;
+ t->red = t->blue = 0;
+ break;
+ case COL_YELLOW: /* actually dark yellow */
+ t->blue = 0;
+ t->red = t->green = 174;
+ break;
+ case COL_BLUE: /* actually dark blue */
+ t->blue = 174;
+ t->red = t->green = 0;
+ break;
+ case COL_MAGENTA: /* actually dark magenta */
+ t->green = 0;
+ t->red = t->blue = 174;
+ break;
+ case COL_CYAN: /* actually dark cyan */
+ t->red = 0;
+ t->green = t->blue = 174;
+ break;
+ case COL_WHITE: /* actually light gray */
+ t->red = t->green = t->blue = 174;
+ if(count == 16)
+ add_to_color_name_list(t, "colorlgr");
+
+ break;
+ case 8: /* dark gray */
+ t->red = t->green = t->blue = 64;
+ if(count == 16){
+ add_to_color_name_list(t, "colordgr");
+ add_to_color_name_list(t, "colormgr");
+ }
+
+ break;
+ case 9: /* red */
+ t->red = 255;
+ t->green = t->blue = 0;
+ break;
+ case 10: /* green */
+ t->green = 255;
+ t->red = t->blue = 0;
+ break;
+ case 11: /* yellow */
+ t->blue = 0;
+ t->red = t->green = 255;
+ break;
+ case 12: /* blue */
+ t->blue = 255;
+ t->red = t->green = 0;
+ break;
+ case 13: /* magenta */
+ t->green = 0;
+ t->red = t->blue = 255;
+ break;
+ case 14: /* cyan */
+ t->red = 0;
+ t->green = t->blue = 255;
+ break;
+ case 15: /* white */
+ t->red = t->green = t->blue = 255;
+ break;
+ default:
+ t->red = cube[i].red;
+ t->green = cube[i].green;
+ t->blue = cube[i].blue;
+ switch(i){
+ case 238:
+ add_to_color_name_list(t, "colordgr");
+ break;
+
+ case 244:
+ add_to_color_name_list(t, "colormgr");
+ break;
+
+ case 250:
+ add_to_color_name_list(t, "colorlgr");
+ break;
+ }
+
+ break;
+ }
+ }
+ else{
+ if(COL_TRANS() && i == count-1){
+ t->red = t->green = t->blue = -1;
+ }
+ else
+ switch(i){
+ case COL_BLACK:
+ t->red = t->green = t->blue = 0;
+ break;
+ case COL_RED: /* actually dark red */
+ t->red = 255;
+ t->green = t->blue = 0;
+ break;
+ case COL_GREEN: /* actually dark green */
+ t->green = 255;
+ t->red = t->blue = 0;
+ break;
+ case COL_YELLOW: /* actually dark yellow */
+ t->blue = 0;
+ t->red = t->green = 255;
+ break;
+ case COL_BLUE: /* actually dark blue */
+ t->blue = 255;
+ t->red = t->green = 0;
+ break;
+ case COL_MAGENTA: /* actually dark magenta */
+ t->green = 0;
+ t->red = t->blue = 255;
+ break;
+ case COL_CYAN: /* actually dark cyan */
+ t->red = 0;
+ t->green = t->blue = 255;
+ break;
+ case COL_WHITE: /* actually light gray */
+ t->red = t->green = t->blue = 255;
+ break;
+ default:
+ /* this will just be a string match */
+ t->red = t->green = t->blue = 256+i;
+ break;
+ }
+ }
+ }
+
+ for(i = 0, t = ct; t && i < count; i++, t++){
+ t->rgb = (char *)fs_get((RGBLEN+1) * sizeof(char));
+ if(COL_TRANS() && i == count-1)
+ snprintf(t->rgb, RGBLEN+1, MATCH_TRAN_COLOR);
+ else
+ snprintf(t->rgb, RGBLEN+1, "%3.3d,%3.3d,%3.3d", t->red, t->green, t->blue);
+ }
+ }
+
+ return(ct);
+}
+
+
+void
+add_to_color_name_list(struct color_table *t, char *name)
+{
+ if(t && name && *name){
+ struct color_name_list *new_name;
+
+ new_name = (struct color_name_list *) fs_get(sizeof(struct color_name_list));
+ if(new_name){
+ memset(new_name, 0, sizeof(*new_name));
+ new_name->namelen = strlen(name);
+
+ new_name->name = (char *) fs_get((new_name->namelen+1) * sizeof(char));
+ if(new_name->name){
+ strncpy(new_name->name, name, new_name->namelen+1);
+ new_name->name[new_name->namelen] = '\0';
+
+ if(t->names){
+ struct color_name_list *nl;
+ for(nl = t->names; nl->next; nl = nl->next)
+ ;
+
+ nl->next = new_name;
+ }
+ else
+ t->names = new_name;
+ }
+ }
+ }
+}
+
+
+void
+free_color_name_list(struct color_name_list **nl)
+{
+ if(nl && *nl){
+ if((*nl)->next)
+ free_color_name_list(&(*nl)->next);
+
+ if((*nl)->name)
+ fs_give((void **) &(*nl)->name);
+
+ fs_give((void **) nl);
+ }
+}
+
+
+/*
+ * Map from integer color value to canonical color name.
+ */
+char *
+colorx(int color)
+{
+ struct color_table *ct;
+ static char cbuf[12];
+
+ /* before we get set up, we still need to use this a bit */
+ if(!color_tbl){
+ switch(color){
+ case COL_BLACK:
+ return("black");
+ case COL_RED:
+ return("red");
+ case COL_GREEN:
+ return("green");
+ case COL_YELLOW:
+ return("yellow");
+ case COL_BLUE:
+ return("blue");
+ case COL_MAGENTA:
+ return("magenta");
+ case COL_CYAN:
+ return("cyan");
+ case COL_WHITE:
+ return("white");
+ default:
+ snprintf(cbuf, sizeof(cbuf), "color%3.3d", color);
+ return(cbuf);
+ }
+ }
+
+ for(ct = color_tbl; ct->names; ct++)
+ if(ct->val == color)
+ break;
+
+ /* rgb _is_ the canonical name */
+ if(ct->names)
+ return(ct->rgb);
+
+ /* not supposed to get here */
+ snprintf(cbuf, sizeof(cbuf), "color%3.3d", color);
+ return(cbuf);
+}
+
+
+/*
+ * Argument is a color name which could be an RGB string, a name like "blue",
+ * or a name like "color011".
+ *
+ * Returns a pointer to the canonical name of the color.
+ */
+char *
+color_to_canonical_name(char *s)
+{
+ struct color_table *ct;
+ struct color_name_list *nl;
+ int done;
+
+ if(!s || !color_tbl)
+ return(NULL);
+
+ if(*s == ' ' || isdigit(*s)){
+ /* check for rgb string instead of name */
+ for(ct = color_tbl; ct->rgb; ct++)
+ if(!strncmp(ct->rgb, s, RGBLEN))
+ break;
+ }
+ else{
+ for(done=0, ct = color_tbl; !done && ct->names; ct++){
+ for(nl = ct->names; !done && nl; nl = nl->next)
+ if(nl->name && !struncmp(nl->name, s, nl->namelen))
+ done++;
+
+ if(done)
+ break;
+ }
+ }
+
+ /* rgb is the canonical name */
+ if(ct->names)
+ return(ct->rgb);
+ else if(!struncmp(s, MATCH_NORM_COLOR, RGBLEN) || !struncmp(s, MATCH_NONE_COLOR, RGBLEN))
+ return(s);
+
+ return("");
+}
+
+
+/*
+ * Argument is the name of a color or an RGB value that we recognize.
+ * The table should be set up so that the val returned is the same whether
+ * or not we choose the canonical name.
+ *
+ * Returns the integer value for the color.
+ */
+int
+color_to_val(char *s)
+{
+ struct color_table *ct;
+ struct color_name_list *nl;
+ int done;
+
+ if(!s || !color_tbl)
+ return(-1);
+
+ if(*s == ' ' || isdigit(*s)){
+ /* check for rgb string instead of name */
+ for(ct = color_tbl; ct->rgb; ct++)
+ if(!strncmp(ct->rgb, s, RGBLEN))
+ break;
+
+ /*
+ * Didn't match any. Find "closest" to match.
+ */
+ if(!ct->rgb){
+ int r = -1, g = -1, b = -1;
+ char *p, *comma, scopy[RGBLEN+1];
+
+ strncpy(scopy, s, sizeof(scopy));
+ scopy[sizeof(scopy)-1] = '\0';
+
+ p = scopy;
+ comma = strchr(p, ',');
+ if(comma){
+ *comma = '\0';
+ r = atoi(p);
+ p = comma+1;
+ if(r >= 0 && r <= 255 && *p){
+ comma = strchr(p, ',');
+ if(comma){
+ *comma = '\0';
+ g = atoi(p);
+ p = comma+1;
+ if(g >= 0 && g <= 255 && *p){
+ b = atoi(p);
+ }
+ }
+ }
+ }
+
+ if(r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255){
+ struct color_table *closest = NULL;
+ int closest_value = 1000000;
+ int cv;
+
+ for(ct = color_tbl; ct->rgb; ct++){
+
+ if(ct->red >= 0 && ct->red <= 255
+ && ct->green >= 0 && ct->green <= 255
+ && ct->blue >= 0 && ct->blue <= 255){
+ cv = (ct->red - r) * (ct->red - r) +
+ (ct->green - g) * (ct->green - g) +
+ (ct->blue - b) * (ct->blue - b);
+ if(cv < closest_value){
+ closest_value = cv;
+ closest = ct;
+ }
+ }
+ }
+
+ if(closest)
+ ct = closest;
+ }
+ }
+ }
+ else{
+ for(done=0, ct = color_tbl; !done && ct->names; ct++){
+ for(nl = ct->names; !done && nl; nl = nl->next)
+ if(nl->name && !struncmp(nl->name, s, nl->namelen))
+ done++;
+
+ if(done)
+ break;
+ }
+ }
+
+ if(ct->names)
+ return(ct->val);
+ else
+ return(-1);
+}
+
+
+void
+free_color_table(struct color_table **ctbl)
+{
+ struct color_table *t;
+
+ if(ctbl && *ctbl){
+ for(t = *ctbl; t->names; t++){
+ free_color_name_list(&t->names);
+
+ if(t->rgb)
+ fs_give((void **) &t->rgb);
+ }
+
+ fs_give((void **) ctbl);
+ }
+}
+
+
+int
+pico_count_in_color_table(void)
+{
+ return(
+ (ANSI_COLOR()
+ ? (ANSI8_COLOR() ? 8 : ANSI16_COLOR() ? 16 : 256)
+ : _colors)
+ + COL_TRANS());
+}
+
+
+void
+pico_nfcolor(char *s)
+{
+ if(_nfcolor)
+ fs_give((void **) &_nfcolor);
+
+ if(s){
+ size_t len;
+
+ len = strlen(s);
+ _nfcolor = (char *) fs_get((len+1) * sizeof(char));
+ if(_nfcolor){
+ strncpy(_nfcolor, s, len+1);
+ _nfcolor[len] = '\0';
+ }
+
+ if(the_normal_color){
+ strncpy(the_normal_color->fg, _nfcolor, MAXCOLORLEN+1);
+ the_normal_color->fg[MAXCOLORLEN] = '\0';
+ }
+ }
+ else if(the_normal_color)
+ free_color_pair(&the_normal_color);
+}
+
+
+void
+pico_nbcolor(char *s)
+{
+ if(_nbcolor)
+ fs_give((void **) &_nbcolor);
+
+ if(s){
+ size_t len;
+
+ len = strlen(s);
+ _nbcolor = (char *) fs_get((len+1) * sizeof(char));
+ if(_nbcolor){
+ strncpy(_nbcolor, s, len+1);
+ _nbcolor[len] = '\0';
+ }
+
+ if(the_normal_color){
+ strncpy(the_normal_color->bg, _nbcolor, MAXCOLORLEN+1);
+ the_normal_color->bg[MAXCOLORLEN] = '\0';
+ }
+ }
+ else if(the_normal_color)
+ free_color_pair(&the_normal_color);
+}
+
+void
+pico_rfcolor(char *s)
+{
+ if(_rfcolor)
+ fs_give((void **) &_rfcolor);
+
+ if(s){
+ size_t len;
+
+ len = strlen(s);
+ _rfcolor = (char *) fs_get((len+1) * sizeof(char));
+ if(_rfcolor){
+ strncpy(_rfcolor, s, len+1);
+ _rfcolor[len] = '\0';
+ }
+
+ if(the_rev_color){
+ strncpy(the_rev_color->fg, _rfcolor, MAXCOLORLEN+1);
+ the_rev_color->fg[MAXCOLORLEN] = '\0';
+ }
+ }
+ else if(the_rev_color)
+ free_color_pair(&the_rev_color);
+}
+
+void
+pico_rbcolor(char *s)
+{
+ if(_rbcolor)
+ fs_give((void **) &_rbcolor);
+
+ if(s){
+ size_t len;
+
+ len = strlen(s);
+ _rbcolor = (char *) fs_get((len+1) * sizeof(char));
+ if(_rbcolor){
+ strncpy(_rbcolor, s, len+1);
+ _rbcolor[len] = '\0';
+ }
+
+ if(the_rev_color){
+ strncpy(the_rev_color->bg, _rbcolor, MAXCOLORLEN+1);
+ the_rev_color->bg[MAXCOLORLEN] = '\0';
+ }
+ }
+ else if(the_rev_color)
+ free_color_pair(&the_rev_color);
+}
+
+
+int
+pico_hascolor(void)
+{
+ if(!_color_inited)
+ tinitcolor();
+
+ return(_color_inited);
+}
+
+
+int
+pico_usingcolor(void)
+{
+ return(_using_color && pico_hascolor());
+}
+
+
+/*
+ * This should only be called when we're using the
+ * unix termdef color, as opposed to the ANSI defined
+ * color stuff or the Windows stuff.
+ */
+int
+pico_trans_color(void)
+{
+ return(_bce && _op);
+}
+
+
+void
+pico_toggle_color(int on)
+{
+ if(on){
+ if(pico_hascolor())
+ _using_color = 1;
+ }
+ else{
+ _using_color = 0;
+ if(_color_inited){
+ _color_inited = 0;
+ if(!panicking())
+ free_color_table(&color_tbl);
+
+ if(ANSI_COLOR())
+ putpad("\033[39;49m");
+ else{
+ if(_op)
+ putpad(_op);
+ if(_oc)
+ putpad(_oc);
+ }
+ }
+ }
+}
+
+
+unsigned
+pico_get_color_options(void)
+{
+ return(color_options);
+}
+
+
+int
+pico_trans_is_on(void)
+{
+ return(COL_TRANS());
+}
+
+
+/*
+ * Absolute set of options. Caller has to OR things together and so forth.
+ */
+void
+pico_set_color_options(unsigned flags)
+{
+ color_options = flags;
+}
+
+void
+pico_endcolor(void)
+{
+ pico_toggle_color(0);
+ if(panicking())
+ return;
+
+ if(_nfcolor)
+ fs_give((void **) &_nfcolor);
+
+ if(_nbcolor)
+ fs_give((void **) &_nbcolor);
+
+ if(_rfcolor)
+ fs_give((void **) &_rfcolor);
+
+ if(_rbcolor)
+ fs_give((void **) &_rbcolor);
+
+ if(_last_fg_color)
+ fs_give((void **) &_last_fg_color);
+
+ if(_last_bg_color)
+ fs_give((void **) &_last_bg_color);
+
+ if(the_rev_color)
+ free_color_pair(&the_rev_color);
+
+ if(the_normal_color)
+ free_color_pair(&the_normal_color);
+}
+
+
+void
+pico_set_nfg_color(void)
+{
+ if(_nfcolor)
+ (void)pico_set_fg_color(_nfcolor);
+}
+
+
+void
+pico_set_nbg_color(void)
+{
+ if(_nbcolor)
+ (void)pico_set_bg_color(_nbcolor);
+}
+
+
+void
+pico_set_normal_color(void)
+{
+ if(!_nfcolor || !_nbcolor ||
+ !pico_set_fg_color(_nfcolor) || !pico_set_bg_color(_nbcolor)){
+ (void)pico_set_fg_color(DEFAULT_NORM_FORE_RGB);
+ (void)pico_set_bg_color(DEFAULT_NORM_BACK_RGB);
+ }
+}
+
+
+/*
+ * If inverse is a color, returns a pointer to that color.
+ * If not, returns NULL.
+ *
+ * NOTE: Don't free this!
+ */
+COLOR_PAIR *
+pico_get_rev_color(void)
+{
+ if(pico_usingcolor() && _rfcolor && _rbcolor &&
+ pico_is_good_color(_rfcolor) && pico_is_good_color(_rbcolor)){
+ if(!the_rev_color)
+ the_rev_color = new_color_pair(_rfcolor, _rbcolor);
+
+ return(the_rev_color);
+ }
+ else
+ return(NULL);
+}
+
+
+/*
+ * Returns a pointer to the normal color.
+ *
+ * NOTE: Don't free this!
+ */
+COLOR_PAIR *
+pico_get_normal_color(void)
+{
+ if(pico_usingcolor() && _nfcolor && _nbcolor &&
+ pico_is_good_color(_nfcolor) && pico_is_good_color(_nbcolor)){
+ if(!the_normal_color)
+ the_normal_color = new_color_pair(_nfcolor, _nbcolor);
+
+ return(the_normal_color);
+ }
+ else
+ return(NULL);
+}
+
+
+/*
+ * Sets color to (fg,bg).
+ * Flags == PSC_NONE No alternate default if fg,bg fails.
+ * == PSC_NORM Set it to Normal color on failure.
+ * == PSC_REV Set it to Reverse color on failure.
+ *
+ * If flag PSC_RET is set, returns an allocated copy of the previous
+ * color pair, otherwise returns NULL.
+ */
+COLOR_PAIR *
+pico_set_colors(char *fg, char *bg, int flags)
+{
+ int uc;
+ COLOR_PAIR *cp = NULL, *rev = NULL;
+
+ if(flags & PSC_RET)
+ cp = pico_get_cur_color();
+
+ if(fg && !strcmp(fg, END_PSEUDO_REVERSE)){
+ EndInverse();
+ if(cp)
+ free_color_pair(&cp);
+ }
+ else if(!((uc=pico_usingcolor()) && fg && bg &&
+ pico_set_fg_color(fg) && pico_set_bg_color(bg))){
+
+ if(uc && flags & PSC_NORM)
+ pico_set_normal_color();
+ else if(flags & PSC_REV){
+ if((rev = pico_get_rev_color()) != NULL){
+ pico_set_fg_color(rev->fg); /* these will succeed */
+ pico_set_bg_color(rev->bg);
+ }
+ else{
+ StartInverse();
+ if(cp){
+ strncpy(cp->fg, END_PSEUDO_REVERSE, MAXCOLORLEN+1);
+ cp->fg[MAXCOLORLEN] = '\0';
+ strncpy(cp->bg, END_PSEUDO_REVERSE, MAXCOLORLEN+1);
+ cp->bg[MAXCOLORLEN] = '\0';
+ }
+ }
+ }
+ }
+
+ return(cp);
+}
+
+
+int
+pico_is_good_color(char *s)
+{
+ struct color_table *ct;
+ struct color_name_list *nl;
+ int done;
+
+ if(!s || !color_tbl)
+ return(FALSE);
+
+ if(!strcmp(s, END_PSEUDO_REVERSE))
+ return(TRUE);
+ else if(!struncmp(s, MATCH_NORM_COLOR, RGBLEN) || !struncmp(s, MATCH_NONE_COLOR, RGBLEN))
+ return(TRUE);
+ else if(*s == ' ' || isdigit(*s)){
+ /* check for rgb string instead of name */
+ for(ct = color_tbl; ct->rgb; ct++)
+ if(!strncmp(ct->rgb, s, RGBLEN))
+ break;
+
+ /* if no match it's still ok if rgb */
+ if(!ct->rgb){
+ int r = -1, g = -1, b = -1;
+ char *p, *comma, scopy[RGBLEN+1];
+
+ strncpy(scopy, s, sizeof(scopy));
+ scopy[sizeof(scopy)-1] = '\0';
+
+ p = scopy;
+ comma = strchr(p, ',');
+ if(comma){
+ *comma = '\0';
+ r = atoi(p);
+ p = comma+1;
+ if(r >= 0 && r <= 255 && *p){
+ comma = strchr(p, ',');
+ if(comma){
+ *comma = '\0';
+ g = atoi(p);
+ p = comma+1;
+ if(g >= 0 && g <= 255 && *p){
+ b = atoi(p);
+ }
+ }
+ }
+ }
+
+ if(r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255)
+ ct = color_tbl; /* to force TRUE */
+ }
+ }
+ else{
+ for(done=0, ct = color_tbl; !done && ct->names; ct++){
+ for(nl = ct->names; !done && nl; nl = nl->next)
+ if(nl->name && !struncmp(nl->name, s, nl->namelen))
+ done++;
+
+ if(done)
+ break;
+ }
+ }
+
+ return(ct->names ? TRUE : FALSE);
+}
+
+
+/*
+ * Return TRUE on success.
+ */
+int
+pico_set_fg_color(char *s)
+{
+ int val;
+
+ if(!s || !color_tbl)
+ return(FALSE);
+
+ if(!strcmp(s, END_PSEUDO_REVERSE)){
+ EndInverse();
+ return(TRUE);
+ }
+
+ if(!struncmp(s, MATCH_NORM_COLOR, RGBLEN))
+ s = _nfcolor;
+ else if(!struncmp(s, MATCH_NONE_COLOR, RGBLEN))
+ return(TRUE);
+
+ if((val = color_to_val(s)) >= 0){
+ size_t len;
+ int changed;
+
+ changed = !_last_fg_color || strcmp(_last_fg_color,colorx(val));
+
+ /* already set correctly */
+ if(!_force_fg_color_change && !changed)
+ return(TRUE);
+
+ _force_fg_color_change = 0;
+
+ if(changed){
+ if(_last_fg_color)
+ fs_give((void **) &_last_fg_color);
+
+ len = strlen(colorx(val));
+ if((_last_fg_color = (char *) fs_get((len+1) * sizeof(char))) != NULL){
+ strncpy(_last_fg_color, colorx(val), len+1);
+ _last_fg_color[len] = '\0';
+ }
+ }
+
+ tfgcolor(val);
+ return(TRUE);
+ }
+ else
+ return(FALSE);
+}
+
+
+int
+pico_set_bg_color(char *s)
+{
+ int val;
+
+ if(!s || !color_tbl)
+ return(FALSE);
+
+ if(!strcmp(s, END_PSEUDO_REVERSE)){
+ EndInverse();
+ return(TRUE);
+ }
+
+ if(!struncmp(s, MATCH_NORM_COLOR, RGBLEN))
+ s = _nbcolor;
+ else if(!struncmp(s, MATCH_NONE_COLOR, RGBLEN))
+ return(TRUE);
+
+ if((val = color_to_val(s)) >= 0){
+ size_t len;
+ int changed;
+
+ changed = !_last_bg_color || strcmp(_last_bg_color,colorx(val));
+
+ /* already set correctly */
+ if(!_force_bg_color_change && !changed)
+ return(TRUE);
+
+ _force_bg_color_change = 0;
+
+ if(changed){
+ if(_last_bg_color)
+ fs_give((void **) &_last_bg_color);
+
+ len = strlen(colorx(val));
+ if((_last_bg_color = (char *) fs_get((len+1) * sizeof(char))) != NULL){
+ strncpy(_last_bg_color, colorx(val), len+1);
+ _last_bg_color[len] = '\0';
+ }
+ }
+
+ tbgcolor(val);
+ return(TRUE);
+ }
+ else
+ return(FALSE);
+}
+
+
+/*
+ * Return a pointer to an rgb string for the input color. The output is 11
+ * characters long and looks like rrr,ggg,bbb.
+ *
+ * Args colorName -- The color to convert to ascii rgb.
+ *
+ * Returns Pointer to a static buffer containing the rgb string. Can use up
+ * to three returned values at once before the first is overwritten.
+ */
+char *
+color_to_asciirgb(char *colorName)
+{
+ static char c_to_a_buf[3][RGBLEN+1];
+ static int whichbuf = 0;
+ struct color_table *ct;
+ struct color_name_list *nl;
+ int done;
+
+ whichbuf = (whichbuf + 1) % 3;
+
+ c_to_a_buf[whichbuf][0] = '\0';
+
+ for(done=0, ct = color_tbl; !done && ct->names; ct++){
+ for(nl = ct->names; !done && nl; nl = nl->next)
+ if(nl->name && !struncmp(nl->name, colorName, nl->namelen))
+ done++;
+
+ if(done)
+ break;
+ }
+
+ if(ct && ct->names){
+ strncpy(c_to_a_buf[whichbuf], ct->rgb, sizeof(c_to_a_buf[0]));
+ c_to_a_buf[whichbuf][sizeof(c_to_a_buf[0])-1] = '\0';
+ }
+ else if(*colorName == ' ' || isdigit(*colorName)){
+ /* check for rgb string instead of name */
+ for(ct = color_tbl; ct->rgb; ct++)
+ if(!strncmp(ct->rgb, colorName, RGBLEN))
+ break;
+
+ /* if no match it's still ok if rgb */
+ if(!ct->rgb){
+ int r = -1, g = -1, b = -1;
+ char *p, *comma, scopy[RGBLEN+1];
+
+ strncpy(scopy, colorName, sizeof(scopy));
+ scopy[sizeof(scopy)-1] = '\0';
+
+ p = scopy;
+ comma = strchr(p, ',');
+ if(comma){
+ *comma = '\0';
+ r = atoi(p);
+ p = comma+1;
+ if(r >= 0 && r <= 255 && *p){
+ comma = strchr(p, ',');
+ if(comma){
+ *comma = '\0';
+ g = atoi(p);
+ p = comma+1;
+ if(g >= 0 && g <= 255 && *p){
+ b = atoi(p);
+ }
+ }
+ }
+ }
+
+ if(r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255){
+ /* it was already RGB */
+ snprintf(c_to_a_buf[whichbuf], sizeof(c_to_a_buf[0]), "%3.3d,%3.3d,%3.3d", r, g, b);
+ }
+ }
+ else{
+ strncpy(c_to_a_buf[whichbuf], ct->rgb, sizeof(c_to_a_buf[0]));
+ c_to_a_buf[whichbuf][sizeof(c_to_a_buf[0])-1] = '\0';
+ }
+ }
+
+ if(!c_to_a_buf[whichbuf][0]){
+ int l;
+
+ /*
+ * If we didn't find the color it could be that it is the
+ * normal color (MATCH_NORM_COLOR) or the none color
+ * (MATCH_NONE_COLOR). If that is the case, this strncpy thing
+ * will work out correctly because those two strings are
+ * RGBLEN long. Otherwise we're in a bit of trouble. This
+ * most likely means that the user is using the same pinerc on
+ * two terminals, one with more colors than the other. We didn't
+ * find a match because this color isn't present on this terminal.
+ * Since the return value of this function is assumed to be
+ * RGBLEN long, we'd better make it that long.
+ * It still won't work correctly because colors will be screwed up,
+ * but at least the embedded colors in filter.c will get properly
+ * sucked up when they're encountered.
+ */
+ strncpy(c_to_a_buf[whichbuf], "xxxxxxxxxxx", RGBLEN); /* RGBLEN is 11 */
+ l = strlen(colorName);
+ strncpy(c_to_a_buf[whichbuf], colorName, (l < RGBLEN) ? l : RGBLEN);
+ c_to_a_buf[whichbuf][RGBLEN] = '\0';
+ }
+
+ return(c_to_a_buf[whichbuf]);
+}
+
+
+char *
+pico_get_last_fg_color(void)
+{
+ char *ret = NULL;
+
+ if(_last_fg_color){
+ size_t len;
+
+ len = strlen(_last_fg_color);
+ if((ret = (char *) fs_get((len+1) * sizeof(char))) != NULL){
+ strncpy(ret, _last_fg_color, len+1);
+ ret[len] = '\0';
+ }
+ }
+
+ return(ret);
+}
+
+
+char *
+pico_get_last_bg_color(void)
+{
+ char *ret = NULL;
+
+ if(_last_bg_color){
+ size_t len;
+
+ len = strlen(_last_bg_color);
+ if((ret = (char *) fs_get((len+1) * sizeof(char))) != NULL){
+ strncpy(ret, _last_bg_color, len+1);
+ ret[len] = '\0';
+ }
+ }
+
+ return(ret);
+}
+
+
+COLOR_PAIR *
+pico_get_cur_color(void)
+{
+ return(new_color_pair(_last_fg_color, _last_bg_color));
+}
+
+#else /* _WINDOWS */
+static short _in_inverse, _in_bold, _in_uline;
+
+int
+pico_trans_is_on(void)
+{
+ return(0);
+}
+
+void
+StartInverse()
+{
+ if(!_in_inverse)
+ mswin_rev(_in_inverse = 1);
+}
+
+void
+EndInverse()
+{
+ if(_in_inverse)
+ mswin_rev(_in_inverse = 0);
+}
+
+int
+InverseState()
+{
+ return(_in_inverse);
+}
+
+void
+StartUnderline()
+{
+ if(!_in_uline)
+ mswin_uline(_in_uline = 1);
+}
+
+void
+EndUnderline()
+{
+ if(_in_uline)
+ mswin_uline(_in_uline = 0);
+}
+
+int
+StartBold()
+{
+ if(!_in_bold)
+ mswin_bold(_in_bold = 1);
+
+ return(1);
+}
+
+void
+EndBold()
+{
+ if(_in_bold)
+ mswin_bold(_in_bold = 0);
+}
+
+
+#endif /* _WINDOWS */
diff --git a/pico/osdep/color.h b/pico/osdep/color.h
new file mode 100644
index 00000000..0dced80c
--- /dev/null
+++ b/pico/osdep/color.h
@@ -0,0 +1,38 @@
+/*
+ * $Id: color.h 1012 2008-03-26 00:44:22Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef PICO_OSDEP_COLOR_INCLUDED
+#define PICO_OSDEP_COLOR_INCLUDED
+
+
+/* exported prototypes */
+void StartInverse(void);
+void flip_inv(int);
+void EndInverse(void);
+int InverseState(void);
+int StartBold(void);
+void EndBold(void);
+void StartUnderline(void);
+void EndUnderline(void);
+int pico_trans_color(void);
+void pico_endcolor(void);
+void pico_toggle_color(int);
+void pico_set_nfg_color(void);
+void pico_set_nbg_color(void);
+
+
+#endif /* PICO_OSDEP_COLOR_INCLUDED */
diff --git a/pico/osdep/filesys.c b/pico/osdep/filesys.c
new file mode 100644
index 00000000..4471882e
--- /dev/null
+++ b/pico/osdep/filesys.c
@@ -0,0 +1,1027 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: filesys.c 770 2007-10-24 00:23:09Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#if HAVE_PWD_H
+# include <pwd.h>
+#endif
+
+#include "../../pith/osdep/temp_nam.h"
+#include "../../pith/charconv/filesys.h"
+
+#include "../estruct.h"
+#include "../mode.h"
+#include "../pico.h"
+#include "../edef.h"
+#include "../efunc.h"
+#include "../keydefs.h"
+
+#include "fsync.h"
+#include "filesys.h"
+
+
+#if HAVE_DIRENT_H
+# include <dirent.h>
+#else
+# define dirent direct
+# if HAVE_SYS_NDIR_H
+# include <sys/ndir.h>
+# endif
+# if HAVE_SYS_DIR_H
+# include <sys/dir.h>
+# endif
+# if HAVE_NDIR_H
+# include <ndir.h>
+# endif
+#endif
+
+
+/*
+ * fexist - returns TRUE if the file exists with mode passed in m,
+ * FALSE otherwise. By side effect returns length of file in l
+ * File is assumed to be a UTF-8 string.
+ */
+int
+fexist(char *file,
+ char *m, /* files mode: r,w,rw,t or x */
+ off_t *l) /* t means use lstat */
+{
+#ifndef _WINDOWS
+ struct stat sbuf;
+ int rv;
+ int (*stat_f)() = (m && *m == 't') ? our_lstat : our_stat;
+
+ if(l)
+ *l = (off_t)0;
+
+ rv = (*stat_f)(file, &sbuf);
+ if(rv < 0){
+ switch(errno){
+ case ENOENT : /* File not found */
+ rv = FIOFNF;
+ break;
+#ifdef ENAMETOOLONG
+ case ENAMETOOLONG : /* Name is too long */
+ rv = FIOLNG;
+ break;
+#endif
+ case EACCES : /* File not found */
+ rv = FIOPER;
+ break;
+ default: /* Some other error */
+ rv = FIOERR;
+ break;
+ }
+
+ return(rv);
+ }
+
+ if(l)
+ *l = (off_t)sbuf.st_size;
+
+ if((sbuf.st_mode&S_IFMT) == S_IFDIR)
+ return(FIODIR);
+ else if(*m == 't'){
+ struct stat sbuf2;
+
+ /*
+ * If it is a symbolic link pointing to a directory, treat
+ * it like it is a directory, not a link.
+ */
+ if((sbuf.st_mode&S_IFMT) == S_IFLNK){
+ rv = our_stat(file, &sbuf2);
+ if(rv < 0){
+ switch(errno){
+ case ENOENT : /* File not found */
+ rv = FIOSYM;
+ break;
+#ifdef ENAMETOOLONG
+ case ENAMETOOLONG : /* Name is too long */
+ rv = FIOLNG;
+ break;
+#endif
+ case EACCES : /* File not found */
+ rv = FIOPER;
+ break;
+ default: /* Some other error */
+ rv = FIOERR;
+ break;
+ }
+
+ return(rv);
+ }
+
+ if((sbuf2.st_mode&S_IFMT) == S_IFDIR)
+ return(FIODIR);
+ }
+
+ return(((sbuf.st_mode&S_IFMT) == S_IFLNK) ? FIOSYM : FIOSUC);
+ }
+
+ if(*m == 'r'){ /* read access? */
+ if(*(m+1) == 'w'){ /* and write access? */
+ rv = (can_access(file,READ_ACCESS)==0)
+ ? (can_access(file,WRITE_ACCESS)==0)
+ ? FIOSUC
+ : FIONWT
+ : FIONRD;
+ }
+ else if(!*(m+1)){ /* just read access? */
+ rv = (can_access(file,READ_ACCESS)==0) ? FIOSUC : FIONRD;
+ }
+ }
+ else if(*m == 'w' && !*(m+1)) /* write access? */
+ rv = (can_access(file,WRITE_ACCESS)==0) ? FIOSUC : FIONWT;
+ else if(*m == 'x' && !*(m+1)) /* execute access? */
+ rv = (can_access(file,EXECUTE_ACCESS)==0) ? FIOSUC : FIONEX;
+ else
+ rv = FIOERR; /* bad m arg */
+
+ return(rv);
+#else /* _WINDOWS */
+ struct stat sbuf;
+
+ if(l != NULL)
+ *l = (off_t)0;
+
+ if(our_stat(file, &sbuf) < 0){
+ if(errno == ENOENT) /* File not found */
+ return(FIOFNF);
+ else
+ return(FIOERR);
+ }
+
+ if(l != NULL)
+ *l = (off_t)sbuf.st_size;
+
+ if(sbuf.st_mode & S_IFDIR)
+ return(FIODIR);
+ else if(*m == 't') /* no links, just say yes */
+ return(FIOSUC);
+
+ if(m[0] == 'r') /* read access? */
+ return((S_IREAD & sbuf.st_mode) ? FIOSUC : FIONRD);
+ else if(m[0] == 'w') /* write access? */
+ return((S_IWRITE & sbuf.st_mode) ? FIOSUC : FIONWT);
+ else if(m[0] == 'x') /* execute access? */
+ return((S_IEXEC & sbuf.st_mode) ? FIOSUC : FIONEX);
+ return(FIOERR); /* what? */
+#endif /* _WINDOWS */
+}
+
+
+/*
+ * isdir - returns true if fn is a readable directory, false otherwise
+ * silent on errors (we'll let someone else notice the problem;)).
+ */
+int
+isdir(char *fn, long *l, time_t *d)
+{
+ struct stat sbuf;
+
+ if(l)
+ *l = 0;
+
+ if(our_stat(fn, &sbuf) < 0)
+ return(0);
+
+ if(l)
+ *l = sbuf.st_size;
+
+ if(d)
+ *d = sbuf.st_mtime;
+
+ return((sbuf.st_mode&S_IFMT) == S_IFDIR);
+}
+
+
+/*
+ * gethomedir - returns the users home directory in UTF-8 string
+ * Note: home is malloc'd for life of pico
+ */
+char *
+gethomedir(int *l)
+{
+ static char *home = NULL;
+ static short hlen = 0;
+
+ if(home == NULL){
+ char buf[NLINE];
+
+#ifndef _WINDOWS
+ strncpy(buf, "~", sizeof(buf));
+ buf[sizeof(buf)-1] = '\0';
+ fixpath(buf, sizeof(buf)); /* let fixpath do the work! */
+#else /* _WINDOWS */
+ if(Pmaster && Pmaster->home_dir)
+ snprintf(buf, sizeof(buf), "%s", Pmaster->home_dir);
+ else
+ snprintf(buf, sizeof(buf), "%c:\\", _getdrive() + 'A' - 1);
+#endif /* _WINDOWS */
+ hlen = strlen(buf);
+ if((home = (char *)malloc((hlen + 1) * sizeof(char))) == NULL){
+ emlwrite("Problem allocating space for home dir", NULL);
+ return(0);
+ }
+
+ strncpy(home, buf, hlen);
+ home[hlen] = '\0';
+ }
+
+ if(l)
+ *l = hlen;
+
+ return(home);
+}
+
+
+/*
+ * homeless - returns true if given file does not reside in the current
+ * user's home directory tree.
+ */
+int
+homeless(char *f)
+{
+ char *home;
+ int len;
+
+ home = gethomedir(&len);
+ return(strncmp(home, f, len));
+}
+
+
+/*
+ * getfnames - return all file names in the given directory in a single
+ * malloc'd string. n contains the number of names
+ */
+char *
+getfnames(char *dn, char *pat, int *n, char *e, size_t elen)
+{
+ size_t l, avail, alloced, incr = 1024;
+ char *names, *np, *p;
+ struct stat sbuf;
+#if defined(ct)
+ FILE *dirp;
+ char fn[DIRSIZ+1];
+#else
+#ifdef _WINDOWS
+ char buf[NLINE+1];
+ struct _finddata_t dbuf;
+ long findrv;
+#else
+ DIR *dirp; /* opened directory */
+#endif
+#endif
+#if defined(ct) || !defined(_WINDOWS)
+ struct dirent *dp;
+#endif
+
+ *n = 0;
+
+ if(our_stat(dn, &sbuf) < 0){
+ switch(errno){
+ case ENOENT : /* File not found */
+ if(e)
+ snprintf(e, elen, "\007File not found: \"%s\"", dn);
+
+ break;
+#ifdef ENAMETOOLONG
+ case ENAMETOOLONG : /* Name is too long */
+ if(e)
+ snprintf(e, elen, "\007File name too long: \"%s\"", dn);
+
+ break;
+#endif
+ default: /* Some other error */
+ if(e)
+ snprintf(e, elen, "\007Error getting file info: \"%s\"", dn);
+
+ break;
+ }
+ return(NULL);
+ }
+ else{
+ /*
+ * We'd like to use 512 * st_blocks as an initial estimate but
+ * some systems have a stat struct with no st_blocks in it.
+ */
+ avail = alloced = MAX(sbuf.st_size, incr);
+ if((sbuf.st_mode&S_IFMT) != S_IFDIR){
+ if(e)
+ snprintf(e, elen, "\007Not a directory: \"%s\"", dn);
+
+ return(NULL);
+ }
+ }
+
+ if((names=(char *)malloc(alloced * sizeof(char))) == NULL){
+ if(e)
+ snprintf(e, elen, "\007Can't malloc space for file names");
+
+ return(NULL);
+ }
+
+#ifndef _WINDOWS
+ errno = 0;
+ if((dirp=opendir(fname_to_locale(dn))) == NULL){
+ if(e)
+ snprintf(e, elen, "\007Can't open \"%s\": %s", dn, errstr(errno));
+
+ free((char *)names);
+ return(NULL);
+ }
+#endif
+
+ np = names;
+
+#if defined(ct)
+ while(fread(&dp, sizeof(struct direct), 1, dirp) > 0) {
+ /* skip empty slots with inode of 0 */
+ if(dp.d_ino == 0)
+ continue;
+ (*n)++; /* count the number of active slots */
+ (void)strncpy(fn, dp.d_name, DIRSIZ);
+ fn[14] = '\0';
+ p = fname_to_utf8(fn);
+ while((*np++ = *p++) != '\0')
+ ;
+ }
+#else /* !defined(ct) */
+#ifdef _WINDOWS
+ strncpy(buf, dn, sizeof(buf));
+ buf[sizeof(buf)-1] = '\0';
+ snprintf(buf, sizeof(buf), "%s%s%s*%s%s", dn,
+ (dn[strlen(dn)-1] == '\\') ? "" : "\\",
+ (pat && *pat) ? pat : "",
+ (pat && *pat && strchr(pat, '.')) ? "" : ".",
+ (pat && *pat && strchr(pat, '.')) ? "" : "*");
+ if((findrv = _findfirst(buf, &dbuf)) < 0){
+ if(e)
+ sprintf(e, "Can't find first file in \"%s\"", dn);
+
+ free((char *) names);
+ return(NULL);
+ }
+
+ do {
+ p = fname_to_utf8(dbuf.name);
+#else /* UNIX */
+ while((dp = readdir(dirp)) != NULL){
+ p = fname_to_utf8(dp->d_name);
+ if(!pat || !*pat || !strncmp(p, pat, strlen(pat))){
+#endif /* UNIX */
+ (*n)++;
+ l = strlen(p);
+ while(avail < l+1){
+ char *oldnames;
+
+ alloced += incr;
+ avail += incr;
+ oldnames = names;
+ if((names=(char *)realloc((void *)names, alloced * sizeof(char)))
+ == NULL){
+ if(e)
+ snprintf(e, elen, "\007Can't malloc enough space for file names");
+
+ return(NULL);
+ }
+
+ np = names + (np-oldnames);
+ }
+
+ avail -= (l+1);
+
+ while((*np++ = *p++) != '\0')
+ ;
+#ifdef _WINDOWS
+ }
+ while(_findnext(findrv, &dbuf) == 0);
+#else /* UNIX */
+ }
+ }
+#endif /* UNIX */
+#endif /* !defined(ct) */
+
+#ifdef _WINDOWS
+ _findclose(findrv);
+#else
+ closedir(dirp); /* shut down */
+#endif
+ return(names);
+}
+
+
+/*
+ * fioperr - given the error number and file name, display error
+ */
+void
+fioperr(int e, char *f)
+{
+ EML eml;
+
+ eml.s = f;
+
+ switch(e){
+ case FIOFNF: /* File not found */
+ emlwrite("\007File \"%s\" not found", &eml);
+ break;
+ case FIOEOF: /* end of file */
+ emlwrite("\007End of file \"%s\" reached", &eml);
+ break;
+ case FIOLNG: /* name too long */
+ emlwrite("\007File name \"%s\" too long", &eml);
+ break;
+ case FIODIR: /* file is a directory */
+ emlwrite("\007File \"%s\" is a directory", &eml);
+ break;
+ case FIONWT:
+ emlwrite("\007Write permission denied: %s", &eml);
+ break;
+ case FIONRD:
+ emlwrite("\007Read permission denied: %s", &eml);
+ break;
+ case FIOPER:
+ emlwrite("\007Permission denied: %s", &eml);
+ break;
+ case FIONEX:
+ emlwrite("\007Execute permission denied: %s", &eml);
+ break;
+ default:
+ emlwrite("\007File I/O error: %s", &eml);
+ }
+}
+
+
+
+/*
+ * pfnexpand - pico's function to expand the given file name if there is
+ * a leading '~'. Converts the homedir from user's locale to UTF-8.
+ * Fn is assumed to already be UTF-8.
+ */
+char *
+pfnexpand(char *fn, size_t fnlen)
+{
+ register char *x, *y, *z;
+ char *home = NULL;
+#ifndef _WINDOWS
+ struct passwd *pw;
+ char name[50];
+
+ if(*fn == '~'){
+ for(x = fn+1, y = name;
+ *x != '/' && *x != '\0' && y-name < sizeof(name)-1;
+ *y++ = *x++)
+ ;
+
+ *y = '\0';
+ if(x == fn + 1){ /* ~/ */
+ if (!(home = (char *) getenv("HOME")))
+ if ((pw = getpwuid(geteuid())) != NULL)
+ home = pw->pw_dir;
+ }
+ else if(*name){ /* ~username/ */
+ if((pw = getpwnam(name)) != NULL)
+ home = pw->pw_dir;
+ }
+
+ if(!home || (strlen(home) + strlen(fn) >= fnlen))
+ return(NULL);
+#else /* _WINDOWS */
+ char name[_MAX_PATH];
+
+ if(*fn == '~' && *(x = fn + 1) == '\\') {
+ if(!(home = (char *) getenv("HOME"))
+ && getenv("HOMEDRIVE") && getenv("HOMEPATH"))
+ snprintf(home = name, sizeof(name), "%s%s",
+ (char *) getenv("HOMEDRIVE"), (char *) getenv("HOMEPATH"));
+#endif /* _WINDOWS */
+ home = fname_to_utf8(home);
+
+ /* make room for expanded path */
+ for(z = x + strlen(x), y = fn + strlen(x) + strlen(home);
+ z >= x;
+ *y-- = *z--)
+ ;
+
+ /* and insert the expanded address */
+ for(x = fn, y = home; *y != '\0' && x-fn < fnlen; *x++ = *y++)
+ ;
+ }
+
+ fn[fnlen-1] = '\0';
+
+ return(fn);
+}
+
+
+/*
+ * fixpath - make the given pathname into an absolute path, incoming
+ * name is UTF-8.
+ */
+void
+fixpath(char *name, size_t namelen)
+{
+#ifdef _WINDOWS
+ char file[_MAX_PATH];
+ int dr;
+
+ if(!namelen)
+ return;
+
+ /* return the full path of given file, so drive spec? */
+ if(name[1] == ':' && isalpha((unsigned char) name[0])){
+ if(name[2] != '\\'){ /* including path? */
+ dr = toupper((unsigned char)name[0]) - 'A' + 1;
+ if((void *)_getdcwd(dr, file, _MAX_PATH) != NULL){
+ if(file[strlen(file)-1] != '\\')
+ strncat(file, "\\", sizeof(file)-1-strlen(file));
+
+ /* add file name */
+ strncat(file, &name[2], sizeof(file)-1-strlen(file));
+ }
+ else
+ return;
+ }
+ else
+ return; /* fully qualified with drive and path! */
+ }
+ else if(name[0] == '\\' && name[1] != '\\') { /* no drive spec! */
+ sprintf(file, "%c:%.*s", _getdrive()+'A'-1, namelen-3, name);
+ }
+ else if(name[0] == '\\' && name[1] == '\\'){ /* Windows network drive */
+ return;
+ }
+ else{
+ if(Pmaster && !(gmode & MDCURDIR)){
+ strncpy(file, ((gmode & MDTREE) || opertree[0])
+ ? opertree : gethomedir(NULL), sizeof(file)-1);
+ file[sizeof(file)-1] = '\0';
+ }
+ else if(!_getcwd(file, sizeof(file))) /* no qualification */
+ return;
+
+ if(*name){ /* if name, append it */
+ if(*file && file[strlen(file)-1] != '\\')
+ strncat(file, "\\", sizeof(file)-1-strlen(file));
+
+ strncat(file, name, sizeof(file)-1-strlen(file));
+ }
+ }
+
+ strncpy(name, file, namelen-1); /* copy back to real buffer */
+ name[namelen-1] = '\0'; /* tie off just in case */
+#else /* UNIX */
+ char *shft;
+
+ /* filenames relative to ~ */
+ if(!((name[0] == '/')
+ || (name[0] == '.'
+ && (name[1] == '/' || (name[1] == '.' && name[2] == '/'))))){
+ if(Pmaster && !(gmode&MDCURDIR)
+ && (*name != '~' && strlen(name)+2 < namelen)){
+
+ if(gmode&MDTREE && strlen(name)+strlen(opertree)+1 < namelen){
+ int off = strlen(opertree);
+
+ for(shft = strchr(name, '\0'); shft >= name; shft--)
+ shft[off+1] = *shft;
+
+ strncpy(name, opertree, MIN(off,namelen-1));
+ name[MIN(off,namelen-1)] = '/';
+ }
+ else{
+ for(shft = strchr(name, '\0'); shft >= name; shft--)
+ shft[2] = *shft;
+
+ name[0] = '~';
+ name[1] = '/';
+ }
+ }
+
+ pfnexpand(name, namelen);
+ }
+#endif /* UNIX */
+}
+
+
+/*
+ * compresspath - given a base path and an additional directory, collapse
+ * ".." and "." elements and return absolute path (appending
+ * base if necessary).
+ *
+ * returns 1 if OK,
+ * 0 if there's a problem
+ * new path, by side effect, if things went OK
+ */
+int
+compresspath(char *base, char *path, size_t pathlen)
+{
+ register int i;
+ int depth = 0;
+ char *p;
+ char *stack[32];
+ char pathbuf[NLINE];
+
+#define PUSHD(X) (stack[depth++] = X)
+#define POPD() ((depth > 0) ? stack[--depth] : "")
+
+#ifndef _WINDOWS
+ if(*path == '~'){
+ fixpath(path, pathlen);
+ strncpy(pathbuf, path, sizeof(pathbuf));
+ pathbuf[sizeof(pathbuf)-1] = '\0';
+ }
+ else if(*path != C_FILESEP)
+ snprintf(pathbuf, sizeof(pathbuf), "%s%c%s", base, C_FILESEP, path);
+ else{
+ strncpy(pathbuf, path, sizeof(pathbuf));
+ pathbuf[sizeof(pathbuf)-1] = '\0';
+ }
+#else /* _WINDOWS */
+ strncpy(pathbuf, path, sizeof(pathbuf));
+ pathbuf[sizeof(pathbuf)-1] = '\0';
+ fixpath(pathbuf, sizeof(pathbuf));
+#endif /* _WINDOWS */
+
+ p = &pathbuf[0];
+ for(i=0; pathbuf[i] != '\0'; i++){ /* pass thru path name */
+ if(pathbuf[i] == C_FILESEP){
+ if(p != pathbuf)
+ PUSHD(p); /* push dir entry */
+
+ p = &pathbuf[i+1]; /* advance p */
+ pathbuf[i] = '\0'; /* cap old p off */
+ continue;
+ }
+
+ if(pathbuf[i] == '.'){ /* special cases! */
+ if(pathbuf[i+1] == '.' /* parent */
+ && (pathbuf[i+2] == C_FILESEP || pathbuf[i+2] == '\0')){
+ if(!strcmp(POPD(), "")) /* bad news! */
+ return(0);
+
+ i += 2;
+ p = (pathbuf[i] == '\0') ? "" : &pathbuf[i+1];
+ }
+ else if(pathbuf[i+1] == C_FILESEP || pathbuf[i+1] == '\0'){
+ i++;
+ p = (pathbuf[i] == '\0') ? "" : &pathbuf[i+1];
+ }
+ }
+ }
+
+ if(*p != '\0')
+ PUSHD(p); /* get last element */
+
+ path[0] = '\0';
+ for(i = 0; i < depth; i++){
+ strncat(path, S_FILESEP, pathlen-strlen(path)-1);
+ path[pathlen-1] = '\0';
+ strncat(path, stack[i], pathlen-strlen(path)-1);
+ path[pathlen-1] = '\0';
+ }
+
+ return(1); /* everything's ok */
+}
+
+
+
+/*
+ * tmpname - return a temporary file name in the given buffer, the filename
+ * is in the directory dir unless dir is NULL. The file did not exist at
+ * the time of the temp_nam call, but was created by temp_nam.
+ */
+void
+tmpname(char *dir, char *name)
+{
+ char *t;
+#ifndef _WINDOWS
+ if((t = temp_nam((dir && *dir) ? dir : NULL, "pico.")) != NULL){
+#else /* _WINDOWS */
+ char tmp[_MAX_PATH];
+
+ if(!((dir && *dir) ||
+ (dir = getenv("TMPDIR")) ||
+ (dir = getenv("TMP")) ||
+ (dir = getenv("TEMP"))))
+ if(!(getcwd(dir = tmp, _MAX_PATH)
+ && fexist(dir, "w", (off_t *) NULL) == FIOSUC))
+ dir = "c:\\";
+
+ if((t = temp_nam_ext(dir, "ae", "txt")) != NULL){
+#endif /* _WINDOWS */
+ strncpy(name, t, NFILEN-1);
+ name[NFILEN-1] = '\0';
+ free((void *)t);
+ }
+ else {
+ emlwrite("Unable to construct temp file name", NULL);
+ name[0] = '\0';
+ }
+}
+
+
+/*
+ * Take a file name, and from it
+ * fabricate a buffer name. This routine knows
+ * about the syntax of file names on the target system.
+ * I suppose that this information could be put in
+ * a better place than a line of code.
+ */
+void
+makename(char bname[], char fname[])
+{
+ register char *cp1;
+ register char *cp2;
+
+ cp1 = &fname[0];
+ while (*cp1 != 0)
+ ++cp1;
+
+ while (cp1!=&fname[0] && cp1[-1]!=C_FILESEP)
+ --cp1;
+
+ cp2 = &bname[0];
+ while (cp2!=&bname[NBUFN-1] && *cp1!=0 && *cp1!=';')
+ *cp2++ = *cp1++;
+
+ *cp2 = 0;
+}
+
+
+/*
+ * copy - copy contents of file 'a' into a file named 'b'. Return error
+ * if either isn't accessible or is a directory
+ */
+int
+copy(char *a, char *b)
+{
+ int in, out, n, rv = 0;
+ char *cb;
+ struct stat tsb, fsb;
+ EML eml;
+ extern int errno;
+
+ if(our_stat(a, &fsb) < 0){ /* get source file info */
+ eml.s = errstr(errno);
+ emlwrite("Can't Copy: %s", &eml);
+ return(-1);
+ }
+
+ if(!(fsb.st_mode&S_IREAD)){ /* can we read it? */
+ eml.s = a;
+ emlwrite("\007Read permission denied: %s", &eml);
+ return(-1);
+ }
+
+ if((fsb.st_mode&S_IFMT) == S_IFDIR){ /* is it a directory? */
+ eml.s = a;
+ emlwrite("\007Can't copy: %s is a directory", &eml);
+ return(-1);
+ }
+
+ if(our_stat(b, &tsb) < 0){ /* get dest file's mode */
+ switch(errno){
+ case ENOENT:
+ break; /* these are OK */
+ default:
+ eml.s = errstr(errno);
+ emlwrite("\007Can't Copy: %s", &eml);
+ return(-1);
+ }
+ }
+ else{
+ if(!(tsb.st_mode&S_IWRITE)){ /* can we write it? */
+ eml.s = b;
+ emlwrite("\007Write permission denied: %s", &eml);
+ return(-1);
+ }
+
+ if((tsb.st_mode&S_IFMT) == S_IFDIR){ /* is it directory? */
+ eml.s = b;
+ emlwrite("\007Can't copy: %s is a directory", &eml);
+ return(-1);
+ }
+
+ if(fsb.st_dev == tsb.st_dev && fsb.st_ino == tsb.st_ino){
+ emlwrite("\007Identical files. File not copied", NULL);
+ return(-1);
+ }
+ }
+
+ if((in = our_open(a, O_RDONLY|O_BINARY, 0600)) < 0){
+ eml.s = errstr(errno);
+ emlwrite("Copy Failed: %s", &eml);
+ return(-1);
+ }
+
+ if((out=our_creat(b, fsb.st_mode&0xfff)) < 0){
+ eml.s = errstr(errno);
+ emlwrite("Can't Copy: %s", &eml);
+ close(in);
+ return(-1);
+ }
+
+ if((cb = (char *)malloc(NLINE*sizeof(char))) == NULL){
+ emlwrite("Can't allocate space for copy buffer!", NULL);
+ close(in);
+ close(out);
+ return(-1);
+ }
+
+ while(1){ /* do the copy */
+ if((n = read(in, cb, NLINE)) < 0){
+ eml.s = errstr(errno);
+ emlwrite("Can't Read Copy: %s", &eml);
+ rv = -1;
+ break; /* get out now */
+ }
+
+ if(n == 0) /* done! */
+ break;
+
+ if(write(out, cb, n) != n){
+ eml.s = errstr(errno);
+ emlwrite("Can't Write Copy: %s", &eml);
+ rv = -1;
+ break;
+ }
+ }
+
+ free(cb);
+ close(in);
+ close(out);
+ return(rv);
+}
+
+
+/*
+ * Open a file for writing. Return TRUE if all is well, and FALSE on error
+ * (cannot create).
+ */
+int
+ffwopen(char *fn, int readonly)
+{
+ extern FIOINFO g_pico_fio;
+#ifndef _WINDOWS
+ int fd;
+ EML eml;
+#endif
+#ifndef MODE_READONLY
+#define MODE_READONLY (0600)
+#endif
+
+ /*
+ * Call open() by hand since we don't want O_TRUNC -- it'll
+ * screw over-quota users. The strategy is to overwrite the
+ * existing file's data and call ftruncate before close to lop
+ * off bytes
+ */
+
+ g_pico_fio.flags = FIOINFO_WRITE;
+ g_pico_fio.name = fn;
+#ifndef _WINDOWS
+ if((fd = our_open(fn, O_CREAT|O_WRONLY, readonly ? MODE_READONLY : 0666)) >= 0
+ && (g_pico_fio.fp = fdopen(fd, "w")) != NULL
+ && fseek(g_pico_fio.fp, 0L, 0) == 0)
+ return (FIOSUC);
+
+
+ eml.s = errstr(errno);
+ emlwrite("Cannot open file for writing: %s", &eml);
+ return (FIOERR);
+#else /* _WINDOWS */
+ if ((g_pico_fio.fp = our_fopen(fn, "w")) == NULL) {
+ emlwrite("Cannot open file for writing", NULL);
+ return (FIOERR);
+ }
+
+#ifdef MODE_READONLY
+ if(readonly)
+ our_chmod(fn, MODE_READONLY); /* fix access rights */
+#endif
+
+ return (FIOSUC);
+#endif /* _WINDOWS */
+}
+
+
+/*
+ * Close a file. Should look at the status in all systems.
+ */
+int
+ffclose(void)
+{
+ extern FIOINFO g_pico_fio;
+#ifndef _WINDOWS
+ EML eml;
+
+ errno = 0;
+ if((g_pico_fio.flags & FIOINFO_WRITE)
+ && (fflush(g_pico_fio.fp) == EOF
+ || ftruncate(fileno(g_pico_fio.fp),
+ (off_t) ftell(g_pico_fio.fp)) < 0)){
+ eml.s = errstr(errno);
+ emlwrite("\007Error preparing to close file: %s", &eml);
+ sleep(5);
+ }
+
+ if (fclose(g_pico_fio.fp) == EOF) {
+ eml.s = errstr(errno);
+ emlwrite("\007Error closing file: %s", &eml);
+ return(FIOERR);
+ }
+#else /* _WINDOWS */
+ if (fclose(g_pico_fio.fp) != FALSE) {
+ emlwrite("Error closing file", NULL);
+ return(FIOERR);
+ }
+#endif /* _WINDOWS */
+
+ return(FIOSUC);
+}
+
+
+
+#define EXTEND_BLOCK 1024
+
+
+/*
+ * ffelbowroom - make sure the destination's got enough room to receive
+ * what we're about to write...
+ */
+int
+ffelbowroom(void)
+{
+#ifndef _WINDOWS
+ register LINE *lp;
+ register long n;
+ int x;
+ char s[EXTEND_BLOCK], *errstring = NULL;
+ struct stat fsbuf;
+ extern FIOINFO g_pico_fio;
+
+ /* Figure out how much room do we need */
+ /* first, what's total */
+ for(n=0L, lp=lforw(curbp->b_linep); lp != curbp->b_linep; lp=lforw(lp))
+ n += (llength(lp) + 1);
+
+ errno = 0; /* make sure previous error are cleared */
+
+ if(fstat(fileno(g_pico_fio.fp), &fsbuf) == 0){
+ n -= fsbuf.st_size;
+
+ if(n > 0L){ /* must be growing file, extend it */
+ memset(s, 'U', EXTEND_BLOCK);
+ if(fseek(g_pico_fio.fp, fsbuf.st_size, 0) == 0){
+ for( ; n > 0L; n -= EXTEND_BLOCK){
+ x = (n < EXTEND_BLOCK) ? (int) n : EXTEND_BLOCK;
+ if(fwrite(s, x * sizeof(char), 1, g_pico_fio.fp) != 1){
+ errstring = errstr(errno);
+ break;
+ }
+ }
+
+ if(!errstring
+ && (fflush(g_pico_fio.fp) == EOF
+ || fsync(fileno(g_pico_fio.fp)) < 0))
+ errstring = errstr(errno);
+
+ if(errstring) /* clean up */
+ (void) ftruncate(fileno(g_pico_fio.fp), (off_t) fsbuf.st_size);
+ else if(fseek(g_pico_fio.fp, 0L, 0) != 0)
+ errstring = errstr(errno);
+ }
+ else
+ errstring = errstr(errno);
+ }
+ }
+ else
+ errstring = errstr(errno);
+
+ if(errstring){
+ snprintf(s, sizeof(s), "Error writing to %s: %s", g_pico_fio.name, errstring);
+ emlwrite(s, NULL);
+ (void) fclose(g_pico_fio.fp);
+ return(FALSE);
+ }
+#endif /* UNIX */
+ return(TRUE);
+}
diff --git a/pico/osdep/filesys.h b/pico/osdep/filesys.h
new file mode 100644
index 00000000..eadbdc86
--- /dev/null
+++ b/pico/osdep/filesys.h
@@ -0,0 +1,43 @@
+/*
+ * $Id: filesys.h 764 2007-10-23 23:44:49Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef PICO_OSDEP_FILESYS_INCLUDED
+#define PICO_OSDEP_FILESYS_INCLUDED
+
+
+#include "../../pith/osdep/canaccess.h" /* for *_ACCESS */
+
+
+/* exported prototypes */
+int fexist(char *, char *, off_t *);
+int isdir(char *, long *, time_t *);
+char *gethomedir(int *);
+int homeless(char *);
+char *getfnames(char *, char *, int *, char *, size_t);
+void fioperr(int, char *);
+char *pfnexpand(char *, size_t);
+void fixpath(char *, size_t);
+int compresspath(char *, char *, size_t);
+void tmpname(char *, char *);
+void makename(char *, char *);
+int copy(char *, char *);
+int ffwopen(char *, int);
+int ffclose(void);
+int ffelbowroom(void);
+
+
+#endif /* PICO_OSDEP_FILESYS_INCLUDED */
diff --git a/pico/osdep/fsync.c b/pico/osdep/fsync.c
new file mode 100644
index 00000000..14a15d29
--- /dev/null
+++ b/pico/osdep/fsync.c
@@ -0,0 +1,31 @@
+/*
+ * Program: File sync emulator
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+
+
+#ifndef HAVE_FSYNC
+/* Emulator for BSD fsync() call
+ * Accepts: file name
+ * Returns: 0 if successful, -1 if failure
+ */
+int
+our_fsync(int fd)
+{
+ sync();
+ return 0;
+}
+#endif /* !HAVE_FSYNC */
+
diff --git a/pico/osdep/fsync.h b/pico/osdep/fsync.h
new file mode 100644
index 00000000..e7df6f93
--- /dev/null
+++ b/pico/osdep/fsync.h
@@ -0,0 +1,30 @@
+/*
+ * $Id: fsync.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef PICO_OSDEP_FSYNC_INCLUDED
+#define PICO_OSDEP_FSYNC_INCLUDED
+
+#ifndef HAVE_FSYNC
+
+#define fsync(F) our_fsync(F)
+
+/* exported prototypes */
+int our_fsync(int);
+
+
+#endif /* !HAVE_FSYNC */
+
+#endif /* PICO_OSDEP_FSYNC_INCLUDED */
diff --git a/pico/osdep/getkey.c b/pico/osdep/getkey.c
new file mode 100644
index 00000000..b55dd092
--- /dev/null
+++ b/pico/osdep/getkey.c
@@ -0,0 +1,565 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: getkey.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "../estruct.h"
+#include "../mode.h"
+#include "../pico.h"
+#include "../edef.h"
+#include "../efunc.h"
+#include "../keydefs.h"
+
+#include "tty.h"
+#include "getkey.h"
+#include "read.h"
+#include "mouse.h"
+
+#ifdef _WINDOWS
+#include "mswin.h"
+static int MapMSKEYtoPK(int c);
+#endif /* _WINDOWS */
+
+
+/* internal declarations */
+static int timeo = 0;
+
+/* these next two are declared in pith/conf.h */
+int
+set_input_timeout(int t)
+{
+ int oldtimeo = timeo;
+
+ timeo = t;
+ return(oldtimeo);
+}
+
+int
+get_input_timeout(void)
+{
+ return(timeo);
+}
+
+
+#ifndef _WINDOWS
+
+
+/* internal prototypes */
+void bail(void);
+int ReadyForKey(int);
+
+
+
+void
+bail(void)
+{
+ sleep(30); /* see if os receives SIGHUP */
+ kill(getpid(), SIGHUP); /* eof or bad error */
+}
+
+
+#if TYPEAH
+/*
+ * typahead - Check to see if any characters are already in the
+ * keyboard buffer
+ */
+int
+typahead(void)
+{
+ int x; /* holds # of pending chars */
+
+ return((ioctl(0,FIONREAD,&x) < 0) ? 0 : x);
+}
+#endif /* TYPEAH */
+
+
+
+/*
+ * ReadyForKey - return true if there's no timeout or we're told input
+ * is available...
+ */
+int
+ReadyForKey(int timeout)
+{
+ switch(input_ready(timeout)){
+ case READY_TO_READ:
+ return(1);
+ break;
+
+ case NO_OP_COMMAND:
+ case NO_OP_IDLE:
+ case READ_INTR:
+ return(0);
+
+ case BAIL_OUT:
+ case PANIC_NOW:
+ emlwrite("\007Problem reading from keyboard!", NULL);
+ kill(getpid(), SIGHUP); /* Bomb out (saving our work)! */
+ /* no return */
+ }
+
+ /* can't happen */
+ return(0);
+}
+
+
+
+/*
+ * GetKey - Read in a key.
+ * Do the standard keyboard preprocessing. Convert the keys to the internal
+ * character set. Resolves escape sequences and returns no-op if global
+ * timeout value exceeded.
+ */
+UCS
+GetKey(void)
+{
+ UCS ch, status, cc;
+
+ if(!ReadyForKey(FUDGE-5))
+ return(NODATA);
+
+ switch(status = kbseq(simple_ttgetc, NULL, bail, input_cs, &ch)){
+ case 0: /* regular character */
+ break;
+
+ case KEY_DOUBLE_ESC:
+ /*
+ * Special hack to get around comm devices eating control characters.
+ */
+ if(!ReadyForKey(5))
+ return(BADESC); /* user typed ESC ESC, then stopped */
+ else
+ switch(status = kbseq(simple_ttgetc, NULL, bail, input_cs, &ch)){
+ case KEY_UP :
+ case KEY_DOWN :
+ case KEY_RIGHT :
+ case KEY_LEFT :
+ case KEY_PGUP :
+ case KEY_PGDN :
+ case KEY_HOME :
+ case KEY_END :
+ case KEY_DEL :
+ case F1 :
+ case F2 :
+ case F3 :
+ case F4 :
+ case F5 :
+ case F6 :
+ case F7 :
+ case F8 :
+ case F9 :
+ case F10 :
+ case F11 :
+ case F12 :
+ return(CTRL | status);
+ break;
+
+ case 0: /* regular character */
+ break;
+
+ default: /* punt the whole thing */
+ (*term.t_beep)();
+ return(BADESC);
+ break;
+ }
+
+ ch &= 0x7f;
+ if(isdigit((unsigned char)ch)){
+ int n = 0, i = ch - '0';
+
+ if(i < 0 || i > 2)
+ return(BADESC); /* bogus literal char value */
+
+ while(n++ < 2){
+ if(!ReadyForKey(5)
+ || (!isdigit((unsigned char) (ch =
+ (*term.t_getchar)(NODATA, NULL, bail)))
+ || (n == 1 && i == 2 && ch > '5')
+ || (n == 2 && i == 25 && ch > '5'))){
+ return(BADESC);
+ }
+
+ i = (i * 10) + (ch - '0');
+ }
+
+ ch = i;
+ }
+ else{
+ if(islower((unsigned char)ch)) /* canonicalize if alpha */
+ ch = toupper((unsigned char)ch);
+
+ return((isalpha((unsigned char)ch) || ch == '@'
+ || (ch >= '[' && ch <= '_'))
+ ? (CTRL | ch) : ((ch == ' ') ? (CTRL | '@') : ch));
+ }
+
+ break;
+
+#ifdef MOUSE
+ case KEY_XTERM_MOUSE:
+ {
+ /*
+ * Special hack to get mouse events from an xterm.
+ * Get the details, then pass it past the keymenu event
+ * handler, and then to the installed handler if there
+ * is one...
+ */
+ static int down = 0;
+ int x, y, button;
+ unsigned long cmd;
+
+ button = (*term.t_getchar)(NODATA, NULL, bail) & 0x03;
+
+ x = (*term.t_getchar)(NODATA, NULL, bail) - '!';
+ y = (*term.t_getchar)(NODATA, NULL, bail) - '!';
+
+ if(button == 0){
+ down = 1;
+ if(checkmouse(&cmd, 1, x, y))
+ return((UCS) cmd);
+ }
+ else if(down && button == 3){
+ down = 0;
+ if(checkmouse(&cmd, 0, x, y))
+ return((UCS) cmd);
+ }
+
+ return(NODATA);
+ }
+
+ break;
+#endif /* MOUSE */
+
+ case KEY_UP :
+ case KEY_DOWN :
+ case KEY_RIGHT :
+ case KEY_LEFT :
+ case KEY_PGUP :
+ case KEY_PGDN :
+ case KEY_HOME :
+ case KEY_END :
+ case KEY_DEL :
+ case F1 :
+ case F2 :
+ case F3 :
+ case F4 :
+ case F5 :
+ case F6 :
+ case F7 :
+ case F8 :
+ case F9 :
+ case F10 :
+ case F11 :
+ case F12 :
+ return(status);
+
+ case CTRL_KEY_UP :
+ return(CTRL | KEY_UP);
+ case CTRL_KEY_DOWN :
+ return(CTRL | KEY_DOWN);
+ case CTRL_KEY_RIGHT :
+ return(CTRL | KEY_RIGHT);
+ case CTRL_KEY_LEFT :
+ return(CTRL | KEY_LEFT);
+
+ case KEY_SWALLOW_Z:
+ status = BADESC;
+ case KEY_SWAL_UP:
+ case KEY_SWAL_DOWN:
+ case KEY_SWAL_LEFT:
+ case KEY_SWAL_RIGHT:
+ do
+ if(!ReadyForKey(2)){
+ status = BADESC;
+ break;
+ }
+ while(!strchr("~qz", (*term.t_getchar)(NODATA, NULL, bail)));
+
+ return((status == BADESC)
+ ? status
+ : status - (KEY_SWAL_UP - KEY_UP));
+ break;
+
+ case KEY_KERMIT:
+ do{
+ cc = ch;
+ if(!ReadyForKey(2))
+ return(BADESC);
+ else
+ ch = (*term.t_getchar)(NODATA, NULL, bail) & 0x7f;
+ }while(cc != '\033' && ch != '\\');
+
+ ch = NODATA;
+ break;
+
+ case BADESC:
+ (*term.t_beep)();
+ return(status);
+
+ default: /* punt the whole thing */
+ (*term.t_beep)();
+ break;
+ }
+
+ if (ch >= 0x00 && ch <= 0x1F) /* C0 control -> C- */
+ ch = CTRL | (ch+'@');
+
+ return(ch);
+}
+
+
+/*
+ * kbseq - looks at an escape sequence coming from the keyboard and
+ * compares it to a trie of known keyboard escape sequences, and
+ * returns the function bound to the escape sequence.
+ *
+ * Args: getcfunc -- Function to get a single character from stdin,
+ * called with the next two arguments to this
+ * function as its arguments.
+ * recorder -- If non-NULL, function used to record keystroke.
+ * bail_handler -- Function used to bail out on read error.
+ * c -- Pointer to returned character.
+ *
+ * Returns: BADESC
+ * The escaped function.
+ * 0 if a regular char with char stuffed in location c.
+ */
+UCS
+kbseq(int (*getcfunc)(int (*recorder)(int ), void (*bail_handler)(void )),
+ int (*recorder)(int),
+ void (*bail_handler)(void),
+ void *data,
+ UCS *ch)
+{
+ unsigned char c;
+ int first = 1;
+ KBESC_T *current;
+
+ current = kbesc;
+ if(current == NULL) /* bag it */
+ return(BADESC);
+
+ while(1){
+ c = (*getcfunc)(recorder, bail_handler);
+
+ while(current->value != c){
+ if(current->left == NULL){ /* NO MATCH */
+ if(first){
+ unsigned long octets_so_far, remaining_octets;
+ unsigned char *inputp;
+ UCS ucs;
+ unsigned char inputbuf[20];
+
+ /*
+ * Regular character.
+ * Read enough bytes to make up a character and convert it to UCS-4.
+ */
+ memset(inputbuf, 0, sizeof(inputbuf));
+ inputbuf[0] = c;
+ octets_so_far = 1;
+ for(;;){
+ remaining_octets = octets_so_far;
+ inputp = inputbuf;
+ ucs = mbtow(data, &inputp, &remaining_octets);
+ switch(ucs){
+ case CCONV_BADCHAR:
+ /*
+ * Not really a BADESC but that ought to
+ * be sufficient. We can add another type if
+ * we need to.
+ */
+ return(BADESC);
+
+ case CCONV_NEEDMORE:
+ if(octets_so_far >= sizeof(inputbuf))
+ return(BADESC);
+
+ c = (*getcfunc)(recorder, bail_handler);
+ inputbuf[octets_so_far++] = c;
+ break;
+
+ default:
+ /* got a good UCS-4 character */
+ *ch = ucs;
+ return(0);
+ }
+ }
+
+ /* NOTREACHED */
+ return(0);
+ }
+ else
+ return(BADESC);
+ }
+ current = current->left;
+ }
+
+ if(current->down == NULL) /* match!!!*/
+ return(current->func);
+ else
+ current = current->down;
+
+ first = 0;
+ }
+}
+
+
+#define newnode() (KBESC_T *)malloc(sizeof(KBESC_T))
+
+/*
+ * kpinsert - insert a keystroke escape sequence into the global search
+ * structure.
+ */
+void
+kpinsert(char *kstr, int kval, int termcap_wins)
+{
+ register char *buf;
+ register KBESC_T *temp;
+ register KBESC_T *trail;
+
+ if(kstr == NULL)
+ return;
+
+ /*
+ * Don't allow escape sequences that don't start with ESC unless
+ * termcap_wins. This is to protect against mistakes in termcap files.
+ */
+ if(!termcap_wins && *kstr != '\033')
+ return;
+
+ temp = trail = kbesc;
+ buf = kstr;
+
+ for(;;){
+ if(temp == NULL){
+ temp = newnode();
+ temp->value = *buf;
+ temp->func = 0;
+ temp->left = NULL;
+ temp->down = NULL;
+ if(kbesc == NULL)
+ kbesc = temp;
+ else
+ trail->down = temp;
+ }
+ else{ /* first entry */
+ while((temp != NULL) && (temp->value != *buf)){
+ trail = temp;
+ temp = temp->left;
+ }
+
+ if(temp == NULL){ /* add new val */
+ temp = newnode();
+ temp->value = *buf;
+ temp->func = 0;
+ temp->left = NULL;
+ temp->down = NULL;
+ trail->left = temp;
+ }
+ }
+
+ if(*(++buf) == '\0')
+ break;
+ else{
+ /*
+ * Ignore attempt to overwrite shorter existing escape sequence.
+ * That means that sequences with higher priority should be
+ * set up first, so if we want termcap sequences to override
+ * hardwired sequences, put the kpinsert calls for the
+ * termcap sequences first. (That's what you get if you define
+ * TERMCAP_WINS.)
+ */
+ if(temp->func != 0)
+ return;
+
+ trail = temp;
+ temp = temp->down;
+ }
+ }
+
+ /*
+ * Ignore attempt to overwrite longer sequences we are a prefix
+ * of (down != NULL) and exact same sequence (func != 0).
+ */
+ if(temp != NULL && temp->down == NULL && temp->func == 0)
+ temp->func = kval;
+}
+
+
+
+/*
+ * kbdestroy() - kills the key pad function key search tree
+ * and frees all lines associated with it
+ *
+ * Should be called with arg kbesc, the top of the tree.
+ */
+void
+kbdestroy(KBESC_T *kb)
+{
+ if(kb){
+ kbdestroy(kb->left);
+ kbdestroy(kb->down);
+ free((char *)kb);
+ kb = NULL;
+ }
+}
+
+#else /* _WINDOWS */
+
+/*
+ * Read in a key.
+ * Do the standard keyboard preprocessing. Convert the keys to the internal
+ * character set. Resolves escape sequences and returns no-op if global
+ * timeout value exceeded.
+ */
+UCS
+GetKey(void)
+{
+ UCS ch = 0;
+ long timein;
+
+
+ ch = NODATA;
+ timein = time(0L);
+
+ /*
+ * Main character processing loop.
+ */
+ while(!mswin_charavail()) {
+
+#ifdef MOUSE
+ /* Check Mouse. If we get a mouse event, convert to char
+ * event and return that. */
+ if (checkmouse (&ch,0,0,0)) {
+ curwp->w_flag |= WFHARD;
+ return (ch);
+ }
+#endif /* MOUSE */
+
+
+ /* Check Timeout. */
+ if(time(0L) >= timein+(FUDGE-10))
+ return(NODATA);
+ }
+
+
+ return (mswin_getc_fast());
+}
+
+#endif /* _WINDOWS */
diff --git a/pico/osdep/getkey.h b/pico/osdep/getkey.h
new file mode 100644
index 00000000..98d52aa7
--- /dev/null
+++ b/pico/osdep/getkey.h
@@ -0,0 +1,36 @@
+/*
+ * $Id: getkey.h 767 2007-10-24 00:03:59Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef PICO_OSDEP_GETKEY_INCLUDED
+#define PICO_OSDEP_GETKEY_INCLUDED
+
+
+#include <general.h>
+
+
+/* exported prototypes */
+UCS GetKey(void);
+void kpinsert(char *, int, int);
+#if TYPEAH
+int typahead(void);
+#endif /* TYPEAH */
+#ifndef _WINDOWS
+UCS kbseq(int (*getcfunc)(int (*recorder)(int ), void (*bail_handler)(void )),
+ int (*recorder)(int), void (*bail_handler)(void), void *, UCS *);
+void kbdestroy(KBESC_T *);
+#endif
+
+#endif /* PICO_OSDEP_GETKEY_INCLUDED */
diff --git a/pico/osdep/makefile.wnt b/pico/osdep/makefile.wnt
new file mode 100644
index 00000000..0eb3d1e7
--- /dev/null
+++ b/pico/osdep/makefile.wnt
@@ -0,0 +1,69 @@
+# $Id: makefile.wnt 14098 2005-10-03 18:54:13Z jpf@u.washington.edu $
+#
+# ========================================================================
+# Copyright 2006-2007 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+#
+#
+# Makefile for WIN NT version of libpicoosd.lib
+#
+#
+CC=cl
+RM=del
+CP=copy
+RC=rc
+
+#includes symbol info for debugging
+CDEBUG= #-Zi -Od
+LDEBUG= /DEBUG /DEBUGTYPE:CV
+
+STDCFLAGS= -I..\..\include -I..\..\regex -nologo -MT -DWIN32 -DDOS -D_WINDOWS -DJOB_CONTROL -DMSC_MALLOC
+
+CFLAGS= $(CDEBUG) $(STDCFLAGS) $(NET) $(EXTRACFLAGS)
+
+LFLAGS= $(LDEBUG) $(EXTRALDFLAGS)
+
+RCFLAGS= #-D_UNICODE -DUNICODE
+
+LIBER=lib
+LIBARGS=/nologo /verbose
+
+HFILES= ../../include/system.h ../../include/general.h \
+ altedit.h chkpoint.h color.h filesys.h fsync.h \
+ getkey.h mswin.h mswin_tw.h mswin_aspell.h mswin_spell.h mouse.h \
+ newmail.h popen.h read.h \
+ shell.h signals.h spell.h terminal.h truncate.h \
+ tty.h
+
+OFILES= altedit.obj chkpoint.obj color.obj filesys.obj fsync.obj \
+ getkey.obj msdlg.obj mswin.obj mswin_tw.obj mswin_aspell.obj mswin_spell.obj \
+ mouse.obj newmail.obj popen.obj \
+ read.obj shell.obj signals.obj spell.obj terminal.obj truncate.obj \
+ tty.obj
+
+all: libpicoosd.lib mswin.res
+
+.c.obj:
+ $(CC) -c $(CFLAGS) "$(MAKEDIR)"\$*.c
+
+$(OFILES): $(HFILES)
+
+libpicoosd.lib: $(OFILES)
+ $(RM) libpicoosd.lib || rem
+ $(LIBER) /out:libpicoosd.lib $(OFILES)
+
+mswin.res: mswin.rc pico.ico mswinhnd.cur resource.h
+ $(RC) $(RCFLAGS) /fo mswin.res mswin.rc
+
+clean:
+ $(RM) *.lib
+ $(RM) *.obj
+ $(RM) mswin.res
diff --git a/pico/osdep/mouse.c b/pico/osdep/mouse.c
new file mode 100644
index 00000000..b671f8c1
--- /dev/null
+++ b/pico/osdep/mouse.c
@@ -0,0 +1,446 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: mouse.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "../headers.h"
+
+#include "getkey.h"
+
+#ifdef _WINDOWS
+#include "mswin.h"
+#endif
+
+#include "mouse.h"
+
+#ifdef MOUSE
+
+
+#ifndef _WINDOWS
+
+/* useful definitions */
+#define XTERM_MOUSE_ON "\033[?1000h" /* DECSET with parm 1000 */
+#define XTERM_MOUSE_OFF "\033[?1000l" /* DECRST with parm 1000 */
+
+
+/* useful declarations */
+static int mexist = 0; /* is the mouse driver installed? */
+static unsigned mnoop;
+
+
+
+/* internal prototypes */
+void mouseon(void);
+void mouseoff(void);
+
+
+
+/*
+ * init_mouse - check for xterm and initialize mouse tracking if present...
+ */
+int
+init_mouse(void)
+{
+ if(mexist)
+ return(TRUE);
+
+ if(getenv("DISPLAY")){
+ mouseon();
+ kpinsert("\033[M", KEY_XTERM_MOUSE, 1);
+ return(mexist = TRUE);
+ }
+ else
+ return(FALSE);
+}
+
+
+/*
+ * end_mouse - clear xterm mouse tracking if present...
+ */
+void
+end_mouse(void)
+{
+ if(mexist){
+ mexist = 0; /* just see if it exists here. */
+ mouseoff();
+ }
+}
+
+
+/*
+ * mouseexist - function to let outsiders know if mouse is turned on
+ * or not.
+ */
+int
+mouseexist(void)
+{
+ return(mexist);
+}
+
+
+/*
+ * mouseon - call made available for programs calling pico to turn ON the
+ * mouse cursor.
+ */
+void
+mouseon(void)
+{
+ fputs(XTERM_MOUSE_ON, stdout);
+}
+
+
+/*
+ * mouseon - call made available for programs calling pico to turn OFF the
+ * mouse cursor.
+ */
+void
+mouseoff(void)
+{
+ fputs(XTERM_MOUSE_OFF, stdout);
+}
+
+
+/*
+ * checkmouse - look for mouse events in key menu and return
+ * appropriate value.
+ */
+int
+checkmouse(unsigned long *ch, int down, int mcol, int mrow)
+{
+ static int oindex;
+ int i = 0, rv = 0;
+ MENUITEM *mp;
+
+ if(!mexist || mcol < 0 || mrow < 0)
+ return(FALSE);
+
+ if(down) /* button down */
+ oindex = -1;
+
+ for(mp = mfunc; mp; mp = mp->next)
+ if(mp->action && M_ACTIVE(mrow, mcol, mp))
+ break;
+
+ if(mp){
+ unsigned long r;
+
+ r = (*mp->action)(down ? M_EVENT_DOWN : M_EVENT_UP,
+ mrow, mcol, M_BUTTON_LEFT, 0);
+ if(r){
+ *ch = r;
+ rv = TRUE;
+ }
+ }
+ else{
+ while(1){ /* see if we understand event */
+ if(i >= 12){
+ i = -1;
+ break;
+ }
+
+ if(M_ACTIVE(mrow, mcol, &menuitems[i]))
+ break;
+
+ i++;
+ }
+
+ if(down){ /* button down */
+ oindex = i; /* remember where */
+ if(i != -1
+ && menuitems[i].label_hiliter != NULL
+ && menuitems[i].val != mnoop) /* invert label */
+ (*menuitems[i].label_hiliter)(1, &menuitems[i]);
+ }
+ else{ /* button up */
+ if(oindex != -1){
+ if(i == oindex){
+ *ch = menuitems[i].val;
+ rv = TRUE;
+ }
+ }
+ }
+ }
+
+ /* restore label */
+ if(!down
+ && oindex != -1
+ && menuitems[oindex].label_hiliter != NULL
+ && menuitems[oindex].val != mnoop)
+ (*menuitems[oindex].label_hiliter)(0, &menuitems[oindex]);
+
+ return(rv);
+}
+
+
+/*
+ * invert_label - highlight the label of the given menu item.
+ */
+void
+invert_label(int state, MENUITEM *m)
+{
+ unsigned i, j;
+ int col_offset, savettrow, savettcol;
+ char *lp;
+
+ get_cursor(&savettrow, &savettcol);
+
+ /*
+ * Leave the command name bold
+ */
+ col_offset = (state || !(lp=strchr(m->label, ' '))) ? 0 : (lp - m->label);
+ movecursor((int)m->tl.r, (int)m->tl.c + col_offset);
+ flip_inv(state);
+
+ for(i = m->tl.r; i <= m->br.r; i++)
+ for(j = m->tl.c + col_offset; j <= m->br.c; j++)
+ if(i == m->lbl.r && j == m->lbl.c + col_offset && m->label){
+ lp = m->label + col_offset; /* show label?? */
+ while(*lp && j++ < m->br.c)
+ putc(*lp++, stdout);
+
+ continue;
+ }
+ else
+ putc(' ', stdout);
+
+ if(state)
+ flip_inv(FALSE);
+
+ movecursor(savettrow, savettcol);
+}
+
+#else /* _WINDOWS */
+
+#define MOUSE_BUTTONS 3
+
+static int mexist = 0; /* is the mouse driver installed? */
+static int nbuttons; /* number of buttons on the mouse */
+static unsigned mnoop;
+
+/*
+ * init_mouse - check for and initialize mouse driver...
+ */
+
+int
+init_mouse(void)
+{
+ nbuttons = MOUSE_BUTTONS;
+ return (mexist = TRUE); /* Mouse always exists under windows */
+}
+
+/*
+ * end_mouse - a no-op on Windows
+ */
+void
+end_mouse(void)
+{
+}
+
+/*
+ * mouseexist - function to let outsiders know if mouse is turned on
+ * or not.
+ */
+int
+mouseexist(void)
+{
+ return(mexist);
+}
+
+/*
+ * checkmouse - Check mouse and return maped command.
+ *
+ * EXPORTED to pico.
+ * NOTE: "down", "xxx", and "yyy" aren't used under windows.
+ */
+int
+checkmouse (unsigned long *ch, int ddd, int xxx, int yyy)
+{
+ static int oindex; /* Index of previous mouse down. */
+ int mcol; /* current mouse column */
+ int mrow; /* current mouse row */
+ unsigned long r;
+ int rv = 0; /* TRUE when we have something to return. */
+ MEvent mouse;
+ int i = 0;
+ MENUITEM *mp;
+
+
+ *ch = 0;
+
+ /* Mouse installed? */
+ if (!mexist)
+ return (FALSE);
+
+ if (!mswin_getmouseevent (&mouse))
+ return (FALSE);
+
+
+ /* Location of mouse event. */
+ mcol = mouse.nColumn;
+ mrow = mouse.nRow;
+
+
+
+ /*
+ * If there is a tracking function it gets all the mouse events
+ * reguardless of where they occur.
+ */
+ if (mtrack != NULL) {
+ r = mtrack (mouse.event, mrow, mcol, mouse.button, mouse.keys);
+ if (r & 0xffff){
+ *ch = r;
+ rv = TRUE;
+ }
+ return (rv);
+ }
+
+
+
+
+ /* Mouse down or up? */
+ if (mouse.event == M_EVENT_DOWN) { /* button down */
+ oindex = -1; /* No Previous mouse down. */
+ }
+
+
+ /* In special screen region? */
+ for(mp = mfunc; mp; mp = mp->next)
+ if(mp->action && M_ACTIVE(mrow, mcol, mp))
+ break;
+
+ if(mp){
+
+ r = (*mp->action)(mouse.event, mrow, mcol, mouse.button, mouse.keys);
+ if (r){
+ *ch = r;
+ rv = TRUE;
+ }
+ }
+ else if(mouse.button == M_BUTTON_LEFT){
+
+ /* In any of the menuitems? */
+ while(1){ /* see if we understand event */
+ if(i >= 12){
+ i = -1; /* Not Found. */
+ break;
+ }
+
+ if(M_ACTIVE(mrow, mcol, &menuitems[i]))
+ break; /* Found. */
+
+ i++; /* Next. */
+ }
+
+ /* Now, was that a mouse down or mouse up? */
+ if (mouse.event == M_EVENT_DOWN) { /* button down */
+ oindex = i; /* remember where */
+ if(i != -1){ /* invert label */
+ if(menuitems[i].label_hiliter != NULL)
+ (*menuitems[i].label_hiliter)(1, &menuitems[i]);
+ else
+ invert_label(1, &menuitems[i]);
+ }
+ }
+ else if (mouse.event == M_EVENT_UP) {/* button up */
+ if (oindex != -1) { /* If up in menu item. */
+ if (i == oindex){ /* And same item down in. */
+ *ch = menuitems[i].val; /* Return menu character. */
+ rv = 1;
+ }
+ }
+ }
+ }
+ else if(mouse.button == M_BUTTON_RIGHT){
+ if (mouse.event == M_EVENT_UP) {
+ mswin_keymenu_popup();
+ }
+ }
+
+ /* If this is mouse up AND there was a mouse down in a menu item
+ * then uninvert that menu item */
+ if(mouse.event == M_EVENT_UP && oindex != -1){
+ if(menuitems[oindex].label_hiliter != NULL)
+ (*menuitems[oindex].label_hiliter)(0, &menuitems[oindex]);
+ else
+ invert_label(0, &menuitems[oindex]);
+ }
+
+ return(rv);
+}
+
+/*
+ * invert_label - highlight the label of the given menu item.
+ */
+void
+invert_label(int state, MENUITEM *m)
+{
+ int r, c;
+ unsigned i, j;
+ char *lp;
+ int old_state;
+ int wasShown;
+ int col_offset;
+ COLOR_PAIR *lastc = NULL;
+ if(m->val == mnoop)
+ return;
+
+
+ mswin_getpos (&r, &c); /* get cursor position */
+ wasShown = mswin_showcaret (0);
+ old_state = mswin_getrevstate ();
+ /*
+ * Leave the command name bold
+ */
+ col_offset = (state || !(lp=strchr(m->label, ' '))) ? 0 : (lp - m->label);
+ (*term.t_move)(m->tl.r, m->tl.c + col_offset);
+ if(state && m->kncp)
+ lastc = pico_set_colorp(m->kncp, PSC_REV|PSC_RET);
+ else if(!state && m->klcp)
+ lastc = pico_set_colorp(m->klcp, PSC_NORM|PSC_RET);
+ else
+ (*term.t_rev)(state);
+
+ for(i = m->tl.r; i <= m->br.r; i++) {
+ for(j = m->tl.c + col_offset; j <= m->br.c; j++) {
+ if(i == m->lbl.r && j == m->lbl.c + col_offset){ /* show label?? */
+ lp = m->label + col_offset;
+ while(*lp && j++ < m->br.c)
+ (*term.t_putchar)(*lp++);
+
+ continue;
+ }
+ else
+ (*term.t_putchar)(' ');
+ }
+ }
+
+ if(lastc){
+ (void)pico_set_colorp(lastc, PSC_NONE);
+ free_color_pair(&lastc);
+ }
+ else
+ (*term.t_rev)(old_state);
+ mswin_move (r, c);
+ mswin_showcaret (wasShown);
+}
+
+
+#endif /* _WINDOWS */
+
+#endif /* MOUSE */
diff --git a/pico/osdep/mouse.h b/pico/osdep/mouse.h
new file mode 100644
index 00000000..db7946b3
--- /dev/null
+++ b/pico/osdep/mouse.h
@@ -0,0 +1,33 @@
+/*
+ * $Id: mouse.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef PICO_OSDEP_MOUSE_INCLUDED
+#define PICO_OSDEP_MOUSE_INCLUDED
+
+#ifdef MOUSE
+
+
+/* exported prototypes */
+int init_mouse(void);
+void end_mouse(void);
+int mouseexist(void);
+int checkmouse(unsigned long *, int, int, int);
+void invert_label(int, MENUITEM *);
+
+
+#endif /* MOUSE */
+
+#endif /* PICO_OSDEP_MOUSE_INCLUDED */
diff --git a/pico/osdep/msdlg.c b/pico/osdep/msdlg.c
new file mode 100644
index 00000000..c4121dac
--- /dev/null
+++ b/pico/osdep/msdlg.c
@@ -0,0 +1,1050 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: msdlg.c 1025 2008-04-08 22:59:38Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*---------------------------------------------------------------------------
+ *
+ * Module: msdlg.c
+ *
+ * Thomas Unger
+ * Networks and Distributed Computing
+ * Computing and Communications
+ * University of Washington
+ * Administration Builiding, AG-44
+ * Seattle, Washington, 98195, USA
+ * Internet: tunger@cac.washington.edu
+ *
+ *
+ * Pine and Pico are registered trademarks of the University of Washington.
+ * No commercial use of these trademarks may be made without prior written
+ * permission of the University of Washington.
+ *
+ * Pine, Pico, and Pilot software and its included text are Copyright
+ * 1989-1998 by the University of Washington.
+ *
+ * The full text of our legal notices is contained in the file called
+ * CPYRIGHT, included with this distribution.
+ *
+ *--------------------------------------------------------------------------*/
+
+#define WIN31
+#define STRICT
+
+#include <system.h>
+#include <general.h>
+
+#include <stdarg.h>
+#include <ddeml.h>
+
+#include "mswin.h"
+#include "msmenu.h"
+#include "resource.h"
+
+#include "../estruct.h"
+#include "../mode.h"
+#include "../pico.h"
+#include "../keydefs.h"
+#include "../edef.h"
+#include "../efunc.h"
+#include "../utf8stub.h"
+#include "../../pith/charconv/filesys.h"
+
+
+extern HWND ghTTYWnd;
+extern HINSTANCE ghInstance;
+extern BOOL gfUseDialogs;
+extern FILE *mswin_debugfile;
+extern int mswin_debug;
+extern TCHAR gszAppName[45];
+
+
+
+#define BTN_FIRSTID 200
+#define BSPACE 4
+#define BWIDTH_MIN 120
+
+
+typedef struct {
+ LPTSTR prompt; /* Prompt. */
+ LPTSTR string; /* Resulting string. */
+ int strlen; /* Length of buffer. */
+ int append; /* Append to existing string. */
+ int passwd; /* Passwd, don't echo (1 use asterisk, 10 space). */
+ unsigned flags; /* other flags. */
+ int dflt;
+ int cancel;
+ int button_count; /* Number of additional buttons. */
+ MDlgButton *button_list; /* List of other buttons. */
+ char **helptext; /* Help text. */
+ char helpkey;
+ int result;
+} OEInfo;
+
+
+static OEInfo gOEInfo;
+static BOOL gDoHelpFirst;
+static WNDPROC gpOldEditProc; /* Old Edit window proc. */
+static WNDPROC gpEditProc;
+static WNDPROC gpOldBtnProc; /* Old Edit window proc. */
+static WNDPROC gpBtnProc;
+
+
+
+
+/*
+ * Forward function declaratins.
+ */
+LONG FAR PASCAL __export EditProc(HWND hBtn, UINT msg, WPARAM wParam,
+ LPARAM lParam);
+LONG FAR PASCAL __export ButtonProc(HWND hBtn, UINT msg, WPARAM wParam,
+ LPARAM lParam);
+BOOL CALLBACK __export mswin_dialog_proc (HWND hDlg, UINT uMsg,
+ WPARAM wParam, LPARAM lParam);
+BOOL CALLBACK __export mswin_select_proc (HWND hDlg, UINT uMsg,
+ WPARAM wParam, LPARAM lParam);
+LONG FAR PASCAL __export KBCProc (HWND hWnd, UINT uMsg, WPARAM wParam,
+ LPARAM lParam);
+
+
+static void BuildButtonList (HWND, MDlgButton *, int, MDlgButton *);
+static BOOL ProcessChar (HWND, WPARAM, OEInfo *, long *);
+static void GetBtnPos (HWND, HWND, RECT *);
+
+int mswin_yesno_lptstr (LPTSTR);
+
+
+int
+mswin_usedialog (void)
+{
+ return (gfUseDialogs);
+}
+
+
+
+/*----------------------------------------------------------------------
+
+
+ Generic text entry. Prompt user for a string.
+
+
+ Args:
+ prompt -- The string to prompt with
+ string -- the buffer result is returned in, and original string (if
+ any) is passed in.
+ field_len -- Maximum length of string to accept
+ append_current -- flag indicating string should not be truncated before
+ accepting input
+ passwd -- a pass word is being fetch. Don't echo on screen
+ button_list -- pointer to array of mDlgButton's. input chars matching
+ those in list return value from list. Nearly identical
+ to Pine's ESCKEY structure.
+ help -- Arrary of strings for help text in bottom screen lines
+ flags -- flags
+
+ Result: editing input string
+ returns -1 unexpected errors
+ returns 0 typed CR or pressed "OK"
+ returns 1 typed ESC or pressed "Cancel"
+ returns 3 typed ^G or PF1 (help)
+ returns 4 typed ^L for a screen redraw
+ or one of the other return values defined in button_list.
+
+ WARNING: Care is required with regard to the escape_list processing.
+ The passed array is terminated with an entry that has ch = -1.
+ Function key labels and key strokes need to be setup externally!
+ Traditionally, a return value of 2 is used for ^T escapes.
+
+
+ Note About Help: We don't get the help text on the first call. If we
+ want help text we have to return 3. We'll get called again
+ with help text. To make it look good, we want to display
+ this the second time we're called. But we don't want to
+ display the help text every time we're called with help
+ text. To know the difference we set gDoHelpFirst when
+ exiting to request help text. If this is set then we
+ display help. If not, then no help.
+
+
+----------------------------------------------------------------------*/
+
+
+/*
+ * xxx implement flags.
+ * xxx display.d uses flags QFFILE, QDEFLT, and QBOBUF
+ */
+
+int
+mswin_dialog(UCS *prompt, UCS *buf, int nbuf, int append_current,
+ int passwd, MDlgButton *button_list, char **help, unsigned flags)
+{
+ DLGPROC dlgprc;
+ int c;
+ LPTSTR promptlpt, buflpt, b;
+ UCS *ucs;
+
+ mswin_flush();
+
+ promptlpt = ucs4_to_lptstr(prompt);
+ buflpt = (LPTSTR) fs_get(nbuf * sizeof(TCHAR));
+ b = ucs4_to_lptstr(buf);
+ if(b){
+ int i;
+
+ for(i = 0; i < nbuf && b[i]; i++)
+ buflpt[i] = b[i];
+
+ if(i < nbuf)
+ buflpt[i] = 0;
+
+ buflpt[nbuf-1] = 0;
+ fs_give((void **) &b);
+ }
+
+ gOEInfo.prompt = promptlpt;
+ gOEInfo.string = buflpt;
+ gOEInfo.strlen = nbuf;
+ gOEInfo.append = append_current;
+ gOEInfo.passwd = passwd;
+ gOEInfo.helptext = help;
+ gOEInfo.helpkey = 0x07; /* ^G */
+ gOEInfo.flags = flags;
+ gOEInfo.button_list = button_list;
+ gOEInfo.result = 0;
+
+ for (c = 0; button_list && button_list[c].ch != -1; ++c)
+ ;
+
+ gOEInfo.button_count = c;
+
+ gpEditProc = (WNDPROC) MakeProcInstance((FARPROC) EditProc, ghInstance);
+ gpBtnProc = (WNDPROC) MakeProcInstance((FARPROC) ButtonProc, ghInstance);
+ dlgprc = (DLGPROC) MakeProcInstance((FARPROC) mswin_dialog_proc, ghInstance);
+
+ DialogBox(ghInstance, MAKEINTRESOURCE (IDD_OPTIONALYENTER), ghTTYWnd, dlgprc);
+
+ FreeProcInstance((FARPROC) dlgprc);
+ FreeProcInstance((FARPROC) gpBtnProc);
+ FreeProcInstance((FARPROC) gpEditProc);
+
+ if(promptlpt)
+ fs_give((void **) &promptlpt);
+
+ ucs = lptstr_to_ucs4(buflpt);
+ if(ucs){
+ int i;
+
+ for(i = 0; i < nbuf && ucs[i]; i++)
+ buf[i] = ucs[i];
+
+ if(i < nbuf)
+ buf[i] = '\0';
+
+ buf[nbuf-1] = '\0';
+ fs_give((void **) &ucs);
+ }
+
+ return(gOEInfo.result);
+}
+
+
+
+
+/*
+ * Dialog procedure for the generic text entry dialog box.
+ */
+BOOL CALLBACK __export
+mswin_dialog_proc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ BOOL ret = FALSE;
+ int i;
+ int biCount;
+ HWND hEdit;
+ MDlgButton built_in[3];
+ long l;
+
+
+ switch (uMsg) {
+ case WM_INITDIALOG:
+ /*
+ * Set the prompt text.
+ */
+ SetDlgItemText(hDlg, IDC_PROMPT, gOEInfo.prompt);
+
+ /*
+ * Set the initial edit text and configure the edit control
+ */
+ if (!gOEInfo.append)
+ *gOEInfo.string = '\0';
+
+ SetDlgItemText(hDlg, IDC_RESPONCE, gOEInfo.string);
+
+ SendDlgItemMessage (hDlg, IDC_RESPONCE, EM_SETPASSWORDCHAR,
+ (gOEInfo.passwd == 1) ? '*' : gOEInfo.passwd ? ' ' : 0, 0L);
+ SendDlgItemMessage (hDlg, IDC_RESPONCE, EM_LIMITTEXT,
+ gOEInfo.strlen - 1, 0L);
+ l = (long) lstrlen(gOEInfo.string);
+ SendDlgItemMessage(hDlg, IDC_RESPONCE, EM_SETSEL, 0, l);
+ hEdit = GetDlgItem (hDlg, IDC_RESPONCE);
+ SetFocus (hEdit);
+ gpOldEditProc = (WNDPROC) GetWindowLong (hEdit, GWL_WNDPROC);
+ SetWindowLong (hEdit, GWL_WNDPROC, (DWORD)(FARPROC)gpEditProc);
+
+
+ /*
+ * Subclass the standard buttons and build buttons for each item
+ * in the button_list.
+ */
+ gpOldBtnProc = (WNDPROC) GetWindowLong (GetDlgItem (hDlg, IDOK), GWL_WNDPROC);
+ SetWindowLong (GetDlgItem (hDlg, IDOK), GWL_WNDPROC,
+ (DWORD)(FARPROC)gpBtnProc);
+ SetWindowLong (GetDlgItem (hDlg, IDCANCEL), GWL_WNDPROC,
+ (DWORD)(FARPROC)gpBtnProc);
+
+ memset (&built_in, 0, sizeof (built_in));
+ built_in[0].id = IDOK;
+ built_in[1].id = IDCANCEL;
+ if (1) {
+ built_in[2].id = IDC_GETHELP;
+ biCount = 3;
+ }
+ else {
+ DestroyWindow (GetDlgItem (hDlg, IDC_GETHELP));
+ biCount = 2;
+ }
+ BuildButtonList (hDlg, built_in, biCount, gOEInfo.button_list);
+
+
+ if (gOEInfo.helptext && gDoHelpFirst) {
+ mswin_showhelpmsg ((WINHAND)hDlg, gOEInfo.helptext);
+ gDoHelpFirst = FALSE;
+ }
+ return (0);
+
+
+ case WM_ACTIVATE:
+ /*
+ * Keep focus on the edit item so key strokes are always
+ * received.
+ */
+ SetFocus (GetDlgItem (hDlg, IDC_RESPONCE));
+ break;
+
+
+ case WM_COMMAND:
+ switch (wParam) {
+ case IDOK:
+ /*
+ * Normal exit. Accept the new text.
+ */
+ GetDlgItemText(hDlg, IDC_RESPONCE, gOEInfo.string, gOEInfo.strlen);
+
+ gOEInfo.result = 0;
+ EndDialog (hDlg, gOEInfo.result);
+ ret = TRUE;
+ break;
+
+ case IDCANCEL:
+ /*
+ * Cancel operation. Don't retreive new text.
+ */
+ gOEInfo.result = 1;
+ EndDialog (hDlg, gOEInfo.result);
+ ret = TRUE;
+ break;
+
+ case IDC_GETHELP:
+ /*
+ * Get help. If we have help text display it. If not,
+ * return value 3, which tells the caller to provide us
+ * with help text.
+ */
+ if (gOEInfo.helptext != NULL) {
+ mswin_showhelpmsg ((WINHAND)hDlg, gOEInfo.helptext);
+ }
+ else {
+ GetDlgItemText(hDlg, IDC_RESPONCE, gOEInfo.string, gOEInfo.strlen);
+ gOEInfo.result = 3;
+ gDoHelpFirst = TRUE;
+ EndDialog (hDlg, gOEInfo.result);
+ }
+ ret = TRUE;
+ break;
+
+ default:
+ /*
+ * Search button list for button with this ID. If found
+ * return it's result code. Retreive text.
+ */
+ if ( wParam >= BTN_FIRSTID &&
+ wParam < BTN_FIRSTID + (WPARAM) gOEInfo.button_count) {
+ GetDlgItemText(hDlg, IDC_RESPONCE, gOEInfo.string, gOEInfo.strlen);
+ i = wParam - BTN_FIRSTID;
+ gOEInfo.result = gOEInfo.button_list[i].rval;
+ EndDialog (hDlg, gOEInfo.result);
+ ret = TRUE;
+ }
+ }
+
+ }
+ return (ret) ;
+}
+
+
+
+
+/*
+ * Subclassed window procedure for Edit control on generic text
+ * entry dialog box.
+ */
+LONG FAR PASCAL __export
+EditProc (HWND hEdit, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ HWND hDlg;
+ LONG ret;
+
+ hDlg = GetParent (hEdit);
+
+ if (msg == WM_GETDLGCODE) {
+ /*
+ * Tell windows that we want to receive ALL keystrokes.
+ */
+ ret = CallWindowProc (gpOldEditProc, hEdit, msg, wParam, lParam);
+ ret |= DLGC_WANTALLKEYS;
+ return (ret);
+ }
+
+
+ if (msg == WM_CHAR) {
+ /*
+ * See if the character typed is a shortcut for any of the
+ * buttons.
+ */
+ if (ProcessChar (hDlg, wParam, &gOEInfo, &ret))
+ return (ret);
+
+ /* No... Fall through for deault processing. */
+ }
+
+ return (CallWindowProc (gpOldEditProc, hEdit, msg, wParam, lParam));
+}
+
+
+
+
+/*
+ * Subclass button windows.
+ *
+ * These buttons will automatically return the input focus to
+ * a control with id IDC_RESPONCE.
+ */
+LONG FAR PASCAL __export
+ButtonProc (HWND hBtn, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ HWND hDlg;
+ LONG ret;
+
+
+ if (uMsg == WM_LBUTTONUP || uMsg == WM_MBUTTONUP || uMsg == WM_RBUTTONUP) {
+ /*
+ * On mouse button up restore input focus to IDC_RESPONCE, which
+ * processes keyboard input.
+ */
+ ret = CallWindowProc (gpOldBtnProc, hBtn, uMsg, wParam, lParam);
+ hDlg = GetParent (hBtn);
+ SetFocus (GetDlgItem (hDlg, IDC_RESPONCE));
+ return (ret);
+ }
+
+ return (CallWindowProc (gpOldBtnProc, hBtn, uMsg, wParam, lParam));
+}
+
+
+
+int
+mswin_yesno(UCS *prompt_ucs4)
+{
+ int ret;
+ LPTSTR prompt_lptstr;
+
+ prompt_lptstr = ucs4_to_lptstr(prompt_ucs4);
+ ret = mswin_yesno_lptstr(prompt_lptstr);
+ fs_give((void **) &prompt_lptstr);
+
+ return(ret);
+}
+
+
+int
+mswin_yesno_utf8(char *prompt_utf8)
+{
+ int ret;
+ LPTSTR prompt_lptstr;
+
+ prompt_lptstr = utf8_to_lptstr(prompt_utf8);
+ ret = mswin_yesno_lptstr(prompt_lptstr);
+ fs_give((void **) &prompt_lptstr);
+
+ return(ret);
+}
+
+/*
+ * Ask a yes/no question with the MessageBox procedure.
+ *
+ * Returns:
+ * 0 - Cancel operation.
+ * 1 - "Yes" was selected.
+ * 2 - "No" was selected.
+ */
+int
+mswin_yesno_lptstr(LPTSTR prompt)
+{
+ int ret;
+
+ mswin_flush ();
+
+ mswin_killsplash();
+
+ ret = MessageBox (ghTTYWnd, prompt, gszAppName,
+ MB_APPLMODAL | MB_ICONQUESTION | MB_YESNOCANCEL);
+
+ switch (ret) {
+ case IDYES: ret = 1; break;
+ case IDNO: ret = 2; break;
+ default:
+ case IDCANCEL: ret = 0; break;
+ }
+ return (ret);
+}
+
+
+/*----------------------------------------------------------------------
+
+
+ Generic selection routine. Display a prompt and a set of buttons for
+ each possible answer.
+
+
+
+ Args:
+ prompt -- The string to prompt with.
+ button_list -- pointer to array of mDlgButton's. input chars
+ matching those in list return value from
+ mlist. Nearly identical to Pine's ESCKEY
+ mstructure.
+ dflt -- Value returned when "Enter" is pressed.
+ on_ctrl_C -- Value returned to cancel dialog (ESC).
+ help -- Arrary of strings for help text in bottom screen
+ lines
+ flags -- flags
+
+ Result:
+ dflt -- Default option selected.
+ on_ctrl_C -- Calcel operation.
+ or one of the other return values defined in button_list.
+
+
+Note: To prcess keyboard in put we use a custom dialog control
+ which is invisible but always has the input focus.
+
+----------------------------------------------------------------------*/
+
+int
+mswin_select(char *utf8prompt, MDlgButton *button_list, int dflt, int on_ctrl_C,
+ char **help, unsigned flags)
+{
+ WNDCLASS wndclass;
+ DLGPROC dlgprc;
+ WNDPROC kbcProc;
+ int c;
+ LPTSTR promptlpt;
+
+ mswin_flush ();
+
+ promptlpt = utf8_to_lptstr(utf8prompt);
+
+ /*
+ * Setup dialog config structure.
+ */
+ gOEInfo.prompt = promptlpt;
+ gOEInfo.string = NULL;
+ gOEInfo.strlen = 0;
+ gOEInfo.append = 0;
+ gOEInfo.passwd = 0;
+ gOEInfo.helptext = help;
+ gOEInfo.helpkey = 'g';
+ gOEInfo.dflt = dflt;
+ gOEInfo.cancel = on_ctrl_C;
+ gOEInfo.flags = flags;
+ gOEInfo.button_list = button_list;
+ gOEInfo.result = 0;
+
+ /*
+ * Count the buttons.
+ */
+ for(c = 0; button_list && button_list[c].ch != -1; ++c)
+ ;
+
+ gOEInfo.button_count = c;
+
+ /*
+ * Register the class for the user control which will receive keyboard
+ * input events.
+ */
+ kbcProc = (WNDPROC) MakeProcInstance((FARPROC) KBCProc, ghInstance);
+ wndclass.style = 0;
+ wndclass.lpfnWndProc = kbcProc;
+ wndclass.cbClsExtra = 0;
+ wndclass.cbWndExtra = 0;
+ wndclass.hInstance = ghInstance;
+ wndclass.hIcon = NULL;
+ wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wndclass.hbrBackground = 0;
+ wndclass.lpszMenuName = NULL;
+ wndclass.lpszClassName = TEXT("KeyboardCapture");
+
+ if(RegisterClass (&wndclass) == 0){
+ /*
+ * There is no good return value to indicate an error.
+ * Cancel the operation.
+ */
+ return(on_ctrl_C);
+ }
+
+ /*
+ * Make other procedure instances.
+ */
+ dlgprc = (DLGPROC) MakeProcInstance((FARPROC) mswin_select_proc, ghInstance);
+ gpBtnProc = (WNDPROC) MakeProcInstance((FARPROC) ButtonProc, ghInstance);
+
+ /*
+ * Bring up the dialog box.
+ */
+ DialogBox(ghInstance, MAKEINTRESOURCE(IDD_SELECT), ghTTYWnd, dlgprc);
+
+ /*
+ * Free the proc instances and window class.
+ */
+ FreeProcInstance((FARPROC) dlgprc);
+ FreeProcInstance((FARPROC) gpBtnProc);
+ UnregisterClass (TEXT("KeyboardCapture"), ghInstance);
+ FreeProcInstance((FARPROC) kbcProc);
+
+ if(promptlpt)
+ fs_give((void **) &promptlpt);
+
+ return(gOEInfo.result);
+}
+
+
+
+
+/*
+ * Dialog procedure for the button selection dialog box.
+ */
+BOOL CALLBACK __export
+mswin_select_proc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ BOOL ret = FALSE;
+ int i;
+ int biCount;
+ MDlgButton built_in[3];
+
+ if (mswin_debug >= 1)
+ fprintf (mswin_debugfile, "Select: uMsg x%x (%d), wParam x%x, lParam x%lx\n",
+ uMsg, uMsg, wParam, lParam);
+
+
+ switch (uMsg) {
+ case WM_INITDIALOG:
+ /*
+ * Show the prompt.
+ */
+ SetDlgItemText(hDlg, IDC_PROMPT, gOEInfo.prompt);
+
+ /*
+ * Set focus to the invisible custom control
+ */
+ SetFocus (GetDlgItem (hDlg, IDC_RESPONCE));
+
+ /*
+ * Subclass the standard buttons and build buttons for each item
+ * in the button_list.
+ */
+ gpOldBtnProc = (WNDPROC) GetWindowLong (GetDlgItem (hDlg, IDCANCEL), GWL_WNDPROC);
+ SetWindowLong (GetDlgItem (hDlg, IDC_GETHELP), GWL_WNDPROC,
+ (DWORD)(FARPROC)gpBtnProc);
+ SetWindowLong (GetDlgItem (hDlg, IDCANCEL), GWL_WNDPROC,
+ (DWORD)(FARPROC)gpBtnProc);
+
+ memset (&built_in, 0, sizeof (built_in));
+ built_in[0].id = IDCANCEL;
+ if (gOEInfo.helptext != NULL) {
+ built_in[1].id = IDC_GETHELP;
+ biCount = 2;
+ }
+ else {
+ DestroyWindow (GetDlgItem (hDlg, IDC_GETHELP));
+ gOEInfo.helpkey = 0;
+ biCount = 1;
+ }
+ BuildButtonList (hDlg, built_in, biCount, gOEInfo.button_list);
+ gDoHelpFirst = FALSE;
+ return (0);
+
+
+ case WM_ACTIVATE:
+ /*
+ * Keep focus on the custom control item so key strokes are always
+ * received.
+ */
+ SetFocus (GetDlgItem (hDlg, IDC_RESPONCE));
+ break;
+
+
+ case WM_COMMAND:
+ switch (wParam) {
+ case IDOK:
+ gOEInfo.result = gOEInfo.dflt;
+ EndDialog (hDlg, gOEInfo.result);
+ ret = TRUE;
+ break;
+
+ case IDCANCEL:
+ gOEInfo.result = gOEInfo.cancel;
+ EndDialog (hDlg, gOEInfo.result);
+ ret = TRUE;
+ break;
+
+ case IDC_GETHELP:
+ /*
+ * Get help. If we have help text display it. If not,
+ * return value 3, which tells the caller to provide us
+ * with help text.
+ */
+ if (gOEInfo.helptext != NULL) {
+ mswin_showhelpmsg ((WINHAND) hDlg, gOEInfo.helptext);
+ }
+ ret = TRUE;
+ break;
+
+ default:
+ if ( wParam >= BTN_FIRSTID &&
+ wParam < BTN_FIRSTID + (WPARAM) gOEInfo.button_count) {
+ i = wParam - BTN_FIRSTID;
+ gOEInfo.result = gOEInfo.button_list[i].rval;
+ EndDialog (hDlg, gOEInfo.result);
+ ret = TRUE;
+ }
+ }
+
+ }
+ return (ret) ;
+}
+
+
+
+/*
+ * Window procedure for the hidden custom control.
+ */
+LONG FAR PASCAL __export
+KBCProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ LONG ret;
+
+
+ if (uMsg == WM_GETDLGCODE) {
+ /*
+ * Tell windows that we want all the character messages.
+ */
+ ret = DefWindowProc (hWnd, uMsg, wParam, lParam);
+ ret |= DLGC_WANTALLKEYS;
+ return (ret);
+ }
+
+
+ if (uMsg == WM_CHAR) {
+ /*
+ * See if the character typed is a shortcut for any of the
+ * buttons.
+ */
+ if (ProcessChar (GetParent (hWnd), wParam, &gOEInfo, &ret))
+ return (ret);
+
+ /* Fall through for default processing. */
+ }
+
+ return (DefWindowProc (hWnd, uMsg, wParam, lParam));
+}
+
+
+
+
+
+
+/*
+ * Check if character activates any button. If it does, send WM_COMMAND
+ * to target window.
+ *
+ * Args:
+ * hWnd - Window to send message to.
+ * wParam - character typed.
+ * oeinfo - dialog config structure. Contains button list.
+ * ret - value to return to windows.
+ *
+ * Returns:
+ * TRUE - Found a match. Exit from Window Procedure returning
+ * *ret to windows.
+ *
+ * FALSE - No match. Continue with default processing of character
+ * message.
+ *
+ */
+static BOOL
+ProcessChar (HWND hWnd, WPARAM wParam, OEInfo *oeinfo, long *ret)
+{
+ int i;
+ int id;
+
+
+ *ret = 0;
+ if (wParam == 0x0d) {
+ /*
+ * "Enter" is same as clicking on OK button.
+ */
+ PostMessage (hWnd, WM_COMMAND, IDOK,
+ MAKELONG (GetDlgItem (hWnd, IDOK), 1));
+ return (TRUE);
+ }
+ if (wParam == 0x1b || wParam == 0x03) {
+ /*
+ * Esc is same as clicking on Cancel button.
+ */
+ PostMessage (hWnd, WM_COMMAND, IDCANCEL,
+ MAKELONG (GetDlgItem (hWnd, IDCANCEL), 1));
+ return (TRUE);
+ }
+
+ /*
+ * Any of the custom buttons respond to this key?
+ */
+ for (i = 0; i < oeinfo->button_count; ++i) {
+ if (wParam == oeinfo->button_list[i].ch) {
+ id = oeinfo->button_list[i].id;
+ PostMessage (hWnd, WM_COMMAND, id,
+ MAKELONG (GetDlgItem (hWnd, id), 1));
+ return (TRUE);
+ }
+ }
+
+ /*
+ * Lastly, is it the help key?
+ */
+ if (oeinfo->helpkey != 0 && wParam == oeinfo->helpkey) {
+ id = IDC_GETHELP;
+ PostMessage (hWnd, WM_COMMAND, id,
+ MAKELONG (GetDlgItem (hWnd, id), 1));
+ return (TRUE);
+ }
+
+
+ /*
+ * Nothing we understand.
+ */
+ return (FALSE);
+}
+
+
+
+
+
+/*
+ * Get a button position in the parent's coordinate space.
+ */
+static void
+GetBtnPos (HWND hPrnt, HWND hWnd, RECT *r)
+{
+ GetWindowRect (hWnd, r);
+ ScreenToClient (hPrnt, (POINT *) r);
+ ScreenToClient (hPrnt, (POINT *) &r->right);
+}
+
+
+
+
+/*
+ * Add buttons to the dialog box.
+ */
+static void
+BuildButtonList(HWND hDlg, MDlgButton *built_in, int biCount, MDlgButton *button_list)
+{
+ RECT rDlg, rb1, rb2, r;
+ HDC hdc;
+ MDlgButton *pB; /* pointer to button struct*/
+ HWND hBtn, hBtn1, hBtn2; /* handle to buttons */
+ int btnCount; /* extra button count */
+ int rows, cols; /* button rows and columns */
+ int bpos; /* button position */
+ int i;
+ int maxstrIdx, maxstrLen; /* button w/ longest caption*/
+ DWORD textExtent; /* width of button caption */
+ int margine; /* left and right margines */
+ int btop, bleft; /* button position */
+ int bwidth, bheight, w; /* button size */
+ int bMinWidth; /* minimum buttonwidth */
+ int bvertSpace, bhorzSpace; /* button spacing */
+ int newWHeight, delta; /* window resizing */
+ TCHAR caption[128];
+ size_t ncaption;
+ HFONT btnFont;
+ SIZE size;
+
+ /*
+ * Are there any buttons to add?
+ */
+ if(button_list == NULL)
+ return;
+
+ maxstrIdx = 0;
+ for(btnCount = 0; button_list[btnCount].ch != -1; ++btnCount){
+ if(lstrlen(button_list[btnCount].label) >
+ lstrlen(button_list[maxstrIdx].label))
+ maxstrIdx = btnCount;
+ }
+
+ if(btnCount == 0)
+ return;
+
+
+ /*
+ * Get the size of the dialog box and the positions of the
+ * first and, if there is one, the second button. Calculate or
+ * default button offsets and positions.
+ */
+ GetClientRect(hDlg, &rDlg);
+ hBtn1 = GetDlgItem(hDlg, built_in[0].id);
+ GetBtnPos(hDlg, hBtn1, &rb1);
+ margine = rb1.left; /* left and right margine */
+ bheight = rb1.bottom - rb1.top; /* button width */
+ bwidth = rb1.right - rb1.left; /* button height. */
+ if(biCount > 1){
+ hBtn2 = GetDlgItem(hDlg, built_in[1].id);
+ GetBtnPos(hDlg, hBtn2, &rb2);
+ bvertSpace = rb2.top - rb1.top; /* vertical spacing */
+ }
+ else{
+ bvertSpace = bheight + BSPACE; /* vertical spacing */
+ }
+
+
+ /*
+ * Get the button font.
+ */
+ btnFont = (HFONT) SendMessage (hBtn1, WM_GETFONT, 0, 0);
+
+ /*
+ * Get the screen extent of the longest button label. min width
+ * is the extent, plus the average width of 5 extra characters for
+ * key stroke, plus margine.
+ */
+ hdc = GetDC (hBtn1);
+ ncaption = sizeof(caption)/sizeof(TCHAR);
+ _sntprintf(caption, ncaption, TEXT("%s '%s'"), button_list[maxstrIdx].label,
+ button_list[maxstrIdx].name);
+
+ maxstrLen = lstrlen(caption);
+ GetTextExtentPoint32(hdc, caption, maxstrLen, &size);
+
+ textExtent = size.cx;
+
+ ReleaseDC(hBtn1, hdc);
+ bMinWidth = LOWORD (textExtent) + (2 * (LOWORD(textExtent) / maxstrLen));
+ if(bwidth < bMinWidth)
+ bwidth = bMinWidth;
+
+ /*
+ * Calculate button positions. If the buttons are going to extend
+ * past the right edge of the dialog box, shrink their width. But
+ * if they get narrower than the min width calculated above then
+ * increase the number of rows.
+ */
+ rows = 1;
+ do {
+ ++rows; /* More rows. */
+ w = bwidth; /* Original button width. */
+ cols = 1 + ((biCount + btnCount) - 1) / rows; /* Calc num cols. */
+
+ /* Need to srink button width? */
+ if (2 * margine + bwidth * cols + BSPACE * (cols - 1) > rDlg.right) {
+ w = ((rDlg.right - (2 * margine)) - (BSPACE * (cols-1))) / cols;
+ }
+ /* Did buttons get too narrow? */
+ } while (w < bMinWidth && cols > 1);
+
+ bwidth = w;
+ bhorzSpace = bwidth + BSPACE; /* horizontal spacing */
+
+
+ /*
+ * Resize window.
+ */
+ newWHeight = rb1.top + (rows * bvertSpace);
+ delta = newWHeight - rDlg.bottom;
+ if (delta != 0) {
+ GetWindowRect (hDlg, &r);
+ MoveWindow (hDlg, r.left, r.top, r.right - r.left,
+ (r.bottom - r.top) + delta, FALSE);
+ }
+
+
+
+ /*
+ * Create new buttons.
+ */
+ for(bpos = 0; bpos < biCount + btnCount; ++bpos) {
+ /*
+ * Calculate position for this button.
+ */
+ btop = rb1.top + (bpos % rows) * bvertSpace;
+ bleft = rb1.left + (bpos / rows) * bhorzSpace;
+
+ if(bpos < biCount){
+ /*
+ * Resize existing buttons.
+ */
+ MoveWindow(GetDlgItem(hDlg, built_in[bpos].id),
+ bleft, btop, bwidth, bheight, FALSE);
+ }
+ else{
+ i = bpos - biCount;
+ pB = &button_list[i];
+
+ _sntprintf(caption, ncaption, TEXT("%s '%s'"), pB->label, pB->name);
+ hBtn = CreateWindow(TEXT("BUTTON"), caption,
+ WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
+ bleft, btop, bwidth, bheight,
+ hDlg, NULL, ghInstance, NULL);
+
+ pB->id = BTN_FIRSTID + i;
+ SetWindowLong(hBtn, GWL_ID, pB->id);
+ SendMessage(hBtn, WM_SETFONT, (WPARAM)btnFont, MAKELPARAM (0, 0));
+
+ /* Subclass button. */
+ SetWindowLong(hBtn, GWL_WNDPROC, (DWORD)(FARPROC)gpBtnProc);
+ EnableWindow(hBtn, TRUE);
+ }
+ }
+}
diff --git a/pico/osdep/msmenu.h b/pico/osdep/msmenu.h
new file mode 100644
index 00000000..0bf63c53
--- /dev/null
+++ b/pico/osdep/msmenu.h
@@ -0,0 +1,97 @@
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef MSMENU_H
+#define MSMENU_H
+
+#include "resource.h"
+
+
+/*
+ * var in pine's key structure we'll use
+ */
+#define KS_OSDATAVAR short menuitem;
+#define KS_OSDATAGET(X) ((X)->menuitem)
+#define KS_OSDATASET(X, Y) ((X)->menuitem = (Y))
+
+/*
+ * Menu key definitions.
+ * Should be same values as in resouce.h
+ * These are all in a range from KS_RANGESTART to KS_RANGEEND
+ * with no holes.
+ */
+#define KS_NONE 0
+#define KS_RANGESTART IDM_MI_VIEW
+
+#define KS_VIEW IDM_MI_VIEW
+#define KS_EXPUNGE IDM_MI_EXPUNGE
+#define KS_ZOOM IDM_MI_ZOOM
+#define KS_SORT IDM_MI_SORT
+#define KS_HDRMODE IDM_MI_HDRMODE
+#define KS_MAINMENU IDM_MI_MAINMENU
+#define KS_FLDRLIST IDM_MI_FLDRLIST
+#define KS_FLDRINDEX IDM_MI_FLDRINDEX
+#define KS_COMPOSER IDM_MI_COMPOSER
+#define KS_PREVPAGE IDM_MI_PREVPAGE
+#define KS_PREVMSG IDM_MI_PREVMSG
+#define KS_NEXTMSG IDM_MI_NEXTMSG
+#define KS_ADDRBOOK IDM_MI_ADDRBOOK
+#define KS_WHEREIS IDM_MI_WHEREIS
+#define KS_PRINT IDM_MI_PRINT
+#define KS_REPLY IDM_MI_REPLY
+#define KS_FORWARD IDM_MI_FORWARD
+#define KS_BOUNCE IDM_MI_BOUNCE
+#define KS_DELETE IDM_MI_DELETE
+#define KS_UNDELETE IDM_MI_UNDELETE
+#define KS_FLAG IDM_MI_FLAG
+#define KS_SAVE IDM_MI_SAVE
+#define KS_EXPORT IDM_MI_EXPORT
+#define KS_TAKEADDR IDM_MI_TAKEADDR
+#define KS_SELECT IDM_MI_SELECT
+#define KS_APPLY IDM_MI_APPLY
+#define KS_POSTPONE IDM_MI_POSTPONE
+#define KS_SEND IDM_MI_SEND
+#define KS_CANCEL IDM_MI_CANCEL
+#define KS_ATTACH IDM_MI_ATTACH
+#define KS_TOADDRBOOK IDM_MI_TOADDRBOOK
+#define KS_READFILE IDM_MI_READFILE
+#define KS_JUSTIFY IDM_MI_JUSTIFY
+#define KS_ALTEDITOR IDM_MI_ALTEDITOR
+#define KS_GENERALHELP IDM_MI_GENERALHELP
+#define KS_SCREENHELP IDM_MI_SCREENHELP
+#define KS_EXIT IDM_MI_EXIT
+#define KS_NEXTPAGE IDM_MI_NEXTPAGE
+#define KS_SAVEFILE IDM_MI_SAVEFILE
+#define KS_CURPOSITION IDM_MI_CURPOSITION
+#define KS_GOTOFLDR IDM_MI_GOTOFLDR
+#define KS_JUMPTOMSG IDM_MI_JUMPTOMSG
+#define KS_RICHHDR IDM_MI_RICHHDR
+#define KS_EXITMODE IDM_MI_EXITMODE
+#define KS_REVIEW IDM_MI_REVIEW
+#define KS_KEYMENU IDM_MI_KEYMENU
+#define KS_SELECTCUR IDM_MI_SELECTCUR
+#define KS_UNDO IDM_MI_UNDO
+#define KS_SPELLCHK IDM_MI_SPELLCHK
+
+#define KS_RANGEEND IDM_MI_SPELLCHK
+
+#define KS_COUNT ((KS_RANGEEND - KS_RANGESTART) + 1)
+
+
+
+#define MENU_DEFAULT 300 /* Default menu for application. */
+#define MENU_COMPOSER 301 /* Menu for pine's composer. */
+
+#endif /* MSMENU_H */
diff --git a/pico/osdep/mswin.bmp b/pico/osdep/mswin.bmp
new file mode 100644
index 00000000..e6882628
--- /dev/null
+++ b/pico/osdep/mswin.bmp
Binary files differ
diff --git a/pico/osdep/mswin.c b/pico/osdep/mswin.c
new file mode 100644
index 00000000..58d2ab3b
--- /dev/null
+++ b/pico/osdep/mswin.c
@@ -0,0 +1,12448 @@
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#define WIN31
+#define STRICT
+
+#define termdef 1 /* don't define "term" external */
+
+#include "../headers.h"
+
+#include <stdarg.h>
+#include <ddeml.h>
+
+#include "mswin.h"
+#include "resource.h"
+
+#include "../../pith/osdep/pipe.h"
+#include "../../pith/osdep/canaccess.h"
+
+#include "../../pith/charconv/filesys.h"
+#include "../../pith/charconv/utf8.h"
+
+#include "../../pith/filttype.h"
+#include "../../pith/osdep/color.h"
+
+#include "mswin_tw.h"
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Defines
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+
+#define ICON
+
+
+/* For debugging, export locals so debugger can see them. */
+#ifdef DEBUG
+#define LOCAL
+#else
+#define LOCAL static
+#endif
+
+
+/*
+ * Define which debugging is desired. Generally only FDEBUG.
+ */
+#define FDEBUG /* Standard file debugging. */
+
+#undef SDEBUG /* Verbose debugging of startup and windows handling*/
+#undef CDEBUG /* Verbose debugging of character input timeing. */
+
+#undef OWN_DEBUG_FILE /* Define if we want to write to our own debug file,
+ * not pine's. */
+
+
+/* Max size permitted for the screen. */
+#define MAXNROW 200
+#define MAXNCOLUMN NLINE
+
+#define MINNROW 10 /* Minimum screen size */
+#define MINNCOLUMN 32
+
+#define MARGINE_LEFT 3
+#define MARGINE_TOP 1
+
+#define FRAME_3D_SIZE 1
+
+#define WIN_MIN_X_SIZE 190 /* Minimum window size. */
+#define WIN_MIN_Y_SIZE 180
+
+#define WIN_X_BORDER_SIZE 8 /* Space taked by window frame. */
+#define WIN_Y_BORDER_SIZE 65
+
+#define FONT_MIN_SIZE 5
+#define FONT_MAX_SIZE 21
+
+#define PRINT_TAB_SIZE 8 /* Tab size used by print code. */
+
+
+#define TB_HEIGHT 32 /* Tool Bar Height. */
+#define TB_BUTTONHEIGHT 16 /* Button Height. */
+#define TB_BUTTONSPACING 8 /* Space between buttons. */
+
+
+/* Some string lengths. */
+#define MAXLEN_TEMPSTR 256 /* Max size for temp storage. */
+
+#define WIN_POS_STR_MAX_LEN 21 /* Max length for window-position
+ * string. */
+
+#define MENU_ITEM_NAME_LEN 32 /* Menu item name lengths. */
+
+
+/* Length of keyboard input queue. */
+#define CHARACTER_QUEUE_LENGTH 32
+#define MOUSE_QUEUE_LENGTH 32
+
+/* Number of resize callback functions we can keep track of. */
+#define RESIZE_CALLBACK_ARRAY_SIZE 3
+
+/* Number of bytes held in the write accumulator. */
+#define WRITE_ACCUM_SIZE 200
+
+/* Max time that may pass between calls to GetMessage. See mswin_charavail()
+ */
+#define GM_MAX_TIME 3000 /* In milliseconds.*/
+
+/* My Timer Message */
+#define MY_TIMER_ID 33
+/* timeout period in miliseconds. */
+#define MY_TIMER_PERIOD (UINT)((IDLE_TIMEOUT + 1)*1000)
+/***** We use variable my_timer_period now instead so that we can set
+ it differently when in pine and when in regular old pico.
+ We're not entirely sure we need it in pico, but we will leave
+ it there because we don't understand.
+ *****/
+#define MY_TIMER_SHORT_PERIOD (UINT)5000 /* used when there is a task in
+ the OnTask list. */
+#define MY_TIMER_VERY_SHORT_PERIOD (UINT)500 /* used when SIGALRM and alarm()
+ is set. */
+#define MY_TIMER_EXCEEDINGLY_SHORT_PERIOD (UINT)80 /* used when
+ gAllowMouseTracking is set */
+
+#define TIMER_FAIL_MESSAGE TEXT("Failed to get all necessary Windows resources (timers). Alpine will run, but may not be able to keep the connection to the server alive. Quitting other applications and restarting Alpine may solve the problem.")
+
+/*
+ * Task bar notification Icon id and call back message for it.
+ */
+#define TASKBAR_ICON_NEWMAIL 1
+#define TASKBAR_ICON_MESSAGE WM_USER+1000
+
+
+/*
+ * Below here are fixed constancs that really should not be changed.
+ */
+
+/* Auto Wrap States. */
+#define WRAP_OFF 0 /* Never wrap to next line. */
+#define WRAP_ON 1 /* Wrap to next line. */
+#define WRAP_NO_SCROLL 2 /* Wrap to next line but DON'T scroll
+ screen to do it. */
+/* Speicial keys in the Character Queue. */
+#define CQ_FLAG_DOWN 0x01
+#define CQ_FLAG_EXTENDED 0x02
+#define CQ_FLAG_ALT 0x04
+
+/* Special ASCII characters. */
+#define ASCII_BEL 0x07
+#define ASCII_BS 0x08
+#define ASCII_TAB 0x09
+#define ASCII_LF 0x0A
+#define ASCII_CR 0x0D
+#define ASCII_XON 0x11
+#define ASCII_XOFF 0x13
+
+/* Character Attributes. */
+#define CHAR_ATTR_NORM 0x00 /* Normal. */
+#define CHAR_ATTR_REV 0x01 /* Reverse Video. */
+#define CHAR_ATTR_BOLD 0x02 /* Reverse Video. */
+#define CHAR_ATTR_ULINE 0x04 /* Reverse Video. */
+#define CHAR_ATTR_SEL 0x08 /* Selected text. */
+#define CHAR_ATTR_NOT 0x80 /* No attributes. */
+
+/*
+ * Different applications that we know about.
+ */
+#define APP_UNKNOWN 0
+#define APP_PICO 1
+#define APP_PICO_IDENT TEXT("pico")
+#define APP_PINE 2
+#define APP_PINE_IDENT TEXT("pine")
+
+/*
+ * Control values for call to AccelCtl.
+ */
+/*#undef ACCELERATORS*/
+#define ACCELERATORS
+#define ACCEL_UNLOAD 0 /* Unload the accelerators. */
+#define ACCEL_LOAD 1 /* Load the accelerators. */
+#define ACCEL_TOGGLE 2 /* Toggle the accelerators. */
+
+/*
+ * flag bits to control which edit menu options can get lit
+ */
+#define EM_NONE 0L
+#define EM_CP 0x0001
+#define EM_CP_APPEND 0x0002
+#define EM_FIND 0x0004
+#define EM_PST 0x0008
+#define EM_PST_ABORT 0x0010
+#define EM_CUT 0x0020
+#define EM_SEL_ALL 0x0040
+
+#define EM_MAX_ACCEL 6
+
+/* Offsets to objects in window extra storage. */
+#define GWL_PTTYINFO 0 /* Offset in Window extra storage. */
+
+/*
+ *
+ */
+#define FONT_CHARSET_FONT DEFAULT_CHARSET
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Typedefs
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+/* Type that the screen array is made up of. */
+#define CHAR unsigned char
+
+/* define possible caret shapes */
+typedef enum {
+ CaretBlock = 0,
+ CaretSmallBlock,
+ CaretHorizBar,
+ CaretVertBar
+} CARETS;
+
+/* Type that the attribute array is made up of. */
+typedef struct _character_attribute {
+ unsigned style:8;
+ DWORD rgbFG, rgbBG;
+} CharAttrib;
+
+typedef int (*ResizeCallBackProc)();
+typedef int (*FileDropCallBackProc)();
+typedef short CORD;
+
+/* NOTE: There is currently code that assumes that CHAR and CharAttrib
+ * are one byte in size. All this code is flaged with a preceeding
+ * assert () */
+
+/* Struct that defines command menu entries. */
+typedef struct tagMenuItem {
+ BOOL miActive;
+ UCS miKey;
+ short miIndex;
+ char *miLabel;
+} MenuItem;
+
+
+/* List of child window IDs and previous window procedures. */
+typedef struct tagBtnList {
+ WORD wndID;
+ WNDPROC wndProc;
+} BtnList;
+
+
+/* General info. */
+typedef struct tagTTYINFO {
+ TCHAR *pScreen; /* Screen. */
+ int *pCellWidth; /* how wide to paint the char */
+ CharAttrib *pAttrib; /* Attributes. */
+ TCHAR writeAccum[WRITE_ACCUM_SIZE];
+ int writeAccumCount;
+ CARETS cCaretStyle; /* Current caret's style */
+ int scrollRange; /* Current scroll bar range. */
+ long scrollPos; /* Current scroll position. */
+ long scrollTo; /* Possition of last scroll to. */
+ HFONT hTTYFont;
+ LOGFONT lfTTYFont;
+ DWORD rgbFGColor; /* Normal forground color. */
+ DWORD rgbBGColor; /* Normal background color. */
+ DWORD rgbRFGColor; /* Reverse forground color. */
+ DWORD rgbRBGColor; /* Reverse background color */
+ unsigned screenDirty:1; /* TRUE if screen needs update. */
+ unsigned eraseScreen:1; /* TRUE if need to erase whole screen */
+ unsigned fMinimized:1; /* True when window is minimized. */
+ unsigned fMaximized:1; /* True when window is maximized. */
+ unsigned fFocused:1; /* True when we have focus. */
+ unsigned fNewLine:1; /* Auto LF on CR. */
+ unsigned fMassiveUpdate:1;/* True when in Massive screen update. */
+ unsigned fNewMailIcon:1; /* True when new mail has arrived. */
+ unsigned fMClosedIcon:1; /* True when no mailbox is open. */
+ unsigned fCursorOn:1; /* True when cursor's shown */
+ unsigned fCaretOn:1; /* True if caret's displayed */
+ unsigned fTrayIcon:1; /* True if tool tray icon's on */
+ ResizeCallBackProc resizer[RESIZE_CALLBACK_ARRAY_SIZE];
+ FileDropCallBackProc dndhandler;
+ int autoWrap; /* Auto wrap to next line. */
+ CharAttrib curAttrib; /* Current character attributes. */
+ int actNRow, actNColumn; /* Actual number of rows and comumns
+ * displayed. */
+ CORD xSize, ySize; /* Size of screen in pixels */
+ CORD xScroll, yScroll; /* ?? */
+ CORD xOffset, yOffset; /* Offset from the left and top of
+ * window contents. */
+ CORD nColumn, nRow; /* Current position of cursor in
+ * cells. */
+ CORD xChar, yChar; /* Width of a char in pixels. */
+ int yCurOffset; /* offset of cursor Y-size */
+ CORD fDesiredSize; /* TRUE when there is a specific size
+ * the window should be expanded to
+ * after being minimized. */
+ CORD xDesPos, yDesPos; /* Desired position. */
+ CORD xDesSize, yDesSize; /* Desired window position. */
+ int curWinMenu; /* Current window menu. */
+ HACCEL hAccel; /* Handle to accelorator keys. */
+ UINT fAccel; /* vector of bound accelerator keys. */
+ CORD toolBarSize; /* Size of toolbar. */
+ BOOL toolBarTop; /* Toolbar on top? */
+ int curToolBarID;
+ BtnList *toolBarBtns;
+ HWND hTBWnd;
+ HBRUSH hTBBrush;
+ BOOL menuItemsCurrent;
+ BOOL noScrollUpdate;
+ BOOL scrollRangeChanged;
+ long noSUpdatePage;
+ long noSUpdateRange;
+ short menuItemsIndex;
+ MenuItem menuItems[KS_COUNT];
+} TTYINFO, *PTTYINFO ;
+
+
+#define MAXCLEN (MAX(MAXCOLORLEN,RGBLEN+1))
+typedef struct MSWINColor {
+ char *colorName;
+ char *canonicalName;
+ COLORREF colorRef;
+} MSWINColor;
+
+
+typedef struct {
+ char *name;
+ CARETS style;
+} MSWinCaret_t;
+
+
+/*
+ * Entry in the OnTask list. This is a list of actions to perform
+ * when a task exits. Currently, the only thing we do is delete
+ * files. if that changes this structure will get more complex.
+ *
+ * hTask == NULL means "This program" and can be used to arrange for
+ * deletion of files when this program exits.
+ */
+typedef struct ontask {
+ struct ontask *next;
+ HTASK hTask;
+ char path[PATH_MAX+1];
+} OnTaskItem;
+
+typedef void (__cdecl *SignalType)(int);
+
+typedef struct _iconlist {
+ HICON hIcon;
+/* char path[PATH_MAX]; */
+/* WORD index; */
+ int id;
+ short row;
+ struct _iconlist *next;
+} IconList;
+
+/*
+ * char * array for printing registry settings.
+ */
+typedef struct MSWR_LINE_BUFFER {
+ char **linep; /* store these as utf8, since that's what we have to pass back */
+ unsigned long size;
+ unsigned long offset;
+} MSWR_LINE_BUFFER_S;
+
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Forward function declarations.
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+LOCAL void *
+MyGetWindowLongPtr(HWND hwnd, int nIndex)
+{
+ return (void *)(LONG_PTR)GetWindowLongPtr(hwnd, nIndex);
+}
+
+LOCAL LONG_PTR
+MySetWindowLongPtr(HWND hwnd, int nIndex, void *NewLongPtr)
+{
+// warning C4244: 'function': conversion from 'LONG_PTR' to 'LONG',
+// possible loss of data
+#pragma warning(push)
+#pragma warning(disable: 4244)
+ return SetWindowLongPtr(hwnd, nIndex, (LONG_PTR)NewLongPtr);
+#pragma warning(pop)
+}
+
+#ifdef WIN32
+#define GET_HINST( hWnd ) ((HINSTANCE) MyGetWindowLongPtr( hWnd, GWLP_HINSTANCE ))
+#define GET_ID( hWnd ) (LOWORD(MyGetWindowLongPtr( hWnd, GWLP_ID )))
+#else
+#define GET_HINST( hWnd ) ((HINSTANCE) GetWindowWord( hWnd, GWW_HINSTANCE ))
+#define GET_ID( hWnd ) ((WORD) GetWindowWord( hWnd, GWW_ID ))
+#endif
+
+#define CONSTRAIN(v,min,max) ((v) = (v) < (min) ? (min) : (v) > (max) ? (max) : (v))
+
+
+/* function prototypes (private) */
+int app_main (int argc, char *argv[]);
+
+void WinExit (void);
+
+LOCAL void mswin_invalidparameter(const wchar_t *, const wchar_t *,
+ const wchar_t *, unsigned int, uintptr_t);
+
+LOCAL BOOL InitApplication (HANDLE);
+LOCAL HWND InitInstance (HANDLE, int);
+LOCAL void MakeArgv (HINSTANCE hInstance, LPSTR cmdLine, int *pargc,
+ CHAR ***pargv);
+LOCAL LRESULT NEAR CreateTTYInfo (HWND hWnd);
+LOCAL BOOL NEAR DestroyTTYInfo (HWND hWnd);
+LOCAL int ResizeTTYScreen (HWND hWnd, PTTYINFO pTTYInfo,
+ int newNRow, int newNColumn);
+LOCAL BOOL ResetTTYFont (HWND, PTTYINFO, LOGFONT *);
+LOCAL BOOL EraseTTY (HWND, HDC);
+LOCAL BOOL PaintTTY (HWND);
+LOCAL BOOL GetMinMaxInfoTTY (HWND hWnd, MINMAXINFO __far *lpmmi);
+LOCAL BOOL AboutToSizeTTY (HWND hWnd, WINDOWPOS *winPos);
+LOCAL BOOL SizeTTY (HWND, int, CORD, CORD);
+LOCAL BOOL SizingTTY (HWND hWnd, int fwSide, LPRECT pRect);
+LOCAL void FrameRect3D(HDC hdc, RECT * pRC, int width, BOOL raised);
+LOCAL void FillRectColor(HDC hDC, RECT * pRC, COLORREF color);
+
+
+LOCAL BOOL MoveTTY (HWND hWnd, int xPos, int yPos);
+LOCAL void ScrollTTY (HWND hWnd, int wScrollCode, int nPos, HWND hScroll);
+LOCAL void CaretTTY (HWND hWnd, CARETS cStyle);
+LOCAL void CaretCreateTTY (HWND hWnd);
+#ifdef WIN32
+LOCAL void MouseWheelTTY (HWND hWnd, int xPos, int yPos,
+ int fwKeys, int zDelta);
+LOCAL void MouseWheelMultiplier ();
+#endif
+BOOL CALLBACK NoMsgsAreSent (void);
+LOCAL BOOL SetTTYFocus (HWND);
+LOCAL BOOL KillTTYFocus (HWND);
+LOCAL BOOL MoveTTYCursor (HWND);
+LOCAL BOOL ProcessTTYKeyDown (HWND hWnd, TCHAR bOut);
+LOCAL BOOL ProcessTTYCharacter (HWND hWnd, TCHAR bOut);
+LOCAL BOOL ProcessTTYMouse (HWND hWnd, int mevent, int button, CORD xPos,
+ CORD yPos, WPARAM keys);
+LOCAL BOOL ProcessTTYFileDrop (HANDLE wParam);
+LOCAL void ProcessTimer (void);
+LOCAL BOOL WriteTTYBlock (HWND, LPTSTR, int);
+LOCAL BOOL WriteTTYText (HWND, LPTSTR, int);
+LOCAL BOOL WriteTTYChar (HWND, TCHAR);
+
+LOCAL VOID GoModalDialogBoxParam (HINSTANCE, LPTSTR, HWND,
+ DLGPROC, LPARAM);
+LOCAL BOOL SelectTTYFont (HWND);
+LOCAL void SetColorAttribute (COLORREF *cf, char *colorName);
+LOCAL void SetReverseColor (void);
+LOCAL BOOL ConvertRGBString (char *colorName, COLORREF *cf);
+LOCAL char *ConvertStringRGB (char *colorName, size_t ncolorName, COLORREF colorRef);
+LOCAL BOOL ScanInt (char *str, int min, int max, int *val);
+
+LOCAL void TBToggle (HWND);
+LOCAL void TBPosToggle (HWND);
+LOCAL void TBShow (HWND);
+LOCAL void TBHide (HWND);
+LOCAL void TBSwap (HWND, int);
+LOCAL unsigned scrwidth(LPTSTR lpText, int nLength);
+LOCAL long pscreen_offset_from_cord(int row, int col, PTTYINFO pTTYInfo);
+
+LOCAL int tcsucmp(LPTSTR o, LPTSTR r);
+LOCAL int _print_send_page(void);
+
+/* defined in region.c */
+int copyregion(int f, int n);
+
+
+#ifdef ACCELERATORS
+#ifdef ACCELERATORS_OPT
+LOCAL void AccelCtl (HWND hWnd, int ctl, BOOL saveChange);
+#endif
+LOCAL void AccelManage (HWND hWnd, long accels);
+LOCAL void UpdateAccelerators (HWND hWnd);
+#endif
+
+LOCAL UINT UpdateEditAllowed(HWND hWnd);
+LOCAL void PopupConfig(HMENU hMenu, MPopup *members, int *n);
+LOCAL MPopup *PopupId(MPopup *members, int id);
+LOCAL BOOL CopyCutPopup (HWND hWnd, UINT fAllowed);
+
+LOCAL void SelStart (int nRow, int nColumn);
+LOCAL void SelFinish (int nRow, int nColumn);
+LOCAL void SelClear (void);
+LOCAL void SelTrackXYMouse (int xPos, int yPos);
+LOCAL void SelTrackMouse (int nRow, int nColumn);
+LOCAL BOOL SelAvailable (void);
+LOCAL void SelDoCopy (HANDLE hCB, DWORD lenCB);
+
+LOCAL void UpdateTrayIcon(DWORD dwMsg, HICON hIcon, LPTSTR tip);
+
+LOCAL void FlushWriteAccum (void);
+
+LOCAL int mswin_reg_lptstr(int, int, LPTSTR, size_t);
+
+LOCAL BOOL MSWRPoke(HKEY hKey, LPTSTR subkey, LPTSTR valstr, LPTSTR data);
+LOCAL int MSWRClear(HKEY hKey, LPTSTR pSubKey);
+LOCAL void MSWRAlpineSet(HKEY hRootKey, LPTSTR subkey, LPTSTR val,
+ int update, LPTSTR data);
+LOCAL int MSWRProtocolSet(HKEY hKey, int type, LPTSTR path_lptstr);
+LOCAL void MSWRAlpineSetHandlers(int update, LPTSTR path);
+LOCAL int MSWRAlpineGet(HKEY hKey, LPTSTR subkey, LPTSTR val,
+ LPTSTR data_lptstr, size_t len);
+
+LOCAL void MSWIconAddList(int row, int id, HICON hIcon);
+LOCAL int MSWIconPaint(int row, HDC hDC);
+LOCAL void MSWIconFree(IconList **ppIcon);
+LOCAL void MSWRLineBufAdd(MSWR_LINE_BUFFER_S *lpLineBuf, LPTSTR line);
+LOCAL int MSWRDump(HKEY hKey, LPTSTR pSubKey, int keyDepth,
+ MSWR_LINE_BUFFER_S *lpLineBuf);
+
+
+ /* ... interface routines ... */
+
+
+void ProcessMenuItem (HWND hWnd, WPARAM wParam);
+
+void AlarmDeliver (void);
+void HUPDeliver (void);
+
+void PrintFontSameAs (HWND hWnd);
+void PrintFontSelect (HWND hWnd);
+void ExtractFontInfo(LOGFONT *pFont,
+ LPTSTR fontName, size_t nfontName,
+ int *fontSize,
+ LPTSTR fontStyle, size_t nfontStyle,
+ int ppi,
+ LPTSTR fontCharSet, size_t nfontCharSet);
+
+LOCAL void DidResize (PTTYINFO pTTYInfo);
+
+LOCAL void UpdateMenu (HWND hWnd);
+LOCAL void EditCut (void);
+LOCAL void EditCopy (void);
+LOCAL void EditCopyAppend (void);
+LOCAL void EditDoCopyData (HANDLE hCB, DWORD lenCB);
+LOCAL void EditPaste (void);
+LOCAL void EditCancelPaste (void);
+LOCAL UCS EditPasteGet (void);
+LOCAL BOOL EditPasteAvailable (void);
+LOCAL void EditSelectAll (void);
+
+LOCAL void MSWHelpShow (cbstr_t);
+LOCAL int MSWHelpSetMenu (HMENU hmenu);
+
+
+LOCAL void SortHandler (int order, int reverse);
+LOCAL void FlagHandler (int order, int reverse);
+
+LOCAL void MyTimerSet (void);
+
+LOCAL LRESULT ConfirmExit (void);
+
+LOCAL void CQInit (void);
+LOCAL BOOL CQAvailable (void);
+LOCAL BOOL CQAdd (UCS c, BOOL fKeyControlDown);
+LOCAL BOOL CQAddUniq (UCS c, BOOL fKeyControlDown);
+LOCAL UCS CQGet ();
+
+LOCAL void MQInit (void);
+LOCAL BOOL MQAvailable (void);
+LOCAL BOOL MQAdd (int mevent, int button, int nRow, int nColumn,
+ int keys, int flags);
+LOCAL BOOL MQGet (MEvent * pmouse);
+LOCAL BOOL MQClear (int flag);
+
+LOCAL UCS mswin_getc (void);
+
+LOCAL DWORD ExplainSystemErr(void);
+
+LOCAL void RestoreMouseCursor();
+
+LOCAL BYTE mswin_string2charsetid(LPTSTR);
+LOCAL int mswin_charsetid2string (LPTSTR fontCharSet,
+ size_t nfontCharSet, BYTE lfCharSet);
+
+LOCAL int mswin_tw_init(MSWIN_TEXTWINDOW *mswin_tw, int id,
+ LPCTSTR title);
+LOCAL MSWIN_TEXTWINDOW *mswin_tw_displaytext_lptstr (LPTSTR, LPTSTR, size_t,
+ LPTSTR *, MSWIN_TEXTWINDOW *mswin_tw,
+ int);
+LOCAL void mswin_tw_print_callback(MSWIN_TEXTWINDOW *mswin_tw);
+
+
+/* Functions exported to MS Windows. */
+
+LRESULT FAR PASCAL __export PWndProc (HWND, UINT, WPARAM, LPARAM);
+BOOL FAR PASCAL __export ToolBarProc (HWND, UINT, WPARAM, LPARAM);
+LRESULT FAR PASCAL __export TBBtnProc (HWND, UINT, WPARAM, LPARAM);
+BOOL FAR PASCAL __export AboutDlgProc (HWND, UINT, WPARAM, LPARAM);
+BOOL FAR PASCAL __export SplashDlgProc (HWND, UINT, WPARAM, LPARAM);
+
+LOCAL HDDEDATA CALLBACK DdeCallback(UINT uType, UINT uFmt, HCONV hconv,
+ HSZ hsz1, HSZ hsz2, HDDEDATA hdata,
+ DWORD dwData1, DWORD dwData2);
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Module globals.
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+
+PTTYINFO gpTTYInfo;
+HWND ghTTYWnd;
+HINSTANCE ghInstance;
+BOOL gfUseDialogs;
+FILE *mswin_debugfile = NULL;
+int mswin_debug = 0;
+TCHAR gszAppName[45];
+
+LOCAL TCHAR TempBuf [MAXLEN_TEMPSTR];
+
+LOCAL TCHAR gszTTYClass[] = TEXT("PineWnd");
+LOCAL int gAppIdent = APP_UNKNOWN;
+
+LOCAL int gNMW_width;
+
+LOCAL HCURSOR ghCursorArrow = NULL;
+LOCAL HCURSOR ghCursorBusy = NULL;
+LOCAL HCURSOR ghCursorIBeam = NULL;
+LOCAL HCURSOR ghCursorHand = NULL;
+LOCAL HCURSOR ghCursorCurrent = NULL;
+
+LOCAL HWND ghSplashWnd = NULL;
+
+LOCAL COLOR_PAIR *the_rev_color, *the_normal_color;
+
+/* Used for Pasting text. */
+LOCAL HANDLE ghPaste = NULL; /* Handle to Paste data. */
+LOCAL TCHAR *gpPasteNext = NULL; /* Pointer to current char. */
+LOCAL size_t gPasteBytesRemain = 0; /* Count of bytes left. */
+LOCAL BOOL gPasteWasCR = FALSE; /* Previous char was CR. */
+LOCAL int gPasteEnabled = MSWIN_PASTE_DISABLE;
+LOCAL getc_t gCopyCutFunction = NULL;
+LOCAL cbarg_t gScrollCallback = NULL;
+LOCAL BOOL gScrolling = FALSE; /* Keeps track of when we are
+ * in scroll routine. */
+LOCAL cbarg_t gSortCallback = NULL;
+LOCAL cbarg_t gFlagCallback = NULL;
+LOCAL cbarg_t gHdrCallback = NULL;
+LOCAL cbarg_t gZoomCallback = NULL;
+LOCAL cbarg_t gFkeyCallback = NULL;
+LOCAL cbarg_t gSelectedCallback = NULL;
+LOCAL BOOL gMouseTracking = FALSE; /* Keeps track of when we are
+ * tracking the mouse. */
+LOCAL FARPROC gWSBlockingProc = NULL;
+LOCAL DLGPROC gToolBarProc = NULL;
+LOCAL WNDPROC gTBBtnProc = NULL;
+
+LOCAL BOOL gAllowCopy = FALSE;
+LOCAL BOOL gAllowCut = FALSE;
+
+LOCAL BOOL gAllowMouseTrack = FALSE;/* Upper layer interested in
+ * mouse tracking. */
+LOCAL short gsMWMultiplier;
+LOCAL MEvent gMTEvent;
+
+LOCAL cbstr_t gHelpGenCallback = NULL;
+LOCAL BOOL gfHelpGenMenu = FALSE; /* TRUE when help menu
+ * installed. */
+LOCAL cbstr_t gHelpCallback = NULL;
+LOCAL BOOL gfHelpMenu = FALSE; /* TRUE when help menu
+ * installed. */
+LOCAL char *gpCloseText;
+
+LOCAL DWORD gGMLastCall = 0; /* Last time I called
+ * GetMessage. */
+LOCAL BOOL gConfirmExit = FALSE;
+LOCAL HICON ghNormalIcon = NULL;
+LOCAL HICON ghNewMailIcon = NULL;
+LOCAL HICON ghMClosedIcon = NULL;
+
+LOCAL int gPrintFontSize;
+LOCAL TCHAR gPrintFontName[LF_FACESIZE];
+LOCAL TCHAR gPrintFontStyle[64];
+LOCAL TCHAR gPrintFontCharSet[256];
+LOCAL BOOL gPrintFontSameAs = TRUE;
+
+LOCAL UINT gTimerCurrentPeriod = 0;
+
+LOCAL cbvoid_t gPeriodicCallback = NULL; /* Function to call. */
+LOCAL DWORD gPeriodicCBTimeout = 0; /* Time of next call. */
+LOCAL DWORD gPeriodicCBTime = 0; /* Delay between calls. */
+
+//=========================================================================
+// n
+//=========================================================================
+MSWIN_TEXTWINDOW gMswinAltWin = {0};
+MSWIN_TEXTWINDOW gMswinNewMailWin = {0};
+
+MSWIN_TEXTWINDOW gMswinIMAPTelem = {0};
+LOCAL cbvoid_t gIMAPDebugONCallback = NULL;
+LOCAL cbvoid_t gIMAPDebugOFFCallback = NULL;
+LOCAL cbvoid_t gEraseCredsCallback = NULL;
+LOCAL cbvoid_t gViewInWindCallback = NULL;
+
+LOCAL cbvoid_t gConfigScreenCallback = NULL;
+
+LOCAL cbarg_t gMouseTrackCallback = NULL;
+
+/* Currently only implement one SIGNAL so only need single variable. */
+LOCAL SignalType gSignalAlarm = SIG_DFL;
+LOCAL DWORD gAlarmTimeout = 0; /* Time alarm expires in
+ * seconds
+ * (GetTickCount()/1000) */
+LOCAL SignalType gSignalHUP = SIG_DFL;
+
+LOCAL IconList *gIconList = NULL;
+
+
+/*
+ * There is some coordination between these names and the similar names
+ * in osdep/unix. The names in the left column are mapped to the colors
+ * in the right column when displaying. Note that the last eight colors map
+ * to the same thing as the 1st eight colors. This is an attempt to inter-
+ * operate with 16-color xterms. When editing a color and writing the color
+ * into the config file, we use the canonical_name (middle column). So if
+ * the user chooses green from the menu we write color010 in the config
+ * file. [This has changed. Now we put the RGB value in the config file
+ * instead. We leave this so that we can interpret color010 in the config
+ * file from old versions of pine.]
+ * We display that as green later. The xterm also displays that as
+ * green, because the bright green (color010) on the xterm is what we want
+ * to consider to be green, and the other one (color002) is dark green.
+ * And dark green sucks.
+ * On the PC we only use the first 11 colors in this table when giving the
+ * user the set color display, since the last eight are repeats.
+ */
+LOCAL MSWINColor MSWINColorTable[] = {
+ "black", "000,000,000", RGB(0,0,0),
+ "red", "255,000,000", RGB(255,0,0),
+ "green", "000,255,000", RGB(0,255,0),
+ "yellow", "255,255,000", RGB(255,255,0),
+ "blue", "000,000,255", RGB(0,0,255),
+ "magenta", "255,000,255", RGB(255,0,255),
+ "cyan", "000,255,255", RGB(0,255,255),
+ "white", "255,255,255", RGB(255,255,255),
+ "colorlgr", "192,192,192", RGB(192,192,192), /* lite gray */
+ "colormgr", "128,128,128", RGB(128,128,128), /* med. gray */
+ "colordgr", "064,064,064", RGB(64,64,64), /* dark gray */
+ "color008", "000,000,000", RGB(0,0,0),
+ "color009", "255,000,000", RGB(255,0,0),
+ "color010", "000,255,000", RGB(0,255,0),
+ "color011", "255,255,000", RGB(255,255,0),
+ "color012", "000,000,255", RGB(0,0,255),
+ "color013", "255,000,255", RGB(255,0,255),
+ "color014", "000,255,255", RGB(0,255,255),
+ "color015", "255,255,255", RGB(255,255,255),
+ NULL, NULL, 0
+};
+
+#define fullColorTableSize ((sizeof(MSWINColorTable) / sizeof(MSWINColorTable[0])) - 1)
+#define visibleColorTableSize (fullColorTableSize - 8)
+
+
+LOCAL MSWinCaret_t MSWinCaretTable[] = {
+ "Block", CaretBlock,
+ "ShortBlock", CaretSmallBlock,
+ "Underline", CaretHorizBar,
+ "VertBar", CaretVertBar,
+ NULL, 0
+};
+
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Windows Functions.
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+/*---------------------------------------------------------------------------
+ * int PASCAL WinMain( HANDLE hInstance, HANDLE hPrevInstance,
+ * LPSTR lpszCmdLine, int nCmdShow )
+ *
+ * Description:
+ * This is the main window loop!
+ *
+ * Parameters:
+ * As documented for all WinMain() functions.
+ *
+ *--------------------------------------------------------------------------*/
+int PASCAL
+wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
+ LPTSTR lpszCmdLine, int nCmdShow )
+{
+ CHAR **argv;
+ int argc, i, nosplash = 0;
+ char *cmdline_utf8;
+
+ ghInstance = hInstance;
+ if (!hPrevInstance)
+ if (!InitApplication( hInstance ))
+ return ( FALSE ) ;
+
+ /*
+ * Instead of crashing into Dr. Watson on invalid parameter calls
+ * into the C library, just return the error indication that these
+ * functions are normally expected to return.
+ */
+ _set_invalid_parameter_handler (mswin_invalidparameter);
+
+#ifdef OWN_DEBUG_FILE /* Want to write to seperate memdebug.txt file. */
+ mswin_debugfile = fopen ("memdebug.txt", "w");
+ fprintf (mswin_debugfile, "Beginning of mswin debug log\n");
+ if (mswin_debugfile != NULL) {
+ mswin_debug = 4;
+ MemDebug (mswin_debug, mswin_debugfile);
+ fprintf (mswin_debugfile, "Show window as: %d\n", nCmdShow);
+ fflush (mswin_debugfile);
+ }
+#endif
+
+ if (NULL == (ghTTYWnd = InitInstance (hInstance, nCmdShow)))
+ return (FALSE);
+
+ /* cmdline_utf8 memory is never freed */
+ cmdline_utf8 = lptstr_to_utf8(lpszCmdLine);
+
+ MakeArgv (hInstance, cmdline_utf8, &argc, &argv);
+
+ for(i = 0; i < argc; i++)
+ if(strcmp((const char *)argv[i], "-nosplash") == 0){
+ nosplash = 1;
+ break;
+ }
+ /* Create Splash window */
+ if(nosplash == 0){
+ ghSplashWnd = CreateDialog(hInstance,
+ MAKEINTRESOURCE( SPLASHDLGBOX ),
+ ghTTYWnd, (DLGPROC)SplashDlgProc);
+ ShowWindow (ghSplashWnd, SW_SHOWNORMAL);
+ }
+
+ atexit (WinExit);
+
+ app_main (argc, (char **)argv);
+
+ return (TRUE);
+}
+
+
+LOCAL void
+mswin_invalidparameter(const wchar_t *expression,
+ const wchar_t *function,
+ const wchar_t *file,
+ unsigned int line,
+ uintptr_t reserved)
+{
+ /* do nothing for now */
+}
+
+
+void
+WinExit (void)
+{
+ MSG msg;
+
+ if (ghTTYWnd == NULL)
+ return;
+
+ UpdateTrayIcon(NIM_DELETE, 0, NULL);
+
+ /* Destroy main window and process remaining events. */
+ DestroyWindow (ghTTYWnd);
+ while (GetMessage (&msg, NULL, 0, 0)) {
+ TranslateMessage (&msg);
+ DispatchMessage (&msg);
+ }
+
+#ifdef OWN_DEBUG_FILE
+ fclose (mswin_debugfile);
+#endif
+ if (gWSBlockingProc != NULL)
+ FreeProcInstance (gWSBlockingProc);
+
+ MemFreeAll ();
+ ghTTYWnd = NULL;
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL InitApplication( HANDLE hInstance )
+ *
+ * Description:
+ * First time initialization stuff. This registers information
+ * such as window classes.
+ *
+ * Parameters:
+ * HANDLE hInstance
+ * Handle to this instance of the application.
+ *
+ *--------------------------------------------------------------------------*/
+LOCAL BOOL
+InitApplication (HANDLE hInstance)
+{
+ WNDCLASS wndclass;
+
+ /*
+ * Register tty window class.
+ */
+ wndclass.style = 0; /* CS_NOCLOSE; */
+ wndclass.lpfnWndProc = PWndProc;
+ wndclass.cbClsExtra = 0;
+ wndclass.cbWndExtra = sizeof (LONG);
+ wndclass.hInstance = hInstance ;
+ /* In win16 we paint our own icon. */
+ wndclass.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE (ALPINEICON));
+ wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
+ wndclass.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
+ wndclass.lpszMenuName = MAKEINTRESOURCE (ALPINEMENU);
+ wndclass.lpszClassName = gszTTYClass ;
+
+ return (RegisterClass (&wndclass));
+}
+
+
+/*---------------------------------------------------------------------------
+ * HWND InitInstance( HANDLE hInstance, int nCmdShow )
+ *
+ * Description:
+ * Initializes instance specific information.
+ *
+ * Parameters:
+ * HANDLE hInstance
+ * Handle to instance
+ *
+ * int nCmdShow
+ * How do we show the window?
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL HWND
+InitInstance (HANDLE hInstance, int nCmdShow)
+{
+ HWND hTTYWnd;
+ TCHAR appIdent[32];
+ SCROLLINFO scrollInfo;
+
+#ifdef SDEBUG
+ if (mswin_debug >= 5)
+ fprintf (mswin_debugfile, "InitInstance::: entered, nCmdShow %d\n",
+ nCmdShow);
+#endif
+
+ LoadString (hInstance, IDS_APPNAME, gszAppName, sizeof(gszAppName) / sizeof(TCHAR));
+
+ /* create the TTY window */
+ hTTYWnd = CreateWindow (gszTTYClass, gszAppName,
+ WS_OVERLAPPEDWINDOW | WS_VSCROLL,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT,
+ HWND_DESKTOP, NULL, hInstance, NULL);
+
+ scrollInfo.cbSize = sizeof(SCROLLINFO);
+ scrollInfo.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_RANGE | SIF_POS;
+ scrollInfo.nMin = 0;
+ scrollInfo.nMax = 1;
+ scrollInfo.nPage = 1;
+ scrollInfo.nPos = 0;
+ SetScrollInfo(hTTYWnd, SB_VERT, &scrollInfo, FALSE);
+ EnableScrollBar (hTTYWnd, SB_VERT, ESB_DISABLE_BOTH);
+
+ if (NULL == hTTYWnd)
+ return (NULL);
+
+ ghTTYWnd = hTTYWnd;
+
+ ghNormalIcon = LoadIcon (hInstance, MAKEINTRESOURCE (ALPINEICON));
+ ghNewMailIcon = LoadIcon (hInstance, MAKEINTRESOURCE (NEWMAILICON));
+ ghMClosedIcon = LoadIcon (hInstance, MAKEINTRESOURCE (MCLOSEDICON));
+
+ ghCursorArrow = LoadCursor (NULL, IDC_ARROW);
+ ghCursorBusy = LoadCursor (NULL, IDC_WAIT);
+ ghCursorIBeam = LoadCursor (NULL, IDC_IBEAM);
+#ifdef IDC_HAND
+ ghCursorHand = LoadCursor (NULL, IDC_HAND);
+#else
+ ghCursorHand = LoadCursor (hInstance, MAKEINTRESOURCE( PICOHAND ));
+#endif
+ ghCursorCurrent = ghCursorArrow;
+
+ MouseWheelMultiplier();
+
+ CQInit ();
+ MQInit ();
+
+
+ /*
+ * Load a resource with the name of the application. Compare to
+ * known applications to determine who we are running under.
+ * currently, only differentiation is the WINSOCK blocking hook.
+ */
+ LoadString (hInstance, IDS_APPIDENT, appIdent, sizeof(appIdent) / sizeof(TCHAR));
+ if (_tcscmp (appIdent, APP_PINE_IDENT) == 0) {
+ gAppIdent = APP_PINE;
+ gWSBlockingProc = MakeProcInstance ( (FARPROC) NoMsgsAreSent,
+ hInstance);
+ }
+ else if (_tcscmp (appIdent, APP_PICO_IDENT) == 0)
+ gAppIdent = APP_PICO;
+ else
+ gAppIdent = APP_UNKNOWN;
+
+
+ return (hTTYWnd);
+}
+
+
+/*---------------------------------------------------------------------------
+ * void MakeArgv ()
+ *
+ * Description:
+ * Build a standard C argc, argv pointers into the command line string.
+ *
+ *
+ * Parameters:
+ * cmdLine - Command line.
+ * *argc - Count of words.
+ * ***argc - Pointer to Pointer to array of pointers to
+ * characters.
+ *
+ *--------------------------------------------------------------------------*/
+LOCAL void
+MakeArgv (HINSTANCE hInstance, char *cmdLine_utf8, int *pargc, CHAR ***pargv)
+{
+ CHAR **argv;
+ LPSTR c;
+ BOOL inWord, inQuote;
+ int wordCount;
+#define CMD_PATH_LEN 128
+ LPTSTR modPath_lptstr;
+ DWORD mpLen;
+
+
+ /* Count words in cmdLine. */
+ wordCount = 0;
+ inWord = FALSE;
+ inQuote = FALSE;
+ for (c = cmdLine_utf8; *c != '\0'; ++c) {
+ if (inQuote) {
+ if(*c == '"' && (*(c+1) == ' ' || *(c+1) == '\t' || *(c+1) == '\0')){
+ inQuote = inWord = FALSE;
+ }
+ }
+ else {
+ if(inWord && (*c == ' ' || *c == '\t' || *c == '\0')){
+ inWord = FALSE;
+ }
+ else if(!inWord && (*c != ' ' && *c != '\t')){
+ inWord = TRUE;
+ wordCount++;
+ if(*c == '"')
+ inQuote = TRUE;
+ }
+ }
+ }
+
+ ++wordCount; /* One for program name. */
+ argv = (CHAR **) MemAlloc (sizeof (CHAR *) * (wordCount + 1));
+ *pargv = argv;
+ *pargc = wordCount;
+
+ modPath_lptstr = (LPTSTR) MemAlloc (CMD_PATH_LEN*sizeof(TCHAR));
+ mpLen = GetModuleFileName (hInstance, modPath_lptstr, CMD_PATH_LEN);
+ if (mpLen > 0) {
+ *(modPath_lptstr + mpLen) = '\0';
+ *(argv++) = (unsigned char *)lptstr_to_utf8(modPath_lptstr);
+ }
+ else
+ *(argv++) = (unsigned char *)"Alpine/Pico";
+
+ MemFree((void *)modPath_lptstr);
+
+ /* Now break up command line. */
+ inWord = FALSE;
+ inQuote = FALSE;
+ for (c = cmdLine_utf8; *c != '\0'; ++c) {
+ if (inQuote) {
+ if(*c == '"' && (*(c+1) == ' ' || *(c+1) == '\t' || *(c+1) == '\0')){
+ inQuote = inWord = FALSE;
+ *c = '\0';
+ }
+ }
+ else {
+ if(inWord && (*c == ' ' || *c == '\t' || *c == '\0')){
+ *c = '\0';
+ inWord = FALSE;
+ }
+ else if(!inWord && (*c != ' ' && *c != '\t')){
+ inWord = TRUE;
+ if(*c == '"'){
+ inQuote = TRUE;
+ *(argv++) = (unsigned char *)c+1;
+ }
+ else
+ *(argv++) = (unsigned char *)c;
+ }
+ }
+ }
+
+ *argv = NULL; /* tie off argv */
+}
+
+
+/*---------------------------------------------------------------------------
+ * LRESULT FAR PASCAL __export PWndProc( HWND hWnd, UINT uMsg,
+ * WPARAM wParam, LPARAM lParam )
+ *
+ * Description:
+ * This is the TTY Window Proc. This handles ALL messages
+ * to the tty window.
+ *
+ * Parameters:
+ * As documented for Window procedures.
+ *
+/*--------------------------------------------------------------------------*/
+LRESULT FAR PASCAL __export
+PWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+#ifdef CDEBUG
+ if (mswin_debug > 12) {
+ fprintf (mswin_debugfile, "PWndProc:: uMsg = 0x%x\n", uMsg);
+ fflush (mswin_debugfile);
+ }
+#endif
+ switch (uMsg) {
+ case WM_CREATE:
+ MyTimerSet ();
+ return (CreateTTYInfo (hWnd));
+
+ case WM_COMMAND:
+ switch ((WORD) wParam) {
+
+ case IDM_OPT_SETFONT:
+ SelectTTYFont (hWnd);
+ break ;
+
+ case IDM_OPT_FONTSAMEAS:
+ PrintFontSameAs (hWnd);
+ break;
+
+ case IDM_OPT_TOOLBAR:
+ TBToggle (hWnd);
+ break;
+
+ case IDM_OPT_TOOLBARPOS:
+ TBPosToggle (hWnd);
+ break;
+
+ case IDM_OPT_USEDIALOGS: {
+ PTTYINFO pTTYInfo;
+
+ gfUseDialogs = !gfUseDialogs;
+ pTTYInfo = (PTTYINFO)(LONG_PTR)MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo)
+ DidResize (pTTYInfo);
+ break;
+ }
+
+ case IDM_OPT_ERASE_CREDENTIALS:
+ if(gEraseCredsCallback)
+ (*gEraseCredsCallback)();
+ break;
+
+ case IDM_MI_VIEWINWIND:
+ if(gViewInWindCallback)
+ (*gViewInWindCallback)();
+ break;
+
+ case IDM_OPT_IMAPTELEM:
+ mswin_tw_init(&gMswinIMAPTelem, (int)LOWORD(wParam),
+ TEXT("IMAP Telemetry"));
+ SetFocus (ghTTYWnd);
+ break;
+
+ case IDM_OPT_NEWMAILWIN:
+ mswin_tw_init(&gMswinNewMailWin, (int)LOWORD(wParam),
+ TEXT("New Mail View"));
+ SetFocus (ghTTYWnd);
+ break;
+
+#ifdef ACCELERATORS_OPT
+ case IDM_OPT_USEACCEL:
+ AccelCtl (hWnd, ACCEL_TOGGLE, TRUE);
+ break;
+#endif
+
+ case IDM_OPT_SETPRINTFONT:
+ PrintFontSelect (hWnd);
+ break;
+
+ case IDM_OPT_CARETBLOCK :
+ CaretTTY(hWnd, CaretBlock);
+ break;
+
+ case IDM_OPT_CARETSMALLBLOCK :
+ CaretTTY(hWnd, CaretSmallBlock);
+ break;
+
+ case IDM_OPT_CARETVBAR :
+ CaretTTY(hWnd, CaretVertBar);
+ break;
+
+ case IDM_OPT_CARETHBAR :
+ CaretTTY(hWnd, CaretHorizBar);
+ break;
+
+ case IDM_ABOUT:
+ GoModalDialogBoxParam ( GET_HINST( hWnd ),
+ MAKEINTRESOURCE( ABOUTDLGBOX ),
+ hWnd,
+ (DLGPROC)AboutDlgProc, (LPARAM) 0 ) ;
+ break;
+
+ case IDM_EDIT_CUT:
+ EditCut ();
+ break;
+
+ case IDM_EDIT_COPY:
+ EditCopy ();
+ break;
+
+ case IDM_EDIT_COPY_APPEND:
+ EditCopyAppend ();
+ break;
+
+ case IDM_EDIT_PASTE:
+ EditPaste ();
+#ifdef ACCELERATORS
+ UpdateAccelerators (hWnd);
+#endif
+ break;
+
+ case IDM_EDIT_CANCEL_PASTE:
+ EditCancelPaste ();
+#ifdef ACCELERATORS
+ UpdateAccelerators (hWnd);
+#endif
+ break;
+
+ case IDM_EDIT_SEL_ALL :
+ EditSelectAll();
+ break;
+
+ case IDM_HELP:
+ MSWHelpShow (gHelpCallback);
+ break;
+
+ case IDM_MI_GENERALHELP:
+ MSWHelpShow (gHelpGenCallback);
+ break;
+
+ case IDM_MI_WHEREIS :
+ CQAdd (gpTTYInfo->menuItems[KS_WHEREIS - KS_RANGESTART].miKey, 0);
+ break;
+
+ case IDM_MI_SORTSUBJECT :
+ case IDM_MI_SORTARRIVAL :
+ case IDM_MI_SORTSIZE :
+ case IDM_MI_SORTFROM :
+ case IDM_MI_SORTTO :
+ case IDM_MI_SORTCC :
+ case IDM_MI_SORTDATE :
+ case IDM_MI_SORTORDERSUB :
+ case IDM_MI_SORTSCORE :
+ case IDM_MI_SORTTHREAD :
+ SortHandler((int)(wParam - IDM_MI_SORTSUBJECT), 0);
+ break;
+
+ case IDM_MI_SORTREVERSE :
+ SortHandler(-1, 1);
+ break;
+
+ case IDM_MI_FLAGIMPORTANT :
+ case IDM_MI_FLAGNEW :
+ case IDM_MI_FLAGANSWERED :
+ case IDM_MI_FLAGDELETED :
+ FlagHandler((int)(wParam - IDM_MI_FLAGIMPORTANT), 0);
+ break;
+
+ default:
+ /* value falling within the menu item range are handled here. */
+ if (wParam >= KS_RANGESTART && wParam <= KS_RANGEEND){
+ ProcessMenuItem (hWnd, wParam);
+ break;
+ }
+ break;
+ }
+ break ;
+
+ case WM_VSCROLL:
+ ScrollTTY (hWnd, LOWORD(wParam), HIWORD(wParam), (HWND) lParam);
+ break;
+
+ case WM_MOUSEWHEEL:
+ MouseWheelTTY (hWnd, LOWORD(lParam), HIWORD(lParam),
+ LOWORD(wParam), (short) HIWORD(wParam));
+ break;
+
+ case WM_ERASEBKGND:
+ if (IsIconic (hWnd))
+ return (DefWindowProc (hWnd, WM_ICONERASEBKGND, wParam, lParam));
+ else
+ EraseTTY (hWnd, (HDC) wParam);
+ break;
+
+ case WM_QUERYDRAGICON:
+ return ((LRESULT)ghNormalIcon);
+
+ case WM_PAINT:
+ PaintTTY (hWnd);
+ break ;
+
+ case WM_GETMINMAXINFO:
+ GetMinMaxInfoTTY (hWnd, (MINMAXINFO __far *)lParam);
+ break;
+
+ case WM_SIZE:
+ SizeTTY (hWnd, (int)wParam, HIWORD(lParam), LOWORD(lParam));
+ break ;
+
+ case WM_SIZING :
+ return(SizingTTY(hWnd, (int)wParam, (LPRECT) lParam));
+
+ case WM_MOVE:
+/* MoveTTY (hWnd, (int) LOWORD(lParam), (int) HIWORD(lParam)); */
+ break;
+
+ case WM_WINDOWPOSCHANGING:
+ /* Allows us to adjust new size of window. */
+ AboutToSizeTTY (hWnd, (WINDOWPOS FAR *) lParam);
+ break;
+
+
+ case TASKBAR_ICON_MESSAGE:
+ /* Notification of a mouse event in the task bar tray.
+ * If they are clicking on it we will restore the window. */
+ if (lParam == WM_LBUTTONDOWN){
+ if(gpTTYInfo->fMinimized)
+ ShowWindow (hWnd, SW_RESTORE);
+ else
+ SetForegroundWindow(hWnd);
+ }
+
+ break;
+
+
+ /*
+ * WM_KEYDOWN is sent for every "key press" and reports on the
+ * keyboard key, with out processing shift and control keys.
+ * WM_CHAR is a synthetic event, created from KEYDOWN and KEYUP
+ * events. It includes processing or control and shift characters.
+ * But does not get generated for extended keys suchs as arrow
+ * keys.
+ * I'm going to try to use KEYDOWN for processing just extended keys
+ * and let CHAR handle the the rest.
+ *
+ * The only key combo that is special is ^-space. For that, I'll use
+ * WM_KEYDOWN and WM_KEYUP to track the state of the control key.
+ */
+ case WM_CHAR:
+ /*
+ * If the windows cursor is visible and we have keyboard input
+ * then hide the windows cursor
+ */
+ mswin_showcursor(FALSE);
+ ProcessTTYCharacter (hWnd, (TCHAR)wParam);
+ break ;
+
+ case WM_KEYDOWN:
+ if (ProcessTTYKeyDown (hWnd, (TCHAR) wParam))
+ return (0);
+
+ return( DefWindowProc( hWnd, uMsg, wParam, lParam ) ) ;
+
+ case WM_SYSCHAR:
+ if (gFkeyCallback && (*gFkeyCallback)(0, 0)
+ && LOBYTE (wParam) == VK_F10){
+ ProcessTTYCharacter (hWnd, (TCHAR)wParam);
+ return (0);
+ }
+
+ return( DefWindowProc( hWnd, uMsg, wParam, lParam ) ) ;
+
+ case WM_SYSKEYDOWN:
+ /*
+ * lParam specifies the context code. Bit 29 is 1 if the ALT key is down
+ * while the key is pressed.
+ */
+ if (!(lParam & (1<<29))
+ && gFkeyCallback && (*gFkeyCallback)(0, 0)
+ && LOBYTE (wParam) == VK_F10
+ && ProcessTTYKeyDown (hWnd, (TCHAR) wParam))
+ return (0);
+
+ return( DefWindowProc( hWnd, uMsg, wParam, lParam ) ) ;
+
+
+ case WM_LBUTTONDOWN:
+ ProcessTTYMouse (hWnd, M_EVENT_DOWN, M_BUTTON_LEFT, LOWORD (lParam),
+ HIWORD (lParam), wParam);
+ break;
+
+ case WM_LBUTTONUP:
+ if (ProcessTTYMouse (hWnd, M_EVENT_UP, M_BUTTON_LEFT, LOWORD (lParam),
+ HIWORD (lParam), wParam))
+ goto callDef;
+ break;
+
+ case WM_MBUTTONDOWN:
+ ProcessTTYMouse (hWnd, M_EVENT_DOWN, M_BUTTON_MIDDLE, LOWORD (lParam),
+ HIWORD (lParam), wParam);
+ break;
+
+ case WM_MBUTTONUP:
+ ProcessTTYMouse (hWnd, M_EVENT_UP, M_BUTTON_MIDDLE, LOWORD (lParam),
+ HIWORD (lParam), wParam);
+ break;
+
+ case WM_RBUTTONDOWN:
+ ProcessTTYMouse (hWnd, M_EVENT_DOWN, M_BUTTON_RIGHT, LOWORD (lParam),
+ HIWORD (lParam), wParam);
+ break;
+
+ case WM_RBUTTONUP:
+ ProcessTTYMouse (hWnd, M_EVENT_UP, M_BUTTON_RIGHT, LOWORD (lParam),
+ HIWORD (lParam), wParam);
+ break;
+
+ case WM_MOUSEMOVE:
+ ProcessTTYMouse (hWnd, M_EVENT_TRACK, 0, LOWORD (lParam),
+ HIWORD (lParam), wParam);
+ break;
+
+ case WM_NCMOUSEMOVE:
+ mswin_showcursor(TRUE);
+ goto callDef; /* pretend it never happened */
+
+ case WM_SETFOCUS:
+ SetTTYFocus (hWnd);
+ break;
+
+ case WM_KILLFOCUS:
+ KillTTYFocus (hWnd);
+ break;
+
+ case WM_SETCURSOR:
+ /* Set cursor. If in client, leave as is. Otherwise, pass to
+ * DefWindow Proc */
+ if (LOWORD(lParam) == HTCLIENT) {
+ SetCursor (ghCursorCurrent);
+ return (TRUE);
+ }
+
+ return( DefWindowProc( hWnd, uMsg, wParam, lParam ) ) ;
+
+ case WM_INITMENU:
+ UpdateMenu (hWnd);
+ break;
+
+ case WM_TIMER:
+ /* Really just used so that we continue to receive messages even while
+ * in background. Causes mswin_getc() to process message and return
+ * to caller so that it can get some periodic processing in. */
+ ProcessTimer ();
+ break;
+
+ case WM_QUERYENDSESSION:
+ /* Returns non-zero if I can exit, otherwize zero, and the end
+ * session operation stops. */
+ return ((LRESULT)ConfirmExit ());
+
+ case WM_DESTROY:
+ KillTimer (hWnd, MY_TIMER_ID);
+ DestroyTTYInfo (hWnd);
+ PostQuitMessage (0);
+ break;
+
+
+ case WM_DROPFILES:
+ if(ProcessTTYFileDrop((HANDLE) wParam) == TRUE)
+ SetForegroundWindow(hWnd);
+
+ break;
+
+
+ case WM_CLOSE:
+ /* If the quit menu is active then insert the quit command
+ * Otherwise, abort. */
+ if (gpTTYInfo->menuItems[KS_EXIT - KS_RANGESTART].miActive) {
+ CQAdd (gpTTYInfo->menuItems[KS_EXIT - KS_RANGESTART].miKey, 0);
+ }
+ else if (gSignalHUP != SIG_DFL && gSignalHUP != SIG_IGN) {
+ if (MessageBox (hWnd,
+ TEXT("Abort PINE/PICO, possibly losing current work?"),
+ gszAppName, MB_OKCANCEL | MB_ICONQUESTION) == IDOK)
+ HUPDeliver ();
+ }
+ break;
+
+
+ default:
+callDef:
+ return( DefWindowProc( hWnd, uMsg, wParam, lParam ) ) ;
+ }
+ return 0L ;
+
+} /* end of PWndProc() */
+
+
+/*---------------------------------------------------------------------------
+ * LRESULT NEAR CreateTTYInfo( HWND hWnd )
+ *
+ * Description:
+ * Creates the tty information structure and sets
+ * menu option availability. Returns -1 if unsuccessful.
+ *
+ * Parameters:
+ * HWND hWnd
+ * Handle to main window.
+ *
+ *-------------------------------------------------------------------------*/
+LOCAL LRESULT NEAR
+CreateTTYInfo (HWND hWnd)
+{
+ HMENU hMenu;
+ PTTYINFO pTTYInfo;
+ LOGFONT newFont;
+ int i, ppi;
+ HDC hDC;
+ HFONT testFont;
+#ifdef SDEBUG
+ if (mswin_debug >= 5)
+ fprintf (mswin_debugfile, "CreateTTYInfo::: entered\n");
+#endif
+
+ hDC = GetDC (ghTTYWnd);
+ ppi = GetDeviceCaps (hDC, LOGPIXELSY);
+ ReleaseDC (ghTTYWnd, hDC);
+
+ pTTYInfo = (PTTYINFO) MemAlloc (sizeof (TTYINFO));
+ if (pTTYInfo == NULL)
+ return ((LRESULT) - 1);
+ gpTTYInfo = pTTYInfo;
+
+ /* initialize TTY info structure */
+ memset (pTTYInfo, 0, sizeof (TTYINFO));
+ /* Shown but not focused. */
+ pTTYInfo->cCaretStyle = CaretBlock;
+ pTTYInfo->fMinimized = FALSE;
+ pTTYInfo->fMaximized = FALSE;
+ pTTYInfo->fFocused = FALSE;
+ pTTYInfo->fNewLine = FALSE;
+ pTTYInfo->fMassiveUpdate = FALSE;
+ pTTYInfo->fNewMailIcon = FALSE;
+ pTTYInfo->fMClosedIcon = FALSE;
+ pTTYInfo->autoWrap = WRAP_NO_SCROLL;
+ pTTYInfo->xOffset = MARGINE_LEFT;
+ pTTYInfo->yOffset = MARGINE_TOP;
+ pTTYInfo->fDesiredSize = FALSE;
+ pTTYInfo->fCursorOn = TRUE;
+ pico_nfcolor(NULL);
+ pico_nbcolor(NULL);
+ pico_rfcolor(NULL);
+ pico_rbcolor(NULL);
+ pico_set_normal_color();
+ pTTYInfo->toolBarTop = TRUE;
+ pTTYInfo->curToolBarID = IDD_TOOLBAR;
+
+ /* Clear menu item array. */
+ pTTYInfo->curWinMenu = ALPINEMENU;
+ for (i = 0; i < KS_COUNT; ++i)
+ pTTYInfo->menuItems[i].miActive = FALSE;
+ pTTYInfo->menuItemsCurrent = FALSE;
+
+ /* Clear resize callback procs. */
+ for (i = 0; i < RESIZE_CALLBACK_ARRAY_SIZE; ++i)
+ pTTYInfo->resizer[i] = NULL;
+
+
+ /* clear screen space */
+ pTTYInfo->pScreen = NULL;
+ pTTYInfo->pCellWidth = NULL;
+ pTTYInfo->pAttrib = NULL;
+
+ /* setup default font information */
+
+ newFont.lfHeight = -MulDiv(12, ppi, 72);
+ newFont.lfWidth = 0;
+ newFont.lfEscapement = 0;
+ newFont.lfOrientation = 0;
+ newFont.lfWeight = 0;
+ newFont.lfItalic = 0;
+ newFont.lfUnderline = 0;
+ newFont.lfStrikeOut = 0;
+ newFont.lfCharSet = FONT_CHARSET_FONT;
+ newFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
+ newFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ newFont.lfQuality = DEFAULT_QUALITY;
+ newFont.lfPitchAndFamily = FIXED_PITCH | FF_MODERN;
+ _sntprintf(newFont.lfFaceName, LF_FACESIZE, TEXT("%s"), TEXT("Courier New"));
+ testFont = CreateFontIndirect(&newFont);
+ if(NULL == testFont)
+ newFont.lfFaceName[0] = '\0';
+ else
+ DeleteObject(testFont);
+
+ /* set TTYInfo handle before any further message processing. */
+
+ MySetWindowLongPtr (hWnd, GWL_PTTYINFO, pTTYInfo);
+
+ /* reset the character information, etc. */
+
+ ResetTTYFont (hWnd, pTTYInfo, &newFont);
+
+
+
+ hMenu = GetMenu (hWnd);
+ EnableMenuItem (hMenu, IDM_EDIT_CUT, MF_BYCOMMAND | MF_GRAYED);
+ EnableMenuItem (hMenu, IDM_EDIT_COPY, MF_BYCOMMAND | MF_GRAYED);
+ EnableMenuItem (hMenu, IDM_EDIT_COPY_APPEND, MF_BYCOMMAND | MF_GRAYED);
+ EnableMenuItem (hMenu, IDM_EDIT_PASTE, MF_BYCOMMAND | MF_GRAYED);
+ return ((LRESULT) TRUE);
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL NEAR DestroyTTYInfo( HWND hWnd )
+ *
+ * Description:
+ * Destroys block associated with TTY window handle.
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window
+ *
+ *-------------------------------------------------------------------------*/
+LOCAL BOOL NEAR
+DestroyTTYInfo (HWND hWnd)
+{
+ PTTYINFO pTTYInfo;
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return (FALSE);
+
+#ifdef ACCELERATORS
+ if(pTTYInfo->hAccel){
+ DestroyAcceleratorTable(pTTYInfo->hAccel);
+ pTTYInfo->hAccel = NULL;
+ pTTYInfo->fAccel = EM_NONE;
+ }
+#endif
+
+ if(pTTYInfo->hTBWnd != NULL)
+ DestroyWindow (pTTYInfo->hTBWnd);
+
+ if(pTTYInfo->hTBBrush != NULL)
+ DeleteObject(pTTYInfo->hTBBrush);
+
+ DeleteObject (pTTYInfo->hTTYFont);
+
+ MemFree (pTTYInfo);
+ return (TRUE);
+}
+
+
+/*---------------------------------------------------------------------------
+ * void ResizeTTYScreen( HWND hWnd, PTTYINFO pTTYInfo,
+ * int newNrow, int newNColumn);
+ *
+ * Description:
+ * Resize the screen to new size, copying data.
+ *
+ * Parameters:
+ * PTTYINFO pTTYInfo
+ * pointer to TTY info structure
+ * newNCo.umn, newNRow
+ * new size of screen.
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+ResizeTTYScreen (HWND hWnd, PTTYINFO pTTYInfo, int newNRow, int newNColumn)
+{
+ CharAttrib *pNewAttrib, tmpAttrib, *pSourceAtt, *pDestAtt;
+ TCHAR *pNewScreen, *pSource, *pDest;
+ int *pNewCW, *pSourceCW, *pDestCW;
+ size_t len;
+ int cells;
+ int r, i;
+ extern TERM term;
+
+
+ if (newNColumn < MINNCOLUMN)
+ newNColumn = MINNCOLUMN;
+ if (newNRow < MINNROW)
+ newNRow = MINNROW;
+
+#ifdef SDEBUG
+ if (mswin_debug >= 5)
+ fprintf (mswin_debugfile, "ResizeTTYScreen::: entered, new row %d, col %d\n",
+ newNRow, newNColumn);
+#endif
+
+
+ SelClear ();
+ cells = newNColumn * newNRow;
+ pNewScreen = (TCHAR *)MemAlloc (cells * sizeof (TCHAR));
+ if (pNewScreen == NULL)
+ return (FALSE);
+
+ pNewCW = (int *)MemAlloc(cells * sizeof(int));
+ if(pNewCW == NULL){
+ MemFree((void *)pNewScreen);
+ return(FALSE);
+ }
+
+ pNewAttrib = (CharAttrib *)MemAlloc (cells * sizeof (CharAttrib));
+ if (pNewAttrib == NULL) {
+ MemFree ((void *)pNewScreen);
+ MemFree ((void *)pNewCW);
+ return (FALSE);
+ }
+
+
+ /*
+ * Clear new screen.
+ */
+
+ for(i = 0; i < cells; i++){
+ pNewScreen[i] = ' ';
+ pNewCW[i] = pTTYInfo->xChar; /* xChar set yet ? */
+ }
+
+ tmpAttrib.style = CHAR_ATTR_NORM;
+ tmpAttrib.rgbFG = pTTYInfo->rgbFGColor;
+ tmpAttrib.rgbBG = pTTYInfo->rgbBGColor;
+ for(r = 0; r < cells; r++)
+ pNewAttrib[r] = tmpAttrib;
+
+ /*
+ * Copy old screen onto new screen.
+ */
+ if (pTTYInfo->pScreen != NULL) {
+
+ for (r = 1; r <= newNRow && r <= pTTYInfo->actNRow; ++r) {
+ pSource = pTTYInfo->pScreen + ((pTTYInfo->actNRow - r) *
+ pTTYInfo->actNColumn);
+ pDest = pNewScreen + ((newNRow - r) * newNColumn);
+ len = MIN (newNColumn, pTTYInfo->actNColumn);
+ for(i = 0; i < len; i++)
+ pDest[i] = pSource[i];
+
+ pSourceCW = pTTYInfo->pCellWidth
+ + ((pTTYInfo->actNRow - r) * pTTYInfo->actNColumn);
+ pDestCW = pNewCW + ((newNRow - r) * newNColumn);
+ memcpy(pDestCW, pSourceCW, len * sizeof(int));
+
+ pSourceAtt = pTTYInfo->pAttrib
+ + ((pTTYInfo->actNRow - r) * pTTYInfo->actNColumn);
+ pDestAtt = pNewAttrib + ((newNRow - r) * newNColumn);
+ len = MIN (newNColumn, pTTYInfo->actNColumn);
+ memcpy (pDestAtt, pSourceAtt, len * sizeof(CharAttrib));
+ }
+
+ pTTYInfo->nColumn = (CORD)MIN (pTTYInfo->nColumn, newNColumn);
+ pTTYInfo->nRow = (CORD)MAX (0,
+ pTTYInfo->nRow + (newNRow - pTTYInfo->actNRow));
+ MemFree (pTTYInfo->pScreen);
+ MemFree (pTTYInfo->pCellWidth);
+ MemFree (pTTYInfo->pAttrib);
+ }
+ else {
+ pTTYInfo->nColumn = (CORD)MIN (pTTYInfo->nColumn, newNColumn);
+ pTTYInfo->nRow = (CORD)MIN (pTTYInfo->nRow, newNRow);
+ }
+
+ pTTYInfo->pScreen = pNewScreen;
+ pTTYInfo->pCellWidth = pNewCW;
+ pTTYInfo->pAttrib = pNewAttrib;
+ pTTYInfo->actNColumn = newNColumn;
+ pTTYInfo->actNRow = newNRow;
+
+
+ /* Repaint whole screen. */
+ pTTYInfo->screenDirty = TRUE;
+ pTTYInfo->eraseScreen = TRUE;
+ InvalidateRect (hWnd, NULL, FALSE);
+
+
+
+ /* Pico specific. */
+ if (term.t_nrow == 0) {
+ term.t_nrow = (short)(newNRow - 1);
+ term.t_ncol = (short)newNColumn;
+ }
+
+
+ return (TRUE);
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL ResetTTYFont( HWND hWnd, PTTYINFO pTTYInfo, LOGFONT *newFont)
+ *
+ * Description:
+ * Resets the TTY character information and causes the
+ * screen to resize to update the scroll information.
+ *
+ * Parameters:
+ * PTTYINFO pTTYInfo
+ * pointer to TTY info structure
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+ResetTTYFont (HWND hWnd, PTTYINFO pTTYInfo, LOGFONT *newFont)
+{
+ HDC hDC;
+ HFONT hFont;
+ TEXTMETRIC tm;
+ int newNRow;
+ int newNColumn;
+ BOOL newsize;
+
+
+#ifdef SDEBUG
+ if (mswin_debug >= 5)
+ fprintf (mswin_debugfile, "ResetTTYFont::: entered, curent window size X %d, Y %d\n",
+ pTTYInfo->xSize, pTTYInfo->ySize);
+#endif
+
+
+ if (NULL == pTTYInfo)
+ return (FALSE);
+
+ SelClear ();
+
+ /*
+ * Create new font.
+ */
+ hFont = CreateFontIndirect (newFont);
+ if (hFont == NULL)
+ return (FALSE);
+ hDC = GetDC (hWnd);
+ SelectObject (hDC, hFont);
+ GetTextMetrics (hDC, &tm);
+ ReleaseDC (hWnd, hDC);
+
+
+ /*
+ * Replace old font.
+ */
+ if (NULL != pTTYInfo->hTTYFont)
+ DeleteObject (pTTYInfo->hTTYFont);
+ pTTYInfo->hTTYFont = hFont;
+ memcpy (&pTTYInfo->lfTTYFont, newFont, sizeof (LOGFONT));
+
+
+ /* Update the char cell size. */
+ pTTYInfo->xChar = (CORD)tm.tmAveCharWidth;
+ pTTYInfo->yChar = (CORD)(tm.tmHeight + tm.tmExternalLeading);
+
+ /* Update the current number of rows and cols. Don't allow
+ * either to be less than zero. */
+ newNRow = MAX (MINNROW,
+ MIN (MAXNROW,
+ (pTTYInfo->ySize - pTTYInfo->toolBarSize - (2 * MARGINE_TOP))/
+ pTTYInfo->yChar));
+ newNColumn = MAX (MINNCOLUMN,
+ MIN (MAXNCOLUMN, (pTTYInfo->xSize - (2 * pTTYInfo->xOffset))/
+ pTTYInfo->xChar));
+
+ newsize = newNRow != pTTYInfo->actNRow ||
+ newNColumn != pTTYInfo->actNColumn;
+ if (newsize)
+ ResizeTTYScreen (hWnd, pTTYInfo, newNRow, newNColumn);
+
+ /* Resize the caret as well. */
+ if(pTTYInfo->fCaretOn)
+ HideCaret (hWnd);
+
+ DestroyCaret ();
+ CaretCreateTTY (hWnd);
+
+ /* Redraw screen and, if the "size" changed, tell the upper layers. */
+ pTTYInfo->screenDirty = TRUE;
+ pTTYInfo->eraseScreen = TRUE;
+ InvalidateRect (hWnd, NULL, FALSE);
+
+ /* Always call the resize functions - even if the screen size
+ * has not changed, the font style may have. */
+ DidResize (pTTYInfo);
+
+ return (TRUE);
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL EraseTTY (HWND hWnd, HDC hDC)
+ *
+ * Description:
+ * Erase the tty background.
+ *
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window (as always)
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+EraseTTY (HWND hWnd, HDC hDC)
+{
+ RECT erect;
+ HBRUSH hBrush;
+
+
+ GetClientRect (hWnd, &erect);
+ hBrush = CreateSolidBrush (gpTTYInfo->rgbBGColor);
+ if (hBrush != NULL) {
+ FillRect (hDC, &erect, hBrush);
+ DeleteObject (hBrush);
+ }
+ return (TRUE);
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL PaintTTY( HWND hWnd )
+ *
+ * Description:
+ * Paints the rectangle determined by the paint struct of
+ * the DC.
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window (as always)
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+PaintTTY (HWND hWnd)
+{
+ int nRow, nCol; /* Top left corner of update. */
+ int nEndRow, nEndCol; /* lower right corner of update. */
+ int nHorzPos, nVertPos; /* Position of each text write. */
+ int col; /* start col of run of similar attr */
+ int count; /* count of run of similar attrib. */
+ int endCount; /* How far to count. */
+ CharAttrib *pAttrib;
+ HDC hDC;
+ LOGFONT tmpFont;
+ HFONT hOrigFont, hOldFont = NULL, hTmpFont;
+ PTTYINFO pTTYInfo;
+ PAINTSTRUCT ps;
+ RECT rect;
+ RECT erect;
+ HBRUSH hBrush;
+ long offset; /* Offset into pScreen array */
+ long endoffset; /* Offset of nEndCol in each row array */
+ CharAttrib *pLastAttrib; /* Attributes of last text write. */
+ CharAttrib *pNewAttrib; /* Attributes of this text write. */
+
+
+#ifdef CDEBUG
+ if (mswin_debug >= 9)
+ fprintf (mswin_debugfile, "PaintTTY::: entered\n");
+#endif
+
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return (FALSE);
+
+ if (IsIconic (hWnd))
+ return (TRUE);
+
+ hDC = BeginPaint (hWnd, &ps);
+ rect = ps.rcPaint;
+
+ hOrigFont = SelectObject (hDC, pTTYInfo->hTTYFont);
+ SetTextColor (hDC, pTTYInfo->rgbFGColor);
+ SetBkColor (hDC, pTTYInfo->rgbBGColor);
+ SetBkMode (hDC, OPAQUE);
+
+ nRow = (rect.top - pTTYInfo->yOffset) / pTTYInfo->yChar;
+ CONSTRAIN (nRow, 0, pTTYInfo->actNRow - 1);
+
+ nEndRow = MIN(pTTYInfo->actNRow - 1,
+ ((rect.bottom - pTTYInfo->yOffset - 1) / pTTYInfo->yChar));
+ nCol = MIN(pTTYInfo->actNColumn - 1,
+ MAX(0, (rect.left - pTTYInfo->xOffset) / pTTYInfo->xChar));
+ nEndCol = MIN(pTTYInfo->actNColumn - 1,
+ ((rect.right - pTTYInfo->xOffset - 1) / pTTYInfo->xChar));
+
+ pLastAttrib = NULL;
+
+ /* Erase screen if necessary. */
+ if (pTTYInfo->eraseScreen) {
+ erect.top = 0;
+ erect.left = 0;
+ erect.bottom = pTTYInfo->ySize;
+ erect.right = pTTYInfo->xSize;
+ hBrush = CreateSolidBrush (pTTYInfo->rgbBGColor);
+ if (hBrush != NULL) {
+ FillRect (hDC, &erect, hBrush);
+ DeleteObject (hBrush);
+ }
+ pTTYInfo->eraseScreen = FALSE;
+ }
+
+
+ /* Paint an inset frame around the text region. */
+ if (pTTYInfo->toolBarSize == 0) {
+ erect.top = 0;
+ erect.bottom = pTTYInfo->ySize;
+ }
+ else if (pTTYInfo->toolBarTop) {
+ erect.top = pTTYInfo->toolBarSize;
+ erect.bottom = pTTYInfo->ySize;
+ }
+ else {
+ erect.top = 0;
+ erect.bottom = pTTYInfo->ySize - pTTYInfo->toolBarSize;
+ }
+ erect.left = 0;
+ erect.right = pTTYInfo->xSize;
+ FrameRect3D (hDC, &erect, FRAME_3D_SIZE, FALSE);
+
+ /* Paint rows of text. */
+ for (; nRow <= nEndRow; nRow++) {
+ nVertPos = (nRow * pTTYInfo->yChar) + pTTYInfo->yOffset;
+ rect.top = nVertPos;
+ rect.bottom = nVertPos + pTTYInfo->yChar;
+
+ /* Paint runs of similar attributes. */
+ col = nCol; /* Start at left. */
+
+ if(col == 0 && MSWIconPaint(nRow, hDC))
+ col += 2;
+
+ /*
+ * col is the column on the screen, not the index
+ * into the array.
+ */
+ while (col <= nEndCol) { /* While not past right. */
+
+ /* Starting with Character at nRow, col, what is its attribute? */
+
+ /* offset is an index into the array */
+ offset = pscreen_offset_from_cord(nRow, col, pTTYInfo);
+ pNewAttrib = pTTYInfo->pAttrib + offset;
+
+ hTmpFont = NULL;
+ if (!(pLastAttrib
+ && pNewAttrib->style == pLastAttrib->style
+ && pNewAttrib->rgbFG == pLastAttrib->rgbFG
+ && pNewAttrib->rgbBG == pLastAttrib->rgbBG)) {
+ /*
+ * Require new font?
+ */
+ if(!pLastAttrib
+ || (pNewAttrib->style & CHAR_ATTR_ULINE)
+ != (pLastAttrib->style & CHAR_ATTR_ULINE)){
+ if(pNewAttrib->style & CHAR_ATTR_ULINE){
+ /*
+ * Find suitable attribute font...
+ */
+ memcpy (&tmpFont, &pTTYInfo->lfTTYFont,
+ sizeof (LOGFONT));
+
+ tmpFont.lfHeight = - pTTYInfo->yChar;
+ tmpFont.lfWidth = - pTTYInfo->xChar;
+
+ tmpFont.lfUnderline = (BYTE)((pNewAttrib->style
+ & CHAR_ATTR_ULINE)
+ == CHAR_ATTR_ULINE);
+
+ hTmpFont = CreateFontIndirect (&tmpFont);
+
+ hOldFont = SelectObject (hDC, hTmpFont);
+ }
+ }
+
+ /*
+ * Set new color attributes. If Reverse or Selected, then
+ * show in reverse colors. But if neither, or both, then
+ * normal colors.
+ */
+ if(pNewAttrib->style & CHAR_ATTR_SEL){
+ SetTextColor (hDC, pNewAttrib->rgbBG);
+ SetBkColor (hDC, pNewAttrib->rgbFG);
+ }
+ else {
+ if(!(pLastAttrib
+ && pNewAttrib->rgbFG == pLastAttrib->rgbFG)
+ || (pLastAttrib->style & CHAR_ATTR_SEL))
+ SetTextColor (hDC, pNewAttrib->rgbFG);
+
+ if(!(pLastAttrib
+ && pNewAttrib->rgbBG == pLastAttrib->rgbBG)
+ || (pLastAttrib->style & CHAR_ATTR_SEL))
+ SetBkColor (hDC, pNewAttrib->rgbBG);
+ }
+ }
+
+ /* Find run of similar attributes. */
+ count = 1;
+ pAttrib = pTTYInfo->pAttrib + (offset + 1);
+ /* endoffset is an index into the pScreen array */
+ endoffset = pscreen_offset_from_cord(nRow, nEndCol, pTTYInfo);
+ endCount = endoffset - offset;
+ while (count <= endCount
+ && pAttrib->style == pNewAttrib->style
+ && pAttrib->rgbFG == pNewAttrib->rgbFG
+ && pAttrib->rgbBG == pNewAttrib->rgbBG){
+ ++pAttrib;
+ ++count;
+ }
+
+ if(hTmpFont != NULL){
+/* BUG: compute new offsets based on hTmpFont font if required */
+ nHorzPos = (col * pTTYInfo->xChar) + pTTYInfo->xOffset;
+ rect.left = nHorzPos;
+ rect.right = nHorzPos + pTTYInfo->xChar * scrwidth(pTTYInfo->pScreen+offset, count);
+ }
+ else{
+ /* Paint run of characters from nRow, col to nRow, col + count
+ * rect.top and rect.bottom have already been calculated. */
+ nHorzPos = (col * pTTYInfo->xChar) + pTTYInfo->xOffset;
+ rect.left = nHorzPos;
+ rect.right = nHorzPos + pTTYInfo->xChar * scrwidth(pTTYInfo->pScreen+offset, count);
+ }
+
+ ExtTextOut (hDC, nHorzPos, nVertPos, ETO_OPAQUE | ETO_CLIPPED,
+ &rect, (LPTSTR) (pTTYInfo->pScreen + offset),
+ count, (int *)(pTTYInfo->pCellWidth+offset));
+
+ /* Overstrike bold chars by hand to preserve char cell size */
+ if(pNewAttrib->style & CHAR_ATTR_BOLD){
+ int old_mode = GetBkMode(hDC);
+ SetBkMode (hDC, TRANSPARENT);
+ ExtTextOut (hDC, nHorzPos + 1, nVertPos, 0,
+ &rect, (LPTSTR) (pTTYInfo->pScreen + offset),
+ count, (int *)(pTTYInfo->pCellWidth+offset));
+ if(old_mode)
+ SetBkMode (hDC, old_mode);
+ }
+
+ /* Move pointer to end of this span of characters. */
+ col += MAX(scrwidth(pTTYInfo->pScreen+offset, count), 1);
+ pLastAttrib = pNewAttrib;
+
+ if(hTmpFont != NULL){
+ SelectObject(hDC, hOldFont);
+ DeleteObject(hTmpFont);
+ }
+ }
+ }
+
+ SelectObject (hDC, hOrigFont);
+ EndPaint (hWnd, &ps);
+ MoveTTYCursor (hWnd);
+ pTTYInfo->screenDirty = FALSE;
+ return (TRUE);
+}
+
+
+/* FillRectColor
+ *
+ *
+ * Description:
+ * FillRectColor is similar to PatB in toolbar.c
+ *
+ * Code based on MFC source code, so presumably efficient.
+ *
+ */
+LOCAL void
+FillRectColor(HDC hDC, RECT * pRC, COLORREF color)
+{
+ SetBkColor(hDC, color);
+ ExtTextOut(hDC, 0, 0, ETO_OPAQUE, pRC, NULL, 0, NULL);
+}
+
+
+/** FrameRect3D
+ *
+ *
+ * Inputs:
+ * hdc - HDC
+ * pRC - pointer to rectangle
+ * width - width for frame (usually one)
+ * raised - TRUE for raised effect, FALSE for sunken effect
+ *
+ * Outputs:
+ * none
+ *
+ * Returns:
+ * void
+ *
+ * Description
+ * Draws a frame with a 3D effect.
+ *
+ * If 'raised' is true, the rectangle will look raised (like
+ * a button); otherwise, the rectangle will look sunk.
+ *
+ */
+void
+FrameRect3D(HDC hdc, RECT * pRC, int width, BOOL raised)
+{
+ COLORREF hilite, shadow;
+ RECT rcTemp;
+
+ shadow = GetSysColor(COLOR_BTNSHADOW);
+ hilite = GetSysColor(COLOR_BTNHIGHLIGHT);
+
+ rcTemp = *pRC;
+
+ rcTemp.right = rcTemp.left + width;
+ FillRectColor(hdc, &rcTemp, raised ? hilite : shadow);
+ rcTemp.right = pRC->right;
+
+ rcTemp.bottom = rcTemp.top + width;
+ FillRectColor(hdc, &rcTemp, raised ? hilite : shadow);
+ rcTemp.bottom = pRC->bottom;
+
+ rcTemp.left = rcTemp.right - width;
+ FillRectColor(hdc, &rcTemp, raised ? shadow : hilite);
+ rcTemp.left = pRC->left;
+
+ rcTemp.top = rcTemp.bottom - width;
+ FillRectColor(hdc, &rcTemp, raised ? shadow : hilite);
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL GetMinMaxInfoTTY (HWND hWnd, (MINMAXINFO __far *)lParam)
+ *
+ * Description:
+ * Return the min and max size that the window can be.
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window
+ *
+ * MINMAXINFO
+ * Info structure that Windows would like us to fill.
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+GetMinMaxInfoTTY (HWND hWnd, MINMAXINFO __far *lpmmi)
+{
+ PTTYINFO pTTYInfo;
+
+
+#ifdef SDEBUG
+ if (mswin_debug >= 5)
+ fprintf (mswin_debugfile, "GetMinMaxInfoTTY::: entered\n");
+#endif
+
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return (FALSE);
+
+ lpmmi->ptMaxTrackSize.x = lpmmi->ptMaxSize.x = MIN (lpmmi->ptMaxSize.x,
+ pTTYInfo->xChar * MAXNCOLUMN + WIN_X_BORDER_SIZE);
+ lpmmi->ptMaxTrackSize.y = lpmmi->ptMaxSize.y = MIN (lpmmi->ptMaxSize.y,
+ pTTYInfo->yChar * MAXNROW + WIN_Y_BORDER_SIZE);
+
+ lpmmi->ptMinTrackSize.x = MAX (WIN_MIN_X_SIZE,
+ pTTYInfo->xChar * MINNCOLUMN + WIN_X_BORDER_SIZE);
+ lpmmi->ptMinTrackSize.y = MAX (WIN_MIN_Y_SIZE,
+ pTTYInfo->yChar * MINNROW + WIN_Y_BORDER_SIZE);
+ return (TRUE);
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL AboutToSizeTTY (HWND hWnd, WINDOWPOS *winPos)
+ *
+ * Description:
+ * Called just before Windows resizes our window. We can change the
+ * values in 'winPos' to change the new size of the window.
+ *
+ * If mswin_setwindow() was called when the window was minimized we
+ * set the new size here.
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window
+ *
+ * WORD wVertSize
+ * new vertical size
+ *
+ * WORD wHorzSize
+ * new horizontal size
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+AboutToSizeTTY (HWND hWnd, WINDOWPOS *winPos)
+{
+ PTTYINFO pTTYInfo;
+
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return ( FALSE );
+
+#ifdef SDEBUG
+ if (mswin_debug >= 5)
+ fprintf (mswin_debugfile, "AboutToSizeTTY::: After x%lx, pos %d, %d, size %d, %d, flags x%x\n",
+ winPos->hwndInsertAfter, winPos->x, winPos->y, winPos->cx,
+ winPos->cy, winPos->flags);
+
+#endif
+
+ /*
+ * Was the window minimized AND is there a desired new size for it?
+ * AND is this a call that specifies a new size and position.
+ */
+ if (pTTYInfo->fMinimized && pTTYInfo->fDesiredSize &&
+ (winPos->flags & (SWP_NOSIZE | SWP_NOMOVE)) == 0) {
+#ifdef SDEBUG
+ if (mswin_debug >= 5)
+ fprintf (mswin_debugfile, "AboutToSizeTTY::: substitue pos (%d, %d), size (%d, %d)\n",
+ pTTYInfo->xDesPos, pTTYInfo->yDesPos,
+ pTTYInfo->xDesSize, pTTYInfo->yDesSize);
+#endif
+ pTTYInfo->fDesiredSize = FALSE;
+ winPos->x = pTTYInfo->xDesPos;
+ winPos->y = pTTYInfo->yDesPos;
+ winPos->cx = pTTYInfo->xDesSize;
+ winPos->cy = pTTYInfo->yDesSize;
+ }
+ return (TRUE);
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL SizeTTY( HWND hWnd, int fwSizeType, CORD wVertSize,
+ * CORD wHorzSize)
+ *
+ * Description:
+ * Sizes TTY and sets up scrolling regions.
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window
+ *
+ * WORD wVertSize
+ * new vertical size
+ *
+ * WORD wHorzSize
+ * new horizontal size
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+SizeTTY (HWND hWnd, int fwSizeType, CORD wVertSize, CORD wHorzSize)
+{
+ PTTYINFO pTTYInfo;
+ int newNColumn;
+ int newNRow;
+
+
+#ifdef SDEBUG
+ if (mswin_debug >= 5)
+ fprintf (mswin_debugfile, "SizeTTY::: entered, sizeType %d, New screen size %d, %d pixels\n",
+ fwSizeType, wHorzSize, wVertSize);
+#endif
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return ( FALSE );
+
+
+ /*
+ * Is the window being minimized or maximized?
+ */
+ switch (fwSizeType) {
+ case SIZE_MINIMIZED:
+ pTTYInfo->fMinimized = TRUE;
+ pTTYInfo->fMaximized = FALSE;
+ return (TRUE);
+ case SIZE_MAXIMIZED:
+ pTTYInfo->fMinimized = FALSE;
+ pTTYInfo->fMaximized = TRUE;
+ break;
+ default:
+ pTTYInfo->fMinimized = pTTYInfo->fMaximized = FALSE;
+ break;
+ }
+
+ pTTYInfo->ySize = (CORD) wVertSize;
+ newNRow = MAX(MINNROW, MIN(MAXNROW,
+ (pTTYInfo->ySize - pTTYInfo->toolBarSize - (2 * MARGINE_TOP)) /
+ pTTYInfo->yChar));
+ if (pTTYInfo->toolBarTop)
+ pTTYInfo->yOffset = MARGINE_TOP + pTTYInfo->toolBarSize;
+ else
+ pTTYInfo->yOffset = MARGINE_TOP;
+
+
+ pTTYInfo->xSize = (CORD) wHorzSize;
+ newNColumn = MAX(MINNCOLUMN,
+ MIN(MAXNCOLUMN, (pTTYInfo->xSize - (2 * MARGINE_LEFT)) /
+ pTTYInfo->xChar));
+ pTTYInfo->xOffset = MARGINE_LEFT;
+
+ if(newNRow == pTTYInfo->actNRow && newNColumn == pTTYInfo->actNColumn)
+ return(FALSE);
+
+ ResizeTTYScreen (hWnd, pTTYInfo, newNRow, newNColumn);
+ pTTYInfo->screenDirty = TRUE;
+ pTTYInfo->eraseScreen = TRUE;
+ InvalidateRect (hWnd, NULL, FALSE);
+
+ if (pTTYInfo->hTBWnd) {
+ if (pTTYInfo->toolBarTop)
+ /* Position at top of window. */
+ SetWindowPos (pTTYInfo->hTBWnd, HWND_TOP,
+ 0, 0,
+ wHorzSize, pTTYInfo->toolBarSize,
+ SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
+ else
+ /* Position at bottom of window. */
+ SetWindowPos (pTTYInfo->hTBWnd, HWND_TOP,
+ 0, pTTYInfo->ySize - pTTYInfo->toolBarSize,
+ wHorzSize, pTTYInfo->toolBarSize,
+ SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
+ }
+
+
+ DidResize (pTTYInfo);
+
+ return (TRUE);
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL SizingTTY( HWND hWnd, int fwSide, LPRECT pRect)
+ *
+ * Description:
+ * Snaps the drag rectangle to char width/height boundaries
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window
+ *
+ * WORD fwSide
+ * edge of window being sized
+ *
+ * LPRECT
+ * screen coords of drag rectangle in and desired size on return
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+SizingTTY (HWND hWnd, int fwSide, LPRECT pRect)
+{
+ PTTYINFO pTTYInfo;
+ int newNRow, newNCol, xClient, yClient,
+ xSys, ySys, xDiff, yDiff;
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return (FALSE);
+
+ xSys = (2 * GetSystemMetrics(SM_CXSIZEFRAME))
+ + GetSystemMetrics(SM_CXVSCROLL);
+ ySys = (2 * GetSystemMetrics(SM_CYSIZEFRAME))
+ + GetSystemMetrics(SM_CYCAPTION)
+ + GetSystemMetrics(SM_CYMENU);
+
+ newNCol = (((pRect->right - pRect->left) - xSys)
+ - (2 * MARGINE_LEFT)) / pTTYInfo->xChar;
+ newNRow = (((pRect->bottom - pRect->top) - ySys) - (2 * MARGINE_TOP)
+ - pTTYInfo->toolBarSize) / pTTYInfo->yChar;
+
+ xClient = (newNCol * pTTYInfo->xChar) + (2 * MARGINE_LEFT);
+ yClient = (newNRow * pTTYInfo->yChar) + (2 * MARGINE_TOP)
+ + pTTYInfo->toolBarSize;
+
+ xDiff = (pRect->left + xClient + xSys) - pRect->right;
+ yDiff = (pRect->top + yClient + ySys) - pRect->bottom;
+
+ if(!(xDiff || yDiff))
+ return(FALSE);
+
+ switch(fwSide){
+ case WMSZ_BOTTOM : /* Bottom edge */
+ pRect->bottom += yDiff;
+ break;
+
+ case WMSZ_BOTTOMLEFT : /*Bottom-left corner */
+ pRect->bottom += yDiff;
+ pRect->left -= xDiff;
+ break;
+
+ case WMSZ_BOTTOMRIGHT : /* Bottom-right corner */
+ pRect->bottom += yDiff;
+ pRect->right += xDiff;
+ break;
+
+ case WMSZ_LEFT : /* Left edge */
+ pRect->left -= xDiff;
+ break;
+
+ case WMSZ_RIGHT : /* Right edge */
+ pRect->right += xDiff;
+ break;
+
+ case WMSZ_TOP : /* Top edge */
+ pRect->top -= yDiff;
+ break;
+
+ case WMSZ_TOPLEFT : /* Top-left corner */
+ pRect->top -= yDiff;
+ pRect->left -= xDiff;
+ break;
+
+ case WMSZ_TOPRIGHT : /* Top-right corner */
+ pRect->top -= yDiff;
+ pRect->right += xDiff;
+ break;
+
+ default :
+ break;
+ }
+
+ if(!(newNRow == pTTYInfo->actNRow && newNCol == pTTYInfo->actNColumn))
+ SizeTTY(hWnd, SIZE_RESTORED, (CORD) yClient, (CORD) xClient);
+
+ return(TRUE);
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL MoveTTY (HWND hWnd, int xPos, int yPos)
+ *
+ * Description:
+ * Notes the fact that the window has moved.
+ * Only real purpose is so we can tell pine which can the write the
+ * new window position to the 'pinerc' file.
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window
+ *
+ * int xPos, yPos
+ * New position of the top left corner.
+ *
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+MoveTTY (HWND hWnd, int xPos, int yPos)
+{
+#ifdef SDEBUG
+ if (mswin_debug >= 5)
+ fprintf (mswin_debugfile, "MoveTTY::: entered\n");
+#endif
+
+ DidResize (gpTTYInfo);
+ return (TRUE);
+}
+
+
+/*---------------------------------------------------------------------------
+ * void ScrollTTY ()
+ *
+ * Description:
+ * Respond to a scroll message by either calling the scroll
+ * callback or inserting a scroll character into the input
+ * stream.
+ *
+ * Scrolling in the TTY window is complicated by the way pine
+ * process events. Normal windows applications are entirly event
+ * driven. The top level does nothing but dispatch events. In
+ * pine, the top level implements the logic. Events are only
+ * dispatched by the lowest levels.
+ *
+ * In normal applications, mouse down in the scroll bar causes
+ * an internal scroll function to be entered. It tracks the
+ * mouse and issues scroll messages as needed. If the
+ * application redraws the screen the scroll function also
+ * dispatches the WM_PAINT message to the application. The
+ * important thing is that this internal scroll function does
+ * not exit until the mouse is released.
+ *
+ * We implement two methods for pine's screen managers to deal
+ * with scroll events. They can receive scroll events as
+ * characters in the normal input stream or they can register a
+ * callback function.
+ *
+ * In the "insert a character in the queue" mode, the scroll
+ * event never gets process until the mouse is release. Auto
+ * repeat scroll events (generated as the mouse is held down)
+ * will cause multiple chars to be inserted in the queue, none
+ * of which will get processed till the mouse is release. In a
+ * compromise, we allow only one scroll char in the queue,
+ * which prevents makes for a more friendly and controllable
+ * behavior.
+ *
+ * In the callback mode, the callback repaints the screen, and
+ * then it calls mswin_flush() which PROCESSES EVENTS! The
+ * Windows internal scroll function does NOT expect that. This
+ * behavior can confuses the scroll function, causing it to
+ * miss mouse up events. We avoid this by setting gScrolling TRUE
+ * when this routine is entered and FALSE when this routine exits
+ * All PeekMessage processors avoid processing any message when
+ * gScrolling is TRUE.
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL void
+ScrollTTY (HWND hWnd, int wScrollCode, int nPos, HWND hScroll)
+{
+ PTTYINFO pTTYInfo;
+ int cmd = 0;
+ long scroll_pos = 0;
+ BOOL noAction = FALSE;
+ BOOL didScroll;
+ FARPROC prevBlockingProc;
+
+
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+
+ if (pTTYInfo == NULL || gScrolling)
+ return;
+
+ gScrolling = TRUE;
+ if (gWSBlockingProc != NULL)
+ prevBlockingProc = WSASetBlockingHook (gWSBlockingProc);
+
+
+
+
+ switch (wScrollCode) {
+ case SB_BOTTOM:
+ cmd = MSWIN_KEY_SCROLLTO;
+ scroll_pos = pTTYInfo->scrollTo = 0;
+ break;
+
+ case SB_TOP:
+ cmd = MSWIN_KEY_SCROLLTO;
+ scroll_pos = pTTYInfo->scrollTo = pTTYInfo->scrollRange;
+ break;
+
+ case SB_LINEDOWN:
+ cmd = MSWIN_KEY_SCROLLDOWNLINE;
+ scroll_pos = 1;
+ break;
+
+ case SB_LINEUP:
+ cmd = MSWIN_KEY_SCROLLUPLINE;
+ scroll_pos = 1;
+ break;
+
+ case SB_PAGEDOWN:
+ cmd = MSWIN_KEY_SCROLLDOWNPAGE;
+ scroll_pos = 1;
+ break;
+
+ case SB_PAGEUP:
+ cmd = MSWIN_KEY_SCROLLUPPAGE;
+ scroll_pos = 1;
+ break;
+
+ case SB_THUMBTRACK:
+ case SB_THUMBPOSITION:
+ cmd = MSWIN_KEY_SCROLLTO;
+ scroll_pos = pTTYInfo->scrollTo = (long) ((float)nPos);
+ break;
+
+ default:
+ noAction = TRUE;
+ break;
+ }
+
+
+ /*
+ * If there is a scroll callback call that. If there is no scroll
+ * callback or the callback says it did not handle the event (returned,
+ * FALSE) queue the scroll cmd.
+ */
+ if (!noAction) {
+ SelClear ();
+ didScroll = FALSE;
+ if (gScrollCallback != NULL) {
+ /* Call scrolling callback. Set blocking hook to our routine
+ * which prevents messages from being dispatched. */
+ if (gWSBlockingProc != NULL)
+ WSASetBlockingHook (gWSBlockingProc);
+ didScroll = gScrollCallback (cmd, scroll_pos);
+ if (gWSBlockingProc != NULL)
+ WSAUnhookBlockingHook ();
+ }
+ /*
+ * If no callback or callback did not do the scrolling operation,
+ * insert a scroll cmd in the input stream.
+ */
+ if (!didScroll)
+ CQAddUniq ((UCS)cmd, 0);
+ }
+
+
+ gScrolling = FALSE;
+ return;
+}
+
+
+#ifdef WIN32
+/*---------------------------------------------------------------------------
+ * void MouseWheelTTY ()
+ *
+ * Description:
+ * Respond to a WM_MOUSEWHEEL event by calling scroll callback
+ * ala ScrollTTY.
+ *
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL void
+MouseWheelTTY (HWND hWnd, int xPos, int yPos, int fwKeys, int zDelta)
+{
+ PTTYINFO pTTYInfo;
+ int cmd;
+ long scroll_pos;
+ FARPROC prevBlockingProc;
+ SCROLLINFO scrollInfo;
+ static int zDelta_accumulated;
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+
+ if (pTTYInfo == NULL || gScrolling)
+ return;
+
+ scrollInfo.cbSize = sizeof(SCROLLINFO);
+ scrollInfo.fMask = SIF_POS | SIF_RANGE;
+ GetScrollInfo(hWnd, SB_VERT, &scrollInfo);
+ if((zDelta < 0 && scrollInfo.nPos < scrollInfo.nMin)
+ || (zDelta > 0 && scrollInfo.nPos >= scrollInfo.nMax))
+ return;
+
+ gScrolling = TRUE;
+ if (gWSBlockingProc != NULL)
+ prevBlockingProc = WSASetBlockingHook (gWSBlockingProc);
+
+ if(fwKeys == MK_MBUTTON)
+ zDelta *= 2; /* double the effect! */
+
+ if(abs(zDelta += zDelta_accumulated) < WHEEL_DELTA){
+ zDelta_accumulated = zDelta;
+ }
+ else{
+ /* Remember any partial increments */
+ zDelta_accumulated = zDelta % WHEEL_DELTA;
+
+ scroll_pos = (long)(gsMWMultiplier * abs((zDelta / WHEEL_DELTA)));
+
+ cmd = (zDelta < 0) ? MSWIN_KEY_SCROLLDOWNLINE : MSWIN_KEY_SCROLLUPLINE;
+
+ SelClear ();
+ if (gScrollCallback != NULL) {
+ /* Call scrolling callback. Set blocking hook to our routine
+ * which prevents messages from being dispatched. */
+ if (gWSBlockingProc != NULL)
+ WSASetBlockingHook (gWSBlockingProc);
+ (void) gScrollCallback (cmd, scroll_pos);
+ if (gWSBlockingProc != NULL)
+ WSAUnhookBlockingHook ();
+ }
+ }
+
+ gScrolling = FALSE;
+ return;
+}
+
+
+LOCAL void
+MouseWheelMultiplier()
+{
+ TCHAR lines[8];
+ DWORD llen = sizeof(lines)/sizeof(TCHAR);
+
+ /* HKEY_CURRENT_USER\Control Panel\Desktop holds the key */
+ gsMWMultiplier = (MSWRPeek(HKEY_CURRENT_USER, TEXT("Control Panel\\Desktop"),
+ TEXT("WheelScrollLines"), lines, &llen) == TRUE)
+ ? (short)_ttoi(lines) : 1;
+}
+#endif
+
+
+/*---------------------------------------------------------------------------
+ * void CaretTTY (HWND hWnd, CARETS cStyle)
+ *
+ * Description:
+ * Adjusts the Caret to the user supplied style
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window
+ *
+ * int wStyle
+ * New style to take on
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL void
+CaretTTY (HWND hWnd, CARETS cStyle)
+{
+ PTTYINFO pTTYInfo;
+
+ if(pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO)){
+ pTTYInfo->cCaretStyle = cStyle;
+ CaretCreateTTY (hWnd);
+ DidResize (gpTTYInfo);
+ }
+}
+
+
+/*---------------------------------------------------------------------------
+ * void CaretCreateTTY (HWND hWnd, BOOL wPosition)
+ *
+ * Description:
+ * Adjusts the Caret to the user supplied style
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window
+ *
+ * BOOL wPosition
+ * whether or not to position it too
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL void
+CaretCreateTTY (HWND hWnd)
+{
+ PTTYINFO pTTYInfo;
+
+ if(pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO)){
+ int n = 0, x, y;
+
+ switch(pTTYInfo->cCaretStyle){
+ case CaretHorizBar :
+ x = pTTYInfo->xChar;
+ y = pTTYInfo->yChar / 5;
+ n = pTTYInfo->yChar - y;
+ break;
+
+ case CaretVertBar :
+ x = pTTYInfo->xChar / 4;
+ y = pTTYInfo->yChar;
+ break;
+
+ case CaretSmallBlock :
+ x = pTTYInfo->xChar;
+ y = pTTYInfo->yChar / 2;
+ n = pTTYInfo->yChar - y;
+ break;
+
+ default :
+ x = pTTYInfo->xChar;
+ y = pTTYInfo->yChar;
+ break;
+ }
+
+ CreateCaret (hWnd, NULL, x, y);
+ pTTYInfo->yCurOffset = n;
+
+ if(pTTYInfo->fCaretOn){
+ ShowCaret(hWnd);
+ MoveTTYCursor(hWnd);
+ }
+ }
+}
+
+
+/*
+ * This routine is inserted as the winsock blocking hook. It's main perpos
+ * is to NOT dispatch messages.
+ */
+BOOL CALLBACK __export
+NoMsgsAreSent (void)
+{
+ return (FALSE);
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL SetTTYFocus( HWND hWnd )
+ *
+ * Description:
+ * Sets the focus to the TTY window also creates caret.
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+SetTTYFocus (HWND hWnd)
+{
+ PTTYINFO pTTYInfo;
+
+#ifdef SDEBUG
+ if (mswin_debug >= 5)
+ fprintf (mswin_debugfile, "SetTTYFocus::: entered\n");
+#endif
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return (FALSE);
+
+ mswin_showcursor(TRUE);
+
+ pTTYInfo->fFocused = TRUE;
+
+ CaretCreateTTY (hWnd);
+
+ MoveTTYCursor (hWnd);
+ return (TRUE);
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL KillTTYFocus( HWND hWnd )
+ *
+ * Description:
+ * Kills TTY focus and destroys the caret.
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+KillTTYFocus (HWND hWnd)
+{
+ PTTYINFO pTTYInfo;
+
+#ifdef SDEBUG
+ if (mswin_debug >= 5)
+ fprintf (mswin_debugfile, "KillTTYFocus::: entered\n");
+#endif
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return (FALSE);
+
+ mswin_showcursor(TRUE);
+
+ if(pTTYInfo->fCaretOn)
+ HideCaret (hWnd);
+
+ DestroyCaret();
+
+ pTTYInfo->fFocused = FALSE;
+
+ return (TRUE);
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL MoveTTYCursor( HWND hWnd )
+ *
+ * Description:
+ * Moves caret to current position.
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+MoveTTYCursor (HWND hWnd)
+{
+ PTTYINFO pTTYInfo;
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return (FALSE);
+
+ if(pTTYInfo->fCaretOn && !pTTYInfo->fMassiveUpdate) {
+ HideCaret (hWnd);
+ SetCaretPos ((pTTYInfo->nColumn * pTTYInfo->xChar) + pTTYInfo->xOffset,
+ (pTTYInfo->nRow * pTTYInfo->yChar)
+ + pTTYInfo->yCurOffset + pTTYInfo->yOffset);
+ ShowCaret (hWnd);
+ }
+
+ return (TRUE);
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL ProcessTTYKeyDown ( HWND hWnd, WORD bOut )
+ *
+ * Description:
+ * Called to process MW_KEYDOWN message. We are only interested in
+ * virtual keys that pico/pine use. All others get passed on to
+ * the default message handler. Regular key presses will return
+ * latter as a WM_CHAR message, with SHIFT and CONTROL processing
+ * already done.
+ *
+ * We do watch for VK_CONTROL to keep track of it's state such
+ * that we can implement ^_space.
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window
+ *
+ * BYTE key
+ * Virtual key code.
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+ProcessTTYKeyDown (HWND hWnd, TCHAR key)
+{
+ UCS myKey;
+ BOOL fKeyControlDown = GetKeyState(VK_CONTROL) < 0;
+ BOOL fKeyAltDown = GetKeyState(VK_MENU) < 0;
+
+ // If the alt key is down, let Windows handle the message. This will
+ // allow the Ctrl+Alt (AltGr) processing to work.
+ if(fKeyAltDown)
+ return FALSE;
+
+ switch (key) {
+ case VK_MENU:
+ case VK_CONTROL:
+ case VK_SHIFT:
+ return FALSE;
+ case VK_UP: myKey = KEY_UP; break;
+ case VK_DOWN: myKey = KEY_DOWN; break;
+ case VK_RIGHT:
+ /* Ctrl-@ is used to advance to the next word. */
+ myKey = fKeyControlDown ? '@': KEY_RIGHT;
+ break;
+ case VK_LEFT:
+ /* Ctrl-left is used to advance to the previous word. */
+ myKey = KEY_LEFT;
+ break;
+ case VK_HOME:
+ /* Ctrl-home is used to advance to the beginning of buffer. */
+ myKey = KEY_HOME;
+ break;
+ case VK_END:
+ /* Ctrl-end is used to advance to the end of buffer. */
+ myKey = KEY_END;
+ break;
+ case VK_PRIOR: myKey = KEY_PGUP; break;
+ case VK_NEXT: myKey = KEY_PGDN; break;
+ case VK_DELETE: myKey = KEY_DEL; break;
+ case VK_F1: myKey = F1; break;
+ case VK_F2: myKey = F2; break;
+ case VK_F3: myKey = F3; break;
+ case VK_F4: myKey = F4; break;
+ case VK_F5: myKey = F5; break;
+ case VK_F6: myKey = F6; break;
+ case VK_F7: myKey = F7; break;
+ case VK_F8: myKey = F8; break;
+ case VK_F9: myKey = F9; break;
+ case VK_F10: myKey = F10; break;
+ case VK_F11: myKey = F11; break;
+ case VK_F12: myKey = F12; break;
+
+ default:
+ if(fKeyControlDown && !(GetKeyState(VK_SHIFT) < 0)) {
+ if(key == '6') {
+ /*
+ * Ctrl-^ is used to set and clear the mark in the
+ * composer (pico) On most other systems Ctrl-6 does the
+ * same thing. Allow that on windows too.
+ */
+ myKey = '^';
+ break;
+ } else if(key == '2') {
+ /* Ctrl-@ is used to advance to the next word. */
+ myKey = '@';
+ break;
+ }
+ }
+
+ return (FALSE); /* Message NOT handled.*/
+ }
+
+ CQAdd (myKey, fKeyControlDown);
+
+ set_time_of_last_input();
+
+ return (TRUE); /* Message handled .*/
+}
+
+
+#ifdef CDEBUG
+char *
+dtime()
+{
+ static char timestring[23];
+ time_t t;
+ struct _timeb timebuffer;
+
+ timestring[0] = '\0';
+ t = time((time_t *) 0);
+ _ftime(&timebuffer);
+ snprintf(timestring, sizeof(timestring), "%.8s.%03ld", ctime(&t)+11, timebuffer.millitm);
+
+ return(timestring);
+}
+#endif
+
+
+/*---------------------------------------------------------------------------
+ * BOOL ProcessTTYCharacter( HWND hWnd, WORD bOut )
+ *
+ * Description:
+ * Place the character into a queue.
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window
+ *
+ * BYTE bOut
+ * byte from keyboard
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+ProcessTTYCharacter (HWND hWnd, TCHAR bOut)
+{
+ // Only check for control key being down if the alt key isn't also down.
+ // Windows uses Ctrl+Alt as AltGr.
+ BOOL fKeyAltDown = GetKeyState(VK_MENU) < 0;
+ BOOL fKeyControlDown = fKeyAltDown ?
+ FALSE : (GetKeyState(VK_CONTROL) < 0);
+
+ if(fKeyControlDown) {
+ if(bOut == ' ')
+ bOut = '@';
+ else
+ bOut += '@';
+ }
+
+ CQAdd ((UCS)bOut, fKeyControlDown);
+
+#ifdef ACCELERATORS
+ UpdateAccelerators (hWnd);
+#endif
+
+ set_time_of_last_input();
+
+ return (TRUE); /* Message handled. */
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL ProcessTTYMouse(HWND hWnd, int mevent, int button,
+ * int xPos, int yPos, WPARAM keys)
+ *
+ * Description:
+ * This is the central control for all mouse events. Every event
+ * gets put into a queue to wait for the upper layer.
+ *
+ * The upper's input routine calls checkmouse() which pulls the
+ * mouse event off the input queue. checkmouse() has a list of
+ * of screen regions. Some regions correspond to a "menu" item
+ * (text button at bottom of screen). There is generally one
+ * region for the central region of the screen.
+ *
+ * Because pine/pico do not interpret mouse drags, we do that here.
+ * When the user presses the button and drags the mouse across the
+ * screen this select the text in the region defined by the drag.
+ * The operation is local to mswin.c, and can only get what text
+ * is on the screen.
+ *
+ * The one exception is that now pico interprets mouse drag events
+ * in the body. pico signals that it wants to track the mouse
+ * by calling mswin_allowmousetrack(). This will 1) turn off
+ * our mouse tracking and 2) cause mouse movement events to
+ * be put on the mouse queue.
+ *
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window
+ *
+ * BYTE bOut
+ * byte from keyboard
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+ProcessTTYMouse (HWND hWnd, int mevent, int button,
+ CORD xPos, CORD yPos, WPARAM winkeys)
+{
+ int nRow;
+ int nColumn;
+ int keys;
+
+ /*
+ * Convert to cell position.
+ */
+ nColumn = (xPos - gpTTYInfo->xOffset) / gpTTYInfo->xChar;
+ if (xPos < gpTTYInfo->xOffset)
+ --nColumn;
+ nRow = (yPos - gpTTYInfo->yOffset) / gpTTYInfo->yChar;
+ if (yPos < gpTTYInfo->yOffset)
+ --nRow;
+
+ /*
+ * Convert window's keys.
+ */
+ keys = 0;
+ if (winkeys & MK_CONTROL)
+ keys |= M_KEY_CONTROL;
+ if (winkeys & MK_SHIFT)
+ keys |= M_KEY_SHIFT;
+
+ /* Adjust the cursor */
+ if((unsigned long) mevent != M_EVENT_UP){
+ if(gMouseTracking)
+ mswin_setcursor(MSWIN_CURSOR_IBEAM);
+ else if(ghCursorCurrent == ghCursorBusy)
+ mswin_setcursor(MSWIN_CURSOR_BUSY);
+ else if(mouse_on_key(nRow, nColumn))
+ mswin_setcursor(MSWIN_CURSOR_HAND);
+ else if(gMouseTrackCallback)
+ mswin_setcursor((*gMouseTrackCallback)(nColumn, (long) nRow));
+ else
+ mswin_setcursor(MSWIN_CURSOR_ARROW);
+ }
+
+ /*
+ * Tracking event or mouse up/down?
+ */
+ if ((unsigned long) mevent == M_EVENT_TRACK) {
+ /*
+ * Who is doing the tracking?
+ */
+ if (gAllowMouseTrack) {
+ /* For tracking, Button info is different. */
+ if (keys & MK_LBUTTON)
+ button = M_BUTTON_LEFT;
+ else if (keys & MK_MBUTTON)
+ button = M_BUTTON_MIDDLE;
+ else if (keys & MK_RBUTTON)
+ button = M_BUTTON_RIGHT;
+ MQAdd (mevent, button, nRow, nColumn, keys,
+ MSWIN_MF_REPLACING);
+ }
+ else
+ SelTrackMouse (nRow, nColumn);
+ }
+ else{
+ /*
+ * Tracking. Only start tracking mouse down in the text region
+ * But allow mouse up anywhere.
+ */
+ if ( (nRow >= 0 && nRow < gpTTYInfo->actNRow &&
+ nColumn >= 0 && nColumn < gpTTYInfo->actNColumn)
+ || (unsigned long) mevent == M_EVENT_UP) {
+
+ /*
+ * Mouse tracking. When the mouse goes down we start
+ * capturing all mouse movement events. If no one else wants
+ * them we will start defining a text selection.
+ */
+ if ((unsigned long) mevent == M_EVENT_DOWN) {
+ gMouseTracking = TRUE;
+ SetCapture (ghTTYWnd);
+ if (!gAllowMouseTrack && button == M_BUTTON_LEFT)
+ SelStart (nRow, nColumn);
+ }
+ else {
+ ReleaseCapture ();
+ if (!gAllowMouseTrack && button == M_BUTTON_LEFT)
+ SelFinish (nRow, nColumn);
+ gMouseTracking = FALSE;
+
+ /*
+ * If right mouse button, toss pop-up menu offering
+ * cut/copy/paste
+ */
+ if(button == M_BUTTON_RIGHT && SelAvailable()){
+ UINT fAllowed = (EM_CP | EM_CP_APPEND);
+
+ if(gAllowCut)
+ fAllowed |= EM_CUT;
+
+ if(CopyCutPopup(hWnd, fAllowed) == TRUE)
+ mevent = M_EVENT_TRACK; /* don't add to input queue! */
+ }
+ }
+
+ /*
+ * Insert event into queue.
+ */
+ if((unsigned long) mevent != M_EVENT_TRACK)
+ MQAdd (mevent, button, nRow, nColumn, keys, 0);
+ }
+ }
+
+ mswin_showcursor(TRUE); /* make sure it's visible */
+
+#ifdef ACCELERATORS
+ UpdateAccelerators (hWnd);
+#endif
+
+ set_time_of_last_input();
+
+ return (0); /* Message handled. */
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL ProcessTimer ()
+ *
+ * Description:
+ * Process the periodic timer calls.
+ *
+ *
+ * Parameters:
+ * None.
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL void
+ProcessTimer (void)
+{
+ /* Time to deliver an alarm signal? */
+ if (gAlarmTimeout != 0 && GetTickCount () / 1000 > gAlarmTimeout)
+ AlarmDeliver ();
+
+ /* Time to make the periodic callback. */
+ if (gPeriodicCallback != NULL &&
+ GetTickCount() / 1000 > gPeriodicCBTimeout) {
+ gPeriodicCBTimeout = GetTickCount() / 1000 +
+ gPeriodicCBTime;
+ gPeriodicCallback ();
+ }
+
+ /*
+ * If tracking the mouse, insert a fake mouse tracking message
+ * At the last know location of the mouse.
+ */
+ if (gAllowMouseTrack) {
+ gMTEvent.event = M_EVENT_TRACK;
+ MQAdd (gMTEvent.event, gMTEvent.button, gMTEvent.nRow,
+ gMTEvent.nColumn, gMTEvent.keys, MSWIN_MF_REPLACING);
+ }
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL WriteTTYBlock( HWND hWnd, LPSTR lpBlock, int nLength )
+ *
+ * Description:
+ * Writes block to TTY screen. Nothing fancy - just
+ * straight TTY.
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window
+ *
+ * LPSTR lpBlock
+ * far pointer to block of data
+ *
+ * int nLength
+ * length of block
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+WriteTTYBlock (HWND hWnd, LPTSTR lpBlock, int nLength)
+{
+ int i, j, width;
+ PTTYINFO pTTYInfo;
+ RECT rect;
+ BOOL fNewLine;
+ long offset;
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return (FALSE);
+
+ for (i = 0 ; i < nLength; i++) {
+ switch (lpBlock[i]) {
+ case ASCII_BEL:
+ /* Bell */
+ MessageBeep (0) ;
+ break ;
+
+ case ASCII_BS:
+ /* Backspace over a whole character */
+ offset = pscreen_offset_from_cord(pTTYInfo->nRow, pTTYInfo->nColumn, pTTYInfo);
+ width = (offset > pTTYInfo->nRow * pTTYInfo->actNColumn)
+ ? scrwidth(pTTYInfo->pScreen+offset-1, 1) : 0;
+
+ if(pTTYInfo->nColumn > 0)
+ pTTYInfo->nColumn = (CORD)(pTTYInfo->nColumn - width);
+
+ MoveTTYCursor (hWnd);
+ break;
+
+ case ASCII_CR:
+ /* Carriage return */
+ pTTYInfo->nColumn = 0 ;
+ MoveTTYCursor (hWnd);
+ if (!pTTYInfo->fNewLine)
+ break;
+
+ /* fall through */
+
+ case ASCII_LF:
+ /* Line feed */
+ if (++pTTYInfo->nRow == pTTYInfo->actNRow) {
+
+ /* Scroll the Screen. */
+
+ /* slide rows 1 - n-1 up to row 0 */
+ memmove ((LPTSTR)pTTYInfo->pScreen,
+ (LPTSTR) (pTTYInfo->pScreen + pTTYInfo->actNColumn),
+ (pTTYInfo->actNRow - 1) * pTTYInfo->actNColumn * sizeof (TCHAR));
+
+ /* initialize new row n-1 */
+ for(j = (pTTYInfo->actNRow - 1) * pTTYInfo->actNColumn;
+ j < pTTYInfo->actNColumn; j++)
+ pTTYInfo->pScreen[j] = (TCHAR) ' ';
+
+
+ /* Scroll the Cell Widths */
+ memmove ((int *)pTTYInfo->pCellWidth,
+ (int *) (pTTYInfo->pCellWidth + pTTYInfo->actNColumn),
+ (pTTYInfo->actNRow - 1) * pTTYInfo->actNColumn * sizeof (int));
+ for(j = (pTTYInfo->actNRow - 1) * pTTYInfo->actNColumn;
+ j < pTTYInfo->actNColumn; j++)
+ pTTYInfo->pCellWidth[j] = pTTYInfo->xChar; /* xChar set yet ? */
+
+ /* Scroll the Attributes. */
+
+ /* slide rows 1 - n-1 up to row 0 */
+ memmove ((CharAttrib *) pTTYInfo->pAttrib,
+ (CharAttrib *) (pTTYInfo->pAttrib + pTTYInfo->actNColumn),
+ (pTTYInfo->actNRow - 1) * pTTYInfo->actNColumn * sizeof(CharAttrib));
+
+ /* initialize new row n-1 to zero */
+ memset ((CharAttrib *) (pTTYInfo->pAttrib +
+ (pTTYInfo->actNRow - 1) * pTTYInfo->actNColumn),
+ 0, pTTYInfo->actNColumn*sizeof(CharAttrib));
+
+ pTTYInfo->screenDirty = TRUE;
+ pTTYInfo->eraseScreen = TRUE;
+ InvalidateRect (hWnd, NULL, FALSE);
+ --pTTYInfo->nRow;
+ }
+
+ MoveTTYCursor (hWnd);
+ break;
+
+ default:
+ offset = pscreen_offset_from_cord(pTTYInfo->nRow, pTTYInfo->nColumn, pTTYInfo);
+ pTTYInfo->pScreen[offset] = lpBlock[i];
+ pTTYInfo->pCellWidth[offset] = wcellwidth((UCS)lpBlock[i]) * pTTYInfo->xChar;
+ pTTYInfo->pAttrib[offset] = pTTYInfo->curAttrib;
+ rect.left = (pTTYInfo->nColumn * pTTYInfo->xChar) +
+ pTTYInfo->xOffset;
+ rect.right = rect.left + pTTYInfo->xChar;
+ rect.top = (pTTYInfo->nRow * pTTYInfo->yChar) +
+ pTTYInfo->yOffset;
+ rect.bottom = rect.top + pTTYInfo->yChar;
+ pTTYInfo->screenDirty = TRUE;
+ InvalidateRect (hWnd, &rect, FALSE);
+
+ /* Line Wrap. */
+ if (pTTYInfo->nColumn < pTTYInfo->actNColumn - 1)
+ pTTYInfo->nColumn++ ;
+ else if (pTTYInfo->autoWrap == WRAP_ON ||
+ (pTTYInfo->autoWrap == WRAP_NO_SCROLL &&
+ pTTYInfo->nRow < pTTYInfo->actNRow - 1)) {
+ fNewLine = pTTYInfo->fNewLine;
+ pTTYInfo->fNewLine = FALSE;
+ WriteTTYBlock (hWnd, TEXT("\r\n"), 2);
+ pTTYInfo->fNewLine = fNewLine;
+ }
+ break;
+ }
+ }
+ return (TRUE);
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL WriteTTYText ( HWND hWnd, LPSTR lpBlock, int nLength )
+ *
+ * Description:
+ * Like WriteTTYBlock but optimized for strings that are text only,
+ * no carrage control characters.
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window
+ *
+ * LPSTR lpBlock
+ * far pointer to block of data
+ *
+ * int nLength
+ * length of block
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+WriteTTYText (HWND hWnd, LPTSTR lpText, int nLength)
+{
+ int i;
+ PTTYINFO pTTYInfo;
+ RECT rect;
+ long offset, endOffset;
+ long colEnd;
+ long screenEnd;
+ int screenwidth;
+
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return ( FALSE );
+
+
+ /* Calculate offset of cursor, end of current column, and end of screen */
+ offset = pscreen_offset_from_cord(pTTYInfo->nRow, pTTYInfo->nColumn, pTTYInfo);
+
+ colEnd = (pTTYInfo->nRow + 1) * pTTYInfo->actNColumn;
+ screenEnd = pTTYInfo->actNRow * pTTYInfo->actNColumn;
+
+
+ /* Text is allowed to wrap around to subsequent lines, but not past end
+ * of screen */
+ endOffset = offset + nLength;
+ if (endOffset >= screenEnd) {
+ nLength = screenEnd - offset;
+ endOffset = offset + nLength - 1; /* Last cell, not one past last */
+ }
+
+
+ /* Calculate bounding rectangle. */
+ if (endOffset <= colEnd) {
+ /* Single line. */
+
+ screenwidth = scrwidth(lpText, nLength);
+
+ rect.left = (pTTYInfo->nColumn * pTTYInfo->xChar) + pTTYInfo->xOffset;
+ rect.right = rect.left + (pTTYInfo->xChar * screenwidth);
+ rect.top = (pTTYInfo->nRow * pTTYInfo->yChar) + pTTYInfo->yOffset;
+ rect.bottom = rect.top + pTTYInfo->yChar;
+ /* Advance cursor on cur line but not past end. */
+ pTTYInfo->nColumn = (CORD)MIN(pTTYInfo->nColumn + screenwidth,
+ pTTYInfo->actNColumn - 1);
+ }
+ else {
+ /* Wraps across multiple lines. Calculate one rect to cover all
+ * lines. */
+ rect.left = 0;
+ rect.right = pTTYInfo->xSize;
+ rect.top = (pTTYInfo->nRow * pTTYInfo->yChar) + pTTYInfo->yOffset;
+ rect.bottom = ((((offset + nLength) / pTTYInfo->actNColumn) + 1) *
+ pTTYInfo->yChar) + pTTYInfo->yOffset;
+ pTTYInfo->nRow = (CORD)(endOffset / pTTYInfo->actNColumn);
+ pTTYInfo->nColumn = (CORD)(endOffset % pTTYInfo->actNColumn);
+ }
+
+
+ /* Apply text and attributes to screen in one smooth motion. */
+ for(i = 0; i < nLength; i++)
+ (pTTYInfo->pScreen+offset)[i] = lpText[i];
+ for(i = 0; i < nLength; i++)
+ (pTTYInfo->pCellWidth+offset)[i] = wcellwidth((UCS)lpText[i]) * pTTYInfo->xChar;
+ for(i = 0; i < nLength; i++)
+ pTTYInfo->pAttrib[offset+i] = pTTYInfo->curAttrib;
+
+ /* Invalidate rectangle */
+ pTTYInfo->screenDirty = TRUE;
+ InvalidateRect (hWnd, &rect, FALSE);
+ return (TRUE);
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL WriteTTYChar (HWND hWnd, char ch)
+ *
+ * Description:
+ * Write a single character to the cursor position and advance the
+ * cursor. Does not handle carage control.
+ *
+ * Parameters:
+ * HWND hWnd
+ * handle to TTY window
+ *
+ * char ch
+ * character being written.
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+WriteTTYChar (HWND hWnd, TCHAR ch)
+{
+ PTTYINFO pTTYInfo;
+ RECT rect;
+ long offset;
+
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return (FALSE);
+
+ offset = (pTTYInfo->nRow * pTTYInfo->actNColumn) +
+ pTTYInfo->nColumn;
+
+ *(pTTYInfo->pScreen + offset) = ch;
+ pTTYInfo->pCellWidth[offset] = wcellwidth((UCS)ch) * pTTYInfo->xChar;
+ pTTYInfo->pAttrib[offset] = pTTYInfo->curAttrib;
+
+ rect.left = (pTTYInfo->nColumn * pTTYInfo->xChar) + pTTYInfo->xOffset;
+ rect.right = rect.left + pTTYInfo->xChar;
+ rect.top = (pTTYInfo->nRow * pTTYInfo->yChar) + pTTYInfo->yOffset;
+ rect.bottom = rect.top + pTTYInfo->yChar;
+ pTTYInfo->screenDirty = TRUE;
+ InvalidateRect (hWnd, &rect, FALSE);
+
+
+
+ /* Line Wrap. */
+ if (pTTYInfo->nColumn < pTTYInfo->actNColumn - 1)
+ pTTYInfo->nColumn++ ;
+ else if ((pTTYInfo->autoWrap == WRAP_ON ||
+ pTTYInfo->autoWrap == WRAP_NO_SCROLL) &&
+ pTTYInfo->nRow < pTTYInfo->actNRow - 1) {
+ pTTYInfo->nRow++;
+ pTTYInfo->nColumn = 0;
+ }
+ return (TRUE);
+}
+
+
+/*---------------------------------------------------------------------------
+ * VOID GoModalDialogBoxParam( HINSTANCE hInstance,
+ * LPTSTR lpszTemplate, HWND hWnd,
+ * DLGPROC lpDlgProc, LPARAM lParam )
+ *
+ * Description:
+ * It is a simple utility function that simply performs the
+ * MPI and invokes the dialog box with a DWORD paramter.
+ *
+ * Parameters:
+ * similar to that of DialogBoxParam() with the exception
+ * that the lpDlgProc is not a procedure instance
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL VOID
+GoModalDialogBoxParam( HINSTANCE hInstance, LPTSTR lpszTemplate,
+ HWND hWnd, DLGPROC lpDlgProc, LPARAM lParam )
+{
+ DLGPROC lpProcInstance ;
+
+ lpProcInstance = (DLGPROC) MakeProcInstance( (FARPROC) lpDlgProc,
+ hInstance ) ;
+ DialogBoxParam( hInstance, lpszTemplate, hWnd, lpProcInstance, lParam ) ;
+ FreeProcInstance( (FARPROC) lpProcInstance ) ;
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL FAR PASCAL __export AboutDlgProc( HWND hDlg, UINT uMsg,
+ * WPARAM wParam, LPARAM lParam )
+ *
+ * Description:
+ * Simulates the Windows System Dialog Box.
+ *
+ * Parameters:
+ * Same as standard dialog procedures.
+ *
+/*--------------------------------------------------------------------------*/
+BOOL FAR PASCAL __export
+AboutDlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch (uMsg) {
+ case WM_INITDIALOG:
+ {
+ TCHAR szTemp [81];
+
+ /* sets up version number for PINE */
+ GetDlgItemText (hDlg, IDD_VERSION, szTemp, sizeof(szTemp)/sizeof(TCHAR));
+ /* szTemp is unicode, mswin_compilation_date etc are cast as %S in mswin.rc */
+ _sntprintf (TempBuf, sizeof(TempBuf)/sizeof(TCHAR), szTemp, mswin_specific_winver(),
+ mswin_majorver(), mswin_minorver(),
+ mswin_compilation_remarks(),
+ mswin_compilation_date());
+ SetDlgItemText (hDlg, IDD_VERSION, (LPTSTR) TempBuf);
+
+ /* get by-line */
+ LoadString (GET_HINST (hDlg), IDS_BYLINE, TempBuf,
+ sizeof(TempBuf) / sizeof(TCHAR));
+ SetDlgItemText (hDlg, IDD_BYLINE, TempBuf);
+
+ }
+ return ( TRUE ) ;
+
+ case WM_CLOSE:
+ EndDialog( hDlg, TRUE ) ;
+ return ( TRUE ) ;
+
+ case WM_COMMAND:
+ switch((WORD) wParam){
+ case IDD_OK :
+ EndDialog( hDlg, TRUE ) ;
+ return ( TRUE ) ;
+
+ default :
+ break;
+ }
+
+ break;
+ }
+ return ( FALSE ) ;
+
+} /* end of AboutDlgProc() */
+
+
+/*---------------------------------------------------------------------------
+ *
+ * Description:
+ * Simulates the Windows System Dialog Box.
+ *
+ * Parameters:
+ * Same as standard dialog procedures.
+ *
+/*--------------------------------------------------------------------------*/
+BOOL FAR PASCAL __export
+SplashDlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ static HBITMAP hbmpSplash;
+
+ switch (uMsg) {
+ case WM_INITDIALOG:
+ if(hbmpSplash = LoadBitmap(GET_HINST(hDlg),
+ MAKEINTRESOURCE(ALPINESPLASH))){
+ BITMAP stBitmap;
+ int cx, cy;
+
+ cx = GetSystemMetrics(SM_CXSCREEN);
+ cy = GetSystemMetrics(SM_CYSCREEN);
+ GetObject(hbmpSplash, sizeof(BITMAP), &stBitmap);
+
+ SetWindowPos(hDlg, HWND_TOPMOST,
+ (cx - stBitmap.bmWidth) / 2,
+ (cy - stBitmap.bmHeight) / 2,
+ stBitmap.bmWidth, stBitmap.bmHeight,
+ SWP_NOCOPYBITS /* | SWP_SHOWWINDOW */);
+ }
+
+ return(TRUE);
+
+ case WM_CTLCOLORDLG :
+ return(TRUE);
+
+ case WM_ERASEBKGND :
+ {
+ HDC hMemDC;
+ POINT stPoint;
+ BITMAP stBitmap;
+ HGDIOBJ hObject;
+
+ if((hMemDC = CreateCompatibleDC((HDC) wParam)) != NULL){
+ hObject = SelectObject(hMemDC, hbmpSplash);
+ SetMapMode(hMemDC, GetMapMode((HDC) wParam));
+
+ GetObject(hbmpSplash, sizeof(BITMAP), &stBitmap);
+ stPoint.x = stBitmap.bmWidth;
+ stPoint.y = stBitmap.bmHeight;
+ DPtoLP((HDC) wParam, &stPoint, 1);
+
+ BitBlt((HDC) wParam,
+ 0, 0, stPoint.x, stPoint.y,
+ hMemDC, 0, 0, SRCCOPY);
+ SelectObject(hMemDC, hObject);
+ DeleteDC(hMemDC) ;
+ }
+
+ return(TRUE);
+ }
+
+ break;
+
+ case WM_CLOSE:
+ DestroyWindow(hDlg);
+ return(TRUE);
+
+ case WM_DESTROY :
+ if(hbmpSplash)
+ DeleteObject(hbmpSplash);
+
+ break;
+
+ case WM_COMMAND: /* No commands! */
+ DestroyWindow(hDlg);
+ return(TRUE);
+ }
+
+ return(FALSE);
+
+}
+
+
+#if 0
+UINT APIENTRY
+SelectTTYFontHook(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch(uMsg){
+ case WM_INITDIALOG :
+ {
+ /*
+ * Deactivate the Style combo box...
+ */
+ HWND hWnd = GetDlgItem(hDlg, cmb2);
+ EnableWindow(hWnd, FALSE);
+ SetWindowPos(hWnd, NULL, 0, 0, 0, 0, SWP_HIDEWINDOW);
+ return(TRUE);
+ }
+
+ break;
+
+ case WM_COMMAND :
+ switch ((WORD) wParam) {
+ case psh3 :
+ {
+ LOGFONT curfont;
+
+ SendMessage(hDlg, WM_CHOOSEFONT_GETLOGFONT,
+ 0, (LPARAM) &curfont);
+ ResetTTYFont (ghTTYWnd, gpTTYInfo, &curfont);
+ return (TRUE);
+ }
+
+ break;
+
+ default :
+ break;
+ }
+
+ break;
+
+ default :
+ break;
+ }
+
+ return(FALSE);
+}
+#endif
+
+
+/*---------------------------------------------------------------------------
+ * BOOL SelectTTYFont( HWND hDlg )
+ *
+ * Description:
+ * Selects the current font for the TTY screen.
+ * Uses the Common Dialog ChooseFont() API.
+ *
+ * Parameters:
+ * HWND hDlg
+ * handle to settings dialog
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL BOOL
+SelectTTYFont (HWND hWnd)
+{
+ CHOOSEFONT cfTTYFont;
+ LOGFONT newFont, origFont;
+ PTTYINFO pTTYInfo;
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return (FALSE);
+
+ memcpy (&newFont, &gpTTYInfo->lfTTYFont, sizeof (LOGFONT));
+ memcpy (&origFont, &gpTTYInfo->lfTTYFont, sizeof (LOGFONT));
+
+ cfTTYFont.lStructSize = sizeof (CHOOSEFONT);
+ cfTTYFont.hwndOwner = hWnd ;
+ cfTTYFont.hDC = NULL ;
+ cfTTYFont.lpLogFont = &newFont;
+ cfTTYFont.Flags = CF_SCREENFONTS | CF_FIXEDPITCHONLY |
+ CF_INITTOLOGFONTSTRUCT |
+#if 0
+ CF_FORCEFONTEXIST | CF_LIMITSIZE |
+ CF_ENABLEHOOK | CF_APPLY;
+#else
+ CF_FORCEFONTEXIST | CF_LIMITSIZE;
+#endif
+ cfTTYFont.nSizeMin = FONT_MIN_SIZE;
+ cfTTYFont.nSizeMax = FONT_MAX_SIZE;
+ cfTTYFont.lCustData = (long) 0 ;
+#if 0
+ cfTTYFont.lpfnHook = SelectTTYFontHook ;
+#else
+ cfTTYFont.lpfnHook = NULL;
+#endif
+ cfTTYFont.lpTemplateName = NULL ;
+ cfTTYFont.hInstance = GET_HINST (hWnd);
+
+
+
+ if (ChooseFont (&cfTTYFont)) {
+ ResetTTYFont (hWnd, pTTYInfo, &newFont);
+ }
+#if 0
+ else{
+ ResetTTYFont (hWnd, pTTYInfo, &origFont);
+ }
+#endif
+
+ return (TRUE);
+}
+
+
+/*
+ * Set a specific color (forground, background, reverse, normal) to
+ * the color specified by name.
+ */
+LOCAL void
+SetColorAttribute (COLORREF *cf, char *colorName)
+{
+ /* color name not in table. Try converting RGB string. */
+ ConvertRGBString (colorName, cf);
+
+ /* Redraw screen. */
+ gpTTYInfo->screenDirty = TRUE;
+ gpTTYInfo->eraseScreen = TRUE;
+ InvalidateRect (ghTTYWnd, NULL, FALSE);
+}
+
+
+/*
+ * Set current color attribute to reverse color
+ */
+void
+SetReverseColor()
+{
+ FlushWriteAccum ();
+ gpTTYInfo->curAttrib.rgbFG = gpTTYInfo->rgbRFGColor;
+ gpTTYInfo->curAttrib.rgbBG = gpTTYInfo->rgbRBGColor;
+}
+
+
+/*
+ * Convert a string to an integer.
+ */
+LOCAL BOOL
+ScanInt (char *str, int min, int max, int *val)
+{
+ char *c;
+ int v;
+ int neg = 1;
+
+
+ if (str == NULL) return (FALSE);
+ if (*str == '\0' || strlen (str) > 9) return (FALSE);
+
+ /* Check for a negative sign. */
+ if (*str == '-') {
+ neg = -1;
+ ++str;
+ }
+
+ /* Check for all digits. */
+ for (c = str; *c != '\0'; ++c) {
+ if (!isdigit((unsigned char)*c))
+ return (FALSE);
+ }
+
+ /* Convert from ascii to int. */
+ v = atoi (str) * neg;
+
+ /* Check constraints. */
+ if (v < min || v > max)
+ return (FALSE);
+ *val = v;
+ return (TRUE);
+}
+
+
+/*
+ * Convert a RGB string to a color ref. The string should look like:
+ * rrr,ggg,bbb
+ * where rrr, ggg, and bbb are numbers between 0 and 255 that represent
+ * red, gree, and blue values. Must be comma seperated.
+ * Returns:
+ * TRUE - Successfully converted string.
+ * FALSE - Bad format, 'cf' unchanged.
+ */
+LOCAL BOOL
+ConvertRGBString (char *colorName, COLORREF *cf)
+{
+ int i, j, n, rgb[3];
+ MSWINColor *ct;
+
+ if(!colorName)
+ return(FALSE);
+
+ /* Is the name in the global color table? */
+ for(ct = MSWINColorTable; ct->colorName; ct++)
+ if(!struncmp(ct->colorName, colorName, (int)strlen(ct->colorName))){
+ *cf = ct->colorRef;
+ return(TRUE);
+ }
+
+ /* Not a named color, try RRR,GGG,BBB */
+ for(i = 0; i < 3; i++){
+ if(i && *colorName++ != ',')
+ return(FALSE);
+
+ for(rgb[i] = 0, j = 0; j < 3; j++)
+ if((n = *colorName++ - '0') < 0 || n > 9)
+ return(FALSE);
+ else
+ rgb[i] = (rgb[i] * 10) + n;
+ }
+
+ *cf = RGB (rgb[0], rgb[1], rgb[2]);
+ return (TRUE);
+}
+
+
+LOCAL char *
+ConvertStringRGB(char *colorName, size_t ncolorName, COLORREF colorRef)
+{
+ MSWINColor *cf;
+
+ for(cf = MSWINColorTable;
+ cf->colorName && cf->colorRef != colorRef;
+ cf++)
+ ;
+
+ if(cf->colorName){
+ strncpy(colorName, cf->colorName, ncolorName);
+ colorName[ncolorName-1] = '\0';
+ }
+ else
+ snprintf(colorName, ncolorName, "%.3d,%.3d,%.3d",
+ GetRValue(colorRef), GetGValue(colorRef), GetBValue(colorRef));
+
+ return(colorName);
+}
+
+
+/*
+ * Map from integer color value to canonical color name.
+ */
+char *
+colorx(int color)
+{
+ MSWINColor *ct;
+ static char cbuf[RGBLEN+1];
+
+ if(color < fullColorTableSize){
+ ct = &MSWINColorTable[color];
+ if(ct->canonicalName)
+ return(ct->canonicalName);
+ }
+
+ /* not supposed to get here */
+ snprintf(cbuf, sizeof(cbuf), "color%03.3d", color);
+ return(cbuf);
+}
+
+
+/*
+ * Argument is a color name which could be an RGB string, a name like "blue",
+ * or a name like "color011".
+ *
+ * Returns a pointer to the canonical name of the color.
+ */
+char *
+color_to_canonical_name(char *s)
+{
+ int i, j, n, rgb[3];
+ MSWINColor *ct;
+ COLORREF cr;
+ static char cn[RGBLEN+1];
+
+ if(!s)
+ return(NULL);
+
+ if(!struncmp(s, MATCH_NORM_COLOR, RGBLEN) || !struncmp(s, MATCH_NONE_COLOR, RGBLEN))
+ return(s);
+
+ for(ct = MSWINColorTable; ct->colorName; ct++)
+ if(!struncmp(ct->colorName, s, (int)strlen(ct->colorName)))
+ break;
+
+ if(ct->colorName)
+ return(ct->canonicalName);
+
+ /* maybe it is RGB? */
+ for(i = 0; i < 3; i++){
+ if(i && *s++ != ',')
+ return("");
+
+ for(rgb[i] = 0, j = 0; j < 3; j++)
+ if((n = *s++ - '0') < 0 || n > 9)
+ return("");
+ else
+ rgb[i] = (rgb[i] * 10) + n;
+ }
+
+ cr = RGB(rgb[0], rgb[1], rgb[2]);
+
+ /*
+ * Now compare that RGB against the color table RGBs. If it is
+ * in the table, return the canonical name, else return the RGB string.
+ */
+ for(ct = MSWINColorTable; ct->colorName; ct++)
+ if(ct->colorRef == cr)
+ break;
+
+ if(ct->colorName)
+ return(ct->canonicalName);
+ else{
+ snprintf(cn, sizeof(cn), "%.3d,%.3d,%.3d",
+ GetRValue(cr), GetGValue(cr), GetBValue(cr));
+ return(cn);
+ }
+}
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Toolbar setup routines.
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+LOCAL void
+TBToggle (HWND hWnd)
+{
+ PTTYINFO pTTYInfo;
+
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return;
+
+ if (pTTYInfo->toolBarSize > 0)
+ TBHide (hWnd);
+ else
+ TBShow (hWnd);
+}
+
+
+LOCAL void
+TBPosToggle (HWND hWnd)
+{
+ PTTYINFO pTTYInfo;
+
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return;
+
+ pTTYInfo->toolBarTop = !pTTYInfo->toolBarTop;
+ if(pTTYInfo->hTBWnd){
+ TBHide (hWnd);
+ TBShow (hWnd);
+ }
+}
+
+
+LOCAL void
+TBShow (HWND hWnd)
+{
+ PTTYINFO pTTYInfo;
+ RECT rc;
+
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return;
+
+
+ /*
+ * Make sure the tool bar not already shown.
+ */
+ if (pTTYInfo->toolBarSize > 0)
+ return;
+
+
+
+ /*
+ * Make procinstance for dialog funciton.
+ */
+ HideCaret (hWnd);
+ if (gToolBarProc == NULL)
+ gToolBarProc = (DLGPROC) MakeProcInstance( (FARPROC) ToolBarProc,
+ ghInstance ) ;
+ if (gTBBtnProc == NULL)
+ gTBBtnProc = (WNDPROC) MakeProcInstance( (FARPROC) TBBtnProc,
+ ghInstance ) ;
+
+
+ /*
+ * Create the dialog box.
+ */
+ pTTYInfo->hTBWnd = CreateDialog (ghInstance,
+ MAKEINTRESOURCE (pTTYInfo->curToolBarID),
+ hWnd,
+ gToolBarProc);
+ if (pTTYInfo->hTBWnd == NULL) {
+ ShowCaret (hWnd);
+ return;
+ }
+
+ SetFocus (hWnd);
+
+
+ /*
+ * Adjust the window size.
+ */
+ GetWindowRect (pTTYInfo->hTBWnd, &rc); /* Get Toolbar size. */
+ pTTYInfo->toolBarSize = (CORD)(rc.bottom - rc.top);
+
+ GetClientRect (hWnd, &rc); /* Get TTY window size. */
+ SizeTTY (hWnd, 0, (CORD)rc.bottom, (CORD)rc.right);
+ ShowCaret (hWnd);
+}
+
+
+LOCAL void
+TBHide (HWND hWnd)
+{
+ PTTYINFO pTTYInfo;
+ RECT rc;
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return;
+
+
+ if (pTTYInfo->toolBarSize == 0)
+ return;
+
+ DestroyWindow (pTTYInfo->hTBWnd);
+ pTTYInfo->hTBWnd = NULL;
+ if (pTTYInfo->toolBarBtns != NULL)
+ MemFree (pTTYInfo->toolBarBtns);
+ pTTYInfo->toolBarBtns = NULL;
+
+
+ /*
+ * Adjust the window size.
+ */
+ pTTYInfo->toolBarSize = 0;
+ GetClientRect (hWnd, &rc);
+ SizeTTY (hWnd, 0, (CORD)rc.bottom, (CORD)rc.right);
+}
+
+
+LOCAL void
+TBSwap (HWND hWnd, int newID)
+{
+ PTTYINFO pTTYInfo;
+ RECT rc;
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return;
+
+ if (pTTYInfo->toolBarSize == 0 || pTTYInfo->curToolBarID == newID)
+ return;
+
+ /*
+ * Dispose of old tool bar window.
+ */
+ HideCaret (hWnd);
+
+ DestroyWindow (pTTYInfo->hTBWnd);
+ pTTYInfo->hTBWnd = NULL;
+ if (pTTYInfo->toolBarBtns != NULL)
+ MemFree (pTTYInfo->toolBarBtns);
+ pTTYInfo->toolBarBtns = NULL;
+
+
+
+ /*
+ * Create the new dialog box.
+ */
+ pTTYInfo->hTBWnd = CreateDialog (ghInstance,
+ MAKEINTRESOURCE (newID),
+ hWnd,
+ gToolBarProc);
+ if (pTTYInfo->hTBWnd == NULL) {
+ ShowCaret (hWnd);
+ return;
+ }
+ pTTYInfo->curToolBarID = newID;
+ SetFocus (hWnd); /* Return focus to parent. */
+
+
+ /*
+ * Fit new tool bar into old tool bars position. This assumes that
+ * all tool bars are about the same height.
+ */
+ GetClientRect (hWnd, &rc); /* Get TTY window size. */
+ if (pTTYInfo->toolBarTop)
+ /* Position at top of window. */
+ SetWindowPos (pTTYInfo->hTBWnd, HWND_TOP,
+ 0, 0,
+ rc.right, pTTYInfo->toolBarSize,
+ SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
+ else
+ /* Position at bottom of window. */
+ SetWindowPos (pTTYInfo->hTBWnd, HWND_TOP,
+ 0, pTTYInfo->ySize - pTTYInfo->toolBarSize,
+ rc.right, pTTYInfo->toolBarSize,
+ SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
+
+ ShowCaret (hWnd);
+}
+
+
+BOOL FAR PASCAL __export
+ToolBarProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ RECT rc;
+ BOOL ret;
+ int height;
+ HBRUSH hBrush;
+ HWND hCld;
+ int btnCount;
+ int i;
+ PTTYINFO pTTYInfo;
+
+
+ pTTYInfo = gpTTYInfo;
+
+ ret = FALSE;
+ switch (msg) {
+
+ case WM_INITDIALOG:
+ /* Fit dialog to window. */
+ GetWindowRect (hWnd, &rc);
+ height = rc.bottom - rc.top;
+ GetClientRect (GetParent (hWnd), &rc);
+ SetWindowPos (hWnd, HWND_TOP, 0, 0, rc.right, height,
+ SWP_NOZORDER | SWP_NOACTIVATE);
+
+ /* Count child windows.*/
+ btnCount = 0;
+ for (hCld = GetWindow (hWnd, GW_CHILD);
+ hCld;
+ hCld = GetWindow (hCld, GW_HWNDNEXT))
+ ++btnCount;
+
+ /* Allocate a list of previous child procs. */
+ if (pTTYInfo->toolBarBtns != NULL)
+ MemFree (pTTYInfo->toolBarBtns);
+ pTTYInfo->toolBarBtns = MemAlloc (sizeof (BtnList) * (btnCount + 1));
+
+ /* Subclass all child windows. */
+ for (i = 0, hCld = GetWindow (hWnd, GW_CHILD);
+ hCld;
+ ++i, hCld = GetWindow (hCld, GW_HWNDNEXT)) {
+ pTTYInfo->toolBarBtns[i].wndID = GET_ID (hCld);
+ pTTYInfo->toolBarBtns[i].wndProc =
+ (WNDPROC)(LONG_PTR)MyGetWindowLongPtr (hCld,
+ GWLP_WNDPROC);
+ MySetWindowLongPtr (hCld, GWLP_WNDPROC, (void *)(LONG_PTR)TBBtnProc);
+ }
+ pTTYInfo->toolBarBtns[i].wndID = 0;
+ pTTYInfo->toolBarBtns[i].wndProc = NULL;
+
+ ret = FALSE;
+ break;
+
+
+ case WM_COMMAND:
+ if (wParam >= KS_RANGESTART && wParam <= KS_RANGEEND){
+ ProcessMenuItem (GetParent (hWnd), wParam);
+ /* Set input focus back to parent. */
+ SetFocus (GetParent (hWnd));
+ ret = TRUE;
+ break;
+ }
+ break;
+
+#ifdef WIN32
+ case WM_CTLCOLORBTN:
+#else
+ case WM_CTLCOLOR:
+#endif
+ if (HIWORD (lParam) == CTLCOLOR_DLG) {
+ if(pTTYInfo->hTBBrush != NULL){
+ DeleteObject(pTTYInfo->hTBBrush);
+ pTTYInfo->hTBBrush = NULL;
+ }
+
+ hBrush = CreateSolidBrush (GetSysColor (COLOR_ACTIVEBORDER));
+ return ((BOOL)!!(pTTYInfo->hTBBrush = hBrush));
+ }
+ }
+ return (ret);
+}
+
+
+/*
+ * Subclass toolbar button windows.
+ *
+ * These buttons will automatically return the input focus to
+ * the toolbar's parent
+ */
+LRESULT FAR PASCAL __export
+TBBtnProc (HWND hBtn, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ PTTYINFO pTTYInfo;
+ HWND hPrnt;
+ int i;
+ WORD id;
+ LRESULT ret;
+ WNDPROC wndProc;
+
+
+ /*
+ * Find previous window proc.
+ */
+ pTTYInfo = gpTTYInfo;
+ id = GET_ID (hBtn);
+ for (i = 0; pTTYInfo->toolBarBtns[i].wndID != 0; ++i)
+ if (pTTYInfo->toolBarBtns[i].wndID == id)
+ goto FoundWindow;
+ /* Whoops! Didn't find window, don't know how to pass message. */
+ return (0);
+
+
+FoundWindow:
+ wndProc = pTTYInfo->toolBarBtns[i].wndProc;
+
+
+
+ if (uMsg == WM_LBUTTONUP || uMsg == WM_MBUTTONUP || uMsg == WM_RBUTTONUP) {
+ /*
+ * On mouse button up restore input focus to IDC_RESPONCE, which
+ * processes keyboard input.
+ */
+ ret = CallWindowProc (wndProc, hBtn, uMsg, wParam, lParam);
+ hPrnt = GetParent (GetParent (hBtn));
+ if (hPrnt)
+ SetFocus (hPrnt);
+ return (ret);
+ }
+
+ return (CallWindowProc (wndProc, hBtn, uMsg, wParam, lParam));
+}
+
+
+/*
+ * return bitmap of allowed Edit Menu items
+ */
+LOCAL UINT
+UpdateEditAllowed(HWND hWnd)
+{
+ PTTYINFO pTTYInfo;
+ UINT fAccel = EM_NONE;
+
+ if((pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO)) != NULL){
+ if(EditPasteAvailable())
+ fAccel |= (EM_PST | EM_PST_ABORT);
+ else if(IsClipboardFormatAvailable (CF_UNICODETEXT) && gPasteEnabled)
+ fAccel |= EM_PST;
+
+ if(SelAvailable()){
+ fAccel |= (EM_CP | EM_CP_APPEND);
+ }
+ else{
+ if(gAllowCut)
+ fAccel |= EM_CUT;
+
+ if(gAllowCopy)
+ fAccel |= (EM_CP | EM_CP_APPEND);
+ }
+
+ if (pTTYInfo->menuItems[KS_WHEREIS - KS_RANGESTART].miActive)
+ fAccel |= EM_FIND;
+ }
+ return(fAccel);
+}
+
+
+#ifdef ACCELERATORS
+#ifdef ACCELERATORS_OPT
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Accelorator key routines.
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+LOCAL void
+AccelCtl (HWND hWnd, int ctl, BOOL saveChange)
+{
+ PTTYINFO pTTYInfo;
+ BOOL load, changed;
+
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return;
+
+ switch (ctl) {
+ case ACCEL_LOAD :
+ load = TRUE;
+ break;
+ case ACCEL_UNLOAD :
+ load = FALSE;
+ break;
+ case ACCEL_TOGGLE :
+ load = pTTYInfo->hAccel == NULL;
+ break;
+ default :
+ load = FALSE;
+ break;
+ }
+
+ changed = FALSE;
+ if (load && pTTYInfo->hAccel == NULL) {
+ /* Load em up. */
+ pTTYInfo->hAccel = LoadAccelerators (ghInstance,
+ MAKEINTRESOURCE (IDR_ACCEL_PINE));
+ changed = TRUE;
+ }
+ else if(!load && pTTYInfo->hAccel) {
+ /* unload em. */
+ FreeResource (pTTYInfo->hAccel);
+ pTTYInfo->hAccel = NULL;
+ changed = TRUE;
+ }
+
+ if (changed && saveChange)
+ DidResize (pTTYInfo);
+}
+#endif
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Accelorator key routines.
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+LOCAL void
+AccelManage (HWND hWnd, long accels)
+{
+ PTTYINFO pTTYInfo;
+ ACCEL accelarray[EM_MAX_ACCEL];
+ int n;
+ static ACCEL am_cp = {
+ FVIRTKEY | FCONTROL | FSHIFT | FNOINVERT, 'C', IDM_EDIT_COPY
+ };
+ static ACCEL am_cp_append = {
+ FVIRTKEY | FCONTROL | FALT | FNOINVERT, 'C', IDM_EDIT_COPY_APPEND
+ };
+ static ACCEL am_find = {
+ FVIRTKEY | FCONTROL | FSHIFT | FNOINVERT, 'F', IDM_MI_WHEREIS
+ };
+ static ACCEL am_pst = {
+ FVIRTKEY | FCONTROL | FSHIFT | FNOINVERT, 'V', IDM_EDIT_PASTE
+ };
+ static ACCEL am_pst_abort = {
+ FVIRTKEY | FCONTROL | FALT | FNOINVERT, 'V', IDM_EDIT_CANCEL_PASTE
+ };
+ static ACCEL am_cut = {
+ FVIRTKEY | FCONTROL | FSHIFT | FNOINVERT, 'X', IDM_EDIT_CUT
+ };
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL || pTTYInfo->fAccel == (UINT)accels)
+ return;
+
+ if(pTTYInfo->hAccel){
+ DestroyAcceleratorTable(pTTYInfo->hAccel);
+ pTTYInfo->hAccel = NULL;
+ pTTYInfo->fAccel = EM_NONE;
+ }
+
+ n = 0;
+
+ if(accels & EM_CP)
+ accelarray[n++] = am_cp;
+
+ if(accels & EM_CP_APPEND)
+ accelarray[n++] = am_cp_append;
+
+ if(accels & EM_FIND)
+ accelarray[n++] = am_find;
+
+ if(accels & EM_PST)
+ accelarray[n++] = am_pst;
+
+ if(accels & EM_PST_ABORT)
+ accelarray[n++] = am_pst_abort;
+
+ if(accels & EM_CUT)
+ accelarray[n++] = am_cut;
+
+ /* Load em up. */
+ if(n && (pTTYInfo->hAccel = CreateAcceleratorTable(accelarray, n)))
+ pTTYInfo->fAccel = accels;
+ else
+ pTTYInfo->fAccel = EM_NONE;
+}
+
+
+LOCAL void
+UpdateAccelerators (HWND hWnd)
+{
+ AccelManage (hWnd, UpdateEditAllowed(hWnd));
+}
+#endif
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Mouse Selection routines
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+LOCAL BOOL SelSelected = FALSE;
+LOCAL BOOL SelTracking = FALSE;
+LOCAL int SelAnchorRow;
+LOCAL int SelAnchorCol;
+LOCAL int SelPointerRow;
+LOCAL int SelPointerCol;
+typedef struct {
+ TCHAR *pRow;
+ int len;
+} CopyRow;
+
+
+LOCAL void
+SelRSet (int oStart, int oEnd)
+{
+ CharAttrib *pca;
+
+ for (pca = gpTTYInfo->pAttrib + oStart; oStart < oEnd; ++pca, ++oStart)
+ pca->style |= CHAR_ATTR_SEL;
+}
+
+
+LOCAL void
+SelRClear (int oStart, int oEnd)
+{
+ CharAttrib *pca;
+
+ for (pca = gpTTYInfo->pAttrib + oStart; oStart < oEnd; ++pca, ++oStart)
+ pca->style &= ~CHAR_ATTR_SEL;
+}
+
+
+LOCAL void
+SelRInvalidate (int oStart, int oEnd)
+{
+ RECT rect;
+ int sRow, sCol;
+ int eRow, eCol;
+
+ sRow = oStart / gpTTYInfo->actNColumn;
+ sCol = oStart % gpTTYInfo->actNColumn;
+ eRow = oEnd / gpTTYInfo->actNColumn;
+ eCol = oEnd % gpTTYInfo->actNColumn;
+
+ rect.top = (sRow * gpTTYInfo->yChar) + gpTTYInfo->yOffset;
+ rect.bottom = ((eRow+1) * gpTTYInfo->yChar) + gpTTYInfo->yOffset;
+ if (sRow == eRow) {
+ rect.left = (sCol * gpTTYInfo->xChar) + gpTTYInfo->xOffset;
+ rect.right = ((eCol+1) * gpTTYInfo->xChar) + gpTTYInfo->xOffset;
+ } else {
+ rect.left = gpTTYInfo->xOffset;
+ rect.right = (gpTTYInfo->actNColumn * gpTTYInfo->xChar) +
+ gpTTYInfo->xOffset;
+ }
+ InvalidateRect (ghTTYWnd, &rect, FALSE);
+}
+
+
+/*
+ * Start a mouse selection.
+ */
+LOCAL void
+SelStart (int nRow, int nColumn)
+{
+ SelClear ();
+ SelTracking = TRUE;
+ SelSelected = TRUE;
+ SelPointerRow = SelAnchorRow = nRow;
+ SelPointerCol = SelAnchorCol = nColumn;
+ return;
+}
+
+
+/*
+ * Finish a mouse selection.
+ */
+LOCAL void
+SelFinish (int nRow, int nColumn)
+{
+ if (nRow == SelAnchorRow && nColumn == SelAnchorCol) {
+ /* Mouse up in same place it went down - no selection. */
+ SelClear ();
+ }
+ else {
+ /* Update screen selection and set final position of mouse
+ * then turn of mouse tracking. Selection remains in effect
+ * until SelClear is called. */
+ SelTrackMouse (nRow, nColumn);
+ SelTracking = FALSE;
+ }
+}
+
+
+LOCAL void
+SelClear (void)
+{
+ int a, p;
+ int s, e;
+
+ if (!SelSelected)
+ return;
+
+ /* Convert the anchor and point coordinates to offsets then
+ * order the offsets. */
+ a = (SelAnchorRow * gpTTYInfo->actNColumn) + SelAnchorCol;
+ p = (SelPointerRow * gpTTYInfo->actNColumn) + SelPointerCol;
+ if (a < p) {
+ s = a;
+ e = p;
+ } else {
+ s = p;
+ e = a;
+ }
+
+ /* Clear selected attribute of those cells in range. */
+ SelRClear (s, e);
+ SelRInvalidate (s, e);
+ SelSelected = FALSE;
+ SelTracking = FALSE;
+}
+
+
+/*
+ * Update the position of the mouse point.
+ */
+LOCAL void
+SelTrackXYMouse (int xPos, int yPos)
+{
+ int nRow;
+ int nColumn;
+
+ nColumn = (xPos - gpTTYInfo->xOffset) / gpTTYInfo->xChar;
+ nRow = (yPos - gpTTYInfo->yOffset) / gpTTYInfo->yChar;
+
+ SelTrackMouse (nRow, nColumn);
+}
+
+
+/*
+ * Update the position of the mouse point.
+ */
+LOCAL void
+SelTrackMouse (int nRow, int nColumn)
+{
+ int a, p, n;
+
+ if (!SelTracking)
+ return;
+
+ /* Constrain the cel position to be on the screen. But allow
+ * for the Column to be one past the right edge of the screen so
+ * the user can select the right most cel of a row. */
+ nColumn = MAX(0, nColumn);
+ nColumn = MIN(gpTTYInfo->actNColumn, nColumn);
+ nRow = MAX(0, nRow);
+ nRow = MIN(gpTTYInfo->actNRow-1, nRow);
+
+
+ /* Convert the anchor, previous mouse position, and new mouse
+ * position to offsets. */
+ a = (SelAnchorRow * gpTTYInfo->actNColumn) + SelAnchorCol;
+ p = (SelPointerRow * gpTTYInfo->actNColumn) + SelPointerCol;
+ n = (nRow * gpTTYInfo->actNColumn) + nColumn;
+
+ /* If previous position same as current position, do nothing. */
+ if (p == n)
+ return;
+
+ /* there are six possible orderings of the points, each with
+ * a different action:
+ * order clear set redraw
+ * n p a n - p n - p
+ * p n a p - n p - n
+ * p a n p - a a - n p - n
+ * a p n p - n p - n
+ * a n p n - p n - p
+ * n a p a - p n - a n - p
+ */
+ if (p < a) {
+ if (n < a) {
+ if (n < p) {
+ SelRSet (n, p);
+ SelRInvalidate (n, p);
+ } else {
+ SelRClear (p, n);
+ SelRInvalidate (p, n);
+ }
+ } else {
+ SelRClear (p, a);
+ SelRSet (a, n);
+ SelRInvalidate (p, n);
+ }
+ } else {
+ if (n > a) {
+ if (n > p) {
+ SelRSet (p, n);
+ SelRInvalidate (p, n);
+ } else {
+ SelRClear (n, p);
+ SelRInvalidate (n, p);
+ }
+ } else {
+ SelRClear (a, p);
+ SelRSet (n, a);
+ SelRInvalidate (n, p);
+ }
+ }
+
+ /* Set new pointer. */
+ SelPointerRow = nRow;
+ SelPointerCol = nColumn;
+}
+
+
+LOCAL BOOL
+SelAvailable (void)
+{
+ return (SelSelected);
+}
+
+
+/*
+ * Copy screen data to clipboard. Actually appends data from screen to
+ * existing handle so as we can implement a "Copy Append" to append to
+ * existing clipboard data.
+ *
+ * The screen does not have real line terminators. We decide where the
+ * actual screen data ends by scanning the line (row) from end backwards
+ * to find the first non-space.
+ *
+ * I don't know how many bytes of data I'll be appending to the clipboard.
+ * So I implemented in two passes. The first finds all the row starts
+ * and length while the second copies the data.
+ */
+LOCAL void
+SelDoCopy (HANDLE hCB, DWORD lenCB)
+{
+ HANDLE newCB; /* Used in reallocation. */
+ TCHAR *pCB; /* Points to CB data. */
+ TCHAR *p2; /* Temp pointer to screen data. */
+ int sRow, eRow; /* Start and End Rows. */
+ int sCol, eCol; /* Start and End columns. */
+ int row, c1, c2; /* temp row and column indexes. */
+ int totalLen; /* total len of new data. */
+ CopyRow *rowTable, *rp; /* pointers to table of rows. */
+ BOOL noLastCRLF = FALSE;
+
+
+ if (OpenClipboard (ghTTYWnd)) { /* ...and we get the CB. */
+ if (EmptyClipboard ()) { /* ...and clear previous CB.*/
+
+
+ /* Find the start and end row and column. */
+ if ( (SelAnchorRow * gpTTYInfo->actNColumn) + SelAnchorCol <
+ (SelPointerRow * gpTTYInfo->actNColumn) + SelPointerCol) {
+ sRow = SelAnchorRow;
+ sCol = SelAnchorCol;
+ eRow = SelPointerRow;
+ eCol = SelPointerCol;
+ }
+ else {
+ sRow = SelPointerRow;
+ sCol = SelPointerCol;
+ eRow = SelAnchorRow;
+ eCol = SelAnchorCol;
+ }
+
+ /* Allocate a table in which we store info on rows. */
+ rowTable = (CopyRow *) MemAlloc (sizeof (CopyRow) * (eRow-sRow+1));
+ if (rowTable == NULL)
+ goto Fail1;
+
+ /* Find the start and length of each row. */
+ totalLen = 0;
+ for (row = sRow, rp = rowTable; row <= eRow; ++row, ++rp) {
+ /* Find beginning and end columns, which depends on if
+ * this is the first or last row in the selection. */
+ c1 = (row == sRow ? sCol : 0);
+ c2 = (row == eRow ? eCol : gpTTYInfo->actNColumn);
+
+ /* Calculate pointer to beginning of this line. */
+ rp->pRow = gpTTYInfo->pScreen +
+ ((row * gpTTYInfo->actNColumn) + c1);
+
+ /* Back down from end column to find first non space.
+ * noLastCRLF indicates if it looks like the selection
+ * should include a CRLF on the end of the line. It
+ * gets set for each line, but only the setting for the
+ * last line in the selection is remembered (which is all
+ * we're interested in). */
+ p2 = gpTTYInfo->pScreen +
+ ((row * gpTTYInfo->actNColumn) + c2);
+ noLastCRLF = TRUE;
+ while (c2 > c1) {
+ if (*(p2-1) != (TCHAR)' ')
+ break;
+ noLastCRLF = FALSE;
+ --c2;
+ --p2;
+ }
+
+ /* Calculate size of line, then increment totalLen plus 2 for
+ * the CRLF which will terminate each line. */
+ rp->len = c2 - c1;
+ totalLen += rp->len + 2;
+ }
+
+ /* Reallocate the memory block. Add one byte for null terminator. */
+ newCB = GlobalReAlloc (hCB, (lenCB + totalLen + 1)*sizeof(TCHAR), 0);
+ if (newCB == NULL)
+ goto Fail2;
+ hCB = newCB;
+
+ pCB = GlobalLock (hCB);
+ if (pCB == NULL)
+ goto Fail2;
+
+ /* Append each of the rows, deliminated by a CRLF. */
+ pCB += lenCB;
+ for (row = sRow, rp = rowTable; row <= eRow; ++row, ++rp) {
+ if (rp->len > 0) {
+ memcpy (pCB, rp->pRow, rp->len * sizeof(TCHAR));
+ pCB += rp->len;
+ }
+ if (row < eRow || !noLastCRLF) {
+ *pCB++ = (TCHAR)ASCII_CR;
+ *pCB++ = (TCHAR)ASCII_LF;
+ }
+ }
+ *pCB = (TCHAR)'\0'; /* Null terminator. */
+ MemFree (rowTable);
+ GlobalUnlock (hCB);
+
+ /* Attempt to pass the data to the clipboard, then release clipboard
+ * and exit function. */
+ if (SetClipboardData (CF_UNICODETEXT, hCB) == NULL)
+ /* Failed! Free the data. */
+ GlobalFree (hCB);
+ CloseClipboard ();
+ }
+ }
+ return;
+
+
+ /* Error exit. */
+Fail2: MemFree (rowTable);
+Fail1: GlobalFree (hCB);
+ CloseClipboard ();
+ return;
+}
+
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Upper Layer Screen routines.
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+/*
+ * Flush the write accumulator buffer.
+ */
+LOCAL void
+FlushWriteAccum (void)
+{
+ if (gpTTYInfo->writeAccumCount > 0) {
+ WriteTTYText (ghTTYWnd, gpTTYInfo->writeAccum,
+ gpTTYInfo->writeAccumCount);
+ gpTTYInfo->writeAccumCount = 0;
+ }
+}
+
+
+/*
+ * Set window's title
+ */
+void
+mswin_settitle(char *utf8_title)
+{
+ TCHAR buf[256];
+ LPTSTR lptstr_title;
+
+ lptstr_title = utf8_to_lptstr(utf8_title);
+ _sntprintf(buf, 256, TEXT("%.*s - Alpine"), 80, lptstr_title);
+ fs_give((void **) &lptstr_title);
+
+ SetWindowText(ghTTYWnd, buf);
+}
+
+
+/*
+ * Return the application instance.
+ */
+WINHAND
+mswin_gethinstance ()
+{
+ return ((WINHAND)ghInstance);
+}
+
+
+WINHAND
+mswin_gethwnd ()
+{
+ return ((WINHAND)ghTTYWnd);
+}
+
+
+/*
+ * destroy splash screen
+ */
+void
+mswin_killsplash()
+{
+ if(ghSplashWnd != NULL){
+ DestroyWindow(ghSplashWnd);
+ ghSplashWnd = NULL;
+ }
+}
+
+
+/*
+ * Called to get mouse event.
+ */
+int
+mswin_getmouseevent (MEvent * pMouse)
+{
+ return (MQGet (pMouse));
+}
+
+
+/*
+ * Make a pop-up menu and track it
+ */
+int
+mswin_popup(MPopup *members)
+{
+ HMENU hMenu;
+ POINT point;
+ MPopup *mp;
+ int n = 1;
+
+ if(members && (hMenu = CreatePopupMenu())){
+
+ PopupConfig(hMenu, members, &n);
+ if(GetCursorPos(&point)
+ && (n = TrackPopupMenu(hMenu,
+ TPM_LEFTALIGN | TPM_TOPALIGN
+ | TPM_RIGHTBUTTON | TPM_LEFTBUTTON
+ | TPM_NONOTIFY | TPM_RETURNCMD,
+ point.x, point.y, 0, ghTTYWnd, NULL))
+ && (mp = PopupId(members, n))){
+ /* Find the member with the internal.id of n */
+ n = mp->internal.id - 1; /* item's "selectable" index */
+ switch(mp->type){
+ case tQueue :
+ CQAdd (mp->data.val, 0);
+ break;
+
+ case tMessage :
+ SendMessage(ghTTYWnd, WM_COMMAND, mp->data.msg, 0);
+ break;
+
+ default :
+ break;
+ }
+ }
+ else
+ n = -1; /* error or nothing selected; same diff */
+
+ DestroyMenu(hMenu);
+ }
+ else
+ n = -1; /* error */
+
+ return(n);
+}
+
+
+LOCAL void
+PopupConfig(HMENU hMenu, MPopup *members, int *n)
+{
+ MENUITEMINFO mitem;
+ int index;
+ TCHAR tcbuf[256];
+
+ for(index = 0; members->type != tTail; members++, index++){
+ memset(&mitem, 0, sizeof(MENUITEMINFO));
+ mitem.cbSize = sizeof(MENUITEMINFO);
+ mitem.fMask = MIIM_TYPE;
+
+ if(members->type == tSubMenu){
+ mitem.fMask |= MIIM_SUBMENU;
+ mitem.fType = MFT_STRING;
+ if(mitem.hSubMenu = CreatePopupMenu()){
+ /* members->label.string is still just a char * */
+ _sntprintf(tcbuf, sizeof(tcbuf)/sizeof(TCHAR),
+ TEXT("%S"), members->label.string);
+ mitem.dwTypeData = tcbuf;
+ mitem.cch = (UINT)_tcslen(tcbuf);
+ InsertMenuItem(hMenu, index, TRUE, &mitem);
+
+ PopupConfig(mitem.hSubMenu, members->data.submenu, n);
+ }
+ }
+ else{
+ switch(members->type){
+ case tSeparator :
+ mitem.fType = MFT_SEPARATOR;
+ break;
+
+ default :
+ mitem.fMask |= MIIM_ID;
+ mitem.wID = members->internal.id = (*n)++;
+ switch(members->label.style){
+ case lChecked :
+ mitem.fMask |= MIIM_STATE;
+ mitem.fState = MFS_CHECKED;
+ break;
+
+ case lDisabled :
+ mitem.fMask |= MIIM_STATE;
+ mitem.fState = MFS_GRAYED;
+ break;
+
+ case lNormal :
+ default :
+ break;
+ }
+
+ mitem.fType = MFT_STRING;
+ _sntprintf(tcbuf, sizeof(tcbuf)/sizeof(TCHAR),
+ TEXT("%S"), members->label.string);
+ mitem.dwTypeData = tcbuf;
+ mitem.cch = (UINT)_tcslen(tcbuf);
+ break;
+ }
+
+ InsertMenuItem(hMenu, index, TRUE, &mitem);
+ }
+ }
+}
+
+
+LOCAL MPopup *
+PopupId(MPopup *members, int id)
+{
+ MPopup *mp;
+
+ for(; members->type != tTail; members++)
+ switch(members->type){
+ case tSubMenu :
+ if(mp = PopupId(members->data.submenu, id))
+ return(mp);
+
+ break;
+
+ case tSeparator :
+ break;
+
+ default :
+ if(members->internal.id == id)
+ return(members);
+
+ break;
+ }
+
+ return(NULL);
+}
+
+
+/*
+ * Make a pop-up offering Copy/Cut/Paste menu and track it
+ */
+LOCAL BOOL
+CopyCutPopup(HWND hWnd, UINT fAllowed)
+{
+ MENUITEMINFO mitem;
+ HMENU hMenu;
+ POINT point;
+ BOOL rv = FALSE;
+ int n = -1;
+
+ /*
+ * If nothing to do just silently return
+ */
+ if(fAllowed != EM_NONE && (hMenu = CreatePopupMenu())){
+ if(fAllowed & EM_CUT){
+ /* Insert a "Copy" option */
+ memset(&mitem, 0, sizeof(MENUITEMINFO));
+ mitem.cbSize = sizeof(MENUITEMINFO);
+ mitem.fMask = MIIM_TYPE | MIIM_ID;
+ mitem.wID = IDM_EDIT_CUT;
+ mitem.fType = MFT_STRING;
+ mitem.dwTypeData = TEXT("Cut");
+ mitem.cch = 3;
+ InsertMenuItem(hMenu, ++n, FALSE, &mitem);
+ }
+
+ if(fAllowed & EM_CP){
+ /* Insert a "Copy" option */
+ memset(&mitem, 0, sizeof(MENUITEMINFO));
+ mitem.cbSize = sizeof(MENUITEMINFO);
+ mitem.fMask = MIIM_TYPE | MIIM_ID;
+ mitem.wID = IDM_EDIT_COPY;
+ mitem.fType = MFT_STRING;
+ mitem.dwTypeData = TEXT("Copy");
+ mitem.cch = 4;
+ InsertMenuItem(hMenu, ++n, FALSE, &mitem);
+ }
+
+ if(fAllowed & EM_CP_APPEND){
+ /* Insert a "Copy Append" option */
+ memset(&mitem, 0, sizeof(MENUITEMINFO));
+ mitem.cbSize = sizeof(MENUITEMINFO);
+ mitem.fMask = MIIM_TYPE | MIIM_ID;
+ mitem.wID = IDM_EDIT_COPY_APPEND;
+ mitem.fType = MFT_STRING;
+ mitem.dwTypeData = TEXT("Copy Append");
+ mitem.cch = 11;
+ InsertMenuItem(hMenu, ++n, FALSE, &mitem);
+ }
+
+ if(fAllowed & EM_PST){
+ /* Insert a "Paste" option */
+ memset(&mitem, 0, sizeof(MENUITEMINFO));
+ mitem.cbSize = sizeof(MENUITEMINFO);
+ mitem.fMask = MIIM_TYPE | MIIM_ID;
+ mitem.wID = IDM_EDIT_PASTE;
+ mitem.fType = MFT_STRING;
+ mitem.dwTypeData = TEXT("Paste");
+ mitem.cch = 5;
+ InsertMenuItem(hMenu, ++n, FALSE, &mitem);
+ }
+
+ if((fAllowed & EM_SEL_ALL) && !(fAllowed & (EM_CP | EM_CP_APPEND))){
+ /* Insert a "Select All" option */
+ memset(&mitem, 0, sizeof(MENUITEMINFO));
+ mitem.cbSize = sizeof(MENUITEMINFO);
+ mitem.fMask = MIIM_TYPE | MIIM_ID;
+ mitem.wID = IDM_EDIT_SEL_ALL;
+ mitem.fType = MFT_STRING;
+ mitem.dwTypeData = TEXT("Select &All");
+ mitem.cch = 11;
+ InsertMenuItem(hMenu, ++n, FALSE, &mitem);
+ }
+
+ if(n >= 0 && GetCursorPos(&point)){
+ TrackPopupMenu(hMenu,
+ TPM_LEFTALIGN | TPM_TOPALIGN
+ | TPM_RIGHTBUTTON | TPM_LEFTBUTTON,
+ point.x, point.y, 0, hWnd, NULL);
+ rv = TRUE;
+ }
+
+ DestroyMenu(hMenu);
+ }
+
+ return(rv);
+}
+
+
+
+/*
+ *
+ */
+void
+pico_popup()
+{
+ MENUITEMINFO mitem;
+ HMENU hMenu;
+ POINT point;
+ UINT fAllow;
+ int n = -1;
+
+ /*
+ * If nothing to do just silently return
+ */
+ if(hMenu = CreatePopupMenu()){
+
+ if((fAllow = UpdateEditAllowed(ghTTYWnd)) != EM_NONE){
+ /* Insert a "Cut" option */
+ memset(&mitem, 0, sizeof(MENUITEMINFO));
+ mitem.cbSize = sizeof(MENUITEMINFO);
+ mitem.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
+ mitem.wID = IDM_EDIT_CUT;
+ mitem.fState = (fAllow & EM_CUT) ? MFS_ENABLED : MFS_GRAYED;
+ mitem.fType = MFT_STRING;
+ mitem.dwTypeData = TEXT("Cut");
+ mitem.cch = 3;
+ InsertMenuItem(hMenu, ++n, FALSE, &mitem);
+
+ /* Insert a "Copy" option */
+ memset(&mitem, 0, sizeof(MENUITEMINFO));
+ mitem.cbSize = sizeof(MENUITEMINFO);
+ mitem.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
+ mitem.wID = IDM_EDIT_COPY;
+ mitem.fState = (fAllow & EM_CP) ? MFS_ENABLED : MFS_GRAYED;
+ mitem.fType = MFT_STRING;
+ mitem.dwTypeData = TEXT("Copy");
+ mitem.cch = 4;
+ InsertMenuItem(hMenu, ++n, FALSE, &mitem);
+
+ /* Insert a "Copy Append" option */
+ memset(&mitem, 0, sizeof(MENUITEMINFO));
+ mitem.cbSize = sizeof(MENUITEMINFO);
+ mitem.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
+ mitem.wID = IDM_EDIT_COPY_APPEND;
+ mitem.fState = (fAllow & EM_CP_APPEND) ? MFS_ENABLED : MFS_GRAYED;
+ mitem.fType = MFT_STRING;
+ mitem.dwTypeData = TEXT("Copy Append");
+ mitem.cch = 11;
+ InsertMenuItem(hMenu, ++n, FALSE, &mitem);
+
+ /* Insert a "Paste" option */
+ memset(&mitem, 0, sizeof(MENUITEMINFO));
+ mitem.cbSize = sizeof(MENUITEMINFO);
+ mitem.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
+ mitem.wID = IDM_EDIT_PASTE;
+ mitem.fType = MFT_STRING;
+ mitem.dwTypeData = TEXT("Paste");
+ mitem.fState = (fAllow & EM_PST) ? MFS_ENABLED : MFS_GRAYED;
+ mitem.cch = 5;
+ InsertMenuItem(hMenu, ++n, FALSE, &mitem);
+ }
+
+ /* Insert a "Select All" option */
+ memset(&mitem, 0, sizeof(MENUITEMINFO));
+ mitem.cbSize = sizeof(MENUITEMINFO);
+ mitem.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
+ mitem.wID = IDM_EDIT_SEL_ALL;
+ mitem.fType = MFT_STRING;
+ mitem.fState = (fAllow & (EM_CP | EM_CP_APPEND))
+ ? MFS_GRAYED : MFS_ENABLED;
+ mitem.dwTypeData = TEXT("Select &All");
+ mitem.cch = 11;
+ InsertMenuItem(hMenu, ++n, FALSE, &mitem);
+
+ if(n >= 0 && GetCursorPos(&point))
+ TrackPopupMenu(hMenu,
+ TPM_LEFTALIGN | TPM_TOPALIGN
+ | TPM_RIGHTBUTTON | TPM_LEFTBUTTON,
+ point.x, point.y, 0, ghTTYWnd, NULL);
+
+ DestroyMenu(hMenu);
+ }
+}
+
+
+
+/*
+ *
+ */
+void
+mswin_paste_popup()
+{
+ MENUITEMINFO mitem;
+ HMENU hMenu;
+ POINT point;
+ UINT fAllow;
+ int n = -1;
+
+ /*
+ * If nothing to do just silently return
+ */
+ if(hMenu = CreatePopupMenu()){
+
+ if((fAllow = UpdateEditAllowed(ghTTYWnd)) != EM_NONE){
+ /* Insert a "Paste" option */
+ memset(&mitem, 0, sizeof(MENUITEMINFO));
+ mitem.cbSize = sizeof(MENUITEMINFO);
+ mitem.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
+ mitem.wID = IDM_EDIT_PASTE;
+ mitem.fType = MFT_STRING;
+ mitem.dwTypeData = TEXT("Paste");
+ mitem.fState = (fAllow & EM_PST) ? MFS_ENABLED : MFS_GRAYED;
+ mitem.cch = 5;
+ InsertMenuItem(hMenu, ++n, FALSE, &mitem);
+
+ if(n >= 0 && GetCursorPos(&point))
+ TrackPopupMenu(hMenu,
+ TPM_LEFTALIGN | TPM_TOPALIGN
+ | TPM_RIGHTBUTTON | TPM_LEFTBUTTON,
+ point.x, point.y, 0, ghTTYWnd, NULL);
+ }
+
+ DestroyMenu(hMenu);
+ }
+}
+
+
+
+void
+mswin_keymenu_popup()
+{
+ HMENU hBarMenu, hMenu;
+ MENUITEMINFO mitem;
+ POINT point;
+ int i, j, n;
+ TCHAR tcbuf[256];
+
+ /*
+ * run thru menubar from left to right and down each list building
+ * a popup of all active members. we run down the menu's rather
+ * than thru the menuItems array so we can preserve order...
+ */
+ if((hMenu = CreatePopupMenu())){
+ if(hBarMenu = GetMenu(ghTTYWnd)){
+ /* For each possible index, look for matching registered key */
+ for(n = 0, i = 1; i <= KS_COUNT; i++)
+ for(j = 0; j < KS_COUNT; j++)
+ if(gpTTYInfo->menuItems[j].miIndex == i){
+ if(gpTTYInfo->menuItems[j].miActive == TRUE
+ && gpTTYInfo->menuItems[j].miLabel){
+ mitem.cbSize = sizeof(MENUITEMINFO);
+ mitem.fMask = MIIM_ID | MIIM_STATE | MIIM_TYPE;
+ mitem.wID = j + KS_RANGESTART;
+ mitem.fState = MFS_ENABLED;
+ mitem.fType = MFT_STRING;
+ /* miLabel is still plain old char *, not utf8 */
+ _sntprintf(tcbuf, sizeof(tcbuf)/sizeof(TCHAR),
+ TEXT("%S"), gpTTYInfo->menuItems[j].miLabel);
+ mitem.dwTypeData = tcbuf;
+ mitem.cch = (UINT)_tcslen(tcbuf);
+ InsertMenuItem(hMenu, n++, TRUE, &mitem);
+
+ if(j + KS_RANGESTART == IDM_MI_SCREENHELP
+ && gHelpCallback){
+ mitem.cbSize = sizeof(MENUITEMINFO);
+ mitem.fMask = MIIM_ID | MIIM_STATE | MIIM_TYPE;
+ mitem.wID = IDM_HELP;
+ mitem.fState = MFS_ENABLED;
+ mitem.fType = MFT_STRING;
+ _sntprintf(tcbuf, sizeof(tcbuf)/sizeof(TCHAR),
+ TEXT("%S"), "Help in New Window");
+ mitem.dwTypeData = tcbuf;
+ mitem.cch = (UINT)_tcslen(tcbuf);
+ InsertMenuItem(hMenu, n++, TRUE, &mitem);
+ }
+ }
+
+ break;
+ }
+
+ if(n > 0 && GetCursorPos(&point)){
+ TrackPopupMenu(hMenu,
+ TPM_LEFTALIGN | TPM_TOPALIGN
+ | TPM_RIGHTBUTTON | TPM_LEFTBUTTON,
+ point.x, point.y, 0, ghTTYWnd, NULL);
+ }
+ }
+
+ DestroyMenu(hMenu);
+ }
+}
+
+
+
+/*
+ *
+ */
+void
+mswin_registericon(int row, int id, char *utf8_file)
+{
+ LPTSTR sPathCopy;
+ HICON hIcon;
+ WORD iIcon;
+ IconList *pIcon;
+
+ /* Turn this off until it can get tuned */
+ return;
+
+ /* Already registered? */
+ for(pIcon = gIconList; pIcon; pIcon = pIcon->next)
+ if(pIcon->id == id){
+ pIcon->row = (short)row;
+ return;
+ }
+
+ sPathCopy = utf8_to_lptstr(utf8_file);
+
+ if(hIcon = ExtractAssociatedIcon(ghInstance, sPathCopy, &iIcon))
+ MSWIconAddList(row, id, hIcon);
+
+ fs_give((void **) &sPathCopy);
+}
+
+
+void
+mswin_destroyicons()
+{
+ MSWIconFree(&gIconList);
+}
+
+
+void
+MSWIconAddList(int row, int id, HICON hIcon)
+{
+ IconList **ppIcon;
+
+ for(ppIcon = &gIconList; *ppIcon; ppIcon = &(*ppIcon)->next)
+ ;
+
+ *ppIcon = (IconList *) MemAlloc (sizeof (IconList));
+ memset(*ppIcon, 0, sizeof(IconList));
+ (*ppIcon)->hIcon = hIcon;
+ (*ppIcon)->id = id;
+ (*ppIcon)->row = (short)row;
+}
+
+
+int
+MSWIconPaint(int row, HDC hDC)
+{
+ IconList *pIcon;
+ int rv = 0;
+
+ for(pIcon = gIconList; pIcon && pIcon->row != row; pIcon = pIcon->next)
+ ;
+
+ if(pIcon){
+ /* Invalidate rectange covering singel character. */
+ DrawIconEx(hDC, 0, (row * gpTTYInfo->yChar) + gpTTYInfo->yOffset,
+ pIcon->hIcon, 2 * gpTTYInfo->xChar, gpTTYInfo->yChar,
+ 0, NULL, DI_NORMAL);
+
+ rv = 1;
+ }
+
+ return(rv);
+}
+
+
+void
+MSWIconFree(IconList **ppIcon)
+{
+ if(ppIcon && *ppIcon){
+ if((*ppIcon)->next)
+ MSWIconFree(&(*ppIcon)->next);
+
+ DestroyIcon((*ppIcon)->hIcon);
+ MemFree (*ppIcon);
+ *ppIcon = NULL;
+ }
+}
+
+
+/*
+ * Set up debugging stuff.
+ */
+void
+mswin_setdebug (int debug, FILE *debugfile)
+{
+ /* Accept new file only if we don't already have a file. */
+ if (mswin_debugfile == 0) {
+ mswin_debug = debug;
+ mswin_debugfile = debugfile;
+ MemDebug (debug, mswin_debugfile);
+ }
+}
+
+
+void
+mswin_setnewmailwidth (int width)
+{
+ gNMW_width = width;
+ gNMW_width = gNMW_width < 20 ? 20 : gNMW_width;
+ gNMW_width = gNMW_width > 170 ? 170 : gNMW_width;
+}
+
+
+/*
+ * Event handler to deal with File Drop events
+ */
+LOCAL BOOL
+ProcessTTYFileDrop (HANDLE wDrop)
+{
+ HDROP hDrop = wDrop;
+ POINT pos;
+ int col, row, i, n;
+ TCHAR fname[1024];
+ char *utf8_fname;
+
+ if(!gpTTYInfo->dndhandler)
+ return(FALSE);
+
+ /* translate drop point to character cell */
+ DragQueryPoint(hDrop, &pos);
+ col = (pos.x - gpTTYInfo->xOffset) / gpTTYInfo->xChar;
+ if (pos.x < gpTTYInfo->xOffset)
+ --col;
+ row = (pos.y - gpTTYInfo->yOffset) / gpTTYInfo->yChar;
+ if (pos.y < gpTTYInfo->yOffset)
+ --row;
+
+ for(n = DragQueryFile(hDrop, (UINT)-1, NULL, 0), i = 0; i < n; i++){
+ DragQueryFile(hDrop, i, fname, 1024);
+ utf8_fname = lptstr_to_utf8(fname);
+ gpTTYInfo->dndhandler (row, col, utf8_fname);
+ fs_give((void **) &utf8_fname);
+ }
+
+ DragFinish(hDrop);
+
+ set_time_of_last_input();
+
+ return(TRUE);
+}
+
+
+/*
+ * Set a callback to deal with Drag 'N Drop events
+ */
+int
+mswin_setdndcallback (int (*cb)())
+{
+ if(gpTTYInfo->dndhandler)
+ gpTTYInfo->dndhandler = NULL;
+
+ if(cb){
+ gpTTYInfo->dndhandler = cb;
+ DragAcceptFiles(ghTTYWnd, TRUE);
+ }
+
+ return(1);
+}
+
+
+/*
+ * Clear previously installed callback to handle Drag 'N Drop
+ * events
+ */
+int
+mswin_cleardndcallback ()
+{
+ gpTTYInfo->dndhandler = NULL;
+ DragAcceptFiles(ghTTYWnd, FALSE);
+ return(1);
+}
+
+
+/*
+ * Set a callback for function 'ch'
+ */
+int
+mswin_setresizecallback (int (*cb)())
+{
+ int i;
+ int e;
+
+
+ /*
+ * Look through whole array for this call back function. Don't
+ * insert duplicate. Also look for empty slot.
+ */
+ e = -1;
+ for (i = 0; i < RESIZE_CALLBACK_ARRAY_SIZE; ++i) {
+ if (gpTTYInfo->resizer[i] == cb)
+ return (0);
+ if (e == -1 && gpTTYInfo->resizer[i] == NULL)
+ e = i;
+ }
+
+ /*
+ * Insert in empty slot or return an error.
+ */
+ if (e != -1) {
+ gpTTYInfo->resizer[e] = cb;
+ return (0);
+ }
+ return (-1);
+}
+
+
+/*
+ * Clear all instances of the callback function 'cb'
+ */
+int
+mswin_clearresizecallback (int (*cb)())
+{
+ int i;
+ int status;
+
+ status = -1;
+ for (i = 0; i < RESIZE_CALLBACK_ARRAY_SIZE; ++i) {
+ if (gpTTYInfo->resizer[i] == cb) {
+ gpTTYInfo->resizer[i] = NULL;
+ status = 0;
+ }
+ }
+ return (status);
+}
+
+
+void
+mswin_beginupdate (void)
+{
+ gpTTYInfo->fMassiveUpdate = TRUE;
+}
+
+
+void
+mswin_endupdate (void)
+{
+ gpTTYInfo->fMassiveUpdate = FALSE;
+ MoveTTYCursor (ghTTYWnd);
+}
+
+
+int
+mswin_charsetid2string(LPTSTR fontCharSet, size_t nfontCharSet, BYTE lfCharSet)
+{
+ TCHAR buf[1024];
+
+ buf[0] = '\0';
+ switch(lfCharSet){
+ case DEFAULT_CHARSET:
+ break;
+ case ANSI_CHARSET:
+ _tcsncpy(buf, TEXT("ANSI_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ case OEM_CHARSET:
+ _tcsncpy(buf, TEXT("OEM_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ case BALTIC_CHARSET:
+ _tcsncpy(buf, TEXT("BALTIC_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ case CHINESEBIG5_CHARSET:
+ _tcsncpy(buf, TEXT("CHINESE_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ case EASTEUROPE_CHARSET:
+ _tcsncpy(buf, TEXT("EASTEUROPE_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ case GB2312_CHARSET:
+ _tcsncpy(buf, TEXT("GF2312_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ case GREEK_CHARSET:
+ _tcsncpy(buf, TEXT("GREEK_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ case HANGUL_CHARSET:
+ _tcsncpy(buf, TEXT("HANGUL_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ case MAC_CHARSET:
+ _tcsncpy(buf, TEXT("MAC_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ case RUSSIAN_CHARSET:
+ _tcsncpy(buf, TEXT("RUSSIAN_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ case SHIFTJIS_CHARSET:
+ _tcsncpy(buf, TEXT("SHIFTJIS_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ case SYMBOL_CHARSET:
+ _tcsncpy(buf, TEXT("SYMBOL_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ case TURKISH_CHARSET:
+ _tcsncpy(buf, TEXT("TURKISH_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ case VIETNAMESE_CHARSET:
+ _tcsncpy(buf, TEXT("VIETNAMESE_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ case JOHAB_CHARSET:
+ _tcsncpy(buf, TEXT("JOHAB_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ case ARABIC_CHARSET:
+ _tcsncpy(buf, TEXT("ARABIC_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ case HEBREW_CHARSET:
+ _tcsncpy(buf, TEXT("HEBREW_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ case THAI_CHARSET:
+ _tcsncpy(buf, TEXT("THAI_CHARSET"), sizeof(buf)/sizeof(TCHAR));
+ break;
+ default:
+ /* default char set if we can't figure it out */
+ break;
+ }
+
+ buf[sizeof(buf)/sizeof(TCHAR) - 1] = '\0';
+
+ _tcsncpy(fontCharSet, buf, nfontCharSet);
+ fontCharSet[nfontCharSet-1] = '\0';
+
+ return 0;
+}
+
+
+BYTE
+mswin_string2charsetid(LPTSTR str)
+{
+ TCHAR tstr[1024];
+
+ if(!str || (lstrlen(str) >= 1024))
+ return DEFAULT_CHARSET;
+
+ if((lstrlen(str) > lstrlen(TEXT("_CHARSET")))
+ && (tcsucmp(str + lstrlen(str) - lstrlen(TEXT("_CHARSET")), TEXT("_CHARSET")) == 0)){
+ _tcsncpy(tstr, str, 1024);
+ tstr[lstrlen(str) - lstrlen(TEXT("_CHARSET"))] = '\0';
+ tstr[1024-1] = '\0';
+ if(tcsucmp(tstr, TEXT("ANSI")) == 0)
+ return ANSI_CHARSET;
+ else if(tcsucmp(tstr, TEXT("DEFAULT")) == 0)
+ return DEFAULT_CHARSET;
+ else if(tcsucmp(tstr, TEXT("OEM")) == 0)
+ return OEM_CHARSET;
+ else if(tcsucmp(tstr, TEXT("BALTIC")) == 0)
+ return BALTIC_CHARSET;
+ else if(tcsucmp(tstr, TEXT("CHINESEBIG5")) == 0)
+ return CHINESEBIG5_CHARSET;
+ else if(tcsucmp(tstr, TEXT("EASTEUROPE")) == 0)
+ return EASTEUROPE_CHARSET;
+ else if(tcsucmp(tstr, TEXT("GB2312")) == 0)
+ return GB2312_CHARSET;
+ else if(tcsucmp(tstr, TEXT("GREEK")) == 0)
+ return GREEK_CHARSET;
+ else if(tcsucmp(tstr, TEXT("HANGUL")) == 0)
+ return HANGUL_CHARSET;
+ else if(tcsucmp(tstr, TEXT("MAC")) == 0)
+ return MAC_CHARSET;
+ else if(tcsucmp(tstr, TEXT("RUSSIAN")) == 0)
+ return RUSSIAN_CHARSET;
+ else if(tcsucmp(tstr, TEXT("SHIFTJIS")) == 0)
+ return SHIFTJIS_CHARSET;
+ else if(tcsucmp(tstr, TEXT("SYMBOL")) == 0)
+ return SYMBOL_CHARSET;
+ else if(tcsucmp(tstr, TEXT("TURKISH")) == 0)
+ return TURKISH_CHARSET;
+ else if(tcsucmp(tstr, TEXT("VIETNAMESE")) == 0)
+ return VIETNAMESE_CHARSET;
+ else if(tcsucmp(tstr, TEXT("JOHAB")) == 0)
+ return JOHAB_CHARSET;
+ else if(tcsucmp(tstr, TEXT("ARABIC")) == 0)
+ return ARABIC_CHARSET;
+ else if(tcsucmp(tstr, TEXT("HEBREW")) == 0)
+ return HEBREW_CHARSET;
+ else if(tcsucmp(tstr, TEXT("THAI")) == 0)
+ return THAI_CHARSET;
+ else
+ return DEFAULT_CHARSET;
+ }
+ else{
+ if(_tstoi(str) > 0)
+ return((BYTE)(_tstoi(str)));
+ else
+ return DEFAULT_CHARSET;
+ }
+}
+
+
+int
+mswin_setwindow(char *fontName_utf8, char *fontSize_utf8, char *fontStyle_utf8,
+ char *windowPosition, char *caretStyle, char *fontCharSet_utf8)
+{
+ LOGFONT newFont;
+ int newHeight;
+ HDC hDC;
+ int ppi;
+ RECT wndRect, cliRect;
+ char *c;
+ char *n;
+ int i;
+ BOOL toolBar = FALSE;
+ BOOL toolBarTop = TRUE;
+ int wColumns, wRows;
+ int wXPos, wYPos;
+ int wXBorder, wYBorder;
+ int wXSize, wYSize;
+ char wp[WIN_POS_STR_MAX_LEN + 1];
+ int showWin = 1;
+ LPTSTR fontName_lpt = NULL;
+ LPTSTR fontCharSet_lpt;
+
+#ifdef SDEBUG
+ if (mswin_debug >= 5)
+ fprintf (mswin_debugfile, "mswin_setwindow::: entered, minimized: %d\n",
+ gpTTYInfo->fMinimized);
+#endif
+
+ /* Require a font name to set font info. */
+ if(fontName_utf8 != NULL && *fontName_utf8 != '\0' &&
+ (fontName_lpt = utf8_to_lptstr(fontName_utf8)) &&
+ _tcslen(fontName_lpt) <= LF_FACESIZE - 1){
+
+ hDC = GetDC(ghTTYWnd);
+ ppi = GetDeviceCaps(hDC, LOGPIXELSY);
+ ReleaseDC(ghTTYWnd, hDC);
+
+ /* Default height, then examin the requested fontSize. */
+ newFont.lfHeight = -MulDiv(12, ppi, 72);
+ if(ScanInt(fontSize_utf8, FONT_MIN_SIZE, FONT_MAX_SIZE, &newHeight)){
+ newHeight = abs(newHeight);
+ newFont.lfHeight = -MulDiv(newHeight, ppi, 72);
+ }
+
+ /* Default font style, then read requested style. */
+ newFont.lfWeight = 0;
+ newFont.lfItalic = 0;
+ if(fontStyle_utf8 != NULL){
+ _strlwr(fontStyle_utf8);
+ if(strstr(fontStyle_utf8, "bold"))
+ newFont.lfWeight = FW_BOLD;
+ if(strstr(fontStyle_utf8, "italic"))
+ newFont.lfItalic = 1;
+ }
+
+ /* Everything else takes the default. */
+ newFont.lfWidth = 0;
+ newFont.lfEscapement = 0;
+ newFont.lfOrientation = 0;
+ newFont.lfUnderline = 0;
+ newFont.lfStrikeOut = 0;
+ fontCharSet_lpt = utf8_to_lptstr(fontCharSet_utf8);
+ if(fontCharSet_lpt){
+ newFont.lfCharSet = mswin_string2charsetid(fontCharSet_lpt);
+ fs_give((void **) &fontCharSet_lpt);
+ }
+
+ newFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
+ newFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ newFont.lfQuality = DEFAULT_QUALITY;
+ newFont.lfPitchAndFamily = FIXED_PITCH;
+ _sntprintf(newFont.lfFaceName, LF_FACESIZE, TEXT("%s"), fontName_lpt);
+ ResetTTYFont (ghTTYWnd, gpTTYInfo, &newFont);
+ }
+
+ if(fontName_lpt)
+ fs_give((void **) &fontName_lpt);
+
+ /*
+ * Set window position. String format is: CxR+X+Y
+ * Where C is the number of columns, R is the number of rows,
+ * and X and Y specify the top left corner of the window.
+ */
+ if(windowPosition != NULL && *windowPosition != '\0'){
+ if(strlen(windowPosition) > sizeof(wp)-1)
+ return(0);
+
+ strncpy(wp, windowPosition, sizeof(wp));
+ wp[sizeof(wp)-1] = '\0';
+
+ /*
+ * Flag characters are at the end of the string. Strip them
+ * off till we get to a number.
+ */
+ i = (int)strlen(wp) - 1;
+ while(i > 0 && (*(wp+i) < '0' || *(wp+i) > '9')){
+ if(*(wp+i) == 't' || *(wp+i) == 'b'){
+ toolBar = TRUE;
+ toolBarTop = (*(wp+i) == 't');
+ }
+
+ if(*(wp+i) == 'd')
+ gfUseDialogs = TRUE;
+
+#ifdef ACCELERATORS_OPT
+ if(*(wp+i) == 'a')
+ AccelCtl(ghTTYWnd, ACCEL_LOAD, FALSE);
+#endif
+
+ *(wp+i) = 0;
+ --i;
+ }
+
+ if(strcmp(wp, "MIN0") == 0){
+ mswin_killsplash();
+ showWin = 0;
+ ShowWindow(ghTTYWnd, SW_MINIMIZE);
+ if(toolBar){
+ gpTTYInfo->toolBarTop = toolBarTop;
+ TBShow(ghTTYWnd);
+ }
+ }
+ else{
+ /* Look for Columns, deliminated by 'x'. */
+ c = strchr (wp, 'x');
+ if (c == NULL)
+ return(0);
+
+ *c = '\0';
+ if(!ScanInt(wp, -999, 9999, &wColumns))
+ return(0);
+
+ /* Look for Rows, deliminated by '+'. */
+ n = c + 1;
+ c = strchr(n, '+');
+ if(c == NULL)
+ return (0);
+
+ *c = '\0';
+ if(!ScanInt(n, -999, 9999, &wRows))
+ return(0);
+
+ /* Look for X position, deliminated by '+'. */
+ n = c + 1;
+ c = strchr(n, '+');
+ if(c == NULL)
+ return(0);
+
+ *c = '\0';
+ if(!ScanInt(n, -999, 9999, &wXPos))
+ return(0);
+
+ /* And get Y position, deliminated by end of string. */
+ n = c + 1;
+ if(!ScanInt(n, -999, 9999, &wYPos))
+ return(0);
+
+
+ /* Constrain the window position and size. */
+ if(wXPos < 0)
+ wXPos = 0;
+
+ if(wYPos < 0)
+ wYPos = 0;
+
+ GetWindowRect(GetDesktopWindow(), &wndRect);
+ if(wXPos > wndRect.right - 20)
+ wXPos = wndRect.right - 100;
+
+ if(wYPos > wndRect.bottom - 20)
+ wYPos = wndRect.bottom - 100;
+
+ /* Get the current window rect and client area. */
+ GetWindowRect(ghTTYWnd, &wndRect);
+ GetClientRect(ghTTYWnd, &cliRect);
+
+ /* Calculate boarder sizes. */
+ wXBorder = wndRect.right - wndRect.left - cliRect.right;
+ wYBorder = wndRect.bottom - wndRect.top - cliRect.bottom;
+
+ /* Show toolbar before calculating content size. */
+ if(toolBar){
+ gpTTYInfo->toolBarTop = toolBarTop;
+ TBShow(ghTTYWnd);
+ }
+
+ /* Calculate new window pos and size. */
+ wXSize = wXBorder + (wColumns * gpTTYInfo->xChar) +
+ (2 * gpTTYInfo->xOffset);
+ wYSize = wYBorder + (wRows * gpTTYInfo->yChar) +
+ gpTTYInfo->toolBarSize + (2 * MARGINE_TOP);
+ if(!gpTTYInfo->fMinimized)
+ MoveWindow(ghTTYWnd, wXPos, wYPos, wXSize, wYSize, TRUE);
+ else{
+ gpTTYInfo->fDesiredSize = TRUE;
+ gpTTYInfo->xDesPos = (CORD)wXPos;
+ gpTTYInfo->yDesPos = (CORD)wYPos;
+ gpTTYInfo->xDesSize = (CORD)wXSize;
+ gpTTYInfo->yDesSize = (CORD)wYSize;
+ }
+ }
+ }
+
+ if(caretStyle != NULL && *caretStyle != '\0')
+ for(i = 0; MSWinCaretTable[i].name; i++)
+ if(!strucmp(MSWinCaretTable[i].name, caretStyle)){
+ CaretTTY(ghTTYWnd, MSWinCaretTable[i].style);
+ break;
+ }
+
+ return(0);
+}
+
+
+int
+mswin_showwindow()
+{
+ mswin_killsplash();
+ ShowWindow (ghTTYWnd, SW_SHOWNORMAL);
+ UpdateWindow (ghTTYWnd);
+
+ return(0);
+}
+
+
+/*
+ * Retreive the current font name, font size, and window position
+ * These get stored in the pinerc file and will be passed to
+ * mswin_setwindow() when pine starts up. See pinerc for comments
+ * on the format.
+ */
+int
+mswin_getwindow(char *fontName_utf8, size_t nfontName,
+ char *fontSize_utf8, size_t nfontSize,
+ char *fontStyle_utf8, size_t nfontStyle,
+ char *windowPosition, size_t nwindowPosition,
+ char *foreColor, size_t nforeColor,
+ char *backColor, size_t nbackColor,
+ char *caretStyle, size_t ncaretStyle,
+ char *fontCharSet_utf8, size_t nfontCharSet)
+{
+ HDC hDC;
+ int ppi;
+ RECT wndRect;
+ char *t;
+
+ if(fontName_utf8 != NULL){
+ t = lptstr_to_utf8(gpTTYInfo->lfTTYFont.lfFaceName);
+ if(strlen(t) < nfontName)
+ snprintf(fontName_utf8, nfontName, "%s", t);
+
+ fs_give((void **) &t);
+ }
+
+ if(fontCharSet_utf8 != NULL){
+ LPTSTR lpt;
+
+ lpt = (LPTSTR) fs_get(nfontCharSet * sizeof(TCHAR));
+ if(lpt){
+ lpt[0] = '\0';
+ mswin_charsetid2string(lpt, nfontCharSet, gpTTYInfo->lfTTYFont.lfCharSet);
+ t = lptstr_to_utf8(lpt);
+ if(strlen(t) < nfontCharSet)
+ snprintf(fontCharSet_utf8, nfontCharSet, "%s", t);
+
+ fs_give((void **) &t);
+
+ fs_give((void **) &lpt);
+ }
+ }
+
+ if(fontSize_utf8 != NULL){
+ hDC = GetDC(ghTTYWnd);
+ ppi = GetDeviceCaps(hDC, LOGPIXELSY);
+ ReleaseDC(ghTTYWnd, hDC);
+ snprintf(fontSize_utf8, nfontSize, "%d", MulDiv(-gpTTYInfo->lfTTYFont.lfHeight, 72, ppi));
+ }
+
+ if(fontStyle_utf8 != NULL){
+ char *sep[] = {"", ", "};
+ int iSep = 0;
+
+ *fontStyle_utf8 = '\0';
+ if(gpTTYInfo->lfTTYFont.lfWeight >= FW_BOLD){
+ strncpy(fontStyle_utf8, "bold", nfontStyle);
+ fontStyle_utf8[nfontStyle-1] = '\0';
+ iSep = 1;
+ }
+
+ if(gpTTYInfo->lfTTYFont.lfItalic){
+ strncat(fontStyle_utf8, sep[iSep], nfontStyle-strlen(fontStyle_utf8)-1);
+ fontStyle_utf8[nfontStyle-1] = '\0';
+ strncat(fontStyle_utf8, "italic", nfontStyle-strlen(fontStyle_utf8)-1);
+ fontStyle_utf8[nfontStyle-1] = '\0';
+ }
+ }
+
+ if(windowPosition != NULL){
+ if(gpTTYInfo->fMinimized){
+ strncpy(windowPosition, "MIN0", nwindowPosition);
+ windowPosition[nwindowPosition-1] = '\0';
+ }
+ else{
+ /*
+ * Get the window position. Constrain the top left corner
+ * to be on the screen.
+ */
+ GetWindowRect(ghTTYWnd, &wndRect);
+ if(wndRect.left < 0)
+ wndRect.left = 0;
+
+ if(wndRect.top < 0)
+ wndRect.top = 0;
+
+ snprintf(windowPosition, nwindowPosition, "%dx%d+%d+%d", gpTTYInfo->actNColumn,
+ gpTTYInfo->actNRow, wndRect.left, wndRect.top);
+ }
+
+ if(gpTTYInfo->toolBarSize > 0){
+ strncat(windowPosition, gpTTYInfo->toolBarTop ? "t" : "b",
+ nwindowPosition-strlen(windowPosition)-1);
+ windowPosition[nwindowPosition-1] = '\0';
+ }
+
+ if(gfUseDialogs){
+ strncat(windowPosition, "d", nwindowPosition-strlen(windowPosition)-1);
+ windowPosition[nwindowPosition-1] = '\0';
+ }
+
+ if(gpTTYInfo->hAccel){
+ strncat(windowPosition, "a", nwindowPosition-strlen(windowPosition)-1);
+ windowPosition[nwindowPosition-1] = '\0';
+ }
+
+ if(gpTTYInfo->fMaximized){
+ strncat(windowPosition, "!", nwindowPosition-strlen(windowPosition)-1);
+ windowPosition[nwindowPosition-1] = '\0';
+ }
+ }
+
+ if(foreColor != NULL)
+ ConvertStringRGB(foreColor, nforeColor, gpTTYInfo->rgbFGColor);
+
+ if(backColor != NULL)
+ ConvertStringRGB(backColor, nbackColor, gpTTYInfo->rgbBGColor);
+
+ if(caretStyle != NULL){
+ int i;
+
+ for(i = 0; MSWinCaretTable[i].name; i++)
+ if(MSWinCaretTable[i].style == gpTTYInfo->cCaretStyle){
+ strncpy(caretStyle, MSWinCaretTable[i].name, ncaretStyle);
+ caretStyle[ncaretStyle-1] = '\0';
+ break;
+ }
+ }
+
+ return (0);
+}
+
+
+void
+mswin_noscrollupdate(int flag)
+{
+ gpTTYInfo->noScrollUpdate = (flag != 0);
+ if(flag == 0 && gpTTYInfo->scrollRangeChanged){
+ mswin_setscrollrange(gpTTYInfo->noSUpdatePage, gpTTYInfo->noSUpdateRange);
+ gpTTYInfo->noSUpdatePage = gpTTYInfo->noSUpdateRange = 0;
+ gpTTYInfo->scrollRangeChanged = 0;
+ }
+}
+
+
+/*
+ * Set the scroll range.
+ */
+void
+mswin_setscrollrange (long page, long max)
+{
+ SCROLLINFO scrollInfo;
+
+ if(gpTTYInfo->noScrollUpdate){
+ gpTTYInfo->noSUpdatePage = page;
+ gpTTYInfo->noSUpdateRange = max;
+ gpTTYInfo->scrollRangeChanged = 1;
+ return;
+ }
+ if (max != gpTTYInfo->scrollRange) {
+ scrollInfo.cbSize = sizeof(SCROLLINFO);
+ scrollInfo.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_RANGE;
+ scrollInfo.nMin = 0;
+
+ if (max > 0) {
+ scrollInfo.nMax = (int) max;
+ scrollInfo.nPage = page;
+ SetScrollInfo(ghTTYWnd, SB_VERT, &scrollInfo, TRUE);
+ EnableScrollBar (ghTTYWnd, SB_VERT, ESB_ENABLE_BOTH);
+ }
+ else {
+ max = 0;
+ scrollInfo.cbSize = sizeof(SCROLLINFO);
+ scrollInfo.fMask |= SIF_POS;
+ scrollInfo.nMax = 1;
+ scrollInfo.nPos = 0;
+ SetScrollInfo(ghTTYWnd, SB_VERT, &scrollInfo, TRUE);
+ EnableScrollBar (ghTTYWnd, SB_VERT, ESB_DISABLE_BOTH);
+ gpTTYInfo->scrollPos = 0;
+ }
+ gpTTYInfo->scrollRange = (int)max;
+ }
+}
+
+
+/*
+ * Set the current scroll position.
+ */
+void
+mswin_setscrollpos (long pos)
+{
+ SCROLLINFO scrollInfo;
+
+ if (pos != gpTTYInfo->scrollPos) {
+ scrollInfo.cbSize = sizeof(SCROLLINFO);
+ scrollInfo.fMask = SIF_PAGE | SIF_RANGE;
+
+ GetScrollInfo(ghTTYWnd, SB_VERT, &scrollInfo);
+
+ scrollInfo.fMask |= SIF_POS;
+ scrollInfo.nPos = (int) pos;
+ SetScrollInfo(ghTTYWnd, SB_VERT, &scrollInfo, TRUE);
+
+ gpTTYInfo->scrollPos = pos;
+ }
+}
+
+
+/*
+ * retreive the current scroll postion.
+ */
+long
+mswin_getscrollpos (void)
+{
+ return ((long)((float)GetScrollPos (ghTTYWnd, SB_VERT)));
+}
+
+
+/*
+ * retreive the latest scroll to position.
+ */
+long
+mswin_getscrollto (void)
+{
+ return (gpTTYInfo->scrollTo);
+}
+
+/*
+ * install function to deal with LINEDOWN events
+ */
+void
+mswin_setscrollcallback (cbarg_t cbfunc)
+{
+ gScrollCallback = cbfunc;
+}
+
+
+/*
+ * install function to deal with sort menu messages
+ */
+void
+mswin_setsortcallback (cbarg_t cbfunc)
+{
+ gSortCallback = cbfunc;
+}
+
+
+/*
+ * install function to deal with sort menu messages
+ */
+void
+mswin_setflagcallback (cbarg_t cbfunc)
+{
+ gFlagCallback = cbfunc;
+}
+
+
+void
+mswin_set_erasecreds_callback (cbvoid_t cbfunc)
+{
+ gEraseCredsCallback = cbfunc;
+}
+
+/*
+ * install function to deal with header mode flipping
+ */
+void
+mswin_sethdrmodecallback (cbarg_t cbfunc)
+{
+ gHdrCallback = cbfunc;
+}
+
+
+/*
+ * install function to deal with view in new window messages
+ */
+void
+mswin_setviewinwindcallback (cbvoid_t cbfunc)
+{
+ gViewInWindCallback = cbfunc;
+}
+
+
+/*
+ * install function to deal with zoom mode flipping
+ */
+void
+mswin_setzoomodecallback (cbarg_t cbfunc)
+{
+ gZoomCallback = cbfunc;
+}
+
+
+/*
+ * install function to deal with function key mode flipping
+ */
+void
+mswin_setfkeymodecallback (cbarg_t cbfunc)
+{
+ gFkeyCallback = cbfunc;
+}
+
+
+/*
+ * install function to deal with apply mode flipping
+ */
+void
+mswin_setselectedcallback (cbarg_t cbfunc)
+{
+ gSelectedCallback = cbfunc;
+}
+
+
+/*
+ * return 1 if new mail window is being used
+ */
+int
+mswin_newmailwinon (void)
+{
+ return(gMswinNewMailWin.hwnd ? 1 : 0);
+}
+
+
+/*
+ */
+void
+mswin_setdebugoncallback (cbvoid_t cbfunc)
+{
+ gIMAPDebugONCallback = cbfunc;
+}
+
+
+void
+mswin_setdebugoffcallback (cbvoid_t cbfunc)
+{
+ gIMAPDebugOFFCallback = cbfunc;
+}
+
+
+int
+mswin_setconfigcallback (cbvoid_t cffunc)
+{
+ gConfigScreenCallback = cffunc;
+ return(1);
+}
+
+
+/*
+ * Set the printer font
+ */
+void
+mswin_setprintfont(char *fontName, char *fontSize,
+ char *fontStyle, char *fontCharSet)
+{
+ LPTSTR fn = NULL, fstyle = NULL, fc = NULL;
+
+ if(fontName)
+ fn = utf8_to_lptstr(fontName);
+
+ if(fontStyle)
+ fstyle = utf8_to_lptstr(fontStyle);
+
+ if(fontCharSet)
+ fc = utf8_to_lptstr(fontCharSet);
+
+ /* Require a font name to set font info. */
+ if(fn != NULL && *fn != '\0' && lstrlen(fn) <= LF_FACESIZE - 1){
+
+ _tcsncpy(gPrintFontName, fn, sizeof(gPrintFontName)/sizeof(TCHAR));
+ gPrintFontName[sizeof(gPrintFontName)/sizeof(TCHAR)-1] = 0;
+ if(fstyle){
+ _tcsncpy(gPrintFontStyle, fstyle, sizeof(gPrintFontStyle)/sizeof(TCHAR));
+ gPrintFontStyle[sizeof(gPrintFontStyle)/sizeof(TCHAR)-1] = 0;
+ _tcslwr(gPrintFontStyle); /* Lower case font style. */
+ }
+
+ if(fc){
+ _tcsncpy(gPrintFontCharSet, fc, sizeof(gPrintFontCharSet)/sizeof(TCHAR));
+ gPrintFontCharSet[sizeof(gPrintFontCharSet)/sizeof(TCHAR)-1] = 0;
+ }
+
+ gPrintFontSize = 12;
+ if(ScanInt(fontSize, FONT_MIN_SIZE, FONT_MAX_SIZE, &gPrintFontSize))
+ gPrintFontSize = abs(gPrintFontSize);
+
+ gPrintFontSameAs = FALSE;
+ }
+ else{
+ gPrintFontName[0] = '\0';
+ gPrintFontSameAs = TRUE;
+ }
+
+ if(fn)
+ fs_give((void **) &fn);
+
+ if(fstyle)
+ fs_give((void **) &fstyle);
+
+ if(fc)
+ fs_give((void **) &fc);
+}
+
+
+void
+mswin_getprintfont(char *fontName_utf8, size_t nfontName,
+ char *fontSize_utf8, size_t nfontSize,
+ char *fontStyle_utf8, size_t nfontStyle,
+ char *fontCharSet_utf8, size_t nfontCharSet)
+{
+ if(gPrintFontName[0] == '\0' || gPrintFontSameAs){
+ if(fontName_utf8 != NULL)
+ *fontName_utf8 = '\0';
+ if(fontSize_utf8 != NULL)
+ *fontSize_utf8 = '\0';
+ if(fontStyle_utf8 != NULL)
+ *fontStyle_utf8 = '\0';
+ if(fontCharSet_utf8 != NULL)
+ *fontCharSet_utf8 = '\0';
+ }
+ else{
+ char *u;
+
+ if(fontName_utf8 != NULL){
+ u = lptstr_to_utf8(gPrintFontName);
+ if(u){
+ strncpy(fontName_utf8, u, nfontName);
+ fontName_utf8[nfontName-1] = 0;
+ fs_give((void **) &u);
+ }
+ }
+
+ if(fontSize_utf8 != NULL)
+ snprintf(fontSize_utf8, nfontSize, "%d", gPrintFontSize);
+
+
+ if(fontStyle_utf8 != NULL){
+ u = lptstr_to_utf8(gPrintFontStyle);
+ if(u){
+ strncpy(fontStyle_utf8, u, nfontStyle);
+ fontStyle_utf8[nfontStyle-1] = 0;
+ fs_give((void **) &u);
+ }
+ }
+
+ if(fontCharSet_utf8 != NULL){
+ u = lptstr_to_utf8(gPrintFontCharSet);
+ if(u){
+ strncpy(fontCharSet_utf8, u, nfontCharSet);
+ fontCharSet_utf8[nfontCharSet-1] = 0;
+ fs_give((void **) &u);
+ }
+ }
+ }
+}
+
+
+/*
+ * Set the window help text. Add or delete the Help menu item as needed.
+ */
+int
+mswin_sethelptextcallback(cbstr_t cbfunc)
+{
+ HMENU hMenu;
+
+ gHelpCallback = cbfunc;
+
+ hMenu = GetMenu (ghTTYWnd);
+ if (hMenu == NULL)
+ return (1);
+
+ return(MSWHelpSetMenu (hMenu));
+
+}
+
+
+/*
+ * Set the window help text. Add or delete the Help menu item as needed.
+ */
+int
+mswin_setgenhelptextcallback(cbstr_t cbfunc)
+{
+ HMENU hMenu;
+
+ gHelpGenCallback = cbfunc;
+
+ hMenu = GetMenu (ghTTYWnd);
+ if (hMenu == NULL)
+ return (1);
+
+ return(MSWHelpSetMenu (hMenu));
+
+}
+
+
+int
+MSWHelpSetMenu(HMENU hMenu)
+{
+ BOOL brc;
+ int count;
+
+ /*
+ * Find menu and update it.
+ */
+ count = GetMenuItemCount (hMenu);
+ if (count == -1)
+ return (1);
+
+ hMenu = GetSubMenu (hMenu, count - 1);
+ if (hMenu == NULL)
+ return (1);
+
+ /*
+ * Insert or delete generic help item
+ */
+ if (gHelpGenCallback == NULL){
+ if (gfHelpGenMenu) {
+ brc = DeleteMenu (hMenu, IDM_MI_GENERALHELP, MF_BYCOMMAND);
+ DrawMenuBar (ghTTYWnd);
+ }
+ gfHelpGenMenu = FALSE;
+ }
+ else {
+ if (!gfHelpGenMenu) {
+ brc = InsertMenu (hMenu, 0,
+ MF_BYPOSITION | MF_STRING,
+ IDM_MI_GENERALHELP, TEXT("&General Help"));
+ DrawMenuBar (ghTTYWnd);
+ }
+ gfHelpGenMenu = TRUE;
+ }
+
+ /*
+ * Insert or delete screen help item
+ */
+ if (gHelpCallback == NULL){
+ if (gfHelpMenu) {
+ brc = DeleteMenu (hMenu, IDM_HELP, MF_BYCOMMAND);
+ DrawMenuBar (ghTTYWnd);
+ }
+ gfHelpMenu = FALSE;
+ }
+ else {
+ if (!gfHelpMenu) {
+ brc = InsertMenu (hMenu, gHelpGenCallback ? 2 : 1,
+ MF_BYPOSITION | MF_STRING,
+ IDM_HELP, TEXT("Screen Help in &New Window"));
+ DrawMenuBar (ghTTYWnd);
+ }
+ gfHelpMenu = TRUE;
+ }
+
+ return (0);
+}
+
+
+/*
+ * Set the text displayed when someone tries to close the application
+ * the wrong way.
+ */
+void
+mswin_setclosetext (char *pCloseText)
+{
+ gpCloseText = pCloseText;
+}
+
+
+/*
+ * Called when upper layer is in a busy loop. Allows us to yeild to
+ * Windows and perhaps process some events. Does not yeild control
+ * to other applications.
+ */
+int
+mswin_yield (void)
+{
+ MSG msg;
+ DWORD start;
+ int didmsg = FALSE;
+
+ if (gScrolling)
+ return (TRUE);
+
+ start = GetTickCount ();
+#ifdef CDEBUG
+ if (mswin_debug > 16)
+ fprintf (mswin_debugfile, "mswin_yeild:: Entered\n");
+#endif
+ if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) {
+ if (gpTTYInfo->hAccel == NULL ||
+ !TranslateAccelerator (ghTTYWnd, gpTTYInfo->hAccel, &msg)) {
+ TranslateMessage (&msg);
+ DispatchMessage (&msg);
+ didmsg = TRUE;
+ }
+ }
+#ifdef CDEBUG
+ if (mswin_debug > 16)
+ fprintf (mswin_debugfile, "mswin_yeild:: Delay %ld msec\n",
+ GetTickCount () - start);
+#endif
+ return (didmsg);
+}
+
+
+/*
+ * Called to see if we can process input.
+ * We can't process input when we are in a scrolling mode.
+ */
+int
+mswin_caninput (void)
+{
+ return (!gScrolling && !gMouseTracking);
+}
+
+
+/*
+ * Called to see if there is a character available.
+ */
+int
+mswin_charavail (void)
+{
+ MSG msg;
+ BOOL ca, pa, ma;
+
+ if (gScrolling)
+ return (FALSE);
+
+ RestoreMouseCursor();
+
+ /*
+ * If there are no windows messages waiting for this app, GetMessage
+ * can take a long time. So before calling GetMessage check if
+ * there is anything I should be doing. If there is, then only
+ * call PeekMessage.
+ * BUT! Don't let too much time elapse between calls to GetMessage
+ * or we'll shut out other windows.
+ */
+ ca = CQAvailable ();
+ pa = EditPasteAvailable ();
+#ifdef CDEBUG
+ start = GetTickCount ();
+ if (mswin_debug > 16)
+ fprintf (mswin_debugfile, "%s mswin_charavail:: Entered, ca %d, pa %d\n",dtime(),ca,pa);
+#endif
+ if ((ca || pa) && GetTickCount () < gGMLastCall + GM_MAX_TIME)
+ ma = PeekMessage (&msg, NULL, 0, 0, PM_NOYIELD | PM_REMOVE);
+ else {
+ ma = GetMessage (&msg, NULL, 0, 0);
+ gGMLastCall = GetTickCount ();
+ }
+ if (ma) {
+ if (gpTTYInfo->hAccel == NULL ||
+ !TranslateAccelerator (ghTTYWnd, gpTTYInfo->hAccel, &msg)) {
+ TranslateMessage (&msg);
+ DispatchMessage (&msg);
+ }
+ }
+#ifdef CDEBUG
+ if (mswin_debug > 16)
+ fprintf (mswin_debugfile, "%s mswin_charavail:: Delay %ld msec\n",
+ dtime(), GetTickCount () - start);
+#endif
+
+ return (pa || ca || CQAvailable ());
+}
+
+
+/*
+ * Call to get next character. Dispatch one message.
+ */
+UCS
+mswin_getc (void)
+{
+ BOOL ca, pa, ma;
+ MSG msg;
+
+ if (gScrolling)
+ return (NODATA);
+
+ RestoreMouseCursor();
+
+ mswin_flush();
+
+
+ /*
+ * If there are no windows messages waiting for this app, GetMessage
+ * can take a long time. So before calling GetMessage check if
+ * there is anything I should be doing. If there is, then only
+ * call PeekMessage.
+ */
+ ca = CQAvailable ();
+ pa = EditPasteAvailable ();
+#ifdef CDEBUG
+ if (mswin_debug > 16) {
+ start = GetTickCount ();
+ fprintf (mswin_debugfile, "mswin_getc:: Entered, ca %d pa %d\n", ca, pa);
+ }
+#endif
+ if ((ca || pa) && GetTickCount () < gGMLastCall + GM_MAX_TIME)
+ ma = PeekMessage (&msg, NULL, 0, 0, PM_NOYIELD | PM_REMOVE);
+ else {
+ ma = GetMessage (&msg, NULL, 0, 0);
+ gGMLastCall = GetTickCount ();
+ }
+ if (ma) {
+ if (gpTTYInfo->hAccel == NULL ||
+ !TranslateAccelerator (ghTTYWnd, gpTTYInfo->hAccel, &msg)) {
+ TranslateMessage (&msg);
+ DispatchMessage (&msg);
+ }
+ }
+#ifdef CDEBUG
+ if (mswin_debug > 16)
+ fprintf (mswin_debugfile, "mswin_getc:: Delay %ld msec\n",
+ GetTickCount () - start);
+#endif
+
+
+ if (pa) {
+ SelClear ();
+ return (EditPasteGet ());
+ }
+ if (ca || CQAvailable ()) {
+ SelClear();
+ return ((UCS)(CQGet ()));
+ }
+
+ return (NODATA);
+}
+
+
+/*
+ * Like mswin_getc, but don't yield control. Returns a CTRL key values
+ * where, for example, ctrl+c --> CTRL|'C'.
+ */
+LOCAL UCS
+mswin_getc_fast (void)
+{
+ RestoreMouseCursor();
+
+ if (EditPasteAvailable ()) {
+ SelClear ();
+ return (EditPasteGet ());
+ }
+ if (CQAvailable ()) {
+ SelClear ();
+ return ((UCS)CQGet ());
+ }
+
+ return (NODATA);
+}
+
+
+/*
+ * Flush the character input queue.
+ */
+void
+mswin_flush_input(void)
+{
+ /*
+ * GetQueueStatus tells us if there are any input messages in the message
+ * queue. If there are, we call mswin_getc which will retrieve the
+ * next message (which may or may not be an input message) and process
+ * it. We do that until all the input messages are gone.
+ */
+ CQInit();
+
+ while(GetQueueStatus(QS_INPUT))
+ (void) mswin_getc();
+
+ /* And we clear Pine's character input queue, too. */
+ CQInit();
+}
+
+
+/*
+ * ibmmove - Move cursor to...
+ */
+int
+mswin_move (int row, int column)
+
+{
+ FlushWriteAccum ();
+ if (row < 0 || row >= gpTTYInfo->actNRow)
+ return (-1);
+ if (column < 0 || column >= gpTTYInfo->actNColumn)
+ return (-1);
+ gpTTYInfo->nRow = (CORD)row;
+ gpTTYInfo->nColumn = (CORD)column;
+ MoveTTYCursor (ghTTYWnd);
+ return (0);
+}
+
+
+int
+mswin_getpos (int *row, int *column)
+{
+ FlushWriteAccum ();
+ if (row == NULL || column == NULL)
+ return (-1);
+ *row = gpTTYInfo->nRow;
+ *column = gpTTYInfo->nColumn;
+ return (0);
+}
+
+
+int
+mswin_getscreensize (int *row, int *column)
+{
+ if (row == NULL || column == NULL)
+ return (-1);
+ *row = gpTTYInfo->actNRow;
+ *column = gpTTYInfo->actNColumn;
+#ifdef SDEBUG
+ if (mswin_debug >= 5)
+ fprintf (mswin_debugfile, "mswin_getscreensize::: reported size %d, %d\n",
+ *row, *column);
+#endif
+
+ *column = MIN(*column, NLINE-1);
+
+ return (0);
+}
+
+
+void
+mswin_minimize()
+{
+ if (!gpTTYInfo->fMinimized)
+ ShowWindow(ghTTYWnd, SW_MINIMIZE);
+}
+
+
+int
+mswin_showcursor (int show)
+{
+ int was_shown = gpTTYInfo->fCursorOn;
+
+ if (show) {
+ /* if the cursor was not already show, show it now. */
+ if (!was_shown) {
+ gpTTYInfo->fCursorOn = TRUE;
+ ShowCursor(TRUE);
+ }
+ }
+ else {
+ /* If the cursor is shown, hide it. */
+ if (was_shown){
+ gpTTYInfo->fCursorOn = FALSE;
+ ShowCursor(FALSE);
+ }
+ }
+
+ return (was_shown);
+}
+
+
+int
+mswin_showcaret (int show)
+{
+ int was_shown = gpTTYInfo->fCaretOn;
+
+ if (show) {
+ /* if the caret was not already show, show it now. */
+ if (!was_shown) {
+ gpTTYInfo->fCaretOn = TRUE;
+ ShowCaret(ghTTYWnd);
+ }
+ }
+ else {
+ /* If the caret is shown, hide it. */
+ if (was_shown){
+ gpTTYInfo->fCaretOn = FALSE;
+ HideCaret(ghTTYWnd);
+ }
+ }
+
+ return (was_shown);
+}
+
+
+int
+mswin_puts (char *utf8_str)
+{
+ int strLen;
+ LPTSTR lptstr_str;
+
+ FlushWriteAccum ();
+ if (utf8_str == NULL)
+ return (-1);
+ if(!(lptstr_str = utf8_to_lptstr(utf8_str)))
+ return(-1);
+ strLen = (int)_tcslen (lptstr_str);
+ if (strLen > 0)
+ WriteTTYText (ghTTYWnd, lptstr_str, strLen);
+
+ fs_give((void **) &lptstr_str);
+ return (0);
+}
+
+
+int
+mswin_puts_n (char *utf8_str, int n)
+{
+ LPTSTR lptstr_str, lptstr_p;
+
+ FlushWriteAccum ();
+ if (utf8_str == NULL)
+ return (-1);
+ lptstr_str = utf8_to_lptstr(utf8_str);
+ if(n < _tcslen(lptstr_str))
+ lptstr_str[n] = '\0';
+ for (lptstr_p = lptstr_str; n > 0 && *lptstr_p; n--, lptstr_p++)
+ ;
+ if (lptstr_p > lptstr_str)
+ WriteTTYText (ghTTYWnd, lptstr_str, (int)(lptstr_p - lptstr_str));
+
+ fs_give((void **) &lptstr_str);
+ return (0);
+}
+
+
+int
+mswin_putblock (char *utf8_str, int strLen)
+{
+ LPTSTR lptstr_str;
+
+ FlushWriteAccum ();
+ if (utf8_str == NULL)
+ return (-1);
+ lptstr_str = utf8_to_lptstr(utf8_str);
+ if (strLen > 0)
+ WriteTTYText (ghTTYWnd, lptstr_str, strLen);
+ fs_give((void **) &lptstr_str);
+ return (0);
+}
+
+
+/*
+ * mswin_putc - put a character at the current position in the
+ * current colors
+ */
+int
+mswin_putc (UCS ucs)
+{
+ TCHAR cc = (TCHAR)ucs;
+ if (ucs >= (UCS)(' ')) {
+ /* Not carriage control. */
+ gpTTYInfo->writeAccum[gpTTYInfo->writeAccumCount++] = (TCHAR)ucs;
+ if (gpTTYInfo->writeAccumCount == WRITE_ACCUM_SIZE)
+ FlushWriteAccum ();
+ }
+ else {
+ /* Carriage control. Need to flush write accumulator and
+ * write this char. */
+ FlushWriteAccum ();
+ WriteTTYBlock (ghTTYWnd, &cc, 1);
+ }
+ return (0);
+}
+
+
+/*
+ * ibmoutc - output a single character with the right attributes, but
+ * don't advance the cursor
+ */
+int
+mswin_outc (char c)
+{
+ RECT rect;
+ long offset;
+
+ FlushWriteAccum ();
+
+ switch (c) {
+ case ASCII_BEL:
+ MessageBeep (0);
+ break;
+
+ case ASCII_BS:
+ case ASCII_CR:
+ case ASCII_LF:
+ /* Do nothing for these screen motion characters. */
+ break;
+
+
+ default:
+ /* Paint character to screen. */
+ offset = (gpTTYInfo->nRow * gpTTYInfo->actNColumn) + gpTTYInfo->nColumn;
+ gpTTYInfo->pScreen[offset] = c;
+ gpTTYInfo->pCellWidth[offset] = wcellwidth((UCS)c) * gpTTYInfo->xChar;
+ gpTTYInfo->pAttrib[offset] = gpTTYInfo->curAttrib;
+
+ /* Invalidate rectange covering singel character. */
+ rect.left = (gpTTYInfo->nColumn * gpTTYInfo->xChar) +
+ gpTTYInfo->xOffset;
+ rect.right = rect.left + gpTTYInfo->xChar;
+ rect.top = (gpTTYInfo->nRow * gpTTYInfo->yChar) +
+ gpTTYInfo->yOffset;
+ rect.bottom = rect.top + gpTTYInfo->yChar;
+ gpTTYInfo->screenDirty = TRUE;
+ InvalidateRect (ghTTYWnd, &rect, FALSE);
+ break;
+ }
+ return (0);
+}
+
+/*
+ * ibmrev - change reverse video state
+ */
+int
+mswin_rev (int state)
+{
+ int curState = (gpTTYInfo->curAttrib.style & CHAR_ATTR_REV) != 0;
+
+ if (state != curState){
+ FlushWriteAccum ();
+ if (state) {
+ gpTTYInfo->curAttrib.style |= CHAR_ATTR_REV;
+ SetReverseColor();
+ }
+ else{
+ gpTTYInfo->curAttrib.style &= ~CHAR_ATTR_REV;
+ pico_set_normal_color();
+ }
+ }
+
+ return (0);
+}
+
+
+/*
+ * Get current reverse video state.
+ */
+int
+mswin_getrevstate (void)
+{
+ return ((gpTTYInfo->curAttrib.style & CHAR_ATTR_REV) != 0);
+}
+
+
+/*
+ * ibmrev - change reverse video state
+ */
+int
+mswin_bold (int state)
+{
+ int curState = (gpTTYInfo->curAttrib.style & CHAR_ATTR_BOLD) != 0;
+
+ if (state != curState){
+ FlushWriteAccum ();
+ if (state)
+ gpTTYInfo->curAttrib.style |= CHAR_ATTR_BOLD;
+ else
+ gpTTYInfo->curAttrib.style &= ~CHAR_ATTR_BOLD;
+ }
+
+ return (0);
+}
+
+
+/*
+ * ibmrev - change reverse video state
+ */
+int
+mswin_uline (int state)
+{
+ int curState = (gpTTYInfo->curAttrib.style & CHAR_ATTR_ULINE) != 0;
+
+ if (state != curState){
+ FlushWriteAccum ();
+ if (state)
+ gpTTYInfo->curAttrib.style |= CHAR_ATTR_ULINE;
+ else
+ gpTTYInfo->curAttrib.style &= ~CHAR_ATTR_ULINE;
+ }
+
+ return (0);
+}
+
+
+/*
+ * ibmeeol - erase to the end of the line
+ */
+int
+mswin_eeol (void)
+{
+ TCHAR *cStart;
+ CharAttrib *aStart;
+ int *cwStart;
+ long length, i;
+ RECT rect;
+
+ FlushWriteAccum ();
+
+ /* From current position to end of line. */
+ length = gpTTYInfo->actNColumn - gpTTYInfo->nColumn;
+
+ cStart = gpTTYInfo->pScreen + (gpTTYInfo->nRow * gpTTYInfo->actNColumn)
+ + gpTTYInfo->nColumn;
+ cwStart = gpTTYInfo->pCellWidth + (gpTTYInfo->nRow * gpTTYInfo->actNColumn)
+ + gpTTYInfo->nColumn;
+ for(i = 0; i < length; i++){
+ cStart[i] = (TCHAR)(' ');
+ cwStart[i] = gpTTYInfo->xChar;
+ }
+
+ aStart = gpTTYInfo->pAttrib
+ + (gpTTYInfo->nRow * gpTTYInfo->actNColumn)
+ + gpTTYInfo->nColumn;
+ for(; length > 0; length--, aStart++){
+ aStart->style = CHAR_ATTR_NORM;
+ aStart->rgbFG = gpTTYInfo->curAttrib.rgbFG;
+ aStart->rgbBG = gpTTYInfo->curAttrib.rgbBG;
+ }
+
+ rect.left = (gpTTYInfo->nColumn * gpTTYInfo->xChar) +
+ gpTTYInfo->xOffset;
+ rect.right = gpTTYInfo->xSize;
+ rect.top = (gpTTYInfo->nRow * gpTTYInfo->yChar) +
+ gpTTYInfo->yOffset;
+ rect.bottom = rect.top + gpTTYInfo->yChar;
+ gpTTYInfo->screenDirty = TRUE;
+ InvalidateRect (ghTTYWnd, &rect, FALSE);
+
+ return (0);
+}
+
+
+/*
+ * ibmeeop - clear from cursor to end of page
+ */
+int
+mswin_eeop (void)
+{
+ TCHAR *cStart;
+ CharAttrib *aStart;
+ int *cwStart;
+ long length, i;
+ RECT rect;
+
+ FlushWriteAccum ();
+ /* From current position to end of screen. */
+
+ cStart = gpTTYInfo->pScreen + (gpTTYInfo->nRow * gpTTYInfo->actNColumn)
+ + gpTTYInfo->nColumn;
+ cwStart = gpTTYInfo->pCellWidth + (gpTTYInfo->nRow * gpTTYInfo->actNColumn)
+ + gpTTYInfo->nColumn;
+ length = (long)((gpTTYInfo->pScreen
+ + (gpTTYInfo->actNColumn * gpTTYInfo->actNRow))
+ - cStart);
+ for(i = 0; i < length; i ++){
+ cStart[i] = (TCHAR)(' ');
+ cwStart[i] = gpTTYInfo->xChar;
+ }
+
+ aStart = gpTTYInfo->pAttrib
+ + (gpTTYInfo->nRow * gpTTYInfo->actNColumn)
+ + gpTTYInfo->nColumn;
+
+ for(; length-- > 0; aStart++){
+ aStart->style = CHAR_ATTR_NORM;
+ aStart->rgbFG = gpTTYInfo->curAttrib.rgbFG;
+ aStart->rgbBG = gpTTYInfo->curAttrib.rgbBG;
+ }
+
+ /* Invalidate a rectangle that includes all of the current line down
+ * to the bottom of the window. */
+ rect.left = 0;
+ rect.right = gpTTYInfo->xSize;
+ rect.top = (gpTTYInfo->nRow * gpTTYInfo->yChar) +
+ gpTTYInfo->yOffset;
+ rect.bottom = gpTTYInfo->ySize;
+ gpTTYInfo->screenDirty = TRUE;
+ InvalidateRect (ghTTYWnd, &rect, FALSE);
+
+ return (0);
+}
+
+
+/*
+ * ibmbeep - system beep...
+ */
+int
+mswin_beep (void)
+{
+ MessageBeep (MB_OK);
+ return (0);
+}
+
+
+/*
+ * pause - wait in function for specified number of seconds.
+ */
+void
+mswin_pause (int seconds)
+{
+ DWORD stoptime;
+
+ stoptime = GetTickCount () + (DWORD) seconds * 1000;
+ while (stoptime > GetTickCount ())
+ mswin_yield ();
+}
+
+
+/*
+ * ibmflush - Flush output to screen.
+ *
+ */
+int
+mswin_flush (void)
+{
+
+ /*
+ * Flush cached changes, then update the window.
+ */
+ FlushWriteAccum ();
+ UpdateWindow (ghTTYWnd);
+
+ return (0);
+}
+
+
+/*
+ * A replacement for fflush
+ * relies on #define fflush mswin_fflush
+ */
+#undef fflush
+int
+mswin_fflush (FILE *f)
+{
+ if (f == stdout) {
+ mswin_flush();
+ }
+ else
+ fflush (f);
+
+ return(0);
+}
+
+
+/*
+ * Set the cursor
+ */
+void
+mswin_setcursor (int newcursor)
+{
+ HCURSOR hNewCursor;
+
+ switch(newcursor){
+ case MSWIN_CURSOR_BUSY :
+ hNewCursor = ghCursorBusy;
+ mswin_showcursor(TRUE);
+ break;
+
+ case MSWIN_CURSOR_IBEAM :
+ hNewCursor = ghCursorIBeam;
+ break;
+
+ case MSWIN_CURSOR_HAND :
+ if(hNewCursor = ghCursorHand)
+ break;
+ /* ELSE fall thru to normal cursor */
+
+ case MSWIN_CURSOR_ARROW :
+ default :
+ hNewCursor = ghCursorArrow;
+ break;
+ }
+
+ /* If new cursor requested, select it. */
+ if (ghCursorCurrent != hNewCursor)
+ SetCursor (ghCursorCurrent = hNewCursor);
+}
+
+
+void
+RestoreMouseCursor()
+{
+ if(ghCursorCurrent == ghCursorBusy)
+ mswin_setcursor(MSWIN_CURSOR_ARROW);
+}
+
+
+/*
+ * Display message in windows dialog box.
+ */
+void
+mswin_messagebox (char *msg_utf8, int err)
+{
+ LPTSTR msg_lptstr;
+
+ mswin_killsplash();
+
+ msg_lptstr = utf8_to_lptstr(msg_utf8);
+ MessageBox (NULL, msg_lptstr, gszAppName,
+ MB_OK | ((err) ? MB_ICONSTOP : MB_ICONINFORMATION));
+ fs_give((void **) &msg_lptstr);
+}
+
+
+/*
+ * Signals whether or not Paste should be turned on in the
+ * menu bar.
+ */
+void
+mswin_allowpaste (int on)
+{
+ static short stackp = 0;
+ static unsigned long stack = 0L;
+
+ switch(on){
+ case MSWIN_PASTE_DISABLE :
+ if(stackp){ /* previous state? */
+ if((stackp -= 2) < 0)
+ stackp = 0;
+
+ gPasteEnabled = ((stack >> stackp) & 0x03);
+ }
+ else
+ gPasteEnabled = MSWIN_PASTE_DISABLE;
+
+ break;
+
+ case MSWIN_PASTE_FULL :
+ case MSWIN_PASTE_LINE :
+ if(gPasteEnabled){ /* current state? */
+ stack |= ((unsigned long) gPasteEnabled << stackp);
+ stackp += 2;
+ }
+
+ gPasteEnabled = on;
+ break;
+ }
+
+#ifdef ACCELERATORS
+ UpdateAccelerators(ghTTYWnd);
+#endif
+}
+
+
+/*
+ * Signals whether or not Copy/Cut should be turned on in the
+ * menu bar.
+ */
+void
+mswin_allowcopy (getc_t copyfunc)
+{
+ gCopyCutFunction = copyfunc;
+ gAllowCopy = (copyfunc != NULL);
+#ifdef ACCELERATORS
+ UpdateAccelerators(ghTTYWnd);
+#endif
+}
+
+
+/*
+ * Signals whether or not Copy/Cut should be turned on in the
+ * menu bar.
+ */
+void
+mswin_allowcopycut (getc_t copyfunc)
+{
+ gCopyCutFunction = copyfunc;
+ gAllowCopy = gAllowCut = (copyfunc != NULL);
+#ifdef ACCELERATORS
+ UpdateAccelerators(ghTTYWnd);
+#endif
+}
+
+
+/*
+ * Replace the clipboard's contents with the given string
+ */
+void
+mswin_addclipboard(char *s)
+{
+ HANDLE hCB;
+ char *pCB;
+ size_t sSize;
+
+ if(s && (sSize = strlen(s))){
+ if (OpenClipboard (ghTTYWnd)) { /* ...and we get the CB. */
+ if (EmptyClipboard ()
+ && (hCB = GlobalAlloc (GMEM_MOVEABLE, sSize+2))){
+ pCB = GlobalLock (hCB);
+ memcpy (pCB, s, sSize);
+ pCB[sSize] = '\0'; /* tie it off */
+
+ GlobalUnlock (hCB);
+
+ if (SetClipboardData (CF_TEXT, hCB) == NULL)
+ /* Failed! Free the data. */
+ GlobalFree (hCB);
+ }
+
+ CloseClipboard ();
+ }
+ }
+}
+
+
+/*
+ * Signals if the upper layer wants to track the mouse.
+ */
+void
+mswin_allowmousetrack (int b)
+{
+ gAllowMouseTrack = b;
+ if (b)
+ SelClear ();
+ MyTimerSet ();
+}
+
+
+/*
+ * register's callback to warn
+ */
+void
+mswin_mousetrackcallback(cbarg_t cbfunc)
+{
+ if(!(gMouseTrackCallback = cbfunc))
+ mswin_setcursor (MSWIN_CURSOR_ARROW);
+}
+
+
+/*
+ * Add text to the new mail icon.
+ * Nothing done in win 3.1 (consider changing the program name?)
+ * For win95 we add some tool tip text to the tray icon.
+ */
+void
+mswin_newmailtext (char *t_utf8)
+{
+ LPTSTR t_lptstr;
+
+ /*
+ * If we're given text, then blip the icon to let the user know.
+ * (NOTE: the new shell also gets an updated tooltip.)
+ * Otherwise, we're being asked to resume our normal state...
+ */
+ if(t_utf8){
+ /*
+ * Change the appearance of minimized icon so user knows there's new
+ * mail waiting for them. On win 3.1 systems we redraw the icon.
+ * on win95 systems we update the icon in the task bar,
+ * and possibly udpate the small icon in the taskbar tool tray.
+ */
+ t_lptstr = utf8_to_lptstr(t_utf8);
+ UpdateTrayIcon(NIM_MODIFY, ghNewMailIcon, t_lptstr);
+ fs_give((void **) &t_lptstr);
+ PostMessage(ghTTYWnd,WM_SETICON,ICON_BIG,(LPARAM) ghNewMailIcon);
+ PostMessage(ghTTYWnd,WM_SETICON,ICON_SMALL,(LPARAM) ghNewMailIcon);
+
+ gpTTYInfo->fNewMailIcon = TRUE;
+ }
+ else if(gpTTYInfo->fNewMailIcon) {
+ UpdateTrayIcon(NIM_MODIFY, ghNormalIcon, TEXT("Alpine"));
+ PostMessage(ghTTYWnd,WM_SETICON,ICON_BIG,(LPARAM) ghNormalIcon);
+ PostMessage(ghTTYWnd,WM_SETICON,ICON_SMALL,(LPARAM) ghNormalIcon);
+
+ gpTTYInfo->fNewMailIcon = FALSE;
+ }
+}
+
+
+void
+mswin_mclosedtext (char *t_utf8)
+{
+ LPTSTR t_lptstr;
+
+ if(t_utf8 && gpTTYInfo->fMClosedIcon == FALSE){
+ /*
+ * Change the appearance of minimized icon so user knows
+ * the mailbox closed.
+ */
+ t_lptstr = utf8_to_lptstr(t_utf8);
+ UpdateTrayIcon(NIM_MODIFY, ghMClosedIcon, t_lptstr);
+ fs_give((void **) &t_lptstr);
+ PostMessage(ghTTYWnd,WM_SETICON,ICON_BIG,(LPARAM) ghMClosedIcon);
+ PostMessage(ghTTYWnd,WM_SETICON,ICON_SMALL,(LPARAM) ghMClosedIcon);
+
+ gpTTYInfo->fMClosedIcon = TRUE;
+ }
+ else if(t_utf8 == NULL && gpTTYInfo->fMClosedIcon) {
+ /* only go from yellow to green */
+ UpdateTrayIcon(NIM_MODIFY, ghNormalIcon, TEXT("Alpine"));
+ PostMessage(ghTTYWnd,WM_SETICON,ICON_BIG,(LPARAM) ghNormalIcon);
+ PostMessage(ghTTYWnd,WM_SETICON,ICON_SMALL,(LPARAM) ghNormalIcon);
+
+ gpTTYInfo->fMClosedIcon = FALSE;
+ }
+}
+
+void
+mswin_trayicon(int show)
+{
+ if(show){
+ if(!gpTTYInfo->fTrayIcon){
+ UpdateTrayIcon(NIM_ADD, ghNormalIcon, TEXT("Alpine"));
+ gpTTYInfo->fTrayIcon = TRUE;
+ }
+ }
+ else{
+ if(gpTTYInfo->fTrayIcon){
+ UpdateTrayIcon(NIM_DELETE, 0, NULL);
+ gpTTYInfo->fTrayIcon = FALSE;
+ }
+ }
+}
+
+
+void
+UpdateTrayIcon(DWORD dwMsg, HICON hIcon, LPTSTR tip)
+{
+ NOTIFYICONDATA nt;
+
+ nt.cbSize = sizeof (nt);
+ nt.hWnd = ghTTYWnd;
+ nt.uID = TASKBAR_ICON_NEWMAIL;
+ switch(dwMsg){
+ case NIM_DELETE :
+ nt.uFlags = 0;
+ break;
+
+ case NIM_ADD : /* send msg to add icon to tray */
+ nt.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
+ nt.uCallbackMessage = TASKBAR_ICON_MESSAGE;
+
+ if(tip){
+ _tcsncpy (nt.szTip, tip, 63);
+ nt.szTip[63] = '\0';
+ }
+ else
+ nt.szTip[0] = '\0';
+ nt.hIcon = hIcon;
+ break;
+
+ case NIM_MODIFY : /* send msg to modify icon in tray */
+ nt.uFlags = NIF_ICON | NIF_TIP;
+ if(tip){
+ _tcsncpy (nt.szTip, tip, 63);
+ nt.szTip[63] = '\0';
+ }
+ else
+ nt.szTip[0] = '\0';
+ nt.hIcon = hIcon;
+ break;
+
+ default :
+ return;
+ }
+
+ Shell_NotifyIcon (dwMsg, &nt);
+}
+
+
+
+/*---------------------------------------------------------------------------
+ *
+ * Client level menu item stuff.
+ *
+ * These are menu items that activate commands in the "client" program.
+ * Generally, the client calls us to tell us which menu items are active
+ * and what key stroke they generate. When such an item is selected it's
+ * key stroke is injected into the character queue as if it was typed by
+ * the user.
+ *
+ *-------------------------------------------------------------------------*/
+
+/*
+ * Clear active status of all "menu items".
+ */
+void
+mswin_menuitemclear (void)
+{
+ int i;
+ HWND hWnd;
+
+
+ for (i = 0; i < KS_COUNT; ++i) {
+ gpTTYInfo->menuItems[i].miActive = FALSE;
+ if (gpTTYInfo->toolBarSize > 0) {
+ hWnd = GetDlgItem(gpTTYInfo->hTBWnd, i + KS_RANGESTART);
+ if (hWnd != NULL)
+ EnableWindow(hWnd, FALSE);
+ }
+ }
+
+ gpTTYInfo->menuItemsCurrent = FALSE;
+ gpTTYInfo->menuItemsIndex = 1;
+ for(i = 0; i < KS_COUNT; i++)
+ gpTTYInfo->menuItems[i].miIndex = 0;
+}
+
+
+/*
+ * Add an item to the cmdmenu
+ */
+void
+mswin_menuitemadd (UCS key, char *label, int menuitem, int flags)
+{
+ int i;
+ HWND hWnd;
+
+ if (menuitem >= KS_RANGESTART && menuitem <= KS_RANGEEND) {
+
+ gpTTYInfo->menuItemsCurrent = FALSE;
+
+ i = menuitem - KS_RANGESTART;
+ gpTTYInfo->menuItems[i].miActive = TRUE;
+ gpTTYInfo->menuItems[i].miKey = key;
+ if(!gpTTYInfo->menuItems[i].miIndex){
+ gpTTYInfo->menuItems[i].miLabel = label;
+ gpTTYInfo->menuItems[i].miIndex = gpTTYInfo->menuItemsIndex++;
+ }
+
+ if (gpTTYInfo->toolBarSize > 0) {
+ hWnd = GetDlgItem(gpTTYInfo->hTBWnd, menuitem);
+ if (hWnd != NULL)
+ EnableWindow(hWnd, TRUE);
+ }
+ }
+}
+
+
+/*
+ * Called when a menu command arrives with an unknown ID. If it is
+ * within the range of the additional menu items, insert the
+ * corresponding character into the char input queue.
+ */
+void
+ProcessMenuItem (HWND hWnd, WPARAM wParam)
+{
+ PTTYINFO pTTYInfo;
+ int i;
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return;
+
+
+ if (wParam >= KS_RANGESTART && wParam <= KS_RANGEEND) {
+ i = (int)(wParam - KS_RANGESTART);
+ if (pTTYInfo->menuItems[i].miActive)
+ CQAdd (pTTYInfo->menuItems[i].miKey, 0);
+ }
+}
+
+
+/*
+ * Called to set a new menu.
+ */
+int
+mswin_setwindowmenu (int menu)
+{
+ int oldmenu;
+ HMENU holdmenu;
+ HMENU hmenu;
+
+
+ oldmenu = gpTTYInfo->curWinMenu;
+ holdmenu = GetMenu (ghTTYWnd);
+ if (gpTTYInfo->curWinMenu != menu) {
+
+ hmenu = LoadMenu (ghInstance, MAKEINTRESOURCE (menu));
+ if (hmenu != NULL) {
+ if (SetMenu (ghTTYWnd, hmenu) != 0) {
+ DestroyMenu (holdmenu);
+ gfHelpMenu = gfHelpGenMenu = FALSE;
+ gpTTYInfo->curWinMenu = menu;
+ }
+ }
+
+ if(menu == MENU_DEFAULT){
+ TBSwap (ghTTYWnd, IDD_TOOLBAR);
+ if(hmenu != NULL)
+ (void) MSWHelpSetMenu(hmenu);
+ }
+ else{
+ TBSwap (ghTTYWnd, IDD_COMPOSER_TB);
+ }
+ }
+ return (oldmenu);
+}
+
+
+
+/*---------------------------------------------------------------------------
+ *
+ * Printing stuff
+ *
+ *-------------------------------------------------------------------------*/
+
+/*
+ * Printing globals
+ */
+LOCAL HDC P_PrintDC; /* Printer device context. */
+LOCAL int P_PageRows; /* Number of rows we put on a page. */
+LOCAL int P_PageColumns; /* Number of columns we put on a page. */
+LOCAL int P_RowHeight; /* Hight of a row in printer pixels. */
+LOCAL int P_CurRow; /* Current row, starting at zero */
+LOCAL int P_CurCol; /* Current col, starting at zero. */
+LOCAL int P_TopOffset; /* Top Margin offset, in pixels. */
+LOCAL int P_LeftOffset; /* Top Margin offset, in pixels. */
+LOCAL HFONT P_hFont; /* Handle to printing font. */
+LPTSTR P_LineText; /* Pointer to line buffer. */
+
+
+
+
+/*
+ * Define the margin as number of lines at top and bottom of page.
+ * (might be better to define as a percent of verticle page size)
+ */
+#define VERTICLE_MARGIN 3 /* lines at top and bottom of page. */
+#define HORIZONTAL_MARGIN 1 /* margine at left & right in chars */
+
+/*
+ * Several errors that can be reported.
+ */
+#define PE_DIALOG_FAILED 1
+#define PE_USER_CANCEL 2
+#define PE_CANT_START_DOC 3
+#define PE_CANT_FINISH_DOC 4
+#define PE_OUT_OF_MEMORY 5
+#define PE_GENERAL_ERROR 6
+#define PE_OUT_OF_DISK 7
+#define PE_PRINTER_NOT_FOUND 8
+#define PE_PINE_INTERNAL 9
+#define PE_FONT_FAILED 10
+
+
+LOCAL struct pe_error_message {
+ int error_code;
+ char *error_message;
+ } P_ErrorMessages[] = {
+ { PE_DIALOG_FAILED, "Print Dialog Failed"},
+ { PE_USER_CANCEL, "User canceled" },
+ { PE_CANT_START_DOC, "Can't start document" },
+ { PE_OUT_OF_MEMORY, "Out of memory" },
+ { PE_CANT_FINISH_DOC, "Can't finish document" },
+ { PE_OUT_OF_MEMORY, "Out of memory" },
+ { PE_OUT_OF_DISK, "Out of disk space" },
+ { PE_PRINTER_NOT_FOUND, "Printer not found" },
+ { PE_PINE_INTERNAL, "Pine internal error" },
+ { PE_FONT_FAILED, "Failed to create font" },
+ { 0, NULL }};
+
+
+
+/*
+ * Send text in the line buffer to the printer.
+ * Advance to next page if necessary.
+ */
+LOCAL int
+_print_send_line (void)
+{
+ int status;
+
+ status = 0;
+ if (P_CurCol > 0)
+ TextOut (P_PrintDC, P_LeftOffset,
+ P_TopOffset + (P_CurRow * P_RowHeight),
+ P_LineText, P_CurCol);
+ P_CurCol = 0;
+ if (++P_CurRow >= P_PageRows)
+ status = _print_send_page ();
+
+ return (status);
+}
+
+
+
+/*
+ * Advance one page.
+ */
+int
+_print_send_page (void)
+{
+ DWORD status;
+
+ if((status = EndPage (P_PrintDC)) > 0){
+ P_CurRow = 0;
+ if((status = StartPage (P_PrintDC)) > 0){
+ SelectObject (P_PrintDC, P_hFont);
+ return(0);
+ }
+ }
+
+#ifdef WIN32
+ ExplainSystemErr();
+ return(PE_GENERAL_ERROR);
+#else
+ switch (status) {
+ case SP_USERABORT: return (PE_USER_CANCEL);
+ case SP_OUTOFDISK: return (PE_OUT_OF_DISK);
+ case SP_OUTOFMEMORY: return (PE_OUT_OF_MEMORY);
+ default:
+ case SP_ERROR: break;
+ }
+#endif
+ return (PE_GENERAL_ERROR);
+}
+
+
+
+/*
+ * Map errors reported to my own error set.
+ */
+int
+_print_map_dlg_error (DWORD error)
+{
+ switch (error) {
+ case 0: return (PE_USER_CANCEL);
+ case CDERR_MEMALLOCFAILURE:
+ case CDERR_MEMLOCKFAILURE:
+ return (PE_OUT_OF_MEMORY);
+ case PDERR_PRINTERNOTFOUND:
+ case PDERR_NODEVICES:
+ return (PE_PRINTER_NOT_FOUND);
+ case CDERR_STRUCTSIZE:
+ return (PE_PINE_INTERNAL);
+ default:
+ return (PE_GENERAL_ERROR);
+ }
+}
+
+
+/*
+ * This is used for converting from UTF-8 to UCS and is
+ * global so that mswin_print_ready can initialize it.
+ */
+static CBUF_S print_cb;
+
+/*
+ * Get the printer ready. Returns ZERO for success, or an error code that
+ * can be passed to mswin_print_error() to get a text message.
+ */
+int
+mswin_print_ready(WINHAND hWnd, LPTSTR docDesc)
+{
+ PRINTDLG pd;
+ DOCINFO di;
+ TEXTMETRIC tm;
+ HDC hDC;
+ int fontSize; /* Size in Points. */
+ int ppi; /* Pixels per inch in device. */
+ int xChar;
+ int status;
+ HFONT oldFont;
+ LOGFONT newFont;
+
+
+ status = 0;
+ P_PrintDC = NULL;
+
+ print_cb.cbufp = print_cb.cbuf;
+
+ /*
+ * Show print dialog.
+ */
+ memset(&pd, 0, sizeof(PRINTDLG));
+ pd.lStructSize = sizeof (PRINTDLG);
+ pd.hwndOwner = (hWnd ? (HWND) hWnd : ghTTYWnd);
+ pd.hDevMode = NULL;
+ pd.hDevNames = NULL;
+ pd.Flags = PD_ALLPAGES | PD_NOSELECTION | PD_NOPAGENUMS |
+ PD_HIDEPRINTTOFILE | PD_RETURNDC;
+ pd.nCopies = 1;
+ if(PrintDlg (&pd) == 0)
+ return(_print_map_dlg_error (CommDlgExtendedError()));
+
+ /*
+ * Returns the device name which we could use to remember what printer
+ * they selected. But instead, we just toss them.
+ */
+ if (pd.hDevNames)
+ GlobalFree (pd.hDevNames);
+ if (pd.hDevMode)
+ GlobalFree (pd.hDevMode);
+
+ /*
+ * Get the device drawing context.
+ * (does PringDlg() ever return success but fail to return a DC?)
+ */
+ if (pd.hDC != NULL)
+ P_PrintDC = pd.hDC;
+ else {
+ status = PE_DIALOG_FAILED;
+ goto Done;
+ }
+
+ /*
+ * Start Document
+ */
+ memset(&di, 0, sizeof (DOCINFO));
+ di.cbSize = sizeof (DOCINFO);
+ di.lpszDocName = docDesc; /* This appears in the print manager*/
+ di.lpszOutput = NULL; /* Could suply a file name to print
+ to. */
+ if(StartDoc(P_PrintDC, &di) <= 0) {
+ ExplainSystemErr();
+ status = PE_CANT_START_DOC;
+ DeleteDC (P_PrintDC);
+ P_PrintDC = NULL;
+ goto Done;
+ }
+
+ /*
+ * Printer font is either same as window font, or is it's own
+ * font.
+ */
+ if (gPrintFontSameAs) {
+
+ /*
+ * Get the current font size in points, then create a new font
+ * of same size for printer. Do the calculation using the actual
+ * screen resolution instead of the logical resolution so that
+ * we get pretty close to the same font size on the printer
+ * as we see on the screen.
+ */
+ hDC = GetDC (ghTTYWnd); /* Temp screen DC. */
+ ppi = (int) ((float)GetDeviceCaps (hDC, VERTRES) /
+ ((float) GetDeviceCaps (hDC, VERTSIZE) / 25.3636));
+#ifdef FDEBUG
+ if (mswin_debug >= 8) {
+ fprintf (mswin_debugfile, "mswin_print_ready: Screen res %d ppi, font height %d pixels\n",
+ ppi, -gpTTYInfo->lfTTYFont.lfHeight);
+ fprintf (mswin_debugfile, " Screen height %d pixel, %d mm\n",
+ GetDeviceCaps (hDC, VERTRES), GetDeviceCaps (hDC, VERTSIZE));
+ }
+#endif
+ ReleaseDC (ghTTYWnd, hDC);
+
+ /* Convert from screen pixels to points. */
+ fontSize = MulDiv (-gpTTYInfo->lfTTYFont.lfHeight, 72, ppi);
+ ++fontSize; /* Fudge a little. */
+
+
+ /* Get printer resolution and convert form points to printer pixels. */
+ ppi = GetDeviceCaps (P_PrintDC, LOGPIXELSY);
+ newFont.lfHeight = -MulDiv (fontSize, ppi, 72);
+ _tcsncpy(newFont.lfFaceName, gpTTYInfo->lfTTYFont.lfFaceName, LF_FACESIZE);
+ newFont.lfFaceName[LF_FACESIZE-1] = 0;
+ newFont.lfItalic = gpTTYInfo->lfTTYFont.lfItalic;
+ newFont.lfWeight = gpTTYInfo->lfTTYFont.lfWeight;
+ newFont.lfCharSet = gpTTYInfo->lfTTYFont.lfCharSet;
+
+
+#ifdef FDEBUG
+ if (mswin_debug >= 8) {
+ fprintf (mswin_debugfile, " font Size %d points\n",
+ fontSize);
+ fprintf (mswin_debugfile, " printer res %d ppi, font height %d pixels\n",
+ ppi, -newFont.lfHeight);
+ fprintf (mswin_debugfile, " paper height %d pixel, %d mm\n",
+ GetDeviceCaps (P_PrintDC, VERTRES),
+ GetDeviceCaps (P_PrintDC, VERTSIZE));
+ }
+#endif
+ }
+ else {
+ ppi = GetDeviceCaps (P_PrintDC, LOGPIXELSY);
+ newFont.lfHeight = -MulDiv (gPrintFontSize, ppi, 72);
+ _tcsncpy(newFont.lfFaceName, gPrintFontName, LF_FACESIZE);
+ newFont.lfFaceName[LF_FACESIZE-1] = 0;
+ newFont.lfWeight = 0;
+ if(_tcsstr(gPrintFontStyle, TEXT("bold")))
+ newFont.lfWeight = FW_BOLD;
+
+ newFont.lfItalic = 0;
+ if(_tcsstr(gPrintFontStyle, TEXT("italic")))
+ newFont.lfItalic = 1;
+
+ newFont.lfCharSet = mswin_string2charsetid(gPrintFontCharSet);
+ }
+
+
+ /* Fill out rest of font description and request font. */
+ newFont.lfWidth = 0;
+ newFont.lfEscapement = 0;
+ newFont.lfOrientation = 0;
+ newFont.lfUnderline = 0;
+ newFont.lfStrikeOut = 0;
+ newFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
+ newFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ newFont.lfQuality = DEFAULT_QUALITY;
+ newFont.lfPitchAndFamily = FIXED_PITCH;
+ P_hFont = CreateFontIndirect (&newFont);
+ if (P_hFont == NULL) {
+ status = PE_FONT_FAILED;
+ DeleteDC (P_PrintDC);
+ goto Done;
+ }
+
+
+ /*
+ * Start page.
+ * Must select font for each page or it returns to default.
+ * Windows seems good about maping selected font to a font that
+ * will actually print on the printer.
+ */
+ StartPage (P_PrintDC);
+ oldFont = SelectObject (P_PrintDC, P_hFont);
+
+
+ /*
+ * Find out about the font we got and set up page size and margins.
+ * This assumes all pages are the same size - which seems reasonable.
+ */
+ GetTextMetrics (P_PrintDC, &tm);
+ xChar = tm.tmAveCharWidth;
+ P_RowHeight = tm.tmHeight + tm.tmExternalLeading;
+
+ /* HORZRES and VERTRES report size of page in printer pixels. */
+ P_PageColumns = GetDeviceCaps (P_PrintDC, HORZRES) / xChar;
+ P_PageRows = GetDeviceCaps (P_PrintDC, VERTRES) / P_RowHeight;
+
+ /* We allow a margin at top and bottom measured in text rows. */
+ P_PageRows -= VERTICLE_MARGIN * 2;
+ P_TopOffset = VERTICLE_MARGIN * P_RowHeight;
+
+ /* And allow for a left and right margine measured in characters. */
+ P_PageColumns -= HORIZONTAL_MARGIN * 2;
+ P_LeftOffset = HORIZONTAL_MARGIN * xChar;
+
+ P_CurRow = 0; /* Start counting at row 0. */
+ P_CurCol = 0; /* At character 0. */
+ P_LineText = (LPTSTR) MemAlloc((P_PageColumns + 1) * sizeof(TCHAR));
+ if(P_LineText == NULL){
+ if(EndDoc (P_PrintDC) <= 0)
+ ExplainSystemErr();
+
+ DeleteObject (P_hFont);
+ P_hFont = NULL;
+ DeleteDC (P_PrintDC);
+ P_PrintDC = NULL;
+ status = PE_OUT_OF_MEMORY;
+ goto Done;
+ }
+
+
+Done:
+ return (status);
+}
+
+
+/*
+ * Called when printing is done.
+ * xxx what happens if there is an error? Will this get called?
+ */
+int
+mswin_print_done(void)
+{
+ if(P_PrintDC != NULL){
+ if(P_LineText != NULL)
+ MemFree((void *) P_LineText);
+
+ if(EndPage (P_PrintDC) <= 0 || EndDoc (P_PrintDC) <= 0)
+ ExplainSystemErr();
+
+ DeleteObject(P_hFont);
+ P_hFont = NULL;
+ DeleteDC(P_PrintDC);
+ P_PrintDC = NULL;
+ }
+
+ return (0);
+}
+
+
+/*
+ * Return ponter to a text string that describes the erorr.
+ */
+char *
+mswin_print_error(int error_code)
+{
+ int i;
+
+ for(i = 0; P_ErrorMessages[i].error_message != NULL; ++i)
+ if(P_ErrorMessages[i].error_code == error_code)
+ return(P_ErrorMessages[i].error_message);
+
+ return("(Unknown error)");
+}
+
+
+/*
+ * Add a single character to the current line.
+ * Only handles CRLF carrage control.
+ */
+int
+mswin_print_char(TCHAR c)
+{
+ switch(c){
+ case ASCII_CR:
+ return(0);
+
+ case ASCII_LF:
+ return(_print_send_line());
+
+ case ASCII_TAB:
+ do{
+ if(P_CurCol == P_PageColumns)
+ return(_print_send_line());
+
+ *(P_LineText + P_CurCol++) = ' ';
+ }
+ while(P_CurCol % PRINT_TAB_SIZE != 0);
+ return(0);
+
+ default:
+ if(P_CurCol == P_PageColumns){
+ int status;
+
+ if((status = _print_send_line()) != 0)
+ return(status);
+ }
+
+ *(P_LineText + P_CurCol++) = c;
+ return(0);
+ }
+}
+
+
+int
+mswin_print_char_utf8(int c)
+{
+ UCS ucs;
+ TCHAR tc;
+ int ret = 0;
+
+ if(utf8_to_ucs4_oneatatime(c, &print_cb, &ucs, NULL)){
+ /* bogus conversion ignores UTF-16 */
+ tc = (TCHAR) ucs;
+ ret = mswin_print_char(tc);
+ }
+
+ return(ret);
+}
+
+
+/*
+ * Send a string to the printer.
+ */
+int
+mswin_print_text(LPTSTR text)
+{
+ int status;
+
+ if(text)
+ while(*text)
+ if((status = mswin_print_char(*(text++))) != 0)
+ return(status);
+
+ return(0);
+}
+
+
+int
+mswin_print_text_utf8(char *text_utf8)
+{
+ LPTSTR text_lpt;
+ int ret = 0;
+
+ if(text_utf8){
+ text_lpt = utf8_to_lptstr(text_utf8);
+ if(text_lpt){
+ ret = mswin_print_text(text_lpt);
+ fs_give((void **) &text_lpt);
+ }
+ }
+
+ return(ret);
+}
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * File dialog boxes.
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+LOCAL TCHAR gHomeDir[PATH_MAX];
+LOCAL TCHAR gLastDir[PATH_MAX];
+
+/*
+ * Keep track of the last dir visited. Most of the time pine just passes us
+ * the "home directory", which usually is not where the user wants to start.
+ * Assume that the first time we are called we are being passed the home
+ * direcory.
+ */
+static void
+FillInitialDir (LPCTSTR *iDir, LPTSTR targDir)
+{
+ if (_tcslen (gHomeDir) == 0) {
+ _tcscpy (gHomeDir, targDir);
+ *iDir = targDir;
+ }
+ else if (_tcscmp (gHomeDir, targDir) == 0 && *gLastDir)
+ *iDir = gLastDir;
+ else
+ *iDir = targDir;
+}
+
+
+
+/*
+ * Display a save file dialog box.
+ *
+ * dir_utf8 > directory to start search in
+ * < directory finished in.
+ * fName_utf8 < Name of file selected
+ * nMaxDName length of dir_utf8
+ * nMaxFName length of fName_utf8.
+ *
+ * Possible return values:
+ * 0 no file selected
+ * 1 file selected
+ * -1 some sort of error
+ */
+int
+mswin_savefile(char *dir_utf8, int nMaxDName, char *fName_utf8, int nMaxFName)
+{
+ OPENFILENAME ofn;
+ TCHAR filters[128], moniker[128];
+ DWORD rc, len;
+ LPTSTR p, extlist_lpt;
+ LPTSTR fName_lpt, f, dir_lpt;
+ char *cp;
+
+ /* convert fName_utf8 to LPTSTR */
+ fName_lpt = (LPTSTR) fs_get(nMaxFName * sizeof(TCHAR));
+ fName_lpt[0] = '\0';
+ if(fName_utf8 && fName_utf8[0]){
+ f = utf8_to_lptstr(fName_utf8);
+ if(f){
+ _tcsncpy(fName_lpt, f, nMaxFName);
+ fName_lpt[nMaxFName-1] = '\0';
+ fs_give((void **) &f);
+ }
+ }
+
+ dir_lpt = (LPTSTR) fs_get(nMaxDName * sizeof(TCHAR));
+ dir_lpt[0] = '\0';
+ if(dir_utf8 && dir_utf8[0]){
+ f = utf8_to_lptstr(dir_utf8);
+ if(f){
+ _tcsncpy(dir_lpt, f, nMaxDName);
+ dir_lpt[nMaxDName-1] = '\0';
+ fs_give((void **) &f);
+ }
+ }
+
+ for(extlist_lpt = NULL, p = _tcschr(fName_lpt, '.'); p; p = _tcschr(++p, '.'))
+ extlist_lpt = p;
+
+ len = sizeof(moniker)/sizeof(TCHAR);
+ if(extlist_lpt && MSWRPeek(HKEY_CLASSES_ROOT, extlist_lpt, NULL, moniker, &len) == TRUE){
+ len = sizeof(filters)/sizeof(TCHAR);
+ filters[0] = '\0';
+ if(MSWRPeek(HKEY_CLASSES_ROOT, moniker, NULL, filters, &len) == TRUE)
+ _sntprintf(filters + _tcslen(filters),
+ sizeof(filters)/sizeof(TCHAR) - _tcslen(filters),
+ TEXT(" (*%s)#*%s#"), extlist_lpt, extlist_lpt);
+ else
+ _sntprintf(filters, sizeof(filters)/sizeof(TCHAR),
+ TEXT("%s (*%s)#*%s#"), moniker, extlist_lpt, extlist_lpt);
+ }
+ else
+ filters[0] = '\0';
+
+ _tcsncat(filters, TEXT("Text Files (*.txt)#*.txt#All Files (*.*)#*.*#"),
+ sizeof(filters)/sizeof(TCHAR));
+ filters[sizeof(filters)/sizeof(TCHAR) - 1] = '\0';
+
+ for(p = filters; *p != '\0'; ++p)
+ if(*p == L'#')
+ *p = '\0';
+
+ /* Set up the BIG STRUCTURE. */
+ memset (&ofn, 0, sizeof(ofn));
+ /*
+ * sizeof(OPENFILENAME) used to work but doesn't work now with
+ * pre-Windows 2000. This is supposed to be the magic constant to
+ * make it work in both cases, according to MSDN.
+ */
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ ofn.hwndOwner = ghTTYWnd;
+ ofn.lpstrFilter = filters;
+ ofn.lpstrCustomFilter = NULL;
+ ofn.nFilterIndex = 1;
+ ofn.lpstrFile = fName_lpt;
+ ofn.nMaxFile = nMaxFName;
+ ofn.lpstrFileTitle = NULL;
+ ofn.nMaxFileTitle = 0;
+ FillInitialDir (&ofn.lpstrInitialDir, dir_lpt);
+ ofn.lpstrTitle = TEXT("Save To File");
+ ofn.Flags = OFN_NOREADONLYRETURN | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
+ ofn.lpstrDefExt = TEXT("txt");
+
+ if(GetSaveFileName(&ofn)){
+ if(ofn.nFileOffset > nMaxDName-1){
+ if(fName_lpt)
+ fs_give((void **) &fName_lpt);
+
+ if(dir_lpt)
+ fs_give((void **) &dir_lpt);
+
+ return(0);
+ }
+
+ /* Copy directory name to dir_lpt. */
+ _tcsncpy(dir_lpt, fName_lpt, nMaxDName-1);
+ dir_lpt[nMaxDName-1] = '\0';
+ if(dir_lpt[ofn.nFileOffset-1] == '\\'
+ && !(ofn.nFileOffset == 3 && _istalpha(dir_lpt[0]) && dir_lpt[1] == ':'))
+ dir_lpt[ofn.nFileOffset-1] = '\0';
+ else
+ dir_lpt[ofn.nFileOffset] = '\0';
+
+ /* Remember last dir visited. */
+ _tcsncpy(gLastDir, dir_lpt, PATH_MAX);
+ gLastDir[PATH_MAX-1] = '\0';
+
+ /* convert back to UTF-8 */
+ cp = lptstr_to_utf8(dir_lpt);
+ if(cp){
+ strncpy(dir_utf8, cp, nMaxDName-1);
+ dir_utf8[nMaxDName-1] = '\0';
+ fs_give((void **) &cp);
+ }
+
+ p = fName_lpt + ofn.nFileOffset;
+
+ /* convert fName back to UTF-8 */
+ cp = lptstr_to_utf8(p);
+ if(cp){
+ strncpy(fName_utf8, cp, nMaxFName-1);
+ fName_utf8[nMaxFName-1] = '\0';
+ fs_give((void **) &cp);
+ }
+
+ if(fName_lpt)
+ fs_give((void **) &fName_lpt);
+
+ if(dir_lpt)
+ fs_give((void **) &dir_lpt);
+
+ return(1);
+ }
+ else{
+ if(fName_lpt)
+ fs_give((void **) &fName_lpt);
+
+ if(dir_lpt)
+ fs_give((void **) &dir_lpt);
+
+ rc = CommDlgExtendedError();
+ return(rc ? -1 : 0);
+ }
+}
+
+
+
+
+/*
+ * Display an open file dialog box.
+ *
+ * dir_utf8 > directory to start search in
+ * < directory finished in.
+ * fName_utf8 < Name of file selected
+ * nMaxDName length of dir_utf8
+ * nMaxFName length of fName_utf8.
+ *
+ * Possible return values:
+ * 0 no file selected
+ * 1 file selected
+ * -1 some sort of error
+ */
+int
+mswin_openfile(char *dir_utf8, int nMaxDName, char *fName_utf8,
+ int nMaxFName, char *extlist_utf8)
+{
+ OPENFILENAME ofn;
+ TCHAR filters[1024];
+ DWORD rc;
+ LPTSTR p;
+ LPTSTR extlist_lpt = NULL;
+ LPTSTR fName_lpt, f, dir_lpt;
+ char *cp;
+
+
+ if(extlist_utf8)
+ extlist_lpt = utf8_to_lptstr(extlist_utf8);
+
+ /*
+ * Set filters array. (pairs of null terminated strings, terminated
+ * by a double null).
+ */
+ _sntprintf(filters, sizeof(filters)/sizeof(TCHAR),
+ TEXT("%s%sAll Files (*.*)#*.*#"),
+ extlist_lpt ? extlist_lpt : TEXT(""), extlist_lpt ? TEXT("#") : TEXT(""));
+
+ if(extlist_lpt)
+ fs_give((void **) &extlist_lpt);
+
+ for(p = filters; *p != '\0'; ++p)
+ if(*p == L'#')
+ *p = '\0';
+
+ /* Set up the BIG STRUCTURE. */
+ memset(&ofn, 0, sizeof(ofn));
+
+ /* convert fName_utf8 to LPTSTR */
+ fName_lpt = (LPTSTR) fs_get(nMaxFName * sizeof(TCHAR));
+ fName_lpt[0] = '\0';
+ if(fName_utf8 && fName_utf8[0]){
+ f = utf8_to_lptstr(fName_utf8);
+ if(f){
+ _tcsncpy(fName_lpt, f, nMaxFName);
+ fName_lpt[nMaxFName-1] = '\0';
+ fs_give((void **) &f);
+ }
+ }
+
+ dir_lpt = (LPTSTR) fs_get(nMaxDName * sizeof(TCHAR));
+ dir_lpt[0] = '\0';
+ if(dir_utf8 && dir_utf8[0]){
+ f = utf8_to_lptstr(dir_utf8);
+ if(f){
+ _tcsncpy(dir_lpt, f, nMaxDName);
+ dir_lpt[nMaxDName-1] = '\0';
+ fs_give((void **) &f);
+ }
+ }
+
+ /*
+ * sizeof(OPENFILENAME) used to work but doesn't work now with
+ * pre-Windows 2000. This is supposed to be the magic constant to
+ * make it work in both cases, according to MSDN.
+ */
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ ofn.hwndOwner = ghTTYWnd;
+ ofn.lpstrFilter = filters;
+ ofn.lpstrCustomFilter = NULL;
+ ofn.nFilterIndex = 1;
+ ofn.lpstrFile = fName_lpt;
+ ofn.nMaxFile = nMaxFName;
+ ofn.lpstrFileTitle = NULL;
+ ofn.nMaxFileTitle = 0;
+ FillInitialDir(&ofn.lpstrInitialDir, dir_lpt);
+ ofn.lpstrTitle = TEXT("Select File");
+ ofn.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST;
+ ofn.lpstrDefExt = TEXT("txt");
+
+ if(GetOpenFileName(&ofn)){
+ if(ofn.nFileOffset > nMaxDName-1){
+ if(fName_lpt)
+ fs_give((void **) &fName_lpt);
+
+ if(dir_lpt)
+ fs_give((void **) &dir_lpt);
+
+ return(0);
+ }
+
+ /* Copy directory name to dir_lpt. */
+ _tcsncpy(dir_lpt, fName_lpt, nMaxDName-1);
+ dir_lpt[nMaxDName-1] = '\0';
+ if(dir_lpt[ofn.nFileOffset-1] == '\\'
+ && !(ofn.nFileOffset == 3 && _istalpha(dir_lpt[0]) && dir_lpt[1] == ':'))
+ dir_lpt[ofn.nFileOffset-1] = '\0';
+ else
+ dir_lpt[ofn.nFileOffset] = '\0';
+
+ /* Remember last dir visited. */
+ _tcsncpy(gLastDir, dir_lpt, PATH_MAX);
+ gLastDir[PATH_MAX-1] = '\0';
+
+ /* convert back to UTF-8 */
+ cp = lptstr_to_utf8(dir_lpt);
+ if(cp){
+ strncpy(dir_utf8, cp, nMaxDName-1);
+ dir_utf8[nMaxDName-1] = '\0';
+ fs_give((void **) &cp);
+ }
+
+ p = fName_lpt + ofn.nFileOffset;
+
+ /* convert fName back to UTF-8 */
+ cp = lptstr_to_utf8(p);
+ if(cp){
+ strncpy(fName_utf8, cp, nMaxFName-1);
+ fName_utf8[nMaxFName-1] = '\0';
+ fs_give((void **) &cp);
+ }
+
+ if(fName_lpt)
+ fs_give((void **) &fName_lpt);
+
+ if(dir_lpt)
+ fs_give((void **) &dir_lpt);
+
+ return(1);
+ }
+ else{
+ if(fName_lpt)
+ fs_give((void **) &fName_lpt);
+
+ if(dir_lpt)
+ fs_give((void **) &dir_lpt);
+
+ rc = CommDlgExtendedError();
+ return(rc ? -1 : 0);
+ }
+}
+
+
+/*
+ * Display an open file dialog box.
+ * Allow selection of multiple files.
+ *
+ * dir_utf8 > directory to start search in
+ * < directory finished in.
+ * fName_utf8 < Names of files selected
+ * nMaxDName length of dir_utf8
+ * nMaxFName length of fName_utf8.
+ *
+ * Possible return values:
+ * 0 no file selected
+ * 1 file selected
+ * -1 some sort of error
+ */
+int
+mswin_multopenfile(char *dir_utf8, int nMaxDName, char *fName_utf8,
+ int nMaxFName, char *extlist_utf8)
+{
+ OPENFILENAME ofn;
+ TCHAR filters[1024];
+ DWORD rc;
+ LPTSTR p;
+ LPTSTR extlist_lpt = NULL;
+ LPTSTR fName_lpt, f, dir_lpt;
+ char *cp, *q;
+
+
+ if(extlist_utf8)
+ extlist_lpt = utf8_to_lptstr(extlist_utf8);
+
+ /*
+ * Set filters array. (pairs of null terminated strings, terminated
+ * by a double null).
+ */
+ _sntprintf(filters, sizeof(filters)/sizeof(TCHAR),
+ TEXT("%s%sAll Files (*.*)#*.*#"),
+ extlist_lpt ? extlist_lpt : TEXT(""), extlist_lpt ? TEXT("#") : TEXT(""));
+
+ if(extlist_lpt)
+ fs_give((void **) &extlist_lpt);
+
+ for(p = filters; *p != '\0'; ++p)
+ if(*p == L'#')
+ *p = '\0';
+
+ /* Set up the BIG STRUCTURE. */
+ memset (&ofn, 0, sizeof(ofn));
+
+ /* convert fName_utf8 to LPTSTR */
+ fName_lpt = (LPTSTR) fs_get(nMaxFName * sizeof(TCHAR));
+ memset(fName_lpt, 0, nMaxFName * sizeof(TCHAR));
+ if(fName_utf8 && fName_utf8[0]){
+ f = utf8_to_lptstr(fName_utf8);
+ if(f){
+ _tcsncpy(fName_lpt, f, nMaxFName);
+ fName_lpt[nMaxFName-1] = '\0';
+ fs_give((void **) &f);
+ }
+ }
+
+ dir_lpt = (LPTSTR) fs_get(nMaxDName * sizeof(TCHAR));
+ memset(dir_lpt, 0, nMaxDName * sizeof(TCHAR));
+ if(dir_utf8 && dir_utf8[0]){
+ f = utf8_to_lptstr(dir_utf8);
+ if(f){
+ _tcsncpy(dir_lpt, f, nMaxDName);
+ dir_lpt[nMaxDName-1] = '\0';
+ fs_give((void **) &f);
+ }
+ }
+
+ /*
+ * sizeof(OPENFILENAME) used to work but doesn't work now with
+ * pre-Windows 2000. This is supposed to be the magic constant to
+ * make it work in both cases, according to MSDN.
+ */
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ ofn.hwndOwner = ghTTYWnd;
+ ofn.lpstrFilter = filters;
+ ofn.lpstrCustomFilter = NULL;
+ ofn.nFilterIndex = 1;
+ ofn.lpstrFile = fName_lpt;
+ ofn.nMaxFile = nMaxFName;
+ ofn.lpstrFileTitle = NULL;
+ ofn.nMaxFileTitle = 0;
+ FillInitialDir(&ofn.lpstrInitialDir, dir_lpt);
+ ofn.lpstrTitle = TEXT("Select Files");
+ ofn.Flags = OFN_ALLOWMULTISELECT | OFN_EXPLORER |
+ OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST;
+ ofn.lpstrDefExt = TEXT("txt");
+
+ if(GetOpenFileName(&ofn)){
+ if(ofn.nFileOffset > nMaxDName-1){
+ if(fName_lpt)
+ fs_give((void **) &fName_lpt);
+
+ if(dir_lpt)
+ fs_give((void **) &dir_lpt);
+
+ return(0);
+ }
+
+ /* Copy directory name to dir_lpt. */
+ _tcsncpy(dir_lpt, fName_lpt, nMaxDName-1);
+ dir_lpt[nMaxDName-1] = '\0';
+ if(dir_lpt[ofn.nFileOffset-1] == '\\'
+ && !(ofn.nFileOffset == 3 && _istalpha(dir_lpt[0]) && dir_lpt[1] == ':'))
+ dir_lpt[ofn.nFileOffset-1] = '\0';
+ else
+ dir_lpt[ofn.nFileOffset] = '\0';
+
+ /* Remember last dir visited. */
+ _tcsncpy(gLastDir, dir_lpt, PATH_MAX);
+ gLastDir[PATH_MAX-1] = '\0';
+
+ /* convert back to UTF-8 */
+ cp = lptstr_to_utf8(dir_lpt);
+ if(cp){
+ strncpy(dir_utf8, cp, nMaxDName-1);
+ dir_utf8[nMaxDName-1] = '\0';
+ fs_give((void **) &cp);
+ }
+
+ /*
+ * The file names are all in the same directory and are separated
+ * by '\0' characters and terminated by double '\0'.
+ * This fact depends on the OFN_EXPLORER bit being set in the flags
+ * above.
+ *
+ * This is complicated because we need to convert all of these file
+ * names to UTF-8.
+ */
+ for(q=fName_utf8, p=fName_lpt + ofn.nFileOffset; *p; p += _tcslen(p)+1){
+ cp = lptstr_to_utf8(p);
+ if(cp){
+ sstrncpy(&q, cp, (int)(nMaxFName-(q-fName_utf8)));
+ if(q-fName_utf8 < nMaxFName){
+ *q++ = '\0';
+ if(q-fName_utf8 < nMaxFName)
+ *q = '\0'; /* the double null if this is the end */
+ }
+ else
+ fName_utf8[nMaxFName-1] = '\0';
+
+ fs_give((void **) &cp);
+ }
+ }
+
+ fName_utf8[nMaxFName-1] = fName_utf8[nMaxFName-2] = '\0';
+
+ if(fName_lpt)
+ fs_give((void **) &fName_lpt);
+
+ if(dir_lpt)
+ fs_give((void **) &dir_lpt);
+
+ return (1);
+ }
+ else{
+ if(fName_lpt)
+ fs_give((void **) &fName_lpt);
+
+ if(dir_lpt)
+ fs_give((void **) &dir_lpt);
+
+ rc = CommDlgExtendedError();
+ return(rc ? -1 : 0);
+ }
+}
+
+
+/*---------------------------------------------------------------------------
+ */
+
+/*
+ * pico_XXcolor() - each function sets a particular attribute
+ */
+void
+pico_nfcolor(char *s)
+{
+ char cbuf[MAXCLEN];
+
+ if(s){
+ SetColorAttribute (&gpTTYInfo->rgbFGColor, s);
+ pico_set_nfg_color();
+
+ if(the_normal_color){
+ strncpy(the_normal_color->fg,
+ ConvertStringRGB(cbuf, sizeof(cbuf), gpTTYInfo->rgbFGColor),
+ MAXCOLORLEN+1);
+ the_normal_color->fg[MAXCOLORLEN] = '\0';
+ }
+ }
+ else{
+ gpTTYInfo->rgbFGColor = GetSysColor (COLOR_WINDOWTEXT);
+ if(the_normal_color)
+ free_color_pair(&the_normal_color);
+ }
+
+ // Update all textwindows with the new FG color.
+ mswin_tw_setcolor((MSWIN_TEXTWINDOW *)-1,
+ gpTTYInfo->rgbFGColor, gpTTYInfo->rgbBGColor);
+}
+
+
+void
+pico_nbcolor(char *s)
+{
+ char cbuf[MAXCLEN];
+
+ if(s){
+ SetColorAttribute (&gpTTYInfo->rgbBGColor, s);
+ pico_set_nbg_color();
+
+ if(the_normal_color){
+ strncpy(the_normal_color->bg,
+ ConvertStringRGB(cbuf, sizeof(cbuf), gpTTYInfo->rgbBGColor),
+ MAXCOLORLEN+1);
+ the_normal_color->fg[MAXCOLORLEN] = '\0';
+ }
+ }
+ else{
+ gpTTYInfo->rgbBGColor = GetSysColor (COLOR_WINDOW);
+ if(the_normal_color)
+ free_color_pair(&the_normal_color);
+ }
+
+ // Update all textwindows with the new BG color.
+ mswin_tw_setcolor((MSWIN_TEXTWINDOW *)-1,
+ gpTTYInfo->rgbFGColor, gpTTYInfo->rgbBGColor);
+}
+
+
+void
+pico_rfcolor(char *s)
+{
+ char cbuf[MAXCLEN];
+
+ if(s){
+ SetColorAttribute (&gpTTYInfo->rgbRFGColor, s);
+
+ if(the_rev_color){
+ strncpy(the_rev_color->fg,
+ ConvertStringRGB(cbuf, sizeof(cbuf), gpTTYInfo->rgbRFGColor),
+ MAXCOLORLEN+1);
+ the_rev_color->fg[MAXCOLORLEN] = '\0';
+ }
+ }
+ else{
+ gpTTYInfo->rgbRFGColor = GetSysColor (COLOR_HIGHLIGHTTEXT);
+ if(the_rev_color)
+ free_color_pair(&the_rev_color);
+ }
+}
+
+
+void
+pico_rbcolor(char *s)
+{
+ char cbuf[MAXCLEN];
+
+ if(s){
+ SetColorAttribute (&gpTTYInfo->rgbRBGColor, s);
+
+ if(the_rev_color){
+ strncpy(the_rev_color->bg,
+ ConvertStringRGB(cbuf, sizeof(cbuf), gpTTYInfo->rgbRBGColor),
+ MAXCOLORLEN+1);
+ the_rev_color->bg[MAXCOLORLEN] = '\0';
+ }
+ }
+ else{
+ gpTTYInfo->rgbRBGColor = GetSysColor (COLOR_HIGHLIGHT);
+ if(the_rev_color)
+ free_color_pair(&the_rev_color);
+ }
+}
+
+
+int
+pico_usingcolor()
+{
+ return(TRUE);
+}
+
+
+int
+pico_count_in_color_table()
+{
+ return(visibleColorTableSize);
+}
+
+
+/*
+ * Return a pointer to an rgb string for the input color. The output is 11
+ * characters long and looks like rrr,ggg,bbb.
+ *
+ * Args colorName -- The color to convert to ascii rgb.
+ *
+ * Returns Pointer to a static buffer containing the rgb string.
+ */
+char *
+color_to_asciirgb(char *colorName)
+{
+ static char c_to_a_buf[3][RGBLEN+1];
+ static int whichbuf = 0;
+ COLORREF cf;
+ int l;
+
+ whichbuf = (whichbuf + 1) % 3;
+
+ if(ConvertRGBString(colorName, &cf)){
+ snprintf(c_to_a_buf[whichbuf], sizeof(c_to_a_buf[0]), "%.3d,%.3d,%.3d",
+ GetRValue(cf), GetGValue(cf), GetBValue(cf));
+ }
+ else{
+ /*
+ * If we didn't find the color it could be that it is the
+ * normal color (MATCH_NORM_COLOR) or the none color
+ * (MATCH_NONE_COLOR). If that is the case, this strncpy thing
+ * will work out correctly because those two strings are
+ * RGBLEN long. Otherwise we're in a bit of trouble. This
+ * most likely means that the user is using the same pinerc on
+ * two terminals, one with more colors than the other. We didn't
+ * find a match because this color isn't present on this terminal.
+ * Since the return value of this function is assumed to be
+ * RGBLEN long, we'd better make it that long.
+ * It still won't work correctly because colors will be screwed up,
+ * but at least the embedded colors in filter.c will get properly
+ * sucked up when they're encountered.
+ */
+ strncpy(c_to_a_buf[whichbuf], "xxxxxxxxxxx", RGBLEN);
+ l = (int)strlen(colorName);
+ strncpy(c_to_a_buf[whichbuf], colorName, (l < RGBLEN) ? l : RGBLEN);
+ c_to_a_buf[whichbuf][RGBLEN] = '\0';
+ }
+
+ return(c_to_a_buf[whichbuf]);
+}
+
+
+void
+pico_set_nfg_color()
+{
+ FlushWriteAccum ();
+ gpTTYInfo->curAttrib.rgbFG = gpTTYInfo->rgbFGColor;
+}
+
+void
+pico_set_nbg_color()
+{
+ FlushWriteAccum ();
+ gpTTYInfo->curAttrib.rgbBG = gpTTYInfo->rgbBGColor;
+}
+
+void
+pico_set_normal_color()
+{
+ pico_set_nfg_color();
+ pico_set_nbg_color();
+}
+
+COLOR_PAIR *
+pico_get_rev_color()
+{
+ char fgbuf[MAXCLEN], bgbuf[MAXCLEN];
+
+ if(!the_rev_color)
+ the_rev_color =
+ new_color_pair(ConvertStringRGB(fgbuf,sizeof(fgbuf),gpTTYInfo->rgbRFGColor),
+ ConvertStringRGB(bgbuf,sizeof(bgbuf),gpTTYInfo->rgbRBGColor));
+ return(the_rev_color);
+}
+
+COLOR_PAIR *
+pico_get_normal_color()
+{
+ char fgbuf[MAXCLEN], bgbuf[MAXCLEN];
+
+ if(!the_normal_color)
+ the_normal_color =
+ new_color_pair(ConvertStringRGB(fgbuf,sizeof(fgbuf),gpTTYInfo->rgbFGColor),
+ ConvertStringRGB(bgbuf,sizeof(bgbuf),gpTTYInfo->rgbBGColor));
+ return(the_normal_color);
+}
+
+/*
+ * Sets color to (fg,bg).
+ * Flags == PSC_NONE No alternate default if fg,bg fails.
+ * == PSC_NORM Set it to Normal color on failure.
+ * == PSC_REV Set it to Reverse color on failure.
+ *
+ * If flag PSC_RET is set, returns an allocated copy of the previous
+ * color pair, otherwise returns NULL.
+ */
+COLOR_PAIR *
+pico_set_colors(char *fg, char *bg, int flags)
+{
+ COLOR_PAIR *cp = NULL;
+
+ if(flags & PSC_RET)
+ cp = pico_get_cur_color();
+
+ if(!(fg && bg && pico_set_fg_color(fg) && pico_set_bg_color(bg))){
+
+ if(flags & PSC_NORM)
+ pico_set_normal_color();
+ else if(flags & PSC_REV)
+ SetReverseColor();
+ }
+
+ return(cp);
+}
+
+
+int
+pico_is_good_color(char *colorName)
+{
+ COLORREF cf;
+
+ if(!struncmp(colorName, MATCH_NORM_COLOR, RGBLEN)
+ || !struncmp(colorName, MATCH_NONE_COLOR, RGBLEN))
+ return(TRUE);
+
+ return(ConvertRGBString(colorName, &cf));
+}
+
+
+int
+pico_set_fg_color(char *colorName)
+{
+ char fgbuf[MAXCLEN];
+
+ FlushWriteAccum ();
+
+ if(!struncmp(colorName, MATCH_NORM_COLOR, RGBLEN)){
+ ConvertStringRGB(fgbuf,sizeof(fgbuf),gpTTYInfo->rgbFGColor);
+ colorName = fgbuf;
+ }
+ else if(!struncmp(colorName, MATCH_NONE_COLOR, RGBLEN))
+ return(TRUE);
+
+ return(ConvertRGBString(colorName, &gpTTYInfo->curAttrib.rgbFG));
+}
+
+
+int
+pico_set_bg_color(char *colorName)
+{
+ char bgbuf[MAXCLEN];
+
+ FlushWriteAccum ();
+
+ if(!struncmp(colorName, MATCH_NORM_COLOR, RGBLEN)){
+ ConvertStringRGB(bgbuf,sizeof(bgbuf),gpTTYInfo->rgbBGColor);
+ colorName = bgbuf;
+ }
+ else if(!struncmp(colorName, MATCH_NONE_COLOR, RGBLEN))
+ return(TRUE);
+
+ return(ConvertRGBString(colorName, &gpTTYInfo->curAttrib.rgbBG));
+}
+
+
+char *
+pico_get_last_fg_color()
+{
+ return(NULL);
+}
+
+
+char *
+pico_get_last_bg_color()
+{
+ return(NULL);
+}
+
+
+unsigned
+pico_get_color_options()
+{
+ return((unsigned)0);
+}
+
+
+void
+pico_set_color_options(unsigned int opts)
+{
+}
+
+
+COLOR_PAIR *
+pico_get_cur_color()
+{
+ char fgbuf[MAXCLEN], bgbuf[MAXCLEN];
+
+ return(new_color_pair(ConvertStringRGB(fgbuf,sizeof(fgbuf),gpTTYInfo->curAttrib.rgbFG),
+ ConvertStringRGB(bgbuf,sizeof(bgbuf),gpTTYInfo->curAttrib.rgbBG)));
+}
+
+
+char *
+mswin_rgbchoice(char *pOldRGB)
+{
+ CHOOSECOLOR cc;
+ static COLORREF custColors[16] = {
+ RGB(0,0,0),
+ RGB(0,0,255),
+ RGB(0,255,0),
+ RGB(0,255,255),
+ RGB(255,0,0),
+ RGB(255,0,255),
+ RGB(255,255,0),
+ RGB(255,255,255),
+ RGB(192,192,192),
+ RGB(128,128,128),
+ RGB(64,64,64)
+ };
+
+ memset(&cc, 0, sizeof(CHOOSECOLOR));
+
+ cc.lStructSize = sizeof(CHOOSECOLOR);
+ cc.hwndOwner = ghTTYWnd;
+ cc.Flags = CC_ANYCOLOR;
+ cc.lpCustColors = &custColors[0];
+
+ if(pOldRGB){
+ int i;
+
+ ConvertRGBString (pOldRGB, &cc.rgbResult);
+ cc.Flags |= CC_RGBINIT;
+
+ for(i = 0; i < 11 && custColors[i] != cc.rgbResult; i++)
+ ;
+
+ if(i == 11){
+ custColors[i] = cc.rgbResult;
+ cc.Flags |= CC_FULLOPEN;
+ }
+
+ }
+
+ if(ChooseColor(&cc)){
+ char rgbbuf[MAXCLEN], *p;
+
+ ConvertStringRGB(rgbbuf, sizeof(rgbbuf), cc.rgbResult);
+ if(p = MemAlloc(MAXCLEN * sizeof(char))){
+ strncpy(p, rgbbuf, MAXCLEN);
+ p[MAXCLEN-1] = '\0';
+ return(p);
+ }
+ }
+
+ return(NULL);
+}
+
+
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Signal and alarm functions
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+/*
+ * Provide a rough implementation of the SIGALRM and alarm functions
+ */
+
+
+
+#if 0
+/*
+ * Set a new handler for a signal.
+ */
+void (__cdecl * __cdecl signal (int sig,void (__cdecl *hndlr)(int)))(int)
+
+{
+ SignalType oldValue;
+
+ switch(sig) {
+ case SIGALRM :
+ oldValue = gSignalAlarm;
+ gSignalAlarm = hndlr;
+ break;
+
+ case SIGHUP :
+ oldValue = gSignalHUP;
+ gSignalHUP = hndlr;
+
+ default:
+ /* All other's are always ignored. */
+ oldValue = SIG_IGN;
+ break;
+ }
+
+ return (oldValue);
+}
+#endif
+
+
+/*
+ * Set the alarm expiration time (in seconds)
+ */
+int
+mswin_alarm (int seconds)
+{
+ int prevtime;
+
+ prevtime = gAlarmTimeout ? (gAlarmTimeout - (GetTickCount () / 1000)): 0;
+ gAlarmTimeout = seconds ? (GetTickCount() / 1000) + seconds : 0;
+ MyTimerSet ();
+ return (prevtime);
+}
+
+
+
+/*
+ * Deliver and clear the alarm.
+ */
+void
+AlarmDeliver ()
+{
+ if (gSignalAlarm != SIG_DFL && gSignalAlarm != SIG_IGN) {
+ /* Clear AlarmTimeout BEFORE calling handler. handler may call back
+ * to reset timeout. */
+ gAlarmTimeout = 0;
+ MyTimerSet ();
+ gSignalAlarm (SIGALRM);
+ }
+}
+
+
+
+void
+HUPDeliver ()
+{
+ if (gSignalHUP) {
+ gSignalHUP (SIGHUP);
+ exit (0);
+ }
+}
+
+
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Printer font selection menu
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+/*
+ * Set the print font to be the same as the window font.
+ * Toggle setting.
+ */
+void
+PrintFontSameAs (HWND hWnd)
+{
+ HDC hDC;
+ int ppi;
+ PTTYINFO pTTYInfo;
+
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return;
+
+ if (gPrintFontSameAs) {
+
+ /* No longer same as window font. Use window font as starting point
+ * for new printer font. User may later modify printer font. */
+ hDC = GetDC (hWnd);
+ ppi = GetDeviceCaps (hDC, LOGPIXELSY);
+ ReleaseDC (ghTTYWnd, hDC);
+ ExtractFontInfo(&pTTYInfo->lfTTYFont,
+ gPrintFontName, sizeof(gPrintFontName)/sizeof(TCHAR),
+ &gPrintFontSize,
+ gPrintFontStyle, sizeof(gPrintFontStyle)/sizeof(TCHAR),
+ ppi,
+ gPrintFontCharSet, sizeof(gPrintFontCharSet)/sizeof(TCHAR));
+ gPrintFontSameAs = FALSE;
+ }
+ else {
+
+ /* Set to be same as the printer font. Destroy printer font info
+ * and set "sameAs" flag to TRUE. */
+ gPrintFontName[0] = '\0';
+ gPrintFontSameAs = TRUE;
+ }
+ DidResize (gpTTYInfo);
+}
+
+
+
+
+
+void
+PrintFontSelect (HWND hWnd)
+{
+ CHOOSEFONT cfTTYFont;
+ LOGFONT newFont;
+ DWORD drc;
+ int ppi;
+ HDC hDC;
+
+
+
+ hDC = GetDC (hWnd);
+ ppi = GetDeviceCaps (hDC, LOGPIXELSY);
+ ReleaseDC (ghTTYWnd, hDC);
+
+
+ newFont.lfHeight = -MulDiv (gPrintFontSize, ppi, 72);
+ _tcsncpy(newFont.lfFaceName, gPrintFontName, LF_FACESIZE);
+ newFont.lfFaceName[LF_FACESIZE-1] = 0;
+ newFont.lfWeight = 0;
+ if(_tcsstr(gPrintFontStyle, TEXT("bold")))
+ newFont.lfWeight = FW_BOLD;
+
+ newFont.lfItalic = 0;
+ if(_tcsstr(gPrintFontStyle, TEXT("italic")))
+ newFont.lfItalic = 1;
+
+ newFont.lfWidth = 0;
+ newFont.lfEscapement = 0;
+ newFont.lfOrientation = 0;
+ newFont.lfUnderline = 0;
+ newFont.lfStrikeOut = 0;
+ newFont.lfCharSet = mswin_string2charsetid(gPrintFontCharSet);
+ newFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
+ newFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ newFont.lfQuality = DEFAULT_QUALITY;
+ newFont.lfPitchAndFamily = FIXED_PITCH;
+
+
+ cfTTYFont.lStructSize = sizeof (CHOOSEFONT);
+ cfTTYFont.hwndOwner = hWnd ;
+ cfTTYFont.hDC = NULL ;
+ cfTTYFont.rgbColors = 0;
+ cfTTYFont.lpLogFont = &newFont;
+ cfTTYFont.Flags = CF_BOTH | CF_FIXEDPITCHONLY |
+ CF_INITTOLOGFONTSTRUCT | CF_ANSIONLY |
+ CF_FORCEFONTEXIST | CF_LIMITSIZE;
+ cfTTYFont.nSizeMin = FONT_MIN_SIZE;
+ cfTTYFont.nSizeMax = FONT_MAX_SIZE;
+ cfTTYFont.lCustData = 0 ;
+ cfTTYFont.lpfnHook = NULL ;
+ cfTTYFont.lpTemplateName = NULL ;
+ cfTTYFont.hInstance = GET_HINST (hWnd);
+
+
+ if (ChooseFont (&cfTTYFont)) {
+ ExtractFontInfo(&newFont,
+ gPrintFontName, sizeof(gPrintFontName)/sizeof(TCHAR),
+ &gPrintFontSize,
+ gPrintFontStyle, sizeof(gPrintFontStyle)/sizeof(TCHAR),
+ ppi,
+ gPrintFontCharSet, sizeof(gPrintFontCharSet)/sizeof(TCHAR));
+ DidResize (gpTTYInfo);
+ }
+ else
+ /* So I can see with the debugger. */
+ drc = CommDlgExtendedError();
+}
+
+
+void
+ExtractFontInfo(LOGFONT *pFont, LPTSTR fontName, size_t nfontName,
+ int *fontSize, LPTSTR fontStyle, size_t nfontStyle,
+ int ppi, LPTSTR fontCharSet, size_t nfontCharSet)
+{
+ TCHAR *sep[] = {TEXT(""), TEXT(", ")};
+ int iSep = 0;
+
+
+ _tcsncpy(fontName, pFont->lfFaceName, nfontName);
+ fontName[nfontName-1] = '\0';
+
+ *fontStyle = '\0';
+ if(pFont->lfWeight >= FW_BOLD) {
+ _tcsncpy(fontStyle, TEXT("bold"), nfontStyle);
+ fontStyle[nfontStyle-1] = '\0';
+ iSep = 1;
+ }
+
+ if(pFont->lfItalic){
+ _tcsncat(fontStyle, sep[iSep], nfontStyle - _tcslen(fontStyle));
+ fontStyle[nfontStyle-1] = '\0';
+
+ _tcsncat(fontStyle, TEXT("italic"), nfontStyle - _tcslen(fontStyle));
+ fontStyle[nfontStyle-1] = '\0';
+ }
+
+ mswin_charsetid2string(fontCharSet, nfontCharSet, pFont->lfCharSet);
+
+ if(fontSize)
+ *fontSize = MulDiv(-pFont->lfHeight, 72, ppi);
+}
+
+
+LOCAL void
+DidResize (PTTYINFO pTTYInfo)
+{
+ int i;
+
+ for (i = 0; i < RESIZE_CALLBACK_ARRAY_SIZE; ++i) {
+ if (pTTYInfo->resizer[i] != NULL)
+ pTTYInfo->resizer[i] (pTTYInfo->actNRow, pTTYInfo->actNColumn);
+ }
+ /*
+ * Put a null character into the input queue so that the data input
+ * loops stop and return to their callers who will then re-calculate the
+ * mouse regions so that the user can click on the new regions of the
+ * screen and have the right thing happen.
+ */
+ CQAdd (NODATA, 0);
+}
+
+
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Cut, Copy, and Paste operations
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+
+/*
+ * Gets called right before the menu is displayed so we can make
+ * any last minute adjustments.
+ */
+LOCAL void
+UpdateMenu (HWND hWnd)
+{
+ HMENU hMenu;
+ PTTYINFO pTTYInfo;
+ int i;
+#ifdef ACCELERATORS
+ UINT fAccel = EM_NONE;
+#endif
+
+ pTTYInfo = (PTTYINFO) MyGetWindowLongPtr (hWnd, GWL_PTTYINFO);
+ if (pTTYInfo == NULL)
+ return;
+
+ hMenu = GetMenu (hWnd);
+ if (hMenu == NULL)
+ return;
+
+ if (ghPaste) {
+ /* Currently pasting so disable paste and enable cancel paste. */
+ EnableMenuItem (hMenu, IDM_EDIT_PASTE, MF_BYCOMMAND|MF_GRAYED);
+ EnableMenuItem (hMenu, IDM_EDIT_CANCEL_PASTE, MF_BYCOMMAND|MF_ENABLED);
+#ifdef ACCELERATORS
+ fAccel |= (EM_PST | EM_PST_ABORT);
+#endif
+ }
+ else {
+ /*
+ * Not pasting. If text is available on clipboard and we are
+ * at a place where we can paste, enable past menu option.
+ */
+ if (IsClipboardFormatAvailable (CF_UNICODETEXT) && gPasteEnabled){
+ EnableMenuItem (hMenu, IDM_EDIT_PASTE, MF_BYCOMMAND|MF_ENABLED);
+#ifdef ACCELERATORS
+ fAccel |= EM_PST;
+#endif
+ }
+ else
+ EnableMenuItem (hMenu, IDM_EDIT_PASTE, MF_BYCOMMAND|MF_GRAYED);
+
+ EnableMenuItem (hMenu, IDM_EDIT_CANCEL_PASTE, MF_BYCOMMAND|MF_GRAYED);
+ }
+
+ if (SelAvailable ()) {
+ EnableMenuItem (hMenu, IDM_EDIT_CUT, MF_BYCOMMAND|MF_GRAYED);
+ EnableMenuItem (hMenu, IDM_EDIT_COPY, MF_BYCOMMAND|MF_ENABLED);
+ EnableMenuItem (hMenu, IDM_EDIT_COPY_APPEND, MF_BYCOMMAND|MF_ENABLED);
+#ifdef ACCELERATORS
+ fAccel |= (EM_CP | EM_CP_APPEND);
+#endif
+ }
+ else {
+ if (gAllowCut){
+ EnableMenuItem (hMenu, IDM_EDIT_CUT, MF_BYCOMMAND | MF_ENABLED);
+#ifdef ACCELERATORS
+ fAccel |= EM_CUT;
+#endif
+ }
+ else
+ EnableMenuItem (hMenu, IDM_EDIT_CUT, MF_BYCOMMAND | MF_GRAYED);
+
+ if (gAllowCopy) {
+ EnableMenuItem (hMenu, IDM_EDIT_COPY, MF_BYCOMMAND | MF_ENABLED);
+ EnableMenuItem (hMenu, IDM_EDIT_COPY_APPEND,
+ MF_BYCOMMAND | MF_ENABLED);
+#ifdef ACCELERATORS
+ fAccel |= (EM_CP | EM_CP_APPEND);
+#endif
+ }
+ else {
+ EnableMenuItem (hMenu, IDM_EDIT_COPY, MF_BYCOMMAND | MF_GRAYED);
+ EnableMenuItem (hMenu, IDM_EDIT_COPY_APPEND,
+ MF_BYCOMMAND | MF_GRAYED);
+ }
+ }
+
+ /*
+ * Set up Font selection menu
+ */
+ if (gPrintFontName[0] == '\0') {
+ CheckMenuItem (hMenu, IDM_OPT_FONTSAMEAS, MF_BYCOMMAND | MF_CHECKED);
+ EnableMenuItem (hMenu, IDM_OPT_SETPRINTFONT,
+ MF_BYCOMMAND | MF_GRAYED);
+ }
+ else {
+ CheckMenuItem (hMenu, IDM_OPT_FONTSAMEAS,
+ MF_BYCOMMAND | MF_UNCHECKED);
+ EnableMenuItem (hMenu, IDM_OPT_SETPRINTFONT,
+ MF_BYCOMMAND | MF_ENABLED);
+ }
+
+ /*
+ * Setup Caret selection menu
+ */
+ EnableMenuItem (hMenu, IDM_OPT_CARETBLOCK, MF_BYCOMMAND | MF_ENABLED);
+ EnableMenuItem (hMenu, IDM_OPT_CARETSMALLBLOCK, MF_BYCOMMAND | MF_ENABLED);
+ EnableMenuItem (hMenu, IDM_OPT_CARETHBAR, MF_BYCOMMAND | MF_ENABLED);
+ EnableMenuItem (hMenu, IDM_OPT_CARETVBAR, MF_BYCOMMAND | MF_ENABLED);
+ CheckMenuRadioItem(hMenu, IDM_OPT_CARETBLOCK, IDM_OPT_CARETVBAR,
+ IDM_OPT_CARETBLOCK + pTTYInfo->cCaretStyle,
+ MF_BYCOMMAND);
+
+ /*
+ * Check toolbar menu.
+ */
+ EnableMenuItem (hMenu, IDM_OPT_TOOLBAR, MF_BYCOMMAND | MF_ENABLED);
+ CheckMenuItem (hMenu, IDM_OPT_TOOLBAR, MF_BYCOMMAND |
+ (pTTYInfo->toolBarSize > 0 ? MF_CHECKED : MF_UNCHECKED));
+ EnableMenuItem (hMenu, IDM_OPT_TOOLBARPOS, MF_BYCOMMAND | MF_ENABLED);
+ CheckMenuItem (hMenu, IDM_OPT_TOOLBARPOS, MF_BYCOMMAND |
+ (pTTYInfo->toolBarTop > 0 ? MF_CHECKED : MF_UNCHECKED));
+
+
+
+ /*
+ * Check the dialogs menu.
+ */
+ /* xxx EnableMenuItem (hMenu, IDM_OPT_USEDIALOGS, MF_BYCOMMAND | MF_ENABLED);*/
+ CheckMenuItem (hMenu, IDM_OPT_USEDIALOGS, MF_BYCOMMAND |
+ (gfUseDialogs ? MF_CHECKED : MF_UNCHECKED));
+
+ /*
+ * Enable the Erase Credentials menu
+ */
+ EnableMenuItem (hMenu, IDM_OPT_ERASE_CREDENTIALS,
+ MF_BYCOMMAND | (gEraseCredsCallback ? MF_ENABLED : MF_GRAYED));
+
+ /*
+ * Enable the View in New Window menu item
+ */
+ EnableMenuItem (hMenu, IDM_MI_VIEWINWIND,
+ MF_BYCOMMAND | (gViewInWindCallback ? MF_ENABLED : MF_GRAYED));
+
+#ifdef ACCELERATORS_OPT
+ CheckMenuItem (hMenu, IDM_OPT_USEACCEL, MF_BYCOMMAND |
+ (pTTYInfo->hAccel ? MF_CHECKED : MF_UNCHECKED));
+#endif
+
+ /*
+ * Setup the sort menu...
+ */
+ if(gSortCallback){
+ i = (*gSortCallback)(0, 0);
+
+ /* NOTE: this func's args are dependent on definition order
+ * in resource.h
+ */
+ CheckMenuRadioItem(hMenu, IDM_MI_SORTSUBJECT, IDM_MI_SORTTHREAD,
+ IDM_MI_SORTSUBJECT + (i & 0x00ff), MF_BYCOMMAND);
+ CheckMenuItem(hMenu, IDM_MI_SORTREVERSE,
+ MF_BYCOMMAND|((i & 0x0100) ? MF_CHECKED : MF_UNCHECKED));
+ }
+ else{
+ CheckMenuRadioItem(hMenu, IDM_MI_SORTSUBJECT, IDM_MI_SORTTHREAD,
+ IDM_MI_SORTARRIVAL, MF_BYCOMMAND);
+ CheckMenuItem(hMenu, IDM_MI_SORTREVERSE, MF_BYCOMMAND | MF_UNCHECKED);
+ }
+
+ /*
+ * Setup the flag menu...
+ */
+ if(gFlagCallback){
+ int flags = (*gFlagCallback)(0, 0);
+ for(i = IDM_MI_FLAGIMPORTANT; i <= IDM_MI_FLAGDELETED; i++)
+ CheckMenuItem(hMenu, i, MF_BYCOMMAND
+ | (((flags >> (i - IDM_MI_FLAGIMPORTANT)) & 0x0001)
+ ? MF_CHECKED : MF_UNCHECKED));
+ }
+
+ /*
+ *
+ */
+ if(gHdrCallback){
+ i = (*gHdrCallback)(0, 0);
+ CheckMenuItem(hMenu, IDM_MI_HDRMODE,
+ MF_BYCOMMAND|((i != 0) ? MF_CHECKED : MF_UNCHECKED));
+ }
+
+ /*
+ *
+ */
+ if(gZoomCallback){
+ i = (*gZoomCallback)(0, 0);
+ CheckMenuItem(hMenu, IDM_MI_ZOOM,
+ MF_BYCOMMAND | (i ? MF_CHECKED : MF_UNCHECKED));
+ }
+ /*
+ * Set up command menu.
+ */
+ if (!pTTYInfo->menuItemsCurrent) {
+ for (i = 0; i < KS_COUNT; ++i)
+ if(i + KS_RANGESTART != KS_GENERALHELP)
+ EnableMenuItem (hMenu, i + KS_RANGESTART,
+ MF_BYCOMMAND | ((pTTYInfo->menuItems[i].miActive)
+ ? MF_ENABLED : MF_GRAYED));
+
+ /*
+ * Special command-specific knowledge here
+ */
+ for(i = IDM_MI_SORTSUBJECT; i <= IDM_MI_SORTREVERSE; i++)
+ EnableMenuItem (hMenu, i,
+ MF_BYCOMMAND
+ | ((pTTYInfo->menuItems[KS_SORT-KS_RANGESTART].miActive)
+ ? MF_ENABLED : MF_GRAYED));
+
+ for(i = IDM_MI_FLAGIMPORTANT; i <= IDM_MI_FLAGDELETED; i++)
+ EnableMenuItem (hMenu, i,
+ MF_BYCOMMAND
+ | ((pTTYInfo->menuItems[KS_FLAG - KS_RANGESTART].miActive)
+ ? MF_ENABLED : MF_GRAYED));
+
+ }
+
+ /*
+ * deal with any callback state dependent enabling
+ */
+ if(pTTYInfo->menuItems[IDM_MI_APPLY - KS_RANGESTART].miActive)
+ EnableMenuItem(hMenu, IDM_MI_APPLY,
+ MF_BYCOMMAND | ((gSelectedCallback
+ && (*gSelectedCallback)(0, 0))
+ ? MF_ENABLED : MF_GRAYED));
+
+ if(pTTYInfo->menuItems[IDM_MI_ZOOM - KS_RANGESTART].miActive)
+ EnableMenuItem(hMenu, IDM_MI_ZOOM,
+ MF_BYCOMMAND | ((gSelectedCallback
+ && (*gSelectedCallback)(0, 0))
+ ? MF_ENABLED : MF_GRAYED));
+
+#ifdef ACCELERATORS
+ if(pTTYInfo->menuItems[KS_WHEREIS - KS_RANGESTART].miActive)
+ fAccel |= EM_FIND;
+
+ AccelManage (hWnd, fAccel);
+#endif
+
+ pTTYInfo->menuItemsCurrent = TRUE;
+}
+
+
+
+/*
+ * Cut region to kill buffer.
+ */
+LOCAL void
+EditCut (void)
+{
+ HANDLE hCB;
+
+ if(gCopyCutFunction == (getc_t)kremove){
+ hCB = GlobalAlloc (GMEM_MOVEABLE, 0);
+ if (hCB != NULL) {
+ kdelete(); /* Clear current kill buffer. */
+ killregion (1, 0); /* Kill Region (and copy to clipboard). */
+ update (); /* And update the screen */
+ }
+ }
+}
+
+
+
+/*
+ * This function copies the kill buffer to the window's clip board.
+ * (actually, it can copy any buffer for which a copyfunc is provided).
+ * Called from ldelete().
+ */
+void
+mswin_killbuftoclip (getc_t copyfunc)
+{
+ HANDLE hCB;
+ getc_t oldfunc;
+
+ /* Save old copy function. */
+ oldfunc = gCopyCutFunction;
+ gCopyCutFunction = copyfunc;
+
+ /* Allocate clip buffer. */
+ hCB = GlobalAlloc (GMEM_MOVEABLE, 0);
+ if (hCB != NULL) {
+ EditDoCopyData (hCB, 0);
+ }
+
+ /* restore copy function. */
+ gCopyCutFunction = oldfunc;
+}
+
+
+
+/*
+ * Copy region to kill buffer.
+ */
+LOCAL void
+EditCopy (void)
+{
+ HANDLE hCB;
+
+ if (SelAvailable()) {
+ /* This is a copy of the windows selection. */
+ hCB = GlobalAlloc (GMEM_MOVEABLE, 0);
+ if (hCB != NULL)
+ SelDoCopy (hCB, 0);
+ }
+ else {
+
+ /* Otherwise, it's a Pico/Pine copy. */
+ if(gCopyCutFunction == (getc_t)kremove){
+ kdelete(); /* Clear current kill buffer. */
+ copyregion (1, 0);
+ }
+
+ hCB = GlobalAlloc (GMEM_MOVEABLE, 0);
+ if (hCB != NULL)
+ EditDoCopyData (hCB, 0);
+ }
+}
+
+
+
+/*
+ * Called in responce to "Copy Append" menu command, when there is an active
+ * Windows selection on the screen.
+ */
+LOCAL void
+EditCopyAppend (void)
+{
+ HANDLE hCB;
+ HANDLE hMyCopy;
+ TCHAR *pCB;
+ TCHAR *pMyCopy;
+ size_t cbSize = 0;
+
+ /* Attempt to copy clipboard data to my own handle. */
+ hMyCopy = NULL;
+ if (OpenClipboard (ghTTYWnd)) { /* And can get clipboard. */
+ hCB = GetClipboardData (CF_UNICODETEXT);
+ if (hCB != NULL) { /* And can get data. */
+ pCB = GlobalLock (hCB);
+ cbSize = _tcslen (pCB); /* It's a null term string. */
+ hMyCopy = GlobalAlloc (GMEM_MOVEABLE, (cbSize+1)*sizeof(*pCB));
+ if (hMyCopy != NULL) { /* And can get memory. */
+ pMyCopy = GlobalLock (hMyCopy);
+ if (pMyCopy != NULL) {
+ memcpy (pMyCopy, pCB, cbSize*sizeof(*pCB)); /* Copy data. */
+ GlobalUnlock (hMyCopy);
+ }
+ else {
+ GlobalFree (hMyCopy);
+ hMyCopy = NULL;
+ }
+ }
+ GlobalUnlock (hCB);
+ } /* GetClipboardData. */
+ CloseClipboard ();
+ } /* OpenClipboard. */
+
+
+
+ /* Now, if I got a copy, append current selection to that
+ * and stuff it back into the clipboard. */
+ if (hMyCopy != NULL) {
+ if (SelAvailable ()) {
+ SelDoCopy (hMyCopy, (DWORD)cbSize);
+ }
+ else {
+ if(gCopyCutFunction == (getc_t)kremove) {
+ kdelete(); /* Clear current kill buffer. */
+ copyregion (1, 0);
+ }
+ EditDoCopyData (hMyCopy, (DWORD)cbSize);
+ }
+ }
+}
+
+
+/*
+ * Copy data from the kill buffer to the clipboard. Handle LF->CRLF
+ * translation if necessary.
+ */
+LOCAL void
+EditDoCopyData (HANDLE hCB, DWORD lenCB)
+{
+ TCHAR *pCB;
+ TCHAR *p;
+ long c; /* would be TCHAR but needs -1 retval from callback */
+ TCHAR lastc = (TCHAR)'\0';
+ DWORD cbSize; /* Allocated size of hCB. */
+ DWORD i;
+#define BUF_INC 4096
+
+ if (gCopyCutFunction != NULL) { /* If there really is data. */
+ if (OpenClipboard (ghTTYWnd)) { /* ...and we get the CB. */
+ if (EmptyClipboard ()) { /* ...and clear previous CB.*/
+ pCB = GlobalLock (hCB);
+ p = pCB + lenCB;
+ cbSize = lenCB;
+ /* Copy it. (BUG: change int arg) */
+ for(i = 0L; (c = (*gCopyCutFunction)((int)i)) != -1; i++){
+ /*
+ * Rather than fix every function that might
+ * get called for character retrieval to supply
+ * CRLF EOLs, let's just fix it here. The downside
+ * is a much slower copy for large buffers, but
+ * hey, what do they want?
+ */
+ if(lenCB + 2L >= cbSize){
+ cbSize += BUF_INC;
+ GlobalUnlock (hCB);
+ hCB = GlobalReAlloc (hCB, cbSize*sizeof(TCHAR), GMEM_MOVEABLE);
+ if (hCB == NULL)
+ return;
+
+ pCB = GlobalLock (hCB);
+ p = pCB + lenCB;
+ }
+
+ if(c == (TCHAR)ASCII_LF && lastc != (TCHAR)ASCII_CR) {
+ *p++ = (TCHAR)ASCII_CR; /* insert CR before LF */
+ lenCB++;
+ }
+
+ *p++ = lastc = (TCHAR)c;
+ lenCB++;
+ }
+
+ /* Only if we got some data. */
+ if (lenCB > 0) {
+ *p = (TCHAR)'\0';
+ GlobalUnlock (hCB);
+
+ if (SetClipboardData (CF_UNICODETEXT, hCB) == NULL)
+ /* Failed! Free the data. */
+ GlobalFree (hCB);
+ }
+ else {
+ /* There was no data copied. */
+ GlobalUnlock (hCB);
+ GlobalFree (hCB);
+ }
+ }
+ CloseClipboard ();
+ }
+ }
+}
+
+
+/*
+ * Get a handle to the current (text) clipboard and make my own copy.
+ * Keep my copy locked because I'll be using it to read bytes from.
+ */
+LOCAL void
+EditPaste (void)
+{
+ HANDLE hCB;
+ LPTSTR pCB;
+ LPTSTR pPaste;
+ size_t cbSize;
+
+ if (ghPaste == NULL) { /* If we are not already pasting. */
+ if (OpenClipboard (ghTTYWnd)) { /* And can get clipboard. */
+ hCB = GetClipboardData (CF_UNICODETEXT);
+ if (hCB != NULL) { /* And can get data. */
+ pCB = GlobalLock (hCB);
+ cbSize = _tcslen (pCB); /* It's a null term string. */
+ ghPaste = GlobalAlloc (GMEM_MOVEABLE, (cbSize+1)*sizeof(TCHAR));
+ if (ghPaste != NULL) { /* And can get memory. */
+ gpPasteNext = GlobalLock (ghPaste);
+ memcpy (gpPasteNext, pCB, (cbSize+1)*sizeof(TCHAR)); /* Copy data. */
+ /* Keep ghPaste locked. */
+
+ /*
+ * If we're paste is enabled but limited to the first
+ * line of the clipboard, prune the paste buffer...
+ */
+ if(gPasteEnabled == MSWIN_PASTE_LINE
+ && (pPaste = _tcschr(gpPasteNext, (TCHAR)ASCII_CR))){
+ *pPaste = (TCHAR)'\0';
+ cbSize = _tcslen(gpPasteNext);
+ }
+
+ /*
+ * If there is a selection (gCopyCutFunction != NULL)
+ * then delete it so that it will be replaced by
+ * the pasted text.
+ */
+ if (gCopyCutFunction != NULL)
+ deleteregion (1, 0);
+
+ gPasteBytesRemain = cbSize;
+ gPasteWasCR = FALSE;
+#ifdef FDEBUG
+ if (mswin_debug > 8)
+ fprintf (mswin_debugfile, "EditPaste:: Paste %d bytes\n",
+ gPasteBytesRemain);
+#endif
+ }
+ GlobalUnlock (hCB);
+ }
+ CloseClipboard ();
+ }
+ }
+}
+
+
+
+
+
+
+/*
+ * Cancel an active paste operation.
+ */
+LOCAL void
+EditCancelPaste (void)
+{
+ if (ghPaste != NULL) { /* Must be pasting. */
+ GlobalUnlock (ghPaste); /* Then Unlock... */
+ GlobalFree (ghPaste); /* ...and free the paste buffer. */
+ ghPaste = NULL; /* Indicates no paste data. */
+ gpPasteNext = NULL; /* Just being tidy. */
+ gPasteBytesRemain = 0; /* ditto. */
+#ifdef FDEBUG
+ if (mswin_debug > 8)
+ fprintf (mswin_debugfile, "EditCancelPaste:: Free Paste Data\n");
+#endif
+ }
+}
+
+
+/*
+ * Get the next byte from the paste buffer. If all bytes have been
+ * retreived, free the paste buffer.
+ * Map all CRLF sequence to a single CR.
+ */
+LOCAL UCS
+EditPasteGet (void)
+{
+ UCS b = NODATA;
+
+ if (ghPaste != NULL) { /* ghPaste tells if we are pasting. */
+ if (gPasteBytesRemain > 0) { /* Just in case... */
+ /* Get one byte and move pointer. */
+ b = (TCHAR) *gpPasteNext++;
+ --gPasteBytesRemain; /* one less. */
+ if (gPasteWasCR && b == (TCHAR)ASCII_LF) {
+ if (gPasteBytesRemain) {
+ /* Skip of LF. */
+ b = (TCHAR) *gpPasteNext++;
+ --gPasteBytesRemain;
+ }
+ else
+ b = NODATA; /* Ignore last LF. */
+ }
+ gPasteWasCR = (b == (TCHAR)ASCII_CR);
+#ifdef FDEBUG
+ if (mswin_debug > 8)
+ fprintf (mswin_debugfile, "EditPasteGet:: char %c, gPasteWasCR %d, gPasteBytesRemain %d\n",
+ b, gPasteWasCR, gPasteBytesRemain);
+#endif
+ }
+ if (gPasteBytesRemain <= 0) { /* All Done? */
+ GlobalUnlock (ghPaste); /* Then Unlock... */
+ GlobalFree (ghPaste); /* ...and free the paste buffer. */
+ ghPaste = NULL; /* Indicates no paste data. */
+ gpPasteNext = NULL; /* Just being tidy. */
+ gPasteBytesRemain = 0; /* ditto. */
+#ifdef FDEBUG
+ if (mswin_debug > 8)
+ fprintf (mswin_debugfile, "EditPasteGet:: Free Paste Data\n");
+#endif
+ }
+ }
+
+ if(b < ' ') {
+ b += '@';
+ b |= CTRL;
+ }
+
+ return (b);
+}
+
+
+/*
+ * Return true if Paste data is available. If gpPaste != NULL then there
+ * is paste data.
+ */
+LOCAL BOOL
+EditPasteAvailable (void)
+{
+ return (ghPaste != NULL);
+}
+
+
+
+/*
+ * Select everything in the buffer
+ */
+LOCAL void
+EditSelectAll()
+{
+ if(ComposerEditing){
+ }
+ else{
+ gotobob(0, 1);
+ setmark(0, 1);
+ gotoeob(0, 1);
+ update (); /* And update the screen */
+ }
+}
+
+
+LOCAL void
+SortHandler(int order, int reverse)
+{
+ int old = (*gSortCallback)(0, 0);
+
+ if(order < 0){
+ old ^= 0x0100; /* flip reverse bit */
+ (*gSortCallback)(1, old);
+ }
+ else
+ (*gSortCallback)(1, order | (old & 0x0100));
+}
+
+
+LOCAL void
+FlagHandler(int index, int args)
+{
+ if(gFlagCallback)
+ (void) (*gFlagCallback)(index + 1, 0L);
+}
+
+
+LOCAL void
+MSWHelpShow (cbstr_t fpHelpCallback)
+{
+ if (fpHelpCallback != NULL){
+ char title[256], *help;
+
+ if(help = (*fpHelpCallback) (title))
+ mswin_displaytext (title, help, strlen(help), NULL, NULL, 0);
+ }
+}
+
+
+
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Adjust the timer frequency as needed.
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+LOCAL void
+MyTimerSet (void)
+{
+ UINT period;
+ /* Decide on period to use. */
+ if (gAllowMouseTrack)
+ period = MY_TIMER_EXCEEDINGLY_SHORT_PERIOD;
+ else
+ period = my_timer_period;
+
+ if (period != gTimerCurrentPeriod) {
+ if (SetTimer (ghTTYWnd, MY_TIMER_ID, period, NULL) == 0)
+ MessageBox (ghTTYWnd, TIMER_FAIL_MESSAGE, NULL,
+ MB_OK | MB_ICONINFORMATION);
+ else
+ gTimerCurrentPeriod = period;
+ }
+}
+
+
+
+
+
+void
+mswin_setperiodiccallback (cbvoid_t periodiccb, long period)
+{
+ if (periodiccb != NULL && period > 0) {
+ gPeriodicCallback = periodiccb;
+ gPeriodicCBTime = period;
+ gPeriodicCBTimeout = GetTickCount () / 1000 + gPeriodicCBTime;
+ }
+ else {
+ gPeriodicCallback = NULL;
+ }
+}
+
+/*
+ * Structure for variables used by mswin_exec_and_wait which need to be
+ * freed in multiple places.
+ */
+typedef struct MSWIN_EXEC_DATA {
+ HANDLE infd;
+ HANDLE outfd;
+ LPTSTR lptstr_whatsit;
+ LPTSTR lptstr_command;
+ LPTSTR lptstr_infile;
+ LPTSTR lptstr_outfile;
+ MSWIN_TEXTWINDOW *mswin_tw;
+} MSWIN_EXEC_DATA;
+
+LOCAL void
+mswin_exec_data_init(MSWIN_EXEC_DATA *exec_data)
+{
+ memset(exec_data, 0, sizeof(MSWIN_EXEC_DATA));
+ exec_data->infd = INVALID_HANDLE_VALUE;
+ exec_data->outfd = INVALID_HANDLE_VALUE;
+}
+
+LOCAL void
+mswin_exec_data_free(MSWIN_EXEC_DATA *exec_data, BOOL delete_outfile)
+{
+ if(exec_data->infd != INVALID_HANDLE_VALUE)
+ CloseHandle(exec_data->infd);
+
+ if(exec_data->outfd != INVALID_HANDLE_VALUE) {
+ CloseHandle(exec_data->outfd);
+ if(delete_outfile)
+ _tunlink(exec_data->lptstr_outfile);
+ }
+
+ if(exec_data->lptstr_infile)
+ fs_give((void **) &exec_data->lptstr_infile);
+ if(exec_data->lptstr_outfile)
+ fs_give((void **) &exec_data->lptstr_outfile);
+ if(exec_data->lptstr_whatsit)
+ fs_give((void **) &exec_data->lptstr_whatsit);
+ if(exec_data->lptstr_command)
+ fs_give((void **) &exec_data->lptstr_command);
+
+ if(exec_data->mswin_tw) {
+ /*
+ * Set the out_file is zero. We don't need mswin_tw
+ * to save the file anymore since we're bailing.
+ */
+ exec_data->mswin_tw->out_file = NULL;
+
+ /*
+ * If the window is still open, then set the id to 0 so
+ * mswin_tw_close_callback() will free the memory whenever
+ * the window closes. Otherwise free it now.
+ */
+ if(exec_data->mswin_tw->hwnd)
+ exec_data->mswin_tw->id = 0;
+ else
+ MemFree(exec_data->mswin_tw);
+ }
+}
+
+/*
+ * Execute command and wait for the child to exit
+ *
+ * whatsit - description of reason exec being called
+ * command - command to run
+ * infile - name of file to pass as stdin
+ * outfile - name of file to pass as stdout
+ * exit_val - where to store return value of the process
+ * mswe_flags -
+ * MSWIN_EAW_CAPT_STDERR - capture stderr along with stdout
+ * MSWIN_EAW_CTRL_C_CANCELS - if user presses ctrl-c, detach child
+ * Returns: 0, successfully completed program
+ * -1, errors occurred
+ * -2, user chose to stop waiting for program before it finished
+ */
+int
+mswin_exec_and_wait (char *utf8_whatsit, char *utf8_command,
+ char *utf8_infile, char *utf8_outfile,
+ int *exit_val, unsigned mswe_flags)
+{
+ MEvent mouse;
+ BOOL brc;
+ int rc;
+ TCHAR waitingFor[256];
+ PROCESS_INFORMATION proc_info;
+ DWORD exit_code;
+ MSWIN_EXEC_DATA exec_data;
+#ifdef ALTED_DOT
+ BOOL b_use_mswin_tw;
+#endif
+
+ mswin_exec_data_init(&exec_data);
+
+ memset(&proc_info, 0, sizeof(proc_info));
+
+ mswin_flush ();
+
+ exec_data.lptstr_infile = utf8_infile ? utf8_to_lptstr(utf8_infile) : NULL;
+ exec_data.lptstr_outfile = utf8_outfile ? utf8_to_lptstr(utf8_outfile) : NULL;
+
+ exec_data.lptstr_command = utf8_to_lptstr(utf8_command);
+ exec_data.lptstr_whatsit = utf8_to_lptstr(utf8_whatsit);
+
+#ifdef ALTED_DOT
+ /* If the command is '.', then use mswin_tw to open the file. */
+ b_use_mswin_tw = utf8_command &&
+ utf8_command[0] == '.' && utf8_command[1] == '\0';
+
+ if(b_use_mswin_tw) {
+
+ proc_info.hThread = INVALID_HANDLE_VALUE;
+ proc_info.hProcess = INVALID_HANDLE_VALUE;
+
+ exec_data.mswin_tw = mswin_tw_displaytext_lptstr(
+ exec_data.lptstr_whatsit, exec_data.lptstr_infile, 4, NULL,
+ exec_data.mswin_tw, MSWIN_DT_FILLFROMFILE);
+
+ if(exec_data.mswin_tw) {
+ mswin_set_readonly(exec_data.mswin_tw, FALSE);
+
+ /* Tell mswin_tw to write the edit contents to this file. */
+ exec_data.mswin_tw->out_file = exec_data.lptstr_outfile;
+
+ /* Make sure mswin_tw isn't freed behind our back. */
+ exec_data.mswin_tw->id = (UINT)-1;
+ brc = TRUE;
+ }
+ else {
+ brc = FALSE;
+ }
+ }
+ else
+#endif /* ALTED_DOT */
+ {
+ SECURITY_ATTRIBUTES atts;
+ STARTUPINFO start_info;
+
+ memset(&atts, 0, sizeof(atts));
+ memset(&start_info, 0, sizeof(start_info));
+
+ /* set file attributes of temp files*/
+ atts.nLength = sizeof(SECURITY_ATTRIBUTES);
+ atts.bInheritHandle = TRUE;
+ atts.lpSecurityDescriptor = NULL;
+
+ /* open files if asked for */
+ if(utf8_infile
+ && ((exec_data.infd = CreateFile(exec_data.lptstr_infile,
+ GENERIC_READ, 0, &atts,
+ OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL))
+ == INVALID_HANDLE_VALUE)){
+
+ mswin_exec_data_free(&exec_data, TRUE);
+ return(-1);
+ }
+
+ if(utf8_outfile
+ && ((exec_data.outfd = CreateFile(exec_data.lptstr_outfile,
+ GENERIC_WRITE, 0, &atts,
+ OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL))
+ == INVALID_HANDLE_VALUE)){
+
+ mswin_exec_data_free(&exec_data, TRUE);
+ return(-1);
+ }
+
+ start_info.dwFlags = STARTF_FORCEONFEEDBACK | STARTF_USESHOWWINDOW;
+ start_info.wShowWindow = (utf8_infile || utf8_outfile) ? SW_SHOWMINNOACTIVE : SW_SHOWNA;
+
+ /* set up i/o redirection */
+ if(utf8_infile)
+ start_info.hStdInput = exec_data.infd;
+ if(utf8_outfile)
+ start_info.hStdOutput = exec_data.outfd;
+ if(utf8_outfile && (mswe_flags & MSWIN_EAW_CAPT_STDERR))
+ start_info.hStdError = exec_data.outfd;
+ if(utf8_infile || utf8_outfile)
+ start_info.dwFlags |= STARTF_USESTDHANDLES;
+
+ brc = CreateProcess(NULL, exec_data.lptstr_command, NULL, NULL,
+ (utf8_infile || utf8_outfile) ? TRUE : FALSE,
+ CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP,
+ NULL, NULL, &start_info, &proc_info);
+ }
+
+ if(brc) {
+ _sntprintf(waitingFor, sizeof(waitingFor)/sizeof(TCHAR),
+ TEXT("%s is currently waiting for the %s (%s) to complete. Click \"Cancel\" to stop waiting, or \"OK\" to continue waiting."),
+ gszAppName, exec_data.lptstr_whatsit, exec_data.lptstr_command);
+
+ if(proc_info.hThread != INVALID_HANDLE_VALUE) {
+ /* Don't need the thread handle, close it now. */
+ CloseHandle (proc_info.hThread);
+ }
+
+ /*
+ * Go into holding pattern until the other application terminates
+ * or we are told to stop waiting.
+ */
+ while(TRUE){
+
+#ifdef ALTED_DOT
+ if(b_use_mswin_tw)
+ {
+ if(!exec_data.mswin_tw)
+ break;
+
+ exit_code = exec_data.mswin_tw->hwnd && exec_data.mswin_tw->out_file ?
+ STILL_ACTIVE : 0;
+ }
+ else
+#endif /* ALTED_DOT */
+ {
+ if(GetExitCodeProcess(proc_info.hProcess, &exit_code) == FALSE)
+ break;
+ }
+
+ if(exit_code == STILL_ACTIVE){
+ rc = mswin_getc();
+ brc = mswin_getmouseevent (&mouse);
+
+ if (rc != NODATA ||
+ (brc && mouse.event == M_EVENT_DOWN)) {
+ if(mswe_flags & MSWIN_EAW_CTRL_C_CANCELS){
+ if(rc == (CTRL|'C'))
+ rc = IDCANCEL;
+ }
+ else{
+ rc = MessageBox (ghTTYWnd, waitingFor, exec_data.lptstr_whatsit,
+ MB_ICONSTOP | MB_OKCANCEL);
+ }
+ SelClear ();
+ if (rc == IDCANCEL){
+ /* terminate message to child ? */
+ mswin_exec_data_free(&exec_data, TRUE);
+ return (-2);
+ }
+ }
+ }
+ else{
+ if(proc_info.hProcess != INVALID_HANDLE_VALUE) {
+ /* do something about child's exit status */
+ CloseHandle (proc_info.hProcess);
+ }
+ if(exit_val)
+ *exit_val = exit_code;
+ break;
+ }
+ }
+
+ if (gpTTYInfo->fMinimized)
+ ShowWindow (ghTTYWnd, SW_SHOWNORMAL);
+
+ /*
+ * If we're using a mswin_tw and we're not capturing the output, we
+ * just bailed immediately. If that's the case, don't bring the main
+ * window up over the top of the textwindow we just brought up.
+ */
+#ifdef ALTED_DOT
+ if(!b_use_mswin_tw || !exec_data.mswin_tw || exec_data.mswin_tw->out_file)
+#endif /* ALTED_DOT */
+ BringWindowToTop (ghTTYWnd);
+
+ mswin_exec_data_free(&exec_data, FALSE);
+ return (0);
+ }
+ else{
+ mswin_exec_data_free(&exec_data, TRUE);
+ return((rc = (int) GetLastError()) ? rc : -1); /* hack */
+ }
+
+ /* NOTREACHED */
+ return(-1);
+}
+
+
+int
+mswin_shell_exec(char *command_utf8, HINSTANCE *pChildProc)
+{
+ int quoted = 0;
+ SHELLEXECUTEINFO shell_info;
+ LPTSTR command_lpt, free_command_lpt;
+ LPTSTR p, q, parm = NULL;
+ TCHAR buf[1024];
+
+ if(!command_utf8)
+ return(-1);
+
+ free_command_lpt = command_lpt = utf8_to_lptstr(command_utf8);
+ if(!command_lpt)
+ return(-1);
+
+ mswin_flush ();
+
+ /*
+ * Pick first arg apart by whitespace until what's to the left
+ * is no longer a valid path/file. Everything else is then an
+ * command line arg...
+ */
+ if(*(p = command_lpt) == '\"'){
+ p = ++command_lpt; /* don't include quote */
+ quoted++;
+ }
+
+ q = buf;
+ while(1)
+ if(!quoted && _istspace(*p)){
+ char *buf_utf8;
+
+ *q = '\0';
+ buf_utf8 = lptstr_to_utf8(buf);
+ if(*buf == '*' || (buf_utf8 && fexist(buf_utf8, "x", (off_t *) NULL) == FIOSUC)){
+ parm = p;
+ if(buf_utf8)
+ fs_give((void **) &buf_utf8);
+
+ break;
+ }
+
+ if(buf_utf8)
+ fs_give((void **) &buf_utf8);
+ }
+ else if(quoted && *p == '\"'){
+ parm = p;
+ break;
+ }
+ else if(!(*q++ = *p++)){
+ parm = p - 1;
+ break;
+ }
+
+ if(*command_lpt && parm){
+ do
+ *parm++ = '\0';
+ while(*parm && _istspace((unsigned char) *parm));
+
+
+ /*
+ * HACK -- since star is very unlikely to actually appear
+ * in a command name thats launched via a shell command line,
+ * a leading one indicates special handling.
+ */
+ if(command_lpt[0] == '*'){
+ if(!_tcsncmp(command_lpt + 1, TEXT("Shell*"), 8)){
+ /* Leave it to ShellExecute magic to "open" the thing */
+ command_lpt = parm;
+ parm = NULL;
+ }
+ }
+ }
+ else{
+ if(free_command_lpt)
+ fs_give((void **) &free_command_lpt);
+
+ return(-1);
+ }
+
+ memset(&shell_info, 0, sizeof(SHELLEXECUTEINFO));
+ shell_info.cbSize = sizeof(SHELLEXECUTEINFO);
+ shell_info.fMask = SEE_MASK_DOENVSUBST
+ | SEE_MASK_NOCLOSEPROCESS
+ | SEE_MASK_FLAG_DDEWAIT;
+ shell_info.hwnd = ghTTYWnd;
+ shell_info.lpFile = command_lpt;
+ shell_info.lpParameters = parm;
+ shell_info.lpDirectory = NULL; /* default is current directory */
+ shell_info.nShow = SW_SHOWNORMAL;
+
+ ShellExecuteEx(&shell_info);
+
+ if((int)(LONG_PTR)shell_info.hInstApp > 32){
+ if(pChildProc)
+ *pChildProc = shell_info.hProcess;
+
+ if(free_command_lpt)
+ fs_give((void **) &free_command_lpt);
+
+ return(0); /* success! */
+ }
+
+ if(free_command_lpt)
+ fs_give((void **) &free_command_lpt);
+
+ return(-1);
+}
+
+
+/*
+ * Generate an error message for a failed windows exec or loadlibrary.
+ */
+void
+mswin_exec_err_msg(char *what, int status, char *buf, size_t buflen)
+{
+ switch(status){
+ case 2:
+ case 3:
+ snprintf(buf, buflen, "%s not found.", what);
+ break;
+
+ case 8:
+ snprintf(buf, buflen, "Not enough memory to run %s.", what);
+ break;
+
+ default:
+ snprintf(buf, buflen, "Error %d starting %s.", status, what);
+ break;
+ }
+}
+
+
+int
+mswin_set_quit_confirm (int confirm)
+{
+ gConfirmExit = (confirm != 0);
+ return (confirm);
+}
+
+
+/*
+ * Called when Windows is in shutting down. Before actually shutting down
+ * Windows goes around to all the applications and asks if it is OK with
+ * them to shut down (WM_QUERYENDSESSION).
+ * If gConfirmExit is set, ask the user if they want to exit.
+ * Returning zero will stop the shutdown, non-zero allows it to proceed.
+ */
+LOCAL LRESULT
+ConfirmExit (void)
+{
+ TCHAR msg[256];
+ int rc;
+
+ if(gConfirmExit){
+ _sntprintf(msg, sizeof(msg)/sizeof(TCHAR),
+ TEXT("Exiting may cause you to lose work in %s, Exit?"),
+ gszAppName);
+ rc = MessageBox (ghTTYWnd, msg, gszAppName, MB_ICONSTOP | MB_OKCANCEL);
+ if(rc == IDCANCEL)
+ return(0);
+ }
+
+ return(1);
+}
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Registry access functions
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+/*
+ * Useful def's
+ */
+#define MSWR_KEY_MAX 128
+#define MSWR_VAL_MAX 128
+#define MSWR_CLASS_MAX 128
+#define MSWR_DATA_MAX 1024
+
+
+#define MSWR_ROOT TEXT("Software\\University of Washington\\Alpine\\1.0")
+#define MSWR_CAPABILITIES TEXT("Software\\University of Washington\\Alpine\\1.0\\Capabilities")
+#define MSWR_APPNAME TEXT("Alpine")
+#define MSWR_DLLPATH TEXT("DLLPath")
+#define MSWR_DLLNAME TEXT("pmapi32.dll")
+#define MSWRDBUF 1152
+
+
+struct mswin_reg_key {
+ HKEY rhk; /* root key (HKEY_LOCAL_MACHINE, ...) */
+ LPTSTR *knames; /* NULL terminated list of keys */
+};
+
+LPTSTR mswin_pine_hklm_regs[] = {
+ MSWR_ROOT,
+ TEXT("Software\\Clients\\Mail\\Alpine"),
+ TEXT("Software\\Clients\\News\\Alpine"),
+ TEXT("Software\\Classes\\Alpine.Url.Mailto"),
+ TEXT("Software\\Classes\\Alpine.Url.News"),
+ TEXT("Software\\Classes\\Alpine.Url.Nntp"),
+ TEXT("Software\\Classes\\Alpine.Url.Imap"),
+ NULL
+};
+
+LPTSTR mswin_pine_hkcu_regs[] = {
+ MSWR_ROOT,
+ NULL
+};
+
+static struct mswin_reg_key mswin_pine_regs[] = {
+ {HKEY_LOCAL_MACHINE, mswin_pine_hklm_regs},
+ {HKEY_CURRENT_USER, mswin_pine_hkcu_regs},
+ {NULL, NULL}
+};
+
+
+/*
+ * data: unitialized buffer, could be null
+ */
+int
+mswin_reg(int op, int tree, char *data_utf8, size_t size)
+{
+ LPTSTR data_lptstr = NULL;
+ int rv;
+
+ if(data_utf8){
+ if(size == 0){
+ /* size is zero when op & MSWR_OP_SET */
+ data_lptstr = utf8_to_lptstr(data_utf8);
+ }
+ else {
+ data_lptstr = (LPTSTR)MemAlloc(size * sizeof(TCHAR));
+ data_lptstr[0] = '\0';
+ }
+ }
+
+ rv = mswin_reg_lptstr(op, tree, data_lptstr, size);
+
+ if(data_utf8 && data_lptstr){
+ char *t_utf8str;
+ if(size){
+ t_utf8str = lptstr_to_utf8(data_lptstr);
+ strncpy(data_utf8, t_utf8str, size);
+ data_utf8[size-1] = '\0';
+ MemFree((void *)t_utf8str);
+ }
+ MemFree((void *)data_lptstr);
+ }
+ return(rv);
+}
+
+int
+mswin_reg_lptstr(int op, int tree, LPTSTR data_lptstr, size_t size)
+{
+ if(op & MSWR_OP_SET){
+ switch(tree){
+ case MSWR_PINE_RC :
+ MSWRAlpineSet(HKEY_CURRENT_USER, NULL,
+ TEXT("PineRC"), op & MSWR_OP_FORCE, data_lptstr);
+ break;
+
+ case MSWR_PINE_CONF :
+ MSWRAlpineSet(HKEY_CURRENT_USER, NULL,
+ TEXT("PineConf"), op & MSWR_OP_FORCE, data_lptstr);
+ break;
+
+ case MSWR_PINE_AUX :
+ MSWRAlpineSet(HKEY_CURRENT_USER, NULL,
+ TEXT("PineAux"), op & MSWR_OP_FORCE, data_lptstr);
+ break;
+
+ case MSWR_PINE_DIR :
+ MSWRAlpineSet(HKEY_LOCAL_MACHINE, NULL,
+ TEXT("Pinedir"), op & MSWR_OP_FORCE, data_lptstr);
+ MSWRAlpineSetHandlers(op & MSWR_OP_FORCE, data_lptstr);
+ break;
+
+ case MSWR_PINE_EXE :
+ MSWRAlpineSet(HKEY_LOCAL_MACHINE, NULL,
+ TEXT("PineEXE"), op & MSWR_OP_FORCE, data_lptstr);
+ break;
+
+ case MSWR_PINE_POS :
+ MSWRAlpineSet(HKEY_CURRENT_USER, NULL,
+ TEXT("PinePos"), op & MSWR_OP_FORCE, data_lptstr);
+ break;
+
+ default :
+ break;
+ }
+ }
+ else if(op & MSWR_OP_GET){
+ switch(tree){
+ case MSWR_PINE_RC :
+ return(MSWRAlpineGet(HKEY_CURRENT_USER, NULL,
+ TEXT("PineRC"), data_lptstr, size));
+ case MSWR_PINE_CONF :
+ return(MSWRAlpineGet(HKEY_CURRENT_USER, NULL,
+ TEXT("PineConf"), data_lptstr, size));
+ case MSWR_PINE_AUX :
+ return(MSWRAlpineGet(HKEY_CURRENT_USER, NULL,
+ TEXT("PineAux"), data_lptstr, size));
+ case MSWR_PINE_DIR :
+ return(MSWRAlpineGet(HKEY_LOCAL_MACHINE, NULL,
+ TEXT("Pinedir"), data_lptstr, size));
+ case MSWR_PINE_EXE :
+ return(MSWRAlpineGet(HKEY_LOCAL_MACHINE, NULL,
+ TEXT("PineEXE"), data_lptstr, size));
+ case MSWR_PINE_POS :
+ return(MSWRAlpineGet(HKEY_CURRENT_USER, NULL,
+ TEXT("PinePos"), data_lptstr, size));
+ default :
+ break;
+ }
+ }
+ else if(op & MSWR_OP_BLAST){
+ int rv = 0, i, j;
+
+ for(i = 0; mswin_pine_regs[i].rhk; i++){
+ for(j = 0; mswin_pine_regs[i].knames[j]; j++)
+ MSWRClear(mswin_pine_regs[i].rhk, mswin_pine_regs[i].knames[j]);
+ }
+ if(rv) return -1;
+ }
+ /* else, ignore unknown op? */
+
+ return(0);
+}
+
+
+LOCAL void
+MSWRAlpineSet(HKEY hRootKey, LPTSTR subkey, LPTSTR val, int update, LPTSTR data)
+{
+ HKEY hKey;
+ TCHAR keybuf[MSWR_KEY_MAX+1];
+
+ _sntprintf(keybuf, MSWR_KEY_MAX+1, TEXT("%s%s%s"), MSWR_ROOT,
+ (subkey && *subkey != '\\') ? TEXT("\\") : TEXT(""),
+ (subkey) ? TEXT("\\") : TEXT(""));
+
+ if(RegCreateKeyEx(hRootKey, keybuf, 0, TEXT("REG_SZ"),
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
+ NULL, &hKey, NULL) == ERROR_SUCCESS){
+ if(update || RegQueryValueEx(hKey, val, NULL, NULL,
+ NULL, NULL) != ERROR_SUCCESS)
+ RegSetValueEx(hKey, val, 0, REG_SZ, (LPBYTE)data, (DWORD)(_tcslen(data)+1)*sizeof(TCHAR));
+
+ RegCloseKey(hKey);
+ }
+}
+
+
+
+LOCAL int
+MSWRAlpineGet(HKEY hKey, LPTSTR subkey, LPTSTR val, LPTSTR data_lptstr, size_t len)
+{
+ TCHAR keybuf[MSWR_KEY_MAX+1];
+ DWORD dlen = (DWORD)len;
+
+ _sntprintf(keybuf, MSWR_KEY_MAX+1, TEXT("%s%s%s"), MSWR_ROOT,
+ (subkey && *subkey != '\\') ? TEXT("\\") : TEXT(""),
+ (subkey) ? TEXT("\\") : TEXT(""));
+
+ return(MSWRPeek(hKey, keybuf, val, data_lptstr, &dlen) == TRUE);
+}
+
+
+
+LOCAL void
+MSWRAlpineSetHandlers(int update, LPTSTR path_lptstr)
+{
+ HKEY hKey, hSubKey;
+ DWORD dwDisp;
+ BYTE tmp_b[MSWR_DATA_MAX];
+ unsigned long tmplen = MSWR_DATA_MAX, tmp_lptstr_tcharlen = MSWR_DATA_MAX/sizeof(TCHAR);
+ int exists;
+ LPTSTR tmp_lptstr = (LPTSTR)tmp_b;
+
+ /* Register as a mail client on this system */
+ if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ MSWR_ROOT, 0, KEY_ALL_ACCESS,
+ &hKey) == ERROR_SUCCESS){
+ if(RegCreateKeyEx(HKEY_LOCAL_MACHINE, MSWR_CAPABILITIES, 0, TEXT("REG_SZ"),
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
+ NULL, &hSubKey, &dwDisp) == ERROR_SUCCESS){
+ MSWRPoke(hSubKey, NULL, TEXT("ApplicationDescription"),
+ TEXT("Alpine - A program for sending, receiving, and filing email and news, whether stored locally or accessed over the network via IMAP, POP3, or NNTP. Alpine is the successor to Pine, and also is maintained by the University of Washington."));
+ MSWRPoke(hSubKey, NULL, TEXT("ApplicationName"),
+ TEXT("Alpine"));
+ _sntprintf(tmp_lptstr, tmp_lptstr_tcharlen, TEXT("%salpine.exe,0"), path_lptstr);
+ MSWRPoke(hSubKey, NULL, TEXT("ApplicationIcon"), tmp_lptstr);
+ MSWRPoke(hSubKey, TEXT("UrlAssociations"), TEXT("mailto"), TEXT("Alpine.Url.Mailto"));
+ MSWRPoke(hSubKey, TEXT("UrlAssociations"), TEXT("news"), TEXT("Alpine.Url.News"));
+ MSWRPoke(hSubKey, TEXT("UrlAssociations"), TEXT("nntp"), TEXT("Alpine.Url.Nntp"));
+ MSWRPoke(hSubKey, TEXT("UrlAssociations"), TEXT("imap"), TEXT("Alpine.Url.Imap"));
+ RegCloseKey(hSubKey);
+ }
+ RegCloseKey(hKey);
+ if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\RegisteredApplications"), 0,
+ KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS){
+ MSWRPoke(hKey, NULL, TEXT("Alpine"), MSWR_CAPABILITIES);
+ RegCloseKey(hKey);
+ }
+ if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ TEXT("Software\\Classes"), 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS){
+ if(RegCreateKeyEx(hKey, TEXT("Alpine.Url.Mailto"), 0, TEXT("REG_SZ"),
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
+ NULL, &hSubKey, &dwDisp) == ERROR_SUCCESS){
+ MSWRProtocolSet(hSubKey, MSWR_SDC_MAIL, path_lptstr);
+ RegCloseKey(hSubKey);
+ }
+ if(RegCreateKeyEx(hKey, TEXT("Alpine.Url.Nntp"), 0, TEXT("REG_SZ"),
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
+ NULL, &hSubKey, &dwDisp) == ERROR_SUCCESS){
+ MSWRProtocolSet(hSubKey, MSWR_SDC_NNTP, path_lptstr);
+ RegCloseKey(hSubKey);
+ }
+ if(RegCreateKeyEx(hKey, TEXT("Alpine.Url.News"), 0, TEXT("REG_SZ"),
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
+ NULL, &hSubKey, &dwDisp) == ERROR_SUCCESS){
+ MSWRProtocolSet(hSubKey, MSWR_SDC_NEWS, path_lptstr);
+ RegCloseKey(hSubKey);
+ }
+ if(RegCreateKeyEx(hKey, TEXT("Alpine.Url.Imap"), 0, TEXT("REG_SZ"),
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
+ NULL, &hSubKey, &dwDisp) == ERROR_SUCCESS){
+ MSWRProtocolSet(hSubKey, MSWR_SDC_IMAP, path_lptstr);
+ RegCloseKey(hSubKey);
+ }
+ RegCloseKey(hKey);
+ }
+ }
+
+ if((exists = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ TEXT("SOFTWARE\\Clients\\Mail\\Alpine"),
+ 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS)
+ || RegCreateKeyEx(HKEY_LOCAL_MACHINE,
+ TEXT("SOFTWARE\\Clients\\Mail\\Alpine"),
+ 0, TEXT("REG_SZ"),
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
+ NULL, &hKey, &dwDisp) == ERROR_SUCCESS){
+ if(update || !exists){
+ DWORD dType;
+ char *tmp_utf8str = NULL;
+
+ MSWRPoke(hKey, NULL, NULL, MSWR_APPNAME);
+ /* set up MAPI dll stuff */
+ *tmp_b = 0;
+ RegQueryValueEx(hKey, MSWR_DLLPATH, NULL, &dType, tmp_b, &tmplen);
+ tmp_lptstr = (LPTSTR)tmp_b;
+ if(!(*tmp_lptstr)
+ || (can_access(tmp_utf8str = lptstr_to_utf8(tmp_lptstr), ACCESS_EXISTS) != 0)){
+ if(*tmp_lptstr)
+ RegDeleteValue(hKey, MSWR_DLLPATH);
+ if(tmp_utf8str)
+ MemFree((void *)tmp_utf8str);
+
+ _sntprintf(tmp_lptstr, tmp_lptstr_tcharlen,
+ TEXT("%s%s"), path_lptstr, MSWR_DLLNAME);
+
+ if(can_access(tmp_utf8str = lptstr_to_utf8(tmp_lptstr), ACCESS_EXISTS) == 0)
+ MSWRPoke(hKey, NULL, MSWR_DLLPATH, tmp_lptstr);
+ }
+ if(tmp_utf8str)
+ MemFree((void *)tmp_utf8str);
+ /* Set "mailto" handler */
+ if(RegCreateKeyEx(hKey, TEXT("Protocols\\Mailto"), 0, TEXT("REG_SZ"),
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
+ NULL, &hSubKey, &dwDisp) == ERROR_SUCCESS){
+ MSWRProtocolSet(hSubKey, MSWR_SDC_MAIL, path_lptstr);
+ RegCloseKey(hSubKey);
+ }
+
+ /* Set normal handler */
+ _sntprintf(tmp_lptstr, tmp_lptstr_tcharlen,
+ TEXT("\"%salpine.exe\""), path_lptstr);
+ MSWRPoke(hKey, TEXT("shell\\open\\command"), NULL, tmp_lptstr);
+ }
+
+ RegCloseKey(hKey);
+ }
+
+ /* Register as a news client on this system */
+ if((exists = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ TEXT("SOFTWARE\\Clients\\News\\Alpine"),
+ 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS)
+ || RegCreateKeyEx(HKEY_LOCAL_MACHINE,
+ TEXT("SOFTWARE\\Clients\\News\\Alpine"),
+ 0, TEXT("REG_SZ"), REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
+ NULL, &hKey, &dwDisp) == ERROR_SUCCESS){
+ if(update || !exists){
+ MSWRPoke(hKey, NULL, NULL, MSWR_APPNAME);
+
+ /* Set "news" handler */
+ if(RegCreateKeyEx(hKey, TEXT("Protocols\\news"), 0, TEXT("REG_SZ"),
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
+ NULL, &hSubKey, &dwDisp) == ERROR_SUCCESS){
+ MSWRProtocolSet(hSubKey, MSWR_SDC_NEWS, path_lptstr);
+ RegCloseKey(hSubKey);
+ }
+ /* Set "nntp" handler */
+ if(RegCreateKeyEx(hKey, TEXT("Protocols\\nntp"), 0, TEXT("REG_SZ"),
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
+ NULL, &hSubKey, &dwDisp) == ERROR_SUCCESS){
+ MSWRProtocolSet(hSubKey, MSWR_SDC_NNTP, path_lptstr);
+ RegCloseKey(hSubKey);
+ }
+
+ /* Set normal handler */
+ _sntprintf(tmp_lptstr, tmp_lptstr_tcharlen,
+ TEXT("\"%salpine.exe\""), path_lptstr);
+ MSWRPoke(hKey, TEXT("shell\\open\\command"), NULL, tmp_lptstr);
+ }
+
+ RegCloseKey(hKey);
+ }
+
+ /* Register as a IMAP url handler */
+ if((exists = RegOpenKeyEx(HKEY_CLASSES_ROOT, TEXT("imap"),
+ 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS)
+ || RegCreateKeyEx(HKEY_CLASSES_ROOT, TEXT("imap"), 0, TEXT("REG_SZ"),
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
+ NULL, &hKey, &dwDisp) == ERROR_SUCCESS){
+ if(update || !exists)
+ MSWRProtocolSet(hKey, MSWR_SDC_IMAP, path_lptstr);
+
+ RegCloseKey(hKey);
+ }
+}
+
+
+
+char *
+mswin_reg_default_browser(char *url_utf8)
+{
+ TCHAR scheme[MSWR_KEY_MAX+1], *p;
+ LPTSTR url_lptstr;
+ char cmdbuf[MSWR_DATA_MAX], *cmd = NULL;
+
+ url_lptstr = utf8_to_lptstr(url_utf8);
+
+ if(url_lptstr && (p = _tcschr(url_lptstr, ':')) && p - url_lptstr < MSWR_KEY_MAX){
+ _tcsncpy(scheme, url_lptstr, p - url_lptstr);
+ scheme[p-url_lptstr] = '\0';
+
+ if(MSWRShellCanOpen(scheme, cmdbuf, MSWR_DATA_MAX, 0)){
+ size_t len;
+
+ len = strlen(cmdbuf) + 2;
+ cmd = (char *) fs_get((len+1) * sizeof(char));
+ if(cmd){
+ if(strchr(cmdbuf, '*'))
+ snprintf(cmd, len+1, "\"%s\"", cmdbuf);
+ else{
+ strncpy(cmd, cmdbuf, len);
+ cmd[len] = '\0';
+ }
+ }
+ }
+ }
+
+ MemFree((void *)url_lptstr);
+
+ return(cmd);
+}
+
+
+int
+mswin_is_def_client(int type)
+{
+ TCHAR buf[MSWR_KEY_MAX+1];
+ DWORD buflen = MSWR_KEY_MAX;
+
+ if(type != MSWR_SDC_MAIL && type != MSWR_SDC_NEWS)
+ return -1;
+
+ if(MSWRPeek(HKEY_CURRENT_USER,
+ type == MSWR_SDC_MAIL ? TEXT("Software\\Clients\\Mail")
+ : TEXT("Software\\Clients\\News"), NULL,
+ buf, &buflen) && !_tcscmp(buf, TEXT("Alpine")))
+ return 1;
+ buflen = MSWR_KEY_MAX;
+ if(MSWRPeek(HKEY_LOCAL_MACHINE,
+ type == MSWR_SDC_MAIL ? TEXT("Software\\Clients\\Mail")
+ : TEXT("Software\\Clients\\News"), NULL,
+ buf, &buflen) && !_tcscmp(buf, TEXT("Alpine")))
+ return 1;
+ return 0;
+}
+
+int
+mswin_set_def_client(int type)
+{
+ HKEY hKey;
+ int successful_set = 0;
+ TCHAR path_lptstr[MSWR_DATA_MAX];
+ DWORD dwDisp;
+
+ if(type != MSWR_SDC_MAIL && type != MSWR_SDC_NEWS)
+ return 1;
+ if(RegOpenKeyEx(HKEY_CURRENT_USER,
+ type == MSWR_SDC_MAIL ? TEXT("Software\\Clients\\Mail")
+ : TEXT("Software\\Clients\\News"),
+ 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS){
+ successful_set = MSWRPoke(hKey, NULL, NULL, TEXT("Alpine"));
+ RegCloseKey(hKey);
+ }
+ else if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ type == MSWR_SDC_MAIL ? TEXT("Software\\Clients\\Mail")
+ : TEXT("Software\\Clients\\News"),
+ 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS){
+ successful_set = MSWRPoke(hKey, NULL, NULL, TEXT("Alpine"));
+ RegCloseKey(hKey);
+ }
+ if(successful_set){
+ mswin_reg_lptstr(MSWR_OP_GET, MSWR_PINE_DIR, path_lptstr, sizeof(path_lptstr)/sizeof(TCHAR));
+ if(type == MSWR_SDC_MAIL){
+ MSWRClear(HKEY_CLASSES_ROOT, TEXT("mailto"));
+ if(RegCreateKeyEx(HKEY_CLASSES_ROOT, TEXT("mailto"), 0, TEXT("REG_SZ"),
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
+ NULL, &hKey, &dwDisp) == ERROR_SUCCESS){
+ MSWRProtocolSet(hKey, MSWR_SDC_MAIL, path_lptstr);
+ RegCloseKey(hKey);
+ }
+ }
+ else if(type == MSWR_SDC_NEWS){
+ MSWRClear(HKEY_CLASSES_ROOT, TEXT("news"));
+ if(RegCreateKeyEx(HKEY_CLASSES_ROOT, TEXT("news"), 0, TEXT("REG_SZ"),
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
+ NULL, &hKey, &dwDisp) == ERROR_SUCCESS){
+ MSWRProtocolSet(hKey, MSWR_SDC_NEWS, path_lptstr);
+ RegCloseKey(hKey);
+ }
+ MSWRClear(HKEY_CLASSES_ROOT, TEXT("nntp"));
+ if(RegCreateKeyEx(HKEY_CLASSES_ROOT, TEXT("nntp"), 0, TEXT("REG_SZ"),
+ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
+ NULL, &hKey, &dwDisp) == ERROR_SUCCESS){
+ MSWRProtocolSet(hKey, MSWR_SDC_NNTP, path_lptstr);
+ RegCloseKey(hKey);
+ }
+ }
+ }
+ return 0;
+}
+
+int
+MSWRProtocolSet(HKEY hKey, int type, LPTSTR path_lptstr)
+{
+ TCHAR tmp_lptstr[MSWR_DATA_MAX];
+ BYTE EditFlags[4];
+ unsigned long tmp_lptstr_len = MSWR_DATA_MAX;
+
+ if(type != MSWR_SDC_MAIL && type != MSWR_SDC_NEWS
+ && type != MSWR_SDC_NNTP && type != MSWR_SDC_IMAP)
+ return -1;
+ MSWRPoke(hKey, NULL, NULL, type == MSWR_SDC_MAIL
+ ? TEXT("URL:MailTo Protocol")
+ : type == MSWR_SDC_NEWS ? TEXT("URL:News Protocol")
+ : type == MSWR_SDC_NNTP ? TEXT("URL:NNTP Protocol")
+ : TEXT("URL:IMAP Prototcol"));
+ MSWRPoke(hKey, NULL, TEXT("URL Protocol"), TEXT(""));
+
+ EditFlags[0] = 0x02;
+ EditFlags[1] = EditFlags[2] = EditFlags[3] = 0;
+
+ (void) RegDeleteValue(hKey, TEXT("EditFlags"));
+ (void) RegSetValueEx(hKey, TEXT("EditFlags"), 0, REG_BINARY,
+ EditFlags, (DWORD) 4);
+
+ _sntprintf(tmp_lptstr, tmp_lptstr_len,
+ TEXT("%salpine.exe,0"), path_lptstr);
+ MSWRPoke(hKey, TEXT("DefaultIcon"), NULL, tmp_lptstr);
+
+ _sntprintf(tmp_lptstr, tmp_lptstr_len,
+ TEXT("\"%salpine.exe\" -url \"%%1\""), path_lptstr);
+ MSWRPoke(hKey, TEXT("shell\\open\\command"), NULL, tmp_lptstr);
+ return 0;
+}
+
+
+/* cmdbuf can stay char * since it's our string */
+BOOL
+MSWRShellCanOpen(LPTSTR key, char *cmdbuf, int clen, int allow_noreg)
+{
+ HKEY hKey;
+ BOOL rv = FALSE;
+
+ /* See if Shell provides a method to open the thing... */
+ if(RegOpenKeyEx(HKEY_CLASSES_ROOT, key,
+ 0, KEY_READ, &hKey) == ERROR_SUCCESS){
+
+ if(cmdbuf){
+ strncpy(cmdbuf, "*Shell*", clen);
+ cmdbuf[clen-1] = '\0';
+ }
+
+ rv = TRUE;
+
+ RegCloseKey(hKey);
+ }
+ else if(allow_noreg && cmdbuf){
+ strncpy(cmdbuf, "*Shell*", clen);
+ cmdbuf[clen-1] = '\0';
+ rv = TRUE;
+ }
+
+ return(rv);
+}
+
+
+/*
+ * Fundamental registry access function that queries for particular values.
+ */
+LOCAL BOOL
+MSWRPeek(HKEY hRootKey, LPTSTR subkey, LPTSTR valstr, LPTSTR data_lptstr, DWORD *dlen)
+{
+ HKEY hKey;
+ DWORD dtype, dlen_bytes = (dlen ? *dlen : 0) * sizeof(TCHAR);
+ LONG rv = !ERROR_SUCCESS;
+
+ if(RegOpenKeyEx(hRootKey, subkey, 0, KEY_READ, &hKey) == ERROR_SUCCESS){
+ rv = RegQueryValueEx(hKey, valstr, NULL, &dtype, (LPBYTE)data_lptstr, &dlen_bytes);
+ if(dlen)
+ (*dlen) = dlen_bytes;
+ RegCloseKey(hKey);
+ }
+
+ return(rv == ERROR_SUCCESS);
+}
+
+
+/*
+ * Fundamental registry access function that sets particular values.
+ */
+LOCAL BOOL
+MSWRPoke(HKEY hKey, LPTSTR subkey, LPTSTR valstr, LPTSTR data_lptstr)
+{
+ DWORD dtype, dwDisp, dlen = MSWR_DATA_MAX;
+ BYTE olddata[MSWR_DATA_MAX];
+ BOOL rv = FALSE;
+
+ if(!subkey
+ || RegCreateKeyEx(hKey, subkey,
+ 0, TEXT("REG_SZ"), REG_OPTION_NON_VOLATILE,
+ KEY_ALL_ACCESS, NULL,
+ &hKey, &dwDisp) == ERROR_SUCCESS){
+
+ if(RegQueryValueEx(hKey, valstr, NULL, &dtype,
+ olddata, &dlen) != ERROR_SUCCESS
+ || _tcscmp((LPTSTR)olddata, data_lptstr)){
+ (void) RegDeleteValue(hKey, valstr);
+ rv = RegSetValueEx(hKey, valstr, 0, REG_SZ,
+ (LPBYTE)data_lptstr,
+ (DWORD)(_tcslen(data_lptstr) + 1)*sizeof(TCHAR)) == ERROR_SUCCESS;
+ }
+
+ if(subkey)
+ RegCloseKey(hKey);
+ }
+
+ return(rv);
+}
+
+
+LOCAL void
+MSWRLineBufAdd(MSWR_LINE_BUFFER_S *lpLineBuf, LPTSTR line)
+{
+ if(lpLineBuf->offset >= lpLineBuf->size){
+ /* this probably won't happen, but just in case */
+ lpLineBuf->size *= 2;
+ lpLineBuf->linep = (char **)MemRealloc(lpLineBuf->linep,
+ (lpLineBuf->size + 1)*sizeof(char *));
+ }
+
+ lpLineBuf->linep[lpLineBuf->offset++] = lptstr_to_utf8(line);
+ lpLineBuf->linep[lpLineBuf->offset] = NULL;
+}
+
+/*
+ * Dump all of the registry values from a list of keys into an array
+ * of UTF8-formatted strings.
+ */
+char **
+mswin_reg_dump(void)
+{
+ MSWR_LINE_BUFFER_S lineBuf;
+ unsigned long initial_size = 256;
+ int i, j;
+
+ lineBuf.linep = (char **)MemAlloc((initial_size+1)*sizeof(char *));
+ lineBuf.size = initial_size;
+ lineBuf.offset = 0;
+
+ MSWRLineBufAdd(&lineBuf, TEXT("Registry values for Alpine:"));
+ MSWRLineBufAdd(&lineBuf, TEXT(""));
+
+ for(i = 0; mswin_pine_regs[i].rhk; i++){
+ MSWRLineBufAdd(&lineBuf, mswin_pine_regs[i].rhk == HKEY_LOCAL_MACHINE
+ ? TEXT("HKEY_LOCAL_MACHINE")
+ : TEXT("HKEY_CURRENT_USER"));
+ for(j = 0; mswin_pine_regs[i].knames[j]; j++)
+ MSWRDump(mswin_pine_regs[i].rhk,
+ mswin_pine_regs[i].knames[j],
+ 1, &lineBuf);
+ }
+
+ return(lineBuf.linep);
+}
+
+
+/*
+ * Recursive function to crawl a registry hierarchy and print the contents.
+ *
+ * Returns: 0
+ */
+LOCAL int
+MSWRDump(HKEY hKey, LPTSTR pSubKey, int keyDepth, MSWR_LINE_BUFFER_S *lpLineBuf)
+{
+ HKEY hSubKey;
+ TCHAR KeyBuf[MSWR_KEY_MAX+1];
+ TCHAR ValBuf[MSWR_VAL_MAX+1];
+ BYTE DataBuf[MSWR_DATA_MAX+1];
+ DWORD dwKeyIndex, dwKeyLen;
+ DWORD dwValIndex, dwValLen, dwDataLen;
+ DWORD dwType;
+ FILETIME ftKeyTime;
+ TCHAR new_buf[1024];
+ unsigned int new_buf_len = 1024;
+ int i, j, k, tab_width = 4;
+
+ /* open the passed subkey */
+ if(RegOpenKeyEx(hKey, pSubKey, 0,
+ KEY_READ, &hSubKey) == ERROR_SUCCESS){
+
+ /* print out key name here */
+ for(i = 0, k = 0; i < keyDepth % 8; i++)
+ for(j = 0; j < tab_width; j++)
+ new_buf[k++] = ' ';
+ _sntprintf(new_buf+k, new_buf_len - k, TEXT("%s"), pSubKey);
+ new_buf[new_buf_len - 1] = '\0';
+ MSWRLineBufAdd(lpLineBuf, new_buf);
+
+ keyDepth++;
+
+ /* Loop through the string values and print their data */
+ for(dwValIndex = 0L, dwValLen = MSWR_VAL_MAX + 1, dwDataLen = MSWR_DATA_MAX + 1;
+ RegEnumValue(hSubKey, dwValIndex, ValBuf, &dwValLen, NULL, &dwType,
+ DataBuf, &dwDataLen) == ERROR_SUCCESS;
+ dwValIndex++, dwValLen = MSWR_VAL_MAX + 1, dwDataLen = MSWR_DATA_MAX + 1){
+
+ /* print out value here */
+ for(i = 0, k = 0; i < keyDepth % 8; i++)
+ for(j = 0; j < tab_width; j++)
+ new_buf[k++] = ' ';
+ _sntprintf(new_buf+k, new_buf_len - k,
+ TEXT("%.*s = %.*s"),
+ dwValLen ? dwValLen : 128,
+ dwValLen ? ValBuf : TEXT("(Default)"),
+ dwType == REG_SZ && dwDataLen ? dwDataLen/sizeof(TCHAR) : 128,
+ (dwType == REG_SZ
+ ? (dwDataLen ? (LPTSTR)DataBuf : TEXT("(No data)"))
+ : TEXT("(Some non-string data)")));
+ new_buf[new_buf_len - 1] = '\0';
+ MSWRLineBufAdd(lpLineBuf, new_buf);
+ }
+
+ /* Loop through the subkeys and recursively print their data */
+ for(dwKeyIndex = 0L, dwKeyLen = MSWR_KEY_MAX + 1;
+ RegEnumKeyEx(hSubKey, dwKeyIndex, KeyBuf, &dwKeyLen,
+ NULL, NULL, NULL, &ftKeyTime) == ERROR_SUCCESS;
+ dwKeyIndex++, dwKeyLen = MSWR_KEY_MAX + 1){
+ MSWRDump(hSubKey, KeyBuf, keyDepth, lpLineBuf);
+ }
+ }
+ else {
+ /* Couldn't open the key. Must not be defined. */
+ for(i = 0, k = 0; i < keyDepth % 8; i++)
+ for(j = 0; j < tab_width; j++)
+ new_buf[k++] = ' ';
+ _sntprintf(new_buf+k, new_buf_len - k, TEXT("%s - Not Defined"), pSubKey);
+ new_buf[new_buf_len - 1] = '\0';
+ MSWRLineBufAdd(lpLineBuf, new_buf);
+ }
+
+ return 0;
+}
+
+
+/*
+ * Fundamental registry access function that removes a registry key
+ * and all its subkeys, their values and data
+ */
+LOCAL int
+MSWRClear(HKEY hKey, LPTSTR pSubKey)
+{
+ HKEY hSubKey;
+ TCHAR KeyBuf[MSWR_KEY_MAX+1];
+ DWORD dwKeyIndex, dwKeyLen;
+ FILETIME ftKeyTime;
+ int rv = 0;
+
+ if(RegOpenKeyEx(hKey, pSubKey, 0,
+ KEY_READ, &hSubKey) == ERROR_SUCCESS){
+ RegCloseKey(hSubKey);
+ if(RegOpenKeyEx(hKey, pSubKey, 0,
+ KEY_ALL_ACCESS, &hSubKey) == ERROR_SUCCESS){
+ for(dwKeyIndex = 0L, dwKeyLen = MSWR_KEY_MAX + 1;
+ RegEnumKeyEx(hSubKey, dwKeyIndex, KeyBuf, &dwKeyLen,
+ NULL, NULL, NULL, &ftKeyTime) == ERROR_SUCCESS;
+ dwKeyLen = MSWR_KEY_MAX + 1)
+ if(MSWRClear(hSubKey, KeyBuf)!= 0){
+ rv = -1;
+ dwKeyIndex++;
+ }
+ RegCloseKey(hSubKey);
+ if(RegDeleteKey(hKey, pSubKey) != ERROR_SUCCESS || rv)
+ return -1;
+ return 0;
+ }
+ return -1;
+ }
+ return 0;
+}
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Text display Windows.
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+/*
+ * Show a help message.
+ * Help text comes as a null terminated array of pointers to lines of
+ * text. Stuff these into a buffer and pass that to MessageBox.
+ */
+void
+mswin_showhelpmsg(WINHAND wnd, char **helplines)
+{
+ char **l;
+ char *helptext_utf8, *p;
+ size_t buflen;
+ HWND hWnd;
+ LPTSTR helptext_lpt;
+
+ hWnd = (HWND) wnd;
+ if(hWnd == NULL)
+ hWnd = ghTTYWnd;
+
+ buflen = 0;
+ for(l = helplines; *l != NULL; ++l)
+ buflen += (strlen(*l)+1);
+
+ helptext_utf8 = (char *) fs_get((buflen + 1) * sizeof(char));
+ if(helptext_utf8 == NULL)
+ return;
+
+ *helptext_utf8 = '\0';
+
+ p = helptext_utf8;
+ for(l = helplines; *l != NULL; ++l){
+ snprintf(p, buflen+1-(p-helptext_utf8), "%s%s", (p == helptext_utf8) ? "" : " ", *l);
+ p += strlen(p);
+ }
+
+ helptext_lpt = utf8_to_lptstr(helptext_utf8);
+
+ MessageBox(hWnd, helptext_lpt, TEXT("Help"),
+ MB_APPLMODAL | MB_ICONINFORMATION | MB_OK);
+
+ fs_give((void **) &helptext_utf8);
+ fs_give((void **) &helptext_lpt);
+}
+
+/*
+ * Callback for when new mail or imap telem window gets canned.
+ */
+LOCAL void
+mswin_tw_close_imap_telem_or_new_mail(MSWIN_TEXTWINDOW *mswin_tw)
+{
+ HMENU hMenu;
+
+ if((mswin_tw->id == IDM_OPT_IMAPTELEM) && gIMAPDebugOFFCallback)
+ (*gIMAPDebugOFFCallback)();
+
+ if(hMenu = GetMenu(ghTTYWnd))
+ CheckMenuItem (hMenu, mswin_tw->id, MF_BYCOMMAND | MF_UNCHECKED);
+}
+
+/*
+ * Callback for when new mail or imap telem window gets cleared.
+ */
+LOCAL void
+mswin_tw_clear_imap_telem_or_new_mail(MSWIN_TEXTWINDOW *mswin_tw)
+{
+ char *tmtxt;
+ time_t now = time((time_t *)0);
+ LPCTSTR desc = (mswin_tw->id == IDM_OPT_IMAPTELEM) ?
+ TEXT("IMAP telemetry recorder") :
+ TEXT("New Mail window");
+
+ tmtxt = ctime(&now);
+
+ mswin_tw_printf(mswin_tw, TEXT("%s started at %.*S"),
+ desc, MIN(100, strlen(tmtxt)-1), tmtxt);
+
+ if(mswin_tw->id == IDM_OPT_NEWMAILWIN)
+ {
+ int i;
+ int fromlen, subjlen, foldlen, len;
+ TCHAR ring2[256];
+
+ foldlen = (int)(.18 * gNMW_width);
+ foldlen = foldlen > 5 ? foldlen : 5;
+ fromlen = (int)(.28 * gNMW_width);
+ subjlen = gNMW_width - 2 - foldlen - fromlen;
+
+ mswin_tw_printf(mswin_tw,
+ TEXT(" %-*s%-*s%-*s"),
+ fromlen, TEXT("From:"),
+ subjlen, TEXT("Subject:"),
+ foldlen, TEXT("Folder:"));
+
+ len = 2 + fromlen + subjlen + foldlen;
+ if(len >= ARRAYSIZE(ring2) + 1)
+ len = ARRAYSIZE(ring2) - 2;
+ for(i = 0; i < len; i++)
+ ring2[i] = '-';
+ ring2[i] = '\0';
+ mswin_tw_puts_lptstr(mswin_tw, ring2);
+ }
+}
+
+/*
+ * Init new mail or imap telem window
+ */
+LOCAL int
+mswin_tw_init(MSWIN_TEXTWINDOW *mswin_tw, int id, LPCTSTR title)
+{
+ if(mswin_tw->hwnd){
+ /* destroy it */
+ mswin_tw_close(mswin_tw);
+ }
+ else{
+ HMENU hMenu;
+
+ mswin_tw->id = id;
+ mswin_tw->hInstance = ghInstance;
+ mswin_tw->print_callback = mswin_tw_print_callback;
+ mswin_tw->close_callback = mswin_tw_close_imap_telem_or_new_mail;
+ mswin_tw->clear_callback = mswin_tw_clear_imap_telem_or_new_mail;
+
+ // If the rcSize rect is empty, then init it to something resembling
+ // the size of the current Pine window. Otherwise we just re-use
+ // whatever the last position/size was.
+ if(IsRectEmpty(&mswin_tw->rcSize))
+ {
+ RECT cliRect;
+ RECT sizeRect;
+
+ GetClientRect(ghTTYWnd, &cliRect);
+ sizeRect.left = CW_USEDEFAULT;
+ sizeRect.top = CW_USEDEFAULT;
+ sizeRect.right = cliRect.right;
+ sizeRect.bottom = cliRect.bottom;
+ mswin_tw->rcSize = sizeRect;
+ }
+
+ if(!mswin_tw_create(mswin_tw, title))
+ return 1;
+
+ mswin_tw_setfont(mswin_tw, gpTTYInfo->hTTYFont);
+ mswin_tw_setcolor(mswin_tw, gpTTYInfo->rgbFGColor, gpTTYInfo->rgbBGColor);
+
+ mswin_tw_clear(mswin_tw);
+
+ if(id == IDM_OPT_IMAPTELEM)
+ {
+ if(gIMAPDebugONCallback)
+ (*gIMAPDebugONCallback)();
+
+ mswin_tw_showwindow(mswin_tw, SW_SHOWNA);
+ }
+ else if(id == IDM_OPT_NEWMAILWIN){
+ mswin_tw_showwindow(mswin_tw, SW_SHOW);
+ }
+
+ if(hMenu = GetMenu(ghTTYWnd))
+ CheckMenuItem (hMenu, mswin_tw->id, MF_BYCOMMAND | MF_CHECKED);
+ }
+
+ return(0);
+}
+
+/*
+ * Display text in a window.
+ *
+ * Parameters:
+ * title - Title of window.
+ * pText - address of text to display.
+ * textLen - Length of text, in bytes. Limited to 64K.
+ * pLines - Array of pointers to lines of text. Each
+ * line is a sepreate allocation block. The
+ * entry in the array of pointers should be a
+ * NULL.
+ *
+ * The text can be supplied as a buffer (pText and textLen) in which
+ * lines are terminated by CRLF (including the last line in buffer).
+ * This buffer should be NULL terminated, too.
+ * Or it can be supplied as a NULL terminated array of pointers to
+ * lines. Each entry points to a separately allocated memory block
+ * containing a null terminated string.
+ *
+ * If the function succeeds the memory containing the text will be
+ * used until the user closes the window, at which point it will be
+ * freed.
+ *
+ * Returns:
+ * mswin_tw - SUCCESS
+ * NULL - Failed.
+ */
+MSWIN_TEXTWINDOW *
+mswin_displaytext(char *title_utf8, char *pText_utf8, size_t npText,
+ char **pLines_utf8, MSWIN_TEXTWINDOW *mswin_tw, int flags)
+{
+ LPTSTR title_lpt = NULL, pText_lpt = NULL, *pLines_lpt = NULL;
+ char **l;
+ size_t pText_lpt_len = 0;
+ int i, count = 0;
+
+ if(pLines_utf8 != NULL){
+ for(count=0, l = pLines_utf8; *l != NULL; ++l)
+ ++count;
+
+ pLines_lpt = (LPTSTR *) fs_get((count + 1) * sizeof(LPTSTR));
+ memset(pLines_lpt, 0, (count + 1) * sizeof(LPTSTR));
+ for(i=0, l = pLines_utf8; *l != NULL && i < count; ++l, ++i)
+ pLines_lpt[i] = utf8_to_lptstr(*l);
+
+ /*caller expects this to be freed */
+ if(!(flags & MSWIN_DT_NODELETE)){
+ for(l = pLines_utf8; *l != NULL; ++l)
+ fs_give((void **) l);
+
+ fs_give((void **) &pLines_utf8);
+ }
+ }
+
+ if(pText_utf8 != NULL && npText > 0){
+ pText_lpt = utf8_to_lptstr(pText_utf8);
+ pText_lpt_len = lstrlen(pText_lpt);
+
+ /*caller expects this to be freed */
+ if(!(flags & MSWIN_DT_NODELETE))
+ fs_give((void **) &pText_utf8);
+ }
+
+ if(title_utf8 != NULL)
+ title_lpt = utf8_to_lptstr(title_utf8);
+
+ mswin_tw = mswin_tw_displaytext_lptstr(title_lpt, pText_lpt, pText_lpt_len,
+ pLines_lpt, mswin_tw, flags);
+
+ if(pLines_lpt != NULL){
+ for(i=0; i < count; ++i)
+ if(pLines_lpt[i])
+ fs_give((void **) &pLines_lpt[i]);
+
+ fs_give((void **) &pLines_lpt);
+ }
+
+ if(pText_lpt != NULL)
+ fs_give((void **) &pText_lpt);
+
+ if(title_lpt != NULL)
+ fs_give((void **) &title_lpt);
+
+ return(mswin_tw);
+}
+
+/*
+ * Callback for when a generic mswin_tw gets killed.
+ */
+LOCAL void
+mswin_tw_close_callback(MSWIN_TEXTWINDOW *mswin_tw)
+{
+ if(mswin_tw->id != -1)
+ MemFree(mswin_tw);
+}
+
+/*
+ * Create a new mswin_tw window. If the MSWIN_DT_USEALTWINDOW flag is set,
+ * then (re)use gMswinAltWin.
+ */
+LOCAL MSWIN_TEXTWINDOW *
+mswin_tw_displaytext_lptstr (LPTSTR title, LPTSTR pText, size_t textLen, LPTSTR *pLines,
+ MSWIN_TEXTWINDOW *mswin_tw, int flags)
+{
+ if (pText == NULL && pLines == NULL)
+ return (NULL);
+
+ /* Was a valid existing window supplied? */
+ if(!mswin_tw)
+ {
+ int ctrl_down = GetKeyState(VK_CONTROL) < 0;
+
+ if((flags & MSWIN_DT_USEALTWINDOW) && !ctrl_down)
+ {
+ mswin_tw = &gMswinAltWin;
+ mswin_tw->id = (UINT)-1; // Tell mswin_tw_close_callback not
+ // to free this buffer.
+ }
+ else
+ {
+ mswin_tw = (MSWIN_TEXTWINDOW *)MemAlloc (sizeof (MSWIN_TEXTWINDOW));
+ if(!mswin_tw)
+ return NULL;
+
+ memset(mswin_tw, 0, sizeof(MSWIN_TEXTWINDOW));
+ mswin_tw->id = 0;
+ }
+
+ mswin_tw->hInstance = ghInstance;
+ mswin_tw->print_callback = mswin_tw_print_callback;
+ mswin_tw->close_callback = mswin_tw_close_callback;
+ mswin_tw->clear_callback = NULL;
+ }
+
+ /* Create a new window. */
+ if (!mswin_tw->hwnd) {
+
+ if(IsRectEmpty(&mswin_tw->rcSize))
+ {
+ RECT sizeRect;
+ RECT cliRect;
+
+ GetClientRect(ghTTYWnd, &cliRect);
+ sizeRect.left = CW_USEDEFAULT;
+ sizeRect.top = CW_USEDEFAULT;
+ sizeRect.right = cliRect.right;
+ sizeRect.bottom = cliRect.bottom;
+ mswin_tw->rcSize = sizeRect;
+ }
+
+ if(!mswin_tw_create(mswin_tw, title)) {
+ MemFree (mswin_tw);
+ return (NULL);
+ }
+
+ mswin_tw_setfont(mswin_tw, gpTTYInfo->hTTYFont);
+ mswin_tw_setcolor(mswin_tw, gpTTYInfo->rgbFGColor, gpTTYInfo->rgbBGColor);
+ }
+ else {
+ /* Invalidate whole window, change title, and move to top. */
+ SetWindowText (mswin_tw->hwnd, title);
+ SetWindowPos (mswin_tw->hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+ }
+
+ mswin_tw_clear(mswin_tw);
+
+ /*
+ * How was text supplied?
+ */
+ if (pLines != NULL) {
+ LPTSTR *l;
+
+ /* Array of pointers to lines supplied. Count lines. */
+ for (l = pLines; *l != NULL; ++l){
+ mswin_tw_puts_lptstr(mswin_tw, *l);
+ }
+ }
+ else {
+#ifdef ALTED_DOT
+ if(flags & MSWIN_DT_FILLFROMFILE)
+ mswin_tw_fill_from_file(mswin_tw, pText);
+ else
+#endif /* ALTED_DOT */
+ /* Pointer to block of text supplied. */
+ mswin_tw_puts_lptstr(mswin_tw, pText);
+ }
+
+ mswin_tw_setsel(mswin_tw, 0, 0);
+
+ mswin_tw_showwindow(mswin_tw, SW_SHOW);
+ return mswin_tw;
+}
+
+void
+mswin_enableimaptelemetry(int state)
+{
+ HMENU hMenu;
+ MENUITEMINFO mitem;
+ TCHAR buf[256];
+ int i;
+
+ hMenu = GetMenu (ghTTYWnd);
+ if (hMenu == NULL)
+ return;
+
+ /*
+ * Make sure hMenu's the right menubar
+ */
+ mitem.cbSize = sizeof(MENUITEMINFO);
+ mitem.fMask = (MIIM_SUBMENU | MIIM_TYPE);
+ mitem.fType = MFT_STRING;
+ mitem.dwTypeData = buf;
+ mitem.cch = 255;
+ if(GetMenuItemCount(hMenu) == 7
+ && GetMenuItemInfo(hMenu, 5, TRUE, &mitem)){
+ if(mitem.fType == MFT_STRING
+ && !_tcscmp(mitem.dwTypeData, TEXT("&Config")))
+ hMenu = mitem.hSubMenu;
+ else
+ return;
+ }
+ else
+ return;
+
+ i = GetMenuItemCount(hMenu);
+ if(state == TRUE && i < 10){
+ mitem.fMask = MIIM_TYPE;
+ mitem.fType = MFT_SEPARATOR;
+ InsertMenuItem (hMenu, 8, TRUE, &mitem);
+
+ mitem.fMask = (MIIM_TYPE | MIIM_ID);
+ mitem.fType = MFT_STRING;
+ mitem.wID = IDM_OPT_IMAPTELEM;
+ mitem.dwTypeData = TEXT("&IMAP Telemetry");
+ mitem.cch = 15;
+ InsertMenuItem (hMenu, 9, TRUE, &mitem);
+
+ DrawMenuBar (ghTTYWnd);
+ }
+ else if(state == FALSE && i == 10){
+ DeleteMenu (hMenu, 8, MF_BYPOSITION);
+ DeleteMenu (hMenu, IDM_OPT_IMAPTELEM, MF_BYCOMMAND);
+ DrawMenuBar (ghTTYWnd);
+ }
+}
+
+int
+mswin_imaptelemetry(char *msg)
+{
+ if(gMswinIMAPTelem.hwnd){
+ LPTSTR msg_lptstr;
+ msg_lptstr = utf8_to_lptstr(msg);
+ mswin_tw_puts_lptstr(&gMswinIMAPTelem, msg_lptstr);
+ fs_give((void **) &msg_lptstr);
+ return(1);
+ }
+ return(0);
+}
+
+
+/*
+ * The newmail window has a format proportional to screen width.
+ * At this point, we've figured out the sizes of the fields, now
+ * we fill that field to it's desired column size. This used to
+ * be a lot eaier when it was one char per column, but in the
+ * unicode world it's not that easy. This code does make the
+ * assumption that ASCII characters are 1 column.
+ */
+LPTSTR
+format_newmail_string(LPTSTR orig_lptstr, int format_len)
+{
+ LPTSTR new_lptstr;
+ int i, colLen;
+
+ new_lptstr = (LPTSTR)MemAlloc((format_len+1)*sizeof(TCHAR));
+
+ /*
+ * Fill up string till we reach the format_len, we can backtrack
+ * if we need elipses.
+ */
+ for(i = 0, colLen = 0;
+ i < format_len && colLen < format_len && orig_lptstr && orig_lptstr[i];
+ i++){
+ new_lptstr[i] = orig_lptstr[i];
+ colLen += wcellwidth(new_lptstr[i]);
+ }
+
+ if(colLen > format_len || (colLen == format_len && orig_lptstr[i])){
+ /*
+ * If we hit the edge of the format and there's still stuff
+ * to write, go back and add ".."
+ */
+ i--;
+ if(wcellwidth(new_lptstr[i]) > 1){
+ colLen -= wcellwidth(new_lptstr[i]);
+ }
+ else{
+ colLen -= wcellwidth(new_lptstr[i]);
+ i--;
+ colLen -= wcellwidth(new_lptstr[i]);
+ }
+ while(colLen < format_len && i < format_len){
+ new_lptstr[i++] = '.';
+ colLen++;
+ }
+ }
+ else{
+ /*
+ * If we've hit the end of the string, add spaces until
+ * we get to the correct length.
+ */
+ for(; colLen < format_len && i < format_len; i++, colLen++){
+ new_lptstr[i] = ' ';
+ }
+ }
+
+ if(i <= format_len)
+ new_lptstr[i] = '\0';
+ else
+ new_lptstr[format_len] = '\0';
+
+ return(new_lptstr);
+}
+
+/*
+ * We're passed the relevant fields, now format them according to window with
+ * and put up for display
+ */
+int
+mswin_newmailwin(int is_us, char *from_utf8, char *subject_utf8, char *folder_utf8)
+{
+ TCHAR tcbuf[256];
+ int foldlen, fromlen, subjlen;
+ LPTSTR from_lptstr = NULL, subject_lptstr = NULL, folder_lptstr = NULL;
+ LPTSTR from_format, subject_format, folder_format;
+
+ if(!gMswinNewMailWin.hwnd)
+ return 0;
+
+ if(from_utf8)
+ from_lptstr = utf8_to_lptstr(from_utf8);
+ if(subject_utf8)
+ subject_lptstr = utf8_to_lptstr(subject_utf8);
+ if(folder_utf8)
+ folder_lptstr = utf8_to_lptstr(folder_utf8);
+
+
+ foldlen = (int)(.18 * gNMW_width);
+ foldlen = foldlen > 5 ? foldlen : 5;
+ fromlen = (int)(.28 * gNMW_width);
+ subjlen = gNMW_width - 2 - foldlen - fromlen;
+
+
+ from_format = format_newmail_string(from_lptstr
+ ? from_lptstr : TEXT(""),
+ fromlen - 1);
+ subject_format = format_newmail_string(subject_lptstr
+ ? subject_lptstr : TEXT("(no subject)"),
+ subjlen - 1);
+ folder_format = format_newmail_string(folder_lptstr
+ ? folder_lptstr : TEXT("INBOX"),
+ foldlen);
+
+ _sntprintf(tcbuf, 256, TEXT("%c %s %s %s"), is_us ? '+' : ' ',
+ from_format, subject_format, folder_format);
+
+ if(from_lptstr)
+ fs_give((void **) &from_lptstr);
+ if(subject_lptstr)
+ fs_give((void **) &subject_lptstr);
+ if(folder_lptstr)
+ fs_give((void **) &folder_lptstr);
+ MemFree((void *)from_format);
+ MemFree((void *)subject_format);
+ MemFree((void *)folder_format);
+
+ mswin_tw_puts_lptstr(&gMswinNewMailWin, tcbuf);
+ return 1;
+}
+
+/*
+ * Mouse up, end selection
+ */
+
+LOCAL void
+mswin_tw_print_callback(MSWIN_TEXTWINDOW *mswin_tw)
+{
+ LPTSTR text;
+ UINT text_len;
+ int rc;
+#define DESC_LEN 180
+ TCHAR description[DESC_LEN+1];
+
+ GetWindowText(mswin_tw->hwnd, description, DESC_LEN);
+
+ rc = mswin_print_ready((WINHAND)mswin_tw->hwnd, description);
+ if (rc != 0) {
+ if (rc != PE_USER_CANCEL) {
+ LPTSTR e;
+
+ e = utf8_to_lptstr(mswin_print_error(rc));
+ if(e){
+ _sntprintf(description, DESC_LEN+1, TEXT("Printing failed: %s"), e);
+ fs_give((void **) &e);
+ }
+
+ MessageBox(mswin_tw->hwnd, description, TEXT("Print Failed"),
+ MB_OK | MB_ICONEXCLAMATION);
+ }
+ return;
+ }
+
+ text_len = mswin_tw_gettextlength(mswin_tw);
+ text = (LPTSTR) fs_get(text_len * sizeof(TCHAR));
+ mswin_tw_gettext(mswin_tw, text, text_len);
+ mswin_print_text(text);
+ fs_give((void **)&text);
+
+ mswin_print_done();
+}
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Character Queue
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+
+typedef struct {
+ BOOL fKeyControlDown;
+ UCS c; /* Bigger than TCHAR for CTRL and MENU setting */
+} CQEntry;
+
+LOCAL CQEntry CQBuffer [CHARACTER_QUEUE_LENGTH];
+LOCAL int CQHead;
+LOCAL int CQTail;
+LOCAL int CQCount;
+
+
+/*---------------------------------------------------------------------------
+ * BOOL CQInit ()
+ *
+ * Description:
+ * Initialize the Character queue.
+ *
+ * Parameters:
+ *
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL void
+CQInit (void)
+{
+ CQHead = 0;
+ CQTail = 0;
+ CQCount = 0;
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL CQAvailable (void)
+ *
+ * Description:
+ * Return TRUE if there are characters in the queue.
+ *
+ * Parameters:
+ *
+ *
+/*--------------------------------------------------------------------------*/
+
+LOCAL BOOL
+CQAvailable (void)
+{
+ return (CQCount > 0);
+}
+
+
+
+/*---------------------------------------------------------------------------
+ * BOOL CQAdd (WORD c, DWORC keyData)
+ *
+ * Description:
+ * Add 'c' to the end of the character queue.
+ *
+ * Parameters:
+ * return true if successfull.
+ *
+/*--------------------------------------------------------------------------*/
+
+LOCAL BOOL
+CQAdd (UCS c, BOOL fKeyControlDown)
+{
+ if (CQCount == CHARACTER_QUEUE_LENGTH)
+ return (FALSE);
+
+ CQBuffer[CQTail].fKeyControlDown = fKeyControlDown;
+ CQBuffer[CQTail].c = c;
+ CQTail = (CQTail + 1) % CHARACTER_QUEUE_LENGTH;
+ ++CQCount;
+ return (TRUE);
+}
+
+
+
+/*---------------------------------------------------------------------------
+ * BOOL CQAddUniq (WORD c, DWORC keyData)
+ *
+ * Description:
+ * Add 'c' to the end of the character queue, only if
+ * there is no other 'c' in the queue
+ *
+ * Parameters:
+ * return true if successfull.
+ *
+/*--------------------------------------------------------------------------*/
+
+LOCAL BOOL
+CQAddUniq (UCS c, BOOL fKeyControlDown)
+{
+ int i;
+ int pos;
+
+ if (CQCount == CHARACTER_QUEUE_LENGTH)
+ return (FALSE);
+
+ pos = CQHead;
+ for (i = 0; i < CQCount; ++i) {
+ if (CQBuffer[pos].c == c)
+ return (FALSE);
+ pos = (pos + 1) % CHARACTER_QUEUE_LENGTH;
+ }
+ return (CQAdd (c, fKeyControlDown));
+}
+
+
+
+
+/*---------------------------------------------------------------------------
+ * int CQGet ()
+ *
+ * Description:
+ * Return the next byte from the head of the queue. If there is
+ * no byte available, returns 0, which is indistinquishable from
+ * '\0'. So it is a good idea to call CQAvailable first.
+ *
+ * Parameters:
+ * none.
+ *
+/*--------------------------------------------------------------------------*/
+
+LOCAL UCS
+CQGet ()
+{
+ UCS c;
+
+ if (CQCount == 0)
+ return (0);
+
+ c = CQBuffer[CQHead].c;
+
+ if(CQBuffer[CQHead].fKeyControlDown)
+ c |= CTRL;
+
+ if(c < ' ') {
+ c += '@';
+ c |= CTRL;
+ }
+
+ CQHead = (CQHead + 1) % CHARACTER_QUEUE_LENGTH;
+ --CQCount;
+ return (c);
+}
+
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Mouse Event Queue
+ *
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+
+
+LOCAL MEvent MQBuffer [MOUSE_QUEUE_LENGTH];
+LOCAL int MQHead;
+LOCAL int MQTail;
+LOCAL int MQCount;
+
+
+/*---------------------------------------------------------------------------
+ * BOOL MQInit ()
+ *
+ * Description:
+ * Initialize the Character queue.
+ *
+ * Parameters:
+ *
+ *
+/*--------------------------------------------------------------------------*/
+LOCAL void
+MQInit (void)
+{
+ MQHead = 0;
+ MQTail = 0;
+ MQCount = 0;
+}
+
+
+/*---------------------------------------------------------------------------
+ * BOOL MQAvailable (void)
+ *
+ * Description:
+ * Return TRUE if there are characters in the queue.
+ *
+ * Parameters:
+ *
+ *
+/*--------------------------------------------------------------------------*/
+
+LOCAL BOOL
+MQAvailable (void)
+{
+ return (MQCount > 0);
+}
+
+
+
+/*---------------------------------------------------------------------------
+ * BOOL MQAdd ()
+ *
+ * Description:
+ * Add 'c' to the end of the character queue.
+ *
+ * Parameters:
+ * return true if successfull.
+ *
+/*--------------------------------------------------------------------------*/
+
+LOCAL BOOL
+MQAdd (int mevent, int button, int nRow, int nColumn, int keys, int flags)
+{
+ int c;
+ int i = 0;
+ BOOL found = FALSE;
+
+
+ /*
+ * Find a queue insertion point.
+ */
+ if (flags & MSWIN_MF_REPLACING) {
+ /* Search for same event on queue. */
+ for ( i = MQHead, c = MQCount;
+ c > 0;
+ i = (i + 1) % MOUSE_QUEUE_LENGTH, --c) {
+ if (MQBuffer[i].event == mevent) {
+ found = TRUE;
+ break;
+ }
+ }
+ }
+ if (!found) {
+ if (MQCount == MOUSE_QUEUE_LENGTH)
+ return (FALSE);
+ i = MQTail;
+ MQTail = (MQTail + 1) % MOUSE_QUEUE_LENGTH;
+ ++MQCount;
+ }
+
+
+ /*
+ * Record data.
+ */
+ MQBuffer[i].event = mevent;
+ MQBuffer[i].button = button;
+ MQBuffer[i].nRow = nRow;
+ MQBuffer[i].nColumn = nColumn;
+ MQBuffer[i].keys = keys;
+ MQBuffer[i].flags = flags;
+
+ /*
+ * Keep record of last mouse position.
+ */
+ gMTEvent = MQBuffer[i];
+
+ return (TRUE);
+}
+
+
+
+
+
+/*---------------------------------------------------------------------------
+ * BOOL MQGet ()
+ *
+ * Description:
+ * Return the next byte from the head of the queue. If there is
+ * no byte available, returns 0, which is indistinquishable from
+ * '\0'. So it is a good idea to call MQAvailable first.
+ *
+ * Parameters:
+ * none.
+ *
+/*--------------------------------------------------------------------------*/
+
+LOCAL BOOL
+MQGet (MEvent * pMouse)
+{
+ if (MQCount == 0)
+ return (FALSE);
+
+
+ *pMouse = MQBuffer[MQHead];
+ MQHead = (MQHead + 1) % MOUSE_QUEUE_LENGTH;
+ --MQCount;
+ return (TRUE);
+}
+
+
+
+
+
+
+DWORD
+ExplainSystemErr()
+{
+ DWORD status;
+ void *lpMsgBuf = NULL;
+ status = GetLastError();
+
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ status,
+ MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
+ (LPTSTR) &lpMsgBuf, 0, NULL);
+
+ if(lpMsgBuf){
+ char *msg;
+
+ msg = lptstr_to_utf8(lpMsgBuf);
+ if(msg){
+ mswin_messagebox(msg, 1);
+ fs_give((void **) &msg);
+ }
+
+ LocalFree(lpMsgBuf);
+ }
+
+ return(status);
+}
+
+
+/*
+ * Called by mswin to scroll text in window in responce to the scrollbar.
+ *
+ * Args: cmd - what type of scroll operation.
+ * scroll_pos - paramter for operation.
+ * used as position for SCROLL_TO operation.
+ *
+ * Returns: TRUE - did the scroll operation.
+ * FALSE - was not able to do the scroll operation.
+ */
+int
+pico_scroll_callback (int cmd, long scroll_pos)
+{
+ switch (cmd) {
+ case MSWIN_KEY_SCROLLUPLINE:
+ scrollupline (0, 1);
+ break;
+
+ case MSWIN_KEY_SCROLLDOWNLINE:
+ scrolldownline (0, 1);
+ break;
+
+ case MSWIN_KEY_SCROLLUPPAGE:
+ backpage (0, 1);
+ break;
+
+ case MSWIN_KEY_SCROLLDOWNPAGE:
+ forwpage (0, 1);
+ break;
+
+ case MSWIN_KEY_SCROLLTO:
+ scrollto (0, 0);
+ break;
+ }
+
+ update ();
+ return (TRUE);
+}
+
+/*
+ * sleep the given number of seconds
+ */
+int
+sleep(int t)
+{
+ time_t out = (time_t)t + time((long *) 0);
+ while(out > time((long *) 0))
+ ;
+ return(0);
+}
+
+int
+tcsucmp(LPTSTR o, LPTSTR r)
+{
+ return(o ? (r ? _tcsicmp(o, r) : 1) : (r ? -1 : 0));
+}
+
+int
+tcsruncmp(LPTSTR o, LPTSTR r, int n)
+{
+ return(o ? (r ? _tcsnicmp(o, r, n) : 1) : (r ? -1 : 0));
+}
+
+int
+strucmp(char *o, char *r)
+{
+ return(o ? (r ? stricmp(o, r) : 1) : (r ? -1 : 0));
+}
+
+
+int
+struncmp(char *o, char *r, int n)
+{
+ return(o ? (r ? strnicmp(o, r, n) : 1) : (r ? -1 : 0));
+}
+
+
+/*
+ * Returns screen width of len characters of lpText.
+ * The width is in character cells. That is, an ascii "A" has
+ * width 1 but a CJK character probably has width 2.
+ */
+unsigned
+scrwidth(LPTSTR lpText, int len)
+{
+ UCS ubuf[1000];
+ unsigned w;
+ int i, thislen, offset;
+
+ offset = w = 0;
+
+ while(len > 0){
+ thislen = MIN(len, 1000);
+ for(i = 0; i < thislen; i++)
+ ubuf[i] = lpText[offset+i];
+
+ w += ucs4_str_width_ptr_to_ptr(&ubuf[0], &ubuf[thislen]);
+
+ offset += thislen;
+ len -= thislen;
+ }
+
+ return w;
+}
+
+
+/*
+ * Returns the index into pScreen given the row,col location
+ * on the screen, taking into account characters with widths
+ * other than 1 cell.
+ */
+long
+pscreen_offset_from_cord(int row, int col, PTTYINFO pTTYInfo)
+{
+ int offset_due_to_row, offset_due_to_col, width;
+
+ /*
+ * Each row starts at a specific offset into pScreen.
+ */
+ offset_due_to_row = row * pTTYInfo->actNColumn;
+
+ /*
+ * Start with col (all chars single width) and go from there.
+ * We need to find the offset that makes the string col wide.
+ * Hopefully we won't ever get a rectange that causes us to
+ * draw half characters, but in case we do we need to err on the
+ * side of too much width instead of not enough. It's a little
+ * tricky because we want to include following zero-width
+ * characters.
+ * fewer characters it would be <= desired width, one more would
+ * be greater than desired width, this one is >= desired width.
+ */
+
+ /* first go to some offset where width is > col */
+ offset_due_to_col = col;
+ width = scrwidth(pTTYInfo->pScreen+offset_due_to_row, offset_due_to_col);
+ while(width <= col && offset_due_to_col < pTTYInfo->actNColumn-1){
+ offset_due_to_col++;
+ width = scrwidth(pTTYInfo->pScreen+offset_due_to_row, offset_due_to_col);
+ }
+
+ /* Now back up until width is no longer > col */
+ while(width > col){
+ offset_due_to_col--;
+ width = scrwidth(pTTYInfo->pScreen+offset_due_to_row, offset_due_to_col);
+ }
+
+ /* But, if we're now less than col, go forward one again. */
+ if(width < col && offset_due_to_col < pTTYInfo->actNColumn-1)
+ offset_due_to_col++;
+
+ return(offset_due_to_row + offset_due_to_col);
+}
+
+
+/*
+ * Returns:
+ * 1 if store pass prompt is set in the registry to on
+ * 0 if set to off
+ * -1 if not set to anything
+ */
+int
+mswin_store_pass_prompt(void)
+{
+ /*
+ * We shouldn't need to prompt anymore, but always return 1
+ * just in case
+ */
+ return(1);
+}
diff --git a/pico/osdep/mswin.h b/pico/osdep/mswin.h
new file mode 100644
index 00000000..238d5fd7
--- /dev/null
+++ b/pico/osdep/mswin.h
@@ -0,0 +1,404 @@
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef MSWIN_H
+#define MSWIN_H
+
+
+#define __far
+#define _far
+#define __export
+
+/*
+ * Equivalent to a windows handle.
+ */
+typedef void *WINHAND;
+
+/*
+ * Mouse input events.
+ */
+typedef struct {
+ int event;
+ int button;
+ int nRow;
+ int nColumn;
+ int keys;
+ int flags;
+} MEvent;
+
+
+/*
+ * These define how mouse events get queued.
+ */
+#define MSWIN_MF_REPLACING 0x1000
+#define MSWIN_MF_REPEATING 0x2000
+
+
+typedef struct {
+ int ch;
+ int rval;
+ LPTSTR name;
+ LPTSTR label;
+ int id;
+} MDlgButton;
+
+
+
+/*
+ * Struct to tell pico how to build and what to return when
+ * it's asked to build a popup menu.
+ */
+typedef struct _popup {
+ enum {tIndex, tQueue, tMessage, tSubMenu,
+ tSeparator, tTail} type;
+ struct { /* menu's label */
+ char *string;
+ enum {lNormal = 0, lChecked, lDisabled} style;
+ } label;
+ union {
+ UCS val; /* Queue: inserted into input queue */
+ UCS msg; /* Message: gets posted to ghTTYWnd */
+ struct _popup *submenu; /* Submenu: array of submenu entries */
+ } data;
+ struct { /* Internal data */
+ int id;
+ } internal;
+} MPopup;
+
+
+
+/*
+ * Type of function expected by mswin_allowcopy and mswin_allowcopycut
+ * and used in EditDoCopyData
+ */
+typedef int (*getc_t)(int pos);
+
+/*
+ * Callback used to fetch text for display in alternate window. Text
+ * can be returned as either a pointer to a null terminated block of
+ * text (a string), with CRLF deliminating lines. OR as a pointer to
+ * an array of pointers to lines, each line being a null terminated
+ * string.
+ */
+typedef int (*gettext_t)(char *title, void **text, long *len, int *format);
+
+/*
+ * Type used by line up/down event handlers to move the body of the
+ * displayed text, by sort callback to get/set sort order, by
+ * header mode to get/set header state...
+ */
+typedef int (*cbarg_t)(int action, long args);
+
+
+/*
+ * Callback used for periodic callback.
+ */
+typedef void (*cbvoid_t)(void);
+typedef char *(*cbstr_t)(char *);
+
+
+#define GETTEXT_TITLELEN 128
+#define GETTEXT_TEXT 1 /* 'text' is pointer to text. */
+#define GETTEXT_LINES 2 /* 'text' is pointer to array of
+ * pointers to lines. */
+
+
+#ifndef PATH_MAX
+#define PATH_MAX 128 /* Max size of a directory path. */
+#endif
+
+/*
+ * Scroll callback values. Used to be mapped in the MSWIN_RANGE_START..END
+ * range, but now are mapped directly to keydefs.h values. Except two of
+ * them which don't have map values in keydefs.h so they're still in the
+ * MSWIN_RANGE_START..END 0x7000 range.
+ */
+#define MSWIN_KEY_SCROLLUPPAGE 0x7000
+#define MSWIN_KEY_SCROLLDOWNPAGE 0x7001
+#define MSWIN_KEY_SCROLLUPLINE KEY_SCRLUPL
+#define MSWIN_KEY_SCROLLDOWNLINE KEY_SCRLDNL
+#define MSWIN_KEY_SCROLLTO KEY_SCRLTO
+
+#define MSWIN_PASTE_DISABLE 0
+#define MSWIN_PASTE_FULL 1
+#define MSWIN_PASTE_LINE 2
+
+
+#define MSWIN_CURSOR_ARROW 0
+#define MSWIN_CURSOR_BUSY 1
+#define MSWIN_CURSOR_IBEAM 2
+#define MSWIN_CURSOR_HAND 3
+
+/*
+ * Flags for mswin_displaytext
+ */
+#define MSWIN_DT_NODELETE 0x0001 /* Don't delete text when
+ * window closes. */
+#define MSWIN_DT_USEALTWINDOW 0x0002 /* Put text in alt window if already
+ * open. Open if not. */
+#define MSWIN_DT_FILLFROMFILE 0x0004 /* pText_utf8 is a filename. */
+
+/*
+ * functions from mswin.c
+ */
+
+
+WINHAND mswin_gethinstance();
+WINHAND mswin_gethwnd ();
+void mswin_killsplash();
+void mswin_settitle(char *);
+int mswin_getmouseevent (MEvent * pMouse);
+void mswin_mousetrackcallback (cbarg_t cbfunc);
+int mswin_popup(MPopup *members);
+void mswin_keymenu_popup ();
+void mswin_registericon(int row, int id, char *file);
+void mswin_destroyicons();
+void mswin_finishicons();
+void mswin_show_icon (int x, int y, char *file);
+void mswin_setdebug (int debug, FILE *debugfile);
+void mswin_setnewmailwidth (int width);
+int mswin_setdndcallback (int (*cb)());
+int mswin_cleardndcallback (void);
+int mswin_setresizecallback (int (*cb)());
+int mswin_clearresizecallback (int (*cb)());
+void mswin_setdebugoncallback (cbvoid_t cbfunc);
+void mswin_setdebugoffcallback (cbvoid_t cbfunc);
+int mswin_setconfigcallback (cbvoid_t cffunc);
+int mswin_sethelpcallback (cbstr_t cbfunc);
+int mswin_setgenhelpcallback (cbstr_t cbfunc);
+void mswin_setclosetext (char *pCloseText);
+int mswin_setwindow (char *fontName, char *fontSize,
+ char *fontStyle, char *windowPosition,
+ char *cursorStyle, char *fontCharSet);
+int mswin_showwindow();
+int mswin_getwindow (char *fontName, size_t nfontName,
+ char *fontSize, size_t nfontSize,
+ char *fontStyle, size_t nfontStyle,
+ char *windowPosition, size_t nwindowPosition,
+ char *foreColor, size_t nforeColor,
+ char *backColor, size_t nbackColor,
+ char *cursorStyle, size_t ncursorStyle,
+ char *fontCharSet, size_t nfontCharSet);
+void mswin_noscrollupdate (int flag);
+void mswin_setscrollrange (long page, long max);
+void mswin_setscrollpos (long pos);
+long mswin_getscrollpos (void);
+long mswin_getscrollto (void);
+void mswin_setscrollcallback (cbarg_t cbfunc);
+void mswin_setsortcallback (cbarg_t cbfunc);
+void mswin_setflagcallback (cbarg_t cbfunc);
+void mswin_sethdrmodecallback (cbarg_t cbfunc);
+void mswin_setselectedcallback (cbarg_t cbfunc);
+void mswin_setzoomodecallback (cbarg_t cbfunc);
+void mswin_setfkeymodecallback (cbarg_t cbfunc);
+void mswin_setprintfont (char *fontName, char *fontSize,
+ char *fontStyle, char *fontCharSet);
+void mswin_getprintfont (char *, size_t, char *, size_t,
+ char *, size_t, char *, size_t);
+int mswin_yield (void);
+int mswin_charavail (void);
+UCS mswin_getc_fast (void);
+void mswin_flush_input (void);
+int mswin_showcursor (int show);
+int mswin_showcaret (int show);
+void mswin_trayicon (int show);
+int mswin_move (int row, int column);
+int mswin_getpos (int *row, int *column);
+int mswin_getscreensize (int *row, int *column);
+void mswin_minimize (void);
+int mswin_putblock (char *utf8_str, int strLen);
+int mswin_puts (char *utf8_str);
+int mswin_puts_n (char *utf8_str, int n);
+int mswin_putc (UCS c);
+int mswin_outc (char c);
+int mswin_rev (int state);
+int mswin_getrevstate (void);
+int mswin_bold (int state);
+int mswin_uline (int state);
+int mswin_eeol (void);
+int mswin_eeop (void);
+int mswin_beep (void);
+void mswin_pause (int);
+int mswin_flush (void);
+void mswin_setcursor (int);
+void mswin_messagebox (char *msg, int);
+void mswin_allowpaste (int);
+void mswin_allowcopycut (getc_t);
+void mswin_allowcopy (getc_t);
+void mswin_addclipboard (char *s);
+void mswin_allowmousetrack (int);
+int mswin_newmailicon (void);
+void mswin_newmailtext (char *t);
+void mswin_newmaildone (void);
+void mswin_mclosedtext (char *t);
+void mswin_menuitemclear (void);
+void mswin_menuitemadd (UCS key, char *label, int menuitem,
+ int flags);
+int mswin_setwindowmenu (int menu);
+int mswin_print_ready (WINHAND hWnd, LPTSTR docDesc);
+int mswin_print_done (void);
+char * mswin_print_error (int errorcode);
+int mswin_print_char (TCHAR c);
+int mswin_print_char_utf8 (int c);
+int mswin_print_text (LPTSTR text);
+int mswin_print_text_utf8 (char *text);
+int mswin_savefile (char *dir, int dirlen, char *fName, int nMaxFName);
+int mswin_openfile (char *dir, int nMaxDName, char *fName, int nMaxFName, char *extlist);
+int mswin_multopenfile (char *dir, int nMaxDName, char *fName, int nMaxFName, char *extlist);
+char *mswin_rgbchoice(char *pOldRGB);
+void mswin_killbuftoclip (getc_t copyfunc);
+void pico_popup();
+void mswin_paste_popup();
+int mswin_fflush (FILE *f);
+void mswin_setperiodiccallback (cbvoid_t periodiccb, long period);
+WINHAND mswin_inst2task (WINHAND hInst);
+int mswin_ontask_del (WINHAND hTask, char *path);
+int mswin_exec_and_wait (char *whatsit, char *command,
+ char *infile, char *outfile,
+ int *exit_val,
+ unsigned mseaw_flags);
+int mswin_shell_exec (char *command, WINHAND *childproc);
+void mswin_exec_err_msg (char *what, int status, char *buf,
+ size_t buflen);
+int mswin_onexit_del (char *path);
+int mswin_set_quit_confirm (int);
+void mswin_showhelpmsg (WINHAND hWnd, char **helplines);
+
+typedef struct MSWIN_TEXTWINDOW MSWIN_TEXTWINDOW;
+MSWIN_TEXTWINDOW *mswin_displaytext (char *title, char *pText, size_t textLen,
+ char **pLines, MSWIN_TEXTWINDOW *mswin_tw,
+ int flags);
+int mswin_imaptelemetry(char *msg);
+void mswin_enableimaptelemetry(int state);
+int mswin_newmailwin(int is_us, char *from,
+ char *subject, char *folder);
+int mswin_newmailwinon(void);
+
+char *mswin_reg_default_browser(char *url);
+int mswin_reg(int op, int tree, char *data, size_t size);
+int mswin_is_def_client(int type);
+int mswin_set_def_client(int type);
+char **mswin_reg_dump(void);
+int mswin_majorver();
+int mswin_minorver();
+char *mswin_compilation_date();
+char *mswin_compilation_remarks();
+char *mswin_specific_winver();
+
+
+int mswin_usedialog (void);
+int mswin_dialog(UCS *prompt, UCS *string, int field_len,
+ int append_current, int passwd,
+ MDlgButton *button_list, char **help, unsigned flags);
+int mswin_select(char *utf8prompt, MDlgButton *button_list,
+ int dflt, int on_ctrl_C, char **help, unsigned flags);
+int mswin_yesno(UCS *);
+int mswin_yesno_utf8(char *);
+
+BOOL MSWRShellCanOpen(LPTSTR key, char *cmdbuf, int clen, int allow_noreg);
+BOOL MSWRPeek(HKEY hRootKey, LPTSTR subkey, LPTSTR valstr,
+ LPTSTR data, DWORD *dlen);
+int mswin_store_pass_prompt(void);
+void mswin_set_erasecreds_callback(cbvoid_t);
+void mswin_setviewinwindcallback (cbvoid_t);
+int mswin_setgenhelptextcallback(cbstr_t);
+int mswin_caninput(void);
+void mswin_beginupdate(void);
+void mswin_endupdate(void);
+int mswin_sethelptextcallback(cbstr_t);
+int strucmp(char *, char *);
+int struncmp(char *, char *, int);
+
+#ifdef MSC_MALLOC
+/*
+ * These definitions will disable the SEGHEAP allocation routines, in
+ * favor of the compliler libraries usual allocators. This is useful
+ * when external debugging tools and the SEGHEAP debugging routines step
+ * on each other...
+ */
+#define MemAlloc(X) malloc(X)
+#define MemFree(X) free(X)
+#define MemRealloc(X,Y) realloc(X,Y)
+#define MemFreeAll()
+#define MemDebug(X,Y)
+#else
+/*
+ * Memory management stuff, from msmem.c
+ */
+typedef unsigned long MemSize;
+typedef void __far * MemPtr;
+
+#define MemAlloc(s) _MemAlloc (s, __FILE__, __LINE__)
+#define malloc(s) _MemAlloc (s, __FILE__, __LINE__)
+#define MemFree(b) _MemFree (b, __FILE__, __LINE__)
+#define free(b) _MemFree (b, __FILE__, __LINE__)
+#define MemRealloc(b,s) _MemRealloc (b, s, __FILE__, __LINE__)
+#define realloc(b,s) _MemRealloc (b, s, __FILE__, __LINE__)
+#define MEM_BLOCK_SIZE_MAX 0xff00
+
+void MemDebug (int debug, FILE *debugFile);
+void __far * _MemAlloc (MemSize size, char __far * file, int line);
+int _MemFree (void __far *block, char __far *file, int line);
+void __far * _MemRealloc (void __far *block, MemSize size,
+ char __far * file, int line);
+MemSize MemBlkSize (MemPtr block);
+void MemFreeAll (void);
+void MemFailSoon (MemSize);
+#endif
+
+/* functions from win moved to mswin.c */
+int pico_scroll_callback (int, long);
+#undef sleep
+int sleep (int);
+
+/*
+ * Signals that are not defined by MS C
+ */
+#define SIGHUP 1 /* Terminal hangup. */
+#define SIGINT 2 /* Ctrl-C sequence */
+#define SIGILL 4 /* illegal instruction - invalid function image */
+#define SIGSEGV 11 /* segment violation */
+#define SIGALRM 14 /* alarm clock */
+#define SIGTERM 15 /* Software termination signal from kill */
+#define SIGABRT 22 /* abnormal termination triggered by abort call */
+#define SIGWINCH 28 /* Change in window size. */
+
+
+#define fflush mswin_fflush
+
+#define alarm mswin_alarm
+
+/*
+ * Registry setting constants
+ */
+#define MSWR_PINE_RC 1
+#define MSWR_PINE_DIR 2
+#define MSWR_PINE_EXE 3
+#define MSWR_PINE_AUX 4
+#define MSWR_PINE_POS 5
+#define MSWR_PINE_CONF 6
+
+#define MSWR_SDC_MAIL 1
+#define MSWR_SDC_NEWS 2
+#define MSWR_SDC_NNTP 3
+#define MSWR_SDC_IMAP 4
+
+#define MSWR_NULL 0x00
+#define MSWR_OP_SET 0x01
+#define MSWR_OP_FORCE 0x02
+#define MSWR_OP_GET 0x04
+#define MSWR_OP_BLAST 0x08
+
+#endif /* MSWIN_H */
diff --git a/pico/osdep/mswin.ico b/pico/osdep/mswin.ico
new file mode 100644
index 00000000..d6b7af76
--- /dev/null
+++ b/pico/osdep/mswin.ico
Binary files differ
diff --git a/pico/osdep/mswin.rc b/pico/osdep/mswin.rc
new file mode 100644
index 00000000..5462b554
--- /dev/null
+++ b/pico/osdep/mswin.rc
@@ -0,0 +1,236 @@
+//Microsoft App Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#define APSTUDIO_HIDDEN_SYMBOLS
+#include "windows.h"
+#undef APSTUDIO_HIDDEN_SYMBOLS
+
+
+/////////////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+ALPINEICON ICON DISCARDABLE "PICO.ICO"
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Bitmap
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Cursor
+//
+
+PICOHAND CURSOR "MSWINHND.CUR"
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+ALPINEMENU MENU DISCARDABLE
+BEGIN
+ POPUP "&File"
+ BEGIN
+ MENUITEM "&Save File", IDM_MI_SAVEFILE
+ MENUITEM "&Read File", IDM_MI_READFILE
+ MENUITEM SEPARATOR
+ MENUITEM "E&xit", IDM_MI_EXIT
+ END
+ POPUP "&Edit"
+ BEGIN
+ MENUITEM "Cu&t\tCtrl-Shift-X", IDM_EDIT_CUT
+ MENUITEM "&Copy\tCtrl-Shift-C", IDM_EDIT_COPY
+ MENUITEM "Copy &Append\tCtrl-Alt-C", IDM_EDIT_COPY_APPEND
+ MENUITEM "&Paste\tCtrl-Shift-V", IDM_EDIT_PASTE
+ MENUITEM "Cance&l Paste!\tCtrl-Alt-V", IDM_EDIT_CANCEL_PASTE
+ MENUITEM SEPARATOR
+ MENUITEM "Select All", IDM_EDIT_SEL_ALL
+ MENUITEM SEPARATOR
+ MENUITEM "&Find\tCtrl-Shift-F", IDM_MI_WHEREIS
+ MENUITEM SEPARATOR
+ MENUITEM "&Spell", IDM_MI_SPELLCHK
+ END
+ POPUP "&Compose"
+ BEGIN
+ MENUITEM "Show Current &Position", IDM_MI_CURPOSITION
+ MENUITEM "&Where Is", IDM_MI_WHEREIS
+ MENUITEM "&Justify Paragraph", IDM_MI_JUSTIFY
+ END
+ POPUP "C&onfig"
+ BEGIN
+ MENUITEM "Set &Font", IDM_OPT_SETFONT
+ MENUITEM SEPARATOR
+ MENUITEM "&ToolBar", IDM_OPT_TOOLBAR
+ MENUITEM "Tool&bar on Top", IDM_OPT_TOOLBARPOS
+ POPUP "&Cursor"
+ BEGIN
+ MENUITEM "Block", IDM_OPT_CARETBLOCK
+ MENUITEM "Small Block", IDM_OPT_CARETSMALLBLOCK
+ MENUITEM "Underline", IDM_OPT_CARETHBAR
+ MENUITEM "Vertical Bar", IDM_OPT_CARETVBAR
+ END
+ MENUITEM SEPARATOR
+ MENUITEM "Use &Dialog Boxes", IDM_OPT_USEDIALOGS
+ END
+ POPUP "&Help"
+ BEGIN
+ MENUITEM "Screen Help", IDM_MI_SCREENHELP
+ MENUITEM "About", IDM_ABOUT
+ END
+END
+
+TEXTWINMENU MENU DISCARDABLE
+BEGIN
+ POPUP "&File"
+ BEGIN
+ MENUITEM "&Close", IDM_FILE_CLOSE
+ MENUITEM SEPARATOR
+ MENUITEM "&Print", IDM_FILE_PRINT
+ END
+END
+
+
+#include "mswin_spell.dlg"
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+ABOUTDLGBOX DIALOG DISCARDABLE 10, 25, 220, 80
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Pico"
+FONT 8, "Helv"
+BEGIN
+ ICON ALPINEICON,IDD_ABOUTICON,10,5,18,20
+ CTEXT "Pico for Windows%S\nVersion %d.%02d%S\nBuild (%S)",
+ IDD_VERSION,46,8,120,26,SS_NOPREFIX | NOT WS_GROUP
+ LTEXT "",IDD_BYLINE,45,40,172,56,SS_NOPREFIX | NOT WS_GROUP
+ DEFPUSHBUTTON "OK",IDD_OK,177,8,40,14
+ PUSHBUTTON "",IDC_BUTTON2,10,5,30,30,BS_ICON | NOT
+ WS_VISIBLE | WS_DISABLED,WS_EX_TRANSPARENT
+END
+
+IDD_TOOLBAR DIALOG DISCARDABLE 0, 0, 302, 12
+STYLE WS_CHILD | WS_VISIBLE
+FONT 8, "MS Sans Serif"
+BEGIN
+ PUSHBUTTON "Quit",IDM_MI_EXIT,1,0,25,11
+ PUSHBUTTON "Save",IDM_MI_SAVEFILE,26,0,26,11
+ PUSHBUTTON "Find",IDM_MI_WHEREIS,52,0,22,11
+ PUSHBUTTON "Spell",IDM_MI_SPELLCHK,74,0,26,11
+END
+
+#ifdef APSTUDIO_INVOKED
+//////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+0X00020000L TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#define APSTUDIO_HIDDEN_SYMBOLS\r\n"
+ "#include ""windows.h""\r\n"
+ "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n"
+ "\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+/////////////////////////////////////////////////////////////////////////////////////
+#endif // APSTUDIO_INVOKED
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_BYLINE "Copyright 2006-2009 University of Washington"
+ IDS_APPNAME "Pico"
+ IDS_APPIDENT "pico"
+END
+
+#ifndef _MAC
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 5,05,0,0
+ PRODUCTVERSION 5,05,0,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "Comments", "see http://www.washington.edu/pine\0"
+ VALUE "CompanyName", "University of Washington\0"
+ VALUE "FileDescription", "Pico\0"
+ VALUE "FileVersion", "5.05\0"
+ VALUE "InternalName", "pico\0"
+ VALUE "LegalCopyright", "Copyright 2006-2009\0"
+ VALUE "OriginalFilename", "pico.exe\0"
+ VALUE "ProductName", " pico\0"
+ VALUE "ProductVersion", "5.05\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+#endif // !_MAC
+
+#ifndef APSTUDIO_INVOKED
+////////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/pico/osdep/mswin_aspell.c b/pico/osdep/mswin_aspell.c
new file mode 100644
index 00000000..26e998da
--- /dev/null
+++ b/pico/osdep/mswin_aspell.c
@@ -0,0 +1,427 @@
+/*
+ * ========================================================================
+ * FILE: MSWIN_ASPELL.C
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+#include <system.h>
+#include "mswin_aspell.h"
+
+/*
+ * Aspell typedefs
+ */
+typedef struct AspellConfig AspellConfig;
+typedef struct AspellSpeller AspellSpeller;
+typedef struct AspellCanHaveError AspellCanHaveError;
+typedef struct AspellStringEnumeration AspellStringEnumeration;
+typedef struct AspellWordList AspellWordList;
+typedef struct AspellError AspellError;
+
+/*
+ * Aspell function prototypes
+ */
+typedef AspellCanHaveError * (__cdecl NEW_ASPELL_SPELLER)(AspellConfig * config);
+typedef AspellConfig * (__cdecl NEW_ASPELL_CONFIG)();
+typedef AspellSpeller * (__cdecl TO_ASPELL_SPELLER)(AspellCanHaveError * obj);
+typedef int (__cdecl ASPELL_CONFIG_REPLACE)(AspellConfig * ths, const char * key, const char * value);
+typedef void (__cdecl DELETE_ASPELL_CONFIG)(AspellConfig * ths);
+typedef const char * (__cdecl ASPELL_CONFIG_RETRIEVE)(AspellConfig * ths, const char * key);
+typedef int (__cdecl ASPELL_SPELLER_ADD_TO_PERSONAL)(AspellSpeller * ths, const char * word, int word_size);
+typedef int (__cdecl ASPELL_SPELLER_ADD_TO_SESSION)(AspellSpeller * ths, const char * word, int word_size);
+typedef int (__cdecl ASPELL_SPELLER_CHECK)(AspellSpeller * ths, const char * word, int word_size);
+typedef const AspellWordList * (__cdecl ASPELL_SPELLER_SUGGEST)(AspellSpeller * ths, const char * word, int word_size);
+typedef int (__cdecl ASPELL_SPELLER_SAVE_ALL_WORD_LISTS)(AspellSpeller * ths);
+typedef int (__cdecl ASPELL_SPELLER_STORE_REPLACEMENT)(AspellSpeller * ths, const char * mis, int mis_size, const char * cor, int cor_size);
+typedef const char * (__cdecl ASPELL_STRING_ENUMERATION_NEXT)(AspellStringEnumeration * ths);
+typedef AspellStringEnumeration *(__cdecl ASPELL_WORD_LIST_ELEMENTS)(const AspellWordList * ths);
+typedef void (__cdecl DELETE_ASPELL_SPELLER)(AspellSpeller * ths);
+typedef void (__cdecl DELETE_ASPELL_STRING_ENUMERATION)(AspellStringEnumeration * ths);
+typedef void (__cdecl DELETE_ASPELL_CAN_HAVE_ERROR)(AspellCanHaveError * ths);
+typedef const char * (__cdecl ASPELL_ERROR_MESSAGE)(const AspellCanHaveError * ths);
+typedef unsigned int (__cdecl ASPELL_ERROR_NUMBER)(const AspellCanHaveError * ths);
+typedef const AspellError * (__cdecl ASPELL_SPELLER_ERROR)(const AspellSpeller * ths);
+typedef const char * (__cdecl ASPELL_SPELLER_ERROR_MESSAGE)(const struct AspellSpeller * ths);
+typedef AspellConfig * (__cdecl ASPELL_SPELLER_CONFIG)(AspellSpeller * ths);
+
+/*
+ * MsWin speller information structure
+ */
+typedef struct ASPELLINFO
+{
+ AspellSpeller *speller;
+ AspellStringEnumeration *suggestion_elements;
+ const char *error_message;
+
+ AspellCanHaveError *err;
+
+ HMODULE mod_aspell;
+
+ NEW_ASPELL_SPELLER *new_aspell_speller;
+ NEW_ASPELL_CONFIG *new_aspell_config;
+ TO_ASPELL_SPELLER *to_aspell_speller;
+ ASPELL_CONFIG_REPLACE *aspell_config_replace;
+ DELETE_ASPELL_CONFIG *delete_aspell_config;
+ ASPELL_CONFIG_RETRIEVE *aspell_config_retrieve;
+ ASPELL_SPELLER_ADD_TO_PERSONAL *aspell_speller_add_to_personal;
+ ASPELL_SPELLER_ADD_TO_SESSION *aspell_speller_add_to_session;
+ ASPELL_SPELLER_CHECK *aspell_speller_check;
+ ASPELL_SPELLER_SUGGEST *aspell_speller_suggest;
+ ASPELL_SPELLER_SAVE_ALL_WORD_LISTS *aspell_speller_save_all_word_lists;
+ ASPELL_SPELLER_STORE_REPLACEMENT *aspell_speller_store_replacement;
+ ASPELL_STRING_ENUMERATION_NEXT *aspell_string_enumeration_next;
+ ASPELL_WORD_LIST_ELEMENTS *aspell_word_list_elements;
+ DELETE_ASPELL_SPELLER *delete_aspell_speller;
+ DELETE_ASPELL_STRING_ENUMERATION *delete_aspell_string_enumeration;
+ DELETE_ASPELL_CAN_HAVE_ERROR *delete_aspell_can_have_error;
+ ASPELL_ERROR_MESSAGE *aspell_error_message;
+ ASPELL_ERROR_NUMBER *aspell_error_number;
+ ASPELL_SPELLER_ERROR *aspell_speller_error;
+ ASPELL_SPELLER_ERROR_MESSAGE *aspell_speller_error_message;
+ ASPELL_SPELLER_CONFIG *aspell_speller_config;
+} ASPELLINFO;
+
+/*
+ * Find, aspell-15.dll, load it, and attempt to initialize our func pointers.
+ * 1:success, 0:failed
+ */
+static int
+speller_load_aspell_library(ASPELLINFO *aspellinfo)
+{
+ HKEY hKey;
+ int success = 1;
+ HMODULE mod_aspell = NULL;
+ TCHAR aspell_fullname[MAX_PATH + 1];
+ static const TCHAR aspell_name[] = TEXT("aspell-15.dll");
+
+ aspell_fullname[0] = '\0';
+
+ // Try to open the Aspell Registry key.
+ if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Aspell"),
+ 0, KEY_READ, &hKey) == ERROR_SUCCESS)
+ {
+ DWORD dtype;
+ TCHAR aspell_path[MAX_PATH + 1];
+ DWORD aspell_path_len = sizeof(aspell_path);
+
+ // Query the path.
+ if(RegQueryValueEx(hKey, TEXT("Path"), NULL, &dtype,
+ (LPBYTE)aspell_path, &aspell_path_len) == ERROR_SUCCESS)
+ {
+ _sntprintf(aspell_fullname, ARRAYSIZE(aspell_fullname),
+ TEXT("%s\\%s"), aspell_path, aspell_name);
+ aspell_fullname[ARRAYSIZE(aspell_fullname) - 1] = 0;
+ }
+
+ RegCloseKey(hKey);
+ }
+
+ // If the registry thing didn't work, then just try doing a Loadlibrary
+ // using the standard Windows LoadLibrary search gunk.
+ if(!aspell_fullname[0])
+ {
+ _tcsncpy(aspell_fullname, aspell_name, ARRAYSIZE(aspell_fullname));
+ aspell_fullname[ARRAYSIZE(aspell_fullname) - 1] = 0;
+ }
+
+ // Load the library.
+ mod_aspell = LoadLibrary(aspell_fullname);
+ if(!mod_aspell)
+ {
+ // Failed.
+ success = 0;
+ }
+ else
+ {
+ // Found the library - now try to initialize our function pointers.
+ //
+#define GET_ASPELL_PROC_ADDR(_functype, _func) \
+ aspellinfo->_func = (_functype *)GetProcAddress(mod_aspell, #_func); \
+ if(!aspellinfo->_func) \
+ success = 0
+
+ GET_ASPELL_PROC_ADDR(NEW_ASPELL_SPELLER, new_aspell_speller);
+ GET_ASPELL_PROC_ADDR(NEW_ASPELL_CONFIG, new_aspell_config);
+ GET_ASPELL_PROC_ADDR(TO_ASPELL_SPELLER, to_aspell_speller);
+ GET_ASPELL_PROC_ADDR(ASPELL_CONFIG_REPLACE, aspell_config_replace);
+ GET_ASPELL_PROC_ADDR(DELETE_ASPELL_CONFIG, delete_aspell_config);
+ GET_ASPELL_PROC_ADDR(ASPELL_CONFIG_RETRIEVE, aspell_config_retrieve);
+ GET_ASPELL_PROC_ADDR(ASPELL_SPELLER_ADD_TO_PERSONAL, aspell_speller_add_to_personal);
+ GET_ASPELL_PROC_ADDR(ASPELL_SPELLER_ADD_TO_SESSION, aspell_speller_add_to_session);
+ GET_ASPELL_PROC_ADDR(ASPELL_SPELLER_CHECK, aspell_speller_check);
+ GET_ASPELL_PROC_ADDR(ASPELL_SPELLER_SUGGEST, aspell_speller_suggest);
+ GET_ASPELL_PROC_ADDR(ASPELL_SPELLER_SAVE_ALL_WORD_LISTS, aspell_speller_save_all_word_lists);
+ GET_ASPELL_PROC_ADDR(ASPELL_SPELLER_STORE_REPLACEMENT, aspell_speller_store_replacement);
+ GET_ASPELL_PROC_ADDR(ASPELL_STRING_ENUMERATION_NEXT, aspell_string_enumeration_next);
+ GET_ASPELL_PROC_ADDR(ASPELL_WORD_LIST_ELEMENTS, aspell_word_list_elements);
+ GET_ASPELL_PROC_ADDR(DELETE_ASPELL_SPELLER, delete_aspell_speller);
+ GET_ASPELL_PROC_ADDR(DELETE_ASPELL_STRING_ENUMERATION, delete_aspell_string_enumeration);
+ GET_ASPELL_PROC_ADDR(DELETE_ASPELL_CAN_HAVE_ERROR, delete_aspell_can_have_error);
+ GET_ASPELL_PROC_ADDR(ASPELL_ERROR_MESSAGE, aspell_error_message);
+ GET_ASPELL_PROC_ADDR(ASPELL_ERROR_NUMBER, aspell_error_number);
+ GET_ASPELL_PROC_ADDR(ASPELL_SPELLER_ERROR, aspell_speller_error);
+ GET_ASPELL_PROC_ADDR(ASPELL_SPELLER_ERROR_MESSAGE, aspell_speller_error_message);
+ GET_ASPELL_PROC_ADDR(ASPELL_SPELLER_CONFIG, aspell_speller_config);
+
+#undef GET_ASPELL_PROC_ADDR
+
+ if(!success)
+ {
+ FreeLibrary(mod_aspell);
+ mod_aspell = NULL;
+ }
+ }
+
+ aspellinfo->mod_aspell = mod_aspell;
+ return success;
+}
+
+/*
+ * Find, load, and initialize the aspell library.
+ * NULL on failure, otherwise an ASPELLINFO pointer.
+ */
+ASPELLINFO *
+speller_init(char *lang)
+{
+ ASPELLINFO *aspellinfo;
+
+ aspellinfo = (ASPELLINFO *)malloc(sizeof(ASPELLINFO));
+ if(aspellinfo)
+ {
+ AspellConfig *config;
+ AspellSpeller *speller;
+
+ memset(aspellinfo, 0, sizeof(ASPELLINFO));
+
+ // Load the aspell library and set up our function pointers, etc.
+ if(!speller_load_aspell_library(aspellinfo))
+ {
+ speller_close(aspellinfo);
+ return NULL;
+ }
+
+ config = aspellinfo->new_aspell_config();
+
+ if(lang)
+ {
+ aspellinfo->aspell_config_replace(config, "lang", lang);
+ }
+
+ aspellinfo->aspell_config_replace(config, "encoding", "utf-8");
+
+ aspellinfo->err = aspellinfo->new_aspell_speller(config);
+ aspellinfo->delete_aspell_config(config);
+
+ if(aspellinfo->aspell_error_number(aspellinfo->err))
+ {
+ aspellinfo->error_message =
+ aspellinfo->aspell_error_message(aspellinfo->err);
+ }
+ else
+ {
+ aspellinfo->speller = aspellinfo->to_aspell_speller(aspellinfo->err);
+ aspellinfo->err = NULL;
+
+ /*
+ TODO: Log the current speller config somewhere?
+
+ config = aspell_speller_config(speller);
+
+ fputs("Using: ", stdout);
+ fputs(aspell_config_retrieve(config, "lang"), stdout);
+ fputs("-", stdout);
+ fputs(aspell_config_retrieve(config, "jargon"), stdout);
+ fputs("-", stdout);
+ fputs(aspell_config_retrieve(config, "size"), stdout);
+ fputs("-", stdout);
+ fputs(aspell_config_retrieve(config, "module"), stdout);
+ fputs("\n\n", stdout);
+ */
+ }
+ }
+
+ return aspellinfo;
+}
+
+/*
+ * Get the last aspell error message.
+ */
+const char *
+speller_get_error_message(ASPELLINFO *aspellinfo)
+{
+ return aspellinfo ? aspellinfo->error_message : NULL;
+}
+
+/*
+ * Close up shop.
+ */
+void
+speller_close(ASPELLINFO *aspellinfo)
+{
+ if(aspellinfo)
+ {
+ // Make sure someone hasn't missed a speller_suggestion_close
+ assert(!aspellinfo->suggestion_elements);
+
+ if(aspellinfo->err)
+ {
+ aspellinfo->delete_aspell_can_have_error(aspellinfo->err);
+ aspellinfo->err = NULL;
+ }
+
+ if(aspellinfo->speller)
+ {
+ aspellinfo->delete_aspell_speller(aspellinfo->speller);
+ aspellinfo->speller = NULL;
+ }
+
+ if(aspellinfo->mod_aspell)
+ {
+ FreeLibrary(aspellinfo->mod_aspell);
+ aspellinfo->mod_aspell = NULL;
+ }
+
+ free(aspellinfo);
+ }
+}
+
+/*
+ * Check the dictionary for a word.
+ * 1:found, 0:not found, -1:error
+ */
+int
+speller_check_word(ASPELLINFO *aspellinfo, const char *word, int word_size)
+{
+ int have = 1;
+
+ if(!isdigit(*word))
+ {
+ have = aspellinfo->aspell_speller_check(aspellinfo->speller, word, word_size);
+ if(have == -1)
+ {
+ aspellinfo->error_message =
+ aspellinfo->aspell_speller_error_message(aspellinfo->speller);
+ }
+
+ }
+
+ return have;
+}
+
+/*
+ * Add word to dictionary.
+ * 1:success, 0:failed
+ */
+int
+speller_add_to_dictionary(ASPELLINFO *aspellinfo, const char *word, int word_size)
+{
+ aspellinfo->aspell_speller_add_to_personal(aspellinfo->speller,
+ word, word_size);
+ if(aspellinfo->aspell_speller_error(aspellinfo->speller))
+ {
+ aspellinfo->error_message =
+ aspellinfo->aspell_speller_error_message(aspellinfo->speller);
+ return 0;
+ }
+
+ aspellinfo->aspell_speller_save_all_word_lists(aspellinfo->speller);
+ if(aspellinfo->aspell_speller_error(aspellinfo->speller))
+ {
+ aspellinfo->error_message =
+ aspellinfo->aspell_speller_error_message(aspellinfo->speller);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Add this word to the current spelling session.
+ * 1:success, 0:failed
+ */
+int
+speller_ignore_all(ASPELLINFO *aspellinfo, const char *word, int word_size)
+{
+ aspellinfo->aspell_speller_add_to_session(aspellinfo->speller, word, word_size);
+ if(aspellinfo->aspell_speller_error(aspellinfo->speller))
+ {
+ aspellinfo->error_message =
+ aspellinfo->aspell_speller_error_message(aspellinfo->speller);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Replace a word.
+ * 1:success, 0:failed
+ */
+int
+speller_replace_word(ASPELLINFO *aspellinfo,
+ const char * mis, int mis_size, const char * cor, int cor_size)
+{
+ int ret;
+
+ ret = aspellinfo->aspell_speller_store_replacement(aspellinfo->speller,
+ mis, mis_size, cor, cor_size);
+ if(ret == -1)
+ {
+ aspellinfo->error_message =
+ aspellinfo->aspell_speller_error_message(aspellinfo->speller);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Init the suggestions element array for a specific word.
+ * 1:success, 0:failed
+ */
+int
+speller_suggestion_init(ASPELLINFO *aspellinfo, const char *word, int word_size)
+{
+ const AspellWordList *suggestions;
+
+ // Make sure someone hasn't missed a speller_suggestion_close
+ assert(!aspellinfo->suggestion_elements);
+
+ suggestions = aspellinfo->aspell_speller_suggest(aspellinfo->speller,
+ word, word_size);
+ if(!suggestions)
+ {
+ aspellinfo->error_message =
+ aspellinfo->aspell_speller_error_message(aspellinfo->speller);
+ return 0;
+ }
+
+ aspellinfo->suggestion_elements =
+ aspellinfo->aspell_word_list_elements(suggestions);
+ return 1;
+}
+
+/*
+ * Find next suggestion. Returns NULL when no more are left.
+ */
+const char *
+speller_suggestion_getnext(ASPELLINFO *aspellinfo)
+{
+ return aspellinfo->aspell_string_enumeration_next(
+ aspellinfo->suggestion_elements);
+}
+
+/*
+ * Close the suggestion element array.
+ */
+void
+speller_suggestion_close(ASPELLINFO *aspellinfo)
+{
+ aspellinfo->delete_aspell_string_enumeration(aspellinfo->suggestion_elements);
+ aspellinfo->suggestion_elements = NULL;
+}
diff --git a/pico/osdep/mswin_aspell.h b/pico/osdep/mswin_aspell.h
new file mode 100644
index 00000000..623890ad
--- /dev/null
+++ b/pico/osdep/mswin_aspell.h
@@ -0,0 +1,30 @@
+/*
+ * ========================================================================
+ * FILE: MSWIN_ASPELL.H
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+typedef struct ASPELLINFO ASPELLINFO;
+
+ASPELLINFO *speller_init(char *lang);
+void speller_close(ASPELLINFO *aspellinfo);
+
+const char *speller_get_error_message(ASPELLINFO *aspellinfo);
+
+int speller_check_word(ASPELLINFO *aspellinfo, const char *word, int word_size);
+int speller_add_to_dictionary(ASPELLINFO *aspellinfo, const char *word, int word_size);
+int speller_ignore_all(ASPELLINFO *aspellinfo, const char *word, int word_size);
+int speller_replace_word(ASPELLINFO *aspellinfo, const char * mis, int mis_size,
+ const char * cor, int cor_size);
+
+int speller_suggestion_init(ASPELLINFO *aspellinfo, const char *word, int word_size);
+const char *speller_suggestion_getnext(ASPELLINFO *aspellinfo);
+void speller_suggestion_close(ASPELLINFO *aspellinfo);
+
diff --git a/pico/osdep/mswin_spell.DLG b/pico/osdep/mswin_spell.DLG
new file mode 100644
index 00000000..53e4aa5a
--- /dev/null
+++ b/pico/osdep/mswin_spell.DLG
@@ -0,0 +1,18 @@
+#include "mswin_spell.h"
+
+DLG_SPELL DIALOG 16, 34, 229, 92
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Dialog Title"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ COMBOBOX ID_COMBO_SUGGESTIONS, 4, 5, 79, 80, CBS_SIMPLE | WS_VSCROLL |
+ WS_TABSTOP | CBS_AUTOHSCROLL
+
+ DEFPUSHBUTTON "&Change", ID_BUTTON_CHANGE, 88, 8, 62, 14, WS_GROUP | WS_TABSTOP
+ PUSHBUTTON "Change &All", ID_BUTTON_CHANGEALL, 88, 27, 62, 14, WS_TABSTOP
+ PUSHBUTTON "&Ignore Once", ID_BUTTON_IGNOREONCE, 88, 46, 62, 14, WS_TABSTOP
+ PUSHBUTTON "I&gnore All", ID_BUTTON_IGNOREALL, 88, 65, 62, 14, WS_TABSTOP
+ PUSHBUTTON "Add to &Dictionary", ID_BUTTON_ADD_TO_DICT, 156, 8, 68, 14, WS_TABSTOP
+ PUSHBUTTON "Ca&ncel", ID_BUTTON_CANCEL, 156, 27, 68, 14, WS_TABSTOP
+END
diff --git a/pico/osdep/mswin_spell.c b/pico/osdep/mswin_spell.c
new file mode 100644
index 00000000..1379beb6
--- /dev/null
+++ b/pico/osdep/mswin_spell.c
@@ -0,0 +1,547 @@
+/*
+ * ========================================================================
+ * MSWIN_SPELL.C
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../headers.h"
+
+#include "mswin_aspell.h"
+#include "mswin_spell.h"
+
+#include <windowsx.h>
+
+typedef struct WORD_INFO
+{
+ ASPELLINFO *aspellinfo; // Aspell information
+
+ const char *utf8_err_message; // Error message, if any
+
+ char *word_utf8; // utf-8
+ LPTSTR word_lptstr;
+
+ int word_doto; // UCS
+ LINE *word_dotp; //
+ int word_size; //
+} WORD_INFO;
+
+extern HINSTANCE ghInstance;
+extern HWND ghTTYWnd;
+
+/*
+ * Function prototypes
+ */
+static int get_next_bad_word(WORD_INFO *word_info);
+static void free_word_info_words(WORD_INFO *word_info);
+static INT_PTR CALLBACK spell_dlg_proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
+
+/*
+ * spell() - check for potentially missspelled words and offer them for
+ * correction. Microsoft Windows specific version.
+ */
+int
+spell(int f, int n)
+{
+ int w_marko_bak;
+ LINE *w_markp_bak;
+ WORD_INFO word_info;
+ ASPELLINFO *aspellinfo;
+
+ emlwrite(_("Checking spelling..."), NULL); /* greetings! */
+
+ memset(&word_info, 0, sizeof(WORD_INFO));
+
+ aspellinfo = speller_init(NULL);
+ if(!aspellinfo ||
+ (word_info.utf8_err_message = speller_get_error_message(aspellinfo)))
+ {
+ if(word_info.utf8_err_message)
+ emlwrite((char *)word_info.utf8_err_message, NULL); /* sad greetings! */
+ else
+ emlwrite(_("Spelling: initializing Aspell failed"), NULL);
+
+ speller_close(aspellinfo);
+ return -1;
+ }
+
+ // Back up current mark.
+ w_marko_bak = curwp->w_marko;
+ w_markp_bak = curwp->w_markp;
+
+ setimark(0, 1);
+ gotobob(0, 1);
+
+ word_info.aspellinfo = aspellinfo;
+
+ if(get_next_bad_word(&word_info))
+ {
+ DialogBoxParam(ghInstance, MAKEINTRESOURCE(DLG_SPELL), ghTTYWnd,
+ spell_dlg_proc, (LPARAM)&word_info);
+ }
+
+ // Restore original mark if possible.
+ if(curwp->w_markp)
+ {
+ // Clear any set marks.
+ setmark(0, 1);
+
+ if(w_markp_bak && llength(w_markp_bak))
+ {
+ // Make sure we don't set mark off the line.
+ if(w_marko_bak >= llength(w_markp_bak))
+ w_marko_bak = llength(w_markp_bak) - 1;
+
+ curwp->w_marko = w_marko_bak;
+ curwp->w_markp = w_markp_bak;
+ }
+ }
+
+ if(word_info.utf8_err_message)
+ emlwrite((char *)word_info.utf8_err_message, NULL);
+ else
+ emlwrite(_("Done checking spelling"), NULL);
+
+ speller_close(aspellinfo);
+ free_word_info_words(&word_info);
+
+ swapimark(0, 1);
+ curwp->w_flag |= WFHARD|WFMODE;
+
+ update();
+ return 1;
+}
+
+/*
+ * handle_cb_editchange() - combobox contents have changed so enable/disable
+ * the Change and Change All buttons accordingly.
+ */
+static void
+handle_cb_editchange(HWND hDlg, HWND hwnd_combo)
+{
+ int text_len = ComboBox_GetTextLength(hwnd_combo);
+
+ Button_Enable(GetDlgItem(hDlg, ID_BUTTON_CHANGE), !!text_len);
+ Button_Enable(GetDlgItem(hDlg, ID_BUTTON_CHANGEALL), !!text_len);
+}
+
+/*
+ * spell_dlg_proc_set_word() - Set up the dialog to display spelling
+ * information and suggestions for the current word.
+ */
+static void
+spell_dlg_proc_set_word(HWND hDlg, WORD_INFO *word_info)
+{
+ TCHAR dlg_title[256];
+ HWND hwnd_combo = GetDlgItem(hDlg, ID_COMBO_SUGGESTIONS);
+
+ // Clear the combobox.
+ ComboBox_ResetContent(hwnd_combo);
+
+ // Set the dialog box title
+ _sntprintf(dlg_title, ARRAYSIZE(dlg_title), TEXT("Not in Dictionary: %s"),
+ word_info->word_lptstr);
+ dlg_title[ARRAYSIZE(dlg_title) - 1] = 0;
+ SetWindowText(hDlg, dlg_title);
+
+ // Populate the combobox with suggestions
+ if(speller_suggestion_init(word_info->aspellinfo, word_info->word_utf8, -1))
+ {
+ const char *suggestion_utf8;
+
+ while(suggestion_utf8 = speller_suggestion_getnext(word_info->aspellinfo))
+ {
+ LPTSTR suggestion_lptstr = utf8_to_lptstr((LPSTR)suggestion_utf8);
+
+ if(suggestion_lptstr)
+ {
+ ComboBox_AddString(hwnd_combo, suggestion_lptstr);
+ fs_give((void **)&suggestion_lptstr);
+ }
+ }
+
+ // Select the first one.
+ ComboBox_SetCurSel(hwnd_combo, 0);
+
+ speller_suggestion_close(word_info->aspellinfo);
+ }
+
+ handle_cb_editchange(hDlg, hwnd_combo);
+}
+
+/*
+ * spell_dlg_next_word() - Move to the next misspelled word and display
+ * the information for it. If no more bad words, kill the dialog.
+ */
+static void
+spell_dlg_next_word(HWND hDlg, WORD_INFO *word_info)
+{
+ if(!get_next_bad_word(word_info))
+ {
+ EndDialog(hDlg, 0);
+ return;
+ }
+
+ spell_dlg_proc_set_word(hDlg, word_info);
+}
+
+/*
+ * chword_n() - change the given word_len pointed to by the curwp->w_dot
+ * pointers to the word in cb
+ */
+static void
+chword_n(int wb_len, UCS *cb)
+{
+ ldelete(wb_len, NULL); /* not saved in kill buffer */
+
+ while(*cb != '\0')
+ linsert(1, *cb++);
+
+ curwp->w_flag |= WFEDIT;
+}
+
+/*
+ * replace_all() - replace all instances of oldword with newword from
+ * the current '.' on. Mark is where curwp will get restored to.
+ */
+static void
+replace_all(UCS *oldword, int oldword_len, UCS *newword)
+{
+ int wrap;
+
+ // Mark should be at "." right now - ie the start of the word we're
+ // checking. We're going to use mark below to restore curwp since
+ // chword_n can potentially realloc LINES and it knows about markp.
+ assert(curwp->w_markp);
+ assert(curwp->w_markp == curwp->w_dotp);
+ assert(curwp->w_marko == curwp->w_doto);
+
+ curwp->w_bufp->b_mode |= MDEXACT; /* case sensitive */
+ while(forscan(&wrap, oldword, NULL, 0, 1))
+ {
+ if(wrap)
+ break; /* wrap NOT allowed! */
+
+ /*
+ * We want to minimize the number of substrings that we report
+ * as matching a misspelled word...
+ */
+ if(lgetc(curwp->w_dotp, 0).c != '>')
+ {
+ LINE *lp = curwp->w_dotp; /* for convenience */
+ int off = curwp->w_doto;
+
+ if(off == 0 || !ucs4_isalpha(lgetc(lp, off - 1).c))
+ {
+ off += oldword_len;
+
+ if(off == llength(lp) || !ucs4_isalpha(lgetc(lp, off).c))
+ {
+ // Replace this word
+ chword_n(oldword_len, newword);
+ }
+ }
+ }
+
+ forwchar(0, 1); /* move on... */
+
+ }
+ curwp->w_bufp->b_mode ^= MDEXACT; /* case insensitive */
+
+ curwp->w_dotp = curwp->w_markp;
+ curwp->w_doto = curwp->w_marko;
+}
+
+/*
+ * handle_button_change() - Someone pressed the Change or Change All button.
+ * So deal with it.
+ */
+static void
+handle_button_change(HWND hDlg, WORD_INFO *word_info, int do_replace_all)
+{
+ int str_lptstr_len;
+ TCHAR str_lptstr[128];
+
+ // Get the length of what they want to change to.
+ str_lptstr_len = ComboBox_GetText(GetDlgItem(hDlg, ID_COMBO_SUGGESTIONS),
+ str_lptstr, ARRAYSIZE(str_lptstr));
+ if(str_lptstr_len)
+ {
+ UCS *str_ucs4;
+ char *str_utf8;
+
+ // Notify the speller so it'll suggest this word next time around.
+ str_utf8 = lptstr_to_utf8(str_lptstr);
+ if(str_utf8)
+ {
+ speller_replace_word(word_info->aspellinfo,
+ word_info->word_utf8, -1, str_utf8, -1);
+ fs_give((void **)&str_utf8);
+ }
+
+ str_ucs4 = lptstr_to_ucs4(str_lptstr);
+ if(!str_ucs4)
+ {
+ // Uh oh - massive error.
+ word_info->utf8_err_message = _("Spelling: lptstr_to_ucs4() failed");
+ EndDialog(hDlg, -1);
+ return;
+ }
+
+ // Move back to start of this word.
+ curwp->w_doto = word_info->word_doto;
+ curwp->w_dotp = word_info->word_dotp;
+
+ if(do_replace_all)
+ {
+ int i;
+ int word_size;
+ UCS word[128];
+
+ word_size = word_info->word_size;
+ if(word_size >= ARRAYSIZE(word) - 1)
+ word_size = ARRAYSIZE(word) - 1;
+
+ for(i = 0; i < word_size; i++)
+ word[i] = lgetc(curwp->w_dotp, curwp->w_doto + i).c;
+
+ word[i] = 0;
+
+ replace_all(word, word_size, str_ucs4);
+
+ // Skip over the word we just inserted.
+ forwchar(FALSE, (int)ucs4_strlen(str_ucs4));
+ }
+ else
+ {
+ // Swap it with the new improved version.
+ chword_n(word_info->word_size, str_ucs4);
+ }
+ fs_give((void **)&str_ucs4);
+ }
+
+ update();
+ spell_dlg_next_word(hDlg, word_info);
+}
+
+/*
+ * center_dialog() - Center a dialog with respect to its parent.
+ */
+static void
+center_dialog(HWND hDlg)
+{
+ int dx;
+ int dy;
+ RECT rcDialog;
+ RECT rcParent;
+
+ GetWindowRect(GetParent(hDlg), &rcParent);
+ GetWindowRect(hDlg, &rcDialog);
+
+ dx = ((rcParent.right - rcParent.left) -
+ (rcDialog.right - rcDialog.left)) / 2 + rcParent.left - rcDialog.left;
+
+ dy = ((rcParent.bottom - rcParent.top) -
+ (rcDialog.bottom - rcDialog.top)) / 2 + rcParent.top - rcDialog.top;
+
+ OffsetRect(&rcDialog, dx, dy);
+
+ SetWindowPos(hDlg, NULL, rcDialog.left, rcDialog.top, 0, 0,
+ SWP_NOSIZE | SWP_NOZORDER);
+}
+
+/*
+ * spell_dlg_on_command() - Handle the WM_COMMAND stuff for the spell dialog.
+ */
+static void
+spell_dlg_on_command(HWND hDlg, int id, HWND hwndCtl, UINT codeNotify)
+{
+ WORD_INFO *word_info =
+ (WORD_INFO *)(LONG_PTR)GetWindowLongPtr(hDlg, GWLP_USERDATA);
+
+ switch(id)
+ {
+ case ID_COMBO_SUGGESTIONS:
+ if(codeNotify == CBN_EDITCHANGE)
+ handle_cb_editchange(hDlg, hwndCtl);
+ break;
+
+ case ID_BUTTON_CHANGE:
+ handle_button_change(hDlg, word_info, 0);
+ break;
+
+ case ID_BUTTON_CHANGEALL:
+ handle_button_change(hDlg, word_info, 1);
+ break;
+
+ case ID_BUTTON_IGNOREONCE:
+ spell_dlg_next_word(hDlg, word_info);
+ break;
+
+ case ID_BUTTON_IGNOREALL:
+ speller_ignore_all(word_info->aspellinfo, word_info->word_utf8, -1);
+ spell_dlg_next_word(hDlg, word_info);
+ break;
+
+ case ID_BUTTON_ADD_TO_DICT:
+ speller_add_to_dictionary(word_info->aspellinfo, word_info->word_utf8, -1);
+ spell_dlg_next_word(hDlg, word_info);
+ break;
+
+ case ID_BUTTON_CANCEL:
+ EndDialog(hDlg, 0);
+ break;
+ }
+}
+
+/*
+ * spell_dlg_proc() - Main spell dialog proc.
+ */
+static INT_PTR CALLBACK
+spell_dlg_proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ WORD_INFO *word_info;
+
+ switch(message)
+ {
+ case WM_INITDIALOG:
+ center_dialog(hDlg);
+
+ // Limit how much junk they can type in.
+ ComboBox_LimitText(GetDlgItem(hDlg, ID_COMBO_SUGGESTIONS), 128);
+
+ word_info = (WORD_INFO *)lParam;
+ SetWindowLongPtr(hDlg, GWLP_USERDATA, (LONG_PTR)word_info);
+
+ spell_dlg_proc_set_word(hDlg, word_info);
+ return TRUE;
+
+ case WM_COMMAND:
+ HANDLE_WM_COMMAND(hDlg, wParam, lParam, spell_dlg_on_command);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*
+ * get_next_word() - Get the next word from dot. And skip words in
+ * quoted lines.
+ */
+static int
+get_next_word(WORD_INFO *word_info)
+{
+ // Skip quoted lines
+ while(lgetc(curwp->w_dotp, 0).c == '>')
+ {
+ if(curwp->w_dotp == curbp->b_linep)
+ return 0;
+
+ curwp->w_dotp = lforw(curwp->w_dotp);
+ curwp->w_doto = 0;
+ curwp->w_flag |= WFMOVE;
+ }
+
+ // Move to a word.
+ while(inword() == FALSE)
+ {
+ if(forwchar(FALSE, 1) == FALSE)
+ {
+ // There is no next word.
+ return 0;
+ }
+ }
+
+ // If mark is currently set, clear it.
+ if(curwp->w_markp)
+ setmark(0, 1);
+
+ // Set mark to beginning of this word.
+ curwp->w_markp = curwp->w_dotp;
+ curwp->w_marko = curwp->w_doto;
+
+ // Get word length
+ word_info->word_doto = curwp->w_doto;
+ word_info->word_dotp = curwp->w_dotp;
+ word_info->word_size = 0;
+
+ while((inword() != FALSE) && (word_info->word_dotp == curwp->w_dotp))
+ {
+ word_info->word_size++;
+
+ if(forwchar(FALSE, 1) == FALSE)
+ break;
+ }
+
+ word_info->word_utf8 = ucs4_to_utf8_cpystr_n(
+ (UCS *)&word_info->word_dotp->l_text[word_info->word_doto],
+ word_info->word_size);
+ return 1;
+}
+
+/*
+ * get_next_word() - free the word_info members word_utf8 and word_lptstr.
+ */
+static void
+free_word_info_words(WORD_INFO *word_info)
+{
+ if(word_info->word_utf8)
+ {
+ fs_give((void **)&word_info->word_utf8);
+ word_info->word_utf8 = NULL;
+ }
+
+ if(word_info->word_lptstr)
+ {
+ fs_give((void **)&word_info->word_lptstr);
+ word_info->word_lptstr = NULL;
+ }
+}
+
+/*
+ * get_next_bad_word() - Search from '.' for the next word we think is
+ * rotten. Mark it and highlight it.
+ */
+static int
+get_next_bad_word(WORD_INFO *word_info)
+{
+ free_word_info_words(word_info);
+
+ while(get_next_word(word_info))
+ {
+ int ret;
+
+ /* pass over words that contain @ */
+ if(strchr(word_info->word_utf8, '@'))
+ ret = 1;
+ else
+ ret = speller_check_word(word_info->aspellinfo, word_info->word_utf8, -1);
+
+ if(ret == -1)
+ {
+ word_info->utf8_err_message =
+ speller_get_error_message(word_info->aspellinfo);
+ if(!word_info->utf8_err_message)
+ word_info->utf8_err_message = _("Spelling: speller_check_word() failed");
+ return 0;
+ }
+ else if(ret == 0)
+ {
+ // Highlight word.
+ update();
+
+ word_info->word_lptstr = utf8_to_lptstr(word_info->word_utf8);
+ return 1;
+ }
+
+ free_word_info_words(word_info);
+ }
+
+ return 0;
+}
diff --git a/pico/osdep/mswin_spell.h b/pico/osdep/mswin_spell.h
new file mode 100644
index 00000000..bfa78116
--- /dev/null
+++ b/pico/osdep/mswin_spell.h
@@ -0,0 +1,9 @@
+#define DLG_SPELL 300
+
+#define ID_COMBO_SUGGESTIONS 100
+#define ID_BUTTON_CHANGE IDOK
+#define ID_BUTTON_CHANGEALL 101
+#define ID_BUTTON_IGNOREONCE 102
+#define ID_BUTTON_IGNOREALL 103
+#define ID_BUTTON_ADD_TO_DICT 104
+#define ID_BUTTON_CANCEL IDCANCEL
diff --git a/pico/osdep/mswin_tw.c b/pico/osdep/mswin_tw.c
new file mode 100644
index 00000000..663c8141
--- /dev/null
+++ b/pico/osdep/mswin_tw.c
@@ -0,0 +1,765 @@
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+#define STRICT
+#define UNICODE
+#define _UNICODE
+#include <windows.h>
+#include <windowsx.h>
+#include <tchar.h>
+
+/*
+ article entitled "About Rich Edit Controls" here (remove all spaces from url):
+ http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/
+ platform/commctls/richedit/richeditcontrols/aboutricheditcontrols.asp
+
+ Rich Edit version DLL
+ 1.0 Riched32.dll
+ 2.0 Riched20.dll
+ 3.0 Riched20.dll
+ 4.1 Msftedit.dll
+
+ The following list describes which versions of Rich Edit are included in
+ which releases of Microsoft Windows.
+ Windows XP SP1 Includes Rich Edit 4.1, Rich Edit 3.0, and a Rich Edit 1.0 emulator.
+ Windows XP Includes Rich Edit 3.0 with a Rich Edit 1.0 emulator.
+ Windows Me Includes Rich Edit 1.0 and 3.0.
+ Windows 2000 Includes Rich Edit 3.0 with a Rich Edit 1.0 emulator.
+ Windows NT 4.0 Includes Rich Edit 1.0 and 2.0.
+ Windows 98 Includes Rich Edit 1.0 and 2.0.
+ Windows 95 Includes only Rich Edit 1.0. However, Riched20.dll is
+ compatible with Windows 95 and may be installed by an
+ application that requires it.
+
+ We're using richedit v2 since it is the first to have Unicode support. Does
+ potentially limit us to Win98 unless we install riched20.dll or it's
+ already there.
+ */
+#define _RICHEDIT_VER 0x0200
+#include <richedit.h>
+#include "resource.h"
+
+#include "mswin_tw.h"
+
+/*
+ * Globals
+ */
+static const TCHAR g_mswin_tw_class_name[] = TEXT("PineTWClass");
+
+// Maximum amount of text allowed in these textwindows.
+// Set via a EM_EXLIMITTEXT message.
+static const LPARAM g_max_text = 8 * 1024 * 1024 - 1;
+
+/*
+ * Function prototypes
+ */
+static LRESULT CALLBACK mswin_tw_wndproc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
+static UINT Edit_ExSetSel(HWND hwnd_edit, LONG cpMin, LONG cpMax);
+static UINT Edit_ExGetTextLen(HWND hwnd_edit, DWORD flags);
+static BOOL Edit_ExIsReadOnly(HWND hwnd_edit);
+
+/*
+ * mswin_tw_create() - Create a mswin textwindow.
+ */
+int
+mswin_tw_create(MSWIN_TEXTWINDOW *mswin_tw, LPCTSTR title)
+{
+ int width, height;
+ static int s_mswin_tw_class_registered = 0;
+
+ mswin_tw->hwnd = NULL;
+ mswin_tw->hwnd_edit = NULL;
+
+ if(!s_mswin_tw_class_registered)
+ {
+ WNDCLASS wndclass;
+
+ LoadLibrary(TEXT("riched20.dll"));
+
+ memset(&wndclass, 0, sizeof(wndclass));
+ wndclass.style = CS_BYTEALIGNWINDOW;
+ wndclass.lpfnWndProc = mswin_tw_wndproc;
+ wndclass.cbClsExtra = 0;
+ wndclass.cbWndExtra = sizeof(ULONG_PTR);
+ wndclass.hInstance = mswin_tw->hInstance ;
+ wndclass.hIcon = LoadIcon (mswin_tw->hInstance, MAKEINTRESOURCE( ALPINEICON));
+ wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
+ wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
+ wndclass.lpszMenuName = MAKEINTRESOURCE(TEXTWINMENU);
+ wndclass.lpszClassName = g_mswin_tw_class_name ;
+
+ RegisterClass(&wndclass);
+
+ s_mswin_tw_class_registered = 1;
+ }
+
+ // If we're passed CW_USEDEFAULT as the position, then use right/bottom
+ // as the width/height. Otherwise use the full rect.
+ width = (mswin_tw->rcSize.left == CW_USEDEFAULT) ?
+ mswin_tw->rcSize.right : mswin_tw->rcSize.right - mswin_tw->rcSize.left;
+ height = (mswin_tw->rcSize.top == CW_USEDEFAULT) ?
+ mswin_tw->rcSize.bottom : mswin_tw->rcSize.bottom - mswin_tw->rcSize.top;
+
+ mswin_tw->hwnd = CreateWindow(
+ g_mswin_tw_class_name, title,
+ WS_OVERLAPPEDWINDOW,
+ mswin_tw->rcSize.left,
+ mswin_tw->rcSize.top,
+ width,
+ height,
+ HWND_DESKTOP, NULL,
+ mswin_tw->hInstance, mswin_tw);
+ if(!mswin_tw->hwnd)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * mswin_tw_close() - close the mswin textwindow.
+ */
+void
+mswin_tw_close(MSWIN_TEXTWINDOW *mswin_tw)
+{
+ if(mswin_tw && mswin_tw->hwnd)
+ DestroyWindow(mswin_tw->hwnd);
+}
+
+/*
+ * EditStreamOutCallback - callback for mswin_tw_write_to_file.
+ */
+static DWORD CALLBACK
+EditStreamOutCallback(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, PLONG pcb)
+{
+ HANDLE hFile = (HANDLE)dwCookie;
+ return !WriteFile(hFile, lpBuff, cb, (DWORD *)pcb, NULL);
+}
+
+/*
+ * mswin_tw_fill_from_file() - write textwindow contents to a file.
+ */
+BOOL
+mswin_tw_write_to_file(MSWIN_TEXTWINDOW *mswin_tw, LPCTSTR file)
+{
+ BOOL fSuccess = FALSE;
+ HANDLE hFile = CreateFile(file, GENERIC_WRITE, 0,
+ 0, OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+ if(hFile != INVALID_HANDLE_VALUE)
+ {
+ EDITSTREAM es;
+
+ es.dwCookie = (DWORD_PTR)hFile;
+ es.dwError = 0;
+ es.pfnCallback = EditStreamOutCallback;
+
+ if(SendMessage(mswin_tw->hwnd_edit, EM_STREAMOUT, SF_TEXT, (LPARAM)&es) &&
+ es.dwError == 0)
+ {
+ fSuccess = TRUE;
+ }
+ }
+
+ CloseHandle(hFile);
+ return fSuccess;
+}
+
+
+#ifdef ALTED_DOT
+
+/*
+ * EditStreamCallback - callback for mswin_tw_fill_from_file.
+ */
+static DWORD CALLBACK
+EditStreamInCallback(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, PLONG pcb)
+{
+ HANDLE hFile = (HANDLE)dwCookie;
+ return !ReadFile(hFile, lpBuff, cb, (DWORD *)pcb, NULL);
+}
+
+
+/*
+ * mswin_tw_fill_from_file() - read file contents into the mswin textwindow.
+ */
+BOOL
+mswin_tw_fill_from_file(MSWIN_TEXTWINDOW *mswin_tw, LPCTSTR file)
+{
+ BOOL fSuccess = FALSE;
+ HANDLE hFile = CreateFile(file, GENERIC_READ, FILE_SHARE_READ,
+ 0, OPEN_EXISTING,
+ FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if(hFile != INVALID_HANDLE_VALUE)
+ {
+ EDITSTREAM es;
+
+ es.dwCookie = (DWORD_PTR)hFile;
+ es.dwError = 0;
+ es.pfnCallback = EditStreamInCallback;
+
+ if(SendMessage(mswin_tw->hwnd_edit, EM_STREAMIN, SF_TEXT, (LPARAM)&es) &&
+ es.dwError == 0)
+ {
+ fSuccess = TRUE;
+ }
+ }
+
+ CloseHandle(hFile);
+ return fSuccess;
+}
+
+#endif /* ALTED_DOT */
+
+/*
+ * mswin_tw_showwindow() - show the main hwnd (SW_SHOWNORMAL, etc.).
+ */
+void
+mswin_tw_showwindow(MSWIN_TEXTWINDOW *mswin_tw, int nCmdShow)
+{
+ if(mswin_tw && mswin_tw->hwnd)
+ ShowWindow(mswin_tw->hwnd, nCmdShow);
+}
+
+/*
+ * mswin_tw_setfont() - Sets the hfont for the entire edit control.
+ */
+void
+mswin_tw_setfont(MSWIN_TEXTWINDOW *mswin_tw, HFONT hfont)
+{
+ if(mswin_tw && mswin_tw->hwnd_edit)
+ SetWindowFont(mswin_tw->hwnd_edit, hfont, TRUE);
+}
+
+/*
+ * mswin_tw_setcolor() - Set colors for entire edit control. If we're
+ * passed -1 for mswin_tw, then set the colors for all textwindows.
+ */
+void
+mswin_tw_setcolor(MSWIN_TEXTWINDOW *mswin_tw,
+ COLORREF TextColor, COLORREF BackColor)
+{
+ if(mswin_tw == (MSWIN_TEXTWINDOW *)-1)
+ {
+ HWND hwnd = NULL;
+
+ while(hwnd = FindWindowEx(NULL, hwnd, g_mswin_tw_class_name, NULL))
+ {
+ mswin_tw = (MSWIN_TEXTWINDOW *)(LONG_PTR)GetWindowLongPtr(
+ hwnd, GWLP_USERDATA);
+ if(mswin_tw)
+ {
+ mswin_tw_setcolor(mswin_tw, TextColor, BackColor);
+ }
+ }
+ }
+ else if(mswin_tw && mswin_tw->hwnd_edit)
+ {
+ CHARFORMAT2W cf2;
+
+ memset(&cf2, 0, sizeof(cf2));
+ cf2.cbSize = sizeof(cf2);
+ cf2.dwMask = CFM_COLOR;
+ cf2.crTextColor = TextColor;
+
+ SendMessage(mswin_tw->hwnd_edit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2);
+ SendMessage(mswin_tw->hwnd_edit, EM_SETBKGNDCOLOR, 0, BackColor);
+ }
+}
+
+/*
+ * mswin_tw_puts_lptstr() - Stuffs a LPTSTR string at the end of our edit
+ * control.
+ */
+int
+mswin_tw_puts_lptstr(MSWIN_TEXTWINDOW *mswin_tw, LPTSTR msg)
+{
+ if(mswin_tw && mswin_tw->hwnd_edit)
+ {
+ TCHAR lf_str[] = TEXT("\n");
+
+ Edit_ExSetSel(mswin_tw->hwnd_edit, -1, -1);
+ Edit_ReplaceSel(mswin_tw->hwnd_edit, msg);
+
+ Edit_ReplaceSel(mswin_tw->hwnd_edit, lf_str);
+
+ Edit_ScrollCaret(mswin_tw->hwnd_edit);
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * mswin_tw_printf() - printf version of mswin_tw_puts_lptstr.
+ */
+int
+mswin_tw_printf(MSWIN_TEXTWINDOW *mswin_tw, LPCTSTR fmt, ...)
+{
+ TCHAR msg[1024];
+ va_list vlist;
+
+ va_start(vlist, fmt);
+ _vsntprintf(msg, ARRAYSIZE(msg), fmt, vlist);
+ va_end(vlist);
+
+ msg[ARRAYSIZE(msg) - 1] = 0;
+ return mswin_tw_puts_lptstr(mswin_tw, msg);
+}
+
+/*
+ * mswin_tw_gettextlength() - Returns the number of TCHARs in the edit control.
+ */
+UINT
+mswin_tw_gettextlength(MSWIN_TEXTWINDOW *mswin_tw)
+{
+ if(mswin_tw && mswin_tw->hwnd_edit)
+ return (UINT)Edit_ExGetTextLen(mswin_tw->hwnd_edit, GTL_USECRLF);
+
+ return 0;
+}
+
+/*
+ * mswin_tw_gettext() - Return value is the number of TCHARs copied into the
+ * output buffer.
+ */
+UINT
+mswin_tw_gettext(MSWIN_TEXTWINDOW *mswin_tw, LPTSTR lptstr_ret, int lptstr_len)
+{
+ if(mswin_tw && mswin_tw->hwnd_edit)
+ {
+ GETTEXTEX gt;
+ LRESULT lresult;
+
+ gt.cb = lptstr_len * sizeof(TCHAR);
+ gt.flags = GT_DEFAULT | GT_USECRLF;
+ gt.codepage = 1200;
+ gt.lpDefaultChar = NULL;
+ gt.lpUsedDefChar = NULL;
+
+ lptstr_ret[0] = 0;
+ lresult = SendMessage(mswin_tw->hwnd_edit, EM_GETTEXTEX,
+ (WPARAM)&gt, (LPARAM)lptstr_ret);
+ return (int)lresult;
+ }
+
+ return 0;
+}
+
+/*
+ * mswin_tw_setsel() - Set edit control selection.
+ */
+void
+mswin_tw_setsel(MSWIN_TEXTWINDOW *mswin_tw, LONG min, LONG max)
+{
+ if(mswin_tw && mswin_tw->hwnd_edit)
+ {
+ Edit_ExSetSel(mswin_tw->hwnd_edit, min, max);
+ }
+}
+
+/*
+ * mswin_set_readonly() - Set readonly status.
+ */
+void
+mswin_set_readonly(MSWIN_TEXTWINDOW *mswin_tw, BOOL read_only)
+{
+ if(mswin_tw && mswin_tw->hwnd_edit)
+ Edit_SetReadOnly(mswin_tw->hwnd_edit, read_only);
+}
+
+/*
+ * mswin_tw_clear() - Clear all text from edit control.
+ */
+void
+mswin_tw_clear(MSWIN_TEXTWINDOW *mswin_tw)
+{
+ if(mswin_tw && mswin_tw->hwnd_edit)
+ {
+ SETTEXTEX stex;
+
+ stex.flags = ST_DEFAULT;
+ stex.codepage = 1200; // Unicode (see richedit.h)
+
+ SendMessage(mswin_tw->hwnd_edit, EM_SETTEXTEX,
+ (WPARAM)&stex, (LPARAM)TEXT(""));
+
+ if(mswin_tw->clear_callback)
+ mswin_tw->clear_callback(mswin_tw);
+ }
+}
+
+/*
+ * MySetWindowLongPtr() - Little wrapper routine which calls the
+ * Windows SetWindowLongPtr() and removes the stupid warning which is
+ * just seriously lame.
+ */
+static LONG_PTR
+MySetWindowLongPtr(HWND hwnd, int nIndex, void *NewLongPtr)
+{
+// warning C4244: 'function': conversion from 'LONG_PTR' to 'LONG',
+// possible loss of data
+#pragma warning(push)
+#pragma warning(disable: 4244)
+ return SetWindowLongPtr(hwnd, nIndex, (LONG_PTR)NewLongPtr);
+#pragma warning(pop)
+}
+
+/*
+ * mswin_tw_wm_command() - WM_CONTEXTMENU handler for textwindows
+ */
+static void
+mswin_tw_wm_contextmenu(MSWIN_TEXTWINDOW *mswin_tw, HWND hwnd, HWND hwndContext,
+ int xPos, int yPos)
+{
+ HMENU hMenu;
+
+ hMenu = CreatePopupMenu();
+ if(hMenu)
+ {
+ int i;
+ CHARRANGE cr;
+ MENUITEMINFO mitem;
+ static const struct
+ {
+ UINT wID;
+ LPTSTR dwTypeData;
+ } s_popup_menu[] =
+ {
+ { IDM_EDIT_COPY, TEXT("&Copy") },
+ { IDM_EDIT_COPY_APPEND, TEXT("Copy &Append") },
+ { IDM_EDIT_CLEAR, TEXT("Clea&r") },
+ };
+
+ memset(&mitem, 0, sizeof(MENUITEMINFO));
+ mitem.cbSize = sizeof(MENUITEMINFO);
+ mitem.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
+ mitem.fType = MFT_STRING;
+
+ SendMessage(mswin_tw->hwnd_edit, EM_EXGETSEL, (WPARAM)0, (LPARAM)&cr);
+
+ for(i = 0; i < ARRAYSIZE(s_popup_menu); i++)
+ {
+ switch(s_popup_menu[i].wID)
+ {
+ case IDM_EDIT_CLEAR:
+ // Only enable it if there is a clear callback set.
+ mitem.fState = (mswin_tw->clear_callback) ?
+ MFS_ENABLED : MFS_GRAYED;
+ break;
+ case IDM_EDIT_COPY:
+ case IDM_EDIT_COPY_APPEND:
+ // Only enable if there is a selection.
+ mitem.fState = (cr.cpMax > cr.cpMin) ?
+ MFS_ENABLED : MFS_GRAYED;
+ break;
+ default:
+ mitem.fState = MFS_ENABLED;
+ break;
+ }
+
+ mitem.wID = s_popup_menu[i].wID;
+ mitem.dwTypeData = s_popup_menu[i].dwTypeData;
+ mitem.cch = (UINT)_tcslen(s_popup_menu[i].dwTypeData);
+ InsertMenuItem(hMenu, i, FALSE, &mitem);
+ }
+
+ TrackPopupMenu(hMenu,
+ TPM_LEFTALIGN | TPM_TOPALIGN |
+ TPM_RIGHTBUTTON | TPM_LEFTBUTTON,
+ xPos, yPos, 0, hwnd, NULL);
+
+ DestroyMenu(hMenu);
+ }
+}
+
+/*
+ * mswin_tw_wm_command() - WM_COMMAND handler for textwindows
+ */
+static void
+mswin_tw_wm_command(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
+{
+ MSWIN_TEXTWINDOW *mswin_tw;
+
+ mswin_tw = (MSWIN_TEXTWINDOW *)(LONG_PTR)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+
+ switch(id)
+ {
+ case IDM_FILE_CLOSE:
+ DestroyWindow(hwnd);
+ break;
+
+ case IDM_FILE_PRINT:
+ if(mswin_tw->print_callback)
+ mswin_tw->print_callback(mswin_tw);
+ break;
+
+ case IDM_EDIT_COPY:
+ SendMessage(mswin_tw->hwnd_edit, WM_COPY, 0, 0);
+ break;
+
+ case IDM_EDIT_CLEAR:
+ mswin_tw_clear(mswin_tw);
+ break;
+
+ case IDM_EDIT_COPY_APPEND:
+ {
+ CHARRANGE cr;
+ int text_len0, text_len1;
+ HWND hwnd_edit = mswin_tw->hwnd_edit;
+ BOOL EditIsReadOnly = Edit_ExIsReadOnly(hwnd_edit);
+
+ SetWindowRedraw(hwnd_edit, FALSE);
+ Edit_SetReadOnly(hwnd_edit, FALSE);
+
+ // Get current selection.
+ SendMessage(hwnd_edit, EM_EXGETSEL, (WPARAM)0, (LPARAM)&cr);
+
+ // Get current length.
+ text_len0 = Edit_ExGetTextLen(hwnd_edit, 0);
+
+ // Paste current clip right before our new selection.
+ Edit_ExSetSel(hwnd_edit, cr.cpMin, cr.cpMin);
+ SendMessage(hwnd_edit, WM_PASTE, 0, 0);
+
+ // Get new length.
+ text_len1 = Edit_ExGetTextLen(hwnd_edit, 0);
+
+ // Select new and old clip and copy em.
+ Edit_ExSetSel(hwnd_edit, cr.cpMin, cr.cpMax + text_len1 - text_len0);
+ SendMessage(hwnd_edit, WM_COPY, 0, 0);
+
+ // Undo our paste and restore original selection.
+ SendMessage(hwnd_edit, WM_UNDO, 0, 0);
+ SendMessage(hwnd_edit, EM_EXSETSEL, (WPARAM)0, (LPARAM)&cr);
+
+ // Set back to read only.
+ Edit_SetReadOnly(hwnd_edit, EditIsReadOnly);
+ SetWindowRedraw(hwnd_edit, TRUE);
+ break;
+ }
+
+ case IDM_EDIT_SEL_ALL:
+ Edit_ExSetSel(mswin_tw->hwnd_edit, 0, -1);
+ break;
+ }
+}
+
+/*
+ * mswin_tw_wm_notify() - WM_NOTIFY handler for textwindows
+ */
+static LRESULT
+mswin_tw_wm_notify(HWND hwnd, int idCtrl, NMHDR *nmhdr)
+{
+ HWND hwnd_edit = nmhdr->hwndFrom;
+
+ if(nmhdr->code == EN_MSGFILTER)
+ {
+ MSGFILTER *msg_filter = (MSGFILTER *)nmhdr;
+
+ if(msg_filter->msg == WM_KEYDOWN)
+ {
+ if(msg_filter->wParam == 'E')
+ {
+ int control_down = GetKeyState(VK_CONTROL) < 0;
+ int shift_down = GetKeyState(VK_SHIFT) < 0;
+
+ // Ctrl+Shift+E toggles the readonly attribute on the text
+ // buffer.
+ if(control_down && shift_down)
+ Edit_SetReadOnly(hwnd_edit, !Edit_ExIsReadOnly(hwnd_edit));
+ return TRUE;
+ }
+ }
+ else if(msg_filter->msg == WM_CHAR)
+ {
+ // Only override these keys if this buffer is readonly.
+ if(Edit_ExIsReadOnly(hwnd_edit))
+ {
+ switch(msg_filter->wParam)
+ {
+ case 'k':
+ SendMessage(hwnd_edit, EM_SCROLL, SB_LINEUP, 0);
+ return TRUE;
+ case 'j':
+ SendMessage(hwnd_edit, EM_SCROLL, SB_LINEDOWN, 0);
+ return TRUE;
+ case '-':
+ case 'b':
+ SendMessage(hwnd_edit, EM_SCROLL, SB_PAGEUP, 0);
+ return TRUE;
+ case ' ':
+ case 'f':
+ SendMessage(hwnd_edit, EM_SCROLL, SB_PAGEDOWN, 0);
+ return TRUE;
+ }
+ }
+ }
+ }
+ else if(nmhdr->code == EN_LINK)
+ {
+ ENLINK *enlink = (ENLINK *)nmhdr;
+
+ if(enlink->msg == WM_LBUTTONDOWN)
+ {
+ TEXTRANGE tr;
+ TCHAR link_buf[1024];
+
+ link_buf[0] = 0;
+ tr.lpstrText = link_buf;
+
+ tr.chrg = enlink->chrg;
+ if(tr.chrg.cpMax - tr.chrg.cpMin > ARRAYSIZE(link_buf))
+ tr.chrg.cpMax = tr.chrg.cpMin + ARRAYSIZE(link_buf);
+
+ SendMessage(hwnd_edit, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
+ ShellExecute(hwnd, TEXT("Open"), link_buf, NULL, NULL, SW_SHOWNORMAL);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/*
+ * mswin_tw_wndproc() - Main window proc for mswin textwindows.
+ */
+static LRESULT CALLBACK
+mswin_tw_wndproc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
+{
+ MSWIN_TEXTWINDOW *mswin_tw;
+
+ mswin_tw = (MSWIN_TEXTWINDOW *)(LONG_PTR)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+
+ switch(msg)
+ {
+ case WM_CREATE:
+ {
+ CREATESTRUCT *pcs = (CREATESTRUCT *)lp;
+
+ mswin_tw = (MSWIN_TEXTWINDOW *)pcs->lpCreateParams;
+
+ MySetWindowLongPtr(hwnd, GWLP_USERDATA, mswin_tw);
+
+ mswin_tw->hwnd_edit = CreateWindowEx(
+ WS_EX_CLIENTEDGE, RICHEDIT_CLASS, 0,
+ WS_VISIBLE | WS_CHILD |
+ WS_CLIPSIBLINGS | WS_CLIPCHILDREN |
+ WS_VSCROLL | WS_HSCROLL |
+ ES_MULTILINE | ES_READONLY | ES_NOHIDESEL,
+ 0, 0, 1, 1,
+ hwnd, 0, mswin_tw->hInstance, 0);
+
+ // We want link and key event notifications.
+ SendMessage(mswin_tw->hwnd_edit, EM_SETEVENTMASK,
+ 0, (ENM_KEYEVENTS | ENM_LINK));
+
+ // Specifies the maximum amount of text that can be entered.
+ SendMessage(mswin_tw->hwnd_edit, EM_EXLIMITTEXT, 0, g_max_text);
+
+ // Enable automatic detection of URLs by our rich edit control.
+ SendMessage(mswin_tw->hwnd_edit, EM_AUTOURLDETECT, TRUE, 0);
+ break;
+ }
+
+ case WM_CONTEXTMENU:
+ mswin_tw_wm_contextmenu(mswin_tw, hwnd, (HWND)wp, GET_X_LPARAM(lp), GET_Y_LPARAM(lp));
+ break;
+
+ case WM_NOTIFY:
+ return mswin_tw_wm_notify(hwnd, (int)wp, (NMHDR *)lp);
+
+ case WM_COMMAND:
+ HANDLE_WM_COMMAND(hwnd, wp, lp, mswin_tw_wm_command);
+ break;
+
+ case WM_SETFOCUS:
+ SetFocus(mswin_tw->hwnd_edit);
+ return TRUE;
+
+ case WM_SIZE:
+ MoveWindow(mswin_tw->hwnd_edit, 0, 0, LOWORD(lp), HIWORD(lp), TRUE);
+ break;
+
+ case WM_WINDOWPOSCHANGED:
+ if(!IsIconic(hwnd))
+ {
+ WINDOWPOS *wpos = (WINDOWPOS *)lp;
+
+ mswin_tw->rcSize.left = wpos->x;
+ mswin_tw->rcSize.top = wpos->y;
+ mswin_tw->rcSize.right = wpos->x + wpos->cx;
+ mswin_tw->rcSize.bottom = wpos->y + wpos->cy;
+ }
+ break;
+
+ case WM_DESTROY:
+ if(mswin_tw->out_file)
+ {
+ mswin_tw->out_file_ret = mswin_tw_write_to_file(mswin_tw,
+ mswin_tw->out_file);
+ }
+
+ mswin_tw->hwnd = NULL;
+ mswin_tw->hwnd_edit = NULL;
+
+ if(mswin_tw->close_callback)
+ mswin_tw->close_callback(mswin_tw);
+ return TRUE;
+
+ default:
+ break;
+ }
+
+ return DefWindowProc(hwnd, msg, wp, lp);
+}
+
+/*
+ * Edit_ExGetTextLen() - Helper routine for getting count of chars.
+ */
+static UINT
+Edit_ExGetTextLen(HWND hwnd_edit, DWORD flags)
+{
+ GETTEXTLENGTHEX gtl;
+
+ gtl.flags = GTL_PRECISE | GTL_NUMCHARS | flags;
+ gtl.codepage = 1200; // Unicode (see richedit.h)
+ return (UINT)SendMessage(hwnd_edit, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0);
+}
+
+/*
+ * Edit_ExSetSel() - Helper routine for setting edit selection.
+ */
+static UINT
+Edit_ExSetSel(HWND hwnd_edit, LONG cpMin, LONG cpMax)
+{
+ CHARRANGE cr;
+
+ if(cpMin == -1)
+ cpMin = Edit_ExGetTextLen(hwnd_edit, 0);
+
+ cr.cpMin = cpMin;
+ cr.cpMax = cpMax;
+ return (UINT)SendMessage(hwnd_edit, EM_EXSETSEL, (WPARAM)0, (LPARAM)&cr);
+}
+
+/*
+ * Edit_ExIsReadOnly() - TRUE if edit buffer is read only.
+ */
+static BOOL
+Edit_ExIsReadOnly(HWND hwnd_edit)
+{
+ LRESULT edit_opts;
+
+ edit_opts = SendMessage(hwnd_edit, EM_GETOPTIONS, 0, 0);
+ return !!(edit_opts & ECO_READONLY);
+}
+
diff --git a/pico/osdep/mswin_tw.h b/pico/osdep/mswin_tw.h
new file mode 100644
index 00000000..93eb7b76
--- /dev/null
+++ b/pico/osdep/mswin_tw.h
@@ -0,0 +1,58 @@
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+typedef struct MSWIN_TEXTWINDOW MSWIN_TEXTWINDOW;
+typedef void (MSWIN_TW_CALLBACK)(MSWIN_TEXTWINDOW *mswin_tw);
+
+typedef struct MSWIN_TEXTWINDOW
+{
+ // User should set these fields before calling mswin_tw_create()
+ UINT id; // Caller can use to determine which
+ // textwindow this is (Ie: IDM_OPT_NEWMAILWIN)
+ HANDLE hInstance;
+ RECT rcSize; // Window position
+
+ MSWIN_TW_CALLBACK *print_callback; // Print menu selected callback routine.
+ MSWIN_TW_CALLBACK *close_callback; // Callback for when window is closed.
+ MSWIN_TW_CALLBACK *clear_callback; // Callback after text is cleared.
+
+ LPCTSTR out_file; // Save edit contents on close to this file.
+ BOOL out_file_ret; // TRUE - out_file written, FALSE - nope.
+
+ // internal fields
+ HWND hwnd; // hwnd for this mswin textwindow.
+ HWND hwnd_edit;
+} MSWIN_TEXTWINDOW;
+
+int mswin_tw_create(MSWIN_TEXTWINDOW *mswin_tw, LPCTSTR title);
+void mswin_tw_close(MSWIN_TEXTWINDOW *mswin_tw);
+
+void mswin_tw_showwindow(MSWIN_TEXTWINDOW *mswin_tw, int nCmdShow);
+
+BOOL mswin_tw_fill_from_file(MSWIN_TEXTWINDOW *mswin_tw, LPCTSTR file);
+BOOL mswin_tw_write_to_file(MSWIN_TEXTWINDOW *mswin_tw, LPCTSTR file);
+
+void mswin_tw_setfont(MSWIN_TEXTWINDOW *mswin_tw, HFONT hfont);
+void mswin_tw_setcolor(MSWIN_TEXTWINDOW *mswin_tw,
+ COLORREF TextColor, COLORREF BackColor);
+
+int mswin_tw_puts_lptstr(MSWIN_TEXTWINDOW *mswin_tw, LPTSTR msg);
+int mswin_tw_printf(MSWIN_TEXTWINDOW *mswin_tw, LPCTSTR fmt, ...);
+
+UINT mswin_tw_gettext(MSWIN_TEXTWINDOW *mswin_tw, LPTSTR lptstr_ret, int lptstr_len);
+UINT mswin_tw_gettextlength(MSWIN_TEXTWINDOW *mswin_tw);
+
+void mswin_tw_setsel(MSWIN_TEXTWINDOW *mswin_tw, LONG min, LONG max);
+void mswin_tw_clear(MSWIN_TEXTWINDOW *mswin_tw);
+
+void mswin_set_readonly(MSWIN_TEXTWINDOW *mswin_tw, BOOL read_only);
diff --git a/pico/osdep/mswinhnd.cur b/pico/osdep/mswinhnd.cur
new file mode 100644
index 00000000..253c3d33
--- /dev/null
+++ b/pico/osdep/mswinhnd.cur
Binary files differ
diff --git a/pico/osdep/newmail.c b/pico/osdep/newmail.c
new file mode 100644
index 00000000..f900a634
--- /dev/null
+++ b/pico/osdep/newmail.c
@@ -0,0 +1,66 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: newmail.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "../estruct.h"
+#include "../mode.h"
+#include "../pico.h"
+#include "../edef.h"
+#include "../efunc.h"
+#include "../keydefs.h"
+
+#include "../../pith/charconv/filesys.h"
+
+#include "newmail.h"
+
+
+
+
+/*
+ * pico_new_mail - just checks mtime and atime of mail file and notifies user
+ * if it's possible that they have new mail.
+ */
+int
+pico_new_mail(void)
+{
+#ifndef _WINDOWS
+ int ret = 0;
+ static time_t lastchk = 0;
+ struct stat sbuf;
+ char inbox[256], *p;
+
+ if((p = (char *)getenv("MAIL")) != NULL)
+ /* fix unsafe sprintf - noticed by petter wahlman <petter@bluezone.no> */
+ snprintf(inbox, sizeof(inbox), "%s", p);
+ else
+ snprintf(inbox, sizeof(inbox), "%s/%s", MAILDIR, (char *) getlogin());
+
+ if(our_stat(inbox, &sbuf) == 0){
+ ret = sbuf.st_atime <= sbuf.st_mtime &&
+ (lastchk < sbuf.st_mtime && lastchk < sbuf.st_atime);
+ lastchk = sbuf.st_mtime;
+ return(ret);
+ }
+ else
+ return(ret);
+#else /* _WINDOWS */
+ return(0);
+#endif
+}
diff --git a/pico/osdep/newmail.h b/pico/osdep/newmail.h
new file mode 100644
index 00000000..5af645e1
--- /dev/null
+++ b/pico/osdep/newmail.h
@@ -0,0 +1,25 @@
+/*
+ * $Id: newmail.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef PICO_OSDEP_NEWMAIL_INCLUDED
+#define PICO_OSDEP_NEWMAIL_INCLUDED
+
+
+/* exported prototypes */
+int pico_new_mail(void);
+
+
+#endif /* PICO_OSDEP_NEWMAIL_INCLUDED */
diff --git a/pico/osdep/os-win.h b/pico/osdep/os-win.h
new file mode 100644
index 00000000..2ececda1
--- /dev/null
+++ b/pico/osdep/os-win.h
@@ -0,0 +1,183 @@
+#ifndef _PICO_OS_INCLUDED
+#define _PICO_OS_INCLUDED
+
+
+/*----------------------------------------------------------------------
+
+ OS dependencies, WIN version. See also the os-win.c files.
+ The following stuff may need to be changed for a new port, but once
+ the port is done it won't change. Further down in the file are a few
+ constants that you may want to configure differently than they
+ are configured, but probably not.
+
+ ----*/
+
+
+
+/*----------------- Are we ANSI? ---------------------------------------*/
+#define ANSI /* this is an ANSI compiler */
+
+/*------ If our compiler doesn't understand type void ------------------*/
+/* #define void char */
+
+/*-------- Standard ANSI functions usually defined in stdlib.h ---------*/
+#include <stdlib.h>
+#include <string.h>
+#include <dos.h>
+#include <direct.h>
+#include <search.h>
+#undef CTRL
+#include <sys/types.h>
+#include <sys/stat.h>
+
+
+/*-------- Standard windows defines and then our window module defines. */
+#include <windows.h>
+#include <limits.h>
+#include <commdlg.h>
+#ifndef WIN32
+#include <print.h>
+#include <toolhelp.h>
+#endif
+#include <cderr.h>
+#include <winsock.h>
+#include <shellapi.h>
+
+#include "mswin.h"
+
+#include <io.h>
+#include <sys/stat.h>
+#include <winsock.h>
+#include <dos.h>
+#include <direct.h>
+#include <memory.h>
+#include <fcntl.h>
+#include <sys/timeb.h>
+
+
+/* Windows only version and resource defines. */
+#include "resource.h"
+
+
+#undef ERROR
+
+
+/*---- Declare sys_errlist() if not already declared -------------------*/
+/* extern char *sys_errlist[]; */
+
+
+
+/*----------------- locale.h -------------------------------------------*/
+/* #include <locale.h> */ /* To make matching and sorting work right */
+#define collator strucmp
+
+
+
+/*----------------- time.h ---------------------------------------------*/
+#include <time.h>
+/* plain time.h isn't enough on some systems */
+/* #include <sys/time.h> */ /* For struct timeval usually in time.h */
+
+
+
+/*--------------- signal.h ---------------------------------------------*/
+#include <signal.h>
+/* #include <sys/signal.h> */
+
+#define SigType void /* value returned by sig handlers is void */
+/* #define SigType int */ /* value returned by sig handlers is int */
+
+/* #define POSIX_SIGNALS */
+/* #define SYSV_SIGNALS */ /* use System-V signal semantics (ttyin.c) */
+
+#define SIG_PROTO(args) args
+
+
+
+/*-------------- A couple typedef's for integer sizes ------------------*/
+typedef unsigned long usign32_t;
+typedef unsigned short usign16_t;
+
+
+
+/*-------------- qsort argument type -----------------------------------*/
+#define QSType void
+/* #define QSType char */
+
+#define QcompType const void
+
+
+/*-------- Is window resizing available? -------------------------------*/
+/* #define RESIZING */
+ /* Actually, under windows it is, but RESIZING compiles in UNIX
+ * signals code for determining when the window resized. Window's
+ * works differently. */
+
+
+
+
+/*-------- If no vfork, use regular fork -------------------------------*/
+/* #define vfork fork */
+
+
+
+/*---- When no screen size can be discovered this is the size used -----*/
+#define DEFAULT_LINES_ON_TERMINAL (25)
+#define DEFAULT_COLUMNS_ON_TERMINAL (80)
+#define NROW DEFAULT_LINES_ON_TERMINAL
+#define NCOL DEFAULT_COLUMNS_ON_TERMINAL
+
+
+
+/*
+ * File name separators, char and string
+ */
+#define C_FILESEP '\\'
+#define S_FILESEP "\\"
+
+
+/*
+ * What and where the tool that checks spelling is located. If this is
+ * undefined, then the spelling checker is not compiled into pico.
+ */
+#define SPELLER
+
+
+/*
+ * Mode passed chmod() to make tmp files exclusively user read/write-able
+ */
+/*#define MODE_READONLY (S_IREAD | S_IWRITE) */
+
+
+#ifdef maindef
+/* possible names and paths of help files under different OSs */
+
+char *pathname[] = {
+ "picorc",
+ "pico.hlp",
+ "\\usr\\local\\",
+ "\\usr\\lib\\",
+ ""
+};
+
+#define NPNAMES (sizeof(pathname)/sizeof(char *))
+
+
+#endif
+
+
+/* Define function that mswin.c calls back for scrolling. */
+int pico_scroll_callback ();
+
+
+#include "mswin.h"
+#include "msmenu.h"
+
+/*
+ * Make sys_errlist visible
+ */
+/* extern char *sys_errlist[]; */
+/* extern int sys_nerr; */
+
+
+#endif /* _PICO_OS_INCLUDED */
diff --git a/pico/osdep/os-wnt.h b/pico/osdep/os-wnt.h
new file mode 100644
index 00000000..a1eb9c3d
--- /dev/null
+++ b/pico/osdep/os-wnt.h
@@ -0,0 +1,181 @@
+#ifndef _PICO_OS_INCLUDED
+#define _PICO_OS_INCLUDED
+
+
+/*----------------------------------------------------------------------
+
+ OS dependencies, WIN NT version. See also the os-wnt.c files.
+ The following stuff may need to be changed for a new port, but once
+ the port is done it won't change. Further down in the file are a few
+ constants that you may want to configure differently than they
+ are configured, but probably not.
+
+ ----*/
+
+
+
+/*----------------- Are we ANSI? ---------------------------------------*/
+#define ANSI /* this is an ANSI compiler */
+
+/*------ If our compiler doesn't understand type void ------------------*/
+/* #define void char */
+
+/*-------- Standard ANSI functions usually defined in stdlib.h ---------*/
+#include <stdlib.h>
+#include <string.h>
+#include <dos.h>
+#include <direct.h>
+#include <search.h>
+#undef CTRL
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#define _WIN32_WINNT WINVER
+
+/*-------- Standard windows defines and then our window module defines. */
+#include <windows.h>
+#include <limits.h>
+#include <commdlg.h>
+#include <cderr.h>
+#include <winsock.h>
+#include <shellapi.h>
+
+#include "mswin.h"
+
+#include <io.h>
+#include <sys/stat.h>
+#include <winsock.h>
+#include <dos.h>
+#include <direct.h>
+#include <memory.h>
+#include <fcntl.h>
+#include <sys/timeb.h>
+
+
+/* Windows only version and resource defines. */
+#include "resource.h"
+
+
+#undef ERROR
+
+
+/*---- Declare sys_errlist() if not already declared -------------------*/
+/* extern char *sys_errlist[]; */
+
+
+
+/*----------------- locale.h -------------------------------------------*/
+#include <locale.h> /* To make matching and sorting work right */
+#define collator strcoll
+
+
+
+/*----------------- time.h ---------------------------------------------*/
+#include <time.h>
+/* plain time.h isn't enough on some systems */
+/* #include <sys/time.h> */ /* For struct timeval usually in time.h */
+
+
+
+/*--------------- signal.h ---------------------------------------------*/
+#include <signal.h>
+/* #include <sys/signal.h> */
+
+#define SigType void /* value returned by sig handlers is void */
+/* #define SigType int */ /* value returned by sig handlers is int */
+
+/* #define POSIX_SIGNALS */
+/* #define SYSV_SIGNALS */ /* use System-V signal semantics (ttyin.c) */
+
+#define SIG_PROTO(args) args
+
+
+
+/*-------------- A couple typedef's for integer sizes ------------------*/
+typedef unsigned long usign32_t;
+typedef unsigned short usign16_t;
+
+
+
+/*-------------- qsort argument type -----------------------------------*/
+#define QSType void
+/* #define QSType char */
+
+#define QcompType const void
+
+
+/*-------- Is window resizing available? -------------------------------*/
+/* #define RESIZING */
+ /* Actually, under windows it is, but RESIZING compiles in UNIX
+ * signals code for determining when the window resized. Window's
+ * works differently. */
+
+
+
+
+/*-------- If no vfork, use regular fork -------------------------------*/
+/* #define vfork fork */
+
+
+
+/*---- When no screen size can be discovered this is the size used -----*/
+#define DEFAULT_LINES_ON_TERMINAL (25)
+#define DEFAULT_COLUMNS_ON_TERMINAL (80)
+#define NROW DEFAULT_LINES_ON_TERMINAL
+#define NCOL DEFAULT_COLUMNS_ON_TERMINAL
+
+
+#define ftruncate chsize
+
+
+/*
+ * File name separators, char and string
+ */
+#define C_FILESEP '\\'
+#define S_FILESEP "\\"
+
+
+/*
+ * What and where the tool that checks spelling is located. If this is
+ * undefined, then the spelling checker is not compiled into pico.
+ */
+#define SPELLER
+
+
+/*
+ * Mode passed chmod() to make tmp files exclusively user read/write-able
+ */
+/*#define MODE_READONLY (S_IREAD | S_IWRITE) */
+
+
+#ifdef maindef
+/* possible names and paths of help files under different OSs */
+
+char *pathname[] = {
+ "picorc",
+ "pico.hlp",
+ "\\usr\\local\\",
+ "\\usr\\lib\\",
+ ""
+};
+
+#define NPNAMES (sizeof(pathname)/sizeof(char *))
+
+
+#endif
+
+
+#include "mswin.h"
+#include "msmenu.h"
+
+/* memmove() is a built-in for DOS/Windows */
+#define bcopy(a,b,s) memmove (b, a, s)
+
+/*
+ * Make sys_errlist visible
+ */
+/* extern char *sys_errlist[]; */
+/* extern int sys_nerr; */
+
+
+#endif /* _PICO_OS_INCLUDED */
diff --git a/pico/osdep/pico.ico b/pico/osdep/pico.ico
new file mode 100644
index 00000000..b78a05ad
--- /dev/null
+++ b/pico/osdep/pico.ico
Binary files differ
diff --git a/pico/osdep/popen.c b/pico/osdep/popen.c
new file mode 100644
index 00000000..575929a6
--- /dev/null
+++ b/pico/osdep/popen.c
@@ -0,0 +1,68 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: popen.c 761 2007-10-23 22:35:18Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+#include "../estruct.h"
+
+
+
+
+/*
+ * P_open - run the given command in a sub-shell returning a file pointer
+ * from which to read the output
+ *
+ * note:
+ * For OS's other than unix, you will have to rewrite this function.
+ * Hopefully it'll be easy to exec the command into a temporary file,
+ * and return a file pointer to that opened file or something.
+ */
+int
+P_open(char *s)
+{
+#if HAVE_POPEN
+ extern FIOINFO g_pico_fio;
+
+ g_pico_fio.flags = FIOINFO_READ;
+ g_pico_fio.name = "pipe";
+
+ if((g_pico_fio.fp = popen(s, "r")) != NULL)
+ return(FIOSUC);
+
+ return(FIOERR);
+#else
+ /* Windows never did this, but piping has been done elsewhere */
+ return(0);
+#endif
+}
+
+
+
+/*
+ * P_close - close the given descriptor
+ *
+ */
+void
+P_close(void)
+{
+#if HAVE_PCLOSE
+ extern FIOINFO g_pico_fio;
+
+ if(g_pico_fio.fp)
+ (void) pclose(g_pico_fio.fp);
+#endif
+}
diff --git a/pico/osdep/popen.h b/pico/osdep/popen.h
new file mode 100644
index 00000000..6e7f686d
--- /dev/null
+++ b/pico/osdep/popen.h
@@ -0,0 +1,26 @@
+/*
+ * $Id: popen.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef PICO_OSDEP_POPEN_INCLUDED
+#define PICO_OSDEP_POPEN_INCLUDED
+
+
+/* exported prototypes */
+int P_open(char *);
+void P_close(void);
+
+
+#endif /* PICO_OSDEP_POPEN_INCLUDED */
diff --git a/pico/osdep/raw.c b/pico/osdep/raw.c
new file mode 100644
index 00000000..a886e0b6
--- /dev/null
+++ b/pico/osdep/raw.c
@@ -0,0 +1,449 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: raw.c 761 2007-10-23 22:35:18Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*
+ * TTY setup routines.
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "../estruct.h"
+
+#include "raw.h"
+
+
+#if HAS_TERMIOS
+/*
+ * These are the TERMIOS-style (POSIX) routines.
+ */
+
+static struct termios _raw_tty, _original_tty;
+
+#elif HAS_TERMIO
+/*
+ * These are the TERMIO-style (System V) routines.
+ */
+
+static struct termio _raw_tty, _original_tty;
+
+#else /* RAW_BSD */
+/*
+ * These are for the BSD-style routines.
+ */
+
+static struct sgttyb _raw_tty, _original_tty;
+static struct ltchars _raw_ltchars, _original_ltchars;
+static struct tchars _raw_tchars, _original_tchars;
+static int _raw_lmode, _original_lmode;
+
+#endif
+
+/*
+ * current raw state
+ */
+static short _inraw = 0;
+
+/*
+ * Set up the tty driver
+ *
+ * Args: state -- which state to put it in. 1 means go into raw (cbreak),
+ * 0 out of raw.
+ *
+ * Result: returns 0 if successful and -1 if not.
+ */
+int
+Raw(int state)
+{
+ /** state is either ON or OFF, as indicated by call **/
+ /* Check return code only on first call. If it fails we're done for and
+ if it goes OK the others will probably go OK too. */
+
+ if(state == 0 && _inraw){
+ /*----- restore state to original -----*/
+
+#if HAS_TERMIOS
+
+ if(tcsetattr(STDIN_FD, TCSADRAIN, &_original_tty) < 0)
+ return -1;
+
+#elif HAS_TERMIO
+
+ if(ioctl(STDIN_FD, TCSETAW, &_original_tty) < 0)
+ return(-1);
+
+#else /* RAW_BSD */
+
+ if(ioctl(STDIN_FD, TIOCSETP, &_original_tty) < 0)
+ return(-1);
+
+ (void)ioctl(STDIN_FD, TIOCSLTC, &_original_ltchars);
+ (void)ioctl(STDIN_FD, TIOCSETC, &_original_tchars);
+ (void)ioctl(STDIN_FD, TIOCLSET, &_original_lmode);
+
+#endif
+
+ _inraw = 0;
+ }
+ else if(state == 1 && ! _inraw){
+ /*----- Go into raw mode (cbreak actually) ----*/
+
+#if HAS_TERMIOS
+
+ if(tcgetattr(STDIN_FD, &_original_tty) < 0)
+ return -1;
+
+ tcgetattr(STDIN_FD, &_raw_tty);
+ _raw_tty.c_lflag &= ~(ICANON | ECHO | IEXTEN); /* noecho raw mode */
+ _raw_tty.c_lflag &= ~ISIG; /* disable signals */
+ _raw_tty.c_iflag &= ~ICRNL; /* turn off CR->NL on input */
+ _raw_tty.c_oflag &= ~ONLCR; /* turn off NL->CR on output */
+
+ _raw_tty.c_cc[VMIN] = '\01'; /* min # of chars to queue */
+ _raw_tty.c_cc[VTIME] = '\0'; /* min time to wait for input*/
+ _raw_tty.c_cc[VINTR] = ctrl('C'); /* make it our special char */
+ _raw_tty.c_cc[VQUIT] = 0;
+ _raw_tty.c_cc[VSUSP] = 0;
+ tcsetattr(STDIN_FD, TCSADRAIN, &_raw_tty);
+
+#elif HAS_TERMIO
+
+ if(ioctl(STDIN_FD, TCGETA, &_original_tty) < 0)
+ return(-1);
+
+ (void)ioctl(STDIN_FD, TCGETA, &_raw_tty); /** again! **/
+ _raw_tty.c_lflag &= ~(ICANON | ECHO); /* noecho raw mode */
+ _raw_tty.c_lflag &= ~ISIG; /* disable signals */
+ _raw_tty.c_iflag &= ~ICRNL; /* turn off CR->NL on input */
+ _raw_tty.c_oflag &= ~ONLCR; /* turn off NL->CR on output */
+ _raw_tty.c_cc[VMIN] = 1; /* min # of chars to queue */
+ _raw_tty.c_cc[VTIME] = 0; /* min time to wait for input*/
+ _raw_tty.c_cc[VINTR] = ctrl('C'); /* make it our special char */
+ _raw_tty.c_cc[VQUIT] = 0;
+ (void)ioctl(STDIN_FD, TCSETAW, &_raw_tty);
+
+#else /* RAW_BSD */
+ if(ioctl(STDIN_FD, TIOCGETP, &_original_tty) < 0)
+ return(-1);
+
+ (void)ioctl(STDIN_FD, TIOCGETP, &_raw_tty);
+ (void)ioctl(STDIN_FD, TIOCGETC, &_original_tchars);
+ (void)ioctl(STDIN_FD, TIOCGETC, &_raw_tchars);
+ (void)ioctl(STDIN_FD, TIOCGLTC, &_original_ltchars);
+ (void)ioctl(STDIN_FD, TIOCGLTC, &_raw_ltchars);
+ (void)ioctl(STDIN_FD, TIOCLGET, &_original_lmode);
+ (void)ioctl(STDIN_FD, TIOCLGET, &_raw_lmode);
+
+ _raw_tty.sg_flags &= ~(ECHO); /* echo off */
+ _raw_tty.sg_flags |= CBREAK; /* raw on */
+ _raw_tty.sg_flags &= ~CRMOD; /* Turn off CR -> LF mapping */
+
+ _raw_tchars.t_intrc = -1; /* Turn off ^C and ^D */
+ _raw_tchars.t_eofc = -1;
+
+ _raw_ltchars.t_lnextc = -1; /* Turn off ^V so we can use it */
+ _raw_ltchars.t_dsuspc = -1; /* Turn off ^Y so we can use it */
+ _raw_ltchars.t_suspc = -1; /* Turn off ^Z; we just read 'em */
+ _raw_ltchars.t_werasc = -1; /* Turn off ^w word erase */
+ _raw_ltchars.t_rprntc = -1; /* Turn off ^R reprint line */
+ _raw_ltchars.t_flushc = -1; /* Turn off ^O output flush */
+
+ (void)ioctl(STDIN_FD, TIOCSETP, &_raw_tty);
+ (void)ioctl(STDIN_FD, TIOCSLTC, &_raw_ltchars);
+ (void)ioctl(STDIN_FD, TIOCSETC, &_raw_tchars);
+ (void)ioctl(STDIN_FD, TIOCLSET, &_raw_lmode);
+#endif
+ _inraw = 1;
+ }
+
+ return(0);
+}
+
+
+/*
+ * Set up the tty driver to use XON/XOFF flow control
+ *
+ * Args: state -- True to make sure XON/XOFF turned on, FALSE off.
+ *
+ * Result: none.
+ */
+void
+xonxoff_proc(int state)
+{
+ if(_inraw){
+ if(state){
+#if HAS_TERMIOS
+
+ if(!(_raw_tty.c_iflag & IXON)){
+ _raw_tty.c_iflag |= IXON;
+ tcsetattr (STDIN_FD, TCSADRAIN, &_raw_tty);
+ }
+
+#elif HAS_TERMIO
+
+ if(!(_raw_tty.c_iflag & IXON)){
+ _raw_tty.c_iflag |= IXON; /* turn ON ^S/^Q on input */
+ (void)ioctl(STDIN_FD, TCSETAW, &_raw_tty);
+
+#else /* RAW_BSD */
+
+ if(_raw_tchars.t_startc == -1 || _raw_tchars.t_stopc == -1){
+ _raw_tchars.t_startc = 'Q' - '@'; /* Turn ON ^S/^Q */
+ _raw_tchars.t_stopc = 'S' - '@';
+ (void)ioctl(STDIN_FD, TIOCSETC, &_raw_tchars);
+ }
+
+#endif
+ }
+ else{
+#if HAS_TERMIOS
+
+ if(_raw_tty.c_iflag & IXON){
+ _raw_tty.c_iflag &= ~IXON; /* turn off ^S/^Q on input */
+ tcsetattr (STDIN_FD, TCSADRAIN, &_raw_tty);
+ }
+
+#elif HAS_TERMIO
+
+ if(_raw_tty.c_iflag & IXON){
+ _raw_tty.c_iflag &= ~IXON; /* turn off ^S/^Q on input */
+ (void)ioctl(STDIN_FD, TCSETAW, &_raw_tty);
+ }
+
+#else /* RAW_BSD */
+ if(!(_raw_tchars.t_startc == -1 && _raw_tchars.t_stopc == -1)){
+ _raw_tchars.t_startc = -1; /* Turn off ^S/^Q */
+ _raw_tchars.t_stopc = -1;
+ (void)ioctl(STDIN_FD, TIOCSETC, &_raw_tchars);
+ }
+#endif
+ }
+ }
+}
+
+
+/*
+ * Set up the tty driver to do LF->CR translation
+ *
+ * Args: state -- True to turn on translation, false to write raw LF's
+ *
+ * Result: none.
+ */
+void
+crlf_proc(int state)
+{
+ if(_inraw){
+ if(state){ /* turn ON NL->CR on output */
+#if HAS_TERMIOS
+
+ if(!(_raw_tty.c_oflag & ONLCR)){
+ _raw_tty.c_oflag |= ONLCR;
+ tcsetattr (STDIN_FD, TCSADRAIN, &_raw_tty);
+ }
+
+#elif HAS_TERMIO
+
+ if(!(_raw_tty.c_oflag & ONLCR)){
+ _raw_tty.c_oflag |= ONLCR;
+ (void)ioctl(STDIN_FD, TCSETAW, &_raw_tty);
+ }
+
+#else /* RAW_BSD */
+ if(!(_raw_tty.sg_flags & CRMOD)){
+ _raw_tty.sg_flags |= CRMOD;
+ (void)ioctl(STDIN_FD, TIOCSETP, &_raw_tty);
+ }
+#endif
+ }
+ else{ /* turn OFF NL-CR on output */
+#if HAS_TERMIOS
+
+ if(_raw_tty.c_oflag & ONLCR){
+ _raw_tty.c_oflag &= ~ONLCR;
+ tcsetattr (STDIN_FD, TCSADRAIN, &_raw_tty);
+ }
+
+#elif HAS_TERMIO
+
+ if(_raw_tty.c_oflag & ONLCR){
+ _raw_tty.c_oflag &= ~ONLCR;
+ (void)ioctl(STDIN_FD, TCSETAW, &_raw_tty);
+ }
+
+#else /* RAW_BSD */
+
+ if(_raw_tty.sg_flags & CRMOD){
+ _raw_tty.sg_flags &= ~CRMOD;
+ (void)ioctl(STDIN_FD, TIOCSETP, &_raw_tty);
+ }
+
+#endif
+ }
+ }
+}
+
+
+/*
+ * Set up the tty driver to hanle interrupt char
+ *
+ * Args: state -- True to turn on interrupt char, false to not
+ *
+ * Result: tty driver that'll send us SIGINT or not
+ */
+void
+intr_proc(int state)
+{
+ if(_inraw){
+ if(state){
+#if HAS_TERMIOS
+
+ _raw_tty.c_lflag |= ISIG; /* enable signals */
+ tcsetattr(STDIN_FD, TCSADRAIN, &_raw_tty);
+
+#elif HAS_TERMIO
+
+ _raw_tty.c_lflag |= ISIG; /* enable signals */
+ (void)ioctl(STDIN_FD, TCSETAW, &_raw_tty);
+
+#else /* RAW_BSD */
+
+ _raw_tchars.t_intrc = ctrl('C');
+ (void)ioctl(STDIN_FD, TIOCSETC, &_raw_tchars);
+
+#endif
+ }
+ else{
+#if HAS_TERMIOS
+
+ _raw_tty.c_lflag &= ~ISIG; /* disable signals */
+ tcsetattr(STDIN_FD, TCSADRAIN, &_raw_tty);
+
+#elif HAS_TERMIO
+
+ _raw_tty.c_lflag &= ~ISIG; /* disable signals */
+ (void)ioctl(STDIN_FD, TCSETAW, &_raw_tty);
+
+#else /* RAW_BSD */
+ _raw_tchars.t_intrc = -1;
+ (void)ioctl(STDIN_FD, TIOCSETC, &_raw_tchars);
+#endif
+ }
+ }
+}
+
+
+/*
+ * Discard any pending input characters
+ *
+ * Args: none
+ *
+ * Result: pending input buffer flushed
+ */
+void
+flush_input(void)
+{
+#if HAS_TERMIOS
+
+ tcflush(STDIN_FD, TCIFLUSH);
+
+#elif HAS_TERMIO
+
+ ioctl(STDIN_FD, TCFLSH, 0);
+
+#else /* RAW_BSD */
+
+#ifdef TIOCFLUSH
+#ifdef FREAD
+ int i = FREAD;
+#else
+ int i = 1;
+#endif
+ ioctl(STDIN_FD, TIOCFLUSH, &i);
+#endif /* TIOCFLUSH */
+
+#endif
+}
+
+
+/*
+ * Turn off hi bit stripping
+ */
+void
+bit_strip_off(void)
+{
+#if HAS_TERMIOS
+
+ _raw_tty.c_iflag &= ~ISTRIP;
+ tcsetattr(STDIN_FD, TCSADRAIN, &_raw_tty);
+
+#elif HAS_TERMIO
+
+ /* no op */
+
+#else /* RAW_BSD */
+
+#ifdef LPASS8
+ _raw_lmode |= LPASS8;
+#endif
+
+#ifdef LPASS8OUT
+ _raw_lmode |= LPASS8OUT;
+#endif
+
+ (void)ioctl(STDIN_FD, TIOCLSET, &_raw_lmode);
+
+#endif
+}
+
+
+/*
+ * Turn off quit character (^\) if possible
+ */
+void
+quit_char_off(void)
+{
+#if HAS_TERMIOS
+
+#elif HAS_TERMIO
+
+#else /* RAW_BSD */
+ _raw_tchars.t_quitc = -1;
+ (void)ioctl(STDIN_FD, TIOCSETC, &_raw_tchars);
+#endif
+}
+
+
+/*
+ * Returns TRUE if tty is < 4800, 0 otherwise.
+ */
+int
+ttisslow(void)
+{
+#if HAS_TERMIOS
+
+ return(cfgetospeed(&_raw_tty) < B4800);
+
+#elif HAS_TERMIO
+
+ return((_raw_tty.c_cflag&CBAUD) < (unsigned int)B4800);
+
+#else /* RAW_BSD */
+
+ return((int)_raw_tty.sg_ispeed < B4800);
+
+#endif
+}
diff --git a/pico/osdep/raw.h b/pico/osdep/raw.h
new file mode 100644
index 00000000..cd5fa203
--- /dev/null
+++ b/pico/osdep/raw.h
@@ -0,0 +1,38 @@
+/*
+ * $Id: raw.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef PICO_OSDEP_RAW_INCLUDED
+#define PICO_OSDEP_RAW_INCLUDED
+
+
+/* useful definitions */
+#define STDIN_FD 0
+#define STDOUT_FD 1
+#define STDERR_FD 2
+
+
+/* exported prototypes */
+int Raw(int);
+void bit_strip_off(void);
+void quit_char_off(void);
+int ttisslow(void);
+void xonxoff_proc(int);
+void flush_input(void);
+void crlf_proc(int);
+void intr_proc(int);
+
+
+#endif /* PICO_OSDEP_RAW_INCLUDED */
diff --git a/pico/osdep/read.c b/pico/osdep/read.c
new file mode 100644
index 00000000..52fe92e5
--- /dev/null
+++ b/pico/osdep/read.c
@@ -0,0 +1,223 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: read.c 763 2007-10-23 23:37:34Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*
+ * Keyboard input test and read functions
+ */
+
+#include <system.h> /* os-dep defs/includes */
+#include <general.h> /* generally useful definitions */
+
+#include "../keydefs.h"
+
+#ifndef _WINDOWS
+#include "raw.h"
+#endif /* !_WINDOWS */
+
+#include "read.h"
+
+
+static time_t _time_of_last_input;
+
+time_t
+time_of_last_input(void)
+{
+ if(_time_of_last_input == 0)
+ _time_of_last_input = time((time_t *) 0);
+
+ return(_time_of_last_input);
+}
+
+#ifndef _WINDOWS
+#if HAVE_SELECT
+
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
+
+
+/*
+ * Check whether or not a character is ready to be read, or time out.
+ * This version uses a select call.
+ *
+ * Args: time_out -- number of seconds before it will time out.
+ *
+ * Result: NO_OP_IDLE: timed out before any chars ready, time_out > 25
+ * NO_OP_COMMAND: timed out before any chars ready, time_out <= 25
+ * READ_INTR: read was interrupted
+ * READY_TO_READ: input is available
+ * BAIL_OUT: reading input failed, time to bail gracefully
+ * PANIC_NOW: system call error, die now
+ */
+UCS
+input_ready(int time_out)
+{
+ struct timeval tmo;
+ fd_set readfds, errfds;
+ int res;
+
+ fflush(stdout);
+
+ if(time_out > 0){
+ /* Check to see if there are bytes to read with a timeout */
+ FD_ZERO(&readfds);
+ FD_ZERO(&errfds);
+ FD_SET(STDIN_FD, &readfds);
+ FD_SET(STDIN_FD, &errfds);
+ tmo.tv_sec = time_out;
+ tmo.tv_usec = 0;
+ res = select(STDIN_FD+1, &readfds, 0, &errfds, &tmo);
+ if(res < 0){
+ if(errno == EINTR || errno == EAGAIN)
+ return(READ_INTR);
+
+ return(BAIL_OUT);
+ }
+
+ if(res == 0){ /* the select timed out */
+#ifndef __CYGWIN__
+ if(getppid() == 1){
+ /* Parent is init! */
+ return(BAIL_OUT);
+ }
+#endif
+
+ /*
+ * "15" is the minimum allowed mail check interval.
+ * Anything less, and we're being told to cycle thru
+ * the command loop because some task is pending...
+ */
+ return(time_out < IDLE_TIMEOUT ? NO_OP_COMMAND : NO_OP_IDLE);
+ }
+ }
+
+ _time_of_last_input = time((time_t *) 0);
+ return(READY_TO_READ);
+}
+
+
+#elif HAVE_POLL
+
+
+#ifdef HAVE_STROPTS_H
+# include <stropts.h>
+#endif
+
+#ifdef HAVE_SYS_POLL_H
+# include <poll.h>
+#endif
+
+/*
+ * Check whether or not a character is ready to be read, or time out.
+ * This version uses a poll call.
+ *
+ * Args: time_out -- number of seconds before it will time out.
+ *
+ * Result: NO_OP_IDLE: timed out before any chars ready, time_out > 25
+ * NO_OP_COMMAND: timed out before any chars ready, time_out <= 25
+ * READ_INTR: read was interrupted
+ * READY_TO_READ: input is available
+ * BAIL_OUT: reading input failed, time to bail gracefully
+ * PANIC_NOW: system call error, die now
+ */
+UCS
+input_ready(int time_out)
+{
+ struct pollfd pollfd;
+ int res;
+
+ fflush(stdout);
+
+ if(time_out > 0){
+ /* Check to see if there are bytes to read with a timeout */
+ pollfd.fd = STDIN_FD;
+ pollfd.events = POLLIN;
+ res = poll (&pollfd, 1, time_out * 1000);
+ if(res >= 0){ /* status bits OK? */
+ if(pollfd.revents & (POLLERR | POLLNVAL))
+ res = -1; /* bad news, exit below! */
+ else if(pollfd.revents & POLLHUP)
+ return(BAIL_OUT);
+ }
+
+ if(res < 0){
+ if(errno == EINTR || errno == EAGAIN)
+ return(READ_INTR);
+
+ return(PANIC_NOW);
+ }
+
+ if(res == 0){ /* the select timed out */
+ if(getppid() == 1){
+ /* Parent is init! */
+ return(BAIL_OUT);
+ }
+
+ /*
+ * "15" is the minimum allowed mail check interval.
+ * Anything less, and we're being told to cycle thru
+ * the command loop because some task is pending...
+ */
+ return(time_out < IDLE_TIMEOUT ? NO_OP_COMMAND : NO_OP_IDLE);
+ }
+ }
+
+ _time_of_last_input = time((time_t *) 0);
+ return(READY_TO_READ);
+}
+
+#endif /* HAVE_POLL */
+
+
+/*
+ * Read one character from STDIN.
+ *
+ * Result: -- the single character read
+ * READ_INTR -- read was interrupted
+ * BAIL_OUT -- read error of some sort
+ */
+int
+read_one_char(void)
+{
+ int res;
+ unsigned char c;
+
+ res = read(STDIN_FD, &c, 1);
+
+ if(res <= 0){
+ /*
+ * Error reading from terminal!
+ * The only acceptable failure is being interrupted. If so,
+ * return a value indicating such...
+ */
+ if(res < 0 && errno == EINTR)
+ return(READ_INTR);
+ else
+ return(BAIL_OUT);
+ }
+
+ return((int)c);
+}
+
+#else /* _WINDOWS */
+int
+set_time_of_last_input(void)
+{
+ _time_of_last_input = time(0L);
+ return(0);
+}
+#endif /* _WINDOWS */
diff --git a/pico/osdep/read.h b/pico/osdep/read.h
new file mode 100644
index 00000000..2ba6c631
--- /dev/null
+++ b/pico/osdep/read.h
@@ -0,0 +1,35 @@
+/*
+ * $Id: read.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef PICO_OSDEP_READ_INCLUDED
+#define PICO_OSDEP_READ_INCLUDED
+
+
+#include <general.h>
+
+
+/* exported prototypes */
+#ifndef _WINDOWS
+UCS input_ready(int);
+int read_one_char(void);
+#else /* _WINDOWS */
+int set_time_of_last_input(void);
+#endif /* _WINDOWS */
+
+time_t time_of_last_input(void);
+
+
+#endif /* PICO_OSDEP_READ_INCLUDED */
diff --git a/pico/osdep/resource.h b/pico/osdep/resource.h
new file mode 100644
index 00000000..4796f16a
--- /dev/null
+++ b/pico/osdep/resource.h
@@ -0,0 +1,189 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by mswin.rc
+//
+#define IDD_OK 1
+#define ABOUTDLGBOX 100
+#define SPLASHDLGBOX 101
+#define IDM_OPT_SETFONT 103
+#define IDM_ABOUT 104
+#define IDM_EDIT_CUT 105
+#define IDM_EDIT_COPY 106
+#define IDM_EDIT_PASTE 107
+#define TEXTWINMENU 108
+#define IDM_EDIT_CANCEL_PASTE 109
+#define IDM_HELP 110
+#define IDD_TOOLBAR 110
+#define IDM_EDIT_COPY_APPEND 111
+#define IDD_COMPOSER_TB 111
+#define IDM_FILE_EXIT 112
+#define IDD_OPTIONALYENTER 112
+#define IDM_OPT_FONTSAMEAS 113
+#define IDD_SELECT 113
+#define IDM_OPT_SETPRINTFONT 114
+#define IDD_SELECTSORT 114
+#define IDM_FILE_CLOSE 115
+#define IDR_ACCEL_PINE 115
+#define IDD_SELECTFLAG 115
+#define IDM_FILE_PRINT 116
+#define IDD_LOGINDLG 116
+#define IDM_OPT_TOOLBAR 117
+#define IDD_CONFIGDLG 117
+#define IDM_OPT_TOOLBARPOS 118
+#define IDD_CFVARSDLG 118
+#define IDM_OPT_USEDIALOGS 119
+#define IDD_LOGINDLG2 119
+#define IDM_OPT_USEACCEL 120
+#define IDM_OPT_IMAPTELEM 121
+#define IDM_EDIT_SEL_ALL 122
+#define IDM_MI_SORTSUBJECT 123
+#define IDM_MI_SORTARRIVAL 124
+#define IDM_MI_SORTFROM 125
+#define IDM_MI_SORTTO 126
+#define IDM_MI_SORTCC 127
+#define IDM_MI_SORTDATE 128
+#define IDM_MI_SORTSIZE 129
+#define IDM_MI_SORTORDERSUB 130
+#define IDM_MI_SORTSCORE 131
+#define IDM_MI_SORTTHREAD 132
+#define IDM_MI_SORTREVERSE 133
+#define IDM_MI_FLAGIMPORTANT 134
+#define IDM_MI_FLAGNEW 135
+#define IDM_MI_FLAGANSWERED 136
+#define IDM_MI_FLAGDELETED 137
+
+// This group must all be consecutive.
+// These are all regular commands that end
+// up in menus at the top of the screen and
+// there are a couple checks for the range (see msmenu.h).
+#define IDM_MI_VIEW 150
+#define IDM_MI_EXPUNGE 151
+#define IDM_MI_ZOOM 152
+#define IDM_MI_SORT 153
+#define IDM_MI_HDRMODE 154
+#define IDM_MI_MAINMENU 155
+#define IDM_MI_FLDRLIST 156
+#define IDM_MI_FLDRINDEX 157
+#define IDM_MI_COMPOSER 158
+#define IDM_MI_PREVPAGE 159
+#define IDM_MI_PREVMSG 160
+#define IDM_MI_NEXTMSG 161
+#define IDM_MI_ADDRBOOK 162
+#define IDM_MI_WHEREIS 163
+#define IDM_MI_PRINT 164
+#define IDM_MI_REPLY 165
+#define IDM_MI_FORWARD 166
+#define IDM_MI_BOUNCE 167
+#define IDM_MI_DELETE 168
+#define IDM_MI_UNDELETE 169
+#define IDM_MI_FLAG 170
+#define IDM_MI_SAVE 171
+#define IDM_MI_EXPORT 172
+#define IDM_MI_TAKEADDR 173
+#define IDM_MI_SELECT 174
+#define IDM_MI_APPLY 175
+#define IDM_MI_POSTPONE 176
+#define IDM_MI_SEND 177
+#define IDM_MI_CANCEL 178
+#define IDM_MI_ATTACH 179
+#define IDM_MI_TOADDRBOOK 180
+#define IDM_MI_READFILE 181
+#define IDM_MI_JUSTIFY 182
+#define IDM_MI_ALTEDITOR 183
+#define IDM_MI_GENERALHELP 184
+#define IDM_MI_SCREENHELP 185
+#define IDM_MI_EXIT 186
+#define IDM_MI_NEXTPAGE 187
+#define IDM_MI_SAVEFILE 188
+#define IDM_MI_CURPOSITION 189
+#define IDM_MI_GOTOFLDR 190
+#define IDM_MI_JUMPTOMSG 191
+#define IDM_MI_RICHHDR 192
+#define IDM_MI_EXITMODE 193
+#define IDM_MI_REVIEW 194
+#define IDM_MI_KEYMENU 195
+#define IDM_MI_SELECTCUR 196
+#define IDM_MI_UNDO 197
+#define IDM_MI_SPELLCHK 198
+
+#define IDD_ARGLIST 199
+#define IDM_OPT_CARETBLOCK 200
+#define IDM_OPT_CARETSMALLBLOCK 201
+#define IDM_OPT_CARETHBAR 202
+#define IDM_OPT_CARETVBAR 203
+#define IDM_OPT_NEWMAILWIN 204
+#define IDM_EDIT_CLEAR 205
+#define IDM_OPT_ERASE_CREDENTIALS 205
+#define IDM_MI_VIEWINWIND 206
+#define ALPINEMENU 300
+#define COMPOSERMENU 301
+#define ALPINEICON 400
+#define NEWMAILICON 401
+#define PICOHAND 402
+#define MCLOSEDICON 403
+#define ALPINESPLASH 501
+#define IDD_ABOUTICON 0x210
+#define IDD_VERSION 0x212
+#define IDD_BYLINE 0x213
+#define IDS_BYLINE 773
+#define IDS_APPNAME 774
+#define IDS_APPIDENT 775
+#define IDC_RESPONCE 1002
+#define IDC_PROMPT 1003
+#define IDC_GETHELP 1004
+#define IDC_SORTARRIVAL 1005
+#define IDC_SORTFIRSTBUTTON IDC_SORTARRIVAL
+#define IDC_SORTORDERSUB 1006
+#define IDC_SORTSUBJECT 1007
+#define IDC_SORTSIZE 1008
+#define IDC_SORTCC 1009
+#define IDC_SORTTO 1010
+#define IDC_SORTFROM 1011
+#define IDC_SORTDATE 1012
+#define IDC_SORTTHREAD 1013
+#define IDC_SORTSCORE 1014
+#define IDC_SORTLASTBUTTON IDC_SORTSCORE
+#define IDC_SORTREVERSE 1015
+#define IDC_FLAGCOL1 1016
+#define IDC_FLAGCOL2 1017
+#define IDC_BUTTON2 1018
+#define IDC_ARGTEXT 1019
+#define IDC_RLOGINE 1020
+#define IDC_RPASSWORD 1021
+#define IDC_RPWTEXT 1022
+#define IDC_PRESPASS 1023
+#define IDC_CONFRRADIO 1023
+#define IDC_CFV_PNAME 1023
+#define IDC_CONFLRADIO 1024
+#define IDC_CFV_EMAILADR 1024
+#define IDC_CONFSRVRTXT 1025
+#define IDC_CFV_MSERVER 1025
+#define IDC_CONFESERVER 1026
+#define IDC_CFV_IMAP 1026
+#define IDC_CONFUNTXT 1027
+#define IDC_CFV_POP3 1027
+#define IDC_CONFEUSERNAME 1028
+#define IDC_CFV_LOGIN 1028
+#define IDC_CONFDFLTFLDR 1029
+#define IDC_CFV_SMTPSERVER 1029
+#define IDC_CONFFLDRTXT 1030
+#define IDC_CFV_DEFMAILER 1030
+#define IDC_CONFEFLDRNAME 1031
+#define IDC_CFV_DEFNEWSRDR 1031
+#define IDC_CONFFNTXT 1032
+#define IDC_CONFEFN 1033
+#define IDC_CONFBROWSE 1034
+#define IDC_CONFDFLTSET 1035
+#define IDC_CONFTEXT 1036
+#define IDC_STATIC -1
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 116
+#define _APS_NEXT_COMMAND_VALUE 122
+#define _APS_NEXT_CONTROL_VALUE 1023
+#define _APS_NEXT_SYMED_VALUE 103
+#endif
+#endif
diff --git a/pico/osdep/shell.c b/pico/osdep/shell.c
new file mode 100644
index 00000000..cdb701dd
--- /dev/null
+++ b/pico/osdep/shell.c
@@ -0,0 +1,171 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: shell.c 807 2007-11-09 01:21:33Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#include <system.h>
+#include <general.h>
+
+#include "../estruct.h"
+#include "../mode.h"
+#include "../pico.h"
+#include "../edef.h"
+#include "../efunc.h"
+#include "../keydefs.h"
+
+#include "tty.h"
+#include "signals.h"
+
+#ifdef _WINDOWS
+#include "mswin.h"
+#endif
+
+#include "shell.h"
+
+
+
+/* internal prototypes */
+#ifdef SIGCONT
+RETSIGTYPE rtfrmshell(int);
+#endif
+
+
+
+#ifdef SIGTSTP
+/*
+ * bktoshell - suspend and wait to be woken up
+ */
+int
+bktoshell(int f, int n)
+{
+ UCS z = CTRL | 'Z';
+
+ if(!(gmode&MDSSPD)){
+ unknown_command(z);
+ return(FALSE);
+ }
+
+ if(Pmaster){
+ if(!Pmaster->suspend){
+ unknown_command(z);
+ return(FALSE);
+ }
+
+ if((*Pmaster->suspend)() == NO_OP_COMMAND){
+ int rv;
+
+ if(km_popped){
+ term.t_mrow = 2;
+ curwp->w_ntrows -= 2;
+ }
+
+ clearcursor();
+ mlerase();
+ rv = (*Pmaster->showmsg)('x');
+ ttresize();
+ picosigs();
+ if(rv) /* Did showmsg corrupt the display? */
+ pico_refresh(0, 1); /* Yes, repaint */
+
+ mpresf = 1;
+ if(km_popped){
+ term.t_mrow = 0;
+ curwp->w_ntrows += 2;
+ }
+ }
+ else{
+ ttresize();
+ pclear(0, term.t_nrow);
+ pico_refresh(0, 1);
+ }
+
+ return(TRUE);
+ }
+
+ if(gmode&MDSPWN){
+ char *shell;
+ int dummy;
+
+ vttidy();
+ movecursor(0, 0);
+ (*term.t_eeop)();
+ printf("\n\n\nUse \"exit\" to return to Pi%s\n",
+ (gmode & MDBRONLY) ? "lot" : "co");
+ system((shell = (char *)getenv("SHELL")) ? shell : "/bin/csh");
+ rtfrmshell(dummy); /* fixup tty */
+ }
+ else {
+ movecursor(term.t_nrow-1, 0);
+ peeol();
+ movecursor(term.t_nrow, 0);
+ peeol();
+ movecursor(term.t_nrow, 0);
+ printf("\n\n\nUse \"fg\" to return to Pi%s\n",
+ (gmode & MDBRONLY) ? "lot" : "co");
+ ttclose();
+ movecursor(term.t_nrow, 0);
+ peeol();
+ (*term.t_flush)();
+
+ signal(SIGCONT, rtfrmshell); /* prepare to restart */
+ signal(SIGTSTP, SIG_DFL); /* prepare to stop */
+ kill(0, SIGTSTP);
+ }
+
+ return(TRUE);
+}
+#else
+#ifdef _WINDOWS
+int
+bktoshell(int f, int n)
+{
+ UCS z = CTRL | 'Z';
+
+ if(!(gmode&MDSSPD)){
+ unknown_command(z);
+ return(FALSE);
+ }
+ if(Pmaster){
+ if(!Pmaster->suspend){
+ unknown_command(z);
+ return(FALSE);
+ }
+ (*Pmaster->suspend)();
+ return(TRUE);
+ }
+
+ mswin_minimize();
+ return(TRUE);
+}
+#endif /* _WINDOWS */
+
+#endif /* SIGTSTP */
+
+#ifdef SIGCONT
+/*
+ * rtfrmshell - back from shell, fix modes and return
+ */
+RETSIGTYPE
+rtfrmshell(int sig)
+{
+ signal(SIGCONT, SIG_DFL);
+ ttopen();
+ ttresize();
+ pclear(0, term.t_nrow);
+ pico_refresh(0, 1);
+}
+#endif /* SIGCONT */
diff --git a/pico/osdep/shell.h b/pico/osdep/shell.h
new file mode 100644
index 00000000..f356485f
--- /dev/null
+++ b/pico/osdep/shell.h
@@ -0,0 +1,27 @@
+/*
+ * $Id: shell.h 807 2007-11-09 01:21:33Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef PICO_OSDEP_SHELL_INCLUDED
+#define PICO_OSDEP_SHELL_INCLUDED
+
+
+/* exported prototypes */
+#if defined(SIGTSTP) || defined(_WINDOWS)
+int bktoshell(int, int);
+#endif
+
+
+#endif /* PICO_OSDEP_SHELL_INCLUDED */
diff --git a/pico/osdep/signals.c b/pico/osdep/signals.c
new file mode 100644
index 00000000..10f02ba1
--- /dev/null
+++ b/pico/osdep/signals.c
@@ -0,0 +1,181 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: signals.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "../headers.h"
+
+#include "../../pith/charconv/filesys.h"
+
+#include "tty.h"
+
+#include "signals.h"
+
+
+/* internal prototypes */
+#ifndef _WINDOWS
+RETSIGTYPE do_hup_signal(int);
+#endif
+
+
+/*
+ * picosigs - Install any handlers for the signals we're interested
+ * in catching.
+ */
+void
+picosigs(void)
+{
+#ifndef _WINDOWS
+ signal(SIGHUP, do_hup_signal); /* deal with SIGHUP */
+ signal(SIGTERM, do_hup_signal); /* deal with SIGTERM */
+#ifdef SIGTSTP
+ signal(SIGTSTP, SIG_DFL);
+#endif
+#if defined(SIGWINCH) && defined(TIOCGWINSZ)
+ signal(SIGWINCH, winch_handler); /* window size changes */
+#endif
+#endif /* !_WINDOWS */
+}
+
+
+
+
+#ifndef _WINDOWS
+/*
+ * do_hup_signal - jump back in the stack to where we can handle this
+ */
+RETSIGTYPE
+do_hup_signal(int sig)
+{
+ signal(SIGHUP, SIG_IGN); /* ignore further SIGHUP's */
+ signal(SIGTERM, SIG_IGN); /* ignore further SIGTERM's */
+ if(Pmaster){
+ extern jmp_buf finstate;
+
+ longjmp(finstate, COMP_GOTHUP);
+ }
+ else{
+ /*
+ * if we've been interrupted and the buffer is changed,
+ * save it...
+ */
+ if(anycb() == TRUE){ /* time to save */
+ if(curbp->b_fname[0] == '\0'){ /* name it */
+ strncpy(curbp->b_fname, "pico.save", sizeof(curbp->b_fname));
+ curbp->b_fname[sizeof(curbp->b_fname)-1] = '\0';
+ }
+ else{
+ strncat(curbp->b_fname, ".save", sizeof(curbp->b_fname)-strlen(curbp->b_fname)-1);
+ curbp->b_fname[sizeof(curbp->b_fname)-1] = '\0';
+ }
+
+ our_unlink(curbp->b_fname);
+ writeout(curbp->b_fname, TRUE);
+ }
+
+ vttidy();
+ exit(1);
+ }
+}
+#endif /* !_WINDOWS */
+
+
+#if defined(SIGWINCH) && defined(TIOCGWINSZ)
+/*
+ * winch_handler - handle window change signal
+ */
+RETSIGTYPE
+winch_handler(int sig)
+{
+ int i;
+ signal(SIGWINCH, winch_handler);
+ if(wheadp != NULL)
+ ttresize();
+ else if (Pmaster){
+ i = Pmaster->arm_winch_cleanup;
+ Pmaster->arm_winch_cleanup = 1;
+ }
+ if(Pmaster && Pmaster->winch_cleanup && Pmaster->arm_winch_cleanup)
+ (*Pmaster->winch_cleanup)();
+ if(wheadp == NULL && Pmaster != NULL)
+ Pmaster->arm_winch_cleanup = i;
+}
+#endif /* SIGWINCH and friends */
+
+
+
+#ifdef POSIX_SIGNALS
+/*----------------------------------------------------------------------
+ Reset signals after critical imap code
+ ----*/
+void
+(*posix_signal(int sig_num, RETSIGTYPE (*action)(int)))(int)
+{
+ struct sigaction new_action, old_action;
+
+ memset((void *)&new_action, 0, sizeof(struct sigaction));
+ sigemptyset (&new_action.sa_mask);
+ new_action.sa_handler = action;
+#ifdef SA_RESTART
+ new_action.sa_flags = SA_RESTART;
+#else
+ new_action.sa_flags = 0;
+#endif
+ sigaction(sig_num, &new_action, &old_action);
+ return(old_action.sa_handler);
+}
+
+int
+posix_sigunblock(int mask)
+{
+ sigset_t sig_mask;
+
+ sigemptyset(&sig_mask);
+ sigaddset(&sig_mask, mask);
+#if HAVE_PTHREAD
+# define sigprocmask pthread_sigmask
+#endif
+ return(sigprocmask(SIG_UNBLOCK, &sig_mask, NULL));
+}
+#endif /* POSIX_SIGNALS */
+
+
+#if defined(sv3) || defined(ct)
+/* Placed by rll to handle the rename function not found in AT&T */
+int
+rename(char *oldname, char *newname)
+{
+ int rtn;
+ char b[NLINE];
+
+ strncpy(b, fname_to_locale(oldname), sizeof(b));
+ b[sizeof(b)-1] = '\0';
+
+ if ((rtn = link(b, fname_to_locale(newname))) != 0) {
+ perror("Was not able to rename file.");
+ return(rtn);
+ }
+
+ if ((rtn = our_unlink(b)) != 0)
+ perror("Was not able to unlink file.");
+
+ return(rtn);
+}
+#endif
+
diff --git a/pico/osdep/signals.h b/pico/osdep/signals.h
new file mode 100644
index 00000000..2ea33cc4
--- /dev/null
+++ b/pico/osdep/signals.h
@@ -0,0 +1,62 @@
+/*
+ * $Id: signals.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef PICO_OSDEP_SIGNALS_INCLUDED
+#define PICO_OSDEP_SIGNALS_INCLUDED
+
+
+/*
+ * [Re]Define signal functions as needed...
+ */
+#ifdef POSIX_SIGNALS
+/*
+ * Redefine signal call to our wrapper of POSIX sigaction
+ */
+#define signal(SIG,ACT) posix_signal(SIG,ACT)
+#define our_sigunblock(SIG) posix_sigunblock(SIG)
+#else /* !POSIX_SIGNALS */
+#ifdef SYSV_SIGNALS
+/*
+ * Redefine signal calls to SYSV style call.
+ */
+#define signal(SIG,ACT) sigset(SIG,ACT)
+#define our_sigunblock(SIG) sigrelse(SIG)
+#else /* !SYSV_SIGNALS */
+#ifdef _WINDOWS
+#define our_sigunblock(SIG)
+#else /* !_WINDOWS */
+/*
+ * Good ol' BSD signals.
+ */
+#define our_sigunblock(SIG)
+#endif /* !_WINDOWS */
+#endif /* !SYSV_SIGNALS */
+#endif /* !POSIX_SIGNALS */
+
+
+
+/* exported prototypes */
+void picosigs(void);
+#if defined(SIGWINCH) && defined(TIOCGWINSZ)
+RETSIGTYPE winch_handler(int);
+#endif
+
+#ifdef POSIX_SIGNALS
+void (*posix_signal(int, RETSIGTYPE (*)()))(int);
+int posix_sigunblock(int);
+#endif
+
+#endif /* PICO_OSDEP_SIGNALS_INCLUDED */
diff --git a/pico/osdep/spell.c b/pico/osdep/spell.c
new file mode 100644
index 00000000..87b345eb
--- /dev/null
+++ b/pico/osdep/spell.c
@@ -0,0 +1,323 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: spell.c 854 2007-12-07 17:44:43Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ *
+ * Program: spell.c
+ */
+
+
+#include <system.h>
+#include <general.h>
+
+#include "../headers.h"
+/*
+#include "../estruct.h"
+#include "../mode.h"
+#include "../pico.h"
+#include "../edef.h"
+#include "../efunc.h"
+#include "../keydefs.h"
+*/
+
+#include "../../pith/charconv/filesys.h"
+
+#include "popen.h"
+#include "altedit.h"
+#include "spell.h"
+
+
+#ifdef SPELLER
+
+int movetoword(UCS *);
+
+
+static char *spellhelp[] = {
+/* TRANSLATORS: Some help text. The ~ characters cause
+ the characters they are in front of to be bold. */
+ N_("Spell Check Help"),
+ " ",
+ N_(" The spell checker examines all words in the text. It then"),
+ N_(" offers each misspelled word for correction while simultaneously"),
+ N_(" highlighting it in the text. To leave a word unchanged simply"),
+ N_("~ hit ~R~e~t~u~r~n at the edit prompt. If a word has been corrected,"),
+ N_(" each occurrence of the incorrect word is offered for replacement."),
+ " ",
+ N_("~ Spell checking can be cancelled at any time by typing ~^~C (~F~3)"),
+ N_(" after exiting help."),
+ " ",
+ N_("End of Spell Check Help"),
+ " ",
+ NULL
+};
+
+
+static char *pinespellhelp[] = {
+ N_("Spell Check Help"),
+ " ",
+ N_("\tThe spell checker examines all words in the text. It then"),
+ N_("\toffers each misspelled word for correction while simultaneously"),
+ N_("\thighlighting it in the text. To leave a word unchanged simply"),
+ N_("\thit Return at the edit prompt. If a word has been corrected,"),
+ N_("\teach occurrence of the incorrect word is offered for replacement."),
+ " ",
+ N_("\tSpell checking can be cancelled at any time by typing ^C (F3)"),
+ N_("\tafter exiting help."),
+ " ",
+ N_("End of Spell Check Help"),
+ " ",
+ NULL
+};
+
+#ifndef _WINDOWS
+/*
+ * spell() - check for potentially missspelled words and offer them for
+ * correction
+ */
+int
+spell(int f, int n)
+{
+ int status, next, ret;
+ char ccb[NLINE], *sp, *fn, *lp, *wsp, c, spc[NLINE];
+ UCS *b;
+ UCS wb[NLINE], cb[NLINE];
+ EML eml;
+
+ setimark(0, 1);
+ emlwrite(_("Checking spelling..."), NULL); /* greetings */
+
+ if(alt_speller)
+ return(alt_editor(1, 0)); /* f == 1 means fork speller */
+
+ if((fn = writetmp(0, NULL)) == NULL){
+ emlwrite(_("Can't write temp file for spell checker"), NULL);
+ return(-1);
+ }
+
+ if((sp = (char *)getenv("SPELL")) == NULL)
+ sp = SPELLER;
+
+ /* exists? */
+ ret = (strlen(sp) + 1);
+ snprintf(spc, sizeof(spc), "%s", sp);
+
+ for(lp = spc, ret = FIOERR; *lp; lp++){
+ if((wsp = strpbrk(lp, " \t")) != NULL){
+ c = *wsp;
+ *wsp = '\0';
+ }
+
+ if(strchr(lp, '/')){
+ ret = fexist(lp, "x", (off_t *)NULL);
+ }
+ else{
+ char *path, fname[MAXPATH+1];
+
+ if(!(path = getenv("PATH")))
+ path = ":/bin:/usr/bin";
+
+ ret = ~FIOSUC;
+ while(ret != FIOSUC && *path && pathcat(fname, &path, lp))
+ ret = fexist(fname, "x", (off_t *)NULL);
+ }
+
+ if(wsp)
+ *wsp = c;
+
+ if(ret == FIOSUC)
+ break;
+ }
+
+ if(ret != FIOSUC){
+ eml.s = sp;
+ emlwrite(_("\007Spell-checking file \"%s\" not found"), &eml);
+ return(-1);
+ }
+
+ snprintf(ccb, sizeof(ccb), "( %s ) < %s", sp, fn);
+ if(P_open(ccb) != FIOSUC){ /* read output from command */
+ our_unlink(fn);
+ emlwrite(_("Can't fork spell checker"), NULL);
+ return(-1);
+ }
+
+ ret = 1;
+ while(ffgetline(wb, NLINE, NULL, 0) == FIOSUC && ret){
+ if((b = ucs4_strchr(wb, (UCS) '\n')) != NULL)
+ *b = '\0';
+
+ ucs4_strncpy(cb, wb, NLINE);
+ cb[NLINE-1] = '\0';
+
+ gotobob(0, 1);
+
+ status = TRUE;
+ next = 1;
+
+ while(status){
+ if(next++)
+ if(movetoword(wb) != TRUE)
+ break;
+
+ update();
+ (*term.t_rev)(1);
+ pputs(wb, 1); /* highlight word */
+ (*term.t_rev)(0);
+
+ if(ucs4_strcmp(cb, wb)){
+ char prompt[2*NLINE + 32];
+ char *wbu, *cbu;
+
+ wbu = ucs4_to_utf8_cpystr(wb);
+ cbu = ucs4_to_utf8_cpystr(cb);
+
+ snprintf(prompt, sizeof(prompt), _("Replace \"%s\" with \"%s\""), wbu, cbu);
+ status=mlyesno_utf8(prompt, TRUE);
+ if(wbu)
+ fs_give((void **) &wbu);
+ if(cbu)
+ fs_give((void **) &cbu);
+ }
+ else{
+ UCS *p;
+
+ p = utf8_to_ucs4_cpystr(_("Edit a replacement: "));
+ status=mlreplyd(p, cb, NLINE, QDEFLT, NULL);
+ if(p)
+ fs_give((void **) &p);
+ }
+
+
+ curwp->w_flag |= WFMOVE; /* put cursor back */
+ sgarbk = 0; /* fake no-keymenu-change! */
+ update();
+ pputs(wb, 0); /* un-highlight */
+
+ switch(status){
+ case TRUE:
+ chword(wb, cb); /* correct word */
+ case FALSE:
+ update(); /* place cursor */
+ break;
+ case ABORT:
+ emlwrite(_("Spell Checking Cancelled"), NULL);
+ ret = FALSE;
+ status = FALSE;
+ break;
+ case HELPCH:
+ if(Pmaster){
+ VARS_TO_SAVE *saved_state;
+
+ saved_state = save_pico_state();
+ (*Pmaster->helper)(pinespellhelp,
+ _("Help with Spelling Checker"), 1);
+ if(saved_state){
+ restore_pico_state(saved_state);
+ free_pico_state(saved_state);
+ }
+ }
+ else
+ pico_help(spellhelp, _("Help with Spelling Checker"), 1);
+
+ case (CTRL|'L'):
+ next = 0; /* don't get next word */
+ sgarbf = TRUE; /* repaint full screen */
+ update();
+ status = TRUE;
+ continue;
+ default:
+ emlwrite("Huh?", NULL); /* shouldn't get here, but.. */
+ status = TRUE;
+ sleep(1);
+ break;
+ }
+
+ forwword(0, 1); /* goto next word */
+ }
+ }
+
+ P_close(); /* clean up */
+ our_unlink(fn);
+ swapimark(0, 1);
+ curwp->w_flag |= WFHARD|WFMODE;
+ sgarbk = TRUE;
+
+ if(ret)
+ emlwrite(_("Done checking spelling"), NULL);
+
+ return(ret);
+}
+
+
+#endif /* UNIX */
+
+
+/*
+ * movetoword() - move to the first occurance of the word w
+ *
+ * returns:
+ * TRUE upon success
+ * FALSE otherwise
+ */
+int
+movetoword(UCS *w)
+{
+ int i;
+ int ret = FALSE;
+ int olddoto;
+ LINE *olddotp;
+ register int off; /* curwp offset */
+ register LINE *lp; /* curwp line */
+
+ olddoto = curwp->w_doto; /* save where we are */
+ olddotp = curwp->w_dotp;
+
+ curwp->w_bufp->b_mode |= MDEXACT; /* case sensitive */
+ while(forscan(&i, w, NULL, 0, 1) == TRUE){
+ if(i)
+ break; /* wrap NOT allowed! */
+
+ lp = curwp->w_dotp; /* for convenience */
+ off = curwp->w_doto;
+
+ /*
+ * We want to minimize the number of substrings that we report
+ * as matching a misspelled word...
+ */
+ if(off == 0 || !ucs4_isalpha(lgetc(lp, off - 1).c)){
+ off += ucs4_strlen(w);
+ if((!ucs4_isalpha(lgetc(lp, off).c) || off == llength(lp))
+ && lgetc(lp, 0).c != '>'){
+ ret = TRUE;
+ break;
+ }
+ }
+
+ forwchar(0, 1); /* move on... */
+
+ }
+ curwp->w_bufp->b_mode ^= MDEXACT; /* case insensitive */
+
+ if(ret == FALSE){
+ curwp->w_dotp = olddotp;
+ curwp->w_doto = olddoto;
+ }
+ else
+ curwp->w_flag |= WFHARD;
+
+ return(ret);
+}
+
+#endif /* SPELLER */
diff --git a/pico/osdep/spell.h b/pico/osdep/spell.h
new file mode 100644
index 00000000..ed7a4428
--- /dev/null
+++ b/pico/osdep/spell.h
@@ -0,0 +1,25 @@
+/*
+ * $Id: spell.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef PICO_OSDEP_SPELL_INCLUDED
+#define PICO_OSDEP_SPELL_INCLUDED
+
+
+/* exported prototypes */
+int spell(int, int);
+
+
+#endif /* PICO_OSDEP_SPELL_INCLUDED */
diff --git a/pico/osdep/terminal.c b/pico/osdep/terminal.c
new file mode 100644
index 00000000..72206c01
--- /dev/null
+++ b/pico/osdep/terminal.c
@@ -0,0 +1,1762 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: terminal.c 921 2008-01-31 02:09:25Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ *
+ * Program: Display routines
+ */
+
+#include <system.h>
+#include <general.h>
+
+
+#include "../estruct.h"
+#include "../keydefs.h"
+#include "../pico.h"
+#include "../mode.h"
+
+#include "raw.h"
+#include "color.h"
+#include "tty.h"
+#include "terminal.h"
+#include "getkey.h"
+
+#ifndef _WINDOWS
+extern long gmode;
+
+
+/*
+ * Useful Defaults
+ */
+#define MARGIN 8 /* size of minimim margin and */
+#define MROW 2 /* rows in menu */
+
+
+/* any special properties of the current terminal */
+unsigned term_capabilities = (TT_EOLEXIST | TT_SCROLLEXIST | TT_INSCHAR | TT_DELCHAR);
+
+
+/*
+ *
+ */
+unsigned
+tthascap(void)
+{
+ return(term_capabilities);
+}
+
+
+#if HAS_TERMINFO
+
+/*
+ * terminfo-based terminal i/o and control routines
+ */
+
+
+/* internal prototypes */
+static int tinfomove(int, int);
+static int tinfoeeol(void);
+static int tinfoeeop(void);
+static int tinfobeep(void);
+static int tinforev(int);
+static int tinfoopen(void);
+static int tinfoterminalinfo(int);
+static int tinfoclose(void);
+static void setup_dflt_esc_seq(void);
+static void tinfoinsert(UCS);
+static void tinfodelete(void);
+
+extern int tput();
+extern int tputs(char *, int, int (*)(int));
+extern char *tgoto(char *, int, int);
+extern char *tigetstr ();
+extern int setupterm(char *, int, int *);
+extern int tigetnum(char *);
+extern int tigetflag(char *);
+
+/**
+ ** Note: The tgoto calls should really be replaced by tparm calls for
+ ** modern terminfo. tgoto(s, x, y) == tparm(s, y, x).
+ **/
+
+int _tlines, _tcolumns;
+char *_clearscreen, *_moveto, *_up, *_down, *_right, *_left,
+ *_setinverse, *_clearinverse,
+ *_setunderline, *_clearunderline,
+ *_setbold, *_clearallattr, /* there is no clear only bold! */
+ *_cleartoeoln, *_cleartoeos,
+ *_deleteline, /* delete line */
+ *_insertline, /* insert line */
+ *_scrollregion, /* define a scrolling region, vt100 */
+ *_insertchar, /* insert character, preferable to : */
+ *_startinsert, /* set insert mode and, */
+ *_endinsert, /* end insert mode */
+ *_deletechar, /* delete character */
+ *_startdelete, /* set delete mode and, */
+ *_enddelete, /* end delete mode */
+ *_scrolldown, /* scroll down */
+ *_scrollup, /* scroll up */
+ *_termcap_init, /* string to start termcap */
+ *_termcap_end, /* string to end termcap */
+ *_op, *_oc, *_setaf, *_setab, *_setf, *_setb, *_scp;
+int _colors, _pairs, _bce;
+char term_name[40];
+
+TERM term = {
+ NROW-1,
+ NCOL,
+ MARGIN,
+ MROW,
+ tinfoopen,
+ tinfoterminalinfo,
+ tinfoclose,
+ ttgetc,
+ ttputc,
+ ttflush,
+ tinfomove,
+ tinfoeeol,
+ tinfoeeop,
+ tinfobeep,
+ tinforev
+};
+
+
+/*
+ * Add default keypad sequences to the trie.
+ */
+static void
+setup_dflt_esc_seq(void)
+{
+ /*
+ * this is sort of a hack [no kidding], but it allows us to use
+ * the function keys on pc's running telnet
+ */
+
+ /*
+ * UW-NDC/UCS vt10[02] application mode.
+ */
+ kpinsert("\033OP", F1, 1);
+ kpinsert("\033OQ", F2, 1);
+ kpinsert("\033OR", F3, 1);
+ kpinsert("\033OS", F4, 1);
+ kpinsert("\033Op", F5, 1);
+ kpinsert("\033Oq", F6, 1);
+ kpinsert("\033Or", F7, 1);
+ kpinsert("\033Os", F8, 1);
+ kpinsert("\033Ot", F9, 1);
+ kpinsert("\033Ou", F10, 1);
+ kpinsert("\033Ov", F11, 1);
+ kpinsert("\033Ow", F12, 1);
+
+ /*
+ * DEC vt100, ANSI and cursor key mode.
+ */
+ kpinsert("\033OA", KEY_UP, 1);
+ kpinsert("\033OB", KEY_DOWN, 1);
+ kpinsert("\033OC", KEY_RIGHT, 1);
+ kpinsert("\033OD", KEY_LEFT, 1);
+
+ /*
+ * special keypad functions
+ */
+ kpinsert("\033[4J", KEY_PGUP, 1);
+ kpinsert("\033[3J", KEY_PGDN, 1);
+ kpinsert("\033[2J", KEY_HOME, 1);
+ kpinsert("\033[N", KEY_END, 1);
+
+ /*
+ * vt220?
+ */
+ kpinsert("\033[5~", KEY_PGUP, 1);
+ kpinsert("\033[6~", KEY_PGDN, 1);
+ kpinsert("\033[1~", KEY_HOME, 1);
+ kpinsert("\033[4~", KEY_END, 1);
+
+ /*
+ * konsole, XTerm (XFree 4.x.x) keyboard setting
+ */
+ kpinsert("\033[H", KEY_HOME, 1);
+ kpinsert("\033[F", KEY_END, 1);
+
+ /*
+ * gnome-terminal 2.6.0, don't know why it
+ * changed from 2.2.1
+ */
+ kpinsert("\033OH", KEY_HOME, 1);
+ kpinsert("\033OF", KEY_END, 1);
+
+ /*
+ * "\033[2~" was common for KEY_HOME in a quick survey
+ * of terminals (though typically the Insert key).
+ * Teraterm 2.33 sends the following escape sequences,
+ * which is quite incompatible with everything
+ * else:
+ * Home: "\033[2~" End: "\033[5~" PgUp: "\033[3~"
+ * PgDn: "\033[6~"
+ * The best thing to do would be to fix TeraTerm
+ * keymappings or to tweak terminfo.
+ */
+
+ /*
+ * ANSI mode.
+ */
+ kpinsert("\033[=a", F1, 1);
+ kpinsert("\033[=b", F2, 1);
+ kpinsert("\033[=c", F3, 1);
+ kpinsert("\033[=d", F4, 1);
+ kpinsert("\033[=e", F5, 1);
+ kpinsert("\033[=f", F6, 1);
+ kpinsert("\033[=g", F7, 1);
+ kpinsert("\033[=h", F8, 1);
+ kpinsert("\033[=i", F9, 1);
+ kpinsert("\033[=j", F10, 1);
+ kpinsert("\033[=k", F11, 1);
+ kpinsert("\033[=l", F12, 1);
+
+ /*
+ * DEC vt100, ANSI and cursor key mode reset.
+ */
+ kpinsert("\033[A", KEY_UP, 1);
+ kpinsert("\033[B", KEY_DOWN, 1);
+ kpinsert("\033[C", KEY_RIGHT, 1);
+ kpinsert("\033[D", KEY_LEFT, 1);
+
+ /*
+ * DEC vt52 mode.
+ */
+ kpinsert("\033A", KEY_UP, 1);
+ kpinsert("\033B", KEY_DOWN, 1);
+ kpinsert("\033C", KEY_RIGHT, 1);
+ kpinsert("\033D", KEY_LEFT, 1);
+
+ /*
+ * DEC vt52 application keys, and some Zenith 19.
+ */
+ kpinsert("\033?r", KEY_DOWN, 1);
+ kpinsert("\033?t", KEY_LEFT, 1);
+ kpinsert("\033?v", KEY_RIGHT, 1);
+ kpinsert("\033?x", KEY_UP, 1);
+
+ /*
+ * Some Ctrl-Arrow keys
+ */
+ kpinsert("\033[5A", CTRL_KEY_UP, 1);
+ kpinsert("\033[5B", CTRL_KEY_DOWN, 1);
+ kpinsert("\033[5C", CTRL_KEY_RIGHT, 1);
+ kpinsert("\033[5D", CTRL_KEY_LEFT, 1);
+
+ kpinsert("\033[1;5A", CTRL_KEY_UP, 1);
+ kpinsert("\033[1;5B", CTRL_KEY_DOWN, 1);
+ kpinsert("\033[1;5C", CTRL_KEY_RIGHT, 1);
+ kpinsert("\033[1;5D", CTRL_KEY_LEFT, 1);
+
+ /*
+ * Map some shift+up/down/left/right to their shiftless counterparts
+ */
+ kpinsert("\033[1;2A", KEY_UP, 1);
+ kpinsert("\033[1;2B", KEY_DOWN, 1);
+ kpinsert("\033[1;2C", KEY_RIGHT, 1);
+ kpinsert("\033[1;2D", KEY_LEFT, 1);
+ kpinsert("\033[a", KEY_UP, 1);
+ kpinsert("\033[b", KEY_DOWN, 1);
+ kpinsert("\033[c", KEY_RIGHT, 1);
+ kpinsert("\033[d", KEY_LEFT, 1);
+
+ /*
+ * Sun Console sequences.
+ */
+ kpinsert("\033[1", KEY_SWALLOW_Z, 1);
+ kpinsert("\033[215", KEY_SWAL_UP, 1);
+ kpinsert("\033[217", KEY_SWAL_LEFT, 1);
+ kpinsert("\033[219", KEY_SWAL_RIGHT, 1);
+ kpinsert("\033[221", KEY_SWAL_DOWN, 1);
+
+ /*
+ * Kermit App Prog Cmd, gobble until ESC \ (kermit should intercept this)
+ */
+ kpinsert("\033_", KEY_KERMIT, 1);
+
+ /*
+ * Fake a control character.
+ */
+ kpinsert("\033\033", KEY_DOUBLE_ESC, 1);
+}
+
+
+static int
+tinfoterminalinfo(int termcap_wins)
+{
+ char *_ku, *_kd, *_kl, *_kr,
+ *_kppu, *_kppd, *_kphome, *_kpend, *_kpdel,
+ *_kf1, *_kf2, *_kf3, *_kf4, *_kf5, *_kf6,
+ *_kf7, *_kf8, *_kf9, *_kf10, *_kf11, *_kf12;
+ char *ttnm;
+
+ if (Pmaster) {
+ /*
+ * setupterm() automatically retrieves the value
+ * of the TERM variable.
+ */
+ int err;
+ ttnm = getenv("TERM");
+ if(!ttnm)
+ return(-1);
+
+ strncpy(term_name, ttnm, sizeof(term_name));
+ term_name[sizeof(term_name)-1] = '\0';
+ setupterm ((char *) 0, 1, &err);
+ if (err != 1) return(err-2);
+ }
+ else {
+ /*
+ * setupterm() issues a message and exits, if the
+ * terminfo data base is gone or the term type is
+ * unknown, if arg2 is 0.
+ */
+ setupterm ((char *) 0, 1, (int *) 0);
+ }
+
+ _clearscreen = tigetstr("clear");
+ _moveto = tigetstr("cup");
+ _up = tigetstr("cuu1");
+ _down = tigetstr("cud1");
+ _right = tigetstr("cuf1");
+ _left = tigetstr("cub1");
+ _setinverse = tigetstr("smso");
+ _clearinverse = tigetstr("rmso");
+ _setunderline = tigetstr("smul");
+ _clearunderline = tigetstr("rmul");
+ _setbold = tigetstr("bold");
+ _clearallattr = tigetstr("sgr0");
+ _cleartoeoln = tigetstr("el");
+ _cleartoeos = tigetstr("ed");
+ _deletechar = tigetstr("dch1");
+ _insertchar = tigetstr("ich1");
+ _startinsert = tigetstr("smir");
+ _endinsert = tigetstr("rmir");
+ _deleteline = tigetstr("dl1");
+ _insertline = tigetstr("il1");
+ _scrollregion = tigetstr("csr");
+ _scrolldown = tigetstr("ind");
+ _scrollup = tigetstr("ri");
+ _termcap_init = tigetstr("smcup");
+ _termcap_end = tigetstr("rmcup");
+ _startdelete = tigetstr("smdc");
+ _enddelete = tigetstr("rmdc");
+ _ku = tigetstr("kcuu1");
+ _kd = tigetstr("kcud1");
+ _kl = tigetstr("kcub1");
+ _kr = tigetstr("kcuf1");
+ _kppu = tigetstr("kpp");
+ _kppd = tigetstr("knp");
+ _kphome = tigetstr("khome");
+ _kpend = tigetstr("kend");
+ _kpdel = tigetstr("kdch1");
+ _kf1 = tigetstr("kf1");
+ _kf2 = tigetstr("kf2");
+ _kf3 = tigetstr("kf3");
+ _kf4 = tigetstr("kf4");
+ _kf5 = tigetstr("kf5");
+ _kf6 = tigetstr("kf6");
+ _kf7 = tigetstr("kf7");
+ _kf8 = tigetstr("kf8");
+ _kf9 = tigetstr("kf9");
+ _kf10 = tigetstr("kf10");
+ _kf11 = tigetstr("kf11");
+ _kf12 = tigetstr("kf12");
+
+ _colors = tigetnum("colors");
+ _pairs = tigetnum("pairs");
+ _setaf = tigetstr("setaf");
+ _setab = tigetstr("setab");
+ _setf = tigetstr("setf");
+ _setb = tigetstr("setb");
+ _scp = tigetstr("scp");
+ _op = tigetstr("op");
+ _oc = tigetstr("oc");
+ _bce = tigetflag("bce");
+
+ _tlines = tigetnum("lines");
+ if(_tlines == -1){
+ char *er;
+ int rr;
+
+ /* tigetnum failed, try $LINES */
+ er = getenv("LINES");
+ if(er && (rr = atoi(er)) > 0)
+ _tlines = rr;
+ }
+
+ _tcolumns = tigetnum("cols");
+ if(_tcolumns == -1){
+ char *ec;
+ int cc;
+
+ /* tigetnum failed, try $COLUMNS */
+ ec = getenv("COLUMNS");
+ if(ec && (cc = atoi(ec)) > 0)
+ _tcolumns = cc;
+ }
+
+ /*
+ * Add default keypad sequences to the trie.
+ * Since these come first, they will override any conflicting termcap
+ * or terminfo escape sequences defined below. An escape sequence is
+ * considered conflicting if one is a prefix of the other.
+ * So, without TERMCAP_WINS, there will likely be some termcap/terminfo
+ * escape sequences that don't work, because they conflict with default
+ * sequences defined here.
+ */
+ if(!termcap_wins)
+ setup_dflt_esc_seq();
+
+ /*
+ * add termcap/info escape sequences to the trie...
+ */
+
+ if(_ku != NULL && _kd != NULL && _kl != NULL && _kr != NULL){
+ kpinsert(_ku, KEY_UP, termcap_wins);
+ kpinsert(_kd, KEY_DOWN, termcap_wins);
+ kpinsert(_kl, KEY_LEFT, termcap_wins);
+ kpinsert(_kr, KEY_RIGHT, termcap_wins);
+ }
+
+ if(_kppu != NULL && _kppd != NULL){
+ kpinsert(_kppu, KEY_PGUP, termcap_wins);
+ kpinsert(_kppd, KEY_PGDN, termcap_wins);
+ }
+
+ kpinsert(_kphome, KEY_HOME, termcap_wins);
+ kpinsert(_kpend, KEY_END, termcap_wins);
+ kpinsert(_kpdel, KEY_DEL, termcap_wins);
+
+ kpinsert(_kf1, F1, termcap_wins);
+ kpinsert(_kf2, F2, termcap_wins);
+ kpinsert(_kf3, F3, termcap_wins);
+ kpinsert(_kf4, F4, termcap_wins);
+ kpinsert(_kf5, F5, termcap_wins);
+ kpinsert(_kf6, F6, termcap_wins);
+ kpinsert(_kf7, F7, termcap_wins);
+ kpinsert(_kf8, F8, termcap_wins);
+ kpinsert(_kf9, F9, termcap_wins);
+ kpinsert(_kf10, F10, termcap_wins);
+ kpinsert(_kf11, F11, termcap_wins);
+ kpinsert(_kf12, F12, termcap_wins);
+
+ /*
+ * Add default keypad sequences to the trie.
+ * Since these come after the termcap/terminfo escape sequences above,
+ * the termcap/info sequences will override any conflicting default
+ * escape sequences defined here.
+ * So, with TERMCAP_WINS, some of the default sequences will be missing.
+ * This means that you'd better get all of your termcap/terminfo entries
+ * correct if you define TERMCAP_WINS.
+ */
+ if(termcap_wins)
+ setup_dflt_esc_seq();
+
+ if(Pmaster)
+ return(0);
+ else
+ return(TRUE);
+}
+
+static int
+tinfoopen(void)
+{
+ int row, col;
+
+ /*
+ * determine the terminal's communication speed and decide
+ * if we need to do optimization ...
+ */
+ if(ttisslow())
+ term_capabilities |= TT_OPTIMIZE;
+
+ col = _tcolumns;
+ row = _tlines;
+ if(row >= 0)
+ row--;
+
+ ttgetwinsz(&row, &col);
+ term.t_nrow = (short) row;
+ term.t_ncol = (short) col;
+
+ if(_cleartoeoln != NULL) /* able to use clear to EOL? */
+ term_capabilities |= TT_EOLEXIST;
+ else
+ term_capabilities &= ~TT_EOLEXIST;
+
+ if(_setinverse != NULL)
+ term_capabilities |= TT_REVEXIST;
+ else
+ term_capabilities &= ~TT_REVEXIST;
+
+ if(_deletechar == NULL && (_startdelete == NULL || _enddelete == NULL))
+ term_capabilities &= ~TT_DELCHAR;
+
+ if(_insertchar == NULL && (_startinsert == NULL || _endinsert == NULL))
+ term_capabilities &= ~TT_INSCHAR;
+
+ if((_scrollregion == NULL || _scrolldown == NULL || _scrollup == NULL)
+ && (_deleteline == NULL || _insertline == NULL))
+ term_capabilities &= ~TT_SCROLLEXIST;
+
+ if(_clearscreen == NULL || _moveto == NULL || _up == NULL){
+ if(Pmaster == NULL){
+ puts("Incomplete terminfo entry\n");
+ exit(1);
+ }
+ }
+
+ ttopen();
+
+ if(_termcap_init && !Pmaster) {
+ putpad(_termcap_init); /* any init terminfo requires */
+ if (_scrollregion)
+ putpad(tgoto(_scrollregion, term.t_nrow, 0)) ;
+ }
+
+ /*
+ * Initialize UW-modified NCSA telnet to use its functionkeys
+ */
+ if((gmode & MDFKEY) && Pmaster == NULL)
+ puts("\033[99h");
+
+ /* return ignored */
+ return(0);
+}
+
+
+static int
+tinfoclose(void)
+{
+ if(!Pmaster){
+ if(gmode&MDFKEY)
+ puts("\033[99l"); /* reset UW-NCSA telnet keys */
+
+ if(_termcap_end) /* any clean up terminfo requires */
+ putpad(_termcap_end);
+ }
+
+ ttclose();
+
+ /* return ignored */
+ return(0);
+}
+
+
+/*
+ * tinfoinsert - insert a character at the current character position.
+ * _insertchar takes precedence.
+ */
+static void
+tinfoinsert(UCS ch)
+{
+ if(_insertchar != NULL){
+ putpad(_insertchar);
+ ttputc(ch);
+ }
+ else{
+ putpad(_startinsert);
+ ttputc(ch);
+ putpad(_endinsert);
+ }
+}
+
+
+/*
+ * tinfodelete - delete a character at the current character position.
+ */
+static void
+tinfodelete(void)
+{
+ if(_startdelete == NULL && _enddelete == NULL)
+ putpad(_deletechar);
+ else{
+ putpad(_startdelete);
+ putpad(_deletechar);
+ putpad(_enddelete);
+ }
+}
+
+
+/*
+ * o_scrolldown() - open a line at the given row position.
+ * use either region scrolling or deleteline/insertline
+ * to open a new line.
+ */
+int
+o_scrolldown(int row, int n)
+{
+ register int i;
+
+ if(_scrollregion != NULL){
+ putpad(tgoto(_scrollregion, term.t_nrow - (term.t_mrow+1), row));
+ tinfomove(row, 0);
+ for(i = 0; i < n; i++)
+ putpad((_scrollup != NULL && *_scrollup != '\0') ? _scrollup : "\n" );
+ putpad(tgoto(_scrollregion, term.t_nrow, 0));
+ tinfomove(row, 0);
+ }
+ else{
+ /*
+ * this code causes a jiggly motion of the keymenu when scrolling
+ */
+ for(i = 0; i < n; i++){
+ tinfomove(term.t_nrow - (term.t_mrow+1), 0);
+ putpad(_deleteline);
+ tinfomove(row, 0);
+ putpad(_insertline);
+ }
+#ifdef NOWIGGLYLINES
+ /*
+ * this code causes a sweeping motion up and down the display
+ */
+ tinfomove(term.t_nrow - term.t_mrow - n, 0);
+ for(i = 0; i < n; i++)
+ putpad(_deleteline);
+ tinfomove(row, 0);
+ for(i = 0; i < n; i++)
+ putpad(_insertline);
+#endif
+ }
+
+ /* return ignored */
+ return(0);
+}
+
+
+/*
+ * o_scrollup() - open a line at the given row position.
+ * use either region scrolling or deleteline/insertline
+ * to open a new line.
+ */
+int
+o_scrollup(int row, int n)
+{
+ register int i;
+
+ if(_scrollregion != NULL){
+ putpad(tgoto(_scrollregion, term.t_nrow - (term.t_mrow+1), row));
+ /* setting scrolling region moves cursor to home */
+ tinfomove(term.t_nrow-(term.t_mrow+1), 0);
+ for(i = 0;i < n; i++)
+ putpad((_scrolldown == NULL || _scrolldown[0] == '\0') ? "\n"
+ : _scrolldown);
+ putpad(tgoto(_scrollregion, term.t_nrow, 0));
+ tinfomove(2, 0);
+ }
+ else{
+ for(i = 0; i < n; i++){
+ tinfomove(row, 0);
+ putpad(_deleteline);
+ tinfomove(term.t_nrow - (term.t_mrow+1), 0);
+ putpad(_insertline);
+ }
+#ifdef NOWIGGLYLINES
+ /* see note above */
+ tinfomove(row, 0);
+ for(i = 0; i < n; i++)
+ putpad(_deleteline);
+ tinfomove(term.t_nrow - term.t_mrow - n, 0);
+ for(i = 0;i < n; i++)
+ putpad(_insertline);
+#endif
+ }
+
+ /* return ignored */
+ return(0);
+}
+
+
+
+/*
+ * o_insert - use terminfo to optimized character insert
+ * returns: true if it optimized output, false otherwise
+ */
+int
+o_insert(UCS c)
+{
+ if(term_capabilities & TT_INSCHAR){
+ tinfoinsert(c);
+ return(1); /* no problems! */
+ }
+
+ return(0); /* can't do it. */
+}
+
+
+/*
+ * o_delete - use terminfo to optimized character insert
+ * returns true if it optimized output, false otherwise
+ */
+int
+o_delete(void)
+{
+ if(term_capabilities & TT_DELCHAR){
+ tinfodelete();
+ return(1); /* deleted, no problem! */
+ }
+
+ return(0); /* no dice. */
+}
+
+
+static int
+tinfomove(int row, int col)
+{
+ putpad(tgoto(_moveto, col, row));
+
+ /* return ignored */
+ return(0);
+}
+
+
+static int
+tinfoeeol(void)
+{
+ int c, starting_col, starting_line;
+ char *last_bg_color;
+
+ /*
+ * If the terminal doesn't have back color erase, then we have to
+ * erase manually to preserve the background color.
+ */
+ if(pico_usingcolor() && (!_bce || !_cleartoeoln)){
+ extern int ttcol, ttrow;
+
+ starting_col = ttcol;
+ starting_line = ttrow;
+ last_bg_color = pico_get_last_bg_color();
+ pico_set_nbg_color();
+ for(c = ttcol; c < term.t_ncol; c++)
+ ttputc(' ');
+
+ tinfomove(starting_line, starting_col);
+ if(last_bg_color){
+ pico_set_bg_color(last_bg_color);
+ free(last_bg_color);
+ }
+ }
+ else if(_cleartoeoln)
+ putpad(_cleartoeoln);
+
+ /* return ignored */
+ return(0);
+}
+
+
+static int
+tinfoeeop(void)
+{
+ int i, starting_col, starting_row;
+
+ /*
+ * If the terminal doesn't have back color erase, then we have to
+ * erase manually to preserve the background color.
+ */
+ if(pico_usingcolor() && (!_bce || !_cleartoeos)){
+ extern int ttcol, ttrow;
+
+ starting_col = ttcol;
+ starting_row = ttrow;
+ tinfoeeol(); /* rest of this line */
+ for(i = ttrow+1; i <= term.t_nrow; i++){ /* the remaining lines */
+ tinfomove(i, 0);
+ tinfoeeol();
+ }
+
+ tinfomove(starting_row, starting_col);
+ }
+ else if(_cleartoeos)
+ putpad(_cleartoeos);
+
+ /* return ignored */
+ return(0);
+}
+
+
+static int
+tinforev(int state) /* change reverse video status */
+{ /* FALSE = normal video, TRUE = rev video */
+ if(state)
+ StartInverse();
+ else
+ EndInverse();
+
+ return(1);
+}
+
+
+static int
+tinfobeep(void)
+{
+ ttputc(BELL);
+
+ /* return ignored */
+ return(0);
+}
+
+
+void
+putpad(char *str)
+{
+ tputs(str, 1, putchar);
+}
+
+#elif HAS_TERMCAP
+
+/*
+ * termcap-based terminal i/o and control routines
+ */
+
+
+/* internal prototypes */
+static int tcapmove(int, int);
+static int tcapeeol(void);
+static int tcapeeop(void);
+static int tcapbeep(void);
+static int tcaprev(int);
+static int tcapopen(void);
+static int tcapterminalinfo(int);
+static int tcapclose(void);
+static void setup_dflt_esc_seq(void);
+static void tcapinsert(UCS);
+static void tcapdelete(void);
+
+extern int tput();
+extern char *tgoto(char *, int, int);
+
+/*
+ * This number used to be 315. No doubt there was a reason for that but we
+ * don't know what it was. It's a bit of a hassle to make it dynamic, and most
+ * modern systems seem to be using terminfo, so we'll just change it to 800.
+ * We weren't stopping on overflow before, so we'll do that, too.
+ */
+#define TCAPSLEN 800
+char tcapbuf[TCAPSLEN];
+int _tlines, _tcolumns;
+char *_clearscreen, *_moveto, *_up, *_down, *_right, *_left,
+ *_setinverse, *_clearinverse,
+ *_setunderline, *_clearunderline,
+ *_setbold, *_clearallattr, /* there is no clear only bold! */
+ *_cleartoeoln, *_cleartoeos,
+ *_deleteline, /* delete line */
+ *_insertline, /* insert line */
+ *_scrollregion, /* define a scrolling region, vt100 */
+ *_insertchar, /* insert character, preferable to : */
+ *_startinsert, /* set insert mode and, */
+ *_endinsert, /* end insert mode */
+ *_deletechar, /* delete character */
+ *_startdelete, /* set delete mode and, */
+ *_enddelete, /* end delete mode */
+ *_scrolldown, /* scroll down */
+ *_scrollup, /* scroll up */
+ *_termcap_init, /* string to start termcap */
+ *_termcap_end, /* string to end termcap */
+ *_op, *_oc, *_setaf, *_setab, *_setf, *_setb, *_scp;
+int _colors, _pairs, _bce;
+char term_name[40];
+
+TERM term = {
+ NROW-1,
+ NCOL,
+ MARGIN,
+ MROW,
+ tcapopen,
+ tcapterminalinfo,
+ tcapclose,
+ ttgetc,
+ ttputc,
+ ttflush,
+ tcapmove,
+ tcapeeol,
+ tcapeeop,
+ tcapbeep,
+ tcaprev
+};
+
+
+/*
+ * Add default keypad sequences to the trie.
+ */
+static void
+setup_dflt_esc_seq(void)
+{
+ /*
+ * this is sort of a hack, but it allows us to use
+ * the function keys on pc's running telnet
+ */
+
+ /*
+ * UW-NDC/UCS vt10[02] application mode.
+ */
+ kpinsert("\033OP", F1, 1);
+ kpinsert("\033OQ", F2, 1);
+ kpinsert("\033OR", F3, 1);
+ kpinsert("\033OS", F4, 1);
+ kpinsert("\033Op", F5, 1);
+ kpinsert("\033Oq", F6, 1);
+ kpinsert("\033Or", F7, 1);
+ kpinsert("\033Os", F8, 1);
+ kpinsert("\033Ot", F9, 1);
+ kpinsert("\033Ou", F10, 1);
+ kpinsert("\033Ov", F11, 1);
+ kpinsert("\033Ow", F12, 1);
+
+ /*
+ * DEC vt100, ANSI and cursor key mode.
+ */
+ kpinsert("\033OA", KEY_UP, 1);
+ kpinsert("\033OB", KEY_DOWN, 1);
+ kpinsert("\033OC", KEY_RIGHT, 1);
+ kpinsert("\033OD", KEY_LEFT, 1);
+
+ /*
+ * special keypad functions
+ */
+ kpinsert("\033[4J", KEY_PGUP, 1);
+ kpinsert("\033[3J", KEY_PGDN, 1);
+ kpinsert("\033[2J", KEY_HOME, 1);
+ kpinsert("\033[N", KEY_END, 1);
+
+ /*
+ * vt220?
+ */
+ kpinsert("\033[5~", KEY_PGUP, 1);
+ kpinsert("\033[6~", KEY_PGDN, 1);
+ kpinsert("\033[1~", KEY_HOME, 1);
+ kpinsert("\033[4~", KEY_END, 1);
+
+ /*
+ * konsole, XTerm (XFree 4.x.x) keyboard setting
+ */
+ kpinsert("\033[H", KEY_HOME, 1);
+ kpinsert("\033[F", KEY_END, 1);
+
+ /*
+ * gnome-terminal 2.6.0, don't know why it
+ * changed from 2.2.1
+ */
+ kpinsert("\033OH", KEY_HOME, 1);
+ kpinsert("\033OF", KEY_END, 1);
+
+ /*
+ * "\033[2~" was common for KEY_HOME in a quick survey
+ * of terminals (though typically the Insert key).
+ * Teraterm 2.33 sends the following escape sequences,
+ * which is quite incompatible with everything
+ * else:
+ * Home: "\033[2~" End: "\033[5~" PgUp: "\033[3~"
+ * PgDn: "\033[6~"
+ * The best thing to do would be to fix TeraTerm
+ * keymappings or to tweak terminfo.
+ */
+
+ /*
+ * ANSI mode.
+ */
+ kpinsert("\033[=a", F1, 1);
+ kpinsert("\033[=b", F2, 1);
+ kpinsert("\033[=c", F3, 1);
+ kpinsert("\033[=d", F4, 1);
+ kpinsert("\033[=e", F5, 1);
+ kpinsert("\033[=f", F6, 1);
+ kpinsert("\033[=g", F7, 1);
+ kpinsert("\033[=h", F8, 1);
+ kpinsert("\033[=i", F9, 1);
+ kpinsert("\033[=j", F10, 1);
+ kpinsert("\033[=k", F11, 1);
+ kpinsert("\033[=l", F12, 1);
+
+ /*
+ * DEC vt100, ANSI and cursor key mode reset.
+ */
+ kpinsert("\033[A", KEY_UP, 1);
+ kpinsert("\033[B", KEY_DOWN, 1);
+ kpinsert("\033[C", KEY_RIGHT, 1);
+ kpinsert("\033[D", KEY_LEFT, 1);
+
+ /*
+ * DEC vt52 mode.
+ */
+ kpinsert("\033A", KEY_UP, 1);
+ kpinsert("\033B", KEY_DOWN, 1);
+ kpinsert("\033C", KEY_RIGHT, 1);
+ kpinsert("\033D", KEY_LEFT, 1);
+
+ /*
+ * DEC vt52 application keys, and some Zenith 19.
+ */
+ kpinsert("\033?r", KEY_DOWN, 1);
+ kpinsert("\033?t", KEY_LEFT, 1);
+ kpinsert("\033?v", KEY_RIGHT, 1);
+ kpinsert("\033?x", KEY_UP, 1);
+
+ /*
+ * Some Ctrl-Arrow keys
+ */
+ kpinsert("\033[5A", CTRL_KEY_UP, 1);
+ kpinsert("\033[5B", CTRL_KEY_DOWN, 1);
+ kpinsert("\033[5C", CTRL_KEY_RIGHT, 1);
+ kpinsert("\033[5D", CTRL_KEY_LEFT, 1);
+
+ kpinsert("\033[1;5A", CTRL_KEY_UP, 1);
+ kpinsert("\033[1;5B", CTRL_KEY_DOWN, 1);
+ kpinsert("\033[1;5C", CTRL_KEY_RIGHT, 1);
+ kpinsert("\033[1;5D", CTRL_KEY_LEFT, 1);
+
+ /*
+ * Map some shift+up/down/left/right to their shiftless counterparts
+ */
+ kpinsert("\033[1;2A", KEY_UP, 1);
+ kpinsert("\033[1;2B", KEY_DOWN, 1);
+ kpinsert("\033[1;2C", KEY_RIGHT, 1);
+ kpinsert("\033[1;2D", KEY_LEFT, 1);
+ kpinsert("\033[a", KEY_UP, 1);
+ kpinsert("\033[b", KEY_DOWN, 1);
+ kpinsert("\033[c", KEY_RIGHT, 1);
+ kpinsert("\033[d", KEY_LEFT, 1);
+
+ /*
+ * Sun Console sequences.
+ */
+ kpinsert("\033[1", KEY_SWALLOW_Z, 1);
+ kpinsert("\033[215", KEY_SWAL_UP, 1);
+ kpinsert("\033[217", KEY_SWAL_LEFT, 1);
+ kpinsert("\033[219", KEY_SWAL_RIGHT, 1);
+ kpinsert("\033[221", KEY_SWAL_DOWN, 1);
+
+ /*
+ * Kermit App Prog Cmd, gobble until ESC \ (kermit should intercept this)
+ */
+ kpinsert("\033_", KEY_KERMIT, 1);
+
+ /*
+ * Fake a control character.
+ */
+ kpinsert("\033\033", KEY_DOUBLE_ESC, 1);
+}
+
+
+/*
+ * Read termcap and set some global variables. Initialize input trie to
+ * decode escape sequences.
+ */
+static int
+tcapterminalinfo(int termcap_wins)
+{
+ char *p, *tgetstr();
+ char tcbuf[2*1024];
+ char *tv_stype;
+ char err_str[72];
+ int err;
+ char *_ku, *_kd, *_kl, *_kr,
+ *_kppu, *_kppd, *_kphome, *_kpend, *_kpdel,
+ *_kf1, *_kf2, *_kf3, *_kf4, *_kf5, *_kf6,
+ *_kf7, *_kf8, *_kf9, *_kf10, *_kf11, *_kf12;
+
+ if (!(tv_stype = getenv("TERM")) || !strncpy(term_name, tv_stype, sizeof(term_name))){
+ if(Pmaster){
+ return(-1);
+ }
+ else{
+ puts("Environment variable TERM not defined!");
+ exit(1);
+ }
+ }
+
+ term_name[sizeof(term_name)-1] = '\0';
+
+ if((err = tgetent(tcbuf, tv_stype)) != 1){
+ if(Pmaster){
+ return(err - 2);
+ }
+ else{
+ snprintf(err_str, sizeof(err_str), "Unknown terminal type %s!", tv_stype);
+ puts(err_str);
+ exit(1);
+ }
+ }
+
+ p = tcapbuf;
+
+ _clearscreen = tgetstr("cl", &p);
+ _moveto = tgetstr("cm", &p);
+ _up = tgetstr("up", &p);
+ _down = tgetstr("do", &p);
+ _right = tgetstr("nd", &p);
+ _left = tgetstr("bs", &p);
+ _setinverse = tgetstr("so", &p);
+ _clearinverse = tgetstr("se", &p);
+ _setunderline = tgetstr("us", &p);
+ _clearunderline = tgetstr("ue", &p);
+ _setbold = tgetstr("md", &p);
+ _clearallattr = tgetstr("me", &p);
+ _cleartoeoln = tgetstr("ce", &p);
+ _cleartoeos = tgetstr("cd", &p);
+ _deletechar = tgetstr("dc", &p);
+ _insertchar = tgetstr("ic", &p);
+ _startinsert = tgetstr("im", &p);
+ _endinsert = tgetstr("ei", &p);
+ _deleteline = tgetstr("dl", &p);
+ _insertline = tgetstr("al", &p);
+ _scrollregion = tgetstr("cs", &p);
+ _scrolldown = tgetstr("sf", &p);
+ _scrollup = tgetstr("sr", &p);
+ _termcap_init = tgetstr("ti", &p);
+ _termcap_end = tgetstr("te", &p);
+ _startdelete = tgetstr("dm", &p);
+ _enddelete = tgetstr("ed", &p);
+ _ku = tgetstr("ku", &p);
+ _kd = tgetstr("kd", &p);
+ _kl = tgetstr("kl", &p);
+ _kr = tgetstr("kr", &p);
+ _kppu = tgetstr("kP", &p);
+ _kppd = tgetstr("kN", &p);
+ _kphome = tgetstr("kh", &p);
+ _kpend = tgetstr("kH", &p);
+ _kpdel = tgetstr("kD", &p);
+ _kf1 = tgetstr("k1", &p);
+ _kf2 = tgetstr("k2", &p);
+ _kf3 = tgetstr("k3", &p);
+ _kf4 = tgetstr("k4", &p);
+ _kf5 = tgetstr("k5", &p);
+ _kf6 = tgetstr("k6", &p);
+ _kf7 = tgetstr("k7", &p);
+ _kf8 = tgetstr("k8", &p);
+ _kf9 = tgetstr("k9", &p);
+ if((_kf10 = tgetstr("k;", &p)) == NULL)
+ _kf10 = tgetstr("k0", &p);
+ _kf11 = tgetstr("F1", &p);
+ _kf12 = tgetstr("F2", &p);
+
+ _colors = tgetnum("Co");
+ _pairs = tgetnum("pa");
+ _setaf = tgetstr("AF", &p);
+ _setab = tgetstr("AB", &p);
+ _setf = tgetstr("Sf", &p);
+ _setb = tgetstr("Sb", &p);
+ _scp = tgetstr("sp", &p);
+ _op = tgetstr("op", &p);
+ _oc = tgetstr("oc", &p);
+ _bce = tgetflag("ut");
+
+ if (p >= &tcapbuf[TCAPSLEN]){
+ puts("Terminal description too big!\n");
+ if(Pmaster)
+ return(-3);
+ else
+ exit(1);
+ }
+
+ _tlines = tgetnum("li");
+ if(_tlines == -1){
+ char *er;
+ int rr;
+
+ /* tgetnum failed, try $LINES */
+ er = getenv("LINES");
+ if(er && (rr = atoi(er)) > 0)
+ _tlines = rr;
+ }
+
+ _tcolumns = tgetnum("co");
+ if(_tcolumns == -1){
+ char *ec;
+ int cc;
+
+ /* tgetnum failed, try $COLUMNS */
+ ec = getenv("COLUMNS");
+ if(ec && (cc = atoi(ec)) > 0)
+ _tcolumns = cc;
+ }
+
+ /*
+ * Add default keypad sequences to the trie.
+ * Since these come first, they will override any conflicting termcap
+ * or terminfo escape sequences defined below. An escape sequence is
+ * considered conflicting if one is a prefix of the other.
+ * So, without TERMCAP_WINS, there will likely be some termcap/terminfo
+ * escape sequences that don't work, because they conflict with default
+ * sequences defined here.
+ */
+ if(!termcap_wins)
+ setup_dflt_esc_seq();
+
+ /*
+ * add termcap/info escape sequences to the trie...
+ */
+
+ if(_ku != NULL && _kd != NULL && _kl != NULL && _kr != NULL){
+ kpinsert(_ku, KEY_UP, termcap_wins);
+ kpinsert(_kd, KEY_DOWN, termcap_wins);
+ kpinsert(_kl, KEY_LEFT, termcap_wins);
+ kpinsert(_kr, KEY_RIGHT, termcap_wins);
+ }
+
+ if(_kppu != NULL && _kppd != NULL){
+ kpinsert(_kppu, KEY_PGUP, termcap_wins);
+ kpinsert(_kppd, KEY_PGDN, termcap_wins);
+ }
+
+ kpinsert(_kphome, KEY_HOME, termcap_wins);
+ kpinsert(_kpend, KEY_END, termcap_wins);
+ kpinsert(_kpdel, KEY_DEL, termcap_wins);
+
+ kpinsert(_kf1, F1, termcap_wins);
+ kpinsert(_kf2, F2, termcap_wins);
+ kpinsert(_kf3, F3, termcap_wins);
+ kpinsert(_kf4, F4, termcap_wins);
+ kpinsert(_kf5, F5, termcap_wins);
+ kpinsert(_kf6, F6, termcap_wins);
+ kpinsert(_kf7, F7, termcap_wins);
+ kpinsert(_kf8, F8, termcap_wins);
+ kpinsert(_kf9, F9, termcap_wins);
+ kpinsert(_kf10, F10, termcap_wins);
+ kpinsert(_kf11, F11, termcap_wins);
+ kpinsert(_kf12, F12, termcap_wins);
+
+ /*
+ * Add default keypad sequences to the trie.
+ * Since these come after the termcap/terminfo escape sequences above,
+ * the termcap/info sequences will override any conflicting default
+ * escape sequences defined here.
+ * So, with TERMCAP_WINS, some of the default sequences will be missing.
+ * This means that you'd better get all of your termcap/terminfo entries
+ * correct if you define TERMCAP_WINS.
+ */
+ if(termcap_wins)
+ setup_dflt_esc_seq();
+
+ if(Pmaster)
+ return(0);
+ else
+ return(TRUE);
+}
+
+static int
+tcapopen(void)
+{
+ int row, col;
+
+ /*
+ * determine the terminal's communication speed and decide
+ * if we need to do optimization ...
+ */
+ if(ttisslow())
+ term_capabilities |= TT_OPTIMIZE;
+
+ col = _tcolumns;
+ row = _tlines;
+ if(row >= 0)
+ row--;
+
+ ttgetwinsz(&row, &col);
+ term.t_nrow = (short) row;
+ term.t_ncol = (short) col;
+
+ if(_cleartoeoln != NULL) /* able to use clear to EOL? */
+ term_capabilities |= TT_EOLEXIST;
+ else
+ term_capabilities &= ~TT_EOLEXIST;
+
+ if(_setinverse != NULL)
+ term_capabilities |= TT_REVEXIST;
+ else
+ term_capabilities &= ~TT_REVEXIST;
+
+ if(_deletechar == NULL && (_startdelete == NULL || _enddelete == NULL))
+ term_capabilities &= ~TT_DELCHAR;
+
+ if(_insertchar == NULL && (_startinsert == NULL || _endinsert == NULL))
+ term_capabilities &= ~TT_INSCHAR;
+
+ if((_scrollregion == NULL || _scrolldown == NULL || _scrollup == NULL)
+ && (_deleteline == NULL || _insertline == NULL))
+ term_capabilities &= ~TT_SCROLLEXIST;
+
+ if(_clearscreen == NULL || _moveto == NULL || _up == NULL){
+ if(Pmaster == NULL){
+ puts("Incomplete termcap entry\n");
+ exit(1);
+ }
+ }
+
+ ttopen();
+
+ if(_termcap_init && !Pmaster) {
+ putpad(_termcap_init); /* any init termcap requires */
+ if (_scrollregion)
+ putpad(tgoto(_scrollregion, term.t_nrow, 0)) ;
+ }
+
+ /*
+ * Initialize UW-modified NCSA telnet to use it's functionkeys
+ */
+ if(gmode&MDFKEY && Pmaster == NULL)
+ puts("\033[99h");
+
+ /* return ignored */
+ return(0);
+}
+
+
+static int
+tcapclose(void)
+{
+ if(!Pmaster){
+ if(gmode&MDFKEY)
+ puts("\033[99l"); /* reset UW-NCSA telnet keys */
+
+ if(_termcap_end) /* any cleanup termcap requires */
+ putpad(_termcap_end);
+ }
+
+ ttclose();
+
+ /* return ignored */
+ return(0);
+}
+
+
+
+/*
+ * tcapinsert - insert a character at the current character position.
+ * _insertchar takes precedence.
+ */
+static void
+tcapinsert(UCS ch)
+{
+ if(_insertchar != NULL){
+ putpad(_insertchar);
+ ttputc(ch);
+ }
+ else{
+ putpad(_startinsert);
+ ttputc(ch);
+ putpad(_endinsert);
+ }
+}
+
+
+/*
+ * tcapdelete - delete a character at the current character position.
+ */
+static void
+tcapdelete(void)
+{
+ if(_startdelete == NULL && _enddelete == NULL)
+ putpad(_deletechar);
+ else{
+ putpad(_startdelete);
+ putpad(_deletechar);
+ putpad(_enddelete);
+ }
+}
+
+
+/*
+ * o_scrolldown - open a line at the given row position.
+ * use either region scrolling or deleteline/insertline
+ * to open a new line.
+ */
+int
+o_scrolldown(int row, int n)
+{
+ register int i;
+
+ if(_scrollregion != NULL){
+ putpad(tgoto(_scrollregion, term.t_nrow - (term.t_mrow+1), row));
+ tcapmove(row, 0);
+ for(i = 0; i < n; i++)
+ putpad( (_scrollup != NULL && *_scrollup != '\0')
+ ? _scrollup : "\n" );
+ putpad(tgoto(_scrollregion, term.t_nrow, 0));
+ tcapmove(row, 0);
+ }
+ else{
+ /*
+ * this code causes a jiggly motion of the keymenu when scrolling
+ */
+ for(i = 0; i < n; i++){
+ tcapmove(term.t_nrow - (term.t_mrow+1), 0);
+ putpad(_deleteline);
+ tcapmove(row, 0);
+ putpad(_insertline);
+ }
+#ifdef NOWIGGLYLINES
+ /*
+ * this code causes a sweeping motion up and down the display
+ */
+ tcapmove(term.t_nrow - term.t_mrow - n, 0);
+ for(i = 0; i < n; i++)
+ putpad(_deleteline);
+ tcapmove(row, 0);
+ for(i = 0; i < n; i++)
+ putpad(_insertline);
+#endif
+ }
+
+ /* return ignored */
+ return(0);
+}
+
+
+/*
+ * o_scrollup - open a line at the given row position.
+ * use either region scrolling or deleteline/insertline
+ * to open a new line.
+ */
+int
+o_scrollup(int row, int n)
+{
+ register int i;
+
+ if(_scrollregion != NULL){
+ putpad(tgoto(_scrollregion, term.t_nrow - (term.t_mrow+1), row));
+ /* setting scrolling region moves cursor to home */
+ tcapmove(term.t_nrow-(term.t_mrow+1), 0);
+ for(i = 0;i < n; i++)
+ putpad((_scrolldown == NULL || _scrolldown[0] == '\0')
+ ? "\n" : _scrolldown);
+ putpad(tgoto(_scrollregion, term.t_nrow, 0));
+ tcapmove(2, 0);
+ }
+ else{
+ for(i = 0; i < n; i++){
+ tcapmove(row, 0);
+ putpad(_deleteline);
+ tcapmove(term.t_nrow - (term.t_mrow+1), 0);
+ putpad(_insertline);
+ }
+#ifdef NOWIGGLYLINES
+ /* see note above */
+ tcapmove(row, 0);
+ for(i = 0; i < n; i++)
+ putpad(_deleteline);
+ tcapmove(term.t_nrow - term.t_mrow - n, 0);
+ for(i = 0;i < n; i++)
+ putpad(_insertline);
+#endif
+ }
+
+ /* return ignored */
+ return(0);
+}
+
+
+/*
+ * o_insert - use termcap info to optimized character insert
+ * returns: true if it optimized output, false otherwise
+ */
+int
+o_insert(UCS c)
+{
+ if(term_capabilities & TT_INSCHAR){
+ tcapinsert(c);
+ return(1); /* no problems! */
+ }
+
+ return(0); /* can't do it. */
+}
+
+
+/*
+ * o_delete - use termcap info to optimized character insert
+ * returns true if it optimized output, false otherwise
+ */
+int
+o_delete(void)
+{
+ if(term_capabilities & TT_DELCHAR){
+ tcapdelete();
+ return(1); /* deleted, no problem! */
+ }
+
+ return(0); /* no dice. */
+}
+
+
+static int
+tcapmove(int row, int col)
+{
+ putpad(tgoto(_moveto, col, row));
+
+ /* return ignored */
+ return(0);
+}
+
+
+static int
+tcapeeol(void)
+{
+ int c, starting_col, starting_line;
+ char *last_bg_color;
+
+ /*
+ * If the terminal doesn't have back color erase, then we have to
+ * erase manually to preserve the background color.
+ */
+ if(pico_usingcolor() && (!_bce || !_cleartoeoln)){
+ extern int ttcol, ttrow;
+
+ starting_col = ttcol;
+ starting_line = ttrow;
+ last_bg_color = pico_get_last_bg_color();
+ pico_set_nbg_color();
+ for(c = ttcol; c < term.t_ncol; c++)
+ ttputc(' ');
+
+ tcapmove(starting_line, starting_col);
+ if(last_bg_color){
+ pico_set_bg_color(last_bg_color);
+ free(last_bg_color);
+ }
+ }
+ else if(_cleartoeoln)
+ putpad(_cleartoeoln);
+
+ /* return ignored */
+ return(0);
+}
+
+
+static int
+tcapeeop(void)
+{
+ int i, starting_col, starting_row;
+
+ /*
+ * If the terminal doesn't have back color erase, then we have to
+ * erase manually to preserve the background color.
+ */
+ if(pico_usingcolor() && (!_bce || !_cleartoeos)){
+ extern int ttcol, ttrow;
+
+ starting_col = ttcol;
+ starting_row = ttrow;
+ tcapeeol(); /* rest of this line */
+ for(i = ttrow+1; i <= term.t_nrow; i++){ /* the remaining lines */
+ tcapmove(i, 0);
+ tcapeeol();
+ }
+
+ tcapmove(starting_row, starting_col);
+ }
+ else if(_cleartoeos)
+ putpad(_cleartoeos);
+
+ /* return ignored */
+ return(0);
+}
+
+
+static int
+tcaprev(int state) /* change reverse video status */
+{ /* FALSE = normal video, TRUE = reverse video */
+ if(state)
+ StartInverse();
+ else
+ EndInverse();
+
+ return(1);
+}
+
+
+static int
+tcapbeep(void)
+{
+ ttputc(BELL);
+
+ /* return ignored */
+ return(0);
+}
+
+
+void
+putpad(char *str)
+{
+ tputs(str, 1, putchar);
+}
+
+#else /* HARD_CODED_ANSI_TERMINAL */
+
+/*
+ * ANSI-specific terminal i/o and control routines
+ */
+
+
+#define BEL 0x07 /* BEL character. */
+#define ESC 0x1B /* ESC character. */
+
+
+extern int ttflush();
+
+extern int ansimove(int, int);
+extern int ansieeol(void);
+extern int ansieeop(void);
+extern int ansibeep(void);
+extern int ansiparm(int);
+extern int ansiopen(void);
+extern int ansiterminalinfo(int);
+extern int ansirev(int);
+
+/*
+ * Standard terminal interface dispatch table. Most of the fields point into
+ * "termio" code.
+ */
+#if defined(VAX) && !defined(__ALPHA)
+globaldef
+#endif
+
+TERM term = {
+ NROW-1,
+ NCOL,
+ MARGIN,
+ MROW,
+ ansiopen,
+ ansiterminalinfo,
+ ttclose,
+ ttgetc,
+ ttputc,
+ ttflush,
+ ansimove,
+ ansieeol,
+ ansieeop,
+ ansibeep,
+ ansirev
+};
+
+int
+ansimove(int row, int col)
+{
+ ttputc(ESC);
+ ttputc('[');
+ ansiparm(row+1);
+ ttputc(';');
+ ansiparm(col+1);
+ ttputc('H');
+}
+
+int
+ansieeol(void)
+{
+ ttputc(ESC);
+ ttputc('[');
+ ttputc('K');
+}
+
+int
+ansieeop(void)
+{
+ ttputc(ESC);
+ ttputc('[');
+ ttputc('J');
+}
+
+int
+ansirev(int state) /* change reverse video state */
+{ /* TRUE = reverse, FALSE = normal */
+ static int PrevState = 0;
+
+ if(state != PrevState) {
+ PrevState = state ;
+ ttputc(ESC);
+ ttputc('[');
+ ttputc(state ? '7': '0');
+ ttputc('m');
+ }
+}
+
+int
+ansibeep(void)
+{
+ ttputc(BEL);
+ ttflush();
+}
+
+int
+ansiparm(int n)
+{
+ register int q;
+
+ q = n/10;
+ if (q != 0)
+ ansiparm(q);
+ ttputc((n%10) + '0');
+}
+
+
+int
+ansiterminalinfo(int termcap_wins)
+{
+#if V7
+ register char *cp;
+ char *getenv();
+
+ if ((cp = getenv("TERM")) == NULL) {
+ puts("Shell variable TERM not defined!");
+ exit(1);
+ }
+ if (strcmp(cp, "vt100") != 0) {
+ puts("Terminal type not 'vt100'!");
+ exit(1);
+ }
+#endif
+ /* revexist = TRUE; dead code? */
+}
+
+int
+ansiopen(void)
+{
+ ttopen();
+}
+
+
+#endif /* HARD_CODED_ANSI_TERMINAL */
+#else /* _WINDOWS */
+
+/* These are all just noops in Windows */
+
+unsigned
+tthascap(void)
+{
+ return(0);
+}
+
+/*
+ * o_insert - optimize screen insert of char c
+ */
+int
+o_insert(UCS c)
+{
+ return(0);
+}
+
+
+/*
+ * o_delete - optimized character deletion
+ */
+int
+o_delete(void)
+{
+ return(0);
+}
+
+
+
+#endif /* _WINDOWS */
diff --git a/pico/osdep/terminal.h b/pico/osdep/terminal.h
new file mode 100644
index 00000000..6449c32c
--- /dev/null
+++ b/pico/osdep/terminal.h
@@ -0,0 +1,50 @@
+/*
+ * $Id: terminal.h 767 2007-10-24 00:03:59Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef PICO_OSDEP_TERMINAL_INCLUDED
+#define PICO_OSDEP_TERMINAL_INCLUDED
+
+
+/*
+ * Useful definitions
+ */
+#define NROW DEFAULT_LINES_ON_TERMINAL
+#define NCOL DEFAULT_COLUMNS_ON_TERMINAL
+
+#define TT_OPTIMIZE 0x01 /* optimize flag(cf line speed) */
+#define TT_EOLEXIST 0x02 /* does clear to EOL exist */
+#define TT_SCROLLEXIST 0x04 /* does insert line exist */
+#define TT_REVEXIST 0x08 /* does reverse video exist? */
+#define TT_INSCHAR 0x10 /* does insert character exist */
+#define TT_DELCHAR 0x20 /* does delete character exist */
+
+
+#define TERM_OPTIMIZE (tthascap() & TT_OPTIMIZE)
+#define TERM_EOLEXIST (tthascap() & TT_EOLEXIST)
+#define TERM_SCROLLEXIST (tthascap() & TT_SCROLLEXIST)
+#define TERM_REVEXIST (tthascap() & TT_REVEXIST)
+#define TERM_INSCHAR (tthascap() & TT_INSCHAR)
+#define TERM_DELCHAR (tthascap() & TT_DELCHAR)
+
+
+/* exported prototypes */
+unsigned tthascap(void);
+#if HAS_TERMINFO || HAS_TERMCAP
+void putpad(char *);
+#endif
+
+
+#endif /* PICO_OSDEP_TERMINAL_INCLUDED */
diff --git a/pico/osdep/truncate.c b/pico/osdep/truncate.c
new file mode 100644
index 00000000..8330338e
--- /dev/null
+++ b/pico/osdep/truncate.c
@@ -0,0 +1,20 @@
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ *
+ *
+ * Tim Rice tim@trr.metro.net Mon Jun 3 16:57:26 PDT 1996
+ *
+ * a quick and dirty trancate()
+ * Altos System V (5.3.1) does not have one
+ * neither does SCO Open Server Enterprise 3.0
+ *
+ */
diff --git a/pico/osdep/truncate.h b/pico/osdep/truncate.h
new file mode 100644
index 00000000..73c2decf
--- /dev/null
+++ b/pico/osdep/truncate.h
@@ -0,0 +1,21 @@
+/*
+ * $Id: truncate.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef PICO_OSDEP_TRUNCATE_INCLUDED
+#define PICO_OSDEP_TRUNCATE_INCLUDED
+
+
+#endif /* PICO_OSDEP_TRUNCATE_INCLUDED */
diff --git a/pico/osdep/tty.c b/pico/osdep/tty.c
new file mode 100644
index 00000000..100b37ea
--- /dev/null
+++ b/pico/osdep/tty.c
@@ -0,0 +1,372 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: tty.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ *
+ * Program: tty routines
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "../estruct.h"
+#include "../mode.h"
+#include "../pico.h"
+#include "../edef.h"
+#include "../efunc.h"
+#include "../keydefs.h"
+
+#include "signals.h"
+#ifndef _WINDOWS
+#include "terminal.h"
+#include "raw.h"
+#include "read.h"
+#else
+#include "mswin.h"
+#endif /* _WINDOWS */
+
+#ifdef MOUSE
+#include "mouse.h"
+#endif /* MOUSE */
+
+#include "tty.h"
+
+
+#ifndef _WINDOWS
+/*
+ * ttopen - this function is called once to set up the terminal device
+ * streams. if called as pine composer, don't mess with
+ * tty modes, but set signal handlers.
+ */
+int
+ttopen(void)
+{
+ if(Pmaster == NULL){
+ Raw(1);
+#ifdef MOUSE
+ if(gmode & MDMOUSE)
+ init_mouse();
+#endif /* MOUSE */
+ xonxoff_proc(preserve_start_stop);
+ }
+
+ picosigs();
+
+ return(1);
+}
+
+
+/*
+ * ttclose - this function gets called just before we go back home to
+ * the command interpreter. If called as pine composer, don't
+ * worry about modes, but set signals to default, pine will
+ * rewire things as needed.
+ */
+int
+ttclose(void)
+{
+ if(Pmaster){
+ signal(SIGHUP, SIG_DFL);
+#ifdef SIGCONT
+ signal(SIGCONT, SIG_DFL);
+#endif
+#if defined(SIGWINCH) && defined(TIOCGWINSZ)
+ signal(SIGWINCH, SIG_DFL);
+#endif
+ }
+ else{
+ Raw(0);
+#ifdef MOUSE
+ end_mouse();
+#endif
+ }
+
+ return(1);
+}
+
+
+/*
+ * ttgetc - Read a character from the terminal, performing no editing
+ * and doing no echo at all.
+ *
+ * Args: return_on_intr -- Function to get a single character from stdin,
+ * recorder -- If non-NULL, function used to record keystroke.
+ * bail_handler -- Function used to bail out on read error.
+ *
+ * Returns: The character read from stdin.
+ * Return_on_intr is returned if read is interrupted.
+ * If read error, BAIL_OUT is returned unless bail_handler is
+ * non-NULL, in which case it is called (and usually it exits).
+ *
+ * If recorder is non-null, it is used to record the keystroke.
+ */
+int
+ttgetc(int return_on_intr, int (*recorder)(int), void (*bail_handler)(void))
+{
+ int c;
+
+ switch(c = read_one_char()){
+ case READ_INTR:
+ return(return_on_intr);
+
+ case BAIL_OUT:
+ if(bail_handler)
+ (*bail_handler)();
+ else
+ return(BAIL_OUT);
+
+ default:
+ return(recorder ? (*recorder)(c) : c);
+ }
+}
+
+
+/*
+ * Simple version of ttgetc with simple error handling
+ *
+ * Args: recorder -- If non-NULL, function used to record keystroke.
+ * bail_handler -- Function used to bail out on read error.
+ *
+ * Returns: The character read from stdin.
+ * If read error, BAIL_OUT is returned unless bail_handler is
+ * non-NULL, in which case it is called (and usually it exits).
+ *
+ * If recorder is non-null, it is used to record the keystroke.
+ * Retries if interrupted.
+ */
+int
+simple_ttgetc(int (*recorder)(int), void (*bail_handler)(void))
+{
+ int res;
+ unsigned char c;
+
+ while((res = read(STDIN_FD, &c, 1)) <= 0)
+ if(!(res < 0 && errno == EINTR))
+ (*bail_handler)();
+
+ return(recorder ? (*recorder)((int)c) : (int)c);
+}
+
+
+/*
+ * ttputc - Write a character to the display.
+ */
+int
+ttputc(UCS ucs)
+{
+ unsigned char obuf[MAX(MB_LEN_MAX,32)];
+ int r, i, width = 0, outchars = 0;
+ int ret = 0;
+
+ if(ucs < 0x80)
+ return(putchar((unsigned char) ucs));
+
+ width = wcellwidth(ucs);
+
+ if(width < 0){
+ width = 1;
+ obuf[outchars++] = '?';
+ }
+ else{
+ /*
+ * Convert the ucs into the multibyte
+ * character that corresponds to the
+ * ucs in the users locale.
+ */
+ outchars = wtomb((char *) obuf, ucs);
+ if(outchars < 0){
+ width = 1;
+ obuf[0] = '?';
+ outchars = 1;
+ }
+ }
+
+ for(i = 0; i < outchars; i++){
+ r = putchar(obuf[i]);
+ ret = (ret == EOF) ? EOF : r;
+ }
+
+ return(ret);
+}
+
+
+/*
+ * ttflush - flush terminal buffer. Does real work where the terminal
+ * output is buffered up. A no-operation on systems where byte
+ * at a time terminal I/O is done.
+ */
+int
+ttflush(void)
+{
+ return(fflush(stdout));
+}
+
+
+/*
+ * ttresize - recompute the screen dimensions if necessary, and then
+ * adjust pico's internal buffers accordingly.
+ */
+void
+ttresize(void)
+{
+ int row = -1, col = -1;
+
+ ttgetwinsz(&row, &col);
+ resize_pico(row, col);
+}
+
+
+
+/*
+ * ttgetwinsz - set global row and column values (if we can get them)
+ * and return.
+ */
+void
+ttgetwinsz(int *row, int *col)
+{
+ extern int _tlines, _tcolumns;
+
+ if(*row < 0)
+ *row = (_tlines > 0) ? _tlines - 1 : NROW - 1;
+ if(*col <= 0)
+ *col = (_tcolumns > 0) ? _tcolumns : NCOL;
+#if defined(SIGWINCH) && defined(TIOCGWINSZ)
+ {
+ struct winsize win;
+
+ if (ioctl(0, TIOCGWINSZ, &win) == 0) { /* set to anything useful.. */
+ if(win.ws_row) /* ... the tty drivers says */
+ *row = win.ws_row - 1;
+
+ if(win.ws_col)
+ *col = win.ws_col;
+ }
+
+ signal(SIGWINCH, winch_handler); /* window size changes */
+ }
+#endif
+
+ if(*col > NLINE-1)
+ *col = NLINE-1;
+}
+
+#else /* _WINDOWS */
+
+#define MARGIN 8 /* size of minimim margin and */
+#define SCRSIZ 64 /* scroll size for extended lines */
+#define MROW 2 /* rows in menu */
+
+/* internal prototypes */
+int mswin_resize (int, int);
+
+/*
+ * Standard terminal interface dispatch table. Fields point to functions
+ * that operate the terminal. All these functions live in mswin.c, but
+ * this structure is defined here because it is specific to pico.
+ */
+TERM term = {
+ 0,
+ 0,
+ MARGIN,
+ MROW,
+ ttopen,
+ NULL,
+ ttclose,
+ NULL, /* was mswin_getc, but not used? */
+ mswin_putc,
+ mswin_flush,
+ mswin_move,
+ mswin_eeol,
+ mswin_eeop,
+ mswin_beep,
+ mswin_rev
+};
+
+/*
+ * This function is called once to set up the terminal device streams.
+ */
+int
+ttopen(void)
+{
+ int rows, columns;
+
+
+ mswin_getscreensize (&rows, &columns);
+ term.t_nrow = rows - 1;
+ term.t_ncol = columns;
+ /* term.t_scrsiz = (columns * 2) / 3; */
+
+ /*
+ * Do we implement optimized character insertion and deletion?
+ * o_insert() and o_delete()
+ */
+ /* inschar = delchar = FALSE; */
+ /* revexist = TRUE; dead code? */
+
+ mswin_setresizecallback (mswin_resize);
+
+ init_mouse();
+
+ return(1);
+}
+
+/*
+ * This function gets called just before we go back home to the command
+ * interpreter.
+ */
+int
+ttclose(void)
+{
+ mswin_clearresizecallback (mswin_resize);
+ return(1);
+}
+
+/*
+ * Flush terminal buffer. Does real work where the terminal output is buffered
+ * up. A no-operation on systems where byte at a time terminal I/O is done.
+ */
+int
+ttflush(void)
+{
+ return(1);
+}
+
+/*
+ * ttresize - recompute the screen dimensions if necessary, and then
+ * adjust pico's internal buffers accordingly.
+ */
+void
+ttresize(void)
+{
+ int row, col;
+
+ mswin_getscreensize(&row, &col);
+ resize_pico (row-1, col);
+}
+
+
+/*
+ * mswin_resize - windows specific callback to set pico's internal tables
+ * to new screen dimensions.
+ */
+int
+mswin_resize(int row, int col)
+{
+ if (wheadp)
+ resize_pico (row-1, col);
+ return (0);
+}
+
+#endif /* _WINDOWS */
diff --git a/pico/osdep/tty.h b/pico/osdep/tty.h
new file mode 100644
index 00000000..ca0a7570
--- /dev/null
+++ b/pico/osdep/tty.h
@@ -0,0 +1,36 @@
+/*
+ * $Id: tty.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef PICO_OSDEP_TTY_INCLUDED
+#define PICO_OSDEP_TTY_INCLUDED
+
+
+#include <general.h>
+
+
+/* exported prototypes */
+int ttopen(void);
+int ttclose(void);
+int ttflush(void);
+void ttresize(void);
+#ifndef _WINDOWS
+int ttgetc(int, int (*recorder)(int), void (*bail_handler)(void));
+int simple_ttgetc(int (*recorder)(int), void (*bail_handler)(void));
+int ttputc(UCS);
+void ttgetwinsz(int *, int *);
+#endif /* !_WINDOWS */
+
+#endif /* PICO_OSDEP_TTY_INCLUDED */
diff --git a/pico/pico-win.lnk b/pico/pico-win.lnk
new file mode 100644
index 00000000..e5d9a931
--- /dev/null
+++ b/pico/pico-win.lnk
@@ -0,0 +1,6 @@
+bdate+mswinver+main
+pico
+pico
+libpico+libw+shell+toolhelp+llibcew+oldnames+commdlg+winsock+
+..\spell\spell
+mswin.def
diff --git a/pico/pico.c b/pico/pico.c
new file mode 100644
index 00000000..166a3d3d
--- /dev/null
+++ b/pico/pico.c
@@ -0,0 +1,1939 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: pico.c 921 2008-01-31 02:09:25Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Main Pine Composer routines
+ *
+ *
+ * WEEMACS/PICO NOTES:
+ *
+ * 01 Nov 89 - MicroEmacs 3.6 vastly pared down and becomes a function call
+ * weemacs() and plugged into the Pine mailer. Lots of unused
+ * MicroEmacs code laying around.
+ *
+ * 17 Jan 90 - weemacs() became weemacs() the composer. Header editing
+ * functions added.
+ *
+ * 09 Sep 91 - weemacs() renamed pico() for the PIne COmposer.
+ *
+ */
+
+
+/*
+ * This program is in public domain; written by Dave G. Conroy.
+ * This file contains the main driving routine, and some keyboard processing
+ * code, for the MicroEMACS screen editor.
+ *
+ * REVISION HISTORY:
+ *
+ * 1.0 Steve Wilhite, 30-Nov-85
+ * - Removed the old LK201 and VT100 logic. Added code to support the
+ * DEC Rainbow keyboard (which is a LK201 layout) using the the Level
+ * 1 Console In ROM INT. See "rainbow.h" for the function key defs
+ * Steve Wilhite, 1-Dec-85
+ * - massive cleanup on code in display.c and search.c
+ *
+ * 2.0 George Jones, 12-Dec-85
+ * - Ported to Amiga.
+ *
+ * 3.0 Daniel Lawrence, 29-Dec-85
+ * 16-apr-86
+ * - updated documentation and froze development for 3.6 net release
+ */
+
+/* make global definitions not external */
+#define maindef
+
+#include "../c-client/mail.h"
+#include "../c-client/utf8.h"
+
+#ifdef _WINDOWS
+/* wingdi.h uses ERROR (!) and we aren't using the c-client ERROR so... */
+#undef ERROR
+
+#endif
+#include "headers.h"
+#include "ebind.h" /* default key bindings */
+#include "../pith/charconv/filesys.h"
+
+
+void func_init(void);
+void breplace(void *w);
+int any_header_changes(void);
+int cleanwhitespace(void);
+int isquotedspace(LINE *);
+
+
+/*
+ * function key mappings
+ */
+static UCS pfkm[12][2] = {
+ { F1, (CTRL|'G')},
+ { F2, (CTRL|'C')},
+ { F3, (CTRL|'X')},
+ { F4, (CTRL|'J')},
+ { F5, (CTRL|'R')},
+ { F6, (CTRL|'W')},
+ { F7, (CTRL|'Y')},
+ { F8, (CTRL|'V')},
+ { F9, (CTRL|'K')},
+ { F10, (CTRL|'U')},
+ { F11, (CTRL|'O')},
+#ifdef SPELLER
+ { F12, (CTRL|'T')}
+#else
+ { F12, (CTRL|'D')}
+#endif
+};
+
+
+/*
+ * flag for the various functions in pico() to set when ready
+ * for pico() to return...
+ */
+int pico_all_done = 0;
+jmp_buf finstate;
+UCS *pico_anchor = NULL;
+extern struct headerentry *headents;
+
+/*
+ * pico - the main routine for Pine's composer.
+ *
+ */
+int
+pico(PICO *pm)
+{
+ UCS c;
+ register int f;
+ register int n;
+ char bname[NBUFN]; /* buffer name of file to read */
+ extern struct on_display ods;
+ int checkpointcnt = 0, input = 0;
+ int ret;
+ char chkptfile[NLINE];
+#ifdef _WINDOWS
+ int cursor_shown;
+#endif
+
+ Pmaster = pm;
+ gmode = MDWRAP;
+ gmode |= pm->pine_flags; /* high 4 bits rsv'd for pine */
+
+ alt_speller = pm->alt_spell;
+ pico_all_done = 0;
+ km_popped = 0;
+
+ if(!vtinit()) /* Init Displays. */
+ return(COMP_CANCEL);
+
+ strncpy(bname, "main", sizeof(bname)); /* default buffer name */
+ bname[sizeof(bname)-1] = '\0';
+ edinit(bname); /* Buffers, windows. */
+
+ if(InitMailHeader(pm)) /* init mail header structure */
+ gmode &= ~(P_BODY | P_HEADEND); /* flip off special header stuff */
+
+ /* setup to process commands */
+ lastflag = 0; /* Fake last flags. */
+ curbp->b_mode |= gmode; /* and set default modes*/
+
+ if(Pmaster->pine_anchor)
+ pico_anchor = utf8_to_ucs4_cpystr(Pmaster->pine_anchor);
+ else
+ pico_anchor = NULL;
+
+ if(Pmaster->quote_str)
+ glo_quote_str = utf8_to_ucs4_cpystr(Pmaster->quote_str);
+ else
+ glo_quote_str = NULL;
+
+ if(Pmaster->wordseps)
+ glo_wordseps = ucs4_cpystr(Pmaster->wordseps);
+ else
+ glo_wordseps = NULL;
+
+ bindtokey(DEL, (gmode & P_DELRUBS) ? forwdel : backdel);
+
+ if(pm->msgtext)
+ breplace(pm->msgtext);
+
+#ifdef _WINDOWS
+ cursor_shown = mswin_showcaret(1); /* turn on for main window */
+ mswin_allowpaste(MSWIN_PASTE_FULL);
+ mswin_setscrollcallback (pico_scroll_callback);
+#endif
+
+ /* prepare for checkpointing */
+ chkptfile[0] = '\0';
+ chkptinit((*Pmaster->ckptdir)(chkptfile, sizeof(chkptfile)), sizeof(chkptfile));
+ if(gmode & P_CHKPTNOW)
+ writeout(chkptfile, TRUE);
+
+ pico_all_done = setjmp(finstate); /* jump out of HUP handler ? */
+
+ if(gmode & MDALTNOW){
+ while(!pico_all_done){
+ if(((gmode & P_BODY) || !Pmaster->headents)
+ && alt_editor(0, 1) < 0)
+ break; /* if problem, drop into pico */
+
+ if(Pmaster->headents){
+ update(); /* paint screen, n' start editing... */
+ HeaderEditor((gmode & (P_HEADEND | P_BODY)) ? 2 : 0, 0);
+ gmode |= P_BODY; /* make sure we enter alt ed next */
+ }
+ else
+ pico_all_done = COMP_EXIT;
+ }
+ }
+ else if(!pico_all_done){
+ if(gmode & P_BODY){ /* begin editing the header? */
+ ArrangeHeader(); /* line up pointers */
+ /*
+ * Move to the offset pine asked us to move to.
+ * Perhaps we should be checking to see if this is
+ * a reasonable number before moving.
+ */
+ if(Pmaster && Pmaster->edit_offset)
+ forwchar(FALSE, Pmaster->edit_offset);
+ }
+ else{
+ update(); /* paint screen, */
+ HeaderEditor((gmode & P_HEADEND) ? 2 : 0, 0);
+ }
+ }
+
+ while(1){
+ if(pico_all_done){
+#ifdef _WINDOWS
+ if(!cursor_shown)
+ mswin_showcaret(0);
+
+ mswin_allowpaste(MSWIN_PASTE_DISABLE);
+ mswin_setscrollcallback (NULL);
+#endif
+ ret = anycb() ? BUF_CHANGED : 0;
+ switch(pico_all_done){ /* prepare for/handle final events */
+ case COMP_EXIT : /* already confirmed */
+ packheader();
+ if(Pmaster
+ && (Pmaster->strip_ws_before_send
+ || Pmaster->allow_flowed_text))
+ cleanwhitespace();
+ ret |= COMP_EXIT;
+ break;
+
+ case COMP_CANCEL : /* also already confirmed */
+ packheader();
+ ret = COMP_CANCEL;
+ break;
+
+ case COMP_GOTHUP:
+ /*
+ * pack up and let caller know that we've received a SIGHUP
+ */
+ if(ComposerEditing) /* expand addr if needed */
+ call_builder(&headents[ods.cur_e], NULL, NULL);
+
+ packheader();
+ ret |= COMP_GOTHUP;
+ break;
+
+ case COMP_SUSPEND :
+ default: /* safest if internal error */
+ /*
+ * If we're in the headers mark the current header line
+ * with start_here bit so caller knows where to reset.
+ * Also set the edit_offset, which is either the offset
+ * into this header line or the offset into the body.
+ * Packheader will adjust edit_offset for multi-line
+ * headers.
+ */
+ if(ComposerEditing){ /* in the headers */
+ headents[ods.cur_e].start_here = 1;
+ Pmaster->edit_offset = ods.p_ind;
+ }
+ else{
+ register LINE *clp;
+ register long offset;
+
+ for(clp = lforw(curbp->b_linep), offset = 0L;
+ clp != curwp->w_dotp;
+ clp = lforw(clp))
+ offset += (llength(clp) + 1);
+
+ Pmaster->edit_offset = offset + curwp->w_doto;
+ }
+
+ packheader();
+ ret |= COMP_SUSPEND;
+ break;
+ }
+
+ if(pico_anchor)
+ fs_give((void **) &pico_anchor);
+ if(glo_quote_str)
+ fs_give((void **) &glo_quote_str);
+ if(glo_wordseps)
+ fs_give((void **) &glo_wordseps);
+
+ vttidy(); /* clean up tty modes */
+ zotdisplay(); /* blast display buffers */
+ zotedit();
+ our_unlink(chkptfile);
+ Pmaster = NULL; /* blat global */
+
+ return(ret);
+ }
+
+ if(km_popped){
+ km_popped--;
+ if(km_popped == 0) /* cause bottom three lines to be repainted */
+ curwp->w_flag |= WFHARD;
+ }
+
+ if(km_popped){ /* temporarily change to cause menu to be painted */
+ term.t_mrow = 2;
+ curwp->w_ntrows -= 2;
+ curwp->w_flag |= WFMODE;
+ movecursor(term.t_nrow-2, 0); /* clear status line, too */
+ peeol();
+ }
+
+ update(); /* Fix up the screen */
+ if(km_popped){
+ term.t_mrow = 0;
+ curwp->w_ntrows += 2;
+ }
+
+#ifdef MOUSE
+#ifdef EX_MOUSE
+ /* New mouse function for real mouse text seletion. */
+ register_mfunc(mouse_in_pico, 2, 0, term.t_nrow - (term.t_mrow+1),
+ term.t_ncol);
+#else
+ mouse_in_content(KEY_MOUSE, -1, -1, -1, 0);
+ register_mfunc(mouse_in_content, 2, 0, term.t_nrow - (term.t_mrow + 1),
+ term.t_ncol);
+#endif
+#endif
+#ifdef _WINDOWS
+ mswin_setdndcallback (composer_file_drop);
+ mswin_mousetrackcallback(pico_cursor);
+#endif
+ c = GetKey();
+ if (term.t_nrow < 6 && c != NODATA){
+ (*term.t_beep)();
+ emlwrite(_("Please make the screen bigger."), NULL);
+ continue;
+ }
+
+#ifdef MOUSE
+#ifdef EX_MOUSE
+ clear_mfunc(mouse_in_pico);
+#else
+ clear_mfunc(mouse_in_content);
+#endif
+#endif
+#ifdef _WINDOWS
+ mswin_cleardndcallback ();
+ mswin_mousetrackcallback(NULL);
+#endif
+ if(c == NODATA || time_to_check()){ /* new mail ? */
+ if((*Pmaster->newmail)(c == NODATA ? 0 : 2, 1) >= 0){
+ int rv;
+
+ if(km_popped){
+ term.t_mrow = 2;
+ curwp->w_ntrows -= 2;
+ curwp->w_flag |= WFHARD;
+ km_popped = 0;
+ }
+
+ clearcursor();
+ mlerase();
+ rv = (*Pmaster->showmsg)(c);
+ ttresize();
+ picosigs(); /* restore altered handlers */
+ if(rv) /* Did showmsg corrupt the display? */
+ PaintBody(0); /* Yes, repaint */
+
+ mpresf = 1;
+ input = 0;
+ }
+
+ clearcursor();
+ movecursor(0, 0);
+ }
+
+ if(km_popped)
+ switch(c){
+ case NODATA:
+ case (CTRL|'L'):
+ km_popped++;
+ break;
+
+ default:
+ mlerase();
+ break;
+ }
+
+ if(c == NODATA) /* no op, getkey timed out */
+ continue;
+ else if(!input++)
+ (*Pmaster->keybinput)();
+
+ if (mpresf != FALSE) { /* message stay around only */
+ if (mpresf++ > NMMESSDELAY) /* so long! */
+ mlerase();
+ }
+
+ f = FALSE; /* vestigial */
+ n = 1;
+ /* Do it. */
+ execute(normalize_cmd(c, pfkm, 2), f, n);
+ if(++checkpointcnt >= CHKPTDELAY){
+ checkpointcnt = 0;
+ writeout(chkptfile, TRUE);
+ }
+ }
+}
+
+/*
+ * Initialize all of the buffers and windows. The buffer name is passed down
+ * as an argument, because the main routine may have been told to read in a
+ * file by default, and we want the buffer name to be right.
+ */
+
+/*
+ * For the pine composer, we don't want to take over the whole screen
+ * for editing. the first some odd lines are to be used for message
+ * header information editing.
+ */
+void
+edinit(char bname[])
+{
+ register BUFFER *bp;
+ register WINDOW *wp;
+
+ if(Pmaster)
+ func_init();
+
+ bp = bfind(bname, TRUE, BFWRAPOPEN); /* First buffer */
+ wp = (WINDOW *) malloc(sizeof(WINDOW)); /* First window */
+
+ if (bp==NULL || wp==NULL){
+ if(Pmaster)
+ return;
+ else
+ exit(1);
+ }
+
+ curbp = bp; /* Make this current */
+ wheadp = wp;
+ curwp = wp;
+ wp->w_wndp = NULL; /* Initialize window */
+ wp->w_bufp = bp;
+ bp->b_nwnd = 1; /* Displayed. */
+ wp->w_linep = bp->b_linep;
+ wp->w_dotp = bp->b_linep;
+ wp->w_doto = 0;
+ wp->w_markp = wp->w_imarkp = NULL;
+ wp->w_marko = wp->w_imarko = 0;
+ bp->b_linecnt = -1;
+
+ if(Pmaster){
+ term.t_mrow = Pmaster->menu_rows;
+ wp->w_toprow = ComposerTopLine = COMPOSER_TOP_LINE;
+ wp->w_ntrows = term.t_nrow - COMPOSER_TOP_LINE - term.t_mrow;
+ fillcol = Pmaster->fillcolumn;
+ strncpy(opertree,
+ (Pmaster->oper_dir && strlen(Pmaster->oper_dir) < NLINE)
+ ? Pmaster->oper_dir : "", sizeof(opertree));
+ opertree[sizeof(opertree)-1] = '\0';
+ input_cs = Pmaster->input_cs;
+ }
+ else{
+ if(sup_keyhelp)
+ term.t_mrow = 0;
+ else
+ term.t_mrow = 2;
+
+ wp->w_toprow = 2;
+ wp->w_ntrows = term.t_nrow - 2 - term.t_mrow;
+ if(userfillcol > 0) /* set fill column */
+ fillcol = userfillcol;
+ else
+ fillcol = term.t_ncol - 6;
+ }
+
+ /*
+ * MDSCUR mode implies MDTREE mode with a opertree of home directory,
+ * unless opertree has been set differently.
+ */
+ if((gmode & MDSCUR) && !opertree[0]){
+ strncpy(opertree, gethomedir(NULL), sizeof(opertree));
+ opertree[sizeof(opertree)-1] = '\0';
+ }
+
+ if(*opertree)
+ fixpath(opertree, sizeof(opertree));
+
+ wp->w_force = 0;
+ wp->w_flag = WFMODE|WFHARD; /* Full. */
+}
+
+
+/*
+ * This is the general command execution routine. It handles the fake binding
+ * of all the keys to "self-insert". It also clears out the "thisflag" word,
+ * and arranges to move it to the "lastflag", so that the next command can
+ * look at it. Return the status of command.
+ */
+int
+execute(UCS c, int f, int n)
+{
+ KEYTAB *ktp;
+ int status, ww;
+
+ ktp = (Pmaster) ? &keytab[0] : &pkeytab[0];
+
+ while (ktp->k_fp != NULL) {
+ if (ktp->k_code == c) {
+
+ if(lastflag&CFFILL){
+ curwp->w_flag |= WFMODE;
+ if(Pmaster == NULL)
+ sgarbk = TRUE;
+ }
+
+ thisflag = 0;
+ status = (*ktp->k_fp)(f, n);
+ if((lastflag & CFFILL) && !(thisflag & CFFILL))
+ fdelete();
+ if((lastflag & CFFLBF) && !(thisflag & CFFLBF))
+ kdelete();
+
+ lastflag = thisflag;
+
+ /*
+ * Reset flag saying wrap should open a new line whenever
+ * we execute a command (as opposed to just typing in text).
+ * However, if that command leaves us in the same line on the
+ * screen, then don't reset.
+ */
+ if(curwp->w_flag & (WFMOVE | WFHARD))
+ curbp->b_flag |= BFWRAPOPEN; /* wrap should open new line */
+
+ return (status);
+ }
+ ++ktp;
+ }
+
+ if(lastflag & CFFILL) /* blat unusable fill data */
+ fdelete();
+ if(lastflag & CFFLBF)
+ kdelete();
+
+ if (VALID_KEY(c)) { /* Self inserting. */
+
+ if (n <= 0) { /* Fenceposts. */
+ lastflag = 0;
+ return (n<0 ? FALSE : TRUE);
+ }
+ thisflag = 0; /* For the future. */
+
+ /* do the appropriate insertion */
+ /* pico never does C mode, this is simple */
+ status = linsert(n, c);
+
+ /*
+ * Check to make sure we didn't go off of the screen
+ * with that character. Take into account tab expansion.
+ * If so wrap the line...
+ */
+ if(curwp->w_bufp->b_mode & MDWRAP){
+ int j, wid;
+
+ wid = 0;
+ for(j = 0; j < llength(curwp->w_dotp); j++)
+ if(ucs4_isspace(lgetc(curwp->w_dotp, j).c)){
+ if(lgetc(curwp->w_dotp, j).c == TAB){
+ ++wid;
+ while(wid & 0x07)
+ ++wid;
+ }
+ else
+ ++wid;
+ }
+ else{
+ ww = wcellwidth((UCS) lgetc(curwp->w_dotp, j).c);
+ wid += (ww >= 0 ? ww : 1);
+ if(wid > fillcol){
+ wrapword();
+ break;
+ }
+ }
+ }
+
+ lastflag = thisflag;
+ return (status);
+ }
+
+ unknown_command(c);
+
+ lastflag = 0; /* Fake last flags. */
+ return (FALSE);
+}
+
+
+
+/*
+ * Fancy quit command, as implemented by Norm. If the any buffer has
+ * changed do a write on that buffer and exit emacs, otherwise simply exit.
+ */
+int
+quickexit(int f, int n)
+{
+ register BUFFER *bp; /* scanning pointer to buffers */
+
+ bp = bheadp;
+ while (bp != NULL) {
+ if ((bp->b_flag&BFCHG) != 0 /* Changed. */
+ && (bp->b_flag&BFTEMP) == 0) { /* Real. */
+ curbp = bp; /* make that buffer cur */
+ filesave(f, n);
+ }
+ bp = bp->b_bufp; /* on to the next buffer */
+ }
+ return(wquit(f, n)); /* conditionally quit */
+}
+
+
+
+/*
+ * abort_composer - ask the question here, then go quit or
+ * return FALSE
+ */
+int
+abort_composer(int f, int n)
+{
+ char *result;
+
+ result = "";
+
+ Pmaster->arm_winch_cleanup++;
+ if(Pmaster->canceltest){
+ if(((Pmaster->pine_flags & MDHDRONLY) && !any_header_changes())
+ || (result = (*Pmaster->canceltest)(redraw_pico_for_callback))){
+ pico_all_done = COMP_CANCEL;
+ emlwrite(result, NULL);
+ Pmaster->arm_winch_cleanup--;
+ return(TRUE);
+ }
+ else{
+ /* TRANSLATORS: The user typed the Cancel command and was
+ asked to confirm that. Instead they canceled the cancel
+ command. */
+ emlwrite(_("Cancel Cancelled"), NULL);
+ curwp->w_flag |= WFMODE; /* and modeline so we */
+ sgarbk = TRUE; /* redraw the keymenu */
+ pclear(term.t_nrow-1, term.t_nrow);
+ Pmaster->arm_winch_cleanup--;
+ return(FALSE);
+ }
+ }
+ else switch(mlyesno_utf8(Pmaster->headents
+ ? _("Cancel message (answering \"Yes\" will abandon your mail message)")
+ : (anycb() == FALSE)
+ ? _("Cancel Edit (and abandon changes)")
+ : _("Cancel Edit"),
+ FALSE)){
+ case TRUE:
+ pico_all_done = COMP_CANCEL;
+ return(TRUE);
+
+ case ABORT:
+ emlwrite(_("\007Cancel Cancelled"), NULL);
+ break;
+
+ default:
+ mlerase();
+ }
+ return(FALSE);
+}
+
+
+/*
+ * suspend_composer - return to pine with what's been edited so far
+ */
+int
+suspend_composer(int f, int n)
+{
+ if(Pmaster && Pmaster->headents)
+ pico_all_done = COMP_SUSPEND;
+ else
+ (*term.t_beep)();
+
+ return(TRUE);
+}
+
+
+
+/*
+ * Quit command. If an argument, always quit. Otherwise confirm if a buffer
+ * has been changed and not written out. Normally bound to "C-X C-C".
+ */
+int
+wquit(int f, int n)
+{
+ register int s;
+
+ if(Pmaster){
+ char *result = NULL;
+ int ret;
+
+ /* First, make sure there are no outstanding problems */
+ if(AttachError()){
+ emlwrite(_("\007Problem with attachments! Fix errors or delete attachments."), NULL);
+ return(FALSE);
+ }
+
+#ifdef SPELLER
+ if(Pmaster->always_spell_check)
+ if(spell(0, 0) == -1)
+ sleep(3); /* problem, show error */
+#endif
+ /*
+ * if we're not in header, show some of it as we verify sending...
+ */
+ display_for_send();
+ packheader();
+ Pmaster->arm_winch_cleanup++;
+ if((!(Pmaster->pine_flags & MDHDRONLY) || any_header_changes())
+ && (ret = (*Pmaster->exittest)(Pmaster->headents,
+ redraw_pico_for_callback,
+ Pmaster->allow_flowed_text,
+ &result))){
+ Pmaster->arm_winch_cleanup--;
+
+ if(ret == -1){
+ pico_all_done = COMP_CANCEL;
+ }
+ else{
+ if(sgarbf)
+ update();
+
+ lchange(WFHARD); /* set update flags... */
+ curwp->w_flag |= WFMODE; /* and modeline so we */
+ sgarbk = TRUE; /* redraw the keymenu */
+ pclear(term.t_nrow-2, term.t_nrow);
+ }
+
+ if(result && *result)
+ emlwrite(result, NULL);
+ }
+ else{
+ Pmaster->arm_winch_cleanup--;
+ pico_all_done = COMP_EXIT;
+ return(TRUE);
+ }
+ }
+ else{
+ if (f != FALSE /* Argument forces it. */
+ || anycb() == FALSE /* All buffers clean. */
+ /* User says it's OK. */
+ /* TRANSLATORS: buffer is the in-memory copy of a file */
+ || (s=mlyesno_utf8(_("Save modified buffer (ANSWERING \"No\" WILL DESTROY CHANGES)"), -1)) == FALSE) {
+ vttidy();
+#if defined(USE_TERMCAP) || defined(USE_TERMINFO) || defined(VMS)
+ kbdestroy(kbesc);
+#endif
+ exit(0);
+ }
+
+ if(s == TRUE){
+ if(filewrite(0,1) == TRUE)
+ wquit(1, 0);
+ }
+ else if(s == ABORT){
+ emlwrite(_("Exit cancelled"), NULL);
+ if(term.t_mrow == 0)
+ curwp->w_flag |= WFHARD; /* cause bottom 3 lines to paint */
+ }
+ return(s);
+ }
+
+ return(FALSE);
+}
+
+
+/*
+ * Has any editing been done to headers?
+ */
+int
+any_header_changes(void)
+{
+ struct headerentry *he;
+
+ for(he = Pmaster->headents; he->name != NULL; he++)
+ if(he->dirty)
+ break;
+
+ return(he->name && he->dirty);
+}
+
+int
+isquotedspace(LINE *line)
+{
+ int i, was_quote = 0;
+ for(i = 0; i < llength(line); i++){
+ if(lgetc(line, i).c == '>')
+ was_quote = 1;
+ else if(was_quote && lgetc(line, i).c == ' '){
+ if(i+1 < llength(line) && ucs4_isspace(lgetc(line,i+1).c))
+ return 1;
+ else
+ return 0;
+ }
+ else
+ return 0;
+ }
+ return 0;
+}
+
+/*
+ * This function serves two purposes, 1) to strip white space when
+ * Pmaster asks that the composition have its trailing white space
+ * stripped, or 2) to prepare the text as flowed text, as Pmaster
+ * is telling us that we're working with flowed text.
+ *
+ * What flowed currently means to us is stripping all trailing white
+ * space, except for one space if the following line is a continuation
+ * of the paragraph. Also, we space-stuff all lines beginning
+ * with white-space, and leave siglines alone.
+ */
+int
+cleanwhitespace(void)
+{
+ LINE *cursor_dotp = NULL, **lp = NULL;
+ int i = 0, cursor_doto = 0, is_cursor_line = 0;
+ int do_space_stuffing = 0;
+
+ if(Pmaster && Pmaster->allow_flowed_text && !(*Pmaster->user_says_noflow)())
+ do_space_stuffing++;
+
+ cursor_dotp = curwp->w_dotp;
+ cursor_doto = curwp->w_doto;
+ gotobob(FALSE, 1);
+
+ for(lp = &curwp->w_dotp; (*lp) != curbp->b_linep; (*lp) = lforw(*lp)){
+ if(!(llength(*lp) == 3
+ && lgetc(*lp, 0).c == '-'
+ && lgetc(*lp, 1).c == '-'
+ && lgetc(*lp, 2).c == ' ')
+ && llength(*lp)){
+ is_cursor_line = (cursor_dotp == (*lp));
+ /* trim trailing whitespace, to be added back if flowing */
+ for(i = llength(*lp); i; i--)
+ if(!ucs4_isspace(lgetc(*lp, i - 1).c))
+ break;
+ if(i != llength(*lp)){
+ int flow_line = 0;
+
+ if(Pmaster && !Pmaster->strip_ws_before_send
+ && lforw(*lp) != curbp->b_linep
+ && llength(lforw(*lp))
+ && !(ucs4_isspace(lgetc(lforw(*lp), 0).c)
+ || isquotedspace(lforw(*lp)))
+ && !(llength(lforw(*lp)) == 3
+ && lgetc(lforw(*lp), 0).c == '-'
+ && lgetc(lforw(*lp), 1).c == '-'
+ && lgetc(lforw(*lp), 2).c == ' '))
+ flow_line = 1;
+ if(flow_line && i && lgetc(*lp, i).c == ' '){
+ /* flowed line ending with space */
+ i++;
+ if(i != llength(*lp)){
+ curwp->w_doto = i;
+ ldelete(llength(*lp) - i, NULL);
+ }
+ }
+ else if(flow_line && i && ucs4_isspace(lgetc(*lp, i).c)){
+ /* flowed line ending with whitespace other than space*/
+ curwp->w_doto = i;
+ ldelete(llength(*lp) - i, NULL);
+ linsert(1, ' ');
+ }
+ else{
+ curwp->w_doto = i;
+ ldelete(llength(*lp) - i, NULL);
+ }
+ }
+ if(do_space_stuffing && llength(*lp) && ucs4_isspace(lgetc(*lp, 0).c)){
+ /* space-stuff only if flowed */
+ if(Pmaster)
+ Pmaster->space_stuffed = 1;
+ curwp->w_doto = 0;
+ if(is_cursor_line && cursor_doto)
+ cursor_doto++;
+ linsert(1, ' ');
+ }
+ if(is_cursor_line)
+ cursor_dotp = (*lp);
+ }
+ }
+
+ /* put the cursor back where we found it */
+ gotobob(FALSE, 1);
+ curwp->w_dotp = cursor_dotp;
+ curwp->w_doto = (cursor_doto < llength(curwp->w_dotp))
+ ? cursor_doto : llength(curwp->w_dotp) - 1;
+
+ return(0);
+}
+
+/*
+ * Remove all trailing white space from the text
+ */
+int
+stripwhitespace(void)
+{
+ int i;
+ LINE *cur_line = lforw(curbp->b_linep);
+
+ do{
+ /* we gotta test for the sigdash case here */
+ if(!(cur_line->l_used == 3 &&
+ lgetc(cur_line, 0).c == '-' &&
+ lgetc(cur_line, 1).c == '-' &&
+ lgetc(cur_line, 2).c == ' '))
+ for(i = cur_line->l_used - 1; i >= 0; i--)
+ if(ucs4_isspace(lgetc(cur_line, i).c))
+ cur_line->l_used--;
+ else
+ break;
+ }while((cur_line = lforw(cur_line)) != curbp->b_linep);
+ return 0;
+}
+
+/*
+ * Abort.
+ * Beep the beeper. Kill off any keyboard macro, etc., that is in progress.
+ * Sometimes called as a routine, to do general aborting of stuff.
+ */
+int
+ctrlg(int f, int n)
+{
+ emlwrite(_("Cancelled"), NULL);
+ return (ABORT);
+}
+
+
+/* tell the user that this command is illegal while we are in
+ * VIEW (read-only) mode
+ */
+int
+rdonly(void)
+{
+ (*term.t_beep)();
+ emlwrite("Key illegal in VIEW mode", NULL);
+ return(FALSE);
+}
+
+
+
+/*
+ * reset all globals to their initial values
+ */
+void
+func_init(void)
+{
+ extern int vtrow;
+ extern int vtcol;
+ extern int lbound;
+
+ /*
+ * re-initialize global buffer type variables ....
+ */
+ fillcol = (term.t_ncol > 80) ? 77 : term.t_ncol - 6;
+ sgarbf = TRUE;
+ mpresf = FALSE;
+ mline_open = FALSE;
+ ComposerEditing = FALSE;
+
+ /*
+ * re-initialize hardware display variables ....
+ */
+ vtrow = vtcol = lbound = 0;
+ clearcursor();
+
+ pat[0] = rpat[0] = '\0';
+ browse_dir[0] = '\0';
+}
+
+
+/*
+ * pico_help - help function for standalone composer
+ * Oops - looks like utf8title is unused!
+ *
+ * This should be fixed to handle TAB characters.
+ */
+int
+pico_help(char *utf8text[], char *utf8title, int i)
+{
+ register int numline = 0;
+ char **p;
+
+ p = utf8text;
+ while(*p++ != NULL)
+ numline++;
+
+ return(wscrollw(COMPOSER_TOP_LINE, term.t_nrow-1, utf8text, numline));
+}
+
+
+
+/*
+ * zotedit() - kills the buffer and frees all lines associated with it!!!
+ */
+void
+zotedit(void)
+{
+ wheadp->w_linep = wheadp->w_dotp = wheadp->w_markp = wheadp->w_imarkp = NULL;
+ bheadp->b_linep = bheadp->b_dotp = bheadp->b_markp = NULL;
+
+ free((char *) wheadp); /* clean up window */
+ wheadp = NULL;
+ curwp = NULL;
+
+ free((char *) bheadp); /* clean up buffers */
+ bheadp = NULL;
+ curbp = NULL;
+
+ zotheader(); /* blast header lines */
+
+ kdelete(); /* blast kill buffer */
+
+}
+
+
+#ifdef MOUSE
+/*
+ * Generic mouse handling functions
+ */
+MENUITEM menuitems[12]; /* key labels and functions */
+MENUITEM *mfunc = NULL; /* list of regional functions */
+mousehandler_t mtrack; /* mouse tracking handler */
+
+/* last mouse position */
+static unsigned long levent = 0L;
+static int lrow = 0, lcol = 0, doubleclick, lbutton, lflags;
+#ifdef DOS
+static clock_t lastcalled = 0;
+#else
+static time_t lastcalled = 0;
+#endif
+static mousehandler_t lastf;
+
+
+/*
+ * register_mfunc - register the given function to get called
+ * on mouse events in the given display region
+ */
+int
+register_mfunc(mousehandler_t f, int tlr, int tlc, int brr, int brc)
+{
+ MENUITEM **mp;
+
+ if(!mouseexist())
+ return(FALSE);
+
+ for(mp = &mfunc; *mp; mp = &(*mp)->next)
+ ;
+
+ *mp = (MENUITEM *)malloc(sizeof(MENUITEM));
+ memset(*mp, 0, sizeof(MENUITEM));
+
+ (*mp)->action = f;
+ (*mp)->tl.r = tlr;
+ (*mp)->br.r = brr;
+ (*mp)->tl.c = tlc;
+ (*mp)->br.c = brc;
+ (*mp)->lbl.c = (*mp)->lbl.r = 0;
+ (*mp)->label = "";
+ return(TRUE);
+}
+
+
+/*
+ * clear_mfunc - clear any previously set mouse function
+ */
+void
+clear_mfunc(mousehandler_t f)
+{
+ MENUITEM *mp, *tp;
+
+ if((mp = mfunc) != NULL){
+ if(mp->action == f)
+ mfunc = mp->next;
+ else
+ for(tp = mp; tp->next; tp = tp->next)
+ if(tp->next->action == f){
+ mp = tp->next;
+ tp->next = tp->next->next;
+ break;
+ }
+
+ if(mp){
+ mp->action = NULL;
+ free(mp);
+ }
+ }
+}
+
+
+
+#ifdef EX_MOUSE
+
+void
+clear_mtrack(void)
+{
+ mtrack = NULL;
+ mswin_allowmousetrack (FALSE);
+}
+
+
+void
+register_mtrack(mousehandler_t f)
+{
+ if (f) {
+ mtrack = f;
+ mswin_allowmousetrack (TRUE);
+ }
+ else
+ clear_mtrack ();
+}
+
+
+static void
+move_dot_to(int row, int col)
+{
+ LINE *lp;
+ int i;
+
+ lp = curwp->w_linep;
+ i = row - ((Pmaster) ? ComposerTopLine : 2);
+ while(i-- && lp != curbp->b_linep) /* count from top */
+ lp = lforw(lp);
+ curgoal = col;
+ curwp->w_dotp = lp; /* to new dot. */
+ curwp->w_doto = getgoal(lp);
+ curwp->w_flag |= WFMOVE;
+}
+
+
+/*
+ * mouse_in_pico
+ *
+ * When the mouse goes down in the body we set the mark and start
+ * tracking.
+ *
+ * As the mouse moves we update the dot and redraw the screen.
+ *
+ * If the mouse moves above or below the pico body region of the
+ * screen we scroll the text and update the dot position.
+ *
+ * When the mouse comes up we clean up. If the mouse did not
+ * move, then we clear the mark and turn off the selection.
+ *
+ * Most of the mouse processing is handled here. The exception is
+ * mouse down in the header. Can't call HeaderEditor() from here so
+ * we send up the KEY_MOUSE character, which gets dispatched to
+ * mousepress(), which _can_ call HeaderEditor().
+ */
+unsigned long
+mouse_in_pico(unsigned long mevent, int row, int col, int button, int flags)
+{
+ unsigned long rv = 0; /* Our return value. */
+ int trow, tcol; /* translated row and col. */
+
+ static int lheader = FALSE; /* Mouse down was in header. */
+
+
+ /*
+ * What's up.
+ */
+ switch (mevent) {
+ case M_EVENT_DOWN:
+ if(button != M_BUTTON_LEFT)
+ break;
+
+ /* Ignore mouse down if not in pico body region. */
+ if (row < 2 || row > term.t_nrow - (term.t_mrow+1)) {
+ clear_mtrack ();
+ break;
+ }
+
+ /* Detect double clicks. Not that we do anything with em, just
+ * detect them. */
+#ifdef DOS
+#ifdef CLOCKS_PER_SEC
+ doubleclick = (lrow == row && lcol == col
+ && clock() < (lastcalled + CLOCKS_PER_SEC/2));
+#else
+#ifdef CLK_TCK
+doubleclick = (lrow == row && lcol == col
+ && clock() < (lastcalled + CLK_TCK/2));
+#else
+ doubleclick = FALSE;
+#endif
+#endif
+ lastcalled = clock();
+#else
+ doubleclick = (lrow == row && lcol == col
+ && time(0) < (lastcalled + 2));
+ lastcalled = time(0);
+#endif
+ lheader = FALSE; /* Rember mouse down position. */
+ levent = mevent;
+ lrow = row;
+ lcol = col;
+ lbutton = button;
+ lflags = flags;
+
+ /* Mouse down in body? */
+ if (row < (Pmaster ? ComposerTopLine : 2)) {
+ /* Mouse down in message header -> no tracking, just remember
+ * where */
+ lheader = TRUE;
+ }
+ else {
+ /* Mouse down in message.
+ * If no shift key and an existing mark -> clear the mark.
+ * If shift key and no existing mark -> set mark before moving */
+ if (!(flags & M_KEY_SHIFT) && curwp->w_markp)
+ setmark (0,1); /* this clears the mark. */
+ else if (flags & M_KEY_SHIFT && !curwp->w_markp)
+ setmark (0,1); /* while this sets the mark. */
+
+ /* Reposition dot to mouse down. */
+ move_dot_to (row, col);
+
+ /* Set the mark to dot if no existing mark. */
+ if (curwp->w_markp == NULL)
+ setmark (0,1);
+
+ /* Track mouse movement. */
+ register_mtrack (mouse_in_pico);
+ update ();
+ lheader = FALSE; /* Just to be sure. */
+ }
+ break;
+
+
+ case M_EVENT_TRACK:
+ /* Mouse tracking. */
+ if (lheader) /* Ignore mouse movement in header. */
+ break;
+
+ /* If above or below body, scroll body and adjust the row and col. */
+ if (row < (Pmaster ? ComposerTopLine : 2)) {
+ /* Scroll text down screen and move dot to top left corner. */
+ scrollupline (0,1);
+ trow = (Pmaster) ? ComposerTopLine : 2;
+ tcol = 0;
+ }
+ else if (row > term.t_nrow - (term.t_mrow + 1)) {
+ /* Scroll text up screen and move dot to bottom right corner. */
+ scrolldownline (0,1);
+ trow = term.t_nrow - (term.t_mrow + 1);
+ tcol = term.t_ncol;
+ }
+ else {
+ trow = row;
+ tcol = col;
+ }
+
+ /* Move dot to target column. */
+ move_dot_to (trow, tcol);
+
+ /* Update screen. */
+ update ();
+ break;
+
+
+ case M_EVENT_UP:
+ if(button == M_BUTTON_RIGHT){
+#ifdef _WINDOWS
+ pico_popup();
+#endif
+ break;
+ }
+ else if(button != M_BUTTON_LEFT)
+ break;
+
+ if (lheader) {
+ lheader = FALSE;
+ /* Last down in header. */
+ if (row == lrow && col == lcol) {
+ /* Mouse up and down in same place in header. Means the
+ * user want to edit the header. Return KEY_MOUSE which
+ * will cause mousepress to be called, which will
+ * call HeaderEditor. Can't call HeaderEditor from here
+ * because that would mess up layering. */
+ if (curwp->w_marko)
+ setmark (0,1);
+ rv = (unsigned long) KEY_MOUSE;
+ }
+ }
+ else {
+ /* If up at same place, clear mark */
+ if (curwp->w_markp == curwp->w_dotp &&
+ curwp->w_marko == curwp->w_doto) {
+ setmark (0,1);
+ curwp->w_flag |= WFMOVE;
+ }
+ clear_mtrack ();
+ update ();
+ }
+ break;
+ }
+
+ return(rv);
+}
+#endif
+
+
+
+/*
+ * mouse_in_content - general mechanism used to pass recognized mouse
+ * events in predefined region back thru the usual
+ * keyboard input stream. The actual return value
+ * passed back from this function is set dynamically
+ * via the "down" argument which is read when both the
+ * "row" and "col" arguments are negative.
+ */
+unsigned long
+mouse_in_content(unsigned long mevent, int row, int col, int button, int flags)
+{
+ unsigned long rv = 0;
+ static unsigned long mouse_val = KEY_MOUSE;
+
+ if(row == -1 && col == -1){
+ mouse_val = mevent; /* setting return value */
+ }
+ else {
+ /* A real event. */
+ levent = mevent;
+ switch (mevent) {
+ case M_EVENT_DOWN:
+ /* Mouse down does not mean anything, just keep track of
+ * where it went down and if this is a double click. */
+#ifdef DOS
+#ifdef CLOCKS_PER_SEC
+ doubleclick = (lrow == row && lcol == col
+ && clock() < (lastcalled + CLOCKS_PER_SEC/2));
+#else
+#ifdef CLK_TCK
+ doubleclick = (lrow == row && lcol == col
+ && clock() < (lastcalled + CLK_TCK/2));
+#else
+ doubleclick = FALSE;
+#endif
+#endif
+ lastcalled = clock();
+#else
+ doubleclick = (lrow == row && lcol == col
+ && time(0) < (lastcalled + 2));
+ lastcalled = time(0);
+#endif
+ lrow = row;
+ lcol = col;
+ lbutton = button;
+ lflags = flags;
+ break;
+
+ case M_EVENT_UP:
+ /* Mouse up. If in the same position as it went down
+ * then we return the value set above, which goes into
+ * the character input stream, which gets processed as
+ * a mouse event by some upper layer, which calls to
+ * mouse_get_last(). */
+ if (lrow == row && lcol == col) {
+ rv = mouse_val;
+ }
+ break;
+
+ case M_EVENT_TRACK:
+ break;
+ }
+ }
+
+ return(rv);
+}
+
+
+/*
+ * mouse_get_last - Get last mouse event.
+ *
+ */
+void
+mouse_get_last(mousehandler_t *f, MOUSEPRESS *mp)
+{
+ if (f != NULL)
+ *f = lastf;
+ if (mp != NULL) {
+ mp->mevent = levent;
+ mp->row = lrow;
+ mp->col = lcol;
+ mp->doubleclick = doubleclick;
+ mp->button = lbutton;
+ mp->flags = lflags;
+ }
+}
+
+
+
+/*
+ * register_key - register the given keystroke to accept mouse events
+ */
+void
+register_key(int i, unsigned rval, char *label, void (*label_printer)(),
+ int row, int col, int len, COLOR_PAIR *kn, COLOR_PAIR *kl)
+{
+ if(i > 11)
+ return;
+
+ menuitems[i].val = rval;
+ menuitems[i].tl.r = menuitems[i].br.r = row;
+ menuitems[i].tl.c = col;
+ menuitems[i].br.c = col + len;
+ menuitems[i].lbl.r = menuitems[i].tl.r;
+ menuitems[i].lbl.c = menuitems[i].tl.c;
+ menuitems[i].label_hiliter = label_printer;
+ if(menuitems[i].label){
+ free(menuitems[i].label);
+ menuitems[i].label = NULL;
+ }
+ if(menuitems[i].kncp)
+ free_color_pair(&menuitems[i].kncp);
+ if(menuitems[i].klcp)
+ free_color_pair(&menuitems[i].klcp);
+ if(kn)
+ menuitems[i].kncp = new_color_pair(kn->fg, kn->bg);
+ else
+ menuitems[i].kncp = NULL;
+ if(kl)
+ menuitems[i].klcp = new_color_pair(kl->fg, kl->bg);
+ else
+ menuitems[i].klcp = NULL;
+
+ if(label){
+ size_t len;
+
+ len = strlen(label);
+ if((menuitems[i].label = (char *)malloc((len+1)*sizeof(char))) != NULL){
+ strncpy(menuitems[i].label, label, len);
+ menuitems[i].label[len] = '\0';
+ }
+ }
+}
+
+
+int
+mouse_on_key(int row, int col)
+{
+ int i;
+
+ for(i = 0; i < 12; i++)
+ if(M_ACTIVE(row, col, &menuitems[i]))
+ return(TRUE);
+
+ return(FALSE);
+}
+#endif /* MOUSE */
+
+
+/*
+ * Below are functions for use outside pico to manipulate text
+ * in a pico's native format (circular linked list of lines).
+ *
+ * The idea is to streamline pico use by making it fairly easy
+ * for outside programs to prepare text intended for pico's use.
+ * The simple char * alternative is messy as it requires two copies
+ * of the same text, and isn't very economic in limited memory
+ * situations (THANKS BELLEVUE-BILLY.).
+ */
+typedef struct picotext {
+ LINE *linep;
+ LINE *dotp;
+ int doto;
+ short crinread;
+} PICOTEXT;
+
+#define PT(X) ((PICOTEXT *)(X))
+
+/*
+ * pico_get - return window struct pointer used as a handle
+ * to the other pico_xxx routines.
+ */
+void *
+pico_get(void)
+{
+ PICOTEXT *wp = NULL;
+ LINE *lp = NULL;
+
+ if((wp = (PICOTEXT *)malloc(sizeof(PICOTEXT))) != NULL){
+ wp->crinread = 0;
+ if((lp = lalloc(0)) == NULL){
+ free(wp);
+ return(NULL);
+ }
+
+ wp->dotp = wp->linep = lp->l_fp = lp->l_bp = lp;
+ wp->doto = 0;
+ }
+ else
+ emlwrite("Can't allocate space for text", NULL);
+
+ return((void *)wp);
+}
+
+/*
+ * pico_give - free resources and give up picotext struct
+ */
+void
+pico_give(void *w)
+{
+ register LINE *lp;
+ register LINE *fp;
+
+ fp = lforw(PT(w)->linep);
+ while((lp = fp) != PT(w)->linep){
+ fp = lforw(lp);
+ free(lp);
+ }
+ free(PT(w)->linep);
+ free((PICOTEXT *)w);
+}
+
+/*
+ * pico_readc - return char at current point. Up to calling routines
+ * to keep cumulative count of chars.
+ * The characters in PT are UCS-4 characters. The caller
+ * of pico_readc is expecting UTF-8 chars. We convert
+ * each UCS-4 character to a string of UTF-8 characters
+ * and return them one at a time.
+ */
+int
+pico_readc(void *w, unsigned char *c, int flags)
+{
+ int rv = 0;
+ UCS ucs;
+ static unsigned char obuf[6];
+ static unsigned char *obufpend = obuf;
+ static unsigned char *obufpnext = obuf;
+
+ if(!(flags & PICOREADC_NOUCS)){
+ if(obufpend > obuf){
+ *c = *obufpnext++;
+ rv++;
+ if(obufpnext >= obufpend){
+ obufpend = obuf;
+ obufpnext = obuf;
+ }
+
+ return(rv);
+ }
+ }
+
+ if(PT(w)->crinread){
+ *c = '\012'; /* return LF */
+ PT(w)->crinread = 0;
+ rv++;
+ }
+ else if(PT(w)->doto < llength(PT(w)->dotp)){ /* normal char to return */
+ if(flags & PICOREADC_NOUCS){
+ *c = (unsigned char) lgetc(PT(w)->dotp, (PT(w)->doto)++).c;
+ rv++;
+ }
+ else{
+ rv++;
+ ucs = lgetc(PT(w)->dotp, (PT(w)->doto)++).c;
+ obufpend = utf8_put(obuf, (unsigned long) ucs);
+ obufpnext = obuf;
+ if(obufpend > obuf){
+ *c = *obufpnext++;
+ if(obufpnext >= obufpend){
+ obufpend = obuf;
+ obufpnext = obuf;
+ }
+ }
+ else
+ *c = '?';
+ }
+ }
+ else if(PT(w)->dotp != PT(w)->linep){ /* return line break */
+ PT(w)->dotp = lforw(PT(w)->dotp);
+ PT(w)->doto = 0;
+#if defined(DOS) || defined(OS2)
+ *c = '\015';
+ PT(w)->crinread++;
+#else
+ *c = '\012'; /* return local eol! */
+#endif
+ rv++;
+ } /* else no chars to return */
+
+ return(rv);
+}
+
+
+/*
+ * pico_writec - write a char into picotext and advance pointers.
+ * Up to calling routines to keep track of total chars
+ * written.
+ * Incoming text (c) is UTF-8 and written chars are UCS-4.
+ * We need to collect up multiple chars to make a single
+ * UCS-4 char, so there needs to be some state between calls.
+ */
+int
+pico_writec(void *w, int c, int flags)
+{
+ int rv = 0;
+
+ if(c == '\r') /* ignore CR's */
+ rv++; /* so fake it */
+ else if(c == '\n'){ /* insert newlines on LF */
+ /*
+ * OK, if there are characters on the current line or
+ * dotp is pointing to the delimiter line, insert a newline
+ * No here's the tricky bit; preserve the implicit EOF newline.
+ */
+ if(lforw(PT(w)->dotp) == PT(w)->linep && PT(w)->dotp != PT(w)->linep){
+ PT(w)->dotp = PT(w)->linep;
+ PT(w)->doto = 0;
+ }
+ else{
+ register LINE *lp;
+
+ if((lp = lalloc(0)) == NULL){
+ emlwrite("Can't allocate space for more characters",NULL);
+ return(0);
+ }
+
+ if(PT(w)->dotp == PT(w)->linep){
+ lforw(lp) = PT(w)->linep;
+ lback(lp) = lback(PT(w)->linep);
+ lforw(lback(lp)) = lback(PT(w)->linep) = lp;
+ }
+ else{
+ lforw(lp) = lforw(PT(w)->dotp);
+ lback(lp) = PT(w)->dotp;
+ lback(lforw(lp)) = lforw(PT(w)->dotp) = lp;
+ PT(w)->dotp = lp;
+ PT(w)->doto = 0;
+ }
+ }
+
+ rv++;
+ }
+ else{
+ if(flags & PICOREADC_NOUCS){
+ /*
+ * With this flag we're reverting to the old behavior where no
+ * UTF-8 to UCS-4 translation takes place. We assume nothing
+ * about the incoming byte stream. We just write a byte at a
+ * time. So even though it's being stored in PT each
+ * item is really limited to a single octet value.
+ */
+ rv = geninsert(&PT(w)->dotp, &PT(w)->doto, PT(w)->linep, c, 0, 1, NULL);
+ }
+ else{
+ static unsigned char cbuf[6];
+ static unsigned char *cbufp = cbuf;
+ UCS obuf[MAX(MB_LEN_MAX,32)];
+ int i, outchars = 0;
+
+ if(cbufp < cbuf+sizeof(cbuf)){
+ unsigned char *inputp;
+ unsigned long remaining_octets;
+ UCS ucs;
+
+ *cbufp++ = (unsigned char) c;
+ inputp = cbuf;
+ remaining_octets = (cbufp - cbuf) * sizeof(unsigned char);
+ ucs = (UCS) utf8_get(&inputp, &remaining_octets);
+
+ switch(ucs){
+ case U8G_ENDSTRG: /* incomplete character, wait */
+ case U8G_ENDSTRI: /* incomplete character, wait */
+ break;
+
+ default:
+ if(ucs & U8G_ERROR || ucs == UBOGON){
+ /*
+ * None of these cases is supposed to happen. If it
+ * does happen then the input stream isn't UTF-8
+ * so something is wrong. Treat each character in the
+ * input buffer as a separate error character and
+ * print a '?' for each.
+ */
+ for(inputp = cbuf; inputp < cbufp; inputp++)
+ obuf[outchars++] = '?';
+
+ cbufp = cbuf;
+ }
+ else{
+ /* got a character */
+ if(ucs >= 0x80 && wcellwidth(ucs) < 0){
+ /*
+ * This happens when we have a UTF-8 character that
+ * we aren't able to print in our locale. For example,
+ * if the locale is setup with the terminal
+ * expecting ISO-8859-1 characters then there are
+ * lots of UTF-8 characters that can't be printed.
+ * Print a '?' instead.
+ This may be the wrong thing to do. What happens if user
+ is just forwarding and doesn't edit. We are going to lose
+ the original value, aren't we? Maybe we do this only
+ when printing to the screen instead.
+ */
+ obuf[outchars++] = '?';
+ }
+ else{
+ /*
+ * A regular ucs character
+ */
+ obuf[outchars++] = ucs;
+ }
+
+ /* update the input buffer */
+ if(inputp >= cbufp) /* this should be the case */
+ cbufp = cbuf;
+ else{ /* extra chars for some reason? */
+ unsigned char *q, *newcbufp;
+
+ newcbufp = (cbufp - inputp) + cbuf;
+ q = cbuf;
+ while(inputp < cbufp)
+ *q++ = *inputp++;
+
+ cbufp = newcbufp;
+ }
+ }
+
+ break;
+ }
+ }
+ else{ /* error */
+ obuf[0] = '?';
+ outchars = 1;
+ cbufp = cbuf; /* start over */
+ }
+
+ /*
+ * Unless we have trouble translating outchars will be
+ * either 1 or 0. It is the UCS-4 character that we converted
+ * the input to. It's an array just so it can hold ? when
+ * there is trouble.
+ */
+ rv = 1;
+ for(i = 0; rv && i < outchars; i++)
+ if(!geninsert(&PT(w)->dotp, &PT(w)->doto, PT(w)->linep, obuf[i], 0, 1, NULL))
+ rv = 0;
+ }
+ }
+
+ /*
+ * Warning, this is no longer number written, because when we have a
+ * multibyte UTF-8 character we won't write anything until we get all
+ * the bytes.
+ */
+ return((rv) ? 1 : 0); /* return number written */
+}
+
+
+/*
+ * pico_puts - just write the given string into the text
+ */
+int
+pico_puts(void *w, char *s, int flags)
+{
+ int rv = 0;
+
+ if(*s != '\0'){
+ rv = 1;
+ while(rv && *s != '\0')
+ if(!pico_writec(w, (int)*s++, flags))
+ rv = 0;
+ }
+
+ return((rv) ? 1 : 0);
+}
+
+
+/*
+ * pico_seek - position dotp and dot at requested location
+ */
+int
+pico_seek(void *w, long offset, int orig)
+{
+ register LINE *lp;
+
+ PT(w)->crinread = 0;
+ switch(orig){
+ case 0 : /* SEEK_SET */
+ PT(w)->dotp = lforw(PT(w)->linep);
+ PT(w)->doto = 0;
+ case 1 : /* SEEK_CUR */
+ lp = PT(w)->dotp;
+ while(lp != PT(w)->linep){
+ if(offset <= llength(lp)){
+ PT(w)->doto = (int)offset;
+ PT(w)->dotp = lp;
+ break;
+ }
+
+ offset -= ((long)llength(lp)
+#if defined(DOS) || defined(OS2)
+ + 2L);
+#else
+ + 1L);
+#endif
+ lp = lforw(lp);
+ }
+ break;
+
+ case 2 : /* SEEK_END */
+ PT(w)->dotp = lback(PT(w)->linep);
+ PT(w)->doto = llength(PT(w)->dotp);
+ break;
+ default :
+ return(-1);
+ }
+
+ return(0);
+}
+
+
+/*
+ * breplace - replace the current window's text with the given
+ * LINEs
+ */
+void
+breplace(void *w)
+{
+ register LINE *lp;
+ register LINE *fp;
+
+ fp = lforw(curbp->b_linep);
+ while((lp = fp) != curbp->b_linep){ /* blast old lines */
+ fp = lforw(lp);
+ free(lp);
+ }
+ free(curbp->b_linep);
+
+ curbp->b_linep = PT(w)->linep; /* arrange pointers */
+
+ /*
+ * Undo space-stuffing that was done when we were preparing to send.
+ * Some error happened so we're back to the composer.
+ */
+ if(Pmaster && Pmaster->space_stuffed){
+ Pmaster->space_stuffed = 0;
+ for(lp = lforw(curbp->b_linep); lp != curbp->b_linep; lp = lforw(lp)){
+ if(llength(lp) && ucs4_isspace(lgetc(lp, 0).c)){
+ curwp->w_dotp = lp;
+ curwp->w_doto = 0;
+ forwdel(FALSE,1);
+ }
+ }
+ }
+
+ curwp->w_linep = lforw(curbp->b_linep);
+ curwp->w_dotp = lforw(curbp->b_linep);
+ curwp->w_doto = 0;
+ curwp->w_markp = curwp->w_imarkp = NULL;
+ curwp->w_marko = curwp->w_imarko = 0;
+
+ curbp->b_dotp = curwp->w_dotp;
+ curbp->b_doto = curbp->b_marko = 0;
+ curbp->b_markp = NULL;
+ curbp->b_linecnt = -1;
+
+ curwp->w_flag |= WFHARD;
+}
+
+
+#ifdef _WINDOWS
+/*
+ *
+ */
+int
+composer_file_drop(int x, int y, char *filename)
+{
+ int attached = 0;
+ EML eml;
+
+ if((ComposerTopLine > 2 && x <= ComposerTopLine)
+ || !LikelyASCII(filename)){
+ AppendAttachment(filename, NULL, NULL);
+ attached++;
+ }
+ else{
+ setimark(FALSE, 1);
+ ifile(filename);
+ swapimark(FALSE, 1);
+ }
+
+ if(ComposerEditing){ /* update display */
+ PaintBody(0);
+ }
+ else{
+ pico_refresh(0, 1);
+ update();
+ }
+
+ eml.s = filename;
+ if(attached)
+ emlwrite(_("Attached dropped file \"%s\""), &eml);
+ else
+ emlwrite(_("Inserted dropped file \"%s\""), &eml);
+
+ if(ComposerEditing){ /* restore cursor */
+ HeaderPaintCursor();
+ }
+ else{
+ curwp->w_flag |= WFHARD;
+ update();
+ }
+
+ return(1);
+}
+
+
+int
+pico_cursor(int col, long row)
+{
+ return((row > 1 && row <= term.t_nrow - (term.t_mrow + 1))
+ ? MSWIN_CURSOR_IBEAM
+ : MSWIN_CURSOR_ARROW);
+}
+#endif /* _WINDOWS */
diff --git a/pico/pico.h b/pico/pico.h
new file mode 100644
index 00000000..cf25abea
--- /dev/null
+++ b/pico/pico.h
@@ -0,0 +1,452 @@
+/*
+ * $Id: pico.h 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2009 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ *
+ * Program: pico.h - definitions for Pine's composer library
+ */
+
+#ifndef PICO_H
+#define PICO_H
+
+#include "mode.h"
+
+#include "../pith/osdep/color.h"
+#include "../pith/osdep/err_desc.h"
+#include "../pith/charconv/utf8.h"
+
+#define errstr(n) error_description(n)
+
+/*
+ * Defined for attachment support
+ */
+#define ATTACHMENTS 1
+
+
+/*
+ * defs of return codes from pine mailer composer.
+ */
+#define BUF_CHANGED 0x01
+#define COMP_CANCEL 0x02
+#define COMP_EXIT 0x04
+#define COMP_FAILED 0x08
+#define COMP_SUSPEND 0x10
+#define COMP_GOTHUP 0x20
+
+
+/*
+ * top line from the top of the screen for the editor to do
+ * its stuff
+ */
+#define COMPOSER_TOP_LINE 2
+#define COMPOSER_TITLE_LINE 0
+
+
+
+#define HLSZ NLINE
+
+/*
+ * definitions of Mail header structures
+ */
+struct hdr_line {
+ UCS text[HLSZ];
+ struct hdr_line *next;
+ struct hdr_line *prev;
+};
+
+
+/* must be the same as HelpType in pith/helptext.h */
+#define HELP_T char **
+
+
+/*
+ * This structure controls the header line items on the screen. An
+ * instance of this should be created and passed in as an argument when
+ * pico is called. The list is terminated by an entry with the name
+ * element NULL.
+ */
+
+struct headerentry {
+ char *prompt;
+ char *name;
+ HELP_T help;
+ int prwid; /* prompt width on screen */
+ int maxlen;
+ char **realaddr;
+ int (*builder)(); /* Function to verify/canonicalize val */
+ struct headerentry *affected_entry, *next_affected;
+ /* entry builder's 4th arg affects */
+ char *(*selector)(); /* Browser for possible values */
+ char *key_label; /* Key label for key to call browser */
+ char *(*fileedit)(); /* Editor for file named in header */
+ int (*nickcmpl)(); /* routine that helps with nickname completion */
+ unsigned display_it:1; /* field is to be displayed by default */
+ unsigned break_on_comma:1; /* Field breaks on commas */
+ unsigned is_attach:1; /* Special case field for attachments */
+ unsigned rich_header:1; /* Field is part of rich header */
+ unsigned only_file_chars:1; /* Field is a file name */
+ unsigned single_space:1; /* Crush multiple spaces into one */
+ unsigned sticky:1; /* Can't change this via affected_entry*/
+ unsigned dirty:1; /* We've changed this entry */
+ unsigned start_here:1; /* begin composer on first on lit */
+ unsigned blank:1; /* blank line separator */
+ unsigned sticky_special:1; /* special treatment */
+#ifdef KS_OSDATAVAR
+ KS_OSDATAVAR /* Port-Specific keymenu data */
+#endif
+ void *bldr_private; /* Data managed by builders */
+ struct hdr_line *hd_text;
+};
+
+
+/*
+ * Structure to pass as arg to builders.
+ *
+ * me -- A pointer to the bldr_private data for the entry currently
+ * being edited.
+ * tptr -- A malloc'd copy of the displayed text for the affected_entry
+ * pointed to by the aff argument.
+ * aff -- A pointer to the bldr_private data for the affected_entry (the
+ * entry that this entry affects).
+ * next -- The next affected_entry in the list. For example, the Lcc entry
+ * affects the To entry which affects the Fcc entry.
+ */
+typedef struct bld_arg {
+ void **me;
+ char *tptr;
+ void **aff;
+ struct bld_arg *next;
+} BUILDER_ARG;
+
+
+/*
+ * structure to keep track of header display
+ */
+struct on_display {
+ int p_ind; /* index into line */
+ int p_len; /* length of line */
+ int p_line; /* physical line on screen */
+ int top_e; /* topline's header entry */
+ struct hdr_line *top_l; /* top line on display */
+ int cur_e; /* current header entry */
+ struct hdr_line *cur_l; /* current hd_line */
+}; /* global on_display struct */
+
+
+/*
+ * Structure to handle attachments
+ */
+typedef struct pico_atmt {
+ char *description; /* attachment description */
+ char *filename; /* file/pseudonym for attachment */
+ char *size; /* size of attachment */
+ char *id; /* attachment id */
+ unsigned short flags;
+ struct pico_atmt *next;
+} PATMT;
+
+/*
+ * Structure to contain color options
+ */
+typedef struct pico_colors {
+ COLOR_PAIR *tbcp; /* title bar color pair */
+ COLOR_PAIR *klcp; /* key label color pair */
+ COLOR_PAIR *kncp; /* key name color pair */
+ COLOR_PAIR *stcp; /* status color pair */
+ COLOR_PAIR *prcp; /* prompt color pair */
+} PCOLORS;
+
+/*
+ * Flags for attachment handling
+ */
+#define A_FLIT 0x0001 /* Accept literal file and size */
+#define A_ERR 0x0002 /* Problem with specified attachment */
+#define A_TMP 0x0004 /* filename is temporary, delete it */
+
+
+/*
+ * Master pine composer structure. Right now there's not much checking
+ * that any of these are pointing to something, so pine must have them pointing
+ * somewhere.
+ */
+typedef struct pico_struct {
+ void *msgtext; /* ptrs to malloc'd arrays of char */
+ char *pine_anchor; /* ptr to pine anchor line */
+ char *pine_version; /* string containing Pine's version */
+ char *oper_dir; /* Operating dir (confine to tree) */
+ char *home_dir; /* Home directory that should be used (WINDOWS) */
+ char *quote_str; /* prepended to lines of quoted text */
+ char *exit_label; /* Label for ^X in keymenu */
+ char *ctrlr_label; /* Label for ^R in keymenu */
+ char *alt_spell; /* Checker to use other than "spell" */
+ char **alt_ed; /* name of alternate editor or NULL */
+ UCS *wordseps; /* word separator characters other than space */
+ int fillcolumn; /* where to wrap */
+ int menu_rows; /* number of rows in menu (0 or 2) */
+ int space_stuffed; /* space-stuffed for flowed, in case needs undoing */
+ long edit_offset; /* offset into hdr line or body */
+ PATMT *attachments; /* linked list of attachments */
+ PCOLORS *colors; /* colors for titlebar and keymenu */
+ void *input_cs; /* passed to mbtow() via kbseq() */
+ long pine_flags; /* entry mode flags */
+ /* The next few bits are features that don't fit in pine_flags */
+ /* If we had this to do over, it would probably be one giant bitmap */
+ unsigned always_spell_check:1; /* always spell-checking upon quit */
+ unsigned strip_ws_before_send:1; /* don't default strip bc of flowed */
+ unsigned allow_flowed_text:1; /* clean text when done to keep flowed */
+ int (*helper)(); /* Pine's help function */
+ int (*showmsg)(); /* Pine's display_message */
+ UCS (*suspend)(); /* Pine's suspend */
+ void (*keybinput)(); /* Pine's keyboard input indicator */
+ int (*tty_fix)(); /* Let Pine fix tty state */
+ long (*newmail)(); /* Pine's report_new_mail */
+ long (*msgntext)(); /* callback to get msg n's text */
+ int (*upload)(); /* callback to rcv uplaoded text */
+ char *(*ckptdir)(); /* callback for checkpoint file dir */
+ int (*exittest)(); /* callback to verify exit request */
+ char *(*canceltest)(); /* callback to verify cancel request */
+ int (*mimetype)(); /* callback to display mime type */
+ int (*expander)(); /* callback to expand address lists */
+ int (*user_says_noflow)(); /* callback to tell us we're not flowing */
+ void (*resize)(); /* callback handling screen resize */
+ void (*winch_cleanup)(); /* callback handling screen resize */
+ void (*newthread)(); /* callback to create new thread */
+ int arm_winch_cleanup; /* do the winch_cleanup if resized */
+ HELP_T search_help;
+ HELP_T ins_help;
+ HELP_T ins_m_help;
+ HELP_T composer_help;
+ HELP_T browse_help;
+ HELP_T attach_help;
+ struct headerentry *headents;
+} PICO;
+
+
+/*
+ * Used to save and restore global pico variables that are destroyed by
+ * calling pico a second time. This happens when pico calls a selector
+ * in the HeaderEditor and that selector calls pico again.
+ */
+typedef struct save_stuff {
+ int vtrow,
+ vtcol,
+ lbound;
+ VIDEO **vscreen,
+ **pscreen; /* save pointers */
+ struct on_display ods; /* save whole struct */
+ short delim_ps,
+ invert_ps;
+ int pico_all_done;
+ jmp_buf finstate;
+ UCS *pico_anchor; /* save pointer */
+ PICO *Pmaster; /* save pointer */
+ int fillcol;
+ UCS *pat; /* save array */
+ int ComposerTopLine,
+ ComposerEditing;
+ long gmode;
+ char *alt_speller; /* save pointer */
+ UCS *quote_str; /* save pointer */
+ UCS *wordseps; /* save pointer */
+ int currow,
+ curcol,
+ thisflag,
+ lastflag,
+ curgoal;
+ char *opertree; /* save array */
+ WINDOW *curwp; /* save pointer */
+ WINDOW *wheadp; /* save pointer */
+ BUFFER *curbp; /* save pointer */
+ BUFFER *bheadp; /* save pointer */
+ int km_popped;
+ int mrow;
+} VARS_TO_SAVE;
+
+
+#ifdef MOUSE
+/*
+ * Mouse buttons.
+ */
+#define M_BUTTON_LEFT 0
+#define M_BUTTON_MIDDLE 1
+#define M_BUTTON_RIGHT 2
+
+
+/*
+ * Flags. (modifier keys)
+ */
+#define M_KEY_CONTROL 0x01 /* Control key was down. */
+#define M_KEY_SHIFT 0x02 /* Shift key was down. */
+
+
+/*
+ * Mouse Events
+ */
+#define M_EVENT_DOWN 0x01 /* Mouse went down. */
+#define M_EVENT_UP 0x02 /* Mouse went up. */
+#define M_EVENT_TRACK 0x04 /* Mouse tracking */
+
+/*
+ * Mouse event information.
+ */
+typedef struct mouse_struct {
+ unsigned long mevent; /* Indicates type of event: Down, Up or Track */
+ char down; /* TRUE when mouse down event */
+ char doubleclick; /* TRUE when double click. */
+ int button; /* button pressed. */
+ int flags; /* What other keys pressed. */
+ int row;
+ int col;
+} MOUSEPRESS;
+
+
+
+typedef unsigned long (*mousehandler_t)(unsigned long, int, int, int, int);
+
+typedef struct point {
+ unsigned r:8; /* row value */
+ unsigned c:8; /* column value */
+} MPOINT;
+
+
+typedef struct menuitem {
+ unsigned val; /* return value */
+ mousehandler_t action; /* action to perform */
+ MPOINT tl; /* top-left corner of active area */
+ MPOINT br; /* bottom-right corner of active area */
+ MPOINT lbl; /* where the label starts */
+ char *label;
+ void (*label_hiliter)();
+ COLOR_PAIR *kncp; /* key name color pair */
+ COLOR_PAIR *klcp; /* key label color pair */
+ struct menuitem *next;
+} MENUITEM;
+#endif
+
+
+/*
+ * Structure used to manage keyboard input that comes as escape
+ * sequences (arrow keys, function keys, etc.)
+ */
+typedef struct KBSTREE {
+ char value;
+ int func; /* Routine to handle it */
+ struct KBSTREE *down;
+ struct KBSTREE *left;
+} KBESC_T;
+
+
+/*
+ * various flags that they may passed to PICO
+ */
+#define P_HICTRL 0x80000000 /* overwrite mode */
+#define P_CHKPTNOW 0x40000000 /* do the checkpoint on entry */
+#define P_DELRUBS 0x20000000 /* map ^H to forwdel */
+#define P_LOCALLF 0x10000000 /* use local vs. NVT EOL */
+#define P_BODY 0x08000000 /* start composer in body */
+#define P_HEADEND 0x04000000 /* start composer at end of header */
+#define P_VIEW MDVIEW /* read-only */
+#define P_FKEYS MDFKEY /* run in function key mode */
+#define P_SECURE MDSCUR /* run in restricted (demo) mode */
+#define P_TREE MDTREE /* restrict to a subtree */
+#define P_SUSPEND MDSSPD /* allow ^Z suspension */
+#define P_ADVANCED MDADVN /* enable advanced features */
+#define P_CURDIR MDCURDIR /* use current dir for lookups */
+#define P_ALTNOW MDALTNOW /* enter alt ed sans hesitation */
+#define P_SUBSHELL MDSPWN /* spawn subshell for suspend */
+#define P_COMPLETE MDCMPLT /* enable file name completion */
+#define P_DOTKILL MDDTKILL /* kill from dot to eol */
+#define P_SHOCUR MDSHOCUR /* cursor follows hilite in browser*/
+#define P_HIBITIGN MDHBTIGN /* ignore chars with hi bit set */
+#define P_DOTFILES MDDOTSOK /* browser displays dot files */
+#define P_NOBODY MDHDRONLY /* Operate only on given headers */
+#define P_ALLOW_GOTO MDGOTO /* support "Goto" in file browser */
+#define P_REPLACE MDREPLACE /* allow "Replace" in "Where is" */
+
+
+/*
+ * Main defs
+ */
+#ifdef maindef
+PICO *Pmaster = NULL; /* composer specific stuff */
+char *version = "5.05"; /* PICO version number */
+
+#else
+extern PICO *Pmaster; /* composer specific stuff */
+extern char *version; /* pico version! */
+
+#endif /* maindef */
+
+
+/*
+ * Flags for FileBrowser call
+ */
+#define FB_READ 0x0001 /* Looking for a file to read. */
+#define FB_SAVE 0x0002 /* Looking for a file to save. */
+#define FB_ATTACH 0x0004 /* Looking for a file to attach */
+#define FB_LMODEPOS 0x0008 /* ListMode is a possibility */
+#define FB_LMODE 0x0010 /* Using ListMode now */
+
+
+/*
+ * Flags for pico_readc/pico_writec.
+ */
+#define PICOREADC_NONE 0x00
+#define PICOREADC_NOUCS 0x01
+
+
+/*
+ * number of keystrokes to delay removing an error message, or new mail
+ * notification, or checkpointing
+ */
+#define MESSDELAY 25
+#define NMMESSDELAY 60
+#ifndef CHKPTDELAY
+#define CHKPTDELAY 100
+#endif
+
+#include "./keydefs.h"
+
+
+/*
+ * useful function definitions
+ */
+int pico(PICO *pm);
+int pico_file_browse(PICO *, char *, size_t, char *, size_t, char *, size_t, int);
+void *pico_get(void);
+void pico_give(void *w);
+int pico_readc(void *w, unsigned char *c, int flags);
+int pico_writec(void *w, int c, int flags);
+int pico_puts(void *w, char *s, int flags);
+int pico_seek(void *w, long offset, int orig);
+int pico_replace(void *, char *);
+int pico_fncomplete(char *, char *, size_t);
+#if defined(DOS) || defined(OS2)
+int pico_nfsetcolor(char *);
+int pico_nbsetcolor(char *);
+int pico_rfsetcolor(char *);
+int pico_rbsetcolor(char *);
+#endif
+#ifdef MOUSE
+int register_mfunc(mousehandler_t, int, int, int, int);
+void clear_mfunc(mousehandler_t);
+unsigned long mouse_in_content(unsigned long, int, int, int, int);
+unsigned long mouse_in_pico(unsigned long, int, int, int, int);
+void mouse_get_last(mousehandler_t *, MOUSEPRESS *);
+void register_key(int, unsigned, char *, void (*)(),
+ int, int, int, COLOR_PAIR *, COLOR_PAIR *);
+int mouse_on_key(int, int);
+#endif /* MOUSE */
+
+
+#endif /* PICO_H */
diff --git a/pico/picolib.def b/pico/picolib.def
new file mode 100644
index 00000000..2f22a750
--- /dev/null
+++ b/pico/picolib.def
@@ -0,0 +1,94 @@
+;
+; pico.def
+; Linker module definition file for pico editor interface
+;
+LIBRARY PICOLIB INITINSTANCE
+DESCRIPTION 'UW pico editor interface'
+DATA
+ MULTIPLE NONSHARED
+EXPORTS
+ ; pico interface
+ pico
+ pico_get
+ pico_give
+ pico_seek
+ pico_new_mail
+ pico_nbcolor
+ pico_rbcolor
+ pico_nfcolor
+ pico_rfcolor
+ pico_puts
+ pico_readc
+ pico_writec
+ pico_fncomplete
+ pico_file_browse
+ ; pc functions
+ ibmgetc
+ ibmbeep
+ ibmputc
+ ibmeeol
+ ibmeeop
+ ibmmove
+ ibmopen
+ ibmclose
+ ibmrev
+ vidUpdate
+ ; keyboard interface
+ kbd_ready
+ kbd_getkey
+ kbd_flush
+ ; mouse/key functions
+ clear_mfunc
+ register_mfunc
+ init_mouse
+ end_mouse
+ checkmouse
+ mouseon
+ mouseoff
+ register_key
+ mouse_get_last
+ ; utility functions
+ interrupt_ok
+ dont_interrupt
+ win_multiplex
+ enter_text_mode
+ exit_text_mode
+ normalize_cmd
+ rebindfunc
+ time_to_check
+ wquit
+ readin
+ update
+ edinit
+ mpresf
+ vtinit
+ movecursor
+ GetKey
+ mouse_in_content
+ mlerase
+ execute
+ makename
+ forwdel
+ forwline
+ emlwrite
+ bindtokey
+ quickexit
+ fixpath
+ set_browser_title
+ gethomedir
+ FileBrowse
+ ; exported global data
+ timeout
+ userfillcol
+ alt_speller
+ sup_keyhelp
+ term
+ peeol
+ gmode
+ curbp
+ curwp
+ Pmaster
+ lastflag
+ opertree
+ km_popped
+ os2_fflush
diff --git a/pico/pilot.c b/pico/pilot.c
new file mode 100644
index 00000000..c00b257d
--- /dev/null
+++ b/pico/pilot.c
@@ -0,0 +1,472 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: pilot.c 1184 2008-12-16 23:52:15Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Main stand-alone Pine File Browser routines
+ *
+ *
+ * BROWSER NOTES:
+ *
+ * 30 Sep 92 - Stand alone PIne's "Lister of Things" came into being.
+ * It's built against libpico.a from a command line like:
+ *
+ * cc pilot.c libpico.a -ltermcap -lc -o pilot
+ *
+ * should it become a fleshed out tool, we'll move it into
+ * the normal build process.
+ */
+
+#include "headers.h"
+#include "../c-client/mail.h"
+#include "../c-client/rfc822.h"
+#include "../pith/osdep/collate.h"
+#include "../pith/charconv/filesys.h"
+#include "../pith/charconv/utf8.h"
+#include "../pith/conf.h"
+
+
+#define PILOT_VERSION "UW PILOT 2.99"
+
+
+extern char *gethomedir(int *);
+
+char *pilot_args(int, char **, int *);
+void pilot_args_help(void);
+void pilot_display_args_err(char *, char **, int);
+
+char args_pilot_missing_flag[] = N_("unknown flag \"%c\"");
+char args_pilot_missing_arg[] = N_("missing or empty argument to \"%c\" flag");
+char args_pilot_missing_num[] = N_("non numeric argument for \"%c\" flag");
+char args_pilot_missing_color[] = N_("missing color for \"%s\" flag");
+char args_pilot_missing_charset[] = N_("missing character set for \"%s\" flag");
+char args_pilot_input_charset[] = N_("input character set \"%s\" is unsupported");
+char args_pilot_output_charset[] = N_("output character set \"%s\" is unsupported");
+
+char *args_pilot_args[] = {
+/* TRANSLATORS: little help printed out when incorrect arguments are
+ given for pilot program. */
+N_("Possible Starting Arguments for Pilot file browser:"),
+"",
+N_("\tArgument\t\tMeaning"),
+N_("\t -a \t\tShowDot - show dot files in file browser"),
+N_("\t -j \t\tGoto - allow 'Goto' command in file browser"),
+N_("\t -g \t\tShow - show cursor in file browser"),
+N_("\t -m \t\tMouse - turn on mouse support"),
+N_("\t -v \t\tOneColumn - use single column display"),
+N_("\t -x \t\tNoKeyhelp - suppress keyhelp"),
+N_("\t -q \t\tTermdefWins - termcap or terminfo takes precedence over defaults"),
+N_("\t -f \t\tKeys - force use of function keys"),
+N_("\t -h \t\tHelp - give this list of options"),
+#ifndef _WINDOWS
+N_("\t -dcs <display_character_set> \tdefault uses LANG or LC_CTYPE from environment"),
+N_("\t -kcs <keyboard_character_set> \tdefaults to display_character_set"),
+N_("\t -syscs\t\tuse system-supplied translation routines"),
+#endif /* ! _WINDOWS */
+N_("\t -n[#s] \tMail - notify about new mail every #s seconds, default=180"),
+N_("\t -t \t\tShutdown - enable special shutdown mode"),
+N_("\t -o <dir>\tOperation - specify the operating directory"),
+N_("\t -z \t\tSuspend - allow use of ^Z suspension"),
+#ifdef _WINDOWS
+N_("\t -cnf color \tforeground color"),
+N_("\t -cnb color \tbackground color"),
+N_("\t -crf color \treverse foreground color"),
+N_("\t -crb color \treverse background color"),
+#endif /* _WINDOWS */
+N_("\t -no_setlocale_collate\tdo not do setlocale(LC_COLLATE)"),
+"",
+N_("\t All arguments may be followed by a directory name to start in."),
+"",
+NULL
+};
+
+
+/*
+ * main standalone browser routine
+ */
+int
+main(int argc, char *argv[])
+{
+ char bname[NBUFN]; /* buffer name of file to read */
+ char filename[NSTRING];
+ char filedir[NSTRING];
+ char *dir;
+ char *display_charmap = NULL, *dc;
+ char *keyboard_charmap = NULL;
+ int use_system = 0;
+ char *err = NULL;
+ int setlocale_collate = 1;
+
+ set_input_timeout(0);
+ Pmaster = NULL; /* turn OFF composer functionality */
+ km_popped = 0;
+ opertree[0] = '\0'; opertree[NLINE] = '\0';
+ filename[0] ='\0';
+ gmode |= MDBRONLY; /* turn on exclusive browser mode */
+
+ /*
+ * Read command line flags before initializing, otherwise, we never
+ * know to init for f_keys...
+ */
+ if((dir = pilot_args(argc, argv, &setlocale_collate)) != NULL){
+ strncpy(filedir, dir, sizeof(filedir));
+ filedir[sizeof(filedir)-1] = '\0';
+ fixpath(filedir, sizeof(filedir));
+ }
+ else{
+ strncpy(filedir, gethomedir(NULL), sizeof(filedir));
+ filedir[sizeof(filedir)-1] = '\0';
+ }
+
+ set_collation(setlocale_collate, 1);
+
+#ifdef _WINDOWS
+ init_utf8_display(1, NULL);
+#else /* UNIX */
+
+#define cpstr(s) strcpy((char *)fs_get(1+strlen(s)), s)
+
+ if(display_character_set)
+ display_charmap = cpstr(display_character_set);
+#if HAVE_LANGINFO_H && defined(CODESET)
+ else if((dc = nl_langinfo_codeset_wrapper()) != NULL)
+ display_charmap = cpstr(dc);
+#endif
+
+ if(!display_charmap)
+ display_charmap = cpstr("US-ASCII");
+
+ if(keyboard_character_set)
+ keyboard_charmap = cpstr(keyboard_character_set);
+ else
+ keyboard_charmap = cpstr(display_charmap);
+
+#undef cpstr
+
+ if(use_system_translation){
+#if PREREQ_FOR_SYS_TRANSLATION
+ use_system++;
+ /* This modifies its arguments */
+ if(setup_for_input_output(use_system, &display_charmap, &keyboard_charmap,
+ &input_cs, &err) == -1){
+ fprintf(stderr, "%s\n", err ? err : "trouble with character set");
+ exit(1);
+ }
+ else if(err){
+ fprintf(stderr, "%s\n", err);
+ fs_give((void **) &err);
+ }
+#endif
+ }
+
+ if(!use_system){
+ if(setup_for_input_output(use_system, &display_charmap, &keyboard_charmap,
+ &input_cs, &err) == -1){
+ fprintf(stderr, "%s\n", err ? err : "trouble with character set");
+ exit(1);
+ }
+ else if(err){
+ fprintf(stderr, "%s\n", err);
+ fs_give((void **) &err);
+ }
+ }
+
+ if(keyboard_charmap){
+ set_locale_charmap(keyboard_charmap);
+ free((void *) keyboard_charmap);
+ }
+
+ if(display_charmap)
+ free((void *) display_charmap);
+
+#endif /* UNIX */
+
+ if(!vtinit()) /* Displays. */
+ exit(1);
+
+ strncpy(bname, "main", sizeof(bname)); /* default buffer name */
+ bname[sizeof(bname)-1] = '\0';
+ edinit(bname); /* Buffers, windows. */
+#if defined(USE_TERMCAP) || defined(USE_TERMINFO) || defined(VMS)
+ if(kbesc == NULL){ /* will arrow keys work ? */
+ (*term.t_putchar)('\007');
+ emlwrite("Warning: keypad keys may be non-functional", NULL);
+ }
+#endif /* USE_TERMCAP/USE_TERMINFO/VMS */
+
+ curbp->b_mode |= gmode; /* and set default modes*/
+ if(get_input_timeout()){
+ EML eml;
+
+ eml.s = comatose(get_input_timeout());
+ emlwrite(_("Checking for new mail every %s seconds"), &eml);
+ }
+
+
+ set_browser_title(PILOT_VERSION);
+ FileBrowse(filedir, sizeof(filedir), filename, sizeof(filename), NULL, 0, 0, NULL);
+ wquit(1, 0);
+ exit(0);
+}
+
+
+/*
+ * Parse the command line args.
+ *
+ * Args ac
+ * av
+ *
+ * Result: command arguments parsed
+ * possible printing of help for command line
+ * various global flags set
+ * returns the name of directory to start in if specified, else NULL
+ */
+char *
+pilot_args(int ac, char **av, int *setlocale_collate)
+{
+ int c, usage = 0;
+ char *str;
+ char tmp_1k_buf[1000]; /* tmp buf to contain err msgs */
+
+Loop:
+ /* while more arguments with leading - */
+ while(--ac > 0 && **++av == '-'){
+ /* while more chars in this argument */
+ while(*++*av){
+
+ if(strcmp(*av, "no_setlocale_collate") == 0){
+ *setlocale_collate = 0;
+ goto Loop;
+ }
+#ifndef _WINDOWS
+ else if(strcmp(*av, "syscs") == 0){
+ use_system_translation = !use_system_translation;
+ goto Loop;
+ }
+#endif /* ! _WINDOWS */
+#ifdef _WINDOWS
+ if(strcmp(*av, "cnf") == 0
+ || strcmp(*av, "cnb") == 0
+ || strcmp(*av, "crf") == 0
+ || strcmp(*av, "crb") == 0){
+
+ char *cmd = *av; /* save it to use below */
+
+ if(--ac){
+ str = *++av;
+ if(cmd[1] == 'n'){
+ if(cmd[2] == 'f')
+ pico_nfcolor(str);
+ else if(cmd[2] == 'b')
+ pico_nbcolor(str);
+ }
+ else if(cmd[1] == 'r'){
+ if(cmd[2] == 'f')
+ pico_rfcolor(str);
+ else if(cmd[2] == 'b')
+ pico_rbcolor(str);
+ }
+ }
+ else{
+ snprintf(tmp_1k_buf, sizeof(tmp_1k_buf), _(args_pilot_missing_color), cmd);
+ pilot_display_args_err(tmp_1k_buf, NULL, 1);
+ usage++;
+ }
+
+ goto Loop;
+ }
+#endif /* _WINDOWS */
+#ifndef _WINDOWS
+ else if(strcmp(*av, "dcs") == 0 || strcmp(*av, "kcs") == 0){
+ char *cmd = *av;
+
+ if(--ac){
+ if(strcmp(*av, "dcs") == 0){
+ display_character_set = *++av;
+ if(!output_charset_is_supported(display_character_set)){
+ snprintf(tmp_1k_buf, sizeof(tmp_1k_buf), _(args_pilot_output_charset), display_character_set);
+ pilot_display_args_err(tmp_1k_buf, NULL, 1);
+ usage++;
+ }
+ }
+ else{
+ keyboard_character_set = *++av;
+ if(!input_charset_is_supported(keyboard_character_set)){
+ snprintf(tmp_1k_buf, sizeof(tmp_1k_buf), _(args_pilot_input_charset), keyboard_character_set);
+ pilot_display_args_err(tmp_1k_buf, NULL, 1);
+ usage++;
+ }
+ }
+ }
+ else{
+ snprintf(tmp_1k_buf, sizeof(tmp_1k_buf), _(args_pilot_missing_charset), cmd);
+ pilot_display_args_err(tmp_1k_buf, NULL, 1);
+ usage++;
+ }
+
+ goto Loop;
+ }
+#endif /* ! _WINDOWS */
+
+ /*
+ * Single char options.
+ */
+ switch(c = **av){
+ /*
+ * These don't take arguments.
+ */
+ case 'a':
+ gmode ^= MDDOTSOK; /* show dot files */
+ break;
+ case 'f': /* -f for function key use */
+ gmode ^= MDFKEY;
+ break;
+ case 'j': /* allow "Goto" in file browser */
+ gmode ^= MDGOTO;
+ break;
+ case 'g': /* show-cursor in file browser */
+ gmode ^= MDSHOCUR;
+ break;
+ case 'm': /* turn on mouse support */
+ gmode ^= MDMOUSE;
+ break;
+ case 'v': /* single column display */
+ gmode ^= MDONECOL;
+ break; /* break back to inner-while */
+ case 'x': /* suppress keyhelp */
+ sup_keyhelp = !sup_keyhelp;
+ break;
+ case 'q': /* -q for termcap takes precedence */
+ gmode ^= MDTCAPWINS;
+ break;
+ case 'z': /* -z to suspend */
+ gmode ^= MDSSPD;
+ break;
+ case 'h':
+ usage++;
+ break;
+
+ /*
+ * These do take arguments.
+ */
+ case 'n': /* -n for new mail notification */
+ case 'o' : /* operating tree */
+ if(*++*av)
+ str = *av;
+ else if(--ac)
+ str = *++av;
+ else{
+ snprintf(tmp_1k_buf, sizeof(tmp_1k_buf), _(args_pilot_missing_arg), c);
+ pilot_display_args_err(tmp_1k_buf, NULL, 1);
+ usage++;
+ goto Loop;
+ }
+
+ switch(c){
+ case 'o':
+ strncpy(opertree, str, NLINE);
+ gmode ^= MDTREE;
+ break;
+
+ /* numeric args */
+ case 'n':
+ if(!isdigit((unsigned char)str[0])){
+ snprintf(tmp_1k_buf, sizeof(tmp_1k_buf), _(args_pilot_missing_num), c);
+ pilot_display_args_err(tmp_1k_buf, NULL, 1);
+ usage++;
+ }
+
+ set_input_timeout(180);
+
+ if(set_input_timeout(atoi(str)) < 30)
+ set_input_timeout(180);
+
+ break;
+ }
+
+ goto Loop;
+
+ default: /* huh? */
+ snprintf(tmp_1k_buf, sizeof(tmp_1k_buf), _(args_pilot_missing_flag), c);
+ pilot_display_args_err(tmp_1k_buf, NULL, 1);
+ usage++;
+ break;
+ }
+ }
+ }
+
+ if(usage)
+ pilot_args_help();
+
+ /* return the directory */
+ if(ac > 0)
+ return(*av);
+ else
+ return(NULL);
+}
+
+
+/*----------------------------------------------------------------------
+ print a few lines of help for command line arguments
+
+ Args: none
+
+ Result: prints help messages
+ ----------------------------------------------------------------------*/
+void
+pilot_args_help(void)
+{
+ char **a;
+ char *pp[2];
+
+ pp[1] = NULL;
+
+ /** print out possible starting arguments... **/
+
+ for(a=args_pilot_args; a && *a; a++){
+ pp[0] = _(*a);
+ pilot_display_args_err(NULL, pp, 0);
+ }
+
+ exit(1);
+}
+
+
+/*----------------------------------------------------------------------
+ write argument error to the display...
+
+ Args: none
+
+ Result: prints help messages
+ ----------------------------------------------------------------------*/
+void
+pilot_display_args_err(char *s, char **a, int err)
+{
+ char errstr[256], *errp;
+ FILE *fp = err ? stderr : stdout;
+
+ if(err && s)
+ snprintf(errp = errstr, sizeof(errstr), _("Argument Error: %.200s"), s);
+ else
+ errp = s;
+
+ if(errp)
+ fprintf(fp, "%s\n", errp);
+
+ while(a && *a)
+ fprintf(fp, "%s\n", *a++);
+}
diff --git a/pico/random.c b/pico/random.c
new file mode 100644
index 00000000..780c083e
--- /dev/null
+++ b/pico/random.c
@@ -0,0 +1,427 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: random.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Random routines
+ *
+ * This file contains the command processing functions for a number of random
+ * commands. There is no functional grouping here, for sure.
+ */
+
+#include "headers.h"
+
+#include "osdep/terminal.h"
+
+int worthit(int *);
+
+int tabsize; /* Tab size (0: use real tabs) */
+
+
+/*
+ * Display the current position of the cursor, in origin 1 X-Y coordinates,
+ * the character that is under the cursor (in octal), and the fraction of the
+ * text that is before the cursor. The displayed column is not the current
+ * column, but the column that would be used on an infinite width display.
+ * Normally this is bound to "C-X =".
+ */
+int
+showcpos(int f, int n)
+{
+ register LINE *clp;
+ register long nch;
+ register int cbo;
+ register long nbc;
+ register int lines;
+ register int thisline;
+ char buffer[80];
+
+ clp = lforw(curbp->b_linep); /* Grovel the data. */
+ cbo = 0;
+ nch = 0L;
+ lines = 0;
+ for (;;) {
+ if (clp==curwp->w_dotp && cbo==curwp->w_doto) {
+ thisline = lines;
+ nbc = nch;
+ }
+ if (cbo == llength(clp)) {
+ if (clp == curbp->b_linep)
+ break;
+ clp = lforw(clp);
+ cbo = 0;
+ lines++;
+ } else
+ ++cbo;
+ ++nch;
+ }
+
+ snprintf(buffer,sizeof(buffer),"line %d of %d (%d%%%%), character %ld of %ld (%d%%%%)",
+ thisline+1, lines+1, (int)((100L*(thisline+1))/(lines+1)),
+ nbc, nch, (nch) ? (int)((100L*nbc)/nch) : 0);
+
+ emlwrite(buffer, NULL);
+ return (TRUE);
+}
+
+
+/*
+ * Return current column. Stop at first non-blank given TRUE argument.
+ */
+int
+getccol(int bflg)
+{
+ UCS c;
+ int i, col;
+
+ col = 0;
+ for (i=0; i<curwp->w_doto; ++i) {
+ c = lgetc(curwp->w_dotp, i).c;
+ if (c!=' ' && c!='\t' && bflg)
+ break;
+
+ if (c == '\t'){
+ col |= 0x07;
+ ++col;
+ }
+ else if (ISCONTROL(c)){
+ col += 2;
+ }
+ else{
+ int ww;
+
+ ww = wcellwidth(c);
+ col += (ww >= 0 ? ww : 1);
+ }
+ }
+
+ return(col);
+}
+
+
+
+/*
+ * Set tab size if given non-default argument (n <> 1). Otherwise, insert a
+ * tab into file. If given argument, n, of zero, change to true tabs.
+ * If n > 1, simulate tab stop every n-characters using spaces. This has to be
+ * done in this slightly funny way because the tab (in ASCII) has been turned
+ * into "C-I" (in 10 bit code) already. Bound to "C-I".
+ */
+int
+tab(int f, int n)
+{
+ if (n < 0)
+ return (FALSE);
+
+ if (n == 0 || n > 1) {
+ tabsize = n;
+ return(TRUE);
+ }
+
+ if (! tabsize)
+ return(linsert(1, '\t'));
+
+ return(linsert(tabsize - (getccol(FALSE) % tabsize), ' '));
+}
+
+
+/*
+ * Insert a newline. Bound to "C-M".
+ */
+int
+newline(int f, int n)
+{
+ register int s;
+
+ if (curbp->b_mode&MDVIEW) /* don't allow this command if */
+ return(rdonly()); /* we are in read only mode */
+
+ if (n < 0)
+ return (FALSE);
+
+ if(TERM_OPTIMIZE && (curwp->w_dotp != curwp->w_bufp->b_linep)){
+ int l;
+
+ if(worthit(&l)){
+ if(curwp->w_doto != 0)
+ l++;
+ scrolldown(curwp, l, n);
+ }
+ }
+
+ /* if we are in C mode and this is a default <NL> */
+ /* pico's never in C mode */
+
+ if(Pmaster && Pmaster->allow_flowed_text && curwp->w_doto
+ && ucs4_isspace(lgetc(curwp->w_dotp, curwp->w_doto - 1).c)
+ && !(curwp->w_doto == 3
+ && lgetc(curwp->w_dotp, 0).c == '-'
+ && lgetc(curwp->w_dotp, 1).c == '-'
+ && lgetc(curwp->w_dotp, 2).c == ' ')){
+ /*
+ * flowed mode, make the newline a hard one by
+ * stripping trailing space.
+ */
+ int i, dellen;
+ for(i = curwp->w_doto - 1;
+ i && ucs4_isspace(lgetc(curwp->w_dotp, i - 1).c);
+ i--);
+ dellen = curwp->w_doto - i;
+ curwp->w_doto = i;
+ ldelete(dellen, NULL);
+ }
+ /* insert some lines */
+ while (n--) {
+ if ((s=lnewline()) != TRUE)
+ return (s);
+ }
+ return (TRUE);
+}
+
+
+
+/*
+ * Delete forward. This is real easy, because the basic delete routine does
+ * all of the work. Watches for negative arguments, and does the right thing.
+ * If any argument is present, it kills rather than deletes, to prevent loss
+ * of text if typed with a big argument. Normally bound to "C-D".
+ */
+int
+forwdel(int f, int n)
+{
+ if (curbp->b_mode&MDVIEW) /* don't allow this command if */
+ return(rdonly()); /* we are in read only mode */
+
+ if (n < 0)
+ return (backdel(f, -n));
+
+ if(TERM_OPTIMIZE && (curwp->w_dotp != curwp->w_bufp->b_linep)){
+ int l;
+
+ if(worthit(&l) && curwp->w_doto == llength(curwp->w_dotp))
+ scrollup(curwp, l+1, 1);
+ }
+
+ if (f != FALSE) { /* Really a kill. */
+ if ((lastflag&CFKILL) == 0)
+ kdelete();
+ thisflag |= CFKILL;
+ }
+
+ return (ldelete((long) n, f ? kinsert : NULL));
+}
+
+
+
+/*
+ * Delete backwards. This is quite easy too, because it's all done with other
+ * functions. Just move the cursor back, and delete forwards. Like delete
+ * forward, this actually does a kill if presented with an argument. Bound to
+ * both "RUBOUT" and "C-H".
+ */
+int
+backdel(int f, int n)
+{
+ register int s;
+
+ if (curbp->b_mode&MDVIEW) /* don't allow this command if */
+ return(rdonly()); /* we are in read only mode */
+
+ if (n < 0)
+ return (forwdel(f, -n));
+
+ if(TERM_OPTIMIZE && curwp->w_dotp != curwp->w_bufp->b_linep){
+ int l;
+
+ if(worthit(&l) && curwp->w_doto == 0 &&
+ lback(curwp->w_dotp) != curwp->w_bufp->b_linep){
+ if(l == curwp->w_toprow)
+ scrollup(curwp, l+1, 1);
+ else if(llength(lback(curwp->w_dotp)) == 0)
+ scrollup(curwp, l-1, 1);
+ else
+ scrollup(curwp, l, 1);
+ }
+ }
+
+ if (f != FALSE) { /* Really a kill. */
+ if ((lastflag&CFKILL) == 0)
+ kdelete();
+
+ thisflag |= CFKILL;
+ }
+
+ if ((s=backchar(f, n)) == TRUE)
+ s = ldelete((long) n, f ? kinsert : NULL);
+
+ return (s);
+}
+
+
+
+/*
+ * killtext - delete the line that the cursor is currently in.
+ * a greatly pared down version of its former self.
+ */
+int
+killtext(int f, int n)
+{
+ register int chunk;
+ int opt_scroll = 0;
+
+ if (curbp->b_mode&MDVIEW) /* don't allow this command if */
+ return(rdonly()); /* we are in read only mode */
+
+ if ((lastflag&CFKILL) == 0) /* Clear kill buffer if */
+ kdelete(); /* last wasn't a kill. */
+
+ if(gmode & MDDTKILL){ /* */
+ if((chunk = llength(curwp->w_dotp) - curwp->w_doto) == 0){
+ chunk = 1;
+ if(TERM_OPTIMIZE)
+ opt_scroll = 1;
+ }
+ }
+ else{
+ gotobol(FALSE, 1); /* wack from bol past newline */
+ chunk = llength(curwp->w_dotp) + 1;
+ if(TERM_OPTIMIZE)
+ opt_scroll = 1;
+ }
+
+ /* optimize what motion we can */
+ if(opt_scroll && (curwp->w_dotp != curwp->w_bufp->b_linep)){
+ int l;
+
+ if(worthit(&l))
+ scrollup(curwp, l, 1);
+ }
+
+ thisflag |= CFKILL;
+ return(ldelete((long) chunk, kinsert));
+}
+
+
+/*
+ * Yank text back from the kill buffer. This is really easy. All of the work
+ * is done by the standard insert routines. All you do is run the loop, and
+ * check for errors. Bound to "C-Y".
+ */
+int
+yank(int f, int n)
+{
+ int c, i;
+ REGION region, *added_region;
+ LINE *dotp;
+
+ if (curbp->b_mode&MDVIEW) /* don't allow this command if */
+ return(rdonly()); /* we are in read only mode */
+
+ if (n < 0)
+ return (FALSE);
+
+ if(TERM_OPTIMIZE && (curwp->w_dotp != curwp->w_bufp->b_linep)){
+ int l;
+
+ if(worthit(&l) && !(lastflag&CFFILL)){
+ register int t = 0;
+ register int i = 0;
+ register int ch;
+
+ while((ch=fremove(i++)) >= 0)
+ if(ch == '\n')
+ t++;
+ if(t+l < curwp->w_toprow+curwp->w_ntrows)
+ scrolldown(curwp, l, t);
+ }
+ }
+
+ if(lastflag & CFFILL){ /* if last command was fillpara() */
+ if(lastflag & CFFLBF){
+ gotoeob(FALSE, 1);
+ dotp = curwp->w_dotp;
+ gotobob(FALSE, 1);
+ curwp->w_doto = 0;
+ getregion(&region, dotp, llength(dotp));
+ }
+ else{
+ added_region = get_last_region_added();
+ if(added_region){
+ curwp->w_dotp = added_region->r_linep;
+ curwp->w_doto = added_region->r_offset;
+ region = (*added_region);
+ }
+ else
+ return(FALSE);
+ }
+
+ if(!ldelete(region.r_size, NULL))
+ return(FALSE);
+ } /* then splat out the saved buffer */
+
+ while (n--) {
+ i = 0;
+ while ((c = ((lastflag&CFFILL)
+ ? ((lastflag & CFFLBF) ? kremove(i) : fremove(i))
+ : kremove(i))) >= 0) {
+ if (c == '\n') {
+ if (lnewline() == FALSE)
+ return (FALSE);
+ } else {
+ if (linsert(1, c) == FALSE)
+ return (FALSE);
+ }
+
+ ++i;
+ }
+ }
+
+ if(lastflag&CFFLPA){ /* if last command was fill paragraph */
+ curwp->w_dotp = lforw(curwp->w_dotp);
+ curwp->w_doto = 0;
+
+ curwp->w_flag |= WFMODE;
+
+ if(!Pmaster){
+ sgarbk = TRUE;
+ emlwrite("", NULL);
+ }
+ }
+
+ return (TRUE);
+}
+
+
+
+/*
+ * worthit - generic sort of test to roughly gage usefulness of using
+ * optimized scrolling.
+ *
+ * note:
+ * returns the line on the screen, l, that the dot is currently on
+ */
+int
+worthit(int *l)
+{
+ int i; /* l is current line */
+ unsigned below; /* below is avg # of ch/line under . */
+
+ *l = doton(&i, &below);
+ below = (i > 0) ? below/(unsigned)i : 0;
+
+ return(below > 3);
+}
diff --git a/pico/region.c b/pico/region.c
new file mode 100644
index 00000000..e1e65896
--- /dev/null
+++ b/pico/region.c
@@ -0,0 +1,364 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: region.c 761 2007-10-23 22:35:18Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Region management routines
+ *
+ * The routines in this file
+ * deal with the region, that magic space
+ * between "." and mark. Some functions are
+ * commands. Some functions are just for
+ * internal use.
+ */
+#include "headers.h"
+
+/*
+ * Kill the region. Ask "getregion"
+ * to figure out the bounds of the region.
+ * Move "." to the start, and kill the characters.
+ * Bound to "C-W".
+ */
+int
+killregion(int f, int n)
+{
+ REGION region;
+
+ if (curbp->b_mode&MDVIEW) /* don't allow this command if */
+ return(rdonly()); /* we are in read only mode */
+
+ if (getregion(&region, curwp->w_markp, curwp->w_marko) != TRUE){
+ return (killtext(f, n));
+ }else {
+ mlerase();
+ }
+
+ if ((lastflag&CFKILL) == 0) /* This is a kill type */
+ kdelete(); /* command, so do magic */
+
+ thisflag |= CFKILL; /* kill buffer stuff. */
+ curwp->w_dotp = region.r_linep;
+ curwp->w_doto = region.r_offset;
+ curwp->w_markp = NULL;
+#ifdef _WINDOWS
+ mswin_allowcopycut(NULL);
+#endif
+
+ if(ldelete(region.r_size, kinsert)){
+ if(curwp->w_dotp == curwp->w_linep && curwp->w_dotp == curbp->b_linep){
+ curwp->w_force = 0; /* Center dot. */
+ curwp->w_flag |= WFFORCE;
+ }
+
+ return(TRUE);
+ }
+
+ return (FALSE);
+}
+
+
+/*
+ * Blast the region without saving . Ask "getregion"
+ * to figure out the bounds of the region.
+ * Move "." to the start, and kill the characters.
+ * Bound to "C-W".
+ */
+int
+deleteregion(int f, int n)
+{
+ REGION region;
+
+ if (curbp->b_mode&MDVIEW) /* don't allow this command if */
+ return(rdonly()); /* we are in read only mode */
+
+ if (getregion(&region, curwp->w_markp, curwp->w_marko) == TRUE){
+ curwp->w_dotp = region.r_linep;
+ curwp->w_doto = region.r_offset;
+ curwp->w_markp = NULL;
+#ifdef _WINDOWS
+ mswin_allowcopycut(NULL);
+#endif
+ if(ldelete(region.r_size, NULL)){
+ if(curwp->w_dotp == curwp->w_linep
+ && curwp->w_dotp == curbp->b_linep){
+ curwp->w_force = 0; /* Center dot. */
+ curwp->w_flag |= WFFORCE;
+ }
+
+ return(TRUE);
+ }
+ }
+
+ return (FALSE);
+}
+
+
+/*
+ * Copy all of the characters in the
+ * region to the kill buffer. Don't move dot
+ * at all. This is a bit like a kill region followed
+ * by a yank. Bound to "M-W".
+ */
+int
+copyregion(int f, int n)
+{
+ register LINE *linep;
+ register int loffs;
+ register int s;
+ REGION region;
+
+ if ((s=getregion(&region, curwp->w_markp, curwp->w_marko)) != TRUE)
+ return (s);
+
+ if ((lastflag&CFKILL) == 0) /* Kill type command. */
+ kdelete();
+
+ thisflag |= CFKILL;
+ linep = region.r_linep; /* Current line. */
+ loffs = region.r_offset; /* Current offset. */
+ while (region.r_size--) {
+ if (loffs == llength(linep)) { /* End of line. */
+ if ((s=kinsert('\n')) != TRUE)
+ return (s);
+ linep = lforw(linep);
+ loffs = 0;
+ } else { /* Middle of line. */
+ if ((s=kinsert(lgetc(linep, loffs).c)) != TRUE)
+ return (s);
+ ++loffs;
+ }
+ }
+
+ return (TRUE);
+}
+
+
+/*
+ * Lower case region. Zap all of the upper
+ * case characters in the region to lower case. Use
+ * the region code to set the limits. Scan the buffer,
+ * doing the changes. Call "lchange" to ensure that
+ * redisplay is done in all buffers. Bound to
+ * "C-X C-L".
+ */
+int
+lowerregion(int f, int n)
+{
+ register LINE *linep;
+ register int loffs;
+ register int c;
+ register int s;
+ REGION region;
+ CELL ac;
+
+ ac.a = 0;
+ if (curbp->b_mode&MDVIEW) /* don't allow this command if */
+ return(rdonly()); /* we are in read only mode */
+
+ if ((s=getregion(&region, curwp->w_markp, curwp->w_marko)) != TRUE)
+ return (s);
+
+ lchange(WFHARD);
+ linep = region.r_linep;
+ loffs = region.r_offset;
+ while (region.r_size--) {
+ if (loffs == llength(linep)) {
+ linep = lforw(linep);
+ loffs = 0;
+ } else {
+ c = lgetc(linep, loffs).c;
+ if (c>='A' && c<='Z'){
+ ac.c = c+'a'-'A';
+ lputc(linep, loffs, ac);
+ }
+ ++loffs;
+ }
+ }
+
+ return (TRUE);
+}
+
+/*
+ * Upper case region. Zap all of the lower
+ * case characters in the region to upper case. Use
+ * the region code to set the limits. Scan the buffer,
+ * doing the changes. Call "lchange" to ensure that
+ * redisplay is done in all buffers. Bound to
+ * "C-X C-L".
+ */
+int
+upperregion(int f, int n)
+{
+ register LINE *linep;
+ register int loffs;
+ register int c;
+ register int s;
+ REGION region;
+ CELL ac;
+
+ ac.a = 0;
+ if (curbp->b_mode&MDVIEW) /* don't allow this command if */
+ return(rdonly()); /* we are in read only mode */
+
+ if ((s=getregion(&region, curwp->w_markp, curwp->w_marko)) != TRUE)
+ return (s);
+
+ lchange(WFHARD);
+ linep = region.r_linep;
+ loffs = region.r_offset;
+ while (region.r_size--) {
+ if (loffs == llength(linep)) {
+ linep = lforw(linep);
+ loffs = 0;
+ } else {
+ c = lgetc(linep, loffs).c;
+ if (c>='a' && c<='z'){
+ ac.c = c - 'a' + 'A';
+ lputc(linep, loffs, ac);
+ }
+ ++loffs;
+ }
+ }
+
+ return (TRUE);
+}
+
+/*
+ * This routine figures out the
+ * bounds of the region in the current window, and
+ * fills in the fields of the "REGION" structure pointed
+ * to by "rp". Because the dot and mark are usually very
+ * close together, we scan outward from dot looking for
+ * mark. This should save time. Return a standard code.
+ * Callers of this routine should be prepared to get
+ * an "ABORT" status; we might make this have the
+ * conform thing later.
+ */
+int
+getregion(REGION *rp, LINE *markp, int marko)
+{
+ register LINE *flp;
+ register LINE *blp;
+ long fsize;
+ register long bsize;
+
+ if (markp == NULL) {
+ return (FALSE);
+ }
+
+ if (curwp->w_dotp == markp) {
+ rp->r_linep = curwp->w_dotp;
+ if (curwp->w_doto < marko) {
+ rp->r_offset = curwp->w_doto;
+ rp->r_size = marko - curwp->w_doto;
+ } else {
+ rp->r_offset = marko;
+ rp->r_size = curwp->w_doto - marko;
+ }
+ return (TRUE);
+ }
+
+ blp = curwp->w_dotp;
+ bsize = curwp->w_doto;
+ flp = curwp->w_dotp;
+ fsize = llength(flp)-curwp->w_doto+1;
+ while (flp!=curbp->b_linep || lback(blp)!=curbp->b_linep) {
+ if (flp != curbp->b_linep) {
+ flp = lforw(flp);
+ if (flp == markp) {
+ rp->r_linep = curwp->w_dotp;
+ rp->r_offset = curwp->w_doto;
+ rp->r_size = fsize + marko;
+ return (TRUE);
+ }
+
+ fsize += llength(flp) + 1;
+ }
+
+ if (lback(blp) != curbp->b_linep) {
+ blp = lback(blp);
+ bsize += llength(blp)+1;
+ if (blp == markp) {
+ rp->r_linep = blp;
+ rp->r_offset = marko;
+ rp->r_size = bsize - marko;
+ return (TRUE);
+ }
+ }
+ }
+
+ emlwrite("Bug: lost mark", NULL);
+ return (FALSE);
+}
+
+
+/*
+ * set the highlight attribute accordingly on all characters in region
+ */
+int
+markregion(int attr)
+{
+ register LINE *linep;
+ register int loffs;
+ register int s;
+ REGION region;
+ CELL ac;
+
+ if ((s=getregion(&region, curwp->w_markp, curwp->w_marko)) != TRUE)
+ return (s);
+
+ lchange(WFHARD);
+ linep = region.r_linep;
+ loffs = region.r_offset;
+ while (region.r_size--) {
+ if (loffs == llength(linep)) {
+ linep = lforw(linep);
+ loffs = 0;
+ } else {
+ ac = lgetc(linep, loffs);
+ ac.a = attr;
+ lputc(linep, loffs, ac);
+ ++loffs;
+ }
+ }
+
+ return (TRUE);
+}
+
+
+/*
+ * clear all the attributes of all the characters in the buffer?
+ * this is real dumb. Movement with mark set needs to be smarter!
+ */
+void
+unmarkbuffer(void)
+{
+ register LINE *linep;
+ register int n;
+ CELL c;
+
+ linep = curwp->w_linep;
+ while(lforw(linep) != curwp->w_linep){
+ n = llength(linep);
+ for(n=0; n < llength(linep); n++){
+ c = lgetc(linep, n);
+ c.a = 0;
+ lputc(linep, n, c);
+ }
+
+ linep = lforw(linep);
+ }
+}
diff --git a/pico/search.c b/pico/search.c
new file mode 100644
index 00000000..0886b24c
--- /dev/null
+++ b/pico/search.c
@@ -0,0 +1,1037 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: search.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Searching routines
+ *
+ * The functions in this file implement commands that search in the forward
+ * and backward directions. There are no special characters in the search
+ * strings. Probably should have a regular expression search, or something
+ * like that.
+ *
+ */
+
+#include "headers.h"
+
+int eq(UCS, UCS);
+int expandp(UCS *, UCS *, int);
+int readnumpat(char *);
+void get_pat_cases(UCS *, UCS *);
+int srpat(char *, UCS *, size_t, int);
+int readpattern(char *, int);
+int replace_pat(UCS *, int *);
+int replace_all(UCS *, UCS *);
+
+
+#define FWS_RETURN(RV) { \
+ thisflag |= CFSRCH; \
+ curwp->w_flag |= WFMODE; \
+ sgarbk = TRUE; \
+ return(RV); \
+ }
+
+
+/*
+ * Search forward. Get a search string from the user, and search, beginning at
+ * ".", for the string. If found, reset the "." to be just after the match
+ * string, and [perhaps] repaint the display. Bound to "C-S".
+ */
+
+/* string search input parameters */
+
+#define PTBEG 1 /* leave the point at the begining on search */
+#define PTEND 2 /* leave the point at the end on search */
+
+#define NPMT (2*NLINE+32)
+
+
+static char *SearchHelpText[] = {
+/* TRANSLATORS: Some help text that goes together in a group. */
+N_("Help for Search Command"),
+" ",
+N_(" Enter the words or characters you would like to search"),
+N_("~ for, then press ~R~e~t~u~r~n. The search then takes place."),
+N_(" When the characters or words that you entered "),
+N_(" are found, the buffer will be redisplayed with the cursor "),
+N_(" at the beginning of the selected text."),
+" ",
+N_(" The most recent string for which a search was made is"),
+N_(" displayed in the \"Search\" prompt between the square"),
+N_(" brackets. This string is the default search prompt."),
+N_("~ Hitting only ~R~e~t~u~r~n or at the prompt will cause the"),
+N_(" search to be made with the default value."),
+" ",
+N_(" The text search is not case sensitive, and will examine the"),
+N_(" entire message."),
+" ",
+N_(" Should the search fail, a message will be displayed."),
+" ",
+N_("End of Search Help."),
+" ",
+NULL
+};
+
+
+/*
+ * Compare two characters. The "bc" comes from the buffer. It has it's case
+ * folded out. The "pc" is from the pattern.
+ */
+int
+eq(UCS bc, UCS pc)
+{
+ if ((curwp->w_bufp->b_mode & MDEXACT) == 0){
+ if (bc>='a' && bc<='z')
+ bc -= 0x20;
+
+ if (pc>='a' && pc<='z')
+ pc -= 0x20;
+ }
+
+ return(bc == pc);
+}
+
+
+int
+forwsearch(int f, int n)
+{
+ int status;
+ int wrapt = FALSE, wrapt2 = FALSE;
+ int repl_mode = FALSE;
+ UCS defpat[NPAT];
+ int search = FALSE;
+ EML eml;
+
+ /* resolve the repeat count */
+ if (n == 0)
+ n = 1;
+
+ if (n < 1) /* search backwards */
+ FWS_RETURN(0);
+
+ defpat[0] = '\0';
+
+ /* ask the user for the text of a pattern */
+ while(1){
+
+ if (gmode & MDREPLACE)
+ status = srpat("Search", defpat, NPAT, repl_mode);
+ else
+ status = readpattern("Search", TRUE);
+
+ switch(status){
+ case TRUE: /* user typed something */
+ search = TRUE;
+ break;
+
+ case HELPCH: /* help requested */
+ if(Pmaster){
+ VARS_TO_SAVE *saved_state;
+
+ saved_state = save_pico_state();
+ (*Pmaster->helper)(Pmaster->search_help,
+ _("Help for Searching"), 1);
+ if(saved_state){
+ restore_pico_state(saved_state);
+ free_pico_state(saved_state);
+ }
+ }
+ else
+ pico_help(SearchHelpText, _("Help for Searching"), 1);
+
+ case (CTRL|'L'): /* redraw requested */
+ pico_refresh(FALSE, 1);
+ update();
+ break;
+
+ case (CTRL|'V'):
+ gotoeob(0, 1);
+ mlerase();
+ FWS_RETURN(TRUE);
+
+ case (CTRL|'Y'):
+ gotobob(0, 1);
+ mlerase();
+ FWS_RETURN(TRUE);
+
+ case (CTRL|'T') :
+ switch(status = readnumpat(_("Search to Line Number : "))){
+ case -1 :
+ emlwrite(_("Search to Line Number Cancelled"), NULL);
+ FWS_RETURN(FALSE);
+
+ case 0 :
+ emlwrite(_("Line number must be greater than zero"), NULL);
+ FWS_RETURN(FALSE);
+
+ case -2 :
+ emlwrite(_("Line number must contain only digits"), NULL);
+ FWS_RETURN(FALSE);
+
+ case -3 :
+ continue;
+
+ default :
+ gotoline(0, status);
+ mlerase();
+ FWS_RETURN(TRUE);
+ }
+
+ break;
+
+ case (CTRL|'W'):
+ {
+ LINE *linep = curwp->w_dotp;
+ int offset = curwp->w_doto;
+
+ gotobop(0, 1);
+ gotobol(0, 1);
+
+ /*
+ * if we're asked to backup and we're already
+ *
+ */
+ if((lastflag & CFSRCH)
+ && linep == curwp->w_dotp
+ && offset == curwp->w_doto
+ && !(offset == 0 && lback(linep) == curbp->b_linep)){
+ backchar(0, 1);
+ gotobop(0, 1);
+ gotobol(0, 1);
+ }
+ }
+
+ mlerase();
+ FWS_RETURN(TRUE);
+
+ case (CTRL|'O'):
+ if(curwp->w_dotp != curbp->b_linep){
+ gotoeop(0, 1);
+ forwchar(0, 1);
+ }
+
+ mlerase();
+ FWS_RETURN(TRUE);
+
+ case (CTRL|'U'):
+ fillbuf(0, 1);
+ mlerase();
+ FWS_RETURN(TRUE);
+
+ case (CTRL|'R'): /* toggle replacement option */
+ repl_mode = !repl_mode;
+ break;
+
+ default:
+ if(status == ABORT)
+ emlwrite(_("Search Cancelled"), NULL);
+ else
+ mlerase();
+
+ FWS_RETURN(FALSE);
+ }
+
+ /* replace option is disabled */
+ if (!(gmode & MDREPLACE)){
+ ucs4_strncpy(defpat, pat, NPAT);
+ defpat[NPAT-1] = '\0';
+ break;
+ }
+ else if (search){ /* search now */
+ ucs4_strncpy(pat, defpat, NPAT); /* remember this search for the future */
+ pat[NPAT-1] = '\0';
+ break;
+ }
+ }
+
+ /*
+ * This code is kind of dumb. What I want is successive C-W 's to
+ * move dot to successive occurences of the pattern. So, if dot is
+ * already sitting at the beginning of the pattern, then we'll move
+ * forward a char before beginning the search. We'll let the
+ * automatic wrapping handle putting the dot back in the right
+ * place...
+ */
+ status = 0; /* using "status" as int temporarily! */
+ while(1){
+ if(defpat[status] == '\0'){
+ forwchar(0, 1);
+ break; /* find next occurence! */
+ }
+
+ if(status + curwp->w_doto >= llength(curwp->w_dotp) ||
+ !eq(defpat[status],lgetc(curwp->w_dotp, curwp->w_doto + status).c))
+ break; /* do nothing! */
+ status++;
+ }
+
+ /* search for the pattern */
+
+ while (n-- > 0) {
+ if((status = forscan(&wrapt,defpat,NULL,0,PTBEG)) == FALSE)
+ break;
+ }
+
+ /* and complain if not there */
+ if (status == FALSE){
+ char *utf8;
+ UCS x[1];
+
+ x[0] = '\0';
+
+ utf8 = ucs4_to_utf8_cpystr(defpat ? defpat : x);
+ /* TRANSLATORS: reporting the result of a failed search */
+ eml.s = utf8;
+ emlwrite(_("\"%s\" not found"), &eml);
+ if(utf8)
+ fs_give((void **) &utf8);
+ }
+ else if((gmode & MDREPLACE) && repl_mode == TRUE){
+ status = replace_pat(defpat, &wrapt2); /* replace pattern */
+ if (wrapt == TRUE || wrapt2 == TRUE){
+ eml.s = (status == ABORT) ? "cancelled but wrapped" : "Wrapped";
+ emlwrite("Replacement %s", &eml);
+ }
+ }
+ else if(wrapt == TRUE){
+ emlwrite("Search Wrapped", NULL);
+ }
+ else if(status == TRUE){
+ emlwrite("", NULL);
+ }
+
+ FWS_RETURN(status);
+}
+
+
+/* Replace a pattern with the pattern the user types in one or more times. */
+int
+replace_pat(UCS *defpat, int *wrapt)
+{
+ register int status;
+ UCS lpat[NPAT], origpat[NPAT]; /* case sensitive pattern */
+ EXTRAKEYS menu_pat[2];
+ int repl_all = FALSE;
+ UCS *b;
+ char utf8tmp[NPMT];
+ UCS prompt[NPMT];
+ UCS *promptp;
+
+ forscan(wrapt, defpat, NULL, 0, PTBEG); /* go to word to be replaced */
+
+ lpat[0] = '\0';
+
+ /* additional 'replace all' menu option */
+ menu_pat[0].name = "^X";
+ menu_pat[0].key = (CTRL|'X');
+ menu_pat[0].label = N_("Repl All");
+ KS_OSDATASET(&menu_pat[0], KS_NONE);
+ menu_pat[1].name = NULL;
+
+ while(1) {
+
+ update();
+ (*term.t_rev)(1);
+ get_pat_cases(origpat, defpat);
+ pputs(origpat, 1); /* highlight word */
+ (*term.t_rev)(0);
+
+ snprintf(utf8tmp, NPMT, "Replace%s \"", repl_all ? " every" : "");
+ b = utf8_to_ucs4_cpystr(utf8tmp);
+ if(b){
+ ucs4_strncpy(prompt, b, NPMT);
+ prompt[NPMT-1] = '\0';
+ fs_give((void **) &b);
+ }
+
+ promptp = &prompt[ucs4_strlen(prompt)];
+
+ expandp(defpat, promptp, NPMT-(promptp-prompt));
+ prompt[NPMT-1] = '\0';
+ promptp += ucs4_strlen(promptp);
+
+ b = utf8_to_ucs4_cpystr("\" with");
+ if(b){
+ ucs4_strncpy(promptp, b, NPMT-(promptp-prompt));
+ promptp += ucs4_strlen(promptp);
+ prompt[NPMT-1] = '\0';
+ fs_give((void **) &b);
+ }
+
+ if(rpat[0] != '\0'){
+ if((promptp-prompt) < NPMT-2){
+ *promptp++ = ' ';
+ *promptp++ = '[';
+ *promptp = '\0';
+ }
+
+ expandp(rpat, promptp, NPMT-(promptp-prompt));
+ prompt[NPMT-1] = '\0';
+ promptp += ucs4_strlen(promptp);
+
+ if((promptp-prompt) < NPMT-1){
+ *promptp++ = ']';
+ *promptp = '\0';
+ }
+ }
+
+ if((promptp-prompt) < NPMT-3){
+ *promptp++ = ' ';
+ *promptp++ = ':';
+ *promptp++ = ' ';
+ *promptp = '\0';
+ }
+
+ prompt[NPMT-1] = '\0';
+
+ status = mlreplyd(prompt, lpat, NPAT, QDEFLT, menu_pat);
+
+ curwp->w_flag |= WFMOVE;
+
+ switch(status){
+
+ case TRUE :
+ case FALSE :
+ if(lpat[0]){
+ ucs4_strncpy(rpat, lpat, NPAT); /* remember default */
+ rpat[NPAT-1] = '\0';
+ }
+ else{
+ ucs4_strncpy(lpat, rpat, NPAT); /* use default */
+ lpat[NPAT-1] = '\0';
+ }
+
+ if (repl_all){
+ status = replace_all(defpat, lpat);
+ }
+ else{
+ chword(defpat, lpat); /* replace word */
+ update();
+ status = TRUE;
+ }
+
+ if(status == TRUE)
+ emlwrite("", NULL);
+
+ return(status);
+
+ case HELPCH: /* help requested */
+ if(Pmaster){
+ VARS_TO_SAVE *saved_state;
+
+ saved_state = save_pico_state();
+ (*Pmaster->helper)(Pmaster->search_help,
+ _("Help for Searching"), 1);
+ if(saved_state){
+ restore_pico_state(saved_state);
+ free_pico_state(saved_state);
+ }
+ }
+ else
+ pico_help(SearchHelpText, _("Help for Searching"), 1);
+
+ case (CTRL|'L'): /* redraw requested */
+ pico_refresh(FALSE, 1);
+ update();
+ break;
+
+ case (CTRL|'X'): /* toggle replace all option */
+ if (repl_all){
+ repl_all = FALSE;
+ /* TRANSLATORS: abbreviation for Replace All occurences */
+ menu_pat[0].label = N_("Repl All");
+ }
+ else{
+ repl_all = TRUE;
+ /* TRANSLATORS: Replace just one occurence */
+ menu_pat[0].label = N_("Repl One");
+ }
+
+ break;
+
+ default:
+ if(status == ABORT){
+ emlwrite(_("Replacement Cancelled"), NULL);
+ pico_refresh(FALSE, 1);
+ }
+ else{
+ mlerase();
+ chword(defpat, origpat);
+ }
+
+ update();
+ return(FALSE);
+ }
+ }
+}
+
+
+/* Since the search is not case sensitive, we must obtain the actual pattern
+ that appears in the text, so that we can highlight (and unhighlight) it
+ without using the wrong cases */
+void
+get_pat_cases(UCS *realpat, UCS *searchpat)
+{
+ int i, searchpatlen, curoff;
+
+ curoff = curwp->w_doto;
+ searchpatlen = ucs4_strlen(searchpat);
+
+ for (i = 0; i < searchpatlen; i++)
+ realpat[i] = lgetc(curwp->w_dotp, curoff++).c;
+
+ realpat[searchpatlen] = '\0';
+}
+
+
+/* Ask the user about every occurence of orig pattern and replace it with a
+ repl pattern if the response is affirmative. */
+int
+replace_all(UCS *orig, UCS *repl)
+{
+ register int status = 0;
+ UCS *b;
+ UCS realpat[NPAT];
+ char utf8tmp[NPMT];
+ UCS *promptp;
+ UCS prompt[NPMT];
+ int wrapt, n = 0;
+ LINE *stop_line = curwp->w_dotp;
+ int stop_offset = curwp->w_doto;
+ EML eml;
+
+ while (1)
+ if (forscan(&wrapt, orig, stop_line, stop_offset, PTBEG)){
+ curwp->w_flag |= WFMOVE; /* put cursor back */
+
+ update();
+ (*term.t_rev)(1);
+ get_pat_cases(realpat, orig);
+ pputs(realpat, 1); /* highlight word */
+ (*term.t_rev)(0);
+ fflush(stdout);
+
+ snprintf(utf8tmp, NPMT, "Replace \"");
+ b = utf8_to_ucs4_cpystr(utf8tmp);
+ if(b){
+ ucs4_strncpy(prompt, b, NPMT);
+ prompt[NPMT-1] = '\0';
+ fs_give((void **) &b);
+ }
+
+ promptp = &prompt[ucs4_strlen(prompt)];
+
+ expandp(orig, promptp, NPMT-(promptp-prompt));
+ prompt[NPMT-1] = '\0';
+ promptp += ucs4_strlen(promptp);
+
+ b = utf8_to_ucs4_cpystr("\" with \"");
+ if(b){
+ ucs4_strncpy(promptp, b, NPMT-(promptp-prompt));
+ promptp += ucs4_strlen(promptp);
+ prompt[NPMT-1] = '\0';
+ fs_give((void **) &b);
+ }
+
+ expandp(repl, promptp, NPMT-(promptp-prompt));
+ prompt[NPMT-1] = '\0';
+ promptp += ucs4_strlen(promptp);
+
+ if((promptp-prompt) < NPMT-1){
+ *promptp++ = '\"';
+ *promptp = '\0';
+ }
+
+ prompt[NPMT-1] = '\0';
+
+ status = mlyesno(prompt, TRUE); /* ask user */
+
+ if (status == TRUE){
+ n++;
+ chword(realpat, repl); /* replace word */
+ update();
+ }else{
+ chword(realpat, realpat); /* replace word by itself */
+ update();
+ if(status == ABORT){ /* if cancelled return */
+ eml.s = comatose(n);
+ emlwrite("Replace All cancelled after %s changes", &eml);
+ return (ABORT); /* ... else keep looking */
+ }
+ }
+ }
+ else{
+ char *utf8;
+
+ utf8 = ucs4_to_utf8_cpystr(orig);
+ if(utf8){
+ eml.s = utf8;
+ emlwrite(_("No more matches for \"%s\""), &eml);
+ fs_give((void **) &utf8);
+ }
+ else
+ emlwrite(_("No more matches"), NULL);
+
+ return (FALSE);
+ }
+}
+
+
+/* Read a replacement pattern. Modeled after readpattern(). */
+int
+srpat(char *utf8prompt, UCS *defpat, size_t defpatlen, int repl_mode)
+{
+ register int s;
+ int i = 0;
+ UCS *b;
+ UCS prompt[NPMT];
+ UCS *promptp;
+ EXTRAKEYS menu_pat[8];
+
+ menu_pat[i = 0].name = "^Y";
+ menu_pat[i].label = N_("FirstLine");
+ menu_pat[i].key = (CTRL|'Y');
+ KS_OSDATASET(&menu_pat[i], KS_NONE);
+
+ menu_pat[++i].name = "^V";
+ menu_pat[i].label = N_("LastLine");
+ menu_pat[i].key = (CTRL|'V');
+ KS_OSDATASET(&menu_pat[i], KS_NONE);
+
+ menu_pat[++i].name = "^R";
+ menu_pat[i].label = repl_mode ? N_("No Replace") : N_("Replace");
+ menu_pat[i].key = (CTRL|'R');
+ KS_OSDATASET(&menu_pat[i], KS_NONE);
+
+ if(!repl_mode){
+ menu_pat[++i].name = "^T";
+ menu_pat[i].label = N_("LineNumber");
+ menu_pat[i].key = (CTRL|'T');
+ KS_OSDATASET(&menu_pat[i], KS_NONE);
+
+ menu_pat[++i].name = "^W";
+ /* TRANSLATORS: Start of paragraph */
+ menu_pat[i].label = N_("Start of Para");
+ menu_pat[i].key = (CTRL|'W');
+ KS_OSDATASET(&menu_pat[i], KS_NONE);
+
+ menu_pat[++i].name = "^O";
+ menu_pat[i].label = N_("End of Para");
+ menu_pat[i].key = (CTRL|'O');
+ KS_OSDATASET(&menu_pat[i], KS_NONE);
+
+ menu_pat[++i].name = "^U";
+ /* TRANSLATORS: Instead of justifying (formatting) just a
+ single paragraph, Full Justify justifies the entire
+ message. */
+ menu_pat[i].label = N_("FullJustify");
+ menu_pat[i].key = (CTRL|'U');
+ KS_OSDATASET(&menu_pat[i], KS_NONE);
+ }
+
+ menu_pat[++i].name = NULL;
+
+ b = utf8_to_ucs4_cpystr(utf8prompt);
+ if(b){
+ ucs4_strncpy(prompt, b, NPMT);
+ prompt[NPMT-1] = '\0';
+ fs_give((void **) &b);
+ }
+
+ promptp = &prompt[ucs4_strlen(prompt)];
+
+ if(repl_mode){
+ b = utf8_to_ucs4_cpystr(" (to replace)");
+ if(b){
+ ucs4_strncpy(promptp, b, NPMT-(promptp-prompt));
+ promptp += ucs4_strlen(promptp);
+ prompt[NPMT-1] = '\0';
+ fs_give((void **) &b);
+ }
+ }
+
+ if(pat[0] != '\0'){
+ if((promptp-prompt) < NPMT-2){
+ *promptp++ = ' ';
+ *promptp++ = '[';
+ *promptp = '\0';
+ }
+
+ expandp(pat, promptp, NPMT-(promptp-prompt));
+ prompt[NPMT-1] = '\0';
+ promptp += ucs4_strlen(promptp);
+
+ if((promptp-prompt) < NPMT-1){
+ *promptp++ = ']';
+ *promptp = '\0';
+ }
+ }
+
+ if((promptp-prompt) < NPMT-2){
+ *promptp++ = ':';
+ *promptp++ = ' ';
+ *promptp = '\0';
+ }
+
+ prompt[NPMT-1] = '\0';
+
+ s = mlreplyd(prompt, defpat, defpatlen, QDEFLT, menu_pat);
+
+ if (s == TRUE || s == FALSE){ /* changed or not, they're done */
+ if(!defpat[0]){ /* use default */
+ ucs4_strncpy(defpat, pat, defpatlen);
+ defpat[defpatlen-1] = '\0';
+ }
+ else if(ucs4_strcmp(pat, defpat)){ /* Specified */
+ ucs4_strncpy(pat, defpat, NPAT);
+ pat[NPAT-1] = '\0';
+ rpat[0] = '\0';
+ }
+
+ s = TRUE; /* let caller know to proceed */
+ }
+
+ return(s);
+}
+
+
+/*
+ * Read a pattern. Stash it in the external variable "pat". The "pat" is not
+ * updated if the user types in an empty line. If the user typed an empty line,
+ * and there is no old pattern, it is an error. Display the old pattern, in the
+ * style of Jeff Lomicka. There is some do-it-yourself control expansion.
+ * change to using <ESC> to delemit the end-of-pattern to allow <NL>s in
+ * the search string.
+ */
+
+int
+readnumpat(char *utf8prompt)
+{
+ int i, n;
+ char numpat[NPMT];
+ EXTRAKEYS menu_pat[2];
+
+ menu_pat[i = 0].name = "^T";
+ menu_pat[i].label = N_("No Line Number");
+ menu_pat[i].key = (CTRL|'T');
+ KS_OSDATASET(&menu_pat[i++], KS_NONE);
+
+ menu_pat[i].name = NULL;
+
+ numpat[0] = '\0';
+ while(1)
+ switch(mlreplyd_utf8(utf8prompt, numpat, NPMT, QNORML, menu_pat)){
+ case TRUE :
+ if(*numpat){
+ for(i = n = 0; numpat[i]; i++)
+ if(strchr("0123456789", numpat[i])){
+ n = (n * 10) + (numpat[i] - '0');
+ }
+ else
+ return(-2);
+
+ return(n);
+ }
+
+ case FALSE :
+ default :
+ return(-1);
+
+ case (CTRL|'T') :
+ return(-3);
+
+ case (CTRL|'L') :
+ case HELPCH :
+ break;
+ }
+}
+
+
+int
+readpattern(char *utf8prompt, int text_mode)
+{
+ register int s;
+ int i;
+ UCS *b;
+ UCS tpat[NPAT+20];
+ UCS *tpatp;
+ EXTRAKEYS menu_pat[7];
+
+ menu_pat[i = 0].name = "^Y";
+ menu_pat[i].label = N_("FirstLine");
+ menu_pat[i].key = (CTRL|'Y');
+ KS_OSDATASET(&menu_pat[i], KS_NONE);
+
+ menu_pat[++i].name = "^V";
+ menu_pat[i].label = N_("LastLine");
+ menu_pat[i].key = (CTRL|'V');
+ KS_OSDATASET(&menu_pat[i], KS_NONE);
+
+ if(text_mode){
+ menu_pat[++i].name = "^T";
+ menu_pat[i].label = N_("LineNumber");
+ menu_pat[i].key = (CTRL|'T');
+ KS_OSDATASET(&menu_pat[i], KS_NONE);
+
+ menu_pat[++i].name = "^W";
+ menu_pat[i].label = N_("Start of Para");
+ menu_pat[i].key = (CTRL|'W');
+ KS_OSDATASET(&menu_pat[i], KS_NONE);
+
+ menu_pat[++i].name = "^O";
+ menu_pat[i].label = N_("End of Para");
+ menu_pat[i].key = (CTRL|'O');
+ KS_OSDATASET(&menu_pat[i], KS_NONE);
+
+ menu_pat[++i].name = "^U";
+ menu_pat[i].label = N_("FullJustify");
+ menu_pat[i].key = (CTRL|'U');
+ KS_OSDATASET(&menu_pat[i], KS_NONE);
+ }
+
+ menu_pat[++i].name = NULL;
+
+ b = utf8_to_ucs4_cpystr(utf8prompt);
+ if(b){
+ ucs4_strncpy(tpat, b, NPAT+20);
+ tpat[NPAT+20-1] = '\0';
+ fs_give((void **) &b);
+ }
+
+ tpatp = &tpat[ucs4_strlen(tpat)];
+
+ if(pat[0] != '\0'){
+ if((tpatp-tpat) < NPAT+20-2){
+ *tpatp++ = ' ';
+ *tpatp++ = '[';
+ *tpatp = '\0';
+ }
+
+ expandp(pat, tpatp, NPAT+20-(tpatp-tpat));
+ tpat[NPAT+20-1] = '\0';
+ tpatp += ucs4_strlen(tpatp);
+
+ if((tpatp-tpat) < NPAT+20-1){
+ *tpatp++ = ']';
+ *tpatp = '\0';
+ }
+ }
+
+ if((tpatp-tpat) < NPAT+20-3){
+ *tpatp++ = ' ';
+ *tpatp++ = ':';
+ *tpatp++ = ' ';
+ *tpatp = '\0';
+ }
+
+ tpat[NPAT+20-1] = '\0';
+
+ s = mlreplyd(tpat, tpat, NPAT, QNORML, menu_pat);
+
+ if ((s == TRUE) && ucs4_strcmp(pat,tpat)){ /* Specified */
+ ucs4_strncpy(pat, tpat, NPAT);
+ pat[NPAT-1] = '\0';
+ rpat[0] = '\0';
+ }
+ else if (s == FALSE && pat[0] != '\0') /* CR, but old one */
+ s = TRUE;
+
+ return(s);
+}
+
+
+/* search forward for a <patrn> */
+int
+forscan(int *wrapt, /* boolean indicating search wrapped */
+ UCS *patrn, /* string to scan for */
+ LINE *limitp, /* stop searching if reached */
+ int limito, /* stop searching if reached */
+ int leavep) /* place to leave point
+ PTBEG = begining of match
+ PTEND = at end of match */
+
+{
+ LINE *curline; /* current line during scan */
+ int curoff; /* position within current line */
+ LINE *lastline; /* last line position during scan */
+ int lastoff; /* position within last line */
+ UCS c; /* character at current position */
+ LINE *matchline; /* current line during matching */
+ int matchoff; /* position in matching line */
+ UCS *patptr; /* pointer into pattern */
+ int stopoff; /* offset to stop search */
+ LINE *stopline; /* line to stop search */
+
+ *wrapt = FALSE;
+
+ /*
+ * the idea is to set the character to end the search at the
+ * next character in the buffer. thus, let the search wrap
+ * completely around the buffer.
+ *
+ * first, test to see if we are at the end of the line,
+ * otherwise start searching on the next character.
+ */
+ if(curwp->w_doto == llength(curwp->w_dotp)){
+ /*
+ * dot is not on end of a line
+ * start at 0 offset of the next line
+ */
+ stopoff = curoff = 0;
+ stopline = curline = lforw(curwp->w_dotp);
+ if (curwp->w_dotp == curbp->b_linep)
+ *wrapt = TRUE;
+ }
+ else{
+ stopoff = curoff = curwp->w_doto;
+ stopline = curline = curwp->w_dotp;
+ }
+
+ /* scan each character until we hit the head link record */
+
+ /*
+ * maybe wrapping is a good idea
+ */
+ while (curline){
+
+ if (curline == curbp->b_linep)
+ *wrapt = TRUE;
+
+ /* save the current position in case we need to
+ restore it on a match */
+
+ lastline = curline;
+ lastoff = curoff;
+
+ /* get the current character resolving EOLs */
+ if (curoff == llength(curline)) { /* if at EOL */
+ curline = lforw(curline); /* skip to next line */
+ curoff = 0;
+ c = '\n'; /* and return a <NL> */
+ }
+ else
+ c = lgetc(curline, curoff++).c; /* get the char */
+
+ /* test it against first char in pattern */
+ if (eq(c, patrn[0]) != FALSE) { /* if we find it..*/
+ /* setup match pointers */
+ matchline = curline;
+ matchoff = curoff;
+ patptr = &patrn[0];
+
+ /* scan through patrn for a match */
+ while (*++patptr != '\0') {
+ /* advance all the pointers */
+ if (matchoff == llength(matchline)) {
+ /* advance past EOL */
+ matchline = lforw(matchline);
+ matchoff = 0;
+ c = '\n';
+ } else
+ c = lgetc(matchline, matchoff++).c;
+
+ if(matchline == limitp && matchoff == limito)
+ return(FALSE);
+
+ /* and test it against the pattern */
+ if (eq(*patptr, c) == FALSE)
+ goto fail;
+ }
+
+ /* A SUCCESSFULL MATCH!!! */
+ /* reset the global "." pointers */
+ if (leavep == PTEND) { /* at end of string */
+ curwp->w_dotp = matchline;
+ curwp->w_doto = matchoff;
+ }
+ else { /* at begining of string */
+ curwp->w_dotp = lastline;
+ curwp->w_doto = lastoff;
+ }
+
+ curwp->w_flag |= WFMOVE; /* flag that we have moved */
+ return(TRUE);
+
+ }
+
+fail:; /* continue to search */
+ if(((curline == stopline) && (curoff == stopoff))
+ || (curline == limitp && curoff == limito))
+ break; /* searched everywhere... */
+ }
+ /* we could not find a match */
+
+ return(FALSE);
+}
+
+
+
+/* expandp: expand control key sequences for output */
+int
+expandp(UCS *srcstr, /* string to expand */
+ UCS *deststr, /* destination of expanded string */
+ int maxlength) /* maximum chars in destination */
+{
+ UCS c; /* current char to translate */
+
+ /* scan through the string */
+ while ((c = *srcstr++) != 0) {
+ if (c == '\n') { /* its an EOL */
+ *deststr++ = '<';
+ *deststr++ = 'N';
+ *deststr++ = 'L';
+ *deststr++ = '>';
+ maxlength -= 4;
+ } else if (c < 0x20 || c == 0x7f) { /* control character */
+ *deststr++ = '^';
+ *deststr++ = c ^ 0x40;
+ maxlength -= 2;
+ } else if (c == '%') {
+ *deststr++ = '%';
+ *deststr++ = '%';
+ maxlength -= 2;
+ } else { /* any other character */
+ *deststr++ = c;
+ maxlength--;
+ }
+
+ /* check for maxlength */
+ if (maxlength < 4) {
+ *deststr++ = '$';
+ *deststr = '\0';
+ return(FALSE);
+ }
+ }
+
+ *deststr = '\0';
+ return(TRUE);
+}
+
+
+/*
+ * chword() - change the given word, wp, pointed to by the curwp->w_dot
+ * pointers to the word in cb
+ */
+void
+chword(UCS *wb, UCS *cb)
+{
+ ldelete((long) ucs4_strlen(wb), NULL); /* not saved in kill buffer */
+ while(*cb != '\0')
+ linsert(1, *cb++);
+
+ curwp->w_flag |= WFEDIT;
+}
diff --git a/pico/utf8stub.c b/pico/utf8stub.c
new file mode 100644
index 00000000..d8738c9a
--- /dev/null
+++ b/pico/utf8stub.c
@@ -0,0 +1,86 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: utf8stub.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#include <system.h>
+#include "utf8stub.h"
+
+
+/*
+ * Stub functions to fill in functions used in utf8 support routines
+ */
+
+
+void *
+fs_get (size_t size)
+{
+ void *block = malloc (size ? size : (size_t) 1);
+ if (!block) fatal ("Out of memory");
+ return (block);
+}
+
+
+void
+fs_resize(void **block, size_t size)
+{
+ if (!(*block = realloc (*block,size ? size : (size_t) 1)))
+ fatal ("Can't resize memory");
+}
+
+void
+fs_give (void **block)
+{
+ free (*block);
+ *block = NULL;
+}
+
+void
+fatal(char *s)
+{
+ ;
+}
+
+
+int
+compare_ulong(unsigned long l1, unsigned long l2)
+{
+ if (l1 < l2) return -1;
+ if (l1 > l2) return 1;
+ return 0;
+}
+
+
+int
+compare_cstring(unsigned char *s1, unsigned char *s2)
+{
+ int i;
+ if (!s1) return s2 ? -1 : 0; /* empty string cases */
+ else if (!s2) return 1;
+ for (; *s1 && *s2; s1++,s2++)
+ if ((i = (compare_ulong (islower (*s1) ? toupper (*s1) : *s1,
+ islower (*s2) ? toupper (*s2) : *s2))) != 0)
+ return i; /* found a difference */
+ if (*s1) return 1; /* first string is longer */
+ return *s2 ? -1 : 0; /* second string longer : strings identical */
+}
+
+int
+panicking(void)
+{
+ return(0);
+}
diff --git a/pico/utf8stub.h b/pico/utf8stub.h
new file mode 100644
index 00000000..666716ab
--- /dev/null
+++ b/pico/utf8stub.h
@@ -0,0 +1,29 @@
+/*
+ * $Id: utf8stub.h 764 2007-10-23 23:44:49Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ *
+ */
+
+#ifndef UTF8STUB_H
+#define UTF8STUB_H
+
+void *fs_get(size_t size);
+void fs_resize(void **block,size_t size);
+void fs_give(void **block);
+void fatal(char *s);
+int compare_ulong(unsigned long l1, unsigned long l2);
+int compare_cstring(unsigned char *s1, unsigned char *s2);
+int panicking(void);
+
+#endif /* UTF8STUB_H */
diff --git a/pico/window.c b/pico/window.c
new file mode 100644
index 00000000..7187e16f
--- /dev/null
+++ b/pico/window.c
@@ -0,0 +1,47 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: window.c 761 2007-10-23 22:35:18Z hubert@u.washington.edu $";
+#endif
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*
+ * Window management. Some of the functions are internal, and some are
+ * attached to keys that the user actually types.
+ */
+
+#include "headers.h"
+
+
+/*
+ * Refresh the screen. With no argument, it just does the refresh. With an
+ * argument it recenters "." in the current window. Bound to "C-L".
+ */
+int
+pico_refresh(int f, int n)
+{
+ /*
+ * since pine mode isn't using the traditional mode line, sgarbf isn't
+ * enough.
+ */
+ if(Pmaster && curwp)
+ curwp->w_flag |= WFMODE;
+
+ if (f == FALSE)
+ sgarbf = TRUE;
+ else if(curwp){
+ curwp->w_force = 0; /* Center dot. */
+ curwp->w_flag |= WFFORCE;
+ }
+
+ return (TRUE);
+}
diff --git a/pico/word.c b/pico/word.c
new file mode 100644
index 00000000..145fc1d1
--- /dev/null
+++ b/pico/word.c
@@ -0,0 +1,1179 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: word.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*
+ * Program: Word at a time routines
+ *
+ * The routines in this file implement commands that work word at a time.
+ * There are all sorts of word mode commands. If I do any sentence and/or
+ * paragraph mode commands, they are likely to be put in this file.
+ */
+
+#include "headers.h"
+
+
+int fpnewline(UCS *quote);
+int fillregion(UCS *qstr, REGION *addedregion);
+int setquotelevelinregion(int quotelevel, REGION *addedregion);
+int is_user_separator(UCS c);
+
+
+/* Word wrap on n-spaces. Back-over whatever precedes the point on the current
+ * line and stop on the first word-break or the beginning of the line. If we
+ * reach the beginning of the line, jump back to the end of the word and start
+ * a new line. Otherwise, break the line at the word-break, eat it, and jump
+ * back to the end of the word.
+ * Returns TRUE on success, FALSE on errors.
+ */
+int
+wrapword(void)
+{
+ register int cnt; /* size of word wrapped to next line */
+ register int bp; /* index to wrap on */
+ register int first = -1;
+ int wid, ww;
+
+ if(curwp->w_doto <= 0) /* no line to wrap? */
+ return(FALSE);
+
+ wid = 0;
+ for(bp = cnt = 0; cnt < llength(curwp->w_dotp) && !bp; cnt++){
+ if(ucs4_isspace(lgetc(curwp->w_dotp, cnt).c)){
+ first = 0;
+ if(lgetc(curwp->w_dotp, cnt).c == TAB){
+ ++wid;
+ while(wid & 0x07)
+ ++wid;
+ }
+ else
+ ++wid;
+ }
+ else{
+ ww = wcellwidth((UCS) lgetc(curwp->w_dotp, cnt).c);
+ wid += (ww >= 0 ? ww : 1);
+ if(!first)
+ first = cnt;
+ }
+
+ if(first > 0 && wid > fillcol)
+ bp = first;
+ }
+
+ if(!bp)
+ return(FALSE);
+
+ /* bp now points to the first character of the next line */
+ cnt = curwp->w_doto - bp;
+ curwp->w_doto = bp;
+
+ if(!lnewline()) /* break the line */
+ return(FALSE);
+
+ /*
+ * if there's a line below, it doesn't start with whitespace
+ * and there's room for this line...
+ */
+ if(!(curbp->b_flag & BFWRAPOPEN)
+ && lforw(curwp->w_dotp) != curbp->b_linep
+ && llength(lforw(curwp->w_dotp))
+ && !ucs4_isspace(lgetc(lforw(curwp->w_dotp), 0).c)
+ && (llength(curwp->w_dotp) + llength(lforw(curwp->w_dotp)) < fillcol)){
+ gotoeol(0, 1); /* then pull text up from below */
+ if(lgetc(curwp->w_dotp, curwp->w_doto - 1).c != ' ')
+ linsert(1, ' ');
+
+ forwdel(0, 1);
+ gotobol(0, 1);
+ }
+
+ curbp->b_flag &= ~BFWRAPOPEN; /* don't open new line next wrap */
+ /* restore dot (account for NL) */
+ if(cnt && !forwchar(0, cnt < 0 ? cnt-1 : cnt))
+ return(FALSE);
+
+ return(TRUE);
+}
+
+
+/*
+ * Move the cursor backward by "n" words. All of the details of motion are
+ * performed by the "backchar" and "forwchar" routines. Error if you try to
+ * move beyond the buffers.
+ */
+int
+backword(int f, int n)
+{
+ if (n < 0)
+ return (forwword(f, -n));
+ if (backchar_no_header_editor(FALSE, 1) == FALSE)
+ return (FALSE);
+ while (n--) {
+ while (inword() == FALSE) {
+ if (backchar_no_header_editor(FALSE, 1) == FALSE)
+ return (FALSE);
+ }
+ while (inword() != FALSE) {
+ if (backchar_no_header_editor(FALSE, 1) == FALSE)
+ return (FALSE);
+ }
+ }
+ return (forwchar(FALSE, 1));
+}
+
+/*
+ * Move the cursor forward by the specified number of words. All of the motion
+ * is done by "forwchar". Error if you try and move beyond the buffer's end.
+ */
+int
+forwword(int f, int n)
+{
+ if (n < 0)
+ return (backword(f, -n));
+ while (n--) {
+#if NFWORD
+ while (inword() != FALSE) {
+ if (forwchar(FALSE, 1) == FALSE)
+ return (FALSE);
+ }
+#endif
+ while (inword() == FALSE) {
+ if (forwchar(FALSE, 1) == FALSE)
+ return (FALSE);
+ }
+#if NFWORD == 0
+ while (inword() != FALSE) {
+ if (forwchar(FALSE, 1) == FALSE)
+ return (FALSE);
+ }
+#endif
+ }
+ return(TRUE);
+}
+
+int
+ucs4_isalnum(UCS c)
+{
+ return((c && c <= 0x7f && isalnum((unsigned char) c))
+ || (c >= 0xA0 && !SPECIAL_SPACE(c)));
+}
+
+int
+ucs4_isalpha(UCS c)
+{
+ return((c && c <= 0x7f && isalpha((unsigned char) c))
+ || (c >= 0xA0 && !SPECIAL_SPACE(c)));
+}
+
+int
+ucs4_isspace(UCS c)
+{
+ return((c < 0xff && isspace((unsigned char) c)) || SPECIAL_SPACE(c));
+}
+
+int
+ucs4_ispunct(UCS c)
+{
+ return !ucs4_isalnum(c) && !ucs4_isspace(c);
+}
+
+#ifdef MAYBELATER
+/*
+ * Move the cursor forward by the specified number of words. As you move,
+ * convert any characters to upper case. Error if you try and move beyond the
+ * end of the buffer. Bound to "M-U".
+ */
+int
+upperword(int f, int n)
+{
+ register int c;
+ CELL ac;
+
+ ac.a = 0;
+ if (curbp->b_mode&MDVIEW) /* don't allow this command if */
+ return(rdonly()); /* we are in read only mode */
+ if (n < 0)
+ return (FALSE);
+ while (n--) {
+ while (inword() == FALSE) {
+ if (forwchar(FALSE, 1) == FALSE)
+ return (FALSE);
+ }
+ while (inword() != FALSE) {
+ c = lgetc(curwp->w_dotp, curwp->w_doto).c;
+ if (c>='a' && c<='z') {
+ ac.c = (c -= 'a'-'A');
+ lputc(curwp->w_dotp, curwp->w_doto, ac);
+ lchange(WFHARD);
+ }
+ if (forwchar(FALSE, 1) == FALSE)
+ return (FALSE);
+ }
+ }
+ return (TRUE);
+}
+
+/*
+ * Move the cursor forward by the specified number of words. As you move
+ * convert characters to lower case. Error if you try and move over the end of
+ * the buffer. Bound to "M-L".
+ */
+int
+lowerword(int f, int n)
+{
+ register int c;
+ CELL ac;
+
+ ac.a = 0;
+ if (curbp->b_mode&MDVIEW) /* don't allow this command if */
+ return(rdonly()); /* we are in read only mode */
+ if (n < 0)
+ return (FALSE);
+ while (n--) {
+ while (inword() == FALSE) {
+ if (forwchar(FALSE, 1) == FALSE)
+ return (FALSE);
+ }
+ while (inword() != FALSE) {
+ c = lgetc(curwp->w_dotp, curwp->w_doto).c;
+ if (c>='A' && c<='Z') {
+ ac.c (c += 'a'-'A');
+ lputc(curwp->w_dotp, curwp->w_doto, ac);
+ lchange(WFHARD);
+ }
+ if (forwchar(FALSE, 1) == FALSE)
+ return (FALSE);
+ }
+ }
+ return (TRUE);
+}
+
+/*
+ * Move the cursor forward by the specified number of words. As you move
+ * convert the first character of the word to upper case, and subsequent
+ * characters to lower case. Error if you try and move past the end of the
+ * buffer. Bound to "M-C".
+ */
+int
+capword(int f, int n)
+{
+ register int c;
+ CELL ac;
+
+ ac.a = 0;
+ if (curbp->b_mode&MDVIEW) /* don't allow this command if */
+ return(rdonly()); /* we are in read only mode */
+ if (n < 0)
+ return (FALSE);
+ while (n--) {
+ while (inword() == FALSE) {
+ if (forwchar(FALSE, 1) == FALSE)
+ return (FALSE);
+ }
+ if (inword() != FALSE) {
+ c = lgetc(curwp->w_dotp, curwp->w_doto).c;
+ if (c>='a' && c<='z') {
+ ac.c = (c -= 'a'-'A');
+ lputc(curwp->w_dotp, curwp->w_doto, ac);
+ lchange(WFHARD);
+ }
+ if (forwchar(FALSE, 1) == FALSE)
+ return (FALSE);
+ while (inword() != FALSE) {
+ c = lgetc(curwp->w_dotp, curwp->w_doto).c;
+ if (c>='A' && c<='Z') {
+ ac.c = (c += 'a'-'A');
+ lputc(curwp->w_dotp, curwp->w_doto, ac);
+ lchange(WFHARD);
+ }
+ if (forwchar(FALSE, 1) == FALSE)
+ return (FALSE);
+ }
+ }
+ }
+ return (TRUE);
+}
+
+/*
+ * Kill forward by "n" words. Remember the location of dot. Move forward by
+ * the right number of words. Put dot back where it was and issue the kill
+ * command for the right number of characters. Bound to "M-D".
+ */
+int
+delfword(int f, int n)
+{
+ register long size;
+ register LINE *dotp;
+ register int doto;
+
+ if (curbp->b_mode&MDVIEW) /* don't allow this command if */
+ return(rdonly()); /* we are in read only mode */
+ if (n < 0)
+ return (FALSE);
+ dotp = curwp->w_dotp;
+ doto = curwp->w_doto;
+ size = 0L;
+ while (n--) {
+#if NFWORD
+ while (inword() != FALSE) {
+ if (forwchar(FALSE,1) == FALSE)
+ return(FALSE);
+ ++size;
+ }
+#endif
+ while (inword() == FALSE) {
+ if (forwchar(FALSE, 1) == FALSE)
+ return (FALSE);
+ ++size;
+ }
+#if NFWORD == 0
+ while (inword() != FALSE) {
+ if (forwchar(FALSE, 1) == FALSE)
+ return (FALSE);
+ ++size;
+ }
+#endif
+ }
+ curwp->w_dotp = dotp;
+ curwp->w_doto = doto;
+ return (ldelete(size, kinsert));
+}
+
+/*
+ * Kill backwards by "n" words. Move backwards by the desired number of words,
+ * counting the characters. When dot is finally moved to its resting place,
+ * fire off the kill command. Bound to "M-Rubout" and to "M-Backspace".
+ */
+int
+delbword(int f, int n)
+{
+ register long size;
+
+ if (curbp->b_mode&MDVIEW) /* don't allow this command if */
+ return(rdonly()); /* we are in read only mode */
+ if (n < 0)
+ return (FALSE);
+ if (backchar(FALSE, 1) == FALSE)
+ return (FALSE);
+ size = 0L;
+ while (n--) {
+ while (inword() == FALSE) {
+ if (backchar(FALSE, 1) == FALSE)
+ return (FALSE);
+ ++size;
+ }
+ while (inword() != FALSE) {
+ if (backchar(FALSE, 1) == FALSE)
+ return (FALSE);
+ ++size;
+ }
+ }
+ if (forwchar(FALSE, 1) == FALSE)
+ return (FALSE);
+ return (ldelete(size, kinsert));
+}
+#endif /* MAYBELATER */
+
+/*
+ * Return TRUE if the character at dot is a character that is considered to be
+ * part of a word.
+ */
+int
+inword(void)
+{
+ if(curwp->w_doto < llength(curwp->w_dotp))
+ {
+ if(ucs4_isalnum(lgetc(curwp->w_dotp, curwp->w_doto).c))
+ {
+ return(TRUE);
+ }
+ else if(ucs4_ispunct(lgetc(curwp->w_dotp, curwp->w_doto).c)
+ && !is_user_separator(lgetc(curwp->w_dotp, curwp->w_doto).c))
+ {
+ if((curwp->w_doto > 0) &&
+ ucs4_isalnum(lgetc(curwp->w_dotp, curwp->w_doto - 1).c) &&
+ (curwp->w_doto + 1 < llength(curwp->w_dotp)) &&
+ ucs4_isalnum(lgetc(curwp->w_dotp, curwp->w_doto + 1).c))
+ {
+ return(TRUE);
+ }
+ }
+ }
+
+ return(FALSE);
+}
+
+
+int
+is_user_separator(UCS c)
+{
+ UCS *u;
+
+ if(glo_wordseps)
+ for(u = glo_wordseps; *u; u++)
+ if(*u == c)
+ return 1;
+
+ return 0;
+}
+
+
+/*
+ * Return number of quotes if whatever starts the line matches the quote string
+ */
+int
+quote_match(UCS *q, LINE *l, UCS *buf, size_t buflen)
+{
+ register int i, n, j, qb;
+
+ *buf = '\0';
+ if(*q == '\0')
+ return(1);
+
+ qb = (ucs4_strlen(q) > 1 && q[ucs4_strlen(q)-1] == ' ') ? 1 : 0;
+ for(n = 0, j = 0; ;){
+ for(i = 0; j <= llength(l) && qb ? q[i+1] : q[i]; i++, j++)
+ if(q[i] != lgetc(l, j).c)
+ return(n);
+
+ n++;
+ if((!qb && q[i] == '\0') || (qb && q[i+1] == '\0')){
+ if(ucs4_strlen(buf) + ucs4_strlen(q) + 1 < buflen){
+ ucs4_strncat(buf, q, buflen-ucs4_strlen(q)-1);
+ buf[buflen-1] = '\0';
+ if(qb && (j > llength(l) || lgetc(l, j).c != ' '))
+ buf[ucs4_strlen(buf)-1] = '\0';
+ }
+ }
+ if(j > llength(l))
+ return(n);
+ else if(qb && lgetc(l, j).c == ' ')
+ j++;
+ }
+ return(n); /* never reached */
+}
+
+
+/* Justify the entire buffer instead of just a paragraph */
+int
+fillbuf(int f, int n)
+{
+ LINE *eobline;
+ REGION region;
+
+ if(curbp->b_mode&MDVIEW){ /* don't allow this command if */
+ return(rdonly()); /* we are in read only mode */
+ }
+ else if (fillcol == 0) { /* no fill column set */
+ mlwrite_utf8("No fill column set", NULL);
+ return(FALSE);
+ }
+
+ if((lastflag & CFFILL) && (lastflag & CFFLBF)){
+ /* no use doing a full justify twice */
+ thisflag |= (CFFLBF | CFFILL);
+ return(TRUE);
+ }
+
+ /* record the pointer of the last line */
+ if(gotoeob(FALSE, 1) == FALSE)
+ return(FALSE);
+
+ eobline = curwp->w_dotp; /* last line of buffer */
+ if(!llength(eobline))
+ eobline = lback(eobline);
+
+ /* and back to the beginning of the buffer */
+ gotobob(FALSE, 1);
+
+ thisflag |= CFFLBF; /* CFFILL also gets set in fillpara */
+
+ if(!Pmaster)
+ sgarbk = TRUE;
+
+ curwp->w_flag |= WFMODE;
+
+ /*
+ * clear the kill buffer, that's where we'll store undo
+ * information, we can't do the fill buffer because
+ * fillpara relies on its contents
+ */
+ kdelete();
+ curwp->w_doto = 0;
+ getregion(&region, eobline, llength(eobline));
+
+ /* Put full message in the kill buffer for undo */
+ if(!ldelete(region.r_size, kinsert))
+ return(FALSE);
+
+ /* before yank'ing, clear lastflag so we don't just unjustify */
+ lastflag &= ~(CFFLBF | CFFILL);
+
+ /* Now in kill buffer, bring back text to use in fillpara */
+ yank(FALSE, 1);
+
+ gotobob(FALSE, 1);
+
+ /* call fillpara until we're at the end of the buffer */
+ while(curwp->w_dotp != curbp->b_linep)
+ if(!(fillpara(FALSE, 1)))
+ return(FALSE);
+
+ return(TRUE);
+}
+
+
+/*
+ * Fill the current paragraph according to the current fill column
+ */
+int
+fillpara(int f, int n)
+{
+ UCS *qstr, qstr2[NSTRING], c;
+ int quotelevel = -1;
+ REGION addedregion;
+ char action = 'P';
+
+ if(curbp->b_mode&MDVIEW){ /* don't allow this command if */
+ return(rdonly()); /* we are in read only mode */
+ }
+ else if (fillcol == 0) { /* no fill column set */
+ mlwrite_utf8("No fill column set", NULL);
+ return(FALSE);
+ }
+ else if(curwp->w_dotp == curbp->b_linep && !curwp->w_markp) /* don't wrap! */
+ return(FALSE);
+
+ /*
+ * If there is already a region set, then we may use it
+ * instead of the current paragraph.
+ */
+
+ if(curwp->w_markp){
+ int k, rv;
+ KEYMENU menu_justify[12];
+ char prompt[100];
+
+ for(k = 0; k < 12; k++){
+ menu_justify[k].name = NULL;
+ KS_OSDATASET(&menu_justify[k], KS_NONE);
+ }
+
+ menu_justify[1].name = "R";
+ menu_justify[1].label = "[" N_("Region") "]";
+ menu_justify[6].name = "^C";
+ menu_justify[6].label = N_("Cancel");
+ menu_justify[7].name = "P";
+ menu_justify[7].label = N_("Paragraph");
+ menu_justify[2].name = "Q";
+ menu_justify[2].label = N_("Quotelevel");
+
+ wkeyhelp(menu_justify); /* paint menu */
+ sgarbk = TRUE;
+ if(Pmaster && curwp)
+ curwp->w_flag |= WFMODE;
+
+ strncpy(prompt, "justify Region, Paragraph; or fix Quotelevel ? ", sizeof(prompt));
+ prompt[sizeof(prompt)-1] = '\0';
+ mlwrite_utf8(prompt, NULL);
+ (*term.t_rev)(1);
+ rv = -1;
+ while(1){
+ switch(c = GetKey()){
+
+ case (CTRL|'C') : /* Bail out! */
+ case F2 :
+ pputs_utf8(_("ABORT"), 1);
+ rv = ABORT;
+ emlwrite("", NULL);
+ break;
+
+ case (CTRL|'M') : /* default */
+ case 'r' :
+ case 'R' :
+ case F3 :
+ pputs_utf8(_("Region"), 1);
+ rv = 'R';
+ break;
+
+ case 'p' :
+ case 'P' :
+ case F7 :
+ pputs_utf8(_("Paragraph"), 1);
+ rv = 'P';
+ break;
+
+ case 'q' :
+ case 'Q' :
+ case F8 :
+ case '0' : case '1' : case '2' : case '3' : case '4' :
+ case '5' : case '6' : case '7' : case '8' : case '9' :
+ pputs_utf8(_("Quotelevel"), 1);
+ while(rv == -1){
+ switch(c){
+ case 'q' :
+ case 'Q' :
+ case F8 :
+ {char num[20];
+
+ num[0] = '\0';
+ switch(mlreplyd_utf8("Quote Level ? ", num, sizeof(num), QNORML, NULL)){
+ case TRUE:
+ if(isdigit(num[0])){
+ quotelevel = atoi(num);
+ if(quotelevel < 0){
+ emlwrite("Quote Level cannot be negative", NULL);
+ sleep(3);
+ }
+ else if(quotelevel > 20){
+ emlwrite("Quote Level should be less than 20", NULL);
+ rv = ABORT;
+ }
+ else{
+ rv = 'Q';
+ }
+ }
+ else if(num[0]){
+ emlwrite("Quote Level should be a number", NULL);
+ sleep(3);
+ }
+
+ break;
+
+ case HELPCH:
+ emlwrite("Enter the number of quotes you want before the text", NULL);
+ sleep(3);
+ break;
+
+ default:
+ emlwrite("Quote Level is a number", NULL);
+ rv = ABORT;
+ break;
+ }
+ }
+
+ break;
+
+ case '0' : case '1' : case '2' : case '3' : case '4' :
+ case '5' : case '6' : case '7' : case '8' : case '9' :
+ rv = 'Q';
+ quotelevel = (int) (c - '0');
+ break;
+ }
+ }
+
+ break;
+
+ case (CTRL|'G') :
+ if(term.t_mrow == 0 && km_popped == 0){
+ movecursor(term.t_nrow-2, 0);
+ peeol();
+ term.t_mrow = 2;
+ (*term.t_rev)(0);
+ wkeyhelp(menu_justify);
+ mlwrite_utf8(prompt, NULL);
+ (*term.t_rev)(1);
+ sgarbk = TRUE; /* mark menu dirty */
+ km_popped++;
+ break;
+ }
+ /* else fall through */
+
+ default:
+ (*term.t_beep)();
+
+ case NODATA :
+ break;
+ }
+
+ (*term.t_flush)();
+ if(rv != -1){
+ (*term.t_rev)(0);
+ if(km_popped){
+ term.t_mrow = 0;
+ movecursor(term.t_nrow, 0);
+ peeol();
+ sgarbf = 1;
+ km_popped = 0;
+ }
+
+ action = rv;
+ break;
+ }
+ }
+
+ if(action != ABORT)
+ emlwrite("", NULL);
+ }
+
+ if(action == 'R' && curwp->w_markp){
+ /* let yank() know that it may be restoring a paragraph */
+ thisflag |= CFFILL;
+
+ if(!Pmaster)
+ sgarbk = TRUE;
+
+ curwp->w_flag |= WFMODE;
+
+ swap_mark_and_dot_if_mark_comes_first();
+
+ /* determine if we're justifying quoted text or not */
+ qstr = (glo_quote_str
+ && quote_match(glo_quote_str,
+ curwp->w_doto > 0 ? curwp->w_dotp->l_fp : curwp->w_dotp,
+ qstr2, NSTRING)
+ && *qstr2) ? qstr2 : NULL;
+
+
+ /*
+ * Fillregion moves dot to the end of the filled region.
+ */
+ if(!fillregion(qstr, &addedregion))
+ return(FALSE);
+
+ set_last_region_added(&addedregion);
+ }
+ else if(action == 'P'){
+
+ /*
+ * Justfiy the current paragraph.
+ */
+
+ if(curwp->w_markp) /* clear mark if already set */
+ setmark(0,0);
+
+ if(gotoeop(FALSE, 1) == FALSE)
+ return(FALSE);
+
+ /* determine if we're justifying quoted text or not */
+ qstr = (glo_quote_str
+ && quote_match(glo_quote_str,
+ curwp->w_dotp, qstr2, NSTRING)
+ && *qstr2) ? qstr2 : NULL;
+
+ setmark(0,0); /* mark last line of para */
+
+ /* jump back to the beginning of the paragraph */
+ gotobop(FALSE, 1);
+
+ /* let yank() know that it may be restoring a paragraph */
+ thisflag |= (CFFILL | CFFLPA);
+
+ if(!Pmaster)
+ sgarbk = TRUE;
+
+ curwp->w_flag |= WFMODE;
+
+ curwp->w_doto = 0; /* start region at beginning of line */
+
+ /*
+ * Fillregion moves dot to the end of the filled region.
+ */
+ if(!fillregion(qstr, &addedregion))
+ return(FALSE);
+
+ set_last_region_added(&addedregion);
+
+ /* Leave cursor on first char of first line after justified region */
+ curwp->w_dotp = lforw(curwp->w_dotp);
+ curwp->w_doto = 0;
+
+ if(curwp->w_markp)
+ setmark(0,0); /* clear mark */
+ }
+ else if(action == 'Q'){
+ /* let yank() know that it may be restoring a paragraph */
+ thisflag |= CFFILL;
+
+ if(!Pmaster)
+ sgarbk = TRUE;
+
+ curwp->w_flag |= WFHARD;
+
+ swap_mark_and_dot_if_mark_comes_first();
+
+ if(!setquotelevelinregion(quotelevel, &addedregion))
+ return(FALSE);
+
+ set_last_region_added(&addedregion);
+ }
+ else{
+ /* abort */
+ }
+
+ return(TRUE);
+}
+
+
+/*
+ * The region we're filling is the region from dot to mark.
+ * We cut out that region and then put it back in filled.
+ * The cut out part is saved in the ldelete call and the
+ * reinstalled region is noted in addedregion, so that yank()
+ * can delete it and restore the saved part.
+ */
+int
+fillregion(UCS *qstr, REGION *addedregion)
+{
+ long c, sz, last_char = 0;
+ int i, j, qlen, same_word,
+ spaces, word_len, word_ind, line_len, ww;
+ int starts_midline = 0;
+ int ends_midline = 0;
+ int offset_into_start;
+ LINE *line_before_start, *lp;
+ UCS line_last, word[NSTRING];
+ REGION region;
+
+ /* if region starts midline insert a newline */
+ if(curwp->w_doto > 0 && curwp->w_doto < llength(curwp->w_dotp))
+ starts_midline++;
+
+ /* if region ends midline insert a newline at end */
+ if(curwp->w_marko > 0 && curwp->w_marko < llength(curwp->w_markp))
+ ends_midline++;
+
+ /* cut the paragraph into our fill buffer */
+ fdelete();
+ if(!getregion(&region, curwp->w_markp, curwp->w_marko))
+ return(FALSE);
+
+ if(!ldelete(region.r_size, finsert))
+ return(FALSE);
+
+ line_before_start = lback(curwp->w_dotp);
+ offset_into_start = curwp->w_doto;
+
+ if(starts_midline)
+ lnewline();
+
+ /* Now insert it back wrapped */
+ spaces = word_len = word_ind = line_len = same_word = 0;
+ qlen = qstr ? ucs4_strlen(qstr) : 0;
+
+ /* Beginning with leading quoting... */
+ if(qstr){
+ i = 0;
+ while(qstr[i]){
+ ww = wcellwidth(qstr[i]);
+ line_len += (ww >= 0 ? ww : 1);
+ linsert(1, qstr[i++]);
+ }
+
+ line_last = ' '; /* no word-flush space! */
+ }
+
+ /* remove first leading quotes if any */
+ if(starts_midline)
+ i = 0;
+ else
+ for(i = qlen; (c = fremove(i)) == ' ' || c == TAB; i++){
+ linsert(1, line_last = (UCS) c);
+ line_len += ((c == TAB) ? (~line_len & 0x07) + 1 : 1);
+ }
+
+ /* then digest the rest... */
+ while((c = fremove(i++)) >= 0){
+ last_char = c;
+ switch(c){
+ case '\n' :
+ /* skip next quote string */
+ j = 0;
+ while(j < qlen && ((c = fremove(i+j)) == qstr[j] || c == ' '))
+ j++;
+
+ i += j;
+
+
+ if(!spaces)
+ spaces++;
+ break;
+
+ case TAB :
+ case ' ' :
+ spaces++;
+ break;
+
+ default :
+ if(spaces){ /* flush word? */
+ if((line_len - qlen > 0)
+ && line_len + word_len + 1 > fillcol
+ && ((ucs4_isspace(line_last))
+ || (linsert(1, ' ')))
+ && same_word == 0
+ && (line_len = fpnewline(qstr)))
+ line_last = ' '; /* no word-flush space! */
+
+ if(word_len){ /* word to write? */
+ if(line_len && !ucs4_isspace(line_last)){
+ linsert(1, ' '); /* need padding? */
+ line_len++;
+ }
+
+ line_len += word_len;
+ for(j = 0; j < word_ind; j++)
+ linsert(1, line_last = word[j]);
+
+ if(spaces > 1 && strchr(".?!:;\")", line_last)){
+ linsert(2, line_last = ' ');
+ line_len += 2;
+ }
+
+ same_word = word_len = word_ind = 0;
+ }
+
+ spaces = 0;
+ }
+
+ if(word_ind + 1 >= NSTRING){
+ /* Magic! Fake that we output a wrapped word */
+ if((line_len - qlen > 0) && same_word == 0){
+ if(!ucs4_isspace(line_last))
+ linsert(1, ' ');
+ line_len = fpnewline(qstr);
+ }
+ same_word = 1;
+ line_len += word_len;
+ for(j = 0; j < word_ind; j++)
+ linsert(1, word[j]);
+
+ word_len = word_ind = 0;
+ line_last = ' ';
+ }
+
+ word[word_ind++] = (UCS) c;
+ ww = wcellwidth((UCS) c);
+ word_len += (ww >= 0 ? ww : 1);
+
+ break;
+ }
+ }
+
+ if(word_len){
+ if((line_len - qlen > 0) && (line_len + word_len + 1 > fillcol) && same_word == 0){
+ if(!ucs4_isspace(line_last))
+ linsert(1, ' ');
+ (void) fpnewline(qstr);
+ }
+ else if(line_len && !ucs4_isspace(line_last))
+ linsert(1, ' ');
+
+ for(j = 0; j < word_ind; j++)
+ linsert(1, word[j]);
+ }
+
+ if(last_char == '\n')
+ lnewline();
+
+ if(ends_midline)
+ (void) fpnewline(qstr);
+
+ /*
+ * Calculate the size of the region that was added.
+ */
+ swapmark(0,1); /* mark current location after adds */
+ addedregion->r_linep = lforw(line_before_start);
+ addedregion->r_offset = offset_into_start;
+ lp = addedregion->r_linep;
+ sz = llength(lp) - addedregion->r_offset;
+ if(lforw(lp) != curwp->w_markp->l_fp){
+ lp = lforw(lp);
+ while(lp != curwp->w_markp->l_fp){
+ sz += llength(lp) + 1;
+ lp = lforw(lp);
+ }
+ }
+
+ sz -= llength(curwp->w_markp) - curwp->w_marko;
+ addedregion->r_size = sz;
+
+ swapmark(0,1);
+
+ if(ends_midline){
+ /*
+ * We want to back up to the end of the original
+ * region instead of being here after the added newline.
+ */
+ curwp->w_doto = 0;
+ backchar(0, 1);
+ unmarkbuffer();
+ markregion(1);
+ }
+
+ return(TRUE);
+}
+
+
+/*
+ * fpnewline - output a fill paragraph newline mindful of quote string
+ */
+int
+fpnewline(UCS *quote)
+{
+ int len;
+
+ lnewline();
+ for(len = 0; quote && *quote; quote++){
+ int ww;
+
+ ww = wcellwidth(*quote);
+ len += (ww >= 0 ? ww : 1);
+ linsert(1, *quote);
+ }
+
+ return(len);
+}
+
+
+int
+setquotelevelinregion(int quotelevel, REGION *addedregion)
+{
+ int i, standards_based = 0;
+ int quote_chars = 0, backuptoprevline = 0;
+ int starts_midline = 0, ends_midline = 0, offset_into_start;
+ long c, sz;
+ UCS qstr_def1[] = { '>', ' ', 0}, qstr_def2[] = { '>', 0};
+ LINE *lp, *line_before_start;
+ REGION region;
+
+ if(curbp->b_mode&MDVIEW) /* don't allow this command if */
+ return(rdonly()); /* we are in read only mode */
+
+ if(!glo_quote_str
+ || !ucs4_strcmp(glo_quote_str, qstr_def1)
+ || !ucs4_strcmp(glo_quote_str, qstr_def2))
+ standards_based++;
+
+ if(!standards_based){
+ emlwrite("Quote level setting only works with standard \"> \" quotes", NULL);
+ return(FALSE);
+ }
+
+ /* if region starts midline insert a newline */
+ if(curwp->w_doto > 0 && curwp->w_doto < llength(curwp->w_dotp))
+ starts_midline++;
+
+ /* if region ends midline insert a newline at end */
+ if(curwp->w_marko > 0 && curwp->w_marko < llength(curwp->w_markp)){
+ ends_midline++;
+ backuptoprevline++;
+ /* count quote chars for re-insertion */
+ for(i = 0; i < llength(curwp->w_markp); ++i)
+ if(lgetc(curwp->w_markp, i).c != '>')
+ break;
+
+ quote_chars = i;
+ }
+ else if(curwp->w_marko == 0)
+ backuptoprevline++;
+
+ /* find the size of the region */
+ getregion(&region, curwp->w_markp, curwp->w_marko);
+
+ /* cut the paragraph into our fill buffer */
+ fdelete();
+ if(!ldelete(region.r_size, finsert))
+ return(FALSE);
+
+ line_before_start = lback(curwp->w_dotp);
+ offset_into_start = curwp->w_doto;
+
+ /* if region starts midline add a newline */
+ if(starts_midline)
+ lnewline();
+
+ i = 0;
+ while(fremove(i) >= 0){
+
+ /* remove all quote strs from current line */
+ if(standards_based){
+ while((c = fremove(i)) == '>')
+ i++;
+
+ if(c == ' ')
+ i++;
+ }
+ else{
+ }
+
+ /* insert quotelevel quote strs */
+ if(standards_based){
+ linsert(quotelevel, '>');
+ if(quotelevel > 0)
+ linsert(1, ' ');
+ }
+ else{
+ }
+
+ /* put back the actual line */
+ while((c = fremove(i++)) >= 0 && c != '\n')
+ linsert(1, (UCS) c);
+
+ if(c == '\n')
+ lnewline();
+ }
+
+ /* if region ends midline add a newline */
+ if(ends_midline){
+ lnewline();
+ if(quote_chars){
+ linsert(quote_chars, '>');
+ if(curwp->w_doto < llength(curwp->w_dotp)
+ && lgetc(curwp->w_dotp, curwp->w_doto).c != ' ')
+ linsert(1, ' ');
+ }
+ }
+
+ /*
+ * Calculate the size of the region that was added.
+ */
+ swapmark(0,1); /* mark current location after adds */
+ addedregion->r_linep = lforw(line_before_start);
+ addedregion->r_offset = offset_into_start;
+ lp = addedregion->r_linep;
+ sz = llength(lp) - addedregion->r_offset;
+ if(lforw(lp) != curwp->w_markp->l_fp){
+ lp = lforw(lp);
+ while(lp != curwp->w_markp->l_fp){
+ sz += llength(lp) + 1;
+ lp = lforw(lp);
+ }
+ }
+
+ sz -= llength(curwp->w_markp) - curwp->w_marko;
+ addedregion->r_size = sz;
+
+ swapmark(0,1);
+
+ /*
+ * This puts us at the end of the quoted region instead
+ * of on the following line. This makes it convenient
+ * for the user to follow a quotelevel adjustment with
+ * a Justify if desired.
+ */
+ if(backuptoprevline){
+ curwp->w_doto = 0;
+ backchar(0, 1);
+ }
+
+ if(ends_midline){ /* doesn't need fixing otherwise */
+ unmarkbuffer();
+ markregion(1);
+ }
+
+ return (TRUE);
+}
diff --git a/pith/IO.ReadMe b/pith/IO.ReadMe
new file mode 100644
index 00000000..55f98067
--- /dev/null
+++ b/pith/IO.ReadMe
@@ -0,0 +1,122 @@
+Here's an attempt at a high-level description of the I/O flow and the
+character set conversions for Alpine from the UNIX point of view. Pico and
+Pine were developed separately so there is a big difference in the way
+that Alpine handles stuff and the way the pico composer handles stuff.
+
+INPUT
+
+There's a low-level function called input_ready() that does a select
+or a poll to see if an octet is ready to be read. Alpine's read_char
+and pico's GetKey call this:
+
+ read_char Alpine
+ check_for_timeout
+ input_ready
+or
+ GetKey Pico
+ ReadyForKey
+ input_ready
+
+Once they've decided an octet is ready to be read they use various versions
+of ttgetc (ttgetc, simple_ttgetc, pine_simple_ttgetc) which all boil down
+to a read of one octet from the keyboard. That incoming stream of
+characters is a stream of multi-byte characters. For example, it might
+be ASCII, UTF-8, ISO-8859-1, or EUC-JP. This would usually be configured
+using the LANG or LC_CTYPE environment variables, or possibly using
+the Alpine Keyboard-Character-Set or Display-Character-Set options.
+The pith routine mbtow is used to convert this stream of bytes into
+UCS-4 characters in the routine kbseq.
+
+ read_char
+ kbseq (uses ttgetc to accumulate bytes)
+ mbtow
+or
+ GetKey
+ kbseq ( " )
+ mbtow
+
+So read_char and GetKey both return UCS-4 characters from the keyboard.
+They actually return a superset of a UCS-4 character (typedef UCS) that
+is 32 bits wide. The superset comes about from the handling of
+escape sequences and error conditions. Besides the 21 bits of UCS-4
+characters other values that might be returned are values like KEY_UP,
+KEY_RESIZE, F1-F12, PF1-PF12, NO_OP_COMMAND, NO_OP_IDLE, BADESC, KEY_JUNK,
+and NODATA. The way that read_char and GetKey work are slightly differnt
+from each other. GetKey has some special bits (CTRL, FUNC, MENU) that
+it might OR together with a character and some of the return values
+are possible in only one or the other function.
+
+Pico is internally UCS-4. A CELL contains one character.
+Alpine, on the other hand, uses UTF-8 internally. The input from read_char
+is read by read_command, radio_buttons, and optionally_enter and is
+then converted to UTF-8 before it is used internally in Alpine.
+
+OUTPUT
+
+All Alpine output to the display funnels through Writechar. The input to
+Writechar is a stream of UTF-8 characters which are then converted to
+the display's multi-byte character stream using c-client's utf8_get
+to convert it to UCS-4 and then wtomb to convert to the multi-byte
+representation.
+The similar function in pico is ttputc (also known as t_putchar) which
+takes an incoming stream of UCS-4 characters and converts it to multi-byte
+characters for the display.
+
+
+
+
+CHARACTER SET CONVERSIONS
+
+Here is a simplified version of the complicated character set conversions
+going on.
+
+ ---------- |--read_char
+ | Keyboard |-ttgetc->-mbtow->-kbseq->| (UCS-4)
+ ---------- (to UCS-4) |--GetKey
+
+
+ |--optionally_enter ----------------
+ read_char->|--read_command ------------> | Alpine internal|
+ |--radio_buttons (to UTF-8) | UTF-8 |
+ ----------------
+or
+ |--pico
+ |--LineEdit ----------
+ GetKey->|--mlyesno ------------> | Composer |
+ |--mlreplyd | UCS-4 |
+ |--FileBrowse ----------
+
+
+ ---------------- ----------
+ | Alpine internal| <-------pico_readc--------- | Composer |
+ | UTF-8 | --------pico_writec-------> | UCS-4 |
+ ---------------- ----------
+
+
+ ---------------- ---------
+ | Alpine internal| -Writechar->-utf8_get->-wtomb-> | Display |
+ | UTF-8 | | (LANG) |
+ ---------------- ---------
+
+ ---------- ---------
+ | Composer | ---pputc-->ttputc->-----wtomb-> | Display |
+ | UCS-4 | | (LANG) |
+ ---------- ---------
+
+
+ ---------------- -----------
+ | Alpine internal| <-decode_text--------<-c-client- | MailStore |
+ | UTF-8 | get_body_part_text | |
+ ---------------- -----------
+
+
+ ---------------- ------------
+ | Alpine internal| <-------------------------- | FileSystem |
+ | UTF-8 | --------------------------> | |
+ ---------------- see flags READ_FROM_LOCALE ------------
+ and WRITE_TO_LOCALE
+
+ ---------- ------------
+ | Composer | <--------ffgetline------------- | FileSystem |
+ | UCS-4 | ---------ffputline------------> | |
+ ---------- ------------
diff --git a/pith/Makefile.am b/pith/Makefile.am
new file mode 100644
index 00000000..ce6c78a3
--- /dev/null
+++ b/pith/Makefile.am
@@ -0,0 +1,47 @@
+## Process this file with automake to produce Makefile.in
+## Use aclocal -I m4; automake
+
+# ========================================================================
+# Copyright 2006-2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+SUBDIRS = osdep charconv
+
+noinst_LIBRARIES = libpith.a
+
+BUILT_SOURCES = helptext.h helptext.c
+
+noinst_PROGRAMS = help_h_gen help_c_gen
+
+libpith_a_SOURCES = ablookup.c abdlc.c addrbook.c addrstring.c adrbklib.c bldaddr.c charset.c \
+ color.c conf.c context.c copyaddr.c detoken.c detach.c editorial.c escapes.c \
+ filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.c imap.c init.c \
+ keyword.c ldap.c list.c mailcap.c mailcmd.c mailindx.c maillist.c mailview.c \
+ margin.c mimedesc.c mimetype.c msgno.c newmail.c news.c pattern.c pipe.c \
+ readfile.c remote.c reply.c rfc2231.c save.c search.c sequence.c send.c sort.c \
+ state.c status.c store.c stream.c string.c strlst.c takeaddr.c tempfile.c text.c \
+ thread.c adjtime.c url.c util.c helptext.c smkeys.c smime.c
+
+help_c_gen$(EXEEXT): $(help_c_gen_OBJECTS) $(help_c_gen_DEPENDENCIES)
+ @rm -f help_c_gen$(EXEEXT)
+ $(LINK) $(help_c_gen_OBJECTS) $(help_c_gen_LDADD)
+help_h_gen$(EXEEXT): $(help_h_gen_OBJECTS) $(help_h_gen_DEPENDENCIES)
+ @rm -f help_h_gen$(EXEEXT)
+ $(LINK) $(help_h_gen_OBJECTS) $(help_h_gen_LDADD)
+
+helptext.c: help_c_gen pine.hlp
+ ./help_c_gen < pine.hlp > $@
+
+helptext.h: help_h_gen pine.hlp
+ ./help_h_gen < pine.hlp > $@
+
+AM_CPPFLAGS = -I@top_builddir@/include -I@top_srcdir@/include
+
+CLEANFILES = helptext.c helptext.h help_h_gen help_c_gen
diff --git a/pith/Makefile.in b/pith/Makefile.in
new file mode 100644
index 00000000..322291b0
--- /dev/null
+++ b/pith/Makefile.in
@@ -0,0 +1,815 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# ========================================================================
+# Copyright 2006-2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+noinst_PROGRAMS = help_h_gen$(EXEEXT) help_c_gen$(EXEEXT)
+subdir = pith
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/acx_pthread.m4 \
+ $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
+ $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/VERSION \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/include/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+ARFLAGS = cru
+libpith_a_AR = $(AR) $(ARFLAGS)
+libpith_a_LIBADD =
+am_libpith_a_OBJECTS = ablookup.$(OBJEXT) abdlc.$(OBJEXT) \
+ addrbook.$(OBJEXT) addrstring.$(OBJEXT) adrbklib.$(OBJEXT) \
+ bldaddr.$(OBJEXT) charset.$(OBJEXT) color.$(OBJEXT) \
+ conf.$(OBJEXT) context.$(OBJEXT) copyaddr.$(OBJEXT) \
+ detoken.$(OBJEXT) detach.$(OBJEXT) editorial.$(OBJEXT) \
+ escapes.$(OBJEXT) filter.$(OBJEXT) flag.$(OBJEXT) \
+ folder.$(OBJEXT) handle.$(OBJEXT) help.$(OBJEXT) \
+ helpindx.$(OBJEXT) hist.$(OBJEXT) icache.$(OBJEXT) \
+ imap.$(OBJEXT) init.$(OBJEXT) keyword.$(OBJEXT) ldap.$(OBJEXT) \
+ list.$(OBJEXT) mailcap.$(OBJEXT) mailcmd.$(OBJEXT) \
+ mailindx.$(OBJEXT) maillist.$(OBJEXT) mailview.$(OBJEXT) \
+ margin.$(OBJEXT) mimedesc.$(OBJEXT) mimetype.$(OBJEXT) \
+ msgno.$(OBJEXT) newmail.$(OBJEXT) news.$(OBJEXT) \
+ pattern.$(OBJEXT) pipe.$(OBJEXT) readfile.$(OBJEXT) \
+ remote.$(OBJEXT) reply.$(OBJEXT) rfc2231.$(OBJEXT) \
+ save.$(OBJEXT) search.$(OBJEXT) sequence.$(OBJEXT) \
+ send.$(OBJEXT) sort.$(OBJEXT) state.$(OBJEXT) status.$(OBJEXT) \
+ store.$(OBJEXT) stream.$(OBJEXT) string.$(OBJEXT) \
+ strlst.$(OBJEXT) takeaddr.$(OBJEXT) tempfile.$(OBJEXT) \
+ text.$(OBJEXT) thread.$(OBJEXT) adjtime.$(OBJEXT) \
+ url.$(OBJEXT) util.$(OBJEXT) helptext.$(OBJEXT) \
+ smkeys.$(OBJEXT) smime.$(OBJEXT)
+libpith_a_OBJECTS = $(am_libpith_a_OBJECTS)
+PROGRAMS = $(noinst_PROGRAMS)
+help_c_gen_SOURCES = help_c_gen.c
+help_c_gen_OBJECTS = help_c_gen.$(OBJEXT)
+help_c_gen_LDADD = $(LDADD)
+help_h_gen_SOURCES = help_h_gen.c
+help_h_gen_OBJECTS = help_h_gen.$(OBJEXT)
+help_h_gen_LDADD = $(LDADD)
+DEFAULT_INCLUDES =
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+SOURCES = $(libpith_a_SOURCES) help_c_gen.c help_h_gen.c
+DIST_SOURCES = $(libpith_a_SOURCES) help_c_gen.c help_h_gen.c
+RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
+ html-recursive info-recursive install-data-recursive \
+ install-dvi-recursive install-exec-recursive \
+ install-html-recursive install-info-recursive \
+ install-pdf-recursive install-ps-recursive install-recursive \
+ installcheck-recursive installdirs-recursive pdf-recursive \
+ ps-recursive uninstall-recursive
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \
+ $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \
+ distdir
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_CFLAGS = @AM_CFLAGS@
+AM_LDFLAGS = @AM_LDFLAGS@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CP = @CP@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+C_CLIENT_CFLAGS = @C_CLIENT_CFLAGS@
+C_CLIENT_GCCOPTLEVEL = @C_CLIENT_GCCOPTLEVEL@
+C_CLIENT_LDFLAGS = @C_CLIENT_LDFLAGS@
+C_CLIENT_SPECIALS = @C_CLIENT_SPECIALS@
+C_CLIENT_TARGET = @C_CLIENT_TARGET@
+C_CLIENT_WITH_IPV6 = @C_CLIENT_WITH_IPV6@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISPELLPROG = @ISPELLPROG@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN = @LN@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKE = @MAKE@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NPA_PROG = @NPA_PROG@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+POSUB = @POSUB@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+PWPROG = @PWPROG@
+RANLIB = @RANLIB@
+REGEX_BUILD = @REGEX_BUILD@
+RM = @RM@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPELLPROG = @SPELLPROG@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEB_BINDIR = @WEB_BINDIR@
+WEB_BUILD = @WEB_BUILD@
+WEB_PUBCOOKIE_BUILD = @WEB_PUBCOOKIE_BUILD@
+WEB_PUBCOOKIE_LIB = @WEB_PUBCOOKIE_LIB@
+WEB_PUBCOOKIE_LINK = @WEB_PUBCOOKIE_LINK@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+acx_pthread_config = @acx_pthread_config@
+alpine_interactive_spellcheck = @alpine_interactive_spellcheck@
+alpine_simple_spellcheck = @alpine_simple_spellcheck@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SUBDIRS = osdep charconv
+noinst_LIBRARIES = libpith.a
+BUILT_SOURCES = helptext.h helptext.c
+libpith_a_SOURCES = ablookup.c abdlc.c addrbook.c addrstring.c adrbklib.c bldaddr.c charset.c \
+ color.c conf.c context.c copyaddr.c detoken.c detach.c editorial.c escapes.c \
+ filter.c flag.c folder.c handle.c help.c helpindx.c hist.c icache.c imap.c init.c \
+ keyword.c ldap.c list.c mailcap.c mailcmd.c mailindx.c maillist.c mailview.c \
+ margin.c mimedesc.c mimetype.c msgno.c newmail.c news.c pattern.c pipe.c \
+ readfile.c remote.c reply.c rfc2231.c save.c search.c sequence.c send.c sort.c \
+ state.c status.c store.c stream.c string.c strlst.c takeaddr.c tempfile.c text.c \
+ thread.c adjtime.c url.c util.c helptext.c smkeys.c smime.c
+
+AM_CPPFLAGS = -I@top_builddir@/include -I@top_srcdir@/include
+CLEANFILES = helptext.c helptext.h help_h_gen help_c_gen
+all: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign pith/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign pith/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLIBRARIES:
+ -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+libpith.a: $(libpith_a_OBJECTS) $(libpith_a_DEPENDENCIES)
+ -rm -f libpith.a
+ $(libpith_a_AR) libpith.a $(libpith_a_OBJECTS) $(libpith_a_LIBADD)
+ $(RANLIB) libpith.a
+
+clean-noinstPROGRAMS:
+ @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/abdlc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ablookup.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/addrbook.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/addrstring.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/adjtime.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/adrbklib.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bldaddr.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/charset.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/color.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conf.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/context.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/copyaddr.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/detach.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/detoken.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/editorial.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/escapes.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filter.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flag.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/folder.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/handle.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/help.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/help_c_gen.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/help_h_gen.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/helpindx.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/helptext.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hist.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/icache.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/init.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/keyword.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/list.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailcap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailcmd.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailindx.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/maillist.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mailview.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/margin.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mimedesc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mimetype.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/msgno.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/newmail.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/news.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pattern.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pipe.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/readfile.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/remote.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/reply.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rfc2231.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/save.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/search.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/send.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sequence.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/smime.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/smkeys.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sort.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/state.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/status.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/store.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stream.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/string.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strlst.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/takeaddr.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tempfile.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/text.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/thread.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/url.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run `make' without going through this Makefile.
+# To change the values of `make' variables: instead of editing Makefiles,
+# (1) if the variable is set in `config.status', edit `config.status'
+# (which will cause the Makefiles to be regenerated when you run `make');
+# (2) otherwise, pass the desired values on the `make' command line.
+$(RECURSIVE_TARGETS):
+ @fail= failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+$(RECURSIVE_CLEAN_TARGETS):
+ @fail= failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ rev=''; for subdir in $$list; do \
+ if test "$$subdir" = "."; then :; else \
+ rev="$$subdir $$rev"; \
+ fi; \
+ done; \
+ rev="$$rev ."; \
+ target=`echo $@ | sed s/-recursive//`; \
+ for subdir in $$rev; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done && test -z "$$fail"
+tags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+ done
+ctags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
+ done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) check-recursive
+all-am: Makefile $(LIBRARIES) $(PROGRAMS)
+installdirs: installdirs-recursive
+installdirs-am:
+install: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES)
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \
+ clean-noinstPROGRAMS mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) all check \
+ ctags-recursive install install-am install-strip \
+ tags-recursive
+
+.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \
+ all all-am check check-am clean clean-generic clean-libtool \
+ clean-noinstLIBRARIES clean-noinstPROGRAMS ctags \
+ ctags-recursive distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs installdirs-am maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-recursive uninstall uninstall-am
+
+
+help_c_gen$(EXEEXT): $(help_c_gen_OBJECTS) $(help_c_gen_DEPENDENCIES)
+ @rm -f help_c_gen$(EXEEXT)
+ $(LINK) $(help_c_gen_OBJECTS) $(help_c_gen_LDADD)
+help_h_gen$(EXEEXT): $(help_h_gen_OBJECTS) $(help_h_gen_DEPENDENCIES)
+ @rm -f help_h_gen$(EXEEXT)
+ $(LINK) $(help_h_gen_OBJECTS) $(help_h_gen_LDADD)
+
+helptext.c: help_c_gen pine.hlp
+ ./help_c_gen < pine.hlp > $@
+
+helptext.h: help_h_gen pine.hlp
+ ./help_h_gen < pine.hlp > $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pith/abdlc.c b/pith/abdlc.c
new file mode 100644
index 00000000..d332a1b7
--- /dev/null
+++ b/pith/abdlc.c
@@ -0,0 +1,2066 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: abdlc.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h" /* for os-dep and pith defs/includes */
+#include "../pith/abdlc.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/status.h"
+#include "../pith/ldap.h"
+#include "../pith/tempfile.h"
+
+
+/*
+ * Internal prototypes
+ */
+void initialize_dlc_cache(void);
+int dlc_siblings(DL_CACHE_S *, DL_CACHE_S *);
+DL_CACHE_S *dlc_mgr(long, DlMgrOps, DL_CACHE_S *);
+void free_cache_array(DL_CACHE_S **, int);
+DL_CACHE_S *dlc_prev(DL_CACHE_S *, DL_CACHE_S *);
+DL_CACHE_S *dlc_next(DL_CACHE_S *, DL_CACHE_S *);
+DL_CACHE_S *get_global_top_dlc(DL_CACHE_S *);
+DL_CACHE_S *get_global_bottom_dlc(DL_CACHE_S *);
+DL_CACHE_S *get_top_dl_of_adrbk(int, DL_CACHE_S *);
+DL_CACHE_S *get_bottom_dl_of_adrbk(int, DL_CACHE_S *);
+
+
+#define NO_PERMISSION _("[ Permission Denied ]")
+/* TRANSLATORS: This is a heading referring to something that is readable
+ but not writeable. */
+#define READONLY _(" (ReadOnly)")
+/* TRANSLATORS: Not readable */
+#define NOACCESS _(" (Un-readable)")
+/* TRANSLATORS: Directories, as in LDAP Directories. A heading. */
+#define OLDSTYLE_DIR_TITLE _("Directories")
+
+
+/* data for the display list cache */
+static DL_CACHE_S *cache_array = (DL_CACHE_S *)NULL;
+static long valid_low,
+ valid_high;
+static int index_of_low,
+ size_of_cache,
+ n_cached;
+
+
+void
+initialize_dlc_cache(void)
+{
+ dprint((11, "- initialize_dlc_cache -\n"));
+
+ (void)dlc_mgr(NO_LINE, Initialize, (DL_CACHE_S *)NULL);
+}
+
+
+void
+done_with_dlc_cache(void)
+{
+ dprint((11, "- done_with_dlc_cache -\n"));
+
+ (void)dlc_mgr(NO_LINE, DoneWithCache, (DL_CACHE_S *)NULL);
+}
+
+
+/*
+ * Returns 1 if the dlc's are related to each other, 0 otherwise.
+ *
+ * The idea is that if you are going to flush one of these dlcs from the
+ * cache you should also flush its partners. For example, if you flush one
+ * Listent from a list you should flush the entire entry including all the
+ * Listents and the ListHead. If you flush a DlcTitle, you should also
+ * flush the SubTitle and the TitleBlankTop.
+ */
+int
+dlc_siblings(DL_CACHE_S *dlc1, DL_CACHE_S *dlc2)
+{
+ if(!dlc1 || !dlc2 || dlc1->adrbk_num != dlc2->adrbk_num)
+ return 0;
+
+ switch(dlc1->type){
+
+ case DlcSimple:
+ case DlcListHead:
+ case DlcListBlankTop:
+ case DlcListBlankBottom:
+ case DlcListEnt:
+ case DlcListClickHere:
+ case DlcListEmpty:
+ switch(dlc2->type){
+ case DlcSimple:
+ case DlcListHead:
+ case DlcListBlankTop:
+ case DlcListBlankBottom:
+ case DlcListEnt:
+ case DlcListClickHere:
+ case DlcListEmpty:
+ return(dlc1->dlcelnum == dlc2->dlcelnum);
+
+ default:
+ return 0;
+ }
+
+ break;
+
+ case DlcDirDelim1:
+ case DlcDirDelim2:
+ switch(dlc2->type){
+ case DlcDirDelim1:
+ case DlcDirDelim2:
+ return 1;
+
+ default:
+ return 0;
+ }
+ break;
+
+ case DlcGlobDelim1:
+ case DlcGlobDelim2:
+ switch(dlc2->type){
+ case DlcGlobDelim1:
+ case DlcGlobDelim2:
+ return 1;
+
+ default:
+ return 0;
+ }
+ break;
+
+ case DlcTitle:
+ case DlcTitleNoPerm:
+ case DlcSubTitle:
+ case DlcTitleBlankTop:
+ switch(dlc2->type){
+ case DlcTitle:
+ case DlcTitleNoPerm:
+ case DlcSubTitle:
+ case DlcTitleBlankTop:
+ return 1;
+
+ default:
+ return 0;
+ }
+ break;
+
+ case DlcDirAccess:
+ case DlcDirSubTitle:
+ case DlcDirBlankTop:
+ switch(dlc2->type){
+ case DlcDirAccess:
+ case DlcDirSubTitle:
+ case DlcDirBlankTop:
+ return 1;
+
+ default:
+ return 0;
+ }
+ break;
+
+ case DlcTitleDashTopCmb:
+ case DlcTitleCmb:
+ case DlcTitleDashBottomCmb:
+ case DlcTitleBlankBottomCmb:
+ case DlcClickHereCmb:
+ case DlcTitleBlankTopCmb:
+ switch(dlc2->type){
+ case DlcTitleDashTopCmb:
+ case DlcTitleCmb:
+ case DlcTitleDashBottomCmb:
+ case DlcTitleBlankBottomCmb:
+ case DlcClickHereCmb:
+ case DlcTitleBlankTopCmb:
+ return 1;
+
+ default:
+ return 0;
+ }
+ break;
+
+ default:
+ return 0;
+ }
+ /*NOTREACHED*/
+}
+
+
+/*
+ * Manage the display list cache.
+ *
+ * The cache is a circular array of DL_CACHE_S elements. It always
+ * contains a contiguous set of display lines.
+ * The lowest numbered line in the cache is
+ * valid_low, and the highest is valid_high. Everything in between is
+ * also valid. Index_of_low is where to look
+ * for the valid_low element in the circular array.
+ *
+ * We make calls to dlc_prev and dlc_next to get new entries for the cache.
+ * We need a starting value before we can do that.
+ *
+ * This returns a pointer to a dlc for the desired row. If you want the
+ * actual display list line you call dlist(row) instead of dlc_mgr.
+ */
+DL_CACHE_S *
+dlc_mgr(long int row, DlMgrOps op, DL_CACHE_S *dlc_start)
+{
+ int new_index, known_index, next_index;
+ DL_CACHE_S *dlc = (DL_CACHE_S *)NULL;
+ long next_row;
+ long prev_row;
+
+
+ if(op == Lookup){
+
+ if(row >= valid_low && row <= valid_high){ /* already cached */
+
+ new_index = ((row - valid_low) + index_of_low) % size_of_cache;
+ dlc = &cache_array[new_index];
+
+ }
+ else if(row > valid_high){ /* row is past where we've looked */
+
+ known_index =
+ ((valid_high - valid_low) + index_of_low) % size_of_cache;
+ next_row = valid_high + 1L;
+
+ /* we'll usually be looking for row = valid_high + 1 */
+ while(next_row <= row){
+
+ new_index = (known_index + 1) % size_of_cache;
+
+ dlc =
+ dlc_next(&cache_array[known_index], &cache_array[new_index]);
+
+ /*
+ * This means somebody changed the file out from underneath
+ * us. This would happen if dlc_next needed to ask for an
+ * abe to figure out what the type of the next row is, but
+ * adrbk_get_ae returned a NULL. I don't think that can
+ * happen, but if it does...
+ */
+ if(dlc->type == DlcNotSet){
+ dprint((1, "dlc_next returned DlcNotSet\n"));
+ goto panic_abook_corrupt;
+ }
+
+ if(n_cached == size_of_cache){ /* replaced low cache entry */
+ valid_low++;
+ index_of_low = (index_of_low + 1) % size_of_cache;
+ }
+ else
+ n_cached++;
+
+ valid_high++;
+
+ next_row++;
+ known_index = new_index; /* for next time through loop */
+ }
+ }
+ else if(row < valid_low){ /* row is back up the screen */
+
+ known_index = index_of_low;
+ prev_row = valid_low - 1L;
+
+ while(prev_row >= row){
+
+ new_index = (known_index - 1 + size_of_cache) % size_of_cache;
+ dlc =
+ dlc_prev(&cache_array[known_index], &cache_array[new_index]);
+
+ if(dlc->type == DlcNotSet){
+ dprint((1, "dlc_prev returned DlcNotSet (1)\n"));
+ goto panic_abook_corrupt;
+ }
+
+ if(n_cached == size_of_cache) /* replaced high cache entry */
+ valid_high--;
+ else
+ n_cached++;
+
+ valid_low--;
+ index_of_low =
+ (index_of_low - 1 + size_of_cache) % size_of_cache;
+
+ prev_row--;
+ known_index = new_index;
+ }
+ }
+ }
+ else if(op == Initialize){
+
+ n_cached = 0;
+
+ if(!cache_array || size_of_cache != 3 * MAX(as.l_p_page,1)){
+ if(cache_array)
+ free_cache_array(&cache_array, size_of_cache);
+
+ size_of_cache = 3 * MAX(as.l_p_page,1);
+ cache_array =
+ (DL_CACHE_S *)fs_get(size_of_cache * sizeof(DL_CACHE_S));
+ memset((void *)cache_array, 0, size_of_cache * sizeof(DL_CACHE_S));
+ }
+
+ /* this will return NULL below and the caller should ignore that */
+ }
+ /*
+ * Flush all rows for a particular addrbook entry from the cache, but
+ * keep the cache alive and anchored in the same place. The particular
+ * entry is the one that dlc_start is one of the rows of.
+ */
+ else if(op == FlushDlcFromCache){
+ long low_entry;
+
+ next_row = dlc_start->global_row - 1;
+ for(; next_row >= valid_low; next_row--){
+ next_index = ((next_row - valid_low) + index_of_low) %
+ size_of_cache;
+ if(!dlc_siblings(dlc_start, &cache_array[next_index]))
+ break;
+ }
+
+ low_entry = next_row + 1L;
+
+ /*
+ * If low_entry now points one past a ListBlankBottom, delete that,
+ * too, since it may not make sense anymore.
+ */
+ if(low_entry > valid_low){
+ next_index = ((low_entry -1L - valid_low) + index_of_low) %
+ size_of_cache;
+ if(cache_array[next_index].type == DlcListBlankBottom)
+ low_entry--;
+ }
+
+ if(low_entry > valid_low){ /* invalidate everything >= this */
+ n_cached -= (valid_high - (low_entry - 1L));
+ valid_high = low_entry - 1L;
+ }
+ else{
+ /*
+ * This is the tough case. That entry was the first thing cached,
+ * so we need to invalidate the whole cache. However, we also
+ * need to keep at least one thing cached for an anchor, so
+ * we need to get the dlc before this one and it should be a
+ * dlc not related to this same addrbook entry.
+ */
+ known_index = index_of_low;
+ prev_row = valid_low - 1L;
+
+ for(;;){
+
+ new_index = (known_index - 1 + size_of_cache) % size_of_cache;
+ dlc =
+ dlc_prev(&cache_array[known_index], &cache_array[new_index]);
+
+ if(dlc->type == DlcNotSet){
+ dprint((1, "dlc_prev returned DlcNotSet (2)\n"));
+ goto panic_abook_corrupt;
+ }
+
+ valid_low--;
+ index_of_low =
+ (index_of_low - 1 + size_of_cache) % size_of_cache;
+
+ if(!dlc_siblings(dlc_start, dlc))
+ break;
+
+ known_index = new_index;
+ }
+
+ n_cached = 1;
+ valid_high = valid_low;
+ }
+ }
+ /*
+ * We have to anchor ourselves at a first element.
+ * Here's how we start at the top.
+ */
+ else if(op == FirstEntry){
+ initialize_dlc_cache();
+ n_cached++;
+ dlc = &cache_array[0];
+ dlc = get_global_top_dlc(dlc);
+ dlc->global_row = row;
+ index_of_low = 0;
+ valid_low = row;
+ valid_high = row;
+ }
+ /* And here's how we start from the bottom. */
+ else if(op == LastEntry){
+ initialize_dlc_cache();
+ n_cached++;
+ dlc = &cache_array[0];
+ dlc = get_global_bottom_dlc(dlc);
+ dlc->global_row = row;
+ index_of_low = 0;
+ valid_low = row;
+ valid_high = row;
+ }
+ /*
+ * And here's how we start from an arbitrary position in the middle.
+ * We root the cache at display line row, so it helps if row is close
+ * to where we're going to be starting so that things are easy to find.
+ * The dl that goes with line row is dl_start from addrbook number
+ * adrbk_num_start.
+ */
+ else if(op == ArbitraryStartingPoint){
+ AddrScrn_Disp dl;
+
+ initialize_dlc_cache();
+ n_cached++;
+ dlc = &cache_array[0];
+ /*
+ * Save this in case fill_in_dl_field needs to free the text
+ * it points to.
+ */
+ dl = dlc->dl;
+ *dlc = *dlc_start;
+ dlc->dl = dl;
+ dlc->global_row = row;
+
+ index_of_low = 0;
+ valid_low = row;
+ valid_high = row;
+ }
+ else if(op == DoneWithCache){
+
+ n_cached = 0;
+ if(cache_array)
+ free_cache_array(&cache_array, size_of_cache);
+ }
+
+ return(dlc);
+
+panic_abook_corrupt:
+ q_status_message(SM_ORDER | SM_DING, 5, 10,
+ _("Addrbook changed unexpectedly, re-syncing..."));
+ dprint((1,
+ _("addrbook changed while we had it open?, re-sync\n")));
+ dprint((2,
+ "valid_low=%ld valid_high=%ld index_of_low=%d size_of_cache=%d\n",
+ valid_low, valid_high, index_of_low, size_of_cache));
+ dprint((2,
+ "n_cached=%d new_index=%d known_index=%d next_index=%d\n",
+ n_cached, new_index, known_index, next_index));
+ dprint((2,
+ "next_row=%ld prev_row=%ld row=%ld\n", next_row, prev_row, row));
+ /* jump back to a safe starting point */
+ longjmp(addrbook_changed_unexpectedly, 1);
+ /*NOTREACHED*/
+}
+
+
+void
+free_cache_array(DL_CACHE_S **c_array, int size)
+{
+ DL_CACHE_S *dlc;
+ int i;
+
+ for(i = 0; i < size; i++){
+ dlc = &(*c_array)[i];
+ /* free any allocated space */
+ switch(dlc->dl.type){
+ case Text:
+ case Title:
+ case TitleCmb:
+ case AskServer:
+ if(dlc->dl.usst)
+ fs_give((void **)&dlc->dl.usst);
+
+ break;
+ default:
+ break;
+ }
+ }
+
+ fs_give((void **)c_array);
+}
+
+
+/*
+ * Get the dlc element that comes before "old". The function that calls this
+ * function is the one that keeps a cache and checks in the cache before
+ * calling here. New is a passed in pointer to a buffer where we fill in
+ * the answer.
+ */
+DL_CACHE_S *
+dlc_prev(DL_CACHE_S *old, DL_CACHE_S *new)
+{
+ PerAddrBook *pab;
+ AdrBk_Entry *abe;
+ adrbk_cntr_t list_count;
+
+ new->adrbk_num = -2;
+ new->dlcelnum = NO_NEXT;
+ new->dlcoffset = NO_NEXT;
+ new->type = DlcNotSet;
+ pab = &as.adrbks[old->adrbk_num];
+
+ switch(old->type){
+ case DlcTitle:
+ case DlcTitleNoPerm:
+ if(old->adrbk_num == 0 && as.config && as.how_many_personals == 0)
+ new->type = DlcGlobDelim2;
+ else if(old->adrbk_num == 0)
+ new->type = DlcOneBeforeBeginning;
+ else if(old->adrbk_num == as.how_many_personals)
+ new->type = DlcGlobDelim2;
+ else
+ new->type = DlcTitleBlankTop;
+
+ break;
+
+ case DlcSubTitle:
+ if(pab->access == NoAccess)
+ new->type = DlcTitleNoPerm;
+ else
+ new->type = DlcTitle;
+
+ break;
+
+ case DlcTitleBlankTop:
+ new->adrbk_num = old->adrbk_num - 1;
+ new->type = DlcSubTitle;
+ break;
+
+ case DlcEmpty:
+ case DlcZoomEmpty:
+ case DlcNoPermission:
+ case DlcNoAbooks:
+ if(F_ON(F_CMBND_ABOOK_DISP,ps_global)){
+ if(as.n_addrbk == 1 && as.n_serv == 0)
+ new->type = DlcOneBeforeBeginning;
+ else
+ new->type = DlcTitleBlankBottomCmb;
+ }
+ else
+ new->type = DlcOneBeforeBeginning;
+
+ break;
+
+ case DlcSimple:
+ {
+ adrbk_cntr_t el;
+ long i;
+
+ i = old->dlcelnum;
+ i--;
+ el = old->dlcelnum - 1;
+ while(i >= 0L){
+ if(!as.zoomed || entry_is_selected(pab->address_book->selects,
+ (a_c_arg_t)el))
+ break;
+
+ el--;
+ i--;
+ }
+
+ if(i >= 0){
+ new->dlcelnum = el;
+ abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) new->dlcelnum);
+ if(abe && abe->tag == Single)
+ new->type = DlcSimple;
+ else if(abe && abe->tag == List)
+ new->type = DlcListBlankBottom;
+ }
+ else if(F_ON(F_CMBND_ABOOK_DISP,ps_global)){
+ if(as.n_addrbk == 1 && as.n_serv == 0)
+ new->type = DlcOneBeforeBeginning;
+ else
+ new->type = DlcTitleBlankBottomCmb;
+ }
+ else
+ new->type = DlcOneBeforeBeginning;
+ }
+
+ break;
+
+ case DlcListHead:
+ {
+ adrbk_cntr_t el;
+ long i;
+
+ i = old->dlcelnum;
+ i--;
+ el = old->dlcelnum - 1;
+ while(i >= 0L){
+ if(!as.zoomed || entry_is_selected(pab->address_book->selects,
+ (a_c_arg_t)el))
+ break;
+
+ el--;
+ i--;
+ }
+
+ if(i >= 0){
+ new->dlcelnum = el;
+ abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) new->dlcelnum);
+ if(abe && abe->tag == Single){
+ new->type = DlcListBlankTop;
+ new->dlcelnum = old->dlcelnum;
+ }
+ else if(abe && abe->tag == List)
+ new->type = DlcListBlankBottom;
+ }
+ else if(F_ON(F_CMBND_ABOOK_DISP,ps_global)){
+ if(as.n_addrbk == 1 && as.n_serv == 0)
+ new->type = DlcOneBeforeBeginning;
+ else
+ new->type = DlcTitleBlankBottomCmb;
+ }
+ else
+ new->type = DlcOneBeforeBeginning;
+ }
+
+ break;
+
+ case DlcListEnt:
+ if(old->dlcoffset > 0){
+ new->type = DlcListEnt;
+ new->dlcelnum = old->dlcelnum;
+ new->dlcoffset = old->dlcoffset - 1;
+ }
+ else{
+ new->type = DlcListHead;
+ new->dlcelnum = old->dlcelnum;
+ }
+
+ break;
+
+ case DlcListClickHere:
+ case DlcListEmpty:
+ new->type = DlcListHead;
+ new->dlcelnum = old->dlcelnum;
+ break;
+
+ case DlcListBlankTop: /* can only occur between a Simple and a List */
+ new->type = DlcSimple;
+ {
+ adrbk_cntr_t el;
+ long i;
+
+ i = old->dlcelnum;
+ i--;
+ el = old->dlcelnum - 1;
+ while(i >= 0L){
+ if(!as.zoomed || entry_is_selected(pab->address_book->selects,
+ (a_c_arg_t)el))
+ break;
+
+ el--;
+ i--;
+ }
+
+ if(i >= 0)
+ new->dlcelnum = el;
+ else{
+ dprint((1, "Bug in addrbook: case ListBlankTop with no selected entry\n"));
+ goto oops;
+ }
+ }
+
+ break;
+
+ case DlcListBlankBottom:
+ new->dlcelnum = old->dlcelnum;
+ abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) new->dlcelnum);
+ if(F_ON(F_EXPANDED_DISTLISTS,ps_global)
+ || exp_is_expanded(pab->address_book->exp, (a_c_arg_t)new->dlcelnum)){
+ list_count = listmem_count_from_abe(abe);
+ if(list_count == 0)
+ new->type = DlcListEmpty;
+ else{
+ new->type = DlcListEnt;
+ new->dlcoffset = list_count - 1;
+ }
+ }
+ else
+ new->type = DlcListClickHere;
+
+ break;
+
+ case DlcGlobDelim1:
+ if(as.how_many_personals == 0)
+ new->type = DlcPersAdd;
+ else{
+ new->adrbk_num = as.how_many_personals - 1;
+ new->type = DlcSubTitle;
+ }
+ break;
+
+ case DlcGlobDelim2:
+ new->type = DlcGlobDelim1;
+ break;
+
+ case DlcPersAdd:
+ new->type = DlcOneBeforeBeginning;
+ break;
+
+ case DlcGlobAdd:
+ new->type = DlcGlobDelim2;
+ break;
+
+ case DlcDirAccess:
+ if(old->adrbk_num == 0 && as.n_addrbk == 0)
+ new->type = DlcOneBeforeBeginning;
+ else if(old->adrbk_num == 0)
+ new->type = DlcDirDelim2;
+ else
+ new->type = DlcDirBlankTop;
+
+ break;
+
+ case DlcDirSubTitle:
+ new->type = DlcDirAccess;
+ break;
+
+ case DlcDirBlankTop:
+ new->adrbk_num = old->adrbk_num -1;
+ new->type = DlcDirSubTitle;
+ break;
+
+ case DlcDirDelim1:
+ new->adrbk_num = as.n_addrbk - 1;
+ if(as.n_addrbk == 0)
+ new->type = DlcNoAbooks;
+ else
+ new = get_bottom_dl_of_adrbk(new->adrbk_num, new);
+
+ break;
+
+ case DlcDirDelim1a:
+ new->type = DlcDirDelim1;
+ break;
+
+ case DlcDirDelim1b:
+ new->type = DlcDirDelim1a;
+ break;
+
+ case DlcDirDelim1c:
+ new->type = DlcDirDelim1b;
+ break;
+
+ case DlcDirDelim2:
+ if(F_ON(F_CMBND_ABOOK_DISP,ps_global))
+ new->type = DlcDirDelim1c;
+ else
+ new->type = DlcDirDelim1;
+
+ break;
+
+ case DlcTitleDashTopCmb:
+ if(old->adrbk_num == 0)
+ new->type = DlcOneBeforeBeginning;
+ else
+ new->type = DlcTitleBlankTopCmb;
+
+ break;
+
+ case DlcTitleCmb:
+ new->type = DlcTitleDashTopCmb;
+ break;
+
+ case DlcTitleDashBottomCmb:
+ new->type = DlcTitleCmb;
+ break;
+
+ case DlcTitleBlankBottomCmb:
+ new->type = DlcTitleDashBottomCmb;
+ break;
+
+ case DlcClickHereCmb:
+ if(as.n_addrbk == 1 && as.n_serv == 0)
+ new->type = DlcOneBeforeBeginning;
+ else
+ new->type = DlcTitleBlankBottomCmb;
+
+ break;
+
+ case DlcTitleBlankTopCmb:
+ new->adrbk_num = old->adrbk_num - 1;
+ new = get_bottom_dl_of_adrbk(new->adrbk_num, new);
+ break;
+
+ case DlcBeginning:
+ case DlcTwoBeforeBeginning:
+ new->type = DlcBeginning;
+ break;
+
+ case DlcOneBeforeBeginning:
+ new->type = DlcTwoBeforeBeginning;
+ break;
+
+oops:
+ default:
+ q_status_message(SM_ORDER | SM_DING, 5, 10,
+ _("Bug in addrbook, not supposed to happen, re-syncing..."));
+ dprint((1, "Bug in addrbook, impossible case (%d) in dlc_prev, re-sync\n",
+ old->type));
+ /* jump back to a safe starting point */
+ longjmp(addrbook_changed_unexpectedly, 1);
+ /*NOTREACHED*/
+ }
+
+ new->global_row = old->global_row - 1L;
+ if(new->adrbk_num == -2)
+ new->adrbk_num = old->adrbk_num;
+
+ return(new);
+}
+
+
+/*
+ * Get the dlc element that comes after "old". The function that calls this
+ * function is the one that keeps a cache and checks in the cache before
+ * calling here.
+ */
+DL_CACHE_S *
+dlc_next(DL_CACHE_S *old, DL_CACHE_S *new)
+{
+ PerAddrBook *pab;
+ AdrBk_Entry *abe;
+ adrbk_cntr_t ab_count;
+ adrbk_cntr_t list_count;
+
+ new->adrbk_num = -2;
+ new->dlcelnum = NO_NEXT;
+ new->dlcoffset = NO_NEXT;
+ new->type = DlcNotSet;
+ pab = &as.adrbks[old->adrbk_num];
+
+ switch(old->type){
+ case DlcTitle:
+ case DlcTitleNoPerm:
+ new->type = DlcSubTitle;
+ break;
+
+ case DlcSubTitle:
+ if((old->adrbk_num == as.how_many_personals - 1) &&
+ (as.config || as.n_addrbk > as.how_many_personals))
+ new->type = DlcGlobDelim1;
+ else if(as.n_serv && !as.config &&
+ (old->adrbk_num == as.n_addrbk - 1))
+ new->type = DlcDirDelim1;
+ else if(old->adrbk_num == as.n_addrbk - 1)
+ new->type = DlcEnd;
+ else{
+ new->adrbk_num = old->adrbk_num + 1;
+ new->type = DlcTitleBlankTop;
+ }
+
+ break;
+
+ case DlcTitleBlankTop:
+ if(pab->access == NoAccess)
+ new->type = DlcTitleNoPerm;
+ else
+ new->type = DlcTitle;
+
+ break;
+
+ case DlcEmpty:
+ case DlcZoomEmpty:
+ case DlcNoPermission:
+ case DlcClickHereCmb:
+ if(F_ON(F_CMBND_ABOOK_DISP,ps_global)){
+ if(old->adrbk_num < as.n_addrbk - 1){
+ new->adrbk_num = old->adrbk_num + 1;
+ new->type = DlcTitleBlankTopCmb;
+ }
+ else if(as.n_serv)
+ new->type = DlcDirDelim1;
+ else
+ new->type = DlcEnd;
+ }
+ else
+ new->type = DlcEnd;
+
+ break;
+
+ case DlcNoAbooks:
+ case DlcGlobAdd:
+ case DlcEnd:
+ new->type = DlcEnd;
+ break;
+
+ case DlcSimple:
+ {
+ adrbk_cntr_t el;
+ long i;
+
+ ab_count = adrbk_count(pab->address_book);
+ i = old->dlcelnum;
+ i++;
+ el = old->dlcelnum + 1;
+ while(i < ab_count){
+ if(!as.zoomed || entry_is_selected(pab->address_book->selects,
+ (a_c_arg_t)el))
+ break;
+
+ el++;
+ i++;
+ }
+
+ if(i < ab_count){
+ new->dlcelnum = el;
+ abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) new->dlcelnum);
+ if(abe && abe->tag == Single)
+ new->type = DlcSimple;
+ else if(abe && abe->tag == List)
+ new->type = DlcListBlankTop;
+ }
+ else if(F_ON(F_CMBND_ABOOK_DISP,ps_global)){
+ if(old->adrbk_num < as.n_addrbk - 1){
+ new->adrbk_num = old->adrbk_num + 1;
+ new->type = DlcTitleBlankTopCmb;
+ }
+ else if(as.n_serv)
+ new->type = DlcDirDelim1;
+ else
+ new->type = DlcEnd;
+ }
+ else
+ new->type = DlcEnd;
+ }
+
+ break;
+
+ case DlcListHead:
+ new->dlcelnum = old->dlcelnum;
+ abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) new->dlcelnum);
+ if(F_ON(F_EXPANDED_DISTLISTS,ps_global)
+ || exp_is_expanded(pab->address_book->exp, (a_c_arg_t)new->dlcelnum)){
+ list_count = listmem_count_from_abe(abe);
+ if(list_count == 0)
+ new->type = DlcListEmpty;
+ else{
+ new->type = DlcListEnt;
+ new->dlcoffset = 0;
+ }
+ }
+ else
+ new->type = DlcListClickHere;
+
+ break;
+
+ case DlcListEnt:
+ new->dlcelnum = old->dlcelnum;
+ abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) new->dlcelnum);
+ list_count = listmem_count_from_abe(abe);
+ if(old->dlcoffset == list_count - 1){ /* last member of list */
+ adrbk_cntr_t el;
+ long i;
+
+ ab_count = adrbk_count(pab->address_book);
+ i = old->dlcelnum;
+ i++;
+ el = old->dlcelnum + 1;
+ while(i < ab_count){
+ if(!as.zoomed || entry_is_selected(pab->address_book->selects,
+ (a_c_arg_t)el))
+ break;
+
+ el++;
+ i++;
+ }
+
+ if(i < ab_count)
+ new->type = DlcListBlankBottom;
+ else if(F_ON(F_CMBND_ABOOK_DISP,ps_global)){
+ if(old->adrbk_num < as.n_addrbk - 1){
+ new->adrbk_num = old->adrbk_num + 1;
+ new->type = DlcTitleBlankTopCmb;
+ }
+ else if(as.n_serv)
+ new->type = DlcDirDelim1;
+ else
+ new->type = DlcEnd;
+ }
+ else
+ new->type = DlcEnd;
+ }
+ else{
+ new->type = DlcListEnt;
+ new->dlcoffset = old->dlcoffset + 1;
+ }
+
+ break;
+
+ case DlcListClickHere:
+ case DlcListEmpty:
+ {
+ adrbk_cntr_t el;
+ long i;
+
+ new->dlcelnum = old->dlcelnum;
+ ab_count = adrbk_count(pab->address_book);
+ i = old->dlcelnum;
+ i++;
+ el = old->dlcelnum + 1;
+ while(i < ab_count){
+ if(!as.zoomed || entry_is_selected(pab->address_book->selects,
+ (a_c_arg_t)el))
+ break;
+
+ el++;
+ i++;
+ }
+
+ if(i < ab_count)
+ new->type = DlcListBlankBottom;
+ else if(F_ON(F_CMBND_ABOOK_DISP,ps_global)){
+ if(old->adrbk_num < as.n_addrbk - 1){
+ new->adrbk_num = old->adrbk_num + 1;
+ new->type = DlcTitleBlankTopCmb;
+ }
+ else if(as.n_serv)
+ new->type = DlcDirDelim1;
+ else
+ new->type = DlcEnd;
+ }
+ else
+ new->type = DlcEnd;
+ }
+
+ break;
+
+ case DlcListBlankTop:
+ new->type = DlcListHead;
+ new->dlcelnum = old->dlcelnum;
+ break;
+
+ case DlcListBlankBottom:
+ {
+ adrbk_cntr_t el;
+ long i;
+
+ ab_count = adrbk_count(pab->address_book);
+ i = old->dlcelnum;
+ i++;
+ el = old->dlcelnum + 1;
+ while(i < ab_count){
+ if(!as.zoomed || entry_is_selected(pab->address_book->selects,
+ (a_c_arg_t)el))
+ break;
+
+ el++;
+ i++;
+ }
+
+ if(i < ab_count){
+ new->dlcelnum = el;
+ abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) new->dlcelnum);
+ if(abe && abe->tag == Single)
+ new->type = DlcSimple;
+ else if(abe && abe->tag == List)
+ new->type = DlcListHead;
+ }
+ else
+ /* can't happen */
+ new->type = DlcEnd;
+ }
+
+ break;
+
+ case DlcGlobDelim1:
+ new->type = DlcGlobDelim2;
+ break;
+
+ case DlcGlobDelim2:
+ if(as.config && as.how_many_personals == as.n_addrbk)
+ new->type = DlcGlobAdd;
+ else{
+ new->adrbk_num = as.how_many_personals;
+ pab = &as.adrbks[new->adrbk_num];
+ if(pab->access == NoAccess)
+ new->type = DlcTitleNoPerm;
+ else
+ new->type = DlcTitle;
+ }
+
+ break;
+
+ case DlcDirDelim1:
+ if(F_ON(F_CMBND_ABOOK_DISP,ps_global))
+ new->type = DlcDirDelim1a;
+ else
+ new->type = DlcDirDelim2;
+
+ new->adrbk_num = 0;
+ break;
+
+ case DlcDirDelim1a:
+ new->type = DlcDirDelim1b;
+ break;
+
+ case DlcDirDelim1b:
+ new->type = DlcDirDelim1c;
+ break;
+
+ case DlcDirDelim1c:
+ new->type = DlcDirDelim2;
+ new->adrbk_num = 0;
+ break;
+
+ case DlcDirDelim2:
+ new->type = DlcDirAccess;
+ new->adrbk_num = 0;
+ break;
+
+ case DlcPersAdd:
+ new->type = DlcGlobDelim1;
+ break;
+
+ case DlcDirAccess:
+ new->type = DlcDirSubTitle;
+ break;
+
+ case DlcDirSubTitle:
+ if(old->adrbk_num == as.n_serv - 1)
+ new->type = DlcEnd;
+ else{
+ new->type = DlcDirBlankTop;
+ new->adrbk_num = old->adrbk_num + 1;
+ }
+
+ break;
+
+ case DlcDirBlankTop:
+ new->type = DlcDirAccess;
+ break;
+
+ case DlcTitleDashTopCmb:
+ new->type = DlcTitleCmb;
+ break;
+
+ case DlcTitleCmb:
+ new->type = DlcTitleDashBottomCmb;
+ break;
+
+ case DlcTitleDashBottomCmb:
+ new->type = DlcTitleBlankBottomCmb;
+ break;
+
+ case DlcTitleBlankBottomCmb:
+ pab = &as.adrbks[old->adrbk_num];
+ if(pab->ostatus != Open && pab->access != NoAccess)
+ new->type = DlcClickHereCmb;
+ else
+ new = get_top_dl_of_adrbk(old->adrbk_num, new);
+
+ break;
+
+ case DlcTitleBlankTopCmb:
+ new->type = DlcTitleDashTopCmb;
+ break;
+
+ case DlcOneBeforeBeginning:
+ new = get_global_top_dlc(new);
+ break;
+
+ case DlcTwoBeforeBeginning:
+ new->type = DlcOneBeforeBeginning;
+ break;
+
+ default:
+ q_status_message(SM_ORDER | SM_DING, 5, 10,
+ _("Bug in addrbook, not supposed to happen, re-syncing..."));
+ dprint((1, "Bug in addrbook, impossible case (%d) in dlc_next, re-sync\n",
+ old->type));
+ /* jump back to a safe starting point */
+ longjmp(addrbook_changed_unexpectedly, 1);
+ /*NOTREACHED*/
+ }
+
+ new->global_row = old->global_row + 1L;
+ if(new->adrbk_num == -2)
+ new->adrbk_num = old->adrbk_num;
+
+ return(new);
+}
+
+
+/*
+ * Get the display line at the very top of whole addrbook screen display.
+ */
+DL_CACHE_S *
+get_global_top_dlc(DL_CACHE_S *new)
+ /* fill in answer here */
+{
+ PerAddrBook *pab;
+
+ new->dlcelnum = NO_NEXT;
+ new->dlcoffset = NO_NEXT;
+ new->type = DlcNotSet;
+
+ if(F_ON(F_CMBND_ABOOK_DISP,ps_global) && !as.config){
+ new->adrbk_num = 0;
+ if(as.n_addrbk > 1 || (as.n_serv && as.n_addrbk == 1))
+ new->type = DlcTitleDashTopCmb;
+ else if(as.n_addrbk == 1)
+ new = get_top_dl_of_adrbk(new->adrbk_num, new);
+ else if(as.n_serv > 0){ /* 1st directory */
+ new->type = DlcDirAccess;
+ new->adrbk_num = 0;
+ }
+ else
+ new->type = DlcNoAbooks;
+ }
+ else{
+ new->adrbk_num = 0;
+
+ if(as.config){
+ if(as.how_many_personals == 0) /* no personals */
+ new->type = DlcPersAdd;
+ else{
+ pab = &as.adrbks[new->adrbk_num]; /* 1st personal */
+ if(pab->access == NoAccess)
+ new->type = DlcTitleNoPerm;
+ else
+ new->type = DlcTitle;
+ }
+ }
+ else if(any_ab_open()){
+ new->adrbk_num = as.cur;
+ new = get_top_dl_of_adrbk(new->adrbk_num, new);
+ }
+ else if(as.n_addrbk > 0){ /* 1st addrbook */
+ pab = &as.adrbks[new->adrbk_num];
+ if(pab->access == NoAccess)
+ new->type = DlcTitleNoPerm;
+ else
+ new->type = DlcTitle;
+ }
+ else if(as.n_serv > 0){ /* 1st directory */
+ new->type = DlcDirAccess;
+ new->adrbk_num = 0;
+ }
+ else
+ new->type = DlcNoAbooks;
+ }
+
+ return(new);
+}
+
+
+/*
+ * Get the last display line for the whole address book screen.
+ * This gives us a way to start at the end and move back up.
+ */
+DL_CACHE_S *
+get_global_bottom_dlc(DL_CACHE_S *new)
+ /* fill in answer here */
+{
+ new->dlcelnum = NO_NEXT;
+ new->dlcoffset = NO_NEXT;
+ new->type = DlcNotSet;
+
+ if(F_ON(F_CMBND_ABOOK_DISP,ps_global) && !as.config){
+ if(as.n_serv){
+ new->type = DlcDirSubTitle;
+ new->adrbk_num = as.n_serv - 1;
+ }
+ else if(as.n_addrbk > 0){
+ new->adrbk_num = as.n_addrbk - 1;
+ new = get_bottom_dl_of_adrbk(new->adrbk_num, new);
+ }
+ else
+ new->type = DlcNoAbooks;
+ }
+ else{
+ new->adrbk_num = MAX(as.n_addrbk - 1, 0);
+
+ if(as.config){
+ if(as.how_many_personals == as.n_addrbk) /* no globals */
+ new->type = DlcGlobAdd;
+ else
+ new->type = DlcSubTitle;
+ }
+ else if(any_ab_open()){
+ new->adrbk_num = as.cur;
+ new = get_bottom_dl_of_adrbk(new->adrbk_num, new);
+ }
+ else if(as.n_serv){
+ new->type = DlcDirSubTitle;
+ new->adrbk_num = as.n_serv - 1;
+ }
+ else{ /* !config && !opened && !n_serv */
+ if(as.n_addrbk)
+ new->type = DlcSubTitle;
+ else
+ new->type = DlcNoAbooks;
+ }
+ }
+
+ return(new);
+}
+
+
+/*
+ * First dl in a particular addrbook, not counting title lines.
+ * Assumes as.opened.
+ */
+DL_CACHE_S *
+get_top_dl_of_adrbk(int adrbk_num, DL_CACHE_S *new)
+
+ /* fill in answer here */
+{
+ PerAddrBook *pab;
+ AdrBk_Entry *abe;
+ adrbk_cntr_t ab_count;
+
+ pab = &as.adrbks[adrbk_num];
+ new->adrbk_num = adrbk_num;
+
+ if(pab->access == NoAccess)
+ new->type = DlcNoPermission;
+ else{
+ adrbk_cntr_t el;
+ long i;
+
+ ab_count = adrbk_count(pab->address_book);
+
+ i = 0L;
+ el = 0;
+ /* find first displayed entry */
+ while(i < ab_count){
+ if(!as.zoomed || entry_is_selected(pab->address_book->selects,
+ (a_c_arg_t)el))
+ break;
+
+ el++;
+ i++;
+ }
+
+ if(i < ab_count){
+ new->dlcelnum = el;
+ abe = adrbk_get_ae(pab->address_book, (a_c_arg_t) new->dlcelnum);
+ if(abe && abe->tag == Single)
+ new->type = DlcSimple;
+ else if(abe && abe->tag == List)
+ new->type = DlcListHead;
+ }
+ else if(as.zoomed)
+ new->type = DlcZoomEmpty;
+ else
+ new->type = DlcEmpty;
+ }
+
+ return(new);
+}
+
+
+/*
+ * Find the last display line for addrbook number adrbk_num.
+ * Assumes as.opened (unless OLD_ABOOK_DISP).
+ */
+DL_CACHE_S *
+get_bottom_dl_of_adrbk(int adrbk_num, DL_CACHE_S *new)
+
+ /* fill in answer here */
+{
+ PerAddrBook *pab;
+ AdrBk_Entry *abe;
+ adrbk_cntr_t ab_count;
+ adrbk_cntr_t list_count;
+
+ pab = &as.adrbks[adrbk_num];
+ new->adrbk_num = adrbk_num;
+
+ if(F_ON(F_CMBND_ABOOK_DISP,ps_global) && pab->ostatus != Open){
+ if(pab->access == NoAccess)
+ new->type = DlcNoPermission;
+ else
+ new->type = DlcClickHereCmb;
+ }
+ else if(F_OFF(F_CMBND_ABOOK_DISP,ps_global) && pab->ostatus != Open){
+ new->type = DlcSubTitle;
+ }
+ else{
+ if(pab->access == NoAccess)
+ new->type = DlcNoPermission;
+ else{
+ adrbk_cntr_t el;
+ long i;
+
+ ab_count = adrbk_count(pab->address_book);
+ i = ab_count - 1;
+ el = ab_count - 1;
+ /* find last displayed entry */
+ while(i >= 0L){
+ if(!as.zoomed || entry_is_selected(pab->address_book->selects,
+ (a_c_arg_t)el))
+ break;
+
+ el--;
+ i--;
+ }
+
+ if(i >= 0){
+ new->dlcelnum = el;
+ abe = adrbk_get_ae(pab->address_book, (a_c_arg_t)new->dlcelnum);
+ if(abe && abe->tag == Single)
+ new->type = DlcSimple;
+ else if(abe && abe->tag == List){
+ if(F_ON(F_EXPANDED_DISTLISTS,ps_global)
+ || exp_is_expanded(pab->address_book->exp,
+ (a_c_arg_t)new->dlcelnum)){
+ list_count = listmem_count_from_abe(abe);
+ if(list_count == 0)
+ new->type = DlcListEmpty;
+ else{
+ new->type = DlcListEnt;
+ new->dlcoffset = list_count - 1;
+ }
+ }
+ else
+ new->type = DlcListClickHere;
+ }
+ }
+ else if(as.zoomed)
+ new->type = DlcZoomEmpty;
+ else
+ new->type = DlcEmpty;
+ }
+
+ }
+
+ return(new);
+}
+
+
+/*
+ * This returns the actual dlc instead of the dl within the dlc.
+ */
+DL_CACHE_S *
+get_dlc(long int row)
+{
+ dprint((11, "- get_dlc(%ld) -\n", row));
+
+ return(dlc_mgr(row, Lookup, (DL_CACHE_S *)NULL));
+}
+
+
+/*
+ * Move to new_dlc and give it row number row_number_to_assign_it.
+ * We copy the passed in dlc in case the caller passed us a pointer into
+ * the cache.
+ */
+void
+warp_to_dlc(DL_CACHE_S *new_dlc, long int row_number_to_assign_it)
+{
+ DL_CACHE_S dlc;
+
+ dprint((9, "- warp_to_dlc(%ld) -\n", row_number_to_assign_it));
+
+ dlc = *new_dlc;
+
+ /*
+ * Make sure we can move forward and backward from these
+ * types that we may wish to warp to. The caller may not have known
+ * to set adrbk_num for these types.
+ */
+ switch(dlc.type){
+ case DlcPersAdd:
+ dlc.adrbk_num = 0;
+ break;
+ case DlcGlobAdd:
+ dlc.adrbk_num = as.n_addrbk;
+ break;
+ default:
+ break;
+ }
+
+ (void)dlc_mgr(row_number_to_assign_it, ArbitraryStartingPoint, &dlc);
+}
+
+
+/*
+ * Move to first dlc and give it row number 0.
+ */
+void
+warp_to_beginning(void)
+{
+ dprint((9, "- warp_to_beginning -\n"));
+
+ (void)dlc_mgr(0L, FirstEntry, (DL_CACHE_S *)NULL);
+}
+
+
+/*
+ * Move to first dlc in abook_num and give it row number 0.
+ */
+void
+warp_to_top_of_abook(int abook_num)
+{
+ DL_CACHE_S dlc;
+
+ dprint((9, "- warp_to_top_of_abook(%d) -\n", abook_num));
+
+ (void)get_top_dl_of_adrbk(abook_num, &dlc);
+ warp_to_dlc(&dlc, 0L);
+}
+
+
+/*
+ * Move to last dlc and give it row number 0.
+ */
+void
+warp_to_end(void)
+{
+ dprint((9, "- warp_to_end -\n"));
+
+ (void)dlc_mgr(0L, LastEntry, (DL_CACHE_S *)NULL);
+}
+
+
+/*
+ * This flushes all of the cache that is related to this dlc.
+ */
+void
+flush_dlc_from_cache(DL_CACHE_S *dlc_to_flush)
+{
+ dprint((11, "- flush_dlc_from_cache -\n"));
+
+ (void)dlc_mgr(NO_LINE, FlushDlcFromCache, dlc_to_flush);
+}
+
+
+/*
+ * Returns 1 if the dlc's match, 0 otherwise.
+ */
+int
+matching_dlcs(DL_CACHE_S *dlc1, DL_CACHE_S *dlc2)
+{
+ if(!dlc1 || !dlc2 ||
+ dlc1->type != dlc2->type ||
+ dlc1->adrbk_num != dlc2->adrbk_num)
+ return 0;
+
+ switch(dlc1->type){
+
+ case DlcSimple:
+ case DlcListHead:
+ case DlcListBlankTop:
+ case DlcListBlankBottom:
+ case DlcListClickHere:
+ case DlcListEmpty:
+ return(dlc1->dlcelnum == dlc2->dlcelnum);
+
+ case DlcListEnt:
+ return(dlc1->dlcelnum == dlc2->dlcelnum &&
+ dlc1->dlcoffset == dlc2->dlcoffset);
+
+ case DlcTitle:
+ case DlcSubTitle:
+ case DlcTitleNoPerm:
+ case DlcTitleBlankTop:
+ case DlcEmpty:
+ case DlcZoomEmpty:
+ case DlcNoPermission:
+ case DlcPersAdd:
+ case DlcGlobAdd:
+ case DlcGlobDelim1:
+ case DlcGlobDelim2:
+ case DlcDirAccess:
+ case DlcDirDelim1:
+ case DlcDirDelim2:
+ case DlcTitleDashTopCmb:
+ case DlcTitleCmb:
+ case DlcTitleDashBottomCmb:
+ case DlcTitleBlankBottomCmb:
+ case DlcClickHereCmb:
+ case DlcTitleBlankTopCmb:
+ return 1;
+
+ case DlcNotSet:
+ case DlcBeginning:
+ case DlcOneBeforeBeginning:
+ case DlcTwoBeforeBeginning:
+ case DlcEnd:
+ case DlcNoAbooks:
+ return 0;
+
+ default:
+ break;
+ }
+ /*NOTREACHED*/
+
+ return 0;
+}
+
+
+/*
+ * Uses information in new to fill in new->dl.
+ */
+void
+fill_in_dl_field(DL_CACHE_S *new)
+{
+ AddrScrn_Disp *dl;
+ PerAddrBook *pab;
+ char buf[6*MAX_SCREEN_COLS + 1];
+ char buf2[6*1024];
+ char hostbuf[128];
+ char *folder;
+ char *q;
+ unsigned screen_width = ps_global->ttyo->screen_cols;
+ unsigned got_width, need_width, cellwidth;
+
+ screen_width = MIN(MAX_SCREEN_COLS, screen_width);
+
+ dl = &(new->dl);
+
+ /* free any previously allocated space */
+ switch(dl->type){
+ case Text:
+ case Title:
+ case TitleCmb:
+ case AskServer:
+ if(dl->usst)
+ fs_give((void **)&dl->usst);
+ default:
+ break;
+ }
+
+ /* set up new dl */
+ switch(new->type){
+ case DlcListBlankTop:
+ case DlcListBlankBottom:
+ case DlcGlobDelim1:
+ case DlcGlobDelim2:
+ case DlcDirDelim1:
+ case DlcDirDelim2:
+ case DlcTitleBlankTop:
+ case DlcDirBlankTop:
+ case DlcTitleBlankBottomCmb:
+ case DlcTitleBlankTopCmb:
+ dl->type = Text;
+ dl->usst = cpystr("");
+ break;
+
+ case DlcTitleDashTopCmb:
+ case DlcTitleDashBottomCmb:
+ case DlcDirDelim1a:
+ case DlcDirDelim1c:
+ /* line of dashes in txt field */
+ dl->type = Text;
+ memset((void *)buf, '-', screen_width * sizeof(char));
+ buf[screen_width] = '\0';
+ dl->usst = cpystr(buf);
+ break;
+
+ case DlcNoPermission:
+ dl->type = Text;
+ dl->usst = cpystr(NO_PERMISSION);
+ break;
+
+ case DlcTitle:
+ case DlcTitleNoPerm:
+ dl->type = Title;
+ pab = &as.adrbks[new->adrbk_num];
+ /* title for this addrbook */
+ snprintf(buf, sizeof(buf), " %s", pab->abnick ? pab->abnick : pab->filename);
+ cellwidth = utf8_width(buf);
+ if(cellwidth > screen_width)
+ cellwidth = utf8_truncate(buf, screen_width);
+
+ /* add space padding */
+ if(cellwidth < screen_width)
+ snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%*.*s",
+ screen_width - cellwidth, screen_width - cellwidth, "");
+
+ buf[sizeof(buf)-1] = '\0';
+
+ if(as.ro_warning && (pab->access == NoAccess || pab->access == ReadOnly)){
+ char *str;
+
+ if(pab->access == NoAccess)
+ str = NOACCESS;
+ else
+ str = READONLY;
+
+ need_width = utf8_width(str);
+
+ if(screen_width > need_width && (q = utf8_count_back_width(buf, buf+strlen(buf), need_width, &got_width)) != NULL)
+
+ (void) utf8_pad_to_width(q, str, sizeof(buf)-(q-buf), need_width, 0);
+ }
+
+ buf[sizeof(buf)-1] = '\0';
+ dl->usst = cpystr(buf);
+ break;
+
+ case DlcSubTitle:
+ dl->type = Text;
+ pab = &as.adrbks[new->adrbk_num];
+ /* Find a hostname to put in title */
+ hostbuf[0] = '\0';
+ folder = NULL;
+ if(pab->type & REMOTE_VIA_IMAP && pab->filename){
+ char *start, *end;
+
+ start = strindex(pab->filename, '{');
+ if(start)
+ end = strindex(start+1, '}');
+
+ if(start && end){
+ strncpy(hostbuf, start + 1,
+ MIN(end - start - 1, sizeof(hostbuf)-1));
+ hostbuf[MIN(end - start - 1, sizeof(hostbuf)-1)] = '\0';
+ if(*(end+1))
+ folder = end+1;
+ }
+ }
+
+ if(!folder)
+ folder = pab->filename;
+
+ /*
+ * Just trying to find the name relative to the home directory
+ * to display in an OS-independent way.
+ */
+ if(folder && in_dir(ps_global->home_dir, folder)){
+ char *p, *new_folder = NULL, *savep = NULL;
+ int l, saveval = 0;
+
+ l = strlen(ps_global->home_dir);
+
+ while(!new_folder && (p = last_cmpnt(folder)) != NULL){
+ if(savep){
+ *savep = saveval;
+ savep = NULL;
+ }
+
+ if(folder + l == p || folder + l + 1 == p)
+ new_folder = p;
+ else{
+ savep = --p;
+ saveval = *savep;
+ *savep = '\0';
+ }
+ }
+
+ if(savep)
+ *savep = saveval;
+
+ if(new_folder)
+ folder = new_folder;
+ }
+
+ snprintf(buf, sizeof(buf), " %s AddressBook%s%s in %s",
+ (pab->type & GLOBAL) ? "Global" : "Personal",
+ (pab->type & REMOTE_VIA_IMAP && *hostbuf) ? " on " : "",
+ (pab->type & REMOTE_VIA_IMAP && *hostbuf) ? hostbuf : "",
+ (folder && *folder) ? folder : "<?>");
+ cellwidth = utf8_width(buf);
+ if(cellwidth > screen_width)
+ cellwidth = utf8_truncate(buf, screen_width);
+
+ /* add space padding */
+ if(cellwidth < screen_width)
+ snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%*.*s",
+ screen_width - cellwidth, screen_width - cellwidth, "");
+
+ buf[sizeof(buf)-1] = '\0';
+ dl->usst = cpystr(buf);
+ break;
+
+ case DlcTitleCmb:
+ dl->type = TitleCmb;
+ pab = &as.adrbks[new->adrbk_num];
+ /* title for this addrbook */
+ snprintf(buf, sizeof(buf), "%s AddressBook <%s>",
+ (new->adrbk_num < as.how_many_personals) ?
+ "Personal" :
+ "Global",
+ pab->abnick ? pab->abnick : pab->filename);
+ cellwidth = utf8_width(buf);
+ if(cellwidth > screen_width)
+ cellwidth = utf8_truncate(buf, screen_width);
+
+ /* add space padding */
+ if(cellwidth < screen_width)
+ snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%*.*s",
+ screen_width - cellwidth, screen_width - cellwidth, "");
+
+ buf[sizeof(buf)-1] = '\0';
+
+ if(as.ro_warning && (pab->access == NoAccess || pab->access == ReadOnly)){
+ char *str;
+
+ if(pab->access == NoAccess)
+ str = NOACCESS;
+ else
+ str = READONLY;
+
+ need_width = utf8_width(str);
+
+ if(screen_width > need_width && (q = utf8_count_back_width(buf, buf+strlen(buf), need_width, &got_width)) != NULL)
+
+ (void) utf8_pad_to_width(q, str, sizeof(buf)-(q-buf), need_width, 0);
+ }
+
+ buf[sizeof(buf)-1] = '\0';
+ dl->usst = cpystr(buf);
+ break;
+
+ case DlcDirDelim1b:
+ dl->type = Text;
+ utf8_snprintf(buf, sizeof(buf), "%-*.*w", screen_width, screen_width, OLDSTYLE_DIR_TITLE);
+ dl->usst = cpystr(buf);
+ break;
+
+ case DlcClickHereCmb:
+ dl->type = ClickHereCmb;
+ break;
+
+ case DlcListClickHere:
+ dl->type = ListClickHere;
+ dl->elnum = new->dlcelnum;
+ break;
+
+ case DlcListEmpty:
+ dl->type = ListEmpty;
+ dl->elnum = new->dlcelnum;
+ break;
+
+ case DlcEmpty:
+ dl->type = Empty;
+ break;
+
+ case DlcZoomEmpty:
+ dl->type = ZoomEmpty;
+ break;
+
+ case DlcNoAbooks:
+ dl->type = NoAbooks;
+ break;
+
+ case DlcPersAdd:
+ dl->type = AddFirstPers;
+ break;
+
+ case DlcGlobAdd:
+ dl->type = AddFirstGlob;
+ break;
+
+ case DlcDirAccess:
+ dl->type = AskServer;
+
+#ifdef ENABLE_LDAP
+ {
+ LDAP_SERV_S *info;
+
+ info = break_up_ldap_server(ps_global->VAR_LDAP_SERVERS[new->adrbk_num]);
+ snprintf(buf2, sizeof(buf2), " %s",
+ (info && info->nick && *info->nick) ? info->nick :
+ (info && info->serv && *info->serv) ? info->serv :
+ comatose(new->adrbk_num + 1));
+ if(info)
+ free_ldap_server_info(&info);
+ }
+#else
+ snprintf(buf2, sizeof(buf2), " %s", comatose(new->adrbk_num + 1));
+#endif
+
+ utf8_snprintf(buf, sizeof(buf), "%-*.*w", screen_width, screen_width, buf2);
+ dl->usst = cpystr(buf);
+ break;
+
+ case DlcDirSubTitle:
+ dl->type = Text;
+#ifdef ENABLE_LDAP
+ {
+ LDAP_SERV_S *info;
+
+ info = break_up_ldap_server(ps_global->VAR_LDAP_SERVERS[new->adrbk_num]);
+ if(info && info->port >= 0)
+ snprintf(buf2, sizeof(buf2), " Directory Server on %s:%d",
+ (info && info->serv && *info->serv) ? info->serv : "<?>",
+ info->port);
+ else
+ snprintf(buf2, sizeof(buf2), " Directory Server on %s",
+ (info && info->serv && *info->serv) ? info->serv : "<?>");
+
+ if(info)
+ free_ldap_server_info(&info);
+ }
+#else
+ snprintf(buf2, sizeof(buf2), " Directory Server %s",
+ comatose(new->adrbk_num + 1));
+#endif
+
+ utf8_snprintf(buf, sizeof(buf), "%-*.*w", screen_width, screen_width, buf2);
+ dl->usst = cpystr(buf);
+ break;
+
+ case DlcSimple:
+ dl->type = Simple;
+ dl->elnum = new->dlcelnum;
+ break;
+
+ case DlcListHead:
+ dl->type = ListHead;
+ dl->elnum = new->dlcelnum;
+ break;
+
+ case DlcListEnt:
+ dl->type = ListEnt;
+ dl->elnum = new->dlcelnum;
+ dl->l_offset = new->dlcoffset;
+ break;
+
+ case DlcBeginning:
+ case DlcOneBeforeBeginning:
+ case DlcTwoBeforeBeginning:
+ dl->type = Beginning;
+ break;
+
+ case DlcEnd:
+ dl->type = End;
+ break;
+
+ default:
+ q_status_message(SM_ORDER | SM_DING, 5, 10,
+ _("Bug in addrbook, not supposed to happen, re-syncing..."));
+ dprint((1, "Bug in addrbook, impossible dflt in fill_in_dl (%d)\n",
+ new->type));
+ /* jump back to a safe starting point */
+ longjmp(addrbook_changed_unexpectedly, 1);
+ /*NOTREACHED*/
+ }
+}
+
+
+/*
+ * Returns a pointer to the member_number'th list member of the list
+ * associated with this display line.
+ */
+char *
+listmem(long int row)
+{
+ PerAddrBook *pab;
+ AddrScrn_Disp *dl;
+
+ dl = dlist(row);
+ if(dl->type != ListEnt)
+ return((char *)NULL);
+
+ pab = &as.adrbks[adrbk_num_from_lineno(row)];
+
+ return(listmem_from_dl(pab->address_book, dl));
+}
+
+
+/*
+ * Returns a pointer to the list member
+ * associated with this display line.
+ */
+char *
+listmem_from_dl(AdrBk *address_book, AddrScrn_Disp *dl)
+{
+ AdrBk_Entry *abe;
+ char **p = (char **)NULL;
+
+ /* This shouldn't happen */
+ if(dl->type != ListEnt)
+ return((char *)NULL);
+
+ abe = adrbk_get_ae(address_book, (a_c_arg_t) dl->elnum);
+
+ /*
+ * If we wanted to be more careful, We'd go through the list making sure
+ * we don't pass the end. We'll count on the caller being careful
+ * instead.
+ */
+ if(abe && abe->tag == List){
+ p = abe->addr.list;
+ p += dl->l_offset;
+ }
+
+ return((p && *p) ? *p : (char *)NULL);
+}
+
+
+/*
+ * How many members in list?
+ */
+adrbk_cntr_t
+listmem_count_from_abe(AdrBk_Entry *abe)
+{
+ char **p;
+
+ if(abe->tag != List)
+ return 0;
+
+ for(p = abe->addr.list; p != NULL && *p != NULL; p++)
+ ;/* do nothing */
+
+ return((adrbk_cntr_t)(p - abe->addr.list));
+}
+
+
+/*
+ * Return a ptr to the row'th line of the global disp_list.
+ * Line numbers count up but you can't count on knowing which line number
+ * goes with the first or the last row. That is, row 0 is not necessarily
+ * special. It could be before the rows that make up the display list, after
+ * them, or anywhere in between. You can't tell what the last row is
+ * numbered, but a dl with type End is returned when you get past the end.
+ * You can't tell what the number of the first row is, but if you go past
+ * the first row a dl of type Beginning will be returned. Row numbers can
+ * be positive or negative. Their values have no meaning other than how
+ * they line up relative to other row numbers.
+ */
+AddrScrn_Disp *
+dlist(long int row)
+{
+ DL_CACHE_S *dlc = (DL_CACHE_S *)NULL;
+
+ dlc = get_dlc(row);
+
+ if(dlc){
+ fill_in_dl_field(dlc);
+ return(&dlc->dl);
+ }
+ else{
+ q_status_message(SM_ORDER | SM_DING, 5, 10,
+ _("Bug in addrbook, not supposed to happen, re-syncing..."));
+ dprint((1, "Bug in addrbook (null dlc in dlist(%ld), not supposed to happen\n", row));
+ /* jump back to a safe starting point */
+
+ dprint((1, "dlist: panic: initialized %d, n_addrbk %d, cur_row %d, top_ent %ld, ro_warning %d, no_op_possbl %d\n",
+ as.initialized, as.n_addrbk, as.cur_row, as.top_ent, as.ro_warning, as.no_op_possbl));
+
+ longjmp(addrbook_changed_unexpectedly, 1);
+ /*NOTREACHED*/
+ }
+}
+
+
+/*
+ * Returns the index of the current address book.
+ */
+int
+adrbk_num_from_lineno(long int lineno)
+{
+ DL_CACHE_S *dlc;
+
+ dlc = get_dlc(lineno);
+
+ return(dlc->adrbk_num);
+}
diff --git a/pith/abdlc.h b/pith/abdlc.h
new file mode 100644
index 00000000..cd7ee72b
--- /dev/null
+++ b/pith/abdlc.h
@@ -0,0 +1,41 @@
+/*
+ * $Id: abdlc.h 769 2007-10-24 00:15:40Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_ABDLC_INCLUDED
+#define PITH_ABDLC_INCLUDED
+
+
+#include "../pith/adrbklib.h"
+
+
+/* exported protoypes */
+DL_CACHE_S *get_dlc(long);
+void warp_to_dlc(DL_CACHE_S *, long);
+void warp_to_beginning(void);
+void warp_to_top_of_abook(int);
+void warp_to_end(void);
+void flush_dlc_from_cache(DL_CACHE_S *);
+int matching_dlcs(DL_CACHE_S *, DL_CACHE_S *);
+void fill_in_dl_field(DL_CACHE_S *);
+char *listmem(long);
+char *listmem_from_dl(AdrBk *, AddrScrn_Disp *);
+adrbk_cntr_t listmem_count_from_abe(AdrBk_Entry *);
+AddrScrn_Disp *dlist(long);
+int adrbk_num_from_lineno(long);
+void done_with_dlc_cache(void);
+
+
+#endif /* PITH_ABDLC_INCLUDED */
diff --git a/pith/ablookup.c b/pith/ablookup.c
new file mode 100644
index 00000000..63ff9133
--- /dev/null
+++ b/pith/ablookup.c
@@ -0,0 +1,1629 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: ablookup.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2009 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h" /* for os-dep and pith defs/includes */
+#include "../pith/debug.h"
+#include "../pith/util.h"
+#include "../pith/adrbklib.h"
+#include "../pith/copyaddr.h"
+#include "../pith/status.h"
+#include "../pith/conf.h"
+#include "../pith/search.h"
+#include "../pith/abdlc.h"
+#include "../pith/addrstring.h"
+#include "../pith/ablookup.h"
+#include "../pith/options.h"
+#include "../pith/takeaddr.h"
+#ifdef ENABLE_LDAP
+#include "../pith/ldap.h"
+#endif /* ENABLE_LDAP */
+
+
+/*
+ * Internal prototypes
+ */
+int addr_is_in_addrbook(PerAddrBook *, ADDRESS *);
+ABOOK_ENTRY_S *adrbk_list_of_possible_trie_completions(AdrBk_Trie *, AdrBk *, char *, unsigned);
+void gather_abook_entry_list(AdrBk *, AdrBk_Trie *, char *, ABOOK_ENTRY_S **, unsigned);
+void add_addr_to_return_list(ADDRESS *, unsigned, char *, int, COMPLETE_S **);
+
+
+/*
+ * Given an address, try to find the first nickname that goes with it.
+ * Copies that nickname into the passed in buffer, which is assumed to
+ * be at least MAX_NICKNAME+1 in length. Returns NULL if it can't be found,
+ * else it returns a pointer to the buffer.
+ */
+char *
+get_nickname_from_addr(struct mail_address *adr, char *buffer, size_t buflen)
+{
+ AdrBk_Entry *abe;
+ char *ret = NULL;
+ SAVE_STATE_S state;
+ jmp_buf save_jmp_buf;
+ int *save_nesting_level;
+ ADDRESS *copied_adr;
+
+ state.savep = NULL;
+ state.stp = NULL;
+ state.dlc_to_warp_to = NULL;
+ copied_adr = copyaddr(adr);
+
+ if(ps_global->remote_abook_validity > 0)
+ (void)adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0);
+
+ save_nesting_level = cpyint(ab_nesting_level);
+ memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
+ if(setjmp(addrbook_changed_unexpectedly)){
+ ret = NULL;
+ if(state.savep)
+ fs_give((void **)&(state.savep));
+ if(state.stp)
+ fs_give((void **)&(state.stp));
+ if(state.dlc_to_warp_to)
+ fs_give((void **)&(state.dlc_to_warp_to));
+
+ q_status_message(SM_ORDER, 3, 5, _("Resetting address book..."));
+ dprint((1,
+ "RESETTING address book... get_nickname_from_addr()!\n"));
+ addrbook_reset();
+ ab_nesting_level = *save_nesting_level;
+ }
+
+ ab_nesting_level++;
+ init_ab_if_needed();
+
+ if(pith_opt_save_and_restore)
+ (*pith_opt_save_and_restore)(SAR_SAVE, &state);
+
+ abe = address_to_abe(copied_adr);
+
+ if(copied_adr)
+ mail_free_address(&copied_adr);
+
+ if(abe && abe->nickname && abe->nickname[0]){
+ strncpy(buffer, abe->nickname, buflen-1);
+ buffer[buflen-1] = '\0';
+ ret = buffer;
+ }
+
+ if(pith_opt_save_and_restore)
+ (*pith_opt_save_and_restore)(SAR_RESTORE, &state);
+
+ memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
+ ab_nesting_level--;
+
+ if(save_nesting_level)
+ fs_give((void **)&save_nesting_level);
+
+ return(ret);
+}
+
+
+/*
+ * Given an address, try to find the first fcc that goes with it.
+ * Copies that fcc into the passed in buffer.
+ * Returns NULL if it can't be found, else it returns a pointer to the buffer.
+ */
+char *
+get_fcc_from_addr(struct mail_address *adr, char *buffer, size_t buflen)
+{
+ AdrBk_Entry *abe;
+ char *ret = NULL;
+ SAVE_STATE_S state;
+ jmp_buf save_jmp_buf;
+ int *save_nesting_level;
+ ADDRESS *copied_adr;
+
+ state.savep = NULL;
+ state.stp = NULL;
+ state.dlc_to_warp_to = NULL;
+ copied_adr = copyaddr(adr);
+
+ if(ps_global->remote_abook_validity > 0)
+ (void)adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0);
+
+ save_nesting_level = cpyint(ab_nesting_level);
+ memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
+ if(setjmp(addrbook_changed_unexpectedly)){
+ ret = NULL;
+ if(state.savep)
+ fs_give((void **)&(state.savep));
+ if(state.stp)
+ fs_give((void **)&(state.stp));
+ if(state.dlc_to_warp_to)
+ fs_give((void **)&(state.dlc_to_warp_to));
+
+ q_status_message(SM_ORDER, 3, 5, _("Resetting address book..."));
+ dprint((1,
+ "RESETTING address book... get_fcc_from_addr()!\n"));
+ addrbook_reset();
+ ab_nesting_level = *save_nesting_level;
+ }
+
+ ab_nesting_level++;
+ init_ab_if_needed();
+
+ if(pith_opt_save_and_restore)
+ (*pith_opt_save_and_restore)(SAR_SAVE, &state);
+
+ abe = address_to_abe(copied_adr);
+
+ if(copied_adr)
+ mail_free_address(&copied_adr);
+
+ if(abe && abe->fcc && abe->fcc[0]){
+ if(!strcmp(abe->fcc, "\"\""))
+ buffer[0] = '\0';
+ else{
+ strncpy(buffer, abe->fcc, buflen-1);
+ buffer[buflen-1] = '\0';
+ }
+
+ ret = buffer;
+ }
+
+ if(pith_opt_save_and_restore)
+ (*pith_opt_save_and_restore)(SAR_RESTORE, &state);
+
+ memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
+ ab_nesting_level--;
+
+ if(save_nesting_level)
+ fs_give((void **)&save_nesting_level);
+
+ return(ret);
+}
+
+
+/*
+ * Given an address, try to find the first address book entry that
+ * matches it and return all the other fields in the passed in pointers.
+ * Caller needs to free the four fields.
+ * Returns -1 if it can't be found, 0 if it is found.
+ */
+int
+get_contactinfo_from_addr(struct mail_address *adr, char **nick, char **full, char **fcc, char **comment)
+{
+ AdrBk_Entry *abe;
+ int ret = -1;
+ SAVE_STATE_S state;
+ jmp_buf save_jmp_buf;
+ int *save_nesting_level;
+ ADDRESS *copied_adr;
+
+ state.savep = NULL;
+ state.stp = NULL;
+ state.dlc_to_warp_to = NULL;
+ copied_adr = copyaddr(adr);
+
+ if(ps_global->remote_abook_validity > 0)
+ (void)adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0);
+
+ save_nesting_level = cpyint(ab_nesting_level);
+ memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
+ if(setjmp(addrbook_changed_unexpectedly)){
+ ret = -1;
+ if(state.savep)
+ fs_give((void **)&(state.savep));
+ if(state.stp)
+ fs_give((void **)&(state.stp));
+ if(state.dlc_to_warp_to)
+ fs_give((void **)&(state.dlc_to_warp_to));
+
+ q_status_message(SM_ORDER, 3, 5, _("Resetting address book..."));
+ dprint((1,
+ "RESETTING address book... get_contactinfo_from_addr()!\n"));
+ addrbook_reset();
+ ab_nesting_level = *save_nesting_level;
+ }
+
+ ab_nesting_level++;
+ init_ab_if_needed();
+
+ if(pith_opt_save_and_restore)
+ (*pith_opt_save_and_restore)(SAR_SAVE, &state);
+
+ abe = address_to_abe(copied_adr);
+
+ if(copied_adr)
+ mail_free_address(&copied_adr);
+
+ if(abe){
+ if(nick && abe->nickname && abe->nickname[0])
+ *nick = cpystr(abe->nickname);
+
+ if(full && abe->fullname && abe->fullname[0])
+ *full = cpystr(abe->fullname);
+
+ if(fcc && abe->fcc && abe->fcc[0])
+ *fcc = cpystr(abe->fcc);
+
+ if(comment && abe->extra && abe->extra[0])
+ *comment = cpystr(abe->extra);
+
+ ret = 0;
+ }
+
+ if(pith_opt_save_and_restore)
+ (*pith_opt_save_and_restore)(SAR_RESTORE, &state);
+
+ memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
+ ab_nesting_level--;
+
+ if(save_nesting_level)
+ fs_give((void **)&save_nesting_level);
+
+ return(ret);
+}
+
+
+/*
+ * This is a very special-purpose routine.
+ * It implements the From or Reply-To address is in the Address Book
+ * part of Pattern matching.
+ */
+void
+address_in_abook(MAILSTREAM *stream, SEARCHSET *searchset,
+ int inabook, PATTERN_S *abooks)
+{
+ char *savebits;
+ MESSAGECACHE *mc;
+ long i, count = 0L;
+ SEARCHSET *s, *ss;
+ ADDRESS *from, *reply_to, *sender, *to, *cc;
+ int is_there, adrbknum, *abooklist = NULL, positive_match;
+ PATTERN_S *pat;
+ PerAddrBook *pab;
+ ENVELOPE *e;
+ SAVE_STATE_S state;
+ jmp_buf save_jmp_buf;
+ int *save_nesting_level;
+
+ if(!stream)
+ return;
+
+ /* everything that matches remains a match */
+ if(inabook == IAB_EITHER)
+ return;
+
+ state.savep = NULL;
+ state.stp = NULL;
+ state.dlc_to_warp_to = NULL;
+
+ /*
+ * This may call build_header_line recursively because we may be in
+ * build_header_line now. So we have to preserve and restore the
+ * sequence bits since we want to use them here.
+ */
+ savebits = (char *) fs_get((stream->nmsgs+1) * sizeof(char));
+
+ for(i = 1L; i <= stream->nmsgs; i++){
+ if((mc = mail_elt(stream, i)) != NULL){
+ savebits[i] = mc->sequence;
+ mc->sequence = 0;
+ }
+ }
+
+ /*
+ * Build a searchset so we can look at all the envelopes
+ * we need to look at but only those we need to look at.
+ * Everything with the searched bit set is still a
+ * possibility, so restrict to that set.
+ */
+
+ for(s = searchset; s; s = s->next)
+ for(i = s->first; i <= s->last; i++)
+ if(i > 0L && i <= stream->nmsgs
+ && (mc=mail_elt(stream, i)) && mc->searched){
+ mc->sequence = 1;
+ count++;
+ }
+
+ ss = build_searchset(stream);
+
+ /*
+ * We save the address book state here so we don't have to do it
+ * each time through the loop below.
+ */
+ if(ss){
+ if(ps_global->remote_abook_validity > 0)
+ (void)adrbk_check_and_fix_all(ab_nesting_level == 0, 0, 0);
+
+ save_nesting_level = cpyint(ab_nesting_level);
+ memcpy(save_jmp_buf, addrbook_changed_unexpectedly, sizeof(jmp_buf));
+ if(setjmp(addrbook_changed_unexpectedly)){
+ if(state.savep)
+ fs_give((void **)&(state.savep));
+ if(state.stp)
+ fs_give((void **)&(state.stp));
+ if(state.dlc_to_warp_to)
+ fs_give((void **)&(state.dlc_to_warp_to));
+
+ q_status_message(SM_ORDER, 3, 5, _("Resetting address book..."));
+ dprint((1,
+ "RESETTING address book... address_in_abook()!\n"));
+ addrbook_reset();
+ ab_nesting_level = *save_nesting_level;
+ }
+
+ ab_nesting_level++;
+ init_ab_if_needed();
+
+ if(pith_opt_save_and_restore)
+ (*pith_opt_save_and_restore)(SAR_SAVE, &state);
+
+ if(as.n_addrbk > 0){
+ abooklist = (int *) fs_get(as.n_addrbk * sizeof(*abooklist));
+ memset((void *) abooklist, 0, as.n_addrbk * sizeof(*abooklist));
+ }
+
+ if(abooklist)
+ switch(inabook & IAB_TYPE_MASK){
+ case IAB_YES:
+ case IAB_NO:
+ for(adrbknum = 0; adrbknum < as.n_addrbk; adrbknum++)
+ abooklist[adrbknum] = 1;
+
+ break;
+
+ case IAB_SPEC_YES:
+ case IAB_SPEC_NO:
+ /* figure out which address books we're going to look in */
+ for(adrbknum = 0; adrbknum < as.n_addrbk; adrbknum++){
+ pab = &as.adrbks[adrbknum];
+ /*
+ * For each address book, check all of the address books
+ * in the pattern's list to see if they are it.
+ */
+ for(pat = abooks; pat; pat = pat->next){
+ if(!strcmp(pab->abnick, pat->substring)
+ || !strcmp(pab->filename, pat->substring)){
+ abooklist[adrbknum] = 1;
+ break;
+ }
+ }
+ }
+
+ break;
+ }
+
+ switch(inabook & IAB_TYPE_MASK){
+ case IAB_YES:
+ case IAB_SPEC_YES:
+ positive_match = 1;
+ break;
+
+ case IAB_NO:
+ case IAB_SPEC_NO:
+ positive_match = 0;
+ break;
+ }
+ }
+
+ if(count){
+ SEARCHSET **sset;
+
+ mail_parameters(NULL, SET_FETCHLOOKAHEADLIMIT, (void *) count);
+
+ /*
+ * This causes the lookahead to fetch precisely
+ * the messages we want (in the searchset) instead
+ * of just fetching the next 20 sequential
+ * messages. If the searching so far has caused
+ * a sparse searchset in a large mailbox, the
+ * difference can be substantial.
+ * This resets automatically after the first fetch.
+ */
+ sset = (SEARCHSET **) mail_parameters(stream,
+ GET_FETCHLOOKAHEAD,
+ (void *) stream);
+ if(sset)
+ *sset = ss;
+ }
+
+ for(s = ss; s; s = s->next){
+ for(i = s->first; i <= s->last; i++){
+ if(i <= 0L || i > stream->nmsgs)
+ continue;
+
+ e = pine_mail_fetchenvelope(stream, i);
+
+ from = e ? e->from : NULL;
+ reply_to = e ? e->reply_to : NULL;
+ sender = e ? e->sender : NULL;
+ to = e ? e->to : NULL;
+ cc = e ? e->cc : NULL;
+
+ is_there = 0;
+ for(adrbknum = 0; !is_there && adrbknum < as.n_addrbk; adrbknum++){
+ if(!abooklist[adrbknum])
+ continue;
+
+ pab = &as.adrbks[adrbknum];
+ is_there = ((inabook & IAB_FROM) && addr_is_in_addrbook(pab, from))
+ || ((inabook & IAB_REPLYTO) && addr_is_in_addrbook(pab, reply_to))
+ || ((inabook & IAB_SENDER) && addr_is_in_addrbook(pab, sender))
+ || ((inabook & IAB_TO) && addr_is_in_addrbook(pab, to))
+ || ((inabook & IAB_CC) && addr_is_in_addrbook(pab, cc));
+ }
+
+ if(positive_match){
+ /*
+ * We matched up until now. If it isn't there, then it
+ * isn't a match. If it is there, leave the searched bit
+ * set.
+ */
+ if(!is_there && i > 0L && i <= stream->nmsgs && (mc = mail_elt(stream, i)))
+ mc->searched = NIL;
+ }
+ else{
+ if(is_there && i > 0L && i <= stream->nmsgs && (mc = mail_elt(stream, i)))
+ mc->searched = NIL;
+ }
+ }
+ }
+
+ if(ss){
+ if(pith_opt_save_and_restore)
+ (*pith_opt_save_and_restore)(SAR_RESTORE, &state);
+
+ memcpy(addrbook_changed_unexpectedly, save_jmp_buf, sizeof(jmp_buf));
+ ab_nesting_level--;
+
+ if(save_nesting_level)
+ fs_give((void **)&save_nesting_level);
+ }
+
+ /* restore sequence bits */
+ for(i = 1L; i <= stream->nmsgs; i++)
+ if((mc = mail_elt(stream, i)) != NULL)
+ mc->sequence = savebits[i];
+
+ fs_give((void **) &savebits);
+
+ if(ss)
+ mail_free_searchset(&ss);
+
+ if(abooklist)
+ fs_give((void **) &abooklist);
+}
+
+
+/*
+ * Given two addresses, check to see if either is in the address book.
+ * Returns 1 if yes, 0 if not found.
+ */
+int
+addr_is_in_addrbook(PerAddrBook *pab, struct mail_address *addr)
+{
+ AdrBk_Entry *abe = NULL;
+ int ret = 0;
+ char abuf[MAX_ADDR_FIELD+1];
+
+ if(!(pab && addr && addr->mailbox))
+ return(ret);
+
+ if(addr){
+ strncpy(abuf, addr->mailbox, sizeof(abuf)-1);
+ abuf[sizeof(abuf)-1] = '\0';
+ if(addr->host && addr->host[0]){
+ strncat(abuf, "@", sizeof(abuf)-strlen(abuf)-1);
+ strncat(abuf, addr->host, sizeof(abuf)-strlen(abuf)-1);
+ }
+
+ if(pab->ostatus != Open && pab->ostatus != NoDisplay)
+ init_abook(pab, NoDisplay);
+
+ abe = adrbk_lookup_by_addr(pab->address_book, abuf,
+ (adrbk_cntr_t *) NULL);
+ }
+
+ ret = abe ? 1 : 0;
+
+ return(ret);
+}
+
+
+/*
+ * Look through addrbooks for nickname, opening addrbooks first
+ * if necessary. It is assumed that the caller will restore the
+ * state of the addrbooks if desired.
+ *
+ * Args: nickname -- the nickname to lookup
+ * clearrefs -- clear reference bits before lookup
+ * which_addrbook -- If matched, addrbook number it was found in.
+ * not_here -- If non-negative, skip looking in this abook.
+ *
+ * Results: A pointer to an AdrBk_Entry is returned, or NULL if not found.
+ * Stop at first match (so order of addrbooks is important).
+ */
+AdrBk_Entry *
+adrbk_lookup_with_opens_by_nick(char *nickname, int clearrefs, int *which_addrbook, int not_here)
+{
+ AdrBk_Entry *abe = (AdrBk_Entry *)NULL;
+ int i;
+ PerAddrBook *pab;
+
+ dprint((5, "- adrbk_lookup_with_opens_by_nick(%s) -\n",
+ nickname ? nickname : "?"));
+
+ for(i = 0; i < as.n_addrbk; i++){
+
+ if(i == not_here)
+ continue;
+
+ pab = &as.adrbks[i];
+
+ if(pab->ostatus != Open && pab->ostatus != NoDisplay)
+ init_abook(pab, NoDisplay);
+
+ if(clearrefs)
+ adrbk_clearrefs(pab->address_book);
+
+ abe = adrbk_lookup_by_nick(pab->address_book,
+ nickname,
+ (adrbk_cntr_t *)NULL);
+ if(abe)
+ break;
+ }
+
+ if(abe && which_addrbook)
+ *which_addrbook = i;
+
+ return(abe);
+}
+
+
+/*
+ * Find the addressbook entry that matches the argument address.
+ * Searches through all addressbooks looking for the match.
+ * Opens addressbooks if necessary. It is assumed that the caller
+ * will restore the state of the addrbooks if desired.
+ *
+ * Args: addr -- the address we're trying to match
+ *
+ * Returns: NULL -- no match found
+ * abe -- a pointer to the addrbook entry that matches
+ */
+AdrBk_Entry *
+address_to_abe(struct mail_address *addr)
+{
+ register PerAddrBook *pab;
+ int adrbk_number;
+ AdrBk_Entry *abe = NULL;
+ char *abuf = NULL;
+
+ if(!(addr && addr->mailbox))
+ return (AdrBk_Entry *)NULL;
+
+ abuf = (char *)fs_get((MAX_ADDR_FIELD + 1) * sizeof(char));
+
+ strncpy(abuf, addr->mailbox, MAX_ADDR_FIELD);
+ abuf[MAX_ADDR_FIELD] = '\0';
+ if(addr->host && addr->host[0]){
+ strncat(abuf, "@", MAX_ADDR_FIELD+1-1-strlen(abuf));
+ strncat(abuf, addr->host, MAX_ADDR_FIELD+1-1-strlen(abuf));
+ }
+
+ /* for each addressbook */
+ for(adrbk_number = 0; adrbk_number < as.n_addrbk; adrbk_number++){
+
+ pab = &as.adrbks[adrbk_number];
+
+ if(pab->ostatus != Open && pab->ostatus != NoDisplay)
+ init_abook(pab, NoDisplay);
+
+ abe = adrbk_lookup_by_addr(pab->address_book,
+ abuf,
+ (adrbk_cntr_t *)NULL);
+ if(abe)
+ break;
+ }
+
+ if(abuf)
+ fs_give((void **)&abuf);
+
+ return(abe);
+}
+
+
+/*
+ * Turn an AdrBk_Entry into an address list
+ *
+ * Args: abe -- the AdrBk_Entry
+ * dl -- the corresponding dl
+ * abook -- which addrbook the abe is in (only used for type ListEnt)
+ * how_many -- The number of addresses is returned here
+ *
+ * Result: allocated address list or NULL
+ */
+ADDRESS *
+abe_to_address(AdrBk_Entry *abe, AddrScrn_Disp *dl, AdrBk *abook, int *how_many)
+{
+ char *fullname, *tmp_a_string;
+ char *list, *l1, **l2;
+ char *fakedomain = "@";
+ ADDRESS *addr = NULL;
+ size_t length;
+ int count = 0;
+
+ if(!dl || !abe)
+ return(NULL);
+
+ fullname = (abe->fullname && abe->fullname[0]) ? abe->fullname : NULL;
+
+ switch(dl->type){
+ case Simple:
+ /* rfc822_parse_adrlist feels free to destroy input so send copy */
+ tmp_a_string = cpystr(abe->addr.addr);
+ rfc822_parse_adrlist(&addr, tmp_a_string, fakedomain);
+
+ if(tmp_a_string)
+ fs_give((void **)&tmp_a_string);
+
+ if(addr && fullname){
+#ifdef ENABLE_LDAP
+ if(strncmp(addr->mailbox, RUN_LDAP, LEN_RL) != 0)
+#endif /* ENABLE_LDAP */
+ {
+ if(addr->personal)
+ fs_give((void **)&addr->personal);
+
+ addr->personal = adrbk_formatname(fullname, NULL, NULL);
+ }
+ }
+
+ if(addr)
+ count++;
+
+ break;
+
+ case ListEnt:
+ /* rfc822_parse_adrlist feels free to destroy input so send copy */
+ tmp_a_string = cpystr(listmem_from_dl(abook, dl));
+ rfc822_parse_adrlist(&addr, tmp_a_string, fakedomain);
+ if(tmp_a_string)
+ fs_give((void **)&tmp_a_string);
+
+ if(addr)
+ count++;
+
+ break;
+
+ case ListHead:
+ length = 0;
+ for(l2 = abe->addr.list; *l2; l2++)
+ length += (strlen(*l2) + 1);
+
+ list = (char *)fs_get(length + 1);
+ l1 = list;
+ for(l2 = abe->addr.list; *l2; l2++){
+ if(l1 != list && length-(l1-list) > 0)
+ *l1++ = ',';
+
+ strncpy(l1, *l2, length-(l1-list));
+ list[length] = '\0';
+ l1 += strlen(l1);
+ count++;
+ }
+
+ rfc822_parse_adrlist(&addr, list, fakedomain);
+ if(list)
+ fs_give((void **)&list);
+
+ break;
+
+ default:
+ dprint((1, "default case in abe_to_address, shouldn't happen\n"));
+ break;
+ }
+
+ if(how_many)
+ *how_many = count;
+
+ return(addr);
+}
+
+
+/*
+ * Turn an AdrBk_Entry into a nickname (if it has a nickname) or a
+ * formatted addr_string which has been rfc1522 decoded.
+ *
+ * Args: abe -- the AdrBk_Entry
+ * dl -- the corresponding dl
+ * abook -- which addrbook the abe is in (only used for type ListEnt)
+ *
+ * Result: allocated string is returned
+ */
+char *
+abe_to_nick_or_addr_string(AdrBk_Entry *abe, AddrScrn_Disp *dl, int abook_num)
+{
+ ADDRESS *addr;
+ char *a_string;
+
+ if(!dl || !abe)
+ return(cpystr(""));
+
+ if((dl->type == Simple || dl->type == ListHead)
+ && abe->nickname && abe->nickname[0]){
+ char *fname;
+ int which_addrbook;
+
+ /*
+ * We prefer to pass back the nickname since that allows the
+ * caller to keep track of which entry the address came from.
+ * This is useful in build_address so that the fcc line can
+ * be kept correct. However, if the nickname is also present in
+ * another addressbook then we have to be careful. If that other
+ * addressbook comes before this one then passing back the nickname
+ * will cause the wrong entry to get used. So check for that
+ * and pass back the addr_string in that case.
+ */
+ if((fname = addr_lookup(abe->nickname, &which_addrbook, abook_num)) != NULL){
+ fs_give((void **)&fname);
+ if(which_addrbook >= abook_num)
+ return(cpystr(abe->nickname));
+ }
+ else
+ return(cpystr(abe->nickname));
+ }
+
+ addr = abe_to_address(abe, dl, as.adrbks[abook_num].address_book, NULL);
+ a_string = addr_list_string(addr, NULL, 0); /* always returns a string */
+ if(addr)
+ mail_free_address(&addr);
+
+ return(a_string);
+}
+
+
+/*
+ * Check to see if address is that of the current user running pine
+ *
+ * Args: a -- Address to check
+ * ps -- The pine_state structure
+ *
+ * Result: returns 1 if it matches, 0 otherwise.
+ *
+ * The mailbox must match the user name and the hostname must match.
+ * In matching the hostname, matches occur if the hostname in the address
+ * is blank, or if it matches the local hostname, or the full hostname
+ * with qualifying domain, or the qualifying domain without a specific host.
+ * Note, there is a very small chance that we will err on the
+ * non-conservative side here. That is, we may decide two addresses are
+ * the same even though they are different (because we do case-insensitive
+ * compares on the mailbox). That might cause a reply not to be sent to
+ * somebody because they look like they are us. This should be very,
+ * very rare.
+ *
+ * It is also considered a match if any of the addresses in alt-addresses
+ * matches a. The check there is simpler. It parses each address in
+ * the list, adding maildomain if there wasn't a domain, and compares
+ * mailbox and host in the ADDRESS's for equality.
+ */
+int
+address_is_us(struct mail_address *a, struct pine *ps)
+{
+ char **t;
+ int ret;
+ char addrstr[500];
+
+ if(!a || a->mailbox == NULL || !ps)
+ ret = 0;
+
+ /* at least LHS must match, but case-independent */
+ else if(strucmp(a->mailbox, ps->VAR_USER_ID) == 0
+
+ && /* and hostname matches */
+
+ /* hostname matches if it's not there, */
+ (a->host == NULL ||
+ /* or if hostname and userdomain (the one user sets) match exactly, */
+ ((ps->userdomain && a->host && strucmp(a->host,ps->userdomain) == 0) ||
+
+ /*
+ * or if(userdomain is either not set or it is set to be
+ * the same as the localdomain or hostname) and (the hostname
+ * of the address matches either localdomain or hostname)
+ */
+ ((ps->userdomain == NULL ||
+ strucmp(ps->userdomain, ps->localdomain) == 0 ||
+ strucmp(ps->userdomain, ps->hostname) == 0) &&
+ (strucmp(a->host, ps->hostname) == 0 ||
+ strucmp(a->host, ps->localdomain) == 0)))))
+ ret = 1;
+
+ /*
+ * If no match yet, check to see if it matches any of the alternate
+ * addresses the user has specified.
+ */
+ else if(!ps_global->VAR_ALT_ADDRS ||
+ !ps_global->VAR_ALT_ADDRS[0] ||
+ !ps_global->VAR_ALT_ADDRS[0][0])
+ ret = 0; /* none defined */
+
+ else{
+ ret = 0;
+ if(a && a->host && a->mailbox)
+ snprintf(addrstr, sizeof(addrstr), "%s@%s", a->mailbox, a->host);
+
+ for(t = ps_global->VAR_ALT_ADDRS; !ret && t[0] && t[0][0]; t++){
+ char *alt;
+ regex_t reg;
+ int err;
+ char ebuf[200];
+
+ alt = (*t);
+
+ if(F_ON(F_DISABLE_REGEX, ps_global) || !contains_regex_special_chars(alt)){
+ ADDRESS *alt_addr;
+ char *alt2;
+
+ alt2 = cpystr(alt);
+ alt_addr = NULL;
+ rfc822_parse_adrlist(&alt_addr, alt2, ps_global->maildomain);
+ if(alt2)
+ fs_give((void **) &alt2);
+
+ if(address_is_same(a, alt_addr))
+ ret = 1;
+
+ if(alt_addr)
+ mail_free_address(&alt_addr);
+ }
+ else{
+ /* treat alt as a regular expression */
+ ebuf[0] = '\0';
+ if(!(err=regcomp(&reg, alt, REG_ICASE | REG_NOSUB | REG_EXTENDED))){
+ err = regexec(&reg, addrstr, 0, NULL, 0);
+ if(err == 0)
+ ret = 1;
+ else if(err != REG_NOMATCH){
+ regerror(err, &reg, ebuf, sizeof(ebuf));
+ if(ebuf[0])
+ dprint((2, "- address_is_us regexec error: %s (%s)", ebuf, alt));
+ }
+
+ regfree(&reg);
+ }
+ else{
+ regerror(err, &reg, ebuf, sizeof(ebuf));
+ if(ebuf[0])
+ dprint((2, "- address_is_us regcomp error: %s (%s)", ebuf, alt));
+ }
+ }
+ }
+ }
+
+ return(ret);
+}
+
+
+/*
+ * In an ad hoc way try to decide if str is meant to be a regular
+ * expression or not. Dot doesn't count * as regex stuff because
+ * we're worried about addresses.
+ *
+ * Returns 0 or 1
+ */
+int
+contains_regex_special_chars(char *str)
+{
+ char special_chars[] = {'*', '|', '+', '?', '{', '[', '^', '$', '\\', '\0'};
+ char *c;
+
+ if(!str)
+ return 0;
+
+ /*
+ * If any of special_chars are in str consider it a regex expression.
+ */
+ for(c = special_chars; *c; c++)
+ if(strindex(str, *c))
+ return 1;
+
+ return 0;
+}
+
+
+/*
+ * Compare the two addresses, and return true if they're the same,
+ * false otherwise
+ *
+ * Args: a -- First address for comparison
+ * b -- Second address for comparison
+ *
+ * Result: returns 1 if it matches, 0 otherwise.
+ */
+int
+address_is_same(struct mail_address *a, struct mail_address *b)
+{
+ return(a && b && a->mailbox && b->mailbox && a->host && b->host
+ && strucmp(a->mailbox, b->mailbox) == 0
+ && strucmp(a->host, b->host) == 0);
+}
+
+
+/*
+ * Returns nonzero if the two address book entries are equal.
+ * Returns zero if not equal.
+ */
+int
+abes_are_equal(AdrBk_Entry *a, AdrBk_Entry *b)
+{
+ int result = 0;
+ char **alist, **blist;
+ char *addra, *addrb;
+
+ if(a && b &&
+ a->tag == b->tag &&
+ ((!a->nickname && !b->nickname) ||
+ (a->nickname && b->nickname && strcmp(a->nickname,b->nickname) == 0)) &&
+ ((!a->fullname && !b->fullname) ||
+ (a->fullname && b->fullname && strcmp(a->fullname,b->fullname) == 0)) &&
+ ((!a->fcc && !b->fcc) ||
+ (a->fcc && b->fcc && strcmp(a->fcc,b->fcc) == 0)) &&
+ ((!a->extra && !b->extra) ||
+ (a->extra && b->extra && strcmp(a->extra,b->extra) == 0))){
+
+ /* If we made it in here, they still might be equal. */
+ if(a->tag == Single)
+ result = strcmp(a->addr.addr,b->addr.addr) == 0;
+ else{
+ alist = a->addr.list;
+ blist = b->addr.list;
+ if(!alist && !blist)
+ result = 1;
+ else{
+ /* compare the whole lists */
+ addra = *alist;
+ addrb = *blist;
+ while(addra && addrb && strcmp(addra,addrb) == 0){
+ alist++;
+ blist++;
+ addra = *alist;
+ addrb = *blist;
+ }
+
+ if(!addra && !addrb)
+ result = 1;
+ }
+ }
+ }
+
+ return(result);
+}
+
+
+/*
+ * Interface to address book lookups for callers outside or inside this file.
+ *
+ * Args: nickname -- The nickname to look up
+ * which_addrbook -- If matched, addrbook number it was found in.
+ * not_here -- If non-negative, skip looking in this abook.
+ *
+ * Result: returns NULL or the corresponding fullname. The fullname is
+ * allocated here so the caller must free it.
+ *
+ * This opens the address books if they haven't been opened and restores
+ * them to the state they were in upon entry.
+ */
+char *
+addr_lookup(char *nickname, int *which_addrbook, int not_here)
+{
+ AdrBk_Entry *abe;
+ SAVE_STATE_S state;
+ char *fullname;
+
+ dprint((9, "- addr_lookup(%s) -\n",nickname ? nickname : "nil"));
+
+ init_ab_if_needed();
+
+ if(pith_opt_save_and_restore)
+ (*pith_opt_save_and_restore)(SAR_SAVE, &state);
+
+ abe = adrbk_lookup_with_opens_by_nick(nickname,0,which_addrbook,not_here);
+
+ fullname = (abe && abe->fullname) ? cpystr(abe->fullname) : NULL;
+
+ if(pith_opt_save_and_restore)
+ (*pith_opt_save_and_restore)(SAR_RESTORE, &state);
+
+ return(fullname);
+}
+
+
+/*
+ * Look in all of the address books for all of the possible entries
+ * that match the query string. The matches can be for the nickname,
+ * for the fullname, or for the address@host part of the address.
+ * All of the matches are at the starts of the strings, not a general
+ * substring match. This is not true anymore. Fullname matches can be
+ * at the start of the fullname or starting after a space in the fullname.
+ * If flags has ALC_INCLUDE_LDAP defined then LDAP
+ * entries are added to the end of the list. The LDAP queries are done
+ * only for those servers that have the 'impl' feature turned on, which
+ * means that lookups should be done implicitly. This feature also
+ * controls whether or not lookups should be done when typing carriage
+ * return (instead of this which is TAB).
+ *
+ * Args query -- What the user has typed so far
+ *
+ * Returns a list of possibilities for the given query string.
+ *
+ * Caller needs to free the answer.
+ */
+COMPLETE_S *
+adrbk_list_of_completions(char *query, MAILSTREAM *stream, imapuid_t uid, int flags)
+{
+ int i;
+ SAVE_STATE_S state;
+ PerAddrBook *pab;
+ ABOOK_ENTRY_S *list, *list2, *biglist = NULL;
+ COMPLETE_S *return_list = NULL, *last_one_added = NULL, *new, *cp, *dp, *dprev;
+ BuildTo toaddr;
+ ADDRESS *addr;
+ char buf[1000];
+ char *newaddr = NULL, *simple_addr = NULL, *fcc = NULL;
+ ENVELOPE *env = NULL;
+ BODY *body = NULL;
+
+ init_ab_if_needed();
+
+ if(pith_opt_save_and_restore)
+ (*pith_opt_save_and_restore)(SAR_SAVE, &state);
+
+ for(i = 0; i < as.n_addrbk; i++){
+
+ pab = &as.adrbks[i];
+
+ if(pab->ostatus != Open && pab->ostatus != NoDisplay)
+ init_abook(pab, NoDisplay);
+
+ list = adrbk_list_of_possible_completions(pab ? pab->address_book : NULL, query);
+ combine_abook_entry_lists(&biglist, list);
+ }
+
+ /*
+ * Eliminate duplicates by NO_NEXTing the entrynums.
+ */
+ for(list = biglist; list; list = list->next)
+ /* eliminate any dups further along in the list */
+ if(list->entrynum != NO_NEXT)
+ for(list2 = list->next; list2; list2 = list2->next)
+ if(list2->entrynum == list->entrynum){
+ list2->entrynum = NO_NEXT;
+ list->matches_bitmap |= list2->matches_bitmap;
+ }
+
+ /* build the return list */
+ for(list = biglist; list; list = list->next)
+ if(list->entrynum != NO_NEXT){
+ fcc = NULL;
+ toaddr.type = Abe;
+ toaddr.arg.abe = adrbk_get_ae(list->ab, list->entrynum);
+ if(our_build_address(toaddr, &newaddr, NULL, &fcc, NULL) == 0){
+ char *reverse_fullname = NULL;
+
+ /*
+ * ALC_FULL is a regular FullName match and that will be
+ * captured in the full_address field. If there was also
+ * an ALC_REVFULL match that means that the user has the
+ * FullName entered in their addrbook as Last, First and
+ * that is where the match was. We want to put that in
+ * the completions structure in the rev_fullname field.
+ */
+ if(list->matches_bitmap & ALC_REVFULL
+ && toaddr.arg.abe
+ && toaddr.arg.abe->fullname && toaddr.arg.abe->fullname[0]
+ && toaddr.arg.abe->fullname[0] != '"'
+ && strindex(toaddr.arg.abe->fullname, ',') != NULL){
+
+ reverse_fullname = toaddr.arg.abe->fullname;
+ }
+
+ if(flags & ALC_INCLUDE_ADDRS){
+ if(toaddr.arg.abe && toaddr.arg.abe->tag == Single
+ && toaddr.arg.abe->addr.addr && toaddr.arg.abe->addr.addr[0]){
+ char *tmp_a_string;
+ char *fakedomain = "@";
+
+ tmp_a_string = cpystr(toaddr.arg.abe->addr.addr);
+ addr = NULL;
+ rfc822_parse_adrlist(&addr, tmp_a_string, fakedomain);
+ if(tmp_a_string)
+ fs_give((void **) &tmp_a_string);
+
+ if(addr){
+ if(addr->mailbox && addr->host
+ && !(addr->host[0] == '@' && addr->host[1] == '\0'))
+ simple_addr = simple_addr_string(addr, buf, sizeof(buf));
+
+ mail_free_address(&addr);
+ }
+ }
+ }
+
+ new = new_complete_s(toaddr.arg.abe ? toaddr.arg.abe->nickname : NULL,
+ newaddr, simple_addr, reverse_fullname, fcc,
+ list->matches_bitmap | ALC_ABOOK);
+
+ /* add to end of list */
+ if(return_list == NULL){
+ return_list = new;
+ last_one_added = new;
+ }
+ else{
+ last_one_added->next = new;
+ last_one_added = new;
+ }
+ }
+
+ if(newaddr)
+ fs_give((void **) &newaddr);
+ }
+
+ if(pith_opt_save_and_restore)
+ (*pith_opt_save_and_restore)(SAR_RESTORE, &state);
+
+ free_abook_entry_s(&biglist);
+
+#ifdef ENABLE_LDAP
+ if(flags & ALC_INCLUDE_LDAP){
+ LDAP_SERV_RES_S *head_of_result_list = NULL, *res;
+ LDAP_CHOOSE_S cs;
+ WP_ERR_S wp_err;
+ LDAPMessage *e;
+
+ memset(&wp_err, 0, sizeof(wp_err));
+
+ /*
+ * This lookup covers all servers with the impl bit set.
+ * It uses the regular LDAP search parameters that the
+ * user has set, not necessarily just a prefix match
+ * like the rest of the address completion above.
+ */
+ head_of_result_list = ldap_lookup_all_work(query, as.n_serv, 0, NULL, &wp_err);
+ for(res = head_of_result_list; res; res = res->next){
+ for(e = ldap_first_entry(res->ld, res->res);
+ e != NULL;
+ e = ldap_next_entry(res->ld, e)){
+ simple_addr = newaddr = NULL;
+ cs.ld = res->ld;
+ cs.selected_entry = e;
+ cs.info_used = res->info_used;
+ cs.serv = res->serv;
+ addr = address_from_ldap(&cs);
+ if(addr){
+ add_addr_to_return_list(addr, ALC_LDAP, query, flags, &return_list);
+ mail_free_address(&addr);
+ }
+ }
+ }
+
+ if(wp_err.error)
+ fs_give((void **) &wp_err.error);
+
+ if(head_of_result_list)
+ free_ldap_result_list(&head_of_result_list);
+ }
+#endif /* ENABLE_LDAP */
+
+ /* add from current message */
+ if(uid > 0 && stream)
+ env = pine_mail_fetch_structure(stream, uid, &body, FT_UID);
+
+ /* from the envelope addresses */
+ if(env){
+ for(addr = env->from; addr; addr = addr->next)
+ add_addr_to_return_list(addr, ALC_CURR, query, flags, &return_list);
+
+ for(addr = env->reply_to; addr; addr = addr->next)
+ add_addr_to_return_list(addr, ALC_CURR, query, flags, &return_list);
+
+ for(addr = env->sender; addr; addr = addr->next)
+ add_addr_to_return_list(addr, ALC_CURR, query, flags, &return_list);
+
+ for(addr = env->to; addr; addr = addr->next)
+ add_addr_to_return_list(addr, ALC_CURR, query, flags, &return_list);
+
+ for(addr = env->cc; addr; addr = addr->next)
+ add_addr_to_return_list(addr, ALC_CURR, query, flags, &return_list);
+
+ /*
+ * May as well search the body for addresses.
+ * Use this function written for TakeAddr.
+ */
+ if(body){
+ TA_S *talist = NULL, *tp;
+
+ if(grab_addrs_from_body(stream, mail_msgno(stream,uid), body, &talist) > 0){
+ if(talist){
+
+ /* rewind to start */
+ while(talist->prev)
+ talist = talist->prev;
+
+ for(tp = talist; tp; tp = tp->next){
+ addr = tp->addr;
+ if(addr)
+ add_addr_to_return_list(addr, ALC_CURR, query, flags, &return_list);
+ }
+
+ free_talines(&talist);
+ }
+ }
+ }
+ }
+
+
+ /*
+ * Check for and eliminate some duplicates.
+ * The criteria for deciding what is a duplicate is
+ * kind of ad hoc.
+ */
+ for(cp = return_list; cp; cp = cp->next)
+ for(dprev = cp, dp = cp->next; dp; ){
+ if(cp->full_address && dp->full_address
+ && !strucmp(dp->full_address, cp->full_address)
+ && (((cp->matches_bitmap & ALC_ABOOK)
+ && (dp->matches_bitmap & ALC_ABOOK)
+ && (!(dp->matches_bitmap & ALC_NICK && dp->nickname && dp->nickname[0])
+ || ((!cp->addr && !dp->addr) || (cp->addr && dp->addr && !strucmp(cp->addr, dp->addr)))))
+ ||
+ (dp->matches_bitmap & ALC_CURR)
+ ||
+ (dp->matches_bitmap & ALC_LDAP
+ && dp->matches_bitmap & (ALC_FULL | ALC_ADDR))
+ ||
+ (cp->matches_bitmap == dp->matches_bitmap
+ && (!(dp->matches_bitmap & ALC_NICK && dp->nickname && dp->nickname[0])
+ || (dp->nickname && cp->nickname
+ && !strucmp(cp->nickname, dp->nickname)))))){
+ /*
+ * dp is equivalent to cp so eliminate dp
+ */
+ dprev->next = dp->next;
+ dp->next = NULL;
+ free_complete_s(&dp);
+ dp = dprev;
+ }
+
+ dprev = dp;
+ dp = dp->next;
+ }
+
+ return(return_list);
+}
+
+
+void
+add_addr_to_return_list(ADDRESS *addr, unsigned bitmap, char *query,
+ int flags, COMPLETE_S **return_list)
+{
+ char buf[1000];
+ char *newaddr = NULL;
+ char *simple_addr = NULL;
+ COMPLETE_S *new = NULL, *cp;
+ ADDRESS *savenext;
+
+ if(return_list && query && addr && addr->mailbox && addr->host){
+
+ savenext = addr->next;
+ addr->next = NULL;
+ newaddr = addr_list_string(addr, NULL, 0);
+ addr->next = savenext;
+
+ /*
+ * If the start of the full_address actually matches the query
+ * string then mark this as ALC_FULL. This might be helpful
+ * when deciding on the longest unambiguous match.
+ */
+ if(newaddr && newaddr[0] && !struncmp(newaddr, query, strlen(query)))
+ bitmap |= ALC_FULL;
+
+ if(newaddr && newaddr[0] && flags & ALC_INCLUDE_ADDRS){
+ if(addr->mailbox && addr->host
+ && !(addr->host[0] == '@' && addr->host[1] == '\0'))
+ simple_addr = simple_addr_string(addr, buf, sizeof(buf));
+
+ if(simple_addr && !simple_addr[0])
+ simple_addr = NULL;
+
+ if(simple_addr && !struncmp(simple_addr, query, strlen(query)))
+ bitmap |= ALC_ADDR;
+ }
+
+ /*
+ * We used to require && bitmap & (ALC_FULL | ALC_ADDR) before
+ * we would add a match but we think that we should match
+ * other stuff that matches (like middle names), too, unless we
+ * are adding an address from the current message.
+ */
+ if((newaddr && newaddr[0])
+ && (!(bitmap & ALC_CURR) || bitmap & (ALC_FULL | ALC_ADDR))){
+ new = new_complete_s(NULL, newaddr, simple_addr, NULL, NULL, bitmap);
+
+ /* add to end of list */
+ if(*return_list == NULL){
+ *return_list = new;
+ }
+ else{
+ for(cp = *return_list; cp->next; cp = cp->next)
+ ;
+
+ cp->next = new;
+ }
+ }
+
+ if(newaddr)
+ fs_give((void **) &newaddr);
+ }
+}
+
+
+/*
+ * nick = nickname
+ * full = whole thing, like Some Body <someb@there.org>
+ * addr = address part, like someb@there.org
+ */
+COMPLETE_S *
+new_complete_s(char *nick, char *full, char *addr,
+ char *rev_fullname, char *fcc, unsigned matches_bitmap)
+{
+ COMPLETE_S *new = NULL;
+
+ new = (COMPLETE_S *) fs_get(sizeof(*new));
+ memset((void *) new, 0, sizeof(*new));
+ new->nickname = nick ? cpystr(nick) : NULL;
+ new->full_address = full ? cpystr(full) : NULL;
+ new->addr = addr ? cpystr(addr) : NULL;
+ new->rev_fullname = rev_fullname ? cpystr(rev_fullname) : NULL;
+ new->fcc = fcc ? cpystr(fcc) : NULL;
+ new->matches_bitmap = matches_bitmap;
+
+ return(new);
+}
+
+
+void
+free_complete_s(COMPLETE_S **compptr)
+{
+ if(compptr && *compptr){
+ if((*compptr)->next)
+ free_complete_s(&(*compptr)->next);
+
+ if((*compptr)->nickname)
+ fs_give((void **) &(*compptr)->nickname);
+
+ if((*compptr)->full_address)
+ fs_give((void **) &(*compptr)->full_address);
+
+ if((*compptr)->addr)
+ fs_give((void **) &(*compptr)->addr);
+
+ if((*compptr)->rev_fullname)
+ fs_give((void **) &(*compptr)->rev_fullname);
+
+ if((*compptr)->fcc)
+ fs_give((void **) &(*compptr)->fcc);
+
+ fs_give((void **) compptr);
+ }
+}
+
+
+ABOOK_ENTRY_S *
+new_abook_entry_s(AdrBk *ab, a_c_arg_t numarg, unsigned bit)
+{
+ ABOOK_ENTRY_S *new = NULL;
+ adrbk_cntr_t entrynum;
+
+ entrynum = (adrbk_cntr_t) numarg;
+
+ new = (ABOOK_ENTRY_S *) fs_get(sizeof(*new));
+ memset((void *) new, 0, sizeof(*new));
+ new->ab = ab;
+ new->entrynum = entrynum;
+ new->matches_bitmap = bit;
+
+ return(new);
+}
+
+
+void
+free_abook_entry_s(ABOOK_ENTRY_S **aep)
+{
+ if(aep && *aep){
+ if((*aep)->next)
+ free_abook_entry_s(&(*aep)->next);
+
+ fs_give((void **) aep);
+ }
+}
+
+
+/*
+ * Add the second list to the end of the first.
+ */
+void
+combine_abook_entry_lists(ABOOK_ENTRY_S **first, ABOOK_ENTRY_S *second)
+{
+ ABOOK_ENTRY_S *sl;
+
+ if(!second)
+ return;
+
+ if(first){
+ if(*first){
+ for(sl = *first; sl->next; sl = sl->next)
+ ;
+
+ sl->next = second;
+ }
+ else
+ *first = second;
+ }
+}
+
+
+ABOOK_ENTRY_S *
+adrbk_list_of_possible_completions(AdrBk *ab, char *prefix)
+{
+ ABOOK_ENTRY_S *list = NULL, *biglist = NULL;
+
+ if(!ab || !prefix)
+ return(biglist);
+
+ if(ab->nick_trie){
+ list = adrbk_list_of_possible_trie_completions(ab->nick_trie, ab, prefix, ALC_NICK);
+ combine_abook_entry_lists(&biglist, list);
+ }
+
+ if(ab->full_trie){
+ list = adrbk_list_of_possible_trie_completions(ab->full_trie, ab, prefix, ALC_FULL);
+ combine_abook_entry_lists(&biglist, list);
+ }
+
+ if(ab->addr_trie){
+ list = adrbk_list_of_possible_trie_completions(ab->addr_trie, ab, prefix, ALC_ADDR);
+ combine_abook_entry_lists(&biglist, list);
+ }
+
+ if(ab->revfull_trie){
+ list = adrbk_list_of_possible_trie_completions(ab->revfull_trie, ab, prefix, ALC_REVFULL);
+ combine_abook_entry_lists(&biglist, list);
+ }
+
+ return(biglist);
+}
+
+
+/*
+ * Look in this address book for all nicknames, addresses, or fullnames
+ * which begin with the prefix prefix, and return an allocated
+ * list of them.
+ */
+ABOOK_ENTRY_S *
+adrbk_list_of_possible_trie_completions(AdrBk_Trie *trie, AdrBk *ab, char *prefix,
+ unsigned bit)
+{
+ AdrBk_Trie *t;
+ char *p, *lookthisup;
+ char buf[1000];
+ ABOOK_ENTRY_S *list = NULL;
+
+ if(!ab || !prefix || !trie)
+ return(list);
+
+ t = trie;
+
+ /* make lookup case independent */
+
+ for(p = prefix; *p && !(*p & 0x80) && islower((unsigned char) *p); p++)
+ ;
+
+ if(*p){
+ strncpy(buf, prefix, sizeof(buf));
+ buf[sizeof(buf)-1] = '\0';
+ for(p = buf; *p; p++)
+ if(!(*p & 0x80) && isupper((unsigned char) *p))
+ *p = tolower(*p);
+
+ lookthisup = buf;
+ }
+ else
+ lookthisup = prefix;
+
+ p = lookthisup;
+
+ while(*p){
+ /* search for character at this level */
+ while(t->value != *p){
+ if(t->right == NULL)
+ return(list); /* no match */
+
+ t = t->right;
+ }
+
+ if(*++p == '\0') /* matched through end of prefix */
+ break;
+
+ /* need to go down to match next character */
+ if(t->down == NULL) /* no match */
+ return(list);
+
+ t = t->down;
+ }
+
+ /*
+ * If we get here that means we found at least
+ * one entry that matches up through prefix.
+ * Gather_abook_list recursively adds the nicknames starting at
+ * this node.
+ */
+ if(t->entrynum != NO_NEXT){
+ /*
+ * Add it to the list.
+ */
+ list = new_abook_entry_s(ab, t->entrynum, bit);
+ }
+
+ gather_abook_entry_list(ab, t->down, prefix, &list, bit);
+
+ return(list);
+}
+
+
+void
+gather_abook_entry_list(AdrBk *ab, AdrBk_Trie *node, char *prefix, ABOOK_ENTRY_S **list, unsigned bit)
+{
+ char *next_prefix = NULL;
+ size_t l;
+ ABOOK_ENTRY_S *newlist = NULL;
+
+ if(node){
+ if(node->entrynum != NO_NEXT || node->down || node->right){
+ l = strlen(prefix ? prefix : "");
+ if(node->entrynum != NO_NEXT){
+ /*
+ * Add it to the list.
+ */
+ newlist = new_abook_entry_s(ab, node->entrynum, bit);
+ combine_abook_entry_lists(list, newlist);
+ }
+
+ /* same prefix for node->right */
+ if(node->right)
+ gather_abook_entry_list(ab, node->right, prefix, list, bit);
+
+ /* prefix is one longer for node->down */
+ if(node->down){
+ next_prefix = (char *) fs_get((l+2) * sizeof(char));
+ strncpy(next_prefix, prefix ? prefix : "", l+2);
+ next_prefix[l] = node->value;
+ next_prefix[l+1] = '\0';
+ gather_abook_entry_list(ab, node->down, next_prefix, list, bit);
+
+ if(next_prefix)
+ fs_give((void **) &next_prefix);
+ }
+ }
+ }
+}
diff --git a/pith/ablookup.h b/pith/ablookup.h
new file mode 100644
index 00000000..4ffb7aa5
--- /dev/null
+++ b/pith/ablookup.h
@@ -0,0 +1,106 @@
+/*
+ * $Id: ablookup.h 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_ABLOOKUP_INCLUDED
+#define PITH_ABLOOKUP_INCLUDED
+
+
+#include "../pith/state.h"
+#include "../pith/pattern.h"
+#include "../pith/adrbklib.h"
+#include "../pith/bldaddr.h"
+
+
+/*
+ * Flags to adrbk_list_of_completions().
+ * ALC_INCLUDE_ADDRS means that the "addr" element
+ * should be included in the COMPLETE_S list. When
+ * this is not set the entries that match because
+ * of an addr match are still included in the list
+ * but the actual "addr" element itself is not filled
+ * in. That isn't needed by Web Alpine and it does
+ * have a cost, though rather small, to fill it in.
+ */
+#define ALC_INCLUDE_ADDRS 0x1
+#define ALC_INCLUDE_LDAP 0x2
+
+/*
+ * Values OR'd together in matches_bitmaps.
+ * These are not in the same namespace as the flags
+ * above, these are just bits for the matches_bitmaps member.
+ *
+ * If ALC_NICK is set that means there was a prefix match on the nickname.
+ * ALC_FULL is a match on the full address.
+ * ALC_ADDR is a match on the mailbox@host part of the address.
+ * ALC_REVFULL is a match on the Last, First Fullname if the user uses that.
+ * ALC_ABOOK means the entry came from an address book entry
+ * ALC_LDAP means the entry came from an LDAP lookup
+ * ALC_CURR means the entry came from the currently viewed message
+ * This is used in both ABOOK_ENTRY_S and COMPLETE_S structures.
+ */
+#define ALC_NICK 0x01
+#define ALC_FULL 0x02
+#define ALC_REVFULL 0x04
+#define ALC_ADDR 0x08
+#define ALC_ABOOK 0x10
+#define ALC_LDAP 0x20
+#define ALC_CURR 0x40
+#define ALC_FCC 0x80
+
+
+typedef struct abook_entry_list {
+ AdrBk *ab;
+ adrbk_cntr_t entrynum;
+ unsigned matches_bitmap;
+ struct abook_entry_list *next;
+} ABOOK_ENTRY_S;
+
+
+typedef struct completelist {
+ char *nickname; /* the nickname */
+ char *full_address; /* Some Body <someb@there.org> */
+ char *addr; /* someb@there.org (costs extra) */
+ char *rev_fullname; /* optional Last, First version of fullname */
+ char *fcc; /* optional fcc associated with address */
+ unsigned matches_bitmap;
+ struct completelist *next;
+} COMPLETE_S;
+
+
+/* exported protoypes */
+char *get_nickname_from_addr(ADDRESS *, char *, size_t);
+char *get_fcc_from_addr(ADDRESS *, char *, size_t);
+int get_contactinfo_from_addr(ADDRESS *, char **, char **, char **, char **);
+void address_in_abook(MAILSTREAM *, SEARCHSET *, int, PATTERN_S *);
+ADDRESS *abe_to_address(AdrBk_Entry *, AddrScrn_Disp *, AdrBk *, int *);
+char *abe_to_nick_or_addr_string(AdrBk_Entry *, AddrScrn_Disp *, int);
+int address_is_us(ADDRESS *, struct pine *);
+int address_is_same(ADDRESS *, ADDRESS *);
+int abes_are_equal(AdrBk_Entry *, AdrBk_Entry *);
+char *addr_lookup(char *, int *, int);
+AdrBk_Entry *adrbk_lookup_with_opens_by_nick(char *, int, int *, int);
+AdrBk_Entry *address_to_abe(ADDRESS *);
+int contains_regex_special_chars(char *str);
+COMPLETE_S *adrbk_list_of_completions(char *, MAILSTREAM *, imapuid_t, int);
+COMPLETE_S *new_complete_s(char *, char *, char *, char *, char *, unsigned);
+void free_complete_s(COMPLETE_S **);
+ABOOK_ENTRY_S *new_abook_entry_s(AdrBk *, a_c_arg_t, unsigned);
+void free_abook_entry_s(ABOOK_ENTRY_S **);
+void combine_abook_entry_lists(ABOOK_ENTRY_S **, ABOOK_ENTRY_S *);
+ABOOK_ENTRY_S *adrbk_list_of_possible_completions(AdrBk *, char *);
+
+
+#endif /* PITH_ABLOOKUP_INCLUDED */
diff --git a/pith/addrbook.c b/pith/addrbook.c
new file mode 100644
index 00000000..9828e4c6
--- /dev/null
+++ b/pith/addrbook.c
@@ -0,0 +1,369 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: addrbook.c 90 2006-07-19 22:30:36Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ addrbook.c
+ display format/support routines
+ ====*/
+
+#include "../c-client/c-client.h"
+
+#include <system.h>
+#include <general.h>
+
+#include "addrbook.h"
+#include "state.h"
+#include "adrbklib.h"
+#include "abdlc.h"
+#include "conf.h"
+#include "conftype.h"
+#include "status.h"
+#include "debug.h"
+#include "osdep/collate.h"
+
+
+/* internal prototypes */
+void parse_format(char *, COL_S *);
+
+
+
+
+/*
+ * We have to call this to set up the format of the columns. There is a
+ * separate format for each addrbook, so we need to call this for each
+ * addrbook. We call it when the pab's are built. It also depends on
+ * whether or not as.checkboxes is set, so if we go into a Select mode
+ * from the address book maintenance screen we need to re-call this. Since
+ * we can't go back out of ListMode we don't have that problem. Restore_state
+ * has to call it because of the as.checkboxes possibly being different in
+ * the two states.
+ */
+void
+addrbook_new_disp_form(PerAddrBook *pab, char **list, int addrbook_num,
+ int (*prefix_f)(PerAddrBook *, int *))
+{
+ char *last_one;
+ int column = 0;
+
+ dprint((9, "- init_disp_form(%s) -\n",
+ (pab && pab->abnick) ? pab->abnick : "?"));
+
+ memset((void *)pab->disp_form, 0, NFIELDS*sizeof(COL_S));
+ pab->disp_form[1].wtype = WeCalculate; /* so we don't get false AllAuto */
+
+ if(prefix_f)
+ as.do_bold = (*prefix_f)(pab, &column);
+
+ /* if custom format is specified */
+ if(list && list[0] && list[0][0]){
+ /* find the one for addrbook_num */
+ for(last_one = *list;
+ *list != NULL && addrbook_num;
+ addrbook_num--,list++)
+ last_one = *list;
+
+ /* If not enough to go around, last one repeats */
+ if(*list == NULL)
+ parse_format(last_one, &(pab->disp_form[column]));
+ else
+ parse_format(*list, &(pab->disp_form[column]));
+ }
+ else{ /* default */
+ /* If 2nd wtype is AllAuto, the widths are calculated old way */
+ pab->disp_form[1].wtype = AllAuto;
+
+ pab->disp_form[column++].type = Nickname;
+ pab->disp_form[column++].type = Fullname;
+ pab->disp_form[column++].type = Addr;
+ /* Fill in rest */
+ while(column < NFIELDS)
+ pab->disp_form[column++].type = Notused;
+ }
+}
+
+
+struct parse_tokens {
+ char *name;
+ ColumnType ctype;
+};
+
+struct parse_tokens ptokens[] = {
+ {"NICKNAME", Nickname},
+ {"FULLNAME", Fullname},
+ {"ADDRESS", Addr},
+ {"FCC", Filecopy},
+ {"COMMENT", Comment},
+ {"DEFAULT", Def},
+ {NULL, Notused}
+};
+
+/*
+ * Parse format_str and fill in disp_form structure based on what's there.
+ *
+ * Args: format_str -- The format string from pinerc.
+ * disp_form -- This is where we fill in the answer.
+ *
+ * The format string consists of special tokens which give the order of
+ * the columns to be displayed. The possible tokens are NICKNAME,
+ * FULLNAME, ADDRESS, FCC, COMMENT. If a token is followed by
+ * parens with an integer inside (FULLNAME(16)) then that means we
+ * make that variable that many characters wide. If it is a percentage, we
+ * allocate that percentage of the columns to that variable. If no
+ * parens, that means we calculate it for the user. The tokens are
+ * delimited by white space. A token of DEFAULT means to calculate the
+ * whole thing as we would if no spec was given. This makes it possible
+ * to specify default for one addrbook and something special for another.
+ */
+void
+parse_format(char *format_str, COL_S *disp_form)
+{
+ int column = 0;
+ char *p, *q;
+ struct parse_tokens *pt;
+ int nicknames, fullnames, addresses, not_allauto;
+ int warnings = 0;
+
+ p = format_str;
+ while(p && *p && column < NFIELDS){
+ p = skip_white_space(p); /* space for next word */
+
+ /* look for the ptoken this word matches */
+ for(pt = ptokens; pt->name; pt++)
+ if(!struncmp(pt->name, p, strlen(pt->name)))
+ break;
+
+ /* ignore unrecognized word */
+ if(!pt->name){
+ char *r;
+
+ if((r=strindex(p, SPACE)) != NULL)
+ *r = '\0';
+
+ dprint((2, "parse_format: ignoring unrecognized word \"%s\" in address-book-formats\n", p ? p : "?"));
+ q_status_message1(SM_ORDER, warnings++==0 ? 1 : 0, 4,
+ /* TRANSLATORS: an informative error message */
+ _("Ignoring unrecognized word \"%s\" in Address-Book-Formats"), p);
+ /* put back space */
+ if(r)
+ *r = SPACE;
+
+ /* skip unrecognized word */
+ while(p && *p && !isspace((unsigned char)(*p)))
+ p++;
+
+ continue;
+ }
+
+ disp_form[column].type = pt->ctype;
+
+ /* skip over name and look for parens */
+ p += strlen(pt->name);
+ if(*p == '('){
+ p++;
+ q = p;
+ while(p && *p && isdigit((unsigned char)*p))
+ p++;
+
+ if(p && *p && *p == ')' && p > q){
+ disp_form[column].wtype = Fixed;
+ disp_form[column].req_width = atoi(q);
+ }
+ else if(p && *p && *p == '%' && p > q){
+ disp_form[column].wtype = Percent;
+ disp_form[column].req_width = atoi(q);
+ }
+ else{
+ disp_form[column].wtype = WeCalculate;
+ if(disp_form[column].type == Nickname)
+ disp_form[column].req_width = 8;
+ else
+ disp_form[column].req_width = 3;
+ }
+ }
+ else{
+ disp_form[column].wtype = WeCalculate;
+ if(disp_form[column].type == Nickname)
+ disp_form[column].req_width = 8;
+ else
+ disp_form[column].req_width = 3;
+ }
+
+ if(disp_form[column].type == Def){
+ /* If any type is DEFAULT, the widths are calculated old way */
+assign_default:
+ column = 0;
+
+ disp_form[column].wtype = AllAuto;
+ disp_form[column++].type = Nickname;
+ disp_form[column].wtype = AllAuto;
+ disp_form[column++].type = Fullname;
+ disp_form[column].wtype = AllAuto;
+ disp_form[column++].type = Addr;
+ /* Fill in rest */
+ while(column < NFIELDS)
+ disp_form[column++].type = Notused;
+
+ return;
+ }
+
+ column++;
+ /* skip text at end of word */
+ while(p && *p && !isspace((unsigned char)(*p)))
+ p++;
+ }
+
+ if(column == 0){
+ q_status_message(SM_ORDER, 0, 4,
+ _("Address-Book-Formats has no recognizable words, using default format"));
+ goto assign_default;
+ }
+
+ /* Fill in rest */
+ while(column < NFIELDS)
+ disp_form[column++].type = Notused;
+
+ /* check to see if user is just re-ordering default fields */
+ nicknames = 0;
+ fullnames = 0;
+ addresses = 0;
+ not_allauto = 0;
+ for(column = 0; column < NFIELDS; column++){
+ if(disp_form[column].type != Notused
+ && disp_form[column].wtype != WeCalculate)
+ not_allauto++;
+
+ switch(disp_form[column].type){
+ case Nickname:
+ nicknames++;
+ break;
+
+ case Fullname:
+ fullnames++;
+ break;
+
+ case Addr:
+ addresses++;
+ break;
+
+ case Filecopy:
+ case Comment:
+ not_allauto++;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /*
+ * Special case: if there is no address field specified, we put in
+ * a special field called WhenNoAddrDisplayed, which causes list
+ * entries to be displayable in all cases.
+ */
+ if(!addresses){
+ for(column = 0; column < NFIELDS; column++)
+ if(disp_form[column].type == Notused)
+ break;
+
+ if(column < NFIELDS){
+ disp_form[column].type = WhenNoAddrDisplayed;
+ disp_form[column].wtype = Special;
+ }
+ }
+
+ if(nicknames == 1 && fullnames == 1 && addresses == 1 && not_allauto == 0)
+ disp_form[0].wtype = AllAuto; /* set to do default widths */
+}
+
+
+/*
+ * Find the first selectable line greater than or equal to line. That is,
+ * the first line the cursor is allowed to start on.
+ * (If there are none >= line, it will find the highest one.)
+ *
+ * Returns the line number of the found line or NO_LINE if there isn't one.
+ */
+long
+first_selectable_line(long int line)
+{
+ long lineno;
+ register PerAddrBook *pab;
+ int i;
+
+ /* skip past non-selectable lines */
+ for(lineno=line;
+ !line_is_selectable(lineno) && dlist(lineno)->type != End;
+ lineno++)
+ ;/* do nothing */
+
+ if(line_is_selectable(lineno))
+ return(lineno);
+
+ /*
+ * There were no selectable lines from lineno on down. Trying looking
+ * back up the list.
+ */
+ for(lineno=line-1;
+ !line_is_selectable(lineno) && dlist(lineno)->type != Beginning;
+ lineno--)
+ ;/* do nothing */
+
+ if(line_is_selectable(lineno))
+ return(lineno);
+
+ /*
+ * No selectable lines at all.
+ * If some of the addrbooks are still not displayed, it is too
+ * early to set the no_op_possbl flag. Or, if some of the addrbooks
+ * are empty but writable, then we should not set it either.
+ */
+ for(i = 0; i < as.n_addrbk; i++){
+ pab = &as.adrbks[i];
+ if(pab->ostatus != Open &&
+ pab->ostatus != HalfOpen &&
+ pab->ostatus != ThreeQuartOpen)
+ return NO_LINE;
+
+ if(pab->access == ReadWrite && adrbk_count(pab->address_book) == 0)
+ return NO_LINE;
+ }
+
+ as.no_op_possbl++;
+ return NO_LINE;
+}
+
+
+/*
+ * Returns 1 if this line is of a type that can have a cursor on it.
+ */
+int
+line_is_selectable(long int lineno)
+{
+ register AddrScrn_Disp *dl;
+
+ if((dl = dlist(lineno)) && (dl->type == Text ||
+ dl->type == ListEmpty ||
+ dl->type == TitleCmb ||
+ dl->type == Beginning ||
+ dl->type == End)){
+
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/pith/addrbook.h b/pith/addrbook.h
new file mode 100644
index 00000000..9c328846
--- /dev/null
+++ b/pith/addrbook.h
@@ -0,0 +1,30 @@
+/*
+ * $Id: addrbook.h 82 2006-07-12 23:36:59Z mikes@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_ADDRBOOK_INCLUDED
+#define PITH_ADDRBOOK_INCLUDED
+
+
+#include "adrbklib.h"
+
+
+/* exported protoypes */
+void addrbook_new_disp_form(PerAddrBook *, char **, int, int (*)(PerAddrBook *, int *));
+long first_selectable_line(long);
+int line_is_selectable(long);
+
+
+#endif /* PITH_ADDRBOOK_INCLUDED */
diff --git a/pith/addrstring.c b/pith/addrstring.c
new file mode 100644
index 00000000..928de43e
--- /dev/null
+++ b/pith/addrstring.c
@@ -0,0 +1,452 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: addrstring.c 770 2007-10-24 00:23:09Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/addrstring.h"
+#include "../pith/state.h"
+#include "../pith/copyaddr.h"
+#include "../pith/charset.h"
+#include "../pith/stream.h"
+
+
+/*
+ * Internal prototypes
+ */
+void rfc822_write_address_decode(char *, size_t, ADDRESS *, int);
+
+
+/*
+ * Format an address structure into a string
+ *
+ * Args: addr -- Single ADDRESS structure to turn into a string
+ *
+ * Result: Fills in buf and returns pointer to it.
+ * Just uses the c-client call to do this.
+ * (the address is not rfc1522 decoded)
+ */
+char *
+addr_string(struct mail_address *addr, char *buf, size_t buflen)
+{
+ ADDRESS *next_addr;
+ RFC822BUFFER rbuf;
+
+ *buf = '\0';
+ next_addr = addr->next;
+ addr->next = NULL;
+ rbuf.f = dummy_soutr;
+ rbuf.s = NULL;
+ rbuf.beg = buf;
+ rbuf.cur = buf;
+ rbuf.end = buf+buflen-1;
+ rfc822_output_address_list(&rbuf, addr, 0L, NULL);
+ *rbuf.cur = '\0';
+ addr->next = next_addr;
+ return(buf);
+}
+
+
+/*
+ * Same as addr_string only it doesn't have to be a
+ * single address.
+ */
+char *
+addr_string_mult(struct mail_address *addr, char *buf, size_t buflen)
+{
+ RFC822BUFFER rbuf;
+
+ *buf = '\0';
+ rbuf.f = dummy_soutr;
+ rbuf.s = NULL;
+ rbuf.beg = buf;
+ rbuf.cur = buf;
+ rbuf.end = buf+buflen-1;
+ rfc822_output_address_list(&rbuf, addr, 0L, NULL);
+ *rbuf.cur = '\0';
+ return(buf);
+}
+
+
+/*
+ * Format an address structure into a simple string: "mailbox@host"
+ *
+ * Args: addr -- Single ADDRESS structure to turn into a string
+ * buf -- buffer to write address in;
+ *
+ * Result: Returns pointer to buf;
+ */
+char *
+simple_addr_string(struct mail_address *addr, char *buf, size_t buflen)
+{
+ RFC822BUFFER rbuf;
+
+ rbuf.f = dummy_soutr;
+ rbuf.s = NULL;
+ rbuf.beg = buf;
+ rbuf.cur = buf;
+ rbuf.end = buf+buflen-1;
+ rfc822_output_address(&rbuf, addr);
+ *rbuf.cur = '\0';
+
+ return(buf);
+}
+
+
+/*
+ * Format an address structure into a simple string: "mailbox@host"
+ * Like simple_addr_string but can be multiple addresses.
+ *
+ * Args: addr -- ADDRESS structure to turn into a string
+ * buf -- buffer to write address in;
+ * buflen -- length of buffer
+ * sep -- separator string
+ *
+ * Result: Returns pointer to internal static formatted string.
+ * Just uses the c-client call to do this.
+ */
+char *
+simple_mult_addr_string(struct mail_address *addr, char *buf, size_t buflen, char *sep)
+{
+ ADDRESS *a;
+ char *dest = buf;
+ size_t seplen = 0;
+
+ if(sep)
+ seplen = strlen(sep);
+
+ *dest = '\0';
+ for(a = addr; a; a = a->next){
+ if(dest > buf && seplen > 0 && buflen-1-(dest-buf) >= seplen){
+ strncpy(dest, sep, seplen);
+ dest += seplen;
+ *dest = '\0';
+ }
+
+ simple_addr_string(a, dest, buflen-(dest-buf));
+ dest += strlen(dest);
+ }
+
+ buf[buflen-1] = '\0';
+
+ return(buf);
+}
+
+
+/*
+ * 1522 encode the personal name portion of addr and return an allocated
+ * copy of the resulting address string.
+ */
+char *
+encode_fullname_of_addrstring(char *addr, char *charset)
+{
+ char *pers_encoded,
+ *tmp_a_string,
+ *ret = NULL;
+ ADDRESS *adr;
+ static char *fakedomain = "@";
+ RFC822BUFFER rbuf;
+ size_t len;
+
+ tmp_a_string = cpystr(addr ? addr : "");
+ adr = NULL;
+ rfc822_parse_adrlist(&adr, tmp_a_string, fakedomain);
+ fs_give((void **)&tmp_a_string);
+
+ if(!adr)
+ return(cpystr(""));
+
+ if(adr->personal && adr->personal[0]){
+ pers_encoded = cpystr(rfc1522_encode(tmp_20k_buf, SIZEOF_20KBUF,
+ (unsigned char *)adr->personal,
+ charset));
+ fs_give((void **)&adr->personal);
+ adr->personal = pers_encoded;
+ }
+
+ len = est_size(adr);
+ ret = (char *) fs_get(len * sizeof(char));
+ ret[0] = '\0';
+ rbuf.f = dummy_soutr;
+ rbuf.s = NULL;
+ rbuf.beg = ret;
+ rbuf.cur = ret;
+ rbuf.end = ret+len-1;
+ rfc822_output_address_list(&rbuf, adr, 0L, NULL);
+ *rbuf.cur = '\0';
+ mail_free_address(&adr);
+ return(ret);
+}
+
+
+/*
+ * 1522 decode the personal name portion of addr and return an allocated
+ * copy of the resulting address string.
+ */
+char *
+decode_fullname_of_addrstring(char *addr, int verbose)
+{
+ char *pers_decoded,
+ *tmp_a_string,
+ *ret = NULL;
+ ADDRESS *adr;
+ static char *fakedomain = "@";
+ RFC822BUFFER rbuf;
+ size_t len;
+
+ tmp_a_string = cpystr(addr ? addr : "");
+ adr = NULL;
+ rfc822_parse_adrlist(&adr, tmp_a_string, fakedomain);
+ fs_give((void **)&tmp_a_string);
+
+ if(!adr)
+ return(cpystr(""));
+
+ if(adr->personal && adr->personal[0]){
+ pers_decoded
+ = cpystr((char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, adr->personal));
+ fs_give((void **)&adr->personal);
+ adr->personal = pers_decoded;
+ }
+
+ len = est_size(adr);
+ ret = (char *) fs_get(len * sizeof(char));
+ ret[0] = '\0';
+ rbuf.f = dummy_soutr;
+ rbuf.s = NULL;
+ rbuf.beg = ret;
+ rbuf.cur = ret;
+ rbuf.end = ret+len-1;
+ rfc822_output_address_list(&rbuf, adr, 0L, NULL);
+ *rbuf.cur = '\0';
+ mail_free_address(&adr);
+ return(ret);
+}
+
+
+/*
+ * Turn a list of address structures into a formatted string
+ *
+ * Args: adrlist -- An adrlist
+ * f -- Function to use to print one address in list. If NULL,
+ * use rfc822_write_address_decode to print whole list.
+ * do_quote -- Quote quotes and dots (only used if f == NULL).
+ * Result: comma separated list of addresses which is
+ * malloced here and returned
+ * (the list is rfc1522 decoded unless f is *not* NULL)
+ */
+char *
+addr_list_string(struct mail_address *adrlist,
+ char *(*f)(struct mail_address *, char *, size_t),
+ int do_quote)
+{
+ size_t len;
+ char *list, *s, string[MAX_ADDR_EXPN+1];
+ register ADDRESS *a;
+
+ if(!adrlist)
+ return(cpystr(""));
+
+ if(f){
+ len = 0;
+ for(a = adrlist; a; a = a->next)
+ len += (strlen((*f)(a, string, sizeof(string))) + 2);
+
+ list = (char *) fs_get((len+1) * sizeof(char));
+ s = list;
+ s[0] = '\0';
+
+ for(a = adrlist; a; a = a->next){
+ sstrncpy(&s, (*f)(a, string, sizeof(string)), len-(s-list));
+ if(a->next && len-(s-list) > 2){
+ *s++ = ',';
+ *s++ = SPACE;
+ }
+ }
+ }
+ else{
+ len = est_size(adrlist);
+ list = (char *) fs_get((len+1) * sizeof(char));
+ list[0] = '\0';
+ rfc822_write_address_decode(list, len+1, adrlist, do_quote);
+ removing_leading_and_trailing_white_space(list);
+ }
+
+ list[len] = '\0';
+ return(list);
+}
+
+
+static long rfc822_dummy_soutr (void *stream, char *string)
+{
+ return LONGT;
+}
+
+/*
+ * Copied from c-client/rfc822.c buf with dot and double quote removed.
+ */
+static const char *rspecials_minus_quote_and_dot = "()<>@,;:\\[]\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\177";
+
+/* Write RFC822 address with 1522 decoding of personal name
+ * and optional quoting.
+ *
+ * The idea is that there are some places where we'd just like to display
+ * the personal name as is before applying confusing quoting. However,
+ * we do want to be careful not to break things that should be quoted so
+ * we'll only use this where we are sure. Quoting may look ugly but it
+ * doesn't usually break anything.
+ */
+void
+rfc822_write_address_decode(char *dest, size_t destlen, struct mail_address *adr, int do_quote)
+{
+ RFC822BUFFER buf;
+ extern const char *rspecials;
+ ADDRESS *copy, *a;
+
+ /*
+ * We want to print the adr list after decoding it. C-client knows
+ * how to parse and print, so we want to use that. But c-client
+ * doesn't decode. So we make a copy of the address list, decode
+ * things there, and let c-client print that.
+ */
+ copy = copyaddrlist(adr);
+ for(a = copy; a; a = a->next){
+ if(a->host){ /* ordinary address? */
+ if(a->personal && *a->personal){
+ unsigned char *p;
+
+ p = rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
+ SIZEOF_20KBUF, a->personal);
+
+ if(p && (char *) p != a->personal){
+ fs_give((void **) &a->personal);
+ a->personal = cpystr((char *) p);
+ }
+ }
+ }
+ else if(a->mailbox && *a->mailbox){ /* start of group? */
+ unsigned char *p;
+
+ p = rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf, SIZEOF_20KBUF, a->mailbox);
+
+ if(p && (char *) p != a->mailbox){
+ fs_give((void **) &a->mailbox);
+ a->mailbox = cpystr((char *) p);
+ }
+ }
+ }
+
+ buf.end = (buf.beg = buf.cur = dest) + destlen;
+ buf.f = rfc822_dummy_soutr;
+ *buf.cur = '\0';
+ buf.s = NIL;
+
+ (void) rfc822_output_address_list(&buf, copy, 0,
+ do_quote ? rspecials : rspecials_minus_quote_and_dot);
+
+ *buf.cur = '\0';
+
+ if(copy)
+ mail_free_address(&copy);
+}
+
+
+/*
+ * Compute an upper bound on the size of the array required by
+ * rfc822_write_address for this list of addresses.
+ *
+ * Args: adrlist -- The address list.
+ *
+ * Returns -- an integer giving the upper bound
+ */
+int
+est_size(struct mail_address *a)
+{
+ int cnt = 0;
+
+ for(; a; a = a->next){
+
+ /* two times personal for possible quoting */
+ cnt += 2 * (a->personal ? (strlen(a->personal)+1) : 0);
+ cnt += 2 * (a->mailbox ? (strlen(a->mailbox)+1) : 0);
+ cnt += (a->adl ? strlen(a->adl) : 0);
+ cnt += (a->host ? strlen(a->host) : 0);
+
+ /*
+ * add room for:
+ * possible single space between fullname and addr
+ * left and right brackets
+ * @ sign
+ * possible : for route addr
+ * , <space>
+ *
+ * So I really think that adding 7 is enough. Instead, I'll add 10.
+ */
+ cnt += 10;
+ }
+
+ return(MAX(cnt, 50)); /* just making sure */
+}
+
+
+/*
+ * Returns the number of addresses in the list.
+ */
+int
+count_addrs(struct mail_address *adrlist)
+{
+ int cnt = 0;
+
+ while(adrlist){
+ if(adrlist->mailbox && adrlist->mailbox[0])
+ cnt++;
+
+ adrlist = adrlist->next;
+ }
+
+ return(cnt);
+}
+
+
+/*
+ * Buf is at least size maxlen+1
+ */
+void
+a_little_addr_string(struct mail_address *addr, char *buf, size_t maxlen)
+{
+ buf[0] = '\0';
+ if(addr){
+ if(addr->personal && addr->personal[0]){
+ char tmp[MAILTMPLEN];
+
+ snprintf(tmp, sizeof(tmp), "%s", addr->personal);
+ iutf8ncpy(buf, (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, tmp),
+ maxlen);
+ }
+ else if(addr->mailbox && addr->mailbox[0]){
+ strncpy(buf, addr->mailbox, maxlen);
+ buf[maxlen] = '\0';
+ if(addr->host && addr->host[0] && addr->host[0] != '.'){
+ strncat(buf, "@", maxlen+1-1-strlen(buf));
+ strncat(buf, addr->host, maxlen+1-1-strlen(buf));
+ }
+ }
+ }
+
+ buf[maxlen] = '\0';
+}
diff --git a/pith/addrstring.h b/pith/addrstring.h
new file mode 100644
index 00000000..16ae8e56
--- /dev/null
+++ b/pith/addrstring.h
@@ -0,0 +1,37 @@
+/*
+ * $Id: addrstring.h 770 2007-10-24 00:23:09Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_ADDRSTRING_INCLUDED
+#define PITH_ADDRSTRING_INCLUDED
+
+
+#define RAWFIELD "-RAW-FIELD-"
+
+
+/* exported protoypes */
+char *addr_string(ADDRESS *, char *, size_t);
+char *addr_string_mult(ADDRESS *, char *, size_t);
+char *simple_addr_string(ADDRESS *, char *, size_t);
+char *simple_mult_addr_string(ADDRESS *, char *, size_t, char *);
+char *encode_fullname_of_addrstring(char *, char *);
+char *decode_fullname_of_addrstring(char *, int);
+char *addr_list_string(ADDRESS *, char *(*)(ADDRESS *, char *, size_t), int);
+int est_size(ADDRESS *);
+int count_addrs(ADDRESS *);
+void a_little_addr_string(ADDRESS *, char *, size_t);
+
+
+#endif /* PITH_ADDRSTRING_INCLUDED */
diff --git a/pith/adjtime.c b/pith/adjtime.c
new file mode 100644
index 00000000..bc125d35
--- /dev/null
+++ b/pith/adjtime.c
@@ -0,0 +1,61 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: adjtime.c 761 2007-10-23 22:35:18Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/adjtime.h"
+
+
+/*
+ * MSC ver 7.0 and less times are since 1900, everybody else's time so far
+ * is since 1970. sheesh.
+ */
+#if defined(DOS) && (_MSC_VER == 700)
+#define EPOCH_ADJ ((time_t)((time_t)(70*365 + 18) * (time_t)86400))
+#endif
+
+/*
+ * Adjust the mtime to return time since Unix epoch. DOS is off by 70 years.
+ */
+time_t
+get_adj_time(void)
+{
+ time_t tt;
+
+ tt = time((time_t *)0);
+
+#ifdef EPOCH_ADJ
+ tt -= EPOCH_ADJ;
+#endif
+
+ return(tt);
+}
+
+
+time_t
+get_adj_name_file_mtime(char *name)
+{
+ time_t mtime;
+
+ mtime = name_file_mtime(name);
+
+#ifdef EPOCH_ADJ
+ if(mtime != (time_t)(-1))
+ mtime -= EPOCH_ADJ;
+#endif
+
+ return(mtime);
+}
diff --git a/pith/adjtime.h b/pith/adjtime.h
new file mode 100644
index 00000000..355b1020
--- /dev/null
+++ b/pith/adjtime.h
@@ -0,0 +1,35 @@
+/*
+ * $Id: adjtime.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_TIME_INCLUDED
+#define PITH_TIME_INCLUDED
+
+
+/*
+ * This is just like a struct timeval. We need it for portability to systems
+ * that don't have a struct timeval.
+ */
+typedef struct our_time_val {
+ long sec;
+ long usec;
+} TIMEVAL_S;
+
+
+/* exported protoypes */
+time_t get_adj_time(void);
+time_t get_adj_name_file_mtime(char *);
+
+
+#endif /* PITH_TIME_INCLUDED */
diff --git a/pith/adrbklib.c b/pith/adrbklib.c
new file mode 100644
index 00000000..01d00353
--- /dev/null
+++ b/pith/adrbklib.c
@@ -0,0 +1,6028 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: adrbklib.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
+#endif
+
+/* ========================================================================
+ * Copyright 2006-2009 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/adrbklib.h"
+#include "../pith/abdlc.h"
+#include "../pith/addrbook.h"
+#include "../pith/addrstring.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/status.h"
+#include "../pith/remote.h"
+#include "../pith/tempfile.h"
+#include "../pith/bldaddr.h"
+#include "../pith/signal.h"
+#include "../pith/busy.h"
+#include "../pith/util.h"
+
+
+AddrScrState as;
+
+void (*pith_opt_save_and_restore)(int, SAVE_STATE_S *);
+
+
+/*
+ * We don't want any end of line fixups to occur, so include "b" in DOS modes.
+ */
+#if defined(DOS) || defined(OS2)
+#define ADRBK_NAME "addrbook"
+#else
+#define ADRBK_NAME ".addressbook"
+#endif
+
+#define OPEN_WRITE_MODE (O_TRUNC|O_WRONLY|O_CREAT|O_BINARY)
+
+
+#ifndef MAXPATH
+#define MAXPATH 1000 /* Longest file path we can deal with */
+#endif
+
+#define TABWIDTH 8
+#define INDENTSTR " "
+#define INDENTXTRA " : "
+#define INDENT 3 /* length of INDENTSTR */
+
+
+#define SLOP 3
+
+static int writing; /* so we can give understandable error message */
+
+AdrBk *edited_abook;
+
+static char empty[] = "";
+
+jmp_buf jump_over_qsort;
+
+
+/* internal prototypes */
+int copy_abook_to_tempfile(AdrBk *, char *, size_t);
+char *dir_containing(char *);
+char *get_next_abook_entry(FILE *, int);
+void strip_addr_string(char *, char **, char **);
+void adrbk_check_local_validity(AdrBk *, long);
+int write_single_abook_entry(AdrBk_Entry *, FILE *, int *, int *, int *, int *);
+char *backcompat_encoding_for_abook(char *, size_t, char *, size_t, char *);
+int percent_abook_saved(void);
+void exp_del_nth(EXPANDED_S *, a_c_arg_t);
+void exp_add_nth(EXPANDED_S *, a_c_arg_t);
+int cmp_ae_by_full_lists_last(const qsort_t *,const qsort_t *);
+int cmp_cntr_by_full_lists_last(const qsort_t *, const qsort_t *);
+int cmp_ae_by_full(const qsort_t *, const qsort_t *);
+int cmp_cntr_by_full(const qsort_t *, const qsort_t *);
+int cmp_ae_by_nick_lists_last(const qsort_t *,const qsort_t *);
+int cmp_cntr_by_nick_lists_last(const qsort_t *, const qsort_t *);
+int cmp_ae_by_nick(const qsort_t *, const qsort_t *);
+int cmp_cntr_by_nick(const qsort_t *, const qsort_t *);
+int cmp_addr(const qsort_t *, const qsort_t *);
+void sort_addr_list(char **);
+int build_abook_datastruct(AdrBk *, char *, size_t);
+AdrBk_Entry *init_ae(AdrBk *, AdrBk_Entry *, char *);
+adrbk_cntr_t count_abook_entries_on_disk(AdrBk *, a_c_arg_t *);
+AdrBk_Entry *adrbk_get_delae(AdrBk *, a_c_arg_t);
+void free_ae_parts(AdrBk_Entry *);
+void add_entry_to_trie(AdrBk_Trie **, char *, a_c_arg_t);
+int build_abook_tries(AdrBk *, char *);
+void repair_abook_tries(AdrBk *);
+adrbk_cntr_t lookup_nickname_in_trie(AdrBk *, char *);
+adrbk_cntr_t lookup_address_in_trie(AdrBk *, char *);
+adrbk_cntr_t lookup_in_abook_trie(AdrBk_Trie *, char *);
+void free_abook_trie(AdrBk_Trie **);
+adrbk_cntr_t re_sort_particular_entry(AdrBk *, a_c_arg_t);
+void move_ab_entry(AdrBk *, a_c_arg_t, a_c_arg_t);
+void insert_ab_entry(AdrBk *, a_c_arg_t, AdrBk_Entry *, int);
+void delete_ab_entry(AdrBk *, a_c_arg_t, int);
+void defvalue_ae(AdrBk_Entry *);
+
+
+/*
+ * Open, read, and parse an address book.
+ *
+ * Args: pab -- the PerAddrBook structure
+ * homedir -- the user's home directory if specified
+ * warning -- put "why failed" message to user here
+ * (provide space of at least 201 chars)
+ *
+ * If filename is NULL, the default will be used in the homedir
+ * passed in. If homedir is NULL, the current dir will be used.
+ * If filename is not NULL and is an absolute path, just the filename
+ * will be used. Otherwise, it will be used relative to the homedir, or
+ * to the current dir depending on whether or not homedir is NULL.
+ *
+ * Expected addressbook file format is:
+ * <nickname>\t<fullname>\t<address_field>\t<fcc>\t<comment>
+ *
+ * The last two fields (\t<fcc>\t<comment>) are optional.
+ *
+ * Lines that start with SPACE are continuation lines. Ends of lines are
+ * treated as if they were spaces. The address field is either a single
+ * address or a list of comma-separated addresses inside parentheses.
+ *
+ * Fields missing from the end of an entry are considered blank.
+ *
+ * Commas in the address field will cause problems, as will tabs in any
+ * field.
+ *
+ * There may be some deleted entries in the addressbook that don't get
+ * used. They look like regular entries except their nicknames start
+ * with the string "#DELETED-YY/MM/DD#".
+ */
+AdrBk *
+adrbk_open(PerAddrBook *pab, char *homedir, char *warning, size_t warninglen, int sort_rule)
+{
+ char path[MAXPATH], *filename;
+ AdrBk *ab;
+ int abook_validity_was_minusone = 0;
+
+
+ filename = pab ? pab->filename : NULL;
+
+ dprint((2, "- adrbk_open(%s) -\n", filename ? filename : ""));
+
+ ab = (AdrBk *)fs_get(sizeof(AdrBk));
+ memset(ab, 0, sizeof(*ab));
+
+ ab->orig_filename = filename ? cpystr(filename) : NULL;
+
+ if(pab->type & REMOTE_VIA_IMAP){
+ int try_cache;
+
+ ab->type = Imap;
+
+ if(!ab->orig_filename || *(ab->orig_filename) != '{'){
+ dprint((1, "adrbk_open: remote: filename=%s\n",
+ ab->orig_filename ? ab->orig_filename : "NULL"));
+ goto bail_out;
+ }
+
+ if(!(ab->rd = rd_new_remdata(RemImap, ab->orig_filename, REMOTE_ABOOK_SUBTYPE))){
+ dprint((1,
+ "adrbk_open: remote: new_remdata failed: %s\n",
+ ab->orig_filename ? ab->orig_filename : "NULL"));
+ goto bail_out;
+ }
+
+ /* Transfer responsibility for the storage object */
+ ab->rd->so = pab->so;
+ pab->so = NULL;
+
+ try_cache = rd_read_metadata(ab->rd);
+
+ if(ab->rd->lf)
+ ab->filename = cpystr(ab->rd->lf);
+
+ /* Transfer responsibility for removal of temp file */
+ if(ab->rd->flags & DEL_FILE){
+ ab->flags |= DEL_FILE;
+ ab->rd->flags &= ~DEL_FILE;
+ }
+
+ if(pab->access == MaybeRorW){
+ if(ab->rd->read_status == 'R')
+ pab->access = ab->rd->access = ReadOnly;
+ else
+ pab->access = ab->rd->access = ReadWrite;
+ }
+ else if(pab->access == ReadOnly){
+ /*
+ * Pass on readonly-ness from being a global addrbook.
+ * This should cause us to open the remote folder readonly,
+ * avoiding error messages about readonly-ness.
+ */
+ ab->rd->access = ReadOnly;
+ }
+
+ /*
+ * The plan is to fetch addrbook data and copy into local file.
+ * Then we open the local copy for reading. We use the IMAP STATUS
+ * command to tell us if we need to update from the remote addrbook.
+ *
+ * If access is NoExists, that probably means we had trouble
+ * opening the remote folder in the adrbk_access routine.
+ * In that case we'll use a cached copy if we have one.
+ */
+ if(pab->access != NoExists){
+
+bootstrap_nocheck_policy:
+ if(try_cache && ps_global->remote_abook_validity == -1 &&
+ !abook_validity_was_minusone)
+ abook_validity_was_minusone++;
+ else{
+ rd_check_remvalid(ab->rd, 1L);
+ abook_validity_was_minusone = 0;
+ }
+
+ /*
+ * If the cached info on this addrbook says it is readonly but
+ * it looks like it's been fixed now, change it to readwrite.
+ */
+ if(!(pab->type & GLOBAL) && ab->rd->read_status == 'R'){
+ /*
+ * We go to this trouble since readonly addrbooks
+ * are likely a mistake. They are usually supposed to be
+ * readwrite. So we open it and check if it's been fixed.
+ */
+ rd_check_readonly_access(ab->rd);
+ if(ab->rd->read_status == 'W'){
+ pab->access = ab->rd->access = ReadWrite;
+ ab->rd->flags |= REM_OUTOFDATE;
+ }
+ else
+ pab->access = ab->rd->access = ReadOnly;
+ }
+
+ if(ab->rd->flags & REM_OUTOFDATE){
+ if(rd_update_local(ab->rd) != 0){
+ dprint((1,
+ "adrbk_open: remote: rd_update_local failed\n"));
+ /*
+ * Don't give up altogether. We still may be
+ * able to use a cached copy.
+ */
+ }
+ else{
+ dprint((7,
+ "%s: copied remote to local (%ld)\n",
+ ab->rd->rn ? ab->rd->rn : "?",
+ (long)ab->rd->last_use));
+ }
+ }
+
+ if(pab->access == ReadWrite)
+ ab->rd->flags |= DO_REMTRIM;
+ }
+
+ /* If we couldn't get to remote folder, try using the cached copy */
+ if(pab->access == NoExists || ab->rd->flags & REM_OUTOFDATE){
+ if(try_cache){
+ pab->access = ab->rd->access = ReadOnly;
+ ab->rd->flags |= USE_OLD_CACHE;
+ q_status_message(SM_ORDER, 3, 4,
+ _("Can't contact remote address book server, using cached copy"));
+ dprint((2,
+ "Can't open remote addrbook %s, using local cached copy %s readonly\n",
+ ab->rd->rn ? ab->rd->rn : "?",
+ ab->rd->lf ? ab->rd->lf : "?"));
+ }
+ else
+ goto bail_out;
+ }
+ }
+ else{
+ ab->type = Local;
+
+ /*------------ figure out and save name of file to open ---------*/
+ if(filename == NULL){
+ if(homedir != NULL){
+ build_path(path, homedir, ADRBK_NAME, sizeof(path));
+ ab->filename = cpystr(path);
+ }
+ else
+ ab->filename = cpystr(ADRBK_NAME);
+ }
+ else{
+ if(is_absolute_path(filename)){
+ ab->filename = cpystr(filename);
+ }
+ else{
+ if(homedir != NULL){
+ build_path(path, homedir, filename, sizeof(path));
+ ab->filename = cpystr(path);
+ }
+ else
+ ab->filename = cpystr(filename);
+ }
+ }
+ }
+
+ if(ab->filename && ab->filename[0]){
+ char buf[MAXPATH];
+
+ strncpy(buf, ab->filename, sizeof(buf)-4);
+ buf[sizeof(buf)-4] = '\0';
+
+ /*
+ * Our_filecopy is used in _WINDOWS to allow
+ * multiple pines to update the address book. The problem is
+ * that if a file is open it can't be deleted, so we need to keep
+ * the main filename closed most of the time.
+ * In Unix, our_filecopy just points to filename.
+ */
+
+#ifdef _WINDOWS
+ /*
+ * If we can't write in the same directory as filename is in, put
+ * the copies in /tmp instead.
+ */
+ if(!(ab->our_filecopy = tempfile_in_same_dir(ab->filename, "a3", NULL)))
+ ab->our_filecopy = temp_nam(NULL, "a3");
+#endif /* _WINDOWS */
+
+ /*
+ * We don't need the copies on Unix because we can rename/delete
+ * open files. Turn the feature off by making the copies point to
+ * the originals.
+ */
+ if(!ab->our_filecopy)
+ ab->our_filecopy = ab->filename;
+ }
+ else{
+ dprint((1, "adrbk_open: ab->filename is NULL???\n"));
+ goto bail_out;
+ }
+
+
+ /*
+ * We're going to make our own copy of the address book file so that
+ * we won't conflict with other instances of pine trying to change it.
+ * In particular, on Windows the address book file cannot be deleted
+ * or renamed into if it is open in another process.
+ */
+ ab->flags |= FILE_OUTOFDATE;
+ if(copy_abook_to_tempfile(ab, warning, warninglen) < 0){
+ dprint((1, "adrbk_open: copy_file failed\n"));
+ if(abook_validity_was_minusone){
+ /*
+ * The file copy failed when it shouldn't have. If the user has
+ * remote_abook_validity == -1 then we'll go back and try to
+ * do the validity check in case that can get us the file we
+ * need to copy. Without the validity check first time we won't
+ * contact the imap server.
+ */
+ dprint((1, "adrbk_open: trying to bootstrap\n"));
+
+ if(ab->our_filecopy){
+ if(ab->our_filecopy != ab->filename){
+ our_unlink(ab->our_filecopy);
+ fs_give((void **)&ab->our_filecopy);
+ }
+
+ ab->our_filecopy = NULL;
+ }
+
+ goto bootstrap_nocheck_policy;
+ }
+
+ goto bail_out;
+ }
+
+ if(!ab->fp)
+ goto bail_out;
+
+ ab->sort_rule = sort_rule;
+ if(pab->access == ReadOnly)
+ ab->sort_rule = AB_SORT_RULE_NONE;
+
+ if(ab){
+ /* allocate header for expanded lists list */
+ ab->exp = (EXPANDED_S *)fs_get(sizeof(EXPANDED_S));
+ /* first real element is NULL */
+ ab->exp->next = (EXPANDED_S *)NULL;
+
+ /* allocate header for checked entries list */
+ ab->checks = (EXPANDED_S *)fs_get(sizeof(EXPANDED_S));
+ /* first real element is NULL */
+ ab->checks->next = (EXPANDED_S *)NULL;
+
+ /* allocate header for selected entries list */
+ ab->selects = (EXPANDED_S *)fs_get(sizeof(EXPANDED_S));
+ /* first real element is NULL */
+ ab->selects->next = (EXPANDED_S *)NULL;
+
+ return(ab);
+ }
+
+bail_out:
+ dprint((2, "adrbk_open: bailing: filenames=%s %s %s fp=%s\n",
+ ab->orig_filename ? ab->orig_filename : "NULL",
+ ab->filename ? ab->filename : "NULL",
+ ab->our_filecopy ? ab->our_filecopy : "NULL",
+ ab->fp ? "open" : "NULL"));
+
+ if(ab->rd){
+ ab->rd->flags &= ~DO_REMTRIM;
+ rd_close_remdata(&ab->rd);
+ }
+
+ if(ab->fp)
+ (void)fclose(ab->fp);
+
+ if(ab->orig_filename)
+ fs_give((void **) &ab->orig_filename);
+
+ if(ab->our_filecopy && ab->our_filecopy != ab->filename){
+ our_unlink(ab->our_filecopy);
+ fs_give((void **) &ab->our_filecopy);
+ }
+
+ if(ab->filename)
+ fs_give((void **) &ab->filename);
+
+ if(pab->so){
+ so_give(&(pab->so));
+ pab->so = NULL;
+ }
+
+ fs_give((void **) &ab);
+
+ return NULL;
+}
+
+
+/*
+ * Copy the address book file to the temporary session copy. Also copy
+ * the hashfile. Any of these files which don't exist will be created.
+ *
+ * Returns 0 success
+ * -1 failure
+ */
+int
+copy_abook_to_tempfile(AdrBk *ab, char *warning, size_t warninglen)
+{
+ int got_it, fd, c,
+ ret = -1,
+ we_cancel = 0;
+ FILE *fp_read = (FILE *)NULL,
+ *fp_write = (FILE *)NULL;
+ char *lc;
+ time_t mtime;
+
+
+ dprint((3, "copy_file(%s) -\n",
+ (ab && ab->filename) ? ab->filename : ""));
+
+ if(!ab || !ab->filename || !ab->filename[0])
+ goto get_out;
+
+ if(!(ab->flags & FILE_OUTOFDATE))
+ return(0);
+
+ /* open filename for reading */
+ fp_read = our_fopen(ab->filename, "rb");
+ if(fp_read == NULL){
+
+ /*
+ * filename probably doesn't exist so we try to create it
+ */
+
+ /* don't want to create in these cases, should already be there */
+ if(ab->type == Imap){
+ if(warning){
+ if(ab->type == Imap){
+ snprintf(warning, warninglen,
+ /* TRANSLATORS: A temporary file for the address book can't
+ be opened. */
+ _("Temp addrbook file can't be opened: %s"),
+ ab->filename);
+ warning[warninglen-1] = '\0';
+ }
+ else{
+ strncpy(warning, _("Address book doesn't exist"), warninglen);
+ warning[warninglen-1] = '\0';
+ }
+ }
+
+ goto get_out;
+ }
+
+ q_status_message1(SM_INFO, 0, 3,
+ _("Address book %.200s doesn't exist, creating"),
+ (lc=last_cmpnt(ab->filename)) ? lc : ab->filename);
+ dprint((2, "Address book %s doesn't exist, creating\n",
+ ab->filename ? ab->filename : "?"));
+
+ /*
+ * Just use user's umask for permissions. Mode is "w" so the create
+ * will happen. We close it right after creating and open it in
+ * read mode again later.
+ */
+ fp_read = our_fopen(ab->filename, "wb"); /* create */
+ if(fp_read == NULL ||
+ fclose(fp_read) == EOF ||
+ (fp_read = our_fopen(ab->filename, "rb")) == NULL){
+ /*--- Create failed, bail out ---*/
+ if(warning){
+ strncpy(warning, error_description(errno), warninglen);
+ warning[warninglen-1] = '\0';
+ }
+
+ dprint((2, "create failed: %s\n",
+ error_description(errno)));
+
+ goto get_out;
+ }
+ }
+
+ /* record new change date of addrbook file */
+ ab->last_change_we_know_about = get_adj_name_file_mtime(ab->filename);
+
+ ab->last_local_valid_chk = get_adj_time();
+
+
+ /* now there is an ab->filename and we have it open for reading */
+
+ got_it = 0;
+
+ /* copy ab->filename to ab->our_filecopy, preserving mtime */
+ if(ab->filename != ab->our_filecopy){
+ struct stat sbuf;
+ struct utimbuf times;
+ int valid_stat = 0;
+
+ dprint((7, "Before abook copies\n"));
+ if((fd = our_open(ab->our_filecopy, OPEN_WRITE_MODE, 0600)) < 0)
+ goto get_out;
+
+ fp_write = fdopen(fd, "wb");
+ rewind(fp_read);
+ if(fstat(fileno(fp_read), &sbuf)){
+ q_status_message1(SM_INFO, 0, 3,
+ "Error: can't stat addrbook \"%.200s\"",
+ (lc=last_cmpnt(ab->filename)) ? lc : ab->filename);
+ dprint((2, "Error: can't stat addrbook \"%s\"\n",
+ ab->filename ? ab->filename : "?"));
+ }
+ else{
+ valid_stat++;
+ times.actime = sbuf.st_atime;
+ times.modtime = sbuf.st_mtime;
+ }
+
+ while((c = getc(fp_read)) != EOF)
+ if(putc(c, fp_write) == EOF)
+ goto get_out;
+
+ (void)fclose(fp_write);
+ fp_write = (FILE *)NULL;
+ if(valid_stat && our_utime(ab->our_filecopy, &times)){
+ q_status_message1(SM_INFO, 0, 3,
+ "Error: can't set mtime for \"%.200s\"",
+ (lc=last_cmpnt(ab->filename)) ? lc : ab->our_filecopy);
+ dprint((2, "Error: can't set mtime for \"%s\"\n",
+ ab->our_filecopy ? ab->our_filecopy : "?"));
+ }
+
+ (void)fclose(fp_read);
+ fp_read = (FILE *)NULL;
+ if(!(ab->fp = our_fopen(ab->our_filecopy, "rb")))
+ goto get_out;
+
+ dprint((7, "After abook file copy\n"));
+ }
+ else{ /* already open to the right file */
+ ab->fp = fp_read;
+ fp_read = (FILE *)NULL;
+ }
+
+ /*
+ * Now we've copied filename to our_filecopy.
+ * Operate on the copy now. Ab->fp is open readonly on
+ * our_filecopy.
+ */
+
+ got_it = 0;
+ mtime = get_adj_name_file_mtime(ab->our_filecopy);
+ we_cancel = busy_cue(NULL, NULL, 1);
+ if(build_abook_datastruct(ab, (warning && !*warning) ? warning : NULL, warninglen)){
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ dprint((2, "failed in build_abook_datastruct\n"));
+ goto get_out;
+ }
+
+ if(ab->arr
+ && build_abook_tries(ab, (warning && !*warning) ? warning : NULL)){
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ dprint((2, "failed in build_abook_tries\n"));
+ goto get_out;
+ }
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ ab->flags &= ~FILE_OUTOFDATE; /* turn off out of date flag */
+ ret = 0;
+
+get_out:
+ if(fp_read)
+ (void)fclose(fp_read);
+
+ if(fp_write)
+ (void)fclose(fp_write);
+
+ if(ret < 0 && ab){
+ if(ab->our_filecopy && ab->our_filecopy != ab->filename)
+ our_unlink(ab->our_filecopy);
+ }
+
+ return(ret);
+}
+
+
+/*
+ * Returns an allocated copy of the directory which contains filename.
+ */
+char *
+dir_containing(char *filename)
+{
+ char dir[MAXPATH+1];
+ char *dirp = NULL;
+
+ if(filename){
+ char *lc;
+
+ if((lc = last_cmpnt(filename)) != NULL){
+ int to_copy;
+
+ to_copy = (lc - filename > 1) ? (lc - filename - 1) : 1;
+ strncpy(dir, filename, MIN(to_copy, sizeof(dir)-1));
+ dir[MIN(to_copy, sizeof(dir)-1)] = '\0';
+ }
+ else{
+ dir[0] = '.';
+ dir[1] = '\0';
+ }
+
+ dirp = cpystr(dir);
+ }
+
+ return(dirp);
+}
+
+
+/*
+ * Checks whether or not the addrbook is sorted correctly according to
+ * the SortType. Returns 1 if is sorted correctly, 0 otherwise.
+ */
+int
+adrbk_is_in_sort_order(AdrBk *ab, int be_quiet)
+{
+ adrbk_cntr_t entry;
+ AdrBk_Entry *ae, *ae_prev;
+ int (*cmp_func)();
+ int we_cancel = 0;
+
+ dprint((9, "- adrbk_is_in_sort_order -\n"));
+
+ if(!ab)
+ return 0;
+
+ if(ab->sort_rule == AB_SORT_RULE_NONE || ab->count < 2)
+ return 1;
+
+ cmp_func = (ab->sort_rule == AB_SORT_RULE_FULL_LISTS) ?
+ cmp_ae_by_full_lists_last :
+ (ab->sort_rule == AB_SORT_RULE_FULL) ?
+ cmp_ae_by_full :
+ (ab->sort_rule == AB_SORT_RULE_NICK_LISTS) ?
+ cmp_ae_by_nick_lists_last :
+ /* (ab->sort_rule == AB_SORT_RULE_NICK) */
+ cmp_ae_by_nick;
+
+ ae_prev = adrbk_get_ae(ab, (a_c_arg_t) 0);
+
+ if(!be_quiet)
+ we_cancel = busy_cue(NULL, NULL, 1);
+
+ for(entry = 1, ae = adrbk_get_ae(ab, (a_c_arg_t) entry);
+ ae != (AdrBk_Entry *)NULL;
+ ae = adrbk_get_ae(ab, (a_c_arg_t) (++entry))){
+
+ if((*cmp_func)((qsort_t *)&ae_prev, (qsort_t *)&ae) > 0){
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ dprint((9, "- adrbk_is_in_sort_order : no (entry %ld) -\n", (long) entry));
+ return 0;
+ }
+
+ ae_prev = ae;
+ }
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ dprint((9, "- adrbk_is_in_sort_order : yes -\n"));
+
+ return 1;
+}
+
+
+/*
+ * Look through the ondisk address book and count the number of entries.
+ *
+ * Args ab -- address book pointer
+ * deleted -- pointer to location to return number of deleted entries
+ * warning -- place to put warning
+ *
+ * Returns number of non-deleted entries.
+ */
+adrbk_cntr_t
+count_abook_entries_on_disk(AdrBk *ab, a_c_arg_t *deleted)
+{
+ FILE *fp_in;
+ char *nickname;
+ adrbk_cntr_t count = 0;
+ adrbk_cntr_t deleted_count = 0;
+ int rew = 1;
+ char nickbuf[50];
+
+ if(!ab || !ab->fp)
+ return -1;
+
+ fp_in = ab->fp;
+
+ while((nickname = get_next_abook_entry(fp_in, rew)) != NULL){
+ rew = 0;
+ strncpy(nickbuf, nickname, sizeof(nickbuf));
+ nickbuf[sizeof(nickbuf)-1] = '\0';
+ fs_give((void **) &nickname);
+ if(strncmp(nickbuf, DELETED, DELETED_LEN) == 0
+ && isdigit((unsigned char)nickbuf[DELETED_LEN])
+ && isdigit((unsigned char)nickbuf[DELETED_LEN+1])
+ && nickbuf[DELETED_LEN+2] == '/'
+ && isdigit((unsigned char)nickbuf[DELETED_LEN+3])
+ && isdigit((unsigned char)nickbuf[DELETED_LEN+4])
+ && nickbuf[DELETED_LEN+5] == '/'
+ && isdigit((unsigned char)nickbuf[DELETED_LEN+6])
+ && isdigit((unsigned char)nickbuf[DELETED_LEN+7])
+ && nickbuf[DELETED_LEN+8] == '#'){
+ deleted_count++;
+ continue;
+ }
+
+ count++;
+ }
+
+ if(deleted)
+ *deleted = (a_c_arg_t) deleted_count;
+
+ return(count);
+}
+
+
+/*
+ * Builds the data structures used with the address book which is
+ * simply an array of entries.
+ */
+int
+build_abook_datastruct(AdrBk *ab, char *warning, size_t warninglen)
+{
+ FILE *fp_in;
+ char *nickname;
+ char *lc;
+ a_c_arg_t count, deleted;
+ adrbk_cntr_t used = 0, delused = 0;
+ int max_nick = 0,
+ max_full = 0, full_two = 0, full_three = 0,
+ max_fcc = 0, fcc_two = 0, fcc_three = 0,
+ max_addr = 0, addr_two = 0, addr_three = 0,
+ this_nick_width, this_full_width, this_addr_width,
+ this_fcc_width;
+ WIDTH_INFO_S *widths;
+ int rew = 1, is_deleted;
+ AdrBk_Entry *ae;
+
+ dprint((9, "- build_abook_datastruct -\n"));
+
+ if(!ab || !ab->fp)
+ return -1;
+
+ errno = 0;
+
+ fp_in = ab->fp;
+
+ /*
+ * If we used a list instead of an array to store the entries we
+ * could avoid this pass through the file to count the entries, which
+ * we use to allocate the array.
+ *
+ * Since we use entry_nums a lot to access address book entries it is
+ * convenient to have an array for quick access, so we'll probably
+ * leave this for now.
+ */
+ count = count_abook_entries_on_disk(ab, &deleted);
+
+ if(count < 0)
+ return -1;
+
+ ab->count = (adrbk_cntr_t) count;
+ ab->del_count = (adrbk_cntr_t) deleted;
+
+ if(count > 0){
+ ab->arr = (AdrBk_Entry *) fs_get(count * sizeof(AdrBk_Entry));
+ memset(ab->arr, 0, count * sizeof(AdrBk_Entry));
+ }
+
+ if(deleted > 0){
+ ab->del = (AdrBk_Entry *) fs_get(deleted * sizeof(AdrBk_Entry));
+ memset(ab->del, 0, deleted * sizeof(AdrBk_Entry));
+ }
+
+ while((nickname = get_next_abook_entry(fp_in, rew)) != NULL){
+
+
+ ae = NULL;
+ rew = 0;
+ is_deleted = 0;
+
+ if(strncmp(nickname, DELETED, DELETED_LEN) == 0
+ && isdigit((unsigned char)nickname[DELETED_LEN])
+ && isdigit((unsigned char)nickname[DELETED_LEN+1])
+ && nickname[DELETED_LEN+2] == '/'
+ && isdigit((unsigned char)nickname[DELETED_LEN+3])
+ && isdigit((unsigned char)nickname[DELETED_LEN+4])
+ && nickname[DELETED_LEN+5] == '/'
+ && isdigit((unsigned char)nickname[DELETED_LEN+6])
+ && isdigit((unsigned char)nickname[DELETED_LEN+7])
+ && nickname[DELETED_LEN+8] == '#'){
+ is_deleted++;
+ }
+
+ ALARM_BLIP();
+ if(!is_deleted && (long) used > MAX_ADRBK_SIZE){
+ q_status_message2(SM_ORDER | SM_DING, 4, 5,
+ "Max addrbook size is %.200s, %.200s too large, giving up",
+ long2string(MAX_ADRBK_SIZE),
+ (lc=last_cmpnt(ab->filename)) ? lc : ab->filename);
+ dprint((1, "build_ondisk: used=%ld > %s\n",
+ (long) used, long2string(MAX_ADRBK_SIZE)));
+ goto io_err;
+ }
+
+ if(is_deleted){
+ if(delused < ab->del_count)
+ ae = &ab->del[delused++];
+ }
+ else{
+ if(used < ab->count)
+ ae = &ab->arr[used++];
+ }
+
+ if(ae)
+ init_ae(ab, ae, nickname);
+
+ fs_give((void **) &nickname);
+
+ if(!ae || is_deleted)
+ continue;
+
+ /*
+ * We're calculating the widths as we read in the data.
+ * We could just go with some default widths to save time.
+ */
+ this_nick_width = 0;
+ this_full_width = 0;
+ this_addr_width = 0;
+ this_fcc_width = 0;
+
+ if(ae->nickname)
+ this_nick_width = (int) utf8_width(ae->nickname);
+
+ if(ae->fullname)
+ this_full_width = (int) utf8_width(ae->fullname);
+
+ if(ae->tag == Single){
+ if(ae->addr.addr)
+ this_addr_width = (int) utf8_width(ae->addr.addr);
+ }
+ else{
+ char **a2;
+
+ this_addr_width = 0;
+ for(a2 = ae->addr.list; *a2 != NULL; a2++)
+ this_addr_width = MAX(this_addr_width, (int) utf8_width(*a2));
+ }
+
+ if(ae->fcc)
+ this_fcc_width = (int) utf8_width(ae->fcc);
+
+ max_nick = MAX(max_nick, this_nick_width);
+
+ if(this_full_width > max_full){
+ full_three = full_two;
+ full_two = max_full;
+ max_full = this_full_width;
+ }
+ else if(this_full_width > full_two){
+ full_three = full_two;
+ full_two = this_full_width;
+ }
+ else if(this_full_width > full_three){
+ full_three = this_full_width;
+ }
+
+ if(this_addr_width > max_addr){
+ addr_three = addr_two;
+ addr_two = max_addr;
+ max_addr = this_addr_width;
+ }
+ else if(this_addr_width > addr_two){
+ addr_three = addr_two;
+ addr_two = this_addr_width;
+ }
+ else if(this_addr_width > addr_three){
+ addr_three = this_addr_width;
+ }
+
+ if(this_fcc_width > max_fcc){
+ fcc_three = fcc_two;
+ fcc_two = max_fcc;
+ max_fcc = this_fcc_width;
+ }
+ else if(this_fcc_width > fcc_two){
+ fcc_three = fcc_two;
+ fcc_two = this_fcc_width;
+ }
+ else if(this_fcc_width > fcc_three){
+ fcc_three = this_fcc_width;
+ }
+ }
+
+ widths = &ab->widths;
+ widths->max_nickname_width = MIN(max_nick, 99);
+ widths->max_fullname_width = MIN(max_full, 99);
+ widths->max_addrfield_width = MIN(max_addr, 99);
+ widths->max_fccfield_width = MIN(max_fcc, 99);
+ widths->third_biggest_fullname_width = MIN(full_three, 99);
+ widths->third_biggest_addrfield_width = MIN(addr_three, 99);
+ widths->third_biggest_fccfield_width = MIN(fcc_three, 99);
+
+ dprint((9, "- build_abook_datastruct done -\n"));
+ return 0;
+
+io_err:
+ if(warning && errno != 0){
+ strncpy(warning, error_description(errno), warninglen);
+ warning[warninglen-1] = '\0';
+ }
+
+ dprint((1, "build_ondisk: io_err: %s\n",
+ error_description(errno)));
+
+ return -1;
+}
+
+
+/*
+ * Builds the trees used for nickname and address lookups.
+ */
+int
+build_abook_tries(AdrBk *ab, char *warning)
+{
+ adrbk_cntr_t entry_num;
+ AdrBk_Entry *ae;
+ int we_cancel = 0;
+
+ if(!ab)
+ return -1;
+
+ dprint((9, "- build_abook_tries(%s) -\n", ab->filename));
+
+
+ if(ab->nick_trie)
+ free_abook_trie(&ab->nick_trie);
+
+ if(ab->addr_trie)
+ free_abook_trie(&ab->addr_trie);
+
+ if(ab->full_trie)
+ free_abook_trie(&ab->full_trie);
+
+ if(ab->revfull_trie)
+ free_abook_trie(&ab->revfull_trie);
+
+ /*
+ * Go through addrbook entries and add each to the tries it
+ * belongs in.
+ */
+ for(entry_num = 0; entry_num < ab->count; entry_num++){
+ ae = adrbk_get_ae(ab, (a_c_arg_t) entry_num);
+ if(ae){
+ /* every nickname in the nick trie */
+ if(ae->nickname && ae->nickname[0])
+ add_entry_to_trie(&ab->nick_trie, ae->nickname, (a_c_arg_t) entry_num);
+
+ if(ae->fullname && ae->fullname[0]){
+ char *reverse = NULL;
+ char *forward = NULL;
+ char *comma = NULL;
+
+ /*
+ * We have some fullnames stored as Last, First. Put both in.
+ */
+ if(ae->fullname[0] != '"'
+ && (comma=strindex(ae->fullname, ',')) != NULL
+ && comma - ae->fullname > 0){
+ forward = adrbk_formatname(ae->fullname, NULL, NULL);
+ if(forward && forward[0]){
+ *comma = '\0';
+ reverse = cpystr(ae->fullname);
+ *comma = ',';
+ }
+ else{
+ if(forward)
+ fs_give((void **) &forward);
+
+ forward = ae->fullname;
+ }
+ }
+ else
+ forward = ae->fullname;
+
+ if(forward){
+ char *addthis;
+
+ /*
+ * Make this add not only the full name as is (forward) but
+ * also add the middle name and last name (names after spaces).
+ * so that they'll be found.
+ */
+ for(addthis=forward;
+ addthis && (*addthis);
+ addthis = strindex(addthis, ' ')){
+ while(*addthis == ' ')
+ addthis++;
+
+ if(*addthis)
+ add_entry_to_trie(&ab->full_trie, addthis, (a_c_arg_t) entry_num);
+ }
+ }
+
+ if(reverse)
+ add_entry_to_trie(&ab->revfull_trie, reverse, (a_c_arg_t) entry_num);
+
+ if(forward && forward != ae->fullname)
+ fs_give((void **) &forward);
+ }
+
+ if(ae->tag == Single && ae->addr.addr && ae->addr.addr[0]){
+ char buf[1000];
+ char *tmp_a_string, *simple_addr = NULL;
+ ADDRESS *addr = NULL;
+ char *fakedomain = "@";
+
+ /*
+ * Isolate the actual address out of ae.
+ */
+ tmp_a_string = cpystr(ae->addr.addr);
+ rfc822_parse_adrlist(&addr, tmp_a_string, fakedomain);
+ if(tmp_a_string)
+ fs_give((void **) &tmp_a_string);
+
+ if(addr){
+ if(addr->mailbox && addr->host
+ && !(addr->host[0] == '@' && addr->host[1] == '\0'))
+ simple_addr = simple_addr_string(addr, buf, sizeof(buf));
+
+ /*
+ * If the fullname wasn't set in the addrbook entry there
+ * may still be one that is part of the address. We need
+ * to be careful because we may be in the middle of opening
+ * the address book right now. Don't call something like
+ * our_build_address because it will probably re-open this
+ * same addrbook infinitely.
+ */
+ if(!(ae->fullname && ae->fullname[0])
+ && addr->personal && addr->personal[0])
+ add_entry_to_trie(&ab->full_trie, addr->personal,
+ (a_c_arg_t) entry_num);
+
+ mail_free_address(&addr);
+ }
+
+ if(simple_addr)
+ add_entry_to_trie(&ab->addr_trie, simple_addr, (a_c_arg_t) entry_num);
+ }
+ }
+ }
+
+ dprint((9, "- build_abook_tries done -\n"));
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ return 0;
+}
+
+
+void
+add_entry_to_trie(AdrBk_Trie **head, char *str, a_c_arg_t entry_num)
+{
+ AdrBk_Trie *temp, *trail;
+ char *addthis, *p, buf[1000];
+
+ if(!head || !str || !*str)
+ return;
+
+ /* add as lower case */
+
+ for(p = str; *p && ((*p & 0x80) || !isupper((unsigned char) *p)); p++)
+ ;
+
+ if(*p){
+ strncpy(buf, str, sizeof(buf));
+ buf[sizeof(buf)-1] = '\0';
+ for(p = buf; *p; p++)
+ if(!(*p & 0x80) && isupper((unsigned char) *p))
+ *p = tolower(*p);
+
+ addthis = buf;
+ }
+ else
+ addthis = str;
+
+ temp = trail = (*head);
+
+ /*
+ * Find way down the trie, adding missing nodes as we go.
+ */
+ for(p = addthis; *p;){
+ if(temp == NULL){
+ temp = (AdrBk_Trie *) fs_get(sizeof(*temp));
+ memset(temp, 0, sizeof(*temp));
+ temp->value = *p;
+ temp->entrynum = NO_NEXT;
+
+ if(*head == NULL)
+ *head = temp;
+ else
+ trail->down = temp;
+ }
+ else{
+ while((temp != NULL) && (temp->value != *p)){
+ trail = temp;
+ temp = temp->right;
+ }
+
+ /* wasn't there, add new node */
+ if(temp == NULL){
+ temp = (AdrBk_Trie *) fs_get(sizeof(*temp));
+ memset(temp, 0, sizeof(*temp));
+ temp->value = *p;
+ temp->entrynum = NO_NEXT;
+ trail->right = temp;
+ }
+ }
+
+ if(*(++p)){
+ trail = temp;
+ temp = temp->down;
+ }
+ }
+
+ /*
+ * If entrynum is already filled in there must be an entry with
+ * the same nickname earlier in the abook. Use that earlier entry.
+ */
+ if(temp != NULL && temp->entrynum == NO_NEXT)
+ temp->entrynum = (adrbk_cntr_t) entry_num;
+}
+
+
+/*
+ * Returns entry_num of first entry with this nickname, else NO_NEXT.
+ */
+adrbk_cntr_t
+lookup_nickname_in_trie(AdrBk *ab, char *nickname)
+{
+ if(!ab || !nickname || !ab->nick_trie)
+ return(-1L);
+
+ return(lookup_in_abook_trie(ab->nick_trie, nickname));
+}
+
+
+/*
+ * Returns entry_num of first entry with this address, else NO_NEXT.
+ */
+adrbk_cntr_t
+lookup_address_in_trie(AdrBk *ab, char *address)
+{
+ dprint((9, "lookup_address_in_trie: %s\n", ab ? (ab->addr_trie ? (address ? address : "?") : "null addr_trie") : "null ab"));
+ if(!ab || !address || !ab->addr_trie)
+ return(-1L);
+
+ return(lookup_in_abook_trie(ab->addr_trie, address));
+}
+
+
+adrbk_cntr_t
+lookup_in_abook_trie(AdrBk_Trie *t, char *str)
+{
+ char *p, *lookthisup;
+ char buf[1000];
+ adrbk_cntr_t ret = NO_NEXT;
+
+ if(!t || !str)
+ return(ret);
+
+ /* make lookup case independent */
+
+ for(p = str; *p && !(*p & 0x80) && islower((unsigned char) *p); p++)
+ ;
+
+ if(*p){
+ strncpy(buf, str, sizeof(buf));
+ buf[sizeof(buf)-1] = '\0';
+ for(p = buf; *p; p++)
+ if(!(*p & 0x80) && isupper((unsigned char) *p))
+ *p = tolower(*p);
+
+ lookthisup = buf;
+ }
+ else
+ lookthisup = str;
+
+ p = lookthisup;
+
+ /*
+ * We usually return out from inside the loop (unless str == "").
+ */
+ while(*p){
+ /* search for character at this level */
+ while(t->value != *p){
+ if(t->right == NULL)
+ return(ret); /* no match */
+
+ t = t->right;
+ }
+
+ if(*++p == '\0') /* end of str, a match */
+ return(t->entrynum);
+
+ /* need to go down to match next character */
+ if(t->down == NULL) /* no match */
+ return(ret);
+
+ t = t->down;
+ }
+
+ return(ret);
+}
+
+
+void
+free_abook_trie(AdrBk_Trie **trie)
+{
+ if(trie){
+ if(*trie){
+ free_abook_trie(&(*trie)->down);
+ free_abook_trie(&(*trie)->right);
+ fs_give((void **) trie);
+ }
+ }
+}
+
+
+/*
+ * Returns pointer to start of next address book entry from disk file.
+ * The return will be in raw form from the file with newlines still
+ * embedded. Or NULL at end of file.
+ *
+ * If rew is set, rewind the file and start over at beginning.
+ */
+char *
+get_next_abook_entry(FILE *fp, int rew)
+{
+ char *returned_lines = NULL, *p;
+ char line[1024];
+ static int will_be_done_next_time = 0;
+ static long next_nickname_offset = 0L;
+ size_t lsize;
+ long offset, saved_offset;
+ long len = 0L;
+
+#define CHUNKSIZE 500
+
+ lsize = sizeof(line);
+
+ if(rew){
+ will_be_done_next_time = 0;
+ rewind(fp);
+ /* skip leading (bogus) continuation lines */
+ do{
+ offset = ftell(fp);
+ line[0] = '\0';
+ line[lsize-2] = '\0';
+ p = fgets(line, lsize, fp);
+
+ if(p == NULL)
+ return(NULL);
+
+ /* line is too long to fit, read the rest and discard */
+ while(line[lsize-2] != '\0' && line[lsize-2] != '\n'
+ && p != NULL){
+
+ /* get next lsize-1 characters, leaving line[0] */
+ line[lsize-2] = '\0';
+ p = fgets(line+1, lsize-1, fp);
+ }
+ }while(line[0] == SPACE);
+
+ /* offset is start of first good line now */
+ next_nickname_offset = offset;
+ }
+
+ if(will_be_done_next_time)
+ return(NULL);
+
+ /* we set this up in rew==1 case or on previous call to this routine */
+ offset = next_nickname_offset;
+
+ /*
+ * The rest is working on finding the start of the next entry so
+ * skip continuation lines
+ */
+ do{
+ next_nickname_offset = ftell(fp);
+ line[0] = '\0';
+ line[lsize-2] = '\0';
+ p = fgets(line, lsize, fp);
+
+ /* line is too long to fit, read the rest and discard */
+ while(line[lsize-2] != '\0' && line[lsize-2] != '\n'
+ && p != NULL){
+
+ /* get next lsize-1 characters, leaving line[0] */
+ line[lsize-2] = '\0';
+ p = fgets(line+1, lsize-1, fp);
+ }
+ }while(line[0] == SPACE);
+
+ /* next_nickname_offset is start of next entry now */
+
+ if(!line[0])
+ will_be_done_next_time = 1;
+
+ len = next_nickname_offset - offset;
+
+ returned_lines = (char *) fs_get((len + 1) * sizeof(char));
+
+ saved_offset = ftell(fp);
+ if(fseek(fp, offset, 0)){
+ dprint((2, "get_next_ab_entry: trouble fseeking\n"));
+ len = 0;
+ fs_give((void **) &returned_lines);
+ }
+ else{
+ if(fread(returned_lines, sizeof(char), (unsigned) len, fp) != len){
+ dprint((2, "get_next_ab_entry: trouble freading\n"));
+ len = 0;
+ fs_give((void **) &returned_lines);
+ }
+ }
+
+ if(fseek(fp, saved_offset, 0)){
+ dprint((2, "get_next_ab_entry: trouble fseeking to saved_offset\n"));
+ len = 0;
+ fs_give((void **) &returned_lines);
+ }
+
+ if(returned_lines)
+ returned_lines[len] = '\0';
+
+ return(returned_lines);
+}
+
+
+/*
+ * Returns a pointer to the start of the mailbox@host part of this
+ * address string, and a pointer to the end + 1. The caller can then
+ * replace the end char with \0 and call the hash function, then put
+ * back the end char. Start_addr and end_addr are assumed to be non-null.
+ */
+void
+strip_addr_string(char *addrstr, char **start_addr, char **end_addr)
+{
+ register char *q;
+ int in_quotes = 0,
+ in_comment = 0;
+ char prev_char = '\0';
+
+ if(!addrstr || !*addrstr){
+ *start_addr = NULL;
+ *end_addr = NULL;
+ return;
+ }
+
+ *start_addr = addrstr;
+
+ for(q = addrstr; *q; q++){
+ switch(*q){
+ case '<':
+ if(!in_quotes && !in_comment){
+ if(*++q){
+ *start_addr = q;
+ /* skip to > */
+ while(*q && *q != '>')
+ q++;
+
+ /* found > */
+ if(*q){
+ *end_addr = q;
+ return;
+ }
+ else
+ q--;
+ }
+ }
+
+ break;
+
+ case LPAREN:
+ if(!in_quotes && !in_comment)
+ in_comment = 1;
+ break;
+
+ case RPAREN:
+ if(in_comment && prev_char != BSLASH)
+ in_comment = 0;
+ break;
+
+ case QUOTE:
+ if(in_quotes && prev_char != BSLASH)
+ in_quotes = 0;
+ else if(!in_quotes && !in_comment)
+ in_quotes = 1;
+ break;
+
+ default:
+ break;
+ }
+
+ prev_char = *q;
+ }
+
+ *end_addr = q;
+}
+
+
+/*
+ * Fill in the passed in ae pointer by parsing the str that is passed.
+ *
+ * Args ab --
+ * ae -- pointer we want to fill in. The individual members of
+ * the ae struct will be allocated here
+ * str -- the string from the on-disk address book file for this entry
+ *
+ * Returns a pointer to ae or NULL if there are problems.
+ */
+AdrBk_Entry *
+init_ae(AdrBk *ab, AdrBk_Entry *a, char *str)
+{
+ char *p;
+ char *addrfield = (char *) NULL;
+ char *addrfield_end;
+ char *nickname, *fullname, *fcc, *extra;
+
+ if(!ab){
+ dprint((2, "init_ae: found trouble: NULL ab\n"));
+ return((AdrBk_Entry *) NULL);
+ }
+
+ defvalue_ae(a);
+
+ p = str;
+
+ REPLACE_NEWLINES_WITH_SPACE(p);
+
+ nickname = p;
+ SKIP_TO_TAB(p);
+ if(!*p){
+ RM_END_SPACE(nickname, p);
+ a->nickname = cpystr(nickname);
+ }
+ else{
+ *p = '\0';
+ RM_END_SPACE(nickname, p);
+ a->nickname = cpystr(nickname);
+ p++;
+ SKIP_SPACE(p);
+ fullname = p;
+ SKIP_TO_TAB(p);
+ if(!*p){
+ RM_END_SPACE(fullname, p);
+ a->fullname = cpystr(fullname);
+ }
+ else{
+ *p = '\0';
+ RM_END_SPACE(fullname, p);
+ a->fullname = cpystr(fullname);
+ p++;
+ SKIP_SPACE(p);
+ addrfield = p;
+ SKIP_TO_TAB(p);
+ if(!*p){
+ RM_END_SPACE(addrfield, p);
+ }
+ else{
+ *p = '\0';
+ RM_END_SPACE(addrfield, p);
+ p++;
+ SKIP_SPACE(p);
+ fcc = p;
+ SKIP_TO_TAB(p);
+ if(!*p){
+ RM_END_SPACE(fcc, p);
+ a->fcc = cpystr(fcc);
+ }
+ else{
+ char *src, *dst;
+
+ *p = '\0';
+ RM_END_SPACE(fcc, p);
+ a->fcc = cpystr(fcc);
+ p++;
+ SKIP_SPACE(p);
+ extra = p;
+ p = extra + strlen(extra);
+ RM_END_SPACE(extra, p);
+
+ /*
+ * When we wrap long comments we insert an extra colon
+ * in the wrap so we can spot it and take it back out.
+ * Pretty much a hack since we thought of it a long
+ * time after designing it, but it eliminates the limit
+ * on length of comments. Here we are looking for
+ * <SP> <SP> : <SP> or
+ * <SP> <SP> <SP> : <SP> and replacing it with <SP>.
+ * There could have been a single \n or \r\n, so that
+ * is why we check for 2 or 3 spaces before the colon.
+ * (This was another mistake.)
+ */
+ dst = src = extra;
+ while(*src != '\0'){
+ if(*src == SPACE && *(src+1) == SPACE &&
+ *(src+2) == ':' && *(src+3) == SPACE){
+
+ /*
+ * If there was an extra space because of the
+ * CRLF (instead of LF) then we already put
+ * a SP in dst last time through the loop
+ * and don't need to add another.
+ */
+ if(src == extra || *(src-1) != SPACE)
+ *dst++ = *src;
+
+ src += 4;
+ }
+ else
+ *dst++ = *src++;
+ }
+
+ *dst = '\0';
+ a->extra = cpystr(extra);
+ }
+ }
+ }
+ }
+
+ /* decode and convert to UTF-8 if we need to */
+
+ convert_possibly_encoded_str_to_utf8(&a->nickname);
+ convert_possibly_encoded_str_to_utf8(&a->fullname);
+ convert_possibly_encoded_str_to_utf8(&a->fcc);
+ convert_possibly_encoded_str_to_utf8(&a->extra);
+
+ /* parse addrfield */
+ if(addrfield){
+ if(*addrfield == '('){ /* it's a list */
+ a->tag = List;
+ p = addrfield;
+ addrfield_end = p + strlen(p);
+
+ /*
+ * Get rid of the parens.
+ * If this isn't true the input file is messed up.
+ */
+ if(p[strlen(p)-1] == ')'){
+ char **ll;
+
+ p[strlen(p)-1] = '\0';
+ p++;
+ a->addr.list = parse_addrlist(p);
+ for(ll = a->addr.list; ll && *ll; ll++)
+ convert_possibly_encoded_str_to_utf8(ll);
+ }
+ else{
+ /* put back what was there to start with */
+ *addrfield_end = ')';
+ a->addr.list = (char **)fs_get(sizeof(char *) * 2);
+ a->addr.list[0] = cpystr(addrfield);
+ a->addr.list[1] = NULL;
+ dprint((2, "parsing error reading addressbook: missing right paren: %s\n",
+ addrfield ? addrfield : "?"));
+ }
+ }
+ else{ /* A plain, single address */
+
+ a->tag = Single;
+ a->addr.addr = cpystr(addrfield);
+ convert_possibly_encoded_str_to_utf8(&a->addr.addr);
+ }
+ }
+ else{
+ /*
+ * If no addrfield, assume an empty Single.
+ */
+ a->addr.addr = cpystr("");
+ a->tag = Single;
+ }
+
+ return(a);
+}
+
+
+/*
+ * Return the size of the address book
+ */
+adrbk_cntr_t
+adrbk_count(AdrBk *ab)
+{
+ return(ab ? ab->count : (adrbk_cntr_t) 0);
+}
+
+
+/*
+ * Return a pointer to the ae that has index number "entry_num".
+ */
+AdrBk_Entry *
+adrbk_get_ae(AdrBk *ab, a_c_arg_t entry_num)
+{
+ if(!ab || entry_num >= (a_c_arg_t) ab->count)
+ return((AdrBk_Entry *) NULL);
+
+ return(&ab->arr[entry_num]);
+}
+
+
+/*
+ * Return a pointer to the deleted ae that has index number "entry_num".
+ */
+AdrBk_Entry *
+adrbk_get_delae(AdrBk *ab, a_c_arg_t entry_num)
+{
+ if(!ab || entry_num >= (a_c_arg_t) ab->del_count)
+ return((AdrBk_Entry *) NULL);
+
+ return(&ab->del[entry_num]);
+}
+
+
+/*
+ * Look up an entry in the address book given a nickname
+ *
+ * Args: ab -- the address book
+ * nickname -- nickname to match
+ * entry_num -- if matched, return entry_num of match here
+ *
+ * Result: A pointer to an AdrBk_Entry is returned, or NULL if not found.
+ *
+ * Lookups usually need to be recursive in case the address
+ * book references itself. This is left to the next level up.
+ * adrbk_clearrefs() is provided to clear all the reference tags in
+ * the address book for loop detection.
+ * When there are duplicates of the same nickname we return the first.
+ * This can only happen if addrbook was edited externally.
+ */
+AdrBk_Entry *
+adrbk_lookup_by_nick(AdrBk *ab, char *nickname, adrbk_cntr_t *entry_num)
+{
+ adrbk_cntr_t num;
+ AdrBk_Entry *ae;
+
+ dprint((5, "- adrbk_lookup_by_nick(%s) (in %s) -\n",
+ nickname ? nickname : "?",
+ (ab && ab->filename) ? ab->filename : "?"));
+
+ if(!ab || !nickname || !nickname[0])
+ return NULL;
+
+
+ num = lookup_nickname_in_trie(ab, nickname);
+
+ if(num != NO_NEXT){
+ ae = adrbk_get_ae(ab, (a_c_arg_t) num);
+ if(entry_num && ae)
+ *entry_num = num;
+
+ return(ae);
+ }
+ else
+ return((AdrBk_Entry *) NULL);
+}
+
+
+/*
+ * Look up an entry in the address book given an address
+ *
+ * Args: ab -- the address book
+ * address -- address to match
+ * entry_num -- if matched, return entry_num of match here
+ *
+ * Result: A pointer to an AdrBk_Entry is returned, or NULL if not found.
+ *
+ * Note: When there are multiple occurrences of an address in an addressbook,
+ * which there will be if more than one nickname points to same address, then
+ * we want this to match the first occurrence so that the fcc you get will
+ * be predictable.
+ */
+AdrBk_Entry *
+adrbk_lookup_by_addr(AdrBk *ab, char *address, adrbk_cntr_t *entry_num)
+{
+ adrbk_cntr_t num;
+ AdrBk_Entry *ae;
+
+ dprint((5, "- adrbk_lookup_by_addr(%s) (in %s) -\n",
+ address ? address : "?",
+ (ab && ab->filename) ? ab->filename : "?"));
+
+ if(!ab || !address || !address[0])
+ return((AdrBk_Entry *)NULL);
+
+ num = lookup_address_in_trie(ab, address);
+
+ if(num != NO_NEXT){
+ ae = adrbk_get_ae(ab, (a_c_arg_t) num);
+ if(entry_num && ae)
+ *entry_num = num;
+
+ return(ae);
+ }
+ else
+ return((AdrBk_Entry *)NULL);
+}
+
+
+/*
+ * Format a full name.
+ *
+ * Args: fullname -- full name out of address book for formatting
+ * first -- Return a pointer to first name here.
+ * last -- Return a pointer to last name here.
+ *
+ * Result: Returns pointer to name formatted for a mail header. Space is
+ * allocated here and should be freed by caller.
+ *
+ * We need this because we store full names as Last, First.
+ * If the name has no comma, then no change is made.
+ * Otherwise the text before the first comma is moved to the end and
+ * the comma is deleted.
+ *
+ * Last and first have to be freed by caller.
+ */
+char *
+adrbk_formatname(char *fullname, char **first, char **last)
+{
+ char *comma;
+ char *new_name;
+
+ if(first)
+ *first = NULL;
+ if(last)
+ *last = NULL;
+
+ /*
+ * There is an assumption that the fullname is a UTF-8 string.
+ */
+
+ if(fullname[0] != '"' && (comma = strindex(fullname, ',')) != NULL){
+ size_t l;
+ int last_name_len = comma - fullname;
+
+ comma++;
+ while(*comma && isspace((unsigned char)*comma))
+ comma++;
+
+ if(first)
+ *first = cpystr(comma);
+
+ if(last){
+ *last = (char *)fs_get((last_name_len + 1) * sizeof(char));
+ strncpy(*last, fullname, last_name_len);
+ (*last)[last_name_len] = '\0';
+ }
+
+ l = strlen(comma) + 1 + last_name_len;
+ new_name = (char *) fs_get((l+1) * sizeof(char));
+ strncpy(new_name, comma, l);
+ new_name[l] = '\0';
+ strncat(new_name, " ", l+1-1-strlen(new_name));
+ new_name[l] = '\0';
+ strncat(new_name, fullname, MIN(last_name_len,l+1-1-strlen(new_name)));
+ new_name[l] = '\0';
+ }
+ else
+ new_name = cpystr(fullname);
+
+ return(new_name);
+}
+
+
+/*
+ * Clear reference flags in preparation for a recursive lookup.
+ *
+ * For loop detection during address book look up. This clears all the
+ * referenced flags, then as the lookup proceeds the referenced flags can
+ * be checked and set.
+ */
+void
+adrbk_clearrefs(AdrBk *ab)
+{
+ adrbk_cntr_t entry_num;
+ AdrBk_Entry *ae;
+
+ dprint((9, "- adrbk_clearrefs -\n"));
+
+ if(!ab)
+ return;
+
+ for(entry_num = 0; entry_num < ab->count; entry_num++){
+ ae = adrbk_get_ae(ab, (a_c_arg_t) entry_num);
+ ae->referenced = 0;
+ }
+}
+
+
+/*
+ * Allocate a new AdrBk_Entry
+ */
+AdrBk_Entry *
+adrbk_newentry(void)
+{
+ AdrBk_Entry *ae;
+
+ ae = (AdrBk_Entry *) fs_get(sizeof(AdrBk_Entry));
+ defvalue_ae(ae);
+
+ return(ae);
+}
+
+
+/*
+ * Just sets ae values to default.
+ * Parts should be freed before calling this or they will leak.
+ */
+void
+defvalue_ae(AdrBk_Entry *ae)
+{
+ ae->nickname = empty;
+ ae->fullname = empty;
+ ae->addr.addr = empty;
+ ae->fcc = empty;
+ ae->extra = empty;
+ ae->tag = NotSet;
+ ae->referenced = 0;
+}
+
+
+AdrBk_Entry *
+copy_ae(AdrBk_Entry *src)
+{
+ AdrBk_Entry *a;
+
+ a = adrbk_newentry();
+ a->tag = src->tag;
+ a->nickname = cpystr(src->nickname ? src->nickname : "");
+ a->fullname = cpystr(src->fullname ? src->fullname : "");
+ a->fcc = cpystr(src->fcc ? src->fcc : "");
+ a->extra = cpystr(src->extra ? src->extra : "");
+ if(a->tag == Single)
+ a->addr.addr = cpystr(src->addr.addr ? src->addr.addr : "");
+ else if(a->tag == List){
+ char **p;
+ int i, n;
+
+ /* count list */
+ for(p = src->addr.list; p && *p; p++)
+ ;/* do nothing */
+
+ if(p == NULL)
+ n = 0;
+ else
+ n = p - src->addr.list;
+
+ a->addr.list = (char **)fs_get((n+1) * sizeof(char *));
+ for(i = 0; i < n; i++)
+ a->addr.list[i] = cpystr(src->addr.list[i]);
+
+ a->addr.list[n] = NULL;
+ }
+
+ return(a);
+}
+
+
+/*
+ * Add an entry to the address book, or modify an existing entry
+ *
+ * Args: ab -- address book to add to
+ * old_entry_num -- the entry we want to modify. If this is NO_NEXT, then
+ * we look up the nickname passed in to see if that's the
+ * entry to modify, else it is a new entry.
+ * nickname -- the nickname for new entry
+ * fullname -- the fullname for new entry
+ * address -- the address for new entry
+ * fcc -- the fcc for new entry
+ * extra -- the extra field for new entry
+ * tag -- the type of new entry
+ * new_entry_num -- return entry_num of new or modified entry here
+ * resort_happened -- means that more than just the current entry changed,
+ * either something was added or order was changed
+ * enable_intr -- tell adrbk_write to enable interrupt handling
+ * be_quiet -- tell adrbk_write to not do percent done messages
+ * write_it -- only do adrbk_write if this is set
+ *
+ * Result: return code: 0 all went well
+ * -2 error writing address book, check errno
+ * -3 no modification, the tag given didn't match
+ * existing tag
+ * -4 tabs are in one of the fields passed in
+ *
+ * If the nickname exists in the address book already, the operation is
+ * considered a modification even if the case does not match exactly,
+ * otherwise it is an add. The entry the operation occurs on is returned
+ * in new. All fields are set to those passed in; that is, passing in NULL
+ * even on a modification will set those fields to NULL as opposed to leaving
+ * them unchanged. It is acceptable to pass in the current strings
+ * in the entry in the case of modification. For address lists, the
+ * structure passed in is what is used, so the storage has to all have
+ * come from fs_get(). If the pointer passed in is the same as
+ * the current field, no change is made.
+ */
+int
+adrbk_add(AdrBk *ab, a_c_arg_t old_entry_num, char *nickname, char *fullname,
+ char *address, char *fcc, char *extra, Tag tag, adrbk_cntr_t *new_entry_num,
+ int *resort_happened, int enable_intr, int be_quiet, int write_it)
+{
+ AdrBk_Entry *a;
+ AdrBk_Entry *ae;
+ adrbk_cntr_t old_enum;
+ adrbk_cntr_t new_enum;
+ int (*cmp_func)();
+ int retval = 0;
+ int need_write = 0;
+ int set_mangled = 0;
+
+ dprint((3, "- adrbk_add(%s) -\n", nickname ? nickname : ""));
+
+ if(!ab)
+ return -2;
+
+ /* ---- Make sure there are no tabs in the stuff to add ------*/
+ if((nickname != NULL && strindex(nickname, TAB) != NULL) ||
+ (fullname != NULL && strindex(fullname, TAB) != NULL) ||
+ (fcc != NULL && strindex(fcc, TAB) != NULL) ||
+ (tag == Single && address != NULL && strindex(address, TAB) != NULL))
+ return -4;
+
+ /*
+ * Are we adding or updating ?
+ *
+ * If old_entry_num was passed in, we're updating that. If nickname
+ * already exists, we're updating that entry. Otherwise, this is an add.
+ */
+ if((adrbk_cntr_t)old_entry_num != NO_NEXT){
+ ae = adrbk_get_ae(ab, old_entry_num);
+ if(ae)
+ old_enum = (adrbk_cntr_t)old_entry_num;
+ }
+ else
+ ae = adrbk_lookup_by_nick(ab, nickname, &old_enum);
+
+ if(ae == NULL){ /*----- adding a new entry ----*/
+
+ ae = adrbk_newentry();
+ ae->tag = tag;
+ if(nickname)
+ ae->nickname = cpystr(nickname);
+ if(fullname)
+ ae->fullname = cpystr(fullname);
+ if(fcc)
+ ae->fcc = cpystr(fcc);
+ if(extra)
+ ae->extra = cpystr(extra);
+
+ if(tag == Single)
+ ae->addr.addr = cpystr(address);
+ else
+ ae->addr.list = (char **)NULL;
+
+ cmp_func = (ab->sort_rule == AB_SORT_RULE_FULL_LISTS) ?
+ cmp_ae_by_full_lists_last :
+ (ab->sort_rule == AB_SORT_RULE_FULL) ?
+ cmp_ae_by_full :
+ (ab->sort_rule == AB_SORT_RULE_NICK_LISTS) ?
+ cmp_ae_by_nick_lists_last :
+ /* (ab->sort_rule == AB_SORT_RULE_NICK) */
+ cmp_ae_by_nick;
+
+ if(ab->sort_rule == AB_SORT_RULE_NONE) /* put it last */
+ new_enum = ab->count;
+ else /* Find slot for it */
+ for(new_enum = 0, a = adrbk_get_ae(ab, (a_c_arg_t) new_enum);
+ a != (AdrBk_Entry *)NULL;
+ a = adrbk_get_ae(ab, (a_c_arg_t) (++new_enum))){
+ if((*cmp_func)((qsort_t *)&a, (qsort_t *)&ae) >= 0)
+ break;
+ }
+
+ /* Insert ae before entry new_enum. */
+ insert_ab_entry(ab, (a_c_arg_t) new_enum, ae, 0);
+
+ if(F_OFF(F_EXPANDED_DISTLISTS,ps_global))
+ exp_add_nth(ab->exp, (a_c_arg_t)new_enum);
+
+ exp_add_nth(ab->selects, (a_c_arg_t)new_enum);
+
+ /*
+ * insert_ab_entry copies the pointers of ae so the things
+ * being pointed to (nickname, ...) are still in use.
+ * Don't free them but free the ae struct itself.
+ */
+ fs_give((void **) &ae);
+
+ /*---- return in pointer if requested -----*/
+ if(new_entry_num)
+ *new_entry_num = new_enum;
+
+ if(resort_happened)
+ *resort_happened = 1;
+
+ if(write_it)
+ retval = adrbk_write(ab, (a_c_arg_t) new_enum, new_entry_num, &set_mangled,
+ enable_intr, be_quiet);
+ }
+ else{
+ /*----- Updating an existing entry ----*/
+
+ int fix_tries = 0;
+
+ if(ae->tag != tag)
+ return -3;
+
+ /*
+ * Instead of just freeing and reallocating here we attempt to re-use
+ * the space that was already allocated if possible.
+ */
+ if(ae->nickname != nickname
+ && ae->nickname != NULL
+ && nickname != NULL
+ && strcmp(nickname, ae->nickname) != 0){
+ need_write++;
+ fix_tries++;
+ /* can use already alloc'd space */
+ if(ae->nickname != NULL && nickname != NULL &&
+ strlen(nickname) <= strlen(ae->nickname)){
+
+ strncpy(ae->nickname, nickname, strlen(ae->nickname)+1);
+ }
+ else{
+ if(ae->nickname != NULL && ae->nickname != empty)
+ fs_give((void **)&ae->nickname);
+
+ ae->nickname = nickname ? cpystr(nickname) : nickname;
+ }
+ }
+
+ if(ae->fullname != fullname
+ && ae->fullname != NULL
+ && fullname != NULL
+ && strcmp(fullname, ae->fullname) != 0){
+ need_write++;
+ if(ae->fullname != NULL && fullname != NULL &&
+ strlen(fullname) <= strlen(ae->fullname)){
+
+ strncpy(ae->fullname, fullname, strlen(ae->fullname)+1);
+ }
+ else{
+ if(ae->fullname != NULL && ae->fullname != empty)
+ fs_give((void **)&ae->fullname);
+
+ ae->fullname = fullname ? cpystr(fullname) : fullname;
+ }
+ }
+
+ if(ae->fcc != fcc
+ && ae->fcc != NULL
+ && fcc != NULL
+ && strcmp(fcc, ae->fcc) != 0){
+ need_write++;
+ if(ae->fcc != NULL && fcc != NULL &&
+ strlen(fcc) <= strlen(ae->fcc)){
+
+ strncpy(ae->fcc, fcc, strlen(ae->fcc)+1);
+ }
+ else{
+ if(ae->fcc != NULL && ae->fcc != empty)
+ fs_give((void **)&ae->fcc);
+
+ ae->fcc = fcc ? cpystr(fcc) : fcc;
+ }
+ }
+
+ if(ae->extra != extra
+ && ae->extra != NULL
+ && extra != NULL
+ && strcmp(extra, ae->extra) != 0){
+ need_write++;
+ if(ae->extra != NULL && extra != NULL &&
+ strlen(extra) <= strlen(ae->extra)){
+
+ strncpy(ae->extra, extra, strlen(ae->extra)+1);
+ }
+ else{
+ if(ae->extra != NULL && ae->extra != empty)
+ fs_give((void **)&ae->extra);
+
+ ae->extra = extra ? cpystr(extra) : extra;
+ }
+ }
+
+ if(tag == Single){
+ /*---- Single ----*/
+ if(ae->addr.addr != address
+ && ae->addr.addr != NULL
+ && address != NULL
+ && strcmp(address, ae->addr.addr) != 0){
+ need_write++;
+ fix_tries++;
+ if(ae->addr.addr != NULL && address != NULL &&
+ strlen(address) <= strlen(ae->addr.addr)){
+
+ strncpy(ae->addr.addr, address, strlen(ae->addr.addr)+1);
+ }
+ else{
+ if(ae->addr.addr != NULL && ae->addr.addr != empty)
+ fs_give((void **)&ae->addr.addr);
+
+ ae->addr.addr = address ? cpystr(address) : address;
+ }
+ }
+ }
+ else{
+ /*---- List -----*/
+ /*
+ * We don't mess with lists here.
+ * The caller has to do it with adrbk_listadd().
+ */
+ ;/* do nothing */
+ }
+
+
+ /*---------- Make sure it's still in order ---------*/
+
+ /*
+ * old_enum is where ae is currently located
+ * put it where it belongs
+ */
+ if(need_write){
+ new_enum = re_sort_particular_entry(ab, (a_c_arg_t) old_enum);
+ if(old_enum != new_enum)
+ fix_tries++;
+ }
+ else
+ new_enum = old_enum;
+
+ /*---- return in pointer if requested -----*/
+ if(new_entry_num)
+ *new_entry_num = new_enum;
+
+ if(resort_happened)
+ *resort_happened = (old_enum != new_enum);
+
+ if(fix_tries)
+ repair_abook_tries(ab);
+
+ if(write_it && need_write){
+ int sort_happened = 0;
+
+ retval = adrbk_write(ab, (a_c_arg_t) new_enum, new_entry_num,
+ &sort_happened, enable_intr, be_quiet);
+
+ set_mangled = sort_happened;
+
+ if(new_entry_num)
+ new_enum = (*new_entry_num);
+
+ if(resort_happened && (sort_happened || (old_enum != new_enum)))
+ *resort_happened = 1;
+ }
+ else
+ retval = 0;
+ }
+
+ if(set_mangled)
+ ps_global->mangled_screen = 1;
+
+ return(retval);
+}
+
+
+/*
+ * Similar to adrbk_add, but lower cost. No sorting is done, the new entry
+ * goes on the end. This won't work if it is an edit instead of an append.
+ * The address book is not committed to disk.
+ *
+ * Args: ab -- address book to add to
+ * nickname -- the nickname for new entry
+ * fullname -- the fullname for new entry
+ * address -- the address for new entry
+ * fcc -- the fcc for new entry
+ * extra -- the extra field for new entry
+ * tag -- the type of new entry
+ *
+ * Result: return code: 0 all went well
+ * -2 error writing address book, check errno
+ * -3 no modification, the tag given didn't match
+ * existing tag
+ * -4 tabs are in one of the fields passed in
+ */
+int
+adrbk_append(AdrBk *ab, char *nickname, char *fullname, char *address, char *fcc,
+ char *extra, Tag tag, adrbk_cntr_t *new_entry_num)
+{
+ AdrBk_Entry *ae;
+
+ dprint((3, "- adrbk_append(%s) -\n", nickname ? nickname : ""));
+
+ if(!ab)
+ return -2;
+
+ /* ---- Make sure there are no tabs in the stuff to add ------*/
+ if((nickname != NULL && strindex(nickname, TAB) != NULL) ||
+ (fullname != NULL && strindex(fullname, TAB) != NULL) ||
+ (fcc != NULL && strindex(fcc, TAB) != NULL) ||
+ (tag == Single && address != NULL && strindex(address, TAB) != NULL))
+ return -4;
+
+ ae = adrbk_newentry();
+ ae->tag = tag;
+ if(nickname)
+ ae->nickname = cpystr(nickname);
+ if(fullname)
+ ae->fullname = cpystr(fullname);
+ if(fcc)
+ ae->fcc = cpystr(fcc);
+ if(extra)
+ ae->extra = cpystr(extra);
+
+ if(tag == Single)
+ ae->addr.addr = cpystr(address);
+ else
+ ae->addr.list = (char **)NULL;
+
+ if(new_entry_num)
+ *new_entry_num = ab->count;
+
+ insert_ab_entry(ab, (a_c_arg_t) ab->count, ae, 0);
+
+ /*
+ * insert_ab_entry copies the pointers of ae so the things
+ * being pointed to (nickname, ...) are still in use.
+ * Don't free them but free the ae struct itself.
+ */
+ fs_give((void **) &ae);
+
+ return(0);
+}
+
+
+/*
+ * The entire address book is assumed sorted correctly except perhaps for
+ * entry number cur. Put it in the correct place. Return the new entry
+ * number for cur.
+ */
+adrbk_cntr_t
+re_sort_particular_entry(AdrBk *ab, a_c_arg_t cur)
+{
+ AdrBk_Entry *ae_cur, *ae_prev, *ae_next, *ae_small_enough, *ae_big_enough;
+ long small_enough;
+ adrbk_cntr_t big_enough;
+ adrbk_cntr_t new_entry_num;
+ int (*cmp_func)(const qsort_t *, const qsort_t *);
+
+ dprint((9, "- re_sort -\n"));
+
+ cmp_func = (ab->sort_rule == AB_SORT_RULE_FULL_LISTS) ?
+ cmp_ae_by_full_lists_last :
+ (ab->sort_rule == AB_SORT_RULE_FULL) ?
+ cmp_ae_by_full :
+ (ab->sort_rule == AB_SORT_RULE_NICK_LISTS) ?
+ cmp_ae_by_nick_lists_last :
+ /* (ab->sort_rule == AB_SORT_RULE_NICK) */
+ cmp_ae_by_nick;
+
+ new_entry_num = (adrbk_cntr_t) cur;
+
+ if(ab->sort_rule == AB_SORT_RULE_NONE)
+ return(new_entry_num);
+
+ ae_cur = adrbk_get_ae(ab, cur);
+
+ if(cur > 0)
+ ae_prev = adrbk_get_ae(ab, cur - 1);
+
+ if(cur < ab->count -1)
+ ae_next = adrbk_get_ae(ab, cur + 1);
+
+ /*
+ * A possible optimization here would be to implement some sort of
+ * binary search to find where it goes instead of stepping through the
+ * entries one at a time.
+ */
+ if(cur > 0 &&
+ (*cmp_func)((qsort_t *)&ae_cur,(qsort_t *)&ae_prev) < 0){
+ /*--- Out of order, needs to be moved up ----*/
+ for(small_enough = (long)cur - 2; small_enough >= 0L; small_enough--){
+ ae_small_enough = adrbk_get_ae(ab,(a_c_arg_t) small_enough);
+ if((*cmp_func)((qsort_t *)&ae_cur, (qsort_t *)&ae_small_enough) >= 0)
+ break;
+ }
+ new_entry_num = (adrbk_cntr_t)(small_enough + 1L);
+ move_ab_entry(ab, cur, (a_c_arg_t) new_entry_num);
+ }
+ else if(cur < ab->count - 1 &&
+ (*cmp_func)((qsort_t *)&ae_cur, (qsort_t *)&ae_next) > 0){
+ /*---- Out of order needs, to be moved towards end of list ----*/
+ for(big_enough = (adrbk_cntr_t)(cur + 2);
+ big_enough < ab->count;
+ big_enough++){
+ ae_big_enough = adrbk_get_ae(ab, (a_c_arg_t) big_enough);
+ if((*cmp_func)((qsort_t *)&ae_cur, (qsort_t *)&ae_big_enough) <= 0)
+ break;
+ }
+ new_entry_num = big_enough - 1;
+ move_ab_entry(ab, cur, (a_c_arg_t) big_enough);
+ }
+
+ dprint((9, "- re_sort done -\n"));
+
+ return(new_entry_num);
+}
+
+
+/*
+ * Delete an entry from the address book
+ *
+ * Args: ab -- the address book
+ * entry_num -- entry to delete
+ * save_deleted -- save deleted as a #DELETED- entry
+ * enable_intr -- tell adrbk_write to enable interrupt handling
+ * be_quiet -- tell adrbk_write to not do percent done messages
+ * write_it -- only do adrbk_write if this is set
+ *
+ * Result: returns: 0 if all went well
+ * -1 if there is no such entry
+ * -2 error writing address book, check errno
+ */
+int
+adrbk_delete(AdrBk *ab, a_c_arg_t entry_num, int save_deleted, int enable_intr,
+ int be_quiet, int write_it)
+{
+ int retval = 0;
+ int set_mangled = 0;
+
+ dprint((3, "- adrbk_delete(%ld) -\n", (long)entry_num));
+
+ if(!ab)
+ return -2;
+
+ delete_ab_entry(ab, entry_num, save_deleted);
+ if(F_OFF(F_EXPANDED_DISTLISTS,ps_global))
+ exp_del_nth(ab->exp, entry_num);
+
+ exp_del_nth(ab->selects, entry_num);
+
+ if(write_it)
+ retval = adrbk_write(ab, 0, NULL, &set_mangled, enable_intr, be_quiet);
+
+ if(set_mangled)
+ ps_global->mangled_screen = 1;
+
+ return(retval);
+}
+
+
+/*
+ * Delete an address out of an address list
+ *
+ * Args: ab -- the address book
+ * entry_num -- the address list we are deleting from
+ * addr -- address in above list to be deleted
+ *
+ * Result: 0: Deletion complete, address book written
+ * -1: Address for deletion not found
+ * -2: Error writing address book. Check errno.
+ *
+ * The address to be deleted is located by matching the string.
+ */
+int
+adrbk_listdel(AdrBk *ab, a_c_arg_t entry_num, char *addr)
+{
+ char **p, *to_free;
+ AdrBk_Entry *ae;
+ int ret;
+ int set_mangled = 0;
+
+ dprint((3, "- adrbk_listdel(%ld) -\n", (long) entry_num));
+
+ if(!ab || entry_num >= ab->count)
+ return -2;
+
+ if(!addr)
+ return -1;
+
+ ae = adrbk_get_ae(ab, entry_num);
+
+ if(ae->tag != List)
+ return -1;
+
+ for(p = ae->addr.list; *p; p++)
+ if(strcmp(*p, addr) == 0)
+ break;
+
+ if(*p == NULL)
+ return -1;
+
+ /* note storage to be freed */
+ if(*p != empty)
+ to_free = *p;
+ else
+ to_free = NULL;
+
+ /* slide all the entries below up (including NULL) */
+ for(; *p; p++)
+ *p = *(p+1);
+
+ if(to_free)
+ fs_give((void **) &to_free);
+
+ ret = adrbk_write(ab, 0, NULL, &set_mangled, 1, 0);
+
+ if(set_mangled)
+ ps_global->mangled_screen = 1;
+
+ return(ret);
+}
+
+
+/*
+ * Delete all addresses out of an address list
+ *
+ * Args: ab -- the address book
+ * entry_num -- the address list we are deleting from
+ *
+ * Result: 0: Deletion complete, address book written
+ * -1: Address for deletion not found
+ * -2: Error writing address book. Check errno.
+ */
+int
+adrbk_listdel_all(AdrBk *ab, a_c_arg_t entry_num)
+{
+ char **p;
+ AdrBk_Entry *ae;
+
+ dprint((3, "- adrbk_listdel_all(%ld) -\n", (long) entry_num));
+
+ if(!ab || entry_num >= ab->count)
+ return -2;
+
+ ae = adrbk_get_ae(ab, entry_num);
+
+ if(ae->tag != List)
+ return -1;
+
+ /* free old list */
+ for(p = ae->addr.list; p && *p; p++)
+ if(*p != empty)
+ fs_give((void **)p);
+
+ if(ae->addr.list)
+ fs_give((void **) &ae->addr.list);
+
+ ae->addr.list = NULL;
+
+ return 0;
+}
+
+
+/*
+ * Add a list of addresses to an already existing address list
+ *
+ * Args: ab -- the address book
+ * entry_num -- the address list we are adding to
+ * addrs -- address list to be added
+ * enable_intr -- tell adrbk_write to enable interrupt handling
+ * be_quiet -- tell adrbk_write to not do percent done messages
+ * write_it -- only do adrbk_write if this is set
+ *
+ * Result: returns 0 : addition made, address book written
+ * -1 : addition to non-list attempted
+ * -2 : error writing address book -- check errno
+ */
+int
+adrbk_nlistadd(AdrBk *ab, a_c_arg_t entry_num, adrbk_cntr_t *new_entry_num,
+ int *resort_happened, char **addrs,
+ int enable_intr, int be_quiet, int write_it)
+{
+ char **p;
+ int cur_size, size_of_additional_list, new_size;
+ int i, rc = 0;
+ int set_mangled = 0;
+ AdrBk_Entry *ae;
+
+ dprint((3, "- adrbk_nlistadd(%ld) -\n", (long) entry_num));
+
+ if(!ab || entry_num >= ab->count)
+ return -2;
+
+ ae = adrbk_get_ae(ab, entry_num);
+
+ if(ae->tag != List)
+ return -1;
+
+ /* count up size of existing list */
+ for(p = ae->addr.list; p != NULL && *p != NULL; p++)
+ ;/* do nothing */
+
+ cur_size = p - ae->addr.list;
+
+ /* count up size of new list */
+ for(p = addrs; p != NULL && *p != NULL; p++)
+ ;/* do nothing */
+
+ size_of_additional_list = p - addrs;
+ new_size = cur_size + size_of_additional_list;
+
+ /* make room at end of list for it */
+ if(cur_size == 0)
+ ae->addr.list = (char **) fs_get(sizeof(char *) * (new_size + 1));
+ else
+ fs_resize((void **) &ae->addr.list, sizeof(char *) * (new_size + 1));
+
+ /* Put new list at the end */
+ for(i = cur_size; i < new_size; i++)
+ (ae->addr.list)[i] = cpystr(addrs[i - cur_size]);
+
+ (ae->addr.list)[new_size] = NULL;
+
+ /*---- sort it into the correct place ------*/
+ if(ab->sort_rule != AB_SORT_RULE_NONE)
+ sort_addr_list(ae->addr.list);
+
+ if(write_it)
+ rc = adrbk_write(ab, entry_num, new_entry_num, &set_mangled, enable_intr, be_quiet);
+
+ if(set_mangled){
+ ps_global->mangled_screen = 1;
+ if(resort_happened)
+ *resort_happened = 1;
+ }
+
+ return(rc);
+}
+
+
+/*
+ * Set the valid variable if we determine that the address book has
+ * been changed by something other than us. This means we should update
+ * our view of the address book when next possible.
+ *
+ * Args ab -- AdrBk handle
+ * do_it_now -- If > 0, check now regardless
+ * If = 0, check if time since last chk more than default
+ * If < 0, check if time since last chk more than -do_it_now
+ */
+void
+adrbk_check_validity(AdrBk *ab, long int do_it_now)
+{
+ dprint((9, "- adrbk_check_validity(%s) -\n",
+ (ab && ab->filename) ? ab->filename : ""));
+
+ if(!ab || ab->flags & FILE_OUTOFDATE)
+ return;
+
+ adrbk_check_local_validity(ab, do_it_now);
+
+ if(ab->type == Imap && !(ab->flags & FILE_OUTOFDATE ||
+ ab->rd->flags & REM_OUTOFDATE))
+ rd_check_remvalid(ab->rd, do_it_now);
+}
+
+
+/*
+ * Set the valid variable if we determine that the address book has
+ * been changed by something other than us. This means we should update
+ * our view of the address book when next possible.
+ *
+ * Args ab -- AdrBk handle
+ * do_it_now -- If > 0, check now regardless
+ * If = 0, check if time since last chk more than default
+ * If < 0, check if time since last chk more than -do_it_now
+ */
+void
+adrbk_check_local_validity(AdrBk *ab, long int do_it_now)
+{
+ time_t mtime, chk_interval;
+
+ dprint((9, "- adrbk_check_local_validity(%s) -\n",
+ (ab && ab->filename) ? ab->filename : ""));
+
+ if(!ab)
+ return;
+
+ if(do_it_now < 0L){
+ chk_interval = -1L * do_it_now;
+ do_it_now = 0L;
+ }
+ else
+ chk_interval = FILE_VALID_CHK_INTERVAL;
+
+ if(!do_it_now &&
+ get_adj_time() <= ab->last_local_valid_chk + chk_interval)
+ return;
+
+ ab->last_local_valid_chk = get_adj_time();
+
+ /*
+ * Check local file for a modification time change.
+ * If this is out of date, don't even bother checking for the remote
+ * folder being out of date. That will get fixed when we reopen.
+ */
+ if(!(ab->flags & FILE_OUTOFDATE) &&
+ ab->last_change_we_know_about != (time_t)(-1) &&
+ (mtime=get_adj_name_file_mtime(ab->filename)) != (time_t)(-1) &&
+ ab->last_change_we_know_about != mtime){
+
+ dprint((2, "adrbk_check_local_validity: addrbook %s has changed\n",
+ ab->filename ? ab->filename : "?"));
+ ab->flags |= FILE_OUTOFDATE;
+ }
+}
+
+
+/*
+ * See if we can re-use an existing stream.
+ *
+ * [ We don't believe we need this anymore now that the stuff in pine.c ]
+ * [ is recycling streams for us, but we haven't thought it through all ]
+ * [ the way enough to get rid of this. Hubert 2003-07-09 ]
+ *
+ * Args name -- Name of folder we want a stream for.
+ *
+ * Returns -- A mail stream suitable for status cmd or append cmd.
+ * NULL if none were available.
+ */
+MAILSTREAM *
+adrbk_handy_stream(char *name)
+{
+ MAILSTREAM *stat_stream = NULL;
+ int i;
+
+ dprint((9, "- adrbk_handy_stream(%s) -\n", name ? name : "?"));
+
+ stat_stream = sp_stream_get(name, SP_SAME);
+
+ /*
+ * Look through our imap streams to see if there is one we can use.
+ */
+ for(i = 0; !stat_stream && i < as.n_addrbk; i++){
+ PerAddrBook *pab;
+
+ pab = &as.adrbks[i];
+
+ if(pab->address_book &&
+ pab->address_book->type == Imap &&
+ pab->address_book->rd &&
+ pab->address_book->rd->type == RemImap &&
+ same_stream(name, pab->address_book->rd->t.i.stream)){
+ stat_stream = pab->address_book->rd->t.i.stream;
+ pab->address_book->rd->last_use = get_adj_time();
+ dprint((7,
+ "%s: used other abook stream for status (%ld)\n",
+ pab->address_book->orig_filename
+ ? pab->address_book->orig_filename : "?",
+ (long)pab->address_book->rd->last_use));
+ }
+ }
+
+ dprint((9, "adrbk_handy_stream: returning %s\n",
+ stat_stream ? "good stream" : "NULL"));
+
+ return(stat_stream);
+}
+
+
+/*
+ * Close address book
+ *
+ * All that is done here is to free the storage, since the address book is
+ * rewritten on every change.
+ */
+void
+adrbk_close(AdrBk *ab)
+{
+ int we_cancel = 0;
+
+ dprint((4, "- adrbk_close(%s) -\n",
+ (ab && ab->filename) ? ab->filename : ""));
+
+ if(!ab)
+ return;
+
+ if(ab->rd)
+ rd_close_remdata(&ab->rd);
+
+ if(ab->fp)
+ (void)fclose(ab->fp);
+
+ if(ab->our_filecopy && ab->filename != ab->our_filecopy){
+ our_unlink(ab->our_filecopy);
+ fs_give((void**) &ab->our_filecopy);
+ }
+
+ if(ab->exp){
+ exp_free(ab->exp);
+ fs_give((void **)&ab->exp); /* free head of list, too */
+ }
+
+ if(ab->checks){
+ exp_free(ab->checks);
+ fs_give((void **)&ab->checks); /* free head of list, too */
+ }
+
+ if(ab->selects){
+ exp_free(ab->selects);
+ fs_give((void **)&ab->selects); /* free head of list, too */
+ }
+
+ if(ab->arr){
+ adrbk_cntr_t entry_num;
+
+ /*
+ * ab->arr is an allocated array. Each element of the array contains
+ * several pointers that point to other allocated stuff, so we
+ * free_ae_parts to get those and then free the array after the loop.
+ */
+ for(entry_num = 0; entry_num < ab->count; entry_num++)
+ free_ae_parts(&ab->arr[entry_num]);
+
+ fs_give((void **) &ab->arr);
+ }
+
+ if(ab->del){
+ adrbk_cntr_t entry_num;
+
+ for(entry_num = 0; entry_num < ab->del_count; entry_num++)
+ free_ae_parts(&ab->del[entry_num]);
+
+ fs_give((void **) &ab->del);
+ }
+
+ if(ab->nick_trie)
+ free_abook_trie(&ab->nick_trie);
+
+ if(ab->addr_trie)
+ free_abook_trie(&ab->addr_trie);
+
+ if(ab->full_trie)
+ free_abook_trie(&ab->full_trie);
+
+ if(ab->revfull_trie)
+ free_abook_trie(&ab->revfull_trie);
+
+ if(we_cancel)
+ cancel_busy_cue(0);
+
+ if(ab->filename){
+ if(ab->flags & DEL_FILE)
+ our_unlink(ab->filename);
+
+ fs_give((void**)&ab->filename);
+ }
+
+ if(ab->orig_filename)
+ fs_give((void**)&ab->orig_filename);
+
+ fs_give((void **) &ab);
+}
+
+
+void
+adrbk_partial_close(AdrBk *ab)
+{
+ dprint((4, "- adrbk_partial_close(%s) -\n",
+ (ab && ab->filename) ? ab->filename : ""));
+
+ exp_free(ab->exp); /* leaves head of list */
+ exp_free(ab->checks); /* leaves head of list */
+}
+
+
+/*
+ * It has been noticed that this stream is dead and it is about to
+ * be removed from the stream pool. We may have a pointer to it for one
+ * of the remote address books. We have to note that the pointer is stale.
+ */
+void
+note_closed_adrbk_stream(MAILSTREAM *stream)
+{
+ PerAddrBook *pab;
+ int i;
+
+ if(!stream)
+ return;
+
+ for(i = 0; i < as.n_addrbk; i++){
+ pab = &as.adrbks[i];
+ if(pab->address_book
+ && pab->address_book->type == Imap
+ && pab->address_book->rd
+ && pab->address_book->rd->type == RemImap
+ && (stream == pab->address_book->rd->t.i.stream)){
+ dprint((4, "- note_closed_adrbk_stream(%s) -\n",
+ (pab->address_book && pab->address_book->orig_filename)
+ ? pab->address_book->orig_filename : ""));
+ pab->address_book->rd->t.i.stream = NULL;
+ }
+ }
+}
+
+
+static adrbk_cntr_t tot_for_percent;
+static adrbk_cntr_t entry_num_for_percent;
+
+/*
+ * Write out the address book.
+ *
+ * If be_quiet is set, don't turn on busy_cue.
+ *
+ * If enable_intr_handling is set, turn on and off interrupt handling.
+ *
+ * Format is as in comment in the adrbk_open routine. Lines are wrapped
+ * to be under 80 characters. This is called on every change to the
+ * address book. Write is first to a temporary file,
+ * which is then renamed to be the real address book so that we won't
+ * destroy the real address book in case of something like a full file
+ * system.
+ *
+ * Writing a temp file and then renaming has the bad side affect of
+ * destroying links. It also overrides any read only permissions on
+ * the mail file since rename ignores such permissions. However, we
+ * handle readonly-ness in addrbook.c before we call this.
+ * We retain the permissions by doing a stat on the old file and a
+ * chmod on the new one (with file_attrib_copy).
+ *
+ * In pre-alpine pine the address book entries were encoded on disk.
+ * We would be happy with raw UTF-8 now but in order to preserve some
+ * backwards compatibility we encode the entries before writing.
+ * We first try to translate to the user's character set and encode
+ * in that, else we use UTF-8 but with 1522 encoding.
+ *
+ * Returns: 0 write was successful
+ * -2 write failed
+ * -5 interrupted
+ */
+int
+adrbk_write(AdrBk *ab, a_c_arg_t current_entry_num, adrbk_cntr_t *new_entry_num,
+ int *sort_happened, int enable_intr_handling, int be_quiet)
+{
+ FILE *ab_stream = NULL;
+ AdrBk_Entry *ae = NULL;
+ adrbk_cntr_t entry_num;
+#ifndef DOS
+ void (*save_sighup)();
+#endif
+ int max_nick = 0,
+ max_full = 0, full_two = 0, full_three = 0,
+ max_fcc = 0, fcc_two = 0, fcc_three = 0,
+ max_addr = 0, addr_two = 0, addr_three = 0,
+ this_nick_width, this_full_width, this_addr_width,
+ this_fcc_width, fd, i;
+ int interrupt_happened = 0, we_cancel = 0, we_turned_on = 0;
+ char *temp_filename = NULL;
+ WIDTH_INFO_S *widths;
+
+ if(!ab)
+ return -2;
+
+ dprint((2, "- adrbk_write(\"%s\") - writing %lu entries\n",
+ ab->filename ? ab->filename : "", (unsigned long) ab->count));
+
+ errno = 0;
+
+ adrbk_check_local_validity(ab, 1L);
+ /* verify that file has not been changed by something else */
+ if(ab->flags & FILE_OUTOFDATE){
+ /* It has changed! */
+ q_status_message(SM_ORDER | SM_DING, 5, 15,
+ /* TRANSLATORS: The address book was changed by something else so alpine
+ is not making the change the user wanted to make to avoid damaging
+ the address book. */
+ _("Addrbook changed by another process, aborting our change to avoid damage..."));
+ dprint((1, "adrbk_write: addrbook %s changed while we had it open, aborting write\n",
+ ab->filename ? ab->filename : "?"));
+ longjmp(addrbook_changed_unexpectedly, 1);
+ /*NOTREACHED*/
+ }
+
+ /*
+ * Verify that remote folder has not been
+ * changed by something else (not if checked in last 5 seconds).
+ *
+ * The -5 is so we won't check every time on a series of quick writes.
+ */
+ rd_check_remvalid(ab->rd, -5L);
+ if(ab->type == Imap){
+ int ro;
+
+ /*
+ * We'll eventually need this open to write to remote, so see if
+ * we can open it now.
+ */
+ rd_open_remote(ab->rd);
+
+ /*
+ * Did someone else change the remote copy?
+ */
+ if((ro=rd_remote_is_readonly(ab->rd)) || ab->rd->flags & REM_OUTOFDATE){
+ if(ro == 1){
+ q_status_message(SM_ORDER | SM_DING, 5, 15,
+ _("Can't access remote addrbook, aborting change..."));
+ dprint((1,
+ "adrbk_write: Can't write to remote addrbook %s, aborting write: open failed\n",
+ ab->rd->rn ? ab->rd->rn : "?"));
+ }
+ else if(ro == 2){
+ if(!(ab->rd->flags & NO_META_UPDATE)){
+ unsigned long save_chk_nmsgs;
+
+ /*
+ * Should have some non-type-specific method of doing this.
+ */
+ switch(ab->rd->type){
+ case RemImap:
+ save_chk_nmsgs = ab->rd->t.i.chk_nmsgs;
+ ab->rd->t.i.chk_nmsgs = 0;/* cause it to be OUTOFDATE */
+ rd_write_metadata(ab->rd, 0);
+ ab->rd->t.i.chk_nmsgs = save_chk_nmsgs;
+ break;
+
+ default:
+ q_status_message(SM_ORDER | SM_DING, 3, 5,
+ "Adrbk_write: Type not supported");
+ break;
+ }
+ }
+
+ q_status_message(SM_ORDER | SM_DING, 5, 15,
+ _("No write permission for remote addrbook, aborting change..."));
+ }
+ else{
+ q_status_message(SM_ORDER | SM_DING, 5, 15,
+ _("Remote addrbook changed, aborting our change to avoid damage..."));
+ dprint((1,
+ "adrbk_write: remote addrbook %s changed while we had it open, aborting write\n",
+ ab->orig_filename ? ab->orig_filename : "?"));
+ }
+
+ rd_close_remote(ab->rd);
+
+ longjmp(addrbook_changed_unexpectedly, 1);
+ /*NOTREACHED*/
+ }
+ }
+
+#ifndef DOS
+ save_sighup = (void (*)())signal(SIGHUP, SIG_IGN);
+#endif
+
+ /*
+ * If we want to be able to modify the address book, we will
+ * need a temp_filename in the same directory as our_filecopy.
+ */
+ if(!(ab->our_filecopy && ab->our_filecopy[0])
+ || !(temp_filename = tempfile_in_same_dir(ab->our_filecopy,"a1",NULL))
+ || (fd = our_open(temp_filename, OPEN_WRITE_MODE, 0600)) < 0){
+ dprint((1, "adrbk_write(%s): failed opening temp file (%s)\n",
+ ab->filename ? ab->filename : "?",
+ temp_filename ? temp_filename : "NULL"));
+ goto io_error;
+ }
+
+ ab_stream = fdopen(fd, "wb");
+ if(ab_stream == NULL){
+ dprint((1, "adrbk_write(%s): fdopen failed\n", temp_filename ? temp_filename : "?"));
+ goto io_error;
+ }
+
+ if(adrbk_is_in_sort_order(ab, be_quiet)){
+ if(sort_happened)
+ *sort_happened = 0;
+
+ if(new_entry_num)
+ *new_entry_num = current_entry_num;
+ }
+ else{
+ if(sort_happened)
+ *sort_happened = 1;
+
+ (void) adrbk_sort(ab, current_entry_num, new_entry_num, be_quiet);
+ }
+
+ /* accept keyboard interrupts */
+ if(enable_intr_handling)
+ we_turned_on = intr_handling_on();
+
+ if(!be_quiet){
+ tot_for_percent = MAX(ab->count, 1);
+ entry_num_for_percent = 0;
+ we_cancel = busy_cue(_("Saving address book"), percent_abook_saved, 0);
+ }
+
+ writing = 1;
+
+ /*
+ * If there are any old deleted entries, copy them to new file.
+ */
+ if(ab->del_count > 0){
+ char *nickname;
+ long delindex;
+
+ /*
+ * If there are deleted entries old enough that we no longer want
+ * to save them, remove them from the list.
+ */
+ for(delindex = (long) ab->del_count-1;
+ delindex >= 0L && ab->del_count > 0; delindex--){
+
+ ae = adrbk_get_delae(ab, (a_c_arg_t) delindex);
+ nickname = ae->nickname;
+
+ if(strncmp(nickname, DELETED, DELETED_LEN) == 0
+ && isdigit((unsigned char)nickname[DELETED_LEN])
+ && isdigit((unsigned char)nickname[DELETED_LEN+1])
+ && nickname[DELETED_LEN+2] == '/'
+ && isdigit((unsigned char)nickname[DELETED_LEN+3])
+ && isdigit((unsigned char)nickname[DELETED_LEN+4])
+ && nickname[DELETED_LEN+5] == '/'
+ && isdigit((unsigned char)nickname[DELETED_LEN+6])
+ && isdigit((unsigned char)nickname[DELETED_LEN+7])
+ && nickname[DELETED_LEN+8] == '#'){
+ int year, month, day;
+ struct tm *tm_before;
+ time_t now, before;
+
+ now = time((time_t *)0);
+ before = now - (time_t)ABOOK_DELETED_EXPIRE_TIME;
+ tm_before = localtime(&before);
+ tm_before->tm_mon++;
+
+ /*
+ * Check to see if it is older than 100 days.
+ */
+ year = atoi(&nickname[DELETED_LEN]);
+ month = atoi(&nickname[DELETED_LEN+3]);
+ day = atoi(&nickname[DELETED_LEN+6]);
+ if(year < 95)
+ year += 100; /* this breaks in year 2095 */
+
+ /*
+ * remove it if it is more than 100 days old
+ */
+ if(!(year > tm_before->tm_year
+ || (year == tm_before->tm_year
+ && month > tm_before->tm_mon)
+ || (year == tm_before->tm_year
+ && month == tm_before->tm_mon
+ && day >= tm_before->tm_mday))){
+
+ /* it's old, dump it */
+ free_ae_parts(ae);
+ ab->del_count--;
+ /* patch the array by moving everything below up */
+ if(delindex < ab->del_count){
+ memmove(&ab->del[delindex],
+ &ab->del[delindex+1],
+ (ab->del_count - delindex) *
+ sizeof(AdrBk_Entry));
+ }
+ }
+ }
+ }
+
+ /* write out what remains */
+ if(ab->del_count){
+ for(delindex = 0L; delindex < ab->del_count; delindex++){
+ ae = adrbk_get_delae(ab, (a_c_arg_t) delindex);
+ if(write_single_abook_entry(ae, ab_stream, NULL, NULL,
+ NULL, NULL) == EOF){
+ dprint((1, "adrbk_write(%s): failed writing deleted entry\n",
+ temp_filename ? temp_filename : "?"));
+ goto io_error;
+ }
+ }
+ }
+ else{
+ /* nothing left, get rid of del array */
+ fs_give((void **) &ab->del);
+ }
+ }
+
+ if(ab->del_count)
+ dprint((4, " adrbk_write: saving %ld deleted entries\n",
+ (long) ab->del_count));
+
+ for(entry_num = 0; entry_num < ab->count; entry_num++){
+ entry_num_for_percent++;
+
+ ALARM_BLIP();
+
+ ae = adrbk_get_ae(ab, (a_c_arg_t) entry_num);
+
+ if(ae == (AdrBk_Entry *) NULL){
+ dprint((1, "adrbk_write(%s): can't find ae while writing addrbook, entry_num = %ld\n",
+ ab->filename ? ab->filename : "?",
+ (long) entry_num));
+ goto io_error;
+ }
+
+ /* write to temp file */
+ if(write_single_abook_entry(ae, ab_stream, &this_nick_width,
+ &this_full_width, &this_addr_width, &this_fcc_width) == EOF){
+ dprint((1, "adrbk_write(%s): failed writing for entry %ld\n",
+ temp_filename ? temp_filename : "?",
+ (long) entry_num));
+ goto io_error;
+ }
+
+ /* keep track of widths */
+ max_nick = MAX(max_nick, this_nick_width);
+ if(this_full_width > max_full){
+ full_three = full_two;
+ full_two = max_full;
+ max_full = this_full_width;
+ }
+ else if(this_full_width > full_two){
+ full_three = full_two;
+ full_two = this_full_width;
+ }
+ else if(this_full_width > full_three){
+ full_three = this_full_width;
+ }
+
+ if(this_addr_width > max_addr){
+ addr_three = addr_two;
+ addr_two = max_addr;
+ max_addr = this_addr_width;
+ }
+ else if(this_addr_width > addr_two){
+ addr_three = addr_two;
+ addr_two = this_addr_width;
+ }
+ else if(this_addr_width > addr_three){
+ addr_three = this_addr_width;
+ }
+
+ if(this_fcc_width > max_fcc){
+ fcc_three = fcc_two;
+ fcc_two = max_fcc;
+ max_fcc = this_fcc_width;
+ }
+ else if(this_fcc_width > fcc_two){
+ fcc_three = fcc_two;
+ fcc_two = this_fcc_width;
+ }
+ else if(this_fcc_width > fcc_three){
+ fcc_three = this_fcc_width;
+ }
+
+ /*
+ * Check to see if we've been interrupted. We check at the bottom
+ * of the loop so that we can handle an interrupt in the last
+ * iteration.
+ */
+ if(enable_intr_handling && ps_global->intr_pending){
+ interrupt_happened++;
+ goto io_error;
+ }
+ }
+
+ if(we_cancel){
+ cancel_busy_cue(-1);
+ we_cancel = 0;
+ }
+
+ if(enable_intr_handling && we_turned_on){
+ intr_handling_off();
+ we_turned_on = 0;
+ }
+
+ if(fclose(ab_stream) == EOF){
+ dprint((1, "adrbk_write: fclose for %s failed\n",
+ temp_filename ? temp_filename : "?"));
+ goto io_error;
+ }
+
+ ab_stream = (FILE *) NULL;
+
+ file_attrib_copy(temp_filename, ab->our_filecopy);
+ if(ab->fp){
+ (void) fclose(ab->fp);
+ ab->fp = NULL; /* in case of problems */
+ }
+
+ if((i=rename_file(temp_filename, ab->our_filecopy)) < 0){
+ dprint((1, "adrbk_write: rename(%s, %s) failed: %s\n",
+ temp_filename ? temp_filename : "?",
+ ab->our_filecopy ? ab->our_filecopy : "?",
+ error_description(errno)));
+#ifdef _WINDOWS
+ if(i == -5){
+ q_status_message2(SM_ORDER | SM_DING, 5, 7,
+ _("Can't replace address book %.200sfile \"%.200s\""),
+ (ab->type == Imap) ? "cache " : "",
+ ab->filename);
+ q_status_message(SM_ORDER | SM_DING, 5, 7,
+ _("If another Alpine is running, quit that Alpine before updating address book."));
+ }
+#endif /* _WINDOWS */
+ goto io_error;
+ }
+
+ /* reopen fp to new file */
+ if(!(ab->fp = our_fopen(ab->our_filecopy, "rb"))){
+ dprint((1, "adrbk_write: can't reopen %s\n",
+ ab->our_filecopy ? ab->our_filecopy : "?"));
+ goto io_error;
+ }
+
+ if(temp_filename){
+ our_unlink(temp_filename);
+ fs_give((void **) &temp_filename);
+ }
+
+ /*
+ * Now copy our_filecopy back to filename.
+ */
+ if(ab->filename != ab->our_filecopy){
+ int c, err = 0;
+ char *lc;
+
+ if(!(ab->filename && ab->filename[0])
+ || !(temp_filename = tempfile_in_same_dir(ab->filename,"a1",NULL))
+ || (fd = our_open(temp_filename, OPEN_WRITE_MODE, 0600)) < 0){
+ dprint((1,
+ "adrbk_write(%s): failed opening temp file (%s)\n",
+ ab->filename ? ab->filename : "?",
+ temp_filename ? temp_filename : "NULL"));
+ err++;
+ }
+
+ if(!err)
+ ab_stream = fdopen(fd, "wb");
+
+ if(ab_stream == NULL){
+ dprint((1, "adrbk_write(%s): fdopen failed\n",
+ temp_filename ? temp_filename : "?"));
+ err++;
+ }
+
+ if(!err){
+ rewind(ab->fp);
+ while((c = getc(ab->fp)) != EOF)
+ if(putc(c, ab_stream) == EOF){
+ err++;
+ break;
+ }
+ }
+
+ if(!err && fclose(ab_stream) == EOF)
+ err++;
+
+ if(!err){
+#ifdef _WINDOWS
+ int tries = 3;
+#endif
+
+ file_attrib_copy(temp_filename, ab->filename);
+
+ /*
+ * This could fail if somebody else has it open, but they should
+ * only have it open for a short time.
+ */
+ while(!err && rename_file(temp_filename, ab->filename) < 0){
+#ifdef _WINDOWS
+ if(i == -5){
+ if(--tries <= 0)
+ err++;
+
+ if(!err){
+ q_status_message2(SM_ORDER, 0, 3,
+ _("Replace of \"%.200s\" failed, trying %.200s"),
+ (lc=last_cmpnt(ab->filename)) ? lc : ab->filename,
+ (tries > 1) ? "again" : "one more time");
+ display_message('x');
+ sleep(3);
+ }
+ }
+#else /* UNIX */
+ err++;
+#endif /* UNIX */
+ }
+ }
+
+ if(err){
+ q_status_message1(SM_ORDER | SM_DING, 5, 5,
+ _("Copy of addrbook to \"%.200s\" failed, changes NOT saved!"),
+ (lc=last_cmpnt(ab->filename)) ? lc : ab->filename);
+ dprint((2, "adrbk_write: failed copying our_filecopy (%s)back to filename (%s): %s, continuing without a net\n",
+ ab->our_filecopy ? ab->our_filecopy : "?",
+ ab->filename ? ab->filename : "?",
+ error_description(errno)));
+ }
+ }
+
+ widths = &ab->widths;
+ widths->max_nickname_width = MIN(max_nick, 99);
+ widths->max_fullname_width = MIN(max_full, 99);
+ widths->max_addrfield_width = MIN(max_addr, 99);
+ widths->max_fccfield_width = MIN(max_fcc, 99);
+ widths->third_biggest_fullname_width = MIN(full_three, 99);
+ widths->third_biggest_addrfield_width = MIN(addr_three, 99);
+ widths->third_biggest_fccfield_width = MIN(fcc_three, 99);
+
+ /* record new change date of addrbook file */
+ ab->last_change_we_know_about = get_adj_name_file_mtime(ab->filename);
+
+#ifndef DOS
+ (void)signal(SIGHUP, save_sighup);
+#endif
+
+ /*
+ * If this is a remote addressbook, copy the file over.
+ * If it fails we warn but continue to operate on the changed,
+ * locally cached addressbook file.
+ */
+ if(ab->type == Imap){
+ int e;
+ char datebuf[200];
+
+ datebuf[0] = '\0';
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ we_cancel = busy_cue(_("Copying to remote addressbook"), NULL, 1);
+ /*
+ * We don't want a cookie upgrade to blast our data in rd->lf by
+ * copying back the remote data before doing the upgrade, and then
+ * proceeding to finish with that data. So we tell rd_upgrade_cookie
+ * about that here.
+ */
+ ab->rd->flags |= BELIEVE_CACHE;
+ if((e = rd_update_remote(ab->rd, datebuf)) != 0){
+ if(e == -1){
+ q_status_message2(SM_ORDER | SM_DING, 3, 5,
+ _("Error opening temporary addrbook file %.200s: %.200s"),
+ ab->rd->lf, error_description(errno));
+ dprint((1,
+ "adrbk_write: error opening temp file %s\n",
+ ab->rd->lf ? ab->rd->lf : "?"));
+ }
+ else{
+ q_status_message2(SM_ORDER | SM_DING, 3, 5,
+ _("Error copying to %.200s: %.200s"),
+ ab->rd->rn, error_description(errno));
+ dprint((1,
+ "adrbk_write: error copying from %s to %s\n",
+ ab->rd->lf ? ab->rd->lf : "?",
+ ab->rd->rn ? ab->rd->rn : "?"));
+ }
+
+ q_status_message(SM_ORDER | SM_DING, 5, 5,
+ _("Copy of addrbook to remote folder failed, changes NOT saved remotely"));
+ }
+ else{
+ rd_update_metadata(ab->rd, datebuf);
+ ab->rd->read_status = 'W';
+ dprint((7,
+ "%s: copied local to remote in adrbk_write (%ld)\n",
+ ab->rd->rn ? ab->rd->rn : "?",
+ (long)ab->rd->last_use));
+ }
+
+ ab->rd->flags &= ~BELIEVE_CACHE;
+ }
+
+ writing = 0;
+
+ if(we_cancel)
+ cancel_busy_cue(0);
+
+ if(temp_filename){
+ our_unlink(temp_filename);
+ fs_give((void **) &temp_filename);
+ }
+
+ return 0;
+
+
+io_error:
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ if(enable_intr_handling && we_turned_on)
+ intr_handling_off();
+
+#ifndef DOS
+ (void)signal(SIGHUP, save_sighup);
+#endif
+ if(interrupt_happened){
+ q_status_message(0, 1, 2, _("Interrupt! Reverting to previous version"));
+ display_message('x');
+ dprint((1, "adrbk_write(%s): Interrupt\n",
+ ab->filename ? ab->filename : "?"));
+ }
+ else
+ dprint((1, "adrbk_write(%s) (%s): some sort of io_error\n",
+ ab->filename ? ab->filename : "?",
+ ab->our_filecopy ? ab->our_filecopy : "?"));
+
+ writing = 0;
+
+ if(ab_stream){
+ fclose(ab_stream);
+ ab_stream = (FILE *)NULL;
+ }
+
+ if(temp_filename){
+ our_unlink(temp_filename);
+ fs_give((void **) &temp_filename);
+ }
+
+ adrbk_partial_close(ab);
+
+ if(interrupt_happened){
+ errno = EINTR; /* for nicer error message */
+ return -5;
+ }
+ else
+ return -2;
+}
+
+
+/*
+ * Writes one addrbook entry with wrapping. Fills in widths
+ * for display purposes. Returns 0, or EOF on error.
+ *
+ * Continuation lines always start with spaces. Tabs are treated as
+ * separators, never as whitespace. When we output tab separators we
+ * always put them on the ends of lines, never on the start of a line
+ * after a continuation. That is, there is always something printable
+ * after continuation spaces.
+ */
+int
+write_single_abook_entry(AdrBk_Entry *ae, FILE *fp, int *ret_nick_width,
+ int *ret_full_width, int *ret_addr_width, int *ret_fcc_width)
+{
+ int len = 0;
+ int nick_width = 0, full_width = 0, addr_width = 0, fcc_width = 0;
+ int tmplen, this_len;
+ char *write_this = NULL;
+
+ if(fp == (FILE *) NULL){
+ dprint((1, "write_single_abook_entry: fp is NULL\n"));
+ return(EOF);
+ }
+
+ if(ae->nickname){
+ nick_width = utf8_width(ae->nickname);
+
+ write_this = backcompat_encoding_for_abook(tmp_20k_buf, 10000,
+ tmp_20k_buf+10000, SIZEOF_20KBUF-10000, ae->nickname);
+
+ this_len = strlen(write_this ? write_this : "");
+
+ /*
+ * We aren't too concerned with where it wraps.
+ * Long lines are ok as long as they aren't super long.
+ */
+ if(len > 100 || (len+this_len > 150 && len > 20)){
+ if(fprintf(fp, "%s%s", NEWLINE, INDENTSTR) == EOF){
+ dprint((1,
+ "write_single_abook_entry: fputs ind1 failed\n"));
+ return(EOF);
+ }
+
+ len = this_len + INDENT;
+ }
+
+ len += this_len;
+
+ if(fputs(write_this, fp) == EOF){
+ dprint((1,
+ "write_single_abook_entry: fputs nick failed\n"));
+ return(EOF);
+ }
+ }
+ else
+ nick_width = 0;
+
+ putc(TAB, fp);
+ len++;
+
+ if(ae->fullname){
+ full_width = utf8_width(ae->fullname);
+
+ write_this = backcompat_encoding_for_abook(tmp_20k_buf, 10000,
+ tmp_20k_buf+10000, SIZEOF_20KBUF-10000, ae->fullname);
+
+ this_len = strlen(write_this ? write_this : "");
+
+
+ if(len > 100 || (len+this_len > 150 && len > 20)){
+ if(fprintf(fp, "%s%s", NEWLINE, INDENTSTR) == EOF){
+ dprint((1,
+ "write_single_abook_entry: fputs ind2 failed\n"));
+ return(EOF);
+ }
+
+ len = this_len + INDENT;
+ }
+
+ len += this_len;
+
+ if(fputs(write_this, fp) == EOF){
+ dprint((1,
+ "write_single_abook_entry: fputs full failed\n"));
+ return(EOF);
+ }
+ }
+ else
+ full_width = 0;
+
+ putc(TAB, fp);
+ len++;
+
+ /* special case, make sure empty list has () */
+ if(ae->tag == List && ae->addr.list == NULL){
+ addr_width = 0;
+ putc('(', fp);
+ putc(')', fp);
+ len += 2;
+ }
+ else if(ae->addr.addr != NULL || ae->addr.list != NULL){
+ if(ae->tag == Single){
+ /*----- Single: just one address ----*/
+ if(ae->addr.addr){
+ addr_width = utf8_width(ae->addr.addr);
+
+ write_this = backcompat_encoding_for_abook(tmp_20k_buf, 10000,
+ tmp_20k_buf+10000, SIZEOF_20KBUF-10000, ae->addr.addr);
+
+ this_len = strlen(write_this ? write_this : "");
+
+ if(len > 100 || (len+this_len > 150 && len > 20)){
+ if(fprintf(fp, "%s%s", NEWLINE, INDENTSTR) == EOF){
+ dprint((1,
+ "write_single_abook_entry: fputs ind3 failed\n"));
+ return(EOF);
+ }
+
+ len = this_len + INDENT;
+ }
+
+ len += this_len;
+
+ if(fputs(write_this, fp) == EOF){
+ dprint((1,
+ "write_single_abook_entry: fputs addr failed\n"));
+ return(EOF);
+ }
+ }
+ else
+ addr_width = 0;
+ }
+ else if(ae->tag == List){
+ register char **a2;
+
+ /*----- List: a distribution list ------*/
+ putc('(', fp);
+ len++;
+ addr_width = 0;
+ for(a2 = ae->addr.list; *a2 != NULL; a2++){
+ if(*a2){
+ if(a2 != ae->addr.list){
+ putc(',', fp);
+ len++;
+ }
+
+ addr_width = MAX(addr_width, utf8_width(*a2));
+
+ write_this = backcompat_encoding_for_abook(tmp_20k_buf, 10000,
+ tmp_20k_buf+10000, SIZEOF_20KBUF-10000, *a2);
+
+ this_len = strlen(write_this ? write_this : "");
+
+ if(len > 100 || (len+this_len > 150 && len > 20)){
+ if(fprintf(fp, "%s%s", NEWLINE, INDENTSTR) == EOF){
+ dprint((1,
+ "write_single_abook_entry: fputs ind3 failed\n"));
+ return(EOF);
+ }
+
+ len = this_len + INDENT;
+ }
+
+ len += this_len;
+
+ if(fputs(write_this, fp) == EOF){
+ dprint((1,
+ "write_single_abook_entry: fputs addrl failed\n"));
+ return(EOF);
+ }
+ }
+ }
+
+ putc(')', fp);
+ len++;
+ }
+ }
+
+ /* If either fcc or extra exists, output both, otherwise, neither */
+ if((ae->fcc && ae->fcc[0]) || (ae->extra && ae->extra[0])){
+ putc(TAB, fp);
+ len++;
+
+ if(ae->fcc && ae->fcc[0]){
+ fcc_width = utf8_width(ae->fcc);
+
+ write_this = backcompat_encoding_for_abook(tmp_20k_buf, 10000,
+ tmp_20k_buf+10000, SIZEOF_20KBUF-10000, ae->fcc);
+
+ this_len = strlen(write_this ? write_this : "");
+
+ if(len > 100 || (len+this_len > 150 && len > 20)){
+ if(fprintf(fp, "%s%s", NEWLINE, INDENTSTR) == EOF){
+ dprint((1,
+ "write_single_abook_entry: fputs ind4 failed\n"));
+ return(EOF);
+ }
+
+ len = this_len + INDENT;
+ }
+
+ len += this_len;
+
+ if(fputs(write_this, fp) == EOF){
+ dprint((1,
+ "write_single_abook_entry: fputs fcc failed\n"));
+ return(EOF);
+ }
+ }
+ else
+ fcc_width = 0;
+
+ putc(TAB, fp);
+ len++;
+
+ if(ae->extra && ae->extra[0]){
+ int space;
+ char *cur, *end;
+ char *extra_copy;
+
+ write_this = backcompat_encoding_for_abook(tmp_20k_buf, 10000,
+ tmp_20k_buf+10000, SIZEOF_20KBUF-10000, ae->extra);
+
+ /*
+ * Copy ae->extra and replace newlines with spaces.
+ * The reason we do this is because the continuation lines
+ * produced by rfc1522_encode may interact with the
+ * continuation lines produced below in a bad way.
+ * In particular, what can happen is that the 1522 continuation
+ * newline can be followed immediately by a newline produced
+ * below. That breaks the continuation since that is a
+ * line with nothing on it. Just turn 1522 continuations into
+ * spaces, which work fine with 1522_decode.
+ */
+ extra_copy = cpystr(write_this);
+ REPLACE_NEWLINES_WITH_SPACE(extra_copy);
+
+ tmplen = strlen(extra_copy);
+
+ if(len > 100 || (len+tmplen > 150 && len > 20)){
+ if(fprintf(fp, "%s%s", NEWLINE, INDENTSTR) == EOF){
+ dprint((1,
+ "write_single_abook_entry: fprintf indent failed\n"));
+ return(EOF);
+ }
+
+ len = INDENT;
+ }
+
+ space = MAX(70 - len, 5);
+ cur = extra_copy;
+ end = cur + tmplen;
+ while(cur < end){
+ if(end-cur > space){
+ int i;
+
+ /* find first space after spot we want to break */
+ for(i = space; cur+i < end && cur[i] != SPACE; i++)
+ ;
+
+ cur[i] = '\0';
+ if(fputs(cur, fp) == EOF){
+ dprint((1,
+ "write_single_abook_entry: fputs extra failed\n"));
+ return(EOF);
+ }
+
+ cur += (i+1);
+
+ if(cur < end){
+ if(fprintf(fp, "%s%s", NEWLINE, INDENTXTRA) == EOF){
+ dprint((1,
+ "write_single_abook_entry: fprintf indent failed\n"));
+ return(EOF);
+ }
+
+ space = 70 - INDENT;
+ }
+ }
+ else{
+ if(fputs(cur, fp) == EOF){
+ dprint((1,
+ "write_single_abook_entry: fputs extra failed\n"));
+ return(EOF);
+ }
+
+ cur = end;
+ }
+ }
+
+ if(extra_copy)
+ fs_give((void **)&extra_copy);
+ }
+ }
+ else
+ fcc_width = 0;
+
+ fprintf(fp, "%s", NEWLINE);
+
+ if(ret_nick_width)
+ *ret_nick_width = nick_width;
+ if(ret_full_width)
+ *ret_full_width = full_width;
+ if(ret_addr_width)
+ *ret_addr_width = addr_width;
+ if(ret_fcc_width)
+ *ret_fcc_width = fcc_width;
+
+ return(0);
+}
+
+
+char *
+backcompat_encoding_for_abook(char *buf1, size_t buf1len, char *buf2,
+ size_t buf2len, char *srcstr)
+{
+ char *encoded = NULL;
+ char *p;
+ int its_ascii = 1;
+
+
+ for(p = srcstr; *p && its_ascii; p++)
+ if(*p & 0x80)
+ its_ascii = 0;
+
+ /* if it is ascii, go with that */
+ if(its_ascii)
+ encoded = srcstr;
+ else{
+ char *trythischarset = NULL;
+
+ /*
+ * If it is possible to translate the UTF-8
+ * string into the user's character set then
+ * do that. For backwards compatibility with
+ * old pines.
+ */
+ if(ps_global->keyboard_charmap && ps_global->keyboard_charmap[0])
+ trythischarset = ps_global->keyboard_charmap;
+ else if(ps_global->display_charmap && ps_global->display_charmap[0])
+ trythischarset = ps_global->display_charmap;
+
+ if(trythischarset){
+ SIZEDTEXT src, dst;
+
+ src.data = (unsigned char *) srcstr;
+ src.size = strlen(srcstr);
+ memset(&dst, 0, sizeof(dst));
+ if(utf8_cstext(&src, trythischarset, &dst, 0)){
+ if(dst.data){
+ strncpy(buf1, (char *) dst.data, buf1len);
+ buf1[buf1len-1] = '\0';
+ fs_give((void **) &dst.data);
+ encoded = rfc1522_encode(buf2, buf2len,
+ (unsigned char *) buf1, trythischarset);
+ if(encoded)
+ REPLACE_NEWLINES_WITH_SPACE(encoded);
+ }
+ }
+ }
+
+ if(!encoded){
+ encoded = rfc1522_encode(buf1, buf1len, (unsigned char *) srcstr, "UTF-8");
+ if(encoded)
+ REPLACE_NEWLINES_WITH_SPACE(encoded);
+ }
+ }
+
+ return(encoded);
+}
+
+
+int
+percent_abook_saved(void)
+{
+ return((int)(((unsigned long)entry_num_for_percent * (unsigned long)100) /
+ (unsigned long)tot_for_percent));
+}
+
+
+/*
+ * Free memory associated with entry ae.
+ *
+ * Args: ae -- Address book entry to be freed.
+ */
+void
+free_ae(AdrBk_Entry **ae)
+{
+ if(!ae || !(*ae))
+ return;
+
+ free_ae_parts(*ae);
+ fs_give((void **) ae);
+}
+
+
+/*
+ * Free memory associated with entry ae but not ae itself.
+ */
+void
+free_ae_parts(AdrBk_Entry *ae)
+{
+ char **p;
+
+ if(!ae)
+ return;
+
+ if(ae->nickname && ae->nickname != empty)
+ fs_give((void **) &ae->nickname);
+
+ if(ae->fullname && ae->fullname != empty)
+ fs_give((void **) &ae->fullname);
+
+ if(ae->tag == Single){
+ if(ae->addr.addr && ae->addr.addr != empty)
+ fs_give((void **) &ae->addr.addr);
+ }
+ else if(ae->tag == List){
+ if(ae->addr.list){
+ for(p = ae->addr.list; *p; p++)
+ if(*p != empty)
+ fs_give((void **) p);
+
+ fs_give((void **) &ae->addr.list);
+ }
+ }
+
+ if(ae->fcc && ae->fcc != empty)
+ fs_give((void **) &ae->fcc);
+
+ if(ae->extra && ae->extra != empty)
+ fs_give((void **) &ae->extra);
+
+ defvalue_ae(ae);
+}
+
+
+/*
+ * Inserts element new_ae before element put_it_before_this.
+ */
+void
+insert_ab_entry(AdrBk *ab, a_c_arg_t put_it_before_this, AdrBk_Entry *new_ae,
+ int use_deleted_list)
+{
+ adrbk_cntr_t before, old_count, new_count;
+ AdrBk_Entry *ae_before, *ae_before_next;
+
+ dprint((7, "- insert_ab_entry(before_this=%ld) -\n", (long) put_it_before_this));
+
+ before = (adrbk_cntr_t) put_it_before_this;
+
+ if(!ab || before == NO_NEXT || (!use_deleted_list && before > ab->count)
+ || (use_deleted_list && before > ab->del_count)){
+ ;
+ }
+ else{
+ /*
+ * add space for new entry to array
+ * slide entries [before ... old_count-1] down one
+ * put new_ae into opened up slot
+ */
+ old_count = use_deleted_list ? ab->del_count : ab->count;
+ new_count = old_count + 1;
+ if(old_count == 0){
+ if(use_deleted_list){
+ if(ab->del) /* shouldn't happen */
+ fs_give((void **) &ab->del);
+
+ /* first entry in new array */
+ ab->del = (AdrBk_Entry *) fs_get(new_count * sizeof(AdrBk_Entry));
+ defvalue_ae(ab->del);
+ }
+ else{
+ if(ab->arr) /* shouldn't happen */
+ fs_give((void **) &ab->arr);
+
+ /* first entry in new array */
+ ab->arr = (AdrBk_Entry *) fs_get(new_count * sizeof(AdrBk_Entry));
+ defvalue_ae(ab->arr);
+ }
+ }
+ else{
+ if(use_deleted_list){
+ fs_resize((void **) &ab->del, new_count * sizeof(AdrBk_Entry));
+ defvalue_ae(&ab->del[new_count-1]);
+ }
+ else{
+ fs_resize((void **) &ab->arr, new_count * sizeof(AdrBk_Entry));
+ defvalue_ae(&ab->arr[new_count-1]);
+ }
+ }
+
+ if(use_deleted_list){
+ ab->del_count = new_count;
+
+ ae_before = adrbk_get_delae(ab, (a_c_arg_t) before);
+ if(ae_before){
+ if(before < old_count){
+ ae_before_next = adrbk_get_delae(ab, (a_c_arg_t) (before+1));
+ memmove(ae_before_next, ae_before,
+ (old_count-before) * sizeof(AdrBk_Entry));
+ }
+
+ memcpy(ae_before, new_ae, sizeof(AdrBk_Entry));
+ }
+ }
+ else{
+ ab->count = new_count;
+
+ ae_before = adrbk_get_ae(ab, (a_c_arg_t) before);
+ if(ae_before){
+ if(before < old_count){
+ ae_before_next = adrbk_get_ae(ab, (a_c_arg_t) (before+1));
+ memmove(ae_before_next, ae_before,
+ (old_count-before) * sizeof(AdrBk_Entry));
+ }
+
+ memcpy(ae_before, new_ae, sizeof(AdrBk_Entry));
+ }
+ }
+
+ }
+
+ if(!use_deleted_list)
+ repair_abook_tries(ab);
+}
+
+
+/*
+ * Moves element move_this_one before element put_it_before_this.
+ */
+void
+move_ab_entry(AdrBk *ab, a_c_arg_t move_this_one, a_c_arg_t put_it_before_this)
+{
+ adrbk_cntr_t m, before;
+ AdrBk_Entry ae_tmp;
+ AdrBk_Entry *ae_m, *ae_m_next, *ae_before, *ae_before_prev, *ae_before_next;
+
+ dprint((7, "- move_ab_entry(move_this=%ld,before_this=%ld) -\n", (long) move_this_one, (long) put_it_before_this));
+
+ m = (adrbk_cntr_t) move_this_one;
+ before = (adrbk_cntr_t) put_it_before_this;
+
+ if(!ab || m == NO_NEXT || before == NO_NEXT
+ || m == before || m+1 == before || before > ab->count){
+ ;
+ }
+ else if(m+1 < before){
+ /*
+ * copy m
+ * slide entries [m+1 ... before-1] up one ("up" means smaller indices)
+ * put m into opened up slot
+ */
+ ae_m = adrbk_get_ae(ab, (a_c_arg_t) m);
+ ae_m_next = adrbk_get_ae(ab, (a_c_arg_t) (m+1));
+ ae_before_prev = adrbk_get_ae(ab, (a_c_arg_t) (before-1));
+ if(ae_m && ae_m_next && ae_before_prev){
+ memcpy(&ae_tmp, ae_m, sizeof(ae_tmp));
+ memmove(ae_m, ae_m_next, (before-m-1) * sizeof(ae_tmp));
+ memcpy(ae_before_prev, &ae_tmp, sizeof(ae_tmp));
+ }
+ }
+ else if(m > before){
+ /*
+ * copy m
+ * slide entries [before ... m-1] down one
+ * put m into opened up slot
+ */
+ ae_m = adrbk_get_ae(ab, (a_c_arg_t) m);
+ ae_before = adrbk_get_ae(ab, (a_c_arg_t) before);
+ ae_before_next = adrbk_get_ae(ab, (a_c_arg_t) (before+1));
+ if(ae_m && ae_before && ae_before_next){
+ memcpy(&ae_tmp, ae_m, sizeof(ae_tmp));
+ memmove(ae_before_next, ae_before, (m-before) * sizeof(ae_tmp));
+ memcpy(ae_before, &ae_tmp, sizeof(ae_tmp));
+ }
+ }
+
+ dprint((9, "- move_ab_entry: done -\n"));
+
+ repair_abook_tries(ab);
+}
+
+
+/*
+ * Deletes element delete_this_one from in-core data structure.
+ * If save_it is set the deleted entry is moved to the deleted_list.
+ */
+void
+delete_ab_entry(AdrBk *ab, a_c_arg_t delete_this_one, int save_it)
+{
+ adrbk_cntr_t d;
+ AdrBk_Entry *ae_deleted, *ae_deleted_next;
+
+ dprint((7, "- delete_ab_entry(delete_this=%ld,save_it=%d) -\n", (long) delete_this_one, save_it));
+
+ d = (adrbk_cntr_t) delete_this_one;
+
+ if(!ab || d == NO_NEXT || d >= ab->count){
+ ;
+ }
+ else{
+ /*
+ * Move the entry to the deleted_list if asked to.
+ */
+ ae_deleted = adrbk_get_ae(ab, (a_c_arg_t) d);
+ if(ae_deleted){
+ if(save_it){
+ char *oldnick, *newnick;
+ size_t len;
+ struct tm *tm_now;
+ time_t now;
+
+ /*
+ * First prepend the prefix
+ * #DELETED-YY/MM/DD#
+ * to the nickname.
+ */
+ now = time((time_t) 0);
+ tm_now = localtime(&now);
+
+ oldnick = ae_deleted->nickname;
+ len = strlen(oldnick) + DELETED_LEN + strlen("YY/MM/DD#");
+
+ newnick = (char *) fs_get((len+1) * sizeof(char));
+ snprintf(newnick, len+1, "%s%02d/%02d/%02d#%s",
+ DELETED, (tm_now->tm_year)%100, tm_now->tm_mon+1,
+ tm_now->tm_mday, oldnick ? oldnick : "");
+ newnick[len] = '\0';
+
+ if(ae_deleted->nickname && ae_deleted->nickname != empty)
+ fs_give((void **) &ae_deleted->nickname);
+
+ ae_deleted->nickname = newnick;
+
+ /*
+ * Now insert this entry in the deleted_list.
+ */
+ insert_ab_entry(ab, (a_c_arg_t) ab->del_count, ae_deleted, 1);
+ }
+ else
+ free_ae_parts(ae_deleted);
+ }
+
+ /*
+ * slide entries [deleted+1 ... count-1] up one
+ */
+ if(d+1 < ab->count){
+ ae_deleted = adrbk_get_ae(ab, (a_c_arg_t) d);
+ ae_deleted_next = adrbk_get_ae(ab, (a_c_arg_t) (d+1));
+ if(ae_deleted && ae_deleted_next)
+ memmove(ae_deleted, ae_deleted_next, (ab->count-d-1) * sizeof(AdrBk_Entry));
+ }
+
+ ab->count--;
+
+ if(ab->count > 0)
+ fs_resize((void **) &ab->arr, ab->count * sizeof(AdrBk_Entry));
+ else
+ fs_give((void **) &ab->arr);
+ }
+
+ repair_abook_tries(ab);
+}
+
+
+/*
+ * We may want to be smarter about this and repair instead of
+ * rebuild these.
+ */
+void
+repair_abook_tries(AdrBk *ab)
+{
+ if(ab->arr){
+ AdrBk_Trie *save_nick_trie, *save_addr_trie,
+ *save_full_trie, *save_revfull_trie;
+
+ save_nick_trie = ab->nick_trie;
+ ab->nick_trie = NULL;
+ save_addr_trie = ab->addr_trie;
+ ab->addr_trie = NULL;
+ save_full_trie = ab->full_trie;
+ ab->full_trie = NULL;
+ save_revfull_trie = ab->revfull_trie;
+ ab->revfull_trie = NULL;
+ if(build_abook_tries(ab, NULL)){
+ dprint((2, "trouble rebuilding tries, restoring\n"));
+ if(ab->nick_trie)
+ free_abook_trie(&ab->nick_trie);
+
+ /* better than nothing */
+ ab->nick_trie = save_nick_trie;
+
+ if(ab->addr_trie)
+ free_abook_trie(&ab->addr_trie);
+
+ ab->addr_trie = save_addr_trie;
+
+ if(ab->full_trie)
+ free_abook_trie(&ab->full_trie);
+
+ ab->full_trie = save_full_trie;
+
+ if(ab->revfull_trie)
+ free_abook_trie(&ab->revfull_trie);
+
+ ab->revfull_trie = save_revfull_trie;
+ }
+ else{
+ if(save_nick_trie)
+ free_abook_trie(&save_nick_trie);
+
+ if(save_addr_trie)
+ free_abook_trie(&save_addr_trie);
+
+ if(save_full_trie)
+ free_abook_trie(&save_full_trie);
+
+ if(save_revfull_trie)
+ free_abook_trie(&save_revfull_trie);
+ }
+ }
+}
+
+
+/*
+ * Free the list of distribution lists which have been expanded.
+ * Leaves the head of the list alone.
+ *
+ * Args: exp_head -- Head of the expanded list.
+ */
+void
+exp_free(EXPANDED_S *exp_head)
+{
+ EXPANDED_S *e, *the_next_one;
+
+ e = exp_head ? exp_head->next : NULL;
+
+ if(!e)
+ return;
+
+ while(e){
+ the_next_one = e->next;
+ fs_give((void **)&e);
+ e = the_next_one;
+ }
+
+ exp_head->next = (EXPANDED_S *)NULL;
+}
+
+
+/*
+ * Is entry n expanded?
+ *
+ * Args: exp_head -- Head of the expanded list.
+ * n -- The entry num to check
+ */
+int
+exp_is_expanded(EXPANDED_S *exp_head, a_c_arg_t n)
+{
+ register EXPANDED_S *e;
+ adrbk_cntr_t nn;
+
+ nn = (adrbk_cntr_t)n;
+
+ e = exp_head ? exp_head->next : NULL;
+
+ /*
+ * The list is kept ordered, so we search until we find it or are
+ * past it.
+ */
+ while(e){
+ if(e->ent >= nn)
+ break;
+
+ e = e->next;
+ }
+
+ return(e && e->ent == nn);
+}
+
+
+/*
+ * How many entries expanded in this addrbook.
+ *
+ * Args: exp_head -- Head of the expanded list.
+ */
+int
+exp_howmany_expanded(EXPANDED_S *exp_head)
+{
+ register EXPANDED_S *e;
+ int cnt = 0;
+
+ e = exp_head ? exp_head->next : NULL;
+
+ while(e){
+ cnt++;
+ e = e->next;
+ }
+
+ return(cnt);
+}
+
+
+/*
+ * Are any entries expanded?
+ *
+ * Args: exp_head -- Head of the expanded list.
+ */
+int
+exp_any_expanded(EXPANDED_S *exp_head)
+{
+ return(exp_head && exp_head->next != NULL);
+}
+
+
+/*
+ * Return next entry num in list.
+ *
+ * Args: cur -- Current position in the list.
+ *
+ * Result: Returns the number of the next entry, or NO_NEXT if there is
+ * no next entry. As a side effect, the cur pointer is incremented.
+ */
+adrbk_cntr_t
+exp_get_next(EXPANDED_S **cur)
+{
+ adrbk_cntr_t ret = NO_NEXT;
+
+ if(cur && *cur && (*cur)->next){
+ ret = (*cur)->next->ent;
+ *cur = (*cur)->next;
+ }
+
+ return(ret);
+}
+
+
+/*
+ * Mark entry n as being expanded.
+ *
+ * Args: exp_head -- Head of the expanded list.
+ * n -- The entry num to mark
+ */
+void
+exp_set_expanded(EXPANDED_S *exp_head, a_c_arg_t n)
+{
+ register EXPANDED_S *e;
+ EXPANDED_S *new;
+ adrbk_cntr_t nn;
+
+ nn = (adrbk_cntr_t)n;
+ if(!exp_head)
+ panic("exp_head not set in exp_set_expanded");
+
+ for(e = exp_head; e->next; e = e->next)
+ if(e->next->ent >= nn)
+ break;
+
+ if(e->next && e->next->ent == nn) /* already there */
+ return;
+
+ /* add new after e */
+ new = (EXPANDED_S *)fs_get(sizeof(EXPANDED_S));
+ new->ent = nn;
+ new->next = e->next;
+ e->next = new;
+}
+
+
+/*
+ * Mark entry n as being *not* expanded.
+ *
+ * Args: exp_head -- Head of the expanded list.
+ * n -- The entry num to mark
+ */
+void
+exp_unset_expanded(EXPANDED_S *exp_head, a_c_arg_t n)
+{
+ register EXPANDED_S *e;
+ EXPANDED_S *delete_this_one = NULL;
+ adrbk_cntr_t nn;
+
+ nn = (adrbk_cntr_t)n;
+ if(!exp_head)
+ panic("exp_head not set in exp_unset_expanded");
+
+ for(e = exp_head; e->next; e = e->next)
+ if(e->next->ent >= nn)
+ break;
+
+ if(e->next && e->next->ent == nn){
+ delete_this_one = e->next;
+ e->next = e->next->next;
+ }
+
+ if(delete_this_one)
+ fs_give((void **)&delete_this_one);
+}
+
+
+/*
+ * Adjust the "expanded" list to correspond to addrbook entry n being
+ * deleted.
+ *
+ * Args: exp_head -- Head of the expanded list.
+ * n -- The entry num being deleted
+ */
+void
+exp_del_nth(EXPANDED_S *exp_head, a_c_arg_t n)
+{
+ register EXPANDED_S *e;
+ int delete_when_done = 0;
+ adrbk_cntr_t nn;
+
+ nn = (adrbk_cntr_t)n;
+ if(!exp_head)
+ panic("exp_head not set in exp_del_nth");
+
+ e = exp_head->next;
+ while(e && e->ent < nn)
+ e = e->next;
+
+ if(e){
+ if(e->ent == nn){
+ delete_when_done++;
+ e = e->next;
+ }
+
+ while(e){
+ e->ent--; /* adjust entry nums */
+ e = e->next;
+ }
+
+ if(delete_when_done)
+ exp_unset_expanded(exp_head, n);
+ }
+}
+
+
+/*
+ * Adjust the "expanded" list to correspond to a new addrbook entry being
+ * added between current entries n-1 and n.
+ *
+ * Args: exp_head -- Head of the expanded list.
+ * n -- The entry num being added
+ *
+ * The new entry is not marked expanded.
+ */
+void
+exp_add_nth(EXPANDED_S *exp_head, a_c_arg_t n)
+{
+ register EXPANDED_S *e;
+ adrbk_cntr_t nn;
+
+ nn = (adrbk_cntr_t)n;
+ if(!exp_head)
+ panic("exp_head not set in exp_add_nth");
+
+ e = exp_head->next;
+ while(e && e->ent < nn)
+ e = e->next;
+
+ while(e){
+ e->ent++; /* adjust entry nums */
+ e = e->next;
+ }
+}
+
+
+static AdrBk *ab_for_sort;
+
+/*
+ * Compare two address book entries. Args are AdrBk_Entry **'s.
+ * Sorts lists after simple addresses and then sorts on Fullname field.
+ */
+int
+cmp_ae_by_full_lists_last(const qsort_t *a, const qsort_t *b)
+{
+ AdrBk_Entry **x = (AdrBk_Entry **)a,
+ **y = (AdrBk_Entry **)b;
+ int result;
+
+ if((*x)->tag == List && (*y)->tag == Single)
+ result = 1;
+ else if((*x)->tag == Single && (*y)->tag == List)
+ result = -1;
+ else{
+ register char *p, *q, *r, *s;
+
+ p = (*x)->fullname;
+ if(*p == '"' && *(p+1))
+ p++;
+
+ q = (*y)->fullname;
+ if(*q == '"' && *(q+1))
+ q++;
+
+ r = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, p);
+
+ s = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf+10000,
+ SIZEOF_20KBUF-10000, q);
+
+ result = (*pcollator)(r, s);
+ if(result == 0)
+ result = (*pcollator)((*x)->nickname, (*y)->nickname);
+ }
+
+ return(result);
+}
+
+
+/*
+ * Compare two address book entries. Args are adrbk_cntr_t *'s (element #'s).
+ * Sorts lists after simple addresses and then sorts on Fullname field.
+ */
+int
+cmp_cntr_by_full_lists_last(const qsort_t *a, const qsort_t *b)
+{
+ adrbk_cntr_t *x = (adrbk_cntr_t *)a, /* *x is an element_number */
+ *y = (adrbk_cntr_t *)b;
+ AdrBk_Entry *x_ae,
+ *y_ae;
+
+ if(ps_global->intr_pending)
+ longjmp(jump_over_qsort, 1);
+
+ ALARM_BLIP();
+
+ x_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*x));
+ y_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*y));
+
+ return(cmp_ae_by_full_lists_last((const qsort_t *) &x_ae,
+ (const qsort_t *) &y_ae));
+}
+
+
+/*
+ * Compare two address book entries. Args are AdrBk_Entry **'s.
+ * Sorts on Fullname field.
+ */
+int
+cmp_ae_by_full(const qsort_t *a, const qsort_t *b)
+{
+ AdrBk_Entry **x = (AdrBk_Entry **)a,
+ **y = (AdrBk_Entry **)b;
+ int result;
+ register char *p, *q, *r, *s;
+
+ p = (*x)->fullname;
+ if(*p == '"' && *(p+1))
+ p++;
+
+ q = (*y)->fullname;
+ if(*q == '"' && *(q+1))
+ q++;
+
+ r = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, p);
+ s = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf+10000,
+ SIZEOF_20KBUF-10000, q);
+ result = (*pcollator)(r, s);
+ if(result == 0)
+ result = (*pcollator)((*x)->nickname, (*y)->nickname);
+
+ return(result);
+}
+
+
+/*
+ * Compare two address book entries. Args are adrbk_cntr_t *'s (element #'s).
+ * Sorts on Fullname field.
+ */
+int
+cmp_cntr_by_full(const qsort_t *a, const qsort_t *b)
+{
+ adrbk_cntr_t *x = (adrbk_cntr_t *)a, /* *x is an element_number */
+ *y = (adrbk_cntr_t *)b;
+ AdrBk_Entry *x_ae,
+ *y_ae;
+
+ if(ps_global->intr_pending)
+ longjmp(jump_over_qsort, 1);
+
+ ALARM_BLIP();
+
+ x_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*x));
+ y_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*y));
+
+ return(cmp_ae_by_full((const qsort_t *) &x_ae, (const qsort_t *) &y_ae));
+}
+
+
+/*
+ * Compare two address book entries. Args are AdrBk_Entry **'s.
+ * Sorts lists after simple addresses and then sorts on Nickname field.
+ */
+int
+cmp_ae_by_nick_lists_last(const qsort_t *a, const qsort_t *b)
+{
+ AdrBk_Entry **x = (AdrBk_Entry **)a,
+ **y = (AdrBk_Entry **)b;
+ int result;
+
+ if((*x)->tag == List && (*y)->tag == Single)
+ result = 1;
+ else if((*x)->tag == Single && (*y)->tag == List)
+ result = -1;
+ else
+ result = (*pcollator)((*x)->nickname, (*y)->nickname);
+
+ return(result);
+}
+
+
+/*
+ * Compare two address book entries. Args are adrbk_cntr_t *'s (element #'s).
+ * Sorts lists after simple addresses and then sorts on Nickname field.
+ */
+int
+cmp_cntr_by_nick_lists_last(const qsort_t *a, const qsort_t *b)
+{
+ adrbk_cntr_t *x = (adrbk_cntr_t *)a, /* *x is an element_number */
+ *y = (adrbk_cntr_t *)b;
+ AdrBk_Entry *x_ae,
+ *y_ae;
+
+ if(ps_global->intr_pending)
+ longjmp(jump_over_qsort, 1);
+
+ ALARM_BLIP();
+
+ x_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*x));
+ y_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*y));
+
+ return(cmp_ae_by_nick_lists_last((const qsort_t *) &x_ae,
+ (const qsort_t *) &y_ae));
+}
+
+
+/*
+ * Compare two address book entries. Args are AdrBk_Entry **'s.
+ * Sorts on Nickname field.
+ */
+int
+cmp_ae_by_nick(const qsort_t *a, const qsort_t *b)
+{
+ AdrBk_Entry **x = (AdrBk_Entry **)a,
+ **y = (AdrBk_Entry **)b;
+
+ return((*pcollator)((*x)->nickname, (*y)->nickname));
+}
+
+
+/*
+ * Compare two address book entries. Args are adrbk_cntr_t *'s (element #'s).
+ * Sorts on Nickname field.
+ */
+int
+cmp_cntr_by_nick(const qsort_t *a, const qsort_t *b)
+{
+ adrbk_cntr_t *x = (adrbk_cntr_t *)a, /* *x is an element_number */
+ *y = (adrbk_cntr_t *)b;
+ AdrBk_Entry *x_ae,
+ *y_ae;
+
+ if(ps_global->intr_pending)
+ longjmp(jump_over_qsort, 1);
+
+ ALARM_BLIP();
+
+ x_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*x));
+ y_ae = adrbk_get_ae(ab_for_sort, (a_c_arg_t)(*y));
+
+ return(cmp_ae_by_nick((const qsort_t *) &x_ae, (const qsort_t *) &y_ae));
+}
+
+
+/*
+ * For sorting a simple list of pointers to addresses (skip initial quotes)
+ */
+int
+cmp_addr(const qsort_t *a1, const qsort_t *a2)
+{
+ char *x = *(char **)a1, *y = *(char **)a2;
+ char *r, *s;
+
+ if(x && *x == '"')
+ x++;
+
+ if(y && *y == '"')
+ y++;
+
+ r = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, x);
+ s = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf+10000,
+ SIZEOF_20KBUF-10000, y);
+ return((*pcollator)(r, s));
+}
+
+
+/*
+ * Sort an array of strings, except skip initial quotes.
+ */
+void
+sort_addr_list(char **list)
+{
+ register char **p;
+
+ /* find size of list */
+ for(p = list; *p != NULL; p++)
+ ;/* do nothing */
+
+ qsort((qsort_t *)list, (size_t)(p - list), sizeof(char *), cmp_addr);
+}
+
+
+/*
+ * Sort this address book.
+ *
+ * Args: ab -- address book to sort
+ * current_entry_num -- see next description
+ * new_entry_num -- return new entry_num of current_entry_num here
+ *
+ * Result: return code: 0 all went well
+ * -2 error writing address book, check errno
+ *
+ * The sorting strategy is to allocate an array of length ab->count which
+ * contains the element numbers 0, 1, ..., ab->count - 1, representing the
+ * entries in the addrbook, of course. Sort the array, then use that
+ * result to swap ae's in the in-core addrbook array before writing it out.
+ */
+int
+adrbk_sort(AdrBk *ab, a_c_arg_t current_entry_num, adrbk_cntr_t *new_entry_num, int be_quiet)
+{
+ adrbk_cntr_t *sort_array, *inv, tmp;
+ long i, j, hi, count;
+ int skip_the_sort = 0, we_cancel = 0, we_turned_on = 0;
+ AdrBk_Entry ae_tmp, *ae_i, *ae_hi;
+ EXPANDED_S *e, *e2, *smallest;
+
+ dprint((5, "- adrbk_sort -\n"));
+
+ count = (long) (ab->count);
+
+ if(!ab)
+ return -2;
+
+ if(ab->sort_rule == AB_SORT_RULE_NONE)
+ return 0;
+
+ if(count < 2)
+ return 0;
+
+ sort_array = (adrbk_cntr_t *) fs_get(count * sizeof(adrbk_cntr_t));
+ inv = (adrbk_cntr_t *) fs_get(count * sizeof(adrbk_cntr_t));
+
+ for(i = 0L; i < count; i++)
+ sort_array[i] = (adrbk_cntr_t) i;
+
+ ab_for_sort = ab;
+
+ if(setjmp(jump_over_qsort))
+ skip_the_sort = 1;
+
+ if(!skip_the_sort){
+ we_turned_on = intr_handling_on();
+ if(!be_quiet)
+ we_cancel = busy_cue(_("Sorting address book"), NULL, 0);
+
+ qsort((qsort_t *)sort_array,
+ (size_t)count,
+ sizeof(adrbk_cntr_t),
+ (ab->sort_rule == AB_SORT_RULE_FULL_LISTS) ?
+ cmp_cntr_by_full_lists_last :
+ (ab->sort_rule == AB_SORT_RULE_FULL) ?
+ cmp_cntr_by_full :
+ (ab->sort_rule == AB_SORT_RULE_NICK_LISTS) ?
+ cmp_cntr_by_nick_lists_last :
+ /* (ab->sort_rule == AB_SORT_RULE_NICK) */
+ cmp_cntr_by_nick);
+ }
+
+ dprint((9, "- adrbk_sort: done with first sort -\n"));
+
+ if(we_turned_on)
+ intr_handling_off();
+
+ if(skip_the_sort){
+ q_status_message(SM_ORDER, 3, 3,
+ _("Address book sort cancelled, using old order for now"));
+ goto skip_the_write_too;
+ }
+
+ dprint((5, "- adrbk_sort (%s)\n",
+ ab->sort_rule==AB_SORT_RULE_FULL_LISTS ? "FullListsLast" :
+ ab->sort_rule==AB_SORT_RULE_FULL ? "Fullname" :
+ ab->sort_rule==AB_SORT_RULE_NICK_LISTS ? "NickListLast" :
+ ab->sort_rule==AB_SORT_RULE_NICK ? "Nickname" : "unknown"));
+
+ /*
+ * Rearrange the in-core array of ae's to be in the new order.
+ * We can do that by sorting the inverse into the correct order in parallel.
+ */
+ for(i = 0L; i < count; i++)
+ inv[sort_array[i]] = i;
+
+ if(new_entry_num && (adrbk_cntr_t) current_entry_num >= 0
+ && (adrbk_cntr_t) current_entry_num < count){
+ *new_entry_num = inv[(adrbk_cntr_t) current_entry_num];
+ }
+
+ /*
+ * The expanded and selected lists will be wrong now. Correct them.
+ * First the expanded list.
+ */
+ e = ab->exp ? ab->exp->next : NULL;
+ while(e){
+ if(e->ent >= 0 && e->ent < count)
+ e->ent = inv[e->ent];
+
+ e = e->next;
+ }
+
+ /*
+ * And sort into ascending order as expected by the exp_ routines.
+ */
+ e = ab->exp ? ab->exp->next : NULL;
+ while(e){
+ /* move smallest to e */
+ e2 = e;
+ smallest = e;
+ while(e2){
+ if(e2->ent != NO_NEXT && e2->ent >= 0 && e2->ent < count && e2->ent < smallest->ent)
+ smallest = e2;
+
+ e2 = e2->next;
+ }
+
+ /* swap values in e and smallest */
+ if(e != smallest){
+ tmp = e->ent;
+ e->ent = smallest->ent;
+ smallest->ent = tmp;
+ }
+
+ e = e->next;
+ }
+
+ /*
+ * Same thing for the selected list.
+ */
+ e = ab->selects ? ab->selects->next : NULL;
+ while(e){
+ if(e->ent >= 0 && e->ent < count)
+ e->ent = inv[e->ent];
+
+ e = e->next;
+ }
+
+ e = ab->selects ? ab->selects->next : NULL;
+ while(e){
+ /* move smallest to e */
+ e2 = e;
+ smallest = e;
+ while(e2){
+ if(e2->ent != NO_NEXT && e2->ent >= 0 && e2->ent < count && e2->ent < smallest->ent)
+ smallest = e2;
+
+ e2 = e2->next;
+ }
+
+ /* swap values in e and smallest */
+ if(e != smallest){
+ tmp = e->ent;
+ e->ent = smallest->ent;
+ smallest->ent = tmp;
+ }
+
+ e = e->next;
+ }
+
+ for(i = 0L; i < count; i++){
+ if(i != inv[i]){
+ /* find inv[j] which = i */
+ for(j = i+1; j < count; j++){
+ if(i == inv[j]){
+ hi = j;
+ break;
+ }
+ }
+
+ /* swap i and hi */
+ ae_i = adrbk_get_ae(ab, (a_c_arg_t) i);
+ ae_hi = adrbk_get_ae(ab, (a_c_arg_t) hi);
+ if(ae_i && ae_hi){
+ memcpy(&ae_tmp, ae_i, sizeof(ae_tmp));
+ memcpy(ae_i, ae_hi, sizeof(ae_tmp));
+ memcpy(ae_hi, &ae_tmp, sizeof(ae_tmp));
+ }
+ /* else can't happen */
+
+ inv[hi] = inv[i];
+ }
+ }
+
+ if(we_cancel)
+ cancel_busy_cue(0);
+
+ repair_abook_tries(ab);
+
+ dprint((9, "- adrbk_sort: done with rearranging -\n"));
+
+skip_the_write_too:
+ if(we_cancel)
+ cancel_busy_cue(0);
+
+ if(sort_array)
+ fs_give((void **) &sort_array);
+
+ if(inv)
+ fs_give((void **) &inv);
+
+ return 0;
+}
+
+
+/*
+ * Returns 1 if any addrbooks are in Open state, 0 otherwise.
+ *
+ * This is a test for ostatus == Open, not for whether or not the address book
+ * FILE is opened.
+ */
+int
+any_ab_open(void)
+{
+ int i, ret = 0;
+
+ if(as.initialized)
+ for(i = 0; ret == 0 && i < as.n_addrbk; i++)
+ if(as.adrbks[i].ostatus == Open)
+ ret++;
+
+ return(ret);
+}
+
+
+/*
+ * Make sure addrbooks are minimally initialized.
+ */
+void
+init_ab_if_needed(void)
+{
+ dprint((9, "- init_ab_if_needed -\n"));
+
+ if(!as.initialized)
+ (void)init_addrbooks(Closed, 0, 0, 1);
+}
+
+
+/*
+ * Sets everything up to get started.
+ *
+ * Args: want_status -- The desired OpenStatus for all addrbooks.
+ * reset_to_top -- Forget about the old location and put cursor
+ * at top.
+ * open_if_only_one -- If want_status is HalfOpen and there is only
+ * section to look at, then promote want_status
+ * to Open.
+ * ro_warning -- Set ReadOnly warning global
+ *
+ * Return: 1 if ok, 0 if problem
+ */
+int
+init_addrbooks(OpenStatus want_status, int reset_to_top, int open_if_only_one, int ro_warning)
+{
+ register PerAddrBook *pab;
+ char *q, **t;
+ long line;
+
+ dprint((4, "-- init_addrbooks(%s, %d, %d, %d) --\n",
+ want_status==Open ?
+ "Open" :
+ want_status==HalfOpen ?
+ "HalfOpen" :
+ want_status==ThreeQuartOpen ?
+ "ThreeQuartOpen" :
+ want_status==NoDisplay ?
+ "NoDisplay" :
+ "Closed",
+ reset_to_top, open_if_only_one, ro_warning));
+
+ as.l_p_page = ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)
+ - HEADER_ROWS(ps_global);
+ if(as.l_p_page <= 0)
+ as.no_op_possbl++;
+ else
+ as.no_op_possbl = 0;
+
+ as.ro_warning = ro_warning;
+
+ /* already been initialized */
+ if(as.initialized){
+ int i;
+
+ /*
+ * Special case. If there is only one addressbook we start the
+ * user out with that open.
+ */
+ if(want_status == HalfOpen &&
+ ((open_if_only_one && as.n_addrbk == 1 && as.n_serv == 0) ||
+ (F_ON(F_EXPANDED_ADDRBOOKS, ps_global) &&
+ F_ON(F_CMBND_ABOOK_DISP, ps_global))))
+ want_status = Open;
+
+ /* open to correct state */
+ for(i = 0; i < as.n_addrbk; i++)
+ init_abook(&as.adrbks[i], want_status);
+
+ if(reset_to_top){
+ warp_to_beginning();
+ as.top_ent = 0L;
+ line = first_selectable_line(0L);
+ if(line == NO_LINE)
+ as.cur_row = 0L;
+ else
+ as.cur_row = line;
+
+ if(as.cur_row >= as.l_p_page)
+ as.top_ent += (as.cur_row - as.l_p_page + 1);
+
+ as.old_cur_row = as.cur_row;
+ }
+
+ dprint((4, "init_addrbooks: already initialized: %d books\n",
+ as.n_addrbk));
+ return((as.n_addrbk + as.n_serv) ? 1 : 0);
+ }
+
+ as.initialized = 1;
+
+ /* count directory servers */
+ as.n_serv = 0;
+ as.n_impl = 0;
+#ifdef ENABLE_LDAP
+ if(ps_global->VAR_LDAP_SERVERS &&
+ ps_global->VAR_LDAP_SERVERS[0] &&
+ ps_global->VAR_LDAP_SERVERS[0][0])
+ for(t = ps_global->VAR_LDAP_SERVERS; t[0] && t[0][0]; t++){
+ LDAP_SERV_S *info;
+
+ as.n_serv++;
+ info = break_up_ldap_server(*t);
+ as.n_impl += (info && info->impl) ? 1 : 0;
+ if(info)
+ free_ldap_server_info(&info);
+ }
+#endif /* ENABLE_LDAP */
+
+ /* count addressbooks */
+ as.how_many_personals = 0;
+ if(ps_global->VAR_ADDRESSBOOK &&
+ ps_global->VAR_ADDRESSBOOK[0] &&
+ ps_global->VAR_ADDRESSBOOK[0][0])
+ for(t = ps_global->VAR_ADDRESSBOOK; t[0] && t[0][0]; t++)
+ as.how_many_personals++;
+
+ as.n_addrbk = as.how_many_personals;
+ if(ps_global->VAR_GLOB_ADDRBOOK &&
+ ps_global->VAR_GLOB_ADDRBOOK[0] &&
+ ps_global->VAR_GLOB_ADDRBOOK[0][0])
+ for(t = ps_global->VAR_GLOB_ADDRBOOK; t[0] && t[0][0]; t++)
+ as.n_addrbk++;
+
+ if(want_status == HalfOpen &&
+ ((open_if_only_one && as.n_addrbk == 1 && as.n_serv == 0) ||
+ (F_ON(F_EXPANDED_ADDRBOOKS, ps_global) &&
+ F_ON(F_CMBND_ABOOK_DISP, ps_global))))
+ want_status = Open;
+
+
+ /*
+ * allocate array of PerAddrBooks
+ * (we don't give this up until we exit Pine, but it's small)
+ */
+ if(as.n_addrbk){
+ as.adrbks = (PerAddrBook *)fs_get(as.n_addrbk * sizeof(PerAddrBook));
+ memset((void *)as.adrbks, 0, as.n_addrbk * sizeof(PerAddrBook));
+
+ /* init PerAddrBook data */
+ for(as.cur = 0; as.cur < as.n_addrbk; as.cur++){
+ char *nickname = NULL,
+ *filename = NULL;
+
+ if(as.cur < as.how_many_personals)
+ q = ps_global->VAR_ADDRESSBOOK[as.cur];
+ else
+ q = ps_global->VAR_GLOB_ADDRBOOK[as.cur - as.how_many_personals];
+
+ pab = &as.adrbks[as.cur];
+
+ /* Parse entry for optional nickname and filename */
+ get_pair(q, &nickname, &filename, 0, 0);
+
+ if(nickname && !*nickname)
+ fs_give((void **)&nickname);
+
+ strncpy(tmp_20k_buf, filename, SIZEOF_20KBUF);
+ fs_give((void **)&filename);
+
+ filename = tmp_20k_buf;
+ if(nickname == NULL)
+ pab->abnick = cpystr(filename);
+ else
+ pab->abnick = nickname;
+
+ if(*filename == '~')
+ fnexpand(filename, SIZEOF_20KBUF);
+
+ if(*filename == '{' || is_absolute_path(filename)){
+ pab->filename = cpystr(filename); /* fully qualified */
+ }
+ else{
+ char book_path[MAXPATH+1];
+ char *lc = last_cmpnt(ps_global->pinerc);
+
+ book_path[0] = '\0';
+ if(lc != NULL){
+ strncpy(book_path, ps_global->pinerc,
+ MIN(lc - ps_global->pinerc, sizeof(book_path)-1));
+ book_path[MIN(lc - ps_global->pinerc,
+ sizeof(book_path)-1)] = '\0';
+ }
+
+ strncat(book_path, filename,
+ sizeof(book_path)-1-strlen(book_path));
+ pab->filename = cpystr(book_path);
+ }
+
+ if(*pab->filename == '{')
+ pab->type |= REMOTE_VIA_IMAP;
+
+ if(as.cur >= as.how_many_personals)
+ pab->type |= GLOBAL;
+
+ pab->access = adrbk_access(pab);
+
+ /* global address books are forced readonly */
+ if(pab->type & GLOBAL && pab->access != NoAccess)
+ pab->access = ReadOnly;
+
+ pab->ostatus = TotallyClosed;
+
+ /*
+ * and remember that the memset above initializes everything
+ * else to 0
+ */
+
+ init_abook(pab, want_status);
+ }
+ }
+
+ /*
+ * Have to reset_to_top in this case since this is the first open,
+ * regardless of the value of the argument, since these values haven't been
+ * set before here.
+ */
+ as.cur = 0;
+ as.top_ent = 0L;
+ warp_to_beginning();
+ line = first_selectable_line(0L);
+
+ if(line == NO_LINE)
+ as.cur_row = 0L;
+ else
+ as.cur_row = line;
+
+ if(as.cur_row >= as.l_p_page){
+ as.top_ent += (as.cur_row - as.l_p_page + 1);
+ as.cur_row = as.l_p_page - 1;
+ }
+
+ as.old_cur_row = as.cur_row;
+
+ return((as.n_addrbk + as.n_serv) ? 1 : 0);
+}
+
+
+/*
+ * Something was changed in options screen, so need to start over.
+ */
+void
+addrbook_reset(void)
+{
+ dprint((4, "- addrbook_reset -\n"));
+ completely_done_with_adrbks();
+}
+
+
+/*
+ * Sort was changed in options screen. Since we only sort normally
+ * when we actually make a change to the address book, we need to
+ * go out of our way to sort here.
+ */
+void
+addrbook_redo_sorts(void)
+{
+ int i;
+ PerAddrBook *pab;
+ AdrBk *ab;
+
+ dprint((4, "- addrbook_redo_sorts -\n"));
+
+ addrbook_reset();
+ init_ab_if_needed();
+
+ for(i = 0; i < as.n_addrbk; i++){
+ pab = &as.adrbks[i];
+ init_abook(pab, NoDisplay);
+ ab = pab->address_book;
+
+ if(!adrbk_is_in_sort_order(ab, 0))
+ adrbk_write(ab, 0, NULL, NULL, 1, 0);
+ }
+
+ addrbook_reset();
+}
+
+
+/*
+ * Returns type of access allowed on this addrbook.
+ */
+AccessType
+adrbk_access(PerAddrBook *pab)
+{
+ char fbuf[MAXPATH+1];
+ AccessType access = NoExists;
+ CONTEXT_S *dummy_cntxt = NULL;
+
+ dprint((9, "- addrbook_access -\n"));
+
+ if(pab && pab->type & REMOTE_VIA_IMAP){
+ /*
+ * Open_fcc creates the folder if it didn't already exist.
+ */
+ if((pab->so = open_fcc(pab->filename, &dummy_cntxt, 1,
+ "Error: ",
+ " Can't fetch remote addrbook.")) != NULL){
+ /*
+ * We know the folder is there but don't know what access
+ * rights we have until we try to select it, which we don't
+ * want to do unless we have to. So delay evaluating.
+ */
+ access = MaybeRorW;
+ }
+ }
+ else if(pab){ /* local file */
+#if defined(NO_LOCAL_ADDRBOOKS)
+ /* don't allow any access to local addrbooks */
+ access = NoAccess;
+#else /* !NO_LOCAL_ADDRBOOKS) */
+ build_path(fbuf, is_absolute_path(pab->filename) ? NULL
+ : ps_global->home_dir,
+ pab->filename, sizeof(fbuf));
+
+#if defined(DOS) || defined(OS2)
+ /*
+ * Microsoft networking causes some access calls to do a DNS query (!!)
+ * when it is turned on. In particular, if there is a / in the filename
+ * this seems to happen. So, just don't allow it.
+ */
+ if(strindex(fbuf, '/') != NULL){
+ dprint((2, "\"/\" not allowed in addrbook name\n"));
+ return NoAccess;
+ }
+#else /* !DOS */
+ /* also prevent backslash in non-DOS addrbook names */
+ if(strindex(fbuf, '\\') != NULL){
+ dprint((2, "\"\\\" not allowed in addrbook name\n"));
+ return NoAccess;
+ }
+#endif /* !DOS */
+
+ if(can_access(fbuf, ACCESS_EXISTS) == 0){
+ if(can_access(fbuf, EDIT_ACCESS) == 0){
+ char *dir, *p;
+
+ dir = ".";
+ if((p = last_cmpnt(fbuf)) != NULL){
+ *--p = '\0';
+ dir = *fbuf ? fbuf : "/";
+ }
+
+#if defined(DOS) || defined(OS2)
+ /*
+ * If the dir has become a drive letter and : (e.g. "c:")
+ * then append a "\". The library function access() in the
+ * win 16 version of MSC seems to require this.
+ */
+ if(isalpha((unsigned char) *dir)
+ && *(dir+1) == ':' && *(dir+2) == '\0'){
+ *(dir+2) = '\\';
+ *(dir+3) = '\0';
+ }
+#endif /* DOS || OS2 */
+
+ /*
+ * Even if we can edit the address book file itself, we aren't
+ * going to be able to change it unless we can also write in
+ * the directory that contains it (because we write into a
+ * temp file and then rename).
+ */
+ if(can_access(dir, EDIT_ACCESS) == 0)
+ access = ReadWrite;
+ else{
+ access = ReadOnly;
+ q_status_message1(SM_ORDER, 2, 2,
+ "Address book directory (%.200s) is ReadOnly",
+ dir);
+ }
+ }
+ else if(can_access(fbuf, READ_ACCESS) == 0)
+ access = ReadOnly;
+ else
+ access = NoAccess;
+ }
+#endif /* !NO_LOCAL_ADDRBOOKS) */
+ }
+
+ return(access);
+}
+
+
+/*
+ * Trim back remote address books if necessary.
+ */
+void
+trim_remote_adrbks(void)
+{
+ register PerAddrBook *pab;
+ int i;
+
+ dprint((2, "- trim_remote_adrbks -\n"));
+
+ if(!as.initialized)
+ return;
+
+ for(i = 0; i < as.n_addrbk; i++){
+ pab = &as.adrbks[i];
+ if(pab->ostatus != TotallyClosed && pab->address_book
+ && pab->address_book->rd)
+ rd_trim_remdata(&pab->address_book->rd);
+ }
+}
+
+
+/*
+ * Free and close everything.
+ */
+void
+completely_done_with_adrbks(void)
+{
+ register PerAddrBook *pab;
+ int i;
+
+ dprint((2, "- completely_done_with_adrbks -\n"));
+
+ ab_nesting_level = 0;
+
+ if(!as.initialized)
+ return;
+
+ for(i = 0; i < as.n_addrbk; i++)
+ init_abook(&as.adrbks[i], TotallyClosed);
+
+ for(i = 0; i < as.n_addrbk; i++){
+ pab = &as.adrbks[i];
+
+ if(pab->filename)
+ fs_give((void **)&pab->filename);
+
+ if(pab->abnick)
+ fs_give((void **)&pab->abnick);
+ }
+
+ done_with_dlc_cache();
+
+ if(as.adrbks)
+ fs_give((void **)&as.adrbks);
+
+ as.n_addrbk = 0;
+ as.initialized = 0;
+}
+
+
+/*
+ * Initialize or re-initialize this address book.
+ *
+ * Args: pab -- the PerAddrBook ptr
+ * want_status -- desired OpenStatus for this address book
+ */
+void
+init_abook(PerAddrBook *pab, OpenStatus want_status)
+{
+ register OpenStatus new_status;
+
+ dprint((4, "- init_abook -\n"));
+ dprint((7, " addrbook nickname = %s filename = %s",
+ pab->abnick ? pab->abnick : "<null>",
+ pab->filename ? pab->filename : "<null>"));
+ dprint((7, " ostatus was %s, want %s\n",
+ pab->ostatus==Open ? "Open" :
+ pab->ostatus==HalfOpen ? "HalfOpen" :
+ pab->ostatus==ThreeQuartOpen ? "ThreeQuartOpen" :
+ pab->ostatus==NoDisplay ? "NoDisplay" :
+ pab->ostatus==Closed ? "Closed" : "TotallyClosed",
+ want_status==Open ? "Open" :
+ want_status==HalfOpen ? "HalfOpen" :
+ want_status==ThreeQuartOpen ? "ThreeQuartOpen" :
+ want_status==NoDisplay ? "NoDisplay" :
+ want_status==Closed ? "Closed" : "TotallyClosed"));
+
+ new_status = want_status; /* optimistic default */
+
+ if(want_status == TotallyClosed){
+ if(pab->address_book != NULL){
+ adrbk_close(pab->address_book);
+ pab->address_book = NULL;
+ }
+
+ if(pab->so != NULL){
+ so_give(&pab->so);
+ pab->so = NULL;
+ }
+ }
+ /*
+ * If we don't need it, release some addrbook memory by calling
+ * adrbk_partial_close().
+ */
+ else if((want_status == Closed || want_status == HalfOpen) &&
+ pab->address_book != NULL){
+ adrbk_partial_close(pab->address_book);
+ }
+ /* If we want the addrbook read in and it hasn't been, do so */
+ else if(want_status == Open || want_status == NoDisplay){
+ if(pab->address_book == NULL){ /* abook handle is not currently active */
+ if(pab->access != NoAccess){
+ char warning[800]; /* place to put a warning */
+ int sort_rule;
+
+ warning[0] = '\0';
+ if(pab->access == ReadOnly)
+ sort_rule = AB_SORT_RULE_NONE;
+ else
+ sort_rule = ps_global->ab_sort_rule;
+
+ pab->address_book = adrbk_open(pab,
+ ps_global->home_dir, warning, sizeof(warning), sort_rule);
+
+ if(pab->address_book == NULL){
+ pab->access = NoAccess;
+ if(want_status == Open){
+ new_status = HalfOpen; /* best we can do */
+ q_status_message1(SM_ORDER | SM_DING, *warning?1:3, 4,
+ _("Error opening/creating address book %.200s"),
+ pab->abnick);
+ if(*warning)
+ q_status_message2(SM_ORDER, 3, 4, "%.200s: %.200s",
+ as.n_addrbk > 1 ? pab->abnick : "addressbook",
+ warning);
+ }
+ else
+ new_status = Closed;
+
+ dprint((1, "Error opening address book %s: %s\n",
+ pab->abnick ? pab->abnick : "?",
+ error_description(errno)));
+ }
+ else{
+ if(pab->access == NoExists)
+ pab->access = ReadWrite;
+
+ if(pab->access == ReadWrite){
+ /*
+ * Add forced entries if there are any. These are
+ * entries that are always supposed to show up in
+ * personal address books. They're specified in the
+ * global config file.
+ */
+ add_forced_entries(pab->address_book);
+ }
+
+ new_status = want_status;
+ dprint((2, "Address book %s (%s) opened with %ld items\n",
+ pab->abnick ? pab->abnick : "?",
+ pab->filename ? pab->filename : "?",
+ (long)adrbk_count(pab->address_book)));
+ if(*warning){
+ dprint((1,
+ "Addressbook parse error in %s (%s): %s\n",
+ pab->abnick ? pab->abnick : "?",
+ pab->filename ? pab->filename : "?",
+ warning));
+ if(!pab->gave_parse_warnings && want_status == Open){
+ pab->gave_parse_warnings++;
+ q_status_message2(SM_ORDER, 3, 4, "%.200s: %.200s",
+ as.n_addrbk > 1 ? pab->abnick : "addressbook",
+ warning);
+ }
+ }
+ }
+ }
+ else{
+ if(want_status == Open){
+ new_status = HalfOpen; /* best we can do */
+ q_status_message1(SM_ORDER | SM_DING, 3, 4,
+ "Insufficient permissions for opening address book %.200s",
+ pab->abnick);
+ }
+ else
+ new_status = Closed;
+ }
+ }
+ /*
+ * File handle was already open but we were in Closed or HalfOpen
+ * state. Check to see if someone else has changed the addrbook
+ * since we first opened it.
+ */
+ else if((pab->ostatus == Closed || pab->ostatus == HalfOpen) &&
+ ps_global->remote_abook_validity > 0)
+ (void)adrbk_check_and_fix(pab, 1, 0, 0);
+ }
+
+ pab->ostatus = new_status;
+}
+
+
+/*
+ * Does a validity check on all the open address books.
+ *
+ * Return -- number of address books with invalid data
+ */
+int
+adrbk_check_all_validity_now(void)
+{
+ int i;
+ int something_out_of_date = 0;
+ PerAddrBook *pab;
+
+ dprint((7, "- adrbk_check_all_validity_now -\n"));
+
+ if(as.initialized){
+ for(i = 0; i < as.n_addrbk; i++){
+ pab = &as.adrbks[i];
+ if(pab->address_book){
+ adrbk_check_validity(pab->address_book, 1L);
+ if(pab->address_book->flags & FILE_OUTOFDATE ||
+ (pab->address_book->rd &&
+ pab->address_book->rd->flags & REM_OUTOFDATE))
+ something_out_of_date++;
+ }
+ }
+ }
+
+ return(something_out_of_date);
+}
+
+
+/*
+ * Fix out-of-date address book. This only fixes the AdrBk part of the
+ * problem, not the pab and display. It closes and reopens the address_book
+ * file, clears the cache, reads new data.
+ *
+ * Arg pab -- Pointer to the PerAddrBook data for this addrbook
+ * safe -- It is safe to apply the fix
+ * low_freq -- This is a low frequency check (longer between checks)
+ *
+ * Returns non-zero if addrbook was fixed
+ */
+int
+adrbk_check_and_fix(PerAddrBook *pab, int safe, int low_freq, int check_now)
+{
+ int ret = 0;
+ long chk_interval;
+
+ if(!as.initialized || !pab)
+ return(ret);
+
+ dprint((7, "- adrbk_check_and_fix(%s) -\n",
+ pab->filename ? pab->filename : "?"));
+
+ if(pab->address_book){
+ if(check_now)
+ chk_interval = 1L;
+ else if(low_freq)
+ chk_interval = -60L * MAX(LOW_FREQ_CHK_INTERVAL,
+ ps_global->remote_abook_validity + 5);
+ else
+ chk_interval = 0L;
+
+ adrbk_check_validity(pab->address_book, chk_interval);
+
+ if(pab->address_book->flags & FILE_OUTOFDATE ||
+ (pab->address_book->rd &&
+ pab->address_book->rd->flags & REM_OUTOFDATE &&
+ !(pab->address_book->rd->flags & USER_SAID_NO))){
+ if(safe){
+ OpenStatus save_status;
+ int save_rem_abook_valid = 0;
+
+ dprint((2, "adrbk_check_and_fix %s: fixing %s\n",
+ debug_time(0,0),
+ pab->filename ? pab->filename : "?"));
+ if(ab_nesting_level > 0){
+ q_status_message3(SM_ORDER, 0, 2,
+ "Resyncing address book%.200s%.200s%.200s",
+ as.n_addrbk > 1 ? " \"" : "",
+ as.n_addrbk > 1 ? pab->abnick : "",
+ as.n_addrbk > 1 ? "\"" : "");
+ display_message('x');
+ fflush(stdout);
+ }
+
+ ret++;
+ save_status = pab->ostatus;
+
+ /* don't do the trim right now */
+ if(pab->address_book->rd)
+ pab->address_book->rd->flags &= ~DO_REMTRIM;
+
+ /*
+ * Have to change this from -1 or we won't actually do
+ * the resync.
+ */
+ if((pab->address_book->rd &&
+ pab->address_book->rd->flags & REM_OUTOFDATE) &&
+ ps_global->remote_abook_validity == -1){
+ save_rem_abook_valid = -1;
+ ps_global->remote_abook_validity = 0;
+ }
+
+ init_abook(pab, TotallyClosed);
+
+ pab->so = NULL;
+ /* this sets up pab->so, so is important */
+ pab->access = adrbk_access(pab);
+
+ /*
+ * If we just re-init to HalfOpen... we won't actually
+ * open the address book, which was open before. That
+ * would be fine but it's a little nicer if we can open
+ * it now so that we don't defer the resync until
+ * the next open, which would be a user action for sure.
+ * Right now we may be here during a newmail check
+ * timeout, so this is a good time to do the resync.
+ */
+ if(save_status == HalfOpen ||
+ save_status == ThreeQuartOpen ||
+ save_status == Closed)
+ init_abook(pab, NoDisplay);
+
+ init_abook(pab, save_status);
+
+ if(save_rem_abook_valid)
+ ps_global->remote_abook_validity = save_rem_abook_valid;
+
+ if(ab_nesting_level > 0 && pab->ostatus == save_status)
+ q_status_message3(SM_ORDER, 0, 2,
+ "Resynced address book%.200s%.200s%.200s",
+ as.n_addrbk > 1 ? " \"" : "",
+ as.n_addrbk > 1 ? pab->abnick : "",
+ as.n_addrbk > 1 ? "\"" : "");
+ }
+ else
+ dprint((2,
+ "adrbk_check_and_fix: not safe to fix %s\n",
+ pab->filename ? pab->filename : "?"));
+ }
+ }
+
+ return(ret);
+}
+
+
+static time_t last_check_and_fix_all;
+/*
+ * Fix out of date address books. This only fixes the AdrBk part of the
+ * problem, not the pab and display. It closes and reopens the address_book
+ * files, clears the caches, reads new data.
+ *
+ * Args safe -- It is safe to apply the fix
+ * low_freq -- This is a low frequency check (longer between checks)
+ *
+ * Returns non-zero if an addrbook was fixed
+ */
+int
+adrbk_check_and_fix_all(int safe, int low_freq, int check_now)
+{
+ int i, ret = 0;
+ PerAddrBook *pab;
+
+ if(!as.initialized)
+ return(ret);
+
+ dprint((7, "- adrbk_check_and_fix_all -\n"));
+
+ last_check_and_fix_all = get_adj_time();
+
+ for(i = 0; i < as.n_addrbk; i++){
+ pab = &as.adrbks[i];
+ if(pab->address_book)
+ ret += adrbk_check_and_fix(pab, safe, low_freq, check_now);
+ }
+
+ return(ret);
+}
+
+
+/*
+ *
+ */
+void
+adrbk_maintenance(void)
+{
+ static time_t last_time_here = 0;
+ time_t now;
+ int i;
+ long low_freq_interval;
+
+ dprint((9, "- adrbk_maintenance -\n"));
+
+ if(!as.initialized)
+ return;
+
+ now = get_adj_time();
+
+ if(now < last_time_here + 120)
+ return;
+
+ last_time_here = now;
+
+ low_freq_interval = MAX(LOW_FREQ_CHK_INTERVAL,
+ ps_global->remote_abook_validity + 5);
+
+ /* check the time here to make it cheap */
+ if(ab_nesting_level == 0 &&
+ ps_global->remote_abook_validity > 0 &&
+ now > last_check_and_fix_all + low_freq_interval * 60L)
+ (void)adrbk_check_and_fix_all(1, 1, 0);
+
+ /* close down idle connections */
+ for(i = 0; i < as.n_addrbk; i++){
+ PerAddrBook *pab;
+
+ pab = &as.adrbks[i];
+
+ if(pab->address_book &&
+ pab->address_book->type == Imap &&
+ pab->address_book->rd &&
+ rd_stream_exists(pab->address_book->rd)){
+ dprint((7,
+ "adrbk_maint: %s: idle cntr %ld (%ld)\n",
+ pab->address_book->orig_filename
+ ? pab->address_book->orig_filename : "?",
+ (long)(now - pab->address_book->rd->last_use),
+ (long)pab->address_book->rd->last_use));
+
+ if(now > pab->address_book->rd->last_use + IMAP_IDLE_TIMEOUT){
+ dprint((2,
+ "adrbk_maint %s: closing idle (%ld secs) connection: %s\n",
+ debug_time(0,0),
+ (long)(now - pab->address_book->rd->last_use),
+ pab->address_book->orig_filename
+ ? pab->address_book->orig_filename : "?"));
+ rd_close_remote(pab->address_book->rd);
+ }
+ else{
+ /*
+ * If we aren't going to close it, we ping it instead to
+ * make sure it stays open and doesn't timeout on us.
+ * This shouldn't be necessary unless the server has a
+ * really short timeout. If we got killed for some reason
+ * we set imap.stream to NULL.
+ * Instead of just pinging, we may as well check for
+ * updates, too.
+ */
+ if(ab_nesting_level == 0 &&
+ ps_global->remote_abook_validity > 0){
+ time_t save_last_use;
+
+ /*
+ * We shouldn't count this as a real last_use.
+ */
+ save_last_use = pab->address_book->rd->last_use;
+ (void)adrbk_check_and_fix(pab, 1, 0, 1);
+ pab->address_book->rd->last_use = save_last_use;
+ }
+ /* just ping it if not safe to fix it */
+ else if(!rd_ping_stream(pab->address_book->rd)){
+ dprint((2,
+ "adrbk_maint: %s: abook stream closed unexpectedly: %s\n",
+ debug_time(0,0),
+ pab->address_book->orig_filename
+ ? pab->address_book->orig_filename : "?"));
+ }
+ }
+ }
+ }
+}
+
+
+/*
+ * Parses a string of comma-separated addresses or nicknames into an
+ * array.
+ *
+ * Returns an allocated, null-terminated list, or NULL.
+ */
+char **
+parse_addrlist(char *addrfield)
+{
+#define LISTCHUNK 500 /* Alloc this many addresses for list at a time */
+ char **al, **ad;
+ char *next_addr, *cur_addr, *p, *q;
+ int slots = LISTCHUNK;
+
+ if(!addrfield)
+ return((char **)NULL);
+
+ /* allocate first chunk */
+ slots = LISTCHUNK;
+ al = (char **)fs_get(sizeof(char *) * (slots+1));
+ ad = al;
+
+ p = addrfield;
+
+ /* skip any leading whitespace */
+ for(q = p; *q && *q == SPACE; q++)
+ ;/* do nothing */
+
+ next_addr = (*q) ? q : NULL;
+
+ /* Loop adding each address in list to array al */
+ for(cur_addr = next_addr; cur_addr; cur_addr = next_addr){
+
+ next_addr = skip_to_next_addr(cur_addr);
+
+ q = cur_addr;
+ SKIP_SPACE(q);
+
+ /* allocate more space */
+ if((ad-al) >= slots){
+ slots += LISTCHUNK;
+ fs_resize((void **)&al, sizeof(char *) * (slots+1));
+ ad = al + slots - LISTCHUNK;
+ }
+
+ if(*q)
+ *ad++ = cpystr(q);
+ }
+
+ *ad++ = NULL;
+
+ /* free up any excess we've allocated */
+ fs_resize((void **)&al, sizeof(char *) * (ad - al));
+ return(al);
+}
+
+
+/*
+ * Args cur -- pointer to the start of the current addr in list.
+ *
+ * Returns a pointer to the start of the next addr or NULL if there are
+ * no more addrs.
+ *
+ * Side effect: current addr has trailing white space removed
+ * and is null terminated.
+ */
+char *
+skip_to_next_addr(char *cur)
+{
+ register char *p,
+ *q;
+ char *ret_pointer;
+ int in_quotes = 0,
+ in_comment = 0;
+ char prev_char = '\0';
+
+ /*
+ * Find delimiting comma or end.
+ * Quoted commas and commented commas don't count.
+ */
+ for(q = cur; *q; q++){
+ switch(*q){
+ case COMMA:
+ if(!in_quotes && !in_comment)
+ goto found_comma;
+ break;
+
+ case LPAREN:
+ if(!in_quotes && !in_comment)
+ in_comment = 1;
+ break;
+
+ case RPAREN:
+ if(in_comment && prev_char != BSLASH)
+ in_comment = 0;
+ break;
+
+ case QUOTE:
+ if(in_quotes && prev_char != BSLASH)
+ in_quotes = 0;
+ else if(!in_quotes && !in_comment)
+ in_quotes = 1;
+ break;
+
+ default:
+ break;
+ }
+
+ prev_char = *q;
+ }
+
+found_comma:
+ if(*q){ /* trailing comma case */
+ *q = '\0';
+ ret_pointer = q + 1;
+ }
+ else
+ ret_pointer = NULL; /* no more addrs after cur */
+
+ /* remove trailing white space from cur */
+ for(p = q - 1; p >= cur && isspace((unsigned char)*p); p--)
+ *p = '\0';
+
+ return(ret_pointer);
+}
+
+
+/*
+ * Add entries specified by system administrator. If the nickname already
+ * exists, it is not touched.
+ */
+void
+add_forced_entries(AdrBk *abook)
+{
+ AdrBk_Entry *abe;
+ char *nickname, *fullname, *address;
+ char *end_of_nick, *end_of_full, **t;
+
+
+ if(!ps_global->VAR_FORCED_ABOOK_ENTRY ||
+ !ps_global->VAR_FORCED_ABOOK_ENTRY[0] ||
+ !ps_global->VAR_FORCED_ABOOK_ENTRY[0][0])
+ return;
+
+ for(t = ps_global->VAR_FORCED_ABOOK_ENTRY; t[0] && t[0][0]; t++){
+ nickname = *t;
+
+ /*
+ * syntax for each element is
+ * nick[whitespace]|[whitespace]Fullname[WS]|[WS]Address
+ */
+
+ /* find end of nickname */
+ end_of_nick = nickname;
+ while(*end_of_nick
+ && !isspace((unsigned char)*end_of_nick)
+ && *end_of_nick != '|')
+ end_of_nick++;
+
+ /* find the pipe character between nickname and fullname */
+ fullname = end_of_nick;
+ while(*fullname && *fullname != '|')
+ fullname++;
+
+ if(*fullname)
+ fullname++;
+
+ *end_of_nick = '\0';
+ abe = adrbk_lookup_by_nick(abook, nickname, NULL);
+
+ if(!abe){ /* If it isn't there, add it */
+
+ /* skip whitespace before fullname */
+ fullname = skip_white_space(fullname);
+
+ /* find the pipe character between fullname and address */
+ end_of_full = fullname;
+ while(*end_of_full && *end_of_full != '|')
+ end_of_full++;
+
+ if(!*end_of_full){
+ dprint((2,
+ "missing | in forced-abook-entry \"%s\"\n",
+ nickname ? nickname : "?"));
+ continue;
+ }
+
+ address = end_of_full + 1;
+
+ /* skip whitespace before address */
+ address = skip_white_space(address);
+
+ if(*address == '('){
+ dprint((2,
+ "no lists allowed in forced-abook-entry \"%s\"\n",
+ address ? address : "?"));
+ continue;
+ }
+
+ /* go back and remove trailing white space from fullname */
+ while(*end_of_full == '|' || isspace((unsigned char)*end_of_full)){
+ *end_of_full = '\0';
+ end_of_full--;
+ }
+
+ dprint((2,
+ "Adding forced abook entry \"%s\"\n", nickname ? nickname : ""));
+
+ (void)adrbk_add(abook,
+ NO_NEXT,
+ nickname,
+ fullname,
+ address,
+ NULL,
+ NULL,
+ Single,
+ (adrbk_cntr_t *)NULL,
+ (int *)NULL,
+ 1,
+ 0,
+ 1);
+ }
+ }
+}
diff --git a/pith/adrbklib.h b/pith/adrbklib.h
new file mode 100644
index 00000000..6b148251
--- /dev/null
+++ b/pith/adrbklib.h
@@ -0,0 +1,857 @@
+/*
+ * $Id: adrbklib.h 1012 2008-03-26 00:44:22Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_ADRBKLIB_INCLUDED
+#define PITH_ADRBKLIB_INCLUDED
+
+
+#include "../pith/indxtype.h"
+#include "../pith/remtype.h"
+#include "../pith/store.h"
+#include "../pith/string.h"
+
+
+/*
+ * Some notes:
+ *
+ * The order that the address book is stored in on disk is the order that
+ * the entries will be displayed in. When an address book is opened,
+ * if it is ReadWrite the sort order is checked and it is sorted if it is
+ * out of order.
+ *
+ * Considerable complexity that previously existed in the address book
+ * code has been removed. Cpu speeds have improved significantly and
+ * memory has increased dramatically, as well. The cost of the complexity
+ * of the lookup file and hashtables and all that stuff is thought to be
+ * more than the benefits. See pre-5.00 pine adrbklib.h for the way it
+ * used to be.
+ *
+ * The display code has remained the same.
+ * There is also some allocation happening
+ * in addrbook.c. In particular, the display is a window into an array
+ * of rows, at least one row per addrbook entry plus more for lists.
+ * Each row is an AddrScrn_Disp structure and those should typically take
+ * up 6 or 8 bytes. A cached copy of addrbook entries is not kept, just
+ * the element number to look it up (and often get it out of the EntryRef
+ * cache). In order to avoid having to allocate all those rows, this is
+ * also in the form of a cache. Only 3 * screen_length rows are kept in
+ * the cache, and the cache is always a simple interval of rows. That is,
+ * rows between valid_low and valid_high are all in the cache. Row numbers
+ * in the display list are a little mysterious. There is no origin. That
+ * is, you don't necessarily know where the start or end of the display
+ * is. You only know how to go forward and backward and how to "warp"
+ * to new locations in the display and go forward and backward from there.
+ * This is because we don't know how many rows there are all together. It
+ * is also a way to avoid searching through everything to get somewhere.
+ * If you want to go to the End, you warp there and start backwards instead
+ * of reading through all the entries to get there. If you edit an entry
+ * so that it sorts into a new location, you warp to that new location to
+ * save processing all of the entries in between.
+ *
+ *
+ * Notes about RFC1522 encoding:
+ *
+ * If the fullname field contains other than US-ASCII characters, it is
+ * encoded using the rules of RFC1522 or its successor. The value actually
+ * stored in the file is encoded, even if it matches the character set of
+ * the user. This is so it is easy to pass the entry around or to change
+ * character sets without invalidating entries in the address book. When
+ * a fullname is displayed, it is first decoded. If the fullname field is
+ * encoded as being from a character set other than the user's character
+ * set, that will be retained until the user edits the field. Then it will
+ * change over to the user's character set. The comment field works in
+ * the same way, as do the "phrase" fields of any addresses. On outgoing
+ * mail, the correct character set will be retained if you use ComposeTo
+ * from the address book screen. However, if you use a nickname from the
+ * composer or ^T from the composer, the character set will be lost if it
+ * is different from the user's character set.
+ *
+ *
+ * Notes about RemoteViaImap address books:
+ *
+ * There are currently two types of address books, Local and Imap. Local means
+ * it is in a local file. Imap means it is stored in a folder on a remote
+ * IMAP server. The folder is a regular folder containing mail messages but
+ * the messages are special. The first message is a header message. The last
+ * message is the address book data. In between messages are old versions of
+ * the address book data. The address book data is stored in the message as
+ * it would be on disk, with no fancy mime encoding or anything. When it is
+ * used the data from the last message in the folder is copied to a local file
+ * and then it is used just like a local file. The local file is a cache for
+ * the remote data. We can tell the remote data has been changed by looking
+ * at the Date of the last message in the remote folder. If we see it has
+ * changed we copy the whole file over again and replace the cache file.
+ * A possibly quicker way to tell if it has changed is if the UID has
+ * changed or the number of messages in the folder has changed. We use those
+ * methods if possible since they don't require opening a new stream and
+ * selecting the folder. There is one metadata file for address book data.
+ * The name of that file is stored in the pinerc file. It contains the names
+ * of the cache files for RemoveViaImap address books plus other caching
+ * information for those address books (uid...).
+ */
+
+#define NFIELDS 11 /* one more than num of data fields in addrbook entry */
+
+/*
+ * The type a_c_arg_t is a little confusing. It's the type we use in
+ * place of adrbk_cntr_t when we want to pass an adrbk_cntr_t in an argument.
+ * We were running into problems with the integral promotion of adrbk_cntr_t
+ * args. A_c_arg_t has to be large enough to hold a promoted adrbk_cntr_t.
+ * So, if adrbk_cntr_t is unsigned short, then a_c_arg_t needs to be int if
+ * int is larger than short, or unsigned int if short is same size as int.
+ * Since usign16_t always fits in a short, a_c_arg_t of unsigned int should
+ * always work for !HUGE. For HUGE, UINT32 will be either an unsigned int
+ * or an unsigned long. If it is an unsigned long, then a_c_arg_t better be
+ * an unsigned long, too. If it is an unsigned int, then a_c_arg_t could
+ * be an unsigned int, too. However, if we just make it unsigned long, then
+ * it will be the same in all cases and big enough in all cases.
+ */
+
+#define adrbk_cntr_t UINT32 /* addrbook counter type */
+typedef unsigned long a_c_arg_t; /* type of arg passed for adrbk_cntr_t */
+#define NO_NEXT ((adrbk_cntr_t)-1)
+#define MAX_ADRBK_SIZE (2000000000L) /* leave room for extra display lines */
+
+/*
+ * The value NO_NEXT is reserved to mean that there is no next address, or that
+ * there is no address number to return. This is similar to getc returning
+ * -1 when there is no char to get, but since we've defined this to be
+ * unsigned we need to reserve one of the valid values for this purpose.
+ * With current implementation it needs to be all 1's, so memset initialization
+ * will work correctly.
+ */
+
+typedef enum {NotSet, Single, List} Tag;
+
+
+/* This is what is actually used by the routines that manipulate things */
+typedef struct adrbk_entry {
+ char *nickname; /* UTF-8 */
+ char *fullname; /* of simple addr or list (stored in UTF-8) */
+ union addr {
+ char *addr; /* for simple Single entries */
+ char **list; /* for distribution lists */
+ } addr;
+ char *fcc; /* fcc specific for when sending to this address */
+ char *extra; /* comments field (stored in UTF-8) */
+ char referenced; /* for detecting loops during lookup */
+ Tag tag; /* single addr (Single) or a list (List) */
+} AdrBk_Entry;
+
+
+typedef struct abook_tree_node {
+ struct abook_tree_node *down; /* next letter of nickname */
+ struct abook_tree_node *right; /* alternate letter of nickname */
+ char value; /* character at this node */
+ adrbk_cntr_t entrynum; /* use NO_NEXT as no-data indicator */
+} AdrBk_Trie;
+
+
+/* information useful for displaying the addrbook */
+typedef struct width_stuff {
+ int max_nickname_width;
+ int max_fullname_width;
+ int max_addrfield_width;
+ int max_fccfield_width;
+ int third_biggest_fullname_width;
+ int third_biggest_addrfield_width;
+ int third_biggest_fccfield_width;
+} WIDTH_INFO_S;
+
+
+typedef struct expanded_list {
+ adrbk_cntr_t ent;
+ struct expanded_list *next;
+} EXPANDED_S;
+
+
+typedef enum {Local, Imap} AdrbkType;
+
+
+#ifndef IMAP_IDLE_TIMEOUT
+#define IMAP_IDLE_TIMEOUT (10L * 60L) /* seconds */
+#endif
+#ifndef FILE_VALID_CHK_INTERVAL
+#define FILE_VALID_CHK_INTERVAL ( 15L) /* seconds */
+#endif
+#ifndef LOW_FREQ_CHK_INTERVAL
+#define LOW_FREQ_CHK_INTERVAL (240) /* minutes */
+#endif
+
+typedef struct adrbk {
+ AdrbkType type; /* type of address book */
+ char *orig_filename; /* passed in filename */
+ char *filename; /* addrbook filename */
+ char *our_filecopy; /* session copy of filename contents */
+ FILE *fp; /* fp for our_filecopy */
+ adrbk_cntr_t count; /* how many entries in addrbook */
+ adrbk_cntr_t del_count; /* how many #DELETED entries in abook */
+ AdrBk_Entry *arr; /* array of entries */
+ AdrBk_Entry *del; /* array of deleted entries */
+ AdrBk_Trie *nick_trie;
+ AdrBk_Trie *addr_trie;
+ AdrBk_Trie *full_trie;
+ AdrBk_Trie *revfull_trie;
+ time_t last_change_we_know_about;/* to look for others changing it*/
+ time_t last_local_valid_chk;/* when valid check was done */
+ unsigned flags; /* see defines in alpine.h (DEL_FILE...)*/
+ WIDTH_INFO_S widths; /* helps addrbook.c format columns */
+ int sort_rule;
+ EXPANDED_S *exp; /* this is for addrbook.c to use. A
+ list of expanded list entry nums is kept here */
+ EXPANDED_S *checks; /* this is for addrbook.c to use. A
+ list of checked entry nums is kept here */
+ EXPANDED_S *selects; /* this is for addrbook.c to use. A
+ list of selected entry nums is kept here */
+ REMDATA_S *rd;
+} AdrBk;
+
+
+/*
+ * The definitions starting here have to do with the virtual scrolling
+ * region view into the addressbooks. That is, the display.
+ *
+ * We could make every use of an AdrBk_Entry go through a function call
+ * like adrbk_get_ae(). Instead, we try to be smart and avoid the extra
+ * function calls by knowing when the addrbook entry is still valid, either
+ * because we haven't called any functions that could invalidate it or because
+ * we have locked it in the cache. If we do lock it, we need to be careful
+ * that it eventually gets unlocked. That can be done by an explicit
+ * adrbk_get_ae(Unlock) call, or it is done implicitly when the address book
+ * is written out. The reason it can get invalidated is that the abe that
+ * we get returned to us is just a pointer to a cached addrbook entry, and
+ * that entry can be flushed from the cache by other addrbook activity.
+ * So we need to be careful to make sure the abe is certain to be valid
+ * before using it.
+ *
+ * Data structures for the display of the address book. There's one
+ * of these structures per line on the screen.
+ *
+ * Types: Title -- The title line for the different address books. It has
+ * a ptr to the text of the Title line.
+ * ClickHereCmb -- This is the line that says to click here to
+ * expand. It changes types into the individual expanded
+ * components once it is expanded. It doesn't have any data
+ * other than an implicit title. This is only used with the
+ * combined-style addrbook display.
+ * ListClickHere --This is the line that says to click here to
+ * expand the members of a distribution list. It changes
+ * types into the individual expanded ListEnt's (if any)
+ * when it is expanded. It has a ptr to an AdrBk_Entry.
+ * ListEmpty -- Line that says this is an empty distribution list. No data.
+ * Empty -- Line that says this is an empty addressbook. No data.
+ * ZoomEmpty -- Line that says no addrs in zoomed view. No data.
+ * AddFirstGLob -- Place holder for adding an abook. No data.
+ * AddFirstPers -- Place holder for adding an abook. No data.
+ * DirServers -- Place holder for accessing directory servers. No data.
+ * Simple -- A single addressbook entry. It has a ptr to an AdrBk_Entry.
+ * When it is displayed, the fields are usually:
+ * <nickname> <fullname> <address or another nic>
+ * ListHead -- The head of an address list. This has a ptr to an
+ * AdrBk_Entry.
+ * <blank line> followed by
+ * <nickname> <fullname> "DISTRIBUTION LIST:"
+ * ListEnt -- The rest of an address list. It has a pointer to its
+ * ListHead element and a ptr (other) to this specific address
+ * (not a ptr to another AdrBk_Entry).
+ * <blank> <blank> <address or another nic>
+ * Text -- A ptr to text. For example, the ----- lines and
+ * whitespace lines.
+ * NoAbooks -- There are no address books at all.
+ * Beginnning -- The (imaginary) elements before the first real element
+ * End -- The (imaginary) elements after the last real element
+ */
+typedef enum {DlNotSet, Empty, ZoomEmpty, AddFirstPers, AddFirstGlob,
+ AskServer, Title, Simple, ListHead, ListClickHere,
+ ListEmpty, ListEnt, Text, Beginning, End, NoAbooks,
+ ClickHereCmb, TitleCmb} LineType;
+/* each line of address book display is one of these structures */
+typedef struct addrscrn_disp {
+ union {
+ struct {
+ adrbk_cntr_t ab_element_number; /* which addrbook entry */
+ adrbk_cntr_t ab_list_offset; /* which member of the list */
+ }addrbook_entry;
+ char *text_ptr; /* a UTF-8 string */
+ }union_to_save_space;
+ LineType type;
+} AddrScrn_Disp;
+#define usst union_to_save_space.text_ptr
+#define elnum union_to_save_space.addrbook_entry.ab_element_number
+#define l_offset union_to_save_space.addrbook_entry.ab_list_offset
+
+#define entry_is_checked exp_is_expanded
+#define entry_get_next exp_get_next
+#define entry_set_checked exp_set_expanded
+#define entry_unset_checked exp_unset_expanded
+#define any_checked exp_any_expanded
+#define howmany_checked exp_howmany_expanded
+
+#define entry_is_selected exp_is_expanded
+#define entry_set_selected exp_set_expanded
+#define entry_unset_selected exp_unset_expanded
+#define any_selected exp_any_expanded
+#define howmany_selected exp_howmany_expanded
+
+#define entry_is_deleted exp_is_expanded
+#define entry_set_deleted exp_set_expanded
+#define howmany_deleted exp_howmany_expanded
+
+#define entry_is_added exp_is_expanded
+#define entry_set_added exp_set_expanded
+
+/*
+ * Argument to expand_address and build_address_internal is a BuildTo,
+ * which is either a char * address or an AdrBk_Entry * (if we have already
+ * looked it up in an addrbook).
+ */
+typedef enum {Str, Abe} Build_To_Arg_Type;
+typedef struct build_to {
+ Build_To_Arg_Type type;
+ union {
+ char *str; /* normal looking address string */
+ AdrBk_Entry *abe; /* addrbook entry */
+ }arg;
+} BuildTo;
+
+
+/* Display lines used up by each top-level addrbook, counting blanks */
+#define LINES_PER_ABOOK (3)
+/* How many of those lines are visible (not blank) */
+#define VIS_LINES_PER_ABOOK (2)
+/* How many extra lines are between the personal and global sections */
+#define XTRA_LINES_BETWEEN (1)
+/* How many lines does the PerAdd line take, counting blank line */
+#define LINES_PER_ADD_LINE (2)
+/* Extra title lines above first entry that are shown when the combined-style
+ display is turned on. */
+#define XTRA_TITLE_LINES_IN_OLD_ABOOK_DISP (4)
+
+typedef enum {DlcNotSet,
+ DlcPersAdd, /* config screen only */
+ DlcGlobAdd, /* " " " */
+
+ DlcTitle, /* top level displays */
+ DlcTitleNoPerm, /* " " " */
+ DlcSubTitle, /* " " " */
+ DlcTitleBlankTop, /* " " " */
+ DlcGlobDelim1, /* " " " */
+ DlcGlobDelim2, /* " " " */
+ DlcDirDelim1, /* " " " */
+ DlcDirDelim2, /* " " " */
+ DlcDirAccess, /* " " " */
+ DlcDirSubTitle, /* " " " */
+ DlcDirBlankTop, /* " " " */
+
+ DlcTitleDashTopCmb, /* combined-style top level display */
+ DlcTitleCmb, /* " " " " " */
+ DlcTitleDashBottomCmb, /* " " " " " */
+ DlcTitleBlankBottomCmb, /* " " " " " */
+ DlcClickHereCmb, /* " " " " " */
+ DlcTitleBlankTopCmb, /* " " " " " */
+ DlcDirDelim1a, /* " " " " " */
+ DlcDirDelim1b, /* " " " " " */
+ DlcDirDelim1c, /* " " " " " */
+
+ DlcEmpty, /* display of a single address book */
+ DlcZoomEmpty, /* " */
+ DlcNoPermission, /* " */
+ DlcSimple, /* " */
+ DlcListHead, /* " */
+ DlcListClickHere, /* " */
+ DlcListEmpty, /* " */
+ DlcListEnt, /* " */
+ DlcListBlankTop, /* " */
+ DlcListBlankBottom, /* " */
+ DlcNoAbooks, /* " */
+
+ DlcOneBeforeBeginning, /* used in both */
+ DlcTwoBeforeBeginning, /* " " " */
+ DlcBeginning, /* " " " */
+ DlcEnd} DlCacheType;
+
+typedef enum {Initialize, FirstEntry, LastEntry, ArbitraryStartingPoint,
+ DoneWithCache, FlushDlcFromCache, Lookup} DlMgrOps;
+typedef enum {Warp, DontWarp} HyperType;
+
+/*
+ * The DlCacheTypes are the types that a dlcache element can be labeled.
+ * The idea is that there needs to be enough information in the single
+ * cache element by itself so that you can figure out what the next and
+ * previous dl rows are by just knowing this one row.
+ *
+ * In the top-level display, there are DlcTitle lines or DlcTitleNoPerm
+ * lines, which are the same except we know that we can't access the
+ * address book in the latter case. DlcSubTitle lines follow each of the
+ * types of Title lines, and Titles within a section are separated by
+ * DlcTitleBlankTop lines, which belong to (have the same adrbk_num as)
+ * the Title they are above.
+ * If there are no address books and no directory servers defined, we
+ * have a DlcNoAbooks line. When we are displaying an individual address
+ * book (not in the top-level display) there is another set of types. An
+ * empty address book consists of one line of type DlcEmpty. An address
+ * book without read permission is a DlcNoPermission. Simple address book
+ * entries consist of a single DlcSimple line. Lists begin with a
+ * DlcListHead. If the list is not expanded the DlcListHead is followed by
+ * a DlcListClickHere. If it is known to be a list with no members the
+ * DlcListHead is followed by a DlcListEmpty. If there are members and
+ * the list is expanded, each list member is a single line of type
+ * DlcListEnt. Two lists are separated by a DlcListBlankBottom belonging
+ * to the first list. A list followed or preceded by a DlcSimple address
+ * row has a DlcListBlank(Top or Bottom) separating it from the
+ * DlcSimple. Above the top row of the display is an imaginary line of
+ * type DlcOneBeforeBeginning. Before that is a DlcTwoBeforeBeginning. And
+ * before that all the lines are just DlcBeginning lines. After the last
+ * display line is a DlcEnd.
+ *
+ * The DlcDirAccess's are indexed by adrbk_num (re-used for this).
+ * Adrbk_num -1 means access all of the servers.
+ * Adrbk_num 0 ... n_serv -1 means access all a particular server.
+ * Adrbk_num n_serv means access as if from composer using config setup.
+ *
+ * Here are the types of lines and where they fall in the top-level display:
+ *
+ * (not a visible line) DlcBeginning
+ * (not a visible line) DlcBeginning
+ * (not a visible line) DlcTwoBeforeBeginning
+ * (not a visible line) DlcOneBeforeBeginning
+ * Title DlcTitle (or TitleNoPerm)
+ * Subtitle DlcSubTitle
+ * ---this is blank---------------- DlcTitleBlankTop
+ * Title DlcTitle (or TitleNoPerm)
+ * Subtitle DlcSubTitle
+ * ---this is blank---------------- DlcGlobDelim1
+ * ---this is blank---------------- DlcGlobDelim2
+ * Title DlcTitle (or TitleNoPerm)
+ * Subtitle DlcSubTitle
+ * ---this is blank---------------- DlcTitleBlankTop
+ * Title DlcTitle (or TitleNoPerm)
+ * Subtitle DlcSubTitle
+ * ---this is blank---------------- DlcDirDelim1
+ * ---this is blank---------------- DlcDirDelim2
+ * Directory (query server 1) DlcDirAccess (adrbk_num 0)
+ * Subtitle DlcDirSubTitle (adrbk_num 0)
+ * ---this is blank---------------- DlcDirBlankTop
+ * Directory (query server 2) DlcDirAccess (adrbk_num 1)
+ * Subtitle DlcDirSubTitle (adrbk_num 1)
+ * (not a visible line) DlcEnd
+ * (not a visible line) DlcEnd
+ *
+ *
+ * There is a combined-style display triggered by the F_CMBND_ABOOK_DISP
+ * feature. It's a mixture of the top-level and open addrbook displays. When an
+ * addrbook is opened the rest of the addrbooks don't disappear from the
+ * screen. In this view, the ClickHere lines can be replaced with the entire
+ * contents of the addrbook, but the other stuff remains on the screen, too.
+ * Here are the types of lines and where they fall in the
+ * combined-style display:
+ *
+ * (not a visible line) DlcBeginning
+ * (not a visible line) DlcBeginning
+ * (not a visible line) DlcTwoBeforeBeginning
+ * (not a visible line) DlcOneBeforeBeginning
+ * -------------------------------- DlcTitleDashTopOld
+ * Title DlcTitleOld
+ * -------------------------------- DlcTitleDashBottomOld
+ * ---this is blank---------------- DlcTitleBlankBottom
+ * ClickHere DlcClickHereOld
+ * ---this is blank---------------- DlcTitleBlankTop
+ * -------------------------------- DlcTitleDashTopOld
+ * Title DlcTitleOld
+ * -------------------------------- DlcTitleDashBottomOld
+ * ---this is blank---------------- DlcTitleBlankBottom
+ * ClickHere DlcClickHereOld
+ * ---this is blank---------------- DlcDirDelim1
+ * -------------------------------- DlcDirDelim1a
+ * Directories DlcDirDelim1b
+ * -------------------------------- DlcDirDelim1c
+ * ---this is blank---------------- DlcDirDelim2
+ * Directory (query server 1) DlcDirAccess (adrbk_num 0)
+ * Subtitle DlcDirSubTitle (adrbk_num 0)
+ * ---this is blank---------------- DlcDirBlankTop
+ * Directory (query server 2) DlcDirAccess (adrbk_num 1)
+ * Subtitle DlcDirSubTitle (adrbk_num 1)
+ * (not a visible line) DlcEnd
+ * (not a visible line) DlcEnd
+ *
+ * If there are no addrbooks in either of the two sections, or no Directory
+ * servers, then that section is left out of the display. If there is only
+ * one address book and no Directories, then the user goes directly into the
+ * single addressbook view which looks like:
+ *
+ * if(no entries in addrbook)
+ *
+ * (not a visible line) DlcBeginning
+ * (not a visible line) DlcBeginning
+ * (not a visible line) DlcTwoBeforeBeginning
+ * (not a visible line) DlcOneBeforeBeginning
+ * Empty or NoPerm or NoAbooks DlcEmpty, DlcZoomEmpty, DlcNoPermission,
+ * or DlcNoAbooks
+ * (not a visible line) DlcEnd
+ * (not a visible line) DlcEnd
+ *
+ * else
+ *
+ * (not a visible line) DlcBeginning
+ * (not a visible line) DlcBeginning
+ * (not a visible line) DlcTwoBeforeBeginning
+ * (not a visible line) DlcOneBeforeBeginning
+ * Simple Entry DlcSimple
+ * Simple Entry DlcSimple
+ * Simple Entry DlcSimple
+ * DlcListBlankTop
+ * List Header DlcListHead
+ * Unexpanded List DlcListClickHere
+ * or
+ * Empty List DlcListEmpty
+ * or
+ * List Entry 1 DlcListEnt
+ * List Entry 2 DlcListEnt
+ * DlcListBlankBottom
+ * List Header DlcListHead
+ * List Entry 1 DlcListEnt
+ * List Entry 2 DlcListEnt
+ * List Entry 3 DlcListEnt
+ * DlcListBlankBottom
+ * Simple Entry DlcSimple
+ * DlcListBlankTop
+ * List Header DlcListHead
+ * Unexpanded List DlcListClickHere
+ * (not a visible line) DlcEnd
+ * (not a visible line) DlcEnd
+ *
+ * The config screen view is similar to the top-level view except there
+ * is no directory section (it has it's own config screen) and if there
+ * are zero personal addrbooks or zero global addrbooks then a placeholder
+ * line of type DlcPersAdd or DlcGlobAdd takes the place of the DlcTitle
+ * line.
+ */
+typedef struct dl_cache {
+ long global_row; /* disp_list row number */
+ adrbk_cntr_t dlcelnum; /* which elnum from that addrbook */
+ adrbk_cntr_t dlcoffset; /* offset in a list, only for ListEnt rows */
+ short adrbk_num; /* which address book we're related to */
+ DlCacheType type; /* type of this row */
+ AddrScrn_Disp dl; /* the actual dl that goes with this row */
+} DL_CACHE_S;
+
+
+typedef enum {Nickname, Fullname, Addr, Filecopy, Comment, Notused,
+ Def, WhenNoAddrDisplayed, Checkbox, Selected} ColumnType;
+
+/*
+ * Users can customize the addrbook display, so this tells us which data
+ * is in a particular column and how wide the column is. There is an
+ * array of these per addrbook, of length NFIELDS (number of possible cols).
+ */
+typedef struct column_description {
+ ColumnType type;
+ WidthType wtype;
+ int req_width; /* requested width (for fixed and percent types) */
+ int width; /* actual width to use */
+ int old_width;
+} COL_S;
+
+
+/* address book attributes for peraddrbook type */
+#define GLOBAL 0x1 /* else it is personal */
+#define REMOTE_VIA_IMAP 0x2 /* else it is a local file */
+
+
+typedef enum {TotallyClosed, /* hash tables not even set up yet */
+ Closed, /* data not read in, no display list */
+ NoDisplay, /* data is accessible, no display list */
+ HalfOpen, /* data not accessible, initial display list is set */
+ ThreeQuartOpen, /* like HalfOpen without partial_close */
+ Open /* data is accessible and display list is set */
+ } OpenStatus;
+
+/*
+ * There is one of these per addressbook.
+ */
+typedef struct peraddrbook {
+ unsigned type;
+ AccessType access;
+ OpenStatus ostatus;
+ char *abnick, /* kept internally in UTF-8 */
+ *filename;
+ AdrBk *address_book; /* the address book handle */
+ int gave_parse_warnings;
+ COL_S disp_form[NFIELDS]; /* display format */
+ int nick_is_displayed; /* these are for convenient, */
+ int full_is_displayed; /* fast access. Could get */
+ int addr_is_displayed; /* same info from disp_form. */
+ int fcc_is_displayed;
+ int comment_is_displayed;
+ STORE_S *so; /* storage obj for addrbook
+ temporarily stored here */
+} PerAddrBook;
+
+
+/*
+ * This keeps track of the state of the screen and information about all
+ * the address books. We usually only have one of these but sometimes
+ * we save a version of this state (with save_state) and re-call the
+ * address book functions. Then when we pop back up to where we were
+ * (with restore_state) the screen and the state of the address books
+ * is restored to what it was.
+ */
+typedef struct addrscreenstate {
+ PerAddrBook *adrbks; /* array of addrbooks */
+ int initialized, /* have we done at least simple init? */
+ n_addrbk, /* how many addrbooks are there */
+ how_many_personals, /* how many of those are personal? */
+ cur, /* current addrbook */
+ cur_row, /* currently selected line */
+ old_cur_row, /* previously selected line */
+ l_p_page; /* lines per (screen) page */
+ long top_ent; /* index in disp_list of top entry on screen */
+ int ro_warning, /* whether or not to give warning */
+ checkboxes, /* whether or not to display checkboxes */
+ selections, /* whether or not to display selections */
+ do_bold, /* display selections in bold */
+ no_op_possbl, /* user can't do anything with current conf */
+ zoomed, /* zoomed into view only selected entries */
+ config, /* called from config screen */
+ n_serv, /* how many directory servers are there */
+ n_impl; /* how many of those have impl bit set */
+#ifdef _WINDOWS
+ long last_ent; /* index of last known entry */
+#endif
+} AddrScrState;
+
+
+/*
+ * AddrBookScreen and AddrBookConfig are the maintenance screens, all the
+ * others are selection screens. The AddrBookConfig screen is an entry
+ * point from the Setup/Addressbooks command in the main menu. Those that
+ * end in Com are called from the pico HeaderEditor, either while in the
+ * composer or while editing an address book entry. SelectManyNicks
+ * returns a comma-separated list of nicknames. SelectAddrLccCom and
+ * SelectNicksCom return a comma-separated list of nicknames.
+ * SelectNickTake, SelectNickCom, and SelectNick all return a single
+ * nickname. The ones that returns multiple nicknames or multiple
+ * addresses all allow ListMode. They are SelectAddrLccCom,
+ * SelectNicksCom, and SelectMultNoFull.
+ */
+typedef enum {AddrBookScreen, /* maintenance screen */
+ AddrBookConfig, /* config screen */
+ SelectAddrLccCom, /* returns list of nicknames of lists */
+ SelectNicksCom, /* just like SelectAddrLccCom, but allows
+ selecting simple *and* list entries */
+ SelectNick, /* returns single nickname */
+ SelectNickTake, /* Same as SelectNick but different help */
+ SelectNickCom, /* Same as SelectNick but from composer */
+ SelectManyNicks, /* Returns list of nicks */
+ SelectAddr, /* Returns single address */
+ SelectAddrNoFull, /* Returns single address without fullname */
+ SelectMultNoFull /* Returns mult addresses without fullname */
+ } AddrBookArg;
+
+
+typedef struct save_state_struct {
+ AddrScrState *savep;
+ OpenStatus *stp;
+ DL_CACHE_S *dlc_to_warp_to;
+} SAVE_STATE_S;
+
+
+typedef struct act_list {
+ PerAddrBook *pab;
+ adrbk_cntr_t num,
+ num_in_dst;
+ unsigned int skip:1,
+ dup:1;
+} ACTION_LIST_S;
+
+
+typedef struct ta_abook_state {
+ PerAddrBook *pab;
+ SAVE_STATE_S state;
+} TA_STATE_S;
+
+
+/*
+ * Many of these should really only have a single value but we give them
+ * an array for uniformity.
+ */
+typedef struct _vcard_info {
+ char **nickname;
+ char **fullname;
+ char *first;
+ char *middle;
+ char *last;
+ char **fcc;
+ char **note;
+ char **title;
+ char **tel;
+ char **email;
+} VCARD_INFO_S;
+
+
+extern AddrScrState as;
+extern jmp_buf addrbook_changed_unexpectedly;
+extern long msgno_for_pico_callback;
+extern BODY *body_for_pico_callback;
+extern ENVELOPE *env_for_pico_callback;
+extern int ab_nesting_level;
+
+
+/*
+ * These constants are supposed to be suitable for use as longs where the longs
+ * are representing a line number or message number.
+ * These constants aren't suitable for use with type adrbk_cntr_t. There is
+ * a constant called NO_NEXT which you probably want for that.
+ */
+#define NO_LINE (2147483645L)
+#define CHANGED_CURRENT (NO_LINE + 1L)
+
+
+/*
+ * The do-while stuff is so these are statements and can be written with
+ * a following ; like a regular statement without worrying about braces and all.
+ */
+#define SKIP_SPACE(p) do{while(*p && *p == SPACE)p++;}while(0)
+#define SKIP_TO_TAB(p) do{while(*p && *p != TAB)p++;}while(0)
+#define RM_END_SPACE(start,end) \
+ do{char *_ptr = end; \
+ while(--_ptr >= start && *_ptr == SPACE)*_ptr = '\0';}while(0)
+#define REPLACE_NEWLINES_WITH_SPACE(p) \
+ do{register char *_qq; \
+ for(_qq = p; *_qq; _qq++) \
+ if(*_qq == '\n' || *_qq == '\r') \
+ *_qq = SPACE;}while(0)
+#define DELETED "#DELETED-"
+#define DELETED_LEN 9
+
+
+#define ONE_HUNDRED_DAYS (60L * 60L * 24L * 100L)
+
+/*
+ * When address book entries are deleted, they are left in the file
+ * with the nickname prepended with a string like #DELETED-96/01/25#,
+ * which stands for year 96, month 1, day 25 of the month. When one of
+ * these entries is more than ABOOK_DELETED_EXPIRE_TIME seconds old,
+ * then it will be totally removed from the address book the next time
+ * an adrbk_write() is done. This is for emergencies where somebody
+ * deletes something from their address book and would like to get it
+ * back. You get it back by editing the nickname field manually to remove
+ * the extra 18 characters off the front.
+ */
+#ifndef ABOOK_DELETED_EXPIRE_TIME
+#define ABOOK_DELETED_EXPIRE_TIME ONE_HUNDRED_DAYS
+#endif
+
+
+#ifdef ENABLE_LDAP
+typedef struct _cust_filt {
+ char *filt;
+ int combine;
+} CUSTOM_FILT_S;
+
+#define RUN_LDAP "LDAP: "
+#define LEN_RL 6
+#define QRUN_LDAP "\"LDAP: "
+#define LEN_QRL 7
+#define LDAP_DISP "[ LDAP Lookup ]"
+#endif
+
+
+/*
+ * There are no restrictions on the length of any of the fields, except that
+ * there are some restrictions in the current input routines.
+ */
+
+/*
+ * The on-disk address book has entries that look like:
+ *
+ * Nickname TAB Fullname TAB Address_Field TAB Fcc TAB Comment
+ *
+ * An entry may be broken over more than one line but only at certain
+ * spots. A continuation line starts with spaces (spaces, not white space).
+ * One place a line break can occur is after any of the TABs. The other
+ * place is in the middle of a list of addresses, between addresses.
+ * The Address_Field may be either a simple address without the fullname
+ * or brackets, or it may be an address list. An address list is
+ * distinguished by the fact that it begins with "(" and ends with ")".
+ * Addresses within a list are comma separated and each address in the list
+ * may be a full rfc822 address, including Fullname and so on.
+ *
+ * Examples:
+ * fred TAB Flintstone, Fred TAB fred@bedrock.net TAB fcc-flintstone TAB comment
+ * or
+ * fred TAB Flintstone, Fred TAB \n
+ * fred@bedrock.net TAB fcc-flintstone TAB \n
+ * comment
+ * somelist TAB Some List TAB (fred, \n
+ * Barney Rubble <barney@bedrock.net>, wilma@bedrock.net) TAB \n
+ * fcc-for-some-list TAB comment
+ */
+
+
+/* exported prototypes */
+AdrBk *adrbk_open(PerAddrBook *, char *, char *, size_t, int);
+int adrbk_is_in_sort_order(AdrBk *, int);
+adrbk_cntr_t adrbk_count(AdrBk *);
+AdrBk_Entry *adrbk_get_ae(AdrBk *, a_c_arg_t);
+AdrBk_Entry *adrbk_lookup_by_nick(AdrBk *, char *, adrbk_cntr_t *);
+AdrBk_Entry *adrbk_lookup_by_addr(AdrBk *, char *, adrbk_cntr_t *);
+char *adrbk_formatname(char *, char **, char **);
+void adrbk_clearrefs(AdrBk *);
+AdrBk_Entry *adrbk_newentry(void);
+AdrBk_Entry *copy_ae(AdrBk_Entry *);
+int adrbk_add(AdrBk *, a_c_arg_t, char *, char *, char *, char *,
+ char *, Tag, adrbk_cntr_t *, int *, int, int, int);
+int adrbk_append(AdrBk *, char *, char *, char *,
+ char *, char *, Tag, adrbk_cntr_t *);
+int adrbk_delete(AdrBk *, a_c_arg_t, int, int, int, int);
+int adrbk_listdel(AdrBk *, a_c_arg_t, char *);
+int adrbk_listdel_all(AdrBk *, a_c_arg_t);
+int adrbk_nlistadd(AdrBk *, a_c_arg_t,adrbk_cntr_t *,int *,char **,int,int,int);
+void adrbk_check_validity(AdrBk *, long);
+MAILSTREAM *adrbk_handy_stream(char *);
+void adrbk_close(AdrBk *);
+void adrbk_partial_close(AdrBk *);
+void note_closed_adrbk_stream(MAILSTREAM *);
+int adrbk_write(AdrBk *, a_c_arg_t, adrbk_cntr_t *, int *, int, int);
+void free_ae(AdrBk_Entry **);
+void exp_free(EXPANDED_S *);
+int exp_is_expanded(EXPANDED_S *, a_c_arg_t);
+int exp_howmany_expanded(EXPANDED_S *);
+int exp_any_expanded(EXPANDED_S *);
+adrbk_cntr_t exp_get_next(EXPANDED_S **);
+void exp_set_expanded(EXPANDED_S *, a_c_arg_t);
+void exp_unset_expanded(EXPANDED_S *, a_c_arg_t);
+int adrbk_sort(AdrBk *, a_c_arg_t, adrbk_cntr_t *, int);
+int any_ab_open(void);
+void init_ab_if_needed(void);
+int init_addrbooks(OpenStatus, int, int, int);
+void addrbook_reset(void);
+void addrbook_redo_sorts(void);
+AccessType adrbk_access(PerAddrBook *);
+void trim_remote_adrbks(void);
+void completely_done_with_adrbks(void);
+void init_abook(PerAddrBook *, OpenStatus);
+int adrbk_check_all_validity_now(void);
+int adrbk_check_and_fix(PerAddrBook *, int, int, int);
+int adrbk_check_and_fix_all(int, int, int);
+void adrbk_maintenance(void);
+char **parse_addrlist(char *);
+char *skip_to_next_addr(char *);
+void add_forced_entries(AdrBk *);
+
+
+#endif /* PITH_ADRBKLIB_INCLUDED */
diff --git a/pith/atttype.h b/pith/atttype.h
new file mode 100644
index 00000000..081aa7a9
--- /dev/null
+++ b/pith/atttype.h
@@ -0,0 +1,46 @@
+/*
+ * $Id: atttype.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_ATTTYPE_INCLUDED
+#define PITH_ATTTYPE_INCLUDED
+
+
+typedef struct attachment {
+ char *description;
+ BODY *body;
+ unsigned test_deferred:1;
+ unsigned can_display:4;
+ unsigned shown:1;
+ unsigned suppress_editorial:1;
+ char *number;
+ char size[25];
+} ATTACH_S;
+
+
+/*
+ * struct to help peruse a, possibly fragmented ala RFC 2231, parm list
+ */
+typedef struct parmlist {
+ PARAMETER *list,
+ *seen;
+ char attrib[32],
+ *value;
+} PARMLIST_S;
+
+
+/* exported protoypes */
+
+
+#endif /* PITH_ATTTYPE_INCLUDED */
diff --git a/pith/bitmap.h b/pith/bitmap.h
new file mode 100644
index 00000000..d79e160e
--- /dev/null
+++ b/pith/bitmap.h
@@ -0,0 +1,39 @@
+/*
+ * $Id: bitmap.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_BITMAP_INCLUDED
+#define PITH_BITMAP_INCLUDED
+
+
+#include "../pith/conftype.h"
+
+
+/*
+ * Max size of a bitmap based on largest customer: feature list count
+ * Problems if a screen's keymenu bitmap ever gets wider than feature list.
+ */
+#define BM_SIZE ((F_FEATURE_LIST_COUNT / 8) \
+ + ((F_FEATURE_LIST_COUNT % 8) ? 1 : 0))
+
+typedef unsigned char bitmap_t[BM_SIZE];
+
+/* clear entire bitmap */
+#define clrbitmap(map) memset((void *)(map), 0, (size_t)BM_SIZE)
+
+/* set entire bitmap */
+#define setbitmap(map) memset((void *)(map), 0xff, (size_t)BM_SIZE)
+
+
+#endif /* PITH_BITMAP_INCLUDED */
diff --git a/pith/bldaddr.c b/pith/bldaddr.c
new file mode 100644
index 00000000..9feeae38
--- /dev/null
+++ b/pith/bldaddr.c
@@ -0,0 +1,1263 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: bldaddr.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ buildaddr.c
+ Support for build_address function and low-level display cache.
+ ====*/
+
+
+#include "../pith/headers.h"
+#include "../pith/bldaddr.h"
+#include "../pith/ldap.h"
+#include "../pith/conf.h"
+#include "../pith/adrbklib.h"
+#include "../pith/copyaddr.h"
+#include "../pith/remote.h"
+#include "../pith/status.h"
+#include "../pith/search.h"
+#include "../pith/addrstring.h"
+#include "../pith/util.h"
+#include "../pith/ablookup.h"
+#include "../pith/news.h"
+#include "../pith/stream.h"
+#include "../pith/mailcmd.h"
+#include "../pith/osdep/pw_stuff.h"
+
+
+/*
+ * Jump back to this location if we discover that one of the open addrbooks
+ * has been changed by some other process.
+ */
+jmp_buf addrbook_changed_unexpectedly;
+
+
+/*
+ * Sometimes the address book code calls something outside of the address
+ * book and that calls back to the address book. For example, you could be
+ * in the address book mgmt screen, call ab_compose, and then a ^T could
+ * call back to an address book select screen. Or moving out of a line
+ * calls back to build_address. This keeps track of the nesting level so
+ * that we can know when it is safe to update an out of date address book.
+ * For example, if we know an address book is open and displayed, it isn't
+ * safe to update it because that would pull the cache out from under the
+ * displayed lines. If we don't have it displayed, then it is ok to close
+ * it and re-open it (adrbk_check_and_fix()).
+ */
+int ab_nesting_level = 0;
+
+
+
+/*
+ * This is like build_address() only it doesn't close
+ * everything down when it is done, and it doesn't open addrbooks that
+ * are already open. Other than that, it has the same functionality.
+ * It opens addrbooks that haven't been opened and saves and restores the
+ * addrbooks open states (if save_and_restore is set).
+ *
+ * Args: to -- the address to attempt expanding (see the
+ * description in expand_address)
+ * full_to -- a pointer to result
+ * This will be allocated here and freed by the caller.
+ * error -- a pointer to an error message, if non-null
+ * fcc -- a pointer to returned fcc, if non-null
+ * This will be allocated here and freed by the caller.
+ * *fcc should be null on entry.
+ * save_and_restore -- restore addrbook states when finished
+ *
+ * Results: 0 -- address is ok
+ * -1 -- address is not ok
+ * full_to contains the expanded address on success, or a copy of to
+ * on failure
+ * *error will point to an error message on failure it it is non-null
+ *
+ * Side effect: Can flush addrbook entry cache entries so they need to be
+ * re-fetched afterwords.
+ */
+int
+our_build_address(BuildTo to, char **full_to, char **error, char **fcc,
+ void (*save_and_restore_f)(int, SAVE_STATE_S *))
+{
+ int ret;
+
+ dprint((7, "- our_build_address - (%s)\n",
+ (to.type == Str) ? (to.arg.str ? to.arg.str : "nul")
+ : (to.arg.abe->nickname ? to.arg.abe->nickname
+ : "no nick")));
+
+ if((to.type == Str && !to.arg.str) || (to.type == Abe && !to.arg.abe)){
+ if(full_to)
+ *full_to = cpystr("");
+ ret = 0;
+ }
+ else
+ ret = build_address_internal(to, full_to, error, fcc, NULL, NULL,
+ save_and_restore_f, 0, NULL);
+
+ dprint((8, " our_build_address says %s address\n",
+ ret ? "BAD" : "GOOD"));
+
+ return(ret);
+}
+
+
+
+#define FCC_SET 1 /* Fcc was set */
+#define LCC_SET 2 /* Lcc was set */
+#define FCC_NOREPO 4 /* Fcc was set in a non-reproducible way */
+#define LCC_NOREPO 8 /* Lcc was set in a non-reproducible way */
+/*
+ * Given an address, expand it based on address books, local domain, etc.
+ * This will open addrbooks if needed before checking (actually one of
+ * its children will open them).
+ *
+ * Args: to -- The given address to expand (see the description
+ * in expand_address)
+ * full_to -- Returned value after parsing to.
+ * error -- This gets pointed at error message, if any
+ * fcc -- Returned value of fcc for first addr in to
+ * no_repo -- Returned value, set to 1 if the fcc or lcc we're
+ * returning is not reproducible from the expanded
+ * address. That is, if we were to run
+ * build_address_internal again on the resulting full_to,
+ * we wouldn't get back the fcc again. For example,
+ * if we expand a list and use the list fcc from the
+ * addrbook, the full_to no longer contains the
+ * information that this was originally list foo.
+ * save_and_restore -- restore addrbook state when done
+ *
+ * Result: 0 is returned if address was OK,
+ * -1 if address wasn't OK.
+ * The address is expanded, fully-qualified, and personal name added.
+ *
+ * Input may have more than one address separated by commas.
+ *
+ * Side effect: Can flush addrbook entry cache entries so they need to be
+ * re-fetched afterwords.
+ */
+int
+build_address_internal(BuildTo to, char **full_to, char **error, char **fcc,
+ int *no_repo, char **lcc,
+ void (*save_and_restore_f)(int, SAVE_STATE_S *),
+ int simple_verify, int *mangled)
+{
+ ADDRESS *a;
+ int loop, i;
+ int tried_route_addr_hack = 0;
+ int did_set = 0;
+ char *tmp = NULL;
+ SAVE_STATE_S state;
+ PerAddrBook *pab;
+
+ dprint((8, "- build_address_internal - (%s)\n",
+ (to.type == Str) ? (to.arg.str ? to.arg.str : "nul")
+ : (to.arg.abe->nickname ? to.arg.abe->nickname
+ : "no nick")));
+
+ init_ab_if_needed();
+ if(save_and_restore_f)
+ (*save_and_restore_f)(SAR_SAVE, &state);
+
+start:
+ loop = 0;
+ ps_global->c_client_error[0] = '\0';
+ wp_exit = wp_nobail = 0;
+
+ a = expand_address(to, ps_global->maildomain,
+ F_OFF(F_QUELL_LOCAL_LOOKUP, ps_global)
+ ? ps_global->maildomain : NULL,
+ &loop, fcc, &did_set, lcc, error,
+ 0, simple_verify, mangled);
+
+ /*
+ * If the address is a route-addr, expand_address() will have rejected
+ * it unless it was enclosed in brackets, since route-addrs can't stand
+ * alone. Try it again with brackets. We should really be checking
+ * each address in the list of addresses instead of assuming there is
+ * only one address, but we don't want to have this function know
+ * all about parsing rfc822 addrs.
+ */
+ if(!tried_route_addr_hack &&
+ ps_global->c_client_error[0] != '\0' &&
+ ((to.type == Str && to.arg.str && to.arg.str[0] == '@') ||
+ (to.type == Abe && to.arg.abe->tag == Single &&
+ to.arg.abe->addr.addr[0] == '@'))){
+ BuildTo bldto;
+
+ tried_route_addr_hack++;
+
+ tmp = (char *)fs_get((size_t)(MAX_ADDR_FIELD + 3));
+
+ /* add brackets to whole thing */
+ strncpy(tmp, "<", MAX_ADDR_FIELD+3);
+ tmp[MAX_ADDR_FIELD+3-1] = '\0';
+ if(to.type == Str)
+ strncat(tmp, to.arg.str, MAX_ADDR_FIELD+3-strlen(tmp)-1);
+ else
+ strncat(tmp, to.arg.abe->addr.addr, MAX_ADDR_FIELD+3-strlen(tmp)-1);
+
+ tmp[MAX_ADDR_FIELD+3-1] = '\0';
+ strncat(tmp, ">", MAX_ADDR_FIELD+3-strlen(tmp)-1);
+ tmp[MAX_ADDR_FIELD+3-1] = '\0';
+
+ loop = 0;
+ ps_global->c_client_error[0] = '\0';
+
+ bldto.type = Str;
+ bldto.arg.str = tmp;
+
+ if(a)
+ mail_free_address(&a);
+
+ /* try it */
+ a = expand_address(bldto, ps_global->maildomain,
+ F_OFF(F_QUELL_LOCAL_LOOKUP, ps_global)
+ ? ps_global->maildomain : NULL,
+ &loop, fcc, &did_set, lcc, error,
+ 0, simple_verify, mangled);
+
+ /* if no error this time, use it */
+ if(ps_global->c_client_error[0] == '\0'){
+ if(save_and_restore_f)
+ (*save_and_restore_f)(SAR_RESTORE, &state);
+
+ /*
+ * Clear references so that Addrbook Entry caching in adrbklib.c
+ * is allowed to throw them out of cache.
+ */
+ for(i = 0; i < as.n_addrbk; i++){
+ pab = &as.adrbks[i];
+ if(pab->ostatus == Open || pab->ostatus == NoDisplay)
+ adrbk_clearrefs(pab->address_book);
+ }
+
+ goto ok;
+ }
+ else{ /* go back and use what we had before, so we get the error */
+ if(tmp)
+ fs_give((void **)&tmp);
+
+ tmp = NULL;
+ goto start;
+ }
+ }
+
+ if(save_and_restore_f)
+ (*save_and_restore_f)(SAR_RESTORE, &state);
+
+ /*
+ * Clear references so that Addrbook Entry caching in adrbklib.c
+ * is allowed to throw them out of cache.
+ */
+ for(i = 0; i < as.n_addrbk; i++){
+ pab = &as.adrbks[i];
+ if(pab->ostatus == Open || pab->ostatus == NoDisplay)
+ adrbk_clearrefs(pab->address_book);
+ }
+
+ if(ps_global->c_client_error[0] != '\0'){
+ /* Parse error. Return string as is and error message */
+ if(full_to){
+ if(to.type == Str)
+ *full_to = cpystr(to.arg.str);
+ else{
+ if(to.arg.abe->nickname && to.arg.abe->nickname[0])
+ *full_to = cpystr(to.arg.abe->nickname);
+ else if(to.arg.abe->tag == Single)
+ *full_to = cpystr(to.arg.abe->addr.addr);
+ else
+ *full_to = cpystr("");
+ }
+ }
+
+ if(error != NULL){
+ /* display previous error and add new one */
+ if(*error){
+ q_status_message(SM_ORDER, 3, 5, *error);
+ display_message('x');
+ fs_give((void **)error);
+ }
+
+ *error = cpystr(ps_global->c_client_error);
+ }
+
+ dprint((2,
+ "build_address_internal returning parse error: %s\n",
+ ps_global->c_client_error ? ps_global->c_client_error : "?"));
+ if(a)
+ mail_free_address(&a);
+
+ if(tmp)
+ fs_give((void **)&tmp);
+
+ if(mangled){
+ if(ps_global->mangled_screen)
+ *mangled |= BUILDER_SCREEN_MANGLED;
+ else if(ps_global->mangled_footer)
+ *mangled |= BUILDER_FOOTER_MANGLED;
+ }
+
+ return -1;
+ }
+
+ if(mangled){
+ if(ps_global->mangled_screen)
+ *mangled |= BUILDER_SCREEN_MANGLED;
+ else if(ps_global->mangled_footer)
+ *mangled |= BUILDER_FOOTER_MANGLED;
+ }
+
+ /*
+ * If there's a loop in the addressbook, we modify the address and
+ * send an error back, but we still return 0.
+ */
+ok:
+ if(loop && error != NULL){
+ /* display previous error and add new one */
+ if(*error){
+ q_status_message(SM_ORDER, 3, 5, *error);
+ display_message('x');
+ fs_give((void **)error);
+ }
+
+ *error = cpystr(_("Loop or Duplicate detected in addressbook!"));
+ }
+
+
+ if(full_to){
+ if(simple_verify){
+ if(tmp){
+ *full_to = tmp; /* add the brackets to route addr */
+ tmp = NULL;
+ }
+ else{
+ /* try to return what they sent us */
+ if(to.type == Str)
+ *full_to = cpystr(to.arg.str);
+ else{
+ if(to.arg.abe->nickname && to.arg.abe->nickname[0])
+ *full_to = cpystr(to.arg.abe->nickname);
+ else if(to.arg.abe->tag == Single)
+ *full_to = cpystr(to.arg.abe->addr.addr);
+ else
+ *full_to = cpystr("");
+ }
+ }
+ }
+ else{
+ RFC822BUFFER rbuf;
+ size_t len;
+
+ len = est_size(a);
+ *full_to = (char *) fs_get(len * sizeof(char));
+ (*full_to)[0] = '\0';
+ /*
+ * Assume that quotes surrounding the whole personal name are
+ * not meant to be literal quotes. That is, the name
+ * "Joe College, PhD." is quoted so that we won't do the
+ * switcheroo of Last, First, not so that the quotes will be
+ * literal. Rfc822_write_address will put the quotes back if they
+ * are needed, so Joe College would end up looking like
+ * "Joe College, PhD." <joe@somewhere.edu> but not like
+ * "\"Joe College, PhD.\"" <joe@somewhere.edu>.
+ */
+ strip_personal_quotes(a);
+ rbuf.f = dummy_soutr;
+ rbuf.s = NULL;
+ rbuf.beg = *full_to;
+ rbuf.cur = *full_to;
+ rbuf.end = (*full_to)+len-1;
+ rfc822_output_address_list(&rbuf, a, 0L, NULL);
+ *rbuf.cur = '\0';
+ }
+ }
+
+ if(no_repo && (did_set & FCC_NOREPO || did_set & LCC_NOREPO))
+ *no_repo = 1;
+
+ /*
+ * The condition in the leading if means that addressbook fcc's
+ * override the fcc-rule (because did_set will be set).
+ */
+ if(fcc && !(did_set & FCC_SET)){
+ char *fcc_got = NULL;
+
+ if((ps_global->fcc_rule == FCC_RULE_LAST
+ || ps_global->fcc_rule == FCC_RULE_CURRENT)
+ && strcmp(fcc_got = get_fcc(NULL), ps_global->VAR_DEFAULT_FCC)){
+ if(*fcc)
+ fs_give((void **)fcc);
+
+ *fcc = cpystr(fcc_got);
+ }
+ else if(a && a->host){ /* not group syntax */
+ if(*fcc)
+ fs_give((void **)fcc);
+
+ if(!tmp)
+ tmp = (char *)fs_get((size_t)200);
+
+ if((ps_global->fcc_rule == FCC_RULE_RECIP ||
+ ps_global->fcc_rule == FCC_RULE_NICK_RECIP) &&
+ get_uname(a ? a->mailbox : NULL, tmp, 200))
+ *fcc = cpystr(tmp);
+ else
+ *fcc = cpystr(ps_global->VAR_DEFAULT_FCC);
+ }
+ else{ /* first addr is group syntax */
+ if(!*fcc)
+ *fcc = cpystr(ps_global->VAR_DEFAULT_FCC);
+ /* else, leave it alone */
+ }
+
+ if(fcc_got)
+ fs_give((void **)&fcc_got);
+ }
+
+ if(a)
+ mail_free_address(&a);
+
+ if(tmp)
+ fs_give((void **)&tmp);
+
+ if(wp_exit)
+ return -1;
+
+ return 0;
+}
+
+
+/*
+ * Expand an address string against the address books, local names, and domain.
+ *
+ * Args: to -- this is either an address string to parse (one or more
+ * address strings separated by commas) or it is an
+ * AdrBk_Entry, in which case it refers to a single addrbook
+ * entry. If it is an abe, then it is treated the same as
+ * if the nickname of this entry was passed in and we
+ * looked it up in the addrbook, except that it doesn't
+ * actually have to have a non-null nickname.
+ * userdomain -- domain the user is in
+ * localdomain -- domain of the password file (usually same as userdomain)
+ * loop_detected -- pointer to an int we set if we detect a loop in the
+ * address books (or a duplicate in a list)
+ * fcc -- Returned value of fcc for first addr in a_string
+ * did_set -- expand_address set the fcc (need this in case somebody
+ * sets fcc explicitly to a value equal to default-fcc)
+ * simple_verify -- don't add list full names or expand 2nd level lists
+ *
+ * Result: An adrlist of expanded addresses is returned
+ *
+ * If the localdomain is NULL, then no lookup against the password file will
+ * be done.
+ */
+ADDRESS *
+expand_address(BuildTo to, char *userdomain, char *localdomain, int *loop_detected,
+ char **fcc, int *did_set, char **lcc, char **error, int recursing,
+ int simple_verify, int *mangled)
+{
+ size_t domain_length, length;
+ ADDRESS *adr, *a, *a_tail, *adr2, *a2, *a_temp, *wp_a;
+ AdrBk_Entry *abe, *abe2;
+ char *list, *l1, **l2;
+ char *tmp_a_string, *q;
+ BuildTo bldto;
+ static char *fakedomain;
+
+ dprint((9, "- expand_address - (%s)\n",
+ (to.type == Str) ? (to.arg.str ? to.arg.str : "nul")
+ : (to.arg.abe->nickname ? to.arg.abe->nickname
+ : "no nick")));
+ /*
+ * We use the domain "@" to detect an unqualified address. If it comes
+ * back from rfc822_parse_adrlist with the host part set to "@", then
+ * we know it must have been unqualified (so we should look it up in the
+ * addressbook). Later, we also use a c-client hack. If an ADDRESS has
+ * a host part that begins with @ then rfc822_write_address()
+ * will write only the local part and leave off the @domain part.
+ *
+ * We also malloc enough space here so that we can strcpy over host below.
+ */
+ domain_length = MAX(localdomain!=NULL ? strlen(localdomain) : (size_t)0,
+ userdomain!=NULL ? strlen(userdomain) : (size_t)0);
+ if(!recursing){
+ fakedomain = (char *)fs_get(domain_length + 1);
+ memset((void *)fakedomain, '@', domain_length);
+ fakedomain[domain_length] = '\0';
+ }
+
+ adr = NULL;
+
+ if(to.type == Str){
+ /* rfc822_parse_adrlist feels free to destroy input so send copy */
+ tmp_a_string = cpystr(to.arg.str);
+ /* remove trailing comma */
+ for(q = tmp_a_string + strlen(tmp_a_string) - 1;
+ q >= tmp_a_string && (*q == SPACE || *q == ',');
+ q--)
+ *q = '\0';
+
+ if(as.n_impl)
+ mail_parameters(NIL, SET_PARSEPHRASE, (void *)massage_phrase_addr);
+
+ rfc822_parse_adrlist(&adr, tmp_a_string, fakedomain);
+
+ if(as.n_impl)
+ mail_parameters(NIL, SET_PARSEPHRASE, NULL);
+
+ /*
+ * Short circuit the process if there was a parsing error.
+ */
+ if(!recursing && ps_global->c_client_error[0] != '\0')
+ mail_free_address(&adr);
+
+ fs_give((void **)&tmp_a_string);
+ }
+ else{
+ if(!to.arg.abe ||
+ (to.arg.abe->tag == Single &&
+ (!to.arg.abe->addr.addr || to.arg.abe->addr.addr[0] == '\0')) ||
+ (to.arg.abe->tag == List &&
+ (!to.arg.abe->addr.list || !to.arg.abe->addr.list[0] ||
+ to.arg.abe->addr.list[0][0] == '\0'))){
+ adr = NULL;
+ }
+ else{
+ /* if we've already looked it up, fake an adr */
+ adr = mail_newaddr();
+ adr->mailbox = cpystr(to.arg.abe->nickname);
+ adr->host = cpystr(fakedomain);
+ }
+ }
+
+ for(a = adr, a_tail = adr; a;){
+
+ /* start or end of c-client group syntax */
+ if(!a->host){
+ a_tail = a;
+ a = a->next;
+ continue;
+ }
+ else if(a->host[0] != '@'){
+ /* Already fully qualified hostname */
+ a_tail = a;
+ a = a->next;
+ }
+ else{
+ /*
+ * Hostname is "@" indicating name wasn't qualified.
+ * Need to look up in address book, and the password file.
+ * If no match then fill in the local domain for host.
+ */
+ if(to.type == Str)
+ abe = adrbk_lookup_with_opens_by_nick(a->mailbox,
+ !recursing,
+ (int *)NULL, -1);
+ else
+ abe = to.arg.abe;
+
+ if(simple_verify && abe == NULL){
+ /*--- Move to next address in list -----*/
+ a_tail = a;
+ a = a->next;
+ }
+ else if(abe == NULL){
+ WP_ERR_S wp_err;
+
+ if(F_OFF(F_COMPOSE_REJECTS_UNQUAL, ps_global)){
+ if(localdomain != NULL && a->personal == NULL){
+ /* lookup in passwd file for local full name */
+ a->personal = local_name_lookup(a->mailbox);
+ /* we know a->host is long enough for localdomain */
+ if(a->personal){
+ /* we know a->host is long enough for userdomain */
+ strncpy(a->host, localdomain, domain_length+1);
+ a->host[domain_length] = '\0';
+ }
+ }
+ }
+
+ /*
+ * Didn't find it in address book or password
+ * file, try white pages.
+ */
+ memset(&wp_err, 0, sizeof(wp_err));
+ wp_err.mangled = mangled;
+ if(!wp_exit && a->personal == NULL &&
+ (wp_a = wp_lookups(a->mailbox, &wp_err, recursing))){
+ if(wp_a->mailbox && wp_a->mailbox[0] &&
+ wp_a->host && wp_a->host[0]){
+ a->personal = wp_a->personal;
+ if(a->adl)fs_give((void **)&a->adl);
+ a->adl = wp_a->adl;
+ if(a->mailbox)fs_give((void **)&a->mailbox);
+ a->mailbox = wp_a->mailbox;
+ if(a->host)fs_give((void **)&a->host);
+ a->host = wp_a->host;
+ }
+
+ fs_give((void **)&wp_a);
+ }
+
+ if(wp_err.error){
+ /*
+ * If wp_err has already been displayed long enough
+ * just get rid of it. Otherwise, try to fit it in
+ * with any other error messages we have had.
+ * In that case we may display error messages in a
+ * weird order. We'll have to see if this is a problem
+ * in real life.
+ */
+ if(status_message_remaining() && error && !wp_exit){
+ if(*error){
+ q_status_message(SM_ORDER, 3, 5, *error);
+ display_message('x');
+ fs_give((void **)error);
+ }
+
+ *error = wp_err.error;
+ }
+ else
+ fs_give((void **)&wp_err.error);
+ }
+
+ /* still haven't found it */
+ if(a->host[0] == '@' && !wp_nobail){
+ int space_phrase;
+
+ /*
+ * Figure out if there is a space in the mailbox so
+ * that user probably meant it to resolve on the
+ * directory server.
+ */
+ space_phrase = (a->mailbox && strindex(a->mailbox, SPACE));
+
+ if(!wp_nobail && (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global) ||
+ (space_phrase && as.n_impl))){
+ char ebuf[200];
+
+ /* TRANSLATORS: The first %s is a mailbox, the second is either
+ directory or addressbook */
+ snprintf(ebuf, sizeof(ebuf), _("Address for \"%s\" not in %s"),
+ a->mailbox,
+ (space_phrase && as.n_impl) ? _("directory")
+ : _("addressbook"));
+
+ if(error){
+ /* display previous error and add new one */
+ if(*error){
+ if(!wp_exit){
+ q_status_message(SM_ORDER, 3, 5, *error);
+ display_message('x');
+ }
+
+ fs_give((void **)error);
+ }
+
+ if(!wp_exit)
+ *error = cpystr(ebuf);
+ }
+
+ if(!wp_exit)
+ strncpy(ps_global->c_client_error, ebuf, 200);
+
+ if(!recursing)
+ fs_give((void **)&fakedomain);
+
+ if(adr)
+ mail_free_address(&adr);
+
+ return(adr);
+ }
+ else if(wp_err.wp_err_occurred){
+ if(!recursing){
+ if(error && *error && !wp_exit)
+ strncpy(ps_global->c_client_error, *error, sizeof(ps_global->c_client_error));
+ else
+ strncpy(ps_global->c_client_error, " ", sizeof(ps_global->c_client_error));
+
+ ps_global->c_client_error[sizeof(ps_global->c_client_error)-1] = '\0';
+
+ fs_give((void **)&fakedomain);
+ if(adr)
+ mail_free_address(&adr);
+
+ return(adr);
+ }
+ }
+ else{
+ /* we know a->host is long enough for userdomain */
+ strncpy(a->host, userdomain, domain_length+1);
+ a->host[domain_length] = '\0';
+ }
+ }
+
+ /*--- Move to next address in list -----*/
+ a_tail = a;
+ a = a->next;
+
+ }
+ /* expand first list, but not others if simple_verify */
+ else if(abe->tag == List && simple_verify && recursing){
+ /*--- Move to next address in list -----*/
+ a_tail = a;
+ a = a->next;
+ }
+ else{
+ /*
+ * There was a match in the address book. We have to do a lot
+ * here because the item from the address book might be a
+ * distribution list. Treat the string just like an address
+ * passed in to parse and recurse on it. Then combine
+ * the personal names from address book. Lastly splice
+ * result into address list being processed
+ */
+
+ /* first addr in list and fcc needs to be filled in */
+ if(!recursing && a == adr && fcc && !(*did_set & FCC_SET)){
+ /*
+ * Easy case for fcc. This is a nickname that has
+ * an fcc associated with it.
+ */
+ if(abe->fcc && abe->fcc[0]){
+ if(*fcc)
+ fs_give((void **)fcc);
+
+ if(!strcmp(abe->fcc, "\"\""))
+ *fcc = cpystr("");
+ else
+ *fcc = cpystr(abe->fcc);
+
+ /*
+ * After we expand the list, we no longer remember
+ * that it came from this address book entry, so
+ * we wouldn't be able to set the fcc again based
+ * on the result. This tells our caller to remember
+ * that for us.
+ */
+ *did_set |= (FCC_SET | FCC_NOREPO);
+ }
+ /*
+ * else if fcc-rule=fcc-by-nickname, use that
+ */
+ else if(abe->nickname && abe->nickname[0] &&
+ (ps_global->fcc_rule == FCC_RULE_NICK ||
+ ps_global->fcc_rule == FCC_RULE_NICK_RECIP)){
+ if(*fcc)
+ fs_give((void **)fcc);
+
+ *fcc = cpystr(abe->nickname);
+ /*
+ * After we expand the list, we no longer remember
+ * that it came from this address book entry, so
+ * we wouldn't be able to set the fcc again based
+ * on the result. This tells our caller to remember
+ * that for us.
+ */
+ *did_set |= (FCC_SET | FCC_NOREPO);
+ }
+ }
+
+ /* lcc needs to be filled in */
+ if(a == adr &&
+ lcc &&
+ (!*lcc || !**lcc)){
+ ADDRESS *atmp;
+ char *tmp;
+
+ /* return fullname for To line */
+ if(abe->fullname && *abe->fullname){
+ RFC822BUFFER rbuf;
+ size_t l, len;
+
+ if(*lcc)
+ fs_give((void **)lcc);
+
+ atmp = mail_newaddr();
+ atmp->mailbox = cpystr(abe->fullname);
+ len = est_size(atmp);
+ tmp = (char *) fs_get(len * sizeof(char));
+ tmp[0] = '\0';
+ /* write the phrase with quoting */
+ rbuf.f = dummy_soutr;
+ rbuf.s = NULL;
+ rbuf.beg = tmp;
+ rbuf.cur = tmp;
+ rbuf.end = tmp+len-1;
+ rfc822_output_address_list(&rbuf, atmp, 0L, NULL);
+ *rbuf.cur = '\0';
+ l = strlen(tmp)+1;
+ *lcc = (char *) fs_get((l+1) * sizeof(char));
+ strncpy(*lcc, tmp, l);
+ (*lcc)[l] = '\0';
+ strncat(*lcc, ";", l+1-strlen(*lcc)-1);
+ (*lcc)[l] = '\0';
+ mail_free_address(&atmp);
+ fs_give((void **)&tmp);
+ *did_set |= (LCC_SET | LCC_NOREPO);
+ }
+ }
+
+ if(recursing && abe->referenced){
+ /*---- caught an address loop! ----*/
+ fs_give(((void **)&a->host));
+ (*loop_detected)++;
+ a->host = cpystr("***address-loop-in-addressbooks***");
+ continue;
+ }
+
+ abe->referenced++; /* For address loop detection */
+ if(abe->tag == List){
+ length = 0;
+ for(l2 = abe->addr.list; *l2; l2++)
+ length += (strlen(*l2) + 1);
+
+ list = (char *)fs_get(length + 1);
+ list[0] = '\0';
+ l1 = list;
+ for(l2 = abe->addr.list; *l2; l2++){
+ if(l1 != list && length+1-(l1-list) > 0)
+ *l1++ = ',';
+
+ strncpy(l1, *l2, length+1-(l1-list));
+ if(l1 > &list[length])
+ l1 = &list[length];
+
+ list[length] = '\0';
+
+ l1 += strlen(l1);
+ }
+
+ bldto.type = Str;
+ bldto.arg.str = list;
+ adr2 = expand_address(bldto, userdomain, localdomain,
+ loop_detected, fcc, did_set,
+ lcc, error, 1, simple_verify,
+ mangled);
+ fs_give((void **)&list);
+ }
+ else if(abe->tag == Single){
+ if(strucmp(abe->addr.addr, a->mailbox)){
+ bldto.type = Str;
+ bldto.arg.str = abe->addr.addr;
+ adr2 = expand_address(bldto, userdomain,
+ localdomain, loop_detected,
+ fcc, did_set, lcc,
+ error, 1, simple_verify,
+ mangled);
+ }
+ else{
+ /*
+ * A loop within plain single entry is ignored.
+ * Set up so later code thinks we expanded.
+ */
+ adr2 = mail_newaddr();
+ adr2->mailbox = cpystr(abe->addr.addr);
+ adr2->host = cpystr(userdomain);
+ adr2->adl = cpystr(a->adl);
+ }
+ }
+
+ abe->referenced--; /* Janet Jackson <janet@dialix.oz.au> */
+ if(adr2 == NULL){
+ /* expanded to nothing, hack out of list */
+ a_temp = a;
+ if(a == adr){
+ adr = a->next;
+ a = adr;
+ a_tail = adr;
+ }
+ else{
+ a_tail->next = a->next;
+ a = a->next;
+ }
+
+ a_temp->next = NULL; /* So free won't do whole list */
+ mail_free_address(&a_temp);
+ continue;
+ }
+
+ /*
+ * Personal names: If the expanded address has a personal
+ * name and the address book entry is a list with a fullname,
+ * tack the full name from the address book on in front.
+ * This mainly occurs with a distribution list where the
+ * list has a full name, and the first person in the list also
+ * has a full name.
+ *
+ * This algorithm doesn't work very well if lists are
+ * included within lists, but it's not clear what would
+ * be better.
+ */
+ if(abe->fullname && abe->fullname[0]){
+ if(adr2->personal && adr2->personal[0]){
+ if(abe->tag == List){
+ /* combine list name and existing name */
+ char *name;
+
+ if(!simple_verify){
+ size_t l;
+
+ l = strlen(adr2->personal) + strlen(abe->fullname) + 4;
+ name = (char *) fs_get((l+1) * sizeof(char));
+ snprintf(name, l+1, "%s -- %s", abe->fullname,
+ adr2->personal);
+ fs_give((void **)&adr2->personal);
+ adr2->personal = name;
+ }
+ }
+ else{
+ /* replace with nickname fullname */
+ fs_give((void **)&adr2->personal);
+ adr2->personal = adrbk_formatname(abe->fullname,
+ NULL, NULL);
+ }
+ }
+ else{
+ if(abe->tag != List || !simple_verify){
+ if(adr2->personal)
+ fs_give((void **)&adr2->personal);
+
+ adr2->personal = adrbk_formatname(abe->fullname,
+ NULL, NULL);
+ }
+ }
+ }
+
+ /* splice new list into old list and remove replaced addr */
+ for(a2 = adr2; a2->next != NULL; a2 = a2->next)
+ ;/* do nothing */
+
+ a2->next = a->next;
+ if(a == adr)
+ adr = adr2;
+ else
+ a_tail->next = adr2;
+
+ /* advance to next item, and free replaced ADDRESS */
+ a_tail = a2;
+ a_temp = a;
+ a = a->next;
+ a_temp->next = NULL; /* So free won't do whole list */
+ mail_free_address(&a_temp);
+ }
+ }
+
+ if((a_tail == adr && fcc && !(*did_set & FCC_SET))
+ || !a_tail->personal)
+ /*
+ * This looks for the addressbook entry that matches the given
+ * address. It looks through all the addressbooks
+ * looking for an exact match and then returns that entry.
+ */
+ abe2 = address_to_abe(a_tail);
+ else
+ abe2 = NULL;
+
+ /*
+ * If there is no personal name yet but we found the address in
+ * an address book, then we take the fullname from that address
+ * book entry and use it. One consequence of this is that if I
+ * have an address book entry with address hubert@cac.washington.edu
+ * and a fullname of Steve Hubert, then there is no way I can
+ * send mail to hubert@cac.washington.edu without having the
+ * personal name filled in for me.
+ */
+ if(!a_tail->personal && abe2 && abe2->fullname && abe2->fullname[0])
+ a_tail->personal = adrbk_formatname(abe2->fullname, NULL, NULL);
+
+ /* if it's first addr in list and fcc hasn't been set yet */
+ if(!recursing && a_tail == adr && fcc && !(*did_set & FCC_SET)){
+ if(abe2 && abe2->fcc && abe2->fcc[0]){
+ if(*fcc)
+ fs_give((void **)fcc);
+
+ if(!strcmp(abe2->fcc, "\"\""))
+ *fcc = cpystr("");
+ else
+ *fcc = cpystr(abe2->fcc);
+
+ *did_set |= FCC_SET;
+ }
+ else if(abe2 && abe2->nickname && abe2->nickname[0] &&
+ (ps_global->fcc_rule == FCC_RULE_NICK ||
+ ps_global->fcc_rule == FCC_RULE_NICK_RECIP)){
+ if(*fcc)
+ fs_give((void **)fcc);
+
+ *fcc = cpystr(abe2->nickname);
+ *did_set |= FCC_SET;
+ }
+ }
+
+ /*
+ * Lcc needs to be filled in.
+ * Bug: if ^T select was used to put the list in the lcc field, then
+ * the list will have been expanded already and the fullname for
+ * the list will be mixed with the initial fullname in the list,
+ * and we don't have anyway to tell them apart. We could look for
+ * the --. We could change expand_address so it doesn't combine
+ * those two addresses.
+ */
+ if(adr &&
+ a_tail == adr &&
+ lcc &&
+ (!*lcc || !**lcc)){
+ if(adr->personal){
+ ADDRESS *atmp;
+ char *tmp;
+ RFC822BUFFER rbuf;
+ size_t l, len;
+
+ if(*lcc)
+ fs_give((void **)lcc);
+
+ atmp = mail_newaddr();
+ atmp->mailbox = cpystr(adr->personal);
+ len = est_size(atmp);
+ tmp = (char *) fs_get(len * sizeof(char));
+ tmp[0] = '\0';
+ /* write the phrase with quoting */
+ rbuf.f = dummy_soutr;
+ rbuf.s = NULL;
+ rbuf.beg = tmp;
+ rbuf.cur = tmp;
+ rbuf.end = tmp+len-1;
+ rfc822_output_address_list(&rbuf, atmp, 0L, NULL);
+ *rbuf.cur = '\0';
+ l = strlen(tmp)+1;
+ *lcc = (char *) fs_get((l+1) * sizeof(char));
+ strncpy(*lcc, tmp, l);
+ (*lcc)[l] = '\0';
+ strncat(*lcc, ";", l+1-strlen(*lcc)-1);
+ (*lcc)[l] = '\0';
+ mail_free_address(&atmp);
+ fs_give((void **)&tmp);
+ *did_set |= (LCC_SET | LCC_NOREPO);
+ }
+ }
+ }
+
+ if(!recursing)
+ fs_give((void **)&fakedomain);
+
+ return(adr);
+}
+
+
+/*
+ * This is a call back from rfc822_parse_adrlist. If we return NULL then
+ * it does its regular parsing. However, if we return and ADDRESS then it
+ * returns that address to us.
+ *
+ * We want a phrase with no address to be parsed just like a nickname would
+ * be in expand_address. That is, the phrase gets put in the mailbox name
+ * and the host is set to the fakedomain. Then expand_address will try to
+ * look that up using wp_lookup().
+ *
+ * Args phrase -- The start of the phrase of the address
+ * end -- The first character after the phrase
+ * host -- The defaulthost that was passed to rfc822_parse_adrlist
+ *
+ * Returns An allocated address, or NULL.
+ */
+ADDRESS *
+massage_phrase_addr(char *phrase, char *end, char *host)
+{
+ ADDRESS *adr = NULL;
+ size_t size;
+
+ if((size = end - phrase) > 0){
+ char *mycopy;
+
+ mycopy = (char *)fs_get((size+1) * sizeof(char));
+ strncpy(mycopy, phrase, size);
+ mycopy[size] = '\0';
+ removing_trailing_white_space(mycopy);
+
+ /*
+ * If it is quoted we want to leave it alone. It will be treated
+ * like an atom in parse_adrlist anyway, which is what we want to
+ * have happen, and we'd have to remove the quotes ourselves here
+ * if we did it. The problem then is that we don't know if we
+ * removed the quotes here or they just weren't there in the
+ * first place.
+ */
+ if(*mycopy == '"' && mycopy[strlen(mycopy)-1] == '"')
+ fs_give((void **)&mycopy);
+ else{
+ adr = mail_newaddr();
+ adr->mailbox = mycopy;
+ adr->host = cpystr(host);
+ }
+ }
+
+ return(adr);
+}
+
+
+/*
+ * Run through the adrlist "adr" and strip off any enclosing quotes
+ * around personal names. That is, change "Joe L. Normal" to
+ * Joe L. Normal.
+ */
+void
+strip_personal_quotes(struct mail_address *adr)
+{
+ int len;
+ register char *p, *q;
+
+ while(adr){
+ if(adr->personal){
+ len = strlen(adr->personal);
+ if(len > 1
+ && adr->personal[0] == '"'
+ && adr->personal[len-1] == '"'){
+ adr->personal[len-1] = '\0';
+ p = adr->personal;
+ q = p + 1;
+ while((*p++ = *q++) != '\0')
+ ;
+ }
+ }
+
+ adr = adr->next;
+ }
+}
+
+
+static char *last_fcc_used;
+/*
+ * Returns alloc'd fcc.
+ */
+char *
+get_fcc(char *fcc_arg)
+{
+ char *fcc;
+
+ /*
+ * Use passed in arg unless it is the same as default (and then
+ * may use that anyway below).
+ */
+ if(fcc_arg && strcmp(fcc_arg, ps_global->VAR_DEFAULT_FCC))
+ fcc = cpystr(fcc_arg);
+ else{
+ if(ps_global->fcc_rule == FCC_RULE_LAST && last_fcc_used)
+ fcc = cpystr(last_fcc_used);
+ else if(ps_global->fcc_rule == FCC_RULE_CURRENT
+ && ps_global->mail_stream
+ && !sp_flagged(ps_global->mail_stream, SP_INBOX)
+ && !IS_NEWS(ps_global->mail_stream)
+ && ps_global->cur_folder
+ && ps_global->cur_folder[0]){
+ CONTEXT_S *cntxt = ps_global->context_current;
+ char *rs = NULL;
+
+ if(((cntxt->use) & CNTXT_SAVEDFLT))
+ rs = ps_global->cur_folder;
+ else
+ rs = ps_global->mail_stream->mailbox;
+
+ fcc = cpystr((rs&&*rs) ? rs : ps_global->VAR_DEFAULT_FCC);
+ }
+ else
+ fcc = cpystr(ps_global->VAR_DEFAULT_FCC);
+ }
+
+ return(fcc);
+}
+
+
+/*
+ * Save the fcc for use with next composition.
+ */
+void
+set_last_fcc(char *fcc)
+{
+ size_t l;
+
+ if(fcc){
+ if(!last_fcc_used)
+ last_fcc_used = cpystr(fcc);
+ else if(strcmp(last_fcc_used, fcc)){
+ if((l=strlen(last_fcc_used)) >= strlen(fcc)){
+ strncpy(last_fcc_used, fcc, l+1);
+ last_fcc_used[l] = '\0';
+ }
+ else{
+ fs_give((void **)&last_fcc_used);
+ last_fcc_used = cpystr(fcc);
+ }
+ }
+ }
+}
+
+
+/*
+ * Figure out what the fcc is based on the given to address.
+ *
+ * Returns an allocated copy of the fcc, to be freed by the caller, or NULL.
+ */
+char *
+get_fcc_based_on_to(struct mail_address *to)
+{
+ ADDRESS *next_addr;
+ char *bufp, *fcc;
+ BuildTo bldto;
+ size_t len;
+
+ if(!to || !to->host || to->host[0] == '.')
+ return(NULL);
+
+ /* pick off first address */
+ next_addr = to->next;
+ to->next = NULL;
+ len = est_size(to);
+ bufp = (char *) fs_get(len * sizeof(char));
+ bufp[0] = '\0';
+
+ bldto.type = Str;
+ bldto.arg.str = cpystr(addr_string(to, bufp, len));
+
+ fs_give((void **)&bufp);
+ to->next = next_addr;
+
+ fcc = NULL;
+
+ (void) build_address_internal(bldto, NULL, NULL, &fcc, NULL, NULL, NULL, 0, NULL);
+
+ if(bldto.arg.str)
+ fs_give((void **) &bldto.arg.str);
+
+ return(fcc);
+}
+
+
+/*
+ * Free storage in headerentry.bldr_private.
+ */
+void
+free_privatetop(PrivateTop **pt)
+{
+ if(pt && *pt){
+ if((*pt)->affector)
+ fs_give((void **)&(*pt)->affector);
+
+ fs_give((void **)pt);
+ }
+}
diff --git a/pith/bldaddr.h b/pith/bldaddr.h
new file mode 100644
index 00000000..964fd5c8
--- /dev/null
+++ b/pith/bldaddr.h
@@ -0,0 +1,122 @@
+/*
+ * $Id: bldaddr.h 769 2007-10-24 00:15:40Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_BLDADDR_INCLUDED
+#define PITH_BLDADDR_INCLUDED
+
+
+#include "../pith/adrbklib.h"
+#include "../pith/state.h"
+#include "../pith/ldap.h"
+#include "../pith/pattern.h"
+#include "../pith/remtype.h"
+
+
+/*
+ * This is the structure that the builders impose on the private data
+ * that is pointed to by the bldr_private pointer in each header entry.
+ *
+ * The bldr_private pointer points to a PrivateTop which consists of two
+ * parts, whose purposes are independent:
+ * ******* This part no longer exists ******
+ * encoded -- This is used to preserve the charset information for
+ * addresses in this entry. For example, if the user types
+ * in a nickname which has a charset in it, the encoded
+ * version containing the charset information is saved here
+ * along with a checksum of the text that created it (the
+ * line containing the nickname).
+ * etext -- Pointer to the encoded text.
+ * chksumlen -- Length of string that produced the etext.
+ * chksumval -- Checksum of that string.
+ * The string that produced the etext is just the displayed
+ * value of the entry, not the nickname before it was expanded.
+ * That's so we can check on the next builder call to see if it
+ * was changed or not. Appending or prepending more addresses to
+ * what's there will work, the etext from the old contents will
+ * be combined with the etext from the appended stuff. (The check
+ * is for appending or prepending so appending AND prepending all
+ * at once won't work, charset will be lost.) If the middle of the
+ * text is edited the charset is lost. If text is removed, the
+ * charset is lost.
+ *
+ * affector -- This is for entries which affect the contents of other
+ * fields. For example, the Lcc field affects what goes in
+ * the To and Fcc fields, and the To field affects what goes
+ * in the Fcc field.
+ * who -- Who caused this field to be set last.
+ * chksumlen -- Length of string in the "who" field that caused the effect.
+ * chksumval -- Checksum of that string.
+ * The string that is being checksummed is the one that is displayed
+ * in the field doing the affecting. So, for the affector in the
+ * Fcc headerentry, the who might point to either To or Lcc and
+ * then the checksummed string would be either the To or Lcc displayed
+ * string. The purpose of the affector is to remember that the
+ * affected field was set from an address book entry that is no
+ * longer identifiable once it is expanded. For example, if a list
+ * is entered into the Lcc field, then the To field gets the list
+ * fullname and the fcc field gets the fcc entry for the list. If
+ * we move out of the Lcc field and back in and call the builder
+ * again, the list has been expanded and we can't tell (except for
+ * the affector) that the same list is what caused the results.
+ * Same for the To field. A nickname entered there will cause the
+ * fcc of that nickname to go in the Fcc field and the affector
+ * will cause it to stick as long as the To field is only appended to.
+ *
+ * It may seem a little strange that the PrivateAffector doesn't have a text
+ * field. The reason is that the text is actually displayed in that field
+ * and so is contained in the entry itself, unlike the PrivateEncoded
+ * which has etext which is not displayed and is only used when we go to
+ * send the mail.
+ */
+
+typedef enum {BP_Unset, BP_To, BP_Lcc} WhoSetUs;
+
+typedef struct private_affector {
+ WhoSetUs who;
+ int cksumlen;
+ unsigned long cksumval;
+} PrivateAffector;
+
+/*
+ * This used to have more than one member. We could get rid
+ * of this structure now if we wanted to, and just have the
+ * PrivateAffector at the top-level, but it's easier alone.
+ * Who knows, we may have a need for something else to be added
+ * to the structure in the future.
+ */
+typedef struct private_top {
+ PrivateAffector *affector;
+} PrivateTop;
+
+/* save_and_restore flags */
+#define SAR_SAVE 1
+#define SAR_RESTORE 2
+
+/* exported prototypes */
+int our_build_address(BuildTo, char **, char **, char **, void (*)(int, SAVE_STATE_S *));
+int build_address_internal(BuildTo, char **, char **, char **, int *, char **,
+ void (*)(int, SAVE_STATE_S *), int, int *);
+ADDRESS *expand_address(BuildTo, char *, char *, int *, char **, \
+ int *, char **, char **, int, int, int *);
+ADDRESS *massage_phrase_addr(char *, char *, char *);
+char *get_fcc(char *);
+void set_last_fcc(char *);
+char *get_fcc_based_on_to(ADDRESS *);
+void free_privatetop(PrivateTop **);
+void strip_personal_quotes(ADDRESS *);
+
+
+#endif /* PITH_BLDADDR_INCLUDED */
diff --git a/pith/busy.h b/pith/busy.h
new file mode 100644
index 00000000..f3be42b9
--- /dev/null
+++ b/pith/busy.h
@@ -0,0 +1,38 @@
+/*
+ * $Id: busy.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_BUSY_INCLUDED
+#define PITH_BUSY_INCLUDED
+
+
+typedef int (*percent_done_t)(); /* returns %done for progress status msg */
+
+
+/* used to tweak busy without it */
+#ifndef ALARM_BLIP
+#define ALARM_BLIP()
+#endif
+
+
+/* exported protoypes */
+
+
+/* currently mandatory to implement stubs */
+
+int busy_cue(char *, percent_done_t, int);
+void cancel_busy_cue(int);
+
+
+#endif /* PITH_BUSY_INCLUDED */
diff --git a/pith/charconv/Makefile.am b/pith/charconv/Makefile.am
new file mode 100644
index 00000000..615a6082
--- /dev/null
+++ b/pith/charconv/Makefile.am
@@ -0,0 +1,19 @@
+## Process this file with automake to produce Makefile.in
+## Use aclocal -I m4; automake
+
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+noinst_LIBRARIES = libpithcc.a
+
+libpithcc_a_SOURCES = filesys.c utf8.c
+
+AM_CPPFLAGS = -I@top_builddir@/include -I@top_srcdir@/include
diff --git a/pith/charconv/Makefile.in b/pith/charconv/Makefile.in
new file mode 100644
index 00000000..3a6f5220
--- /dev/null
+++ b/pith/charconv/Makefile.in
@@ -0,0 +1,527 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = pith/charconv
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/acx_pthread.m4 \
+ $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
+ $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/VERSION \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/include/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+ARFLAGS = cru
+libpithcc_a_AR = $(AR) $(ARFLAGS)
+libpithcc_a_LIBADD =
+am_libpithcc_a_OBJECTS = filesys.$(OBJEXT) utf8.$(OBJEXT)
+libpithcc_a_OBJECTS = $(am_libpithcc_a_OBJECTS)
+DEFAULT_INCLUDES =
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+SOURCES = $(libpithcc_a_SOURCES)
+DIST_SOURCES = $(libpithcc_a_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_CFLAGS = @AM_CFLAGS@
+AM_LDFLAGS = @AM_LDFLAGS@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CP = @CP@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+C_CLIENT_CFLAGS = @C_CLIENT_CFLAGS@
+C_CLIENT_GCCOPTLEVEL = @C_CLIENT_GCCOPTLEVEL@
+C_CLIENT_LDFLAGS = @C_CLIENT_LDFLAGS@
+C_CLIENT_SPECIALS = @C_CLIENT_SPECIALS@
+C_CLIENT_TARGET = @C_CLIENT_TARGET@
+C_CLIENT_WITH_IPV6 = @C_CLIENT_WITH_IPV6@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISPELLPROG = @ISPELLPROG@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN = @LN@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKE = @MAKE@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NPA_PROG = @NPA_PROG@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+POSUB = @POSUB@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+PWPROG = @PWPROG@
+RANLIB = @RANLIB@
+REGEX_BUILD = @REGEX_BUILD@
+RM = @RM@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPELLPROG = @SPELLPROG@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEB_BINDIR = @WEB_BINDIR@
+WEB_BUILD = @WEB_BUILD@
+WEB_PUBCOOKIE_BUILD = @WEB_PUBCOOKIE_BUILD@
+WEB_PUBCOOKIE_LIB = @WEB_PUBCOOKIE_LIB@
+WEB_PUBCOOKIE_LINK = @WEB_PUBCOOKIE_LINK@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+acx_pthread_config = @acx_pthread_config@
+alpine_interactive_spellcheck = @alpine_interactive_spellcheck@
+alpine_simple_spellcheck = @alpine_simple_spellcheck@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LIBRARIES = libpithcc.a
+libpithcc_a_SOURCES = filesys.c utf8.c
+AM_CPPFLAGS = -I@top_builddir@/include -I@top_srcdir@/include
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign pith/charconv/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign pith/charconv/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLIBRARIES:
+ -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+libpithcc.a: $(libpithcc_a_OBJECTS) $(libpithcc_a_DEPENDENCIES)
+ -rm -f libpithcc.a
+ $(libpithcc_a_AR) libpithcc.a $(libpithcc_a_OBJECTS) $(libpithcc_a_LIBADD)
+ $(RANLIB) libpithcc.a
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filesys.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utf8.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LIBRARIES)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libtool clean-noinstLIBRARIES ctags distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags uninstall uninstall-am
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pith/charconv/filesys.c b/pith/charconv/filesys.c
new file mode 100644
index 00000000..c9ef0f05
--- /dev/null
+++ b/pith/charconv/filesys.c
@@ -0,0 +1,721 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: filesys.c 770 2007-10-24 00:23:09Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/* includable WITHOUT dependency on c-client */
+#include "../../c-client/mail.h"
+#include "../../c-client/utf8.h"
+
+#ifdef _WINDOWS
+/* wingdi.h uses ERROR (!) and we aren't using the c-client ERROR so... */
+#undef ERROR
+#endif
+
+#include <system.h>
+#include <general.h>
+
+#include "../../c-client/fs.h"
+
+/* includable WITHOUT dependency on pico */
+#include "../../pico/keydefs.h"
+#ifdef _WINDOWS
+#include "../../pico/osdep/mswin.h"
+#endif
+
+#include "filesys.h"
+#include "utf8.h"
+
+
+#define bad_char ((UCS) '?')
+
+
+/*
+ * Make it easier to use the convert_to_locale function for filenames
+ * and directory names. Note, only one at a time because there's only
+ * one buffer.
+ * This isn't being freed as it stands now.
+ */
+char *
+fname_to_locale(char *fname)
+{
+ static char *fname_locale_buf = NULL;
+ static size_t fname_locale_len = 0;
+ char *converted_fname, *p;
+
+ p = convert_to_locale(fname);
+ if(p)
+ converted_fname = p;
+ else
+ converted_fname = fname;
+
+ if(converted_fname){
+ if(strlen(converted_fname)+1 > fname_locale_len){
+ if(fname_locale_buf)
+ fs_give((void **) &fname_locale_buf);
+
+ fname_locale_len = strlen(converted_fname)+1;
+ fname_locale_buf = (char *) fs_get(fname_locale_len * sizeof(char));
+ }
+
+ strncpy(fname_locale_buf, converted_fname, fname_locale_len);
+ fname_locale_buf[fname_locale_len-1] = '\0';
+ }
+ else{
+ if(fname_locale_len == 0){
+ fname_locale_len = 1;
+ fname_locale_buf = (char *) fs_get(fname_locale_len * sizeof(char));
+ }
+
+ fname_locale_buf[0] = '\0';
+ }
+
+ if(p)
+ fs_give((void **) &p);
+
+ return(fname_locale_buf);
+}
+
+
+/*
+ * Make it easier to use the convert_to_utf8 function for filenames
+ * and directory names. Note, only one at a time because there's only
+ * one buffer.
+ * This isn't being freed as it stands now.
+ */
+char *
+fname_to_utf8(char *fname)
+{
+ static char *fname_utf8_buf = NULL;
+ static size_t fname_utf8_len = 0;
+ char *converted_fname, *p;
+
+ p = convert_to_utf8(fname, NULL, 0);
+ if(p)
+ converted_fname = p;
+ else
+ converted_fname = fname;
+
+ if(converted_fname){
+ if(strlen(converted_fname)+1 > fname_utf8_len){
+ if(fname_utf8_buf)
+ fs_give((void **) &fname_utf8_buf);
+
+ fname_utf8_len = strlen(converted_fname)+1;
+ fname_utf8_buf = (char *) fs_get(fname_utf8_len * sizeof(char));
+ }
+
+ strncpy(fname_utf8_buf, converted_fname, fname_utf8_len);
+ fname_utf8_buf[fname_utf8_len-1] = '\0';
+ }
+ else{
+ if(fname_utf8_len == 0){
+ fname_utf8_len = 1;
+ fname_utf8_buf = (char *) fs_get(fname_utf8_len * sizeof(char));
+ }
+
+ fname_utf8_buf[0] = '\0';
+ }
+
+ if(p)
+ fs_give((void **) &p);
+
+ return(fname_utf8_buf);
+}
+
+
+/*
+ * The fp file pointer is open for read on a file which has contents
+ * that are encoded in the user's locale charset. That multibyte stream
+ * of characters is converted to wide characters and returned one at
+ * a time.
+ *
+ * Not sure what to do if an uninterpretable character happens. Returning
+ * the bad character now.
+ */
+UCS
+read_a_wide_char(FILE *fp,
+ void *input_cs) /* input_cs ignored in Windows */
+{
+#ifdef _WINDOWS
+ _TINT val;
+
+ val = _fgettc(fp);
+ if(val == _TEOF)
+ return(CCONV_EOF);
+
+ return((UCS) val);
+#else /* UNIX */
+ unsigned long octets_so_far, remaining_octets;
+ unsigned char *inputp;
+ unsigned char inputbuf[20];
+ int c;
+ UCS ucs;
+
+ c = fgetc(fp);
+ if(c == EOF)
+ return(CCONV_EOF);
+
+ /*
+ * Read enough bytes to make up a character and convert it to UCS-4.
+ */
+ memset(inputbuf, 0, sizeof(inputbuf));
+ inputbuf[0] = (unsigned char) c;
+ octets_so_far = 1;
+ for(;;){
+ remaining_octets = octets_so_far;
+ inputp = inputbuf;
+ ucs = mbtow(input_cs, &inputp, &remaining_octets);
+ switch(ucs){
+ case CCONV_BADCHAR:
+ return(bad_char);
+
+ case CCONV_NEEDMORE:
+ if(octets_so_far >= sizeof(inputbuf))
+ return(bad_char);
+
+ c = fgetc(fp);
+ if(c == EOF)
+ return(CCONV_EOF);
+
+ inputbuf[octets_so_far++] = (unsigned char) c;
+ break;
+
+ default:
+ /* got a good UCS-4 character */
+ return(ucs);
+ }
+ }
+
+ return(bad_char);
+#endif /* UNIX */
+}
+
+
+int
+write_a_wide_char(UCS ucs, FILE *fp)
+{
+#ifdef _WINDOWS
+ int rv = 1;
+ TCHAR w;
+
+ w = (TCHAR) ucs;
+ if(_fputtc(w, fp) == _TEOF)
+ rv = EOF;
+
+ return(rv);
+#else /* UNIX */
+ int rv = 1;
+ int i, outchars;
+ unsigned char obuf[MAX(MB_LEN_MAX,32)];
+
+ if(ucs < 0x80){
+ obuf[0] = (unsigned char) ucs;
+ outchars = 1;
+ }
+ else{
+ outchars = wtomb((char *) obuf, ucs);
+ if(outchars < 0){
+ outchars = 1;
+ obuf[0] = bad_char; /* ??? */
+ }
+ }
+
+ for(i = 0; i < outchars; i++)
+ if(fputc(obuf[i], fp) == EOF){
+ rv = EOF;
+ break;
+ }
+
+ return(rv);
+#endif /* UNIX */
+}
+
+
+int
+our_stat(char *filename, struct stat *sbuf)
+{
+#ifdef _WINDOWS
+ LPTSTR f = NULL;
+ int ret = -1;
+ struct _stat s;
+
+ f = utf8_to_lptstr((LPSTR) filename);
+ if(f){
+ ret = _tstat(f, &s);
+
+ sbuf->st_dev = s.st_dev;
+ sbuf->st_ino = s.st_ino;
+ sbuf->st_mode = s.st_mode;
+ sbuf->st_nlink = s.st_nlink;
+ sbuf->st_uid = s.st_uid;
+ sbuf->st_gid = s.st_gid;
+ sbuf->st_rdev = s.st_rdev;
+ sbuf->st_size = s.st_size;
+ sbuf->st_atime = (time_t) s.st_atime;
+ sbuf->st_mtime = (time_t) s.st_mtime;
+ sbuf->st_ctime = (time_t) s.st_ctime;
+
+ fs_give((void **) &f);
+ }
+
+ return ret;
+#else /* UNIX */
+ return(stat(fname_to_locale(filename), sbuf));
+#endif /* UNIX */
+}
+
+
+int
+our_lstat(char *filename, struct stat *sbuf)
+{
+#ifdef _WINDOWS
+ assert(0); /* lstat not used in Windows */
+ return(-1);
+#else /* UNIX */
+ return(lstat(fname_to_locale(filename), sbuf));
+#endif /* UNIX */
+}
+
+
+FILE *
+our_fopen(char *path, char *mode)
+{
+#ifdef _WINDOWS
+ LPTSTR p = NULL, m = NULL;
+ FILE *ret = NULL;
+ char *mode_with_ccs = NULL;
+ char buf[500];
+ size_t len;
+
+ if(mode && (*mode == 'r' || *mode == 'a')){
+ char *force_bom_check = ", ccs=UNICODE";
+
+ if(strchr(mode, 'b'))
+ mode_with_ccs = mode;
+ else{
+ /*
+ * The docs seem to say that we don't need the ccs parameter and
+ * if the file has a BOM at the beginning it will notice that and
+ * use it. However, we're not seeing that. Instead, what we see is
+ * that giving a parameter of UNICODE causes the desired behavior.
+ * This causes it to check for a BOM and if it finds one it uses it.
+ * If it doesn't find one, it treats the file as ANSI, which is what
+ * we want.
+ */
+ if((len = strlen(mode) + strlen(force_bom_check)) < sizeof(buf)){
+ len = sizeof(buf)-1;
+ mode_with_ccs = buf;
+ }
+ else
+ mode_with_ccs = (char *) MemAlloc((len+1) * sizeof(char));
+
+ if(mode_with_ccs)
+ snprintf(mode_with_ccs, len+1, "%s%s", mode, force_bom_check);
+ else
+ mode_with_ccs = mode; /* can't happen */
+ }
+ }
+ else if(mode && (*mode == 'w')){
+ char *force_utf8 = ", ccs=UTF-8";
+
+ if(strchr(mode, 'b'))
+ mode_with_ccs = mode;
+ else{
+ if((len = strlen(mode) + strlen(force_utf8)) < sizeof(buf)){
+ len = sizeof(buf)-1;
+ mode_with_ccs = buf;
+ }
+ else
+ mode_with_ccs = (char *) MemAlloc((len+1) * sizeof(char));
+
+ if(mode_with_ccs)
+ snprintf(mode_with_ccs, len+1, "%s%s", mode, force_utf8);
+ else
+ mode_with_ccs = mode; /* can't happen */
+ }
+ }
+
+ p = utf8_to_lptstr((LPSTR) path);
+
+ if(p){
+ m = utf8_to_lptstr((LPSTR) mode_with_ccs);
+ if(m){
+ ret = _tfopen(p, m);
+ MemFree((void *) m);
+ }
+
+ fs_give((void **) &p);
+ }
+
+ if(mode_with_ccs && mode_with_ccs != buf && mode_with_ccs != mode)
+ MemFree((void *) mode_with_ccs);
+
+ return ret;
+#else /* UNIX */
+ return(fopen(fname_to_locale(path), mode));
+#endif /* UNIX */
+}
+
+
+int
+our_open(char *path, int flags, mode_t mode)
+{
+#ifdef _WINDOWS
+ LPTSTR p = NULL;
+ int ret = -1;
+
+ /*
+ * Setting the _O_WTEXT flag when opening a file for reading
+ * will cause us to read the first few bytes to check for
+ * a BOM and to translate from that encoding if we find it.
+ * This only works with stream I/O, not low-level read/write.
+ *
+ * When opening for writing the flag _O_U8TEXT will cause
+ * us to put a UTF-8 BOM at the start of the file.
+ *
+ * O_TEXT will cause LF -> CRLF on output, opposite on input
+ * O_BINARY suppresses that.
+ * _O_U8TEXT implies O_TEXT.
+ */
+
+ p = utf8_to_lptstr((LPSTR) path);
+
+ if(p){
+ ret = _topen(p, flags, mode);
+ fs_give((void **) &p);
+ }
+
+ return ret;
+#else /* UNIX */
+ return(open(fname_to_locale(path), flags, mode));
+#endif /* UNIX */
+}
+
+
+int
+our_creat(char *path, mode_t mode)
+{
+#ifdef _WINDOWS
+ LPTSTR p = NULL;
+ int ret = -1;
+
+ p = utf8_to_lptstr((LPSTR) path);
+
+ if(p){
+ ret = _tcreat(p, mode);
+ fs_give((void **) &p);
+ }
+
+ return ret;
+#else /* UNIX */
+ return(creat(fname_to_locale(path), mode));
+#endif /* UNIX */
+}
+
+
+int
+our_mkdir(char *path, mode_t mode)
+{
+#ifdef _WINDOWS
+ /* mode is a noop for _WINDOWS */
+ LPTSTR p = NULL;
+ int ret = -1;
+
+ p = utf8_to_lptstr((LPSTR) path);
+
+ if(p){
+ ret = _tmkdir(p);
+ fs_give((void **) &p);
+ }
+
+ return ret;
+#else /* UNIX */
+ return(mkdir(fname_to_locale(path), mode));
+#endif /* UNIX */
+}
+
+
+int
+our_rename(char *oldpath, char *newpath)
+{
+#ifdef _WINDOWS
+ LPTSTR pold = NULL, pnew = NULL;
+ int ret = -1;
+
+ pold = utf8_to_lptstr((LPSTR) oldpath);
+ pnew = utf8_to_lptstr((LPSTR) newpath);
+
+ if(pold && pnew)
+ ret = _trename(pold, pnew);
+
+ if(pold)
+ fs_give((void **) &pold);
+ if(pnew)
+ fs_give((void **) &pnew);
+
+ return ret;
+#else /* UNIX */
+ char *p, *pold;
+ size_t len;
+ int ret = -1;
+
+ p = fname_to_locale(oldpath);
+ if(p){
+ len = strlen(p);
+ pold = (char *) fs_get((len+1) * sizeof(char));
+ strncpy(pold, p, len+1);
+ pold[len] = '\0';
+
+ ret = rename(pold, fname_to_locale(newpath));
+ fs_give((void **) &pold);
+ }
+
+ return ret;
+#endif /* UNIX */
+}
+
+
+int
+our_unlink(char *path)
+{
+#ifdef _WINDOWS
+ LPTSTR p = NULL;
+ int ret = -1;
+
+ p = utf8_to_lptstr((LPSTR) path);
+
+ if(p){
+ ret = _tunlink(p);
+ fs_give((void **) &p);
+ }
+
+ return ret;
+#else /* UNIX */
+ return(unlink(fname_to_locale(path)));
+#endif /* UNIX */
+}
+
+
+int
+our_link(char *oldpath, char *newpath)
+{
+#ifdef _WINDOWS
+ assert(0); /* link not used in Windows */
+ return(-1);
+#else /* UNIX */
+ char *p, *pold;
+ size_t len;
+ int ret = -1;
+
+ p = fname_to_locale(oldpath);
+ if(p){
+ len = strlen(p);
+ pold = (char *) fs_get((len+1) * sizeof(char));
+ strncpy(pold, p, len+1);
+ pold[len] = '\0';
+
+ ret = link(pold, fname_to_locale(newpath));
+ fs_give((void **) &pold);
+ }
+
+ return ret;
+#endif /* UNIX */
+}
+
+
+int
+our_truncate(char *path, off_t size)
+{
+ int ret = -1;
+#if defined(_WINDOWS) || !defined(HAVE_TRUNCATE)
+ int fdes;
+#endif
+
+#ifdef _WINDOWS
+ if((fdes = our_open(path, O_RDWR | O_CREAT | S_IREAD | S_IWRITE | _O_U8TEXT, 0600)) != -1){
+ if(chsize(fdes, size) == 0)
+ ret = 0;
+
+ close(fdes);
+ }
+
+#else /* UNIX */
+
+#ifdef HAVE_TRUNCATE
+ ret = truncate(fname_to_locale(path), size);
+#else /* !HAVE_TRUNCATE */
+
+ if((fdes = our_open(path, O_RDWR, 0600)) != -1){
+ ret = chsize(fdes, size) ;
+
+ if(close(fdes))
+ ret = -1;
+ }
+#endif /* !HAVE_TRUNCATE */
+#endif /* UNIX */
+
+ return ret;
+}
+
+
+int
+our_chmod(char *path, mode_t mode)
+{
+#ifdef _WINDOWS
+ LPTSTR p = NULL;
+ int ret = -1;
+
+ p = utf8_to_lptstr((LPSTR) path);
+ if(p){
+ ret = _tchmod(p, mode);
+ fs_give((void **) &p);
+ }
+
+ return ret;
+#else /* UNIX */
+ return(chmod(fname_to_locale(path), mode));
+#endif /* UNIX */
+}
+
+
+int
+our_chown(char *path, uid_t owner, gid_t group)
+{
+#ifdef _WINDOWS
+ return 0;
+#else /* UNIX */
+ return(chown(fname_to_locale(path), owner, group));
+#endif /* UNIX */
+}
+
+
+int
+our_utime(char *path, struct utimbuf *buf)
+{
+#ifdef _WINDOWS
+ LPTSTR p = NULL;
+ int ret = -1;
+
+ p = utf8_to_lptstr((LPSTR) path);
+
+ if(p){
+ ret = _tutime(p, buf);
+ fs_give((void **) &p);
+ }
+
+ return ret;
+#else /* UNIX */
+ return(utime(fname_to_locale(path), buf));
+#endif /* UNIX */
+}
+
+/*
+ * Return a malloc'd utf8-encoded char * of the provided environment
+ * variable. The env_variable argument is assumed not to be UTF-8. Returns
+ * NULL if no such environment variable.
+ *
+ * We'll pretty much swap out getenv's where convenient. Windows pretty
+ * much doesn't want to do getenv once we do unicode
+ */
+char *
+our_getenv(char *env_variable)
+{
+#ifdef _WINDOWS
+ TCHAR lptstr_env_variable[MAXPATH+1], *p;
+ int i;
+
+ for(i = 0; env_variable[i] && i < MAXPATH; i++)
+ lptstr_env_variable[i] = env_variable[i];
+ lptstr_env_variable[i] = '\0';
+ if(p = _tgetenv(lptstr_env_variable))
+ return(lptstr_to_utf8(p));
+ else
+ return(NULL);
+#else /* !_WINDOWS */
+ char *p, *utf8_p, *env_cpy;
+ size_t len;
+ if((p = getenv(env_variable)) != NULL){
+ /* all this when what we want is a cpystr */
+ utf8_p = fname_to_utf8(p);
+ len = strlen(utf8_p);
+ env_cpy = (char *)fs_get((len+1)*sizeof(char));
+ strncpy(env_cpy, utf8_p, len+1);
+ env_cpy[len] = '\0';
+
+ return(env_cpy);
+ }
+ else
+ return(NULL);
+#endif /* !_WINDOWS */
+}
+
+
+int
+our_access(char *path, int mode)
+{
+#ifdef _WINDOWS
+ LPTSTR p = NULL;
+ int ret = -1;
+
+ p = utf8_to_lptstr((LPSTR) path);
+ if(p){
+ ret = _taccess(p, mode);
+ fs_give((void **) &p);
+ }
+
+ return ret;
+#else /* UNIX */
+ return(access(fname_to_locale(path), mode));
+#endif /* UNIX */
+}
+
+
+/*
+ * Fgets that doesn't do any character encoding translation or any
+ * of that Windows stuff.
+ */
+char *
+fgets_binary(char *s, int size, FILE *fp)
+{
+#ifdef _WINDOWS
+ char *p;
+ char c;
+ int r;
+
+ /*
+ * Use fread low-level input instead of fgets.
+ * Maybe if we understood better we wouldn't need this.
+ */
+ if(!s)
+ return s;
+
+ p = s;
+ while(p-s < size-1 && (r=fread(&c, sizeof(c), (size_t) 1, fp)) == 1 && c != '\n')
+ *p++ = c;
+
+ if(p-s < size-1 && r == 1){
+ /* must have gotten to end of line */
+ *p++ = '\n';
+ }
+
+ *p = '\0';
+ return(s);
+
+#else /* UNIX */
+ return(fgets(s, size, fp));
+#endif /* UNIX */
+}
diff --git a/pith/charconv/filesys.h b/pith/charconv/filesys.h
new file mode 100644
index 00000000..07703092
--- /dev/null
+++ b/pith/charconv/filesys.h
@@ -0,0 +1,50 @@
+/*-----------------------------------------------------------------------
+ $Id: filesys.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ -----------------------------------------------------------------------*/
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_CHARCONV_FILESYS_INCLUDED
+#define PITH_CHARCONV_FILESYS_INCLUDED
+
+
+#include <general.h>
+
+
+/*
+ * Exported Prototypes
+ */
+char *fname_to_locale(char *);
+char *fname_to_utf8(char *);
+UCS read_a_wide_char(FILE *fp, void *input_cs);
+int write_a_wide_char(UCS ucs, FILE *fp);
+int our_stat(char *, struct stat *);
+FILE *our_fopen(char *, char *);
+int our_open(char *, int, mode_t);
+int our_creat(char *, mode_t);
+int our_mkdir(char *, mode_t);
+int our_rename(char *, char *);
+int our_unlink(char *);
+int our_link(char *, char *);
+int our_lstat(char *, struct stat *);
+int our_chmod(char *, mode_t);
+int our_chown(char *, uid_t, gid_t);
+int our_truncate(char *, off_t);
+int our_utime(char *, struct utimbuf *);
+int our_access(char *, int);
+char *our_getenv(char *);
+char *fgets_binary(char *, int, FILE *);
+
+
+#endif /* PITH_CHARCONV_FILESYS_INCLUDED */
diff --git a/pith/charconv/makefile.wnt b/pith/charconv/makefile.wnt
new file mode 100644
index 00000000..6700ec3b
--- /dev/null
+++ b/pith/charconv/makefile.wnt
@@ -0,0 +1,58 @@
+# $Id: makefile.wnt 14098 2005-10-03 18:54:13Z jpf@u.washington.edu $
+#
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+#
+#
+# Makefile for WIN NT version of libpithcc.lib
+#
+#
+CC=cl
+RM=del
+CP=copy
+RC=rc
+
+#includes symbol info for debugging
+CDEBUG= #-Zi -Od
+LDEBUG= /DEBUG /DEBUGTYPE:CV
+
+STDCFLAGS= -I..\..\include -I../../regex -nologo -MT -DWIN32 -DDOS -D_WINDOWS -DJOB_CONTROL -DMSC_MALLOC
+
+CFLAGS= $(CDEBUG) $(STDCFLAGS) $(NET) $(EXTRACFLAGS)
+
+LFLAGS= $(LDEBUG) $(EXTRALDFLAGS)
+
+RCFLAGS =
+
+# switches for library building
+LIBER=lib
+LIBARGS=/nologo /verbose
+
+HFILES= ../../include/system.h ../../include/general.h \
+ filesys.h utf8.h
+
+OFILES= filesys.obj utf8.obj
+
+all: libpithcc.lib
+
+.c.obj:
+ $(CC) -c $(CFLAGS) "$(MAKEDIR)"\$*.c
+
+$(OFILES): $(HFILES)
+
+libpithcc.lib: $(OFILES)
+ $(RM) libpithcc.lib || rem
+ $(LIBER) /out:libpithcc.lib $(OFILES)
+
+clean:
+ $(RM) *.lib
+ $(RM) *.obj
diff --git a/pith/charconv/utf8.c b/pith/charconv/utf8.c
new file mode 100644
index 00000000..411e1ddd
--- /dev/null
+++ b/pith/charconv/utf8.c
@@ -0,0 +1,2512 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: utf8.c 1184 2008-12-16 23:52:15Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+/* includable WITHOUT dependency on c-client */
+#include "../../c-client/mail.h"
+#include "../../c-client/utf8.h"
+
+#ifdef _WINDOWS
+/* wingdi.h uses ERROR (!) and we aren't using the c-client ERROR so... */
+#undef ERROR
+#endif
+
+#include <system.h>
+
+#include "../../c-client/fs.h"
+
+/* includable WITHOUT dependency on pico */
+#include "../../pico/keydefs.h"
+
+#include "../osdep/collate.h"
+#include "../filttype.h"
+
+#include "utf8.h"
+
+#include <stdarg.h>
+
+
+unsigned single_width_chars_a_to_b(UCS *, int, int);
+
+
+static char locale_charmap[50];
+
+static int native_utf8;
+static void *display_data;
+
+void
+init_utf8_display(int utf8, void *rmap)
+{
+ native_utf8 = utf8;
+ display_data = rmap;
+}
+
+
+/*
+ * Argument is a UCS-4 wide character.
+ * Returns the environment dependent cell width of the
+ * character when printed to the screen.
+ * This will be -1 if the character is not printable.
+ * It will be >= zero if it is printable.
+ *
+ * Note that in the case it is not printable but it is still sent to
+ * Writechar, Writechar will print a '?' with width 1.
+ */
+int
+wcellwidth(UCS ucs)
+{
+ char dummy[32];
+ long w;
+
+ /*
+ * We believe that on modern unix systems wchar_t is a UCS-4 character.
+ * That's the assumption here.
+ */
+
+ if(native_utf8){ /* display is UTF-8 capable */
+ w = ucs4_width((unsigned long) ucs);
+ return((w & U4W_ERROR) ? -1 : w);
+ }
+ else if(display_data){
+ if(wtomb(dummy, ucs) < 0)
+ return(-1);
+ else{
+ w = ucs4_width((unsigned long) ucs);
+ return((w & U4W_ERROR) ? -1 : w);
+ }
+ }
+#ifndef _WINDOWS
+ else
+ return(wcwidth((wchar_t) ucs));
+#else
+ return(0);
+#endif
+}
+
+
+/*
+ * Argument is a UCS-4 wide character.
+ * It is converted to the multibyte version (for example UTF8 or EUC-JP).
+ * Dest is a buffer at least xx chars wide where the multi-byte version
+ * of the wide character will be written.
+ * The returned value is the number of bytes written to dest or -1
+ * if the conversion can't be done.
+ */
+int
+wtomb(char *dest, UCS ucs)
+{
+ /*
+ * We believe that on modern unix systems wchar_t is a UCS-4 character.
+ * That's the assumption here.
+ */
+
+ if(native_utf8){
+ unsigned char *newdptr;
+
+ newdptr = utf8_put((unsigned char *) dest, (unsigned long) ucs);
+ return((newdptr == (unsigned char *) dest) ? -1 : newdptr - (unsigned char *) dest);
+ }
+ else if(display_data){
+ unsigned long ucs4;
+ int ret;
+
+ ucs4 = (unsigned long) ucs;
+ ret = ucs4_rmaplen(&ucs4, 1, (unsigned short *) display_data, 0);
+ if(ret >= 0)
+ ucs4_rmapbuf((unsigned char *) dest, &ucs4, 1, (unsigned short *) display_data, 0);
+ else
+ ret = -1;
+
+ return(ret);
+ }
+ else
+ return(wcrtomb(dest, (wchar_t) ucs, NULL));
+}
+
+
+/*
+ * This function does not necessarily update inputp and remaining_octets, so
+ * don't rely on that. The c-client version does but the other doesn't.
+ */
+UCS
+mbtow(void *input_cs, unsigned char **inputp, unsigned long *remaining_octets)
+{
+ UCS ucs;
+
+ if(input_cs){
+ CHARSET *cast_input_cs;
+
+ cast_input_cs = (CHARSET *) input_cs;
+
+ switch((ucs = (UCS) ucs4_cs_get(cast_input_cs, inputp, remaining_octets))){
+ case U8G_ENDSTRG:
+ case U8G_ENDSTRI:
+ return(CCONV_NEEDMORE);
+
+ default:
+ if(ucs & U8G_ERROR || ucs == UBOGON)
+ return(CCONV_BADCHAR);
+
+ return(ucs);
+ }
+ }
+ else{
+ size_t ret;
+ wchar_t w;
+
+ /*
+ * Warning: input_cs and remaining_octets are unused in this
+ * half of the if/else.
+ *
+ * Unfortunately, we can't tell the difference between a source string
+ * that is just not long enough and one that has characters that can't
+ * be converted even though it is long enough. We return NEEDMORE in both cases.
+ */
+ ret = mbstowcs(&w, (char *) (*inputp), 1);
+ if(ret == (size_t)(-1))
+ return(CCONV_NEEDMORE);
+ else{
+ ucs = (UCS) w;
+ return(ucs);
+ }
+ }
+}
+
+
+void
+set_locale_charmap(char *charmap)
+{
+ if(charmap){
+ strncpy(locale_charmap, charmap, sizeof(locale_charmap));
+ locale_charmap[sizeof(locale_charmap)-1] = '\0';
+ }
+ else
+ locale_charmap[0] = '\0';
+}
+
+
+/*
+ * This ensures that the string is UTF-8. If str is already a UTF-8 string,
+ * NULL is returned. Otherwise, an allocated string which is UTF-8 is returned.
+ * The caller is responsible for freeing the returned value.
+ *
+ * Args str -- the string to convert
+ */
+char *
+convert_to_utf8(char *str, char *fromcharset, int flags)
+{
+ char *ret = NULL;
+ char *fcharset;
+ SIZEDTEXT src, result;
+ const CHARSET *cs;
+ int try;
+
+ src.data = (unsigned char *) str;
+ src.size = strlen(str);
+
+ /* already UTF-8, return NULL */
+ if(!(flags & CU8_NOINFER)
+ && (cs = utf8_infercharset(&src))
+ && (cs->type == CT_ASCII || cs->type == CT_UTF8))
+ return(ret);
+
+ try = 1;
+ while(try < 5){
+ switch(try){
+ case 1:
+ fcharset = fromcharset;
+ if(fcharset && strucmp("UTF-8", fcharset) != 0)
+ break; /* give it a try */
+ else
+ try++; /* fall through */
+
+ case 2:
+ if(!(flags & CU8_NOINFER)){
+ fcharset = cs ? cs->name : NULL;
+ if(fcharset && strucmp("UTF-8", fcharset) != 0)
+ break;
+ else
+ try++; /* fall through */
+ }
+ else
+ try++; /* fall through */
+
+ case 3:
+ fcharset = locale_charmap;
+ if(fcharset && strucmp("UTF-8", fcharset) != 0)
+ break;
+ else
+ try++; /* fall through */
+
+ default:
+ fcharset = "ISO-8859-1"; /* this will "work" */
+ break;
+ }
+
+ memset(&result, 0, sizeof(result));
+
+ if(fcharset && utf8_text(&src, fcharset, &result, 0L)){
+ if(!(result.size == src.size && result.data == src.data)){
+ ret = (char *) fs_get((result.size+1) * sizeof(char));
+ strncpy(ret, (char *) result.data, result.size);
+ ret[result.size] = '\0';
+ }
+ /* else no conversion necessary */
+
+ return(ret);
+ }
+
+ try++;
+ }
+
+ /* won't make it to here */
+ return(ret);
+}
+
+
+/*
+ * Convert from UTF-8 to user's locale charset.
+ * This actually uses the wtomb routine to do the conversion, and that
+ * relies on setup_for_input_output having been called.
+ * If no conversion is necessary, NULL is returned, otherwise an allocated
+ * string in the locale charset is returned and the caller is responsible
+ * for freeing it.
+ */
+char *
+convert_to_locale(char *utf8str)
+{
+#define CHNK 500
+ char *inp, *retp, *ret = NULL;
+ CBUF_S cb;
+ int r, alloced;
+
+ if(native_utf8 || !utf8str || !utf8str[0])
+ return(NULL);
+
+ cb.cbuf[0] = '\0';
+ cb.cbufp = cb.cbufend = cb.cbuf;
+ inp = utf8str;
+
+ alloced = CHNK;
+ ret = (char *) fs_get(alloced * sizeof(char));
+ retp = ret;
+
+ /*
+ * There's gotta be a better way to do this but utf8_to_locale was
+ * available and everything looks like a nail when all you have
+ * is a hammer.
+ */
+ while(*inp){
+ /*
+ * We're placing the outgoing stream of characters in ret, a multi-byte
+ * array of characters in the user's locale charset. See if there is
+ * enough room for the next wide characters worth of output chars
+ * and allocate more space if not.
+ */
+ if((alloced - (retp-ret)) < MAX(MB_LEN_MAX,32)){
+ alloced += CHNK;
+ fs_resize((void **) &ret, alloced * sizeof(char));
+ }
+
+ r = utf8_to_locale((int) *inp++, &cb,
+ (unsigned char *) retp, alloced-(retp-ret));
+
+ retp += r;
+ }
+
+ *retp = '\0';
+
+ fs_resize((void **) &ret, strlen(ret)+1);
+
+ return(ret);
+}
+
+
+/*
+ * Pass in a stream of UTF-8 characters in 'c' and return obuf
+ * filled in with multi-byte characters. The return value is the
+ * number of valid characters in obuf to be used.
+ */
+int
+utf8_to_locale(int c, CBUF_S *cb, unsigned char obuf[], size_t obuf_size)
+{
+ int outchars = 0;
+
+ if(!(cb && cb->cbufp))
+ return(0);
+
+ if(cb->cbufp < cb->cbuf+sizeof(cb->cbuf)){
+ unsigned char *inputp;
+ unsigned long remaining_octets;
+ UCS ucs;
+
+ *(cb->cbufp)++ = (unsigned char) c;
+ inputp = cb->cbuf;
+ remaining_octets = (cb->cbufp - cb->cbuf) * sizeof(unsigned char);
+ ucs = (UCS) utf8_get(&inputp, &remaining_octets);
+
+ switch(ucs){
+ case U8G_ENDSTRG: /* incomplete character, wait */
+ case U8G_ENDSTRI: /* incomplete character, wait */
+ break;
+
+ default:
+ if(ucs & U8G_ERROR || ucs == UBOGON){
+ /*
+ * None of these cases is supposed to happen. If it
+ * does happen then the input stream isn't UTF-8
+ * so something is wrong. Treat each character in the
+ * input buffer as a separate error character and
+ * print a '?' for each.
+ */
+ for(inputp = cb->cbuf; inputp < cb->cbufp; inputp++)
+ obuf[outchars++] = '?';
+
+ cb->cbufp = cb->cbuf;
+ }
+ else{
+ if(ucs >= 0x80 && wcellwidth(ucs) < 0){
+ /*
+ * This happens when we have a UTF-8 character that
+ * we aren't able to print in our locale. For example,
+ * if the locale is setup with the terminal
+ * expecting ISO-8859-1 characters then there are
+ * lots of UTF-8 characters that can't be printed.
+ * Print a '?' instead.
+ */
+ obuf[outchars++] = '?';
+ }
+ else{
+ /*
+ * Convert the ucs into the multibyte
+ * character that corresponds to the
+ * ucs in the users locale.
+ */
+ outchars = wtomb((char *) obuf, ucs);
+ if(outchars < 0){
+ obuf[0] = '?';
+ outchars = 1;
+ }
+ }
+
+ /* update the input buffer */
+ if(inputp >= cb->cbufp) /* this should be the case */
+ cb->cbufp = cb->cbuf;
+ else{ /* extra chars for some reason? */
+ unsigned char *q, *newcbufp;
+
+ newcbufp = (cb->cbufp - inputp) + cb->cbuf;
+ q = cb->cbuf;
+ while(inputp < cb->cbufp)
+ *q++ = *inputp++;
+
+ cb->cbufp = newcbufp;
+ }
+ }
+
+ break;
+ }
+ }
+ else{ /* error */
+ obuf[0] = '?';
+ outchars = 1;
+ cb->cbufp = cb->cbuf; /* start over */
+ }
+
+ return(outchars);
+}
+
+
+/*
+ * Returns the screen cells width of the UCS-4 string argument.
+ * The source string is zero terminated.
+ */
+unsigned
+ucs4_str_width(UCS *ucsstr)
+{
+ unsigned width = 0;
+ int w;
+
+ if(ucsstr)
+ while(*ucsstr){
+ w = wcellwidth(*ucsstr++);
+ if(w != U4W_CTLSRGT)
+ width += (w < 0 ? 1 : w);
+ }
+
+ return width;
+}
+
+
+/*
+ * Returns the screen cells width of the UCS-4 string argument
+ * from ucsstr[a] through (inclusive) ucsstr[b].
+ * No checking is done to make sure a starts in the middle
+ * of a UCS-4 array.
+ */
+unsigned
+ucs4_str_width_a_to_b(UCS *ucsstr, int a, int b)
+{
+ unsigned width = 0;
+ int i, w;
+
+ if(ucsstr)
+ for(i = a; i <= b && ucsstr[i]; i++){
+ w = wcellwidth(ucsstr[i]);
+ if(w != U4W_CTLSRGT)
+ width += (w < 0 ? 1 : w);
+ }
+
+ return width;
+}
+
+
+/*
+ * Returns the screen cells width of the UCS-4 string argument
+ * from ustart through (exclusive) uend.
+ * No checking is done to make sure it starts in the middle
+ * of a UCS-4 array.
+ */
+unsigned
+ucs4_str_width_ptr_to_ptr(UCS *ustart, UCS *uend)
+{
+ UCS *u;
+ unsigned width = 0;
+ int w;
+
+ if(!ustart)
+ return width;
+
+ if(ustart)
+ for(u = ustart; u < uend; u++){
+ w = wcellwidth(*u);
+ if(w != U4W_CTLSRGT)
+ width += (w < 0 ? 1 : w);
+ }
+
+ return(width);
+}
+
+
+/*
+ * Return the largest possible pointer into ucs4str so that the width
+ * of the string from ucs4str to the pointer (exclusive)
+ * is maxwidth or less. Also stops at a null character.
+ */
+UCS *
+ucs4_particular_width(UCS *ucs4str, int maxwidth)
+{
+ UCS *u;
+ int w_consumed = 0, w, done = 0;
+
+ u = ucs4str;
+
+ if(u)
+ while(!done && *u && w_consumed <= maxwidth){
+ w = wcellwidth(*u);
+ w = (w >= 0 ? w : 1);
+ if(w_consumed + w <= maxwidth){
+ w_consumed += w;
+ ++u;
+ }
+ else
+ ++done;
+ }
+
+ return(u);
+}
+
+
+/*
+ * Convert and copy a UTF-8 string into a UCS-4 NULL
+ * terminated array. Just like cpystr only it converts
+ * from UTF-8 to UCS-4.
+ *
+ * Returned UCS-4 string needs to be freed by caller.
+ */
+UCS *
+utf8_to_ucs4_cpystr(char *utf8src)
+{
+ size_t retsize;
+ UCS *ret = NULL;
+ UCS ucs;
+ unsigned long remaining_octets;
+ unsigned char *readptr;
+ size_t arrayindex;
+
+ /*
+ * We don't know how big to allocate the return array
+ * because variable numbers of octets in the src array
+ * will combine to make UCS-4 characters. The number of
+ * UCS-4 characters is less than or equal to the number
+ * of src characters, though.
+ */
+
+ if(!utf8src)
+ return NULL;
+
+ retsize = strlen(utf8src) + 1;
+
+ ret = (UCS *) fs_get(retsize * sizeof(*ret));
+ memset(ret, 0, retsize * sizeof(*ret));
+
+ readptr = (unsigned char *) utf8src;
+ remaining_octets = retsize-1;
+ arrayindex = 0;
+
+ while(remaining_octets > 0 && *readptr && arrayindex < retsize-1){
+ ucs = (UCS) utf8_get(&readptr, &remaining_octets);
+
+ if(ucs & U8G_ERROR || ucs == UBOGON)
+ remaining_octets = 0;
+ else
+ ret[arrayindex++] = ucs;
+ }
+
+ ret[arrayindex] = '\0';
+
+ /* get rid of excess size */
+ if(arrayindex+1 < retsize)
+ fs_resize((void **) &ret, (arrayindex + 1) * sizeof(*ret));
+
+ return ret;
+}
+
+
+/*
+ * Convert and copy a UCS-4 zero-terminated array into a UTF-8 NULL
+ * terminated string. Just like cpystr only it converts
+ * from UCS-4 to UTF-8.
+ *
+ * Returned UTF-8 string needs to be freed by caller.
+ */
+char *
+ucs4_to_utf8_cpystr(UCS *ucs4src)
+{
+ unsigned char *ret = NULL;
+ unsigned char *writeptr;
+ int i;
+
+ if(!ucs4src)
+ return NULL;
+
+ /*
+ * Over-allocate and then resize at the end.
+ */
+
+ /* count characters in source */
+ for(i = 0; ucs4src[i]; i++)
+ ;
+
+ ret = (unsigned char *) fs_get((6*i + 1) * sizeof(*ret));
+ memset(ret, 0, (6*i + 1) * sizeof(*ret));
+
+ writeptr = ret;
+ for(i = 0; ucs4src[i]; i++)
+ writeptr = utf8_put(writeptr, (unsigned long) ucs4src[i]);
+
+ /* get rid of excess size */
+ fs_resize((void **) &ret, (writeptr - ret + 1) * sizeof(*ret));
+
+ return ((char *) ret);
+}
+
+
+/*
+ * Similar to above but copy a fixed number of source
+ * characters instead of going until null terminator.
+ */
+char *
+ucs4_to_utf8_cpystr_n(UCS *ucs4src, int ucs4src_len)
+{
+ unsigned char *ret = NULL;
+ unsigned char *writeptr;
+ int i;
+
+ if(!ucs4src)
+ return NULL;
+
+ /*
+ * Over-allocate and then resize at the end.
+ */
+
+ ret = (unsigned char *) fs_get((6*ucs4src_len + 1) * sizeof(*ret));
+ memset(ret, 0, (6*ucs4src_len + 1) * sizeof(*ret));
+
+ writeptr = ret;
+ for(i = 0; i < ucs4src_len; i++)
+ writeptr = utf8_put(writeptr, (unsigned long) ucs4src[i]);
+
+ /* get rid of excess size */
+ fs_resize((void **) &ret, (writeptr - ret + 1) * sizeof(*ret));
+
+ return ((char *) ret);
+}
+
+
+#ifdef _WINDOWS
+/*
+ * Convert a UTF-8 argument into an LPTSTR version
+ * of that argument. The result is allocated here
+ * and should be freed by the caller.
+ */
+LPTSTR
+utf8_to_lptstr(LPSTR arg_utf8)
+{
+ int lptstr_len;
+ LPTSTR lptstr_ret = NULL;
+
+ lptstr_len = MultiByteToWideChar( CP_UTF8, 0, arg_utf8, -1, NULL, 0 );
+ if(lptstr_len > 0)
+ {
+ lptstr_ret = (LPTSTR)fs_get(lptstr_len * sizeof(TCHAR));
+ lptstr_len = MultiByteToWideChar( CP_UTF8, 0,
+ arg_utf8, -1, lptstr_ret, lptstr_len );
+ }
+
+ if(!lptstr_len)
+ {
+ /* check GetLastError()? */
+ lptstr_ret = (LPTSTR)fs_get(sizeof(TCHAR));
+ lptstr_ret[0] = 0;
+ }
+
+ return lptstr_ret;
+}
+
+
+/*
+ * Convert an LPTSTR argument into a UTF-8 version
+ * of that argument. The result is allocated here
+ * and should be freed by the caller.
+ */
+LPSTR
+lptstr_to_utf8(LPTSTR arg_lptstr)
+{
+ int utf8str_len;
+ LPSTR utf8str_ret = NULL;
+
+ utf8str_len = WideCharToMultiByte( CP_UTF8, 0, arg_lptstr, -1, NULL, 0, NULL, NULL );
+ if(utf8str_len > 0)
+ {
+ utf8str_ret = (LPSTR)fs_get(utf8str_len * sizeof(CHAR));
+ utf8str_len = WideCharToMultiByte( CP_UTF8, 0,
+ arg_lptstr, -1, utf8str_ret, utf8str_len, NULL, NULL );
+ }
+
+ if(!utf8str_len)
+ {
+ /* check GetLastError()? */
+ utf8str_ret = (LPSTR)fs_get(sizeof(CHAR));
+ utf8str_ret[0] = 0;
+ }
+
+ return utf8str_ret;
+}
+
+
+/*
+ * Convert a UCS4 argument into an LPTSTR version
+ * of that argument. The result is allocated here
+ * and should be freed by the caller.
+ */
+LPTSTR
+ucs4_to_lptstr(UCS *arg_ucs4)
+{
+ LPTSTR ret_lptstr = NULL;
+ size_t len;
+ size_t i;
+
+ if(arg_ucs4){
+ len = ucs4_strlen(arg_ucs4);
+ ret_lptstr = (LPTSTR) fs_get((len+1) * sizeof(TCHAR));
+ /* bogus conversion ignores UTF-16 */
+ for(i = 0; i < len; i++)
+ ret_lptstr[i] = arg_ucs4[i];
+
+ ret_lptstr[len] = '\0';
+ }
+
+ return(ret_lptstr);
+}
+
+
+/*
+ * Convert an LPTSTR argument into a UCS4 version
+ * of that argument. The result is MemAlloc'd here
+ * and should be freed by the caller.
+ */
+UCS *
+lptstr_to_ucs4(LPTSTR arg_lptstr)
+{
+ UCS *ret_ucs4 = NULL;
+ size_t len;
+ size_t i;
+
+ if(arg_lptstr){
+ len = _tcslen(arg_lptstr);
+ ret_ucs4 = (UCS *) fs_get((len+1)*sizeof(UCS));
+ /* bogus conversion ignores UTF-16 */
+ for(i = 0; i < len; i++)
+ ret_ucs4[i] = arg_lptstr[i];
+
+ ret_ucs4[len] = '\0';
+ }
+
+ return(ret_ucs4);
+}
+
+#endif /* _WINDOWS */
+
+
+/*
+ * Pass in a stream of UTF-8 characters 1-at-a-time in 'c' and return obuf
+ * 1-at-a-time filled in with UCS characters. The return value is the
+ * number of valid characters in obuf to be used. It can only
+ * be 1 or 0 characters since we're only getting one UTF-8 character
+ * at a time.
+ */
+int
+utf8_to_ucs4_oneatatime(int c, CBUF_S *cb, UCS *obuf, int *obufwidth)
+{
+ int width = 0, outchars = 0;
+
+ if(!(cb && cb->cbufp))
+ return(0);
+
+ if(cb->cbufp < cb->cbuf+sizeof(cb->cbuf)){
+ unsigned char *inputp;
+ unsigned long remaining_octets;
+ UCS ucs;
+
+ *cb->cbufp++ = (unsigned char) c;
+ inputp = cb->cbuf;
+ remaining_octets = (cb->cbufp - cb->cbuf) * sizeof(unsigned char);
+ ucs = (UCS) utf8_get(&inputp, &remaining_octets);
+
+ switch(ucs){
+ case U8G_ENDSTRG: /* incomplete character, wait */
+ case U8G_ENDSTRI: /* incomplete character, wait */
+ break;
+
+ default:
+ if(ucs & U8G_ERROR || ucs == UBOGON){
+ /*
+ * None of these cases is supposed to happen. If it
+ * does happen then the input stream isn't UTF-8
+ * so something is wrong.
+ */
+ outchars++;
+ *obuf = '?';
+ cb->cbufp = cb->cbuf;
+ width = 1;
+ }
+ else{
+ outchars++;
+ if(ucs < 0x80 && ucs >= 0x20)
+ width = 1;
+
+ if(ucs >= 0x80 && (width=wcellwidth(ucs)) < 0){
+ /*
+ * This happens when we have a UTF-8 character that
+ * we aren't able to print in our locale. For example,
+ * if the locale is setup with the terminal
+ * expecting ISO-8859-1 characters then there are
+ * lots of UTF-8 characters that can't be printed.
+ * Print a '?' instead.
+ * Don't think this should happen in Windows.
+ */
+ *obuf = '?';
+ }
+ else{
+ *obuf = ucs;
+ }
+
+ /* update the input buffer */
+ if(inputp >= cb->cbufp) /* this should be the case */
+ cb->cbufp = cb->cbuf;
+ else{ /* extra chars for some reason? */
+ unsigned char *q, *newcbufp;
+
+ newcbufp = (cb->cbufp - inputp) + cb->cbuf;
+ q = cb->cbuf;
+ while(inputp < cb->cbufp)
+ *q++ = *inputp++;
+
+ cb->cbufp = newcbufp;
+ }
+ }
+
+ break;
+ }
+ }
+ else{ /* error */
+ *obuf = '?';
+ outchars = 1;
+ width = 1;
+ cb->cbufp = cb->cbuf; /* start over */
+ }
+
+ if(obufwidth)
+ *obufwidth = width;
+
+ return(outchars);
+}
+
+
+/*
+ * Return an allocated copy of a zero-terminated UCS-4 string.
+ */
+UCS *
+ucs4_cpystr(UCS *ucs4src)
+{
+ size_t arraysize;
+ UCS *ret = NULL;
+ size_t i;
+
+ if(!ucs4src)
+ return NULL;
+
+ arraysize = ucs4_strlen(ucs4src);
+
+ ret = (UCS *) fs_get((arraysize+1) * sizeof(*ret));
+ memset(ret, 0, (arraysize+1) * sizeof(*ret));
+
+ for(i = 0; i < arraysize; i++)
+ ret[i] = ucs4src[i];
+
+ return ret;
+}
+
+
+UCS *
+ucs4_strncpy(UCS *ucs4dst, UCS *ucs4src, size_t n)
+{
+ size_t i;
+
+ if(ucs4src && ucs4dst){
+ for(i = 0; i < n; i++){
+ ucs4dst[i] = ucs4src[i];
+ if(ucs4dst[i] == '\0')
+ break;
+ }
+ }
+
+ return ucs4dst;
+}
+
+
+UCS *
+ucs4_strncat(UCS *ucs4dst, UCS *ucs4src, size_t n)
+{
+ size_t i;
+ UCS *u;
+
+ if(ucs4src && ucs4dst){
+ for(u = ucs4dst; *u; u++)
+ ;
+
+ for(i = 0; i < n; i++){
+ u[i] = ucs4src[i];
+ if(u[i] == '\0')
+ break;
+ }
+
+ if(i == n)
+ u[i] = '\0';
+ }
+
+ return ucs4dst;
+}
+
+
+/*
+ * Like strlen only this returns the number of non-zero characters
+ * in a zero-terminated UCS-4 array.
+ */
+size_t
+ucs4_strlen(UCS *ucs4str)
+{
+ size_t i = 0;
+
+ if(ucs4str)
+ while(ucs4str[i])
+ i++;
+
+ return(i);
+}
+
+
+int
+ucs4_strcmp(UCS *s1, UCS *s2)
+{
+ for(; *s1 == *s2; s1++, s2++)
+ if(*s1 == '\0')
+ return 0;
+
+ return((*s1 < *s2) ? -1 : 1);
+}
+
+
+UCS *
+ucs4_strchr(UCS *s, UCS c)
+{
+ if(!s)
+ return NULL;
+
+ while(*s && *s != c)
+ s++;
+
+ if(*s || !c)
+ return s;
+ else
+ return NULL;
+}
+
+
+UCS *
+ucs4_strrchr(UCS *s, UCS c)
+{
+ UCS *ret = NULL;
+
+ if(!s)
+ return ret;
+
+ while(*s){
+ if(*s == c)
+ ret = s;
+
+ s++;
+ }
+
+ return ret;
+}
+
+
+/*
+ * Returns the screen cells width of the UTF-8 string argument.
+ */
+unsigned
+utf8_width(char *str)
+{
+ unsigned width = 0;
+ int this_width;
+ UCS ucs;
+ unsigned long remaining_octets;
+ char *readptr;
+
+ if(!(str && *str))
+ return(width);
+
+ readptr = str;
+ remaining_octets = readptr ? strlen(readptr) : 0;
+
+ while(remaining_octets > 0 && *readptr){
+
+ ucs = (UCS) utf8_get((unsigned char **) &readptr, &remaining_octets);
+
+ if(ucs & U8G_ERROR || ucs == UBOGON){
+ /*
+ * This should not happen, but do something to handle it anyway.
+ * Treat each character as a single width character, which is what should
+ * probably happen when we actually go to write it out.
+ */
+ remaining_octets--;
+ readptr++;
+ this_width = 1;
+ }
+ else{
+ this_width = wcellwidth(ucs);
+
+ /*
+ * If this_width is -1 that means we can't print this character
+ * with our current locale. Writechar will print a '?'.
+ */
+ if(this_width < 0)
+ this_width = 1;
+ }
+
+ width += (unsigned) this_width;
+ }
+
+ return(width);
+}
+
+
+/*
+ * Copy UTF-8 characters from src into dst.
+ * This is intended to be used if you want to truncate a string at
+ * the start instead of the end. For example, you have a long string
+ * like
+ * this_is_a_long_string
+ * but not enough space to fit it into a particular field. You want to
+ * end up with
+ * s_a_long_string
+ * where that fits in a particular width. Perhaps you'd use this with ...
+ * to get
+ * ...s_a_long_string
+ * This right adjusts the end of the string in the width space and
+ * cuts it off at the start. If there is enough width for the whole
+ * string it will copy the string into dst with no padding.
+ *
+ * Copy enough characters so that the result will have screen width of
+ * want_width screen cells in current locale.
+ *
+ * Dstlen is the available space in dst. No more than dstlen bytes will be written
+ * to dst. This is just for protection, it shouldn't be relied on to
+ * do anything useful. Dstlen should be large enough. Otherwise you'll get
+ * characters truncated in the middle or something like that.
+ *
+ * Returned value is the number of bytes written to dst, not including
+ * the possible terminating null.
+ *
+ * If we can't hit want_width exactly because of double width characters
+ * then we will pad the end of the string with space in order to make
+ * the width exact.
+ */
+size_t
+utf8_to_width_rhs(char *dst, /* destination buffer */
+ char *src, /* source string */
+ size_t dstlen, /* space in dest */
+ unsigned want_width) /* desired screen width */
+{
+ int this_width;
+ unsigned width_consumed = 0;
+ UCS ucs;
+ unsigned long remaining_octets;
+ char *readptr, *goodreadptr, *savereadptr, *endptr;
+ size_t nb = 0;
+
+ if(!src){
+ if(dstlen > 0)
+ dst[0] = '\0';
+
+ return nb;
+ }
+
+ /*
+ * Start at the end of the source string and go backwards until we
+ * get to the desired width, but not more than the width.
+ */
+ readptr = src + strlen(src);
+ endptr = readptr;
+ goodreadptr = readptr;
+ width_consumed = 0;
+ savereadptr = readptr;
+
+ for(readptr = savereadptr-1; readptr >= src && width_consumed < want_width && (endptr - readptr) < dstlen;
+ readptr = savereadptr-1){
+
+ savereadptr = readptr;
+ remaining_octets = goodreadptr - readptr;
+ ucs = (UCS) utf8_get((unsigned char **) &readptr, &remaining_octets);
+
+ /*
+ * Handling the error case is tough because an error will be the normal thing that
+ * happens as we back through the string. So we're just going to punt on the
+ * error for now.
+ */
+ if(!(ucs & U8G_ERROR || ucs == UBOGON)){
+ if(remaining_octets > 0){
+ /*
+ * This means there are some bad octets after this good
+ * character so things are not going to work out well.
+ * Bail out.
+ */
+ savereadptr = src; /* we're done */
+ }
+ else{
+ this_width = wcellwidth(ucs);
+
+ if(this_width < 0)
+ this_width = 1;
+
+ if(width_consumed + (unsigned) this_width <= want_width){ /* ok */
+ width_consumed += (unsigned) this_width;
+ goodreadptr = savereadptr;
+ }
+ else
+ savereadptr = src; /* we're done */
+ }
+ }
+ }
+
+ /*
+ * Copy characters from goodreadptr to endptr into dst.
+ */
+ nb = MIN(endptr-goodreadptr, dstlen-1);
+ strncpy(dst, goodreadptr, nb);
+ dst[nb] = '\0';
+
+ /*
+ * Pad out with spaces in order to hit width exactly.
+ */
+ while(width_consumed < want_width && nb < dstlen-1){
+ dst[nb++] = ' ';
+ dst[nb] = '\0';
+ width_consumed++;
+ }
+
+ return nb;
+}
+
+
+/*
+ * The arguments being converted are UTF-8 strings.
+ * This routine attempts to make it possible to use screen cell
+ * widths in a format specifier. In a one-byte per screen cell
+ * world we might have used %10.10s to cause a string to occupy
+ * 10 screen positions. Since the width and precision are really
+ * referring to numbers of bytes instead of screen positions that
+ * won't work with UTF-8 input. We emulate that behavior with
+ * the format string %w. %m.nw means to use the m and n as
+ * screen width indicators instead of bytes indicators.
+ *
+ * There is no reason to use this routine unless you want to use
+ * min field with or precision with the specifier. A plain %w without
+ * widths is equivalent exactly to a plain %s in a regular printf.
+ *
+ * Double-width characters complicate things. It may not be possible
+ * to satisfy the request exactly. For example, %3w for an input
+ * string that is made up of two double-width characters.
+ * This routine will arbitrarily use a trailing space character if
+ * needed to make the width come out correctly where a half of a
+ * double-width character would have been needed. We'll see how
+ * that works for us.
+ *
+ * %w only works for strings (it's a %s replacement).
+ *
+ * Buffer overflow is handled by the size argument. %.30s will work
+ * to limit a particular string to 30 bytes, but you lose that
+ * ability with %w, since it may write more than precision bytes
+ * in order to get to the desired width. It is best to choose
+ * size large enough so that it doesn't come into play, otherwise
+ * it may be possible to get partial UTF-8 characters because of
+ * the truncation.
+ *
+ * The return value isn't quite the same as the return value
+ * of snprintf. It is the number of bytes written, not counting
+ * the trailing null, just like snprintf. However, if it is
+ * truncated due to size then the output is size, not the
+ * number of characters that would have been written.
+ */
+int
+utf8_snprintf(char *dest, size_t size, char *fmt, ...)
+{
+ char newfmt[100], buf[20], *q, *pdest, *width_str, *end;
+ char *start_of_specifier;
+ char *input_str;
+ int int_arg;
+ double double_arg;
+ void *ptr_arg;
+ unsigned got_width;
+ int more_flags, ret, w;
+ int min_field_width, field_precision, modifier;
+ int flags_minus, flags_plus, flags_space, flags_zero, flags_pound;
+ va_list args;
+
+ newfmt[0] = '\0';
+ q = newfmt;
+
+ pdest = dest;
+
+#define IS_ROOM_IN_DEST(n_more_chars) \
+ ((pdest - dest + (n_more_chars) <= size) ? 1 : 0)
+
+ /*
+ * Strategy: Look through the fmt string for %w's. Replace the
+ * %w's in the format string with %s's but with possibly different
+ * width and precision arguments which will make it come out right.
+ * Then call the regular system vsnprintf with the altered format
+ * string but same arguments.
+ *
+ * That would be nice but it doesn't quite work. Why? Because a
+ * %*w will need to have the value in the integer argument the *
+ * refers to modified. Can't do it as far as I can tell. Or we could
+ * remove the integer argument somehow before calling printf. Can't
+ * do it. Or we could somehow add an additional conversion specifier
+ * that caused nothing to be printed but ate up the integer arg.
+ * Can't figure out how to do that either.
+ *
+ * Since we can't figure out how to do it, the alternative is to
+ * construct the result one piece at a time, pasting together the
+ * pieces from the different conversions.
+ */
+ va_start(args, fmt);
+
+ while(*fmt && IS_ROOM_IN_DEST(1)){
+ if(*fmt == '%'){
+ start_of_specifier = fmt++;
+
+ min_field_width = field_precision = -1;
+ flags_minus = flags_plus = flags_space = flags_zero = flags_pound = 0;
+
+ /* flags */
+ more_flags = 1;
+ while(more_flags){
+ switch(*fmt){
+ case '-':
+ flags_minus++;
+ fmt++;
+ break;
+
+ case '+':
+ flags_plus++;
+ fmt++;
+ break;
+
+ case ' ':
+ flags_space++;
+ fmt++;
+ break;
+
+ case '0':
+ flags_zero++;
+ fmt++;
+ break;
+
+ case '#':
+ flags_pound++;
+ fmt++;
+ break;
+
+ default:
+ more_flags = 0;
+ break;
+ }
+ }
+
+ /* minimum field width */
+ if(*fmt == '*'){
+ min_field_width = va_arg(args, int);
+ fmt++;
+ }
+ else if(*fmt >= '0' && *fmt <= '9'){
+ width_str = fmt;
+ while (*fmt >= '0' && *fmt <= '9')
+ fmt++;
+
+ strncpy(buf, width_str, MIN(fmt-width_str,sizeof(buf)));
+ if(sizeof(buf) > fmt-width_str)
+ buf[fmt-width_str] = '\0';
+
+ buf[sizeof(buf)-1] = '\0';
+
+ min_field_width = atoi(width_str);
+ }
+
+ /* field precision */
+ if(*fmt == '.'){
+ fmt++;
+ if(*fmt == '*'){
+ field_precision = va_arg(args, int);
+ fmt++;
+ }
+ else if(*fmt >= '0' && *fmt <= '9'){
+ width_str = fmt;
+ while (*fmt >= '0' && *fmt <= '9')
+ fmt++;
+
+ strncpy(buf, width_str, MIN(fmt-width_str,sizeof(buf)));
+ if(sizeof(buf) > fmt-width_str)
+ buf[fmt-width_str] = '\0';
+
+ buf[sizeof(buf)-1] = '\0';
+
+ field_precision = atoi(width_str);
+ }
+ }
+
+ /* length modifier */
+ if(*fmt == 'h' || *fmt == 'l' || *fmt == 'L')
+ modifier = *fmt++;
+
+ /* conversion character */
+ switch(*fmt){
+ case 'w':
+ /*
+ * work with va_arg(char *) to figure out width
+ * and precision needed to produce the screen width
+ * and precision asked for in %w using some of the
+ * utf8 width routines we have.
+ */
+
+ input_str = va_arg(args, char *);
+ if(field_precision >=0 || min_field_width >= 0)
+ w = utf8_width(input_str);
+
+ if(field_precision >= 0){
+ if(w <= field_precision)
+ field_precision = -1; /* print it all */
+ else{
+ /*
+ * We need to cut off some of the input_str
+ * in this case.
+ */
+ end = utf8_count_forw_width(input_str, field_precision, &got_width);
+ field_precision = (int) (end - input_str);
+ /* new w with this field_precision */
+ w = got_width;
+ }
+ }
+
+ /* need some padding */
+ if(min_field_width >= 0)
+ min_field_width = ((field_precision >= 0) ? field_precision : strlen(input_str)) +
+ MAX(0, min_field_width - w);
+
+ /*
+ * Now we just need to get the new format string
+ * set correctly in newfmt.
+ */
+ q = newfmt;
+ if(q-newfmt < sizeof(newfmt))
+ *q++ = '%';
+
+ if(flags_minus && q-newfmt < sizeof(newfmt))
+ *q++ = '-';
+ if(flags_plus && q-newfmt < sizeof(newfmt))
+ *q++ = '+';
+ if(flags_space && q-newfmt < sizeof(newfmt))
+ *q++ = ' ';
+ if(flags_zero && q-newfmt < sizeof(newfmt))
+ *q++ = '0';
+ if(flags_pound && q-newfmt < sizeof(newfmt))
+ *q++ = '#';
+
+ if(min_field_width >= 0){
+ snprintf(buf, sizeof(buf), "%d", min_field_width);
+ sstrncpy(&q, buf, sizeof(newfmt)-(q-newfmt));
+ }
+
+ if(field_precision >= 0){
+ if(q-newfmt < sizeof(newfmt))
+ *q++ = '.';
+
+ snprintf(buf, sizeof(buf), "%d", field_precision);
+ sstrncpy(&q, buf, sizeof(newfmt)-(q-newfmt));
+ }
+
+ if(q-newfmt < sizeof(newfmt))
+ *q++ = 's';
+
+ if(q-newfmt < sizeof(newfmt))
+ *q++ = '\0';
+
+ snprintf(pdest, size - (pdest-dest), newfmt, input_str);
+ pdest += strlen(pdest);
+
+ break;
+
+ case '\0':
+ fmt--;
+ break;
+
+ default:
+ /* make a new format which leaves out the dynamic '*' arguments */
+ q = newfmt;
+ if(q-newfmt < sizeof(newfmt))
+ *q++ = '%';
+
+ if(flags_minus && q-newfmt < sizeof(newfmt))
+ *q++ = '-';
+ if(flags_plus && q-newfmt < sizeof(newfmt))
+ *q++ = '+';
+ if(flags_space && q-newfmt < sizeof(newfmt))
+ *q++ = ' ';
+ if(flags_zero && q-newfmt < sizeof(newfmt))
+ *q++ = '0';
+ if(flags_pound && q-newfmt < sizeof(newfmt))
+ *q++ = '#';
+
+ if(min_field_width >= 0){
+ snprintf(buf, sizeof(buf), "%d", min_field_width);
+ sstrncpy(&q, buf, sizeof(newfmt)-(q-newfmt));
+ }
+
+ if(field_precision >= 0){
+ if(q-newfmt < sizeof(newfmt))
+ *q++ = '.';
+
+ snprintf(buf, sizeof(buf), "%d", field_precision);
+ sstrncpy(&q, buf, sizeof(newfmt)-(q-newfmt));
+ }
+
+ if(q-newfmt < sizeof(newfmt))
+ *q++ = *fmt;
+
+ if(q-newfmt < sizeof(newfmt))
+ *q++ = '\0';
+
+ switch(*fmt){
+ case 'd': case 'i': case 'o':
+ case 'x': case 'X': case 'u': case 'c':
+ int_arg = va_arg(args, int);
+ snprintf(pdest, size - (pdest-dest), newfmt, int_arg);
+ pdest += strlen(pdest);
+ break;
+
+ case 's':
+ input_str = va_arg(args, char *);
+ snprintf(pdest, size - (pdest-dest), newfmt, input_str);
+ pdest += strlen(pdest);
+ break;
+
+ case 'f': case 'e': case 'E':
+ case 'g': case 'G':
+ double_arg = va_arg(args, double);
+ snprintf(pdest, size - (pdest-dest), newfmt, double_arg);
+ pdest += strlen(pdest);
+ break;
+
+ case 'p':
+ ptr_arg = va_arg(args, void *);
+ snprintf(pdest, size - (pdest-dest), newfmt, ptr_arg);
+ pdest += strlen(pdest);
+ break;
+
+ case '%':
+ if(IS_ROOM_IN_DEST(1))
+ *pdest++ = '%';
+
+ break;
+
+ default:
+ /* didn't think of this type */
+ assert(0);
+ break;
+ }
+
+ break;
+ }
+
+ fmt++;
+ }
+ else{
+ if(IS_ROOM_IN_DEST(1))
+ *pdest++ = *fmt++;
+ }
+ }
+
+ ret = pdest - dest;
+
+ if(IS_ROOM_IN_DEST(1))
+ *pdest++ = '\0';
+
+ va_end(args);
+
+ return ret;
+}
+
+
+/*
+ * Copy UTF-8 characters from src into dst.
+ * Copy enough characters so that the result will have (<=) screen width of
+ * want_width screen cells in current locale.
+ *
+ * Dstlen is the available space in dst. No more than dstlen bytes will be written
+ * to dst.
+ *
+ * Returned value is the number of bytes written to dst, not including
+ * the possible terminating null.
+ * Got_width is another returned value. It is the width in screen cells of
+ * the string placed in dst. It will be the same as want_width if there
+ * are enough characters in the src to do that and if the character widths
+ * hit the width exactly. It will be less than want_width if we run out
+ * of src characters or if the next character width would skip over the
+ * width we want, because it is double width.
+ *
+ * Zero width characters are collected and included at the end of the string.
+ * That is, if we make it to want_width but there is still a zero length
+ * character sitting in src, we add that to dst. This might be an accent
+ * or something like that.
+ */
+size_t
+utf8_to_width(char *dst, /* destination buffer */
+ char *src, /* source string */
+ size_t dstlen, /* space in dst */
+ unsigned want_width, /* desired screen width */
+ unsigned *got_width) /* returned screen width in dst */
+{
+ int this_width;
+ unsigned width_consumed = 0;
+ UCS ucs;
+ unsigned long remaining_octets;
+ char *writeptr, *readptr, *savereadptr, *endptr;
+ int ran_out_of_space = 0;
+
+ readptr = src;
+
+ remaining_octets = readptr ? strlen(readptr) : 0;
+
+ writeptr = dst;
+ endptr = writeptr + dstlen;
+
+ if(readptr && writeptr){
+ while(width_consumed <= want_width && remaining_octets > 0 && writeptr < dst + dstlen && !ran_out_of_space){
+ savereadptr = readptr;
+ ucs = (UCS) utf8_get((unsigned char **) &readptr, &remaining_octets);
+
+ if(ucs & U8G_ERROR || ucs == UBOGON)
+ remaining_octets = 0;
+ else{
+ this_width = wcellwidth(ucs);
+
+ /*
+ * If this_width is -1 that means we can't print this character
+ * with our current locale. Writechar will print a '?'.
+ */
+ if(this_width < 0)
+ this_width = 1;
+
+ if(width_consumed + (unsigned) this_width <= want_width){
+ /* append this utf8 character to dst if it will fit */
+ if(writeptr + (readptr - savereadptr) < endptr){
+ width_consumed += this_width;
+ while(savereadptr < readptr)
+ *writeptr++ = *savereadptr++;
+ }
+ else
+ ran_out_of_space++; /* no more utf8 to dst */
+ }
+ else
+ remaining_octets = 0; /* we're done */
+ }
+ }
+
+ if(writeptr < endptr)
+ *writeptr = '\0';
+ }
+
+ if(got_width)
+ *got_width = width_consumed;
+
+ return(writeptr ? (writeptr - dst) : 0);
+}
+
+
+/*
+ * Str is a UTF-8 string.
+ * Count forward width screencell positions and return a pointer to the
+ * end of the string that is width wide.
+ * The returned pointer points at the next character (where the null would
+ * be placed).
+ *
+ * Got_width is another returned value. It is the width in screen cells of
+ * the string from str to the returned pointer. It will be the same as
+ * want_width if there are enough characters in the str to do that
+ * and if the character widths hit the width exactly. It will be less
+ * than want_width if we run out of characters or if the next character
+ * width would skip over the width we want, because it is double width.
+ */
+char *
+utf8_count_forw_width(char *str, unsigned want_width, unsigned *got_width)
+{
+ int this_width;
+ unsigned width_consumed = 0;
+ UCS ucs;
+ unsigned long remaining_octets;
+ char *readptr;
+ char *retptr;
+
+ retptr = readptr = str;
+
+ remaining_octets = readptr ? strlen(readptr) : 0;
+
+ while(width_consumed <= want_width && remaining_octets > 0){
+
+ ucs = (UCS) utf8_get((unsigned char **) &readptr, &remaining_octets);
+
+ if(ucs & U8G_ERROR || ucs == UBOGON){
+ /*
+ * This should not happen, but do something to handle it anyway.
+ * Treat each character as a single width character, which is what should
+ * probably happen when we actually go to write it out.
+ */
+ remaining_octets--;
+ readptr++;
+ this_width = 1;
+ }
+ else{
+ this_width = wcellwidth(ucs);
+
+ /*
+ * If this_width is -1 that means we can't print this character
+ * with our current locale. Writechar will print a '?'.
+ */
+ if(this_width < 0)
+ this_width = 1;
+ }
+
+ if(width_consumed + (unsigned) this_width <= want_width){
+ width_consumed += (unsigned) this_width;
+ retptr = readptr;
+ }
+ else
+ remaining_octets = 0; /* we're done */
+ }
+
+ if(got_width)
+ *got_width = width_consumed;
+
+ return(retptr);
+}
+
+
+/*
+ * Copy a null terminator into a UTF-8 string in place so that the string is
+ * no more than a certain screen width wide. If the string is already less
+ * than or equal in width to the requested width, no change is made.
+ *
+ * The actual width accomplished is returned. Note that it may be less than
+ * max_width due to double width characters as well as due to the fact that
+ * it fits wholly in the max_width.
+ *
+ * Returned value is the actual screen width of str when done.
+ *
+ * A side effect is that a terminating null may have been written into
+ * the passed in string.
+ */
+unsigned
+utf8_truncate(char *str, unsigned max_width)
+{
+ int this_width;
+ unsigned width_consumed = 0;
+ UCS ucs;
+ unsigned long remaining_octets;
+ char *readptr, *savereadptr;
+
+ readptr = str;
+
+ remaining_octets = readptr ? strlen(readptr) : 0;
+
+ if(readptr){
+ while(width_consumed <= max_width && remaining_octets > 0){
+
+ savereadptr = readptr;
+ ucs = (UCS) utf8_get((unsigned char **) &readptr, &remaining_octets);
+
+ if(ucs & U8G_ERROR || ucs == UBOGON){
+ /*
+ * This should not happen, but do something to handle it anyway.
+ * Treat each character as a single width character, which is what should
+ * probably happen when we actually go to write it out.
+ */
+ remaining_octets--;
+ readptr++;
+ this_width = 1;
+ }
+ else{
+ this_width = wcellwidth(ucs);
+
+ /*
+ * If this_width is -1 that means we can't print this character
+ * with our current locale. Writechar will print a '?'.
+ */
+ if(this_width < 0)
+ this_width = 1;
+ }
+
+ if(width_consumed + (unsigned) this_width <= max_width){
+ width_consumed += (unsigned) this_width;
+ }
+ else{
+ remaining_octets = 0; /* we're done */
+ *savereadptr = '\0';
+ }
+ }
+ }
+
+ return(width_consumed);
+}
+
+
+/*
+ * Copy UTF-8 characters from src into dst.
+ * Copy enough characters so that the result will have screen width of
+ * want_width screen cells in current locale.
+ * If there aren't enough characters in src to get to want_width, pad on
+ * left or right according to left_adjust argument.
+ *
+ * Dstlen is the available space in dst. No more than dstlen bytes will be written
+ * to dst. Dst will be null terminated if there is enough room, but not
+ * if that would overflow dst's len.
+ *
+ * Returned value is the number of bytes written to dst, not including
+ * the possible terminating null.
+ */
+size_t
+utf8_pad_to_width(char *dst, /* destination buffer */
+ char *src, /* source string */
+ size_t dstlen, /* space in dst */
+ unsigned want_width, /* desired screen width */
+ int left_adjust) /* adjust left or right in want_width columns */
+{
+ unsigned got_width = 0;
+ int need_more, howmany;
+ size_t len_left, bytes_used;
+
+ bytes_used = utf8_to_width(dst, src, dstlen, want_width, &got_width);
+ len_left = dstlen - bytes_used;
+
+ need_more = want_width - got_width;
+ howmany = MIN(need_more, len_left);
+
+ if(howmany > 0){
+ char *end, *newend, *p, *q;
+
+ end = dst + bytes_used;
+ newend = end + howmany;
+ if(left_adjust){
+ /*
+ * Add padding to end of string. Simply append
+ * the needed number of spaces, or however many will fit
+ * if we don't have enough space.
+ */
+ for(q = end; q < newend; q++)
+ *q = ' ';
+ }
+ else{
+ /*
+ * Add padding to start of string.
+ */
+
+ /* slide existing string over */
+ for(p = end - 1, q = newend - 1; p >= dst; p--, q--)
+ *q = *p;
+
+ /* fill rest with spaces */
+ for(; q >= dst; q--)
+ *q = ' ';
+ }
+
+ bytes_used += howmany;
+ }
+
+ if(bytes_used < dstlen)
+ dst[bytes_used] = '\0';
+
+ return(bytes_used);
+}
+
+
+/*
+ * Str is a UTF-8 string.
+ * Start_here is a pointer into the string. It points one position past
+ * the last byte that should be considered a part of the length string.
+ * Count back want_width screencell positions and return a pointer to the
+ * start of the string that is want_width wide and ends with start_here.
+ *
+ * Since characters may be more than one cell width wide we may end up
+ * skipping over the exact width. That is, if we need to we'll go back
+ * too far (by one cell width). Account for that in the call by looking
+ * at got_width.
+ *
+ * Note that this call gives a possible got_width == want_width+1 as
+ * opposed to utf8_count_forw_width which gives got_width == want-1 instead.
+ * That was just what was needed at the time, maybe it needs to be
+ * optional.
+ */
+char *
+utf8_count_back_width(char *str, char *start_here, unsigned want_width, unsigned *got_width)
+{
+ unsigned width_consumed = 0;
+ int this_width;
+ UCS ucs;
+ unsigned long remaining_octets;
+ char *ptr, *savereadptr, *goodreadptr;
+
+ savereadptr = start_here;
+ goodreadptr = start_here;
+
+ for(ptr = savereadptr - 1; width_consumed < want_width && ptr >= str; ptr = savereadptr - 1){
+
+ savereadptr = ptr;
+ remaining_octets = goodreadptr - ptr;
+ ucs = (UCS) utf8_get((unsigned char **) &ptr, &remaining_octets);
+
+ if(!(ucs & U8G_ERROR || ucs == UBOGON)){
+ if(remaining_octets > 0){
+ /*
+ * This means there are some bad octets after this good
+ * character so things are not going to work out well.
+ * Bail out.
+ */
+ savereadptr = str; /* we're done */
+ }
+ else{
+ this_width = wcellwidth(ucs);
+
+ /*
+ * If this_width is -1 that means we can't print this character
+ * with our current locale. Writechar will print a '?'.
+ */
+ if(this_width < 0)
+ this_width = 1;
+
+ width_consumed += (unsigned) this_width;
+ goodreadptr = savereadptr;
+ }
+ }
+ }
+
+ if(got_width)
+ *got_width = width_consumed;
+
+ return(savereadptr);
+}
+
+
+/*----------------------------------------------------------------------
+ copy the source string onto the destination string returning with
+ the destination string pointer at the end of the destination text
+
+ motivation for this is to avoid twice passing over a string that's
+ being appended to twice (i.e., strcpy(t, x); t += strlen(t))
+
+ This doesn't really belong here but it is used here.
+ ----*/
+void
+sstrncpy(char **d, char *s, int n)
+{
+ while(n-- > 0 && (**d = *s++) != '\0')
+ (*d)++;
+}
+
+
+/*
+ * If use_system_routines is set then NULL is the return value and it is
+ * not an error. Display_charmap and keyboard_charmap should come over as
+ * malloced strings and will be filled in with the result.
+ *
+ * Returns a void pointer to the input_cs CHARSET which is
+ * passed to mbtow via kbseq().
+ * If !use_system_routines && NULL is returned, that is an error and err should
+ * have a message.
+ * display_charmap and keyboard_charmap should be malloced data and may be
+ * realloced and changed here.
+ */
+int
+setup_for_input_output(int use_system_routines, char **display_charmap,
+ char **keyboard_charmap, void **input_cs_arg, char **err)
+{
+ const CHARSET *cs;
+ const CHARSET *input_cs = NULL;
+ int already_tried = 0;
+ int supported = 0;
+ char buf[1000];
+
+#define cpstr(s) strcpy((char *)fs_get(1+strlen(s)), s)
+
+ if(err)
+ *err = NULL;
+
+ if(!display_charmap || !keyboard_charmap || !input_cs_arg){
+ *err = cpstr("Bad call to setup_for_input_output");
+ return(-1);
+ }
+
+ if(use_system_routines){
+#if PREREQ_FOR_SYS_TRANSLATION
+ char *dcm;
+
+ dcm = nl_langinfo_codeset_wrapper();
+ dcm = dcm ? dcm : "US-ASCII";
+
+ init_utf8_display(0, NULL);
+ if(*display_charmap){
+ if(dcm && strucmp(*display_charmap, dcm)){
+ snprintf(buf, sizeof(buf),
+ _("Display character set \"%s\" is ignored when using system translation"),
+ *display_charmap);
+
+ *err = cpstr(buf);
+ }
+
+ fs_give((void **) display_charmap);
+ }
+
+ if(*keyboard_charmap){
+ if(!*err && dcm && strucmp(*keyboard_charmap, dcm)){
+ snprintf(buf, sizeof(buf),
+ _("Keyboard character set \"%s\" is ignored when using system translation"),
+ *keyboard_charmap);
+
+ *err = cpstr(buf);
+ }
+
+ fs_give((void **) keyboard_charmap);
+ }
+
+ *display_charmap = cpstr(dcm);
+ *keyboard_charmap = cpstr(dcm);
+#else
+ *err = cpstr("Bad call to setup_for_input_output");
+#endif
+
+ *input_cs_arg = NULL;
+ return(0);
+ }
+
+
+try_again1:
+ if(!(*display_charmap))
+ *display_charmap = cpstr("US-ASCII");
+
+ if(!(*keyboard_charmap))
+ *keyboard_charmap = cpstr(*display_charmap);
+
+ if(*keyboard_charmap){
+ supported = input_charset_is_supported(*keyboard_charmap);
+
+ if(supported){
+ if(!strucmp(*keyboard_charmap, "utf-8"))
+ input_cs = utf8_charset(*keyboard_charmap);
+ else if((cs = utf8_charset(*keyboard_charmap)) != NULL)
+ input_cs = cs;
+ }
+ else{
+ if(err && !*err){
+ int iso2022jp = 0;
+
+ if(!strucmp(*keyboard_charmap, "ISO-2022-JP"))
+ iso2022jp = 1;
+
+ snprintf(buf, sizeof(buf),
+ /* TRANSLATORS: The first argument is the name of the character
+ set the user is trying to use (which is unsupported by alpine).
+ The second argument is " (except for posting)" if they are
+ trying to use ISO-2022-JP for something other than posting. */
+ _("Character set \"%s\" is unsupported%s, using US-ASCII"),
+ *keyboard_charmap,
+ iso2022jp ? _(" (except for posting)") : "");
+
+ *err = cpstr(buf);
+ }
+
+ input_cs = NULL;
+ fs_give((void **) keyboard_charmap);
+ *keyboard_charmap = cpstr("US-ASCII");
+ if(!already_tried){
+ already_tried++;
+ goto try_again1;
+ }
+ }
+ }
+
+
+try_again2:
+ if(!(*display_charmap))
+ *display_charmap = cpstr("US-ASCII");
+
+ if(*display_charmap){
+ supported = output_charset_is_supported(*display_charmap);
+ if(supported){
+ if(!strucmp(*display_charmap, "utf-8"))
+ init_utf8_display(1, NULL);
+ else if((cs = utf8_charset(*display_charmap)) != NULL)
+ init_utf8_display(0, utf8_rmap_gen(cs, NULL));
+ }
+ else{
+ if(err && !*err){
+ int iso2022jp = 0;
+
+ if(!strucmp(*display_charmap, "ISO-2022-JP"))
+ iso2022jp = 1;
+
+ snprintf(buf, sizeof(buf),
+ _("Character set \"%s\" is unsupported%s, using US-ASCII"),
+ *display_charmap,
+ iso2022jp ? _(" (except for posting)") : "");
+
+ *err = cpstr(buf);
+ }
+
+ fs_give((void **) display_charmap);
+ if(!already_tried){
+ already_tried++;
+ goto try_again2;
+ }
+ }
+ }
+ else{
+ if(err && !*err)
+ *err = cpstr(_("Help, can't figure out display character set or even use US-ASCII."));
+ }
+
+#undef cpstr
+
+ *input_cs_arg = (void *) input_cs;
+
+ return(0);
+}
+
+
+int
+input_charset_is_supported(char *input_charset)
+{
+ const CHARSET *cs;
+
+ if(!(input_charset && *input_charset))
+ return 0;
+
+ if(!strucmp(input_charset, "utf-8"))
+ return 1;
+
+ if((cs = utf8_charset(input_charset)) != NULL){
+
+ /*
+ * This was true 2006-09-25.
+ */
+ switch(cs->type){
+ case CT_ASCII: case CT_1BYTE0: case CT_1BYTE:
+ case CT_1BYTE8: case CT_EUC: case CT_DBYTE:
+ case CT_DBYTE2: case CT_SJIS: case CT_UCS2:
+ case CT_UCS4: case CT_UTF16:
+ return 1;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+
+int
+output_charset_is_supported(char *output_charset)
+{
+ const CHARSET *cs;
+
+ if(!(output_charset && *output_charset))
+ return 0;
+
+ if(!strucmp(output_charset, "utf-8"))
+ return 1;
+
+ if((cs = utf8_charset(output_charset)) != NULL && utf8_rmap_gen(cs, NULL))
+ return 1;
+
+ return 0;
+}
+
+
+int
+posting_charset_is_supported(char *posting_charset)
+{
+ return(posting_charset && *posting_charset
+ && (!strucmp(posting_charset, "ISO-2022-JP")
+ || output_charset_is_supported(posting_charset)));
+}
+
+
+/*
+ * This function is only defined in this special case and so calls
+ * to it should be wrapped in the same macro conditionals.
+ *
+ * Returns the default display charset for a UNIX terminal emulator,
+ * it is what nl_langinfo(CODESET) should return but we need to
+ * wrap nl_langinfo because we know of strange behaving implementations.
+ */
+#if !defined(_WINDOWS) && HAVE_LANGINFO_H && defined(CODESET)
+char *
+nl_langinfo_codeset_wrapper(void)
+{
+ char *ret = NULL;
+
+ ret = nl_langinfo(CODESET);
+
+ /*
+ * If the value returned from nl_langinfo() is not a real charset,
+ * see if we can figure out what they meant. If we can't figure it
+ * out return NULL and let the caller decide what to do.
+ */
+ if(ret && *ret && !output_charset_is_supported(ret)){
+ if(!strcmp("ANSI_X3.4-1968", ret)
+ || !strcmp("646", ret)
+ || !strcmp("ASCII", ret)
+ || !strcmp("C", ret)
+ || !strcmp("POSIX", ret))
+ ret = "US-ASCII";
+ else if(!strucmp(ret, "UTF8"))
+ ret = "UTF-8";
+ else if(!strucmp(ret, "EUCJP"))
+ ret = "EUC-JP";
+ else if(!strucmp(ret, "EUCKP"))
+ ret = "EUC-KP";
+ else if(!strucmp(ret, "SJIS"))
+ ret = "SHIFT-JIS";
+ else if(strstr(ret, "8859")){
+ char *p;
+
+ /* check for digits after 8859 */
+ p = strstr(ret, "8859");
+ p += 4;
+ if(!isdigit(*p))
+ p++;
+
+ if(isdigit(*p)){
+ static char buf[12];
+
+ memset(buf, 0, sizeof(buf));
+ strncpy(buf, "ISO-8859-", sizeof(buf));
+ buf[9] = *p++;
+ if(isdigit(*p))
+ buf[10] = *p;
+
+ ret = buf;
+ }
+ }
+ }
+
+ if(ret && !output_charset_is_supported(ret))
+ ret = NULL;
+
+ return(ret);
+}
+#endif
+
+
+/*
+ * Convert the "orig" string from UTF-8 to "charset". If no conversion is
+ * needed the return value will point to orig. If a conversion is done,
+ * the return string should be freed by the caller.
+ * If not possible, returns NULL.
+ */
+char *
+utf8_to_charset(char *orig, char *charset, int report_err)
+{
+ SIZEDTEXT src, dst;
+ char *ret = orig;
+
+ if(!charset || !charset[0] || !orig || !orig[0] || !strucmp(charset, "utf-8"))
+ return ret;
+
+ src.size = strlen(orig);
+ src.data = (unsigned char *) orig;
+
+ if(!strucmp(charset, "us-ascii")){
+ size_t i;
+
+ for(i = 0; i < src.size; i++)
+ if(src.data[i] & 0x80)
+ return NULL;
+
+ return ret;
+ }
+
+ /*
+ * This works for ISO-2022-JP because of special code in utf8_cstext
+ * but not for other 2022 charsets.
+ */
+ memset(&dst, 0, sizeof(dst));
+ if(utf8_cstext(&src, charset, &dst, report_err ? 0 : '?') && dst.size > 0 && dst.data)
+ ret = (char *) dst.data; /* c-client already null terminates it */
+ else
+ ret = NULL;
+
+ if((unsigned char *) ret != dst.data && dst.data)
+ fs_give((void **) &dst.data);
+
+ return ret;
+}
+
+
+/*
+ * Turn a number into a string with comma's
+ *
+ * Args: number -- The long to be turned into a string.
+ *
+ * Result: pointer to static string representing number with commas
+ * Can use up to 3 comatose results at once.
+ */
+char *
+comatose(long int number)
+{
+ long i, x, done_one;
+ static char buf[3][50];
+ static int whichbuf = 0;
+ char *b;
+
+ whichbuf = (whichbuf + 1) % 3;
+
+ if(number == 0){
+ strncpy(buf[whichbuf], "0", sizeof(buf[0]));
+ buf[whichbuf][sizeof(buf[0])-1] = '\0';
+ return(buf[whichbuf]);
+ }
+
+ done_one = 0;
+ b = buf[whichbuf];
+ for(i = 1000000000; i >= 1; i /= 1000) {
+ x = number / i;
+ number = number % i;
+ if(x != 0 || done_one) {
+ if(b != buf[whichbuf] && (b-buf[whichbuf]) < sizeof(buf[0]))
+ *b++ = ',';
+
+ snprintf(b, sizeof(buf[0])-(b-buf[whichbuf]), done_one ? "%03ld" : "%ld", x);
+ b += strlen(b);
+ done_one = 1;
+ }
+ }
+
+ if(b-buf[whichbuf] < sizeof(buf[0]))
+ *b = '\0';
+
+ return(buf[whichbuf]);
+}
+
+
+/* leave out the commas */
+char *
+tose(long int number)
+{
+ static char buf[3][50];
+ static int whichbuf = 0;
+
+ whichbuf = (whichbuf + 1) % 3;
+
+ snprintf(buf[whichbuf], sizeof(buf[0]), "%ld", number);
+
+ return(buf[whichbuf]);
+}
+
+
+/*
+ * line_paint - where the real work of managing what is displayed gets done.
+ */
+void
+line_paint(int offset, /* current dot offset into vl */
+ struct display_line *displ,
+ int *passwd) /* flag to hide display of chars */
+{
+ int i, w, w2, already_got_one = 0;
+ int vfirst, vlast, dfirst, dlast, vi, di;
+ int new_vbase;
+ unsigned (*width_a_to_b)(UCS *, int, int);
+
+ /*
+ * Set passwd to 10 in caller if you want to conceal the
+ * password but not print asterisks for feedback.
+ *
+ * Set passwd to 1 in caller to conceal by printing asterisks.
+ */
+ if(passwd && *passwd >= 10){ /* don't show asterisks */
+ if(*passwd > 10)
+ return;
+ else
+ *passwd = 11; /* only blat once */
+
+ i = 0;
+ (*displ->movecursor)(displ->row, displ->col);
+ while(i++ <= displ->dwid)
+ (*displ->writechar)(' ');
+
+ (*displ->movecursor)(displ->row, displ->col);
+ return;
+ }
+
+ if(passwd && *passwd)
+ width_a_to_b = single_width_chars_a_to_b;
+ else
+ width_a_to_b = ucs4_str_width_a_to_b;
+
+ /*
+ * vl is the virtual line (the actual data). We operate on it by typing
+ * characters to be added and deleting and so forth. In this routine we
+ * copy a subset of those UCS-4 characters in vl into dl, the display
+ * array, and show that subset on the screen.
+ *
+ * Offset is the location of the cursor in vl.
+ *
+ * We will display the string starting from vbase.
+ * We have dwid screen cells to work in.
+ * We may have to adjust vbase in order to display the
+ * part of the string that contains the cursor.
+ *
+ * We'll make the display look like
+ * vl a b c d e f g h i j k l m
+ * xxxxxxxxxxxxx <- width dwid window
+ * < d e f g h >
+ * |
+ * vbase
+ * The < will be there if vbase > 0.
+ * The > will be there if the string from vbase to the
+ * end can't all fit in the window.
+ */
+
+ memset(displ->dl, 0, displ->dlen * sizeof(UCS));
+
+ /*
+ * Adjust vbase so offset is not out of the window to the right.
+ * (The +2 in w + 2 is for a possible " >" if the string goes past
+ * the right hand edge of the window and if the last visible character
+ * is double wide. We don't want the offset to be under that > character.)
+ */
+ for(w = (*width_a_to_b)(displ->vl, displ->vbase, offset);
+ w + 2 + (displ->vbase ? 1 : 0) > displ->dwid;
+ w = (*width_a_to_b)(displ->vl, displ->vbase, offset)){
+ /*
+ * offset is off the window to the right
+ * It looks like a b c d e f g h
+ * | |
+ * vbase offset
+ * and offset is either past the right edge,
+ * or right at the right edge (and maybe under >),
+ * or one before right at the edge (and maybe on space
+ * for half a character).
+ *
+ * Since the characters may be double width it is slightly
+ * complicated to figure out how far to increase vbase.
+ * We're going to scoot over past width w/2 characters and
+ * then see if that's sufficient.
+ */
+ new_vbase = displ->vbase + 1;
+ for(w2 = (*width_a_to_b)(displ->vl, displ->vbase+1, new_vbase);
+ w2 < displ->dwid/2;
+ w2 = (*width_a_to_b)(displ->vl, displ->vbase+1, new_vbase))
+ new_vbase++;
+
+ displ->vbase = new_vbase;
+ }
+
+ /* adjust so offset is not out of the window to the left */
+ while(displ->vbase > 0 && displ->vbase >= offset){
+ /* add about dwid/2 more width */
+ new_vbase = displ->vbase - 1;
+ for(w2 = (*width_a_to_b)(displ->vl, new_vbase, displ->vbase);
+ w2 < (displ->dwid+1)/2 && new_vbase > 0;
+ w2 = (*width_a_to_b)(displ->vl, new_vbase, displ->vbase))
+ new_vbase--;
+
+ /* but don't let it get too small, recheck off right end */
+ for(w = (*width_a_to_b)(displ->vl, new_vbase, offset);
+ w + 2 + (new_vbase ? 1 : 0) > displ->dwid;
+ w = (*width_a_to_b)(displ->vl, displ->vbase, offset))
+ new_vbase++;
+
+ displ->vbase = MAX(new_vbase, 0);
+ }
+
+ if(displ->vbase == 1 && ((passwd && *passwd) || wcellwidth(displ->vl[0]) == 1))
+ displ->vbase = 0;
+
+ vfirst = displ->vbase;
+ dfirst = 0;
+ if(displ->vbase > 0){ /* off screen cue left */
+ dfirst = 1; /* index which matches vfirst */
+ displ->dl[0] = '<';
+ }
+
+ vlast = displ->vused-1; /* end */
+ w = (*width_a_to_b)(displ->vl, vfirst, vlast);
+
+ if(w + dfirst > displ->dwid){ /* off window right */
+
+ /* find last ucs character to be printed */
+ while(w + dfirst > displ->dwid - 1) /* -1 for > */
+ w = (*width_a_to_b)(displ->vl, vfirst, --vlast);
+
+ /* worry about double-width characters */
+ if(w + dfirst == displ->dwid - 1){ /* no prob, hit it exactly */
+ dlast = dfirst + vlast - vfirst + 1; /* +1 for > */
+ displ->dl[dlast] = '>';
+ }
+ else{
+ dlast = dfirst + vlast - vfirst + 1;
+ displ->dl[dlast++] = ' ';
+ displ->dl[dlast] = '>';
+ }
+ }
+ else
+ dlast = dfirst + vlast - vfirst;
+
+ /*
+ * Copy the relevant part of the virtual line into the display line.
+ */
+ for(vi = vfirst, di = dfirst; vi <= vlast; vi++, di++)
+ if(passwd && *passwd)
+ displ->dl[di] = '*'; /* to conceal password */
+ else
+ displ->dl[di] = displ->vl[vi];
+
+ /*
+ * Add spaces to clear the rest of the line.
+ * We have dwid total space to fill.
+ */
+ w = (*width_a_to_b)(displ->dl, 0, dlast); /* width through dlast */
+ for(di = dlast+1, i = displ->dwid - w; i > 0 ; i--)
+ displ->dl[di++] = ' ';
+
+ /*
+ * Draw from left to right, skipping until we get to
+ * something that is different. Characters may be different
+ * widths than they were initially so paint from there the
+ * rest of the way.
+ */
+ for(di = 0; displ->dl[di]; di++){
+ if(already_got_one || displ->dl[di] != displ->olddl[di]){
+ /* move cursor first time */
+ if(!already_got_one++){
+ w = (di > 0) ? (*width_a_to_b)(displ->dl, 0, di-1) : 0;
+ (*displ->movecursor)(displ->row, displ->col + w);
+ }
+
+ (*displ->writechar)(displ->dl[di]);
+ displ->olddl[di] = displ->dl[di];
+ }
+ }
+
+ memset(&displ->olddl[di], 0, (displ->dlen - di) * sizeof(UCS));
+
+ /*
+ * Move the cursor to the offset.
+ *
+ * The offset is relative to the start of the virtual array. We need
+ * to find the location on the screen. The offset into the display array
+ * will be offset-vbase+dfirst. We want to be at the start of that
+ * character, so we need to find the width of all the characters up
+ * to that point.
+ */
+ w = (offset > 0) ? (*width_a_to_b)(displ->dl, 0, offset-displ->vbase+dfirst-1) : 0;
+
+ (*displ->movecursor)(displ->row, displ->col + w);
+}
+
+
+/*
+ * This is just like ucs4_str_width_a_to_b() except all of the characters
+ * are assumed to be of width 1. This is for printing out *'s when user
+ * enters a password, while still managing to use the same code to do the
+ * display.
+ */
+unsigned
+single_width_chars_a_to_b(UCS *ucsstr, int a, int b)
+{
+ unsigned width = 0;
+ int i;
+
+ if(ucsstr)
+ for(i = a; i <= b && ucsstr[i]; i++)
+ width++;
+
+ return width;
+}
diff --git a/pith/charconv/utf8.h b/pith/charconv/utf8.h
new file mode 100644
index 00000000..d22a8a7c
--- /dev/null
+++ b/pith/charconv/utf8.h
@@ -0,0 +1,106 @@
+/*-----------------------------------------------------------------------
+ $Id: utf8.h 1025 2008-04-08 22:59:38Z hubert@u.washington.edu $
+ -----------------------------------------------------------------------*/
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_CHARCONV_UTF8_INCLUDED
+#define PITH_CHARCONV_UTF8_INCLUDED
+
+
+#include <general.h>
+#include "../filttype.h"
+
+
+/* flags for convert_to_utf8 */
+#define CU8_NONE 0x00
+#define CU8_NOINFER 0x01 /* Not ok to infer charset */
+
+
+/*
+ * The data in vl and dl is UCS-4 characters.
+ * They are arrays of size vlen and dlen of unsigned longs.
+ */
+struct display_line {
+ int row, col; /* where display starts */
+ UCS *vl; /* virtual line, the actual data string */
+ int vlen; /* size of vl array */
+ int vused; /* elements of vl in use */
+ int vbase; /* index into array, first virtual char on display */
+ UCS *dl; /* visible part of virtual line on display */
+ UCS *olddl;
+ int dlen; /* size of dl array */
+ int dwid; /* screenwidth avail for dl */
+ void (*movecursor)(int, int);
+ void (*writechar)(UCS);
+};
+
+
+/*
+ * Exported Prototypes
+ */
+void init_utf8_display(int, void *);
+int wcellwidth(UCS);
+int wtomb(char *, UCS);
+UCS mbtow(void *, unsigned char **, unsigned long *);
+void set_locale_charmap(char *);
+char *convert_to_utf8(char *, char *, int);
+char *convert_to_locale(char *);
+int utf8_to_locale(int c, CBUF_S *cb, unsigned char obuf[], size_t obuf_size);
+unsigned ucs4_str_width(UCS *);
+unsigned ucs4_str_width_a_to_b(UCS *, int, int);
+unsigned ucs4_str_width_ptr_to_ptr(UCS *, UCS *);
+UCS *ucs4_particular_width(UCS*, int);
+UCS *utf8_to_ucs4_cpystr(char *);
+char *ucs4_to_utf8_cpystr(UCS *);
+char *ucs4_to_utf8_cpystr_n(UCS *, int);
+#ifdef _WINDOWS
+LPTSTR utf8_to_lptstr(LPSTR);
+LPSTR lptstr_to_utf8(LPTSTR);
+LPTSTR ucs4_to_lptstr(UCS *);
+UCS *lptstr_to_ucs4(LPTSTR);
+#endif /* _WINDOWS */
+int utf8_to_ucs4_oneatatime(int, CBUF_S *, UCS *, int *);
+size_t ucs4_strlen(UCS *s);
+int ucs4_strcmp(UCS *s1, UCS *s2);
+UCS *ucs4_cpystr(UCS *s);
+UCS *ucs4_strncpy(UCS *ucs4dst, UCS *ucs4src, size_t n);
+UCS *ucs4_strncat(UCS *ucs4dst, UCS *ucs4src, size_t n);
+UCS *ucs4_strchr(UCS *s, UCS c);
+UCS *ucs4_strrchr(UCS *s, UCS c);
+unsigned utf8_width(char *);
+size_t utf8_to_width_rhs(char *, char *, size_t, unsigned);
+int utf8_snprintf(char *, size_t, char *, ...);
+size_t utf8_to_width(char *, char *, size_t, unsigned, unsigned *);
+size_t utf8_pad_to_width(char *, char *, size_t, unsigned, int);
+unsigned utf8_truncate(char *, unsigned);
+char *utf8_count_back_width(char *, char *, unsigned, unsigned *);
+char *utf8_count_forw_width(char *, unsigned, unsigned *);
+void sstrncpy(char **, char *, int);
+int setup_for_input_output(int, char **, char **, void **, char **);
+int input_charset_is_supported(char *);
+int output_charset_is_supported(char *);
+int posting_charset_is_supported(char *);
+char *utf8_to_charset(char *, char *, int);
+char *comatose(long);
+char *tose(long);
+void line_paint(int, struct display_line *, int *);
+
+#if !defined(_WINDOWS) && HAVE_LANGINFO_H && defined(CODESET)
+char *nl_langinfo_codeset_wrapper(void);
+#endif
+
+
+#endif /* PITH_CHARCONV_UTF8_INCLUDED */
diff --git a/pith/charset.c b/pith/charset.c
new file mode 100644
index 00000000..6177c7c4
--- /dev/null
+++ b/pith/charset.c
@@ -0,0 +1,929 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: charset.c 1032 2008-04-11 00:30:04Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/charset.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/escapes.h"
+#include "../pith/mimedesc.h"
+#include "../pith/filter.h"
+#include "../pith/string.h"
+#include "../pith/options.h"
+
+
+/*
+ * Internal prototypes
+ */
+int rfc1522_token(char *, int (*)(int), char *, char **);
+int rfc1522_valtok(int);
+int rfc1522_valenc(int);
+int rfc1522_valid(char *, char **, char **, char **, char **);
+void rfc1522_copy_and_transliterate(unsigned char *, unsigned char **, size_t,
+ unsigned char *, unsigned long, char *);
+unsigned char *rfc1522_encoded_word(unsigned char *, int, char *);
+char *rfc1522_8bit(void *, int);
+char *rfc1522_binary(void *, int);
+
+
+char *
+body_charset(MAILSTREAM *stream, long int msgno, unsigned char *section)
+{
+ BODY *body;
+ char *charset;
+
+
+ if((body = mail_body(stream, msgno, section)) && body->type == TYPETEXT){
+ if(!(charset = parameter_val(body->parameter, "charset")))
+ charset = cpystr("US-ASCII");
+
+ return(charset);
+ }
+
+ return(NULL);
+}
+
+
+/*
+ * Copies the source string into allocated space with the 8-bit EUC codes
+ * (on Unix) or the Shift-JIS (on PC) converted into ISO-2022-JP.
+ * Caller is responsible for freeing the result.
+ */
+unsigned char *
+trans_euc_to_2022_jp(unsigned char *src)
+{
+ size_t len, alloc;
+ unsigned char *rv, *p, *q;
+ int inside_esc_seq = 0;
+ int c1 = -1; /* remembers first of pair for Shift-JIS */
+
+ if(!src)
+ return(NULL);
+
+ len = strlen((char *) src);
+
+ /*
+ * Worst possible increase is every other character an 8-bit character.
+ * In that case, each of those gets 6 extra charactes for the escape
+ * sequences. We're not too concerned about the extra length because
+ * these are relatively short strings.
+ */
+ alloc = len + 1 + ((len+1)/2) * 6;
+ rv = (unsigned char *) fs_get(alloc * sizeof(char));
+
+ for(p = src, q = rv; *p; p++){
+ if(inside_esc_seq){
+ if(c1 >= 0){ /* second of a pair? */
+ int adjust = *p < 159;
+ int rowOffset = c1 < 160 ? 112 : 176;
+ int cellOffset = adjust ? (*p > 127 ? 32 : 31) : 126;
+
+ *q++ = ((c1 - rowOffset) << 1) - adjust;
+ *q++ = *p - cellOffset;
+ c1 = -1;
+ }
+ else if(*p & 0x80){
+ *q++ = (*p & 0x7f);
+ }
+ else{
+ *q++ = '\033';
+ *q++ = '(';
+ *q++ = 'B';
+ *q++ = (*p);
+ c1 = -1;
+ inside_esc_seq = 0;
+ }
+ }
+ else{
+ if(*p & 0x80){
+ *q++ = '\033';
+ *q++ = '$';
+ *q++ = 'B';
+ *q++ = (*p & 0x7f);
+ inside_esc_seq = 1;
+ }
+ else{
+ *q++ = (*p);
+ }
+ }
+ }
+
+ if(inside_esc_seq){
+ *q++ = '\033';
+ *q++ = '(';
+ *q++ = 'B';
+ }
+
+ *q = '\0';
+
+ return(rv);
+}
+
+
+/*
+ * * * * * * * * * RFC 1522 support routines * * * * * * * *
+ *
+ * RFC 1522 support is *very* loosely based on code contributed
+ * by Lars-Erik Johansson <lej@cdg.chalmers.se>. Thanks to Lars-Erik,
+ * and appologies for taking such liberties with his code.
+ */
+
+#define RFC1522_INIT "=?"
+#define RFC1522_INIT_L 2
+#define RFC1522_TERM "?="
+#define RFC1522_TERM_L 2
+#define RFC1522_DLIM "?"
+#define RFC1522_DLIM_L 1
+#define RFC1522_MAXW 256 /* RFC's say 75, but no senders seem to care*/
+#define ESPECIALS "()<>@,;:\"/[]?.="
+#define RFC1522_OVERHEAD(S) (RFC1522_INIT_L + RFC1522_TERM_L + \
+ (2 * RFC1522_DLIM_L) + strlen(S) + 1);
+#define RFC1522_ENC_CHAR(C) (((C) & 0x80) || !rfc1522_valtok(C) \
+ || (C) == '_' )
+
+/*
+ * rfc1522_decode_to_utf8 - try to decode the given source string ala RFC 2047
+ * (obsoleted RFC 1522) into the given destination buffer,
+ * encoded in UTF-8.
+ *
+ * How large should d be? The decoded string of octets will fit in
+ * the same size string as the source string. However, because we're
+ * translating that into UTF-8 the result may expand. Currently the
+ * Thai character set has single octet characters which expand to
+ * three octets in UTF-8. So it would be safe to use 3 * strlen(s)
+ * for the size of d. One can imagine a currently non-existent
+ * character set that expanded to 4 octets instead, so use 4 to be
+ * super safe.
+ *
+ * Returns: pointer to either the destination buffer containing the
+ * decoded text, or a pointer to the source buffer if there was
+ * no valid 'encoded-word' found during scanning.
+ */
+unsigned char *
+rfc1522_decode_to_utf8(unsigned char *d, size_t len, char *s)
+{
+ unsigned char *rv = NULL, *p;
+ char *start = s, *sw, *enc, *txt, *ew, **q, *lang;
+ char *cset;
+ unsigned long l;
+ int i;
+
+ *d = '\0'; /* init destination */
+
+ while(s && (sw = strstr(s, RFC1522_INIT))){
+ if(!rv) /* there's something to do, init it */
+ rv = d;
+ /* validate the rest of the encoded-word */
+ if(rfc1522_valid(sw, &cset, &enc, &txt, &ew)){
+ /*
+ * We may have been putting off copying the first part of the
+ * source while waiting to see if we have to copy at all.
+ */
+ if(rv == d && s != start){
+ rfc1522_copy_and_transliterate(rv, &d, len, (unsigned char *) start,
+ sw - start, NULL);
+ s = sw;
+ }
+
+ /* copy everything between s and sw to destination */
+ for(i = 0; &s[i] < sw; i++)
+ if(!isspace((unsigned char)s[i])){ /* if some non-whitespace */
+ while(s < sw && d-rv<len-1)
+ *d++ = (unsigned char) *s++;
+
+ break;
+ }
+
+ enc[-1] = txt[-1] = ew[0] = '\0'; /* tie off token strings */
+
+ if((lang = strchr(cset, '*')) != NULL)
+ *lang++ = '\0';
+
+ /* based on encoding, write the encoded text to output buffer */
+ switch(*enc){
+ case 'Q' : /* 'Q' encoding */
+ case 'q' :
+ /* special hocus-pocus to deal with '_' exception, too bad */
+ for(l = 0L, i = 0; txt[l]; l++)
+ if(txt[l] == '_')
+ i++;
+
+ if(i){
+ q = (char **) fs_get((i + 1) * sizeof(char *));
+ for(l = 0L, i = 0; txt[l]; l++)
+ if(txt[l] == '_'){
+ q[i++] = &txt[l];
+ txt[l] = SPACE;
+ }
+
+ q[i] = NULL;
+ }
+ else
+ q = NULL;
+
+ if((p = rfc822_qprint((unsigned char *)txt, strlen(txt), &l)) != NULL){
+ rfc1522_copy_and_transliterate(rv, &d, len, p, l, cset);
+ fs_give((void **)&p); /* free encoded buf */
+ }
+ else{
+ if(q)
+ fs_give((void **) &q);
+
+ goto bogus;
+ }
+
+ if(q){ /* restore underscores */
+ for(i = 0; q[i]; i++)
+ *(q[i]) = '_';
+
+ fs_give((void **)&q);
+ }
+
+ break;
+
+ case 'B' : /* 'B' encoding */
+ case 'b' :
+ if((p = rfc822_base64((unsigned char *) txt, strlen(txt), &l)) != NULL){
+ rfc1522_copy_and_transliterate(rv, &d, len, p, l, cset);
+ fs_give((void **)&p); /* free encoded buf */
+ }
+ else
+ goto bogus;
+
+ break;
+
+ default:
+ rfc1522_copy_and_transliterate(rv, &d, len, (unsigned char *) txt,
+ strlen(txt), NULL);
+ dprint((1, "RFC1522_decode: Unknown ENCODING: %s\n",
+ enc ? enc : "?"));
+ break;
+ }
+
+ /* restore trompled source string */
+ enc[-1] = txt[-1] = '?';
+ ew[0] = RFC1522_TERM[0];
+
+ /* advance s to start of text after encoded-word */
+ s = ew + RFC1522_TERM_L;
+
+ if(lang)
+ lang[-1] = '*';
+ }
+ else{
+ /*
+ * Found intro, but bogus data followed, treat it as normal text.
+ */
+ l = (sw - s) + RFC1522_INIT_L;
+ rfc1522_copy_and_transliterate(rv, &d, len, (unsigned char *) s, l, NULL);
+ for(; isspace((unsigned char) *(s+l)) && d-rv<len-1;l++)
+ *d++ = *(s+l); /* copy any trailing space */
+ rv[len-1] = '\0';
+ *d = '\0';
+ s += l;
+ }
+ }
+
+ if(rv){
+ if(s && *s){ /* copy remaining text */
+ rfc1522_copy_and_transliterate(rv, &d, len, (unsigned char *) s, strlen(s), NULL);
+ rv[len-1] = '\0';
+ }
+ }
+ else if(s){
+ rv = d;
+ rfc1522_copy_and_transliterate(rv, &d, len, (unsigned char *) s, strlen(s), NULL);
+ rv[len-1] = '\0';
+ }
+
+ return(rv ? rv : (unsigned char *) start);
+
+ bogus:
+ dprint((1, "RFC1522_decode: BOGUS INPUT: -->%s<--\n",
+ start ? start : "?"));
+ return((unsigned char *) start);
+}
+
+
+/*
+ * rfc1522_token - scan the given source line up to the end_str making
+ * sure all subsequent chars are "valid" leaving endp
+ * a the start of the end_str.
+ * Returns: TRUE if we got a valid token, FALSE otherwise
+ */
+int
+rfc1522_token(char *s, int (*valid) (int), char *end_str, char **endp)
+{
+ while(*s){
+ if((char) *s == *end_str /* test for matching end_str */
+ && ((end_str[1])
+ ? !strncmp((char *)s + 1, end_str + 1, strlen(end_str + 1))
+ : 1)){
+ *endp = s;
+ return(TRUE);
+ }
+
+ if(!(*valid)(*s++)) /* test for valid char */
+ break;
+ }
+
+ return(FALSE);
+}
+
+
+/*
+ * rfc1522_valtok - test for valid character in the RFC 1522 encoded
+ * word's charset and encoding fields.
+ */
+int
+rfc1522_valtok(int c)
+{
+ return(!(c == SPACE || iscntrl(c & 0x7f) || strindex(ESPECIALS, c)));
+}
+
+
+/*
+ * rfc1522_valenc - test for valid character in the RFC 1522 encoded
+ * word's encoded-text field.
+ */
+int
+rfc1522_valenc(int c)
+{
+ return(!(c == '?' || c == SPACE) && isprint((unsigned char)c));
+}
+
+
+/*
+ * rfc1522_valid - validate the given string as to it's rfc1522-ness
+ */
+int
+rfc1522_valid(char *s, char **charset, char **enc, char **txt, char **endp)
+{
+ char *c, *e, *t, *p;
+ int rv;
+
+ rv = rfc1522_token(c = s+RFC1522_INIT_L, rfc1522_valtok, RFC1522_DLIM, &e)
+ && rfc1522_token(++e, rfc1522_valtok, RFC1522_DLIM, &t)
+ && rfc1522_token(++t, rfc1522_valenc, RFC1522_TERM, &p)
+ && p - s <= RFC1522_MAXW;
+
+ if(charset)
+ *charset = c;
+
+ if(enc)
+ *enc = e;
+
+ if(txt)
+ *txt = t;
+
+ if(endp)
+ *endp = p;
+
+ return(rv);
+}
+
+
+/*
+ * rfc1522_copy_and_transliterate - copy given buf to destination buffer
+ * as UTF-8 characters
+ */
+void
+rfc1522_copy_and_transliterate(unsigned char *rv,
+ unsigned char **d,
+ size_t len,
+ unsigned char *s,
+ unsigned long l,
+ char *cset)
+{
+ unsigned long i;
+ SIZEDTEXT src, xsrc;
+
+ src.data = s;
+ src.size = l;
+ memset(&xsrc, 0, sizeof(SIZEDTEXT));
+
+ /* transliterate decoded segment to utf-8 */
+ if(cset){
+ if(strucmp((char *) cset, "us-ascii")
+ && strucmp((char *) cset, "utf-8")){
+ if(utf8_charset(cset)){
+ if(!utf8_text(&src, cset, &xsrc, 0L)){
+ /* should not happen */
+ panic("c-client failed to transliterate recognized characterset");
+ }
+ }
+ else{
+ /* non-xlatable charset */
+ for(i = 0; i < l; i++)
+ if(src.data[i] & 0x80){
+ xsrc.data = (unsigned char *) fs_get((l+1) * sizeof(unsigned char));
+ xsrc.size = l;
+ for(i = 0; i < l; i++)
+ xsrc.data[i] = (src.data[i] & 0x80) ? '?' : src.data[i];
+
+ break;
+ }
+ }
+ }
+ }
+ else{
+ const CHARSET *cs;
+
+ src.data = s;
+ src.size = strlen((char *) s);
+
+ if((cs = utf8_infercharset(&src))){
+ if(!(cs->type == CT_ASCII || cs->type == CT_UTF8)){
+ if(!utf8_text_cs(&src, cs, &xsrc, 0L, 0L)){
+ /* should not happen */
+ panic("c-client failed to transliterate recognized characterset");
+ }
+ }
+ }
+ else if((cset=ps_global->VAR_UNK_CHAR_SET)
+ && strucmp((char *) cset, "us-ascii")
+ && strucmp((char *) cset, "utf-8")
+ && utf8_charset(cset)){
+ if(!utf8_text(&src, cset, &xsrc, 0L)){
+ /* should not happen */
+ panic("c-client failed to transliterate recognized character set");
+ }
+ }
+ else{
+ /* unknown bytes - mask off high bit chars */
+ for(i = 0; i < l; i++)
+ if(src.data[i] & 0x80){
+ xsrc.data = (unsigned char *) fs_get((l+1) * sizeof(unsigned char));
+ xsrc.size = l;
+ for(i = 0; i < l; i++)
+ xsrc.data[i] = (src.data[i] & 0x80) ? '?' : src.data[i];
+
+ break;
+ }
+ }
+ }
+
+ if(xsrc.data){
+ s = xsrc.data;
+ l = xsrc.size;
+ }
+
+ i = MIN(l,len-1-((*d)-rv));
+ strncpy((char *) (*d), (char *) s, i);
+ (*d)[i] = '\0';
+ *d += l; /* advance dest ptr to EOL */
+ if((*d)-rv > len-1)
+ *d = rv+len-1;
+
+ if(xsrc.data && src.data != xsrc.data)
+ fs_give((void **) &xsrc.data);
+}
+
+
+
+/*
+ * rfc1522_encode - encode the given source string ala RFC 1522,
+ * IF NECESSARY, into the given destination buffer.
+ * Don't bother copying if it turns out encoding
+ * isn't necessary.
+ *
+ * Returns: pointer to either the destination buffer containing the
+ * encoded text, or a pointer to the source buffer if we didn't
+ * have to encode anything.
+ */
+char *
+rfc1522_encode(char *d, size_t dlen, unsigned char *s, char *charset)
+{
+ unsigned char *p, *q;
+ int n;
+
+ if(!s)
+ return((char *) s);
+
+ if(!charset)
+ charset = UNKNOWN_CHARSET;
+
+ /* look for a reason to encode */
+ for(p = s, n = 0; *p; p++)
+ if((*p) & 0x80){
+ n++;
+ }
+ else if(*p == RFC1522_INIT[0]
+ && !strncmp((char *) p, RFC1522_INIT, RFC1522_INIT_L)){
+ if(rfc1522_valid((char *) p, NULL, NULL, NULL, (char **) &q))
+ p = q + RFC1522_TERM_L - 1; /* advance past encoded gunk */
+ }
+ else if(*p == ESCAPE && match_escapes((char *)(p+1))){
+ n++;
+ }
+
+ if(n){ /* found, encoding to do */
+ char *rv = d, *t,
+ enc = (n > (2 * (p - s)) / 3) ? 'B' : 'Q';
+
+ while(*s){
+ if(d-rv < dlen-1-(RFC1522_INIT_L+2*RFC1522_DLIM_L+1)){
+ sstrncpy(&d, RFC1522_INIT, dlen-(d-rv)); /* insert intro header, */
+ sstrncpy(&d, charset, dlen-(d-rv)); /* character set tag, */
+ sstrncpy(&d, RFC1522_DLIM, dlen-(d-rv)); /* and encoding flavor */
+ if(dlen-(d-rv) > 0)
+ *d++ = enc;
+
+ sstrncpy(&d, RFC1522_DLIM, dlen-(d-rv));
+ }
+
+ /*
+ * feed lines to encoder such that they're guaranteed
+ * less than RFC1522_MAXW.
+ */
+ p = rfc1522_encoded_word(s, enc, charset);
+ if(enc == 'B') /* insert encoded data */
+ sstrncpy(&d, t = rfc1522_binary(s, p - s), dlen-1-(d-rv));
+ else /* 'Q' encoding */
+ sstrncpy(&d, t = rfc1522_8bit(s, p - s), dlen-1-(d-rv));
+
+ sstrncpy(&d, RFC1522_TERM, dlen-1-(d-rv)); /* insert terminator */
+ fs_give((void **) &t);
+ if(*p) /* more src string follows */
+ sstrncpy(&d, "\015\012 ", dlen-1-(d-rv)); /* insert cont. line */
+
+ s = p; /* advance s */
+ }
+
+ rv[dlen-1] = '\0';
+ return(rv);
+ }
+ else
+ return((char *) s); /* no work for us here */
+}
+
+
+
+/*
+ * rfc1522_encoded_word -- cut given string into max length encoded word
+ *
+ * Return: pointer into 's' such that the encoded 's' is no greater
+ * than RFC1522_MAXW
+ *
+ * NOTE: this line break code is NOT cognizant of any SI/SO
+ * charset requirements nor similar strategies using escape
+ * codes. Hopefully this will matter little and such
+ * representation strategies don't also include 8bit chars.
+ */
+unsigned char *
+rfc1522_encoded_word(unsigned char *s, int enc, char *charset)
+{
+ int goal = RFC1522_MAXW - RFC1522_OVERHEAD(charset);
+
+ if(enc == 'B') /* base64 encode */
+ for(goal = ((goal / 4) * 3) - 2; goal && *s; goal--, s++)
+ ;
+ else /* special 'Q' encoding */
+ for(; goal && *s; s++)
+ if((goal -= RFC1522_ENC_CHAR(*s) ? 3 : 1) < 0)
+ break;
+
+ return(s);
+}
+
+
+
+/*
+ * rfc1522_8bit -- apply RFC 1522 'Q' encoding to the given 8bit buffer
+ *
+ * Return: alloc'd buffer containing encoded string
+ */
+char *
+rfc1522_8bit(void *src, int slen)
+{
+ char *ret = (char *) fs_get ((size_t) (3*slen + 2));
+ char *d = ret;
+ unsigned char c;
+ unsigned char *s = (unsigned char *) src;
+
+ while (slen--) { /* for each character */
+ if (((c = *s++) == '\015') && (*s == '\012') && slen) {
+ *d++ = '\015'; /* true line break */
+ *d++ = *s++;
+ slen--;
+ }
+ else if(c == SPACE){ /* special encoding case */
+ *d++ = '_';
+ }
+ else if(RFC1522_ENC_CHAR(c)){
+ *d++ = '='; /* quote character */
+ C2XPAIR(c, d);
+ }
+ else
+ *d++ = (char) c; /* ordinary character */
+ }
+
+ *d = '\0'; /* tie off destination */
+ return(ret);
+}
+
+
+/*
+ * rfc1522_binary -- apply RFC 1522 'B' encoding to the given 8bit buffer
+ *
+ * Return: alloc'd buffer containing encoded string
+ */
+char *
+rfc1522_binary (void *src, int srcl)
+{
+ static char *v =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ unsigned char *s = (unsigned char *) src;
+ char *ret, *d;
+
+ d = ret = (char *) fs_get ((size_t) ((((srcl + 2) / 3) * 4) + 1));
+ for (; srcl; s += 3) { /* process tuplets */
+ /* byte 1: high 6 bits (1) */
+ *d++ = v[s[0] >> 2];
+ /* byte 2: low 2 bits (1), high 4 bits (2) */
+ *d++ = v[((s[0] << 4) + (--srcl ? (s[1] >> 4) : 0)) & 0x3f];
+ /* byte 3: low 4 bits (2), high 2 bits (3) */
+ *d++ = srcl ? v[((s[1] << 2) + (--srcl ? (s[2] >> 6) :0)) & 0x3f] :'=';
+ /* byte 4: low 6 bits (3) */
+ *d++ = srcl ? v[s[2] & 0x3f] : '=';
+ if(srcl)
+ srcl--; /* count third character if processed */
+ }
+
+ *d = '\0'; /* tie off string */
+ return(ret); /* return the resulting string */
+}
+
+
+/*
+ * Checks if charset conversion is possible and which quality could be achieved
+ *
+ * args: from_cs -- charset to convert from
+ * to_cs -- charset to convert to
+ *
+ * Results:
+ * CONV_TABLE->table -- conversion table, NULL if conversion not needed
+ * or not supported
+ * CONV_TABLE->quality -- conversion quality (conversion not supported, not
+ * needed, loses special chars, or loses letters
+ *
+ * The other entries of CONV_TABLE are used inside this function only
+ * and may not be used outside unless this documentation is updated.
+ */
+CONV_TABLE *
+conversion_table(char *from_cs, char *to_cs)
+{
+ int i, j;
+ unsigned char *p = NULL;
+ unsigned short *fromtab, *totab;
+ CONV_TABLE *ct = NULL;
+ const CHARSET *from, *to;
+ static CONV_TABLE null_tab;
+
+ if(!(from_cs && *from_cs && to_cs && *to_cs) || !strucmp(from_cs, to_cs)){
+ memset(&null_tab, 0, sizeof(null_tab));
+ null_tab.quality = CV_NO_TRANSLATE_NEEDED;
+ return(&null_tab);
+ }
+
+ /*
+ * First check to see if we are already set up for this pair of charsets.
+ */
+ if((ct = ps_global->conv_table) != NULL
+ && ct->from_charset && ct->to_charset
+ && !strucmp(ct->from_charset, from_cs)
+ && !strucmp(ct->to_charset, to_cs))
+ return(ct);
+
+ /*
+ * No such luck. Get rid of the cache of the previous translation table
+ * and build a new one.
+ */
+ if(ct){
+ if(ct->table && (ct->convert != gf_convert_utf8_charset))
+ fs_give((void **) &ct->table);
+
+ if(ct->from_charset)
+ fs_give((void **) &ct->from_charset);
+
+ if(ct->to_charset)
+ fs_give((void **) &ct->to_charset);
+ }
+ else
+ ct = ps_global->conv_table = (CONV_TABLE *) fs_get(sizeof(*ct));
+
+ memset(ct, 0, sizeof(*ct));
+
+ ct->from_charset = cpystr(from_cs);
+ ct->to_charset = cpystr(to_cs);
+ ct->quality = CV_NO_TRANSLATE_POSSIBLE;
+
+ /*
+ * Check to see if a translation is feasible.
+ */
+ from = utf8_charset(from_cs);
+ to = utf8_charset(to_cs);
+
+ if(from && to){ /* if both charsets found */
+ /* no mapping if same or from is ASCII */
+ if((from->type == to->type && from->tab == to->tab)
+ || (from->type == CT_ASCII))
+ ct->quality = CV_NO_TRANSLATE_NEEDED;
+ else switch(from->type){
+ case CT_1BYTE0: /* 1 byte no table */
+ case CT_1BYTE: /* 1 byte ASCII + table 0x80-0xff */
+ case CT_1BYTE8: /* 1 byte table 0x00 - 0xff */
+ switch(to->type){
+ case CT_1BYTE0: /* 1 byte no table */
+ case CT_1BYTE: /* 1 byte ASCII + table 0x80-0xff */
+ case CT_1BYTE8: /* 1 byte table 0x00 - 0xff */
+ ct->quality = (from->script & to->script) ?
+ CV_LOSES_SOME_LETTERS : CV_LOSES_SPECIAL_CHARS;
+ break;
+ }
+ break;
+ case CT_UTF8: /* variable UTF-8 encoded Unicode no table */
+ /* If source is UTF-8, see if destination charset has an 8 or 16 bit
+ * coded character set that we can translate to. By special
+ * dispensation, kludge ISO-2022-JP to EUC or Shift-JIS, but don't
+ * try to do any other ISO 2022 charsets or UTF-7.
+ */
+ switch (to->type){
+ case CT_SJIS: /* 2 byte Shift-JIS */
+ /* only win if can get EUC-JP chartab */
+ if(utf8_charset("EUC-JP"))
+ ct->quality = CV_LOSES_SOME_LETTERS;
+ break;
+ case CT_ASCII: /* 7-bit ASCII no table */
+ case CT_1BYTE0: /* 1 byte no table */
+ case CT_1BYTE: /* 1 byte ASCII + table 0x80-0xff */
+ case CT_1BYTE8: /* 1 byte table 0x00 - 0xff */
+ case CT_EUC: /* 2 byte ASCII + utf8_eucparam base/CS2/CS3 */
+ case CT_DBYTE: /* 2 byte ASCII + utf8_eucparam */
+ case CT_DBYTE2: /* 2 byte ASCII + utf8_eucparam plane1/2 */
+ ct->quality = CV_LOSES_SOME_LETTERS;
+ break;
+ }
+ break;
+ }
+
+ switch (ct->quality) { /* need to map? */
+ case CV_NO_TRANSLATE_POSSIBLE:
+ case CV_NO_TRANSLATE_NEEDED:
+ break; /* no mapping needed */
+ default: /* do mapping */
+ switch (from->type) {
+ case CT_UTF8: /* UTF-8 to legacy character set */
+ if((ct->table = utf8_rmap (to_cs)) != NULL)
+ ct->convert = gf_convert_utf8_charset;
+ break;
+
+ case CT_1BYTE0: /* ISO 8859-1 */
+ case CT_1BYTE: /* low part ASCII, high part other */
+ case CT_1BYTE8: /* low part has some non-ASCII */
+ /*
+ * The fromtab and totab tables are mappings from the 128 character
+ * positions 128-255 to their Unicode values (so unsigned shorts).
+ * The table we are creating is such that if
+ *
+ * from_char_value -> unicode_value
+ * to_char_value -> same_unicode_value
+ *
+ * then we want to map from_char_value -> to_char_value
+ *
+ * To simplify conversions we create the whole 256 element array,
+ * with the first 128 positions just the identity. If there is no
+ * conversion for a particular from_char_value (that is, no
+ * to_char_value maps to the same unicode character) then we put
+ * '?' in that character. We may want to output blob on the PC,
+ * but don't so far.
+ *
+ * If fromtab or totab are NULL, that means the mapping is simply
+ * the identity mapping. Since that is still useful to us, we
+ * create it on the fly.
+ */
+ fromtab = (unsigned short *) from->tab;
+ totab = (unsigned short *) to->tab;
+
+ ct->convert = gf_convert_8bit_charset;
+ p = ct->table = (unsigned char *)
+ fs_get(256 * sizeof(unsigned char));
+ for(i = 0; i < 256; i++){
+ unsigned int fc;
+ p[i] = '?';
+ switch(from->type){ /* get "from" UCS-2 codepoint */
+ case CT_1BYTE0: /* ISO 8859-1 */
+ fc = i;
+ break;
+ case CT_1BYTE: /* low part ASCII, high part other */
+ fc = (i < 128) ? i : fromtab[i-128];
+ break;
+ case CT_1BYTE8: /* low part has some non-ASCII */
+ fc = fromtab[i];
+ break;
+ }
+ switch(to->type){ /* match against "to" UCS-2 codepoint */
+ case CT_1BYTE0: /* identity match for ISO 8859-1*/
+ if(fc < 256)
+ p[i] = fc;
+ break;
+ case CT_1BYTE: /* ASCII is identity, search high part */
+ if(fc < 128) p[i] = fc;
+ else for(j = 0; j < 128; j++){
+ if(fc == totab[j]){
+ p[i] = 128 + j;
+ break;
+ }
+ }
+ break;
+ case CT_1BYTE8: /* search all codepoints */
+ for(j = 0; j < 256; j++){
+ if(fc == totab[j]){
+ p[i] = j;
+ break;
+ }
+ }
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ return(ct);
+}
+
+
+/*
+ * Replace personal names in list of addresses with
+ * decoded personal names in UTF-8.
+ * Assumes we can free and reallocate the name.
+ */
+void
+decode_addr_names_to_utf8(struct mail_address *a)
+{
+ for(; a; a = a->next)
+ if(a->personal)
+ convert_possibly_encoded_str_to_utf8(&a->personal);
+}
+
+
+/*
+ * Strp is a pointer to an allocated string.
+ * This routine will convert the string to UTF-8, possibly
+ * freeing and re-allocating it.
+ * The source string may or may not have RFC1522 encoding
+ * which will be undone using rfc1522_decode.
+ * The string will have been converted on return.
+ */
+void
+convert_possibly_encoded_str_to_utf8(char **strp)
+{
+ size_t len, lensrc, lenresult;
+ char *bufp, *decoded;
+
+ if(!strp || !*strp || **strp == '\0')
+ return;
+
+ len = 4 * strlen(*strp) + 1;
+ bufp = (char *) fs_get(len);
+
+ decoded = (char *) rfc1522_decode_to_utf8((unsigned char *) bufp, len, *strp);
+ if(decoded != (*strp)){ /* unchanged */
+ if((lensrc=strlen(*strp)) >= (lenresult=strlen(decoded))){
+ strncpy(*strp, decoded, lensrc);
+ (*strp)[lensrc] = '\0';
+ }
+ else{
+ fs_give((void **) strp);
+ if(decoded == bufp){ /* this will be true */
+ fs_resize((void **) &bufp, lenresult+1);
+ *strp = bufp;
+ bufp = NULL;
+ }
+ else{ /* this is unreachable */
+ *strp = cpystr(decoded);
+ }
+ }
+ }
+ /* else, already UTF-8 */
+
+ if(bufp)
+ fs_give((void **) &bufp);
+}
diff --git a/pith/charset.h b/pith/charset.h
new file mode 100644
index 00000000..f89c6e4c
--- /dev/null
+++ b/pith/charset.h
@@ -0,0 +1,56 @@
+/*
+ * $Id: charset.h 765 2007-10-23 23:51:37Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_CHARSET_INCLUDED
+#define PITH_CHARSET_INCLUDED
+
+
+#include "../pith/filttype.h"
+
+
+typedef struct conversion_table {
+ char *from_charset;
+ char *to_charset;
+ int quality;
+ void *table;
+ filter_t convert;
+} CONV_TABLE;
+
+
+/* Conversion table quality of tranlation */
+#define CV_NO_TRANSLATE_POSSIBLE 1 /* We don't know how to */
+ /* translate this pair */
+#define CV_NO_TRANSLATE_NEEDED 2 /* Not necessary, no-op */
+#define CV_LOSES_SPECIAL_CHARS 3 /* Letters will translate */
+ /* ok but some special chars */
+ /* may be lost */
+#define CV_LOSES_SOME_LETTERS 4 /* Some special chars and */
+ /* some letters may be lost */
+
+
+#define CSET_MAX 64
+
+
+/* exported protoypes */
+char *body_charset(MAILSTREAM *, long, unsigned char *);
+unsigned char *trans_euc_to_2022_jp(unsigned char *);
+unsigned char *rfc1522_decode_to_utf8(unsigned char *, size_t, char *);
+char *rfc1522_encode(char *, size_t, unsigned char *, char *);
+CONV_TABLE *conversion_table(char *, char *);
+void decode_addr_names_to_utf8(ADDRESS *);
+void convert_possibly_encoded_str_to_utf8(char **);
+
+
+#endif /* PITH_CHARSET_INCLUDED */
diff --git a/pith/color.c b/pith/color.c
new file mode 100644
index 00000000..9794294b
--- /dev/null
+++ b/pith/color.c
@@ -0,0 +1,234 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: color.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/color.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/filter.h"
+
+
+char *
+color_embed(char *fg, char *bg)
+{
+ static char buf[(2 * RGBLEN) + 5], *p;
+
+ p = buf;
+ if(fg){
+ if(sizeof(buf)-(p-buf) > 1){
+ *p++ = TAG_EMBED;
+ *p++ = TAG_FGCOLOR;
+ }
+
+ sstrncpy(&p, color_to_asciirgb(fg), sizeof(buf)-(p-buf));
+ }
+
+ if(bg){
+ if(sizeof(buf)-(p-buf) > 1){
+ *p++ = TAG_EMBED;
+ *p++ = TAG_BGCOLOR;
+ }
+
+ sstrncpy(&p, color_to_asciirgb(bg), sizeof(buf)-(p-buf));
+ }
+
+ buf[sizeof(buf)-1] = '\0';
+
+ return(buf);
+}
+
+
+int
+colorcmp(char *color1, char *color2)
+{
+ if(color1 && color2)
+ return(strcmp(color_to_asciirgb(color1), color_to_asciirgb(color2)));
+
+ /* if both NULL they're the same? */
+ return(!(color1 || color2));
+}
+
+
+
+struct quote_colors {
+ COLOR_PAIR *color;
+ struct quote_colors *next;
+};
+
+
+int
+color_a_quote(long int linenum, char *line, LT_INS_S **ins, void *is_flowed_msg)
+{
+ int countem = 0;
+ struct variable *vars = ps_global->vars;
+ char *p;
+ struct quote_colors *colors = NULL, *cp, *next;
+ COLOR_PAIR *col = NULL;
+ int is_flowed = is_flowed_msg ? *((int *)is_flowed_msg) : 0;
+
+ p = line;
+ if(!is_flowed)
+ while(isspace((unsigned char)*p))
+ p++;
+
+ if(p[0] == '>'){
+ struct quote_colors *c;
+
+ /*
+ * We have a fixed number of quote level colors (3). If there are
+ * more levels of quoting than are defined, they repeat.
+ */
+ if(VAR_QUOTE1_FORE_COLOR && VAR_QUOTE1_BACK_COLOR &&
+ (col = new_color_pair(VAR_QUOTE1_FORE_COLOR,
+ VAR_QUOTE1_BACK_COLOR)) &&
+ pico_is_good_colorpair(col)){
+ c = (struct quote_colors *)fs_get(sizeof(*c));
+ memset(c, 0, sizeof(*c));
+ c->color = col;
+ col = NULL;
+ colors = c;
+ cp = c;
+ countem++;
+ if(VAR_QUOTE2_FORE_COLOR && VAR_QUOTE2_BACK_COLOR &&
+ (col = new_color_pair(VAR_QUOTE2_FORE_COLOR,
+ VAR_QUOTE2_BACK_COLOR)) &&
+ pico_is_good_colorpair(col)){
+ c = (struct quote_colors *)fs_get(sizeof(*c));
+ memset(c, 0, sizeof(*c));
+ c->color = col;
+ col = NULL;
+ cp->next = c;
+ cp = c;
+ countem++;
+ if(VAR_QUOTE3_FORE_COLOR && VAR_QUOTE3_BACK_COLOR &&
+ (col = new_color_pair(VAR_QUOTE3_FORE_COLOR,
+ VAR_QUOTE3_BACK_COLOR)) &&
+ pico_is_good_colorpair(col)){
+ c = (struct quote_colors *)fs_get(sizeof(*cp));
+ memset(c, 0, sizeof(*c));
+ c->color = col;
+ col = NULL;
+ cp->next = c;
+ cp = c;
+ countem++;
+ }
+ }
+ }
+ }
+
+ if(col)
+ free_color_pair(&col);
+
+ cp = NULL;
+ while(*p == '>'){
+ cp = (cp && cp->next) ? cp->next : colors;
+
+ if(countem > 0)
+ ins = gf_line_test_new_ins(ins, p,
+ color_embed(cp->color->fg, cp->color->bg),
+ (2 * RGBLEN) + 4);
+
+ countem = (countem == 1) ? 0 : countem;
+
+ p++;
+ if(!is_flowed)
+ for(; isspace((unsigned char)*p); p++)
+ ;
+ }
+
+ if(colors){
+ char fg[RGBLEN + 1], bg[RGBLEN + 1], rgbbuf[RGBLEN + 1];
+
+ strncpy(fg, color_to_asciirgb(VAR_NORM_FORE_COLOR), sizeof(fg));
+ strncpy(bg, color_to_asciirgb(VAR_NORM_BACK_COLOR), sizeof(bg));
+ fg[sizeof(fg)-1] = '\0';
+ bg[sizeof(bg)-1] = '\0';
+
+ /*
+ * Loop watching colors, and override with most recent
+ * quote color whenever the normal foreground and background
+ * colors are in force.
+ */
+ while(*p)
+ if(*p++ == TAG_EMBED){
+
+ switch(*p++){
+ case TAG_HANDLE :
+ p += *p + 1; /* skip handle key */
+ break;
+
+ case TAG_FGCOLOR :
+ snprintf(rgbbuf, sizeof(rgbbuf), "%s", p);
+ p += RGBLEN; /* advance past color value */
+
+ if(!colorcmp(rgbbuf, VAR_NORM_FORE_COLOR)
+ && !colorcmp(bg, VAR_NORM_BACK_COLOR))
+ ins = gf_line_test_new_ins(ins, p,
+ color_embed(cp->color->fg,NULL),
+ RGBLEN + 2);
+ break;
+
+ case TAG_BGCOLOR :
+ snprintf(rgbbuf, sizeof(rgbbuf), "%s", p);
+ p += RGBLEN; /* advance past color value */
+
+ if(!colorcmp(rgbbuf, VAR_NORM_BACK_COLOR)
+ && !colorcmp(fg, VAR_NORM_FORE_COLOR))
+ ins = gf_line_test_new_ins(ins, p,
+ color_embed(NULL,cp->color->bg),
+ RGBLEN + 2);
+
+ break;
+
+ default :
+ break;
+ }
+ }
+
+ ins = gf_line_test_new_ins(ins, line + strlen(line),
+ color_embed(VAR_NORM_FORE_COLOR,
+ VAR_NORM_BACK_COLOR),
+ (2 * RGBLEN) + 4);
+ for(cp = colors; cp && cp->color; cp = next){
+ free_color_pair(&cp->color);
+ next = cp->next;
+ fs_give((void **)&cp);
+ }
+ }
+
+ return(0);
+}
+
+
+void
+free_spec_colors(SPEC_COLOR_S **colors)
+{
+ if(colors && *colors){
+ free_spec_colors(&(*colors)->next);
+ if((*colors)->spec)
+ fs_give((void **)&(*colors)->spec);
+ if((*colors)->fg)
+ fs_give((void **)&(*colors)->fg);
+ if((*colors)->bg)
+ fs_give((void **)&(*colors)->bg);
+ if((*colors)->val)
+ free_pattern(&(*colors)->val);
+
+ fs_give((void **)colors);
+ }
+}
diff --git a/pith/color.h b/pith/color.h
new file mode 100644
index 00000000..b90d82cf
--- /dev/null
+++ b/pith/color.h
@@ -0,0 +1,87 @@
+/*
+ * $Id: color.h 768 2007-10-24 00:10:03Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_COLOR_INCLUDED
+#define PITH_COLOR_INCLUDED
+
+
+#include "../pith/filttype.h"
+#include "../pith/pattern.h"
+#include "../pith/osdep/color.h"
+
+
+typedef struct spec_color_s {
+ int inherit; /* this isn't a color, it is INHERIT */
+ char *spec;
+ char *fg;
+ char *bg;
+ PATTERN_S *val;
+ struct spec_color_s *next;
+} SPEC_COLOR_S;
+
+
+/*
+ * These are default colors that you'll get when you turn
+ * color on. The way color works is that the closest possible
+ * RGB value is chosen so these colors will produce different
+ * results in the different color models (the 8-color, 16-color,
+ * xterm 256-color, and PC-Alpine color).
+ * See init_color_table() for the RGB values we use.
+ * The 8-color model uses the 8 0 or 255 possibilities. So,
+ * for example, if the default color is "000,217,217" the
+ * closes 8-color version of that is going to be "000,255,255".
+ * In the 16-color terminal we use 000, 174, and 255 as the
+ * possible values. That means that a default value
+ * of "000,214,000" maps to "000,174,000" (a dull green)
+ * but "000,215,000" maps to "000,255,000" (a bright green).
+ *
+ * The colors which don't have defaults map to either the current
+ * setting of the Normal color or the current setting of the
+ * Reverse color, depending on what we thought was right long ago.
+ */
+#define DEFAULT_TITLE_FORE_RGB "000,000,000"
+#define DEFAULT_TITLE_BACK_RGB "255,255,000"
+#define DEFAULT_TITLECLOSED_FORE_RGB "255,255,255"
+#define DEFAULT_TITLECLOSED_BACK_RGB "255,000,000"
+#define DEFAULT_METAMSG_FORE_RGB "000,000,000"
+#define DEFAULT_METAMSG_BACK_RGB "255,255,000"
+#define DEFAULT_QUOTE1_FORE_RGB "000,000,000"
+#define DEFAULT_QUOTE1_BACK_RGB "000,217,217"
+#define DEFAULT_QUOTE2_FORE_RGB "000,000,000"
+#define DEFAULT_QUOTE2_BACK_RGB "204,214,000"
+#define DEFAULT_QUOTE3_FORE_RGB "000,000,000"
+#define DEFAULT_QUOTE3_BACK_RGB "000,214,000"
+#define DEFAULT_SIGNATURE_FORE_RGB "000,000,255"
+#define DEFAULT_SIGNATURE_BACK_RGB "255,255,255"
+#define DEFAULT_IND_PLUS_FORE_RGB "000,000,000"
+#define DEFAULT_IND_PLUS_BACK_RGB "000,174,174"
+#define DEFAULT_IND_IMP_FORE_RGB "240,240,240"
+#define DEFAULT_IND_IMP_BACK_RGB "174,000,000"
+#define DEFAULT_IND_ANS_FORE_RGB "255,000,000"
+#define DEFAULT_IND_ANS_BACK_RGB "174,174,000"
+#define DEFAULT_IND_NEW_FORE_RGB "240,240,240"
+#define DEFAULT_IND_NEW_BACK_RGB "174,000,174"
+#define DEFAULT_IND_OP_FORE_RGB "192,192,192"
+#define DEFAULT_IND_OP_BACK_RGB "255,255,255"
+
+
+/* exported protoypes */
+char *color_embed(char *, char *);
+int colorcmp(char *, char *);
+int color_a_quote(long, char *, LT_INS_S **, void *);
+void free_spec_colors(SPEC_COLOR_S **);
+
+
+#endif /* PITH_COLOR_INCLUDED */
diff --git a/pith/conf.c b/pith/conf.c
new file mode 100644
index 00000000..1375b8dc
--- /dev/null
+++ b/pith/conf.c
@@ -0,0 +1,8241 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: conf.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2009 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ conf.c
+ Implements the Pine configuration management routines
+ ====*/
+
+
+#include "../pith/headers.h"
+#include "../pith/init.h"
+#include "../pith/conf.h"
+#include "../pith/state.h"
+#include "../pith/remote.h"
+#include "../pith/keyword.h"
+#include "../pith/mailview.h"
+#include "../pith/list.h"
+#include "../pith/status.h"
+#include "../pith/ldap.h"
+#include "../pith/folder.h"
+#include "../pith/thread.h"
+#include "../pith/news.h"
+#include "../pith/util.h"
+#include "../pith/pattern.h"
+#include "../pith/color.h"
+#include "../pith/options.h"
+#include "../pith/busy.h"
+#include "../pith/readfile.h"
+#include "../pith/hist.h"
+#include "../pith/mailindx.h"
+#include "../pith/tempfile.h"
+#include "../pith/icache.h"
+#include "../pith/sort.h"
+#include "../pith/smime.h"
+#include "../pith/charconv/utf8.h"
+#ifdef _WINDOWS
+#include "../pico/osdep/mswin.h"
+#endif
+
+
+#define TO_BAIL_THRESHOLD 60
+
+
+/*
+ * Internal prototypes
+ */
+void convert_configvars_to_utf8(struct variable *, char *);
+void convert_configvar_to_utf8(struct variable *, char *);
+void set_current_pattern_vals(struct pine *);
+void convert_pattern_data(void);
+void convert_filts_pattern_data(void);
+void convert_scores_pattern_data(void);
+void convert_pinerc_patterns(long);
+void convert_pinerc_filts_patterns(long);
+void convert_pinerc_scores_patterns(long);
+void set_old_growth_bits(struct pine *, int);
+int var_is_in_rest_of_file(char *, char *);
+char *skip_over_this_var(char *, char *);
+char *native_nl(char *);
+void set_color_val(struct variable *, int);
+int copy_localfile_to_remotefldr(RemType, char *, char *, char *, char **);
+char *backcompat_convert_from_utf8(char *, size_t, char *);
+#ifdef _WINDOWS
+char *transformed_color(char *);
+int convert_pc_gray_names(struct pine *, PINERC_S *, EditWhich);
+int unix_color_style_in_pinerc(PINERC_S *);
+char *pcpine_general_help(char *);
+char *pcpine_help(HelpType); /* defined in alpine/help */
+#endif /* _WINDOWS */
+
+
+/* hook too allow caller to decide what to do about failure */
+int (*pith_opt_remote_pinerc_failure)(void);
+
+
+/*------------------------------------
+Some definitions to keep the static "variable" array below
+a bit more readable...
+ ----*/
+CONF_TXT_T cf_text_comment[] = "#\n# Alpine configuration file\n#\n# This file sets the configuration options used by Alpine and PC-Alpine. These\n# options are usually set from within Alpine or PC-Alpine. There may be a\n# system-wide configuration file which sets the defaults for some of the\n# variables. On Unix, run alpine -conf to see how system defaults have been set.\n# For variables that accept multiple values, list elements are\
+ separated by\n# commas. A line beginning with a space or tab is considered to be a\n# continuation of the previous line. For a variable to be unset its value must\n# be blank. To set a variable to the empty string its value should be \"\".\n# You can override system defaults by setting a variable to the empty string.\n# Lines beginning with \"#\" are comments, and ignored by Alpine.\n";
+
+
+CONF_TXT_T cf_text_personal_name[] = "Over-rides your full name from Unix password file. Required for PC-Alpine.";
+
+CONF_TXT_T cf_text_user_id[] = "Your login/e-mail user name";
+
+CONF_TXT_T cf_text_user_domain[] = "Sets domain part of From: and local addresses in outgoing mail.";
+
+CONF_TXT_T cf_text_smtp_server[] = "List of SMTP servers for sending mail. If blank: Unix Alpine uses sendmail.";
+
+CONF_TXT_T cf_text_nntp_server[] = "NNTP server for posting news. Also sets news-collections for news reading.";
+
+#ifdef SMIME
+
+CONF_TXT_T cf_text_publiccertdir[] = "Public certificates are kept in files in this directory. The files should\n# contain certificates in PEM format. The name of each file should look\n# like <emailaddress>.crt. The default directory is .alpine-smime/public.";
+
+CONF_TXT_T cf_text_privatekeydir[] = "Private keys are kept in files in this directory. The files are in PEM format.\n# The name of a file should look like <emailaddress>.key.\n# The default directory is .alpine-smime/private.";
+
+CONF_TXT_T cf_text_cacertdir[] = "Certificate Authority certificates (in addition to the normal CACerts for the\n# system) are kept in files in this directory. The files are in PEM format.\n# Filenames should end with .crt. The default directory is .alpine-smime/ca.";
+
+CONF_TXT_T cf_text_publiccertcontainer[] = "If this option is set then public certificates are kept in a single container\n# \"file\" similar to a remote configuration file instead of in the\n# smime-publiccert-directory. The value can be a remote or local folder\n# specification like for a non-standard pinerc value. The default\n# is that it is not set.";
+
+CONF_TXT_T cf_text_privatekeycontainer[] = "If this option is set then private keys are kept in a single container\n# \"file\" similar to a remote configuration file instead of in the\n# private-key-directory. The value can be a remote or local folder\n# specification like for a non-standard pinerc value. The default\n# is that it is not set.";
+
+CONF_TXT_T cf_text_cacertcontainer[] = "If this option is set then CAcerts are kept in a single container\n# \"file\" similar to a remote configuration file instead of in the\n# ca-cert-directory. The value can be a remote or local folder\n# specification like for a non-standard pinerc value. The default\n# is that it is not set.";
+
+#endif /* SMIME */
+
+#ifdef ENABLE_LDAP
+CONF_TXT_T cf_text_ldap_server[] = "LDAP servers for looking up addresses.";
+#endif /* ENABLE_LDAP */
+
+CONF_TXT_T cf_text_rss_news[] = "RSS News feed";
+
+CONF_TXT_T cf_text_rss_weather[] = "RSS Weather feed";
+
+CONF_TXT_T cf_text_wp_indexheight[] = "Web Alpine index table row height";
+
+CONF_TXT_T cf_text_wp_indexlines[] = "Web Alpine number of index lines in table";
+
+CONF_TXT_T cf_text_wp_aggstate[] = "Web Alpine aggregate operations tab state";
+
+CONF_TXT_T cf_text_wp_state[] = "Web Alpine various aspects of cross-session state";
+
+CONF_TXT_T cf_text_wp_columns[] = "Web Alpine preferred width for message display in characters";
+
+CONF_TXT_T cf_text_inbox_path[] = "Path of (local or remote) INBOX, e.g. ={mail.somewhere.edu}inbox\n# Normal Unix default is the local INBOX (usually /usr/spool/mail/$USER).";
+
+CONF_TXT_T cf_text_incoming_folders[] = "List of incoming msg folders besides INBOX, e.g. ={host2}inbox, {host3}inbox\n# Syntax: optnl-label {optnl-imap-host-name}folder-path";
+
+CONF_TXT_T cf_text_folder_collections[] = "List of directories where saved-message folders may be. First one is\n# the default for Saves. Example: Main {host1}mail/[], Desktop mail\\[]\n# Syntax: optnl-label {optnl-imap-hostname}optnl-directory-path[]";
+
+CONF_TXT_T cf_text_news_collections[] = "List, only needed if nntp-server not set, or news is on a different host\n# than used for NNTP posting. Examples: News *[] or News *{host3/nntp}[]\n# Syntax: optnl-label *{news-host/protocol}[]";
+
+CONF_TXT_T cf_text_pruned_folders[] = "List of folders, assumed to be in first folder collection,\n# offered for pruning each month. For example: mumble";
+
+CONF_TXT_T cf_text_default_fcc[] = "Over-rides default path for sent-mail folder, e.g. =old-mail (using first\n# folder collection dir) or ={host2}sent-mail or =\"\" (to suppress saving).\n# Default: sent-mail (Unix) or SENTMAIL.MTX (PC) in default folder collection.";
+
+CONF_TXT_T cf_text_default_saved[] = "Over-rides default path for saved-msg folder, e.g. =saved-messages (using 1st\n# folder collection dir) or ={host2}saved-mail or =\"\" (to suppress saving).\n# Default: saved-messages (Unix) or SAVEMAIL.MTX (PC) in default collection.";
+
+CONF_TXT_T cf_text_postponed_folder[] = "Over-rides default path for postponed messages folder, e.g. =pm (which uses\n# first folder collection dir) or ={host4}pm (using home dir on host4).\n# Default: postponed-msgs (Unix) or POSTPOND.MTX (PC) in default fldr coltn.";
+
+CONF_TXT_T cf_text_mail_directory[] = "Alpine compares this value with the first folder collection directory.\n# If they match (or no folder collections are defined), and the directory\n# does not exist, Alpine will create and use it. Default: ~/mail";
+
+CONF_TXT_T cf_text_read_message_folder[] = "If set, specifies where already-read messages will be moved upon quitting.";
+
+CONF_TXT_T cf_text_form_letter_folder[] = "If set, specifies where form letters should be stored.";
+
+CONF_TXT_T cf_text_trash_folder[] = "If set, specifies where trash is moved to in Web Alpine.";
+
+CONF_TXT_T cf_text_signature_file[] = "Over-rides default path for signature file. Default is ~/.signature";
+
+CONF_TXT_T cf_text_literal_sig[] = "Contains the actual signature contents as opposed to the signature filename.\n# If defined, this overrides the signature-file. Default is undefined.";
+
+CONF_TXT_T cf_text_global_address_book[] = "List of file or path names for global/shared addressbook(s).\n# Default: none\n# Syntax: optnl-label path-name";
+
+CONF_TXT_T cf_text_address_book[] = "List of file or path names for personal addressbook(s).\n# Default: ~/.addressbook (Unix) or \\PINE\\ADDRBOOK (PC)\n# Syntax: optnl-label path-name";
+
+CONF_TXT_T cf_text_feature_list[] = "List of features; see Alpine's Setup/options menu for the current set.\n# e.g. feature-list= select-without-confirm, signature-at-bottom\n# Default condition for all of the features is no-.";
+
+CONF_TXT_T cf_text_initial_keystroke_list[] = "Alpine executes these keys upon startup (e.g. to view msg 13: i,j,1,3,CR,v)";
+
+CONF_TXT_T cf_text_default_composer_hdrs[] = "Only show these headers (by default) when composing messages";
+
+CONF_TXT_T cf_text_customized_hdrs[] = "Add these customized headers (and possible default values) when composing";
+
+CONF_TXT_T cf_text_view_headers[] = "When viewing messages, include this list of headers";
+
+CONF_TXT_T cf_text_view_margin_left[] = "When viewing messages, number of blank spaces between left display edge and text";
+
+CONF_TXT_T cf_text_view_margin_right[] = "When viewing messages, number of blank spaces between right display edge and text";
+
+CONF_TXT_T cf_text_quote_suppression[] = "When viewing messages, number of lines of quote displayed before suppressing";
+
+CONF_TXT_T cf_text_wordsep[] = "When these characters appear in the middle of a word in the composer\n# the forward word function will stop at the first text following (as happens\n# with SPACE characters by default)";
+
+CONF_TXT_T cf_text_color_style[] = "Controls display of color";
+
+CONF_TXT_T cf_text_current_indexline_style[] = "Controls display of color for current index line";
+
+CONF_TXT_T cf_text_titlebar_color_style[] = "Controls display of color for the titlebar at top of screen";
+
+CONF_TXT_T cf_text_view_hdr_color[] = "When viewing messages, these are the header colors";
+
+CONF_TXT_T cf_text_save_msg_name_rule[] = "Determines default folder name for Saves...\n# Choices: default-folder, by-sender, by-from, by-recipient, last-folder-used.\n# Default: \"default-folder\", i.e. \"saved-messages\" (Unix) or \"SAVEMAIL\" (PC).";
+
+CONF_TXT_T cf_text_fcc_name_rule[] = "Determines default name for Fcc...\n# Choices: default-fcc, by-recipient, last-fcc-used.\n# Default: \"default-fcc\" (see also \"default-fcc=\" variable.)";
+
+CONF_TXT_T cf_text_sort_key[] = "Sets presentation order of messages in Index. Choices:\n# Subject, From, Arrival, Date, Size, To, Cc, OrderedSubj, Score, and Thread.\n# Order may be reversed by appending /Reverse. Default: \"Arrival\".";
+
+CONF_TXT_T cf_text_addrbook_sort_rule[] = "Sets presentation order of address book entries. Choices: dont-sort,\n# fullname-with-lists-last, fullname, nickname-with-lists-last, nickname\n# Default: \"fullname-with-lists-last\".";
+
+CONF_TXT_T cf_text_folder_sort_rule[] = "Sets presentation order of folder list entries. Choices: alphabetical,\n# alpha-with-dirs-last, alpha-with-dirs-first.\n# Default: \"alpha-with-directories-last\".";
+
+CONF_TXT_T cf_text_old_char_set[] = "Character-set is obsolete, use display-character-set, keyboard-character-set,\n# and posting-character-set.";
+
+CONF_TXT_T cf_text_disp_char_set[] = "Reflects capabilities of the display you have.\n# If unset, the default is taken from your locale. That is usually the right\n# thing to use. Typical alternatives include UTF-8, ISO-8859-x, and EUC-JP\n# (where x is a number between 1 and 9).";
+
+CONF_TXT_T cf_text_key_char_set[] = "Reflects capabilities of the keyboard you have.\n# If unset, the default is to use the same value\n# used for the display-character-set.";
+
+CONF_TXT_T cf_text_post_character_set[] = "Defaults to UTF-8. This is used for outgoing messages.\n# It is usually correct to leave this unset.";
+
+CONF_TXT_T cf_text_unk_character_set[] = "Defaults to nothing, which is equivalent to US-ASCII. This is used for\n# unlabeled incoming messages. It is ok to leave this unset but if you receive\n# unlabeled mail that is usually in some known character set, set that here.";
+
+CONF_TXT_T cf_text_editor[] = "Specifies the program invoked by ^_ in the Composer,\n# or the \"enable-alternate-editor-implicitly\" feature.";
+
+CONF_TXT_T cf_text_speller[] = "Specifies the program invoked by ^T in the Composer.";
+
+CONF_TXT_T cf_text_deadlets[] = "Specifies the number of dead letter files to keep when canceling.";
+
+CONF_TXT_T cf_text_fillcol[] = "Specifies the column of the screen where the composer should wrap.";
+
+CONF_TXT_T cf_text_replystr[] = "Specifies the string to insert when replying to a message.";
+
+CONF_TXT_T cf_text_quotereplstr[] = "Specifies the string to replace quotes with when viewing a message.";
+
+CONF_TXT_T cf_text_replyintro[] = "Specifies the introduction to insert when replying to a message.";
+
+CONF_TXT_T cf_text_emptyhdr[] = "Specifies the string to use when sending a message with no to or cc.";
+
+CONF_TXT_T cf_text_image_viewer[] = "Program to view images (e.g. GIF or TIFF attachments).";
+
+CONF_TXT_T cf_text_browser[] = "List of programs to open Internet URLs (e.g. http or ftp references).";
+
+CONF_TXT_T cf_text_inc_startup[] = "Sets message which cursor begins on. Choices: first-unseen, first-recent,\n# first-important, first-important-or-unseen, first-important-or-recent,\n# first, last. Default: \"first-unseen\".";
+
+CONF_TXT_T cf_pruning_rule[] = "Allows a default answer for the prune folder questions. Choices: yes-ask,\n# yes-no, no-ask, no-no, ask-ask, ask-no. Default: \"ask-ask\".";
+
+CONF_TXT_T cf_reopen_rule[] = "Controls behavior when reopening an already open folder.";
+
+CONF_TXT_T cf_text_thread_disp_style[] = "Style that MESSAGE INDEX is displayed in when threading.";
+
+CONF_TXT_T cf_text_thread_index_style[] = "Style of THREAD INDEX or default MESSAGE INDEX when threading.";
+
+CONF_TXT_T cf_text_thread_more_char[] = "When threading, character used to indicate collapsed messages underneath.";
+
+CONF_TXT_T cf_text_thread_exp_char[] = "When threading, character used to indicate expanded messages underneath.";
+
+CONF_TXT_T cf_text_thread_lastreply_char[] = "When threading, character used to indicate this is the last reply\n# to the parent of this message.";
+
+CONF_TXT_T cf_text_use_only_domain_name[] = "If \"user-domain\" not set, strips hostname in FROM address. (Unix only)";
+
+CONF_TXT_T cf_text_printer[] = "Your default printer selection";
+
+CONF_TXT_T cf_text_personal_print_command[] = "List of special print commands";
+
+CONF_TXT_T cf_text_personal_print_cat[] = "Which category default print command is in";
+
+CONF_TXT_T cf_text_standard_printer[] = "The system wide standard printers";
+
+CONF_TXT_T cf_text_last_time_prune_quest[] = "Set by Alpine; controls beginning-of-month sent-mail pruning.";
+
+CONF_TXT_T cf_text_last_version_used[] = "Set by Alpine; controls display of \"new version\" message.";
+
+CONF_TXT_T cf_text_disable_drivers[] = "List of mail drivers to disable.";
+
+CONF_TXT_T cf_text_disable_auths[] = "List of SASL authenticators to disable.";
+
+CONF_TXT_T cf_text_remote_abook_metafile[] = "Set by Alpine; contains data for caching remote address books.";
+
+CONF_TXT_T cf_text_old_patterns[] = "Patterns is obsolete, use patterns-xxx";
+
+CONF_TXT_T cf_text_old_filters[] = "Patterns-filters is obsolete, use patterns-filters2";
+
+CONF_TXT_T cf_text_old_scores[] = "Patterns-scores is obsolete, use patterns-scores2";
+
+CONF_TXT_T cf_text_patterns[] = "Patterns and their actions are stored here.";
+
+CONF_TXT_T cf_text_remote_abook_history[] = "How many extra copies of remote address book should be kept. Default: 3";
+
+CONF_TXT_T cf_text_remote_abook_validity[] = "Minimum number of minutes between checks for remote address book changes.\n# 0 means never check except when opening a remote address book.\n# -1 means never check. Default: 5";
+
+CONF_TXT_T cf_text_bugs_fullname[] = "Full name for bug report address used by \"Report Bug\" command";
+
+CONF_TXT_T cf_text_bugs_address[] = "Email address used to send bug reports";
+
+CONF_TXT_T cf_text_bugs_extras[] = "Program/Script used by \"Report Bug\" command. No default.";
+
+CONF_TXT_T cf_text_suggest_fullname[] = "Full name for suggestion address used by \"Report Bug\" command";
+
+CONF_TXT_T cf_text_suggest_address[] = "Email address used to send suggestions";
+
+CONF_TXT_T cf_text_local_fullname[] = "Full name for \"local support\" address used by \"Report Bug\" command.\n# Default: Local Support";
+
+CONF_TXT_T cf_text_local_address[] = "Email address used to send to \"local support\".\n# Default: postmaster";
+
+CONF_TXT_T cf_text_forced_abook[] = "Force these address book entries into all writable personal address books.\n# Syntax is forced-abook-entry=nickname|fullname|address\n# This is a comma-separated list of entries, each with syntax above.\n# Existing entries with same nickname are not replaced.\n# Example: help|Help Desk|help@ourdomain.com";
+
+CONF_TXT_T cf_text_kblock_passwd[] = "This is a number between 1 and 5. It is the number of times a user will\n# have to enter a password when they run the keyboard lock command in the\n# main menu. Default is 1.";
+
+CONF_TXT_T cf_text_sendmail_path[] = "This names the path to an alternative program, and any necessary arguments,\n# to be used in posting mail messages. Example:\n# /usr/lib/sendmail -oem -t -oi\n# or,\n# /usr/local/bin/sendit.sh\n# The latter a script found in Alpine distribution's contrib/util directory.\n# NOTE: The program MUST read the message to be posted on standard input,\n# AND operate in the style of sendmail's \"-t\" option.";
+
+CONF_TXT_T cf_text_oper_dir[] = "This names the root of the tree to which the user is restricted when reading\n# and writing folders and files. For example, on Unix ~/work confines the\n# user to the subtree beginning with their work subdirectory.\n# (Note: this alone is not sufficient for preventing access. You will also\n# need to restrict shell access and so on, see Alpine Technical Notes.)\n# Default: not set (so no restriction)";
+
+CONF_TXT_T cf_text_in_fltr[] = "This variable takes a list of programs that message text is piped into\n# after MIME decoding, prior to display.";
+
+CONF_TXT_T cf_text_out_fltr[] = "This defines a program that message text is piped into before MIME\n# encoding, prior to sending";
+
+CONF_TXT_T cf_text_alt_addrs[] = "A list of alternate addresses the user is known by";
+
+CONF_TXT_T cf_text_keywords[] = "A list of keywords for use in categorizing messages";
+
+CONF_TXT_T cf_text_kw_colors[] = "Colors used to display keywords in the index";
+
+CONF_TXT_T cf_text_kw_braces[] = "Characters which surround keywords in SUBJKEY token.\n# Default is \"{\" \"} \"";
+
+CONF_TXT_T cf_text_opening_sep[] = "Characters between subject and opening text in SUBJECTTEXT token.\n# Default is \" - \"";
+
+CONF_TXT_T cf_text_abook_formats[] = "This is a list of formats for address books. Each entry in the list is made\n# up of space-delimited tokens telling which fields are displayed and in\n# which order. See help text";
+
+CONF_TXT_T cf_text_index_format[] = "This gives a format for displaying the index. It is made\n# up of space-delimited tokens telling which fields are displayed and in\n# which order. See help text";
+
+CONF_TXT_T cf_text_overlap[] = "The number of lines of overlap when scrolling through message text";
+
+CONF_TXT_T cf_text_maxremstreams[] = "The maximum number of non-stayopen remote connections that Alpine will use";
+
+CONF_TXT_T cf_text_permlocked[] = "A list of folders that should be left open once opened (INBOX is implicit)";
+
+CONF_TXT_T cf_text_margin[] = "Number of lines from top and bottom of screen where single\n# line scrolling occurs.";
+
+CONF_TXT_T cf_text_stat_msg_delay[] = "The number of seconds to sleep after writing a status message";
+
+CONF_TXT_T cf_text_busy_cue_rate[] = "Number of times per-second to update busy cue messages";
+
+CONF_TXT_T cf_text_mailcheck[] = "The approximate number of seconds between checks for new mail";
+
+CONF_TXT_T cf_text_mailchecknoncurr[] = "The approximate number of seconds between checks for new mail in folders\n# other than the current folder and inbox.\n# Default is same as mail-check-interval";
+
+CONF_TXT_T cf_text_maildropcheck[] = "The minimum number of seconds between checks for new mail in a Mail Drop.\n# This is always effectively at least as large as the mail-check-interval";
+
+CONF_TXT_T cf_text_nntprange[] = "For newsgroups accessed using NNTP, only messages numbered in the range\n# lastmsg-range+1 to lastmsg will be considered";
+
+CONF_TXT_T cf_text_news_active[] = "Path and filename of news configuration's active file.\n# The default is typically \"/usr/lib/news/active\".";
+
+CONF_TXT_T cf_text_news_spooldir[] = "Directory containing system's news data.\n# The default is typically \"/usr/spool/news\"";
+
+CONF_TXT_T cf_text_upload_cmd[] = "Path and filename of the program used to upload text from your terminal\n# emulator's into Alpine's composer.";
+
+CONF_TXT_T cf_text_upload_prefix[] = "Text sent to terminal emulator prior to invoking the program defined by\n# the upload-command variable.\n# Note: _FILE_ will be replaced with the temporary file used in the upload.";
+
+CONF_TXT_T cf_text_download_cmd[] = "Path and filename of the program used to download text via your terminal\n# emulator from Alpine's export and save commands.";
+
+CONF_TXT_T cf_text_download_prefix[] = "Text sent to terminal emulator prior to invoking the program defined by\n# the download-command variable.\n# Note: _FILE_ will be replaced with the temporary file used in the download.";
+
+CONF_TXT_T cf_text_goto_default[] = "Sets the default folder and collection offered at the Goto Command's prompt.";
+
+CONF_TXT_T cf_text_mailcap_path[] = "Sets the search path for the mailcap configuration file.\n# NOTE: colon delimited under UNIX, semi-colon delimited under DOS/Windows/OS2.";
+
+CONF_TXT_T cf_text_mimetype_path[] = "Sets the search path for the mimetypes configuration file.\n# NOTE: colon delimited under UNIX, semi-colon delimited under DOS/Windows/OS2.";
+
+CONF_TXT_T cf_text_newmail_fifo_path[] = "Sets the filename for the newmail fifo (named pipe). Unix only.";
+
+CONF_TXT_T cf_text_nmw_width[] = "Sets the width for the NewMail screen.";
+
+CONF_TXT_T cf_text_user_input_timeo[] = "If no user input for this many hours, Alpine will exit if in an idle loop\n# waiting for a new command. If set to zero (the default), then there will\n# be no timeout.";
+
+CONF_TXT_T cf_text_debug_mem[] = "Debug-memory is obsolete";
+
+CONF_TXT_T cf_text_tcp_open_timeo[] = "Sets the time in seconds that Alpine will attempt to open a network\n# connection. The default is 30, the minimum is 5, and the maximum is\n# system defined (typically 75).";
+
+CONF_TXT_T cf_text_tcp_read_timeo[] = "Network read warning timeout. The default is 15, the minimum is 5, and the\n# maximum is 1000.";
+
+CONF_TXT_T cf_text_tcp_write_timeo[] = "Network write warning timeout. The default is 0 (unset), the minimum\n# is 5 (if not 0), and the maximum is 1000.";
+
+CONF_TXT_T cf_text_tcp_query_timeo[] = "If this much time has elapsed at the time of a tcp read or write\n# timeout, Alpine will ask if you want to break the connection.\n# Default is 60 seconds, minimum is 5, maximum is 1000.";
+
+CONF_TXT_T cf_text_rsh_open_timeo[] = "Sets the time in seconds that Alpine will attempt to open a UNIX remote\n# shell connection. The default is 15, min is 5, and max is unlimited.\n# Zero disables rsh altogether.";
+
+CONF_TXT_T cf_text_rsh_path[] = "Sets the name of the command used to open a UNIX remote shell connection.\n# The default is typically /usr/ucb/rsh.";
+
+CONF_TXT_T cf_text_rsh_command[] = "Sets the format of the command used to open a UNIX remote\n# shell connection. The default is \"%s %s -l %s exec /etc/r%sd\"\n# NOTE: the 4 (four) \"%s\" entries MUST exist in the provided command\n# where the first is for the command's path, the second is for the\n# host to connect to, the third is for the user to connect as, and the\n# fourth is for the connection method (typically \"imap\")";
+
+CONF_TXT_T cf_text_ssh_open_timeo[] = "Sets the time in seconds that Alpine will attempt to open a UNIX secure\n# shell connection. The default is 15, min is 5, and max is unlimited.\n# Zero disables ssh altogether.";
+
+CONF_TXT_T cf_text_inc_check_timeo[] = "Sets the time in seconds that Alpine will attempt to open a network\n# connection when checking for new unseen messages in an incoming folder.\n# The default is 5.";
+
+CONF_TXT_T cf_text_inc_check_interval[] = "Sets the approximate number of seconds between checks for unseen messages\n# in incoming folders. The default is 180.";
+
+CONF_TXT_T cf_text_inc_second_check_interval[] = "Sets the approximate number of seconds between checks for unseen messages\n# for other than local or IMAP folders. The default is 180.";
+
+CONF_TXT_T cf_text_inc_check_list[] = "List of incoming folders to check for unseen messages. The default if left\n# blank is to check all incoming folders.";
+
+CONF_TXT_T cf_text_ssh_path[] = "Sets the name of the command used to open a UNIX secure shell connection.\n# Typically this is /usr/bin/ssh.";
+
+CONF_TXT_T cf_text_ssh_command[] = "Sets the format of the command used to open a UNIX secure\n# shell connection. The default is \"%s %s -l %s exec /etc/r%sd\"\n# NOTE: the 4 (four) \"%s\" entries MUST exist in the provided command\n# where the first is for the command's path, the second is for the\n# host to connect to, the third is for the user to connect as, and the\n# fourth is for the connection method (typically \"imap\")";
+
+CONF_TXT_T cf_text_version_threshold[] = "Sets the version number Alpine will use as a threshold for offering\n# its new version message on startup.";
+
+CONF_TXT_T cf_text_archived_folders[] = "List of folder pairs; the first indicates a folder to archive, and the\n# second indicates the folder read messages in the first should\n# be moved to.";
+
+CONF_TXT_T cf_text_elm_style_save[] = "Elm-style-save is obsolete, use saved-msg-name-rule";
+
+CONF_TXT_T cf_text_header_in_reply[] = "Header-in-reply is obsolete, use include-header-in-reply in feature-list";
+
+CONF_TXT_T cf_text_feature_level[] = "Feature-level is obsolete, use feature-list";
+
+CONF_TXT_T cf_text_old_style_reply[] = "Old-style-reply is obsolete, use signature-at-bottom in feature-list";
+
+CONF_TXT_T cf_text_compose_mime[] = "Compose-mime is obsolete";
+
+CONF_TXT_T cf_text_show_all_characters[] = "Show-all-characters is obsolete";
+
+CONF_TXT_T cf_text_save_by_sender[] = "Save-by-sender is obsolete, use saved-msg-name-rule";
+
+CONF_TXT_T cf_text_file_dir[] = "Default directory used for Attachment handling (attach and save)\n# and Export command output";
+
+CONF_TXT_T cf_text_folder_extension[] = "Folder-extension is obsolete";
+
+CONF_TXT_T cf_text_normal_foreground_color[] = "Choose: black, blue, green, cyan, red, magenta, yellow, or white.";
+
+CONF_TXT_T cf_text_window_position[] = "Window position in the format: CxR+X+Y\n# Where C and R are the window size in characters and X and Y are the\n# screen position of the top left corner of the window.\n# This is no longer used unless position is not set in registry.";
+
+CONF_TXT_T cf_text_newsrc_path[] = "Full path and name of NEWSRC file";
+
+
+/*----------------------------------------------------------------------
+These are the variables that control a number of pine functions. They
+come out of the .pinerc and the /usr/local/lib/pine.conf files. Some can
+be set by the user while in Alpine. Eventually all the local ones should
+be so and maybe the global ones too.
+
+Each variable can have a command-line, user, global, and current value.
+All of these values are malloc'd. The user value is the one read out of
+the user's .pinerc, the global value is the one from the system pine
+configuration file. There are often defaults for the global values, set
+at the start of init_vars(). Perhaps someday there will be group values.
+The current value is the one that is actually in use.
+ ----*/
+/* name is_changed_val
+ remove_quotes |
+ is_outermost | |
+ is_onlymain | | |
+ is_fixed | | | |
+ is_list | | | | |
+ is_global | | | | | |
+ is_user | | | | | | |
+ been_written | | | | | | | |
+ is_used | | | | | | | | |
+ is_obsolete | | | | | | | | | |
+ | | | | | | | | | | |
+ (on following line) description | | | | | | | | | | |
+ | | | | | | | | | | | |
+ | | | | | | | | | | | | */
+static struct variable variables[] = {
+{"personal-name", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_personal_name},
+{
+#if defined(DOS) || defined(OS2)
+ /* Have to have this on DOS, PC's, Macs, etc... */
+ "user-id", 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0,
+#else /* Don't allow on UNIX machines for some security */
+ "user-id", 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0,
+#endif
+ "User ID", cf_text_user_id},
+{"user-domain", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_user_domain},
+{"smtp-server", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0,
+ "SMTP Server (for sending)", cf_text_smtp_server},
+{"nntp-server", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0,
+ "NNTP Server (for news)", cf_text_nntp_server},
+{"inbox-path", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_inbox_path},
+{"incoming-archive-folders", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ NULL, cf_text_archived_folders},
+{"pruned-folders", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0,
+ NULL, cf_text_pruned_folders},
+{"default-fcc", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ "Default Fcc (File carbon copy)", cf_text_default_fcc},
+{"default-saved-msg-folder", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ "Default Saved Message Folder", cf_text_default_saved},
+{"postponed-folder", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_postponed_folder},
+{"read-message-folder", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_read_message_folder},
+{"form-letter-folder", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_form_letter_folder},
+{"trash-folder", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_trash_folder},
+{"literal-signature", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_literal_sig},
+{"signature-file", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_signature_file},
+{"feature-list", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ NULL, cf_text_feature_list},
+{"initial-keystroke-list", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ NULL, cf_text_initial_keystroke_list},
+{"default-composer-hdrs", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ "Default Composer Headers", cf_text_default_composer_hdrs},
+{"customized-hdrs", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ "Customized Headers", cf_text_customized_hdrs},
+{"viewer-hdrs", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ "Viewer Headers", cf_text_view_headers},
+{"viewer-margin-left", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_view_margin_left},
+{"viewer-margin-right", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_view_margin_right},
+{"quote-suppression-threshold", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_quote_suppression},
+{"saved-msg-name-rule", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ "Saved Message Name Rule", cf_text_save_msg_name_rule},
+{"fcc-name-rule", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_fcc_name_rule},
+{"sort-key", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_sort_key},
+{"addrbook-sort-rule", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ "Address Book Sort Rule", cf_text_addrbook_sort_rule},
+{"folder-sort-rule", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_folder_sort_rule},
+{"goto-default-rule", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_goto_default},
+{"incoming-startup-rule", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_inc_startup},
+{"pruning-rule", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_pruning_rule},
+{"folder-reopen-rule", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_reopen_rule},
+{"threading-display-style", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_thread_disp_style},
+{"threading-index-style", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_thread_index_style},
+{"threading-indicator-character", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_thread_more_char},
+{"threading-expanded-character", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_thread_exp_char},
+{"threading-lastreply-character", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ "Threading Last Reply Character", cf_text_thread_lastreply_char},
+#ifndef _WINDOWS
+{"display-character-set", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_disp_char_set},
+{"character-set", 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_old_char_set},
+{"keyboard-character-set", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_key_char_set},
+#endif /* ! _WINDOWS */
+{"posting-character-set", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_post_character_set},
+{"unknown-character-set", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_unk_character_set},
+{"editor", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0,
+ NULL, cf_text_editor},
+{"speller", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_speller},
+{"composer-wrap-column", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_fillcol},
+{"reply-indent-string", 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+ NULL, cf_text_replystr},
+{"reply-leadin", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_replyintro},
+{"quote-replace-string", 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+ NULL, cf_text_quotereplstr},
+{"composer-word-separators", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0,
+ NULL, cf_text_wordsep},
+{"empty-header-message", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_emptyhdr},
+{"image-viewer", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_image_viewer},
+{"use-only-domain-name", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_use_only_domain_name},
+{"bugs-fullname", 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_bugs_fullname},
+{"bugs-address", 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_bugs_address},
+{"bugs-additional-data", 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_bugs_extras},
+{"suggest-fullname", 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_suggest_fullname},
+{"suggest-address", 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_suggest_address},
+{"local-fullname", 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_local_fullname},
+{"local-address", 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_local_address},
+{"forced-abook-entry", 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,
+ NULL, cf_text_forced_abook},
+{"kblock-passwd-count", 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_kblock_passwd},
+{"display-filters", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ NULL, cf_text_in_fltr},
+{"sending-filters", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ NULL, cf_text_out_fltr},
+{"alt-addresses", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ "Alternate Addresses", cf_text_alt_addrs},
+{"keywords", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ NULL, cf_text_keywords},
+{"keyword-surrounding-chars", 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+ "Keyword Surrounding Characters", cf_text_kw_braces},
+{"opening-text-separator-chars", 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+ "Opening Text Separator Characters", cf_text_opening_sep},
+{"addressbook-formats", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ "Address Book Formats", cf_text_abook_formats},
+{"index-format", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_index_format},
+{"viewer-overlap", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_overlap},
+{"scroll-margin", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_margin},
+{"status-message-delay", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_stat_msg_delay},
+{"busy-cue-rate", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_busy_cue_rate},
+{"mail-check-interval", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_mailcheck},
+{"mail-check-interval-noncurrent", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_mailchecknoncurr},
+{"maildrop-check-minimum", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_maildropcheck},
+{"nntp-range", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ "NNTP Range", cf_text_nntprange},
+{"newsrc-path", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_newsrc_path},
+{"news-active-file-path", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_news_active},
+{"news-spool-directory", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_news_spooldir},
+{"upload-command", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_upload_cmd},
+{"upload-command-prefix", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_upload_prefix},
+{"download-command", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_download_cmd},
+{"download-command-prefix", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_download_prefix},
+{"mailcap-search-path", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_mailcap_path},
+{"mimetype-search-path", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_mimetype_path},
+{"url-viewers", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ "URL-Viewers", cf_text_browser},
+{"max-remote-connections", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ "Maximum Remote Connections", cf_text_maxremstreams},
+{"stay-open-folders", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ "Stayopen Folders", cf_text_permlocked},
+{"incoming-check-timeout", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_inc_check_timeo},
+{"incoming-check-interval", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_inc_check_interval},
+{"incoming-check-interval-secondary", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_inc_second_check_interval},
+{"incoming-check-list", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ NULL, cf_text_inc_check_list},
+{"dead-letter-files", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_deadlets},
+#if !defined(DOS) && !defined(OS2) && !defined(LEAVEOUTFIFO)
+{"newmail-fifo-path", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ "NewMail FIFO Path", cf_text_newmail_fifo_path},
+#endif
+{"newmail-window-width", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ "NewMail Window Width", cf_text_nmw_width},
+/*
+ * Starting here, the variables are hidden in the Setup/Config screen.
+ * They are exposed if feature expose-hidden-config is set.
+ */
+{"incoming-folders", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ NULL, cf_text_incoming_folders},
+{"mail-directory", 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_mail_directory},
+{"folder-collections", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ NULL, cf_text_folder_collections},
+{"news-collections", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ NULL, cf_text_news_collections},
+{"address-book", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ NULL, cf_text_address_book},
+{"global-address-book", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ NULL, cf_text_global_address_book},
+{"standard-printer", 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0,
+ NULL, cf_text_standard_printer},
+{"last-time-prune-questioned", 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0,
+ NULL, cf_text_last_time_prune_quest},
+{"last-version-used", 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0,
+ NULL, cf_text_last_version_used},
+{"sendmail-path", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_sendmail_path},
+{"operating-dir", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_oper_dir},
+{"user-input-timeout", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_user_input_timeo},
+/* OBSOLETE */
+#ifdef DEBUGJOURNAL
+{"debug-memory", 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_debug_mem},
+#endif
+{"tcp-open-timeout", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ "TCP Open Timeout", cf_text_tcp_open_timeo},
+{"tcp-read-warning-timeout", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ "TCP Read Warning Timeout", cf_text_tcp_read_timeo},
+{"tcp-write-warning-timeout", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ "TCP Write Warning Timeout", cf_text_tcp_write_timeo},
+{"tcp-query-timeout", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ "TCP Query Timeout", cf_text_tcp_query_timeo},
+{"rsh-command", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_rsh_command},
+{"rsh-path", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_rsh_path},
+{"rsh-open-timeout", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_rsh_open_timeo},
+{"ssh-command", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_ssh_command},
+{"ssh-path", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_ssh_path},
+{"ssh-open-timeout", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_ssh_open_timeo},
+{"new-version-threshold", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_version_threshold},
+{"disable-these-drivers", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0,
+ NULL, cf_text_disable_drivers},
+{"disable-these-authenticators", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0,
+ NULL, cf_text_disable_auths},
+{"remote-abook-metafile", 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0,
+ NULL, cf_text_remote_abook_metafile},
+{"remote-abook-history", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_remote_abook_history},
+{"remote-abook-validity", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_remote_abook_validity},
+{"printer", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_printer},
+{"personal-print-command", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ NULL, cf_text_personal_print_command},
+{"personal-print-category", 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_personal_print_cat},
+{"patterns", 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ NULL, cf_text_old_patterns},
+{"patterns-roles", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ NULL, cf_text_patterns},
+{"patterns-filters2", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ "Patterns Filters", cf_text_patterns},
+{"patterns-filters", 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ NULL, cf_text_old_filters},
+{"patterns-scores2", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ "Patterns Scores", cf_text_patterns},
+{"patterns-scores", 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ NULL, cf_text_old_scores},
+{"patterns-indexcolors", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ NULL, cf_text_patterns},
+{"patterns-other", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ NULL, cf_text_patterns},
+{"patterns-search", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ NULL, cf_text_patterns},
+/* OBSOLETE VARS */
+{"elm-style-save", 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_elm_style_save},
+{"header-in-reply", 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_header_in_reply},
+{"feature-level", 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_feature_level},
+{"old-style-reply", 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_old_style_reply},
+{"compose-mime", 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_compose_mime},
+{"show-all-characters", 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_show_all_characters},
+{"save-by-sender", 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_save_by_sender},
+#if defined(DOS) || defined(OS2)
+{"file-directory", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_file_dir},
+{"folder-extension", 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_folder_extension},
+#endif
+#ifndef _WINDOWS
+{"color-style", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_color_style},
+#endif
+{"current-indexline-style", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_current_indexline_style},
+{"titlebar-color-style", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_titlebar_color_style},
+{"normal-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, cf_text_normal_foreground_color},
+{"normal-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"reverse-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"reverse-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"title-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"title-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"title-closed-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"title-closed-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"status-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"status-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"keylabel-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"keylabel-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"keyname-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"keyname-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"selectable-item-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"selectable-item-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"meta-message-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"meta-message-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"quote1-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"quote1-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"quote2-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"quote2-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"quote3-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"quote3-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"incoming-unseen-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"incoming-unseen-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"signature-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"signature-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"prompt-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"prompt-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"header-general-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"header-general-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"index-to-me-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"index-to-me-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"index-important-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"index-important-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"index-deleted-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"index-deleted-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"index-answered-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"index-answered-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"index-new-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"index-new-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"index-recent-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"index-recent-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"index-forward-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"index-forward-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"index-unseen-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"index-unseen-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"index-highpriority-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"index-highpriority-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"index-lowpriority-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"index-lowpriority-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"index-arrow-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"index-arrow-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"index-subject-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"index-subject-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"index-from-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"index-from-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"index-opening-foreground-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"index-opening-background-color", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0},
+{"viewer-hdr-colors", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0,
+ "Viewer Header Colors", cf_text_view_hdr_color},
+{"keyword-colors", 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0,
+ NULL, cf_text_kw_colors},
+#ifdef _WINDOWS
+{"font-name", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, "name and size of font."},
+{"font-size", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, NULL},
+{"font-style", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, NULL},
+{"font-char-set", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, NULL},
+{"print-font-name", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ NULL, "name and size of printer font."},
+{"print-font-size", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, NULL},
+{"print-font-style", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, NULL},
+{"print-font-char-set", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, NULL},
+{"window-position", 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0,
+ NULL, cf_text_window_position},
+{"cursor-style", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, NULL},
+#endif /* _WINDOWS */
+#ifdef SMIME
+{"smime-public-cert-directory", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ "S/MIME - Public Cert Directory", cf_text_publiccertdir},
+{"smime-public-cert-container", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ "S/MIME - Public Cert Container", cf_text_publiccertcontainer},
+{"smime-private-key-directory", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ "S/MIME - Private Key Directory", cf_text_privatekeydir},
+{"smime-private-key-container", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ "S/MIME - Private Key Container", cf_text_privatekeycontainer},
+{"smime-cacert-directory", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ "S/MIME - Cert Authority Directory", cf_text_cacertdir},
+{"smime-cacert-container", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ "S/MIME - Cert Authority Container", cf_text_cacertcontainer},
+#endif /* SMIME */
+#ifdef ENABLE_LDAP
+{"ldap-servers", 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0,
+ "LDAP Servers", cf_text_ldap_server},
+#endif /* ENABLE_LDAP */
+{"rss-news", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ "WEB ALPINE - RSS News", cf_text_rss_news},
+{"rss-weather", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ "WEB ALPINE - RSS Weather", cf_text_rss_weather},
+{"wp-indexheight", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ "WEB ALPINE - Index Height", cf_text_wp_indexheight},
+{"wp-indexlines", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ "WEB ALPINE - Index Lines", cf_text_wp_indexlines},
+{"wp-aggstate", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ "WEB ALPINE - Aggregate State", cf_text_wp_aggstate},
+{"wp-state", 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0,
+ "WEB ALPINE - Cross Session State", cf_text_wp_state},
+{"wp-columns", 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0,
+ "WEB ALPINE - Columns", cf_text_wp_columns},
+{NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL,NULL}
+};
+
+
+struct variable *
+var_from_name(char *name)
+{
+ struct variable *v;
+ int i;
+
+ if(!(name && name[0]))
+ return(NULL);
+
+ for(i = 0; (v = &variables[i]) && v->name; i++)
+ if(!strucmp(v->name,name))
+ return(v);
+
+ return(NULL);
+}
+
+
+void
+init_init_vars(struct pine *ps)
+{
+ ps->vars = variables;
+}
+
+
+#define DSIZE (25000)
+/* this is just like dprint except it prints to a char * */
+#ifdef DEBUG
+#define mprint(n,x) { \
+ if(debug >= (n)){ \
+ snprintf x ; \
+ db += strlen(db); \
+ } \
+ }
+#else
+#define mprint(n,x)
+#endif
+
+/*
+ * this was split out from init_vars so we can get at the
+ * pinerc location sooner.
+ */
+void
+init_pinerc(struct pine *ps, char **debug_out)
+{
+ char buf[MAXPATH+1], *p, *db;
+#if defined(DOS) || defined(OS2)
+ char buf2[MAXPATH+1], l_pinerc[MAXPATH+1];
+ int nopinerc = 0, confregset = -1;
+ register struct variable *vars = ps->vars;
+#endif
+
+#ifdef DEBUG
+ /*
+ * Since this routine is called before we've had a chance to set up
+ * the debug file for output, we put the debugging into memory and
+ * pass it back to the caller for use after init_debug(). We just
+ * allocate plenty of space.
+ */
+ if(debug_out){
+ db = *debug_out = (char *)fs_get(DSIZE * sizeof(char));
+ db[0] = '\0';
+ }
+#endif
+
+ mprint(2, (db, DSIZE-(db-(*debug_out)), "\n -- init_pinerc --\n\n"));
+
+#if defined(DOS) || defined(OS2)
+ /*
+ * Rules for the config/support file locations under DOS are:
+ *
+ * 1) The location of the PINERC is searched for in the following
+ * order of precedence:
+ * - File pointed to by '-p' command line option
+ * - File pointed to by PINERC environment variable
+ * - $HOME\pine
+ * - same dir as argv[0]
+ *
+ * 2) The HOME environment variable, if not set, defaults to
+ * root of the current working drive (see alpine.c)
+ *
+ * 3) The default for external files (PINE.SIG and ADDRBOOK) is the
+ * same directory as the pinerc
+ *
+ * 4) The support files (PINE.HLP and PINE.NDX) are expected to be in
+ * the same directory as PINE.EXE.
+ */
+
+ if(ps->prc){
+ mprint(2, (db, DSIZE-(db-(*debug_out)),
+ "Personal config \"%.100s\" comes from command line\n",
+ (ps->prc && ps->prc->name) ? ps->prc->name : "<no name>"));
+ }
+ else{
+ mprint(2, (db, DSIZE-(db-(*debug_out)),
+ "Personal config not set on cmdline, checking for $PINERC\n"));
+ }
+
+ /*
+ * First, if prc hasn't been set by a command-line -p, check to see
+ * if PINERC is in the environment. If so, treat it just like we
+ * would have treated it if it were a command-line arg.
+ */
+ if(!ps->prc && (p = getenv("PINERC")) && *p){
+ char path[MAXPATH], dir[MAXPATH];
+
+ if(IS_REMOTE(p) || is_absolute_path(p)){
+ strncpy(path, p, sizeof(path)-1);
+ path[sizeof(path)-1] = '\0';
+ }
+ else{
+ getcwd(dir, sizeof(dir));
+ build_path(path, dir, p, sizeof(path));
+ }
+
+ if(!IS_REMOTE(p))
+ ps->pinerc = cpystr(path);
+
+ ps->prc = new_pinerc_s(path);
+
+ if(ps->prc){
+ mprint(2, (db, DSIZE-(db-(*debug_out)),
+ " yes, personal config \"%.100s\" comes from $PINERC\n",
+ (ps->prc && ps->prc->name) ? ps->prc->name : "<no name>"));
+ }
+ }
+
+ /*
+ * Pinerc used to be the name of the pinerc file. Then we added
+ * the possibility of the pinerc file being remote, and we replaced
+ * the variable pinerc with the structure prc. Unfortunately, some
+ * parts of pine rely on the fact that pinerc is the name of the
+ * pinerc _file_, and use the directory that the pinerc file is located
+ * in for their own purposes. We want to preserve that so things will
+ * keep working. So, even if the real pinerc is remote, we need to
+ * put the name of a pinerc file in the pinerc variable so that the
+ * directory which contains that file is writable. The file itself
+ * doesn't have to exist for this purpose, since we are really only
+ * using the name of the directory containing the file. Twisted.
+ * (Alternatively, we could fix all of the code that uses the pinerc
+ * variable for this purpose to use a new variable which really is
+ * just a directory.) hubert 2000-sep
+ *
+ * There are 3 cases. If pinerc is already set that means that the user
+ * gave either a -p pinerc or an environment pinerc that is a local file,
+ * and we are done. If pinerc is not set, then either prc is set or not.
+ * If prc is set then the -p arg or PINERC value is a remote pinerc.
+ * In that case we need to find a local directory to use, and put that
+ * directory in the pinerc variable (with a fake filename tagged on).
+ * If prc is not set, then user hasn't told us anything so we have to
+ * try to find the default pinerc file by looking down the path of
+ * possibilities. When we find it, we'll also use that directory.
+ */
+ if(!ps->pinerc){
+ *l_pinerc = '\0';
+ *buf = '\0';
+
+ if(ps->prc){ /* remote pinerc case */
+ /*
+ * We don't give them an l_pinerc unless they tell us where
+ * to put it.
+ */
+ if(ps->aux_files_dir)
+ build_path(l_pinerc, ps->aux_files_dir, SYSTEM_PINERC,
+ sizeof(l_pinerc));
+ else{
+ /*
+ * Search for a writable directory.
+ * Mimic what happens in !prc for local case, except we
+ * don't need to look for the actual file.
+ */
+
+ /* check if $HOME\PINE is writable */
+ build_path(buf2, ps->home_dir, DF_PINEDIR, sizeof(buf2));
+ if(is_writable_dir(buf2) == 0)
+ build_path(l_pinerc, buf2, SYSTEM_PINERC, sizeof(l_pinerc));
+ else{ /* $HOME\PINE not a writable dir */
+ /* use this unless registry redirects us */
+ build_path(l_pinerc, ps->pine_dir, SYSTEM_PINERC,
+ sizeof(l_pinerc));
+#ifdef _WINDOWS
+ /* if in registry, use that value */
+ if(mswin_reg(MSWR_OP_GET, MSWR_PINE_RC, buf2, sizeof(buf2))
+ && !IS_REMOTE(buf2)){
+ strncpy(l_pinerc, buf2, sizeof(l_pinerc)-1);
+ l_pinerc[sizeof(l_pinerc)-1] = '\0';
+ }
+#endif
+ }
+ }
+ }
+ else{ /* searching for pinerc file to use */
+ /*
+ * Buf2 is $HOME\PINE. If $HOME is not explicitly set,
+ * it defaults to the current working drive (often C:).
+ * See alpine.c to see how it is initially set.
+ */
+
+ mprint(2, (db, DSIZE-(db-(*debug_out)), " no, searching...\n"));
+ build_path(buf2, ps->home_dir, DF_PINEDIR, sizeof(buf2));
+ mprint(2, (db, DSIZE-(db-(*debug_out)),
+ " checking for writable %.100s dir \"%.100s\" off of homedir\n",
+ DF_PINEDIR, buf2));
+ if(is_writable_dir(buf2) == 0){
+ /*
+ * $HOME\PINE exists and is writable.
+ * See if $HOME\PINE\PINERC exists.
+ */
+ build_path(buf, buf2, SYSTEM_PINERC, sizeof(buf));
+ strncpy(l_pinerc, buf, sizeof(l_pinerc)-1);
+ l_pinerc[sizeof(l_pinerc)-1] = '\0';
+ mprint(2, (db, DSIZE-(db-(*debug_out)), " yes, now checking for file \"%.100s\"\n",
+ buf));
+ if(can_access(buf, ACCESS_EXISTS) == 0){ /* found it! */
+ /*
+ * Buf is what we were looking for.
+ * It is local and can be used for the directory, too.
+ */
+ mprint(2, (db, DSIZE-(db-(*debug_out)), " found it\n"));
+ }
+ else{
+ /*
+ * No $HOME\PINE\PINERC, look for
+ * one in same dir as PINE.EXE.
+ */
+ build_path(buf2, ps->pine_dir, SYSTEM_PINERC,
+ sizeof(buf2));
+ mprint(2, (db, DSIZE-(db-(*debug_out)),
+ " no, checking for \"%.100s\" in pine.exe dir\n",
+ buf2));
+ if(can_access(buf2, ACCESS_EXISTS) == 0){
+ /* found it! */
+ mprint(2, (db, DSIZE-(db-(*debug_out)), " found it\n"));
+ strncpy(buf, buf2, sizeof(buf)-1);
+ buf[sizeof(buf)-1] = '\0';
+ strncpy(l_pinerc, buf2, sizeof(l_pinerc)-1);
+ l_pinerc[sizeof(l_pinerc)-1] = '\0';
+ }
+ else{
+#ifdef _WINDOWS
+ mprint(2, (db, DSIZE-(db-(*debug_out)), " no, checking in registry\n"));
+ if(mswin_reg(MSWR_OP_GET, MSWR_PINE_RC,
+ buf2, sizeof(buf2))){
+ strncpy(buf, buf2, sizeof(buf)-1);
+ buf[sizeof(buf)-1] = '\0';
+ if(!IS_REMOTE(buf2)){
+ strncpy(l_pinerc, buf2, sizeof(l_pinerc)-1);
+ l_pinerc[sizeof(l_pinerc)-1] = '\0';
+ }
+ /*
+ * Now buf is the pinerc to be used, l_pinerc is
+ * the directory, which may be either same as buf
+ * or it may be $HOME\PINE if registry gives us
+ * a remote pinerc.
+ */
+ mprint(2, (db, DSIZE-(db-(*debug_out)), " found \"%.100s\" in registry\n",
+ buf));
+ }
+ else{
+ nopinerc = 1;
+ mprint(2, (db, DSIZE-(db-(*debug_out)), " not found, asking user\n"));
+ }
+#else
+ mprint(2, (db, DSIZE-(db-(*debug_out)), " not found\n"));
+#endif
+ }
+ }
+
+ /*
+ * Buf is the pinerc (could be remote if from registry)
+ * and l_pinerc is the local pinerc, which may not exist.
+ */
+ }
+ else{ /* $HOME\PINE not a writable dir */
+ /*
+ * We notice that the order of checking in the registry
+ * and checking in the ALPINE.EXE directory are different
+ * in this case versus the is_writable_dir(buf2) case, and
+ * that does sort of look like a bug. However,
+ * we don't think this is a bug since we did it on purpose
+ * a long time ago. So even though we can't remember why
+ * it is this way, we think we would rediscover why if we
+ * changed it! So we won't change it.
+ */
+
+ /*
+ * Change the default to use to the ALPINE.EXE directory.
+ */
+ build_path(buf, ps->pine_dir, SYSTEM_PINERC, sizeof(buf));
+ strncpy(l_pinerc, buf, sizeof(l_pinerc)-1);
+ l_pinerc[sizeof(l_pinerc)-1] = '\0';
+#ifdef _WINDOWS
+ mprint(2, (db, DSIZE-(db-(*debug_out)), " no, not writable, checking in registry\n"));
+ /* if in registry, use that value */
+ if(mswin_reg(MSWR_OP_GET, MSWR_PINE_RC, buf2, sizeof(buf2))){
+ strncpy(buf, buf2, sizeof(buf)-1);
+ buf[sizeof(buf)-1] = '\0';
+ mprint(2, (db, DSIZE-(db-(*debug_out)), " found \"%.100s\" in registry\n",
+ buf));
+ if(!IS_REMOTE(buf)){
+ strncpy(l_pinerc, buf, sizeof(l_pinerc)-1);
+ l_pinerc[sizeof(l_pinerc)-1] = '\0';
+ }
+ }
+ else{
+ mprint(2, (db, DSIZE-(db-(*debug_out)),
+ " no, checking for \"%.100s\" in alpine.exe dir\n",
+ buf));
+
+ if(can_access(buf, ACCESS_EXISTS) == 0){
+ mprint(2, (db, DSIZE-(db-(*debug_out)), " found it\n"));
+ }
+ else{
+ nopinerc = 1;
+ mprint(2, (db, DSIZE-(db-(*debug_out)), " not found, asking user\n"));
+ }
+ }
+#else
+ mprint(2, (db, DSIZE-(db-(*debug_out)),
+ " no, checking for \"%.100s\" in alpine.exe dir\n",
+ buf));
+
+ if(can_access(buf, ACCESS_EXISTS) == 0){
+ mprint(2, (db, DSIZE-(db-(*debug_out)), " found it\n"));
+ }
+ else{
+ mprint(2, (db, DSIZE-(db-(*debug_out)), " not found, creating it\n"));
+ }
+#endif
+ }
+
+ /*
+ * When we get here we have buf set to the name of the
+ * pinerc, which could be local or remote. We have l_pinerc
+ * set to the same as buf if buf is local, and set to another
+ * name otherwise, hopefully contained in a writable directory.
+ */
+#ifdef _WINDOWS
+ if(nopinerc || ps_global->install_flag){
+ char buf3[MAXPATH+1];
+
+ confregset = 0;
+ strncpy(buf3, buf, MAXPATH);
+ buf3[MAXPATH] = '\0';
+ if(os_config_dialog(buf3, MAXPATH,
+ &confregset, nopinerc) == 0){
+ strncpy(buf, buf3, MAXPATH);
+ buf[MAXPATH] = '\0';
+ mprint(2, (db, DSIZE-(db-(*debug_out)), " not found, creating it\n"));
+ mprint(2, (db, DSIZE-(db-(*debug_out)), " user says use \"%.100s\"\n", buf));
+ if(!IS_REMOTE(buf)){
+ strncpy(l_pinerc, buf, MAXPATH);
+ l_pinerc[MAXPATH] = '\0';
+ }
+ }
+ else{
+ exit(-1);
+ }
+ }
+#endif
+ ps->prc = new_pinerc_s(buf);
+ }
+
+ ps->pinerc = cpystr(l_pinerc);
+ }
+
+#if defined(DOS) || defined(OS2)
+ /*
+ * The goal here is to set the auxiliary directory in the pinerc variable.
+ * We are making the assumption that any reference to the pinerc variable
+ * after this point is used only as a directory in which to store things,
+ * with the prc variable being the preferred place to store pinerc location.
+ * If -aux isn't set, then there is no change. -jpf 08/2001
+ */
+ if(ps->aux_files_dir){
+ l_pinerc[0] = '\0';
+ build_path(l_pinerc, ps->aux_files_dir, SYSTEM_PINERC,
+ sizeof(l_pinerc));
+ if(ps->pinerc) fs_give((void **)&ps->pinerc);
+ ps->pinerc = cpystr(l_pinerc);
+ mprint(2, (db, DSIZE-(db-(*debug_out)), "Setting aux_files_dir to \"%.100s\"\n",
+ ps->aux_files_dir));
+ }
+#endif
+
+#ifdef _WINDOWS
+ if(confregset && (ps->update_registry != UREG_NEVER_SET))
+ mswin_reg(MSWR_OP_SET | ((ps->update_registry == UREG_ALWAYS_SET)
+ || confregset == 1 ? MSWR_OP_FORCE : 0),
+ MSWR_PINE_RC,
+ (ps->prc && ps->prc->name) ?
+ ps->prc->name : ps->pinerc, (size_t)NULL);
+#endif
+
+ /*
+ * Now that we know the default for the PINERC, build NEWSRC default.
+ * Backward compatibility makes this kind of funky. If what the
+ * c-client thinks the NEWSRC should be exists *AND* it doesn't
+ * already exist in the PINERC's dir, use c-client's default, otherwise
+ * use the one next to the PINERC...
+ */
+ p = last_cmpnt(ps->pinerc);
+ buf[0] = '\0';
+ if(p != NULL){
+ strncpy(buf, ps->pinerc, MIN(p - ps->pinerc, sizeof(buf)-1));
+ buf[MIN(p - ps->pinerc, sizeof(buf)-1)] = '\0';
+ }
+
+ mprint(2, (db, DSIZE-(db-(*debug_out)), "Using directory \"%.100s\" for auxiliary files\n", buf));
+ strncat(buf, "NEWSRC", sizeof(buf)-1-strlen(buf));
+
+ if(!(p = (void *) mail_parameters(NULL, GET_NEWSRC, (void *)NULL))
+ || can_access(p, ACCESS_EXISTS) < 0
+ || can_access(buf, ACCESS_EXISTS) == 0){
+ mail_parameters(NULL, SET_NEWSRC, (void *)buf);
+ GLO_NEWSRC_PATH = cpystr(buf);
+ }
+ else
+ GLO_NEWSRC_PATH = cpystr(p);
+
+ if(ps->pconf){
+ mprint(2, (db, DSIZE-(db-(*debug_out)), "Global config \"%.100s\" comes from command line\n",
+ (ps->pconf && ps->pconf->name) ? ps->pconf->name : "<no name>"));
+ }
+ else{
+ mprint(2, (db, DSIZE-(db-(*debug_out)),
+ "Global config not set on cmdline, checking for $PINECONF\n"));
+ }
+
+ if(!ps->pconf && (p = getenv("PINECONF"))){
+ ps->pconf = new_pinerc_s(p);
+ if(ps->pconf){
+ mprint(2, (db, DSIZE-(db-(*debug_out)),
+ " yes, global config \"%.100s\" comes from $PINECONF\n",
+ (ps->pconf && ps->pconf->name) ? ps->pconf->name : "<no name>"));
+ }
+ }
+#ifdef _WINDOWS
+ else if(!ps->pconf
+ && mswin_reg(MSWR_OP_GET, MSWR_PINE_CONF, buf2, sizeof(buf2))){
+ ps->pconf = new_pinerc_s(buf2);
+ if(ps->pconf){
+ mprint(2, (db, DSIZE-(db-(*debug_out)),
+ " yes, global config \"%.100s\" comes from Registry\n",
+ (ps->pconf && ps->pconf->name) ? ps->pconf->name : "<no name>"));
+ }
+ }
+#endif
+ if(!ps->pconf){
+ mprint(2, (db, DSIZE-(db-(*debug_out)), " no, there is no global config\n"));
+ }
+#ifdef _WINDOWS
+ else if (ps->pconf && ps->pconf->name &&
+ (ps->update_registry != UREG_NEVER_SET)){
+ mswin_reg(MSWR_OP_SET | ((ps->update_registry == UREG_ALWAYS_SET)
+ ? MSWR_OP_FORCE : 0),
+ MSWR_PINE_CONF,
+ ps->pconf->name, (size_t)NULL);
+ }
+#endif
+
+ if(!ps->prc)
+ ps->prc = new_pinerc_s(ps->pinerc);
+
+ if(ps->exceptions){
+ mprint(2, (db, DSIZE-(db-(*debug_out)),
+ "Exceptions config \"%.100s\" comes from command line\n",
+ ps->exceptions));
+ }
+ else{
+ mprint(2, (db, DSIZE-(db-(*debug_out)),
+ "Exceptions config not set on cmdline, checking for $PINERCEX\n"));
+ }
+
+ /*
+ * Exceptions is done slightly differently from pinerc. Instead of setting
+ * post_prc in args.c we just set the string and use it here. We do
+ * that so that we can put it in the same directory as the pinerc if
+ * exceptions is a relative name, and pinerc may not be set until here.
+ *
+ * First, just like for pinerc, check environment variable if it wasn't
+ * set on the command line.
+ */
+ if(!ps->exceptions && (p = getenv("PINERCEX")) && *p){
+ ps->exceptions = cpystr(p);
+ if(ps->exceptions){
+ mprint(2, (db, DSIZE-(db-(*debug_out)),
+ " yes, exceptions config \"%.100s\" comes from $PINERCEX\n",
+ ps->exceptions));
+ }
+ }
+
+ /*
+ * If still not set, try specific file in same dir as pinerc.
+ * Only use it if the file exists.
+ */
+ if(!ps->exceptions){
+ p = last_cmpnt(ps->pinerc);
+ buf[0] = '\0';
+ if(p != NULL){
+ strncpy(buf, ps->pinerc, MIN(p - ps->pinerc, sizeof(buf)-1));
+ buf[MIN(p - ps->pinerc, sizeof(buf)-1)] = '\0';
+ }
+
+ strncat(buf, "PINERCEX", sizeof(buf)-1-strlen(buf));
+
+ mprint(2, (db, DSIZE-(db-(*debug_out)),
+ " no, checking for default \"%.100s\" in pinerc dir\n", buf));
+ if(can_access(buf, ACCESS_EXISTS) == 0) /* found it! */
+ ps->exceptions = cpystr(buf);
+
+ if(ps->exceptions){
+ mprint(2, (db, DSIZE-(db-(*debug_out)),
+ " yes, exceptions config \"%.100s\" comes from default\n",
+ ps->exceptions));
+ }
+ else{
+ mprint(2, (db, DSIZE-(db-(*debug_out)), " no, there is no exceptions config\n"));
+ }
+ }
+
+#else /* unix */
+
+ if(ps->pconf){
+ mprint(2, (db, DSIZE-(db-(*debug_out)), "Global config \"%.100s\" comes from command line\n",
+ (ps->pconf && ps->pconf->name) ? ps->pconf->name : "<no name>"));
+ }
+
+ if(!ps->pconf){
+ ps->pconf = new_pinerc_s(SYSTEM_PINERC);
+ if(ps->pconf){
+ mprint(2, (db, DSIZE-(db-(*debug_out)), "Global config \"%.100s\" is default\n",
+ (ps->pconf && ps->pconf->name) ? ps->pconf->name : "<no name>"));
+ }
+ }
+
+ if(!ps->pconf){
+ mprint(2, (db, DSIZE-(db-(*debug_out)), "No global config!\n"));
+ }
+
+ if(ps->prc){
+ mprint(2, (db, DSIZE-(db-(*debug_out)), "Personal config \"%.100s\" comes from command line\n",
+ (ps->prc && ps->prc->name) ? ps->prc->name : "<no name>"));
+ }
+
+ if(!ps->pinerc){
+ build_path(buf, ps->home_dir, ".pinerc", sizeof(buf));
+ ps->pinerc = cpystr(buf);
+ }
+
+ if(!ps->prc){
+ ps->prc = new_pinerc_s(ps->pinerc);
+ if(ps->prc){
+ mprint(2, (db, DSIZE-(db-(*debug_out)), "Personal config \"%.100s\" is default\n",
+ (ps->prc && ps->prc->name) ? ps->prc->name : "<no name>"));
+ }
+ }
+
+ if(!ps->prc){
+ mprint(2, (db, DSIZE-(db-(*debug_out)), "No personal config!\n"));
+ }
+
+ if(ps->exceptions){
+ mprint(2, (db, DSIZE-(db-(*debug_out)),
+ "Exceptions config \"%.100s\" comes from command line\n",
+ ps->exceptions));
+ }
+
+ /*
+ * If not set, try specific file in same dir as pinerc.
+ * Only use it if the file exists.
+ */
+ if(!ps->exceptions){
+ p = last_cmpnt(ps->pinerc);
+ buf[0] = '\0';
+ if(p != NULL){
+ strncpy(buf, ps->pinerc, MIN(p - ps->pinerc, sizeof(buf)-1));
+ buf[MIN(p - ps->pinerc, sizeof(buf)-1)] = '\0';
+ }
+
+ strncat(buf, ".pinercex", sizeof(buf)-1-strlen(buf));
+ mprint(2, (db, DSIZE-(db-(*debug_out)), "Exceptions config not set on cmdline\n checking for default \"%.100s\" in pinerc dir\n", buf));
+
+ if(can_access(buf, ACCESS_EXISTS) == 0) /* found it! */
+ ps->exceptions = cpystr(buf);
+
+ if(ps->exceptions){
+ mprint(2, (db, DSIZE-(db-(*debug_out)),
+ " yes, exceptions config \"%.100s\" is default\n",
+ ps->exceptions));
+ }
+ else{
+ mprint(2, (db, DSIZE-(db-(*debug_out)), " no, there is no exceptions config\n"));
+ }
+ }
+
+#endif /* unix */
+
+ if(ps->exceptions){
+
+ if(!IS_REMOTE(ps->exceptions) &&
+ !is_absolute_path(ps->exceptions)){
+#if defined(DOS) || defined(OS2)
+ p = last_cmpnt(ps->pinerc);
+ buf[0] = '\0';
+ if(p != NULL){
+ strncpy(buf, ps->pinerc, MIN(p - ps->pinerc, sizeof(buf)-1));
+ buf[MIN(p - ps->pinerc, sizeof(buf)-1)] = '\0';
+ }
+
+ strncat(buf, ps->exceptions, sizeof(buf)-1-strlen(buf));
+#else
+ build_path(buf, ps->home_dir, ps->exceptions, sizeof(buf));
+#endif
+ }
+ else{
+ strncpy(buf, ps->exceptions, sizeof(buf)-1);
+ buf[sizeof(buf)-1] = '\0';
+ }
+
+ ps->post_prc = new_pinerc_s(buf);
+
+ fs_give((void **)&ps->exceptions);
+ }
+
+ mprint(2, (db, DSIZE-(db-(*debug_out)), "\n Global config: %.100s\n",
+ (ps->pconf && ps->pconf->name) ? ps->pconf->name : "<none>"));
+ mprint(2, (db, DSIZE-(db-(*debug_out)), " Personal config: %.100s\n",
+ (ps->prc && ps->prc->name) ? ps->prc->name : "<none>"));
+ mprint(2, (db, DSIZE-(db-(*debug_out)), " Exceptions config: %.100s\n",
+ (ps->post_prc && ps->post_prc->name) ? ps->post_prc->name
+ : "<none>"));
+#if !defined(DOS) && !defined(OS2)
+ if(SYSTEM_PINERC_FIXED){
+ mprint(2, (db, DSIZE-(db-(*debug_out)), " Fixed config: %.100s\n", SYSTEM_PINERC_FIXED));
+ }
+#endif
+
+ mprint(2, (db, DSIZE-(db-(*debug_out)), "\n"));
+}
+
+
+/*----------------------------------------------------------------------
+ Initialize the variables
+
+ Args: ps -- The usual pine structure
+
+ Result:
+
+ This reads the system pine configuration file and the user's pine
+configuration file ".pinerc" and places the results in the variables
+structure. It sorts out what was read and sets a few other variables
+based on the contents.
+ ----*/
+void
+init_vars(struct pine *ps, void (*cmds_f) (struct pine *, char **))
+{
+ char buf[MAXPATH+1], *p, *q, **s;
+ register struct variable *vars = ps->vars;
+ int obs_header_in_reply = 0, /* the obs_ variables are to */
+ obs_old_style_reply = 0, /* support backwards compatibility */
+ obs_save_by_sender, i, def_sort_rev;
+ long rvl;
+ PINERC_S *fixedprc = NULL;
+ FeatureLevel obs_feature_level;
+ char *fromcharset = NULL;
+ char *err = NULL;
+
+ dprint((5, "init_vars:\n"));
+
+ /*--- The defaults here are defined in os-xxx.h so they can vary
+ per machine ---*/
+
+ GLO_PRINTER = cpystr(DF_DEFAULT_PRINTER);
+ GLO_ELM_STYLE_SAVE = cpystr(DF_ELM_STYLE_SAVE);
+ GLO_SAVE_BY_SENDER = cpystr(DF_SAVE_BY_SENDER);
+ GLO_HEADER_IN_REPLY = cpystr(DF_HEADER_IN_REPLY);
+ GLO_INBOX_PATH = cpystr("inbox");
+ GLO_DEFAULT_FCC = cpystr(DF_DEFAULT_FCC);
+ GLO_DEFAULT_SAVE_FOLDER = cpystr(DEFAULT_SAVE);
+ GLO_POSTPONED_FOLDER = cpystr(POSTPONED_MSGS);
+ GLO_TRASH_FOLDER = cpystr(TRASH_FOLDER);
+ GLO_USE_ONLY_DOMAIN_NAME = cpystr(DF_USE_ONLY_DOMAIN_NAME);
+ GLO_FEATURE_LEVEL = cpystr("sappling");
+ GLO_OLD_STYLE_REPLY = cpystr(DF_OLD_STYLE_REPLY);
+ GLO_SORT_KEY = cpystr(DF_SORT_KEY);
+ GLO_SAVED_MSG_NAME_RULE = cpystr(DF_SAVED_MSG_NAME_RULE);
+ GLO_FCC_RULE = cpystr(DF_FCC_RULE);
+ GLO_AB_SORT_RULE = cpystr(DF_AB_SORT_RULE);
+ GLO_FLD_SORT_RULE = cpystr(DF_FLD_SORT_RULE);
+ GLO_SIGNATURE_FILE = cpystr(DF_SIGNATURE_FILE);
+ GLO_MAIL_DIRECTORY = cpystr(DF_MAIL_DIRECTORY);
+ GLO_REMOTE_ABOOK_HISTORY = cpystr(DF_REMOTE_ABOOK_HISTORY);
+ GLO_REMOTE_ABOOK_VALIDITY = cpystr(DF_REMOTE_ABOOK_VALIDITY);
+ GLO_GOTO_DEFAULT_RULE = cpystr(DF_GOTO_DEFAULT_RULE);
+ GLO_INCOMING_STARTUP = cpystr(DF_INCOMING_STARTUP);
+ GLO_PRUNING_RULE = cpystr(DF_PRUNING_RULE);
+ GLO_REOPEN_RULE = cpystr(DF_REOPEN_RULE);
+ GLO_THREAD_DISP_STYLE = cpystr(DF_THREAD_DISP_STYLE);
+ GLO_THREAD_INDEX_STYLE = cpystr(DF_THREAD_INDEX_STYLE);
+ GLO_THREAD_MORE_CHAR = cpystr(DF_THREAD_MORE_CHAR);
+ GLO_THREAD_EXP_CHAR = cpystr(DF_THREAD_EXP_CHAR);
+ GLO_THREAD_LASTREPLY_CHAR = cpystr(DF_THREAD_LASTREPLY_CHAR);
+ GLO_BUGS_FULLNAME = cpystr("Sorry No Address");
+ GLO_BUGS_ADDRESS = cpystr("nobody");
+ GLO_SUGGEST_FULLNAME = cpystr("Sorry No Address");
+ GLO_SUGGEST_ADDRESS = cpystr("nobody");
+ GLO_LOCAL_FULLNAME = cpystr(DF_LOCAL_FULLNAME);
+ GLO_LOCAL_ADDRESS = cpystr(DF_LOCAL_ADDRESS);
+ GLO_OVERLAP = cpystr(DF_OVERLAP);
+ GLO_MAXREMSTREAM = cpystr(DF_MAXREMSTREAM);
+ GLO_MARGIN = cpystr(DF_MARGIN);
+ GLO_FILLCOL = cpystr(DF_FILLCOL);
+ GLO_DEADLETS = cpystr(DF_DEADLETS);
+ GLO_NMW_WIDTH = cpystr(DF_NMW_WIDTH);
+ GLO_REPLY_STRING = cpystr("> ");
+ GLO_REPLY_INTRO = cpystr(DEFAULT_REPLY_INTRO);
+ GLO_EMPTY_HDR_MSG = cpystr("undisclosed-recipients");
+ GLO_STATUS_MSG_DELAY = cpystr("0");
+ GLO_ACTIVE_MSG_INTERVAL = cpystr("12");
+ GLO_USERINPUTTIMEO = cpystr("0");
+ GLO_INCCHECKTIMEO = cpystr("5");
+ GLO_INCCHECKINTERVAL = cpystr("180");
+ GLO_INC2NDCHECKINTERVAL = cpystr("180");
+ GLO_MAILCHECK = cpystr(DF_MAILCHECK);
+ GLO_MAILCHECKNONCURR = cpystr("0");
+ GLO_MAILDROPCHECK = cpystr(DF_MAILDROPCHECK);
+ GLO_NNTPRANGE = cpystr("0");
+ GLO_KBLOCK_PASSWD_COUNT = cpystr(DF_KBLOCK_PASSWD_COUNT);
+ GLO_INDEX_COLOR_STYLE = cpystr("flip-colors");
+ GLO_TITLEBAR_COLOR_STYLE = cpystr("default");
+ GLO_POST_CHAR_SET = cpystr("UTF-8");
+#ifdef DF_FOLDER_EXTENSION
+ GLO_FOLDER_EXTENSION = cpystr(DF_FOLDER_EXTENSION);
+#endif
+#ifdef DF_SMTP_SERVER
+ GLO_SMTP_SERVER = parse_list(DF_SMTP_SERVER, 1,
+ PL_REMSURRQUOT, NULL);
+#endif
+
+#ifdef DF_SSHPATH
+ GLO_SSHPATH = cpystr(DF_SSHPATH);
+#endif
+#ifdef DF_SSHCMD
+ GLO_SSHCMD = cpystr(DF_SSHCMD);
+#endif
+
+#ifndef _WINDOWS
+ GLO_COLOR_STYLE = cpystr("no-color");
+ GLO_NORM_FORE_COLOR = cpystr(DEFAULT_NORM_FORE_RGB);
+ GLO_NORM_BACK_COLOR = cpystr(DEFAULT_NORM_BACK_RGB);
+#endif
+ GLO_TITLE_FORE_COLOR = cpystr(DEFAULT_TITLE_FORE_RGB);
+ GLO_TITLE_BACK_COLOR = cpystr(DEFAULT_TITLE_BACK_RGB);
+ GLO_TITLECLOSED_FORE_COLOR = cpystr(DEFAULT_TITLECLOSED_FORE_RGB);
+ GLO_TITLECLOSED_BACK_COLOR = cpystr(DEFAULT_TITLECLOSED_BACK_RGB);
+ GLO_METAMSG_FORE_COLOR = cpystr(DEFAULT_METAMSG_FORE_RGB);
+ GLO_METAMSG_BACK_COLOR = cpystr(DEFAULT_METAMSG_BACK_RGB);
+ GLO_QUOTE1_FORE_COLOR = cpystr(DEFAULT_QUOTE1_FORE_RGB);
+ GLO_QUOTE1_BACK_COLOR = cpystr(DEFAULT_QUOTE1_BACK_RGB);
+ GLO_QUOTE2_FORE_COLOR = cpystr(DEFAULT_QUOTE2_FORE_RGB);
+ GLO_QUOTE2_BACK_COLOR = cpystr(DEFAULT_QUOTE2_BACK_RGB);
+ GLO_QUOTE3_FORE_COLOR = cpystr(DEFAULT_QUOTE3_FORE_RGB);
+ GLO_QUOTE3_BACK_COLOR = cpystr(DEFAULT_QUOTE3_BACK_RGB);
+ GLO_SIGNATURE_FORE_COLOR = cpystr(DEFAULT_SIGNATURE_FORE_RGB);
+ GLO_SIGNATURE_BACK_COLOR = cpystr(DEFAULT_SIGNATURE_BACK_RGB);
+ GLO_IND_PLUS_FORE_COLOR = cpystr(DEFAULT_IND_PLUS_FORE_RGB);
+ GLO_IND_PLUS_BACK_COLOR = cpystr(DEFAULT_IND_PLUS_BACK_RGB);
+ GLO_IND_IMP_FORE_COLOR = cpystr(DEFAULT_IND_IMP_FORE_RGB);
+ GLO_IND_IMP_BACK_COLOR = cpystr(DEFAULT_IND_IMP_BACK_RGB);
+ GLO_IND_ANS_FORE_COLOR = cpystr(DEFAULT_IND_ANS_FORE_RGB);
+ GLO_IND_ANS_BACK_COLOR = cpystr(DEFAULT_IND_ANS_BACK_RGB);
+ GLO_IND_NEW_FORE_COLOR = cpystr(DEFAULT_IND_NEW_FORE_RGB);
+ GLO_IND_NEW_BACK_COLOR = cpystr(DEFAULT_IND_NEW_BACK_RGB);
+ GLO_IND_OP_FORE_COLOR = cpystr(DEFAULT_IND_OP_FORE_RGB);
+ GLO_IND_OP_BACK_COLOR = cpystr(DEFAULT_IND_OP_BACK_RGB);
+ GLO_VIEW_MARGIN_LEFT = cpystr("0");
+ GLO_VIEW_MARGIN_RIGHT = cpystr(DF_VIEW_MARGIN_RIGHT);
+ GLO_QUOTE_SUPPRESSION = cpystr(DF_QUOTE_SUPPRESSION);
+ GLO_KW_BRACES = cpystr("\"{\" \"} \"");
+ GLO_OPENING_SEP = cpystr(" - ");
+ GLO_WP_INDEXHEIGHT = cpystr("24");
+ GLO_WP_AGGSTATE = cpystr("1");
+ GLO_WP_STATE = cpystr("");
+#ifdef DF_VAR_SPELLER
+ GLO_SPELLER = cpystr(DF_VAR_SPELLER);
+#endif
+#ifdef SMIME
+ GLO_PUBLICCERT_DIR = cpystr(DF_PUBLICCERT_DIR);
+ GLO_PRIVATEKEY_DIR = cpystr(DF_PRIVATEKEY_DIR);
+ GLO_CACERT_DIR = cpystr(DF_CACERT_DIR);
+#endif /* SMIME */
+
+ /*
+ * Default first value for addrbook list if none set.
+ * We also want to be sure to set global_val to the default
+ * if is_fixed, so that address-book= will cause the default to happen.
+ */
+ if(!GLO_ADDRESSBOOK && !FIX_ADDRESSBOOK)
+ GLO_ADDRESSBOOK = parse_list(DF_ADDRESSBOOK, 1, 0, NULL);
+
+ /*
+ * Default first value if none set.
+ */
+ if(!GLO_STANDARD_PRINTER && !FIX_STANDARD_PRINTER)
+ GLO_STANDARD_PRINTER = parse_list(DF_STANDARD_PRINTER, 1, 0, NULL);
+
+/*
+ * Defining this default sshpath should cause ssh to be preferred over rsh
+ * when attempting imapd preauth calls.
+ */
+#ifdef DF_SSHPATH
+ if(DF_SSHPATH
+ && is_absolute_path(DF_SSHPATH)
+ && can_access(DF_SSHPATH, EXECUTE_ACCESS) == 0){
+ mail_parameters(NULL, SET_SSHPATH, (void *) DF_SSHPATH);
+ }
+#endif
+/*
+ * It isn't usually necessary to define this.
+ */
+#ifdef DF_SSHCMD
+ if(DF_SSHCMD){
+ mail_parameters(NULL, SET_SSHCOMMAND, (void *) DF_SSHCMD);
+ }
+#endif
+
+#if !defined(DOS) && !defined(OS2)
+ /*
+ * This is here instead of in init_pinerc so that we can get by without
+ * having a global fixedprc, since we don't need it anymore after this.
+ */
+ fixedprc = new_pinerc_s(SYSTEM_PINERC_FIXED);
+#endif
+
+ if(ps->pconf){
+ read_pinerc(ps->pconf, vars, ParseGlobal);
+ if(ps->pconf->type != Loc)
+ rd_close_remote(ps->pconf->rd);
+ }
+
+ if(ps->prc){
+ read_pinerc(ps->prc, vars, ParsePers);
+ if(ps->prc->type != Loc)
+ rd_close_remote(ps->prc->rd);
+ }
+
+ if(ps->post_prc){
+ read_pinerc(ps->post_prc, vars, ParsePersPost);
+ if(ps->post_prc->type != Loc)
+ rd_close_remote(ps->post_prc->rd);
+ }
+
+ if(fixedprc){
+ read_pinerc(fixedprc, vars, ParseFixed);
+ free_pinerc_s(&fixedprc);
+ }
+
+ ps->ew_for_except_vars = ps->post_prc ? Post : Main;
+
+ if(ps->exit_if_no_pinerc && ps->first_time_user){
+
+ /* TRANSLATORS: -bail is a literal option name, don't change it. */
+ exceptional_exit(_("Exiting because -bail option is set and config file doesn't exist."), -1);
+ }
+
+ /*
+ * Convert everything having to do with the config to UTF-8
+ * in order to avoid having to worry about it all over the
+ * place.
+ * Set the character-set first so that we may use that in
+ * the conversion process.
+ */
+ set_collation(0, 1);
+
+#ifndef _WINDOWS
+#if (HAVE_LANGINFO_H && defined(CODESET))
+
+ if(output_charset_is_supported(nl_langinfo_codeset_wrapper()))
+ ps->GLO_CHAR_SET = cpystr(nl_langinfo_codeset_wrapper());
+ else{
+ ps->GLO_CHAR_SET = cpystr("UTF-8");
+ dprint((1,"nl_langinfo(CODESET) returns unrecognized value=\"%s\", using UTF-8 as default\n", (p=nl_langinfo(CODESET)) ? p : ""));
+ }
+#else
+ ps->GLO_CHAR_SET = cpystr("UTF-8");
+#endif
+
+ set_current_val(&vars[V_CHAR_SET], TRUE, TRUE);
+ set_current_val(&vars[V_OLD_CHAR_SET], TRUE, TRUE);
+ set_current_val(&vars[V_KEY_CHAR_SET], TRUE, TRUE);
+#endif /* ! _WINDOWS */
+
+ set_current_val(&vars[V_POST_CHAR_SET], TRUE, TRUE);
+
+ /*
+ * Also set up the feature list because we need the
+ * Use-System-Translation feature to set up the charmaps.
+ */
+
+ /* way obsolete, backwards compatibility */
+ set_current_val(&vars[V_FEATURE_LEVEL], TRUE, TRUE);
+ if(strucmp(VAR_FEATURE_LEVEL, "seedling") == 0)
+ obs_feature_level = Seedling;
+ else if(strucmp(VAR_FEATURE_LEVEL, "old-growth") == 0)
+ obs_feature_level = Seasoned;
+ else
+ obs_feature_level = Sapling;
+
+ /* obsolete, backwards compatibility */
+ set_current_val(&vars[V_OLD_STYLE_REPLY], TRUE, TRUE);
+ obs_old_style_reply = !strucmp(VAR_OLD_STYLE_REPLY, "yes");
+
+ set_feature_list_current_val(&vars[V_FEATURE_LIST]);
+ process_feature_list(ps, VAR_FEATURE_LIST,
+ (obs_feature_level == Seasoned) ? 1 : 0,
+ obs_header_in_reply, obs_old_style_reply);
+
+
+ /*
+ * Redo set_collation call with correct value for collation,
+ * but we're hardwiring ctype on now. That's because nl_langinfo()
+ * call needs it and system-dependent wcwidth and wcrtomb functions
+ * need it.
+ */
+ set_collation(F_OFF(F_DISABLE_SETLOCALE_COLLATE, ps_global), 1);
+
+ /*
+ * Set up to send the correct sequence of bytes to the display terminal.
+ */
+
+ if(reset_character_set_stuff(&err) == -1)
+ panic(err ? err : "trouble with character set setup");
+ else if(err){
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, err);
+ fs_give((void **) &err);
+ }
+
+ /*
+ * Now we use the configvars from above to convert the rest
+ * to UTF-8. That should be ok because the ones above should
+ * be ASCII.
+ */
+ if(ps->keyboard_charmap && strucmp(ps->keyboard_charmap, "UTF-8")
+ && strucmp(ps->keyboard_charmap, "US-ASCII"))
+ fromcharset = ps->keyboard_charmap;
+ else if(ps->display_charmap && strucmp(ps->display_charmap, "UTF-8")
+ && strucmp(ps->display_charmap, "US-ASCII"))
+ fromcharset = ps->display_charmap;
+#ifndef _WINDOWS
+ else if(VAR_OLD_CHAR_SET && strucmp(VAR_OLD_CHAR_SET, "UTF-8")
+ && strucmp(VAR_OLD_CHAR_SET, "US-ASCII"))
+ fromcharset = VAR_OLD_CHAR_SET;
+#endif /* ! _WINDOWS */
+
+ convert_configvars_to_utf8(vars, fromcharset);
+
+ /*
+ * If we already set this while reading the remote pinerc, don't
+ * change it.
+ */
+ if(!VAR_REMOTE_ABOOK_METADATA || !VAR_REMOTE_ABOOK_METADATA[0])
+ set_current_val(&vars[V_REMOTE_ABOOK_METADATA], TRUE, TRUE);
+
+ /*
+ * mail-directory variable is obsolete, put its value in
+ * default folder-collection list
+ */
+ set_current_val(&vars[V_MAIL_DIRECTORY], TRUE, TRUE);
+ if(!GLO_FOLDER_SPEC){
+ build_path(tmp_20k_buf, VAR_MAIL_DIRECTORY, "[]", SIZEOF_20KBUF);
+ GLO_FOLDER_SPEC = parse_list(tmp_20k_buf, 1, 0, NULL);
+ }
+
+ set_current_val(&vars[V_FOLDER_SPEC], TRUE, TRUE);
+
+ set_current_val(&vars[V_NNTP_SERVER], TRUE, TRUE);
+ for(i = 0; VAR_NNTP_SERVER && VAR_NNTP_SERVER[i]; i++)
+ removing_quotes(VAR_NNTP_SERVER[i]);
+
+ set_news_spec_current_val(TRUE, TRUE);
+
+ set_current_val(&vars[V_INBOX_PATH], TRUE, TRUE);
+
+ set_current_val(&vars[V_USER_DOMAIN], TRUE, TRUE);
+ if(VAR_USER_DOMAIN
+ && VAR_USER_DOMAIN[0]
+ && (p = strrindex(VAR_USER_DOMAIN, '@'))){
+ if(*(++p)){
+ char *q;
+
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,
+ "User-domain (%s) cannot contain \"@\", using \"%s\"",
+ VAR_USER_DOMAIN, p);
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ q = VAR_USER_DOMAIN;
+ while((*q++ = *p++) != '\0')
+ ;/* do nothing */
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,
+ "User-domain (%s) cannot contain \"@\", deleting",
+ VAR_USER_DOMAIN);
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ if(ps->vars[V_USER_DOMAIN].post_user_val.p){
+ fs_give((void **)&ps->vars[V_USER_DOMAIN].post_user_val.p);
+ set_current_val(&vars[V_USER_DOMAIN], TRUE, TRUE);
+ }
+
+ if(VAR_USER_DOMAIN
+ && VAR_USER_DOMAIN[0]
+ && (p = strrindex(VAR_USER_DOMAIN, '@'))){
+ if(ps->vars[V_USER_DOMAIN].main_user_val.p){
+ fs_give((void **)&ps->vars[V_USER_DOMAIN].main_user_val.p);
+ set_current_val(&vars[V_USER_DOMAIN], TRUE, TRUE);
+ }
+ }
+ }
+ }
+
+ set_current_val(&vars[V_USE_ONLY_DOMAIN_NAME], TRUE, TRUE);
+ set_current_val(&vars[V_REPLY_STRING], TRUE, TRUE);
+ set_current_val(&vars[V_WORDSEPS], TRUE, TRUE);
+ set_current_val(&vars[V_QUOTE_REPLACE_STRING], TRUE, TRUE);
+ set_current_val(&vars[V_REPLY_INTRO], TRUE, TRUE);
+ set_current_val(&vars[V_EMPTY_HDR_MSG], TRUE, TRUE);
+
+#ifdef ENABLE_LDAP
+ set_current_val(&vars[V_LDAP_SERVERS], TRUE, TRUE);
+#endif /* ENABLE_LDAP */
+
+ /* obsolete, backwards compatibility */
+ set_current_val(&vars[V_HEADER_IN_REPLY], TRUE, TRUE);
+ obs_header_in_reply=!strucmp(VAR_HEADER_IN_REPLY, "yes");
+
+ set_current_val(&vars[V_PERSONAL_PRINT_COMMAND], TRUE, TRUE);
+ set_current_val(&vars[V_STANDARD_PRINTER], TRUE, TRUE);
+ set_current_val(&vars[V_PRINTER], TRUE, TRUE);
+ if(vars[V_PERSONAL_PRINT_COMMAND].is_fixed && !vars[V_PRINTER].is_fixed)
+ printer_value_check_and_adjust();
+
+ set_current_val(&vars[V_LAST_TIME_PRUNE_QUESTION], TRUE, TRUE);
+ if(VAR_LAST_TIME_PRUNE_QUESTION != NULL){
+ /* The month value in the file runs from 1-12, the variable here
+ runs from 0-11; the value in the file used to be 0-11, but we're
+ fixing it in January */
+ ps->last_expire_year = atoi(VAR_LAST_TIME_PRUNE_QUESTION);
+ ps->last_expire_month =
+ atoi(strindex(VAR_LAST_TIME_PRUNE_QUESTION, '.') + 1);
+ if(ps->last_expire_month == 0){
+ /* Fix for 0 because of old bug */
+ snprintf(buf, sizeof(buf), "%d.%d", ps_global->last_expire_year,
+ ps_global->last_expire_month + 1);
+ set_variable(V_LAST_TIME_PRUNE_QUESTION, buf, 1, 1, Main);
+ }else{
+ ps->last_expire_month--;
+ }
+ }else{
+ ps->last_expire_year = -1;
+ ps->last_expire_month = -1;
+ }
+
+ set_current_val(&vars[V_BUGS_FULLNAME], TRUE, TRUE);
+ set_current_val(&vars[V_BUGS_ADDRESS], TRUE, TRUE);
+ set_current_val(&vars[V_SUGGEST_FULLNAME], TRUE, TRUE);
+ set_current_val(&vars[V_SUGGEST_ADDRESS], TRUE, TRUE);
+ set_current_val(&vars[V_LOCAL_FULLNAME], TRUE, TRUE);
+ set_current_val(&vars[V_LOCAL_ADDRESS], TRUE, TRUE);
+ set_current_val(&vars[V_BUGS_EXTRAS], TRUE, TRUE);
+ set_current_val(&vars[V_KBLOCK_PASSWD_COUNT], TRUE, TRUE);
+ set_current_val(&vars[V_DEFAULT_FCC], TRUE, TRUE);
+ set_current_val(&vars[V_POSTPONED_FOLDER], TRUE, TRUE);
+ set_current_val(&vars[V_TRASH_FOLDER], TRUE, TRUE);
+ set_current_val(&vars[V_READ_MESSAGE_FOLDER], TRUE, TRUE);
+ set_current_val(&vars[V_FORM_FOLDER], TRUE, TRUE);
+ set_current_val(&vars[V_EDITOR], TRUE, TRUE);
+ set_current_val(&vars[V_SPELLER], TRUE, TRUE);
+ set_current_val(&vars[V_IMAGE_VIEWER], TRUE, TRUE);
+ set_current_val(&vars[V_BROWSER], TRUE, TRUE);
+ set_current_val(&vars[V_SMTP_SERVER], TRUE, TRUE);
+ set_current_val(&vars[V_COMP_HDRS], TRUE, TRUE);
+ set_current_val(&vars[V_CUSTOM_HDRS], TRUE, TRUE);
+ set_current_val(&vars[V_SENDMAIL_PATH], TRUE, TRUE);
+ set_current_val(&vars[V_DISPLAY_FILTERS], TRUE, TRUE);
+ set_current_val(&vars[V_SEND_FILTER], TRUE, TRUE);
+ set_current_val(&vars[V_ALT_ADDRS], TRUE, TRUE);
+ set_current_val(&vars[V_ABOOK_FORMATS], TRUE, TRUE);
+ set_current_val(&vars[V_KW_BRACES], TRUE, TRUE);
+ set_current_val(&vars[V_OPENING_SEP], TRUE, TRUE);
+ set_current_val(&vars[V_UNK_CHAR_SET], TRUE, TRUE);
+#ifdef SMIME
+ set_current_val(&vars[V_PUBLICCERT_DIR], TRUE, TRUE);
+ set_current_val(&vars[V_PUBLICCERT_CONTAINER], TRUE, TRUE);
+ set_current_val(&vars[V_PRIVATEKEY_DIR], TRUE, TRUE);
+ set_current_val(&vars[V_PRIVATEKEY_CONTAINER], TRUE, TRUE);
+ set_current_val(&vars[V_CACERT_DIR], TRUE, TRUE);
+ set_current_val(&vars[V_CACERT_CONTAINER], TRUE, TRUE);
+#endif /* SMIME */
+
+ set_current_val(&vars[V_KEYWORDS], TRUE, TRUE);
+ ps_global->keywords = init_keyword_list(VAR_KEYWORDS);
+
+ set_current_val(&vars[V_OPER_DIR], TRUE, TRUE);
+ if(VAR_OPER_DIR && !VAR_OPER_DIR[0]){
+ init_error(ps, SM_ORDER | SM_DING, 3, 5,
+ "Setting operating-dir to the empty string is not allowed. Will be ignored.");
+ fs_give((void **)&VAR_OPER_DIR);
+ if(FIX_OPER_DIR)
+ fs_give((void **)&FIX_OPER_DIR);
+ if(GLO_OPER_DIR)
+ fs_give((void **)&GLO_OPER_DIR);
+ if(COM_OPER_DIR)
+ fs_give((void **)&COM_OPER_DIR);
+ if(ps_global->vars[V_OPER_DIR].post_user_val.p)
+ fs_give((void **)&ps_global->vars[V_OPER_DIR].post_user_val.p);
+ if(ps_global->vars[V_OPER_DIR].main_user_val.p)
+ fs_give((void **)&ps_global->vars[V_OPER_DIR].main_user_val.p);
+ }
+
+ set_current_val(&vars[V_PERSONAL_PRINT_CATEGORY], TRUE, TRUE);
+ ps->printer_category = -1;
+ if(VAR_PERSONAL_PRINT_CATEGORY != NULL)
+ ps->printer_category = atoi(VAR_PERSONAL_PRINT_CATEGORY);
+
+ if(ps->printer_category < 1 || ps->printer_category > 3){
+ char **tt;
+ char aname[100], wname[100];
+
+ strncpy(aname, ANSI_PRINTER, sizeof(aname));
+ aname[sizeof(aname)-1] = '\0';
+ strncat(aname, "-no-formfeed", sizeof(aname)-strlen(aname)-1);
+ strncpy(wname, WYSE_PRINTER, sizeof(wname));
+ wname[sizeof(wname)-1] = '\0';
+ strncat(wname, "-no-formfeed", sizeof(wname)-strlen(wname)-1);
+ if(strucmp(VAR_PRINTER, ANSI_PRINTER) == 0
+ || strucmp(VAR_PRINTER, aname) == 0
+ || strucmp(VAR_PRINTER, WYSE_PRINTER) == 0
+ || strucmp(VAR_PRINTER, wname) == 0)
+ ps->printer_category = 1;
+ else if(VAR_STANDARD_PRINTER && VAR_STANDARD_PRINTER[0]){
+ for(tt = VAR_STANDARD_PRINTER; *tt; tt++)
+ if(strucmp(VAR_PRINTER, *tt) == 0)
+ break;
+
+ if(*tt)
+ ps->printer_category = 2;
+ }
+
+ /* didn't find it yet */
+ if(ps->printer_category < 1 || ps->printer_category > 3){
+ if(VAR_PERSONAL_PRINT_COMMAND && VAR_PERSONAL_PRINT_COMMAND[0]){
+ for(tt = VAR_PERSONAL_PRINT_COMMAND; *tt; tt++)
+ if(strucmp(VAR_PRINTER, *tt) == 0)
+ break;
+
+ if(*tt)
+ ps->printer_category = 3;
+ }
+ }
+ }
+
+ set_current_val(&vars[V_OVERLAP], TRUE, TRUE);
+ ps->viewer_overlap = i = atoi(DF_OVERLAP);
+ if(SVAR_OVERLAP(ps, i, tmp_20k_buf, SIZEOF_20KBUF))
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ else
+ ps->viewer_overlap = i;
+
+ set_current_val(&vars[V_MARGIN], TRUE, TRUE);
+ ps->scroll_margin = i = atoi(DF_MARGIN);
+ if(SVAR_MARGIN(ps, i, tmp_20k_buf, SIZEOF_20KBUF))
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ else
+ ps->scroll_margin = i;
+
+ set_current_val(&vars[V_FILLCOL], TRUE, TRUE);
+ ps->composer_fillcol = i = atoi(DF_FILLCOL);
+ if(SVAR_FILLCOL(ps, i, tmp_20k_buf, SIZEOF_20KBUF))
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ else
+ ps->composer_fillcol = i;
+
+ set_current_val(&vars[V_QUOTE_SUPPRESSION], TRUE, TRUE);
+ ps->quote_suppression_threshold = i = atoi(DF_QUOTE_SUPPRESSION);
+ if(SVAR_QUOTE_SUPPRESSION(ps, i, tmp_20k_buf, SIZEOF_20KBUF))
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ else{
+ if(i > 0 && i < Q_SUPP_LIMIT){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,
+ "Ignoring Quote-Suppression-Threshold value of %.50s, see help",
+ VAR_QUOTE_SUPPRESSION);
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ }
+ else{
+ if(i < 0 && i != Q_DEL_ALL)
+ ps->quote_suppression_threshold = -i;
+ else
+ ps->quote_suppression_threshold = i;
+ }
+ }
+
+ set_current_val(&vars[V_DEADLETS], TRUE, TRUE);
+ ps->deadlets = i = atoi(DF_DEADLETS);
+ if(SVAR_DEADLETS(ps, i, tmp_20k_buf, SIZEOF_20KBUF))
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ else
+ ps->deadlets = i;
+
+ set_current_val(&vars[V_STATUS_MSG_DELAY], TRUE, TRUE);
+ ps->status_msg_delay = i = 0;
+ if(SVAR_MSGDLAY(ps, i, tmp_20k_buf, SIZEOF_20KBUF))
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ else
+ ps->status_msg_delay = i;
+
+ set_current_val(&vars[V_ACTIVE_MSG_INTERVAL], TRUE, TRUE);
+ ps->active_status_interval = i = 8;
+ if(SVAR_ACTIVEINTERVAL(ps, i, tmp_20k_buf, SIZEOF_20KBUF))
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ else
+ ps->active_status_interval = i;
+
+ set_current_val(&vars[V_REMOTE_ABOOK_HISTORY], TRUE, TRUE);
+ ps->remote_abook_history = i = atoi(DF_REMOTE_ABOOK_HISTORY);
+ if(SVAR_AB_HIST(ps, i, tmp_20k_buf, SIZEOF_20KBUF))
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ else
+ ps->remote_abook_history = i;
+
+ set_current_val(&vars[V_REMOTE_ABOOK_VALIDITY], TRUE, TRUE);
+ ps->remote_abook_validity = i = atoi(DF_REMOTE_ABOOK_VALIDITY);
+ if(SVAR_AB_VALID(ps, i, tmp_20k_buf, SIZEOF_20KBUF))
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ else
+ ps->remote_abook_validity = i;
+
+ set_current_val(&vars[V_USERINPUTTIMEO], TRUE, TRUE);
+ ps->hours_to_timeout = i = 0;
+ if(SVAR_USER_INPUT(ps, i, tmp_20k_buf, SIZEOF_20KBUF))
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ else
+ ps->hours_to_timeout = i;
+
+ /* timeo is a regular extern int because it is referenced in pico */
+ set_current_val(&vars[V_MAILCHECK], TRUE, TRUE);
+ set_input_timeout(i = 15);
+ if(SVAR_MAILCHK(ps, i, tmp_20k_buf, SIZEOF_20KBUF))
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ else
+ set_input_timeout(i);
+
+ set_current_val(&vars[V_MAILCHECKNONCURR], TRUE, TRUE);
+ ps->check_interval_for_noncurr = i = 0;
+ if(SVAR_MAILCHKNONCURR(ps, i, tmp_20k_buf, SIZEOF_20KBUF))
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ else
+ ps->check_interval_for_noncurr = i;
+
+#ifdef DEBUGJOURNAL
+ ps->debugmem = 1;
+#else
+ ps->debugmem = 0;
+#endif
+
+ i = 30;
+ set_current_val(&vars[V_TCPOPENTIMEO], TRUE, TRUE);
+ /* this is just for the error, we don't save the result */
+ if(VAR_TCPOPENTIMEO && SVAR_TCP_OPEN(ps, i, tmp_20k_buf, SIZEOF_20KBUF))
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+
+ i = 15;
+ set_current_val(&vars[V_TCPREADWARNTIMEO], TRUE, TRUE);
+ /* this is just for the error, we don't save the result */
+ if(VAR_TCPREADWARNTIMEO && SVAR_TCP_READWARN(ps, i, tmp_20k_buf, SIZEOF_20KBUF))
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+
+ i = 0;
+ set_current_val(&vars[V_TCPWRITEWARNTIMEO], TRUE, TRUE);
+ /* this is just for the error, we don't save the result */
+ if(VAR_TCPWRITEWARNTIMEO && SVAR_TCP_WRITEWARN(ps, i, tmp_20k_buf, SIZEOF_20KBUF))
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+
+ i = 15;
+ set_current_val(&vars[V_RSHOPENTIMEO], TRUE, TRUE);
+ /* this is just for the error, we don't save the result */
+ if(VAR_RSHOPENTIMEO && SVAR_RSH_OPEN(ps, i, tmp_20k_buf, SIZEOF_20KBUF))
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+
+ i = 15;
+ set_current_val(&vars[V_SSHOPENTIMEO], TRUE, TRUE);
+ /* this is just for the error, we don't save the result */
+ if(VAR_SSHOPENTIMEO && SVAR_SSH_OPEN(ps, i, tmp_20k_buf, SIZEOF_20KBUF))
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+
+ set_current_val(&vars[V_INCCHECKLIST], TRUE, TRUE);
+
+ set_current_val(&vars[V_INCCHECKTIMEO], TRUE, TRUE);
+ ps->inc_check_timeout = i = 5;
+ if(SVAR_INC_CHECK_TIMEO(ps, i, tmp_20k_buf, SIZEOF_20KBUF))
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ else
+ ps->inc_check_timeout = i;
+
+ set_current_val(&vars[V_INCCHECKINTERVAL], TRUE, TRUE);
+ ps->inc_check_interval = i = 180;
+ if(SVAR_INC_CHECK_INTERV(ps, i, tmp_20k_buf, SIZEOF_20KBUF))
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ else
+ ps->inc_check_interval = i;
+
+ set_current_val(&vars[V_INC2NDCHECKINTERVAL], TRUE, TRUE);
+ ps->inc_second_check_interval = i = 180;
+ if(SVAR_INC_2NDCHECK_INTERV(ps, i, tmp_20k_buf, SIZEOF_20KBUF))
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ else
+ ps->inc_second_check_interval = i;
+
+ rvl = 60L;
+ set_current_val(&vars[V_MAILDROPCHECK], TRUE, TRUE);
+ /* this is just for the error, we don't save the result */
+ if(VAR_MAILDROPCHECK && SVAR_MAILDCHK(ps, rvl, tmp_20k_buf, SIZEOF_20KBUF))
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+
+ rvl = 0L;
+ set_current_val(&vars[V_NNTPRANGE], TRUE, TRUE);
+ /* this is just for the error, we don't save the result */
+ if(VAR_NNTPRANGE && SVAR_NNTPRANGE(ps, rvl, tmp_20k_buf, SIZEOF_20KBUF))
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+
+ set_current_val(&vars[V_TCPQUERYTIMEO], TRUE, TRUE);
+ ps->tcp_query_timeout = i = TO_BAIL_THRESHOLD;
+ if(VAR_TCPQUERYTIMEO && SVAR_TCP_QUERY(ps, i, tmp_20k_buf, SIZEOF_20KBUF))
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ else
+ ps->tcp_query_timeout = i;
+
+ set_current_val(&vars[V_NEWSRC_PATH], TRUE, TRUE);
+ if(VAR_NEWSRC_PATH && VAR_NEWSRC_PATH[0])
+ mail_parameters(NULL, SET_NEWSRC, (void *)VAR_NEWSRC_PATH);
+
+ set_current_val(&vars[V_NEWS_ACTIVE_PATH], TRUE, TRUE);
+ if(VAR_NEWS_ACTIVE_PATH)
+ mail_parameters(NULL, SET_NEWSACTIVE,
+ (void *)VAR_NEWS_ACTIVE_PATH);
+
+ set_current_val(&vars[V_NEWS_SPOOL_DIR], TRUE, TRUE);
+ if(VAR_NEWS_SPOOL_DIR)
+ mail_parameters(NULL, SET_NEWSSPOOL,
+ (void *)VAR_NEWS_SPOOL_DIR);
+
+ /* guarantee a save default */
+ set_current_val(&vars[V_DEFAULT_SAVE_FOLDER], TRUE, TRUE);
+ if(!VAR_DEFAULT_SAVE_FOLDER || !VAR_DEFAULT_SAVE_FOLDER[0])
+ set_variable(V_DEFAULT_SAVE_FOLDER,
+ (GLO_DEFAULT_SAVE_FOLDER && GLO_DEFAULT_SAVE_FOLDER[0])
+ ? GLO_DEFAULT_SAVE_FOLDER
+ : DEFAULT_SAVE, 1, 0, Main);
+
+ set_current_val(&vars[V_SIGNATURE_FILE], TRUE, TRUE);
+ set_current_val(&vars[V_LITERAL_SIG], TRUE, TRUE);
+ set_current_val(&vars[V_GLOB_ADDRBOOK], TRUE, TRUE);
+ set_current_val(&vars[V_ADDRESSBOOK], TRUE, TRUE);
+ set_current_val(&vars[V_FORCED_ABOOK_ENTRY], TRUE, TRUE);
+ set_current_val(&vars[V_DISABLE_DRIVERS], TRUE, TRUE);
+ set_current_val(&vars[V_DISABLE_AUTHS], TRUE, TRUE);
+
+ set_current_val(&vars[V_VIEW_HEADERS], TRUE, TRUE);
+ /* strip spaces and colons */
+ if(ps->VAR_VIEW_HEADERS){
+ for(s = ps->VAR_VIEW_HEADERS; (q = *s) != NULL; s++){
+ if(q[0]){
+ removing_leading_white_space(q);
+ /* look for colon or space or end */
+ for(p = q; *p && !isspace((unsigned char)*p) && *p != ':'; p++)
+ ;/* do nothing */
+
+ *p = '\0';
+ if(strucmp(q, ALL_EXCEPT) == 0)
+ ps->view_all_except = 1;
+ }
+ }
+ }
+
+ set_current_val(&vars[V_VIEW_MARGIN_LEFT], TRUE, TRUE);
+ set_current_val(&vars[V_VIEW_MARGIN_RIGHT], TRUE, TRUE);
+ set_current_val(&vars[V_UPLOAD_CMD], TRUE, TRUE);
+ set_current_val(&vars[V_UPLOAD_CMD_PREFIX], TRUE, TRUE);
+ set_current_val(&vars[V_DOWNLOAD_CMD], TRUE, TRUE);
+ set_current_val(&vars[V_DOWNLOAD_CMD_PREFIX], TRUE, TRUE);
+ set_current_val(&vars[V_MAILCAP_PATH], TRUE, TRUE);
+ set_current_val(&vars[V_MIMETYPE_PATH], TRUE, TRUE);
+#if !defined(DOS) && !defined(OS2) && !defined(LEAVEOUTFIFO)
+ set_current_val(&vars[V_FIFOPATH], TRUE, TRUE);
+#endif
+
+ set_current_val(&vars[V_RSHPATH], TRUE, TRUE);
+ if(VAR_RSHPATH
+ && is_absolute_path(VAR_RSHPATH)
+ && can_access(VAR_RSHPATH, EXECUTE_ACCESS) == 0){
+ mail_parameters(NULL, SET_RSHPATH, (void *) VAR_RSHPATH);
+ }
+
+ set_current_val(&vars[V_RSHCMD], TRUE, TRUE);
+ if(VAR_RSHCMD){
+ mail_parameters(NULL, SET_RSHCOMMAND, (void *) VAR_RSHCMD);
+ }
+
+ set_current_val(&vars[V_SSHPATH], TRUE, TRUE);
+ if(VAR_SSHPATH) {
+ if(is_absolute_path(VAR_SSHPATH)
+ && can_access(VAR_SSHPATH, EXECUTE_ACCESS) == 0){
+ mail_parameters(NULL, SET_SSHPATH, (void *) VAR_SSHPATH);
+ }
+ else {
+ mail_parameters(NULL, SET_SSHPATH, (void *) NULL);
+ }
+ }
+
+ set_current_val(&vars[V_SSHCMD], TRUE, TRUE);
+ if(VAR_SSHCMD) {
+ if(VAR_SSHCMD[0]) {
+ mail_parameters(NULL, SET_SSHCOMMAND, (void *) VAR_SSHCMD);
+ }
+ else {
+ mail_parameters(NULL, SET_SSHCOMMAND, (void *) NULL);
+ }
+ }
+
+#if defined(DOS) || defined(OS2)
+
+ set_current_val(&vars[V_FILE_DIR], TRUE, TRUE);
+
+#ifdef _WINDOWS
+ set_current_val(&vars[V_FONT_NAME], TRUE, TRUE);
+ set_current_val(&vars[V_FONT_SIZE], TRUE, TRUE);
+ set_current_val(&vars[V_FONT_STYLE], TRUE, TRUE);
+ set_current_val(&vars[V_FONT_CHAR_SET], TRUE, TRUE);
+ set_current_val(&vars[V_CURSOR_STYLE], TRUE, TRUE);
+ set_current_val(&vars[V_WINDOW_POSITION], TRUE, TRUE);
+
+ if(F_OFF(F_STORE_WINPOS_IN_CONFIG, ps_global)){
+ /* if win position is in the registry, use it */
+ if(mswin_reg(MSWR_OP_GET, MSWR_PINE_POS, buf, sizeof(buf))){
+ if(VAR_WINDOW_POSITION)
+ fs_give((void **)&VAR_WINDOW_POSITION);
+
+ VAR_WINDOW_POSITION = cpystr(buf);
+ }
+ else if(VAR_WINDOW_POSITION
+ && (ps->update_registry != UREG_NEVER_SET)){
+ /* otherwise, put it there */
+ mswin_reg(MSWR_OP_SET | ((ps->update_registry == UREG_ALWAYS_SET)
+ ? MSWR_OP_FORCE : 0),
+ MSWR_PINE_POS,
+ VAR_WINDOW_POSITION, (size_t)NULL);
+ }
+ }
+
+ mswin_setwindow (VAR_FONT_NAME, VAR_FONT_SIZE,
+ VAR_FONT_STYLE, VAR_WINDOW_POSITION,
+ VAR_CURSOR_STYLE, VAR_FONT_CHAR_SET);
+
+ /* this is no longer used */
+ if(VAR_WINDOW_POSITION)
+ fs_give((void **)&VAR_WINDOW_POSITION);
+
+ set_current_val(&vars[V_PRINT_FONT_NAME], TRUE, TRUE);
+ set_current_val(&vars[V_PRINT_FONT_SIZE], TRUE, TRUE);
+ set_current_val(&vars[V_PRINT_FONT_STYLE], TRUE, TRUE);
+ set_current_val(&vars[V_PRINT_FONT_CHAR_SET], TRUE, TRUE);
+ mswin_setprintfont (VAR_PRINT_FONT_NAME,
+ VAR_PRINT_FONT_SIZE,
+ VAR_PRINT_FONT_STYLE,
+ VAR_PRINT_FONT_CHAR_SET);
+
+ mswin_setgenhelptextcallback(pcpine_general_help);
+
+ mswin_setclosetext ("Use the \"Q\" command to exit Alpine.");
+
+ {
+ char foreColor[64], backColor[64];
+
+ mswin_getwindow(NULL, 0, NULL, 0, NULL, 0, NULL, 0,
+ foreColor, sizeof(foreColor), backColor, sizeof(backColor),
+ NULL, 0, NULL, 0);
+ if(!GLO_NORM_FORE_COLOR)
+ GLO_NORM_FORE_COLOR = cpystr(foreColor);
+
+ if(!GLO_NORM_BACK_COLOR)
+ GLO_NORM_BACK_COLOR = cpystr(backColor);
+ }
+#endif /* _WINDOWS */
+#endif /* DOS */
+
+ /*
+ * We want the version number to start out as 1.0 for Alpine, but
+ * we also want to use the same old config file that was used
+ * with Pine. The Pine version numbers made it up to 4.64 and we
+ * want Alpine's 1.0 to be larger than 4.64 so we keep a separate
+ * internal version number which is the real version number
+ * plus 4. That's what gets written in LAST_VERS_USED.
+ */
+ strncpy(ps->vers_internal, ALPINE_VERSION, sizeof(ps->vers_internal));
+ ps->vers_internal[sizeof(ps->vers_internal)-1] = '\0';
+ if(isdigit(ps->vers_internal[0]) && ps->vers_internal[0] < '6')
+ ps->vers_internal[0] = ps->vers_internal[0] + 4;
+
+ set_current_val(&vars[V_LAST_VERS_USED], TRUE, TRUE);
+ /* Check for special cases first */
+ if(VAR_LAST_VERS_USED
+ && (isdigit(ps->vers_internal[0])
+ && ps->vers_internal[1] == '.'
+ && isdigit((unsigned char)ps->vers_internal[2])
+ && isdigit((unsigned char)ps->vers_internal[3])
+ && isalpha((unsigned char)ps->vers_internal[4])
+ && strncmp(VAR_LAST_VERS_USED, ps->vers_internal, 4) >= 0)){
+ ps->show_new_version = 0;
+ }
+ /* Otherwise just do lexicographic comparision... */
+ else if(VAR_LAST_VERS_USED
+ && strcmp(VAR_LAST_VERS_USED, ps->vers_internal) >= 0){
+ ps->show_new_version = 0;
+ }
+ else{
+#ifdef _WINDOWS
+ /*
+ * If this is the first time we've run a version > 4.40, and there
+ * is evidence that the config file has not been used by unix pine,
+ * then we convert color008 to colorlgr, color009 to colormgr, and
+ * color010 to colordgr. If the config file is being used by
+ * unix pine then color009 may really supposed to be red, etc.
+ * Same if we've already run 4.41 or higher. We don't have to do
+ * anything if we are new to alpine.
+ */
+ ps->pre441 = (VAR_LAST_VERS_USED
+ && strcmp(VAR_LAST_VERS_USED, "4.40") <= 0);
+#endif /* _WINDOWS */
+
+ /*
+ * Don't offer the new version message if we're told not to.
+ */
+ set_current_val(&vars[V_NEW_VER_QUELL], TRUE, TRUE);
+ ps->show_new_version = !(VAR_NEW_VER_QUELL
+ && strcmp(ps->vers_internal,
+ VAR_NEW_VER_QUELL) < 0);
+
+#ifdef _WINDOWS
+ if(!ps_global->install_flag)
+#endif /* _WINDOWS */
+ {
+ if(VAR_LAST_VERS_USED){
+ strncpy(ps_global->pine_pre_vers, VAR_LAST_VERS_USED,
+ sizeof(ps_global->pine_pre_vers));
+ ps_global->pine_pre_vers[sizeof(ps_global->pine_pre_vers)-1] = '\0';
+ }
+
+ set_variable(V_LAST_VERS_USED, ps->vers_internal, 1, 1,
+ ps_global->ew_for_except_vars);
+ }
+ }
+
+ /* Obsolete, backwards compatibility */
+ set_current_val(&vars[V_ELM_STYLE_SAVE], TRUE, TRUE);
+ /* Also obsolete */
+ set_current_val(&vars[V_SAVE_BY_SENDER], TRUE, TRUE);
+ if(!strucmp(VAR_ELM_STYLE_SAVE, "yes"))
+ set_variable(V_SAVE_BY_SENDER, "yes", 1, 1, Main);
+ obs_save_by_sender = !strucmp(VAR_SAVE_BY_SENDER, "yes");
+
+ set_current_pattern_vals(ps);
+
+ set_current_val(&vars[V_INDEX_FORMAT], TRUE, TRUE);
+ init_index_format(VAR_INDEX_FORMAT, &ps->index_disp_format);
+
+ /* this should come after pre441 is set or not */
+ set_current_color_vals(ps);
+
+ set_current_val(&vars[V_RSS_NEWS], TRUE, TRUE);
+ set_current_val(&vars[V_RSS_WEATHER], TRUE, TRUE);
+ set_current_val(&vars[V_WP_INDEXHEIGHT], TRUE, TRUE);
+ set_current_val(&vars[V_WP_INDEXLINES], TRUE, TRUE);
+ set_current_val(&vars[V_WP_AGGSTATE], TRUE, TRUE);
+ set_current_val(&vars[V_WP_STATE], TRUE, TRUE);
+ set_current_val(&vars[V_WP_COLUMNS], TRUE, TRUE);
+
+ set_current_val(&vars[V_PRUNED_FOLDERS], TRUE, TRUE);
+ set_current_val(&vars[V_ARCHIVED_FOLDERS], TRUE, TRUE);
+ set_current_val(&vars[V_INCOMING_FOLDERS], TRUE, TRUE);
+ set_current_val(&vars[V_SORT_KEY], TRUE, TRUE);
+ if(decode_sort(VAR_SORT_KEY, &ps->def_sort, &def_sort_rev) == -1){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Sort type \"%.200s\" is invalid", VAR_SORT_KEY);
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ ps->def_sort = SortArrival;
+ ps->def_sort_rev = 0;
+ }
+ else
+ ps->def_sort_rev = def_sort_rev;
+
+ cur_rule_value(&vars[V_SAVED_MSG_NAME_RULE], TRUE, TRUE);
+ {NAMEVAL_S *v; int i;
+ for(i = 0; (v = save_msg_rules(i)); i++)
+ if(v->value == ps_global->save_msg_rule)
+ break;
+
+ /* if save_msg_rule is not default, or is explicitly set to default */
+ if((ps_global->save_msg_rule != SAV_RULE_DEFLT) ||
+ (v && v->name &&
+ (!strucmp(ps_global->vars[V_SAVED_MSG_NAME_RULE].post_user_val.p,
+ v->name) ||
+ !strucmp(ps_global->vars[V_SAVED_MSG_NAME_RULE].main_user_val.p,
+ v->name))))
+ obs_save_by_sender = 0; /* don't overwrite */
+ }
+
+ cur_rule_value(&vars[V_FCC_RULE], TRUE, TRUE);
+ cur_rule_value(&vars[V_AB_SORT_RULE], TRUE, TRUE);
+
+#ifndef _WINDOWS
+ cur_rule_value(&vars[V_COLOR_STYLE], TRUE, TRUE);
+#endif
+
+ cur_rule_value(&vars[V_INDEX_COLOR_STYLE], TRUE, TRUE);
+ cur_rule_value(&vars[V_TITLEBAR_COLOR_STYLE], TRUE, TRUE);
+ cur_rule_value(&vars[V_FLD_SORT_RULE], TRUE, TRUE);
+ cur_rule_value(&vars[V_INCOMING_STARTUP], TRUE, TRUE);
+ cur_rule_value(&vars[V_PRUNING_RULE], TRUE, TRUE);
+ cur_rule_value(&vars[V_REOPEN_RULE], TRUE, TRUE);
+ cur_rule_value(&vars[V_GOTO_DEFAULT_RULE], TRUE, TRUE);
+ cur_rule_value(&vars[V_THREAD_DISP_STYLE], TRUE, TRUE);
+ cur_rule_value(&vars[V_THREAD_INDEX_STYLE], TRUE, TRUE);
+
+ set_current_val(&vars[V_THREAD_MORE_CHAR], TRUE, TRUE);
+ if(VAR_THREAD_MORE_CHAR[0] && VAR_THREAD_MORE_CHAR[1]){
+ init_error(ps, SM_ORDER | SM_DING, 3, 5,
+ _("Only using first character of threading-indicator-character option"));
+ VAR_THREAD_MORE_CHAR[1] = '\0';
+ }
+
+ set_current_val(&vars[V_THREAD_EXP_CHAR], TRUE, TRUE);
+ if(VAR_THREAD_EXP_CHAR[0] && VAR_THREAD_EXP_CHAR[1]){
+ init_error(ps, SM_ORDER | SM_DING, 3, 5,
+ _("Only using first character of threading-expanded-character option"));
+ VAR_THREAD_EXP_CHAR[1] = '\0';
+ }
+
+ set_current_val(&vars[V_THREAD_LASTREPLY_CHAR], TRUE, TRUE);
+ if(!VAR_THREAD_LASTREPLY_CHAR[0])
+ VAR_THREAD_LASTREPLY_CHAR = cpystr(DF_THREAD_LASTREPLY_CHAR);
+
+ if(VAR_THREAD_LASTREPLY_CHAR[0] && VAR_THREAD_LASTREPLY_CHAR[1]){
+ init_error(ps, SM_ORDER | SM_DING, 3, 5,
+ _("Only using first character of threading-lastreply-character option"));
+ VAR_THREAD_LASTREPLY_CHAR[1] = '\0';
+ }
+
+ set_current_val(&vars[V_MAXREMSTREAM], TRUE, TRUE);
+ ps->s_pool.max_remstream = i = atoi(DF_MAXREMSTREAM);
+ if(SVAR_MAXREMSTREAM(ps, i, tmp_20k_buf, SIZEOF_20KBUF))
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ else
+ ps->s_pool.max_remstream = i;
+
+ set_current_val(&vars[V_PERMLOCKED], TRUE, TRUE);
+
+ set_current_val(&vars[V_NMW_WIDTH], TRUE, TRUE);
+ ps->nmw_width = i = atoi(DF_NMW_WIDTH);
+ if(SVAR_NMW_WIDTH(ps, i, tmp_20k_buf, SIZEOF_20KBUF))
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ else
+ ps->nmw_width = i;
+
+ /* backwards compatibility */
+ if(obs_save_by_sender){
+ ps->save_msg_rule = SAV_RULE_FROM;
+ set_variable(V_SAVED_MSG_NAME_RULE, "by-from", 1, 1, Main);
+ }
+
+ /* this should come after process_feature_list because of use_fkeys */
+ if(!ps->start_in_index)
+ set_current_val(&vars[V_INIT_CMD_LIST], FALSE, TRUE);
+ if(VAR_INIT_CMD_LIST && VAR_INIT_CMD_LIST[0] && VAR_INIT_CMD_LIST[0][0])
+ if(cmds_f)
+ (*cmds_f)(ps, VAR_INIT_CMD_LIST);
+
+#ifdef _WINDOWS
+ mswin_set_quit_confirm (F_OFF(F_QUIT_WO_CONFIRM, ps_global));
+#endif /* _WINDOWS */
+
+#ifdef DEBUG
+ dump_configuration(0);
+#endif /* DEBUG */
+}
+
+
+void
+convert_configvars_to_utf8(struct variable *vars, char *fromcharset)
+{
+ struct variable *v;
+
+ /*
+ * Make sure that everything is UTF-8.
+ */
+ for(v = vars; v->name; v++)
+ convert_configvar_to_utf8(v, fromcharset);
+}
+
+
+void
+convert_configvar_to_utf8(struct variable *v, char *fromcharset)
+{
+ char **p, *conv, **valptr;
+ int i;
+
+ /*
+ * Make sure that everything is UTF-8.
+ */
+ if(v->is_list){
+ for(i = 0; i < 7; i++){
+ switch(i){
+ case 1: valptr = v->current_val.l; break;
+ case 0: valptr = v->main_user_val.l; break;
+ case 2: valptr = v->changed_val.l; break;
+ case 3: valptr = v->post_user_val.l; break;
+ case 4: valptr = v->global_val.l; break;
+ case 5: valptr = v->fixed_val.l; break;
+ case 6: valptr = v->cmdline_val.l; break;
+ default: panic("bad case in convert_configvar");
+ }
+
+ if(valptr){
+ for(p = valptr; *p; p++){
+ if(**p){
+ conv = convert_to_utf8(*p, fromcharset, 0);
+ if(conv){
+ fs_give((void **) p);
+ *p = conv;
+ }
+ }
+ }
+ }
+ }
+ }
+ else{
+ for(i = 0; i < 7; i++){
+ switch(i){
+ case 1: valptr = &v->current_val.p; break;
+ case 0: valptr = &v->main_user_val.p; break;
+ case 2: valptr = &v->changed_val.p; break;
+ case 3: valptr = &v->post_user_val.p; break;
+ case 4: valptr = &v->global_val.p; break;
+ case 5: valptr = &v->fixed_val.p; break;
+ case 6: valptr = &v->cmdline_val.p; break;
+ default: panic("bad case in convert_configvar");
+ }
+
+ if(valptr && *valptr && (*valptr)[0]){
+ conv = convert_to_utf8(*valptr, fromcharset, 0);
+ if(conv){
+ fs_give((void **) valptr);
+ *valptr = conv;
+ }
+ }
+ }
+ }
+}
+
+
+
+/*
+ * Standard feature name sections
+ */
+char *
+feature_list_section(FEATURE_S *feature)
+{
+#define PREF_NONE -1
+ static char *feat_sect[] = {
+#define PREF_MISC 0
+ /* TRANSLATORS: section heading in configuration screen */
+ N_("Advanced User Preferences"),
+#define PREF_FLDR 1
+ /* TRANSLATORS: section heading in configuration screen */
+ N_("Folder Preferences"),
+#define PREF_ADDR 2
+ /* TRANSLATORS: section heading in configuration screen */
+ N_("Address Book Preferences"),
+#define PREF_COMP 3
+ /* TRANSLATORS: section heading in configuration screen */
+ N_("Composer Preferences"),
+#define PREF_NEWS 4
+ /* TRANSLATORS: section heading in configuration screen */
+ N_("News Preferences"),
+#define PREF_VIEW 5
+ /* TRANSLATORS: section heading in configuration screen */
+ N_("Viewer Preferences"),
+#define PREF_ACMD 6
+ /* TRANSLATORS: section heading in configuration screen */
+ N_("Advanced Command Preferences"),
+#define PREF_PRNT 7
+ /* TRANSLATORS: section heading in configuration screen */
+ N_("Printer Preferences"),
+#define PREF_RPLY 8
+ /* TRANSLATORS: section heading in configuration screen */
+ N_("Reply Preferences"),
+#define PREF_SEND 9
+ /* TRANSLATORS: section heading in configuration screen */
+ N_("Sending Preferences"),
+#define PREF_INDX 10
+ /* TRANSLATORS: section heading in configuration screen */
+ N_("Message Index Preferences"),
+#define PREF_HIDDEN 11
+ HIDDEN_PREF
+};
+
+ return((feature && feature->section > PREF_NONE
+ && feature->section < (sizeof(feat_sect)/sizeof(feat_sect[0])))
+ ? _(feat_sect[feature->section]) : NULL);
+}
+
+
+/* any os-specific exclusions */
+#if defined(DOS) || defined(OS2)
+#define PREF_OS_LWSD PREF_NONE
+#define PREF_OS_LCLK PREF_NONE
+#define PREF_OS_STSP PREF_NONE
+#define PREF_OS_SPWN PREF_NONE
+#define PREF_OS_XNML PREF_NONE
+#define PREF_OS_USFK PREF_MISC
+#define PREF_OS_MOUSE PREF_NONE
+#else
+#define PREF_OS_LWSD PREF_MISC
+#define PREF_OS_LCLK PREF_COMP
+#define PREF_OS_STSP PREF_MISC
+#define PREF_OS_SPWN PREF_MISC
+#define PREF_OS_XNML PREF_MISC
+#define PREF_OS_USFK PREF_NONE
+#define PREF_OS_MOUSE PREF_MISC
+#endif
+
+
+/*
+ * Standard way to get at feature list members...
+ */
+FEATURE_S *
+feature_list(int index)
+{
+ /*
+ * This list is alphabatized by feature string, but the
+ * macro values need not be ordered.
+ */
+ static FEATURE_S feat_list[] = {
+/* Composer prefs */
+ {"allow-changing-from", NULL,
+ F_ALLOW_CHANGING_FROM, h_config_allow_chg_from, PREF_COMP, 1},
+ {"alternate-compose-menu", NULL,
+ F_ALT_COMPOSE_MENU, h_config_alt_compose_menu, PREF_COMP, 0},
+ {"alternate-role-menu", "Alternate Role (#) Menu",
+ F_ALT_ROLE_MENU, h_config_alt_role_menu, PREF_COMP, 0},
+ {"compose-cancel-confirm-uses-yes", NULL,
+ F_CANCEL_CONFIRM, h_config_cancel_confirm, PREF_COMP, 0},
+ {"compose-rejects-unqualified-addrs", "Compose Rejects Unqualified Addresses",
+ F_COMPOSE_REJECTS_UNQUAL, h_config_compose_rejects_unqual, PREF_COMP, 0},
+ {"compose-send-offers-first-filter", NULL,
+ F_FIRST_SEND_FILTER_DFLT, h_config_send_filter_dflt, PREF_COMP, 0},
+ {"compose-cut-from-cursor", "Ctrl-K Cuts From Cursor",
+ F_DEL_FROM_DOT, h_config_del_from_dot, PREF_COMP, 0},
+ {"compose-maps-delete-key-to-ctrl-d", "Delete Key Maps to Ctrl-D",
+ F_COMPOSE_MAPS_DEL, h_config_compose_maps_del, PREF_COMP, 0},
+ {"quell-dead-letter-on-cancel", "Do Not Save to Deadletter on Cancel",
+ F_QUELL_DEAD_LETTER, h_config_quell_dead_letter, PREF_COMP, 0},
+ {"enable-alternate-editor-cmd", "Enable Alternate Editor Command",
+ F_ENABLE_ALT_ED, h_config_enable_alt_ed, PREF_COMP, 1},
+ {"enable-alternate-editor-implicitly", NULL,
+ F_ALT_ED_NOW, h_config_alt_ed_now, PREF_COMP, 0},
+ {"enable-search-and-replace", "Enable Search and Replace",
+ F_ENABLE_SEARCH_AND_REPL, h_config_enable_search_and_repl, PREF_COMP, 1},
+ {"enable-sigdashes", NULL,
+ F_ENABLE_SIGDASHES, h_config_sigdashes, PREF_COMP, 0},
+ {"quell-mailchecks-composing-except-inbox", "Prevent Mailchecks While Composing Except for INBOX",
+ F_QUELL_PINGS_COMPOSING, h_config_quell_checks_comp, PREF_COMP, 0},
+ {"quell-mailchecks-composing-inbox", "Prevent Mailchecks While Composing for INBOX",
+ F_QUELL_PINGS_COMPOSING_INBOX, h_config_quell_checks_comp_inbox, PREF_COMP, 0},
+ {"quell-user-lookup-in-passwd-file", "Prevent User Lookup in Password File",
+ F_QUELL_LOCAL_LOOKUP, h_config_quell_local_lookup, PREF_OS_LCLK, 0},
+ {"spell-check-before-sending", NULL,
+ F_ALWAYS_SPELL_CHECK, h_config_always_spell_check, PREF_COMP, 0},
+
+/* Reply Prefs */
+ {"copy-to-address-to-from-if-it-is-us", "Copy To Address to From if it is Us",
+ F_COPY_TO_TO_FROM, h_config_copy_to_to_from, PREF_RPLY, 0},
+ {"enable-reply-indent-string-editing", NULL,
+ F_ENABLE_EDIT_REPLY_INDENT, h_config_prefix_editing, PREF_RPLY, 0},
+ {"include-attachments-in-reply", "Include Attachments in Reply",
+ F_ATTACHMENTS_IN_REPLY, h_config_attach_in_reply, PREF_RPLY, 0},
+ {"include-header-in-reply", "Include Header in Reply",
+ F_INCLUDE_HEADER, h_config_include_header, PREF_RPLY, 0},
+ {"include-text-in-reply", "Include Text in Reply",
+ F_AUTO_INCLUDE_IN_REPLY, h_config_auto_include_reply, PREF_RPLY, 0},
+ {"reply-always-uses-reply-to", "Reply Always Uses Reply-To",
+ F_AUTO_REPLY_TO, h_config_auto_reply_to, PREF_RPLY, 0},
+ {"signature-at-bottom", "Signature at Bottom",
+ F_SIG_AT_BOTTOM, h_config_sig_at_bottom, PREF_RPLY, 0},
+ {"strip-from-sigdashes-on-reply", "Strip From Sigdashes on Reply",
+ F_ENABLE_STRIP_SIGDASHES, h_config_strip_sigdashes, PREF_RPLY, 0},
+ {"forward-as-attachment", "Forward messages as attachments",
+ F_FORWARD_AS_ATTACHMENT, h_config_forward_as_attachment, PREF_RPLY, 0},
+
+/* Sending Prefs */
+ {"disable-sender", "Do Not Generate Sender Header",
+ F_DISABLE_SENDER, h_config_disable_sender, PREF_SEND, 0},
+ {"use-sender-not-x-sender", "Use Sender Instead of X-X-Sender",
+ F_USE_SENDER_NOT_X, h_config_use_sender_not_x, PREF_SEND, 0},
+ {"quell-flowed-text", "Do Not Send Flowed Text",
+ F_QUELL_FLOWED_TEXT, h_config_quell_flowed_text, PREF_SEND, 0},
+ {"downgrade-multipart-to-text", "Downgrade Multipart to Text",
+ F_COMPOSE_ALWAYS_DOWNGRADE, h_downgrade_multipart_to_text, PREF_SEND, 0},
+ {"enable-8bit-esmtp-negotiation", "Enable 8bit ESMTP Negotiation",
+ F_ENABLE_8BIT, h_config_8bit_smtp, PREF_SEND, 1},
+#ifdef BACKGROUND_POST
+ {"enable-background-sending", NULL,
+ F_BACKGROUND_POST, h_config_compose_bg_post, PREF_SEND, 0},
+#endif
+ {"enable-delivery-status-notification", NULL,
+ F_DSN, h_config_compose_dsn, PREF_SEND, 0},
+ {"enable-verbose-smtp-posting", "Enable Verbose SMTP Posting",
+ F_VERBOSE_POST, h_config_verbose_post, PREF_SEND, 0},
+ {"fcc-without-attachments", "Fcc Does Not Include Attachments",
+ F_NO_FCC_ATTACH, h_config_no_fcc_attach, PREF_SEND, 0},
+ {"fcc-on-bounce", "Include Fcc When Bouncing Messages",
+ F_FCC_ON_BOUNCE, h_config_fcc_on_bounce, PREF_SEND, 0},
+ {"mark-fcc-seen", NULL,
+ F_MARK_FCC_SEEN, h_config_mark_fcc_seen, PREF_SEND, 0},
+ {"fcc-only-without-confirm", "Send to Fcc Only Without Confirming",
+ F_AUTO_FCC_ONLY, h_config_auto_fcc_only, PREF_SEND, 0},
+ {"send-without-confirm", "Send Without Confirming",
+ F_SEND_WO_CONFIRM, h_config_send_wo_confirm, PREF_SEND, 0},
+ {"strip-whitespace-before-send", "Strip Whitespace Before Sending",
+ F_STRIP_WS_BEFORE_SEND, h_config_strip_ws_before_send, PREF_SEND, 0},
+ {"warn-if-blank-fcc", "Warn if Blank Fcc",
+ F_WARN_ABOUT_NO_FCC, h_config_warn_if_fcc_blank, PREF_SEND, 0},
+ {"warn-if-blank-subject", "Warn if Blank Subject",
+ F_WARN_ABOUT_NO_SUBJECT, h_config_warn_if_subj_blank, PREF_SEND, 0},
+ {"warn-if-blank-to-and-cc-and-newsgroups", "Warn if Blank To and CC and Newsgroups",
+ F_WARN_ABOUT_NO_TO_OR_CC, h_config_warn_if_no_to_or_cc, PREF_SEND, 0},
+
+/* Folder */
+ {"combined-folder-display", NULL,
+ F_CMBND_FOLDER_DISP, h_config_combined_folder_display, PREF_FLDR, 0},
+ {"combined-subdirectory-display", NULL,
+ F_CMBND_SUBDIR_DISP, h_config_combined_subdir_display, PREF_FLDR, 0},
+ {"enable-lame-list-mode", "Compensate for Deficient IMAP servers",
+ F_FIX_BROKEN_LIST, h_config_lame_list_mode, PREF_FLDR, 0},
+ {"enable-dot-folders", "Enable Hidden Folders",
+ F_ENABLE_DOT_FOLDERS, h_config_enable_dot_folders, PREF_FLDR, 0},
+ {"enable-incoming-folders", "Enable Incoming Folders Collection",
+ F_ENABLE_INCOMING, h_config_enable_incoming, PREF_FLDR, 0},
+ {"enable-incoming-folders-checking", NULL,
+ F_ENABLE_INCOMING_CHECKING, h_config_enable_incoming_checking, PREF_FLDR, 0},
+ {"incoming-checking-includes-total", NULL,
+ F_INCOMING_CHECKING_TOTAL, h_config_incoming_checking_total, PREF_FLDR, 0},
+ {"incoming-checking-uses-recent", NULL,
+ F_INCOMING_CHECKING_RECENT, h_config_incoming_checking_recent, PREF_FLDR, 0},
+ {"expanded-view-of-folders", "Expanded View of Folders",
+ F_EXPANDED_FOLDERS, h_config_expanded_folders, PREF_FLDR, 0},
+ {"quell-empty-directories", "Hide Empty Directories",
+ F_QUELL_EMPTY_DIRS, h_config_quell_empty_dirs, PREF_FLDR, 0},
+ {"separate-folder-and-directory-entries", "Separate Folder and Directory Entries",
+ F_SEPARATE_FLDR_AS_DIR, h_config_separate_fold_dir_view, PREF_FLDR, 0},
+ {"single-column-folder-list", NULL,
+ F_SINGLE_FOLDER_LIST, h_config_single_list, PREF_FLDR, 0},
+ {"sort-default-fcc-alpha", "Sort Default Fcc Folder Alphabetically",
+ F_SORT_DEFAULT_FCC_ALPHA, h_config_sort_fcc_alpha, PREF_FLDR, 0},
+ {"sort-default-save-alpha", "Sort Default Save Folder Alphabetically",
+ F_SORT_DEFAULT_SAVE_ALPHA, h_config_sort_save_alpha, PREF_FLDR, 0},
+ {"vertical-folder-list", "Use Vertical Folder List",
+ F_VERTICAL_FOLDER_LIST, h_config_vertical_list, PREF_FLDR, 0},
+
+/* Addr book */
+ {"combined-addrbook-display", "Combined Address Book Display",
+ F_CMBND_ABOOK_DISP, h_config_combined_abook_display, PREF_ADDR, 0},
+ {"expanded-view-of-addressbooks", "Expanded View of Address Books",
+ F_EXPANDED_ADDRBOOKS, h_config_expanded_addrbooks, PREF_ADDR, 0},
+ {"expanded-view-of-distribution-lists", "Expanded View of Distribution Lists",
+ F_EXPANDED_DISTLISTS, h_config_expanded_distlists, PREF_ADDR, 0},
+#ifdef ENABLE_LDAP
+ {"ldap-result-to-addrbook-add", "LDAP Result to Addressbook Add",
+ F_ADD_LDAP_TO_ABOOK, h_config_add_ldap, PREF_ADDR, 0},
+#endif
+
+/* Index prefs */
+ {"auto-open-next-unread", NULL,
+ F_AUTO_OPEN_NEXT_UNREAD, h_config_auto_open_unread, PREF_INDX, 0},
+ {"continue-tab-without-confirm", "Continue NextNew Without Confirming",
+ F_TAB_NO_CONFIRM, h_config_tab_no_prompt, PREF_INDX, 0},
+ {"convert-dates-to-localtime", NULL,
+ F_DATES_TO_LOCAL, h_config_dates_to_local, PREF_INDX, 0},
+ {"delete-skips-deleted", NULL,
+ F_DEL_SKIPS_DEL, h_config_del_skips_del, PREF_INDX, 1},
+ {"disable-index-locale-dates", NULL,
+ F_DISABLE_INDEX_LOCALE_DATES, h_config_disable_index_locale_dates, PREF_INDX, 0},
+ {"enable-cruise-mode", NULL,
+ F_ENABLE_SPACE_AS_TAB, h_config_cruise_mode, PREF_INDX, 0},
+ {"enable-cruise-mode-delete", "Enable Cruise Mode With Deleting",
+ F_ENABLE_TAB_DELETES, h_config_cruise_mode_delete, PREF_INDX, 0},
+ {"mark-for-cc", "Mark for CC",
+ F_MARK_FOR_CC, h_config_mark_for_cc, PREF_INDX, 1},
+ {"next-thread-without-confirm", "Read Next Thread Without Confirming",
+ F_NEXT_THRD_WO_CONFIRM, h_config_next_thrd_wo_confirm, PREF_INDX, 0},
+ {"return-to-inbox-without-confirm", "Return to INBOX Without Confirming",
+ F_RET_INBOX_NO_CONFIRM, h_config_inbox_no_confirm, PREF_INDX, 0},
+ {"show-sort", "Show Sort in Titlebar",
+ F_SHOW_SORT, h_config_show_sort, PREF_INDX, 0},
+ {"tab-uses-unseen-for-next-folder", "Tab Uses Unseen for Next Folder",
+ F_TAB_USES_UNSEEN, h_config_tab_uses_unseen, PREF_INDX, 0},
+ {"tab-visits-next-new-message-only", NULL,
+ F_TAB_TO_NEW, h_config_tab_new_only, PREF_INDX, 0},
+ {"thread-index-shows-important-color", NULL,
+ F_COLOR_LINE_IMPORTANT, h_config_color_thrd_import, PREF_INDX, 0},
+ {"thread-sorts-by-arrival", "Thread Sorts by Arrival",
+ F_THREAD_SORTS_BY_ARRIVAL, h_config_thread_sorts_by_arrival, PREF_INDX, 0},
+
+/* Viewer prefs */
+ {"enable-msg-view-addresses", "Enable Message View Address Links",
+ F_SCAN_ADDR, h_config_enable_view_addresses, PREF_VIEW, 0},
+ {"enable-msg-view-attachments", "Enable Message View Attachment Links",
+ F_VIEW_SEL_ATTACH, h_config_enable_view_attach, PREF_VIEW, 0},
+ {"enable-msg-view-urls", "Enable Message View URL Links",
+ F_VIEW_SEL_URL, h_config_enable_view_url, PREF_VIEW, 1},
+ {"enable-msg-view-web-hostnames", "Enable Message View Web Hostname Links",
+ F_VIEW_SEL_URL_HOST, h_config_enable_view_web_host, PREF_VIEW, 1},
+ {"enable-msg-view-forced-arrows", "Enable Message View Forced Arrows",
+ F_FORCE_ARROWS, h_config_enable_view_arrows, PREF_VIEW, 0},
+ /* set to TRUE for windows */
+ {"pass-c1-control-characters-as-is", NULL,
+ F_PASS_C1_CONTROL_CHARS, h_config_pass_c1_control, PREF_VIEW, 0},
+ {"pass-control-characters-as-is", NULL,
+ F_PASS_CONTROL_CHARS, h_config_pass_control, PREF_VIEW, 0},
+ {"prefer-plain-text", NULL,
+ F_PREFER_PLAIN_TEXT, h_config_prefer_plain_text, PREF_VIEW, 0},
+ {"quell-charset-warning", "Suppress Character Set Warning",
+ F_QUELL_CHARSET_WARNING, h_config_quell_charset_warning, PREF_VIEW, 0},
+ {"quell-server-after-link-in-html", "Suppress Server After Link in HTML",
+ F_QUELL_HOST_AFTER_URL, h_config_quell_host_after_url, PREF_VIEW, 0},
+
+/* News */
+ {"compose-sets-newsgroup-without-confirm", "Compose Sets Newsgroup Without Confirming",
+ F_COMPOSE_TO_NEWSGRP, h_config_compose_news_wo_conf, PREF_NEWS, 0},
+ {"enable-8bit-nntp-posting", "Enable 8bit NNTP Posting",
+ F_ENABLE_8BIT_NNTP, h_config_8bit_nntp, PREF_NEWS, 0},
+ {"enable-multiple-newsrcs", NULL,
+ F_ENABLE_MULNEWSRCS, h_config_enable_mulnewsrcs, PREF_NEWS, 0},
+ {"mult-newsrc-hostnames-as-typed", "Multiple Newsrc Hostnames as Typed",
+ F_MULNEWSRC_HOSTNAMES_AS_TYPED, h_config_mulnews_as_typed, PREF_NEWS, 0},
+ {"hide-nntp-path", "Hide NNTP Path",
+ F_HIDE_NNTP_PATH, h_config_hide_nntp_path, PREF_NEWS, 0},
+ {"news-approximates-new-status", NULL,
+ F_FAKE_NEW_IN_NEWS, h_config_news_uses_recent, PREF_NEWS, 1},
+ {"news-deletes-across-groups", NULL,
+ F_NEWS_CROSS_DELETE, h_config_news_cross_deletes, PREF_NEWS, 0},
+ {"news-offers-catchup-on-close", "News Offers Catchup on Close",
+ F_NEWS_CATCHUP, h_config_news_catchup, PREF_NEWS, 0},
+ {"news-post-without-validation", NULL,
+ F_NO_NEWS_VALIDATION, h_config_post_wo_validation, PREF_NEWS, 0},
+ {"news-read-in-newsrc-order", "News Read in Newsrc Order",
+ F_READ_IN_NEWSRC_ORDER, h_config_read_in_newsrc_order, PREF_NEWS, 0},
+ {"nntp-search-uses-overview", "NNTP Search Uses Overview",
+ F_NNTP_SEARCH_USES_OVERVIEW, h_config_nntp_search_uses_overview, PREF_NEWS, 1},
+ {"predict-nntp-server", "Predict NNTP Server",
+ F_PREDICT_NNTP_SERVER, h_config_predict_nntp_server, PREF_NEWS, 0},
+ {"quell-extra-post-prompt", "Suppress Extra Posting Prompt",
+ F_QUELL_EXTRA_POST_PROMPT, h_config_quell_post_prompt, PREF_NEWS, 0},
+
+/* Print */
+ {"enable-print-via-y-command", NULL,
+ F_ENABLE_PRYNT, h_config_enable_y_print, PREF_PRNT, 0},
+ {"print-formfeed-between-messages", NULL,
+ F_AGG_PRINT_FF, h_config_ff_between_msgs, PREF_PRNT, 0},
+ {"print-includes-from-line", NULL,
+ F_FROM_DELIM_IN_PRINT, h_config_print_from, PREF_PRNT, 0},
+ {"print-index-enabled", NULL,
+ F_PRINT_INDEX, h_config_print_index, PREF_PRNT, 0},
+ {"print-offers-custom-cmd-prompt", "Print Offers Custom Command Prompt",
+ F_CUSTOM_PRINT, h_config_custom_print, PREF_PRNT, 0},
+
+/* adv cmd prefs */
+ {"enable-aggregate-command-set", NULL,
+ F_ENABLE_AGG_OPS, h_config_enable_agg_ops, PREF_ACMD, 1},
+ {"enable-arrow-navigation", NULL,
+ F_ARROW_NAV, h_config_arrow_nav, PREF_ACMD, 1},
+ {"enable-arrow-navigation-relaxed", NULL,
+ F_RELAXED_ARROW_NAV, h_config_relaxed_arrow_nav, PREF_ACMD, 1},
+ {"enable-bounce-cmd", "Enable Bounce Command",
+ F_ENABLE_BOUNCE, h_config_enable_bounce, PREF_ACMD, 1},
+ {"enable-exit-via-lessthan-command", NULL,
+ F_ENABLE_LESSTHAN_EXIT, h_config_enable_lessthan_exit, PREF_ACMD, 1},
+ {"enable-flag-cmd", "Enable Flag Command",
+ F_ENABLE_FLAG, h_config_enable_flag, PREF_ACMD, 1},
+ {"enable-flag-screen-implicitly", NULL,
+ F_FLAG_SCREEN_DFLT, h_config_flag_screen_default, PREF_ACMD, 0},
+ {"enable-flag-screen-keyword-shortcut", NULL,
+ F_FLAG_SCREEN_KW_SHORTCUT, h_config_flag_screen_kw_shortcut,PREF_ACMD, 1},
+ {"enable-full-header-and-text", "Enable Full Header and Text",
+ F_ENABLE_FULL_HDR_AND_TEXT, h_config_enable_full_hdr_and_text, PREF_ACMD, 0},
+ {"enable-full-header-cmd", "Enable Full Header Command",
+ F_ENABLE_FULL_HDR, h_config_enable_full_hdr, PREF_ACMD, 1},
+ {"enable-goto-in-file-browser", "Enable Goto in File Browser",
+ F_ALLOW_GOTO, h_config_allow_goto, PREF_ACMD, 1},
+ {"enable-jump-shortcut", NULL,
+ F_ENABLE_JUMP, h_config_enable_jump, PREF_ACMD, 1},
+ {"enable-partial-match-lists", NULL,
+ F_ENABLE_SUB_LISTS, h_config_sub_lists, PREF_ACMD, 1},
+ {"enable-tab-completion", NULL,
+ F_ENABLE_TAB_COMPLETE, h_config_enable_tab_complete, PREF_ACMD, 1},
+ {"enable-unix-pipe-cmd", "Enable Unix Pipe Command",
+ F_ENABLE_PIPE, h_config_enable_pipe, PREF_ACMD, 1},
+ {"quell-full-header-auto-reset", "Suppress Full Header Auto Reset",
+ F_QUELL_FULL_HDR_RESET, h_config_quell_full_hdr_reset, PREF_ACMD, 0},
+
+/* Adv user prefs */
+#if !defined(DOS) && !defined(OS2)
+ {"allow-talk", NULL,
+ F_ALLOW_TALK, h_config_allow_talk, PREF_MISC, 0},
+#endif
+ {"assume-slow-link", NULL,
+ F_FORCE_LOW_SPEED, h_config_force_low_speed, PREF_OS_LWSD, 0},
+ {"auto-move-read-msgs", "Auto Move Read Messages",
+ F_AUTO_READ_MSGS, h_config_auto_read_msgs, PREF_MISC, 0},
+ {"auto-unselect-after-apply", NULL,
+ F_AUTO_UNSELECT, h_config_auto_unselect, PREF_MISC, 0},
+ {"auto-unzoom-after-apply", NULL,
+ F_AUTO_UNZOOM, h_config_auto_unzoom, PREF_MISC, 1},
+ {"auto-zoom-after-select", NULL,
+ F_AUTO_ZOOM, h_config_auto_zoom, PREF_MISC, 1},
+ {"busy-cue-spinner-only", NULL,
+ F_USE_BORING_SPINNER, h_config_use_boring_spinner, PREF_MISC, 0},
+ {"check-newmail-when-quitting", NULL,
+ F_CHECK_MAIL_ONQUIT, h_config_check_mail_onquit, PREF_MISC, 0},
+ {"confirm-role-even-for-default", "Confirm Role Even for Default",
+ F_ROLE_CONFIRM_DEFAULT, h_config_confirm_role, PREF_MISC, 0},
+ {"disable-keymenu", NULL,
+ F_BLANK_KEYMENU, h_config_blank_keymenu, PREF_MISC, 0},
+ {"disable-password-caching", NULL,
+ F_DISABLE_PASSWORD_CACHING, h_config_disable_password_caching,
+ PREF_MISC, 0},
+ {"disable-regular-expression-matching-for-alternate-addresses", NULL,
+ F_DISABLE_REGEX, h_config_disable_regex, PREF_MISC, 0},
+ {"disable-save-input-history", NULL,
+ F_DISABLE_SAVE_INPUT_HISTORY, h_config_input_history, PREF_MISC, 0},
+ {"disable-take-fullname-in-addresses", "Disable Take Fullname in Addresses",
+ F_DISABLE_TAKE_FULLNAMES, h_config_take_fullname, PREF_MISC, 0},
+ {"disable-take-last-comma-first", NULL,
+ F_DISABLE_TAKE_LASTFIRST, h_config_take_lastfirst, PREF_MISC, 0},
+ {"disable-terminal-reset-for-display-filters", "Disable Terminal Reset for Display Filters",
+ F_DISABLE_TERM_RESET_DISP, h_config_disable_reset_disp, PREF_MISC, 0},
+ {"enable-dot-files", NULL,
+ F_ENABLE_DOT_FILES, h_config_enable_dot_files, PREF_MISC, 0},
+ {"enable-fast-recent-test", NULL,
+ F_ENABLE_FAST_RECENT, h_config_fast_recent, PREF_MISC, 0},
+ {"enable-mail-check-cue", NULL,
+ F_SHOW_DELAY_CUE, h_config_show_delay_cue, PREF_MISC, 0},
+ {"enable-mailcap-param-substitution", "Enable Mailcap Parameter Substitution",
+ F_DO_MAILCAP_PARAM_SUBST, h_config_mailcap_params, PREF_MISC, 0},
+ {"enable-mouse-in-xterm", "Enable Mouse in Xterm",
+ F_ENABLE_MOUSE, h_config_enable_mouse, PREF_OS_MOUSE, 0},
+ {"enable-newmail-in-xterm-icon", "Enable Newmail in Xterm Icon",
+ F_ENABLE_XTERM_NEWMAIL, h_config_enable_xterm_newmail, PREF_OS_XNML, 0},
+ {"enable-newmail-short-text-in-icon", "Enable Newmail Short Text in Icon",
+ F_ENABLE_NEWMAIL_SHORT_TEXT, h_config_enable_newmail_short_text, PREF_OS_XNML, 0},
+ {"enable-suspend", NULL,
+ F_CAN_SUSPEND, h_config_can_suspend, PREF_MISC, 0},
+ {"enable-take-export", NULL,
+ F_ENABLE_TAKE_EXPORT, h_config_enable_take_export, PREF_MISC, 0},
+ {"enable-rules-under-take", "Enable Take Rules",
+ F_ENABLE_ROLE_TAKE, h_config_enable_role_take, PREF_MISC, 0},
+#ifdef _WINDOWS
+ {"enable-tray-icon", NULL,
+ F_ENABLE_TRAYICON, h_config_tray_icon, PREF_MISC, 0},
+#endif
+ {"expose-hidden-config", NULL,
+ F_EXPOSE_HIDDEN_CONFIG, h_config_expose_hidden_config, PREF_MISC, 0},
+ {"expunge-only-manually", NULL,
+ F_EXPUNGE_MANUALLY, h_config_expunge_manually, PREF_MISC, 0},
+ {"expunge-without-confirm", "Expunge Without Confirming",
+ F_AUTO_EXPUNGE, h_config_auto_expunge, PREF_MISC, 0},
+ {"expunge-without-confirm-everywhere", "Expunge Without Confirming Everywhere",
+ F_FULL_AUTO_EXPUNGE, h_config_full_auto_expunge, PREF_MISC, 0},
+ {"force-arrow-cursor", NULL,
+ F_FORCE_ARROW, h_config_force_arrow, PREF_MISC, 0},
+ {"maildrops-preserve-state", NULL,
+ F_MAILDROPS_PRESERVE_STATE, h_config_maildrops_preserve_state,
+ PREF_MISC, 0},
+ {"offer-expunge-of-inbox", "Offer Expunge of INBOX",
+ F_EXPUNGE_INBOX, h_config_expunge_inbox, PREF_MISC, 0},
+ {"offer-expunge-of-stayopen-folders", "Offer Expunge of Stayopen Folders",
+ F_EXPUNGE_STAYOPENS, h_config_expunge_stayopens, PREF_MISC, 0},
+ {"preopen-stayopen-folders", NULL,
+ F_PREOPEN_STAYOPENS, h_config_preopen_stayopens, PREF_MISC, 0},
+ {"preserve-start-stop-characters", "Preserve Start/Stop Characters",
+ F_PRESERVE_START_STOP, h_config_preserve_start_stop, PREF_OS_STSP, 0},
+ {"quell-folder-internal-msg", "Prevent Folder Internal Message",
+ F_QUELL_INTERNAL_MSG, h_config_quell_folder_internal_msg, PREF_MISC, 0},
+ {"quell-partial-fetching", "Prevent Partial Fetching",
+ F_QUELL_PARTIAL_FETCH, h_config_quell_partial, PREF_MISC, 0},
+ {"prune-uses-yyyy-mm", "Prune Uses YYYY-MM",
+ F_PRUNE_USES_ISO, h_config_prune_uses_iso, PREF_MISC, 0},
+ {"quit-without-confirm", "Quit Without Confirming",
+ F_QUIT_WO_CONFIRM, h_config_quit_wo_confirm, PREF_MISC, 0},
+ {"quote-replace-nonflowed", NULL,
+ F_QUOTE_REPLACE_NOFLOW, h_config_quote_replace_noflow, PREF_MISC, 0},
+ {"save-aggregates-copy-sequence", "Save Combines Copies (may be out of order)",
+ F_AGG_SEQ_COPY, h_config_save_aggregates, PREF_MISC, 1},
+ {"save-partial-msg-without-confirm", "Save Partial Message Without Confirming",
+ F_SAVE_PARTIAL_WO_CONFIRM, h_config_save_part_wo_confirm, PREF_MISC, 0},
+ {"save-will-advance", NULL,
+ F_SAVE_ADVANCES, h_config_save_advances, PREF_MISC, 0},
+ {"save-will-not-delete", NULL,
+ F_SAVE_WONT_DELETE, h_config_save_wont_delete, PREF_MISC, 0},
+ {"save-will-quote-leading-froms", NULL,
+ F_QUOTE_ALL_FROMS, h_config_quote_all_froms, PREF_MISC, 0},
+ {"scramble-message-id", "Scramble the Message-ID When Sending",
+ F_ROT13_MESSAGE_ID, h_config_scramble_message_id, PREF_MISC, 0},
+ {"select-without-confirm", "Select Ctrl-T Foldername Without Confirming",
+ F_SELECT_WO_CONFIRM, h_config_select_wo_confirm, PREF_MISC, 0},
+ {"show-cursor", NULL,
+ F_SHOW_CURSOR, h_config_show_cursor, PREF_MISC, 0},
+ {"show-plain-text-internally", NULL,
+ F_SHOW_TEXTPLAIN_INT, h_config_textplain_int, PREF_MISC, 0},
+ {"show-selected-in-boldface", "Show Selected in Boldface",
+ F_SELECTED_SHOWN_BOLD, h_config_select_in_bold, PREF_MISC, 0},
+ {"slash-collapses-entire-thread", NULL,
+ F_SLASH_COLL_ENTIRE, h_config_slash_coll_entire, PREF_MISC, 0},
+#ifdef _WINDOWS
+ {"store-window-position-in-config", "Store Window Position in Config",
+ F_STORE_WINPOS_IN_CONFIG, h_config_winpos_in_config, PREF_MISC, 0},
+#endif
+ {"suppress-asterisks-in-password-prompt", "Suppress Asterisks in Password Prompt",
+ F_QUELL_ASTERISKS, h_config_quell_asterisks,
+ PREF_MISC, 0},
+ {"quell-attachment-extension-warn", "Suppress Attachment Extension Warning",
+ F_QUELL_ATTACH_EXT_WARN, h_config_quell_attach_ext_warn,
+ PREF_MISC, 0},
+ {"quell-attachment-extra-prompt", "Suppress Attachment Extra Prompt",
+ F_QUELL_ATTACH_EXTRA_PROMPT, h_config_quell_attach_extra_prompt,
+ PREF_MISC, 0},
+ {"quell-berkeley-format-timezone", "Suppress Berkeley Format Timezone",
+ F_QUELL_BEZERK_TIMEZONE, h_config_no_bezerk_zone, PREF_MISC, 0},
+ {"quell-content-id", "Suppress Content-ID",
+ F_QUELL_CONTENT_ID, h_config_quell_content_id, PREF_MISC, 0},
+ {"quell-filtering-done-message", "Suppress Filtering Done Message",
+ F_QUELL_FILTER_DONE_MSG, h_config_quell_filtering_done_message,
+ PREF_MISC, 0},
+ {"quell-filtering-messages", "Suppress Filtering Messages",
+ F_QUELL_FILTER_MSGS, h_config_quell_filtering_messages,
+ PREF_MISC, 0},
+ {"quell-imap-envelope-update", "Suppress IMAP Envelope Update",
+ F_QUELL_IMAP_ENV_CB, h_config_quell_imap_env, PREF_MISC, 0},
+ {"quell-lock-failure-warnings", "Suppress Lock Failure Warnings",
+ F_QUELL_LOCK_FAILURE_MSGS, h_config_quell_lock_failure_warnings,
+ PREF_MISC, 0},
+ {"quell-maildomain-warning", "Suppress Maildomain Warning",
+ F_QUELL_MAILDOMAIN_WARNING, h_config_quell_domain_warn, PREF_MISC, 0},
+ {"quell-news-envelope-update", "Suppress News Envelope Update",
+ F_QUELL_NEWS_ENV_CB, h_config_quell_news_env, PREF_MISC, 0},
+#ifdef _WINDOWS
+ {"quell-ssl-largeblocks", "Prevent SSL Largeblocks",
+ F_QUELL_SSL_LARGEBLOCKS, h_config_quell_ssl_largeblocks, PREF_MISC, 0},
+#endif
+ {"quell-status-message-beeping", "Suppress Status Message Beeping",
+ F_QUELL_BEEPS, h_config_quell_beeps, PREF_MISC, 0},
+ {"quell-timezone-comment-when-sending", "Suppress Timezone Comment When Sending",
+ F_QUELL_TIMEZONE, h_config_quell_tz_comment, PREF_MISC, 0},
+ {"suppress-user-agent-when-sending", NULL,
+ F_QUELL_USERAGENT, h_config_suppress_user_agent, PREF_MISC, 0},
+ {"tab-checks-recent", "Tab Checks for Recent Messages",
+ F_TAB_CHK_RECENT, h_config_tab_checks_recent, PREF_MISC, 0},
+ {"termdef-takes-precedence", NULL,
+ F_TCAP_WINS, h_config_termcap_wins, PREF_MISC, 0},
+ {"try-alternative-authentication-driver-first", NULL,
+ F_PREFER_ALT_AUTH, h_config_alt_auth, PREF_MISC, 0},
+ {"unselect-will-not-advance", NULL,
+ F_UNSELECT_WONT_ADVANCE, h_config_unsel_wont_advance, PREF_MISC, 0},
+ {"use-current-dir", "Use Current Directory",
+ F_USE_CURRENT_DIR, h_config_use_current_dir, PREF_MISC, 0},
+ {"use-function-keys", NULL,
+ F_USE_FK, h_config_use_fk, PREF_OS_USFK, 0},
+ {"use-regular-startup-rule-for-stayopen-folders", "Use Regular Startup Rule for Stayopen Folders",
+ F_STARTUP_STAYOPEN, h_config_use_reg_start_for_stayopen, PREF_MISC, 0},
+ {"use-resent-to-in-rules", "Use Resent-To in Rules",
+ F_USE_RESENTTO, h_config_use_resentto, PREF_MISC, 0},
+ {"use-subshell-for-suspend", "Use Subshell for Suspend",
+ F_SUSPEND_SPAWNS, h_config_suspend_spawns, PREF_OS_SPWN, 0},
+#ifndef _WINDOWS
+ {"use-system-translation", NULL,
+ F_USE_SYSTEM_TRANS, h_config_use_system_translation, PREF_MISC, 0},
+#endif
+
+/* Hidden Features */
+ {"old-growth", NULL,
+ F_OLD_GROWTH, NO_HELP, PREF_NONE, 0},
+ {"disable-config-cmd", NULL,
+ F_DISABLE_CONFIG_SCREEN, h_config_disable_config_cmd, PREF_HIDDEN, 0},
+ {"disable-keyboard-lock-cmd", NULL,
+ F_DISABLE_KBLOCK_CMD, h_config_disable_kb_lock, PREF_HIDDEN, 0},
+ {"disable-password-cmd", NULL,
+ F_DISABLE_PASSWORD_CMD, h_config_disable_password_cmd, PREF_HIDDEN, 0},
+ {"disable-pipes-in-sigs", NULL,
+ F_DISABLE_PIPES_IN_SIGS, h_config_disable_pipes_in_sigs, PREF_HIDDEN, 0},
+ {"disable-pipes-in-templates", NULL,
+ F_DISABLE_PIPES_IN_TEMPLATES, h_config_disable_pipes_in_templates,
+ PREF_HIDDEN, 0},
+ {"disable-roles-setup-cmd", NULL,
+ F_DISABLE_ROLES_SETUP, h_config_disable_roles_setup, PREF_HIDDEN, 0},
+ {"disable-roles-sig-edit", NULL,
+ F_DISABLE_ROLES_SIGEDIT, h_config_disable_roles_sigedit, PREF_HIDDEN, 0},
+ {"disable-roles-template-edit", NULL,
+ F_DISABLE_ROLES_TEMPLEDIT, h_config_disable_roles_templateedit,
+ PREF_HIDDEN, 0},
+ {"disable-setlocale-collate", NULL,
+ F_DISABLE_SETLOCALE_COLLATE, h_config_disable_collate, PREF_HIDDEN, 0},
+ {"disable-shared-namespaces", NULL,
+ F_DISABLE_SHARED_NAMESPACES, h_config_disable_shared, PREF_HIDDEN, 0},
+ {"disable-signature-edit-cmd", NULL,
+ F_DISABLE_SIGEDIT_CMD, h_config_disable_signature_edit, PREF_HIDDEN, 0},
+ {"new-thread-on-blank-subject", "New Thread on Blank Subject",
+ F_NEW_THREAD_ON_BLANK_SUBJECT, h_config_new_thread_blank_subject, PREF_HIDDEN, 1},
+ {"quell-personal-name-prompt", NULL,
+ F_QUELL_PERSONAL_NAME_PROMPT, h_config_quell_personal_name_prompt, PREF_HIDDEN, 0},
+ {"quell-user-id-prompt", "Quell User ID Prompt",
+ F_QUELL_USER_ID_PROMPT, h_config_quell_user_id_prompt, PREF_HIDDEN, 0},
+#ifdef SMIME
+ {"smime-dont-do-smime", "S/MIME -- Turn off S/MIME",
+ F_DONT_DO_SMIME, h_config_smime_dont_do_smime, PREF_HIDDEN, 0},
+ {"smime-encrypt-by-default", "S/MIME -- Encrypt by Default",
+ F_ENCRYPT_DEFAULT_ON, h_config_smime_encrypt_by_default, PREF_HIDDEN, 0},
+ {"smime-remember-passphrase", "S/MIME -- Remember S/MIME Passphrase",
+ F_REMEMBER_SMIME_PASSPHRASE, h_config_smime_remember_passphrase, PREF_HIDDEN, 0},
+ {"smime-sign-by-default", "S/MIME -- Sign by Default",
+ F_SIGN_DEFAULT_ON, h_config_smime_sign_by_default, PREF_HIDDEN, 0},
+#ifdef APPLEKEYCHAIN
+ {"publiccerts-in-keychain", "S/MIME -- Public Certs in MacOS Keychain",
+ F_PUBLICCERTS_IN_KEYCHAIN, h_config_smime_pubcerts_in_keychain, PREF_HIDDEN, 0},
+#endif
+#endif
+ {"selectable-item-nobold", NULL,
+ F_SLCTBL_ITEM_NOBOLD, NO_HELP, PREF_NONE, 0},
+ {"send-confirms-only-expanded", NULL, /* exposed in Web Alpine */
+ F_SEND_CONFIRM_ON_EXPAND, h_config_send_confirms_only_expanded, PREF_HIDDEN, 0},
+ {"enable-jump-cmd", NULL, /* exposed in Web Alpine */
+ F_ENABLE_JUMP_CMD, h_config_enable_jump_command, PREF_HIDDEN, 0},
+ {"enable-newmail-sound", NULL, /* exposed in Web Alpine */
+ F_ENABLE_NEWMAIL_SOUND, h_config_enable_newmail_sound, PREF_HIDDEN, 0},
+ {"render-html-internally", NULL, /* exposed in Web Alpine */
+ F_RENDER_HTML_INTERNALLY, h_config_render_html_internally, PREF_HIDDEN, 0}
+ };
+
+ return((index >= 0 && index < (sizeof(feat_list)/sizeof(feat_list[0])))
+ ? &feat_list[index] : NULL);
+}
+
+
+/*
+ * feature_list_index -- return index of given feature id in
+ * feature list
+ */
+int
+feature_list_index(int id)
+{
+ FEATURE_S *feature;
+ int i;
+
+ for(i = 0; (feature = feature_list(i)); i++)
+ if(id == feature->id)
+ return(i);
+
+ return(-1);
+}
+
+
+/*
+ * feature_list_name -- return the given feature id's corresponding name
+ */
+char *
+feature_list_name(int id)
+{
+ FEATURE_S *f;
+
+ return((f = feature_list(feature_list_index(id))) ? f->name : "");
+}
+
+
+int
+feature_list_id(char *name)
+{
+ FEATURE_S *f;
+ int i;
+
+ for(i = 0; (f = feature_list(i)); i++)
+ if(!strucmp(f->name, name))
+ return(f->id);
+
+ return(-1);
+}
+
+
+/*
+ * feature_list_help -- return the given feature id's corresponding help
+ */
+HelpType
+feature_list_help(int id)
+{
+ FEATURE_S *f;
+
+ return((f = feature_list(feature_list_index(id))) ? f->help : NO_HELP);
+}
+
+
+/*
+ * All the arguments past "list" are the backwards compatibility hacks.
+ */
+void
+process_feature_list(struct pine *ps, char **list, int old_growth, int hir, int osr)
+{
+ register char *q;
+ char **p,
+ *lvalue[BM_SIZE * 8];
+ int i,
+ yorn;
+ long l;
+ FEATURE_S *feat;
+
+
+ /* clear all previous settings and reset them to default */
+ for(i = 0; (feat = feature_list(i)) != NULL; i++)
+ F_SET(feat->id, ps, feat->defval);
+
+ /* backwards compatibility */
+ if(hir)
+ F_TURN_ON(F_INCLUDE_HEADER, ps);
+
+ /* ditto */
+ if(osr)
+ F_TURN_ON(F_SIG_AT_BOTTOM, ps);
+
+ /* ditto */
+ if(old_growth)
+ set_old_growth_bits(ps, 0);
+
+ /* now run through the list (global, user, and cmd_line lists are here) */
+ if(list){
+ for(p = list; (q = *p) != NULL; p++){
+ if(struncmp(q, "no-", 3) == 0){
+ yorn = 0;
+ q += 3;
+ }else{
+ yorn = 1;
+ }
+
+ for(i = 0; (feat = feature_list(i)) != NULL; i++){
+ if(strucmp(q, feat->name) == 0){
+ if(feat->id == F_OLD_GROWTH){
+ set_old_growth_bits(ps, yorn);
+ }else{
+ F_SET(feat->id, ps, yorn);
+ }
+ break;
+ }
+ }
+
+ /* if it wasn't in that list */
+ if(feat == NULL)
+ dprint((1,"Unrecognized feature in feature-list (%s%s)\n",
+ (yorn ? "" : "no-"), q ? q : "?"));
+ }
+ }
+
+ /*
+ * Turn on gratuitous '>From ' quoting, if requested...
+ */
+ mail_parameters(NULL, SET_FROMWIDGET,
+ F_ON(F_QUOTE_ALL_FROMS, ps) ? VOIDT : NIL);
+
+ /*
+ * Turn off .lock creation complaints...
+ */
+ if(F_ON(F_QUELL_LOCK_FAILURE_MSGS, ps))
+ mail_parameters(NULL, SET_LOCKEACCESERROR, (void *) 0);
+
+ /*
+ * Turn on quelling of pseudo message.
+ */
+ if(F_ON(F_QUELL_INTERNAL_MSG,ps_global))
+ mail_parameters(NULL, SET_USERHASNOLIFE, (void *) 1);
+
+ l = F_ON(F_MULNEWSRC_HOSTNAMES_AS_TYPED,ps_global) ? 0L : 1L;
+ mail_parameters(NULL, SET_NEWSRCCANONHOST, (void *) l);
+
+ ps->pass_ctrl_chars = F_ON(F_PASS_CONTROL_CHARS,ps_global) ? 1 : 0;
+ ps->pass_c1_ctrl_chars = F_ON(F_PASS_C1_CONTROL_CHARS,ps_global) ? 1 : 0;
+
+#ifndef _WINDOWS
+ if(F_ON(F_QUELL_BEZERK_TIMEZONE,ps_global))
+ mail_parameters(NULL, SET_NOTIMEZONES, (void *) 1);
+#endif
+
+ if(F_ON(F_USE_FK, ps))
+ ps->orig_use_fkeys = 1;
+
+ /* Will we have to build a new list? */
+ if(!(old_growth || hir || osr))
+ return;
+
+ /*
+ * Build a new list for feature-list. The only reason we ever need to
+ * do this is if one of the obsolete options is being converted
+ * into a feature-list item, and it isn't already included in the user's
+ * feature-list.
+ */
+ i = 0;
+ for(p = LVAL(&ps->vars[V_FEATURE_LIST], Main);
+ p && (q = *p); p++){
+ /* already have it or cancelled it, don't need to add later */
+ if(hir && (strucmp(q, "include-header-in-reply") == 0 ||
+ strucmp(q, "no-include-header-in-reply") == 0)){
+ hir = 0;
+ }else if(osr && (strucmp(q, "signature-at-bottom") == 0 ||
+ strucmp(q, "no-signature-at-bottom") == 0)){
+ osr = 0;
+ }else if(old_growth && (strucmp(q, "old-growth") == 0 ||
+ strucmp(q, "no-old-growth") == 0)){
+ old_growth = 0;
+ }
+ lvalue[i++] = cpystr(q);
+ }
+
+ /* check to see if we still need to build a new list */
+ if(!(old_growth || hir || osr))
+ return;
+
+ if(hir)
+ lvalue[i++] = "include-header-in-reply";
+ if(osr)
+ lvalue[i++] = "signature-at-bottom";
+ if(old_growth)
+ lvalue[i++] = "old-growth";
+ lvalue[i] = NULL;
+ set_variable_list(V_FEATURE_LIST, lvalue, TRUE, Main);
+}
+
+
+void
+set_current_pattern_vals(struct pine *ps)
+{
+ struct variable *vars = ps->vars;
+
+ set_current_val(&vars[V_PATTERNS], TRUE, TRUE);
+ set_current_val(&vars[V_PAT_ROLES], TRUE, TRUE);
+ set_current_val(&vars[V_PAT_FILTS], TRUE, TRUE);
+ set_current_val(&vars[V_PAT_FILTS_OLD], TRUE, TRUE);
+ set_current_val(&vars[V_PAT_SCORES], TRUE, TRUE);
+ set_current_val(&vars[V_PAT_SCORES_OLD], TRUE, TRUE);
+ set_current_val(&vars[V_PAT_INCOLS], TRUE, TRUE);
+ set_current_val(&vars[V_PAT_OTHER], TRUE, TRUE);
+ set_current_val(&vars[V_PAT_SRCH], TRUE, TRUE);
+
+ /*
+ * If old pattern variable (V_PATTERNS) is set and the new ones aren't
+ * in the config file, then convert the old data into the new variables.
+ * It isn't quite that simple, though, because we don't store unset
+ * variables in remote pinercs. Check for the variables but if we
+ * don't find any of them, also check the version number. This change was
+ * made in version 4.30. We could just check that except that we're
+ * worried somebody will make an incompatible version number change in
+ * their local version, and will break this. So we check both the
+ * version # and the var_in_pinerc things to be safer.
+ */
+ if(vars[V_PATTERNS].current_val.l
+ && vars[V_PATTERNS].current_val.l[0]
+ && !var_in_pinerc(vars[V_PAT_ROLES].name)
+ && !var_in_pinerc(vars[V_PAT_FILTS].name)
+ && !var_in_pinerc(vars[V_PAT_FILTS_OLD].name)
+ && !var_in_pinerc(vars[V_PAT_SCORES].name)
+ && !var_in_pinerc(vars[V_PAT_SCORES_OLD].name)
+ && !var_in_pinerc(vars[V_PAT_INCOLS].name)
+ && isdigit((unsigned char) ps->pine_pre_vers[0])
+ && ps->pine_pre_vers[1] == '.'
+ && isdigit((unsigned char) ps->pine_pre_vers[2])
+ && isdigit((unsigned char) ps->pine_pre_vers[3])
+ && strncmp(ps->pine_pre_vers, "4.30", 4) < 0){
+ convert_pattern_data();
+ }
+
+ /*
+ * Otherwise, if FILTS_OLD is set and FILTS isn't in the config file,
+ * convert FILTS_OLD to FILTS. Same for SCORES.
+ * The reason FILTS was changed was so we could change the
+ * semantics of how rules work when there are pieces in the rule that
+ * we don't understand. At the same time as the FILTS change we added
+ * a rule to detect 8bitSubjects. So a user might have a filter that
+ * deletes messages with 8bitSubjects. The problem is that that same
+ * filter in a FILTS_OLD pine would match because it would ignore the
+ * 8bitSubject part of the pattern and match on the rest. So we changed
+ * the semantics so that rules with unknown bits would be ignored
+ * instead of used. We had to change variable names at the same time
+ * because we were adding the 8bit thing and the old pines are still
+ * out there. Filters and Scores can both be dangerous. Roles, Colors,
+ * and Other seem less dangerous so not worth adding a new variable.
+ * This was changed in 4.50.
+ */
+ else{
+ if(vars[V_PAT_FILTS_OLD].current_val.l
+ && vars[V_PAT_FILTS_OLD].current_val.l[0]
+ && !var_in_pinerc(vars[V_PAT_FILTS].name)
+ && !var_in_pinerc(vars[V_PAT_SCORES].name)
+ && isdigit((unsigned char) ps->pine_pre_vers[0])
+ && ps->pine_pre_vers[1] == '.'
+ && isdigit((unsigned char) ps->pine_pre_vers[2])
+ && isdigit((unsigned char) ps->pine_pre_vers[3])
+ && strncmp(ps->pine_pre_vers, "4.50", 4) < 0){
+ convert_filts_pattern_data();
+ }
+
+ if(vars[V_PAT_SCORES_OLD].current_val.l
+ && vars[V_PAT_SCORES_OLD].current_val.l[0]
+ && !var_in_pinerc(vars[V_PAT_FILTS].name)
+ && !var_in_pinerc(vars[V_PAT_SCORES].name)
+ && isdigit((unsigned char) ps->pine_pre_vers[0])
+ && ps->pine_pre_vers[1] == '.'
+ && isdigit((unsigned char) ps->pine_pre_vers[2])
+ && isdigit((unsigned char) ps->pine_pre_vers[3])
+ && strncmp(ps->pine_pre_vers, "4.50", 4) < 0){
+ convert_scores_pattern_data();
+ }
+ }
+
+ if(vars[V_PAT_ROLES].post_user_val.l)
+ ps_global->ew_for_role_take = Post;
+ else
+ ps_global->ew_for_role_take = Main;
+
+ if(vars[V_PAT_FILTS].post_user_val.l)
+ ps_global->ew_for_filter_take = Post;
+ else
+ ps_global->ew_for_filter_take = Main;
+
+ if(vars[V_PAT_SCORES].post_user_val.l)
+ ps_global->ew_for_score_take = Post;
+ else
+ ps_global->ew_for_score_take = Main;
+
+ if(vars[V_PAT_INCOLS].post_user_val.l)
+ ps_global->ew_for_incol_take = Post;
+ else
+ ps_global->ew_for_incol_take = Main;
+
+ if(vars[V_PAT_OTHER].post_user_val.l)
+ ps_global->ew_for_other_take = Post;
+ else
+ ps_global->ew_for_other_take = Main;
+
+ if(vars[V_PAT_SRCH].post_user_val.l)
+ ps_global->ew_for_srch_take = Post;
+ else
+ ps_global->ew_for_srch_take = Main;
+}
+
+
+/*
+ * Foreach of the config files;
+ * transfer the data to the new variables.
+ */
+void
+convert_pattern_data(void)
+{
+ convert_pinerc_patterns(PAT_USE_MAIN);
+ convert_pinerc_patterns(PAT_USE_POST);
+}
+
+
+void
+convert_filts_pattern_data(void)
+{
+ convert_pinerc_filts_patterns(PAT_USE_MAIN);
+ convert_pinerc_filts_patterns(PAT_USE_POST);
+}
+
+
+void
+convert_scores_pattern_data(void)
+{
+ convert_pinerc_scores_patterns(PAT_USE_MAIN);
+ convert_pinerc_scores_patterns(PAT_USE_POST);
+}
+
+
+/*
+ * Foreach of the four variables, transfer the data for this config file
+ * from the old patterns variable. We don't have to convert OTHER patterns
+ * or SRCH patterns because they didn't exist in pines without patterns-other.
+ *
+ * If the original variable had patlines with type File then we convert
+ * all of the individual patterns to type Lit, because each pattern can
+ * be of any category. Lit patterns are better tested, anyway.
+ */
+void
+convert_pinerc_patterns(long int use_flags)
+{
+ long old_rflags;
+ long rflags;
+ PAT_S *pat;
+ PAT_STATE pstate;
+ ACTION_S *act;
+
+ old_rflags = (ROLE_OLD_PAT | use_flags);
+
+ rflags = 0L;
+ if(any_patterns(old_rflags, &pstate)){
+ dprint((2, "converting old patterns to new (%s)\n", (use_flags == PAT_USE_MAIN) ? "Main" : "Post"));
+ for(pat = first_pattern(&pstate);
+ pat;
+ pat = next_pattern(&pstate)){
+ if((act = pat->action) != NULL){
+ if(act->is_a_role &&
+ add_to_pattern(pat, ROLE_DO_ROLES | use_flags))
+ rflags |= ROLE_DO_ROLES;
+ if(act->is_a_incol &&
+ add_to_pattern(pat, ROLE_DO_INCOLS | use_flags))
+ rflags |= ROLE_DO_INCOLS;
+ if(act->is_a_score &&
+ add_to_pattern(pat, ROLE_DO_SCORES | use_flags))
+ rflags |= ROLE_DO_SCORES;
+ if(act->is_a_filter &&
+ add_to_pattern(pat, ROLE_DO_FILTER | use_flags))
+ rflags |= ROLE_DO_FILTER;
+ }
+ }
+
+ if(rflags)
+ if(write_patterns(rflags | use_flags))
+ dprint((1,
+ "Trouble converting patterns to new variable\n"));
+ }
+}
+
+
+/*
+ * If the original variable had patlines with type File then we convert
+ * all of the individual patterns to type Lit, because each pattern can
+ * be of any category. Lit patterns are better tested, anyway.
+ */
+void
+convert_pinerc_filts_patterns(long int use_flags)
+{
+ long old_rflags;
+ long rflags;
+ PAT_S *pat;
+ PAT_STATE pstate;
+ ACTION_S *act;
+
+ old_rflags = (ROLE_OLD_FILT | use_flags);
+
+ rflags = 0L;
+ if(any_patterns(old_rflags, &pstate)){
+ dprint((2, "converting old filter patterns to new (%s)\n", (use_flags == PAT_USE_MAIN) ? "Main" : "Post"));
+ for(pat = first_pattern(&pstate);
+ pat;
+ pat = next_pattern(&pstate)){
+ if((act = pat->action) != NULL){
+ if(act->is_a_filter &&
+ add_to_pattern(pat, ROLE_DO_FILTER | use_flags))
+ rflags |= ROLE_DO_FILTER;
+ }
+ }
+
+ if(rflags)
+ if(write_patterns(rflags | use_flags))
+ dprint((1,
+ "Trouble converting filter patterns to new variable\n"));
+ }
+}
+
+
+/*
+ * If the original variable had patlines with type File then we convert
+ * all of the individual patterns to type Lit, because each pattern can
+ * be of any category. Lit patterns are better tested, anyway.
+ */
+void
+convert_pinerc_scores_patterns(long int use_flags)
+{
+ long old_rflags;
+ long rflags;
+ PAT_S *pat;
+ PAT_STATE pstate;
+ ACTION_S *act;
+
+ old_rflags = (ROLE_OLD_SCORE | use_flags);
+
+ rflags = 0L;
+ if(any_patterns(old_rflags, &pstate)){
+ dprint((2, "converting old scores patterns to new (%s)\n", (use_flags == PAT_USE_MAIN) ? "Main" : "Post"));
+ for(pat = first_pattern(&pstate);
+ pat;
+ pat = next_pattern(&pstate)){
+ if((act = pat->action) != NULL){
+ if(act->is_a_score &&
+ add_to_pattern(pat, ROLE_DO_SCORES | use_flags))
+ rflags |= ROLE_DO_SCORES;
+ }
+ }
+
+ if(rflags)
+ if(write_patterns(rflags | use_flags))
+ dprint((1,
+ "Trouble converting scores patterns to new variable\n"));
+ }
+}
+
+
+/*
+ * set_old_growth_bits - Command used to set or unset old growth set
+ * of features
+ */
+void
+set_old_growth_bits(struct pine *ps, int val)
+{
+ int i;
+
+ for(i = 1; i <= F_FEATURE_LIST_COUNT; i++)
+ if(test_old_growth_bits(ps, i))
+ F_SET(i, ps, val);
+}
+
+
+
+/*
+ * test_old_growth_bits - Test to see if all the old growth bits are on,
+ * *or* if a particular feature index is in the old
+ * growth set.
+ *
+ * WEIRD ALERT: if index == F_OLD_GROWTH bit values are tested
+ * otherwise a bits existence in the set is tested!!!
+ *
+ * BUG: this will break if an old growth feature number is ever >= 32.
+ */
+int
+test_old_growth_bits(struct pine *ps, int index)
+{
+ /*
+ * this list defines F_OLD_GROWTH set
+ */
+ static unsigned long old_growth_bits = ((1 << F_ENABLE_FULL_HDR) |
+ (1 << F_ENABLE_PIPE) |
+ (1 << F_ENABLE_TAB_COMPLETE) |
+ (1 << F_QUIT_WO_CONFIRM) |
+ (1 << F_ENABLE_JUMP) |
+ (1 << F_ENABLE_ALT_ED) |
+ (1 << F_ENABLE_BOUNCE) |
+ (1 << F_ENABLE_AGG_OPS) |
+ (1 << F_ENABLE_FLAG) |
+ (1 << F_CAN_SUSPEND));
+ if(index >= 32)
+ return(0);
+
+ if(index == F_OLD_GROWTH){
+ for(index = 1; index <= F_FEATURE_LIST_COUNT; index++)
+ if(((1 << index) & old_growth_bits) && F_OFF(index, ps))
+ return(0);
+
+ return(1);
+ }
+ else
+ return((1 << index) & old_growth_bits);
+}
+
+
+/*
+ * Side effect is that the appropriate global variable is set, and the
+ * appropriate current_val is set.
+ */
+void
+cur_rule_value(struct variable *var, int expand, int cmdline)
+{
+ int i;
+ NAMEVAL_S *v;
+
+ set_current_val(var, expand, cmdline);
+
+ if(var == &ps_global->vars[V_SAVED_MSG_NAME_RULE]){
+ if(ps_global->VAR_SAVED_MSG_NAME_RULE)
+ for(i = 0; (v = save_msg_rules(i)); i++)
+ if(!strucmp(ps_global->VAR_SAVED_MSG_NAME_RULE, S_OR_L(v))){
+ ps_global->save_msg_rule = v->value;
+ break;
+ }
+ }
+#ifndef _WINDOWS
+ else if(var == &ps_global->vars[V_COLOR_STYLE]){
+ if(ps_global->VAR_COLOR_STYLE)
+ for(i = 0; (v = col_style(i)); i++)
+ if(!strucmp(ps_global->VAR_COLOR_STYLE, S_OR_L(v))){
+ ps_global->color_style = v->value;
+ break;
+ }
+ }
+#endif
+ else if(var == &ps_global->vars[V_INDEX_COLOR_STYLE]){
+ if(ps_global->VAR_INDEX_COLOR_STYLE)
+ for(i = 0; (v = index_col_style(i)); i++)
+ if(!strucmp(ps_global->VAR_INDEX_COLOR_STYLE, S_OR_L(v))){
+ ps_global->index_color_style = v->value;
+ break;
+ }
+ }
+ else if(var == &ps_global->vars[V_TITLEBAR_COLOR_STYLE]){
+ if(ps_global->VAR_TITLEBAR_COLOR_STYLE)
+ for(i = 0; (v = titlebar_col_style(i)); i++)
+ if(!strucmp(ps_global->VAR_TITLEBAR_COLOR_STYLE, S_OR_L(v))){
+ ps_global->titlebar_color_style = v->value;
+ break;
+ }
+ }
+ else if(var == &ps_global->vars[V_FCC_RULE]){
+ if(ps_global->VAR_FCC_RULE)
+ for(i = 0; (v = fcc_rules(i)); i++)
+ if(!strucmp(ps_global->VAR_FCC_RULE, S_OR_L(v))){
+ ps_global->fcc_rule = v->value;
+ break;
+ }
+ }
+ else if(var == &ps_global->vars[V_GOTO_DEFAULT_RULE]){
+ if(ps_global->VAR_GOTO_DEFAULT_RULE)
+ for(i = 0; (v = goto_rules(i)); i++)
+ if(!strucmp(ps_global->VAR_GOTO_DEFAULT_RULE, S_OR_L(v))){
+ ps_global->goto_default_rule = v->value;
+ break;
+ }
+ }
+ else if(var == &ps_global->vars[V_INCOMING_STARTUP]){
+ if(ps_global->VAR_INCOMING_STARTUP)
+ for(i = 0; (v = incoming_startup_rules(i)); i++)
+ if(!strucmp(ps_global->VAR_INCOMING_STARTUP, S_OR_L(v))){
+ ps_global->inc_startup_rule = v->value;
+ break;
+ }
+ }
+ else if(var == &ps_global->vars[V_PRUNING_RULE]){
+ if(ps_global->VAR_PRUNING_RULE)
+ for(i = 0; (v = pruning_rules(i)); i++)
+ if(!strucmp(ps_global->VAR_PRUNING_RULE, S_OR_L(v))){
+ ps_global->pruning_rule = v->value;
+ break;
+ }
+ }
+ else if(var == &ps_global->vars[V_REOPEN_RULE]){
+ if(ps_global->VAR_REOPEN_RULE)
+ for(i = 0; (v = reopen_rules(i)); i++)
+ if(!strucmp(ps_global->VAR_REOPEN_RULE, S_OR_L(v))){
+ ps_global->reopen_rule = v->value;
+ break;
+ }
+ }
+ else if(var == &ps_global->vars[V_FLD_SORT_RULE]){
+ if(ps_global->VAR_FLD_SORT_RULE)
+ for(i = 0; (v = fld_sort_rules(i)); i++)
+ if(!strucmp(ps_global->VAR_FLD_SORT_RULE, S_OR_L(v))){
+ ps_global->fld_sort_rule = v->value;
+ break;
+ }
+ }
+ else if(var == &ps_global->vars[V_AB_SORT_RULE]){
+ if(ps_global->VAR_AB_SORT_RULE)
+ for(i = 0; (v = ab_sort_rules(i)); i++)
+ if(!strucmp(ps_global->VAR_AB_SORT_RULE, S_OR_L(v))){
+ ps_global->ab_sort_rule = v->value;
+ break;
+ }
+ }
+ else if(var == &ps_global->vars[V_THREAD_DISP_STYLE]){
+ if(ps_global->VAR_THREAD_DISP_STYLE)
+ for(i = 0; (v = thread_disp_styles(i)); i++)
+ if(!strucmp(ps_global->VAR_THREAD_DISP_STYLE, S_OR_L(v))){
+ ps_global->thread_disp_style = v->value;
+ break;
+ }
+ }
+ else if(var == &ps_global->vars[V_THREAD_INDEX_STYLE]){
+ if(ps_global->VAR_THREAD_INDEX_STYLE)
+ for(i = 0; (v = thread_index_styles(i)); i++)
+ if(!strucmp(ps_global->VAR_THREAD_INDEX_STYLE, S_OR_L(v))){
+ ps_global->thread_index_style = v->value;
+ break;
+ }
+ }
+}
+
+
+/*
+ * Standard way to get at save message rules...
+ */
+NAMEVAL_S *
+save_msg_rules(int index)
+{
+ static NAMEVAL_S save_rules[] = {
+ {"by-from", NULL, SAV_RULE_FROM},
+ {"by-nick-of-from", NULL, SAV_RULE_NICK_FROM_DEF},
+ {"by-nick-of-from-then-from", NULL, SAV_RULE_NICK_FROM},
+ {"by-fcc-of-from", NULL, SAV_RULE_FCC_FROM_DEF},
+ {"by-fcc-of-from-then-from", NULL, SAV_RULE_FCC_FROM},
+ {"by-realname-of-from", NULL, SAV_RULE_RN_FROM_DEF},
+ {"by-realname-of-from-then-from", NULL, SAV_RULE_RN_FROM},
+ {"by-sender", NULL, SAV_RULE_SENDER},
+ {"by-nick-of-sender", NULL, SAV_RULE_NICK_SENDER_DEF},
+ {"by-nick-of-sender-then-sender", NULL, SAV_RULE_NICK_SENDER},
+ {"by-fcc-of-sender", NULL, SAV_RULE_FCC_SENDER_DEF},
+ {"by-fcc-of-sender-then-sender", NULL, SAV_RULE_FCC_SENDER},
+ {"by-realname-of-sender", NULL, SAV_RULE_RN_SENDER_DEF},
+ {"by-realname-of-sender-then-sender", NULL, SAV_RULE_RN_SENDER},
+ {"by-recipient", NULL, SAV_RULE_RECIP},
+ {"by-nick-of-recip", NULL, SAV_RULE_NICK_RECIP_DEF},
+ {"by-nick-of-recip-then-recip", NULL, SAV_RULE_NICK_RECIP},
+ {"by-fcc-of-recip", NULL, SAV_RULE_FCC_RECIP_DEF},
+ {"by-fcc-of-recip-then-recip", NULL, SAV_RULE_FCC_RECIP},
+ {"by-realname-of-recip", NULL, SAV_RULE_RN_RECIP_DEF},
+ {"by-realname-of-recip-then-recip", NULL, SAV_RULE_RN_RECIP},
+ {"by-replyto", NULL, SAV_RULE_REPLYTO},
+ {"by-nick-of-replyto", NULL, SAV_RULE_NICK_REPLYTO_DEF},
+ {"by-nick-of-replyto-then-replyto", NULL, SAV_RULE_NICK_REPLYTO},
+ {"by-fcc-of-replyto", NULL, SAV_RULE_FCC_REPLYTO_DEF},
+ {"by-fcc-of-replyto-then-replyto", NULL, SAV_RULE_FCC_REPLYTO},
+ {"by-realname-of-replyto", NULL, SAV_RULE_RN_REPLYTO_DEF},
+ {"by-realname-of-replyto-then-replyto", NULL, SAV_RULE_RN_REPLYTO},
+ {"last-folder-used", NULL, SAV_RULE_LAST},
+ {"default-folder", NULL, SAV_RULE_DEFLT}
+ };
+
+ return((index >= 0 && index < (sizeof(save_rules)/sizeof(save_rules[0])))
+ ? &save_rules[index] : NULL);
+}
+
+
+/*
+ * Standard way to get at fcc rules...
+ */
+NAMEVAL_S *
+fcc_rules(int index)
+{
+ static NAMEVAL_S f_rules[] = {
+ {"default-fcc", NULL, FCC_RULE_DEFLT},
+ {"last-fcc-used", NULL, FCC_RULE_LAST},
+ {"by-recipient", NULL, FCC_RULE_RECIP},
+ {"by-nickname", NULL, FCC_RULE_NICK},
+ {"by-nick-then-recip", NULL, FCC_RULE_NICK_RECIP},
+ {"current-folder", NULL, FCC_RULE_CURRENT}
+ };
+
+ return((index >= 0 && index < (sizeof(f_rules)/sizeof(f_rules[0])))
+ ? &f_rules[index] : NULL);
+}
+
+
+/*
+ * Standard way to get at addrbook sort rules...
+ */
+NAMEVAL_S *
+ab_sort_rules(int index)
+{
+ static NAMEVAL_S ab_rules[] = {
+ {"fullname-with-lists-last", NULL, AB_SORT_RULE_FULL_LISTS},
+ {"fullname", NULL, AB_SORT_RULE_FULL},
+ {"nickname-with-lists-last", NULL, AB_SORT_RULE_NICK_LISTS},
+ {"nickname", NULL, AB_SORT_RULE_NICK},
+ {"dont-sort", NULL, AB_SORT_RULE_NONE}
+ };
+
+ return((index >= 0 && index < (sizeof(ab_rules)/sizeof(ab_rules[0])))
+ ? &ab_rules[index] : NULL);
+}
+
+
+/*
+ * Standard way to get at color styles.
+ */
+NAMEVAL_S *
+col_style(int index)
+{
+ static NAMEVAL_S col_styles[] = {
+ {"no-color", NULL, COL_NONE},
+ {"use-termdef", NULL, COL_TERMDEF},
+ {"force-ansi-8color", NULL, COL_ANSI8},
+ {"force-ansi-16color", NULL, COL_ANSI16},
+ {"force-xterm-256color", NULL, COL_ANSI256}
+ };
+
+ return((index >= 0 && index < (sizeof(col_styles)/sizeof(col_styles[0])))
+ ? &col_styles[index] : NULL);
+}
+
+
+/*
+ * Standard way to get at index color styles.
+ */
+NAMEVAL_S *
+index_col_style(int index)
+{
+ static NAMEVAL_S ind_col_styles[] = {
+ {"flip-colors", NULL, IND_COL_FLIP},
+ {"reverse", NULL, IND_COL_REV},
+ {"reverse-fg", NULL, IND_COL_FG},
+ {"reverse-fg-no-ambiguity", NULL, IND_COL_FG_NOAMBIG},
+ {"reverse-bg", NULL, IND_COL_BG},
+ {"reverse-bg-no-ambiguity", NULL, IND_COL_BG_NOAMBIG}
+ };
+
+ return((index >= 0 && index < (sizeof(ind_col_styles)/sizeof(ind_col_styles[0]))) ? &ind_col_styles[index] : NULL);
+}
+
+
+/*
+ * Standard way to get at titlebar color styles.
+ */
+NAMEVAL_S *
+titlebar_col_style(int index)
+{
+ static NAMEVAL_S tbar_col_styles[] = {
+ {"default", NULL, TBAR_COLOR_DEFAULT},
+ {"indexline", NULL, TBAR_COLOR_INDEXLINE},
+ {"reverse-indexline", NULL, TBAR_COLOR_REV_INDEXLINE}
+ };
+
+ return((index >= 0 && index < (sizeof(tbar_col_styles)/sizeof(tbar_col_styles[0]))) ? &tbar_col_styles[index] : NULL);
+}
+
+
+/*
+ * Standard way to get at folder sort rules...
+ */
+NAMEVAL_S *
+fld_sort_rules(int index)
+{
+ static NAMEVAL_S fdl_rules[] = {
+ {"alphabetical", NULL, FLD_SORT_ALPHA},
+ {"alpha-with-dirs-last", NULL, FLD_SORT_ALPHA_DIR_LAST},
+ {"alpha-with-dirs-first", NULL, FLD_SORT_ALPHA_DIR_FIRST}
+ };
+
+ return((index >= 0 && index < (sizeof(fdl_rules)/sizeof(fdl_rules[0])))
+ ? &fdl_rules[index] : NULL);
+}
+
+
+/*
+ * Standard way to get at incoming startup rules...
+ */
+NAMEVAL_S *
+incoming_startup_rules(int index)
+{
+ static NAMEVAL_S is_rules[] = {
+ {"first-unseen", NULL, IS_FIRST_UNSEEN},
+ {"first-recent", NULL, IS_FIRST_RECENT},
+ {"first-important", NULL, IS_FIRST_IMPORTANT},
+ {"first-important-or-unseen", NULL, IS_FIRST_IMPORTANT_OR_UNSEEN},
+ {"first-important-or-recent", NULL, IS_FIRST_IMPORTANT_OR_RECENT},
+ {"first", NULL, IS_FIRST},
+ {"last", NULL, IS_LAST}
+ };
+
+ return((index >= 0 && index < (sizeof(is_rules)/sizeof(is_rules[0])))
+ ? &is_rules[index] : NULL);
+}
+
+
+NAMEVAL_S *
+startup_rules(int index)
+{
+ static NAMEVAL_S is2_rules[] = {
+ {"first-unseen", NULL, IS_FIRST_UNSEEN},
+ {"first-recent", NULL, IS_FIRST_RECENT},
+ {"first-important", NULL, IS_FIRST_IMPORTANT},
+ {"first-important-or-unseen", NULL, IS_FIRST_IMPORTANT_OR_UNSEEN},
+ {"first-important-or-recent", NULL, IS_FIRST_IMPORTANT_OR_RECENT},
+ {"first", NULL, IS_FIRST},
+ {"last", NULL, IS_LAST},
+ {"default", NULL, IS_NOTSET}
+ };
+
+ return((index >= 0 && index < (sizeof(is2_rules)/sizeof(is2_rules[0])))
+ ? &is2_rules[index] : NULL);
+}
+
+
+/*
+ * Standard way to get at pruning-rule values.
+ */
+NAMEVAL_S *
+pruning_rules(int index)
+{
+ static NAMEVAL_S pr_rules[] = {
+ {"ask about rename, ask about deleting","ask-ask", PRUNE_ASK_AND_ASK},
+ {"ask about rename, don't delete", "ask-no", PRUNE_ASK_AND_NO},
+ {"always rename, ask about deleting", "yes-ask", PRUNE_YES_AND_ASK},
+ {"always rename, don't delete", "yes-no", PRUNE_YES_AND_NO},
+ {"don't rename, ask about deleting", "no-ask", PRUNE_NO_AND_ASK},
+ {"don't rename, don't delete", "no-no", PRUNE_NO_AND_NO}
+ };
+
+ return((index >= 0 && index < (sizeof(pr_rules)/sizeof(pr_rules[0])))
+ ? &pr_rules[index] : NULL);
+}
+
+
+/*
+ * Standard way to get at reopen-rule values.
+ */
+NAMEVAL_S *
+reopen_rules(int index)
+{
+ static NAMEVAL_S ro_rules[] = {
+ /* TRANSLATORS: short description of a feature option */
+ {"Always reopen", "yes-yes",
+ REOPEN_YES_YES},
+ /* TRANSLATORS: short description of a feature option, default in brackets */
+ {"Yes for POP/NNTP, Ask about other remote [Yes]", "yes-ask-y",
+ REOPEN_YES_ASK_Y},
+ /* TRANSLATORS: short description of a feature option, default in brackets */
+ {"Yes for POP/NNTP, Ask about other remote [No]", "yes-ask-n",
+ REOPEN_YES_ASK_N},
+ /* TRANSLATORS: short description of a feature option */
+ {"Yes for POP/NNTP, No for other remote", "yes-no",
+ REOPEN_YES_NO},
+ /* TRANSLATORS: short description of a feature option, default in brackets */
+ {"Always ask [Yes]", "ask-ask-y",
+ REOPEN_ASK_ASK_Y},
+ /* TRANSLATORS: short description of a feature option, default in brackets */
+ {"Always ask [No]", "ask-ask-n",
+ REOPEN_ASK_ASK_N},
+ /* TRANSLATORS: short description of a feature option, default in brackets */
+ {"Ask about POP/NNTP [Yes], No for other remote", "ask-no-y",
+ REOPEN_ASK_NO_Y},
+ /* TRANSLATORS: short description of a feature option, default in brackets */
+ {"Ask about POP/NNTP [No], No for other remote", "ask-no-n",
+ REOPEN_ASK_NO_N},
+ /* TRANSLATORS: short description of a feature option */
+ {"Never reopen", "no-no",
+ REOPEN_NO_NO},
+ };
+
+ return((index >= 0 && index < (sizeof(ro_rules)/sizeof(ro_rules[0])))
+ ? &ro_rules[index] : NULL);
+}
+
+
+/*
+ * Standard way to get at thread_disp_style values.
+ */
+NAMEVAL_S *
+thread_disp_styles(int index)
+{
+ static NAMEVAL_S td_styles[] = {
+ {"none", "none", THREAD_NONE},
+ {"show-thread-structure", "struct", THREAD_STRUCT},
+ {"mutt-like", "mutt", THREAD_MUTTLIKE},
+ {"indent-subject-1", "subj1", THREAD_INDENT_SUBJ1},
+ {"indent-subject-2", "subj2", THREAD_INDENT_SUBJ2},
+ {"indent-from-1", "from1", THREAD_INDENT_FROM1},
+ {"indent-from-2", "from2", THREAD_INDENT_FROM2},
+ {"show-structure-in-from", "struct-from", THREAD_STRUCT_FROM}
+ };
+
+ return((index >= 0 && index < (sizeof(td_styles)/sizeof(td_styles[0])))
+ ? &td_styles[index] : NULL);
+}
+
+
+/*
+ * Standard way to get at thread_index_style values.
+ */
+NAMEVAL_S *
+thread_index_styles(int index)
+{
+ static NAMEVAL_S ti_styles[] = {
+ {"regular-index-with-expanded-threads", "exp", THRDINDX_EXP},
+ {"regular-index-with-collapsed-threads","coll", THRDINDX_COLL},
+ {"separate-index-screen-always", "sep", THRDINDX_SEP},
+ {"separate-index-screen-except-for-single-messages","sep-auto",
+ THRDINDX_SEP_AUTO}
+ };
+
+ return((index >= 0 && index < (sizeof(ti_styles)/sizeof(ti_styles[0])))
+ ? &ti_styles[index] : NULL);
+}
+
+
+/*
+ * Standard way to get at goto default rules...
+ */
+NAMEVAL_S *
+goto_rules(int index)
+{
+ static NAMEVAL_S g_rules[] = {
+ {"folder-in-first-collection", NULL, GOTO_FIRST_CLCTN},
+ {"inbox-or-folder-in-first-collection", NULL, GOTO_INBOX_FIRST_CLCTN},
+ {"inbox-or-folder-in-recent-collection", NULL, GOTO_INBOX_RECENT_CLCTN},
+ {"first-collection-with-inbox-default", NULL, GOTO_FIRST_CLCTN_DEF_INBOX},
+ {"most-recent-folder", NULL, GOTO_LAST_FLDR}
+ };
+
+ return((index >= 0 && index < (sizeof(g_rules)/sizeof(g_rules[0])))
+ ? &g_rules[index] : NULL);
+}
+
+
+NAMEVAL_S *
+pat_fldr_types(int index)
+{
+ static NAMEVAL_S pat_fldr_list[] = {
+ {"Any", "ANY", FLDR_ANY},
+ {"News", "NEWS", FLDR_NEWS},
+ {"Email", "EMAIL", FLDR_EMAIL},
+ {"Specific (Enter Incoming Nicknames or use ^T)", "SPEC", FLDR_SPECIFIC}
+ };
+
+ return((index >= 0 &&
+ index < (sizeof(pat_fldr_list)/sizeof(pat_fldr_list[0])))
+ ? &pat_fldr_list[index] : NULL);
+}
+
+
+NAMEVAL_S *
+inabook_fldr_types(int indexarg)
+{
+ static NAMEVAL_S inabook_fldr_list[] = {
+ {"Don't care, always matches", "E", IAB_EITHER},
+ {"Yes, in any address book", "YES", IAB_YES},
+ {"No, not in any address book", "NO", IAB_NO},
+ {"Yes, in specific address books", "SYES", IAB_SPEC_YES},
+ {"No, not in any of specific address books", "SNO", IAB_SPEC_NO}
+ };
+
+ int index = indexarg & IAB_TYPE_MASK;
+
+ return((index >= 0 &&
+ index < (sizeof(inabook_fldr_list)/sizeof(inabook_fldr_list[0])))
+ ? &inabook_fldr_list[index] : NULL);
+}
+
+
+NAMEVAL_S *
+filter_types(int index)
+{
+ static NAMEVAL_S filter_type_list[] = {
+ {"Just Set Message Status", "NONE", FILTER_STATE},
+ {"Delete", "DEL", FILTER_KILL},
+ {"Move (Enter folder name(s) in primary collection, or use ^T)",
+ "FLDR", FILTER_FOLDER}
+ };
+
+ return((index >= 0 &&
+ index < (sizeof(filter_type_list)/sizeof(filter_type_list[0])))
+ ? &filter_type_list[index] : NULL);
+}
+
+
+NAMEVAL_S *
+role_repl_types(int index)
+{
+ static NAMEVAL_S role_repl_list[] = {
+ {"Never", "NO", ROLE_REPL_NO},
+ {"With confirmation", "YES", ROLE_REPL_YES},
+ {"Without confirmation", "NC", ROLE_REPL_NOCONF}
+ };
+
+ return((index >= 0 &&
+ index < (sizeof(role_repl_list)/sizeof(role_repl_list[0])))
+ ? &role_repl_list[index] : NULL);
+}
+
+
+NAMEVAL_S *
+role_forw_types(int index)
+{
+ static NAMEVAL_S role_forw_list[] = {
+ {"Never", "NO", ROLE_FORW_NO},
+ {"With confirmation", "YES", ROLE_FORW_YES},
+ {"Without confirmation", "NC", ROLE_FORW_NOCONF}
+ };
+
+ return((index >= 0 &&
+ index < (sizeof(role_forw_list)/sizeof(role_forw_list[0])))
+ ? &role_forw_list[index] : NULL);
+}
+
+
+NAMEVAL_S *
+role_comp_types(int index)
+{
+ static NAMEVAL_S role_comp_list[] = {
+ {"Never", "NO", ROLE_COMP_NO},
+ {"With confirmation", "YES", ROLE_COMP_YES},
+ {"Without confirmation", "NC", ROLE_COMP_NOCONF}
+ };
+
+ return((index >= 0 &&
+ index < (sizeof(role_comp_list)/sizeof(role_comp_list[0])))
+ ? &role_comp_list[index] : NULL);
+}
+
+
+NAMEVAL_S *
+role_status_types(int index)
+{
+ static NAMEVAL_S role_status_list[] = {
+ {"Don't care, always matches", "E", PAT_STAT_EITHER},
+ {"Yes", "YES", PAT_STAT_YES},
+ {"No", "NO", PAT_STAT_NO}
+ };
+
+ return((index >= 0 &&
+ index < (sizeof(role_status_list)/sizeof(role_status_list[0])))
+ ? &role_status_list[index] : NULL);
+}
+
+
+NAMEVAL_S *
+msg_state_types(int index)
+{
+ static NAMEVAL_S msg_state_list[] = {
+ {"Don't change it", "LV", ACT_STAT_LEAVE},
+ {"Set this state", "SET", ACT_STAT_SET},
+ {"Clear this state", "CLR", ACT_STAT_CLEAR}
+ };
+
+ return((index >= 0 &&
+ index < (sizeof(msg_state_list)/sizeof(msg_state_list[0])))
+ ? &msg_state_list[index] : NULL);
+}
+
+
+#ifdef ENABLE_LDAP
+NAMEVAL_S *
+ldap_search_rules(int index)
+{
+ static NAMEVAL_S ldap_search_list[] = {
+ {"contains", NULL, LDAP_SRCH_CONTAINS},
+ {"equals", NULL, LDAP_SRCH_EQUALS},
+ {"begins-with", NULL, LDAP_SRCH_BEGINS},
+ {"ends-with", NULL, LDAP_SRCH_ENDS}
+ };
+
+ return((index >= 0 &&
+ index < (sizeof(ldap_search_list)/sizeof(ldap_search_list[0])))
+ ? &ldap_search_list[index] : NULL);
+}
+
+
+NAMEVAL_S *
+ldap_search_types(int index)
+{
+ static NAMEVAL_S ldap_types_list[] = {
+ {"name", NULL, LDAP_TYPE_CN},
+ {"surname", NULL, LDAP_TYPE_SUR},
+ {"givenname", NULL, LDAP_TYPE_GIVEN},
+ {"email", NULL, LDAP_TYPE_EMAIL},
+ {"name-or-email", NULL, LDAP_TYPE_CN_EMAIL},
+ {"surname-or-givenname", NULL, LDAP_TYPE_SUR_GIVEN},
+ {"sur-or-given-or-name-or-email", NULL, LDAP_TYPE_SEVERAL}
+ };
+
+ return((index >= 0 &&
+ index < (sizeof(ldap_types_list)/sizeof(ldap_types_list[0])))
+ ? &ldap_types_list[index] : NULL);
+}
+
+
+NAMEVAL_S *
+ldap_search_scope(int index)
+{
+ static NAMEVAL_S ldap_scope_list[] = {
+ {"base", NULL, LDAP_SCOPE_BASE},
+ {"onelevel", NULL, LDAP_SCOPE_ONELEVEL},
+ {"subtree", NULL, LDAP_SCOPE_SUBTREE}
+ };
+
+ return((index >= 0 &&
+ index < (sizeof(ldap_scope_list)/sizeof(ldap_scope_list[0])))
+ ? &ldap_scope_list[index] : NULL);
+}
+#endif
+
+
+/*
+ * Choose from the global default, command line args, pinerc values to set
+ * the actual value of the variable that we will use. Start at the top
+ * and work down from higher to lower precedence.
+ * For lists, we may inherit values from lower precedence
+ * versions if that's the way the user specifies it.
+ * The user can put INHERIT_DEFAULT as the first entry in a list and that
+ * means it will inherit the current values, for example the values
+ * from the global_val, or the value from the main_user_val could be
+ * inherited in the post_user_val.
+ */
+void
+set_current_val(struct variable *var, int expand, int cmdline)
+{
+ int is_set[5], is_inherit[5];
+ int i, j, k, cnt, start;
+ char **tmp, **t, **list[5];
+ char *p;
+
+ dprint((9,
+ "set_current_val(var=%s%s, expand=%d, cmdline=%d)\n",
+ (var && var->name) ? var->name : "?",
+ (var && var->is_list) ? " (list)" : "",
+ expand, cmdline));
+
+ if(!var)
+ return;
+
+ if(var->is_list){ /* variable is a list */
+
+ for(j = 0; j < 5; j++){
+ t = j==0 ? var->global_val.l :
+ j==1 ? var->main_user_val.l :
+ j==2 ? var->post_user_val.l :
+ j==3 ? ((cmdline) ? var->cmdline_val.l : NULL) :
+ var->fixed_val.l;
+
+ is_set[j] = is_inherit[j] = 0;
+ list[j] = NULL;
+
+ if(t){
+ if(!expand){
+ is_set[j]++;
+ list[j] = t;
+ }
+ else{
+ for(i = 0; t[i]; i++){
+ if(expand_variables(tmp_20k_buf, SIZEOF_20KBUF, t[i],
+ 0)){
+ /* successful expand */
+ is_set[j]++;
+ list[j] = t;
+ break;
+ }
+ }
+ }
+
+ if(list[j] && list[j][0] && !strcmp(list[j][0],INHERIT))
+ is_inherit[j]++;
+ }
+ }
+
+ cnt = 0;
+ start = 0;
+ /* count how many items in current_val list */
+ /* Admin wants default, which is global_val. */
+ if(var->is_fixed && var->fixed_val.l == NULL){
+ cnt = 0;
+ if(is_set[0]){
+ for(; list[0][cnt]; cnt++)
+ ;
+ }
+ }
+ else{
+ for(j = 0; j < 5; j++){
+ if(is_set[j]){
+ if(!is_inherit[j]){
+ cnt = 0; /* reset */
+ start = j;
+ }
+
+ for(i = is_inherit[j] ? 1 : 0; list[j][i]; i++)
+ cnt++;
+ }
+ }
+ }
+
+ free_list_array(&var->current_val.l); /* clean up any old values */
+
+ /* check to see if anything is set */
+ if(is_set[0] + is_set[1] + is_set[2] + is_set[3] + is_set[4] > 0){
+ var->current_val.l = (char **)fs_get((cnt+1)*sizeof(char *));
+ tmp = var->current_val.l;
+ if(var->is_fixed && var->fixed_val.l == NULL){
+ if(is_set[0]){
+ for(i = 0; list[0][i]; i++){
+ if(!expand)
+ *tmp++ = cpystr(list[0][i]);
+ else if(expand_variables(tmp_20k_buf, SIZEOF_20KBUF,
+ list[0][i], 0))
+ *tmp++ = cpystr(tmp_20k_buf);
+ }
+ }
+ }
+ else{
+ for(j = start; j < 5; j++){
+ if(is_set[j]){
+ for(i = is_inherit[j] ? 1 : 0; list[j][i]; i++){
+ if(!expand)
+ *tmp++ = cpystr(list[j][i]);
+ else if(expand_variables(tmp_20k_buf,SIZEOF_20KBUF,
+ list[j][i], 0))
+ *tmp++ = cpystr(tmp_20k_buf);
+ }
+ }
+ }
+ }
+
+ *tmp = NULL;
+ }
+ else
+ var->current_val.l = NULL;
+ }
+ else{ /* variable is not a list */
+ char *strvar = NULL;
+
+ for(j = 0; j < 5; j++){
+
+ p = j==0 ? var->fixed_val.p :
+ j==1 ? ((cmdline) ? var->cmdline_val.p : NULL) :
+ j==2 ? var->post_user_val.p :
+ j==3 ? var->main_user_val.p :
+ var->global_val.p;
+
+ is_set[j] = 0;
+
+ if(p){
+ if(!expand){
+ is_set[j]++;
+ if(!strvar)
+ strvar = p;
+ }
+ else if(expand_variables(tmp_20k_buf, SIZEOF_20KBUF, p,
+ (var == &ps_global->vars[V_MAILCAP_PATH] ||
+ var == &ps_global->vars[V_MIMETYPE_PATH]))){
+ is_set[j]++;
+ if(!strvar)
+ strvar = p;
+ }
+ }
+ }
+
+ /* Admin wants default, which is global_val. */
+ if(var->is_fixed && var->fixed_val.p == NULL)
+ strvar = var->global_val.p;
+
+ if(var->current_val.p) /* free previous value */
+ fs_give((void **)&var->current_val.p);
+
+ if(strvar){
+ if(!expand)
+ var->current_val.p = cpystr(strvar);
+ else{
+ expand_variables(tmp_20k_buf, SIZEOF_20KBUF, strvar,
+ (var == &ps_global->vars[V_MAILCAP_PATH] ||
+ var == &ps_global->vars[V_MIMETYPE_PATH]));
+ var->current_val.p = cpystr(tmp_20k_buf);
+ }
+ }
+ else
+ var->current_val.p = NULL;
+ }
+
+ if(var->is_fixed && !is_inherit[4]){
+ char **flist;
+ int fixed_len, user_len;
+
+ /*
+ * sys mgr fixed this variable and user is trying to change it
+ */
+ for(k = 1; !(ps_global->give_fixed_warning &&
+ ps_global->fix_fixed_warning) && k <= 3; k++){
+ if(is_set[k]){
+ if(var->is_list){
+ t = k==1 ? ((cmdline) ? var->cmdline_val.l : NULL) :
+ k==2 ? var->post_user_val.l :
+ var->main_user_val.l;
+
+ /* If same length and same contents, don't warn. */
+ for(flist=var->fixed_val.l; flist && *flist; flist++)
+ ;/* just counting */
+
+ fixed_len = var->fixed_val.l ? (flist - var->fixed_val.l)
+ : 0;
+ for(flist=t; flist && *flist; flist++)
+ ;/* just counting */
+
+ user_len = t ? (flist - t) : 0;
+ if(user_len == fixed_len){
+ for(i=0; i < user_len; i++){
+ for(j=0; j < user_len; j++)
+ if(!strucmp(t[i], var->fixed_val.l[j]))
+ break;
+
+ if(j == user_len){
+ ps_global->give_fixed_warning = 1;
+ if(k != 1)
+ ps_global->fix_fixed_warning = 1;
+
+ break;
+ }
+ }
+ }
+ else{
+ ps_global->give_fixed_warning = 1;
+ if(k != 1)
+ ps_global->fix_fixed_warning = 1;
+ }
+ }
+ else{
+ p = k==1 ? ((cmdline) ? var->cmdline_val.p : NULL) :
+ k==2 ? var->post_user_val.p :
+ var->main_user_val.p;
+
+ if((var->fixed_val.p && !p) ||
+ (!var->fixed_val.p && p) ||
+ (var->fixed_val.p && p && strucmp(var->fixed_val.p, p))){
+ ps_global->give_fixed_warning = 1;
+ if(k != 1)
+ ps_global->fix_fixed_warning = 1;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+void
+set_news_spec_current_val(int expand, int cmdline)
+{
+ struct variable *newsvar = &ps_global->vars[V_NEWS_SPEC];
+ struct variable *fvar = &ps_global->vars[V_FOLDER_SPEC];
+
+ /* check to see if it has a value */
+ set_current_val(newsvar, expand, cmdline);
+
+ /*
+ * If no value, we might want to fake a value. We'll do that if
+ * there is no news collection already defined in FOLDER_SPEC and if
+ * there is also an NNTP_SERVER defined.
+ */
+ if(!newsvar->current_val.l && ps_global->VAR_NNTP_SERVER &&
+ ps_global->VAR_NNTP_SERVER[0] && ps_global->VAR_NNTP_SERVER[0][0] &&
+ !news_in_folders(fvar)){
+ char buf[MAXPATH];
+
+ newsvar->global_val.l = (char **)fs_get(2 * sizeof(char *));
+ snprintf(buf, sizeof(buf), "{%.*s/nntp}#news.[]", sizeof(buf)-20,
+ ps_global->VAR_NNTP_SERVER[0]);
+ newsvar->global_val.l[0] = cpystr(buf);
+ newsvar->global_val.l[1] = NULL;
+ set_current_val(newsvar, expand, cmdline);
+ /*
+ * But we're going to get rid of the fake global_val in case
+ * things change.
+ */
+ free_list_array(&newsvar->global_val.l);
+ }
+}
+
+
+/*
+ * Feature-list has to be handled separately from the other variables
+ * because it is additive. The other variables choose one of command line,
+ * or pine.conf, or pinerc. Feature list adds them. This could easily be
+ * converted to a general purpose routine if we add more additive variables.
+ *
+ * This works by replacing earlier values with later ones. That is, command
+ * line settings have higher precedence than global settings and that is
+ * accomplished by putting the command line features after the global
+ * features in the list. When they are processed, the last one wins.
+ *
+ * Feature-list also has a backwards compatibility hack.
+ */
+void
+set_feature_list_current_val(struct variable *var)
+{
+ char **list;
+ char **list_fixed;
+ char no_allow[50];
+ int i, j, k, m,
+ elems = 0;
+
+ /* count the lists so we can allocate */
+ for(m = 0; m < 6; m++){
+ list = m==0 ? var->global_val.l :
+ m==1 ? var->main_user_val.l :
+ m==2 ? var->post_user_val.l :
+ m==3 ? ps_global->feat_list_back_compat :
+ m==4 ? var->cmdline_val.l :
+ var->fixed_val.l;
+ if(list)
+ for(i = 0; list[i]; i++)
+ elems++;
+ }
+
+ list_fixed = var->fixed_val.l;
+
+ if(var->current_val.l)
+ free_list_array(&var->current_val.l);
+
+ var->current_val.l = (char **)fs_get((elems+1) * sizeof(char *));
+
+ /*
+ * We need to warn the user if the sys mgr has restricted him or her
+ * from changing a feature that he or she is trying to change.
+ *
+ * We're not catching the old-growth macro since we're just comparing
+ * strings. That is, it works correctly, but the user won't be warned
+ * if the user old-growth and the mgr says no-quit-without-confirm.
+ */
+
+ j = 0;
+ strncpy(no_allow, "no-", 3);
+ strncpy(no_allow+3, feature_list_name(F_ALLOW_CHANGING_FROM), sizeof(no_allow)-3-1);
+ no_allow[sizeof(no_allow)-1] = '\0';
+
+ for(m = 0; m < 6; m++){
+ list = m==0 ? var->global_val.l :
+ m==1 ? var->main_user_val.l :
+ m==2 ? var->post_user_val.l :
+ m==3 ? ps_global->feat_list_back_compat :
+ m==4 ? var->cmdline_val.l :
+ var->fixed_val.l;
+ if(list)
+ for(i = 0; list[i]; i++){
+ var->current_val.l[j++] = cpystr(list[i]);
+
+ /* this is the warning section */
+ if(m >= 1 && m <= 4){
+ for(k = 0; list_fixed && list_fixed[k]; k++){
+ char *p, *q;
+ p = list[i];
+ q = list_fixed[k];
+ if(!struncmp(p, "no-", 3))
+ p += 3;
+ if(!struncmp(q, "no-", 3))
+ q += 3;
+ if(!strucmp(q, p) && strucmp(list[i], list_fixed[k])){
+ ps_global->give_fixed_warning = 1;
+ if(m <= 2)
+ ps_global->fix_fixed_warning = 1;
+ }
+ }
+ }
+ else if(m == 5 && !strucmp(list[i], no_allow))
+ ps_global->never_allow_changing_from = 1;
+ }
+ }
+
+#ifdef NEVER_ALLOW_CHANGING_FROM
+ ps_global->never_allow_changing_from = 1;
+#endif
+
+ var->current_val.l[j] = NULL;
+}
+
+
+
+/*----------------------------------------------------------------------
+
+ Expand Metacharacters/variables in file-names
+
+ Read input line and expand shell-variables/meta-characters
+
+ <input> <replaced by>
+ $variable getenv("variable")
+ ${variable} getenv("variable")
+ ${variable:-defvalue} is getenv("variable") if variable is defined and
+ is defvalue otherwise
+ ~ getenv("HOME")
+ \c c
+ <others> <just copied>
+
+NOTE handling of braces in ${name} doesn't check much or do error recovery
+
+ If colon_path is set, then we expand ~ not only at the start of linein,
+ but also after each : in the path.
+
+ ----*/
+#define is_allowed_envchar(C, S) ((S) == 0 ? !isspace((C)) && (C) != '/'\
+ : (((C) >= 'a' && (C) <= 'z') \
+ || ((C) >= 'A' && (C) <= 'Z') \
+ || ((C) >= '0' && (C) <= '9')))
+
+char *
+expand_variables(char *lineout, size_t lineoutlen, char *linein, int colon_path)
+{
+ char *src = linein, *dest = lineout, *p;
+ char *limit = lineout + lineoutlen;
+ int envexpand = 0, sp;
+
+ if(!linein)
+ return(NULL);
+
+ sp = strncmp(src,"LIT:pattern=\"/NICK=", strlen("LIT:pattern=\"/NICK=")) == 0;
+ while(*src ){ /* something in input string */
+ if(*src == '$' && *(src+1) == '$'){
+ /*
+ * $$ to escape chars we're interested in, else
+ * it's up to the user of the variable to handle the
+ * backslash...
+ */
+ if(dest < limit)
+ *dest++ = *++src; /* copy next as is */
+ }else
+#if !(defined(DOS) || defined(OS2))
+ if(*src == '\\' && *(src+1) == '$'){
+ /*
+ * backslash to escape chars we're interested in, else
+ * it's up to the user of the variable to handle the
+ * backslash...
+ */
+ if(dest < limit)
+ *dest++ = *++src; /* copy next as is */
+ }else if(*src == '~' &&
+ (src == linein || (colon_path && *(src-1) == ':'))){
+ char buf[MAXPATH];
+ int i;
+
+ for(i = 0; i < sizeof(buf)-1 && src[i] && src[i] != '/'; i++)
+ buf[i] = src[i];
+
+ src += i; /* advance src pointer */
+ buf[i] = '\0'; /* tie off buf string */
+ fnexpand(buf, sizeof(buf)); /* expand the path */
+
+ for(p = buf; dest < limit && (*dest = *p); p++, dest++)
+ ;
+
+ continue;
+ }else
+#endif
+ if(*src == '$'){ /* shell variable */
+ char word[128+1], *colon = NULL, *rbrace = NULL;
+
+ envexpand++; /* signal that we've expanded a var */
+ src++; /* skip dollar */
+ if(*src == '{'){ /* starts with brace? */
+ src++;
+ rbrace = strindex(src, '}');
+ if(rbrace){
+ /* look for default value */
+ colon = strstr(src, ":-");
+ if(colon && (rbrace < colon))
+ colon = NULL;
+ }
+ }
+
+ p = word;
+
+ /* put the env variable to be looked up in word */
+ if(rbrace){
+ while(*src
+ && (p-word < sizeof(word)-1)
+ && ((colon && src < colon) || (!colon && src < rbrace))){
+ if(isspace((unsigned char) *src)){
+ /*
+ * Illegal input. This should be an error of some
+ * sort but instead of that we'll just backup to the
+ * $ and treat it like it wasn't there.
+ */
+ while(*src != '$')
+ src--;
+
+ envexpand--;
+ goto just_copy;
+ }
+ else
+ *p++ = *src++;
+ }
+
+ /* adjust src for next char */
+ src = rbrace + 1;
+ }
+ else{
+ while(*src && is_allowed_envchar((unsigned char) *src, sp)
+ && (p-word < sizeof(word)-1))
+ *p++ = *src++;
+ }
+
+ *p = '\0';
+
+ if((p = getenv(word)) != NULL){ /* check for word in environment */
+ while(*p && dest < limit)
+ *dest++ = *p++;
+ }
+ else if(colon){ /* else possible default value */
+ p = colon + 2;
+ while(*p && p < rbrace && dest < limit)
+ *dest++ = *p++;
+ }
+
+ continue;
+ }else{ /* other cases: just copy */
+just_copy:
+ if(dest < limit)
+ *dest++ = *src;
+ }
+
+ if(*src) /* next character (if any) */
+ src++;
+ }
+
+ if(dest < limit)
+ *dest = '\0';
+ else
+ lineout[lineoutlen-1] = '\0';
+
+ return((envexpand && lineout[0] == '\0') ? NULL : lineout);
+}
+
+
+/*----------------------------------------------------------------------
+ Sets login, full_username and home_dir
+
+ Args: ps -- The Pine structure to put the user name, etc in
+
+ Result: sets the fullname, login and home_dir field of the pine structure
+ returns 0 on success, -1 if not.
+ ----*/
+#define MAX_INIT_ERRS 10
+void
+init_error(struct pine *ps, int flags, int min_time, int max_time, char *message)
+{
+ int i;
+
+ if(!ps->init_errs){
+ ps->init_errs = (INIT_ERR_S *)fs_get((MAX_INIT_ERRS + 1) *
+ sizeof(*ps->init_errs));
+ memset(ps->init_errs, 0, (MAX_INIT_ERRS + 1) * sizeof(*ps->init_errs));
+ }
+
+ for(i = 0; i < MAX_INIT_ERRS; i++)
+ if(!(ps->init_errs)[i].message){
+ (ps->init_errs)[i].message = cpystr(message);
+ (ps->init_errs)[i].min_time = min_time;
+ (ps->init_errs)[i].max_time = max_time;
+ (ps->init_errs)[i].flags = flags;
+ dprint((2, "%s\n", message ? message : "?"));
+ break;
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Read and parse a pinerc file
+
+ Args: Filename -- name of the .pinerc file to open and read
+ vars -- The vars structure to store values in
+ which_vars -- Whether the local or global values are being read
+
+ Result:
+
+ This may be the local file or the global file. The values found are
+merged with the values currently in vars. All values are strings and
+are malloced; and existing values will be freed before the assignment.
+Those that are <unset> will be left unset; their values will be NULL.
+ ----*/
+void
+read_pinerc(PINERC_S *prc, struct variable *vars, ParsePinerc which_vars)
+{
+ char *filename, *file, *value, **lvalue, *line, *error;
+ char *p, *p1, *free_file = NULL;
+ struct variable *v;
+ PINERC_LINE *pline = NULL;
+ int line_count, was_quoted;
+ int i;
+
+ if(!prc)
+ return;
+
+ dprint((2, "reading_pinerc \"%s\"\n",
+ prc->name ? prc->name : "?"));
+
+ if(prc->type == Loc){
+ filename = prc->name ? prc->name : "";
+ file = free_file = read_file(filename, 0);
+
+ /*
+ * This is questionable. In case the user edits the pinerc
+ * in Windows and adds a UTF-8 BOM, we skip it here. If the
+ * user adds a Unicode BOM we're in trouble. We could write it
+ * with the BOM ourselves but so far we leave it BOMless in
+ * order that it's the same on Unix and Windows.
+ */
+ if(BOM_UTF8(file))
+ file += 3;
+ }
+ else{
+ if((file = read_remote_pinerc(prc, which_vars)) != NULL)
+ ps_global->c_client_error[0] = '\0';
+
+ free_file = file;
+ }
+
+ if(file == NULL || *file == '\0'){
+#ifdef DEBUG
+ if(file == NULL){
+ dprint((2, "Open failed: %s\n", error_description(errno)));
+ }
+ else{
+ if(prc->type == Loc){
+ dprint((1, "Read_pinerc: empty pinerc (new?)\n"));
+ }
+ else{
+ dprint((1, "Read_pinerc: new remote pinerc\n"));
+ }
+ }
+#endif /* DEBUG */
+
+ if(which_vars == ParsePers){
+ /* problems getting remote config */
+ if(file == NULL && prc->type == RemImap){
+ if(!pith_opt_remote_pinerc_failure
+ || !(*pith_opt_remote_pinerc_failure)())
+ exceptional_exit(_("Unable to read or write remote configuration"), -1);
+ }
+
+ ps_global->first_time_user = 1;
+ prc->outstanding_pinerc_changes = 1;
+ }
+
+ return;
+ }
+ else{
+ if(prc->type == Loc &&
+ (which_vars == ParseFixed || which_vars == ParseGlobal ||
+ (can_access(filename, ACCESS_EXISTS) == 0 &&
+ can_access(filename, EDIT_ACCESS) != 0))){
+ prc->readonly = 1;
+ if(prc == ps_global->prc)
+ ps_global->readonly_pinerc = 1;
+ }
+
+ /*
+ * accept CRLF or LF newlines
+ */
+ for(p = file; *p && *p != '\012'; p++)
+ ;
+
+ if(p > file && *p && *(p-1) == '\015') /* cvt crlf to lf */
+ for(p1 = p - 1; (*p1 = *p) != '\0'; p++)
+ if(!(*p == '\015' && *(p+1) == '\012'))
+ p1++;
+ }
+
+ dprint((2, "Read %d characters:\n", strlen(file)));
+
+ if(which_vars == ParsePers || which_vars == ParsePersPost){
+ /*--- Count up lines and allocate structures */
+ for(line_count = 0, p = file; *p != '\0'; p++)
+ if(*p == '\n')
+ line_count++;
+
+ prc->pinerc_lines = (PINERC_LINE *)
+ fs_get((3 + line_count) * sizeof(PINERC_LINE));
+ memset((void *)prc->pinerc_lines, 0,
+ (3 + line_count) * sizeof(PINERC_LINE));
+ pline = prc->pinerc_lines;
+ }
+
+ for(p = file, line = file; *p != '\0';){
+ /*----- Grab the line ----*/
+ line = p;
+ while(*p && *p != '\n')
+ p++;
+ if(*p == '\n'){
+ *p++ = '\0';
+ }
+
+ /*----- Comment Line -----*/
+ if(*line == '#'){
+ /* no comments in remote pinercs */
+ if(pline && prc->type == Loc){
+ pline->is_var = 0;
+ pline->line = cpystr(line);
+ pline++;
+ }
+ continue;
+ }
+
+ if(*line == '\0' || *line == '\t' || *line == ' '){
+ p1 = line;
+ while(*p1 == '\t' || *p1 == ' ')
+ p1++;
+ if(pline){
+ /*
+ * This could be a continuation line from some future
+ * version of pine, or it could be a continuation line
+ * from a PC-Pine variable we don't know about in unix.
+ */
+ if(*p1 != '\0')
+ pline->line = cpystr(line);
+ else
+ pline->line = cpystr("");
+ pline->is_var = 0;
+ pline++;
+ }
+ continue;
+ }
+
+ /*----- look up matching 'v' and leave "value" after '=' ----*/
+ for(v = vars; *line && v->name; v++)
+ if((i = strlen(v->name)) < strlen(line) && !struncmp(v->name,line,i)){
+ int j;
+
+ for(j = i; line[j] == ' ' || line[j] == '\t'; j++)
+ ;
+
+ if(line[j] == '='){ /* bingo! */
+ for(value = &line[j+1];
+ *value == ' ' || *value == '\t';
+ value++)
+ ;
+
+ break;
+ }
+ /* else either unrecognized var or bogus line */
+ }
+
+ /*----- Didn't match any variable or bogus format -----*/
+ /*
+ * This could be a variable from some future
+ * version of pine, or it could be a PC-Pine variable
+ * we don't know about in unix. Either way, we want to preserve
+ * it in the file.
+ */
+ if(!v->name){
+ if(pline){
+ pline->is_var = 0;
+ pline->line = cpystr(line);
+ pline++;
+ }
+ continue;
+ }
+
+ /*
+ * Previous versions have caused duplicate pinerc data to be
+ * written to pinerc files. This clause erases the duplicate
+ * information when we read it, and it will be removed from the file
+ * if we call write_pinerc. We test to see if the same variable
+ * appears later in the file, if so, we skip over it here.
+ * We don't care about duplicates if this isn't a pinerc we might
+ * write out, so include pline in the conditional.
+ * Note that we will leave all of the duplicate comments and blank
+ * lines in the file unless it is a remote pinerc. Luckily, the
+ * bug that caused the duplicates only applied to remote pinercs,
+ * so we should have that case covered.
+ *
+ * If we find a duplicate, we point p to the start
+ * of the next line that should be considered, and then skip back
+ * to the top of the loop.
+ */
+ if(pline && var_is_in_rest_of_file(v->name, p)){
+ if(v->is_list)
+ p = skip_over_this_var(line, p);
+
+ continue;
+ }
+
+
+ /*----- Obsolete variable, read it anyway below, might use it -----*/
+ if(v->is_obsolete){
+ if(pline){
+ pline->obsolete_var = 1;
+ pline->line = cpystr(line);
+ pline->var = v;
+ }
+ }
+
+ /*----- Variable is in the list but unused for some reason -----*/
+ if(!v->is_used){
+ if(pline){
+ pline->is_var = 0;
+ pline->line = cpystr(line);
+ pline++;
+ }
+ continue;
+ }
+
+ /*--- Var is not user controlled, leave it alone for back compat ---*/
+ if(!v->is_user && pline){
+ pline->is_var = 0;
+ pline->line = cpystr(line);
+ pline++;
+ continue;
+ }
+
+ if(which_vars == ParseFixed)
+ v->is_fixed = 1;
+
+ /*---- variable is unset, or it's global but expands to nothing ----*/
+ if(!*value
+ || (which_vars == ParseGlobal
+ && !expand_variables(tmp_20k_buf, SIZEOF_20KBUF, value,
+ (v == &ps_global->vars[V_MAILCAP_PATH] ||
+ v == &ps_global->vars[V_MIMETYPE_PATH])))){
+ if(v->is_user && pline){
+ pline->is_var = 1;
+ pline->var = v;
+ pline++;
+ }
+ continue;
+ }
+
+ /*--value is non-empty, store it handling quotes and trailing space--*/
+ if(*value == '"' && !v->is_list && v->del_quotes){
+ was_quoted = 1;
+ value++;
+ for(p1 = value; *p1 && *p1 != '"'; p1++);
+ if(*p1 == '"')
+ *p1 = '\0';
+ else
+ removing_trailing_white_space(value);
+ }else
+ was_quoted = 0;
+
+ /*
+ * List Entry Parsing
+ *
+ * The idea is to parse a comma separated list of
+ * elements, preserving quotes, and understanding
+ * continuation lines (that is ',' == "\n ").
+ * Quotes must be balanced within elements. Space
+ * within elements is preserved, but leading and trailing
+ * space is trimmed. This is a generic function, and it's
+ * left to the the functions that use the lists to make sure
+ * they contain valid data...
+ */
+ if(v->is_list){
+
+ was_quoted = 0;
+ line_count = 0;
+ p1 = value;
+ while(1){ /* generous count of list elements */
+ if(*p1 == '"') /* ignore ',' if quoted */
+ was_quoted = (was_quoted) ? 0 : 1 ;
+
+ if((*p1 == ',' && !was_quoted) || *p1 == '\n' || *p1 == '\0')
+ line_count++; /* count this element */
+
+ if(*p1 == '\0' || *p1 == '\n'){ /* deal with EOL */
+ if(p1 < p || *p1 == '\n'){
+ *p1++ = ','; /* fix null or newline */
+
+ if(*p1 != '\t' && *p1 != ' '){
+ *(p1-1) = '\0'; /* tie off list */
+ p = p1; /* reset p */
+ break;
+ }
+ }else{
+ p = p1; /* end of pinerc */
+ break;
+ }
+ }else
+ p1++;
+ }
+
+ error = NULL;
+ lvalue = parse_list(value, line_count,
+ v->del_quotes ? PL_REMSURRQUOT : PL_NONE,
+ &error);
+ if(error){
+ dprint((1,
+ "read_pinerc: ERROR: %s in %s = \"%s\"\n",
+ error ? error : "?",
+ v->name ? v->name : "?",
+ value ? value : "?"));
+ }
+ /*
+ * Special case: turn "" strings into empty strings.
+ * This allows users to turn off default lists. For example,
+ * if smtp-server is set then a user could override smtp-server
+ * with smtp-server="".
+ */
+ for(i = 0; lvalue[i]; i++)
+ if(lvalue[i][0] == '"' &&
+ lvalue[i][1] == '"' &&
+ lvalue[i][2] == '\0')
+ lvalue[i][0] = '\0';
+ }
+
+ if(pline){
+ if(v->is_user && (which_vars == ParsePers || !v->is_onlymain)){
+ if(v->is_list){
+ char ***l;
+
+ l = (which_vars == ParsePers) ? &v->main_user_val.l
+ : &v->post_user_val.l;
+ free_list_array(l);
+ *l = lvalue;
+ }
+ else{
+ char **p;
+
+ p = (which_vars == ParsePers) ? &v->main_user_val.p
+ : &v->post_user_val.p;
+ if(p && *p != NULL)
+ fs_give((void **)p);
+
+ *p = cpystr(value);
+ }
+
+ if(pline){
+ pline->is_var = 1;
+ pline->var = v;
+ pline->is_quoted = was_quoted;
+ pline++;
+ }
+ }
+ }
+ else if(which_vars == ParseGlobal){
+ if(v->is_global){
+ if(v->is_list){
+ free_list_array(&v->global_val.l);
+ v->global_val.l = lvalue;
+ }
+ else{
+ if(v->global_val.p != NULL)
+ fs_give((void **) &(v->global_val.p));
+
+ v->global_val.p = cpystr(value);
+ }
+ }
+ }
+ else{ /* which_vars == ParseFixed */
+ if(v->is_user || v->is_global){
+ if(v->is_list){
+ free_list_array(&v->fixed_val.l);
+ v->fixed_val.l = lvalue;
+ }
+ else{
+ if(v->fixed_val.p != NULL)
+ fs_give((void **) &(v->fixed_val.p));
+
+ v->fixed_val.p = cpystr(value);
+ }
+ }
+ }
+
+#ifdef DEBUG
+ if(v->is_list){
+ char **l;
+ l = (which_vars == ParsePers) ? v->main_user_val.l :
+ (which_vars == ParsePersPost) ? v->post_user_val.l :
+ (which_vars == ParseGlobal) ? v->global_val.l :
+ v->fixed_val.l;
+ if(l && *l && **l){
+ dprint((5, " %20.20s : %s\n",
+ v->name ? v->name : "?",
+ *l ? *l : "?"));
+ while(++l && *l && **l)
+ dprint((5, " %20.20s : %s\n", "",
+ *l ? *l : "?"));
+ }
+ }else{
+ char *p;
+ p = (which_vars == ParsePers) ? v->main_user_val.p :
+ (which_vars == ParsePersPost) ? v->post_user_val.p :
+ (which_vars == ParseGlobal) ? v->global_val.p :
+ v->fixed_val.p;
+ if(p && *p)
+ dprint((5, " %20.20s : %s\n",
+ v->name ? v->name : "?",
+ p ? p : "?"));
+ }
+#endif /* DEBUG */
+ }
+
+ if(pline){
+ pline->line = NULL;
+ pline->is_var = 0;
+ if(!prc->pinerc_written && prc->type == Loc){
+ prc->pinerc_written = name_file_mtime(filename);
+ dprint((5, "read_pinerc: time_pinerc_written = %ld\n",
+ (long) prc->pinerc_written));
+ }
+ }
+
+ if(free_file)
+ fs_give((void **) &free_file);
+}
+
+
+/*
+ * Args varname The variable name we're looking for
+ * begin Begin looking here
+ *
+ * Returns 1 if variable varname appears in the rest of the file
+ * 0 if not
+ */
+int
+var_is_in_rest_of_file(char *varname, char *begin)
+{
+ char *p;
+
+ if(!(varname && *varname && begin && *begin))
+ return 0;
+
+ p = begin;
+
+ while((p = srchstr(p, varname)) != NULL){
+ /* beginning of a line? */
+ if(p > begin && (*(p-1) != '\n' && *(p-1) != '\r')){
+ p++;
+ continue;
+ }
+
+ /* followed by [ SPACE ] < = > ? */
+ p += strlen(varname);
+ while(*p == ' ' || *p == '\t')
+ p++;
+
+ if(*p == '=')
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Args begin Variable to skip starts here.
+ * nextline This is where the next line starts. We need to know this
+ * because the input has been mangled a little. A \0 has
+ * replaced the \n at the end of the first line, but we can
+ * use nextline to help us out of that quandry.
+ *
+ * Return a pointer to the start of the first line after this variable
+ * and all of its continuation lines.
+ */
+char *
+skip_over_this_var(char *begin, char *nextline)
+{
+ char *p;
+
+ p = begin;
+
+ while(1){
+ if(*p == '\0' || *p == '\n'){ /* EOL */
+ if(p < nextline || *p == '\n'){ /* there may be another line */
+ p++;
+ if(*p != ' ' && *p != '\t') /* no continuation line */
+ return(p);
+ }
+ else /* end of file */
+ return(p);
+ }
+ else
+ p++;
+ }
+}
+
+
+static char quotes[3] = {'"', '"', '\0'};
+/*----------------------------------------------------------------------
+ Write out the .pinerc state information
+
+ Args: ps -- The pine structure to take state to be written from
+ which -- Which pinerc to write
+ flags -- If bit WRP_NOUSER is set, then assume that there is
+ not a user present to answer questions.
+
+ This writes to a temporary file first, and then renames that to
+ be the new .pinerc file to protect against disk error. This has the
+ problem of possibly messing up file protections, ownership and links.
+ ----*/
+int
+write_pinerc(struct pine *ps, EditWhich which, int flags)
+{
+ char *p, *dir, *tmp = NULL, *pinrc;
+ char *pval, **lval;
+ int bc = 1;
+ PINERC_LINE *pline;
+ struct variable *var;
+ time_t mtime;
+ char *filename;
+ REMDATA_S *rd = NULL;
+ PINERC_S *prc = NULL;
+ STORE_S *so = NULL;
+
+ dprint((2,"---- write_pinerc(%s) ----\n",
+ (which == Main) ? "Main" : "Post"));
+
+ switch(which){
+ case Main:
+ prc = ps ? ps->prc : NULL;
+ break;
+ case Post:
+ prc = ps ? ps->post_prc : NULL;
+ break;
+ default:
+ break;
+ }
+
+ if(!prc)
+ return(-1);
+
+ if(prc->quit_to_edit){
+ if(!(flags & WRP_NOUSER))
+ quit_to_edit_msg(prc);
+
+ return(-1);
+ }
+
+ if(prc->type != Loc && !prc->readonly){
+
+ bc = 0; /* don't do backcompat conversion */
+ rd = prc->rd;
+ if(!rd)
+ return(-1);
+
+ rd_check_remvalid(rd, -10L);
+
+ if(rd->flags & REM_OUTOFDATE){
+ if((flags & WRP_NOUSER) || unexpected_pinerc_change()){
+ prc->outstanding_pinerc_changes = 1;
+ if(!(flags & WRP_NOUSER))
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ "Pinerc \"%.200s\" NOT saved",
+ prc->name ? prc->name : "");
+ dprint((2, "write_pinerc: remote pinerc changed\n"));
+ return(-1);
+ }
+ else
+ rd->flags &= ~REM_OUTOFDATE;
+ }
+
+ rd_open_remote(rd);
+
+ if(rd->access == ReadWrite){
+ int ro;
+
+ if((ro=rd_remote_is_readonly(rd)) || rd->flags & REM_OUTOFDATE){
+ if(ro == 1){
+ if(!(flags & WRP_NOUSER))
+ q_status_message(SM_ORDER | SM_DING, 5, 15,
+ _("Can't access remote config, changes NOT saved!"));
+ dprint((1,
+ "write_pinerc: Can't write to remote pinerc %s, aborting write\n",
+ rd->rn ? rd->rn : "?"));
+ }
+ else if(ro == 2){
+ if(!(rd->flags & NO_META_UPDATE)){
+ unsigned long save_chk_nmsgs;
+
+ switch(rd->type){
+ case RemImap:
+ save_chk_nmsgs = rd->t.i.chk_nmsgs;
+ rd->t.i.chk_nmsgs = 0;
+ rd_write_metadata(rd, 0);
+ rd->t.i.chk_nmsgs = save_chk_nmsgs;
+ break;
+
+ default:
+ q_status_message(SM_ORDER | SM_DING, 3, 5,
+ "Write_pinerc: Type not supported");
+ break;
+ }
+ }
+
+ if(!(flags & WRP_NOUSER))
+ q_status_message1(SM_ORDER | SM_DING, 5, 15,
+ _("No write permission for remote config %.200s, changes NOT saved!"),
+ rd->rn);
+ }
+ else{
+ if(!(flags & WRP_NOUSER))
+ q_status_message(SM_ORDER | SM_DING, 5, 15,
+ _("Remote config changed, aborting our change to avoid damage..."));
+ dprint((1,
+ "write_pinerc: remote config %s changed since we started pine, aborting write\n",
+ prc->name ? prc->name : "?"));
+ }
+
+ rd->flags &= ~DO_REMTRIM;
+ return(-1);
+ }
+
+ filename = rd->lf;
+ }
+ else{
+ prc->readonly = 1;
+ if(prc == ps->prc)
+ ps->readonly_pinerc = 1;
+ }
+ }
+ else
+ filename = prc->name ? prc->name : "";
+
+ pinrc = prc->name ? prc->name : "";
+
+ if(prc->type == Loc){
+ mtime = name_file_mtime(filename);
+ if(prc->pinerc_written
+ && prc->pinerc_written != mtime
+ && ((flags & WRP_NOUSER) || unexpected_pinerc_change())){
+ prc->outstanding_pinerc_changes = 1;
+
+ if(!(flags & WRP_NOUSER))
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ "Pinerc \"%.200s\" NOT saved", pinrc);
+
+ dprint((2,"write_pinerc: mtime mismatch: \"%s\": %ld != %ld\n",
+ filename ? filename : "?",
+ (long) prc->pinerc_written, (long) mtime));
+ return(-1);
+ }
+ }
+
+ /* don't write if pinerc is read-only */
+ if(prc->readonly ||
+ (filename &&
+ can_access(filename, ACCESS_EXISTS) == 0 &&
+ can_access(filename, EDIT_ACCESS) != 0)){
+ prc->readonly = 1;
+ if(prc == ps->prc)
+ ps->readonly_pinerc = 1;
+
+ if(!(flags & WRP_NOUSER))
+ q_status_message1(SM_ORDER | SM_DING, 0, 5,
+ _("Can't modify configuration file \"%.200s\": ReadOnly"),
+ pinrc);
+ dprint((2, "write_pinerc: fail because can't access pinerc\n"));
+
+ if(rd)
+ rd->flags &= ~DO_REMTRIM;
+
+ return(-1);
+ }
+
+ if(rd && rd->flags & NO_FILE){
+ so = rd->sonofile;
+ so_truncate(rd->sonofile, 0L); /* reset storage object */
+ }
+ else{
+ dir = ".";
+ if((p = last_cmpnt(filename)) != NULL){
+ *--p = '\0';
+ dir = filename;
+ }
+
+#if defined(DOS) || defined(OS2)
+ if(!(isalpha((unsigned char)dir[0]) && dir[1] == ':' && dir[2] == '\0')
+ && (can_access(dir, EDIT_ACCESS) < 0 &&
+ our_mkdir(dir, 0700) < 0))
+ {
+ if(!(flags & WRP_NOUSER))
+ q_status_message2(SM_ORDER | SM_DING, 3, 5,
+ /* TRANSLATORS: first argument is a filename, second
+ arg is the text of the error message */
+ _("Error creating \"%.200s\" : %.200s"), dir,
+ error_description(errno));
+ if(rd)
+ rd->flags &= ~DO_REMTRIM;
+
+ return(-1);
+ }
+
+ tmp = temp_nam(dir, "rc");
+
+ if(*dir && tmp && !in_dir(dir, tmp)){
+ our_unlink(tmp);
+ fs_give((void **)&tmp);
+ }
+
+ if(p)
+ *p = '\\';
+
+ if(tmp == NULL)
+ goto io_err;
+
+#else /* !DOS */
+ tmp = temp_nam((*dir) ? dir : "/", "pinerc");
+
+ /*
+ * If temp_nam can't write in dir it puts the temp file in a
+ * temp directory, which won't help us when we go to rename.
+ */
+ if(*dir && tmp && !in_dir(dir, tmp)){
+ our_unlink(tmp);
+ fs_give((void **)&tmp);
+ }
+
+ if(p)
+ *p = '/';
+
+ if(tmp == NULL)
+ goto io_err;
+
+#endif /* !DOS */
+
+ if((so = so_get(FileStar, tmp, WRITE_ACCESS)) == NULL)
+ goto io_err;
+ }
+
+ if(!(flags & WRP_PRESERV_WRITTEN))
+ for(var = ps->vars; var->name != NULL; var++)
+ var->been_written = 0;
+
+ if(prc->type == Loc && ps->first_time_user &&
+ !so_puts(so, native_nl(cf_text_comment)))
+ goto io_err;
+
+ /* Write out what was in the .pinerc */
+ for(pline = prc->pinerc_lines;
+ pline && (pline->is_var || pline->line); pline++){
+ if(pline->is_var){
+ var = pline->var;
+
+ if(var->is_list)
+ lval = LVAL(var, which);
+ else
+ pval = PVAL(var, which);
+
+ /* variable is not set */
+ if((var->is_list && (!lval || !lval[0])) ||
+ (!var->is_list && !pval)){
+ /* leave null variables out of remote pinerc */
+ if(prc->type == Loc &&
+ (!so_puts(so, var->name) || !so_puts(so, "=") ||
+ !so_puts(so, NEWLINE)))
+ goto io_err;
+ }
+ /* var is set to empty string */
+ else if((var->is_list && lval[0][0] == '\0') ||
+ (!var->is_list && pval[0] == '\0')){
+ if(!so_puts(so, var->name) || !so_puts(so, "=") ||
+ !so_puts(so, quotes) || !so_puts(so, NEWLINE))
+ goto io_err;
+ }
+ else{
+ if(var->is_list){
+ int i = 0;
+
+ for(i = 0; lval[i]; i++){
+ snprintf(tmp_20k_buf, 10000, "%s%s%s%s%s",
+ (i) ? "\t" : var->name,
+ (i) ? "" : "=",
+ lval[i][0] ? lval[i] : quotes,
+ lval[i+1] ? "," : "", NEWLINE);
+ tmp_20k_buf[10000-1] = '\0';
+ if(!so_puts(so, bc ? backcompat_convert_from_utf8(tmp_20k_buf+10000, SIZEOF_20KBUF-10000, tmp_20k_buf) : tmp_20k_buf))
+ goto io_err;
+ }
+ }
+ else{
+ snprintf(tmp_20k_buf, 10000, "%s=%s%s%s%s",
+ var->name,
+ (pline->is_quoted && pval[0] != '\"')
+ ? "\"" : "",
+ pval,
+ (pline->is_quoted && pval[0] != '\"')
+ ? "\"" : "", NEWLINE);
+ tmp_20k_buf[10000-1] = '\0';
+ if(!so_puts(so, bc ? backcompat_convert_from_utf8(tmp_20k_buf+10000, SIZEOF_20KBUF-10000, tmp_20k_buf) : tmp_20k_buf))
+ goto io_err;
+ }
+ }
+
+ var->been_written = 1;
+
+ }else{
+ /*
+ * The description text should be changed into a message
+ * about the variable being obsolete when a variable is
+ * moved to obsolete status. We add that message before
+ * the variable unless it is already there. However, we
+ * leave the variable itself in case the user runs an old
+ * version of pine again. Note that we have read in the
+ * value of the variable in read_pinerc and translated it
+ * into a new variable if appropriate.
+ */
+ if(pline->obsolete_var && prc->type == Loc){
+ if(pline <= prc->pinerc_lines || (pline-1)->line == NULL ||
+ strlen((pline-1)->line) < 3 ||
+ strucmp((pline-1)->line+2, pline->var->descrip) != 0)
+ if(!so_puts(so, "# ") ||
+ !so_puts(so, native_nl(pline->var->descrip)) ||
+ !so_puts(so, NEWLINE))
+ goto io_err;
+ }
+
+ /* remove comments from remote pinercs */
+ if((prc->type == Loc ||
+ (pline->line[0] != '#' && pline->line[0] != '\0')) &&
+ (!so_puts(so, pline->line) || !so_puts(so, NEWLINE)))
+ goto io_err;
+ }
+ }
+
+ /* Now write out all the variables not in the .pinerc */
+ for(var = ps->vars; var->name != NULL; var++){
+ if(!var->is_user || var->been_written || !var->is_used ||
+ var->is_obsolete || (var->is_onlymain && which != Main))
+ continue;
+
+ if(var->is_list)
+ lval = LVAL(var, which);
+ else
+ pval = PVAL(var, which);
+
+ /*
+ * set description to NULL to eliminate preceding
+ * blank and comment line.
+ */
+ if(prc->type == Loc && var->descrip && *var->descrip &&
+ (!so_puts(so, NEWLINE) || !so_puts(so, "# ") ||
+ !so_puts(so, native_nl(var->descrip)) || !so_puts(so, NEWLINE)))
+ goto io_err;
+
+ /* variable is not set */
+ /** Don't know what the global_val thing is for. SH, Mar 00 **/
+ if((var->is_list && (!lval || (!lval[0] && !var->global_val.l))) ||
+ (!var->is_list && !pval)){
+ /* leave null variables out of remote pinerc */
+ if(prc->type == Loc &&
+ (!so_puts(so, var->name) || !so_puts(so, "=") ||
+ !so_puts(so, NEWLINE)))
+ goto io_err;
+ }
+ /* var is set to empty string */
+ else if((var->is_list && (!lval[0] || !lval[0][0]))
+ || (!var->is_list && pval[0] == '\0')){
+ if(!so_puts(so, var->name) || !so_puts(so, "=") ||
+ !so_puts(so, quotes) || !so_puts(so, NEWLINE))
+ goto io_err;
+ }
+ else if(var->is_list){
+ int i = 0;
+
+ for(i = 0; lval[i] ; i++){
+ snprintf(tmp_20k_buf, 10000, "%s%s%s%s%s",
+ (i) ? "\t" : var->name,
+ (i) ? "" : "=",
+ lval[i],
+ lval[i+1] ? "," : "", NEWLINE);
+ tmp_20k_buf[10000-1] = '\0';
+ if(!so_puts(so, bc ? backcompat_convert_from_utf8(tmp_20k_buf+10000, SIZEOF_20KBUF-10000, tmp_20k_buf) : tmp_20k_buf))
+ goto io_err;
+ }
+ }
+ else{
+ char *pconverted;
+
+ pconverted = bc ? backcompat_convert_from_utf8(tmp_20k_buf, SIZEOF_20KBUF, pval) : pval;
+
+ if(!so_puts(so, var->name) || !so_puts(so, "=") ||
+ !so_puts(so, pconverted) || !so_puts(so, NEWLINE))
+ goto io_err;
+ }
+ }
+
+ if(!(rd && rd->flags & NO_FILE)){
+ if(so_give(&so))
+ goto io_err;
+
+ file_attrib_copy(tmp, filename);
+ if(rename_file(tmp, filename) < 0)
+ goto io_err;
+ }
+
+ if(prc->type != Loc){
+ int e, we_cancel;
+ char datebuf[200];
+
+ datebuf[0] = '\0';
+
+ if(!(flags & WRP_NOUSER))
+ we_cancel = busy_cue(_("Copying to remote config"), NULL, 1);
+
+ if((e = rd_update_remote(rd, datebuf)) != 0){
+ dprint((1,
+ "write_pinerc: error copying from %s to %s\n",
+ rd->lf ? rd->lf : "<memory>", rd->rn ? rd->rn : "?"));
+ if(!(flags & WRP_NOUSER)){
+ q_status_message2(SM_ORDER | SM_DING, 3, 5,
+ _("Error copying to %.200s: %.200s"),
+ rd->rn, error_description(errno));
+
+ q_status_message(SM_ORDER | SM_DING, 5, 5,
+ _("Copy of config to remote folder failed, changes NOT saved remotely"));
+ }
+ }
+ else{
+ rd_update_metadata(rd, datebuf);
+ rd->read_status = 'W';
+ rd_trim_remdata(&rd);
+ rd_close_remote(rd);
+ }
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+ }
+
+ prc->outstanding_pinerc_changes = 0;
+
+ if(prc->type == Loc){
+ prc->pinerc_written = name_file_mtime(filename);
+ dprint((2, "wrote pinerc: %s: time_pinerc_written = %ld\n",
+ pinrc ? pinrc : "?", (long) prc->pinerc_written));
+ }
+ else{
+ dprint((2, "wrote pinerc: %s\n", pinrc ? pinrc : "?"));
+ }
+
+ if(tmp){
+ our_unlink(tmp);
+ fs_give((void **)&tmp);
+ }
+
+ return(0);
+
+ io_err:
+ if(!(flags & WRP_NOUSER))
+ q_status_message2(SM_ORDER | SM_DING, 3, 5,
+ _("Error saving configuration in \"%.200s\": %.200s"),
+ pinrc, error_description(errno));
+
+ dprint((1, "Error writing %s : %s\n", pinrc ? pinrc : "?",
+ error_description(errno)));
+ if(rd)
+ rd->flags &= ~DO_REMTRIM;
+ if(tmp){
+ our_unlink(tmp);
+ fs_give((void **)&tmp);
+ }
+
+ return(-1);
+}
+
+
+/*
+ * The srcstr is UTF-8. In order to help the user with
+ * running this pine and an old pre-alpine pine on the same config
+ * file we attempt to convert the values of the config variables
+ * to the user's character set before writing.
+ */
+char *
+backcompat_convert_from_utf8(char *buf, size_t buflen, char *srcstr)
+{
+ char *converted = NULL;
+ char *p;
+ int its_ascii = 1;
+
+
+ for(p = srcstr; *p && its_ascii; p++)
+ if(*p & 0x80)
+ its_ascii = 0;
+
+ /* if it is ascii, go with that */
+ if(its_ascii)
+ converted = srcstr;
+ else{
+ char *trythischarset = NULL;
+
+ /*
+ * If it is possible to translate the UTF-8
+ * string into the user's character set then
+ * do that. For backwards compatibility with
+ * old pines.
+ */
+ if(ps_global->keyboard_charmap && ps_global->keyboard_charmap[0])
+ trythischarset = ps_global->keyboard_charmap;
+ else if(ps_global->display_charmap && ps_global->display_charmap[0])
+ trythischarset = ps_global->display_charmap;
+
+ if(trythischarset){
+ SIZEDTEXT src, dst;
+
+ src.data = (unsigned char *) srcstr;
+ src.size = strlen(srcstr);
+ memset(&dst, 0, sizeof(dst));
+ if(utf8_cstext(&src, trythischarset, &dst, 0)){
+ if(dst.data){
+ strncpy(buf, (char *) dst.data, buflen);
+ buf[buflen-1] = '\0';
+ fs_give((void **) &dst.data);
+ converted = buf;
+ }
+ }
+ }
+
+ if(!converted)
+ converted = srcstr;
+ }
+
+ return(converted);
+}
+
+
+/*
+ * Given a unix-style source string which may contain LFs,
+ * convert those to CRLFs if appropriate.
+ *
+ * Returns a pointer to the converted string. This will be a string
+ * stored in tmp_20k_buf.
+ *
+ * This is just used for the variable descriptions in the pinerc file. It
+ * could certainly be fancier. It simply converts all \n to NEWLINE.
+ */
+char *
+native_nl(char *src)
+{
+ char *q, *p;
+
+ tmp_20k_buf[0] = '\0';
+
+ if(src){
+ for(q = (char *)tmp_20k_buf; *src; src++){
+ if(*src == '\n'){
+ for(p = NEWLINE; *p; p++)
+ *q++ = *p;
+ }
+ else
+ *q++ = *src;
+ }
+
+ *q = '\0';
+ }
+
+ return((char *)tmp_20k_buf);
+}
+
+
+void
+quit_to_edit_msg(PINERC_S *prc)
+{
+ /* TRANSLATORS: The %s is either "Postload " or nothing. A Postload config file
+ is a type of config file. */
+ q_status_message1(SM_ORDER, 3, 4, _("Must quit Alpine to change %sconfig file."),
+ (prc == ps_global->post_prc) ? "Postload " : "");
+}
+
+
+/*------------------------------------------------------------
+ Return TRUE if the given string was a feature name present in the
+ pinerc as it was when pine was started...
+ ----*/
+int
+var_in_pinerc(char *s)
+{
+ PINERC_LINE *pline;
+
+ for(pline = ps_global->prc ? ps_global->prc->pinerc_lines : NULL;
+ pline && (pline->var || pline->line); pline++)
+ if(pline->var && pline->var->name && !strucmp(s, pline->var->name))
+ return(1);
+
+ for(pline = ps_global->post_prc ? ps_global->post_prc->pinerc_lines : NULL;
+ pline && (pline->var || pline->line); pline++)
+ if(pline->var && pline->var->name && !strucmp(s, pline->var->name))
+ return(1);
+
+ return(0);
+}
+
+
+/*------------------------------------------------------------
+ Free resources associated with pinerc_lines data
+ ----*/
+void
+free_pinerc_lines(PINERC_LINE **pinerc_lines)
+{
+ PINERC_LINE *pline;
+
+ if(pinerc_lines && *pinerc_lines){
+ for(pline = *pinerc_lines; pline->var || pline->line; pline++)
+ if(pline->line)
+ fs_give((void **)&pline->line);
+
+ fs_give((void **)pinerc_lines);
+ }
+}
+
+
+/*------------------------------------------------------------
+ Dump out a global pine.conf on the standard output with fresh
+ comments. Preserves variables currently set in SYSTEM_PINERC, if any.
+ ----*/
+void
+dump_global_conf(void)
+{
+ FILE *f;
+ struct variable *var;
+ PINERC_S *prc;
+
+ prc = new_pinerc_s(SYSTEM_PINERC);
+ read_pinerc(prc, variables, ParseGlobal);
+ if(prc)
+ free_pinerc_s(&prc);
+
+ f = stdout;
+ if(f == NULL)
+ goto io_err;
+
+ fprintf(f, "# %s -- system wide pine configuration\n#\n",
+ SYSTEM_PINERC);
+ fprintf(f, "# Values here affect all pine users unless they've overridden the values\n");
+ fprintf(f, "# in their .pinerc files. A copy of this file with current comments may\n");
+ fprintf(f, "# be obtained by running \"pine -conf\". It will be printed to standard output.\n#\n");
+ fprintf(f,"# For a variable to be unset its value must be null/blank. This is not the\n");
+ fprintf(f,"# same as the value of \"empty string\", which can be used to effectively\n");
+ fprintf(f,"# \"unset\" a variable that has a default or previously assigned value.\n");
+ fprintf(f,"# To set a variable to the empty string its value should be \"\".\n");
+ fprintf(f,"# Switch variables are set to either \"yes\" or \"no\", and default to \"no\".\n");
+ fprintf(f,"# Except for feature-list items, which are additive, values set in the\n");
+ fprintf(f,"# .pinerc file replace those in pine.conf, and those in pine.conf.fixed\n");
+ fprintf(f,"# over-ride all others. Features can be over-ridden in .pinerc or\n");
+ fprintf(f,"# pine.conf.fixed by pre-pending the feature name with \"no-\".\n#\n");
+ fprintf(f,"# (These comments are automatically inserted.)\n");
+
+ for(var = variables; var->name != NULL; var++){
+ if(!var->is_global || !var->is_used || var->is_obsolete)
+ continue;
+
+ if(var->descrip && *var->descrip){
+ if(fprintf(f, "\n# %s\n", var->descrip) == EOF)
+ goto io_err;
+ }
+
+ if(var->is_list){
+ if(var->global_val.l == NULL){
+ if(fprintf(f, "%s=\n", var->name) == EOF)
+ goto io_err;
+ }else{
+ int i;
+
+ for(i=0; var->global_val.l[i]; i++)
+ if(fprintf(f, "%s%s%s%s\n", (i) ? "\t" : var->name,
+ (i) ? "" : "=", var->global_val.l[i],
+ var->global_val.l[i+1] ? ",":"") == EOF)
+ goto io_err;
+ }
+ }else{
+ if(var->global_val.p == NULL){
+ if(fprintf(f, "%s=\n", var->name) == EOF)
+ goto io_err;
+ }else if(strlen(var->global_val.p) == 0){
+ if(fprintf(f, "%s=\"\"\n", var->name) == EOF)
+ goto io_err;
+ }else{
+ if(fprintf(f,"%s=%s\n",var->name,var->global_val.p) == EOF)
+ goto io_err;
+ }
+ }
+ }
+ exit(0);
+
+
+ io_err:
+ fprintf(stderr, "Error writing config to stdout: %s\n",
+ error_description(errno));
+ exit(-1);
+}
+
+
+/*------------------------------------------------------------
+ Dump out a pinerc to filename with fresh
+ comments. Preserves variables currently set in pinerc, if any.
+ ----*/
+void
+dump_new_pinerc(char *filename)
+{
+ FILE *f;
+ struct variable *var;
+ char buf[MAXPATH], *p;
+ PINERC_S *prc;
+
+
+ p = ps_global->pinerc;
+
+#if defined(DOS) || defined(OS2)
+ if(!ps_global->pinerc){
+ char *p;
+
+ if(p = getenv("PINERC")){
+ ps_global->pinerc = cpystr(p);
+ }else{
+ char buf2[MAXPATH];
+ build_path(buf2, ps_global->home_dir, DF_PINEDIR, sizeof(buf2));
+ build_path(buf, buf2, SYSTEM_PINERC, sizeof(buf));
+ }
+
+ p = buf;
+ }
+#else /* !DOS */
+ if(!ps_global->pinerc){
+ build_path(buf, ps_global->home_dir, ".pinerc", sizeof(buf));
+ p = buf;
+ }
+#endif /* !DOS */
+
+ prc = new_pinerc_s(p);
+ read_pinerc(prc, variables, ParsePers);
+ if(prc)
+ free_pinerc_s(&prc);
+
+ f = NULL;;
+ if(filename[0] == '\0'){
+ fprintf(stderr, "Missing argument to \"-pinerc\".\n");
+ }else if(!strcmp(filename, "-")){
+ f = stdout;
+ }else{
+ f = our_fopen(filename, "wb");
+ }
+
+ if(f == NULL)
+ goto io_err;
+
+ if(fprintf(f, "%s", cf_text_comment) == EOF)
+ goto io_err;
+
+ for(var = variables; var->name != NULL; var++){
+ dprint((7,"write_pinerc: %s = %s\n",
+ var->name ? var->name : "?",
+ var->main_user_val.p ? var->main_user_val.p : "<not set>"));
+ if(!var->is_user || !var->is_used || var->is_obsolete)
+ continue;
+
+ /*
+ * set description to NULL to eliminate preceding
+ * blank and comment line.
+ */
+ if(var->descrip && *var->descrip){
+ if(fprintf(f, "\n# %s\n", var->descrip) == EOF)
+ goto io_err;
+ }
+
+ if(var->is_list){
+ if(var->main_user_val.l == NULL){
+ if(fprintf(f, "%s=\n", var->name) == EOF)
+ goto io_err;
+ }else{
+ int i;
+
+ for(i=0; var->main_user_val.l[i]; i++)
+ if(fprintf(f, "%s%s%s%s\n", (i) ? "\t" : var->name,
+ (i) ? "" : "=", var->main_user_val.l[i],
+ var->main_user_val.l[i+1] ? ",":"") == EOF)
+ goto io_err;
+ }
+ }else{
+ if(var->main_user_val.p == NULL){
+ if(fprintf(f, "%s=\n", var->name) == EOF)
+ goto io_err;
+ }else if(strlen(var->main_user_val.p) == 0){
+ if(fprintf(f, "%s=\"\"\n", var->name) == EOF)
+ goto io_err;
+ }else{
+ if(fprintf(f,"%s=%s\n",var->name,var->main_user_val.p) == EOF)
+ goto io_err;
+ }
+ }
+ }
+ exit(0);
+
+
+io_err:
+ snprintf(buf, sizeof(buf), "Error writing config to %s: %s\n",
+ filename, error_description(errno));
+ exceptional_exit(buf, -1);
+}
+
+
+/*----------------------------------------------------------------------
+ Set a user variable and save the .pinerc
+
+ Args: var -- The index of the variable to set from conftype.h (V_....)
+ value -- The string to set the value to
+
+ Result: -1 is returned on failure and 0 is returned on success
+
+ The vars data structure is updated and the pinerc saved.
+ ----*/
+int
+set_variable(int var, char *value, int expand, int commit, EditWhich which)
+{
+ struct variable *v;
+ char **apval;
+ PINERC_S *prc;
+
+ v = &ps_global->vars[var];
+
+ if(!v->is_user)
+ panic1("Trying to set non-user variable %s", v->name);
+
+ /* Override value of which, at most one of these should be set */
+ if(v->is_onlymain)
+ which = Main;
+ else if(v->is_outermost)
+ which = ps_global->ew_for_except_vars;
+
+ apval = APVAL(v, which);
+
+ if(!apval)
+ return(-1);
+
+ if(*apval)
+ fs_give((void **)apval);
+
+ *apval = value ? cpystr(value) : NULL;
+ set_current_val(v, expand, FALSE);
+
+ switch(which){
+ case Main:
+ prc = ps_global->prc;
+ break;
+ case Post:
+ prc = ps_global->post_prc;
+ break;
+ default:
+ break;
+ }
+
+ if(prc)
+ prc->outstanding_pinerc_changes = 1;
+
+ return(commit ? write_pinerc(ps_global, which, WRP_NONE) : 0);
+}
+
+
+/*----------------------------------------------------------------------
+ Set a user variable list and save the .pinerc
+
+ Args: var -- The index of the variable to set from conftype.h (V_....)
+ lvalue -- The list to set the value to
+
+ Result: -1 is returned on failure and 0 is returned on success
+
+ The vars data structure is updated and if write_it, the pinerc is saved.
+ ----*/
+int
+set_variable_list(int var, char **lvalue, int write_it, EditWhich which)
+{
+ char ***alval;
+ int i;
+ struct variable *v = &ps_global->vars[var];
+ PINERC_S *prc;
+
+ if(!v->is_user || !v->is_list)
+ panic1("BOTCH: Trying to set non-user or non-list variable %s", v->name);
+
+ /* Override value of which, at most one of these should be set */
+ if(v->is_onlymain)
+ which = Main;
+ else if(v->is_outermost)
+ which = ps_global->ew_for_except_vars;
+
+ alval = ALVAL(v, which);
+ if(!alval)
+ return(-1);
+
+ if(*alval)
+ free_list_array(alval);
+
+ if(lvalue){
+ for(i = 0; lvalue[i] ; i++) /* count elements */
+ ;
+
+ *alval = (char **) fs_get((i+1) * sizeof(char *));
+
+ for(i = 0; lvalue[i] ; i++)
+ (*alval)[i] = cpystr(lvalue[i]);
+
+ (*alval)[i] = NULL;
+ }
+
+ set_current_val(v, TRUE, FALSE);
+
+ switch(which){
+ case Main:
+ prc = ps_global->prc;
+ break;
+ case Post:
+ prc = ps_global->post_prc;
+ break;
+ default:
+ break;
+ }
+
+ if(prc)
+ prc->outstanding_pinerc_changes = 1;
+
+ return(write_it ? write_pinerc(ps_global, which, WRP_NONE) : 0);
+}
+
+
+void
+set_current_color_vals(struct pine *ps)
+{
+ struct variable *vars = ps->vars;
+ int later_color_is_set = 0;
+
+ set_current_val(&vars[V_NORM_FORE_COLOR], TRUE, TRUE);
+ set_current_val(&vars[V_NORM_BACK_COLOR], TRUE, TRUE);
+ pico_nfcolor(VAR_NORM_FORE_COLOR);
+ pico_nbcolor(VAR_NORM_BACK_COLOR);
+
+ set_current_val(&vars[V_REV_FORE_COLOR], TRUE, TRUE);
+ set_current_val(&vars[V_REV_BACK_COLOR], TRUE, TRUE);
+ pico_rfcolor(VAR_REV_FORE_COLOR);
+ pico_rbcolor(VAR_REV_BACK_COLOR);
+
+ set_color_val(&vars[V_TITLE_FORE_COLOR], 1);
+ set_color_val(&vars[V_TITLECLOSED_FORE_COLOR], 0);
+ set_color_val(&vars[V_STATUS_FORE_COLOR], 1);
+ set_color_val(&vars[V_KEYLABEL_FORE_COLOR], 1);
+ set_color_val(&vars[V_KEYNAME_FORE_COLOR], 1);
+ set_color_val(&vars[V_SLCTBL_FORE_COLOR], 1);
+ set_color_val(&vars[V_METAMSG_FORE_COLOR], 1);
+ set_color_val(&vars[V_PROMPT_FORE_COLOR], 1);
+ set_color_val(&vars[V_HEADER_GENERAL_FORE_COLOR], 1);
+ set_color_val(&vars[V_IND_PLUS_FORE_COLOR], 0);
+ set_color_val(&vars[V_IND_IMP_FORE_COLOR], 0);
+ set_color_val(&vars[V_IND_DEL_FORE_COLOR], 0);
+ set_color_val(&vars[V_IND_HIPRI_FORE_COLOR], 0);
+ set_color_val(&vars[V_IND_LOPRI_FORE_COLOR], 0);
+ set_color_val(&vars[V_IND_ANS_FORE_COLOR], 0);
+ set_color_val(&vars[V_IND_NEW_FORE_COLOR], 0);
+ set_color_val(&vars[V_IND_REC_FORE_COLOR], 0);
+ set_color_val(&vars[V_IND_FWD_FORE_COLOR], 0);
+ set_color_val(&vars[V_IND_UNS_FORE_COLOR], 0);
+ set_color_val(&vars[V_IND_ARR_FORE_COLOR], 0);
+ set_color_val(&vars[V_IND_SUBJ_FORE_COLOR], 0);
+ set_color_val(&vars[V_IND_FROM_FORE_COLOR], 0);
+ set_color_val(&vars[V_IND_OP_FORE_COLOR], 0);
+ set_color_val(&vars[V_INCUNSEEN_FORE_COLOR], 0);
+ set_color_val(&vars[V_SIGNATURE_FORE_COLOR], 0);
+
+ set_current_val(&ps->vars[V_VIEW_HDR_COLORS], TRUE, TRUE);
+ set_current_val(&ps->vars[V_KW_COLORS], TRUE, TRUE);
+ set_custom_spec_colors(ps);
+
+ /*
+ * Set up the quoting colors. If a later color is set but not an earlier
+ * color we set the earlier color to Normal to make it easier when
+ * we go to use the colors. However, if the only quote colors set are
+ * Normal that is the same as no settings, so delete them.
+ */
+ set_color_val(&vars[V_QUOTE1_FORE_COLOR], 0);
+ set_color_val(&vars[V_QUOTE2_FORE_COLOR], 0);
+ set_color_val(&vars[V_QUOTE3_FORE_COLOR], 0);
+
+ if((!(VAR_QUOTE3_FORE_COLOR && VAR_QUOTE3_BACK_COLOR) ||
+ (!strucmp(VAR_QUOTE3_FORE_COLOR, VAR_NORM_FORE_COLOR) &&
+ !strucmp(VAR_QUOTE3_BACK_COLOR, VAR_NORM_BACK_COLOR))) &&
+ (!(VAR_QUOTE2_FORE_COLOR && VAR_QUOTE2_BACK_COLOR) ||
+ (!strucmp(VAR_QUOTE2_FORE_COLOR, VAR_NORM_FORE_COLOR) &&
+ !strucmp(VAR_QUOTE2_BACK_COLOR, VAR_NORM_BACK_COLOR))) &&
+ (!(VAR_QUOTE1_FORE_COLOR && VAR_QUOTE1_BACK_COLOR) ||
+ (!strucmp(VAR_QUOTE1_FORE_COLOR, VAR_NORM_FORE_COLOR) &&
+ !strucmp(VAR_QUOTE1_BACK_COLOR, VAR_NORM_BACK_COLOR)))){
+ /*
+ * They are all either Normal or not set. Delete them all.
+ */
+ if(VAR_QUOTE3_FORE_COLOR)
+ fs_give((void **)&VAR_QUOTE3_FORE_COLOR);
+ if(VAR_QUOTE3_BACK_COLOR)
+ fs_give((void **)&VAR_QUOTE3_BACK_COLOR);
+ if(VAR_QUOTE2_FORE_COLOR)
+ fs_give((void **)&VAR_QUOTE2_FORE_COLOR);
+ if(VAR_QUOTE2_BACK_COLOR)
+ fs_give((void **)&VAR_QUOTE2_BACK_COLOR);
+ if(VAR_QUOTE1_FORE_COLOR)
+ fs_give((void **)&VAR_QUOTE1_FORE_COLOR);
+ if(VAR_QUOTE1_BACK_COLOR)
+ fs_give((void **)&VAR_QUOTE1_BACK_COLOR);
+ }
+ else{ /* something is non-Normal */
+ if(VAR_QUOTE3_FORE_COLOR && VAR_QUOTE3_BACK_COLOR)
+ later_color_is_set++;
+
+ /* if 3 is set but not 2, set 2 to Normal */
+ if(VAR_QUOTE2_FORE_COLOR && VAR_QUOTE2_BACK_COLOR)
+ later_color_is_set++;
+ else if(later_color_is_set)
+ set_color_val(&vars[V_QUOTE2_FORE_COLOR], 1);
+
+ /* if 3 or 2 is set but not 1, set 1 to Normal */
+ if(VAR_QUOTE1_FORE_COLOR && VAR_QUOTE1_BACK_COLOR)
+ later_color_is_set++;
+ else if(later_color_is_set)
+ set_color_val(&vars[V_QUOTE1_FORE_COLOR], 1);
+ }
+
+#ifdef _WINDOWS
+ if(ps->pre441){
+ int conv_main = 0, conv_post = 0;
+
+ ps->pre441 = 0;
+ if(ps->prc && !unix_color_style_in_pinerc(ps->prc)){
+ conv_main = convert_pc_gray_names(ps, ps->prc, Main);
+ if(conv_main)
+ ps->prc->outstanding_pinerc_changes = 1;
+ }
+
+
+ if(ps->post_prc && !unix_color_style_in_pinerc(ps->post_prc)){
+ conv_post = convert_pc_gray_names(ps, ps->post_prc, Post);
+ if(conv_post)
+ ps->post_prc->outstanding_pinerc_changes = 1;
+ }
+
+ if(conv_main || conv_post){
+ if(conv_main)
+ write_pinerc(ps, Main, WRP_NONE);
+
+ if(conv_post)
+ write_pinerc(ps, Post, WRP_NONE);
+
+ set_current_color_vals(ps);
+ }
+ }
+#endif /* _WINDOWS */
+
+ pico_set_normal_color();
+}
+
+
+/*
+ * Set current_val for the foreground and background color vars, which
+ * are assumed to be in order. If a set_current_val on them doesn't
+ * produce current_vals, then use the colors from defvar to set those
+ * current_vals.
+ */
+void
+set_color_val(struct variable *v, int use_default)
+{
+ set_current_val(v, TRUE, TRUE);
+ set_current_val(v+1, TRUE, TRUE);
+
+ if(!(v->current_val.p && v->current_val.p[0] &&
+ (v+1)->current_val.p && (v+1)->current_val.p[0])){
+ struct variable *defvar;
+
+ if(v->current_val.p)
+ fs_give((void **)&v->current_val.p);
+ if((v+1)->current_val.p)
+ fs_give((void **)&(v+1)->current_val.p);
+
+ if(!use_default)
+ return;
+
+ if(var_defaults_to_rev(v))
+ defvar = &ps_global->vars[V_REV_FORE_COLOR];
+ else
+ defvar = &ps_global->vars[V_NORM_FORE_COLOR];
+
+ /* use default vars values instead */
+ if(defvar && defvar->current_val.p && defvar->current_val.p[0] &&
+ (defvar+1)->current_val.p && (defvar+1)->current_val.p[0]){
+ v->current_val.p = cpystr(defvar->current_val.p);
+ (v+1)->current_val.p = cpystr((defvar+1)->current_val.p);
+ }
+ }
+}
+
+
+int
+var_defaults_to_rev(struct variable *v)
+{
+ return(v == &ps_global->vars[V_REV_FORE_COLOR] ||
+ v == &ps_global->vars[V_TITLE_FORE_COLOR] ||
+ v == &ps_global->vars[V_STATUS_FORE_COLOR] ||
+ v == &ps_global->vars[V_KEYNAME_FORE_COLOR] ||
+ v == &ps_global->vars[V_PROMPT_FORE_COLOR]);
+}
+
+
+
+/*
+ * Each item in the list looks like:
+ *
+ * /HDR=<header>/FG=<foreground color>/BG=<background color>
+ *
+ * We separate the three pieces into an array of structures to make
+ * it easier to deal with later.
+ */
+void
+set_custom_spec_colors(struct pine *ps)
+{
+ if(ps->hdr_colors)
+ free_spec_colors(&ps->hdr_colors);
+
+ ps->hdr_colors = spec_colors_from_varlist(ps->VAR_VIEW_HDR_COLORS, 1);
+
+ /* fit keyword colors into the same structures for code re-use */
+ if(ps->kw_colors)
+ free_spec_colors(&ps->kw_colors);
+
+ ps->kw_colors = spec_colors_from_varlist(ps->VAR_KW_COLORS, 1);
+}
+
+
+/*
+ * Input is one item from config variable.
+ *
+ * Return value must be freed by caller. The return is a single SPEC_COLOR_S,
+ * not a list.
+ */
+SPEC_COLOR_S *
+spec_color_from_var(char *t, int already_expanded)
+{
+ char *p, *spec, *fg, *bg;
+ PATTERN_S *val;
+ SPEC_COLOR_S *new_hcolor = NULL;
+
+ if(t && t[0] && !strcmp(t, INHERIT)){
+ new_hcolor = (SPEC_COLOR_S *)fs_get(sizeof(*new_hcolor));
+ memset((void *)new_hcolor, 0, sizeof(*new_hcolor));
+ new_hcolor->inherit = 1;
+ }
+ else if(t && t[0]){
+ char tbuf[10000];
+
+ if(!already_expanded){
+ tbuf[0] = '\0';
+ if(expand_variables(tbuf, sizeof(tbuf), t, 0))
+ t = tbuf;
+ }
+
+ spec = fg = bg = NULL;
+ val = NULL;
+ if((p = srchstr(t, "/HDR=")) != NULL)
+ spec = remove_backslash_escapes(p+5);
+ if((p = srchstr(t, "/FG=")) != NULL)
+ fg = remove_backslash_escapes(p+4);
+ if((p = srchstr(t, "/BG=")) != NULL)
+ bg = remove_backslash_escapes(p+4);
+ val = parse_pattern("VAL", t, 0);
+
+ if(spec && *spec){
+ /* remove colons */
+ if((p = strindex(spec, ':')) != NULL)
+ *p = '\0';
+
+ new_hcolor = (SPEC_COLOR_S *)fs_get(sizeof(*new_hcolor));
+ memset((void *)new_hcolor, 0, sizeof(*new_hcolor));
+ new_hcolor->spec = spec;
+ new_hcolor->fg = fg;
+ new_hcolor->bg = bg;
+ new_hcolor->val = val;
+ }
+ else{
+ if(spec)
+ fs_give((void **)&spec);
+ if(fg)
+ fs_give((void **)&fg);
+ if(bg)
+ fs_give((void **)&bg);
+ if(val)
+ free_pattern(&val);
+ }
+ }
+
+ return(new_hcolor);
+}
+
+
+/*
+ * Input is a list from config file.
+ *
+ * Return value may be a list of SPEC_COLOR_S and must be freed by caller.
+ */
+SPEC_COLOR_S *
+spec_colors_from_varlist(char **varlist, int already_expanded)
+{
+ char **s, *t;
+ SPEC_COLOR_S *new_hc = NULL;
+ SPEC_COLOR_S *new_hcolor, **nexthc;
+
+ nexthc = &new_hc;
+ if(varlist){
+ for(s = varlist; (t = *s) != NULL; s++){
+ if(t[0]){
+ new_hcolor = spec_color_from_var(t, already_expanded);
+ if(new_hcolor){
+ *nexthc = new_hcolor;
+ nexthc = &new_hcolor->next;
+ }
+ }
+ }
+ }
+
+ return(new_hc);
+}
+
+
+/*
+ * Returns allocated charstar suitable for config var for a single
+ * SPEC_COLOR_S.
+ */
+char *
+var_from_spec_color(SPEC_COLOR_S *hc)
+{
+ char *ret_val = NULL;
+ char *p, *spec = NULL, *fg = NULL, *bg = NULL, *val = NULL;
+ size_t len;
+
+ if(hc && hc->inherit)
+ ret_val = cpystr(INHERIT);
+ else if(hc){
+ if(hc->spec)
+ spec = add_viewerhdr_escapes(hc->spec);
+ if(hc->fg)
+ fg = add_viewerhdr_escapes(hc->fg);
+ if(hc->bg)
+ bg = add_viewerhdr_escapes(hc->bg);
+ if(hc->val){
+ p = pattern_to_string(hc->val);
+ if(p){
+ val = add_viewerhdr_escapes(p);
+ fs_give((void **)&p);
+ }
+ }
+
+ len = strlen("/HDR=/FG=/BG=") + strlen(spec ? spec : "") +
+ strlen(fg ? fg : "") + strlen(bg ? bg : "") +
+ strlen(val ? "/VAL=" : "") + strlen(val ? val : "");
+ ret_val = (char *) fs_get(len + 1);
+ snprintf(ret_val, len+1, "/HDR=%s/FG=%s/BG=%s%s%s",
+ spec ? spec : "", fg ? fg : "", bg ? bg : "",
+ val ? "/VAL=" : "", val ? val : "");
+
+ if(spec)
+ fs_give((void **)&spec);
+ if(fg)
+ fs_give((void **)&fg);
+ if(bg)
+ fs_give((void **)&bg);
+ if(val)
+ fs_give((void **)&val);
+ }
+
+ return(ret_val);
+}
+
+
+/*
+ * Returns allocated charstar suitable for config var for a single
+ * SPEC_COLOR_S.
+ */
+char **
+varlist_from_spec_colors(SPEC_COLOR_S *hcolors)
+{
+ SPEC_COLOR_S *hc;
+ char **ret_val = NULL;
+ int i;
+
+ /* count how many */
+ for(hc = hcolors, i = 0; hc; hc = hc->next, i++)
+ ;
+
+ ret_val = (char **)fs_get((i+1) * sizeof(*ret_val));
+ memset((void *)ret_val, 0, (i+1) * sizeof(*ret_val));
+ for(hc = hcolors, i = 0; hc; hc = hc->next, i++)
+ ret_val[i] = var_from_spec_color(hc);
+
+ return(ret_val);
+}
+
+
+void
+update_posting_charset(struct pine *ps, int revert)
+{
+#ifndef _WINDOWS
+ if(F_ON(F_USE_SYSTEM_TRANS, ps)){
+ if(!revert)
+ q_status_message(SM_ORDER, 5, 5, _("This change has no effect because feature Use-System-Translation is on"));
+ }
+ else{
+#endif /* ! _WINDOWS */
+ if(ps->posting_charmap)
+ fs_give((void **) &ps->posting_charmap);
+
+ if(ps->VAR_POST_CHAR_SET){
+ ps->posting_charmap = cpystr(ps->VAR_POST_CHAR_SET);
+ if(!posting_charset_is_supported(ps->posting_charmap)){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,
+ _("Posting-Character set \"%s\" is unsupported, using UTF-8"),
+ ps->posting_charmap);
+ q_status_message(SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ fs_give((void **) &ps->posting_charmap);
+ ps->posting_charmap = cpystr("UTF-8");
+ }
+ }
+ else
+ ps->posting_charmap = cpystr("UTF-8");
+#ifndef _WINDOWS
+ }
+#endif /* ! _WINDOWS */
+}
+
+
+#define FIXED_COMMENT _("(fixed)")
+#define DEFAULT_COMMENT _("(default)")
+#define OVERRIDE_COMMENT _("(overridden)")
+
+int
+feature_gets_an_x(struct pine *ps, struct variable *var, FEATURE_S *feature,
+ char **comment, EditWhich ew)
+{
+ char **lval, **lvalexc, **lvalnorm;
+ char *def = DEFAULT_COMMENT;
+ int j, done = 0;
+ int feature_fixed_on = 0, feature_fixed_off = 0;
+
+ if(comment)
+ *comment = NULL;
+
+ lval = LVAL(var, ew);
+ lvalexc = LVAL(var, ps->ew_for_except_vars);
+ lvalnorm = LVAL(var, Main);
+
+ /* feature value is administratively fixed */
+ if((j = feature_in_list(var->fixed_val.l, feature->name)) != 0){
+ if(j == 1)
+ feature_fixed_on++;
+ else if(j == -1)
+ feature_fixed_off++;
+
+ done++;
+ if(comment)
+ *comment = FIXED_COMMENT;
+ }
+
+ /*
+ * We have an exceptions config setting which overrides anything
+ * we do here, in the normal config.
+ */
+ if(!done &&
+ ps->ew_for_except_vars != Main && ew == Main &&
+ feature_in_list(lvalexc, feature->name)){
+ done++;
+ if(comment)
+ *comment = OVERRIDE_COMMENT;
+ }
+
+ /*
+ * Feature is set On in default but not set here.
+ */
+ if(!done &&
+ !feature_in_list(lval, feature->name) &&
+ ((feature_in_list(var->global_val.l, feature->name) == 1) ||
+ ((ps->ew_for_except_vars != Main &&
+ ew == ps->ew_for_except_vars &&
+ feature_in_list(lvalnorm, feature->name) == 1)))){
+ done = 17;
+ if(comment)
+ *comment = def;
+ }
+
+ if(!done &&
+ feature->defval &&
+ !feature_in_list(lval, feature->name) &&
+ !feature_in_list(var->global_val.l, feature->name) &&
+ (ps->ew_for_except_vars == Main ||
+ ew != ps->ew_for_except_vars ||
+ !feature_in_list(lvalnorm, feature->name))){
+ done = 17;
+ if(comment)
+ *comment = def;
+ }
+
+ return(feature_fixed_on ||
+ (!feature_fixed_off &&
+ (done == 17 ||
+ test_feature(lval, feature->name,
+ test_old_growth_bits(ps, feature->id)))));
+}
+
+
+int
+longest_feature_comment(struct pine *ps, EditWhich ew)
+{
+ int lc = 0;
+
+ lc = MAX(lc, utf8_width(FIXED_COMMENT));
+ lc = MAX(lc, utf8_width(DEFAULT_COMMENT));
+ if(ps->ew_for_except_vars != Main && ew == Main)
+ lc = MAX(lc, utf8_width(OVERRIDE_COMMENT));
+
+ return(lc);
+}
+
+
+void
+toggle_feature(struct pine *ps, struct variable *var, FEATURE_S *f,
+ int just_flip_value, EditWhich ew)
+{
+ char **vp, *p, **lval, ***alval;
+ int og, on_before, was_set;
+ char *err;
+ long l;
+
+ og = test_old_growth_bits(ps, f->id);
+
+ /*
+ * if this feature is in the fixed set, or old-growth is in the fixed
+ * set and this feature is in the old-growth set, don't alter it...
+ */
+ for(vp = var->fixed_val.l; vp && *vp; vp++){
+ p = (struncmp(*vp, "no-", 3)) ? *vp : *vp + 3;
+ if(!strucmp(p, f->name) || (og && !strucmp(p, "old-growth"))){
+ q_status_message(SM_ORDER, 3, 3,
+ /* TRANSLATORS: In the configuration screen, telling the user we
+ can't change this option because the system administrator
+ prohibits it. */
+ _("Can't change value fixed by sys-admin."));
+ return;
+ }
+ }
+
+ on_before = F_ON(f->id, ps);
+
+ lval = LVAL(var, ew);
+ alval = ALVAL(var, ew);
+ if(just_flip_value)
+ was_set = test_feature(lval, f->name, og);
+ else
+ was_set = feature_gets_an_x(ps, var, f, NULL, ew);
+
+ if(alval)
+ set_feature(alval, f->name, !was_set);
+
+ set_feature_list_current_val(var);
+ process_feature_list(ps, var->current_val.l, 0, 0, 0);
+
+ /*
+ * Handle any features that need special attention here...
+ */
+ if(on_before != F_ON(f->id, ps))
+ switch(f->id){
+ case F_QUOTE_ALL_FROMS :
+ mail_parameters(NULL,SET_FROMWIDGET,F_ON(f->id,ps) ? VOIDT : NIL);
+ break;
+
+ case F_FAKE_NEW_IN_NEWS :
+ if(IS_NEWS(ps->mail_stream))
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ "news-approximates-new-status won't affect current newsgroup until next open");
+
+ break;
+
+ case F_COLOR_LINE_IMPORTANT :
+ case F_DATES_TO_LOCAL :
+ clear_index_cache(ps->mail_stream, 0);
+ break;
+
+ case F_DISABLE_INDEX_LOCALE_DATES :
+ reset_index_format();
+ clear_index_cache(ps->mail_stream, 0);
+ break;
+
+ case F_MARK_FOR_CC :
+ clear_index_cache(ps->mail_stream, 0);
+ if(THREADING() && sp_viewing_a_thread(ps->mail_stream))
+ unview_thread(ps, ps->mail_stream, ps->msgmap);
+
+ break;
+
+ case F_HIDE_NNTP_PATH :
+ mail_parameters(NULL, SET_NNTPHIDEPATH,
+ F_ON(f->id, ps) ? VOIDT : NIL);
+ break;
+
+ case F_MAILDROPS_PRESERVE_STATE :
+ mail_parameters(NULL, SET_SNARFPRESERVE,
+ F_ON(f->id, ps) ? VOIDT : NIL);
+ break;
+
+ case F_DISABLE_SHARED_NAMESPACES :
+ mail_parameters(NULL, SET_DISABLEAUTOSHAREDNS,
+ F_ON(f->id, ps) ? VOIDT : NIL);
+ break;
+
+ case F_QUELL_LOCK_FAILURE_MSGS :
+ mail_parameters(NULL, SET_LOCKEACCESERROR,
+ F_ON(f->id, ps) ? VOIDT : NIL);
+ break;
+
+ case F_MULNEWSRC_HOSTNAMES_AS_TYPED :
+ l = F_ON(f->id, ps) ? 0L : 1L;
+ mail_parameters(NULL, SET_NEWSRCCANONHOST, (void *) l);
+ break;
+
+ case F_QUELL_INTERNAL_MSG :
+ mail_parameters(NULL, SET_USERHASNOLIFE,
+ F_ON(f->id, ps) ? VOIDT : NIL);
+ break;
+
+ case F_DISABLE_SETLOCALE_COLLATE :
+ set_collation(F_OFF(F_DISABLE_SETLOCALE_COLLATE, ps), 1);
+ break;
+
+#ifndef _WINDOWS
+ case F_USE_SYSTEM_TRANS :
+ err = NULL;
+ reset_character_set_stuff(&err);
+ if(err){
+ q_status_message(SM_ORDER | SM_DING, 3, 4, err);
+ fs_give((void **) &err);
+ }
+
+ break;
+#endif /* ! _WINDOWS */
+
+ case F_ENABLE_INCOMING_CHECKING :
+ if(!on_before && F_OFF(F_ENABLE_INCOMING, ps))
+ q_status_message(SM_ORDER, 0, 3, _("This option has no effect without Enable-Incoming-Folders"));
+
+ clear_incoming_valid_bits();
+ break;
+
+ case F_INCOMING_CHECKING_TOTAL :
+ case F_INCOMING_CHECKING_RECENT :
+ if(!on_before && F_OFF(F_ENABLE_INCOMING_CHECKING, ps))
+ q_status_message(SM_ORDER, 0, 3, _("This option has no effect without Enable-Incoming-Folders-Checking"));
+
+ clear_incoming_valid_bits();
+ break;
+
+ case F_THREAD_SORTS_BY_ARRIVAL :
+ clear_index_cache(ps->mail_stream, 0);
+ refresh_sort(ps->mail_stream, sp_msgmap(ps->mail_stream), SRT_NON);
+ break;
+
+#ifdef SMIME
+ case F_DONT_DO_SMIME :
+ smime_deinit();
+ break;
+
+#ifdef APPLEKEYCHAIN
+ case F_PUBLICCERTS_IN_KEYCHAIN :
+ smime_deinit();
+ break;
+#endif
+#endif
+
+ default :
+ break;
+ }
+}
+
+
+/*
+ * Returns 1 -- Feature is in the list and positive
+ * 0 -- Feature is not in the list at all
+ * -1 -- Feature is in the list and negative (no-)
+ */
+int
+feature_in_list(char **l, char *f)
+{
+ char *p;
+ int rv = 0, forced_off;
+
+ for(; l && *l; l++){
+ p = (forced_off = !struncmp(*l, "no-", 3)) ? *l + 3 : *l;
+ if(!strucmp(p, f))
+ rv = forced_off ? -1 : 1;
+ }
+
+ return(rv);
+}
+
+
+/*
+ * test_feature - runs thru a feature list, and returns:
+ * 1 if feature explicitly set and matches 'v'
+ * 0 if feature not explicitly set *or* doesn't match 'v'
+ */
+int
+test_feature(char **l, char *f, int g)
+{
+ char *p;
+ int rv = 0, forced_off;
+
+ for(; l && *l; l++){
+ p = (forced_off = !struncmp(*l, "no-", 3)) ? *l + 3 : *l;
+ if(!strucmp(p, f))
+ rv = !forced_off;
+ else if(g && !strucmp(p, "old-growth"))
+ rv = !forced_off;
+ }
+
+ return(rv);
+}
+
+
+void
+set_feature(char ***l, char *f, int v)
+{
+ char **list = l ? *l : NULL, newval[256];
+ int count = 0;
+
+ snprintf(newval, sizeof(newval), "%s%s", v ? "" : "no-", f);
+ for(; list && *list; list++, count++)
+ if((**list == '\0') /* anything can replace an empty value */
+ || !strucmp(((!struncmp(*list, "no-", 3)) ? *list + 3 : *list), f)){
+ fs_give((void **)list); /* replace with new value */
+ *list = cpystr(newval);
+ return;
+ }
+
+ /*
+ * if we got here, we didn't find it in the list, so grow the list
+ * and add it..
+ */
+ if(!*l)
+ *l = (char **)fs_get((count + 2) * sizeof(char *));
+ else
+ fs_resize((void **)l, (count + 2) * sizeof(char *));
+
+ (*l)[count] = cpystr(newval);
+ (*l)[count + 1] = NULL;
+}
+
+
+int
+reset_character_set_stuff(char **err)
+{
+ int use_system = 0;
+ char buf[1000];
+
+ if(err)
+ *err = NULL;
+
+ if(ps_global->display_charmap)
+ fs_give((void **) &ps_global->display_charmap);
+
+ if(ps_global->keyboard_charmap)
+ fs_give((void **) &ps_global->keyboard_charmap);
+
+ if(ps_global->posting_charmap)
+ fs_give((void **) &ps_global->posting_charmap);
+
+#ifdef _WINDOWS
+ ps_global->display_charmap = cpystr("UTF-8");
+#else /* UNIX */
+ if(ps_global->VAR_CHAR_SET)
+ ps_global->display_charmap = cpystr(ps_global->VAR_CHAR_SET);
+ else{
+#if HAVE_LANGINFO_H && defined(CODESET)
+ ps_global->display_charmap = cpystr(nl_langinfo_codeset_wrapper());
+#else
+ ps_global->display_charmap = cpystr("UTF-8");
+#endif
+ }
+#endif /* UNIX */
+
+ if(!ps_global->display_charmap)
+ ps_global->display_charmap = cpystr("US-ASCII");
+
+#ifdef _WINDOWS
+ ps_global->keyboard_charmap = cpystr("UTF-8");
+#else /* UNIX */
+ if(ps_global->VAR_KEY_CHAR_SET)
+ ps_global->keyboard_charmap = cpystr(ps_global->VAR_KEY_CHAR_SET);
+ else
+ ps_global->keyboard_charmap = cpystr(ps_global->display_charmap);
+
+ if(!ps_global->keyboard_charmap)
+ ps_global->keyboard_charmap = cpystr("US-ASCII");
+
+ if(F_ON(F_USE_SYSTEM_TRANS, ps_global)){
+#if PREREQ_FOR_SYS_TRANSLATION
+ use_system++;
+ /* This modifies its arguments */
+ if(setup_for_input_output(use_system, &ps_global->display_charmap,
+ &ps_global->keyboard_charmap,
+ &ps_global->input_cs, (err && *err) ? NULL : err) == -1)
+ return -1;
+#endif
+ }
+#endif /* UNIX */
+
+ if(!use_system){
+ if(setup_for_input_output(use_system, &ps_global->display_charmap,
+ &ps_global->keyboard_charmap,
+ &ps_global->input_cs, (err && *err) ? NULL : err) == -1)
+ return -1;
+ }
+
+ if(!use_system && ps_global->VAR_POST_CHAR_SET){
+ ps_global->posting_charmap = cpystr(ps_global->VAR_POST_CHAR_SET);
+ if(!posting_charset_is_supported(ps_global->posting_charmap)){
+ if(err && !*err){
+ snprintf(buf, sizeof(buf),
+ _("Posting-Character-Set \"%s\" is unsupported, using UTF-8"),
+ ps_global->posting_charmap);
+ *err = cpystr(buf);
+ }
+
+ fs_give((void **) &ps_global->posting_charmap);
+ ps_global->posting_charmap = cpystr("UTF-8");
+ }
+ }
+ else{
+ if(use_system && ps_global->VAR_POST_CHAR_SET
+ && strucmp(ps_global->VAR_POST_CHAR_SET, "UTF-8"))
+ if(err && !*err)
+ *err = cpystr(_("Posting-Character-Set is ignored with Use-System-Translation turned on"));
+
+ ps_global->posting_charmap = cpystr("UTF-8");
+ }
+
+ set_locale_charmap(ps_global->keyboard_charmap);
+
+ return(0);
+}
+
+
+/*
+ * Given a single printer string from the config file, returns pointers
+ * to alloc'd strings containing the printer nickname, the command,
+ * the init string, the trailer string, everything but the nickname string,
+ * and everything but the command string. All_but_cmd includes the trailing
+ * space at the end (the one before the command) but all_but_nick does not
+ * include the leading space (the one before the [).
+ * If you pass in a pointer it is guaranteed to come back pointing to an
+ * allocated string, even if it is just an empty string. It is ok to pass
+ * NULL for any of the six return strings.
+ */
+void
+parse_printer(char *input, char **nick, char **cmd, char **init, char **trailer,
+ char **all_but_nick, char **all_but_cmd)
+{
+ char *p, *q, *start, *saved_options = NULL;
+ int tmpsave, cnt;
+
+ if(!input)
+ input = "";
+
+ if(nick || all_but_nick){
+ if((p = srchstr(input, " [")) != NULL){
+ if(all_but_nick)
+ *all_but_nick = cpystr(p+1);
+
+ if(nick){
+ while(p-1 > input && isspace((unsigned char)*(p-1)))
+ p--;
+
+ tmpsave = *p;
+ *p = '\0';
+ *nick = cpystr(input);
+ *p = tmpsave;
+ }
+ }
+ else{
+ if(nick)
+ *nick = cpystr("");
+
+ if(all_but_nick)
+ *all_but_nick = cpystr(input);
+ }
+ }
+
+ if((p = srchstr(input, "] ")) != NULL){
+ do{
+ ++p;
+ }while(isspace((unsigned char)*p));
+
+ tmpsave = *p;
+ *p = '\0';
+ saved_options = cpystr(input);
+ *p = tmpsave;
+ }
+ else
+ p = input;
+
+ if(cmd)
+ *cmd = cpystr(p);
+
+ if(init){
+ if(saved_options && (p = srchstr(saved_options, "INIT="))){
+ start = p + strlen("INIT=");
+ for(cnt=0, p = start; *p && *(p+1) && isxpair(p); p += 2)
+ cnt++;
+
+ q = *init = (char *)fs_get((cnt + 1) * sizeof(char));
+ for(p = start; *p && *(p+1) && isxpair(p); p += 2)
+ *q++ = read_hex(p);
+
+ *q = '\0';
+ }
+ else
+ *init = cpystr("");
+ }
+
+ if(trailer){
+ if(saved_options && (p = srchstr(saved_options, "TRAILER="))){
+ start = p + strlen("TRAILER=");
+ for(cnt=0, p = start; *p && *(p+1) && isxpair(p); p += 2)
+ cnt++;
+
+ q = *trailer = (char *)fs_get((cnt + 1) * sizeof(char));
+ for(p = start; *p && *(p+1) && isxpair(p); p += 2)
+ *q++ = read_hex(p);
+
+ *q = '\0';
+ }
+ else
+ *trailer = cpystr("");
+ }
+
+ if(all_but_cmd){
+ if(saved_options)
+ *all_but_cmd = saved_options;
+ else
+ *all_but_cmd = cpystr("");
+ }
+ else if(saved_options)
+ fs_give((void **)&saved_options);
+}
+
+
+int
+copy_pinerc(char *local, char *remote, char **err_msg)
+{
+ return(copy_localfile_to_remotefldr(RemImap, local, remote,
+ REMOTE_PINERC_SUBTYPE,
+ err_msg));
+}
+
+
+int
+copy_abook(char *local, char *remote, char **err_msg)
+{
+ return(copy_localfile_to_remotefldr(RemImap, local, remote,
+ REMOTE_ABOOK_SUBTYPE,
+ err_msg));
+}
+
+
+/*
+ * Copy local file to remote folder.
+ *
+ * Args remotetype -- type of remote folder
+ * local -- name of local file
+ * remote -- name of remote folder
+ * subtype --
+ *
+ * Returns 0 on success.
+ */
+int
+copy_localfile_to_remotefldr(RemType remotetype, char *local, char *remote,
+ char *subtype, char **err_msg)
+{
+ int retfail = -1;
+ unsigned flags;
+ REMDATA_S *rd;
+
+ dprint((9, "copy_localfile_to_remotefldr(%s,%s)\n",
+ local ? local : "<null>",
+ remote ? remote : "<null>"));
+
+ *err_msg = (char *)fs_get(MAXPATH * sizeof(char));
+
+ if(!local || !*local){
+ snprintf(*err_msg, MAXPATH, _("No local file specified"));
+ return(retfail);
+ }
+
+ if(!remote || !*remote){
+ snprintf(*err_msg, MAXPATH, _("No remote folder specified"));
+ return(retfail);
+ }
+
+ if(!IS_REMOTE(remote)){
+ snprintf(*err_msg, MAXPATH, _("Remote folder name \"%s\" %s"), remote,
+ (*remote != '{') ? _("must begin with \"{\"") : _("not valid"));
+ return(retfail);
+ }
+
+ if(IS_REMOTE(local)){
+ snprintf(*err_msg, MAXPATH, _("First argument \"%s\" must be a local filename"),
+ local);
+ return(retfail);
+ }
+
+ if(can_access(local, ACCESS_EXISTS) != 0){
+ snprintf(*err_msg, MAXPATH, _("Local file \"%s\" does not exist"), local);
+ return(retfail);
+ }
+
+ if(can_access(local, READ_ACCESS) != 0){
+ snprintf(*err_msg, MAXPATH, _("Can't read local file \"%s\": %s"),
+ local, error_description(errno));
+ return(retfail);
+ }
+
+ /*
+ * Check if remote folder exists and create it if it doesn't.
+ */
+ flags = 0;
+ rd = rd_create_remote(remotetype, remote, subtype,
+ &flags, _("Error: "), _("Can't copy to remote folder."));
+
+ if(!rd || rd->access == NoExists){
+ snprintf(*err_msg, MAXPATH, _("Can't create \"%s\""), remote);
+ if(rd)
+ rd_free_remdata(&rd);
+
+ return(retfail);
+ }
+
+ if(rd->access == MaybeRorW)
+ rd->access = ReadWrite;
+
+ rd->flags |= (NO_META_UPDATE | DO_REMTRIM);
+ rd->lf = cpystr(local);
+
+ rd_open_remote(rd);
+ if(!rd_stream_exists(rd)){
+ snprintf(*err_msg, MAXPATH, _("Can't open remote folder \"%s\""), rd->rn);
+ rd_free_remdata(&rd);
+ return(retfail);
+ }
+
+ if(rd_remote_is_readonly(rd)){
+ snprintf(*err_msg, MAXPATH, _("Remote folder \"%s\" is readonly"), rd->rn);
+ rd_free_remdata(&rd);
+ return(retfail);
+ }
+
+ switch(rd->type){
+ case RemImap:
+ /*
+ * Empty folder, add a header msg.
+ */
+ if(rd->t.i.stream->nmsgs == 0){
+ if(rd_init_remote(rd, 1) != 0){
+ snprintf(*err_msg, MAXPATH,
+ _("Failed initializing remote folder \"%s\", check debug file"),
+ rd->rn);
+ rd_free_remdata(&rd);
+ return(retfail);
+ }
+ }
+
+ fs_give((void **)err_msg);
+ *err_msg = NULL;
+ if(rd_chk_for_hdr_msg(&(rd->t.i.stream), rd, err_msg)){
+ rd_free_remdata(&rd);
+ return(retfail);
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ if(rd_update_remote(rd, NULL) != 0){
+ snprintf(*err_msg, MAXPATH, _("Error copying to remote folder \"%s\""), rd->rn);
+ rd_free_remdata(&rd);
+ return(retfail);
+ }
+
+ rd_update_metadata(rd, NULL);
+ rd_close_remdata(&rd);
+
+ fs_give((void **)err_msg);
+ return(0);
+}
+
+
+/*----------------------------------------------------------------------
+ Panic pine - call on detected programmatic errors to exit pine, with arg
+
+ Input: message -- printf styule string for panic message (see above)
+ arg -- argument for printf string
+
+ Result: The various tty modes are restored
+ If debugging is active a core dump will be generated
+ Exits Pine
+ ----*/
+void
+panic1(char *message, char *arg)
+{
+ char buf1[1001], buf2[1001];
+
+ snprintf(buf1, sizeof(buf1), "%.*s", MAX(sizeof(buf1) - 1 - strlen(message), 0), arg);
+ snprintf(buf2, sizeof(buf2), message, buf1);
+ panic(buf2);
+}
+
+
+/*
+ *
+ */
+HelpType
+config_help(int var, int feature)
+{
+ switch(var){
+ case V_FEATURE_LIST :
+ return(feature_list_help(feature));
+ break;
+
+ case V_PERSONAL_NAME :
+ return(h_config_pers_name);
+ case V_USER_ID :
+ return(h_config_user_id);
+ case V_USER_DOMAIN :
+ return(h_config_user_dom);
+ case V_SMTP_SERVER :
+ return(h_config_smtp_server);
+ case V_NNTP_SERVER :
+ return(h_config_nntp_server);
+ case V_INBOX_PATH :
+ return(h_config_inbox_path);
+ case V_PRUNED_FOLDERS :
+ return(h_config_pruned_folders);
+ case V_DEFAULT_FCC :
+ return(h_config_default_fcc);
+ case V_DEFAULT_SAVE_FOLDER :
+ return(h_config_def_save_folder);
+ case V_POSTPONED_FOLDER :
+ return(h_config_postponed_folder);
+ case V_READ_MESSAGE_FOLDER :
+ return(h_config_read_message_folder);
+ case V_FORM_FOLDER :
+ return(h_config_form_folder);
+ case V_ARCHIVED_FOLDERS :
+ return(h_config_archived_folders);
+ case V_SIGNATURE_FILE :
+ return(h_config_signature_file);
+ case V_LITERAL_SIG :
+ return(h_config_literal_sig);
+ case V_INIT_CMD_LIST :
+ return(h_config_init_cmd_list);
+ case V_COMP_HDRS :
+ return(h_config_comp_hdrs);
+ case V_CUSTOM_HDRS :
+ return(h_config_custom_hdrs);
+ case V_VIEW_HEADERS :
+ return(h_config_viewer_headers);
+ case V_VIEW_MARGIN_LEFT :
+ return(h_config_viewer_margin_left);
+ case V_VIEW_MARGIN_RIGHT :
+ return(h_config_viewer_margin_right);
+ case V_QUOTE_SUPPRESSION :
+ return(h_config_quote_suppression);
+ case V_SAVED_MSG_NAME_RULE :
+ return(h_config_saved_msg_name_rule);
+ case V_FCC_RULE :
+ return(h_config_fcc_rule);
+ case V_SORT_KEY :
+ return(h_config_sort_key);
+ case V_AB_SORT_RULE :
+ return(h_config_ab_sort_rule);
+ case V_FLD_SORT_RULE :
+ return(h_config_fld_sort_rule);
+ case V_POST_CHAR_SET :
+ return(h_config_post_char_set);
+ case V_UNK_CHAR_SET :
+ return(h_config_unk_char_set);
+#ifndef _WINDOWS
+ case V_KEY_CHAR_SET :
+ return(h_config_key_char_set);
+ case V_CHAR_SET :
+ return(h_config_char_set);
+#endif /* ! _WINDOWS */
+ case V_EDITOR :
+ return(h_config_editor);
+ case V_SPELLER :
+ return(h_config_speller);
+ case V_DISPLAY_FILTERS :
+ return(h_config_display_filters);
+ case V_SEND_FILTER :
+ return(h_config_sending_filter);
+ case V_ALT_ADDRS :
+ return(h_config_alt_addresses);
+ case V_KEYWORDS :
+ return(h_config_keywords);
+ case V_KW_BRACES :
+ return(h_config_kw_braces);
+ case V_OPENING_SEP :
+ return(h_config_opening_sep);
+ case V_KW_COLORS :
+ return(h_config_kw_color);
+ case V_ABOOK_FORMATS :
+ return(h_config_abook_formats);
+ case V_INDEX_FORMAT :
+ return(h_config_index_format);
+ case V_INCCHECKTIMEO :
+ return(h_config_incoming_timeo);
+ case V_INCCHECKINTERVAL :
+ return(h_config_incoming_interv);
+ case V_INC2NDCHECKINTERVAL :
+ return(h_config_incoming_second_interv);
+ case V_INCCHECKLIST :
+ return(h_config_incoming_list);
+ case V_OVERLAP :
+ return(h_config_viewer_overlap);
+ case V_MAXREMSTREAM :
+ return(h_config_maxremstream);
+ case V_PERMLOCKED :
+ return(h_config_permlocked);
+ case V_MARGIN :
+ return(h_config_scroll_margin);
+ case V_DEADLETS :
+ return(h_config_deadlets);
+ case V_FILLCOL :
+ return(h_config_composer_wrap_column);
+ case V_TCPOPENTIMEO :
+ return(h_config_tcp_open_timeo);
+ case V_TCPREADWARNTIMEO :
+ return(h_config_tcp_readwarn_timeo);
+ case V_TCPWRITEWARNTIMEO :
+ return(h_config_tcp_writewarn_timeo);
+ case V_TCPQUERYTIMEO :
+ return(h_config_tcp_query_timeo);
+ case V_RSHOPENTIMEO :
+ return(h_config_rsh_open_timeo);
+ case V_SSHOPENTIMEO :
+ return(h_config_ssh_open_timeo);
+ case V_USERINPUTTIMEO :
+ return(h_config_user_input_timeo);
+ case V_REMOTE_ABOOK_VALIDITY :
+ return(h_config_remote_abook_validity);
+ case V_REMOTE_ABOOK_HISTORY :
+ return(h_config_remote_abook_history);
+ case V_INCOMING_FOLDERS :
+ return(h_config_incoming_folders);
+ case V_FOLDER_SPEC :
+ return(h_config_folder_spec);
+ case V_NEWS_SPEC :
+ return(h_config_news_spec);
+ case V_ADDRESSBOOK :
+ return(h_config_address_book);
+ case V_GLOB_ADDRBOOK :
+ return(h_config_glob_addrbook);
+ case V_LAST_VERS_USED :
+ return(h_config_last_vers);
+ case V_SENDMAIL_PATH :
+ return(h_config_sendmail_path);
+ case V_OPER_DIR :
+ return(h_config_oper_dir);
+ case V_RSHPATH :
+ return(h_config_rshpath);
+ case V_RSHCMD :
+ return(h_config_rshcmd);
+ case V_SSHPATH :
+ return(h_config_sshpath);
+ case V_SSHCMD :
+ return(h_config_sshcmd);
+ case V_NEW_VER_QUELL :
+ return(h_config_new_ver_quell);
+ case V_DISABLE_DRIVERS :
+ return(h_config_disable_drivers);
+ case V_DISABLE_AUTHS :
+ return(h_config_disable_auths);
+ case V_REMOTE_ABOOK_METADATA :
+ return(h_config_abook_metafile);
+ case V_REPLY_STRING :
+ return(h_config_reply_indent_string);
+ case V_WORDSEPS :
+ return(h_config_wordseps);
+ case V_QUOTE_REPLACE_STRING :
+ return(h_config_quote_replace_string);
+ case V_REPLY_INTRO :
+ return(h_config_reply_intro);
+ case V_EMPTY_HDR_MSG :
+ return(h_config_empty_hdr_msg);
+ case V_STATUS_MSG_DELAY :
+ return(h_config_status_msg_delay);
+ case V_ACTIVE_MSG_INTERVAL :
+ return(h_config_active_msg_interval);
+ case V_MAILCHECK :
+ return(h_config_mailcheck);
+ case V_MAILCHECKNONCURR :
+ return(h_config_mailchecknoncurr);
+ case V_MAILDROPCHECK :
+ return(h_config_maildropcheck);
+ case V_NNTPRANGE :
+ return(h_config_nntprange);
+ case V_NEWS_ACTIVE_PATH :
+ return(h_config_news_active);
+ case V_NEWS_SPOOL_DIR :
+ return(h_config_news_spool);
+ case V_IMAGE_VIEWER :
+ return(h_config_image_viewer);
+ case V_USE_ONLY_DOMAIN_NAME :
+ return(h_config_domain_name);
+ case V_LAST_TIME_PRUNE_QUESTION :
+ return(h_config_prune_date);
+ case V_UPLOAD_CMD:
+ return(h_config_upload_cmd);
+ case V_UPLOAD_CMD_PREFIX:
+ return(h_config_upload_prefix);
+ case V_DOWNLOAD_CMD:
+ return(h_config_download_cmd);
+ case V_DOWNLOAD_CMD_PREFIX:
+ return(h_config_download_prefix);
+ case V_GOTO_DEFAULT_RULE:
+ return(h_config_goto_default);
+ case V_INCOMING_STARTUP:
+ return(h_config_inc_startup);
+ case V_PRUNING_RULE:
+ return(h_config_pruning_rule);
+ case V_REOPEN_RULE:
+ return(h_config_reopen_rule);
+ case V_THREAD_DISP_STYLE:
+ return(h_config_thread_disp_style);
+ case V_THREAD_INDEX_STYLE:
+ return(h_config_thread_index_style);
+ case V_THREAD_MORE_CHAR:
+ return(h_config_thread_indicator_char);
+ case V_THREAD_EXP_CHAR:
+ return(h_config_thread_exp_char);
+ case V_THREAD_LASTREPLY_CHAR:
+ return(h_config_thread_lastreply_char);
+ case V_MAILCAP_PATH :
+ return(h_config_mailcap_path);
+ case V_MIMETYPE_PATH :
+ return(h_config_mimetype_path);
+#if !defined(DOS) && !defined(OS2) && !defined(LEAVEOUTFIFO)
+ case V_FIFOPATH :
+ return(h_config_fifopath);
+#endif
+ case V_NMW_WIDTH :
+ return(h_config_newmailwidth);
+ case V_NEWSRC_PATH :
+ return(h_config_newsrc_path);
+ case V_BROWSER :
+ return(h_config_browser);
+#if defined(DOS) || defined(OS2)
+ case V_FILE_DIR :
+ return(h_config_file_dir);
+#endif
+ case V_NORM_FORE_COLOR :
+ case V_NORM_BACK_COLOR :
+ return(h_config_normal_color);
+ case V_REV_FORE_COLOR :
+ case V_REV_BACK_COLOR :
+ return(h_config_reverse_color);
+ case V_TITLE_FORE_COLOR :
+ case V_TITLE_BACK_COLOR :
+ return(h_config_title_color);
+ case V_TITLECLOSED_FORE_COLOR :
+ case V_TITLECLOSED_BACK_COLOR :
+ return(h_config_titleclosed_color);
+ case V_STATUS_FORE_COLOR :
+ case V_STATUS_BACK_COLOR :
+ return(h_config_status_color);
+ case V_SLCTBL_FORE_COLOR :
+ case V_SLCTBL_BACK_COLOR :
+ return(h_config_slctbl_color);
+ case V_QUOTE1_FORE_COLOR :
+ case V_QUOTE2_FORE_COLOR :
+ case V_QUOTE3_FORE_COLOR :
+ case V_QUOTE1_BACK_COLOR :
+ case V_QUOTE2_BACK_COLOR :
+ case V_QUOTE3_BACK_COLOR :
+ return(h_config_quote_color);
+ case V_INCUNSEEN_FORE_COLOR :
+ case V_INCUNSEEN_BACK_COLOR :
+ return(h_config_incunseen_color);
+ case V_SIGNATURE_FORE_COLOR :
+ case V_SIGNATURE_BACK_COLOR :
+ return(h_config_signature_color);
+ case V_PROMPT_FORE_COLOR :
+ case V_PROMPT_BACK_COLOR :
+ return(h_config_prompt_color);
+ case V_HEADER_GENERAL_FORE_COLOR :
+ case V_HEADER_GENERAL_BACK_COLOR :
+ return(h_config_header_general_color);
+ case V_IND_PLUS_FORE_COLOR :
+ case V_IND_IMP_FORE_COLOR :
+ case V_IND_DEL_FORE_COLOR :
+ case V_IND_ANS_FORE_COLOR :
+ case V_IND_NEW_FORE_COLOR :
+ case V_IND_UNS_FORE_COLOR :
+ case V_IND_REC_FORE_COLOR :
+ case V_IND_FWD_FORE_COLOR :
+ case V_IND_PLUS_BACK_COLOR :
+ case V_IND_IMP_BACK_COLOR :
+ case V_IND_DEL_BACK_COLOR :
+ case V_IND_ANS_BACK_COLOR :
+ case V_IND_NEW_BACK_COLOR :
+ case V_IND_UNS_BACK_COLOR :
+ case V_IND_REC_BACK_COLOR :
+ case V_IND_FWD_BACK_COLOR :
+ return(h_config_index_color);
+ case V_IND_OP_FORE_COLOR :
+ case V_IND_OP_BACK_COLOR :
+ return(h_config_index_opening_color);
+ case V_IND_SUBJ_FORE_COLOR :
+ case V_IND_SUBJ_BACK_COLOR :
+ return(h_config_index_subject_color);
+ case V_IND_FROM_FORE_COLOR :
+ case V_IND_FROM_BACK_COLOR :
+ return(h_config_index_from_color);
+ case V_IND_HIPRI_FORE_COLOR :
+ case V_IND_HIPRI_BACK_COLOR :
+ case V_IND_LOPRI_FORE_COLOR :
+ case V_IND_LOPRI_BACK_COLOR :
+ return(h_config_index_pri_color);
+ case V_IND_ARR_FORE_COLOR :
+ case V_IND_ARR_BACK_COLOR :
+ return(h_config_index_arrow_color);
+ case V_KEYLABEL_FORE_COLOR :
+ case V_KEYLABEL_BACK_COLOR :
+ return(h_config_keylabel_color);
+ case V_KEYNAME_FORE_COLOR :
+ case V_KEYNAME_BACK_COLOR :
+ return(h_config_keyname_color);
+ case V_METAMSG_FORE_COLOR :
+ case V_METAMSG_BACK_COLOR :
+ return(h_config_metamsg_color);
+ case V_VIEW_HDR_COLORS :
+ return(h_config_customhdr_color);
+ case V_PRINTER :
+ return(h_config_printer);
+ case V_PERSONAL_PRINT_CATEGORY :
+ return(h_config_print_cat);
+ case V_PERSONAL_PRINT_COMMAND :
+ return(h_config_print_command);
+ case V_PAT_ROLES :
+ return(h_config_pat_roles);
+ case V_PAT_FILTS :
+ return(h_config_pat_filts);
+ case V_PAT_SCORES :
+ return(h_config_pat_scores);
+ case V_PAT_INCOLS :
+ return(h_config_pat_incols);
+ case V_PAT_OTHER :
+ return(h_config_pat_other);
+ case V_PAT_SRCH :
+ return(h_config_pat_srch);
+ case V_INDEX_COLOR_STYLE :
+ return(h_config_index_color_style);
+ case V_TITLEBAR_COLOR_STYLE :
+ return(h_config_titlebar_color_style);
+#ifdef _WINDOWS
+ case V_FONT_NAME :
+ return(h_config_font_name);
+ case V_FONT_SIZE :
+ return(h_config_font_size);
+ case V_FONT_STYLE :
+ return(h_config_font_style);
+ case V_FONT_CHAR_SET :
+ return(h_config_font_char_set);
+ case V_PRINT_FONT_NAME :
+ return(h_config_print_font_name);
+ case V_PRINT_FONT_SIZE :
+ return(h_config_print_font_size);
+ case V_PRINT_FONT_STYLE :
+ return(h_config_print_font_style);
+ case V_PRINT_FONT_CHAR_SET :
+ return(h_config_print_font_char_set);
+ case V_WINDOW_POSITION :
+ return(h_config_window_position);
+ case V_CURSOR_STYLE :
+ return(h_config_cursor_style);
+#else
+ case V_COLOR_STYLE :
+ return(h_config_color_style);
+#endif
+#ifdef ENABLE_LDAP
+ case V_LDAP_SERVERS :
+ return(h_config_ldap_servers);
+#endif
+#ifdef SMIME
+ case V_PUBLICCERT_DIR :
+ return(h_config_smime_pubcertdir);
+ case V_PUBLICCERT_CONTAINER :
+ return(h_config_smime_pubcertcon);
+ case V_PRIVATEKEY_DIR :
+ return(h_config_smime_privkeydir);
+ case V_PRIVATEKEY_CONTAINER :
+ return(h_config_smime_privkeycon);
+ case V_CACERT_DIR :
+ return(h_config_smime_cacertdir);
+ case V_CACERT_CONTAINER :
+ return(h_config_smime_cacertcon);
+#endif
+ case V_RSS_NEWS :
+ return(h_config_rss_news);
+ case V_RSS_WEATHER :
+ return(h_config_rss_weather);
+ case V_WP_INDEXHEIGHT :
+ return(h_config_wp_indexheight);
+ case V_WP_INDEXLINES :
+ return(h_config_wp_indexlines);
+ case V_WP_AGGSTATE :
+ return(h_config_wp_aggstate);
+ case V_WP_STATE :
+ return(h_config_wp_state);
+ case V_WP_COLUMNS :
+ return(h_config_wp_columns);
+ default :
+ return(NO_HELP);
+ }
+}
+
+
+/*
+ * We don't want the user to be able to edit their pinerc and set
+ * printer to whatever they want if personal-print-command is fixed.
+ * So make sure printer is set to something legitimate. If it isn't,
+ * set it to something standard and return non-zero.
+ */
+int
+printer_value_check_and_adjust(void)
+{
+ char **tt;
+ char aname[100], wname[100];
+ int ok = 0;
+ struct variable *vars = ps_global->vars;
+
+ if(vars[V_PERSONAL_PRINT_COMMAND].is_fixed && !vars[V_PRINTER].is_fixed){
+ strncpy(aname, ANSI_PRINTER, sizeof(aname));
+ aname[sizeof(aname)-1] = '\0';
+ strncat(aname, "-no-formfeed", sizeof(aname)-strlen(aname)-1);
+ strncpy(wname, WYSE_PRINTER, sizeof(wname));
+ wname[sizeof(wname)-1] = '\0';
+ strncat(wname, "-no-formfeed", sizeof(wname)-strlen(wname)-1);
+ if(strucmp(VAR_PRINTER, ANSI_PRINTER) == 0
+ || strucmp(VAR_PRINTER, aname) == 0
+ || strucmp(VAR_PRINTER, WYSE_PRINTER) == 0
+ || strucmp(VAR_PRINTER, wname) == 0)
+ ok++;
+ else if(VAR_STANDARD_PRINTER && VAR_STANDARD_PRINTER[0]){
+ for(tt = VAR_STANDARD_PRINTER; *tt; tt++)
+ if(strucmp(VAR_PRINTER, *tt) == 0)
+ break;
+
+ if(*tt)
+ ok++;
+ }
+
+ if(!ok){
+ char *val;
+ struct variable *v;
+
+ if(VAR_STANDARD_PRINTER && VAR_STANDARD_PRINTER[0])
+ val = VAR_STANDARD_PRINTER[0];
+ else
+ val = ANSI_PRINTER;
+
+ v = &vars[V_PRINTER];
+ if(v->main_user_val.p)
+ fs_give((void **)&v->main_user_val.p);
+ if(v->post_user_val.p)
+ fs_give((void **)&v->post_user_val.p);
+ if(v->current_val.p)
+ fs_give((void **)&v->current_val.p);
+
+ v->main_user_val.p = cpystr(val);
+ v->current_val.p = cpystr(val);
+ }
+ }
+
+ return(!ok);
+}
+
+
+char **
+get_supported_options(void)
+{
+ char **config;
+ DRIVER *d;
+ AUTHENTICATOR *a;
+ char *title = _("Supported features in this Alpine");
+ char sbuf[MAX_SCREEN_COLS+1];
+ int cnt, alcnt, len, cols, disabled, any_disabled = 0;;
+
+ /*
+ * Line count:
+ * Title + blank = 2
+ * SSL Title + SSL lines + blank = 4
+ * Auth title + blank = 2
+ * Driver title + blank = 2
+ * LDAP title + LDAP line = 2
+ * Disabled explanation + blank line = 4
+ * end = 1
+ */
+ cnt = 17;
+ for(a = mail_lookup_auth(1); a; a = a->next)
+ cnt++;
+ for(d = (DRIVER *)mail_parameters(NIL, GET_DRIVERS, NIL);
+ d; d = d->next)
+ cnt++;
+
+ alcnt = cnt;
+ config = (char **) fs_get(alcnt * sizeof(char *));
+ memset(config, 0, alcnt * sizeof(char *));
+
+ cols = ps_global->ttyo ? ps_global->ttyo->screen_cols : 0;
+ len = utf8_width(title);
+ snprintf(sbuf, sizeof(sbuf), "%*s%s", cols > len ? (cols-len)/2 : 0, "", title);
+
+ cnt = 0;
+ if(cnt < alcnt)
+ config[cnt] = cpystr(sbuf);
+
+ if(++cnt < alcnt)
+ config[cnt] = cpystr("");
+
+ if(++cnt < alcnt)
+ /* TRANSLATORS: headings */
+ config[cnt] = cpystr(_("Encryption:"));
+
+ if(++cnt < alcnt && mail_parameters(NIL, GET_SSLDRIVER, NIL))
+ config[cnt] = cpystr(_(" TLS and SSL"));
+ else
+ config[cnt] = cpystr(_(" None (no TLS or SSL)"));
+#ifdef SMIME
+ if(++cnt < alcnt)
+ config[cnt] = cpystr(" S/MIME");
+#endif
+
+ if(++cnt < alcnt)
+ config[cnt] = cpystr("");
+
+ if(++cnt < alcnt)
+ config[cnt] = cpystr(_("Authenticators:"));
+
+ for(a = mail_lookup_auth(1); a; a = a->next){
+ disabled = (a->client == NULL && a->server == NULL);
+ any_disabled += disabled;
+ snprintf(sbuf, sizeof(sbuf), " %s%s", a->name, disabled ? " (disabled)" : "");
+ if(++cnt < alcnt)
+ config[cnt] = cpystr(sbuf);
+ }
+
+ if(++cnt < alcnt)
+ config[cnt] = cpystr("");
+
+ if(++cnt < alcnt)
+ config[cnt] = cpystr(_("Mailbox drivers:"));
+
+ for(d = (DRIVER *)mail_parameters(NIL, GET_DRIVERS, NIL);
+ d; d = d->next){
+ disabled = (d->flags & DR_DISABLE);
+ any_disabled += disabled;
+ snprintf(sbuf, sizeof(sbuf), " %s%s", d->name, disabled ? " (disabled)" : "");
+ if(++cnt < alcnt)
+ config[cnt] = cpystr(sbuf);
+ }
+
+ if(++cnt < alcnt)
+ config[cnt] = cpystr("");
+
+ if(++cnt < alcnt)
+ config[cnt] = cpystr(_("Directories:"));
+
+#ifdef ENABLE_LDAP
+ if(++cnt < alcnt)
+ config[cnt] = cpystr(" LDAP");
+#else
+ if(++cnt < alcnt)
+ config[cnt] = cpystr(" None (no LDAP)");
+#endif
+
+ if(any_disabled){
+ if(++cnt < alcnt)
+ config[cnt] = cpystr("");
+
+ if(ps_global->ttyo){
+ if(++cnt < alcnt)
+ config[cnt] = cpystr(_("Authenticators may be disabled because of the \"disable-these-authenticators\" hidden config option. Mailbox drivers may be disabled because of the \"disable-these-drivers\" hidden config option."));
+ }
+ else{
+ if(++cnt < alcnt)
+ config[cnt] = cpystr(_("Authenticators may be disabled because of the \"disable-these-authenticators\""));
+ if(++cnt < alcnt)
+ config[cnt] = cpystr(_("hidden config option. Mailbox drivers may be disabled because of the"));
+ if(++cnt < alcnt)
+ config[cnt] = cpystr(_("\"disable-these-drivers\" hidden config option."));
+ }
+ }
+
+ if(++cnt < alcnt)
+ config[cnt] = NULL;
+
+ return(config);
+}
+
+
+unsigned
+reset_startup_rule(MAILSTREAM *stream)
+{
+ long rflags = ROLE_DO_OTHER;
+ PAT_STATE pstate;
+ PAT_S *pat;
+ unsigned startup_rule;
+
+ startup_rule = IS_NOTSET;
+
+ if(stream && nonempty_patterns(rflags, &pstate)){
+ for(pat = first_pattern(&pstate); pat; pat = next_pattern(&pstate)){
+ if(match_pattern(pat->patgrp, stream, NULL, NULL, NULL,
+ SE_NOSERVER|SE_NOPREFETCH))
+ break;
+ }
+
+ if(pat && pat->action && !pat->action->bogus)
+ startup_rule = pat->action->startup_rule;
+ }
+
+ return(startup_rule);
+}
+
+
+#ifdef _WINDOWS
+
+char *
+transformed_color(old)
+ char *old;
+{
+ if(!old)
+ return("");
+
+ if(!struncmp(old, "color008", 8))
+ return("colorlgr");
+ else if(!struncmp(old, "color009", 8))
+ return("colormgr");
+ else if(!struncmp(old, "color010", 8))
+ return("colordgr");
+
+ return("");
+}
+
+
+/*
+ * If this is the first time we've run a version > 4.40, and there
+ * is evidence that the config file has not been used by unix pine,
+ * then we convert color008 to colorlgr, color009 to colormgr, and
+ * color010 to colordgr. If the config file is being used by
+ * unix pine then color008 may really supposed to be color008, color009
+ * may really supposed to be red, and color010 may really supposed to be
+ * green. Same if we've already run 4.41 or higher previously.
+ *
+ * Returns 0 if no changes, > 0 if something was changed.
+ */
+int
+convert_pc_gray_names(ps, prc, which)
+ struct pine *ps;
+ PINERC_S *prc;
+ EditWhich which;
+{
+ struct variable *v;
+ int ret = 0, ic = 0;
+ char **s, *t, *p, *pstr, *new, *pval, **apval, **lval;
+
+ for(v = ps->vars; v->name; v++){
+ if(!color_holding_var(ps, v) || v == &ps->vars[V_KW_COLORS])
+ continue;
+
+ if(v == &ps->vars[V_VIEW_HDR_COLORS]){
+
+ if((lval = LVAL(v,which)) != NULL){
+ /* fix these in place */
+ for(s = lval; (t = *s) != NULL; s++){
+ if((p = srchstr(t, "FG=color008")) ||
+ (p = srchstr(t, "FG=color009")) ||
+ (p = srchstr(t, "FG=color010"))){
+ strncpy(p+3, transformed_color(p+3), 8);
+ ret++;
+ }
+
+ if((p = srchstr(t, "BG=color008")) ||
+ (p = srchstr(t, "BG=color009")) ||
+ (p = srchstr(t, "BG=color010"))){
+ strncpy(p+3, transformed_color(p+3), 8);
+ ret++;
+ }
+ }
+ }
+ }
+ else{
+ if((pval = PVAL(v,which)) != NULL){
+ apval = APVAL(v,which);
+ if(apval && (!strucmp(pval, "color008") ||
+ !strucmp(pval, "color009") ||
+ !strucmp(pval, "color010"))){
+ new = transformed_color(pval);
+ if(*apval)
+ fs_give((void **)apval);
+
+ *apval = cpystr(new);
+ ret++;
+ }
+ }
+ }
+ }
+
+ v = &ps->vars[V_PAT_INCOLS];
+ if((lval = LVAL(v,which)) != NULL){
+ for(s = lval; (t = *s) != NULL; s++){
+ if((pstr = srchstr(t, "action=")) != NULL){
+ if((p = srchstr(pstr, "FG=color008")) ||
+ (p = srchstr(pstr, "FG=color009")) ||
+ (p = srchstr(pstr, "FG=color010"))){
+ strncpy(p+3, transformed_color(p+3), 8);
+ ic++;
+ }
+
+ if((p = srchstr(pstr, "BG=color008")) ||
+ (p = srchstr(pstr, "BG=color009")) ||
+ (p = srchstr(pstr, "BG=color010"))){
+ strncpy(p+3, transformed_color(p+3), 8);
+ ic++;
+ }
+ }
+ }
+ }
+
+ if(ic)
+ set_current_val(&ps->vars[V_PAT_INCOLS], TRUE, TRUE);
+
+ return(ret+ic);
+}
+
+
+int
+unix_color_style_in_pinerc(prc)
+ PINERC_S *prc;
+{
+ PINERC_LINE *pline;
+
+ for(pline = prc ? prc->pinerc_lines : NULL;
+ pline && (pline->var || pline->line); pline++)
+ if(pline->line && !struncmp("color-style=", pline->line, 12))
+ return(1);
+
+ return(0);
+}
+
+char *
+pcpine_general_help(titlebuf)
+ char *titlebuf;
+{
+ if(titlebuf)
+ strcpy(titlebuf, "PC Alpine For Windows");
+
+ return(pcpine_help(h_pine_for_windows));
+}
+
+#endif /* _WINDOWS */
diff --git a/pith/conf.h b/pith/conf.h
new file mode 100644
index 00000000..4c6bae4a
--- /dev/null
+++ b/pith/conf.h
@@ -0,0 +1,895 @@
+/*
+ * $Id: conf.h 1155 2008-08-21 18:33:21Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_CONFIG_INCLUDED
+#define PITH_CONFIG_INCLUDED
+
+
+#include "../pith/conftype.h"
+#include "../pith/remtype.h"
+#include "../pith/state.h"
+#include "../pith/pattern.h"
+#include "../pith/color.h"
+
+
+#ifndef DF_REMOTE_ABOOK_VALIDITY
+#define DF_REMOTE_ABOOK_VALIDITY "5"
+#endif
+#ifndef DF_GOTO_DEFAULT_RULE
+#define DF_GOTO_DEFAULT_RULE "inbox-or-folder-in-recent-collection"
+#endif
+#ifndef DF_INCOMING_STARTUP
+#define DF_INCOMING_STARTUP "first-unseen"
+#endif
+#ifndef DF_PRUNING_RULE
+#define DF_PRUNING_RULE "ask-ask"
+#endif
+#ifndef DF_REOPEN_RULE
+#define DF_REOPEN_RULE "ask-no-n"
+#endif
+#ifndef DF_THREAD_DISP_STYLE
+#define DF_THREAD_DISP_STYLE "struct"
+#endif
+#ifndef DF_THREAD_INDEX_STYLE
+#define DF_THREAD_INDEX_STYLE "exp"
+#endif
+#ifndef DF_THREAD_MORE_CHAR
+#define DF_THREAD_MORE_CHAR ">"
+#endif
+#ifndef DF_THREAD_EXP_CHAR
+#define DF_THREAD_EXP_CHAR "."
+#endif
+#ifndef DF_THREAD_LASTREPLY_CHAR
+#define DF_THREAD_LASTREPLY_CHAR "\\"
+#endif
+#ifndef DF_MAILDROPCHECK
+#define DF_MAILDROPCHECK "60"
+#endif
+#ifndef DF_MAXREMSTREAM
+#define DF_MAXREMSTREAM "3"
+#endif
+#ifndef DF_VIEW_MARGIN_RIGHT
+#define DF_VIEW_MARGIN_RIGHT "4"
+#endif
+#ifndef DF_QUOTE_SUPPRESSION
+#define DF_QUOTE_SUPPRESSION "0"
+#endif
+#ifndef DF_DEADLETS
+#define DF_DEADLETS "1"
+#endif
+#ifndef DF_NMW_WIDTH
+#define DF_NMW_WIDTH "80"
+#endif
+
+
+#define VAR_PERSONAL_NAME vars[V_PERSONAL_NAME].current_val.p
+#define FIX_PERSONAL_NAME vars[V_PERSONAL_NAME].fixed_val.p
+#define COM_PERSONAL_NAME vars[V_PERSONAL_NAME].cmdline_val.p
+#define VAR_USER_ID vars[V_USER_ID].current_val.p
+#define COM_USER_ID vars[V_USER_ID].cmdline_val.p
+#define VAR_USER_DOMAIN vars[V_USER_DOMAIN].current_val.p
+#define VAR_SMTP_SERVER vars[V_SMTP_SERVER].current_val.l
+#define FIX_SMTP_SERVER vars[V_SMTP_SERVER].fixed_val.l
+#define COM_SMTP_SERVER vars[V_SMTP_SERVER].cmdline_val.l
+#define GLO_SMTP_SERVER vars[V_SMTP_SERVER].global_val.l
+#define VAR_INBOX_PATH vars[V_INBOX_PATH].current_val.p
+#define GLO_INBOX_PATH vars[V_INBOX_PATH].global_val.p
+#define VAR_INCOMING_FOLDERS vars[V_INCOMING_FOLDERS].current_val.l
+#define VAR_FOLDER_SPEC vars[V_FOLDER_SPEC].current_val.l
+#define GLO_FOLDER_SPEC vars[V_FOLDER_SPEC].global_val.l
+#define VAR_NEWS_SPEC vars[V_NEWS_SPEC].current_val.l
+#define VAR_ARCHIVED_FOLDERS vars[V_ARCHIVED_FOLDERS].current_val.l
+#define VAR_PRUNED_FOLDERS vars[V_PRUNED_FOLDERS].current_val.l
+#define GLO_PRUNED_FOLDERS vars[V_PRUNED_FOLDERS].global_val.l
+#define VAR_DEFAULT_FCC vars[V_DEFAULT_FCC].current_val.p
+#define GLO_DEFAULT_FCC vars[V_DEFAULT_FCC].global_val.p
+#define VAR_DEFAULT_SAVE_FOLDER vars[V_DEFAULT_SAVE_FOLDER].current_val.p
+#define GLO_DEFAULT_SAVE_FOLDER vars[V_DEFAULT_SAVE_FOLDER].global_val.p
+#define VAR_POSTPONED_FOLDER vars[V_POSTPONED_FOLDER].current_val.p
+#define GLO_POSTPONED_FOLDER vars[V_POSTPONED_FOLDER].global_val.p
+#define VAR_MAIL_DIRECTORY vars[V_MAIL_DIRECTORY].current_val.p
+#define GLO_MAIL_DIRECTORY vars[V_MAIL_DIRECTORY].global_val.p
+#define VAR_READ_MESSAGE_FOLDER vars[V_READ_MESSAGE_FOLDER].current_val.p
+#define GLO_READ_MESSAGE_FOLDER vars[V_READ_MESSAGE_FOLDER].global_val.p
+#define VAR_FORM_FOLDER vars[V_FORM_FOLDER].current_val.p
+#define GLO_FORM_FOLDER vars[V_FORM_FOLDER].global_val.p
+#define VAR_TRASH_FOLDER vars[V_TRASH_FOLDER].current_val.p
+#define GLO_TRASH_FOLDER vars[V_TRASH_FOLDER].global_val.p
+#define VAR_SIGNATURE_FILE vars[V_SIGNATURE_FILE].current_val.p
+#define GLO_SIGNATURE_FILE vars[V_SIGNATURE_FILE].global_val.p
+#define VAR_LITERAL_SIG vars[V_LITERAL_SIG].current_val.p
+#define VAR_GLOB_ADDRBOOK vars[V_GLOB_ADDRBOOK].current_val.l
+#define VAR_ADDRESSBOOK vars[V_ADDRESSBOOK].current_val.l
+#define GLO_ADDRESSBOOK vars[V_ADDRESSBOOK].global_val.l
+#define FIX_ADDRESSBOOK vars[V_ADDRESSBOOK].fixed_val.l
+#define VAR_FEATURE_LIST vars[V_FEATURE_LIST].current_val.l
+#define VAR_INIT_CMD_LIST vars[V_INIT_CMD_LIST].current_val.l
+#define GLO_INIT_CMD_LIST vars[V_INIT_CMD_LIST].global_val.l
+#define COM_INIT_CMD_LIST vars[V_INIT_CMD_LIST].cmdline_val.l
+#define VAR_COMP_HDRS vars[V_COMP_HDRS].current_val.l
+#define GLO_COMP_HDRS vars[V_COMP_HDRS].global_val.l
+#define VAR_CUSTOM_HDRS vars[V_CUSTOM_HDRS].current_val.l
+#define VAR_VIEW_HEADERS vars[V_VIEW_HEADERS].current_val.l
+#define VAR_VIEW_MARGIN_LEFT vars[V_VIEW_MARGIN_LEFT].current_val.p
+#define GLO_VIEW_MARGIN_LEFT vars[V_VIEW_MARGIN_LEFT].global_val.p
+#define VAR_VIEW_MARGIN_RIGHT vars[V_VIEW_MARGIN_RIGHT].current_val.p
+#define GLO_VIEW_MARGIN_RIGHT vars[V_VIEW_MARGIN_RIGHT].global_val.p
+#define VAR_QUOTE_SUPPRESSION vars[V_QUOTE_SUPPRESSION].current_val.p
+#define GLO_QUOTE_SUPPRESSION vars[V_QUOTE_SUPPRESSION].global_val.p
+#ifndef _WINDOWS
+#define VAR_COLOR_STYLE vars[V_COLOR_STYLE].current_val.p
+#define GLO_COLOR_STYLE vars[V_COLOR_STYLE].global_val.p
+#endif
+#define VAR_INDEX_COLOR_STYLE vars[V_INDEX_COLOR_STYLE].current_val.p
+#define GLO_INDEX_COLOR_STYLE vars[V_INDEX_COLOR_STYLE].global_val.p
+#define VAR_TITLEBAR_COLOR_STYLE vars[V_TITLEBAR_COLOR_STYLE].current_val.p
+#define GLO_TITLEBAR_COLOR_STYLE vars[V_TITLEBAR_COLOR_STYLE].global_val.p
+#define VAR_SAVED_MSG_NAME_RULE vars[V_SAVED_MSG_NAME_RULE].current_val.p
+#define GLO_SAVED_MSG_NAME_RULE vars[V_SAVED_MSG_NAME_RULE].global_val.p
+#define VAR_FCC_RULE vars[V_FCC_RULE].current_val.p
+#define GLO_FCC_RULE vars[V_FCC_RULE].global_val.p
+#define VAR_SORT_KEY vars[V_SORT_KEY].current_val.p
+#define GLO_SORT_KEY vars[V_SORT_KEY].global_val.p
+#define COM_SORT_KEY vars[V_SORT_KEY].cmdline_val.p
+#define VAR_AB_SORT_RULE vars[V_AB_SORT_RULE].current_val.p
+#define GLO_AB_SORT_RULE vars[V_AB_SORT_RULE].global_val.p
+#define VAR_FLD_SORT_RULE vars[V_FLD_SORT_RULE].current_val.p
+#define GLO_FLD_SORT_RULE vars[V_FLD_SORT_RULE].global_val.p
+#ifndef _WINDOWS
+#define VAR_CHAR_SET vars[V_CHAR_SET].current_val.p
+#define GLO_CHAR_SET vars[V_CHAR_SET].global_val.p
+#define VAR_OLD_CHAR_SET vars[V_OLD_CHAR_SET].current_val.p
+#define VAR_KEY_CHAR_SET vars[V_KEY_CHAR_SET].current_val.p
+#endif /* ! _WINDOWS */
+#define VAR_POST_CHAR_SET vars[V_POST_CHAR_SET].current_val.p
+#define GLO_POST_CHAR_SET vars[V_POST_CHAR_SET].global_val.p
+#define VAR_UNK_CHAR_SET vars[V_UNK_CHAR_SET].current_val.p
+#define VAR_EDITOR vars[V_EDITOR].current_val.l
+#define GLO_EDITOR vars[V_EDITOR].global_val.l
+#define VAR_SPELLER vars[V_SPELLER].current_val.p
+#define GLO_SPELLER vars[V_SPELLER].global_val.p
+#define VAR_FILLCOL vars[V_FILLCOL].current_val.p
+#define GLO_FILLCOL vars[V_FILLCOL].global_val.p
+#define VAR_DEADLETS vars[V_DEADLETS].current_val.p
+#define GLO_DEADLETS vars[V_DEADLETS].global_val.p
+#define VAR_REPLY_STRING vars[V_REPLY_STRING].current_val.p
+#define GLO_REPLY_STRING vars[V_REPLY_STRING].global_val.p
+#define VAR_WORDSEPS vars[V_WORDSEPS].current_val.l
+#define VAR_QUOTE_REPLACE_STRING vars[V_QUOTE_REPLACE_STRING].current_val.p
+#define GLO_QUOTE_REPLACE_STRING vars[V_QUOTE_REPLACE_STRING].global_val.p
+#define VAR_REPLY_INTRO vars[V_REPLY_INTRO].current_val.p
+#define GLO_REPLY_INTRO vars[V_REPLY_INTRO].global_val.p
+#define VAR_EMPTY_HDR_MSG vars[V_EMPTY_HDR_MSG].current_val.p
+#define GLO_EMPTY_HDR_MSG vars[V_EMPTY_HDR_MSG].global_val.p
+#define VAR_IMAGE_VIEWER vars[V_IMAGE_VIEWER].current_val.p
+#define GLO_IMAGE_VIEWER vars[V_IMAGE_VIEWER].global_val.p
+#define VAR_USE_ONLY_DOMAIN_NAME vars[V_USE_ONLY_DOMAIN_NAME].current_val.p
+#define GLO_USE_ONLY_DOMAIN_NAME vars[V_USE_ONLY_DOMAIN_NAME].global_val.p
+#define VAR_PRINTER vars[V_PRINTER].current_val.p
+#define GLO_PRINTER vars[V_PRINTER].global_val.p
+#define VAR_PERSONAL_PRINT_COMMAND vars[V_PERSONAL_PRINT_COMMAND].current_val.l
+#define GLO_PERSONAL_PRINT_COMMAND vars[V_PERSONAL_PRINT_COMMAND].global_val.l
+#define VAR_PERSONAL_PRINT_CATEGORY vars[V_PERSONAL_PRINT_CATEGORY].current_val.p
+#define GLO_PERSONAL_PRINT_CATEGORY vars[V_PERSONAL_PRINT_CATEGORY].global_val.p
+#define VAR_STANDARD_PRINTER vars[V_STANDARD_PRINTER].current_val.l
+#define GLO_STANDARD_PRINTER vars[V_STANDARD_PRINTER].global_val.l
+#define FIX_STANDARD_PRINTER vars[V_STANDARD_PRINTER].fixed_val.l
+#define VAR_LAST_TIME_PRUNE_QUESTION vars[V_LAST_TIME_PRUNE_QUESTION].current_val.p
+#define VAR_LAST_VERS_USED vars[V_LAST_VERS_USED].current_val.p
+#define VAR_BUGS_FULLNAME vars[V_BUGS_FULLNAME].current_val.p
+#define GLO_BUGS_FULLNAME vars[V_BUGS_FULLNAME].global_val.p
+#define VAR_BUGS_ADDRESS vars[V_BUGS_ADDRESS].current_val.p
+#define GLO_BUGS_ADDRESS vars[V_BUGS_ADDRESS].global_val.p
+#define VAR_BUGS_EXTRAS vars[V_BUGS_EXTRAS].current_val.p
+#define GLO_BUGS_EXTRAS vars[V_BUGS_EXTRAS].global_val.p
+#define VAR_SUGGEST_FULLNAME vars[V_SUGGEST_FULLNAME].current_val.p
+#define GLO_SUGGEST_FULLNAME vars[V_SUGGEST_FULLNAME].global_val.p
+#define VAR_SUGGEST_ADDRESS vars[V_SUGGEST_ADDRESS].current_val.p
+#define GLO_SUGGEST_ADDRESS vars[V_SUGGEST_ADDRESS].global_val.p
+#define VAR_LOCAL_FULLNAME vars[V_LOCAL_FULLNAME].current_val.p
+#define GLO_LOCAL_FULLNAME vars[V_LOCAL_FULLNAME].global_val.p
+#define VAR_LOCAL_ADDRESS vars[V_LOCAL_ADDRESS].current_val.p
+#define GLO_LOCAL_ADDRESS vars[V_LOCAL_ADDRESS].global_val.p
+#define VAR_FORCED_ABOOK_ENTRY vars[V_FORCED_ABOOK_ENTRY].current_val.l
+#define VAR_KBLOCK_PASSWD_COUNT vars[V_KBLOCK_PASSWD_COUNT].current_val.p
+#define GLO_KBLOCK_PASSWD_COUNT vars[V_KBLOCK_PASSWD_COUNT].global_val.p
+#define VAR_STATUS_MSG_DELAY vars[V_STATUS_MSG_DELAY].current_val.p
+#define GLO_STATUS_MSG_DELAY vars[V_STATUS_MSG_DELAY].global_val.p
+#define VAR_ACTIVE_MSG_INTERVAL vars[V_ACTIVE_MSG_INTERVAL].current_val.p
+#define GLO_ACTIVE_MSG_INTERVAL vars[V_ACTIVE_MSG_INTERVAL].global_val.p
+#define GLO_SENDMAIL_PATH vars[V_SENDMAIL_PATH].global_val.p
+#define FIX_SENDMAIL_PATH vars[V_SENDMAIL_PATH].fixed_val.p
+#define COM_SENDMAIL_PATH vars[V_SENDMAIL_PATH].cmdline_val.p
+#define VAR_OPER_DIR vars[V_OPER_DIR].current_val.p
+#define GLO_OPER_DIR vars[V_OPER_DIR].global_val.p
+#define FIX_OPER_DIR vars[V_OPER_DIR].fixed_val.p
+#define COM_OPER_DIR vars[V_OPER_DIR].cmdline_val.p
+#define VAR_DISPLAY_FILTERS vars[V_DISPLAY_FILTERS].current_val.l
+#define VAR_SEND_FILTER vars[V_SEND_FILTER].current_val.l
+#define GLO_SEND_FILTER vars[V_SEND_FILTER].global_val.l
+#define VAR_ALT_ADDRS vars[V_ALT_ADDRS].current_val.l
+#define VAR_KEYWORDS vars[V_KEYWORDS].current_val.l
+#define VAR_KW_COLORS vars[V_KW_COLORS].current_val.l
+#define VAR_KW_BRACES vars[V_KW_BRACES].current_val.p
+#define GLO_KW_BRACES vars[V_KW_BRACES].global_val.p
+#define VAR_OPENING_SEP vars[V_OPENING_SEP].current_val.p
+#define GLO_OPENING_SEP vars[V_OPENING_SEP].global_val.p
+#define VAR_ABOOK_FORMATS vars[V_ABOOK_FORMATS].current_val.l
+#define VAR_INDEX_FORMAT vars[V_INDEX_FORMAT].current_val.p
+#define VAR_OVERLAP vars[V_OVERLAP].current_val.p
+#define GLO_OVERLAP vars[V_OVERLAP].global_val.p
+#define VAR_MAXREMSTREAM vars[V_MAXREMSTREAM].current_val.p
+#define GLO_MAXREMSTREAM vars[V_MAXREMSTREAM].global_val.p
+#define VAR_PERMLOCKED vars[V_PERMLOCKED].current_val.l
+#define VAR_MARGIN vars[V_MARGIN].current_val.p
+#define GLO_MARGIN vars[V_MARGIN].global_val.p
+#define VAR_MAILCHECK vars[V_MAILCHECK].current_val.p
+#define GLO_MAILCHECK vars[V_MAILCHECK].global_val.p
+#define VAR_MAILCHECKNONCURR vars[V_MAILCHECKNONCURR].current_val.p
+#define GLO_MAILCHECKNONCURR vars[V_MAILCHECKNONCURR].global_val.p
+#define VAR_MAILDROPCHECK vars[V_MAILDROPCHECK].current_val.p
+#define GLO_MAILDROPCHECK vars[V_MAILDROPCHECK].global_val.p
+#define VAR_NNTPRANGE vars[V_NNTPRANGE].current_val.p
+#define GLO_NNTPRANGE vars[V_NNTPRANGE].global_val.p
+#define VAR_NEWSRC_PATH vars[V_NEWSRC_PATH].current_val.p
+#define GLO_NEWSRC_PATH vars[V_NEWSRC_PATH].global_val.p
+#define VAR_NEWS_ACTIVE_PATH vars[V_NEWS_ACTIVE_PATH].current_val.p
+#define GLO_NEWS_ACTIVE_PATH vars[V_NEWS_ACTIVE_PATH].global_val.p
+#define VAR_NEWS_SPOOL_DIR vars[V_NEWS_SPOOL_DIR].current_val.p
+#define GLO_NEWS_SPOOL_DIR vars[V_NEWS_SPOOL_DIR].global_val.p
+#define VAR_DISABLE_DRIVERS vars[V_DISABLE_DRIVERS].current_val.l
+#define VAR_DISABLE_AUTHS vars[V_DISABLE_AUTHS].current_val.l
+#define VAR_REMOTE_ABOOK_METADATA vars[V_REMOTE_ABOOK_METADATA].current_val.p
+#define VAR_REMOTE_ABOOK_HISTORY vars[V_REMOTE_ABOOK_HISTORY].current_val.p
+#define GLO_REMOTE_ABOOK_HISTORY vars[V_REMOTE_ABOOK_HISTORY].global_val.p
+#define VAR_REMOTE_ABOOK_VALIDITY vars[V_REMOTE_ABOOK_VALIDITY].current_val.p
+#define GLO_REMOTE_ABOOK_VALIDITY vars[V_REMOTE_ABOOK_VALIDITY].global_val.p
+ /* Elm style save is obsolete in Pine 3.81 (see saved msg name rule) */
+#define VAR_ELM_STYLE_SAVE vars[V_ELM_STYLE_SAVE].current_val.p
+#define GLO_ELM_STYLE_SAVE vars[V_ELM_STYLE_SAVE].global_val.p
+ /* Header in reply is obsolete in Pine 3.83 (see feature list) */
+#define VAR_HEADER_IN_REPLY vars[V_HEADER_IN_REPLY].current_val.p
+#define GLO_HEADER_IN_REPLY vars[V_HEADER_IN_REPLY].global_val.p
+ /* Feature level is obsolete in Pine 3.83 (see feature list) */
+#define VAR_FEATURE_LEVEL vars[V_FEATURE_LEVEL].current_val.p
+#define GLO_FEATURE_LEVEL vars[V_FEATURE_LEVEL].global_val.p
+ /* Old style reply is obsolete in Pine 3.83 (see feature list) */
+#define VAR_OLD_STYLE_REPLY vars[V_OLD_STYLE_REPLY].current_val.p
+#define GLO_OLD_STYLE_REPLY vars[V_OLD_STYLE_REPLY].global_val.p
+ /* Save by sender is obsolete in Pine 3.83 (see saved msg name rule) */
+#define VAR_SAVE_BY_SENDER vars[V_SAVE_BY_SENDER].current_val.p
+#define GLO_SAVE_BY_SENDER vars[V_SAVE_BY_SENDER].global_val.p
+#define VAR_NNTP_SERVER vars[V_NNTP_SERVER].current_val.l
+#define FIX_NNTP_SERVER vars[V_NNTP_SERVER].fixed_val.l
+#ifdef ENABLE_LDAP
+#define VAR_LDAP_SERVERS vars[V_LDAP_SERVERS].current_val.l
+#endif
+#define VAR_RSS_NEWS vars[V_RSS_NEWS].current_val.p
+#define VAR_RSS_WEATHER vars[V_RSS_WEATHER].current_val.p
+#define VAR_WP_INDEXHEIGHT vars[V_WP_INDEXHEIGHT].current_val.p
+#define VAR_WP_INDEXLINES vars[V_WP_INDEXLINES].current_val.p
+#define GLO_WP_INDEXHEIGHT vars[V_WP_INDEXHEIGHT].global_val.p
+#define VAR_WP_AGGSTATE vars[V_WP_AGGSTATE].current_val.p
+#define GLO_WP_AGGSTATE vars[V_WP_AGGSTATE].global_val.p
+#define VAR_WP_STATE vars[V_WP_STATE].current_val.p
+#define GLO_WP_STATE vars[V_WP_STATE].global_val.p
+#define VAR_WP_COLUMNS vars[V_WP_COLUMNS].current_val.p
+#define VAR_UPLOAD_CMD vars[V_UPLOAD_CMD].current_val.p
+#define VAR_UPLOAD_CMD_PREFIX vars[V_UPLOAD_CMD_PREFIX].current_val.p
+#define VAR_DOWNLOAD_CMD vars[V_DOWNLOAD_CMD].current_val.p
+#define VAR_DOWNLOAD_CMD_PREFIX vars[V_DOWNLOAD_CMD_PREFIX].current_val.p
+#define VAR_GOTO_DEFAULT_RULE vars[V_GOTO_DEFAULT_RULE].current_val.p
+#define GLO_GOTO_DEFAULT_RULE vars[V_GOTO_DEFAULT_RULE].global_val.p
+#define VAR_MAILCAP_PATH vars[V_MAILCAP_PATH].current_val.p
+#define VAR_MIMETYPE_PATH vars[V_MIMETYPE_PATH].current_val.p
+#define VAR_FIFOPATH vars[V_FIFOPATH].current_val.p
+#define VAR_NMW_WIDTH vars[V_NMW_WIDTH].current_val.p
+#define GLO_NMW_WIDTH vars[V_NMW_WIDTH].global_val.p
+#define VAR_USERINPUTTIMEO vars[V_USERINPUTTIMEO].current_val.p
+#define GLO_USERINPUTTIMEO vars[V_USERINPUTTIMEO].global_val.p
+#define VAR_TCPOPENTIMEO vars[V_TCPOPENTIMEO].current_val.p
+#define VAR_TCPREADWARNTIMEO vars[V_TCPREADWARNTIMEO].current_val.p
+#define VAR_TCPWRITEWARNTIMEO vars[V_TCPWRITEWARNTIMEO].current_val.p
+#define VAR_TCPQUERYTIMEO vars[V_TCPQUERYTIMEO].current_val.p
+#define VAR_RSHOPENTIMEO vars[V_RSHOPENTIMEO].current_val.p
+#define VAR_RSHPATH vars[V_RSHPATH].current_val.p
+#define VAR_RSHCMD vars[V_RSHCMD].current_val.p
+#define VAR_SSHOPENTIMEO vars[V_SSHOPENTIMEO].current_val.p
+#define VAR_INCCHECKTIMEO vars[V_INCCHECKTIMEO].current_val.p
+#define GLO_INCCHECKTIMEO vars[V_INCCHECKTIMEO].global_val.p
+#define VAR_INCCHECKINTERVAL vars[V_INCCHECKINTERVAL].current_val.p
+#define GLO_INCCHECKINTERVAL vars[V_INCCHECKINTERVAL].global_val.p
+#define VAR_INC2NDCHECKINTERVAL vars[V_INC2NDCHECKINTERVAL].current_val.p
+#define GLO_INC2NDCHECKINTERVAL vars[V_INC2NDCHECKINTERVAL].global_val.p
+#define VAR_INCCHECKLIST vars[V_INCCHECKLIST].current_val.l
+#define VAR_SSHPATH vars[V_SSHPATH].current_val.p
+#define GLO_SSHPATH vars[V_SSHPATH].global_val.p
+#define VAR_SSHCMD vars[V_SSHCMD].current_val.p
+#define GLO_SSHCMD vars[V_SSHCMD].global_val.p
+#define VAR_NEW_VER_QUELL vars[V_NEW_VER_QUELL].current_val.p
+#define VAR_BROWSER vars[V_BROWSER].current_val.l
+#define VAR_INCOMING_STARTUP vars[V_INCOMING_STARTUP].current_val.p
+#define GLO_INCOMING_STARTUP vars[V_INCOMING_STARTUP].global_val.p
+#define VAR_PRUNING_RULE vars[V_PRUNING_RULE].current_val.p
+#define GLO_PRUNING_RULE vars[V_PRUNING_RULE].global_val.p
+#define VAR_REOPEN_RULE vars[V_REOPEN_RULE].current_val.p
+#define GLO_REOPEN_RULE vars[V_REOPEN_RULE].global_val.p
+#define VAR_THREAD_DISP_STYLE vars[V_THREAD_DISP_STYLE].current_val.p
+#define GLO_THREAD_DISP_STYLE vars[V_THREAD_DISP_STYLE].global_val.p
+#define VAR_THREAD_INDEX_STYLE vars[V_THREAD_INDEX_STYLE].current_val.p
+#define GLO_THREAD_INDEX_STYLE vars[V_THREAD_INDEX_STYLE].global_val.p
+#define VAR_THREAD_MORE_CHAR vars[V_THREAD_MORE_CHAR].current_val.p
+#define GLO_THREAD_MORE_CHAR vars[V_THREAD_MORE_CHAR].global_val.p
+#define VAR_THREAD_EXP_CHAR vars[V_THREAD_EXP_CHAR].current_val.p
+#define GLO_THREAD_EXP_CHAR vars[V_THREAD_EXP_CHAR].global_val.p
+#define VAR_THREAD_LASTREPLY_CHAR vars[V_THREAD_LASTREPLY_CHAR].current_val.p
+#define GLO_THREAD_LASTREPLY_CHAR vars[V_THREAD_LASTREPLY_CHAR].global_val.p
+
+#if defined(DOS) || defined(OS2)
+#define VAR_FILE_DIR vars[V_FILE_DIR].current_val.p
+#define GLO_FILE_DIR vars[V_FILE_DIR].current_val.p
+#define VAR_FOLDER_EXTENSION vars[V_FOLDER_EXTENSION].current_val.p
+#define GLO_FOLDER_EXTENSION vars[V_FOLDER_EXTENSION].global_val.p
+
+#ifdef _WINDOWS
+#define VAR_FONT_NAME vars[V_FONT_NAME].current_val.p
+#define VAR_FONT_SIZE vars[V_FONT_SIZE].current_val.p
+#define VAR_FONT_STYLE vars[V_FONT_STYLE].current_val.p
+#define VAR_FONT_CHAR_SET vars[V_FONT_CHAR_SET].current_val.p
+#define VAR_PRINT_FONT_NAME vars[V_PRINT_FONT_NAME].current_val.p
+#define VAR_PRINT_FONT_SIZE vars[V_PRINT_FONT_SIZE].current_val.p
+#define VAR_PRINT_FONT_STYLE vars[V_PRINT_FONT_STYLE].current_val.p
+#define VAR_PRINT_FONT_CHAR_SET vars[V_PRINT_FONT_CHAR_SET].current_val.p
+#define VAR_WINDOW_POSITION vars[V_WINDOW_POSITION].current_val.p
+#define VAR_CURSOR_STYLE vars[V_CURSOR_STYLE].current_val.p
+#endif /* _WINDOWS */
+#endif /* DOS or OS2 */
+
+#define VAR_NORM_FORE_COLOR vars[V_NORM_FORE_COLOR].current_val.p
+#define GLO_NORM_FORE_COLOR vars[V_NORM_FORE_COLOR].global_val.p
+#define VAR_NORM_BACK_COLOR vars[V_NORM_BACK_COLOR].current_val.p
+#define GLO_NORM_BACK_COLOR vars[V_NORM_BACK_COLOR].global_val.p
+#define VAR_REV_FORE_COLOR vars[V_REV_FORE_COLOR].current_val.p
+#define GLO_REV_FORE_COLOR vars[V_REV_FORE_COLOR].global_val.p
+#define VAR_REV_BACK_COLOR vars[V_REV_BACK_COLOR].current_val.p
+#define GLO_REV_BACK_COLOR vars[V_REV_BACK_COLOR].global_val.p
+#define VAR_TITLE_FORE_COLOR vars[V_TITLE_FORE_COLOR].current_val.p
+#define GLO_TITLE_FORE_COLOR vars[V_TITLE_FORE_COLOR].global_val.p
+#define VAR_TITLE_BACK_COLOR vars[V_TITLE_BACK_COLOR].current_val.p
+#define GLO_TITLE_BACK_COLOR vars[V_TITLE_BACK_COLOR].global_val.p
+#define VAR_TITLECLOSED_FORE_COLOR vars[V_TITLECLOSED_FORE_COLOR].current_val.p
+#define GLO_TITLECLOSED_FORE_COLOR vars[V_TITLECLOSED_FORE_COLOR].global_val.p
+#define VAR_TITLECLOSED_BACK_COLOR vars[V_TITLECLOSED_BACK_COLOR].current_val.p
+#define GLO_TITLECLOSED_BACK_COLOR vars[V_TITLECLOSED_BACK_COLOR].global_val.p
+#define VAR_STATUS_FORE_COLOR vars[V_STATUS_FORE_COLOR].current_val.p
+#define VAR_STATUS_BACK_COLOR vars[V_STATUS_BACK_COLOR].current_val.p
+#define VAR_HEADER_GENERAL_FORE_COLOR vars[V_HEADER_GENERAL_FORE_COLOR].current_val.p
+#define VAR_HEADER_GENERAL_BACK_COLOR vars[V_HEADER_GENERAL_BACK_COLOR].current_val.p
+#define VAR_IND_PLUS_FORE_COLOR vars[V_IND_PLUS_FORE_COLOR].current_val.p
+#define GLO_IND_PLUS_FORE_COLOR vars[V_IND_PLUS_FORE_COLOR].global_val.p
+#define VAR_IND_PLUS_BACK_COLOR vars[V_IND_PLUS_BACK_COLOR].current_val.p
+#define GLO_IND_PLUS_BACK_COLOR vars[V_IND_PLUS_BACK_COLOR].global_val.p
+#define VAR_IND_IMP_FORE_COLOR vars[V_IND_IMP_FORE_COLOR].current_val.p
+#define GLO_IND_IMP_FORE_COLOR vars[V_IND_IMP_FORE_COLOR].global_val.p
+#define VAR_IND_IMP_BACK_COLOR vars[V_IND_IMP_BACK_COLOR].current_val.p
+#define GLO_IND_IMP_BACK_COLOR vars[V_IND_IMP_BACK_COLOR].global_val.p
+#define VAR_IND_DEL_FORE_COLOR vars[V_IND_DEL_FORE_COLOR].current_val.p
+#define VAR_IND_DEL_BACK_COLOR vars[V_IND_DEL_BACK_COLOR].current_val.p
+#define VAR_IND_ANS_FORE_COLOR vars[V_IND_ANS_FORE_COLOR].current_val.p
+#define GLO_IND_ANS_FORE_COLOR vars[V_IND_ANS_FORE_COLOR].global_val.p
+#define VAR_IND_ANS_BACK_COLOR vars[V_IND_ANS_BACK_COLOR].current_val.p
+#define GLO_IND_ANS_BACK_COLOR vars[V_IND_ANS_BACK_COLOR].global_val.p
+#define VAR_IND_NEW_FORE_COLOR vars[V_IND_NEW_FORE_COLOR].current_val.p
+#define GLO_IND_NEW_FORE_COLOR vars[V_IND_NEW_FORE_COLOR].global_val.p
+#define VAR_IND_NEW_BACK_COLOR vars[V_IND_NEW_BACK_COLOR].current_val.p
+#define GLO_IND_NEW_BACK_COLOR vars[V_IND_NEW_BACK_COLOR].global_val.p
+#define VAR_IND_REC_FORE_COLOR vars[V_IND_REC_FORE_COLOR].current_val.p
+#define VAR_IND_REC_BACK_COLOR vars[V_IND_REC_BACK_COLOR].current_val.p
+#define VAR_IND_FWD_FORE_COLOR vars[V_IND_FWD_FORE_COLOR].current_val.p
+#define VAR_IND_FWD_BACK_COLOR vars[V_IND_FWD_BACK_COLOR].current_val.p
+#define VAR_IND_UNS_FORE_COLOR vars[V_IND_UNS_FORE_COLOR].current_val.p
+#define VAR_IND_UNS_BACK_COLOR vars[V_IND_UNS_BACK_COLOR].current_val.p
+#define VAR_IND_OP_FORE_COLOR vars[V_IND_OP_FORE_COLOR].current_val.p
+#define GLO_IND_OP_FORE_COLOR vars[V_IND_OP_FORE_COLOR].global_val.p
+#define VAR_IND_OP_BACK_COLOR vars[V_IND_OP_BACK_COLOR].current_val.p
+#define GLO_IND_OP_BACK_COLOR vars[V_IND_OP_BACK_COLOR].global_val.p
+#define VAR_IND_SUBJ_FORE_COLOR vars[V_IND_SUBJ_FORE_COLOR].current_val.p
+#define VAR_IND_SUBJ_BACK_COLOR vars[V_IND_SUBJ_BACK_COLOR].current_val.p
+#define VAR_IND_FROM_FORE_COLOR vars[V_IND_FROM_FORE_COLOR].current_val.p
+#define VAR_IND_FROM_BACK_COLOR vars[V_IND_FROM_BACK_COLOR].current_val.p
+#define VAR_IND_HIPRI_FORE_COLOR vars[V_IND_HIPRI_FORE_COLOR].current_val.p
+#define VAR_IND_HIPRI_BACK_COLOR vars[V_IND_HIPRI_BACK_COLOR].current_val.p
+#define VAR_IND_LOPRI_FORE_COLOR vars[V_IND_LOPRI_FORE_COLOR].current_val.p
+#define VAR_IND_LOPRI_BACK_COLOR vars[V_IND_LOPRI_BACK_COLOR].current_val.p
+#define VAR_IND_ARR_FORE_COLOR vars[V_IND_ARR_FORE_COLOR].current_val.p
+#define VAR_IND_ARR_BACK_COLOR vars[V_IND_ARR_BACK_COLOR].current_val.p
+#define VAR_KEYLABEL_FORE_COLOR vars[V_KEYLABEL_FORE_COLOR].current_val.p
+#define VAR_KEYLABEL_BACK_COLOR vars[V_KEYLABEL_BACK_COLOR].current_val.p
+#define VAR_KEYNAME_FORE_COLOR vars[V_KEYNAME_FORE_COLOR].current_val.p
+#define VAR_KEYNAME_BACK_COLOR vars[V_KEYNAME_BACK_COLOR].current_val.p
+#define VAR_SLCTBL_FORE_COLOR vars[V_SLCTBL_FORE_COLOR].current_val.p
+#define VAR_SLCTBL_BACK_COLOR vars[V_SLCTBL_BACK_COLOR].current_val.p
+#define VAR_METAMSG_FORE_COLOR vars[V_METAMSG_FORE_COLOR].current_val.p
+#define GLO_METAMSG_FORE_COLOR vars[V_METAMSG_FORE_COLOR].global_val.p
+#define VAR_METAMSG_BACK_COLOR vars[V_METAMSG_BACK_COLOR].current_val.p
+#define GLO_METAMSG_BACK_COLOR vars[V_METAMSG_BACK_COLOR].global_val.p
+#define VAR_QUOTE1_FORE_COLOR vars[V_QUOTE1_FORE_COLOR].current_val.p
+#define GLO_QUOTE1_FORE_COLOR vars[V_QUOTE1_FORE_COLOR].global_val.p
+#define VAR_QUOTE1_BACK_COLOR vars[V_QUOTE1_BACK_COLOR].current_val.p
+#define GLO_QUOTE1_BACK_COLOR vars[V_QUOTE1_BACK_COLOR].global_val.p
+#define VAR_QUOTE2_FORE_COLOR vars[V_QUOTE2_FORE_COLOR].current_val.p
+#define GLO_QUOTE2_FORE_COLOR vars[V_QUOTE2_FORE_COLOR].global_val.p
+#define VAR_QUOTE2_BACK_COLOR vars[V_QUOTE2_BACK_COLOR].current_val.p
+#define GLO_QUOTE2_BACK_COLOR vars[V_QUOTE2_BACK_COLOR].global_val.p
+#define VAR_QUOTE3_FORE_COLOR vars[V_QUOTE3_FORE_COLOR].current_val.p
+#define GLO_QUOTE3_FORE_COLOR vars[V_QUOTE3_FORE_COLOR].global_val.p
+#define VAR_QUOTE3_BACK_COLOR vars[V_QUOTE3_BACK_COLOR].current_val.p
+#define GLO_QUOTE3_BACK_COLOR vars[V_QUOTE3_BACK_COLOR].global_val.p
+#define VAR_INCUNSEEN_FORE_COLOR vars[V_INCUNSEEN_FORE_COLOR].current_val.p
+#define VAR_INCUNSEEN_BACK_COLOR vars[V_INCUNSEEN_BACK_COLOR].current_val.p
+#define VAR_SIGNATURE_FORE_COLOR vars[V_SIGNATURE_FORE_COLOR].current_val.p
+#define GLO_SIGNATURE_FORE_COLOR vars[V_SIGNATURE_FORE_COLOR].global_val.p
+#define VAR_SIGNATURE_BACK_COLOR vars[V_SIGNATURE_BACK_COLOR].current_val.p
+#define GLO_SIGNATURE_BACK_COLOR vars[V_SIGNATURE_BACK_COLOR].global_val.p
+#define VAR_PROMPT_FORE_COLOR vars[V_PROMPT_FORE_COLOR].current_val.p
+#define VAR_PROMPT_BACK_COLOR vars[V_PROMPT_BACK_COLOR].current_val.p
+#define VAR_VIEW_HDR_COLORS vars[V_VIEW_HDR_COLORS].current_val.l
+#ifdef SMIME
+#define VAR_PUBLICCERT_DIR vars[V_PUBLICCERT_DIR].current_val.p
+#define GLO_PUBLICCERT_DIR vars[V_PUBLICCERT_DIR].global_val.p
+#define VAR_PRIVATEKEY_DIR vars[V_PRIVATEKEY_DIR].current_val.p
+#define GLO_PRIVATEKEY_DIR vars[V_PRIVATEKEY_DIR].global_val.p
+#define VAR_CACERT_DIR vars[V_CACERT_DIR].current_val.p
+#define GLO_CACERT_DIR vars[V_CACERT_DIR].global_val.p
+#define VAR_PUBLICCERT_CONTAINER vars[V_PUBLICCERT_CONTAINER].current_val.p
+#define VAR_PRIVATEKEY_CONTAINER vars[V_PRIVATEKEY_CONTAINER].current_val.p
+#define VAR_CACERT_CONTAINER vars[V_CACERT_CONTAINER].current_val.p
+#endif /* SMIME */
+
+
+/*
+ * Define some bitmap operations for manipulating features.
+ */
+
+#define _BITCHAR(bit) ((bit) / 8)
+#define _BITBIT(bit) (1 << ((bit) % 8))
+
+/* is bit set? */
+#define bitnset(bit,map) (((map)[_BITCHAR(bit)] & _BITBIT(bit)) ? 1 : 0)
+/* set bit */
+#define setbitn(bit,map) ((map)[_BITCHAR(bit)] |= _BITBIT(bit))
+/* clear bit */
+#define clrbitn(bit,map) ((map)[_BITCHAR(bit)] &= ~_BITBIT(bit))
+
+
+/* Feature list support */
+/* Is feature "feature" turned on? */
+#define F_ON(feature,ps) (bitnset((feature),(ps)->feature_list))
+#define F_OFF(feature,ps) (!F_ON(feature,ps))
+#define F_TURN_ON(feature,ps) (setbitn((feature),(ps)->feature_list))
+#define F_TURN_OFF(feature,ps) (clrbitn((feature),(ps)->feature_list))
+/* turn off or on depending on value */
+#define F_SET(feature,ps,value) ((value) ? F_TURN_ON((feature),(ps)) \
+ : F_TURN_OFF((feature),(ps)))
+
+/*
+ * Save message rules. if these grow, widen pine
+ * struct's save_msg_rule...
+ */
+#define SAV_RULE_DEFLT 0
+#define SAV_RULE_LAST 1
+#define SAV_RULE_FROM 2
+#define SAV_RULE_NICK_FROM_DEF 3
+#define SAV_RULE_NICK_FROM 4
+#define SAV_RULE_FCC_FROM_DEF 5
+#define SAV_RULE_FCC_FROM 6
+#define SAV_RULE_RN_FROM_DEF 7
+#define SAV_RULE_RN_FROM 8
+#define SAV_RULE_SENDER 9
+#define SAV_RULE_NICK_SENDER_DEF 10
+#define SAV_RULE_NICK_SENDER 11
+#define SAV_RULE_FCC_SENDER_DEF 12
+#define SAV_RULE_FCC_SENDER 13
+#define SAV_RULE_RN_SENDER_DEF 14
+#define SAV_RULE_RN_SENDER 15
+#define SAV_RULE_RECIP 16
+#define SAV_RULE_NICK_RECIP_DEF 17
+#define SAV_RULE_NICK_RECIP 18
+#define SAV_RULE_FCC_RECIP_DEF 19
+#define SAV_RULE_FCC_RECIP 20
+#define SAV_RULE_RN_RECIP_DEF 21
+#define SAV_RULE_RN_RECIP 22
+#define SAV_RULE_REPLYTO 23
+#define SAV_RULE_NICK_REPLYTO_DEF 24
+#define SAV_RULE_NICK_REPLYTO 25
+#define SAV_RULE_FCC_REPLYTO_DEF 26
+#define SAV_RULE_FCC_REPLYTO 27
+#define SAV_RULE_RN_REPLYTO_DEF 28
+#define SAV_RULE_RN_REPLYTO 29
+
+/*
+ * Fcc rules. if these grow, widen pine
+ * struct's fcc_rule...
+ */
+#define FCC_RULE_DEFLT 0
+#define FCC_RULE_RECIP 1
+#define FCC_RULE_LAST 2
+#define FCC_RULE_NICK 3
+#define FCC_RULE_NICK_RECIP 4
+#define FCC_RULE_CURRENT 5
+
+/*
+ * Addrbook sorting rules. if these grow, widen pine
+ * struct's ab_sort_rule...
+ */
+#define AB_SORT_RULE_FULL_LISTS 0
+#define AB_SORT_RULE_FULL 1
+#define AB_SORT_RULE_NICK_LISTS 2
+#define AB_SORT_RULE_NICK 3
+#define AB_SORT_RULE_NONE 4
+
+/*
+ * Incoming startup rules. if these grow, widen pine
+ * struct's inc_startup_rule and reset_startup_rule().
+ */
+#define IS_FIRST_UNSEEN 0
+#define IS_FIRST_RECENT 1
+#define IS_FIRST_IMPORTANT 2
+#define IS_FIRST_IMPORTANT_OR_UNSEEN 3
+#define IS_FIRST_IMPORTANT_OR_RECENT 4
+#define IS_FIRST 5
+#define IS_LAST 6
+#define IS_NOTSET 7 /* for reset version */
+
+/*
+ * Pruning rules. If these grow, widen pruning_rule.
+ */
+#define PRUNE_ASK_AND_ASK 0
+#define PRUNE_ASK_AND_NO 1
+#define PRUNE_YES_AND_ASK 2
+#define PRUNE_YES_AND_NO 3
+#define PRUNE_NO_AND_ASK 4
+#define PRUNE_NO_AND_NO 5
+
+/*
+ * Folder reopen rules. If these grow, widen reopen_rule.
+ */
+#define REOPEN_YES_YES 0
+#define REOPEN_YES_ASK_Y 1
+#define REOPEN_YES_ASK_N 2
+#define REOPEN_YES_NO 3
+#define REOPEN_ASK_ASK_Y 4
+#define REOPEN_ASK_ASK_N 5
+#define REOPEN_ASK_NO_Y 6
+#define REOPEN_ASK_NO_N 7
+#define REOPEN_NO_NO 8
+
+/*
+ * Goto default rules.
+ */
+#define GOTO_INBOX_RECENT_CLCTN 0
+#define GOTO_INBOX_FIRST_CLCTN 1
+#define GOTO_LAST_FLDR 2
+#define GOTO_FIRST_CLCTN 3
+#define GOTO_FIRST_CLCTN_DEF_INBOX 4
+
+/*
+ * Thread display styles. If these grow, widen thread_disp_rule.
+ */
+#define THREAD_NONE 0
+#define THREAD_STRUCT 1
+#define THREAD_MUTTLIKE 2
+#define THREAD_INDENT_SUBJ1 3
+#define THREAD_INDENT_SUBJ2 4
+#define THREAD_INDENT_FROM1 5
+#define THREAD_INDENT_FROM2 6
+#define THREAD_STRUCT_FROM 7
+
+/*
+ * Thread index styles. If these grow, widen thread_index_rule.
+ */
+#define THRDINDX_EXP 0
+#define THRDINDX_COLL 1
+#define THRDINDX_SEP 2
+#define THRDINDX_SEP_AUTO 3
+
+/*
+ * Titlebar color styles. If these grow, widen titlebar_color_style.
+ */
+#define TBAR_COLOR_DEFAULT 0
+#define TBAR_COLOR_INDEXLINE 1
+#define TBAR_COLOR_REV_INDEXLINE 2
+
+/*
+ * PC-Pine update registry modes
+ */
+#ifdef _WINDOWS
+#define UREG_NORMAL 0
+#define UREG_ALWAYS_SET 1
+#define UREG_NEVER_SET 2
+#endif
+
+
+/*
+ * Folder list sort rules
+ */
+#define FLD_SORT_ALPHA 0
+#define FLD_SORT_ALPHA_DIR_LAST 1
+#define FLD_SORT_ALPHA_DIR_FIRST 2
+
+
+/*
+ * Color styles
+ */
+#define COL_NONE 0
+#define COL_TERMDEF 1
+#define COL_ANSI8 2
+#define COL_ANSI16 3
+#define COL_ANSI256 4
+
+
+/* styles for apply_rev_color() */
+#define IND_COL_FLIP 0
+#define IND_COL_REV 1
+#define IND_COL_FG 2
+#define IND_COL_FG_NOAMBIG 3
+#define IND_COL_BG 4
+#define IND_COL_BG_NOAMBIG 5
+
+
+
+/*
+ * Macros to help set numeric pinerc variables. Defined here are the
+ * allowed min and max values as well as the name of the var as it
+ * should be displayed in error messages.
+ */
+#define Q_SUPP_LIMIT (4)
+#define Q_DEL_ALL (-10)
+#define SVAR_OVERLAP(ps,n,e,el) strtoval((ps)->VAR_OVERLAP, \
+ &(n), 0, 20, 0, (e), \
+ (el), \
+ "Viewer-Overlap")
+#define SVAR_MAXREMSTREAM(ps,n,e,el) strtoval((ps)->VAR_MAXREMSTREAM, \
+ &(n), 0, 15, 0, (e), \
+ (el), \
+ "Max-Remote-Connections")
+#define SVAR_MARGIN(ps,n,e,el) strtoval((ps)->VAR_MARGIN, \
+ &(n), 0, 20, 0, (e), \
+ (el), \
+ "Scroll-Margin")
+#define SVAR_FILLCOL(ps,n,e,el) strtoval((ps)->VAR_FILLCOL, \
+ &(n), 0, MAX_FILLCOL, 0, (e), \
+ (el), \
+ "Composer-Wrap-Column")
+#define SVAR_QUOTE_SUPPRESSION(ps,n,e,el) strtoval((ps)->VAR_QUOTE_SUPPRESSION, \
+ &(n), -(Q_SUPP_LIMIT-1), \
+ 1000, Q_DEL_ALL, (e), \
+ (el), \
+ "Quote-Suppression-Threshold")
+#define SVAR_DEADLETS(ps,n,e,el) strtoval((ps)->VAR_DEADLETS, \
+ &(n), 0, 9, 0, (e), \
+ (el), \
+ "Dead-Letter-Files")
+#define SVAR_MSGDLAY(ps,n,e,el) strtoval((ps)->VAR_STATUS_MSG_DELAY, \
+ &(n), -10, 30, 0, (e), \
+ (el), \
+ "Status-Message-Delay")
+#define SVAR_ACTIVEINTERVAL(ps,n,e,el) strtoval((ps)->VAR_ACTIVE_MSG_INTERVAL, \
+ &(n), 0, 20, 0, (e), \
+ (el), \
+ "Active-Msg-Updates-Per-Second")
+#define SVAR_MAILCHK(ps,n,e,el) strtoval((ps)->VAR_MAILCHECK, \
+ &(n), 15, 30000, 0, (e), \
+ (el), \
+ "Mail-Check-Interval")
+#define SVAR_MAILCHKNONCURR(ps,n,e,el) strtoval((ps)->VAR_MAILCHECKNONCURR, \
+ &(n), 15, 30000, 0, (e), \
+ (el), \
+ "Mail-Check-Interval-Noncurrent")
+#define SVAR_AB_HIST(ps,n,e,el) strtoval((ps)->VAR_REMOTE_ABOOK_HISTORY, \
+ &(n), 0, 100, 0, (e), \
+ (el), \
+ "Remote-Abook-History")
+#define SVAR_AB_VALID(ps,n,e,el) strtoval((ps)->VAR_REMOTE_ABOOK_VALIDITY, \
+ &(n), -1, 30000, 0, (e), \
+ (el), \
+ "Remote-Abook-Validity")
+#define SVAR_USER_INPUT(ps,n,e,el) strtoval((ps)->VAR_USERINPUTTIMEO, \
+ &(n), 0, 1000, 0, (e), \
+ (el), \
+ "User-Input-Timeout")
+#define SVAR_TCP_OPEN(ps,n,e,el) strtoval((ps)->VAR_TCPOPENTIMEO, \
+ &(n), 5, 30000, 5, (e), \
+ (el), \
+ "Tcp-Open-Timeout")
+#define SVAR_TCP_READWARN(ps,n,e,el) strtoval((ps)->VAR_TCPREADWARNTIMEO, \
+ &(n), 5, 30000, 5, (e), \
+ (el), \
+ "Tcp-Read-Warning-Timeout")
+#define SVAR_TCP_WRITEWARN(ps,n,e,el) strtoval((ps)->VAR_TCPWRITEWARNTIMEO, \
+ &(n), 5, 30000, 0, (e), \
+ (el), \
+ "Tcp-Write-Warning-Timeout")
+#define SVAR_TCP_QUERY(ps,n,e,el) strtoval((ps)->VAR_TCPQUERYTIMEO, \
+ &(n), 5, 30000, 0, (e), \
+ (el), \
+ "Tcp-Query-Timeout")
+#define SVAR_RSH_OPEN(ps,n,e,el) strtoval((ps)->VAR_RSHOPENTIMEO, \
+ &(n), 5, 30000, 0, (e), \
+ (el), \
+ "Rsh-Open-Timeout")
+#define SVAR_SSH_OPEN(ps,n,e,el) strtoval((ps)->VAR_SSHOPENTIMEO, \
+ &(n), 5, 30000, 0, (e), \
+ (el), \
+ "Ssh-Open-Timeout")
+#define SVAR_INC_CHECK_TIMEO(ps,n,e,el) strtoval((ps)->VAR_INCCHECKTIMEO, \
+ &(n), 1, 30000, 1, (e), \
+ (el), \
+ "Incoming-Check-Timeout")
+#define SVAR_INC_CHECK_INTERV(ps,n,e,el) strtoval((ps)->VAR_INCCHECKINTERVAL, \
+ &(n), 15, 30000, 0, (e), \
+ (el), \
+ "Incoming-Check-Interval")
+
+#define SVAR_INC_2NDCHECK_INTERV(ps,n,e,el) strtoval((ps)->VAR_INC2NDCHECKINTERVAL, \
+ &(n), 15, 30000, 0, (e), \
+ (el), \
+ "Incoming-Secondary-Check-Interval")
+
+#define SVAR_NNTPRANGE(ps,n,e,el) strtolval((ps)->VAR_NNTPRANGE, \
+ &(n), 0L, 30000L, 0L, (e), \
+ (el), \
+ "Nntp-Range")
+#define SVAR_MAILDCHK(ps,n,e,el) strtolval((ps)->VAR_MAILDROPCHECK, \
+ &(n), 60L, 30000L, 0L, (e), \
+ (el), \
+ "Maildrop-Check-Minimum")
+#define SVAR_NMW_WIDTH(ps,n,e,el) strtoval((ps)->VAR_NMW_WIDTH, \
+ &(n), 20, 170, 0, (e), \
+ (el), \
+ "NewMail-Window-Width")
+
+
+/*----------------------------------------------------------------------
+ struct for pruning old Fcc, usually "sent-mail" folders.
+ ----*/
+struct sm_folder {
+ char *name;
+ int month_num;
+};
+
+
+#define PVAL(v,w) ((v) ? (((w) == Main) ? (v)->main_user_val.p : \
+ (v)->post_user_val.p) : NULL)
+#define APVAL(v,w) ((v) ? (((w) == Main) ? &(v)->main_user_val.p : \
+ &(v)->post_user_val.p) : NULL)
+#define LVAL(v,w) ((v) ? (((w) == Main) ? (v)->main_user_val.l : \
+ (v)->post_user_val.l) : NULL)
+#define ALVAL(v,w) ((v) ? (((w) == Main) ? &(v)->main_user_val.l : \
+ &(v)->post_user_val.l) : NULL)
+
+
+/* use shortname if present, else regular long name */
+#define S_OR_L(v) (((v) && (v)->shortname) ? (v)->shortname : \
+ ((v) ? (v)->name : NULL))
+
+
+/*
+ * Flags for write_pinerc
+ */
+#define WRP_NONE 0
+#define WRP_NOUSER 0x01
+#define WRP_PRESERV_WRITTEN 0x02
+
+
+#define ALL_EXCEPT "all-except"
+#define INHERIT "INHERIT"
+#define HIDDEN_PREF "Normally Hidden Preferences"
+#define WYSE_PRINTER "attached-to-wyse"
+#define METASTR "\nremote-abook-metafile="
+
+
+#define CONF_TXT_T char
+
+
+/* exported protoypes */
+void init_init_vars(struct pine *);
+void init_pinerc(struct pine *, char **);
+void init_vars(struct pine *, void (*)(struct pine *, char **));
+char *feature_list_section(FEATURE_S *);
+FEATURE_S *feature_list(int);
+int feature_list_index(int);
+int feature_list_id(char *);
+char *feature_list_name(int);
+struct variable *var_from_name(char *);
+HelpType feature_list_help(int);
+void process_feature_list(struct pine *, char **, int , int, int);
+void cur_rule_value(struct variable *, int, int);
+NAMEVAL_S *save_msg_rules(int);
+NAMEVAL_S *fcc_rules(int);
+NAMEVAL_S *ab_sort_rules(int);
+NAMEVAL_S *col_style(int);
+NAMEVAL_S *index_col_style(int);
+NAMEVAL_S *titlebar_col_style(int);
+NAMEVAL_S *fld_sort_rules(int);
+NAMEVAL_S *incoming_startup_rules(int);
+NAMEVAL_S *startup_rules(int);
+NAMEVAL_S *pruning_rules(int);
+NAMEVAL_S *reopen_rules(int);
+NAMEVAL_S *thread_disp_styles(int);
+NAMEVAL_S *thread_index_styles(int);
+NAMEVAL_S *goto_rules(int);
+NAMEVAL_S *pat_fldr_types(int);
+NAMEVAL_S *inabook_fldr_types(int);
+NAMEVAL_S *filter_types(int);
+NAMEVAL_S *role_repl_types(int);
+NAMEVAL_S *role_forw_types(int);
+NAMEVAL_S *role_comp_types(int);
+NAMEVAL_S *role_status_types(int);
+NAMEVAL_S *msg_state_types(int);
+#ifdef ENABLE_LDAP
+NAMEVAL_S *ldap_search_rules(int);
+NAMEVAL_S *ldap_search_types(int);
+NAMEVAL_S *ldap_search_scope(int);
+#endif
+void set_current_val(struct variable *, int, int);
+void set_news_spec_current_val(int, int);
+void set_feature_list_current_val(struct variable *);
+char *expand_variables(char *, size_t, char *, int);
+void init_error(struct pine *, int, int, int, char *);
+void read_pinerc(PINERC_S *, struct variable *, ParsePinerc);
+int write_pinerc(struct pine *, EditWhich, int);
+void quit_to_edit_msg(PINERC_S *);
+int var_in_pinerc(char *);
+void dump_global_conf(void);
+void dump_new_pinerc(char *);
+int set_variable(int, char *, int, int, EditWhich);
+int set_variable_list(int, char **, int, EditWhich);
+void set_current_color_vals(struct pine *);
+int var_defaults_to_rev(struct variable *);
+void set_custom_spec_colors(struct pine *);
+SPEC_COLOR_S *spec_color_from_var(char *, int);
+SPEC_COLOR_S *spec_colors_from_varlist(char **, int);
+char *var_from_spec_color(SPEC_COLOR_S *);
+char **varlist_from_spec_colors(SPEC_COLOR_S *);
+void update_posting_charset(struct pine *, int);
+int test_old_growth_bits(struct pine *, int);
+int feature_gets_an_x(struct pine *, struct variable *, FEATURE_S *, char **, EditWhich);
+int longest_feature_comment(struct pine *, EditWhich);
+void toggle_feature(struct pine *, struct variable *, FEATURE_S *, int, EditWhich);
+int feature_in_list(char **, char *);
+int test_feature(char **, char *, int);
+void set_feature(char ***, char *, int);
+int reset_character_set_stuff(char **);
+void parse_printer(char *, char **, char **, char **, char **, char **, char **);
+int copy_pinerc(char *, char *, char **);
+int copy_abook(char *, char *, char **);
+HelpType config_help(int, int);
+int printer_value_check_and_adjust(void);
+char **get_supported_options(void);
+unsigned reset_startup_rule(MAILSTREAM *);
+void free_pinerc_lines(PINERC_LINE **);
+void panic1(char *, char *);
+
+/* mandatory to implement prototypes */
+int set_input_timeout(int);
+int get_input_timeout();
+
+/* decide what to do: return 0 to continue, nonzero to abort pinerc write */
+int unexpected_pinerc_change();
+
+
+#endif /* PITH_CONFIG_INCLUDED */
diff --git a/pith/conftype.h b/pith/conftype.h
new file mode 100644
index 00000000..d1fddddb
--- /dev/null
+++ b/pith/conftype.h
@@ -0,0 +1,709 @@
+/*
+ * $Id: conftype.h 1155 2008-08-21 18:33:21Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_CONFTYPE_INCLUDED
+#define PITH_CONFTYPE_INCLUDED
+
+
+#include "../pith/helptext.h"
+#include "../pith/remtype.h"
+
+
+typedef enum {Sapling, Seedling, Seasoned} FeatureLevel;
+
+
+/*
+ * The array is initialized in pith/conf.c so the order of that initialization
+ * must correspond to the order of the values here. The order is
+ * significant in that it determines the order that the variables
+ * are written into the pinerc file and the order they show up in in the
+ * config screen.
+ */
+typedef enum { V_PERSONAL_NAME = 0
+ , V_USER_ID
+ , V_USER_DOMAIN
+ , V_SMTP_SERVER
+ , V_NNTP_SERVER
+ , V_INBOX_PATH
+ , V_ARCHIVED_FOLDERS
+ , V_PRUNED_FOLDERS
+ , V_DEFAULT_FCC
+ , V_DEFAULT_SAVE_FOLDER
+ , V_POSTPONED_FOLDER
+ , V_READ_MESSAGE_FOLDER
+ , V_FORM_FOLDER
+ , V_TRASH_FOLDER
+ , V_LITERAL_SIG
+ , V_SIGNATURE_FILE
+ , V_FEATURE_LIST
+ , V_INIT_CMD_LIST
+ , V_COMP_HDRS
+ , V_CUSTOM_HDRS
+ , V_VIEW_HEADERS
+ , V_VIEW_MARGIN_LEFT
+ , V_VIEW_MARGIN_RIGHT
+ , V_QUOTE_SUPPRESSION
+ , V_SAVED_MSG_NAME_RULE
+ , V_FCC_RULE
+ , V_SORT_KEY
+ , V_AB_SORT_RULE
+ , V_FLD_SORT_RULE
+ , V_GOTO_DEFAULT_RULE
+ , V_INCOMING_STARTUP
+ , V_PRUNING_RULE
+ , V_REOPEN_RULE
+ , V_THREAD_DISP_STYLE
+ , V_THREAD_INDEX_STYLE
+ , V_THREAD_MORE_CHAR
+ , V_THREAD_EXP_CHAR
+ , V_THREAD_LASTREPLY_CHAR
+#ifndef _WINDOWS
+ , V_CHAR_SET
+ , V_OLD_CHAR_SET
+ , V_KEY_CHAR_SET
+#endif /* ! _WINDOWS */
+ , V_POST_CHAR_SET
+ , V_UNK_CHAR_SET
+ , V_EDITOR
+ , V_SPELLER
+ , V_FILLCOL
+ , V_REPLY_STRING
+ , V_REPLY_INTRO
+ , V_QUOTE_REPLACE_STRING
+ , V_WORDSEPS
+ , V_EMPTY_HDR_MSG
+ , V_IMAGE_VIEWER
+ , V_USE_ONLY_DOMAIN_NAME
+ , V_BUGS_FULLNAME
+ , V_BUGS_ADDRESS
+ , V_BUGS_EXTRAS
+ , V_SUGGEST_FULLNAME
+ , V_SUGGEST_ADDRESS
+ , V_LOCAL_FULLNAME
+ , V_LOCAL_ADDRESS
+ , V_FORCED_ABOOK_ENTRY
+ , V_KBLOCK_PASSWD_COUNT
+ , V_DISPLAY_FILTERS
+ , V_SEND_FILTER
+ , V_ALT_ADDRS
+ , V_KEYWORDS
+ , V_KW_BRACES
+ , V_OPENING_SEP
+ , V_ABOOK_FORMATS
+ , V_INDEX_FORMAT
+ , V_OVERLAP
+ , V_MARGIN
+ , V_STATUS_MSG_DELAY
+ , V_ACTIVE_MSG_INTERVAL
+ , V_MAILCHECK
+ , V_MAILCHECKNONCURR
+ , V_MAILDROPCHECK
+ , V_NNTPRANGE
+ , V_NEWSRC_PATH
+ , V_NEWS_ACTIVE_PATH
+ , V_NEWS_SPOOL_DIR
+ , V_UPLOAD_CMD
+ , V_UPLOAD_CMD_PREFIX
+ , V_DOWNLOAD_CMD
+ , V_DOWNLOAD_CMD_PREFIX
+ , V_MAILCAP_PATH
+ , V_MIMETYPE_PATH
+ , V_BROWSER
+ , V_MAXREMSTREAM
+ , V_PERMLOCKED
+ , V_INCCHECKTIMEO
+ , V_INCCHECKINTERVAL
+ , V_INC2NDCHECKINTERVAL
+ , V_INCCHECKLIST
+ , V_DEADLETS
+#if !defined(DOS) && !defined(OS2) && !defined(LEAVEOUTFIFO)
+ , V_FIFOPATH
+#endif
+ , V_NMW_WIDTH
+ /*
+ * Starting here, the rest of the variables are hidden by default in config
+ * screen. They are exposed with expose-hidden-config feature.
+ */
+ , V_INCOMING_FOLDERS
+ , V_MAIL_DIRECTORY
+ , V_FOLDER_SPEC
+ , V_NEWS_SPEC
+ , V_ADDRESSBOOK
+ , V_GLOB_ADDRBOOK
+ , V_STANDARD_PRINTER
+ , V_LAST_TIME_PRUNE_QUESTION
+ , V_LAST_VERS_USED
+ , V_SENDMAIL_PATH
+ , V_OPER_DIR
+ , V_USERINPUTTIMEO
+#ifdef DEBUGJOURNAL
+ , V_DEBUGMEM /* obsolete */
+#endif
+ , V_TCPOPENTIMEO
+ , V_TCPREADWARNTIMEO
+ , V_TCPWRITEWARNTIMEO
+ , V_TCPQUERYTIMEO
+ , V_RSHCMD
+ , V_RSHPATH
+ , V_RSHOPENTIMEO
+ , V_SSHCMD
+ , V_SSHPATH
+ , V_SSHOPENTIMEO
+ , V_NEW_VER_QUELL
+ , V_DISABLE_DRIVERS
+ , V_DISABLE_AUTHS
+ , V_REMOTE_ABOOK_METADATA
+ , V_REMOTE_ABOOK_HISTORY
+ , V_REMOTE_ABOOK_VALIDITY
+ , V_PRINTER
+ , V_PERSONAL_PRINT_COMMAND
+ , V_PERSONAL_PRINT_CATEGORY
+ , V_PATTERNS /* obsolete */
+ , V_PAT_ROLES
+ , V_PAT_FILTS
+ , V_PAT_FILTS_OLD /* obsolete */
+ , V_PAT_SCORES
+ , V_PAT_SCORES_OLD /* obsolete */
+ , V_PAT_INCOLS
+ , V_PAT_OTHER
+ , V_PAT_SRCH
+ , V_ELM_STYLE_SAVE /* obsolete */
+ , V_HEADER_IN_REPLY /* obsolete */
+ , V_FEATURE_LEVEL /* obsolete */
+ , V_OLD_STYLE_REPLY /* obsolete */
+ , V_COMPOSE_MIME /* obsolete */
+ , V_SHOW_ALL_CHARACTERS /* obsolete */
+ , V_SAVE_BY_SENDER /* obsolete */
+#if defined(DOS) || defined(OS2)
+ , V_FILE_DIR
+ , V_FOLDER_EXTENSION
+#endif
+#ifndef _WINDOWS
+ , V_COLOR_STYLE
+#endif
+ , V_INDEX_COLOR_STYLE
+ , V_TITLEBAR_COLOR_STYLE
+ , V_NORM_FORE_COLOR
+ , V_NORM_BACK_COLOR
+ , V_REV_FORE_COLOR
+ , V_REV_BACK_COLOR
+ , V_TITLE_FORE_COLOR
+ , V_TITLE_BACK_COLOR
+ , V_TITLECLOSED_FORE_COLOR
+ , V_TITLECLOSED_BACK_COLOR
+ , V_STATUS_FORE_COLOR
+ , V_STATUS_BACK_COLOR
+ , V_KEYLABEL_FORE_COLOR
+ , V_KEYLABEL_BACK_COLOR
+ , V_KEYNAME_FORE_COLOR
+ , V_KEYNAME_BACK_COLOR
+ , V_SLCTBL_FORE_COLOR
+ , V_SLCTBL_BACK_COLOR
+ , V_METAMSG_FORE_COLOR
+ , V_METAMSG_BACK_COLOR
+ , V_QUOTE1_FORE_COLOR
+ , V_QUOTE1_BACK_COLOR
+ , V_QUOTE2_FORE_COLOR
+ , V_QUOTE2_BACK_COLOR
+ , V_QUOTE3_FORE_COLOR
+ , V_QUOTE3_BACK_COLOR
+ , V_INCUNSEEN_FORE_COLOR
+ , V_INCUNSEEN_BACK_COLOR
+ , V_SIGNATURE_FORE_COLOR
+ , V_SIGNATURE_BACK_COLOR
+ , V_PROMPT_FORE_COLOR
+ , V_PROMPT_BACK_COLOR
+ , V_HEADER_GENERAL_FORE_COLOR
+ , V_HEADER_GENERAL_BACK_COLOR
+ , V_IND_PLUS_FORE_COLOR
+ , V_IND_PLUS_BACK_COLOR
+ , V_IND_IMP_FORE_COLOR
+ , V_IND_IMP_BACK_COLOR
+ , V_IND_DEL_FORE_COLOR
+ , V_IND_DEL_BACK_COLOR
+ , V_IND_ANS_FORE_COLOR
+ , V_IND_ANS_BACK_COLOR
+ , V_IND_NEW_FORE_COLOR
+ , V_IND_NEW_BACK_COLOR
+ , V_IND_REC_FORE_COLOR
+ , V_IND_REC_BACK_COLOR
+ , V_IND_FWD_FORE_COLOR
+ , V_IND_FWD_BACK_COLOR
+ , V_IND_UNS_FORE_COLOR
+ , V_IND_UNS_BACK_COLOR
+ , V_IND_HIPRI_FORE_COLOR
+ , V_IND_HIPRI_BACK_COLOR
+ , V_IND_LOPRI_FORE_COLOR
+ , V_IND_LOPRI_BACK_COLOR
+ , V_IND_ARR_FORE_COLOR
+ , V_IND_ARR_BACK_COLOR
+ , V_IND_SUBJ_FORE_COLOR
+ , V_IND_SUBJ_BACK_COLOR
+ , V_IND_FROM_FORE_COLOR
+ , V_IND_FROM_BACK_COLOR
+ , V_IND_OP_FORE_COLOR
+ , V_IND_OP_BACK_COLOR
+ , V_VIEW_HDR_COLORS
+ , V_KW_COLORS
+#if defined(DOS) || defined(OS2)
+#ifdef _WINDOWS
+ , V_FONT_NAME
+ , V_FONT_SIZE
+ , V_FONT_STYLE
+ , V_FONT_CHAR_SET
+ , V_PRINT_FONT_NAME
+ , V_PRINT_FONT_SIZE
+ , V_PRINT_FONT_STYLE
+ , V_PRINT_FONT_CHAR_SET
+ , V_WINDOW_POSITION
+ , V_CURSOR_STYLE
+#endif
+#endif
+#ifdef SMIME
+ , V_PUBLICCERT_DIR
+ , V_PUBLICCERT_CONTAINER
+ , V_PRIVATEKEY_DIR
+ , V_PRIVATEKEY_CONTAINER
+ , V_CACERT_DIR
+ , V_CACERT_CONTAINER
+#endif
+#ifdef ENABLE_LDAP
+ , V_LDAP_SERVERS /* should be last so make will work right */
+#endif
+ , V_RSS_NEWS
+ , V_RSS_WEATHER
+ , V_WP_INDEXHEIGHT
+ , V_WP_INDEXLINES
+ , V_WP_AGGSTATE
+ , V_WP_STATE
+ , V_WP_COLUMNS
+ , V_DUMMY
+} VariableIndex;
+
+#define V_LAST_VAR (V_DUMMY - 1)
+
+
+/*
+ * The list of feature numbers (which bit goes with which feature).
+ * The order of the features is not significant.
+ */
+typedef enum {
+ F_OLD_GROWTH = 0,
+ F_ENABLE_FULL_HDR,
+ F_ENABLE_PIPE,
+ F_ENABLE_TAB_COMPLETE,
+ F_QUIT_WO_CONFIRM,
+ F_ENABLE_JUMP,
+ F_ENABLE_ALT_ED,
+ F_ENABLE_BOUNCE,
+ F_ENABLE_AGG_OPS,
+ F_ENABLE_FLAG,
+ F_CAN_SUSPEND,
+ F_USE_FK,
+ F_INCLUDE_HEADER,
+ F_SIG_AT_BOTTOM,
+ F_DEL_SKIPS_DEL,
+ F_AUTO_EXPUNGE,
+ F_FULL_AUTO_EXPUNGE,
+ F_EXPUNGE_MANUALLY,
+ F_AUTO_READ_MSGS,
+ F_AUTO_FCC_ONLY,
+ F_READ_IN_NEWSRC_ORDER,
+ F_SELECT_WO_CONFIRM,
+ F_SAVE_PARTIAL_WO_CONFIRM,
+ F_NEXT_THRD_WO_CONFIRM,
+ F_USE_CURRENT_DIR,
+ F_STARTUP_STAYOPEN,
+ F_USE_RESENTTO,
+ F_SAVE_WONT_DELETE,
+ F_SAVE_ADVANCES,
+ F_UNSELECT_WONT_ADVANCE,
+ F_FORCE_LOW_SPEED,
+ F_FORCE_ARROW,
+ F_PRUNE_USES_ISO,
+ F_ALT_ED_NOW,
+ F_SHOW_DELAY_CUE,
+ F_CANCEL_CONFIRM,
+ F_AUTO_OPEN_NEXT_UNREAD,
+ F_DISABLE_INDEX_LOCALE_DATES,
+ F_SELECTED_SHOWN_BOLD,
+ F_QUOTE_ALL_FROMS,
+ F_AUTO_INCLUDE_IN_REPLY,
+ F_DISABLE_CONFIG_SCREEN,
+ F_DISABLE_PASSWORD_CACHING,
+ F_DISABLE_REGEX,
+ F_DISABLE_PASSWORD_CMD,
+ F_DISABLE_UPDATE_CMD,
+ F_DISABLE_KBLOCK_CMD,
+ F_DISABLE_SIGEDIT_CMD,
+ F_DISABLE_ROLES_SETUP,
+ F_DISABLE_ROLES_SIGEDIT,
+ F_DISABLE_ROLES_TEMPLEDIT,
+ F_DISABLE_PIPES_IN_SIGS,
+ F_DISABLE_PIPES_IN_TEMPLATES,
+ F_ATTACHMENTS_IN_REPLY,
+ F_ENABLE_INCOMING,
+ F_ENABLE_INCOMING_CHECKING,
+ F_INCOMING_CHECKING_TOTAL,
+ F_INCOMING_CHECKING_RECENT,
+ F_NO_NEWS_VALIDATION,
+ F_QUELL_EXTRA_POST_PROMPT,
+ F_DISABLE_TAKE_LASTFIRST,
+ F_DISABLE_TAKE_FULLNAMES,
+ F_DISABLE_TERM_RESET_DISP,
+ F_DISABLE_SENDER,
+ F_ROT13_MESSAGE_ID,
+ F_QUELL_LOCAL_LOOKUP,
+ F_COMPOSE_TO_NEWSGRP,
+ F_PRESERVE_START_STOP,
+ F_COMPOSE_REJECTS_UNQUAL,
+ F_FAKE_NEW_IN_NEWS,
+ F_SUSPEND_SPAWNS,
+ F_ENABLE_8BIT,
+ F_COMPOSE_MAPS_DEL,
+ F_ENABLE_8BIT_NNTP,
+ F_ENABLE_MOUSE,
+ F_SHOW_CURSOR,
+ F_PASS_CONTROL_CHARS,
+ F_PASS_C1_CONTROL_CHARS,
+ F_SINGLE_FOLDER_LIST,
+ F_VERTICAL_FOLDER_LIST,
+ F_TAB_CHK_RECENT,
+ F_AUTO_REPLY_TO,
+ F_VERBOSE_POST,
+ F_FCC_ON_BOUNCE,
+ F_SEND_WO_CONFIRM,
+ F_USE_SENDER_NOT_X,
+ F_BLANK_KEYMENU,
+ F_DISABLE_SAVE_INPUT_HISTORY,
+ F_CUSTOM_PRINT,
+ F_DEL_FROM_DOT,
+ F_AUTO_ZOOM,
+ F_AUTO_UNZOOM,
+ F_PRINT_INDEX,
+ F_ALLOW_TALK,
+ F_AGG_PRINT_FF,
+ F_ENABLE_DOT_FILES,
+ F_ENABLE_DOT_FOLDERS,
+ F_FIRST_SEND_FILTER_DFLT,
+ F_ALWAYS_LAST_FLDR_DFLT,
+ F_TAB_TO_NEW,
+ F_MARK_FOR_CC,
+ F_WARN_ABOUT_NO_SUBJECT,
+ F_WARN_ABOUT_NO_FCC,
+ F_WARN_ABOUT_NO_TO_OR_CC,
+ F_QUELL_DEAD_LETTER,
+ F_QUELL_BEEPS,
+ F_QUELL_LOCK_FAILURE_MSGS,
+ F_ENABLE_SPACE_AS_TAB,
+ F_USE_BORING_SPINNER,
+ F_ENABLE_TAB_DELETES,
+ F_FLAG_SCREEN_KW_SHORTCUT,
+ F_FLAG_SCREEN_DFLT,
+ F_ENABLE_XTERM_NEWMAIL,
+ F_ENABLE_NEWMAIL_SHORT_TEXT,
+ F_EXPANDED_DISTLISTS,
+ F_AGG_SEQ_COPY,
+ F_DISABLE_ALARM,
+ F_DISABLE_SETLOCALE_COLLATE,
+ F_FROM_DELIM_IN_PRINT,
+ F_BACKGROUND_POST,
+ F_ALLOW_GOTO,
+ F_DSN,
+ F_ENABLE_SEARCH_AND_REPL,
+ F_ARROW_NAV,
+ F_RELAXED_ARROW_NAV,
+ F_TCAP_WINS,
+ F_ENABLE_SIGDASHES,
+ F_ENABLE_STRIP_SIGDASHES,
+ F_NEW_THREAD_ON_BLANK_SUBJECT,
+ F_QUELL_PARTIAL_FETCH,
+ F_QUELL_PERSONAL_NAME_PROMPT,
+ F_QUELL_USER_ID_PROMPT,
+ F_VIEW_SEL_ATTACH,
+ F_VIEW_SEL_URL,
+ F_VIEW_SEL_URL_HOST,
+ F_SCAN_ADDR,
+ F_FORCE_ARROWS,
+ F_PREFER_PLAIN_TEXT,
+ F_QUELL_CHARSET_WARNING,
+ F_COPY_TO_TO_FROM,
+ F_ENABLE_EDIT_REPLY_INDENT,
+ F_ENABLE_PRYNT,
+ F_ALLOW_CHANGING_FROM,
+ F_ENABLE_SUB_LISTS,
+ F_ENABLE_LESSTHAN_EXIT,
+ F_ENABLE_FAST_RECENT,
+ F_TAB_USES_UNSEEN,
+ F_ENABLE_ROLE_TAKE,
+ F_ENABLE_TAKE_EXPORT,
+ F_QUELL_ATTACH_EXTRA_PROMPT,
+ F_QUELL_ASTERISKS,
+ F_QUELL_ATTACH_EXT_WARN,
+ F_QUELL_FILTER_MSGS,
+ F_QUELL_FILTER_DONE_MSG,
+ F_SHOW_SORT,
+ F_FIX_BROKEN_LIST,
+ F_ENABLE_MULNEWSRCS,
+ F_PREDICT_NNTP_SERVER,
+ F_NEWS_CROSS_DELETE,
+ F_NEWS_CATCHUP,
+ F_QUELL_INTERNAL_MSG,
+ F_QUELL_IMAP_ENV_CB,
+ F_QUELL_NEWS_ENV_CB,
+ F_SEPARATE_FLDR_AS_DIR,
+ F_CMBND_ABOOK_DISP,
+ F_CMBND_FOLDER_DISP,
+ F_CMBND_SUBDIR_DISP,
+ F_EXPANDED_ADDRBOOKS,
+ F_EXPANDED_FOLDERS,
+ F_QUELL_EMPTY_DIRS,
+ F_SHOW_TEXTPLAIN_INT,
+ F_ROLE_CONFIRM_DEFAULT,
+ F_TAB_NO_CONFIRM,
+ F_DATES_TO_LOCAL,
+ F_RET_INBOX_NO_CONFIRM,
+ F_CHECK_MAIL_ONQUIT,
+ F_PREOPEN_STAYOPENS,
+ F_EXPUNGE_STAYOPENS,
+ F_EXPUNGE_INBOX,
+ F_NO_FCC_ATTACH,
+ F_DO_MAILCAP_PARAM_SUBST,
+ F_PREFER_ALT_AUTH,
+ F_SLCTBL_ITEM_NOBOLD,
+ F_QUELL_PINGS_COMPOSING,
+ F_QUELL_PINGS_COMPOSING_INBOX,
+ F_QUELL_BEZERK_TIMEZONE,
+ F_QUELL_CONTENT_ID,
+ F_QUELL_MAILDOMAIN_WARNING,
+ F_DISABLE_SHARED_NAMESPACES,
+ F_HIDE_NNTP_PATH,
+ F_MAILDROPS_PRESERVE_STATE,
+ F_EXPOSE_HIDDEN_CONFIG,
+ F_ALT_COMPOSE_MENU,
+ F_ALT_ROLE_MENU,
+ F_ALWAYS_SPELL_CHECK,
+ F_QUELL_TIMEZONE,
+ F_QUELL_USERAGENT,
+ F_COLOR_LINE_IMPORTANT,
+ F_SLASH_COLL_ENTIRE,
+ F_ENABLE_FULL_HDR_AND_TEXT,
+ F_QUELL_FULL_HDR_RESET,
+ F_MARK_FCC_SEEN,
+ F_MULNEWSRC_HOSTNAMES_AS_TYPED,
+ F_STRIP_WS_BEFORE_SEND,
+ F_QUELL_FLOWED_TEXT,
+ F_COMPOSE_ALWAYS_DOWNGRADE,
+ F_SORT_DEFAULT_FCC_ALPHA,
+ F_SORT_DEFAULT_SAVE_ALPHA,
+ F_QUOTE_REPLACE_NOFLOW,
+ F_AUTO_UNSELECT,
+ F_SEND_CONFIRM_ON_EXPAND,
+ F_ENABLE_NEWMAIL_SOUND,
+ F_RENDER_HTML_INTERNALLY,
+ F_ENABLE_JUMP_CMD,
+ F_FORWARD_AS_ATTACHMENT,
+#ifndef _WINDOWS
+ F_USE_SYSTEM_TRANS,
+#endif /* ! _WINDOWS */
+ F_QUELL_HOST_AFTER_URL,
+ F_NNTP_SEARCH_USES_OVERVIEW,
+ F_THREAD_SORTS_BY_ARRIVAL,
+#ifdef _WINDOWS
+ F_ENABLE_TRAYICON,
+ F_QUELL_SSL_LARGEBLOCKS,
+ F_STORE_WINPOS_IN_CONFIG,
+#endif
+#ifdef ENABLE_LDAP
+ F_ADD_LDAP_TO_ABOOK,
+#endif
+#ifdef SMIME
+ F_DONT_DO_SMIME,
+ F_SIGN_DEFAULT_ON,
+ F_ENCRYPT_DEFAULT_ON,
+ F_REMEMBER_SMIME_PASSPHRASE,
+#ifdef APPLEKEYCHAIN
+ F_PUBLICCERTS_IN_KEYCHAIN,
+#endif
+#endif
+ F_FEATURE_LIST_COUNT /* Number of features */
+} FeatureList;
+
+
+typedef struct init_err {
+ int flags, min_time, max_time;
+ char *message;
+} INIT_ERR_S;
+
+
+struct variable {
+ char *name;
+ unsigned is_obsolete:1; /* variable read in, not written unless set */
+ unsigned is_used:1; /* Some variables are disabled */
+ unsigned been_written:1;
+ unsigned is_user:1;
+ unsigned is_global:1;
+ unsigned is_list:1; /* flag indicating variable is a list */
+ unsigned is_fixed:1; /* sys mgr has fixed this variable */
+ unsigned is_onlymain:1; /* read and written from main_user_val */
+ unsigned is_outermost:1; /* read and written from outermost pinerc */
+ unsigned del_quotes:1; /* remove double quotes */
+ unsigned is_changed_val:1; /* WP: use the changed val instead of cur val */
+ char *dname; /* display name */
+ char *descrip; /* description */
+ union {
+ char *p; /* pointer to single string value */
+ char **l; /* pointer to list of string values */
+ } current_val;
+ union {
+ char *p; /* pointer to single string value */
+ char **l; /* pointer to list of string values */
+ } main_user_val; /* from pinerc */
+ union {
+ char *p; /* pointer to single string value */
+ char **l; /* pointer to list of string values */
+ } changed_val; /* currently different from pinerc */
+ union {
+ char *p; /* pointer to single string value */
+ char **l; /* pointer to list of string values */
+ } post_user_val; /* from pinerc */
+ union {
+ char *p; /* pointer to single string value */
+ char **l; /* pointer to list of string values */
+ } global_val; /* from default or pine.conf */
+ union {
+ char *p; /* pointer to single string value */
+ char **l; /* pointer to list of string values */
+ } fixed_val; /* fixed value assigned in pine.conf.fixed */
+ union {
+ char *p; /* pointer to single string value */
+ char **l; /* pointer to list of string values */
+ } cmdline_val; /* user typed as cmdline arg */
+};
+
+
+typedef struct feature_entry {
+ char *name;
+ char *dname; /* display name, same as name if NULL */
+ int id;
+ HelpType help;
+ int section; /* for grouping in config screen */
+ int defval; /* default value, 0 or 1 */
+} FEATURE_S;
+
+
+typedef struct pinerc_line {
+ char *line;
+ struct variable *var;
+ unsigned int is_var:1;
+ unsigned int is_quoted:1;
+ unsigned int obsolete_var:1;
+} PINERC_LINE;
+
+
+/*
+ * Each pinerc has one of these.
+ */
+typedef struct pinerc_s {
+ RemType type; /* type of pinerc, remote or local */
+ char *name; /* file name or remote name */
+ REMDATA_S *rd; /* remote data structure */
+ time_t pinerc_written;
+ unsigned readonly:1;
+ unsigned outstanding_pinerc_changes:1;
+ unsigned quit_to_edit:1;
+ PINERC_LINE *pinerc_lines;
+} PINERC_S;
+
+
+typedef enum {ParsePers, ParsePersPost, ParseGlobal, ParseFixed} ParsePinerc;
+
+
+/* data stored in a line in the metadata file */
+typedef struct remote_data_meta {
+ char *local_cache_file;
+ imapuid_t uidvalidity;
+ imapuid_t uidnext;
+ imapuid_t uid;
+ unsigned long nmsgs;
+ char read_status; /* 'R' for readonly, 'W' for readwrite */
+ char *date;
+} REMDATA_META_S;
+
+
+/*
+ * Generic name/value pair structure
+ */
+typedef struct nameval {
+ char *name; /* the name that goes on the screen */
+ char *shortname; /* if non-NULL, name that goes in config file */
+ int value; /* the internal bit number */
+} NAMEVAL_S;
+
+
+typedef enum {Main, Post, None} EditWhich;
+
+
+#ifdef SMIME
+
+typedef enum {Directory, Container, Keychain, Nada} SmimeHolderType;
+
+typedef struct certlist {
+ char *name;
+ void *x509_cert; /* this is type (X509 *) */
+ struct certlist *next;
+}CertList;
+
+typedef struct smime_stuff {
+ unsigned inited:1;
+ unsigned do_sign:1; /* set true if signing */
+ unsigned do_encrypt:1; /* set true if encrypting */
+ unsigned need_passphrase:1; /* set true if loading a key failed due to lack of passphrase */
+ unsigned entered_passphrase:1; /* user entered a passphrase */
+ unsigned already_auto_asked:1; /* asked for passphrase automatically, not again */
+ volatile char passphrase[100]; /* storage for the entered passphrase */
+ char *passphrase_emailaddr; /* pointer to allocated storage */
+
+ /*
+ * If we are using the Container type it is easiest if we
+ * read in and maintain a list of certs and then write them
+ * out all at once. For Directory type we just leave the data
+ * in the individual files and read or write the individual
+ * files when needed, so we don't have a list of all the certs.
+ */
+ SmimeHolderType publictype;
+ char *publicpath;
+ char *publiccontent;
+ CertList *publiccertlist;
+
+ SmimeHolderType privatetype;
+ char *privatepath;
+ char *privatecontent;
+ void *personal_certs; /* this is type (PERSONAL_CERT *) */
+
+ SmimeHolderType catype;
+ char *capath;
+ char *cacontent;
+
+} SMIME_STUFF_S;
+
+#endif /* SMIME */
+
+
+/* exported protoypes */
+
+
+#endif /* PITH_CONFTYPE_INCLUDED */
diff --git a/pith/context.c b/pith/context.c
new file mode 100644
index 00000000..05f2d301
--- /dev/null
+++ b/pith/context.c
@@ -0,0 +1,788 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: context.c 1144 2008-08-14 16:53:34Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/conf.h"
+#include "../pith/context.h"
+#include "../pith/state.h"
+#include "../pith/status.h"
+#include "../pith/stream.h"
+#include "../pith/folder.h"
+#include "../pith/util.h"
+#include "../pith/tempfile.h"
+
+
+/*
+ * Internal prototypes
+ */
+char *context_percent_quote(char *);
+
+
+/* Context Manager context format digester
+ * Accepts: context string and buffers for sprintf-suitable context,
+ * remote host (for display), remote context (for display),
+ * and view string
+ * Returns: NULL if successful, else error string
+ *
+ * Comments: OK, here's what we expect a fully qualified context to
+ * look like:
+ *
+ * [*] [{host ["/"<proto>] [:port]}] [<cntxt>] "[" [<view>] "]" [<cntxt>]
+ *
+ * 2) It's understood that double "[" or "]" are used to
+ * quote a literal '[' or ']' in a context name.
+ *
+ * 3) an empty view in context implies a view of '*', so that's
+ * what get's put in the view string
+ *
+ * The 2nd,3rd,4th, and 5th args all have length at least len.
+ *
+ */
+char *
+context_digest(char *context, char *scontext, char *host, char *rcontext,
+ char *view, size_t len)
+{
+ char *p, *viewp = view;
+ char *scontextp = scontext;
+ int i = 0;
+
+ if((p = context) == NULL || *p == '\0'){
+ if(scontext) /* so the caller can apply */
+ strncpy(scontext, "%s", len); /* folder names as is. */
+
+ scontext[len-1] = '\0';
+ return(NULL); /* no error, just empty context */
+ }
+
+ /* find hostname if requested and exists */
+ if(*p == '{' || (*p == '*' && *++p == '{')){
+ for(p++; *p && *p != '}' ; p++)
+ if(host && p-context < len-1)
+ *host++ = *p; /* copy host if requested */
+
+ while(*p && *p != '}') /* find end of imap host */
+ p++;
+
+ if(*p == '\0')
+ return("Unbalanced '}'"); /* bogus. */
+ else
+ p++; /* move into next field */
+ }
+
+ for(; *p ; p++){ /* get thru context */
+ if(rcontext && i < len-1)
+ rcontext[i] = *p; /* copy if requested */
+
+ i++;
+
+ if(*p == '['){ /* done? */
+ if(*++p == '\0') /* look ahead */
+ return("Unbalanced '['");
+
+ if(*p != '[') /* not quoted: "[[" */
+ break;
+ }
+ else if(*p == ']' && *(p+1) == ']') /* these may be quoted, too */
+ p++;
+ }
+
+ if(*p == '\0')
+ return("No '[' in context");
+
+ for(; *p ; p++){ /* possibly in view portion ? */
+ if(*p == ']'){
+ if(*(p+1) == ']') /* is quoted */
+ p++;
+ else
+ break;
+ }
+
+ if(viewp && viewp-view < len-1)
+ *viewp++ = *p;
+ }
+
+ if(*p != ']')
+ return("No ']' in context");
+
+ for(; *p ; p++){ /* trailing context ? */
+ if(rcontext && i < len-1)
+ rcontext[i] = *p;
+ i++;
+ }
+
+ if(host) *host = '\0';
+ if(rcontext && i < len) rcontext[i] = '\0';
+ if(viewp) {
+/* MAIL_LIST: dealt with it in new_context since could be either '%' or '*'
+ if(viewp == view && viewp-view < len-1)
+ *viewp++ = '*';
+*/
+
+ *viewp = '\0';
+ }
+
+ if(scontextp){ /* sprint'able context request ? */
+ if(*context == '*'){
+ if(scontextp-scontext < len-1)
+ *scontextp++ = *context;
+
+ context++;
+ }
+
+ if(*context == '{'){
+ while(*context && *context != '}'){
+ if(scontextp-scontext < len-1)
+ *scontextp++ = *context;
+
+ context++;
+ }
+
+ *scontextp++ = '}';
+ }
+
+ for(p = rcontext; *p ; p++){
+ if(*p == '[' && *(p+1) == ']'){
+ if(scontextp-scontext < len-2){
+ *scontextp++ = '%'; /* replace "[]" with "%s" */
+ *scontextp++ = 's';
+ }
+
+ p++; /* skip ']' */
+ }
+ else if(scontextp-scontext < len-1)
+ *scontextp++ = *p;
+ }
+
+ *scontextp = '\0';
+ }
+
+ return(NULL); /* no problems to report... */
+}
+
+
+/* Context Manager apply name to context
+ * Accepts: buffer to write, context to apply, ambiguous folder name
+ * Returns: buffer filled with fully qualified name in context
+ * No context applied if error
+ */
+char *
+context_apply(char *b, CONTEXT_S *c, char *name, size_t len)
+{
+ if(!c || IS_REMOTE(name) ||
+ (!IS_REMOTE(c->context) && is_absolute_path(name))){
+ strncpy(b, name, len-1); /* no context! */
+ }
+ else if(name[0] == '#'){
+ if(IS_REMOTE(c->context)){
+ char *p = strchr(c->context, '}'); /* name specifies namespace */
+ snprintf(b, len, "%.*s", MIN(p - c->context + 1, len-1), c->context);
+ b[MIN(p - c->context + 1, len-1)] = '\0';
+ snprintf(b+strlen(b), len-strlen(b), "%.*s", len-1-strlen(b), name);
+ }
+ else{
+ strncpy(b, name, len-1);
+ }
+ }
+ else if(c->dir && c->dir->ref){ /* has reference string! */
+ snprintf(b, len, "%.*s", len-1, c->dir->ref);
+ b[len-1] = '\0';
+ snprintf(b+strlen(b), len-strlen(b), "%.*s", len-1-strlen(b), name);
+ }
+ else{ /* no ref, apply to context */
+ char *pq = NULL;
+
+ /*
+ * Have to quote %s for the sprintf because we're using context
+ * as a format string.
+ */
+ pq = context_percent_quote(c->context);
+
+ if(strlen(c->context) + strlen(name) < len)
+ snprintf(b, len, pq, name);
+ else{
+ char *t;
+ size_t l;
+
+ l = strlen(pq)+strlen(name);
+ t = (char *) fs_get((l+1) * sizeof(char));
+ snprintf(t, l+1, pq, name);
+ strncpy(b, t, len-1);
+ fs_give((void **)&t);
+ }
+
+ if(pq)
+ fs_give((void **) &pq);
+ }
+
+ b[len-1] = '\0';
+ return(b);
+}
+
+
+/*
+ * Insert % before existing %'s so printf will print a real %.
+ * This is a special routine just for contexts. It only does the % stuffing
+ * for %'s inside of the braces of the context name, not for %'s to
+ * the right of the braces, which we will be using for printf format strings.
+ * Returns a malloced string which the caller is responsible for.
+ */
+char *
+context_percent_quote(char *context)
+{
+ char *pq = NULL;
+
+ if(!context || !*context)
+ pq = cpystr("");
+ else{
+ if(IS_REMOTE(context)){
+ char *end, *p, *q;
+
+ /* don't worry about size efficiency, just allocate double */
+ pq = (char *) fs_get((2*strlen(context) + 1) * sizeof(char));
+
+ end = strchr(context, '}');
+ p = context;
+ q = pq;
+ while(*p){
+ if(*p == '%' && p < end)
+ *q++ = '%';
+
+ *q++ = *p++;
+ }
+
+ *q = '\0';
+ }
+ else
+ pq = cpystr(context);
+ }
+
+ return(pq);
+}
+
+
+/* Context Manager check if name is ambiguous
+ * Accepts: candidate string
+ * Returns: T if ambiguous, NIL if fully-qualified
+ */
+
+int
+context_isambig (char *s)
+{
+ return(!(*s == '{' || *s == '#'));
+}
+
+
+/*----------------------------------------------------------------------
+ Check to see if user is allowed to read or write this folder.
+
+ Args: s -- the name to check
+
+ Result: Returns 1 if OK
+ Returns 0 and posts an error message if access is denied
+ ----*/
+int
+context_allowed(char *s)
+{
+ struct variable *vars = ps_global ? ps_global->vars : NULL;
+ int retval = 1;
+ MAILSTREAM stream; /* fake stream for error message in mm_notify */
+
+ if(ps_global
+ && ps_global->restricted
+ && (strindex("./~", s[0]) || srchstr(s, "/../"))){
+ stream.mailbox = s;
+ mm_notify(&stream, "Restricted mode doesn't allow operation", WARN);
+ retval = 0;
+ }
+ else if(vars && VAR_OPER_DIR
+ && s[0] != '{' && !(s[0] == '*' && s[1] == '{')
+ && strucmp(s,ps_global->inbox_name) != 0
+ && strcmp(s, ps_global->VAR_INBOX_PATH) != 0){
+ char *p, *free_this = NULL;
+
+ p = s;
+ if(strindex(s, '~')){
+ p = strindex(s, '~');
+ free_this = (char *)fs_get(strlen(p) + 200);
+ strncpy(free_this, p, strlen(p)+200);
+ fnexpand(free_this, strlen(p)+200);
+ p = free_this;
+ }
+ else if(p[0] != '/'){ /* add home dir to relative paths */
+ free_this = p = (char *)fs_get(strlen(s)
+ + strlen(ps_global->home_dir) + 2);
+ build_path(p, ps_global->home_dir, s,
+ strlen(s)+strlen(ps_global->home_dir)+2);
+ }
+
+ if(!in_dir(VAR_OPER_DIR, p)){
+ char err[200];
+
+ /* TRANSLATORS: User is restricted to operating within a certain directory */
+ snprintf(err, sizeof(err), _("Not allowed outside of %.150s"), VAR_OPER_DIR);
+ stream.mailbox = p;
+ mm_notify(&stream, err, WARN);
+ retval = 0;
+ }
+ else if(srchstr(p, "/../")){ /* check for .. in path */
+ stream.mailbox = p;
+ mm_notify(&stream, "\"..\" not allowed in name", WARN);
+ retval = 0;
+ }
+
+ if(free_this)
+ fs_give((void **)&free_this);
+ }
+
+ return retval;
+}
+
+
+
+/* Context Manager create mailbox
+ * Accepts: context
+ * mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long
+context_create (CONTEXT_S *context, MAILSTREAM *stream, char *mailbox)
+{
+ char tmp[MAILTMPLEN]; /* must be within context */
+
+ return(context_allowed(context_apply(tmp, context, mailbox, sizeof(tmp)))
+ ? pine_mail_create(stream,tmp) : 0L);
+}
+
+
+/* Context Manager open
+ * Accepts: context
+ * candidate stream for recycling
+ * mailbox name
+ * open options
+ * Returns: stream to use on success, NIL on failure
+ */
+
+MAILSTREAM *
+context_open (CONTEXT_S *context, MAILSTREAM *old, char *name, long int opt, long int *retflags)
+{
+ char tmp[MAILTMPLEN]; /* build FQN from ambiguous name */
+
+ if(!context_allowed(context_apply(tmp, context, name, sizeof(tmp)))){
+ /*
+ * If a stream is passed to context_open, we will either re-use
+ * it or close it.
+ */
+ if(old)
+ pine_mail_close(old);
+
+ return((MAILSTREAM *)NULL);
+ }
+
+ return(pine_mail_open(old, tmp, opt, retflags));
+}
+
+
+/* Context Manager status
+ * Accepts: context
+ * candidate stream for recycling
+ * mailbox name
+ * open options
+ * Returns: T if call succeeds, NIL on failure
+ */
+
+long
+context_status(CONTEXT_S *context, MAILSTREAM *stream, char *name, long int opt)
+{
+ return(context_status_full(context, stream, name, opt, NULL, NULL));
+}
+
+
+long
+context_status_full(CONTEXT_S *context, MAILSTREAM *stream, char *name,
+ long int opt, imapuid_t *uidvalidity, imapuid_t *uidnext)
+{
+ char tmp[MAILTMPLEN]; /* build FQN from ambiguous name */
+ long flags = opt;
+
+ return(context_allowed(context_apply(tmp, context, name, sizeof(tmp)))
+ ? pine_mail_status_full(stream,tmp,flags,uidvalidity,uidnext) : 0L);
+}
+
+
+/* Context Manager status
+ *
+ * This is very similar to context_status. Instead of a stream pointer we
+ * receive a pointer to a pointer so that we can return a stream that we
+ * opened for further use by the caller.
+ *
+ * Accepts: context
+ * candidate stream for recycling
+ * mailbox name
+ * open options
+ * Returns: T if call succeeds, NIL on failure
+ */
+
+long
+context_status_streamp(CONTEXT_S *context, MAILSTREAM **streamp, char *name, long int opt)
+{
+ return(context_status_streamp_full(context,streamp,name,opt,NULL,NULL));
+}
+
+
+long
+context_status_streamp_full(CONTEXT_S *context, MAILSTREAM **streamp, char *name,
+ long int opt, imapuid_t *uidvalidity, imapuid_t *uidnext)
+{
+ MAILSTREAM *stream;
+ char tmp[MAILTMPLEN]; /* build FQN from ambiguous name */
+
+ if(!context_allowed(context_apply(tmp, context, name, sizeof(tmp))))
+ return(0L);
+
+ if(!streamp)
+ stream = NULL;
+ else{
+ if(!*streamp && IS_REMOTE(tmp)){
+ *streamp = pine_mail_open(NULL, tmp,
+ OP_SILENT|OP_HALFOPEN|SP_USEPOOL, NULL);
+ }
+
+ stream = *streamp;
+ }
+
+ return(pine_mail_status_full(stream, tmp, opt, uidvalidity, uidnext));
+}
+
+
+/* Context Manager rename
+ * Accepts: context
+ * mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long
+context_rename (CONTEXT_S *context, MAILSTREAM *stream, char *old, char *new)
+{
+ char tmp[MAILTMPLEN],tmp2[MAILTMPLEN];
+
+ return((context_allowed(context_apply(tmp, context, old, sizeof(tmp)))
+ && context_allowed(context_apply(tmp2, context, new, sizeof(tmp2))))
+ ? pine_mail_rename(stream,tmp,tmp2) : 0L);
+}
+
+
+MAILSTREAM *
+context_already_open_stream(CONTEXT_S *context, char *name, int flags)
+{
+ char tmp[MAILTMPLEN];
+
+ return(already_open_stream(context_apply(tmp, context, name, sizeof(tmp)),
+ flags));
+}
+
+
+/* Context Manager delete mailbox
+ * Accepts: context
+ * mail stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long
+context_delete (CONTEXT_S *context, MAILSTREAM *stream, char *name)
+{
+ char tmp[MAILTMPLEN]; /* build FQN from ambiguous name */
+
+ return(context_allowed(context_apply(tmp, context, name, sizeof(tmp)))
+ ? pine_mail_delete(stream, tmp) : 0L);
+}
+
+
+/* Context Manager append message string
+ * Accepts: context
+ * mail stream
+ * destination mailbox
+ * stringstruct of message to append
+ * Returns: T on success, NIL on failure
+ */
+
+long
+context_append (CONTEXT_S *context, MAILSTREAM *stream, char *name, STRING *msg)
+{
+ char tmp[MAILTMPLEN]; /* build FQN from ambiguous name */
+
+ return(context_allowed(context_apply(tmp, context, name, sizeof(tmp)))
+ ? pine_mail_append(stream, tmp, msg) : 0L);
+}
+
+
+/* Context Manager append message string with flags
+ * Accepts: context
+ * mail stream
+ * destination mailbox
+ * flags to assign message being appended
+ * date of message being appended
+ * stringstruct of message to append
+ * Returns: T on success, NIL on failure
+ */
+
+long
+context_append_full(CONTEXT_S *context, MAILSTREAM *stream, char *name,
+ char *flags, char *date, STRING *msg)
+{
+ char tmp[MAILTMPLEN]; /* build FQN from ambiguous name */
+
+ return(context_allowed(context_apply(tmp, context, name, sizeof(tmp)))
+ ? pine_mail_append_full(stream, tmp, flags, date, msg) : 0L);
+}
+
+
+/* Context Manager append multiple message
+ * Accepts: context
+ * mail stream
+ * destination mailbox
+ * append data callback
+ * arbitrary ata for callback use
+ * Returns: T on success, NIL on failure
+ */
+
+long
+context_append_multiple(CONTEXT_S *context, MAILSTREAM *stream, char *name,
+ append_t af, APPENDPACKAGE *data, MAILSTREAM *not_this_stream)
+{
+ char tmp[MAILTMPLEN]; /* build FQN from ambiguous name */
+
+ return(context_allowed(context_apply(tmp, context, name, sizeof(tmp)))
+ ? pine_mail_append_multiple(stream, tmp, af, data, not_this_stream) : 0L);
+}
+
+
+/* Mail copy message(s)
+ * Accepts: context
+ * mail stream
+ * sequence
+ * destination mailbox
+ */
+
+long
+context_copy (CONTEXT_S *context, MAILSTREAM *stream, char *sequence, char *name)
+{
+ char *s, tmp[MAILTMPLEN]; /* build FQN from ambiguous name */
+
+ tmp[0] = '\0';
+
+ if(context_apply(tmp, context, name, sizeof(tmp))[0] == '{'){
+ if((s = strindex(tmp, '}')) != NULL)
+ s++;
+ else
+ return(0L);
+ }
+ else
+ s = tmp;
+
+ tmp[sizeof(tmp)-1] = '\0';
+
+ if(!*s)
+ strncpy(s = tmp, "INBOX", sizeof(tmp)); /* presume "inbox" ala c-client */
+
+ tmp[sizeof(tmp)-1] = '\0';
+
+ return(context_allowed(s) ? pine_mail_copy(stream, sequence, s) : 0L);
+}
+
+
+/*
+ * Context manager stream usefulness test
+ * Accepts: context
+ * mail name
+ * mailbox name
+ * mail stream to test against mailbox name
+ * Returns: stream if useful, else NIL
+ */
+MAILSTREAM *
+context_same_stream(CONTEXT_S *context, char *name, MAILSTREAM *stream)
+{
+ extern MAILSTREAM *same_stream(char *name, MAILSTREAM *stream);
+ char tmp[MAILTMPLEN]; /* build FQN from ambiguous name */
+
+ return(same_stream(context_apply(tmp, context, name, sizeof(tmp)), stream));
+}
+
+
+/*
+ * new_context - creates and fills in a new context structure, leaving
+ * blank the actual folder list (to be filled in later as
+ * needed). Also, parses the context string provided
+ * picking out any user defined label. Context lines are
+ * of the form:
+ *
+ * [ ["] <string> ["] <white-space>] <context>
+ *
+ */
+CONTEXT_S *
+new_context(char *cntxt_string, int *prime)
+{
+ CONTEXT_S *c;
+ char host[MAXPATH], rcontext[MAXPATH],
+ view[MAXPATH], dcontext[MAXPATH],
+ *nickname = NULL, *c_string = NULL, *p;
+
+ /*
+ * do any context string parsing (like splitting user-supplied
+ * label from actual context)...
+ */
+ get_pair(cntxt_string, &nickname, &c_string, 0, 0);
+
+ if(update_bboard_spec(c_string, tmp_20k_buf, SIZEOF_20KBUF)){
+ fs_give((void **) &c_string);
+ c_string = cpystr(tmp_20k_buf);
+ }
+
+ if(c_string && *c_string == '\"')
+ (void)removing_double_quotes(c_string);
+
+ view[0] = rcontext[0] = host[0] = dcontext[0] = '\0';
+ if((p = context_digest(c_string, dcontext, host, rcontext, view, MAXPATH)) != NULL){
+ q_status_message2(SM_ORDER | SM_DING, 3, 4,
+ "Bad context, %.200s : %.200s", p, c_string);
+ fs_give((void **) &c_string);
+ if(nickname)
+ fs_give((void **)&nickname);
+
+ return(NULL);
+ }
+ else
+ fs_give((void **) &c_string);
+
+ c = (CONTEXT_S *) fs_get(sizeof(CONTEXT_S)); /* get new context */
+ memset((void *) c, 0, sizeof(CONTEXT_S)); /* and initialize it */
+ if(*host)
+ c->server = cpystr(host); /* server + params */
+
+ c->context = cpystr(dcontext);
+
+ if(strstr(c->context, "#news."))
+ c->use |= CNTXT_NEWS;
+
+ c->dir = new_fdir(NULL, view, (c->use & CNTXT_NEWS) ? '*' : '%');
+
+ /* fix up nickname */
+ if(!(c->nickname = nickname)){ /* make one up! */
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%.100s",
+ (c->use & CNTXT_NEWS)
+ ? "News"
+ : (c->dir->ref)
+ ? (c->dir->ref) :"Mail",
+ (c->server) ? " on " : "",
+ (c->server) ? c->server : "");
+ c->nickname = cpystr(tmp_20k_buf);
+ }
+
+ if(prime && !*prime){
+ *prime = 1;
+ c->use |= CNTXT_SAVEDFLT;
+ }
+
+ /* fix up label */
+ if(NEWS_TEST(c)){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%sews groups%s%.100s",
+ (*host) ? "N" : "Local n", (*host) ? " on " : "",
+ (*host) ? host : "");
+ }
+ else{
+ p = srchstr(rcontext, "[]");
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%solders%s%.100s in %.*s%s",
+ (*host) ? "F" : "Local f", (*host) ? " on " : "",
+ (*host) ? host : "",
+ p ? MIN(p - rcontext, 100) : 0,
+ rcontext, (p && (p - rcontext) > 0) ? "" : "home directory");
+ }
+
+ c->label = cpystr(tmp_20k_buf);
+
+ dprint((5, "Context: %s serv:%s ref: %s view: %s\n",
+ c->context ? c->context : "?",
+ (c->server) ? c->server : "\"\"",
+ (c->dir->ref) ? c->dir->ref : "\"\"",
+ (c->dir->view.user) ? c->dir->view.user : "\"\""));
+
+ return(c);
+}
+
+
+/*
+ * Release resources associated with global context list
+ */
+void
+free_contexts(CONTEXT_S **ctxt)
+{
+ if(ctxt && *ctxt){
+ free_contexts(&(*ctxt)->next);
+ free_context(ctxt);
+ }
+}
+
+
+/*
+ * Release resources associated with the given context structure
+ */
+void
+free_context(CONTEXT_S **cntxt)
+{
+ if(cntxt && *cntxt){
+ if(*cntxt == ps_global->context_current)
+ ps_global->context_current = NULL;
+
+ if(*cntxt == ps_global->context_last)
+ ps_global->context_last = NULL;
+
+ if(*cntxt == ps_global->last_save_context)
+ ps_global->last_save_context = NULL;
+
+ if((*cntxt)->context)
+ fs_give((void **) &(*cntxt)->context);
+
+ if((*cntxt)->server)
+ fs_give((void **) &(*cntxt)->server);
+
+ if((*cntxt)->nickname)
+ fs_give((void **)&(*cntxt)->nickname);
+
+ if((*cntxt)->label)
+ fs_give((void **) &(*cntxt)->label);
+
+ if((*cntxt)->comment)
+ fs_give((void **) &(*cntxt)->comment);
+
+ if((*cntxt)->selected.reference)
+ fs_give((void **) &(*cntxt)->selected.reference);
+
+ if((*cntxt)->selected.sub)
+ free_selected(&(*cntxt)->selected.sub);
+
+ free_strlist(&(*cntxt)->selected.folders);
+
+ free_fdir(&(*cntxt)->dir, 1);
+
+ fs_give((void **)cntxt);
+ }
+}
diff --git a/pith/context.h b/pith/context.h
new file mode 100644
index 00000000..a2917332
--- /dev/null
+++ b/pith/context.h
@@ -0,0 +1,52 @@
+/*
+ * $Id: context.h 764 2007-10-23 23:44:49Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_CONTEXT_INCLUDED
+#define PITH_CONTEXT_INCLUDED
+
+
+#include "../pith/foldertype.h"
+#include "../pith/savetype.h"
+
+
+/* exported prototypes */
+char *context_digest(char *, char *, char *, char *, char *, size_t);
+char *context_apply(char *, CONTEXT_S *, char *, size_t);
+int context_isambig(char *);
+int context_allowed(char *);
+long context_create(CONTEXT_S *, MAILSTREAM *, char *);
+MAILSTREAM *context_open(CONTEXT_S *, MAILSTREAM *, char *, long, long *);
+long context_status(CONTEXT_S *, MAILSTREAM *, char *, long);
+long context_status_full(CONTEXT_S *, MAILSTREAM *, char *,
+ long, imapuid_t *, imapuid_t *);
+long context_status_streamp(CONTEXT_S *, MAILSTREAM **,
+ char *, long);
+long context_status_streamp_full(CONTEXT_S *, MAILSTREAM **, char *, long,
+ imapuid_t *, imapuid_t *);
+long context_rename(CONTEXT_S *, MAILSTREAM *, char *, char *);
+MAILSTREAM *context_already_open_stream(CONTEXT_S *, char *, int);
+long context_delete(CONTEXT_S *, MAILSTREAM *, char *);
+long context_append(CONTEXT_S *, MAILSTREAM *, char *, STRING *);
+long context_append_full(CONTEXT_S *, MAILSTREAM *, char *, char *, char *, STRING *);
+long context_append_multiple(CONTEXT_S *, MAILSTREAM *, char *, append_t,
+ APPENDPACKAGE *, MAILSTREAM *);
+long context_copy(CONTEXT_S *, MAILSTREAM *, char *, char *);
+MAILSTREAM *context_same_stream(CONTEXT_S *, char *, MAILSTREAM *);
+CONTEXT_S *new_context(char *, int *);
+void free_contexts(CONTEXT_S **);
+void free_context(CONTEXT_S **);
+
+
+#endif /* PITH_CONTEXT_INCLUDED */
diff --git a/pith/copyaddr.c b/pith/copyaddr.c
new file mode 100644
index 00000000..1c8a5565
--- /dev/null
+++ b/pith/copyaddr.c
@@ -0,0 +1,70 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: copyaddr.c 761 2007-10-23 22:35:18Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/copyaddr.h"
+
+
+/*
+ * Copy the first address in list a and return it in allocated memory.
+ */
+ADDRESS *
+copyaddr(struct mail_address *a)
+{
+ ADDRESS *new = NULL;
+
+ if(a){
+ new = mail_newaddr();
+ if(a->personal)
+ new->personal = cpystr(a->personal);
+
+ if(a->adl)
+ new->adl = cpystr(a->adl);
+
+ if(a->mailbox)
+ new->mailbox = cpystr(a->mailbox);
+
+ if(a->host)
+ new->host = cpystr(a->host);
+
+ new->next = NULL;
+ }
+
+ return(new);
+}
+
+
+/*
+ * Copy the whole list a.
+ */
+ADDRESS *
+copyaddrlist(struct mail_address *a)
+{
+ ADDRESS *new = NULL, *head = NULL, *current;
+
+ for(; a; a = a->next){
+ new = copyaddr(a);
+ if(!head)
+ head = current = new;
+ else{
+ current->next = new;
+ current = new;
+ }
+ }
+
+ return(head);
+}
diff --git a/pith/copyaddr.h b/pith/copyaddr.h
new file mode 100644
index 00000000..790cffe9
--- /dev/null
+++ b/pith/copyaddr.h
@@ -0,0 +1,25 @@
+/*
+ * $Id: copyaddr.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_COPYADDR_INCLUDED
+#define PITH_COPYADDR_INCLUDED
+
+
+/* exported protoypes */
+ADDRESS *copyaddr(ADDRESS *);
+ADDRESS *copyaddrlist(ADDRESS *);
+
+
+#endif /* PITH_COPYADDR_INCLUDED */
diff --git a/pith/debug.h b/pith/debug.h
new file mode 100644
index 00000000..912791d8
--- /dev/null
+++ b/pith/debug.h
@@ -0,0 +1,58 @@
+/*
+ * $Id: debug.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_DEBUG_INCLUDED
+#define PITH_DEBUG_INCLUDED
+
+
+/*======================================================================
+ Macros for debug printfs
+ n is debugging level:
+ 1 logs only highest level events and errors
+ 2 logs events like file writes
+ 3
+ 4 logs each command
+ 5
+ 6
+ 7 logs details of command execution (7 is highest to run any production)
+ allows core dumps without cleaning up terminal modes
+ 8
+ 9 logs gross details of command execution
+
+ ====*/
+
+
+#ifdef DEBUG
+
+#define dprint(x) { output_debug_msg x ; }
+
+/* global debugging level */
+extern int debug;
+
+/* mandatory to implement stubs */
+void output_debug_msg(int, char *fmt, ...);
+void dump_configuration(int);
+void dump_contexts();
+
+
+
+#else /* !DEBUG */
+
+#define dprint(x)
+
+#endif /* !DEBUG */
+
+
+#endif /* PITH_DEBUG_INCLUDED */
diff --git a/pith/detach.c b/pith/detach.c
new file mode 100644
index 00000000..b3122c68
--- /dev/null
+++ b/pith/detach.c
@@ -0,0 +1,837 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: detach.c 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/detach.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/store.h"
+#include "../pith/filter.h"
+#include "../pith/mailview.h"
+#include "../pith/status.h"
+#include "../pith/addrstring.h"
+#include "../pith/bldaddr.h"
+#include "../pith/mimedesc.h"
+#include "../pith/adjtime.h"
+#include "../pith/pipe.h"
+#include "../pith/busy.h"
+#include "../pith/signal.h"
+#include "../pico/osdep/filesys.h"
+
+
+/*
+ * We need to define simple functions here for the piping and
+ * temporary storage object below. We can use the filter.c functions
+ * because they're already in use for the "putchar" function passed to
+ * detach.
+ */
+static STORE_S *detach_so = NULL;
+
+
+/*
+ * The display filter is locally global because it's set in df_trigger_cmp
+ * which sniffs at lines of the unencoded segment...
+ */
+typedef struct _trigger {
+ int (*cmp)(char *, char *);
+ char *text;
+ char *cmd;
+ struct _trigger *next;
+} TRGR_S;
+
+static char *display_filter;
+static TRGR_S *df_trigger_list;
+
+FETCH_READC_S *g_fr_desc;
+
+#define INIT_FETCH_CHUNK ((unsigned long)(8 * 1024L))
+#define MIN_FETCH_CHUNK ((unsigned long)(4 * 1024L))
+#define MAX_FETCH_CHUNK ((unsigned long)(256 * 1024L))
+#define TARGET_INTR_TIME ((unsigned long)2000000L) /* two seconds */
+#define FETCH_READC g_fr_desc->readc
+
+
+/*
+ * Internal Prototypes
+ */
+/*
+ * This function is intentionally declared without an argument type so
+ * that warnings will go away in Windows. We're using gf_io_t for both
+ * input and output functions and the arguments aren't actually the
+ * same in the two cases. We should really have a read version and
+ * a write version of gf_io_t. That's why this is like this for now.
+ */
+int detach_writec();
+TRGR_S *build_trigger_list(void);
+void blast_trigger_list(TRGR_S **);
+int df_trigger_cmp(long, char *, LT_INS_S **, void *);
+int df_trigger_cmp_text(char *, char *);
+int df_trigger_cmp_lwsp(char *, char *);
+int df_trigger_cmp_start(char *, char *);
+int fetch_readc_cleanup(void);
+char *fetch_gets(readfn_t, void *, unsigned long, GETS_DATA *);
+int fetch_readc(unsigned char *);
+
+
+
+/*----------------------------------------------------------------------
+ detach the given raw body part; don't do any decoding
+
+ Args: a bunch
+
+ Returns: NULL on success, error message otherwise
+ ----*/
+char *
+detach_raw(MAILSTREAM *stream, /* c-client stream to use */
+ long int msg_no, /* message number to deal with */
+ char *part_no, /* part number of message */
+ gf_io_t pc, /* where to put it */
+ int flags)
+{
+ FETCH_READC_S *frd = (FETCH_READC_S *)fs_get(sizeof(FETCH_READC_S));
+ char *err = NULL;
+ int column = (flags & FM_DISPLAY) ? ps_global->ttyo->screen_cols : 80;
+
+ memset(g_fr_desc = frd, 0, sizeof(FETCH_READC_S));
+ frd->stream = stream;
+ frd->msgno = msg_no;
+ frd->section = part_no;
+ frd->size = 0; /* wouldn't be here otherwise */
+ frd->readc = fetch_readc;
+ frd->chunk = pine_mail_fetch_text(stream, msg_no, NULL, &frd->read, 0);
+ frd->endp = &frd->chunk[frd->read];
+ frd->chunkp = frd->chunk;
+
+ gf_filter_init();
+ if (!(flags & FM_NOWRAP))
+ gf_link_filter(gf_wrap, gf_wrap_filter_opt(column, column, NULL, 0,
+ (flags & FM_DISPLAY)
+ ? GFW_HANDLES : 0));
+ err = gf_pipe(FETCH_READC, pc);
+
+ return(err);
+}
+
+
+/*----------------------------------------------------------------------
+ detach the given body part using the given encoding
+
+ Args: a bunch
+
+ Returns: NULL on success, error message otherwise
+ ----*/
+char *
+detach(MAILSTREAM *stream, /* c-client stream to use */
+ long int msg_no, /* message number to deal with */
+ char *part_no, /* part number of message */
+ long int partial, /* if >0, limit read to this many bytes */
+ long int *len, /* returns bytes read in this arg */
+ gf_io_t pc, /* where to put it */
+ FILTLIST_S *aux_filters, /* null terminated array of filts */
+ long flags)
+{
+ unsigned long rv;
+ unsigned long size;
+ long fetch_flags;
+ int we_cancel = 0, is_text;
+ char *status, trigger[MAILTMPLEN];
+ char *charset = NULL;
+ BODY *body;
+ static char err_string[100];
+ FETCH_READC_S fetch_part;
+
+ err_string[0] = '\0';
+
+ if(!ps_global->print && !pc_is_picotext(pc))
+ we_cancel = busy_cue(NULL, NULL, 1);
+
+ gf_filter_init(); /* prepare for filtering! */
+
+ if(!(body = mail_body(stream, msg_no, (unsigned char *) part_no)))
+ return(_("Can't find body for requested message"));
+
+ is_text = body->type == TYPETEXT;
+
+ size = body->size.bytes;
+ if(partial > 0L && partial < size)
+ size = partial;
+
+ fetch_flags = (flags & ~DT_NODFILTER);
+ fetch_readc_init(&fetch_part, stream, msg_no, part_no, body->size.bytes, partial,
+ fetch_flags);
+ rv = size ? size : 1;
+
+ switch(body->encoding) { /* handle decoding */
+ case ENC7BIT:
+ case ENC8BIT:
+ case ENCBINARY:
+ break;
+
+ case ENCBASE64:
+ gf_link_filter(gf_b64_binary, NULL);
+ break;
+
+ case ENCQUOTEDPRINTABLE:
+ gf_link_filter(gf_qp_8bit, NULL);
+ break;
+
+ case ENCOTHER:
+ default:
+ dprint((1, "detach: unknown CTE: \"%s\" (%d)\n",
+ (body->encoding <= ENCMAX
+ && body_encodings[body->encoding])
+ ? body_encodings[body->encoding]
+ : "BEYOND-KNOWN-TYPES",
+ body->encoding));
+ break;
+ }
+
+ /* convert all text to UTF-8 */
+ if(is_text){
+ charset = parameter_val(body->parameter, "charset");
+
+ /*
+ * If the charset is unlabeled or unknown replace it
+ * with the user's configured unknown charset.
+ */
+ if(!charset || !strucmp(charset, UNKNOWN_CHARSET) || !strucmp(charset, "us-ascii")){
+ if(charset)
+ fs_give((void **) &charset);
+
+ if(ps_global->VAR_UNK_CHAR_SET)
+ charset = cpystr(ps_global->VAR_UNK_CHAR_SET);
+ }
+
+ /* convert to UTF-8 */
+ if(!(charset && !strucmp(charset, "utf-8")))
+ gf_link_filter(gf_utf8, gf_utf8_opt(charset));
+
+ if(charset)
+ fs_give((void **) &charset);
+ }
+
+ /*
+ * If we're detaching a text segment and there are user-defined
+ * filters and there are text triggers to look for, install filter
+ * to let us look at each line...
+ */
+ display_filter = NULL;
+ if(is_text
+ && ps_global->tools.display_filter
+ && ps_global->tools.display_filter_trigger
+ && ps_global->VAR_DISPLAY_FILTERS
+ && !(flags & DT_NODFILTER)){
+ /* check for "static" triggers (i.e., none or CHARSET) */
+ if(!(display_filter = (*ps_global->tools.display_filter_trigger)(body, trigger, sizeof(trigger)))
+ && (df_trigger_list = build_trigger_list())){
+ /* else look for matching text trigger */
+ gf_link_filter(gf_line_test,
+ gf_line_test_opt(df_trigger_cmp, NULL));
+ }
+ }
+ else
+ /* add aux filters if we're not going to MIME decode into a temporary
+ * storage object, otherwise we pass the aux_filters on to gf_filter
+ * below so it can pass what comes out of the external filter command
+ * thru the rest of the filters...
+ */
+ for( ; aux_filters && aux_filters->filter; aux_filters++)
+ gf_link_filter(aux_filters->filter, aux_filters->data);
+
+ /*
+ * Following canonical model, after decoding convert newlines from
+ * crlf to local convention. ALSO, convert newlines if we're fetching
+ * a multipart segment since an external handler's going to have to
+ * make sense of it...
+ */
+ if(is_text || body->type == TYPEMESSAGE || body->type == TYPEMULTIPART)
+ gf_link_filter(gf_nvtnl_local, NULL);
+
+ /*
+ * If we're detaching a text segment and a user-defined filter may
+ * need to be invoked later (see below), decode the segment into
+ * a temporary storage object...
+ */
+ if(is_text
+ && ps_global->tools.display_filter
+ && ps_global->tools.display_filter_trigger
+ && ps_global->VAR_DISPLAY_FILTERS
+ && !(flags & DT_NODFILTER)
+ && !(detach_so = so_get(CharStar, NULL, EDIT_ACCESS))){
+ strncpy(err_string,
+ _("Formatting error: no space to make copy, no display filters used"), sizeof(err_string));
+ err_string[sizeof(err_string)-1] = '\0';
+ }
+
+ if((status = gf_pipe(FETCH_READC, detach_so ? detach_writec : pc)) != NULL) {
+ snprintf(err_string, sizeof(err_string), "Formatting error: %s", status);
+ rv = 0L;
+ }
+
+ /*
+ * If we wrote to a temporary area, there MAY be a user-defined
+ * filter to invoke. Filter it it (or not if no trigger match)
+ * *AND* send the output thru any auxiliary filters, destroy the
+ * temporary object and be done with it...
+ */
+ if(detach_so){
+ if(!err_string[0] && display_filter && *display_filter){
+ FILTLIST_S *p, *aux = NULL;
+ size_t count;
+
+ if(aux_filters){
+ /* insert NL conversion filters around remaining aux_filters
+ * so they're not tripped up by local NL convention
+ */
+ for(p = aux_filters; p->filter; p++) /* count aux_filters */
+ ;
+
+ count = (p - aux_filters) + 3;
+ p = aux = (FILTLIST_S *) fs_get(count * sizeof(FILTLIST_S));
+ memset(p, 0, count * sizeof(FILTLIST_S));
+ p->filter = gf_local_nvtnl;
+ p++;
+ for(; aux_filters->filter; p++, aux_filters++)
+ *p = *aux_filters;
+
+ p->filter = gf_nvtnl_local;
+ }
+
+ if((status = (*ps_global->tools.display_filter)(display_filter, detach_so, pc, aux)) != NULL){
+ snprintf(err_string, sizeof(err_string), "Formatting error: %s", status);
+ rv = 0L;
+ }
+
+ if(aux)
+ fs_give((void **)&aux);
+ }
+ else{ /* just copy it, then */
+ gf_io_t gc;
+
+ gf_set_so_readc(&gc, detach_so);
+ so_seek(detach_so, 0L, 0);
+ gf_filter_init();
+ if(aux_filters){
+ /* if other filters are involved, correct for
+ * newlines on either side of the pipe...
+ */
+ gf_link_filter(gf_local_nvtnl, NULL);
+ for( ; aux_filters->filter ; aux_filters++)
+ gf_link_filter(aux_filters->filter, aux_filters->data);
+
+ gf_link_filter(gf_nvtnl_local, NULL);
+ }
+
+ if((status = gf_pipe(gc, pc)) != NULL){ /* Second pass, sheesh */
+ snprintf(err_string, sizeof(err_string), "Formatting error: %s", status);
+ rv = 0L;
+ }
+
+ gf_clear_so_readc(detach_so);
+ }
+
+ so_give(&detach_so); /* blast temp copy */
+ }
+
+ if(!ps_global->print && we_cancel)
+ cancel_busy_cue(0);
+
+ if (len)
+ *len = rv;
+
+ if(df_trigger_list)
+ blast_trigger_list(&df_trigger_list);
+
+ return((err_string[0] == '\0') ? NULL : err_string);
+}
+
+
+int
+detach_writec(int c)
+{
+ return(so_writec(c, detach_so));
+}
+
+
+/*
+ * build_trigger_list - return possible triggers in a list of triggers
+ * structs
+ */
+TRGR_S *
+build_trigger_list(void)
+{
+ TRGR_S *tp = NULL, **trailp;
+ char **l, *test, *str, *ep, *cmd = NULL;
+ int i;
+
+ trailp = &tp;
+ for(l = ps_global->VAR_DISPLAY_FILTERS ; l && *l; l++){
+ get_pair(*l, &test, &cmd, 1, 1);
+ if(test && valid_filter_command(&cmd)){
+ *trailp = (TRGR_S *) fs_get(sizeof(TRGR_S));
+ (*trailp)->cmp = df_trigger_cmp_text;
+ str = test;
+ if(*test == '_' && (i = strlen(test)) > 10
+ && *(ep = &test[i-1]) == '_' && *--ep == ')'){
+ if(struncmp(test, "_CHARSET(", 9) == 0){
+ fs_give((void **)&test);
+ fs_give((void **)&cmd);
+ fs_give((void **)trailp);
+ continue;
+ }
+
+ if(strncmp(test+1, "LEADING(", 8) == 0){
+ (*trailp)->cmp = df_trigger_cmp_lwsp;
+ *ep = '\0';
+ str = cpystr(test+9);
+ fs_give((void **)&test);
+ }
+ else if(strncmp(test+1, "BEGINNING(", 10) == 0){
+ (*trailp)->cmp = df_trigger_cmp_start;
+ *ep = '\0';
+ str = cpystr(test+11);
+ fs_give((void **)&test);
+ }
+ }
+
+ (*trailp)->text = str;
+ (*trailp)->cmd = cmd;
+ *(trailp = &(*trailp)->next) = NULL;
+ }
+ else{
+ fs_give((void **)&test);
+ fs_give((void **)&cmd);
+ }
+ }
+
+ return(tp);
+}
+
+
+/*
+ * blast_trigger_list - zot any list of triggers we've been using
+ */
+void
+blast_trigger_list(TRGR_S **tlist)
+{
+ if((*tlist)->next)
+ blast_trigger_list(&(*tlist)->next);
+
+ fs_give((void **)&(*tlist)->text);
+ fs_give((void **)&(*tlist)->cmd);
+ fs_give((void **)tlist);
+}
+
+
+/*
+ * df_trigger_cmp - compare the line passed us with the list of defined
+ * display filter triggers
+ */
+int
+df_trigger_cmp(long int n, char *s, LT_INS_S **e, void *l)
+{
+ register TRGR_S *tp;
+ int result;
+
+ if(!display_filter) /* already found? */
+ for(tp = df_trigger_list; tp; tp = tp->next)
+ if(tp->cmp){
+ if((result = (*tp->cmp)(s, tp->text)) < 0)
+ tp->cmp = NULL;
+ else if(result > 0)
+ return(((display_filter = tp->cmd) != NULL) ? 1 : 0);
+ }
+
+ return(0);
+}
+
+
+/*
+ * df_trigger_cmp_text - return 1 if s1 is in s2
+ */
+int
+df_trigger_cmp_text(char *s1, char *s2)
+{
+ return(strstr(s1, s2) != NULL);
+}
+
+
+/*
+ * df_trigger_cmp_lwsp - compare the line passed us with the list of defined
+ * display filter triggers. returns:
+ *
+ * 0 if we don't know yet
+ * 1 if we match
+ * -1 if we clearly don't match
+ */
+int
+df_trigger_cmp_lwsp(char *s1, char *s2)
+{
+ while(*s1 && isspace((unsigned char)*s1))
+ s1++;
+
+ return((*s1) ? (!strncmp(s1, s2, strlen(s2)) ? 1 : -1) : 0);
+}
+
+
+/*
+ * df_trigger_cmp_start - return 1 if first strlen(s2) chars start s1
+ */
+int
+df_trigger_cmp_start(char *s1, char *s2)
+{
+ return(!strncmp(s1, s2, strlen(s2)));
+}
+
+
+/*
+ * valid_filter_command - make sure argv[0] of command really exists.
+ * "cmd" is required to be an alloc'd string since
+ * it will get realloc'd if the command's path is
+ * expanded.
+ */
+int
+valid_filter_command(char **cmd)
+{
+ int i;
+ char cpath[MAXPATH+1], *p;
+
+ if(!(cmd && *cmd))
+ return(FALSE);
+
+ /*
+ * copy cmd to build expanded path if necessary.
+ */
+ for(i = 0; i < sizeof(cpath) && (cpath[i] = (*cmd)[i]); i++)
+ if(isspace((unsigned char)(*cmd)[i])){
+ cpath[i] = '\0'; /* tie off command's path*/
+ break;
+ }
+
+#if defined(DOS) || defined(OS2)
+ if(is_absolute_path(cpath)){
+ size_t l;
+
+ fixpath(cpath, sizeof(cpath));
+ l = strlen(cpath) + strlen(&(*cmd)[i]);
+ p = (char *) fs_get((l+1) * sizeof(char));
+ strncpy(p, cpath, l); /* copy new path */
+ p[l] = '\0';
+ strncat(p, &(*cmd)[i], l+1-1-strlen(p)); /* and old args */
+ p[l] = '\0';
+ fs_give((void **) cmd); /* free it */
+ *cmd = p; /* and assign new buf */
+ }
+#else
+ if(cpath[0] == '~'){
+ if(fnexpand(cpath, sizeof(cpath))){
+ size_t l;
+
+ l = strlen(cpath) + strlen(&(*cmd)[i]);
+ p = (char *) fs_get((l+1) * sizeof(char));
+ strncpy(p, cpath, l); /* copy new path */
+ p[l] = '\0';
+ strncat(p, &(*cmd)[i], l+1-1-strlen(p)); /* and old args */
+ p[l] = '\0';
+ fs_give((void **) cmd); /* free it */
+ *cmd = p; /* and assign new buf */
+ }
+ else
+ return(FALSE);
+ }
+#endif
+
+ return(is_absolute_path(cpath) && can_access(cpath, EXECUTE_ACCESS) == 0);
+}
+
+
+void
+fetch_readc_init(FETCH_READC_S *frd, MAILSTREAM *stream, long int msgno,
+ char *section, unsigned long size, long partial, long int flags)
+{
+ int nointr = 0;
+
+ nointr = flags & DT_NOINTR;
+ flags &= ~DT_NOINTR;
+
+ memset(g_fr_desc = frd, 0, sizeof(FETCH_READC_S));
+ frd->stream = stream;
+ frd->msgno = msgno;
+ frd->section = section;
+ frd->flags = flags;
+ frd->size = size;
+ frd->readc = fetch_readc;
+
+#ifdef SMIME
+ /*
+ * The call to imap_cache below will return true in the case where
+ * we've already stashed fake data in the content of the part.
+ * This happens when an S/MIME message is decrypted.
+ */
+#endif
+
+ if(modern_imap_stream(stream)
+ && !imap_cache(stream, msgno, section, NULL, NULL)
+ && (size > INIT_FETCH_CHUNK || (partial > 0L && partial < size))
+ && (F_OFF(F_QUELL_PARTIAL_FETCH, ps_global)
+ ||
+#ifdef _WINDOWS
+ F_ON(F_QUELL_SSL_LARGEBLOCKS, ps_global)
+#else
+ 0
+#endif
+ )){
+
+ if(partial > 0L && partial < size){
+ /* partial fetch is being asked for */
+ frd->size = partial;
+ }
+
+ frd->allocsize = MIN(INIT_FETCH_CHUNK,frd->size);
+ frd->chunk = (char *) fs_get ((frd->allocsize + 1) * sizeof(char));
+ frd->chunksize = frd->allocsize/2; /* this gets doubled 1st time */
+ frd->endp = frd->chunk;
+ frd->free_me = 1;
+
+ if(!nointr)
+ if(intr_handling_on())
+ frd->we_turned_on = 1;
+
+ if(!(partial > 0L && partial < size)){
+ frd->cache = so_get(CharStar, NULL, EDIT_ACCESS);
+ so_truncate(frd->cache, size); /* pre-allocate */
+ }
+ }
+ else{ /* fetch the whole bloody thing here */
+ frd->chunk = mail_fetch_body(stream, msgno, section, &frd->read, flags);
+
+ /* This only happens if the server gave us a bogus size */
+ if(partial > 0L && partial < size){
+ /* partial fetch is being asked for */
+ frd->size = partial;
+ frd->endp = &frd->chunk[frd->size];
+ }
+ else if(size != frd->read){
+ dprint((1,
+ "fetch_readc_init: size mismatch: size=%lu read=%lu, continue...\n",
+ frd->size, frd->read));
+ q_status_message(SM_ORDER | SM_DING, 0, 3,
+ _("Message size does not match expected size, continuing..."));
+ frd->size = MIN(size, frd->read);
+ frd->endp = &frd->chunk[frd->read];
+ }
+ else
+ frd->endp = &frd->chunk[frd->read];
+ }
+
+ frd->chunkp = frd->chunk;
+}
+
+
+int
+fetch_readc_cleanup(void)
+{
+ if(g_fr_desc){
+ if(g_fr_desc->we_turned_on)
+ intr_handling_off();
+
+ if(g_fr_desc->chunk && g_fr_desc->free_me)
+ fs_give((void **) &g_fr_desc->chunk);
+
+ if(g_fr_desc->cache){
+ SIZEDTEXT text;
+
+ text.size = g_fr_desc->size;
+ text.data = (unsigned char *) so_text(g_fr_desc->cache);
+ imap_cache(g_fr_desc->stream, g_fr_desc->msgno,
+ g_fr_desc->section, NULL, &text);
+ g_fr_desc->cache->txt = (void *) NULL;
+ so_give(&g_fr_desc->cache);
+ }
+ }
+
+ return(0);
+}
+
+
+char *
+fetch_gets(readfn_t f, void *stream, long unsigned int size, GETS_DATA *md)
+{
+ unsigned long n;
+
+ n = MIN(g_fr_desc->chunksize, size);
+ g_fr_desc->read += n;
+ g_fr_desc->endp = &g_fr_desc->chunk[n];
+
+ (*f) (stream, n, g_fr_desc->chunkp = g_fr_desc->chunk);
+
+ if(g_fr_desc->cache)
+ so_nputs(g_fr_desc->cache, g_fr_desc->chunk, (long) n);
+
+ /* BUG: need to read requested "size" in case it's larger than chunk? */
+
+ return(NULL);
+}
+
+
+int
+fetch_readc(unsigned char *c)
+{
+ extern void gf_error(char *);
+
+ if(ps_global->intr_pending){
+ (void) fetch_readc_cleanup();
+ /* TRANSLATORS: data transfer was interrupted by something */
+ gf_error(g_fr_desc->error ? g_fr_desc->error :_("Transfer interrupted!"));
+ /* no return */
+ }
+ else if(g_fr_desc->chunkp == g_fr_desc->endp){
+
+ /* Anything to read, do it */
+ if(g_fr_desc->read < g_fr_desc->size){
+ void *old_gets;
+ int rv;
+ TIMEVAL_S before, after;
+ long diff, wdiff;
+ unsigned long save_read;
+
+ old_gets = mail_parameters(g_fr_desc->stream, GET_GETS,
+ (void *)NULL);
+ mail_parameters(g_fr_desc->stream, SET_GETS, (void *) fetch_gets);
+
+ /*
+ * Adjust chunksize with the goal that it will be about
+ * TARGET_INTR_TIME useconds +- 20%
+ * to finish the partial fetch. We want that time
+ * to be small so that interrupts will happen fast, but we want
+ * the chunksize large so that the whole fetch will happen
+ * fast. So it's a tradeoff between those two things.
+ *
+ * If the estimated fetchtime is getting too large, we
+ * half the chunksize. If it is small, we double
+ * the chunksize. If it is in between, we leave it. There is
+ * some risk of oscillating between two values, but who cares?
+ */
+ if(g_fr_desc->fetchtime <
+ TARGET_INTR_TIME - TARGET_INTR_TIME/5)
+ g_fr_desc->chunksize *= 2;
+ else if(g_fr_desc->fetchtime >
+ TARGET_INTR_TIME + TARGET_INTR_TIME/5)
+ g_fr_desc->chunksize /= 2;
+
+ g_fr_desc->chunksize = MIN(MAX_FETCH_CHUNK,
+ MAX(MIN_FETCH_CHUNK,
+ g_fr_desc->chunksize));
+
+#ifdef _WINDOWS
+ /*
+ * If this feature is set, limit the max size to less than
+ * 16K - 5, the magic number that avoids Microsoft's bug.
+ * Let's just go with 12K instead of 16K - 5.
+ */
+ if(F_ON(F_QUELL_SSL_LARGEBLOCKS, ps_global))
+ g_fr_desc->chunksize =
+ MIN(AVOID_MICROSOFT_SSL_CHUNKING_BUG, g_fr_desc->chunksize);
+#endif
+
+ /* don't ask for more than there should be left to ask for */
+ g_fr_desc->chunksize =
+ MIN(g_fr_desc->size - g_fr_desc->read, g_fr_desc->chunksize);
+
+ /*
+ * If chunksize grew, reallocate chunk.
+ */
+ if(g_fr_desc->chunksize > g_fr_desc->allocsize){
+ g_fr_desc->allocsize = g_fr_desc->chunksize;
+ fs_give((void **) &g_fr_desc->chunk);
+ g_fr_desc->chunk = (char *) fs_get ((g_fr_desc->allocsize + 1)
+ * sizeof(char));
+ g_fr_desc->endp = g_fr_desc->chunk;
+ g_fr_desc->chunkp = g_fr_desc->chunk;
+ }
+
+ save_read = g_fr_desc->read;
+ (void)get_time(&before);
+
+ rv = mail_partial_body(g_fr_desc->stream, g_fr_desc->msgno,
+ g_fr_desc->section, g_fr_desc->read,
+ g_fr_desc->chunksize, g_fr_desc->flags);
+
+ /*
+ * If the amount we actually read is less than the amount we
+ * asked for we assume that is because the server gave us a
+ * bogus size when we originally asked for it.
+ */
+ if(g_fr_desc->chunksize > (g_fr_desc->read - save_read)){
+ dprint((1,
+ "partial_body returned less than asked for: asked=%lu got=%lu, continue...\n",
+ g_fr_desc->chunksize, g_fr_desc->read - save_read));
+ if(g_fr_desc->read - save_read > 0)
+ q_status_message(SM_ORDER | SM_DING, 0, 3,
+ _("Message size does not match expected size, continuing..."));
+ else{
+ rv = 0;
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("Server returns zero bytes, Quell-Partial-Fetch feature may help"));
+ }
+
+ g_fr_desc->size = g_fr_desc->read;
+ }
+
+ if(get_time(&after) == 0){
+ diff = time_diff(&after, &before);
+ wdiff = MIN(TARGET_INTR_TIME + TARGET_INTR_TIME/2,
+ MAX(TARGET_INTR_TIME - TARGET_INTR_TIME/2, diff));
+ /*
+ * Fetchtime is an exponentially weighted average of the number
+ * of usecs it takes to do a single fetch of whatever the
+ * current chunksize is. Since the fetch time probably isn't
+ * simply proportional to the chunksize, we don't try to
+ * calculate a chunksize by keeping track of the bytes per
+ * second. Instead, we just double or half the chunksize if
+ * we are too fast or too slow. That happens the next time
+ * through the loop a few lines up.
+ * Too settle it down a bit, Windsorize the mean.
+ */
+ g_fr_desc->fetchtime = (g_fr_desc->fetchtime == 0)
+ ? wdiff
+ : g_fr_desc->fetchtime/2 + wdiff/2;
+ dprint((8,
+ "fetch: diff=%ld wdiff=%ld fetchave=%ld prev chunksize=%ld\n",
+ diff, wdiff, g_fr_desc->fetchtime, g_fr_desc->chunksize));
+ }
+ else /* just set it so it won't affect anything */
+ g_fr_desc->fetchtime = TARGET_INTR_TIME;
+
+ /* UNinstall mailgets */
+ mail_parameters(g_fr_desc->stream, SET_GETS, old_gets);
+
+ if(!rv){
+ (void) fetch_readc_cleanup();
+ gf_error("Partial fetch failed!");
+ /* no return */
+ }
+ }
+ else /* clean up and return done. */
+ return(fetch_readc_cleanup());
+ }
+
+ *c = *g_fr_desc->chunkp++;
+
+ return(1);
+}
diff --git a/pith/detach.h b/pith/detach.h
new file mode 100644
index 00000000..b6cdaec0
--- /dev/null
+++ b/pith/detach.h
@@ -0,0 +1,69 @@
+/*
+ * $Id: detach.h 1025 2008-04-08 22:59:38Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_DETACH_INCLUDED
+#define PITH_DETACH_INCLUDED
+
+
+#include "../pith/filttype.h"
+#include "../pith/store.h"
+
+
+/*
+ * Data used to keep track of partial fetches...
+ */
+typedef struct _fetch_read {
+ unsigned free_me:1;
+ unsigned we_turned_on:1;
+ MAILSTREAM *stream; /* stream of open mailbox */
+ unsigned long msgno; /* message number within mailbox */
+ char *section, /* MIME section within message */
+ *chunk, /* block of partial fetched data */
+ *chunkp, /* pointer to next char in block */
+ *endp, /* cell past last char in block */
+ *error; /* Error message to report */
+ unsigned long read, /* bytes read so far */
+ size, /* total bytes to read */
+ chunksize, /* size of chunk block */
+ allocsize; /* allocated size of chunk block */
+ long flags, /* flags to use fetching block */
+ fetchtime; /* usecs avg per chunk fetch */
+ gf_io_t readc;
+ STORE_S *cache;
+} FETCH_READC_S;
+
+
+extern FETCH_READC_S *g_fr_desc;
+
+#define AVOID_MICROSOFT_SSL_CHUNKING_BUG ((unsigned long)(12 * 1024L))
+
+
+/*
+ * This lazily gets combined with FT_ flags from c-client so make
+ * it different from all those possible values.
+ */
+#define DT_NODFILTER (long) 0x10000
+#define DT_NOINTR (long) 0x20000
+
+
+/* exported protoypes */
+char *detach_raw(MAILSTREAM *, long, char *, gf_io_t, int);
+char *detach(MAILSTREAM *, long, char *, long, long *, gf_io_t, FILTLIST_S *, long);
+int valid_filter_command(char **);
+void fetch_readc_init(FETCH_READC_S *, MAILSTREAM *, long, char *,
+ unsigned long, long, long);
+
+#endif /* PITH_DETACH_INCLUDED */
diff --git a/pith/detoken.c b/pith/detoken.c
new file mode 100644
index 00000000..6f0584ab
--- /dev/null
+++ b/pith/detoken.c
@@ -0,0 +1,567 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: detoken.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/detoken.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/status.h"
+#include "../pith/pattern.h"
+#include "../pith/reply.h"
+#include "../pith/mailindx.h"
+#include "../pith/options.h"
+
+
+/*
+ * Hook to read signature from local file
+ */
+char *(*pith_opt_get_signature_file)(char *, int, int, int);
+
+
+/*
+ * Internal prototypes
+ */
+char *detoken_guts(char *, int, ENVELOPE *, ACTION_S *, REDRAFT_POS_S **, int, int *);
+char *handle_if_token(char *, char *, int, ENVELOPE *, ACTION_S *, char **);
+char *get_token_arg(char *, char **);
+
+
+/*
+ * Detokenize signature or template files.
+ *
+ * If is_sig, we always use literal sigs before sigfiles if they are
+ * defined. So, check for role->litsig and use it. If it doesn't exist, use
+ * the global literal sig if defined. Else the role->sig file or the
+ * global signature file.
+ *
+ * If !is_sig, use role->template.
+ *
+ * So we start with a literal signature or a signature or template file.
+ * If that's a file, we read it first. The file could be remote.
+ * Then we detokenize the literal signature or file contents and return
+ * an allocated string which the caller frees.
+ *
+ * Args role -- See above about what happens depending on is_sig.
+ * relative to the pinerc dir.
+ * env -- The envelope to use for detokenizing. May be NULL.
+ * prenewlines -- How many blank lines should be included at start.
+ * postnewlines -- How many blank lines should be included after.
+ * is_sig -- This is a signature (not a template file).
+ * redraft_pos -- This is a return value. If it is non-NULL coming in,
+ * then the cursor position is returned here.
+ * impl -- This is a combination argument which is both an input
+ * argument and a return value. If it is non-NULL and = 0,
+ * that means that we want the cursor position returned here,
+ * even if that position is set implicitly to the end of
+ * the output string. If it is = 1 coming in, that means
+ * we only want the cursor position to be set if it is set
+ * explicitly. If it is 2, or if redraft_pos is NULL,
+ * we don't set it at all.
+ * If the cursor position gets set explicitly by a
+ * _CURSORPOS_ token in the file then this is set to 2
+ * on return. If the cursor position is set implicitly to
+ * the end of the included file, then this is set to 1
+ * on return.
+ *
+ * Returns -- An allocated string is returned.
+ */
+char *
+detoken(ACTION_S *role, ENVELOPE *env, int prenewlines, int postnewlines,
+ int is_sig, REDRAFT_POS_S **redraft_pos, int *impl)
+{
+ char *ret = NULL,
+ *src = NULL,
+ *literal_sig = NULL,
+ *sigfile = NULL;
+
+ if(is_sig){
+ /*
+ * If role->litsig is set, we use it;
+ * Else, if VAR_LITERAL_SIG is set, we use that;
+ * Else, if role->sig is set, we use that;
+ * Else, if VAR_SIGNATURE_FILE is set, we use that.
+ * This can be a little surprising if you set the VAR_LITERAL_SIG
+ * and don't set a role->litsig but do set a role->sig. The
+ * VAR_LITERAL_SIG will be used, not role->sig. The reason for this
+ * is mostly that it is much easier to display the right stuff
+ * in the various config screens if we do it that way. Besides,
+ * people will typically use only literal sigs or only sig files,
+ * there is no reason to mix them, so we don't provide support to
+ * do so.
+ */
+ if(role && role->litsig)
+ literal_sig = role->litsig;
+ else if(ps_global->VAR_LITERAL_SIG)
+ literal_sig = ps_global->VAR_LITERAL_SIG;
+ else if(role && role->sig)
+ sigfile = role->sig;
+ else
+ sigfile = ps_global->VAR_SIGNATURE_FILE;
+ }
+ else if(role && role->template)
+ sigfile = role->template;
+
+ if(literal_sig)
+ src = get_signature_lit(literal_sig, prenewlines, postnewlines, is_sig,1);
+ else if(sigfile && pith_opt_get_signature_file)
+ src = (*pith_opt_get_signature_file)(sigfile, prenewlines, postnewlines, is_sig);
+
+ if(src){
+ if(*src)
+ ret = detoken_src(src, FOR_TEMPLATE, env, role, redraft_pos, impl);
+
+ fs_give((void **)&src);
+ }
+
+ return(ret);
+}
+
+
+/*
+ * Filter the source string from the template file and return an allocated
+ * copy of the result with text replacements for the tokens.
+ * Fill in offset in redraft_pos.
+ *
+ * This is really inefficient but who cares? It's just cpu time.
+ */
+char *
+detoken_src(char *src, int for_what, ENVELOPE *env, ACTION_S *role,
+ REDRAFT_POS_S **redraft_pos, int *impl)
+{
+ int loopcnt = 25; /* just in case, avoid infinite loop */
+ char *ret, *str1, *str2;
+ int done = 0;
+
+ if(!src)
+ return(src);
+
+ /*
+ * We keep running it through until it stops changing so user can
+ * nest calls to token stuff.
+ */
+ str1 = src;
+ do {
+ /* short-circuit if no chance it will change */
+ if(strindex(str1, '_'))
+ str2 = detoken_guts(str1, for_what, env, role, NULL, 0, NULL);
+ else
+ str2 = str1;
+
+ if(str1 && str2 && (str1 == str2 || !strcmp(str1, str2))){
+ done++; /* It stopped changing */
+ if(str1 && str1 != src && str1 != str2)
+ fs_give((void **)&str1);
+ }
+ else{ /* Still changing */
+ if(str1 && str1 != src && str1 != str2)
+ fs_give((void **)&str1);
+
+ str1 = str2;
+ }
+
+ } while(str2 && !done && loopcnt-- > 0);
+
+ /*
+ * Have to run it through once more to get the redraft_pos and
+ * to remove any backslash escape for a token.
+ */
+ if((str2 && strindex(str2, '_')) ||
+ (impl && *impl == 0 && redraft_pos && !*redraft_pos)){
+ ret = detoken_guts(str2, for_what, env, role, redraft_pos, 1, impl);
+ if(str2 != src)
+ fs_give((void **)&str2);
+ }
+ else if(str2){
+ if(str2 == src)
+ ret = cpystr(str2);
+ else
+ ret = str2;
+ }
+
+ return(ret);
+}
+
+
+/*
+ * The guts of the detokenizing routines. Filter the src string looking for
+ * tokens and replace them with the appropriate text. In the case of the
+ * cursor_pos token we set redraft_pos instead.
+ *
+ * Args src -- The source string
+ * for_what --
+ * env -- Envelope to look in for token replacements.
+ * redraft_pos -- Return the redraft offset here, if non-zero.
+ * last_pass -- This is a flag to tell detoken_guts whether or not to do
+ * the replacement for _CURSORPOS_. Leave it as is until
+ * the last pass. We need this because we want to defer
+ * cursor placement until the very last call to detoken,
+ * otherwise we'd have to keep track of the cursor
+ * position as subsequent text replacements (nested)
+ * take place.
+ * This same flag is also used to decide when to eliminate
+ * backslash escapes from in front of tokens. The only
+ * use of backslash escapes is to escape an entire token.
+ * That is, \_DATE_ is a literal _DATE_, but any other
+ * backslash is a literal backslash. That way, nobody
+ * but wackos will have to worry about backslashes.
+ * impl -- This is a combination argument which is both an input
+ * argument and a return value. If it is non-NULL and 0
+ * coming in, that means that we should set redraft_pos,
+ * even if that position is set implicitly to the end of
+ * the output string. If it is 1 coming in, that means
+ * we only want the cursor position to be set if it is set
+ * explicitly. If it is 2 coming in (or if
+ * redraft_pos is NULL) then we don't set it at all.
+ * If the cursor position gets set explicitly by a
+ * _CURSORPOS_ token in the file then this is set to 2
+ * on return. If the cursor position is set implicitly to
+ * the end of the included file, then this is set to 1
+ * on return.
+ *
+ * Returns pointer to alloced result
+ */
+char *
+detoken_guts(char *src, int for_what, ENVELOPE *env, ACTION_S *role,
+ REDRAFT_POS_S **redraft_pos, int last_pass, int *impl)
+{
+#define MAXSUB 500
+ char *p, *q = NULL, *dst = NULL;
+ char subbuf[MAXSUB+1], *repl;
+ INDEX_PARSE_T *pt;
+ long l, cnt = 0L;
+ int sizing_pass = 1, suppress_tokens = 0;
+
+ if(!src)
+ return(NULL);
+
+top:
+
+ /*
+ * The tokens we look for begin with _. The only escaping mechanism
+ * is a backslash in front of a token. This will give you the literal
+ * token. So \_DATE_ is a literal _DATE_.
+ * Tokens like _word_ are replaced with the appropriate text if
+ * word is recognized. If _word_ is followed immediately by a left paren
+ * it is an if-else thingie. _word_(match_this,if_text,else_text) means to
+ * replace that with either the if_text or else_text depending on whether
+ * what _word_ (without the paren) would produce matches match_this or not.
+ */
+ p = src;
+ while(*p){
+ switch(*p){
+ case '_': /* possible start of token */
+ if(!suppress_tokens &&
+ (pt = itoktype(p+1, for_what | DELIM_USCORE)) != NULL){
+ char *free_this = NULL;
+
+ p += (strlen(pt->name) + 2); /* skip over token */
+
+ repl = subbuf;
+ subbuf[0] = '\0';
+
+ if(pt->ctype == iCursorPos){
+ if(!last_pass){ /* put it back */
+ subbuf[0] = '_';
+ strncpy(subbuf+1, pt->name, sizeof(subbuf)-2);
+ subbuf[sizeof(subbuf)-1] = '\0';
+ strncat(subbuf, "_", sizeof(subbuf)-strlen(subbuf)-1);
+ subbuf[sizeof(subbuf)-1] = '\0';
+ }
+
+ if(!sizing_pass){
+ if(q-dst < cnt+1)
+ *q = '\0';
+
+ l = strlen(dst);
+ if(redraft_pos && impl && *impl != 2){
+ if(!*redraft_pos){
+ *redraft_pos =
+ (REDRAFT_POS_S *)fs_get(sizeof(**redraft_pos));
+ memset((void *)*redraft_pos, 0,
+ sizeof(**redraft_pos));
+ (*redraft_pos)->hdrname = cpystr(":");
+ }
+
+ (*redraft_pos)->offset = l;
+ *impl = 2; /* set explicitly */
+ }
+ }
+ }
+ else if(pt->what_for & FOR_REPLY_INTRO)
+ repl = get_reply_data(env, role, pt->ctype,
+ subbuf, sizeof(subbuf)-1);
+
+ if(*p == LPAREN){ /* if-else construct */
+ char *skip_ahead;
+
+ repl = free_this = handle_if_token(repl, p, for_what,
+ env, role,
+ &skip_ahead);
+ p = skip_ahead;
+ }
+
+ if(repl && repl[0]){
+ if(sizing_pass)
+ cnt += (long)strlen(repl);
+ else{
+ strncpy(q, repl, cnt-(q-dst));
+ dst[cnt] = '\0';
+ q += strlen(repl);
+ }
+ }
+
+ if(free_this)
+ fs_give((void **)&free_this);
+ }
+ else{ /* unrecognized token, treat it just like text */
+ suppress_tokens = 0;
+ if(sizing_pass)
+ cnt++;
+ else if(q-dst < cnt+1)
+ *q++ = *p;
+
+ p++;
+ }
+
+ break;
+
+ case BSLASH:
+ /*
+ * If a real token follows the backslash, then the backslash
+ * is here to escape the token. Otherwise, it's just a
+ * regular character.
+ */
+ if(*(p+1) == '_' &&
+ ((pt = itoktype(p+2, for_what | DELIM_USCORE)) != NULL)){
+ /*
+ * Backslash is escape for literal token.
+ * If we're on the last pass we want to eliminate the
+ * backslash, otherwise we keep it.
+ * In either case, suppress_tokens will cause the token
+ * lookup to be skipped above so that the token will
+ * be treated as literal text.
+ */
+ suppress_tokens++;
+ if(last_pass){
+ p++;
+ break;
+ }
+ /* else, fall through and keep backslash */
+ }
+ /* this is a literal backslash, fall through */
+
+ default:
+ if(sizing_pass)
+ cnt++;
+ else if(q-dst < cnt+1)
+ *q++ = *p; /* copy the character */
+
+ p++;
+ break;
+ }
+ }
+
+ if(!sizing_pass && q-dst < cnt+1)
+ *q = '\0';
+
+ if(sizing_pass){
+ sizing_pass = 0;
+ /*
+ * Now we're done figuring out how big the answer will be. We
+ * allocate space for it and go back through filling it in.
+ */
+ cnt = MAX(cnt, 0L);
+ q = dst = (char *)fs_get((cnt + 1) * sizeof(char));
+ goto top;
+ }
+
+ /*
+ * Set redraft_pos to character following the template, unless
+ * it has already been set.
+ */
+ if(dst && impl && *impl == 0 && redraft_pos && !*redraft_pos){
+ *redraft_pos = (REDRAFT_POS_S *)fs_get(sizeof(**redraft_pos));
+ memset((void *)*redraft_pos, 0, sizeof(**redraft_pos));
+ (*redraft_pos)->offset = strlen(dst);
+ (*redraft_pos)->hdrname = cpystr(":");
+ *impl = 1;
+ }
+
+ if(dst && cnt >= 0)
+ dst[cnt] = '\0';
+
+ return(dst);
+}
+
+
+/*
+ * Do the if-else part of the detokenization for one case of if-else.
+ * The input src should like (match_this, if_matched, else)...
+ *
+ * Args expands_to -- This is what the token to the left of the paren
+ * expanded to, and this is the thing we're going to
+ * compare with the match_this part.
+ * src -- The source string beginning with the left paren.
+ * for_what --
+ * env --
+ * skip_ahead -- Tells caller how long the (...) part was so caller can
+ * skip over that part of the source.
+ *
+ * Returns -- an allocated string which is the answer, or NULL if nothing.
+ */
+char *
+handle_if_token(char *expands_to, char *src, int for_what, ENVELOPE *env,
+ ACTION_S *role, char **skip_ahead)
+{
+ char *ret = NULL;
+ char *skip_to;
+ char *match_this, *if_matched, *else_part;
+
+ if(skip_ahead)
+ *skip_ahead = src;
+
+ if(!src || *src != LPAREN){
+ dprint((1,"botch calling handle_if_token, missing paren\n"));
+ return(ret);
+ }
+
+ if(!*++src){
+ q_status_message(SM_ORDER, 3, 3,
+ "Unexpected end of token string in Reply-LeadIn, Sig, or template");
+ return(ret);
+ }
+
+ match_this = get_token_arg(src, &skip_to);
+
+ /*
+ * If the match_this argument is a token, detokenize it first.
+ */
+ if(match_this && *match_this == '_'){
+ char *exp_match_this;
+
+ exp_match_this = detoken_src(match_this, for_what, env,
+ role, NULL, NULL);
+ fs_give((void **)&match_this);
+ match_this = exp_match_this;
+ }
+
+ if(!match_this)
+ match_this = cpystr("");
+
+ if(!expands_to)
+ expands_to = "";
+
+ src = skip_to;
+ while(src && *src && (isspace((unsigned char)*src) || *src == ','))
+ src++;
+
+ if_matched = get_token_arg(src, &skip_to);
+ src = skip_to;
+ while(src && *src && (isspace((unsigned char)*src) || *src == ','))
+ src++;
+
+ else_part = get_token_arg(src, &skip_to);
+ src = skip_to;
+ while(src && *src && *src != RPAREN)
+ src++;
+
+ if(src && *src == RPAREN)
+ src++;
+
+ if(skip_ahead)
+ *skip_ahead = src;
+
+ if(!strcmp(match_this, expands_to)){
+ ret = if_matched;
+ if(else_part)
+ fs_give((void **)&else_part);
+ }
+ else{
+ ret = else_part;
+ if(if_matched)
+ fs_give((void **)&if_matched);
+ }
+
+ fs_give((void **)&match_this);
+
+ return(ret);
+}
+
+
+char *
+get_token_arg(char *src, char **skip_to)
+{
+ int quotes = 0, done = 0;
+ char *ret = NULL, *p;
+
+ while(*src && isspace((unsigned char)*src)) /* skip space before string */
+ src++;
+
+ if(*src == RPAREN){
+ if(skip_to)
+ *skip_to = src;
+
+ return(ret);
+ }
+
+ p = ret = (char *)fs_get((strlen(src) + 1) * sizeof(char));
+ while(!done){
+ switch(*src){
+ case QUOTE:
+ if(++quotes == 2)
+ done++;
+
+ src++;
+ break;
+
+ case BSLASH: /* don't count \" as a quote, just copy */
+ if(*(src+1) == BSLASH || *(src+1) == QUOTE){
+ src++; /* skip backslash */
+ *p++ = *src++;
+ }
+ else
+ src++;
+
+ break;
+
+ case SPACE:
+ case TAB:
+ case RPAREN:
+ case COMMA:
+ if(quotes)
+ *p++ = *src++;
+ else
+ done++;
+
+ break;
+
+ case '\0':
+ done++;
+ break;
+
+ default:
+ *p++ = *src++;
+ break;
+ }
+ }
+
+ *p = '\0';
+ if(skip_to)
+ *skip_to = src;
+
+ return(ret);
+}
diff --git a/pith/detoken.h b/pith/detoken.h
new file mode 100644
index 00000000..cf7f34ea
--- /dev/null
+++ b/pith/detoken.h
@@ -0,0 +1,29 @@
+/*
+ * $Id: detoken.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_DETOKEN_INCLUDED
+#define PITH_DETOKEN_INCLUDED
+
+
+#include "../pith/pattern.h"
+#include "../pith/repltype.h"
+
+
+/* exported protoypes */
+char *detoken(ACTION_S *, ENVELOPE *, int, int, int, REDRAFT_POS_S **, int *);
+char *detoken_src(char *, int, ENVELOPE *, ACTION_S *, REDRAFT_POS_S **, int *);
+
+
+#endif /* PITH_DETOKEN_INCLUDED */
diff --git a/pith/editorial.c b/pith/editorial.c
new file mode 100644
index 00000000..a20d66ae
--- /dev/null
+++ b/pith/editorial.c
@@ -0,0 +1,198 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: editorial.c 768 2007-10-24 00:10:03Z hubert@u.washington.edu $";
+#endif
+
+/* ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+
+ editorial.c
+ Implements editorial text insertion/formatting
+
+ ====*/
+
+
+#include "../pith/headers.h"
+#include "../pith/conf.h"
+#include "../pith/state.h"
+#include "../pith/margin.h"
+#include "../pith/filter.h"
+#include "../pith/handle.h"
+#include "../pith/mailview.h"
+#include "../pith/editorial.h"
+
+/*
+ * Internal prototypes
+ */
+int quote_editorial(long, char *, LT_INS_S **, void *);
+
+
+/*
+ * Struct to help with editorial comment insertion
+ */
+#define EDITORIAL_MAX 128
+typedef struct _editorial_s {
+ char prefix[EDITORIAL_MAX];
+ int prelen;
+ char postfix[EDITORIAL_MAX];
+ int postlen;
+ int do_color;
+} EDITORIAL_S;
+
+
+char *
+format_editorial(char *s, int width, int flags, HANDLE_S **handlesp, gf_io_t pc)
+{
+ gf_io_t gc;
+ int *margin;
+ EDITORIAL_S es;
+ URL_HILITE_S uh;
+
+ /* ASSUMPTION #2,341: All MIME-decoding is done by now */
+ gf_set_readc(&gc, s, strlen(s), CharStar, 0);
+
+ margin = format_view_margin();
+ if(flags & FM_NOINDENT)
+ margin[0] = margin[1] = 0;
+
+ /* safety net */
+ if(width - (margin[0] + margin[1]) < 5){
+ margin[0] = margin[1] = 0;
+ if(width < 5)
+ width = 80;
+ }
+
+ width -= (margin[0] + margin[1]);
+
+ if(width > 40){
+ width -= 12;
+
+ es.prelen = MAX(2, MIN(margin[0] + 6, sizeof(es.prefix) - 3));
+ snprintf(es.prefix, sizeof(es.prefix), "%s[ ", repeat_char(es.prelen - 2, ' '));
+ es.postlen = 2;
+ strncpy(es.postfix, " ]", sizeof(es.postfix));
+ es.postfix[sizeof(es.postfix)-1] = '\0';
+ }
+ else if(width > 20){
+ width -= 6;
+
+ es.prelen = MAX(2, MIN(margin[0] + 3, sizeof(es.prefix) - 3));
+ snprintf(es.prefix, sizeof(es.prefix), "%s[ ", repeat_char(es.prelen - 2, ' '));
+ es.postlen = 2;
+ strncpy(es.postfix, " ]", sizeof(es.postfix));
+ es.postfix[sizeof(es.postfix)-1] = '\0';
+ }
+ else{
+ width -= 2;
+ strncpy(es.prefix, "[", sizeof(es.prefix));
+ es.prefix[sizeof(es.prefix)-1] = '\0';
+ strncpy(es.postfix, "]", sizeof(es.postfix));
+ es.postfix[sizeof(es.postfix)-1] = '\0';
+ es.prelen = 1;
+ es.postlen = 1;
+ }
+
+ es.do_color = (!(flags & FM_NOCOLOR) && (flags & FM_DISPLAY) && pico_usingcolor());
+
+ gf_filter_init();
+
+ /* catch urls */
+ if((F_ON(F_VIEW_SEL_URL, ps_global)
+ || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
+ || F_ON(F_SCAN_ADDR, ps_global))
+ && handlesp){
+ gf_link_filter(gf_line_test,
+ gf_line_test_opt(url_hilite,
+ gf_url_hilite_opt(&uh,handlesp,0)));
+ }
+
+ gf_link_filter(gf_wrap, gf_wrap_filter_opt(width, width, NULL, 0,
+ (handlesp ? GFW_HANDLES : GFW_NONE)));
+ gf_link_filter(gf_line_test, gf_line_test_opt(quote_editorial, &es));
+
+ /* If not for display, change to local end of line */
+ if(!(flags & FM_DISPLAY))
+ gf_link_filter(gf_nvtnl_local, NULL);
+
+ return(gf_pipe(gc, pc));
+}
+
+
+int
+quote_editorial(long int linenum, char *line, LT_INS_S **ins, void *local)
+{
+ COLOR_PAIR *col = NULL;
+
+ ins = gf_line_test_new_ins(ins, line,
+ ((EDITORIAL_S *)local)->prefix,
+ ((EDITORIAL_S *)local)->prelen);
+ if(((EDITORIAL_S *)local)->do_color
+ && ps_global->VAR_METAMSG_FORE_COLOR
+ && ps_global->VAR_METAMSG_BACK_COLOR
+ && (col = new_color_pair(ps_global->VAR_METAMSG_FORE_COLOR,
+ ps_global->VAR_METAMSG_BACK_COLOR))){
+ if(!pico_is_good_colorpair(col))
+ free_color_pair(&col);
+
+ if(col){
+ char *p;
+ char normal_embed[(2 * RGBLEN) + 5];
+ char quote_color_embed[(2 * RGBLEN) + 5];
+
+ strncpy(quote_color_embed,
+ color_embed(col->fg, col->bg),
+ sizeof(quote_color_embed));
+ quote_color_embed[sizeof(quote_color_embed)-1] = '\0';
+
+ ins = gf_line_test_new_ins(ins, line,
+ quote_color_embed, (2 * RGBLEN) + 4);
+
+ /*
+ * If there was already a color change back to normal color
+ * in the line that was passed in, then instead of allowing
+ * that color change back to normal we want to change that
+ * to a color change back to our METAMSG color instead.
+ * Search line for that and modify it.
+ */
+
+ strncpy(normal_embed,
+ color_embed(ps_global->VAR_NORM_FORE_COLOR,
+ ps_global->VAR_NORM_BACK_COLOR),
+ sizeof(normal_embed));
+ normal_embed[sizeof(normal_embed)-1] = '\0';
+
+ for(p = line; (p = strstr(p, normal_embed)); p++){
+
+ /*
+ * Replace the normal color with our special quoting
+ * color. No need to change it if there are no
+ * characters after the color change because we're
+ * going to change the color to normal right below
+ * this anyway.
+ */
+ if(strlen(p) > strlen(quote_color_embed))
+ rplstr(p, strlen(p)+1, strlen(quote_color_embed), quote_color_embed);
+ }
+
+ ins = gf_line_test_new_ins(ins, line+strlen(line),
+ normal_embed, (2 * RGBLEN) + 4);
+ free_color_pair(&col);
+ }
+ }
+
+ ins = gf_line_test_new_ins(ins, line + strlen(line),
+ ((EDITORIAL_S *)local)->postfix,
+ ((EDITORIAL_S *)local)->postlen);
+ return(0);
+}
diff --git a/pith/editorial.h b/pith/editorial.h
new file mode 100644
index 00000000..32be8fce
--- /dev/null
+++ b/pith/editorial.h
@@ -0,0 +1,28 @@
+/*-----------------------------------------------------------------------
+ $Id: editorial.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ -----------------------------------------------------------------------*/
+
+/* ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_EDITORIAL_INCLUDED
+#define PITH_EDITORIAL_INCLUDED
+
+
+#include "../pith/filter.h"
+
+
+/* exported protoypes */
+char *format_editorial(char *, int, int, HANDLE_S **, gf_io_t);
+
+
+#endif /* PITH_EDITORIAL_INCLUDED */
diff --git a/pith/escapes.c b/pith/escapes.c
new file mode 100644
index 00000000..f6f5c8fe
--- /dev/null
+++ b/pith/escapes.c
@@ -0,0 +1,69 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: escapes.c 761 2007-10-23 22:35:18Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+
+ escapes.c
+ Implements known escape code matching
+
+ ====*/
+
+
+#include "../pith/headers.h"
+#include "../pith/escapes.h"
+
+
+
+/*------------------------------------------------------------------
+ This list of known escape sequences is taken from RFC's 1486 and 1554
+ and draft-apng-cc-encoding, and the X11R5 source with only a remote
+ understanding of what this all means...
+
+ NOTE: if the length of these should extend beyond 4 chars, fix
+ MAX_ESC_LEN in filter.c
+ ----*/
+#ifdef _WINDOWS
+static char *known_escapes[] = {
+ "(B", "(J", "$@", "$B", /* RFC 1468 */
+ "(H",
+ NULL};
+#else
+static char *known_escapes[] = {
+ "(B", "(J", "$@", "$B", /* RFC 1468 */
+ "(H",
+ "$A", "$(C", "$(D", ".A", ".F", /* added by RFC 1554 */
+ "$)C", "$)A", "$*E", "$*X", /* those in apng-draft */
+ "$+G", "$+H", "$+I", "$+J", "$+K",
+ "$+L", "$+M",
+ ")I", "-A", "-B", "-C", "-D", /* codes form X11R5 source */
+ "-F", "-G", "-H", "-L", "-M",
+ "-$(A", "$(B", "$)B", "$)D",
+ NULL};
+#endif
+
+
+int
+match_escapes(char *esc_seq)
+{
+ char **p;
+ int n;
+
+ for(p = known_escapes; *p && strncmp(esc_seq, *p, n = strlen(*p)); p++)
+ ;
+
+ return(*p ? n + 1 : 0);
+}
diff --git a/pith/escapes.h b/pith/escapes.h
new file mode 100644
index 00000000..1a4750f4
--- /dev/null
+++ b/pith/escapes.h
@@ -0,0 +1,24 @@
+/*
+ * $Id: escapes.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_ESCAPES_INCLUDED
+#define PITH_ESCAPES_INCLUDED
+
+
+/* exported protoypes */
+int match_escapes(char *);
+
+
+#endif /* PITH_ESCAPES_INCLUDED */
diff --git a/pith/filter.c b/pith/filter.c
new file mode 100644
index 00000000..b8712b23
--- /dev/null
+++ b/pith/filter.c
@@ -0,0 +1,11305 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: filter.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ filter.c
+
+ This code provides a generalized, flexible way to allow
+ piping of data thru filters. Each filter is passed a structure
+ that it will use to hold its static data while it operates on
+ the stream of characters that are passed to it. After processing
+ it will either return or call the next filter in
+ the pipe with any character (or characters) it has ready to go. This
+ means some terminal type of filter has to be the last in the
+ chain (i.e., one that writes the passed char someplace, but doesn't
+ call another filter).
+
+ See below for more details.
+
+ The motivation is to handle MIME decoding, richtext conversion,
+ iso_code stripping and anything else that may come down the
+ pike (e.g., PEM) in an elegant fashion. mikes (920811)
+
+ TODO:
+ reasonable error handling
+
+ ====*/
+
+
+#include "../pith/headers.h"
+#include "../pith/filter.h"
+#include "../pith/conf.h"
+#include "../pith/store.h"
+#include "../pith/color.h"
+#include "../pith/escapes.h"
+#include "../pith/pipe.h"
+#include "../pith/status.h"
+#include "../pith/string.h"
+#include "../pith/util.h"
+#include "../pith/url.h"
+#include "../pith/init.h"
+#include "../pith/help.h"
+#include "../pico/keydefs.h"
+
+#ifdef _WINDOWS
+#include "../pico/osdep/mswin.h"
+#endif
+
+
+/*
+ * Internal prototypes
+ */
+int gf_so_writec(int);
+int gf_so_readc(unsigned char *);
+int gf_freadc(unsigned char *);
+int gf_freadc_locale(unsigned char *);
+int gf_freadc_getchar(unsigned char *, void *);
+int gf_fwritec(int);
+int gf_fwritec_locale(int);
+#ifdef _WINDOWS
+int gf_freadc_windows(unsigned char *);
+#endif /* _WINDOWS */
+int gf_preadc(unsigned char *);
+int gf_preadc_locale(unsigned char *);
+int gf_preadc_getchar(unsigned char *, void *);
+int gf_pwritec(int);
+int gf_pwritec_locale(int);
+int gf_sreadc(unsigned char *);
+int gf_sreadc_locale(unsigned char *);
+int gf_sreadc_getchar(unsigned char *, void *);
+int gf_swritec(int);
+int gf_swritec_locale(int);
+void gf_terminal(FILTER_S *, int);
+void gf_error(char *);
+char *gf_filter_puts(char *);
+void gf_filter_eod(void);
+
+void gf_8bit_put(FILTER_S *, int);
+
+
+
+/*
+ * System specific options
+ */
+#ifdef _WINDOWS
+#define CRLF_NEWLINES
+#endif
+
+
+/*
+ * Hooks for callers to adjust behavior
+ */
+char *(*pith_opt_pretty_var_name)(char *);
+char *(*pith_opt_pretty_feature_name)(char *, int);
+
+
+/*
+ * pointer to first function in a pipe, and pointer to last filter
+ */
+FILTER_S *gf_master = NULL;
+static gf_io_t last_filter;
+static char *gf_error_string;
+static long gf_byte_count;
+static jmp_buf gf_error_state;
+
+
+#define GF_NOOP 0x01 /* flags used by generalized */
+#define GF_EOD 0x02 /* filters */
+#define GF_DATA 0x04 /* See filter.c for more */
+#define GF_ERROR 0x08 /* details */
+#define GF_RESET 0x10
+
+
+/*
+ * A list of states used by the various filters. Reused in many filters.
+ */
+#define DFL 0
+#define EQUAL 1
+#define HEX 2
+#define WSPACE 3
+#define CCR 4
+#define CLF 5
+#define TOKEN 6
+#define TAG 7
+#define HANDLE 8
+#define HDATA 9
+#define ESC 10
+#define ESCDOL 11
+#define ESCPAR 12
+#define EUC 13
+#define BOL 14
+#define FL_QLEV 15
+#define FL_STF 16
+#define FL_SIG 17
+#define STOP_DECODING 18
+#define SPACECR 19
+
+
+
+/*
+ * Macros to reduce function call overhead associated with calling
+ * each filter for each byte filtered, and to minimize filter structure
+ * dereferences. NOTE: "queuein" has to do with putting chars into the
+ * filter structs data queue. So, writing at the queuein offset is
+ * what a filter does to pass processed data out of itself. Ditto for
+ * queueout. This explains the FI --> queueout init stuff below.
+ */
+#define GF_QUE_START(F) (&(F)->queue[0])
+#define GF_QUE_END(F) (&(F)->queue[GF_MAXBUF - 1])
+
+#define GF_IP_INIT(F) ip = (F) ? &(F)->queue[(F)->queuein] : NULL
+#define GF_IP_INIT_GLO(F) (*ipp) = (F) ? &(F)->queue[(F)->queuein] : NULL
+#define GF_EIB_INIT(F) eib = (F) ? GF_QUE_END(F) : NULL
+#define GF_EIB_INIT_GLO(F) (*eibp) = (F) ? GF_QUE_END(F) : NULL
+#define GF_OP_INIT(F) op = (F) ? &(F)->queue[(F)->queueout] : NULL
+#define GF_EOB_INIT(F) eob = (F) ? &(F)->queue[(F)->queuein] : NULL
+
+#define GF_IP_END(F) (F)->queuein = ip - GF_QUE_START(F)
+#define GF_IP_END_GLO(F) (F)->queuein = (unsigned char *)(*ipp) - (unsigned char *)GF_QUE_START(F)
+#define GF_OP_END(F) (F)->queueout = op - GF_QUE_START(F)
+
+#define GF_INIT(FI, FO) unsigned char *GF_OP_INIT(FI); \
+ unsigned char *GF_EOB_INIT(FI); \
+ unsigned char *GF_IP_INIT(FO); \
+ unsigned char *GF_EIB_INIT(FO);
+
+#define GF_CH_RESET(F) (op = eob = GF_QUE_START(F), \
+ (F)->queueout = (F)->queuein = 0)
+
+#define GF_END(FI, FO) (GF_OP_END(FI), GF_IP_END(FO))
+
+#define GF_FLUSH(F) ((GF_IP_END(F), (*(F)->f)((F), GF_DATA), \
+ GF_IP_INIT(F), GF_EIB_INIT(F)) ? 1 : 0)
+#define GF_FLUSH_GLO(F) ((GF_IP_END_GLO(F), (*(F)->f)((F), GF_DATA), \
+ GF_IP_INIT_GLO(F), GF_EIB_INIT_GLO(F)) ? 1 : 0)
+
+#define GF_PUTC(F, C) ((int)(*ip++ = (C), (ip >= eib) ? GF_FLUSH(F) : 1))
+#define GF_PUTC_GLO(F, C) ((int)(*(*ipp)++ = (C), ((*ipp) >= (*eibp)) ? GF_FLUSH_GLO(F) : 1))
+
+/*
+ * Introducing the *_GLO macros for use in splitting the big macros out
+ * into functions (wrap_flush, wrap_eol). The reason we need a
+ * separate macro is because of the vars ip, eib, op, and eob, which are
+ * set up locally in a call to GF_INIT. To preserve these variables
+ * in the new functions, we now pass pointers to these four vars. Each
+ * of these new functions expects the presence of pointer vars
+ * ipp, eibp, opp, and eobp.
+ */
+
+#define GF_GETC(F, C) ((op < eob) ? (((C) = *op++), 1) : GF_CH_RESET(F))
+
+#define GF_COLOR_PUTC(F, C) { \
+ char *p; \
+ char cb[RGBLEN+1]; \
+ GF_PUTC_GLO((F)->next, TAG_EMBED); \
+ GF_PUTC_GLO((F)->next, TAG_FGCOLOR); \
+ strncpy(cb, color_to_asciirgb((C)->fg), sizeof(cb)); \
+ cb[sizeof(cb)-1] = '\0'; \
+ p = cb; \
+ for(; *p; p++) \
+ GF_PUTC_GLO((F)->next, *p); \
+ GF_PUTC_GLO((F)->next, TAG_EMBED); \
+ GF_PUTC_GLO((F)->next, TAG_BGCOLOR); \
+ strncpy(cb, color_to_asciirgb((C)->bg), sizeof(cb)); \
+ cb[sizeof(cb)-1] = '\0'; \
+ p = cb; \
+ for(; *p; p++) \
+ GF_PUTC_GLO((F)->next, *p); \
+ }
+
+/*
+ * Generalized getc and putc routines. provided here so they don't
+ * need to be re-done elsewhere to
+ */
+
+/*
+ * pointers to objects to be used by the generic getc and putc
+ * functions
+ */
+static struct gf_io_struct {
+ FILE *file;
+ PIPE_S *pipe;
+ char *txtp;
+ unsigned long n;
+ int flags;
+ CBUF_S cb;
+} gf_in, gf_out;
+
+#define GF_SO_STACK struct gf_so_stack
+static GF_SO_STACK {
+ STORE_S *so;
+ GF_SO_STACK *next;
+} *gf_so_in, *gf_so_out;
+
+
+
+/*
+ * Returns 1 if pc will write into a PicoText object, 0 otherwise.
+ *
+ * The purpose of this routine is so that we can avoid setting SIGALARM
+ * when writing into a PicoText object, because that type of object uses
+ * unprotected malloc/free/realloc, which can't be interrupted.
+ */
+int
+pc_is_picotext(gf_io_t pc)
+{
+ return(pc == gf_so_writec && gf_so_out && gf_so_out->so &&
+ gf_so_out->so->src == ExternalText);
+}
+
+
+
+/*
+ * setup to use and return a pointer to the generic
+ * getc function
+ */
+void
+gf_set_readc(gf_io_t *gc, void *txt, long unsigned int len, SourceType src, int flags)
+{
+ gf_in.n = len;
+ gf_in.flags = flags;
+ gf_in.cb.cbuf[0] = '\0';
+ gf_in.cb.cbufp = gf_in.cb.cbuf;
+ gf_in.cb.cbufend = gf_in.cb.cbuf;
+
+ if(src == FileStar){
+ gf_in.file = (FILE *)txt;
+ fseek(gf_in.file, 0L, 0);
+#ifdef _WINDOWS
+ *gc = (flags & READ_FROM_LOCALE) ? gf_freadc_windows
+ : gf_freadc;
+#else /* UNIX */
+ *gc = (flags & READ_FROM_LOCALE) ? gf_freadc_locale
+ : gf_freadc;
+#endif /* UNIX */
+ }
+ else if(src == PipeStar){
+ gf_in.pipe = (PIPE_S *)txt;
+ *gc = gf_preadc;
+ *gc = (flags & READ_FROM_LOCALE) ? gf_preadc_locale
+ : gf_preadc;
+ }
+ else{
+ gf_in.txtp = (char *)txt;
+ *gc = (flags & READ_FROM_LOCALE) ? gf_sreadc_locale
+ : gf_sreadc;
+ }
+}
+
+
+/*
+ * setup to use and return a pointer to the generic
+ * putc function
+ */
+void
+gf_set_writec(gf_io_t *pc, void *txt, long unsigned int len, SourceType src, int flags)
+{
+ gf_out.n = len;
+ gf_out.flags = flags;
+ gf_out.cb.cbuf[0] = '\0';
+ gf_out.cb.cbufp = gf_out.cb.cbuf;
+ gf_out.cb.cbufend = gf_out.cb.cbuf;
+
+ if(src == FileStar){
+ gf_out.file = (FILE *)txt;
+#ifdef _WINDOWS
+ *pc = gf_fwritec;
+#else /* UNIX */
+ *pc = (flags & WRITE_TO_LOCALE) ? gf_fwritec_locale
+ : gf_fwritec;
+#endif /* UNIX */
+ }
+ else if(src == PipeStar){
+ gf_out.pipe = (PIPE_S *)txt;
+ *pc = (flags & WRITE_TO_LOCALE) ? gf_pwritec_locale
+ : gf_pwritec;
+ }
+ else{
+ gf_out.txtp = (char *)txt;
+ *pc = (flags & WRITE_TO_LOCALE) ? gf_swritec_locale
+ : gf_swritec;
+ }
+}
+
+
+/*
+ * setup to use and return a pointer to the generic
+ * getc function
+ */
+void
+gf_set_so_readc(gf_io_t *gc, STORE_S *so)
+{
+ GF_SO_STACK *sp = (GF_SO_STACK *) fs_get(sizeof(GF_SO_STACK));
+
+ sp->so = so;
+ sp->next = gf_so_in;
+ gf_so_in = sp;
+ *gc = gf_so_readc;
+}
+
+
+void
+gf_clear_so_readc(STORE_S *so)
+{
+ GF_SO_STACK *sp;
+
+ if((sp = gf_so_in) != NULL){
+ if(so == sp->so){
+ gf_so_in = gf_so_in->next;
+ fs_give((void **) &sp);
+ }
+ else
+ panic("Programmer botch: Can't unstack store readc");
+ }
+ else
+ panic("Programmer botch: NULL store clearing store readc");
+}
+
+
+/*
+ * setup to use and return a pointer to the generic
+ * putc function
+ */
+void
+gf_set_so_writec(gf_io_t *pc, STORE_S *so)
+{
+ GF_SO_STACK *sp = (GF_SO_STACK *) fs_get(sizeof(GF_SO_STACK));
+
+ sp->so = so;
+ sp->next = gf_so_out;
+ gf_so_out = sp;
+ *pc = gf_so_writec;
+}
+
+
+void
+gf_clear_so_writec(STORE_S *so)
+{
+ GF_SO_STACK *sp;
+
+ if((sp = gf_so_out) != NULL){
+ if(so == sp->so){
+ gf_so_out = gf_so_out->next;
+ fs_give((void **) &sp);
+ }
+ else
+ panic("Programmer botch: Can't unstack store writec");
+ }
+ else
+ panic("Programmer botch: NULL store clearing store writec");
+}
+
+
+/*
+ * put the character to the object previously defined
+ */
+int
+gf_so_writec(int c)
+{
+ return(so_writec(c, gf_so_out->so));
+}
+
+
+/*
+ * get a character from an object previously defined
+ */
+int
+gf_so_readc(unsigned char *c)
+{
+ return(so_readc(c, gf_so_in->so));
+}
+
+
+/* get a character from a file */
+/* assumes gf_out struct is filled in */
+int
+gf_freadc(unsigned char *c)
+{
+ int rv = 0;
+
+ do {
+ errno = 0;
+ clearerr(gf_in.file);
+ rv = fread(c, sizeof(unsigned char), (size_t)1, gf_in.file);
+ } while(!rv && ferror(gf_in.file) && errno == EINTR);
+
+ return(rv);
+}
+
+
+int
+gf_freadc_locale(unsigned char *c)
+{
+ return(generic_readc_locale(c, gf_freadc_getchar, (void *) gf_in.file, &gf_in.cb));
+}
+
+
+/*
+ * This is just to make it work with generic_readc_locale.
+ */
+int
+gf_freadc_getchar(unsigned char *c, void *extraarg)
+{
+ FILE *file;
+ int rv = 0;
+
+ file = (FILE *) extraarg;
+
+ do {
+ errno = 0;
+ clearerr(file);
+ rv = fread(c, sizeof(unsigned char), (size_t)1, file);
+ } while(!rv && ferror(file) && errno == EINTR);
+
+ return(rv);
+}
+
+
+/*
+ * Put a character to a file.
+ * Assumes gf_out struct is filled in.
+ * Returns 1 on success, <= 0 on failure.
+ */
+int
+gf_fwritec(int c)
+{
+ unsigned char ch = (unsigned char)c;
+ int rv = 0;
+
+ do
+ rv = fwrite(&ch, sizeof(unsigned char), (size_t)1, gf_out.file);
+ while(!rv && ferror(gf_out.file) && errno == EINTR);
+
+ return(rv);
+}
+
+
+/*
+ * The locale version converts from UTF-8 to user's locale charset
+ * before writing the characters.
+ */
+int
+gf_fwritec_locale(int c)
+{
+ int rv = 1;
+ int i, outchars;
+ unsigned char obuf[MAX(MB_LEN_MAX,32)];
+
+ if((outchars = utf8_to_locale(c, &gf_out.cb, obuf, sizeof(obuf))) != 0){
+ for(i = 0; i < outchars; i++)
+ if(gf_fwritec(obuf[i]) != 1){
+ rv = 0;
+ break;
+ }
+ }
+
+ return(rv);
+}
+
+
+#ifdef _WINDOWS
+/*
+ * Read unicode characters from windows filesystem and return
+ * them as a stream of UTF-8 characters. The stream is assumed
+ * opened so that it will know how to put together the unicode.
+ *
+ * (This is totally untested, copied loosely from so_file_readc_windows
+ * which may or may not be appropriate.)
+ */
+int
+gf_freadc_windows(unsigned char *c)
+{
+ int rv = 0;
+ UCS ucs;
+
+ /* already got some from previous call? */
+ if(gf_in.cb.cbufend > gf_in.cb.cbuf){
+ *c = *gf_in.cb.cbufp;
+ gf_in.cb.cbufp++;
+ rv++;
+ if(gf_in.cb.cbufp >= gf_in.cb.cbufend){
+ gf_in.cb.cbufend = gf_in.cb.cbuf;
+ gf_in.cb.cbufp = gf_in.cb.cbuf;
+ }
+
+ return(rv);
+ }
+
+ if(gf_in.file){
+ /* windows only so second arg is ignored */
+ ucs = read_a_wide_char(gf_in.file, NULL);
+ rv = (ucs == CCONV_EOF) ? 0 : 1;
+ }
+
+ if(rv){
+ /*
+ * Now we need to convert the UCS character to UTF-8
+ * and dole out the UTF-8 one char at a time.
+ */
+ gf_in.cb.cbufend = utf8_put(gf_in.cb.cbuf, (unsigned long) ucs);
+ gf_in.cb.cbufp = gf_in.cb.cbuf;
+ if(gf_in.cb.cbufend > gf_in.cb.cbuf){
+ *c = *gf_in.cb.cbufp;
+ gf_in.cb.cbufp++;
+ if(gf_in.cb.cbufp >= gf_in.cb.cbufend){
+ gf_in.cb.cbufend = gf_in.cb.cbuf;
+ gf_in.cb.cbufp = gf_in.cb.cbuf;
+ }
+ }
+ else
+ *c = '?';
+ }
+
+ return(rv);
+}
+#endif /* _WINDOWS */
+
+
+int
+gf_preadc(unsigned char *c)
+{
+ return(pipe_readc(c, gf_in.pipe));
+}
+
+
+int
+gf_preadc_locale(unsigned char *c)
+{
+ return(generic_readc_locale(c, gf_preadc_getchar, (void *) gf_in.pipe, &gf_in.cb));
+}
+
+
+/*
+ * This is just to make it work with generic_readc_locale.
+ */
+int
+gf_preadc_getchar(unsigned char *c, void *extraarg)
+{
+ PIPE_S *pipe;
+
+ pipe = (PIPE_S *) extraarg;
+
+ return(pipe_readc(c, pipe));
+}
+
+
+/*
+ * Put a character to a pipe.
+ * Assumes gf_out struct is filled in.
+ * Returns 1 on success, <= 0 on failure.
+ */
+int
+gf_pwritec(int c)
+{
+ return(pipe_writec(c, gf_out.pipe));
+}
+
+
+/*
+ * The locale version converts from UTF-8 to user's locale charset
+ * before writing the characters.
+ */
+int
+gf_pwritec_locale(int c)
+{
+ int rv = 1;
+ int i, outchars;
+ unsigned char obuf[MAX(MB_LEN_MAX,32)];
+
+ if((outchars = utf8_to_locale(c, &gf_out.cb, obuf, sizeof(obuf))) != 0){
+ for(i = 0; i < outchars; i++)
+ if(gf_pwritec(obuf[i]) != 1){
+ rv = 0;
+ break;
+ }
+ }
+
+ return(rv);
+}
+
+
+/* get a character from a string, return nonzero if things OK */
+/* assumes gf_out struct is filled in */
+int
+gf_sreadc(unsigned char *c)
+{
+ return((gf_in.n) ? *c = *(gf_in.txtp)++, gf_in.n-- : 0);
+}
+
+
+int
+gf_sreadc_locale(unsigned char *c)
+{
+ return(generic_readc_locale(c, gf_sreadc_getchar, NULL, &gf_in.cb));
+}
+
+
+int
+gf_sreadc_getchar(unsigned char *c, void *extraarg)
+{
+ /*
+ * extraarg is ignored and gf_sreadc just uses globals instead.
+ * That's ok as long as we don't call it more than once at a time.
+ */
+ return(gf_sreadc(c));
+}
+
+
+/*
+ * Put a character to a string.
+ * Assumes gf_out struct is filled in.
+ * Returns 1 on success, <= 0 on failure.
+ */
+int
+gf_swritec(int c)
+{
+ return((gf_out.n) ? *(gf_out.txtp)++ = c, gf_out.n-- : 0);
+}
+
+
+/*
+ * The locale version converts from UTF-8 to user's locale charset
+ * before writing the characters.
+ */
+int
+gf_swritec_locale(int c)
+{
+ int rv = 1;
+ int i, outchars;
+ unsigned char obuf[MAX(MB_LEN_MAX,32)];
+
+ if((outchars = utf8_to_locale(c, &gf_out.cb, obuf, sizeof(obuf))) != 0){
+ for(i = 0; i < outchars; i++)
+ if(gf_swritec(obuf[i]) != 1){
+ rv = 0;
+ break;
+ }
+ }
+
+ return(rv);
+}
+
+
+/*
+ * output the given string with the given function
+ */
+int
+gf_puts(register char *s, gf_io_t pc)
+{
+ while(*s != '\0')
+ if(!(*pc)((unsigned char)*s++))
+ return(0); /* ERROR putting char ! */
+
+ return(1);
+}
+
+
+/*
+ * output the given string with the given function
+ */
+int
+gf_nputs(register char *s, long int n, gf_io_t pc)
+{
+ while(n--)
+ if(!(*pc)((unsigned char)*s++))
+ return(0); /* ERROR putting char ! */
+
+ return(1);
+}
+
+
+/*
+ * Read a stream of multi-byte characters from the
+ * user's locale charset and return a stream of
+ * UTF-8 characters, one at a time. The input characters
+ * are obtained by using the get_a_char function.
+ *
+ * Args c -- the returned octet
+ * get_a_char -- function to get a single octet of the multibyte
+ * character. The first arg of that function is the
+ * returned value and the second arg is for the
+ * functions use. The second arg is replaced with
+ * extraarg when it is called.
+ * extraarg -- The second arg to get_a_char.
+ * cb -- Storage area for state between calls to this func.
+ */
+int
+generic_readc_locale(unsigned char *c,
+ int (*get_a_char)(unsigned char *, void *),
+ void *extraarg,
+ CBUF_S *cb)
+{
+ unsigned long octets_so_far = 0, remaining_octets;
+ unsigned char *inputp;
+ unsigned char ch;
+ UCS ucs;
+ unsigned char inputbuf[20];
+ int rv = 0;
+ int got_one = 0;
+
+ /* already got some from previous call? */
+ if(cb->cbufend > cb->cbuf){
+ *c = *cb->cbufp;
+ cb->cbufp++;
+ rv++;
+ if(cb->cbufp >= cb->cbufend){
+ cb->cbufend = cb->cbuf;
+ cb->cbufp = cb->cbuf;
+ }
+
+ return(rv);
+ }
+
+ memset(inputbuf, 0, sizeof(inputbuf));
+ if((*get_a_char)(&ch, extraarg) == 0)
+ return(0);
+
+ inputbuf[octets_so_far++] = ch;
+
+ while(!got_one){
+ remaining_octets = octets_so_far;
+ inputp = inputbuf;
+ ucs = mbtow(ps_global->input_cs, &inputp, &remaining_octets);
+ switch(ucs){
+ case CCONV_BADCHAR:
+ return(rv);
+
+ case CCONV_NEEDMORE:
+/*
+ * Do we need to do something with the characters we've
+ * collected that don't form a valid UCS character?
+ * Probably need to try discarding them one at a time
+ * from the front instead of just throwing them all out.
+ */
+ if(octets_so_far >= sizeof(inputbuf))
+ return(rv);
+
+ if((*get_a_char)(&ch, extraarg) == 0)
+ return(rv);
+
+ inputbuf[octets_so_far++] = ch;
+ break;
+
+ default:
+ /* got a good UCS-4 character */
+ got_one++;
+ break;
+ }
+ }
+
+ /*
+ * Now we need to convert the UCS character to UTF-8
+ * and dole out the UTF-8 one char at a time.
+ */
+ rv++;
+ cb->cbufend = utf8_put(cb->cbuf, (unsigned long) ucs);
+ cb->cbufp = cb->cbuf;
+ if(cb->cbufend > cb->cbuf){
+ *c = *cb->cbufp;
+ cb->cbufp++;
+ if(cb->cbufp >= cb->cbufend){
+ cb->cbufend = cb->cbuf;
+ cb->cbufp = cb->cbuf;
+ }
+ }
+ else
+ *c = '?';
+
+ return(rv);
+}
+
+
+/*
+ * Start of generalized filter routines
+ */
+
+/*
+ * initializing function to make sure list of filters is empty.
+ */
+void
+gf_filter_init(void)
+{
+ FILTER_S *flt, *fltn = gf_master;
+
+ while((flt = fltn) != NULL){ /* free list of old filters */
+ fltn = flt->next;
+ fs_give((void **)&flt);
+ }
+
+ gf_master = NULL;
+ gf_error_string = NULL; /* clear previous errors */
+ gf_byte_count = 0L; /* reset counter */
+}
+
+
+
+/*
+ * link the given filter into the filter chain
+ */
+void
+gf_link_filter(filter_t f, void *data)
+{
+ FILTER_S *new, *tail;
+
+#ifdef CRLF_NEWLINES
+ /*
+ * If the system's native EOL convention is CRLF, then there's no
+ * point in passing data thru a filter that's not doing anything
+ */
+ if(f == gf_nvtnl_local || f == gf_local_nvtnl)
+ return;
+#endif
+
+ new = (FILTER_S *)fs_get(sizeof(FILTER_S));
+ memset(new, 0, sizeof(FILTER_S));
+
+ new->f = f; /* set the function pointer */
+ new->opt = data; /* set any optional parameter data */
+ (*f)(new, GF_RESET); /* have it setup initial state */
+
+ if((tail = gf_master) != NULL){ /* or add it to end of existing */
+ while(tail->next) /* list */
+ tail = tail->next;
+
+ tail->next = new;
+ }
+ else /* attach new struct to list */
+ gf_master = new; /* start a new list */
+}
+
+
+/*
+ * terminal filter, doesn't call any other filters, typically just does
+ * something with the output
+ */
+void
+gf_terminal(FILTER_S *f, int flg)
+{
+ if(flg == GF_DATA){
+ GF_INIT(f, f);
+
+ while(op < eob)
+ if((*last_filter)(*op++) <= 0) /* generic terminal filter */
+ gf_error(errno ? error_description(errno) : "Error writing pipe");
+
+ GF_CH_RESET(f);
+ }
+ else if(flg == GF_RESET)
+ errno = 0; /* prepare for problems */
+}
+
+
+/*
+ * set some outside gf_io_t function to the terminal function
+ * for example: a function to write a char to a file or into a buffer
+ */
+void
+gf_set_terminal(gf_io_t f) /* function to set generic filter */
+
+{
+ last_filter = f;
+}
+
+
+/*
+ * common function for filter's to make it known that an error
+ * has occurred. Jumps back to gf_pipe with error message.
+ */
+void
+gf_error(char *s)
+{
+ /* let the user know the error passed in s */
+ gf_error_string = s;
+ longjmp(gf_error_state, 1);
+}
+
+
+/*
+ * The routine that shoves each byte through the chain of
+ * filters. It sets up error handling, and the terminal function.
+ * Then loops getting bytes with the given function, and passing
+ * it on to the first filter in the chain.
+ */
+char *
+gf_pipe(gf_io_t gc, gf_io_t pc)
+ /* how to get a character */
+{
+ unsigned char c;
+
+ dprint((4, "-- gf_pipe: "));
+
+ /*
+ * set up for any errors a filter may encounter
+ */
+ if(setjmp(gf_error_state)){
+ dprint((4, "ERROR: %s\n",
+ gf_error_string ? gf_error_string : "NULL"));
+ return(gf_error_string); /* */
+ }
+
+ /*
+ * set and link in the terminal filter
+ */
+ gf_set_terminal(pc);
+ gf_link_filter(gf_terminal, NULL);
+
+ /*
+ * while there are chars to process, send them thru the pipe.
+ * NOTE: it's necessary to enclose the loop below in a block
+ * as the GF_INIT macro calls some automatic var's into
+ * existence. It can't be placed at the start of gf_pipe
+ * because its useful for us to be called without filters loaded
+ * when we're just being used to copy bytes between storage
+ * objects.
+ */
+ {
+ GF_INIT(gf_master, gf_master);
+
+ while((*gc)(&c)){
+ gf_byte_count++;
+
+#ifdef _WINDOWS
+ if(!(gf_byte_count & 0x3ff))
+ /* Under windows we yield to allow event processing.
+ * Progress display is handled throught the alarm()
+ * mechinism.
+ */
+ mswin_yield ();
+#endif
+
+ GF_PUTC(gf_master, c & 0xff);
+ }
+
+ /*
+ * toss an end-of-data marker down the pipe to give filters
+ * that have any buffered data the opportunity to dump it
+ */
+ (void) GF_FLUSH(gf_master);
+ (*gf_master->f)(gf_master, GF_EOD);
+ }
+
+ dprint((4, "done.\n"));
+ return(NULL); /* everything went OK */
+}
+
+
+/*
+ * return the number of bytes piped so far
+ */
+long
+gf_bytes_piped(void)
+{
+ return(gf_byte_count);
+}
+
+
+/*
+ * filter the given input with the given command
+ *
+ * Args: cmd -- command string to execute
+ * prepend -- string to prepend to filtered input
+ * source_so -- storage object containing data to be filtered
+ * pc -- function to write filtered output with
+ * aux_filters -- additional filters to pass data thru after "cmd"
+ *
+ * Returns: NULL on sucess, reason for failure (not alloc'd!) on error
+ */
+char *
+gf_filter(char *cmd, char *prepend, STORE_S *source_so, gf_io_t pc,
+ FILTLIST_S *aux_filters, int disable_reset,
+ void (*pipecb_f)(PIPE_S *, int, void *))
+{
+ unsigned char c, obuf[MAX(MB_LEN_MAX,32)];
+ int flags, outchars, i;
+ char *errstr = NULL, buf[MAILTMPLEN];
+ PIPE_S *fpipe;
+ CBUF_S cb;
+#ifdef NON_BLOCKING_IO
+ int n;
+#endif
+
+ dprint((4, "so_filter: \"%s\"\n", cmd ? cmd : "?"));
+
+ gf_filter_init();
+
+ /*
+ * After coming back from user's pipe command we need to convert
+ * the output from the pipe back to UTF-8.
+ */
+ if(ps_global->keyboard_charmap && strucmp("UTF-8", ps_global->keyboard_charmap))
+ gf_link_filter(gf_utf8, gf_utf8_opt(ps_global->keyboard_charmap));
+
+ for( ; aux_filters && aux_filters->filter; aux_filters++)
+ gf_link_filter(aux_filters->filter, aux_filters->data);
+
+ gf_set_terminal(pc);
+ gf_link_filter(gf_terminal, NULL);
+
+ cb.cbuf[0] = '\0';
+ cb.cbufp = cb.cbuf;
+ cb.cbufend = cb.cbuf;
+
+ /*
+ * Spawn filter feeding it data, and reading what it writes.
+ */
+ so_seek(source_so, 0L, 0);
+ flags = PIPE_WRITE | PIPE_READ | PIPE_NOSHELL |
+ (!disable_reset ? PIPE_RESET : 0);
+
+ if((fpipe = open_system_pipe(cmd, NULL, NULL, flags, 0, pipecb_f, pipe_report_error)) != NULL){
+
+#ifdef NON_BLOCKING_IO
+
+ if(fcntl(fileno(fpipe->in.f), F_SETFL, NON_BLOCKING_IO) == -1)
+ errstr = "Can't set up non-blocking IO";
+
+ if(prepend && (fputs(prepend, fpipe->out.f) == EOF
+ || fputc('\n', fpipe->out.f) == EOF))
+ errstr = error_description(errno);
+
+ while(!errstr){
+ /* if the pipe can't hold a K we're sunk (too bad PIPE_MAX
+ * isn't ubiquitous ;).
+ */
+ for(n = 0; !errstr && fpipe->out.f && n < 1024; n++)
+ if(!so_readc(&c, source_so)){
+ fclose(fpipe->out.f);
+ fpipe->out.f = NULL;
+ }
+ else{
+ /*
+ * Got a UTF-8 character from source_so.
+ * We need to convert it to the user's locale charset
+ * and then send the result to the pipe.
+ */
+ if((outchars = utf8_to_locale((int) c, &cb, obuf, sizeof(obuf))) != 0)
+ for(i = 0; i < outchars && !errstr; i++)
+ if(fputc(obuf[i], fpipe->out.f) == EOF)
+ errstr = error_description(errno);
+ }
+
+ /*
+ * Note: We clear errno here and test below, before ferror,
+ * because *some* stdio implementations consider
+ * EAGAIN and EWOULDBLOCK equivalent to EOF...
+ */
+ errno = 0;
+ clearerr(fpipe->in.f); /* fix from <cananian@cananian.mit.edu> */
+
+ while(!errstr && fgets(buf, sizeof(buf), fpipe->in.f))
+ errstr = gf_filter_puts(buf);
+
+ /* then fgets failed! */
+ if(!errstr && !(errno == EAGAIN || errno == EWOULDBLOCK)){
+ if(feof(fpipe->in.f)) /* nothing else interesting! */
+ break;
+ else if(ferror(fpipe->in.f)) /* bummer. */
+ errstr = error_description(errno);
+ }
+ else if(errno == EAGAIN || errno == EWOULDBLOCK)
+ clearerr(fpipe->in.f);
+ }
+
+#else /* !NON_BLOCKING_IO */
+
+ if(prepend && (pipe_puts(prepend, fpipe) == EOF
+ || pipe_putc('\n', fpipe) == EOF))
+ errstr = error_description(errno);
+
+ /*
+ * Well, do the best we can, and hope the pipe we're writing
+ * doesn't fill up before we start reading...
+ */
+ while(!errstr && so_readc(&c, source_so))
+ if((outchars = utf8_to_locale((int) c, &cb, obuf, sizeof(obuf))) != 0)
+ for(i = 0; i < outchars && !errstr; i++)
+ if(pipe_putc(obuf[i], fpipe) == EOF)
+ errstr = error_description(errno);
+
+ if(pipe_close_write(fpipe))
+ errstr = _("Pipe command returned error.");
+
+ while(!errstr && pipe_gets(buf, sizeof(buf), fpipe))
+ errstr = gf_filter_puts(buf);
+
+#endif /* !NON_BLOCKING_IO */
+
+ if(close_system_pipe(&fpipe, NULL, pipecb_f) && !errstr)
+ errstr = _("Pipe command returned error.");
+
+ gf_filter_eod();
+ }
+ else
+ errstr = _("Error setting up pipe command.");
+
+ return(errstr);
+}
+
+
+/*
+ * gf_filter_puts - write the given string down the filter's pipe
+ */
+char *
+gf_filter_puts(register char *s)
+{
+ GF_INIT(gf_master, gf_master);
+
+ /*
+ * set up for any errors a filter may encounter
+ */
+ if(setjmp(gf_error_state)){
+ dprint((4, "ERROR: gf_filter_puts: %s\n",
+ gf_error_string ? gf_error_string : "NULL"));
+ return(gf_error_string);
+ }
+
+ while(*s)
+ GF_PUTC(gf_master, (*s++) & 0xff);
+
+ GF_END(gf_master, gf_master);
+ return(NULL);
+}
+
+
+/*
+ * gf_filter_eod - flush pending data filter's input queue and deliver
+ * the GF_EOD marker.
+ */
+void
+gf_filter_eod(void)
+{
+ GF_INIT(gf_master, gf_master);
+ (void) GF_FLUSH(gf_master);
+ (*gf_master->f)(gf_master, GF_EOD);
+}
+
+
+/*
+ * END OF PIPE SUPPORT ROUTINES, BEGINNING OF FILTERS
+ *
+ * Filters MUST use the specified interface (pointer to filter
+ * structure, the unsigned character buffer in that struct, and a
+ * cmd flag), and pass each resulting octet to the next filter in the
+ * chain. Only the terminal filter need not call another filter.
+ * As a result, filters share a pretty general structure.
+ * Typically three main conditionals separate initialization from
+ * data from end-of-data command processing.
+ *
+ * Lastly, being character-at-a-time, they're a little more complex
+ * to write than filters operating on buffers because some state
+ * must typically be kept between characters. However, for a
+ * little bit of complexity here, much convenience is gained later
+ * as they can be arbitrarily chained together at run time and
+ * consume few resources (especially memory or disk) as they work.
+ * (NOTE 951005: even less cpu now that data between filters is passed
+ * via a vector.)
+ *
+ * A few notes about implementing filters:
+ *
+ * - A generic filter template looks like:
+ *
+ * void
+ * gf_xxx_filter(f, flg)
+ * FILTER_S *f;
+ * int flg;
+ * {
+ * GF_INIT(f, f->next); // def's var's to speed queue drain
+ *
+ * if(flg == GF_DATA){
+ * register unsigned char c;
+ *
+ * while(GF_GETC(f, c)){ // macro taking data off input queue
+ * // operate on c and pass it on here
+ * GF_PUTC(f->next, c); // macro writing output queue
+ * }
+ *
+ * GF_END(f, f->next); // macro to sync pointers/offsets
+ * //WARNING: DO NOT RETURN BEFORE ALL INCOMING DATA'S PROCESSED
+ * }
+ * else if(flg == GF_EOD){
+ * // process any buffered data here and pass it on
+ * GF_FLUSH(f->next); // flush pending data to next filter
+ * (*f->next->f)(f->next, GF_EOD);
+ * }
+ * else if(flg == GF_RESET){
+ * // initialize any data in the struct here
+ * }
+ * }
+ *
+ * - Any free storage allocated during initialization (typically tied
+ * to the "line" pointer in FILTER_S) is the filter's responsibility
+ * to clean up when the GF_EOD command comes through.
+ *
+ * - Filter's must pass GF_EOD they receive on to the next
+ * filter in the chain so it has the opportunity to flush
+ * any buffered data.
+ *
+ * - All filters expect NVT end-of-lines. The idea is to prepend
+ * or append either the gf_local_nvtnl or gf_nvtnl_local
+ * os-dependant filters to the data on the appropriate end of the
+ * pipe for the task at hand.
+ *
+ * - NOTE: As of 951004, filters no longer take their input as a single
+ * char argument, but rather get data to operate on via a vector
+ * representing the input queue in the FILTER_S structure.
+ *
+ */
+
+
+
+/*
+ * BASE64 TO BINARY encoding and decoding routines below
+ */
+
+
+/*
+ * BINARY to BASE64 filter (encoding described in rfc1341)
+ */
+void
+gf_binary_b64(FILTER_S *f, int flg)
+{
+ static char *v =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ GF_INIT(f, f->next);
+
+ if(flg == GF_DATA){
+ register unsigned char c;
+ register unsigned char t = f->t;
+ register long n = f->n;
+
+ while(GF_GETC(f, c)){
+
+ switch(n++){
+ case 0 : case 3 : case 6 : case 9 : case 12: case 15: case 18:
+ case 21: case 24: case 27: case 30: case 33: case 36: case 39:
+ case 42: case 45:
+ GF_PUTC(f->next, v[c >> 2]);
+ /* byte 1: high 6 bits (1) */
+ t = c << 4; /* remember high 2 bits for next */
+ break;
+
+ case 1 : case 4 : case 7 : case 10: case 13: case 16: case 19:
+ case 22: case 25: case 28: case 31: case 34: case 37: case 40:
+ case 43:
+ GF_PUTC(f->next, v[(t|(c>>4)) & 0x3f]);
+ t = c << 2;
+ break;
+
+ case 2 : case 5 : case 8 : case 11: case 14: case 17: case 20:
+ case 23: case 26: case 29: case 32: case 35: case 38: case 41:
+ case 44:
+ GF_PUTC(f->next, v[(t|(c >> 6)) & 0x3f]);
+ GF_PUTC(f->next, v[c & 0x3f]);
+ break;
+ }
+
+ if(n == 45){ /* start a new line? */
+ GF_PUTC(f->next, '\015');
+ GF_PUTC(f->next, '\012');
+ n = 0L;
+ }
+ }
+
+ f->n = n;
+ f->t = t;
+ GF_END(f, f->next);
+ }
+ else if(flg == GF_EOD){ /* no more data */
+ switch (f->n % 3) { /* handle trailing bytes */
+ case 0: /* no trailing bytes */
+ break;
+
+ case 1:
+ GF_PUTC(f->next, v[(f->t) & 0x3f]);
+ GF_PUTC(f->next, '='); /* byte 3 */
+ GF_PUTC(f->next, '='); /* byte 4 */
+ break;
+
+ case 2:
+ GF_PUTC(f->next, v[(f->t) & 0x3f]);
+ GF_PUTC(f->next, '='); /* byte 4 */
+ break;
+ }
+
+ /* end with CRLF */
+ if(f->n){
+ GF_PUTC(f->next, '\015');
+ GF_PUTC(f->next, '\012');
+ }
+
+ (void) GF_FLUSH(f->next);
+ (*f->next->f)(f->next, GF_EOD);
+ }
+ else if(flg == GF_RESET){
+ dprint((9, "-- gf_reset binary_b64\n"));
+ f->n = 0L;
+ }
+}
+
+
+
+/*
+ * BASE64 to BINARY filter (encoding described in rfc1341)
+ */
+void
+gf_b64_binary(FILTER_S *f, int flg)
+{
+ static char v[] = {65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,
+ 65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,
+ 65,65,65,65,65,65,65,65,65,65,65,62,65,65,65,63,
+ 52,53,54,55,56,57,58,59,60,61,65,65,65,64,65,65,
+ 65, 0, 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,65,65,65,65,65,
+ 65,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
+ 41,42,43,44,45,46,47,48,49,50,51,65,65,65,65,65};
+ GF_INIT(f, f->next);
+
+ if(flg == GF_DATA){
+ register unsigned char c;
+ register unsigned char t = f->t;
+ register int n = (int) f->n;
+ register int state = f->f1;
+
+ while(GF_GETC(f, c)){
+
+ if(state){
+ state = 0;
+ if (c != '=') {
+ gf_error("Illegal '=' in base64 text");
+ /* NO RETURN */
+ }
+ }
+
+ /* in range, and a valid value? */
+ if((c & ~0x7f) || (c = v[c]) > 63){
+ if(c == 64){
+ switch (n++) { /* check quantum position */
+ case 2:
+ state++; /* expect an equal as next char */
+ break;
+
+ case 3:
+ n = 0L; /* restart quantum */
+ break;
+
+ default: /* impossible quantum position */
+ gf_error("Internal base64 decoder error");
+ /* NO RETURN */
+ }
+ }
+ }
+ else{
+ switch (n++) { /* install based on quantum position */
+ case 0: /* byte 1: high 6 bits */
+ t = c << 2;
+ break;
+
+ case 1: /* byte 1: low 2 bits */
+ GF_PUTC(f->next, (t|(c >> 4)));
+ t = c << 4; /* byte 2: high 4 bits */
+ break;
+
+ case 2: /* byte 2: low 4 bits */
+ GF_PUTC(f->next, (t|(c >> 2)));
+ t = c << 6; /* byte 3: high 2 bits */
+ break;
+
+ case 3:
+ GF_PUTC(f->next, t | c);
+ n = 0L; /* reinitialize mechanism */
+ break;
+ }
+ }
+ }
+
+ f->f1 = state;
+ f->t = t;
+ f->n = n;
+ GF_END(f, f->next);
+ }
+ else if(flg == GF_EOD){
+ (void) GF_FLUSH(f->next);
+ (*f->next->f)(f->next, GF_EOD);
+ }
+ else if(flg == GF_RESET){
+ dprint((9, "-- gf_reset b64_binary\n"));
+ f->n = 0L; /* quantum position */
+ f->f1 = 0; /* state holder: equal seen? */
+ }
+}
+
+
+
+
+/*
+ * QUOTED-PRINTABLE ENCODING AND DECODING filters below.
+ * encoding described in rfc1341
+ */
+
+#define GF_MAXLINE 80 /* good buffer size */
+
+/*
+ * default action for QUOTED-PRINTABLE to 8BIT decoder
+ */
+#define GF_QP_DEFAULT(f, c) { \
+ if((c) == ' '){ \
+ state = WSPACE; \
+ /* reset white space! */ \
+ (f)->linep = (f)->line; \
+ *((f)->linep)++ = ' '; \
+ } \
+ else if((c) == '='){ \
+ state = EQUAL; \
+ } \
+ else \
+ GF_PUTC((f)->next, (c)); \
+ }
+
+
+/*
+ * QUOTED-PRINTABLE to 8BIT filter
+ */
+void
+gf_qp_8bit(FILTER_S *f, int flg)
+{
+
+ GF_INIT(f, f->next);
+
+ if(flg == GF_DATA){
+ register unsigned char c;
+ register int state = f->f1;
+
+ while(GF_GETC(f, c)){
+
+ switch(state){
+ case DFL : /* default case */
+ default:
+ GF_QP_DEFAULT(f, c);
+ break;
+
+ case CCR : /* non-significant space */
+ state = DFL;
+ if(c == '\012')
+ continue; /* go on to next char */
+
+ GF_QP_DEFAULT(f, c);
+ break;
+
+ case EQUAL :
+ if(c == '\015'){ /* "=\015" is a soft EOL */
+ state = CCR;
+ break;
+ }
+
+ if(c == '='){ /* compatibility clause for old guys */
+ GF_PUTC(f->next, '=');
+ state = DFL;
+ break;
+ }
+
+ if(!isxdigit((unsigned char)c)){ /* must be hex! */
+ /*
+ * First character after '=' not a hex digit.
+ * This ain't right, but we're going to treat it as
+ * plain old text instead of an '=' followed by hex.
+ * In other words, they forgot to encode the '='.
+ * Before 4.60 we just bailed with an error here, but now
+ * we keep going as long as we are just displaying
+ * the result (and not saving it or something).
+ *
+ * Wait! The users don't like that. They want to be able
+ * to use it even if it might be wrong. So just plow
+ * ahead even if displaying.
+ *
+ * Better have this be a constant string so that if we
+ * get multiple instances of it in a single message we
+ * can avoid the too many error messages problem. It
+ * better be the same message as the one a few lines
+ * below, as well.
+ *
+ * Turn off decoding after encountering such an error and
+ * just dump the rest of the text as is.
+ */
+ state = STOP_DECODING;
+ GF_PUTC(f->next, '=');
+ GF_PUTC(f->next, c);
+ q_status_message(SM_ORDER,3,3,
+ _("Warning: Non-hexadecimal character in QP encoding!"));
+
+ dprint((2, "gf_qp_8bit: warning: non-hex char in QP encoding: char \"%c\" (%d) follows =\n", c, c));
+ break;
+ }
+
+ if (isdigit ((unsigned char)c))
+ f->t = c - '0';
+ else
+ f->t = c - (isupper((unsigned char)c) ? 'A' - 10 : 'a' - 10);
+
+ f->f2 = c; /* store character in case we have to
+ back out in !isxdigit below */
+
+ state = HEX;
+ break;
+
+ case HEX :
+ state = DFL;
+ if(!isxdigit((unsigned char)c)){ /* must be hex! */
+ state = STOP_DECODING;
+ GF_PUTC(f->next, '=');
+ GF_PUTC(f->next, f->f2);
+ GF_PUTC(f->next, c);
+ q_status_message(SM_ORDER,3,3,
+ _("Warning: Non-hexadecimal character in QP encoding!"));
+
+ dprint((2, "gf_qp_8bit: warning: non-hex char in QP encoding: char \"%c\" (%d) follows =%c\n", c, c, f->f2));
+ break;
+ }
+
+ if (isdigit((unsigned char)c))
+ c -= '0';
+ else
+ c -= (isupper((unsigned char)c) ? 'A' - 10 : 'a' - 10);
+
+ GF_PUTC(f->next, c + (f->t << 4));
+ break;
+
+ case WSPACE :
+ if(c == ' '){ /* toss it in with other spaces */
+ if(f->linep - f->line < GF_MAXLINE)
+ *(f->linep)++ = ' ';
+ break;
+ }
+
+ state = DFL;
+ if(c == '\015'){ /* not our white space! */
+ f->linep = f->line; /* reset buffer */
+ GF_PUTC(f->next, '\015');
+ break;
+ }
+
+ /* the spaces are ours, write 'em */
+ f->n = f->linep - f->line;
+ while((f->n)--)
+ GF_PUTC(f->next, ' ');
+
+ GF_QP_DEFAULT(f, c); /* take care of 'c' in default way */
+ break;
+
+ case STOP_DECODING :
+ GF_PUTC(f->next, c);
+ break;
+ }
+ }
+
+ f->f1 = state;
+ GF_END(f, f->next);
+ }
+ else if(flg == GF_EOD){
+ fs_give((void **)&(f->line));
+ (void) GF_FLUSH(f->next);
+ (*f->next->f)(f->next, GF_EOD);
+ }
+ else if(flg == GF_RESET){
+ dprint((9, "-- gf_reset qp_8bit\n"));
+ f->f1 = DFL;
+ f->linep = f->line = (char *)fs_get(GF_MAXLINE * sizeof(char));
+ }
+}
+
+
+
+/*
+ * USEFUL MACROS TO HELP WITH QP ENCODING
+ */
+
+#define QP_MAXL 75 /* 76th place only for continuation */
+
+/*
+ * Macro to test and wrap long quoted printable lines
+ */
+#define GF_8BIT_WRAP(f) { \
+ GF_PUTC((f)->next, '='); \
+ GF_PUTC((f)->next, '\015'); \
+ GF_PUTC((f)->next, '\012'); \
+ }
+
+/*
+ * write a quoted octet in QUOTED-PRINTABLE encoding, adding soft
+ * line break if needed.
+ */
+#define GF_8BIT_PUT_QUOTE(f, c) { \
+ if(((f)->n += 3) > QP_MAXL){ \
+ GF_8BIT_WRAP(f); \
+ (f)->n = 3; /* set line count */ \
+ } \
+ GF_PUTC((f)->next, '='); \
+ GF_PUTC((f)->next, HEX_CHAR1(c)); \
+ GF_PUTC((f)->next, HEX_CHAR2(c)); \
+ }
+
+/*
+ * just write an ordinary octet in QUOTED-PRINTABLE, wrapping line
+ * if needed.
+ */
+#define GF_8BIT_PUT(f, c) { \
+ if((++(f->n)) > QP_MAXL){ \
+ GF_8BIT_WRAP(f); \
+ f->n = 1L; \
+ } \
+ if(f->n == 1L && c == '.'){ \
+ GF_8BIT_PUT_QUOTE(f, c); \
+ f->n = 3; \
+ } \
+ else \
+ GF_PUTC(f->next, c); \
+ }
+
+
+/*
+ * default action for 8bit to quoted printable encoder
+ */
+#define GF_8BIT_DEFAULT(f, c) if((c) == ' '){ \
+ state = WSPACE; \
+ } \
+ else if(c == '\015'){ \
+ state = CCR; \
+ } \
+ else if(iscntrl(c & 0x7f) || (c == 0x7f) \
+ || (c & 0x80) || (c == '=')){ \
+ GF_8BIT_PUT_QUOTE(f, c); \
+ } \
+ else{ \
+ GF_8BIT_PUT(f, c); \
+ }
+
+
+/*
+ * 8BIT to QUOTED-PRINTABLE filter
+ */
+void
+gf_8bit_qp(FILTER_S *f, int flg)
+{
+ short dummy_dots = 0, dummy_dmap = 1;
+ GF_INIT(f, f->next);
+
+ if(flg == GF_DATA){
+ register unsigned char c;
+ register int state = f->f1;
+
+ while(GF_GETC(f, c)){
+
+ /* keep track of "^JFrom " */
+ Find_Froms(f->t, dummy_dots, f->f2, dummy_dmap, c);
+
+ switch(state){
+ case DFL : /* handle ordinary case */
+ GF_8BIT_DEFAULT(f, c);
+ break;
+
+ case CCR : /* true line break? */
+ state = DFL;
+ if(c == '\012'){
+ GF_PUTC(f->next, '\015');
+ GF_PUTC(f->next, '\012');
+ f->n = 0L;
+ }
+ else{ /* nope, quote the CR */
+ GF_8BIT_PUT_QUOTE(f, '\015');
+ GF_8BIT_DEFAULT(f, c); /* and don't forget about c! */
+ }
+ break;
+
+ case WSPACE:
+ state = DFL;
+ if(c == '\015' || f->t){ /* handle the space */
+ GF_8BIT_PUT_QUOTE(f, ' ');
+ f->t = 0; /* reset From flag */
+ }
+ else
+ GF_8BIT_PUT(f, ' ');
+
+ GF_8BIT_DEFAULT(f, c); /* handle 'c' in the default way */
+ break;
+ }
+ }
+
+ f->f1 = state;
+ GF_END(f, f->next);
+ }
+ else if(flg == GF_EOD){
+ switch(f->f1){
+ case CCR :
+ GF_8BIT_PUT_QUOTE(f, '\015'); /* write the last cr */
+ break;
+
+ case WSPACE :
+ GF_8BIT_PUT_QUOTE(f, ' '); /* write the last space */
+ break;
+ }
+
+ (void) GF_FLUSH(f->next);
+ (*f->next->f)(f->next, GF_EOD);
+ }
+ else if(flg == GF_RESET){
+ dprint((9, "-- gf_reset 8bit_qp\n"));
+ f->f1 = DFL; /* state from last character */
+ f->f2 = 1; /* state of "^NFrom " bitmap */
+ f->t = 0;
+ f->n = 0L; /* number of chars in current line */
+ }
+}
+
+/*
+ * This filter converts characters in one character set (the character
+ * set of a message, for example) to another (the user's character set).
+ */
+void
+gf_convert_8bit_charset(FILTER_S *f, int flg)
+{
+ static unsigned char *conv_table = NULL;
+ GF_INIT(f, f->next);
+
+ if(flg == GF_DATA){
+ register unsigned char c;
+
+ while(GF_GETC(f, c)){
+ GF_PUTC(f->next, conv_table ? conv_table[c] : c);
+ }
+
+ GF_END(f, f->next);
+ }
+ else if(flg == GF_EOD){
+ (void) GF_FLUSH(f->next);
+ (*f->next->f)(f->next, GF_EOD);
+ }
+ else if(flg == GF_RESET){
+ dprint((9, "-- gf_reset convert_8bit_charset\n"));
+ conv_table = (f->opt) ? (unsigned char *) (f->opt) : NULL;
+
+ }
+}
+
+
+typedef struct _utf8c_s {
+ void *conv_table;
+ int report_err;
+} UTF8C_S;
+
+
+/*
+ * This filter converts characters in UTF-8 to an 8-bit or 16-bit charset.
+ * Characters missing from the destination set, and invalid UTF-8 sequences,
+ * will be converted to "?".
+ */
+void
+gf_convert_utf8_charset(FILTER_S *f, int flg)
+{
+ static unsigned short *conv_table = NULL;
+ static int report_err = 0;
+ register int more = f->f2;
+ register long u = f->n;
+
+ /*
+ * "more" is the number of subsequent octets needed to complete a character,
+ * it is stored in f->f2.
+ * "u" is the accumulated Unicode character, it is stored in f->n
+ */
+
+ GF_INIT(f, f->next);
+
+ if(flg == GF_DATA){
+ register unsigned char c;
+
+ while(GF_GETC(f, c)){
+ if(!conv_table){ /* can't do much if no conversion table */
+ GF_PUTC(f->next, c);
+ }
+ /* UTF-8 continuation? */
+ else if((c > 0x7f) && (c < 0xc0)){
+ if(more){
+ u <<= 6; /* shift current value by 6 bits */
+ u |= c & 0x3f;
+ if (!--more){ /* last octet? */
+ if(u >= 0xffff || (u = conv_table[u]) == NOCHAR){
+ /*
+ * non-BMP character or a UTF-8 character
+ * which is not representable in the
+ * charset we're converting to.
+ */
+ c = '?';
+ if(report_err){
+ if(f->opt)
+ fs_give((void **) &f->opt);
+
+ /* TRANSLATORS: error while translating from one
+ character set to another, for example from UTF-8
+ to ISO-2022-JP or something like that. */
+ gf_error(_("translation error"));
+ /* NO RETURN */
+ }
+ }
+ else{
+ if(u > 0xff){
+ c = (unsigned char) (u >> 8);
+ GF_PUTC(f->next, c);
+ }
+
+ c = (unsigned char) u & 0xff;
+ }
+
+ GF_PUTC(f->next, c);
+ }
+ }
+ else{ /* continuation when not in progress */
+ GF_PUTC(f->next, '?');
+ }
+ }
+ else{
+ if(more){ /* incomplete UTF-8 character */
+ GF_PUTC(f->next, '?');
+ more = 0;
+ }
+ if(c < 0x80){ /* U+0000 - U+007f */
+ GF_PUTC(f->next, c);
+ }
+ else if(c < 0xe0){ /* U+0080 - U+07ff */
+ u = c & 0x1f; /* first 5 bits of 12 */
+ more = 1;
+ }
+ else if(c < 0xf0){ /* U+1000 - U+ffff */
+ u = c & 0x0f; /* first 4 bits of 16 */
+ more = 2;
+ }
+ /* in case we ever support non-BMP Unicode */
+ else if (c < 0xf8){ /* U+10000 - U+10ffff */
+ u = c & 0x07; /* first 3 bits of 20.5 */
+ more = 3;
+ }
+#if 0 /* ISO 10646 not in Unicode */
+ else if (c < 0xfc){ /* ISO 10646 20000 - 3ffffff */
+ u = c & 0x03; /* first 2 bits of 26 */
+ more = 4;
+ }
+ else if (c < 0xfe){ /* ISO 10646 4000000 - 7fffffff */
+ u = c & 0x03; /* first 2 bits of 26 */
+ more = 5;
+ }
+#endif
+ else{ /* not in Unicode */
+ GF_PUTC(f->next, '?');
+ }
+ }
+ }
+
+ f->f2 = more;
+ f->n = u;
+ GF_END(f, f->next);
+ }
+ else if(flg == GF_EOD){
+ (void) GF_FLUSH(f->next);
+ if(f->opt)
+ fs_give((void **) &f->opt);
+
+ (*f->next->f)(f->next, GF_EOD);
+ }
+ else if(flg == GF_RESET){
+ dprint((9, "-- gf_reset convert_utf8_charset\n"));
+ conv_table = ((UTF8C_S *) f->opt)->conv_table;
+ report_err = ((UTF8C_S *) f->opt)->report_err;
+ f->f2 = 0;
+ f->n = 0L;
+ }
+}
+
+
+void *
+gf_convert_utf8_charset_opt(void *table, int report_err)
+{
+ UTF8C_S *utf8c;
+
+ utf8c = (UTF8C_S *) fs_get(sizeof(UTF8C_S));
+ utf8c->conv_table = table;
+ utf8c->report_err = report_err;
+ return((void *) utf8c);
+}
+
+
+/*
+ * ISO-2022-JP to EUC (on Unix) or Shift-JIS (on PC) filter
+ *
+ * The routine is call ..._to_euc but it is really to either euc (unix Pine)
+ * or to Shift-JIS (if PC-Pine).
+ */
+void
+gf_2022_jp_to_euc(FILTER_S *f, int flg)
+{
+ register unsigned char c;
+ register int state = f->f1;
+
+ /*
+ * f->t lit means we're in middle of decoding a sequence of characters.
+ * f->f2 keeps track of first character of pair for Shift-JIS.
+ * f->f1 is the state.
+ */
+
+ GF_INIT(f, f->next);
+
+ if(flg == GF_DATA){
+ while(GF_GETC(f, c)){
+ switch(state){
+ case ESC: /* saw ESC */
+ if(!f->t && c == '$')
+ state = ESCDOL;
+ else if(f->t && c == '(')
+ state = ESCPAR;
+ else{
+ GF_PUTC(f->next, '\033');
+ GF_PUTC(f->next, c);
+ state = DFL;
+ }
+
+ break;
+
+ case ESCDOL: /* saw ESC $ */
+ if(c == 'B' || c == '@'){
+ state = EUC;
+ f->t = 1; /* filtering into euc */
+ f->f2 = -1; /* first character of pair */
+ }
+ else{
+ GF_PUTC(f->next, '\033');
+ GF_PUTC(f->next, '$');
+ GF_PUTC(f->next, c);
+ state = DFL;
+ }
+
+ break;
+
+ case ESCPAR: /* saw ESC ( */
+ if(c == 'B' || c == 'J' || c == 'H'){
+ state = DFL;
+ f->t = 0; /* done filtering */
+ }
+ else{
+ GF_PUTC(f->next, '\033'); /* Don't set hibit for */
+ GF_PUTC(f->next, '('); /* escape sequences, which */
+ GF_PUTC(f->next, c); /* this appears to be. */
+ }
+
+ break;
+
+ case EUC: /* filtering into euc */
+ if(c == '\033')
+ state = ESC;
+ else{
+#ifdef _WINDOWS /* Shift-JIS */
+ c &= 0x7f; /* 8-bit can't win */
+ if (f->f2 >= 0){ /* second of a pair? */
+ int rowOffset = (f->f2 < 95) ? 112 : 176;
+ int cellOffset = (f->f2 % 2) ? ((c > 95) ? 32 : 31)
+ : 126;
+
+ GF_PUTC(f->next, ((f->f2 + 1) >> 1) + rowOffset);
+ GF_PUTC(f->next, c + cellOffset);
+ f->f2 = -1; /* restart */
+ }
+ else if(c > 0x20 && c < 0x7f)
+ f->f2 = c; /* first of pair */
+ else{
+ GF_PUTC(f->next, c); /* write CTL as itself */
+ f->f2 = -1;
+ }
+#else /* EUC */
+ GF_PUTC(f->next, (c > 0x20 && c < 0x7f) ? c | 0x80 : c);
+#endif
+ }
+
+ break;
+
+ case DFL:
+ default:
+ if(c == '\033')
+ state = ESC;
+ else
+ GF_PUTC(f->next, c);
+
+ break;
+ }
+ }
+
+ f->f1 = state;
+ GF_END(f, f->next);
+ }
+ else if(flg == GF_EOD){
+ switch(state){
+ case ESC:
+ GF_PUTC(f->next, '\033');
+ break;
+
+ case ESCDOL:
+ GF_PUTC(f->next, '\033');
+ GF_PUTC(f->next, '$');
+ break;
+
+ case ESCPAR:
+ GF_PUTC(f->next, '\033'); /* Don't set hibit for */
+ GF_PUTC(f->next, '('); /* escape sequences. */
+ break;
+ }
+
+ (void) GF_FLUSH(f->next);
+ (*f->next->f)(f->next, GF_EOD);
+ }
+ else if(flg == GF_RESET){
+ dprint((9, "-- gf_reset jp_to_euc\n"));
+ f->f1 = DFL; /* state */
+ f->t = 0; /* not translating to euc */
+ }
+}
+
+
+/*
+ * EUC (on Unix) or Shift-JIS (on PC) to ISO-2022-JP filter
+ */
+void
+gf_native8bitjapanese_to_2022_jp(FILTER_S *f, int flg)
+{
+#ifdef _WINDOWS
+ gf_sjis_to_2022_jp(f, flg);
+#else
+ gf_euc_to_2022_jp(f, flg);
+#endif
+}
+
+
+void
+gf_euc_to_2022_jp(FILTER_S *f, int flg)
+{
+ register unsigned char c;
+
+ /*
+ * f->t lit means we've sent the start esc seq but not the end seq.
+ * f->f2 keeps track of first character of pair for Shift-JIS.
+ */
+
+ GF_INIT(f, f->next);
+
+ if(flg == GF_DATA){
+ while(GF_GETC(f, c)){
+ if(f->t){
+ if(c & 0x80){
+ GF_PUTC(f->next, c & 0x7f);
+ }
+ else{
+ GF_PUTC(f->next, '\033');
+ GF_PUTC(f->next, '(');
+ GF_PUTC(f->next, 'B');
+ GF_PUTC(f->next, c);
+ f->f2 = -1;
+ f->t = 0;
+ }
+ }
+ else{
+ if(c & 0x80){
+ GF_PUTC(f->next, '\033');
+ GF_PUTC(f->next, '$');
+ GF_PUTC(f->next, 'B');
+ GF_PUTC(f->next, c & 0x7f);
+ f->t = 1;
+ }
+ else{
+ GF_PUTC(f->next, c);
+ }
+ }
+ }
+
+ GF_END(f, f->next);
+ }
+ else if(flg == GF_EOD){
+ if(f->t){
+ GF_PUTC(f->next, '\033');
+ GF_PUTC(f->next, '(');
+ GF_PUTC(f->next, 'B');
+ f->t = 0;
+ f->f2 = -1;
+ }
+
+ (void) GF_FLUSH(f->next);
+ (*f->next->f)(f->next, GF_EOD);
+ }
+ else if(flg == GF_RESET){
+ dprint((9, "-- gf_reset euc_to_jp\n"));
+ f->t = 0;
+ f->f2 = -1;
+ }
+}
+
+void
+gf_sjis_to_2022_jp(FILTER_S *f, int flg)
+{
+ register unsigned char c;
+
+ /*
+ * f->t lit means we've sent the start esc seq but not the end seq.
+ * f->f2 keeps track of first character of pair for Shift-JIS.
+ */
+
+ GF_INIT(f, f->next);
+
+ if(flg == GF_DATA){
+ while(GF_GETC(f, c)){
+ if(f->t){
+ if(f->f2 >= 0){ /* second of a pair? */
+ int adjust = c < 159;
+ int rowOffset = f->f2 < 160 ? 112 : 176;
+ int cellOffset = adjust ? (c > 127 ? 32 : 31) : 126;
+
+ GF_PUTC(f->next, ((f->f2 - rowOffset) << 1) - adjust);
+ GF_PUTC(f->next, c - cellOffset);
+ f->f2 = -1;
+ }
+ else if(c & 0x80){
+ f->f2 = c; /* remember first of pair */
+ }
+ else{
+ GF_PUTC(f->next, '\033');
+ GF_PUTC(f->next, '(');
+ GF_PUTC(f->next, 'B');
+ GF_PUTC(f->next, c);
+ f->f2 = -1;
+ f->t = 0;
+ }
+ }
+ else{
+ if(c & 0x80){
+ GF_PUTC(f->next, '\033');
+ GF_PUTC(f->next, '$');
+ GF_PUTC(f->next, 'B');
+ f->f2 = c;
+ f->t = 1;
+ }
+ else{
+ GF_PUTC(f->next, c);
+ }
+ }
+ }
+
+ GF_END(f, f->next);
+ }
+ else if(flg == GF_EOD){
+ if(f->t){
+ GF_PUTC(f->next, '\033');
+ GF_PUTC(f->next, '(');
+ GF_PUTC(f->next, 'B');
+ f->t = 0;
+ f->f2 = -1;
+ }
+
+ (void) GF_FLUSH(f->next);
+ (*f->next->f)(f->next, GF_EOD);
+ }
+ else if(flg == GF_RESET){
+ dprint((9, "-- gf_reset sjis_to_jp\n"));
+ f->t = 0;
+ f->f2 = -1;
+ }
+}
+
+
+
+/*
+ * Various charset to UTF-8 Translation filter
+ */
+
+/*
+ * utf8 conversion options
+ */
+typedef struct _utf8_s {
+ CHARSET *charset;
+ unsigned long ucsc;
+} UTF8_S;
+
+#define UTF8_BLOCK 1024
+#define UTF8_EOB(f) ((f)->line + (f)->f2 - 1)
+#define UTF8_ADD(f, c) \
+ { \
+ if(p >= eobuf){ \
+ f->f2 += UTF8_BLOCK; \
+ fs_resize((void **)&f->line, \
+ (size_t) f->f2 * sizeof(char)); \
+ eobuf = UTF8_EOB(f); \
+ p = eobuf - UTF8_BLOCK; \
+ } \
+ *p++ = c; \
+ }
+#define GF_UTF8_FLUSH(f) { \
+ register long n; \
+ SIZEDTEXT intext, outtext; \
+ intext.data = (unsigned char *) f->line; \
+ intext.size = p - f->line; \
+ memset(&outtext, 0, sizeof(SIZEDTEXT)); \
+ if(!((UTF8_S *) f->opt)->charset){ \
+ for(n = 0; n < intext.size; n++) \
+ GF_PUTC(f->next, (intext.data[n] & 0x80) ? '?' : intext.data[n]); \
+ } \
+ else if(utf8_text_cs(&intext, ((UTF8_S *) f->opt)->charset, &outtext, NULL, NULL)){ \
+ for(n = 0; n < outtext.size; n++) \
+ GF_PUTC(f->next, outtext.data[n]); \
+ if(outtext.data && intext.data != outtext.data) \
+ fs_give((void **) &outtext.data); \
+ } \
+ else{ \
+ for(n = 0; n < intext.size; n++) \
+ GF_PUTC(f->next, '?'); \
+ } \
+ }
+
+
+/*
+ * gf_utf8 - text in specified charset to to UTF-8 filter
+ * Process line-at-a-time rather than character
+ * because ISO-2022-JP. Call utf8_text_cs by hand
+ * rather than utf8_text to reduce the cost of
+ * utf8_charset() for each line.
+ */
+void
+gf_utf8(FILTER_S *f, int flg)
+{
+ register char *p = f->linep;
+ register char *eobuf = UTF8_EOB(f);
+ GF_INIT(f, f->next);
+
+ if(flg == GF_DATA){
+ register int state = f->f1;
+ register unsigned char c;
+
+ while(GF_GETC(f, c)){
+
+ switch(state){
+ case CCR :
+ state = DFL;
+ if(c == '\012'){
+ GF_UTF8_FLUSH(f);
+ p = f->line;
+ GF_PUTC(f->next, '\015');
+ GF_PUTC(f->next, '\012');
+ }
+ else{
+ UTF8_ADD(f, '\015');
+ UTF8_ADD(f, c);
+ }
+
+ break;
+
+ default :
+ if(c == '\015'){
+ state = CCR;
+ }
+ else
+ UTF8_ADD(f, c);
+ }
+ }
+
+ f->f1 = state;
+ GF_END(f, f->next);
+ }
+ else if(flg == GF_EOD){
+
+ if(p != f->line)
+ GF_UTF8_FLUSH(f);
+
+ fs_give((void **) &f->line);
+ fs_give((void **) &f->opt);
+ (void) GF_FLUSH(f->next);
+ (*f->next->f)(f->next, GF_EOD);
+ }
+ else if(GF_RESET){
+ dprint((9, "-- gf_reset utf8\n"));
+ f->f1 = DFL;
+ f->f2 = UTF8_BLOCK; /* input buffer length */
+ f->line = p = (char *) fs_get(f->f2 * sizeof(char));
+ }
+
+ f->linep = p;
+}
+
+
+void *
+gf_utf8_opt(char *charset)
+{
+ UTF8_S *utf8;
+
+ utf8 = (UTF8_S *) fs_get(sizeof(UTF8_S));
+
+ utf8->charset = (CHARSET *) utf8_charset(charset);
+
+ /*
+ * When we get 8-bit non-ascii characters but it is supposed to
+ * be ascii we want it to turn into question marks, not
+ * just behave as if it is UTF-8 which is what happens
+ * with ascii because there is no translation table.
+ * So we need to catch the ascii special case here.
+ */
+ if(utf8->charset && utf8->charset->type == CT_ASCII)
+ utf8->charset = NULL;
+
+ return((void *) utf8);
+}
+
+
+/*
+ * RICHTEXT-TO-PLAINTEXT filter
+ */
+
+/*
+ * option to be used by rich2plain (NOTE: if this filter is ever
+ * used more than once in a pipe, all instances will have the same
+ * option value)
+ */
+
+
+/*----------------------------------------------------------------------
+ richtext to plaintext filter
+
+ Args: f --
+ flg --
+
+ This basically removes all richtext formatting. A cute hack is used
+ to get bold and underlining to work.
+ Further work could be done to handle things like centering and right
+ and left flush, but then it could no longer be done in place. This
+ operates on text *with* CRLF's.
+
+ WARNING: does not wrap lines!
+ ----*/
+void
+gf_rich2plain(FILTER_S *f, int flg)
+{
+ static int rich_bold_on = 0, rich_uline_on = 0;
+
+/* BUG: qoute incoming \255 values */
+ GF_INIT(f, f->next);
+
+ if(flg == GF_DATA){
+ register unsigned char c;
+ register int state = f->f1;
+ register int plain;
+
+ plain = f->opt ? (*(int *) f->opt) : 0;
+
+ while(GF_GETC(f, c)){
+
+ switch(state){
+ case TOKEN : /* collect a richtext token */
+ if(c == '>'){ /* what should we do with it? */
+ state = DFL; /* return to default next time */
+ *(f->linep) = '\0'; /* cap off token */
+ if(f->line[0] == 'l' && f->line[1] == 't'){
+ GF_PUTC(f->next, '<'); /* literal '<' */
+ }
+ else if(f->line[0] == 'n' && f->line[1] == 'l'){
+ GF_PUTC(f->next, '\015');/* newline! */
+ GF_PUTC(f->next, '\012');
+ }
+ else if(!strcmp("comment", f->line)){
+ (f->f2)++;
+ }
+ else if(!strcmp("/comment", f->line)){
+ f->f2 = 0;
+ }
+ else if(!strcmp("/paragraph", f->line)) {
+ GF_PUTC(f->next, '\r');
+ GF_PUTC(f->next, '\n');
+ GF_PUTC(f->next, '\r');
+ GF_PUTC(f->next, '\n');
+ }
+ else if(!plain /* gf_rich_plain */){
+ if(!strcmp(f->line, "bold")) {
+ GF_PUTC(f->next, TAG_EMBED);
+ GF_PUTC(f->next, TAG_BOLDON);
+ rich_bold_on = 1;
+ } else if(!strcmp(f->line, "/bold")) {
+ GF_PUTC(f->next, TAG_EMBED);
+ GF_PUTC(f->next, TAG_BOLDOFF);
+ rich_bold_on = 0;
+ } else if(!strcmp(f->line, "italic")) {
+ GF_PUTC(f->next, TAG_EMBED);
+ GF_PUTC(f->next, TAG_ULINEON);
+ rich_uline_on = 1;
+ } else if(!strcmp(f->line, "/italic")) {
+ GF_PUTC(f->next, TAG_EMBED);
+ GF_PUTC(f->next, TAG_ULINEOFF);
+ rich_uline_on = 0;
+ } else if(!strcmp(f->line, "underline")) {
+ GF_PUTC(f->next, TAG_EMBED);
+ GF_PUTC(f->next, TAG_ULINEON);
+ rich_uline_on = 1;
+ } else if(!strcmp(f->line, "/underline")) {
+ GF_PUTC(f->next, TAG_EMBED);
+ GF_PUTC(f->next, TAG_ULINEOFF);
+ rich_uline_on = 0;
+ }
+ }
+ /* else we just ignore the token! */
+
+ f->linep = f->line; /* reset token buffer */
+ }
+ else{ /* add char to token */
+ if(f->linep - f->line > 40){
+ /* What? rfc1341 says 40 char tokens MAX! */
+ fs_give((void **)&(f->line));
+ gf_error("Richtext token over 40 characters");
+ /* NO RETURN */
+ }
+
+ *(f->linep)++ = isupper((unsigned char)c) ? c-'A'+'a' : c;
+ }
+ break;
+
+ case CCR :
+ state = DFL; /* back to default next time */
+ if(c == '\012'){ /* treat as single space? */
+ GF_PUTC(f->next, ' ');
+ break;
+ }
+ /* fall thru to process c */
+
+ case DFL :
+ default:
+ if(c == '<')
+ state = TOKEN;
+ else if(c == '\015')
+ state = CCR;
+ else if(!f->f2) /* not in comment! */
+ GF_PUTC(f->next, c);
+
+ break;
+ }
+ }
+
+ f->f1 = state;
+ GF_END(f, f->next);
+ }
+ else if(flg == GF_EOD){
+ if((f->f1 = (f->linep != f->line)) != 0){
+ /* incomplete token!! */
+ gf_error("Incomplete token in richtext");
+ /* NO RETURN */
+ }
+
+ if(rich_uline_on){
+ GF_PUTC(f->next, TAG_EMBED);
+ GF_PUTC(f->next, TAG_ULINEOFF);
+ rich_uline_on = 0;
+ }
+ if(rich_bold_on){
+ GF_PUTC(f->next, TAG_EMBED);
+ GF_PUTC(f->next, TAG_BOLDOFF);
+ rich_bold_on = 0;
+ }
+
+ fs_give((void **)&(f->line));
+ (void) GF_FLUSH(f->next);
+ (*f->next->f)(f->next, GF_EOD);
+ }
+ else if(flg == GF_RESET){
+ dprint((9, "-- gf_reset rich2plain\n"));
+ f->f1 = DFL; /* state */
+ f->f2 = 0; /* set means we're in a comment */
+ f->linep = f->line = (char *)fs_get(45 * sizeof(char));
+ }
+}
+
+
+/*
+ * function called from the outside to set
+ * richtext filter's options
+ */
+void *
+gf_rich2plain_opt(int *plain)
+{
+ return((void *) plain);
+}
+
+
+
+/*
+ * ENRICHED-TO-PLAIN text filter
+ */
+
+#define TEF_QUELL 0x01
+#define TEF_NOFILL 0x02
+
+
+
+/*----------------------------------------------------------------------
+ enriched text to plain text filter (ala rfc1523)
+
+ Args: f -- state and input data
+ flg --
+
+ This basically removes all enriched formatting. A cute hack is used
+ to get bold and underlining to work.
+
+ Further work could be done to handle things like centering and right
+ and left flush, but then it could no longer be done in place. This
+ operates on text *with* CRLF's.
+
+ WARNING: does not wrap lines!
+ ----*/
+void
+gf_enriched2plain(FILTER_S *f, int flg)
+{
+ static int enr_uline_on = 0, enr_bold_on = 0;
+
+/* BUG: qoute incoming \255 values */
+ GF_INIT(f, f->next);
+
+ if(flg == GF_DATA){
+ register unsigned char c;
+ register int state = f->f1;
+ register int plain;
+
+ plain = f->opt ? (*(int *) f->opt) : 0;
+
+ while(GF_GETC(f, c)){
+
+ switch(state){
+ case TOKEN : /* collect a richtext token */
+ if(c == '>'){ /* what should we do with it? */
+ int off = *f->line == '/';
+ char *token = f->line + (off ? 1 : 0);
+ state = DFL;
+ *f->linep = '\0';
+ if(!strcmp("param", token)){
+ if(off)
+ f->f2 &= ~TEF_QUELL;
+ else
+ f->f2 |= TEF_QUELL;
+ }
+ else if(!strcmp("nofill", token)){
+ if(off)
+ f->f2 &= ~TEF_NOFILL;
+ else
+ f->f2 |= TEF_NOFILL;
+ }
+ else if(!plain /* gf_enriched_plain */){
+ /* Following is a cute hack or two to get
+ bold and underline on the screen.
+ See Putline0n() where these codes are
+ interpreted */
+ if(!strcmp("bold", token)) {
+ GF_PUTC(f->next, TAG_EMBED);
+ GF_PUTC(f->next, off ? TAG_BOLDOFF : TAG_BOLDON);
+ enr_bold_on = off ? 0 : 1;
+ } else if(!strcmp("italic", token)) {
+ GF_PUTC(f->next, TAG_EMBED);
+ GF_PUTC(f->next, off ? TAG_ULINEOFF : TAG_ULINEON);
+ enr_uline_on = off ? 0 : 1;
+ } else if(!strcmp("underline", token)) {
+ GF_PUTC(f->next, TAG_EMBED);
+ GF_PUTC(f->next, off ? TAG_ULINEOFF : TAG_ULINEON);
+ enr_uline_on = off ? 0 : 1;
+ }
+ }
+ /* else we just ignore the token! */
+
+ f->linep = f->line; /* reset token buffer */
+ }
+ else if(c == '<'){ /* literal '<'? */
+ if(f->linep == f->line){
+ GF_PUTC(f->next, '<');
+ state = DFL;
+ }
+ else{
+ fs_give((void **)&(f->line));
+ gf_error("Malformed Enriched text: unexpected '<'");
+ /* NO RETURN */
+ }
+ }
+ else{ /* add char to token */
+ if(f->linep - f->line > 60){ /* rfc1523 says 60 MAX! */
+ fs_give((void **)&(f->line));
+ gf_error("Malformed Enriched text: token too long");
+ /* NO RETURN */
+ }
+
+ *(f->linep)++ = isupper((unsigned char)c) ? c-'A'+'a' : c;
+ }
+ break;
+
+ case CCR :
+ if(c != '\012'){ /* treat as single space? */
+ state = DFL; /* lone cr? */
+ f->f2 &= ~TEF_QUELL;
+ GF_PUTC(f->next, '\015');
+ goto df;
+ }
+
+ state = CLF;
+ break;
+
+ case CLF :
+ if(c == '\015'){ /* treat as single space? */
+ state = CCR; /* repeat crlf's mean real newlines */
+ f->f2 |= TEF_QUELL;
+ GF_PUTC(f->next, '\r');
+ GF_PUTC(f->next, '\n');
+ break;
+ }
+ else{
+ state = DFL;
+ if(!((f->f2) & TEF_QUELL))
+ GF_PUTC(f->next, ' ');
+
+ f->f2 &= ~TEF_QUELL;
+ }
+
+ /* fall thru to take care of 'c' */
+
+ case DFL :
+ default :
+ df :
+ if(c == '<')
+ state = TOKEN;
+ else if(c == '\015' && (!((f->f2) & TEF_NOFILL)))
+ state = CCR;
+ else if(!((f->f2) & TEF_QUELL))
+ GF_PUTC(f->next, c);
+
+ break;
+ }
+ }
+
+ f->f1 = state;
+ GF_END(f, f->next);
+ }
+ else if(flg == GF_EOD){
+ if((f->f1 = (f->linep != f->line)) != 0){
+ /* incomplete token!! */
+ gf_error("Incomplete token in richtext");
+ /* NO RETURN */
+ }
+ if(enr_uline_on){
+ GF_PUTC(f->next, TAG_EMBED);
+ GF_PUTC(f->next, TAG_ULINEOFF);
+ enr_uline_on = 0;
+ }
+ if(enr_bold_on){
+ GF_PUTC(f->next, TAG_EMBED);
+ GF_PUTC(f->next, TAG_BOLDOFF);
+ enr_bold_on = 0;
+ }
+
+ /* Make sure we end with a newline so everything gets flushed */
+ GF_PUTC(f->next, '\015');
+ GF_PUTC(f->next, '\012');
+
+ fs_give((void **)&(f->line));
+
+ (void) GF_FLUSH(f->next);
+ (*f->next->f)(f->next, GF_EOD);
+ }
+ else if(flg == GF_RESET){
+ dprint((9, "-- gf_reset enriched2plain\n"));
+ f->f1 = DFL; /* state */
+ f->f2 = 0; /* set means we're in a comment */
+ f->linep = f->line = (char *)fs_get(65 * sizeof(char));
+ }
+}
+
+
+/*
+ * function called from the outside to set
+ * richtext filter's options
+ */
+void *
+gf_enriched2plain_opt(int *plain)
+{
+ return((void *) plain);
+}
+
+
+
+/*
+ * HTML-TO-PLAIN text filter
+ */
+
+
+/* OK, here's the plan:
+
+ * a universal output function handles writing chars and worries
+ * about wrapping.
+
+ * a unversal element collector reads chars and collects params
+ * and dispatches the appropriate element handler.
+
+ * element handlers are stacked. The most recently dispatched gets
+ * first crack at the incoming character stream. It passes bytes it's
+ * done with or not interested in to the next
+
+ * installs that handler as the current one collecting data...
+
+ * stacked handlers take their params from the element collector and
+ * accept chars or do whatever they need to do. Sort of a vertical
+ * piping? recursion-like? hmmm.
+
+ * at least I think this is how it'll work. tres simple, non?
+
+ */
+
+
+/*
+ * Some important constants
+ */
+#define HTML_BUF_LEN 2048 /* max scratch buffer length */
+#define MAX_ENTITY 20 /* maximum length of an entity */
+#define MAX_ELEMENT 72 /* maximum length of an element */
+#define HTML_MOREDATA 0 /* expect more entity data */
+#define HTML_ENTITY 1 /* valid entity collected */
+#define HTML_BADVALUE 0x0100 /* good data, but bad entity value */
+#define HTML_BADDATA 0x0200 /* bad data found looking for entity */
+#define HTML_LITERAL 0x0400 /* Literal character value */
+#define HTML_NEWLINE 0x010A /* hard newline */
+#define HTML_DOBOLD 0x0400 /* Start Bold display */
+#define HTML_ID_GET 0 /* indent func: return current val */
+#define HTML_ID_SET 1 /* indent func: set to absolute val */
+#define HTML_ID_INC 2 /* indent func: increment by val */
+#define HTML_HX_CENTER 0x0001
+#define HTML_HX_ULINE 0x0002
+#define RSS_ITEM_LIMIT 20 /* RSS 2.0 ITEM depth limit */
+
+
+/*
+ * Handler data, state information including function that uses it
+ */
+typedef struct handler_s {
+ FILTER_S *html_data;
+ void *element;
+ long x, y, z;
+ void *dp;
+ unsigned char *s;
+ struct handler_s *below;
+} HANDLER_S;
+
+/*
+ * Element Property structure
+ */
+typedef struct _element_properties {
+ char *element;
+ int (*handler)(HANDLER_S *, int, int);
+ unsigned blocklevel:1;
+} ELPROP_S;
+
+/*
+ * Types used to manage HTML parsing
+ */
+static void html_handoff(HANDLER_S *, int);
+
+
+/*
+ * to help manage line wrapping.
+ */
+typedef struct _wrap_line {
+ char *buf; /* buf to collect wrapped text */
+ int used, /* number of chars in buf */
+ width, /* text's width as displayed */
+ len; /* length of allocated buf */
+} WRAPLINE_S;
+
+
+/*
+ * to help manage centered text
+ */
+typedef struct _center_s {
+ WRAPLINE_S line; /* buf to assembled centered text */
+ WRAPLINE_S word; /* word being to append to Line */
+ int anchor;
+ short space;
+} CENTER_S;
+
+
+/*
+ * Collector data and state information
+ */
+typedef struct collector_s {
+ char buf[HTML_BUF_LEN]; /* buffer to collect data */
+ int len; /* length of that buffer */
+ unsigned end_tag:1; /* collecting a closing tag */
+ unsigned hit_equal:1; /* collecting right half of attrib */
+ unsigned mkup_decl:1; /* markup declaration */
+ unsigned start_comment:1; /* markup declaration comment */
+ unsigned end_comment:1; /* legit comment format */
+ unsigned hyphen:1; /* markup hyphen read */
+ unsigned badform:1; /* malformed markup element */
+ unsigned overrun:1; /* Overran buf above */
+ unsigned proc_inst:1; /* XML processing instructions */
+ unsigned empty:1; /* empty element */
+ unsigned was_quoted:1; /* basically to catch null string */
+ char quoted; /* quoted element param value */
+ char *element; /* element's collected name */
+ PARAMETER *attribs; /* element's collected attributes */
+ PARAMETER *cur_attrib; /* attribute now being collected */
+} CLCTR_S;
+
+
+/*
+ * State information for all element handlers
+ */
+typedef struct html_data {
+ HANDLER_S *h_stack; /* handler list */
+ CLCTR_S *el_data; /* element collector data */
+ CENTER_S *centered; /* struct to manage centered text */
+ int (*token)(FILTER_S *, int);
+ char quoted; /* quoted, by either ' or ", text */
+ short indent_level; /* levels of indention */
+ int in_anchor; /* text now being written to anchor */
+ int blanks; /* Consecutive blank line count */
+ int wrapcol; /* column to wrap lines on */
+ int *prefix; /* buffer containing Anchor prefix */
+ int prefix_used;
+ long line_bufsize; /* current size of the line buffer */
+ COLOR_PAIR *color;
+ struct {
+ int state; /* embedded data state */
+ char *color; /* embedded color pointer */
+ } embedded;
+ CBUF_S cb; /* utf8->ucs4 conversion state */
+ unsigned wrapstate:1; /* whether or not to wrap output */
+ unsigned li_pending:1; /* <LI> next token expected */
+ unsigned de_pending:1; /* <DT> or <DD> next token expected */
+ unsigned bold_on:1; /* currently bolding text */
+ unsigned uline_on:1; /* currently underlining text */
+ unsigned center:1; /* center output text */
+ unsigned bitbucket:1; /* Ignore input */
+ unsigned head:1; /* In doc's HEAD */
+ unsigned body:1; /* In doc's BODY */
+ unsigned alt_entity:1; /* use alternative entity values */
+ unsigned wrote:1; /* anything witten yet? */
+} HTML_DATA_S;
+
+
+/*
+ * HTML filter options
+ */
+typedef struct _html_opts {
+ char *base; /* Base URL for this html file */
+ int columns, /* Display columns (excluding margins) */
+ indent; /* Left margin */
+ HANDLE_S **handlesp; /* Head of handles */
+ htmlrisk_t warnrisk_f; /* Nasty link warning call */
+ ELPROP_S *element_table; /* markup element table */
+ RSS_FEED_S **feedp; /* hook for RSS feed response */
+ unsigned strip:1; /* Hilite TAGs allowed */
+ unsigned handles_loc:1; /* Local handles requested? */
+ unsigned showserver:1; /* Display server after anchors */
+ unsigned outputted:1; /* any */
+ unsigned no_relative_links:1; /* Disable embeded relative links */
+ unsigned related_content:1; /* Embeded related content */
+ unsigned html:1; /* Output content in HTML */
+ unsigned html_imgs:1; /* Output IMG tags in HTML content */
+} HTML_OPT_S;
+
+
+
+/*
+ * Some macros to make life a little easier
+ */
+#define WRAP_COLS(X) ((X)->opt ? ((HTML_OPT_S *)(X)->opt)->columns : 80)
+#define HTML_INDENT(X) ((X)->opt ? ((HTML_OPT_S *)(X)->opt)->indent : 0)
+#define HTML_WROTE(X) (HD(X)->wrote)
+#define HTML_BASE(X) ((X)->opt ? ((HTML_OPT_S *)(X)->opt)->base : NULL)
+#define STRIP(X) ((X)->opt && ((HTML_OPT_S *)(X)->opt)->strip)
+#define PASS_HTML(X) ((X)->opt && ((HTML_OPT_S *)(X)->opt)->html)
+#define PASS_IMAGES(X) ((X)->opt && ((HTML_OPT_S *)(X)->opt)->html_imgs)
+#define HANDLESP(X) (((HTML_OPT_S *)(X)->opt)->handlesp)
+#define DO_HANDLES(X) ((X)->opt && HANDLESP(X))
+#define HANDLES_LOC(X) ((X)->opt && ((HTML_OPT_S *)(X)->opt)->handles_loc)
+#define SHOWSERVER(X) ((X)->opt && ((HTML_OPT_S *)(X)->opt)->showserver)
+#define NO_RELATIVE(X) ((X)->opt && ((HTML_OPT_S *)(X)->opt)->no_relative_links)
+#define RELATED_OK(X) ((X)->opt && ((HTML_OPT_S *)(X)->opt)->related_content)
+#define ELEMENTS(X) (((HTML_OPT_S *)(X)->opt)->element_table)
+#define RSS_FEED(X) (*(((HTML_OPT_S *)(X)->opt)->feedp))
+#define MAKE_LITERAL(C) (HTML_LITERAL | ((C) & 0xff))
+#define IS_LITERAL(C) (HTML_LITERAL & (C))
+#define HD(X) ((HTML_DATA_S *)(X)->data)
+#define ED(X) (HD(X)->el_data)
+#define EL(X) ((ELPROP_S *) (X)->element)
+#define ASCII_ISSPACE(C) ((C) < 0x80 && isspace((unsigned char) (C)))
+#define HTML_ISSPACE(C) (IS_LITERAL(C) == 0 && ((C) == HTML_NEWLINE || ASCII_ISSPACE(C)))
+#define NEW_CLCTR(X) { \
+ ED(X) = (CLCTR_S *)fs_get(sizeof(CLCTR_S)); \
+ memset(ED(X), 0, sizeof(CLCTR_S)); \
+ HD(X)->token = html_element_collector; \
+ }
+
+#define FREE_CLCTR(X) { \
+ if(ED(X)->attribs){ \
+ PARAMETER *p; \
+ while((p = ED(X)->attribs) != NULL){ \
+ ED(X)->attribs = ED(X)->attribs->next; \
+ if(p->attribute) \
+ fs_give((void **)&p->attribute); \
+ if(p->value) \
+ fs_give((void **)&p->value); \
+ fs_give((void **)&p); \
+ } \
+ } \
+ if(ED(X)->element) \
+ fs_give((void **) &ED(X)->element); \
+ fs_give((void **) &ED(X)); \
+ HD(X)->token = NULL; \
+ }
+#define HANDLERS(X) (HD(X)->h_stack)
+#define BOLD_BIT(X) (HD(X)->bold_on)
+#define ULINE_BIT(X) (HD(X)->uline_on)
+#define CENTER_BIT(X) (HD(X)->center)
+#define HTML_FLUSH(X) { \
+ html_write(X, (X)->line, (X)->linep - (X)->line); \
+ (X)->linep = (X)->line; \
+ (X)->f2 = 0L; \
+ }
+#define HTML_BOLD(X, S) if(! STRIP(X)){ \
+ if((S)){ \
+ html_output((X), TAG_EMBED); \
+ html_output((X), TAG_BOLDON); \
+ } \
+ else if(!(S)){ \
+ html_output((X), TAG_EMBED); \
+ html_output((X), TAG_BOLDOFF); \
+ } \
+ }
+#define HTML_ULINE(X, S) \
+ if(! STRIP(X)){ \
+ if((S)){ \
+ html_output((X), TAG_EMBED); \
+ html_output((X), TAG_ULINEON); \
+ } \
+ else if(!(S)){ \
+ html_output((X), TAG_EMBED); \
+ html_output((X), TAG_ULINEOFF); \
+ } \
+ }
+#define HTML_ITALIC(X, S) \
+ if(! STRIP(X)){ \
+ if(S){ \
+ html_output((X), TAG_EMBED); \
+ html_output((X), TAG_ITALICON); \
+ } \
+ else if(!(S)){ \
+ html_output((X), TAG_EMBED); \
+ html_output((X), TAG_ITALICOFF); \
+ } \
+ }
+#define HTML_STRIKE(X, S) \
+ if(! STRIP(X)){ \
+ if(S){ \
+ html_output((X), TAG_EMBED); \
+ html_output((X), TAG_STRIKEON); \
+ } \
+ else if(!(S)){ \
+ html_output((X), TAG_EMBED); \
+ html_output((X), TAG_STRIKEOFF); \
+ } \
+ }
+#define HTML_BIG(X, S) \
+ if(! STRIP(X)){ \
+ if(S){ \
+ html_output((X), TAG_EMBED); \
+ html_output((X), TAG_BIGON); \
+ } \
+ else if(!(S)){ \
+ html_output((X), TAG_EMBED); \
+ html_output((X), TAG_BIGOFF); \
+ } \
+ }
+#define HTML_SMALL(X, S) \
+ if(! STRIP(X)){ \
+ if(S){ \
+ html_output((X), TAG_EMBED); \
+ html_output((X), TAG_SMALLON); \
+ } \
+ else if(!(S)){ \
+ html_output((X), TAG_EMBED); \
+ html_output((X), TAG_SMALLOFF); \
+ } \
+ }
+#define WRAPPED_LEN(X) ((HD(f)->centered) \
+ ? (HD(f)->centered->line.width \
+ + HD(f)->centered->word.width \
+ + ((HD(f)->centered->line.width \
+ && HD(f)->centered->word.width) \
+ ? 1 : 0)) \
+ : 0)
+#define HTML_DUMP_LIT(F, S, L) { \
+ int i, c; \
+ for(i = 0; i < (L); i++){ \
+ c = ASCII_ISSPACE((unsigned char)(S)[i]) \
+ ? (S)[i] \
+ : MAKE_LITERAL((S)[i]); \
+ HTML_TEXT(F, c); \
+ } \
+ }
+#define HTML_PROC(F, C) { \
+ if(HD(F)->token){ \
+ int i; \
+ if((i = (*(HD(F)->token))(F, C)) != 0){ \
+ if(i < 0){ \
+ HTML_DUMP_LIT(F, "<", 1); \
+ if(HD(F)->el_data->element){ \
+ HTML_DUMP_LIT(F, \
+ HD(F)->el_data->element, \
+ strlen(HD(F)->el_data->element));\
+ } \
+ if(HD(F)->el_data->len){ \
+ HTML_DUMP_LIT(F, \
+ HD(F)->el_data->buf, \
+ HD(F)->el_data->len); \
+ } \
+ HTML_TEXT(F, C); \
+ } \
+ FREE_CLCTR(F); \
+ } \
+ } \
+ else if((C) == '<'){ \
+ NEW_CLCTR(F); \
+ } \
+ else \
+ HTML_TEXT(F, C); \
+ }
+#define HTML_LINEP_PUTC(F, C) { \
+ if((F)->linep - (F)->line >= (HD(F)->line_bufsize - 1)){ \
+ size_t offset = (F)->linep - (F)->line; \
+ fs_resize((void **) &(F)->line, \
+ (HD(F)->line_bufsize * 2) * sizeof(char)); \
+ HD(F)->line_bufsize *= 2; \
+ (F)->linep = &(F)->line[offset]; \
+ } \
+ *(F)->linep++ = (C); \
+ }
+#define HTML_TEXT(F, C) switch((F)->f1){ \
+ case WSPACE : \
+ if(HTML_ISSPACE(C)) /* ignore repeated WS */ \
+ break; \
+ HTML_TEXT_OUT(F, ' '); \
+ (F)->f1 = DFL;/* stop sending chars here */ \
+ /* fall thru to process 'c' */ \
+ case DFL: \
+ if(HD(F)->bitbucket) \
+ (F)->f1 = DFL; /* no op */ \
+ else if(HTML_ISSPACE(C) && HD(F)->wrapstate) \
+ (F)->f1 = WSPACE;/* coalesce white space */ \
+ else HTML_TEXT_OUT(F, C); \
+ break; \
+ }
+#define HTML_TEXT_OUT(F, C) if(HANDLERS(F)) /* let handlers see C */ \
+ (*EL(HANDLERS(F))->handler)(HANDLERS(F),(C),GF_DATA); \
+ else \
+ html_output(F, C);
+#ifdef DEBUG
+#define HTML_DEBUG_EL(S, D) { \
+ dprint((5, "-- html %s: %s\n", \
+ S ? S : "?", \
+ (D)->element \
+ ? (D)->element : "NULL")); \
+ if(debug > 5){ \
+ PARAMETER *p; \
+ for(p = (D)->attribs; \
+ p && p->attribute; \
+ p = p->next) \
+ dprint((6, \
+ " PARM: %s%s%s\n", \
+ p->attribute \
+ ? p->attribute : "NULL",\
+ p->value ? "=" : "", \
+ p->value ? p->value : ""));\
+ } \
+ }
+#else
+#define HTML_DEBUG_EL(S, D)
+#endif
+
+#ifndef SYSTEM_PINE_INFO_PATH
+#define SYSTEM_PINE_INFO_PATH "/usr/local/lib/pine.info"
+#endif
+#define CHTML_VAR_EXPAND(S) (!strcmp(S, "PINE_INFO_PATH") \
+ ? SYSTEM_PINE_INFO_PATH : S)
+
+/*
+ * Protos for Tag handlers
+ */
+int html_head(HANDLER_S *, int, int);
+int html_base(HANDLER_S *, int, int);
+int html_title(HANDLER_S *, int, int);
+int html_body(HANDLER_S *, int, int);
+int html_a(HANDLER_S *, int, int);
+int html_br(HANDLER_S *, int, int);
+int html_hr(HANDLER_S *, int, int);
+int html_p(HANDLER_S *, int, int);
+int html_table(HANDLER_S *, int, int);
+int html_caption(HANDLER_S *, int, int);
+int html_tr(HANDLER_S *, int, int);
+int html_td(HANDLER_S *, int, int);
+int html_th(HANDLER_S *, int, int);
+int html_thead(HANDLER_S *, int, int);
+int html_tbody(HANDLER_S *, int, int);
+int html_tfoot(HANDLER_S *, int, int);
+int html_col(HANDLER_S *, int, int);
+int html_colgroup(HANDLER_S *, int, int);
+int html_b(HANDLER_S *, int, int);
+int html_u(HANDLER_S *, int, int);
+int html_i(HANDLER_S *, int, int);
+int html_em(HANDLER_S *, int, int);
+int html_strong(HANDLER_S *, int, int);
+int html_s(HANDLER_S *, int, int);
+int html_big(HANDLER_S *, int, int);
+int html_small(HANDLER_S *, int, int);
+int html_font(HANDLER_S *, int, int);
+int html_img(HANDLER_S *, int, int);
+int html_map(HANDLER_S *, int, int);
+int html_area(HANDLER_S *, int, int);
+int html_form(HANDLER_S *, int, int);
+int html_input(HANDLER_S *, int, int);
+int html_option(HANDLER_S *, int, int);
+int html_optgroup(HANDLER_S *, int, int);
+int html_button(HANDLER_S *, int, int);
+int html_select(HANDLER_S *, int, int);
+int html_textarea(HANDLER_S *, int, int);
+int html_label(HANDLER_S *, int, int);
+int html_fieldset(HANDLER_S *, int, int);
+int html_ul(HANDLER_S *, int, int);
+int html_ol(HANDLER_S *, int, int);
+int html_menu(HANDLER_S *, int, int);
+int html_dir(HANDLER_S *, int, int);
+int html_li(HANDLER_S *, int, int);
+int html_h1(HANDLER_S *, int, int);
+int html_h2(HANDLER_S *, int, int);
+int html_h3(HANDLER_S *, int, int);
+int html_h4(HANDLER_S *, int, int);
+int html_h5(HANDLER_S *, int, int);
+int html_h6(HANDLER_S *, int, int);
+int html_blockquote(HANDLER_S *, int, int);
+int html_address(HANDLER_S *, int, int);
+int html_pre(HANDLER_S *, int, int);
+int html_center(HANDLER_S *, int, int);
+int html_div(HANDLER_S *, int, int);
+int html_span(HANDLER_S *, int, int);
+int html_dl(HANDLER_S *, int, int);
+int html_dt(HANDLER_S *, int, int);
+int html_dd(HANDLER_S *, int, int);
+int html_script(HANDLER_S *, int, int);
+int html_applet(HANDLER_S *, int, int);
+int html_style(HANDLER_S *, int, int);
+int html_kbd(HANDLER_S *, int, int);
+int html_dfn(HANDLER_S *, int, int);
+int html_var(HANDLER_S *, int, int);
+int html_tt(HANDLER_S *, int, int);
+int html_samp(HANDLER_S *, int, int);
+int html_sub(HANDLER_S *, int, int);
+int html_sup(HANDLER_S *, int, int);
+int html_cite(HANDLER_S *, int, int);
+int html_code(HANDLER_S *, int, int);
+int html_ins(HANDLER_S *, int, int);
+int html_del(HANDLER_S *, int, int);
+int html_abbr(HANDLER_S *, int, int);
+
+/*
+ * Protos for RSS 2.0 Tag handlers
+ */
+int rss_rss(HANDLER_S *, int, int);
+int rss_channel(HANDLER_S *, int, int);
+int rss_title(HANDLER_S *, int, int);
+int rss_image(HANDLER_S *, int, int);
+int rss_link(HANDLER_S *, int, int);
+int rss_description(HANDLER_S *, int, int);
+int rss_ttl(HANDLER_S *, int, int);
+int rss_item(HANDLER_S *, int, int);
+
+/*
+ * Proto's for support routines
+ */
+void html_pop(FILTER_S *, ELPROP_S *);
+int html_push(FILTER_S *, ELPROP_S *);
+int html_element_collector(FILTER_S *, int);
+int html_element_flush(CLCTR_S *);
+void html_element_comment(FILTER_S *, char *);
+void html_element_output(FILTER_S *, int);
+int html_entity_collector(FILTER_S *, int, UCS *, char **);
+void html_a_prefix(FILTER_S *);
+void html_a_finish(HANDLER_S *);
+void html_a_output_prefix(FILTER_S *, int);
+void html_a_output_info(HANDLER_S *);
+void html_a_relative(char *, char *, HANDLE_S *);
+int html_href_relative(char *);
+int html_indent(FILTER_S *, int, int);
+void html_blank(FILTER_S *, int);
+void html_newline(FILTER_S *);
+void html_output(FILTER_S *, int);
+void html_output_string(FILTER_S *, char *);
+void html_output_raw_tag(FILTER_S *, char *);
+void html_output_normal(FILTER_S *, int, int);
+void html_output_flush(FILTER_S *);
+void html_output_centered(FILTER_S *, int, int);
+void html_centered_handle(int *, char *, int);
+void html_centered_putc(WRAPLINE_S *, int);
+void html_centered_flush(FILTER_S *);
+void html_centered_flush_line(FILTER_S *);
+void html_write_anchor(FILTER_S *, int);
+void html_write_newline(FILTER_S *);
+void html_write_indent(FILTER_S *, int);
+void html_write(FILTER_S *, char *, int);
+void html_putc(FILTER_S *, int);
+int html_event_attribute(char *);
+char *rss_skip_whitespace(char *s);
+ELPROP_S *element_properties(FILTER_S *, char *);
+
+
+/*
+ * Named entity table -- most from HTML 2.0 (rfc1866) plus some from
+ * W3C doc "Additional named entities for HTML"
+ */
+static struct html_entities {
+ char *name; /* entity name */
+ UCS value; /* UCS entity value */
+ char *plain; /* US-ASCII representation */
+} entity_tab[] = {
+ {"quot", 0x0022}, /* 34 - quotation mark */
+ {"amp", 0x0026}, /* 38 - ampersand */
+ {"apos", 0x0027}, /* 39 - apostrophe */
+ {"lt", 0x003C}, /* 60 - less-than sign */
+ {"gt", 0x003E}, /* 62 - greater-than sign */
+ {"nbsp", 0x00A0, " "}, /* 160 - no-break space */
+ {"iexcl", 0x00A1}, /* 161 - inverted exclamation mark */
+ {"cent", 0x00A2}, /* 162 - cent sign */
+ {"pound", 0x00A3}, /* 163 - pound sign */
+ {"curren", 0x00A4, "CUR"}, /* 164 - currency sign */
+ {"yen", 0x00A5}, /* 165 - yen sign */
+ {"brvbar", 0x00A6, "|"}, /* 166 - broken bar */
+ {"sect", 0x00A7}, /* 167 - section sign */
+ {"uml", 0x00A8, "\""}, /* 168 - diaeresis */
+ {"copy", 0x00A9, "(C)"}, /* 169 - copyright sign */
+ {"ordf", 0x00AA, "a"}, /* 170 - feminine ordinal indicator */
+ {"laquo", 0x00AB, "<<"}, /* 171 - left-pointing double angle quotation mark */
+ {"not", 0x00AC, "NOT"}, /* 172 - not sign */
+ {"shy", 0x00AD, "-"}, /* 173 - soft hyphen */
+ {"reg", 0x00AE, "(R)"}, /* 174 - registered sign */
+ {"macr", 0x00AF}, /* 175 - macron */
+ {"deg", 0x00B0, "DEG"}, /* 176 - degree sign */
+ {"plusmn", 0x00B1, "+/-"}, /* 177 - plus-minus sign */
+ {"sup2", 0x00B2}, /* 178 - superscript two */
+ {"sup3", 0x00B3}, /* 179 - superscript three */
+ {"acute", 0x00B4, "'"}, /* 180 - acute accent */
+ {"micro", 0x00B5}, /* 181 - micro sign */
+ {"para", 0x00B6}, /* 182 - pilcrow sign */
+ {"middot", 0x00B7}, /* 183 - middle dot */
+ {"cedil", 0x00B8}, /* 184 - cedilla */
+ {"sup1", 0x00B9}, /* 185 - superscript one */
+ {"ordm", 0x00BA, "o"}, /* 186 - masculine ordinal indicator */
+ {"raquo", 0x00BB, ">>"}, /* 187 - right-pointing double angle quotation mark */
+ {"frac14", 0x00BC, " 1/4"}, /* 188 - vulgar fraction one quarter */
+ {"frac12", 0x00BD, " 1/2"}, /* 189 - vulgar fraction one half */
+ {"frac34", 0x00BE, " 3/4"}, /* 190 - vulgar fraction three quarters */
+ {"iquest", 0x00BF}, /* 191 - inverted question mark */
+ {"Agrave", 0x00C0, "A"}, /* 192 - latin capital letter a with grave */
+ {"Aacute", 0x00C1, "A"}, /* 193 - latin capital letter a with acute */
+ {"Acirc", 0x00C2, "A"}, /* 194 - latin capital letter a with circumflex */
+ {"Atilde", 0x00C3, "A"}, /* 195 - latin capital letter a with tilde */
+ {"Auml", 0x00C4, "AE"}, /* 196 - latin capital letter a with diaeresis */
+ {"Aring", 0x00C5, "A"}, /* 197 - latin capital letter a with ring above */
+ {"AElig", 0x00C6, "AE"}, /* 198 - latin capital letter ae */
+ {"Ccedil", 0x00C7, "C"}, /* 199 - latin capital letter c with cedilla */
+ {"Egrave", 0x00C8, "E"}, /* 200 - latin capital letter e with grave */
+ {"Eacute", 0x00C9, "E"}, /* 201 - latin capital letter e with acute */
+ {"Ecirc", 0x00CA, "E"}, /* 202 - latin capital letter e with circumflex */
+ {"Euml", 0x00CB, "E"}, /* 203 - latin capital letter e with diaeresis */
+ {"Igrave", 0x00CC, "I"}, /* 204 - latin capital letter i with grave */
+ {"Iacute", 0x00CD, "I"}, /* 205 - latin capital letter i with acute */
+ {"Icirc", 0x00CE, "I"}, /* 206 - latin capital letter i with circumflex */
+ {"Iuml", 0x00CF, "I"}, /* 207 - latin capital letter i with diaeresis */
+ {"ETH", 0x00D0, "DH"}, /* 208 - latin capital letter eth */
+ {"Ntilde", 0x00D1, "N"}, /* 209 - latin capital letter n with tilde */
+ {"Ograve", 0x00D2, "O"}, /* 210 - latin capital letter o with grave */
+ {"Oacute", 0x00D3, "O"}, /* 211 - latin capital letter o with acute */
+ {"Ocirc", 0x00D4, "O"}, /* 212 - latin capital letter o with circumflex */
+ {"Otilde", 0x00D5, "O"}, /* 213 - latin capital letter o with tilde */
+ {"Ouml", 0x00D6, "O"}, /* 214 - latin capital letter o with diaeresis */
+ {"times", 0x00D7, "x"}, /* 215 - multiplication sign */
+ {"Oslash", 0x00D8, "O"}, /* 216 - latin capital letter o with stroke */
+ {"Ugrave", 0x00D9, "U"}, /* 217 - latin capital letter u with grave */
+ {"Uacute", 0x00DA, "U"}, /* 218 - latin capital letter u with acute */
+ {"Ucirc", 0x00DB, "U"}, /* 219 - latin capital letter u with circumflex */
+ {"Uuml", 0x00DC, "UE"}, /* 220 - latin capital letter u with diaeresis */
+ {"Yacute", 0x00DD, "Y"}, /* 221 - latin capital letter y with acute */
+ {"THORN", 0x00DE, "P"}, /* 222 - latin capital letter thorn */
+ {"szlig", 0x00DF, "ss"}, /* 223 - latin small letter sharp s (German <a href="/wiki/Eszett" title="Eszett">Eszett</a>) */
+ {"agrave", 0x00E0, "a"}, /* 224 - latin small letter a with grave */
+ {"aacute", 0x00E1, "a"}, /* 225 - latin small letter a with acute */
+ {"acirc", 0x00E2, "a"}, /* 226 - latin small letter a with circumflex */
+ {"atilde", 0x00E3, "a"}, /* 227 - latin small letter a with tilde */
+ {"auml", 0x00E4, "ae"}, /* 228 - latin small letter a with diaeresis */
+ {"aring", 0x00E5, "a"}, /* 229 - latin small letter a with ring above */
+ {"aelig", 0x00E6, "ae"}, /* 230 - latin lowercase ligature ae */
+ {"ccedil", 0x00E7, "c"}, /* 231 - latin small letter c with cedilla */
+ {"egrave", 0x00E8, "e"}, /* 232 - latin small letter e with grave */
+ {"eacute", 0x00E9, "e"}, /* 233 - latin small letter e with acute */
+ {"ecirc", 0x00EA, "e"}, /* 234 - latin small letter e with circumflex */
+ {"euml", 0x00EB, "e"}, /* 235 - latin small letter e with diaeresis */
+ {"igrave", 0x00EC, "i"}, /* 236 - latin small letter i with grave */
+ {"iacute", 0x00ED, "i"}, /* 237 - latin small letter i with acute */
+ {"icirc", 0x00EE, "i"}, /* 238 - latin small letter i with circumflex */
+ {"iuml", 0x00EF, "i"}, /* 239 - latin small letter i with diaeresis */
+ {"eth", 0x00F0, "dh"}, /* 240 - latin small letter eth */
+ {"ntilde", 0x00F1, "n"}, /* 241 - latin small letter n with tilde */
+ {"ograve", 0x00F2, "o"}, /* 242 - latin small letter o with grave */
+ {"oacute", 0x00F3, "o"}, /* 243 - latin small letter o with acute */
+ {"ocirc", 0x00F4, "o"}, /* 244 - latin small letter o with circumflex */
+ {"otilde", 0x00F5, "o"}, /* 245 - latin small letter o with tilde */
+ {"ouml", 0x00F6, "oe"}, /* 246 - latin small letter o with diaeresis */
+ {"divide", 0x00F7, "/"}, /* 247 - division sign */
+ {"oslash", 0x00F8, "o"}, /* 248 - latin small letter o with stroke */
+ {"ugrave", 0x00F9, "u"}, /* 249 - latin small letter u with grave */
+ {"uacute", 0x00FA, "u"}, /* 250 - latin small letter u with acute */
+ {"ucirc", 0x00FB, "u"}, /* 251 - latin small letter u with circumflex */
+ {"uuml", 0x00FC, "ue"}, /* 252 - latin small letter u with diaeresis */
+ {"yacute", 0x00FD, "y"}, /* 253 - latin small letter y with acute */
+ {"thorn", 0x00FE, "p"}, /* 254 - latin small letter thorn */
+ {"yuml", 0x00FF, "y"}, /* 255 - latin small letter y with diaeresis */
+ {"OElig", 0x0152, "OE"}, /* 338 - latin capital ligature oe */
+ {"oelig", 0x0153, "oe"}, /* 339 - latin small ligature oe */
+ {"Scaron", 0x0160, "S"}, /* 352 - latin capital letter s with caron */
+ {"scaron", 0x0161, "s"}, /* 353 - latin small letter s with caron */
+ {"Yuml", 0x0178, "Y"}, /* 376 - latin capital letter y with diaeresis */
+ {"fnof", 0x0192, "f"}, /* 402 - latin small letter f with hook */
+ {"circ", 0x02C6}, /* 710 - modifier letter circumflex accent */
+ {"tilde", 0x02DC, "~"}, /* 732 - small tilde */
+ {"Alpha", 0x0391}, /* 913 - greek capital letter alpha */
+ {"Beta", 0x0392}, /* 914 - greek capital letter beta */
+ {"Gamma", 0x0393}, /* 915 - greek capital letter gamma */
+ {"Delta", 0x0394}, /* 916 - greek capital letter delta */
+ {"Epsilon", 0x0395}, /* 917 - greek capital letter epsilon */
+ {"Zeta", 0x0396}, /* 918 - greek capital letter zeta */
+ {"Eta", 0x0397}, /* 919 - greek capital letter eta */
+ {"Theta", 0x0398}, /* 920 - greek capital letter theta */
+ {"Iota", 0x0399}, /* 921 - greek capital letter iota */
+ {"Kappa", 0x039A}, /* 922 - greek capital letter kappa */
+ {"Lambda", 0x039B}, /* 923 - greek capital letter lamda */
+ {"Mu", 0x039C}, /* 924 - greek capital letter mu */
+ {"Nu", 0x039D}, /* 925 - greek capital letter nu */
+ {"Xi", 0x039E}, /* 926 - greek capital letter xi */
+ {"Omicron", 0x039F}, /* 927 - greek capital letter omicron */
+ {"Pi", 0x03A0}, /* 928 - greek capital letter pi */
+ {"Rho", 0x03A1}, /* 929 - greek capital letter rho */
+ {"Sigma", 0x03A3}, /* 931 - greek capital letter sigma */
+ {"Tau", 0x03A4}, /* 932 - greek capital letter tau */
+ {"Upsilon", 0x03A5}, /* 933 - greek capital letter upsilon */
+ {"Phi", 0x03A6}, /* 934 - greek capital letter phi */
+ {"Chi", 0x03A7}, /* 935 - greek capital letter chi */
+ {"Psi", 0x03A8}, /* 936 - greek capital letter psi */
+ {"Omega", 0x03A9}, /* 937 - greek capital letter omega */
+ {"alpha", 0x03B1}, /* 945 - greek small letter alpha */
+ {"beta", 0x03B2}, /* 946 - greek small letter beta */
+ {"gamma", 0x03B3}, /* 947 - greek small letter gamma */
+ {"delta", 0x03B4}, /* 948 - greek small letter delta */
+ {"epsilon", 0x03B5}, /* 949 - greek small letter epsilon */
+ {"zeta", 0x03B6}, /* 950 - greek small letter zeta */
+ {"eta", 0x03B7}, /* 951 - greek small letter eta */
+ {"theta", 0x03B8}, /* 952 - greek small letter theta */
+ {"iota", 0x03B9}, /* 953 - greek small letter iota */
+ {"kappa", 0x03BA}, /* 954 - greek small letter kappa */
+ {"lambda", 0x03BB}, /* 955 - greek small letter lamda */
+ {"mu", 0x03BC}, /* 956 - greek small letter mu */
+ {"nu", 0x03BD}, /* 957 - greek small letter nu */
+ {"xi", 0x03BE}, /* 958 - greek small letter xi */
+ {"omicron", 0x03BF}, /* 959 - greek small letter omicron */
+ {"pi", 0x03C0}, /* 960 - greek small letter pi */
+ {"rho", 0x03C1}, /* 961 - greek small letter rho */
+ {"sigmaf", 0x03C2}, /* 962 - greek small letter final sigma */
+ {"sigma", 0x03C3}, /* 963 - greek small letter sigma */
+ {"tau", 0x03C4}, /* 964 - greek small letter tau */
+ {"upsilon", 0x03C5}, /* 965 - greek small letter upsilon */
+ {"phi", 0x03C6}, /* 966 - greek small letter phi */
+ {"chi", 0x03C7}, /* 967 - greek small letter chi */
+ {"psi", 0x03C8}, /* 968 - greek small letter psi */
+ {"omega", 0x03C9}, /* 969 - greek small letter omega */
+ {"thetasym", 0x03D1}, /* 977 - greek theta symbol */
+ {"upsih", 0x03D2}, /* 978 - greek upsilon with hook symbol */
+ {"piv", 0x03D6}, /* 982 - greek pi symbol */
+ {"ensp", 0x2002}, /* 8194 - en space */
+ {"emsp", 0x2003}, /* 8195 - em space */
+ {"thinsp", 0x2009}, /* 8201 - thin space */
+ {"zwnj", 0x200C}, /* 8204 - zero width non-joiner */
+ {"zwj", 0x200D}, /* 8205 - zero width joiner */
+ {"lrm", 0x200E}, /* 8206 - left-to-right mark */
+ {"rlm", 0x200F}, /* 8207 - right-to-left mark */
+ {"ndash", 0x2013}, /* 8211 - en dash */
+ {"mdash", 0x2014}, /* 8212 - em dash */
+ {"#8213", 0x2015, "--"}, /* 2015 - horizontal bar */
+ {"#8214", 0x2016, "||"}, /* 2016 - double vertical line */
+ {"#8215", 0x2017, "__"}, /* 2017 - double low line */
+ {"lsquo", 0x2018}, /* 8216 - left single quotation mark */
+ {"rsquo", 0x2019}, /* 8217 - right single quotation mark */
+ {"sbquo", 0x201A}, /* 8218 - single low-9 quotation mark */
+ {"ldquo", 0x201C}, /* 8220 - left double quotation mark */
+ {"rdquo", 0x201D}, /* 8221 - right double quotation mark */
+ {"bdquo", 0x201E, ",,"}, /* 8222 - double low-9 quotation mark */
+ {"#8223", 0x201F, "``"}, /* 201F - double high reversed-9 quotation mark */
+ {"dagger", 0x2020}, /* 8224 - dagger */
+ {"Dagger", 0x2021}, /* 8225 - double dagger */
+ {"bull", 0x2022, "*"}, /* 8226 - bullet */
+ {"hellip", 0x2026}, /* 8230 - horizontal ellipsis */
+ {"permil", 0x2030}, /* 8240 - per mille sign */
+ {"prime", 0x2032, "\'"}, /* 8242 - prime */
+ {"Prime", 0x2033, "\'\'"}, /* 8243 - double prime */
+ {"#8244", 0x2034, "\'\'\'"}, /* 2034 - triple prime */
+ {"lsaquo", 0x2039}, /* 8249 - single left-pointing angle quotation mark */
+ {"rsaquo", 0x203A}, /* 8250 - single right-pointing angle quotation mark */
+ {"#8252", 0x203C, "!!"}, /* 203C - double exclamation mark */
+ {"oline", 0x203E, "-"}, /* 8254 - overline */
+ {"frasl", 0x2044}, /* 8260 - fraction slash */
+ {"#8263", 0x2047, "??"}, /* 2047 - double question mark */
+ {"#8264", 0x2048, "?!"}, /* 2048 - question exclamation mark */
+ {"#8265", 0x2049, "!?"}, /* 2049 - exclamation question mark */
+ {"#8279", 0x2057, "\'\'\'\'"}, /* 2057 - quad prime */
+ {"euro", 0x20AC, "EUR"}, /* 8364 - euro sign */
+ {"image", 0x2111}, /* 8465 - black-letter capital i */
+ {"weierp", 0x2118}, /* 8472 - script capital p (<a href="/wiki/Weierstrass" title="Weierstrass">Weierstrass</a> p) */
+ {"real", 0x211C}, /* 8476 - black-letter capital r */
+ {"trade", 0x2122, "[tm]"}, /* 8482 - trademark sign */
+ {"alefsym", 0x2135}, /* 8501 - alef symbol */
+ {"larr", 0x2190}, /* 8592 - leftwards arrow */
+ {"uarr", 0x2191}, /* 8593 - upwards arrow */
+ {"rarr", 0x2192}, /* 8594 - rightwards arrow */
+ {"darr", 0x2193}, /* 8595 - downwards arrow */
+ {"harr", 0x2194}, /* 8596 - left right arrow */
+ {"crarr", 0x21B5}, /* 8629 - downwards arrow with corner leftwards */
+ {"lArr", 0x21D0}, /* 8656 - leftwards double arrow */
+ {"uArr", 0x21D1}, /* 8657 - upwards double arrow */
+ {"rArr", 0x21D2}, /* 8658 - rightwards double arrow */
+ {"dArr", 0x21D3}, /* 8659 - downwards double arrow */
+ {"hArr", 0x21D4}, /* 8660 - left right double arrow */
+ {"forall", 0x2200}, /* 8704 - for all */
+ {"part", 0x2202}, /* 8706 - partial differential */
+ {"exist", 0x2203}, /* 8707 - there exists */
+ {"empty", 0x2205}, /* 8709 - empty set */
+ {"nabla", 0x2207}, /* 8711 - nabla */
+ {"isin", 0x2208}, /* 8712 - element of */
+ {"notin", 0x2209}, /* 8713 - not an element of */
+ {"ni", 0x220B}, /* 8715 - contains as member */
+ {"prod", 0x220F}, /* 8719 - n-ary product */
+ {"sum", 0x2211}, /* 8721 - n-ary summation */
+ {"minus", 0x2212}, /* 8722 - minus sign */
+ {"lowast", 0x2217}, /* 8727 - asterisk operator */
+ {"radic", 0x221A}, /* 8730 - square root */
+ {"prop", 0x221D}, /* 8733 - proportional to */
+ {"infin", 0x221E}, /* 8734 - infinity */
+ {"ang", 0x2220}, /* 8736 - angle */
+ {"and", 0x2227}, /* 8743 - logical and */
+ {"or", 0x2228}, /* 8744 - logical or */
+ {"cap", 0x2229}, /* 8745 - intersection */
+ {"cup", 0x222A}, /* 8746 - union */
+ {"int", 0x222B}, /* 8747 - integral */
+ {"there4", 0x2234}, /* 8756 - therefore */
+ {"sim", 0x223C}, /* 8764 - tilde operator */
+ {"cong", 0x2245}, /* 8773 - congruent to */
+ {"asymp", 0x2248}, /* 8776 - almost equal to */
+ {"ne", 0x2260}, /* 8800 - not equal to */
+ {"equiv", 0x2261}, /* 8801 - identical to (equivalent to) */
+ {"le", 0x2264}, /* 8804 - less-than or equal to */
+ {"ge", 0x2265}, /* 8805 - greater-than or equal to */
+ {"sub", 0x2282}, /* 8834 - subset of */
+ {"sup", 0x2283}, /* 8835 - superset of */
+ {"nsub", 0x2284}, /* 8836 - not a subset of */
+ {"sube", 0x2286}, /* 8838 - subset of or equal to */
+ {"supe", 0x2287}, /* 8839 - superset of or equal to */
+ {"oplus", 0x2295}, /* 8853 - circled plus */
+ {"otimes", 0x2297}, /* 8855 - circled times */
+ {"perp", 0x22A5}, /* 8869 - up tack */
+ {"sdot", 0x22C5}, /* 8901 - dot operator */
+ {"lceil", 0x2308}, /* 8968 - left ceiling */
+ {"rceil", 0x2309}, /* 8969 - right ceiling */
+ {"lfloor", 0x230A}, /* 8970 - left floor */
+ {"rfloor", 0x230B}, /* 8971 - right floor */
+ {"lang", 0x2329}, /* 9001 - left-pointing angle bracket */
+ {"rang", 0x232A}, /* 9002 - right-pointing angle bracket */
+ {"loz", 0x25CA}, /* 9674 - lozenge */
+ {"spades", 0x2660}, /* 9824 - black spade suit */
+ {"clubs", 0x2663}, /* 9827 - black club suit */
+ {"hearts", 0x2665}, /* 9829 - black heart suit */
+ {"diams", 0x2666} /* 9830 - black diamond suit */
+};
+
+
+/*
+ * Table of supported elements and corresponding handlers
+ */
+static ELPROP_S html_element_table[] = {
+ {"HTML"}, /* HTML ignore if seen? */
+ {"HEAD", html_head}, /* slurp until <BODY> ? */
+ {"TITLE", html_title}, /* Document Title */
+ {"BASE", html_base}, /* HREF base */
+ {"BODY", html_body}, /* HTML BODY */
+ {"A", html_a}, /* Anchor */
+ {"ABBR", html_abbr}, /* Abbreviation */
+ {"IMG", html_img}, /* Image */
+ {"MAP", html_map}, /* Image Map */
+ {"AREA", html_area}, /* Image Map Area */
+ {"HR", html_hr, 1}, /* Horizontal Rule */
+ {"BR", html_br}, /* Line Break */
+ {"P", html_p, 1}, /* Paragraph */
+ {"OL", html_ol, 1}, /* Ordered List */
+ {"UL", html_ul, 1}, /* Unordered List */
+ {"MENU", html_menu}, /* Menu List */
+ {"DIR", html_dir}, /* Directory List */
+ {"LI", html_li}, /* ... List Item */
+ {"DL", html_dl, 1}, /* Definition List */
+ {"DT", html_dt}, /* ... Def. Term */
+ {"DD", html_dd}, /* ... Def. Definition */
+ {"I", html_i}, /* Italic Text */
+ {"EM", html_em}, /* Typographic Emphasis */
+ {"STRONG", html_strong}, /* STRONG Typo Emphasis */
+ {"VAR", html_i}, /* Variable Name */
+ {"B", html_b}, /* Bold Text */
+ {"U", html_u}, /* Underline Text */
+ {"S", html_s}, /* Strike-Through Text */
+ {"STRIKE", html_s}, /* Strike-Through Text */
+ {"BIG", html_big}, /* Big Font Text */
+ {"SMALL", html_small}, /* Small Font Text */
+ {"FONT", html_font}, /* Font display directives */
+ {"BLOCKQUOTE", html_blockquote, 1}, /* Blockquote */
+ {"ADDRESS", html_address, 1}, /* Address */
+ {"CENTER", html_center}, /* Centered Text v3.2 */
+ {"DIV", html_div, 1}, /* Document Division 3.2 */
+ {"SPAN", html_span}, /* Text Span */
+ {"H1", html_h1, 1}, /* Headings... */
+ {"H2", html_h2, 1},
+ {"H3", html_h3,1},
+ {"H4", html_h4, 1},
+ {"H5", html_h5, 1},
+ {"H6", html_h6, 1},
+ {"PRE", html_pre, 1}, /* Preformatted Text */
+ {"KBD", html_kbd}, /* Keyboard Input (NO OP) */
+ {"DFN", html_dfn}, /* Definition (NO OP) */
+ {"VAR", html_var}, /* Variable (NO OP) */
+ {"TT", html_tt}, /* Typetype (NO OP) */
+ {"SAMP", html_samp}, /* Sample Text (NO OP) */
+ {"CITE", html_cite}, /* Citation (NO OP) */
+ {"CODE", html_code}, /* Code Text (NO OP) */
+ {"INS", html_ins}, /* Text Inseted (NO OP) */
+ {"DEL", html_del}, /* Text Deleted (NO OP) */
+ {"SUP", html_sup}, /* Text Superscript (NO OP) */
+ {"SUB", html_sub}, /* Text Superscript (NO OP) */
+ {"STYLE", html_style}, /* CSS Definitions */
+
+/*----- Handlers below UNIMPLEMENTED (and won't until later) -----*/
+
+ {"FORM", html_form, 1}, /* form within a document */
+ {"INPUT", html_input}, /* One input field, options */
+ {"BUTTON", html_button}, /* Push Button */
+ {"OPTION", html_option}, /* One option within Select */
+ {"OPTION", html_optgroup}, /* Option Group Definition */
+ {"SELECT", html_select}, /* Selection from a set */
+ {"TEXTAREA", html_textarea}, /* A multi-line input field */
+ {"LABEL", html_label}, /* Control Label */
+ {"FIELDSET", html_fieldset, 1}, /* Fieldset Control Group */
+
+/*----- Handlers below NEVER TO BE IMPLEMENTED -----*/
+ {"SCRIPT", html_script}, /* Embedded scripting statements */
+ {"APPLET", NULL}, /* Embedded applet statements */
+ {"OBJECT", NULL}, /* Embedded object statements */
+ {"LINK", NULL}, /* References to external data */
+ {"PARAM", NULL}, /* Applet/Object parameters */
+
+/*----- Handlers below provide limited support for RFC 1942 Tables -----*/
+
+ {"TABLE", html_table, 1}, /* Table */
+ {"CAPTION", html_caption}, /* Table Caption */
+ {"TR", html_tr}, /* Table Table Row */
+ {"TD", html_td}, /* Table Table Data */
+ {"TH", html_th}, /* Table Table Head */
+ {"THEAD", html_thead}, /* Table Table Head */
+ {"TBODY", html_tbody}, /* Table Table Body */
+ {"TFOOT", html_tfoot}, /* Table Table Foot */
+ {"COL", html_col}, /* Table Column Attibutes */
+ {"COLGROUP", html_colgroup}, /* Table Column Group Attibutes */
+
+ {NULL, NULL}
+};
+
+
+/*
+ * Table of supported RSS 2.0 elements
+ */
+static ELPROP_S rss_element_table[] = {
+ {"RSS", rss_rss}, /* RSS 2.0 version */
+ {"CHANNEL", rss_channel}, /* RSS 2.0 Channel */
+ {"TITLE", rss_title}, /* RSS 2.0 Title */
+ {"IMAGE", rss_image}, /* RSS 2.0 Channel Image */
+ {"LINK", rss_link}, /* RSS 2.0 Channel/Item Link */
+ {"DESCRIPTION", rss_description}, /* RSS 2.0 Channel/Item Description */
+ {"ITEM", rss_item}, /* RSS 2.0 Channel ITEM */
+ {"TTL", rss_ttl}, /* RSS 2.0 Item TTL */
+ {NULL, NULL}
+};
+
+
+/*
+ * Initialize the given handler, and add it to the stack if it
+ * requests it.
+ *
+ * Returns: 1 if handler chose to get pushed on stack
+ * 0 if handler declined
+ */
+int
+html_push(FILTER_S *fd, ELPROP_S *ep)
+{
+ HANDLER_S *new;
+
+ new = (HANDLER_S *)fs_get(sizeof(HANDLER_S));
+ memset(new, 0, sizeof(HANDLER_S));
+ new->html_data = fd;
+ new->element = ep;
+ if((*ep->handler)(new, 0, GF_RESET)){ /* stack the handler? */
+ new->below = HANDLERS(fd);
+ HANDLERS(fd) = new; /* push */
+ return(1);
+ }
+
+ fs_give((void **) &new);
+ return(0);
+}
+
+
+/*
+ * Remove the most recently installed the given handler
+ * after letting it accept its demise.
+ */
+void
+html_pop(FILTER_S *fd, ELPROP_S *ep)
+{
+ HANDLER_S *tp;
+
+ for(tp = HANDLERS(fd); tp && ep != EL(tp); tp = tp->below){
+ HANDLER_S *tp2;
+ ELPROP_S *ep2;
+
+ dprint((3, "-- html error: bad nesting: given /%s expected /%s", ep->element, EL(tp)->element));
+ /* if no evidence of opening tag, ignore given closing tag */
+ for(tp2 = HANDLERS(fd); tp2 && ep != EL(tp2); tp2 = tp2->below)
+ ;
+
+ if(!tp2){
+ dprint((3, "-- html error: no opening tag for given tag /%s", ep->element));
+ return;
+ }
+
+ (void) (*EL(tp)->handler)(tp, 0, GF_EOD);
+ HANDLERS(fd) = tp->below;
+ }
+
+ if(tp){
+ (void) (*EL(tp)->handler)(tp, 0, GF_EOD); /* may adjust handler list */
+ if(tp != HANDLERS(fd)){
+ HANDLER_S *p;
+
+ for(p = HANDLERS(fd); p->below != tp; p = p->below)
+ ;
+
+ if(p)
+ p->below = tp->below; /* remove from middle of stack */
+ /* BUG: else programming botch and we should die */
+ }
+ else
+ HANDLERS(fd) = tp->below; /* pop */
+
+ fs_give((void **)&tp);
+ }
+ else{
+ /* BUG: should MAKE SURE NOT TO EMIT IT */
+ dprint((3, "-- html error: end tag without a start: %s", ep->element));
+ }
+}
+
+
+/*
+ * Deal with data passed a hander in its GF_DATA state
+ */
+static void
+html_handoff(HANDLER_S *hd, int ch)
+{
+ if(hd->below)
+ (void) (*EL(hd->below)->handler)(hd->below, ch, GF_DATA);
+ else
+ html_output(hd->html_data, ch);
+}
+
+
+/*
+ * HTML <BR> element handler
+ */
+int
+html_br(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_RESET){
+ if(PASS_HTML(hd->html_data)){
+ html_output_raw_tag(hd->html_data, "br");
+ }
+ else{
+ html_output(hd->html_data, HTML_NEWLINE);
+ }
+ }
+
+ return(0); /* don't get linked */
+}
+
+
+/*
+ * HTML <HR> (Horizontal Rule) element handler
+ */
+int
+html_hr(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_RESET){
+ if(PASS_HTML(hd->html_data)){
+ html_output_raw_tag(hd->html_data, "hr");
+ }
+ else{
+ int i, old_wrap, width, align;
+ PARAMETER *p;
+
+ width = WRAP_COLS(hd->html_data);
+ align = 0;
+ for(p = HD(hd->html_data)->el_data->attribs;
+ p && p->attribute;
+ p = p->next)
+ if(p->value){
+ if(!strucmp(p->attribute, "ALIGN")){
+ if(!strucmp(p->value, "LEFT"))
+ align = 1;
+ else if(!strucmp(p->value, "RIGHT"))
+ align = 2;
+ }
+ else if(!strucmp(p->attribute, "WIDTH")){
+ char *cp;
+
+ width = 0;
+ for(cp = p->value; *cp; cp++)
+ if(*cp == '%'){
+ width = (WRAP_COLS(hd->html_data)*MIN(100,width))/100;
+ break;
+ }
+ else if(isdigit((unsigned char) *cp))
+ width = (width * 10) + (*cp - '0');
+
+ width = MIN(width, WRAP_COLS(hd->html_data));
+ }
+ }
+
+ html_blank(hd->html_data, 1); /* at least one blank line */
+
+ old_wrap = HD(hd->html_data)->wrapstate;
+ HD(hd->html_data)->wrapstate = 0;
+ if((i = MAX(0, WRAP_COLS(hd->html_data) - width))
+ && ((align == 0) ? i /= 2 : (align == 2)))
+ for(; i > 0; i--)
+ html_output(hd->html_data, ' ');
+
+ for(i = 0; i < width; i++)
+ html_output(hd->html_data, '_');
+
+ html_blank(hd->html_data, 1);
+ HD(hd->html_data)->wrapstate = old_wrap;
+ }
+ }
+
+ return(0); /* don't get linked */
+}
+
+
+/*
+ * HTML <P> (paragraph) element handler
+ */
+int
+html_p(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ if(PASS_HTML(hd->html_data)){
+ html_output_raw_tag(hd->html_data, "p");
+ }
+ else{
+ /* Make sure there's at least 1 blank line */
+ html_blank(hd->html_data, 1);
+
+ /* adjust indent level if needed */
+ if(HD(hd->html_data)->li_pending){
+ html_indent(hd->html_data, 4, HTML_ID_INC);
+ HD(hd->html_data)->li_pending = 0;
+ }
+ }
+ }
+ else if(cmd == GF_EOD){
+ if(PASS_HTML(hd->html_data)){
+ html_output_string(hd->html_data, "</p>");
+ }
+ else{
+ /* Make sure there's at least 1 blank line */
+ html_blank(hd->html_data, 1);
+ }
+ }
+
+ return(1); /* GET linked */
+}
+
+
+/*
+ * HTML Table <TABLE> (paragraph) table row
+ */
+int
+html_table(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_DATA){
+ if(PASS_HTML(hd->html_data)){
+ html_handoff(hd, ch);
+ }
+ }
+ else if(cmd == GF_RESET){
+ if(PASS_HTML(hd->html_data)){
+ html_output_raw_tag(hd->html_data, "table");
+ }
+ else
+ /* Make sure there's at least 1 blank line */
+ html_blank(hd->html_data, 0);
+ }
+ else if(cmd == GF_EOD){
+ if(PASS_HTML(hd->html_data)){
+ html_output_string(hd->html_data, "</table>");
+ }
+ else
+ /* Make sure there's at least 1 blank line */
+ html_blank(hd->html_data, 0);
+ }
+ return(PASS_HTML(hd->html_data)); /* maybe get linked */
+}
+
+
+/*
+ * HTML <CAPTION> (Table Caption) element handler
+ */
+int
+html_caption(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ if(PASS_HTML(hd->html_data)){
+ html_output_raw_tag(hd->html_data, "caption");
+ }
+ else{
+ /* turn ON the centered bit */
+ CENTER_BIT(hd->html_data) = 1;
+ }
+ }
+ else if(cmd == GF_EOD){
+ if(PASS_HTML(hd->html_data)){
+ html_output_string(hd->html_data, "</caption>");
+ }
+ else{
+ /* turn OFF the centered bit */
+ CENTER_BIT(hd->html_data) = 0;
+ }
+ }
+
+ return(1);
+}
+
+
+/*
+ * HTML Table <TR> (paragraph) table row
+ */
+int
+html_tr(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_DATA){
+ if(PASS_HTML(hd->html_data)){
+ html_handoff(hd, ch);
+ }
+ }
+ else if(cmd == GF_RESET){
+ if(PASS_HTML(hd->html_data)){
+ html_output_raw_tag(hd->html_data, "tr");
+ }
+ else
+ /* Make sure there's at least 1 blank line */
+ html_blank(hd->html_data, 0);
+ }
+ else if(cmd == GF_EOD){
+ if(PASS_HTML(hd->html_data)){
+ html_output_string(hd->html_data, "</tr>");
+ }
+ else
+ /* Make sure there's at least 1 blank line */
+ html_blank(hd->html_data, 0);
+ }
+ return(PASS_HTML(hd->html_data)); /* maybe get linked */
+}
+
+
+/*
+ * HTML Table <TD> (paragraph) table data
+ */
+int
+html_td(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_DATA){
+ if(PASS_HTML(hd->html_data)){
+ html_handoff(hd, ch);
+ }
+ }
+ else if(cmd == GF_RESET){
+ if(PASS_HTML(hd->html_data)){
+ html_output_raw_tag(hd->html_data, "td");
+ }
+ else{
+ PARAMETER *p;
+
+ for(p = HD(hd->html_data)->el_data->attribs;
+ p && p->attribute;
+ p = p->next)
+ if(!strucmp(p->attribute, "nowrap")
+ && (hd->html_data->f2 || hd->html_data->n)){
+ HTML_DUMP_LIT(hd->html_data, " | ", 3);
+ break;
+ }
+ }
+ }
+ else if(cmd == GF_EOD){
+ if(PASS_HTML(hd->html_data)){
+ html_output_string(hd->html_data, "</td>");
+ }
+ }
+
+ return(PASS_HTML(hd->html_data)); /* maybe get linked */
+}
+
+
+/*
+ * HTML Table <TH> (paragraph) table head
+ */
+int
+html_th(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_DATA){
+ if(PASS_HTML(hd->html_data)){
+ html_handoff(hd, ch);
+ }
+ }
+ else if(cmd == GF_RESET){
+ if(PASS_HTML(hd->html_data)){
+ html_output_raw_tag(hd->html_data, "th");
+ }
+ else{
+ PARAMETER *p;
+
+ for(p = HD(hd->html_data)->el_data->attribs;
+ p && p->attribute;
+ p = p->next)
+ if(!strucmp(p->attribute, "nowrap")
+ && (hd->html_data->f2 || hd->html_data->n)){
+ HTML_DUMP_LIT(hd->html_data, " | ", 3);
+ break;
+ }
+ }
+ }
+ else if(cmd == GF_EOD){
+ if(PASS_HTML(hd->html_data)){
+ html_output_string(hd->html_data, "</th>");
+ }
+ }
+
+ return(PASS_HTML(hd->html_data)); /* don't get linked */
+}
+
+
+/*
+ * HTML Table <THEAD> table head
+ */
+int
+html_thead(HANDLER_S *hd, int ch, int cmd)
+{
+ if(PASS_HTML(hd->html_data)){
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ html_output_raw_tag(hd->html_data, "thead");
+ }
+ else if(cmd == GF_EOD){
+ html_output_string(hd->html_data, "</thead>");
+ }
+
+ return(1); /* GET linked */
+ }
+
+ return(0); /* don't get linked */
+}
+
+
+/*
+ * HTML Table <TBODY> table body
+ */
+int
+html_tbody(HANDLER_S *hd, int ch, int cmd)
+{
+ if(PASS_HTML(hd->html_data)){
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ html_output_raw_tag(hd->html_data, "tbody");
+ }
+ else if(cmd == GF_EOD){
+ html_output_string(hd->html_data, "</tbody>");
+ }
+
+ return(1); /* GET linked */
+ }
+
+ return(0); /* don't get linked */
+}
+
+
+/*
+ * HTML Table <TFOOT> table body
+ */
+int
+html_tfoot(HANDLER_S *hd, int ch, int cmd)
+{
+ if(PASS_HTML(hd->html_data)){
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ html_output_raw_tag(hd->html_data, "tfoot");
+ }
+ else if(cmd == GF_EOD){
+ html_output_string(hd->html_data, "</tfoot>");
+ }
+
+ return(1); /* GET linked */
+ }
+
+ return(0); /* don't get linked */
+}
+
+
+/*
+ * HTML <COL> (Table Column Attributes) element handler
+ */
+int
+html_col(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_RESET){
+ if(PASS_HTML(hd->html_data)){
+ html_output_raw_tag(hd->html_data, "col");
+ }
+ }
+
+ return(0); /* don't get linked */
+}
+
+
+/*
+ * HTML Table <COLGROUP> table body
+ */
+int
+html_colgroup(HANDLER_S *hd, int ch, int cmd)
+{
+ if(PASS_HTML(hd->html_data)){
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ html_output_raw_tag(hd->html_data, "colgroup");
+ }
+ else if(cmd == GF_EOD){
+ html_output_string(hd->html_data, "</colgroup>");
+ }
+
+ return(1); /* GET linked */
+ }
+
+ return(0); /* don't get linked */
+}
+
+
+/*
+ * HTML <I> (italic text) element handler
+ */
+int
+html_i(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_DATA){
+ /* include LITERAL in spaceness test! */
+ if(hd->x && !ASCII_ISSPACE((unsigned char) (ch & 0xff))){
+ HTML_ITALIC(hd->html_data, 1);
+ hd->x = 0;
+ }
+
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ hd->x = 1;
+ }
+ else if(cmd == GF_EOD){
+ if(!hd->x)
+ HTML_ITALIC(hd->html_data, 0);
+ }
+
+ return(1); /* get linked */
+}
+
+
+/*
+ * HTML <EM> element handler
+ */
+int
+html_em(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_DATA){
+ if(!PASS_HTML(hd->html_data)){
+ /* include LITERAL in spaceness test! */
+ if(hd->x && !ASCII_ISSPACE((unsigned char) (ch & 0xff))){
+ HTML_ITALIC(hd->html_data, 1);
+ hd->x = 0;
+ }
+ }
+
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ if(PASS_HTML(hd->html_data)){
+ html_output_raw_tag(hd->html_data, "em");
+ }
+ else{
+ hd->x = 1;
+ }
+ }
+ else if(cmd == GF_EOD){
+ if(PASS_HTML(hd->html_data)){
+ html_output_string(hd->html_data, "</em>");
+ }
+ else{
+ if(!hd->x)
+ HTML_ITALIC(hd->html_data, 0);
+ }
+ }
+
+ return(1); /* get linked */
+}
+
+
+/*
+ * HTML <STRONG> element handler
+ */
+int
+html_strong(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_DATA){
+ if(!PASS_HTML(hd->html_data)){
+ /* include LITERAL in spaceness test! */
+ if(hd->x && !ASCII_ISSPACE((unsigned char) (ch & 0xff))){
+ HTML_ITALIC(hd->html_data, 1);
+ hd->x = 0;
+ }
+ }
+
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ if(PASS_HTML(hd->html_data)){
+ html_output_raw_tag(hd->html_data, "strong");
+ }
+ else{
+ hd->x = 1;
+ }
+ }
+ else if(cmd == GF_EOD){
+ if(PASS_HTML(hd->html_data)){
+ html_output_string(hd->html_data, "</strong>");
+ }
+ else{
+ if(!hd->x)
+ HTML_ITALIC(hd->html_data, 0);
+ }
+ }
+
+ return(1); /* get linked */
+}
+
+
+/*
+ * HTML <u> (Underline text) element handler
+ */
+int
+html_u(HANDLER_S *hd, int ch, int cmd)
+{
+ if(PASS_HTML(hd->html_data)){
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ html_output_raw_tag(hd->html_data, "u");
+ }
+ else if(cmd == GF_EOD){
+ html_output_string(hd->html_data, "</u>");
+ }
+
+ return(1); /* get linked */
+ }
+
+ return(0); /* do NOT get linked */
+}
+
+
+/*
+ * HTML <b> (Bold text) element handler
+ */
+int
+html_b(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_DATA){
+ if(!PASS_HTML(hd->html_data)){
+ /* include LITERAL in spaceness test! */
+ if(hd->x && !ASCII_ISSPACE((unsigned char) (ch & 0xff))){
+ HTML_BOLD(hd->html_data, 1);
+ hd->x = 0;
+ }
+ }
+
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ if(PASS_HTML(hd->html_data)){
+ html_output_raw_tag(hd->html_data, "b");
+ }
+ else{
+ hd->x = 1;
+ }
+ }
+ else if(cmd == GF_EOD){
+ if(PASS_HTML(hd->html_data)){
+ html_output_string(hd->html_data, "</b>");
+ }
+ else{
+ if(!hd->x)
+ HTML_BOLD(hd->html_data, 0);
+ }
+ }
+
+ return(1); /* get linked */
+}
+
+
+/*
+ * HTML <s> (strike-through text) element handler
+ */
+int
+html_s(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_DATA){
+ if(!PASS_HTML(hd->html_data)){
+ /* include LITERAL in spaceness test! */
+ if(hd->x && !ASCII_ISSPACE((unsigned char) (ch & 0xff))){
+ HTML_STRIKE(hd->html_data, 1);
+ hd->x = 0;
+ }
+ }
+
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ if(PASS_HTML(hd->html_data)){
+ html_output_raw_tag(hd->html_data, "s");
+ }
+ else{
+ hd->x = 1;
+ }
+ }
+ else if(cmd == GF_EOD){
+ if(PASS_HTML(hd->html_data)){
+ html_output_string(hd->html_data, "</s>");
+ }
+ else{
+ if(!hd->x)
+ HTML_STRIKE(hd->html_data, 0);
+ }
+ }
+
+ return(1); /* get linked */
+}
+
+
+/*
+ * HTML <big> (BIG text) element handler
+ */
+int
+html_big(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_DATA){
+ /* include LITERAL in spaceness test! */
+ if(hd->x && !ASCII_ISSPACE((unsigned char) (ch & 0xff))){
+ HTML_BIG(hd->html_data, 1);
+ hd->x = 0;
+ }
+
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ hd->x = 1;
+ }
+ else if(cmd == GF_EOD){
+ if(!hd->x)
+ HTML_BIG(hd->html_data, 0);
+ }
+
+ return(1); /* get linked */
+}
+
+
+/*
+ * HTML <small> (SMALL text) element handler
+ */
+int
+html_small(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_DATA){
+ /* include LITERAL in spaceness test! */
+ if(hd->x && !ASCII_ISSPACE((unsigned char) (ch & 0xff))){
+ HTML_SMALL(hd->html_data, 1);
+ hd->x = 0;
+ }
+
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ hd->x = 1;
+ }
+ else if(cmd == GF_EOD){
+ if(!hd->x)
+ HTML_SMALL(hd->html_data, 0);
+ }
+
+ return(1); /* get linked */
+}
+
+
+/*
+ * HTML <FONT> element handler
+ */
+int
+html_font(HANDLER_S *hd, int ch, int cmd)
+{
+ if(PASS_HTML(hd->html_data)){
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ html_output_raw_tag(hd->html_data, "font");
+ }
+ else if(cmd == GF_EOD){
+ html_output_string(hd->html_data, "</font>");
+ }
+
+ return(1); /* get linked */
+ }
+
+ return(0);
+}
+
+
+/*
+ * HTML <IMG> element handler
+ */
+int
+html_img(HANDLER_S *hd, int ch, int cmd)
+{
+ PARAMETER *p;
+ char *alt = NULL, *src = NULL, *s;
+
+ if(cmd == GF_RESET){
+ if(PASS_HTML(hd->html_data)){
+ html_output_raw_tag(hd->html_data, "img");
+ }
+ else{
+ for(p = HD(hd->html_data)->el_data->attribs;
+ p && p->attribute;
+ p = p->next)
+ if(p->value && p->value[0]){
+ if(!strucmp(p->attribute, "alt"))
+ alt = p->value;
+ if(!strucmp(p->attribute, "src"))
+ src = p->value;
+ }
+
+ /*
+ * Multipart/Related Content ID pointer
+ * ONLY attached messages are recognized
+ * if we ever decide web bugs aren't a problem
+ * anymore then we might expand the scope
+ */
+ if(src
+ && DO_HANDLES(hd->html_data)
+ && RELATED_OK(hd->html_data)
+ && struncmp(src, "cid:", 4) == 0){
+ char buf[32];
+ int i, n;
+ HANDLE_S *h = new_handle(HANDLESP(hd->html_data));
+
+ h->type = IMG;
+ h->h.img.src = cpystr(src + 4);
+ h->h.img.alt = cpystr((alt) ? alt : "Attached Image");
+
+ HTML_TEXT(hd->html_data, TAG_EMBED);
+ HTML_TEXT(hd->html_data, TAG_HANDLE);
+
+ sprintf(buf, "%d", h->key);
+ n = strlen(buf);
+ HTML_TEXT(hd->html_data, n);
+ for(i = 0; i < n; i++){
+ unsigned int uic = buf[i];
+ HTML_TEXT(hd->html_data, uic);
+ }
+
+ return(0);
+ }
+ else if(alt && strlen(alt) < 256){ /* arbitrary "reasonable" limit */
+ HTML_DUMP_LIT(hd->html_data, alt, strlen(alt));
+ HTML_TEXT(hd->html_data, ' ');
+ return(0);
+ }
+ else if(src
+ && (s = strrindex(src, '/'))
+ && *++s != '\0'){
+ HTML_TEXT(hd->html_data, '[');
+ HTML_DUMP_LIT(hd->html_data, s, strlen(s));
+ HTML_TEXT(hd->html_data, ']');
+ HTML_TEXT(hd->html_data, ' ');
+ return(0);
+ }
+
+ /* text filler of last resort */
+ HTML_DUMP_LIT(hd->html_data, "[IMAGE] ", 7);
+ }
+ }
+
+ return(0); /* don't get linked */
+}
+
+
+/*
+ * HTML <MAP> (Image Map) element handler
+ */
+int
+html_map(HANDLER_S *hd, int ch, int cmd)
+{
+ if(PASS_HTML(hd->html_data) && PASS_IMAGES(hd->html_data)){
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ html_output_raw_tag(hd->html_data, "map");
+ }
+ else if(cmd == GF_EOD){
+ html_output_string(hd->html_data, "</map>");
+ }
+
+ return(1);
+ }
+
+ return(0);
+}
+
+
+/*
+ * HTML <AREA> (Image Map Area) element handler
+ */
+int
+html_area(HANDLER_S *hd, int ch, int cmd)
+{
+ if(PASS_HTML(hd->html_data) && PASS_IMAGES(hd->html_data)){
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ html_output_raw_tag(hd->html_data, "area");
+ }
+ else if(cmd == GF_EOD){
+ html_output_string(hd->html_data, "</area>");
+ }
+
+ return(1);
+ }
+
+ return(0);
+}
+
+
+/*
+ * HTML <FORM> (Form) element handler
+ */
+int
+html_form(HANDLER_S *hd, int ch, int cmd)
+{
+ if(PASS_HTML(hd->html_data)){
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ PARAMETER **pp;
+
+ /* SECURITY: make sure to redirect to new browser instance */
+ for(pp = &(HD(hd->html_data)->el_data->attribs);
+ *pp && (*pp)->attribute;
+ pp = &(*pp)->next)
+ if(!strucmp((*pp)->attribute, "target")){
+ if((*pp)->value)
+ fs_give((void **) &(*pp)->value);
+
+ (*pp)->value = cpystr("_blank");
+ }
+
+ if(!*pp){
+ *pp = (PARAMETER *)fs_get(sizeof(PARAMETER));
+ memset(*pp, 0, sizeof(PARAMETER));
+ (*pp)->attribute = cpystr("target");
+ (*pp)->value = cpystr("_blank");
+ }
+
+ html_output_raw_tag(hd->html_data, "form");
+ }
+ else if(cmd == GF_EOD){
+ html_output_string(hd->html_data, "</form>");
+ }
+ }
+ else{
+ if(cmd == GF_RESET){
+ html_blank(hd->html_data, 0);
+ HTML_DUMP_LIT(hd->html_data, "[FORM]", 6);
+ html_blank(hd->html_data, 0);
+ }
+ }
+
+ return(PASS_HTML(hd->html_data)); /* maybe get linked */
+}
+
+
+/*
+ * HTML <INPUT> (Form) element handler
+ */
+int
+html_input(HANDLER_S *hd, int ch, int cmd)
+{
+ if(PASS_HTML(hd->html_data)){
+ if(cmd == GF_RESET){
+ html_output_raw_tag(hd->html_data, "input");
+ }
+ }
+
+ return(0); /* don't get linked */
+}
+
+
+/*
+ * HTML <BUTTON> (Form) element handler
+ */
+int
+html_button(HANDLER_S *hd, int ch, int cmd)
+{
+ if(PASS_HTML(hd->html_data)){
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ html_output_raw_tag(hd->html_data, "button");
+ }
+ else if(cmd == GF_EOD){
+ html_output_string(hd->html_data, "</button>");
+ }
+
+ return(1); /* get linked */
+ }
+
+ return(0);
+}
+
+
+/*
+ * HTML <OPTION> (Form) element handler
+ */
+int
+html_option(HANDLER_S *hd, int ch, int cmd)
+{
+ if(PASS_HTML(hd->html_data)){
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ html_output_raw_tag(hd->html_data, "option");
+ }
+ else if(cmd == GF_EOD){
+ html_output_string(hd->html_data, "</option>");
+ }
+
+ return(1); /* get linked */
+ }
+
+ return(0);
+}
+
+
+/*
+ * HTML <OPTGROUP> (Form) element handler
+ */
+int
+html_optgroup(HANDLER_S *hd, int ch, int cmd)
+{
+ if(PASS_HTML(hd->html_data)){
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ html_output_raw_tag(hd->html_data, "optgroup");
+ }
+ else if(cmd == GF_EOD){
+ html_output_string(hd->html_data, "</optgroup>");
+ }
+
+ return(1); /* get linked */
+ }
+
+ return(0);
+}
+
+
+/*
+ * HTML <SELECT> (Form) element handler
+ */
+int
+html_select(HANDLER_S *hd, int ch, int cmd)
+{
+ if(PASS_HTML(hd->html_data)){
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ html_output_raw_tag(hd->html_data, "select");
+ }
+ else if(cmd == GF_EOD){
+ html_output_string(hd->html_data, "</select>");
+ }
+
+ return(1); /* get linked */
+ }
+
+ return(0);
+}
+
+
+/*
+ * HTML <TEXTAREA> (Form) element handler
+ */
+int
+html_textarea(HANDLER_S *hd, int ch, int cmd)
+{
+ if(PASS_HTML(hd->html_data)){
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ html_output_raw_tag(hd->html_data, "textarea");
+ }
+ else if(cmd == GF_EOD){
+ html_output_string(hd->html_data, "</textarea>");
+ }
+
+ return(1); /* get linked */
+ }
+
+ return(0);
+}
+
+
+/*
+ * HTML <LABEL> (Form) element handler
+ */
+int
+html_label(HANDLER_S *hd, int ch, int cmd)
+{
+ if(PASS_HTML(hd->html_data)){
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ html_output_raw_tag(hd->html_data, "label");
+ }
+ else if(cmd == GF_EOD){
+ html_output_string(hd->html_data, "</label>");
+ }
+
+ return(1); /* get linked */
+ }
+
+ return(0);
+}
+
+
+/*
+ * HTML <FIELDSET> (Form) element handler
+ */
+int
+html_fieldset(HANDLER_S *hd, int ch, int cmd)
+{
+ if(PASS_HTML(hd->html_data)){
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ html_output_raw_tag(hd->html_data, "fieldset");
+ }
+ else if(cmd == GF_EOD){
+ html_output_string(hd->html_data, "</fieldset>");
+ }
+
+ return(1); /* get linked */
+ }
+
+ return(0);
+}
+
+
+/*
+ * HTML <HEAD> element handler
+ */
+int
+html_head(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ HD(hd->html_data)->head = 1;
+ }
+ else if(cmd == GF_EOD){
+ HD(hd->html_data)->head = 0;
+ }
+
+ return(1); /* get linked */
+}
+
+
+/*
+ * HTML <BASE> element handler
+ */
+int
+html_base(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_RESET){
+ if(HD(hd->html_data)->head && !HTML_BASE(hd->html_data)){
+ PARAMETER *p;
+
+ for(p = HD(hd->html_data)->el_data->attribs;
+ p && p->attribute && strucmp(p->attribute, "HREF");
+ p = p->next)
+ ;
+
+ if(p && p->value && !((HTML_OPT_S *)(hd->html_data)->opt)->base)
+ ((HTML_OPT_S *)(hd->html_data)->opt)->base = cpystr(p->value);
+ }
+ }
+
+ return(0); /* DON'T get linked */
+}
+
+
+/*
+ * HTML <TITLE> element handler
+ */
+int
+html_title(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_DATA){
+ if(hd->x + 1 >= hd->y){
+ hd->y += 80;
+ fs_resize((void **)&hd->s, (size_t)hd->y * sizeof(unsigned char));
+ }
+
+ hd->s[hd->x++] = (unsigned char) ch;
+ }
+ else if(cmd == GF_RESET){
+ hd->x = 0L;
+ hd->y = 80L;
+ hd->s = (unsigned char *)fs_get((size_t)hd->y * sizeof(unsigned char));
+ }
+ else if(cmd == GF_EOD){
+ /* Down the road we probably want to give these bytes to
+ * someone...
+ */
+ hd->s[hd->x] = '\0';
+ fs_give((void **)&hd->s);
+ }
+
+ return(1); /* get linked */
+}
+
+
+/*
+ * HTML <BODY> element handler
+ */
+int
+html_body(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ if(PASS_HTML(hd->html_data)){
+ PARAMETER *p, *tp;
+ char **style = NULL, *text = NULL, *bgcolor = NULL, *pcs;
+
+ /* modify any attributes in a useful way? */
+ for(p = HD(hd->html_data)->el_data->attribs;
+ p && p->attribute;
+ p = p->next)
+ if(p->value){
+ if(!strucmp(p->attribute, "style"))
+ style = &p->value;
+ else if(!strucmp(p->attribute, "text"))
+ text = p->value;
+ /*
+ * bgcolor NOT passed since user setting takes precedence
+ *
+ else if(!strucmp(p->attribute, "bgcolor"))
+ bgcolor = p->value;
+ */
+ }
+
+ /* colors pretty much it */
+ if(text || bgcolor){
+ if(!style){
+ tp = (PARAMETER *)fs_get(sizeof(PARAMETER));
+ memset(tp, 0, sizeof(PARAMETER));
+ tp->next = HD(hd->html_data)->el_data->attribs;
+ HD(hd->html_data)->el_data->attribs = tp;
+ tp->attribute = cpystr("style");
+
+ tmp_20k_buf[0] = '\0';
+ style = &tp->value;
+ pcs = "%s%s%s%s%s";
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s", *style);
+ fs_give((void **) style);
+ pcs = "; %s%s%s%s%s";
+ }
+
+ snprintf(tmp_20k_buf + strlen(tmp_20k_buf),
+ SIZEOF_20KBUF - strlen(tmp_20k_buf),
+ pcs,
+ (text) ? "color: " : "", (text) ? text : "",
+ (text && bgcolor) ? ";" : "",
+ (bgcolor) ? "background-color: " : "", (bgcolor) ? bgcolor : "");
+ *style = cpystr(tmp_20k_buf);
+ }
+
+ html_output_raw_tag(hd->html_data, "div");
+ }
+
+ HD(hd->html_data)->body = 1;
+ }
+ else if(cmd == GF_EOD){
+ if(PASS_HTML(hd->html_data)){
+ html_output_string(hd->html_data, "</div>");
+ }
+
+ HD(hd->html_data)->body = 0;
+ }
+
+ return(1); /* get linked */
+}
+
+
+/*
+ * HTML <A> (Anchor) element handler
+ */
+int
+html_a(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+
+ if(hd->dp) /* remember text within anchor tags */
+ so_writec(ch, (STORE_S *) hd->dp);
+ }
+ else if(cmd == GF_RESET){
+ int i, n, x;
+ char buf[256];
+ HANDLE_S *h;
+ PARAMETER *p, *href = NULL, *name = NULL;
+
+ /*
+ * Pending Anchor!?!?
+ * space insertion/line breaking that's yet to get done...
+ */
+ if(HD(hd->html_data)->prefix){
+ dprint((2, "-- html error: nested or unterminated anchor\n"));
+ html_a_finish(hd);
+ }
+
+ /*
+ * Look for valid Anchor data vis the filter installer's parms
+ * (e.g., Only allow references to our internal URLs if asked)
+ */
+ for(p = HD(hd->html_data)->el_data->attribs;
+ p && p->attribute;
+ p = p->next)
+ if(!strucmp(p->attribute, "HREF")
+ && p->value
+ && (HANDLES_LOC(hd->html_data)
+ || struncmp(p->value, "x-alpine-", 9)
+ || struncmp(p->value, "x-pine-help", 11)
+ || p->value[0] == '#'))
+ href = p;
+ else if(!strucmp(p->attribute, "NAME"))
+ name = p;
+
+ if(DO_HANDLES(hd->html_data) && (href || name)){
+ h = new_handle(HANDLESP(hd->html_data));
+
+ /*
+ * Enhancement: we might want to get fancier and parse the
+ * href a bit further such that we can launch images using
+ * our image viewer, or browse local files or directories
+ * with our internal tools. Of course, having the jump-off
+ * point into text/html always be the defined "web-browser",
+ * just might be the least confusing UI-wise...
+ */
+ h->type = URL;
+
+ if(name && name->value)
+ h->h.url.name = cpystr(name->value);
+
+ /*
+ * Prepare to build embedded prefix...
+ */
+ HD(hd->html_data)->prefix = (int *) fs_get(64 * sizeof(int));
+ x = 0;
+
+ /*
+ * Is this something that looks like a URL? If not and
+ * we were giving some "base" string, proceed ala RFC1808...
+ */
+ if(href){
+ if(HTML_BASE(hd->html_data) && !rfc1738_scan(href->value, &n)){
+ html_a_relative(HTML_BASE(hd->html_data), href->value, h);
+ }
+ else if(!(NO_RELATIVE(hd->html_data) && html_href_relative(href->value)))
+ h->h.url.path = cpystr(href->value);
+
+ if(pico_usingcolor()){
+ char *fg = NULL, *bg = NULL, *q;
+
+ if(ps_global->VAR_SLCTBL_FORE_COLOR
+ && colorcmp(ps_global->VAR_SLCTBL_FORE_COLOR,
+ ps_global->VAR_NORM_FORE_COLOR))
+ fg = ps_global->VAR_SLCTBL_FORE_COLOR;
+
+ if(ps_global->VAR_SLCTBL_BACK_COLOR
+ && colorcmp(ps_global->VAR_SLCTBL_BACK_COLOR,
+ ps_global->VAR_NORM_BACK_COLOR))
+ bg = ps_global->VAR_SLCTBL_BACK_COLOR;
+
+ if(fg || bg){
+ COLOR_PAIR *tmp;
+
+ /*
+ * The blacks are just known good colors for testing
+ * whether the other color is good.
+ */
+ tmp = new_color_pair(fg ? fg : colorx(COL_BLACK),
+ bg ? bg : colorx(COL_BLACK));
+ if(pico_is_good_colorpair(tmp)){
+ q = color_embed(fg, bg);
+
+ for(i = 0; q[i]; i++)
+ HD(hd->html_data)->prefix[x++] = q[i];
+ }
+
+ if(tmp)
+ free_color_pair(&tmp);
+ }
+
+ if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global))
+ HD(hd->html_data)->prefix[x++] = HTML_DOBOLD;
+ }
+ else
+ HD(hd->html_data)->prefix[x++] = HTML_DOBOLD;
+ }
+
+ HD(hd->html_data)->prefix[x++] = TAG_EMBED;
+ HD(hd->html_data)->prefix[x++] = TAG_HANDLE;
+
+ snprintf(buf, sizeof(buf), "%ld", hd->x = h->key);
+ HD(hd->html_data)->prefix[x++] = n = strlen(buf);
+ for(i = 0; i < n; i++)
+ HD(hd->html_data)->prefix[x++] = buf[i];
+
+ HD(hd->html_data)->prefix_used = x;
+
+ hd->dp = (void *) so_get(CharStar, NULL, EDIT_ACCESS);
+ }
+ }
+ else if(cmd == GF_EOD){
+ html_a_finish(hd);
+ }
+
+ return(1); /* get linked */
+}
+
+
+void
+html_a_prefix(FILTER_S *f)
+{
+ int *prefix, n;
+
+ /* Do this so we don't visit from html_output... */
+ prefix = HD(f)->prefix;
+ HD(f)->prefix = NULL;
+
+ for(n = 0; n < HD(f)->prefix_used; n++)
+ html_a_output_prefix(f, prefix[n]);
+
+ fs_give((void **) &prefix);
+}
+
+
+/*
+ * html_a_finish - house keeping associated with end of link tag
+ */
+void
+html_a_finish(HANDLER_S *hd)
+{
+ if(DO_HANDLES(hd->html_data)){
+ if(HD(hd->html_data)->prefix){
+ if(!PASS_HTML(hd->html_data)){
+ char *empty_link = "[LINK]";
+ int i;
+
+ html_a_prefix(hd->html_data);
+ for(i = 0; empty_link[i]; i++)
+ html_output(hd->html_data, empty_link[i]);
+ }
+ }
+
+ if(pico_usingcolor()){
+ char *fg = NULL, *bg = NULL, *p;
+ int i;
+
+ if(ps_global->VAR_SLCTBL_FORE_COLOR
+ && colorcmp(ps_global->VAR_SLCTBL_FORE_COLOR,
+ ps_global->VAR_NORM_FORE_COLOR))
+ fg = ps_global->VAR_NORM_FORE_COLOR;
+
+ if(ps_global->VAR_SLCTBL_BACK_COLOR
+ && colorcmp(ps_global->VAR_SLCTBL_BACK_COLOR,
+ ps_global->VAR_NORM_BACK_COLOR))
+ bg = ps_global->VAR_NORM_BACK_COLOR;
+
+ if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global))
+ HTML_BOLD(hd->html_data, 0); /* turn OFF bold */
+
+ if(fg || bg){
+ COLOR_PAIR *tmp;
+
+ /*
+ * The blacks are just known good colors for testing
+ * whether the other color is good.
+ */
+ tmp = new_color_pair(fg ? fg : colorx(COL_BLACK),
+ bg ? bg : colorx(COL_BLACK));
+ if(pico_is_good_colorpair(tmp)){
+ p = color_embed(fg, bg);
+
+ for(i = 0; p[i]; i++)
+ html_output(hd->html_data, p[i]);
+ }
+
+ if(tmp)
+ free_color_pair(&tmp);
+ }
+ }
+ else
+ HTML_BOLD(hd->html_data, 0); /* turn OFF bold */
+
+ html_output(hd->html_data, TAG_EMBED);
+ html_output(hd->html_data, TAG_HANDLEOFF);
+
+ html_a_output_info(hd);
+ }
+}
+
+
+/*
+ * html_output_a_prefix - dump Anchor prefix data
+ */
+void
+html_a_output_prefix(FILTER_S *f, int c)
+{
+ switch(c){
+ case HTML_DOBOLD :
+ HTML_BOLD(f, 1);
+ break;
+
+ default :
+ html_output(f, c);
+ break;
+ }
+}
+
+
+
+/*
+ * html_a_output_info - dump possibly deceptive link info into text.
+ * phark the phishers.
+ */
+void
+html_a_output_info(HANDLER_S *hd)
+{
+ int l, risky = 0, hl = 0, tl;
+ char *url = NULL, *hn = NULL, *txt;
+ HANDLE_S *h;
+
+ /* find host anchor references */
+ if((h = get_handle(*HANDLESP(hd->html_data), (int) hd->x)) != NULL
+ && h->h.url.path != NULL
+ && (hn = rfc1738_scan(rfc1738_str(url = cpystr(h->h.url.path)), &l)) != NULL
+ && (hn = srchstr(hn,"://")) != NULL){
+
+ for(hn += 3, hl = 0; hn[hl] && hn[hl] != '/' && hn[hl] != '?'; hl++)
+ ;
+ }
+
+ if(hn && hl){
+ /*
+ * look over anchor's text to see if there's a
+ * mismatch between href target and url-ish
+ * looking text. throw a red flag if so.
+ * similarly, toss one if the target's referenced
+ * by a
+ */
+ if(hd->dp){
+ so_writec('\0', (STORE_S *) hd->dp);
+
+ if((txt = (char *) so_text((STORE_S *) hd->dp)) != NULL
+ && (txt = rfc1738_scan(txt, &tl)) != NULL
+ && (txt = srchstr(txt,"://")) != NULL){
+
+ for(txt += 3, tl = 0; txt[tl] && txt[tl] != '/' && txt[tl] != '?'; tl++)
+ ;
+
+ if(tl != hl)
+ risky++;
+ else
+ /* look for non matching text */
+ for(l = 0; l < tl && l < hl; l++)
+ if(tolower((unsigned char) txt[l]) != tolower((unsigned char) hn[l])){
+ risky++;
+ break;
+ }
+ }
+
+ so_give((STORE_S **) &hd->dp);
+ }
+
+ /* look for literal IP, anything possibly encoded or auth specifier */
+ if(!risky){
+ int digits = 1;
+
+ for(l = 0; l < hl; l++){
+ if(hn[l] == '@' || hn[l] == '%'){
+ risky++;
+ break;
+ }
+ else if(!(hn[l] == '.' || isdigit((unsigned char) hn[l])))
+ digits = 0;
+ }
+
+ if(digits)
+ risky++;
+ }
+
+ /* Insert text of link's domain */
+ if(SHOWSERVER(hd->html_data)){
+ char *q;
+ COLOR_PAIR *col = NULL, *colnorm = NULL;
+
+ html_output(hd->html_data, ' ');
+ html_output(hd->html_data, '[');
+
+ if(pico_usingcolor()
+ && ps_global->VAR_METAMSG_FORE_COLOR
+ && ps_global->VAR_METAMSG_BACK_COLOR
+ && (col = new_color_pair(ps_global->VAR_METAMSG_FORE_COLOR,
+ ps_global->VAR_METAMSG_BACK_COLOR))){
+ if(!pico_is_good_colorpair(col))
+ free_color_pair(&col);
+
+ if(col){
+ q = color_embed(col->fg, col->bg);
+
+ for(l = 0; q[l]; l++)
+ html_output(hd->html_data, q[l]);
+ }
+ }
+
+ for(l = 0; l < hl; l++)
+ html_output(hd->html_data, hn[l]);
+
+ if(col){
+ if(ps_global->VAR_NORM_FORE_COLOR
+ && ps_global->VAR_NORM_BACK_COLOR
+ && (colnorm = new_color_pair(ps_global->VAR_NORM_FORE_COLOR,
+ ps_global->VAR_NORM_BACK_COLOR))){
+ if(!pico_is_good_colorpair(colnorm))
+ free_color_pair(&colnorm);
+
+ if(colnorm){
+ q = color_embed(colnorm->fg, colnorm->bg);
+ free_color_pair(&colnorm);
+
+ for(l = 0; q[l]; l++)
+ html_output(hd->html_data, q[l]);
+ }
+ }
+
+ free_color_pair(&col);
+ }
+
+ html_output(hd->html_data, ']');
+ }
+ }
+
+ /*
+ * if things look OK so far, make sure nothing within
+ * the url looks too fishy...
+ */
+ while(!risky && hn
+ && (hn = rfc1738_scan(hn, &l)) != NULL
+ && (hn = srchstr(hn,"://")) != NULL){
+ int digits = 1;
+
+ for(hn += 3, hl = 0; hn[hl] && hn[hl] != '/' && hn[hl] != '?'; hl++){
+ /*
+ * auth spec, encoded characters, or possibly non-standard port
+ * should raise a red flag
+ */
+ if(hn[hl] == '@' || hn[hl] == '%' || hn[hl] == ':'){
+ risky++;
+ break;
+ }
+ else if(!(hn[hl] == '.' || isdigit((unsigned char) hn[hl])))
+ digits = 0;
+ }
+
+ /* dotted-dec/raw-int address should cause suspicion as well */
+ if(digits)
+ risky++;
+ }
+
+ if(risky && ((HTML_OPT_S *) hd->html_data->opt)->warnrisk_f)
+ (*((HTML_OPT_S *) hd->html_data->opt)->warnrisk_f)();
+
+ fs_give((void **) &url);
+}
+
+
+
+/*
+ * relative_url - put full url path in h based on base and relative url
+ */
+void
+html_a_relative(char *base_url, char *rel_url, HANDLE_S *h)
+{
+ size_t len;
+ char tmp[MAILTMPLEN], *p, *q;
+ char *scheme = NULL, *net = NULL, *path = NULL,
+ *parms = NULL, *query = NULL, *frag = NULL,
+ *base_scheme = NULL, *base_net_loc = NULL,
+ *base_path = NULL, *base_parms = NULL,
+ *base_query = NULL, *base_frag = NULL,
+ *rel_scheme = NULL, *rel_net_loc = NULL,
+ *rel_path = NULL, *rel_parms = NULL,
+ *rel_query = NULL, *rel_frag = NULL;
+
+ /* Rough parse of base URL */
+ rfc1808_tokens(base_url, &base_scheme, &base_net_loc, &base_path,
+ &base_parms, &base_query, &base_frag);
+
+ /* Rough parse of this URL */
+ rfc1808_tokens(rel_url, &rel_scheme, &rel_net_loc, &rel_path,
+ &rel_parms, &rel_query, &rel_frag);
+
+ scheme = rel_scheme; /* defaults */
+ net = rel_net_loc;
+ path = rel_path;
+ parms = rel_parms;
+ query = rel_query;
+ frag = rel_frag;
+ if(!scheme && base_scheme){
+ scheme = base_scheme;
+ if(!net){
+ net = base_net_loc;
+ if(path){
+ if(*path != '/'){
+ if(base_path){
+ for(p = q = base_path; /* Drop base path's tail */
+ (p = strchr(p, '/'));
+ q = ++p)
+ ;
+
+ len = q - base_path;
+ }
+ else
+ len = 0;
+
+ if(len + strlen(rel_path) < sizeof(tmp)-1){
+ if(len)
+ snprintf(path = tmp, sizeof(tmp), "%.*s", len, base_path);
+
+ strncpy(tmp + len, rel_path, sizeof(tmp)-len);
+ tmp[sizeof(tmp)-1] = '\0';
+
+ /* Follow RFC 1808 "Step 6" */
+ for(p = tmp; (p = strchr(p, '.')); )
+ switch(*(p+1)){
+ /*
+ * a) All occurrences of "./", where "." is a
+ * complete path segment, are removed.
+ */
+ case '/' :
+ if(p > tmp)
+ for(q = p; (*q = *(q+2)) != '\0'; q++)
+ ;
+ else
+ p++;
+
+ break;
+
+ /*
+ * b) If the path ends with "." as a
+ * complete path segment, that "." is
+ * removed.
+ */
+ case '\0' :
+ if(p == tmp || *(p-1) == '/')
+ *p = '\0';
+ else
+ p++;
+
+ break;
+
+ /*
+ * c) All occurrences of "<segment>/../",
+ * where <segment> is a complete path
+ * segment not equal to "..", are removed.
+ * Removal of these path segments is
+ * performed iteratively, removing the
+ * leftmost matching pattern on each
+ * iteration, until no matching pattern
+ * remains.
+ *
+ * d) If the path ends with "<segment>/..",
+ * where <segment> is a complete path
+ * segment not equal to "..", that
+ * "<segment>/.." is removed.
+ */
+ case '.' :
+ if(p > tmp + 1){
+ for(q = p - 2; q > tmp && *q != '/'; q--)
+ ;
+
+ if(*q == '/')
+ q++;
+
+ if(q + 1 == p /* no "//.." */
+ || (*q == '.' /* and "../.." */
+ && *(q+1) == '.'
+ && *(q+2) == '/')){
+ p += 2;
+ break;
+ }
+
+ switch(*(p+2)){
+ case '/' :
+ len = (p - q) + 3;
+ p = q;
+ for(; (*q = *(q+len)) != '\0'; q++)
+ ;
+
+ break;
+
+ case '\0':
+ *(p = q) = '\0';
+ break;
+
+ default:
+ p += 2;
+ break;
+ }
+ }
+ else
+ p += 2;
+
+ break;
+
+ default :
+ p++;
+ break;
+ }
+ }
+ else
+ path = ""; /* lame. */
+ }
+ }
+ else{
+ path = base_path;
+ if(!parms){
+ parms = base_parms;
+ if(!query)
+ query = base_query;
+ }
+ }
+ }
+ }
+
+ len = (scheme ? strlen(scheme) : 0) + (net ? strlen(net) : 0)
+ + (path ? strlen(path) : 0) + (parms ? strlen(parms) : 0)
+ + (query ? strlen(query) : 0) + (frag ? strlen(frag ) : 0) + 8;
+
+ h->h.url.path = (char *) fs_get(len * sizeof(char));
+ snprintf(h->h.url.path, len, "%s%s%s%s%s%s%s%s%s%s%s%s",
+ scheme ? scheme : "", scheme ? ":" : "",
+ net ? "//" : "", net ? net : "",
+ (path && *path == '/') ? "" : ((path && net) ? "/" : ""),
+ path ? path : "",
+ parms ? ";" : "", parms ? parms : "",
+ query ? "?" : "", query ? query : "",
+ frag ? "#" : "", frag ? frag : "");
+
+ if(base_scheme)
+ fs_give((void **) &base_scheme);
+
+ if(base_net_loc)
+ fs_give((void **) &base_net_loc);
+
+ if(base_path)
+ fs_give((void **) &base_path);
+
+ if(base_parms)
+ fs_give((void **) &base_parms);
+
+ if(base_query)
+ fs_give((void **) &base_query);
+
+ if(base_frag)
+ fs_give((void **) &base_frag);
+
+ if(rel_scheme)
+ fs_give((void **) &rel_scheme);
+
+ if(rel_net_loc)
+ fs_give((void **) &rel_net_loc);
+
+ if(rel_parms)
+ fs_give((void **) &rel_parms);
+
+ if(rel_query)
+ fs_give((void **) &rel_query);
+
+ if(rel_frag)
+ fs_give((void **) &rel_frag);
+
+ if(rel_path)
+ fs_give((void **) &rel_path);
+}
+
+
+/*
+ * html_href_relative - href
+ */
+int
+html_href_relative(char *url)
+{
+ int i;
+
+ if(url)
+ for(i = 0; i < 32 && url[i]; i++)
+ if(!(isalpha((unsigned char) url[i]) || url[i] == '_' || url[i] == '-')){
+ if(url[i] == ':')
+ return(FALSE);
+ else
+ break;
+ }
+
+ return(TRUE);
+}
+
+
+/*
+ * HTML <UL> (Unordered List) element handler
+ */
+int
+html_ul(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ if(PASS_HTML(hd->html_data)){
+ html_output_raw_tag(hd->html_data, "ul");
+ }
+ else{
+ HD(hd->html_data)->li_pending = 1;
+ html_blank(hd->html_data, 0);
+ }
+ }
+ else if(cmd == GF_EOD){
+ if(PASS_HTML(hd->html_data)){
+ html_output_string(hd->html_data, "</ul>");
+ }
+ else{
+ html_blank(hd->html_data, 0);
+
+ if(!HD(hd->html_data)->li_pending)
+ html_indent(hd->html_data, -4, HTML_ID_INC);
+ else
+ HD(hd->html_data)->li_pending = 0;
+ }
+ }
+
+ return(1); /* get linked */
+}
+
+
+/*
+ * HTML <OL> (Ordered List) element handler
+ */
+int
+html_ol(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ if(PASS_HTML(hd->html_data)){
+ html_output_raw_tag(hd->html_data, "ol");
+ }
+ else{
+ /*
+ * Signal that we're expecting to see <LI> as our next elemnt
+ * and set the the initial ordered count.
+ */
+ HD(hd->html_data)->li_pending = 1;
+ hd->x = 1L;
+ html_blank(hd->html_data, 0);
+ }
+ }
+ else if(cmd == GF_EOD){
+ if(PASS_HTML(hd->html_data)){
+ html_output_string(hd->html_data, "</ol>");
+ }
+ else{
+ html_blank(hd->html_data, 0);
+
+ if(!HD(hd->html_data)->li_pending)
+ html_indent(hd->html_data, -4, HTML_ID_INC);
+ else
+ HD(hd->html_data)->li_pending = 0;
+ }
+ }
+
+ return(1); /* get linked */
+}
+
+
+/*
+ * HTML <MENU> (Menu List) element handler
+ */
+int
+html_menu(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ if(PASS_HTML(hd->html_data)){
+ html_output_raw_tag(hd->html_data, "menu");
+ }
+ else{
+ HD(hd->html_data)->li_pending = 1;
+ }
+ }
+ else if(cmd == GF_EOD){
+ if(PASS_HTML(hd->html_data)){
+ html_output_string(hd->html_data, "</menu>");
+ }
+ else{
+ html_blank(hd->html_data, 0);
+
+ if(!HD(hd->html_data)->li_pending)
+ html_indent(hd->html_data, -4, HTML_ID_INC);
+ else
+ HD(hd->html_data)->li_pending = 0;
+ }
+ }
+
+ return(1); /* get linked */
+}
+
+
+/*
+ * HTML <DIR> (Directory List) element handler
+ */
+int
+html_dir(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ if(PASS_HTML(hd->html_data)){
+ html_output_raw_tag(hd->html_data, "dir");
+ }
+ else{
+ HD(hd->html_data)->li_pending = 1;
+ }
+ }
+ else if(cmd == GF_EOD){
+ if(PASS_HTML(hd->html_data)){
+ html_output_string(hd->html_data, "</dir>");
+ }
+ else{
+ html_blank(hd->html_data, 0);
+
+ if(!HD(hd->html_data)->li_pending)
+ html_indent(hd->html_data, -4, HTML_ID_INC);
+ else
+ HD(hd->html_data)->li_pending = 0;
+ }
+ }
+
+ return(1); /* get linked */
+}
+
+
+/*
+ * HTML <LI> (List Item) element handler
+ */
+int
+html_li(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_DATA){
+ if(PASS_HTML(hd->html_data)){
+ html_handoff(hd, ch);
+ }
+ }
+ else if(cmd == GF_RESET){
+ HANDLER_S *p, *found = NULL;
+
+ /*
+ * There better be a an unordered list, ordered list,
+ * Menu or Directory handler installed
+ * or else we crap out...
+ */
+ for(p = HANDLERS(hd->html_data); p; p = p->below)
+ if(EL(p)->handler == html_ul
+ || EL(p)->handler == html_ol
+ || EL(p)->handler == html_menu
+ || EL(p)->handler == html_dir){
+ found = p;
+ break;
+ }
+
+ if(found){
+ if(PASS_HTML(hd->html_data)){
+ }
+ else{
+ char buf[8], *p;
+ int wrapstate;
+
+ /* Start a new line */
+ html_blank(hd->html_data, 0);
+
+ /* adjust indent level if needed */
+ if(HD(hd->html_data)->li_pending){
+ html_indent(hd->html_data, 4, HTML_ID_INC);
+ HD(hd->html_data)->li_pending = 0;
+ }
+
+ if(EL(found)->handler == html_ul){
+ int l = html_indent(hd->html_data, 0, HTML_ID_GET);
+
+ strncpy(buf, " ", sizeof(buf));
+ buf[1] = (l < 5) ? '*' : (l < 9) ? '+' : (l < 17) ? 'o' : '#';
+ }
+ else if(EL(found)->handler == html_ol)
+ snprintf(buf, sizeof(buf), "%2ld.", found->x++);
+ else if(EL(found)->handler == html_menu){
+ strncpy(buf, " ->", sizeof(buf));
+ buf[sizeof(buf)-1] = '\0';
+ }
+
+ html_indent(hd->html_data, -4, HTML_ID_INC);
+
+ /* So we don't munge whitespace */
+ wrapstate = HD(hd->html_data)->wrapstate;
+ HD(hd->html_data)->wrapstate = 0;
+
+ html_write_indent(hd->html_data, HD(hd->html_data)->indent_level);
+ for(p = buf; *p; p++)
+ html_output(hd->html_data, (int) *p);
+
+ HD(hd->html_data)->wrapstate = wrapstate;
+ html_indent(hd->html_data, 4, HTML_ID_INC);
+ }
+ /* else BUG: should really bitch about this */
+ }
+
+ if(PASS_HTML(hd->html_data)){
+ html_output_raw_tag(hd->html_data, "li");
+ return(1); /* get linked */
+ }
+ }
+ else if(cmd == GF_EOD){
+ if(PASS_HTML(hd->html_data)){
+ html_output_string(hd->html_data, "</li>");
+ }
+ }
+
+ return(PASS_HTML(hd->html_data)); /* DON'T get linked */
+}
+
+
+/*
+ * HTML <DL> (Definition List) element handler
+ */
+int
+html_dl(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ if(PASS_HTML(hd->html_data)){
+ html_output_raw_tag(hd->html_data, "dl");
+ }
+ else{
+ /*
+ * Set indention level for definition terms and definitions...
+ */
+ hd->x = html_indent(hd->html_data, 0, HTML_ID_GET);
+ hd->y = hd->x + 2;
+ hd->z = hd->y + 4;
+ }
+ }
+ else if(cmd == GF_EOD){
+ if(PASS_HTML(hd->html_data)){
+ html_output_string(hd->html_data, "</dl>");
+ }
+ else{
+ html_indent(hd->html_data, (int) hd->x, HTML_ID_SET);
+ html_blank(hd->html_data, 1);
+ }
+ }
+
+ return(1); /* get linked */
+}
+
+
+/*
+ * HTML <DT> (Definition Term) element handler
+ */
+int
+html_dt(HANDLER_S *hd, int ch, int cmd)
+{
+ if(PASS_HTML(hd->html_data)){
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ html_output_raw_tag(hd->html_data, "dt");
+ }
+ else if(cmd == GF_EOD){
+ html_output_string(hd->html_data, "</dt>");
+ }
+
+ return(1); /* get linked */
+ }
+
+ if(cmd == GF_RESET){
+ HANDLER_S *p;
+
+ /*
+ * There better be a Definition Handler installed
+ * or else we crap out...
+ */
+ for(p = HANDLERS(hd->html_data); p && EL(p)->handler != html_dl; p = p->below)
+ ;
+
+ if(p){ /* adjust indent level if needed */
+ html_indent(hd->html_data, (int) p->y, HTML_ID_SET);
+ html_blank(hd->html_data, 1);
+ }
+ /* BUG: else should really bitch about this */
+ }
+
+ return(0); /* DON'T get linked */
+}
+
+
+/*
+ * HTML <DD> (Definition Definition) element handler
+ */
+int
+html_dd(HANDLER_S *hd, int ch, int cmd)
+{
+ if(PASS_HTML(hd->html_data)){
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ html_output_raw_tag(hd->html_data, "dd");
+ }
+ else if(cmd == GF_EOD){
+ html_output_string(hd->html_data, "</dd>");
+ }
+
+ return(1); /* get linked */
+ }
+
+ if(cmd == GF_RESET){
+ HANDLER_S *p;
+
+ /*
+ * There better be a Definition Handler installed
+ * or else we crap out...
+ */
+ for(p = HANDLERS(hd->html_data); p && EL(p)->handler != html_dl; p = p->below)
+ ;
+
+ if(p){ /* adjust indent level if needed */
+ html_indent(hd->html_data, (int) p->z, HTML_ID_SET);
+ html_blank(hd->html_data, 0);
+ }
+ /* BUG: should really bitch about this */
+ }
+
+ return(0); /* DON'T get linked */
+}
+
+
+/*
+ * HTML <H1> (Headings 1) element handler.
+ *
+ * Bold, very-large font, CENTERED. One or two blank lines
+ * above and below. For our silly character cell's that
+ * means centered and ALL CAPS...
+ */
+int
+html_h1(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ if(PASS_HTML(hd->html_data)){
+ html_output_raw_tag(hd->html_data, "h1");
+ }
+ else{
+ /* turn ON the centered bit */
+ CENTER_BIT(hd->html_data) = 1;
+ }
+ }
+ else if(cmd == GF_EOD){
+ if(PASS_HTML(hd->html_data)){
+ html_output_string(hd->html_data, "</h1>");
+ }
+ else{
+ /* turn OFF the centered bit, add blank line */
+ CENTER_BIT(hd->html_data) = 0;
+ html_blank(hd->html_data, 1);
+ }
+ }
+
+ return(1); /* get linked */
+}
+
+
+/*
+ * HTML <H2> (Headings 2) element handler
+ */
+int
+html_h2(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_DATA){
+ if(PASS_HTML(hd->html_data)){
+ html_handoff(hd, ch);
+ }
+ else{
+ if((hd->x & HTML_HX_ULINE) && !ASCII_ISSPACE((unsigned char) (ch & 0xff))){
+ HTML_ULINE(hd->html_data, 1);
+ hd->x ^= HTML_HX_ULINE; /* only once! */
+ }
+
+ html_handoff(hd, (ch < 128 && islower((unsigned char) ch))
+ ? toupper((unsigned char) ch) : ch);
+ }
+ }
+ else if(cmd == GF_RESET){
+ if(PASS_HTML(hd->html_data)){
+ html_output_raw_tag(hd->html_data, "h2");
+ }
+ else{
+ /*
+ * Bold, large font, flush-left. One or two blank lines
+ * above and below.
+ */
+ if(CENTER_BIT(hd->html_data)) /* stop centering for now */
+ hd->x = HTML_HX_CENTER;
+ else
+ hd->x = 0;
+
+ hd->x |= HTML_HX_ULINE;
+
+ CENTER_BIT(hd->html_data) = 0;
+ hd->y = html_indent(hd->html_data, 0, HTML_ID_SET);
+ hd->z = HD(hd->html_data)->wrapcol;
+ HD(hd->html_data)->wrapcol = WRAP_COLS(hd->html_data) - 8;
+ html_blank(hd->html_data, 1);
+ }
+ }
+ else if(cmd == GF_EOD){
+ if(PASS_HTML(hd->html_data)){
+ html_output_string(hd->html_data, "</h2>");
+ }
+ else{
+ /*
+ * restore previous centering, and indent level
+ */
+ if(!(hd->x & HTML_HX_ULINE))
+ HTML_ULINE(hd->html_data, 0);
+
+ html_indent(hd->html_data, hd->y, HTML_ID_SET);
+ html_blank(hd->html_data, 1);
+ CENTER_BIT(hd->html_data) = (hd->x & HTML_HX_CENTER) != 0;
+ HD(hd->html_data)->wrapcol = hd->z;
+ }
+ }
+
+ return(1); /* get linked */
+}
+
+
+/*
+ * HTML <H3> (Headings 3) element handler
+ */
+int
+html_h3(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_DATA){
+ if(!PASS_HTML(hd->html_data)){
+ if((hd->x & HTML_HX_ULINE) && !ASCII_ISSPACE((unsigned char) (ch & 0xff))){
+ HTML_ULINE(hd->html_data, 1);
+ hd->x ^= HTML_HX_ULINE; /* only once! */
+ }
+ }
+
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ if(PASS_HTML(hd->html_data)){
+ html_output_raw_tag(hd->html_data, "h3");
+ }
+ else{
+ /*
+ * Italic, large font, slightly indented from the left
+ * margin. One or two blank lines above and below.
+ */
+ if(CENTER_BIT(hd->html_data)) /* stop centering for now */
+ hd->x = HTML_HX_CENTER;
+ else
+ hd->x = 0;
+
+ hd->x |= HTML_HX_ULINE;
+ CENTER_BIT(hd->html_data) = 0;
+ hd->y = html_indent(hd->html_data, 2, HTML_ID_SET);
+ hd->z = HD(hd->html_data)->wrapcol;
+ HD(hd->html_data)->wrapcol = WRAP_COLS(hd->html_data) - 8;
+ html_blank(hd->html_data, 1);
+ }
+ }
+ else if(cmd == GF_EOD){
+ if(PASS_HTML(hd->html_data)){
+ html_output_string(hd->html_data, "</h3>");
+ }
+ else{
+ /*
+ * restore previous centering, and indent level
+ */
+ if(!(hd->x & HTML_HX_ULINE))
+ HTML_ULINE(hd->html_data, 0);
+
+ html_indent(hd->html_data, hd->y, HTML_ID_SET);
+ html_blank(hd->html_data, 1);
+ CENTER_BIT(hd->html_data) = (hd->x & HTML_HX_CENTER) != 0;
+ HD(hd->html_data)->wrapcol = hd->z;
+ }
+ }
+
+ return(1); /* get linked */
+}
+
+
+/*
+ * HTML <H4> (Headings 4) element handler
+ */
+int
+html_h4(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ if(PASS_HTML(hd->html_data)){
+ html_output_raw_tag(hd->html_data, "h4");
+ }
+ else{
+ /*
+ * Bold, normal font, indented more than H3. One blank line
+ * above and below.
+ */
+ hd->x = CENTER_BIT(hd->html_data); /* stop centering for now */
+ CENTER_BIT(hd->html_data) = 0;
+ hd->y = html_indent(hd->html_data, 4, HTML_ID_SET);
+ hd->z = HD(hd->html_data)->wrapcol;
+ HD(hd->html_data)->wrapcol = WRAP_COLS(hd->html_data) - 8;
+ html_blank(hd->html_data, 1);
+ }
+ }
+ else if(cmd == GF_EOD){
+ if(PASS_HTML(hd->html_data)){
+ html_output_string(hd->html_data, "</h4>");
+ }
+ else{
+ /*
+ * restore previous centering, and indent level
+ */
+ html_indent(hd->html_data, (int) hd->y, HTML_ID_SET);
+ html_blank(hd->html_data, 1);
+ CENTER_BIT(hd->html_data) = hd->x;
+ HD(hd->html_data)->wrapcol = hd->z;
+ }
+ }
+
+ return(1); /* get linked */
+}
+
+
+/*
+ * HTML <H5> (Headings 5) element handler
+ */
+int
+html_h5(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ if(PASS_HTML(hd->html_data)){
+ html_output_raw_tag(hd->html_data, "h5");
+ }
+ else{
+ /*
+ * Italic, normal font, indented as H4. One blank line
+ * above.
+ */
+ hd->x = CENTER_BIT(hd->html_data); /* stop centering for now */
+ CENTER_BIT(hd->html_data) = 0;
+ hd->y = html_indent(hd->html_data, 6, HTML_ID_SET);
+ hd->z = HD(hd->html_data)->wrapcol;
+ HD(hd->html_data)->wrapcol = WRAP_COLS(hd->html_data) - 8;
+ html_blank(hd->html_data, 1);
+ }
+ }
+ else if(cmd == GF_EOD){
+ if(PASS_HTML(hd->html_data)){
+ html_output_string(hd->html_data, "</h5>");
+ }
+ else{
+ /*
+ * restore previous centering, and indent level
+ */
+ html_indent(hd->html_data, (int) hd->y, HTML_ID_SET);
+ html_blank(hd->html_data, 1);
+ CENTER_BIT(hd->html_data) = hd->x;
+ HD(hd->html_data)->wrapcol = hd->z;
+ }
+ }
+
+ return(1); /* get linked */
+}
+
+
+/*
+ * HTML <H6> (Headings 6) element handler
+ */
+int
+html_h6(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ if(PASS_HTML(hd->html_data)){
+ html_output_raw_tag(hd->html_data, "h6");
+ }
+ else{
+ /*
+ * Bold, indented same as normal text, more than H5. One
+ * blank line above.
+ */
+ hd->x = CENTER_BIT(hd->html_data); /* stop centering for now */
+ CENTER_BIT(hd->html_data) = 0;
+ hd->y = html_indent(hd->html_data, 8, HTML_ID_SET);
+ hd->z = HD(hd->html_data)->wrapcol;
+ HD(hd->html_data)->wrapcol = WRAP_COLS(hd->html_data) - 8;
+ html_blank(hd->html_data, 1);
+ }
+ }
+ else if(cmd == GF_EOD){
+ if(PASS_HTML(hd->html_data)){
+ html_output_string(hd->html_data, "</h6>");
+ }
+ else{
+ /*
+ * restore previous centering, and indent level
+ */
+ html_indent(hd->html_data, (int) hd->y, HTML_ID_SET);
+ html_blank(hd->html_data, 1);
+ CENTER_BIT(hd->html_data) = hd->x;
+ HD(hd->html_data)->wrapcol = hd->z;
+ }
+ }
+
+ return(1); /* get linked */
+}
+
+
+/*
+ * HTML <BlockQuote> element handler
+ */
+int
+html_blockquote(HANDLER_S *hd, int ch, int cmd)
+{
+ int j;
+#define HTML_BQ_INDENT 6
+
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ if(PASS_HTML(hd->html_data)){
+ html_output_raw_tag(hd->html_data, "blockquote");
+ }
+ else{
+ /*
+ * A typical rendering might be a slight extra left and
+ * right indent, and/or italic font. The Blockquote element
+ * causes a paragraph break, and typically provides space
+ * above and below the quote.
+ */
+ html_indent(hd->html_data, HTML_BQ_INDENT, HTML_ID_INC);
+ j = HD(hd->html_data)->wrapstate;
+ HD(hd->html_data)->wrapstate = 0;
+ html_blank(hd->html_data, 1);
+ HD(hd->html_data)->wrapstate = j;
+ HD(hd->html_data)->wrapcol -= HTML_BQ_INDENT;
+ }
+ }
+ else if(cmd == GF_EOD){
+ if(PASS_HTML(hd->html_data)){
+ html_output_string(hd->html_data, "</blockquote>");
+ }
+ else{
+ html_blank(hd->html_data, 1);
+
+ j = HD(hd->html_data)->wrapstate;
+ HD(hd->html_data)->wrapstate = 0;
+ html_indent(hd->html_data, -(HTML_BQ_INDENT), HTML_ID_INC);
+ HD(hd->html_data)->wrapstate = j;
+ HD(hd->html_data)->wrapcol += HTML_BQ_INDENT;
+ }
+ }
+
+ return(1); /* get linked */
+}
+
+
+/*
+ * HTML <Address> element handler
+ */
+int
+html_address(HANDLER_S *hd, int ch, int cmd)
+{
+ int j;
+#define HTML_ADD_INDENT 2
+
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ if(PASS_HTML(hd->html_data)){
+ html_output_raw_tag(hd->html_data, "address");
+ }
+ else{
+ /*
+ * A typical rendering might be a slight extra left and
+ * right indent, and/or italic font. The Blockquote element
+ * causes a paragraph break, and typically provides space
+ * above and below the quote.
+ */
+ html_indent(hd->html_data, HTML_ADD_INDENT, HTML_ID_INC);
+ j = HD(hd->html_data)->wrapstate;
+ HD(hd->html_data)->wrapstate = 0;
+ html_blank(hd->html_data, 1);
+ HD(hd->html_data)->wrapstate = j;
+ }
+ }
+ else if(cmd == GF_EOD){
+ if(PASS_HTML(hd->html_data)){
+ html_output_string(hd->html_data, "</address>");
+ }
+ else{
+ html_blank(hd->html_data, 1);
+
+ j = HD(hd->html_data)->wrapstate;
+ HD(hd->html_data)->wrapstate = 0;
+ html_indent(hd->html_data, -(HTML_ADD_INDENT), HTML_ID_INC);
+ HD(hd->html_data)->wrapstate = j;
+ }
+ }
+
+ return(1); /* get linked */
+}
+
+
+/*
+ * HTML <PRE> (Preformatted Text) element handler
+ */
+int
+html_pre(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_DATA){
+ /*
+ * remove CRLF after '>' in element.
+ * We see CRLF because wrapstate is off.
+ */
+ switch(hd->y){
+ case 2 :
+ if(ch == '\012'){
+ hd->y = 3;
+ return(1);
+ }
+ else
+ html_handoff(hd, '\015');
+
+ break;
+
+ case 1 :
+ if(ch == '\015'){
+ hd->y = 2;
+ return(1);
+ }
+
+ case 3 :
+ /* passing tags? replace CRLF with <BR> to make
+ * sure hard newline survives in the end...
+ */
+ if(PASS_HTML(hd->html_data))
+ hd->y = 4; /* keep looking for CRLF */
+ else
+ hd->y = 0; /* stop looking */
+
+ break;
+
+ case 4 :
+ if(ch == '\015'){
+ hd->y = 5;
+ return(1);
+ }
+
+ break;
+
+ case 5 :
+ hd->y = 4;
+ if(ch == '\012'){
+ html_output_string(hd->html_data, "<br />");
+ return(1);
+ }
+ else
+ html_handoff(hd, '\015'); /* not CRLF, pass raw CR */
+
+ break;
+
+ default : /* zero case */
+ break;
+ }
+
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ hd->y = 1;
+ if(PASS_HTML(hd->html_data)){
+ html_output_raw_tag(hd->html_data, "pre");
+ }
+ else{
+ if(hd->html_data)
+ hd->html_data->f1 = DFL; \
+
+ html_blank(hd->html_data, 1);
+ hd->x = HD(hd->html_data)->wrapstate;
+ HD(hd->html_data)->wrapstate = 0;
+ }
+ }
+ else if(cmd == GF_EOD){
+ if(PASS_HTML(hd->html_data)){
+ html_output_string(hd->html_data, "</pre>");
+ }
+ else{
+ HD(hd->html_data)->wrapstate = (hd->x != 0);
+ html_blank(hd->html_data, 0);
+ }
+ }
+
+ return(1);
+}
+
+
+/*
+ * HTML <CENTER> (Centerd Text) element handler
+ */
+int
+html_center(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ if(PASS_HTML(hd->html_data)){
+ html_output_raw_tag(hd->html_data, "center");
+ }
+ else{
+ /* turn ON the centered bit */
+ CENTER_BIT(hd->html_data) = 1;
+ }
+ }
+ else if(cmd == GF_EOD){
+ if(PASS_HTML(hd->html_data)){
+ html_output_string(hd->html_data, "</center>");
+ }
+ else{
+ /* turn OFF the centered bit */
+ CENTER_BIT(hd->html_data) = 0;
+ }
+ }
+
+ return(1);
+}
+
+
+/*
+ * HTML <DIV> (Document Divisions) element handler
+ */
+int
+html_div(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ if(PASS_HTML(hd->html_data)){
+ html_output_raw_tag(hd->html_data, "div");
+ }
+ else{
+ PARAMETER *p;
+
+ for(p = HD(hd->html_data)->el_data->attribs;
+ p && p->attribute;
+ p = p->next)
+ if(!strucmp(p->attribute, "ALIGN")){
+ if(p->value){
+ /* remember previous values */
+ hd->x = CENTER_BIT(hd->html_data);
+ hd->y = html_indent(hd->html_data, 0, HTML_ID_GET);
+
+ html_blank(hd->html_data, 0);
+ CENTER_BIT(hd->html_data) = !strucmp(p->value, "CENTER");
+ html_indent(hd->html_data, 0, HTML_ID_SET);
+ /* NOTE: "RIGHT" not supported yet */
+ }
+ }
+ }
+ }
+ else if(cmd == GF_EOD){
+ if(PASS_HTML(hd->html_data)){
+ html_output_string(hd->html_data, "</div>");
+ }
+ else{
+ /* restore centered bit and indentiousness */
+ CENTER_BIT(hd->html_data) = hd->y;
+ html_indent(hd->html_data, hd->y, HTML_ID_SET);
+ html_blank(hd->html_data, 0);
+ }
+ }
+
+ return(1);
+}
+
+
+/*
+ * HTML <SPAN> (Text Span) element handler
+ */
+int
+html_span(HANDLER_S *hd, int ch, int cmd)
+{
+ if(PASS_HTML(hd->html_data)){
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ html_output_raw_tag(hd->html_data, "span");
+ }
+ else if(cmd == GF_EOD){
+ html_output_string(hd->html_data, "</span>");
+ }
+
+ return(1);
+ }
+
+ return(0);
+}
+
+
+/*
+ * HTML <KBD> (Text Kbd) element handler
+ */
+int
+html_kbd(HANDLER_S *hd, int ch, int cmd)
+{
+ if(PASS_HTML(hd->html_data)){
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ html_output_raw_tag(hd->html_data, "kbd");
+ }
+ else if(cmd == GF_EOD){
+ html_output_string(hd->html_data, "</kbd>");
+ }
+
+ return(1);
+ }
+
+ return(0);
+}
+
+
+/*
+ * HTML <DFN> (Text Definition) element handler
+ */
+int
+html_dfn(HANDLER_S *hd, int ch, int cmd)
+{
+ if(PASS_HTML(hd->html_data)){
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ html_output_raw_tag(hd->html_data, "dfn");
+ }
+ else if(cmd == GF_EOD){
+ html_output_string(hd->html_data, "</dfn>");
+ }
+
+ return(1);
+ }
+
+ return(0);
+}
+
+
+/*
+ * HTML <TT> (Text Tt) element handler
+ */
+int
+html_tt(HANDLER_S *hd, int ch, int cmd)
+{
+ if(PASS_HTML(hd->html_data)){
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ html_output_raw_tag(hd->html_data, "tt");
+ }
+ else if(cmd == GF_EOD){
+ html_output_string(hd->html_data, "</tt>");
+ }
+
+ return(1);
+ }
+
+ return(0);
+}
+
+
+/*
+ * HTML <VAR> (Text Var) element handler
+ */
+int
+html_var(HANDLER_S *hd, int ch, int cmd)
+{
+ if(PASS_HTML(hd->html_data)){
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ html_output_raw_tag(hd->html_data, "var");
+ }
+ else if(cmd == GF_EOD){
+ html_output_string(hd->html_data, "</var>");
+ }
+
+ return(1);
+ }
+
+ return(0);
+}
+
+
+/*
+ * HTML <SAMP> (Text Samp) element handler
+ */
+int
+html_samp(HANDLER_S *hd, int ch, int cmd)
+{
+ if(PASS_HTML(hd->html_data)){
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ html_output_raw_tag(hd->html_data, "samp");
+ }
+ else if(cmd == GF_EOD){
+ html_output_string(hd->html_data, "</samp>");
+ }
+
+ return(1);
+ }
+
+ return(0);
+}
+
+
+/*
+ * HTML <SUP> (Text Superscript) element handler
+ */
+int
+html_sup(HANDLER_S *hd, int ch, int cmd)
+{
+ if(PASS_HTML(hd->html_data)){
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ html_output_raw_tag(hd->html_data, "sup");
+ }
+ else if(cmd == GF_EOD){
+ html_output_string(hd->html_data, "</sup>");
+ }
+
+ return(1);
+ }
+
+ return(0);
+}
+
+
+/*
+ * HTML <SUB> (Text Subscript) element handler
+ */
+int
+html_sub(HANDLER_S *hd, int ch, int cmd)
+{
+ if(PASS_HTML(hd->html_data)){
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ html_output_raw_tag(hd->html_data, "sub");
+ }
+ else if(cmd == GF_EOD){
+ html_output_string(hd->html_data, "</sub>");
+ }
+
+ return(1);
+ }
+
+ return(0);
+}
+
+
+/*
+ * HTML <CITE> (Text Citation) element handler
+ */
+int
+html_cite(HANDLER_S *hd, int ch, int cmd)
+{
+ if(PASS_HTML(hd->html_data)){
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ html_output_raw_tag(hd->html_data, "cite");
+ }
+ else if(cmd == GF_EOD){
+ html_output_string(hd->html_data, "</cite>");
+ }
+
+ return(1);
+ }
+
+ return(0);
+}
+
+
+/*
+ * HTML <CODE> (Text Code) element handler
+ */
+int
+html_code(HANDLER_S *hd, int ch, int cmd)
+{
+ if(PASS_HTML(hd->html_data)){
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ html_output_raw_tag(hd->html_data, "code");
+ }
+ else if(cmd == GF_EOD){
+ html_output_string(hd->html_data, "</code>");
+ }
+
+ return(1);
+ }
+
+ return(0);
+}
+
+
+/*
+ * HTML <INS> (Text Inserted) element handler
+ */
+int
+html_ins(HANDLER_S *hd, int ch, int cmd)
+{
+ if(PASS_HTML(hd->html_data)){
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ html_output_raw_tag(hd->html_data, "ins");
+ }
+ else if(cmd == GF_EOD){
+ html_output_string(hd->html_data, "</ins>");
+ }
+
+ return(1);
+ }
+
+ return(0);
+}
+
+
+/*
+ * HTML <DEL> (Text Deleted) element handler
+ */
+int
+html_del(HANDLER_S *hd, int ch, int cmd)
+{
+ if(PASS_HTML(hd->html_data)){
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ html_output_raw_tag(hd->html_data, "del");
+ }
+ else if(cmd == GF_EOD){
+ html_output_string(hd->html_data, "</del>");
+ }
+
+ return(1);
+ }
+
+ return(0);
+}
+
+
+/*
+ * HTML <ABBR> (Text Abbreviation) element handler
+ */
+int
+html_abbr(HANDLER_S *hd, int ch, int cmd)
+{
+ if(PASS_HTML(hd->html_data)){
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ html_output_raw_tag(hd->html_data, "abbr");
+ }
+ else if(cmd == GF_EOD){
+ html_output_string(hd->html_data, "</abbr>");
+ }
+
+ return(1);
+ }
+
+ return(0);
+}
+
+
+/*
+ * HTML <SCRIPT> element handler
+ */
+int
+html_script(HANDLER_S *hd, int ch, int cmd)
+{
+ /* Link in and drop everything within on the floor */
+ return(1);
+}
+
+
+/*
+ * HTML <APPLET> element handler
+ */
+int
+html_applet(HANDLER_S *hd, int ch, int cmd)
+{
+ /* Link in and drop everything within on the floor */
+ return(1);
+}
+
+
+/*
+ * HTML <STYLE> CSS element handler
+ */
+int
+html_style(HANDLER_S *hd, int ch, int cmd)
+{
+ static STORE_S *css_stuff ;
+
+ if(PASS_HTML(hd->html_data)){
+ if(cmd == GF_DATA){
+ /* collect style settings */
+ so_writec(ch, css_stuff);
+ }
+ else if(cmd == GF_RESET){
+ if(css_stuff)
+ so_give(&css_stuff);
+
+ css_stuff = so_get(CharStar, NULL, EDIT_ACCESS);
+ }
+ else if(cmd == GF_EOD){
+ /*
+ * TODO: strip anything mischievous and pass on
+ */
+
+ so_give(&css_stuff);
+ }
+ }
+
+ return(1);
+}
+
+/*
+ * RSS 2.0 <RSS> version
+ */
+int
+rss_rss(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_RESET){
+ PARAMETER *p;
+
+ for(p = HD(hd->html_data)->el_data->attribs;
+ p && p->attribute;
+ p = p->next)
+ if(!strucmp(p->attribute, "VERSION")){
+ if(p->value && !strucmp(p->value,"2.0"))
+ return(0); /* do not link in */
+ }
+
+ gf_error("Incompatible RSS version");
+ /* NO RETURN */
+ }
+
+ return(0); /* not linked or error means we never get here */
+}
+
+/*
+ * RSS 2.0 <CHANNEL>
+ */
+int
+rss_channel(HANDLER_S *hd, int ch, int cmd)
+{
+ if(cmd == GF_DATA){
+ html_handoff(hd, ch);
+ }
+ else if(cmd == GF_RESET){
+ RSS_FEED_S *feed;
+
+ feed = RSS_FEED(hd->html_data) = fs_get(sizeof(RSS_FEED_S));
+ memset(feed, 0, sizeof(RSS_FEED_S));
+ }
+
+ return(1); /* link in */
+}
+
+/*
+ * RSS 2.0 <TITLE>
+ */
+int
+rss_title(HANDLER_S *hd, int ch, int cmd)
+{
+ static STORE_S *title_so;
+
+ if(cmd == GF_DATA){
+ /* collect data */
+ if(title_so){
+ so_writec(ch, title_so);
+ }
+ }
+ else if(cmd == GF_RESET){
+ if(RSS_FEED(hd->html_data)){
+ /* prepare for data */
+ if(title_so)
+ so_give(&title_so);
+
+ title_so = so_get(CharStar, NULL, EDIT_ACCESS);
+ }
+ }
+ else if(cmd == GF_EOD){
+ if(title_so){
+ RSS_FEED_S *feed = RSS_FEED(hd->html_data);
+ RSS_ITEM_S *rip;
+
+ if(feed){
+ if(rip = feed->items){
+ for(; rip->next; rip = rip->next)
+ ;
+
+ if(rip->title)
+ fs_give((void **) &rip->title);
+
+ rip->title = cpystr(rss_skip_whitespace(so_text(title_so)));
+ }
+ else{
+ if(feed->title)
+ fs_give((void **) &feed->title);
+
+ feed->title = cpystr(rss_skip_whitespace(so_text(title_so)));
+ }
+ }
+
+ so_give(&title_so);
+ }
+ }
+
+ return(1); /* link in */
+}
+
+/*
+ * RSS 2.0 <IMAGE>
+ */
+int
+rss_image(HANDLER_S *hd, int ch, int cmd)
+{
+ static STORE_S *img_so;
+
+ if(cmd == GF_DATA){
+ /* collect data */
+ if(img_so){
+ so_writec(ch, img_so);
+ }
+ }
+ else if(cmd == GF_RESET){
+ if(RSS_FEED(hd->html_data)){
+ /* prepare to collect data */
+ if(img_so)
+ so_give(&img_so);
+
+ img_so = so_get(CharStar, NULL, EDIT_ACCESS);
+ }
+ }
+ else if(cmd == GF_EOD){
+ if(img_so){
+ RSS_FEED_S *feed = RSS_FEED(hd->html_data);
+
+ if(feed){
+ if(feed->image)
+ fs_give((void **) &feed->image);
+
+ feed->image = cpystr(rss_skip_whitespace(so_text(img_so)));
+ }
+
+ so_give(&img_so);
+ }
+ }
+
+ return(1); /* link in */
+}
+
+/*
+ * RSS 2.0 <LINK>
+ */
+int
+rss_link(HANDLER_S *hd, int ch, int cmd)
+{
+ static STORE_S *link_so;
+
+ if(cmd == GF_DATA){
+ /* collect data */
+ if(link_so){
+ so_writec(ch, link_so);
+ }
+ }
+ else if(cmd == GF_RESET){
+ if(RSS_FEED(hd->html_data)){
+ /* prepare to collect data */
+ if(link_so)
+ so_give(&link_so);
+
+ link_so = so_get(CharStar, NULL, EDIT_ACCESS);
+ }
+ }
+ else if(cmd == GF_EOD){
+ if(link_so){
+ RSS_FEED_S *feed = RSS_FEED(hd->html_data);
+ RSS_ITEM_S *rip;
+
+ if(feed){
+ if(rip = feed->items){
+ for(; rip->next; rip = rip->next)
+ ;
+
+ if(rip->link)
+ fs_give((void **) &rip->link);
+
+ rip->link = cpystr(rss_skip_whitespace(so_text(link_so)));
+ }
+ else{
+ if(feed->link)
+ fs_give((void **) &feed->link);
+
+ feed->link = cpystr(rss_skip_whitespace(so_text(link_so)));
+ }
+ }
+
+ so_give(&link_so);
+ }
+ }
+
+ return(1); /* link in */
+}
+
+/*
+ * RSS 2.0 <DESCRIPTION>
+ */
+int
+rss_description(HANDLER_S *hd, int ch, int cmd)
+{
+ static STORE_S *desc_so;
+
+ if(cmd == GF_DATA){
+ /* collect data */
+ if(desc_so){
+ so_writec(ch, desc_so);
+ }
+ }
+ else if(cmd == GF_RESET){
+ if(RSS_FEED(hd->html_data)){
+ /* prepare to collect data */
+ if(desc_so)
+ so_give(&desc_so);
+
+ desc_so = so_get(CharStar, NULL, EDIT_ACCESS);
+ }
+ }
+ else if(cmd == GF_EOD){
+ if(desc_so){
+ RSS_FEED_S *feed = RSS_FEED(hd->html_data);
+ RSS_ITEM_S *rip;
+
+ if(feed){
+ if(rip = feed->items){
+ for(; rip->next; rip = rip->next)
+ ;
+
+ if(rip->description)
+ fs_give((void **) &rip->description);
+
+ rip->description = cpystr(rss_skip_whitespace(so_text(desc_so)));
+ }
+ else{
+ if(feed->description)
+ fs_give((void **) &feed->description);
+
+ feed->description = cpystr(rss_skip_whitespace(so_text(desc_so)));
+ }
+ }
+
+ so_give(&desc_so);
+ }
+ }
+
+ return(1); /* link in */
+}
+
+/*
+ * RSS 2.0 <TTL> (in minutes)
+ */
+int
+rss_ttl(HANDLER_S *hd, int ch, int cmd)
+{
+ RSS_FEED_S *feed = RSS_FEED(hd->html_data);
+
+ if(cmd == GF_DATA){
+ if(isdigit((unsigned char) ch))
+ feed->ttl = ((feed->ttl * 10) + (ch - '0'));
+ }
+ else if(cmd == GF_RESET){
+ /* prepare to collect data */
+ feed->ttl = 0;
+ }
+ else if(cmd == GF_EOD){
+ }
+
+ return(1); /* link in */
+}
+
+/*
+ * RSS 2.0 <ITEM>
+ */
+int
+rss_item(HANDLER_S *hd, int ch, int cmd)
+{
+ /* BUG: verify no ITEM nesting? */
+ if(cmd == GF_RESET){
+ RSS_FEED_S *feed;
+
+ if((feed = RSS_FEED(hd->html_data)) != NULL){
+ RSS_ITEM_S **rip;
+ int n = 0;
+
+ for(rip = &feed->items; *rip; rip = &(*rip)->next)
+ if(++n > RSS_ITEM_LIMIT)
+ return(0);
+
+ *rip = fs_get(sizeof(RSS_ITEM_S));
+ memset(*rip, 0, sizeof(RSS_ITEM_S));
+ }
+ }
+
+ return(0); /* don't link in */
+}
+
+
+char *
+rss_skip_whitespace(char *s)
+{
+ for(; *s && isspace((unsigned char) *s); s++)
+ ;
+
+ return(s);
+}
+
+
+/*
+ * return the function associated with the given element name
+ */
+ELPROP_S *
+element_properties(FILTER_S *fd, char *el_name)
+{
+ register ELPROP_S *el_table = ELEMENTS(fd);
+
+ for(; el_table->element; el_table++)
+ if(!strucmp(el_name, el_table->element))
+ return(el_table);
+
+ return(NULL);
+}
+
+
+/*
+ * collect element's name and any attribute/value pairs then
+ * dispatch to the appropriate handler.
+ *
+ * Returns 1 : got what we wanted
+ * 0 : we need more data
+ * -1 : bogus input
+ */
+int
+html_element_collector(FILTER_S *fd, int ch)
+{
+ if(ch == '>'){
+ if(ED(fd)->overrun){
+ /*
+ * If problem processing, don't bother doing anything
+ * internally, just return such that none of what we've
+ * digested is displayed.
+ */
+ HTML_DEBUG_EL("too long", ED(fd));
+ return(1); /* Let it go, Jim */
+ }
+ else if(ED(fd)->mkup_decl){
+ if(ED(fd)->badform){
+ dprint((2, "-- html error: bad form: %.*s\n",
+ ED(fd)->len, ED(fd)->buf ? ED(fd)->buf : "?"));
+ /*
+ * Invalid comment -- make some guesses as
+ * to whether we should stop with this greater-than...
+ */
+ if(ED(fd)->buf[0] != '-'
+ || ED(fd)->len < 4
+ || (ED(fd)->buf[1] == '-'
+ && ED(fd)->buf[ED(fd)->len - 1] == '-'
+ && ED(fd)->buf[ED(fd)->len - 2] == '-'))
+ return(1);
+ }
+ else{
+ dprint((5, "-- html: OK: %.*s\n",
+ ED(fd)->len, ED(fd)->buf ? ED(fd)->buf : "?"));
+ if(ED(fd)->start_comment == ED(fd)->end_comment){
+ if(ED(fd)->len > 10){
+ ED(fd)->buf[ED(fd)->len - 2] = '\0';
+ html_element_comment(fd, ED(fd)->buf + 2);
+ }
+
+ return(1);
+ }
+ /* else keep collecting comment below */
+ }
+ }
+ else if(ED(fd)->proc_inst){
+ return(1); /* return without display... */
+ }
+ else if(!strucmp(ED(fd)->element,"STYLE") && ED(fd)->badform){
+ dprint((2, "-- html error: empty tag with STYLE parameter!"));
+ return(1);
+ }
+ else if(!ED(fd)->quoted || ED(fd)->badform){
+ ELPROP_S *ep;
+
+ /*
+ * We either have the whole thing or all that we could
+ * salvage from it. Try our best...
+ */
+
+ if(HD(fd)->bitbucket)
+ return(1); /* element inside chtml clause! */
+
+ if(!ED(fd)->badform && html_element_flush(ED(fd)))
+ return(1); /* return without display... */
+
+ /*
+ * If we ran into an empty tag or we don't know how to deal
+ * with it, just go on, ignoring it...
+ */
+ if(ED(fd)->element && (ep = element_properties(fd, ED(fd)->element))){
+ if(ep->handler){
+ /* dispatch the element's handler */
+ HTML_DEBUG_EL(ED(fd)->end_tag ? "POP" : "PUSH", ED(fd));
+ if(ED(fd)->end_tag){
+ html_pop(fd, ep); /* remove it's handler */
+ }
+ else{
+ /* if a block element, pop any open <p>'s */
+ if(ep->blocklevel){
+ HANDLER_S *tp;
+
+ for(tp = HANDLERS(fd); tp && EL(tp)->handler == html_p; tp = tp->below){
+ HTML_DEBUG_EL("Unclosed <P>", ED(fd));
+ html_pop(fd, EL(tp));
+ break;
+ }
+ }
+
+ /* enforce table nesting */
+ if(!strucmp(ep->element, "tr")){
+ if(!HANDLERS(fd) || (strucmp(EL(HANDLERS(fd))->element, "table") && strucmp(EL(HANDLERS(fd))->element, "tbody") && strucmp(EL(HANDLERS(fd))->element, "thead"))){
+ dprint((2, "-- html error: bad nesting for <TR>, GOT %s\n", (HANDLERS(fd)) ? EL(HANDLERS(fd))->element : "NO-HANDLERS"));
+ if(HANDLERS(fd) && !strucmp(EL(HANDLERS(fd))->element,"tr")){
+ dprint((2, "-- html error: bad nesting popping previous <TR>"));
+ html_pop(fd, EL(HANDLERS(fd)));
+ }
+ else{
+ dprint((2, "-- html error: bad nesting pusing <TABLE>"));
+ html_push(fd, element_properties(fd, "table"));
+ }
+ }
+ }
+ else if(!strucmp(ep->element, "td") || !strucmp(ep->element, "th")){
+ if(!HANDLERS(fd)){
+ dprint((2, "-- html error: bad nesting: NO HANDLERS before <TD>"));
+ html_push(fd, element_properties(fd, "table"));
+ html_push(fd, element_properties(fd, "tr"));
+ }
+ else if(strucmp(EL(HANDLERS(fd))->element, "tr")){
+ dprint((2, "-- html error: bad nesting for <TD>, GOT %s\n", EL(HANDLERS(fd))->element));
+ html_push(fd, element_properties(fd, "tr"));
+ }
+ else if(!strucmp(EL(HANDLERS(fd))->element, "td")){
+ dprint((2, "-- html error: bad nesting popping <TD>"));
+ html_pop(fd, EL(HANDLERS(fd)));
+ }
+ }
+
+ /* add it's handler */
+ if(html_push(fd, ep)){
+ if(ED(fd)->empty){
+ /* remove empty element */
+ html_pop(fd, ep);
+ }
+ }
+ }
+ }
+ else {
+ HTML_DEBUG_EL("IGNORED", ED(fd));
+ }
+ }
+ else{ /* else, empty or unrecognized */
+ HTML_DEBUG_EL("?", ED(fd));
+ }
+
+ return(1); /* all done! see, that didn't hurt */
+ }
+ }
+ else if(ch == '/' && ED(fd)->element && ED(fd)->len){
+ ED(fd)->empty = 1;
+ }
+ else
+ ED(fd)->empty = 0;
+
+ if(ED(fd)->mkup_decl){
+ if((ch &= 0xff) == '-'){
+ if(ED(fd)->hyphen){
+ ED(fd)->hyphen = 0;
+ if(ED(fd)->start_comment)
+ ED(fd)->end_comment = 1;
+ else
+ ED(fd)->start_comment = 1;
+ }
+ else
+ ED(fd)->hyphen = 1;
+ }
+ else{
+ if(ED(fd)->end_comment)
+ ED(fd)->start_comment = ED(fd)->end_comment = 0;
+
+ /*
+ * no "--" after ! or non-whitespace between comments - bad
+ */
+ if(ED(fd)->len < 2 || (!ED(fd)->start_comment
+ && !ASCII_ISSPACE((unsigned char) ch)))
+ ED(fd)->badform = 1; /* non-comment! */
+
+ ED(fd)->hyphen = 0;
+ }
+
+ /*
+ * Remember the comment for possible later processing, if
+ * it get's too long, remember first and last few chars
+ * so we know when to terminate (and throw some garbage
+ * in between when we toss out what's between.
+ */
+ if(ED(fd)->len == HTML_BUF_LEN){
+ ED(fd)->buf[2] = ED(fd)->buf[3] = 'X';
+ ED(fd)->buf[4] = ED(fd)->buf[ED(fd)->len - 2];
+ ED(fd)->buf[5] = ED(fd)->buf[ED(fd)->len - 1];
+ ED(fd)->len = 6;
+ }
+
+ ED(fd)->buf[(ED(fd)->len)++] = ch;
+ return(0); /* comments go in the bit bucket */
+ }
+ else if(ED(fd)->overrun || ED(fd)->badform){
+ return(0); /* swallow char's until next '>' */
+ }
+ else if(!ED(fd)->element && !ED(fd)->len){
+ if(ch == '/'){ /* validate leading chars */
+ ED(fd)->end_tag = 1;
+ return(0);
+ }
+ else if(ch == '!'){
+ ED(fd)->mkup_decl = 1;
+ return(0);
+ }
+ else if(ch == '?'){
+ ED(fd)->proc_inst = 1;
+ return(0);
+ }
+ else if(!isalpha((unsigned char) ch))
+ return(-1); /* can't be a tag! */
+ }
+ else if(ch == '\"' || ch == '\''){
+ if(!ED(fd)->hit_equal){
+ ED(fd)->badform = 1; /* quote in element name?!? */
+ return(0);
+ }
+
+ if(ED(fd)->quoted){
+ if(ED(fd)->quoted == (char) ch){
+ /* end of a quoted value */
+ ED(fd)->quoted = 0;
+ if(ED(fd)->len && html_element_flush(ED(fd)))
+ ED(fd)->badform = 1;
+
+ return(0); /* continue collecting chars */
+ }
+ /* ELSE fall thru writing other quoting char */
+ }
+ else{
+ ED(fd)->quoted = (char) ch;
+ ED(fd)->was_quoted = 1;
+ return(0); /* need more data */
+ }
+ }
+
+ ch &= 0xff; /* strip any "literal" high bits */
+ if(ED(fd)->quoted
+ || isalnum(ch)
+ || strchr("#-.!", ch)){
+ if(ED(fd)->len < ((ED(fd)->element || !ED(fd)->hit_equal)
+ ? HTML_BUF_LEN:MAX_ELEMENT)){
+ ED(fd)->buf[(ED(fd)->len)++] = ch;
+ }
+ else
+ ED(fd)->overrun = 1; /* flag it broken */
+ }
+ else if(ASCII_ISSPACE((unsigned char) ch) || ch == '='){
+ if((ED(fd)->len || ED(fd)->was_quoted) && html_element_flush(ED(fd))){
+ ED(fd)->badform = 1;
+ return(0); /* else, we ain't done yet */
+ }
+
+ if(!ED(fd)->hit_equal)
+ ED(fd)->hit_equal = (ch == '=');
+ }
+ else
+ ED(fd)->badform = 1; /* unrecognized data?? */
+
+ return(0); /* keep collecting */
+}
+
+
+/*
+ * Element collector found complete string, integrate it and reset
+ * internal collection buffer.
+ *
+ * Returns zero if element collection buffer flushed, error flag otherwise
+ */
+int
+html_element_flush(CLCTR_S *el_data)
+{
+ int rv = 0;
+
+ if(el_data->hit_equal){ /* adding a value */
+ el_data->hit_equal = 0;
+ if(el_data->cur_attrib){
+ if(!el_data->cur_attrib->value){
+ el_data->cur_attrib->value = cpystr(el_data->len
+ ? el_data->buf : "");
+ }
+ else{
+ dprint((2, "** element: unexpected value: %.10s...\n",
+ (el_data->len && el_data->buf) ? el_data->buf : "\"\""));
+ rv = 1;
+ }
+ }
+ else{
+ dprint((2, "** element: missing attribute name: %.10s...\n",
+ (el_data->len && el_data->buf) ? el_data->buf : "\"\""));
+ rv = 2;
+ }
+ }
+ else if(el_data->len){
+ if(!el_data->element){
+ el_data->element = cpystr(el_data->buf);
+ }
+ else{
+ PARAMETER *p = (PARAMETER *)fs_get(sizeof(PARAMETER));
+ memset(p, 0, sizeof(PARAMETER));
+ if(el_data->attribs){
+ el_data->cur_attrib->next = p;
+ el_data->cur_attrib = p;
+ }
+ else
+ el_data->attribs = el_data->cur_attrib = p;
+
+ p->attribute = cpystr(el_data->buf);
+ }
+
+ }
+
+ el_data->was_quoted = 0; /* reset collector buf and state */
+ el_data->len = 0;
+ memset(el_data->buf, 0, HTML_BUF_LEN);
+ return(rv); /* report whatever happened above */
+}
+
+
+/*
+ * html_element_comment - "Special" comment handling here
+ */
+void
+html_element_comment(FILTER_S *f, char *s)
+{
+ char *p;
+
+ while(*s && ASCII_ISSPACE((unsigned char) *s))
+ s++;
+
+ /*
+ * WARNING: "!--chtml" denotes "Conditional HTML", a UW-ism.
+ */
+ if(!struncmp(s, "chtml ", 6)){
+ s += 6;
+ if(!struncmp(s, "if ", 3)){
+ HD(f)->bitbucket = 1; /* default is failure! */
+ switch(*(s += 3)){
+ case 'P' :
+ case 'p' :
+ if(!struncmp(s + 1, "inemode=", 8)){
+ if(!strucmp(s = removing_quotes(s + 9), "function_key")
+ && F_ON(F_USE_FK, ps_global))
+ HD(f)->bitbucket = 0;
+ else if(!strucmp(s, "running"))
+ HD(f)->bitbucket = 0;
+ else if(!strucmp(s, "phone_home") && ps_global->phone_home)
+ HD(f)->bitbucket = 0;
+#ifdef _WINDOWS
+ else if(!strucmp(s, "os_windows"))
+ HD(f)->bitbucket = 0;
+#endif
+ }
+
+ break;
+
+ case '[' : /* test */
+ if((p = strindex(++s, ']')) != NULL){
+ *p = '\0'; /* tie off test string */
+ removing_leading_white_space(s);
+ removing_trailing_white_space(s);
+ if(*s == '-' && *(s+1) == 'r'){ /* readable file? */
+ for(s += 2; *s && ASCII_ISSPACE((unsigned char) *s); s++)
+ ;
+
+
+ HD(f)->bitbucket = (can_access(CHTML_VAR_EXPAND(removing_quotes(s)),
+ READ_ACCESS) != 0);
+ }
+ }
+
+ break;
+
+ default :
+ break;
+ }
+ }
+ else if(!strucmp(s, "else")){
+ HD(f)->bitbucket = !HD(f)->bitbucket;
+ }
+ else if(!strucmp(s, "endif")){
+ /* Clean up after chtml here */
+ HD(f)->bitbucket = 0;
+ }
+ }
+ else if(!HD(f)->bitbucket){
+ if(!struncmp(s, "#include ", 9)){
+ char buf[MAILTMPLEN], *bufp;
+ int len, end_of_line;
+ FILE *fp;
+
+ /* Include the named file */
+ if(!struncmp(s += 9, "file=", 5)
+ && (fp = our_fopen(CHTML_VAR_EXPAND(removing_quotes(s+5)), "r"))){
+ html_element_output(f, HTML_NEWLINE);
+
+ while(fgets(buf, sizeof(buf), fp)){
+ if((len = strlen(buf)) && buf[len-1] == '\n'){
+ end_of_line = 1;
+ buf[--len] = '\0';
+ }
+ else
+ end_of_line = 0;
+
+ for(bufp = buf; len; bufp++, len--)
+ html_element_output(f, (int) *bufp);
+
+ if(end_of_line)
+ html_element_output(f, HTML_NEWLINE);
+ }
+
+ fclose(fp);
+ html_element_output(f, HTML_NEWLINE);
+ HD(f)->blanks = 0;
+ if(f->f1 == WSPACE)
+ f->f1 = DFL;
+ }
+ }
+ else if(!struncmp(s, "#echo ", 6)){
+ if(!struncmp(s += 6, "var=", 4)){
+ char *p, buf[MAILTMPLEN];
+ ADDRESS *adr;
+ extern char datestamp[];
+
+ if(!strcmp(s = removing_quotes(s + 4), "ALPINE_VERSION")){
+ p = ALPINE_VERSION;
+ }
+ else if(!strcmp(s, "ALPINE_REVISION")){
+ p = get_alpine_revision_string(buf, sizeof(buf));
+ }
+ else if(!strcmp(s, "C_CLIENT_VERSION")){
+ p = CCLIENTVERSION;
+ }
+ else if(!strcmp(s, "ALPINE_COMPILE_DATE")){
+ p = datestamp;
+ }
+ else if(!strcmp(s, "ALPINE_TODAYS_DATE")){
+ rfc822_date(p = buf);
+ }
+ else if(!strcmp(s, "_LOCAL_FULLNAME_")){
+ p = (ps_global->VAR_LOCAL_FULLNAME
+ && ps_global->VAR_LOCAL_FULLNAME[0])
+ ? ps_global->VAR_LOCAL_FULLNAME
+ : "Local Support";
+ }
+ else if(!strcmp(s, "_LOCAL_ADDRESS_")){
+ p = (ps_global->VAR_LOCAL_ADDRESS
+ && ps_global->VAR_LOCAL_ADDRESS[0])
+ ? ps_global->VAR_LOCAL_ADDRESS
+ : "postmaster";
+ adr = rfc822_parse_mailbox(&p, ps_global->maildomain);
+ snprintf(p = buf, sizeof(buf), "%s@%s", adr->mailbox, adr->host);
+ mail_free_address(&adr);
+ }
+ else if(!strcmp(s, "_BUGS_FULLNAME_")){
+ p = (ps_global->VAR_BUGS_FULLNAME
+ && ps_global->VAR_BUGS_FULLNAME[0])
+ ? ps_global->VAR_BUGS_FULLNAME
+ : "Place to report Alpine Bugs";
+ }
+ else if(!strcmp(s, "_BUGS_ADDRESS_")){
+ p = (ps_global->VAR_BUGS_ADDRESS
+ && ps_global->VAR_BUGS_ADDRESS[0])
+ ? ps_global->VAR_BUGS_ADDRESS : "postmaster";
+ adr = rfc822_parse_mailbox(&p, ps_global->maildomain);
+ snprintf(p = buf, sizeof(buf), "%s@%s", adr->mailbox, adr->host);
+ mail_free_address(&adr);
+ }
+ else if(!strcmp(s, "CURRENT_DIR")){
+ getcwd(p = buf, sizeof(buf));
+ }
+ else if(!strcmp(s, "HOME_DIR")){
+ p = ps_global->home_dir;
+ }
+ else if(!strcmp(s, "PINE_CONF_PATH")){
+#if defined(_WINDOWS) || !defined(SYSTEM_PINERC)
+ p = "/usr/local/lib/pine.conf";
+#else
+ p = SYSTEM_PINERC;
+#endif
+ }
+ else if(!strcmp(s, "PINE_CONF_FIXED_PATH")){
+#ifdef SYSTEM_PINERC_FIXED
+ p = SYSTEM_PINERC_FIXED;
+#else
+ p = "/usr/local/lib/pine.conf.fixed";
+#endif
+ }
+ else if(!strcmp(s, "PINE_INFO_PATH")){
+ p = SYSTEM_PINE_INFO_PATH;
+ }
+ else if(!strcmp(s, "MAIL_SPOOL_PATH")){
+ p = sysinbox();
+ }
+ else if(!strcmp(s, "MAIL_SPOOL_LOCK_PATH")){
+ /* Don't put the leading /tmp/. */
+ int i, j;
+
+ p = sysinbox();
+ if(p){
+ for(j = 0, i = 0; p[i] && j < MAILTMPLEN - 1; i++){
+ if(p[i] == '/')
+ buf[j++] = '\\';
+ else
+ buf[j++] = p[i];
+ }
+ buf[j++] = '\0';
+ p = buf;
+ }
+ }
+ else if(!struncmp(s, "VAR_", 4)){
+ p = s+4;
+ if(pith_opt_pretty_var_name)
+ p = (*pith_opt_pretty_var_name)(p);
+ }
+ else if(!struncmp(s, "FEAT_", 5)){
+ p = s+5;
+ if(pith_opt_pretty_feature_name)
+ p = (*pith_opt_pretty_feature_name)(p, -1);
+ }
+ else
+ p = NULL;
+
+ if(p){
+ if(f->f1 == WSPACE){
+ html_element_output(f, ' ');
+ f->f1 = DFL; /* clear it */
+ }
+
+ while(*p)
+ html_element_output(f, (int) *p++);
+ }
+ }
+ }
+ }
+}
+
+
+void
+html_element_output(FILTER_S *f, int ch)
+{
+ if(HANDLERS(f))
+ (*EL(HANDLERS(f))->handler)(HANDLERS(f), ch, GF_DATA);
+ else
+ html_output(f, ch);
+}
+
+
+/*
+ * collect html entity and return its UCS value when done.
+ *
+ * Returns HTML_MOREDATA : we need more data
+ * HTML_ENTITY : entity collected
+ * HTML_BADVALUE : good data, but no named match or out of range
+ * HTML_BADDATA : invalid input
+ *
+ * NOTES:
+ * - entity format is "'&' tag ';'" and represents a literal char
+ * - named entities are CASE SENSITIVE.
+ * - numeric char references (where the tag is prefixed with a '#')
+ * are a char with that numbers value
+ * - numeric vals are 0-255 except for the ranges: 0-8, 11-31, 127-159.
+ */
+int
+html_entity_collector(FILTER_S *f, int ch, UCS *ucs, char **alt)
+{
+ static int len = 0;
+ static char buf[MAX_ENTITY+2];
+ int rv, i;
+
+ if(len == MAX_ENTITY){
+ rv = HTML_BADDATA;
+ }
+ else if((len == 0)
+ ? (isalpha((unsigned char) ch) || ch == '#')
+ : ((isdigit((unsigned char) ch)
+ || (isalpha((unsigned char) ch) && buf[0] != '#')))){
+ buf[len++] = ch;
+ return(HTML_MOREDATA);
+ }
+ else if(ch == ';' || ASCII_ISSPACE((unsigned char) ch)){
+ buf[len] = '\0'; /* got something! */
+ if(buf[0] == '#'){
+ *ucs = (UCS) strtoul(&buf[1], NULL, 10);
+ if(alt){
+ *alt = NULL;
+ for(i = 0; i < sizeof(entity_tab)/sizeof(struct html_entities); i++)
+ if(entity_tab[i].value == *ucs){
+ *alt = entity_tab[i].plain;
+ break;
+ }
+ }
+
+ len = 0;
+ return(HTML_ENTITY);
+ }
+ else{
+ rv = HTML_BADVALUE; /* in case of no match */
+ for(i = 0; i < sizeof(entity_tab)/sizeof(struct html_entities); i++)
+ if(strcmp(entity_tab[i].name, buf) == 0){
+ *ucs = entity_tab[i].value;
+ if(alt)
+ *alt = entity_tab[i].plain;
+
+ len = 0;
+ return(HTML_ENTITY);
+ }
+ }
+ }
+ else
+ rv = HTML_BADDATA; /* bogus input! */
+
+ if(alt){
+ buf[len] = '\0';
+ *alt = buf;
+ }
+
+ len = 0;
+ return(rv);
+}
+
+
+/*----------------------------------------------------------------------
+ HTML text to plain text filter
+
+ This basically tries to do the best it can with HTML 2.0 (RFC1866)
+ with bits of RFC 1942 (plus some HTML 3.2 thrown in as well) text
+ formatting.
+
+ ----*/
+void
+gf_html2plain(FILTER_S *f, int flg)
+{
+/* BUG: qoute incoming \255 values (see "yuml" above!) */
+ if(flg == GF_DATA){
+ register int c;
+ GF_INIT(f, f->next);
+
+ if(!HTML_WROTE(f)){
+ int ii;
+
+ for(ii = HTML_INDENT(f); ii > 0; ii--)
+ html_putc(f, ' ');
+
+ HTML_WROTE(f) = 1;
+ }
+
+ while(GF_GETC(f, c)){
+ /*
+ * First we have to collect any literal entities...
+ * that is, IF we're not already collecting one
+ * AND we're not in element's text or, if we are, we're
+ * not in quoted text. Whew.
+ */
+ if(f->t){
+ char *alt = NULL;
+ UCS ucs;
+
+ switch(html_entity_collector(f, c, &ucs, &alt)){
+ case HTML_MOREDATA: /* more data required? */
+ continue; /* go get another char */
+
+ case HTML_BADVALUE :
+ case HTML_BADDATA :
+ /* if supplied, process bogus data */
+ HTML_PROC(f, '&');
+ for(; *alt; alt++){
+ unsigned int uic = *alt;
+ HTML_PROC(f, uic);
+ }
+
+ if(c == '&' && !HD(f)->quoted){
+ f->t = '&';
+ continue;
+ }
+ else
+ f->t = 0; /* don't come back next time */
+
+ break;
+
+ default : /* thing to process */
+ f->t = 0; /* don't come back */
+
+ /*
+ * do something with UCS codepoint. If it's
+ * not displayable then use the alt version
+ * otherwise
+ * cvt UCS to UTF-8 and toss into next filter.
+ */
+ if(ucs > 127 && wcellwidth(ucs) < 0){
+ if(alt){
+ for(; *alt; alt++){
+ c = MAKE_LITERAL(*alt);
+ HTML_PROC(f, c);
+ }
+
+ continue;
+ }
+ else
+ c = MAKE_LITERAL('?');
+ }
+ else{
+ unsigned char utf8buf[8], *p1, *p2;
+
+ p2 = utf8_put(p1 = (unsigned char *) utf8buf, (unsigned long) ucs);
+ for(; p1 < p2; p1++){
+ c = MAKE_LITERAL(*p1);
+ HTML_PROC(f, c);
+ }
+
+ continue;
+ }
+
+ break;
+ }
+ }
+ else if(!PASS_HTML(f) && c == '&' && !HD(f)->quoted){
+ f->t = '&';
+ continue;
+ }
+
+ /*
+ * then we process whatever we got...
+ */
+
+ HTML_PROC(f, c);
+ }
+
+ GF_OP_END(f); /* clean up our input pointers */
+ }
+ else if(flg == GF_EOD){
+ while(HANDLERS(f)){
+ dprint((2, "-- html error: no closing tag for %s",EL(HANDLERS(f))->element));
+ html_pop(f, EL(HANDLERS(f)));
+ }
+
+ html_output(f, HTML_NEWLINE);
+ if(ULINE_BIT(f))
+ HTML_ULINE(f, ULINE_BIT(f) = 0);
+
+ if(BOLD_BIT(f))
+ HTML_BOLD(f, BOLD_BIT(f) = 0);
+
+ HTML_FLUSH(f);
+ fs_give((void **)&f->line);
+ if(HD(f)->color)
+ free_color_pair(&HD(f)->color);
+
+ fs_give(&f->data);
+ if(f->opt){
+ if(((HTML_OPT_S *)f->opt)->base)
+ fs_give((void **) &((HTML_OPT_S *)f->opt)->base);
+
+ fs_give(&f->opt);
+ }
+
+ (*f->next->f)(f->next, GF_DATA);
+ (*f->next->f)(f->next, GF_EOD);
+ }
+ else if(flg == GF_RESET){
+ dprint((9, "-- gf_reset html2plain\n"));
+ f->data = (HTML_DATA_S *) fs_get(sizeof(HTML_DATA_S));
+ memset(f->data, 0, sizeof(HTML_DATA_S));
+ /* start with flowing text */
+ HD(f)->wrapstate = !PASS_HTML(f);
+ HD(f)->wrapcol = WRAP_COLS(f);
+ f->f1 = DFL; /* state */
+ f->f2 = 0; /* chars in wrap buffer */
+ f->n = 0L; /* chars on line so far */
+ f->linep = f->line = (char *)fs_get(HTML_BUF_LEN * sizeof(char));
+ HD(f)->line_bufsize = HTML_BUF_LEN; /* initial bufsize of line */
+ HD(f)->alt_entity = (!ps_global->display_charmap
+ || strucmp(ps_global->display_charmap, "iso-8859-1"));
+ HD(f)->cb.cbufp = HD(f)->cb.cbufend = HD(f)->cb.cbuf;
+ }
+}
+
+
+
+/*
+ * html_indent - do the requested indent level function with appropriate
+ * flushing and such.
+ *
+ * Returns: indent level prior to set/increment
+ */
+int
+html_indent(FILTER_S *f, int val, int func)
+{
+ int old = HD(f)->indent_level;
+
+ /* flush pending data at old indent level */
+ switch(func){
+ case HTML_ID_INC :
+ html_output_flush(f);
+ if((HD(f)->indent_level += val) < 0)
+ HD(f)->indent_level = 0;
+
+ break;
+
+ case HTML_ID_SET :
+ html_output_flush(f);
+ HD(f)->indent_level = val;
+ break;
+
+ default :
+ break;
+ }
+
+ return(old);
+}
+
+
+
+/*
+ * html_blanks - Insert n blank lines into output
+ */
+void
+html_blank(FILTER_S *f, int n)
+{
+ /* Cap off any flowing text, and then write blank lines */
+ if(f->f2 || f->n || CENTER_BIT(f) || HD(f)->centered || WRAPPED_LEN(f))
+ html_output(f, HTML_NEWLINE);
+
+ if(HD(f)->wrapstate)
+ while(HD(f)->blanks < n) /* blanks inc'd by HTML_NEWLINE */
+ html_output(f, HTML_NEWLINE);
+}
+
+
+
+/*
+ * html_newline -- insert a newline mindful of embedded tags
+ */
+void
+html_newline(FILTER_S *f)
+{
+ html_write_newline(f); /* commit an actual newline */
+
+ if(f->n){ /* and keep track of blank lines */
+ HD(f)->blanks = 0;
+ f->n = 0L;
+ }
+ else
+ HD(f)->blanks++;
+}
+
+
+/*
+ * output the given char, handling any requested wrapping.
+ * It's understood that all whitespace handed us is written. In other
+ * words, junk whitespace is weeded out before it's given to us here.
+ *
+ */
+void
+html_output(FILTER_S *f, int ch)
+{
+ UCS uc;
+ int width;
+ void (*o_f)(FILTER_S *, int, int) = CENTER_BIT(f) ? html_output_centered : html_output_normal;
+
+ /*
+ * if ch is a control token, just pass it on, else, collect
+ * utf8-encoded characters to determine width,then feed into
+ * output routines
+ */
+ if(ch == TAG_EMBED || HD(f)->embedded.state || (ch > 0xff && IS_LITERAL(ch) == 0)){
+ (*o_f)(f, ch, 1);
+ }
+ else if(utf8_to_ucs4_oneatatime(ch & 0xff, &(HD(f)->cb), &uc, &width)){
+ unsigned char *cp;
+
+ for(cp = HD(f)->cb.cbuf; cp <= HD(f)->cb.cbufend; cp++){
+ (*o_f)(f, *cp, width);
+ width = 0; /* only count it once */
+ }
+
+ HD(f)->cb.cbufp = HD(f)->cb.cbufend = HD(f)->cb.cbuf;
+ }
+ else
+ HD(f)->cb.cbufend = HD(f)->cb.cbufp;
+ /* else do nothing until we have a full character */
+}
+
+
+void
+html_output_string(FILTER_S *f, char *s)
+{
+ for(; *s; s++)
+ html_output(f, *s);
+}
+
+
+void
+html_output_raw_tag(FILTER_S *f, char *tag)
+{
+ PARAMETER *p;
+ char *vp;
+ int i;
+
+ html_output(f, '<');
+ html_output_string(f, tag);
+ for(p = HD(f)->el_data->attribs;
+ p && p->attribute;
+ p = p->next){
+ /* SECURITY: no javascript */
+ /* PRIVACY: no img src without permission */
+ /* BUGS: no class collisions since <head> ignored */
+ if(html_event_attribute(p->attribute)
+ || !strucmp(p->attribute, "class")
+ || (!PASS_IMAGES(f) && !strucmp(tag, "img") && !strucmp(p->attribute, "src")))
+ continue;
+
+ /* PRIVACY: sniff out background images */
+ if(p->value && !PASS_IMAGES(f)){
+ if(!strucmp(p->attribute, "style")){
+ if((vp = srchstr(p->value, "background-image")) != NULL){
+ /* neuter in place */
+ vp[11] = vp[12] = vp[13] = vp[14] = vp[15] = 'X';
+ }
+ else{
+ for(vp = p->value; (vp = srchstr(vp, "background")) != NULL; vp++)
+ if(vp[10] == ' ' || vp[10] == ':')
+ for(i = 11; vp[i] && vp[i] != ';'; i++)
+ if((vp[i] == 'u' && vp[i+1] == 'r' && vp[i+2] == 'l' && vp[i+3] == '(')
+ || vp[i] == ':' || vp[i] == '/' || vp[i] == '.')
+ vp[0] = 'X';
+ }
+ }
+ else if(!strucmp(p->attribute, "background")){
+ char *ip;
+
+ for(ip = p->value; *ip && !(*ip == ':' || *ip == '/' || *ip == '.'); ip++)
+ ;
+
+ if(ip)
+ continue;
+ }
+ }
+
+ html_output(f, ' ');
+ html_output_string(f, p->attribute);
+ if(p->value){
+ html_output(f, '=');
+ html_output(f, '\"');
+ html_output_string(f, p->value);
+ html_output(f, '\"');
+ }
+ }
+
+ /* append warning to form submission */
+ if(!strucmp(tag, "form")){
+ html_output_string(f, " onsubmit=\"return window.confirm('This form is submitting information to an outside server.\\nAre you sure?');\"");
+ }
+
+ if(ED(f)->end_tag){
+ html_output(f, ' ');
+ html_output(f, '/');
+ }
+
+ html_output(f, '>');
+}
+
+
+int
+html_event_attribute(char *attr)
+{
+ int i;
+ static char *events[] = {
+ "onabort", "onblur", "onchange", "onclick", "ondblclick", "ondragdrop",
+ "onerror", "onfocus", "onkeydown", "onkeypress", "onkeyup", "onload",
+ "onmousedown", "onmousemove", "onmouseout", "onmouseover", "onmouseup", "onmove",
+ "onreset", "onresize", "onselec", "onsubmit", "onunload"
+ };
+
+ if((attr[0] == 'o' || attr[0] == 'O') && (attr[1] == 'n' || attr[1] == 'N'))
+ for(i = 0; i < sizeof(events)/sizeof(events[0]); i++)
+ if(!strucmp(attr, events[i]))
+ return(TRUE);
+
+ return(FALSE);
+}
+
+
+void
+html_output_normal(FILTER_S *f, int ch, int width)
+{
+ if(HD(f)->centered){
+ html_centered_flush(f);
+ fs_give((void **) &HD(f)->centered->line.buf);
+ fs_give((void **) &HD(f)->centered->word.buf);
+ fs_give((void **) &HD(f)->centered);
+ }
+
+ if(HD(f)->wrapstate){
+ if(ch == HTML_NEWLINE){ /* hard newline */
+ html_output_flush(f);
+ html_newline(f);
+ }
+ else
+ HD(f)->blanks = 0; /* reset blank line counter */
+
+ if(ch == TAG_EMBED){ /* takes up no space */
+ HD(f)->embedded.state = -5;
+ HTML_LINEP_PUTC(f, TAG_EMBED);
+ }
+ else if(HD(f)->embedded.state){ /* ditto */
+ if(HD(f)->embedded.state == -5){
+ /* looking for specially handled tags following TAG_EMBED */
+ if(ch == TAG_HANDLE)
+ HD(f)->embedded.state = -1; /* next ch is length */
+ else if(ch == TAG_FGCOLOR || ch == TAG_BGCOLOR){
+ if(!HD(f)->color)
+ HD(f)->color = new_color_pair(NULL, NULL);
+
+ if(ch == TAG_FGCOLOR)
+ HD(f)->embedded.color = HD(f)->color->fg;
+ else
+ HD(f)->embedded.color = HD(f)->color->bg;
+
+ HD(f)->embedded.state = RGBLEN;
+ }
+ else
+ HD(f)->embedded.state = 0; /* non-special */
+ }
+ else if(HD(f)->embedded.state > 0){
+ /* collecting up an RGBLEN color or length, ignore tags */
+ (HD(f)->embedded.state)--;
+ if(HD(f)->embedded.color)
+ *HD(f)->embedded.color++ = ch;
+
+ if(HD(f)->embedded.state == 0 && HD(f)->embedded.color){
+ *HD(f)->embedded.color = '\0';
+ HD(f)->embedded.color = NULL;
+ }
+ }
+ else if(HD(f)->embedded.state < 0){
+ HD(f)->embedded.state = ch; /* number of embedded chars */
+ }
+ else{
+ (HD(f)->embedded.state)--;
+ if(HD(f)->embedded.color)
+ *HD(f)->embedded.color++ = ch;
+
+ if(HD(f)->embedded.state == 0 && HD(f)->embedded.color){
+ *HD(f)->embedded.color = '\0';
+ HD(f)->embedded.color = NULL;
+ }
+ }
+
+ HTML_LINEP_PUTC(f, ch);
+ }
+ else if(HTML_ISSPACE(ch)){
+ html_output_flush(f);
+ }
+ else{
+ if(HD(f)->prefix)
+ html_a_prefix(f);
+
+ if((f->f2 += width) + 1 >= WRAP_COLS(f)){
+ HTML_LINEP_PUTC(f, ch & 0xff);
+ HTML_FLUSH(f);
+ html_newline(f);
+ if(HD(f)->in_anchor)
+ html_write_anchor(f, HD(f)->in_anchor);
+ }
+ else
+ HTML_LINEP_PUTC(f, ch & 0xff);
+ }
+ }
+ else{
+ if(HD(f)->prefix)
+ html_a_prefix(f);
+
+ html_output_flush(f);
+
+ switch(HD(f)->embedded.state){
+ case 0 :
+ switch(ch){
+ default :
+ /*
+ * It's difficult to both preserve whitespace and wrap at the
+ * same time so we'll do a dumb wrap at the edge of the screen.
+ * Since this shouldn't come up much in real life we'll hope
+ * it is good enough.
+ */
+ if(!PASS_HTML(f) && (f->n + width) > WRAP_COLS(f))
+ html_newline(f);
+
+ f->n += width; /* inc displayed char count */
+ HD(f)->blanks = 0; /* reset blank line counter */
+ html_putc(f, ch & 0xff);
+ break;
+
+ case TAG_EMBED : /* takes up no space */
+ html_putc(f, TAG_EMBED);
+ HD(f)->embedded.state = -2;
+ break;
+
+ case HTML_NEWLINE : /* newline handling */
+ if(!f->n)
+ break;
+
+ case '\n' :
+ html_newline(f);
+
+ case '\r' :
+ break;
+ }
+
+ break;
+
+ case -2 :
+ HD(f)->embedded.state = 0;
+ switch(ch){
+ case TAG_HANDLE :
+ HD(f)->embedded.state = -1; /* next ch is length */
+ break;
+
+ case TAG_BOLDON :
+ BOLD_BIT(f) = 1;
+ break;
+
+ case TAG_BOLDOFF :
+ BOLD_BIT(f) = 0;
+ break;
+
+ case TAG_ULINEON :
+ ULINE_BIT(f) = 1;
+ break;
+
+ case TAG_ULINEOFF :
+ ULINE_BIT(f) = 0;
+ break;
+
+ case TAG_FGCOLOR :
+ if(!HD(f)->color)
+ HD(f)->color = new_color_pair(NULL, NULL);
+
+ HD(f)->embedded.color = HD(f)->color->fg;
+ HD(f)->embedded.state = 11;
+ break;
+
+ case TAG_BGCOLOR :
+ if(!HD(f)->color)
+ HD(f)->color = new_color_pair(NULL, NULL);
+
+ HD(f)->embedded.color = HD(f)->color->bg;
+ HD(f)->embedded.state = 11;
+ break;
+
+ case TAG_HANDLEOFF :
+ ch = TAG_INVOFF;
+ HD(f)->in_anchor = 0;
+ break;
+
+ default :
+ break;
+ }
+
+ html_putc(f, ch);
+ break;
+
+ case -1 :
+ HD(f)->embedded.state = ch; /* number of embedded chars */
+ html_putc(f, ch);
+ break;
+
+ default :
+ HD(f)->embedded.state--;
+ if(HD(f)->embedded.color)
+ *HD(f)->embedded.color++ = ch;
+
+ if(HD(f)->embedded.state == 0 && HD(f)->embedded.color){
+ *HD(f)->embedded.color = '\0';
+ HD(f)->embedded.color = NULL;
+ }
+
+ html_putc(f, ch);
+ break;
+ }
+ }
+}
+
+
+/*
+ * flush any buffered chars waiting for wrapping.
+ */
+void
+html_output_flush(FILTER_S *f)
+{
+ if(f->f2){
+ if(f->n && ((int) f->n) + 1 + f->f2 > HD(f)->wrapcol)
+ html_newline(f); /* wrap? */
+
+ if(f->n){ /* text already on the line? */
+ html_putc(f, ' ');
+ f->n++; /* increment count */
+ }
+ else{
+ /* write at start of new line */
+ html_write_indent(f, HD(f)->indent_level);
+
+ if(HD(f)->in_anchor)
+ html_write_anchor(f, HD(f)->in_anchor);
+ }
+
+ f->n += f->f2;
+ HTML_FLUSH(f);
+ }
+}
+
+
+
+/*
+ * html_output_centered - managed writing centered text
+ */
+void
+html_output_centered(FILTER_S *f, int ch, int width)
+{
+ if(!HD(f)->centered){ /* new text? */
+ html_output_flush(f);
+ if(f->n) /* start on blank line */
+ html_newline(f);
+
+ HD(f)->centered = (CENTER_S *) fs_get(sizeof(CENTER_S));
+ memset(HD(f)->centered, 0, sizeof(CENTER_S));
+ /* and grab a buf to start collecting centered text */
+ HD(f)->centered->line.len = WRAP_COLS(f);
+ HD(f)->centered->line.buf = (char *) fs_get(HD(f)->centered->line.len
+ * sizeof(char));
+ HD(f)->centered->line.used = HD(f)->centered->line.width = 0;
+ HD(f)->centered->word.len = 32;
+ HD(f)->centered->word.buf = (char *) fs_get(HD(f)->centered->word.len
+ * sizeof(char));
+ HD(f)->centered->word.used = HD(f)->centered->word.width = 0;
+ }
+
+ if(ch == HTML_NEWLINE){ /* hard newline */
+ html_centered_flush(f);
+ }
+ else if(ch == TAG_EMBED){ /* takes up no space */
+ HD(f)->embedded.state = -5;
+ html_centered_putc(&HD(f)->centered->word, TAG_EMBED);
+ }
+ else if(HD(f)->embedded.state){
+ if(HD(f)->embedded.state == -5){
+ /* looking for specially handled tags following TAG_EMBED */
+ if(ch == TAG_HANDLE)
+ HD(f)->embedded.state = -1; /* next ch is length */
+ else if(ch == TAG_FGCOLOR || ch == TAG_BGCOLOR){
+ if(!HD(f)->color)
+ HD(f)->color = new_color_pair(NULL, NULL);
+
+ if(ch == TAG_FGCOLOR)
+ HD(f)->embedded.color = HD(f)->color->fg;
+ else
+ HD(f)->embedded.color = HD(f)->color->bg;
+
+ HD(f)->embedded.state = RGBLEN;
+ }
+ else
+ HD(f)->embedded.state = 0; /* non-special */
+ }
+ else if(HD(f)->embedded.state > 0){
+ /* collecting up an RGBLEN color or length, ignore tags */
+ (HD(f)->embedded.state)--;
+ if(HD(f)->embedded.color)
+ *HD(f)->embedded.color++ = ch;
+
+ if(HD(f)->embedded.state == 0 && HD(f)->embedded.color){
+ *HD(f)->embedded.color = '\0';
+ HD(f)->embedded.color = NULL;
+ }
+ }
+ else if(HD(f)->embedded.state < 0){
+ HD(f)->embedded.state = ch; /* number of embedded chars */
+ }
+ else{
+ (HD(f)->embedded.state)--;
+ if(HD(f)->embedded.color)
+ *HD(f)->embedded.color++ = ch;
+
+ if(HD(f)->embedded.state == 0 && HD(f)->embedded.color){
+ *HD(f)->embedded.color = '\0';
+ HD(f)->embedded.color = NULL;
+ }
+ }
+
+ html_centered_putc(&HD(f)->centered->word, ch);
+ }
+ else if(ASCII_ISSPACE((unsigned char) ch)){
+ if(!HD(f)->centered->space++){ /* end of a word? flush! */
+ int i;
+
+ if(WRAPPED_LEN(f) > HD(f)->wrapcol){
+ html_centered_flush_line(f);
+ /* fall thru to put current "word" on blank "line" */
+ }
+ else if(HD(f)->centered->line.width){
+ /* put space char between line and appended word */
+ html_centered_putc(&HD(f)->centered->line, ' ');
+ HD(f)->centered->line.width++;
+ }
+
+ for(i = 0; i < HD(f)->centered->word.used; i++)
+ html_centered_putc(&HD(f)->centered->line,
+ HD(f)->centered->word.buf[i]);
+
+ HD(f)->centered->line.width += HD(f)->centered->word.width;
+ HD(f)->centered->word.used = 0;
+ HD(f)->centered->word.width = 0;
+ }
+ }
+ else{
+ if(HD(f)->prefix)
+ html_a_prefix(f);
+
+ /* ch is start of next word */
+ HD(f)->centered->space = 0;
+ if(HD(f)->centered->word.width >= WRAP_COLS(f))
+ html_centered_flush(f);
+
+ html_centered_putc(&HD(f)->centered->word, ch);
+ HD(f)->centered->word.width++;
+ }
+}
+
+
+/*
+ * html_centered_putc -- add given char to given WRAPLINE_S
+ */
+void
+html_centered_putc(WRAPLINE_S *wp, int ch)
+{
+ if(wp->used + 1 >= wp->len){
+ wp->len += 64;
+ fs_resize((void **) &wp->buf, wp->len * sizeof(char));
+ }
+
+ wp->buf[wp->used++] = ch;
+}
+
+
+
+/*
+ * html_centered_flush - finish writing any pending centered output
+ */
+void
+html_centered_flush(FILTER_S *f)
+{
+ int i;
+
+ /*
+ * If word present (what about line?) we need to deal with
+ * appending it...
+ */
+ if(HD(f)->centered->word.width && WRAPPED_LEN(f) > HD(f)->wrapcol)
+ html_centered_flush_line(f);
+
+ if(WRAPPED_LEN(f)){
+ /* figure out how much to indent */
+ if((i = (WRAP_COLS(f) - WRAPPED_LEN(f))/2) > 0)
+ html_write_indent(f, i);
+
+ if(HD(f)->centered->anchor)
+ html_write_anchor(f, HD(f)->centered->anchor);
+
+ html_centered_handle(&HD(f)->centered->anchor,
+ HD(f)->centered->line.buf,
+ HD(f)->centered->line.used);
+ html_write(f, HD(f)->centered->line.buf, HD(f)->centered->line.used);
+
+ if(HD(f)->centered->word.used){
+ if(HD(f)->centered->line.width)
+ html_putc(f, ' ');
+
+ html_centered_handle(&HD(f)->centered->anchor,
+ HD(f)->centered->word.buf,
+ HD(f)->centered->word.used);
+ html_write(f, HD(f)->centered->word.buf,
+ HD(f)->centered->word.used);
+ }
+
+ HD(f)->centered->line.used = HD(f)->centered->word.used = 0;
+ HD(f)->centered->line.width = HD(f)->centered->word.width = 0;
+ }
+ else{
+ if(HD(f)->centered->word.used){
+ html_write(f, HD(f)->centered->word.buf,
+ HD(f)->centered->word.used);
+ HD(f)->centered->line.used = HD(f)->centered->word.used = 0;
+ HD(f)->centered->line.width = HD(f)->centered->word.width = 0;
+ }
+ HD(f)->blanks++; /* advance the blank line counter */
+ }
+
+ html_newline(f); /* finish the line */
+}
+
+
+/*
+ * html_centered_handle - scan the line for embedded handles
+ */
+void
+html_centered_handle(int *h, char *line, int len)
+{
+ int n;
+
+ while(len-- > 0)
+ if(*line++ == TAG_EMBED && len-- > 0)
+ switch(*line++){
+ case TAG_HANDLE :
+ if((n = *line++) >= --len){
+ *h = 0;
+ len -= n;
+ while(n--)
+ *h = (*h * 10) + (*line++ - '0');
+ }
+ break;
+
+ case TAG_HANDLEOFF :
+ case TAG_INVOFF :
+ *h = 0; /* assumption 23,342: inverse off ends tags */
+ break;
+
+ default :
+ break;
+ }
+}
+
+
+
+/*
+ * html_centered_flush_line - flush the centered "line" only
+ */
+void
+html_centered_flush_line(FILTER_S *f)
+{
+ if(HD(f)->centered->line.used){
+ int i, j;
+
+ /* hide "word" from flush */
+ i = HD(f)->centered->word.used;
+ j = HD(f)->centered->word.width;
+ HD(f)->centered->word.used = 0;
+ HD(f)->centered->word.width = 0;
+ html_centered_flush(f);
+
+ HD(f)->centered->word.used = i;
+ HD(f)->centered->word.width = j;
+ }
+}
+
+
+/*
+ * html_write_indent - write indention mindful of display attributes
+ */
+void
+html_write_indent(FILTER_S *f, int indent)
+{
+ if(! STRIP(f)){
+ if(BOLD_BIT(f)){
+ html_putc(f, TAG_EMBED);
+ html_putc(f, TAG_BOLDOFF);
+ }
+
+ if(ULINE_BIT(f)){
+ html_putc(f, TAG_EMBED);
+ html_putc(f, TAG_ULINEOFF);
+ }
+ }
+
+ f->n = indent;
+ while(indent-- > 0)
+ html_putc(f, ' '); /* indent as needed */
+
+ /*
+ * Resume any previous embedded state
+ */
+ if(! STRIP(f)){
+ if(BOLD_BIT(f)){
+ html_putc(f, TAG_EMBED);
+ html_putc(f, TAG_BOLDON);
+ }
+
+ if(ULINE_BIT(f)){
+ html_putc(f, TAG_EMBED);
+ html_putc(f, TAG_ULINEON);
+ }
+ }
+}
+
+
+/*
+ *
+ */
+void
+html_write_anchor(FILTER_S *f, int anchor)
+{
+ char buf[256];
+ int i;
+
+ html_putc(f, TAG_EMBED);
+ html_putc(f, TAG_HANDLE);
+ snprintf(buf, sizeof(buf), "%d", anchor);
+ html_putc(f, (int) strlen(buf));
+
+ for(i = 0; buf[i]; i++)
+ html_putc(f, buf[i]);
+}
+
+
+/*
+ * html_write_newline - write a newline mindful of display attributes
+ */
+void
+html_write_newline(FILTER_S *f)
+{
+ int i;
+
+ if(! STRIP(f)){ /* First tie, off any embedded state */
+ if(HD(f)->in_anchor){
+ html_putc(f, TAG_EMBED);
+ html_putc(f, TAG_INVOFF);
+ }
+
+ if(BOLD_BIT(f)){
+ html_putc(f, TAG_EMBED);
+ html_putc(f, TAG_BOLDOFF);
+ }
+
+ if(ULINE_BIT(f)){
+ html_putc(f, TAG_EMBED);
+ html_putc(f, TAG_ULINEOFF);
+ }
+
+ if(HD(f)->color && (HD(f)->color->fg[0] || HD(f)->color->bg[0])){
+ char *p;
+ int i;
+
+ p = color_embed(ps_global->VAR_NORM_FORE_COLOR,
+ ps_global->VAR_NORM_BACK_COLOR);
+ for(i = 0; i < 2 * (RGBLEN + 2); i++)
+ html_putc(f, p[i]);
+ }
+ }
+
+ html_write(f, "\015\012", 2);
+ for(i = HTML_INDENT(f); i > 0; i--)
+ html_putc(f, ' ');
+
+ if(! STRIP(f)){ /* First tie, off any embedded state */
+ if(BOLD_BIT(f)){
+ html_putc(f, TAG_EMBED);
+ html_putc(f, TAG_BOLDON);
+ }
+
+ if(ULINE_BIT(f)){
+ html_putc(f, TAG_EMBED);
+ html_putc(f, TAG_ULINEON);
+ }
+
+ if(HD(f)->color && (HD(f)->color->fg[0] || HD(f)->color->bg[0])){
+ char *p, *tfg, *tbg;
+ int i;
+ COLOR_PAIR *tmp;
+
+ tfg = HD(f)->color->fg;
+ tbg = HD(f)->color->bg;
+ tmp = new_color_pair(tfg[0] ? tfg
+ : color_to_asciirgb(ps_global->VAR_NORM_FORE_COLOR),
+ tbg[0] ? tbg
+ : color_to_asciirgb(ps_global->VAR_NORM_BACK_COLOR));
+ if(pico_is_good_colorpair(tmp)){
+ p = color_embed(tfg[0] ? tfg
+ : ps_global->VAR_NORM_FORE_COLOR,
+ tbg[0] ? tbg
+ : ps_global->VAR_NORM_BACK_COLOR);
+ for(i = 0; i < 2 * (RGBLEN + 2); i++)
+ html_putc(f, p[i]);
+ }
+
+ if(tmp)
+ free_color_pair(&tmp);
+ }
+ }
+}
+
+
+/*
+ * html_write - write given n-length string to next filter
+ */
+void
+html_write(FILTER_S *f, char *s, int n)
+{
+ GF_INIT(f, f->next);
+
+ while(n-- > 0){
+ /* keep track of attribute state? Not if last char! */
+ if(!STRIP(f) && *s == TAG_EMBED && n-- > 0){
+ GF_PUTC(f->next, TAG_EMBED);
+ switch(*++s){
+ case TAG_BOLDON :
+ BOLD_BIT(f) = 1;
+ break;
+ case TAG_BOLDOFF :
+ BOLD_BIT(f) = 0;
+ break;
+ case TAG_ULINEON :
+ ULINE_BIT(f) = 1;
+ break;
+ case TAG_ULINEOFF :
+ ULINE_BIT(f) = 0;
+ break;
+ case TAG_HANDLEOFF :
+ HD(f)->in_anchor = 0;
+ GF_PUTC(f->next, TAG_INVOFF);
+ s++;
+ continue;
+ case TAG_HANDLE :
+ if(n-- > 0){
+ int i = *++s;
+
+ GF_PUTC(f->next, TAG_HANDLE);
+ if(i <= n){
+ int anum = 0;
+ HANDLE_S *h;
+
+ n -= i;
+ GF_PUTC(f->next, i);
+ while(1){
+ anum = (anum * 10) + (*++s - '0');
+ if(--i)
+ GF_PUTC(f->next, *s);
+ else
+ break;
+ }
+
+ if(DO_HANDLES(f)
+ && (h = get_handle(*HANDLESP(f), anum)) != NULL
+ && (h->type == URL || h->type == Attach)){
+ HD(f)->in_anchor = anum;
+ }
+ }
+ }
+
+ break;
+ default:
+ break;
+ }
+ }
+
+ GF_PUTC(f->next, (*s++) & 0xff);
+ }
+
+ GF_IP_END(f->next); /* clean up next's input pointers */
+}
+
+
+/*
+ * html_putc -- actual work of writing to next filter.
+ * NOTE: Small opt not using full GF_END since our input
+ * pointers don't need adjusting.
+ */
+void
+html_putc(FILTER_S *f, int ch)
+{
+ GF_INIT(f, f->next);
+ GF_PUTC(f->next, ch & 0xff);
+ GF_IP_END(f->next); /* clean up next's input pointers */
+}
+
+
+
+/*
+ * Only current option is to turn on embedded data stripping for text
+ * bound to a printer or composer.
+ */
+void *
+gf_html2plain_opt(char *base,
+ int columns,
+ int *margin,
+ HANDLE_S **handlesp,
+ htmlrisk_t risk_f,
+ int flags)
+{
+ HTML_OPT_S *op;
+ int margin_l, margin_r;
+
+ op = (HTML_OPT_S *) fs_get(sizeof(HTML_OPT_S));
+
+ op->base = cpystr(base);
+ margin_l = (margin) ? margin[0] : 0;
+ margin_r = (margin) ? margin[1] : 0;
+ op->indent = margin_l;
+ op->columns = columns - (margin_l + margin_r);
+ op->strip = ((flags & GFHP_STRIPPED) == GFHP_STRIPPED);
+ op->handlesp = handlesp;
+ op->handles_loc = ((flags & GFHP_LOCAL_HANDLES) == GFHP_LOCAL_HANDLES);
+ op->showserver = ((flags & GFHP_SHOW_SERVER) == GFHP_SHOW_SERVER);
+ op->warnrisk_f = risk_f;
+ op->no_relative_links = ((flags & GFHP_NO_RELATIVE) == GFHP_NO_RELATIVE);
+ op->related_content = ((flags & GFHP_RELATED_CONTENT) == GFHP_RELATED_CONTENT);
+ op->html = ((flags & GFHP_HTML) == GFHP_HTML);
+ op->html_imgs = ((flags & GFHP_HTML_IMAGES) == GFHP_HTML_IMAGES);
+ op->element_table = html_element_table;
+ return((void *) op);
+}
+
+
+void *
+gf_html2plain_rss_opt(RSS_FEED_S **feedp, int flags)
+{
+ HTML_OPT_S *op;
+ int margin_l, margin_r;
+
+ op = (HTML_OPT_S *) fs_get(sizeof(HTML_OPT_S));
+ memset(op, 0, sizeof(HTML_OPT_S));
+
+ op->base = cpystr("");
+ op->element_table = rss_element_table;
+ *(op->feedp = feedp) = NULL;
+ return((void *) op);
+}
+
+void
+gf_html2plain_rss_free(RSS_FEED_S **feedp)
+{
+ if(feedp && *feedp){
+ if((*feedp)->title)
+ fs_give((void **) &(*feedp)->title);
+
+ if((*feedp)->link)
+ fs_give((void **) &(*feedp)->link);
+
+ if((*feedp)->description)
+ fs_give((void **) &(*feedp)->description);
+
+ if((*feedp)->source)
+ fs_give((void **) &(*feedp)->source);
+
+ if((*feedp)->image)
+ fs_give((void **) &(*feedp)->image);
+
+ gf_html2plain_rss_free_items(&((*feedp)->items));
+ fs_give((void **) feedp);
+ }
+}
+
+void
+gf_html2plain_rss_free_items(RSS_ITEM_S **itemp)
+{
+ if(itemp && *itemp){
+ if((*itemp)->title)
+ fs_give((void **) &(*itemp)->title);
+
+ if((*itemp)->link)
+ fs_give((void **) &(*itemp)->link);
+
+ if((*itemp)->description)
+ fs_give((void **) &(*itemp)->description);
+
+ if((*itemp)->source)
+ fs_give((void **) &(*itemp)->source);
+
+ gf_html2plain_rss_free_items(&(*itemp)->next);
+ fs_give((void **) itemp);
+ }
+}
+
+
+/* END OF HTML-TO-PLAIN text filter */
+
+/*
+ * ESCAPE CODE FILTER - remove unknown and possibly dangerous escape codes
+ * from the text stream.
+ */
+
+#define MAX_ESC_LEN 5
+
+/*
+ * the simple filter, removes unknown escape codes from the stream
+ */
+void
+gf_escape_filter(FILTER_S *f, int flg)
+{
+ register char *p;
+ GF_INIT(f, f->next);
+
+ if(flg == GF_DATA){
+ register unsigned char c;
+ register int state = f->f1;
+
+ while(GF_GETC(f, c)){
+
+ if(state){
+ if(c == '\033' || f->n == MAX_ESC_LEN){
+ f->line[f->n] = '\0';
+ f->n = 0L;
+ if(!match_escapes(f->line)){
+ GF_PUTC(f->next, '^');
+ GF_PUTC(f->next, '[');
+ }
+ else
+ GF_PUTC(f->next, '\033');
+
+ p = f->line;
+ while(*p)
+ GF_PUTC(f->next, *p++);
+
+ if(c == '\033')
+ continue;
+ else
+ state = 0; /* fall thru */
+ }
+ else{
+ f->line[f->n++] = c; /* collect */
+ continue;
+ }
+ }
+
+ if(c == '\033')
+ state = 1;
+ else
+ GF_PUTC(f->next, c);
+ }
+
+ f->f1 = state;
+ GF_END(f, f->next);
+ }
+ else if(flg == GF_EOD){
+ if(f->f1){
+ if(!match_escapes(f->line)){
+ GF_PUTC(f->next, '^');
+ GF_PUTC(f->next, '[');
+ }
+ else
+ GF_PUTC(f->next, '\033');
+ }
+
+ for(p = f->line; f->n; f->n--, p++)
+ GF_PUTC(f->next, *p);
+
+ fs_give((void **)&(f->line)); /* free temp line buffer */
+ (void) GF_FLUSH(f->next);
+ (*f->next->f)(f->next, GF_EOD);
+ }
+ else if(flg == GF_RESET){
+ dprint((9, "-- gf_reset escape\n"));
+ f->f1 = 0;
+ f->n = 0L;
+ f->linep = f->line = (char *)fs_get((MAX_ESC_LEN + 1) * sizeof(char));
+ }
+}
+
+
+
+/*
+ * CONTROL CHARACTER FILTER - transmogrify control characters into their
+ * corresponding string representations (you know, ^blah and such)...
+ */
+
+/*
+ * the simple filter transforms unknown control characters in the stream
+ * into harmless strings.
+ */
+void
+gf_control_filter(FILTER_S *f, int flg)
+{
+ GF_INIT(f, f->next);
+
+ if(flg == GF_DATA){
+ register unsigned char c;
+ register int filt_only_c0;
+
+ filt_only_c0 = f->opt ? (*(int *) f->opt) : 0;
+
+ while(GF_GETC(f, c)){
+
+ if(((c < 0x20 || c == 0x7f)
+ || (c >= 0x80 && c < 0xA0 && !filt_only_c0))
+ && !(ASCII_ISSPACE((unsigned char) c)
+ || c == '\016' || c == '\017' || c == '\033')){
+ GF_PUTC(f->next, c >= 0x80 ? '~' : '^');
+ GF_PUTC(f->next, (c == 0x7f) ? '?' : (c & 0x1f) + '@');
+ }
+ else
+ GF_PUTC(f->next, c);
+ }
+
+ GF_END(f, f->next);
+ }
+ else if(flg == GF_EOD){
+ (void) GF_FLUSH(f->next);
+ (*f->next->f)(f->next, GF_EOD);
+ }
+}
+
+
+/*
+ * function called from the outside to set
+ * control filter's option, which says to filter C0 control characters
+ * but not C1 control chars. We don't call it at all if we don't want
+ * to filter C0 chars either.
+ */
+void *
+gf_control_filter_opt(int *filt_only_c0)
+{
+ return((void *) filt_only_c0);
+}
+
+
+/*
+ * TAG FILTER - quote all TAG_EMBED characters by doubling them.
+ * This prevents the possibility of embedding other tags.
+ * We assume that this filter should only be used for something
+ * that is eventually writing to a display, which has the special
+ * knowledge of quoted TAG_EMBEDs.
+ */
+void
+gf_tag_filter(FILTER_S *f, int flg)
+{
+ GF_INIT(f, f->next);
+
+ if(flg == GF_DATA){
+ register unsigned char c;
+
+ while(GF_GETC(f, c)){
+
+ if((c & 0xff) == (TAG_EMBED & 0xff)){
+ GF_PUTC(f->next, TAG_EMBED);
+ GF_PUTC(f->next, c);
+ }
+ else
+ GF_PUTC(f->next, c);
+ }
+
+ GF_END(f, f->next);
+ }
+ else if(flg == GF_EOD){
+ (void) GF_FLUSH(f->next);
+ (*f->next->f)(f->next, GF_EOD);
+ }
+}
+
+
+/*
+ * LINEWRAP FILTER - insert CRLF's at end of nearest whitespace before
+ * specified line width
+ */
+
+
+typedef struct wrap_col_s {
+ unsigned bold:1;
+ unsigned uline:1;
+ unsigned inverse:1;
+ unsigned tags:1;
+ unsigned do_indent:1;
+ unsigned on_comma:1;
+ unsigned flowed:1;
+ unsigned delsp:1;
+ unsigned quoted:1;
+ unsigned allwsp:1;
+ unsigned hard_nl:1;
+ unsigned leave_flowed:1;
+ unsigned use_color:1;
+ unsigned hdr_color:1;
+ unsigned for_compose:1;
+ unsigned handle_soft_hyphen:1;
+ unsigned saw_soft_hyphen:1;
+ unsigned trailing_space:1;
+ unsigned char utf8buf[7];
+ unsigned char *utf8bufp;
+ COLOR_PAIR *color;
+ STORE_S *spaces;
+ short embedded,
+ space_len;
+ char *lineendp;
+ int anchor,
+ prefbrk,
+ prefbrkn,
+ quote_depth,
+ quote_count,
+ sig,
+ state,
+ wrap_col,
+ wrap_max,
+ margin_l,
+ margin_r,
+ indent;
+ char special[256];
+} WRAP_S;
+
+#define WRAP_MARG_L(F) (((WRAP_S *)(F)->opt)->margin_l)
+#define WRAP_MARG_R(F) (((WRAP_S *)(F)->opt)->margin_r)
+#define WRAP_COL(F) (((WRAP_S *)(F)->opt)->wrap_col - WRAP_MARG_R(F) - ((((WRAP_S *)(F)->opt)->leave_flowed) ? 1 : 0))
+#define WRAP_MAX_COL(F) (((WRAP_S *)(F)->opt)->wrap_max - WRAP_MARG_R(F) - ((((WRAP_S *)(F)->opt)->leave_flowed) ? 1 : 0))
+#define WRAP_INDENT(F) (((WRAP_S *)(F)->opt)->indent)
+#define WRAP_DO_IND(F) (((WRAP_S *)(F)->opt)->do_indent)
+#define WRAP_COMMA(F) (((WRAP_S *)(F)->opt)->on_comma)
+#define WRAP_FLOW(F) (((WRAP_S *)(F)->opt)->flowed)
+#define WRAP_DELSP(F) (((WRAP_S *)(F)->opt)->delsp)
+#define WRAP_FL_QD(F) (((WRAP_S *)(F)->opt)->quote_depth)
+#define WRAP_FL_QC(F) (((WRAP_S *)(F)->opt)->quote_count)
+#define WRAP_FL_SIG(F) (((WRAP_S *)(F)->opt)->sig)
+#define WRAP_HARD(F) (((WRAP_S *)(F)->opt)->hard_nl)
+#define WRAP_LV_FLD(F) (((WRAP_S *)(F)->opt)->leave_flowed)
+#define WRAP_USE_CLR(F) (((WRAP_S *)(F)->opt)->use_color)
+#define WRAP_HDR_CLR(F) (((WRAP_S *)(F)->opt)->hdr_color)
+#define WRAP_FOR_CMPS(F) (((WRAP_S *)(F)->opt)->for_compose)
+#define WRAP_HANDLE_SOFT_HYPHEN(F) (((WRAP_S *)(F)->opt)->handle_soft_hyphen)
+#define WRAP_SAW_SOFT_HYPHEN(F) (((WRAP_S *)(F)->opt)->saw_soft_hyphen)
+#define WRAP_UTF8BUF(F, C) (((WRAP_S *)(F)->opt)->utf8buf[C])
+#define WRAP_UTF8BUFP(F) (((WRAP_S *)(F)->opt)->utf8bufp)
+#define WRAP_STATE(F) (((WRAP_S *)(F)->opt)->state)
+#define WRAP_QUOTED(F) (((WRAP_S *)(F)->opt)->quoted)
+#define WRAP_TAGS(F) (((WRAP_S *)(F)->opt)->tags)
+#define WRAP_BOLD(F) (((WRAP_S *)(F)->opt)->bold)
+#define WRAP_ULINE(F) (((WRAP_S *)(F)->opt)->uline)
+#define WRAP_INVERSE(F) (((WRAP_S *)(F)->opt)->inverse)
+#define WRAP_LASTC(F) (((WRAP_S *)(F)->opt)->lineendp)
+#define WRAP_EMBED(F) (((WRAP_S *)(F)->opt)->embedded)
+#define WRAP_ANCHOR(F) (((WRAP_S *)(F)->opt)->anchor)
+#define WRAP_PB_OFF(F) (((WRAP_S *)(F)->opt)->prefbrk)
+#define WRAP_PB_LEN(F) (((WRAP_S *)(F)->opt)->prefbrkn)
+#define WRAP_ALLWSP(F) (((WRAP_S *)(F)->opt)->allwsp)
+#define WRAP_SPC_LEN(F) (((WRAP_S *)(F)->opt)->space_len)
+#define WRAP_TRL_SPC(F) (((WRAP_S *)(F)->opt)->trailing_space)
+#define WRAP_SPEC(F, C) ((WRAP_S *) (F)->opt)->special[C]
+#define WRAP_COLOR(F) (((WRAP_S *)(F)->opt)->color)
+#define WRAP_COLOR_SET(F) ((WRAP_COLOR(F)) && (WRAP_COLOR(F)->fg[0]))
+#define WRAP_SPACES(F) (((WRAP_S *)(F)->opt)->spaces)
+#define WRAP_PUTC(F,C,W) { \
+ if((F)->linep == WRAP_LASTC(F)){ \
+ size_t offset = (F)->linep - (F)->line; \
+ fs_resize((void **) &(F)->line, \
+ (2 * offset) * sizeof(char)); \
+ (F)->linep = &(F)->line[offset]; \
+ WRAP_LASTC(F) = &(F)->line[2*offset-1]; \
+ } \
+ *(F)->linep++ = (C); \
+ (F)->f2 += (W); \
+ }
+
+#define WRAP_EMBED_PUTC(F,C) { \
+ if((F)->f2){ \
+ WRAP_PUTC((F), C, 0); \
+ } \
+ else \
+ so_writec(C, WRAP_SPACES(F)); \
+}
+
+#define WRAP_COLOR_UNSET(F) { \
+ if(WRAP_COLOR_SET(F)){ \
+ WRAP_COLOR(F)->fg[0] = '\0'; \
+ } \
+ }
+
+/*
+ * wrap_flush_embed flags
+ */
+#define WFE_NONE 0 /* Nothing special */
+#define WFE_CNT_HANDLE 1 /* account for/don't write handles */
+
+
+int wrap_flush(FILTER_S *, unsigned char **, unsigned char **, unsigned char **, unsigned char **);
+int wrap_flush_embed(FILTER_S *, unsigned char **, unsigned char **,
+ unsigned char **, unsigned char **);
+int wrap_flush_s(FILTER_S *,char *, int, int, unsigned char **, unsigned char **,
+ unsigned char **, unsigned char **, int);
+int wrap_eol(FILTER_S *, int, unsigned char **, unsigned char **,
+ unsigned char **, unsigned char **);
+int wrap_bol(FILTER_S *, int, int, unsigned char **,
+ unsigned char **, unsigned char **, unsigned char **);
+int wrap_quote_insert(FILTER_S *, unsigned char **, unsigned char **,
+ unsigned char **, unsigned char **);
+
+/*
+ * the no longer simple filter, breaks lines at end of white space nearest
+ * to global "gf_wrap_width" in length
+ * It also supports margins, indents (inverse indenting, really) and
+ * flowed text (ala RFC 3676)
+ *
+ */
+void
+gf_wrap(FILTER_S *f, int flg)
+{
+ register long i;
+ GF_INIT(f, f->next);
+
+ /*
+ * f->f1 state
+ * f->line buffer where next "word" being considered is stored
+ * f->f2 width in screen cells of f->line stuff
+ * f->n width in screen cells of the part of this line committed to next
+ * filter so far
+ */
+
+ if(flg == GF_DATA){
+ register unsigned char c;
+ register int state = f->f1;
+ int width, full_character;
+
+ while(GF_GETC(f, c)){
+
+ switch(state){
+ case CCR : /* CRLF or CR in text ? */
+ state = BOL; /* either way, handle start */
+
+ if(WRAP_FLOW(f)){
+ /* wrapped line? */
+ if(f->f2 == 0 && WRAP_SPC_LEN(f) && WRAP_TRL_SPC(f)){
+ /*
+ * whack trailing space char, but be aware
+ * of embeds in space buffer. grok them just
+ * in case they contain a 0x20 value
+ */
+ if(WRAP_DELSP(f)){
+ char *sb, *sbp, *scp = NULL;
+ int x;
+
+ for(sb = sbp = (char *)so_text(WRAP_SPACES(f)); *sbp; sbp++){
+ switch(*sbp){
+ case ' ' :
+ scp = sbp;
+ break;
+
+ case TAG_EMBED :
+ sbp++;
+ switch (*sbp++){
+ case TAG_HANDLE :
+ x = (int) *sbp++;
+ if(strlen(sbp) >= x)
+ sbp += (x - 1);
+
+ break;
+
+ case TAG_FGCOLOR :
+ case TAG_BGCOLOR :
+ if(strlen(sbp) >= RGBLEN)
+ sbp += (RGBLEN - 1);
+
+ break;
+
+ default :
+ break;
+ }
+
+ break;
+
+ default :
+ break;
+ }
+ }
+
+ /* replace space buf without trailing space char */
+ if(scp){
+ STORE_S *ns = so_get(CharStar, NULL, EDIT_ACCESS);
+
+ *scp++ = '\0';
+ WRAP_SPC_LEN(f)--;
+ WRAP_TRL_SPC(f) = 0;
+
+ so_puts(ns, sb);
+ so_puts(ns, scp);
+
+ so_give(&WRAP_SPACES(f));
+ WRAP_SPACES(f) = ns;
+ }
+ }
+ }
+ else{ /* fixed line */
+ WRAP_HARD(f) = 1;
+ wrap_flush(f, &ip, &eib, &op, &eob);
+ wrap_eol(f, 0, &ip, &eib, &op, &eob);
+
+ /*
+ * When we get to a real end of line, we don't need to
+ * remember what the special color was anymore because
+ * we aren't going to be changing back to it. We unset it
+ * so that we don't keep resetting the color to normal.
+ */
+ WRAP_COLOR_UNSET(f);
+ }
+
+ if(c == '\012'){ /* get c following LF */
+ break;
+ }
+ /* else c is first char of new line, fall thru */
+ }
+ else{
+ wrap_flush(f, &ip, &eib, &op, &eob);
+ wrap_eol(f, 0, &ip, &eib, &op, &eob);
+ WRAP_COLOR_UNSET(f); /* see note above */
+ if(c == '\012'){
+ break;
+ }
+ /* else fall thru to deal with beginning of line */
+ }
+
+ case BOL :
+ if(WRAP_FLOW(f)){
+ if(c == '>'){
+ WRAP_FL_QC(f) = 1; /* init it */
+ state = FL_QLEV; /* go collect it */
+ }
+ else {
+ /* if EMBEDed, process it and return here */
+ if(c == (unsigned char) TAG_EMBED){
+ WRAP_EMBED_PUTC(f, TAG_EMBED);
+ WRAP_STATE(f) = state;
+ state = TAG;
+ continue;
+ }
+
+ /* quote level change implies new paragraph */
+ if(WRAP_FL_QD(f)){
+ WRAP_FL_QD(f) = 0;
+ if(WRAP_HARD(f) == 0){
+ WRAP_HARD(f) = 1;
+ wrap_flush(f, &ip, &eib, &op, &eob);
+ wrap_eol(f, 0, &ip, &eib, &op, &eob);
+ WRAP_COLOR_UNSET(f); /* see note above */
+ }
+ }
+
+ if(WRAP_HARD(f)){
+ wrap_bol(f, 0, 1, &ip, &eib, &op,
+ &eob); /* write quoting prefix */
+ WRAP_HARD(f) = 0;
+ }
+
+ switch (c) {
+ case '\015' : /* a blank line? */
+ wrap_flush(f, &ip, &eib, &op, &eob);
+ state = CCR; /* go collect it */
+ break;
+
+ case ' ' : /* space stuffed */
+ state = FL_STF; /* just eat it */
+ break;
+
+ case '-' : /* possible sig-dash */
+ WRAP_FL_SIG(f) = 1; /* init state */
+ state = FL_SIG; /* go collect it */
+ break;
+
+ default :
+ state = DFL; /* go back to normal */
+ goto case_dfl; /* handle c like DFL case */
+ }
+ }
+ }
+ else{
+ state = DFL;
+ if(WRAP_COMMA(f) && c == TAB){
+ wrap_bol(f, 1, 0, &ip, &eib, &op,
+ &eob); /* convert to normal indent */
+ break;
+ }
+
+ wrap_bol(f,0,0, &ip, &eib, &op, &eob);
+ goto case_dfl; /* handle c like DFL case */
+ }
+
+ break;
+
+ case FL_QLEV :
+ if(c == '>'){ /* another level */
+ WRAP_FL_QC(f)++;
+ }
+ else {
+ /* if EMBEDed, process it and return here */
+ if(c == (unsigned char) TAG_EMBED){
+ WRAP_EMBED_PUTC(f, TAG_EMBED);
+ WRAP_STATE(f) = state;
+ state = TAG;
+ continue;
+ }
+
+ /* quote level change signals new paragraph */
+ if(WRAP_FL_QC(f) != WRAP_FL_QD(f)){
+ WRAP_FL_QD(f) = WRAP_FL_QC(f);
+ if(WRAP_HARD(f) == 0){ /* add hard newline */
+ WRAP_HARD(f) = 1; /* hard newline */
+ wrap_flush(f, &ip, &eib, &op, &eob);
+ wrap_eol(f, 0, &ip, &eib, &op, &eob);
+ WRAP_COLOR_UNSET(f); /* see note above */
+ }
+ }
+
+ if(WRAP_HARD(f)){
+ wrap_bol(f,0,1, &ip, &eib, &op, &eob);
+ WRAP_HARD(f) = 0;
+ }
+
+ switch (c) {
+ case '\015' : /* a blank line? */
+ wrap_flush(f, &ip, &eib, &op, &eob);
+ state = CCR; /* go collect it */
+ break;
+
+ case ' ' : /* space-stuffed! */
+ state = FL_STF; /* just eat it */
+ break;
+
+ case '-' : /* sig dash? */
+ WRAP_FL_SIG(f) = 1;
+ state = FL_SIG;
+ break;
+
+ default : /* something else */
+ state = DFL;
+ goto case_dfl; /* handle c like DFL */
+ }
+ }
+
+ break;
+
+ case FL_STF : /* space stuffed */
+ switch (c) {
+ case '\015' : /* a blank line? */
+ wrap_flush(f, &ip, &eib, &op, &eob);
+ state = CCR; /* go collect it */
+ break;
+
+ case (unsigned char) TAG_EMBED : /* process TAG data */
+ WRAP_EMBED_PUTC(f, TAG_EMBED);
+ WRAP_STATE(f) = state; /* and return */
+ state = TAG;
+ continue;
+
+ case '-' : /* sig dash? */
+ WRAP_FL_SIG(f) = 1;
+ WRAP_ALLWSP(f) = 0;
+ state = FL_SIG;
+ break;
+
+ default : /* something else */
+ state = DFL;
+ goto case_dfl; /* handle c like DFL */
+ }
+
+ break;
+
+ case FL_SIG : /* sig-dash collector */
+ switch (WRAP_FL_SIG(f)){ /* possible sig-dash? */
+ case 1 :
+ if(c != '-'){ /* not a sigdash */
+ if((f->n + WRAP_SPC_LEN(f) + 1) > WRAP_COL(f)){
+ wrap_flush_embed(f, &ip, &eib, &op,
+ &eob); /* note any embedded*/
+ wrap_eol(f, 1, &ip, &eib,
+ &op, &eob); /* plunk down newline */
+ wrap_bol(f, 1, 1, &ip, &eib,
+ &op, &eob); /* write any prefix */
+ }
+
+ WRAP_PUTC(f,'-', 1); /* write what we got */
+
+ WRAP_FL_SIG(f) = 0;
+ state = DFL;
+ goto case_dfl;
+ }
+
+ /* don't put anything yet until we know to wrap or not */
+ WRAP_FL_SIG(f) = 2;
+ break;
+
+ case 2 :
+ if(c != ' '){ /* not a sigdash */
+ WRAP_PUTC(f, '-', 1);
+ if((f->n + WRAP_SPC_LEN(f) + 2) > WRAP_COL(f)){
+ wrap_flush_embed(f, &ip, &eib, &op,
+ &eob); /* note any embedded*/
+ wrap_eol(f, 1, &ip, &eib,
+ &op, &eob); /* plunk down newline */
+ wrap_bol(f, 1, 1, &ip, &eib, &op,
+ &eob); /* write any prefix */
+ }
+
+ WRAP_PUTC(f,'-', 1); /* write what we got */
+
+ WRAP_FL_SIG(f) = 0;
+ state = DFL;
+ goto case_dfl;
+ }
+
+ /* don't put anything yet until we know to wrap or not */
+ WRAP_FL_SIG(f) = 3;
+ break;
+
+ case 3 :
+ if(c == '\015'){ /* success! */
+ /* known sigdash, newline if soft nl */
+ if(WRAP_SPC_LEN(f)){
+ wrap_flush(f, &ip, &eib, &op, &eob);
+ wrap_eol(f, 0, &ip, &eib, &op, &eob);
+ wrap_bol(f, 0, 1, &ip, &eib, &op, &eob);
+ }
+ WRAP_PUTC(f,'-',1);
+ WRAP_PUTC(f,'-',1);
+ WRAP_PUTC(f,' ',1);
+
+ state = CCR;
+ break;
+ }
+ else{
+ WRAP_FL_SIG(f) = 4; /* possible success */
+ }
+
+ case 4 :
+ switch(c){
+ case (unsigned char) TAG_EMBED :
+ /*
+ * At this point we're almost 100% sure that we've got
+ * a sigdash. Putc it (adding newline if previous
+ * was a soft nl) so we get it the right color
+ * before we store this new embedded stuff
+ */
+ if(WRAP_SPC_LEN(f)){
+ wrap_flush(f, &ip, &eib, &op, &eob);
+ wrap_eol(f, 0, &ip, &eib, &op, &eob);
+ wrap_bol(f, 0, 1, &ip, &eib, &op, &eob);
+ }
+ WRAP_PUTC(f,'-',1);
+ WRAP_PUTC(f,'-',1);
+ WRAP_PUTC(f,' ',1);
+
+ WRAP_FL_SIG(f) = 5;
+ break;
+
+ case '\015' : /* success! */
+ /*
+ * We shouldn't get here, but in case we do, we have
+ * not yet put the sigdash
+ */
+ if(WRAP_SPC_LEN(f)){
+ wrap_flush(f, &ip, &eib, &op, &eob);
+ wrap_eol(f, 0, &ip, &eib, &op, &eob);
+ wrap_bol(f, 0, 1, &ip, &eib, &op, &eob);
+ }
+ WRAP_PUTC(f,'-',1);
+ WRAP_PUTC(f,'-',1);
+ WRAP_PUTC(f,' ',1);
+
+ state = CCR;
+ break;
+
+ default : /* that's no sigdash! */
+ /* write what we got but didn't put yet */
+ WRAP_PUTC(f,'-', 1);
+ WRAP_PUTC(f,'-', 1);
+ WRAP_PUTC(f,' ', 1);
+
+ WRAP_FL_SIG(f) = 0;
+ wrap_flush(f, &ip, &eib, &op, &eob);
+ WRAP_SPC_LEN(f) = 1;
+ state = DFL; /* set normal state */
+ goto case_dfl; /* and go do "c" */
+ }
+
+ break;
+
+ case 5 :
+ WRAP_STATE(f) = FL_SIG; /* come back here */
+ WRAP_FL_SIG(f) = 6; /* and seek EOL */
+ WRAP_EMBED_PUTC(f, TAG_EMBED);
+ state = TAG; /* process embed */
+ goto case_tag;
+
+ case 6 :
+ /*
+ * at this point we've already putc the sigdash in case 4
+ */
+ switch(c){
+ case (unsigned char) TAG_EMBED :
+ WRAP_FL_SIG(f) = 5;
+ break;
+
+ case '\015' : /* success! */
+ state = CCR;
+ break;
+
+ default : /* that's no sigdash! */
+ /*
+ * probably never reached (fake sigdash with embedded
+ * stuff) but if this did get reached, then we
+ * might have accidentally disobeyed a soft nl
+ */
+ WRAP_FL_SIG(f) = 0;
+ wrap_flush(f, &ip, &eib, &op, &eob);
+ WRAP_SPC_LEN(f) = 1;
+ state = DFL; /* set normal state */
+ goto case_dfl; /* and go do "c" */
+ }
+
+ break;
+
+
+ default :
+ dprint((2, "-- gf_wrap: BROKEN FLOW STATE: %d\n",
+ WRAP_FL_SIG(f)));
+ WRAP_FL_SIG(f) = 0;
+ state = DFL; /* set normal state */
+ goto case_dfl; /* and go process "c" */
+ }
+
+ break;
+
+ case_dfl :
+ case DFL :
+ /*
+ * This was just if(WRAP_SPEC(f, c)) before the change to add
+ * the == 0 test. This isn't quite right, either. We should really
+ * be looking for special characters in the UCS characters, not
+ * in the incoming stream of UTF-8. It is not right to
+ * call this on bytes that are in the middle of a UTF-8 character,
+ * hence the == 0 test which restricts it to the first byte
+ * of a character. This isn't right, either, but it's closer.
+ * Also change the definition of WRAP_SPEC so that isspace only
+ * matches ascii characters, which will never be in the middle
+ * of a UTF-8 multi-byte character.
+ */
+ if((WRAP_UTF8BUFP(f) - &WRAP_UTF8BUF(f, 0)) == 0 && WRAP_SPEC(f, c)){
+ WRAP_SAW_SOFT_HYPHEN(f) = 0;
+ switch(c){
+ default :
+ if(WRAP_QUOTED(f))
+ break;
+
+ if(f->f2){ /* any non-lwsp to flush? */
+ if(WRAP_COMMA(f)){
+ /* remember our second best break point */
+ WRAP_PB_OFF(f) = f->linep - f->line;
+ WRAP_PB_LEN(f) = f->f2;
+ break;
+ }
+ else
+ wrap_flush(f, &ip, &eib, &op, &eob);
+ }
+
+ switch(c){ /* remember separator */
+ case ' ' :
+ WRAP_SPC_LEN(f)++;
+ WRAP_TRL_SPC(f) = 1;
+ so_writec(' ',WRAP_SPACES(f));
+ break;
+
+ case TAB :
+ {
+ int i = (int) f->n + WRAP_SPC_LEN(f);
+
+ do
+ WRAP_SPC_LEN(f)++;
+ while(++i & 0x07);
+
+ so_writec(TAB,WRAP_SPACES(f));
+ WRAP_TRL_SPC(f) = 0;
+ }
+
+ break;
+
+ default : /* some control char? */
+ WRAP_SPC_LEN(f) += 2;
+ WRAP_TRL_SPC(f) = 0;
+ break;
+ }
+
+ continue;
+
+ case '\"' :
+ WRAP_QUOTED(f) = !WRAP_QUOTED(f);
+ break;
+
+ case '\015' : /* already has newline? */
+ state = CCR;
+ continue;
+
+ case '\012' : /* bare LF in text? */
+ wrap_flush(f, &ip, &eib, &op, &eob); /* they must've */
+ wrap_eol(f, 0, &ip, &eib, &op, &eob); /* meant */
+ wrap_bol(f,1,1, &ip, &eib, &op, &eob); /* newline... */
+ continue;
+
+ case (unsigned char) TAG_EMBED :
+ WRAP_EMBED_PUTC(f, TAG_EMBED);
+ WRAP_STATE(f) = state;
+ state = TAG;
+ continue;
+
+ case ',' :
+ if(!WRAP_QUOTED(f)){
+ /* handle this special case in general code below */
+ if(f->n + WRAP_SPC_LEN(f) + f->f2 + 1 > WRAP_MAX_COL(f)
+ && WRAP_ALLWSP(f) && WRAP_PB_OFF(f))
+ break;
+
+ if(f->n + WRAP_SPC_LEN(f) + f->f2 + 1 > WRAP_COL(f)){
+ if(WRAP_ALLWSP(f)) /* if anything visible */
+ wrap_flush(f, &ip, &eib, &op,
+ &eob); /* ... blat buf'd chars */
+
+ wrap_eol(f, 1, &ip, &eib, &op,
+ &eob); /* plunk down newline */
+ wrap_bol(f, 1, 1, &ip, &eib, &op,
+ &eob); /* write any prefix */
+ }
+
+ WRAP_PUTC(f, ',', 1); /* put out comma */
+ wrap_flush(f, &ip, &eib, &op,
+ &eob); /* write buf'd chars */
+ continue;
+ }
+
+ break;
+ }
+ }
+ else if(WRAP_HANDLE_SOFT_HYPHEN(f)
+ && (WRAP_UTF8BUFP(f) - &WRAP_UTF8BUF(f, 0)) == 1
+ && WRAP_UTF8BUF(f, 0) == 0xC2 && c == 0xAD){
+ /*
+ * This is a soft hyphen. If there is enough space for
+ * a real hyphen to fit on the line here then we can
+ * flush everything up to before the soft hyphen,
+ * and simply remember that we saw a soft hyphen.
+ * If it turns out that we can't fit the next piece in
+ * then wrap_eol will append a real hyphen to the line.
+ * If we can fit another piece in it will be because we've
+ * reached the next break point. At that point we'll flush
+ * everything but won't include the unneeded hyphen. We erase
+ * the fact that we saw this soft hyphen because it have
+ * become irrelevant.
+ *
+ * If the hyphen is the character that puts us over the edge
+ * we go through the else case.
+ */
+
+ /* erase this soft hyphen character from buffer */
+ WRAP_UTF8BUFP(f) = &WRAP_UTF8BUF(f, 0);
+
+ if((f->n + WRAP_SPC_LEN(f) + f->f2 + 1) <= WRAP_COL(f)){
+ if(f->f2) /* any non-lwsp to flush? */
+ wrap_flush(f, &ip, &eib, &op, &eob);
+
+ /* remember that we saw the soft hyphen */
+ WRAP_SAW_SOFT_HYPHEN(f) = 1;
+ }
+ else{
+ /*
+ * Everything up to the hyphen fits, otherwise it
+ * would have already been flushed the last time
+ * through the loop. But the hyphen won't fit. So
+ * we need to go back to the last line break and
+ * break there instead. Then start a new line with
+ * the buffered up characters and the soft hyphen.
+ */
+ wrap_flush_embed(f, &ip, &eib, &op, &eob);
+ wrap_eol(f, 1, &ip, &eib, &op,
+ &eob); /* plunk down newline */
+ wrap_bol(f,1,1, &ip, &eib, &op,
+ &eob); /* write any prefix */
+
+ /*
+ * Now we're in the same situation as we would have
+ * been above except we're on a new line. Try to
+ * flush out the characters seen up to the hyphen.
+ */
+ if((f->n + WRAP_SPC_LEN(f) + f->f2 + 1) <= WRAP_COL(f)){
+ if(f->f2) /* any non-lwsp to flush? */
+ wrap_flush(f, &ip, &eib, &op, &eob);
+
+ /* remember that we saw the soft hyphen */
+ WRAP_SAW_SOFT_HYPHEN(f) = 1;
+ }
+ else
+ WRAP_SAW_SOFT_HYPHEN(f) = 0;
+ }
+
+ continue;
+ }
+
+ full_character = 0;
+
+ {
+ unsigned char *inputp;
+ unsigned long remaining_octets;
+ UCS ucs;
+
+ if(WRAP_UTF8BUFP(f) < &WRAP_UTF8BUF(f, 0) + 6){ /* always true */
+
+ *WRAP_UTF8BUFP(f)++ = c;
+ remaining_octets = WRAP_UTF8BUFP(f) - &WRAP_UTF8BUF(f, 0);
+ if(remaining_octets == 1 && isascii(WRAP_UTF8BUF(f, 0))){
+ full_character++;
+ if(c == TAB){
+ int i = (int) f->n;
+
+ while(i & 0x07)
+ i++;
+
+ width = i - f->n;
+ }
+ else if(c < 0x80 && iscntrl((unsigned char) c))
+ width = 2;
+ else
+ width = 1;
+ }
+ else{
+ inputp = &WRAP_UTF8BUF(f, 0);
+ ucs = (UCS) utf8_get(&inputp, &remaining_octets);
+ switch(ucs){
+ case U8G_ENDSTRG: /* incomplete character, wait */
+ case U8G_ENDSTRI: /* incomplete character, wait */
+ width = 0;
+ break;
+
+ default:
+ if(ucs & U8G_ERROR || ucs == UBOGON){
+ /*
+ * None of these cases is supposed to happen. If it
+ * does happen then the input stream isn't UTF-8
+ * so something is wrong. Writechar will treat
+ * each octet in the input buffer as a separate
+ * error character and print a '?' for each,
+ * so the width will be the number of octets.
+ */
+ width = WRAP_UTF8BUFP(f) - &WRAP_UTF8BUF(f, 0);
+ full_character++;
+ }
+ else{
+ /* got a character */
+ width = wcellwidth(ucs);
+ full_character++;
+
+ if(width < 0){
+ /*
+ * This happens when we have a UTF-8 character that
+ * we aren't able to print in our locale. For example,
+ * if the locale is setup with the terminal
+ * expecting ISO-8859-1 characters then there are
+ * lots of UTF-8 characters that can't be printed.
+ * Print a '?' instead.
+ */
+ width = 1;
+ }
+ }
+
+ break;
+ }
+ }
+ }
+ else{
+ /*
+ * This cannot happen because an error would have
+ * happened at least by character #6. So if we get
+ * here there is a bug in utf8_get().
+ */
+ if(WRAP_UTF8BUFP(f) == &WRAP_UTF8BUF(f, 0) + 6){
+ *WRAP_UTF8BUFP(f)++ = c;
+ }
+
+ /*
+ * We could possibly do some more sophisticated
+ * resynchronization here, but we aren't doing
+ * anything in Writechar so it wouldn't match up
+ * with that anyway. Just figure each character will
+ * end up being printed as a ? character.
+ */
+ width = WRAP_UTF8BUFP(f) - &WRAP_UTF8BUF(f, 0);
+ full_character++;
+ }
+ }
+
+ if(WRAP_ALLWSP(f)){
+ /*
+ * Nothing is visible yet but the first word may be too long
+ * all by itself. We need to break early.
+ */
+ if(f->n + WRAP_SPC_LEN(f) + f->f2 + width > WRAP_MAX_COL(f)){
+ /*
+ * A little reaching behind the curtain here.
+ * if there's at least a preferable break point, use
+ * it and stuff what's left back into the wrap buffer.
+ * The "nwsp" latch is used to skip leading whitespace
+ * The second half of the test prevents us from wrapping
+ * at the preferred break point in the case that it
+ * is so early in the line that it doesn't help.
+ * That is, the width of the indent is even more than
+ * the width of the first part before the preferred
+ * break point. An example would be breaking after
+ * "To:" when the indent is 4 which is > 3.
+ */
+ if(WRAP_PB_OFF(f) && WRAP_PB_LEN(f) >= WRAP_INDENT(f)){
+ char *p1 = f->line + WRAP_PB_OFF(f);
+ char *p2 = f->linep;
+ char c2;
+ int nwsp = 0, left_after_wrap;
+
+ left_after_wrap = f->f2 - WRAP_PB_LEN(f);
+
+ f->f2 = WRAP_PB_LEN(f);
+ f->linep = p1;
+
+ wrap_flush(f, &ip, &eib, &op, &eob); /* flush shortened buf */
+
+ /* put back rest of characters */
+ while(p1 < p2){
+ c2 = *p1++;
+ if(!(c2 == ' ' || c2 == '\t') || nwsp){
+ WRAP_PUTC(f, c2, 0);
+ nwsp = 1;
+ }
+ else
+ left_after_wrap--; /* wrong if a tab! */
+ }
+
+ f->f2 = MAX(left_after_wrap, 0);
+
+ wrap_eol(f, 1, &ip, &eib, &op,
+ &eob); /* plunk down newline */
+ wrap_bol(f,1,1, &ip, &eib, &op,
+ &eob); /* write any prefix */
+
+ /*
+ * What's this for?
+ * If we do the less preferable break point at
+ * the space we don't want to lose the fact that
+ * we might be able to break at this comma for
+ * the next one.
+ */
+ if(full_character && c == ','){
+ WRAP_PUTC(f, c, 1);
+ wrap_flush(f, &ip, &eib, &op, &eob);
+ WRAP_UTF8BUFP(f) = &WRAP_UTF8BUF(f, 0);
+ }
+ }
+ else{
+ wrap_flush(f, &ip, &eib, &op, &eob);
+
+ wrap_eol(f, 1, &ip, &eib, &op,
+ &eob); /* plunk down newline */
+ wrap_bol(f,1,1, &ip, &eib, &op,
+ &eob); /* write any prefix */
+ }
+ }
+ }
+ else if((f->n + WRAP_SPC_LEN(f) + f->f2 + width) > WRAP_COL(f)){
+ wrap_flush_embed(f, &ip, &eib, &op, &eob);
+ wrap_eol(f, 1, &ip, &eib, &op,
+ &eob); /* plunk down newline */
+ wrap_bol(f,1,1, &ip, &eib, &op,
+ &eob); /* write any prefix */
+ }
+
+ /*
+ * Commit entire multibyte UTF-8 character at once
+ * instead of writing partial characters into the
+ * buffer.
+ */
+ if(full_character){
+ unsigned char *q;
+
+ for(q = &WRAP_UTF8BUF(f, 0); q < WRAP_UTF8BUFP(f); q++){
+ WRAP_PUTC(f, *q, width);
+ width = 0;
+ }
+
+ WRAP_UTF8BUFP(f) = &WRAP_UTF8BUF(f, 0);
+ }
+
+ break;
+
+ case_tag :
+ case TAG :
+ WRAP_EMBED_PUTC(f, c);
+ switch(c){
+ case TAG_HANDLE :
+ WRAP_EMBED(f) = -1;
+ state = HANDLE;
+ break;
+
+ case TAG_FGCOLOR :
+ case TAG_BGCOLOR :
+ WRAP_EMBED(f) = RGBLEN;
+ state = HDATA;
+ break;
+
+ default :
+ state = WRAP_STATE(f);
+ break;
+ }
+
+ break;
+
+ case HANDLE :
+ WRAP_EMBED_PUTC(f, c);
+ WRAP_EMBED(f) = c;
+ state = HDATA;
+ break;
+
+ case HDATA :
+ if(f->f2){
+ WRAP_PUTC(f, c, 0);
+ }
+ else
+ so_writec(c, WRAP_SPACES(f));
+
+ if(!(WRAP_EMBED(f) -= 1)){
+ state = WRAP_STATE(f);
+ }
+
+ break;
+ }
+ }
+
+ f->f1 = state;
+ GF_END(f, f->next);
+ }
+ else if(flg == GF_EOD){
+ wrap_flush(f, &ip, &eib, &op, &eob);
+ if(WRAP_COLOR(f))
+ free_color_pair(&WRAP_COLOR(f));
+
+ fs_give((void **) &f->line); /* free temp line buffer */
+ so_give(&WRAP_SPACES(f));
+ fs_give((void **) &f->opt); /* free wrap widths struct */
+ (void) GF_FLUSH(f->next);
+ (*f->next->f)(f->next, GF_EOD);
+ }
+ else if(flg == GF_RESET){
+ dprint((9, "-- gf_reset wrap\n"));
+ f->f1 = BOL;
+ f->n = 0L; /* displayed length of line so far */
+ f->f2 = 0; /* displayed length of buffered chars */
+ WRAP_HARD(f) = 1; /* starting at beginning of line */
+ if(! (WRAP_S *) f->opt)
+ f->opt = gf_wrap_filter_opt(75, 80, NULL, 0, 0);
+
+ while(WRAP_INDENT(f) >= WRAP_MAX_COL(f))
+ WRAP_INDENT(f) /= 2;
+
+ f->line = (char *) fs_get(WRAP_MAX_COL(f) * sizeof(char));
+ f->linep = f->line;
+ WRAP_LASTC(f) = &f->line[WRAP_MAX_COL(f) - 1];
+
+ for(i = 0; i < 256; i++)
+ ((WRAP_S *) f->opt)->special[i] = ((i == '\"' && WRAP_COMMA(f))
+ || i == '\015'
+ || i == '\012'
+ || (i == (unsigned char) TAG_EMBED
+ && WRAP_TAGS(f))
+ || (i == ',' && WRAP_COMMA(f)
+ && !WRAP_QUOTED(f))
+ || ASCII_ISSPACE(i));
+ WRAP_SPACES(f) = so_get(CharStar, NULL, EDIT_ACCESS);
+ WRAP_UTF8BUFP(f) = &WRAP_UTF8BUF(f, 0);
+ }
+}
+
+int
+wrap_flush(FILTER_S *f, unsigned char **ipp, unsigned char **eibp,
+ unsigned char **opp, unsigned char **eobp)
+{
+ register char *s;
+ register int n;
+
+ s = (char *)so_text(WRAP_SPACES(f));
+ n = so_tell(WRAP_SPACES(f));
+ so_seek(WRAP_SPACES(f), 0L, 0);
+ wrap_flush_s(f, s, n, WRAP_SPC_LEN(f), ipp, eibp, opp, eobp, WFE_NONE);
+ so_truncate(WRAP_SPACES(f), 0L);
+ WRAP_SPC_LEN(f) = 0;
+ WRAP_TRL_SPC(f) = 0;
+ s = f->line;
+ n = f->linep - f->line;
+ wrap_flush_s(f, s, n, f->f2, ipp, eibp, opp, eobp, WFE_NONE);
+ f->f2 = 0;
+ f->linep = f->line;
+ WRAP_PB_OFF(f) = 0;
+ WRAP_PB_LEN(f) = 0;
+
+ return 0;
+}
+
+int
+wrap_flush_embed(FILTER_S *f, unsigned char **ipp, unsigned char **eibp, unsigned char **opp, unsigned char **eobp)
+{
+ register char *s;
+ register int n;
+ s = (char *)so_text(WRAP_SPACES(f));
+ n = so_tell(WRAP_SPACES(f));
+ so_seek(WRAP_SPACES(f), 0L, 0);
+ wrap_flush_s(f, s, n, 0, ipp, eibp, opp, eobp, WFE_CNT_HANDLE);
+ so_truncate(WRAP_SPACES(f), 0L);
+ WRAP_SPC_LEN(f) = 0;
+ WRAP_TRL_SPC(f) = 0;
+
+ return 0;
+}
+
+int
+wrap_flush_s(FILTER_S *f, char *s, int n, int w, unsigned char **ipp,
+ unsigned char **eibp, unsigned char **opp, unsigned char **eobp, int flags)
+{
+ f->n += w;
+
+ for(; n > 0; n--,s++){
+ if(*s == TAG_EMBED){
+ if(n-- > 0){
+ switch(*++s){
+ case TAG_BOLDON :
+ GF_PUTC_GLO(f->next,TAG_EMBED);
+ GF_PUTC_GLO(f->next,TAG_BOLDON);
+ WRAP_BOLD(f) = 1;
+ break;
+ case TAG_BOLDOFF :
+ GF_PUTC_GLO(f->next,TAG_EMBED);
+ GF_PUTC_GLO(f->next,TAG_BOLDOFF);
+ WRAP_BOLD(f) = 0;
+ break;
+ case TAG_ULINEON :
+ GF_PUTC_GLO(f->next,TAG_EMBED);
+ GF_PUTC_GLO(f->next,TAG_ULINEON);
+ WRAP_ULINE(f) = 1;
+ break;
+ case TAG_ULINEOFF :
+ GF_PUTC_GLO(f->next,TAG_EMBED);
+ GF_PUTC_GLO(f->next,TAG_ULINEOFF);
+ WRAP_ULINE(f) = 0;
+ break;
+ case TAG_INVOFF :
+ GF_PUTC_GLO(f->next,TAG_EMBED);
+ GF_PUTC_GLO(f->next,TAG_INVOFF);
+ WRAP_ANCHOR(f) = 0;
+ break;
+ case TAG_HANDLE :
+ if((flags & WFE_CNT_HANDLE) == 0)
+ GF_PUTC_GLO(f->next,TAG_EMBED);
+
+ if(n-- > 0){
+ int i = *++s;
+
+ if((flags & WFE_CNT_HANDLE) == 0)
+ GF_PUTC_GLO(f->next, TAG_HANDLE);
+
+ if(i <= n){
+ n -= i;
+
+ if((flags & WFE_CNT_HANDLE) == 0)
+ GF_PUTC_GLO(f->next, i);
+
+ WRAP_ANCHOR(f) = 0;
+ while(i-- > 0){
+ WRAP_ANCHOR(f) = (WRAP_ANCHOR(f) * 10) + (*++s-'0');
+
+ if((flags & WFE_CNT_HANDLE) == 0)
+ GF_PUTC_GLO(f->next,*s);
+ }
+
+ }
+ }
+ break;
+ case TAG_FGCOLOR :
+ if(pico_usingcolor() && n >= RGBLEN){
+ int i;
+ GF_PUTC_GLO(f->next,TAG_EMBED);
+ GF_PUTC_GLO(f->next,TAG_FGCOLOR);
+ if(!WRAP_COLOR(f))
+ WRAP_COLOR(f)=new_color_pair(NULL,NULL);
+ strncpy(WRAP_COLOR(f)->fg, s+1, RGBLEN);
+ WRAP_COLOR(f)->fg[RGBLEN]='\0';
+ i = RGBLEN;
+ n -= i;
+ while(i-- > 0)
+ GF_PUTC_GLO(f->next,
+ (*++s) & 0xff);
+ }
+ break;
+ case TAG_BGCOLOR :
+ if(pico_usingcolor() && n >= RGBLEN){
+ int i;
+ GF_PUTC_GLO(f->next,TAG_EMBED);
+ GF_PUTC_GLO(f->next,TAG_BGCOLOR);
+ if(!WRAP_COLOR(f))
+ WRAP_COLOR(f)=new_color_pair(NULL,NULL);
+ strncpy(WRAP_COLOR(f)->bg, s+1, RGBLEN);
+ WRAP_COLOR(f)->bg[RGBLEN]='\0';
+ i = RGBLEN;
+ n -= i;
+ while(i-- > 0)
+ GF_PUTC_GLO(f->next,
+ (*++s) & 0xff);
+ }
+ break;
+ default :
+ break;
+ }
+ }
+ }
+ else if(w){
+
+ if(f->n <= WRAP_MAX_COL(f)){
+ GF_PUTC_GLO(f->next, (*s) & 0xff);
+ }
+ else{
+ dprint((2, "-- gf_wrap: OVERRUN: %c\n", (*s) & 0xff));
+ }
+
+ WRAP_ALLWSP(f) = 0;
+ }
+ }
+
+ return 0;
+}
+
+int
+wrap_eol(FILTER_S *f, int c, unsigned char **ipp, unsigned char **eibp,
+ unsigned char **opp, unsigned char **eobp)
+{
+ if(WRAP_SAW_SOFT_HYPHEN(f)){
+ WRAP_SAW_SOFT_HYPHEN(f) = 0;
+ GF_PUTC_GLO(f->next, '-'); /* real hyphen */
+ }
+
+ if(c && WRAP_LV_FLD(f))
+ GF_PUTC_GLO(f->next, ' ');
+
+ if(WRAP_BOLD(f)){
+ GF_PUTC_GLO(f->next, TAG_EMBED);
+ GF_PUTC_GLO(f->next, TAG_BOLDOFF);
+ }
+
+ if(WRAP_ULINE(f)){
+ GF_PUTC_GLO(f->next, TAG_EMBED);
+ GF_PUTC_GLO(f->next, TAG_ULINEOFF);
+ }
+
+ if(WRAP_INVERSE(f) || WRAP_ANCHOR(f)){
+ GF_PUTC_GLO(f->next, TAG_EMBED);
+ GF_PUTC_GLO(f->next, TAG_INVOFF);
+ }
+
+ if(WRAP_COLOR_SET(f)){
+ char *p;
+ char cb[RGBLEN+1];
+ GF_PUTC_GLO(f->next, TAG_EMBED);
+ GF_PUTC_GLO(f->next, TAG_FGCOLOR);
+ strncpy(cb, color_to_asciirgb(ps_global->VAR_NORM_FORE_COLOR), sizeof(cb));
+ cb[sizeof(cb)-1] = '\0';
+ p = cb;
+ for(; *p; p++)
+ GF_PUTC_GLO(f->next, *p);
+ GF_PUTC_GLO(f->next, TAG_EMBED);
+ GF_PUTC_GLO(f->next, TAG_BGCOLOR);
+ strncpy(cb, color_to_asciirgb(ps_global->VAR_NORM_BACK_COLOR), sizeof(cb));
+ cb[sizeof(cb)-1] = '\0';
+ p = cb;
+ for(; *p; p++)
+ GF_PUTC_GLO(f->next, *p);
+ }
+
+ GF_PUTC_GLO(f->next, '\015');
+ GF_PUTC_GLO(f->next, '\012');
+ f->n = 0L;
+ so_truncate(WRAP_SPACES(f), 0L);
+ WRAP_SPC_LEN(f) = 0;
+ WRAP_TRL_SPC(f) = 0;
+
+ return 0;
+}
+
+int
+wrap_bol(FILTER_S *f, int ivar, int q, unsigned char **ipp, unsigned char **eibp,
+ unsigned char **opp, unsigned char **eobp)
+{
+ int n = WRAP_MARG_L(f) + (ivar ? WRAP_INDENT(f) : 0);
+
+ if(WRAP_HDR_CLR(f)){
+ char *p;
+ char cbuf[RGBLEN+1];
+ int k;
+
+ if((k = WRAP_MARG_L(f)) > 0)
+ while(k-- > 0){
+ n--;
+ f->n++;
+ GF_PUTC_GLO(f->next, ' ');
+ }
+
+ GF_PUTC_GLO(f->next, TAG_EMBED);
+ GF_PUTC_GLO(f->next, TAG_FGCOLOR);
+ strncpy(cbuf,
+ color_to_asciirgb(ps_global->VAR_HEADER_GENERAL_FORE_COLOR),
+ sizeof(cbuf));
+ cbuf[sizeof(cbuf)-1] = '\0';
+ p = cbuf;
+ for(; *p; p++)
+ GF_PUTC_GLO(f->next, *p);
+ GF_PUTC_GLO(f->next, TAG_EMBED);
+ GF_PUTC_GLO(f->next, TAG_BGCOLOR);
+ strncpy(cbuf,
+ color_to_asciirgb(ps_global->VAR_HEADER_GENERAL_BACK_COLOR),
+ sizeof(cbuf));
+ cbuf[sizeof(cbuf)-1] = '\0';
+ p = cbuf;
+ for(; *p; p++)
+ GF_PUTC_GLO(f->next, *p);
+ }
+
+ while(n-- > 0){
+ f->n++;
+ GF_PUTC_GLO(f->next, ' ');
+ }
+
+ WRAP_ALLWSP(f) = 1;
+
+ if(q)
+ wrap_quote_insert(f, ipp, eibp, opp, eobp);
+
+ if(WRAP_BOLD(f)){
+ GF_PUTC_GLO(f->next, TAG_EMBED);
+ GF_PUTC_GLO(f->next, TAG_BOLDON);
+ }
+ if(WRAP_ULINE(f)){
+ GF_PUTC_GLO(f->next, TAG_EMBED);
+ GF_PUTC_GLO(f->next, TAG_ULINEON);
+ }
+ if(WRAP_INVERSE(f)){
+ GF_PUTC_GLO(f->next, TAG_EMBED);
+ GF_PUTC_GLO(f->next, TAG_INVON);
+ }
+ if(WRAP_COLOR_SET(f)){
+ char *p;
+ if(WRAP_COLOR(f)->fg[0]){
+ char cb[RGBLEN+1];
+ GF_PUTC_GLO(f->next, TAG_EMBED);
+ GF_PUTC_GLO(f->next, TAG_FGCOLOR);
+ strncpy(cb, color_to_asciirgb(WRAP_COLOR(f)->fg), sizeof(cb));
+ cb[sizeof(cb)-1] = '\0';
+ p = cb;
+ for(; *p; p++)
+ GF_PUTC_GLO(f->next, *p);
+ }
+ if(WRAP_COLOR(f)->bg[0]){
+ char cb[RGBLEN+1];
+ GF_PUTC_GLO(f->next, TAG_EMBED);
+ GF_PUTC_GLO(f->next, TAG_BGCOLOR);
+ strncpy(cb, color_to_asciirgb(WRAP_COLOR(f)->bg), sizeof(cb));
+ cb[sizeof(cb)-1] = '\0';
+ p = cb;
+ for(; *p; p++)
+ GF_PUTC_GLO(f->next, *p);
+ }
+ }
+ if(WRAP_ANCHOR(f)){
+ char buf[64]; int i;
+ GF_PUTC_GLO(f->next, TAG_EMBED);
+ GF_PUTC_GLO(f->next, TAG_HANDLE);
+ snprintf(buf, sizeof(buf), "%d", WRAP_ANCHOR(f));
+ GF_PUTC_GLO(f->next, (int) strlen(buf));
+ for(i = 0; buf[i]; i++)
+ GF_PUTC_GLO(f->next, buf[i]);
+ }
+
+ return 0;
+}
+
+int
+wrap_quote_insert(FILTER_S *f, unsigned char **ipp, unsigned char **eibp,
+ unsigned char **opp, unsigned char **eobp)
+{
+ int j, i;
+ COLOR_PAIR *col = NULL;
+ char *prefix = NULL, *last_prefix = NULL;
+
+ if(ps_global->VAR_QUOTE_REPLACE_STRING){
+ get_pair(ps_global->VAR_QUOTE_REPLACE_STRING, &prefix, &last_prefix, 0, 0);
+ if(!prefix && last_prefix){
+ prefix = last_prefix;
+ last_prefix = NULL;
+ }
+ }
+
+ for(j = 0; j < WRAP_FL_QD(f); j++){
+ if(WRAP_USE_CLR(f)){
+ if((j % 3) == 0
+ && ps_global->VAR_QUOTE1_FORE_COLOR
+ && ps_global->VAR_QUOTE1_BACK_COLOR
+ && (col = new_color_pair(ps_global->VAR_QUOTE1_FORE_COLOR,
+ ps_global->VAR_QUOTE1_BACK_COLOR))
+ && pico_is_good_colorpair(col)){
+ GF_COLOR_PUTC(f, col);
+ }
+ else if((j % 3) == 1
+ && ps_global->VAR_QUOTE2_FORE_COLOR
+ && ps_global->VAR_QUOTE2_BACK_COLOR
+ && (col = new_color_pair(ps_global->VAR_QUOTE2_FORE_COLOR,
+ ps_global->VAR_QUOTE2_BACK_COLOR))
+ && pico_is_good_colorpair(col)){
+ GF_COLOR_PUTC(f, col);
+ }
+ else if((j % 3) == 2
+ && ps_global->VAR_QUOTE3_FORE_COLOR
+ && ps_global->VAR_QUOTE3_BACK_COLOR
+ && (col = new_color_pair(ps_global->VAR_QUOTE3_FORE_COLOR,
+ ps_global->VAR_QUOTE3_BACK_COLOR))
+ && pico_is_good_colorpair(col)){
+ GF_COLOR_PUTC(f, col);
+ }
+ if(col){
+ free_color_pair(&col);
+ col = NULL;
+ }
+ }
+
+ if(!WRAP_LV_FLD(f)){
+ if(!WRAP_FOR_CMPS(f) && ps_global->VAR_QUOTE_REPLACE_STRING && prefix){
+ for(i = 0; prefix[i]; i++)
+ GF_PUTC_GLO(f->next, prefix[i]);
+ f->n += utf8_width(prefix);
+ }
+ else if(ps_global->VAR_REPLY_STRING
+ && (!strcmp(ps_global->VAR_REPLY_STRING, ">")
+ || !strcmp(ps_global->VAR_REPLY_STRING, "\">\""))){
+ GF_PUTC_GLO(f->next, '>');
+ f->n += 1;
+ }
+ else{
+ GF_PUTC_GLO(f->next, '>');
+ GF_PUTC_GLO(f->next, ' ');
+ f->n += 2;
+ }
+ }
+ else{
+ GF_PUTC_GLO(f->next, '>');
+ f->n += 1;
+ }
+ }
+ if(j && WRAP_LV_FLD(f)){
+ GF_PUTC_GLO(f->next, ' ');
+ f->n++;
+ }
+ else if(j && last_prefix){
+ for(i = 0; last_prefix[i]; i++)
+ GF_PUTC_GLO(f->next, last_prefix[i]);
+ f->n += utf8_width(last_prefix);
+ }
+
+ if(prefix)
+ fs_give((void **)&prefix);
+ if(last_prefix)
+ fs_give((void **)&last_prefix);
+
+ return 0;
+}
+
+
+/*
+ * function called from the outside to set
+ * wrap filter's width option
+ */
+void *
+gf_wrap_filter_opt(int width, int width_max, int *margin, int indent, int flags)
+{
+ WRAP_S *wrap;
+
+ /* NOTE: variables MUST be sanity checked before they get here */
+ wrap = (WRAP_S *) fs_get(sizeof(WRAP_S));
+ memset(wrap, 0, sizeof(WRAP_S));
+ wrap->wrap_col = width;
+ wrap->wrap_max = width_max;
+ wrap->indent = indent;
+ wrap->margin_l = (margin) ? margin[0] : 0;
+ wrap->margin_r = (margin) ? margin[1] : 0;
+ wrap->tags = (GFW_HANDLES & flags) == GFW_HANDLES;
+ wrap->on_comma = (GFW_ONCOMMA & flags) == GFW_ONCOMMA;
+ wrap->flowed = (GFW_FLOWED & flags) == GFW_FLOWED;
+ wrap->leave_flowed = (GFW_FLOW_RESULT & flags) == GFW_FLOW_RESULT;
+ wrap->delsp = (GFW_DELSP & flags) == GFW_DELSP;
+ wrap->use_color = (GFW_USECOLOR & flags) == GFW_USECOLOR;
+ wrap->hdr_color = (GFW_HDRCOLOR & flags) == GFW_HDRCOLOR;
+ wrap->for_compose = (GFW_FORCOMPOSE & flags) == GFW_FORCOMPOSE;
+ wrap->handle_soft_hyphen = (GFW_SOFTHYPHEN & flags) == GFW_SOFTHYPHEN;
+
+ return((void *) wrap);
+}
+
+
+void *
+gf_url_hilite_opt(URL_HILITE_S *uh, HANDLE_S **handlesp, int flags)
+{
+ if(uh){
+ memset(uh, 0, sizeof(URL_HILITE_S));
+ uh->handlesp = handlesp;
+ uh->hdr_color = (URH_HDRCOLOR & flags) == URH_HDRCOLOR;
+ }
+
+ return((void *) uh);
+}
+
+
+#define PF_QD(F) (((PREFLOW_S *)(F)->opt)->quote_depth)
+#define PF_QC(F) (((PREFLOW_S *)(F)->opt)->quote_count)
+#define PF_SIG(F) (((PREFLOW_S *)(F)->opt)->sig)
+
+typedef struct preflow_s {
+ int quote_depth,
+ quote_count,
+ sig;
+} PREFLOW_S;
+
+/*
+ * This would normally be handled in gf_wrap. If there is a possibility
+ * that a url we want to recognize is cut in half by a soft newline we
+ * want to fix that up by putting the halves back together. We do that
+ * by deleting the soft newline and putting it all in one line. It will
+ * still get wrapped later in gf_wrap. It isn't pretty with all the
+ * goto's, but whatta ya gonna do?
+ */
+void
+gf_preflow(FILTER_S *f, int flg)
+{
+ GF_INIT(f, f->next);
+
+ if(flg == GF_DATA){
+ register unsigned char c;
+ register int state = f->f1;
+ register int pending = f->f2;
+
+ while(GF_GETC(f, c)){
+ switch(state){
+ case DFL:
+default_case:
+ switch(c){
+ case ' ':
+ state = WSPACE;
+ break;
+
+ case '\015':
+ state = CCR;
+ break;
+
+ default:
+ GF_PUTC(f->next, c);
+ break;
+ }
+
+ break;
+
+ case CCR:
+ switch(c){
+ case '\012':
+ pending = 1;
+ state = BOL;
+ break;
+
+ default:
+ GF_PUTC(f->next, '\012');
+ state = DFL;
+ goto default_case;
+ break;
+ }
+
+ break;
+
+ case WSPACE:
+ switch(c){
+ case '\015':
+ state = SPACECR;
+ break;
+
+ default:
+ GF_PUTC(f->next, ' ');
+ state = DFL;
+ goto default_case;
+ break;
+ }
+
+ break;
+
+ case SPACECR:
+ switch(c){
+ case '\012':
+ pending = 2;
+ state = BOL;
+ break;
+
+ default:
+ GF_PUTC(f->next, ' ');
+ GF_PUTC(f->next, '\012');
+ state = DFL;
+ goto default_case;
+ break;
+ }
+
+ break;
+
+ case BOL:
+ PF_QC(f) = 0;
+ if(c == '>'){ /* count quote level */
+ PF_QC(f)++;
+ state = FL_QLEV;
+ }
+ else{
+done_counting_quotes:
+ if(c == ' '){ /* eat stuffed space */
+ state = FL_STF;
+ break;
+ }
+
+done_with_stuffed_space:
+ if(c == '-'){ /* look for signature */
+ PF_SIG(f) = 1;
+ state = FL_SIG;
+ break;
+ }
+
+done_with_sig:
+ if(pending == 2){
+ if(PF_QD(f) == PF_QC(f) && PF_SIG(f) < 4){
+ /* delete pending */
+
+ PF_QD(f) = PF_QC(f);
+
+ /* suppress quotes, too */
+ PF_QC(f) = 0;
+ }
+ else{
+ /*
+ * This should have been a hard new line
+ * instead so leave out the trailing space.
+ */
+ GF_PUTC(f->next, '\015');
+ GF_PUTC(f->next, '\012');
+
+ PF_QD(f) = PF_QC(f);
+ }
+ }
+ else if(pending == 1){
+ GF_PUTC(f->next, '\015');
+ GF_PUTC(f->next, '\012');
+ PF_QD(f) = PF_QC(f);
+ }
+ else{
+ PF_QD(f) = PF_QC(f);
+ }
+
+ pending = 0;
+ state = DFL;
+ while(PF_QC(f)-- > 0)
+ GF_PUTC(f->next, '>');
+
+ switch(PF_SIG(f)){
+ case 0:
+ default:
+ break;
+
+ case 1:
+ GF_PUTC(f->next, '-');
+ break;
+
+ case 2:
+ GF_PUTC(f->next, '-');
+ GF_PUTC(f->next, '-');
+ break;
+
+ case 3:
+ case 4:
+ GF_PUTC(f->next, '-');
+ GF_PUTC(f->next, '-');
+ GF_PUTC(f->next, ' ');
+ break;
+ }
+
+ PF_SIG(f) = 0;
+ goto default_case; /* to handle c */
+ }
+
+ break;
+
+ case FL_QLEV: /* count quote level */
+ if(c == '>')
+ PF_QC(f)++;
+ else
+ goto done_counting_quotes;
+
+ break;
+
+ case FL_STF: /* eat stuffed space */
+ goto done_with_stuffed_space;
+ break;
+
+ case FL_SIG: /* deal with sig indicator */
+ switch(PF_SIG(f)){
+ case 1: /* saw '-' */
+ if(c == '-')
+ PF_SIG(f) = 2;
+ else
+ goto done_with_sig;
+
+ break;
+
+ case 2: /* saw '--' */
+ if(c == ' ')
+ PF_SIG(f) = 3;
+ else
+ goto done_with_sig;
+
+ break;
+
+ case 3: /* saw '-- ' */
+ if(c == '\015')
+ PF_SIG(f) = 4; /* it really is a sig line */
+
+ goto done_with_sig;
+ break;
+ }
+
+ break;
+ }
+ }
+
+ f->f1 = state;
+ f->f2 = pending;
+ GF_END(f, f->next);
+ }
+ else if(flg == GF_EOD){
+ fs_give((void **) &f->opt);
+ (void) GF_FLUSH(f->next);
+ (*f->next->f)(f->next, GF_EOD);
+ }
+ else if(flg == GF_RESET){
+ PREFLOW_S *pf;
+
+ pf = (PREFLOW_S *) fs_get(sizeof(*pf));
+ memset(pf, 0, sizeof(*pf));
+ f->opt = (void *) pf;
+
+ f->f1 = BOL; /* state */
+ f->f2 = 0; /* pending */
+ PF_QD(f) = 0; /* quote depth */
+ PF_QC(f) = 0; /* quote count */
+ PF_SIG(f) = 0; /* sig level */
+ }
+}
+
+
+
+
+/*
+ * LINE PREFIX FILTER - insert given text at beginning of each
+ * line
+ */
+
+
+#define GF_PREFIX_WRITE(s) { \
+ register char *p; \
+ if((p = (s)) != NULL) \
+ while(*p) \
+ GF_PUTC(f->next, *p++); \
+ }
+
+
+/*
+ * the simple filter, prepends each line with the requested prefix.
+ * if prefix is null, does nothing, and as with all filters, assumes
+ * NVT end of lines.
+ */
+void
+gf_prefix(FILTER_S *f, int flg)
+{
+ GF_INIT(f, f->next);
+
+ if(flg == GF_DATA){
+ register unsigned char c;
+ register int state = f->f1;
+ register int first = f->f2;
+
+ while(GF_GETC(f, c)){
+
+ if(first){ /* write initial prefix!! */
+ first = 0; /* but just once */
+ GF_PREFIX_WRITE((char *) f->opt);
+ }
+
+ /*
+ * State == 0 is the starting state and the usual state.
+ * State == 1 means we saw a CR and haven't acted on it yet.
+ * We are looking for a LF to get the CRLF end of line.
+ * However, we also treat bare CR and bare LF as if they
+ * were CRLF sequences. What else could it mean in text?
+ * This filter is only used for text so that is probably
+ * a reasonable interpretation of the bad input.
+ */
+ if(c == '\015'){ /* CR */
+ if(state){ /* Treat pending CR as endofline, */
+ GF_PUTC(f->next, '\015'); /* and remain in saw-a-CR state. */
+ GF_PUTC(f->next, '\012');
+ GF_PREFIX_WRITE((char *) f->opt);
+ }
+ else{
+ state = 1;
+ }
+ }
+ else if(c == '\012'){ /* LF */
+ GF_PUTC(f->next, '\015'); /* Got either a CRLF or a bare LF, */
+ GF_PUTC(f->next, '\012'); /* treat both as if a CRLF. */
+ GF_PREFIX_WRITE((char *) f->opt);
+ state = 0;
+ }
+ else{ /* any other character */
+ if(state){
+ GF_PUTC(f->next, '\015'); /* Treat pending CR as endofline. */
+ GF_PUTC(f->next, '\012');
+ GF_PREFIX_WRITE((char *) f->opt);
+ state = 0;
+ }
+
+ GF_PUTC(f->next, c);
+ }
+ }
+
+ f->f1 = state; /* save state for next chunk of data */
+ f->f2 = first;
+ GF_END(f, f->next);
+ }
+ else if(flg == GF_EOD){
+ (void) GF_FLUSH(f->next);
+ (*f->next->f)(f->next, GF_EOD);
+ }
+ else if(flg == GF_RESET){
+ dprint((9, "-- gf_reset prefix\n"));
+ f->f1 = 0;
+ f->f2 = 1; /* nothing written yet */
+ }
+}
+
+
+/*
+ * function called from the outside to set
+ * prefix filter's prefix string
+ */
+void *
+gf_prefix_opt(char *prefix)
+{
+ return((void *) prefix);
+}
+
+
+/*
+ * LINE TEST FILTER - accumulate lines and offer each to the provided
+ * test function.
+ */
+
+typedef struct _linetest_s {
+ linetest_t f;
+ void *local;
+} LINETEST_S;
+
+
+/* accumulator growth increment */
+#define LINE_TEST_BLOCK 1024
+
+#define GF_LINE_TEST_EOB(f) \
+ ((f)->line + ((f)->f2 - 1))
+
+#define GF_LINE_TEST_ADD(f, c) \
+ { \
+ if(p >= eobuf){ \
+ f->f2 += LINE_TEST_BLOCK; \
+ fs_resize((void **)&f->line, \
+ (size_t) f->f2 * sizeof(char)); \
+ eobuf = GF_LINE_TEST_EOB(f); \
+ p = eobuf - LINE_TEST_BLOCK; \
+ } \
+ *p++ = c; \
+ }
+
+#define GF_LINE_TEST_TEST(F, D) \
+ { \
+ unsigned char c; \
+ register char *cp; \
+ register int l; \
+ LT_INS_S *ins = NULL, *insp; \
+ *p = '\0'; \
+ (D) = (*((LINETEST_S *) (F)->opt)->f)((F)->n++, \
+ (F)->line, &ins, \
+ ((LINETEST_S *) (F)->opt)->local); \
+ if((D) < 2){ \
+ if((D) < 0){ \
+ if((F)->line) \
+ fs_give((void **) &(F)->line); \
+ if((F)->opt) \
+ fs_give((void **) &(F)->opt); \
+ gf_error(_("translation error")); \
+ /* NO RETURN */ \
+ } \
+ for(insp = ins, cp = (F)->line; cp < p; ){ \
+ if(insp && cp == insp->where){ \
+ if(insp->len > 0){ \
+ for(l = 0; l < insp->len; l++){ \
+ c = (unsigned char) insp->text[l]; \
+ GF_PUTC((F)->next, c); \
+ } \
+ insp = insp->next; \
+ continue; \
+ } else if(insp->len < 0){ \
+ cp -= insp->len; \
+ insp = insp->next; \
+ continue; \
+ } \
+ } \
+ GF_PUTC((F)->next, *cp); \
+ cp++; \
+ } \
+ while(insp){ \
+ for(l = 0; l < insp->len; l++){ \
+ c = (unsigned char) insp->text[l]; \
+ GF_PUTC((F)->next, c); \
+ } \
+ insp = insp->next; \
+ } \
+ gf_line_test_free_ins(&ins); \
+ } \
+ }
+
+
+
+/*
+ * this simple filter accumulates characters until a newline, offers it
+ * to the provided test function, and then passes it on. It assumes
+ * NVT EOLs.
+ */
+void
+gf_line_test(FILTER_S *f, int flg)
+{
+ register char *p = f->linep;
+ register char *eobuf = GF_LINE_TEST_EOB(f);
+ GF_INIT(f, f->next);
+
+ if(flg == GF_DATA){
+ register unsigned char c;
+ register int state = f->f1;
+
+ while(GF_GETC(f, c)){
+
+ if(state){
+ state = 0;
+ if(c == '\012'){
+ int done;
+
+ GF_LINE_TEST_TEST(f, done);
+
+ p = (f)->line;
+
+ if(done == 2) /* skip this line! */
+ continue;
+
+ GF_PUTC(f->next, '\015');
+ GF_PUTC(f->next, '\012');
+ /*
+ * if the line tester returns TRUE, it's
+ * telling us its seen enough and doesn't
+ * want to see any more. Remove ourself
+ * from the pipeline...
+ */
+ if(done){
+ if(gf_master == f){
+ gf_master = f->next;
+ }
+ else{
+ FILTER_S *fprev;
+
+ for(fprev = gf_master;
+ fprev && fprev->next != f;
+ fprev = fprev->next)
+ ;
+
+ if(fprev) /* wha??? */
+ fprev->next = f->next;
+ else
+ continue;
+ }
+
+ while(GF_GETC(f, c)) /* pass input */
+ GF_PUTC(f->next, c);
+
+ (void) GF_FLUSH(f->next); /* and drain queue */
+ fs_give((void **)&f->line);
+ fs_give((void **)&f); /* wax our data */
+ return;
+ }
+ else
+ continue;
+ }
+ else /* add CR to buffer */
+ GF_LINE_TEST_ADD(f, '\015');
+ } /* fall thru to handle 'c' */
+
+ if(c == '\015') /* newline? */
+ state = 1;
+ else
+ GF_LINE_TEST_ADD(f, c);
+ }
+
+ f->f1 = state;
+ GF_END(f, f->next);
+ }
+ else if(flg == GF_EOD){
+ int i;
+
+ GF_LINE_TEST_TEST(f, i); /* examine remaining data */
+ fs_give((void **) &f->line); /* free line buffer */
+ fs_give((void **) &f->opt); /* free test struct */
+ (void) GF_FLUSH(f->next);
+ (*f->next->f)(f->next, GF_EOD);
+ }
+ else if(flg == GF_RESET){
+ dprint((9, "-- gf_reset line_test\n"));
+ f->f1 = 0; /* state */
+ f->n = 0L; /* line number */
+ f->f2 = LINE_TEST_BLOCK; /* size of alloc'd line */
+ f->line = p = (char *) fs_get(f->f2 * sizeof(char));
+ }
+
+ f->linep = p;
+}
+
+
+/*
+ * function called from the outside to operate on accumulated line.
+ */
+void *
+gf_line_test_opt(linetest_t test_f, void *local)
+{
+ LINETEST_S *ltp;
+
+ ltp = (LINETEST_S *) fs_get(sizeof(LINETEST_S));
+ memset(ltp, 0, sizeof(LINETEST_S));
+ ltp->f = test_f;
+ ltp->local = local;
+ return((void *) ltp);
+}
+
+
+
+LT_INS_S **
+gf_line_test_new_ins(LT_INS_S **ins, char *p, char *s, int n)
+{
+ *ins = (LT_INS_S *) fs_get(sizeof(LT_INS_S));
+ if(((*ins)->len = n) > 0)
+ strncpy((*ins)->text = (char *) fs_get(n * sizeof(char)), s, n);
+ else
+ (*ins)->text = NULL;
+
+ (*ins)->where = p;
+ (*ins)->next = NULL;
+ return(&(*ins)->next);
+}
+
+
+void
+gf_line_test_free_ins(LT_INS_S **ins)
+{
+ if(ins && *ins){
+ if((*ins)->next)
+ gf_line_test_free_ins(&(*ins)->next);
+
+ if((*ins)->text)
+ fs_give((void **) &(*ins)->text);
+
+ fs_give((void **) ins);
+ }
+}
+
+
+/*
+ * PREPEND EDITORIAL FILTER - conditionally prepend output text
+ * with editorial comment
+ */
+
+typedef struct _preped_s {
+ prepedtest_t f;
+ char *text;
+} PREPED_S;
+
+
+/*
+ * gf_prepend_editorial - accumulate filtered text and prepend its
+ * output with given text
+ *
+ *
+ */
+void
+gf_prepend_editorial(FILTER_S *f, int flg)
+{
+ GF_INIT(f, f->next);
+
+ if(flg == GF_DATA){
+ register unsigned char c;
+
+ while(GF_GETC(f, c)){
+ so_writec(c, (STORE_S *) f->data);
+ }
+
+ GF_END(f, f->next);
+ }
+ else if(flg == GF_EOD){
+ unsigned char c;
+
+ if(!((PREPED_S *)(f)->opt)->f || (*((PREPED_S *)(f)->opt)->f)()){
+ char *p = ((PREPED_S *)(f)->opt)->text;
+
+ for( ; p && *p; p++)
+ GF_PUTC(f->next, *p);
+ }
+
+ so_seek((STORE_S *) f->data, 0L, 0);
+ while(so_readc(&c, (STORE_S *) f->data)){
+ GF_PUTC(f->next, c);
+ }
+
+ so_give((STORE_S **) &f->data);
+ fs_give((void **) &f->opt);
+ (void) GF_FLUSH(f->next);
+ (*f->next->f)(f->next, GF_EOD);
+ }
+ else if(flg == GF_RESET){
+ dprint((9, "-- gf_reset line_test\n"));
+ f->data = (void *) so_get(CharStar, NULL, EDIT_ACCESS);
+ }
+}
+
+
+/*
+ * function called from the outside to setup prepending editorial
+ * to output text
+ */
+void *
+gf_prepend_editorial_opt(prepedtest_t test_f, char *text)
+{
+ PREPED_S *pep;
+
+ pep = (PREPED_S *) fs_get(sizeof(PREPED_S));
+ memset(pep, 0, sizeof(PREPED_S));
+ pep->f = test_f;
+ pep->text = text;
+ return((void *) pep);
+}
+
+
+/*
+ * Network virtual terminal to local newline convention filter
+ */
+void
+gf_nvtnl_local(FILTER_S *f, int flg)
+{
+ GF_INIT(f, f->next);
+
+ if(flg == GF_DATA){
+ register unsigned char c;
+ register int state = f->f1;
+
+ while(GF_GETC(f, c)){
+ if(state){
+ state = 0;
+ if(c == '\012'){
+ GF_PUTC(f->next, '\012');
+ continue;
+ }
+ else
+ GF_PUTC(f->next, '\015');
+ /* fall thru to deal with 'c' */
+ }
+
+ if(c == '\015')
+ state = 1;
+ else
+ GF_PUTC(f->next, c);
+ }
+
+ f->f1 = state;
+ GF_END(f, f->next);
+ }
+ else if(flg == GF_EOD){
+ (void) GF_FLUSH(f->next);
+ (*f->next->f)(f->next, GF_EOD);
+ }
+ else if(flg == GF_RESET){
+ dprint((9, "-- gf_reset nvtnl_local\n"));
+ f->f1 = 0;
+ }
+}
+
+
+/*
+ * local to network newline convention filter
+ */
+void
+gf_local_nvtnl(FILTER_S *f, int flg)
+{
+ GF_INIT(f, f->next);
+
+ if(flg == GF_DATA){
+ register unsigned char c;
+
+ while(GF_GETC(f, c)){
+ if(c == '\012'){
+ GF_PUTC(f->next, '\015');
+ GF_PUTC(f->next, '\012');
+ }
+ else
+ GF_PUTC(f->next, c);
+ }
+
+ GF_END(f, f->next);
+ }
+ else if(flg == GF_EOD){
+ (void) GF_FLUSH(f->next);
+ (*f->next->f)(f->next, GF_EOD);
+ }
+ else if(GF_RESET){
+ dprint((9, "-- gf_reset local_nvtnl\n"));
+ /* no op */
+ }
+
+}
diff --git a/pith/filter.h b/pith/filter.h
new file mode 100644
index 00000000..07cfbd7a
--- /dev/null
+++ b/pith/filter.h
@@ -0,0 +1,222 @@
+/*
+ * $Id: filter.h 1169 2008-08-27 06:42:06Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_FILTER_INCLUDED
+#define PITH_FILTER_INCLUDED
+
+
+#include "../pith/filttype.h"
+#include "../pith/handle.h"
+#include "../pith/store.h"
+#include "../pith/osdep/pipe.h"
+
+
+/* gf_html2plain flags */
+#define GFHP_STRIPPED 0x01
+#define GFHP_HANDLES 0x02
+#define GFHP_LOCAL_HANDLES 0x04
+#define GFHP_NO_RELATIVE 0x08
+#define GFHP_RELATED_CONTENT 0x10
+#define GFHP_SHOW_SERVER 0x20
+#define GFHP_HTML 0x40
+#define GFHP_HTML_IMAGES 0x80
+
+
+/* gf_wrap flags */
+#define GFW_NONE 0x000 /* no flags */
+#define GFW_HANDLES 0x001 /* anticpate handle data */
+#define GFW_ONCOMMA 0x002 /* prefer comma wrap to spaces */
+#define GFW_FLOWED 0x004
+#define GFW_FLOW_RESULT 0x008
+#define GFW_DELSP 0x010
+#define GFW_USECOLOR 0x020
+#define GFW_HDRCOLOR 0x040
+#define GFW_FORCOMPOSE 0x080
+#define GFW_SOFTHYPHEN 0x100 /* do something special with soft-hyphens */
+
+
+/* gf_url_hilite flags */
+#define URH_NONE 0x00 /* no flags */
+#define URH_HDRCOLOR 0x01 /* in header, use header base color */
+
+
+#define TAG_EMBED '\377' /* Announces embedded data in text string */
+#define TAG_INVON 'A' /* Supported character attributes */
+#define TAG_INVOFF 'a'
+#define TAG_BOLDON 'B'
+#define TAG_BOLDOFF 'b'
+#define TAG_ULINEON 'C'
+#define TAG_ULINEOFF 'c'
+#define TAG_FGCOLOR 'D' /* Change to this foreground color */
+#define TAG_BGCOLOR 'd' /* Change to this background color */
+#define TAG_ITALICON 'E'
+#define TAG_ITALICOFF 'e'
+#define TAG_STRIKEON 'F'
+#define TAG_STRIKEOFF 'f'
+#define TAG_BIGON 'G'
+#define TAG_BIGOFF 'g'
+#define TAG_SMALLON 'H'
+#define TAG_SMALLOFF 'h'
+#define TAG_HANDLE 'Z' /* indicate's a handle to an action */
+#define TAG_HANDLEOFF 'z' /* indicate's end of handle text */
+
+
+typedef struct url_hilite_s {
+ HANDLE_S **handlesp;
+ int hdr_color;
+} URL_HILITE_S;
+
+typedef struct _rss_item_s {
+ char *title;
+ char *link;
+ char *description;
+ char *source;
+ struct _rss_item_s *next;
+} RSS_ITEM_S;
+
+typedef struct _rss_feed_s {
+ char *title;
+ char *image;
+ char *link;
+ char *description;
+ char *source;
+ int ttl;
+ struct _rss_item_s *items;
+} RSS_FEED_S;
+
+
+/*
+ * This searchs for lines beginning with From<space> so that we can QP-encode
+ * them. It also searches for lines consisting of only a dot. Some mailers
+ * will mangle these lines. The reason it is ifdef'd is because most people
+ * seem to prefer the >From style escape provided by a lot of mail software
+ * to the QP-encoding.
+ * Froms, dots, bmap, and dmap may be any integer type. C is the next char.
+ * bmap and dmap should be initialized to 1.
+ * froms is incremented by 1 whenever a line beginning From_ is found.
+ * dots is incremented by 1 whenever a line with only a dot is found.
+ */
+#define Find_Froms(froms,dots,bmap,dmap,c) { int x,y; \
+ switch (c) { \
+ case '\n': case '\r': \
+ x = 0x1; \
+ y = 0x7; \
+ bmap = 0; \
+ break; \
+ case 'F': \
+ x = 0x3; \
+ y = 0; \
+ break; \
+ case 'r': \
+ x = 0x7; \
+ y = 0; \
+ break; \
+ case 'o': \
+ x = 0xf; \
+ y = 0; \
+ break; \
+ case 'm': \
+ x = 0x1f; \
+ y = 0; \
+ break; \
+ case ' ': \
+ x = 0x3f; \
+ y = 0; \
+ break; \
+ case '.': \
+ x = 0; \
+ y = 0x3; \
+ break; \
+ default: \
+ x = 0; \
+ y = 0; \
+ break; \
+ } \
+ bmap = ((x >> 1) == bmap) ? x : 0; \
+ froms += (bmap == 0x3f ? 1 : 0); \
+ if(y == 0x7 && dmap != 0x3){ \
+ y = 0x1; \
+ dmap = 0; \
+ } \
+ dmap = ((y >> 1) == dmap) ? y : 0; \
+ dots += (dmap == 0x7 ? 1 : 0); \
+ }
+
+
+/* exported protoypes */
+int generic_readc_locale(unsigned char *c,
+ int (*get_a_char)(unsigned char *, void *),
+ void *extraarg,
+ CBUF_S *cb);
+int pc_is_picotext(gf_io_t);
+void gf_set_readc(gf_io_t *, void *, unsigned long, SourceType, int);
+void gf_set_writec(gf_io_t *, void *, unsigned long, SourceType, int);
+void gf_set_so_readc(gf_io_t *, STORE_S *);
+void gf_clear_so_readc(STORE_S *);
+void gf_set_so_writec(gf_io_t *, STORE_S *);
+void gf_clear_so_writec(STORE_S *);
+int gf_puts(char *, gf_io_t);
+int gf_nputs(char *, long, gf_io_t);
+void gf_filter_init(void);
+void gf_link_filter(filter_t, void *);
+void gf_set_terminal(gf_io_t);
+char *gf_pipe(gf_io_t, gf_io_t);
+long gf_bytes_piped(void);
+char *gf_filter(char *, char *, STORE_S *, gf_io_t, FILTLIST_S *, int,
+ void (*)(PIPE_S *, int, void *));
+void gf_binary_b64(FILTER_S *, int);
+void gf_b64_binary(FILTER_S *, int);
+void gf_qp_8bit(FILTER_S *, int);
+void gf_8bit_qp(FILTER_S *, int);
+void gf_convert_8bit_charset(FILTER_S *, int);
+void gf_convert_utf8_charset(FILTER_S *, int);
+void *gf_convert_utf8_charset_opt(void *, int);
+void gf_2022_jp_to_euc(FILTER_S *, int);
+void gf_native8bitjapanese_to_2022_jp(FILTER_S *, int);
+void gf_euc_to_2022_jp(FILTER_S *, int);
+void gf_sjis_to_2022_jp(FILTER_S *, int);
+void gf_utf8(FILTER_S *, int);
+void *gf_utf8_opt(char *);
+void gf_rich2plain(FILTER_S *, int);
+void *gf_rich2plain_opt(int *);
+void gf_enriched2plain(FILTER_S *, int);
+void *gf_enriched2plain_opt(int *);
+void gf_html2plain(FILTER_S *, int);
+void *gf_html2plain_opt(char *, int, int *, HANDLE_S **, htmlrisk_t, int);
+void *gf_html2plain_rss_opt(RSS_FEED_S **, int);
+void gf_html2plain_rss_free(RSS_FEED_S **);
+void gf_html2plain_rss_free_items(RSS_ITEM_S **);
+void gf_escape_filter(FILTER_S *, int);
+void gf_control_filter(FILTER_S *, int);
+void *gf_control_filter_opt(int *);
+void gf_tag_filter(FILTER_S *, int);
+void gf_wrap(FILTER_S *, int);
+void *gf_wrap_filter_opt(int, int, int *, int, int);
+void gf_preflow(FILTER_S *, int);
+void gf_prefix(FILTER_S *, int);
+void *gf_prefix_opt(char *);
+void gf_line_test(FILTER_S *, int);
+void *gf_line_test_opt(linetest_t, void *);
+LT_INS_S **gf_line_test_new_ins(LT_INS_S **, char *, char *, int);
+void gf_line_test_free_ins(LT_INS_S **);
+void gf_prepend_editorial(FILTER_S *, int);
+void *gf_prepend_editorial_opt(prepedtest_t, char *);
+void gf_nvtnl_local(FILTER_S *, int);
+void gf_local_nvtnl(FILTER_S *, int);
+void *gf_url_hilite_opt(URL_HILITE_S *, HANDLE_S **, int);
+
+
+#endif /* PITH_FILTER_INCLUDED */
diff --git a/pith/filttype.h b/pith/filttype.h
new file mode 100644
index 00000000..21a1bec5
--- /dev/null
+++ b/pith/filttype.h
@@ -0,0 +1,75 @@
+/*
+ * $Id: filttype.h 767 2007-10-24 00:03:59Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_FILTTYPE_INCLUDED
+#define PITH_FILTTYPE_INCLUDED
+
+
+/*
+ * Size of generic filter's input/output queue
+ */
+#define GF_MAXBUF 256
+
+
+/*
+ * typedefs of generalized filters used by gf_pipe
+ */
+typedef struct filter_s { /* type to hold data for filter function */
+ void (*f)(struct filter_s *, int);
+ struct filter_s *next; /* next filter to call */
+ long n; /* number of chars seen */
+ short f1; /* flags */
+ int f2; /* second place for flags */
+ unsigned char t; /* temporary char */
+ char *line; /* place for temporary storage */
+ char *linep; /* pointer into storage space */
+ void *opt; /* optional per instance data */
+ void *data; /* misc internal data pointer */
+ unsigned char queue[1 + GF_MAXBUF];
+ short queuein, queueout;
+} FILTER_S;
+
+
+typedef struct filter_insert_s {
+ char *where;
+ char *text;
+ int len;
+ struct filter_insert_s *next;
+} LT_INS_S;
+
+
+typedef int (*gf_io_t)(); /* type of get and put char function */
+typedef void (*filter_t)(FILTER_S *, int);
+typedef int (*linetest_t)(long, char *, LT_INS_S **, void *);
+typedef void (*htmlrisk_t)(void);
+typedef int (*prepedtest_t)(void);
+
+typedef struct filtlist_s {
+ filter_t filter;
+ void *data;
+} FILTLIST_S;
+
+
+typedef struct cbuf_s {
+ unsigned char cbuf[6]; /* used for converting to or from */
+ unsigned char *cbufp; /* locale-specific charset */
+ unsigned char *cbufend;
+} CBUF_S;
+
+
+/* exported protoypes */
+
+
+#endif /* PITH_FILTTYPE_INCLUDED */
diff --git a/pith/flag.c b/pith/flag.c
new file mode 100644
index 00000000..b1bbf9c4
--- /dev/null
+++ b/pith/flag.c
@@ -0,0 +1,758 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: flag.c 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ flag.c
+ Implements Pine message flag management routines
+ ====*/
+
+
+#include "../pith/headers.h"
+#include "../pith/flag.h"
+#include "../pith/pineelt.h"
+#include "../pith/icache.h"
+#include "../pith/mailindx.h"
+#include "../pith/mailcmd.h"
+#include "../pith/msgno.h"
+#include "../pith/thread.h"
+#include "../pith/sort.h"
+#include "../pith/news.h"
+#include "../pith/sequence.h"
+
+
+/*
+ * Internal prototypes
+ */
+void flag_search(MAILSTREAM *, int, MsgNo, MSGNO_S *, long (*)(MAILSTREAM *));
+long flag_search_sequence(MAILSTREAM *, MSGNO_S *, long, int);
+
+
+
+/*----------------------------------------------------------------------
+ Return sequence number based on given index that are search-worthy
+
+ Args: stream --
+ msgmap --
+ msgno --
+ flags -- flags for msgline_hidden
+
+ Result: 0 : index not search-worthy
+ -1 : index out of bounds
+ 1 - stream->nmsgs : sequence number to flag search
+
+ ----*/
+long
+flag_search_sequence(MAILSTREAM *stream, MSGNO_S *msgmap, long int msgno, int flags)
+{
+ long rawno = msgno;
+
+ return((msgno > stream->nmsgs
+ || msgno <= 0
+ || (msgmap && !(rawno = mn_m2raw(msgmap, msgno))))
+ ? -1L /* out of range! */
+ : ((get_lflag(stream, NULL, rawno, MN_EXLD)
+ || (msgmap && msgline_hidden(stream, msgmap, msgno, flags)))
+ ? 0L /* NOT interesting! */
+ : rawno));
+}
+
+
+
+/*----------------------------------------------------------------------
+ Perform mail_search based on flag bits
+
+ Args: stream --
+ flags --
+ start --
+ msgmap --
+
+ Result: if no set_* specified, call mail_search to light the searched
+ bit for all the messages matching the given flags. If set_start
+ specified, it is an index (possibly into set_msgmap) telling
+ us where to search for invalid flag state hence when we
+ return everything with the searched bit is interesting and
+ everything with the valid bit lit is believably valid.
+
+ ----*/
+void
+flag_search(MAILSTREAM *stream, int flags, MsgNo set_start, MSGNO_S *set_msgmap,
+ long int (*ping)(MAILSTREAM *))
+{
+ long n, i, new;
+ char *seq;
+ SEARCHPGM *pgm;
+ SEARCHSET *full_set = NULL, **set;
+ MESSAGECACHE *mc;
+ extern MAILSTREAM *mm_search_stream;
+
+ if(!stream)
+ return;
+
+ new = sp_new_mail_count(stream);
+
+ /* Anything we don't already have flags for? */
+ if(set_start){
+ /*
+ * Use elt's sequence bit to coalesce runs in ascending
+ * sequence order...
+ */
+ for(i = 1L; i <= stream->nmsgs; i++)
+ if((mc = mail_elt(stream, i)) != NULL)
+ mc->sequence = 0;
+
+ for(i = set_start;
+ (n = flag_search_sequence(stream, set_msgmap, i, MH_ANYTHD)) >= 0L;
+ (flags & F_SRCHBACK) ? i-- : i++)
+ if(n > 0L && n <= stream->nmsgs
+ && (mc = mail_elt(stream, n)) && !mc->valid)
+ mc->sequence = 1;
+
+ /* Unroll searchset in ascending sequence order */
+ set = &full_set;
+ for(i = 1L; i <= stream->nmsgs; i++)
+ if((mc = mail_elt(stream, i)) && mc->sequence){
+ if(*set){
+ if(((*set)->last ? (*set)->last : (*set)->first)+1L == i)
+ (*set)->last = i;
+ else
+ set = &(*set)->next;
+ }
+
+ if(!*set){
+ *set = mail_newsearchset();
+ (*set)->first = i;
+ }
+ }
+
+ /*
+ * No search-worthy messsages?, prod the server for
+ * any flag updates and clear the searched bits...
+ */
+ if(full_set){
+ if(full_set->first == 1
+ && full_set->last == stream->nmsgs
+ && full_set->next == NULL)
+ mail_free_searchset(&full_set);
+ }
+ else{
+ for(i = 1L; i <= stream->nmsgs; i++)
+ if((mc = mail_elt(stream, i)) != NULL)
+ mc->searched = 0;
+
+ if(ping)
+ (*ping)(stream); /* prod server for any flag updates */
+
+ if(!(flags & F_NOFILT) && new != sp_new_mail_count(stream)){
+ process_filter_patterns(stream, sp_msgmap(stream),
+ sp_new_mail_count(stream));
+
+ refresh_sort(stream, sp_msgmap(stream), SRT_NON);
+ flag_search(stream, flags, set_start, set_msgmap, ping);
+ }
+
+ return;
+ }
+ }
+
+ if((!is_imap_stream(stream) || modern_imap_stream(stream))
+ && !(IS_NEWS(stream))){
+ pgm = mail_newsearchpgm();
+
+ if(flags & F_SEEN)
+ pgm->seen = 1;
+
+ if(flags & F_UNSEEN)
+ pgm->unseen = 1;
+
+ if(flags & F_DEL)
+ pgm->deleted = 1;
+
+ if(flags & F_UNDEL)
+ pgm->undeleted = 1;
+
+ if(flags & F_ANS)
+ pgm->answered = 1;
+
+ if(flags & F_UNANS)
+ pgm->unanswered = 1;
+
+ if(flags & F_FLAG)
+ pgm->flagged = 1;
+
+ if(flags & F_UNFLAG)
+ pgm->unflagged = 1;
+
+ if(flags & (F_FWD | F_UNFWD)){
+ STRINGLIST **slpp;
+
+ for(slpp = (flags & F_FWD) ? &pgm->keyword : &pgm->unkeyword;
+ *slpp;
+ slpp = &(*slpp)->next)
+ ;
+
+ *slpp = mail_newstringlist();
+ (*slpp)->text.data = (unsigned char *) cpystr(FORWARDED_FLAG);
+ (*slpp)->text.size = (unsigned long) strlen(FORWARDED_FLAG);
+ }
+
+ if(flags & F_RECENT)
+ pgm->recent = 1;
+
+ if(flags & F_OR_SEEN){
+ SEARCHPGM *pgm2 = mail_newsearchpgm();
+ pgm2->or = mail_newsearchor();
+ pgm2->or->first = pgm;
+ pgm2->or->second = mail_newsearchpgm();
+ pgm2->or->second->seen = 1;
+ pgm = pgm2;
+ }
+
+ if(flags & F_OR_UNSEEN){
+ SEARCHPGM *pgm2 = mail_newsearchpgm();
+ pgm2->or = mail_newsearchor();
+ pgm2->or->first = pgm;
+ pgm2->or->second = mail_newsearchpgm();
+ pgm2->or->second->unseen = 1;
+ pgm = pgm2;
+ }
+
+ if(flags & F_OR_DEL){
+ SEARCHPGM *pgm2 = mail_newsearchpgm();
+ pgm2->or = mail_newsearchor();
+ pgm2->or->first = pgm;
+ pgm2->or->second = mail_newsearchpgm();
+ pgm2->or->second->deleted = 1;
+ pgm = pgm2;
+ }
+
+ if(flags & F_OR_UNDEL){
+ SEARCHPGM *pgm2 = mail_newsearchpgm();
+ pgm2->or = mail_newsearchor();
+ pgm2->or->first = pgm;
+ pgm2->or->second = mail_newsearchpgm();
+ pgm2->or->second->undeleted = 1;
+ pgm = pgm2;
+ }
+
+ if(flags & F_OR_FLAG){
+ SEARCHPGM *pgm2 = mail_newsearchpgm();
+ pgm2->or = mail_newsearchor();
+ pgm2->or->first = pgm;
+ pgm2->or->second = mail_newsearchpgm();
+ pgm2->or->second->flagged = 1;
+ pgm = pgm2;
+ }
+
+ if(flags & F_OR_UNFLAG){
+ SEARCHPGM *pgm2 = mail_newsearchpgm();
+ pgm2->or = mail_newsearchor();
+ pgm2->or->first = pgm;
+ pgm2->or->second = mail_newsearchpgm();
+ pgm2->or->second->unflagged = 1;
+ pgm = pgm2;
+ }
+
+ if(flags & F_OR_ANS){
+ SEARCHPGM *pgm2 = mail_newsearchpgm();
+ pgm2->or = mail_newsearchor();
+ pgm2->or->first = pgm;
+ pgm2->or->second = mail_newsearchpgm();
+ pgm2->or->second->answered = 1;
+ pgm = pgm2;
+ }
+
+ if(flags & F_OR_UNANS){
+ SEARCHPGM *pgm2 = mail_newsearchpgm();
+ pgm2->or = mail_newsearchor();
+ pgm2->or->first = pgm;
+ pgm2->or->second = mail_newsearchpgm();
+ pgm2->or->second->unanswered = 1;
+ pgm = pgm2;
+ }
+
+ if(flags & (F_OR_FWD | F_OR_UNFWD)){
+ STRINGLIST **slpp;
+ SEARCHPGM *pgm2 = mail_newsearchpgm();
+ pgm2->or = mail_newsearchor();
+ pgm2->or->first = pgm;
+ pgm2->or->second = mail_newsearchpgm();
+
+ for(slpp = (flags & F_OR_FWD)
+ ? &pgm2->or->second->keyword
+ : &pgm2->or->second->unkeyword;
+ *slpp;
+ slpp = &(*slpp)->next)
+ ;
+
+ *slpp = mail_newstringlist();
+ (*slpp)->text.data = (unsigned char *) cpystr(FORWARDED_FLAG);
+ (*slpp)->text.size = (unsigned long) strlen(FORWARDED_FLAG);
+
+ pgm = pgm2;
+ }
+
+ if(flags & F_OR_RECENT){
+ SEARCHPGM *pgm2 = mail_newsearchpgm();
+ pgm2->or = mail_newsearchor();
+ pgm2->or->first = pgm;
+ pgm2->or->second = mail_newsearchpgm();
+ pgm2->or->second->recent = 1;
+ pgm = pgm2;
+ }
+
+ pgm->msgno = full_set;
+
+ pine_mail_search_full(mm_search_stream = stream, NULL,
+ pgm, SE_NOPREFETCH | SE_FREE);
+
+ if(!(flags & F_NOFILT) && new != sp_new_mail_count(stream)){
+ process_filter_patterns(stream, sp_msgmap(stream),
+ sp_new_mail_count(stream));
+
+ flag_search(stream, flags, set_start, set_msgmap, ping);
+ }
+ }
+ else{
+ if(full_set){
+ /* sequence bits of interesting msgs set */
+ mail_free_searchset(&full_set);
+ }
+ else{
+ /* light sequence bits of interesting msgs */
+ for(i = 1L;
+ (n = flag_search_sequence(stream, set_msgmap, i, MH_ANYTHD)) >= 0L;
+ i++)
+ if(n > 0L && n <= stream->nmsgs
+ && (mc = mail_elt(stream, n)) && !mc->valid)
+ mc->sequence = 1;
+ else
+ mc->sequence = 0;
+ }
+
+ for(i = 1L; i <= stream->nmsgs; i++)
+ if((mc = mail_elt(stream, i)) != NULL)
+ mc->searched = 0;
+
+ if((seq = build_sequence(stream, NULL, NULL)) != NULL){
+ pine_mail_fetch_flags(stream, seq, 0L);
+ fs_give((void **) &seq);
+ }
+ }
+}
+
+
+
+/*----------------------------------------------------------------------
+ count messages on stream with specified system flag attributes
+
+ Args: stream -- The stream/folder to look at message status
+ flags -- flags on folder/stream to examine
+
+ Result: count of messages flagged as requested
+
+ Task: return count of message flagged as requested while being
+ as server/network friendly as possible.
+
+ Strategy: run thru flags to make sure they're all valid. If any
+ invalid, do a search starting with the invalid message.
+ If all valid, ping the server to let it know we're
+ receptive to flag updates. At this
+
+ ----------------------------------------------------------------------*/
+long
+count_flagged(MAILSTREAM *stream, long int flags)
+{
+ long n, count;
+ MESSAGECACHE *mc;
+
+ if(!stream)
+ return(0L);
+
+ flag_search(stream, flags, 1, NULL, pine_mail_ping);
+
+ /* Paw thru once more since all should be updated */
+ for(n = 1L, count = 0L; n <= stream->nmsgs; n++)
+ if((((mc = mail_elt(stream, n)) && mc->searched)
+ || (mc && mc->valid && FLAG_MATCH(flags, mc, stream)))
+ && !get_lflag(stream, NULL, n, MN_EXLD)){
+ mc->searched = 1; /* caller may be interested! */
+ count++;
+ }
+
+ return(count);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Find the first message with the specified flags set
+
+ Args: flags -- Flags in messagecache to match on
+ stream -- The stream/folder to look at message status
+
+ Result: Message number of first message with specified flags set or the
+ number of the last message if none found.
+ ----------------------------------------------------------------------*/
+MsgNo
+first_sorted_flagged(long unsigned int flags, MAILSTREAM *stream, long int set_start, int opts)
+{
+ MsgNo i, n, start_with, winner = 0L;
+ MESSAGECACHE *mc;
+ int last;
+ MSGNO_S *msgmap;
+
+ msgmap = sp_msgmap(stream);
+
+ last = (opts & FSF_LAST);
+
+ /* set_start only affects which search bits we light */
+ start_with = set_start ? set_start
+ : (flags & F_SRCHBACK)
+ ? mn_get_total(msgmap) : 1L;
+ flag_search(stream, flags, start_with, msgmap, NULL);
+
+ for(i = start_with;
+ (n = flag_search_sequence(stream, msgmap, i,
+ (opts & FSF_SKIP_CHID) ? 0 : MH_ANYTHD)) >= 0L;
+ (flags & F_SRCHBACK) ? i-- : i++)
+ if(n > 0L && n <= stream->nmsgs
+ && (((mc = mail_elt(stream, n)) && mc->searched)
+ || (mc && mc->valid && FLAG_MATCH(flags, mc, stream)))){
+ winner = i;
+ if(!last)
+ break;
+ }
+
+ if(winner == 0L && flags != F_UNDEL && flags != F_NONE){
+ dprint((4,
+ "First_sorted_flagged didn't find a winner, look for undeleted\n"));
+ winner = first_sorted_flagged(F_UNDEL, stream, 0L,
+ opts | (mn_get_revsort(msgmap) ? 0 : FSF_LAST));
+ }
+
+ if(winner == 0L && flags != F_NONE){
+ dprint((4,
+ "First_sorted_flagged didn't find an undeleted, look for visible\n"));
+ winner = first_sorted_flagged(F_NONE, stream, 0L,
+ opts | (mn_get_revsort(msgmap) ? 0 : FSF_LAST));
+ }
+
+ dprint((4,
+ "First_sorted_flagged returning winner = %ld\n", winner));
+ return(winner ? winner
+ : (mn_get_revsort(msgmap)
+ ? 1L : mn_get_total(msgmap)));
+}
+
+
+
+/*----------------------------------------------------------------------
+ Find the next message with specified flags set
+
+ Args: flags -- Flags in messagecache to match on
+ stream -- The stream/folder to look at message status
+ start -- Start looking after this message
+ opts -- These bits are both input and output. On input the bit
+ NSF_TRUST_FLAGS tells us whether we need to ping or not.
+ On input, the bit NSF_SEARCH_BACK tells us that we want to
+ know about matches <= start if we don't find any > start.
+ On output, NSF_FLAG_MATCH is set if we matched a message.
+ Returns: Message number of the matched message, if any; else the start # or
+ the max_msgno if the mailbox changed dramatically.
+ ----------------------------------------------------------------------*/
+MsgNo
+next_sorted_flagged(long unsigned int flags, MAILSTREAM *stream, long int start, int *opts)
+{
+ MsgNo i, n, dir;
+ MESSAGECACHE *mc;
+ int rev, fss_flags = 0;
+ MSGNO_S *msgmap;
+
+ msgmap = sp_msgmap(stream);
+
+ /*
+ * Search for the next thing the caller's interested in...
+ */
+
+ fss_flags = (opts && *opts & NSF_SKIP_CHID) ? 0 : MH_ANYTHD;
+ rev = (opts && *opts & NSF_SEARCH_BACK);
+ dir = (rev ? -1L : 1L);
+
+ flag_search(stream, flags | (rev ? F_SRCHBACK : 0), start + dir,
+ msgmap,
+ (opts && ((*opts) & NSF_TRUST_FLAGS)) ? NULL : pine_mail_ping);
+
+ for(i = start + dir;
+ (n = flag_search_sequence(stream, msgmap,
+ i, fss_flags)) >= 0L;
+ i += dir)
+ if(n > 0L && n <= stream->nmsgs
+ && (((mc = mail_elt(stream, n)) && mc->searched)
+ || (mc && mc->valid && FLAG_MATCH(flags, mc, stream)))){
+ /* actually found a msg matching the flags */
+ if(opts)
+ (*opts) |= NSF_FLAG_MATCH;
+
+ return(i);
+ }
+
+
+ return(MIN(start, mn_get_total(msgmap)));
+}
+
+
+
+/*----------------------------------------------------------------------
+ get the requested LOCAL flag bits for the given pine message number
+
+ Accepts: msgs - pointer to message manipulation struct
+ n - message number to get
+ f - bitmap of interesting flags
+ Returns: non-zero if flag set, 0 if not set or no elt (error?)
+
+ NOTE: this can be used to test system flags
+ ----*/
+int
+get_lflag(MAILSTREAM *stream, MSGNO_S *msgs, long int n, int f)
+{
+ MESSAGECACHE *mc;
+ PINELT_S *pelt;
+ unsigned long rawno;
+
+ rawno = msgs ? mn_m2raw(msgs, n) : n;
+ if(!stream || rawno < 1L || rawno > stream->nmsgs)
+ return(0);
+
+ mc = mail_elt(stream, rawno);
+ if(!mc || (pelt = (PINELT_S *) mc->sparep) == NULL)
+ return(f ? 0 : 1);
+
+ return((!f)
+ ? !(pelt->hidden || pelt->excluded || pelt->selected ||
+ pelt->colhid || pelt->collapsed || pelt->searched)
+ : (((f & MN_HIDE) ? pelt->hidden : 0)
+ || ((f & MN_EXLD) ? pelt->excluded : 0)
+ || ((f & MN_SLCT) ? pelt->selected : 0)
+ || ((f & MN_STMP) ? pelt->tmp : 0)
+ || ((f & MN_USOR) ? pelt->unsorted : 0)
+ || ((f & MN_COLL) ? pelt->collapsed : 0)
+ || ((f & MN_CHID) ? pelt->colhid : 0)
+ || ((f & MN_CHID2) ? pelt->colhid2 : 0)
+ || ((f & MN_SRCH) ? pelt->searched : 0)));
+}
+
+
+
+/*----------------------------------------------------------------------
+ set the requested LOCAL flag bits for the given pine message number
+
+ Accepts: msgs - pointer to message manipulation struct
+ n - message number to set
+ f - bitmap of interesting flags
+ v - value (on or off) flag should get
+ Returns: our index number of first
+
+ NOTE: this isn't to be used for setting IMAP system flags
+ ----*/
+int
+set_lflag(MAILSTREAM *stream, MSGNO_S *msgs, long int n, int f, int v)
+{
+ MESSAGECACHE *mc;
+ long rawno = 0L;
+ PINETHRD_S *thrd, *topthrd = NULL;
+ PINELT_S **peltp, *pelt;
+
+ if(n < 1L || n > mn_get_total(msgs))
+ return(0L);
+
+ if((rawno=mn_m2raw(msgs, n)) > 0L && stream && rawno <= stream->nmsgs
+ && (mc = mail_elt(stream, rawno))){
+ int was_invisible, is_invisible;
+ int chk_thrd_cnt = 0, thrd_was_visible, was_hidden, is_hidden;
+
+ if((*(peltp = (PINELT_S **) &mc->sparep) == NULL)){
+ *peltp = (PINELT_S *) fs_get(sizeof(PINELT_S));
+ memset(*peltp, 0, sizeof(PINELT_S));
+ }
+
+ pelt = (*peltp);
+
+ was_invisible = (pelt->hidden || pelt->colhid) ? 1 : 0;
+
+ if((chk_thrd_cnt = ((msgs->visible_threads >= 0L)
+ && THRD_INDX_ENABLED() && (f & MN_HIDE) && (pelt->hidden != v))) != 0){
+ thrd = fetch_thread(stream, rawno);
+ if(thrd && thrd->top){
+ if(thrd->top == thrd->rawno)
+ topthrd = thrd;
+ else
+ topthrd = fetch_thread(stream, thrd->top);
+ }
+
+ if(topthrd){
+ thrd_was_visible = thread_has_some_visible(stream, topthrd);
+ was_hidden = pelt->hidden ? 1 : 0;
+ }
+ }
+
+ if((f & MN_HIDE) && pelt->hidden != v){
+ pelt->hidden = v;
+ msgs->flagged_hid += (v) ? 1L : -1L;
+
+ if(pelt->hidden && THREADING() && !THRD_INDX()
+ && stream == ps_global->mail_stream
+ && ps_global->thread_disp_style == THREAD_MUTTLIKE)
+ clear_index_cache_for_thread(stream, fetch_thread(stream, rawno),
+ sp_msgmap(stream));
+ }
+
+ if((f & MN_CHID) && pelt->colhid != v){
+ pelt->colhid = v;
+ msgs->flagged_chid += (v) ? 1L : -1L;
+ }
+
+ if((f & MN_CHID2) && pelt->colhid2 != v){
+ pelt->colhid2 = v;
+ msgs->flagged_chid2 += (v) ? 1L : -1L;
+ }
+
+ if((f & MN_COLL) && pelt->collapsed != v){
+ pelt->collapsed = v;
+ msgs->flagged_coll += (v) ? 1L : -1L;
+ }
+
+ if((f & MN_USOR) && pelt->unsorted != v){
+ pelt->unsorted = v;
+ msgs->flagged_usor += (v) ? 1L : -1L;
+ }
+
+ if((f & MN_EXLD) && pelt->excluded != v){
+ pelt->excluded = v;
+ msgs->flagged_exld += (v) ? 1L : -1L;
+ }
+
+ if((f & MN_SLCT) && pelt->selected != v){
+ pelt->selected = v;
+ msgs->flagged_tmp += (v) ? 1L : -1L;
+ }
+
+ if((f & MN_SRCH) && pelt->searched != v){
+ pelt->searched = v;
+ msgs->flagged_srch += (v) ? 1L : -1L;
+ }
+
+ if((f & MN_STMP) && pelt->tmp != v){
+ pelt->tmp = v;
+ msgs->flagged_stmp += (v) ? 1L : -1L;
+ }
+
+ is_invisible = (pelt->hidden || pelt->colhid) ? 1 : 0;
+
+ if(was_invisible != is_invisible)
+ msgs->flagged_invisible += (v) ? 1L : -1L;
+
+ /*
+ * visible_threads keeps track of how many of the max_thrdno threads
+ * are visible and how many are MN_HIDE-hidden.
+ */
+ if(chk_thrd_cnt && topthrd
+ && (was_hidden != (is_hidden = pelt->hidden ? 1 : 0))){
+ if(!thrd_was_visible && !is_hidden){
+ /* it is visible now, increase count by one */
+ msgs->visible_threads++;
+ }
+ else if(thrd_was_visible && is_hidden){
+ /* thread may have been hidden, check */
+ if(!thread_has_some_visible(stream, topthrd))
+ msgs->visible_threads--;
+ }
+ /* else no change */
+ }
+ }
+
+ return(1);
+}
+
+
+/*
+ * Copy value of flag from to flag to.
+ */
+void
+copy_lflags(MAILSTREAM *stream, MSGNO_S *msgmap, int from, int to)
+{
+ unsigned long i;
+ int hide;
+
+ hide = ((to == MN_SLCT) && (any_lflagged(msgmap, MN_HIDE) > 0L));
+
+ set_lflags(stream, msgmap, to, 0);
+
+ if(any_lflagged(msgmap, from))
+ for(i = 1L; i <= mn_get_total(msgmap); i++)
+ if(get_lflag(stream, msgmap, i, from))
+ set_lflag(stream, msgmap, i, to, 1);
+ else if(hide)
+ set_lflag(stream, msgmap, i, MN_HIDE, 1);
+}
+
+
+/*
+ * Set flag f to value v in all message.
+ */
+void
+set_lflags(MAILSTREAM *stream, MSGNO_S *msgmap, int f, int v)
+{
+ unsigned long i;
+
+ if((v == 0 && any_lflagged(msgmap, f)) || v )
+ for(i = 1L; i <= mn_get_total(msgmap); i++)
+ set_lflag(stream, msgmap, i, f, v);
+}
+
+
+
+/*----------------------------------------------------------------------
+ return whether the given flag is set somewhere in the folder
+
+ Accepts: msgs - pointer to message manipulation struct
+ f - flag bitmap to act on
+ Returns: number of messages with the given flag set.
+ NOTE: the sum, if multiple flags tested, is bogus
+ ----*/
+long
+any_lflagged(MSGNO_S *msgs, int f)
+{
+ if(!msgs)
+ return(0L);
+
+ if(f == MN_NONE)
+ return(!(msgs->flagged_hid || msgs->flagged_exld || msgs->flagged_tmp ||
+ msgs->flagged_coll || msgs->flagged_chid || msgs->flagged_srch));
+ else if(f == (MN_HIDE | MN_CHID))
+ return(msgs->flagged_invisible); /* special non-bogus case */
+ else
+ return(((f & MN_HIDE) ? msgs->flagged_hid : 0L)
+ + ((f & MN_EXLD) ? msgs->flagged_exld : 0L)
+ + ((f & MN_SLCT) ? msgs->flagged_tmp : 0L)
+ + ((f & MN_SRCH) ? msgs->flagged_srch : 0L)
+ + ((f & MN_STMP) ? msgs->flagged_stmp : 0L)
+ + ((f & MN_COLL) ? msgs->flagged_coll : 0L)
+ + ((f & MN_USOR) ? msgs->flagged_usor : 0L)
+ + ((f & MN_CHID) ? msgs->flagged_chid : 0L)
+ + ((f & MN_CHID2) ? msgs->flagged_chid2 : 0L));
+}
diff --git a/pith/flag.h b/pith/flag.h
new file mode 100644
index 00000000..0e96e116
--- /dev/null
+++ b/pith/flag.h
@@ -0,0 +1,155 @@
+/*
+ * $Id: flag.h 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_FLAG_INCLUDED
+#define PITH_FLAG_INCLUDED
+
+
+#include "../pith/msgno.h"
+
+
+/*
+ * Useful def's to specify interesting flags
+ */
+#define F_NONE 0x00000000
+#define F_SEEN 0x00000001
+#define F_UNSEEN 0x00000002
+#define F_DEL 0x00000004
+#define F_UNDEL 0x00000008
+#define F_FLAG 0x00000010
+#define F_UNFLAG 0x00000020
+#define F_ANS 0x00000040
+#define F_UNANS 0x00000080
+#define F_FWD 0x00000100
+#define F_UNFWD 0x00000200
+#define F_RECENT 0x00000400
+#define F_UNRECENT 0x00000800
+#define F_DRAFT 0x00001000
+#define F_UNDRAFT 0x00002000
+
+#define F_SRCHBACK 0x00004000 /* search backwards instead of forw */
+#define F_NOFILT 0x00008000 /* defer processing filters */
+
+#define F_OR_SEEN 0x00010000
+#define F_OR_UNSEEN 0x00020000
+#define F_OR_DEL 0x00040000
+#define F_OR_UNDEL 0x00080000
+#define F_OR_FLAG 0x00100000
+#define F_OR_UNFLAG 0x00200000
+#define F_OR_ANS 0x00400000
+#define F_OR_UNANS 0x00800000
+#define F_OR_FWD 0x01000000
+#define F_OR_UNFWD 0x02000000
+#define F_OR_RECENT 0x04000000
+#define F_OR_UNRECENT 0x08000000
+
+#define F_KEYWORD 0x10000000
+#define F_UNKEYWORD 0x20000000
+
+#define F_COMMENT 0x40000000
+
+
+/*
+ * Useful flag checking macro
+ */
+#define FLAG_MATCH(F,M,S) (((((F)&F_SEEN) ? (M)->seen \
+ : ((F)&F_UNSEEN) ? !(M)->seen : 1) \
+ && (((F)&F_DEL) ? (M)->deleted \
+ : ((F)&F_UNDEL) ? !(M)->deleted : 1) \
+ && (((F)&F_ANS) ? (M)->answered \
+ : ((F)&F_UNANS) ? !(M)->answered : 1) \
+ && (((F)&F_FWD) ? ((S) && user_flag_is_set((S),(M)->msgno,FORWARDED_FLAG)) \
+ : ((F)&F_UNFWD) ? ((S) && !user_flag_is_set((S),(M)->msgno,FORWARDED_FLAG)) : 1) \
+ && (((F)&F_FLAG) ? (M)->flagged \
+ : ((F)&F_UNFLAG) ? !(M)->flagged : 1) \
+ && (((F)&F_RECENT) ? (M)->recent \
+ : ((F)&F_UNRECENT) ? !(M)->recent : 1)) \
+ || ((((F)&F_OR_SEEN) ? (M)->seen \
+ : ((F)&F_OR_UNSEEN) ? !(M)->seen : 0) \
+ || (((F)&F_OR_DEL) ? (M)->deleted \
+ : ((F)&F_OR_UNDEL) ? !(M)->deleted : 0) \
+ || (((F)&F_OR_ANS) ? (M)->answered \
+ : ((F)&F_OR_UNANS) ? !(M)->answered : 0) \
+ || (((F)&F_OR_FWD) ? ((S) && user_flag_is_set((S),(M)->msgno,FORWARDED_FLAG)) \
+ : ((F)&F_OR_UNFWD) ? !((S) && user_flag_is_set((S),(M)->msgno,FORWARDED_FLAG)) : 0) \
+ || (((F)&F_OR_FLAG)? (M)->flagged \
+ : ((F)&F_OR_UNFLAG) ? !(M)->flagged : 0) \
+ || (((F)&F_OR_RECENT)? (M)->recent \
+ : ((F)&F_OR_UNRECENT) ? !(M)->recent : 0)))
+
+
+/*
+ * These are def's to help manage local, private flags pine uses
+ * to maintain it's mapping table (see MSGNO_S def). The local flags
+ * are actually stored in spare bits in c-client's per-message
+ * MESSAGECACHE struct. But they're private, you ask. Since the flags
+ * are tied to the actual message (independent of the mapping), storing
+ * them in the c-client means we don't have to worry about them during
+ * sorting and such. See {set,get}_lflags for more on local flags.
+ *
+ * MN_HIDE hides messages which are not visible due to Zooming.
+ * MN_EXLD hides messages which have been filtered away.
+ * MN_SLCT marks messages which have been Selected.
+ * MN_SRCH marks messages that are the result of a search
+ * MN_COLL marks a point in the thread tree where the view has been
+ * collapsed, hiding the messages below that point
+ * MN_CHID hides messages which are collapsed out of view
+ * MN_CHID2 is similar to CHID and is introduced for performance reasons.
+ * When using the separate-thread-index the toplevel messages are
+ * MN_COLL and everything else is MN_CHID. However, if we view a
+ * single thread from there, instead of marking all of those top
+ * level threads MN_HIDE (or something) we change the semantics
+ * of the flags. When viewing a single thread we mark the messages
+ * of the thread with MN_CHID2 for performance reasons.
+ */
+#define MN_NONE 0x0000 /* No Pine specific flags */
+#define MN_HIDE 0x0001 /* Pine specific hidden */
+#define MN_EXLD 0x0002 /* Pine specific excluded */
+#define MN_SLCT 0x0004 /* Pine specific selected */
+#define MN_COLL 0x0008 /* Pine specific collapsed */
+#define MN_CHID 0x0010 /* A parent somewhere above us is collapsed */
+#define MN_CHID2 0x0020 /* performance related */
+#define MN_USOR 0x0040 /* New message which hasn't been sorted yet */
+#define MN_STMP 0x0080 /* Temporary storage for a per-message bit */
+#define MN_SRCH 0x0100 /* Search result */
+
+
+/* next_sorted_flagged options */
+#define NSF_TRUST_FLAGS 0x01 /* input flag, don't need to ping */
+#define NSF_SKIP_CHID 0x02 /* input flag, skip MN_CHID messages */
+#define NSF_SEARCH_BACK 0x04 /* input flag, search backward if none forw */
+#define NSF_FLAG_MATCH 0x08 /* return flag, actually got a match */
+
+/* first_sorted_flagged options */
+#define FSF_LAST 0x01 /* last instead of first */
+#define FSF_SKIP_CHID 0x02 /* skip MN_CHID messages */
+
+
+typedef long MsgNo;
+
+
+/* exported protoypes */
+long count_flagged(MAILSTREAM *, long);
+MsgNo first_sorted_flagged(unsigned long, MAILSTREAM *, long, int);
+MsgNo next_sorted_flagged(unsigned long, MAILSTREAM *, long, int *);
+int get_lflag(MAILSTREAM *, MSGNO_S *, long, int);
+int set_lflag(MAILSTREAM *, MSGNO_S *, long, int, int);
+void copy_lflags(MAILSTREAM *, MSGNO_S *, int, int);
+void set_lflags(MAILSTREAM *, MSGNO_S *, int, int);
+long any_lflagged(MSGNO_S *, int);
+
+
+#endif /* PITH_FLAG_INCLUDED */
diff --git a/pith/folder.c b/pith/folder.c
new file mode 100644
index 00000000..d07604a0
--- /dev/null
+++ b/pith/folder.c
@@ -0,0 +1,2759 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: folder.c 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../c-client/utf8aux.h"
+#include "../pith/folder.h"
+#include "../pith/state.h"
+#include "../pith/context.h"
+#include "../pith/init.h"
+#include "../pith/conf.h"
+#include "../pith/stream.h"
+#include "../pith/imap.h"
+#include "../pith/util.h"
+#include "../pith/flag.h"
+#include "../pith/status.h"
+#include "../pith/busy.h"
+#include "../pith/mailindx.h"
+
+
+typedef struct _build_folder_list_data {
+ long mask; /* bitmap of responses to ignore */
+ LISTARGS_S args;
+ LISTRES_S response;
+ int is_move_folder;
+ FLIST *list;
+} BFL_DATA_S;
+
+
+#define FCHUNK 64
+
+
+/*
+ * Internal prototypes
+ */
+void mail_list_exists(MAILSTREAM *, char *, int, long, void *, unsigned);
+void init_incoming_folder_list(struct pine *, CONTEXT_S *);
+void mail_list_filter(MAILSTREAM *, char *, int, long, void *, unsigned);
+void mail_lsub_filter(MAILSTREAM *, char *, int, long, void *, unsigned);
+int mail_list_in_collection(char **, char *, char *, char *);
+char *folder_last_cmpnt(char *, int);
+void free_folder_entries(FLIST **);
+int folder_insert_sorted(int, int, int, FOLDER_S *, FLIST *,
+ int (*)(FOLDER_S *, FOLDER_S *));
+void folder_insert_index(FOLDER_S *, int, FLIST *);
+void resort_folder_list(FLIST *flist);
+int compare_folders_alpha_qsort(const qsort_t *a1, const qsort_t *a2);
+int compare_folders_dir_alpha_qsort(const qsort_t *a1, const qsort_t *a2);
+int compare_folders_alpha_dir_qsort(const qsort_t *a1, const qsort_t *a2);
+int compare_names(const qsort_t *, const qsort_t *);
+void init_incoming_unseen_data(struct pine *, FOLDER_S *f);
+
+
+char *
+folder_lister_desc(CONTEXT_S *cntxt, FDIR_S *fdp)
+{
+ char *p, *q;
+ unsigned char *fname;
+
+ q = ((p = strstr(cntxt->context, "%s")) && !*(p+2)
+ && !strncmp(fdp->ref, cntxt->context, p - cntxt->context))
+ ? fdp->ref + (p - cntxt->context) : fdp->ref;
+ fname = folder_name_decoded((unsigned char *) q);
+ /* Provide context in new collection header */
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Dir: %s", fname ? (char *) fname : q);
+ if(fname) fs_give((void **)&fname);
+
+ return(cpystr(tmp_20k_buf));
+}
+
+
+void
+reset_context_folders(CONTEXT_S *cntxt)
+{
+ CONTEXT_S *tc;
+
+ for(tc = cntxt; tc && tc->prev ; tc = tc->prev)
+ ; /* start at beginning */
+
+ for( ; tc ; tc = tc->next){
+ free_folder_list(tc);
+
+ while(tc->dir->prev){
+ FDIR_S *tp = tc->dir->prev;
+ free_fdir(&tc->dir, 0);
+ tc->dir = tp;
+ }
+ }
+}
+
+
+/*
+ * next_folder_dir - return a directory structure with the folders it
+ * contains
+ */
+FDIR_S *
+next_folder_dir(CONTEXT_S *context, char *new_dir, int build_list, MAILSTREAM **streamp)
+{
+ char tmp[MAILTMPLEN], dir[3];
+ FDIR_S *tmp_fp, *fp;
+
+ fp = (FDIR_S *) fs_get(sizeof(FDIR_S));
+ memset(fp, 0, sizeof(FDIR_S));
+ (void) context_apply(tmp, context, new_dir, MAILTMPLEN);
+ dir[0] = context->dir->delim;
+ dir[1] = '\0';
+ strncat(tmp, dir, sizeof(tmp)-1-strlen(tmp));
+ fp->ref = cpystr(tmp);
+ fp->delim = context->dir->delim;
+ fp->view.internal = cpystr(NEWS_TEST(context) ? "*" : "%");
+ fp->folders = init_folder_entries();
+ fp->status = CNTXT_NOFIND;
+ tmp_fp = context->dir; /* temporarily rebind */
+ context->dir = fp;
+
+ if(build_list)
+ build_folder_list(streamp, context, NULL, NULL,
+ NEWS_TEST(context) ? BFL_LSUB : BFL_NONE);
+
+ context->dir = tmp_fp;
+ return(fp);
+}
+
+
+/*
+ * Return which pinerc incoming folder #index is in.
+ */
+EditWhich
+config_containing_inc_fldr(FOLDER_S *folder)
+{
+ char **t;
+ int i, keep_going = 1, inheriting = 0;
+ struct variable *v = &ps_global->vars[V_INCOMING_FOLDERS];
+
+ if(v->is_fixed)
+ return(None);
+
+ /* is it in exceptions config? */
+ if(v->post_user_val.l){
+ for(i = 0, t=v->post_user_val.l; t[i]; i++){
+ if(expand_variables(tmp_20k_buf, SIZEOF_20KBUF, t[i], 0)){
+ keep_going = 0;
+
+ if(!strcmp(tmp_20k_buf, INHERIT))
+ inheriting = 1;
+ else if(folder->varhash == line_hash(tmp_20k_buf))
+ return(Post);
+ }
+ }
+ }
+
+ if(inheriting)
+ keep_going = 1;
+
+ /* is it in main config? */
+ if(keep_going && v->main_user_val.l){
+ for(i = 0, t=v->main_user_val.l; t[i]; i++){
+ if(expand_variables(tmp_20k_buf, SIZEOF_20KBUF, t[i], 0) &&
+ folder->varhash == line_hash(tmp_20k_buf))
+ return(Main);
+ }
+ }
+
+ return(None);
+}
+
+
+/*----------------------------------------------------------------------
+ Format the given folder name for display for the user
+
+ Args: folder -- The folder name to fix up
+
+Not sure this always makes it prettier. It could do nice truncation if we
+passed in a length. Right now it adds the path name of the mail
+subdirectory if appropriate.
+ ----*/
+
+char *
+pretty_fn(char *folder)
+{
+ if(!strucmp(folder, ps_global->inbox_name))
+ return(ps_global->inbox_name);
+ else
+ return(folder);
+}
+
+
+/*----------------------------------------------------------------------
+ Return the path delimiter for the given folder on the given server
+
+ Args: folder -- folder type for delimiter
+
+ ----*/
+int
+get_folder_delimiter(char *folder)
+{
+ MM_LIST_S ldata;
+ LISTRES_S response;
+ int ourstream = 0;
+
+ memset(mm_list_info = &ldata, 0, sizeof(MM_LIST_S));
+ ldata.filter = mail_list_response;
+ memset(ldata.data = &response, 0, sizeof(LISTRES_S));
+
+ if(*folder == '{'
+ && !(ldata.stream = sp_stream_get(folder, SP_MATCH))
+ && !(ldata.stream = sp_stream_get(folder, SP_SAME))){
+ if((ldata.stream = pine_mail_open(NULL,folder,
+ OP_HALFOPEN|OP_SILENT|SP_USEPOOL|SP_TEMPUSE,
+ NULL)) != NULL){
+ ourstream++;
+ }
+ else{
+ return(FEX_ERROR);
+ }
+ }
+
+ pine_mail_list(ldata.stream, folder, "", NULL);
+
+ if(ourstream)
+ pine_mail_close(ldata.stream);
+
+ return(response.delim);
+}
+
+
+/*----------------------------------------------------------------------
+ Check to see if folder exists in given context
+
+ Args: cntxt -- context inwhich to interpret "file" arg
+ file -- name of folder to check
+
+ Result: returns FEX_ISFILE if the folder exists and is a folder
+ FEX_ISDIR if the folder exists and is a directory
+ FEX_NOENT if it doesn't exist
+ FEX_ERROR on error
+
+ The two existence return values above may be logically OR'd
+
+ Uses mail_list to sniff out the existence of the requested folder.
+ The context string is just here for convenience. Checking for
+ folder's existence within a given context is probably more efficiently
+ handled outside this function for now using build_folder_list().
+
+ ----*/
+int
+folder_exists(CONTEXT_S *cntxt, char *file)
+{
+ return(folder_name_exists(cntxt, file, NULL));
+}
+
+
+/*----------------------------------------------------------------------
+ Check to see if folder exists in given context
+
+ Args: cntxt -- context in which to interpret "file" arg
+ file -- name of folder to check
+ name -- name of folder folder with context applied
+
+ Result: returns FEX_ISFILE if the folder exists and is a folder
+ FEX_ISDIR if the folder exists and is a directory
+ FEX_NOENT if it doesn't exist
+ FEX_ERROR on error
+
+ The two existence return values above may be logically OR'd
+
+ Uses mail_list to sniff out the existence of the requested folder.
+ The context string is just here for convenience. Checking for
+ folder's existence within a given context is probably more efficiently
+ handled outside this function for now using build_folder_list().
+
+ ----*/
+int
+folder_name_exists(CONTEXT_S *cntxt, char *file, char **fullpath)
+{
+ MM_LIST_S ldata;
+ EXISTDATA_S parms;
+ int we_cancel = 0, res;
+ char *p, reference[MAILTMPLEN], tmp[MAILTMPLEN], *tfolder = NULL;
+
+ /*
+ * No folder means "inbox".
+ */
+ if(*file == '{' && (p = strchr(file, '}')) && (!*(p+1))){
+ size_t l;
+
+ l = strlen(file)+strlen("inbox");
+ tfolder = (char *) fs_get((l+1) * sizeof(char));
+ snprintf(tfolder, l+1, "%s%s", file, "inbox");
+ file = tfolder;
+ cntxt = NULL;
+ }
+
+ mm_list_info = &ldata; /* tie down global reference */
+ memset(&ldata, 0, sizeof(ldata));
+ ldata.filter = mail_list_exists;
+
+ ldata.stream = sp_stream_get(context_apply(tmp, cntxt, file, sizeof(tmp)),
+ SP_SAME);
+
+ memset(ldata.data = &parms, 0, sizeof(EXISTDATA_S));
+
+ /*
+ * If no preset reference string, must be at top of context
+ */
+ if(cntxt && context_isambig(file)){
+ /* inbox in first context is the real inbox */
+ if(ps_global->context_list == cntxt && !strucmp(file, ps_global->inbox_name)){
+ reference[0] = '\0';
+ parms.args.reference = reference;
+ }
+ else if(!(parms.args.reference = cntxt->dir->ref)){
+ char *p;
+
+ if((p = strstr(cntxt->context, "%s")) != NULL){
+ strncpy(parms.args.reference = reference,
+ cntxt->context,
+ MIN(p - cntxt->context, sizeof(reference)-1));
+ reference[MIN(p - cntxt->context, sizeof(reference)-1)] = '\0';
+ if(*(p += 2))
+ parms.args.tail = p;
+ }
+ else
+ parms.args.reference = cntxt->context;
+ }
+
+ parms.fullname = fullpath;
+ }
+
+ ps_global->mm_log_error = 0;
+ ps_global->noshow_error = 1;
+
+ we_cancel = busy_cue(NULL, NULL, 1);
+
+ parms.args.name = file;
+
+ res = pine_mail_list(ldata.stream, parms.args.reference, parms.args.name,
+ &ldata.options);
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ ps_global->noshow_error = 0;
+
+ if(cntxt && cntxt->dir && parms.response.delim)
+ cntxt->dir->delim = parms.response.delim;
+
+ if(tfolder)
+ fs_give((void **)&tfolder);
+ return(((res == FALSE) || ps_global->mm_log_error)
+ ? FEX_ERROR
+ : (((parms.response.isfile)
+ ? FEX_ISFILE : 0 )
+ | ((parms.response.isdir)
+ ? FEX_ISDIR : 0)
+ | ((parms.response.ismarked)
+ ? FEX_ISMARKED : 0)
+ | ((parms.response.unmarked)
+ ? FEX_UNMARKED : 0)));
+}
+
+
+void
+mail_list_exists(MAILSTREAM *stream, char *mailbox, int delim, long int attribs,
+ void *data, unsigned int options)
+{
+ if(delim)
+ ((EXISTDATA_S *) data)->response.delim = delim;
+
+ if(mailbox && *mailbox){
+ if(!(attribs & LATT_NOSELECT)){
+ ((EXISTDATA_S *) data)->response.isfile = 1;
+ ((EXISTDATA_S *) data)->response.count += 1;
+ }
+
+ if(!(attribs & LATT_NOINFERIORS)){
+ ((EXISTDATA_S *) data)->response.isdir = 1;
+ ((EXISTDATA_S *) data)->response.count += 1;
+ }
+
+ if(attribs & LATT_MARKED)
+ ((EXISTDATA_S *) data)->response.ismarked = 1;
+
+ /* don't mark #move folders unmarked */
+ if(attribs & LATT_UNMARKED && !(options & PML_IS_MOVE_MBOX))
+ ((EXISTDATA_S *) data)->response.unmarked = 1;
+
+ if(attribs & LATT_HASCHILDREN)
+ ((EXISTDATA_S *) data)->response.haschildren = 1;
+
+ if(attribs & LATT_HASNOCHILDREN)
+ ((EXISTDATA_S *) data)->response.hasnochildren = 1;
+
+ if(((EXISTDATA_S *) data)->fullname
+ && ((((EXISTDATA_S *) data)->args.reference[0] != '\0')
+ ? struncmp(((EXISTDATA_S *) data)->args.reference, mailbox,
+ strlen(((EXISTDATA_S *)data)->args.reference))
+ : struncmp(((EXISTDATA_S *) data)->args.name, mailbox,
+ strlen(((EXISTDATA_S *) data)->args.name)))){
+ char *p;
+ size_t alloclen;
+ size_t len = (((stream && stream->mailbox)
+ ? strlen(stream->mailbox) : 0)
+ + strlen(((EXISTDATA_S *) data)->args.reference)
+ + strlen(((EXISTDATA_S *) data)->args.name)
+ + strlen(mailbox)) * sizeof(char);
+
+ /*
+ * Fully qualify (in the c-client name structure sense)
+ * anything that's not in the context of the "reference"...
+ */
+ if(*((EXISTDATA_S *) data)->fullname)
+ fs_give((void **) ((EXISTDATA_S *) data)->fullname);
+
+ alloclen = len;
+ *((EXISTDATA_S *) data)->fullname = (char *) fs_get(alloclen);
+ if(*mailbox != '{'
+ && stream && stream->mailbox && *stream->mailbox == '{'
+ && (p = strindex(stream->mailbox, '}'))){
+ len = (p - stream->mailbox) + 1;
+ strncpy(*((EXISTDATA_S *) data)->fullname,
+ stream->mailbox, MIN(len,alloclen));
+ p = *((EXISTDATA_S *) data)->fullname + len;
+ }
+ else
+ p = *((EXISTDATA_S *) data)->fullname;
+
+ strncpy(p, mailbox, alloclen-(p-(*((EXISTDATA_S *) data)->fullname)));
+ (*((EXISTDATA_S *) data)->fullname)[alloclen-1] = '\0';;
+ }
+ }
+}
+
+
+void
+mail_list_response(MAILSTREAM *stream, char *mailbox, int delim, long int attribs,
+ void *data, unsigned int options)
+{
+ int counted = 0;
+
+ if(delim)
+ ((LISTRES_S *) data)->delim = delim;
+
+ if(mailbox && *mailbox){
+ if(!(attribs & LATT_NOSELECT)){
+ counted++;
+ ((LISTRES_S *) data)->isfile = 1;
+ ((LISTRES_S *) data)->count += 1;
+ }
+
+ if(!(attribs & LATT_NOINFERIORS)){
+ ((LISTRES_S *) data)->isdir = 1;
+
+ if(!counted)
+ ((LISTRES_S *) data)->count += 1;
+ }
+
+ if(attribs & LATT_HASCHILDREN)
+ ((LISTRES_S *) data)->haschildren = 1;
+
+ if(attribs & LATT_HASNOCHILDREN)
+ ((LISTRES_S *) data)->hasnochildren = 1;
+ }
+}
+
+
+char *
+folder_as_breakout(CONTEXT_S *cntxt, char *name)
+{
+ if(context_isambig(name)){ /* if simple check doesn't pan out */
+ char tmp[2*MAILTMPLEN], *p, *f; /* look harder */
+
+ if(!cntxt->dir->delim){
+ (void) context_apply(tmp, cntxt, "", sizeof(tmp)/2);
+ cntxt->dir->delim = get_folder_delimiter(tmp);
+ }
+
+ if((p = strindex(name, cntxt->dir->delim)) != NULL){
+ if(p == name){ /* assumption 6,321: delim is root */
+ if(cntxt->context[0] == '{'
+ && (p = strindex(cntxt->context, '}'))){
+ strncpy(tmp, cntxt->context,
+ MIN((p - cntxt->context) + 1, sizeof(tmp)/2));
+ tmp[MIN((p - cntxt->context) + 1, sizeof(tmp)/2)] = '\0';
+ strncpy(&tmp[MIN((p - cntxt->context) + 1, sizeof(tmp)/2)],
+ name, sizeof(tmp)/2-strlen(tmp));
+ tmp[sizeof(tmp)-1] = '\0';
+ return(cpystr(tmp));
+ }
+ else
+ return(cpystr(name));
+ }
+ else{ /* assumption 6,322: no create ~foo */
+ strncpy(tmp, name, MIN(p - name, MAILTMPLEN));
+ /* lop off trailingpath */
+ tmp[MIN(p - name, sizeof(tmp)/2)] = '\0';
+ f = NULL;
+ (void)folder_name_exists(cntxt, tmp, &f);
+ if(f){
+ snprintf(tmp, sizeof(tmp), "%s%s",f,p);
+ tmp[sizeof(tmp)-1] = '\0';
+ fs_give((void **) &f);
+ return(cpystr(tmp));
+ }
+ }
+ }
+ }
+
+ return(NULL);
+}
+
+
+/*----------------------------------------------------------------------
+ Initialize global list of contexts for folder collections.
+
+ Interprets collections defined in the pinerc and orders them for
+ pine's use. Parses user-provided context labels and sets appropriate
+ use flags and the default prototype for that collection.
+ (See find_folders for how the actual folder list is found).
+
+ ----*/
+void
+init_folders(struct pine *ps)
+{
+ CONTEXT_S *tc, *top = NULL, **clist;
+ int i, prime = 0;
+
+ clist = &top;
+
+ /*
+ * If no incoming folders are config'd, but the user asked for
+ * them via feature, make sure at least "inbox" ends up there...
+ */
+ if(F_ON(F_ENABLE_INCOMING, ps) && !ps->VAR_INCOMING_FOLDERS){
+ ps->VAR_INCOMING_FOLDERS = (char **)fs_get(2 * sizeof(char *));
+ ps->VAR_INCOMING_FOLDERS[0] = cpystr(ps->inbox_name);
+ ps->VAR_INCOMING_FOLDERS[1] = NULL;
+ }
+
+ /*
+ * Build context that's a list of folders the user's defined
+ * as receiveing new messages. At some point, this should
+ * probably include adding a prefix with the new message count.
+ * fake new context...
+ */
+ if(ps->VAR_INCOMING_FOLDERS && ps->VAR_INCOMING_FOLDERS[0]
+ && (tc = new_context("Incoming-Folders []", NULL))){
+ tc->dir->status &= ~CNTXT_NOFIND;
+ tc->use |= CNTXT_INCMNG; /* mark this as incoming collection */
+ if(tc->label)
+ fs_give((void **) &tc->label);
+
+ /* TRANSLATORS: a label */
+ tc->label = cpystr(_("Incoming Message Folders"));
+
+ *clist = tc;
+ clist = &tc->next;
+
+ init_incoming_folder_list(ps, tc);
+ }
+
+ /*
+ * Build list of folder collections. Because of the way init.c
+ * works, we're guaranteed at least a default. Also write any
+ * "bogus format" messages...
+ */
+ for(i = 0; ps->VAR_FOLDER_SPEC && ps->VAR_FOLDER_SPEC[i] ; i++)
+ if((tc = new_context(ps->VAR_FOLDER_SPEC[i], &prime)) != NULL){
+ *clist = tc; /* add it to list */
+ clist = &tc->next; /* prepare for next */
+ tc->var.v = &ps->vars[V_FOLDER_SPEC];
+ tc->var.i = i;
+ }
+
+
+ /*
+ * Whoah cowboy!!! Guess we couldn't find a valid folder
+ * collection???
+ */
+ if(!prime)
+ panic(_("No folder collections defined"));
+
+ /*
+ * At this point, insert the INBOX mapping as the leading
+ * folder entry of the first collection...
+ */
+ init_inbox_mapping(ps->VAR_INBOX_PATH, top);
+
+ set_news_spec_current_val(TRUE, TRUE);
+
+ /*
+ * If news groups, loop thru list adding to collection list
+ */
+ for(i = 0; ps->VAR_NEWS_SPEC && ps->VAR_NEWS_SPEC[i] ; i++)
+ if(ps->VAR_NEWS_SPEC[i][0]
+ && (tc = new_context(ps->VAR_NEWS_SPEC[i], NULL))){
+ *clist = tc; /* add it to list */
+ clist = &tc->next; /* prepare for next */
+ tc->var.v = &ps->vars[V_NEWS_SPEC];
+ tc->var.i = i;
+ }
+
+ ps->context_list = top; /* init pointers */
+ ps->context_current = (top->use & CNTXT_INCMNG) ? top->next : top;
+ ps->context_last = NULL;
+ /* Tie up all the previous pointers */
+ for(; top; top = top->next)
+ if(top->next)
+ top->next->prev = top;
+
+#ifdef DEBUG
+ dump_contexts();
+#endif
+}
+
+
+/*
+ * Add incoming list of folders to context.
+ */
+void
+init_incoming_folder_list(struct pine *ps, CONTEXT_S *cntxt)
+{
+ int i;
+ char *folder_string, *nickname;
+ FOLDER_S *f;
+
+ for(i = 0; ps->VAR_INCOMING_FOLDERS[i] ; i++){
+ /*
+ * Parse folder line for nickname and folder name.
+ * No nickname on line is OK.
+ */
+ get_pair(ps->VAR_INCOMING_FOLDERS[i], &nickname, &folder_string,0,0);
+
+ /*
+ * Allow for inbox to be specified in the incoming list, but
+ * don't let it show up along side the one magically inserted
+ * above!
+ */
+ if(!folder_string || !strucmp(ps->inbox_name, folder_string)){
+ if(folder_string)
+ fs_give((void **)&folder_string);
+
+ if(nickname)
+ fs_give((void **)&nickname);
+
+ continue;
+ }
+ else if(update_bboard_spec(folder_string, tmp_20k_buf, SIZEOF_20KBUF)){
+ fs_give((void **) &folder_string);
+ folder_string = cpystr(tmp_20k_buf);
+ }
+
+ f = new_folder(folder_string,
+ line_hash(ps->VAR_INCOMING_FOLDERS[i]));
+ f->isfolder = 1;
+ fs_give((void **)&folder_string);
+
+ if(nickname){
+ if(strucmp(ps->inbox_name, nickname)){
+ f->nickname = nickname;
+ f->name_len = strlen(f->nickname);
+ }
+ else
+ fs_give((void **)&nickname);
+ }
+
+ init_incoming_unseen_data(ps, f);
+
+ folder_insert(f->nickname
+ && (strucmp(f->nickname, ps->inbox_name) == 0)
+ ? -1 : folder_total(FOLDERS(cntxt)),
+ f, FOLDERS(cntxt));
+ }
+}
+
+
+void
+init_incoming_unseen_data(struct pine *ps, FOLDER_S *f)
+{
+ int j, check_this = 0;
+
+ if(!f)
+ return;
+
+ /* see if this folder is in the monitoring list */
+ if(F_ON(F_ENABLE_INCOMING_CHECKING, ps)){
+ if(!ps->VAR_INCCHECKLIST)
+ check_this++; /* everything in by default */
+ else{
+ for(j = 0; !check_this && ps->VAR_INCCHECKLIST[j]; j++){
+ if((f->nickname && !strucmp(ps->VAR_INCCHECKLIST[j],f->nickname))
+ || (f->name && !strucmp(ps->VAR_INCCHECKLIST[j],f->name)))
+ check_this++;
+ }
+ }
+ }
+
+ if(check_this)
+ f->last_unseen_update = LUU_INIT;
+ else
+ f->last_unseen_update = LUU_NEVERCHK;
+}
+
+
+void
+reinit_incoming_folder_list(struct pine *ps, CONTEXT_S *context)
+{
+ free_folder_entries(&(FOLDERS(context)));
+ FOLDERS(context) = init_folder_entries();
+ init_incoming_folder_list(ps_global, context);
+ init_inbox_mapping(ps_global->VAR_INBOX_PATH, context);
+}
+
+
+/*
+ *
+ */
+void
+init_inbox_mapping(char *path, CONTEXT_S *cntxt)
+{
+ FOLDER_S *f;
+ int check_this, j;
+
+ /*
+ * If mapping already exists, blast it and replace it below...
+ */
+ if((f = folder_entry(0, FOLDERS(cntxt)))
+ && f->nickname && !strcmp(f->nickname, ps_global->inbox_name))
+ folder_delete(0, FOLDERS(cntxt));
+
+ if(path){
+ f = new_folder(path, 0);
+ f->nickname = cpystr(ps_global->inbox_name);
+ f->name_len = strlen(f->nickname);
+ }
+ else
+ f = new_folder(ps_global->inbox_name, 0);
+
+ f->isfolder = 1;
+
+ /* see if this folder is in the monitoring list */
+ check_this = 0;
+ if(F_ON(F_ENABLE_INCOMING_CHECKING, ps_global) && ps_global->VAR_INCOMING_FOLDERS && ps_global->VAR_INCOMING_FOLDERS[0]){
+ if(!ps_global->VAR_INCCHECKLIST)
+ check_this++; /* everything in by default */
+ else{
+ for(j = 0; !check_this && ps_global->VAR_INCCHECKLIST[j]; j++){
+ if((f->nickname && !strucmp(ps_global->VAR_INCCHECKLIST[j],f->nickname))
+ || (f->name && !strucmp(ps_global->VAR_INCCHECKLIST[j],f->name)))
+ check_this++;
+ }
+ }
+ }
+
+ if(check_this)
+ f->last_unseen_update = LUU_INIT;
+ else
+ f->last_unseen_update = LUU_NEVERCHK;
+
+ folder_insert(0, f, FOLDERS(cntxt));
+}
+
+
+FDIR_S *
+new_fdir(char *ref, char *view, int wildcard)
+{
+ FDIR_S *rv = (FDIR_S *) fs_get(sizeof(FDIR_S));
+
+ memset((void *) rv, 0, sizeof(FDIR_S));
+
+ /* Monkey with the view to make sure it has wildcard? */
+ if(view && *view){
+ rv->view.user = cpystr(view);
+
+ /*
+ * This is sorta hairy since, for simplicity we allow
+ * users to use '*' in the view, but for mail
+ * we really mean '%' as def'd in 2060...
+ */
+ if(wildcard == '*'){ /* must be #news. */
+ if(strindex(view, wildcard))
+ rv->view.internal = cpystr(view);
+ }
+ else{ /* must be mail */
+ char *p;
+
+ if((p = strpbrk(view, "*%")) != NULL){
+ rv->view.internal = p = cpystr(view);
+ while((p = strpbrk(p, "*%")) != NULL)
+ *p++ = '%'; /* convert everything to '%' */
+ }
+ }
+
+ if(!rv->view.internal){
+ size_t l;
+
+ l = strlen(view)+2;
+ rv->view.internal = (char *) fs_get((l+1) * sizeof(char));
+ snprintf(rv->view.internal, l+1, "%c%s%c", wildcard, view, wildcard);
+ }
+ }
+ else{
+ rv->view.internal = (char *) fs_get(2 * sizeof(char));
+ snprintf(rv->view.internal, 2, "%c", wildcard);
+ }
+
+ if(ref)
+ rv->ref = ref;
+
+ rv->folders = init_folder_entries();
+ rv->status = CNTXT_NOFIND;
+ return(rv);
+}
+
+
+void
+free_fdir(FDIR_S **f, int recur)
+{
+ if(f && *f){
+ if((*f)->prev && recur)
+ free_fdir(&(*f)->prev, 1);
+
+ if((*f)->ref)
+ fs_give((void **)&(*f)->ref);
+
+ if((*f)->view.user)
+ fs_give((void **)&(*f)->view.user);
+
+ if((*f)->view.internal)
+ fs_give((void **)&(*f)->view.internal);
+
+ if((*f)->desc)
+ fs_give((void **)&(*f)->desc);
+
+ free_folder_entries(&(*f)->folders);
+ fs_give((void **) f);
+ }
+}
+
+
+int
+update_bboard_spec(char *bboard, char *buf, size_t buflen)
+{
+ char *p = NULL, *origbuf;
+ int nntp = 0, bracket = 0;
+
+ origbuf = buf;
+
+ if(*bboard == '*'
+ || (*bboard == '{' && (p = strindex(bboard, '}')) && *(p+1) == '*')){
+ /* server name ? */
+ if(p || (*(bboard+1) == '{' && (p = strindex(++bboard, '}'))))
+ while(bboard <= p && (buf-origbuf < buflen)) /* copy it */
+ if((*buf++ = *bboard++) == '/' && !strncmp(bboard, "nntp", 4))
+ nntp++;
+
+ if(*bboard == '*')
+ bboard++;
+
+ if(!nntp)
+ /*
+ * See if path portion looks newsgroup-ish while being aware
+ * of the "view" portion of the spec...
+ */
+ for(p = bboard; *p; p++)
+ if(*p == '['){
+ if(bracket) /* only one set allowed! */
+ break;
+ else
+ bracket++;
+ }
+ else if(*p == ']'){
+ if(bracket != 1) /* must be closing bracket */
+ break;
+ else
+ bracket++;
+ }
+ else if(!(isalnum((unsigned char) *p) || strindex(".-", *p)))
+ break;
+
+ snprintf(buf, buflen-(buf-origbuf), "%s%s%s",
+ (!nntp && *p) ? "#public" : "#news.",
+ (!nntp && *p && *bboard != '/') ? "/" : "",
+ bboard);
+
+ return(1);
+ }
+
+ return(0);
+}
+
+
+/*
+ * build_folder_list - call mail_list to fetch us a list of folders
+ * from the given context.
+ */
+void
+build_folder_list(MAILSTREAM **stream, CONTEXT_S *context, char *pat, char *content, int flags)
+{
+ MM_LIST_S ldata;
+ BFL_DATA_S response;
+ int local_open = 0, we_cancel = 0, resort = 0;
+ char reference[2*MAILTMPLEN], *p;
+
+ if(!(context->dir->status & CNTXT_NOFIND)
+ || (context->dir->status & CNTXT_PARTFIND))
+ return; /* find already done! */
+
+ dprint((7, "build_folder_list: %s %s\n",
+ context ? context->context : "NULL",
+ pat ? pat : "NULL"));
+
+ we_cancel = busy_cue(NULL, NULL, 1);
+
+ /*
+ * Set up the pattern of folder name's to match within the
+ * given context.
+ */
+ if(!pat || ((*pat == '*' || *pat == '%') && *(pat+1) == '\0')){
+ context->dir->status &= ~CNTXT_NOFIND; /* let'em know we tried */
+ pat = context->dir->view.internal;
+ }
+ else
+ context->use |= CNTXT_PARTFIND; /* or are in a partial find */
+
+ memset(mm_list_info = &ldata, 0, sizeof(MM_LIST_S));
+ ldata.filter = (NEWS_TEST(context)) ? mail_lsub_filter : mail_list_filter;
+ memset(ldata.data = &response, 0, sizeof(BFL_DATA_S));
+ response.list = FOLDERS(context);
+
+ if(flags & BFL_FLDRONLY)
+ response.mask = LATT_NOSELECT;
+
+ /*
+ * if context is associated with a server, prepare a stream for
+ * sending our request.
+ */
+ if(*context->context == '{'){
+ /*
+ * Try using a stream we've already got open...
+ */
+ if(stream && *stream
+ && !(ldata.stream = same_stream(context->context, *stream))){
+ pine_mail_close(*stream);
+ *stream = NULL;
+ }
+
+ if(!ldata.stream)
+ ldata.stream = sp_stream_get(context->context, SP_MATCH);
+
+ if(!ldata.stream)
+ ldata.stream = sp_stream_get(context->context, SP_SAME);
+
+ /* gotta open a new one? */
+ if(!ldata.stream){
+ ldata.stream = mail_cmd_stream(context, &local_open);
+ if(stream)
+ *stream = ldata.stream;
+ }
+
+ dprint((ldata.stream ? 7 : 1, "build_folder_list: mail_open(%s) %s.\n",
+ context->server ? context->server : "?",
+ ldata.stream ? "OK" : "FAILED"));
+
+ if(!ldata.stream){
+ context->use &= ~CNTXT_PARTFIND; /* unset partial find bit */
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ return;
+ }
+ }
+ else if(stream && *stream){ /* no server, simple case */
+ if(!sp_flagged(*stream, SP_LOCKED))
+ pine_mail_close(*stream);
+
+ *stream = NULL;
+ }
+
+ /*
+ * If preset reference string, we're somewhere in the hierarchy.
+ * ELSE we must be at top of context...
+ */
+ response.args.name = pat;
+ if(!(response.args.reference = context->dir->ref)){
+ if((p = strstr(context->context, "%s")) != NULL){
+ strncpy(response.args.reference = reference,
+ context->context,
+ MIN(p - context->context, sizeof(reference)/2));
+ reference[MIN(p - context->context, sizeof(reference)/2)] = '\0';
+ if(*(p += 2))
+ response.args.tail = p;
+ }
+ else
+ response.args.reference = context->context;
+ }
+
+ if(flags & BFL_SCAN)
+ mail_scan(ldata.stream, response.args.reference,
+ response.args.name, content);
+ else if(flags & BFL_LSUB)
+ mail_lsub(ldata.stream, response.args.reference, response.args.name);
+ else{
+ set_read_predicted(1);
+ pine_mail_list(ldata.stream, response.args.reference, response.args.name,
+ &ldata.options);
+ set_read_predicted(0);
+ }
+
+ if(context->dir && response.response.delim)
+ context->dir->delim = response.response.delim;
+
+ if(!(flags & (BFL_LSUB|BFL_SCAN)) && F_ON(F_QUELL_EMPTY_DIRS, ps_global)){
+ LISTRES_S listres;
+ FOLDER_S *f;
+ int i;
+
+ for(i = 0; i < folder_total(response.list); i++)
+ if((f = folder_entry(i, FOLDERS(context)))->isdir){
+ /*
+ * we don't need to do a list if we know already
+ * whether there are children or not.
+ */
+ if(!f->haschildren && !f->hasnochildren){
+ memset(ldata.data = &listres, 0, sizeof(LISTRES_S));
+ ldata.filter = mail_list_response;
+
+ if(context->dir->ref)
+ snprintf(reference, sizeof(reference), "%s%s", context->dir->ref, f->name);
+ else
+ context_apply(reference, context, f->name, sizeof(reference)/2);
+
+ /* append the delimiter to the reference */
+ for(p = reference; *p; p++)
+ ;
+
+ *p++ = context->dir->delim;
+ *p = '\0';
+
+ pine_mail_list(ldata.stream, reference, "%", NULL);
+
+ /* anything interesting inside? */
+ f->hasnochildren = (listres.count <= 1L);
+ f->haschildren = !f->hasnochildren;;
+ }
+
+ if(f->hasnochildren){
+ if(f->isfolder){
+ f->isdir = 0;
+ if(ps_global->fld_sort_rule == FLD_SORT_ALPHA_DIR_FIRST
+ || ps_global->fld_sort_rule == FLD_SORT_ALPHA_DIR_LAST)
+ resort = 1;
+ }
+ /*
+ * We don't want to hide directories
+ * that are only directories even if they are
+ * empty. We only want to hide the directory
+ * piece of a dual-use folder when there are
+ * no children in the directory (and the user
+ * most likely thinks of it as a folder instead
+ * of a folder and a directory).
+ */
+ else if(f->isdir && f->isdual){
+ folder_delete(i, FOLDERS(context));
+ i--;
+ }
+ }
+ }
+ }
+
+ if(resort)
+ resort_folder_list(response.list);
+
+ if(local_open && !stream)
+ pine_mail_close(ldata.stream);
+
+ if(context->use & CNTXT_PRESRV)
+ folder_select_restore(context);
+
+ context->use &= ~CNTXT_PARTFIND; /* unset partial list bit */
+ if(we_cancel)
+ cancel_busy_cue(-1);
+}
+
+
+/*
+ * Validate LIST response to command issued from build_folder_list
+ *
+ */
+void
+mail_list_filter(MAILSTREAM *stream, char *mailbox, int delim, long int attribs, void *data, unsigned int options)
+{
+ BFL_DATA_S *ld = (BFL_DATA_S *) data;
+ FOLDER_S *new_f = NULL, *dual_f = NULL;
+ int suppress_folder_add = 0;
+
+ if(!ld)
+ return;
+
+ if(delim)
+ ld->response.delim = delim;
+
+ /* test against mask of DIS-allowed attributes */
+ if((ld->mask & attribs)){
+ dprint((3, "mail_list_filter: failed attribute test"));
+ return;
+ }
+
+ /*
+ * First, make sure response fits our "reference" arg
+ * NOTE: build_folder_list can't supply breakout?
+ */
+ if(!mail_list_in_collection(&mailbox, ld->args.reference,
+ ld->args.name, ld->args.tail))
+ return;
+
+ /* ignore dotfolders unless told not to */
+ if(F_OFF(F_ENABLE_DOT_FOLDERS, ps_global) && *mailbox == '.'){
+ dprint((3, "mail_list_filter: dotfolder disallowed"));
+ return;
+ }
+
+ /*
+ * If this response is INBOX we have to handle it specially.
+ * The special cases are:
+ *
+ * Incoming folders are enabled and this INBOX is in the Incoming
+ * folders list already. We don't want to list it here, as well,
+ * unless it is also a directory.
+ *
+ * Incoming folders are not enabled, but we inserted INBOX into
+ * this primary collection (with init_inbox_mapping()) so it is
+ * already accounted for unless it is also a directory.
+ *
+ * Incoming folders are not enabled, but we inserted INBOX into
+ * the primary collection (which we are not looking at here) so
+ * it is already accounted for there. We'll add it as a directory
+ * as well here if that is what is called for.
+ */
+ if(!strucmp(mailbox, ps_global->inbox_name)){
+ int ftotal, i;
+ FOLDER_S *f;
+ char fullname[1000], tmp1[1000], tmp2[1000], *l1, *l2;
+
+ /* fullname is the name of the mailbox in the list response */
+ if(ld->args.reference){
+ strncpy(fullname, ld->args.reference, sizeof(fullname)-1);
+ fullname[sizeof(fullname)-1] = '\0';
+ }
+ else
+ fullname[0] = '\0';
+
+ strncat(fullname, mailbox, sizeof(fullname)-strlen(fullname)-1);
+ fullname[sizeof(fullname)-1] = '\0';
+
+ /* check if Incoming Folders are enabled */
+ if(ps_global->context_list && ps_global->context_list->use & CNTXT_INCMNG){
+ int this_inbox_is_in_incoming = 0;
+
+ /*
+ * Figure out if this INBOX is already in the Incoming list.
+ */
+
+ /* compare fullname to each incoming folder */
+ ftotal = folder_total(FOLDERS(ps_global->context_list));
+ for(i = 0; i < ftotal && !this_inbox_is_in_incoming; i++){
+ f = folder_entry(i, FOLDERS(ps_global->context_list));
+ if(f && f->name){
+ if(same_remote_mailboxes(fullname, f->name)
+ || ((!IS_REMOTE(fullname) && !IS_REMOTE(f->name))
+ && (l1=mailboxfile(tmp1,fullname))
+ && (l2=mailboxfile(tmp2,f->name))
+ && !strcmp(l1,l2)))
+ this_inbox_is_in_incoming++;
+ }
+ }
+
+ if(this_inbox_is_in_incoming){
+ /*
+ * Don't add a folder for this, only a directory if called for.
+ * If it isn't a directory, skip it.
+ */
+ if(!(delim && !(attribs & LATT_NOINFERIORS)))
+ return;
+
+ suppress_folder_add++;
+ }
+ }
+ else{
+ int inbox_is_in_this_collection = 0;
+
+ /* is INBOX already in this folder list? */
+ ftotal = folder_total(ld->list);
+ for(i = 0; i < ftotal && !inbox_is_in_this_collection; i++){
+ f = folder_entry(i, ld->list);
+ if(!strucmp(FLDR_NAME(f), ps_global->inbox_name))
+ inbox_is_in_this_collection++;
+ }
+
+ if(inbox_is_in_this_collection){
+
+ /*
+ * Inbox is already inserted in this collection. Unless
+ * it is also a directory, we are done.
+ */
+ if(!(delim && !(attribs & LATT_NOINFERIORS)))
+ return;
+
+ /*
+ * Since it is also a directory, what we do depends on
+ * the F_SEP feature. If that feature is not set we just
+ * want to mark the existing entry dual-use. If F_SEP is
+ * set we want to add a new directory entry.
+ */
+ if(F_ON(F_SEPARATE_FLDR_AS_DIR, ps_global)){
+ f->isdir = 0;
+ f->haschildren = 0;
+ f->hasnochildren = 0;
+ /* fall through and add a new directory */
+ }
+ else{
+ /* mark existing entry dual-use and return */
+ ld->response.count++;
+ ld->response.isdir = 1;
+ f->isdir = 1;
+ if(attribs & LATT_HASCHILDREN)
+ f->haschildren = 1;
+
+ if(attribs & LATT_HASNOCHILDREN)
+ f->hasnochildren = 1;
+
+ return;
+ }
+
+ suppress_folder_add++;
+ }
+ else{
+ int found_it = 0;
+ int this_inbox_is_primary_inbox = 0;
+
+ /*
+ * See if this INBOX is the same as the INBOX we inserted
+ * in the primary collection.
+ */
+
+ /* first find the existing INBOX entry */
+ ftotal = folder_total(FOLDERS(ps_global->context_list));
+ for(i = 0; i < ftotal && !found_it; i++){
+ f = folder_entry(i, FOLDERS(ps_global->context_list));
+ if(!strucmp(FLDR_NAME(f), ps_global->inbox_name))
+ found_it++;
+ }
+
+ if(found_it && f && f->name){
+ if(same_remote_mailboxes(fullname, f->name)
+ || ((!IS_REMOTE(fullname) && !IS_REMOTE(f->name))
+ && (l1=mailboxfile(tmp1,fullname))
+ && (l2=mailboxfile(tmp2,f->name))
+ && !strcmp(l1,l2)))
+ this_inbox_is_primary_inbox++;
+ }
+
+ if(this_inbox_is_primary_inbox){
+ /*
+ * Don't add a folder for this, only a directory if called for.
+ * If it isn't a directory, skip it.
+ */
+ if(!(delim && !(attribs & LATT_NOINFERIORS)))
+ return;
+
+ suppress_folder_add++;
+ }
+ }
+ }
+ }
+
+ /* is it a mailbox? */
+ if(!(attribs & LATT_NOSELECT) && !suppress_folder_add){
+ ld->response.count++;
+ ld->response.isfile = 1;
+ new_f = new_folder(mailbox, 0);
+ new_f->isfolder = 1;
+
+ if(F_ON(F_SEPARATE_FLDR_AS_DIR, ps_global)){
+ folder_insert(-1, new_f, ld->list);
+ dual_f = new_f;
+ new_f = NULL;
+ }
+ }
+
+ /* directory? */
+ if(delim && !(attribs & LATT_NOINFERIORS)){
+ ld->response.count++;
+ ld->response.isdir = 1;
+
+ if(!new_f)
+ new_f = new_folder(mailbox, 0);
+
+ new_f->isdir = 1;
+ if(attribs & LATT_HASCHILDREN)
+ new_f->haschildren = 1;
+ if(attribs & LATT_HASNOCHILDREN)
+ new_f->hasnochildren = 1;
+
+ /*
+ * When we have F_SEPARATE_FLDR_AS_DIR we still want to know
+ * whether the name really represents both so that we don't
+ * inadvertently delete both when the user meant one or the
+ * other.
+ */
+ if(dual_f){
+ if(attribs & LATT_HASCHILDREN)
+ dual_f->haschildren = 1;
+
+ if(attribs & LATT_HASNOCHILDREN)
+ dual_f->hasnochildren = 1;
+
+ dual_f->isdual = 1;
+ new_f->isdual = 1;
+ }
+ }
+
+ if(new_f)
+ folder_insert(-1, new_f, ld->list);
+
+ if(attribs & LATT_MARKED)
+ ld->response.ismarked = 1;
+
+ /* don't mark #move folders unmarked */
+ if(attribs & LATT_UNMARKED && !(options & PML_IS_MOVE_MBOX))
+ ld->response.unmarked = 1;
+
+ if(attribs & LATT_HASCHILDREN)
+ ld->response.haschildren = 1;
+
+ if(attribs & LATT_HASNOCHILDREN)
+ ld->response.hasnochildren = 1;
+}
+
+
+/*
+ * Validate LSUB response to command issued from build_folder_list
+ *
+ */
+void
+mail_lsub_filter(MAILSTREAM *stream, char *mailbox, int delim, long int attribs,
+ void *data, unsigned int options)
+{
+ BFL_DATA_S *ld = (BFL_DATA_S *) data;
+ FOLDER_S *new_f = NULL;
+ char *ref;
+
+ if(delim)
+ ld->response.delim = delim;
+
+ /* test against mask of DIS-allowed attributes */
+ if((ld->mask & attribs)){
+ dprint((3, "mail_lsub_filter: failed attribute test"));
+ return;
+ }
+
+ /* Normalize mailbox and reference strings re: namespace */
+ if(!strncmp(mailbox, "#news.", 6))
+ mailbox += 6;
+
+ if(!strncmp(ref = ld->args.reference, "#news.", 6))
+ ref += 6;
+
+ if(!mail_list_in_collection(&mailbox, ref, ld->args.name, ld->args.tail))
+ return;
+
+ if(!(attribs & LATT_NOSELECT)){
+ ld->response.count++;
+ ld->response.isfile = 1;
+ new_f = new_folder(mailbox, 0);
+ new_f->isfolder = 1;
+ folder_insert(F_ON(F_READ_IN_NEWSRC_ORDER, ps_global)
+ ? folder_total(ld->list) : -1,
+ new_f, ld->list);
+ }
+
+ /* We don't support directories in #news */
+}
+
+/* human readable name for a folder. memory freed by caller
+ * This is a jacket to conversion from modified utf7 to utf8.
+ */
+unsigned char *folder_name_decoded(unsigned char *mailbox)
+{
+ unsigned char *s;
+ s = (unsigned char *) utf8_from_mutf7((unsigned char *) mailbox);
+ if (s == NULL) s = (unsigned char *) cpystr(mailbox);
+ return s;
+}
+
+int
+mail_list_in_collection(char **mailbox, char *ref, char *name, char *tail)
+{
+ int boxlen, reflen, taillen;
+ char *p;
+
+ boxlen = strlen(*mailbox);
+ reflen = ref ? strlen(ref) : 0;
+ taillen = tail ? strlen(tail) : 0;
+
+ if(boxlen
+ && (reflen ? !struncmp(*mailbox, ref, reflen)
+ : (p = strpbrk(name, "%*"))
+ ? !struncmp(*mailbox, name, p - name)
+ : !strucmp(*mailbox,name))
+ && (!taillen
+ || (taillen < boxlen - reflen
+ && !strucmp(&(*mailbox)[boxlen - taillen], tail)))){
+ if(taillen)
+ (*mailbox)[boxlen - taillen] = '\0';
+
+ if(*(*mailbox += reflen))
+ return(TRUE);
+ }
+ /*
+ * else don't worry about context "breakouts" since
+ * build_folder_list doesn't let the user introduce
+ * one...
+ */
+
+ return(FALSE);
+}
+
+
+/*
+ * rebuild_folder_list -- free up old list and re-issue commands to build
+ * a new list.
+ */
+void
+refresh_folder_list(CONTEXT_S *context, int nodirs, int startover, MAILSTREAM **streamp)
+{
+ if(startover)
+ free_folder_list(context);
+
+ build_folder_list(streamp, context, NULL, NULL,
+ (NEWS_TEST(context) ? BFL_LSUB : BFL_NONE)
+ | ((nodirs) ? BFL_FLDRONLY : BFL_NONE));
+}
+
+
+/*
+ * free_folder_list - loop thru the context's lists of folders
+ * clearing all entries without nicknames
+ * (as those were user provided) AND reset the
+ * context's find flag.
+ *
+ * NOTE: if fetched() information (e.g., like message counts come back
+ * in bboard collections), we may want to have a check before
+ * executing the loop and setting the FIND flag.
+ */
+void
+free_folder_list(CONTEXT_S *cntxt)
+{
+ int n, i;
+
+ /*
+ * In this case, don't blast the list as it was given to us by the
+ * user and not the result of a mail_list call...
+ */
+ if(cntxt->use & CNTXT_INCMNG)
+ return;
+
+ if(cntxt->use & CNTXT_PRESRV)
+ folder_select_preserve(cntxt);
+
+ for(n = folder_total(FOLDERS(cntxt)), i = 0; n > 0; n--)
+ if(folder_entry(i, FOLDERS(cntxt))->nickname)
+ i++; /* entry wasn't from LIST */
+ else
+ folder_delete(i, FOLDERS(cntxt));
+
+ cntxt->dir->status |= CNTXT_NOFIND; /* do find next time... */
+ /* or add the fake entry */
+ cntxt->use &= ~(CNTXT_PSEUDO | CNTXT_PRESRV | CNTXT_ZOOM);
+}
+
+
+/*
+ * default_save_context - return the default context for saved messages
+ */
+CONTEXT_S *
+default_save_context(CONTEXT_S *cntxt)
+{
+ while(cntxt)
+ if((cntxt->use) & CNTXT_SAVEDFLT)
+ return(cntxt);
+ else
+ cntxt = cntxt->next;
+
+ return(NULL);
+}
+
+
+
+/*
+ * folder_complete - foldername completion routine
+ *
+ * Result: returns 0 if the folder doesn't have a any completetion
+ * 1 if the folder has a completion (*AND* "name" is
+ * replaced with the completion)
+ *
+ */
+int
+folder_complete(CONTEXT_S *context, char *name, size_t namelen, int *completions)
+{
+ return(folder_complete_internal(context, name, namelen, completions, FC_NONE));
+}
+
+
+/*
+ *
+ */
+int
+folder_complete_internal(CONTEXT_S *context, char *name, size_t namelen,
+ int *completions, int flags)
+{
+ int i, match = -1, ftotal;
+ char tmp[MAXFOLDER+2], *a, *b, *fn, *pat;
+ FOLDER_S *f;
+
+ if(completions)
+ *completions = 0;
+
+ if(*name == '\0' || !context_isambig(name))
+ return(0);
+
+ if(!((context->use & CNTXT_INCMNG) || ALL_FOUND(context))){
+ /*
+ * Build the folder list from scratch since we may need to
+ * traverse hierarchy...
+ */
+
+ free_folder_list(context);
+ snprintf(tmp, sizeof(tmp), "%s%c", name, NEWS_TEST(context) ? '*' : '%');
+ build_folder_list(NULL, context, tmp, NULL,
+ (NEWS_TEST(context) & !(flags & FC_FORCE_LIST))
+ ? BFL_LSUB : BFL_NONE);
+ }
+
+ *tmp = '\0'; /* find uniq substring */
+ ftotal = folder_total(FOLDERS(context));
+ for(i = 0; i < ftotal; i++){
+ f = folder_entry(i, FOLDERS(context));
+ fn = FLDR_NAME(f);
+ pat = name;
+ if(!(NEWS_TEST(context) || (context->use & CNTXT_INCMNG))){
+ fn = folder_last_cmpnt(fn, context->dir->delim);
+ pat = folder_last_cmpnt(pat, context->dir->delim);
+ }
+
+ if(!strncmp(fn, pat, strlen(pat))){
+ if(match != -1){ /* oh well, do best we can... */
+ a = fn;
+ if(match >= 0){
+ f = folder_entry(match, FOLDERS(context));
+ fn = FLDR_NAME(f);
+ if(!NEWS_TEST(context))
+ fn = folder_last_cmpnt(fn, context->dir->delim);
+
+ strncpy(tmp, fn, sizeof(tmp)-1);
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+
+ match = -2;
+ b = tmp; /* remember largest common text */
+ while(*a && *b && *a == *b)
+ *b++ = *a++;
+
+ *b = '\0';
+ }
+ else
+ match = i; /* bingo?? */
+ }
+ }
+
+ if(match >= 0){ /* found! */
+ f = folder_entry(match, FOLDERS(context));
+ fn = FLDR_NAME(f);
+ if(!(NEWS_TEST(context) || (context->use & CNTXT_INCMNG)))
+ fn = folder_last_cmpnt(fn, context->dir->delim);
+
+ strncpy(pat, fn, namelen-(pat-name));
+ name[namelen-1] = '\0';
+ if(f->isdir && !f->isfolder){
+ name[i = strlen(name)] = context->dir->delim;
+ name[i+1] = '\0';
+ }
+ }
+ else if(match == -2){ /* closest we could find */
+ strncpy(pat, tmp, namelen-(pat-name));
+ name[namelen-1] = '\0';
+ }
+
+ if(completions)
+ *completions = ftotal;
+
+ if(!((context->use & CNTXT_INCMNG) || ALL_FOUND(context)))
+ free_folder_list(context);
+
+ return((match >= 0) ? ftotal : 0);
+}
+
+
+char *
+folder_last_cmpnt(char *s, int d)
+{
+ register char *p;
+
+ if(d)
+ for(p = s; (p = strindex(p, d)); s = ++p)
+ ;
+
+ return(s);
+}
+
+
+/*
+ * init_folder_entries - return a piece of memory suitable for attaching
+ * a list of folders...
+ *
+ */
+FLIST *
+init_folder_entries(void)
+{
+ FLIST *flist = (FLIST *) fs_get(sizeof(FLIST));
+ flist->folders = (FOLDER_S **) fs_get(FCHUNK * sizeof(FOLDER_S *));
+ memset((void *)flist->folders, 0, (FCHUNK * sizeof(FOLDER_S *)));
+ flist->allocated = FCHUNK;
+ flist->used = 0;
+ return(flist);
+}
+
+
+/*
+ * new_folder - return a brand new folder entry, with the given name
+ * filled in.
+ *
+ * NOTE: THIS IS THE ONLY WAY TO PUT A NAME INTO A FOLDER ENTRY!!!
+ * STRCPY WILL NOT WORK!!!
+ */
+FOLDER_S *
+new_folder(char *name, long unsigned int hash)
+{
+ FOLDER_S *tmp;
+ size_t l = strlen(name);
+
+ tmp = (FOLDER_S *)fs_get(sizeof(FOLDER_S) + (l * sizeof(char)));
+ memset((void *)tmp, 0, sizeof(FOLDER_S));
+ strncpy(tmp->name, name, l);
+ tmp->name[l] = '\0';
+ tmp->name_len = (unsigned char) l;
+ tmp->varhash = hash;
+ return(tmp);
+}
+
+
+/*
+ * folder_entry - folder struct access routine. Permit reference to
+ * folder structs via index number. Serves two purposes:
+ * 1) easy way for callers to access folder data
+ * conveniently
+ * 2) allows for a custom manager to limit memory use
+ * under certain rather limited "operating systems"
+ * who shall renameless, but whose initials are DOS
+ *
+ *
+ */
+FOLDER_S *
+folder_entry(int i, FLIST *flist)
+{
+ return((i >= flist->used) ? NULL:flist->folders[i]);
+}
+
+
+/*
+ * free_folder_entries - release all resources associated with the given
+ * list of folder entries
+ */
+void
+free_folder_entries(FLIST **flist)
+{
+ register int i;
+
+ if(!(flist && *flist))
+ return;
+
+ i = (*flist)->used;
+ while(i--){
+ if((*flist)->folders[i]->nickname)
+ fs_give((void **) &(*flist)->folders[i]->nickname);
+
+ fs_give((void **) &((*flist)->folders[i]));
+ }
+
+ fs_give((void **) &((*flist)->folders));
+ fs_give((void **) flist);
+}
+
+
+/*
+ * return the number of folders associated with the given folder list
+ */
+int
+folder_total(FLIST *flist)
+{
+ return((int) flist->used);
+}
+
+
+/*
+ * return the index number of the given name in the given folder list
+ */
+int
+folder_index(char *name, CONTEXT_S *cntxt, int flags)
+{
+ register int i = 0;
+ FOLDER_S *f;
+ char *fname;
+
+ for(i = 0; (f = folder_entry(i, FOLDERS(cntxt))); i++)
+ if(((flags & FI_FOLDER) && (f->isfolder || (cntxt->use & CNTXT_INCMNG)))
+ || ((flags & FI_DIR) && f->isdir)){
+ fname = FLDR_NAME(f);
+#if defined(DOS) || defined(OS2)
+ if(flags & FI_RENAME){ /* case-dependent for rename */
+ if(*name == *fname && strcmp(name, fname) == 0)
+ return(i);
+ }
+ else{
+ if(toupper((unsigned char)(*name))
+ == toupper((unsigned char)(*fname)) && strucmp(name, fname) == 0)
+ return(i);
+ }
+#else
+ if(*name == *fname && strcmp(name, fname) == 0)
+ return(i);
+#endif
+ }
+
+ return(-1);
+}
+
+
+/*
+ * folder_is_nick - check to see if the given name is a nickname
+ * for some folder in the given context...
+ *
+ * NOTE: no need to check if mm_list_names has been done as
+ * nicknames can only be set by configuration...
+ */
+char *
+folder_is_nick(char *nickname, FLIST *flist, int flags)
+{
+ register int i = 0;
+ FOLDER_S *f;
+
+ if(!(nickname && *nickname && flist))
+ return(NULL);
+
+ while((f = folder_entry(i, flist)) != NULL){
+ /*
+ * The second part of the OR is checking in a case-indep
+ * way for INBOX. It should be restricted to the context
+ * to which we add the INBOX folder, which would be either
+ * the Incoming Folders collection or the first collection
+ * if there is no Incoming collection. We don't need to check
+ * the collection because nickname assignment has already
+ * done that for us. Most folders don't have nicknames, only
+ * incoming folders and folders like inbox if not in incoming.
+ */
+ if(f->nickname
+ && (!strcmp(nickname, f->nickname)
+ || (!strucmp(nickname, f->nickname)
+ && !strucmp(nickname, ps_global->inbox_name)))){
+ char source[MAILTMPLEN], *target = NULL;
+
+ /*
+ * If f is a maildrop, then we want to return the
+ * destination folder, not the whole #move thing.
+ */
+ if(!(flags & FN_WHOLE_NAME)
+ && check_for_move_mbox(f->name, source, sizeof(source), &target))
+ return(target);
+ else
+ return(f->name);
+ }
+ else
+ i++;
+ }
+
+ return(NULL);
+}
+
+
+char *
+folder_is_target_of_nick(char *longname, CONTEXT_S *cntxt)
+{
+ register int i = 0;
+ FOLDER_S *f;
+ FLIST *flist = NULL;
+
+ if(cntxt && cntxt == ps_global->context_list)
+ flist = FOLDERS(cntxt);
+
+ if(!(longname && *longname && flist))
+ return(NULL);
+
+ while((f = folder_entry(i, flist)) != NULL){
+ if(f->nickname && f->name && !strcmp(longname, f->name))
+ return(f->nickname);
+ else
+ i++;
+ }
+
+ return(NULL);
+}
+
+
+/*----------------------------------------------------------------------
+ Insert the given folder name into the sorted folder list
+ associated with the given context. Only allow ambiguous folder
+ names IF associated with a nickname.
+
+ Args: index -- Index to insert at, OR insert in sorted order if -1
+ folder -- folder structure to insert into list
+ flist -- folder list to insert folder into
+
+ **** WARNING ****
+ DON'T count on the folder pointer being valid after this returns
+ *** ALL FOLDER ELEMENT READS SHOULD BE THRU folder_entry() ***
+
+ ----*/
+int
+folder_insert(int index, FOLDER_S *folder, FLIST *flist)
+{
+ /* requested index < 0 means add to sorted list */
+ if(index < 0 && (index = folder_total(flist)) > 0)
+ index = folder_insert_sorted(index / 2, 0, index, folder, flist,
+ (ps_global->fld_sort_rule == FLD_SORT_ALPHA_DIR_FIRST)
+ ? compare_folders_dir_alpha
+ : (ps_global->fld_sort_rule == FLD_SORT_ALPHA_DIR_LAST)
+ ? compare_folders_alpha_dir
+ : compare_folders_alpha);
+
+ folder_insert_index(folder, index, flist);
+ return(index);
+}
+
+
+/*
+ * folder_insert_sorted - Insert given folder struct into given list
+ * observing sorting specified by given
+ * comparison function
+ */
+int
+folder_insert_sorted(int index, int min_index, int max_index, FOLDER_S *folder,
+ FLIST *flist, int (*compf)(FOLDER_S *, FOLDER_S *))
+{
+ int i;
+
+ return(((i = (*compf)(folder_entry(index, flist), folder)) == 0)
+ ? index
+ : (i < 0)
+ ? ((++index >= max_index)
+ ? max_index
+ : ((*compf)(folder_entry(index, flist), folder) > 0)
+ ? index
+ : folder_insert_sorted((index + max_index) / 2, index,
+ max_index, folder, flist, compf))
+ : ((index <= min_index)
+ ? min_index
+ : folder_insert_sorted((min_index + index) / 2, min_index, index,
+ folder, flist, compf)));
+}
+
+
+/*
+ * folder_insert_index - Insert the given folder struct into the global list
+ * at the given index.
+ */
+void
+folder_insert_index(FOLDER_S *folder, int index, FLIST *flist)
+{
+ register FOLDER_S **flp, **iflp;
+
+ /* if index is beyond size, place at end of list */
+ index = MIN(index, flist->used);
+
+ /* grow array ? */
+ if(flist->used + 1 > flist->allocated){
+ flist->allocated += FCHUNK;
+ fs_resize((void **)&(flist->folders),
+ flist->allocated * sizeof(FOLDER_S *));
+ }
+
+ /* shift array left */
+ iflp = &((flist->folders)[index]);
+ for(flp = &((flist->folders)[flist->used]);
+ flp > iflp; flp--)
+ flp[0] = flp[-1];
+
+ flist->folders[index] = folder;
+ flist->used += 1;
+}
+
+
+void
+resort_folder_list(FLIST *flist)
+{
+ if(flist && folder_total(flist) > 1 && flist->folders)
+ qsort(flist->folders, folder_total(flist), sizeof(flist->folders[0]),
+ (ps_global->fld_sort_rule == FLD_SORT_ALPHA_DIR_FIRST)
+ ? compare_folders_dir_alpha_qsort
+ : (ps_global->fld_sort_rule == FLD_SORT_ALPHA_DIR_LAST)
+ ? compare_folders_alpha_dir_qsort
+ : compare_folders_alpha_qsort);
+}
+
+
+/*----------------------------------------------------------------------
+ Removes a folder at the given index in the given context's
+ list.
+
+Args: index -- Index in folder list of folder to be removed
+ flist -- folder list
+ ----*/
+void
+folder_delete(int index, FLIST *flist)
+{
+ register int i;
+ FOLDER_S *f;
+
+ if(flist->used
+ && (index < 0 || index >= flist->used))
+ return; /* bogus call! */
+
+ if((f = folder_entry(index, flist))->nickname)
+ fs_give((void **)&(f->nickname));
+
+ fs_give((void **) &(flist->folders[index]));
+ for(i = index; i < flist->used - 1; i++)
+ flist->folders[i] = flist->folders[i+1];
+
+
+ flist->used -= 1;
+}
+
+
+/*----------------------------------------------------------------------
+ compare two names for qsort, case independent
+
+ Args: pointers to strings to compare
+
+ Result: integer result of strcmp of the names. Uses simple
+ efficiency hack to speed the string comparisons up a bit.
+
+ ----------------------------------------------------------------------*/
+int
+compare_names(const qsort_t *x, const qsort_t *y)
+{
+ char *a = *(char **)x, *b = *(char **)y;
+ int r;
+#define CMPI(X,Y) ((X)[0] - (Y)[0])
+#define UCMPI(X,Y) ((isupper((unsigned char)((X)[0])) \
+ ? (X)[0] - 'A' + 'a' : (X)[0]) \
+ - (isupper((unsigned char)((Y)[0])) \
+ ? (Y)[0] - 'A' + 'a' : (Y)[0]))
+
+ /*---- Inbox always sorts to the top ----*/
+ if(UCMPI(a, ps_global->inbox_name) == 0
+ && strucmp(a, ps_global->inbox_name) == 0)
+ return((CMPI(a, b) == 0 && strcmp(a, b) == 0) ? 0 : -1);
+ else if((UCMPI(b, ps_global->inbox_name)) == 0
+ && strucmp(b, ps_global->inbox_name) == 0)
+ return((CMPI(a, b) == 0 && strcmp(a, b) == 0) ? 0 : 1);
+
+ /*----- The sent-mail folder, is always next unless... ---*/
+ else if(F_OFF(F_SORT_DEFAULT_FCC_ALPHA, ps_global)
+ && CMPI(a, ps_global->VAR_DEFAULT_FCC) == 0
+ && strcmp(a, ps_global->VAR_DEFAULT_FCC) == 0)
+ return((CMPI(a, b) == 0 && strcmp(a, b) == 0) ? 0 : -1);
+ else if(F_OFF(F_SORT_DEFAULT_FCC_ALPHA, ps_global)
+ && CMPI(b, ps_global->VAR_DEFAULT_FCC) == 0
+ && strcmp(b, ps_global->VAR_DEFAULT_FCC) == 0)
+ return((CMPI(a, b) == 0 && strcmp(a, b) == 0) ? 0 : 1);
+
+ /*----- The saved-messages folder, is always next unless... ---*/
+ else if(F_OFF(F_SORT_DEFAULT_SAVE_ALPHA, ps_global)
+ && CMPI(a, ps_global->VAR_DEFAULT_SAVE_FOLDER) == 0
+ && strcmp(a, ps_global->VAR_DEFAULT_SAVE_FOLDER) == 0)
+ return((CMPI(a, b) == 0 && strcmp(a, b) == 0) ? 0 : -1);
+ else if(F_OFF(F_SORT_DEFAULT_SAVE_ALPHA, ps_global)
+ && CMPI(b, ps_global->VAR_DEFAULT_SAVE_FOLDER) == 0
+ && strcmp(b, ps_global->VAR_DEFAULT_SAVE_FOLDER) == 0)
+ return((CMPI(a, b) == 0 && strcmp(a, b) == 0) ? 0 : 1);
+
+ else
+ return((r = CMPI(a, b)) ? r : strcmp(a, b));
+}
+
+
+/*----------------------------------------------------------------------
+ compare two folder structs for ordering alphabetically
+
+ Args: pointers to folder structs to compare
+
+ Result: integer result of dir-bit and strcmp of the folders.
+ ----------------------------------------------------------------------*/
+int
+compare_folders_alpha(FOLDER_S *f1, FOLDER_S *f2)
+{
+ int i;
+ char *f1name = FLDR_NAME(f1),
+ *f2name = FLDR_NAME(f2);
+
+ return(((i = compare_names(&f1name, &f2name)) != 0)
+ ? i : (f2->isdir - f1->isdir));
+}
+
+
+int
+compare_folders_alpha_qsort(const qsort_t *a1, const qsort_t *a2)
+{
+ FOLDER_S *f1 = *((FOLDER_S **) a1);
+ FOLDER_S *f2 = *((FOLDER_S **) a2);
+
+ return(compare_folders_alpha(f1, f2));
+}
+
+
+/*----------------------------------------------------------------------
+ compare two folder structs alphabetically with dirs first
+
+ Args: pointers to folder structs to compare
+
+ Result: integer result of dir-bit and strcmp of the folders.
+ ----------------------------------------------------------------------*/
+int
+compare_folders_dir_alpha(FOLDER_S *f1, FOLDER_S *f2)
+{
+ int i;
+
+ if((i = (f2->isdir - f1->isdir)) == 0){
+ char *f1name = FLDR_NAME(f1),
+ *f2name = FLDR_NAME(f2);
+
+ return(compare_names(&f1name, &f2name));
+ }
+
+ return(i);
+}
+
+
+int
+compare_folders_dir_alpha_qsort(const qsort_t *a1, const qsort_t *a2)
+{
+ FOLDER_S *f1 = *((FOLDER_S **) a1);
+ FOLDER_S *f2 = *((FOLDER_S **) a2);
+
+ return(compare_folders_dir_alpha(f1, f2));
+}
+
+
+/*----------------------------------------------------------------------
+ compare two folder structs alphabetically with dirs last
+
+ Args: pointers to folder structs to compare
+
+ Result: integer result of dir-bit and strcmp of the folders.
+ ----------------------------------------------------------------------*/
+int
+compare_folders_alpha_dir(FOLDER_S *f1, FOLDER_S *f2)
+{
+ int i;
+
+ if((i = (f1->isdir - f2->isdir)) == 0){
+ char *f1name = FLDR_NAME(f1),
+ *f2name = FLDR_NAME(f2);
+
+ return(compare_names(&f1name, &f2name));
+ }
+
+ return(i);
+}
+
+
+int
+compare_folders_alpha_dir_qsort(const qsort_t *a1, const qsort_t *a2)
+{
+ FOLDER_S *f1 = *((FOLDER_S **) a1);
+ FOLDER_S *f2 = *((FOLDER_S **) a2);
+
+ return(compare_folders_alpha_dir(f1, f2));
+}
+
+
+/*
+ * Find incoming folders and update the unseen counts
+ * if necessary.
+ */
+void
+folder_unseen_count_updater(unsigned long flags)
+{
+ CONTEXT_S *ctxt;
+ time_t oldest, started_checking;
+ int ftotal, i, first = -1;
+ FOLDER_S *f;
+
+ /*
+ * We would only do this if there is an incoming collection, the
+ * user wants us to monitor, and we're in the folder screen.
+ */
+ if(ps_global->in_folder_screen && F_ON(F_ENABLE_INCOMING_CHECKING, ps_global)
+ && (ctxt=ps_global->context_list) && ctxt->use & CNTXT_INCMNG
+ && (ftotal = folder_total(FOLDERS(ctxt)))){
+ /*
+ * Search through the last_unseen_update times to find
+ * the one that was updated longest ago, and start with
+ * that one. We don't want to delay long doing these
+ * checks so may only check some of them each time we
+ * get called. An update time equal to 1 means don't check
+ * this folder at all.
+ */
+ for(i = 0; i < ftotal; i++){
+ f = folder_entry(i, FOLDERS(ctxt));
+ if(f && LUU_YES(f->last_unseen_update)){
+ first = i;
+ oldest = f->last_unseen_update;
+ break;
+ }
+ }
+
+ /*
+ * Now first is the first in the list that
+ * should ever be checked. Next find the
+ * one that should be checked next, the one
+ * that was checked longest ago.
+ */
+ if(first >= 0){
+ for(i = 1; i < ftotal; i++){
+ f = folder_entry(i, FOLDERS(ctxt));
+ if(f && LUU_YES(f->last_unseen_update) && f->last_unseen_update < oldest){
+ first = i;
+ oldest = f->last_unseen_update;
+ }
+ }
+ }
+
+ /* now first is the next one to be checked */
+
+ started_checking = time(0);
+
+ for(i = first; i < ftotal; i++){
+ /* update the next one */
+ f = folder_entry(i, FOLDERS(ctxt));
+ if(f && LUU_YES(f->last_unseen_update)
+ && (flags & UFU_FORCE
+ /* or it's been long enough and we've not been in this function too long */
+ || (((time(0) - f->last_unseen_update) >= ps_global->inc_check_interval)
+ && ((time(0) - started_checking) < MIN(4,ps_global->inc_check_timeout)))))
+ update_folder_unseen(f, ctxt, flags, NULL);
+ }
+
+ for(i = 0; i < first; i++){
+ f = folder_entry(i, FOLDERS(ctxt));
+ if(f && LUU_YES(f->last_unseen_update)
+ && (flags & UFU_FORCE
+ || (((time(0) - f->last_unseen_update) >= ps_global->inc_check_interval)
+ && ((time(0) - started_checking) < MIN(4,ps_global->inc_check_timeout)))))
+ update_folder_unseen(f, ctxt, flags, NULL);
+ }
+ }
+}
+
+
+/*
+ * Update the count of unseen in the FOLDER_S struct
+ * for this folder. This will update if the time
+ * interval has passed or if the FORCE flag is set.
+ */
+void
+update_folder_unseen(FOLDER_S *f, CONTEXT_S *ctxt, unsigned long flags,
+ MAILSTREAM *this_is_the_stream)
+{
+ time_t now;
+ int orig_valid;
+ int use_imap_interval = 0;
+ int stream_is_open = 0;
+ unsigned long orig_unseen, orig_new, orig_tot;
+ char mailbox_name[MAILTMPLEN];
+ char *target = NULL;
+ DRIVER *d;
+
+ if(!f || !LUU_YES(f->last_unseen_update))
+ return;
+
+ now = time(0);
+ context_apply(mailbox_name, ctxt, f->name, MAILTMPLEN);
+
+ if(!mailbox_name[0])
+ return;
+
+ if(check_for_move_mbox(mailbox_name, NULL, 0, &target)){
+ MAILSTREAM *strm;
+
+ /*
+ * If this maildrop is the currently open stream use that.
+ * I'm not altogether sure that this is a good way to
+ * check this.
+ */
+ if(target
+ && ((strm=ps_global->mail_stream)
+ && strm->snarf.name
+ && (!strcmp(target,strm->mailbox)
+ || !strcmp(target,strm->original_mailbox)))){
+ stream_is_open++;
+ }
+ }
+ else{
+ MAILSTREAM *m = NULL;
+
+ stream_is_open = (this_is_the_stream
+ || (m=sp_stream_get(mailbox_name, SP_MATCH | SP_RO_OK))
+ || ((m=ps_global->mail_stream) && !sp_dead_stream(m)
+ && same_stream_and_mailbox(mailbox_name,m))
+ || (!IS_REMOTE(mailbox_name)
+ && (m=already_open_stream(mailbox_name, AOS_NONE)))) ? 1 : 0;
+
+ if(stream_is_open){
+ if(!this_is_the_stream)
+ this_is_the_stream = m;
+ }
+ else{
+ /*
+ * If it's IMAP or local we use a shorter interval.
+ */
+ d = mail_valid(NIL, mailbox_name, (char *) NIL);
+ if((d && !strcmp(d->name, "imap")) || !IS_REMOTE(mailbox_name))
+ use_imap_interval++;
+ }
+ }
+
+ /*
+ * Update if forced, or if it's been a while, or if we have a
+ * stream open to this mailbox already.
+ */
+ if(flags & UFU_FORCE
+ || stream_is_open
+ || ((use_imap_interval
+ && (now - f->last_unseen_update) >= ps_global->inc_check_interval)
+ || ((now - f->last_unseen_update) >= ps_global->inc_second_check_interval))){
+ unsigned long tot, uns, new;
+ unsigned long *totp = NULL, *unsp = NULL, *newp = NULL;
+
+ orig_valid = f->unseen_valid;
+ orig_unseen = f->unseen;
+ orig_new = f->new;
+ orig_tot = f->total;
+
+ if(F_ON(F_INCOMING_CHECKING_RECENT, ps_global))
+ newp = &new;
+ else
+ unsp = &uns;
+
+ if(F_ON(F_INCOMING_CHECKING_TOTAL, ps_global))
+ totp = &tot;
+
+ f->unseen_valid = 0;
+
+ dprint((9, "update_folder_unseen(%s)", FLDR_NAME(f)));
+ if(get_recent_in_folder(mailbox_name, newp, unsp, totp, this_is_the_stream)){
+ f->last_unseen_update = time(0);
+ f->unseen_valid = 1;
+ if(unsp)
+ f->unseen = uns;
+
+ if(newp)
+ f->new = new;
+
+ if(totp)
+ f->total = tot;
+
+ if(!orig_valid){
+ dprint((9, "update_folder_unseen(%s): original: %s%s%s%s",
+ FLDR_NAME(f),
+ F_ON(F_INCOMING_CHECKING_RECENT,ps_global) ? "new=" : "unseen=",
+ F_ON(F_INCOMING_CHECKING_RECENT,ps_global) ? comatose(f->new) : comatose(f->unseen),
+ F_ON(F_INCOMING_CHECKING_TOTAL,ps_global) ? " tot=" : "",
+ F_ON(F_INCOMING_CHECKING_TOTAL,ps_global) ? comatose(f->total) : ""));
+ }
+
+ if(orig_valid
+ && ((F_ON(F_INCOMING_CHECKING_RECENT, ps_global)
+ && orig_new != f->new)
+ ||
+ (F_OFF(F_INCOMING_CHECKING_RECENT, ps_global)
+ && orig_unseen != f->unseen)
+ ||
+ (F_ON(F_INCOMING_CHECKING_TOTAL, ps_global)
+ && orig_tot != f->total))){
+
+ if(ps_global->in_folder_screen)
+ ps_global->noticed_change_in_unseen = 1;
+
+ dprint((9, "update_folder_unseen(%s): changed: %s%s%s%s",
+ FLDR_NAME(f),
+ F_ON(F_INCOMING_CHECKING_RECENT,ps_global) ? "new=" : "unseen=",
+ F_ON(F_INCOMING_CHECKING_RECENT,ps_global) ? comatose(f->new) : comatose(f->unseen),
+ F_ON(F_INCOMING_CHECKING_TOTAL,ps_global) ? " tot=" : "",
+ F_ON(F_INCOMING_CHECKING_TOTAL,ps_global) ? comatose(f->total) : ""));
+
+ if(flags & UFU_ANNOUNCE
+ && ((F_ON(F_INCOMING_CHECKING_RECENT, ps_global)
+ && orig_new < f->new)
+ ||
+ (F_OFF(F_INCOMING_CHECKING_RECENT, ps_global)
+ && orig_unseen < f->unseen))){
+ if(F_ON(F_INCOMING_CHECKING_RECENT, ps_global))
+ q_status_message3(SM_ASYNC, 1, 3, "%s: %s %s",
+ FLDR_NAME(f), comatose(f->new),
+ _("new"));
+ else
+ q_status_message3(SM_ASYNC, 1, 3, "%s: %s %s",
+ FLDR_NAME(f), comatose(f->unseen),
+ _("unseen"));
+ }
+ }
+ }
+ else
+ f->last_unseen_update = LUU_NOMORECHK; /* no further checking */
+ }
+}
+
+
+void
+update_folder_unseen_by_stream(MAILSTREAM *strm, unsigned long flags)
+{
+ CONTEXT_S *ctxt;
+ int ftotal, i;
+ char mailbox_name[MAILTMPLEN];
+ char *cn, tmp[MAILTMPLEN];
+ FOLDER_S *f;
+
+ /*
+ * Attempt to figure out which incoming folder this stream
+ * is open to, if any, so we can update the unseen counters.
+ */
+ if(strm
+ && F_ON(F_ENABLE_INCOMING_CHECKING, ps_global)
+ && (ctxt=ps_global->context_list) && ctxt->use & CNTXT_INCMNG
+ && (ftotal = folder_total(FOLDERS(ctxt)))){
+ for(i = 0; i < ftotal; i++){
+ f = folder_entry(i, FOLDERS(ctxt));
+ context_apply(mailbox_name, ctxt, f->name, MAILTMPLEN);
+ if(same_stream_and_mailbox(mailbox_name, strm)
+ || (!IS_REMOTE(mailbox_name) && (cn=mailboxfile(tmp,mailbox_name)) && (*cn) && (!strcmp(cn, strm->mailbox) || !strcmp(cn, strm->original_mailbox)))){
+ /* if we failed earlier on this one, give it another go */
+ if(f->last_unseen_update == LUU_NOMORECHK)
+ init_incoming_unseen_data(ps_global, f);
+
+ update_folder_unseen(f, ctxt, flags, strm);
+ return;
+ }
+ }
+ }
+}
+
+
+/*
+ * Find the number of new, unseen, and the total number of
+ * messages in mailbox_name.
+ * If the corresponding arg is NULL it will skip the work
+ * necessary for that flag.
+ *
+ * Returns 1 if successful, 0 if not.
+ */
+int
+get_recent_in_folder(char *mailbox_name, long unsigned int *new,
+ long unsigned int *unseen, long unsigned int *total,
+ MAILSTREAM *this_is_the_stream)
+{
+ MAILSTREAM *strm = NIL;
+ unsigned long tot, nw, uns;
+ int gotit = 0;
+ int maildrop = 0;
+ char *target = NULL;
+ MSGNO_S *msgmap;
+ long excluded, flags;
+ extern MAILSTATUS mm_status_result;
+
+ dprint((9, "get_recent_in_folder(%s)", mailbox_name ? mailbox_name : "?"));
+
+ if(check_for_move_mbox(mailbox_name, NULL, 0, &target)){
+
+ maildrop++;
+
+ /*
+ * If this maildrop is the currently open stream use that.
+ */
+ if(target
+ && ((strm=ps_global->mail_stream)
+ && strm->snarf.name
+ && (!strcmp(target,strm->mailbox)
+ || !strcmp(target,strm->original_mailbox)))){
+ gotit++;
+ msgmap = sp_msgmap(strm);
+ excluded = any_lflagged(msgmap, MN_EXLD);
+
+ tot = strm->nmsgs - excluded;
+ if(tot){
+ if(new){
+ if(sp_recent_since_visited(strm) == 0)
+ nw = 0;
+ else
+ nw = count_flagged(strm, F_RECENT | F_UNSEEN | F_UNDEL);
+ }
+
+ if(unseen)
+ uns = count_flagged(strm, F_UNSEEN | F_UNDEL);
+ }
+ else{
+ nw = 0;
+ uns = 0;
+ }
+ }
+ /* else fall through to just open it case */
+ }
+
+ /* do we already have it selected? */
+ if(!gotit
+ && ((strm = this_is_the_stream)
+ || (strm = sp_stream_get(mailbox_name, SP_MATCH | SP_RO_OK))
+ || (!IS_REMOTE(mailbox_name)
+ && (strm = already_open_stream(mailbox_name, AOS_NONE))))){
+ gotit++;
+
+ /*
+ * Unfortunately, we have to worry about excluded
+ * messages. The user doesn't want to have
+ * excluded messages count in the totals, especially
+ * recent excluded messages.
+ */
+
+ msgmap = sp_msgmap(strm);
+ excluded = any_lflagged(msgmap, MN_EXLD);
+
+ tot = strm->nmsgs - excluded;
+ if(tot){
+ if(new){
+ if(sp_recent_since_visited(strm) == 0)
+ nw = 0;
+ else
+ nw = count_flagged(strm, F_RECENT | F_UNSEEN | F_UNDEL);
+ }
+
+ if(unseen)
+ uns = count_flagged(strm, F_UNSEEN | F_UNDEL);
+ }
+ else{
+ nw = 0;
+ uns = 0;
+ }
+ }
+ /*
+ * No, but how about another stream to same server which
+ * could be used for a STATUS command?
+ */
+ else if(!gotit && (strm = sp_stream_get(mailbox_name, SP_SAME))
+ && modern_imap_stream(strm)){
+
+ flags = 0L;
+ if(total)
+ flags |= SA_MESSAGES;
+
+ if(new)
+ flags |= SA_RECENT;
+
+ if(unseen)
+ flags |= SA_UNSEEN;
+
+ mm_status_result.flags = 0L;
+
+ pine_mail_status(strm, mailbox_name, flags);
+ if(total){
+ if(mm_status_result.flags & SA_MESSAGES){
+ tot = mm_status_result.messages;
+ gotit++;
+ }
+ }
+
+ if(!(total && !gotit)){
+ if(new){
+ if(mm_status_result.flags & SA_RECENT){
+ nw = mm_status_result.recent;
+ gotit++;
+ }
+ else
+ gotit = 0;
+ }
+ }
+
+ if(!((total || new) && !gotit)){
+ if(unseen){
+ if(mm_status_result.flags & SA_UNSEEN){
+ uns = mm_status_result.unseen;
+ gotit++;
+ }
+ else
+ gotit = 0;
+ }
+ }
+ }
+
+ /* Let's just Select it. */
+ if(!gotit){
+ long saved_timeout;
+ long openflags;
+
+ /*
+ * Traditional unix folders don't notice new mail if
+ * they are opened readonly. So maildrops with unix folder
+ * targets will snarf to the file but the stream that is
+ * opened won't see the new mail. So make all maildrop
+ * opens non-readonly here.
+ */
+ openflags = SP_USEPOOL | SP_TEMPUSE | (maildrop ? 0 : OP_READONLY);
+
+ saved_timeout = (long) mail_parameters(NULL, GET_OPENTIMEOUT, NULL);
+ mail_parameters(NULL, SET_OPENTIMEOUT, (void *) (long) ps_global->inc_check_timeout);
+ strm = pine_mail_open(NULL, mailbox_name, openflags, NULL);
+ mail_parameters(NULL, SET_OPENTIMEOUT, (void *) saved_timeout);
+
+ if(strm){
+ gotit++;
+ msgmap = sp_msgmap(strm);
+ excluded = any_lflagged(msgmap, MN_EXLD);
+
+ tot = strm->nmsgs - excluded;
+ if(tot){
+ if(new){
+ if(sp_recent_since_visited(strm) == 0)
+ nw = 0;
+ else
+ nw = count_flagged(strm, F_RECENT | F_UNSEEN | F_UNDEL);
+ }
+
+ if(unseen)
+ uns = count_flagged(strm, F_UNSEEN | F_UNDEL);
+ }
+ else{
+ nw = 0;
+ uns = 0;
+ }
+
+ pine_mail_close(strm);
+ }
+ }
+
+ if(gotit){
+ if(new)
+ *new = nw;
+
+ if(unseen)
+ *unseen = uns;
+
+ if(total)
+ *total = tot;
+ }
+
+ return(gotit);
+}
+
+
+void
+clear_incoming_valid_bits(void)
+{
+ CONTEXT_S *ctxt;
+ int ftotal, i;
+ FOLDER_S *f;
+
+ if(F_ON(F_ENABLE_INCOMING_CHECKING, ps_global)
+ && (ctxt=ps_global->context_list) && ctxt->use & CNTXT_INCMNG
+ && (ftotal = folder_total(FOLDERS(ctxt))))
+ for(i = 0; i < ftotal; i++){
+ f = folder_entry(i, FOLDERS(ctxt));
+ init_incoming_unseen_data(ps_global, f);
+ }
+}
+
+
+int
+selected_folders(CONTEXT_S *context)
+{
+ int i, n, total;
+
+ n = folder_total(FOLDERS(context));
+ for(total = i = 0; i < n; i++)
+ if(folder_entry(i, FOLDERS(context))->selected)
+ total++;
+
+ return(total);
+}
+
+
+SELECTED_S *
+new_selected(void)
+{
+ SELECTED_S *selp;
+
+ selp = (SELECTED_S *)fs_get(sizeof(SELECTED_S));
+ selp->sub = NULL;
+ selp->reference = NULL;
+ selp->folders = NULL;
+ selp->zoomed = 0;
+
+ return selp;
+}
+
+
+/*
+ * Free the current selected struct and all of the
+ * following structs in the list
+ */
+void
+free_selected(SELECTED_S **selp)
+{
+ if(!selp || !(*selp))
+ return;
+ if((*selp)->sub)
+ free_selected(&((*selp)->sub));
+
+ free_strlist(&(*selp)->folders);
+ if((*selp)->reference)
+ fs_give((void **) &(*selp)->reference);
+
+ fs_give((void **) selp);
+}
+
+
+void
+folder_select_preserve(CONTEXT_S *context)
+{
+ if(context
+ && !(context->use & CNTXT_PARTFIND)){
+ FOLDER_S *fp;
+ STRLIST_S **slpp;
+ SELECTED_S *selp = &context->selected;
+ int i, folder_n;
+
+ if(!context->dir->ref){
+ if(!context->selected.folders)
+ slpp = &context->selected.folders;
+ else
+ return;
+ }
+ else{
+ if(!selected_folders(context))
+ return;
+ else{
+ while(selp->sub){
+ selp = selp->sub;
+ if(!strcmp(selp->reference, context->dir->ref))
+ return;
+ }
+ selp->sub = new_selected();
+ selp = selp->sub;
+ slpp = &(selp->folders);
+ }
+ }
+ folder_n = folder_total(FOLDERS(context));
+
+ for(i = 0; i < folder_n; i++)
+ if((fp = folder_entry(i, FOLDERS(context)))->selected){
+ *slpp = new_strlist(fp->name);
+ slpp = &(*slpp)->next;
+ }
+
+ /* Only remember "ref" if any folders were selected */
+ if(selp->folders && context->dir->ref)
+ selp->reference = cpystr(context->dir->ref);
+
+ selp->zoomed = (context->use & CNTXT_ZOOM) != 0;
+ }
+}
+
+
+int
+folder_select_restore(CONTEXT_S *context)
+{
+ int rv = 0;
+
+ if(context
+ && !(context->use & CNTXT_PARTFIND)){
+ STRLIST_S *slp;
+ SELECTED_S *selp, *pselp = NULL;
+ int i, found = 0;
+
+ selp = &(context->selected);
+
+ if(context->dir->ref){
+ pselp = selp;
+ selp = selp->sub;
+ while(selp && strcmp(selp->reference, context->dir->ref)){
+ pselp = selp;
+ selp = selp->sub;
+ }
+ if (selp)
+ found = 1;
+ }
+ else
+ found = selp->folders != 0;
+ if(found){
+ for(slp = selp->folders; slp; slp = slp->next)
+ if(slp->name
+ && (i = folder_index(slp->name, context, FI_FOLDER)) >= 0){
+ folder_entry(i, FOLDERS(context))->selected = 1;
+ rv++;
+ }
+
+ /* Used, always clean them up */
+ free_strlist(&selp->folders);
+ if(selp->reference)
+ fs_give((void **) &selp->reference);
+
+ if(selp->zoomed){
+ context->use |= CNTXT_ZOOM;
+ selp->zoomed = 0;
+ }
+ if(!(selp == &context->selected)){
+ if(pselp){
+ pselp->sub = selp->sub;
+ fs_give((void **) &selp);
+ }
+ }
+ }
+ }
+
+ return(rv);
+}
+
diff --git a/pith/folder.h b/pith/folder.h
new file mode 100644
index 00000000..dfec2ca2
--- /dev/null
+++ b/pith/folder.h
@@ -0,0 +1,134 @@
+/*
+ * $Id: folder.h 880 2007-12-18 00:57:56Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_FOLDER_INCLUDED
+#define PITH_FOLDER_INCLUDED
+
+
+#include "../pith/foldertype.h"
+#include "../pith/conftype.h"
+#include "../pith/context.h"
+#include "../pith/state.h"
+
+
+/*
+ * Structs to ease c-client LIST/LSUB interaction
+ */
+typedef struct _listargs {
+ char *reference, /* IMAP LIST "reference" arg */
+ *name, /* IMAP LIST "name" arg */
+ *tail; /* Pine "context" after "name" part */
+} LISTARGS_S;
+
+typedef struct _listresponse {
+ long count;
+ int delim;
+ unsigned isfile:1,
+ isdir:1,
+ ismarked:1,
+ unmarked:1,
+ haschildren:1,
+ hasnochildren:1;
+} LISTRES_S;
+
+
+typedef struct _existdata {
+ LISTARGS_S args;
+ LISTRES_S response;
+ char **fullname;
+ int is_move_folder;
+} EXISTDATA_S;
+
+
+#define FEX_NOENT 0x0000 /* file_exists: doesn't exist */
+#define FEX_ISFILE 0x0001 /* file_exists: name is a file */
+#define FEX_ISDIR 0x0002 /* file_exists: name is a dir */
+#define FEX_ISMARKED 0x0004 /* file_exists: is interesting */
+#define FEX_UNMARKED 0x0008 /* file_exists: known UNinteresting */
+#define FEX_ERROR 0x1000 /* file_exists: error occured */
+
+#define BFL_NONE 0x00 /* build_folder_list: no flag */
+#define BFL_FLDRONLY 0x01 /* ignore directories */
+#define BFL_LSUB 0x02 /* use mail_lsub vs mail_list */
+#define BFL_SCAN 0x04 /* use mail_scan vs mail_list */
+#define BFL_CHILDREN 0x08 /* make sure haschildren is accurate */
+
+#define FI_FOLDER 0x01 /* folder_index flags */
+#define FI_DIR 0x02
+#define FI_RENAME 0x04
+#define FI_ANY (FI_FOLDER | FI_DIR)
+
+#define FN_NONE 0x00 /* flags modifying folder_is_nick */
+#define FN_WHOLE_NAME 0x01 /* return long name if #move */
+
+#define FC_NONE 0 /* flags for folder_complete */
+#define FC_FORCE_LIST 1
+
+#define UFU_NONE 0x00 /* flags for update_folder_unseen */
+#define UFU_FORCE 0x01
+#define UFU_ANNOUNCE 0x02 /* announce increases with q_status */
+
+
+/* exported protoypes */
+char *folder_lister_desc(CONTEXT_S *, FDIR_S *);
+void reset_context_folders(CONTEXT_S *);
+FDIR_S *next_folder_dir(CONTEXT_S *, char *, int, MAILSTREAM **);
+EditWhich config_containing_inc_fldr(FOLDER_S *);
+char *pretty_fn(char *);
+int get_folder_delimiter(char *);
+int folder_exists(CONTEXT_S *, char *);
+int folder_name_exists(CONTEXT_S *, char *, char **);
+char *folder_as_breakout(CONTEXT_S *, char *);
+void init_folders(struct pine *);
+void init_inbox_mapping(char *, CONTEXT_S *);
+void reinit_incoming_folder_list(struct pine *, CONTEXT_S *);
+FDIR_S *new_fdir(char *, char *, int);
+void free_fdir(FDIR_S **, int);
+void build_folder_list(MAILSTREAM **, CONTEXT_S *, char *, char *, int);
+void free_folder_list(CONTEXT_S *);
+CONTEXT_S *default_save_context(CONTEXT_S *);
+int folder_complete(CONTEXT_S *, char *, size_t, int *);
+FOLDER_S *new_folder(char *, unsigned long);
+FOLDER_S *folder_entry(int, FLIST *);
+int folder_total(FLIST *);
+int folder_index(char *, CONTEXT_S *, int);
+char *folder_is_nick(char *, FLIST *, int);
+char *folder_is_target_of_nick(char *, CONTEXT_S *);
+int folder_insert(int, FOLDER_S *, FLIST *);
+FLIST *init_folder_entries(void);
+int compare_folders_alpha(FOLDER_S *, FOLDER_S *);
+int compare_folders_dir_alpha(FOLDER_S *, FOLDER_S *);
+int compare_folders_alpha_dir(FOLDER_S *, FOLDER_S *);
+void mail_list_response(MAILSTREAM *, char *, int, long, void *, unsigned);
+void folder_seen_count_updater(void *);
+void folder_unseen_count_updater(unsigned long);
+void update_folder_unseen(FOLDER_S *, CONTEXT_S *, unsigned long, MAILSTREAM *);
+void update_folder_unseen_by_stream(MAILSTREAM *, unsigned long);
+int get_recent_in_folder(char *, unsigned long *, unsigned long *,
+ unsigned long *, MAILSTREAM *);
+void clear_incoming_valid_bits(void);
+int selected_folders(CONTEXT_S *);
+SELECTED_S *new_selected(void);
+void free_selected(SELECTED_S **);
+void folder_select_preserve(CONTEXT_S *);
+int folder_select_restore(CONTEXT_S *);
+int update_bboard_spec(char *, char *, size_t);
+void refresh_folder_list(CONTEXT_S *, int, int, MAILSTREAM **);
+int folder_complete_internal(CONTEXT_S *, char *, size_t, int *, int);
+void folder_delete(int, FLIST *);
+unsigned char *folder_name_decoded(unsigned char *);
+
+#endif /* PITH_FOLDER_INCLUDED */
diff --git a/pith/foldertype.h b/pith/foldertype.h
new file mode 100644
index 00000000..95d8b14a
--- /dev/null
+++ b/pith/foldertype.h
@@ -0,0 +1,163 @@
+/*
+ * $Id: foldertype.h 768 2007-10-24 00:10:03Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_FOLDERTYPE_INCLUDED
+#define PITH_FOLDERTYPE_INCLUDED
+
+
+#include "../pith/conftype.h"
+#include "../pith/string.h"
+
+
+/*
+ * Type definitions for folder and context structures.
+ */
+
+
+/*------------------------------
+ Used for displaying as well as
+ keeping track of folders.
+ ----*/
+typedef struct folder {
+ unsigned char name_len; /* name length */
+ unsigned isfolder:1; /* is it a folder? */
+ unsigned isdir:1; /* is it a directory? */
+ /* isdual is only set if the user has the separate
+ directory and folder display option set. Otherwise,
+ we can tell it's dual use because both isfolder
+ and isdir will be set. */
+ unsigned isdual:1; /* dual use */
+ unsigned haschildren:1; /* dir known to have children */
+ unsigned hasnochildren:1; /* known not to have children */
+ unsigned scanned:1; /* scanned by c-client */
+ unsigned selected:1; /* selected by user */
+ unsigned subscribed:1; /* selected by user */
+ unsigned unseen_valid:1; /* unseen count is valid */
+ unsigned long varhash; /* hash of var for incoming */
+ imapuid_t uidvalidity; /* only for #move folder */
+ imapuid_t uidnext; /* only for #move folder */
+ char *nickname; /* folder's short name */
+ unsigned long unseen; /* for monitoring unseen */
+ unsigned long new; /* for monitoring unseen */
+ unsigned long total; /* for monitoring unseen */
+ time_t last_unseen_update; /* see LUU_ constants below */
+ char name[1]; /* folder's name */
+} FOLDER_S;
+
+
+/* special values stored in last_unseen_update */
+#define LUU_INIT ((time_t) 0) /* before first check is done */
+#define LUU_NEVERCHK ((time_t) 1) /* don't check this folder */
+#define LUU_NOMORECHK ((time_t) 2) /* check failed so stop checking */
+
+/* this value is eligible for checking */
+#define LUU_YES(luu) (((luu) != LUU_NEVERCHK) && ((luu) != LUU_NOMORECHK))
+
+
+/*
+ * Folder List Structure - provides for two ways to access and manage
+ * folder list data. One as an array of pointers
+ * to folder structs or
+ */
+typedef struct folder_list {
+ unsigned used;
+ unsigned allocated;
+ FOLDER_S **folders; /* array of pointers to folder structs */
+} FLIST;
+
+
+/*
+ * digested form of context including pointer to the parent
+ * level of hierarchy...
+ */
+typedef struct folder_dir {
+ char *ref, /* collection location */
+ *desc, /* Optional description */
+ delim, /* dir/file delimiter */
+ status; /* folder data's status */
+ struct {
+ char *user,
+ *internal;
+ } view; /* file's within dir */
+
+ FLIST *folders; /* folder data */
+ struct folder_dir *prev; /* parent directory */
+} FDIR_S;
+
+
+typedef struct selected_s {
+ char *reference; /* location of selected */
+ STRLIST_S *folders; /* list of selected */
+ unsigned zoomed:1; /* zoomed state */
+ struct selected_s *sub;
+} SELECTED_S;
+
+
+/*------------------------------
+ Stucture to keep track of the various folder collections being
+ dealt with.
+ ----*/
+typedef struct context {
+ FDIR_S *dir; /* directory stack */
+ char *context, /* raw context string */
+ *server, /* server name/parms */
+ *nickname, /* user provided nickname */
+ *label, /* Description */
+ *comment, /* Optional comment */
+ last_folder[MAXFOLDER+1]; /* last folder used */
+ struct {
+ struct variable *v; /* variable where defined */
+ short i; /* index into config list */
+ } var;
+
+ unsigned short use, /* use flags (see below) */
+ d_line; /* display line for labels */
+ SELECTED_S selected;
+ struct context *next, /* next context struct */
+ *prev; /* previous context struct */
+} CONTEXT_S;
+
+/*
+ * Flags to indicate context (i.e., folder collection) use
+ */
+#define CNTXT_PSEUDO 0x0001 /* fake folder entry exists */
+#define CNTXT_INCMNG 0x0002 /* inbox collection */
+#define CNTXT_SAVEDFLT 0x0004 /* default save collection */
+#define CNTXT_PARTFIND 0x0008 /* partial find done */
+#define CNTXT_NOFIND 0x0010 /* no find done in context */
+#define CNTXT_FINDALL 0x0020 /* Do a find_all on context */
+#define CNTXT_NEWS 0x0040 /* News namespace collection */
+#define CNTXT_SUBDIR 0x0080 /* subdirectory within col'n */
+#define CNTXT_PRESRV 0x0100 /* preserve/restore selected */
+#define CNTXT_ZOOM 0x0200 /* context display narrowed */
+#define CNTXT_INHERIT 0x1000
+
+
+/*
+ * Macros to help users of above two structures...
+ */
+#define NEWS_TEST(c) ((c) && ((c)->use & CNTXT_NEWS))
+
+#define FOLDERS(c) ((c)->dir->folders)
+#define FLDR_NAME(X) ((X) ? ((X)->nickname ? (X)->nickname : (X)->name) :"")
+#define ALL_FOUND(X) (((X)->dir->status & CNTXT_NOFIND) == 0 && \
+ ((X)->dir->status & CNTXT_PARTFIND) == 0)
+
+
+/* exported protoypes */
+
+
+#endif /* PITH_FOLDERTYPE_INCLUDED */
diff --git a/pith/handle.c b/pith/handle.c
new file mode 100644
index 00000000..cf6fec57
--- /dev/null
+++ b/pith/handle.c
@@ -0,0 +1,169 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: handle.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/handle.h"
+#include "../pith/mailview.h"
+
+
+HANDLE_S *
+get_handle(HANDLE_S *handles, int key)
+{
+ HANDLE_S *h;
+
+ if((h = handles) != NULL){
+ for( ; h ; h = h->next)
+ if(h->key == key)
+ return(h);
+
+ for(h = handles->prev ; h ; h = h->prev)
+ if(h->key == key)
+ return(h);
+ }
+
+ return(NULL);
+}
+
+
+void
+init_handles(HANDLE_S **handlesp)
+{
+ if(handlesp)
+ *handlesp = NULL;
+
+ (void) url_external_specific_handler(NULL, 0);
+}
+
+
+
+HANDLE_S *
+new_handle(HANDLE_S **handlesp)
+{
+ HANDLE_S *hp, *h = NULL;
+
+ if(handlesp){
+ h = (HANDLE_S *) fs_get(sizeof(HANDLE_S));
+ memset(h, 0, sizeof(HANDLE_S));
+
+ /* Put it in the list */
+ if((hp = *handlesp) != NULL){
+ while(hp->next)
+ hp = hp->next;
+
+ h->key = hp->key + 1;
+ hp->next = h;
+ h->prev = hp;
+ }
+ else{
+ /* Assumption #2,340: There are NO ZERO KEY HANDLES */
+ h->key = 1;
+ *handlesp = h;
+ }
+ }
+
+ return(h);
+}
+
+
+/*
+ * Normally we ignore the is_used bit in HANDLE_S. However, if we are
+ * using the delete_quotes filter, we pay attention to it. All of the is_used
+ * bits are off by default, and the delete_quotes filter turns them on
+ * if it is including lines with those handles.
+ *
+ * This is a bit of a crock, since it depends heavily on the order of the
+ * filters. Notice that the charset_editorial filter, which comes after
+ * delete_quotes and adds a handle, has to explicitly set the is_used bit!
+ */
+void
+delete_unused_handles(HANDLE_S **handlesp)
+{
+ HANDLE_S *h, *nexth;
+
+ if(handlesp && *handlesp && (*handlesp)->using_is_used){
+ for(h = *handlesp; h && h->prev; h = h->prev)
+ ;
+
+ for(; h; h = nexth){
+ nexth = h->next;
+ if(h->is_used == 0){
+ if(h == *handlesp)
+ *handlesp = nexth;
+
+ free_handle(&h);
+ }
+ }
+ }
+}
+
+
+void
+free_handle(HANDLE_S **h)
+{
+ if(h){
+ if((*h)->next) /* clip from list */
+ (*h)->next->prev = (*h)->prev;
+
+ if((*h)->prev)
+ (*h)->prev->next = (*h)->next;
+
+ if((*h)->type == URL){ /* destroy malloc'd data */
+ if((*h)->h.url.path)
+ fs_give((void **) &(*h)->h.url.path);
+
+ if((*h)->h.url.tool)
+ fs_give((void **) &(*h)->h.url.tool);
+
+ if((*h)->h.url.name)
+ fs_give((void **) &(*h)->h.url.name);
+ }
+
+ free_handle_locations(&(*h)->loc);
+
+ fs_give((void **) h);
+ }
+}
+
+
+void
+free_handles(HANDLE_S **handlesp)
+{
+ HANDLE_S *h;
+
+ if(handlesp && *handlesp){
+ while((h = (*handlesp)->next) != NULL)
+ free_handle(&h);
+
+ while((h = (*handlesp)->prev) != NULL)
+ free_handle(&h);
+
+ free_handle(handlesp);
+ }
+}
+
+
+void
+free_handle_locations(POSLIST_S **l)
+{
+ if(*l){
+ free_handle_locations(&(*l)->next);
+ fs_give((void **) l);
+ }
+}
+
+
diff --git a/pith/handle.h b/pith/handle.h
new file mode 100644
index 00000000..a6f87ada
--- /dev/null
+++ b/pith/handle.h
@@ -0,0 +1,90 @@
+/*
+ * $Id: handle.h 814 2007-11-14 18:39:28Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_HANDLE_INCLUDED
+#define PITH_HANDLE_INCLUDED
+
+
+#include "../pith/context.h"
+#include "../pith/msgno.h"
+#include "../pith/atttype.h"
+#include "../pith/util.h"
+
+
+typedef struct screen_position_list {
+ Pos where;
+ struct screen_position_list *next;
+} POSLIST_S;
+
+
+/*
+ * Struct to help manage embedded urls (and anythin' else we might embed)
+ */
+typedef struct handle_s {
+ int key; /* tag number embedded in text */
+ enum {URL, Attach, Folder, Function, IMG} type;
+ unsigned force_display:1; /* Don't ask before launching */
+ unsigned using_is_used:1; /* bit below is being used */
+ unsigned is_used:1; /* if not, remove it from list */
+ unsigned color_unseen:1; /* we're coloring folders with unseen */
+ unsigned is_dual_do_open:1; /* choosing this handle means open */
+ union {
+ struct { /* URL corresponding to this handle */
+ char *path, /* Actual url string */
+ *tool, /* displaying application */
+ *name; /* URL's NAME attribute */
+ } url; /* stuff to describe URL handle */
+ struct {
+ char *src, /* src of image (CID: only?) */
+ *alt; /* image alternate text */
+ } img; /* stuff to describe img */
+ ATTACH_S *attach; /* Attachment struct for this handle */
+ struct {
+ int index; /* folder's place in context's list */
+ CONTEXT_S *context; /* description of folders */
+ } f; /* stuff to describe Folder handle */
+ struct {
+ struct { /* function and args to pass it */
+ MAILSTREAM *stream;
+ MSGNO_S *msgmap;
+ long msgno;
+ } args;
+ void (*f)(MAILSTREAM *, MSGNO_S *, long);
+ } func;
+ } h;
+ POSLIST_S *loc; /* list of places it exists in text */
+ struct handle_s *next, *prev; /* next and previous in the list */
+} HANDLE_S ;
+
+
+
+/*
+ * Function used to dispatch locally handled URL's
+ */
+typedef int (*url_tool_t)(char *);
+
+
+/* exported protoypes */
+HANDLE_S *get_handle(HANDLE_S *, int);
+void init_handles(HANDLE_S **);
+HANDLE_S *new_handle(HANDLE_S **);
+void delete_unused_handles(HANDLE_S **);
+void free_handle(HANDLE_S **);
+void free_handles(HANDLE_S **);
+void free_handle_locations(POSLIST_S **);
+
+
+#endif /* PITH_HANDLE_INCLUDED */
diff --git a/pith/headers.h b/pith/headers.h
new file mode 100644
index 00000000..f1fe5df7
--- /dev/null
+++ b/pith/headers.h
@@ -0,0 +1,63 @@
+/*
+ * $Id: headers.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#ifndef PITH_HEADERS_INCLUDED
+#define PITH_HEADERS_INCLUDED
+
+
+/*----------------------------------------------------------------------
+ Include files
+ ----*/
+#include <system.h> /* os-dep defs/includes */
+#include <general.h> /* generally useful definitions */
+
+#include "../c-client/mail.h" /* for MAILSTREAM and friends */
+#include "../c-client/osdep.h"
+#include "../c-client/rfc822.h" /* for soutr_t and such */
+#include "../c-client/misc.h" /* for cpystr proto */
+#include "../c-client/utf8.h" /* for CHARSET and such*/
+#include "../c-client/imap4r1.h"
+
+/* include osdep protos and def'ns */
+#include "osdep/bldpath.h"
+#include "osdep/canaccess.h"
+#include "osdep/canonicl.h"
+#include "osdep/collate.h"
+#include "osdep/color.h"
+#include "osdep/coredump.h"
+#include "osdep/creatdir.h"
+#include "osdep/debugtime.h"
+#include "osdep/domnames.h"
+#include "osdep/err_desc.h"
+#include "osdep/fgetpos.h"
+#include "osdep/filesize.h"
+#include "osdep/fnexpand.h"
+#include "osdep/hostname.h"
+#include "osdep/lstcmpnt.h"
+#include "osdep/mimedisp.h"
+#include "osdep/pipe.h"
+#include "osdep/pithosd.h"
+#include "osdep/pw_stuff.h"
+#include "osdep/rename.h"
+#include "osdep/tempfile.h"
+#include "osdep/temp_nam.h"
+#include "osdep/writ_dir.h"
+#include "charconv/utf8.h"
+#include "charconv/filesys.h"
+
+#include "debug.h"
+
+#endif /* PITH_HEADERS_INCLUDED */
diff --git a/pith/help.c b/pith/help.c
new file mode 100644
index 00000000..4e8ed61a
--- /dev/null
+++ b/pith/help.c
@@ -0,0 +1,369 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: help.c 900 2008-01-05 01:13:26Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/help.h"
+#include "../pith/flag.h"
+#include "../pith/conf.h"
+#include "../pith/sort.h"
+
+
+REV_MSG_S rmjoarray[RMJLEN]; /* For regular journal */
+REV_MSG_S rmloarray[RMLLEN]; /* debug 0-4 */
+REV_MSG_S rmhiarray[RMHLEN]; /* debug 5-9 */
+int rmjofirst = -1, rmjolast = -1;
+int rmlofirst = -1, rmlolast = -1;
+int rmhifirst = -1, rmhilast = -1;
+int rm_not_right_now;
+
+
+
+HelpType
+help_name2section(char *url, int url_len)
+{
+ char name[256];
+ HelpType newhelp = NO_HELP;
+ struct help_texts *t;
+
+ snprintf(name, sizeof(name), "%.*s", MIN(url_len,sizeof(name)), url);
+
+ for(t = h_texts; t->help_text != NO_HELP; t++)
+ if(!strucmp(t->tag, name)){
+ newhelp = t->help_text;
+ break;
+ }
+
+ return(newhelp);
+}
+
+
+char *
+get_alpine_revision_string(char *buf, size_t nbuf)
+{
+ char ourbuf[100], *p;
+ char *rev = NULL;
+
+ buf[0] = '\0';
+ ourbuf[0] = '\0';
+
+ /* HelpType (the type of h_revision) is assumed to be char ** */
+ if(h_revision && h_revision[0] && h_revision[0][0]){
+ strncpy(ourbuf, h_revision[0], sizeof(ourbuf)-1);
+ ourbuf[sizeof(ourbuf)-1] = '\0';
+ }
+
+ if(ourbuf[0]){
+ /* move to revision number */
+ for(p = ourbuf; *p && !isdigit((unsigned char) (*p)); p++)
+ ;
+
+ if(*p)
+ rev = p;
+
+ if(rev){
+ /* skip to following space */
+ for(; *p && !isspace((unsigned char) (*p)); p++)
+ ;
+
+ /* skip whitespace */
+ for(; *p && isspace((unsigned char) (*p)); p++)
+ ;
+
+ /* skip over date to following space */
+ for(; *p && !isspace((unsigned char) (*p)); p++)
+ ;
+
+ strncpy(buf, rev, MIN(p-rev, nbuf-1));
+ buf[MIN(p-rev,nbuf-1)] = '\0';
+ }
+ }
+
+ return(buf);
+}
+
+
+char *
+get_alpine_revision_number(char *buf, size_t nbuf)
+{
+ char ourbuf[100], *p;
+ char *rev = NULL;
+
+ buf[0] = '\0';
+ ourbuf[0] = '\0';
+
+ /* HelpType (the type of h_revision) is assumed to be char ** */
+ if(h_revision && h_revision[0] && h_revision[0][0]){
+ strncpy(ourbuf, h_revision[0], sizeof(ourbuf)-1);
+ ourbuf[sizeof(ourbuf)-1] = '\0';
+ }
+
+ if(ourbuf[0]){
+ /* move to revision number */
+ for(p = ourbuf; *p && !isdigit((unsigned char) (*p)); p++)
+ ;
+
+ if(*p)
+ rev = p;
+
+ if(rev){
+ /* skip to following space */
+ for(; *p && !isspace((unsigned char) (*p)); p++)
+ ;
+
+ strncpy(buf, rev, MIN(p-rev, nbuf-1));
+ buf[MIN(p-rev,nbuf-1)] = '\0';
+ }
+ }
+
+ return(buf);
+}
+
+
+#ifdef DEBUG
+
+void
+debugjournal_to_file(FILE *dfile)
+{
+ int donejo, donelo, donehi, jo, lo, hi;
+ RMCat rmcat;
+
+ if(dfile && (rmjofirst >= 0 || rmlofirst >= 0 || rmhifirst >= 0)
+ && rmjofirst < RMJLEN && rmjolast < RMJLEN
+ && rmlofirst < RMLLEN && rmlolast < RMLLEN
+ && rmhifirst < RMHLEN && rmhilast < RMHLEN
+ && (rmjofirst < 0 || rmjolast >= 0)
+ && (rmlofirst < 0 || rmlolast >= 0)
+ && (rmhifirst < 0 || rmhilast >= 0)){
+
+ donejo = donehi = donelo = 0;
+ jo = rmjofirst;
+ if(jo < 0)
+ donejo = 1;
+
+ lo = rmlofirst;
+ if(lo < 0)
+ donelo = 1;
+
+ hi = rmhifirst;
+ if(hi < 0)
+ donehi = 1;
+
+ while(!(donejo && donelo && donehi)){
+ REV_MSG_S *pjo, *plo, *phi, *p;
+
+ if(!donejo)
+ pjo = &rmjoarray[jo];
+ else
+ pjo = NULL;
+
+ if(!donelo)
+ plo = &rmloarray[lo];
+ else
+ plo = NULL;
+
+ if(!donehi)
+ phi = &rmhiarray[hi];
+ else
+ phi = NULL;
+
+ if(pjo && (!plo || pjo->seq <= plo->seq)
+ && (!phi || pjo->seq <= phi->seq))
+ rmcat = Jo;
+ else if(plo && (!phi || plo->seq <= phi->seq))
+ rmcat = Lo;
+ else if(phi)
+ rmcat = Hi;
+ else
+ rmcat = No;
+
+ if(rmcat == Jo){
+ p = pjo;
+ if(jo == rmjofirst &&
+ (((rmjolast + 1) % RMJLEN) == rmjofirst) &&
+ fputs("*** Level -1 entries prior to this are deleted", dfile) == EOF)
+ break;
+ }
+ else if(rmcat == Lo){
+ p = plo;
+ if(lo == rmlofirst &&
+ (((rmlolast + 1) % RMLLEN) == rmlofirst) &&
+ fputs("*** Level 0-4 entries prior to this are deleted", dfile) == EOF)
+ break;
+ }
+ else if(rmcat == Hi){
+ p = phi;
+ if(hi == rmhifirst &&
+ (((rmhilast + 1) % RMHLEN) == rmhifirst) &&
+ fputs("*** Level 5-9 entries prior to this are deleted", dfile) == EOF)
+ break;
+ }
+ else if(rmcat == No){
+ p = NULL;
+ }
+
+ if(p){
+ if(p->timestamp && p->timestamp[0]
+ && (fputs(p->timestamp, dfile) == EOF
+ || fputs(": ", dfile) == EOF))
+ break;
+
+ if(p->message && p->message[0]
+ && (fputs(p->message, dfile) == EOF
+ || fputs("\n", dfile) == EOF))
+ break;
+ }
+
+ switch(rmcat){
+ case Jo:
+ if(jo == rmjolast)
+ donejo++;
+ else
+ jo = (jo + 1) % RMJLEN;
+
+ break;
+
+ case Lo:
+ if(lo == rmlolast)
+ donelo++;
+ else
+ lo = (lo + 1) % RMLLEN;
+
+ break;
+
+ case Hi:
+ if(hi == rmhilast)
+ donehi++;
+ else
+ hi = (hi + 1) % RMHLEN;
+
+ break;
+
+ default:
+ donejo++;
+ donelo++;
+ donehi++;
+ break;
+ }
+ }
+ }
+}
+
+#endif /* DEBUG */
+
+
+/*----------------------------------------------------------------------
+ Add a message to the circular status message review buffer
+
+ Args: message -- The message to add
+ -----*/
+void
+add_review_message(char *message, int level)
+{
+ int next_is_continuation = 0, cur_is_continuation = 0;
+ char *p, *q;
+ static unsigned long rmseq = 0L;
+
+ if(rm_not_right_now || !(message && *message))
+ return;
+
+ /*
+ * Debug output can have newlines in it, so split up each newline piece
+ * by hand and make them separate messages.
+ */
+ rm_not_right_now = 1;
+ for(p = message; *p; p = (*q && !next_is_continuation) ? q+1 : q){
+ for(q = p; *q && *q != '\n' && (q-p) < RMMSGLEN; q++)
+ ;
+
+ if(p == q)
+ continue;
+
+ cur_is_continuation = next_is_continuation;
+
+ if((q-p) == RMMSGLEN && *q && *q != '\n')
+ next_is_continuation = 1;
+ else
+ next_is_continuation = 0;
+
+ if(level < 0){
+ if(rmjofirst < 0){
+ rmjofirst = 0;
+ rmjolast = 0;
+ }
+ else{
+ rmjolast = (rmjolast + 1) % RMJLEN;
+ if(rmjolast == rmjofirst)
+ rmjofirst = (rmjofirst + 1) % RMJLEN;
+ }
+
+ rmjoarray[rmjolast].level = (short) level;
+ rmjoarray[rmjolast].seq = rmseq++;
+ rmjoarray[rmjolast].continuation = cur_is_continuation ? 1 : 0;
+ memset(rmjoarray[rmjolast].message, 0, (RMMSGLEN+1)*sizeof(char));
+ strncpy(rmjoarray[rmjolast].message, p, MIN(q-p,RMMSGLEN));
+#ifdef DEBUG
+ memset(rmjoarray[rmjolast].timestamp, 0, (RMTIMLEN+1)*sizeof(char));
+ strncpy(rmjoarray[rmjolast].timestamp, debug_time(0,1), RMTIMLEN);
+#endif
+ }
+ else if(level <= 4){
+ if(rmlofirst < 0){
+ rmlofirst = 0;
+ rmlolast = 0;
+ }
+ else{
+ rmlolast = (rmlolast + 1) % RMLLEN;
+ if(rmlolast == rmlofirst)
+ rmlofirst = (rmlofirst + 1) % RMLLEN;
+ }
+
+ rmloarray[rmlolast].level = (short) level;
+ rmloarray[rmlolast].seq = rmseq++;
+ rmloarray[rmlolast].continuation = cur_is_continuation ? 1 : 0;
+ memset(rmloarray[rmlolast].message, 0, (RMMSGLEN+1)*sizeof(char));
+ strncpy(rmloarray[rmlolast].message, p, MIN(q-p,RMMSGLEN));
+#ifdef DEBUG
+ memset(rmloarray[rmlolast].timestamp, 0, (RMTIMLEN+1)*sizeof(char));
+ strncpy(rmloarray[rmlolast].timestamp, debug_time(0,1), RMTIMLEN);
+#endif
+ }
+ else{
+ if(rmhifirst < 0){
+ rmhifirst = 0;
+ rmhilast = 0;
+ }
+ else{
+ rmhilast = (rmhilast + 1) % RMHLEN;
+ if(rmhilast == rmhifirst)
+ rmhifirst = (rmhifirst + 1) % RMHLEN;
+ }
+
+ rmhiarray[rmhilast].level = (short) level;
+ rmhiarray[rmhilast].seq = rmseq++;
+ rmhiarray[rmhilast].continuation = cur_is_continuation ? 1 : 0;
+ memset(rmhiarray[rmhilast].message, 0, (RMMSGLEN+1)*sizeof(char));
+ strncpy(rmhiarray[rmhilast].message, p, MIN(q-p,RMMSGLEN));
+#ifdef DEBUG
+ memset(rmhiarray[rmhilast].timestamp, 0, (RMTIMLEN+1)*sizeof(char));
+ strncpy(rmhiarray[rmhilast].timestamp, debug_time(0,1), RMTIMLEN);
+#endif
+ }
+ }
+
+ rm_not_right_now = 0;
+}
diff --git a/pith/help.h b/pith/help.h
new file mode 100644
index 00000000..fb3bcd24
--- /dev/null
+++ b/pith/help.h
@@ -0,0 +1,61 @@
+/*
+ * $Id: help.h 900 2008-01-05 01:13:26Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_HELP_INCLUDED
+#define PITH_HELP_INCLUDED
+
+
+#include "../pith/state.h"
+#include "../pith/filttype.h"
+#include "../pith/helptext.h"
+
+
+#define RMMSGLEN 120
+#define RMTIMLEN 15
+#define RMJLEN 500
+#define RMLLEN 2000
+#define RMHLEN 2000
+
+typedef struct _rev_msg {
+ unsigned long seq;
+ short level; /* -1 for journal, debuglevel for dprint */
+ unsigned continuation:1;
+ char message[RMMSGLEN+1];
+ char timestamp[RMTIMLEN+1];
+} REV_MSG_S;
+
+
+typedef enum {No, Jo, Lo, Hi} RMCat;
+
+extern REV_MSG_S rmjoarray[RMJLEN]; /* For regular journal */
+extern REV_MSG_S rmloarray[RMLLEN]; /* debug 0-4 */
+extern REV_MSG_S rmhiarray[RMHLEN]; /* debug 5-9 */
+extern int rmjofirst, rmjolast;
+extern int rmlofirst, rmlolast;
+extern int rmhifirst, rmhilast;
+extern int rm_not_right_now;
+
+
+/* exported protoypes */
+HelpType help_name2section(char *, int);
+void debugjournal_to_file(FILE *);
+void add_review_message(char *, int);
+int gripe_gripe_to(char *);
+char *get_alpine_revision_string(char *, size_t);
+char *get_alpine_revision_number(char *, size_t);
+
+
+#endif /* PITH_HELP_INCLUDED */
diff --git a/pith/help_c_gen.c b/pith/help_c_gen.c
new file mode 100644
index 00000000..77ae2ed9
--- /dev/null
+++ b/pith/help_c_gen.c
@@ -0,0 +1,291 @@
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+
+typedef struct helplist {
+ char *name;
+ struct helplist *next;
+} HELPLIST_S;
+
+
+HELPLIST_S *help_list;
+
+
+void preamble(FILE *ofp);
+void body(FILE *ifp, FILE *ofp);
+char *quote_clean(char *rawline);
+int only_tags(char *line);
+int append_to_help_list(HELPLIST_S **, char *new);
+void print_help_list(HELPLIST_S *, FILE *fp);
+
+
+int
+main(int argc, char **argv)
+{
+ preamble(stdout);
+ body(stdin, stdout);
+ exit(0);
+}
+
+
+void
+preamble(FILE *ofp)
+{
+ fprintf(ofp, "\n\t\t/*\n");
+ fprintf(ofp, "\t\t * AUTMATICALLY GENERATED FILE!\n");
+ fprintf(ofp, "\t\t * DO NOT EDIT!!\n");
+ fprintf(ofp, "\t\t * See help_c_gen.c.\n\t\t */\n\n\n");
+ fprintf(ofp, "#include <stdio.h>\n#include \"headers.h\"\n#include \"helptext.h\"\n\n");
+}
+
+
+void
+body(FILE *ifp, FILE *ofp)
+{
+ char rawline[10000];
+ char *line;
+#define SPACE ' '
+ char *p, *helpname;
+ int in_text = 0, new_topic = 0, first_one = 1, justtags;
+
+ while(fgets(rawline, sizeof(rawline), ifp) != NULL){
+ if(rawline[0] == '#')
+ continue;
+
+ line = quote_clean(rawline);
+
+ if(!line){
+ /*
+ * Put errors in result so that it will cause a compile
+ * error and be noticed.
+ */
+ fprintf(ofp, "Error: quote_clean returns NULL for help line\n %s\n", rawline);
+ exit(-1);
+ }
+
+ justtags = 0;
+ if(!strncmp(line, "====", 4)){
+ p = line;
+ /* skip to first space */
+ while(*p && *p != SPACE)
+ p++;
+
+ if(!*p){
+ fprintf(ofp, "Error: help input line\n %s\n No space after ====\n", rawline);
+ exit(-1);
+ }
+
+ /* skip spaces */
+ while(*p && *p == SPACE)
+ p++;
+
+ if(!*p){
+ fprintf(ofp, "Error: help input line\n %s\n Missing helpname after ====\n", rawline);
+ exit(-1);
+ }
+
+ helpname = p;
+
+ /* skip to next space */
+ while(*p && *p != SPACE)
+ p++;
+
+ *p = '\0'; /* tie off helpname */
+
+ /* finish previous one */
+ if(in_text)
+ fprintf(ofp, "NULL\n};\n\n\n");
+
+ in_text = new_topic = 1;
+
+ fprintf(ofp, "char *%s[] = {\n", helpname);
+
+ if(append_to_help_list(&help_list, helpname) < 0){
+ fprintf(ofp, "Error: Can't allocate memory for help_list after line\n %s\n", rawline);
+ exit(-1);
+ }
+ }
+ else if(line[0] == '\0'){
+ if(in_text)
+ fprintf(ofp, "\" \",\n"); /* why the space? */
+ }
+ else if(only_tags(line)){
+ if(in_text){
+ fprintf(ofp, "\"%s\",\n", line);
+ justtags = 1;
+ }
+ }
+
+ if(line[0] && line[0] != '='){
+ if(in_text && !justtags){
+ if(first_one){
+ first_one = 0;
+ fprintf(ofp, "/*\n");
+ fprintf(ofp, "TRANSLATORS: The translation strings for pith/helptext.c\n");
+ fprintf(ofp, "are automatically generated by a script from the help\n");
+ fprintf(ofp, "text in pith/pine.hlp. This means that the translation job for\n");
+ fprintf(ofp, "the help text is particularly difficult.\n");
+ fprintf(ofp, "This is HTML source so please leave the text inside HTML tags untranslated.\n");
+ fprintf(ofp, "HTML tags like <LI> or <TITLE> should, of course, be left untranslated.\n");
+ fprintf(ofp, "Special HTML characters like &lt; (less than character) should be left alone.\n");
+ fprintf(ofp, "Alpine option names are short phrases with the words separated by\n");
+ fprintf(ofp, "dashes. An example of an option name is Quell-Extra-Post-Prompt.\n");
+ fprintf(ofp, "Option names should not be translated.\n");
+ fprintf(ofp, "The file pith/helptext.c contains many separate help topics.\n");
+ fprintf(ofp, "Some of them are very short and some are long. If left unsorted the\n");
+ fprintf(ofp, "text for a single topic is together in the translation file. The start\n");
+ fprintf(ofp, "of each new topic is marked by the comment\n");
+ fprintf(ofp, "TRANSLATORS: Start of new help topic.\n");
+ fprintf(ofp, "*/\n");
+ }
+ else if(new_topic){
+ new_topic = 0;
+ fprintf(ofp, "/* TRANSLATORS: Start of new help topic. */\n");
+ }
+
+ fprintf(ofp, "N_(\"%s\"),\n", line);
+ }
+ else{
+ ; /* skip leading cruft */
+ }
+ }
+ }
+
+ if(in_text)
+ fprintf(ofp, "NULL\n};\n\n\n");
+
+ print_help_list(help_list, ofp);
+}
+
+
+char *
+quote_clean(char *rawline)
+{
+ char *p, *q, *cleaned = NULL;
+ size_t len;
+
+ if(rawline){
+ len = strlen(rawline);
+ cleaned = (char *) malloc((2*len+1) * sizeof(char));
+
+ if(cleaned){
+ p = rawline;
+ q = cleaned;
+
+ while(*p && *p != '\n'){
+ if(*p == '"' && !(p > rawline && *(p-1) == '\\'))
+ *q++ = '\\';
+
+ *q++ = *p++;
+ }
+
+ *q = '\0';
+ }
+ }
+
+ return cleaned;
+}
+
+
+int
+only_tags(char *line)
+{
+ char *p;
+ int is_tags = 1; /* only tags seen so far */
+
+ if(!line)
+ return 0;
+
+ p = line;
+
+ while(is_tags && *p){
+ /* leading space before a tag */
+ while(*p && isspace(*p))
+ p++;
+
+ if(*p == '<'){
+ p++;
+ /* skip through interior of tag */
+ while(*p && *p != '<' && *p != '>')
+ p++;
+
+ if(*p == '>'){
+ p++;
+ /* trailing space after tag */
+ while(*p && isspace(*p))
+ p++;
+ }
+ else
+ is_tags = 0;
+ }
+ else if(*p)
+ is_tags = 0;
+ }
+
+ return is_tags;
+}
+
+
+int
+append_to_help_list(HELPLIST_S **head, char *name)
+{
+ HELPLIST_S *new, *h;
+ size_t len;
+
+ if(!(name && *name && head))
+ return 0;
+
+ new = (HELPLIST_S *) malloc(sizeof(*new));
+ if(!new)
+ return -1;
+
+ memset(new, 0, sizeof(*new));
+ len = strlen(name);
+ new->name = (char *) malloc((len+1) * sizeof(char));
+ strncpy(new->name, name, len);
+ new->name[len] = '\0';
+
+ if(*head){
+ for(h = *head; h->next; h = h->next)
+ ;
+
+ h->next = new;
+ }
+ else
+ *head = new;
+
+ return 0;
+}
+
+
+void
+print_help_list(HELPLIST_S *head, FILE *fp)
+{
+ HELPLIST_S *h;
+
+ if(head){
+ fprintf(fp, "struct help_texts h_texts[] = {\n");
+
+ for(h = head; h; h = h->next)
+ if(h->name && h->name[0])
+ fprintf(fp, "{%s,\"%s\"},\n", h->name, h->name);
+
+ fprintf(fp, "{NO_HELP, NULL}\n};\n");
+ }
+}
diff --git a/pith/help_h_gen.c b/pith/help_h_gen.c
new file mode 100644
index 00000000..d858b0fb
--- /dev/null
+++ b/pith/help_h_gen.c
@@ -0,0 +1,92 @@
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+void preamble(FILE *ofp);
+void body(FILE *ifp, FILE *ofp);
+void postamble(FILE *ofp);
+
+
+int
+main(int argc, char **argv)
+{
+ preamble(stdout);
+ body(stdin, stdout);
+ postamble(stdout);
+ exit(0);
+}
+
+
+void
+preamble(FILE *ofp)
+{
+ fprintf(ofp, "\n\t\t/*\n");
+ fprintf(ofp, "\t\t * AUTMATICALLY GENERATED FILE!\n");
+ fprintf(ofp, "\t\t * DO NOT EDIT!!\n");
+ fprintf(ofp, "\t\t * See help_h_gen.c.\n\t\t */\n\n\n");
+ fprintf(ofp, "#ifndef PITH_HELPTEXT_INCLUDED\n");
+ fprintf(ofp, "#define PITH_HELPTEXT_INCLUDED\n\n\n");
+ fprintf(ofp, "#define\tHelpType\tchar **\n");
+ fprintf(ofp, "#define\tNO_HELP\t((char **) NULL)\n\n");
+ fprintf(ofp, "struct help_texts {\n");
+ fprintf(ofp, " HelpType help_text;\n");
+ fprintf(ofp, " char *tag;\n};\n\n");
+}
+
+
+void
+body(FILE *ifp, FILE *ofp)
+{
+ char line[10000];
+ char *space = " ";
+ char *p;
+
+ while(fgets(line, sizeof(line), ifp) != NULL){
+ if(!strncmp(line, "====", 4)){
+ p = strtok(line, space);
+ if(p){
+ p = strtok(NULL, space);
+ if(p){
+ if(isalpha(*p))
+ fprintf(ofp, "extern char *%s[];\n", p);
+ else{
+ fprintf(ofp, "Error: help input line\n %s\nis bad\n", line);
+ exit(-1);
+ }
+ }
+ else{
+ fprintf(ofp, "Error: help input\n %scontains ==== without following helpname\n", line);
+ exit(-1);
+ }
+
+ }
+ else{
+ fprintf(ofp, "Error: help input\n %scontains ==== without following space\n", line);
+ exit(-1);
+ }
+ }
+ }
+}
+
+
+void
+postamble(FILE *ofp)
+{
+ fprintf(ofp, "\nextern struct help_texts h_texts[];\n\n\n");
+ fprintf(ofp, "#endif /* PITH_HELPTEXT_INCLUDED */\n");
+}
diff --git a/pith/helpindx.c b/pith/helpindx.c
new file mode 100644
index 00000000..20cb9273
--- /dev/null
+++ b/pith/helpindx.c
@@ -0,0 +1,132 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: helpindx.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*
+ * very short, very specialized
+ *
+ *
+ *
+ */
+
+#include "system.h"
+
+#define HELP_KEY_MAX 64 /* maximum length of a key */
+
+struct hindx {
+ char key[HELP_KEY_MAX]; /* name of help section */
+ long offset; /* where help text starts */
+ short lines; /* how many lines there are */
+};
+
+int
+main(int argc, char **argv)
+{
+ char *p, s[1024];
+ long index;
+ int section,
+ len,
+ line,
+ i;
+ FILE *hp,
+ *hip, /* help index ptr */
+ *hhp; /* help header ptr */
+ struct hindx irec;
+
+ if(argc < 4){
+ fprintf(stderr,
+ "usage: helpindx <help_file> <index_file> <header_file>\n");
+ exit(-1);
+ }
+
+ if((hp = fopen(argv[1], "rb")) == NULL){ /* problems */
+ perror(argv[1]);
+ exit(-1);
+ }
+
+ if((hip = fopen(argv[2], "wb")) == NULL){ /* problems */
+ perror(argv[2]);
+ exit(-1);
+ }
+
+ if((hhp = fopen(argv[3], "w")) == NULL){ /* problems */
+ perror(argv[3]);
+ exit(-1);
+ }
+
+ fprintf(hhp,"/*\n * Alpine Help text header file\n */\n");
+ fprintf(hhp,"\n#ifndef PITH_HELPTEXT_INCLUDED\n#define PITH_HELPTEXT_INCLUDED\n");
+ fprintf(hhp,"\n#define\tHELP_KEY_MAX\t%d\n", HELP_KEY_MAX);
+ fprintf(hhp,"\ntypedef\tshort\tHelpType;\n");
+ fprintf(hhp,"\n#define\tNO_HELP\t(-1)\n");
+ fprintf(hhp,"struct hindx {\n char key[HELP_KEY_MAX];");
+ fprintf(hhp,"\t\t/* name of help section */\n");
+ fprintf(hhp," long offset;\t\t\t/* where help text starts */\n");
+ fprintf(hhp," short lines;\t\t\t/* how many lines there are */\n");
+ fprintf(hhp,"};\n\n\n/*\n * defs for help section titles\n */\n");
+
+ index = 0L;
+ line = section = 0;
+
+ while(fgets(s, sizeof(s) - 1, hp) != NULL){
+ line++;
+ len = strlen(s);
+ if(s[0] == '='){ /* new section? */
+ i = 0;
+ while((s[i] == '=' || isspace((unsigned char)s[i])) && i < len)
+ i++;
+
+ if(section)
+ fwrite(&irec, sizeof(struct hindx), 1, hip);
+
+ irec.offset = index + (long)i; /* save where name starts */
+ irec.lines = 0;
+ p = &irec.key[0]; /* save name field */
+ while(!isspace((unsigned char)s[i]) && i < len)
+ *p++ = s[i++];
+ *p = '\0';
+
+ if(irec.key[0] == '\0'){
+ fprintf(stderr,"Invalid help line %d: %s", line, s);
+ exit(-1);
+ }
+ else
+ fprintf(hhp, "#define\t%s\t%d\n", irec.key, section++);
+
+ }
+ else if(s[0] == '#' && section){
+ fprintf(stderr,"Comments not allowed in help text: line %d", line);
+ exit(-1);
+ }
+ else{
+ irec.lines++;
+ }
+ index += len;
+ }
+
+ if(section) /* write last entry */
+ fwrite(&irec, sizeof(struct hindx), 1, hip);
+
+ fprintf(hhp, "#define\tLASTHELP\t%d\n", section);
+
+ fprintf(hhp,"\n#endif /* PITH_HELPTEXT_INCLUDED */\n");
+
+ fclose(hp);
+ fclose(hip);
+ fclose(hhp);
+ exit(0);
+}
diff --git a/pith/hist.c b/pith/hist.c
new file mode 100644
index 00000000..18a132a1
--- /dev/null
+++ b/pith/hist.c
@@ -0,0 +1,218 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: hist.c 807 2007-11-09 01:21:33Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#include "../pith/headers.h"
+#include "../pith/conf.h"
+#include "../pith/hist.h"
+
+
+void
+init_hist(HISTORY_S **history, int histsize)
+{
+ size_t l;
+
+ if(!history)
+ return;
+
+ if(!*history){
+ l = sizeof(**history) + histsize * sizeof(ONE_HIST_S);
+ *history = (HISTORY_S *) fs_get(l);
+ memset(*history, 0, l);
+ (*history)->histsize = histsize;
+ (*history)->origindex = histsize - 1;
+ add_to_histlist(history);
+ }
+
+ (*history)->curindex = (*history)->origindex;
+}
+
+
+void
+free_hist(HISTORY_S **history)
+{
+ int i;
+
+ if(history && *history){
+
+ for(i = 0; i < (*history)->histsize; i++)
+ if((*history)->hist[i] && (*history)->hist[i]->str)
+ fs_give((void **) &(*history)->hist[i]->str);
+
+ fs_give((void **) history);
+ }
+}
+
+
+char *
+get_prev_hist(HISTORY_S *history, char *savethis, unsigned saveflags, void *cntxt)
+{
+ int nextcurindex;
+ size_t l;
+
+ if(!(history && history->histsize > 0))
+ return NULL;
+
+ nextcurindex = (history->curindex + 1) % history->histsize;
+
+ /* already at start of history */
+ if(nextcurindex == history->origindex
+ || !(history->hist[nextcurindex] && history->hist[nextcurindex]->str
+ && history->hist[nextcurindex]->str[0]))
+ return NULL;
+
+ /* save what user typed */
+ if(history->curindex == history->origindex){
+ if(!savethis)
+ savethis = "";
+
+ if(!history->hist[history->origindex]){
+ history->hist[history->origindex] = (ONE_HIST_S *) fs_get(sizeof(ONE_HIST_S));
+ memset(history->hist[history->origindex], 0, sizeof(ONE_HIST_S));
+ }
+
+ if(history->hist[history->origindex]->str){
+ if(strlen(history->hist[history->origindex]->str) < (l=strlen(savethis)))
+ fs_resize((void **) &history->hist[history->origindex]->str, l+1);
+
+ strncpy(history->hist[history->origindex]->str, savethis, l+1);
+ history->hist[history->origindex]->str[l] = '\0';
+ }
+ else
+ history->hist[history->origindex]->str = cpystr(savethis);
+
+ history->hist[history->origindex]->flags = saveflags;
+
+ history->hist[history->origindex]->cntxt = cntxt;
+ }
+
+ history->curindex = nextcurindex;
+
+ return((history->hist[history->curindex] && history->hist[history->curindex]->str)
+ ? history->hist[history->curindex]->str : NULL);
+}
+
+
+char *
+get_next_hist(HISTORY_S *history, char *savethis, unsigned saveflags, void *cntxt)
+{
+ if(!(history && history->histsize > 0))
+ return NULL;
+
+ /* already at end (most recent) of history */
+ if(history->curindex == history->origindex)
+ return NULL;
+
+ history->curindex = (history->curindex + history->histsize - 1) % history->histsize;
+
+ return((history->hist[history->curindex] && history->hist[history->curindex]->str)
+ ? history->hist[history->curindex]->str : NULL);
+}
+
+
+void
+save_hist(HISTORY_S *history, char *savethis, unsigned saveflags, void *cntxt)
+{
+ size_t l;
+ int plusone;
+
+ if(!(history && history->histsize > 0))
+ return;
+
+ plusone = (history->origindex + 1) % history->histsize;
+
+ if(!history->hist[history->origindex]){
+ history->hist[history->origindex] = (ONE_HIST_S *) fs_get(sizeof(ONE_HIST_S));
+ memset(history->hist[history->origindex], 0, sizeof(ONE_HIST_S));
+ }
+
+ if(savethis && savethis[0]
+ && (!history->hist[history->origindex]->str
+ || strcmp(history->hist[history->origindex]->str, savethis)
+ || history->hist[history->origindex]->flags != saveflags
+ || history->hist[history->origindex]->cntxt != cntxt)
+ && !(history->hist[plusone] && history->hist[plusone]->str
+ && !strcmp(history->hist[plusone]->str, savethis)
+ && history->hist[history->origindex]->flags == saveflags
+ && history->hist[history->origindex]->cntxt == cntxt)){
+ if(history->hist[history->origindex]->str){
+ if(strlen(history->hist[history->origindex]->str) < (l=strlen(savethis)))
+ fs_resize((void **) &history->hist[history->origindex]->str, l+1);
+
+ strncpy(history->hist[history->origindex]->str, savethis, l+1);
+ history->hist[history->origindex]->str[l] = '\0';
+ }
+ else{
+ history->hist[history->origindex]->str = cpystr(savethis);
+ }
+
+ history->hist[history->origindex]->flags = saveflags;
+ history->hist[history->origindex]->cntxt = cntxt;
+
+ history->origindex = (history->origindex + history->histsize - 1) % history->histsize;
+ if(history->hist[history->origindex] && history->hist[history->origindex]->str)
+ history->hist[history->origindex]->str[0] = '\0';
+ }
+}
+
+
+/*
+ * Returns count of items entered into history.
+ */
+int
+items_in_hist(HISTORY_S *history)
+{
+ int i, cnt = 0;
+
+ if(history && history->histsize > 0)
+ for(i = 0; i < history->histsize; i++)
+ if(history->hist[i] && history->hist[i]->str)
+ cnt++;
+
+ return(cnt);
+}
+
+
+static HISTORY_S **histlist[100];
+
+void
+add_to_histlist(HISTORY_S **history)
+{
+ int i;
+
+ if(history){
+ /* find empty slot */
+ for(i = 0; i < 100; i++)
+ if(!histlist[i])
+ break;
+
+ if(i < 100)
+ histlist[i] = history;
+ }
+}
+
+
+void
+free_histlist(void)
+{
+ int i;
+
+ for(i = 0; i < 100; i++)
+ if(histlist[i])
+ free_hist(histlist[i]);
+}
diff --git a/pith/hist.h b/pith/hist.h
new file mode 100644
index 00000000..b3a2b421
--- /dev/null
+++ b/pith/hist.h
@@ -0,0 +1,53 @@
+/*
+ * $Id: hist.h 768 2007-10-24 00:10:03Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_HIST_INCLUDED
+#define PITH_HIST_INCLUDED
+
+
+#define HISTSIZE (20+1)
+
+typedef struct one_hist {
+ char *str;
+ unsigned flags;
+ void *cntxt;
+} ONE_HIST_S;
+
+
+typedef struct history_s {
+ int histsize;
+ int origindex;
+ int curindex;
+ ONE_HIST_S *hist[1]; /* has size histsize */
+} HISTORY_S;
+
+
+#define HISTORY_UP_KEYNAME "Up"
+#define HISTORY_DOWN_KEYNAME "Down"
+#define HISTORY_KEYLABEL N_("History")
+
+
+void init_hist(HISTORY_S **, int);
+void free_hist(HISTORY_S **);
+char *get_prev_hist(HISTORY_S *, char *, unsigned, void *);
+char *get_next_hist(HISTORY_S *, char *, unsigned, void *);
+void save_hist(HISTORY_S *, char *, unsigned, void *);
+int items_in_hist(HISTORY_S *);
+void add_to_histlist(HISTORY_S **);
+void free_histlist(void);
+
+
+#endif /* PITH_HIST_INCLUDED */
diff --git a/pith/icache.c b/pith/icache.c
new file mode 100644
index 00000000..fc2a1eb8
--- /dev/null
+++ b/pith/icache.c
@@ -0,0 +1,452 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: icache.c 874 2007-12-15 02:51:06Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/icache.h"
+#include "../pith/mailindx.h"
+#include "../pith/flag.h"
+#include "../pith/msgno.h"
+#include "../pith/status.h"
+#include "../pith/pineelt.h"
+
+/*
+ * Internal prototypes
+ */
+
+
+/*
+ * * * * Index entry cache manager * * *
+ */
+
+
+/*
+ * Erase a particular entry in the cache.
+ */
+void
+clear_index_cache_ent(MAILSTREAM *stream, long int msgno, unsigned int flags)
+{
+ long rawno = -1L;
+ PINELT_S **peltp;
+ MESSAGECACHE *mc;
+
+ if(stream){
+ if(flags && IC_USE_RAW_MSGNO)
+ rawno = msgno;
+ else
+ rawno = mn_m2raw(sp_msgmap(stream), msgno);
+
+ if(rawno > 0L && rawno <= stream->nmsgs){
+ mc = mail_elt(stream, rawno);
+ if(mc && mc->sparep){
+ peltp = (PINELT_S **) &mc->sparep;
+ if((*peltp)->ice){
+ /*
+ * This is intended to be a lightweight reset of
+ * just the widths and print_format strings. For example,
+ * the width of the screen changed and nothing else.
+ * We simply unset the widths_done bit and it
+ * is up to the drawer to free and recalculate the
+ * print_format strings and to reset the widths.
+ *
+ * The else case is a clear of the entire cache entry
+ * leaving behind only the empty structure.
+ */
+ if(flags & IC_CLEAR_WIDTHS_DONE){
+ (*peltp)->ice->widths_done = 0;
+
+ /* also zero out hash value */
+ (*peltp)->ice->id = 0;
+
+ if((*peltp)->ice->tice){
+ (*peltp)->ice->tice->widths_done = 0;
+
+ /* also zero out hash value */
+ (*peltp)->ice->tice->id = 0;
+ }
+ }
+ else
+ clear_ice(&(*peltp)->ice);
+ }
+ }
+ }
+ }
+}
+
+
+void
+clear_index_cache(MAILSTREAM *stream, unsigned int flags)
+{
+ long rawno;
+
+ if(stream){
+ set_need_format_setup(stream);
+ for(rawno = 1L; rawno <= stream->nmsgs; rawno++)
+ clear_index_cache_ent(stream, rawno, flags | IC_USE_RAW_MSGNO);
+ }
+}
+
+
+void
+clear_index_cache_for_thread(MAILSTREAM *stream, PINETHRD_S *thrd, MSGNO_S *msgmap)
+{
+ unsigned long msgno;
+
+ if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs)
+ return;
+
+ msgno = mn_raw2m(msgmap, thrd->rawno);
+
+ clear_index_cache_ent(stream, msgno, 0);
+
+ if(thrd->next)
+ clear_index_cache_for_thread(stream, fetch_thread(stream, thrd->next),
+ msgmap);
+
+ if(thrd->branch)
+ clear_index_cache_for_thread(stream, fetch_thread(stream, thrd->branch),
+ msgmap);
+}
+
+
+void
+clear_icache_flags(MAILSTREAM *stream)
+{
+ sp_set_icache_flags(stream, 0);
+}
+
+
+void
+set_need_format_setup(MAILSTREAM *stream)
+{
+ sp_set_icache_flags(stream, sp_icache_flags(stream) | SP_NEED_FORMAT_SETUP);
+}
+
+
+int
+need_format_setup(MAILSTREAM *stream)
+{
+ return(sp_icache_flags(stream) & SP_NEED_FORMAT_SETUP);
+}
+
+
+void
+set_format_includes_msgno(MAILSTREAM *stream)
+{
+ sp_set_icache_flags(stream, sp_icache_flags(stream) | SP_FORMAT_INCLUDES_MSGNO);
+}
+
+
+int
+format_includes_msgno(MAILSTREAM *stream)
+{
+ return(sp_icache_flags(stream) & SP_FORMAT_INCLUDES_MSGNO);
+}
+
+
+void
+set_format_includes_smartdate(MAILSTREAM *stream)
+{
+ sp_set_icache_flags(stream, sp_icache_flags(stream) | SP_FORMAT_INCLUDES_SMARTDATE);
+}
+
+
+int
+format_includes_smartdate(MAILSTREAM *stream)
+{
+ return(sp_icache_flags(stream) & SP_FORMAT_INCLUDES_SMARTDATE);
+}
+
+
+/*
+ * Almost a free_ice, but we leave the memory there for the ICE_S.
+ */
+void
+clear_ice(ICE_S **ice)
+{
+ if(ice && *ice){
+ free_ifield(&(*ice)->ifield);
+
+ if((*ice)->linecolor)
+ free_color_pair(&(*ice)->linecolor);
+
+ if((*ice)->tice)
+ clear_ice(&(*ice)->tice);
+
+ /* do these one at a time so we don't clear tice */
+ (*ice)->color_lookup_done = 0;
+ (*ice)->to_us = 0;
+ (*ice)->cc_us = 0;
+ (*ice)->plus = 0;
+ (*ice)->id = 0;
+ }
+}
+
+
+void
+free_ice(ICE_S **ice)
+{
+ if(ice && *ice){
+
+ if((*ice)->tice)
+ free_ice(&(*ice)->tice);
+
+ clear_ice(ice);
+
+ fs_give((void **) ice);
+ }
+}
+
+
+void
+free_ifield(IFIELD_S **ifld)
+{
+ if(ifld && *ifld){
+ free_ifield(&(*ifld)->next);
+ free_ielem(&(*ifld)->ielem);
+ fs_give((void **) ifld);
+ }
+}
+
+
+void
+free_ielem(IELEM_S **il)
+{
+ if(il && *il){
+ free_ielem(&(*il)->next);
+ if((*il)->freeprintf && (*il)->print_format)
+ fs_give((void **) &(*il)->print_format);
+
+ if((*il)->freecolor && (*il)->color)
+ free_color_pair(&(*il)->color);
+
+ if((*il)->freedata && (*il)->data)
+ fs_give((void **) &(*il)->data);
+
+ fs_give((void **) il);
+ }
+}
+
+
+/*
+ * Returns the index cache entry associated with this message.
+ * If it doesn't already exist it is instantiated.
+ */
+ICE_S *
+fetch_ice(MAILSTREAM *stream, long unsigned int rawno)
+{
+ PINELT_S **peltp;
+ MESSAGECACHE *mc;
+
+ if(!stream || rawno < 1L || rawno > stream->nmsgs)
+ return NULL;
+
+ if(!(mc = mail_elt(stream, rawno)))
+ return NULL;
+
+ /*
+ * any private elt data yet?
+ */
+ if((*(peltp = (PINELT_S **) &mc->sparep) == NULL)){
+ *peltp = (PINELT_S *) fs_get(sizeof(PINELT_S));
+ memset(*peltp, 0, sizeof(PINELT_S));
+ }
+
+ if((*peltp)->ice == NULL)
+ (*peltp)->ice = new_ice();
+
+ if(need_format_setup(stream) && setup_header_widths)
+ (*setup_header_widths)(stream);
+
+ return((*peltp)->ice);
+}
+
+
+ICE_S **
+fetch_ice_ptr(MAILSTREAM *stream, long unsigned int rawno)
+{
+ PINELT_S **peltp;
+ MESSAGECACHE *mc;
+
+ if(!stream || rawno < 1L || rawno > stream->nmsgs)
+ return NULL;
+
+ if(!(mc = mail_elt(stream, rawno)))
+ return NULL;
+
+ /*
+ * any private elt data yet?
+ */
+ if((*(peltp = (PINELT_S **) &mc->sparep) == NULL)){
+ *peltp = (PINELT_S *) fs_get(sizeof(PINELT_S));
+ memset(*peltp, 0, sizeof(PINELT_S));
+ }
+
+ return(&(*peltp)->ice);
+}
+
+
+ICE_S *
+copy_ice(ICE_S *src)
+{
+ ICE_S *head = NULL;
+
+ if(src){
+ head = new_ice();
+
+ head->color_lookup_done = src->color_lookup_done;
+ head->widths_done = src->widths_done;
+ head->to_us = src->to_us;
+ head->cc_us = src->cc_us;
+ head->plus = src->plus;
+ head->id = src->id;
+
+ if(src->linecolor)
+ head->linecolor = new_color_pair(src->linecolor->fg, src->linecolor->bg);
+
+ if(src->ifield)
+ head->ifield = copy_ifield(src->ifield);
+
+ if(src->tice)
+ head->tice = copy_ice(src->tice);
+ }
+
+ return(head);
+}
+
+
+IFIELD_S *
+copy_ifield(IFIELD_S *src)
+{
+ IFIELD_S *head = NULL;
+
+ if(src){
+ head = new_ifield(NULL);
+
+ if(src->next)
+ head->next = copy_ifield(src->next);
+
+ head->ctype = src->ctype;
+ head->width = src->width;
+ head->leftadj = src->leftadj;
+
+ if(src->ielem)
+ head->ielem = copy_ielem(src->ielem);
+ }
+
+ return(head);
+}
+
+
+IELEM_S *
+copy_ielem(IELEM_S *src)
+{
+ IELEM_S *head = NULL;
+
+ if(src){
+ head = new_ielem(NULL);
+
+ if(src->next)
+ head->next = copy_ielem(src->next);
+
+ head->type = src->type;
+ head->wid = src->wid;
+
+ if(src->color){
+ head->color = new_color_pair(src->color->fg, src->color->bg);
+ head->freecolor = 1;
+ }
+
+ if(src->data){
+ head->data = cpystr(src->data);
+ head->datalen = strlen(head->data);
+ head->freedata = 1;
+ }
+
+ if(src->print_format){
+ head->print_format = cpystr(src->print_format);
+ head->freeprintf = strlen(head->print_format) + 1;
+ }
+ }
+
+ return(head);
+}
+
+
+ICE_S *
+new_ice(void)
+{
+ ICE_S *ice;
+
+ ice = (ICE_S *) fs_get(sizeof(ICE_S));
+ memset(ice, 0, sizeof(ICE_S));
+ return(ice);
+}
+
+
+/*
+ * Create new IFIELD_S, zero it out, and insert it at end.
+ */
+IFIELD_S *
+new_ifield(IFIELD_S **ifieldp)
+{
+ IFIELD_S *ifield, *ip;
+
+ ifield = (IFIELD_S *) fs_get(sizeof(*ifield));
+ memset(ifield, 0, sizeof(*ifield));
+
+ if(ifieldp){
+ ip = *ifieldp;
+ if(ip){
+ for(ip = (*ifieldp); ip && ip->next; ip = ip->next)
+ ;
+
+ ip->next = ifield;
+ }
+ else
+ *ifieldp = ifield;
+ }
+
+ return(ifield);
+}
+
+
+/*
+ * Create new IELEM_S, zero it out, and insert it at end.
+ */
+IELEM_S *
+new_ielem(IELEM_S **ielemp)
+{
+ IELEM_S *ielem, *ip;
+
+ ielem = (IELEM_S *) fs_get(sizeof(*ielem));
+ memset(ielem, 0, sizeof(*ielem));
+
+ if(ielemp){
+ ip = *ielemp;
+ if(ip){
+ for(ip = (*ielemp); ip && ip->next; ip = ip->next)
+ ;
+
+ ip->next = ielem;
+ }
+ else
+ *ielemp = ielem;
+ }
+
+ return(ielem);
+}
diff --git a/pith/icache.h b/pith/icache.h
new file mode 100644
index 00000000..3d9b9a7a
--- /dev/null
+++ b/pith/icache.h
@@ -0,0 +1,56 @@
+/*
+ * $Id: icache.h 874 2007-12-15 02:51:06Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_ICACHE_INCLUDED
+#define PITH_ICACHE_INCLUDED
+
+
+#include "../pith/msgno.h"
+#include "../pith/thread.h"
+#include "../pith/indxtype.h"
+
+
+/* flags for clear_index_cache() */
+#define IC_USE_RAW_MSGNO 0x01
+#define IC_CLEAR_WIDTHS_DONE 0x02
+
+
+/* exported protoypes */
+void clear_index_cache_ent(MAILSTREAM *, long, unsigned);
+void clear_index_cache(MAILSTREAM *, unsigned);
+void clear_index_cache_for_thread(MAILSTREAM *, PINETHRD_S *, MSGNO_S *);
+void clear_icache_flags(MAILSTREAM *);
+void set_need_format_setup(MAILSTREAM *);
+int need_format_setup(MAILSTREAM *);
+void set_format_includes_msgno(MAILSTREAM *);
+int format_includes_msgno(MAILSTREAM *);
+void set_format_includes_smartdate(MAILSTREAM *);
+int format_includes_smartdate(MAILSTREAM *);
+void free_ice(ICE_S **);
+void clear_ice(ICE_S **);
+void free_ifield(IFIELD_S **);
+void free_ielem(IELEM_S **);
+ICE_S *fetch_ice(MAILSTREAM *, unsigned long);
+ICE_S **fetch_ice_ptr(MAILSTREAM *, unsigned long);
+ICE_S *copy_ice(ICE_S *);
+IFIELD_S *copy_ifield(IFIELD_S *);
+IELEM_S *copy_ielem(IELEM_S *);
+ICE_S *new_ice(void);
+IFIELD_S *new_ifield(IFIELD_S **);
+IELEM_S *new_ielem(IELEM_S **);
+
+
+#endif /* PITH_ICACHE_INCLUDED */
diff --git a/pith/imap.c b/pith/imap.c
new file mode 100644
index 00000000..ea4c5b1f
--- /dev/null
+++ b/pith/imap.c
@@ -0,0 +1,1111 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: imap.c 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/imap.h"
+#include "../pith/msgno.h"
+#include "../pith/state.h"
+#include "../pith/flag.h"
+#include "../pith/pineelt.h"
+#include "../pith/status.h"
+#include "../pith/conftype.h"
+#include "../pith/context.h"
+#include "../pith/thread.h"
+#include "../pith/mailview.h"
+#include "../pith/mailpart.h"
+#include "../pith/mailindx.h"
+#include "../pith/mailcmd.h"
+#include "../pith/save.h"
+#include "../pith/util.h"
+#include "../pith/stream.h"
+#include "../pith/newmail.h"
+#include "../pith/icache.h"
+#include "../pith/options.h"
+
+#ifdef _WINDOWS
+#include "../pico/osdep/mswin.h"
+#endif
+
+
+/*
+ * Internal prototypes
+ */
+long imap_seq_exec(MAILSTREAM *, char *, long (*)(MAILSTREAM *, long, void *), void *);
+long imap_seq_exec_append(MAILSTREAM *, long, void *);
+char *ps_get(size_t);
+
+
+/*
+ * Exported globals setup by searching functions to tell mm_searched
+ * where to put message numbers that matched the search criteria,
+ * and to allow mm_searched to return number of matches.
+ */
+MAILSTREAM *mm_search_stream;
+long mm_search_count = 0L;
+MAILSTATUS mm_status_result;
+
+MM_LIST_S *mm_list_info;
+
+MMLOGIN_S *mm_login_list = NULL;
+MMLOGIN_S *cert_failure_list = NULL;
+
+/*
+ * Instead of storing cached passwords in free storage, store them in this
+ * private space. This makes it easier and more reliable when we want
+ * to zero this space out. We only store passwords here (char *) so we
+ * don't need to worry about alignment.
+ */
+static volatile char private_store[1024];
+
+static int critical_depth = 0;
+
+/* hook to hang callback on "current" message expunge */
+void (*pith_opt_current_expunged)(long unsigned int);
+
+#ifdef SIGINT
+RETSIGTYPE (*hold_int)(int);
+#endif
+
+#ifdef SIGTERM
+RETSIGTYPE (*hold_term)(int);
+#endif
+
+#ifdef SIGHUP
+RETSIGTYPE (*hold_hup)(int);
+#endif
+
+#ifdef SIGUSR2
+RETSIGTYPE (*hold_usr2)(int);
+#endif
+
+
+
+/*----------------------------------------------------------------------
+ receive notification that search found something
+
+ Input: mail stream and message number of located item
+
+ Result: nothing, not used by pine
+ ----*/
+void
+mm_searched(MAILSTREAM *stream, long unsigned int rawno)
+{
+ MESSAGECACHE *mc;
+
+ if(rawno > 0L && stream && rawno <= stream->nmsgs
+ && (mc = mail_elt(stream, rawno))){
+ mc->searched = 1;
+ if(stream == mm_search_stream)
+ mm_search_count++;
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ receive notification of new mail from imap daemon
+
+ Args: stream -- The stream the message count report is for.
+ number -- The number of messages now in folder.
+
+ Result: Sets value in pine state indicating new mailbox size
+
+ Called when the number of messages in the mailbox goes up. This
+ may also be called as a result of an expunge. It increments the
+ new_mail_count based on a the difference between the current idea of
+ the maximum number of messages and what mm_exists claims. The new mail
+ notification is done in newmail.c
+
+ Only worry about the cases when the number grows, as mm_expunged
+ handles shrinkage...
+
+ ----*/
+void
+mm_exists(MAILSTREAM *stream, long unsigned int number)
+{
+ long new_this_call, n;
+ int exbits = 0, lflags = 0;
+ MSGNO_S *msgmap;
+
+#ifdef DEBUG
+ if(ps_global->debug_imap > 1 || ps_global->debugmem)
+ dprint((3, "=== mm_exists(%lu,%s) called ===\n", number,
+ !stream ? "(no stream)" : !stream->mailbox ? "(null)" : stream->mailbox));
+#endif
+
+ msgmap = sp_msgmap(stream);
+ if(!msgmap)
+ return;
+
+ if(mn_get_nmsgs(msgmap) != (long) number){
+ sp_set_mail_box_changed(stream, 1);
+ /* titlebar will be affected */
+ if(ps_global->mail_stream == stream)
+ ps_global->mangled_header = 1;
+ }
+
+ if(mn_get_nmsgs(msgmap) < (long) number){
+ new_this_call = (long) number - mn_get_nmsgs(msgmap);
+ sp_set_new_mail_count(stream,
+ sp_new_mail_count(stream) + new_this_call);
+ sp_set_recent_since_visited(stream,
+ sp_recent_since_visited(stream) + new_this_call);
+
+ mn_add_raw(msgmap, new_this_call);
+
+ /*
+ * Set local "recent" and "hidden" bits...
+ */
+ for(n = 0; n < new_this_call; n++, number--){
+ if(msgno_exceptions(stream, number, "0", &exbits, FALSE))
+ exbits |= MSG_EX_RECENT;
+ else
+ exbits = MSG_EX_RECENT;
+
+ msgno_exceptions(stream, number, "0", &exbits, TRUE);
+
+ if(SORT_IS_THREADED(msgmap))
+ lflags |= MN_USOR;
+
+ /*
+ * If we're zoomed, then hide this message too since
+ * it couldn't have possibly been selected yet...
+ */
+ lflags |= (any_lflagged(msgmap, MN_HIDE) ? MN_HIDE : 0);
+ if(lflags)
+ set_lflag(stream, msgmap, mn_get_total(msgmap) - n, lflags, 1);
+ }
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Receive notification from IMAP that a single message has been expunged
+
+ Args: stream -- The stream/folder the message is expunged from
+ rawno -- The raw message number that was expunged
+
+mm_expunged is always called on an expunge. Simply remove all
+reference to the expunged message, shifting internal mappings as
+necessary.
+ ----*/
+void
+mm_expunged(MAILSTREAM *stream, long unsigned int rawno)
+{
+ MESSAGECACHE *mc;
+ long i;
+ int is_current = 0;
+ MSGNO_S *msgmap;
+
+#ifdef DEBUG
+ if(ps_global->debug_imap > 1 || ps_global->debugmem)
+ dprint((3, "mm_expunged(%s,%lu)\n",
+ stream
+ ? (stream->mailbox
+ ? stream->mailbox
+ : "(no stream)")
+ : "(null)", rawno));
+#endif
+
+ msgmap = sp_msgmap(stream);
+ if(!msgmap)
+ return;
+
+ if(ps_global->mail_stream == stream)
+ is_current++;
+
+ if((i = mn_raw2m(msgmap, (long) rawno)) != 0L){
+ dprint((7, "mm_expunged: rawno=%lu msgno=%ld nmsgs=%ld max_msgno=%ld flagged_exld=%ld\n", rawno, i, mn_get_nmsgs(msgmap), mn_get_total(msgmap), msgmap->flagged_exld));
+
+ sp_set_mail_box_changed(stream, 1);
+ sp_set_expunge_count(stream, sp_expunge_count(stream) + 1);
+
+ if(is_current){
+ reset_check_point(stream);
+ ps_global->mangled_header = 1;
+
+ /* flush invalid cache entries */
+ while(i <= mn_get_total(msgmap))
+ clear_index_cache_ent(stream, i++, 0);
+
+ /* let app know what happened */
+ if(pith_opt_current_expunged)
+ (*pith_opt_current_expunged)(rawno);
+ }
+ }
+ else{
+ dprint((7,
+ "mm_expunged: rawno=%lu was excluded, flagged_exld was %d\n",
+ rawno, msgmap->flagged_exld));
+ dprint((7, " nmsgs=%ld max_msgno=%ld\n",
+ mn_get_nmsgs(msgmap), mn_get_total(msgmap)));
+ if(rawno > 0L && rawno <= stream->nmsgs)
+ mc = mail_elt(stream, rawno);
+
+ if(!mc){
+ dprint((7, " cannot get mail_elt(%lu)\n",
+ rawno));
+ }
+ else if(!mc->sparep){
+ dprint((7, " mail_elt(%lu)->sparep is NULL\n",
+ rawno));
+ }
+ else{
+ dprint((7, " mail_elt(%lu)->sparep->excluded=%d\n",
+ rawno, (int) (((PINELT_S *) mc->sparep)->excluded)));
+ }
+ }
+
+ if(SORT_IS_THREADED(msgmap)
+ && (SEP_THRDINDX()
+ || ps_global->thread_disp_style != THREAD_NONE)){
+ long cur;
+
+ /*
+ * When we're sorting with a threaded method an expunged
+ * message may cause the rest of the sort to be wrong. This
+ * isn't so bad if we're just looking at the index. However,
+ * it also causes the thread tree (PINETHRD_S) to become
+ * invalid, so if we're using a threading view we need to
+ * sort in order to fix the tree and to protect fetch_thread().
+ */
+ sp_set_need_to_rethread(stream, 1);
+
+ /*
+ * If we expunged the current message which was a member of the
+ * viewed thread, and the adjustment to current will take us
+ * out of that thread, fix it if we can, by backing current up
+ * into the thread. We'd like to just check after mn_flush_raw
+ * below but the problem is that the elts won't change until
+ * after we return from mm_expunged. So we have to manually
+ * check the other messages for CHID2 flags instead of thinking
+ * that we can expunge the current message and then check. It won't
+ * work because the elt will still refer to the expunged message.
+ */
+ if(sp_viewing_a_thread(stream)
+ && get_lflag(stream, NULL, rawno, MN_CHID2)
+ && mn_total_cur(msgmap) == 1
+ && mn_is_cur(msgmap, mn_raw2m(msgmap, (long) rawno))
+ && (cur = mn_get_cur(msgmap)) > 1L
+ && cur < mn_get_total(msgmap)
+ && !get_lflag(stream, msgmap, cur + 1L, MN_CHID2)
+ && get_lflag(stream, msgmap, cur - 1L, MN_CHID2))
+ mn_set_cur(msgmap, cur - 1L);
+ }
+
+ /*
+ * Keep on top of our special flag counts.
+ *
+ * NOTE: This is allowed since mail_expunged releases
+ * data for this message after the callback.
+ */
+ if(rawno > 0L && rawno <= stream->nmsgs && (mc = mail_elt(stream, rawno))){
+ PINELT_S *pelt = (PINELT_S *) mc->sparep;
+
+ if(pelt){
+ if(pelt->hidden)
+ msgmap->flagged_hid--;
+
+ if(pelt->excluded)
+ msgmap->flagged_exld--;
+
+ if(pelt->selected)
+ msgmap->flagged_tmp--;
+
+ if(pelt->colhid)
+ msgmap->flagged_chid--;
+
+ if(pelt->colhid2)
+ msgmap->flagged_chid2--;
+
+ if(pelt->collapsed)
+ msgmap->flagged_coll--;
+
+ if(pelt->tmp)
+ msgmap->flagged_stmp--;
+
+ if(pelt->unsorted)
+ msgmap->flagged_usor--;
+
+ if(pelt->searched)
+ msgmap->flagged_srch--;
+
+ if(pelt->hidden || pelt->colhid)
+ msgmap->flagged_invisible--;
+
+ free_pine_elt(&mc->sparep);
+ }
+ }
+
+ /*
+ * if it's in the sort array, flush it, otherwise
+ * decrement raw sequence numbers greater than "rawno"
+ */
+ mn_flush_raw(msgmap, (long) rawno);
+}
+
+
+void
+mm_flags(MAILSTREAM *stream, long unsigned int rawno)
+{
+ /*
+ * The idea here is to clean up any data pine might have cached
+ * that has anything to do with the indicated message number.
+ */
+ if(stream == ps_global->mail_stream){
+ long msgno, t;
+ PINETHRD_S *thrd;
+
+ if(scores_are_used(SCOREUSE_GET) & SCOREUSE_STATEDEP)
+ clear_msg_score(stream, rawno);
+
+ msgno = mn_raw2m(sp_msgmap(stream), (long) rawno);
+
+ /* if in thread index */
+ if(THRD_INDX()){
+ if((thrd = fetch_thread(stream, rawno))
+ && thrd->top
+ && (thrd = fetch_thread(stream, thrd->top))
+ && thrd->rawno
+ && (t = mn_raw2m(sp_msgmap(stream), thrd->rawno)))
+ clear_index_cache_ent(stream, t, 0);
+ }
+ else if(THREADING()){
+ if(msgno > 0L)
+ clear_index_cache_ent(stream, msgno, 0);
+
+ /*
+ * If a parent is collapsed, clear that parent's
+ * index cache entry.
+ */
+ if((thrd = fetch_thread(stream, rawno)) && thrd->parent){
+ thrd = fetch_thread(stream, thrd->parent);
+ while(thrd){
+ if(get_lflag(stream, NULL, thrd->rawno, MN_COLL)
+ && (t = mn_raw2m(sp_msgmap(stream), (long) thrd->rawno)))
+ clear_index_cache_ent(stream, t, 0);
+
+ if(thrd->parent)
+ thrd = fetch_thread(stream, thrd->parent);
+ else
+ thrd = NULL;
+ }
+ }
+ }
+ else if(msgno > 0L)
+ clear_index_cache_ent(stream, msgno, 0);
+
+ if(msgno && mn_is_cur(sp_msgmap(stream), msgno))
+ ps_global->mangled_header = 1;
+ }
+
+ /*
+ * We count up flag changes here. The
+ * dont_count_flagchanges variable tries to prevent us from
+ * counting when we're just fetching flags.
+ */
+ if(!(ps_global->dont_count_flagchanges
+ && stream == ps_global->mail_stream)){
+ int exbits;
+
+ check_point_change(stream);
+
+ /* we also note flag changes for filtering purposes */
+ if(msgno_exceptions(stream, rawno, "0", &exbits, FALSE))
+ exbits |= MSG_EX_STATECHG;
+ else
+ exbits = MSG_EX_STATECHG;
+
+ msgno_exceptions(stream, rawno, "0", &exbits, TRUE);
+ }
+}
+
+
+void
+mm_list(MAILSTREAM *stream, int delimiter, char *mailbox, long int attributes)
+{
+#ifdef DEBUG
+ if(ps_global->debug_imap > 2 || ps_global->debugmem)
+ dprint((5, "mm_list \"%s\": delim: '%c', %s%s%s%s%s%s\n",
+ mailbox ? mailbox : "?", delimiter ? delimiter : 'X',
+ (attributes & LATT_NOINFERIORS) ? ", no inferiors" : "",
+ (attributes & LATT_NOSELECT) ? ", no select" : "",
+ (attributes & LATT_MARKED) ? ", marked" : "",
+ (attributes & LATT_UNMARKED) ? ", unmarked" : "",
+ (attributes & LATT_HASCHILDREN) ? ", has children" : "",
+ (attributes & LATT_HASNOCHILDREN) ? ", has no children" : ""));
+#endif
+
+ if(!mm_list_info->stream || stream == mm_list_info->stream)
+ (*mm_list_info->filter)(stream, mailbox, delimiter,
+ attributes, mm_list_info->data,
+ mm_list_info->options);
+}
+
+
+void
+mm_lsub(MAILSTREAM *stream, int delimiter, char *mailbox, long int attributes)
+{
+#ifdef DEBUG
+ if(ps_global->debug_imap > 2 || ps_global->debugmem)
+ dprint((5, "LSUB \"%s\": delim: '%c', %s%s%s%s%s%s\n",
+ mailbox ? mailbox : "?", delimiter ? delimiter : 'X',
+ (attributes & LATT_NOINFERIORS) ? ", no inferiors" : "",
+ (attributes & LATT_NOSELECT) ? ", no select" : "",
+ (attributes & LATT_MARKED) ? ", marked" : "",
+ (attributes & LATT_UNMARKED) ? ", unmarked" : "",
+ (attributes & LATT_HASCHILDREN) ? ", has children" : "",
+ (attributes & LATT_HASNOCHILDREN) ? ", has no children" : ""));
+#endif
+
+ if(!mm_list_info->stream || stream == mm_list_info->stream)
+ (*mm_list_info->filter)(stream, mailbox, delimiter,
+ attributes, mm_list_info->data,
+ mm_list_info->options);
+}
+
+
+void
+mm_status(MAILSTREAM *stream, char *mailbox, MAILSTATUS *status)
+{
+ /*
+ * We implement mail_status for the #move namespace by adding a wrapper
+ * routine, pine_mail_status. It may have to call the real mail_status
+ * twice for #move folders and combine the results. It sets
+ * pine_cached_status to point to a local status variable to store the
+ * intermediate results.
+ */
+ if(status){
+ if(pine_cached_status != NULL)
+ *pine_cached_status = *status;
+ else
+ mm_status_result = *status;
+ }
+
+#ifdef DEBUG
+ if(status){
+ if(pine_cached_status)
+ dprint((2,
+ "mm_status: Preliminary pass for #move\n"));
+
+ dprint((2, "mm_status: Mailbox \"%s\"",
+ mailbox ? mailbox : "?"));
+ if(status->flags & SA_MESSAGES)
+ dprint((2, ", %lu messages", status->messages));
+
+ if(status->flags & SA_RECENT)
+ dprint((2, ", %lu recent", status->recent));
+
+ if(status->flags & SA_UNSEEN)
+ dprint((2, ", %lu unseen", status->unseen));
+
+ if(status->flags & SA_UIDVALIDITY)
+ dprint((2, ", %lu UID validity", status->uidvalidity));
+
+ if(status->flags & SA_UIDNEXT)
+ dprint((2, ", %lu next UID", status->uidnext));
+
+ dprint((2, "\n"));
+ }
+#endif
+}
+
+
+/*----------------------------------------------------------------------
+ Write imap debugging information into log file
+
+ Args: strings -- the string for the debug file
+
+ Result: message written to the debug log file
+ ----*/
+void
+mm_dlog(char *string)
+{
+ char *p, *q = NULL, save, *continued;
+ int more = 1;
+
+#ifdef _WINDOWS
+ mswin_imaptelemetry(string);
+#endif
+#ifdef DEBUG
+ continued = "";
+ p = string;
+#ifdef DEBUGJOURNAL
+ /* because string can be really long and we don't want to lose any of it */
+ if(p)
+ more = 1;
+
+ while(more){
+ if(q){
+ *q = save;
+ p = q;
+ continued = "(Continuation line) ";
+ }
+
+ if(strlen(p) > 63000){
+ q = p + 60000;
+ save = *q;
+ *q = '\0';
+ }
+ else
+ more = 0;
+#endif
+ dprint(((ps_global->debug_imap >= 4 && debug < 4) ? debug : 4,
+ "IMAP DEBUG %s%s: %s\n",
+ continued ? continued : "",
+ debug_time(1, ps_global->debug_timestamp), p ? p : "?"));
+#ifdef DEBUGJOURNAL
+ }
+#endif
+#endif
+}
+
+
+/*----------------------------------------------------------------------
+ Ignore signals when imap is running through critical code
+
+ Args: stream -- The stream on which critical operation is proceeding
+ ----*/
+
+void
+mm_critical(MAILSTREAM *stream)
+{
+ stream = stream; /* For compiler complaints that this isn't used */
+
+ if(++critical_depth == 1){
+
+#ifdef SIGHUP
+ hold_hup = signal(SIGHUP, SIG_IGN);
+#endif
+#ifdef SIGUSR2
+ hold_usr2 = signal(SIGUSR2, SIG_IGN);
+#endif
+#ifdef SIGINT
+ hold_int = signal(SIGINT, SIG_IGN);
+#endif
+#ifdef SIGTERM
+ hold_term = signal(SIGTERM, SIG_IGN);
+#endif
+ }
+
+ dprint((9, "IMAP critical (depth now %d) on %s\n",
+ critical_depth,
+ (stream && stream->mailbox) ? stream->mailbox : "<no folder>" ));
+}
+
+
+/*----------------------------------------------------------------------
+ Reset signals after critical imap code
+ ----*/
+void
+mm_nocritical(MAILSTREAM *stream)
+{
+ stream = stream; /* For compiler complaints that this isn't used */
+
+ if(--critical_depth == 0){
+
+#ifdef SIGHUP
+ (void)signal(SIGHUP, hold_hup);
+#endif
+#ifdef SIGUSR2
+ (void)signal(SIGUSR2, hold_usr2);
+#endif
+#ifdef SIGINT
+ (void)signal(SIGINT, hold_int);
+#endif
+#ifdef SIGTERM
+ (void)signal(SIGTERM, hold_term);
+#endif
+ }
+
+ critical_depth = MAX(critical_depth, 0);
+
+ dprint((9, "Done with IMAP critical (depth now %d) on %s\n",
+ critical_depth,
+ (stream && stream->mailbox) ? stream->mailbox : "<no folder>" ));
+}
+
+
+void
+mm_fatal(char *message)
+{
+ panic(message);
+}
+
+
+char *
+imap_referral(MAILSTREAM *stream, char *ref, long int code)
+{
+ char *buf = NULL;
+
+ if(ref && !struncmp(ref, "imap://", 7)){
+ char *folder = NULL;
+ imapuid_t uid_val, uid;
+ int rv;
+
+ rv = url_imap_folder(ref, &folder, &uid, &uid_val, NULL, 1);
+ switch(code){
+ case REFAUTHFAILED :
+ case REFAUTH :
+ if((rv & URL_IMAP_IMAILBOXLIST) && (rv & URL_IMAP_ISERVERONLY))
+ buf = cpystr(folder);
+
+ break;
+
+ case REFSELECT :
+ case REFCREATE :
+ case REFDELETE :
+ case REFRENAME :
+ case REFSUBSCRIBE :
+ case REFUNSUBSCRIBE :
+ case REFSTATUS :
+ case REFCOPY :
+ case REFAPPEND :
+ if(rv & URL_IMAP_IMESSAGELIST)
+ buf = cpystr(folder);
+
+ break;
+
+ default :
+ break;
+ }
+
+ if(folder)
+ fs_give((void **) &folder);
+ }
+
+ return(buf);
+}
+
+
+long
+imap_proxycopy(MAILSTREAM *stream, char *sequence, char *mailbox, long int flags)
+{
+ SE_APP_S args;
+ long ret;
+
+ args.folder = mailbox;
+ args.flags = flags;
+
+ if(pith_opt_save_size_changed_prompt)
+ (*pith_opt_save_size_changed_prompt)(0L, SSCP_INIT);
+
+ ret = imap_seq_exec(stream, sequence, imap_seq_exec_append, &args);
+
+ if(pith_opt_save_size_changed_prompt)
+ (*pith_opt_save_size_changed_prompt)(0L, SSCP_END);
+
+ return(ret);
+}
+
+
+long
+imap_seq_exec(MAILSTREAM *stream, char *sequence,
+ long int (*func)(MAILSTREAM *, long int, void *),
+ void *args)
+{
+ unsigned long i,j,x;
+
+ while (*sequence) { /* while there is something to parse */
+ if (*sequence == '*') { /* maximum message */
+ if(!(i = stream->nmsgs)){
+ mm_log ("No messages, so no maximum message number",ERROR);
+ return(0L);
+ }
+
+ sequence++; /* skip past * */
+ }
+ else if (!(i = strtoul ((const char *) sequence,&sequence,10))
+ || (i > stream->nmsgs)){
+ mm_log ("Sequence invalid",ERROR);
+ return(0L);
+ }
+
+ switch (*sequence) { /* see what the delimiter is */
+ case ':': /* sequence range */
+ if (*++sequence == '*') { /* maximum message */
+ if (stream->nmsgs) j = stream->nmsgs;
+ else {
+ mm_log ("No messages, so no maximum message number",ERROR);
+ return NIL;
+ }
+
+ sequence++; /* skip past * */
+ }
+ /* parse end of range */
+ else if (!(j = strtoul ((const char *) sequence,&sequence,10)) ||
+ (j > stream->nmsgs)) {
+ mm_log ("Sequence range invalid",ERROR);
+ return NIL;
+ }
+
+ if (*sequence && *sequence++ != ',') {
+ mm_log ("Sequence range syntax error",ERROR);
+ return NIL;
+ }
+
+ if (i > j) { /* swap the range if backwards */
+ x = i; i = j; j = x;
+ }
+
+ while (i <= j)
+ if(!(*func)(stream, i++, args))
+ return(0L);
+
+ break;
+ case ',': /* single message */
+ ++sequence; /* skip the delimiter, fall into end case */
+ case '\0': /* end of sequence */
+ if(!(*func)(stream, i, args))
+ return(0L);
+
+ break;
+ default: /* anything else is a syntax error! */
+ mm_log ("Sequence syntax error",ERROR);
+ return NIL;
+ }
+ }
+
+ return T; /* successfully parsed sequence */
+}
+
+
+long
+imap_seq_exec_append(MAILSTREAM *stream, long int msgno, void *args)
+{
+ char *save_folder, *flags = NULL, date[64];
+ CONTEXT_S *cntxt = NULL;
+ int our_stream = 0;
+ long rv = 0L;
+ MAILSTREAM *save_stream;
+ SE_APP_S *sa = (SE_APP_S *) args;
+ MESSAGECACHE *mc;
+ STORE_S *so;
+
+ save_folder = (strucmp(sa->folder, ps_global->inbox_name) == 0)
+ ? ps_global->VAR_INBOX_PATH : sa->folder;
+
+ save_stream = save_msg_stream(cntxt, save_folder, &our_stream);
+
+ if((so = so_get(CharStar, NULL, WRITE_ACCESS)) != NULL){
+ /* store flags before the fetch so UNSEEN bit isn't flipped */
+ mc = (msgno > 0L && stream && msgno <= stream->nmsgs)
+ ? mail_elt(stream, msgno) : NULL;
+
+ flags = flag_string(stream, msgno, F_ANS|F_FWD|F_FLAG|F_SEEN|F_KEYWORD);
+ if(mc && mc->day)
+ mail_date(date, mc);
+ else
+ *date = '\0';
+
+ rv = save_fetch_append(stream, msgno, NULL,
+ save_stream, save_folder, NULL,
+ mc ? mc->rfc822_size : 0L, flags, date, so);
+ if(flags)
+ fs_give((void **) &flags);
+
+ if(rv < 0 || sp_expunge_count(stream)){
+ cmd_cancelled("Attached message Save");
+ rv = 0L;
+ }
+ /* else whatever broke in save_fetch_append shoulda bitched */
+
+ so_give(&so);
+ }
+ else{
+ dprint((1, "Can't allocate store for save: %s\n",
+ error_description(errno)));
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ "Problem creating space for message text.");
+ }
+
+ if(our_stream)
+ mail_close(save_stream);
+
+ return(rv);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Get login and password from user for IMAP login
+
+ Args: mb -- The mail box property struct
+ user -- Buffer to return the user name in
+ passwd -- Buffer to return the passwd in
+ trial -- The trial number or number of attempts to login
+ user is at least size NETMAXUSER
+ passwd is apparently at least MAILTMPLEN, but mrc has asked us to
+ use a max size of about 100 instead
+
+ Result: username and password passed back to imap
+ ----*/
+void
+mm_login(NETMBX *mb, char *user, char *pwd, long int trial)
+{
+ mm_login_work(mb, user, pwd, trial, NULL, NULL);
+}
+
+
+/*----------------------------------------------------------------------
+ Exported method to retrieve logged in user name associated with stream
+
+ Args: host -- host to find associated login name with.
+
+ Result:
+ ----*/
+char *
+cached_user_name(char *host)
+{
+ MMLOGIN_S *l;
+ STRLIST_S *h;
+
+ if((l = mm_login_list) && host)
+ do
+ for(h = l->hosts; h; h = h->next)
+ if(!strucmp(host, h->name))
+ return(l->user);
+ while((l = l->next) != NULL);
+
+ return(NULL);
+}
+
+
+int
+imap_same_host(STRLIST_S *hl1, STRLIST_S *hl2)
+{
+ STRLIST_S *lp;
+
+ for( ; hl1; hl1 = hl1->next)
+ for(lp = hl2; lp; lp = lp->next)
+ if(!strucmp(hl1->name, lp->name))
+ return(TRUE);
+
+ return(FALSE);
+}
+
+
+/*
+ * For convenience, we use the same m_list structure (but a different
+ * instance) for storing a list of hosts we've asked the user about when
+ * SSL validation fails. If this function returns TRUE, that means we
+ * have previously asked the user about this host. Ok_novalidate == 1 means
+ * the user said yes, it was ok. Ok_novalidate == 0 means the user
+ * said no. Warned means we warned them already.
+ */
+int
+imap_get_ssl(MMLOGIN_S *m_list, STRLIST_S *hostlist, int *ok_novalidate, int *warned)
+{
+ MMLOGIN_S *l;
+
+ for(l = m_list; l; l = l->next)
+ if(imap_same_host(l->hosts, hostlist)){
+ if(ok_novalidate)
+ *ok_novalidate = l->ok_novalidate;
+
+ if(warned)
+ *warned = l->warned;
+
+ return(TRUE);
+ }
+
+ return(FALSE);
+}
+
+
+/*
+ * Just trying to guess the username the user might want to use on this
+ * host, the user will confirm.
+ */
+char *
+imap_get_user(MMLOGIN_S *m_list, STRLIST_S *hostlist)
+{
+ MMLOGIN_S *l;
+
+ for(l = m_list; l; l = l->next)
+ if(imap_same_host(l->hosts, hostlist))
+ return(l->user);
+
+ return(NULL);
+}
+
+
+/*
+ * If we have a matching hostname, username, and altflag in our cache,
+ * attempt to login with the password from the cache.
+ */
+int
+imap_get_passwd(MMLOGIN_S *m_list, char *passwd, char *user, STRLIST_S *hostlist, int altflag)
+{
+ MMLOGIN_S *l;
+
+ dprint((9,
+ "imap_get_passwd: checking user=%s alt=%d host=%s%s%s\n",
+ user ? user : "(null)",
+ altflag,
+ hostlist->name ? hostlist->name : "",
+ (hostlist->next && hostlist->next->name) ? ", " : "",
+ (hostlist->next && hostlist->next->name) ? hostlist->next->name
+ : ""));
+ for(l = m_list; l; l = l->next)
+ if(imap_same_host(l->hosts, hostlist)
+ && *user
+ && !strcmp(user, l->user)
+ && l->altflag == altflag){
+ if(passwd){
+ strncpy(passwd, l->passwd, NETMAXPASSWD);
+ passwd[NETMAXPASSWD-1] = '\0';
+ }
+ dprint((9, "imap_get_passwd: match\n"));
+ dprint((10, "imap_get_passwd: trying passwd=\"%s\"\n",
+ passwd ? passwd : "?"));
+ return(TRUE);
+ }
+
+ dprint((9, "imap_get_passwd: no match\n"));
+ return(FALSE);
+}
+
+
+
+void
+imap_set_passwd(MMLOGIN_S **l, char *passwd, char *user, STRLIST_S *hostlist,
+ int altflag, int ok_novalidate, int warned)
+{
+ STRLIST_S **listp;
+ size_t len;
+
+ dprint((9, "imap_set_passwd\n"));
+ for(; *l; l = &(*l)->next)
+ if(imap_same_host((*l)->hosts, hostlist)
+ && !strcmp(user, (*l)->user)
+ && altflag == (*l)->altflag){
+ if(strcmp(passwd, (*l)->passwd) ||
+ (*l)->ok_novalidate != ok_novalidate ||
+ (*l)->warned != warned)
+ break;
+ else
+ return;
+ }
+
+ if(!*l){
+ *l = (MMLOGIN_S *)fs_get(sizeof(MMLOGIN_S));
+ memset(*l, 0, sizeof(MMLOGIN_S));
+ }
+
+ len = strlen(passwd);
+ if(!(*l)->passwd || strlen((*l)->passwd) < len)
+ (*l)->passwd = ps_get(len+1);
+
+ strncpy((*l)->passwd, passwd, len+1);
+
+ (*l)->altflag = altflag;
+ (*l)->ok_novalidate = ok_novalidate;
+ (*l)->warned = warned;
+
+ if(!(*l)->user)
+ (*l)->user = cpystr(user);
+
+ dprint((9, "imap_set_passwd: user=%s altflag=%d\n",
+ (*l)->user ? (*l)->user : "?",
+ (*l)->altflag));
+
+ for( ; hostlist; hostlist = hostlist->next){
+ for(listp = &(*l)->hosts;
+ *listp && strucmp((*listp)->name, hostlist->name);
+ listp = &(*listp)->next)
+ ;
+
+ if(!*listp){
+ *listp = new_strlist(hostlist->name);
+ dprint((9, "imap_set_passwd: host=%s\n",
+ (*listp)->name ? (*listp)->name : "?"));
+ }
+ }
+
+ dprint((10, "imap_set_passwd: passwd=\"%s\"\n",
+ passwd ? passwd : "?"));
+}
+
+
+
+void
+imap_flush_passwd_cache(int dumpcache)
+{
+ size_t len;
+ volatile char *p;
+
+ /* equivalent of memset but can't be optimized away */
+ len = sizeof(private_store);
+ p = private_store;
+ while(len-- > 0)
+ *p++ = '\0';
+
+ if(dumpcache){
+ MMLOGIN_S *l;
+
+ while((l = mm_login_list) != NULL){
+ mm_login_list = mm_login_list->next;
+ if(l->user)
+ fs_give((void **) &l->user);
+
+ free_strlist(&l->hosts);
+
+ fs_give((void **) &l);
+ }
+
+ while((l = cert_failure_list) != NULL){
+ cert_failure_list = cert_failure_list->next;
+ if(l->user)
+ fs_give((void **) &l->user);
+
+ free_strlist(&l->hosts);
+
+ fs_give((void **) &l);
+ }
+ }
+}
+
+
+/*
+ * Mimics fs_get except it only works for char * (no alignment hacks), it
+ * stores in a static array so it is easy to zero it out (that's the whole
+ * purpose), allocations always happen at the end (no free).
+ * If we go past array limit, we don't break, we just use free storage.
+ * Should be awfully rare, though.
+ */
+char *
+ps_get(size_t size)
+{
+ static char *last = (char *) private_store;
+ char *block = NULL;
+
+ /* there is enough space */
+ if(size <= sizeof(private_store) - (last - (char *) private_store)){
+ block = last;
+ last += size;
+ }
+ else{
+ dprint((2,
+ "Out of password caching space in private_store\n"));
+ dprint((2,
+ "Using free storage instead\n"));
+ block = fs_get(size);
+ }
+
+ return(block);
+}
diff --git a/pith/imap.h b/pith/imap.h
new file mode 100644
index 00000000..86a0b533
--- /dev/null
+++ b/pith/imap.h
@@ -0,0 +1,135 @@
+/*
+ * $Id: imap.h 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_IMAP_INCLUDED
+#define PITH_IMAP_INCLUDED
+
+
+#include "../pith/string.h"
+
+
+#define NETMAXPASSWD 100
+
+
+/*
+ * struct used to keep track of password/host/user triples.
+ * The problem is we only want to try user names and passwords if
+ * we've already tried talking to this host before.
+ *
+ */
+typedef struct _mmlogin_s {
+ char *user,
+ *passwd;
+ unsigned altflag:1;
+ unsigned ok_novalidate:1;
+ unsigned warned:1;
+ STRLIST_S *hosts;
+ struct _mmlogin_s *next;
+} MMLOGIN_S;
+
+
+typedef struct _se_app_s {
+ char *folder;
+ long flags;
+} SE_APP_S;
+
+
+/*
+ * struct to help manage mail_list calls/callbacks
+ */
+typedef struct mm_list_s {
+ MAILSTREAM *stream;
+ unsigned options;
+ void (*filter)(MAILSTREAM *, char *, int, long, void *, unsigned);
+ void *data;
+} MM_LIST_S;
+
+
+/*
+ * return values for IMAP URL parser
+ */
+#define URL_IMAP_MASK 0x0007
+#define URL_IMAP_ERROR 0
+#define URL_IMAP_IMAILBOXLIST 0x0001
+#define URL_IMAP_IMESSAGELIST 0x0002
+#define URL_IMAP_IMESSAGEPART 0x0004
+#define URL_IMAP_IMBXLSTLSUB 0x0010
+#define URL_IMAP_ISERVERONLY 0x0020
+
+
+/*
+ * Exported globals setup by searching functions to tell mm_searched
+ * where to put message numbers that matched the search criteria,
+ * and to allow mm_searched to return number of matches.
+ */
+extern MAILSTREAM *mm_search_stream;
+extern long mm_search_count;
+extern MAILSTATUS mm_status_result;
+
+extern MM_LIST_S *mm_list_info;
+
+
+extern MMLOGIN_S *mm_login_list;
+extern MMLOGIN_S *cert_failure_list;
+
+
+/*
+ * These are declared in c-client and implemented in ../pith/imap.c
+ */
+#if 0
+ void mm_searched (MAILSTREAM *stream,unsigned long number);
+ void mm_exists (MAILSTREAM *stream,unsigned long number);
+ void mm_expunged (MAILSTREAM *stream,unsigned long number);
+ void mm_flags (MAILSTREAM *stream,unsigned long number);
+ void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes);
+ void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes);
+ void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status);
+ void mm_dlog (char *string);
+ void mm_critical (MAILSTREAM *stream);
+ void mm_nocritical (MAILSTREAM *stream);
+ void mm_fatal (char *string);
+#endif
+
+/*
+ * These are declared in c-client and must be implemented in application
+ */
+#if 0
+ void mm_notify (MAILSTREAM *stream,char *string,long errflg);
+ void mm_log (char *string,long errflg);
+ void mm_login (NETMBX *mb,char *user,char *pwd,long trial);
+ long mm_diskerror (MAILSTREAM *stream,long errcode,long serious);
+#endif
+
+
+/* exported protoypes */
+char *imap_referral(MAILSTREAM *, char *, long);
+long imap_proxycopy(MAILSTREAM *, char *, char *, long);
+char *cached_user_name(char *);
+int imap_same_host(STRLIST_S *, STRLIST_S *);
+int imap_get_ssl(MMLOGIN_S *, STRLIST_S *, int *, int *);
+char *imap_get_user(MMLOGIN_S *, STRLIST_S *);
+int imap_get_passwd(MMLOGIN_S *, char *, char *, STRLIST_S *, int);
+void imap_set_passwd(MMLOGIN_S **, char *, char *, STRLIST_S *, int, int, int);
+void imap_flush_passwd_cache(int);
+
+
+/* currently mandatory to implement stubs */
+
+/* called by build_folder_list(), ok if it does nothing */
+void set_read_predicted(int);
+void mm_login_work (NETMBX *mb,char *user,char *pwd,long trial,char *usethisprompt, char *altuserforcache);
+
+
+#endif /* PITH_IMAP_INCLUDED */
diff --git a/pith/indxtype.h b/pith/indxtype.h
new file mode 100644
index 00000000..ee01a9bb
--- /dev/null
+++ b/pith/indxtype.h
@@ -0,0 +1,249 @@
+/*
+ * $Id: indxtype.h 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_INDXTYPE_INCLUDED
+#define PITH_INDXTYPE_INCLUDED
+
+#include "../pith/osdep/color.h"
+
+#include "../pith/msgno.h"
+#include "../pith/charset.h"
+
+
+/*
+ * Flags for msgline_hidden.
+ *
+ * MH_NONE means we should only consider messages which are or would be
+ * visible in the index view. That is, messages which are not hidden due
+ * to zooming, not hidden because they are in a collapsed part of a
+ * thread, and not hidden because we are in one thread and they are in
+ * another and the view we are using only shows one thread.
+ * MH_THISTHD changes that a little bit. It considers more messages
+ * to be visible. In particular, messages in this thread which are
+ * hidden due to collapsing are considered to be visible instead of hidden.
+ * This is useful if we are viewing a message and hit Next, and want
+ * to see the next message in the thread even if it was in a
+ * collapsed thread. This only makes sense when using the separate
+ * thread index and viewing a thread. "This thread" is the thread that
+ * is being viewed, not the thread that msgno is part of. So it is the
+ * thread that has been marked MN_CHID2.
+ * MH_ANYTHD adds more visibility. It doesn't matter if the message is in
+ * the same thread or if it is collapsed or not. If a message is not
+ * hidden due to zooming, then it is not hidden. Notice that ANYTHD
+ * implies THISTHD.
+ */
+#define MH_NONE 0x0
+#define MH_THISTHD 0x1
+#define MH_ANYTHD 0x2
+
+
+typedef enum {iNothing, iStatus, iFStatus, iIStatus, iSIStatus,
+ iDate, iLDate, iS1Date, iS2Date, iS3Date, iS4Date, iSDate,
+ iDateIso, iDateIsoS,
+ iSDateIso, iSDateIsoS, iSDateS1, iSDateS2, iSDateS3, iSDateS4,
+ iSDateTime,
+ iSDateTimeIso, iSDateTimeIsoS, iSDateTimeS1,
+ iSDateTimeS2, iSDateTimeS3, iSDateTimeS4,
+ iSDateTime24,
+ iSDateTimeIso24, iSDateTimeIsoS24, iSDateTimeS124,
+ iSDateTimeS224, iSDateTimeS324, iSDateTimeS424,
+ iRDate, iTimezone,
+ iTime24, iTime12,
+ iCurDate, iCurDateIso, iCurDateIsoS, iCurTime24, iCurTime12,
+ iCurDay, iCurDay2Digit, iCurDayOfWeek, iCurDayOfWeekAbb,
+ iCurMon, iCurMon2Digit, iCurMonLong, iCurMonAbb,
+ iCurYear, iCurYear2Digit,
+ iLstMon, iLstMon2Digit, iLstMonLong, iLstMonAbb,
+ iLstMonYear, iLstMonYear2Digit,
+ iLstYear, iLstYear2Digit,
+ iMessNo, iAtt, iMsgID,
+ iSubject, iSubjKey, iSubjKeyInit,
+ iSubjectText, iSubjKeyText, iSubjKeyInitText,
+ iOpeningText, iOpeningTextNQ,
+ iKey, iKeyInit,
+ iPrefDate, iPrefTime, iPrefDateTime,
+ iCurPrefDate, iCurPrefTime, iCurPrefDateTime,
+ iSize, iSizeComma, iSizeNarrow, iDescripSize,
+ iNewsAndTo, iToAndNews, iNewsAndRecips, iRecipsAndNews,
+ iFromTo, iFromToNotNews, iFrom, iTo, iSender, iCc, iNews, iRecips,
+ iCurNews, iArrow,
+ iMailbox, iAddress, iInit, iCursorPos,
+ iDay2Digit, iMon2Digit, iYear2Digit,
+ iSTime, iKSize,
+ iRoleNick, iNewLine,
+ iHeader, iText,
+ iPrio, iPrioAlpha, iPrioBang,
+ iScore, iDayOfWeekAbb, iDayOfWeek,
+ iDay, iDayOrdinal, iMonAbb, iMonLong, iMon, iYear} IndexColType;
+
+typedef enum {AllAuto, Fixed, Percent, WeCalculate, Special} WidthType;
+
+typedef enum {eText = 0, eKeyWord, eThreadCount, eThreadInfo, eTypeCol} ElemType;
+
+typedef enum {Left, Right} ColAdj;
+
+typedef struct index_parse_tokens {
+ char *name;
+ IndexColType ctype;
+ int what_for;
+} INDEX_PARSE_T;
+
+
+/* these are flags for the what_for field in INDEX_PARSE_T */
+#define FOR_NOTHING 0x00
+#define FOR_INDEX 0x01
+#define FOR_REPLY_INTRO 0x02
+#define FOR_TEMPLATE 0x04 /* or for signature */
+#define FOR_FILT 0x08
+#define DELIM_USCORE 0x10
+#define DELIM_PAREN 0x20
+#define DELIM_COLON 0x40
+
+
+#define DEFAULT_REPLY_INTRO "default"
+
+
+typedef struct hdr_token_description {
+ char *hdrname;
+ int fieldnum;
+ int fieldsepcnt;
+ ColAdj adjustment;
+ char *fieldseps;
+} HEADER_TOK_S;
+
+typedef struct col_description {
+ IndexColType ctype;
+ WidthType wtype;
+ int req_width;
+ int width;
+ int actual_length;
+ int monabb_width; /* hack */
+ ColAdj adjustment;
+ HEADER_TOK_S *hdrtok;
+} INDEX_COL_S;
+
+
+/*
+ * Ensure that all the data in an index_elem (in the data member)
+ * is stored in UTF-8. The writing routines (paint_index_line() for pine)
+ * should then assume UTF-8 when using the data. The process of putting
+ * data into an index_elem will likely involve a conversion into UTF-8.
+ * For example, the Subject or From fields may have data in some arbitrary
+ * character set.
+ */
+typedef struct index_elem {
+ struct index_elem *next;
+ char *print_format;
+ COLOR_PAIR *color; /* color, if any, for this element */
+ char *data; /* ptr to full text for this element */
+ unsigned int datalen; /* not counting terminating null */
+ ElemType type; /* part of field data represents */
+ unsigned freedata:1; /* free data when done */
+ unsigned freecolor:1; /* free color when done */
+ unsigned freeprintf:8; /* how much alloced for print_format */
+ unsigned wid:16; /* redundant, width from print_format */
+} IELEM_S;
+
+
+/*
+ * 3 is room for '%', '.', and 's'. You need to add one for '\0';
+ * The format string looks like:
+ *
+ * %13.13s or %-13.13s
+ *
+ * The '-' is there if left is set and the 13 is the 'width' in the call.
+ */
+#define PRINT_FORMAT_LEN(width,left) (3 + ((left) ? 1 : 0) + 2 * ((width) > 999 ? 4 : (width) > 99 ? 3 : (width) > 9 ? 2 : 1))
+
+
+/*
+ * Each field consists of a list of elements. The leftadj bit means we
+ * should left adjust the whole thing in the field width, padding on the
+ * right with spaces or truncating on the right. We haven't yet fully
+ * implemented right adjust, except that it is easy for a single element
+ * field. So be careful with multi-element, right-adjusted fields.
+ */
+typedef struct index_field {
+ struct index_field *next;
+ IndexColType ctype;
+ unsigned width:16; /* width of whole field */
+ unsigned leftadj:1; /* left adjust elements in field */
+ IELEM_S *ielem; /* list of elements in this field */
+} IFIELD_S;
+
+
+/*
+ * If the index_cache_entry has an ifield list, it is assumed that the
+ * ifields have had their ielement lists filled in.
+ * The widths_done bit is set after the widths and print formats are setup
+ * to do the correct width. A width change could be done by unsetting
+ * the widths_done bit and then recalculating the widths in the ifields
+ * and resetting the print_format strings in the ielems.
+ */
+typedef struct index_cache_entry {
+ IFIELD_S *ifield; /* list of fields */
+ COLOR_PAIR *linecolor;
+ unsigned color_lookup_done:1; /* efficiency hacks */
+ unsigned widths_done:1;
+ unsigned to_us:1;
+ unsigned cc_us:1;
+ int plus;
+ unsigned long id; /* hash value */
+ struct index_cache_entry *tice; /* thread index header line */
+} ICE_S;
+
+
+/*
+ * Pieces needed to construct a valid folder index entry, and to
+ * control what can be fetched when (due to callbacks and such)
+ */
+typedef struct index_data {
+ MAILSTREAM *stream;
+ ADDRESS *from, /* always valid */
+ *to, /* check valid bit, fetch as req'd */
+ *cc, /* check valid bit, fetch as req'd */
+ *sender; /* check valid bit, fetch as req'd */
+ char *newsgroups, /* check valid bit, fetch as req'd */
+ *subject, /* always valid */
+ *date; /* always valid */
+ long msgno, /* tells us what we're looking at */
+ rawno,
+ size; /* always valid */
+ unsigned no_fetch:1, /* lit when we're in a callback */
+ bogus:2, /* lit when there were problems */
+ valid_to:1, /* trust struct's "to" pointer */
+ valid_cc:1, /* trust struct's "cc" pointer */
+ valid_sender:1, /* trust struct's "sender" pointer */
+ valid_news:1, /* trust struct's "news" pointer */
+ valid_resent_to:1, /* trust struct's "resent-to" ptr */
+ resent_to_us:1; /* lit when we know its true */
+} INDEXDATA_S;
+
+
+/*
+ * Line_hash can't return LINE_HASH_N, so we use it in a couple places as
+ * a special value that we can assign knowing it can't be a real hash value.
+ */
+#define LINE_HASH_N 0xffffffff
+
+
+typedef enum {NoKW, KW, KWInit} SubjKW;
+
+
+/* exported protoypes */
+
+
+#endif /* PITH_INDXTYPE_INCLUDED */
diff --git a/pith/init.c b/pith/init.c
new file mode 100644
index 00000000..d7942dcb
--- /dev/null
+++ b/pith/init.c
@@ -0,0 +1,546 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: init.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ init.c
+ Routines for pine start up and initialization
+ ====*/
+
+
+#include "../pith/headers.h"
+#include "../pith/init.h"
+#include "../pith/conf.h"
+#include "../pith/status.h"
+#include "../pith/folder.h"
+
+
+/*
+ * Internal prototypes
+ */
+int compare_sm_files(const qsort_t *, const qsort_t *);
+
+
+
+/*----------------------------------------------------------------------
+ Sets login, full_username and home_dir
+
+ Args: ps -- The Pine structure to put the user name, etc in
+
+ Result: sets the fullname, login and home_dir field of the pine structure
+ returns 0 on success, -1 if not.
+ ----*/
+
+int
+init_username(struct pine *ps)
+{
+ char *expanded;
+ int rv;
+
+ rv = 0;
+ expanded = NULL;
+#if defined(DOS) || defined(OS2)
+ if(ps->COM_USER_ID)
+ expanded = expand_variables(tmp_20k_buf, SIZEOF_20KBUF,
+ ps->COM_USER_ID, 0);
+
+ if(!expanded && ps->vars[V_USER_ID].post_user_val.p)
+ expanded = expand_variables(tmp_20k_buf, SIZEOF_20KBUF,
+ ps->vars[V_USER_ID].post_user_val.p, 0);
+
+ if(!expanded && ps->vars[V_USER_ID].main_user_val.p)
+ expanded = expand_variables(tmp_20k_buf, SIZEOF_20KBUF,
+ ps->vars[V_USER_ID].main_user_val.p, 0);
+
+ if(!expanded)
+ ps->blank_user_id = 1;
+
+ ps->VAR_USER_ID = cpystr(expanded ? expanded : "");
+#else
+ ps->VAR_USER_ID = cpystr(ps->ui.login);
+ if(!ps->VAR_USER_ID[0]){
+ fprintf(stderr, "Who are you? (Unable to look up login name)\n");
+ rv = -1;
+ }
+#endif
+
+ expanded = NULL;
+ if(ps->vars[V_PERSONAL_NAME].is_fixed){
+ if(ps->FIX_PERSONAL_NAME){
+ expanded = expand_variables(tmp_20k_buf, SIZEOF_20KBUF,
+ ps->FIX_PERSONAL_NAME, 0);
+ }
+ if(ps->vars[V_PERSONAL_NAME].main_user_val.p ||
+ ps->vars[V_PERSONAL_NAME].post_user_val.p){
+ ps_global->give_fixed_warning = 1;
+ ps_global->fix_fixed_warning = 1;
+ }
+ else if(ps->COM_PERSONAL_NAME)
+ ps_global->give_fixed_warning = 1;
+ }
+ else{
+ if(ps->COM_PERSONAL_NAME)
+ expanded = expand_variables(tmp_20k_buf, SIZEOF_20KBUF,
+ ps->COM_PERSONAL_NAME, 0);
+
+ if(!expanded && ps->vars[V_PERSONAL_NAME].post_user_val.p)
+ expanded = expand_variables(tmp_20k_buf, SIZEOF_20KBUF,
+ ps->vars[V_PERSONAL_NAME].post_user_val.p, 0);
+
+ if(!expanded && ps->vars[V_PERSONAL_NAME].main_user_val.p)
+ expanded = expand_variables(tmp_20k_buf, SIZEOF_20KBUF,
+ ps->vars[V_PERSONAL_NAME].main_user_val.p, 0);
+ }
+
+ if(!expanded){
+ expanded = ps->ui.fullname;
+#if defined(DOS) || defined(OS2)
+ ps->blank_personal_name = 1;
+#endif
+ }
+
+ ps->VAR_PERSONAL_NAME = cpystr(expanded ? expanded : "");
+
+ dprint((1, "Userid: %s\nFullname: \"%s\"\n",
+ ps->VAR_USER_ID, ps->VAR_PERSONAL_NAME));
+ return(rv);
+}
+
+
+/*----------------------------------------------------------------------
+ Sets home_dir
+
+ Args: ps -- The Pine structure to put the user name, etc in
+
+ Result: sets the home_dir field of the pine structure
+ returns 0 on success, -1 if not.
+ ----*/
+
+int
+init_userdir(struct pine *ps)
+{
+ char fld_dir[MAXPATH+1];
+
+ if(strlen(ps->home_dir) + strlen(ps->VAR_MAIL_DIRECTORY)+2 > MAXPATH){
+ printf(_("Folders directory name is longer than %d\n"), MAXPATH);
+ printf(_("Directory name: \"%s/%s\"\n"),ps->home_dir,
+ ps->VAR_MAIL_DIRECTORY);
+ return(-1);
+ }
+#if defined(DOS) || defined(OS2)
+ if(ps->VAR_MAIL_DIRECTORY[1] == ':'){
+ strncpy(fld_dir, ps->VAR_MAIL_DIRECTORY, sizeof(fld_dir)-1);
+ fld_dir[sizeof(fld_dir)-1] = '\0';
+ }
+ else
+#endif
+ build_path(fld_dir, ps->home_dir, ps->VAR_MAIL_DIRECTORY, sizeof(fld_dir));
+ ps->folders_dir = cpystr(fld_dir);
+
+ return(0);
+}
+
+
+/*----------------------------------------------------------------------
+ Fetch the hostname of the current system and put it in pine struct
+
+ Args: ps -- The pine structure to put the hostname, etc in
+
+ Result: hostname, localdomain, userdomain and maildomain are set
+
+
+** Pine uses the following set of names:
+ hostname - The fully-qualified hostname. Obtained with
+ gethostbyname() which reads /etc/hosts or does a DNS
+ lookup. This may be blank.
+ localdomain - The domain name without the host. Obtained from the
+ above hostname if it has a "." in it. Removes first
+ segment. If hostname has no "." in it then the hostname
+ is used. This may be blank.
+ userdomain - Explicitly configured domainname. This is read out of the
+ global pine.conf or user's .pinerc. The user's entry in the
+ .pinerc overrides.
+
+** Pine has the following uses for such names:
+
+ 1. On outgoing messages in the From: line
+ Uses userdomain if there is one. If not uses, uses
+ hostname unless Pine has been configured to use localdomain.
+
+ 2. When expanding/fully-qualifying unqualified addresses during
+ composition
+ (same as 1)
+
+ 3. When expanding/fully-qualifying unqualified addresses during
+ composition when a local entry in the password file exists for
+ name.
+ If no userdomain is given, then this lookup is always done
+ and the hostname is used unless Pine's been configured to
+ use the localdomain. If userdomain is defined, it is used,
+ but no local lookup is done. We can't assume users on the
+ local host are valid in the given domain (and, for simplicity,
+ have chosen to ignore the cases userdomain matches localdomain
+ or localhost). Setting user-lookup-even-if-domain-mismatch
+ feature will tell pine to override this behavior and perform
+ the local lookup anyway. The problem of a global "even-if"
+ set and a .pinerc-defined user-domain of something odd causing
+ the local lookup, but this will only effect the personal name,
+ and is not judged to be a significant problem.
+
+ 4. In determining if an address is that of the current pine user for
+ formatting index and filtering addresses when replying
+ If a userdomain is specified the address must match the
+ userdomain exactly. If a userdomain is not specified or the
+ userdomain is the same as the hostname or domainname, then
+ an address will be considered the users if it matches either
+ the domainname or the hostname. Of course, the userid must
+ match too.
+
+ 5. In Message ID's
+ The fully-qualified hostname is always users here.
+
+
+** Setting the domain names
+ To set the domain name for all Pine users on the system to be
+different from what Pine figures out from DNS, set the domain name in
+the "user-domain" variable in pine.conf. To set the domain name for an
+individual user, set the "user-domain" variable in his .pinerc.
+The .pinerc setting overrides any other setting.
+ ----*/
+int
+init_hostname(struct pine *ps)
+{
+ char hostname[MAX_ADDRESS+1], domainname[MAX_ADDRESS+1];
+
+ getdomainnames(hostname, sizeof(hostname)-1,
+ domainname, sizeof(domainname)-1);
+
+ if(ps->hostname)
+ fs_give((void **)&ps->hostname);
+
+ ps->hostname = cpystr(hostname);
+
+ if(ps->localdomain)
+ fs_give((void **)&ps->localdomain);
+
+ ps->localdomain = cpystr(domainname);
+ ps->userdomain = NULL;
+
+ if(ps->VAR_USER_DOMAIN && ps->VAR_USER_DOMAIN[0]){
+ ps->maildomain = ps->userdomain = ps->VAR_USER_DOMAIN;
+ }else{
+#if defined(DOS) || defined(OS2)
+ if(ps->VAR_USER_DOMAIN)
+ ps->blank_user_domain = 1; /* user domain set to null string! */
+
+ ps->maildomain = ps->localdomain[0] ? ps->localdomain : ps->hostname;
+#else
+ ps->maildomain = strucmp(ps->VAR_USE_ONLY_DOMAIN_NAME, "yes")
+ ? ps->hostname : ps->localdomain;
+#endif
+ }
+
+ /*
+ * Tell c-client what domain to use when completing unqualified
+ * addresses it finds in local mailboxes. Remember, it won't
+ * affect what's to the right of '@' for unqualified addresses in
+ * remote folders...
+ */
+ mail_parameters(NULL, SET_LOCALHOST, (void *) ps->maildomain);
+ if(F_OFF(F_QUELL_MAILDOMAIN_WARNING, ps) && !strchr(ps->maildomain, '.')){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Incomplete maildomain \"%s\"."),
+ ps->maildomain);
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ strncpy(tmp_20k_buf,
+ _("Return address in mail you send may be incorrect."), SIZEOF_20KBUF);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ }
+
+ dprint((1,"User domain name being used \"%s\"\n",
+ ps->userdomain == NULL ? "" : ps->userdomain));
+ dprint((1,"Local Domain name being used \"%s\"\n",
+ ps->localdomain ? ps->localdomain : "?"));
+ dprint((1,"Host name being used \"%s\"\n",
+ ps->hostname ? ps->hostname : "?"));
+ dprint((1,
+ "Mail Domain name being used (by c-client too) \"%s\"\n",
+ ps->maildomain ? ps->maildomain : "?"));
+
+ if(!ps->maildomain || !ps->maildomain[0]){
+#if defined(DOS) || defined(OS2)
+ if(ps->blank_user_domain)
+ return(0); /* prompt for this in send.c:dos_valid_from */
+#endif
+ fprintf(stderr, _("No host name or domain name set\n"));
+ return(-1);
+ }
+ else
+ return(0);
+}
+
+
+/*----------------------------------------------------------------------
+ Make sure the default save folders exist in the default
+ save context.
+ ----*/
+void
+init_save_defaults(void)
+{
+ CONTEXT_S *save_cntxt;
+
+ if(!ps_global->VAR_DEFAULT_FCC ||
+ !*ps_global->VAR_DEFAULT_FCC ||
+ !ps_global->VAR_DEFAULT_SAVE_FOLDER ||
+ !*ps_global->VAR_DEFAULT_SAVE_FOLDER)
+ return;
+
+ if(!(save_cntxt = default_save_context(ps_global->context_list)))
+ save_cntxt = ps_global->context_list;
+
+ if(!(folder_exists(save_cntxt, ps_global->VAR_DEFAULT_FCC) & FEX_ISFILE))
+ context_create(save_cntxt, NULL, ps_global->VAR_DEFAULT_FCC);
+
+ if(!(folder_exists(save_cntxt, ps_global->VAR_DEFAULT_SAVE_FOLDER) &
+ FEX_ISFILE))
+ context_create(save_cntxt, NULL, ps_global->VAR_DEFAULT_SAVE_FOLDER);
+
+ free_folder_list(save_cntxt);
+}
+
+
+/*----------------------------------------------------------------------
+ Put sent-mail files in date order
+
+ Args: a, b -- The names of two files. Expects names to be sent-mail-mmm-yy
+ Other names will sort in order and come before those
+ in above format.
+ ----*/
+int
+compare_sm_files(const qsort_t *aa, const qsort_t *bb)
+{
+ struct sm_folder *a = (struct sm_folder *)aa,
+ *b = (struct sm_folder *)bb;
+
+ if(a->month_num == -1 && b->month_num == -1 && a->name && b->name)
+ return(strucmp(a->name, b->name));
+ if(a->month_num == -1) return(-1);
+ if(b->month_num == -1) return(1);
+
+ return(a->month_num - b->month_num);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Create an ordered list of sent-mail folders and their month numbers
+
+ Args: dir -- The directory to find the list of files in
+
+ Result: Pointer to list of files is returned.
+
+This list includes all files that start with "sent-mail", but not "sent-mail"
+itself.
+ ----*/
+struct sm_folder *
+get_mail_list(CONTEXT_S *list_cntxt, char *folder_base)
+{
+ register struct sm_folder *sm = NULL;
+ struct sm_folder *sml = NULL;
+ char *filename;
+ int i, folder_base_len;
+ int max_files;
+ char searchname[MAXPATH+1];
+
+ if((folder_base_len = strlen(folder_base)) == 0 || !list_cntxt){
+ sml = (struct sm_folder *) fs_get(sizeof(struct sm_folder));
+ memset((void *)sml, 0, sizeof(struct sm_folder));
+ return(sml);
+ }
+
+#ifdef DOS
+ if(*list_cntxt->context != '{'){ /* NOT an IMAP collection! */
+ snprintf(searchname, sizeof(searchname), "%4.4s*", folder_base);
+ folder_base_len = strlen(searchname) - 1;
+ }
+ else
+#endif
+ snprintf(searchname, sizeof(searchname), "%.*s*", sizeof(searchname)-2, folder_base);
+
+ build_folder_list(NULL, list_cntxt, searchname, NULL, BFL_FLDRONLY);
+
+ max_files = MIN(MAX(0, folder_total(FOLDERS(list_cntxt))), 5000);
+ sml = sm = (struct sm_folder *) fs_get(sizeof(struct sm_folder)*(max_files+1));
+ memset((void *)sml, 0, sizeof(struct sm_folder) * (max_files+1));
+
+ for(i = 0; i < folder_total(FOLDERS(list_cntxt)); i++){
+ filename = folder_entry(i, FOLDERS(list_cntxt))->name;
+#ifdef DOS
+ if(struncmp(filename, folder_base, folder_base_len) == 0
+ && strucmp(filename, folder_base)){
+
+ if(*list_cntxt->context != '{'){
+ int j;
+ for(j = 0; j < 4; j++)
+ if(!isdigit((unsigned char)filename[folder_base_len + j]))
+ break;
+
+ if(j < 4) /* not proper date format! */
+ continue; /* keep trying */
+ }
+#else
+#ifdef OS2
+ if(strnicmp(filename, folder_base, folder_base_len) == 0
+ && stricmp(filename, folder_base)){
+#else
+ if(strncmp(filename, folder_base, folder_base_len) == 0
+ && strcmp(filename, folder_base)){
+#endif
+#endif
+ sm->name = cpystr(filename);
+#ifdef DOS
+ if(*list_cntxt->context != '{'){ /* NOT an IMAP collection! */
+ sm->month_num = (sm->name[folder_base_len] - '0') * 10;
+ sm->month_num += sm->name[folder_base_len + 1] - '0';
+ }
+ else
+#endif
+ sm->month_num = month_num(sm->name + (size_t)folder_base_len + 1);
+ sm++;
+ if(sm >= &sml[max_files])
+ break; /* Too many files, ignore the rest ; shouldn't occur */
+ }
+ }
+
+ /* anything to sort?? */
+ if(sml->name && *(sml->name) && (sml+1)->name && *((sml+1)->name)){
+ qsort(sml,
+ sm - sml,
+ sizeof(struct sm_folder),
+ compare_sm_files);
+ }
+
+ return(sml);
+}
+
+
+
+int
+check_prune_time(time_t *now, struct tm **tm_now)
+{
+ char tmp[50];
+
+ *now = time((time_t *) 0);
+ *tm_now = localtime(now);
+
+ /*
+ * If the last time we did this is blank (as if pine's run for
+ * first time), don't go thru list asking, but just note it for
+ * the next time...
+ */
+ if(ps_global->VAR_LAST_TIME_PRUNE_QUESTION == NULL){
+ ps_global->last_expire_year = (*tm_now)->tm_year;
+ ps_global->last_expire_month = (*tm_now)->tm_mon;
+ snprintf(tmp, sizeof(tmp), "%d.%d", ps_global->last_expire_year,
+ ps_global->last_expire_month + 1);
+ set_variable(V_LAST_TIME_PRUNE_QUESTION, tmp, 1, 1, Main);
+ return(0);
+ }
+
+ if(ps_global->last_expire_year != -1 &&
+ ((*tm_now)->tm_year < ps_global->last_expire_year ||
+ ((*tm_now)->tm_year == ps_global->last_expire_year &&
+ (*tm_now)->tm_mon <= ps_global->last_expire_month)))
+ return(0);
+
+ return(1);
+}
+
+
+int
+first_run_of_month(void)
+{
+ time_t now;
+ struct tm *tm_now;
+
+ now = time((time_t *) 0);
+ tm_now = localtime(&now);
+
+ if(ps_global->last_expire_year == -1 ||
+ (tm_now->tm_year < ps_global->last_expire_year ||
+ (tm_now->tm_year == ps_global->last_expire_year &&
+ tm_now->tm_mon <= ps_global->last_expire_month)))
+ return(0);
+
+ return(1);
+}
+
+
+int
+first_run_of_year(void)
+{
+ time_t now;
+ struct tm *tm_now;
+
+ now = time((time_t *) 0);
+ tm_now = localtime(&now);
+
+ if(ps_global->last_expire_year == -1 ||
+ (tm_now->tm_year <= ps_global->last_expire_year))
+ return(0);
+
+ return(1);
+}
+
+
+/*
+ * prune_move_folder - rename folder in context and delete old copy
+ * Returns -1 if unsuccessful.
+ */
+int
+prune_move_folder(char *oldpath, char *newpath, CONTEXT_S *prune_cntxt)
+{
+ char spath[MAXPATH+1];
+
+ strncpy(spath, oldpath, sizeof(spath)-1);
+ spath[sizeof(spath)-1] = '\0';
+
+ /*--- User says OK to rename ---*/
+ dprint((5, "rename \"%s\" to \"%s\"\n",
+ spath ? spath : "?", newpath ? newpath : "?"));
+ q_status_message1(SM_ORDER, 1, 3,
+ /* TRANSLATORS: arg is a filename */
+ _("Renaming \"%s\" at start of month"),
+ pretty_fn(spath ? spath : "?"));
+
+ if(!context_rename(prune_cntxt, NULL, spath, newpath)){
+ q_status_message2(SM_ORDER | SM_DING, 3, 4,
+ /* TRANSLATORS: 1st arg is filename, 2nd is error message */
+ _("Error renaming \"%s\": %s"),
+ pretty_fn(spath ? spath : "?"),
+ error_description(errno));
+ dprint((1, "Error renaming %s to %s: %s\n",
+ spath ? spath : "?", newpath ? newpath : "?",
+ error_description(errno)));
+ display_message('x');
+ return -1;
+ }
+
+ context_create(prune_cntxt, NULL, spath);
+
+ return 0;
+}
diff --git a/pith/init.h b/pith/init.h
new file mode 100644
index 00000000..243b53e7
--- /dev/null
+++ b/pith/init.h
@@ -0,0 +1,42 @@
+/*
+ * $Id: init.h 900 2008-01-05 01:13:26Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_INIT_INCLUDED
+#define PITH_INIT_INCLUDED
+
+#include "../pith/state.h"
+#include "../pith/conftype.h"
+#include "../pith/context.h"
+
+
+#define ALPINE_VERSION PACKAGE_VERSION
+
+#define LEGAL_NOTICE \
+ "For Copyright information press \"?\""
+
+/* exported protoypes */
+int init_username(struct pine *);
+int init_userdir(struct pine *);
+int init_hostname(struct pine *);
+void init_save_defaults(void);
+int check_prune_time(time_t *, struct tm **);
+int prune_move_folder(char *, char *, CONTEXT_S *);
+int first_run_of_month(void);
+int first_run_of_year(void);
+struct sm_folder *get_mail_list(CONTEXT_S *, char *);
+
+
+#endif /* PITH_INIT_INCLUDED */
diff --git a/pith/keyword.c b/pith/keyword.c
new file mode 100644
index 00000000..2251e91d
--- /dev/null
+++ b/pith/keyword.c
@@ -0,0 +1,441 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: keyword.c 807 2007-11-09 01:21:33Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/keyword.h"
+#include "../pith/state.h"
+#include "../pith/flag.h"
+#include "../pith/string.h"
+#include "../pith/status.h"
+#include "../pith/util.h"
+
+
+/*
+ * Internal prototypes
+ */
+
+
+/*
+ * Read the keywords array into a KEYWORD_S structure.
+ * Make sure that all of the strings are UTF-8.
+ */
+KEYWORD_S *
+init_keyword_list(char **keywordarray)
+{
+ char **t, *nickname, *keyword;
+ KEYWORD_S *head = NULL, **tail;
+
+ tail = &head;
+ for(t = keywordarray; t && *t && **t; t++){
+ nickname = keyword = NULL;
+ get_pair(*t, &nickname, &keyword, 0, 0);
+ *tail = new_keyword_s(keyword, nickname);
+ tail = &(*tail)->next;
+
+ if(keyword)
+ fs_give((void **) &keyword);
+
+ if(nickname)
+ fs_give((void **) &nickname);
+ }
+
+ return(head);
+}
+
+
+KEYWORD_S *
+new_keyword_s(char *keyword, char *nickname)
+{
+ KEYWORD_S *kw = NULL;
+
+ kw = (KEYWORD_S *) fs_get(sizeof(*kw));
+ memset(kw, 0, sizeof(*kw));
+
+ if(keyword && *keyword)
+ kw->kw = cpystr(keyword);
+
+ if(nickname && *nickname)
+ kw->nick = cpystr(nickname);
+
+ return(kw);
+}
+
+
+void
+free_keyword_list(KEYWORD_S **kl)
+{
+ if(kl && *kl){
+ if((*kl)->next)
+ free_keyword_list(&(*kl)->next);
+
+ if((*kl)->kw)
+ fs_give((void **) &(*kl)->kw);
+
+ if((*kl)->nick)
+ fs_give((void **) &(*kl)->nick);
+
+ fs_give((void **) kl);
+ }
+}
+
+
+/*
+ * Return a pointer to the keyword associated with a nickname, or the
+ * input itself if no match.
+ */
+char *
+nick_to_keyword(char *nick)
+{
+ KEYWORD_S *kw;
+ char *ret;
+
+ ret = nick;
+ for(kw = ps_global->keywords; kw; kw = kw->next)
+ if(!strcmp(nick, kw->nick ? kw->nick : kw->kw ? kw->kw : "")){
+ if(kw->nick)
+ ret = kw->kw;
+
+ break;
+ }
+
+ return(ret);
+}
+
+
+/*
+ * Return a pointer to the nickname associated with a keyword, or the
+ * input itself if no match.
+ */
+char *
+keyword_to_nick(char *keyword)
+{
+ KEYWORD_S *kw;
+ char *ret;
+
+ ret = keyword;
+ for(kw = ps_global->keywords; kw; kw = kw->next)
+ if(!strcmp(keyword, kw->kw ? kw->kw : "")){
+ if(kw->nick)
+ ret = kw->nick;
+
+ break;
+ }
+
+ return(ret);
+}
+
+
+/*
+ * Return a pointer to the keyword associated with an initial, or the
+ * input itself if no match.
+ * Only works for ascii initials.
+ */
+char *
+initial_to_keyword(char *initial)
+{
+ KEYWORD_S *kw;
+ char *ret;
+ int init;
+ int kwinit;
+
+ ret = initial;
+ if(initial[0] && initial[1] == '\0'){
+ init = initial[0];
+ if(isascii(init) && isupper(init))
+ init = tolower((unsigned char) init);
+
+ for(kw = ps_global->keywords; kw; kw = kw->next){
+ if(kw->nick)
+ kwinit = kw->nick[0];
+ else if(kw->kw)
+ kwinit = kw->kw[0];
+
+ if(isascii(kwinit) && isupper(kwinit))
+ kwinit = tolower((unsigned char) kwinit);
+
+ if(kwinit == init){
+ ret = kw->kw;
+ break;
+ }
+ }
+ }
+
+ return(ret);
+}
+
+
+int
+user_flag_is_set(MAILSTREAM *stream, long unsigned int rawno, char *keyword)
+{
+ int j, is_set = 0;
+ MESSAGECACHE *mc;
+
+ if(stream && keyword && keyword[0]){
+ if(rawno > 0L && stream
+ && rawno <= stream->nmsgs
+ && (mc = mail_elt(stream, rawno)) != NULL){
+ j = user_flag_index(stream, keyword);
+ if(j >= 0 && j < NUSERFLAGS && ((1 << j) & mc->user_flags))
+ is_set++;
+ }
+ }
+
+ return(is_set);
+}
+
+
+/*
+ * Returns the bit position of the keyword in stream, else -1.
+ */
+int
+user_flag_index(MAILSTREAM *stream, char *keyword)
+{
+ int i, retval = -1;
+ char *p;
+
+ if(stream && keyword && keyword[0]){
+ for(i = 0; i < NUSERFLAGS; i++)
+ if((p=stream_to_user_flag_name(stream, i)) && !strucmp(keyword, p)){
+ retval = i;
+ break;
+ }
+ }
+
+ return(retval);
+}
+
+
+char *
+stream_to_user_flag_name(MAILSTREAM *stream, int k)
+{
+ if(stream && stream->user_flags && k >= 0 && k < NUSERFLAGS)
+ return(stream->user_flags[k]);
+
+ return(NULL);
+}
+
+
+int
+some_user_flags_defined(MAILSTREAM *stream)
+{
+ int k;
+
+ if(stream)
+ for(k = 0; k < NUSERFLAGS; k++)
+ if(stream_to_user_flag_name(stream, k))
+ return 1;
+
+ return 0;
+}
+
+
+/*----------------------------------------------------------------------
+ Build flags string based on requested flags and what's set in messagecache
+
+ Args: flags -- flags to test
+
+ Result: allocated, space-delimited flags string is returned
+ ----*/
+char *
+flag_string(MAILSTREAM *stream, long rawno, long int flags)
+{
+ MESSAGECACHE *mc;
+ char *p, *q, *returned_flags = NULL;
+ size_t len = 0;
+ int k;
+
+ mc = (rawno > 0L && stream && rawno <= stream->nmsgs)
+ ? mail_elt(stream, rawno) : NULL;
+ if(!mc)
+ return returned_flags;
+
+ if((flags & F_DEL) && mc->deleted)
+ len += strlen("\\DELETED") + 1;
+
+ if((flags & F_ANS) && mc->answered)
+ len += strlen("\\ANSWERED") + 1;
+
+ if((flags & F_FWD) && user_flag_is_set(stream, rawno, FORWARDED_FLAG))
+ len += strlen(FORWARDED_FLAG) + 1;
+
+ if((flags & F_FLAG) && mc->flagged)
+ len += strlen("\\FLAGGED") + 1;
+
+ if((flags & F_SEEN) && mc->seen)
+ len += strlen("\\SEEN") + 1;
+
+ if((flags & F_KEYWORD) && stream->user_flags){
+ for(k = 0; k < NUSERFLAGS; k++){
+ if((q=stream_to_user_flag_name(stream, k))
+ && user_flag_is_set(stream, rawno, q)){
+ len += strlen(q) + 1;
+ }
+ }
+ }
+
+ returned_flags = (char *) fs_get((len+1) * sizeof(char));
+ p = returned_flags;
+ *p = '\0';
+
+ if((flags & F_DEL) && mc->deleted)
+ sstrncpy(&p, "\\DELETED ", len+1-(p-returned_flags));
+
+ if((flags & F_ANS) && mc->answered)
+ sstrncpy(&p, "\\ANSWERED ", len+1-(p-returned_flags));
+
+ if((flags & F_FWD) && user_flag_is_set(stream, rawno, FORWARDED_FLAG)){
+ sstrncpy(&p, FORWARDED_FLAG, len+1-(p-returned_flags));
+ sstrncpy(&p, " ", len+1-(p-returned_flags));
+ }
+
+ if((flags & F_FLAG) && mc->flagged)
+ sstrncpy(&p, "\\FLAGGED ", len+1-(p-returned_flags));
+
+ if((flags & F_SEEN) && mc->seen)
+ sstrncpy(&p, "\\SEEN ", len+1-(p-returned_flags));
+
+ if((flags & F_KEYWORD) && stream->user_flags){
+ for(k = 0; k < NUSERFLAGS; k++){
+ if((q=stream_to_user_flag_name(stream, k))
+ && user_flag_is_set(stream, rawno, q)){
+ sstrncpy(&p, q, len+1-(p-returned_flags));
+ sstrncpy(&p, " ", len+1-(p-returned_flags));
+ len += strlen(p) + 1;
+ }
+ }
+ }
+
+ if(p != returned_flags && (len+1-(p-returned_flags)>0))
+ *--p = '\0';
+
+ return returned_flags;
+}
+
+
+long
+get_msgno_by_msg_id(MAILSTREAM *stream, char *message_id, MSGNO_S *msgmap)
+{
+ SEARCHPGM *pgm = NULL;
+ long hint = mn_m2raw(msgmap, mn_get_cur(msgmap));
+ long newmsgno = -1L;
+ int iter = 0;
+ MESSAGECACHE *mc;
+ extern MAILSTREAM *mm_search_stream;
+ extern long mm_search_count;
+
+ if(!(message_id && message_id[0]) || stream->nmsgs < 1L)
+ return(newmsgno);
+
+ mm_search_count = 0L;
+ mm_search_stream = stream;
+ while(mm_search_count == 0L && iter++ < 3
+ && (pgm = mail_newsearchpgm()) != NULL){
+ pgm->message_id = mail_newstringlist();
+ pgm->message_id->text.data = (unsigned char *) cpystr(message_id);
+ pgm->message_id->text.size = strlen(message_id);
+
+ if(iter > 1 || hint > stream->nmsgs)
+ iter++;
+
+ if(iter == 1){
+ /* restrict to hint message on first try */
+ pgm->msgno = mail_newsearchset();
+ pgm->msgno->first = pgm->msgno->last = hint;
+ }
+ else if(iter == 2){
+ /* restrict to last 50 messages on 2nd try */
+ pgm->msgno = mail_newsearchset();
+ if(stream->nmsgs > 100L)
+ pgm->msgno->first = stream->nmsgs-50L;
+ else{
+ pgm->msgno->first = 1L;
+ iter++;
+ }
+
+ pgm->msgno->last = stream->nmsgs;
+ }
+
+ pine_mail_search_full(stream, NULL, pgm, SE_NOPREFETCH | SE_FREE);
+
+ if(mm_search_count){
+ for(newmsgno=stream->nmsgs; newmsgno > 0L; newmsgno--)
+ if((mc = mail_elt(stream, newmsgno)) && mc->searched)
+ break;
+ }
+ }
+
+ return(mn_raw2m(msgmap, newmsgno));
+}
+
+
+/*
+ * These chars are not allowed in keywords.
+ *
+ * Returns 0 if ok, 1 if not.
+ * Returns an allocated error message on error.
+ */
+int
+keyword_check(char *kw, char **error)
+{
+ register char *t;
+ char buf[100], *p;
+
+ if(!kw || !kw[0])
+ return 1;
+
+ kw = nick_to_keyword(kw);
+
+ for(p = kw; p && *p; p++)
+ if(!isascii(*p)){
+ if(error)
+ *error = cpystr("Keywords must be all ASCII characters");
+
+ return 1;
+ }
+
+ if((t = strindex(kw, SPACE)) ||
+ (t = strindex(kw, '{')) ||
+ (t = strindex(kw, '(')) ||
+ (t = strindex(kw, ')')) ||
+ (t = strindex(kw, ']')) ||
+ (t = strindex(kw, '%')) ||
+ (t = strindex(kw, '"')) ||
+ (t = strindex(kw, '\\')) ||
+ (t = strindex(kw, '*'))){
+ char s[4];
+ s[0] = '"';
+ s[1] = *t;
+ s[2] = '"';
+ s[3] = '\0';
+ if(error){
+ snprintf(buf, sizeof(buf), "%s not allowed in keywords",
+ *t == SPACE ?
+ "Spaces" :
+ *t == '"' ?
+ "Quotes" :
+ *t == '%' ?
+ "Percents" :
+ s);
+ *error = cpystr(buf);
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/pith/keyword.h b/pith/keyword.h
new file mode 100644
index 00000000..915de83c
--- /dev/null
+++ b/pith/keyword.h
@@ -0,0 +1,48 @@
+/*
+ * $Id: keyword.h 807 2007-11-09 01:21:33Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_KEYWORD_INCLUDED
+#define PITH_KEYWORD_INCLUDED
+
+
+#include "../pith/msgno.h"
+
+
+/* these are always UTF-8 */
+typedef struct keywords {
+ char *kw;
+ char *nick;
+ struct keywords *next;
+} KEYWORD_S;
+
+
+/* exported protoypes */
+KEYWORD_S *init_keyword_list(char **);
+KEYWORD_S *new_keyword_s(char *, char *);
+void free_keyword_list(KEYWORD_S **);
+char *nick_to_keyword(char *);
+char *keyword_to_nick(char *);
+char *initial_to_keyword(char *);
+int user_flag_is_set(MAILSTREAM *, unsigned long, char *);
+int user_flag_index(MAILSTREAM *, char *);
+char *stream_to_user_flag_name(MAILSTREAM *, int);
+int some_user_flags_defined(MAILSTREAM *);
+char *flag_string(MAILSTREAM *, long, long);
+long get_msgno_by_msg_id(MAILSTREAM *, char *, MSGNO_S *);
+int keyword_check(char *, char **);
+
+
+#endif /* PITH_KEYWORD_INCLUDED */
diff --git a/pith/ldap.c b/pith/ldap.c
new file mode 100644
index 00000000..d40641e4
--- /dev/null
+++ b/pith/ldap.c
@@ -0,0 +1,1798 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: ldap.c 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/ldap.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/status.h"
+#include "../pith/util.h"
+#include "../pith/imap.h"
+#include "../pith/busy.h"
+#include "../pith/signal.h"
+#include "../pith/ablookup.h"
+
+
+/*
+ * Until we can think of a better way to do this. If user exits from an
+ * ldap address selection screen we want to return -1 so that call_builder
+ * will stay on the same line. Might want to use another return value
+ * for the builder so that call_builder more fully understands what we're
+ * up to.
+ */
+int wp_exit;
+int wp_nobail;
+
+
+#ifdef ENABLE_LDAP
+/*
+ * Hook to allow user input on whether or not to save chosen LDAP result
+ */
+void (*pith_opt_save_ldap_entry)(struct pine *, LDAP_CHOOSE_S *, int);
+
+
+/*
+ * Internal prototypes
+ */
+LDAP_SERV_RES_S *ldap_lookup(LDAP_SERV_S *, char *, CUSTOM_FILT_S *, WP_ERR_S *, int);
+LDAP_SERV_S *copy_ldap_serv_info(LDAP_SERV_S *);
+int our_ldap_get_lderrno(LDAP *, char **, char **);
+int our_ldap_set_lderrno(LDAP *, int, char *, char *);
+#endif /* ENABLE_LDAP */
+
+
+/*
+ * This function does white pages lookups.
+ *
+ * Args string -- the string to use in the lookup
+ *
+ * Returns NULL -- lookup failed
+ * Address -- A single address is returned if lookup was successfull.
+ */
+ADDRESS *
+wp_lookups(char *string, WP_ERR_S *wp_err, int recursing)
+{
+ ADDRESS *ret_a = NULL;
+ char ebuf[200];
+#ifdef ENABLE_LDAP
+ LDAP_SERV_RES_S *free_when_done = NULL;
+ LDAPLookupStyle style;
+ LDAP_CHOOSE_S *winning_e = NULL;
+ LDAP_SERV_S *info = NULL;
+ static char *fakedomain = "@";
+ char *tmp_a_string;
+ int auwe_rv = 0;
+
+ /*
+ * Runtime ldap lookup of addrbook entry.
+ */
+ if(!strncmp(string, RUN_LDAP, LEN_RL)){
+ LDAP_SERV_RES_S *head_of_result_list;
+
+ info = break_up_ldap_server(string+LEN_RL);
+ head_of_result_list = ldap_lookup(info, "", NULL, wp_err, 1);
+
+ if(head_of_result_list){
+ if(!wp_exit)
+ auwe_rv = ask_user_which_entry(head_of_result_list, string,
+ &winning_e,
+ wp_err,
+ wp_err->wp_err_occurred
+ ? DisplayIfOne : DisplayIfTwo);
+
+ if(auwe_rv != -5)
+ free_when_done = head_of_result_list;
+ }
+ else{
+ wp_err->wp_err_occurred = 1;
+ if(wp_err->error){
+ q_status_message(SM_ORDER, 3, 5, wp_err->error);
+ display_message('x');
+ fs_give((void **)&wp_err->error);
+ }
+
+ /* try using backup email address */
+ if(info && info->mail && *info->mail){
+ tmp_a_string = cpystr(info->mail);
+ rfc822_parse_adrlist(&ret_a, tmp_a_string, fakedomain);
+ fs_give((void **)&tmp_a_string);
+
+ wp_err->error =
+ cpystr(_("Directory lookup failed, using backup email address"));
+ }
+ else{
+ /*
+ * Do this so the awful LDAP: ... string won't show up
+ * in the composer. This shouldn't actually happen in
+ * real life, so we're not too concerned about it. If we
+ * were we'd want to recover the nickname we started with
+ * somehow, or something like that.
+ */
+ ret_a = mail_newaddr();
+ ret_a->mailbox = cpystr("missing-username");
+ wp_err->error = cpystr(_("Directory lookup failed, no backup email address available"));
+ }
+
+ q_status_message(SM_ORDER, 3, 5, wp_err->error);
+ display_message('x');
+ }
+ }
+ else{
+ style = F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global)
+ ? DisplayIfOne : DisplayIfTwo;
+ auwe_rv = ldap_lookup_all(string, as.n_serv, recursing, style, NULL,
+ &winning_e, wp_err, &free_when_done);
+ }
+
+ if(winning_e && auwe_rv != -5){
+ ret_a = address_from_ldap(winning_e);
+
+ if(pith_opt_save_ldap_entry && ret_a && F_ON(F_ADD_LDAP_TO_ABOOK, ps_global) && !info)
+ (*pith_opt_save_ldap_entry)(ps_global, winning_e, 1);
+
+
+ fs_give((void **)&winning_e);
+ }
+
+ /* Info's only set in the RUN_LDAP case */
+ if(info){
+ if(ret_a && ret_a->host){
+ ADDRESS *backup = NULL;
+
+ if(info->mail && *info->mail)
+ rfc822_parse_adrlist(&backup, info->mail, fakedomain);
+
+ if(!backup || !address_is_same(ret_a, backup)){
+ if(wp_err->error){
+ q_status_message(SM_ORDER, 3, 5, wp_err->error);
+ display_message('x');
+ fs_give((void **)&wp_err->error);
+ }
+
+ snprintf(ebuf, sizeof(ebuf),
+ _("Warning: current address different from saved address (%s)"),
+ info->mail);
+ wp_err->error = cpystr(ebuf);
+ q_status_message(SM_ORDER, 3, 5, wp_err->error);
+ display_message('x');
+ }
+
+ if(backup)
+ mail_free_address(&backup);
+ }
+
+ free_ldap_server_info(&info);
+ }
+
+ if(free_when_done)
+ free_ldap_result_list(&free_when_done);
+#endif /* ENABLE_LDAP */
+
+ if(ret_a){
+ if(ret_a->mailbox){ /* indicates there was a MAIL attribute */
+ if(!ret_a->host || !ret_a->host[0]){
+ if(ret_a->host)
+ fs_give((void **)&ret_a->host);
+
+ ret_a->host = cpystr("missing-hostname");
+ wp_err->wp_err_occurred = 1;
+ if(wp_err->error)
+ fs_give((void **)&wp_err->error);
+
+ wp_err->error = cpystr(_("Missing hostname in LDAP address"));
+ q_status_message(SM_ORDER, 3, 5, wp_err->error);
+ display_message('x');
+ }
+
+ if(!ret_a->mailbox[0]){
+ if(ret_a->mailbox)
+ fs_give((void **)&ret_a->mailbox);
+
+ ret_a->mailbox = cpystr("missing-username");
+ wp_err->wp_err_occurred = 1;
+ if(wp_err->error)
+ fs_give((void **)&wp_err->error);
+
+ wp_err->error = cpystr(_("Missing username in LDAP address"));
+ q_status_message(SM_ORDER, 3, 5, wp_err->error);
+ display_message('x');
+ }
+ }
+ else{
+ wp_err->wp_err_occurred = 1;
+
+ if(wp_err->error)
+ fs_give((void **)&wp_err->error);
+
+ snprintf(ebuf, sizeof(ebuf), _("No email address available for \"%s\""),
+ (ret_a->personal && *ret_a->personal)
+ ? ret_a->personal
+ : "selected entry");
+ wp_err->error = cpystr(ebuf);
+ q_status_message(SM_ORDER, 3, 5, wp_err->error);
+ display_message('x');
+ mail_free_address(&ret_a);
+ ret_a = NULL;
+ }
+ }
+
+ return(ret_a);
+}
+
+
+#ifdef ENABLE_LDAP
+
+/*
+ * Goes through all servers looking up string.
+ *
+ * Args string -- String to search for
+ * who -- Which servers to look on
+ * style -- Sometimes we want to display no matter what, sometimes
+ * only if more than one entry, sometimes only if email
+ * addresses, ...
+ *
+ * The possible styles are:
+ * AlwaysDisplayAndMailRequired: This happens when user ^T's from
+ * composer to address book screen, and
+ * then queries directory server.
+ * AlwaysDisplay: This happens when user is browsing
+ * in address book maintenance screen and
+ * then queries directory server.
+ * DisplayIfOne: These two come from implicit lookups
+ * DisplayIfTwo: from build_address. If the compose rejects
+ * unqualified feature is on we get the
+ * DisplayIfOne, otherwise IfTwo.
+ *
+ * cust -- Use this custom filter instead of configured filters
+ * winning_e -- Return value
+ * wp_err -- Error handling
+ * free_when_done -- Caller needs to free this
+ *
+ * Returns -- value returned by ask_user_which_entry
+ */
+int
+ldap_lookup_all(char *string, int who, int recursing, LDAPLookupStyle style,
+ CUSTOM_FILT_S *cust, LDAP_CHOOSE_S **winning_e,
+ WP_ERR_S *wp_err, LDAP_SERV_RES_S **free_when_done)
+{
+ int retval = -1;
+ LDAP_SERV_RES_S *head_of_result_list = NULL;
+
+ wp_exit = wp_nobail = 0;
+ if(free_when_done)
+ *free_when_done = NULL;
+
+ head_of_result_list = ldap_lookup_all_work(string, who, recursing,
+ cust, wp_err);
+
+ if(!wp_exit)
+ retval = ask_user_which_entry(head_of_result_list, string, winning_e,
+ wp_err,
+ (wp_err->wp_err_occurred &&
+ style == DisplayIfTwo) ? DisplayIfOne
+ : style);
+
+ /*
+ * Because winning_e probably points into the result list
+ * we need to leave the result list alone and have the caller
+ * free it after they are done with winning_e.
+ */
+ if(retval != -5 && free_when_done)
+ *free_when_done = head_of_result_list;
+
+ return(retval);
+}
+
+
+/*
+ * Goes through all servers looking up string.
+ *
+ * Args string -- String to search for
+ * who -- Which servers to look on
+ * cust -- Use this custom filter instead of configured filters
+ * wp_err -- Error handling
+ *
+ * Returns -- list of results that needs to be freed by caller
+ */
+LDAP_SERV_RES_S *
+ldap_lookup_all_work(char *string, int who, int recursing,
+ CUSTOM_FILT_S *cust, WP_ERR_S *wp_err)
+{
+ int i;
+ LDAP_SERV_RES_S *serv_res;
+ LDAP_SERV_RES_S *rr, *head_of_result_list = NULL;
+
+ /* If there is at least one server */
+ if(ps_global->VAR_LDAP_SERVERS && ps_global->VAR_LDAP_SERVERS[0] &&
+ ps_global->VAR_LDAP_SERVERS[0][0]){
+ int how_many_servers;
+
+ for(i = 0; ps_global->VAR_LDAP_SERVERS[i] &&
+ ps_global->VAR_LDAP_SERVERS[i][0]; i++)
+ ;
+
+ how_many_servers = i;
+
+ /* For each server in list */
+ for(i = 0; !wp_exit && ps_global->VAR_LDAP_SERVERS[i] &&
+ ps_global->VAR_LDAP_SERVERS[i][0]; i++){
+ LDAP_SERV_S *info;
+
+ dprint((6, "ldap_lookup_all_work: lookup on server (%.256s)\n",
+ ps_global->VAR_LDAP_SERVERS[i]));
+ info = NULL;
+ if(who == -1 || who == i || who == as.n_serv)
+ info = break_up_ldap_server(ps_global->VAR_LDAP_SERVERS[i]);
+
+ /*
+ * Who tells us which servers to look on.
+ * Who == -1 means all servers.
+ * Who == 0 means server[0].
+ * Who == 1 means server[1].
+ * Who == as.n_serv means query on those with impl set.
+ */
+ if(!(who == -1 || who == i ||
+ (who == as.n_serv && !recursing && info && info->impl) ||
+ (who == as.n_serv && recursing && info && info->rhs))){
+
+ if(info)
+ free_ldap_server_info(&info);
+
+ continue;
+ }
+
+ dprint((6, "ldap_lookup_all_work: ldap_lookup (server: %.20s...)(string: %s)\n",
+ ps_global->VAR_LDAP_SERVERS[i], string));
+ serv_res = ldap_lookup(info, string, cust,
+ wp_err, how_many_servers > 1);
+ if(serv_res){
+ /* Add new one to end of list so they come in the right order */
+ for(rr = head_of_result_list; rr && rr->next; rr = rr->next)
+ ;
+
+ if(rr)
+ rr->next = serv_res;
+ else
+ head_of_result_list = serv_res;
+ }
+
+ if(info)
+ free_ldap_server_info(&info);
+ }
+ }
+
+ return(head_of_result_list);
+}
+
+
+/*
+ * Do an LDAP lookup to the server described in the info argument.
+ *
+ * Args info -- LDAP info for server.
+ * string -- String to lookup.
+ * cust -- Possible custom filter description.
+ * wp_err -- We set this is we get a white pages error.
+ * name_in_error -- Caller sets this if they want us to include the server
+ * name in error messages.
+ *
+ * Returns Results of lookup, NULL if lookup failed.
+ */
+LDAP_SERV_RES_S *
+ldap_lookup(LDAP_SERV_S *info, char *string, CUSTOM_FILT_S *cust,
+ WP_ERR_S *wp_err, int name_in_error)
+{
+ char ebuf[900];
+ char buf[900];
+ char *serv, *base, *serv_errstr;
+ char *mailattr, *snattr, *gnattr, *cnattr;
+ int we_cancel = 0, we_turned_on = 0;
+ LDAP_SERV_RES_S *serv_res = NULL;
+ LDAP *ld;
+ long pwdtrial = 0L;
+ int ld_errnum;
+ char *ld_errstr;
+
+
+ if(!info)
+ return(serv_res);
+
+ serv = cpystr((info->serv && *info->serv) ? info->serv : "?");
+
+ if(name_in_error)
+ snprintf(ebuf, sizeof(ebuf), " (%s)",
+ (info->nick && *info->nick) ? info->nick : serv);
+ else
+ ebuf[0] = '\0';
+
+ serv_errstr = cpystr(ebuf);
+ base = cpystr(info->base ? info->base : "");
+
+ if(info->port < 0)
+ info->port = LDAP_PORT;
+
+ if(info->type < 0)
+ info->type = DEF_LDAP_TYPE;
+
+ if(info->srch < 0)
+ info->srch = DEF_LDAP_SRCH;
+
+ if(info->time < 0)
+ info->time = DEF_LDAP_TIME;
+
+ if(info->size < 0)
+ info->size = DEF_LDAP_SIZE;
+
+ if(info->scope < 0)
+ info->scope = DEF_LDAP_SCOPE;
+
+ mailattr = (info->mailattr && info->mailattr[0]) ? info->mailattr
+ : DEF_LDAP_MAILATTR;
+ snattr = (info->snattr && info->snattr[0]) ? info->snattr
+ : DEF_LDAP_SNATTR;
+ gnattr = (info->gnattr && info->gnattr[0]) ? info->gnattr
+ : DEF_LDAP_GNATTR;
+ cnattr = (info->cnattr && info->cnattr[0]) ? info->cnattr
+ : DEF_LDAP_CNATTR;
+
+ /*
+ * We may want to keep ldap handles open, but at least for
+ * now, re-open them every time.
+ */
+
+ dprint((3, "ldap_lookup(%s,%d)\n", serv ? serv : "?", info->port));
+
+ snprintf(ebuf, sizeof(ebuf), "Searching%s%s%s on %s",
+ (string && *string) ? " for \"" : "",
+ (string && *string) ? string : "",
+ (string && *string) ? "\"" : "",
+ serv);
+ we_turned_on = intr_handling_on(); /* this erases keymenu */
+ we_cancel = busy_cue(ebuf, NULL, 0);
+ if(wp_err->mangled)
+ *(wp_err->mangled) = 1;
+
+#ifdef _SOLARIS_SDK
+ if(info->tls || info->tlsmust)
+ ldapssl_client_init(NULL, NULL);
+ if((ld = ldap_init(serv, info->port)) == NULL)
+#else
+#if (LDAPAPI >= 11)
+ if((ld = ldap_init(serv, info->port)) == NULL)
+#else
+ if((ld = ldap_open(serv, info->port)) == NULL)
+#endif
+#endif
+ {
+ /* TRANSLATORS: All of the three args together are an error message */
+ snprintf(ebuf, sizeof(ebuf), _("Access to LDAP server failed: %s%s(%s)"),
+ errno ? error_description(errno) : "",
+ errno ? " " : "",
+ serv);
+ wp_err->wp_err_occurred = 1;
+ if(wp_err->error)
+ fs_give((void **)&wp_err->error);
+
+ wp_err->error = cpystr(ebuf);
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ q_status_message(SM_ORDER, 3, 5, wp_err->error);
+ display_message('x');
+ dprint((2, "%s\n", ebuf));
+ }
+ else if(!ps_global->intr_pending){
+ int proto = 3, tlsmustbail = 0;
+ char pwd[NETMAXPASSWD], user[NETMAXUSER];
+ char *passwd = NULL;
+ char hostbuf[1024];
+ NETMBX mb;
+#ifndef _WINDOWS
+ int rc;
+#endif
+
+ memset(&mb, 0, sizeof(mb));
+
+#ifdef _SOLARIS_SDK
+ if(info->tls || info->tlsmust)
+ rc = ldapssl_install_routines(ld);
+#endif
+
+ if(ldap_v3_is_supported(ld) &&
+ our_ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &proto) == 0){
+ dprint((5, "ldap: using version 3 protocol\n"));
+ }
+
+ /*
+ * If we don't set RESTART then the select() waiting for the answer
+ * in libldap will be interrupted and stopped by our busy_cue.
+ */
+ our_ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
+
+ /*
+ * If we need to authenticate, get the password. We are not
+ * supporting SASL authentication, just LDAP simple.
+ */
+ if(info->binddn && info->binddn[0]){
+ char pmt[500];
+ char *space;
+
+ snprintf(hostbuf, sizeof(hostbuf), "{%s}dummy", info->serv ? info->serv : "?");
+
+ /*
+ * We don't handle multiple space-delimited hosts well.
+ * We don't know which we're asking for a password for.
+ * We're not connected yet so we can't know.
+ */
+ if((space=strindex(hostbuf, ' ')) != NULL)
+ *space = '\0';
+
+ mail_valid_net_parse_work(hostbuf, &mb, "ldap");
+ mb.port = info->port;
+ mb.tlsflag = (info->tls || info->tlsmust) ? 1 : 0;
+
+try_password_again:
+
+ if(mb.tlsflag
+ && (pwdtrial > 0 ||
+#ifndef _WINDOWS
+#ifdef _SOLARIS_SDK
+ (rc == LDAP_SUCCESS)
+#else /* !_SOLARIS_SDK */
+ ((rc=ldap_start_tls_s(ld, NULL, NULL)) == LDAP_SUCCESS)
+#endif /* !_SOLARIS_SDK */
+#else /* _WINDOWS */
+ 0 /* TODO: find a way to do this in Windows */
+#endif /* _WINDOWS */
+ ))
+ mb.tlsflag = 1;
+ else
+ mb.tlsflag = 0;
+
+ if((info->tls || info->tlsmust) && !mb.tlsflag){
+ q_status_message(SM_ORDER, 3, 5, "Not able to start TLS encryption for LDAP server");
+ if(info->tlsmust)
+ tlsmustbail++;
+ }
+
+ if(!tlsmustbail){
+ snprintf(pmt, sizeof(pmt), " %s", (info->nick && *info->nick) ? info->nick : serv);
+ mm_login_work(&mb, user, pwd, pwdtrial, pmt, info->binddn);
+ if(pwd && pwd[0])
+ passwd = pwd;
+ }
+ }
+
+
+ /*
+ * LDAPv2 requires the bind. v3 doesn't require it but we want
+ * to tell the server we're v3 if the server supports v3, and if the
+ * server doesn't support v3 the bind is required.
+ */
+ if(tlsmustbail || ldap_simple_bind_s(ld, info->binddn, passwd) != LDAP_SUCCESS){
+ wp_err->wp_err_occurred = 1;
+
+ ld_errnum = our_ldap_get_lderrno(ld, NULL, &ld_errstr);
+
+ if(!tlsmustbail && info->binddn && info->binddn[0] && pwdtrial < 2L
+ && ld_errnum == LDAP_INVALID_CREDENTIALS){
+ pwdtrial++;
+ q_status_message(SM_ORDER, 3, 5, _("Invalid password"));
+ goto try_password_again;
+ }
+
+ snprintf(ebuf, sizeof(ebuf), _("LDAP server failed: %s%s%s%s"),
+ ldap_err2string(ld_errnum),
+ serv_errstr,
+ (ld_errstr && *ld_errstr) ? ": " : "",
+ (ld_errstr && *ld_errstr) ? ld_errstr : "");
+
+ if(wp_err->error)
+ fs_give((void **)&wp_err->error);
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ ldap_unbind(ld);
+ wp_err->error = cpystr(ebuf);
+ q_status_message(SM_ORDER, 3, 5, wp_err->error);
+ display_message('x');
+ dprint((2, "%s\n", ebuf));
+ }
+ else if(!ps_global->intr_pending){
+ int srch_res, args, slen, flen;
+#define TEMPLATELEN 512
+ char filt_template[TEMPLATELEN + 1];
+ char filt_format[2*TEMPLATELEN + 1];
+ char filter[2*TEMPLATELEN + 1];
+ char scp[2*TEMPLATELEN + 1];
+ char *p, *q;
+ LDAPMessage *res = NULL;
+ int intr_happened = 0;
+ int tl;
+
+ tl = (info->time == 0) ? info->time : info->time + 10;
+
+ our_ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &tl);
+ our_ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &info->size);
+
+ /*
+ * If a custom filter has been passed in and it doesn't include a
+ * request to combine it with the configured filter, then replace
+ * any configured filter with the passed in filter.
+ */
+ if(cust && cust->filt && !cust->combine){
+ if(info->cust)
+ fs_give((void **)&info->cust);
+
+ info->cust = cpystr(cust->filt);
+ }
+
+ if(info->cust && *info->cust){ /* use custom filter if present */
+ strncpy(filt_template, info->cust, sizeof(filt_template));
+ filt_template[sizeof(filt_template)-1] = '\0';
+ }
+ else{ /* else use configured filter */
+ switch(info->type){
+ case LDAP_TYPE_SUR:
+ snprintf(filt_template, sizeof(filt_template), "(%s=%%s)", snattr);
+ break;
+ case LDAP_TYPE_GIVEN:
+ snprintf(filt_template, sizeof(filt_template), "(%s=%%s)", gnattr);
+ break;
+ case LDAP_TYPE_EMAIL:
+ snprintf(filt_template, sizeof(filt_template), "(%s=%%s)", mailattr);
+ break;
+ case LDAP_TYPE_CN_EMAIL:
+ snprintf(filt_template, sizeof(filt_template), "(|(%s=%%s)(%s=%%s))", cnattr,
+ mailattr);
+ break;
+ case LDAP_TYPE_SUR_GIVEN:
+ snprintf(filt_template, sizeof(filt_template), "(|(%s=%%s)(%s=%%s))",
+ snattr, gnattr);
+ break;
+ case LDAP_TYPE_SEVERAL:
+ snprintf(filt_template, sizeof(filt_template),
+ "(|(%s=%%s)(%s=%%s)(%s=%%s)(%s=%%s))",
+ cnattr, mailattr, snattr, gnattr);
+ break;
+ default:
+ case LDAP_TYPE_CN:
+ snprintf(filt_template, sizeof(filt_template), "(%s=%%s)", cnattr);
+ break;
+ }
+ }
+
+ /* just copy if custom */
+ if(info->cust && *info->cust)
+ info->srch = LDAP_SRCH_EQUALS;
+
+ p = filt_template;
+ q = filt_format;
+ memset((void *)filt_format, 0, sizeof(filt_format));
+ args = 0;
+ while(*p && (q - filt_format) + 4 < sizeof(filt_format)){
+ if(*p == '%' && *(p+1) == 's'){
+ args++;
+ switch(info->srch){
+ /* Exact match */
+ case LDAP_SRCH_EQUALS:
+ *q++ = *p++;
+ *q++ = *p++;
+ break;
+
+ /* Append wildcard after %s */
+ case LDAP_SRCH_BEGINS:
+ *q++ = *p++;
+ *q++ = *p++;
+ *q++ = '*';
+ break;
+
+ /* Insert wildcard before %s */
+ case LDAP_SRCH_ENDS:
+ *q++ = '*';
+ *q++ = *p++;
+ *q++ = *p++;
+ break;
+
+ /* Put wildcard before and after %s */
+ default:
+ case LDAP_SRCH_CONTAINS:
+ *q++ = '*';
+ *q++ = *p++;
+ *q++ = *p++;
+ *q++ = '*';
+ break;
+ }
+ }
+ else
+ *q++ = *p++;
+ }
+
+ if(q - filt_format < sizeof(filt_format))
+ *q = '\0';
+
+ filt_format[sizeof(filt_format)-1] = '\0';
+
+ /*
+ * If combine is lit we put the custom filter and the filt_format
+ * filter and combine them with an &.
+ */
+ if(cust && cust->filt && cust->combine){
+ char *combined;
+ size_t l;
+
+ l = strlen(filt_format) + strlen(cust->filt) + 3;
+ combined = (char *) fs_get((l+1) * sizeof(char));
+ snprintf(combined, l+1, "(&%s%s)", cust->filt, filt_format);
+ strncpy(filt_format, combined, sizeof(filt_format));
+ filt_format[sizeof(filt_format)-1] = '\0';
+ fs_give((void **) &combined);
+ }
+
+ /*
+ * Ad hoc attempt to make "Steve Hubert" match
+ * Steven Hubert but not Steven Shubert.
+ * We replace a <SPACE> with * <SPACE> (not * <SPACE> *).
+ */
+ memset((void *)scp, 0, sizeof(scp));
+ if(info->nosub)
+ strncpy(scp, string, sizeof(scp));
+ else{
+ p = string;
+ q = scp;
+ while(*p && (q - scp) + 1 < sizeof(scp)){
+ if(*p == SPACE && *(p+1) != SPACE){
+ *q++ = '*';
+ *q++ = *p++;
+ }
+ else
+ *q++ = *p++;
+ }
+ }
+
+ scp[sizeof(scp)-1] = '\0';
+
+ slen = strlen(scp);
+ flen = strlen(filt_format);
+ /* truncate string if it will overflow filter */
+ if(args*slen + flen - 2*args > sizeof(filter)-1)
+ scp[(sizeof(filter)-1 - flen)/args] = '\0';
+
+ /*
+ * Replace %s's with scp.
+ */
+ switch(args){
+ case 0:
+ snprintf(filter, sizeof(filter), filt_format);
+ break;
+ case 1:
+ snprintf(filter, sizeof(filter), filt_format, scp);
+ break;
+ case 2:
+ snprintf(filter, sizeof(filter), filt_format, scp, scp);
+ break;
+ case 3:
+ snprintf(filter, sizeof(filter), filt_format, scp, scp, scp);
+ break;
+ case 4:
+ snprintf(filter, sizeof(filter), filt_format, scp, scp, scp, scp);
+ break;
+ case 5:
+ snprintf(filter, sizeof(filter), filt_format, scp, scp, scp, scp, scp);
+ break;
+ case 6:
+ snprintf(filter, sizeof(filter), filt_format, scp, scp, scp, scp, scp, scp);
+ break;
+ case 7:
+ snprintf(filter, sizeof(filter), filt_format, scp, scp, scp, scp, scp, scp, scp);
+ break;
+ case 8:
+ snprintf(filter, sizeof(filter), filt_format, scp, scp, scp, scp, scp, scp, scp,
+ scp);
+ break;
+ case 9:
+ snprintf(filter, sizeof(filter), filt_format, scp, scp, scp, scp, scp, scp, scp,
+ scp, scp);
+ break;
+ case 10:
+ default:
+ snprintf(filter, sizeof(filter), filt_format, scp, scp, scp, scp, scp, scp, scp,
+ scp, scp, scp);
+ break;
+ }
+
+ /* replace double *'s with single *'s in filter */
+ for(p = q = filter; *p; p++)
+ if(*p != '*' || p == filter || *(p-1) != '*')
+ *q++ = *p;
+
+ *q = '\0';
+
+ (void) removing_double_quotes(base);
+ dprint((5, "about to ldap_search(\"%s\", %s)\n",
+ base ? base : "?", filter ? filter : "?"));
+ if(ps_global->intr_pending)
+ srch_res = LDAP_PROTOCOL_ERROR;
+ else{
+ int msgid;
+ time_t start_time;
+
+ start_time = time((time_t *)0);
+
+ dprint((6, "ldap_lookup: calling ldap_search\n"));
+ msgid = ldap_search(ld, base, info->scope, filter, NULL, 0);
+
+ if(msgid == -1)
+ srch_res = our_ldap_get_lderrno(ld, NULL, NULL);
+ else{
+ int lres;
+ /*
+ * Warning: struct timeval is not portable. However, since it is
+ * part of LDAP api it must be portable to all platforms LDAP
+ * has been ported to.
+ */
+ struct timeval t;
+
+ t.tv_sec = 1; t.tv_usec = 0;
+
+ do {
+ if(ps_global->intr_pending)
+ intr_happened = 1;
+
+ dprint((6, "ldap_result(id=%d): ", msgid));
+ if((lres=ldap_result(ld, msgid, LDAP_MSG_ALL, &t, &res)) == -1){
+ /* error */
+ srch_res = our_ldap_get_lderrno(ld, NULL, NULL);
+ dprint((6, "error (-1 returned): ld_errno=%d\n",
+ srch_res));
+ }
+ else if(lres == 0){ /* timeout, no results available */
+ if(intr_happened){
+ ldap_abandon(ld, msgid);
+ srch_res = LDAP_PROTOCOL_ERROR;
+ if(our_ldap_get_lderrno(ld, NULL, NULL) == LDAP_SUCCESS)
+ our_ldap_set_lderrno(ld, LDAP_PROTOCOL_ERROR, NULL, NULL);
+
+ dprint((6, "timeout, intr: srch_res=%d\n",
+ srch_res));
+ }
+ else if(info->time > 0 &&
+ ((long)time((time_t *)0) - start_time) > info->time){
+ /* try for partial results */
+ t.tv_sec = 0; t.tv_usec = 0;
+ lres = ldap_result(ld, msgid, LDAP_MSG_RECEIVED, &t, &res);
+ if(lres > 0 && lres != LDAP_RES_SEARCH_RESULT){
+ srch_res = LDAP_SUCCESS;
+ dprint((6, "partial result: lres=0x%x\n", lres));
+ }
+ else{
+ if(lres == 0)
+ ldap_abandon(ld, msgid);
+
+ srch_res = LDAP_TIMEOUT;
+ if(our_ldap_get_lderrno(ld, NULL, NULL) == LDAP_SUCCESS)
+ our_ldap_set_lderrno(ld, LDAP_TIMEOUT, NULL, NULL);
+
+ dprint((6,
+ "timeout, total_time (%d), srch_res=%d\n",
+ info->time, srch_res));
+ }
+ }
+ else{
+ dprint((6, "timeout\n"));
+ }
+ }
+ else{
+ srch_res = ldap_result2error(ld, res, 0);
+ dprint((6, "lres=0x%x, srch_res=%d\n", lres,
+ srch_res));
+ }
+ }while(lres == 0 &&
+ !(intr_happened ||
+ (info->time > 0 &&
+ ((long)time((time_t *)0) - start_time) > info->time)));
+ }
+ }
+
+ if(intr_happened){
+ wp_exit = 1;
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ if(wp_err->error)
+ fs_give((void **)&wp_err->error);
+ else{
+ q_status_message(SM_ORDER, 0, 1, "Interrupt");
+ display_message('x');
+ fflush(stdout);
+ }
+
+ if(res)
+ ldap_msgfree(res);
+ if(ld)
+ ldap_unbind(ld);
+
+ res = NULL; ld = NULL;
+ }
+ else if(srch_res != LDAP_SUCCESS &&
+ srch_res != LDAP_TIMELIMIT_EXCEEDED &&
+ srch_res != LDAP_RESULTS_TOO_LARGE &&
+ srch_res != LDAP_TIMEOUT &&
+ srch_res != LDAP_SIZELIMIT_EXCEEDED){
+ wp_err->wp_err_occurred = 1;
+
+ ld_errnum = our_ldap_get_lderrno(ld, NULL, &ld_errstr);
+
+ snprintf(ebuf, sizeof(ebuf), _("LDAP search failed: %s%s%s%s"),
+ ldap_err2string(ld_errnum),
+ serv_errstr,
+ (ld_errstr && *ld_errstr) ? ": " : "",
+ (ld_errstr && *ld_errstr) ? ld_errstr : "");
+
+ if(wp_err->error)
+ fs_give((void **)&wp_err->error);
+
+ wp_err->error = cpystr(ebuf);
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ q_status_message(SM_ORDER, 3, 5, wp_err->error);
+ display_message('x');
+ dprint((2, "%s\n", ebuf));
+ if(res)
+ ldap_msgfree(res);
+ if(ld)
+ ldap_unbind(ld);
+
+ res = NULL; ld = NULL;
+ }
+ else{
+ int cnt;
+
+ cnt = ldap_count_entries(ld, res);
+
+ if(cnt > 0){
+
+ if(srch_res == LDAP_TIMELIMIT_EXCEEDED ||
+ srch_res == LDAP_RESULTS_TOO_LARGE ||
+ srch_res == LDAP_TIMEOUT ||
+ srch_res == LDAP_SIZELIMIT_EXCEEDED){
+ wp_err->wp_err_occurred = 1;
+ ld_errnum = our_ldap_get_lderrno(ld, NULL, &ld_errstr);
+
+ snprintf(ebuf, sizeof(ebuf), _("LDAP partial results: %s%s%s%s"),
+ ldap_err2string(ld_errnum),
+ serv_errstr,
+ (ld_errstr && *ld_errstr) ? ": " : "",
+ (ld_errstr && *ld_errstr) ? ld_errstr : "");
+ dprint((2, "%s\n", ebuf));
+ if(wp_err->error)
+ fs_give((void **)&wp_err->error);
+
+ wp_err->error = cpystr(ebuf);
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ q_status_message(SM_ORDER, 3, 5, wp_err->error);
+ display_message('x');
+ }
+
+ dprint((5, "Matched %d entries on %s\n",
+ cnt, serv ? serv : "?"));
+
+ serv_res = (LDAP_SERV_RES_S *)fs_get(sizeof(LDAP_SERV_RES_S));
+ memset((void *)serv_res, 0, sizeof(*serv_res));
+ serv_res->ld = ld;
+ serv_res->res = res;
+ serv_res->info_used = copy_ldap_serv_info(info);
+ /* Save by reference? */
+ if(info->ref){
+ snprintf(buf, sizeof(buf), "%s:%s", serv, comatose(info->port));
+ serv_res->serv = cpystr(buf);
+ }
+ else
+ serv_res->serv = NULL;
+
+ serv_res->next = NULL;
+ }
+ else{
+ if(srch_res == LDAP_TIMELIMIT_EXCEEDED ||
+ srch_res == LDAP_RESULTS_TOO_LARGE ||
+ srch_res == LDAP_TIMEOUT ||
+ srch_res == LDAP_SIZELIMIT_EXCEEDED){
+ wp_err->wp_err_occurred = 1;
+ wp_err->ldap_errno = srch_res;
+
+ ld_errnum = our_ldap_get_lderrno(ld, NULL, &ld_errstr);
+
+ snprintf(ebuf, sizeof(ebuf), _("LDAP search failed: %s%s%s%s"),
+ ldap_err2string(ld_errnum),
+ serv_errstr,
+ (ld_errstr && *ld_errstr) ? ": " : "",
+ (ld_errstr && *ld_errstr) ? ld_errstr : "");
+
+ if(wp_err->error)
+ fs_give((void **)&wp_err->error);
+
+ wp_err->error = cpystr(ebuf);
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ q_status_message(SM_ORDER, 3, 5, wp_err->error);
+ display_message('x');
+ dprint((2, "%s\n", ebuf));
+ }
+
+ dprint((5, "Matched 0 entries on %s\n",
+ serv ? serv : "?"));
+ if(res)
+ ldap_msgfree(res);
+ if(ld)
+ ldap_unbind(ld);
+
+ res = NULL; ld = NULL;
+ }
+ }
+ }
+ }
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ if(we_turned_on)
+ intr_handling_off();
+
+ if(serv)
+ fs_give((void **)&serv);
+ if(base)
+ fs_give((void **)&base);
+ if(serv_errstr)
+ fs_give((void **)&serv_errstr);
+
+ return(serv_res);
+}
+
+
+/*
+ * Given a list of entries, present them to user so user may
+ * select one.
+ *
+ * Args head -- The head of the list of results
+ * orig -- The string the user was searching for
+ * result -- Returned pointer to chosen LDAP_SEARCH_WINNER or NULL
+ * wp_err -- Error handling
+ * style --
+ *
+ * Returns 0 ok
+ * -1 Exit chosen by user
+ * -2 None of matched entries had an email address
+ * -3 No matched entries
+ * -4 Goback to Abook List chosen by user
+ * -5 caller shouldn't free head
+ */
+int
+ask_user_which_entry(LDAP_SERV_RES_S *head, char *orig, LDAP_CHOOSE_S **result,
+ WP_ERR_S *wp_err, LDAPLookupStyle style)
+{
+ ADDR_CHOOSE_S ac;
+ char t[200];
+ int retval;
+
+ dprint((3, "ask_user_which(style=%s)\n",
+ style == AlwaysDisplayAndMailRequired ? "AlwaysDisplayAndMailRequired" :
+ style == AlwaysDisplay ? "AlwaysDisplay" :
+ style == DisplayIfTwo ? "DisplayIfTwo" :
+ style == DisplayForURL ? "DisplayForURL" :
+ style == DisplayIfOne ? "DisplayIfOne" : "?"));
+
+ /*
+ * Set up a screen for user to choose one entry.
+ */
+
+ if(style == AlwaysDisplay || style == DisplayForURL)
+ snprintf(t, sizeof(t), "SEARCH RESULTS INDEX");
+ else{
+ int len;
+
+ len = strlen(orig);
+ snprintf(t, sizeof(t), _("SELECT ONE ADDRESS%s%s%s"),
+ (orig && *orig && len < 40) ? " FOR \"" : "",
+ (orig && *orig && len < 40) ? orig : "",
+ (orig && *orig && len < 40) ? "\"" : "");
+ }
+
+ memset(&ac, 0, sizeof(ADDR_CHOOSE_S));
+ ac.title = cpystr(t);
+ ac.res_head = head;
+
+ retval = ldap_addr_select(ps_global, &ac, result, style, wp_err, orig);
+
+ switch(retval){
+ case 0: /* Ok */
+ break;
+
+ case -1: /* Exit chosen by user */
+ wp_exit = 1;
+ break;
+
+ case -4: /* GoBack to AbookList chosen by user */
+ break;
+
+ case -5:
+ wp_nobail = 1;
+ break;
+
+ case -2:
+ if(style != AlwaysDisplay){
+ if(wp_err->error)
+ fs_give((void **)&wp_err->error);
+
+ wp_err->error =
+ cpystr(_("None of the names matched on directory server has an email address"));
+ q_status_message(SM_ORDER, 3, 5, wp_err->error);
+ display_message('x');
+ }
+
+ break;
+
+ case -3:
+ if(style == AlwaysDisplayAndMailRequired){
+ if(wp_err->error)
+ fs_give((void **)&wp_err->error);
+
+ wp_err->error = cpystr(_("No matches on directory server"));
+ q_status_message(SM_ORDER, 3, 5, wp_err->error);
+ display_message('x');
+ }
+
+ break;
+ }
+
+ fs_give((void **)&ac.title);
+
+ return(retval);
+}
+
+
+ADDRESS *
+address_from_ldap(LDAP_CHOOSE_S *winning_e)
+{
+ ADDRESS *ret_a = NULL;
+
+ if(winning_e){
+ char *a;
+ BerElement *ber;
+
+ ret_a = mail_newaddr();
+ for(a = ldap_first_attribute(winning_e->ld, winning_e->selected_entry, &ber);
+ a != NULL;
+ a = ldap_next_attribute(winning_e->ld, winning_e->selected_entry, ber)){
+ int i;
+ char *p;
+ char **vals;
+
+ dprint((9, "attribute: %s\n", a ? a : "?"));
+ if(!ret_a->personal &&
+ strcmp(a, winning_e->info_used->cnattr) == 0){
+ dprint((9, "Got cnattr:"));
+ vals = ldap_get_values(winning_e->ld, winning_e->selected_entry, a);
+ for(i = 0; vals[i] != NULL; i++)
+ dprint((9, " %s\n",
+ vals[i] ? vals[i] : "?"));
+
+ if(vals && vals[0])
+ ret_a->personal = cpystr(vals[0]);
+
+ ldap_value_free(vals);
+ }
+ else if(!ret_a->mailbox &&
+ strcmp(a, winning_e->info_used->mailattr) == 0){
+ dprint((9, "Got mailattr:"));
+ vals = ldap_get_values(winning_e->ld, winning_e->selected_entry, a);
+ for(i = 0; vals[i] != NULL; i++)
+ dprint((9, " %s\n",
+ vals[i] ? vals[i] : "?"));
+
+ /* use first one */
+ if(vals && vals[0]){
+ if((p = strindex(vals[0], '@')) != NULL){
+ ret_a->host = cpystr(p+1);
+ *p = '\0';
+ }
+
+ ret_a->mailbox = cpystr(vals[0]);
+ }
+
+ ldap_value_free(vals);
+ }
+
+ our_ldap_memfree(a);
+ }
+ }
+
+ return(ret_a);
+}
+
+
+/*
+ * Break up the ldap-server string stored in the pinerc into its
+ * parts. The structure is allocated here and should be freed by the caller.
+ *
+ * The original string looks like
+ * <servername>[:port] <SPACE> "/base=<base>/impl=1/..."
+ *
+ * Args serv_str -- The original string from the pinerc to parse.
+ *
+ * Returns A pointer to a structure with filled in answers.
+ *
+ * Some of the members have defaults. If port is -1, that means to use
+ * the default LDAP_PORT. If base is NULL, use "". Type and srch have
+ * defaults defined in alpine.h. If cust is non-NULL, it overrides type and
+ * srch.
+ */
+LDAP_SERV_S *
+break_up_ldap_server(char *serv_str)
+{
+ char *lserv;
+ char *q, *p, *tail;
+ int i, only_one = 1;
+ LDAP_SERV_S *info = NULL;
+
+ if(!serv_str)
+ return(info);
+
+ info = (LDAP_SERV_S *)fs_get(sizeof(LDAP_SERV_S));
+
+ /*
+ * Initialize to defaults.
+ */
+ memset((void *)info, 0, sizeof(*info));
+ info->port = -1;
+ info->srch = -1;
+ info->type = -1;
+ info->time = -1;
+ info->size = -1;
+ info->scope = -1;
+
+ /* copy the whole string to work on */
+ lserv = cpystr(serv_str);
+ if(lserv)
+ removing_trailing_white_space(lserv);
+
+ if(!lserv || !*lserv || *lserv == '"'){
+ if(lserv)
+ fs_give((void **)&lserv);
+
+ if(info)
+ free_ldap_server_info(&info);
+
+ return(NULL);
+ }
+
+ tail = lserv;
+ while((tail = strindex(tail, SPACE)) != NULL){
+ tail++;
+ if(*tail == '"' || *tail == '/'){
+ *(tail-1) = '\0';
+ break;
+ }
+ else
+ only_one = 0;
+ }
+
+ /* tail is the part after server[:port] <SPACE> */
+ if(tail && *tail){
+ removing_leading_white_space(tail);
+ (void)removing_double_quotes(tail);
+ }
+
+ /* get the optional port number */
+ if(only_one && (q = strindex(lserv, ':')) != NULL){
+ int ldapport = -1;
+
+ *q = '\0';
+ if((ldapport = atoi(q+1)) >= 0)
+ info->port = ldapport;
+ }
+
+ /* use lserv for serv even though it has a few extra bytes alloced */
+ info->serv = lserv;
+
+ if(tail && *tail){
+ /* get the search base */
+ if((q = srchstr(tail, "/base=")) != NULL)
+ info->base = remove_backslash_escapes(q+6);
+
+ if((q = srchstr(tail, "/binddn=")) != NULL)
+ info->binddn = remove_backslash_escapes(q+8);
+
+ /* get the implicit parameter */
+ if((q = srchstr(tail, "/impl=1")) != NULL)
+ info->impl = 1;
+
+ /* get the rhs parameter */
+ if((q = srchstr(tail, "/rhs=1")) != NULL)
+ info->rhs = 1;
+
+ /* get the ref parameter */
+ if((q = srchstr(tail, "/ref=1")) != NULL)
+ info->ref = 1;
+
+ /* get the nosub parameter */
+ if((q = srchstr(tail, "/nosub=1")) != NULL)
+ info->nosub = 1;
+
+ /* get the tls parameter */
+ if((q = srchstr(tail, "/tls=1")) != NULL)
+ info->tls = 1;
+
+ /* get the tlsmust parameter */
+ if((q = srchstr(tail, "/tlsm=1")) != NULL)
+ info->tlsmust = 1;
+
+ /* get the search type value */
+ if((q = srchstr(tail, "/type=")) != NULL){
+ NAMEVAL_S *v;
+
+ q += 6;
+ if((p = strindex(q, '/')) != NULL)
+ *p = '\0';
+
+ for(i = 0; (v = ldap_search_types(i)); i++)
+ if(!strucmp(q, v->name)){
+ info->type = v->value;
+ break;
+ }
+
+ if(p)
+ *p = '/';
+ }
+
+ /* get the search rule value */
+ if((q = srchstr(tail, "/srch=")) != NULL){
+ NAMEVAL_S *v;
+
+ q += 6;
+ if((p = strindex(q, '/')) != NULL)
+ *p = '\0';
+
+ for(i = 0; (v = ldap_search_rules(i)); i++)
+ if(!strucmp(q, v->name)){
+ info->srch = v->value;
+ break;
+ }
+
+ if(p)
+ *p = '/';
+ }
+
+ /* get the scope */
+ if((q = srchstr(tail, "/scope=")) != NULL){
+ NAMEVAL_S *v;
+
+ q += 7;
+ if((p = strindex(q, '/')) != NULL)
+ *p = '\0';
+
+ for(i = 0; (v = ldap_search_scope(i)); i++)
+ if(!strucmp(q, v->name)){
+ info->scope = v->value;
+ break;
+ }
+
+ if(p)
+ *p = '/';
+ }
+
+ /* get the time limit */
+ if((q = srchstr(tail, "/time=")) != NULL){
+ q += 6;
+ if((p = strindex(q, '/')) != NULL)
+ *p = '\0';
+
+ /* This one's a number */
+ if(*q){
+ char *err;
+
+ err = strtoval(q, &i, 0, 500, 0, tmp_20k_buf, SIZEOF_20KBUF, "ldap timelimit");
+ if(err){
+ dprint((1, "%s\n", err ? err : "?"));
+ }
+ else
+ info->time = i;
+ }
+
+ if(p)
+ *p = '/';
+ }
+
+ /* get the size limit */
+ if((q = srchstr(tail, "/size=")) != NULL){
+ q += 6;
+ if((p = strindex(q, '/')) != NULL)
+ *p = '\0';
+
+ /* This one's a number */
+ if(*q){
+ char *err;
+
+ err = strtoval(q, &i, 0, 500, 0, tmp_20k_buf, SIZEOF_20KBUF, "ldap sizelimit");
+ if(err){
+ dprint((1, "%s\n", err ? err : "?"));
+ }
+ else
+ info->size = i;
+ }
+
+ if(p)
+ *p = '/';
+ }
+
+ /* get the custom search filter */
+ if((q = srchstr(tail, "/cust=")) != NULL)
+ info->cust = remove_backslash_escapes(q+6);
+
+ /* get the nickname */
+ if((q = srchstr(tail, "/nick=")) != NULL)
+ info->nick = remove_backslash_escapes(q+6);
+
+ /* get the mail attribute name */
+ if((q = srchstr(tail, "/matr=")) != NULL)
+ info->mailattr = remove_backslash_escapes(q+6);
+
+ /* get the sn attribute name */
+ if((q = srchstr(tail, "/satr=")) != NULL)
+ info->snattr = remove_backslash_escapes(q+6);
+
+ /* get the gn attribute name */
+ if((q = srchstr(tail, "/gatr=")) != NULL)
+ info->gnattr = remove_backslash_escapes(q+6);
+
+ /* get the cn attribute name */
+ if((q = srchstr(tail, "/catr=")) != NULL)
+ info->cnattr = remove_backslash_escapes(q+6);
+
+ /* get the backup mail address */
+ if((q = srchstr(tail, "/mail=")) != NULL)
+ info->mail = remove_backslash_escapes(q+6);
+ }
+
+ return(info);
+}
+
+
+void
+free_ldap_server_info(LDAP_SERV_S **info)
+{
+ if(info && *info){
+ if((*info)->serv)
+ fs_give((void **)&(*info)->serv);
+
+ if((*info)->base)
+ fs_give((void **)&(*info)->base);
+
+ if((*info)->cust)
+ fs_give((void **)&(*info)->cust);
+
+ if((*info)->binddn)
+ fs_give((void **)&(*info)->binddn);
+
+ if((*info)->nick)
+ fs_give((void **)&(*info)->nick);
+
+ if((*info)->mail)
+ fs_give((void **)&(*info)->mail);
+
+ if((*info)->mailattr)
+ fs_give((void **)&(*info)->mailattr);
+
+ if((*info)->snattr)
+ fs_give((void **)&(*info)->snattr);
+
+ if((*info)->gnattr)
+ fs_give((void **)&(*info)->gnattr);
+
+ if((*info)->cnattr)
+ fs_give((void **)&(*info)->cnattr);
+
+ fs_give((void **)info);
+ *info = NULL;
+ }
+}
+
+
+LDAP_SERV_S *
+copy_ldap_serv_info(LDAP_SERV_S *src)
+{
+ LDAP_SERV_S *info = NULL;
+
+ if(src){
+ info = (LDAP_SERV_S *) fs_get(sizeof(*info));
+
+ /*
+ * Initialize to defaults.
+ */
+ memset((void *)info, 0, sizeof(*info));
+
+ info->serv = src->serv ? cpystr(src->serv) : NULL;
+ info->base = src->base ? cpystr(src->base) : NULL;
+ info->cust = src->cust ? cpystr(src->cust) : NULL;
+ info->binddn = src->binddn ? cpystr(src->binddn) : NULL;
+ info->nick = src->nick ? cpystr(src->nick) : NULL;
+ info->mail = src->mail ? cpystr(src->mail) : NULL;
+ info->mailattr = cpystr((src->mailattr && src->mailattr[0])
+ ? src->mailattr : DEF_LDAP_MAILATTR);
+ info->snattr = cpystr((src->snattr && src->snattr[0])
+ ? src->snattr : DEF_LDAP_SNATTR);
+ info->gnattr = cpystr((src->gnattr && src->gnattr[0])
+ ? src->gnattr : DEF_LDAP_GNATTR);
+ info->cnattr = cpystr((src->cnattr && src->cnattr[0])
+ ? src->cnattr : DEF_LDAP_CNATTR);
+
+ info->port = (src->port < 0) ? LDAP_PORT : src->port;
+ info->time = (src->time < 0) ? DEF_LDAP_TIME : src->time;
+ info->size = (src->size < 0) ? DEF_LDAP_SIZE : src->size;
+ info->type = (src->type < 0) ? DEF_LDAP_TYPE : src->type;
+ info->srch = (src->srch < 0) ? DEF_LDAP_SRCH : src->srch;
+ info->scope = (src->scope < 0) ? DEF_LDAP_SCOPE : src->scope;
+ info->impl = src->impl;
+ info->rhs = src->rhs;
+ info->ref = src->ref;
+ info->nosub = src->nosub;
+ info->tls = src->tls;
+ }
+
+ return(info);
+}
+
+
+void
+free_ldap_result_list(LDAP_SERV_RES_S **r)
+{
+ if(r && *r){
+ free_ldap_result_list(&(*r)->next);
+ if((*r)->res)
+ ldap_msgfree((*r)->res);
+ if((*r)->ld)
+ ldap_unbind((*r)->ld);
+ if((*r)->info_used)
+ free_ldap_server_info(&(*r)->info_used);
+ if((*r)->serv)
+ fs_give((void **) &(*r)->serv);
+
+ fs_give((void **) r);
+ }
+}
+
+
+/*
+ * Mask API differences.
+ */
+void
+our_ldap_memfree(void *a)
+{
+#if (LDAPAPI >= 15)
+ if(a)
+ ldap_memfree(a);
+#endif
+}
+
+
+/*
+ * Mask API differences.
+ */
+void
+our_ldap_dn_memfree(void *a)
+{
+#if defined(_WINDOWS)
+ if(a)
+ ldap_memfree(a);
+#else
+#if (LDAPAPI >= 15)
+ if(a)
+ ldap_memfree(a);
+#else
+ if(a)
+ free(a);
+#endif
+#endif
+}
+
+
+/*
+ * More API masking.
+ */
+int
+our_ldap_get_lderrno(LDAP *ld, char **m, char **s)
+{
+ int ret = 0;
+
+#if (LDAPAPI >= 2000)
+ if(ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, (void *)&ret) == 0){
+ if(s)
+ ldap_get_option(ld, LDAP_OPT_ERROR_STRING, (void *)s);
+ }
+#elif (LDAPAPI >= 15)
+ ret = ldap_get_lderrno(ld, m, s);
+#else
+ ret = ld->ld_errno;
+ if(s)
+ *s = ld->ld_error;
+#endif
+
+ return(ret);
+}
+
+
+/*
+ * More API masking.
+ */
+int
+our_ldap_set_lderrno(LDAP *ld, int e, char *m, char *s)
+{
+ int ret;
+
+#if (LDAPAPI >= 2000)
+ if(ldap_set_option(ld, LDAP_OPT_ERROR_NUMBER, (void *)&e) == 0)
+ ret = LDAP_SUCCESS;
+ else
+ (void)ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, (void *)&ret);
+#elif (LDAPAPI >= 15)
+ ret = ldap_set_lderrno(ld, e, m, s);
+#else
+ /* this is all we care about */
+ ld->ld_errno = e;
+ ret = LDAP_SUCCESS;
+#endif
+
+ return(ret);
+}
+
+
+/*
+ * More API masking.
+ */
+int
+our_ldap_set_option(LDAP *ld, int option, void *optdata)
+{
+ int ret;
+
+#if (LDAPAPI >= 15)
+ ret = ldap_set_option(ld, option, optdata);
+#else
+ switch(option){
+ case LDAP_OPT_TIMELIMIT:
+ ld->ld_timelimit = *(int *)optdata;
+ break;
+
+ case LDAP_OPT_SIZELIMIT:
+ ld->ld_sizelimit = *(int *)optdata;
+ break;
+
+ case LDAP_OPT_RESTART:
+ if((int)optdata)
+ ld->ld_options |= LDAP_OPT_RESTART;
+ else
+ ld->ld_options &= ~LDAP_OPT_RESTART;
+
+ break;
+
+ /*
+ * Does nothing here. There is only one protocol version supported.
+ */
+ case LDAP_OPT_PROTOCOL_VERSION:
+ ret = -1;
+ break;
+
+ default:
+ panic("LDAP function not implemented");
+ }
+#endif
+
+ return(ret);
+}
+
+
+/*
+ * Returns 1 if we can use LDAP version 3 protocol.
+ */
+int
+ldap_v3_is_supported(LDAP *ld)
+{
+ return(1);
+}
+
+struct tl_table {
+ char *ldap_ese;
+ char *translated;
+};
+
+static struct tl_table ldap_trans_table[]={
+ /*
+ * TRANSLATORS: This is a list of LDAP attributes with translations to present
+ * to the user. For example the attribute mail is Email Address and the attribute
+ * cn is Name.
+ */
+ {"mail", N_("Email Address")},
+#define LDAP_MAIL_ATTR 0
+ {"sn", N_("Surname")},
+#define LDAP_SN_ATTR 1
+ {"givenName", N_("Given Name")},
+#define LDAP_GN_ATTR 2
+ {"cn", N_("Name")},
+#define LDAP_CN_ATTR 3
+ {"electronicmail", N_("Email Address")},
+#define LDAP_EMAIL_ATTR 4
+ {"o", N_("Organization")},
+ {"ou", N_("Unit")},
+ {"c", N_("Country")},
+ {"st", N_("State or Province")},
+ {"l", N_("Locality")},
+ {"objectClass", N_("Object Class")},
+ {"title", N_("Title")},
+ {"departmentNumber", N_("Department")},
+ {"postalAddress", N_("Postal Address")},
+ {"homePostalAddress", N_("Home Address")},
+ {"mailStop", N_("Mail Stop")},
+ {"telephoneNumber", N_("Voice Telephone")},
+ {"homePhone", N_("Home Telephone")},
+ {"officePhone", N_("Office Telephone")},
+ {"facsimileTelephoneNumber", N_("FAX Telephone")},
+ {"mobile", N_("Mobile Telephone")},
+ {"pager", N_("Pager")},
+ {"roomNumber", N_("Room Number")},
+ {"uid", N_("User ID")},
+ {NULL, NULL}
+};
+
+char *
+ldap_translate(char *a, LDAP_SERV_S *info_used)
+{
+ int i;
+
+ if(info_used){
+ if(info_used->mailattr && strucmp(info_used->mailattr, a) == 0)
+ return(_(ldap_trans_table[LDAP_MAIL_ATTR].translated));
+ else if(info_used->snattr && strucmp(info_used->snattr, a) == 0)
+ return(_(ldap_trans_table[LDAP_SN_ATTR].translated));
+ else if(info_used->gnattr && strucmp(info_used->gnattr, a) == 0)
+ return(_(ldap_trans_table[LDAP_GN_ATTR].translated));
+ else if(info_used->cnattr && strucmp(info_used->cnattr, a) == 0)
+ return(_(ldap_trans_table[LDAP_CN_ATTR].translated));
+ }
+
+ for(i = 0; ldap_trans_table[i].ldap_ese; i++){
+ if(info_used)
+ switch(i){
+ case LDAP_MAIL_ATTR:
+ case LDAP_SN_ATTR:
+ case LDAP_GN_ATTR:
+ case LDAP_CN_ATTR:
+ case LDAP_EMAIL_ATTR:
+ continue;
+ }
+
+ if(strucmp(ldap_trans_table[i].ldap_ese, a) == 0)
+ return(_(ldap_trans_table[i].translated));
+ }
+
+ return(a);
+}
+
+
+#endif /* ENABLE_LDAP */
diff --git a/pith/ldap.h b/pith/ldap.h
new file mode 100644
index 00000000..2168e5f7
--- /dev/null
+++ b/pith/ldap.h
@@ -0,0 +1,186 @@
+/*
+ * $Id: ldap.h 1012 2008-03-26 00:44:22Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_LDAP_INCLUDED
+#define PITH_LDAP_INCLUDED
+
+
+#include "../pith/state.h"
+#include "../pith/adrbklib.h"
+
+
+#ifdef ENABLE_LDAP
+
+/*
+ * This is used to consolidate related information about a server. This
+ * information is all stored in the ldap-servers variable, per server.
+ */
+typedef struct ldap_serv {
+ char *serv, /* Server name */
+ *base, /* Search base */
+ *binddn, /* Bind DN if non-anonymous */
+ *cust, /* Custom search filter */
+ *nick, /* Nickname */
+ *mail, /* Backup email address */
+ *mailattr, /* "Mail" attribute name */
+ *snattr, /* "Surname" attribute name */
+ *gnattr, /* "Givenname" attribute name */
+ *cnattr; /* "CommonName" attribute name */
+ int port, /* Port number */
+ time, /* Time limit */
+ size, /* Size limit */
+ impl, /* Use implicitly feature */
+ rhs, /* Lookup contents feature */
+ ref, /* Save by reference feature */
+ nosub, /* Disable space sub feature */
+ tls, /* Attempt TLS */
+ tlsmust, /* Require TLS */
+ type, /* Search type (surname...) */
+ srch, /* Search rule (contains...) */
+ scope; /* Scope of search (base...) */
+} LDAP_SERV_S;
+
+
+/*
+ * Structures to control the LDAP address selection screen
+ *
+ * We may run into the problem of LDAP databases containing non-UTF-8 data
+ * because they are old. They should have all UTF-8 data and that is what
+ * we are assuming. If we wanted to accomodate these servers we could
+ * translate the data when we use it. LDAP data is only used in a few
+ * places so it might not be too hard to fix it. There are four calls
+ * into the LDAP library that produce character strings which are
+ * supposed to be UTF-8. They are
+ * ldap_get_dn
+ * ldap_first_attribute
+ * ldap_next_attribute
+ * ldap_get_values
+ * We call those from a half dozen functions. We could fix it by
+ * having a directory-character-set per server and passing that around
+ * in the LDAP_SERV_RES_S structure, I think. For now, let's go with
+ * the assumption that everything is already UTF-8.
+ */
+typedef struct ldap_serv_results {
+ LDAP *ld; /* LDAP handle */
+ LDAPMessage *res; /* LDAP search result */
+ LDAP_SERV_S *info_used;
+ char *serv;
+ struct ldap_serv_results *next;
+} LDAP_SERV_RES_S;
+
+
+typedef struct addr_choose {
+ LDAP_SERV_RES_S *res_head;
+ char *title;
+ LDAP *selected_ld; /* from which ld was entry selected */
+ LDAPMessage *selected_entry; /* which entry was selected */
+ LDAP_SERV_S *info_used;
+ char *selected_serv;
+} ADDR_CHOOSE_S;
+
+
+/*
+ * This is very similar to LDAP_SERV_RES_S, but selected_entry
+ * is a single entry instead of a result list.
+ */
+typedef struct ldap_choose_results {
+ LDAP *ld; /* LDAP handle */
+ LDAPMessage *selected_entry;
+ LDAP_SERV_S *info_used;
+ char *serv;
+} LDAP_CHOOSE_S;
+
+
+/*
+ * How the LDAP lookup should work.
+ */
+typedef enum {AlwaysDisplay,
+ AlwaysDisplayAndMailRequired,
+ DisplayIfTwo,
+ DisplayIfOne,
+ DisplayForURL
+ } LDAPLookupStyle;
+
+
+#define LDAP_TYPE_CN 0
+#define LDAP_TYPE_SUR 1
+#define LDAP_TYPE_GIVEN 2
+#define LDAP_TYPE_EMAIL 3
+#define LDAP_TYPE_CN_EMAIL 4
+#define LDAP_TYPE_SUR_GIVEN 5
+#define LDAP_TYPE_SEVERAL 6
+
+#define LDAP_SRCH_CONTAINS 0
+#define LDAP_SRCH_EQUALS 1
+#define LDAP_SRCH_BEGINS 2
+#define LDAP_SRCH_ENDS 3
+
+#define DEF_LDAP_TYPE 6
+#define DEF_LDAP_SRCH 2
+#define DEF_LDAP_TIME 30
+#define DEF_LDAP_SIZE 0
+#define DEF_LDAP_SCOPE LDAP_SCOPE_SUBTREE
+#define DEF_LDAP_MAILATTR "mail"
+#define DEF_LDAP_SNATTR "sn"
+#define DEF_LDAP_GNATTR "givenname"
+#define DEF_LDAP_CNATTR "cn"
+
+#endif /* ENABLE_LDAP */
+
+
+/*
+ * Error handling argument for white pages lookups.
+ */
+typedef struct wp_err {
+ char *error;
+ int wp_err_occurred;
+ int *mangled;
+ int ldap_errno;
+} WP_ERR_S;
+
+
+extern int wp_exit;
+extern int wp_nobail;
+
+
+/* exported protoypes */
+ADDRESS *wp_lookups(char *, WP_ERR_S *, int);
+#ifdef ENABLE_LDAP
+int ldap_lookup_all(char *, int, int, LDAPLookupStyle, CUSTOM_FILT_S *,
+ LDAP_CHOOSE_S **, WP_ERR_S *, LDAP_SERV_RES_S **);
+char *ldap_translate(char *, LDAP_SERV_S *);
+ADDRESS *address_from_ldap(LDAP_CHOOSE_S *);
+LDAP_SERV_S *break_up_ldap_server(char *);
+void free_ldap_server_info(LDAP_SERV_S **);
+void free_ldap_result_list(LDAP_SERV_RES_S **);
+void our_ldap_memfree(void *);
+void our_ldap_dn_memfree(void *);
+int our_ldap_set_option(LDAP *, int, void *);
+int ldap_v3_is_supported(LDAP *);
+int ask_user_which_entry(LDAP_SERV_RES_S *, char *,
+ LDAP_CHOOSE_S **, WP_ERR_S *, LDAPLookupStyle);
+LDAP_SERV_RES_S *ldap_lookup_all_work(char *, int, int, CUSTOM_FILT_S *, WP_ERR_S *);
+
+
+/*
+ * This must be defined in the application
+ */
+int ldap_addr_select(struct pine *, ADDR_CHOOSE_S *, LDAP_CHOOSE_S **,
+ LDAPLookupStyle, WP_ERR_S *, char *);
+#endif /* ENABLE_LDAP */
+
+
+#endif /* PITH_LDAP_INCLUDED */
diff --git a/pith/list.c b/pith/list.c
new file mode 100644
index 00000000..a862260a
--- /dev/null
+++ b/pith/list.c
@@ -0,0 +1,188 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: list.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ list.c
+ ====*/
+
+
+#include "../pith/headers.h"
+#include "../pith/list.h"
+#include "../pith/string.h"
+
+
+/*
+ * Internal prototypes
+ */
+
+
+/*
+ * parse_list - takes a comma delimited list of "count" elements and
+ * returns an array of pointers to each element neatly
+ * malloc'd in its own array. Any errors are returned
+ * in the string pointed to by "error"
+ *
+ * If remove_surrounding_double_quotes is set, then double quotes around
+ * each element of the list are removed. We can't do this for all list
+ * variables. For example, incoming folders look like
+ * nickname foldername
+ * in the config file. Each of those may be quoted separately.
+ *
+ * NOTE: only recognizes escaped quotes
+ */
+char **
+parse_list(char *list, int count, int flags, char **error)
+{
+ char **lvalue, *p2, *p3, *p4;
+ int was_quoted = 0;
+ int remove_surrounding_double_quotes;
+ int commas_may_be_escaped;
+
+ remove_surrounding_double_quotes = (flags & PL_REMSURRQUOT);
+ commas_may_be_escaped = (flags & PL_COMMAQUOTE);
+
+ lvalue = (char **) fs_get((count+1) * sizeof(char *));
+ count = 0;
+ while(*list){ /* pick elements from list */
+ p2 = list; /* find end of element */
+ while(1){
+ if(*p2 == '"') /* ignore ',' if quoted */
+ was_quoted = (was_quoted) ? 0 : 1 ;
+
+ if(*p2 == '\\' && *(p2+1) == '"')
+ p2++; /* preserve escaped quotes, too */
+
+ if((*p2 == ',' && !was_quoted) || *p2 == '\0')
+ break;
+
+ if(commas_may_be_escaped && *p2 == '\\' && *(p2+1) == ',')
+ p2++;
+
+ p2++;
+ }
+
+ if(was_quoted){ /* unbalanced quotes! */
+ if(error)
+ *error = "Unbalanced quotes";
+
+ break;
+ }
+
+ /*
+ * if element found, eliminate trailing
+ * white space and tie into variable list
+ */
+ if(p2 != list){
+ for(p3 = p2 - 1; isspace((unsigned char) *p3) && list < p3; p3--)
+ ;
+
+ p4 = fs_get(((p3 - list) + 2) * sizeof(char));
+ lvalue[count] = p4;
+ while(list <= p3)
+ *p4++ = *list++;
+
+ *p4 = '\0';
+
+ if(remove_surrounding_double_quotes)
+ removing_double_quotes(lvalue[count]);
+
+ count++;
+ }
+
+ if(*(list = p2) != '\0'){ /* move to beginning of next val */
+ while(*list == ',' || isspace((unsigned char)*list))
+ list++;
+ }
+ }
+
+ lvalue[count] = NULL; /* tie off pointer list */
+ return(lvalue);
+}
+
+
+/*
+ * Free array of string pointers and associated strings
+ *
+ * Args: list -- array of char *'s
+ */
+void
+free_list_array(char ***list)
+{
+ char **p;
+
+ if(list && *list){
+ for(p = *list; *p; p++)
+ fs_give((void **) p);
+
+ fs_give((void **) list);
+ }
+}
+
+
+/*
+ * Copy array of string pointers and associated strings
+ *
+ * Args: list -- array of char *'s
+ *
+ * Returns: Allocated array of string pointers and allocated copies of strings.
+ * Caller should free the list array when done.
+ */
+char **
+copy_list_array(char **list)
+{
+ int i, cnt = 0;
+ char **p, **ret_list = NULL;
+
+ if(list){
+ while(list[cnt++])
+ ;
+
+ p = ret_list = (char **)fs_get((cnt+1) * sizeof(char *));
+ memset((void *) ret_list, 0, (cnt+1) * sizeof(char *));
+
+ for(i=0; list[i]; i++, p++)
+ *p = cpystr(list[i]);
+
+ *p = NULL;
+
+ }
+
+ return(ret_list);
+}
+
+
+int
+equal_list_arrays(char **list1, char **list2)
+{
+ int ret = 0;
+
+ if(list1 && list2){
+ while(*list1){
+ if(!*list2 || strcmp(*list1, *list2) != 0)
+ break;
+
+ list1++;
+ list2++;
+ }
+
+ if(*list1 == NULL && *list2 == NULL)
+ ret++;
+ }
+
+ return(ret);
+}
diff --git a/pith/list.h b/pith/list.h
new file mode 100644
index 00000000..abe32811
--- /dev/null
+++ b/pith/list.h
@@ -0,0 +1,32 @@
+/*
+ * $Id: list.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_LIST_INCLUDED
+#define PITH_LIST_INCLUDED
+
+
+#define PL_NONE 0x00 /* flags modifying parse_list */
+#define PL_REMSURRQUOT 0x01 /* rm surrounding quotes */
+#define PL_COMMAQUOTE 0x02 /* backslash quotes comma */
+
+
+/* exported protoypes */
+char **parse_list(char *, int, int, char **);
+char **copy_list_array(char **);
+void free_list_array(char ***);
+int equal_list_arrays(char **, char **);
+
+
+#endif /* PITH_LIST_INCLUDED */
diff --git a/pith/mailcap.c b/pith/mailcap.c
new file mode 100644
index 00000000..34dce329
--- /dev/null
+++ b/pith/mailcap.c
@@ -0,0 +1,976 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: mailcap.c 1012 2008-03-26 00:44:22Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/mailcap.h"
+#include "../pith/init.h"
+#include "../pith/conf.h"
+#include "../pith/mimetype.h"
+#include "../pith/mimedesc.h"
+#include "../pith/status.h"
+#include "../pith/util.h"
+#include "../pith/readfile.h"
+
+/*
+ * We've decided not to implement the RFC1524 standard minimum path, because
+ * some of us think it is harder to debug a problem when you may be misled
+ * into looking at the wrong mailcap entry. Likewise for MIME.Types files.
+ */
+#if defined(DOS) || defined(OS2)
+#define MC_PATH_SEPARATOR ';'
+#define MC_USER_FILE "MAILCAP"
+#define MC_STDPATH NULL
+#else /* !DOS */
+#define MC_PATH_SEPARATOR ':'
+#define MC_USER_FILE NULL
+#define MC_STDPATH \
+ ".mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap"
+#endif /* !DOS */
+
+#ifdef _WINDOWS
+#define MC_ADD_TMP " %s"
+#else
+#define MC_ADD_TMP " < %s"
+#endif
+
+typedef struct mcap_entry {
+ struct mcap_entry *next;
+ int needsterminal;
+ char *contenttype;
+ char *command;
+ char *testcommand;
+ char *label; /* unused */
+ char *printcommand; /* unused */
+} MailcapEntry;
+
+struct mailcap_data {
+ MailcapEntry *head, **tail;
+ STRINGLIST *raw;
+} MailcapData;
+
+#define MC_TOKEN_MAX 64
+
+
+/*
+ * Internal prototypes
+ */
+void mc_init(void);
+void mc_process_file(char *);
+void mc_parse_file(char *);
+int mc_parse_line(char **, char **);
+int mc_comment(char **);
+int mc_token(char **, char **);
+void mc_build_entry(char **);
+int mc_sane_command(char *);
+MailcapEntry *mc_get_command(int, char *, BODY *, int, int *);
+int mc_ctype_match(int, char *, char *);
+int mc_passes_test(MailcapEntry *, int, char *, BODY *);
+char *mc_bld_test_cmd(char *, int, char *, BODY *);
+char *mc_cmd_bldr(char *, int, char *, BODY *, char *, char **);
+MailcapEntry *mc_new_entry(void);
+void mc_free_entry(MailcapEntry **);
+
+
+char *
+mc_conf_path(char *def_path, char *env_path, char *user_file, int separator, char *stdpath)
+{
+ char *path;
+
+ /* We specify MIMETYPES as a path override */
+ if(def_path)
+ /* there may need to be an override specific to pine */
+ path = cpystr(def_path);
+ else if(env_path)
+ path = cpystr(env_path);
+ else{
+#if defined(DOS) || defined(OS2)
+ char *s;
+
+ /*
+ * This gets interesting. Since we don't have any standard location
+ * for config/data files, look in the same directory as the PINERC
+ * and the same dir as PINE.EXE. This is similar to the UNIX
+ * situation with personal config info coming before
+ * potentially shared config data...
+ */
+ if(s = last_cmpnt(ps_global->pinerc)){
+ strncpy(tmp_20k_buf+1000, ps_global->pinerc, MIN(s - ps_global->pinerc,SIZEOF_20KBUF-1000));
+ tmp_20k_buf[1000+MIN(s - ps_global->pinerc,SIZEOF_20KBUF-1000-1)] = '\0';
+ }
+ else
+ strncpy(tmp_20k_buf+1000, ".\\", SIZEOF_20KBUF-1000);
+
+ /* pinerc directory version of file */
+ build_path(tmp_20k_buf+2000, tmp_20k_buf+1000, user_file, SIZEOF_20KBUF-2000);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+
+ /* pine.exe directory version of file */
+ build_path(tmp_20k_buf+3000, ps_global->pine_dir, user_file, SIZEOF_20KBUF-3000);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+
+ /* combine them */
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%c%s", tmp_20k_buf+2000, separator, tmp_20k_buf+3000);
+
+#else /* !DOS */
+ build_path(tmp_20k_buf, ps_global->home_dir, stdpath, SIZEOF_20KBUF);
+#endif /* !DOS */
+ path = cpystr(tmp_20k_buf);
+ }
+
+ return(path);
+}
+
+
+/*
+ * mc_init - Run down the path gathering all the mailcap entries.
+ * Returns with the Mailcap list built.
+ */
+void
+mc_init(void)
+{
+ char *s,
+ *pathcopy,
+ *path,
+ image_viewer[MAILTMPLEN];
+
+ if(MailcapData.raw) /* already have the file? */
+ return;
+ else
+ MailcapData.tail = &MailcapData.head;
+
+ dprint((5, "- mc_init -\n"));
+
+ pathcopy = mc_conf_path(ps_global->VAR_MAILCAP_PATH, getenv("MAILCAPS"),
+ MC_USER_FILE, MC_PATH_SEPARATOR, MC_STDPATH);
+
+ path = pathcopy; /* overloaded "path" */
+
+ /*
+ * Insert an entry for the image-viewer variable from .pinerc, if present.
+ */
+ if(ps_global->VAR_IMAGE_VIEWER && *ps_global->VAR_IMAGE_VIEWER){
+ MailcapEntry *mc = mc_new_entry();
+
+ snprintf(image_viewer, sizeof(image_viewer), "%s %%s", ps_global->VAR_IMAGE_VIEWER);
+
+ MailcapData.raw = mail_newstringlist();
+ MailcapData.raw->text.data = (unsigned char *) cpystr(image_viewer);
+ mc->command = (char *) MailcapData.raw->text.data;
+ mc->contenttype = "image/*";
+ mc->label = "Alpine Image Viewer";
+ dprint((5, "mailcap: using image-viewer=%s\n",
+ ps_global->VAR_IMAGE_VIEWER
+ ? ps_global->VAR_IMAGE_VIEWER : "?"));
+ }
+
+ dprint((7, "mailcap: path: %s\n", path ? path : "?"));
+ while(path){
+ s = strindex(path, MC_PATH_SEPARATOR);
+ if(s)
+ *s++ = '\0';
+ mc_process_file(path);
+ path = s;
+ }
+
+ if(pathcopy)
+ fs_give((void **)&pathcopy);
+
+#ifdef DEBUG
+ if(debug >= 11){
+ MailcapEntry *mc;
+ int i = 0;
+
+ dprint((11, "Collected mailcap entries\n"));
+ for(mc = MailcapData.head; mc; mc = mc->next){
+
+ dprint((11, "%d: ", i++));
+ if(mc->label)
+ dprint((11, "%s\n", mc->label ? mc->label : "?"));
+ if(mc->contenttype)
+ dprint((11, " %s",
+ mc->contenttype ? mc->contenttype : "?"));
+ if(mc->command)
+ dprint((11, " command: %s\n",
+ mc->command ? mc->command : "?"));
+ if(mc->testcommand)
+ dprint((11, " testcommand: %s",
+ mc->testcommand ? mc->testcommand : "?"));
+ if(mc->printcommand)
+ dprint((11, " printcommand: %s",
+ mc->printcommand ? mc->printcommand : "?"));
+ dprint((11, " needsterminal %d\n", mc->needsterminal));
+ }
+ }
+#endif /* DEBUG */
+}
+
+
+/*
+ * Add all the entries from this file onto the Mailcap list.
+ */
+void
+mc_process_file(char *file)
+{
+ char filebuf[MAXPATH+1], *file_data;
+
+ dprint((5, "mailcap: process_file: %s\n", file ? file : "?"));
+
+ (void)strncpy(filebuf, file, MAXPATH);
+ filebuf[MAXPATH] = '\0';
+ file = fnexpand(filebuf, sizeof(filebuf));
+ dprint((7, "mailcap: processing file: %s\n", file ? file : "?"));
+ switch(is_writable_dir(file)){
+ case 0: case 1: /* is a directory */
+ dprint((1, "mailcap: %s is a directory, should be a file\n",
+ file ? file : "?"));
+ return;
+
+ case 2: /* ok */
+ break;
+
+ case 3: /* doesn't exist */
+ dprint((5, "mailcap: %s doesn't exist\n", file ? file : "?"));
+ return;
+
+ default:
+ panic("Programmer botch in mc_process_file");
+ /*NOTREACHED*/
+ }
+
+ if((file_data = read_file(file, READ_FROM_LOCALE)) != NULL){
+ STRINGLIST *newsl, **sl;
+
+ /* Create a new container */
+ newsl = mail_newstringlist();
+ newsl->text.data = (unsigned char *) file_data;
+
+ /* figure out where in the list it should go */
+ for(sl = &MailcapData.raw; *sl; sl = &((*sl)->next))
+ ;
+
+ *sl = newsl; /* Add it to the list */
+
+ mc_parse_file(file_data); /* the process mailcap data */
+ }
+ else
+ dprint((5, "mailcap: %s can't be read\n", file ? file : "?"));
+}
+
+
+void
+mc_parse_file(char *file)
+{
+ char *tokens[MC_TOKEN_MAX];
+
+ while(mc_parse_line(&file, tokens))
+ mc_build_entry(tokens);
+}
+
+
+int
+mc_parse_line(char **line, char **tokens)
+{
+ char **tokenp = tokens;
+
+ while(mc_comment(line)) /* skip comment lines */
+ ;
+
+ while(mc_token(tokenp, line)) /* collect ';' delim'd tokens */
+ if(++tokenp - tokens >= MC_TOKEN_MAX)
+ fatal("Ran out of tokens parsing mailcap file"); /* outch! */
+
+ *++tokenp = NULL; /* tie off list */
+ return(*tokens != NULL);
+}
+
+
+/*
+ * Retuns 1 if line is a comment, 0 otherwise
+ */
+int
+mc_comment(char **line)
+{
+ if(**line == '\n'){ /* blank line is a comment, too */
+ (*line)++;
+ return(1);
+ }
+
+ if(**line == '#'){
+ while(**line) /* !EOF */
+ if(*++(*line) == '\n'){ /* EOL? */
+ (*line)++;
+ break;
+ }
+
+ return(1);
+ }
+
+ return(0);
+}
+
+
+/*
+ * Retuns 0 if EOL, 1 otherwise
+ */
+int
+mc_token(char **token, char **line)
+{
+ int rv = 0;
+ char *start, *wsp = NULL;
+
+ *token = NULL; /* init the slot for this token */
+
+ /* skip leading white space */
+ while(**line && isspace((unsigned char) **line))
+ (*line)++;
+
+ start = *line;
+
+ /* Then see what's left */
+ while(1)
+ switch(**line){
+ case ';' : /* End-Of-Token */
+ rv = 1; /* let caller know more follows */
+ case '\n' : /* EOL */
+ if(wsp)
+ *wsp = '\0'; /* truncate white space? */
+ else
+ *start = '\0'; /* if we have a token, tie it off */
+
+ (*line)++; /* and get ready to parse next one */
+
+ if(rv == 1){ /* ignore trailing semicolon */
+ while(**line){
+ if(**line == '\n')
+ rv = 0;
+
+ if(isspace((unsigned char) **line))
+ (*line)++;
+ else
+ break;
+ }
+ }
+
+ case '\0' : /* EOF */
+ return(rv);
+
+ case '\\' : /* Quoted char */
+ (*line)++;
+#if defined(DOS) || defined(OS2)
+ /*
+ * RFC 1524 says that backslash is used to quote
+ * the next character, but since backslash is part of pathnames
+ * on DOS we're afraid people will not put double backslashes
+ * in their mailcap files. Therefore, we violate the RFC by
+ * looking ahead to the next character. If it looks like it
+ * is just part of a pathname, then we consider a single
+ * backslash to *not* be a quoting character, but a literal
+ * backslash instead.
+ *
+ * SO:
+ * If next char is any of these, treat the backslash
+ * that preceded it like a regular character.
+ */
+ if(**line && isascii(**line)
+ && (isalnum((unsigned char) **line) || strchr("_+-=~" , **line))){
+ *start++ = '\\';
+ wsp = NULL;
+ break;
+ }
+ else
+#endif /* !DOS */
+
+ if(**line == '\n'){ /* quoted line break */
+ *start = ' ';
+ (*line)++; /* just move on */
+ while(isspace((unsigned char) **line))
+ (*line)++;
+
+ break;
+ }
+ else if(**line == '%') /* quoted '%' becomes "%%" */
+ *--(*line) = '%'; /* overwrite '\' !! */
+
+ /* Fall thru and copy/advance pointers*/
+
+ default :
+ if(!*token)
+ *token = start;
+
+ *start = *(*line)++;
+ wsp = (isspace((unsigned char) *start) && !wsp) ? start : NULL;
+ start++;
+ break;
+ }
+}
+
+
+void
+mc_build_entry(char **tokens)
+{
+ MailcapEntry *mc;
+
+ if(!tokens[0]){
+ dprint((5, "mailcap: missing content type!\n"));
+ return;
+ }
+ else if(!tokens[1] || !mc_sane_command(tokens[1])){
+ dprint((5, "mailcap: missing/bogus command!\n"));
+ return;
+ }
+
+ mc = mc_new_entry();
+ mc->contenttype = *tokens++;
+ mc->command = *tokens++;
+
+ dprint((9, "mailcap: content type: %s\n command: %s\n",
+ mc->contenttype ? mc->contenttype : "?",
+ mc->command ? mc->command : "?"));
+
+ /* grok options */
+ for( ; *tokens; tokens++){
+ char *arg;
+
+ /* legit value? */
+ if(!isalnum((unsigned char) **tokens)){
+ dprint((5, "Unknown parameter = \"%s\"", *tokens));
+ continue;
+ }
+
+ if((arg = strindex(*tokens, '=')) != NULL){
+ *arg = ' ';
+ while(arg > *tokens && isspace((unsigned char) arg[-1]))
+ arg--;
+
+ *arg++ = '\0'; /* tie off parm arg */
+ while(*arg && isspace((unsigned char) *arg))
+ arg++;
+
+ if(!*arg)
+ arg = NULL;
+ }
+
+ if(!strucmp(*tokens, "needsterminal")){
+ mc->needsterminal = 1;
+ dprint((9, "mailcap: set needsterminal\n"));
+ }
+ else if(!strucmp(*tokens, "copiousoutput")){
+ mc->needsterminal = 2;
+ dprint((9, "mailcap: set copiousoutput\n"));
+ }
+ else if(arg && !strucmp(*tokens, "test")){
+ mc->testcommand = arg;
+ dprint((9, "mailcap: testcommand=%s\n",
+ mc->testcommand ? mc->testcommand : "?"));
+ }
+ else if(arg && !strucmp(*tokens, "description")){
+ mc->label = arg;
+ dprint((9, "mailcap: label=%s\n",
+ mc->label ? mc->label : "?"));
+ }
+ else if(arg && !strucmp(*tokens, "print")){
+ mc->printcommand = arg;
+ dprint((9, "mailcap: printcommand=%s\n",
+ mc->printcommand ? mc->printcommand : "?"));
+ }
+ else if(arg && !strucmp(*tokens, "compose")){
+ /* not used */
+ dprint((9, "mailcap: not using compose=%s\n",
+ arg ? arg : "?"));
+ }
+ else if(arg && !strucmp(arg, "composetyped")){
+ /* not used */
+ dprint((9, "mailcap: not using composetyped=%s\n",
+ arg ? arg : "?"));
+ }
+ else if(arg && !strucmp(arg, "textualnewlines")){
+ /* not used */
+ dprint((9,
+ "mailcap: not using texttualnewlines=%s\n",
+ arg ? arg : "?"));
+ }
+ else if(arg && !strucmp(arg, "edit")){
+ /* not used */
+ dprint((9, "mailcap: not using edit=%s\n",
+ arg ? arg : "?"));
+ }
+ else if(arg && !strucmp(arg, "x11-bitmap")){
+ /* not used */
+ dprint((9, "mailcap: not using x11-bitmap=%s\n",
+ arg ? arg : "?"));
+ }
+ else
+ dprint((9, "mailcap: ignoring unknown flag: %s\n",
+ arg ? arg : "?"));
+ }
+}
+
+
+/*
+ * Tests for mailcap defined command's sanity
+ */
+int
+mc_sane_command(char *command)
+{
+ /* First, test that a command string actually exists */
+ if(command && *command){
+#ifdef LATER
+ /*
+ * NOTE: Maybe we'll do this later. The problem is when the
+ * mailcap's been misconfigured. We then end up supressing
+ * valuable output when the user actually tries to launch the
+ * spec'd viewer.
+ */
+
+ /* Second, Make sure we can get at it */
+ if(can_access_in_path(getenv("PATH"), command, EXECUTE_ACCESS) >= 0)
+#endif
+ return(1);
+ }
+
+ return(0); /* failed! */
+}
+
+
+/*
+ * Returns the mailcap entry for type/subtype from the successfull
+ * mailcap entry, or NULL if none. Command string still contains % stuff.
+ */
+MailcapEntry *
+mc_get_command(int type, char *subtype, BODY *body,
+ int check_extension, int *sp_handlingp)
+{
+ MailcapEntry *mc;
+ char tmp_subtype[256], tmp_ext[16], *ext = NULL;
+
+ dprint((5, "- mc_get_command(%s/%s) -\n",
+ body_type_names(type),
+ subtype ? subtype : "?"));
+
+ if(type == TYPETEXT
+ && (!subtype || !strucmp(subtype, "plain"))
+ && F_ON(F_SHOW_TEXTPLAIN_INT, ps_global))
+ return(NULL);
+
+ mc_init();
+
+ if(check_extension){
+ char *fname;
+ MT_MAP_T e2b;
+
+ /*
+ * Special handling for when we're looking at what's likely
+ * binary application data. Look for a file name extension
+ * that we might use to hook a helper app to.
+ *
+ * NOTE: This used to preclude an "app/o-s" mailcap entry
+ * since this took precedence. Now that there are
+ * typically two scans through the check_extension
+ * mechanism, the mailcap entry now takes precedence.
+ */
+ if((fname = get_filename_parameter(NULL, 0, body, &e2b.from.ext)) != NULL
+ && e2b.from.ext && e2b.from.ext[0]){
+ if(strlen(e2b.from.ext) < sizeof(tmp_ext) - 2){
+ strncpy(ext = tmp_ext, e2b.from.ext - 1, sizeof(tmp_ext)); /* remember it */
+ tmp_ext[sizeof(tmp_ext)-1] = '\0';
+ if(mt_srch_mime_type(mt_srch_by_ext, &e2b)){
+ type = e2b.to.mime.type; /* mapped type */
+ strncpy(subtype = tmp_subtype, e2b.to.mime.subtype,
+ sizeof(tmp_subtype)-1);
+ tmp_subtype[sizeof(tmp_subtype)-1] = '\0';
+ fs_give((void **) &e2b.to.mime.subtype);
+ body = NULL; /* the params no longer apply */
+ }
+ }
+
+ fs_give((void **) &fname);
+ }
+ else{
+ if(fname)
+ fs_give((void **) &fname);
+
+ return(NULL);
+ }
+ }
+
+ for(mc = MailcapData.head; mc; mc = mc->next)
+ if(mc_ctype_match(type, subtype, mc->contenttype)
+ && mc_passes_test(mc, type, subtype, body)){
+ dprint((9,
+ "mc_get_command: type=%s/%s, command=%s\n",
+ body_type_names(type),
+ subtype ? subtype : "?",
+ mc->command ? mc->command : "?"));
+ return(mc);
+ }
+
+ if(mime_os_specific_access()){
+ static MailcapEntry fake_mc;
+ static char fake_cmd[1024];
+ char tmp_mime_type[256];
+
+ memset(&fake_mc, 0, sizeof(MailcapEntry));
+ fake_cmd[0] = '\0';
+ fake_mc.command = fake_cmd;
+
+ snprintf(tmp_mime_type, sizeof(tmp_mime_type), "%s/%s", body_types[type], subtype);
+ if(mime_get_os_mimetype_command(tmp_mime_type, ext, fake_cmd,
+ sizeof(fake_cmd), check_extension, sp_handlingp))
+ return(&fake_mc);
+ }
+
+ return(NULL);
+}
+
+
+/*
+ * Check whether the pattern "pat" matches this type/subtype.
+ * Returns 1 if it does, 0 if not.
+ */
+int
+mc_ctype_match(int type, char *subtype, char *pat)
+{
+ char *type_name = body_type_names(type);
+ int len = strlen(type_name);
+
+ dprint((5, "mc_ctype_match: %s == %s / %s ?\n",
+ pat ? pat : "?",
+ type_name ? type_name : "?",
+ subtype ? subtype : "?"));
+
+ return(!struncmp(type_name, pat, len)
+ && ((pat[len] == '/'
+ && (!pat[len+1] || pat[len+1] == '*'
+ || !strucmp(subtype, &pat[len+1])))
+ || !pat[len]));
+}
+
+
+/*
+ * Run the test command for entry mc to see if this entry currently applies to
+ * applies to this type/subtype.
+ *
+ * Returns 1 if it does pass test (exits with status 0), 0 otherwise.
+ */
+int
+mc_passes_test(MailcapEntry *mc, int type, char *subtype, BODY *body)
+{
+ char *cmd = NULL;
+ int rv;
+
+ dprint((5, "- mc_passes_test -\n"));
+
+ if(mc->testcommand
+ && *mc->testcommand
+ && !(cmd = mc_bld_test_cmd(mc->testcommand, type, subtype, body)))
+ return(FALSE); /* couldn't be built */
+
+ if(!mc->testcommand || !cmd || !*cmd){
+ if(cmd)
+ fs_give((void **)&cmd);
+
+ dprint((7, "no test command, so Pass\n"));
+ return 1;
+ }
+
+ rv = exec_mailcap_test_cmd(cmd);
+ dprint((7, "mc_passes_test: \"%s\" %s (rv=%d)\n",
+ cmd ? cmd : "?", rv ? "Failed" : "Passed", rv)) ;
+
+ fs_give((void **)&cmd);
+
+ return(!rv);
+}
+
+
+int
+mailcap_can_display(int type, char *subtype, BODY *body, int check_extension)
+{
+ dprint((5, "- mailcap_can_display -\n"));
+
+ return(mc_get_command(type, subtype, body,
+ check_extension, NULL) != NULL);
+}
+
+
+MCAP_CMD_S *
+mailcap_build_command(int type, char *subtype, BODY *body,
+ char *tmp_file, int *needsterm, int chk_extension)
+{
+ MailcapEntry *mc;
+ char *command, *err = NULL;
+ MCAP_CMD_S *mc_cmd = NULL;
+ int sp_handling = 0;
+
+ dprint((5, "- mailcap_build_command -\n"));
+
+ mc = mc_get_command(type, subtype, body, chk_extension, &sp_handling);
+ if(!mc){
+ q_status_message(SM_ORDER, 3, 4, "Error constructing viewer command");
+ dprint((1,
+ "mailcap_build_command: no command string for %s/%s\n",
+ body_type_names(type), subtype ? subtype : "?"));
+ return((MCAP_CMD_S *)NULL);
+ }
+
+ if(needsterm)
+ *needsterm = mc->needsterminal;
+
+ if(sp_handling)
+ command = cpystr(mc->command);
+ else if(!(command = mc_cmd_bldr(mc->command, type, subtype, body, tmp_file, &err)) && err && *err)
+ q_status_message(SM_ORDER, 5, 5, err);
+
+ dprint((5, "built command: %s\n", command ? command : "?"));
+
+ if(command){
+ mc_cmd = (MCAP_CMD_S *)fs_get(sizeof(MCAP_CMD_S));
+ mc_cmd->command = command;
+ mc_cmd->special_handling = sp_handling;
+ }
+ return(mc_cmd);
+}
+
+
+/*
+ * mc_bld_test_cmd - build the command to test if the given type flies
+ *
+ * mc_cmd_bldr's tmp_file argument is NULL as we're not going to
+ * decode and write each and every MIME segment's data to a temp file
+ * when no test's going to use the data anyway.
+ */
+char *
+mc_bld_test_cmd(char *controlstring, int type, char *subtype, BODY *body)
+{
+ return(mc_cmd_bldr(controlstring, type, subtype, body, NULL, NULL));
+}
+
+
+/*
+ * mc_cmd_bldr - construct a command string to execute
+ *
+ * If tmp_file is null, then the contents of the given MIME segment
+ * is not provided. This is useful for building the "test=" string
+ * as it doesn't operate on the segment's data.
+ *
+ * The return value is an alloc'd copy of the command to be executed.
+ */
+char *
+mc_cmd_bldr(char *controlstring, int type, char *subtype,
+ BODY *body, char *tmp_file, char **err)
+{
+ char *from, *to, *s, *parm;
+ int prefixed = 0, used_tmp_file = 0;
+
+ dprint((8, "- mc_cmd_bldr -\n"));
+
+ for(from = controlstring, to = tmp_20k_buf; *from; ++from){
+ if(prefixed){ /* previous char was % */
+ prefixed = 0;
+ switch(*from){
+ case '%': /* turned \% into this earlier */
+ if(to-tmp_20k_buf < SIZEOF_20KBUF)
+ *to++ = '%';
+
+ break;
+
+ case 's': /* insert tmp_file name in cmd */
+ if(tmp_file){
+ used_tmp_file = 1;
+ sstrncpy(&to, tmp_file, SIZEOF_20KBUF-(to-tmp_20k_buf));
+ }
+ else
+ dprint((1,
+ "mc_cmd_bldr: %%s in cmd but not supplied!\n"));
+
+ break;
+
+ case 't': /* insert MIME type/subtype */
+ /* quote to prevent funny business */
+ if(to-tmp_20k_buf < SIZEOF_20KBUF)
+ *to++ = '\'';
+
+ sstrncpy(&to, body_type_names(type), SIZEOF_20KBUF-(to-tmp_20k_buf));
+
+ if(to-tmp_20k_buf < SIZEOF_20KBUF)
+ *to++ = '/';
+
+ sstrncpy(&to, subtype, SIZEOF_20KBUF-(to-tmp_20k_buf));
+
+ if(to-tmp_20k_buf < SIZEOF_20KBUF)
+ *to++ = '\'';
+
+ break;
+
+ case '{': /* insert requested MIME param */
+ if(F_OFF(F_DO_MAILCAP_PARAM_SUBST, ps_global)){
+ int save;
+
+ dprint((2, "mc_cmd_bldr: param subs %s\n",
+ from ? from : "?"));
+ if(err){
+ if((s = strindex(from, '}')) != NULL){
+ save = *++s;
+ *s = '\0';
+ }
+
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,
+ "Mailcap: see hidden feature %.200s (%%%.200s)",
+ feature_list_name(F_DO_MAILCAP_PARAM_SUBST), from);
+ *err = tmp_20k_buf;
+ if(s)
+ *s = save;
+ }
+
+ return(NULL);
+ }
+
+ s = strindex(from, '}');
+ if(!s){
+ q_status_message1(SM_ORDER, 0, 4,
+ "Ignoring ill-formed parameter reference in mailcap file: %.200s", from);
+ break;
+ }
+
+ *s = '\0';
+ ++from; /* from is the part inside the brackets now */
+
+ parm = parameter_val(body ? body->parameter : NULL, from);
+
+ dprint((9,
+ "mc_cmd_bldr: parameter %s = %s\n",
+ from ? from : "?", parm ? parm : "(not found)"));
+
+ /*
+ * Quote parameter values for /bin/sh.
+ * Put single quotes around the whole thing but every time
+ * there is an actual single quote put it outside of the
+ * single quotes with a backslash in front of it. So the
+ * parameter value fred's car
+ * turns into 'fred'\''s car'
+ */
+ if(to-tmp_20k_buf < SIZEOF_20KBUF)
+ *to++ = '\''; /* opening quote */
+
+ if(parm){
+ char *p;
+
+ /*
+ * Copy value, but quote single quotes for /bin/sh
+ * Backslash quote is ignored inside single quotes so
+ * have to put those outside of the single quotes.
+ * (The parm+1000 nonsense is to protect against
+ * malicious mail trying to overflow our buffer.)
+ *
+ * TCH - Change 2/8/1999
+ * Also quote the ` to prevent execution of arbitrary code
+ */
+ for(p = parm; *p && p < parm+1000; p++){
+ if((*p == '\'') || (*p == '`')){
+ if(to-tmp_20k_buf+4 < SIZEOF_20KBUF){
+ *to++ = '\''; /* closing quote */
+ *to++ = '\\';
+ *to++ = *p; /* quoted character */
+ *to++ = '\''; /* opening quote */
+ }
+ }
+ else if(to-tmp_20k_buf < SIZEOF_20KBUF)
+ *to++ = *p;
+ }
+
+ fs_give((void **) &parm);
+ }
+
+ if(to-tmp_20k_buf < SIZEOF_20KBUF)
+ *to++ = '\''; /* closing quote for /bin/sh */
+
+ *s = '}'; /* restore */
+ from = s;
+ break;
+
+ /*
+ * %n and %F are used by metamail to support otherwise
+ * unrecognized multipart Content-Types. Pine does
+ * not use these since we're only dealing with the individual
+ * parts at this point.
+ */
+ case 'n':
+ case 'F':
+ default:
+ dprint((9,
+ "Ignoring %s format code in mailcap file: %%%c\n",
+ (*from == 'n' || *from == 'F') ? "unimplemented"
+ : "unrecognized",
+ *from));
+ break;
+ }
+ }
+ else if(*from == '%') /* next char is special */
+ prefixed = 1;
+ else if(to-tmp_20k_buf < SIZEOF_20KBUF) /* regular character, just copy */
+ *to++ = *from;
+ }
+
+ if(to-tmp_20k_buf < SIZEOF_20KBUF)
+ *to = '\0';
+
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+
+ /*
+ * file not specified, redirect to stdin
+ */
+ if(!used_tmp_file && tmp_file)
+ snprintf(to, SIZEOF_20KBUF-(to-tmp_20k_buf), MC_ADD_TMP, tmp_file);
+
+ return(cpystr(tmp_20k_buf));
+}
+
+
+/*
+ *
+ */
+MailcapEntry *
+mc_new_entry(void)
+{
+ MailcapEntry *mc = (MailcapEntry *) fs_get(sizeof(MailcapEntry));
+ memset(mc, 0, sizeof(MailcapEntry));
+ *MailcapData.tail = mc;
+ MailcapData.tail = &mc->next;
+ return(mc);
+}
+
+
+/*
+ * Free a list of mailcap entries
+ */
+void
+mc_free_entry(MailcapEntry **mc)
+{
+ if(mc && *mc){
+ mc_free_entry(&(*mc)->next);
+ fs_give((void **) mc);
+ }
+}
+
+
+void
+mailcap_free(void)
+{
+ mail_free_stringlist(&MailcapData.raw);
+ mc_free_entry(&MailcapData.head);
+}
diff --git a/pith/mailcap.h b/pith/mailcap.h
new file mode 100644
index 00000000..25ccd115
--- /dev/null
+++ b/pith/mailcap.h
@@ -0,0 +1,40 @@
+/*
+ * $Id: mailcap.h 1012 2008-03-26 00:44:22Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_MAILCAP_INCLUDED
+#define PITH_MAILCAP_INCLUDED
+
+
+typedef struct mcap_cmd {
+ char *command; /* command to execute */
+ int special_handling; /* special os handling */
+} MCAP_CMD_S;
+
+
+/* exported protoypes */
+char *mc_conf_path(char *, char *, char *, int, char *);
+int mailcap_can_display(int, char *, BODY *, int);
+MCAP_CMD_S *mailcap_build_command(int, char *, BODY *, char *, int *, int);
+void mailcap_free(void);
+
+/* currently mandatory to implement stubs */
+
+/* return exit status of test command */
+int exec_mailcap_test_cmd(char *);
+
+
+
+#endif /* PITH_MAILCAP_INCLUDED */
diff --git a/pith/mailcmd.c b/pith/mailcmd.c
new file mode 100644
index 00000000..78ba98ad
--- /dev/null
+++ b/pith/mailcmd.c
@@ -0,0 +1,2741 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: mailcmd.c 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/mailcmd.h"
+#include "../pith/conf.h"
+#include "../pith/status.h"
+#include "../pith/flag.h"
+#include "../pith/thread.h"
+#include "../pith/util.h"
+#include "../pith/folder.h"
+#include "../pith/sort.h"
+#include "../pith/newmail.h"
+#include "../pith/mailview.h"
+#include "../pith/mailindx.h"
+#include "../pith/save.h"
+#include "../pith/news.h"
+#include "../pith/sequence.h"
+#include "../pith/stream.h"
+#include "../pith/ldap.h"
+#include "../pith/options.h"
+#include "../pith/busy.h"
+#include "../pith/icache.h"
+#include "../pith/ablookup.h"
+#include "../pith/search.h"
+#include "../pith/charconv/utf8.h"
+
+#ifdef _WINDOWS
+#include "../pico/osdep/mswin.h"
+#endif
+
+
+/*
+ * Internal prototypes
+ */
+
+
+/*
+ * optional function hooks
+ */
+int (*pith_opt_read_msg_prompt)(long, char *);
+int (*pith_opt_reopen_folder)(struct pine *, int *);
+int (*pith_opt_expunge_prompt)(MAILSTREAM *, char *, long);
+void (*pith_opt_begin_closing)(int, char *);
+void get_new_message_count(MAILSTREAM *, int, long *, long *);
+char *new_messages_string(MAILSTREAM *);
+void search_for_our_regex_addresses(MAILSTREAM *stream, char type,
+ int not, SEARCHSET *searchset);
+
+
+
+/*----------------------------------------------------------------------
+ Complain about command on empty folder
+
+ Args: map -- msgmap
+ type -- type of message that's missing
+ cmd -- string explaining command attempted
+
+ ----*/
+int
+any_messages(MSGNO_S *map, char *type, char *cmd)
+{
+ if(mn_get_total(map) <= 0L){
+ q_status_message5(SM_ORDER, 0, 2, "No %s%s%s%s%s",
+ type ? type : "",
+ type ? " " : "",
+ THRD_INDX() ? "threads" : "messages",
+ (!cmd || *cmd != '.') ? " " : "",
+ cmd ? cmd : "in folder");
+ return(FALSE);
+ }
+
+ return(TRUE);
+}
+
+
+/*----------------------------------------------------------------------
+ test whether or not we have a valid stream to set flags on
+
+ Args: state -- pine state containing vital signs
+ cmd -- string explaining command attempted
+ permflag -- associated permanent flag state
+
+ Result: returns 1 if we can set flags, otw 0 and complains
+
+ ----*/
+int
+can_set_flag(struct pine *state, char *cmd, int permflag)
+{
+ if((!permflag && READONLY_FOLDER(state->mail_stream))
+ || sp_dead_stream(state->mail_stream)){
+ q_status_message2(SM_ORDER | (sp_dead_stream(state->mail_stream)
+ ? SM_DING : 0),
+ 0, 3,
+ "Can't %s message. Folder is %s.", cmd,
+ (sp_dead_stream(state->mail_stream)) ? "closed" : "read-only");
+ return(FALSE);
+ }
+
+ return(TRUE);
+}
+
+
+/*----------------------------------------------------------------------
+ Complain about command on empty folder
+
+ Args: type -- type of message that's missing
+ cmd -- string explaining command attempted
+
+ ----*/
+void
+cmd_cancelled(char *cmd)
+{
+ /* TRANSLATORS: Arg is replaced with the command name or the word Command */
+ q_status_message1(SM_INFO, 0, 2, _("%s cancelled"), cmd ? cmd : _("Command"));
+}
+
+
+/*----------------------------------------------------------------------
+ Execute DELETE message command
+
+ Args: state -- Various satate info
+ msgmap -- map of c-client to local message numbers
+
+ Result: with side effect of "current" message delete flag set
+
+ ----*/
+int
+cmd_delete(struct pine *state, MSGNO_S *msgmap, int copts,
+ char *(*cmd_action_f)(struct pine *, MSGNO_S *))
+{
+ int lastmsg, rv = 0;
+ long msgno, del_count = 0L, new;
+ char *sequence = NULL, prompt[128];
+
+ dprint((4, "\n - delete message -\n"));
+ if(!(any_messages(msgmap, NULL, "to Delete")
+ && can_set_flag(state, "delete", state->mail_stream->perm_deleted)))
+ return rv;
+
+ rv++;
+
+ if(sp_io_error_on_stream(state->mail_stream)){
+ sp_set_io_error_on_stream(state->mail_stream, 0);
+ pine_mail_check(state->mail_stream); /* forces write */
+ }
+
+ if(MCMD_ISAGG(copts)){
+ sequence = selected_sequence(state->mail_stream, msgmap, &del_count, 0);
+ snprintf(prompt, sizeof(prompt), "%ld%s message%s marked for deletion",
+ del_count, (copts & MCMD_AGG_2) ? "" : " selected", plural(del_count));
+ }
+ else{
+ long rawno;
+
+ msgno = mn_get_cur(msgmap);
+ rawno = mn_m2raw(msgmap, msgno);
+ del_count = 1L; /* return current */
+ sequence = cpystr(long2string(rawno));
+ lastmsg = (msgno >= mn_get_total(msgmap));
+ snprintf(prompt, sizeof(prompt), "%s%s marked for deletion",
+ lastmsg ? "Last message" : "Message ",
+ lastmsg ? "" : long2string(msgno));
+ }
+
+ dprint((3, "DELETE: msg %s\n", sequence ? sequence : "?"));
+ new = sp_new_mail_count(state->mail_stream);
+ mail_flag(state->mail_stream, sequence, "\\DELETED", ST_SET);
+ fs_give((void **) &sequence);
+ if(new != sp_new_mail_count(state->mail_stream))
+ process_filter_patterns(state->mail_stream, state->msgmap,
+ sp_new_mail_count(state->mail_stream));
+
+ if(cmd_action_f){
+ char *rv;
+
+ if((rv = (*cmd_action_f)(state, msgmap)) != NULL)
+ strncat(prompt, rv, sizeof(prompt) - strlen(prompt)- 1);
+ }
+
+ if(!(copts & MCMD_SILENT))
+ q_status_message(SM_ORDER, 0, 3, prompt);
+
+ return rv;
+}
+
+
+/*----------------------------------------------------------------------
+ Execute UNDELETE message command
+
+ Args: state -- Various satate info
+ msgmap -- map of c-client to local message numbers
+
+ Result: with side effect of "current" message delete flag UNset
+
+ ----*/
+int
+cmd_undelete(struct pine *state, MSGNO_S *msgmap, int copts)
+{
+ long del_count;
+ char *sequence;
+ int wasdeleted = FALSE, rv = 0;
+ MESSAGECACHE *mc;
+
+ dprint((4, "\n - undelete -\n"));
+ if(!(any_messages(msgmap, NULL, "to Undelete")
+ && can_set_flag(state, "undelete", state->mail_stream->perm_deleted)))
+ return rv;
+
+ rv++;
+
+ if(MCMD_ISAGG(copts)){
+ del_count = 0L; /* return current */
+ sequence = selected_sequence(state->mail_stream, msgmap, &del_count, 1);
+ wasdeleted = TRUE;
+ }
+ else{
+ long rawno;
+ int exbits = 0;
+
+ del_count = 1L; /* return current */
+ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
+ sequence = cpystr(long2string(rawno));
+ wasdeleted = (state->mail_stream
+ && rawno > 0L && rawno <= state->mail_stream->nmsgs
+ && (mc = mail_elt(state->mail_stream, rawno))
+ && mc->valid
+ && mc->deleted);
+ /*
+ * Mark this message manually flagged so we don't re-filter it
+ * with a filter which only sets flags.
+ */
+ if(msgno_exceptions(state->mail_stream, rawno, "0", &exbits, FALSE))
+ exbits |= MSG_EX_MANUNDEL;
+ else
+ exbits = MSG_EX_MANUNDEL;
+
+ msgno_exceptions(state->mail_stream, rawno, "0", &exbits, TRUE);
+ }
+
+ dprint((3, "UNDELETE: msg %s\n", sequence ? sequence : "?"));
+
+ mail_flag(state->mail_stream, sequence, "\\DELETED", 0L);
+ fs_give((void **) &sequence);
+
+ if((copts & MCMD_SILENT) == 0){
+ if(del_count == 1L && MCMD_ISAGG(copts) == 0){
+ q_status_message(SM_ORDER, 0, 3,
+ wasdeleted
+ ? _("Deletion mark removed, message won't be deleted")
+ : _("Message not marked for deletion; no action taken"));
+ }
+ else
+ q_status_message2(SM_ORDER, 0, 3,
+ _("Deletion mark removed from %s message%s"),
+ comatose(del_count), plural(del_count));
+ }
+
+ if(sp_io_error_on_stream(state->mail_stream)){
+ sp_set_io_error_on_stream(state->mail_stream, 0);
+ pine_mail_check(state->mail_stream); /* forces write */
+ }
+
+ return rv;
+}
+
+
+int
+cmd_expunge_work(MAILSTREAM *stream, MSGNO_S *msgmap)
+{
+ long old_max_msgno;
+ int rv = 0;
+
+ old_max_msgno = mn_get_total(msgmap);
+ delete_filtered_msgs(stream);
+ ps_global->expunge_in_progress = 1;
+ mail_expunge(stream);
+ ps_global->expunge_in_progress = 0;
+
+ dprint((2,"expunge complete cur:%ld max:%ld\n",
+ mn_get_cur(msgmap), mn_get_total(msgmap)));
+ /*
+ * This is only actually necessary if this causes the width of the
+ * message number field to change. That is, it depends on the
+ * format the user is using as well as on the max_msgno. Since it
+ * should be rare, we'll just do it whenever it happens.
+ * Also have to check for an increase in max_msgno on new mail.
+ */
+ if((old_max_msgno >= 1000L && mn_get_total(msgmap) < 1000L)
+ || (old_max_msgno >= 10000L && mn_get_total(msgmap) < 10000L)
+ || (old_max_msgno >= 100000L && mn_get_total(msgmap) < 100000L)){
+ clear_index_cache(stream, 0);
+ rv = 1;
+ }
+
+ /*
+ * mm_exists and mm_expunge take care of updating max_msgno
+ * and selecting a new message should the selected get removed
+ */
+
+
+ reset_check_point(stream);
+
+ return(rv);
+}
+
+
+CONTEXT_S *
+broach_get_folder(CONTEXT_S *context, int *inbox, char **folder)
+{
+ CONTEXT_S *tc;
+
+ if(ps_global->goto_default_rule == GOTO_LAST_FLDR){
+ tc = context ? context : ps_global->context_current;
+ *inbox = 1; /* fill in last_folder below */
+ }
+ else if(ps_global->goto_default_rule == GOTO_FIRST_CLCTN){
+ tc = (ps_global->context_list->use & CNTXT_INCMNG)
+ ? ps_global->context_list->next : ps_global->context_list;
+ ps_global->last_unambig_folder[0] = '\0';
+ *inbox = 1; /* fill in last_folder below */
+ }
+ else if(ps_global->goto_default_rule == GOTO_FIRST_CLCTN_DEF_INBOX){
+ tc = (ps_global->context_list->use & CNTXT_INCMNG)
+ ? ps_global->context_list->next : ps_global->context_list;
+ tc->last_folder[0] = '\0';
+ *inbox = 0;
+ ps_global->last_unambig_folder[0] = '\0';
+ }
+ else{
+ *inbox = (ps_global->cur_folder
+ && ps_global->inbox_name
+ && strucmp(ps_global->cur_folder,ps_global->inbox_name) == 0
+ && (!ps_global->context_current
+ || ps_global->context_current->use & CNTXT_INCMNG
+ || (!(ps_global->context_list->use & CNTXT_INCMNG)
+ && ps_global->context_current == ps_global->context_list)));
+ if(!*inbox)
+ tc = ps_global->context_list; /* inbox's context */
+ else if(ps_global->goto_default_rule == GOTO_INBOX_FIRST_CLCTN){
+ tc = (ps_global->context_list->use & CNTXT_INCMNG)
+ ? ps_global->context_list->next : ps_global->context_list;
+ ps_global->last_unambig_folder[0] = '\0';
+ }
+ else
+ tc = context ? context : ps_global->context_current;
+ }
+
+ if(folder){
+ if(!*inbox){
+ *folder = ps_global->inbox_name;
+ }
+ else
+ *folder = (ps_global->last_unambig_folder[0])
+ ? ps_global->last_unambig_folder
+ : ((tc->last_folder[0]) ? tc->last_folder : NULL);
+ }
+
+ return(tc);
+}
+
+
+/*----------------------------------------------------------------------
+ Actually attempt to open given folder
+
+ Args: newfolder -- The folder name to open
+ streamp -- Candidate stream for recycling. This stream will either
+ be re-used, or it will be closed.
+
+ Result: 1 if the folder was successfully opened
+ 0 if the folder open failed and went back to old folder
+ -1 if open failed and no folder is left open
+
+ Attempt to open the folder name given. If the open of the new folder
+ fails then the previously open folder will remain open, unless
+ something really bad has happened. The designate inbox will always be
+ kept open, and when a request to open it is made the already open
+ stream will be used.
+ ----*/
+int
+do_broach_folder(char *newfolder, CONTEXT_S *new_context, MAILSTREAM **streamp,
+ long unsigned int flags)
+{
+ MAILSTREAM *m, *strm, *stream = streamp ? *streamp : NULL;
+ int open_inbox, rv, old_tros, we_cancel = 0,
+ do_reopen = 0, n, was_dead = 0, cur_already_set = 0;
+ char expanded_file[MAX(MAXPATH,MAILTMPLEN)+1],
+ *old_folder, *old_path, *p, *report;
+ unsigned char *fname;
+ long openmode, rflags = 0L, pc = 0L, cur, raw;
+ ENVELOPE *env = NULL;
+ char status_msg[81];
+ SortOrder old_sort;
+ unsigned perfolder_startup_rule;
+ char tmp1[MAILTMPLEN], tmp2[MAILTMPLEN], *lname, *mname;
+
+ openmode = SP_USERFLDR;
+
+ dprint((1, "About to open folder \"%s\" inbox is: \"%s\"\n",
+ newfolder ? newfolder : "?",
+ ps_global->inbox_name ? ps_global->inbox_name : "?"));
+
+ /*
+ *--- Set flag that we're opening the inbox, a special case.
+ *
+ * We want to know if inbox is being opened either by name OR
+ * fully qualified path...
+ */
+ if(strucmp(newfolder, ps_global->inbox_name) == 0)
+ open_inbox = (flags & DB_INBOXWOCNTXT || new_context == ps_global->context_list);
+ else{
+ open_inbox = (strcmp(newfolder, ps_global->VAR_INBOX_PATH) == 0
+ || same_remote_mailboxes(newfolder, ps_global->VAR_INBOX_PATH)
+ || (!IS_REMOTE(newfolder)
+ && (lname=mailboxfile(tmp1,newfolder))
+ && (mname=mailboxfile(tmp2,ps_global->VAR_INBOX_PATH))
+ && !strcmp(lname,mname)));
+
+ /* further checking for inbox open */
+ if(!open_inbox && new_context && context_isambig(newfolder)){
+ if((p = folder_is_nick(newfolder, FOLDERS(new_context), FN_WHOLE_NAME)) != NULL){
+ /*
+ * Check for an incoming folder other
+ * than INBOX that also point to INBOX.
+ */
+ open_inbox = (strucmp(p, ps_global->inbox_name) == 0
+ || strcmp(p, ps_global->VAR_INBOX_PATH) == 0
+ || same_remote_mailboxes(p, ps_global->VAR_INBOX_PATH)
+ || (!IS_REMOTE(p)
+ && (lname=mailboxfile(tmp1,p))
+ && (mname=mailboxfile(tmp2,ps_global->VAR_INBOX_PATH))
+ && !strcmp(lname,mname)));
+ }
+ else if(!(new_context->use & CNTXT_INCMNG)){
+ char tmp3[MAILTMPLEN];
+
+ /*
+ * Check to see if we are opening INBOX using the folder name
+ * and a context. We won't have recognized this is the
+ * same as INBOX without applying the context first.
+ */
+ context_apply(tmp3, new_context, newfolder, sizeof(tmp3));
+ open_inbox = (strucmp(tmp3, ps_global->inbox_name) == 0
+ || strcmp(tmp3, ps_global->VAR_INBOX_PATH) == 0
+ || same_remote_mailboxes(tmp3, ps_global->VAR_INBOX_PATH)
+ || (!IS_REMOTE(tmp3)
+ && (lname=mailboxfile(tmp1,tmp3))
+ && (mname=mailboxfile(tmp2,ps_global->VAR_INBOX_PATH))
+ && !strcmp(lname,mname)));
+ }
+ }
+ }
+
+ if(open_inbox)
+ new_context = ps_global->context_list; /* restore first context */
+
+ was_dead = sp_a_locked_stream_is_dead();
+
+ /*----- Little to do to if reopening same folder -----*/
+ if(new_context == ps_global->context_current && ps_global->mail_stream
+ && (strcmp(newfolder, ps_global->cur_folder) == 0
+ || (open_inbox && sp_flagged(ps_global->mail_stream, SP_INBOX)))){
+ if(stream){
+ pine_mail_close(stream); /* don't need it */
+ stream = NULL;
+ }
+
+ if(sp_dead_stream(ps_global->mail_stream))
+ do_reopen++;
+
+ /*
+ * If it is a stream which could probably discover newmail by
+ * reopening and user has YES set for those streams, or it
+ * is a stream which may discover newmail by reopening and
+ * user has YES set for those stream, then do_reopen.
+ */
+ if(!do_reopen
+ &&
+ (((ps_global->mail_stream->dtb
+ && ((ps_global->mail_stream->dtb->flags & DR_NONEWMAIL)
+ || (ps_global->mail_stream->rdonly
+ && ps_global->mail_stream->dtb->flags
+ & DR_NONEWMAILRONLY)))
+ && (ps_global->reopen_rule == REOPEN_YES_YES
+ || ps_global->reopen_rule == REOPEN_YES_ASK_Y
+ || ps_global->reopen_rule == REOPEN_YES_ASK_N
+ || ps_global->reopen_rule == REOPEN_YES_NO))
+ ||
+ ((ps_global->mail_stream->dtb
+ && ps_global->mail_stream->rdonly
+ && !(ps_global->mail_stream->dtb->flags & DR_LOCAL))
+ && (ps_global->reopen_rule == REOPEN_YES_YES))))
+ do_reopen++;
+
+ /*
+ * If it is a stream which could probably discover newmail by
+ * reopening and user has ASK set for those streams, or it
+ * is a stream which may discover newmail by reopening and
+ * user has ASK set for those stream, then ask.
+ */
+ if(!do_reopen
+ && pith_opt_reopen_folder
+ && (*pith_opt_reopen_folder)(ps_global, &do_reopen) < 0){
+ cmd_cancelled(NULL);
+ return(0);
+ }
+
+ if(do_reopen){
+ /*
+ * If it's not healthy or if the user explicitly wants to
+ * do a reopen, we reset things and fall thru
+ * to actually reopen it.
+ */
+ if(sp_dead_stream(ps_global->mail_stream)){
+ dprint((2, "Stream was dead, reopening \"%s\"\n",
+ newfolder ? newfolder : "?"));
+ }
+
+ /* clean up */
+ pine_mail_actually_close(ps_global->mail_stream);
+ ps_global->mangled_header = 1;
+ clear_index_cache(ps_global->mail_stream, 0);
+ }
+ else{
+ if(!(flags & DB_NOVISIT))
+ sp_set_recent_since_visited(ps_global->mail_stream, 0L);
+
+ return(1); /* successful open of same folder! */
+ }
+ }
+
+ /*
+ * If ambiguous foldername (not fully qualified), make sure it's
+ * not a nickname for a folder in the given context...
+ */
+
+ /* might get reset below */
+ strncpy(expanded_file, newfolder, sizeof(expanded_file));
+ expanded_file[sizeof(expanded_file)-1] = '\0';
+
+ if(!open_inbox && new_context && context_isambig(newfolder)){
+ if((p = folder_is_nick(newfolder, FOLDERS(new_context), FN_WHOLE_NAME)) != NULL){
+ strncpy(expanded_file, p, sizeof(expanded_file));
+ expanded_file[sizeof(expanded_file)-1] = '\0';
+ dprint((2, "broach_folder: nickname for %s is %s\n",
+ expanded_file ? expanded_file : "?",
+ newfolder ? newfolder : "?"));
+ }
+ else if((new_context->use & CNTXT_INCMNG)
+ && (folder_index(newfolder, new_context, FI_FOLDER) < 0)
+ && !is_absolute_path(newfolder)){
+ fname = folder_name_decoded((unsigned char *)newfolder);
+ q_status_message1(SM_ORDER, 3, 4,
+ _("Can't find Incoming Folder %s."), fname ? (char *) fname : newfolder);
+ if(stream)
+ pine_mail_close(stream);
+
+ if(fname)
+ fs_give((void **)&fname);
+
+ return(0);
+ }
+ }
+
+ /*--- Opening inbox, inbox has been already opened, the easy case ---*/
+ /*
+ * [ It is probably true that we could eliminate most of this special ]
+ * [ inbox stuff and just get the inbox stream back when we do the ]
+ * [ context_open below, but figuring that out hasn't been done. ]
+ */
+ if(open_inbox && (strm=sp_inbox_stream())){
+ if(sp_dead_stream(strm)){
+ /*
+ * if dead INBOX, just close it and let it be reopened.
+ * This is different from the do_reopen case above,
+ * because we're going from another open mail folder to the
+ * dead INBOX.
+ */
+ dprint((2, "INBOX was dead, closing before reopening\n"));
+ pine_mail_actually_close(strm);
+ }
+ else{
+ /*
+ * Clean up the mail_stream we're leaving.
+ */
+ if(ps_global->mail_stream
+ && (!sp_flagged(ps_global->mail_stream, SP_PERMLOCKED)
+ || (sp_flagged(ps_global->mail_stream, SP_INBOX)
+ && F_ON(F_EXPUNGE_INBOX, ps_global))
+ || (!sp_flagged(ps_global->mail_stream, SP_INBOX)
+ && sp_flagged(ps_global->mail_stream, SP_PERMLOCKED)
+ && F_ON(F_EXPUNGE_STAYOPENS, ps_global))))
+ expunge_and_close(ps_global->mail_stream, NULL,
+ sp_flagged(ps_global->mail_stream, SP_PERMLOCKED)
+ ? EC_NO_CLOSE : EC_NONE);
+ else if(!sp_flagged(ps_global->mail_stream, SP_INBOX)
+ && sp_flagged(ps_global->mail_stream, SP_PERMLOCKED)){
+ /*
+ * We want to save our position in the folder so that when we
+ * come back to this folder again, we can place the cursor on
+ * a reasonable message number.
+ */
+ sp_set_saved_cur_msg_id(ps_global->mail_stream, NULL);
+
+ if(ps_global->mail_stream->nmsgs > 0L){
+ cur = mn_get_cur(sp_msgmap(ps_global->mail_stream));
+ raw = mn_m2raw(sp_msgmap(ps_global->mail_stream), cur);
+ if(raw > 0L && raw <= ps_global->mail_stream->nmsgs)
+ env = pine_mail_fetchstructure(ps_global->mail_stream,
+ raw, NULL);
+
+ if(env && env->message_id && env->message_id[0])
+ sp_set_saved_cur_msg_id(ps_global->mail_stream,
+ env->message_id);
+ }
+ }
+
+ /*
+ * Make the already open inbox the current mailbox.
+ */
+ ps_global->mail_stream = strm;
+ ps_global->msgmap = sp_msgmap(strm);
+
+ if(was_dead && pith_opt_icon_text)
+ (*pith_opt_icon_text)(NULL, IT_MCLOSED);
+
+ dprint((7, "%ld %ld %x\n",
+ mn_get_cur(ps_global->msgmap),
+ mn_get_total(ps_global->msgmap),
+ ps_global->mail_stream));
+ /*
+ * remember last context and folder
+ */
+ if(context_isambig(ps_global->cur_folder)){
+ ps_global->context_last = ps_global->context_current;
+ snprintf(ps_global->context_current->last_folder,
+ sizeof(ps_global->context_current->last_folder),
+ "%s", ps_global->cur_folder);
+ ps_global->last_unambig_folder[0] = '\0';
+ }
+ else{
+ ps_global->context_last = NULL;
+ snprintf(ps_global->last_unambig_folder,
+ sizeof(ps_global->last_unambig_folder),
+ "%s", ps_global->cur_folder);
+ }
+
+ p = sp_fldr(ps_global->mail_stream) ? sp_fldr(ps_global->mail_stream)
+ : ps_global->inbox_name;
+ strncpy(ps_global->cur_folder, p, sizeof(ps_global->cur_folder)-1);
+ ps_global->cur_folder[sizeof(ps_global->cur_folder)-1] = '\0';
+ ps_global->context_current = ps_global->context_list;
+ reset_index_format();
+ clear_index_cache(ps_global->mail_stream, 0);
+ /* MUST sort before restoring msgno! */
+ refresh_sort(ps_global->mail_stream, ps_global->msgmap, SRT_NON);
+ report = new_messages_string(ps_global->mail_stream);
+ q_status_message3(SM_ORDER, 0, 3,
+ (mn_get_total(ps_global->msgmap) > 1)
+ ? _("Opened folder \"%s\" with %s messages%s")
+ : _("Opened folder \"%s\" with %s message%s"),
+ ps_global->inbox_name,
+ long2string(mn_get_total(ps_global->msgmap)),
+ report ? report : "");
+ if(report)
+ fs_give((void **)&report);
+
+#ifdef _WINDOWS
+ mswin_settitle(ps_global->inbox_name);
+#endif
+ if(stream)
+ pine_mail_close(stream);
+
+ if(!(flags & DB_NOVISIT))
+ sp_set_recent_since_visited(ps_global->mail_stream, 0L);
+
+ return(1);
+ }
+ }
+
+ if(!new_context && !expand_foldername(expanded_file,sizeof(expanded_file))){
+ if(stream)
+ pine_mail_close(stream);
+
+ return(0);
+ }
+
+ /*
+ * This is a safe time to clean up dead streams because nothing should
+ * be referencing them right now.
+ */
+ sp_cleanup_dead_streams();
+
+ old_folder = NULL;
+ old_path = NULL;
+ old_sort = SortArrival; /* old sort */
+ old_tros = 0; /* old reverse sort ? */
+ /*---- now close the old one we had open if there was one ----*/
+ if(ps_global->mail_stream != NULL){
+ old_folder = cpystr(ps_global->cur_folder);
+ old_path = cpystr(ps_global->mail_stream->original_mailbox
+ ? ps_global->mail_stream->original_mailbox
+ : ps_global->mail_stream->mailbox);
+ old_sort = mn_get_sort(ps_global->msgmap);
+ old_tros = mn_get_revsort(ps_global->msgmap);
+ if(!sp_flagged(ps_global->mail_stream, SP_PERMLOCKED)
+ || (sp_flagged(ps_global->mail_stream, SP_INBOX)
+ && F_ON(F_EXPUNGE_INBOX, ps_global))
+ || (!sp_flagged(ps_global->mail_stream, SP_INBOX)
+ && sp_flagged(ps_global->mail_stream, SP_PERMLOCKED)
+ && F_ON(F_EXPUNGE_STAYOPENS, ps_global)))
+ expunge_and_close(ps_global->mail_stream, NULL,
+ sp_flagged(ps_global->mail_stream, SP_PERMLOCKED)
+ ? EC_NO_CLOSE : EC_NONE);
+ else if(!sp_flagged(ps_global->mail_stream, SP_INBOX)
+ && sp_flagged(ps_global->mail_stream, SP_PERMLOCKED)){
+ /*
+ * We want to save our position in the folder so that when we
+ * come back to this folder again, we can place the cursor on
+ * a reasonable message number.
+ */
+
+ sp_set_saved_cur_msg_id(ps_global->mail_stream, NULL);
+
+ if(ps_global->mail_stream->nmsgs > 0L){
+ cur = mn_get_cur(sp_msgmap(ps_global->mail_stream));
+ raw = mn_m2raw(sp_msgmap(ps_global->mail_stream), cur);
+ if(raw > 0L && raw <= ps_global->mail_stream->nmsgs)
+ env = pine_mail_fetchstructure(ps_global->mail_stream,
+ raw, NULL);
+
+ if(env && env->message_id && env->message_id[0])
+ sp_set_saved_cur_msg_id(ps_global->mail_stream,
+ env->message_id);
+ }
+ }
+
+ ps_global->mail_stream = NULL;
+ }
+
+ snprintf(status_msg, sizeof(status_msg), "%sOpening \"", do_reopen ? "Re-" : "");
+ fname = folder_name_decoded((unsigned char *)newfolder);
+ strncat(status_msg, pretty_fn(fname ? (char*) fname : newfolder),
+ sizeof(status_msg)-strlen(status_msg) - 2);
+ if(fname) fs_give((void **)&fname);
+ status_msg[sizeof(status_msg)-2] = '\0';
+ strncat(status_msg, "\"", sizeof(status_msg)-strlen(status_msg) - 1);
+ status_msg[sizeof(status_msg)-1] = '\0';
+ we_cancel = busy_cue(status_msg, NULL, 0);
+
+ /*
+ * if requested, make access to folder readonly (only once)
+ */
+ if(ps_global->open_readonly_on_startup){
+ openmode |= OP_READONLY;
+ ps_global->open_readonly_on_startup = 0;
+ }
+
+ if(!(flags & DB_NOVISIT))
+ ps_global->first_open_was_attempted = 1;
+
+ openmode |= SP_USEPOOL;
+
+ if(stream)
+ sp_set_first_unseen(stream, 0L);
+
+ /* in case we closed the old stream by cancelling the connection, do
+ * not let that interfere with opening the new stream.
+ */
+ ps_global->user_says_cancel = 0;
+
+ m = context_open((new_context && !open_inbox) ? new_context : NULL,
+ stream,
+ open_inbox ? ps_global->VAR_INBOX_PATH : expanded_file,
+ openmode | (open_inbox ? SP_INBOX : 0),
+ &rflags);
+
+ /*
+ * We aren't in a situation where we want a single cancel to
+ * apply to multiple opens.
+ */
+ ps_global->user_says_cancel = 0;
+
+ if(streamp)
+ *streamp = m;
+
+
+ dprint((8, "Opened folder %p \"%s\" (context: \"%s\")\n",
+ m, (m && m->mailbox) ? m->mailbox : "nil",
+ (new_context && new_context->context)
+ ? new_context->context : "nil"));
+
+
+ /* Can get m != NULL if correct passwd for remote, but wrong name */
+ if(m == NULL || m->halfopen){
+ /*-- non-existent local mailbox, or wrong passwd for remote mailbox--*/
+ /* fall back to currently open mailbox */
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ ps_global->mail_stream = NULL;
+ if(m)
+ pine_mail_actually_close(m);
+
+ rv = 0;
+ dprint((8, "Old folder: \"%s\"\n",
+ old_folder == NULL ? "" : old_folder));
+ if(old_folder != NULL){
+ if(strcmp(old_folder, ps_global->inbox_name) == 0){
+ ps_global->mail_stream = sp_inbox_stream();
+ ps_global->msgmap = sp_msgmap(ps_global->mail_stream);
+
+ dprint((8, "Reactivate inbox %ld %ld %p\n",
+ mn_get_cur(ps_global->msgmap),
+ mn_get_total(ps_global->msgmap),
+ ps_global->mail_stream));
+ p = sp_fldr(ps_global->mail_stream)
+ ? sp_fldr(ps_global->mail_stream)
+ : ps_global->inbox_name;
+ strncpy(ps_global->cur_folder, p,
+ sizeof(ps_global->cur_folder)-1);
+ ps_global->cur_folder[sizeof(ps_global->cur_folder)-1] = '\0';
+ }
+ else{
+ ps_global->mail_stream = pine_mail_open(NULL, old_path,
+ openmode, &rflags);
+ /* mm_log will take care of error message here */
+ if(ps_global->mail_stream == NULL){
+ rv = -1;
+ }
+ else{
+ ps_global->msgmap = sp_msgmap(ps_global->mail_stream);
+ mn_set_sort(ps_global->msgmap, old_sort);
+ mn_set_revsort(ps_global->msgmap, old_tros);
+ ps_global->mangled_header = 1;
+ reset_index_format();
+ clear_index_cache(ps_global->mail_stream, 0);
+
+ if(!(rflags & SP_MATCH)){
+ sp_set_expunge_count(ps_global->mail_stream, 0L);
+ sp_set_new_mail_count(ps_global->mail_stream, 0L);
+ sp_set_dead_stream(ps_global->mail_stream, 0);
+ sp_set_noticed_dead_stream(ps_global->mail_stream, 0);
+
+ reset_check_point(ps_global->mail_stream);
+ if(IS_NEWS(ps_global->mail_stream)
+ && ps_global->mail_stream->rdonly)
+ msgno_exclude_deleted(ps_global->mail_stream,
+ sp_msgmap(ps_global->mail_stream));
+
+ if(mn_get_total(ps_global->msgmap) > 0)
+ mn_set_cur(ps_global->msgmap,
+ first_sorted_flagged(F_NONE,
+ ps_global->mail_stream,
+ 0L,
+ THREADING()
+ ? 0 : FSF_SKIP_CHID));
+
+ if(!(mn_get_sort(ps_global->msgmap) == SortArrival
+ && !mn_get_revsort(ps_global->msgmap)))
+ refresh_sort(ps_global->mail_stream,
+ ps_global->msgmap, SRT_NON);
+ }
+
+ fname = folder_name_decoded((unsigned char *)old_folder);
+ q_status_message1(SM_ORDER, 0, 3,
+ "Folder \"%s\" reopened", fname ? (char *)fname : old_folder);
+ if(fname)
+ fs_give((void **)&fname);
+ }
+ }
+
+ if(rv == 0)
+ mn_set_cur(ps_global->msgmap,
+ MIN(mn_get_cur(ps_global->msgmap),
+ mn_get_total(ps_global->msgmap)));
+
+ fs_give((void **)&old_folder);
+ fs_give((void **)&old_path);
+ }
+ else
+ rv = -1;
+
+ if(rv == -1){
+ q_status_message(SM_ORDER | SM_DING, 0, 4, _("No folder opened"));
+ mn_set_total(ps_global->msgmap, 0L);
+ mn_set_nmsgs(ps_global->msgmap, 0L);
+ mn_set_cur(ps_global->msgmap, -1L);
+ ps_global->cur_folder[0] = '\0';
+ }
+
+ if(was_dead && !sp_a_locked_stream_is_dead() && pith_opt_icon_text)
+ (*pith_opt_icon_text)(NULL, IT_MCLOSED);
+
+ if(ps_global->mail_stream && !(flags & DB_NOVISIT))
+ sp_set_recent_since_visited(ps_global->mail_stream, 0L);
+
+ if(ps_global->mail_stream)
+ sp_set_first_unseen(ps_global->mail_stream, 0L);
+
+ return(rv);
+ }
+ else{
+ if(old_folder != NULL){
+ fs_give((void **)&old_folder);
+ fs_give((void **)&old_path);
+ }
+ }
+
+ update_folder_unseen_by_stream(m, UFU_NONE);
+
+ /*----- success in opening the new folder ----*/
+ dprint((2, "Opened folder \"%s\" with %ld messages\n",
+ m->mailbox ? m->mailbox : "?", m->nmsgs));
+
+
+ /*--- A Little house keeping ---*/
+
+ ps_global->mail_stream = m;
+ if(!(flags & DB_NOVISIT))
+ sp_set_recent_since_visited(ps_global->mail_stream, 0L);
+
+ ps_global->msgmap = sp_msgmap(m);
+ if(!(rflags & SP_MATCH)){
+ sp_set_expunge_count(m, 0L);
+ sp_set_new_mail_count(m, 0L);
+ sp_set_dead_stream(m, 0);
+ sp_set_noticed_dead_stream(m, 0);
+ sp_set_mail_box_changed(m, 0);
+ reset_check_point(m);
+ }
+
+ if(was_dead && !sp_a_locked_stream_is_dead() && pith_opt_icon_text)
+ (*pith_opt_icon_text)(NULL, IT_MCLOSED);
+
+ ps_global->last_unambig_folder[0] = '\0';
+
+ /*
+ * remember old folder and context...
+ */
+ if(context_isambig(ps_global->cur_folder)){
+ ps_global->context_last = ps_global->context_current;
+ snprintf(ps_global->context_current->last_folder,
+ sizeof(ps_global->context_current->last_folder),
+ "%s", ps_global->cur_folder);
+ ps_global->last_unambig_folder[0] = '\0';
+ }
+ else{
+ ps_global->context_last = NULL;
+ snprintf(ps_global->last_unambig_folder,
+ sizeof(ps_global->last_unambig_folder),
+ "%s", ps_global->cur_folder);
+ }
+
+ /* folder in a subdir of context? */
+ if(ps_global->context_current->dir->prev)
+ snprintf(ps_global->cur_folder, sizeof(ps_global->cur_folder), "%s%s",
+ ps_global->context_current->dir->ref, newfolder);
+ else{
+ strncpy(ps_global->cur_folder,
+ (open_inbox) ? ps_global->inbox_name : newfolder,
+ sizeof(ps_global->cur_folder)-1);
+ ps_global->cur_folder[sizeof(ps_global->cur_folder)-1] = '\0';
+ }
+
+ sp_set_fldr(ps_global->mail_stream, ps_global->cur_folder);
+
+ if(new_context){
+ ps_global->context_last = ps_global->context_current;
+ ps_global->context_current = new_context;
+
+ if(!open_inbox)
+ sp_set_context(ps_global->mail_stream, ps_global->context_current);
+ }
+
+ clear_index_cache(ps_global->mail_stream, 0);
+ reset_index_format();
+
+ /*
+ * Start news reading with messages the user's marked deleted
+ * hidden from view...
+ */
+ if(IS_NEWS(ps_global->mail_stream) && ps_global->mail_stream->rdonly)
+ msgno_exclude_deleted(ps_global->mail_stream, ps_global->msgmap);
+
+ if(we_cancel && F_OFF(F_QUELL_FILTER_MSGS, ps_global))
+ cancel_busy_cue(0);
+
+ /*
+ * If the stream we got from the open above was already opened earlier
+ * for some temporary use, then it wouldn't have been filtered. That's
+ * why we need this flag, so that we will filter if needed.
+ */
+ if(!sp_flagged(ps_global->mail_stream, SP_FILTERED))
+ process_filter_patterns(ps_global->mail_stream, ps_global->msgmap, 0L);
+
+ /*
+ * If no filtering messages wait until here to cancel the busy cue
+ * because the user will be waiting for that filtering with nothing
+ * showing the activity otherwise.
+ */
+ if(we_cancel && F_ON(F_QUELL_FILTER_MSGS, ps_global))
+ cancel_busy_cue(0);
+
+ if(!(rflags & SP_MATCH) || !(rflags & SP_LOCKED))
+ reset_sort_order(SRT_VRB);
+ else if(sp_new_mail_count(ps_global->mail_stream) > 0L
+ || sp_unsorted_newmail(ps_global->mail_stream)
+ || sp_need_to_rethread(ps_global->mail_stream))
+ refresh_sort(ps_global->mail_stream, ps_global->msgmap, SRT_NON);
+
+ report = new_messages_string(ps_global->mail_stream);
+ fname = folder_name_decoded((unsigned char *)newfolder);
+ q_status_message7(SM_ORDER, 0, 4,
+ "%s \"%s\" opened with %s message%s%s%s%s",
+ IS_NEWS(ps_global->mail_stream)
+ ? "News group" : "Folder",
+ open_inbox ? pretty_fn(fname ? (char *) fname : newfolder)
+ : (fname ? (char *)fname : newfolder),
+ comatose(mn_get_total(ps_global->msgmap)),
+ plural(mn_get_total(ps_global->msgmap)),
+ (!open_inbox
+ && sp_flagged(ps_global->mail_stream, SP_PERMLOCKED))
+ ? " (StayOpen)" : "",
+ READONLY_FOLDER(ps_global->mail_stream)
+ ? " READONLY" : "",
+ report ? report : "");
+
+ if(fname)
+ fs_give((void **)&fname);
+
+ if(report)
+ fs_give((void **)&report);
+
+#ifdef _WINDOWS
+ mswin_settitle(pretty_fn(newfolder));
+#endif
+ /*
+ * Set current message number when re-opening Stay-Open or
+ * cached folders.
+ */
+ if(rflags & SP_MATCH){
+ if(rflags & SP_LOCKED){
+ if(F_OFF(F_STARTUP_STAYOPEN, ps_global)
+ && (cur = get_msgno_by_msg_id(ps_global->mail_stream,
+ sp_saved_cur_msg_id(ps_global->mail_stream),
+ ps_global->msgmap)) >= 1L
+ && cur <= mn_get_total(ps_global->msgmap)){
+ cur_already_set++;
+ mn_set_cur(ps_global->msgmap, (MsgNo) cur);
+ if(flags & DB_FROMTAB){
+ /*
+ * When we TAB to a folder that is a StayOpen folder we try
+ * to increment the current message # by one instead of doing
+ * some search again. Some people probably won't like this
+ * behavior, especially if the new message that has arrived
+ * comes before where we are in the index. That's why we have
+ * the F_STARTUP_STAYOPEN feature above.
+ */
+ mn_inc_cur(m, ps_global->msgmap, MH_NONE);
+ }
+ /* else leave it where it is */
+
+ adjust_cur_to_visible(ps_global->mail_stream, ps_global->msgmap);
+ }
+ }
+ else{
+ /*
+ * If we're reopening a cached open stream that wasn't explicitly
+ * kept open by the user, then the user expects it to act pretty
+ * much like we are re-opening the stream. A problem is that the
+ * recent messages are still recent because we haven't closed the
+ * stream, so we fake a quasi-recentness by remembering the last
+ * uid assigned on the stream when we pine_mail_close. Then when
+ * we come back messages with uids higher than that are recent.
+ *
+ * If uid_validity has changed, then we don't use any special
+ * treatment, but just do the regular search.
+ */
+ if(m->uid_validity == sp_saved_uid_validity(m)){
+ long i;
+
+ /*
+ * Because first_sorted_flagged uses sequence numbers, find the
+ * sequence number of the first message after the old last
+ * uid assigned. I.e., the first recent message.
+ */
+ for(i = m->nmsgs; i > 0L; i--)
+ if(mail_uid(m, i) <= sp_saved_uid_last(m))
+ break;
+
+ if(i > 0L && i < m->nmsgs)
+ pc = i+1L;
+ }
+ }
+ }
+
+
+ if(!cur_already_set && mn_get_total(ps_global->msgmap) > 0L){
+
+ perfolder_startup_rule = reset_startup_rule(ps_global->mail_stream);
+
+ if(ps_global->start_entry > 0){
+ mn_set_cur(ps_global->msgmap, mn_get_revsort(ps_global->msgmap)
+ ? first_sorted_flagged(F_NONE, m,
+ ps_global->start_entry,
+ THREADING() ? 0 : FSF_SKIP_CHID)
+ : first_sorted_flagged(F_SRCHBACK, m,
+ ps_global->start_entry,
+ THREADING() ? 0 : FSF_SKIP_CHID));
+ ps_global->start_entry = 0;
+ }
+ else if(perfolder_startup_rule != IS_NOTSET ||
+ open_inbox ||
+ ps_global->context_current->use & CNTXT_INCMNG){
+ unsigned use_this_startup_rule;
+
+ if(perfolder_startup_rule != IS_NOTSET)
+ use_this_startup_rule = perfolder_startup_rule;
+ else
+ use_this_startup_rule = ps_global->inc_startup_rule;
+
+ switch(use_this_startup_rule){
+ /*
+ * For news in incoming collection we're doing the same thing
+ * for first-unseen and first-recent. In both those cases you
+ * get first-unseen if FAKE_NEW is off and first-recent if
+ * FAKE_NEW is on. If FAKE_NEW is on, first unseen is the
+ * same as first recent because all recent msgs are unseen
+ * and all unrecent msgs are seen (see pine_mail_open).
+ */
+ case IS_FIRST_UNSEEN:
+first_unseen:
+ mn_set_cur(ps_global->msgmap,
+ (sp_first_unseen(m)
+ && mn_get_sort(ps_global->msgmap) == SortArrival
+ && !mn_get_revsort(ps_global->msgmap)
+ && !get_lflag(ps_global->mail_stream, NULL,
+ sp_first_unseen(m), MN_EXLD)
+ && (n = mn_raw2m(ps_global->msgmap,
+ sp_first_unseen(m))))
+ ? n
+ : first_sorted_flagged(F_UNSEEN | F_UNDEL, m, pc,
+ THREADING() ? 0 : FSF_SKIP_CHID));
+ break;
+
+ case IS_FIRST_RECENT:
+first_recent:
+ /*
+ * We could really use recent for news but this is the way
+ * it has always worked, so we'll leave it. That is, if
+ * the FAKE_NEW feature is on, recent and unseen are
+ * equivalent, so it doesn't matter. If the feature isn't
+ * on, all the undeleted messages are unseen and we start
+ * at the first one. User controls with the FAKE_NEW feature.
+ */
+ if(IS_NEWS(ps_global->mail_stream)){
+ mn_set_cur(ps_global->msgmap,
+ first_sorted_flagged(F_UNSEEN|F_UNDEL, m, pc,
+ THREADING() ? 0 : FSF_SKIP_CHID));
+ }
+ else{
+ mn_set_cur(ps_global->msgmap,
+ first_sorted_flagged(F_RECENT | F_UNSEEN
+ | F_UNDEL,
+ m, pc,
+ THREADING() ? 0 : FSF_SKIP_CHID));
+ }
+ break;
+
+ case IS_FIRST_IMPORTANT:
+ mn_set_cur(ps_global->msgmap,
+ first_sorted_flagged(F_FLAG|F_UNDEL, m, pc,
+ THREADING() ? 0 : FSF_SKIP_CHID));
+ break;
+
+ case IS_FIRST_IMPORTANT_OR_UNSEEN:
+
+ if(IS_NEWS(ps_global->mail_stream))
+ goto first_unseen;
+
+ {
+ MsgNo flagged, first_unseen;
+
+ flagged = first_sorted_flagged(F_FLAG|F_UNDEL, m, pc,
+ THREADING() ? 0 : FSF_SKIP_CHID);
+ first_unseen = (sp_first_unseen(m)
+ && mn_get_sort(ps_global->msgmap) == SortArrival
+ && !mn_get_revsort(ps_global->msgmap)
+ && !get_lflag(ps_global->mail_stream, NULL,
+ sp_first_unseen(m), MN_EXLD)
+ && (n = mn_raw2m(ps_global->msgmap,
+ sp_first_unseen(m))))
+ ? n
+ : first_sorted_flagged(F_UNSEEN|F_UNDEL, m, pc,
+ THREADING() ? 0 : FSF_SKIP_CHID);
+ mn_set_cur(ps_global->msgmap,
+ (MsgNo) MIN((int) flagged, (int) first_unseen));
+
+ }
+
+ break;
+
+ case IS_FIRST_IMPORTANT_OR_RECENT:
+
+ if(IS_NEWS(ps_global->mail_stream))
+ goto first_recent;
+
+ {
+ MsgNo flagged, first_recent;
+
+ flagged = first_sorted_flagged(F_FLAG|F_UNDEL, m, pc,
+ THREADING() ? 0 : FSF_SKIP_CHID);
+ first_recent = first_sorted_flagged(F_RECENT | F_UNSEEN
+ | F_UNDEL,
+ m, pc,
+ THREADING() ? 0 : FSF_SKIP_CHID);
+ mn_set_cur(ps_global->msgmap,
+ (MsgNo) MIN((int) flagged, (int) first_recent));
+ }
+
+ break;
+
+ case IS_FIRST:
+ mn_set_cur(ps_global->msgmap,
+ first_sorted_flagged(F_UNDEL, m, pc,
+ THREADING() ? 0 : FSF_SKIP_CHID));
+ break;
+
+ case IS_LAST:
+ mn_set_cur(ps_global->msgmap,
+ first_sorted_flagged(F_UNDEL, m, pc,
+ FSF_LAST | (THREADING() ? 0 : FSF_SKIP_CHID)));
+ break;
+
+ default:
+ panic("Unexpected incoming startup case");
+ break;
+
+ }
+ }
+ else if(IS_NEWS(ps_global->mail_stream)){
+ /*
+ * This will go to two different places depending on the FAKE_NEW
+ * feature (see pine_mail_open).
+ */
+ mn_set_cur(ps_global->msgmap,
+ first_sorted_flagged(F_UNSEEN|F_UNDEL, m, pc,
+ THREADING() ? 0 : FSF_SKIP_CHID));
+ }
+ else{
+ mn_set_cur(ps_global->msgmap,
+ mn_get_revsort(ps_global->msgmap)
+ ? 1L
+ : mn_get_total(ps_global->msgmap));
+ }
+
+ adjust_cur_to_visible(ps_global->mail_stream, ps_global->msgmap);
+ }
+ else if(!(rflags & SP_MATCH)){
+ mn_set_cur(ps_global->msgmap, -1L);
+ }
+
+ if(ps_global->mail_stream)
+ sp_set_first_unseen(ps_global->mail_stream, 0L);
+
+ return(1);
+}
+
+
+/*----------------------------------------------------------------------
+ Expand a folder name, taking account of the folders_dir and
+ any home directory reference
+
+ Args: filename -- The name of the file that is the folder
+
+ Result: The folder name is expanded in place.
+ Returns 0 and queues status message if unsuccessful.
+ Input string is overwritten with expanded name.
+ Returns 1 if successful.
+
+ ----*/
+int
+expand_foldername(char *filename, size_t len)
+{
+ char temp_filename[MAXPATH+1];
+
+ dprint((5, "=== expand_foldername called (%s) ===\n",
+ filename ? filename : "?"));
+
+ /*
+ * We used to check for valid filename chars here if "filename"
+ * didn't refer to a remote mailbox. This has been rethought
+ */
+
+ strncpy(temp_filename, filename, sizeof(temp_filename)-1);
+ temp_filename[sizeof(temp_filename)-1] = '\0';
+ if(strucmp(temp_filename, "inbox") == 0) {
+ strncpy(filename, ps_global->VAR_INBOX_PATH == NULL ? "inbox" :
+ ps_global->VAR_INBOX_PATH, len-1);
+ filename[len-1] = '\0';
+ } else if(temp_filename[0] == '{') {
+ strncpy(filename, temp_filename, len-1);
+ filename[len-1] = '\0';
+ } else if(ps_global->restricted && filename_is_restricted(temp_filename)){
+ q_status_message(SM_ORDER, 0, 3, "Can only open local folders");
+ return(0);
+ } else if(temp_filename[0] == '*') {
+ strncpy(filename, temp_filename, len-1);
+ filename[len-1] = '\0';
+ } else if(ps_global->VAR_OPER_DIR && filename_parent_ref(temp_filename)){
+ q_status_message(SM_ORDER, 0, 3,
+ "\"..\" not allowed in folder name");
+ return(0);
+ } else if (is_homedir_path(temp_filename)){
+ if(fnexpand(temp_filename, sizeof(temp_filename)) == NULL) {
+ q_status_message1(SM_ORDER, 3, 3, "Error expanding folder \"%s\"", temp_filename);
+ return(0);
+ }
+ strncpy(filename, temp_filename, len-1);
+ filename[len-1] = '\0';
+ } else if(F_ON(F_USE_CURRENT_DIR, ps_global) || is_absolute_path(temp_filename)){
+ strncpy(filename, temp_filename, len-1);
+ filename[len-1] = '\0';
+ } else if(ps_global->VAR_OPER_DIR){
+ build_path(filename, ps_global->VAR_OPER_DIR, temp_filename, len);
+ } else {
+ build_path(filename,
+#ifdef IS_WINDOWS
+ ps_global->folders_dir,
+#else /* UNIX */
+ ps_global->home_dir,
+#endif /* UNIX */
+ temp_filename, len);
+ }
+
+ dprint((5, "returning \"%s\"\n", filename ? filename : "?"));
+ return(1);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Expunge (if confirmed) and close a mail stream
+
+ Args: stream -- The MAILSTREAM * to close
+ final_msg -- If non-null, this should be set to point to a
+ message to print out in the caller, it is allocated
+ here and freed by the caller.
+
+ Result: Mail box is expunged and closed. A message is displayed to
+ say what happened
+ ----*/
+void
+expunge_and_close(MAILSTREAM *stream, char **final_msg, long unsigned int flags)
+{
+ long i, delete_count, seen_not_del;
+ char buff1[MAX_SCREEN_COLS+1], *moved_msg = NULL,
+ buff2[MAX_SCREEN_COLS+1], *folder;
+ CONTEXT_S *context;
+ struct variable *vars = ps_global->vars;
+ int ret, expunge = FALSE, no_close = 0;
+ char ing[4];
+
+ no_close = (flags & EC_NO_CLOSE);
+
+ if(!(stream && sp_flagged(stream, SP_LOCKED)))
+ stream = NULL;
+
+ /* check for dead stream */
+ if(stream && sp_dead_stream(stream)){
+ pine_mail_actually_close(stream);
+ stream = NULL;
+ }
+
+ if(stream != NULL){
+ context = sp_context(stream);
+ folder = STREAMNAME(stream);
+
+ dprint((2, "expunge_and_close: \"%s\"%s\n",
+ folder, no_close ? " (NO_CLOSE bit set)" : ""));
+
+ update_folder_unseen_by_stream(stream, UFU_NONE);
+
+ if(final_msg)
+ strncpy(ing, "ed", sizeof(ing));
+ else
+ strncpy(ing, "ing", sizeof(ing));
+
+ ing[sizeof(ing)-1] = '\0';
+
+ buff1[0] = '\0';
+ buff2[0] = '\0';
+
+ if(!stream->rdonly){
+
+ if(pith_opt_begin_closing)
+ (*pith_opt_begin_closing)(flags, folder);
+
+ mail_expunge_prefilter(stream, MI_CLOSING);
+
+ /*
+ * Be sure to expunge any excluded (filtered) msgs
+ * Do it here so they're not copied into read/archived
+ * folders *AND* to be sure we don't refilter them
+ * next time the folder's opened.
+ */
+ for(i = 1L; i <= stream->nmsgs; i++)
+ if(get_lflag(stream, NULL, i, MN_EXLD)){ /* if there are any */
+ delete_filtered_msgs(stream); /* delete them all */
+ expunge = TRUE;
+ break;
+ }
+
+ /* Save read messages? */
+ if(VAR_READ_MESSAGE_FOLDER && VAR_READ_MESSAGE_FOLDER[0]
+ && sp_flagged(stream, SP_INBOX)
+ && (seen_not_del = count_flagged(stream, F_SEEN | F_UNDEL))){
+
+ if(F_ON(F_AUTO_READ_MSGS,ps_global)
+ || (pith_opt_read_msg_prompt
+ && (*pith_opt_read_msg_prompt)(seen_not_del, VAR_READ_MESSAGE_FOLDER)))
+ /* move inbox's read messages */
+ moved_msg = move_read_msgs(stream, VAR_READ_MESSAGE_FOLDER,
+ buff1, sizeof(buff1), -1L);
+ }
+ else if(VAR_ARCHIVED_FOLDERS)
+ moved_msg = move_read_incoming(stream, context, folder,
+ VAR_ARCHIVED_FOLDERS,
+ buff1, sizeof(buff1));
+
+ /*
+ * We need the count_flagged to be executed not only to set
+ * delete_count, but also to set the searched bits in all of
+ * the deleted messages. The searched bit is used in the monkey
+ * business section below which undeletes deleted messages
+ * before expunging. It determines which messages are deleted
+ * by examining the searched bit, which had better be set or not
+ * based on this count_flagged call rather than some random
+ * search that happened earlier.
+ */
+ delete_count = count_flagged(stream, F_DEL);
+ if(F_ON(F_EXPUNGE_MANUALLY,ps_global))
+ delete_count = 0L;
+
+ ret = 'n';
+ if(!ps_global->noexpunge_on_close && delete_count){
+
+ if(F_ON(F_FULL_AUTO_EXPUNGE,ps_global)
+ || (F_ON(F_AUTO_EXPUNGE, ps_global)
+ && ((!strucmp(folder,ps_global->inbox_name))
+ || (context && (context->use & CNTXT_INCMNG)))
+ && context_isambig(folder))){
+ ret = 'y';
+ }
+ else if(pith_opt_expunge_prompt)
+ ret = (*pith_opt_expunge_prompt)(stream, pretty_fn(folder), delete_count);
+
+ /* get this message back in queue */
+ if(moved_msg)
+ q_status_message(SM_ORDER,
+ F_ON(F_AUTO_READ_MSGS,ps_global) ? 0 : 3, 5, moved_msg);
+
+ if(ret == 'y'){
+ long filtered;
+
+ filtered = any_lflagged(sp_msgmap(stream), MN_EXLD);
+
+ snprintf(buff2, sizeof(buff2),
+ "%s%s%s%.30s%s%s %s message%s and remov%s %s.",
+ no_close ? "" : "Clos",
+ no_close ? "" : ing,
+ no_close ? "" : " \"",
+ no_close ? "" : pretty_fn(folder),
+ no_close ? "" : "\". ",
+ final_msg ? "Kept" : "Keeping",
+ comatose(stream->nmsgs - filtered - delete_count),
+ plural(stream->nmsgs - filtered - delete_count),
+ ing,
+ long2string(delete_count));
+ if(final_msg)
+ *final_msg = cpystr(buff2);
+ else
+ q_status_message(SM_ORDER,
+ no_close ? 1 :
+ (F_ON(F_AUTO_EXPUNGE,ps_global)
+ || F_ON(F_FULL_AUTO_EXPUNGE,ps_global))
+ ? 0 : 3,
+ 5, buff2);
+
+ flush_status_messages(1);
+ ps_global->mm_log_error = 0;
+ ps_global->expunge_in_progress = 1;
+ mail_expunge(stream);
+ ps_global->expunge_in_progress = 0;
+ if(ps_global->mm_log_error && final_msg && *final_msg){
+ fs_give((void **)final_msg);
+ *final_msg = NULL;
+ }
+ }
+ }
+
+ if(ret != 'y'){
+ if(!ps_global->noexpunge_on_close && expunge){
+ MESSAGECACHE *mc;
+ char *seq;
+ int expbits;
+
+ /*
+ * filtered message monkey business.
+ * The Plan:
+ * 1) light sequence bits for legit deleted msgs
+ * and store marker in local extension
+ * 2) clear their deleted flag
+ * 3) perform expunge to removed filtered msgs
+ * 4) restore deleted flags for legit msgs
+ * based on local extension bit
+ */
+ for(i = 1L; i <= stream->nmsgs; i++)
+ if(!get_lflag(stream, NULL, i, MN_EXLD)
+ && (((mc = mail_elt(stream, i)) && mc->valid && mc->deleted)
+ || (mc && !mc->valid && mc->searched))){
+ mc->sequence = 1;
+ expbits = MSG_EX_DELETE;
+ msgno_exceptions(stream, i, "0", &expbits, TRUE);
+ }
+ else if((mc = mail_elt(stream, i)) != NULL)
+ mc->sequence = 0;
+
+ if((seq = build_sequence(stream, NULL, NULL)) != NULL){
+ mail_flag(stream, seq, "\\DELETED", ST_SILENT);
+ fs_give((void **) &seq);
+ }
+
+ ps_global->mm_log_error = 0;
+ ps_global->expunge_in_progress = 1;
+ mail_expunge(stream);
+ ps_global->expunge_in_progress = 0;
+
+ for(i = 1L; i <= stream->nmsgs; i++)
+ if((mc = mail_elt(stream, i)) != NULL)
+ mc->sequence
+ = (msgno_exceptions(stream, i, "0", &expbits, FALSE)
+ && (expbits & MSG_EX_DELETE));
+
+ if((seq = build_sequence(stream, NULL, NULL)) != NULL){
+ mail_flag(stream, seq, "\\DELETED", ST_SET|ST_SILENT);
+ fs_give((void **) &seq);
+ }
+ }
+
+ if(!no_close){
+ if(stream->nmsgs){
+ snprintf(buff2, sizeof(buff2),
+ "Clos%s folder \"%.*s\". %s%s%s message%s.",
+ ing,
+ sizeof(buff2)-50, pretty_fn(folder),
+ final_msg ? "Kept" : "Keeping",
+ (stream->nmsgs == 1L) ? " single" : " all ",
+ (stream->nmsgs > 1L)
+ ? comatose(stream->nmsgs) : "",
+ plural(stream->nmsgs));
+ }
+ else{
+ snprintf(buff2, sizeof(buff2), "Clos%s empty folder \"%.*s\"",
+ ing, sizeof(buff2)-50, pretty_fn(folder));
+ }
+
+ if(final_msg)
+ *final_msg = cpystr(buff2);
+ else
+ q_status_message(SM_ORDER, 0, 3, buff2);
+ }
+ }
+ }
+ else{
+ if(IS_NEWS(stream)){
+ /*
+ * Mark the filtered messages deleted so they aren't
+ * filtered next time.
+ */
+ for(i = 1L; i <= stream->nmsgs; i++){
+ int exbits;
+ if(msgno_exceptions(stream, i, "0" , &exbits, FALSE)
+ && (exbits & MSG_EX_FILTERED)){
+ delete_filtered_msgs(stream);
+ break;
+ }
+ }
+ /* first, look to archive read messages */
+ if((moved_msg = move_read_incoming(stream, context, folder,
+ VAR_ARCHIVED_FOLDERS,
+ buff1, sizeof(buff1))) != NULL)
+ q_status_message(SM_ORDER,
+ F_ON(F_AUTO_READ_MSGS,ps_global) ? 0 : 3, 5, moved_msg);
+
+ snprintf(buff2, sizeof(buff2), "Clos%s news group \"%.*s\"",
+ ing, sizeof(buff2)-50, pretty_fn(folder));
+
+ if(F_ON(F_NEWS_CATCHUP, ps_global)){
+ MESSAGECACHE *mc;
+
+ /* count visible messages */
+ (void) count_flagged(stream, F_DEL);
+ for(i = 1L, delete_count = 0L; i <= stream->nmsgs; i++)
+ if(!(get_lflag(stream, NULL, i, MN_EXLD)
+ || ((mc = mail_elt(stream, i)) && mc->valid
+ && mc->deleted)
+ || (mc && !mc->valid && mc->searched)))
+ delete_count++;
+
+ if(delete_count && pith_opt_expunge_prompt){
+ ret = (*pith_opt_expunge_prompt)(stream, pretty_fn(folder), delete_count);
+ if(ret == 'y'){
+ char seq[64];
+
+ snprintf(seq, sizeof(seq), "1:%ld", stream->nmsgs);
+ mail_flag(stream, seq, "\\DELETED", ST_SET|ST_SILENT);
+ }
+ }
+ }
+
+ if(F_ON(F_NEWS_CROSS_DELETE, ps_global))
+ cross_delete_crossposts(stream);
+ }
+ else
+ snprintf(buff2, sizeof(buff2),
+ "Clos%s read-only folder \"%.*s\". No changes to save",
+ ing, sizeof(buff2)-60, pretty_fn(folder));
+
+ if(final_msg)
+ *final_msg = cpystr(buff2);
+ else
+ q_status_message(SM_ORDER, 0, 2, buff2);
+ }
+
+ /*
+ * Make darn sure any mm_log fallout caused above get's seen...
+ */
+ if(!no_close){
+ flush_status_messages(1);
+ pine_mail_close(stream);
+ }
+ }
+}
+
+
+void
+agg_select_all(MAILSTREAM *stream, MSGNO_S *msgmap, long int *diff, int on)
+{
+ long i;
+ int hidden = any_lflagged(msgmap, MN_HIDE) > 0L;
+
+ for(i = 1L; i <= mn_get_total(msgmap); i++){
+ if(on){ /* mark 'em all */
+ set_lflag(stream, msgmap, i, MN_SLCT, 1);
+ }
+ else { /* unmark 'em all */
+ if(get_lflag(stream, msgmap, i, MN_SLCT)){
+ if(diff)
+ (*diff)++;
+
+ set_lflag(stream, msgmap, i, MN_SLCT, 0);
+ }
+ else if(hidden)
+ set_lflag(stream, msgmap, i, MN_HIDE, 0);
+ }
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Move all read messages from srcfldr to dstfldr
+
+ Args: stream -- stream to usr
+ dstfldr -- folder to receive moved messages
+ buf -- place to write success message
+
+ Returns: success message or NULL for failure
+ ----*/
+char *
+move_read_msgs(MAILSTREAM *stream, char *dstfldr, char *buf, size_t buflen, long int searched)
+{
+ long i, raw;
+ int we_cancel = 0;
+ MSGNO_S *msgmap = NULL;
+ CONTEXT_S *save_context = NULL;
+ char *bufp = NULL;
+ MESSAGECACHE *mc;
+
+ if(!is_absolute_path(dstfldr)
+ && !(save_context = default_save_context(ps_global->context_list)))
+ save_context = ps_global->context_list;
+
+ /*
+ * Use the "searched" bit to select the set of messages
+ * we want to save. If searched is non-neg, the message
+ * cache already has the necessary "searched" bits set.
+ */
+ if(searched < 0L)
+ searched = count_flagged(stream, F_SEEN | F_UNDEL);
+
+ if(searched){
+ /*
+ * We're going to be messing with SLCT flags in order
+ * to do our work. If this stream is a StayOpen stream
+ * we want to restore those flags after we're done
+ * using them. So copy them into STMP so we can put them
+ * back below.
+ */
+ msgmap = sp_msgmap(stream);
+ if(sp_flagged(stream, SP_PERMLOCKED))
+ copy_lflags(stream, msgmap, MN_SLCT, MN_STMP);
+
+ set_lflags(stream, msgmap, MN_SLCT, 0);
+
+ /* select search results */
+ for(i = 1L; i <= mn_get_total(msgmap); i++)
+ if((raw = mn_m2raw(msgmap, i)) > 0L && stream
+ && raw <= stream->nmsgs
+ && (mc = mail_elt(stream,raw))
+ && ((mc->valid && mc->seen && !mc->deleted)
+ || (!mc->valid && mc->searched)))
+ set_lflag(stream, msgmap, i, MN_SLCT, 1);
+
+ pseudo_selected(stream, msgmap);
+ snprintf(buf, buflen, "Moving %s read message%s to \"%s\"",
+ comatose(searched), plural(searched), dstfldr);
+ we_cancel = busy_cue(buf, NULL, 0);
+ if(save(ps_global, stream, save_context, dstfldr, msgmap,
+ SV_DELETE | SV_FIX_DELS | SV_INBOXWOCNTXT) == searched)
+ strncpy(bufp = buf + 1, "Moved", MIN(5,buflen)); /* change Moving to Moved */
+
+ buf[buflen-1] = '\0';
+ if(we_cancel)
+ cancel_busy_cue(bufp ? 0 : -1);
+
+ if(sp_flagged(stream, SP_PERMLOCKED)){
+ restore_selected(msgmap);
+ copy_lflags(stream, msgmap, MN_STMP, MN_SLCT);
+ }
+ }
+
+ return(bufp);
+}
+
+
+/*----------------------------------------------------------------------
+ Move read messages from folder if listed in archive
+
+ Args:
+
+ ----*/
+char *
+move_read_incoming(MAILSTREAM *stream, CONTEXT_S *context, char *folder,
+ char **archive, char *buf, size_t buflen)
+{
+ char *s, *d, *f = folder;
+ long seen_undel;
+
+ if(buf && buflen > 0)
+ buf[0] = '\0';
+
+ if(archive && !sp_flagged(stream, SP_INBOX)
+ && context && (context->use & CNTXT_INCMNG)
+ && ((context_isambig(folder)
+ && folder_is_nick(folder, FOLDERS(context), 0))
+ || folder_index(folder, context, FI_FOLDER) > 0)
+ && (seen_undel = count_flagged(stream, F_SEEN | F_UNDEL))){
+
+ for(; f && *archive; archive++){
+ char *p;
+
+ get_pair(*archive, &s, &d, 1, 0);
+ if(s && d
+ && (!strcmp(s, folder)
+ || (context_isambig(folder)
+ && (p = folder_is_nick(folder, FOLDERS(context), 0))
+ && !strcmp(s, p)))){
+ if(F_ON(F_AUTO_READ_MSGS,ps_global)
+ || (pith_opt_read_msg_prompt
+ && (*pith_opt_read_msg_prompt)(seen_undel, d)))
+ buf = move_read_msgs(stream, d, buf, buflen, seen_undel);
+
+ f = NULL; /* bust out after cleaning up */
+ }
+
+ fs_give((void **)&s);
+ fs_give((void **)&d);
+ }
+ }
+
+ return((buf && *buf) ? buf : NULL);
+}
+
+
+/*----------------------------------------------------------------------
+ Delete all references to a deleted news posting
+
+
+ ---*/
+void
+cross_delete_crossposts(MAILSTREAM *stream)
+{
+ if(count_flagged(stream, F_DEL)){
+ static char *fields[] = {"Xref", NULL};
+ MAILSTREAM *tstream;
+ CONTEXT_S *fake_context;
+ char *xref, *p, *group, *uidp,
+ *newgrp, newfolder[MAILTMPLEN];
+ long i, hostlatch = 0L;
+ imapuid_t uid;
+ int we_cancel = 0;
+ MESSAGECACHE *mc;
+
+ strncpy(newfolder, stream->mailbox, sizeof(newfolder));
+ newfolder[sizeof(newfolder)-1] = '\0';
+ if(!(newgrp = strstr(newfolder, "#news.")))
+ return; /* weird mailbox */
+
+ newgrp += 6;
+
+ we_cancel = busy_cue("Busy deleting crosspostings", NULL, 1);
+
+ /* build subscribed list */
+ strncpy(newgrp, "[]", sizeof(newfolder)-(newgrp-newfolder));
+ newfolder[sizeof(newfolder)-1] = '\0';
+ fake_context = new_context(newfolder, 0);
+ build_folder_list(NULL, fake_context, "*", NULL, BFL_LSUB);
+
+ for(i = 1L; i <= stream->nmsgs; i++)
+ if(!get_lflag(stream, NULL, i, MN_EXLD)
+ && (mc = mail_elt(stream, i)) && mc->deleted){
+
+ if((xref = pine_fetchheader_lines(stream, i, NULL, fields)) != NULL){
+ if((p = strstr(xref, ": ")) != NULL){
+ p += 2;
+ hostlatch = 0L;
+ while(*p){
+ group = p;
+ uidp = NULL;
+
+ /* get server */
+ while(*++p && !isspace((unsigned char) *p))
+ if(*p == ':'){
+ *p = '\0';
+ uidp = p + 1;
+ }
+
+ /* tie off uid/host */
+ if(*p)
+ *p++ = '\0';
+
+ if(uidp){
+ /*
+ * For the nonce, we're only deleting valid
+ * uid's from outside the current newsgroup
+ * and inside only subscribed newsgroups
+ */
+ if(strcmp(group, stream->mailbox
+ + (newgrp - newfolder))
+ && folder_index(group, fake_context,
+ FI_FOLDER) >= 0){
+ if((uid = strtoul(uidp, NULL, 10)) != 0L){
+ strncpy(newgrp, group, sizeof(newfolder)-(newgrp-newfolder));
+ newfolder[sizeof(newfolder)-1] = '\0';
+ if((tstream = pine_mail_open(NULL,
+ newfolder,
+ SP_USEPOOL,
+ NULL)) != NULL){
+ mail_flag(tstream, ulong2string(uid),
+ "\\DELETED",
+ ST_SET | ST_UID);
+ pine_mail_close(tstream);
+ }
+ }
+ else
+ break; /* bogus uid */
+ }
+ }
+ else if(!hostlatch++){
+ char *p, *q;
+
+ if(stream->mailbox[0] == '{'
+ && !((p = strpbrk(stream->mailbox+1, "}:/"))
+ && !struncmp(stream->mailbox + 1,
+ q = canonical_name(group),
+ p - (stream->mailbox + 1))
+ && q[p - (stream->mailbox + 1)] == '\0'))
+ break; /* different server? */
+ }
+ else
+ break; /* bogus field! */
+ }
+ }
+
+ fs_give((void **) &xref);
+ }
+ }
+
+ free_context(&fake_context);
+
+ if(we_cancel)
+ cancel_busy_cue(0);
+ }
+}
+
+
+/*
+ * Original version from Eduardo Chappa.
+ *
+ * Returns a string describing the number of new/unseen messages
+ * for use in the status line. Can return NULL. Caller must free the memory.
+ */
+char *
+new_messages_string(MAILSTREAM *stream)
+{
+ char message[80] = {'\0'};
+ long new = 0L, uns = 0L;
+ int i, imapstatus = 0;
+
+ for (i = 0; ps_global->index_disp_format[i].ctype != iNothing
+ && ps_global->index_disp_format[i].ctype != iIStatus
+ && ps_global->index_disp_format[i].ctype != iSIStatus; i++)
+ ;
+
+ imapstatus = ps_global->index_disp_format[i].ctype == iIStatus
+ || ps_global->index_disp_format[i].ctype == iSIStatus;
+
+ get_new_message_count(stream, imapstatus, &new, &uns);
+
+ if(imapstatus)
+ snprintf(message, sizeof(message), " - %s%s%s%s%s%s%s",
+ uns != 0L ? comatose((long) new) : "",
+ uns != 0L ? " " : "",
+ uns != 0L ? _("recent") : "",
+ uns > 0L ? ", " : "",
+ uns != -1L ? comatose((long) uns) : "",
+ uns != -1L ? " " : "",
+ uns != -1L ? _("unseen") : "");
+ else if(!imapstatus && new > 0L)
+ snprintf(message, sizeof(message), " - %s %s",
+ comatose((long) new), _("new"));
+
+ return(*message ? cpystr(message) : NULL);
+}
+
+
+void
+get_new_message_count(MAILSTREAM *stream, int imapstatus,
+ long *new, long *unseen)
+{
+ if(new)
+ *new = 0L;
+
+ if(unseen)
+ *unseen = 0L;
+
+ if(imapstatus){
+ if(new)
+ *new = count_flagged(stream, F_RECENT | F_UNSEEN | F_UNDEL);
+
+ if(!IS_NEWS(stream)){
+ if(unseen)
+ *unseen = count_flagged(stream, F_UNSEEN | F_UNDEL);
+ }
+ else if(unseen)
+ *unseen = -1L;
+ }
+ else{
+ if(IS_NEWS(stream)){
+ if(F_ON(F_FAKE_NEW_IN_NEWS, ps_global)){
+ if(new)
+ *new = count_flagged(stream, F_RECENT | F_UNSEEN | F_UNDEL);
+ }
+ }
+ else{
+ if(new)
+ *new = count_flagged(stream, F_UNSEEN | F_UNDEL | F_UNANS);
+ }
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ ZOOM the message index (set any and all necessary hidden flag bits)
+
+ Args: state -- usual pine state
+ msgmap -- usual message mapping
+ Returns: number of messages zoomed in on
+
+ ----*/
+long
+zoom_index(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, int onflag)
+{
+ long i, count = 0L, first = 0L, msgno;
+ PINETHRD_S *thrd = NULL, *topthrd = NULL, *nthrd;
+
+ if(any_lflagged(msgmap, onflag)){
+
+ if(THREADING() && sp_viewing_a_thread(stream)){
+ /* get top of current thread */
+ thrd = fetch_thread(stream, mn_m2raw(msgmap, mn_get_cur(msgmap)));
+ if(thrd && thrd->top)
+ topthrd = fetch_thread(stream, thrd->top);
+ }
+
+ for(i = 1L; i <= mn_get_total(msgmap); i++){
+ if(!get_lflag(stream, msgmap, i, onflag)){
+ set_lflag(stream, msgmap, i, MN_HIDE, 1);
+ }
+ else{
+
+ /*
+ * Because subject lines depend on whether or not
+ * other parts of the thread above us are visible or not.
+ */
+ if(THREADING() && !THRD_INDX()
+ && ps_global->thread_disp_style == THREAD_MUTTLIKE)
+ clear_index_cache_ent(stream, i, 0);
+
+ /*
+ * If a selected message is hidden beneath a collapsed
+ * thread (not beneath a thread index line, but a collapsed
+ * thread or subthread) then we make it visible. The user
+ * should be able to see the selected messages when they
+ * Zoom. We could get a bit fancier and re-collapse the
+ * thread when the user unzooms, but we don't do that
+ * for now.
+ */
+ if(THREADING() && !THRD_INDX()
+ && get_lflag(stream, msgmap, i, MN_CHID)){
+
+ /*
+ * What we need to do is to unhide this message and
+ * uncollapse any parent above us.
+ * Also, when we uncollapse a parent, we need to
+ * trace back down the tree and unhide until we get
+ * to a collapse point or the end. That's what
+ * set_thread_subtree does.
+ */
+
+ thrd = fetch_thread(stream, mn_m2raw(msgmap, i));
+
+ if(thrd && thrd->parent)
+ thrd = fetch_thread(stream, thrd->parent);
+ else
+ thrd = NULL;
+
+ /* unhide and uncollapse its parents */
+ while(thrd){
+ /* if this parent is collapsed */
+ if(get_lflag(stream, NULL, thrd->rawno, MN_COLL)){
+ /* uncollapse this parent and unhide its subtree */
+ msgno = mn_raw2m(msgmap, thrd->rawno);
+ if(msgno > 0L && msgno <= mn_get_total(msgmap)){
+ set_lflag(stream, msgmap, msgno,
+ MN_COLL | MN_CHID, 0);
+ if(thrd->next &&
+ (nthrd = fetch_thread(stream, thrd->next)))
+ set_thread_subtree(stream, nthrd, msgmap,
+ 0, MN_CHID);
+ }
+
+ /* collapse symbol will be wrong */
+ clear_index_cache_ent(stream, msgno, 0);
+ }
+
+ /*
+ * Continue up tree to next parent looking for
+ * more collapse points.
+ */
+ if(thrd->parent)
+ thrd = fetch_thread(stream, thrd->parent);
+ else
+ thrd = NULL;
+ }
+ }
+
+ count++;
+ if(!first){
+ if(THRD_INDX()){
+ /* find msgno of top of thread for msg i */
+ if((thrd=fetch_thread(stream, mn_m2raw(msgmap, i)))
+ && thrd->top)
+ first = mn_raw2m(msgmap, thrd->top);
+ }
+ else if(THREADING() && sp_viewing_a_thread(stream)){
+ /* want first selected message in this thread */
+ if(topthrd
+ && (thrd=fetch_thread(stream, mn_m2raw(msgmap, i)))
+ && thrd->top
+ && topthrd->rawno == thrd->top)
+ first = i;
+ }
+ else
+ first = i;
+ }
+ }
+ }
+
+ if(THRD_INDX()){
+ thrd = fetch_thread(stream, mn_m2raw(msgmap, mn_get_cur(msgmap)));
+ if(count_lflags_in_thread(stream, thrd, msgmap, onflag) == 0)
+ mn_set_cur(msgmap, first);
+ }
+ else if((THREADING() && sp_viewing_a_thread(stream))
+ || !get_lflag(stream, msgmap, mn_get_cur(msgmap), onflag)){
+ if(!first){
+ int flags = 0;
+
+ /*
+ * Nothing was selected in the thread we were in, so
+ * drop back to the Thread Index instead. Set the current
+ * thread to the first one that has a selection in it.
+ */
+
+ unview_thread(state, stream, msgmap);
+
+ i = next_sorted_flagged(F_UNDEL, stream, 1L, &flags);
+
+ if(flags & NSF_FLAG_MATCH
+ && (thrd=fetch_thread(stream, mn_m2raw(msgmap, i)))
+ && thrd->top)
+ first = mn_raw2m(msgmap, thrd->top);
+ else
+ first = 1L; /* can't happen */
+
+ mn_set_cur(msgmap, first);
+ }
+ else{
+ if(msgline_hidden(stream, msgmap, mn_get_cur(msgmap), 0))
+ mn_set_cur(msgmap, first);
+ }
+ }
+ }
+
+ return(count);
+}
+
+
+
+/*----------------------------------------------------------------------
+ UnZOOM the message index (clear any and all hidden flag bits)
+
+ Args: state -- usual pine state
+ msgmap -- usual message mapping
+ Returns: 1 if hidden bits to clear and they were, 0 if none to clear
+
+ ----*/
+int
+unzoom_index(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap)
+{
+ register long i;
+
+ if(!any_lflagged(msgmap, MN_HIDE))
+ return(0);
+
+ for(i = 1L; i <= mn_get_total(msgmap); i++)
+ set_lflag(stream, msgmap, i, MN_HIDE, 0);
+
+ return(1);
+}
+
+
+int
+agg_text_select(MAILSTREAM *stream, MSGNO_S *msgmap, char type, char *namehdr,
+ int not, int check_for_my_addresses,
+ char *sstring, char *charset, SEARCHSET **limitsrch)
+{
+ int old_imap, we_cancel;
+ int me_with_regex = 0;
+ long searchflags;
+ SEARCHPGM *srchpgm, *pgm, *secondpgm = NULL, *thirdpgm = NULL;
+ SEARCHPGM *mepgm = NULL;
+
+ if(!stream)
+ return(1);
+
+ old_imap = (is_imap_stream(stream) && !modern_imap_stream(stream));
+
+ /*
+ * Special case code for matching one of the user's addresses.
+ */
+ if(check_for_my_addresses){
+ char **t, *alt;
+
+ if(F_OFF(F_DISABLE_REGEX, ps_global)){
+ for(t = ps_global->VAR_ALT_ADDRS; !me_with_regex && t && t[0] && t[0][0]; t++){
+ alt = (*t);
+ if(contains_regex_special_chars(alt))
+ me_with_regex++;
+ }
+ }
+
+ /*
+ * In this case we can't use search because it doesn't support
+ * regex. So we have to manually do the whole thing ourselves.
+ * The searching is done in the subroutine and the searched bits
+ * will be set on return.
+ */
+ if(me_with_regex){
+ search_for_our_regex_addresses(stream, type, not, limitsrch ? *limitsrch : NULL);
+ return(0);
+ }
+ else{
+ PATGRP_S *patgrp = NULL;
+ PATTERN_S *p = NULL;
+ PATTERN_S *pattern = NULL, **nextp;
+ char buf[1000];
+
+ /*
+ * We're going to use the pattern matching machinery to generate
+ * a search program. We build a pattern whose only purpose is
+ * to generate the program.
+ */
+ nextp = &pattern;
+
+ /* add standard me addresses to list */
+ if(ps_global->VAR_USER_ID){
+ if(ps_global->userdomain && ps_global->userdomain[0]){
+ p = (PATTERN_S *) fs_get(sizeof(*p));
+ memset((void *) p, 0, sizeof(*p));
+ snprintf(buf, sizeof(buf), "%s@%s", ps_global->VAR_USER_ID,
+ ps_global->userdomain);
+ p->substring = cpystr(buf);
+ *nextp = p;
+ nextp = &p->next;
+ }
+
+ if(!ps_global->userdomain && ps_global->localdomain && ps_global->localdomain[0]){
+ p = (PATTERN_S *) fs_get(sizeof(*p));
+ memset((void *) p, 0, sizeof(*p));
+ snprintf(buf, sizeof(buf), "%s@%s", ps_global->VAR_USER_ID,
+ ps_global->localdomain);
+ p->substring = cpystr(buf);
+ *nextp = p;
+ nextp = &p->next;
+ }
+
+ if(!ps_global->userdomain && ps_global->hostname && ps_global->hostname[0]){
+ p = (PATTERN_S *) fs_get(sizeof(*p));
+ memset((void *) p, 0, sizeof(*p));
+ snprintf(buf, sizeof(buf), "%s@%s", ps_global->VAR_USER_ID,
+ ps_global->hostname);
+ p->substring = cpystr(buf);
+ *nextp = p;
+ nextp = &p->next;
+ }
+ }
+
+ /* add user's alternate addresses */
+ for(t = ps_global->VAR_ALT_ADDRS; t && t[0] && t[0][0]; t++){
+ alt = (*t);
+ if(alt && alt[0]){
+ p = (PATTERN_S *) fs_get(sizeof(*p));
+ memset((void *) p, 0, sizeof(*p));
+ p->substring = cpystr(alt);
+ *nextp = p;
+ nextp = &p->next;
+ }
+ }
+
+ patgrp = (PATGRP_S *) fs_get(sizeof(*patgrp));
+ memset((void *) patgrp, 0, sizeof(*patgrp));
+
+ switch(type){
+ case 'r' :
+ patgrp->recip = pattern;
+ break;
+ case 'p' :
+ patgrp->partic = pattern;
+ break;
+ case 'f' :
+ patgrp->from = pattern;
+ break;
+ case 'c' :
+ patgrp->cc = pattern;
+ break;
+ case 't' :
+ patgrp->to = pattern;
+ break;
+ default :
+ q_status_message(SM_ORDER, 3, 3, "Unhandled case in agg_text_select");
+ break;
+ }
+
+ mepgm = match_pattern_srchpgm(patgrp, stream, NULL);
+
+ free_patgrp(&patgrp);
+ }
+ }
+
+ if(mepgm){
+ if(not && !old_imap){
+ srchpgm = mail_newsearchpgm();
+ srchpgm->not = mail_newsearchpgmlist();
+ srchpgm->not->pgm = mepgm;
+ }
+ else{
+ srchpgm = mepgm;
+ }
+
+ }
+ else{
+ /* create a search program and fill it in */
+ srchpgm = pgm = mail_newsearchpgm();
+ if(not && !old_imap){
+ srchpgm->not = mail_newsearchpgmlist();
+ srchpgm->not->pgm = mail_newsearchpgm();
+ pgm = srchpgm->not->pgm;
+ }
+ }
+
+ if(!mepgm)
+ switch(type){
+ case 'h' : /* Any header */
+ pgm->header = mail_newsearchheader (namehdr, sstring);
+ break;
+
+ case 'r' : /* TO or CC */
+ if(old_imap){
+ /* No OR on old servers */
+ pgm->to = mail_newstringlist();
+ pgm->to->text.data = (unsigned char *) cpystr(sstring);
+ pgm->to->text.size = strlen(sstring);
+ secondpgm = mail_newsearchpgm();
+ secondpgm->cc = mail_newstringlist();
+ secondpgm->cc->text.data = (unsigned char *) cpystr(sstring);
+ secondpgm->cc->text.size = strlen(sstring);
+ }
+ else{
+ pgm->or = mail_newsearchor();
+ pgm->or->first->to = mail_newstringlist();
+ pgm->or->first->to->text.data = (unsigned char *) cpystr(sstring);
+ pgm->or->first->to->text.size = strlen(sstring);
+ pgm->or->second->cc = mail_newstringlist();
+ pgm->or->second->cc->text.data = (unsigned char *) cpystr(sstring);
+ pgm->or->second->cc->text.size = strlen(sstring);
+ }
+
+ break;
+
+ case 'p' : /* TO or CC or FROM */
+ if(old_imap){
+ /* No OR on old servers */
+ pgm->to = mail_newstringlist();
+ pgm->to->text.data = (unsigned char *) cpystr(sstring);
+ pgm->to->text.size = strlen(sstring);
+ secondpgm = mail_newsearchpgm();
+ secondpgm->cc = mail_newstringlist();
+ secondpgm->cc->text.data = (unsigned char *) cpystr(sstring);
+ secondpgm->cc->text.size = strlen(sstring);
+ thirdpgm = mail_newsearchpgm();
+ thirdpgm->from = mail_newstringlist();
+ thirdpgm->from->text.data = (unsigned char *) cpystr(sstring);
+ thirdpgm->from->text.size = strlen(sstring);
+ }
+ else{
+ pgm->or = mail_newsearchor();
+ pgm->or->first->to = mail_newstringlist();
+ pgm->or->first->to->text.data = (unsigned char *) cpystr(sstring);
+ pgm->or->first->to->text.size = strlen(sstring);
+
+ pgm->or->second->or = mail_newsearchor();
+ pgm->or->second->or->first->cc = mail_newstringlist();
+ pgm->or->second->or->first->cc->text.data =
+ (unsigned char *) cpystr(sstring);
+ pgm->or->second->or->first->cc->text.size = strlen(sstring);
+ pgm->or->second->or->second->from = mail_newstringlist();
+ pgm->or->second->or->second->from->text.data =
+ (unsigned char *) cpystr(sstring);
+ pgm->or->second->or->second->from->text.size = strlen(sstring);
+ }
+
+ break;
+
+ case 'f' : /* FROM */
+ pgm->from = mail_newstringlist();
+ pgm->from->text.data = (unsigned char *) cpystr(sstring);
+ pgm->from->text.size = strlen(sstring);
+ break;
+
+ case 'c' : /* CC */
+ pgm->cc = mail_newstringlist();
+ pgm->cc->text.data = (unsigned char *) cpystr(sstring);
+ pgm->cc->text.size = strlen(sstring);
+ break;
+
+ case 't' : /* TO */
+ pgm->to = mail_newstringlist();
+ pgm->to->text.data = (unsigned char *) cpystr(sstring);
+ pgm->to->text.size = strlen(sstring);
+ break;
+
+ case 's' : /* SUBJECT */
+ pgm->subject = mail_newstringlist();
+ pgm->subject->text.data = (unsigned char *) cpystr(sstring);
+ pgm->subject->text.size = strlen(sstring);
+ break;
+
+ case 'a' : /* ALL TEXT */
+ pgm->text = mail_newstringlist();
+ pgm->text->text.data = (unsigned char *) cpystr(sstring);
+ pgm->text->text.size = strlen(sstring);
+ break;
+
+ case 'b' : /* ALL BODY TEXT */
+ pgm->body = mail_newstringlist();
+ pgm->body->text.data = (unsigned char *) cpystr(sstring);
+ pgm->body->text.size = strlen(sstring);
+ break;
+
+ default :
+ dprint((1,"\n - BOTCH: select_text unrecognized type\n"));
+ return(1);
+ }
+
+ /*
+ * If we happen to have any messages excluded, make sure we
+ * don't waste time searching their text...
+ */
+ srchpgm->msgno = (limitsrch ? *limitsrch : NULL);
+
+ /* TRANSLATORS: warning to user that we're busy selecting messages */
+ we_cancel = busy_cue(_("Busy Selecting"), NULL, 1);
+
+ searchflags = SE_NOPREFETCH | (secondpgm ? 0 : SE_FREE);
+
+ pine_mail_search_full(stream, !old_imap ? charset : NULL, srchpgm,
+ searchflags);
+
+ /* search for To or Cc; or To or Cc or From on old imap server */
+ if(secondpgm){
+ if(srchpgm){
+ srchpgm->msgno = NULL;
+ mail_free_searchpgm(&srchpgm);
+ }
+
+ secondpgm->msgno = (limitsrch ? *limitsrch : NULL);
+ searchflags |= (SE_RETAIN | (thirdpgm ? 0 : SE_FREE));
+
+ pine_mail_search_full(stream, NULL, secondpgm, searchflags);
+
+ if(thirdpgm){
+ if(secondpgm){
+ secondpgm->msgno = NULL;
+ mail_free_searchpgm(&secondpgm);
+ }
+
+ thirdpgm->msgno = (limitsrch ? *limitsrch : NULL);
+ searchflags |= SE_FREE;
+ pine_mail_search_full(stream, NULL, thirdpgm, searchflags);
+ }
+ }
+
+ /* we know this was freed in mail_search, let caller know */
+ if(limitsrch)
+ *limitsrch = NULL;
+
+ if(old_imap && not){
+ MESSAGECACHE *mc;
+ long msgno;
+
+ /*
+ * Old imap server doesn't have a NOT, so we actually searched for
+ * the subject (or whatever) instead of !subject. Flip the searched
+ * bits.
+ */
+ for(msgno = 1L; msgno <= mn_get_total(msgmap); msgno++)
+ if(stream && msgno <= stream->nmsgs
+ && (mc=mail_elt(stream, msgno)) && mc->searched)
+ mc->searched = NIL;
+ else
+ mc->searched = T;
+ }
+
+ if(we_cancel)
+ cancel_busy_cue(0);
+
+ return(0);
+}
+
+
+void
+search_for_our_regex_addresses(MAILSTREAM *stream, char type, int not,
+ SEARCHSET *searchset)
+{
+ long rawno, count = 0L;
+ MESSAGECACHE *mc;
+ ADDRESS *addr1 = NULL, *addr2 = NULL, *addr3 = NULL;
+ ENVELOPE *env;
+ SEARCHSET *s, *ss = NULL;
+ extern MAILSTREAM *mm_search_stream;
+ extern long mm_search_count;
+
+ mm_search_count = 0L;
+ mm_search_stream = stream;
+
+ if(!stream)
+ return;
+
+ /* set searched bits to zero */
+ for(rawno = 1L; rawno <= stream->nmsgs; rawno++)
+ if((mc=mail_elt(stream, rawno)) != NULL)
+ mc->searched = NIL;
+
+ /* set sequence bits for envelopes we need */
+ for(rawno = 1L; rawno <= stream->nmsgs; rawno++){
+ if((mc = mail_elt(stream, rawno)) != NULL){
+ if((!searchset || in_searchset(searchset, (unsigned long) rawno))
+ && !mc->private.msg.env){
+ mc->sequence = 1;
+ count++;
+ }
+ else
+ mc->sequence = 0;
+ }
+ }
+
+ /*
+ * Set up a searchset that will control the fetch ahead.
+ */
+ if(count){
+ ss = build_searchset(stream);
+ if(ss){
+ SEARCHSET **sset = NULL;
+
+ mail_parameters(NULL, SET_FETCHLOOKAHEADLIMIT, (void *) count);
+
+ /* this resets automatically after the first fetch */
+ sset = (SEARCHSET **) mail_parameters(stream,
+ GET_FETCHLOOKAHEAD,
+ (void *) stream);
+ if(sset)
+ *sset = ss;
+ }
+ }
+
+ for(s = searchset; s; s = s->next){
+ for(rawno = s->first; rawno <= s->last; rawno++){
+ env = pine_mail_fetchenvelope(stream, rawno);
+ addr1 = addr2 = addr3 = NULL;
+ switch(type){
+ case 'r' :
+ addr1 = env ? env->to : NULL;
+ addr2 = env ? env->cc : NULL;
+ break;
+ case 'p' :
+ addr1 = env ? env->to : NULL;
+ addr2 = env ? env->cc : NULL;
+ addr3 = env ? env->from : NULL;
+ break;
+ case 'f' :
+ addr1 = env ? env->from : NULL;
+ break;
+ case 'c' :
+ addr1 = env ? env->cc : NULL;
+ break;
+ break;
+ case 't' :
+ addr1 = env ? env->to : NULL;
+ break;
+ default :
+ q_status_message(SM_ORDER, 3, 3, "Unhandled case2 in agg_text_select");
+ break;
+ }
+
+ if(addr1 && address_is_us(addr1, ps_global)){
+ if((mc=mail_elt(stream, rawno)) != NULL)
+ mm_searched(stream, rawno);
+ }
+ else if(addr2 && address_is_us(addr2, ps_global)){
+ if((mc=mail_elt(stream, rawno)) != NULL)
+ mm_searched(stream, rawno);
+ }
+ else if(addr3 && address_is_us(addr3, ps_global)){
+ if((mc=mail_elt(stream, rawno)) != NULL)
+ mm_searched(stream, rawno);
+ }
+ }
+ }
+
+ if(ss)
+ mail_free_searchset(&ss);
+
+ if(not){
+ for(rawno = 1L; rawno <= stream->nmsgs; rawno++){
+ if((mc=mail_elt(stream, rawno)) && mc->searched)
+ mc->searched = NIL;
+ else
+ mc->searched = T;
+ }
+ }
+}
+
+
+int
+agg_flag_select(MAILSTREAM *stream, int not, int crit, SEARCHSET **limitsrch)
+{
+ SEARCHPGM *pgm;
+
+ pgm = mail_newsearchpgm();
+ switch(crit){
+ case 'n' :
+ if(not){
+ SEARCHPGM *notpgm;
+
+ /* this is the same as seen or deleted or answered */
+ pgm->not = mail_newsearchpgmlist();
+ notpgm = pgm->not->pgm = mail_newsearchpgm();
+ notpgm->unseen = notpgm->undeleted = notpgm->unanswered = 1;
+ }
+ else
+ pgm->unseen = pgm->undeleted = pgm->unanswered = 1;
+
+ break;
+
+ case 'd' :
+ if(not)
+ pgm->undeleted = 1;
+ else
+ pgm->deleted = 1;
+
+ break;
+
+ case 'r' :
+ if(not)
+ pgm->old = 1;
+ else
+ pgm->recent = 1;
+
+ break;
+
+ case 'u' :
+ if(not)
+ pgm->seen = 1;
+ else
+ pgm->unseen = 1;
+
+ break;
+
+ case 'a':
+ /*
+ * Not a true "not", we are implicitly only interested in undeleted.
+ */
+ if(not)
+ pgm->unanswered = pgm->undeleted = 1;
+ else
+ pgm->answered = pgm->undeleted = 1;
+ break;
+
+ case 'f':
+ {
+ STRINGLIST **slpp;
+
+ for(slpp = (not) ? &pgm->unkeyword : &pgm->keyword;
+ *slpp;
+ slpp = &(*slpp)->next)
+ ;
+
+ *slpp = mail_newstringlist();
+ (*slpp)->text.data = (unsigned char *) cpystr(FORWARDED_FLAG);
+ (*slpp)->text.size = (unsigned long) strlen(FORWARDED_FLAG);
+ }
+
+ break;
+
+ case '*' :
+ if(not)
+ pgm->unflagged = 1;
+ else
+ pgm->flagged = 1;
+
+ break;
+
+ default :
+ return(1);
+ break;
+ }
+
+ pgm->msgno = (limitsrch ? *limitsrch : NULL);
+ pine_mail_search_full(stream, NULL, pgm, SE_NOPREFETCH | SE_FREE);
+ /* we know this was freed in mail_search, let caller know */
+ if(limitsrch)
+ *limitsrch = NULL;
+
+ return(0);
+}
+
+
+/*
+ * Get the user name from the mailbox portion of an address.
+ *
+ * Args: mailbox -- the mailbox portion of an address (lhs of address)
+ * target -- a buffer to put the result in
+ * len -- length of the target buffer
+ *
+ * Returns the left most portion up to the first '%', ':' or '@',
+ * and to the right of any '!' (as if c-client would give us such a mailbox).
+ * Returns NULL if it can't find a username to point to.
+ */
+char *
+get_uname(char *mailbox, char *target, int len)
+{
+ int i, start, end;
+
+ if(!mailbox || !*mailbox)
+ return(NULL);
+
+ end = strlen(mailbox) - 1;
+ for(start = end; start > -1 && mailbox[start] != '!'; start--)
+ if(strindex("%:@", mailbox[start]))
+ end = start - 1;
+
+ start++; /* compensate for either case above */
+
+ for(i = start; i <= end && (i-start) < (len-1); i++) /* copy name */
+ target[i-start] = isupper((unsigned char)mailbox[i])
+ ? tolower((unsigned char)mailbox[i])
+ : mailbox[i];
+
+ target[i-start] = '\0'; /* tie it off */
+
+ return(*target ? target : NULL);
+}
diff --git a/pith/mailcmd.h b/pith/mailcmd.h
new file mode 100644
index 00000000..9e99c6f3
--- /dev/null
+++ b/pith/mailcmd.h
@@ -0,0 +1,77 @@
+/*
+ * $Id: mailcmd.h 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_MAILCMD_INCLUDED
+#define PITH_MAILCMD_INCLUDED
+
+
+#include "../pith/state.h"
+#include "../pith/msgno.h"
+#include "../pith/context.h"
+#include "../pith/indxtype.h"
+
+
+#define EC_NONE 0x00 /* flags modifying expunge_and_close */
+#define EC_NO_CLOSE 0x01 /* don't close at end */
+
+
+/*
+ * mailcmd options
+ */
+#define MCMD_NONE 0
+#define MCMD_AGG 0x01
+#define MCMD_AGG_2 0x02
+#define MCMD_SILENT 0x04
+
+
+/* do_broach_folder flags */
+#define DB_NOVISIT 0x01 /* this is a preopen, not a real visit */
+#define DB_FROMTAB 0x02 /* opening because of TAB command */
+#define DB_INBOXWOCNTXT 0x04 /* interpret inbox as one true inbox */
+
+
+/*
+ * generic "is aggregate message command?" test
+ */
+#define MCMD_ISAGG(O) ((O) & (MCMD_AGG | MCMD_AGG_2))
+
+
+/* exported protoypes */
+int any_messages(MSGNO_S *, char *, char *);
+void bogus_utf8_command(char *, char *);
+int can_set_flag(struct pine *, char *, int);
+void cmd_cancelled(char *);
+void cmd_quota(struct pine *);
+int cmd_delete(struct pine *, MSGNO_S *, int, char *(*)(struct pine *, MSGNO_S *));
+int cmd_undelete(struct pine *, MSGNO_S *, int);
+int cmd_expunge_work(MAILSTREAM *, MSGNO_S *);
+CONTEXT_S *broach_get_folder(CONTEXT_S *, int *, char **);
+int do_broach_folder(char *, CONTEXT_S *, MAILSTREAM **, unsigned long);
+void expunge_and_close(MAILSTREAM *, char **, unsigned long);
+void agg_select_all(MAILSTREAM *, MSGNO_S *, long *, int);
+char *move_read_msgs(MAILSTREAM *, char *, char *, size_t, long);
+char *move_read_incoming(MAILSTREAM *, CONTEXT_S *, char *, char **, char *, size_t);
+void cross_delete_crossposts(MAILSTREAM *);
+long zoom_index(struct pine *, MAILSTREAM *, MSGNO_S *, int);
+int unzoom_index(struct pine *, MAILSTREAM *, MSGNO_S *);
+int agg_text_select(MAILSTREAM *, MSGNO_S *, char, char *, int, int, char *,
+ char *, SEARCHSET **);
+int agg_flag_select(MAILSTREAM *, int, int, SEARCHSET **);
+char *get_uname(char *, char *, int);
+int expand_foldername(char *, size_t);
+
+
+#endif /* PITH_MAILCMD_INCLUDED */
diff --git a/pith/mailindx.c b/pith/mailindx.c
new file mode 100644
index 00000000..f3b68bfb
--- /dev/null
+++ b/pith/mailindx.c
@@ -0,0 +1,6434 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: mailindx.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
+#endif
+
+/* ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/mailindx.h"
+#include "../pith/mailview.h"
+#include "../pith/flag.h"
+#include "../pith/icache.h"
+#include "../pith/msgno.h"
+#include "../pith/thread.h"
+#include "../pith/strlst.h"
+#include "../pith/status.h"
+#include "../pith/mailcmd.h"
+#include "../pith/search.h"
+#include "../pith/charset.h"
+#include "../pith/reply.h"
+#include "../pith/bldaddr.h"
+#include "../pith/addrstring.h"
+#include "../pith/news.h"
+#include "../pith/util.h"
+#include "../pith/pattern.h"
+#include "../pith/sequence.h"
+#include "../pith/color.h"
+#include "../pith/stream.h"
+#include "../pith/string.h"
+#include "../pith/send.h"
+#include "../pith/options.h"
+#include "../pith/ablookup.h"
+#ifdef _WINDOWS
+#include "../pico/osdep/mswin.h"
+#endif
+
+/*
+ * pointers to formatting functions
+ */
+ICE_S *(*format_index_line)(INDEXDATA_S *);
+void (*setup_header_widths)(MAILSTREAM *);
+
+/*
+ * pointer to optional load_overview functionality
+ */
+void (*pith_opt_paint_index_hline)(MAILSTREAM *, long, ICE_S *);
+
+/*
+ * pointer to hook for saving index format state
+ */
+void (*pith_opt_save_index_state)(int);
+
+/*
+ * hook to allow caller to insert cue that indicates a condensed
+ * thread relationship cue
+ */
+int (*pith_opt_condense_thread_cue)(PINETHRD_S *, ICE_S *, char **, size_t *, int, int);
+int (*pith_opt_truncate_sfstr)(void);
+
+
+/*
+ * Internal prototypes
+ */
+void setup_for_thread_index_screen(void);
+ICE_S *format_index_index_line(INDEXDATA_S *);
+ICE_S *format_thread_index_line(INDEXDATA_S *);
+int set_index_addr(INDEXDATA_S *, char *, ADDRESS *, char *, int, char *);
+int ctype_is_fixed_length(IndexColType);
+void setup_index_header_widths(MAILSTREAM *);
+void setup_thread_header_widths(MAILSTREAM *);
+int parse_index_format(char *, INDEX_COL_S **);
+int index_in_overview(MAILSTREAM *);
+ADDRESS *fetch_from(INDEXDATA_S *);
+ADDRESS *fetch_sender(INDEXDATA_S *);
+char *fetch_newsgroups(INDEXDATA_S *);
+char *fetch_subject(INDEXDATA_S *);
+char *fetch_date(INDEXDATA_S *);
+long fetch_size(INDEXDATA_S *);
+BODY *fetch_body(INDEXDATA_S *);
+char *fetch_firsttext(INDEXDATA_S *idata, int);
+char *fetch_header(INDEXDATA_S *idata, char *hdrname);
+void subj_str(INDEXDATA_S *, char *, size_t, SubjKW, int, ICE_S *);
+void key_str(INDEXDATA_S *, SubjKW, ICE_S *);
+void header_str(INDEXDATA_S *, HEADER_TOK_S *, ICE_S *);
+void prio_str(INDEXDATA_S *, IndexColType, ICE_S *);
+void from_str(IndexColType, INDEXDATA_S *, char *, size_t, ICE_S *);
+int day_of_week(struct date *);
+int day_of_year(struct date *);
+unsigned long ice_hash(ICE_S *);
+char *left_adjust(int);
+char *right_adjust(int);
+char *format_str(int, int);
+char *copy_format_str(int, int, char *, int);
+void set_print_format(IELEM_S *, int, int);
+void set_ielem_widths_in_field(IFIELD_S *);
+
+
+#define BIGWIDTH 2047
+
+
+/*----------------------------------------------------------------------
+ Initialize the index_disp_format array in ps_global from this
+ format string.
+
+ Args: format -- the string containing the format tokens
+ answer -- put the answer here, free first if there was a previous
+ value here
+ ----*/
+void
+init_index_format(char *format, INDEX_COL_S **answer)
+{
+ char *p;
+ int i, w, monabb_width = 0, column = 0;
+
+ /*
+ * Record the fact that SCORE appears in some index format. This
+ * is a heavy-handed approach. It will stick at 1 if any format ever
+ * contains score during this session. This is ok since it will just
+ * cause recalculation if wrong and these things rarely change much.
+ */
+ if(!ps_global->a_format_contains_score && format
+ && strstr(format, "SCORE")){
+ ps_global->a_format_contains_score = 1;
+ /* recalculate need for scores */
+ scores_are_used(SCOREUSE_INVALID);
+ }
+
+ set_need_format_setup(ps_global->mail_stream);
+ /* if custom format is specified, try it, else go with default */
+ if(!(format && *format && parse_index_format(format, answer))){
+ static INDEX_COL_S answer_default[] = {
+ {iStatus, Fixed, 3},
+ {iMessNo, WeCalculate},
+ {iSDateTime24, WeCalculate},
+ {iFromTo, Percent, 33}, /* percent of rest */
+ {iSizeNarrow, WeCalculate},
+ {iSubjKey, Percent, 67},
+ {iNothing}
+ };
+
+ if(*answer)
+ free_index_format(answer);
+
+ *answer = (INDEX_COL_S *)fs_get(sizeof(answer_default));
+ memcpy(*answer, answer_default, sizeof(answer_default));
+ }
+
+ /*
+ * Test to see how long the month abbreviations are.
+ */
+ for(i = 1; i <= 12; i++){
+ p = month_abbrev_locale(i);
+ monabb_width = MAX(utf8_width(p), monabb_width);
+ }
+
+ monabb_width = MIN(MAX(2, monabb_width), 5);
+
+ /*
+ * Fill in req_width's for WeCalculate items.
+ */
+ for(column = 0; (*answer)[column].ctype != iNothing; column++){
+
+ /* don't use strftime if we're not trying to use the LC_TIME stuff */
+ if(F_ON(F_DISABLE_INDEX_LOCALE_DATES, ps_global)){
+ switch((*answer)[column].ctype){
+ case iSDate:
+ (*answer)[column].ctype = iS1Date;
+ break;
+ case iSDateTime:
+ (*answer)[column].ctype = iSDateTimeS1;
+ break;
+ case iSDateTime24:
+ (*answer)[column].ctype = iSDateTimeS124;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if((*answer)[column].wtype == WeCalculate){
+ switch((*answer)[column].ctype){
+ case iPrio:
+ case iPrioBang:
+ case iAtt:
+ (*answer)[column].req_width = 1;
+ break;
+ case iYear2Digit:
+ case iDay:
+ case iMon:
+ case iDay2Digit:
+ case iMon2Digit:
+ case iArrow:
+ case iKeyInit:
+ (*answer)[column].req_width = 2;
+ break;
+ case iStatus:
+ case iMessNo:
+ case iInit:
+ (*answer)[column].req_width = 3;
+ break;
+ case iYear:
+ case iDayOrdinal:
+ case iSIStatus:
+ (*answer)[column].req_width = 4;
+ break;
+ case iTime24:
+ case iTimezone:
+ case iSizeNarrow:
+ case iKey:
+ (*answer)[column].req_width = 5;
+ break;
+ case iFStatus:
+ case iIStatus:
+ case iScore:
+ (*answer)[column].req_width = 6;
+ break;
+ case iTime12:
+ case iSTime:
+ case iKSize:
+ case iSize:
+ case iPrioAlpha:
+ (*answer)[column].req_width = 7;
+ break;
+ case iS1Date:
+ case iS2Date:
+ case iS3Date:
+ case iS4Date:
+ case iDateIsoS:
+ case iSizeComma:
+ (*answer)[column].req_width = 8;
+ break;
+ case iMonAbb:
+ (*answer)[column].req_width = monabb_width;
+ (*answer)[column].monabb_width = monabb_width;
+ break;
+ case iDayOfWeekAbb:
+ {
+ w = 0;
+
+ /*
+ * Test to see how long it is.
+ */
+ for(i = 0; i < 7; i++){
+ p = day_abbrev_locale(i);
+ w = MAX(utf8_width(p), w);
+ }
+
+ (*answer)[column].req_width = MIN(MAX(2, w), 5);
+ }
+ break;
+ case iDate:
+ (*answer)[column].req_width = monabb_width + 3;
+ (*answer)[column].monabb_width = monabb_width;
+ break;
+ case iMonLong:
+ {
+ w = 0;
+
+ /*
+ * Test to see how long it is.
+ */
+ for(i = 1; i <= 12; i++){
+ p = month_name_locale(i);
+ w = MAX(utf8_width(p), w);
+ }
+
+ (*answer)[column].req_width = MIN(MAX(3, w), 12);
+ }
+ break;
+ case iDayOfWeek:
+ {
+ w = 0;
+
+ for(i = 0; i < 7; i++){
+ p = day_name_locale(i);
+ w = MAX(utf8_width(p), w);
+ }
+
+ (*answer)[column].req_width = MIN(MAX(3, w), 12);
+ }
+ break;
+ case iSDate:
+ case iSDateTime:
+ case iSDateTime24:
+ case iPrefDate:
+ case iPrefTime:
+ case iPrefDateTime:
+ {
+ /*
+ * Format a date to see how long it is.
+ * Make it as least as long as "Yesterday".
+ * We should really use the width of the longest
+ * of the translated yesterdays and friends but...
+ */
+ struct tm tm;
+ int len = 20;
+ char ss[100];
+
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_year = 106;
+ tm.tm_mon = 11;
+ tm.tm_mday = 31;
+ tm.tm_hour = 3;
+ tm.tm_min = 23;
+ tm.tm_wday = 3;
+ switch((*answer)[column].ctype){
+ case iPrefTime:
+ our_strftime(ss, sizeof(ss), "%X", &tm);
+ break;
+ case iPrefDateTime:
+ our_strftime(ss, sizeof(ss), "%c", &tm);
+ len = 32;
+ break;
+ default:
+ our_strftime(ss, sizeof(ss), "%x", &tm);
+ break;
+ }
+ (*answer)[column].req_width = MIN(MAX(9, utf8_width(ss)), len);
+ }
+
+ (*answer)[column].monabb_width = monabb_width;
+ break;
+
+ case iDescripSize:
+ case iSDateIsoS:
+ case iSDateS1: case iSDateS2: case iSDateS3: case iSDateS4:
+ case iSDateTimeIsoS:
+ case iSDateTimeS1: case iSDateTimeS2: case iSDateTimeS3: case iSDateTimeS4:
+ case iSDateTimeIsoS24:
+ case iSDateTimeS124: case iSDateTimeS224: case iSDateTimeS324: case iSDateTimeS424:
+ /*
+ * These SDates are 8 wide but they need to be 9 for "Yesterday".
+ */
+ (*answer)[column].req_width = 9;
+ break;
+ case iDateIso:
+ case iSDateIso: case iSDateTimeIso: case iSDateTimeIso24:
+ (*answer)[column].req_width = 10;
+ break;
+ case iLDate:
+ (*answer)[column].req_width = 12;
+ (*answer)[column].monabb_width = monabb_width;
+ break;
+ case iRDate:
+ (*answer)[column].req_width = 16;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ calc_extra_hdrs();
+ if(get_extra_hdrs())
+ (void) mail_parameters(NULL, SET_IMAPEXTRAHEADERS,
+ (void *) get_extra_hdrs());
+}
+
+
+void
+reset_index_format(void)
+{
+ long rflags = ROLE_DO_OTHER;
+ PAT_STATE pstate;
+ PAT_S *pat;
+ int we_set_it = 0;
+
+ if(ps_global->mail_stream && nonempty_patterns(rflags, &pstate)){
+ for(pat = first_pattern(&pstate); pat; pat = next_pattern(&pstate)){
+ if(match_pattern(pat->patgrp, ps_global->mail_stream, NULL,
+ NULL, NULL, SE_NOSERVER|SE_NOPREFETCH))
+ break;
+ }
+
+ if(pat && pat->action && !pat->action->bogus
+ && pat->action->index_format){
+ we_set_it++;
+ init_index_format(pat->action->index_format,
+ &ps_global->index_disp_format);
+ }
+ }
+
+ if(!we_set_it)
+ init_index_format(ps_global->VAR_INDEX_FORMAT,
+ &ps_global->index_disp_format);
+}
+
+
+void
+free_index_format(INDEX_COL_S **disp_format)
+{
+ INDEX_COL_S *cdesc = NULL;
+
+ if(disp_format && *disp_format){
+ for(cdesc = (*disp_format); cdesc->ctype != iNothing; cdesc++)
+ if(cdesc->hdrtok)
+ free_hdrtok(&cdesc->hdrtok);
+
+ fs_give((void **) disp_format);
+ }
+}
+
+
+HEADER_TOK_S *
+new_hdrtok(char *hdrname)
+{
+ HEADER_TOK_S *hdrtok;
+
+ hdrtok = (HEADER_TOK_S *) fs_get(sizeof(HEADER_TOK_S));
+ memset(hdrtok, 0, sizeof(HEADER_TOK_S));
+ hdrtok->hdrname = hdrname ? cpystr(hdrname) : NULL;
+ hdrtok->fieldnum = 0;
+ hdrtok->adjustment = Left;
+ hdrtok->fieldsepcnt = 1;
+ hdrtok->fieldseps = cpystr(" ");
+
+ return(hdrtok);
+}
+
+
+void
+free_hdrtok(HEADER_TOK_S **hdrtok)
+{
+ if(hdrtok && *hdrtok){
+ if((*hdrtok)->hdrname)
+ fs_give((void **) &(*hdrtok)->hdrname);
+
+ if((*hdrtok)->fieldseps)
+ fs_give((void **) &(*hdrtok)->fieldseps);
+
+ fs_give((void **) hdrtok);
+ }
+}
+
+
+/* popular ones first to make it slightly faster */
+static INDEX_PARSE_T itokens[] = {
+ {"STATUS", iStatus, FOR_INDEX},
+ {"MSGNO", iMessNo, FOR_INDEX},
+ {"DATE", iDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"FROMORTO", iFromTo, FOR_INDEX},
+ {"FROMORTONOTNEWS", iFromToNotNews, FOR_INDEX},
+ {"SIZE", iSize, FOR_INDEX},
+ {"SIZECOMMA", iSizeComma, FOR_INDEX},
+ {"SIZENARROW", iSizeNarrow, FOR_INDEX},
+ {"KSIZE", iKSize, FOR_INDEX},
+ {"SUBJECT", iSubject, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"FULLSTATUS", iFStatus, FOR_INDEX},
+ {"IMAPSTATUS", iIStatus, FOR_INDEX},
+ {"SHORTIMAPSTATUS", iSIStatus, FOR_INDEX},
+ {"SUBJKEY", iSubjKey, FOR_INDEX},
+ {"SUBJKEYINIT", iSubjKeyInit, FOR_INDEX},
+ {"SUBJECTTEXT", iSubjectText, FOR_INDEX},
+ {"SUBJKEYTEXT", iSubjKeyText, FOR_INDEX},
+ {"SUBJKEYINITTEXT", iSubjKeyInitText, FOR_INDEX},
+ {"OPENINGTEXT", iOpeningText, FOR_INDEX},
+ {"OPENINGTEXTNQ", iOpeningTextNQ, FOR_INDEX},
+ {"KEY", iKey, FOR_INDEX},
+ {"KEYINIT", iKeyInit, FOR_INDEX},
+ {"DESCRIPSIZE", iDescripSize, FOR_INDEX},
+ {"ATT", iAtt, FOR_INDEX},
+ {"SCORE", iScore, FOR_INDEX},
+ {"PRIORITY", iPrio, FOR_INDEX},
+ {"PRIORITYALPHA", iPrioAlpha, FOR_INDEX},
+ {"PRIORITY!", iPrioBang, FOR_INDEX},
+ {"LONGDATE", iLDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SHORTDATE1", iS1Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SHORTDATE2", iS2Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SHORTDATE3", iS3Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SHORTDATE4", iS4Date, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"DATEISO", iDateIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SHORTDATEISO", iDateIsoS, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATE", iSDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTTIME", iSTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATEISO", iSDateIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATESHORTISO",iSDateIsoS, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATES1", iSDateS1, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATES2", iSDateS2, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATES3", iSDateS3, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATES4", iSDateS4, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATETIME", iSDateTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATETIMEISO",iSDateTimeIso, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATETIMESHORTISO",iSDateTimeIsoS,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATETIMES1", iSDateTimeS1, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATETIMES2", iSDateTimeS2, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATETIMES3", iSDateTimeS3, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATETIMES4", iSDateTimeS4, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATETIME24", iSDateTime24, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATETIMEISO24", iSDateTimeIso24,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATETIMESHORTISO24",iSDateTimeIsoS24,FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATETIMES124", iSDateTimeS124, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATETIMES224", iSDateTimeS224, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATETIMES324", iSDateTimeS324, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SMARTDATETIMES424", iSDateTimeS424, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"TIME24", iTime24, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"TIME12", iTime12, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"TIMEZONE", iTimezone, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"MONTHABBREV", iMonAbb, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"DAYOFWEEKABBREV", iDayOfWeekAbb, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"DAYOFWEEK", iDayOfWeek, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"FROM", iFrom, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"TO", iTo, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"SENDER", iSender, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"CC", iCc, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"RECIPS", iRecips, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"NEWS", iNews, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"TOANDNEWS", iToAndNews, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"NEWSANDTO", iNewsAndTo, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"RECIPSANDNEWS", iRecipsAndNews, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"NEWSANDRECIPS", iNewsAndRecips, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"MSGID", iMsgID, FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"CURNEWS", iCurNews, FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"DAYDATE", iRDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"PREFDATE", iPrefDate, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"PREFTIME", iPrefTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"PREFDATETIME", iPrefDateTime, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"DAY", iDay, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"DAYORDINAL", iDayOrdinal, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"DAY2DIGIT", iDay2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"MONTHLONG", iMonLong, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"MONTH", iMon, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"MONTH2DIGIT", iMon2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"YEAR", iYear, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"YEAR2DIGIT", iYear2Digit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"ADDRESS", iAddress, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"MAILBOX", iMailbox, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"ROLENICK", iRoleNick, FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"INIT", iInit, FOR_INDEX|FOR_REPLY_INTRO|FOR_TEMPLATE},
+ {"CURDATE", iCurDate, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURDATEISO", iCurDateIso, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURDATEISOS", iCurDateIsoS, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURTIME24", iCurTime24, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURTIME12", iCurTime12, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURDAY", iCurDay, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURDAY2DIGIT", iCurDay2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURDAYOFWEEK", iCurDayOfWeek, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURDAYOFWEEKABBREV", iCurDayOfWeekAbb,
+ FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURMONTH", iCurMon, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURMONTH2DIGIT", iCurMon2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURMONTHLONG", iCurMonLong, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURMONTHABBREV", iCurMonAbb, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURYEAR", iCurYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURYEAR2DIGIT", iCurYear2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURPREFDATE", iCurPrefDate, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURPREFTIME", iCurPrefTime, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"CURPREFDATETIME", iCurPrefDateTime,
+ FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"LASTMONTH", iLstMon, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"LASTMONTH2DIGIT", iLstMon2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"LASTMONTHLONG", iLstMonLong, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"LASTMONTHABBREV", iLstMonAbb, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"LASTMONTHYEAR", iLstMonYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"LASTMONTHYEAR2DIGIT", iLstMonYear2Digit,
+ FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"LASTYEAR", iLstYear, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"LASTYEAR2DIGIT", iLstYear2Digit, FOR_REPLY_INTRO|FOR_TEMPLATE|FOR_FILT},
+ {"HEADER", iHeader, FOR_INDEX},
+ {"TEXT", iText, FOR_INDEX},
+ {"ARROW", iArrow, FOR_INDEX},
+ {"NEWLINE", iNewLine, FOR_REPLY_INTRO},
+ {"CURSORPOS", iCursorPos, FOR_TEMPLATE},
+ {NULL, iNothing, FOR_NOTHING}
+};
+
+INDEX_PARSE_T *
+itoken(int i)
+{
+ return((i < sizeof(itokens) && itokens[i].name) ? &itokens[i] : NULL);
+}
+
+
+/*
+ * Args txt -- The token being checked begins at the beginning
+ * of txt. The end of the token is delimited by a null, or
+ * white space, or an underscore if DELIM_USCORE is set,
+ * or a left paren if DELIM_PAREN is set.
+ * flags -- Flags contains the what_for value, and DELIM_ values.
+ *
+ * Returns A ptr to an INDEX_PARSE_T from itokens above, else NULL.
+ */
+INDEX_PARSE_T *
+itoktype(char *txt, int flags)
+{
+ INDEX_PARSE_T *pt;
+ char token[100 + 1];
+ char *v, *w;
+
+ /*
+ * Separate a copy of the possible token out of txt.
+ */
+ v = txt;
+ w = token;
+ while(w < token+sizeof(token)-1 &&
+ *v &&
+ !(!(*v & 0x80) && isspace((unsigned char)*v)) &&
+ !(flags & DELIM_USCORE && *v == '_') &&
+ !(flags & DELIM_PAREN && *v == '(') &&
+ !(flags & DELIM_COLON && *v == ':'))
+ *w++ = *v++;
+
+ *w = '\0';
+
+ for(pt = itokens; pt->name; pt++)
+ if(pt->what_for & flags && !strucmp(pt->name, token))
+ return(pt);
+
+ return(NULL);
+}
+
+
+int
+parse_index_format(char *format_str, INDEX_COL_S **answer)
+{
+ int i, column = 0;
+ char *p, *q;
+ INDEX_PARSE_T *pt;
+ INDEX_COL_S cdesc[200]; /* plenty of temp storage for answer */
+
+ memset((void *)cdesc, 0, sizeof(cdesc));
+
+ p = format_str;
+ while(p && *p && column < 200-1){
+ /* skip leading white space for next word */
+ p = skip_white_space(p);
+ pt = itoktype(p, FOR_INDEX | DELIM_PAREN | DELIM_COLON);
+
+ /* ignore unrecognized word */
+ if(!pt){
+ for(q = p; *p && !isspace((unsigned char)*p); p++)
+ ;
+
+ if(*p)
+ *p++ = '\0';
+
+ dprint((1,
+ "parse_index_format: unrecognized token: %s\n",
+ q ? q : "?"));
+ q_status_message1(SM_ORDER | SM_DING, 0, 3,
+ _("Unrecognized word in index-format: %s"), q);
+ continue;
+ }
+
+ cdesc[column].ctype = pt->ctype;
+
+ if(pt->ctype == iHeader || pt->ctype == iText){
+ /*
+ * iHeader field has special syntax.
+ *
+ * HEADER:hdrname(width,fieldnum,field_separators,L_or_R)
+ *
+ * where width is the regular width or percentage width or
+ * left out for default width, fieldnum defaults to 0 for
+ * whole thing, 1 for first field, ...
+ * and field_separators is a list of characters which separate
+ * the fields. The whole parenthesized part is optional. If used
+ * the arguments can be dropped from the right, so
+ *
+ * HEADER:hdrname or
+ * HEADER:hdrname(10) or
+ * HEADER:hdrname(10%) or
+ * HEADER:hdrname(10,2) or
+ * HEADER:hdrname(,2) or
+ * HEADER:hdrname(10,2, ) or
+ * HEADER:hdrname(10,2,\,:) or
+ * HEADER:hdrname(10,2,\,:,R)
+ *
+ * iText field uses the hdrtok field for convenience. It has syntax
+ *
+ * TEXT:text or
+ * TEXT:text(10) or
+ * TEXT:text(10%)
+ *
+ * and the literal text goes into the index line. It is also special
+ * because there is no 1 column space after this field.
+ */
+
+ /* skip over name */
+ p += strlen(pt->name);
+
+ /* look for header name */
+ if(*p == ':'){
+ char *w, hdrname[200];
+
+ hdrname[0] = '\0';
+ w = hdrname;
+ p++;
+ if(*p == '\"'){ /* quoted name */
+ p++;
+ while(w < hdrname + sizeof(hdrname)-1 && *p != '\"'){
+ if(*p == '\\')
+ p++;
+
+ *w++ = *p++;
+ }
+
+ *w = '\0';
+ if(*p == '\"')
+ p++;
+ }
+ else{
+ while(w < hdrname + sizeof(hdrname)-1 &&
+ !(!(*p & 0x80) && isspace((unsigned char)*p)) &&
+ *p != '(')
+ *w++ = *p++;
+
+ *w = '\0';
+ }
+
+ if(hdrname[0]){
+ cdesc[column].hdrtok = new_hdrtok(hdrname);
+ }
+ else{
+ if(pt->ctype == iHeader){
+ dprint((1, "parse_index_token: HEADER should be followed by :hdrname\n"));
+ q_status_message(SM_ORDER | SM_DING, 0, 3, "index token HEADER should be followed by :hdrname");
+ }
+ else{
+ dprint((1, "parse_index_token: TEXT should be followed by :text\n"));
+ q_status_message(SM_ORDER | SM_DING, 0, 3, "index token TEXT should be followed by :text");
+ }
+ }
+ }
+ else{
+ if(pt->ctype == iHeader){
+ dprint((1, "parse_index_token: HEADER should be followed by :hdrname, not %s\n", p));
+ q_status_message(SM_ORDER | SM_DING, 0, 3, "index token HEADER should be followed by :hdrname");
+ }
+ else{
+ dprint((1, "parse_index_token: TEXT should be followed by :text, not %s\n", p));
+ q_status_message(SM_ORDER | SM_DING, 0, 3, "index token TEXT should be followed by :text");
+ }
+
+ /* skip over rest of bogus config */
+ while(!(!(*p & 0x80) && isspace((unsigned char)*p)) && *p != '(')
+ p++;
+ }
+ }
+ else{
+ /* skip over name and look for parens */
+ p += strlen(pt->name);
+ }
+
+ if(*p == '('){
+ p++;
+ q = p;
+ while(p && *p && isdigit((unsigned char) *p))
+ p++;
+
+ if(pt->ctype == iHeader){
+ /* first argument is width or width percentage, like for others */
+ if(p && *p && (*p == ')' || *p == ',')){
+ if(p > q){
+ cdesc[column].wtype = Fixed;
+ cdesc[column].req_width = atoi(q);
+ }
+ else{
+ cdesc[column].wtype = WeCalculate;
+ cdesc[column].req_width = 0;
+ }
+ }
+ else if(p && *p && *p == '%' && p > q){
+ cdesc[column].wtype = Percent;
+ cdesc[column].req_width = atoi(q);
+ p++;
+ }
+ else{
+ cdesc[column].wtype = WeCalculate;
+ cdesc[column].req_width = 0;
+ }
+
+ /* optional 2nd argument is field number, 0 whole thing, 1, 2, ... */
+ if(p && *p && *p == ','){
+ p++;
+ /* no space allowed between arguments */
+ if(*p && isdigit((unsigned char) *p)){
+ q = p;
+ while(*p && isdigit((unsigned char) *p))
+ p++;
+
+ cdesc[column].hdrtok->fieldnum = atoi(q);
+
+ /*
+ * Optional 3rd argument is field separators.
+ * Comma is \, and backslash is \\.
+ */
+ if(*p == ','){
+ int j;
+
+ p++;
+ /* don't use default */
+ if(*p && *p != ')' && *p != ',' && cdesc[column].hdrtok->fieldseps)
+ cdesc[column].hdrtok->fieldseps[0] = '\0';
+
+ j = 0;
+ if(*p == '\"' && strchr(p+1, '\"')){
+ p++;
+ while(*p && *p != ')' && *p != '\"' && *p != ','){
+ if(cdesc[column].hdrtok->fieldseps)
+ fs_resize((void **) &cdesc[column].hdrtok->fieldseps, j+2);
+
+ if(*p == '\\' && *(p+1))
+ p++;
+
+ if(cdesc[column].hdrtok->fieldseps){
+ cdesc[column].hdrtok->fieldseps[j++] = *p++;
+ cdesc[column].hdrtok->fieldseps[j] = '\0';
+ cdesc[column].hdrtok->fieldsepcnt = j;
+ }
+ }
+
+ if(*p == '\"')
+ p++;
+ }
+ else{
+ while(*p && *p != ')' && *p != ','){
+ if(cdesc[column].hdrtok->fieldseps)
+ fs_resize((void **) &cdesc[column].hdrtok->fieldseps, j+2);
+ if(*p == '\\' && *(p+1))
+ p++;
+
+ if(cdesc[column].hdrtok->fieldseps){
+ cdesc[column].hdrtok->fieldseps[j++] = *p++;
+ cdesc[column].hdrtok->fieldseps[j] = '\0';
+ cdesc[column].hdrtok->fieldsepcnt = j;
+ }
+ }
+ }
+
+ /* optional 4th argument, left or right adjust */
+ if(*p == ','){
+ p++;
+ if(*p == 'L' || *p == 'l')
+ cdesc[column].hdrtok->adjustment = Left;
+ else if(*p == 'R' || *p == 'r')
+ cdesc[column].hdrtok->adjustment = Right;
+ else{
+ dprint((1, "parse_index_token: HEADER 4th argument should be L or R, not\n", *p ? p : "<null>"));
+ q_status_message(SM_ORDER | SM_DING, 0, 3, "HEADER 4th argument should be L or R");
+ }
+ }
+ }
+ }
+ else{
+ dprint((1, "parse_index_token: HEADER 2nd argument should be field number, not\n", *p ? p : "<null>"));
+ q_status_message(SM_ORDER | SM_DING, 0, 3, "HEADER 2nd argument should be field number, a non-negative digit");
+ }
+ }
+ }
+ else{
+ if(p && *p && *p == ')' && p > q){
+ cdesc[column].wtype = Fixed;
+ cdesc[column].req_width = atoi(q);
+ }
+ else if(p && *p && *p == '%' && p > q){
+ cdesc[column].wtype = Percent;
+ cdesc[column].req_width = atoi(q);
+ }
+ else{
+ cdesc[column].wtype = WeCalculate;
+ cdesc[column].req_width = 0;
+ }
+ }
+ }
+ else{
+ /* if they left out width for iText we can figure it out */
+ if(pt->ctype == iText && cdesc[column].hdrtok && cdesc[column].hdrtok->hdrname){
+ cdesc[column].wtype = Fixed;
+ cdesc[column].req_width = utf8_width(cdesc[column].hdrtok->hdrname);
+ }
+ else{
+ cdesc[column].wtype = WeCalculate;
+ cdesc[column].req_width = 0;
+ }
+ }
+
+ column++;
+ /* skip text at end of word */
+ while(p && *p && !isspace((unsigned char)*p))
+ p++;
+ }
+
+ /* if, after all that, we didn't find anything recognizable, bitch */
+ if(!column){
+ dprint((1, "Completely unrecognizable index-format\n"));
+ q_status_message(SM_ORDER | SM_DING, 0, 3,
+ _("Configured \"index-format\" unrecognizable. Using default."));
+ return(0);
+ }
+
+ /* Finish with Nothing column */
+ cdesc[column].ctype = iNothing;
+
+ /* free up old answer */
+ if(*answer)
+ free_index_format(answer);
+
+ /* allocate space for new answer */
+ *answer = (INDEX_COL_S *)fs_get((column+1)*sizeof(INDEX_COL_S));
+ memset((void *)(*answer), 0, (column+1)*sizeof(INDEX_COL_S));
+ /* copy answer to real place */
+ for(i = 0; i <= column; i++)
+ (*answer)[i] = cdesc[i];
+
+ return(1);
+}
+
+
+/*
+ * These types are basically fixed in width.
+ * The order is slightly significant. The ones towards the front of the
+ * list get space allocated sooner than the ones at the end of the list.
+ */
+static IndexColType fixed_ctypes[] = {
+ iMessNo, iStatus, iFStatus, iIStatus, iSIStatus,
+ iDate, iSDate, iSDateTime, iSDateTime24,
+ iSTime, iLDate,
+ iS1Date, iS2Date, iS3Date, iS4Date, iDateIso, iDateIsoS,
+ iSDateIso, iSDateIsoS,
+ iSDateS1, iSDateS2, iSDateS3, iSDateS4,
+ iSDateTimeIso, iSDateTimeIsoS,
+ iSDateTimeS1, iSDateTimeS2, iSDateTimeS3, iSDateTimeS4,
+ iSDateTimeIso24, iSDateTimeIsoS24,
+ iSDateTimeS124, iSDateTimeS224, iSDateTimeS324, iSDateTimeS424,
+ iSize, iSizeComma, iSizeNarrow, iKSize, iDescripSize,
+ iPrio, iPrioBang, iPrioAlpha, iInit,
+ iAtt, iTime24, iTime12, iTimezone, iMonAbb, iYear, iYear2Digit,
+ iDay2Digit, iMon2Digit, iDayOfWeekAbb, iScore, iMonLong, iDayOfWeek
+};
+
+
+int
+ctype_is_fixed_length(IndexColType ctype)
+{
+ int j;
+
+ for(j = 0; ; j++){
+ if(j >= sizeof(fixed_ctypes)/sizeof(*fixed_ctypes))
+ break;
+
+ if(ctype == fixed_ctypes[j])
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/*----------------------------------------------------------------------
+ Setup the widths of the various columns in the index display
+ ----*/
+void
+setup_index_header_widths(MAILSTREAM *stream)
+{
+ int colspace; /* for reserving space between columns */
+ int j, some_to_calculate;
+ int space_left, screen_width, fix;
+ int keep_going, tot_pct, was_sl;
+ long max_msgno;
+ WidthType wtype;
+ INDEX_COL_S *cdesc;
+
+ max_msgno = mn_get_total(ps_global->msgmap);
+
+ dprint((8, "=== setup_index_header_widths() ===\n"));
+
+ clear_icache_flags(stream);
+ screen_width = ps_global->ttyo->screen_cols;
+ space_left = screen_width;
+ some_to_calculate = 0;
+ colspace = -1;
+
+ /*
+ * Calculate how many fields there are so we know how many spaces
+ * between columns to reserve. Fill in Fixed widths now. Reserve
+ * special case WeCalculate with non-zero req_widths before doing
+ * Percent cases below.
+ */
+ for(cdesc = ps_global->index_disp_format;
+ cdesc->ctype != iNothing;
+ cdesc++){
+
+ if(cdesc->wtype == Fixed){
+ cdesc->width = cdesc->req_width;
+ if(cdesc->width > 0)
+ colspace++;
+ }
+ else if(cdesc->wtype == Percent){
+ cdesc->width = 0; /* calculated later */
+ colspace++;
+ }
+ else{ /* WeCalculate */
+ cdesc->width = cdesc->req_width; /* reserve this for now */
+ some_to_calculate++;
+ colspace++;
+ }
+
+ /* no space after iText */
+ if(cdesc->ctype == iText)
+ colspace--;
+
+ space_left -= cdesc->width;
+ }
+
+ colspace = MAX(colspace, 0);
+
+ space_left -= colspace; /* space between columns */
+
+ ps_global->display_keywords_in_subject = 0;
+ ps_global->display_keywordinits_in_subject = 0;
+
+ /*
+ * Set the actual lengths for the fixed width fields and set up
+ * the left or right adjustment for everything.
+ * There should be a case setting actual_length for all of the types
+ * in fixed_ctypes.
+ */
+ for(cdesc = ps_global->index_disp_format;
+ cdesc->ctype != iNothing;
+ cdesc++){
+
+ wtype = cdesc->wtype;
+
+ if(cdesc->ctype == iSubjKey || cdesc->ctype == iSubjKeyText)
+ ps_global->display_keywords_in_subject = 1;
+ else if(cdesc->ctype == iSubjKeyInit || cdesc->ctype == iSubjKeyInitText)
+ ps_global->display_keywordinits_in_subject = 1;
+
+ if(wtype == WeCalculate || wtype == Percent || cdesc->width != 0){
+
+ switch(cdesc->ctype){
+ case iSDate: case iSDateIso: case iSDateIsoS:
+ case iSDateS1: case iSDateS2: case iSDateS3: case iSDateS4:
+ case iSDateTime: case iSDateTimeIso: case iSDateTimeIsoS:
+ case iSDateTimeS1: case iSDateTimeS2: case iSDateTimeS3: case iSDateTimeS4:
+ case iSDateTime24: case iSDateTimeIso24: case iSDateTimeIsoS24:
+ case iSDateTimeS124: case iSDateTimeS224: case iSDateTimeS324: case iSDateTimeS424:
+ case iSTime:
+ set_format_includes_smartdate(stream);
+ break;
+
+ default:
+ break;
+ }
+
+ if(ctype_is_fixed_length(cdesc->ctype)){
+ switch(cdesc->ctype){
+ case iPrio:
+ case iPrioBang:
+ case iAtt:
+ cdesc->actual_length = 1;
+ cdesc->adjustment = Left;
+ break;
+
+ case iYear2Digit:
+ case iDay2Digit:
+ case iMon2Digit:
+ cdesc->actual_length = 2;
+ cdesc->adjustment = Left;
+ break;
+
+ case iArrow:
+ cdesc->actual_length = 2;
+ cdesc->adjustment = Right;
+ break;
+
+ case iStatus:
+ case iInit:
+ cdesc->actual_length = 3;
+ cdesc->adjustment = Left;
+ break;
+
+ case iMessNo:
+ set_format_includes_msgno(stream);
+ if(max_msgno < 1000)
+ cdesc->actual_length = 3;
+ else if(max_msgno < 10000)
+ cdesc->actual_length = 4;
+ else if(max_msgno < 100000)
+ cdesc->actual_length = 5;
+ else
+ cdesc->actual_length = 6;
+
+ cdesc->adjustment = Right;
+ break;
+
+ case iYear:
+ case iSIStatus:
+ cdesc->actual_length = 4;
+ cdesc->adjustment = Left;
+ break;
+
+ case iTime24:
+ case iTimezone:
+ cdesc->actual_length = 5;
+ cdesc->adjustment = Left;
+ break;
+
+ case iSizeNarrow:
+ cdesc->actual_length = 5;
+ cdesc->adjustment = Right;
+ break;
+
+ case iFStatus:
+ case iIStatus:
+ cdesc->actual_length = 6;
+ cdesc->adjustment = Left;
+ break;
+
+ case iScore:
+ cdesc->actual_length = 6;
+ cdesc->adjustment = Right;
+ break;
+
+ case iTime12:
+ case iSize:
+ case iKSize:
+ cdesc->actual_length = 7;
+ cdesc->adjustment = Right;
+ break;
+
+ case iSTime:
+ cdesc->actual_length = 7;
+ cdesc->adjustment = Left;
+ break;
+
+ case iPrioAlpha:
+ cdesc->actual_length = 7;
+ cdesc->adjustment = Left;
+ break;
+
+
+ case iS1Date:
+ case iS2Date:
+ case iS3Date:
+ case iS4Date:
+ case iDateIsoS:
+ cdesc->actual_length = 8;
+ cdesc->adjustment = Left;
+ break;
+
+ case iSizeComma:
+ cdesc->actual_length = 8;
+ cdesc->adjustment = Right;
+ break;
+
+ case iSDate:
+ case iSDateTime:
+ case iSDateTime24:
+ case iMonAbb:
+ case iDayOfWeekAbb:
+ case iDayOfWeek:
+ case iDate:
+ case iMonLong:
+ cdesc->actual_length = cdesc->req_width;
+ cdesc->adjustment = Left;
+ break;
+
+ case iSDateIsoS:
+ case iSDateS1: case iSDateS2: case iSDateS3: case iSDateS4:
+ case iSDateTimeIsoS:
+ case iSDateTimeIsoS24:
+ case iSDateTimeS1: case iSDateTimeS2: case iSDateTimeS3: case iSDateTimeS4:
+ case iSDateTimeS124: case iSDateTimeS224: case iSDateTimeS324: case iSDateTimeS424:
+ case iSDateIso: case iSDateTimeIso: case iSDateTimeIso24:
+ if(cdesc->ctype == iSDateIso
+ || cdesc->ctype == iSDateTimeIso
+ || cdesc->ctype == iSDateTimeIso24)
+ cdesc->actual_length = 10;
+ else
+ cdesc->actual_length = 9;
+
+ cdesc->adjustment = Left;
+ break;
+
+ case iDescripSize:
+ cdesc->actual_length = 9;
+ cdesc->adjustment = Right;
+ break;
+
+ case iDateIso:
+ cdesc->actual_length = 10;
+ cdesc->adjustment = Left;
+ break;
+
+ case iLDate:
+ cdesc->actual_length = 12;
+ cdesc->adjustment = Left;
+ break;
+
+ default:
+ panic("Unhandled fixed case in setup_index_header");
+ break;
+ }
+ }
+ else if(cdesc->ctype == iHeader)
+ cdesc->adjustment = cdesc->hdrtok ? cdesc->hdrtok->adjustment : Left;
+ else
+ cdesc->adjustment = Left;
+ }
+ }
+
+ if(ps_global->display_keywords_in_subject)
+ ps_global->display_keywordinits_in_subject = 0;
+
+ /* if have reserved unneeded space for size, give it back */
+ for(cdesc = ps_global->index_disp_format;
+ cdesc->ctype != iNothing;
+ cdesc++)
+ if(cdesc->ctype == iSize || cdesc->ctype == iKSize ||
+ cdesc->ctype == iSizeNarrow ||
+ cdesc->ctype == iSizeComma || cdesc->ctype == iDescripSize){
+ if(cdesc->actual_length == 0){
+ if((fix=cdesc->width) > 0){ /* had this reserved */
+ cdesc->width = 0;
+ space_left += fix;
+ }
+
+ space_left++; /* +1 for space between columns */
+ }
+ }
+
+ /*
+ * Calculate the field widths that are basically fixed in width.
+ * Do them in this order in case we don't have enough space to go around.
+ * The set of fixed_ctypes here is the same as the set where we
+ * set the actual_lengths above.
+ */
+ for(j = 0; space_left > 0 && some_to_calculate; j++){
+
+ if(j >= sizeof(fixed_ctypes)/sizeof(*fixed_ctypes))
+ break;
+
+ for(cdesc = ps_global->index_disp_format;
+ cdesc->ctype != iNothing && space_left > 0 && some_to_calculate;
+ cdesc++)
+ if(cdesc->ctype == fixed_ctypes[j] && cdesc->wtype == WeCalculate){
+ some_to_calculate--;
+ fix = MIN(cdesc->actual_length - cdesc->width, space_left);
+ cdesc->width += fix;
+ space_left -= fix;
+ }
+ }
+
+ /*
+ * Fill in widths for Percent cases. If there are no more to calculate,
+ * use the percentages as relative numbers and use the rest of the space,
+ * else treat them as absolute percentages of the original avail screen.
+ */
+ if(space_left > 0){
+ if(some_to_calculate){
+ int tot_requested = 0;
+
+ /*
+ * Requests are treated as percent of screen width. See if they
+ * will all fit. If not, trim them back proportionately.
+ */
+ for(cdesc = ps_global->index_disp_format;
+ cdesc->ctype != iNothing;
+ cdesc++){
+ if(cdesc->wtype == Percent){
+ /* The 2, 200, and +100 are because we're rounding */
+ fix = ((2*cdesc->req_width *
+ (screen_width-colspace))+100) / 200;
+ tot_requested += fix;
+ }
+ }
+
+ if(tot_requested > space_left){
+ int multiplier = (100 * space_left) / tot_requested;
+
+ for(cdesc = ps_global->index_disp_format;
+ cdesc->ctype != iNothing && space_left > 0;
+ cdesc++){
+ if(cdesc->wtype == Percent){
+ /* The 2, 200, and +100 are because we're rounding */
+ fix = ((2*cdesc->req_width *
+ (screen_width-colspace))+100) / 200;
+ fix = (2 * fix * multiplier + 100) / 200;
+ fix = MIN(fix, space_left);
+ cdesc->width += fix;
+ space_left -= fix;
+ }
+ }
+ }
+ else{
+ for(cdesc = ps_global->index_disp_format;
+ cdesc->ctype != iNothing && space_left > 0;
+ cdesc++){
+ if(cdesc->wtype == Percent){
+ /* The 2, 200, and +100 are because we're rounding */
+ fix = ((2*cdesc->req_width *
+ (screen_width-colspace))+100) / 200;
+ fix = MIN(fix, space_left);
+ cdesc->width += fix;
+ space_left -= fix;
+ }
+ }
+ }
+ }
+ else{
+ tot_pct = 0;
+ was_sl = space_left;
+ /* add up total percentages requested */
+ for(cdesc = ps_global->index_disp_format;
+ cdesc->ctype != iNothing;
+ cdesc++)
+ if(cdesc->wtype == Percent)
+ tot_pct += cdesc->req_width;
+
+ /* give relative weight to requests */
+ for(cdesc = ps_global->index_disp_format;
+ cdesc->ctype != iNothing && space_left > 0 && tot_pct > 0;
+ cdesc++){
+ if(cdesc->wtype == Percent){
+ fix = ((2*cdesc->req_width*was_sl)+tot_pct) / (2*tot_pct);
+ fix = MIN(fix, space_left);
+ cdesc->width += fix;
+ space_left -= fix;
+ }
+ }
+ }
+ }
+
+ /* split up rest, give twice as much to Subject */
+ keep_going = 1;
+ while(space_left > 0 && keep_going){
+ keep_going = 0;
+ for(cdesc = ps_global->index_disp_format;
+ cdesc->ctype != iNothing && space_left > 0;
+ cdesc++){
+ if(cdesc->wtype == WeCalculate && !ctype_is_fixed_length(cdesc->ctype)){
+ keep_going++;
+ cdesc->width++;
+ space_left--;
+ if(space_left > 0 && (cdesc->ctype == iSubject
+ || cdesc->ctype == iSubjectText
+ || cdesc->ctype == iSubjKey
+ || cdesc->ctype == iSubjKeyText
+ || cdesc->ctype == iSubjKeyInit
+ || cdesc->ctype == iSubjKeyInitText)){
+ cdesc->width++;
+ space_left--;
+ }
+ }
+ }
+ }
+
+ /* if still more, pad out percent's */
+ keep_going = 1;
+ while(space_left > 0 && keep_going){
+ keep_going = 0;
+ for(cdesc = ps_global->index_disp_format;
+ cdesc->ctype != iNothing && space_left > 0;
+ cdesc++){
+ if(cdesc->wtype == Percent && !ctype_is_fixed_length(cdesc->ctype)){
+ keep_going++;
+ cdesc->width++;
+ space_left--;
+ }
+ }
+ }
+
+ /* if user made Fixed fields too big, give back space */
+ keep_going = 1;
+ while(space_left < 0 && keep_going){
+ keep_going = 0;
+ for(cdesc = ps_global->index_disp_format;
+ cdesc->ctype != iNothing && space_left < 0;
+ cdesc++){
+ if(cdesc->wtype == Fixed && cdesc->width > 0){
+ keep_going++;
+ cdesc->width--;
+ space_left++;
+ }
+ }
+ }
+
+ if(pith_opt_save_index_state)
+ (*pith_opt_save_index_state)(FALSE);
+}
+
+
+void
+setup_thread_header_widths(MAILSTREAM *stream)
+{
+ clear_icache_flags(stream);
+ if(pith_opt_save_index_state)
+ (*pith_opt_save_index_state)(TRUE);
+}
+
+
+/*
+ * load_overview - c-client call back to gather overview data
+ *
+ * Note: if we never get called, UID represents a hole
+ * if we're passed a zero UID, totally bogus overview data
+ * if we're passed a zero obuf, mostly bogus overview data
+ */
+void
+load_overview(MAILSTREAM *stream, imapuid_t uid, OVERVIEW *obuf, long unsigned int rawno)
+{
+ if(obuf && rawno >= 1L && stream && rawno <= stream->nmsgs){
+ INDEXDATA_S idata;
+ ICE_S *ice;
+
+ memset(&idata, 0, sizeof(INDEXDATA_S));
+ idata.no_fetch = 1;
+
+ /*
+ * Only really load the thing if we've got an NNTP stream
+ * otherwise we're just using mail_fetch_overview to load the
+ * IMAP envelope cache with the specific set of messages
+ * in a single RTT.
+ */
+ idata.stream = stream;
+ idata.rawno = rawno;
+ idata.msgno = mn_raw2m(sp_msgmap(stream), idata.rawno);
+ idata.size = obuf->optional.octets;
+ idata.from = obuf->from;
+ idata.date = obuf->date;
+ idata.subject = obuf->subject;
+
+ ice = (*format_index_line)(&idata);
+ if(idata.bogus && ice){
+ if(THRD_INDX()){
+ if(ice->tice)
+ clear_ice(&ice->tice);
+ }
+ else
+ clear_ice(&ice);
+ }
+ else if(F_OFF(F_QUELL_NEWS_ENV_CB, ps_global)
+ && (!THRD_INDX() || (ice && ice->tice))
+ && !msgline_hidden(stream, sp_msgmap(stream), idata.msgno, 0)
+ && pith_opt_paint_index_hline){
+ (*pith_opt_paint_index_hline)(stream, idata.msgno, ice);
+ }
+ }
+}
+
+
+ICE_S *
+build_header_work(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap,
+ long int msgno, long int top_msgno, int msgcount, int *fetched)
+{
+ ICE_S *ice, *ic;
+ MESSAGECACHE *mc;
+ long n, i, cnt, rawno, visible, limit = -1L;
+
+ rawno = mn_m2raw(msgmap, msgno);
+
+ /* cache hit? */
+ if(THRD_INDX()){
+ ice = fetch_ice(stream, rawno);
+ if(!ice)
+ return(NULL);
+
+ if(ice->tice && ice->tice->ifield
+ && ice->tice->color_lookup_done && ice->tice->widths_done){
+#ifdef DEBUG
+ char buf[MAX_SCREEN_COLS+1];
+ simple_index_line(buf, sizeof(buf), ice->tice, msgno);
+#endif
+ dprint((9, "Hitt: Returning %p -> <%s (%d)\n",
+ ice->tice,
+ buf[0] ? buf : "?",
+ buf[0] ? strlen(buf) : 0));
+ return(ice);
+ }
+ }
+ else{
+ ice = fetch_ice(stream, rawno);
+ if(!ice)
+ return(NULL);
+
+ if(ice->ifield && ice->color_lookup_done && ice->widths_done){
+#ifdef DEBUG
+ char buf[MAX_SCREEN_COLS+1];
+ simple_index_line(buf, sizeof(buf), ice, msgno);
+#endif
+ dprint((9, "Hit: Returning %p -> <%s (%d)\n",
+ ice,
+ buf[0] ? buf : "?",
+ buf[0] ? strlen(buf) : 0));
+ return(ice);
+ }
+ }
+
+ /*
+ * If we are in THRD_INDX() and the width changed we don't currently
+ * have a method of fixing just the widths and print_format strings.
+ * Instead, we clear the index cache entry and start over.
+ */
+ if(THRD_INDX() && ice && ice->tice && ice->tice->ifield
+ && !ice->tice->widths_done){
+ clear_ice(&ice->tice);
+ }
+
+ /*
+ * Fetch everything we need to start filling in the index line
+ * explicitly via mail_fetch_overview. On an nntp stream
+ * this has the effect of building the index lines in the
+ * load_overview callback. Under IMAP we're either getting
+ * the envelope data via the imap_envelope callback or
+ * preloading the cache. Either way, we're getting exactly
+ * what we want rather than relying on linear lookahead sort
+ * of prefetch...
+ */
+ if(!(fetched && *fetched) && index_in_overview(stream)
+ && ((THRD_INDX() && !(ice->tice && ice->tice->ifield))
+ || (!THRD_INDX() && !ice->ifield))){
+ char *seq;
+ int count;
+ MESSAGECACHE *mc;
+ PINETHRD_S *thrd;
+
+ if(fetched)
+ (*fetched)++;
+
+ /* clear sequence bits */
+ for(n = 1L; n <= stream->nmsgs; n++)
+ if((mc = mail_elt(stream, n)) != NULL)
+ mc->sequence = 0;
+
+ /*
+ * Light interesting bits
+ * NOTE: not set above because m2raw's cheaper
+ * than raw2m for every message
+ */
+
+ /*
+ * Unfortunately, it is expensive to calculate visible pages
+ * in thread index if we are zoomed, so we don't try.
+ */
+ if(THRD_INDX() && any_lflagged(msgmap, MN_HIDE))
+ visible = msgmap->visible_threads;
+ else if(THREADING() && sp_viewing_a_thread(stream)){
+ /*
+ * We know that all visible messages in the thread are marked
+ * with MN_CHID2.
+ */
+ for(visible = 0L, n = top_msgno;
+ visible < msgcount && n <= mn_get_total(msgmap);
+ n++){
+
+ if(!get_lflag(stream, msgmap, n, MN_CHID2))
+ break;
+
+ if(!msgline_hidden(stream, msgmap, n, 0))
+ visible++;
+ }
+
+ }
+ else
+ visible = mn_get_total(msgmap)
+ - any_lflagged(msgmap, MN_HIDE|MN_CHID);
+
+ limit = MIN(visible, msgcount);
+
+ if(THRD_INDX()){
+ count = i = 0;
+
+ /*
+ * First add the msgno we're asking for in case it
+ * isn't visible.
+ */
+ thrd = fetch_thread(stream, mn_m2raw(msgmap, msgno));
+ if(msgno <= mn_get_total(msgmap)
+ && (!(ic=fetch_ice(stream,thrd->rawno)) || !(ic=ic->tice) || !ic->ifield)){
+ count += mark_msgs_in_thread(stream, thrd, msgmap);
+ }
+
+ thrd = fetch_thread(stream, mn_m2raw(msgmap, top_msgno));
+
+ /*
+ * Loop through visible threads, marking them for fetching.
+ * Stop at end of screen or sooner if we run out of visible
+ * threads.
+ */
+ while(thrd){
+ n = mn_raw2m(msgmap, thrd->rawno);
+ if(n >= msgno
+ && n <= mn_get_total(msgmap)
+ && (!(ic=fetch_ice(stream,thrd->rawno)) || !(ic=ic->tice) || !ic->ifield)){
+ count += mark_msgs_in_thread(stream, thrd, msgmap);
+ }
+
+ if(++i >= limit)
+ break;
+
+ /* find next thread which is visible */
+ do{
+ if(mn_get_revsort(msgmap) && thrd->prevthd)
+ thrd = fetch_thread(stream, thrd->prevthd);
+ else if(!mn_get_revsort(msgmap) && thrd->nextthd)
+ thrd = fetch_thread(stream, thrd->nextthd);
+ else
+ thrd = NULL;
+ } while(thrd
+ && msgline_hidden(stream, msgmap,
+ mn_raw2m(msgmap, thrd->rawno), 0));
+ }
+ }
+ else{
+ count = i = 0;
+
+ /*
+ * First add the msgno we're asking for in case it
+ * isn't visible.
+ */
+ if(msgno > 0L && msgno <= mn_get_total(msgmap)
+ && (!(ic=fetch_ice(stream, (rawno=mn_m2raw(msgmap,msgno)))) || !ic->ifield)){
+ if((thrd = fetch_thread(stream, rawno)) != NULL){
+ /*
+ * If we're doing a MUTTLIKE display the index line
+ * may depend on the thread parent, and grandparent,
+ * and further back. So just fetch the whole thread
+ * in that case.
+ */
+ if(THREADING()
+ && ps_global->thread_disp_style == THREAD_MUTTLIKE
+ && thrd->top)
+ thrd = fetch_thread(stream, thrd->top);
+
+ count += mark_msgs_in_thread(stream, thrd, msgmap);
+ }
+ else if(rawno > 0L && rawno <= stream->nmsgs
+ && (mc = mail_elt(stream,rawno))
+ && !mc->private.msg.env){
+ mc->sequence = 1;
+ count++;
+ }
+ }
+
+ n = top_msgno;
+ while(1){
+ if(n >= msgno
+ && n <= mn_get_total(msgmap)
+ && (!(ic=fetch_ice(stream, (rawno=mn_m2raw(msgmap,n)))) || !ic->ifield)){
+ if((thrd = fetch_thread(stream, rawno)) != NULL){
+ /*
+ * If we're doing a MUTTLIKE display the index line
+ * may depend on the thread parent, and grandparent,
+ * and further back. So just fetch the whole thread
+ * in that case.
+ */
+ if(THREADING()
+ && ps_global->thread_disp_style == THREAD_MUTTLIKE
+ && thrd->top)
+ thrd = fetch_thread(stream, thrd->top);
+
+ count += mark_msgs_in_thread(stream, thrd, msgmap);
+ }
+ else if(rawno > 0L && rawno <= stream->nmsgs
+ && (mc = mail_elt(stream,rawno))
+ && !mc->private.msg.env){
+ mc->sequence = 1;
+ count++;
+ }
+ }
+
+ if(++i >= limit)
+ break;
+
+ /* find next n which is visible */
+ while(++n <= mn_get_total(msgmap)
+ && msgline_hidden(stream, msgmap, n, 0))
+ ;
+ }
+ }
+
+ if(count){
+ seq = build_sequence(stream, NULL, NULL);
+ if(seq){
+ ps_global->dont_count_flagchanges = 1;
+ mail_fetch_overview_sequence(stream, seq,
+ (stream->dtb && stream->dtb->name
+ && !strcmp(stream->dtb->name, "imap"))
+ ? NULL : load_overview);
+ ps_global->dont_count_flagchanges = 0;
+ fs_give((void **) &seq);
+ }
+ }
+
+ /*
+ * reassign ice from the cache as it may've been built
+ * within the overview callback or it may have become stale
+ * in the prior sequence bit setting loop ...
+ */
+ rawno = mn_m2raw(msgmap, msgno);
+ ice = fetch_ice(stream, rawno);
+ if(!ice)
+ return(NULL);
+ }
+
+ if((THRD_INDX() && !(ice->tice && ice->tice->ifield))
+ || (!THRD_INDX() && !ice->ifield)){
+ INDEXDATA_S idata;
+
+ /*
+ * With pre-fetching/callback-formatting done and no success,
+ * fall into formatting the requested line...
+ */
+ memset(&idata, 0, sizeof(INDEXDATA_S));
+ idata.stream = stream;
+ idata.msgno = msgno;
+ idata.rawno = mn_m2raw(msgmap, msgno);
+ if(stream && idata.rawno > 0L && idata.rawno <= stream->nmsgs
+ && (mc = mail_elt(stream, idata.rawno))){
+ idata.size = mc->rfc822_size;
+ index_data_env(&idata, pine_mail_fetchenvelope(stream,idata.rawno));
+ }
+ else
+ idata.bogus = 2;
+
+ ice = (*format_index_line)(&idata);
+ if(!ice)
+ return(NULL);
+ }
+
+ /*
+ * If needed, reset the print_format strings so that they add up to
+ * the right total width. The reset width functionality isn't implemented
+ * for THRD_INDX() so we are just doing a complete rebuild in that
+ * case. This is driven by the clear_ice() call in clear_index_cache_ent()
+ * so it should never be the case that THRD_INDX() is true and only
+ * widths_done needs to be fixed.
+ */
+ if((!THRD_INDX() && ice->ifield && !ice->widths_done)){
+ ICE_S *working_ice;
+ IFIELD_S *ifield;
+ INDEX_COL_S *cdesc;
+
+ if(need_format_setup(stream))
+ setup_header_widths(stream);
+
+ if(THRD_INDX())
+ working_ice = ice ? ice->tice : NULL;
+ else
+ working_ice = ice;
+
+ if(working_ice){
+ /*
+ * First fix the ifield widths. The cdescs with nonzero widths
+ * should correspond to the ifields that are defined.
+ */
+ ifield = working_ice->ifield;
+ for(cdesc = ps_global->index_disp_format;
+ cdesc->ctype != iNothing && ifield; cdesc++){
+ if(cdesc->width){
+ if(cdesc->ctype != ifield->ctype){
+ dprint((1, "build_header_work(%ld): cdesc->ctype=%d != ifield->ctype=%d NOT SUPPOSED TO HAPPEN!\n", msgno, (int) cdesc->ctype, (int) ifield->ctype));
+ assert(0);
+ }
+
+ ifield->width = cdesc->width;
+ ifield = ifield->next;
+ }
+ }
+
+ /* fix the print_format strings and widths */
+ for(ifield = working_ice->ifield; ifield; ifield = ifield->next)
+ set_ielem_widths_in_field(ifield);
+
+ working_ice->widths_done = 1;
+ }
+ }
+
+ if(THRD_INDX() && ice->tice)
+ ice->tice->color_lookup_done = 1;
+
+ /*
+ * Look for a color for this line (and other lines in the current
+ * view). This does a SEARCH for each role which has a color until
+ * it finds a match. This will be satisfied by the c-client
+ * cache created by the mail_fetch_overview above if it is a header
+ * search.
+ */
+ if(!THRD_INDX() && !ice->color_lookup_done){
+ COLOR_PAIR *linecolor;
+ SEARCHSET *ss, *s;
+ ICE_S *ic;
+ PAT_STATE *pstate = NULL;
+
+ if(pico_usingcolor()){
+ if(limit < 0L){
+ if(THREADING() && sp_viewing_a_thread(stream)){
+ for(visible = 0L, n = top_msgno;
+ visible < msgcount && n <= mn_get_total(msgmap);
+ n++){
+
+ if(!get_lflag(stream, msgmap, n, MN_CHID2))
+ break;
+
+ if(!msgline_hidden(stream, msgmap, n, 0))
+ visible++;
+ }
+
+ }
+ else
+ visible = mn_get_total(msgmap)
+ - any_lflagged(msgmap, MN_HIDE|MN_CHID);
+
+ limit = MIN(visible, msgcount);
+ }
+ /* clear sequence bits */
+ for(n = 1L; n <= stream->nmsgs; n++)
+ if((mc = mail_elt(stream, n)) != NULL)
+ mc->sequence = 0;
+
+ cnt = i = 0;
+ n = top_msgno;
+ while(1){
+ if(n >= msgno
+ && n <= mn_get_total(msgmap)
+ && (!(ic=fetch_ice(stream,(rawno = mn_m2raw(msgmap, n)))) || !ic->color_lookup_done)){
+
+ if(rawno >= 1L && rawno <= stream->nmsgs
+ && (mc = mail_elt(stream, rawno))){
+ mc->sequence = 1;
+ cnt++;
+ }
+ }
+
+ if(++i >= limit)
+ break;
+
+ /* find next n which is visible */
+ while(++n <= mn_get_total(msgmap)
+ && msgline_hidden(stream, msgmap, n, 0))
+ ;
+ }
+
+ /*
+ * Why is there a loop here? The first call to get_index_line_color
+ * will return a set of messages which match one of the roles.
+ * Then, we eliminate those messages from the search set and try
+ * again. This time we'd get past that role and into a different
+ * role. Because of that, we hang onto the state and don't reset
+ * to the first_pattern on the second and subsequent times
+ * through the loop, avoiding fruitless match_pattern calls in
+ * get_index_line_color.
+ * Before the first call, pstate should be set to NULL.
+ */
+ while(cnt > 0L){
+ ss = build_searchset(stream);
+ if(ss){
+ int colormatch;
+
+ linecolor = NULL;
+ colormatch = get_index_line_color(stream, ss, &pstate,
+ &linecolor);
+
+ /*
+ * Assign this color to all matched msgno's and
+ * turn off the sequence bit so we won't check
+ * for them again.
+ */
+ if(colormatch){
+ for(s = ss; s; s = s->next){
+ for(n = s->first; n <= s->last; n++){
+ if(n >= 1L && n <= stream->nmsgs
+ && (mc = mail_elt(stream, n))
+ && mc->searched){
+ cnt--;
+ mc->sequence = 0;
+ ic = fetch_ice(stream, n);
+ if(ic){
+ ic->color_lookup_done = 1;
+ if(linecolor)
+ ic->linecolor = new_color_pair(linecolor->fg,
+ linecolor->bg);
+ }
+ }
+ }
+ }
+
+ if(linecolor)
+ free_color_pair(&linecolor);
+ }
+ else{
+ /* have to mark the rest of the lookups done */
+ for(s = ss; s && cnt > 0; s = s->next){
+ for(n = s->first; n <= s->last && cnt > 0; n++){
+ if(n >= 1L && n <= stream->nmsgs
+ && (mc = mail_elt(stream, n))
+ && mc->sequence){
+ cnt--;
+ ic = fetch_ice(stream, n);
+ if(ic)
+ ic->color_lookup_done = 1;
+ }
+ }
+ }
+
+ /* just making sure */
+ cnt = 0L;
+ }
+
+ mail_free_searchset(&ss);
+ }
+ else
+ cnt = 0L;
+ }
+
+ ice = fetch_ice(stream, mn_m2raw(msgmap, msgno));
+ }
+ else
+ ice->color_lookup_done = 1;
+ }
+
+ return(ice); /* Return formatted index data */
+}
+
+
+int
+day_of_week(struct date *d)
+{
+ int m, y;
+
+ m = d->month;
+ y = d->year;
+ if(m <= 2){
+ m += 9;
+ y--;
+ }
+ else
+ m -= 3; /* March is month 0 */
+
+ return((d->day+2+((7+31*m)/12)+y+(y/4)+(y/400)-(y/100))%7);
+}
+
+
+static int daytab[2][13] = {
+ {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
+ {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
+};
+
+int
+day_of_year(struct date *d)
+{
+ int i, leap, doy;
+
+ if(d->year <= 0 || d->month < 1 || d->month > 12)
+ return(-1);
+
+ doy = d->day;
+ leap = (d->year%4 == 0 && d->year%100 != 0) || d->year%400 == 0;
+ for(i = 1; i < d->month; i++)
+ doy += daytab[leap][i];
+
+ return(doy);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Format a string summarizing the message header for index on screen
+
+ Args: buffer -- buffer to place formatted line
+ idata -- snot it takes to format the line
+
+ Result: returns pointer given buffer IF entry formatted
+ else NULL if there was a problem (but the buffer is
+ still suitable for display)
+ ----*/
+ICE_S *
+format_index_index_line(INDEXDATA_S *idata)
+{
+ char str[BIGWIDTH+1], to_us, status, *field,
+ *p, *newsgroups;
+ int i, collapsed = 0, start, fromfield;
+ long l, score;
+ BODY *body = NULL;
+ MESSAGECACHE *mc;
+ ADDRESS *addr, *toaddr, *ccaddr, *last_to;
+ PINETHRD_S *thrd = NULL;
+ INDEX_COL_S *cdesc = NULL;
+ ICE_S *ice, **icep;
+ IFIELD_S *ifield;
+ IELEM_S *ielem;
+ struct variable *vars = ps_global->vars;
+
+ dprint((8, "=== format_index_line(msgno=%ld,rawno=%ld) ===\n",
+ idata ? idata->msgno : -1, idata ? idata->rawno : -1));
+
+
+ ice = fetch_ice(idata->stream, idata->rawno);
+ if(!ice)
+ return(NULL);
+
+ free_ifield(&ice->ifield);
+
+ /*
+ * Operate on a temporary copy of ice. The reason for this
+ * is that we may end up causing a pine_mail_fetchenvelope() call
+ * (e.g., in to_us_symbol_for_thread()) that causes an mm_flags()
+ * and mm_flags may do a clear_ice(), freeing the ice we are working
+ * on out from under us. We try to fetch everything we need in
+ * build_header_work() but c-client will short-circuit our request
+ * if we already got the raw header for some reason. One possible
+ * reason is a categorizer command in a filter. In that case
+ * we still need a fetch fast to get the rest of the envelope data.
+ */
+ ice = copy_ice(ice);
+
+ /* is this a collapsed thread index line? */
+ if(!idata->bogus && THREADING()){
+ thrd = fetch_thread(idata->stream, idata->rawno);
+ collapsed = thrd && thrd->next
+ && get_lflag(idata->stream, NULL,
+ idata->rawno, MN_COLL);
+ }
+
+ /* calculate contents of the required fields */
+ for(cdesc = ps_global->index_disp_format; cdesc->ctype != iNothing; cdesc++)
+ if(cdesc->width){
+ memset(str, 0, sizeof(str));
+ ifield = new_ifield(&ice->ifield);
+ ifield->ctype = cdesc->ctype;
+ ifield->width = cdesc->width;
+ fromfield = 0;
+
+ if(idata->bogus){
+ if(cdesc->ctype == iMessNo)
+ snprintf(str, sizeof(str), "%*.*s", ifield->width, ifield->width, " ");
+ else if(idata->bogus < 2 && (cdesc->ctype == iSubject
+ || cdesc->ctype == iSubjectText
+ || cdesc->ctype == iSubjKey
+ || cdesc->ctype == iSubjKeyText
+ || cdesc->ctype == iSubjKeyInit
+ || cdesc->ctype == iSubjKeyInitText))
+ snprintf(str, sizeof(str), "%s", _("[ No Message Text Available ]"));
+ }
+ else
+ switch(cdesc->ctype){
+ case iStatus:
+ to_us = status = ' ';
+ if(collapsed){
+ thrd = fetch_thread(idata->stream, idata->rawno);
+ to_us = to_us_symbol_for_thread(idata->stream, thrd, 1);
+ status = status_symbol_for_thread(idata->stream, thrd,
+ cdesc->ctype);
+ }
+ else{
+ if((mc=mail_elt(idata->stream,idata->rawno)) && mc->flagged)
+ to_us = '*'; /* simple */
+ else if(!IS_NEWS(idata->stream)){
+ for(addr = fetch_to(idata); addr; addr = addr->next)
+ if(address_is_us(addr, ps_global)){
+ ice->to_us = 1;
+ if(to_us == ' ')
+ to_us = '+';
+
+ break;
+ }
+
+ if(to_us != '+' && resent_to_us(idata)){
+ ice->to_us = 1;
+ if(to_us == ' ')
+ to_us = '+';
+ }
+
+ if(to_us == ' ' && F_ON(F_MARK_FOR_CC,ps_global))
+ for(addr = fetch_cc(idata); addr; addr = addr->next)
+ if(address_is_us(addr, ps_global)){
+ ice->cc_us = 1;
+ to_us = '-';
+ break;
+ }
+ }
+
+ status = (!idata->stream || !IS_NEWS(idata->stream)
+ || F_ON(F_FAKE_NEW_IN_NEWS, ps_global))
+ ? 'N' : ' ';
+
+ if(mc->seen)
+ status = ' ';
+
+ if(user_flag_is_set(idata->stream, idata->rawno, FORWARDED_FLAG))
+ status = 'F';
+
+ if(mc->answered)
+ status = 'A';
+
+ if(mc->deleted)
+ status = 'D';
+ }
+
+ snprintf(str, sizeof(str), "%c %c", to_us, status);
+
+ ifield->leftadj = 1;
+ for(i = 0; i < 3; i++){
+ ielem = new_ielem(&ifield->ielem);
+ ielem->freedata = 1;
+ ielem->data = (char *) fs_get(2 * sizeof(char));
+ ielem->data[0] = str[i];
+ ielem->data[1] = '\0';
+ ielem->datalen = 1;
+ set_print_format(ielem, 1, ifield->leftadj);
+ }
+
+ if(pico_usingcolor()){
+
+ if(str[0] == '*'){
+ if(VAR_IND_IMP_FORE_COLOR && VAR_IND_IMP_BACK_COLOR){
+ ielem = ifield->ielem;
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_IMP_FORE_COLOR, VAR_IND_IMP_BACK_COLOR);
+ }
+ }
+ else if(str[0] == '+' || str[0] == '-'){
+ if(VAR_IND_PLUS_FORE_COLOR && VAR_IND_PLUS_BACK_COLOR){
+ ielem = ifield->ielem;
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_PLUS_FORE_COLOR, VAR_IND_PLUS_BACK_COLOR);
+ }
+ }
+
+ if(str[2] == 'D'){
+ if(VAR_IND_DEL_FORE_COLOR && VAR_IND_DEL_BACK_COLOR){
+ ielem = ifield->ielem->next->next;
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_DEL_FORE_COLOR, VAR_IND_DEL_BACK_COLOR);
+ }
+ }
+ else if(str[2] == 'A'){
+ if(VAR_IND_ANS_FORE_COLOR && VAR_IND_ANS_BACK_COLOR){
+ ielem = ifield->ielem->next->next;
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_ANS_FORE_COLOR, VAR_IND_ANS_BACK_COLOR);
+ }
+ }
+ else if(str[2] == 'F'){
+ if(VAR_IND_FWD_FORE_COLOR && VAR_IND_FWD_BACK_COLOR){
+ ielem = ifield->ielem->next->next;
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_FWD_FORE_COLOR, VAR_IND_FWD_BACK_COLOR);
+ }
+ }
+ else if(str[2] == 'N'){
+ if(VAR_IND_NEW_FORE_COLOR && VAR_IND_NEW_BACK_COLOR){
+ ielem = ifield->ielem->next->next;
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_NEW_FORE_COLOR, VAR_IND_NEW_BACK_COLOR);
+ }
+ }
+ }
+
+ break;
+
+ case iFStatus:
+ case iIStatus:
+ case iSIStatus:
+ {
+ char new, answered, deleted, flagged;
+
+ if(collapsed){
+ thrd = fetch_thread(idata->stream, idata->rawno);
+ to_us = to_us_symbol_for_thread(idata->stream, thrd, 0);
+ }
+ else{
+ to_us = ' ';
+ if(!IS_NEWS(idata->stream)){
+ for(addr = fetch_to(idata); addr; addr = addr->next)
+ if(address_is_us(addr, ps_global)){
+ to_us = '+';
+ break;
+ }
+
+ if(to_us == ' ' && resent_to_us(idata))
+ to_us = '+';
+
+ if(to_us == ' ' && F_ON(F_MARK_FOR_CC,ps_global))
+ for(addr = fetch_cc(idata); addr; addr = addr->next)
+ if(address_is_us(addr, ps_global)){
+ to_us = '-';
+ break;
+ }
+ }
+ }
+
+ new = answered = deleted = flagged = ' ';
+
+ if(collapsed){
+ unsigned long save_branch, cnt, tot_in_thrd;
+
+ /*
+ * Branch is a sibling, not part of the thread, so
+ * don't consider it when displaying this line.
+ */
+ save_branch = thrd->branch;
+ thrd->branch = 0L;
+
+ tot_in_thrd = count_flags_in_thread(idata->stream, thrd,
+ F_NONE);
+
+ cnt = count_flags_in_thread(idata->stream, thrd, F_DEL);
+ if(cnt)
+ deleted = (cnt == tot_in_thrd) ? 'D' : 'd';
+
+ cnt = count_flags_in_thread(idata->stream, thrd, F_ANS);
+ if(cnt)
+ answered = (cnt == tot_in_thrd) ? 'A' : 'a';
+
+ /* no lower case *, same thing for some or all */
+ if(count_flags_in_thread(idata->stream, thrd, F_FLAG))
+ flagged = '*';
+
+ new = status_symbol_for_thread(idata->stream, thrd,
+ cdesc->ctype);
+
+ thrd->branch = save_branch;
+ }
+ else{
+ mc = (idata->rawno > 0L && idata->stream
+ && idata->rawno <= idata->stream->nmsgs)
+ ? mail_elt(idata->stream, idata->rawno) : NULL;
+ if(mc && mc->valid){
+ if(cdesc->ctype == iIStatus || cdesc->ctype == iSIStatus){
+ if(mc->recent)
+ new = mc->seen ? 'R' : 'N';
+ else if (!mc->seen)
+ new = 'U';
+ }
+ else if(!mc->seen
+ && (!IS_NEWS(idata->stream)
+ || F_ON(F_FAKE_NEW_IN_NEWS, ps_global)))
+ new = 'N';
+
+ if(mc->answered)
+ answered = 'A';
+
+ if(mc->deleted)
+ deleted = 'D';
+
+ if(mc->flagged)
+ flagged = '*';
+ }
+ }
+
+ snprintf(str, sizeof(str), "%c %c%c%c%c", to_us, flagged, new,
+ answered, deleted);
+
+ if(cdesc->ctype == iSIStatus)
+ start = 2;
+ else
+ start = 0;
+
+ ifield->leftadj = 1;
+ for(i = start; i < 6; i++){
+ ielem = new_ielem(&ifield->ielem);
+ ielem->freedata = 1;
+ ielem->data = (char *) fs_get(2 * sizeof(char));
+ ielem->data[0] = str[i];
+ ielem->data[1] = '\0';
+ ielem->datalen = 1;
+ set_print_format(ielem, 1, ifield->leftadj);
+ }
+
+ if(pico_usingcolor()){
+
+ if(str[0] == '+' || str[0] == '-'){
+ if(start == 0
+ && VAR_IND_PLUS_FORE_COLOR
+ && VAR_IND_PLUS_BACK_COLOR){
+ ielem = ifield->ielem;
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_PLUS_FORE_COLOR, VAR_IND_PLUS_BACK_COLOR);
+ }
+ }
+
+ if(str[2] == '*'){
+ if(VAR_IND_IMP_FORE_COLOR && VAR_IND_IMP_BACK_COLOR){
+ if(start == 2)
+ ielem = ifield->ielem;
+ else
+ ielem = ifield->ielem->next->next;
+
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_IMP_FORE_COLOR, VAR_IND_IMP_BACK_COLOR);
+ }
+ }
+
+ if(str[3] == 'N' || str[3] == 'n'){
+ if(VAR_IND_NEW_FORE_COLOR && VAR_IND_NEW_BACK_COLOR){
+ if(start == 2)
+ ielem = ifield->ielem->next;
+ else
+ ielem = ifield->ielem->next->next->next;
+
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_NEW_FORE_COLOR, VAR_IND_NEW_BACK_COLOR);
+ }
+ }
+ else if(str[3] == 'R' || str[3] == 'r'){
+ if(VAR_IND_REC_FORE_COLOR && VAR_IND_REC_BACK_COLOR){
+ if(start == 2)
+ ielem = ifield->ielem->next;
+ else
+ ielem = ifield->ielem->next->next->next;
+
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_REC_FORE_COLOR, VAR_IND_REC_BACK_COLOR);
+ }
+ }
+ else if(str[3] == 'U' || str[3] == 'u'){
+ if(VAR_IND_UNS_FORE_COLOR && VAR_IND_UNS_BACK_COLOR){
+ if(start == 2)
+ ielem = ifield->ielem->next;
+ else
+ ielem = ifield->ielem->next->next->next;
+
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_UNS_FORE_COLOR, VAR_IND_UNS_BACK_COLOR);
+ }
+ }
+
+ if(str[4] == 'A' || str[4] == 'a'){
+ if(VAR_IND_ANS_FORE_COLOR && VAR_IND_ANS_BACK_COLOR){
+ if(start == 2)
+ ielem = ifield->ielem->next->next;
+ else
+ ielem = ifield->ielem->next->next->next->next;
+
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_ANS_FORE_COLOR, VAR_IND_ANS_BACK_COLOR);
+ }
+ }
+
+ if(str[5] == 'D' || str[5] == 'd'){
+ if(VAR_IND_DEL_FORE_COLOR && VAR_IND_DEL_BACK_COLOR){
+ if(start == 2)
+ ielem = ifield->ielem->next->next->next;
+ else
+ ielem = ifield->ielem->next->next->next->next->next;
+
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_DEL_FORE_COLOR, VAR_IND_DEL_BACK_COLOR);
+ }
+ }
+ }
+ }
+
+ break;
+
+ case iMessNo:
+ /*
+ * This is a special case. The message number is
+ * generated on the fly in the painting routine.
+ * But the data array is allocated here in case it
+ * is useful for the paint routine.
+ */
+ snprintf(str, sizeof(str), "%*.*s", ifield->width, ifield->width, " ");
+ break;
+
+ case iArrow:
+ snprintf(str, sizeof(str), "%-*.*s", ifield->width, ifield->width, " ");
+ if(VAR_IND_ARR_FORE_COLOR && VAR_IND_ARR_BACK_COLOR){
+ ifield->leftadj = 1;
+ ielem = new_ielem(&ifield->ielem);
+ ielem->freedata = 1;
+ ielem->data = cpystr(str);
+ ielem->datalen = strlen(str);
+ set_print_format(ielem, ifield->width, ifield->leftadj);
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_ARR_FORE_COLOR,
+ VAR_IND_ARR_BACK_COLOR);
+ }
+
+ break;
+
+ case iScore:
+ score = get_msg_score(idata->stream, idata->rawno);
+ if(score == SCORE_UNDEF){
+ SEARCHSET *ss = NULL;
+
+ ss = mail_newsearchset();
+ ss->first = ss->last = (unsigned long) idata->rawno;
+ if(ss){
+ /*
+ * This looks like it might be expensive to get the
+ * score for each message when needed but it shouldn't
+ * be too bad because we know we have the envelope
+ * data cached. We can't calculate all of the scores
+ * we need for the visible messages right here in
+ * one fell swoop because we don't have the other
+ * envelopes yet. And we can't get the other
+ * envelopes at this point because we may be in
+ * the middle of a c-client callback (pine_imap_env).
+ * (Actually we could, because we know whether or
+ * not we're in the callback because of the no_fetch
+ * parameter.)
+ * We have another problem if the score rules depend
+ * on something other than envelope data. I guess they
+ * only do that if they have an alltext (search the
+ * text of the message) definition. So, we're going
+ * to pass no_fetch to calculate_scores so that it
+ * can return an error if we need the text data but
+ * can't get it because of no_fetch. Setting bogus
+ * will cause us to do the scores calculation later
+ * when we are no longer in the callback.
+ */
+ idata->bogus =
+ (calculate_some_scores(idata->stream,
+ ss, idata->no_fetch) == 0)
+ ? 1 : 0;
+ score = get_msg_score(idata->stream, idata->rawno);
+ mail_free_searchset(&ss);
+ }
+ }
+
+ snprintf(str, sizeof(str), "%ld", score != SCORE_UNDEF ? score : 0L);
+ break;
+
+ case iDate: case iMonAbb: case iLDate:
+ case iSDate: case iSTime:
+ case iS1Date: case iS2Date: case iS3Date: case iS4Date:
+ case iDateIso: case iDateIsoS: case iTime24: case iTime12:
+ case iSDateIsoS: case iSDateIso:
+ case iSDateS1: case iSDateS2: case iSDateS3: case iSDateS4:
+ case iSDateTime:
+ case iSDateTimeIsoS: case iSDateTimeIso:
+ case iSDateTimeS1: case iSDateTimeS2: case iSDateTimeS3: case iSDateTimeS4:
+ case iSDateTime24:
+ case iSDateTimeIsoS24: case iSDateTimeIso24:
+ case iSDateTimeS124: case iSDateTimeS224: case iSDateTimeS324: case iSDateTimeS424:
+ case iTimezone: case iYear: case iYear2Digit:
+ case iRDate: case iDay: case iDay2Digit: case iMon2Digit:
+ case iDayOrdinal: case iMon: case iMonLong:
+ case iDayOfWeekAbb: case iDayOfWeek:
+ case iPrefDate: case iPrefTime: case iPrefDateTime:
+ date_str(fetch_date(idata), cdesc->ctype, 0, str, sizeof(str), cdesc->monabb_width);
+ break;
+
+ case iFromTo:
+ case iFromToNotNews:
+ case iFrom:
+ case iAddress:
+ case iMailbox:
+ fromfield++;
+ from_str(cdesc->ctype, idata, str, sizeof(str), ice);
+ break;
+
+ case iTo:
+ if(((field = ((addr = fetch_to(idata))
+ ? "To"
+ : (addr = fetch_cc(idata))
+ ? "Cc"
+ : NULL))
+ && !set_index_addr(idata, field, addr, NULL, BIGWIDTH, str))
+ || !field)
+ if((newsgroups = fetch_newsgroups(idata)) != NULL)
+ snprintf(str, sizeof(str), "%-.*s", BIGWIDTH, newsgroups);
+
+ break;
+
+ case iCc:
+ set_index_addr(idata, "Cc", fetch_cc(idata), NULL, BIGWIDTH, str);
+ break;
+
+ case iRecips:
+ toaddr = fetch_to(idata);
+ ccaddr = fetch_cc(idata);
+ for(last_to = toaddr;
+ last_to && last_to->next;
+ last_to = last_to->next)
+ ;
+
+ /* point end of to list temporarily at cc list */
+ if(last_to)
+ last_to->next = ccaddr;
+
+ set_index_addr(idata, "To", toaddr, NULL, BIGWIDTH, str);
+
+ if(last_to)
+ last_to->next = NULL;
+
+ break;
+
+ case iSender:
+ fromfield++;
+ if((addr = fetch_sender(idata)) != NULL)
+ set_index_addr(idata, "Sender", addr, NULL, BIGWIDTH, str);
+
+ break;
+
+ case iInit:
+ {ADDRESS *addr;
+
+ if((addr = fetch_from(idata)) && addr->personal){
+ char *name, *initials = NULL;
+
+ name = (char *) rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, addr->personal);
+ if(name == addr->personal){
+ strncpy(tmp_20k_buf, name, SIZEOF_20KBUF-1);
+ tmp_20k_buf[SIZEOF_20KBUF - 1] = '\0';
+ name = (char *) tmp_20k_buf;
+ }
+
+ if(name && *name){
+ initials = reply_quote_initials(name);
+ snprintf(str, sizeof(str), "%-.*s", BIGWIDTH, initials);
+ }
+ }
+ }
+
+ break;
+
+ case iSize:
+ /* 0 ... 9999 */
+ if((l = fetch_size(idata)) < 10*1000L)
+ snprintf(str, sizeof(str), "(%lu)", l);
+ /* 10K ... 999K */
+ else if(l < 1000L*1000L - 1000L/2){
+ l = l/1000L + (l%1000L >= 1000L/2 ? 1L : 0L);
+ snprintf(str, sizeof(str), "(%luK)", l);
+ }
+ /* 1.0M ... 99.9M */
+ else if(l < 1000L*100L*1000L - 100L*1000L/2){
+ l = l/(100L*1000L) + (l%(100L*1000L) >= (100*1000L/2)
+ ? 1L : 0L);
+ snprintf(str, sizeof(str), "(%lu.%luM)", l/10L, l % 10L);
+ }
+ /* 100M ... 2000M */
+ else if(l <= 2*1000L*1000L*1000L){
+ l = l/(1000L*1000L) + (l%(1000L*1000L) >= (1000L*1000L/2)
+ ? 1L : 0L);
+ snprintf(str, sizeof(str), "(%luM)", l);
+ }
+ else
+ snprintf(str, sizeof(str), "(HUGE!)");
+
+ break;
+
+ case iSizeComma:
+ /* 0 ... 99,999 */
+ if((l = fetch_size(idata)) < 100*1000L)
+ snprintf(str, sizeof(str), "(%s)", comatose(l));
+ /* 100K ... 9,999K */
+ else if(l < 10L*1000L*1000L - 1000L/2){
+ l = l/1000L + (l%1000L >= 1000L/2 ? 1L : 0L);
+ snprintf(str, sizeof(str), "(%sK)", comatose(l));
+ }
+ /* 10.0M ... 999.9M */
+ else if(l < 1000L*1000L*1000L - 100L*1000L/2){
+ l = l/(100L*1000L) + (l%(100L*1000L) >= (100*1000L/2)
+ ? 1L : 0L);
+ snprintf(str, sizeof(str), "(%lu.%luM)", l/10L, l % 10L);
+ }
+ /* 1,000M ... 2,000M */
+ else if(l <= 2*1000L*1000L*1000L){
+ l = l/(1000L*1000L) + (l%(1000L*1000L) >= (1000L*1000L/2)
+ ? 1L : 0L);
+ snprintf(str, sizeof(str), "(%sM)", comatose(l));
+ }
+ else
+ snprintf(str, sizeof(str), "(HUGE!)");
+
+ break;
+
+ case iSizeNarrow:
+ /* 0 ... 999 */
+ if((l = fetch_size(idata)) < 1000L)
+ snprintf(str, sizeof(str), "(%lu)", l);
+ /* 1K ... 99K */
+ else if(l < 100L*1000L - 1000L/2){
+ l = l/1000L + (l%1000L >= 1000L/2 ? 1L : 0L);
+ snprintf(str, sizeof(str), "(%luK)", l);
+ }
+ /* .1M ... .9M */
+ else if(l < 1000L*1000L - 100L*1000L/2){
+ l = l/(100L*1000L) + (l%(100L*1000L) >= 100L*1000L/2
+ ? 1L : 0L);
+ snprintf(str, sizeof(str), "(.%luM)", l);
+ }
+ /* 1M ... 99M */
+ else if(l < 1000L*100L*1000L - 1000L*1000L/2){
+ l = l/(1000L*1000L) + (l%(1000L*1000L) >= (1000L*1000L/2)
+ ? 1L : 0L);
+ snprintf(str, sizeof(str), "(%luM)", l);
+ }
+ /* .1G ... .9G */
+ else if(l < 1000L*1000L*1000L - 100L*1000L*1000L/2){
+ l = l/(100L*1000L*1000L) + (l%(100L*1000L*1000L) >=
+ (100L*1000L*1000L/2) ? 1L : 0L);
+ snprintf(str, sizeof(str), "(.%luG)", l);
+ }
+ /* 1G ... 2G */
+ else if(l <= 2*1000L*1000L*1000L){
+ l = l/(1000L*1000L*1000L) + (l%(1000L*1000L*1000L) >=
+ (1000L*1000L*1000L/2) ? 1L : 0L);
+ snprintf(str, sizeof(str), "(%luG)", l);
+ }
+ else
+ snprintf(str, sizeof(str), "(HUGE!)");
+
+ break;
+
+ /* From Carl Jacobsen <carl@ucsd.edu> */
+ case iKSize:
+ l = fetch_size(idata);
+ l = (l / 1024L) + (l % 1024L != 0 ? 1 : 0);
+
+ if(l < 1024L) { /* 0k .. 1023k */
+ snprintf(str, sizeof(str), "(%luk)", l);
+
+ } else if (l < 100L * 1024L){ /* 1.0M .. 99.9M */
+ snprintf(str, sizeof(str), "(%lu.M)", (l * 10L) / 1024L);
+ if ((p = strchr(str, '.')) != NULL) {
+ p--; p[1] = p[0]; p[0] = '.'; /* swap last digit & . */
+ }
+ } else if (l <= 2L * 1024L * 1024L) { /* 100M .. 2048 */
+ snprintf(str, sizeof(str), "(%luM)", l / 1024L);
+ } else {
+ snprintf(str, sizeof(str), "(HUGE!)");
+ }
+
+ break;
+
+ case iDescripSize:
+ if((body = fetch_body(idata)) != NULL)
+ switch(body->type){
+ case TYPETEXT:
+ {
+ mc = (idata->rawno > 0L && idata->stream
+ && idata->rawno <= idata->stream->nmsgs)
+ ? mail_elt(idata->stream, idata->rawno) : NULL;
+ if(mc && mc->rfc822_size < 6000)
+ snprintf(str, sizeof(str), "(short )");
+ else if(mc && mc->rfc822_size < 25000)
+ snprintf(str, sizeof(str), "(medium )");
+ else if(mc && mc->rfc822_size < 100000)
+ snprintf(str, sizeof(str), "(long )");
+ else
+ snprintf(str, sizeof(str), "(huge )");
+ }
+
+ break;
+
+ case TYPEMULTIPART:
+ if(strucmp(body->subtype, "MIXED") == 0){
+ int x;
+
+ x = body->nested.part
+ ? body->nested.part->body.type
+ : TYPETEXT + 1000;
+ switch(x){
+ case TYPETEXT:
+ if(body->nested.part->body.size.bytes < 6000)
+ snprintf(str, sizeof(str), "(short+ )");
+ else if(body->nested.part->body.size.bytes
+ < 25000)
+ snprintf(str, sizeof(str), "(medium+)");
+ else if(body->nested.part->body.size.bytes
+ < 100000)
+ snprintf(str, sizeof(str), "(long+ )");
+ else
+ snprintf(str, sizeof(str), "(huge+ )");
+ break;
+
+ default:
+ snprintf(str, sizeof(str), "(multi )");
+ break;
+ }
+ }
+ else if(strucmp(body->subtype, "DIGEST") == 0)
+ snprintf(str, sizeof(str), "(digest )");
+ else if(strucmp(body->subtype, "ALTERNATIVE") == 0)
+ snprintf(str, sizeof(str), "(mul/alt)");
+ else if(strucmp(body->subtype, "PARALLEL") == 0)
+ snprintf(str, sizeof(str), "(mul/par)");
+ else
+ snprintf(str, sizeof(str), "(multi )");
+
+ break;
+
+ case TYPEMESSAGE:
+ snprintf(str, sizeof(str), "(message)");
+ break;
+
+ case TYPEAPPLICATION:
+ snprintf(str, sizeof(str), "(applica)");
+ break;
+
+ case TYPEAUDIO:
+ snprintf(str, sizeof(str), "(audio )");
+ break;
+
+ case TYPEIMAGE:
+ snprintf(str, sizeof(str), "(image )");
+ break;
+
+ case TYPEVIDEO:
+ snprintf(str, sizeof(str), "(video )");
+ break;
+
+ default:
+ snprintf(str, sizeof(str), "(other )");
+ break;
+ }
+
+ break;
+
+ case iAtt:
+ str[0] = SPACE;
+ str[1] = '\0';
+ if((body = fetch_body(idata)) &&
+ body->type == TYPEMULTIPART &&
+ strucmp(body->subtype, "ALTERNATIVE") != 0){
+ PART *part;
+ int atts = 0;
+
+ part = body->nested.part; /* 1st part, don't count */
+ while(part && part->next && atts < 10){
+ atts++;
+ part = part->next;
+ }
+
+ if(atts > 9)
+ str[0] = '*';
+ else if(atts > 0)
+ str[0] = '0' + atts;
+ }
+
+ break;
+
+ case iSubject:
+ subj_str(idata, str, sizeof(str), NoKW, 0, ice);
+ break;
+
+ case iSubjectText:
+ subj_str(idata, str, sizeof(str), NoKW, 1, ice);
+ break;
+
+ case iSubjKey:
+ subj_str(idata, str, sizeof(str), KW, 0, ice);
+ break;
+
+ case iSubjKeyText:
+ subj_str(idata, str, sizeof(str), KW, 1, ice);
+ break;
+
+ case iSubjKeyInit:
+ subj_str(idata, str, sizeof(str), KWInit, 0, ice);
+ break;
+
+ case iSubjKeyInitText:
+ subj_str(idata, str, sizeof(str), KWInit, 1, ice);
+ break;
+
+ case iOpeningText:
+ case iOpeningTextNQ:
+ if(idata->no_fetch)
+ idata->bogus = 1;
+ else{
+ char *first_text;
+
+ first_text = fetch_firsttext(idata, cdesc->ctype == iOpeningTextNQ);
+
+ if(first_text){
+ strncpy(str, first_text, BIGWIDTH);
+ str[BIGWIDTH] = '\0';
+ fs_give((void **) &first_text);
+ }
+ }
+
+ break;
+
+ case iKey:
+ key_str(idata, KW, ice);
+ break;
+
+ case iKeyInit:
+ key_str(idata, KWInit, ice);
+ break;
+
+ case iNews:
+ if((newsgroups = fetch_newsgroups(idata)) != NULL){
+ strncpy(str, newsgroups, BIGWIDTH);
+ str[BIGWIDTH] = '\0';
+ }
+
+ break;
+
+ case iNewsAndTo:
+ if((newsgroups = fetch_newsgroups(idata)) != NULL)
+ strncpy(str, newsgroups, sizeof(str));
+
+ if((l = strlen(str)) < sizeof(str)){
+ if(sizeof(str) - l < 6)
+ strncpy(str+l, "...", sizeof(str)-l);
+ else{
+ if(l > 0){
+ strncpy(str+l, " and ", sizeof(str)-l);
+ set_index_addr(idata, "To", fetch_to(idata),
+ NULL, BIGWIDTH-l-5, str+l+5);
+ if(!str[l+5])
+ str[l] = '\0';
+ }
+ else
+ set_index_addr(idata, "To", fetch_to(idata),
+ NULL, BIGWIDTH, str);
+ }
+ }
+
+ break;
+
+ case iToAndNews:
+ set_index_addr(idata, "To", fetch_to(idata),
+ NULL, BIGWIDTH, str);
+ if((l = strlen(str)) < sizeof(str) &&
+ (newsgroups = fetch_newsgroups(idata))){
+ if(sizeof(str) - l < 6)
+ strncpy(str+l, "...", sizeof(str)-l);
+ else{
+ if(l > 0)
+ strncpy(str+l, " and ", sizeof(str)-l);
+
+ if(l > 0)
+ strncpy(str+l+5, newsgroups, BIGWIDTH-l-5);
+ else
+ strncpy(str, newsgroups, BIGWIDTH);
+ }
+ }
+
+ break;
+
+ case iNewsAndRecips:
+ if((newsgroups = fetch_newsgroups(idata)) != NULL)
+ strncpy(str, newsgroups, BIGWIDTH);
+
+ if((l = strlen(str)) < BIGWIDTH){
+ if(BIGWIDTH - l < 6)
+ strncpy(str+l, "...", BIGWIDTH-l);
+ else{
+ toaddr = fetch_to(idata);
+ ccaddr = fetch_cc(idata);
+ for(last_to = toaddr;
+ last_to && last_to->next;
+ last_to = last_to->next)
+ ;
+
+ /* point end of to list temporarily at cc list */
+ if(last_to)
+ last_to->next = ccaddr;
+
+ if(l > 0){
+ strncpy(str+l, " and ", sizeof(str)-l);
+ set_index_addr(idata, "To", toaddr,
+ NULL, BIGWIDTH-l-5, str+l+5);
+ if(!str[l+5])
+ str[l] = '\0';
+ }
+ else
+ set_index_addr(idata, "To", toaddr, NULL, BIGWIDTH, str);
+
+ if(last_to)
+ last_to->next = NULL;
+ }
+ }
+
+ break;
+
+ case iRecipsAndNews:
+ toaddr = fetch_to(idata);
+ ccaddr = fetch_cc(idata);
+ for(last_to = toaddr;
+ last_to && last_to->next;
+ last_to = last_to->next)
+ ;
+
+ /* point end of to list temporarily at cc list */
+ if(last_to)
+ last_to->next = ccaddr;
+
+ set_index_addr(idata, "To", toaddr, NULL, BIGWIDTH, str);
+
+ if(last_to)
+ last_to->next = NULL;
+
+ if((l = strlen(str)) < BIGWIDTH &&
+ (newsgroups = fetch_newsgroups(idata))){
+ if(BIGWIDTH - l < 6)
+ strncpy(str+l, "...", BIGWIDTH-l);
+ else{
+ if(l > 0)
+ strncpy(str+l, " and ", sizeof(str)-l);
+
+ if(l > 0)
+ strncpy(str+l+5, newsgroups, BIGWIDTH-l-5);
+ else
+ strncpy(str, newsgroups, BIGWIDTH);
+ }
+ }
+
+ break;
+
+ case iPrio:
+ case iPrioAlpha:
+ case iPrioBang:
+ prio_str(idata, cdesc->ctype, ice);
+ break;
+
+ case iHeader:
+ header_str(idata, cdesc->hdrtok, ice);
+ break;
+
+ case iText:
+ strncpy(str, (cdesc->hdrtok && cdesc->hdrtok->hdrname) ? cdesc->hdrtok->hdrname : "", sizeof(str));
+ str[sizeof(str)-1] = '\0';
+ break;
+
+ default:
+ break;
+ }
+
+ /*
+ * If the element wasn't already filled in above, do it here.
+ */
+ if(!ifield->ielem){
+ ielem = new_ielem(&ifield->ielem);
+
+ ielem->freedata = 1;
+ ielem->data = cpystr(str);
+ ielem->datalen = strlen(str);
+
+ if(fromfield && pico_usingcolor()
+ && ps_global->VAR_IND_FROM_FORE_COLOR
+ && ps_global->VAR_IND_FROM_BACK_COLOR){
+ ielem->type = eTypeCol;
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(ps_global->VAR_IND_FROM_FORE_COLOR,
+ ps_global->VAR_IND_FROM_BACK_COLOR);
+ /*
+ * This space is here so that if the text does
+ * not extend all the way to the end of the field then
+ * we'll switch the color back and paint the rest of the
+ * field in the Normal color or the index line color.
+ */
+ ielem = new_ielem(&ielem);
+ ielem->freedata = 1;
+ ielem->data = cpystr(" ");
+ ielem->datalen = 1;
+ }
+ else if((cdesc->ctype == iOpeningText || cdesc->ctype == iOpeningTextNQ)
+ && pico_usingcolor()
+ && ps_global->VAR_IND_OP_FORE_COLOR
+ && ps_global->VAR_IND_OP_BACK_COLOR){
+ ielem->type = eTypeCol;
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(ps_global->VAR_IND_OP_FORE_COLOR,
+ ps_global->VAR_IND_OP_BACK_COLOR);
+ /*
+ * This space is here so that if the text does
+ * not extend all the way to the end of the field then
+ * we'll switch the color back and paint the rest of the
+ * field in the Normal color or the index line color.
+ */
+ ielem = new_ielem(&ielem);
+ ielem->freedata = 1;
+ ielem->data = cpystr(" ");
+ ielem->datalen = 1;
+ }
+
+ ifield->leftadj = (cdesc->adjustment == Left) ? 1 : 0;
+ set_ielem_widths_in_field(ifield);
+ }
+ }
+
+ ice->widths_done = 1;
+ ice->id = ice_hash(ice);
+
+ /*
+ * Now we have to put the temporary copy of ice back as the
+ * real thing.
+ */
+ icep = fetch_ice_ptr(idata->stream, idata->rawno);
+ if(icep){
+ free_ice(icep); /* free what is already there */
+ *icep = ice;
+ }
+
+ return(ice);
+}
+
+
+ICE_S *
+format_thread_index_line(INDEXDATA_S *idata)
+{
+ char *p, buffer[BIGWIDTH+1];
+ int thdlen, space_left, i;
+ PINETHRD_S *thrd = NULL;
+ ICE_S *ice, *tice = NULL, **ticep = NULL;
+ IFIELD_S *ifield;
+ IELEM_S *ielem;
+ int (*save_sfstr_func)(void);
+ struct variable *vars = ps_global->vars;
+
+ dprint((8, "=== format_thread_index_line(%ld,%ld) ===\n",
+ idata ? idata->msgno : -1, idata ? idata->rawno : -1));
+
+ space_left = ps_global->ttyo->screen_cols;
+
+ if(ps_global->msgmap->max_thrdno < 1000)
+ thdlen = 3;
+ else if(ps_global->msgmap->max_thrdno < 10000)
+ thdlen = 4;
+ else if(ps_global->msgmap->max_thrdno < 100000)
+ thdlen = 5;
+ else
+ thdlen = 6;
+
+ ice = fetch_ice(idata->stream, idata->rawno);
+
+ thrd = fetch_thread(idata->stream, idata->rawno);
+
+ if(!thrd || !ice) /* can't happen? */
+ return(ice);
+
+ if(!ice->tice){
+ tice = (ICE_S *) fs_get(sizeof(*tice));
+ memset(tice, 0, sizeof(*tice));
+ ice->tice = tice;
+ }
+
+ tice = ice->tice;
+
+ if(!tice)
+ return(ice);
+
+ free_ifield(&tice->ifield);
+
+ ticep = &ice->tice;
+ tice = copy_ice(tice);
+
+ if(space_left >= 3){
+ char to_us, status;
+
+ p = buffer;
+ to_us = to_us_symbol_for_thread(idata->stream, thrd, 1);
+ status = status_symbol_for_thread(idata->stream, thrd, iStatus);
+
+ if((p-buffer)+3 < sizeof(buffer)){
+ p[0] = to_us;
+ p[1] = ' ';
+ p[2] = status;
+ p[3] = '\0';;
+ }
+
+ space_left -= 3;
+
+ ifield = new_ifield(&tice->ifield);
+ ifield->ctype = iStatus;
+ ifield->width = 3;
+ ifield->leftadj = 1;
+ for(i = 0; i < 3; i++){
+ ielem = new_ielem(&ifield->ielem);
+ ielem->freedata = 1;
+ ielem->data = (char *) fs_get(2 * sizeof(char));
+ ielem->data[0] = p[i];
+ ielem->data[1] = '\0';
+ ielem->datalen = 1;
+ set_print_format(ielem, 1, ifield->leftadj);
+ }
+
+ if(pico_usingcolor()){
+ if(to_us == '*'
+ && VAR_IND_IMP_FORE_COLOR && VAR_IND_IMP_BACK_COLOR){
+ ielem = ifield->ielem;
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_IMP_FORE_COLOR,
+ VAR_IND_IMP_BACK_COLOR);
+ if(F_ON(F_COLOR_LINE_IMPORTANT, ps_global))
+ tice->linecolor = new_color_pair(VAR_IND_IMP_FORE_COLOR,
+ VAR_IND_IMP_BACK_COLOR);
+ }
+ else if((to_us == '+' || to_us == '-')
+ && VAR_IND_PLUS_FORE_COLOR && VAR_IND_PLUS_BACK_COLOR){
+ ielem = ifield->ielem;
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_PLUS_FORE_COLOR,
+ VAR_IND_PLUS_BACK_COLOR);
+ }
+
+ if(status == 'D'
+ && VAR_IND_DEL_FORE_COLOR && VAR_IND_DEL_BACK_COLOR){
+ ielem = ifield->ielem->next->next;
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_DEL_FORE_COLOR,
+ VAR_IND_DEL_BACK_COLOR);
+ }
+ else if(status == 'N'
+ && VAR_IND_NEW_FORE_COLOR && VAR_IND_NEW_BACK_COLOR){
+ ielem = ifield->ielem->next->next;
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_NEW_FORE_COLOR,
+ VAR_IND_NEW_BACK_COLOR);
+ }
+ }
+ }
+
+ if(space_left >= thdlen+1){
+ p = buffer;
+ space_left--;
+
+ snprintf(p, sizeof(buffer), "%*.*s", thdlen, thdlen, "");
+ space_left -= thdlen;
+
+ ifield = new_ifield(&tice->ifield);
+ ifield->ctype = iMessNo;
+ ifield->width = thdlen;
+ ifield->leftadj = 0;
+ ielem = new_ielem(&ifield->ielem);
+ ielem->freedata = 1;
+ ielem->data = cpystr(p);
+ ielem->datalen = strlen(p);
+ set_print_format(ielem, ifield->width, ifield->leftadj);
+ }
+
+ if(space_left >= 7){
+
+ p = buffer;
+ space_left--;
+
+ date_str(fetch_date(idata), iDate, 0, p, sizeof(buffer), 0);
+ if(sizeof(buffer) > 6)
+ p[6] = '\0';
+
+ if(strlen(p) < 6 && (sizeof(buffer)) > 6){
+ char *q;
+
+ for(q = p + strlen(p); q < p + 6; q++)
+ *q = ' ';
+ }
+
+ space_left -= 6;
+
+ ifield = new_ifield(&tice->ifield);
+ ifield->ctype = iDate;
+ ifield->width = 6;
+ ifield->leftadj = 1;
+ ielem = new_ielem(&ifield->ielem);
+ ielem->freedata = 1;
+ ielem->data = cpystr(p);
+ ielem->datalen = ifield->width;
+ set_print_format(ielem, ifield->width, ifield->leftadj);
+ }
+
+
+ if(space_left > 3){
+ int from_width, subj_width, bigthread_adjust;
+ long in_thread;
+ char from[BIGWIDTH+1];
+ char tcnt[50];
+
+ space_left--;
+
+ in_thread = count_lflags_in_thread(idata->stream, thrd,
+ ps_global->msgmap, MN_NONE);
+
+ p = buffer;
+ if(in_thread == 1 && THRD_AUTO_VIEW())
+ snprintf(tcnt, sizeof(tcnt), " ");
+ else
+ snprintf(tcnt, sizeof(tcnt), "(%ld)", in_thread);
+
+ bigthread_adjust = MAX(0, strlen(tcnt) - 3);
+
+ /* third of the rest */
+ from_width = MAX((space_left-1)/3 - bigthread_adjust, 1);
+
+ /* the rest */
+ subj_width = space_left - from_width - 1;
+
+ if(strlen(tcnt) > subj_width)
+ tcnt[subj_width] = '\0';
+
+ from[0] = '\0';
+ save_sfstr_func = pith_opt_truncate_sfstr;
+ pith_opt_truncate_sfstr = NULL;
+ from_str(iFromTo, idata, from, sizeof(from), tice);
+ pith_opt_truncate_sfstr = save_sfstr_func;
+
+ ifield = new_ifield(&tice->ifield);
+ ifield->leftadj = 1;
+ ielem = new_ielem(&ifield->ielem);
+ ielem->freedata = 1;
+ ielem->type = eTypeCol;
+ ielem->data = cpystr(from);
+ ielem->datalen = strlen(from);
+ ifield->width = from_width;
+ set_print_format(ielem, ifield->width, ifield->leftadj);
+ ifield->ctype = iFrom;
+ if(from_width > 0 && pico_usingcolor()
+ && VAR_IND_FROM_FORE_COLOR && VAR_IND_FROM_BACK_COLOR){
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_FROM_FORE_COLOR,
+ VAR_IND_FROM_BACK_COLOR);
+ }
+
+ ifield = new_ifield(&tice->ifield);
+ ifield->leftadj = 0;
+ ielem = new_ielem(&ifield->ielem);
+ ielem->freedata = 1;
+ ielem->data = cpystr(tcnt);
+ ielem->datalen = strlen(tcnt);
+ ifield->width = ielem->datalen;
+ set_print_format(ielem, ifield->width, ifield->leftadj);
+ ifield->ctype = iAtt; /* not used, except that it isn't special */
+
+ subj_width -= strlen(tcnt);
+
+ if(subj_width > 0)
+ subj_width--;
+
+ if(subj_width > 0){
+ if(idata->bogus){
+ if(idata->bogus < 2)
+ snprintf(buffer, sizeof(buffer), "%-.*s", BIGWIDTH,
+ _("[ No Message Text Available ]"));
+ }
+ else{
+ buffer[0] = '\0';
+ save_sfstr_func = pith_opt_truncate_sfstr;
+ pith_opt_truncate_sfstr = NULL;
+ subj_str(idata, buffer, sizeof(buffer), NoKW, 0, NULL);
+ pith_opt_truncate_sfstr = save_sfstr_func;
+ }
+
+ ifield = new_ifield(&tice->ifield);
+ ifield->leftadj = 1;
+ ielem = new_ielem(&ifield->ielem);
+ ielem->freedata = 1;
+ ielem->type = eTypeCol;
+ ielem->data = cpystr(buffer);
+ ielem->datalen = strlen(buffer);
+ ifield->width = subj_width;
+ set_print_format(ielem, ifield->width, ifield->leftadj);
+ ifield->ctype = iSubject;
+ if(pico_usingcolor() && VAR_IND_SUBJ_FORE_COLOR && VAR_IND_SUBJ_BACK_COLOR){
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(VAR_IND_SUBJ_FORE_COLOR,
+ VAR_IND_SUBJ_BACK_COLOR);
+ }
+ }
+ }
+ else if(space_left > 1){
+ snprintf(p, sizeof(buffer)-(p-buffer), "%-.*s", space_left-1, " ");
+ ifield = new_ifield(&tice->ifield);
+ ifield->leftadj = 1;
+ ielem = new_ielem(&ifield->ielem);
+ ielem->freedata = 1;
+ ielem->data = cpystr(p);
+ ielem->datalen = strlen(p);
+ ifield->width = space_left-1;
+ set_print_format(ielem, ifield->width, ifield->leftadj);
+ ifield->ctype = iSubject;
+ }
+
+ tice->widths_done = 1;
+ tice->id = ice_hash(tice);
+
+ if(ticep){
+ free_ice(ticep); /* free what is already there */
+ *ticep = tice;
+ }
+
+ return(ice);
+}
+
+
+/*
+ * Print the fields of ice in buf with a single space between fields.
+ *
+ * Args buf -- place to put the line
+ * ice -- the data for the line
+ * msgno -- this is the msgno to be used, blanks if <= 0
+ *
+ * Returns a pointer to buf.
+ */
+char *
+simple_index_line(char *buf, size_t buflen, ICE_S *ice, long int msgno)
+{
+ char *p;
+ IFIELD_S *ifield, *previfield = NULL;
+ IELEM_S *ielem;
+
+ if(!buf)
+ panic("NULL buf in simple_index_line()");
+
+ if(buflen > 0)
+ buf[0] = '\0';
+
+ p = buf;
+
+ if(ice){
+
+ for(ifield = ice->ifield; ifield && p-buf < buflen; ifield = ifield->next){
+
+ /* space between fields */
+ if(ifield != ice->ifield && !(previfield && previfield->ctype == iText))
+ *p++ = ' ';
+
+ /* message number string is generated on the fly */
+ if(ifield->ctype == iMessNo){
+ ielem = ifield->ielem;
+ if(ielem && ielem->datalen >= ifield->width){
+ if(msgno > 0L)
+ snprintf(ielem->data, ielem->datalen+1, "%*.ld", ifield->width, msgno);
+ else
+ snprintf(ielem->data, ielem->datalen+1, "%*.*s", ifield->width, ifield->width, "");
+ }
+ }
+
+ for(ielem = ifield->ielem;
+ ielem && ielem->print_format && p-buf < buflen;
+ ielem = ielem->next){
+ char *src;
+ size_t bytes_added;
+
+ src = ielem->data;
+ bytes_added = utf8_pad_to_width(p, src,
+ buflen-(p-buf) * sizeof(char),
+ ielem->wid, ifield->leftadj);
+ p += bytes_added;
+ }
+
+ previfield = ifield;
+ }
+
+ if(p-buf < buflen)
+ *p = '\0';
+ }
+
+ buf[buflen-1] = '\0';
+
+ return(buf);
+}
+
+
+/*
+ * Look in current mail_stream for matches for messages in the searchset
+ * which match a color rule pattern. Return the color.
+ * The searched bit will be set for all of the messages which match the
+ * first pattern which has a match.
+ *
+ * Args stream -- the mail stream
+ * searchset -- restrict attention to this set of messages
+ * pstate -- The pattern state. On the first call it will be Null.
+ * Null means start over with a new first_pattern.
+ * After that it will be pointing to our local PAT_STATE
+ * so that next_pattern goes to the next one after the
+ * ones we've already checked.
+ *
+ * Returns 0 if no match, 1 if a match.
+ * The color that goes with the matched rule in returned_color.
+ * It may be NULL, which indicates default.
+ */
+int
+get_index_line_color(MAILSTREAM *stream, SEARCHSET *searchset,
+ PAT_STATE **pstate, COLOR_PAIR **returned_color)
+{
+ PAT_S *pat = NULL;
+ long rflags = ROLE_INCOL;
+ COLOR_PAIR *color = NULL;
+ int match = 0;
+ static PAT_STATE localpstate;
+
+ dprint((7, "get_index_line_color\n"));
+
+ if(returned_color)
+ *returned_color = NULL;
+
+ if(*pstate)
+ pat = next_pattern(*pstate);
+ else{
+ *pstate = &localpstate;
+ if(!nonempty_patterns(rflags, *pstate))
+ *pstate = NULL;
+
+ if(*pstate)
+ pat = first_pattern(*pstate);
+ }
+
+ if(*pstate){
+
+ /* Go through the possible roles one at a time until we get a match. */
+ while(!match && pat){
+ if(match_pattern(pat->patgrp, stream, searchset, NULL,
+ get_msg_score, SE_NOSERVER|SE_NOPREFETCH)){
+ if(!pat->action || pat->action->bogus)
+ break;
+
+ match++;
+ if(pat->action && pat->action->incol)
+ color = new_color_pair(pat->action->incol->fg,
+ pat->action->incol->bg);
+ }
+ else
+ pat = next_pattern(*pstate);
+ }
+ }
+
+ if(match && returned_color)
+ *returned_color = color;
+
+ return(match);
+}
+
+
+/*
+ *
+ */
+int
+index_in_overview(MAILSTREAM *stream)
+{
+ INDEX_COL_S *cdesc = NULL;
+
+ if(!(stream->mailbox && IS_REMOTE(stream->mailbox)))
+ return(FALSE); /* no point! */
+
+ if(stream->dtb && stream->dtb->name && !strcmp(stream->dtb->name, "nntp")){
+
+ if(THRD_INDX())
+ return(TRUE);
+
+ for(cdesc = ps_global->index_disp_format;
+ cdesc->ctype != iNothing;
+ cdesc++)
+ switch(cdesc->ctype){
+ case iTo: /* can't be satisfied by XOVER */
+ case iSender: /* ... or specifically handled */
+ case iDescripSize: /* ... in news case */
+ case iAtt:
+ return(FALSE);
+
+ default :
+ break;
+ }
+ }
+
+ return(TRUE);
+}
+
+
+
+/*
+ * fetch_from - called to get a the index entry's "From:" field
+ */
+int
+resent_to_us(INDEXDATA_S *idata)
+{
+ if(!idata->valid_resent_to){
+ static char *fields[] = {"Resent-To", NULL};
+ char *h;
+
+ if(idata->no_fetch){
+ idata->bogus = 1; /* don't do this */
+ return(FALSE);
+ }
+
+ if((h = pine_fetchheader_lines(idata->stream,idata->rawno,NULL,fields)) != NULL){
+ idata->resent_to_us = parsed_resent_to_us(h);
+ fs_give((void **) &h);
+ }
+
+ idata->valid_resent_to = 1;
+ }
+
+ return(idata->resent_to_us);
+}
+
+
+int
+parsed_resent_to_us(char *h)
+{
+ char *p, *q;
+ ADDRESS *addr = NULL;
+ int rv = FALSE;
+
+ if((p = strindex(h, ':')) != NULL){
+ for(q = ++p; (q = strpbrk(q, "\015\012")) != NULL; q++)
+ *q = ' '; /* quash junk */
+
+ rfc822_parse_adrlist(&addr, p, ps_global->maildomain);
+ if(addr){
+ rv = address_is_us(addr, ps_global);
+ mail_free_address(&addr);
+ }
+ }
+
+ return(rv);
+}
+
+
+
+/*
+ * fetch_from - called to get a the index entry's "From:" field
+ */
+ADDRESS *
+fetch_from(INDEXDATA_S *idata)
+{
+ if(idata->no_fetch) /* implies from is valid */
+ return(idata->from);
+ else if(idata->bogus)
+ idata->bogus = 2;
+ else{
+ ENVELOPE *env;
+
+ /* c-client call's just cache access at this point */
+ if((env = pine_mail_fetchenvelope(idata->stream, idata->rawno)) != NULL)
+ return(env->from);
+
+ idata->bogus = 1;
+ }
+
+ return(NULL);
+}
+
+
+/*
+ * fetch_to - called to get a the index entry's "To:" field
+ */
+ADDRESS *
+fetch_to(INDEXDATA_S *idata)
+{
+ if(idata->no_fetch){ /* check for specific validity */
+ if(idata->valid_to)
+ return(idata->to);
+ else
+ idata->bogus = 1; /* can't give 'em what they want */
+ }
+ else if(idata->bogus){
+ idata->bogus = 2; /* elevate bogosity */
+ }
+ else{
+ ENVELOPE *env;
+
+ /* c-client call's just cache access at this point */
+ if((env = pine_mail_fetchenvelope(idata->stream, idata->rawno)) != NULL)
+ return(env->to);
+
+ idata->bogus = 1;
+ }
+
+ return(NULL);
+}
+
+
+/*
+ * fetch_cc - called to get a the index entry's "Cc:" field
+ */
+ADDRESS *
+fetch_cc(INDEXDATA_S *idata)
+{
+ if(idata->no_fetch){ /* check for specific validity */
+ if(idata->valid_cc)
+ return(idata->cc);
+ else
+ idata->bogus = 1; /* can't give 'em what they want */
+ }
+ else if(idata->bogus){
+ idata->bogus = 2; /* elevate bogosity */
+ }
+ else{
+ ENVELOPE *env;
+
+ /* c-client call's just cache access at this point */
+ if((env = pine_mail_fetchenvelope(idata->stream, idata->rawno)) != NULL)
+ return(env->cc);
+
+ idata->bogus = 1;
+ }
+
+ return(NULL);
+}
+
+
+
+/*
+ * fetch_sender - called to get a the index entry's "Sender:" field
+ */
+ADDRESS *
+fetch_sender(INDEXDATA_S *idata)
+{
+ if(idata->no_fetch){ /* check for specific validity */
+ if(idata->valid_sender)
+ return(idata->sender);
+ else
+ idata->bogus = 1; /* can't give 'em what they want */
+ }
+ else if(idata->bogus){
+ idata->bogus = 2; /* elevate bogosity */
+ }
+ else{
+ ENVELOPE *env;
+
+ /* c-client call's just cache access at this point */
+ if((env = pine_mail_fetchenvelope(idata->stream, idata->rawno)) != NULL)
+ return(env->sender);
+
+ idata->bogus = 1;
+ }
+
+ return(NULL);
+}
+
+
+/*
+ * fetch_newsgroups - called to get a the index entry's "Newsgroups:" field
+ */
+char *
+fetch_newsgroups(INDEXDATA_S *idata)
+{
+ if(idata->no_fetch){ /* check for specific validity */
+ if(idata->valid_news)
+ return(idata->newsgroups);
+ else
+ idata->bogus = 1; /* can't give 'em what they want */
+ }
+ else if(idata->bogus){
+ idata->bogus = 2; /* elevate bogosity */
+ }
+ else{
+ ENVELOPE *env;
+
+ /* c-client call's just cache access at this point */
+ if((env = pine_mail_fetchenvelope(idata->stream, idata->rawno)) != NULL)
+ return(env->newsgroups);
+
+ idata->bogus = 1;
+ }
+
+ return(NULL);
+}
+
+
+/*
+ * fetch_subject - called to get at the index entry's "Subject:" field
+ */
+char *
+fetch_subject(INDEXDATA_S *idata)
+{
+ if(idata->no_fetch) /* implies subject is valid */
+ return(idata->subject);
+ else if(idata->bogus)
+ idata->bogus = 2;
+ else{
+ ENVELOPE *env;
+
+ /* c-client call's just cache access at this point */
+ if((env = pine_mail_fetchenvelope(idata->stream, idata->rawno)) != NULL)
+ return(env->subject);
+
+ idata->bogus = 1;
+ }
+
+ return(NULL);
+}
+
+
+/*
+ * Return an allocated copy of the first few characters from the body
+ * of the message for possible use in the index screen.
+ *
+ * Maybe we could figure out some way to do aggregate calls to get
+ * this info for all the lines in view instead of all the one at a
+ * time calls we're doing now.
+ */
+char *
+fetch_firsttext(INDEXDATA_S *idata, int delete_quotes)
+{
+ ENVELOPE *env;
+ BODY *body = NULL;
+ char *firsttext = NULL;
+ STORE_S *so;
+ gf_io_t pc;
+ long partial_fetch_len = 0L;
+ SEARCHSET *ss, **sset;
+
+try_again:
+
+ /*
+ * Prevent wild prefetch, just get the one we're after.
+ * Can we get this somehow in the overview call in build_header_work?
+ */
+ ss = mail_newsearchset();
+ ss->first = idata->rawno;
+ sset = (SEARCHSET **) mail_parameters(idata->stream,
+ GET_FETCHLOOKAHEAD,
+ (void *) idata->stream);
+ if(sset)
+ *sset = ss;
+
+ if((env = pine_mail_fetchstructure(idata->stream, idata->rawno, &body)) != NULL){
+ if(body){
+ char *subtype = NULL;
+ char *partno;
+
+ if((body->type == TYPETEXT
+ && (subtype=body->subtype) && ALLOWED_SUBTYPE(subtype))
+ ||
+ (body->type == TYPEMULTIPART && body->nested.part
+ && body->nested.part->body.type == TYPETEXT
+ && (subtype=body->nested.part->body.subtype)
+ && ALLOWED_SUBTYPE(subtype))
+ ||
+ (body->type == TYPEMULTIPART && body->nested.part
+ && body->nested.part->body.type == TYPEMULTIPART
+ && body->nested.part->body.nested.part
+ && body->nested.part->body.nested.part->body.type == TYPETEXT
+ && (subtype=body->nested.part->body.nested.part->body.subtype)
+ && ALLOWED_SUBTYPE(subtype))){
+
+ if((so = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
+ char buf[1025], *p;
+ unsigned char c;
+ int success;
+ int one_space_done = 0;
+
+ if(partial_fetch_len == 0L){
+ if(subtype && !strucmp(subtype, "html"))
+ partial_fetch_len = 1024L;
+ else if(subtype && !strucmp(subtype, "plain"))
+ partial_fetch_len = delete_quotes ? 128L : 64L;
+ else
+ partial_fetch_len = 256L;
+ }
+
+ if((body->type == TYPETEXT
+ && (subtype=body->subtype) && ALLOWED_SUBTYPE(subtype))
+ ||
+ (body->type == TYPEMULTIPART && body->nested.part
+ && body->nested.part->body.type == TYPETEXT
+ && (subtype=body->nested.part->body.subtype)
+ && ALLOWED_SUBTYPE(subtype)))
+ partno = "1";
+ else
+ partno = "1.1";
+
+ gf_set_so_writec(&pc, so);
+ success = get_body_part_text(idata->stream, body, idata->rawno,
+ partno, partial_fetch_len, pc,
+ NULL, NULL,
+ GBPT_NOINTR | GBPT_PEEK |
+ (delete_quotes ? GBPT_DELQUOTES : 0));
+ gf_clear_so_writec(so);
+
+ if(success){
+ so_seek(so, 0L, 0);
+ p = buf;
+ while(p-buf < sizeof(buf)-1 && so_readc(&c, so)){
+ /* delete leading whitespace */
+ if(p == buf && isspace(c))
+ ;
+ /* and include just one space per run of whitespace */
+ else if(isspace(c)){
+ if(!one_space_done){
+ *p++ = SPACE;
+ one_space_done++;
+ }
+ }
+ else{
+ one_space_done = 0;
+ *p++ = c;
+ }
+ }
+
+ *p = '\0';
+
+ if(p > buf){
+ size_t l;
+
+ l = strlen(buf);
+ l += 100;
+ firsttext = fs_get((l+1) * sizeof(char));
+ firsttext[0] = '\0';
+ iutf8ncpy(firsttext, buf, l);
+ firsttext[l] = '\0';
+ removing_trailing_white_space(firsttext);
+ }
+ }
+
+ so_give(&so);
+
+ /* first if means we didn't fetch all of the data */
+ if(!(success > 1 && success < partial_fetch_len)){
+ if(partial_fetch_len < 4096L
+ && (!firsttext || utf8_width(firsttext) < 50)){
+ if(firsttext)
+ fs_give((void **) &firsttext);
+
+ partial_fetch_len = 4096L;
+ goto try_again;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if(ss)
+ mail_free_searchset(&ss);
+
+ return(firsttext);
+}
+
+
+/*
+ * fetch_date - called to get at the index entry's "Date:" field
+ */
+char *
+fetch_date(INDEXDATA_S *idata)
+{
+ if(idata->no_fetch) /* implies date is valid */
+ return(idata->date);
+ else if(idata->bogus)
+ idata->bogus = 2;
+ else{
+ ENVELOPE *env;
+
+ /* c-client call's just cache access at this point */
+ if((env = pine_mail_fetchenvelope(idata->stream, idata->rawno)) != NULL)
+ return((char *) env->date);
+
+ idata->bogus = 1;
+ }
+
+ return(NULL);
+}
+
+
+/*
+ * fetch_header - called to get at the index entry's "Hdrname:" field
+ */
+char *
+fetch_header(INDEXDATA_S *idata, char *hdrname)
+{
+ if(idata->no_fetch)
+ idata->bogus = 1;
+ else if(idata->bogus)
+ idata->bogus = 2;
+ else{
+ char *h, *p, *q, *decoded, *fields[2];
+ size_t retsize, decsize;
+ char *ret = NULL;
+ unsigned char *decode_buf = NULL;
+
+ fields[0] = hdrname;
+ fields[1] = NULL;
+ if(hdrname && hdrname[0]
+ && (h = pine_fetchheader_lines(idata->stream, idata->rawno,
+ NULL, fields))){
+
+ if(strlen(h) < strlen(hdrname) + 1){
+ fs_give((void **) &h);
+ return(cpystr(""));
+ }
+
+ /* skip "hdrname:" */
+ for(p = h + strlen(hdrname) + 1;
+ *p && isspace((unsigned char)*p); p++)
+ ;
+
+ decsize = (4 * strlen(p)) + 1;
+ decode_buf = (unsigned char *) fs_get(decsize * sizeof(unsigned char));
+ decoded = (char *) rfc1522_decode_to_utf8(decode_buf, decsize, p);
+ p = decoded;
+
+ retsize = strlen(decoded);
+ q = ret = (char *) fs_get((retsize+1) * sizeof(char));
+
+ *q = '\0';
+ while(q-ret < retsize && *p){
+ if(*p == '\015' || *p == '\012')
+ p++;
+ else if(*p == '\t'){
+ *q++ = SPACE;
+ p++;
+ }
+ else
+ *q++ = *p++;
+ }
+
+ *q = '\0';
+
+ fs_give((void **) &h);
+ if(decode_buf)
+ fs_give((void **) &decode_buf);
+
+ return(ret);
+ }
+
+ idata->bogus = 1;
+ }
+
+ return(NULL);
+}
+
+
+/*
+ * fetch_size - called to get at the index entry's "size" field
+ */
+long
+fetch_size(INDEXDATA_S *idata)
+{
+ if(idata->no_fetch) /* implies size is valid */
+ return(idata->size);
+ else if(idata->bogus)
+ idata->bogus = 2;
+ else{
+ MESSAGECACHE *mc;
+
+ if(idata->stream && idata->rawno > 0L
+ && idata->rawno <= idata->stream->nmsgs
+ && (mc = mail_elt(idata->stream, idata->rawno)))
+ return(mc->rfc822_size);
+
+ idata->bogus = 1;
+ }
+
+ return(0L);
+}
+
+
+/*
+ * fetch_body - called to get a the index entry's body structure
+ */
+BODY *
+fetch_body(INDEXDATA_S *idata)
+{
+ BODY *body;
+
+ if(idata->bogus || idata->no_fetch){
+ idata->bogus = 2;
+ return(NULL);
+ }
+
+ if(pine_mail_fetchstructure(idata->stream, idata->rawno, &body))
+ return(body);
+
+ idata->bogus = 1;
+ return(NULL);
+}
+
+
+/*
+ * s is at least size width+1
+ */
+int
+set_index_addr(INDEXDATA_S *idata,
+ char *field,
+ struct mail_address *addr,
+ char *prefix,
+ int width,
+ char *s)
+{
+ ADDRESS *atmp;
+ char *p, *stmp = NULL, *sptr;
+ char *save_personal = NULL;
+ int orig_width;
+
+ s[0] = '\0';
+
+ for(atmp = addr; idata->stream && atmp; atmp = atmp->next)
+ if(atmp->host && atmp->host[0] == '.'){
+ char *pref, *h, *fields[2];
+
+ if(idata->no_fetch){
+ idata->bogus = 1;
+ return(TRUE);
+ }
+
+ fields[0] = field;
+ fields[1] = NULL;
+ if((h = pine_fetchheader_lines(idata->stream, idata->rawno,
+ NULL, fields)) != NULL){
+ if(strlen(h) < strlen(field) + 1){
+ p = h + strlen(h);
+ }
+ else{
+ /* skip "field:" */
+ for(p = h + strlen(field) + 1;
+ *p && isspace((unsigned char)*p); p++)
+ ;
+ }
+
+ orig_width = width;
+ sptr = stmp = (char *) fs_get((orig_width+1) * sizeof(char));
+
+ /* add prefix */
+ for(pref = prefix; pref && *pref; pref++)
+ if(width){
+ *sptr++ = *pref;
+ width--;
+ }
+ else
+ break;
+
+ while(width--)
+ if(*p == '\015' || *p == '\012')
+ p++; /* skip CR LF */
+ else if(!*p)
+ *sptr++ = ' ';
+ else if(*p == '\t'){
+ *sptr++ = ' ';
+ p++;
+ }
+ else
+ *sptr++ = *p++;
+
+ *sptr = '\0'; /* tie off return string */
+
+ if(stmp){
+ iutf8ncpy(s, stmp, orig_width+1);
+ s[orig_width] = '\0';
+ fs_give((void **) &stmp);
+ }
+
+ fs_give((void **) &h);
+ return(TRUE);
+ }
+ /* else fall thru and display what c-client gave us */
+ }
+
+ if(addr && !addr->next /* only one address */
+ && addr->host /* not group syntax */
+ && addr->personal && addr->personal[0]){ /* there is a personal name */
+ char buftmp[MAILTMPLEN];
+ int l;
+
+ if((l = prefix ? strlen(prefix) : 0) != 0)
+ strncpy(s, prefix, width+1);
+
+ snprintf(buftmp, sizeof(buftmp), "%s", addr->personal);
+ p = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
+ SIZEOF_20KBUF, buftmp);
+ removing_leading_and_trailing_white_space(p);
+
+ iutf8ncpy(s + l, p, width - l);
+
+ s[width] = '\0';
+
+ if(*(s+l))
+ return(TRUE);
+ else{
+ save_personal = addr->personal;
+ addr->personal = NULL;
+ }
+ }
+
+ if(addr){
+ char *a_string;
+ int l;
+
+ a_string = addr_list_string(addr, NULL, 0);
+ if(save_personal)
+ addr->personal = save_personal;
+
+ if((l = prefix ? strlen(prefix) : 0) != 0)
+ strncpy(s, prefix, width+1);
+
+ iutf8ncpy(s + l, a_string, width - l);
+ s[width] = '\0';
+
+ fs_give((void **)&a_string);
+
+ return(TRUE);
+ }
+
+ if(save_personal)
+ addr->personal = save_personal;
+
+ return(FALSE);
+}
+
+
+void
+index_data_env(INDEXDATA_S *idata, ENVELOPE *env)
+{
+ if(!env){
+ idata->bogus = 2;
+ return;
+ }
+
+ idata->from = env->from;
+ idata->to = env->to;
+ idata->cc = env->cc;
+ idata->sender = env->sender;
+ idata->subject = env->subject;
+ idata->date = (char *) env->date;
+ idata->newsgroups = env->newsgroups;
+
+ idata->valid_to = 1; /* signal that everythings here */
+ idata->valid_cc = 1;
+ idata->valid_sender = 1;
+ idata->valid_news = 1;
+}
+
+
+/*
+ * Put a string representing the date into str. The source date is
+ * in the string datesrc. The format to be used is in type.
+ * Notice that type is an IndexColType, but really only a subset of
+ * IndexColType types are allowed.
+ *
+ * Args datesrc -- The source date string
+ * type -- What type of output we want
+ * v -- If set, variable width output is ok. (Oct 9 not Oct 9)
+ * str -- Put the answer here.
+ * str_len -- Length of str
+ * monabb_width -- This is a hack to get dates to line up right. For
+ * example, in French (but without accents here)
+ * dec. 21
+ * fevr. 23
+ * mars 7
+ * For this monabb_width would be 5.
+ */
+void
+date_str(char *datesrc, IndexColType type, int v, char *str, size_t str_len,
+ int monabb_width)
+{
+ char year4[5], /* 4 digit year */
+ yearzero[3], /* zero padded, 2-digit year */
+ monzero[3], /* zero padded, 2-digit month */
+ mon[3], /* 1 or 2-digit month, no pad */
+ dayzero[3], /* zero padded, 2-digit day */
+ day[3], /* 1 or 2-digit day, no pad */
+ dayord[3], /* 2-letter ordinal label */
+ monabb[10], /* 3-letter month abbrev */
+ /* actually maybe not 3 if localized */
+ hour24[3], /* 2-digit, 24 hour clock hour */
+ hour12[3], /* 12 hour clock hour, no pad */
+ minzero[3], /* zero padded, 2-digit minutes */
+ timezone[6]; /* timezone, like -0800 or +... */
+ int hr12;
+ int curtype, lastmonthtype, lastyeartype, preftype;
+ int sdatetimetype, sdatetime24type;
+ struct date d;
+#define TODAYSTR N_("Today")
+
+ curtype = (type == iCurDate ||
+ type == iCurDateIso ||
+ type == iCurDateIsoS ||
+ type == iCurPrefDate ||
+ type == iCurPrefDateTime ||
+ type == iCurPrefTime ||
+ type == iCurTime24 ||
+ type == iCurTime12 ||
+ type == iCurDay ||
+ type == iCurDay2Digit ||
+ type == iCurDayOfWeek ||
+ type == iCurDayOfWeekAbb ||
+ type == iCurMon ||
+ type == iCurMon2Digit ||
+ type == iCurMonLong ||
+ type == iCurMonAbb ||
+ type == iCurYear ||
+ type == iCurYear2Digit);
+ lastmonthtype = (type == iLstMon ||
+ type == iLstMon2Digit ||
+ type == iLstMonLong ||
+ type == iLstMonAbb ||
+ type == iLstMonYear ||
+ type == iLstMonYear2Digit);
+ lastyeartype = (type == iLstYear ||
+ type == iLstYear2Digit);
+ sdatetimetype = (type == iSDateTime ||
+ type == iSDateTimeIso ||
+ type == iSDateTimeIsoS ||
+ type == iSDateTimeS1 ||
+ type == iSDateTimeS2 ||
+ type == iSDateTimeS3 ||
+ type == iSDateTimeS4 ||
+ type == iSDateTime24 ||
+ type == iSDateTimeIso24 ||
+ type == iSDateTimeIsoS24 ||
+ type == iSDateTimeS124 ||
+ type == iSDateTimeS224 ||
+ type == iSDateTimeS324 ||
+ type == iSDateTimeS424);
+ sdatetime24type = (type == iSDateTime24 ||
+ type == iSDateTimeIso24 ||
+ type == iSDateTimeIsoS24 ||
+ type == iSDateTimeS124 ||
+ type == iSDateTimeS224 ||
+ type == iSDateTimeS324 ||
+ type == iSDateTimeS424);
+ preftype = (type == iPrefDate ||
+ type == iPrefDateTime ||
+ type == iPrefTime ||
+ type == iCurPrefDate ||
+ type == iCurPrefDateTime ||
+ type == iCurPrefTime);
+ if(str_len > 0)
+ str[0] = '\0';
+
+ if(!(datesrc && datesrc[0]) && !(curtype || lastmonthtype || lastyeartype))
+ return;
+
+ if(curtype || lastmonthtype || lastyeartype){
+ char dbuf[200];
+
+ rfc822_date(dbuf);
+ parse_date(dbuf, &d);
+
+ if(lastyeartype)
+ d.year--;
+ else if(lastmonthtype){
+ d.month--;
+ if(d.month <= 0){
+ d.month = 12;
+ d.year--;
+ }
+ }
+ }
+ else{
+ parse_date(F_ON(F_DATES_TO_LOCAL,ps_global)
+ ? convert_date_to_local(datesrc) : datesrc, &d);
+ if(d.year == -1 || d.month == -1 || d.day == -1){
+ sdatetimetype = 0;
+ sdatetime24type = 0;
+ preftype = 0;
+ switch(type){
+ case iSDate: case iSDateTime: case iSDateTime24:
+ type = iS1Date;
+ break;
+
+ case iSDateIso: case iSDateTimeIso: case iSDateTimeIso24:
+ case iPrefDate: case iPrefTime: case iPrefDateTime:
+ type = iDateIso;
+ break;
+
+ case iSDateIsoS: case iSDateTimeIsoS: case iSDateTimeIsoS24:
+ type = iDateIsoS;
+ break;
+
+ case iSDateS1: case iSDateTimeS1: case iSDateTimeS124:
+ type = iS1Date;
+ break;
+
+ case iSDateS2: case iSDateTimeS2: case iSDateTimeS224:
+ type = iS1Date;
+ break;
+
+ case iSDateS3: case iSDateTimeS3: case iSDateTimeS324:
+ type = iS1Date;
+ break;
+
+ case iSDateS4: case iSDateTimeS4: case iSDateTimeS424:
+ type = iS1Date;
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ /* some special ones to start with */
+ if(preftype){
+ struct tm tm, *tmptr = NULL;
+ time_t now;
+
+ /*
+ * Make sure we get the right one if we're using current time.
+ */
+ if(curtype){
+ now = time((time_t *) 0);
+ if(now != (time_t) -1)
+ tmptr = localtime(&now);
+ }
+
+ if(!tmptr){
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_year = MIN(MAX(d.year-1900, 0), 2000);
+ tm.tm_mon = MIN(MAX(d.month-1, 0), 11);
+ tm.tm_mday = MIN(MAX(d.day, 1), 31);
+ tm.tm_hour = MIN(MAX(d.hour, 0), 23);
+ tm.tm_min = MIN(MAX(d.minute, 0), 59);
+ tm.tm_wday = MIN(MAX(d.wkday, 0), 6);
+ tmptr = &tm;
+ }
+
+ switch(type){
+ case iPrefDate:
+ case iCurPrefDate:
+ our_strftime(str, str_len, "%x", tmptr);
+ break;
+ case iPrefTime:
+ case iCurPrefTime:
+ our_strftime(str, str_len, "%X", tmptr);
+ break;
+ case iPrefDateTime:
+ case iCurPrefDateTime:
+ our_strftime(str, str_len, "%c", tmptr);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ return;
+ }
+
+ strncpy(monabb, (d.month > 0 && d.month < 13)
+ ? month_abbrev_locale(d.month) : "", sizeof(monabb));
+ monabb[sizeof(monabb)-1] = '\0';
+
+ strncpy(mon, (d.month > 0 && d.month < 13)
+ ? int2string(d.month) : "", sizeof(mon));
+ mon[sizeof(mon)-1] = '\0';
+
+ strncpy(day, (d.day > 0 && d.day < 32)
+ ? int2string(d.day) : "", sizeof(day));
+ day[sizeof(day)-1] = '\0';
+
+ strncpy(dayord,
+ (d.day <= 0 || d.day > 31) ? "" :
+ (d.day == 1 || d.day == 21 || d.day == 31) ? "st" :
+ (d.day == 2 || d.day == 22 ) ? "nd" :
+ (d.day == 3 || d.day == 23 ) ? "rd" : "th", sizeof(dayord));
+
+ dayord[sizeof(dayord)-1] = '\0';
+
+ strncpy(year4, (d.year >= 1000 && d.year < 10000)
+ ? int2string(d.year) : "????", sizeof(year4));
+ year4[sizeof(year4)-1] = '\0';
+
+ if(d.year >= 0){
+ if((d.year % 100) < 10){
+ yearzero[0] = '0';
+ strncpy(yearzero+1, int2string(d.year % 100), sizeof(yearzero)-1);
+ }
+ else
+ strncpy(yearzero, int2string(d.year % 100), sizeof(yearzero));
+ }
+ else
+ strncpy(yearzero, "??", sizeof(yearzero));
+
+ yearzero[sizeof(yearzero)-1] = '\0';
+
+ if(d.month > 0 && d.month < 10){
+ monzero[0] = '0';
+ strncpy(monzero+1, int2string(d.month), sizeof(monzero)-1);
+ }
+ else if(d.month >= 10 && d.month <= 12)
+ strncpy(monzero, int2string(d.month), sizeof(monzero));
+ else
+ strncpy(monzero, "??", sizeof(monzero));
+
+ monzero[sizeof(monzero)-1] = '\0';
+
+ if(d.day > 0 && d.day < 10){
+ dayzero[0] = '0';
+ strncpy(dayzero+1, int2string(d.day), sizeof(dayzero)-1);
+ }
+ else if(d.day >= 10 && d.day <= 31)
+ strncpy(dayzero, int2string(d.day), sizeof(dayzero));
+ else
+ strncpy(dayzero, "??", sizeof(dayzero));
+
+ dayzero[sizeof(dayzero)-1] = '\0';
+
+ hr12 = (d.hour == 0) ? 12 :
+ (d.hour > 12) ? (d.hour - 12) : d.hour;
+ hour12[0] = '\0';
+ if(hr12 > 0 && hr12 <= 12)
+ strncpy(hour12, int2string(hr12), sizeof(hour12));
+
+ hour12[sizeof(hour12)-1] = '\0';
+
+ hour24[0] = '\0';
+ if(d.hour >= 0 && d.hour < 10){
+ hour24[0] = '0';
+ strncpy(hour24+1, int2string(d.hour), sizeof(hour24)-1);
+ }
+ else if(d.hour >= 10 && d.hour < 24)
+ strncpy(hour24, int2string(d.hour), sizeof(hour24));
+
+ hour24[sizeof(hour24)-1] = '\0';
+
+ minzero[0] = '\0';
+ if(d.minute >= 0 && d.minute < 10){
+ minzero[0] = '0';
+ strncpy(minzero+1, int2string(d.minute), sizeof(minzero)-1);
+ }
+ else if(d.minute >= 10 && d.minute <= 60)
+ strncpy(minzero, int2string(d.minute), sizeof(minzero));
+
+ minzero[sizeof(minzero)-1] = '\0';
+
+ if(sizeof(timezone) > 5){
+ if(d.hours_off_gmt <= 0){
+ timezone[0] = '-';
+ d.hours_off_gmt *= -1;
+ d.min_off_gmt *= -1;
+ }
+ else
+ timezone[0] = '+';
+
+ timezone[1] = '\0';
+ if(d.hours_off_gmt >= 0 && d.hours_off_gmt < 10){
+ timezone[1] = '0';
+ strncpy(timezone+2, int2string(d.hours_off_gmt), sizeof(timezone)-2);
+ }
+ else if(d.hours_off_gmt >= 10 && d.hours_off_gmt < 24)
+ strncpy(timezone+1, int2string(d.hours_off_gmt), sizeof(timezone)-1);
+ else{
+ timezone[1] = '0';
+ timezone[2] = '0';
+ }
+
+ timezone[3] = '\0';
+ if(d.min_off_gmt >= 0 && d.min_off_gmt < 10){
+ timezone[3] = '0';
+ strncpy(timezone+4, int2string(d.min_off_gmt), sizeof(timezone)-4);
+ }
+ else if(d.min_off_gmt >= 10 && d.min_off_gmt <= 60)
+ strncpy(timezone+3, int2string(d.min_off_gmt), sizeof(timezone)-3);
+ else{
+ timezone[3] = '0';
+ timezone[4] = '0';
+ }
+
+ timezone[5] = '\0';
+ timezone[sizeof(timezone)-1] = '\0';
+ }
+
+ switch(type){
+ case iRDate:
+ /* this one is not locale-specific */
+ snprintf(str, str_len, "%s%s%s %s %s",
+ (d.wkday != -1) ? day_abbrev(d.wkday) : "",
+ (d.wkday != -1) ? ", " : "",
+ day,
+ (d.month > 0 && d.month < 13) ? month_abbrev(d.month) : "",
+ year4);
+ break;
+ case iDayOfWeekAbb:
+ case iCurDayOfWeekAbb:
+ strncpy(str, (d.wkday >= 0 && d.wkday <= 6) ? day_abbrev_locale(d.wkday) : "", str_len);
+ str[str_len-1] = '\0';
+ break;
+ case iDayOfWeek:
+ case iCurDayOfWeek:
+ strncpy(str, (d.wkday >= 0 && d.wkday <= 6) ? day_name_locale(d.wkday) : "", str_len);
+ str[str_len-1] = '\0';
+ break;
+ case iYear:
+ case iCurYear:
+ case iLstYear:
+ case iLstMonYear:
+ strncpy(str, year4, str_len);
+ break;
+ case iDay2Digit:
+ case iCurDay2Digit:
+ strncpy(str, dayzero, str_len);
+ break;
+ case iMon2Digit:
+ case iCurMon2Digit:
+ case iLstMon2Digit:
+ strncpy(str, monzero, str_len);
+ break;
+ case iYear2Digit:
+ case iCurYear2Digit:
+ case iLstYear2Digit:
+ case iLstMonYear2Digit:
+ strncpy(str, yearzero, str_len);
+ break;
+ case iTimezone:
+ strncpy(str, timezone, str_len);
+ break;
+ case iDay:
+ case iCurDay:
+ strncpy(str, day, str_len);
+ break;
+ case iDayOrdinal:
+ snprintf(str, str_len, "%s%s", day, dayord);
+ break;
+ case iMon:
+ case iCurMon:
+ case iLstMon:
+ if(d.month > 0 && d.month <= 12)
+ strncpy(str, int2string(d.month), str_len);
+
+ break;
+ case iMonAbb:
+ case iCurMonAbb:
+ case iLstMonAbb:
+ strncpy(str, monabb, str_len);
+ break;
+ case iMonLong:
+ case iCurMonLong:
+ case iLstMonLong:
+ strncpy(str, (d.month > 0 && d.month < 13)
+ ? month_name_locale(d.month) : "", str_len);
+ break;
+ case iDate:
+ case iCurDate:
+ if(v)
+ snprintf(str, str_len, "%s%s%s", monabb, (monabb[0] && day[0]) ? " " : "", day);
+ else{
+ if(monabb_width > 0)
+ utf8_snprintf(str, str_len, "%-*.*w %2s",
+ monabb_width, monabb_width, monabb, day);
+ else
+ snprintf(str, str_len, "%s %2s", monabb, day);
+ }
+
+ break;
+ case iLDate:
+ if(v)
+ snprintf(str, str_len, "%s%s%s%s%s", monabb,
+ (monabb[0] && day[0]) ? " " : "", day,
+ ((monabb[0] || day[0]) && year4[0]) ? ", " : "",
+ year4);
+ else{
+ if(monabb_width > 0)
+ utf8_snprintf(str, str_len, "%-*.*w %2s%c %4s",
+ monabb_width, monabb_width,
+ monabb, day,
+ (monabb[0] && day[0] && year4[0]) ? ',' : ' ', year4);
+ else
+ snprintf(str, str_len, "%s %2s%c %4s", monabb, day,
+ (monabb[0] && day[0] && year4[0]) ? ',' : ' ',
+ year4);
+ }
+
+ break;
+ case iS1Date:
+ case iS2Date:
+ case iS3Date:
+ case iS4Date:
+ case iDateIso:
+ case iDateIsoS:
+ case iCurDateIso:
+ case iCurDateIsoS:
+ if(monzero[0] == '?' && dayzero[0] == '?' &&
+ yearzero[0] == '?')
+ snprintf(str, str_len, "%8s", "");
+ else{
+ switch(type){
+ case iS1Date:
+ snprintf(str, str_len, "%2s/%2s/%2s",
+ monzero, dayzero, yearzero);
+ break;
+ case iS2Date:
+ snprintf(str, str_len, "%2s/%2s/%2s",
+ dayzero, monzero, yearzero);
+ break;
+ case iS3Date:
+ snprintf(str, str_len, "%2s.%2s.%2s",
+ dayzero, monzero, yearzero);
+ break;
+ case iS4Date:
+ snprintf(str, str_len, "%2s.%2s.%2s",
+ yearzero, monzero, dayzero);
+ break;
+ case iDateIsoS:
+ case iCurDateIsoS:
+ snprintf(str, str_len, "%2s-%2s-%2s",
+ yearzero, monzero, dayzero);
+ break;
+ case iDateIso:
+ case iCurDateIso:
+ snprintf(str, str_len, "%4s-%2s-%2s",
+ year4, monzero, dayzero);
+ break;
+ default:
+ break;
+ }
+ }
+
+ break;
+ case iTime24:
+ case iCurTime24:
+ snprintf(str, str_len, "%2s%c%2s",
+ (hour24[0] && minzero[0]) ? hour24 : "",
+ (hour24[0] && minzero[0]) ? ':' : ' ',
+ (hour24[0] && minzero[0]) ? minzero : "");
+ break;
+ case iTime12:
+ case iCurTime12:
+ snprintf(str, str_len, "%s%c%2s%s",
+ (hour12[0] && minzero[0]) ? hour12 : "",
+ (hour12[0] && minzero[0]) ? ':' : ' ',
+ (hour12[0] && minzero[0]) ? minzero : "",
+ (hour12[0] && minzero[0] && d.hour < 12) ? "am" :
+ (hour12[0] && minzero[0] && d.hour >= 12) ? "pm" :
+ " ");
+ break;
+ case iSDate: case iSDateIso: case iSDateIsoS:
+ case iSDateS1: case iSDateS2: case iSDateS3: case iSDateS4:
+ case iSDateTime: case iSDateTimeIso: case iSDateTimeIsoS:
+ case iSDateTimeS1: case iSDateTimeS2: case iSDateTimeS3: case iSDateTimeS4:
+ case iSDateTime24: case iSDateTimeIso24: case iSDateTimeIsoS24:
+ case iSDateTimeS124: case iSDateTimeS224: case iSDateTimeS324: case iSDateTimeS424:
+ { struct date now, last_day;
+ char dbuf[200];
+ int msg_day_of_year, now_day_of_year, today;
+ int diff, ydiff, last_day_of_year;
+
+ rfc822_date(dbuf);
+ parse_date(dbuf, &now);
+ today = day_of_week(&now) + 7;
+
+ if(today >= 0+7 && today <= 6+7){
+ now_day_of_year = day_of_year(&now);
+ msg_day_of_year = day_of_year(&d);
+ ydiff = now.year - d.year;
+
+ if(msg_day_of_year == -1)
+ diff = -100;
+ else if(ydiff == 0)
+ diff = now_day_of_year - msg_day_of_year;
+ else if(ydiff == 1){
+ last_day = d;
+ last_day.month = 12;
+ last_day.day = 31;
+ last_day_of_year = day_of_year(&last_day);
+
+ diff = now_day_of_year +
+ (last_day_of_year - msg_day_of_year);
+ }
+ else if(ydiff == -1){
+ last_day = now;
+ last_day.month = 12;
+ last_day.day = 31;
+ last_day_of_year = day_of_year(&last_day);
+
+ diff = -1 * (msg_day_of_year +
+ (last_day_of_year - now_day_of_year));
+ }
+ else if(ydiff > 1)
+ diff = 100;
+ else
+ diff = -100;
+
+ if(diff == 0)
+ strncpy(str, _(TODAYSTR), str_len);
+ else if(diff == 1)
+ strncpy(str, _("Yesterday"), str_len);
+ else if(diff > 1 && diff < 7)
+ snprintf(str, str_len, "%s", day_name_locale((today - diff) % 7));
+ else if(diff == -1)
+ strncpy(str, _("Tomorrow"), str_len);
+ else if(diff < -1 && diff > -7)
+ snprintf(str, str_len, _("Next %.3s!"),
+ day_name_locale((today - diff) % 7));
+ else if(diff > 0
+ && (ydiff == 0
+ || (ydiff == 1 && 12 + now.month - d.month < 6))){
+ if(v)
+ snprintf(str, str_len, "%s%s%s", monabb,
+ (monabb[0] && day[0]) ? " " : "", day);
+ else{
+ if(monabb_width > 0)
+ utf8_snprintf(str, str_len, "%-*.*w %2s",
+ monabb_width, monabb_width, monabb, day);
+ else
+ snprintf(str, str_len, "%s %2s", monabb, day);
+ }
+ }
+ else{
+ if(msg_day_of_year == -1 && (type == iSDate || type == iSDateTime))
+ type = iSDateTimeIsoS;
+
+ switch(type){
+ case iSDate: case iSDateTime: case iSDateTime24:
+ {
+ struct tm tm;
+
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_year = MIN(MAX(d.year-1900, 0), 2000);
+ tm.tm_mon = MIN(MAX(d.month-1, 0), 11);
+ tm.tm_mday = MIN(MAX(d.day, 1), 31);
+ tm.tm_hour = MIN(MAX(d.hour, 0), 23);
+ tm.tm_min = MIN(MAX(d.minute, 0), 59);
+ our_strftime(str, str_len, "%x", &tm);
+ }
+
+ break;
+ case iSDateS1: case iSDateTimeS1: case iSDateTimeS124:
+ if(v)
+ snprintf(str, str_len, "%s/%s/%s%s", mon, day, yearzero,
+ diff < 0 ? "!" : "");
+ else
+ snprintf(str, str_len, "%s%s/%s/%s%s",
+ (mon[0] && mon[1]) ? "" : " ",
+ mon, dayzero, yearzero,
+ diff < 0 ? "!" : "");
+ break;
+ case iSDateS2: case iSDateTimeS2: case iSDateTimeS224:
+ if(v)
+ snprintf(str, str_len, "%s/%s/%s%s", day, mon, yearzero,
+ diff < 0 ? "!" : "");
+ else
+ snprintf(str, str_len, "%s%s/%s/%s%s",
+ (day[0] && day[1]) ? "" : " ",
+ day, monzero, yearzero,
+ diff < 0 ? "!" : "");
+ break;
+ case iSDateS3: case iSDateTimeS3: case iSDateTimeS324:
+ if(v)
+ snprintf(str, str_len, "%s.%s.%s%s", day, mon, yearzero,
+ diff < 0 ? "!" : "");
+ else
+ snprintf(str, str_len, "%s%s.%s.%s%s",
+ (day[0] && day[1]) ? "" : " ",
+ day, monzero, yearzero,
+ diff < 0 ? "!" : "");
+ break;
+ case iSDateS4: case iSDateTimeS4: case iSDateTimeS424:
+ if(v)
+ snprintf(str, str_len, "%s.%s.%s%s",
+ yearzero, monzero, dayzero,
+ diff < 0 ? "!" : "");
+ else
+ snprintf(str, str_len, "%s.%s.%s%s",
+ yearzero, monzero, dayzero,
+ diff < 0 ? "!" : "");
+ break;
+ case iSDateIsoS: case iSDateTimeIsoS: case iSDateTimeIsoS24:
+ snprintf(str, str_len, "%2s-%2s-%2s",
+ yearzero, monzero, dayzero);
+ break;
+ case iSDateIso: case iSDateTimeIso: case iSDateTimeIso24:
+ snprintf(str, str_len, "%4s-%2s-%2s",
+ year4, monzero, dayzero);
+ break;
+ default:
+ break;
+ }
+ }
+
+ }
+ else{
+ if(v)
+ snprintf(str, str_len, "%s%s%s", monabb,
+ (monabb[0] && day[0]) ? " " : "", day);
+ else{
+ if(monabb_width > 0)
+ utf8_snprintf(str, str_len, "%-*.*w %2s",
+ monabb_width, monabb_width, monabb, day);
+ else
+ snprintf(str, str_len, "%s %2s", monabb, day);
+ }
+ }
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ str[str_len-1] = '\0';
+
+ if(type == iSTime ||
+ (sdatetimetype && !strcmp(str, _(TODAYSTR)))){
+ struct date now, last_day;
+ char dbuf[200], *Ddd, *ampm;
+ int daydiff;
+
+ str[0] = '\0';
+ rfc822_date(dbuf);
+ parse_date(dbuf, &now);
+
+ /* Figure out if message date lands in the past week */
+
+ /* (if message dated this month or last month...) */
+ if((d.year == now.year && d.month >= now.month - 1) ||
+ (d.year == now.year - 1 && d.month == 12 && now.month == 1)){
+
+ daydiff = day_of_year(&now) - day_of_year(&d);
+
+ /*
+ * If msg in end of last year (and we're in first bit of "this"
+ * year), diff will be backwards; fix up by adding number of days
+ * in last year (usually 365, but occasionally 366)...
+ */
+ if(d.year == now.year - 1){
+ last_day = d;
+ last_day.month = 12;
+ last_day.day = 31;
+
+ daydiff += day_of_year(&last_day);
+ }
+ }
+ else
+ daydiff = -100; /* comfortably out of range (of past week) */
+
+ /* Build 2-digit hour and am/pm indicator, used below */
+
+ if(d.hour >= 0 && d.hour < 24){
+ snprintf(hour12, sizeof(hour12), "%02d", (d.hour % 12 == 0) ? 12 : d.hour % 12);
+ ampm = (d.hour < 12) ? "am" : "pm";
+ snprintf(hour24, sizeof(hour24), "%02d", d.hour);
+ }
+ else{
+ strncpy(hour12, "??", sizeof(hour12));
+ hour12[sizeof(hour12)-1] = '\0';
+ ampm = "__";
+ strncpy(hour24, "??", sizeof(hour24));
+ hour24[sizeof(hour24)-1] = '\0';
+ }
+
+ /* Build date/time in str, in format similar to that used by w(1) */
+
+ if(daydiff == 0){ /* If date is today, "HH:MMap" */
+ if(d.minute >= 0 && d.minute < 60)
+ snprintf(minzero, sizeof(minzero), "%02d", d.minute);
+ else{
+ strncpy(minzero, "??", sizeof(minzero));
+ minzero[sizeof(minzero)-1] = '\0';
+ }
+
+ snprintf(str, str_len, "%s:%s%s", sdatetime24type ? hour24 : hour12,
+ minzero, sdatetime24type ? "" : ampm);
+ }
+ else if(daydiff >= 1 && daydiff < 6){ /* If <1wk ago, "DddHHap" */
+
+ if(d.month >= 1 && d.day >= 1 && d.year >= 0 &&
+ d.month <= 12 && d.day <= 31 && d.year <= 9999)
+ Ddd = day_abbrev_locale(day_of_week(&d));
+ else
+ Ddd = "???";
+
+ snprintf(str, str_len, "%s%s%s", Ddd, hour12, ampm);
+ }
+ else{ /* date is old or future, "ddMmmyy" */
+ strncpy(monabb, (d.month >= 1 && d.month <= 12)
+ ? month_abbrev_locale(d.month) : "???", sizeof(monabb));
+ monabb[sizeof(monabb)-1] = '\0';
+
+ if(d.day >= 1 && d.day <= 31)
+ snprintf(dayzero, sizeof(dayzero), "%02d", d.day);
+ else{
+ strncpy(dayzero, "??", sizeof(dayzero));
+ dayzero[sizeof(dayzero)-1] = '\0';
+ }
+
+ if(d.year >= 0 && d.year <= 9999)
+ snprintf(yearzero, sizeof(yearzero), "%02d", d.year % 100);
+ else{
+ strncpy(yearzero, "??", sizeof(yearzero));
+ yearzero[sizeof(yearzero)-1] = '\0';
+ }
+
+ snprintf(str, str_len, "%s%s%s", dayzero, monabb, yearzero);
+ }
+
+ if(str[0] == '0'){ /* leading 0 (date|hour) elided or blanked */
+ if(v)
+ memmove(str, str + 1, strlen(str));
+ else
+ str[0] = ' ';
+ }
+ }
+}
+
+
+/*
+ * Format a string representing the keywords into ice.
+ *
+ * This needs to be done in UTF-8, which may be tricky since it isn't labelled.
+ *
+ * Args idata -- which message?
+ * kwtype -- keywords or kw initials
+ * ice -- index cache entry for message
+ */
+void
+key_str(INDEXDATA_S *idata, SubjKW kwtype, ICE_S *ice)
+{
+ int firstone = 1;
+ KEYWORD_S *kw;
+ char *word;
+ COLOR_PAIR *color = NULL;
+ SPEC_COLOR_S *sc = ps_global->kw_colors;
+ IELEM_S *ielem = NULL;
+ IFIELD_S *ourifield = NULL;
+
+ if(ice && ice->ifield){
+ /* move to last ifield, the one we're working */
+ for(ourifield = ice->ifield;
+ ourifield && ourifield->next;
+ ourifield = ourifield->next)
+ ;
+ }
+
+ if(!ourifield)
+ return;
+
+ if(kwtype == KWInit){
+ for(kw = ps_global->keywords; kw; kw = kw->next){
+ if(user_flag_is_set(idata->stream, idata->rawno, kw->kw)){
+ word = (kw->nick && kw->nick[0]) ? kw->nick :
+ (kw->kw && kw->kw[0]) ? kw->kw : "";
+
+ /*
+ * Pick off the first initial. Since word is UTF-8 it may
+ * take more than one byte for the first initial.
+ */
+
+ if(word && word[0]){
+ UCS ucs;
+ unsigned long remaining_octets;
+ unsigned char *inputp;
+
+ remaining_octets = strlen(word);
+ inputp = (unsigned char *) word;
+ ucs = (UCS) utf8_get(&inputp, &remaining_octets);
+ if(!(ucs & U8G_ERROR || ucs == UBOGON)){
+ ielem = new_ielem(&ourifield->ielem);
+ ielem->freedata = 1;
+ ielem->datalen = (unsigned) (inputp - (unsigned char *) word);
+ ielem->data = (char *) fs_get((ielem->datalen + 1) * sizeof(char));
+ strncpy(ielem->data, word, ielem->datalen);
+ ielem->data[ielem->datalen] = '\0';
+
+ if(pico_usingcolor()
+ && ((kw->nick && kw->nick[0]
+ && (color=hdr_color(kw->nick,NULL,sc)))
+ || (kw->kw && kw->kw[0]
+ && (color=hdr_color(kw->kw,NULL,sc))))){
+ ielem->color = color;
+ color = NULL;
+ }
+ }
+ }
+
+ if(color)
+ free_color_pair(&color);
+ }
+ }
+ }
+ else if(kwtype == KW){
+ for(kw = ps_global->keywords; kw; kw = kw->next){
+ if(user_flag_is_set(idata->stream, idata->rawno, kw->kw)){
+
+ if(!firstone){
+ ielem = new_ielem(&ourifield->ielem);
+ ielem->freedata = 1;
+ ielem->data = cpystr(" ");
+ ielem->datalen = 1;
+ }
+
+ firstone = 0;
+
+ word = (kw->nick && kw->nick[0]) ? kw->nick :
+ (kw->kw && kw->kw[0]) ? kw->kw : "";
+
+ if(word[0]){
+ ielem = new_ielem(&ourifield->ielem);
+ ielem->freedata = 1;
+ ielem->data = cpystr(word);
+ ielem->datalen = strlen(word);
+
+ if(pico_usingcolor()
+ && ((kw->nick && kw->nick[0]
+ && (color=hdr_color(kw->nick,NULL,sc)))
+ || (kw->kw && kw->kw[0]
+ && (color=hdr_color(kw->kw,NULL,sc))))){
+ ielem->color = color;
+ color = NULL;
+ }
+ }
+
+ if(color)
+ free_color_pair(&color);
+ }
+ }
+ }
+
+ /*
+ * If we're coloring some of the fields then add a dummy field
+ * at the end that can soak up the rest of the space after the last
+ * colored keyword. Otherwise, the last one's color will extend to
+ * the end of the field.
+ */
+ if(pico_usingcolor()){
+ ielem = new_ielem(&ourifield->ielem);
+ ielem->freedata = 1;
+ ielem->data = cpystr(" ");
+ ielem->datalen = 1;
+ }
+
+ ourifield->leftadj = 1;
+ set_ielem_widths_in_field(ourifield);
+}
+
+
+void
+prio_str(INDEXDATA_S *idata, IndexColType ctype, ICE_S *ice)
+{
+ IFIELD_S *ourifield = NULL;
+ IELEM_S *ielem = NULL;
+ char *hdrval;
+ PRIORITY_S *p;
+ int v;
+
+ if(ice && ice->ifield){
+ /* move to last ifield, the one we're working */
+ for(ourifield = ice->ifield;
+ ourifield && ourifield->next;
+ ourifield = ourifield->next)
+ ;
+ }
+
+ if(!ourifield)
+ return;
+
+ hdrval = fetch_header(idata, PRIORITYNAME);
+
+ if(hdrval && hdrval[0] && isdigit(hdrval[0])){
+ v = atoi(hdrval);
+ if(v >= 1 && v <= 5 && v != 3){
+
+ ielem = new_ielem(&ourifield->ielem);
+ ielem->freedata = 1;
+
+ switch(ctype){
+ case iPrio:
+ ielem->data = (char *) fs_get(2 * sizeof(char));
+ ielem->data[0] = hdrval[0];
+ ielem->data[1] = '\0';
+ break;
+
+ case iPrioAlpha:
+ for(p = priorities; p && p->desc; p++)
+ if(p->val == v)
+ break;
+
+ if(p && p->desc)
+ ielem->data = cpystr(p->desc);
+
+ break;
+
+ case iPrioBang:
+ ielem->data = (char *) fs_get(2 * sizeof(char));
+ ielem->data[0] = '\0';
+ switch(v){
+ case 1: case 2:
+ ielem->data[0] = '!';
+ break;
+
+ case 4: case 5:
+ /*
+ * We could put a Unicode downarrow in here but
+ * we have no way of knowing if the user's font
+ * will have it (I think).
+ */
+ ielem->data[0] = 'v';
+ break;
+ }
+
+ ielem->data[1] = '\0';
+
+ break;
+
+ default:
+ panic("Unhandled case in prio_str");
+ break;
+ }
+
+ if(!ielem->data)
+ ielem->data = cpystr("");
+
+ if(ielem && ielem->data)
+ ielem->datalen = strlen(ielem->data);
+
+ if((v == 1 || v == 2) && pico_usingcolor()
+ && ps_global->VAR_IND_HIPRI_FORE_COLOR
+ && ps_global->VAR_IND_HIPRI_BACK_COLOR){
+ ielem->type = eTypeCol;
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(ps_global->VAR_IND_HIPRI_FORE_COLOR, ps_global->VAR_IND_HIPRI_BACK_COLOR);
+ }
+ else if((v == 4 || v == 5) && pico_usingcolor()
+ && ps_global->VAR_IND_LOPRI_FORE_COLOR
+ && ps_global->VAR_IND_LOPRI_BACK_COLOR){
+ ielem->type = eTypeCol;
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(ps_global->VAR_IND_LOPRI_FORE_COLOR, ps_global->VAR_IND_LOPRI_BACK_COLOR);
+ }
+
+ ourifield->leftadj = 1;
+ set_ielem_widths_in_field(ourifield);
+ }
+
+ fs_give((void **) &hdrval);
+ }
+}
+
+
+void
+header_str(INDEXDATA_S *idata, HEADER_TOK_S *hdrtok, ICE_S *ice)
+{
+ IFIELD_S *ourifield = NULL;
+ IELEM_S *ielem = NULL;
+ char *fieldval = NULL;
+
+ if(ice && ice->ifield){
+ /* move to last ifield, the one we're working */
+ for(ourifield = ice->ifield;
+ ourifield && ourifield->next;
+ ourifield = ourifield->next)
+ ;
+ }
+
+ if(!ourifield)
+ return;
+
+ fieldval = get_fieldval(idata, hdrtok);
+
+ if(fieldval){
+ ielem = new_ielem(&ourifield->ielem);
+ ielem->freedata = 1;
+ ielem->data = fieldval;
+ ielem->datalen = strlen(fieldval);
+ fieldval = NULL;
+ ourifield->leftadj = (hdrtok->adjustment == Left) ? 1 : 0;
+ }
+
+ set_ielem_widths_in_field(ourifield);
+}
+
+
+char *
+get_fieldval(INDEXDATA_S *idata, HEADER_TOK_S *hdrtok)
+{
+ int sep, fieldnum;
+ char *hdrval = NULL, *testval;
+ char *fieldval = NULL, *firstval;
+ char *retfieldval = NULL;
+
+ if(!hdrtok)
+ return(retfieldval);
+
+ if(hdrtok && hdrtok->hdrname && hdrtok->hdrname[0])
+ hdrval = fetch_header(idata, hdrtok ? hdrtok->hdrname : "");
+
+ /* find start of fieldnum'th field */
+ fieldval = hdrval;
+ for(fieldnum = MAX(hdrtok->fieldnum-1, 0);
+ fieldnum > 0 && fieldval && *fieldval; fieldnum--){
+
+ firstval = NULL;
+ for(sep = 0; sep < hdrtok->fieldsepcnt; sep++){
+ testval = hdrtok->fieldseps ? strchr(fieldval, hdrtok->fieldseps[sep]) : NULL;
+ if(testval && (!firstval || testval < firstval))
+ firstval = testval;
+ }
+
+ fieldval = firstval;
+ if(fieldval && *fieldval)
+ fieldval++;
+ }
+
+ /* tie off end of field */
+ if(fieldval && *fieldval && hdrtok->fieldnum > 0){
+ firstval = NULL;
+ for(sep = 0; sep < hdrtok->fieldsepcnt; sep++){
+ testval = hdrtok->fieldseps ? strchr(fieldval, hdrtok->fieldseps[sep]) : NULL;
+ if(testval && (!firstval || testval < firstval))
+ firstval = testval;
+ }
+
+ if(firstval)
+ *firstval = '\0';
+ }
+
+ if(!fieldval)
+ fieldval = "";
+
+ retfieldval = cpystr(fieldval);
+
+ if(hdrval)
+ fs_give((void **) &hdrval);
+
+ return(retfieldval);
+}
+
+
+long
+scorevalfrommsg(MAILSTREAM *stream, MsgNo rawno, HEADER_TOK_S *hdrtok, int no_fetch)
+{
+ INDEXDATA_S idata;
+ MESSAGECACHE *mc;
+ char *fieldval = NULL;
+ long retval = 0L;
+
+ memset(&idata, 0, sizeof(INDEXDATA_S));
+ idata.stream = stream;
+ idata.no_fetch = no_fetch;
+ idata.msgno = mn_raw2m(sp_msgmap(stream), rawno);
+ idata.rawno = rawno;
+ if(stream && idata.rawno > 0L && idata.rawno <= stream->nmsgs
+ && (mc = mail_elt(stream, idata.rawno))){
+ idata.size = mc->rfc822_size;
+ index_data_env(&idata, pine_mail_fetchenvelope(stream,idata.rawno));
+ }
+ else
+ idata.bogus = 2;
+
+ fieldval = get_fieldval(&idata, hdrtok);
+
+ if(fieldval){
+ retval = atol(fieldval);
+ fs_give((void **) &fieldval);
+ }
+
+ return(retval);
+}
+
+
+/*
+ * Put a string representing the subject into str. Idata tells us which
+ * message we are referring to.
+ *
+ * This means we should ensure that all data ends up being UTF-8 data.
+ * That covers the data in ice ielems and str.
+ *
+ * Args idata -- which message?
+ * str -- destination buffer
+ * strsize -- size of str buffer
+ * kwtype -- prepend keywords or kw initials before the subject
+ * opening -- add first text from body of message if there's room
+ * ice -- index cache entry for message
+ */
+void
+subj_str(INDEXDATA_S *idata, char *str, size_t strsize, SubjKW kwtype, int opening, ICE_S *ice)
+{
+ char *subject, *origsubj, *origstr, *rawsubj, *sptr = NULL;
+ char *p, *border, *q = NULL, *free_subj = NULL;
+ char *sp;
+ size_t len;
+ int width = -1;
+ int depth = 0, mult = 2;
+ int save;
+ int do_subj = 0, truncated_tree = 0;
+ PINETHRD_S *thd, *thdorig;
+ IELEM_S *ielem = NULL, *subjielem = NULL;
+ IFIELD_S *ourifield = NULL;
+
+ if(strsize <= 0)
+ return;
+
+ /*
+ * If we need the data at the start of the message and we're in
+ * a c-client callback, defer the data lookup until later.
+ */
+ if(opening && idata->no_fetch){
+ idata->bogus = 1;
+ return;
+ }
+
+ if(ice && ice->ifield){
+ /* move to last ifield, the one we're working on */
+ for(ourifield = ice->ifield;
+ ourifield && ourifield->next;
+ ourifield = ourifield->next)
+ ;
+ }
+
+ str[0] = str[strsize-1] = '\0';
+ origstr = str;
+ rawsubj = fetch_subject(idata);
+ if(!rawsubj)
+ rawsubj = "";
+
+ /*
+ * Before we do anything else, decode the character set in the subject and
+ * work with the result.
+ */
+ sp = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
+ SIZEOF_20KBUF, rawsubj);
+
+ len = strlen(sp);
+ len += 100; /* for possible charset, escaped characters */
+ origsubj = fs_get((len+1) * sizeof(unsigned char));
+ origsubj[0] = '\0';
+
+ iutf8ncpy(origsubj, sp, len);
+
+ origsubj[len] = '\0';
+ removing_trailing_white_space(origsubj);
+
+ /*
+ * origsubj is the original subject but it has been decoded. We need
+ * to free it at the end of this routine.
+ */
+
+
+ /*
+ * prepend_keyword will put the keyword stuff before the subject
+ * and split the subject up into its colored parts in subjielem.
+ * Subjielem is a local ielem which will have to be fit into the
+ * real ifield->ielem later. The print_format strings in subjielem will
+ * not be filled in by prepend_keyword because of the fact that we
+ * may have to adjust things for threading below.
+ * We use subjielem in case we want to insert some threading information
+ * at the front of the subject.
+ */
+ if(kwtype == KW || kwtype == KWInit){
+ subject = prepend_keyword_subject(idata->stream, idata->rawno,
+ origsubj, kwtype,
+ ourifield ? &subjielem : NULL,
+ ps_global->VAR_KW_BRACES);
+ free_subj = subject;
+ }
+ else{
+ subject = origsubj;
+ if(ourifield){
+ subjielem = new_ielem(&subjielem);
+ subjielem->type = eTypeCol;
+ subjielem->freedata = 1;
+ subjielem->data = cpystr(subject);
+ subjielem->datalen = strlen(subject);
+ if(pico_usingcolor()
+ && ps_global->VAR_IND_SUBJ_FORE_COLOR
+ && ps_global->VAR_IND_SUBJ_BACK_COLOR){
+ subjielem->freecolor = 1;
+ subjielem->color = new_color_pair(ps_global->VAR_IND_SUBJ_FORE_COLOR, ps_global->VAR_IND_SUBJ_BACK_COLOR);
+ }
+ }
+ }
+
+ /*
+ * This space is here so that if the subject does
+ * not extend all the way to the end of the field then
+ * we'll switch the color back and paint the rest of the
+ * field in the Normal color or the index line color.
+ */
+ if(!opening){
+ ielem = new_ielem(&subjielem);
+ ielem->freedata = 1;
+ ielem->data = cpystr(" ");
+ ielem->datalen = 1;
+ }
+
+ if(!subject)
+ subject = "";
+
+ if(THREADING()
+ && (ps_global->thread_disp_style == THREAD_STRUCT
+ || ps_global->thread_disp_style == THREAD_MUTTLIKE
+ || ps_global->thread_disp_style == THREAD_INDENT_SUBJ1
+ || ps_global->thread_disp_style == THREAD_INDENT_SUBJ2)){
+
+ /*
+ * Why do we want to truncate the subject and from strs?
+ * It's so we can put the [5] thread count things in below when
+ * we are threading and the thread structure runs off the right
+ * hand edge of the screen. This routine doesn't know that it
+ * is running off the edge unless it knows the actual width
+ * that we have to draw in.
+ */
+ if(pith_opt_truncate_sfstr
+ && (*pith_opt_truncate_sfstr)()
+ && ourifield
+ && ourifield->width > 0)
+ width = ourifield->width;
+
+ if(width < 0)
+ width = strsize-1;
+
+ width = MIN(width, strsize-1);
+
+ /*
+ * We're counting on the fact that this initial part of the
+ * string is ascii and we have one octet per character and
+ * characters are width 1 on the screen.
+ */
+ border = str + width;
+
+ thdorig = thd = fetch_thread(idata->stream, idata->rawno);
+
+ if(pith_opt_condense_thread_cue)
+ width = (*pith_opt_condense_thread_cue)(thd, ice, &str, &strsize, width,
+ thd && thd->next
+ && get_lflag(idata->stream,
+ NULL,idata->rawno,
+ MN_COLL));
+
+ /*
+ * width is < available strsize and
+ * border points to something less than or equal
+ * to the end of the buffer.
+ */
+
+ sptr = str;
+
+ if(thd)
+ while(thd->parent &&
+ (thd = fetch_thread(idata->stream, thd->parent)))
+ depth++;
+
+ if(depth > 0){
+ if(ps_global->thread_disp_style == THREAD_INDENT_SUBJ1)
+ mult = 1;
+
+ sptr += (mult*depth);
+ for(thd = thdorig, p = str + mult*depth - mult;
+ thd && thd->parent && p >= str;
+ thd = fetch_thread(idata->stream, thd->parent), p -= mult){
+ if(p + mult >= border && !q){
+ if(width >= 4 && depth < 100){
+ snprintf(str, width+1, "%*s[%2d]", width-4, "", depth);
+ q = str + width-4;
+ }
+ else if(width >= 5 && depth < 1000){
+ snprintf(str, width+1, "%*s[%3d]", width-5, "", depth);
+ q = str + width-5;
+ }
+ else{
+ snprintf(str, width+1, "%s", repeat_char(width, '.'));
+ q = str;
+ }
+
+ border = q;
+ truncated_tree++;
+ }
+
+ if(p < border){
+ p[0] = ' ';
+ if(p + 1 < border)
+ p[1] = ' ';
+
+ if(ps_global->thread_disp_style == THREAD_STRUCT
+ || ps_global->thread_disp_style == THREAD_MUTTLIKE){
+ /*
+ * WARNING!
+ * There is an unwarranted assumption here that VAR_THREAD_LASTREPLY_CHAR[0]
+ * is ascii.
+ */
+ if(thd == thdorig && !thd->branch)
+ p[0] = ps_global->VAR_THREAD_LASTREPLY_CHAR[0];
+ else if(thd == thdorig || thd->branch)
+ p[0] = '|';
+
+ if(p + 1 < border && thd == thdorig)
+ p[1] = '-';
+ }
+ }
+ }
+ }
+
+ if(sptr && !truncated_tree){
+ /*
+ * Look to see if the subject is the same as the previous
+ * message in the thread, if any. If it is the same, don't
+ * reprint the subject.
+ *
+ * Note that when we're prepending keywords to the subject,
+ * and the user changes a keyword, we do invalidate
+ * the index cache for that message but we don't go to the
+ * trouble of invalidating the index cache for the the child
+ * of that node in the thread, so the MUTT subject line
+ * display for the child may be wrong. That is, it may show
+ * it is the same as this subject even though it no longer
+ * is, or vice versa.
+ */
+ if(ps_global->thread_disp_style == THREAD_MUTTLIKE){
+ if(depth == 0)
+ do_subj++;
+ else{
+ if(thdorig->parent &&
+ (thd = fetch_thread(idata->stream, thdorig->parent))
+ && thd->rawno){
+ char *this_orig = NULL,
+ *prev_orig = NULL,
+ *free_prev_orig = NULL,
+ *this_prep = NULL, /* includes prepend */
+ *prev_prep = NULL;
+ ENVELOPE *env;
+ mailcache_t mc;
+ SORTCACHE *sc = NULL;
+
+ /* get the stripped subject of previous message */
+ mc = (mailcache_t) mail_parameters(NIL, GET_CACHE, NIL);
+ if(mc)
+ sc = (*mc)(idata->stream, thd->rawno, CH_SORTCACHE);
+
+ if(sc && sc->subject)
+ prev_orig = sc->subject;
+ else{
+ char *stripthis;
+
+ env = pine_mail_fetchenvelope(idata->stream,
+ thd->rawno);
+ stripthis = (env && env->subject)
+ ? env->subject : "";
+
+ mail_strip_subject(stripthis, &prev_orig);
+
+ free_prev_orig = prev_orig;
+ }
+
+ mail_strip_subject(rawsubj, &this_orig);
+
+ if(kwtype == KW || kwtype == KWInit){
+ prev_prep = prepend_keyword_subject(idata->stream,
+ thd->rawno,
+ prev_orig,
+ kwtype, NULL,
+ ps_global->VAR_KW_BRACES);
+
+ this_prep = prepend_keyword_subject(idata->stream,
+ idata->rawno,
+ this_orig,
+ kwtype, NULL,
+ ps_global->VAR_KW_BRACES);
+
+ if((this_prep || prev_prep)
+ && ((this_prep && !prev_prep)
+ || (prev_prep && !this_prep)
+ || strucmp(this_prep, prev_prep)))
+ do_subj++;
+ }
+ else{
+ if((this_orig || prev_orig)
+ && ((this_orig && !prev_orig)
+ || (prev_orig && !this_orig)
+ || strucmp(this_orig, prev_orig)))
+ do_subj++;
+ }
+
+ /*
+ * If some of the thread is zoomed out of view, we
+ * want to display the subject of the first one that
+ * is in view. If any of the parents or grandparents
+ * etc of this message are visible, then we don't
+ * need to worry about it. If all of the parents have
+ * been zoomed away, then this is the first one.
+ *
+ * When you're looking at a particular case where
+ * some of the messages of a thread are selected it
+ * seems like we should look at not only our
+ * direct parents, but the siblings of the parent
+ * too. But that's not really correct, because those
+ * siblings are basically the starts of different
+ * branches, separate from our branch. They could
+ * have their own subjects, for example. This will
+ * give us cases where it looks like we are showing
+ * the subject too much, but it will be correct!
+ *
+ * In zoom_index() we clear_index_cache_ent for
+ * some lines which have subjects which might become
+ * visible when we zoom, and also in set_lflags
+ * where we might change subjects by unselecting
+ * something when zoomed.
+ */
+ if(!do_subj){
+ while(thd){
+ if(!msgline_hidden(idata->stream,
+ sp_msgmap(idata->stream),
+ mn_raw2m(sp_msgmap(idata->stream),
+ (long) thd->rawno),
+ 0)){
+ break; /* found a visible parent */
+ }
+
+ if(thd && thd->parent)
+ thd = fetch_thread(idata->stream,thd->parent);
+ else
+ thd = NULL;
+ }
+
+ if(!thd) /* none were visible */
+ do_subj++;
+ }
+
+ if(this_orig)
+ fs_give((void **) &this_orig);
+
+ if(this_prep)
+ fs_give((void **) &this_prep);
+
+ if(free_prev_orig)
+ fs_give((void **) &free_prev_orig);
+
+ if(prev_prep)
+ fs_give((void **) &prev_prep);
+ }
+ else
+ do_subj++;
+ }
+ }
+ else
+ do_subj++;
+
+ if(do_subj){
+ /*
+ * We don't need to worry about truncating to width
+ * here. If we go over the right hand edge it will be
+ * truncated.
+ */
+ strsize -= (sptr - str);
+
+ strncpy(sptr, subject, strsize-1);
+ sptr[strsize-1] = '\0';
+ }
+ else if(ps_global->thread_disp_style == THREAD_MUTTLIKE){
+ strsize -= (sptr - str);
+
+ if(strsize > 0){
+ sptr[0] = '>';
+ sptr++;
+ }
+
+ /*
+ * We decided we don't need the subject so we'd better
+ * eliminate subjielem.
+ */
+ free_ielem(&subjielem);
+ }
+ }
+ else
+ free_ielem(&subjielem); /* no room for actual subject */
+
+ if(ourifield && sptr && sptr > origstr){
+ ielem = new_ielem(&ourifield->ielem);
+ ielem->type = eThreadInfo;
+ ielem->freedata = 1;
+ save = *sptr;
+ *sptr = '\0';
+ ielem->data = cpystr(origstr);
+ ielem->datalen = strlen(origstr);
+ *sptr = save;
+ }
+ }
+ else{
+ /*
+ * Not much to do for the non-threading case. Just copy the
+ * subject we have so far into str and truncate it.
+ */
+ strncpy(str, subject, strsize-1);
+ str[strsize-1] = '\0';
+ }
+
+ if(ourifield){
+ /*
+ * We need to add subjielem to the end of the ourifield->ielem list.
+ */
+ if(subjielem){
+ if(ourifield->ielem){
+ for(ielem = ourifield->ielem;
+ ielem && ielem->next; ielem = ielem->next)
+ ;
+
+ ielem->next = subjielem;
+ }
+ else
+ ourifield->ielem = subjielem;
+ }
+
+ ourifield->leftadj = 1;
+ }
+
+ if(opening && ourifield){
+ IELEM_S *ftielem = NULL;
+ size_t len;
+ char *first_text;
+
+ first_text = fetch_firsttext(idata, 0);
+
+ if(first_text){
+ char sep[200];
+ int seplen;
+
+ strncpy(sep, ps_global->VAR_OPENING_SEP ? ps_global->VAR_OPENING_SEP : " - ",
+ sizeof(sep));
+ sep[sizeof(sep)-1] = '\0';
+ removing_double_quotes(sep);
+ seplen = strlen(sep);
+
+ ftielem = new_ielem(&ftielem);
+ ftielem->type = eTypeCol;
+ ftielem->freedata = 1;
+ len = strlen(first_text) + seplen;
+ ftielem->data = (char *) fs_get((len + 1) * sizeof(char));
+
+ strncpy(ftielem->data, sep, seplen);
+ strncpy(ftielem->data+seplen, first_text, len+1-seplen);
+ ftielem->data[len] = '\0';
+
+ ftielem->datalen = strlen(ftielem->data);
+ if(first_text)
+ fs_give((void **) &first_text);
+
+ if(ftielem){
+ if(pico_usingcolor()
+ && ps_global->VAR_IND_OP_FORE_COLOR
+ && ps_global->VAR_IND_OP_BACK_COLOR){
+ ftielem->freecolor = 1;
+ ftielem->color = new_color_pair(ps_global->VAR_IND_OP_FORE_COLOR, ps_global->VAR_IND_OP_BACK_COLOR);
+
+ /*
+ * This space is here so that if the opening text does
+ * not extend all the way to the end of the field then
+ * we'll switch the color back and paint the rest of the
+ * field in the Normal color or the index line color.
+ */
+ ielem = new_ielem(&ftielem);
+ ielem->freedata = 1;
+ ielem->data = cpystr(" ");
+ ielem->datalen = 1;
+ }
+
+ if(ourifield->ielem){
+ for(ielem = ourifield->ielem;
+ ielem && ielem->next; ielem = ielem->next)
+ ;
+
+ ielem->next = ftielem;
+ }
+ else
+ ourifield->ielem = ftielem;
+ }
+
+ ourifield->leftadj = 1;
+ }
+ }
+
+ if(ourifield)
+ set_ielem_widths_in_field(ourifield);
+
+ if(origsubj)
+ fs_give((void **) &origsubj);
+
+ if(free_subj)
+ fs_give((void **) &free_subj);
+}
+
+
+/*
+ * Returns an allocated string which is the passed in subject with a
+ * list of keywords prepended.
+ *
+ * If kwtype == KW you will end up with
+ *
+ * {keyword1 keyword2} subject
+ *
+ * (actually, keyword nicknames will be used instead of the actual keywords
+ * in the case that the user defined nicknames)
+ *
+ * If kwtype == KWInit you get
+ *
+ * {AB} subject
+ *
+ * where A is the first letter of the first keyword and B is the first letter
+ * of the second defined keyword. No space between them. There could be more
+ * than two.
+ *
+ * If an ielemp is passed in it will be filled out with the data and colors
+ * of the pieces of the subject but the print_format strings will not
+ * be set.
+ */
+char *
+prepend_keyword_subject(MAILSTREAM *stream, long int rawno, char *subject,
+ SubjKW kwtype, IELEM_S **ielemp, char *braces)
+{
+ char *p, *next_piece, *retsubj = NULL, *str;
+ char *left_brace = NULL, *right_brace = NULL;
+ size_t len;
+ int some_set = 0, save;
+ IELEM_S *ielem;
+ KEYWORD_S *kw;
+ COLOR_PAIR *color = NULL;
+ SPEC_COLOR_S *sc = ps_global->kw_colors;
+
+ if(!subject)
+ subject = "";
+
+ if(braces && *braces)
+ get_pair(braces, &left_brace, &right_brace, 1, 0);
+
+ len = (left_brace ? strlen(left_brace) : 0) +
+ (right_brace ? strlen(right_brace) : 0);
+
+ if(stream && rawno >= 0L && rawno <= stream->nmsgs){
+ for(kw = ps_global->keywords; kw; kw = kw->next)
+ if(user_flag_is_set(stream, rawno, kw->kw)){
+ if(kwtype == KW){
+ if(some_set)
+ len++; /* space between keywords */
+
+ str = kw->nick ? kw->nick : kw->kw ? kw->kw : "";
+ len += strlen(str);
+ }
+ else if(kwtype == KWInit){
+ str = kw->nick ? kw->nick : kw->kw ? kw->kw : "";
+ /* interested in only the first UTF-8 initial */
+ if(str && str[0]){
+ UCS ucs;
+ unsigned long remaining_octets;
+ unsigned char *inputp;
+
+ remaining_octets = strlen(str);
+ inputp = (unsigned char *) str;
+ ucs = (UCS) utf8_get(&inputp, &remaining_octets);
+ if(!(ucs & U8G_ERROR || ucs == UBOGON)){
+ len += (unsigned) (inputp - (unsigned char *) str);
+ }
+ }
+ }
+
+ some_set++;
+ }
+ }
+
+ if((kwtype == KW || kwtype == KWInit) && some_set){
+ len += strlen(subject); /* subject is already UTF-8 if needed */
+ retsubj = (char *) fs_get((len + 1) * sizeof(*retsubj));
+ memset(retsubj, 0, (len + 1) * sizeof(*retsubj));
+ next_piece = p = retsubj;
+
+ for(kw = ps_global->keywords; kw; kw = kw->next){
+ if(user_flag_is_set(stream, rawno, kw->kw)){
+ if(p == retsubj){
+ if(left_brace && len > 0)
+ sstrncpy(&p, left_brace, len);
+ }
+ else if(kwtype == KW)
+ *p++ = ' ';
+
+ if(ielemp && p > next_piece){
+ save = *p;
+ *p = '\0';
+ ielem = new_ielem(ielemp);
+ ielem->freedata = 1;
+ ielem->data = cpystr(next_piece);
+ ielem->datalen = strlen(next_piece);
+ *p = save;
+ next_piece = p;
+ }
+
+ str = kw->nick ? kw->nick : kw->kw ? kw->kw : "";
+
+ if(kwtype == KWInit){
+ if(str && str[0]){
+ UCS ucs;
+ unsigned long remaining_octets;
+ unsigned char *inputp;
+
+ remaining_octets = strlen(str);
+ inputp = (unsigned char *) str;
+ ucs = (UCS) utf8_get(&inputp, &remaining_octets);
+ if(!(ucs & U8G_ERROR || ucs == UBOGON)){
+ if(len-(p-retsubj) > 0){
+ sstrncpy(&p, str, MIN(inputp - (unsigned char *) str,len-(p-retsubj)));
+ if(p > next_piece && ielemp && pico_usingcolor()
+ && ((kw->nick && kw->nick[0]
+ && (color=hdr_color(kw->nick,NULL,sc)))
+ || (kw->kw && kw->kw[0]
+ && (color=hdr_color(kw->kw,NULL,sc))))){
+ ielem = new_ielem(ielemp);
+ ielem->freedata = 1;
+ save = *p;
+ *p = '\0';
+ ielem->data = cpystr(next_piece);
+ ielem->datalen = strlen(next_piece);
+ ielem->color = color;
+ color = NULL;
+ *p = save;
+ next_piece = p;
+ }
+ }
+ }
+
+ if(color)
+ free_color_pair(&color);
+ }
+ }
+ else{
+ if(len-(p-retsubj) > 0)
+ sstrncpy(&p, str, len-(p-retsubj));
+
+ if(p > next_piece && ielemp && pico_usingcolor()
+ && ((kw->nick && kw->nick[0]
+ && (color=hdr_color(kw->nick,NULL,sc)))
+ || (kw->kw && kw->kw[0]
+ && (color=hdr_color(kw->kw,NULL,sc))))){
+ ielem = new_ielem(ielemp);
+ ielem->freedata = 1;
+ save = *p;
+ *p = '\0';
+ ielem->data = cpystr(next_piece);
+ ielem->datalen = strlen(next_piece);
+ ielem->color = color;
+ color = NULL;
+ *p = save;
+ next_piece = p;
+ }
+
+ if(color)
+ free_color_pair(&color);
+ }
+ }
+ }
+
+ if(len-(p-retsubj) > 0 && right_brace)
+ sstrncpy(&p, right_brace, len-(p-retsubj));
+
+ if(ielemp && p > next_piece){
+ save = *p;
+ *p = '\0';
+ ielem = new_ielem(ielemp);
+ ielem->freedata = 1;
+ ielem->data = cpystr(next_piece);
+ ielem->datalen = strlen(next_piece);
+ *p = save;
+ next_piece = p;
+ }
+
+ if(len-(p-retsubj) > 0 && subject)
+ sstrncpy(&p, subject, len-(p-retsubj));
+
+ if(ielemp && p > next_piece){
+ save = *p;
+ *p = '\0';
+ ielem = new_ielem(ielemp);
+ ielem->type = eTypeCol;
+ ielem->freedata = 1;
+ ielem->data = cpystr(next_piece);
+ ielem->datalen = strlen(next_piece);
+ *p = save;
+ next_piece = p;
+ if(pico_usingcolor()
+ && ps_global->VAR_IND_SUBJ_FORE_COLOR
+ && ps_global->VAR_IND_SUBJ_BACK_COLOR){
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(ps_global->VAR_IND_SUBJ_FORE_COLOR, ps_global->VAR_IND_SUBJ_BACK_COLOR);
+ }
+ }
+
+ retsubj[len] = '\0'; /* just making sure */
+ }
+ else{
+ if(ielemp){
+ ielem = new_ielem(ielemp);
+ ielem->type = eTypeCol;
+ ielem->freedata = 1;
+ ielem->data = cpystr(subject);
+ ielem->datalen = strlen(subject);
+ if(pico_usingcolor()
+ && ps_global->VAR_IND_SUBJ_FORE_COLOR
+ && ps_global->VAR_IND_SUBJ_BACK_COLOR){
+ ielem->freecolor = 1;
+ ielem->color = new_color_pair(ps_global->VAR_IND_SUBJ_FORE_COLOR, ps_global->VAR_IND_SUBJ_BACK_COLOR);
+ }
+ }
+
+ retsubj = cpystr(subject);
+ }
+
+ if(braces){
+ if(left_brace)
+ fs_give((void **) &left_brace);
+
+ if(right_brace)
+ fs_give((void **) &right_brace);
+ }
+
+ return(retsubj);
+}
+
+
+/*
+ * This means we should ensure that all data ends up being UTF-8 data.
+ * That covers the data in ice ielems and str.
+ */
+void
+from_str(IndexColType ctype, INDEXDATA_S *idata, char *str, size_t strsize, ICE_S *ice)
+{
+ char *field, *newsgroups, *border, *p, *fptr = NULL, *q = NULL;
+ ADDRESS *addr;
+ int width = -1;
+ int depth = 0, mult = 2;
+ PINETHRD_S *thd, *thdorig;
+
+ if(THREADING()
+ && (ps_global->thread_disp_style == THREAD_INDENT_FROM1
+ || ps_global->thread_disp_style == THREAD_INDENT_FROM2
+ || ps_global->thread_disp_style == THREAD_STRUCT_FROM)){
+
+ if(pith_opt_truncate_sfstr && (*pith_opt_truncate_sfstr)()){
+ IFIELD_S *ourifield = NULL;
+
+ if(ice && ice->ifield){
+ /* move to last ifield, the one we're working on */
+ for(ourifield = ice->ifield;
+ ourifield && ourifield->next;
+ ourifield = ourifield->next)
+ ;
+ }
+
+ if(ourifield && ourifield->width > 0)
+ width = ourifield->width;
+ }
+
+ if(width < 0)
+ width = strsize-1;
+
+ width = MIN(width, strsize-1);
+
+ thdorig = thd = fetch_thread(idata->stream, idata->rawno);
+ border = str + width;
+ if(pith_opt_condense_thread_cue)
+ width = (*pith_opt_condense_thread_cue)(thd, ice, &str, &strsize, width,
+ thd && thd->next
+ && get_lflag(idata->stream,
+ NULL,idata->rawno,
+ MN_COLL));
+
+ fptr = str;
+
+ if(thd)
+ while(thd->parent && (thd = fetch_thread(idata->stream, thd->parent)))
+ depth++;
+
+ if(depth > 0){
+ if(ps_global->thread_disp_style == THREAD_INDENT_FROM1)
+ mult = 1;
+
+ fptr += (mult*depth);
+ for(thd = thdorig, p = str + mult*depth - mult;
+ thd && thd->parent && p >= str;
+ thd = fetch_thread(idata->stream, thd->parent), p -= mult){
+ if(p + mult >= border && !q){
+ if(width >= 4 && depth < 100){
+ snprintf(str, width+1, "%*s[%2d]", width-4, "", depth);
+ q = str + width-4;
+ }
+ else if(width >= 5 && depth < 1000){
+ snprintf(str, width+1, "%*s[%3d]", width-5, "", depth);
+ q = str + width-5;
+ }
+ else{
+ snprintf(str, width+1, "%s", repeat_char(width, '.'));
+ q = str;
+ }
+
+ border = q;
+ fptr = NULL;
+ }
+
+ if(p + 1 < border){
+ p[0] = p[1] = ' ';
+ if(ps_global->thread_disp_style == THREAD_STRUCT_FROM){
+ /*
+ * WARNING!
+ * There is an unwarranted assumption here that VAR_THREAD_LASTREPLY_CHAR[0]
+ * is ascii.
+ */
+ if(thd == thdorig && !thd->branch)
+ p[0] = ps_global->VAR_THREAD_LASTREPLY_CHAR[0];
+ else if(thd == thdorig || thd->branch)
+ p[0] = '|';
+
+ if(thd == thdorig)
+ p[1] = '-';
+ }
+ }
+ else if(p < border){
+ p[0] = ' ';
+ if(ps_global->thread_disp_style == THREAD_STRUCT_FROM){
+ /*
+ * WARNING!
+ * There is an unwarranted assumption here that VAR_THREAD_LASTREPLY_CHAR[0]
+ * is ascii.
+ */
+ if(thd == thdorig && !thd->branch)
+ p[0] = ps_global->VAR_THREAD_LASTREPLY_CHAR[0];
+ else if(thd == thdorig || thd->branch)
+ p[0] = '|';
+ }
+ }
+ }
+ }
+ }
+ else
+ fptr = str;
+
+ if(fptr){
+ strsize -= (fptr - str);
+ switch(ctype){
+ case iFromTo:
+ case iFromToNotNews:
+ if(!(addr = fetch_from(idata)) || address_is_us(addr, ps_global)){
+ if(strsize-1 <= 4){
+ strncpy(fptr, "To: ", strsize-1);
+ fptr[strsize-1] = '\0';
+ break;
+ }
+ else{
+ if((field = ((addr = fetch_to(idata))
+ ? "To"
+ : (addr = fetch_cc(idata))
+ ? "Cc"
+ : NULL))
+ && set_index_addr(idata, field, addr, "To: ",
+ strsize-1, fptr))
+ break;
+
+ if(ctype == iFromTo &&
+ (newsgroups = fetch_newsgroups(idata)) &&
+ *newsgroups){
+ snprintf(fptr, strsize, "To: %-*.*s", strsize-1-4, strsize-1-4,
+ newsgroups);
+ break;
+ }
+
+ /* else fall thru to From: */
+ }
+ }
+ /* else fall thru to From: */
+
+ if(idata->bogus)
+ break;
+
+ case iFrom:
+ set_index_addr(idata, "From", fetch_from(idata), NULL, strsize-1, fptr);
+ break;
+
+ case iAddress:
+ case iMailbox:
+ if((addr = fetch_from(idata)) && addr->mailbox && addr->mailbox[0]){
+ char *mb = NULL, *hst = NULL, *at = NULL;
+ size_t len;
+
+ mb = addr->mailbox;
+ if(ctype == iAddress && addr->host && addr->host[0]
+ && addr->host[0] != '.'){
+ at = "@";
+ hst = addr->host;
+ }
+
+ len = strlen(mb);
+ if(!at || strsize-1 <= len)
+ snprintf(fptr, strsize, "%-*.*s", strsize-1, strsize-1, mb);
+ else
+ snprintf(fptr, strsize, "%s@%-*.*s", mb, strsize-1-len-1, strsize-1-len-1, hst);
+ }
+
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+
+/*
+ * Set up the elements contained in field so that they take up the
+ * whole field width. Data is assumed to be UTF-8.
+ */
+void
+set_ielem_widths_in_field(IFIELD_S *ifield)
+{
+ IELEM_S *ielem = NULL;
+ int datawidth, fmtwidth;
+
+ if(!ifield)
+ return;
+
+ fmtwidth = ifield->width;
+
+ for(ielem = ifield->ielem; ielem && fmtwidth > 0; ielem = ielem->next){
+ if(!ifield->leftadj && ielem->next){
+ dprint((1, "set_ielem_widths_in_field(%d): right adjust with multiple elements, NOT SUPPOSED TO HAPPEN!\n", (int) ifield->ctype));
+ assert(0);
+ }
+
+ datawidth = (int) utf8_width(ielem->data);
+ if(datawidth >= fmtwidth || !ielem->next){
+ set_print_format(ielem, fmtwidth, ifield->leftadj);
+ fmtwidth = 0;
+ }
+ else{
+ set_print_format(ielem, datawidth, ifield->leftadj);
+ fmtwidth -= datawidth;
+ }
+ }
+}
+
+
+/*
+ * Simple hash function from K&R 2nd edition, p. 144.
+ *
+ * This one is modified to never return 0 so we can use that as a special
+ * value. Also, LINE_HASH_N fits in an unsigned long, so it too can be used
+ * as a special value that can't be returned by line_hash.
+ */
+unsigned long
+line_hash(char *s)
+{
+ unsigned long hashval;
+
+ for(hashval = 0; *s != '\0'; s++)
+ hashval = *s + 31 * hashval;
+
+ hashval = hashval % LINE_HASH_N;
+
+ if(!hashval)
+ hashval++;
+
+ return(hashval);
+}
+
+
+/*
+ * Returns nonzero if considered hidden, 0 if not considered hidden.
+ */
+int
+msgline_hidden(MAILSTREAM *stream, MSGNO_S *msgmap, long int msgno, int flags)
+{
+ int ret;
+
+ if(flags & MH_ANYTHD){
+ ret = ((any_lflagged(msgmap, MN_HIDE) > 0)
+ && get_lflag(stream, msgmap, msgno, MN_HIDE));
+ }
+ else if(flags & MH_THISTHD && THREADING() && sp_viewing_a_thread(stream)){
+ ret = (get_lflag(stream, msgmap, msgno, MN_HIDE)
+ || !get_lflag(stream, msgmap, msgno, MN_CHID2));
+ }
+ else{
+ if(THREADING() && sp_viewing_a_thread(stream)){
+ ret = (get_lflag(stream, msgmap, msgno, MN_HIDE)
+ || !get_lflag(stream, msgmap, msgno, MN_CHID2)
+ || get_lflag(stream, msgmap, msgno, MN_CHID));
+ }
+ else if(THRD_INDX()){
+ /*
+ * If this message is in the collapsed part of a thread,
+ * it's hidden. It must be a top-level of a thread to be
+ * considered visible. Even if it is top-level, it is only
+ * visible if some message in the thread is not hidden.
+ */
+ if(get_lflag(stream, msgmap, msgno, MN_CHID)) /* not top */
+ ret = 1;
+ else{
+ unsigned long rawno;
+ PINETHRD_S *thrd = NULL;
+
+ rawno = mn_m2raw(msgmap, msgno);
+ if(rawno)
+ thrd = fetch_thread(stream, rawno);
+
+ ret = !thread_has_some_visible(stream, thrd);
+ }
+ }
+ else{
+ ret = ((any_lflagged(msgmap, MN_HIDE | MN_CHID) > 0)
+ && get_lflag(stream, msgmap, msgno, MN_HIDE | MN_CHID));
+ }
+ }
+
+ dprint((10,
+ "msgline_hidden(%ld): %s\n", msgno, ret ? "HID" : "VIS"));
+
+ return(ret);
+}
+
+
+void
+adjust_cur_to_visible(MAILSTREAM *stream, MSGNO_S *msgmap)
+{
+ long n, cur;
+ int dir;
+
+ cur = mn_get_cur(msgmap);
+
+ /* if current is hidden, adjust */
+ if(cur >= 1L && cur <= mn_get_total(msgmap)
+ && msgline_hidden(stream, msgmap, cur, 0)){
+
+ dir = mn_get_revsort(msgmap) ? -1 : 1;
+
+ for(n = cur;
+ ((dir == 1 && n >= 1L) || (dir == -1 && n <= mn_get_total(msgmap)))
+ && (n >= 1L && n <= mn_get_total(msgmap))
+ && msgline_hidden(stream, msgmap, n, 0);
+ n -= dir)
+ ;
+
+ if(((dir == 1 && n >= 1L) || (dir == -1 && n <= mn_get_total(msgmap)))
+ && (n >= 1L && n <= mn_get_total(msgmap)))
+ mn_reset_cur(msgmap, n);
+ else{ /* no visible in that direction */
+ for(n = cur;
+ ((dir == 1 && n >= 1L) || (dir == -1 && n <= mn_get_total(msgmap)))
+ && (n >= 1L && n <= mn_get_total(msgmap))
+ && msgline_hidden(stream, msgmap, n, 0);
+ n += dir)
+ ;
+
+ if(((dir == 1 && n >= 1L) || (dir == -1 && n <= mn_get_total(msgmap)))
+ && (n >= 1L && n <= mn_get_total(msgmap)))
+ mn_reset_cur(msgmap, n);
+ /* else trouble! */
+ }
+ }
+}
+
+
+void
+setup_for_index_index_screen(void)
+{
+ format_index_line = format_index_index_line;
+ setup_header_widths = setup_index_header_widths;
+}
+
+
+void
+setup_for_thread_index_screen(void)
+{
+ format_index_line = format_thread_index_line;
+ setup_header_widths = setup_thread_header_widths;
+}
+
+
+unsigned long
+ice_hash(ICE_S *ice)
+{
+ char buf[MAX_SCREEN_COLS+1];
+
+ buf[0] = '\0';
+
+ if(ice)
+ simple_index_line(buf, sizeof(buf), ice, 0L);
+
+ buf[sizeof(buf) - 1] = '\0';
+
+ return(line_hash(buf));
+}
+
+
+char *
+left_adjust(int width)
+{
+ return(format_str(width, 1));
+}
+
+
+char *
+right_adjust(int width)
+{
+ return(format_str(width, 0));
+}
+
+
+/*
+ * Returns allocated and filled in format string.
+ */
+char *
+format_str(int width, int left)
+{
+ char *format;
+ size_t len;
+
+ len = PRINT_FORMAT_LEN(width,left) * sizeof(char);
+ format = (char *) fs_get(len + 1);
+ copy_format_str(width, left, format, len);
+ format[len] = '\0';
+
+ return(format);
+}
+
+
+/*
+ * Put the left or right adjusted format string of width width into
+ * dest. Dest is of size n+1.
+ */
+char *
+copy_format_str(int width, int left, char *dest, int n)
+{
+ char *p;
+
+ p = int2string(width);
+
+ snprintf(dest, n+1, "%%%s%s.%ss", left ? "-" : "", p, p);
+
+ dest[n] = '\0';
+
+ return(dest);
+}
+
+
+/*
+ * Sets up the print_format string to be width wide with left or right
+ * adjust. Takes care of memory freeing and allocation.
+ */
+void
+set_print_format(IELEM_S *ielem, int width, int leftadj)
+{
+ if(ielem){
+ ielem->wid = width;
+
+ if(ielem->print_format){
+ /* is there enough room? */
+ if(ielem->freeprintf < PRINT_FORMAT_LEN(width,leftadj)+1){
+ fs_resize((void **) &ielem->print_format,
+ (PRINT_FORMAT_LEN(width,leftadj)+1) * sizeof(char));
+ ielem->freeprintf = (PRINT_FORMAT_LEN(width,leftadj) + 1) * sizeof(char);
+ }
+
+ copy_format_str(width, leftadj, ielem->print_format,
+ PRINT_FORMAT_LEN(width,leftadj));
+ }
+ else{
+ ielem->print_format = leftadj ? left_adjust(width)
+ : right_adjust(width);
+ ielem->freeprintf = (PRINT_FORMAT_LEN(width,leftadj) + 1) * sizeof(char);
+ }
+ }
+}
diff --git a/pith/mailindx.h b/pith/mailindx.h
new file mode 100644
index 00000000..4c45268c
--- /dev/null
+++ b/pith/mailindx.h
@@ -0,0 +1,60 @@
+/*
+ * $Id: mailindx.h 925 2008-02-06 02:03:01Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_MAILINDX_INCLUDED
+#define PITH_MAILINDX_INCLUDED
+
+
+#include "../pith/indxtype.h"
+#include "../pith/msgno.h"
+#include "../pith/state.h"
+#include "../pith/pattern.h"
+#include "../pith/flag.h"
+
+
+extern ICE_S *(*format_index_line)(INDEXDATA_S *);
+extern void (*setup_header_widths)(MAILSTREAM *);
+
+
+/* exported prototypes */
+int msgline_hidden(MAILSTREAM *, MSGNO_S *, long, int);
+void adjust_cur_to_visible(MAILSTREAM *, MSGNO_S *);
+unsigned long line_hash(char *);
+void init_index_format(char *, INDEX_COL_S **);
+void free_index_format(INDEX_COL_S **);
+void reset_index_format(void);
+INDEX_PARSE_T *itoktype(char *, int);
+char *prepend_keyword_subject(MAILSTREAM *, long, char *, SubjKW, IELEM_S **, char *);
+int get_index_line_color(MAILSTREAM *, SEARCHSET *, PAT_STATE **, COLOR_PAIR **);
+void setup_for_index_index_screen(void);
+void setup_for_thread_index_screen(void);
+INDEX_PARSE_T *itoken(int);
+void load_overview(MAILSTREAM *, unsigned long, OVERVIEW *, imapuid_t);
+ICE_S *build_header_work(struct pine *, MAILSTREAM *, MSGNO_S *, long, long, int, int *);
+char *simple_index_line(char *, size_t, ICE_S *, long);
+int resent_to_us(INDEXDATA_S *);
+int parsed_resent_to_us(char *);
+ADDRESS *fetch_cc(INDEXDATA_S *);
+void index_data_env(INDEXDATA_S *, ENVELOPE *);
+void date_str(char *, IndexColType, int, char *, size_t, int);
+ADDRESS *fetch_to(INDEXDATA_S *);
+char *get_fieldval(INDEXDATA_S *, HEADER_TOK_S *);
+long scorevalfrommsg(MAILSTREAM *, MsgNo, HEADER_TOK_S *, int);
+HEADER_TOK_S *new_hdrtok(char *);
+void free_hdrtok(HEADER_TOK_S **);
+
+
+#endif /* PITH_MAILINDX_INCLUDED */
diff --git a/pith/maillist.c b/pith/maillist.c
new file mode 100644
index 00000000..5b51ceaf
--- /dev/null
+++ b/pith/maillist.c
@@ -0,0 +1,210 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: maillist.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*
+ * * * * * * * * * RFC 2369 support routines * * * * * * * *
+ */
+
+
+#include "../pith/headers.h"
+#include "../pith/maillist.h"
+#include "../pith/state.h"
+#include "../pith/url.h"
+
+
+/*
+ * Internal prototypes
+ */
+int rfc2369_parse(char *, RFC2369_S *);
+
+/*
+ * * NOTE * These have to remain in sync with the MLCMD_* macros
+ * in maillist.h. Sorry.
+ */
+static RFC2369FIELD_S rfc2369_fields[] = {
+ {"List-Help",
+ "get information about the list and instructions on how to join",
+ "seek help"},
+ {"List-Unsubscribe",
+ "remove yourself from the list (Unsubscribe)",
+ "UNsubscribe"},
+ {"List-Subscribe",
+ "add yourself to the list (Subscribe)",
+ "Subscribe"},
+ {"List-Post",
+ "send a message to the entire list (Post)",
+ "post a message"},
+ {"List-Owner",
+ "send a message to the list owner",
+ "contact the list owner"},
+ {"List-Archive",
+ "view archive of messages sent to the list",
+ "view the archive"}
+};
+
+
+char **
+rfc2369_hdrs(char **hdrs)
+{
+ int i;
+
+ for(i = 0; i < MLCMD_COUNT; i++)
+ hdrs[i] = rfc2369_fields[i].name;
+
+ hdrs[i] = NULL;
+ return(hdrs);
+}
+
+
+int
+rfc2369_parse_fields(char *h, RFC2369_S *data)
+{
+ char *ep, *nhp, *tp;
+ int i, rv = FALSE;
+
+ for(i = 0; i < MLCMD_COUNT; i++)
+ data[i].field = rfc2369_fields[i];
+
+ for(nhp = h; h; h = nhp){
+ /* coerce h to start of field */
+ for(ep = h;;)
+ if((tp = strpbrk(ep, "\015\012")) != NULL){
+ if(strindex(" \t", *((ep = tp) + 2))){
+ *ep++ = ' '; /* flatten continuation */
+ *ep++ = ' ';
+ for(; *ep; ep++) /* advance past whitespace */
+ if(*ep == '\t')
+ *ep = ' ';
+ else if(*ep != ' ')
+ break;
+ }
+ else{
+ *ep = '\0'; /* tie off header data */
+ nhp = ep + 2; /* start of next header */
+ break;
+ }
+ }
+ else{
+ while(*ep) /* find the end of this line */
+ ep++;
+
+ nhp = NULL; /* no more header fields */
+ break;
+ }
+
+ /* if length is within reason, see if we're interested */
+ if(ep - h < MLCMD_REASON && rfc2369_parse(h, data))
+ rv = TRUE;
+ }
+
+ return(rv);
+}
+
+
+int
+rfc2369_parse(char *h, RFC2369_S *data)
+{
+ int l, ifield, idata = 0;
+ char *p, *p1, *url, *comment;
+
+ /* look for interesting name's */
+ for(ifield = 0; ifield < MLCMD_COUNT; ifield++)
+ if(!struncmp(h, rfc2369_fields[ifield].name,
+ l = strlen(rfc2369_fields[ifield].name))
+ && *(h += l) == ':'){
+ /* unwrap any transport encodings */
+ if((p = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
+ SIZEOF_20KBUF, ++h)) == tmp_20k_buf)
+ strcpy(h, p); /* assumption #383: decoding shrinks */
+
+ url = comment = NULL;
+ while(*h){
+ while(*h == ' ')
+ h++;
+
+ switch(*h){
+ case '<' : /* URL */
+ if((p = strindex(h, '>')) != NULL){
+ url = ++h; /* remember where it starts */
+ *p = '\0'; /* tie it off */
+ h = p + 1; /* advance h */
+ for(p = p1 = url; (*p1 = *p) != '\0'; p++)
+ if(*p1 != ' ')
+ p1++; /* remove whitespace ala RFC */
+ }
+ else
+ *h = '\0'; /* tie off junk */
+
+ break;
+
+ case '(' : /* Comment */
+ comment = rfc822_skip_comment(&h, LONGT);
+ break;
+
+ case 'N' : /* special case? */
+ case 'n' :
+ if(ifield == MLCMD_POST
+ && (*(h+1) == 'O' || *(h+1) == 'o')
+ && (!*(h+2) || *(h+2) == ' ')){
+ ; /* yup! */
+
+ url = h;
+ *(h + 2) = '\0';
+ h += 3;
+ break;
+ }
+
+ default :
+ removing_trailing_white_space(h);
+ if(!url
+ && (url = rfc1738_scan(h, &l))
+ && url == h && l == strlen(h)){
+ removing_trailing_white_space(h);
+ data[ifield].data[idata].value = url;
+ }
+ else
+ data[ifield].data[idata].error = h;
+
+ return(1); /* return junk */
+ }
+
+ while(*h == ' ')
+ h++;
+
+ switch(*h){
+ case ',' :
+ h++;
+
+ case '\0':
+ if(url || (comment && *comment)){
+ data[ifield].data[idata].value = url;
+ data[ifield].data[idata].comment = comment;
+ url = comment = NULL;
+ }
+
+ if(++idata == MLCMD_MAXDATA)
+ *h = '\0';
+
+ default :
+ break;
+ }
+ }
+ }
+
+ return(idata);
+}
diff --git a/pith/maillist.h b/pith/maillist.h
new file mode 100644
index 00000000..bc9b02b5
--- /dev/null
+++ b/pith/maillist.h
@@ -0,0 +1,57 @@
+/*
+ * $Id: maillist.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_MAILLIST_INCLUDED
+#define PITH_MAILLIST_INCLUDED
+
+
+/*
+ * Constants and structs to aid RFC 2369 support
+ */
+#define MLCMD_HELP 0
+#define MLCMD_UNSUB 1
+#define MLCMD_SUB 2
+#define MLCMD_POST 3
+#define MLCMD_OWNER 4
+#define MLCMD_ARCHIVE 5
+#define MLCMD_COUNT 6
+#define MLCMD_MAXDATA 3
+#define MLCMD_REASON 8192
+
+
+typedef struct rfc2369_field_s {
+ char *name,
+ *description,
+ *action;
+} RFC2369FIELD_S;
+
+typedef struct rfc2369_data_s {
+ char *value,
+ *comment,
+ *error;
+} RFC2369DATA_S;
+
+typedef struct rfc2369_s {
+ RFC2369FIELD_S field;
+ RFC2369DATA_S data[MLCMD_MAXDATA];
+} RFC2369_S;
+
+
+/* exported protoypes */
+char **rfc2369_hdrs(char **);
+int rfc2369_parse_fields(char *, RFC2369_S *);
+
+
+#endif /* PITH_MAILLIST_INCLUDED */
diff --git a/pith/mailpart.h b/pith/mailpart.h
new file mode 100644
index 00000000..e7038086
--- /dev/null
+++ b/pith/mailpart.h
@@ -0,0 +1,51 @@
+/*
+ * $Id: mailpart.h 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_MAILPART_INCLUDED
+#define PITH_MAILPART_INCLUDED
+
+
+#include "../pith/atttype.h"
+#include "../pith/state.h"
+
+
+#define MIME_MSG(t,s) ((t) == TYPEMESSAGE && (s) && !strucmp((s),"rfc822"))
+
+#define MIME_DGST(t,s) ((t) == TYPEMULTIPART && (s) && !strucmp((s),"digest"))
+
+/* Is this a message attachment? */
+#define MIME_VCARD(t,s) ((((t) == TYPETEXT || (t) == TYPEAPPLICATION) \
+ && (s) && !strucmp((s),"DIRECTORY")) \
+ || ((t) == TYPETEXT \
+ && (s) && !strucmp((s),"X-VCARD")))
+
+/* Is this a multipart signed? */
+#define MIME_MULT_SIGNED(t,s) ((t) == TYPEMULTIPART && (s) && !strucmp((s),"signed"))
+
+
+#define MIME_MSG_A(a) MIME_MSG((a)->body->type, (a)->body->subtype)
+
+/* Is this a digest attachment? */
+#define MIME_DGST_A(a) MIME_DGST((a)->body->type, (a)->body->subtype)
+
+/* Is this a vCard attachment? */
+#define MIME_VCARD_A(a) MIME_VCARD((a)->body->type, (a)->body->subtype)
+
+
+/* exported protoypes */
+
+
+#endif /* PITH_MAILPART_INCLUDED */
diff --git a/pith/mailview.c b/pith/mailview.c
new file mode 100644
index 00000000..40728aab
--- /dev/null
+++ b/pith/mailview.c
@@ -0,0 +1,3271 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: mailview.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2009 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+
+ mailview.c
+ Implements message data gathering and formatting
+
+ ====*/
+
+
+#include "headers.h"
+#include "../pith/mailview.h"
+#include "../pith/conf.h"
+#include "../pith/msgno.h"
+#include "../pith/editorial.h"
+#include "../pith/mimedesc.h"
+#include "../pith/margin.h"
+#include "../pith/color.h"
+#include "../pith/strlst.h"
+#include "../pith/charset.h"
+#include "../pith/status.h"
+#include "../pith/maillist.h"
+#include "../pith/mailcmd.h"
+#include "../pith/mailindx.h"
+#include "../pith/imap.h"
+#include "../pith/detach.h"
+#include "../pith/text.h"
+#include "../pith/url.h"
+#include "../pith/rfc2231.h"
+#include "../pith/list.h"
+#include "../pith/stream.h"
+#include "../pith/send.h"
+#include "../pith/filter.h"
+#include "../pith/string.h"
+#include "../pith/ablookup.h"
+#include "../pith/escapes.h"
+#include "../pith/keyword.h"
+#include "../pith/smime.h"
+
+
+#define FBUF_LEN (50)
+
+#define ISRFCEOL(S) (*(S) == '\015' && *((S)+1) == '\012')
+
+
+/*
+ * This is a list of header fields that are represented canonically
+ * by the c-client's ENVELOPE structure. The list is used by the
+ * two functions below to decide if a given field is included in this
+ * set.
+ */
+static struct envelope_s {
+ char *name;
+ long val;
+} envelope_hdrs[] = {
+ {"from", FE_FROM},
+ {"sender", FE_SENDER},
+ {"date", FE_DATE},
+ {"to", FE_TO},
+ {"cc", FE_CC},
+ {"bcc", FE_BCC},
+ {"newsgroups", FE_NEWSGROUPS},
+ {"subject", FE_SUBJECT},
+ {"message-id", FE_MESSAGEID},
+ {"reply-to", FE_REPLYTO},
+ {"followup-to", FE_FOLLOWUPTO},
+ {"in-reply-to", FE_INREPLYTO},
+/* {"return-path", FE_RETURNPATH}, not usually filled in */
+ {"references", FE_REFERENCES},
+ {NULL, 0}
+};
+
+
+/*
+ * Hook for optional display of rfc2369 content
+ */
+int (*pith_opt_rfc2369_editorial)(long, HANDLE_S **, int, int, gf_io_t);
+
+
+
+
+/*
+ * Internal prototypes
+ */
+int format_blip_seen(long);
+int is_an_env_hdr(char *);
+int is_an_addr_hdr(char *);
+void format_env_hdr(MAILSTREAM *, long, char *, ENVELOPE *,
+ fmt_env_t, gf_io_t, char *, char *, int);
+int delineate_this_header(char *, char *, char **, char **);
+char *url_embed(int);
+int color_headers(long, char *, LT_INS_S **, void *);
+int url_hilite_hdr(long, char *, LT_INS_S **, void *);
+int pad_to_right_edge(long, char *, LT_INS_S **, void *);
+int url_bogus_imap(char **, char *, char *);
+int format_raw_header(MAILSTREAM *, long, char *, gf_io_t);
+void format_envelope(MAILSTREAM *, long, char *, ENVELOPE *,
+ gf_io_t, long, char *, int);
+int any_hdr_color(char *);
+void format_addr_string(MAILSTREAM *, long, char *, char *,
+ ADDRESS *, int, char *, gf_io_t);
+void pine_rfc822_write_address_noquote(ADDRESS *, gf_io_t, int *);
+void format_newsgroup_string(char *, char *, int, gf_io_t);
+int format_raw_hdr_string(char *, char *, gf_io_t, char *, int);
+int format_env_puts(char *, gf_io_t);
+int find_field(char **, char *, size_t);
+int embed_color(COLOR_PAIR *, gf_io_t);
+COLOR_PAIR *get_cur_embedded_color(void);
+void clear_cur_embedded_color(void);
+
+
+
+
+/*----------------------------------------------------------------------
+ Format a message message for viewing
+
+ Args: msgno -- The number of the message to view
+ env -- pointer to the message's envelope
+ body -- pointer to the message's body
+ handlesp -- address of pointer to the message's handles
+ flgs -- possible flags listed in pith/mailview.h with
+ prefix FM_
+ pc -- write to this function
+
+Result: Returns true if no problems encountered, else false.
+
+First the envelope is formatted; next a list of all attachments is
+formatted if there is more than one. Then all the body parts are
+formatted, fetching them as needed. This includes headers of included
+message. Richtext is also formatted. An entry is made in the text for
+parts that are not displayed or can't be displayed.
+
+ ----*/
+int
+format_message(long int msgno, ENVELOPE *env, struct mail_bodystruct *body,
+ HANDLE_S **handlesp, int flgs, gf_io_t pc)
+{
+ char *decode_err = NULL;
+ HEADER_S h;
+ int width;
+
+ clear_cur_embedded_color();
+
+ if(!(flgs & FM_DISPLAY))
+ flgs |= FM_NOINDENT;
+
+ width = (flgs & FM_DISPLAY) ? ps_global->ttyo->screen_cols : 80;
+
+ /*---- format and copy envelope ----*/
+ if(!(flgs & FM_NOEDITORIAL)){
+ if(ps_global->full_header == 1)
+ /* TRANSLATORS: User is viewing a message and all the quoted text is
+ being shown. */
+ q_status_message(SM_INFO, 0, 3, _("All quoted text being included"));
+ else if(ps_global->full_header == 2)
+ q_status_message(SM_INFO, 0, 3,
+ /* TRANSLATORS: User is viewing a message and all of
+ the header text is being shown. */
+ _("Full header mode ON. All header text being included"));
+ }
+
+ HD_INIT(&h, ps_global->VAR_VIEW_HEADERS, ps_global->view_all_except, FE_DEFAULT);
+ switch(format_header(ps_global->mail_stream, msgno, NULL,
+ env, &h, NULL, handlesp, flgs, NULL, pc)){
+
+ case -1 : /* write error */
+ goto write_error;
+
+ case 1 : /* fetch error */
+ if(!(gf_puts("[ Error fetching header ]", pc)
+ && !gf_puts(NEWLINE, pc)))
+ goto write_error;
+
+ break;
+ }
+
+ if(!(body == NULL
+ || (ps_global->full_header == 2
+ && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global))))
+ format_attachment_list(msgno, body, handlesp, flgs, width, pc);
+
+ /* write delimiter and body */
+ if(gf_puts(NEWLINE, pc)
+ && (decode_err = format_body(msgno, body, handlesp, &h, flgs, width, pc)) == NULL)
+ return(1);
+
+
+ write_error:
+
+ if(!(flgs & FM_DISPLAY))
+ q_status_message1(SM_ORDER, 3, 4, _("Error writing message: %s"),
+ decode_err ? decode_err : error_description(errno));
+
+ return(0);
+}
+
+
+char *
+format_body(long int msgno, BODY *body, HANDLE_S **handlesp, HEADER_S *hp, int flgs, int width, gf_io_t pc)
+{
+ int filt_only_c0 = 0, wrapflags, error_found = 0;
+ int is_in_sig = OUT_SIG_BLOCK;
+ char *charset, *decode_err = NULL, *tmp1, *description;
+ ATTACH_S *a;
+ URL_HILITE_S uh;
+ gf_io_t gc;
+
+ if(body == NULL
+ || (ps_global->full_header == 2
+ && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global))) {
+
+ /*--- Server is not an IMAP2bis, It can't parse MIME
+ so we just show the text here. Hopefully the
+ message isn't a MIME message
+ ---*/
+ void *text2;
+
+ if((text2 = (void *)pine_mail_fetch_text(ps_global->mail_stream,
+ msgno, NULL, NULL, NIL)) != NULL){
+
+ if(!gf_puts(NEWLINE, pc)) /* write delimiter */
+ return("Write Error");
+
+ gf_set_readc(&gc, text2, (unsigned long)strlen(text2), CharStar, 0);
+ gf_filter_init();
+
+ /*
+ * We need to translate the message
+ * into UTF-8, but that's trouble in the full header case
+ * because we don't know what to translate from. We'll just
+ * take a guess by looking for the first text part and
+ * using its charset.
+ */
+ if(body && body->type == TYPETEXT)
+ charset = parameter_val(body->parameter, "charset");
+ else if(body && body->type == TYPEMULTIPART && body->nested.part
+ && body->nested.part->body.type == TYPETEXT)
+ charset = parameter_val(body->nested.part->body.parameter, "charset");
+ else
+ charset = ps_global->display_charmap;
+
+ if(strucmp(charset, "us-ascii") && strucmp(charset, "utf-8")){
+ /* transliterate message text to UTF-8 */
+ gf_link_filter(gf_utf8, gf_utf8_opt(charset));
+ }
+
+ /* link in filters, similar to what is done in decode_text() */
+ if(!ps_global->pass_ctrl_chars){
+ gf_link_filter(gf_escape_filter, NULL);
+ filt_only_c0 = 1;
+ gf_link_filter(gf_control_filter,
+ gf_control_filter_opt(&filt_only_c0));
+ }
+
+ gf_link_filter(gf_tag_filter, NULL);
+
+ if((F_ON(F_VIEW_SEL_URL, ps_global)
+ || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
+ || F_ON(F_SCAN_ADDR, ps_global))
+ && handlesp){
+ gf_link_filter(gf_line_test,
+ gf_line_test_opt(url_hilite,
+ gf_url_hilite_opt(&uh,handlesp,0)));
+ }
+
+ if((flgs & FM_DISPLAY)
+ && !(flgs & FM_NOCOLOR)
+ && pico_usingcolor()
+ && ps_global->VAR_SIGNATURE_FORE_COLOR
+ && ps_global->VAR_SIGNATURE_BACK_COLOR){
+ gf_link_filter(gf_line_test, gf_line_test_opt(color_signature, &is_in_sig));
+ }
+
+ if((flgs & FM_DISPLAY)
+ && !(flgs & FM_NOCOLOR)
+ && pico_usingcolor()
+ && ps_global->VAR_QUOTE1_FORE_COLOR
+ && ps_global->VAR_QUOTE1_BACK_COLOR){
+ gf_link_filter(gf_line_test, gf_line_test_opt(color_a_quote, NULL));
+ }
+
+ if(!(flgs & FM_NOWRAP)){
+ wrapflags = (flgs & FM_DISPLAY) ? (GFW_HANDLES|GFW_SOFTHYPHEN) : GFW_NONE;
+ if(flgs & FM_DISPLAY
+ && !(flgs & FM_NOCOLOR)
+ && pico_usingcolor())
+ wrapflags |= GFW_USECOLOR;
+ gf_link_filter(gf_wrap, gf_wrap_filter_opt(width, width,
+ (flgs & FM_NOINDENT)
+ ? NULL : format_view_margin(),
+ 0,
+ wrapflags));
+ }
+
+ gf_link_filter(gf_nvtnl_local, NULL);
+ if((decode_err = gf_pipe(gc, pc)) != NULL){
+ /* TRANSLATORS: There was an error putting together a message for
+ viewing. The arg is the description of the error. */
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Formatting error: %s"), decode_err);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ if(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
+ && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
+ && gf_puts(NEWLINE, pc))
+ decode_err = NULL;
+ else
+ return(decode_err);
+ }
+ }
+
+ if(!text2){
+ if(!gf_puts(NEWLINE, pc)
+ || !gf_puts(_(" [ERROR fetching text of message]"), pc)
+ || !gf_puts(NEWLINE, pc)
+ || !gf_puts(NEWLINE, pc))
+ return("Write Error");
+ }
+ }
+ else{
+ int show_parts = 0;
+
+ /*======== Now loop through formatting all the parts =======*/
+ for(a = ps_global->atmts; a->description != NULL; a++) {
+
+ if(a->body->type == TYPEMULTIPART){
+#ifdef SMIME
+ if(strucmp(a->body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0){
+ if(a->description){
+ if(!(!format_editorial(a->description, width, flgs, handlesp, pc)
+ && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
+ return("Write Error");
+ }
+ }
+#endif /* SMIME */
+ continue;
+ }
+
+ if(!a->shown) {
+ if(a->suppress_editorial)
+ continue;
+
+ if(!(flgs & FM_NOEDITORIAL)
+ && (!gf_puts(NEWLINE, pc)
+ || (decode_err = part_desc(a->number, a->body,
+ (flgs & FM_DISPLAY)
+ ? (a->can_display != MCD_NONE)
+ ? 1 : 2
+ : 3, width, flgs, pc))))
+ return("Write Error");
+
+ continue;
+ }
+
+ switch(a->body->type){
+
+ case TYPETEXT:
+ /*
+ * If a message is multipart *and* the first part of it
+ * is text *and that text is empty, there is a good chance that
+ * there was actually something there that c-client was
+ * unable to parse. Here we report the empty message body
+ * and insert the raw RFC822.TEXT (if full-headers are
+ * on).
+ */
+ if(body->type == TYPEMULTIPART
+ && a == ps_global->atmts
+ && a->body->size.bytes == 0
+ && F_ON(F_ENABLE_FULL_HDR, ps_global)){
+ char *err = NULL;
+
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,
+ "Empty or malformed message%s.",
+ ps_global->full_header == 2
+ ? ". Displaying raw text"
+ : ". Use \"H\" to see raw text");
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+
+ if(!(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
+ && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
+ && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
+ return("Write Error");
+
+ if(ps_global->full_header == 2
+ && (err = detach_raw(ps_global->mail_stream, msgno,
+ a->number, pc, flgs))){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,
+ "%s%s [ Formatting error: %s ]%s%s",
+ NEWLINE, NEWLINE, err, NEWLINE, NEWLINE);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ if(!gf_puts(tmp_20k_buf, pc))
+ return("Write Error");
+ }
+
+ break;
+ }
+
+ /*
+ * Don't write our delimiter if this text part is
+ * the first part of a message/rfc822 segment...
+ */
+ if(show_parts && a != ps_global->atmts
+ && !((a[-1].body && a[-1].body->type == TYPEMESSAGE)
+#ifdef SMIME
+ || (a[-1].body->type == TYPEMULTIPART
+ && a[-1].body->subtype
+ && (strucmp(a[-1].body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0)
+ && &a[-1] != ps_global->atmts
+ && a[-2].body && a[-2].body->type == TYPEMESSAGE)
+#endif /* SMIME */
+ )
+ && !(flgs & FM_NOEDITORIAL)){
+ tmp1 = a->body->description ? a->body->description
+ : "Attached Text";
+ description = iutf8ncpy((char *)(tmp_20k_buf+10000),
+ (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+15000), 5000, tmp1), 5000);
+
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Part %s: \"%.1024s\"", a->number,
+ description);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ if(!(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
+ && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
+ && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
+ return("Write Error");
+ }
+
+ error_found += decode_text(a, msgno, pc, handlesp,
+ (flgs & FM_DISPLAY) ? InLine : QStatus,
+ flgs);
+ break;
+
+ case TYPEMESSAGE:
+ tmp1 = a->body->description ? a->body->description
+ : (strucmp(a->body->subtype, "delivery-status") == 0)
+ ? "Delivery Status"
+ : "Included Message";
+ description = iutf8ncpy((char *)(tmp_20k_buf+10000),
+ (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+15000), 5000, tmp1), 5000);
+
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Part %s: \"%.1024s\"", a->number,
+ description);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+
+ if(!(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
+ && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
+ && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
+ return("Write Error");
+
+ if(a->body->subtype && strucmp(a->body->subtype, "rfc822") == 0){
+ /* imapenvonly, we may not have all the headers we need */
+ if(a->body->nested.msg->env->imapenvonly)
+ mail_fetch_header(ps_global->mail_stream, msgno,
+ a->number, NULL, NULL, FT_PEEK);
+ switch(format_header(ps_global->mail_stream, msgno, a->number,
+ a->body->nested.msg->env, hp,
+ NULL, handlesp, flgs, NULL, pc)){
+ case -1 : /* write error */
+ return("Write Error");
+
+ case 1 : /* fetch error */
+ if(!(gf_puts("[ Error fetching header ]", pc)
+ && !gf_puts(NEWLINE, pc)))
+ return("Write Error");
+
+ break;
+ }
+ }
+ else if(a->body->subtype && strucmp(a->body->subtype, "external-body") == 0){
+ int *margin, avail, m1, m2;
+
+ avail = width;
+ margin = (flgs & FM_NOINDENT) ? NULL : format_view_margin();
+
+ m1 = MAX(MIN(margin ? margin[0] : 0, avail), 0);
+ avail -= m1;
+
+ m2 = MAX(MIN(margin ? margin[1] : 0, avail), 0);
+ avail -= m2;
+
+ if(format_editorial("This part is not included and can be fetched as follows:", avail, flgs, handlesp, pc)
+ || !gf_puts(NEWLINE, pc)
+ || format_editorial(display_parameters(a->body->parameter), avail, flgs, handlesp, pc))
+ return("Write Error");
+ }
+ else
+ error_found += decode_text(a, msgno, pc, handlesp,
+ (flgs&FM_DISPLAY) ? InLine : QStatus,
+ flgs);
+
+ if(!gf_puts(NEWLINE, pc))
+ return("Write Error");
+
+ break;
+
+ default:
+ if((decode_err = part_desc(a->number, a->body,
+ (flgs & FM_DISPLAY) ? 1 : 3,
+ width, flgs, pc)) != NULL)
+ return("Write Error");
+ }
+
+ show_parts++;
+ }
+
+ if(!(!error_found
+ && (pith_opt_rfc2369_editorial ? (*pith_opt_rfc2369_editorial)(msgno, handlesp, flgs, width, pc) : 1)
+ && format_blip_seen(msgno)))
+ return("Cannot format body.");
+ }
+
+ return(NULL);
+}
+
+
+int
+format_attachment_list(long int msgno, BODY *body, HANDLE_S **handlesp, int flgs, int width, gf_io_t pc)
+{
+ ATTACH_S *a;
+
+ if(flgs & FM_NEW_MESS) {
+ zero_atmts(ps_global->atmts);
+ describe_mime(body, "", 1, 1, 0, flgs);
+ }
+
+ /*----- First do the list of parts/attachments if needed ----*/
+ if((flgs & FM_DISPLAY)
+ && (ps_global->atmts[1].description
+ || (ps_global->atmts[0].body
+ && ps_global->atmts[0].body->type != TYPETEXT))){
+ char tmp[6*MAX_SCREEN_COLS + 1], *tmpp;
+ int i, n, maxnumwid = 0, maxsizewid = 0, *margin;
+ int avail, m1, m2, hwid, s1, s2, s3, s4, s5, dwid, shownwid;
+ int sizewid, descwid, dashwid, partwid, padwid;
+ COLOR_PAIR *hdrcolor = NULL;
+
+ if((flgs & FM_DISPLAY)
+ && !(flgs & FM_NOCOLOR)
+ && pico_usingcolor()
+ && ps_global->VAR_HEADER_GENERAL_FORE_COLOR
+ && ps_global->VAR_HEADER_GENERAL_BACK_COLOR
+ && ps_global->VAR_NORM_FORE_COLOR
+ && ps_global->VAR_NORM_BACK_COLOR
+ && (colorcmp(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
+ ps_global->VAR_NORM_FORE_COLOR)
+ || colorcmp(ps_global->VAR_HEADER_GENERAL_BACK_COLOR,
+ ps_global->VAR_NORM_BACK_COLOR))){
+
+ if((hdrcolor = new_color_pair(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
+ ps_global->VAR_HEADER_GENERAL_BACK_COLOR)) != NULL){
+ if(!pico_is_good_colorpair(hdrcolor))
+ free_color_pair(&hdrcolor);
+ }
+ }
+
+ margin = (flgs & FM_NOINDENT) ? NULL : format_view_margin();
+
+ /*
+ * Attachment list header
+ */
+
+ avail = width;
+
+ m1 = MAX(MIN(margin ? margin[0] : 0, avail), 0);
+ avail -= m1;
+
+ m2 = MAX(MIN(margin ? margin[1] : 0, avail), 0);
+ avail -= m2;
+
+ hwid = MAX(avail, 0);
+
+ i = utf8_width(_("Parts/Attachments:"));
+ partwid = MIN(i, hwid);
+ padwid = hdrcolor ? (hwid-partwid) : 0;
+
+ if(m1 > 0){
+ snprintf(tmp, sizeof(tmp), "%*.*s", m1, m1, "");
+ if(!gf_puts(tmp, pc))
+ return(0);
+ }
+
+ utf8_snprintf(tmp, sizeof(tmp),
+ "%-*.*w%*.*s",
+ /* TRANSLATORS: A label */
+ partwid, partwid, _("Parts/Attachments:"),
+ padwid, padwid, "");
+
+ if(!((!hdrcolor || embed_color(hdrcolor, pc)) && gf_puts(tmp, pc) && gf_puts(NEWLINE, pc)))
+ return(0);
+
+
+ /*----- Figure max display widths -----*/
+ for(a = ps_global->atmts; a->description != NULL; a++){
+ if((n = utf8_width(a->number)) > maxnumwid)
+ maxnumwid = n;
+
+ if((n = utf8_width(a->size)) > maxsizewid)
+ maxsizewid = n;
+ }
+
+ /*
+ * ----- adjust max lengths for nice display -----
+ *
+ * marg _ D _ number _ Shown _ _ _ size _ _ description marg
+ *
+ */
+
+ avail = width - m1 - m2;
+
+ s1 = MAX(MIN(1, avail), 0);
+ avail -= s1;
+
+ dwid = MAX(MIN(1, avail), 0);
+ avail -= dwid;
+
+ s2 = MAX(MIN(1, avail), 0);
+ avail -= s2;
+
+ maxnumwid = MIN(maxnumwid, width/3);
+ maxnumwid = MAX(MIN(maxnumwid, avail), 0);
+ avail -= maxnumwid;
+
+ s3 = MAX(MIN(1, avail), 0);
+ avail -= s3;
+
+ shownwid = MAX(MIN(5, avail), 0);
+ avail -= shownwid;
+
+ s4 = MAX(MIN(3, avail), 0);
+ avail -= s4;
+
+ sizewid = MAX(MIN(maxsizewid, avail), 0);
+ avail -= sizewid;
+
+ s5 = MAX(MIN(2, avail), 0);
+ avail -= s5;
+
+ descwid = MAX(0, avail);
+
+ /*----- Format the list of attachments -----*/
+ for(a = ps_global->atmts; a->description != NULL; a++){
+ COLOR_PAIR *lastc = NULL;
+ char numbuf[50];
+ int thisdescwid, padwid;
+
+#ifdef SMIME
+ if(a->body->type == TYPEMULTIPART
+ && (strucmp(a->body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0))
+ continue;
+#endif /* SMIME */
+
+ i = utf8_width((descwid > 2 && a->description) ? a->description : "");
+ thisdescwid = MIN(i, descwid);
+ padwid = hdrcolor ? (descwid-thisdescwid) : 0;
+
+ if(m1 > 0){
+ snprintf(tmp, sizeof(tmp), "%*.*s", m1, m1, "");
+ if(!gf_puts(tmp, pc))
+ return(0);
+ }
+
+ utf8_snprintf(tmp, sizeof(tmp),
+ "%*.*s%*.*w%*.*s%-*.*w%*.*s%*.*w%*.*s%*.*w%*.*s%-*.*w",
+ s1, s1, "",
+ dwid, dwid,
+ msgno_part_deleted(ps_global->mail_stream, msgno, a->number) ? "D" : "",
+ s2, s2, "",
+ maxnumwid, maxnumwid,
+ a->number
+ ? short_str(a->number, numbuf, sizeof(numbuf), maxnumwid, FrontDots)
+ : "",
+ s3, s3, "",
+ shownwid, shownwid,
+ a->shown ? "Shown" :
+ (a->can_display != MCD_NONE && !(a->can_display & MCD_EXT_PROMPT))
+ ? "OK " : "",
+ s4, s4, "",
+ sizewid, sizewid,
+ a->size ? a->size : "",
+ s5, s5, "",
+ thisdescwid, thisdescwid,
+ (descwid > 2 && a->description) ? a->description : "");
+
+ if(!(!hdrcolor || embed_color(hdrcolor, pc)))
+ return(0);
+
+ if(F_ON(F_VIEW_SEL_ATTACH, ps_global) && handlesp){
+ char buf[16], color[64];
+ int l;
+ HANDLE_S *h;
+
+ for(tmpp = tmp; *tmpp && *tmpp == ' '; tmpp++)
+ if(!(*pc)(' '))
+ return(0);
+
+ h = new_handle(handlesp);
+ h->type = Attach;
+ h->h.attach = a;
+
+ snprintf(buf, sizeof(buf), "%d", h->key);
+ buf[sizeof(buf)-1] = '\0';
+
+ if(!(flgs & FM_NOCOLOR)
+ && handle_start_color(color, sizeof(color), &l, 1)){
+ lastc = get_cur_embedded_color();
+ if(!gf_nputs(color, (long) l, pc))
+ return(0);
+ }
+ else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)
+ && (!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDON))))
+ return(0);
+
+ if(!((*pc)(TAG_EMBED) && (*pc)(TAG_HANDLE)
+ && (*pc)(strlen(buf)) && gf_puts(buf, pc)))
+ return(0);
+ }
+ else
+ tmpp = tmp;
+
+ if(!format_env_puts(tmpp, pc))
+ return(0);
+
+ if(F_ON(F_VIEW_SEL_ATTACH, ps_global) && handlesp){
+ if(lastc){
+ if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
+ if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
+ return(0);
+ }
+
+ if(!embed_color(lastc, pc))
+ return(0);
+
+ free_color_pair(&lastc);
+ }
+ else if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
+ return(0);
+
+ if(!((*pc)(TAG_EMBED) && (*pc)(TAG_INVOFF)))
+ return(0);
+ }
+
+ if(padwid > 0){
+ snprintf(tmp, sizeof(tmp), "%*.*s", padwid, padwid, "");
+ if(!gf_puts(tmp, pc))
+ return(0);
+ }
+
+ if(!gf_puts(NEWLINE, pc))
+ return(0);
+ }
+
+ /*
+ * Dashed line after list
+ */
+
+ if(hdrcolor){
+ avail = width - m1 - m2;
+ hwid = MAX(avail, 0);
+
+ dashwid = MAX(MIN(40, hwid-2), 0);
+ padwid = hwid - dashwid;
+ if(m1 > 0){
+ snprintf(tmp, sizeof(tmp), "%*.*s", m1, m1, "");
+ if(!gf_puts(tmp, pc))
+ return(0);
+ }
+
+ snprintf(tmp, sizeof(tmp),
+ "%s%*.*s",
+ repeat_char(dashwid, '-'),
+ padwid, padwid, "");
+ }
+ else{
+ avail = width - m1 -2;
+
+ dashwid = MAX(MIN(40, avail), 0);
+ avail -= dashwid;
+
+ snprintf(tmp, sizeof(tmp),
+ "%*.*s%s",
+ m1, m1, "",
+ repeat_char(dashwid, '-'));
+ }
+
+ if(!((!hdrcolor || embed_color(hdrcolor, pc)) && gf_puts(tmp, pc) && gf_puts(NEWLINE, pc)))
+ return(0);
+
+ if(hdrcolor)
+ free_color_pair(&hdrcolor);
+ }
+
+ return(1);
+}
+
+
+
+/*
+ * format_blip_seen - if seen bit (which is usually cleared as a side-effect
+ * of body part fetches as we're formatting) for the
+ * given message isn't set (likely because there
+ * weren't any parts suitable for display), then make
+ * sure to set it here.
+ */
+int
+format_blip_seen(long int msgno)
+{
+ MESSAGECACHE *mc;
+
+ if(msgno > 0L && ps_global->mail_stream
+ && msgno <= ps_global->mail_stream->nmsgs
+ && (mc = mail_elt(ps_global->mail_stream, msgno))
+ && !mc->seen
+ && !ps_global->mail_stream->rdonly)
+ mail_flag(ps_global->mail_stream, long2string(msgno), "\\SEEN", ST_SET);
+
+ return(1);
+}
+
+
+/*
+ * is_an_env_hdr - is this name a header in the envelope structure?
+ *
+ * name - the header name to check
+ */
+int
+is_an_env_hdr(char *name)
+{
+ register int i;
+
+ for(i = 0; envelope_hdrs[i].name; i++)
+ if(!strucmp(name, envelope_hdrs[i].name))
+ return(1);
+
+ return(0);
+}
+
+
+
+
+/*
+ * is_an_addr_hdr - is this an address header?
+ *
+ * name - the header name to check
+ */
+int
+is_an_addr_hdr(char *fieldname)
+{
+ char fbuf[FBUF_LEN+1];
+ char *colon, *fname;
+ static char *addr_headers[] = {
+ "from",
+ "reply-to",
+ "to",
+ "cc",
+ "bcc",
+ "return-path",
+ "sender",
+ "x-sender",
+ "x-x-sender",
+ "resent-from",
+ "resent-to",
+ "resent-cc",
+ NULL
+ };
+
+ /* so it is pointing to NULL */
+ char **p = addr_headers + sizeof(addr_headers)/sizeof(*addr_headers) - 1;
+
+ if((colon = strindex(fieldname, ':')) != NULL){
+ strncpy(fbuf, fieldname, MIN(colon-fieldname,sizeof(fbuf)));
+ fbuf[MIN(colon-fieldname,sizeof(fbuf)-1)] = '\0';
+ fname = fbuf;
+ }
+ else
+ fname = fieldname;
+
+ if(fname && *fname){
+ for(p = addr_headers; *p; p++)
+ if(!strucmp(fname, *p))
+ break;
+ }
+
+ return((*p) ? 1 : 0);
+}
+
+
+/*
+ * Format a single field from the envelope
+ */
+void
+format_env_hdr(MAILSTREAM *stream, long int msgno, char *section, ENVELOPE *env,
+ fmt_env_t fmt_env, gf_io_t pc, char *field, char *oacs, int flags)
+{
+ register int i;
+
+ if(!fmt_env)
+ fmt_env = format_envelope;
+
+ for(i = 0; envelope_hdrs[i].name; i++)
+ if(!strucmp(field, envelope_hdrs[i].name)){
+ (*fmt_env)(stream, msgno, section, env, pc, envelope_hdrs[i].val, oacs, flags);
+ return;
+ }
+}
+
+
+/*
+ * Look through header string beginning with "begin", for the next
+ * occurrence of header "field". Set "start" to that. Set "end" to point one
+ * position past all of the continuation lines that go with "field".
+ * That is, if "end" is converted to a null
+ * character then the string "start" will be the next occurence of header
+ * "field" including all of its continuation lines. Assume we
+ * have CRLF's as end of lines.
+ *
+ * If "field" is NULL, then we just leave "start" pointing to "begin" and
+ * make "end" the end of that header.
+ *
+ * Returns 1 if found, 0 if not.
+ */
+int
+delineate_this_header(char *field, char *begin, char **start, char **end)
+{
+ char tmpfield[MAILTMPLEN+2]; /* copy of field with colon appended */
+ char *p;
+ char *begin_srch;
+
+ if(field == NULL){
+ if(!begin || !*begin || isspace((unsigned char)*begin))
+ return 0;
+ else
+ *start = begin;
+ }
+ else{
+ strncpy(tmpfield, field, sizeof(tmpfield)-2);
+ tmpfield[sizeof(tmpfield)-2] = '\0';
+ strncat(tmpfield, ":", sizeof(tmpfield)-strlen(tmpfield)-1);
+ tmpfield[sizeof(tmpfield)-1] = '\0';
+
+ /*
+ * We require that start is at the beginning of a line, so
+ * either it equals begin (which we assume is the beginning of a
+ * line) or it is preceded by a CRLF.
+ */
+ begin_srch = begin;
+ *start = srchstr(begin_srch, tmpfield);
+ while(*start && *start != begin
+ && !(*start - 2 >= begin && ISRFCEOL(*start - 2))){
+ begin_srch = *start + 1;
+ *start = srchstr(begin_srch, tmpfield);
+ }
+
+ if(!*start)
+ return 0;
+ }
+
+ for(p = *start; *p; p++){
+ if(ISRFCEOL(p)
+ && (!isspace((unsigned char)*(p+2)) || *(p+2) == '\015')){
+ /*
+ * The final 015 in the test above is to test for the end
+ * of the headers.
+ */
+ *end = p+2;
+ break;
+ }
+ }
+
+ if(!*p)
+ *end = p;
+
+ return 1;
+}
+
+
+
+int
+handle_start_color(char *colorstring, size_t buflen, int *len, int use_hdr_color)
+{
+ *len = 0;
+
+ if(pico_usingcolor()){
+ char *fg = NULL, *bg = NULL, *s;
+ char *basefg = NULL, *basebg = NULL;
+
+ basefg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_FORE_COLOR
+ : ps_global->VAR_NORM_FORE_COLOR;
+ basebg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_BACK_COLOR
+ : ps_global->VAR_NORM_BACK_COLOR;
+
+ if(ps_global->VAR_SLCTBL_FORE_COLOR
+ && colorcmp(ps_global->VAR_SLCTBL_FORE_COLOR, basefg))
+ fg = ps_global->VAR_SLCTBL_FORE_COLOR;
+
+ if(ps_global->VAR_SLCTBL_BACK_COLOR
+ && colorcmp(ps_global->VAR_SLCTBL_BACK_COLOR, basebg))
+ bg = ps_global->VAR_SLCTBL_BACK_COLOR;
+
+ if(bg || fg){
+ COLOR_PAIR *tmp;
+
+ /*
+ * The blacks are just known good colors for
+ * testing whether the other color is good.
+ */
+ if((tmp = new_color_pair(fg ? fg : colorx(COL_BLACK),
+ bg ? bg : colorx(COL_BLACK))) != NULL){
+ if(pico_is_good_colorpair(tmp))
+ for(s = color_embed(fg, bg);
+ (*len) < buflen && (colorstring[*len] = *s);
+ s++, (*len)++)
+ ;
+
+ free_color_pair(&tmp);
+ }
+ }
+
+ if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
+ strncpy(colorstring + (*len), url_embed(TAG_BOLDON), MIN(3,buflen-(*len)));
+ *len += 2;
+ }
+ }
+
+ colorstring[buflen-1] = '\0';
+
+ return(*len != 0);
+}
+
+
+int
+handle_end_color(char *colorstring, size_t buflen, int *len)
+{
+ *len = 0;
+ if(pico_usingcolor()){
+ char *fg = NULL, *bg = NULL, *s;
+
+ /*
+ * We need to change the fg and bg colors back even if they
+ * are the same as the Normal Colors so that color_a_quote
+ * will have a chance to pick up those colors as the ones to
+ * switch to. We don't do this before the handle above so that
+ * the quote color will flow into the selectable item when
+ * the selectable item color is partly the same as the
+ * normal color. That is, suppose the normal color was black on
+ * cyan and the selectable color was blue on cyan, only a fg color
+ * change. We preserve the only-a-fg-color-change in a quote by
+ * letting the quote background color flow into the selectable text.
+ */
+ if(ps_global->VAR_SLCTBL_FORE_COLOR)
+ fg = ps_global->VAR_NORM_FORE_COLOR;
+
+ if(ps_global->VAR_SLCTBL_BACK_COLOR)
+ bg = ps_global->VAR_NORM_BACK_COLOR;
+
+ if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
+ strncpy(colorstring, url_embed(TAG_BOLDOFF), MIN(3,buflen));
+ *len = 2;
+ }
+
+ if(fg || bg)
+ for(s = color_embed(fg, bg); (*len) < buflen && (colorstring[*len] = *s); s++, (*len)++)
+ ;
+ }
+
+ colorstring[buflen-1] = '\0';
+
+ return(*len != 0);
+}
+
+
+char *
+url_embed(int embed)
+{
+ static char buf[3] = {TAG_EMBED};
+ buf[1] = embed;
+ buf[2] = '\0';
+ return(buf);
+}
+
+
+/*
+ * Paint the signature.
+ */
+int
+color_signature(long int linenum, char *line, LT_INS_S **ins, void *is_in_sig)
+{
+ struct variable *vars = ps_global->vars;
+ int *in_sig_block;
+ COLOR_PAIR *col = NULL;
+
+ if(is_in_sig == NULL)
+ return 0;
+
+ in_sig_block = (int *) is_in_sig;
+
+ if(!strcmp(line, SIGDASHES))
+ *in_sig_block = START_SIG_BLOCK;
+ else if(*line == '\0')
+ /*
+ * Suggested by Eduardo: allow for a blank line right after
+ * the sigdashes.
+ */
+ *in_sig_block = (*in_sig_block == START_SIG_BLOCK)
+ ? IN_SIG_BLOCK : OUT_SIG_BLOCK;
+ else
+ *in_sig_block = (*in_sig_block != OUT_SIG_BLOCK)
+ ? IN_SIG_BLOCK : OUT_SIG_BLOCK;
+
+ if(*in_sig_block != OUT_SIG_BLOCK
+ && VAR_SIGNATURE_FORE_COLOR && VAR_SIGNATURE_BACK_COLOR
+ && (col = new_color_pair(VAR_SIGNATURE_FORE_COLOR,
+ VAR_SIGNATURE_BACK_COLOR))){
+ if(!pico_is_good_colorpair(col))
+ free_color_pair(&col);
+ }
+
+ if(col){
+ char *p, fg[RGBLEN + 1], bg[RGBLEN + 1], rgbbuf[RGBLEN + 1];
+
+ ins = gf_line_test_new_ins(ins, line,
+ color_embed(col->fg, col->bg),
+ (2 * RGBLEN) + 4);
+
+ strncpy(fg, color_to_asciirgb(VAR_NORM_FORE_COLOR), sizeof(fg));
+ fg[sizeof(fg)-1] = '\0';
+ strncpy(bg, color_to_asciirgb(VAR_NORM_BACK_COLOR), sizeof(bg));
+ bg[sizeof(bg)-1] = '\0';
+
+ /*
+ * Loop watching colors, and override with
+ * signature color whenever the normal foreground and background
+ * colors are in force.
+ */
+
+ for(p = line; *p; )
+ if(*p++ == TAG_EMBED){
+
+ switch(*p++){
+ case TAG_HANDLE :
+ p += *p + 1; /* skip handle key */
+ break;
+
+ case TAG_FGCOLOR :
+ snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
+ rgbbuf[sizeof(rgbbuf)-1] = '\0';
+ p += RGBLEN; /* advance past color value */
+
+ if(!colorcmp(rgbbuf, VAR_NORM_FORE_COLOR)
+ && !colorcmp(bg, VAR_NORM_BACK_COLOR))
+ ins = gf_line_test_new_ins(ins, p,
+ color_embed(col->fg,NULL),
+ RGBLEN + 2);
+ break;
+
+ case TAG_BGCOLOR :
+ snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
+ rgbbuf[sizeof(rgbbuf)-1] = '\0';
+ p += RGBLEN; /* advance past color value */
+
+ if(!colorcmp(rgbbuf, VAR_NORM_BACK_COLOR)
+ && !colorcmp(fg, VAR_NORM_FORE_COLOR))
+ ins = gf_line_test_new_ins(ins, p,
+ color_embed(NULL,col->bg),
+ RGBLEN + 2);
+
+ break;
+
+ default :
+ break;
+ }
+ }
+
+ ins = gf_line_test_new_ins(ins, line + strlen(line),
+ color_embed(VAR_NORM_FORE_COLOR,
+ VAR_NORM_BACK_COLOR),
+ (2 * RGBLEN) + 4);
+ free_color_pair(&col);
+ }
+
+ return 0;
+}
+
+
+/*
+ * Line filter to add color to displayed headers.
+ */
+int
+color_headers(long int linenum, char *line, LT_INS_S **ins, void *local)
+{
+ static char field[FBUF_LEN + 1];
+ char fg[RGBLEN + 1], bg[RGBLEN + 1], rgbbuf[RGBLEN + 1];
+ char *p, *q, *value, *beg;
+ COLOR_PAIR *color;
+ int in_quote = 0, in_comment = 0, did_color = 0;
+ struct variable *vars = ps_global->vars;
+
+ field[FBUF_LEN] = '\0';
+
+ if(isspace((unsigned char)*line)) /* continuation line */
+ value = line;
+ else{
+ if(!(value = strindex(line, ':')))
+ return(0);
+
+ memset(field, 0, sizeof(field));
+ strncpy(field, line, MIN(value-line, sizeof(field)-1));
+ }
+
+ for(value++; isspace((unsigned char)*value); value++)
+ ;
+
+ strncpy(fg, color_to_asciirgb(VAR_HEADER_GENERAL_FORE_COLOR), sizeof(fg));
+ fg[sizeof(fg)-1] = '\0';
+ strncpy(bg, color_to_asciirgb(VAR_HEADER_GENERAL_BACK_COLOR), sizeof(bg));
+ bg[sizeof(bg)-1] = '\0';
+
+ /*
+ * Split into two cases depending on whether this is a header which
+ * contains addresses or not. We may color addresses separately.
+ */
+ if(is_an_addr_hdr(field)){
+
+ /*
+ * If none of the patterns are for this header, don't bother parsing
+ * and checking each address.
+ */
+ if(!any_hdr_color(field))
+ return(0);
+
+ /*
+ * First check for patternless patterns which color whole line.
+ */
+ if((color = hdr_color(field, NULL, ps_global->hdr_colors)) != NULL){
+ if(pico_is_good_colorpair(color)){
+ ins = gf_line_test_new_ins(ins, value,
+ color_embed(color->fg, color->bg),
+ (2 * RGBLEN) + 4);
+ strncpy(fg, color_to_asciirgb(color->fg), sizeof(fg));
+ fg[sizeof(fg)-1] = '\0';
+ strncpy(bg, color_to_asciirgb(color->bg), sizeof(bg));
+ bg[sizeof(bg)-1] = '\0';
+ did_color++;
+ }
+ else
+ free_color_pair(&color);
+ }
+
+ /*
+ * Then go through checking address by address.
+ * Keep track of quotes and watch for color changes, and override
+ * with most recent header color whenever the normal foreground
+ * and background colors are in force.
+ */
+ beg = p = value;
+ while(*p){
+ switch(*p){
+ case '\\':
+ /* skip next character */
+ if(*(p+1) && (in_comment || in_quote))
+ p += 2;
+ else
+ p++;
+
+ break;
+
+ case '"':
+ if(!in_comment)
+ in_quote = 1 - in_quote;
+
+ p++;
+ break;
+
+ case '(':
+ in_comment++;
+ p++;
+ break;
+
+ case ')':
+ if(in_comment > 0)
+ in_comment--;
+
+ p++;
+ break;
+
+ case ',':
+ if(!(in_quote || in_comment)){
+ /* we reached the end of this address */
+ *p = '\0';
+ if(color)
+ free_color_pair(&color);
+
+ if((color = hdr_color(field, beg,
+ ps_global->hdr_colors)) != NULL){
+ if(pico_is_good_colorpair(color)){
+ did_color++;
+ ins = gf_line_test_new_ins(ins, beg,
+ color_embed(color->fg,
+ color->bg),
+ (2 * RGBLEN) + 4);
+ *p = ',';
+ for(q = p; q > beg &&
+ isspace((unsigned char)*(q-1)); q--)
+ ;
+
+ ins = gf_line_test_new_ins(ins, q,
+ color_embed(fg, bg),
+ (2 * RGBLEN) + 4);
+ }
+ else
+ free_color_pair(&color);
+ }
+ else
+ *p = ',';
+
+ for(p++; isspace((unsigned char)*p); p++)
+ ;
+
+ beg = p;
+ }
+ else
+ p++;
+
+ break;
+
+ case TAG_EMBED:
+ switch(*(++p)){
+ case TAG_HANDLE:
+ p++;
+ p += *p + 1; /* skip handle key */
+ break;
+
+ case TAG_FGCOLOR:
+ p++;
+ snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
+ rgbbuf[sizeof(rgbbuf)-1] = '\0';
+ p += RGBLEN; /* advance past color value */
+
+ if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_FORE_COLOR))
+ ins = gf_line_test_new_ins(ins, p,
+ color_embed(color->fg,NULL),
+ RGBLEN + 2);
+ break;
+
+ case TAG_BGCOLOR:
+ p++;
+ snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
+ rgbbuf[sizeof(rgbbuf)-1] = '\0';
+ p += RGBLEN; /* advance past color value */
+
+ if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_BACK_COLOR))
+ ins = gf_line_test_new_ins(ins, p,
+ color_embed(NULL,color->bg),
+ RGBLEN + 2);
+
+ break;
+
+ default:
+ break;
+ }
+
+ break;
+
+ default:
+ p++;
+ break;
+ }
+ }
+
+ for(q = beg; *q && isspace((unsigned char)*q); q++)
+ ;
+
+ if(*q && !(in_quote || in_comment)){
+ /* we reached the end of this address */
+ if(color)
+ free_color_pair(&color);
+
+ if((color = hdr_color(field, beg, ps_global->hdr_colors)) != NULL){
+ if(pico_is_good_colorpair(color)){
+ did_color++;
+ ins = gf_line_test_new_ins(ins, beg,
+ color_embed(color->fg,
+ color->bg),
+ (2 * RGBLEN) + 4);
+ for(q = p; q > beg && isspace((unsigned char)*(q-1)); q--)
+ ;
+
+ ins = gf_line_test_new_ins(ins, q,
+ color_embed(fg, bg),
+ (2 * RGBLEN) + 4);
+ }
+ else
+ free_color_pair(&color);
+ }
+ }
+
+ if(color)
+ free_color_pair(&color);
+
+ if(did_color)
+ ins = gf_line_test_new_ins(ins, line + strlen(line),
+ color_embed(VAR_HEADER_GENERAL_FORE_COLOR,
+ VAR_HEADER_GENERAL_BACK_COLOR),
+ (2 * RGBLEN) + 4);
+ }
+ else{
+
+ color = hdr_color(field, value, ps_global->hdr_colors);
+
+ if(color){
+ if(pico_is_good_colorpair(color)){
+ ins = gf_line_test_new_ins(ins, value,
+ color_embed(color->fg, color->bg),
+ (2 * RGBLEN) + 4);
+
+ /*
+ * Loop watching colors, and override with header
+ * color whenever the normal foreground and background
+ * colors are in force.
+ */
+ p = value;
+ while(*p)
+ if(*p++ == TAG_EMBED){
+
+ switch(*p++){
+ case TAG_HANDLE:
+ p += *p + 1; /* skip handle key */
+ break;
+
+ case TAG_FGCOLOR:
+ snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
+ rgbbuf[sizeof(rgbbuf)-1] = '\0';
+ p += RGBLEN; /* advance past color value */
+
+ if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_FORE_COLOR)
+ && !colorcmp(bg, VAR_HEADER_GENERAL_BACK_COLOR))
+ ins = gf_line_test_new_ins(ins, p,
+ color_embed(color->fg,NULL),
+ RGBLEN + 2);
+ break;
+
+ case TAG_BGCOLOR:
+ snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
+ rgbbuf[sizeof(rgbbuf)-1] = '\0';
+ p += RGBLEN; /* advance past color value */
+
+ if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_BACK_COLOR)
+ && !colorcmp(fg, VAR_HEADER_GENERAL_FORE_COLOR))
+ ins = gf_line_test_new_ins(ins, p,
+ color_embed(NULL,color->bg),
+ RGBLEN + 2);
+
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ ins = gf_line_test_new_ins(ins, line + strlen(line),
+ color_embed(VAR_HEADER_GENERAL_FORE_COLOR,
+ VAR_HEADER_GENERAL_BACK_COLOR),
+ (2 * RGBLEN) + 4);
+ }
+
+ free_color_pair(&color);
+ }
+ }
+
+ return(0);
+}
+
+
+int
+url_hilite(long int linenum, char *line, LT_INS_S **ins, void *local)
+{
+ register char *lp, *up = NULL, *urlp = NULL,
+ *weburlp = NULL, *mailurlp = NULL;
+ int n, n1, n2, n3, l;
+ char buf[256], color[256];
+ HANDLE_S *h;
+ URL_HILITE_S *uh;
+
+ for(lp = line; ; lp = up + n){
+ /* scan for all of them so we can choose the first */
+ if(F_ON(F_VIEW_SEL_URL,ps_global))
+ urlp = rfc1738_scan(lp, &n1);
+ if(F_ON(F_VIEW_SEL_URL_HOST,ps_global))
+ weburlp = web_host_scan(lp, &n2);
+ if(F_ON(F_SCAN_ADDR,ps_global))
+ mailurlp = mail_addr_scan(lp, &n3);
+
+ if(urlp || weburlp || mailurlp){
+ up = urlp ? urlp :
+ weburlp ? weburlp : mailurlp;
+ if(up == urlp && weburlp && weburlp < up)
+ up = weburlp;
+ if(mailurlp && mailurlp < up)
+ up = mailurlp;
+
+ if(up == urlp){
+ n = n1;
+ weburlp = mailurlp = NULL;
+ }
+ else if(up == weburlp){
+ n = n2;
+ mailurlp = NULL;
+ }
+ else{
+ n = n3;
+ weburlp = NULL;
+ }
+ }
+ else
+ break;
+
+ uh = (URL_HILITE_S *) local;
+
+ h = new_handle(uh->handlesp);
+ h->type = URL;
+ h->h.url.path = (char *) fs_get((n + 10) * sizeof(char));
+ snprintf(h->h.url.path, n+10, "%s%.*s",
+ weburlp ? "http://" : (mailurlp ? "mailto:" : ""), n, up);
+ h->h.url.path[n+10-1] = '\0';
+
+ if(handle_start_color(color, sizeof(color), &l, uh->hdr_color))
+ ins = gf_line_test_new_ins(ins, up, color, l);
+ else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global))
+ ins = gf_line_test_new_ins(ins, up, url_embed(TAG_BOLDON), 2);
+
+ buf[0] = TAG_EMBED;
+ buf[1] = TAG_HANDLE;
+ snprintf(&buf[3], sizeof(buf)-3, "%d", h->key);
+ buf[sizeof(buf)-1] = '\0';
+ buf[2] = strlen(&buf[3]);
+ ins = gf_line_test_new_ins(ins, up, buf, (int) buf[2] + 3);
+
+ /* in case it was the current selection */
+ ins = gf_line_test_new_ins(ins, up + n, url_embed(TAG_INVOFF), 2);
+
+ if(scroll_handle_end_color(color, sizeof(color), &l, uh->hdr_color))
+ ins = gf_line_test_new_ins(ins, up + n, color, l);
+ else
+ ins = gf_line_test_new_ins(ins, up + n, url_embed(TAG_BOLDOFF), 2);
+
+ urlp = weburlp = mailurlp = NULL;
+ }
+
+ return(0);
+}
+
+
+int
+url_hilite_hdr(long int linenum, char *line, LT_INS_S **ins, void *local)
+{
+ static int check_for_urls = 0;
+ register char *lp;
+
+ if(isspace((unsigned char)*line)) /* continuation, check or not
+ depending on last line */
+ lp = line;
+ else{
+ check_for_urls = 0;
+ if((lp = strchr(line, ':')) != NULL){ /* there ought to always be a colon */
+ FieldType ft;
+
+ *lp = '\0';
+
+ if(((ft = pine_header_standard(line)) == FreeText
+ || ft == Subject
+ || ft == TypeUnknown)
+ && strucmp(line, "message-id")
+ && strucmp(line, "newsgroups")
+ && strucmp(line, "references")
+ && strucmp(line, "in-reply-to")
+ && strucmp(line, "received")
+ && strucmp(line, "date")){
+ check_for_urls = 1;
+ }
+
+ *lp = ':';
+ }
+ }
+
+ if(check_for_urls)
+ (void) url_hilite(linenum, lp + 1, ins, local);
+
+ return(0);
+}
+
+
+int
+pad_to_right_edge(long int linenum, char *line, LT_INS_S **ins, void *local)
+{
+ char *p;
+ int wid = 0;
+ int total_wid;
+ struct variable *vars = ps_global->vars;
+
+ if(!line[0])
+ return 0;
+
+ total_wid = *((int *) local);
+
+ /* calculate width of line */
+ p = line;
+ while(*p){
+
+ switch(*p){
+ case TAG_EMBED:
+ p++;
+ switch(*p){
+ case TAG_HANDLE:
+ p++;
+ p += *p + 1; /* skip handle key */
+ break;
+
+ case TAG_FGCOLOR :
+ case TAG_BGCOLOR :
+ p += (RGBLEN + 1); /* 1 for TAG, RGBLEN for color */
+ break;
+
+ case TAG_INVON:
+ case TAG_INVOFF:
+ case TAG_BOLDON:
+ case TAG_BOLDOFF:
+ case TAG_ULINEON:
+ case TAG_ULINEOFF:
+ p++;
+ break;
+
+ default: /* literal embed char */
+ break;
+ }
+
+ break;
+
+ case TAB:
+ p++;
+ while(((++wid) & 0x07) != 0) /* add tab's spaces */
+ ;
+
+ break;
+
+ default:
+ wid += width_at_this_position((unsigned char *) p, strlen(p));
+ p++;
+ break;
+ }
+ }
+
+ if(total_wid > wid){
+ ins = gf_line_test_new_ins(ins, line + strlen(line),
+ color_embed(VAR_HEADER_GENERAL_FORE_COLOR,
+ VAR_HEADER_GENERAL_BACK_COLOR),
+ (2 * RGBLEN) + 4);
+ ins = gf_line_test_new_ins(ins, line+strlen(line),
+ repeat_char(total_wid-wid, ' '), total_wid-wid);
+ ins = gf_line_test_new_ins(ins, line + strlen(line),
+ color_embed(VAR_NORM_FORE_COLOR,
+ VAR_NORM_BACK_COLOR),
+ (2 * RGBLEN) + 4);
+ }
+
+ return(0);
+}
+
+
+
+#define UES_LEN 12
+#define UES_MAX 32
+int
+url_external_specific_handler(char *url, int len)
+{
+ static char list[UES_LEN * UES_MAX];
+
+ if(url){
+ char *p;
+ int i;
+
+ for(i = 0; i < UES_MAX && *(p = &list[i * UES_LEN]); i++)
+ if(strlen(p) == len && !struncmp(p, url, len))
+ return(1);
+ }
+ else{ /* initialize! */
+ char **l, *test, *cmd, *p, *p2;
+ int i = 0, n;
+
+ memset(list, 0, sizeof(list));
+ for(l = ps_global->VAR_BROWSER ; l && *l; l++){
+ get_pair(*l, &test, &cmd, 1, 1);
+
+ if((p = srchstr(test, "_scheme(")) && (p2 = strstr(p+8, ")_"))){
+ *p2 = '\0';
+
+ for(p += 8; *p && i < UES_MAX; p += n)
+ if((p2 = strchr(p, ',')) != NULL){
+ if((n = p2 - p) < UES_LEN){
+ strncpy(&list[i * UES_LEN], p, MIN(n, sizeof(list)-(i * UES_LEN)));
+ i++;
+ }
+ else
+ dprint((1,
+ "* * * HANLDER TOO LONG: %.*s\n", n,
+ p ? p : "?"));
+
+ n++;
+ }
+ else{
+ if(strlen(p) <= UES_LEN){
+ strncpy(&list[i * UES_LEN], p, sizeof(list)-(i * UES_LEN));
+ i++;
+ }
+
+ break;
+ }
+ }
+
+ if(test)
+ fs_give((void **) &test);
+
+ if(cmd)
+ fs_give((void **) &cmd);
+ }
+ }
+
+ return(0);
+}
+
+
+int
+url_imap_folder(char *true_url, char **folder, imapuid_t *uid_val,
+ imapuid_t *uid, char **search, int silent)
+{
+ char *url, *scheme, *p, *cmd, *server = NULL,
+ *user = NULL, *auth = NULL, *mailbox = NULL,
+ *section = NULL;
+ size_t l;
+ int rv = URL_IMAP_ERROR;
+
+ /*
+ * Since we're planting nulls, operate on a temporary copy...
+ */
+ scheme = silent ? NULL : "IMAP";
+ url = cpystr(true_url + 7);
+
+ /* Try to pick apart the "iserver" portion */
+ if((cmd = strchr(url, '/')) != NULL){ /* iserver "/" [mailbox] ? */
+ *cmd++ = '\0';
+ }
+ else{
+ dprint((2, "-- URL IMAP FOLDER: missing: %s\n",
+ url ? url : "?"));
+ cmd = &url[strlen(url)-1]; /* assume only iserver */
+ }
+
+ if((p = strchr(url, '@')) != NULL){ /* user | auth | pass? */
+ *p++ = '\0';
+ server = rfc1738_str(p);
+
+ /* only ";auth=*" supported (and also ";auth=anonymous") */
+ if((p = srchstr(url, ";auth=")) != NULL){
+ *p = '\0';
+ auth = rfc1738_str(p + 6);
+ }
+
+ if(*url)
+ user = rfc1738_str(url);
+ }
+ else
+ server = rfc1738_str(url);
+
+ if(!*server)
+ return(url_bogus_imap(&url, scheme, "No server specified"));
+
+ /*
+ * "iserver" in hand, pick apart the "icommand"...
+ */
+ p = NULL;
+ if(!*cmd || (p = srchstr(cmd, ";type="))){
+ char *criteria;
+
+ /*
+ * No "icommand" (all top-level folders) or "imailboxlist"...
+ */
+ if(p){
+ *p = '\0'; /* tie off criteria */
+ criteria = rfc1738_str(cmd); /* get "enc_list_mailbox" */
+ if(!strucmp(p = rfc1738_str(p+6), "lsub"))
+ rv |= URL_IMAP_IMBXLSTLSUB;
+ else if(strucmp(p, "list"))
+ return(url_bogus_imap(&url, scheme,
+ "Invalid list type specified"));
+ }
+ else{
+ rv |= URL_IMAP_ISERVERONLY;
+ criteria = "";
+ }
+
+ /* build folder list from specified server/criteria/list-method */
+ l = strlen(server) + strlen(criteria) + 10 + (user ? (strlen(user)+2) : 9);
+ *folder = (char *) fs_get((l+1) * sizeof(char));
+ snprintf(*folder, l+1, "{%s/%s%s%s}%s%s%s", server,
+ user ? "user=\"" : "Anonymous",
+ user ? user : "",
+ user ? "\"" : "",
+ *criteria ? "[" : "", criteria, *criteria ? "[" : "");
+ (*folder)[l] = '\0';
+ rv |= URL_IMAP_IMAILBOXLIST;
+ }
+ else{
+ if((p = srchstr(cmd, "/;uid=")) != NULL){ /* "imessagepart" */
+ *p = '\0'; /* tie off mailbox [uidvalidity] */
+ if((section = srchstr(p += 6, "/;section=")) != NULL){
+ *section = '\0'; /* tie off UID */
+ section = rfc1738_str(section + 10);
+/* BUG: verify valid section spec ala rfc 2060 */
+ dprint((2,
+ "-- URL IMAP FOLDER: section not used: %s\n",
+ section ? section : "?"));
+ }
+
+ if(!(*uid = rfc1738_num(&p)) || *p) /* decode UID */
+ return(url_bogus_imap(&url, scheme, "Invalid data in UID"));
+
+ /* optional "uidvalidity"? */
+ if((p = srchstr(cmd, ";uidvalidity=")) != NULL){
+ *p = '\0';
+ p += 13;
+ if(!(*uid_val = rfc1738_num(&p)) || *p)
+ return(url_bogus_imap(&url, scheme,
+ "Invalid UIDVALIDITY"));
+ }
+
+ mailbox = rfc1738_str(cmd);
+ rv = URL_IMAP_IMESSAGEPART;
+ }
+ else{ /* "imessagelist" */
+ /* optional "uidvalidity"? */
+ if((p = srchstr(cmd, ";uidvalidity=")) != NULL){
+ *p = '\0';
+ p += 13;
+ if(!(*uid_val = rfc1738_num(&p)) || *p)
+ return(url_bogus_imap(&url, scheme,
+ "Invalid UIDVALIDITY"));
+ }
+
+ /* optional "enc_search"? */
+ if((p = strchr(cmd, '?')) != NULL){
+ *p = '\0';
+ if(search)
+ *search = cpystr(rfc1738_str(p + 1));
+/* BUG: verify valid search spec ala rfc 2060 */
+ }
+
+ mailbox = rfc1738_str(cmd);
+ rv = URL_IMAP_IMESSAGELIST;
+ }
+
+ if(auth && *auth != '*' && strucmp(auth, "anonymous"))
+ q_status_message(SM_ORDER, 3, 3,
+ "Unsupported authentication method. Using standard login.");
+
+ /*
+ * At this point our structure should contain the
+ * digested url. Now put it together for c-client...
+ */
+ l = strlen(server) + 8 + (mailbox ? strlen(mailbox) : 0)
+ + (user ? (strlen(user)+2) : 9);
+ *folder = (char *) fs_get((l+1) * sizeof(char));
+ snprintf(*folder, l+1, "{%s%s%s%s%s}%s", server,
+ (user || !(auth && strucmp(auth, "anonymous"))) ? "/" : "",
+ user ? "user=\"" : ((auth && strucmp(auth, "anonymous")) ? "" : "Anonymous"),
+ user ? user : "",
+ user ? "\"" : "",
+ mailbox);
+ (*folder)[l] = '\0';
+ }
+
+ fs_give((void **) &url);
+ return(rv);
+}
+
+
+int
+url_bogus_imap(char **freeme, char *url, char *problem)
+{
+ fs_give((void **) freeme);
+ (void) url_bogus(url, problem);
+ return(URL_IMAP_ERROR);
+}
+
+
+/*
+ * url_bogus - report url syntax errors and such
+ */
+int
+url_bogus(char *url, char *reason)
+{
+ dprint((2, "-- bogus url \"%s\": %s\n",
+ url ? url : "<NULL URL>", reason ? reason : "?"));
+ if(url)
+ q_status_message3(SM_ORDER|SM_DING, 2, 3,
+ "Malformed \"%.*s\" URL: %.200s",
+ (void *) (strchr(url, ':') - url), url, reason);
+
+ return(0);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Format header text suitable for display
+
+ Args: stream -- mail stream for various header text fetches
+ msgno -- sequence number in stream of message we're interested in
+ section -- which section of message
+ env -- pointer to msg's envelope
+ hdrs -- struct containing what's to get formatted
+ prefix -- prefix to append to each output line
+ handlesp -- address of pointer to the message's handles
+ flags -- FM_ flags, see pith/mailview.h
+ final_pc -- function to write header text with
+
+ Result: 0 if all's well, -1 if write error, 1 if fetch error
+
+ NOTE: Blank-line delimiter is NOT written here. Newlines are written
+ in the local convention.
+
+ ----*/
+#define FHT_OK 0
+#define FHT_WRTERR -1
+#define FHT_FTCHERR 1
+int
+format_header(MAILSTREAM *stream, long int msgno, char *section, ENVELOPE *env,
+ HEADER_S *hdrs, char *prefix, HANDLE_S **handlesp, int flags,
+ fmt_env_t fmt_env, gf_io_t final_pc)
+{
+ int rv = FHT_OK;
+ int nfields, i;
+ char *h = NULL, **fields = NULL, **v, *q, *start,
+ *finish, *current;
+ STORE_S *tmp_store;
+ URL_HILITE_S uh;
+ gf_io_t tmp_pc, tmp_gc;
+ struct variable *vars = ps_global->vars;
+
+ if((tmp_store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL)
+ gf_set_so_writec(&tmp_pc, tmp_store);
+ else
+ return(FHT_WRTERR);
+
+ if(!fmt_env)
+ fmt_env = format_envelope;
+
+ if(ps_global->full_header == 2){
+ rv = format_raw_header(stream, msgno, section, tmp_pc);
+ }
+ else{
+ /*
+ * First, calculate how big a fields array we need.
+ */
+
+ /* Custom header viewing list specified */
+ if(hdrs->type == HD_LIST){
+ /* view all these headers */
+ if(!hdrs->except){
+ for(nfields = 0, v = hdrs->h.l; (q = *v) != NULL; v++)
+ if(!is_an_env_hdr(q))
+ nfields++;
+ }
+ /* view all except these headers */
+ else{
+ for(nfields = 0, v = hdrs->h.l; *v != NULL; v++)
+ nfields++;
+
+ if(nfields > 1)
+ nfields--; /* subtract one for ALL_EXCEPT field */
+ }
+ }
+ else
+ nfields = 6; /* default view */
+
+ /* allocate pointer space */
+ if(nfields){
+ fields = (char **)fs_get((size_t)(nfields+1) * sizeof(char *));
+ memset(fields, 0, (size_t)(nfields+1) * sizeof(char *));
+ }
+
+ if(hdrs->type == HD_LIST){
+ /* view all these headers */
+ if(!hdrs->except){
+ /* put the non-envelope headers in fields */
+ if(nfields)
+ for(i = 0, v = hdrs->h.l; (q = *v) != NULL; v++)
+ if(!is_an_env_hdr(q))
+ fields[i++] = q;
+ }
+ /* view all except these headers */
+ else{
+ /* put the list of headers not to view in fields */
+ if(nfields)
+ for(i = 0, v = hdrs->h.l; (q = *v) != NULL; v++)
+ if(strucmp(ALL_EXCEPT, q))
+ fields[i++] = q;
+ }
+
+ v = hdrs->h.l;
+ }
+ else{
+ if(nfields){
+ fields[i = 0] = "Resent-Date";
+ fields[++i] = "Resent-From";
+ fields[++i] = "Resent-To";
+ fields[++i] = "Resent-cc";
+ fields[++i] = "Resent-Subject";
+ }
+
+ v = fields;
+ }
+
+ /* custom view with exception list */
+ if(hdrs->type == HD_LIST && hdrs->except){
+ /*
+ * Go through each header in h and print it.
+ * First we check to see if it is an envelope header so we
+ * can print our envelope version of it instead of the raw version.
+ */
+
+ /* fetch all the other headers */
+ if(nfields)
+ h = pine_fetchheader_lines_not(stream, msgno, section, fields);
+
+ for(current = h;
+ h && delineate_this_header(NULL, current, &start, &finish);
+ current = finish){
+ char tmp[MAILTMPLEN+1];
+ char *colon_loc;
+
+ colon_loc = strindex(start, ':');
+ if(colon_loc && colon_loc < finish){
+ strncpy(tmp, start, MIN(colon_loc-start, sizeof(tmp)-1));
+ tmp[MIN(colon_loc-start, sizeof(tmp)-1)] = '\0';
+ }
+ else
+ colon_loc = NULL;
+
+ if(colon_loc && is_an_env_hdr(tmp)){
+ char *dummystart, *dummyfinish;
+
+ /*
+ * Pretty format for env hdrs.
+ * If the same header appears more than once, only
+ * print the last to avoid duplicates.
+ * They should have been combined in the env when parsed.
+ */
+ if(!delineate_this_header(tmp, current+1, &dummystart,
+ &dummyfinish))
+ format_env_hdr(stream, msgno, section, env,
+ fmt_env, tmp_pc, tmp, hdrs->charset, flags);
+ }
+ else{
+ if((rv = format_raw_hdr_string(start, finish, tmp_pc,
+ hdrs->charset, flags)) != 0)
+ goto write_error;
+ else
+ start = finish;
+ }
+ }
+ }
+ /* custom view or default */
+ else{
+ /* fetch the non-envelope headers */
+ if(nfields)
+ h = pine_fetchheader_lines(stream, msgno, section, fields);
+
+ /* default envelope for default view */
+ if(hdrs->type == HD_BFIELD)
+ (*fmt_env)(stream, msgno, section, env, tmp_pc, hdrs->h.b, hdrs->charset, flags);
+
+ /* go through each header in list, v initialized above */
+ for(; (q = *v) != NULL; v++){
+ if(is_an_env_hdr(q)){
+ /* pretty format for env hdrs */
+ format_env_hdr(stream, msgno, section, env,
+ fmt_env, tmp_pc, q, hdrs->charset, flags);
+ }
+ else{
+ /*
+ * Go through h finding all occurences of this header
+ * and all continuation lines, and output.
+ */
+ for(current = h;
+ h && delineate_this_header(q,current,&start,&finish);
+ current = finish){
+ if((rv = format_raw_hdr_string(start, finish, tmp_pc,
+ hdrs->charset, flags)) != 0)
+ goto write_error;
+ else
+ start = finish;
+ }
+ }
+ }
+ }
+ }
+
+
+ write_error:
+
+ gf_clear_so_writec(tmp_store);
+
+ if(!rv){ /* valid data? Do wrapping and filtering... */
+ int column;
+ char *errstr, *display_filter = NULL, trigger[MAILTMPLEN];
+ STORE_S *df_store = NULL;
+
+ so_seek(tmp_store, 0L, 0);
+
+ column = (flags & FM_DISPLAY) ? ps_global->ttyo->screen_cols : 80;
+
+ /*
+ * Test for and act on any display filter
+ * This barely makes sense. The display filter is going
+ * to be getting UTF-8'ized headers here. In pre-alpine
+ * pine the display filter was being fed already decoded
+ * headers in whatever character set they were in.
+ * The good news is that that didn't make much
+ * sense either, so this shouldn't break anything.
+ * It seems unlikely that anybody is doing anything useful
+ * with the header part of display filters.
+ */
+ if(ps_global->tools.display_filter
+ && ps_global->tools.display_filter_trigger
+ && (display_filter = (*ps_global->tools.display_filter_trigger)(NULL, trigger, sizeof(trigger)))){
+ if((df_store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
+
+ gf_set_so_writec(&tmp_pc, df_store);
+ gf_set_so_readc(&tmp_gc, df_store);
+ if((errstr = (*ps_global->tools.display_filter)(display_filter, tmp_store, tmp_pc, NULL)) != NULL){
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("Formatting error: %s"), errstr);
+ rv = FHT_WRTERR;
+ }
+ else
+ so_seek(df_store, 0L, 0);
+
+ gf_clear_so_writec(df_store);
+ }
+ else{
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ "No space for filtered text.");
+ rv = FHT_WRTERR;
+ }
+ }
+ else{
+ so_seek(tmp_store, 0L, 0);
+ gf_set_so_readc(&tmp_gc, tmp_store);
+ }
+
+ if(!rv){
+ int *margin, wrapflags = GFW_ONCOMMA;
+
+ gf_filter_init();
+ gf_link_filter(gf_local_nvtnl, NULL);
+
+ if((F_ON(F_VIEW_SEL_URL, ps_global)
+ || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
+ || F_ON(F_SCAN_ADDR, ps_global))
+ && handlesp){
+ gf_link_filter(gf_line_test,
+ gf_line_test_opt(url_hilite_hdr,
+ gf_url_hilite_opt(&uh,handlesp,1)));
+ wrapflags |= GFW_HANDLES;
+ }
+
+ if((flags & FM_DISPLAY)
+ && !(flags & FM_NOCOLOR)
+ && pico_usingcolor()
+ && ((VAR_NORM_FORE_COLOR
+ && VAR_HEADER_GENERAL_FORE_COLOR
+ && colorcmp(VAR_NORM_FORE_COLOR, VAR_HEADER_GENERAL_FORE_COLOR))
+ ||
+ (VAR_NORM_BACK_COLOR
+ && VAR_HEADER_GENERAL_BACK_COLOR
+ && colorcmp(VAR_NORM_BACK_COLOR, VAR_HEADER_GENERAL_BACK_COLOR))))
+ wrapflags |= GFW_HDRCOLOR;
+
+ if((flags & FM_DISPLAY)
+ && !(flags & FM_NOCOLOR)
+ && pico_usingcolor()
+ && ps_global->hdr_colors){
+ gf_link_filter(gf_line_test,
+ gf_line_test_opt(color_headers, NULL));
+ wrapflags |= (GFW_HANDLES | GFW_HDRCOLOR);
+ }
+
+ if(prefix && *prefix)
+ column = MAX(column-strlen(prefix), 50);
+
+ margin = format_view_margin();
+
+ if(!(flags & FM_NOWRAP))
+ gf_link_filter(gf_wrap,
+ gf_wrap_filter_opt(column, column,
+ (flags & FM_NOINDENT) ? NULL : margin,
+ 4, wrapflags));
+
+ if(prefix && *prefix)
+ gf_link_filter(gf_prefix, gf_prefix_opt(prefix));
+
+ if((flags & FM_DISPLAY)
+ && !(flags & FM_NOCOLOR)
+ && pico_usingcolor()
+ && ((VAR_NORM_FORE_COLOR
+ && VAR_HEADER_GENERAL_FORE_COLOR
+ && colorcmp(VAR_NORM_FORE_COLOR, VAR_HEADER_GENERAL_FORE_COLOR))
+ ||
+ (VAR_NORM_BACK_COLOR
+ && VAR_HEADER_GENERAL_BACK_COLOR
+ && colorcmp(VAR_NORM_BACK_COLOR, VAR_HEADER_GENERAL_BACK_COLOR)))){
+ int right_margin;
+ int total_wid;
+
+ right_margin = margin ? margin[1] : 0;
+ total_wid = column - right_margin;
+
+ gf_link_filter(gf_line_test,
+ gf_line_test_opt(pad_to_right_edge, (void *) &total_wid));
+ wrapflags |= GFW_HANDLES;
+ }
+
+ gf_link_filter(gf_nvtnl_local, NULL);
+
+ if((errstr = gf_pipe(tmp_gc, final_pc)) != NULL){
+ rv = FHT_WRTERR;
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ "Can't build header : %.200s", errstr);
+ }
+ }
+
+ if(df_store){
+ gf_clear_so_readc(df_store);
+ so_give(&df_store);
+ }
+ else
+ gf_clear_so_readc(tmp_store);
+ }
+
+ so_give(&tmp_store);
+
+ if(h)
+ fs_give((void **)&h);
+
+ if(fields)
+ fs_give((void **)&fields);
+
+ return(rv);
+}
+
+
+/*----------------------------------------------------------------------
+ Format RAW header text for display
+
+ Args: stream -- mail stream for various header text fetches
+ rawno -- sequence number in stream of message we're interested in
+ section -- which section of message
+ pc -- function to write header text with
+
+ Result: 0 if all's well, -1 if write error, 1 if fetch error
+
+ NOTE: Blank-line delimiter is NOT written here. Newlines are written
+ in the local convention.
+
+ ----*/
+int
+format_raw_header(MAILSTREAM *stream, long int msgno, char *section, gf_io_t pc)
+{
+ char *h = mail_fetch_header(stream, msgno, section, NULL, NULL, FT_PEEK);
+ unsigned char c;
+
+ if(h){
+ while(*h){
+ if(ISRFCEOL(h)){
+ h += 2;
+ if(!gf_puts(NEWLINE, pc))
+ return(FHT_WRTERR);
+
+ if(ISRFCEOL(h)) /* all done! */
+ return(FHT_OK);
+ }
+ else if((unsigned char)(*h) < 0x80 && FILTER_THIS(*h) &&
+ !(*(h+1) && *h == ESCAPE && match_escapes(h+1))){
+ c = (unsigned char) *h++;
+ if(!((*pc)(c >= 0x80 ? '~' : '^')
+ && (*pc)((c == 0x7f) ? '?' : (c & 0x1f) + '@')))
+ return(FHT_WRTERR);
+ }
+ else if(!(*pc)(*h++))
+ return(FHT_WRTERR);
+ }
+ }
+ else
+ return(FHT_FTCHERR);
+
+ return(FHT_OK);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Format c-client envelope data suitable for display
+
+ Args: s -- mail stream for various header text fetches
+ n -- raw sequence number in stream of message we're interested in
+ sect -- which section of message
+ e -- pointer to msg's envelope
+ pc -- function to write header text with
+ which -- which header lines to write
+ oacs --
+ flags -- FM_ flags, see pith/mailview.h
+
+ Result: 0 if all's well, -1 if write error, 1 if fetch error
+
+ NOTE: Blank-line delimiter is NOT written here. Newlines are written
+ in the local convention.
+
+ ----*/
+void
+format_envelope(MAILSTREAM *s, long int n, char *sect, ENVELOPE *e, gf_io_t pc,
+ long int which, char *oacs, int flags)
+{
+ char *q, *p2, buftmp[MAILTMPLEN];
+
+ if(!e)
+ return;
+
+ if((which & FE_DATE) && e->date) {
+ q = "Date: ";
+ snprintf(buftmp, sizeof(buftmp), "%s",
+ F_ON(F_DATES_TO_LOCAL,ps_global)
+ ? convert_date_to_local((char *) e->date) : (char *) e->date);
+ buftmp[sizeof(buftmp)-1] = '\0';
+ p2 = (char *)rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
+ SIZEOF_20KBUF, buftmp);
+ gf_puts(q, pc);
+ format_env_puts(p2, pc);
+ gf_puts(NEWLINE, pc);
+ }
+
+ if((which & FE_FROM) && e->from)
+ format_addr_string(s, n, sect, "From: ", e->from, flags, oacs, pc);
+
+ if((which & FE_REPLYTO) && e->reply_to
+ && (!e->from || !address_is_same(e->reply_to, e->from)))
+ format_addr_string(s, n, sect, "Reply-To: ", e->reply_to, flags, oacs, pc);
+
+ if((which & FE_TO) && e->to)
+ format_addr_string(s, n, sect, "To: ", e->to, flags, oacs, pc);
+
+ if((which & FE_CC) && e->cc)
+ format_addr_string(s, n, sect, "Cc: ", e->cc, flags, oacs, pc);
+
+ if((which & FE_BCC) && e->bcc)
+ format_addr_string(s, n, sect, "Bcc: ", e->bcc, flags, oacs, pc);
+
+ if((which & FE_RETURNPATH) && e->return_path)
+ format_addr_string(s, n, sect, "Return-Path: ", e->return_path,
+ flags, oacs, pc);
+
+ if((which & FE_NEWSGROUPS) && e->newsgroups){
+ int bogus = NIL;
+ format_newsgroup_string("Newsgroups: ", e->newsgroups, flags, pc);
+ if (!e->ngpathexists && e->message_id &&
+ strncmp (e->message_id,"<alpine.",8) &&
+ strncmp (e->message_id,"<Pine.",6) &&
+ strncmp (e->message_id,"<MS-C.",6) &&
+ strncmp (e->message_id,"<MailManager.",13) &&
+ strncmp (e->message_id,"<EasyMail.",11) &&
+ strncmp (e->message_id,"<ML-",4)) bogus = T;
+
+ if(bogus)
+ q_status_message(SM_ORDER, 0, 3,
+ "Unverified Newsgroup header -- Message MAY or MAY NOT have been posted");
+ }
+
+ if((which & FE_FOLLOWUPTO) && e->followup_to)
+ format_newsgroup_string("Followup-To: ", e->followup_to, flags, pc);
+
+ if((which & FE_SUBJECT) && e->subject && e->subject[0]){
+ char *freeme = NULL;
+
+ q = "Subject: ";
+ gf_puts(q, pc);
+
+ p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->subject), SIZEOF_20KBUF-10000);
+
+ if(flags & FM_DISPLAY
+ && (ps_global->display_keywords_in_subject
+ || ps_global->display_keywordinits_in_subject)){
+
+ /* don't bother if no keywords are defined */
+ if(some_user_flags_defined(s))
+ p2 = freeme = prepend_keyword_subject(s, n, p2,
+ ps_global->display_keywords_in_subject ? KW : KWInit,
+ NULL, ps_global->VAR_KW_BRACES);
+ }
+
+ format_env_puts(p2, pc);
+
+ if(freeme)
+ fs_give((void **) &freeme);
+
+ gf_puts(NEWLINE, pc);
+ }
+
+ if((which & FE_SENDER) && e->sender
+ && (!e->from || !address_is_same(e->sender, e->from)))
+ format_addr_string(s, n, sect, "Sender: ", e->sender, flags, oacs, pc);
+
+ if((which & FE_MESSAGEID) && e->message_id){
+ q = "Message-ID: ";
+ gf_puts(q, pc);
+ p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->message_id), SIZEOF_20KBUF-10000);
+ format_env_puts(p2, pc);
+ gf_puts(NEWLINE, pc);
+ }
+
+ if((which & FE_INREPLYTO) && e->in_reply_to){
+ q = "In-Reply-To: ";
+ gf_puts(q, pc);
+ p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->in_reply_to), SIZEOF_20KBUF-10000);
+ format_env_puts(p2, pc);
+ gf_puts(NEWLINE, pc);
+ }
+
+ if((which & FE_REFERENCES) && e->references) {
+ q = "References: ";
+ gf_puts(q, pc);
+ p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->references), SIZEOF_20KBUF-10000);
+ format_env_puts(p2, pc);
+ gf_puts(NEWLINE, pc);
+ }
+}
+
+
+
+
+/*
+ * The argument fieldname is something like "Subject:..." or "Subject".
+ * Look through the specs in speccolor for a match of the fieldname,
+ * and return the color that goes with any match, or NULL.
+ * Caller should free the color.
+ */
+COLOR_PAIR *
+hdr_color(char *fieldname, char *value, SPEC_COLOR_S *speccolor)
+{
+ SPEC_COLOR_S *hc = NULL;
+ COLOR_PAIR *color_pair = NULL;
+ char *colon, *fname;
+ char fbuf[FBUF_LEN+1];
+ int gotit;
+ PATTERN_S *pat;
+
+ colon = strindex(fieldname, ':');
+ if(colon){
+ strncpy(fbuf, fieldname, MIN(colon-fieldname,FBUF_LEN));
+ fbuf[MIN(colon-fieldname,FBUF_LEN)] = '\0';
+ fname = fbuf;
+ }
+ else
+ fname = fieldname;
+
+ if(fname && *fname)
+ for(hc = speccolor; hc; hc = hc->next)
+ if(hc->spec && !strucmp(fname, hc->spec)){
+ if(!hc->val)
+ break;
+
+ gotit = 0;
+ for(pat = hc->val; !gotit && pat; pat = pat->next)
+ if(srchstr(value, pat->substring))
+ gotit++;
+
+ if(gotit)
+ break;
+ }
+
+ if(hc && hc->fg && hc->fg[0] && hc->bg && hc->bg[0])
+ color_pair = new_color_pair(hc->fg, hc->bg);
+
+ return(color_pair);
+}
+
+
+/*
+ * The argument fieldname is something like "Subject:..." or "Subject".
+ * Look through the specs in hdr_colors for a match of the fieldname,
+ * and return 1 if that fieldname is in one of the patterns, 0 otherwise.
+ */
+int
+any_hdr_color(char *fieldname)
+{
+ SPEC_COLOR_S *hc = NULL;
+ char *colon, *fname;
+ char fbuf[FBUF_LEN+1];
+
+ colon = strindex(fieldname, ':');
+ if(colon){
+ strncpy(fbuf, fieldname, MIN(colon-fieldname,FBUF_LEN));
+ fbuf[MIN(colon-fieldname,FBUF_LEN)] = '\0';
+ fname = fbuf;
+ }
+ else
+ fname = fieldname;
+
+ if(fname && *fname)
+ for(hc = ps_global->hdr_colors; hc; hc = hc->next)
+ if(hc->spec && !strucmp(fname, hc->spec))
+ break;
+
+ return(hc ? 1 : 0);
+}
+
+
+/*----------------------------------------------------------------------
+ Format an address field, wrapping lines nicely at commas
+
+ Args: field_name -- The name of the field we're formatting ("TO: ", ...)
+ addr -- ADDRESS structure to format
+
+ Result: A formatted, malloced string is returned.
+ ----------------------------------------------------------------------*/
+void
+format_addr_string(MAILSTREAM *stream, long int msgno, char *section, char *field_name,
+ struct mail_address *addr, int flags, char *oacs, gf_io_t pc)
+{
+ char *ptmp, *mtmp;
+ int trailing = 0, group = 0;
+ ADDRESS *atmp;
+
+ if(!addr)
+ return;
+
+ /*
+ * quickly run down address list to make sure none are patently bogus.
+ * If so, just blat raw field out.
+ */
+ for(atmp = addr; stream && atmp; atmp = atmp->next)
+ if(atmp->host && atmp->host[0] == '.'){
+ char *field, *fields[2];
+
+ fields[1] = NULL;
+ fields[0] = cpystr(field_name);
+ if((ptmp = strchr(fields[0], ':')) != NULL)
+ *ptmp = '\0';
+
+ if((field = pine_fetchheader_lines(stream, msgno, section, fields)) != NULL){
+ char *h, *t;
+
+ for(t = h = field; *h ; t++)
+ if(*t == '\015' && *(t+1) == '\012'){
+ *t = '\0'; /* tie off line */
+ format_env_puts(h, pc);
+ if(*(h = (++t) + 1)) /* set new h and skip CRLF */
+ gf_puts(NEWLINE, pc); /* more to write */
+ else
+ break;
+ }
+ else if(!*t){ /* shouldn't happen much */
+ if(h != t)
+ format_env_puts(h, pc);
+
+ break;
+ }
+
+ fs_give((void **)&field);
+ }
+
+ fs_give((void **)&fields[0]);
+ gf_puts(NEWLINE, pc);
+ dprint((2, "Error in \"%s\" field address\n",
+ field_name ? field_name : "?"));
+ return;
+ }
+
+ gf_puts(field_name, pc);
+
+ while(addr){
+ atmp = addr->next; /* remember what's next */
+ addr->next = NULL;
+ if(!addr->host && addr->mailbox){
+ mtmp = addr->mailbox;
+ addr->mailbox = cpystr((char *)rfc1522_decode_to_utf8(
+ (unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, addr->mailbox));
+ }
+
+ ptmp = addr->personal; /* RFC 1522 personal name? */
+ addr->personal = iutf8ncpy((char *)tmp_20k_buf, (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+10000), SIZEOF_20KBUF-10000, addr->personal), 10000);
+ tmp_20k_buf[10000-1] = '\0';
+
+ if(!trailing) /* 1st pass, just address */
+ trailing++;
+ else{ /* else comma, unless */
+ if(!((group == 1 && addr->host) /* 1st addr in group, */
+ || (!addr->host && !addr->mailbox))){ /* or end of group */
+ gf_puts(",", pc);
+#if 0
+ gf_puts(NEWLINE, pc); /* ONE address/line please */
+ gf_puts(" ", pc);
+#endif
+ }
+
+ gf_puts(" ", pc);
+ }
+
+ pine_rfc822_write_address_noquote(addr, pc, &group);
+
+ addr->personal = ptmp; /* restore old personal ptr */
+ if(!addr->host && addr->mailbox){
+ fs_give((void **)&addr->mailbox);
+ addr->mailbox = mtmp;
+ }
+
+ addr->next = atmp;
+ addr = atmp;
+ }
+
+ gf_puts(NEWLINE, pc);
+}
+
+
+
+
+const char *rspecials_minus_quote_and_dot = "()<>@,;:\\[]";
+ /* RFC822 continuation, must start with CRLF */
+#define RFC822CONT "\015\012 "
+
+/* Write RFC822 address with some quoting turned off.
+ * Accepts:
+ * address to interpret
+ *
+ * (This is a copy of c-client's rfc822_write_address except
+ * we don't quote double quote and dot in personal names. It writes
+ * to a gf_io_t instead of to a buffer so that we don't have to worry
+ * about fixed sized buffer overflowing. It's also special cased to deal
+ * with only a single address.)
+ *
+ * The idea is that there are some places where we'd just like to display
+ * the personal name as is before applying confusing quoting. However,
+ * we do want to be careful not to break things that should be quoted so
+ * we'll only use this where we are sure. Quoting may look ugly but it
+ * doesn't usually break anything.
+ */
+void
+pine_rfc822_write_address_noquote(struct mail_address *adr, gf_io_t pc, int *group)
+{
+ extern const char *rspecials;
+
+ if (adr->host) { /* ordinary address? */
+ if (!(adr->personal || adr->adl)) pine_rfc822_address (adr, pc);
+ else { /* no, must use phrase <route-addr> form */
+ if (adr->personal)
+ pine_rfc822_cat (adr->personal, rspecials_minus_quote_and_dot, pc);
+
+ gf_puts(" <", pc); /* write address delimiter */
+ pine_rfc822_address(adr, pc);
+ gf_puts (">", pc); /* closing delimiter */
+ }
+
+ if(*group)
+ (*group)++;
+ }
+ else if (adr->mailbox) { /* start of group? */
+ /* yes, write group name */
+ pine_rfc822_cat (adr->mailbox, rspecials, pc);
+
+ gf_puts (": ", pc); /* write group identifier */
+ *group = 1; /* in a group */
+ }
+ else if (*group) { /* must be end of group (but be paranoid) */
+ gf_puts (";", pc);
+ *group = 0; /* no longer in that group */
+ }
+}
+
+
+/* Write RFC822 route-address to string
+ * Accepts:
+ * address to interpret
+ */
+
+void
+pine_rfc822_address(struct mail_address *adr, gf_io_t pc)
+{
+ extern char *wspecials;
+
+ if (adr && adr->host) { /* no-op if no address */
+ if (adr->adl) { /* have an A-D-L? */
+ gf_puts (adr->adl, pc);
+ gf_puts (":", pc);
+ }
+ /* write mailbox name */
+ pine_rfc822_cat (adr->mailbox, wspecials, pc);
+ if (*adr->host != '@') { /* unless null host (HIGHLY discouraged!) */
+ gf_puts ("@", pc); /* host delimiter */
+ gf_puts (adr->host, pc); /* write host name */
+ }
+ }
+}
+
+
+/* Concatenate RFC822 string
+ * Accepts:
+ * pointer to string to concatenate
+ * list of special characters
+ */
+
+void
+pine_rfc822_cat(char *src, const char *specials, gf_io_t pc)
+{
+ char *s;
+
+ if (strpbrk (src,specials)) { /* any specials present? */
+ gf_puts ("\"", pc); /* opening quote */
+ /* truly bizarre characters in there? */
+ while ((s = strpbrk (src,"\\\"")) != NULL) {
+ char save[2];
+
+ /* turn it into a null-terminated piece */
+ save[0] = *s;
+ save[1] = '\0';
+ *s = '\0';
+ gf_puts (src, pc); /* yes, output leader */
+ *s = save[0];
+ gf_puts ("\\", pc); /* quoting */
+ gf_puts (save, pc); /* output the bizarre character */
+ src = ++s; /* continue after the bizarre character */
+ }
+ if (*src) gf_puts (src, pc);/* output non-bizarre string */
+ gf_puts ("\"", pc); /* closing quote */
+ }
+ else gf_puts (src, pc); /* otherwise it's the easy case */
+}
+
+
+/*----------------------------------------------------------------------
+ Format an address field, wrapping lines nicely at commas
+
+ Args: field_name -- The name of the field we're formatting ("TO:", Cc:...)
+ newsgrps -- ADDRESS structure to format
+
+ Result: A formatted, malloced string is returned.
+
+The resuling lines formatted are 80 columns wide.
+ ----------------------------------------------------------------------*/
+void
+format_newsgroup_string(char *field_name, char *newsgrps, int flags, gf_io_t pc)
+{
+ char buf[MAILTMPLEN];
+ int trailing = 0, llen, alen;
+ char *next_ng;
+
+ if(!newsgrps || !*newsgrps)
+ return;
+
+ gf_puts(field_name, pc);
+
+ llen = strlen(field_name);
+ while(*newsgrps){
+ for(next_ng = newsgrps; *next_ng && *next_ng != ','; next_ng++);
+ strncpy(buf, newsgrps, MIN(next_ng - newsgrps, sizeof(buf)-1));
+ buf[MIN(next_ng - newsgrps, sizeof(buf)-1)] = '\0';
+ newsgrps = next_ng;
+ if(*newsgrps)
+ newsgrps++;
+ alen = strlen(buf);
+ if(!trailing){ /* first time thru, just address */
+ llen += alen;
+ trailing++;
+ }
+ else{ /* else preceding comma */
+ gf_puts(",", pc);
+ llen++;
+
+ if(alen + llen + 1 > 76){
+ gf_puts(NEWLINE, pc);
+ gf_puts(" ", pc);
+ llen = alen + 5;
+ }
+ else{
+ gf_puts(" ", pc);
+ llen += alen + 1;
+ }
+ }
+
+ if(alen && llen > 76){ /* handle long addresses */
+ register char *q, *p = &buf[alen-1];
+
+ while(p > buf){
+ if(isspace((unsigned char)*p)
+ && (llen - (alen - (int)(p - buf))) < 76){
+ for(q = buf; q < p; q++)
+ (*pc)(*q); /* write character */
+
+ gf_puts(NEWLINE, pc);
+ gf_puts(" ", pc);
+ gf_puts(p, pc);
+ break;
+ }
+ else
+ p--;
+ }
+
+ if(p == buf) /* no reasonable break point */
+ gf_puts(buf, pc);
+ }
+ else
+ gf_puts(buf, pc);
+ }
+
+ gf_puts(NEWLINE, pc);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Format a text field that's part of some raw (non-envelope) message header
+
+ Args: start --
+ finish --
+ pc --
+
+ Result: Semi-digested text (RFC 1522 decoded, anyway) written with "pc"
+
+ ----------------------------------------------------------------------*/
+int
+format_raw_hdr_string(char *start, char *finish, gf_io_t pc, char *oacs, int flags)
+{
+ register char *current;
+ unsigned char *p, *tmp = NULL, c;
+ size_t n, len;
+ char ch;
+ int rv = FHT_OK;
+
+ ch = *finish;
+ *finish = '\0';
+
+ if((n = 4*(finish-start)) > SIZEOF_20KBUF-1){
+ len = n+1;
+ p = tmp = (unsigned char *) fs_get(len * sizeof(unsigned char));
+ }
+ else{
+ len = SIZEOF_20KBUF;
+ p = (unsigned char *) tmp_20k_buf;
+ }
+
+ if(islower((unsigned char)(*start)))
+ *start = toupper((unsigned char)(*start));
+
+ current = (char *) rfc1522_decode_to_utf8(p, len, start);
+
+ /* output from start to finish */
+ while(*current && rv == FHT_OK)
+ if(ISRFCEOL(current)){
+ if(!gf_puts(NEWLINE, pc))
+ rv = FHT_WRTERR;
+
+ current += 2;
+ }
+ else if((unsigned char)(*current) < 0x80 && FILTER_THIS(*current) &&
+ !(*(current+1) && *current == ESCAPE && match_escapes(current+1))){
+ c = (unsigned char) *current++;
+ if(!((*pc)(c >= 0x80 ? '~' : '^')
+ && (*pc)((c == 0x7f) ? '?' : (c & 0x1f) + '@')))
+ rv = FHT_WRTERR;
+ }
+ else if(!(*pc)(*current++))
+ rv = FHT_WRTERR;
+
+ if(tmp)
+ fs_give((void **) &tmp);
+
+ *finish = ch;
+
+ return(rv);
+}
+
+
+
+
+/*----------------------------------------------------------------------
+ Format a text field that's part of some raw (non-envelope) message header
+
+ Args: s --
+ pc --
+
+ Result: Output
+
+ ----------------------------------------------------------------------*/
+int
+format_env_puts(char *s, gf_io_t pc)
+{
+ if(ps_global->pass_ctrl_chars)
+ return(gf_puts(s, pc));
+
+ for(; *s; s++)
+ if((unsigned char)(*s) < 0x80 && FILTER_THIS(*s) && !(*(s+1) && *s == ESCAPE && match_escapes(s+1))){
+ if(!((*pc)((unsigned char) (*s) >= 0x80 ? '~' : '^')
+ && (*pc)((*s == 0x7f) ? '?' : (*s & 0x1f) + '@')))
+ return(0);
+ }
+ else if(!(*pc)(*s))
+ return(0);
+
+ return(1);
+}
+
+
+char *
+display_parameters(PARAMETER *params)
+{
+ int n, longest = 0;
+ char *d, *printme;
+ PARAMETER *p;
+ PARMLIST_S *parmlist;
+
+ for(p = params; p; p = p->next) /* ok if we include *'s */
+ if(p->attribute && (n = strlen(p->attribute)) > longest)
+ longest = MIN(32, n); /* shouldn't be any bigger than 32 */
+
+ d = tmp_20k_buf;
+ tmp_20k_buf[0] = '\0';
+ if((parmlist = rfc2231_newparmlist(params)) != NULL){
+ n = 0; /* n overloaded */
+ while(rfc2231_list_params(parmlist) && d < tmp_20k_buf + 10000){
+ if(n++){
+ snprintf(d, 10000-(d-tmp_20k_buf), "\n");
+ tmp_20k_buf[10000-1] = '\0';
+ d += strlen(d);
+ }
+
+ if(parmlist->value){
+ if(parmlist->attrib && strucmp(parmlist->attrib, "url") == 0){
+ snprintf(printme = tmp_20k_buf + 11000, 1000, "%s", parmlist->value);
+ sqzspaces(printme);
+ }
+ else
+ printme = strsquish(tmp_20k_buf + 11000, 1000, parmlist->value, 100);
+ }
+ else
+ printme = "";
+
+ snprintf(d, 10000-(d-tmp_20k_buf), "%-*s: %s", longest,
+ parmlist->attrib ? parmlist->attrib : "", printme);
+
+ tmp_20k_buf[10000-1] = '\0';
+ d += strlen(d);
+ }
+
+ rfc2231_free_parmlist(&parmlist);
+ }
+
+ return(tmp_20k_buf);
+}
+
+
+/*----------------------------------------------------------------------
+ Fetch the requested header fields from the msgno specified
+
+ Args: stream -- mail stream of open folder
+ msgno -- number of message to get header lines from
+ fields -- array of pointers to desired fields
+
+ Returns: allocated string containing matched header lines,
+ NULL on error.
+ ----*/
+char *
+pine_fetch_header(MAILSTREAM *stream, long int msgno, char *section, char **fields, long int flags)
+{
+ STRINGLIST *sl;
+ char *p, *m, *h = NULL, *match = NULL, *free_this, tmp[MAILTMPLEN];
+ char **pflds = NULL, **pp = NULL, **qq;
+
+ /*
+ * If the user misconfigures it is possible to have one of the fields
+ * set to the empty string instead of a header name. We want to catch
+ * that here instead of asking the server the nonsensical question.
+ */
+ for(pp = fields ? &fields[0] : NULL; pp && *pp; pp++)
+ if(!**pp)
+ break;
+
+ if(pp && *pp){ /* found an empty header field, fix it */
+ pflds = copy_list_array(fields);
+ for(pp = pflds; pp && *pp; pp++){
+ if(!**pp){ /* scoot rest of the lines up */
+ free_this = *pp;
+ for(qq = pp; *qq; qq++)
+ *qq = *(qq+1);
+
+ if(free_this)
+ fs_give((void **) &free_this);
+ }
+ }
+
+ /* no headers to look for, return NULL */
+ if(pflds && !*pflds && !(flags & FT_NOT)){
+ free_list_array(&pflds);
+ return(NULL);
+ }
+ }
+ else
+ pflds = fields;
+
+ sl = (pflds && *pflds) ? new_strlst(pflds) : NULL; /* package up fields */
+ h = mail_fetch_header(stream, msgno, section, sl, NULL, flags | FT_PEEK);
+ if (sl)
+ free_strlst(&sl);
+
+ if(!h){
+ if(pflds && pflds != fields)
+ free_list_array(&pflds);
+
+ return(NULL);
+ }
+
+ while(find_field(&h, tmp, sizeof(tmp))){
+ for(pp = &pflds[0]; *pp && strucmp(tmp, *pp); pp++)
+ ;
+
+ /* interesting field? */
+ if((p = (flags & FT_NOT) ? ((*pp) ? NULL : tmp) : *pp) != NULL){
+ /*
+ * Hold off allocating space for matching fields until
+ * we at least find one to copy...
+ */
+ if(!match)
+ match = m = fs_get(strlen(h) + strlen(p) + 1);
+
+ while(*p) /* copy field name */
+ *m++ = *p++;
+
+ while(*h && (*m++ = *h++)) /* header includes colon */
+ if(*(m-1) == '\n' && (*h == '\r' || !isspace((unsigned char)*h)))
+ break;
+
+ *m = '\0'; /* tie off match string */
+ }
+ else{ /* no match, pass this field */
+ while(*h && !(*h++ == '\n'
+ && (*h == '\r' || !isspace((unsigned char)*h))))
+ ;
+ }
+ }
+
+ if(pflds && pflds != fields)
+ free_list_array(&pflds);
+
+ return(match ? match : cpystr(""));
+}
+
+
+int
+find_field(char **h, char *tmp, size_t ntmp)
+{
+ char *otmp = tmp;
+
+ if(!h || !*h || !**h || isspace((unsigned char)**h))
+ return(0);
+
+ while(tmp-otmp<ntmp-1 && **h && **h != ':' && !isspace((unsigned char)**h))
+ *tmp++ = *(*h)++;
+
+ *tmp = '\0';
+ return(1);
+}
+
+
+static char *_last_embedded_fg_color, *_last_embedded_bg_color;
+
+
+int
+embed_color(COLOR_PAIR *cp, gf_io_t pc)
+{
+ if(cp && cp->fg){
+ if(_last_embedded_fg_color)
+ fs_give((void **)&_last_embedded_fg_color);
+
+ _last_embedded_fg_color = cpystr(cp->fg);
+
+ if(!(pc && (*pc)(TAG_EMBED) && (*pc)(TAG_FGCOLOR) &&
+ gf_puts(color_to_asciirgb(cp->fg), pc)))
+ return 0;
+ }
+
+ if(cp && cp->bg){
+ if(_last_embedded_bg_color)
+ fs_give((void **)&_last_embedded_bg_color);
+
+ _last_embedded_bg_color = cpystr(cp->bg);
+
+ if(!(pc && (*pc)(TAG_EMBED) && (*pc)(TAG_BGCOLOR) &&
+ gf_puts(color_to_asciirgb(cp->bg), pc)))
+ return 0;
+ }
+
+ return 1;
+}
+
+
+COLOR_PAIR *
+get_cur_embedded_color(void)
+{
+ COLOR_PAIR *ret;
+
+ if(_last_embedded_fg_color && _last_embedded_bg_color)
+ ret = new_color_pair(_last_embedded_fg_color, _last_embedded_bg_color);
+ else
+ ret = pico_get_cur_color();
+
+ return(ret);
+}
+
+
+void
+clear_cur_embedded_color(void)
+{
+ if(_last_embedded_fg_color)
+ fs_give((void **)&_last_embedded_fg_color);
+
+ if(_last_embedded_bg_color)
+ fs_give((void **)&_last_embedded_bg_color);
+}
+
+
+int
+scroll_handle_start_color(char *colorstring, size_t buflen, int *len)
+{
+ *len = 0;
+
+ if(pico_usingcolor()){
+ char *fg = NULL, *bg = NULL, *s;
+
+ if(ps_global->VAR_SLCTBL_FORE_COLOR
+ && colorcmp(ps_global->VAR_SLCTBL_FORE_COLOR,
+ ps_global->VAR_NORM_FORE_COLOR))
+ fg = ps_global->VAR_SLCTBL_FORE_COLOR;
+
+ if(ps_global->VAR_SLCTBL_BACK_COLOR
+ && colorcmp(ps_global->VAR_SLCTBL_BACK_COLOR,
+ ps_global->VAR_NORM_BACK_COLOR))
+ bg = ps_global->VAR_SLCTBL_BACK_COLOR;
+
+ if(bg || fg){
+ COLOR_PAIR *tmp;
+
+ /*
+ * The blacks are just known good colors for
+ * testing whether the other color is good.
+ */
+ if((tmp = new_color_pair(fg ? fg : colorx(COL_BLACK),
+ bg ? bg : colorx(COL_BLACK))) != NULL){
+ if(pico_is_good_colorpair(tmp))
+ for(s = color_embed(fg, bg);
+ (*len) < buflen && (colorstring[*len] = *s);
+ s++, (*len)++)
+ ;
+
+ free_color_pair(&tmp);
+ }
+ }
+
+ if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
+ strncpy(colorstring + (*len), url_embed(TAG_BOLDON), MIN(3,buflen-(*len)));
+ *len += 2;
+ }
+ }
+
+ colorstring[buflen-1] = '\0';
+
+ return(*len != 0);
+}
+
+
+int
+scroll_handle_end_color(char *colorstring, size_t buflen, int *len, int use_hdr_color)
+{
+ *len = 0;
+ if(pico_usingcolor()){
+ char *fg = NULL, *bg = NULL, *s;
+ char *basefg = NULL, *basebg = NULL;
+
+ basefg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_FORE_COLOR
+ : ps_global->VAR_NORM_FORE_COLOR;
+ basebg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_BACK_COLOR
+ : ps_global->VAR_NORM_BACK_COLOR;
+
+ /*
+ * We need to change the fg and bg colors back even if they
+ * are the same as the Normal Colors so that color_a_quote
+ * will have a chance to pick up those colors as the ones to
+ * switch to. We don't do this before the handle above so that
+ * the quote color will flow into the selectable item when
+ * the selectable item color is partly the same as the
+ * normal color. That is, suppose the normal color was black on
+ * cyan and the selectable color was blue on cyan, only a fg color
+ * change. We preserve the only-a-fg-color-change in a quote by
+ * letting the quote background color flow into the selectable text.
+ */
+ if(ps_global->VAR_SLCTBL_FORE_COLOR)
+ fg = basefg;
+
+ if(ps_global->VAR_SLCTBL_BACK_COLOR)
+ bg = basebg;
+
+ if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
+ strncpy(colorstring, url_embed(TAG_BOLDOFF), MIN(3,buflen));
+ *len = 2;
+ }
+
+ if(fg || bg)
+ for(s = color_embed(fg, bg); (*len) < buflen && (colorstring[*len] = *s); s++, (*len)++)
+ ;
+ }
+
+ colorstring[buflen-1] = '\0';
+
+ return(*len != 0);
+}
+
+
+/*
+ * Helper routine that is of limited use.
+ * We need to tally up the screen width of
+ * a UTF-8 string as we go through the string.
+ * We just want the width of the character starting
+ * at str (and no longer than remaining_octets).
+ * If we're plopped into the middle of a UTF-8
+ * character we just want to return width zero.
+ */
+int
+width_at_this_position(unsigned char *str, unsigned long n)
+{
+ unsigned char *inputp = str;
+ unsigned long remaining_octets = n;
+ UCS ucs;
+ int width = 0;
+
+ ucs = (UCS) utf8_get(&inputp, &remaining_octets);
+ if(!(ucs & U8G_ERROR || ucs == UBOGON)){
+ width = wcellwidth(ucs);
+ /* Writechar will print a '?' */
+ if(width < 0)
+ width = 1;
+ }
+
+ return(width);
+}
diff --git a/pith/mailview.h b/pith/mailview.h
new file mode 100644
index 00000000..a89f3f95
--- /dev/null
+++ b/pith/mailview.h
@@ -0,0 +1,155 @@
+/*
+ * $Id: mailview.h 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_MAILVIEW_INCLUDED
+#define PITH_MAILVIEW_INCLUDED
+
+
+#include "../pith/store.h"
+#include "../pith/handle.h"
+#include "../pith/bitmap.h"
+#include "../pith/helptext.h"
+#include "../pith/msgno.h"
+#include "../pith/filttype.h"
+#include "../pith/pattern.h"
+#include "../pith/state.h"
+#include "../pith/charset.h"
+#include "../pith/color.h"
+
+
+/* format_message flags */
+#define FM_DISPLAY 0x0001 /* result is headed for display */
+#define FM_NEW_MESS 0x0002 /* a new message so zero out attachment descrip */
+#define FM_NOWRAP 0x0008 /* no wrapping done */
+#define FM_NOCOLOR 0x0010 /* no color added */
+#define FM_NOINDENT 0x0020 /* no indents, but only works has effect if wrapping */
+#define FM_NOEDITORIAL 0x0040 /* no editorial comments */
+#define FM_NOHTMLREL 0x0200 /* no relative links */
+#define FM_HTMLRELATED 0x0400 /* allow multi/related */
+#define FM_FORCEPREFPLN 0x0800 /* force prefer-plain this time */
+#define FM_FORCENOPREFPLN 0x1000 /* force not prefer-plain this time */
+#define FM_HIDESERVER 0x2000 /* HIDE servername after active HTML links */
+#define FM_HTML 0x4000 /* filter/preserve HTML markup */
+#define FM_HTMLIMAGES 0x8000 /* filter/preserve HTML IMG tags */
+
+
+#define SIGDASHES "-- "
+#define START_SIG_BLOCK 2
+#define IN_SIG_BLOCK 1
+#define OUT_SIG_BLOCK 0
+
+
+/*
+ * Which header fields should format_envelope output?
+ */
+#define FE_FROM 0x0001
+#define FE_SENDER 0x0002
+#define FE_DATE 0x0004
+#define FE_TO 0x0008
+#define FE_CC 0x0010
+#define FE_BCC 0x0020
+#define FE_NEWSGROUPS 0x0040
+#define FE_SUBJECT 0x0080
+#define FE_MESSAGEID 0x0100
+#define FE_REPLYTO 0x0200
+#define FE_FOLLOWUPTO 0x0400
+#define FE_INREPLYTO 0x0800
+#define FE_RETURNPATH 0x1000
+#define FE_REFERENCES 0x2000
+#define FE_DEFAULT (FE_FROM | FE_DATE | FE_TO | FE_CC | FE_BCC \
+ | FE_NEWSGROUPS | FE_SUBJECT | FE_REPLYTO \
+ | FE_FOLLOWUPTO)
+
+
+/*
+ * Function to format
+ */
+typedef void (*fmt_env_t)(MAILSTREAM *, long int, char *, ENVELOPE *, gf_io_t, long int, char *, int);
+
+/*
+ * Structure and macros to help control format_header_text
+ */
+typedef struct header_s {
+ unsigned type:4;
+ unsigned except:1;
+ union {
+ char **l; /* list of char *'s */
+ long b; /* bit field of header fields (FE_* above) */
+ } h;
+ char charset[CSET_MAX];
+} HEADER_S;
+
+
+/*
+ * Macro's to help sort out how we display MIME types
+ */
+#define MCD_NONE 0x00
+#define MCD_INTERNAL 0x01
+#define MCD_EXTERNAL 0x02
+#define MCD_EXT_PROMPT 0x04
+
+
+#define HD_LIST 1
+#define HD_BFIELD 2
+#define HD_INIT(H, L, E, B) { \
+ if((L) && (L)[0]){ \
+ (H)->type = HD_LIST; \
+ (H)->except = (E); \
+ (H)->h.l = (L); \
+ } \
+ else{ \
+ (H)->type = HD_BFIELD; \
+ (H)->h.b = (B); \
+ (H)->except = 0; \
+ } \
+ (H)->charset[0] = '\0'; \
+ }
+
+
+/* exported protoypes */
+int format_message(long, ENVELOPE *, BODY *, HANDLE_S **, int, gf_io_t);
+int format_attachment_list(long int, BODY *, HANDLE_S **, int, int, gf_io_t);
+char *format_body(long int, BODY *, HANDLE_S **, HEADER_S *, int, int, gf_io_t);
+int url_hilite(long, char *, LT_INS_S **, void *);
+int handle_start_color(char *, size_t, int *, int);
+int handle_end_color(char *, size_t, int *);
+
+/*
+ * BUG: BELOW IS UNIX/PC ONLY since config'd browser means nothing to webpine
+ */
+
+int url_external_specific_handler(char *, int);
+int url_imap_folder(char *, char **, imapuid_t *, imapuid_t *, char **, int);
+int url_bogus(char *, char *);
+void pine_rfc822_address(ADDRESS *, gf_io_t);
+void pine_rfc822_cat(char *, const char *, gf_io_t);
+int format_header(MAILSTREAM *, long, char *, ENVELOPE *, HEADER_S *,
+ char *, HANDLE_S **, int, fmt_env_t, gf_io_t);
+COLOR_PAIR *hdr_color(char *, char *, SPEC_COLOR_S *);
+char *display_parameters(PARAMETER *);
+char *pine_fetch_header(MAILSTREAM *, long, char *, char **, long);
+int color_signature(long, char *, LT_INS_S **, void *);
+int scroll_handle_start_color(char *, size_t, int *);
+int scroll_handle_end_color(char *, size_t, int *, int);
+int width_at_this_position(unsigned char *, unsigned long);
+
+/* currently mandatory to implement stubs */
+
+/* this is used in rfc2369_editorial() in format_message() */
+void rfc2369_display(MAILSTREAM *, MSGNO_S *, long);
+
+
+#endif /* PITH_MAILVIEW_INCLUDED */
diff --git a/pith/makefile.wnt b/pith/makefile.wnt
new file mode 100644
index 00000000..d9316dba
--- /dev/null
+++ b/pith/makefile.wnt
@@ -0,0 +1,88 @@
+# $Id: makefile.wnt 14098 2005-10-03 18:54:13Z jpf@u.washington.edu $
+#
+# ========================================================================
+# Copyright 2006-2007 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+#
+#
+# Makefile for WIN NT version of the libpith.lib
+#
+#
+CC=cl
+RM=del
+CP=copy
+RC=rc
+
+#includes symbol info for debugging
+CDEBUG= #-Zi -Od
+LDEBUG= /DEBUG /DEBUGTYPE:CV
+
+STDCFLAGS= -I..\include -I..\regex -nologo -MT -DWIN32 -DDOS -D_WINDOWS -DMSC_MALLOC
+
+CFLAGS= $(CDEBUG) $(STDCFLAGS) $(NET) $(EXTRACFLAGS)
+
+LFLAGS= $(LDEBUG) $(EXTRALDFLAGS)
+
+RCFLAGS =
+
+LIBER=lib
+LIBARGS=/nologo /verbose
+
+HFILES= ../include/system.h ../include/general.h \
+ abdlc.h ablookup.h addrbook.h addrstring.h adjtime.h adrbklib.h atttype.h \
+ bitmap.h bldaddr.h busy.h charset.h color.h conf.h conftype.h context.h \
+ copyaddr.h debug.h detach.h detoken.h editorial.h escapes.h filter.h filttype.h \
+ flag.h folder.h foldertype.h handle.h headers.h help.h helptext.h hist.h icache.h imap.h indxtype.h \
+ init.h keyword.h ldap.h list.h mailcap.h mailcmd.h mailindx.h maillist.h \
+ mailpart.h mailview.h margin.h mimedesc.h mimetype.h msgno.h newmail.h news.h \
+ options.h pattern.h pineelt.h pipe.h readfile.h remote.h remtype.h repltype.h reply.h \
+ rfc2231.h save.h savetype.h search.h send.h sequence.h signal.h sort.h sorttype.h \
+ state.h status.h store.h stream.h string.h strlst.h takeaddr.h tempfile.h text.h \
+ thread.h url.h user.h util.h
+
+OFILES= ablookup.obj abdlc.obj addrbook.obj addrstring.obj adrbklib.obj bldaddr.obj charset.obj \
+ color.obj conf.obj context.obj copyaddr.obj detoken.obj detach.obj editorial.obj escapes.obj \
+ filter.obj flag.obj folder.obj handle.obj help.obj helptext.obj hist.obj icache.obj imap.obj init.obj \
+ keyword.obj ldap.obj list.obj mailcap.obj mailcmd.obj mailindx.obj maillist.obj mailview.obj \
+ margin.obj mimedesc.obj mimetype.obj msgno.obj newmail.obj news.obj pattern.obj pipe.obj \
+ readfile.obj remote.obj reply.obj rfc2231.obj save.obj search.obj sequence.obj send.obj sort.obj state.obj \
+ status.obj store.obj stream.obj string.obj strlst.obj takeaddr.obj tempfile.obj text.obj \
+ thread.obj adjtime.obj url.obj util.obj
+
+all: libpith.lib
+
+helptext.h: help_h_gen.exe pine.hlp
+ help_h_gen.exe < pine.hlp > helptext.h
+
+helptext.c: help_c_gen.exe pine.hlp
+ help_c_gen.exe < pine.hlp > helptext.c
+
+help_h_gen.exe:
+ $(CC) /c $(CFLAGS) help_h_gen.c
+ link /subsystem:console /out:help_h_gen.exe $(LFLAGS) help_h_gen.obj
+
+help_c_gen.exe:
+ $(CC) /c $(CFLAGS) help_c_gen.c
+ link /subsystem:console /out:help_c_gen.exe $(LFLAGS) help_c_gen.obj
+
+.c.obj:
+ $(CC) -c $(CFLAGS) "$(MAKEDIR)"\$*.c
+
+$(OFILES): $(HFILES)
+
+libpith.lib: $(OFILES)
+ $(RM) libpith.lib || rem
+ $(LIBER) /out:libpith.lib $(OFILES)
+
+clean:
+ $(RM) *.lib
+ $(RM) *.obj
+ $(RM) helptext.h helptext.c help_h_gen.exe help_c_gen.exe
diff --git a/pith/margin.c b/pith/margin.c
new file mode 100644
index 00000000..90fdd209
--- /dev/null
+++ b/pith/margin.c
@@ -0,0 +1,138 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: margin.c 1032 2008-04-11 00:30:04Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+
+ margin.c
+ Implements message data gathering and formatting
+
+ ====*/
+
+
+#include "../pith/headers.h"
+#include "../pith/string.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+
+
+/*
+ * Internal prototypes
+ */
+
+
+/*
+ * format_view_margin - return sane value for vertical margins
+ */
+int *
+format_view_margin(void)
+{
+ static int margin[2];
+ char tmp[100], e[200], *err, lastchar = 0;
+ int left = 0, right = 0, leftm = 0, rightm = 0;
+ size_t l;
+
+ memset(margin, 0, sizeof(margin));
+
+ /*
+ * We initially tried to make sure that the user didn't shoot themselves
+ * in the foot by setting this too small. People seem to want to do some
+ * strange stuff, so we're going to relax the wild shot detection and
+ * let people set what they want, until we get to the point of totally
+ * absurd. We've also added the possibility of appending the letter c
+ * onto the width. That means treat the value as an absolute column
+ * instead of a width. That is, a right margin of 76c means wrap at
+ * column 76, whereas right margin of 4 means to wrap at column
+ * screen width - 4.
+ */
+ if(ps_global->VAR_VIEW_MARGIN_LEFT){
+ strncpy(tmp, ps_global->VAR_VIEW_MARGIN_LEFT, sizeof(tmp)-1);
+ tmp[sizeof(tmp)-1] = '\0';
+ removing_leading_and_trailing_white_space(tmp);
+ if(tmp[0]){
+ l = strlen(tmp);
+ if(l > 0)
+ lastchar = tmp[l-1];
+
+ if(lastchar == 'c')
+ tmp[l-1] = '\0';
+
+ if((err = strtoval(tmp, &left, 0, 0, 0, e, sizeof(e), "Viewer-margin-left")) != NULL){
+ leftm = 0;
+ dprint((2, "%s\n", err));
+ }
+ else{
+ if(lastchar == 'c')
+ leftm = left-1;
+ else
+ leftm = left;
+
+ leftm = MIN(MAX(0, leftm), ps_global->ttyo->screen_cols);
+ }
+ }
+ }
+
+ if(ps_global->VAR_VIEW_MARGIN_RIGHT){
+ strncpy(tmp, ps_global->VAR_VIEW_MARGIN_RIGHT, sizeof(tmp)-1);
+ tmp[sizeof(tmp)-1] = '\0';
+ removing_leading_and_trailing_white_space(tmp);
+ if(tmp[0]){
+ l = strlen(tmp);
+ if(l > 0)
+ lastchar = tmp[l-1];
+
+ if(lastchar == 'c')
+ tmp[l-1] = '\0';
+
+ if((err = strtoval(tmp, &right, 0, 0, 0, e, sizeof(e), "Viewer-margin-right")) != NULL){
+ rightm = 0;
+ dprint((2, "%s\n", err));
+ }
+ else{
+ if(lastchar == 'c')
+ rightm = ps_global->ttyo->screen_cols - right;
+ else
+ rightm = right;
+
+ rightm = MIN(MAX(0, rightm), ps_global->ttyo->screen_cols);
+ }
+ }
+ }
+
+ if((rightm > 0 || leftm > 0) && rightm >= 0 && leftm >= 0
+ && ps_global->ttyo->screen_cols - rightm - leftm >= 8){
+ margin[0] = leftm;
+ margin[1] = rightm;
+ }
+
+ return(margin);
+}
+
+
+/*
+ * Give a margin for help and such
+ */
+int *
+non_messageview_margin(void)
+{
+ static int margin[2];
+
+ margin[0] = 0;
+ margin[1] = 4;
+
+ return(margin);
+}
diff --git a/pith/margin.h b/pith/margin.h
new file mode 100644
index 00000000..3c497c6f
--- /dev/null
+++ b/pith/margin.h
@@ -0,0 +1,26 @@
+/*
+ * $Id: margin.h 1032 2008-04-11 00:30:04Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_MARGIN_INCLUDED
+#define PITH_MARGIN_INCLUDED
+
+
+/* exported protoypes */
+int *format_view_margin(void);
+int *non_messageview_margin(void);
+
+
+#endif /* PITH_MARGIN_INCLUDED */
diff --git a/pith/mimedesc.c b/pith/mimedesc.c
new file mode 100644
index 00000000..66b39839
--- /dev/null
+++ b/pith/mimedesc.c
@@ -0,0 +1,879 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: mimedesc.c 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/mimedesc.h"
+#include "../pith/mimetype.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/mailview.h"
+#include "../pith/rfc2231.h"
+#include "../pith/editorial.h"
+#include "../pith/mailpart.h"
+#include "../pith/mailcap.h"
+#include "../pith/smime.h"
+
+
+/* internal prototypes */
+int mime_known_text_subtype(char *);
+ATTACH_S *next_attachment(void);
+void format_mime_size(char *, size_t, BODY *, int);
+int mime_show(BODY *);
+
+
+/*
+ * Def's to help in sorting out multipart/alternative
+ */
+#define SHOW_NONE 0
+#define SHOW_PARTS 1
+#define SHOW_ALL_EXT 2
+#define SHOW_ALL 3
+
+/*
+ * Def's to control format_mime_size output
+ */
+#define FMS_NONE 0x00
+#define FMS_SPACE 0x01
+
+
+/*----------------------------------------------------------------------
+ Add lines to the attachments structure
+
+ Args: body -- body of the part being described
+ prefix -- The prefix for numbering the parts
+ num -- The number of this specific part
+ should_show -- Flag indicating which of alternate parts should be shown
+ multalt -- Flag indicating the part is one of the multipart
+ alternative parts (so suppress editorial comment)
+
+Result: The ps_global->attachments data structure is filled in. This
+is called recursively to descend through all the parts of a message.
+The description strings filled in are malloced and should be freed.
+
+ ----*/
+void
+describe_mime(struct mail_bodystruct *body, char *prefix, int num,
+ int should_show, int multalt, int flags)
+{
+ PART *part;
+ char numx[512], string[800], *description;
+ int n, named = 0, can_display_ext;
+ ATTACH_S *a;
+
+ if(!body)
+ return;
+
+ if(body->type == TYPEMULTIPART){
+ int alt_to_show = 0;
+
+ if(strucmp(body->subtype, "alternative") == 0){
+ int effort, best_effort = SHOW_NONE;
+
+ /*---- Figure out which alternative part to display ---*/
+ /*
+ * This is kind of complicated because some TEXT types
+ * are more displayable than others. We don't want to
+ * commit to displaying a text-part alternative that we
+ * don't directly recognize unless that's all there is.
+ */
+ for(part=body->nested.part, n=1; part; part=part->next, n++)
+ if(flags & FM_FORCEPREFPLN
+ || (!(flags & FM_FORCENOPREFPLN)
+ && F_ON(F_PREFER_PLAIN_TEXT, ps_global)
+ && part->body.type == TYPETEXT
+ && (!part->body.subtype
+ || !strucmp(part->body.subtype, "PLAIN")))){
+ if((effort = mime_show(&part->body)) != SHOW_ALL_EXT){
+ best_effort = effort;
+ alt_to_show = n;
+ break;
+ }
+ }
+ else if((effort = mime_show(&part->body)) >= best_effort
+ && (part->body.type != TYPETEXT || mime_known_text_subtype(part->body.subtype))
+ && effort != SHOW_ALL_EXT){
+ best_effort = effort;
+ alt_to_show = n;
+ }
+ else if(part->body.type == TYPETEXT && alt_to_show == 0){
+ best_effort = effort;
+ alt_to_show = n;
+ }
+ }
+ else if(!strucmp(body->subtype, "digest")){
+ memset(a = next_attachment(), 0, sizeof(ATTACH_S));
+ if(*prefix){
+ prefix[n = strlen(prefix) - 1] = '\0';
+ a->number = cpystr(prefix);
+ prefix[n] = '.';
+ }
+ else
+ a->number = cpystr("");
+
+ a->description = cpystr("Multipart/Digest");
+ a->body = body;
+ a->can_display = MCD_INTERNAL;
+ (a+1)->description = NULL;
+ }
+#ifdef SMIME
+ else if(!strucmp(body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)){
+ memset(a = next_attachment(), 0, sizeof(ATTACH_S));
+ if(*prefix){
+ prefix[n = strlen(prefix) - 1] = '\0';
+ a->number = cpystr(prefix);
+ prefix[n] = '.';
+ }
+ else
+ a->number = cpystr("");
+
+ a->description = body->description ? cpystr(body->description)
+ : cpystr("");
+ a->body = body;
+ a->can_display = MCD_INTERNAL;
+ (a+1)->description = NULL;
+ }
+#endif /* SMIME */
+ else if(mailcap_can_display(body->type, body->subtype, body, 0)
+ || (can_display_ext
+ = mailcap_can_display(body->type, body->subtype, body, 1))){
+ memset(a = next_attachment(), 0, sizeof(ATTACH_S));
+ if(*prefix){
+ prefix[n = strlen(prefix) - 1] = '\0';
+ a->number = cpystr(prefix);
+ prefix[n] = '.';
+ }
+ else
+ a->number = cpystr("");
+
+ snprintf(string, sizeof(string), "%s/%s", body_type_names(body->type),
+ body->subtype);
+ string[sizeof(string)-1] = '\0';
+ a->description = cpystr(string);
+ a->body = body;
+ a->can_display = MCD_EXTERNAL;
+ if(can_display_ext)
+ a->can_display |= MCD_EXT_PROMPT;
+ (a+1)->description = NULL;
+ }
+
+ for(part=body->nested.part, n=1; part; part=part->next, n++){
+ snprintf(numx, sizeof(numx), "%s%d.", prefix, n);
+ numx[sizeof(numx)-1] = '\0';
+ /*
+ * Last arg to describe_mime here. If we have chosen one part
+ * of a multipart/alternative to display then we suppress
+ * the editorial messages on the other parts.
+ */
+ describe_mime(&(part->body),
+ (part->body.type == TYPEMULTIPART) ? numx : prefix,
+ n, should_show && (n == alt_to_show || !alt_to_show),
+ alt_to_show != 0, flags);
+ }
+ }
+ else{
+ char tmp1[MAILTMPLEN], tmp2[MAILTMPLEN];
+ size_t ll;
+
+ a = next_attachment();
+ format_mime_size(a->size, sizeof(a->size), body, FMS_SPACE);
+
+ a->suppress_editorial = (multalt != 0);
+
+ snprintf(tmp1, sizeof(tmp1), "%s", body->description ? body->description : "");
+ tmp1[sizeof(tmp1)-1] = '\0';
+ snprintf(tmp2, sizeof(tmp2), "%s", (!body->description && body->type == TYPEMESSAGE && body->encoding <= ENCBINARY && body->subtype && strucmp(body->subtype, "rfc822") == 0 && body->nested.msg->env && body->nested.msg->env->subject) ? body->nested.msg->env->subject : "");
+ tmp2[sizeof(tmp2)-1] = '\0';
+
+ description = (body->description)
+ ? (char *) rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, tmp1)
+ : (body->type == TYPEMESSAGE
+ && body->encoding <= ENCBINARY
+ && body->subtype
+ && strucmp(body->subtype, "rfc822") == 0
+ && body->nested.msg->env
+ && body->nested.msg->env->subject)
+ ? (char *) rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, tmp2)
+ : (body->type == TYPEMESSAGE
+ && body->subtype
+ && !strucmp(body->subtype, "delivery-status"))
+ ? "Delivery Status"
+ : NULL;
+
+ description = iutf8ncpy((char *)(tmp_20k_buf+1000), description, 1000);
+ snprintf(string, sizeof(string), "%s%s%s%s",
+ type_desc(body->type,body->subtype,body->parameter,
+ body->disposition.type ? body->disposition.parameter : NULL, 0),
+ (description && description[0]) ? ", \"" : "",
+ (description && description[0]) ? description : "",
+ (description && description[0]) ? "\"": "");
+ string[sizeof(string)-1] =- '\0';
+ a->description = cpystr(string);
+ a->body = body;
+
+ if(body->disposition.type){
+ named = strucmp(body->disposition.type, "inline");
+ }
+ else{
+ char *value;
+
+
+ /*
+ * This test remains for backward compatibility
+ */
+ if(body && (value = parameter_val(body->parameter, "name")) != NULL){
+ named = strucmp(value, "Message Body");
+ fs_give((void **) &value);
+ }
+ }
+
+ /*
+ * Make sure we have the tools available to display the
+ * type/subtype, *AND* that we can decode it if needed.
+ * Of course, if it's text, we display it anyway in the
+ * mail_view_screen so put off testing mailcap until we're
+ * explicitly asked to display that segment 'cause it could
+ * be expensive to test...
+ */
+ if((body->type == TYPETEXT && !named)
+ || MIME_VCARD(body->type,body->subtype)){
+ a->test_deferred = 1;
+ a->can_display = MCD_INTERNAL;
+ }
+ else{
+ a->test_deferred = 0;
+ a->can_display = mime_can_display(body->type, body->subtype, body);
+ }
+
+ /*
+ * Deferred means we can display it
+ */
+ a->shown = ((a->can_display & MCD_INTERNAL)
+ && !MIME_VCARD(body->type,body->subtype)
+ && (!named || multalt
+ || (body->type == TYPETEXT && num == 1
+ && !(*prefix && strcmp(prefix,"1."))))
+ && (body->type != TYPEMESSAGE
+ || (body->type == TYPEMESSAGE
+ && body->encoding <= ENCBINARY))
+ && should_show);
+ ll = (strlen(prefix) + 16) * sizeof(char);
+ a->number = (char *) fs_get(ll);
+ snprintf(a->number, ll, "%s%d",prefix, num);
+ a->number[ll-1] = '\0';
+ (a+1)->description = NULL;
+ if(body->type == TYPEMESSAGE && body->encoding <= ENCBINARY
+ && body->subtype && strucmp(body->subtype, "rfc822") == 0){
+ body = body->nested.msg->body;
+ snprintf(numx, sizeof(numx), "%.*s%d.", sizeof(numx)-20, prefix, num);
+ numx[sizeof(numx)-1] = '\0';
+ describe_mime(body, numx, 1, should_show, 0, flags);
+ }
+ }
+}
+
+
+int
+mime_known_text_subtype(char *subtype)
+{
+ char **p;
+ static char *known_types[] = {
+ "plain",
+ "html",
+ "enriched",
+ "richtext",
+ NULL
+ };
+
+ if(!(subtype && *subtype))
+ return(1);
+
+ for(p = known_types; *p; p++)
+ if(!strucmp(subtype, *p))
+ return(1);
+ return(0);
+}
+
+
+/*
+ * Returns attribute value or NULL.
+ * Value returned needs to be freed by caller
+ */
+char *
+parameter_val(PARAMETER *param, char *attribute)
+{
+ if(!(param && attribute && attribute[0]))
+ return(NULL);
+
+ return(rfc2231_get_param(param, attribute, NULL, NULL));
+}
+
+
+/*
+ * Get sender_filename, the filename set by the sender in the attachment.
+ * If a sender_filename buffer is passed in, the answer is copied to it
+ * and a pointer to it is returned. If sender_filename is passed in as NULL
+ * then an allocated copy of the sender filename is returned instead.
+ * If ext_ptr is non-NULL then it is set to point to the extension name.
+ * It is not a separate copy, it points into the string sender_filename.
+ */
+char *
+get_filename_parameter(char *sender_filename, size_t sfsize, BODY *body, char **ext_ptr)
+{
+ char *p = NULL;
+ char *decoded_name = NULL;
+ char *filename = NULL;
+ char tmp[1000];
+
+ if(!body)
+ return(NULL);
+
+ if(sender_filename){
+ if(sfsize <= 0)
+ return(NULL);
+
+ sender_filename[0] = '\0';
+ }
+
+ /*
+ * First check for Content-Disposition's "filename" parameter and
+ * if that isn't found for the deprecated Content-Type "name" parameter.
+ */
+ if((p = parameter_val(body->disposition.parameter, "filename"))
+ || (p = parameter_val(body->parameter, "name"))){
+
+ /*
+ * If somebody sent us and incorrectly rfc2047 encoded
+ * parameter value instead of what rfc2231 suggest we
+ * grudglingly try to fix it.
+ */
+ if(p[0] == '=' && p[1] == '?')
+ decoded_name = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp,
+ sizeof(tmp), p);
+
+ if(!decoded_name)
+ decoded_name = p;
+
+ filename = last_cmpnt(decoded_name);
+
+ if(!filename)
+ filename = decoded_name;
+ }
+
+ if(filename){
+ if(sender_filename){
+ strncpy(sender_filename, filename, sfsize-1);
+ sender_filename[sfsize-1] = '\0';
+ }
+ else
+ sender_filename = cpystr(filename);
+ }
+
+ if(p)
+ fs_give((void **) &p);
+
+ /* ext_ptr will end up pointing into sender_filename string */
+ if(ext_ptr && sender_filename)
+ mt_get_file_ext(sender_filename, ext_ptr);
+
+ return(sender_filename);
+}
+
+
+/*----------------------------------------------------------------------
+ Return a pointer to the next attachment struct
+
+ Args: none
+
+ ----*/
+ATTACH_S *
+next_attachment(void)
+{
+ ATTACH_S *a;
+ int n;
+
+ for(a = ps_global->atmts; a->description; a++)
+ ;
+
+ if((n = a - ps_global->atmts) + 1 >= ps_global->atmts_allocated){
+ ps_global->atmts_allocated *= 2;
+ fs_resize((void **)&ps_global->atmts,
+ ps_global->atmts_allocated * sizeof(ATTACH_S));
+ a = &ps_global->atmts[n];
+ }
+
+ return(a);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Zero out the attachments structure and free up storage
+ ----*/
+void
+zero_atmts(ATTACH_S *atmts)
+{
+ ATTACH_S *a;
+
+ for(a = atmts; a->description != NULL; a++){
+ fs_give((void **)&(a->description));
+ fs_give((void **)&(a->number));
+ }
+
+ atmts->description = NULL;
+}
+
+
+char *
+body_type_names(int t)
+{
+#define TLEN 31
+ static char body_type[TLEN + 1];
+ char *p;
+
+ body_type[0] = '\0';
+ strncpy(body_type, /* copy the given type */
+ (t > -1 && t < TYPEMAX && body_types[t])
+ ? body_types[t] : "Other", TLEN);
+ body_type[sizeof(body_type)-1] = '\0';
+
+ for(p = body_type + 1; *p; p++) /* make it presentable */
+ if(isascii((unsigned char) (*p)) && isupper((unsigned char) (*p)))
+ *p = tolower((unsigned char)(*p));
+
+ return(body_type); /* present it */
+}
+
+
+/*----------------------------------------------------------------------
+ Mapping table use to neatly display charset parameters
+ ----*/
+
+static struct set_names {
+ char *rfcname,
+ *humanname;
+} charset_names[] = {
+ {"US-ASCII", "Plain Text"},
+ {"ISO-8859-1", "Latin 1 (Western Europe)"},
+ {"ISO-8859-2", "Latin 2 (Eastern Europe)"},
+ {"ISO-8859-3", "Latin 3 (Southern Europe)"},
+ {"ISO-8859-4", "Latin 4 (Northern Europe)"},
+ {"ISO-8859-5", "Latin & Cyrillic"},
+ {"ISO-8859-6", "Latin & Arabic"},
+ {"ISO-8859-7", "Latin & Greek"},
+ {"ISO-8859-8", "Latin & Hebrew"},
+ {"ISO-8859-9", "Latin 5 (Turkish)"},
+ {"ISO-8859-10", "Latin 6 (Nordic)"},
+ {"ISO-8859-11", "Latin & Thai"},
+ {"ISO-8859-13", "Latin 7 (Baltic)"},
+ {"ISO-8859-14", "Latin 8 (Celtic)"},
+ {"ISO-8859-15", "Latin 9 (Euro)"},
+ {"KOI8-R", "Latin & Russian"},
+ {"KOI8-U", "Latin & Ukranian"},
+ {"VISCII", "Latin & Vietnamese"},
+ {"GB2312", "Latin & Simplified Chinese"},
+ {"BIG5", "Latin & Traditional Chinese"},
+ {"EUC-JP", "Latin & Japanese"},
+ {"Shift-JIS", "Latin & Japanese"},
+ {"Shift_JIS", "Latin & Japanese"},
+ {"EUC-KR", "Latin & Korean"},
+ {"ISO-2022-CN", "Latin & Chinese"},
+ {"ISO-2022-JP", "Latin & Japanese"},
+ {"ISO-2022-KR", "Latin & Korean"},
+ {"UTF-7", "7-bit encoded Unicode"},
+ {"UTF-8", "Internet-standard Unicode"},
+ {"ISO-2022-JP-2", "Multilingual"},
+ {NULL, NULL}
+};
+
+
+/*----------------------------------------------------------------------
+ Return a nicely formatted discription of the type of the part
+ ----*/
+
+char *
+type_desc(int type, char *subtype, PARAMETER *params, PARAMETER *disp_params, int full)
+{
+ static char type_d[200];
+ int i;
+ char *p, *parmval;
+
+ p = type_d;
+ sstrncpy(&p, body_type_names(type), sizeof(type_d)-(p-type_d));
+ if(full && subtype){
+ *p++ = '/';
+ sstrncpy(&p, subtype, sizeof(type_d)-(p-type_d));
+ }
+
+ type_d[sizeof(type_d)-1] = '\0';
+
+ switch(type){
+ case TYPETEXT:
+ parmval = parameter_val(params, "charset");
+
+ if(parmval){
+ for(i = 0; charset_names[i].rfcname; i++)
+ if(!strucmp(parmval, charset_names[i].rfcname)){
+ if(!strucmp(parmval, ps_global->display_charmap
+ ? ps_global->display_charmap : "us-ascii")
+ || !strucmp(parmval, "us-ascii"))
+ i = -1;
+
+ break;
+ }
+
+ if(i >= 0){ /* charset to write */
+ if(charset_names[i].rfcname){
+ sstrncpy(&p, " (charset: ", sizeof(type_d)-(p-type_d));
+ sstrncpy(&p, charset_names[i].rfcname
+ ? charset_names[i].rfcname : "Unknown", sizeof(type_d)-(p-type_d));
+ if(full){
+ sstrncpy(&p, " \"", sizeof(type_d)-(p-type_d));
+ sstrncpy(&p, charset_names[i].humanname
+ ? charset_names[i].humanname
+ : parmval, sizeof(type_d)-(p-type_d));
+ if(sizeof(type_d)-(p-type_d) > 0)
+ *p++ = '\"';
+ }
+
+ sstrncpy(&p, ")", sizeof(type_d)-(p-type_d));
+ }
+ else{
+ sstrncpy(&p, " (charset: ", sizeof(type_d)-(p-type_d));
+ sstrncpy(&p, parmval, sizeof(type_d)-(p-type_d));
+ sstrncpy(&p, ")", sizeof(type_d)-(p-type_d));
+ }
+ }
+
+ fs_give((void **) &parmval);
+ }
+
+ break;
+
+ case TYPEMESSAGE:
+ if(full && subtype && strucmp(subtype, "external-body") == 0)
+ if((parmval = parameter_val(params, "access-type")) != NULL){
+ snprintf(p, sizeof(type_d)-(p-type_d), " (%s%s)", full ? "Access: " : "", parmval);
+ fs_give((void **) &parmval);
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ if(full && type != TYPEMULTIPART && type != TYPEMESSAGE){
+ if((parmval = parameter_val(params, "name")) != NULL){
+ snprintf(p, sizeof(type_d)-(p-type_d), " (Name: \"%s\")", parmval);
+ fs_give((void **) &parmval);
+ }
+ else if((parmval = parameter_val(disp_params, "filename")) != NULL){
+ snprintf(p, sizeof(type_d)-(p-type_d), " (Filename: \"%s\")", parmval);
+ fs_give((void **) &parmval);
+ }
+ }
+
+ type_d[sizeof(type_d)-1] = '\0';
+
+ return(type_d);
+}
+
+
+
+
+void
+format_mime_size(char *string, size_t stringlen, struct mail_bodystruct *b, int flags)
+{
+ char tmp[10], *p = NULL;
+ char *origstring;
+
+
+ if(stringlen <= 0)
+ return;
+
+ origstring = string;
+
+ if(flags & FMS_SPACE)
+ *string++ = ' ';
+
+ switch(b->encoding){
+ case ENCBASE64 :
+ if(b->type == TYPETEXT){
+ if(flags & FMS_SPACE)
+ *(string-1) = '~';
+ else
+ *string++ = '~';
+ }
+
+ strncpy(p = string, byte_string((3 * b->size.bytes) / 4), stringlen-(string-origstring));
+ break;
+
+ default :
+ case ENCQUOTEDPRINTABLE :
+ if(flags & FMS_SPACE)
+ *(string-1) = '~';
+ else
+ *string++ = '~';
+
+ case ENC8BIT :
+ case ENC7BIT :
+ if(b->type == TYPETEXT)
+ /* lines with no CRLF aren't counted, just add one so it makes more sense */
+ snprintf(string, stringlen-(string-origstring), "%s lines", comatose(b->size.lines+1));
+ else
+ strncpy(p = string, byte_string(b->size.bytes), stringlen-(string-origstring));
+
+ break;
+ }
+
+ origstring[stringlen-1] = '\0';
+
+ if(p){
+ for(; *p && (isascii((unsigned char) *p) && (isdigit((unsigned char) *p)
+ || ispunct((unsigned char) *p))); p++)
+ ;
+
+ snprintf(tmp, sizeof(tmp), (flags & FMS_SPACE) ? " %-5.5s" : " %s", p);
+ tmp[sizeof(tmp)-1] = '\0';
+ strncpy(p, tmp, stringlen-(p-origstring));
+ }
+
+ origstring[stringlen-1] = '\0';
+}
+
+
+
+/*----------------------------------------------------------------------
+ Determine if we can show all, some or none of the parts of a body
+
+Args: body --- The message body to check
+
+Returns: SHOW_ALL, SHOW_ALL_EXT, SHOW_PART or SHOW_NONE depending on
+ how much of the body can be shown and who can show it.
+ ----*/
+int
+mime_show(struct mail_bodystruct *body)
+{
+ int effort, best_effort;
+ PART *p;
+
+ if(!body)
+ return(SHOW_NONE);
+
+ switch(body->type) {
+ case TYPEMESSAGE:
+ if(!strucmp(body->subtype, "rfc822"))
+ return(mime_show(body->nested.msg->body) == SHOW_ALL
+ ? SHOW_ALL: SHOW_PARTS);
+ /* else fall thru to default case... */
+
+ default:
+ /*
+ * Since we're testing for internal displayability, give the
+ * internal result over an external viewer
+ */
+ effort = mime_can_display(body->type, body->subtype, body);
+ if(effort == MCD_NONE)
+ return(SHOW_NONE);
+ else if(effort & MCD_INTERNAL)
+ return(SHOW_ALL);
+ else
+ return(SHOW_ALL_EXT);
+
+ case TYPEMULTIPART:
+ best_effort = SHOW_NONE;
+ for(p = body->nested.part; p; p = p->next)
+ if((effort = mime_show(&p->body)) > best_effort)
+ best_effort = effort;
+
+ return(best_effort);
+ }
+}
+
+
+/*
+ * fcc_size_guess
+ */
+long
+fcc_size_guess(struct mail_bodystruct *body)
+{
+ long size = 0L;
+
+ if(body){
+ if(body->type == TYPEMULTIPART){
+ PART *part;
+
+ for(part = body->nested.part; part; part = part->next)
+ size += fcc_size_guess(&part->body);
+ }
+ else{
+ size = body->size.bytes;
+ /*
+ * If it is ENCBINARY we will be base64 encoding it. This
+ * ideally increases the size by a factor of 4/3, but there
+ * is a per-line increase in that because of the CRLFs and
+ * because the number of characters in the line might not
+ * be a factor of 3. So push it up by 3/2 instead. This still
+ * won't catch all the cases. In particular, attachements with
+ * lots of short lines (< 10) will expand by more than that,
+ * but that's ok since this is an optimization. That's why
+ * so_cs_puts uses the 3/2 factor when it does a resize, so
+ * that it won't have to resize linearly until it gets there.
+ */
+ if(body->encoding == ENCBINARY)
+ size = 3*size/2;
+ }
+ }
+
+ return(size);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Format a strings describing one unshown part of a Mime message
+
+Args: number -- A string with the part number i.e. "3.2.1"
+ body -- The body part
+ type -- 1 - Not shown, but can be
+ 2 - Not shown, cannot be shown
+ 3 - Can't print
+ width -- allowed width per line of editorial comment
+ pc -- function used to write the description comment
+
+Result: formatted description written to object ref'd by "pc"
+ ----*/
+char *
+part_desc(char *number, BODY *body, int type, int width, int flags, gf_io_t pc)
+{
+ char *t;
+ char buftmp[MAILTMPLEN], sizebuf[256];
+
+ if(!gf_puts(NEWLINE, pc))
+ return("No space for description");
+
+ format_mime_size(sizebuf, 256, body, FMS_NONE);
+
+ snprintf(buftmp, sizeof(buftmp), "%s", body->description ? body->description : "");
+ buftmp[sizeof(buftmp)-1] = '\0';
+ snprintf(tmp_20k_buf+10000, SIZEOF_20KBUF-10000, "Part %s, %s%.2048s%s%s %s.",
+ number,
+ body->description == NULL ? "" : "\"",
+ body->description == NULL ? ""
+ : (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, buftmp),
+ body->description == NULL ? "" : "\" ",
+ type_desc(body->type, body->subtype, body->parameter, NULL, 1),
+ sizebuf);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+
+ iutf8ncpy((char *)tmp_20k_buf, (char *)(tmp_20k_buf+10000), 10000);
+ tmp_20k_buf[10000] = '\0';
+
+ t = &tmp_20k_buf[strlen(tmp_20k_buf)];
+
+#ifdef SMIME
+ /* if smime and not attempting print */
+ if(F_OFF(F_DONT_DO_SMIME, ps_global) && is_pkcs7_body(body) && type != 3){
+
+ sstrncpy(&t, "\015\012", SIZEOF_20KBUF-(t-tmp_20k_buf));
+
+ if(ps_global->smime && ps_global->smime->need_passphrase){
+ sstrncpy(&t,
+ "This part is a PKCS7 S/MIME enclosure. "
+ "You may be able to view it by entering the correct passphrase "
+ "with the \"Decrypt\" command.",
+ SIZEOF_20KBUF-(t-tmp_20k_buf));
+ }
+ else{
+ sstrncpy(&t,
+ "This part is a PKCS7 S/MIME enclosure. "
+ "Press \"^E\" for more information.",
+ SIZEOF_20KBUF-(t-tmp_20k_buf));
+ }
+
+ } else
+#endif
+
+ if(type){
+ sstrncpy(&t, "\015\012", SIZEOF_20KBUF-(t-tmp_20k_buf));
+ switch(type) {
+ case 1:
+ if(MIME_VCARD(body->type,body->subtype))
+ sstrncpy(&t,
+ /* TRANSLATORS: This is the description of an attachment that isn't being
+ shown but that can be viewed or saved. */
+ _("Not Shown. Use the \"V\" command to view or save to address book."), SIZEOF_20KBUF-(t-tmp_20k_buf));
+ else
+ sstrncpy(&t,
+ /* TRANSLATORS: This is the description of an attachment that isn't being
+ shown but that can be viewed or saved. */
+ _("Not Shown. Use the \"V\" command to view or save this part."), SIZEOF_20KBUF-(t-tmp_20k_buf));
+
+ break;
+
+ case 2:
+ sstrncpy(&t, "Cannot ", SIZEOF_20KBUF-(t-tmp_20k_buf));
+ if(body->type != TYPEAUDIO && body->type != TYPEVIDEO)
+ sstrncpy(&t, "dis", SIZEOF_20KBUF-(t-tmp_20k_buf));
+
+ sstrncpy(&t,
+ "play this part. Press \"V\" then \"S\" to save in a file.", SIZEOF_20KBUF-(t-tmp_20k_buf));
+ break;
+
+ case 3:
+ sstrncpy(&t, _("Unable to print this part."), SIZEOF_20KBUF-(t-tmp_20k_buf));
+ break;
+ }
+ }
+
+ if(!(t = format_editorial(tmp_20k_buf, width, flags, NULL, pc))){
+ if(!gf_puts(NEWLINE, pc))
+ t = "No space for description";
+ }
+
+ return(t);
+}
+
+
+/*----------------------------------------------------------------------
+ Can we display this type/subtype?
+
+ Args: type -- the MIME type to check
+ subtype -- the MIME subtype
+ params -- parameters
+ use_viewer -- tell caller he should run external viewer cmd to view
+
+ Result: Returns:
+
+ MCD_NONE if we can't display this type at all
+ MCD_INTERNAL if we can display it internally
+ MCD_EXTERNAL if it can be displayed via an external viewer
+
+ ----*/
+int
+mime_can_display(int type, char *subtype, BODY *body)
+{
+ return((mailcap_can_display(type, subtype, body, 0)
+ ? MCD_EXTERNAL
+ : (mailcap_can_display(type, subtype, body, 1)
+ ? (MCD_EXT_PROMPT | MCD_EXTERNAL) : MCD_NONE))
+ | ((type == TYPETEXT || type == TYPEMESSAGE
+ || MIME_VCARD(type,subtype))
+ ? MCD_INTERNAL : MCD_NONE));
+}
diff --git a/pith/mimedesc.h b/pith/mimedesc.h
new file mode 100644
index 00000000..39372b1d
--- /dev/null
+++ b/pith/mimedesc.h
@@ -0,0 +1,37 @@
+/*
+ * $Id: mimedesc.h 1012 2008-03-26 00:44:22Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_MIMEDESC_INCLUDED
+#define PITH_MIMEDESC_INCLUDED
+
+
+#include "../pith/atttype.h"
+#include "../pith/filttype.h"
+
+
+/* exported prototypes */
+void describe_mime(BODY *, char *, int, int, int, int);
+void zero_atmts(ATTACH_S *);
+char *body_type_names(int);
+char *type_desc(int, char *, PARAMETER *, PARAMETER *, int);
+long fcc_size_guess(BODY *);
+char *part_desc(char *, BODY *, int, int, int, gf_io_t);
+char *parameter_val(PARAMETER *, char *);
+int mime_can_display(int, char *, BODY *);
+char *get_filename_parameter(char *, size_t, BODY *, char **);
+
+
+#endif /* PITH_MIMEDESC_INCLUDED */
diff --git a/pith/mimetype.c b/pith/mimetype.c
new file mode 100644
index 00000000..3da1362c
--- /dev/null
+++ b/pith/mimetype.c
@@ -0,0 +1,374 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: mimetype.c 955 2008-03-06 23:52:36Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/mimetype.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/mailcap.h"
+#include "../pith/util.h"
+
+/*
+ * We've decided not to implement the RFC1524 standard minimum path, because
+ * some of us think it is harder to debug a problem when you may be misled
+ * into looking at the wrong mailcap entry. Likewise for MIME.Types files.
+ */
+#if defined(DOS) || defined(OS2)
+#define MT_PATH_SEPARATOR ';'
+#define MT_USER_FILE "MIMETYPE"
+#define MT_STDPATH NULL
+#else /* !DOS */
+#define MT_PATH_SEPARATOR ':'
+#define MT_USER_FILE NULL
+#define MT_STDPATH \
+ ".mime.types:/etc/mime.types:/usr/local/lib/mime.types"
+#endif /* !DOS */
+
+#define LINE_BUF_SIZE 2000
+
+
+/*
+ * Types used to pass parameters and operator functions to the
+ * mime.types searching routines.
+ */
+#define MT_MAX_FILE_EXTENSION 3
+
+
+/*
+ * Internal prototypes
+ */
+int mt_browse_types_file(MT_OPERATORPROC, MT_MAP_T *, char *);
+int mt_srch_by_type(MT_MAP_T *, FILE *);
+
+
+
+/*
+ * Exported function that does the work of sniffing the mime.types
+ * files and filling in the body pointer if found. Returns 1 (TRUE) if
+ * extension found, and body pointer filled in, 0 (FALSE) otherwise.
+ */
+int
+set_mime_type_by_extension(struct mail_bodystruct *body, char *filename)
+{
+ MT_MAP_T e2b;
+
+ if(mt_get_file_ext(filename, &e2b.from.ext)
+ && mt_srch_mime_type(mt_srch_by_ext, &e2b)){
+ body->type = e2b.to.mime.type;
+ body->subtype = e2b.to.mime.subtype; /* NOTE: subtype was malloc'd */
+ return(1);
+ }
+
+ return(0);
+}
+
+
+/*
+ * Exported function that maps from mime types to file extensions.
+ */
+int
+set_mime_extension_by_type (char *ext, char *mtype)
+{
+ MT_MAP_T t2e;
+
+ t2e.from.mime_type = mtype;
+ t2e.to.ext = ext;
+ return (mt_srch_mime_type (mt_srch_by_type, &t2e));
+}
+
+
+
+
+/*
+ * Separate and return a pointer to the first character in the 'filename'
+ * character buffer that comes after the rightmost '.' character in the
+ * filename. (What I mean is a pointer to the filename - extension).
+ *
+ * Returns 1 if an extension is found, 0 otherwise.
+ */
+int
+mt_get_file_ext(char *filename, char **extension)
+{
+ dprint((5, "mt_get_file_ext : filename=\"%s\", ",
+ filename ? filename : "?"));
+
+ for(*extension = NULL; filename && *filename; filename++)
+ if(*filename == '.')
+ *extension = filename + 1;
+
+ dprint((5, "extension=\"%s\"\n",
+ (extension && *extension) ? *extension : "?"));
+
+ return(*extension ? 1 : 0);
+}
+
+
+/*
+ * Build a list of possible mime.type files. For each one that exists
+ * call the mt_operator function.
+ * Loop terminates when mt_operator returns non-zero.
+ */
+int
+mt_srch_mime_type(MT_OPERATORPROC mt_operator, MT_MAP_T *mt_map)
+{
+ char *s, *pathcopy, *path;
+ int rv = 0;
+
+ dprint((5, "- mt_srch_mime_type -\n"));
+
+ pathcopy = mc_conf_path(ps_global->VAR_MIMETYPE_PATH, getenv("MIMETYPES"),
+ MT_USER_FILE, MT_PATH_SEPARATOR, MT_STDPATH);
+
+ path = pathcopy; /* overloaded "path" */
+
+ dprint((7, "mime_types: path: %s\n", path ? path : "?"));
+ while(path){
+ if((s = strindex(path, MT_PATH_SEPARATOR)) != NULL)
+ *s++ = '\0';
+
+ if((rv = mt_browse_types_file(mt_operator, mt_map, path)) != 0)
+ break;
+
+ path = s;
+ }
+
+ if(pathcopy)
+ fs_give((void **)&pathcopy);
+
+ if(!rv && mime_os_specific_access()){
+ if(mt_operator == mt_srch_by_ext){
+ char buf[256];
+
+ buf[0] = '\0';
+ if(mime_get_os_mimetype_from_ext(mt_map->from.ext, buf, 256)){
+ if((s = strindex(buf, '/')) != NULL){
+ *s++ = '\0';
+ mt_map->to.mime.type = mt_translate_type(buf);
+ mt_map->to.mime.subtype = cpystr(s);
+ rv = 1;
+ }
+ }
+ }
+ else if(mt_operator == mt_srch_by_type){
+ if(mime_get_os_ext_from_mimetype(mt_map->from.mime_type,
+ mt_map->to.ext, 32)){
+ /* the 32 comes from var ext[] in display_attachment() */
+ if(*(s = mt_map->to.ext) == '.')
+ while((*s = *(s+1)) != '\0')
+ s++;
+
+ rv = 1;
+ }
+ }
+ else
+ panic("Unhandled mime type search");
+ }
+
+ /* if we still can not find the type, but it is a .docx (or alike) extension
+ set the type here. Do not use the grope function.
+ */
+ if(rv == 0){
+ rv = 1; /* assume success */
+ mt_map->to.mime.type = TYPEAPPLICATION;
+ if(!strucmp(mt_map->from.ext, "docx"))
+ mt_map->to.mime.subtype = cpystr("VND.OPENXMLFORMATS-OFFICEDOCUMENT.WORDPROCESSINGML.DOCUMENT");
+ else if(!strucmp(mt_map->from.ext, "xslx"))
+ mt_map->to.mime.subtype = cpystr("VND.OPENXMLFORMATS-OFFICEDOCUMENT.SPREADSHEETML.SHEET");
+ else if(!strucmp(mt_map->from.ext, "xltx"))
+ mt_map->to.mime.subtype = cpystr("VND.OPENXMLFORMATS-OFFICEDOCUMENT.SPREADSHEETML.TEMPLATE");
+ else if(!strucmp(mt_map->from.ext, "potx"))
+ mt_map->to.mime.subtype = cpystr("VND.OPENXMLFORMATS-OFFICEDOCUMENT.PRESENTATIONML.TEMPLATE");
+ else if(!strucmp(mt_map->from.ext, "ppsx"))
+ mt_map->to.mime.subtype = cpystr("VND.OPENXMLFORMATS-OFFICEDOCUMENT.PRESENTATIONML.SLIDESHOW");
+ else if(!strucmp(mt_map->from.ext, "pptx"))
+ mt_map->to.mime.subtype = cpystr("VND.OPENXMLFORMATS-OFFICEDOCUMENT.PRESENTATIONML.PRESENTATION");
+ else if(!strucmp(mt_map->from.ext, "sldx"))
+ mt_map->to.mime.subtype = cpystr("VND.OPENXMLFORMATS-OFFICEDOCUMENT.PRESENTATIONML.SLIDE");
+ else if(!strucmp(mt_map->from.ext, "dotx"))
+ mt_map->to.mime.subtype = cpystr("VND.OPENXMLFORMATS-OFFICEDOCUMENT.WORDPROCESSINGML.TEMPLATE");
+ else if(!strucmp(mt_map->from.ext, "xlam"))
+ mt_map->to.mime.subtype = cpystr("VND.MS-EXCEL.ADDIN.MACROENABLED.12");
+ else if(!strucmp(mt_map->from.ext, "xslb"))
+ mt_map->to.mime.subtype = cpystr("VND.MS-EXCEL.SHEET.BINARY.MACROENABLED.12");
+ else rv = 0; /* else, failure */
+ }
+
+
+ return(rv);
+}
+
+
+/*
+ * Try to match a file extension against extensions found in the file
+ * ``filename'' if that file exists. return 1 if a match
+ * was found and 0 in all other cases.
+ */
+int
+mt_browse_types_file(MT_OPERATORPROC mt_operator, MT_MAP_T *mt_map, char *filename)
+{
+ int rv = 0;
+ FILE *file;
+
+ dprint((7, "mt_browse_types_file(%s)\n", filename ? filename : "?"));
+ if((file = our_fopen(filename, "rb")) != NULL){
+ rv = (*mt_operator)(mt_map, file);
+ fclose(file);
+ }
+ else{
+ dprint((1, "mt_browse: FAILED open(%s) : %s.\n",
+ filename ? filename : "?", error_description(errno)));
+ }
+
+ return(rv);
+}
+
+
+/*
+ * scan each line of the file. Treat each line as a mime type definition.
+ * The first word is a type/subtype specification. All following words
+ * are file extensions belonging to that type/subtype. Words are separated
+ * bij whitespace characters.
+ * If a file extension occurs more than once, then the first definition
+ * determines the file type and subtype.
+ */
+int
+mt_srch_by_ext(MT_MAP_T *e2b, FILE *file)
+{
+ char buffer[LINE_BUF_SIZE];
+
+ /* construct a loop reading the file line by line. Then check each
+ * line for a matching definition.
+ */
+ while(fgets(buffer,LINE_BUF_SIZE,file) != NULL){
+ char *typespec;
+ char *try_extension;
+
+ if(buffer[0] == '#')
+ continue; /* comment */
+
+ /* divide the input buffer into words separated by whitespace.
+ * The first words is the type and subtype. All following words
+ * are file extensions.
+ */
+ dprint((5, "traverse: buffer=\"%s\"\n", buffer));
+ typespec = strtok(buffer," \t"); /* extract type,subtype */
+ if(!typespec)
+ continue;
+
+ dprint((5, "typespec=\"%s\"\n", typespec ? typespec : "?"));
+ while((try_extension = strtok(NULL, " \t\n\r")) != NULL){
+ /* compare the extensions, and assign the type if a match
+ * is found.
+ */
+ dprint((5,"traverse: trying ext \"%s\"\n",try_extension));
+ if(strucmp(try_extension, e2b->from.ext) == 0){
+ /* split the 'type/subtype' specification */
+ char *type, *subtype = NULL;
+
+ type = strtok(typespec,"/");
+ if(type)
+ subtype = strtok(NULL,"/");
+
+ dprint((5, "traverse: type=%s, subtype=%s.\n",
+ type ? type : "<null>",
+ subtype ? subtype : "<null>"));
+ /* The type is encoded as a small integer. we have to
+ * translate the character string naming the type into
+ * the corresponding number.
+ */
+ e2b->to.mime.type = mt_translate_type(type);
+ e2b->to.mime.subtype = cpystr(subtype ? subtype : "x-unknown");
+ return 1; /* a match has been found */
+ }
+ }
+ }
+
+ dprint((5, "traverse: search failed.\n"));
+ return 0;
+}
+
+
+/*
+ * scan each line of the file. Treat each line as a mime type definition.
+ * Here we are looking for a matching type. When that is found return the
+ * first extension that is three chars or less.
+ */
+int
+mt_srch_by_type(MT_MAP_T *t2e, FILE *file)
+{
+ char buffer[LINE_BUF_SIZE];
+
+ /* construct a loop reading the file line by line. Then check each
+ * line for a matching definition.
+ */
+ while(fgets(buffer,LINE_BUF_SIZE,file) != NULL){
+ char *typespec;
+ char *try_extension;
+
+ if(buffer[0] == '#')
+ continue; /* comment */
+
+ /* divide the input buffer into words separated by whitespace.
+ * The first words is the type and subtype. All following words
+ * are file extensions.
+ */
+ dprint((5, "traverse: buffer=%s.\n", buffer));
+ typespec = strtok(buffer," \t"); /* extract type,subtype */
+ dprint((5, "typespec=%s.\n", typespec ? typespec : "?"));
+ if (strucmp (typespec, t2e->from.mime_type) == 0) {
+ while((try_extension = strtok(NULL, " \t\n\r")) != NULL){
+ if (strlen (try_extension) <= MT_MAX_FILE_EXTENSION) {
+ strncpy (t2e->to.ext, try_extension, 32);
+ /*
+ * not sure of the 32, so don't write to byte 32
+ * on purpose with
+ * t2e->to.ext[31] = '\0';
+ * in case that breaks something
+ */
+
+ return (1);
+ }
+ }
+ }
+ }
+
+ dprint((5, "traverse: search failed.\n"));
+ return 0;
+}
+
+
+/*
+ * Translate a character string representing a content type into a short
+ * integer number, according to the coding described in c-client/mail.h
+ * List of content types taken from rfc1521, September 1993.
+ */
+int
+mt_translate_type(char *type)
+{
+ int i;
+
+ for (i=0;(i<=TYPEMAX) && body_types[i] && strucmp(type,body_types[i]);i++)
+ ;
+
+ if (i > TYPEMAX)
+ i = TYPEOTHER;
+ else if (!body_types[i]) /* if empty slot, assign it to this type */
+ body_types[i] = cpystr (type);
+
+ return(i);
+}
diff --git a/pith/mimetype.h b/pith/mimetype.h
new file mode 100644
index 00000000..54981615
--- /dev/null
+++ b/pith/mimetype.h
@@ -0,0 +1,51 @@
+/*
+ * $Id: mimetype.h 769 2007-10-24 00:15:40Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_MIMETYPE_INCLUDED
+#define PITH_MIMETYPE_INCLUDED
+
+
+/*
+ * Struct passed mime.types search functions
+ */
+typedef struct {
+ union {
+ char *ext;
+ char *mime_type;
+ } from;
+ union {
+ struct {
+ int type;
+ char *subtype;
+ } mime;
+ char *ext;
+ } to;
+} MT_MAP_T;
+
+
+typedef int (* MT_OPERATORPROC)(MT_MAP_T *, FILE *);
+
+
+/* exported protoypes */
+int set_mime_type_by_extension(BODY *, char *);
+int set_mime_extension_by_type(char *, char *);
+int mt_srch_by_ext(MT_MAP_T *, FILE *);
+int mt_get_file_ext(char *, char **);
+int mt_srch_mime_type(MT_OPERATORPROC, MT_MAP_T *);
+int mt_translate_type(char *);
+
+
+#endif /* PITH_MIMETYPE_INCLUDED */
diff --git a/pith/msgno.c b/pith/msgno.c
new file mode 100644
index 00000000..465a42e0
--- /dev/null
+++ b/pith/msgno.c
@@ -0,0 +1,941 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: msgno.c 854 2007-12-07 17:44:43Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/msgno.h"
+#include "../pith/flag.h"
+#include "../pith/mailindx.h"
+#include "../pith/pineelt.h"
+#include "../pith/icache.h"
+
+
+/* internal prototypes */
+void set_msg_score(MAILSTREAM *, long, long);
+
+
+/*
+ * * * * Message number management functions * * *
+ */
+
+
+/*----------------------------------------------------------------------
+ Initialize a message manipulation structure for the given total
+
+ Accepts: msgs - pointer to pointer to message manipulation struct
+ tot - number of messages to initialize with
+ ----*/
+void
+msgno_init(MSGNO_S **msgs, long int tot, SortOrder def_sort, int def_sort_rev)
+{
+ long slop = (tot + 1L) % 64;
+ size_t len;
+
+ if(!msgs)
+ return;
+
+ if(!(*msgs)){
+ (*msgs) = (MSGNO_S *)fs_get(sizeof(MSGNO_S));
+ memset((void *)(*msgs), 0, sizeof(MSGNO_S));
+ }
+
+ (*msgs)->sel_cur = 0L;
+ (*msgs)->sel_cnt = 1L;
+ (*msgs)->sel_size = 8L;
+ len = (size_t)(*msgs)->sel_size * sizeof(long);
+ if((*msgs)->select)
+ fs_resize((void **)&((*msgs)->select), len);
+ else
+ (*msgs)->select = (long *)fs_get(len);
+
+ (*msgs)->select[0] = (tot) ? 1L : 0L;
+
+ (*msgs)->sort_size = (tot + 1L) + (64 - slop);
+ len = (size_t)(*msgs)->sort_size * sizeof(long);
+ if((*msgs)->sort)
+ fs_resize((void **)&((*msgs)->sort), len);
+ else
+ (*msgs)->sort = (long *)fs_get(len);
+
+ memset((void *)(*msgs)->sort, 0, len);
+ for(slop = 1L ; slop <= tot; slop++) /* reusing "slop" */
+ (*msgs)->sort[slop] = slop;
+
+ /*
+ * If there is filtering happening, isort will become larger than sort.
+ * Sort is a list of raw message numbers in their sorted order. There
+ * are missing raw numbers because some of the messages are excluded
+ * (MN_EXLD) from the view. Isort has one entry for every raw message
+ * number, which maps to the corresponding msgno (the row in the sort
+ * array). Some of the entries in isort are not used because those
+ * messages are excluded, but the entry is still there because we want
+ * to map from rawno to message number and the row number is the rawno.
+ */
+ (*msgs)->isort_size = (*msgs)->sort_size;
+ if((*msgs)->isort)
+ fs_resize((void **)&((*msgs)->isort), len);
+ else
+ (*msgs)->isort = (long *)fs_get(len);
+
+ (*msgs)->max_msgno = tot;
+ (*msgs)->nmsgs = tot;
+
+ /* set the inverse array */
+ msgno_reset_isort(*msgs);
+
+ (*msgs)->sort_order = def_sort;
+ (*msgs)->reverse_sort = def_sort_rev;
+ (*msgs)->flagged_hid = 0L;
+ (*msgs)->flagged_exld = 0L;
+ (*msgs)->flagged_chid = 0L;
+ (*msgs)->flagged_chid2= 0L;
+ (*msgs)->flagged_coll = 0L;
+ (*msgs)->flagged_usor = 0L;
+ (*msgs)->flagged_tmp = 0L;
+ (*msgs)->flagged_stmp = 0L;
+
+ /*
+ * This one is the total number of messages which are flagged
+ * hid OR chid. It isn't the sum of those two because a
+ * message may be flagged both at the same time.
+ */
+ (*msgs)->flagged_invisible = 0L;
+
+ /*
+ * And this keeps track of visible threads in the THRD_INDX. This is
+ * weird because a thread is visible if any of its messages are
+ * not hidden, including those that are CHID hidden. You can't just
+ * count up all the messages that are hid or chid because you would
+ * miss a thread that has its top-level message hidden but some chid
+ * message not hidden.
+ */
+ (*msgs)->visible_threads = -1L;
+}
+
+
+/*
+ * Isort makes mn_raw2m fast. Alternatively, we could look through
+ * the sort array to do mn_raw2m.
+ */
+void
+msgno_reset_isort(MSGNO_S *msgs)
+{
+ long i;
+
+ if(msgs){
+ /*
+ * Zero isort so raw messages numbers which don't appear in the
+ * sort array show up as undefined.
+ */
+ memset((void *) msgs->isort, 0,
+ (size_t) msgs->isort_size * sizeof(long));
+
+ /* fill in all the defined entries */
+ for(i = 1L; i <= mn_get_total(msgs); i++)
+ msgs->isort[msgs->sort[i]] = i;
+ }
+}
+
+
+
+/*----------------------------------------------------------------------
+ Release resources of a message manipulation structure
+
+ Accepts: msgs - pointer to message manipulation struct
+ n - number to test
+ Returns: with specified structure and its members free'd
+ ----*/
+void
+msgno_give(MSGNO_S **msgs)
+{
+ if(msgs && *msgs){
+ if((*msgs)->sort)
+ fs_give((void **) &((*msgs)->sort));
+
+ if((*msgs)->isort)
+ fs_give((void **) &((*msgs)->isort));
+
+ if((*msgs)->select)
+ fs_give((void **) &((*msgs)->select));
+
+ fs_give((void **) msgs);
+ }
+}
+
+
+
+/*----------------------------------------------------------------------
+ Release resources of a message part exception list
+
+ Accepts: parts -- list of parts to free
+ Returns: with specified structure and its members free'd
+ ----*/
+void
+msgno_free_exceptions(PARTEX_S **parts)
+{
+ if(parts && *parts){
+ if((*parts)->next)
+ msgno_free_exceptions(&(*parts)->next);
+
+ fs_give((void **) &(*parts)->partno);
+ fs_give((void **) parts);
+ }
+}
+
+
+
+/*----------------------------------------------------------------------
+ Increment the current message number
+
+ Accepts: msgs - pointer to message manipulation struct
+ ----*/
+void
+msgno_inc(MAILSTREAM *stream, MSGNO_S *msgs, int flags)
+{
+ long i;
+
+ if(!msgs || mn_get_total(msgs) < 1L)
+ return;
+
+ for(i = msgs->select[msgs->sel_cur] + 1; i <= mn_get_total(msgs); i++){
+ if(!msgline_hidden(stream, msgs, i, flags)){
+ (msgs)->select[((msgs)->sel_cur)] = i;
+ break;
+ }
+ }
+}
+
+
+
+/*----------------------------------------------------------------------
+ Decrement the current message number
+
+ Accepts: msgs - pointer to message manipulation struct
+ ----*/
+void
+msgno_dec(MAILSTREAM *stream, MSGNO_S *msgs, int flags)
+{
+ long i;
+
+ if(!msgs || mn_get_total(msgs) < 1L)
+ return;
+
+ for(i = (msgs)->select[((msgs)->sel_cur)] - 1L; i >= 1L; i--){
+ if(!msgline_hidden(stream, msgs, i, flags)){
+ (msgs)->select[((msgs)->sel_cur)] = i;
+ break;
+ }
+ }
+}
+
+
+
+/*----------------------------------------------------------------------
+ Got thru the message mapping table, and remove messages with DELETED flag
+
+ Accepts: stream -- mail stream to removed message references from
+ msgs -- pointer to message manipulation struct
+ f -- flags to use a purge criteria
+ ----*/
+void
+msgno_exclude_deleted(MAILSTREAM *stream, MSGNO_S *msgs)
+{
+ long i, rawno;
+ MESSAGECACHE *mc;
+ int need_isort_reset = 0;
+
+ if(!msgs || msgs->max_msgno < 1L)
+ return;
+
+ /*
+ * With 3.91 we're using a new strategy for finding and operating
+ * on all the messages with deleted status. The idea is to do a
+ * mail_search for deleted messages so the elt's "searched" bit gets
+ * set, and then to scan the elt's for them and set our local bit
+ * to indicate they're excluded...
+ */
+ (void) count_flagged(stream, F_DEL);
+
+ /*
+ * Start with the end of the folder and work backwards so that
+ * msgno_exclude doesn't have to shift the entire array each time when
+ * there are lots of deleteds. In fact, if everything is deleted (like
+ * might be the case in a huge newsgroup) then it never has to shift
+ * anything. It is always at the end of the array just eliminating the
+ * last one instead. So instead of an n**2 operation, it is n.
+ */
+ for(i = msgs->max_msgno; i >= 1L; i--)
+ if((rawno = mn_m2raw(msgs, i)) > 0L && stream && rawno <= stream->nmsgs
+ && (mc = mail_elt(stream, rawno))
+ && ((mc->valid && mc->deleted) || (!mc->valid && mc->searched))){
+ msgno_exclude(stream, msgs, i, 0);
+ need_isort_reset++;
+ }
+
+ if(need_isort_reset)
+ msgno_reset_isort(msgs);
+
+ /*
+ * If we excluded away a zoomed display, unhide everything...
+ */
+ if(msgs->max_msgno > 0L && any_lflagged(msgs, MN_HIDE) >= msgs->max_msgno)
+ for(i = 1L; i <= msgs->max_msgno; i++)
+ set_lflag(stream, msgs, i, MN_HIDE, 0);
+}
+
+
+
+void
+msgno_exclude(MAILSTREAM *stream, MSGNO_S *msgmap, long int msgno, int reset_isort)
+{
+ long i;
+
+ /*--- clear all flags to keep our counts consistent ---*/
+ set_lflag(stream, msgmap, msgno, MN_HIDE | MN_CHID | MN_CHID2 | MN_SLCT, 0);
+ set_lflag(stream, msgmap, msgno, MN_EXLD, 1); /* mark excluded */
+
+ /* erase knowledge in sort array (shift array down) */
+ for(i = msgno + 1L; i <= msgmap->max_msgno; i++)
+ msgmap->sort[i-1L] = msgmap->sort[i];
+
+ msgmap->max_msgno = MAX(0L, msgmap->max_msgno - 1L);
+ if(reset_isort)
+ msgno_reset_isort(msgmap);
+
+ msgno_flush_selected(msgmap, msgno);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Accepts: stream -- mail stream to removed message references from
+ msgs -- pointer to message manipulation struct
+ flags
+ MI_REFILTERING -- do includes appropriate for refiltering
+ MI_STATECHGONLY -- when refiltering, maybe only re-include
+ messages which have had state changes
+ since they were originally filtered
+ Returns 1 if any new messages are included (indicating that we need
+ to re-sort)
+ 0 if no new messages are included
+ ----*/
+int
+msgno_include(MAILSTREAM *stream, MSGNO_S *msgs, int flags)
+{
+ long i, slop, old_total, old_size;
+ int exbits, ret = 0;
+ size_t len;
+ MESSAGECACHE *mc;
+
+ if(!msgs)
+ return(ret);
+
+ for(i = 1L; i <= stream->nmsgs; i++){
+ if(!msgno_exceptions(stream, i, "0", &exbits, FALSE))
+ exbits = 0;
+
+ if((((flags & MI_REFILTERING) && (exbits & MSG_EX_FILTERED)
+ && !(exbits & MSG_EX_FILED)
+ && (!(flags & MI_STATECHGONLY) || (exbits & MSG_EX_STATECHG)))
+ || (!(flags & MI_REFILTERING) && !(exbits & MSG_EX_FILTERED)))
+ && get_lflag(stream, NULL, i, MN_EXLD)){
+ old_total = msgs->max_msgno;
+ old_size = msgs->sort_size;
+ slop = (msgs->max_msgno + 1L) % 64;
+ msgs->sort_size = (msgs->max_msgno + 1L) + (64 - slop);
+ len = (size_t) msgs->sort_size * sizeof(long);
+ if(msgs->sort){
+ if(old_size != msgs->sort_size)
+ fs_resize((void **)&(msgs->sort), len);
+ }
+ else
+ msgs->sort = (long *)fs_get(len);
+
+ ret = 1;
+ msgs->sort[++msgs->max_msgno] = i;
+ msgs->isort[i] = msgs->max_msgno;
+ set_lflag(stream, msgs, msgs->max_msgno, MN_EXLD, 0);
+ if(flags & MI_REFILTERING){
+ exbits &= ~(MSG_EX_FILTERED | MSG_EX_TESTED);
+ msgno_exceptions(stream, i, "0", &exbits, TRUE);
+ }
+
+ if(old_total <= 0L){ /* if no previous messages, */
+ if(!msgs->select){ /* select the new message */
+ msgs->sel_size = 8L;
+ len = (size_t)msgs->sel_size * sizeof(long);
+ msgs->select = (long *)fs_get(len);
+ }
+
+ msgs->sel_cnt = 1L;
+ msgs->sel_cur = 0L;
+ msgs->select[0] = 1L;
+ }
+ }
+ else if((flags & MI_REFILTERING)
+ && (exbits & (MSG_EX_FILTERED | MSG_EX_TESTED))
+ && !(exbits & MSG_EX_FILED)
+ && (!(exbits & MSG_EX_MANUNDEL)
+ || ((mc = mail_elt(stream, i)) && mc->deleted))
+ && (!(flags & MI_STATECHGONLY) || (exbits & MSG_EX_STATECHG))){
+ /*
+ * We get here if the message was filtered by a filter that
+ * just changes status bits (it wasn't excluded), and now also
+ * if the message was merely tested for filtering. It has also
+ * not been manually undeleted. If it was manually undeleted, we
+ * don't want to reprocess the filter, undoing the user's
+ * manual undeleting. Of course, a new pine will re check this
+ * message anyway, so the user had better be using this
+ * manual undeleting only to temporarily save him or herself
+ * from an expunge before Saving or printing or something.
+ * Also, we want to still try filtering if the message has at
+ * all been marked deleted, even if the there was any manual
+ * undeleting, since this directly precedes an expunge, we want
+ * to make sure the filter does the right thing before getting
+ * rid of the message forever.
+ */
+ exbits &= ~(MSG_EX_FILTERED | MSG_EX_TESTED);
+ msgno_exceptions(stream, i, "0", &exbits, TRUE);
+ }
+ }
+
+ return(ret);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Add the given number of raw message numbers to the end of the
+ current list...
+
+ Accepts: msgs - pointer to message manipulation struct
+ n - number to add
+ Returns: with fixed up msgno struct
+
+ Only have to adjust the sort array, as since new mail can't cause
+ selection!
+ ----*/
+void
+msgno_add_raw(MSGNO_S *msgs, long int n)
+{
+ long slop, islop, old_total, old_size, old_isize;
+ size_t len, ilen;
+
+ if(!msgs || n <= 0L)
+ return;
+
+ old_total = msgs->max_msgno;
+ old_size = msgs->sort_size;
+ old_isize = msgs->isort_size;
+ slop = (msgs->max_msgno + n + 1L) % 64;
+ islop = (msgs->nmsgs + n + 1L) % 64;
+ msgs->sort_size = (msgs->max_msgno + n + 1L) + (64 - slop);
+ msgs->isort_size = (msgs->nmsgs + n + 1L) + (64 - islop);
+ len = (size_t) msgs->sort_size * sizeof(long);
+ ilen = (size_t) msgs->isort_size * sizeof(long);
+ if(msgs->sort){
+ if(old_size != msgs->sort_size)
+ fs_resize((void **) &(msgs->sort), len);
+ }
+ else
+ msgs->sort = (long *) fs_get(len);
+
+ if(msgs->isort){
+ if(old_isize != msgs->isort_size)
+ fs_resize((void **) &(msgs->isort), ilen);
+ }
+ else
+ msgs->isort = (long *) fs_get(ilen);
+
+ while(n-- > 0){
+ msgs->sort[++msgs->max_msgno] = ++msgs->nmsgs;
+ msgs->isort[msgs->nmsgs] = msgs->max_msgno;
+ }
+
+ if(old_total <= 0L){ /* if no previous messages, */
+ if(!msgs->select){ /* select the new message */
+ msgs->sel_size = 8L;
+ len = (size_t) msgs->sel_size * sizeof(long);
+ msgs->select = (long *) fs_get(len);
+ }
+
+ msgs->sel_cnt = 1L;
+ msgs->sel_cur = 0L;
+ msgs->select[0] = 1L;
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Remove all knowledge of the given raw message number
+
+ Accepts: msgs - pointer to message manipulation struct
+ rawno - number to remove
+ Returns: with fixed up msgno struct
+
+ After removing *all* references, adjust the sort array and
+ various pointers accordingly...
+ ----*/
+void
+msgno_flush_raw(MSGNO_S *msgs, long int rawno)
+{
+ long i, old_sorted = 0L;
+ int shift = 0;
+
+ if(!msgs)
+ return;
+
+ /* blast rawno from sort array */
+ for(i = 1L; i <= msgs->max_msgno; i++){
+ if(msgs->sort[i] == rawno){
+ old_sorted = i;
+ shift++;
+ }
+
+ if(shift && i < msgs->max_msgno)
+ msgs->sort[i] = msgs->sort[i + 1L];
+
+ if(msgs->sort[i] > rawno)
+ msgs->sort[i] -= 1L;
+ }
+
+ /*---- now, fixup counts and select array ----*/
+ if(--msgs->nmsgs < 0)
+ msgs->nmsgs = 0L;
+
+ if(old_sorted){
+ if(--msgs->max_msgno < 0)
+ msgs->max_msgno = 0L;
+
+ msgno_flush_selected(msgs, old_sorted);
+ }
+
+ msgno_reset_isort(msgs);
+
+ { char b[100];
+ snprintf(b, sizeof(b),
+ "isort validity: end of msgno_flush_raw: rawno=%ld\n", rawno);
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Remove all knowledge of the given selected message number
+
+ Accepts: msgs - pointer to message manipulation struct
+ n - number to remove
+ Returns: with fixed up selec members in msgno struct
+
+ Remove reference and fix up selected message numbers beyond
+ the specified number
+ ----*/
+void
+msgno_flush_selected(MSGNO_S *msgs, long int n)
+{
+ long i;
+ int shift = 0;
+
+ for(i = 0L; i < msgs->sel_cnt; i++){
+ if(!shift && (msgs->select[i] == n))
+ shift++;
+
+ if(shift && i + 1L < msgs->sel_cnt)
+ msgs->select[i] = msgs->select[i + 1L];
+
+ if(n < msgs->select[i] || msgs->select[i] > msgs->max_msgno)
+ msgs->select[i] -= 1L;
+ }
+
+ if(shift && msgs->sel_cnt > 1L)
+ msgs->sel_cnt -= 1L;
+}
+
+
+void
+msgno_set_sort(MSGNO_S *msgs, SortOrder sort)
+{
+ if(msgs){
+ if(sort == SortScore)
+ scores_are_used(SCOREUSE_INVALID);
+
+ msgs->sort_order = sort;
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Test to see if the given message number is in the selected message
+ list...
+
+ Accepts: msgs - pointer to message manipulation struct
+ n - number to test
+ Returns: true if n is in selected array, false otherwise
+
+ ----*/
+int
+msgno_in_select(MSGNO_S *msgs, long int n)
+{
+ long i;
+
+ if(msgs)
+ for(i = 0L; i < msgs->sel_cnt; i++)
+ if(msgs->select[i] == n)
+ return(1);
+
+ return(0);
+}
+
+
+
+/*----------------------------------------------------------------------
+ return our index number for the given raw message number
+
+ Accepts: msgs - pointer to message manipulation struct
+ msgno - number that's important
+ part
+ Returns: our index number of given raw message
+
+ ----*/
+int
+msgno_exceptions(MAILSTREAM *stream, long int rawno, char *part, int *bits, int set)
+{
+ PINELT_S **peltp;
+ PARTEX_S **partp;
+ MESSAGECACHE *mc;
+
+ if(!stream || rawno < 1L || rawno > stream->nmsgs)
+ return FALSE;
+
+ /*
+ * Get pointer to exceptional part list, and scan down it
+ * for the requested part...
+ */
+ if((mc = mail_elt(stream, rawno)) && (*(peltp = (PINELT_S **) &mc->sparep)))
+ for(partp = &(*peltp)->exceptions; *partp; partp = &(*partp)->next){
+ if(part){
+ if(!strcmp(part, (*partp)->partno)){
+ if(bits){
+ if(set)
+ (*partp)->handling = *bits;
+ else
+ *bits = (*partp)->handling;
+ }
+
+ return(TRUE); /* bingo! */
+ }
+ }
+ else if(bits){
+ /*
+ * The caller provided flags, but no part.
+ * We are looking to see if the bits are set in any of the
+ * parts. This doesn't count parts with non-digit partno's (like
+ * scores) because those are used differently.
+ * any of the flags...
+ */
+ if((*partp)->partno && *(*partp)->partno &&
+ isdigit((unsigned char) *(*partp)->partno) &&
+ (*bits & (*partp)->handling) == *bits)
+ return(TRUE);
+ }
+ else
+ /*
+ * The caller didn't specify a part, so
+ * they must just be interested in whether
+ * the msg had any exceptions at all...
+ */
+ return(TRUE);
+ }
+
+ if(set && part){
+ if(!*peltp){
+ *peltp = (PINELT_S *) fs_get(sizeof(PINELT_S));
+ memset(*peltp, 0, sizeof(PINELT_S));
+ partp = &(*peltp)->exceptions;
+ }
+
+ (*partp) = (PARTEX_S *) fs_get(sizeof(PARTEX_S));
+ (*partp)->partno = cpystr(part);
+ (*partp)->next = NULL;
+ (*partp)->handling = *bits;
+ return(TRUE);
+ }
+
+ if(bits) /* init bits */
+ *bits = 0;
+
+ return(FALSE);
+}
+
+
+/*
+ * Checks whether any parts of any of the messages in msgmap are marked
+ * for deletion.
+ */
+int
+msgno_any_deletedparts(MAILSTREAM *stream, MSGNO_S *msgmap)
+{
+ long n, rawno;
+ PINELT_S *pelt;
+ PARTEX_S **partp;
+ MESSAGECACHE *mc;
+
+ for(n = mn_first_cur(msgmap); n > 0L; n = mn_next_cur(msgmap))
+ if((rawno = mn_m2raw(msgmap, n)) > 0L
+ && stream && rawno <= stream->nmsgs
+ && (mc = mail_elt(stream, rawno))
+ && (pelt = (PINELT_S *) mc->sparep))
+ for(partp = &pelt->exceptions; *partp; partp = &(*partp)->next)
+ if(((*partp)->handling & MSG_EX_DELETE)
+ && (*partp)->partno
+ && *(*partp)->partno != '0'
+ && isdigit((unsigned char) *(*partp)->partno))
+ return(1);
+
+ return(0);
+}
+
+
+int
+msgno_part_deleted(MAILSTREAM *stream, long int rawno, char *part)
+{
+ char *p;
+ int expbits;
+
+ /*
+ * Is this attachment or any of it's parents in the
+ * MIME structure marked for deletion?
+ */
+ for(p = part; p && *p; p = strindex(++p, '.')){
+ if(*p == '.')
+ *p = '\0';
+
+ (void) msgno_exceptions(stream, rawno, part, &expbits, FALSE);
+ if(!*p)
+ *p = '.';
+
+ if(expbits & MSG_EX_DELETE)
+ return(TRUE);
+ }
+
+ /* Finally, check if the whole message body's deleted */
+ return(msgno_exceptions(stream, rawno, "", &expbits, FALSE)
+ ? (expbits & MSG_EX_DELETE) : FALSE);
+}
+
+
+
+/*
+ * Set the score for a message to score, which can be anything including
+ * SCORE_UNDEF.
+ */
+void
+set_msg_score(MAILSTREAM *stream, long int rawmsgno, long int score)
+{
+ int intscore;
+
+ /* scores are between SCORE_MIN and SCORE_MAX, so ok */
+ intscore = (int) score;
+
+ (void) msgno_exceptions(stream, rawmsgno, "S", &intscore, TRUE);
+}
+
+
+/*
+ * Returns the score for a message. If that score is undefined the value
+ * returned will be SCORE_UNDEF, so the caller has to be prepared for that.
+ * The caller should calculate the undefined scores before calling this.
+ */
+long
+get_msg_score(MAILSTREAM *stream, long int rawmsgno)
+{
+ int s;
+ long score;
+
+ if(msgno_exceptions(stream, rawmsgno, "S", &s, FALSE))
+ score = (long) s;
+ else
+ score = SCORE_UNDEF;
+
+ return(score);
+}
+
+
+void
+clear_msg_score(MAILSTREAM *stream, long int rawmsgno)
+{
+ if(!stream)
+ return;
+
+ set_msg_score(stream, rawmsgno, SCORE_UNDEF);
+}
+
+
+/*
+ * Set all the score values to undefined.
+ */
+void
+clear_folder_scores(MAILSTREAM *stream)
+{
+ long n;
+
+ if(!stream)
+ return;
+
+ for(n = 1L; n <= stream->nmsgs; n++)
+ clear_msg_score(stream, n);
+}
+
+
+/*
+ * Calculates all of the scores for the searchset and stores them in the
+ * mail elts. Careful, this function uses patterns so if the caller is using
+ * patterns then the caller will probably have to reset the pattern functions.
+ * That is, will have to call first_pattern again with the correct type.
+ *
+ * Args: stream
+ * searchset -- calculate scores for this set of messages
+ * no_fetch -- we're in a callback from c-client, don't call c-client
+ *
+ * Returns 1 -- ok
+ * 0 -- error, because of no_fetch
+ */
+int
+calculate_some_scores(MAILSTREAM *stream, SEARCHSET *searchset, int no_fetch)
+{
+ PAT_S *pat = NULL;
+ PAT_STATE pstate;
+ char *savebits;
+ long newscore, addtoscore, score;
+ int error = 0;
+ long rflags = ROLE_SCORE;
+ long n, i;
+ SEARCHSET *s;
+ MESSAGECACHE *mc;
+ HEADER_TOK_S *hdrtok;
+
+ dprint((7, "calculate_some_scores\n"));
+
+ if(nonempty_patterns(rflags, &pstate)){
+
+ /* calculate scores */
+ if(searchset){
+
+ /* this calls match_pattern which messes up searched bits */
+ savebits = (char *)fs_get((stream->nmsgs+1) * sizeof(char));
+ for(i = 1L; i <= stream->nmsgs; i++)
+ savebits[i] = (mc = mail_elt(stream, i)) ? mc->searched : 0;
+
+ /*
+ * First set all the scores in the searchset to zero so that they
+ * will no longer be undefined.
+ */
+ score = 0L;
+ for(s = searchset; s; s = s->next)
+ for(n = s->first; n <= s->last; n++)
+ set_msg_score(stream, n, score);
+
+ for(pat = first_pattern(&pstate);
+ !error && pat;
+ pat = next_pattern(&pstate)){
+
+ newscore = pat->action->scoreval;
+ hdrtok = pat->action->scorevalhdrtok;
+
+ /*
+ * This no_fetch probably isn't necessary since
+ * we will actually have fetched this with
+ * the envelope. Just making sure.
+ */
+ if(hdrtok && no_fetch){
+ error++;
+ break;
+ }
+
+ switch(match_pattern(pat->patgrp, stream, searchset, NULL, NULL,
+ (no_fetch ? MP_IN_CCLIENT_CB : 0)
+ | (SE_NOSERVER|SE_NOPREFETCH))){
+ case 1:
+ if(!pat->action || pat->action->bogus)
+ break;
+
+ for(s = searchset; s; s = s->next)
+ for(n = s->first; n <= s->last; n++)
+ if(n > 0L && stream && n <= stream->nmsgs
+ && (mc = mail_elt(stream, n)) && mc->searched){
+ if((score = get_msg_score(stream,n)) == SCORE_UNDEF)
+ score = 0L;
+
+ if(hdrtok)
+ addtoscore = scorevalfrommsg(stream, n, hdrtok, no_fetch);
+ else
+ addtoscore = newscore;
+
+ score += addtoscore;
+ set_msg_score(stream, n, score);
+ }
+
+ break;
+
+ case 0:
+ break;
+
+ case -1:
+ error++;
+ break;
+ }
+ }
+
+ for(i = 1L; i <= stream->nmsgs; i++)
+ if((mc = mail_elt(stream, i)) != NULL)
+ mc->searched = savebits[i];
+
+ fs_give((void **)&savebits);
+
+ if(error){
+ /*
+ * Revert to undefined scores.
+ */
+ score = SCORE_UNDEF;
+ for(s = searchset; s; s = s->next)
+ for(n = s->first; n <= s->last; n++)
+ set_msg_score(stream, n, score);
+ }
+ }
+ }
+
+ return(error ? 0 : 1);
+}
+
+
+void
+free_pine_elt(void **sparep)
+{
+ PINELT_S **peltp;
+
+ peltp = (PINELT_S **) sparep;
+
+ if(peltp && *peltp){
+ msgno_free_exceptions(&(*peltp)->exceptions);
+ if((*peltp)->pthrd)
+ fs_give((void **) &(*peltp)->pthrd);
+
+ if((*peltp)->ice)
+ free_ice(&(*peltp)->ice);
+
+ fs_give((void **) peltp);
+ }
+}
diff --git a/pith/msgno.h b/pith/msgno.h
new file mode 100644
index 00000000..00b669c1
--- /dev/null
+++ b/pith/msgno.h
@@ -0,0 +1,207 @@
+/*
+ * $Id: msgno.h 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_MSGNO_INCLUDED
+#define PITH_MSGNO_INCLUDED
+
+
+#include "../pith/sorttype.h"
+
+
+/*
+ * Macros to support anything you'd ever want to do with a message
+ * number...
+ */
+#define mn_init(P, m) msgno_init((P), (m), \
+ ps_global->def_sort, ps_global->def_sort_rev)
+
+#define mn_get_cur(p) (((p) && (p)->select) \
+ ? (p)->select[(p)->sel_cur] : -1)
+
+#define mn_set_cur(p, m) do{ \
+ if(p){ \
+ (p)->select[(p)->sel_cur] = (m); \
+ } \
+ }while(0)
+
+#define mn_inc_cur(s, p, f) msgno_inc(s, p, f)
+
+#define mn_dec_cur(s, p, f) msgno_dec(s, p, f)
+
+#define mn_add_cur(p, m) do{ \
+ if(p){ \
+ if((p)->sel_cnt+1L > (p)->sel_size){ \
+ (p)->sel_size += 10L; \
+ fs_resize((void **)&((p)->select), \
+ (size_t)(p)->sel_size \
+ * sizeof(long)); \
+ } \
+ (p)->select[((p)->sel_cnt)++] = (m); \
+ } \
+ }while(0)
+
+#define mn_total_cur(p) ((p) ? (p)->sel_cnt : 0L)
+
+#define mn_first_cur(p) (((p) && (p)->sel_cnt > 0L) \
+ ? (p)->select[(p)->sel_cur = 0] : 0L)
+
+#define mn_next_cur(p) (((p) && ((p)->sel_cur + 1) < (p)->sel_cnt) \
+ ? (p)->select[++((p)->sel_cur)] : -1L)
+
+#define mn_is_cur(p, m) msgno_in_select((p), (m))
+
+#define mn_reset_cur(p, m) do{ \
+ if(p){ \
+ (p)->sel_cur = 0L; \
+ (p)->sel_cnt = 1L; \
+ (p)->sel_size = 8L; \
+ fs_resize((void **)&((p)->select), \
+ (size_t)(p)->sel_size * sizeof(long));\
+ (p)->select[0] = (m); \
+ } \
+ }while(0)
+
+#define mn_m2raw(p, m) (((p) && (p)->sort && (m) > 0 \
+ && (m) <= mn_get_total(p)) \
+ ? (p)->sort[m] : 0L)
+
+#define mn_raw2m(p, m) (((p) && (p)->isort && (m) > 0 \
+ && (m) <= mn_get_nmsgs(p)) \
+ ? (p)->isort[m] : 0L)
+
+#define mn_get_total(p) ((p) ? (p)->max_msgno : 0L)
+
+#define mn_set_total(p, m) do{ if(p) (p)->max_msgno = (m); }while(0)
+
+#define mn_get_nmsgs(p) ((p) ? (p)->nmsgs : 0L)
+
+#define mn_set_nmsgs(p, m) do{ if(p) (p)->nmsgs = (m); }while(0)
+
+#define mn_add_raw(p, m) msgno_add_raw((p), (m))
+
+#define mn_flush_raw(p, m) msgno_flush_raw((p), (m))
+
+#define mn_get_sort(p) ((p) ? (p)->sort_order : SortArrival)
+
+#define mn_set_sort(p, t) msgno_set_sort((p), (t))
+
+#define mn_get_revsort(p) ((p) ? (p)->reverse_sort : 0)
+
+#define mn_set_revsort(p, t) do{ \
+ if(p) \
+ (p)->reverse_sort = (t); \
+ }while(0)
+
+#define mn_get_mansort(p) ((p) ? (p)->manual_sort : 0)
+
+#define mn_set_mansort(p, t) do{ \
+ if(p) \
+ (p)->manual_sort = (t); \
+ }while(0)
+
+#define mn_give(P) msgno_give(P)
+
+
+/*
+ * This is *the* struct that keeps track of the pine message number to
+ * raw c-client sequence number mappings. The mapping is necessary
+ * because pine may re-sort or even hide (exclude) c-client numbers
+ * from the displayed list of messages. See mailindx.c:msgno_* and
+ * the mn_* macros above for how this things gets used. See
+ * mailcmd.c:pseudo_selected for an explanation of the funny business
+ * going on with the "hilited" field...
+ */
+typedef struct msg_nos {
+ long *select, /* selected message array */
+ sel_cur, /* current interesting msg */
+ sel_cnt, /* its size */
+ sel_size, /* its size */
+ *sort, /* sorted array of msgno's */
+ sort_size, /* its size */
+ *isort, /* inverse of sort array */
+ isort_size, /* its size */
+ max_msgno, /* total messages in table */
+ nmsgs, /* total msgs in folder */
+ hilited, /* holder for "current" msg*/
+ top, /* message at top of screen*/
+ max_thrdno,
+ top_after_thrd; /* top after thrd view */
+ SortOrder sort_order; /* list's current sort */
+ unsigned reverse_sort:1; /* whether that's reversed */
+ unsigned manual_sort:1; /* sorted with $ command */
+ long flagged_hid, /* hidden count */
+ flagged_exld, /* excluded count */
+ flagged_coll, /* collapsed count */
+ flagged_chid, /* collapsed-hidden count */
+ flagged_chid2, /* */
+ flagged_usor, /* new unsorted mail */
+ flagged_tmp, /* tmp flagged count */
+ flagged_stmp, /* stmp flagged count */
+ flagged_invisible, /* this one's different */
+ flagged_srch, /* search result/not slctd */
+ visible_threads; /* so is this one */
+} MSGNO_S;
+
+
+#define MSG_EX_DELETE 0x0001 /* part is deleted */
+#define MSG_EX_RECENT 0x0002
+#define MSG_EX_TESTED 0x0004 /* filtering has been run on this msg */
+#define MSG_EX_FILTERED 0x0008 /* msg has actually been filtered away*/
+#define MSG_EX_FILED 0x0010 /* msg has been filed */
+#define MSG_EX_FILTONCE 0x0020
+#define MSG_EX_FILEONCE 0x0040 /* These last two mean that the
+ message has been filtered or filed
+ already but the filter rule was
+ non-terminating so it is still
+ possible it will get filtered
+ again. When we're done, we flip
+ these two to EX_FILTERED and
+ EX_FILED, the permanent versions. */
+#define MSG_EX_PEND_EXLD 0x0080 /* pending exclusion */
+#define MSG_EX_MANUNDEL 0x0100 /* has been manually undeleted */
+#define MSG_EX_STATECHG 0x0200 /* state change since filtering */
+
+/* msgno_include flags */
+#define MI_NONE 0x00
+#define MI_REFILTERING 0x01
+#define MI_STATECHGONLY 0x02
+#define MI_CLOSING 0x04
+
+
+/* exported protoypes */
+void msgno_init(MSGNO_S **, long, SortOrder, int);
+void msgno_reset_isort(MSGNO_S *);
+void msgno_give(MSGNO_S **);
+void msgno_inc(MAILSTREAM *, MSGNO_S *, int);
+void msgno_dec(MAILSTREAM *, MSGNO_S *, int);
+void msgno_exclude_deleted(MAILSTREAM *, MSGNO_S *);
+void msgno_exclude(MAILSTREAM *, MSGNO_S *, long, int);
+int msgno_include(MAILSTREAM *, MSGNO_S *, int);
+void msgno_add_raw(MSGNO_S *, long);
+void msgno_flush_raw(MSGNO_S *, long);
+void msgno_flush_selected(MSGNO_S *, long);
+void msgno_set_sort(MSGNO_S *, SortOrder);
+int msgno_in_select(MSGNO_S *, long);
+int msgno_exceptions(MAILSTREAM *, long, char *, int *, int);
+int msgno_any_deletedparts(MAILSTREAM *, MSGNO_S *);
+int msgno_part_deleted(MAILSTREAM *, long, char *);
+long get_msg_score(MAILSTREAM *, long);
+void clear_msg_score(MAILSTREAM *, long);
+void clear_folder_scores(MAILSTREAM *);
+int calculate_some_scores(MAILSTREAM *, SEARCHSET *, int);
+void free_pine_elt(void **);
+
+
+#endif /* PITH_MSGNO_INCLUDED */
diff --git a/pith/newmail.c b/pith/newmail.c
new file mode 100644
index 00000000..26d9a986
--- /dev/null
+++ b/pith/newmail.c
@@ -0,0 +1,948 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: newmail.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/newmail.h"
+#include "../pith/conf.h"
+#include "../pith/flag.h"
+#include "../pith/mailindx.h"
+#include "../pith/msgno.h"
+#include "../pith/bldaddr.h"
+#include "../pith/stream.h"
+#include "../pith/sort.h"
+#include "../pith/status.h"
+#include "../pith/util.h"
+#include "../pith/thread.h"
+#include "../pith/options.h"
+#include "../pith/folder.h"
+#include "../pith/ablookup.h"
+
+#ifdef _WINDOWS
+#include "../pico/osdep/mswin.h"
+#endif
+
+
+/*
+ * Hooks for various stuff
+ */
+void (*pith_opt_newmail_announce)(MAILSTREAM *, long, long);
+void (*pith_opt_newmail_check_cue)(int);
+void (*pith_opt_checkpoint_cue)(int);
+void (*pith_opt_icon_text)(char *, int);
+
+
+void fixup_flags(MAILSTREAM *, MSGNO_S *);
+int check_point(MAILSTREAM *, CheckPointTime, int);
+
+
+/*----------------------------------------------------------------------
+ new_mail() - check for new mail in the inbox
+
+ Input: force -- flag indicating we should check for new mail no matter
+ time_for_check_point -- 0: GoodTime, 1: BadTime, 2: VeryBadTime
+ flags -- whether to q a new mail status message or defer the sort
+
+ Result: returns -1 if there was no new mail. Otherwise returns the
+ sorted message number of the smallest message number that
+ was changed. That is the screen needs to be repainted from
+ that message down.
+
+ Limit frequency of checks because checks use some resources. That is
+ they generate an IMAP packet or require locking the local mailbox.
+ (Acutally the lock isn't required, a stat will do, but the current
+ c-client mail code locks before it stats.)
+
+ Returns >= 0 only if there is a change in the given mail stream. Otherwise
+ this returns -1. On return the message counts in the pine
+ structure are updated to reflect the current number of messages including
+ any new mail and any expunging.
+
+ --- */
+long
+new_mail(int force_arg, CheckPointTime time_for_check_point, int flags)
+{
+ static time_t last_check_point_call = 0;
+ long since_last_input;
+ time_t expunged_reaper_to, adj_idle_timeout, interval, adj;
+ static int nexttime = 0;
+ time_t now;
+ long n, rv = 0, t_nm_count = 0, exp_count;
+ MAILSTREAM *m;
+ int force, i, started_on;
+ int new_mail_was_announced = 0;
+ int have_pinged_non_special = 0;
+ int timeo;
+
+ dprint((9, "new mail called (force=%d %s flags=0x%x)\n",
+ force_arg,
+ time_for_check_point == GoodTime ? "GoodTime" :
+ time_for_check_point == BadTime ? "BadTime" :
+ time_for_check_point == VeryBadTime ? "VeryBad" :
+ time_for_check_point == DoItNow ? "DoItNow" : "?",
+ flags));
+
+ force = force_arg;
+
+ now = time(0);
+
+ timeo = get_input_timeout();
+
+ if(time_for_check_point == GoodTime)
+ adrbk_maintenance();
+
+ if(time_for_check_point == GoodTime || force_arg)
+ folder_unseen_count_updater(UFU_ANNOUNCE | (force_arg ? UFU_FORCE : 0));
+
+ if(sp_need_to_rethread(ps_global->mail_stream))
+ force = 1;
+
+ if(!force && sp_unsorted_newmail(ps_global->mail_stream))
+ force = !(flags & NM_DEFER_SORT);
+
+ if(!ps_global->mail_stream
+ || !(timeo || force || sp_a_locked_stream_changed()))
+ return(-1);
+
+ last_check_point_call = now;
+ since_last_input = (long) now - (long) time_of_last_input();
+
+ /*
+ * We have this for loop followed by the do-while so that we will prefer
+ * to ping the active streams before we ping the inactive ones, in cases
+ * where the pings or checks are taking a long time.
+ */
+ for(i = 0; i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ if(!m || m->halfopen ||
+ (m != ps_global->mail_stream &&
+ !(force_arg && sp_flagged(m, SP_LOCKED))))
+ continue;
+
+ /*
+ * This for() loop is only the current folder, unless the user
+ * has forced the check, in which case it is all locked folders.
+ */
+
+ /*
+ * After some amount of inactivity on a stream, the server may
+ * close the stream. Each protocol has its own idea of how much
+ * inactivity should be allowed before the stream is closed. For
+ * example, IMAP specifies that the server should not close the
+ * stream unilaterally until at least 30 minutes of inactivity.
+ * The GET_IDLETIMEOUT call gives us that time in minutes. We
+ * want to be sure to keep the stream alive if it is about to die
+ * due to inactivity.
+ */
+ adj_idle_timeout = 60 * (long) mail_parameters(m,GET_IDLETIMEOUT,NULL);
+ if(adj_idle_timeout <= 0)
+ adj_idle_timeout = 600;
+
+ adj = (adj_idle_timeout >= 50 * FUDGE) ? 5 * FUDGE :
+ (adj_idle_timeout >= 30 * FUDGE) ? 3 * FUDGE :
+ (adj_idle_timeout >= 20 * FUDGE) ? 2 * FUDGE : FUDGE;
+ adj_idle_timeout = MAX(adj_idle_timeout - adj, 120);
+
+ /*
+ * Set interval to mail-check-interval unless
+ * mail-check-interval-noncurrent is nonzero and this is not inbox
+ * or current stream.
+ */
+ if(ps_global->check_interval_for_noncurr > 0
+ && m != ps_global->mail_stream
+ && !sp_flagged(m, SP_INBOX))
+ interval = ps_global->check_interval_for_noncurr;
+ else
+ interval = timeo;
+
+ /*
+ * We want to make sure that we notice expunges, but we don't have
+ * to be fanatical about it. Every once in a while we'll do a ping
+ * because we haven't had a command that notices expunges for a
+ * while. It's also a larger number than interval so it gives us a
+ * convenient interval to do slower pinging than interval if we
+ * are busy.
+ */
+ if(interval <= adj_idle_timeout)
+ expunged_reaper_to = MIN(MAX(2*interval,180), adj_idle_timeout);
+ else
+ expunged_reaper_to = interval;
+
+ /*
+ * User may want to avoid new mail checking while composing.
+ * In this case we will only do the keepalives.
+ */
+ if(timeo == 0
+ || (flags & NM_FROM_COMPOSER
+ && ((F_ON(F_QUELL_PINGS_COMPOSING, ps_global)
+ && !sp_flagged(m, SP_INBOX))
+ ||
+ (F_ON(F_QUELL_PINGS_COMPOSING_INBOX, ps_global)
+ && sp_flagged(m, SP_INBOX))))){
+ interval = expunged_reaper_to = 0;
+ }
+
+ dprint((9,
+ "%s: force=%d interval=%ld exp_reap_to=%ld adj_idle_to=%ld\n",
+ STREAMNAME(m), force_arg, (long) interval,
+ (long) expunged_reaper_to, (long) adj_idle_timeout));
+ dprint((9,
+ " since_last_ping=%ld since_last_reap=%ld\n",
+ (long) (now - sp_last_ping(m)),
+ (long) (now - sp_last_expunged_reaper(m))));
+
+ /* if check_point does a check it resets last_ping time */
+ if(force_arg || (timeo && expunged_reaper_to > 0))
+ (void) check_point(m, time_for_check_point, flags);
+
+ /*
+ * Remember that unless force_arg is set, this check is really
+ * only for the current folder. This is usually going to fire
+ * on the first test, which is the interval the user set.
+ */
+ if(force_arg
+ ||
+ (timeo
+ &&
+ ((interval && (now - sp_last_ping(m) >= interval-1))
+ ||
+ (expunged_reaper_to
+ && (now - sp_last_expunged_reaper(m)) >= expunged_reaper_to)
+ ||
+ (now - sp_last_ping(m) >= adj_idle_timeout)))){
+
+ if((flags & NM_STATUS_MSG) && pith_opt_newmail_check_cue)
+ (*pith_opt_newmail_check_cue)(TRUE);
+
+ dprint((7, "Mail_Ping(%s): lastping=%ld er=%ld%s%s %s\n",
+ STREAMNAME(m),
+ (long) (now - sp_last_ping(m)),
+ (long) (now - sp_last_expunged_reaper(m)),
+ force_arg ? " [forced]" :
+ interval && (now-sp_last_ping(m) >= interval-1)
+ ? " [it's time]" :
+ expunged_reaper_to
+ && (now - sp_last_expunged_reaper(m) >= expunged_reaper_to)
+ ? " [expunged reaper]"
+ : " [keepalive]",
+ m == ps_global->mail_stream ? " [current]" : "",
+ debug_time(0,1)));
+
+ /*
+ * We're about to ping the stream.
+ * If the stream is a #move Mail Drop there is a minimum time
+ * between re-opens of the mail drop to check for new mail.
+ * If the check is forced by the user, they want it to
+ * happen now. We use knowledge of c-client internals to
+ * make this happen.
+ */
+ if(force_arg && m && m->snarf.name)
+ m->snarf.time = 0;
+
+ /*-- Ping the stream to check for new mail --*/
+ if(sp_dead_stream(m)){
+ dprint((6, "no check: stream is dead\n"));
+ }
+ else if(!pine_mail_ping(m)){
+ dprint((6, "ping failed: stream is dead\n"));
+ sp_set_dead_stream(m, 1);
+ }
+
+ dprint((7, "Ping complete: %s\n", debug_time(0,1)));
+
+ if((flags & NM_STATUS_MSG) && pith_opt_newmail_check_cue)
+ (*pith_opt_newmail_check_cue)(FALSE);
+ }
+ }
+
+ if(nexttime < 0 || nexttime >= ps_global->s_pool.nstream)
+ nexttime = 0;
+
+ i = started_on = nexttime;
+ do {
+ m = ps_global->s_pool.streams[i];
+
+ nexttime = i;
+ if(ps_global->s_pool.nstream > 0)
+ i = (i + 1) % ps_global->s_pool.nstream;
+
+ /*
+ * This do() loop handles all the other streams that weren't covered
+ * in the for() loop above.
+ */
+ if((!m || m->halfopen) ||
+ (m == ps_global->mail_stream ||
+ (force_arg && sp_flagged(m, SP_LOCKED)))){
+ nexttime = i;
+ continue;
+ }
+
+ /*
+ * If it is taking an extra long time to do the pings and checks,
+ * think about skipping some of them. Always do at least one of
+ * these non-special streams (if they are due to be pinged).
+ * The nexttime variable keeps track of where we were so that we
+ * don't always ping the first one in the list and then skip out.
+ */
+ if((time(0) - now >= 5) && have_pinged_non_special){
+ dprint((7, "skipping pings due to delay: %s\n",
+ debug_time(0,1)));
+ break;
+ }
+
+ nexttime = i;
+ have_pinged_non_special++;
+
+ adj_idle_timeout = 60 * (long) mail_parameters(m,GET_IDLETIMEOUT,NULL);
+ if(adj_idle_timeout <= 0)
+ adj_idle_timeout = 600;
+
+ adj = (adj_idle_timeout >= 50 * FUDGE) ? 5 * FUDGE :
+ (adj_idle_timeout >= 30 * FUDGE) ? 3 * FUDGE :
+ (adj_idle_timeout >= 20 * FUDGE) ? 2 * FUDGE : FUDGE;
+ adj_idle_timeout = MAX(adj_idle_timeout - adj, 120);
+
+ if(ps_global->check_interval_for_noncurr > 0
+ && m != ps_global->mail_stream
+ && !sp_flagged(m, SP_INBOX))
+ interval = ps_global->check_interval_for_noncurr;
+ else
+ interval = timeo;
+
+ if(interval <= adj_idle_timeout)
+ expunged_reaper_to = MIN(MAX(2*interval,180), adj_idle_timeout);
+ else
+ expunged_reaper_to = interval;
+
+ if(timeo == 0
+ || (flags & NM_FROM_COMPOSER
+ && ((F_ON(F_QUELL_PINGS_COMPOSING, ps_global)
+ && !sp_flagged(m, SP_INBOX))
+ ||
+ (F_ON(F_QUELL_PINGS_COMPOSING_INBOX, ps_global)
+ && sp_flagged(m, SP_INBOX))))){
+ interval = expunged_reaper_to = 0;
+ }
+
+ dprint((9,
+ "%s: force=%d interval=%ld exp_reap_to=%ld adj_idle_to=%ld\n",
+ STREAMNAME(m), force_arg, (long) interval,
+ (long) expunged_reaper_to, (long) adj_idle_timeout));
+ dprint((9,
+ " since_last_ping=%ld since_last_reap=%ld since_last_input=%ld\n",
+ (long) (now - sp_last_ping(m)),
+ (long) (now - sp_last_expunged_reaper(m)),
+ (long) since_last_input));
+
+ /* if check_point does a check it resets last_ping time */
+ if(force_arg || (timeo && expunged_reaper_to > 0))
+ (void) check_point(m, time_for_check_point, flags);
+
+
+ /*
+ * The check here is a little bit different from the current folder
+ * check in the for() loop above. In particular, we defer our
+ * pinging for awhile if the last input was recent (except for the
+ * inbox!). We ping streams which are cached but not actively being
+ * used (that is, the non-locked streams) at a slower rate.
+ * If we don't use them for a long time we will eventually close them
+ * (in maybe_kill_old_stream()) but we do it when we want to instead
+ * of when the server wants us to by attempting to keep it alive here.
+ * The other reason to ping the cached streams is that we only get
+ * told the new mail in those streams is recent one time, the messages
+ * that weren't handled here will no longer be recent next time
+ * we open the folder.
+ */
+ if((force_arg && sp_flagged(m, SP_LOCKED))
+ ||
+ (timeo
+ &&
+ ((interval
+ && sp_flagged(m, SP_LOCKED)
+ && ((since_last_input >= 3
+ && (now-sp_last_ping(m) >= interval-1))
+ || (sp_flagged(m, SP_INBOX)
+ && (now-sp_last_ping(m) >= interval-1))))
+ ||
+ (expunged_reaper_to
+ && sp_flagged(m, SP_LOCKED)
+ && (now-sp_last_expunged_reaper(m) >= expunged_reaper_to))
+ ||
+ (expunged_reaper_to
+ && !sp_flagged(m, SP_LOCKED)
+ && since_last_input >= 3
+ && (now-sp_last_ping(m) >= expunged_reaper_to))
+ ||
+ (now - sp_last_ping(m) >= adj_idle_timeout)))){
+
+ if((flags & NM_STATUS_MSG) && pith_opt_newmail_check_cue)
+ (*pith_opt_newmail_check_cue)(TRUE);
+
+ dprint((7,
+ "Mail_Ping(%s): lastping=%ld er=%ld%s idle: %ld %s\n",
+ STREAMNAME(m),
+ (long) (now - sp_last_ping(m)),
+ (long) (now - sp_last_expunged_reaper(m)),
+ (force_arg && sp_flagged(m, SP_LOCKED)) ? " [forced]" :
+ (interval
+ && sp_flagged(m, SP_LOCKED)
+ && ((since_last_input >= 3
+ && (now-sp_last_ping(m) >= interval-1))
+ || (sp_flagged(m, SP_INBOX)
+ && (now-sp_last_ping(m) >= interval-1))))
+ ? " [it's time]" :
+ (expunged_reaper_to
+ && sp_flagged(m, SP_LOCKED)
+ && (now-sp_last_expunged_reaper(m) >= expunged_reaper_to))
+ ? " [expunged reaper]" :
+ (expunged_reaper_to
+ && !sp_flagged(m, SP_LOCKED)
+ && since_last_input >= 3
+ && (now-sp_last_ping(m) >= expunged_reaper_to))
+ ? " [slow ping]" : " [keepalive]",
+ since_last_input,
+ debug_time(0,1)));
+
+ /*
+ * We're about to ping the stream.
+ * If the stream is a #move Mail Drop there is a minimum time
+ * between re-opens of the mail drop to check for new mail.
+ * If the check is forced by the user, they want it to
+ * happen now. We use knowledge of c-client internals to
+ * make this happen.
+ */
+ if(force_arg && m && m->snarf.name)
+ m->snarf.time = 0;
+
+ /*-- Ping the stream to check for new mail --*/
+ if(sp_dead_stream(m)){
+ dprint((6, "no check: stream is dead\n"));
+ }
+ else if(!pine_mail_ping(m)){
+ dprint((6, "ping failed: stream is dead\n"));
+ sp_set_dead_stream(m, 1);
+ }
+
+ dprint((7, "Ping complete: %s\n", debug_time(0,1)));
+
+ if((flags & NM_STATUS_MSG) && pith_opt_newmail_check_cue)
+ (*pith_opt_newmail_check_cue)(FALSE);
+ }
+ } while(i != started_on);
+
+ /*
+ * Current mail box state changed, could be additions or deletions.
+ * Also check if we need to do sorting that has been deferred.
+ * We handle the current stream separately from the rest in the
+ * similar loop that follows this paragraph.
+ */
+ m = ps_global->mail_stream;
+ if(sp_mail_box_changed(m) || sp_unsorted_newmail(m)
+ || sp_need_to_rethread(m)){
+ dprint((7,
+ "Cur new mail, %s, new_mail_count: %ld expunge: %ld, max_msgno: %ld\n",
+ (m && m->mailbox) ? m->mailbox : "?",
+ sp_new_mail_count(m),
+ sp_expunge_count(m),
+ mn_get_total(sp_msgmap(m))));
+
+ new_mail_was_announced = 0;
+ if(sp_mail_box_changed(m))
+ fixup_flags(m, sp_msgmap(m));
+
+ if(sp_new_mail_count(m))
+ process_filter_patterns(m, sp_msgmap(m), sp_new_mail_count(m));
+
+ /* worry about sorting */
+ if((sp_new_mail_count(m) > 0L
+ || sp_unsorted_newmail(m)
+ || sp_need_to_rethread(m))
+ && !((flags & NM_DEFER_SORT)
+ || any_lflagged(sp_msgmap(m), MN_HIDE)))
+ refresh_sort(m, sp_msgmap(m),
+ (flags & NM_STATUS_MSG) ? SRT_VRB : SRT_NON);
+ else if(sp_new_mail_count(m) > 0L)
+ sp_set_unsorted_newmail(m, 1);
+
+ if(sp_new_mail_count(m) > 0L){
+ sp_set_mail_since_cmd(m, sp_mail_since_cmd(m)+sp_new_mail_count(m));
+ rv += (t_nm_count = sp_new_mail_count(m));
+ sp_set_new_mail_count(m, 0L);
+
+ if((flags & NM_STATUS_MSG) && pith_opt_newmail_announce){
+ for(n = m->nmsgs; n > 1L; n--)
+ if(!get_lflag(m, NULL, n, MN_EXLD))
+ break;
+
+ (*pith_opt_newmail_announce)(m, n, t_nm_count);
+
+ if(n)
+ new_mail_was_announced++;
+ }
+ }
+
+ update_folder_unseen_by_stream(m, new_mail_was_announced ? UFU_NONE : UFU_ANNOUNCE);
+
+ if(flags & NM_STATUS_MSG)
+ sp_set_mail_box_changed(m, 0);
+ }
+
+ /* the rest of the streams */
+ for(i = 0; i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ if(!m || m == ps_global->mail_stream)
+ continue;
+
+ if(sp_mail_box_changed(m)){
+ /*-- New mail for this stream, queue up the notification --*/
+ dprint((7,
+ "New mail, %s, new_mail_count: %ld expunge: %ld, max_msgno: %ld\n",
+ (m && m->mailbox) ? m->mailbox : "?",
+ sp_new_mail_count(m),
+ sp_expunge_count(m),
+ mn_get_total(sp_msgmap(m))));
+
+ new_mail_was_announced = 0;
+ fixup_flags(m, sp_msgmap(m));
+
+ if(sp_new_mail_count(m))
+ process_filter_patterns(m, sp_msgmap(m), sp_new_mail_count(m));
+
+ if(sp_new_mail_count(m) > 0){
+ sp_set_unsorted_newmail(m, 1);
+ sp_set_mail_since_cmd(m, sp_mail_since_cmd(m) +
+ sp_new_mail_count(m));
+ if(sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR))
+ rv += (t_nm_count = sp_new_mail_count(m));
+
+ sp_set_new_mail_count(m, 0L);
+
+ /* messages only for user's streams */
+ if(flags & NM_STATUS_MSG
+ && pith_opt_newmail_announce
+ && sp_flagged(m, SP_LOCKED)
+ && sp_flagged(m, SP_USERFLDR)){
+ for(n = m->nmsgs; n > 1L; n--)
+ if(!get_lflag(m, NULL, n, MN_EXLD))
+ break;
+
+ (*pith_opt_newmail_announce)(m, n, t_nm_count);
+
+ if(n)
+ new_mail_was_announced++;
+ }
+ }
+
+ update_folder_unseen_by_stream(m, new_mail_was_announced ? UFU_NONE : UFU_ANNOUNCE);
+
+ if(flags & NM_STATUS_MSG)
+ sp_set_mail_box_changed(m, 0);
+ }
+ }
+
+ /* so quit_screen can tell new mail from expunged mail */
+ exp_count = sp_expunge_count(ps_global->mail_stream);
+
+ /* see if we want to kill any cached streams */
+ for(i = 0; i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ if(sp_last_ping(m) >= now)
+ maybe_kill_old_stream(m);
+ }
+
+ /*
+ * This is here to prevent banging on the down arrow key (or holding
+ * it down and repeating) at the end of the index from causing
+ * a whole bunch of new mail checks. Last_nextitem_forcechk does get
+ * set at the place it is tested in mailcmd.c, but this is here to
+ * reset it to a little bit later in case it takes a second or more
+ * to check for the mail. If we didn't do this, we'd just check every
+ * keystroke as long as the checks took more than a second.
+ */
+ if(force_arg)
+ ps_global->last_nextitem_forcechk = time(0);
+
+ dprint((6, "******** new mail returning %ld ********\n",
+ rv ? rv : (exp_count ? 0 : -1)));
+ return(rv ? rv : (exp_count ? 0 : -1));
+}
+
+
+/*
+ * format_new_mail_msg - actual work of generating intro,
+ * from, subject and expanded subject
+ */
+void
+format_new_mail_msg(char *folder, long int number, ENVELOPE *e,
+ char *intro, char *from, char *subj, char *subjex,
+ size_t buflen) /* min length of each of the 4 above if they're non-null */
+{
+ char *p, tmp[MAILTMPLEN+1], subj_leadin[MAILTMPLEN];
+ static char *carray[] = { "regarding",
+ "concerning",
+ "about",
+ "as to",
+ "as regards",
+ "as respects",
+ "in re",
+ "re",
+ "respecting",
+ "in point of",
+ "with regard to",
+ "subject:"
+ };
+
+ if(buflen > 0){
+ if(intro)
+ intro[0] = '\0';
+
+ if(from)
+ from[0] = '\0';
+
+ if(subj)
+ subj[0] = '\0';
+
+ if(subjex)
+ subjex[0] = '\0';
+ }
+
+ if(from && e && e->from){
+ if(e->from->personal && e->from->personal[0]){
+ /*
+ * The reason we use so many characters for tmp is because we
+ * may have multiple MIME3 chunks and we don't want to truncate
+ * in the middle of one of them before decoding.
+ */
+ snprintf(tmp, sizeof(tmp), "%s", e->from->personal);
+ p = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf, SIZEOF_20KBUF, tmp);
+ removing_leading_and_trailing_white_space(p);
+ if(*p)
+ snprintf(from, buflen, "%.200s", p);
+ }
+
+ if(!from[0]){
+ snprintf(tmp, sizeof(tmp), "%s%s%s",
+ e->from->mailbox,
+ e->from->host ? "@" : "",
+ e->from->host ? e->from->host : "");
+ snprintf(from, buflen, "%.200s", tmp);
+ }
+ }
+
+ if(number <= 1L){
+ if(e && e->subject && e->subject[0]){
+ snprintf(tmp, sizeof(tmp), "%s", e->subject);
+ p = (char *) rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, tmp);
+ if(subj)
+ snprintf(subj, buflen, "%.200s", p);
+ }
+
+ snprintf(subj_leadin, sizeof(subj_leadin), " %s ", carray[(unsigned)random()%12]);
+ if(!(from && from[0]))
+ subj_leadin[1] = toupper((unsigned char)subj_leadin[1]);
+ }
+
+ if(subj && subjex){
+ if(subj[0]){
+ snprintf(subjex, buflen, "%s%.200s%s",
+ (number <= 1L) ? (subj[0] ? subj_leadin : "")
+ : "",
+ (number <= 1L) ? (subj[0] ? subj
+ : from ? " w" : " W")
+ : "",
+ (number <= 1L) ? (subj[0] ? "" : "ith no subject")
+ : "");
+ }
+ else
+ subj[0] = subjex[0] = '\0';
+ }
+
+ if(intro){
+ if(!folder){
+ if(number > 1)
+ /* TRANSLATORS: The argument is the number of new messages */
+ snprintf(intro, buflen, _("%ld new messages!"), number);
+ else
+ /* TRANSLATORS: The argument is either " to you" or nothing */
+ snprintf(intro, buflen, _("New mail%s!"),
+ (e && address_is_us(e->to, ps_global)) ? _(" to you") : "");
+ }
+ else {
+ long fl, tot, newfl;
+ char *fname = folder ? (char *) folder_name_decoded((unsigned char *) folder) : "";
+
+ if(number > 1)
+ snprintf(intro, buflen, _("%ld messages saved to folder \"%.80s\""),
+ number, fname);
+ else
+ snprintf(intro, buflen, _("Mail saved to folder \"%.80s\""), fname);
+
+ if((fl=utf8_width(fname)) > 10 &&
+ (tot=utf8_width(intro) + utf8_width(from ? from : "") + utf8_width(subj ? subj : "")) >
+ ps_global->ttyo->screen_cols - 2){
+ char *f = fs_get((strlen(fname) + 1)*sizeof(char));
+ newfl = MAX(10, fl-(tot-(ps_global->ttyo->screen_cols - 2)));
+ utf8_to_width_rhs(f, fname, strlen(fname) + 1, newfl-3);
+ if(number > 1)
+ snprintf(intro, buflen, _("%ld messages saved to folder \"...%.80s\""), number, f);
+ else
+ snprintf(intro, buflen, _("Mail saved to folder \"...%.80s\""), f);
+ if(f) fs_give((void **)&f);
+ }
+
+ if (fname && *fname)
+ fs_give((void **)&fname);
+ }
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Straighten out any local flag problems here. We can't take care of
+ them in the mm_exists or mm_expunged callbacks since the flags
+ themselves are in an MESSAGECACHE and we're not allowed to reenter
+ c-client from a callback...
+
+ Args: stream -- mail stream to operate on
+ msgmap -- messages in that stream to fix up
+
+ Result: returns with local flags as they should be
+
+ ----*/
+void
+fixup_flags(MAILSTREAM *stream, MSGNO_S *msgmap)
+{
+ /*
+ * Deal with the case where expunged away all of the
+ * zoomed messages. Unhide everything in that case...
+ */
+ if(mn_get_total(msgmap) > 0L){
+ long i;
+
+ if(any_lflagged(msgmap, MN_HIDE) >= mn_get_total(msgmap)){
+ for(i = 1L; i <= mn_get_total(msgmap); i++)
+ set_lflag(stream, msgmap, i, MN_HIDE, 0);
+
+ mn_set_cur(msgmap, THREADING()
+ ? first_sorted_flagged(F_NONE, stream, 0L,
+ (THREADING() ? 0 : FSF_SKIP_CHID)
+ | FSF_LAST)
+ : mn_get_total(msgmap));
+ }
+ else if(any_lflagged(msgmap, MN_HIDE)){
+ /*
+ * if we got here, there are some hidden messages and
+ * some not. Make sure the current message is one
+ * that's not...
+ */
+ for(i = mn_get_cur(msgmap); i <= mn_get_total(msgmap); i++)
+ if(!msgline_hidden(stream, msgmap, i, 0)){
+ mn_set_cur(msgmap, i);
+ break;
+ }
+
+ for(i = mn_get_cur(msgmap); i > 0L; i--)
+ if(!msgline_hidden(stream, msgmap, i, 0)){
+ mn_set_cur(msgmap, i);
+ break;
+ }
+ }
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Force write of the main file so user doesn't lose too much when
+ something bad happens. The only thing that can get lost is flags, such
+ as when new mail arrives, is read, deleted or answered.
+
+ Args: timing -- indicates if it's a good time for to do a checkpoint
+
+ Result: returns 1 if checkpoint was written,
+ 0 if not.
+
+NOTE: mail_check will also notice new mail arrival, so it's imperative that
+code exist after this function is called that can deal with updating the
+various pieces of pine's state associated with the message count and such.
+
+Only need to checkpoint current stream because there are no changes going
+on with other streams when we're not manipulating them.
+ ----*/
+
+int
+check_point(MAILSTREAM *stream, CheckPointTime timing, int flags)
+{
+ int freq, tm, check_count, tst1 = 0, tst2 = 0, accel;
+ long since_last_input, since_first_change;
+ time_t now, then;
+#define AT_LEAST (180)
+#define MIN_LONG_CHK_IDLE (10)
+
+ if(!stream || stream->rdonly || stream->halfopen
+ || (check_count = sp_check_cnt(stream)) == 0)
+ return(0);
+
+ since_last_input = (long) time(0) - (long) time_of_last_input();
+
+ if(timing == GoodTime && since_last_input <= 0)
+ timing = VeryBadTime;
+ else if(timing == GoodTime && since_last_input <= 4)
+ timing = BadTime;
+
+ dprint((9, "check_point(%s: %s)\n",
+ timing == GoodTime ? "GoodTime" :
+ timing == BadTime ? "BadTime" :
+ timing == VeryBadTime ? "VeryBadTime" : "DoItNow",
+ STREAMNAME(stream)));
+
+ freq = CHECK_POINT_FREQ * (timing==GoodTime ? 1 : timing==BadTime ? 3 : 4);
+ tm = CHECK_POINT_TIME * (timing==GoodTime ? 2 : timing==BadTime ? 4 : 6);
+ tm = MAX(100, tm);
+
+ if(timing == DoItNow){
+ dprint((9, "DoItNow\n"));
+ }
+ else{
+ since_first_change = (long) (time(0) - sp_first_status_change(stream));
+ accel = MIN(3, MAX(1, (4 * since_first_change)/tm));
+ tst1 = ((check_count * since_last_input) >= (30/accel) * freq);
+ tst2 = ((since_first_change >= tm)
+ && (since_last_input >= MIN_LONG_CHK_IDLE));
+ dprint((9,
+ "Chk changes(%d) x since_last_input(%ld) (=%ld) >= freq(%d) x 30/%d (=%d) ? %s\n",
+ check_count, since_last_input,
+ check_count * since_last_input, freq, accel, (30/accel)*freq,
+ tst1 ? "Yes" : "No"));
+
+ dprint((9,
+ " or since_1st_change(%ld) >= tm(%d) && since_last_input >= %d ? %s\n",
+ since_first_change, tm, MIN_LONG_CHK_IDLE,
+ tst2 ? "Yes" : "No"));
+ }
+
+ if(timing == DoItNow || tst1 || tst2){
+
+ if(timing == DoItNow
+ || time(0) - sp_last_chkpnt_done(stream) >= AT_LEAST){
+ then = time(0);
+ dprint((2, "Checkpoint: %s Since 1st change: %ld secs idle: %ld secs\n",
+ debug_time(0,1),
+ (long) (then - sp_first_status_change(stream)),
+ since_last_input))
+;
+ if((flags & NM_STATUS_MSG) && pith_opt_checkpoint_cue)
+ (*pith_opt_checkpoint_cue)(TRUE);
+
+ pine_mail_check(stream); /* write file state */
+
+ now = time(0);
+ dprint((2,
+ "Checkpoint complete: %s%s%s%s\n",
+ debug_time(0,1),
+ (now-then > 0) ? " (elapsed: " : "",
+ (now-then > 0) ? comatose((long)(now-then)) : "",
+ (now-then > 0) ? " secs)" : ""));
+
+ if((flags & NM_STATUS_MSG) && pith_opt_checkpoint_cue)
+ (*pith_opt_checkpoint_cue)(FALSE);
+
+ return(1);
+ }
+ else{
+ dprint((9,
+ "Skipping checkpoint since last was only %ld secs ago (< %d)\n",
+ (long) (time(0) - sp_last_chkpnt_done(stream)), AT_LEAST));
+ }
+ }
+
+ return(0);
+}
+
+
+/*----------------------------------------------------------------------
+ Call this when we need to tell the check pointing mechanism about
+ mailbox state changes.
+ ----------------------------------------------------------------------*/
+void
+check_point_change(MAILSTREAM *stream)
+{
+ if(!sp_check_cnt(stream))
+ sp_set_first_status_change(stream, time(0));
+
+ sp_set_check_cnt(stream, sp_check_cnt(stream)+1);
+ dprint((9, "check_point_change(%s): increment to %d\n",
+ STREAMNAME(stream), sp_check_cnt(stream)));
+}
+
+
+
+/*----------------------------------------------------------------------
+ Call this when a mail file is written to reset timer and counter
+ for next check_point.
+ ----------------------------------------------------------------------*/
+void
+reset_check_point(MAILSTREAM *stream)
+{
+ time_t now;
+
+ now = time(0);
+
+ sp_set_check_cnt(stream, 0);
+ sp_set_first_status_change(stream, 0);
+ sp_set_last_chkpnt_done(stream, now);
+ sp_set_last_ping(stream, now);
+ sp_set_last_expunged_reaper(stream, now);
+}
+
+
+int
+changes_to_checkpoint(MAILSTREAM *stream)
+{
+ return(sp_check_cnt(stream) > 0);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Zero the counters that keep track of mail accumulated between
+ commands.
+ ----*/
+void
+zero_new_mail_count(void)
+{
+ int i;
+ MAILSTREAM *m;
+
+ dprint((9, "New_mail_count zeroed\n"));
+
+ for(i = 0; i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR)
+ && sp_mail_since_cmd(m)){
+ if(pith_opt_icon_text)
+ (*pith_opt_icon_text)(NULL, IT_NEWMAIL);
+
+ break;
+ }
+ }
+
+ for(i = 0; i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ if(m && sp_flagged(m, SP_LOCKED))
+ sp_set_mail_since_cmd(m, 0L);
+ }
+}
diff --git a/pith/newmail.h b/pith/newmail.h
new file mode 100644
index 00000000..4ee6c982
--- /dev/null
+++ b/pith/newmail.h
@@ -0,0 +1,59 @@
+/*
+ * $Id: newmail.h 807 2007-11-09 01:21:33Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_NEWMAIL_INCLUDED
+#define PITH_NEWMAIL_INCLUDED
+
+
+typedef enum {GoodTime = 0, BadTime, VeryBadTime, DoItNow} CheckPointTime;
+
+
+/*
+ * Macro to help with new mail check timing...
+ */
+#define NM_TIMING(X) (((X)==NO_OP_IDLE) ? GoodTime : \
+ (((X)==NO_OP_COMMAND) ? BadTime : VeryBadTime))
+#define NM_NONE 0x00
+#define NM_STATUS_MSG 0x01
+#define NM_DEFER_SORT 0x02
+#define NM_FROM_COMPOSER 0x04
+
+
+/*
+ * Icon text types, to tell which icon to use
+ */
+#define IT_NEWMAIL 0
+#define IT_MCLOSED 1
+
+
+/* exported protoypes */
+long new_mail(int, CheckPointTime, int);
+void format_new_mail_msg(char *, long, ENVELOPE *, char *, char *, char *, char *, size_t);
+void check_point_change(MAILSTREAM *);
+void reset_check_point(MAILSTREAM *);
+int changes_to_checkpoint(MAILSTREAM *);
+void zero_new_mail_count(void);
+void init_newmailfifo(char *);
+void close_newmailfifo(void);
+
+
+/* mandatory to implement prototypes */
+void newmail_check_cue(int);
+void newmail_check_point_cue(int);
+void icon_text(char *, int);
+time_t time_of_last_input(void);
+
+
+#endif /* PITH_NEWMAIL_INCLUDED */
diff --git a/pith/news.c b/pith/news.c
new file mode 100644
index 00000000..c0077c2c
--- /dev/null
+++ b/pith/news.c
@@ -0,0 +1,459 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: news.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/news.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/context.h"
+#include "../pith/stream.h"
+#include "../pith/util.h"
+
+
+typedef enum {NotChecked, NotInCache, Found, Missing, End} NgCacheReturns;
+
+
+/*
+ * Internal prototypes
+ */
+NgCacheReturns chk_newsgrp_cache(char *);
+void add_newsgrp_cache(char *, NgCacheReturns);
+
+
+/*----------------------------------------------------------------------
+ Function to see if a given MAILSTREAM mailbox is in the news namespace
+
+ Input: stream -- mail stream to test
+
+ Result:
+ ----*/
+int
+ns_test(char *mailbox, char *namespace)
+{
+ if(mailbox){
+ switch(*mailbox){
+ case '#' :
+ return(!struncmp(mailbox + 1, namespace, strlen(namespace)));
+
+ case '{' :
+ {
+ NETMBX mbx;
+
+ if(mail_valid_net_parse(mailbox, &mbx))
+ return(ns_test(mbx.mailbox, namespace));
+ }
+
+ break;
+
+ default :
+ break;
+ }
+ }
+
+ return(0);
+}
+
+
+int
+news_in_folders(struct variable *var)
+{
+ int i, found_news = 0;
+ CONTEXT_S *tc;
+
+ if(!(var && var->current_val.l))
+ return(found_news);
+
+ for(i=0; !found_news && var->current_val.l[i]; i++){
+ if((tc = new_context(var->current_val.l[i], NULL)) != NULL){
+ if(tc->use & CNTXT_NEWS)
+ found_news++;
+
+ free_context(&tc);
+ }
+ }
+
+ return(found_news);
+}
+
+
+/*----------------------------------------------------------------------
+ Verify and canonicalize news groups names.
+ Called from the message composer
+
+Args: given_group -- List of groups typed by user
+ expanded_group -- pointer to point to expanded list, which will be
+ allocated here and freed in caller. If this is
+ NULL, don't attempt to validate.
+ error -- pointer to store error message
+ fcc -- pointer to point to fcc, which will be
+ allocated here and freed in caller
+
+Returns: 0 if all is OK
+ -1 if addresses weren't valid
+
+Test the given list of newstroups against those recognized by our nntp
+servers. Testing by actually trying to open the list is much cheaper, both
+in bandwidth and memory, than yanking the whole list across the wire.
+ ----*/
+int
+news_grouper(char *given_group, char **expanded_group, char **error,
+ char **fccptr, void (*delay_warning)(void))
+{
+ char ng_error[90], *p1, *p2, *name, *end, *ep, **server,
+ ng_ref[MAILTMPLEN];
+ int expanded_len = 0, num_in_error = 0, cnt_errs;
+
+ MAILSTREAM *stream = NULL;
+ struct ng_list {
+ char *groupname;
+ NgCacheReturns found;
+ struct ng_list *next;
+ }*nglist = NULL, **ntmpp, *ntmp;
+#ifdef SENDNEWS
+ static int no_servers = 0;
+#endif
+
+ dprint((5,
+ "- news_build - (%s)\n", given_group ? given_group : "nul"));
+
+ if(error)
+ *error = NULL;
+
+ ng_ref[0] = '\0';
+
+ /*------ parse given entries into a list ----*/
+ ntmpp = &nglist;
+ for(name = given_group; *name; name = end){
+
+ /* find start of next group name */
+ while(*name && (isspace((unsigned char)*name) || *name == ','))
+ name++;
+
+ /* find end of group name */
+ end = name;
+ while(*end && !isspace((unsigned char)*end) && *end != ',')
+ end++;
+
+ if(end != name){
+ *ntmpp = (struct ng_list *)fs_get(sizeof(struct ng_list));
+ (*ntmpp)->next = NULL;
+ (*ntmpp)->found = NotChecked;
+ (*ntmpp)->groupname = fs_get(end - name + 1);
+ strncpy((*ntmpp)->groupname, name, end - name);
+ (*ntmpp)->groupname[end - name] = '\0';
+ ntmpp = &(*ntmpp)->next;
+ if(!expanded_group)
+ break; /* no need to continue if just doing fcc */
+ }
+ }
+
+ /*
+ * If fcc is not set or is set to default, then replace it if
+ * one of the recipient rules is in effect.
+ */
+ if(fccptr){
+ if((ps_global->fcc_rule == FCC_RULE_RECIP ||
+ ps_global->fcc_rule == FCC_RULE_NICK_RECIP) &&
+ (nglist && nglist->groupname)){
+ if(*fccptr)
+ fs_give((void **) fccptr);
+
+ *fccptr = cpystr(nglist->groupname);
+ }
+ else if(!*fccptr) /* already default otherwise */
+ *fccptr = cpystr(ps_global->VAR_DEFAULT_FCC);
+ }
+
+ if(!nglist){
+ if(expanded_group)
+ *expanded_group = cpystr("");
+ return 0;
+ }
+
+ if(!expanded_group)
+ return 0;
+
+#ifdef DEBUG
+ for(ntmp = nglist; debug >= 9 && ntmp; ntmp = ntmp->next)
+ dprint((9, "Parsed group: --[%s]--\n",
+ ntmp->groupname ? ntmp->groupname : "?"));
+#endif
+
+ /* If we are doing validation */
+ if(F_OFF(F_NO_NEWS_VALIDATION, ps_global)){
+ int need_to_talk_to_server = 0;
+
+ /*
+ * First check our cache of validated newsgroups to see if we even
+ * have to open a stream.
+ */
+ for(ntmp = nglist; ntmp; ntmp = ntmp->next){
+ ntmp->found = chk_newsgrp_cache(ntmp->groupname);
+ if(ntmp->found == NotInCache)
+ need_to_talk_to_server++;
+ }
+
+ if(need_to_talk_to_server){
+
+#ifdef SENDNEWS
+ if(no_servers == 0)
+#endif
+ if(delay_warning)
+ (*delay_warning)();
+
+ /*
+ * Build a stream to the first server that'll talk to us...
+ */
+ for(server = ps_global->VAR_NNTP_SERVER;
+ server && *server && **server;
+ server++){
+ snprintf(ng_ref, sizeof(ng_ref), "{%.*s/nntp}#news.",
+ sizeof(ng_ref)-30, *server);
+ if((stream = pine_mail_open(stream, ng_ref,
+ OP_HALFOPEN|SP_USEPOOL|SP_TEMPUSE,
+ NULL)) != NULL)
+ break;
+ }
+ if(!server || !stream){
+ if(error)
+#ifdef SENDNEWS
+ {
+ /* don't say this over and over */
+ if(no_servers == 0){
+ if(!server || !*server || !**server)
+ no_servers++;
+
+ *error = cpystr(no_servers
+ /* TRANSLATORS: groups refers to news groups */
+ ? _("Can't validate groups. No servers defined")
+ /* TRANSLATORS: groups refers to news groups */
+ : _("Can't validate groups. No servers responding"));
+ }
+ }
+#else
+ *error = cpystr((!server || !*server || !**server)
+ ? _("No servers defined for posting to newsgroups")
+ /* TRANSLATORS: groups refers to news groups */
+ : _("Can't validate groups. No servers responding"));
+#endif
+ *expanded_group = cpystr(given_group);
+ goto done;
+ }
+ }
+
+ /*
+ * Now, go thru the list, making sure we can at least open each one...
+ */
+ for(server = ps_global->VAR_NNTP_SERVER;
+ server && *server && **server; server++){
+ /*
+ * It's faster and easier right now just to open the stream and
+ * do our own finds than to use the current folder_exists()
+ * interface...
+ */
+ for(ntmp = nglist; ntmp; ntmp = ntmp->next){
+ if(ntmp->found == NotInCache){
+ snprintf(ng_ref, sizeof(ng_ref), "{%.*s/nntp}#news.%.*s",
+ sizeof(ng_ref)/2 - 10, *server,
+ sizeof(ng_ref)/2 - 10, ntmp->groupname);
+ ps_global->noshow_error = 1;
+ stream = pine_mail_open(stream, ng_ref,
+ OP_SILENT|SP_USEPOOL|SP_TEMPUSE,
+ NULL);
+ ps_global->noshow_error = 0;
+ if(stream)
+ add_newsgrp_cache(ntmp->groupname, ntmp->found = Found);
+ }
+
+ }
+
+ if(stream){
+ pine_mail_close(stream);
+ stream = NULL;
+ }
+
+ }
+
+ }
+
+ /* figure length of string for matching groups */
+ for(ntmp = nglist; ntmp; ntmp = ntmp->next){
+ if(ntmp->found == Found || F_ON(F_NO_NEWS_VALIDATION, ps_global))
+ expanded_len += strlen(ntmp->groupname) + 2;
+ else{
+ num_in_error++;
+ if(ntmp->found == NotInCache)
+ add_newsgrp_cache(ntmp->groupname, ntmp->found = Missing);
+ }
+ }
+
+ /*
+ * allocate and write the allowed, and error lists...
+ */
+ p1 = *expanded_group = fs_get((expanded_len + 1) * sizeof(char));
+ if(error && num_in_error){
+ cnt_errs = num_in_error;
+ memset((void *)ng_error, 0, sizeof(ng_error));
+ snprintf(ng_error, sizeof(ng_error), "Unknown news group%s: ", plural(num_in_error));
+ ep = ng_error + strlen(ng_error);
+ }
+ for(ntmp = nglist; ntmp; ntmp = ntmp->next){
+ p2 = ntmp->groupname;
+ if(ntmp->found == Found || F_ON(F_NO_NEWS_VALIDATION, ps_global)){
+ while(*p2)
+ *p1++ = *p2++;
+
+ if(ntmp->next){
+ *p1++ = ',';
+ *p1++ = ' ';
+ }
+ }
+ else if (error){
+ while(*p2 && (ep - ng_error < sizeof(ng_error)-1))
+ *ep++ = *p2++;
+
+ if(--cnt_errs > 0 && (ep - ng_error < sizeof(ng_error)-3)){
+ strncpy(ep, ", ", sizeof(ng_error)-(ep-ng_error));
+ ep += 2;
+ }
+ }
+ }
+
+ *p1 = '\0';
+
+ if(error && num_in_error)
+ *error = cpystr(ng_error);
+
+done:
+ while((ntmp = nglist) != NULL){
+ nglist = nglist->next;
+ fs_give((void **)&ntmp->groupname);
+ fs_give((void **)&ntmp);
+ }
+
+ return(num_in_error ? -1 : 0);
+}
+
+
+typedef struct ng_cache {
+ char *name;
+ NgCacheReturns val;
+}NgCache;
+
+static NgCache *ng_cache_ptr;
+#if defined(DOS) && !defined(_WINDOWS)
+#define MAX_NGCACHE_ENTRIES 15
+#else
+#define MAX_NGCACHE_ENTRIES 40
+#endif
+/*
+ * Simple newsgroup validity cache. Opening a newsgroup to see if it
+ * exists can be very slow on a heavily loaded NNTP server, so we cache
+ * the results.
+ */
+NgCacheReturns
+chk_newsgrp_cache(char *group)
+{
+ register NgCache *ngp;
+
+ for(ngp = ng_cache_ptr; ngp && ngp->name; ngp++){
+ if(strcmp(group, ngp->name) == 0)
+ return(ngp->val);
+ }
+
+ return NotInCache;
+}
+
+
+/*
+ * Add an entry to the newsgroup validity cache.
+ *
+ * LRU entry is the one on the bottom, oldest on the top.
+ * A slot has an entry in it if name is not NULL.
+ */
+void
+add_newsgrp_cache(char *group, NgCacheReturns result)
+{
+ register NgCache *ngp;
+ NgCache save_ngp;
+
+ /* first call, initialize cache */
+ if(!ng_cache_ptr){
+ int i;
+
+ ng_cache_ptr =
+ (NgCache *)fs_get((MAX_NGCACHE_ENTRIES+1)*sizeof(NgCache));
+ for(i = 0; i <= MAX_NGCACHE_ENTRIES; i++){
+ ng_cache_ptr[i].name = NULL;
+ ng_cache_ptr[i].val = NotInCache;
+ }
+ ng_cache_ptr[MAX_NGCACHE_ENTRIES].val = End;
+ }
+
+ if(chk_newsgrp_cache(group) == NotInCache){
+ /* find first empty slot or End */
+ for(ngp = ng_cache_ptr; ngp->name; ngp++)
+ ;/* do nothing */
+ if(ngp->val == End){
+ /*
+ * Cache is full, throw away top entry, move everything up,
+ * and put new entry on the bottom.
+ */
+ ngp = ng_cache_ptr;
+ if(ngp->name) /* just making sure */
+ fs_give((void **)&ngp->name);
+
+ for(; (ngp+1)->name; ngp++){
+ ngp->name = (ngp+1)->name;
+ ngp->val = (ngp+1)->val;
+ }
+ }
+ ngp->name = cpystr(group);
+ ngp->val = result;
+ }
+ else{
+ /*
+ * Move this entry from current location to last to preserve
+ * LRU order.
+ */
+ for(ngp = ng_cache_ptr; ngp && ngp->name; ngp++){
+ if(strcmp(group, ngp->name) == 0) /* found it */
+ break;
+ }
+ save_ngp.name = ngp->name;
+ save_ngp.val = ngp->val;
+ for(; (ngp+1)->name; ngp++){
+ ngp->name = (ngp+1)->name;
+ ngp->val = (ngp+1)->val;
+ }
+ ngp->name = save_ngp.name;
+ ngp->val = save_ngp.val;
+ }
+}
+
+
+void
+free_newsgrp_cache(void)
+{
+ register NgCache *ngp;
+
+ for(ngp = ng_cache_ptr; ngp && ngp->name; ngp++)
+ fs_give((void **)&ngp->name);
+ if(ng_cache_ptr)
+ fs_give((void **)&ng_cache_ptr);
+}
diff --git a/pith/news.h b/pith/news.h
new file mode 100644
index 00000000..95ba4026
--- /dev/null
+++ b/pith/news.h
@@ -0,0 +1,37 @@
+/*
+ * $Id: news.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_NEWS_INCLUDED
+#define PITH_NEWS_INCLUDED
+
+
+#include "../pith/conftype.h"
+
+
+/*
+ * Useful macro to test if current folder is a bboard type (meaning
+ * netnews for now) collection...
+ */
+#define IS_NEWS(S) ((S) ? ns_test((S)->mailbox, "news") : 0)
+
+
+/* exported protoypes */
+int ns_test(char *, char *);
+int news_in_folders(struct variable *);
+int news_grouper(char *, char **, char **, char **, void (*)(void));
+void free_newsgrp_cache(void);
+
+
+#endif /* PITH_NEWS_INCLUDED */
diff --git a/pith/options.h b/pith/options.h
new file mode 100644
index 00000000..a4ed3fd3
--- /dev/null
+++ b/pith/options.h
@@ -0,0 +1,228 @@
+/*
+ * $Id: options.h 101 2006-08-10 22:53:04Z mikes@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_OPTIONS_INCLUDED
+#define PITH_OPTIONS_INCLUDED
+
+/*
+ * function hooks to fill in optional/user-selectable behaviors in pith functions
+ *
+ * You'll find a very brief explanation of what they do here, but you'll need to
+ * look at the places they're called to fully understand how they're intended to
+ * be called and what they're intended to provide.
+ */
+
+
+#include "indxtype.h" /* for ICE_S */
+#include "thread.h" /* for PINETHRD_S */
+#include "handle.h" /* for HANDLE_S */
+#include "filttype.h" /* for gf_io_t */
+#include "state.h" /* for struct pine */
+#include "adrbklib.h" /* for SAVE_STATE_S */
+#ifdef ENABLE_LDAP
+#include "ldap.h"
+#endif
+
+
+/*
+ * optional call in mailindx.c:{from,subject}_str to shrink thread
+ * relationship cue if desired
+ */
+extern int (*pith_opt_condense_thread_cue)(PINETHRD_S *, ICE_S *, char **, size_t *, int, int);
+
+
+/*
+ * optional call in mailindx.c:setup_header_widths to save various bits
+ * of format state
+ */
+extern void (*pith_opt_save_index_state)(int);
+
+
+/*
+ * optional call in mailindx.c:load_overview to paint the gathered overview data
+ * on the display
+ */
+extern void (*pith_opt_paint_index_hline)(MAILSTREAM *, long, ICE_S *);
+
+/*
+ * optional hook in mailview.c:format_message to allow for inserting an
+ * [editorial commment] in message text to indicate the message contains
+ * list-management pointers
+ */
+extern int (*pith_opt_rfc2369_editorial)(long, HANDLE_S **, int, int, gf_io_t);
+
+#ifdef ENABLE_LDAP
+/*
+ * optional hook in ldap.c:wp_lookups to ask user where to save chosen LDAP result
+ */
+extern void (*pith_opt_save_ldap_entry)(struct pine *, LDAP_CHOOSE_S *, int);
+#endif
+
+/*
+ * optional hook in addrbook.c:bunch-of-funcs to allow saving/restoring
+ * state (screen state and such) before and after calls that might be
+ * reentered
+ */
+extern void (*pith_opt_save_and_restore)(int, SAVE_STATE_S *);
+
+/*
+ * optional hooks in newmail.c:new_mail to allow for various indicators
+ * during the new mail check/arrival and checkpoint process
+ */
+extern void (*pith_opt_newmail_announce)(MAILSTREAM *, long, long);
+extern void (*pith_opt_newmail_check_cue)(int);
+extern void (*pith_opt_checkpoint_cue)(int);
+extern void (*pith_opt_icon_text)(char *, int);
+
+/*
+ * optional hook in remote.c to provide name for storing address book
+ * metadata
+ */
+extern char *(*pith_opt_rd_metadata_name)(void);
+
+/*
+ * optional hook in conf.c:read_pinerc to let caller deal with hard
+ * unreadable remote config file error
+ * Return TRUE to continue, FALSE otherwise
+ */
+extern int (*pith_opt_remote_pinerc_failure)(void);
+
+/*
+ * optional hook in mailcmd.c:do_broach_folder allowing for user prompt
+ * of closed folder open.
+ * Return -1 on cancel, zero otherwise. Set second arg by reference
+ * to TRUE for reopen.
+ */
+extern int (*pith_opt_reopen_folder)(struct pine *, int *);
+
+/*
+ * optional call in mailcmd.c:expunge_and_close to prompt for read message removal
+ */
+extern int (*pith_opt_read_msg_prompt)(long, char *);
+
+/*
+ * optional hook in mailcmd.c:expunge_and_close to prompt for expunge
+ * confirmation. Return 'y' to expunge/delete. Do not allow cancel.
+ */
+extern int (*pith_opt_expunge_prompt)(MAILSTREAM *, char *, long);
+
+/*
+ * optional hook in mailcmd.c:expunge_and_close called when a folder is
+ * about to be closed and expunged. Flags passed to expunge_and_close are
+ * in turn passed in this call.
+ */
+extern void (*pith_opt_begin_closing)(int, char *);
+
+/*
+ * optional hook in reply.c:reply_harvet to allow for user selection
+ * of reply-to vs. from address
+ * Return 'y' to use "reply-to" field.
+ */
+extern int (*pith_opt_replyto_prompt)(void);
+
+
+/*
+ * optional hook in reply.c:reply_harvet to allow for user choice
+ * of reply to all vs just sender
+ * Return -1 to cancel reply altogether, set reply flag by reference
+ */
+extern int (*pith_opt_reply_to_all_prompt)(int *);
+
+
+/*
+ * optional hook in save.c:create_for_save to allow for user confirmation
+ * of folder being created.
+ * Return: 1 for proceed, -1 for decline, 0 for error
+ */
+extern int (*pith_opt_save_create_prompt)(CONTEXT_S *, char *, int);
+
+
+/*
+ * optional hook in send.c:check_addresses to allow for user confirmation
+ * of sending to MAILER-DAEMON
+ */
+extern int (*pith_opt_daemon_confirm)(void);
+
+
+/*
+ * optional hook in save.c to prompt for permission to continue save
+ * in spite of size error. Return 'y' to continue or 'a' to answer
+ * yes to all until next reinitialization of the function.
+ */
+extern int (*pith_opt_save_size_changed_prompt)(long, int);
+
+
+/*
+ * optional hook to process filter patterns using external command
+ * on message contents
+ */
+extern void (*pith_opt_filter_pattern_cmd)(char **, SEARCHSET *, MAILSTREAM *, long, INTVL_S *);
+
+
+/*
+ * Hook to read signature from local file
+ */
+extern char *(*pith_opt_get_signature_file)(char *, int, int, int);
+
+
+/*
+ * Hook to make variable names pretty in help text
+ */
+extern char *(*pith_opt_pretty_var_name)(char *);
+
+
+/*
+ * Hook to make feature names pretty in help text
+ */
+extern char *(*pith_opt_pretty_feature_name)(char *, int);
+
+
+/*
+ * optional hook in mailindx.c:{from,subject}_str to cause the returned
+ * string to be truncated at the formatted width. 1 truncates.
+ * This is useful because the truncated string ends slightly
+ * differently than it would if it weren't truncated, for
+ * example it might end ... or something like that.
+ */
+extern int (*pith_opt_truncate_sfstr)(void);
+
+
+/*
+ * A stream is being closed. If there is something in the
+ * application that needs to react to that handle it here.
+ */
+extern void (*pith_opt_closing_stream)(MAILSTREAM *);
+
+
+/*
+ * Callback from mm_expunged to let us know the "current"
+ * message was expunged
+ */
+extern void (*pith_opt_current_expunged)(long unsigned int);
+
+/*
+ * Option User-Agent Header Prefix
+ */
+extern char *(*pith_opt_user_agent_prefix)(void);
+
+
+/*
+ * optional call to prompt for S/MIME passphase
+ */
+extern int (*pith_opt_smime_get_passphrase)(void);
+
+
+#endif /* PITH_OPTIONS_INCLUDED */
diff --git a/pith/osdep/Makefile.am b/pith/osdep/Makefile.am
new file mode 100644
index 00000000..1d790f3c
--- /dev/null
+++ b/pith/osdep/Makefile.am
@@ -0,0 +1,22 @@
+## Process this file with automake to produce Makefile.in
+## Use aclocal -I m4; automake
+
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+noinst_LIBRARIES = libpithosd.a
+
+libpithosd_a_SOURCES = bldpath.c canaccess.c canonicl.c collate.c color.c coredump.c \
+ creatdir.c debugtime.c domnames.c err_desc.c fgetpos.c filesize.c \
+ fnexpand.c hostname.c lstcmpnt.c mimedisp.c pipe.c pw_stuff.c \
+ rename.c tempfile.c temp_nam.c writ_dir.c
+
+AM_CPPFLAGS = -I@top_builddir@/include -I@top_srcdir@/include
diff --git a/pith/osdep/Makefile.in b/pith/osdep/Makefile.in
new file mode 100644
index 00000000..436101c8
--- /dev/null
+++ b/pith/osdep/Makefile.in
@@ -0,0 +1,558 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = pith/osdep
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/acx_pthread.m4 \
+ $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
+ $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/VERSION \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/include/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+ARFLAGS = cru
+libpithosd_a_AR = $(AR) $(ARFLAGS)
+libpithosd_a_LIBADD =
+am_libpithosd_a_OBJECTS = bldpath.$(OBJEXT) canaccess.$(OBJEXT) \
+ canonicl.$(OBJEXT) collate.$(OBJEXT) color.$(OBJEXT) \
+ coredump.$(OBJEXT) creatdir.$(OBJEXT) debugtime.$(OBJEXT) \
+ domnames.$(OBJEXT) err_desc.$(OBJEXT) fgetpos.$(OBJEXT) \
+ filesize.$(OBJEXT) fnexpand.$(OBJEXT) hostname.$(OBJEXT) \
+ lstcmpnt.$(OBJEXT) mimedisp.$(OBJEXT) pipe.$(OBJEXT) \
+ pw_stuff.$(OBJEXT) rename.$(OBJEXT) tempfile.$(OBJEXT) \
+ temp_nam.$(OBJEXT) writ_dir.$(OBJEXT)
+libpithosd_a_OBJECTS = $(am_libpithosd_a_OBJECTS)
+DEFAULT_INCLUDES =
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+SOURCES = $(libpithosd_a_SOURCES)
+DIST_SOURCES = $(libpithosd_a_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_CFLAGS = @AM_CFLAGS@
+AM_LDFLAGS = @AM_LDFLAGS@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CP = @CP@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+C_CLIENT_CFLAGS = @C_CLIENT_CFLAGS@
+C_CLIENT_GCCOPTLEVEL = @C_CLIENT_GCCOPTLEVEL@
+C_CLIENT_LDFLAGS = @C_CLIENT_LDFLAGS@
+C_CLIENT_SPECIALS = @C_CLIENT_SPECIALS@
+C_CLIENT_TARGET = @C_CLIENT_TARGET@
+C_CLIENT_WITH_IPV6 = @C_CLIENT_WITH_IPV6@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISPELLPROG = @ISPELLPROG@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN = @LN@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKE = @MAKE@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NPA_PROG = @NPA_PROG@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+POSUB = @POSUB@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+PWPROG = @PWPROG@
+RANLIB = @RANLIB@
+REGEX_BUILD = @REGEX_BUILD@
+RM = @RM@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPELLPROG = @SPELLPROG@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEB_BINDIR = @WEB_BINDIR@
+WEB_BUILD = @WEB_BUILD@
+WEB_PUBCOOKIE_BUILD = @WEB_PUBCOOKIE_BUILD@
+WEB_PUBCOOKIE_LIB = @WEB_PUBCOOKIE_LIB@
+WEB_PUBCOOKIE_LINK = @WEB_PUBCOOKIE_LINK@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+acx_pthread_config = @acx_pthread_config@
+alpine_interactive_spellcheck = @alpine_interactive_spellcheck@
+alpine_simple_spellcheck = @alpine_simple_spellcheck@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LIBRARIES = libpithosd.a
+libpithosd_a_SOURCES = bldpath.c canaccess.c canonicl.c collate.c color.c coredump.c \
+ creatdir.c debugtime.c domnames.c err_desc.c fgetpos.c filesize.c \
+ fnexpand.c hostname.c lstcmpnt.c mimedisp.c pipe.c pw_stuff.c \
+ rename.c tempfile.c temp_nam.c writ_dir.c
+
+AM_CPPFLAGS = -I@top_builddir@/include -I@top_srcdir@/include
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign pith/osdep/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign pith/osdep/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLIBRARIES:
+ -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+libpithosd.a: $(libpithosd_a_OBJECTS) $(libpithosd_a_DEPENDENCIES)
+ -rm -f libpithosd.a
+ $(libpithosd_a_AR) libpithosd.a $(libpithosd_a_OBJECTS) $(libpithosd_a_LIBADD)
+ $(RANLIB) libpithosd.a
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bldpath.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/canaccess.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/canonicl.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/collate.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/color.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/coredump.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/creatdir.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/debugtime.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/domnames.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/err_desc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fgetpos.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filesize.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fnexpand.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hostname.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lstcmpnt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mimedisp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pipe.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pw_stuff.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rename.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/temp_nam.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tempfile.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/writ_dir.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LIBRARIES)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libtool clean-noinstLIBRARIES ctags distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags uninstall uninstall-am
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/pith/osdep/ReadMe b/pith/osdep/ReadMe
new file mode 100644
index 00000000..30d9c876
--- /dev/null
+++ b/pith/osdep/ReadMe
@@ -0,0 +1,13 @@
+
+README FOR DIRECTORY: pith/osdep/
+
+ Modules in this directory should provide OS-independent interfaces
+for various OS-dependent methods of getting at resources or whatever.
+They should compile on their own without pine, c-client or pico
+dependencies.
+
+NOTES:
+
+ "_WINDOWS" currently differentiates windows from unix code.
+
+ "BUG:" comments mean there's something that needs to get worked on
diff --git a/pith/osdep/bldpath.c b/pith/osdep/bldpath.c
new file mode 100644
index 00000000..62d59e30
--- /dev/null
+++ b/pith/osdep/bldpath.c
@@ -0,0 +1,190 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: bldpath.c 934 2008-02-23 00:44:29Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+
+#if STDC_HEADERS
+#include <ctype.h>
+#endif
+
+#include "bldpath.h"
+
+/*
+ * Useful definitions
+ */
+#ifdef _WINDOWS
+#define ROOTED(S) (*(S) == '\\' || (isalpha((unsigned char) (S)[0]) && (S)[1] == ':'))
+#define HOMERELATIVE(S) (FALSE)
+#else /* UNIX */
+#define ROOTED(S) (*(S) == '/')
+#define HOMERELATIVE(S) (*(S) == '~')
+#endif
+
+
+
+
+/*----------------------------------------------------------------------
+ Paste together two pieces of a file name path
+
+ Args: pathbuf -- Put the result here
+ first_part -- of path name
+ second_part -- of path name
+ len -- Length of pathbuf
+
+ Result: New path is in pathbuf. Note that
+ we don't have to check for /'s at end of first_part and beginning
+ of second_part since multiple slashes are ok.
+
+BUGS: This is a first stab at dealing with fs naming dependencies, and others
+still exist.
+ ----*/
+void
+build_path(char *pathbuf, char *first_part, char *second_part, size_t len)
+{
+ if(!(pathbuf && len > 0))
+ return;
+
+ pathbuf[0] = '\0';
+
+ if(!first_part || is_rooted_path(second_part)){
+ if(second_part)
+ strncpy(pathbuf, second_part, len-1);
+
+ pathbuf[len-1] = '\0';
+ }
+ else{
+#ifdef _WINDOWS
+ int i;
+ char *orig_pathbuf = pathbuf;
+
+ for(i = 0; i < len-2 && first_part[i]; i++)
+ *pathbuf++ = first_part[i];
+
+ if(second_part){
+ if(i && first_part[i-1] == '\\'){ /* first part ended with \ */
+ if(*second_part == '\\') /* and second starts with \ */
+ second_part++; /* else just append second */
+ }
+ else if(*second_part != '\\') /* no slash at all, so */
+ *pathbuf++ = '\\'; /* insert one... */
+
+ while(pathbuf-orig_pathbuf < len-1 && *second_part)
+ *pathbuf++ = *second_part++;
+ }
+
+ *pathbuf = '\0';
+
+#else /* UNIX */
+
+ size_t fpl = 0;
+
+ strncpy(pathbuf, first_part, len-2);
+ pathbuf[len-2] = '\0';
+ if(second_part){
+ if(*pathbuf && pathbuf[(fpl=strlen(pathbuf))-1] != '/'){
+ pathbuf[fpl++] = '/';
+ pathbuf[fpl] = '\0';
+ }
+
+ strncat(pathbuf, second_part, len-1-strlen(pathbuf));
+ }
+
+ pathbuf[len-1] = '\0';
+
+#endif
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Test to see if the given file path is absolute
+
+ Args: file -- file path to test
+
+ Result: TRUE if absolute, FALSE otw
+
+ ----*/
+int
+is_absolute_path(char *path)
+{
+ return(path && (ROOTED(path) || HOMERELATIVE(path)));
+}
+
+
+/*
+ * homedir_in_path - return pointer to point in path string where home dir
+ * reference starts
+ */
+int
+is_rooted_path(char *path)
+{
+ return(path && ROOTED(path));
+}
+
+
+/*
+ * homedir_in_path - return pointer to point in path string where home dir
+ * reference starts
+ */
+int
+is_homedir_path(char *path)
+{
+ return(path && HOMERELATIVE(path));
+}
+
+
+/*
+ * homedir_in_path - return pointer to point in path string where home dir
+ * reference starts
+ */
+char *
+homedir_in_path(char *path)
+{
+#ifdef _WINDOWS
+ return(NULL);
+#else /* UNIX */
+ char *p;
+
+ return((p = strchr(path, '~')) ? p : NULL);
+#endif
+}
+
+
+/*
+ *
+ */
+char *
+filename_parent_ref(char *s)
+{
+#ifdef _WINDOWS
+ return(strstr(s, "\\..\\"));
+#else /* UNIX */
+ return(strstr(s, "/../"));
+#endif
+}
+
+
+int
+filename_is_restricted(char *s)
+{
+#ifdef _WINDOWS
+ return(filename_parent_ref(s) != NULL);
+#else /* UNIX */
+ return(strchr("./~", s[0]) != NULL || filename_parent_ref(s) != NULL);
+#endif
+}
diff --git a/pith/osdep/bldpath.h b/pith/osdep/bldpath.h
new file mode 100644
index 00000000..16ec8226
--- /dev/null
+++ b/pith/osdep/bldpath.h
@@ -0,0 +1,29 @@
+/*
+ * $Id: bldpath.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_OSDEP_BLDPATH_INCLUDED
+#define PITH_OSDEP_BLDPATH_INCLUDED
+
+
+void build_path(char *, char *, char *, size_t);
+int is_absolute_path(char *);
+int is_rooted_path(char *);
+int is_homedir_path(char *);
+char *homedir_in_path(char *);
+char *filename_parent_ref(char *);
+int filename_is_restricted(char *);
+
+
+#endif /* PITH_OSDEP_BLDPATH_INCLUDED */
diff --git a/pith/osdep/canaccess.c b/pith/osdep/canaccess.c
new file mode 100644
index 00000000..4d79c407
--- /dev/null
+++ b/pith/osdep/canaccess.c
@@ -0,0 +1,160 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: canaccess.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include "bldpath.h"
+#include "fnexpand.h"
+#include "../charconv/utf8.h"
+#include "../charconv/filesys.h"
+#include "canaccess.h"
+
+
+/*
+ * Useful definitions
+ */
+#ifdef _WINDOWS
+
+#define ACCESS_IN_CWD(F,M) (can_access((F), (M)))
+#define PATH_SEP ';'
+#define FILE_SEP '\\'
+
+#else /* UNIX */
+
+#define ACCESS_IN_CWD(F,M) (-1)
+#define PATH_SEP ':'
+#define FILE_SEP '/'
+
+#endif /* UNIX */
+
+
+
+/*
+ * Check if we can access a file in a given way
+ *
+ * Args: file -- The file to check
+ * mode -- The mode ala the access() system call, see ACCESS_EXISTS
+ * and friends in alpine.h.
+ *
+ * Result: returns 0 if the user can access the file according to the mode,
+ * -1 if he can't (and errno is set).
+ *
+ *
+ */
+int
+can_access(char *file, int mode)
+{
+#ifdef _WINDOWS
+ struct stat buf;
+
+ /*
+ * NOTE: The WinNT access call returns that every directory is readable and
+ * writable. We actually want to know if the write is going to fail, so we
+ * try it. We don't read directories in Windows so we skip implementing that.
+ */
+ if(mode & WRITE_ACCESS && file && !our_stat(file, &buf) && (buf.st_mode & S_IFMT) == S_IFDIR){
+ char *testname;
+ int fd;
+ size_t l = 0;
+
+ /*
+ * We'd like to just call temp_nam here, since it creates a file
+ * and does what we want. However, temp_nam calls us!
+ */
+ if((testname = malloc(MAXPATH * sizeof(char)))){
+ strncpy(testname, file, MAXPATH-1);
+ testname[MAXPATH-1] = '\0';
+ if(testname[0] && testname[(l=strlen(testname))-1] != '\\' &&
+ l+1 < MAXPATH){
+ l++;
+ strncat(testname, "\\", MAXPATH-strlen(testname)-1);
+ testname[MAXPATH-1] = '\0';
+ }
+
+ if(l+8 < MAXPATH &&
+ strncat(testname, "caXXXXXX", MAXPATH-strlen(testname)-1) && mktemp(testname)){
+ if((fd = our_open(testname, O_CREAT|O_EXCL|O_WRONLY|O_BINARY, 0600)) >= 0){
+ (void)close(fd);
+ our_unlink(testname);
+ free(testname);
+ /* success, drop through to access call */
+ }
+ else{
+ free(testname);
+ /* can't write in the directory */
+ return(-1);
+ }
+ }
+ else{
+ free(testname);
+ return(-1);
+ }
+ }
+ }
+ if(mode & EXECUTE_ACCESS) /* Windows access has no execute mode */
+ mode &= ~EXECUTE_ACCESS; /* and crashes because of it */
+#endif /* WINDOWS */
+
+ return(our_access(file, mode));
+}
+
+
+/*----------------------------------------------------------------------
+ Check if we can access a file in a given way in the given path
+
+ Args: path -- The path to look for "file" in
+ file -- The file to check
+ mode -- The mode ala the access() system call, see ACCESS_EXISTS
+ and friends in alpine.h.
+
+ Result: returns 0 if the user can access the file according to the mode,
+ -1 if he can't (and errno is set).
+ ----*/
+int
+can_access_in_path(char *path, char *file, int mode)
+{
+ char tmp[MAXPATH];
+ int rv = -1;
+
+ if(!path || !*path || is_rooted_path(file)){
+ rv = can_access(file, mode);
+ }
+ else if(is_homedir_path(file)){
+ strncpy(tmp, file, sizeof(tmp));
+ tmp[sizeof(tmp)-1] = '\0';
+ rv = fnexpand(tmp, sizeof(tmp)) ? can_access(tmp, mode) : -1;
+ }
+ else if((rv = ACCESS_IN_CWD(file,mode)) < 0){
+ char path_copy[MAXPATH + 1], *p, *t;
+
+ if(strlen(path) < MAXPATH){
+ strncpy(path_copy, path, sizeof(path_copy));
+ path_copy[sizeof(path_copy)-1] = '\0';
+
+ for(p = path_copy; p && *p; p = t){
+ if((t = strchr(p, PATH_SEP)) != NULL)
+ *t++ = '\0';
+
+ snprintf(tmp, sizeof(tmp), "%s%c%s", p, FILE_SEP, file);
+ if((rv = can_access(tmp, mode)) == 0)
+ break;
+ }
+ }
+ }
+
+ return(rv);
+}
diff --git a/pith/osdep/canaccess.h b/pith/osdep/canaccess.h
new file mode 100644
index 00000000..13ff7cf1
--- /dev/null
+++ b/pith/osdep/canaccess.h
@@ -0,0 +1,51 @@
+/*
+ * $Id: canaccess.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_OSDEP_CANACCESS_INCLUDED
+#define PITH_OSDEP_CANACCESS_INCLUDED
+
+
+#define EXECUTE_ACCESS 0x01 /* These five are */
+#define WRITE_ACCESS 0x02 /* for the calls */
+#define READ_ACCESS 0x04 /* to access() */
+#define ACCESS_EXISTS 0x00 /* <etc> */
+#define EDIT_ACCESS (WRITE_ACCESS + READ_ACCESS)
+
+/*
+ * These flags are used in calls to so_get.
+ * They're OR'd together with the ACCESS flags above but
+ * are otherwise unrelated.
+ */
+#define OWNER_ONLY 0x08 /* open with mode 0600 */
+/*
+ * If the storage object being written to needs to be converted to
+ * the character set of the user's locale, then use this flag. For
+ * example, when exporting to a file. Do not use this if the data
+ * will eventually go to the display because the data is expected
+ * to remain as UTF-8 until it gets to Writechar where it will be
+ * converted.
+ */
+#define WRITE_TO_LOCALE 0x10 /* convert to locale-specific charset */
+#define READ_FROM_LOCALE 0x20 /* convert from locale-specific charset */
+
+
+/*
+ * Exported Prototypes
+ */
+int can_access(char *, int);
+int can_access_in_path(char *, char *, int);
+
+
+#endif /* PITH_OSDEP_CANACCESS_INCLUDED */
diff --git a/pith/osdep/canonicl.c b/pith/osdep/canonicl.c
new file mode 100644
index 00000000..09791148
--- /dev/null
+++ b/pith/osdep/canonicl.c
@@ -0,0 +1,71 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: canonicl.c 764 2007-10-23 23:44:49Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../../c-client/mail.h"
+extern unsigned char *lcase(unsigned char *);
+
+#ifdef _WINDOWS
+/* wingdi.h uses ERROR (!) and we aren't using the c-client ERROR so... */
+#undef ERROR
+#endif
+
+#include <system.h>
+
+#include "canonicl.h"
+
+
+/*----------------------------------------------------------------------
+ Return canonical form of host name ala c-client (UNIX version).
+
+ Args: host -- The host name
+
+ Result: Canonical form, or input argument (worst case)
+
+ You can call it twice without worrying about copying
+ the results, but not more than twice.
+ ----*/
+char *
+canonical_name(char *host)
+{
+ struct hostent *hent;
+ char tmp[MAILTMPLEN];
+ static int whichbuf = 0;
+ static char buf[2][NETMAXHOST+1];
+ char *b;
+
+ whichbuf = (whichbuf + 1) % 2;
+ b = buf[whichbuf];
+
+ /* domain literal is easy */
+ if (host[0] == '[' && host[(strlen (host))-1] == ']')
+ strncpy(b, host, NETMAXHOST);
+ else{
+ strncpy(tmp, host, sizeof(tmp)-1);
+ tmp[sizeof(tmp)-1] = '\0';
+
+ hent = gethostbyname((char *) lcase((unsigned char *) tmp));
+ if(hent && hent->h_name)
+ strncpy(b, hent->h_name, NETMAXHOST);
+ else
+ strncpy(b, host, NETMAXHOST);
+ }
+
+ b[NETMAXHOST] = '\0';
+ return(b);
+}
+
+
diff --git a/pith/osdep/canonicl.h b/pith/osdep/canonicl.h
new file mode 100644
index 00000000..97c05b93
--- /dev/null
+++ b/pith/osdep/canonicl.h
@@ -0,0 +1,26 @@
+/*
+ * $Id: canonicl.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_OSDEP_CANONICL_INCLUDED
+#define PITH_OSDEP_CANONICL_INCLUDED
+
+
+/*
+ * Exported Prototypes
+ */
+char *canonical_name(char *);
+
+
+#endif /* PITH_OSDEP_CANONICL_INCLUDED */
diff --git a/pith/osdep/collate.c b/pith/osdep/collate.c
new file mode 100644
index 00000000..9a60379c
--- /dev/null
+++ b/pith/osdep/collate.c
@@ -0,0 +1,170 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: collate.c 766 2007-10-23 23:59:00Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+
+#include "collate.h"
+
+
+/*
+ * global hook
+ */
+int (*pcollator)();
+
+
+void
+set_collation(int collation, int ctype)
+{
+ extern int collator(); /* set to strcoll if available in system.h */
+
+ pcollator = strucmp;
+
+#ifdef LC_COLLATE
+ if(collation){
+ char *status = NULL;
+
+ /*
+ * This may not have the desired effect, if collator is not
+ * defined to be strcoll in os.h and strcmp and friends
+ * don't know about locales. If your system does have strcoll
+ * but we haven't defined collator to be strcoll in os.h, let us know.
+ */
+ status = setlocale(LC_COLLATE, "");
+
+ /*
+ * If there is an error or if the locale is the "C" locale, then we
+ * don't want to use strcoll because in the default "C" locale strcoll
+ * uses strcmp ordering and we want strucmp ordering.
+ *
+ * The problem with this is that setlocale returns a string which is
+ * not equal to "C" on some systems even when the locale is "C", so we
+ * can't really tell on those systems. On some systems like that, we
+ * may end up with a strcmp-style collation instead of a strucmp-style.
+ * We recommend that the users of those systems explicitly set
+ * LC_COLLATE in their environment.
+ */
+ if(status && !(status[0] == 'C' && status[1] == '\0'))
+ pcollator = collator;
+ }
+#endif
+#ifdef LC_CTYPE
+ if(ctype){
+ (void)setlocale(LC_CTYPE, "");
+ }
+#endif
+
+#ifdef LC_TIME
+ setlocale(LC_TIME, "");
+#endif
+}
+
+
+/*
+ * sstrcasecmp - compare two pointers to strings case independently
+ */
+int
+sstrcasecmp(const qsort_t *s1, const qsort_t *s2)
+{
+ return((*pcollator)(*(char **)s1, *(char **)s2));
+}
+
+
+#ifndef _WINDOWS
+
+/*--------------------------------------------------
+ A case insensitive strcmp()
+
+ Args: o, r -- The two strings to compare
+
+ Result: integer indicating which is greater
+ ---*/
+int
+strucmp(register char *o, register char *r)
+{
+ if(o == NULL){
+ if(r == NULL)
+ return 0;
+ else
+ return -1;
+ }
+ else if(r == NULL)
+ return 1;
+
+ while(*o && *r
+ && ((isupper((unsigned char)(*o))
+ ? (unsigned char)tolower((unsigned char)(*o))
+ : (unsigned char)(*o))
+ == (isupper((unsigned char)(*r))
+ ? (unsigned char)tolower((unsigned char)(*r))
+ : (unsigned char)(*r)))){
+ o++;
+ r++;
+ }
+
+ return((isupper((unsigned char)(*o))
+ ? tolower((unsigned char)(*o))
+ : (int)(unsigned char)(*o))
+ - (isupper((unsigned char)(*r))
+ ? tolower((unsigned char)(*r))
+ : (int)(unsigned char)(*r)));
+}
+
+/*----------------------------------------------------------------------
+ A case insensitive strncmp()
+
+ Args: o, r -- The two strings to compare
+ n -- length to stop comparing strings at
+
+ Result: integer indicating which is greater
+
+ ----*/
+int
+struncmp(register char *o, register char *r, register int n)
+{
+ if(n < 1)
+ return 0;
+
+ if(o == NULL){
+ if(r == NULL)
+ return 0;
+ else
+ return -1;
+ }
+ else if(r == NULL)
+ return 1;
+
+ n--;
+ while(n && *o && *r
+ && ((isupper((unsigned char)(*o))
+ ? (unsigned char)tolower((unsigned char)(*o))
+ : (unsigned char)(*o))
+ == (isupper((unsigned char)(*r))
+ ? (unsigned char)tolower((unsigned char)(*r))
+ : (unsigned char)(*r)))){
+ o++;
+ r++;
+ n--;
+ }
+
+ return((isupper((unsigned char)(*o))
+ ? tolower((unsigned char)(*o))
+ : (int)(unsigned char)(*o))
+ - (isupper((unsigned char)(*r))
+ ? tolower((unsigned char)(*r))
+ : (int)(unsigned char)(*r)));
+}
+#endif
diff --git a/pith/osdep/collate.h b/pith/osdep/collate.h
new file mode 100644
index 00000000..80aaab90
--- /dev/null
+++ b/pith/osdep/collate.h
@@ -0,0 +1,32 @@
+/*
+ * $Id: collate.h 925 2008-02-06 02:03:01Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_OSDEP_COLLATE_INCLUDED
+#define PITH_OSDEP_COLLATE_INCLUDED
+
+
+/*
+ * Exported Prototypes
+ */
+void set_collation(int, int);
+int strucmp(char *, char *);
+int struncmp(char *, char *, int);
+int sstrcasecmp(const qsort_t *, const qsort_t *);
+
+extern int (*pcollator)();
+
+
+#endif /* PITH_OSDEP_COLLATE_INCLUDED */
diff --git a/pith/osdep/color.c b/pith/osdep/color.c
new file mode 100644
index 00000000..faf3c675
--- /dev/null
+++ b/pith/osdep/color.c
@@ -0,0 +1,93 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: color.c 761 2007-10-23 22:35:18Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+ /*
+ *
+ * These routines themselves aren't necessarily OS-specific, they
+ * are all called from within pico, pine and webpine.
+ *
+ * They used to be in pico source (osdep/unix, mswin.c), but considering
+ * webpine uses color as well and it should *not* have to be linked
+ * against libpico and considering pico uses these routines but should
+ * not have to link against libpith (and in turn c-client) we put them
+ * in pith/osdep which should only have to link against system libraries
+ * and thus be include freely in all of pine, pico and webpine.
+ */
+
+
+#include <system.h>
+#include "./color.h"
+
+
+
+/*
+ * new_color_pair - allocate a new color pair structure assigning
+ * given foreground and background color strings
+ */
+COLOR_PAIR *
+new_color_pair(char *fg, char *bg)
+{
+ COLOR_PAIR *ret;
+
+ if((ret = (COLOR_PAIR *) malloc(sizeof(*ret))) != NULL){
+ memset(ret, 0, sizeof(*ret));
+ if(fg){
+ strncpy(ret->fg, fg, MAXCOLORLEN);
+ ret->fg[MAXCOLORLEN] = '\0';
+ }
+
+ if(bg){
+ strncpy(ret->bg, bg, MAXCOLORLEN);
+ ret->bg[MAXCOLORLEN] = '\0';
+ }
+ }
+
+ return(ret);
+}
+
+
+/*
+ * free_color_pair - release resources associated with given
+ * color pair structure
+ */
+void
+free_color_pair(COLOR_PAIR **cp)
+{
+ if(cp && *cp){
+ free(*cp);
+ *cp = NULL;
+ }
+}
+
+
+/*
+ * Just like pico_set_color except it doesn't set the color, it just
+ * returns the value. Assumes def of PSC_NONE, since otherwise we always
+ * succeed and don't need to call this.
+ */
+int
+pico_is_good_colorpair(COLOR_PAIR *cp)
+{
+ return(cp && pico_is_good_color(cp->fg) && pico_is_good_color(cp->bg));
+}
+
+
+COLOR_PAIR *
+pico_set_colorp(COLOR_PAIR *col, int flags)
+{
+ return(pico_set_colors(col ? col->fg : NULL, col ? col->bg : NULL, flags));
+}
diff --git a/pith/osdep/color.h b/pith/osdep/color.h
new file mode 100644
index 00000000..32c86242
--- /dev/null
+++ b/pith/osdep/color.h
@@ -0,0 +1,98 @@
+/*
+ * $Id: color.h 1012 2008-03-26 00:44:22Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_OSDEP_COLOR_INCLUDED
+#define PITH_OSDEP_COLOR_INCLUDED
+
+
+#define RGBLEN 11
+#define MAXCOLORLEN 11 /* longest string a color can be */
+
+typedef struct COLOR_PAIR {
+ char fg[MAXCOLORLEN+1];
+ char bg[MAXCOLORLEN+1];
+} COLOR_PAIR;
+
+#define COL_BLACK 0
+#define COL_RED 1
+#define COL_GREEN 2
+#define COL_YELLOW 3
+#define COL_BLUE 4
+#define COL_MAGENTA 5
+#define COL_CYAN 6
+#define COL_WHITE 7
+
+#define DEFAULT_NORM_FORE_RGB "000,000,000"
+#define DEFAULT_NORM_BACK_RGB "255,255,255"
+
+/* flags for pico_set_color() */
+#define PSC_NONE 0x0
+#define PSC_NORM 0x1
+#define PSC_REV 0x2
+#define PSC_RET 0x4 /* return an allocated copy of previous color */
+
+
+/*
+ * MATCH_NORM_COLOR means that the color that is set to this value
+ * will actually use the corresponding fg or bg color from the
+ * so called Normal Color. A MATCH_NONE_COLOR means that the
+ * corresponding fg or bg color will just be left alone, so that
+ * it will stay the same as it was. This is useful when you want
+ * to change the foreground color but let the background match
+ * whatever it was before, for example in colored index lines.
+ *
+ * Note: these need to be RGBLEN in length because they are sometimes
+ * used in places where an RGB value is expected.
+ */
+#define MATCH_NORM_COLOR "norm_padded"
+#define MATCH_NONE_COLOR "none_padded"
+
+#define MATCH_TRAN_COLOR "transparent"
+
+
+/* exported prototypes */
+COLOR_PAIR *new_color_pair(char *, char *);
+void free_color_pair(COLOR_PAIR **cp);
+int pico_is_good_colorpair(COLOR_PAIR *);
+COLOR_PAIR *pico_set_colorp(COLOR_PAIR *, int);
+
+
+/* required prototypes (os/app dependent ) */
+int pico_usingcolor(void);
+int pico_hascolor(void);
+char *colorx(int);
+char *color_to_asciirgb(char *);
+int pico_is_good_color(char *);
+COLOR_PAIR *pico_set_colors(char *, char *, int);
+int pico_set_fg_color(char *);
+int pico_set_bg_color(char *);
+void pico_nfcolor(char *);
+void pico_nbcolor(char *);
+void pico_rfcolor(char *);
+void pico_rbcolor(char *);
+COLOR_PAIR *pico_get_cur_color(void);
+COLOR_PAIR *pico_get_rev_color(void);
+void pico_set_normal_color(void);
+void pico_set_color_options(unsigned);
+unsigned pico_get_color_options(void);
+int pico_trans_is_on(void);
+char *pico_get_last_fg_color(void);
+char *pico_get_last_bg_color(void);
+char *color_to_canonical_name(char *);
+int pico_count_in_color_table(void);
+
+
+#endif /* PITH_OSDEP_COLOR_INCLUDED */
diff --git a/pith/osdep/coredump.c b/pith/osdep/coredump.c
new file mode 100644
index 00000000..52ad27d7
--- /dev/null
+++ b/pith/osdep/coredump.c
@@ -0,0 +1,31 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: coredump.c 761 2007-10-23 22:35:18Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include "coredump.h"
+
+
+/*----------------------------------------------------------------------
+ Abort with a core dump
+ ----*/
+void
+coredump(void)
+{
+ abort();
+}
+
+
diff --git a/pith/osdep/coredump.h b/pith/osdep/coredump.h
new file mode 100644
index 00000000..05c95a62
--- /dev/null
+++ b/pith/osdep/coredump.h
@@ -0,0 +1,26 @@
+/*
+ * $Id: coredump.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_OSDEP_COREDUMP_INCLUDED
+#define PITH_OSDEP_COREDUMP_INCLUDED
+
+
+/*
+ * Exported Prototypes
+ */
+void coredump(void);
+
+
+#endif /* PITH_OSDEP_COREDUMP_INCLUDED */
diff --git a/pith/osdep/creatdir.c b/pith/osdep/creatdir.c
new file mode 100644
index 00000000..bc15da21
--- /dev/null
+++ b/pith/osdep/creatdir.c
@@ -0,0 +1,55 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: creatdir.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include "../charconv/utf8.h"
+#include "../charconv/filesys.h"
+#include "creatdir.h"
+
+
+#ifdef S_IRWXU
+#define MAILDIR_MODE S_IRWXU
+#else
+#define MAILDIR_MODE 0700
+#endif
+
+
+
+/*----------------------------------------------------------------------
+ Create the mail subdirectory.
+
+ Args: dir -- Name of the directory to create
+
+ Result: Directory is created. Returns 0 on success, else -1 on error
+ and errno is valid.
+ ----*/
+int
+create_mail_dir(char *dir)
+{
+ if(our_mkdir(dir, MAILDIR_MODE) < 0)
+ return(-1);
+
+#ifndef _WINDOWS
+ our_chmod(dir, MAILDIR_MODE);
+
+ /* Some systems need this, on others we don't care if it fails */
+ our_chown(dir, getuid(), getgid());
+#endif /* !_WINDOWS */
+
+ return(0);
+}
diff --git a/pith/osdep/creatdir.h b/pith/osdep/creatdir.h
new file mode 100644
index 00000000..496d1f6c
--- /dev/null
+++ b/pith/osdep/creatdir.h
@@ -0,0 +1,26 @@
+/*
+ * $Id: creatdir.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_OSDEP_CREATDIR_INCLUDED
+#define PITH_OSDEP_CREATDIR_INCLUDED
+
+
+/*
+ * Exported Prototypes
+ */
+int create_mail_dir(char *);
+
+
+#endif /* PITH_OSDEP_CREATDIR_INCLUDED */
diff --git a/pith/osdep/debugtime.c b/pith/osdep/debugtime.c
new file mode 100644
index 00000000..a1ee3b2a
--- /dev/null
+++ b/pith/osdep/debugtime.c
@@ -0,0 +1,133 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: debugtime.c 770 2007-10-24 00:23:09Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <stdio.h>
+
+#include <system.h>
+#include "debugtime.h"
+
+#ifdef DEBUG
+
+/*
+ * Returns a pointer to static string for a timestamp.
+ *
+ * If timestamp is set .subseconds are added if available.
+ * If include_date is set the date is appended.
+ */
+char *
+debug_time(int include_date, int include_subseconds)
+{
+ time_t t;
+ struct tm *tm_now;
+#if HAVE_GETTIMEOFDAY
+ struct timeval tp;
+ struct timezone tzp;
+#else
+ struct _timeb timebuffer;
+#endif
+ static char timestring[23];
+ char subsecond[8];
+ char datestr[7];
+
+ timestring[0] = '\0';
+
+#if HAVE_GETTIMEOFDAY
+ if(gettimeofday(&tp, &tzp) == 0){
+ t = (time_t)tp.tv_sec;
+ if(include_date){
+ tm_now = localtime(&t);
+ snprintf(datestr, sizeof(datestr), " %d/%d", tm_now->tm_mon+1, tm_now->tm_mday);
+ }
+ else
+ datestr[0] = '\0';
+
+ if(include_subseconds)
+ snprintf(subsecond, sizeof(subsecond), ".%06ld", tp.tv_usec);
+ else
+ subsecond[0] = '\0';
+
+ snprintf(timestring, sizeof(timestring), "%.8s%.7s%.6s", ctime(&t)+11, subsecond, datestr);
+ }
+#else /* !HAVE_GETTIMEOFDAY */
+ /* Should be _WINDOWS */
+ t = time((time_t *)0);
+ if(include_date){
+ tm_now = localtime(&t);
+ snprintf(datestr, sizeof(datestr), " %d/%d", tm_now->tm_mon+1, tm_now->tm_mday);
+ }
+ else
+ datestr[0] = '\0';
+
+ if(include_subseconds){
+ _ftime(&timebuffer);
+ snprintf(subsecond, sizeof(subsecond), ".%03ld", timebuffer.millitm);
+ }
+ else
+ subsecond[0] = '\0';
+
+ snprintf(timestring, sizeof(timestring), "%.8s%.7s%.6s", ctime(&t)+11, subsecond, datestr);
+#endif /* HAVE_GETTIMEOFDAY */
+
+ return(timestring);
+}
+#endif /* DEBUG */
+
+
+/*
+ * Fills in the passed in structure with the current time.
+ *
+ * Returns 0 if ok
+ * -1 if can't do it
+ */
+int
+get_time(TIMEVAL_S *our_time_val)
+{
+#if HAVE_GETTIMEOFDAY
+ struct timeval tp;
+ struct timezone tzp;
+
+ if(gettimeofday(&tp, &tzp) == 0){
+ our_time_val->sec = tp.tv_sec;
+ our_time_val->usec = tp.tv_usec;
+ return 0;
+ }
+#else /* !HAVE_GETTIMEOFDAY */
+#ifdef _WINDOWS
+ struct _timeb timebuffer;
+
+ _ftime(&timebuffer);
+ our_time_val->sec = (long)timebuffer.time;
+ our_time_val->usec = 1000L * (long)timebuffer.millitm;
+ return 0;
+#endif
+#endif
+
+ return -1;
+}
+
+
+/*
+ * Returns the difference between the two values, in microseconds.
+ * Value returned is first - second.
+ */
+long
+time_diff(TIMEVAL_S *first, TIMEVAL_S *second)
+{
+ return(1000000L*(first->sec - second->sec) + (first->usec - second->usec));
+}
+
+
diff --git a/pith/osdep/debugtime.h b/pith/osdep/debugtime.h
new file mode 100644
index 00000000..6a3eda1e
--- /dev/null
+++ b/pith/osdep/debugtime.h
@@ -0,0 +1,31 @@
+/*
+ * $Id: debugtime.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_OSDEP_DEBUGTIME_INCLUDED
+#define PITH_OSDEP_DEBUGTIME_INCLUDED
+
+#include "../adjtime.h"
+
+/*
+ * Exported Prototypes
+ */
+#ifdef DEBUG
+char *debug_time(int, int);
+#endif
+int get_time(TIMEVAL_S *);
+long time_diff(TIMEVAL_S *, TIMEVAL_S *);
+
+
+#endif /* PITH_OSDEP_DEBUGTIME_INCLUDED */
diff --git a/pith/osdep/domnames.c b/pith/osdep/domnames.c
new file mode 100644
index 00000000..47a95278
--- /dev/null
+++ b/pith/osdep/domnames.c
@@ -0,0 +1,145 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: domnames.c 1176 2008-09-29 21:16:42Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "domnames.h"
+
+
+/*----------------------------------------------------------------------
+ Get the current host and domain names
+
+ Args: hostname -- buffer to return the hostname in
+ hsize -- size of buffer above
+ domainname -- buffer to return domain name in
+ dsize -- size of buffer above
+
+ Result: The system host and domain names are returned. If the full host
+ name is akbar.cac.washington.edu then the domainname is
+ cac.washington.edu.
+
+On Internet connected hosts this look up uses /etc/hosts and DNS to
+figure all this out. On other less well connected machines some other
+file may be read. If there is no notion of a domain name the domain
+name may be left blank. On a PC where there really isn't a host name
+this should return blank strings. The .pinerc will take care of
+configuring the domain names. That is, this should only return the
+native system's idea of what the names are if the system has such
+a concept.
+ ----*/
+void
+getdomainnames(char *hostname, int hsize, char *domainname, int dsize)
+{
+#if HAVE_NETDB_H
+ char *dn, hname[MAX_ADDRESS+1];
+ struct hostent *he;
+ char **alias;
+ char *maybe = NULL;
+
+ if(gethostname(hname, MAX_ADDRESS))
+ hname[0] = 0xff;
+
+ /* sanity check of hostname string */
+ for(dn = hname; (*dn > 0x20) && (*dn < 0x7f); ++dn)
+ ;
+
+ if(*dn){ /* if invalid string returned, return "unknown" */
+ strncpy(domainname, "unknown", dsize-1);
+ domainname[dsize-1] = '\0';
+ strncpy(hostname, "unknown", hsize-1);
+ hostname[hsize-1] = '\0';
+ return;
+ }
+
+ he = gethostbyname(hname);
+ hostname[0] = '\0';
+
+ if(he == NULL){
+ strncpy(hostname, hname, hsize-1);
+ hostname[hsize-1] = '\0';
+ }
+ else{
+ /*
+ * If no dot in hostname it may be the case that there
+ * is an alias which is really the fully-qualified
+ * hostname. This could happen if the administrator has
+ * (incorrectly) put the unqualified name first in the
+ * hosts file, for example. The problem with looking for
+ * an alias with a dot is that now we're guessing, since
+ * the aliases aren't supposed to be the official hostname.
+ * We'll compromise and only use an alias if the primary
+ * name has no dot and exactly one of the aliases has a
+ * dot.
+ */
+ strncpy(hostname, he->h_name, hsize-1);
+ hostname[hsize-1] = '\0';
+ if(strchr(hostname, '.') == NULL){ /* no dot in hostname */
+ for(alias = he->h_aliases; *alias; alias++){
+ if(strchr(*alias, '.') != NULL){ /* found one */
+ if(maybe){ /* oops, this is the second one */
+ maybe = NULL;
+ break;
+ }
+ else
+ maybe = *alias;
+ }
+ }
+
+ if(maybe){
+ strncpy(hostname, maybe, hsize-1);
+ hostname[hsize-1] = '\0';
+ }
+ }
+ }
+
+ hostname[hsize-1] = '\0';
+
+ if((dn = strchr(hostname, '.')) != NULL)
+ strncpy(domainname, dn+1, dsize-1);
+ else
+ strncpy(domainname, hostname, dsize-1);
+
+ domainname[dsize-1] = '\0';
+#else /* !HAVE_NETDB_H */
+ /* should only be _WINDOWS */
+
+#ifdef _WINDOWS
+ char *p;
+ extern char *mylocalhost(void);
+
+ hostname[0] = domainname[0] = '\0';
+ if(p = mylocalhost())
+ snprintf(hostname, hsize, "%s", p);
+
+ snprintf(domainname, dsize, "%s",
+ (hostname[0] && hostname[0] != '[' && (p = strchr(hostname,'.')))
+ ? p+1 : hostname);
+#else /* !_WINDOWS */
+
+ char *p, hname[MAX_ADDRESS+1];
+
+ hostname[0] = domainname[0] = '\0';
+ if(gethostname(hname, MAX_ADDRESS) == 0)
+ snprintf(hostname, hsize, "%s", hname);
+
+ snprintf(domainname, dsize, "%s",
+ (hostname[0] && hostname[0] != '[' && (p = strchr(hostname,'.')))
+ ? p+1 : hostname);
+#endif /* !_WINDOWS */
+#endif /* !HAVE_NETDB_H */
+}
diff --git a/pith/osdep/domnames.h b/pith/osdep/domnames.h
new file mode 100644
index 00000000..303561a7
--- /dev/null
+++ b/pith/osdep/domnames.h
@@ -0,0 +1,26 @@
+/*
+ * $Id: domnames.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_OSDEP_DOMNAMES_INCLUDED
+#define PITH_OSDEP_DOMNAMES_INCLUDED
+
+
+/*
+ * Exported Prototypes
+ */
+void getdomainnames(char *, int, char *, int);
+
+
+#endif /* PITH_OSDEP_DOMNAMES_INCLUDED */
diff --git a/pith/osdep/err_desc.c b/pith/osdep/err_desc.c
new file mode 100644
index 00000000..645953e9
--- /dev/null
+++ b/pith/osdep/err_desc.c
@@ -0,0 +1,44 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: err_desc.c 761 2007-10-23 22:35:18Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include "err_desc.h"
+
+
+/*----------------------------------------------------------------------
+ Return string describing the error
+
+ Args: errnumber -- The system error number (errno)
+
+ Result: long string describing the error is returned
+ ----*/
+char *
+error_description(int errnumber)
+{
+ static char buffer[50+1];
+
+ buffer[0] = '\0';
+
+ if(errnumber >= 0)
+ snprintf(buffer, sizeof(buffer), "%s", strerror(errnumber));
+ else
+ snprintf(buffer, sizeof(buffer), "Unknown error #%d", errnumber);
+
+ return ( (char *) buffer);
+}
+
+
diff --git a/pith/osdep/err_desc.h b/pith/osdep/err_desc.h
new file mode 100644
index 00000000..9fff814e
--- /dev/null
+++ b/pith/osdep/err_desc.h
@@ -0,0 +1,26 @@
+/*
+ * $Id: err_desc.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_OSDEP_ERR_DESC_INCLUDED
+#define PITH_OSDEP_ERR_DESC_INCLUDED
+
+
+/*
+ * Exported Prototypes
+ */
+char *error_description(int);
+
+
+#endif /* PITH_OSDEP_ERR_DESC_INCLUDED */
diff --git a/pith/osdep/fgetpos.c b/pith/osdep/fgetpos.c
new file mode 100644
index 00000000..94dcc6c0
--- /dev/null
+++ b/pith/osdep/fgetpos.c
@@ -0,0 +1,50 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: fgetpos.c 761 2007-10-23 22:35:18Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <stdio.h>
+#include "fgetpos.h"
+
+
+/*----------------------------------------------------------------------
+ This is just a call to the ANSI C fgetpos function.
+ ----*/
+int
+fget_pos(FILE *stream, fpos_t *ptr)
+{
+#ifdef IS_NON_ANSI
+ *ptr = (fpos_t)ftell(stream);
+ return (*ptr == -1L ? -1 : 0);
+#else /* ANSI */
+ return(fgetpos(stream, ptr));
+#endif /* ANSI */
+}
+
+
+/*----------------------------------------------------------------------
+ This is just a call to the ANSI C fsetpos function.
+ ----*/
+int
+fset_pos(FILE *stream, fpos_t *ptr)
+{
+#ifdef IS_NON_ANSI
+ return fseek(stream, *ptr, 0);
+#else /* ANSI */
+ return(fsetpos(stream, ptr));
+#endif /* ANSI */
+}
+
+
diff --git a/pith/osdep/fgetpos.h b/pith/osdep/fgetpos.h
new file mode 100644
index 00000000..32fb7f33
--- /dev/null
+++ b/pith/osdep/fgetpos.h
@@ -0,0 +1,27 @@
+/*
+ * $Id: fgetpos.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_OSDEP_FGETPOS_INCLUDED
+#define PITH_OSDEP_FGETPOS_INCLUDED
+
+
+/*
+ * Exported Prototypes
+ */
+int fget_pos(FILE *, fpos_t *);
+int fset_pos(FILE *, fpos_t *);
+
+
+#endif /* PITH_OSDEP_FGETPOS_INCLUDED */
diff --git a/pith/osdep/filesize.c b/pith/osdep/filesize.c
new file mode 100644
index 00000000..e96479e9
--- /dev/null
+++ b/pith/osdep/filesize.c
@@ -0,0 +1,123 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: filesize.c 761 2007-10-23 22:35:18Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include "../charconv/utf8.h"
+#include "../charconv/filesys.h"
+#include "filesize.h"
+
+
+/*----------------------------------------------------------------------
+ Return the number of bytes in given file
+
+ Args: file -- file name
+
+ Result: the number of bytes in the file is returned or
+ -1 on error, in which case errno is valid
+ ----*/
+long
+name_file_size(char *file)
+{
+ struct stat buffer;
+
+ if(our_stat(file, &buffer) != 0)
+ return(-1L);
+
+ return((long)buffer.st_size);
+}
+
+
+/*----------------------------------------------------------------------
+ Return the number of bytes in given file
+
+ Args: fp -- FILE * for open file
+
+ Result: the number of bytes in the file is returned or
+ -1 on error, in which case errno is valid
+ ----*/
+long
+fp_file_size(FILE *fp)
+{
+ struct stat buffer;
+
+ if(fstat(fileno(fp), &buffer) != 0)
+ return(-1L);
+
+ return((long)buffer.st_size);
+}
+
+
+/*----------------------------------------------------------------------
+ Return the modification time of given file
+
+ Args: file -- file name
+
+ Result: the time of last modification (mtime) of the file is returned or
+ -1 on error, in which case errno is valid
+ ----*/
+time_t
+name_file_mtime(char *file)
+{
+ struct stat buffer;
+
+ if(our_stat(file, &buffer) != 0)
+ return((time_t)(-1));
+
+ return(buffer.st_mtime);
+}
+
+
+/*----------------------------------------------------------------------
+ Return the modification time of given file
+
+ Args: fp -- FILE * for open file
+
+ Result: the time of last modification (mtime) of the file is returned or
+ -1 on error, in which case errno is valid
+ ----*/
+time_t
+fp_file_mtime(FILE *fp)
+{
+ struct stat buffer;
+
+ if(fstat(fileno(fp), &buffer) != 0)
+ return((time_t)(-1));
+
+ return(buffer.st_mtime);
+}
+
+
+/*----------------------------------------------------------------------
+ Copy the mode, owner, and group of sourcefile to targetfile.
+
+ Args: targetfile --
+ sourcefile --
+
+ We don't bother keeping track of success or failure because we don't care.
+ ----*/
+void
+file_attrib_copy(char *targetfile, char *sourcefile)
+{
+ struct stat buffer;
+
+ if(our_stat(sourcefile, &buffer) == 0){
+ our_chmod(targetfile, buffer.st_mode);
+#if HAVE_CHOWN
+ our_chown(targetfile, buffer.st_uid, buffer.st_gid);
+#endif
+ }
+}
diff --git a/pith/osdep/filesize.h b/pith/osdep/filesize.h
new file mode 100644
index 00000000..a80297dc
--- /dev/null
+++ b/pith/osdep/filesize.h
@@ -0,0 +1,31 @@
+/*
+ * $Id: filesize.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_OSDEP_FILESIZE_INCLUDED
+#define PITH_OSDEP_FILESIZE_INCLUDED
+
+
+/*
+ * Exported Prototypes
+ */
+long name_file_size(char *);
+long fp_file_size(FILE *);
+time_t name_file_mtime(char *);
+time_t fp_file_mtime(FILE *);
+void file_attrib_copy(char *, char *);
+
+
+
+#endif /* PITH_OSDEP_FILESIZE_INCLUDED */
diff --git a/pith/osdep/fnexpand.c b/pith/osdep/fnexpand.c
new file mode 100644
index 00000000..1856c231
--- /dev/null
+++ b/pith/osdep/fnexpand.c
@@ -0,0 +1,96 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: fnexpand.c 761 2007-10-23 22:35:18Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+
+#include "../charconv/filesys.h"
+
+#include "fnexpand.h"
+
+
+
+
+/*----------------------------------------------------------------------
+ Expand the ~ in a file ala the csh (as home directory)
+
+ Args: buf -- The filename to expand (nothing happens unless begins with ~)
+ len -- The length of the buffer passed in (expansion is in place)
+
+ Result: Expanded string is returned using same storage as passed in.
+ If expansion fails, NULL is returned
+ ----*/
+char *
+fnexpand(char *buf, int len)
+{
+#ifdef _WINDOWS
+ /* We used to use ps_global->home_dir, now we have to build it */
+ if(*buf == '~' && *(buf+1) == '\\'){
+ char temp_path[_MAX_PATH], home_buf[_MAX_PATH], *temp_home_str;
+
+ if(getenv("HOME") != NULL)
+ temp_home_str = getenv("HOME");
+ else{
+ /* should eventually strip this out of get_user_info */
+ char *p, *q;
+
+ if((p = (char *) getenv("HOMEDRIVE"))
+ && (q = (char *) getenv("HOMEPATH")))
+ snprintf(home_buf, sizeof(home_buf), "%s%s", p, q);
+ else
+ snprintf(home_buf, sizeof(home_buf), "%c:\\", '@' + _getdrive());
+
+ temp_home_str = home_buf;
+ }
+ snprintf(temp_path, sizeof(temp_path), "%s", buf+1);
+ snprintf(buf, sizeof(buf), "%s%s", temp_path, fname_to_utf8(temp_home_str));
+ }
+ return(buf);
+#else /* UNIX */
+ struct passwd *pw;
+ register char *x,*y;
+ char name[20], *tbuf;
+
+ if(*buf == '~') {
+ for(x = buf+1, y = name;
+ *x != '/' && *x != '\0' && y < name + sizeof(name)-1;
+ *y++ = *x++)
+ ;
+
+ *y = '\0';
+ if(x == buf + 1)
+ pw = getpwuid(getuid());
+ else
+ pw = getpwnam(name);
+
+ if(pw == NULL)
+ return((char *)NULL);
+ if(strlen(pw->pw_dir) + strlen(buf) > len) {
+ return((char *)NULL);
+ }
+
+ if((tbuf = (char *) malloc((len+1)*sizeof(char))) != NULL){
+ snprintf(tbuf, len, "%s%s", pw->pw_dir, x);
+ snprintf(buf, len, "%s", tbuf);
+ free((void *)tbuf);
+ }
+ }
+
+ return(len ? buf : (char *)NULL);
+#endif /* UNIX */
+}
+
+
diff --git a/pith/osdep/fnexpand.h b/pith/osdep/fnexpand.h
new file mode 100644
index 00000000..df2dd899
--- /dev/null
+++ b/pith/osdep/fnexpand.h
@@ -0,0 +1,26 @@
+/*
+ * $Id: fnexpand.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_OSDEP_FNEXPAND_INCLUDED
+#define PITH_OSDEP_FNEXPAND_INCLUDED
+
+
+/*
+ * Exported Prototypes
+ */
+char *fnexpand(char *, int);
+
+
+#endif /* PITH_OSDEP_FNEXPAND_INCLUDED */
diff --git a/pith/osdep/forkwait.h b/pith/osdep/forkwait.h
new file mode 100644
index 00000000..e1aafa84
--- /dev/null
+++ b/pith/osdep/forkwait.h
@@ -0,0 +1,39 @@
+/*
+ * $Id: forkwait.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_OSDEP_FORKWAIT_INCLUDED
+#define PITH_OSDEP_FORKWAIT_INCLUDED
+
+
+#if HAVE_UNION_WAIT
+#define WAITSTATUS_T union wait
+#else
+#define WAITSTATUS_T int
+#endif
+
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(X) (((X) >> 8) & 0xff) /* high bits tell exit value */
+#endif
+#ifndef WIFEXITED
+#define WIFEXITED(X) (!((X) & 0xff)) /* low bits tell how it died */
+#endif
+
+#if !HAVE_WORKING_VFORK
+#define vfork fork
+#endif
+
+
+#endif /* PITH_OSDEP_FORKWAIT_INCLUDED */
+
diff --git a/pith/osdep/hostname.c b/pith/osdep/hostname.c
new file mode 100644
index 00000000..6ba445d3
--- /dev/null
+++ b/pith/osdep/hostname.c
@@ -0,0 +1,119 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: hostname.c 1176 2008-09-29 21:16:42Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+
+#if HAVE_GETHOSTNAME
+
+#elif HAVE_UNAME
+
+#include <sys/utsname.h>
+
+#elif defined(SYSTEMID)
+
+#elif defined(XENIX)
+
+#endif
+
+#include "hostname.h"
+
+
+
+/*----------------------------------------------------------------------
+ Call system gethostname
+
+ Args: hostname -- buffer to return host name in
+ size -- Size of buffer hostname is to be returned in
+
+ Result: returns 0 if the hostname is correctly set,
+ -1 if not (and errno is set).
+ ----*/
+int
+hostname(char *hostname, int size)
+{
+#if HAVE_GETHOSTNAME
+
+ if (gethostname(hostname, size))
+ return -1;
+
+ /* sanity check of hostname string */
+ for (*dn = hname; (*dn > 0x20) && (*dn < 0x7f); ++dn)
+ ;
+
+ if (*dn) /* non-ascii in hostname */
+ strncpy(hostname, "unknown", size-1);
+
+ hostname[size - 1] = '\0';
+ return 0;
+
+#elif HAVE_UNAME
+
+ /** This routine compliments of Scott McGregor at the HP
+ Corporate Computing Center **/
+
+ int uname(struct utsname *);
+ struct utsname name;
+
+ (void)uname(&name);
+ (void)strncpy(hostname,name.nodename,size-1);
+
+ hostname[size - 1] = '\0';
+ return 0;
+
+#elif defined(SYSTEMID)
+ char buf[32];
+ FILE *fp;
+ char *p;
+
+ if ((fp = our_fopen("/etc/systemid", "rb")) != 0) {
+ fgets(buf, sizeof(buf) - 1, fp);
+ fclose(fp);
+ if ((p = strindex(buf, '\n')) != NULL)
+ *p = '\0';
+ (void) strncpy(hostname, buf, size - 1);
+ hostname[size - 1] = '\0';
+ return 0;
+ }
+
+#elif defined(XENIX)
+
+#ifdef DOUNAME
+ /** This routine compliments of Scott McGregor at the HP
+ Corporate Computing Center **/
+
+ int uname();
+ struct utsname name;
+
+ (void) uname(&name);
+ (void) strncpy(hostname,name.nodename,size-1);
+#else
+ (void) strncpy(hostname, HOSTNAME, size-1);
+#endif /* DOUNAME */
+
+ hostname[size - 1] = '\0';
+ return 0;
+#else
+ /* We shouldn't get here except for the windows
+ * case, which currently doesn't use this (as
+ * it appears nothing else does as well)
+ */
+ return -1;
+
+#endif
+}
+
+
diff --git a/pith/osdep/hostname.h b/pith/osdep/hostname.h
new file mode 100644
index 00000000..65303ce4
--- /dev/null
+++ b/pith/osdep/hostname.h
@@ -0,0 +1,26 @@
+/*
+ * $Id: hostname.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_OSDEP_HOSTNAME_INCLUDED
+#define PITH_OSDEP_HOSTNAME_INCLUDED
+
+
+/*
+ * Exported Prototypes
+ */
+int hostname(char *, int);
+
+
+#endif /* PITH_OSDEP_HOSTNAME_INCLUDED */
diff --git a/pith/osdep/lstcmpnt.c b/pith/osdep/lstcmpnt.c
new file mode 100644
index 00000000..2a920939
--- /dev/null
+++ b/pith/osdep/lstcmpnt.c
@@ -0,0 +1,103 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: lstcmpnt.c 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include <string.h>
+#include "../../pith/charconv/filesys.h"
+#include "canaccess.h"
+#include "lstcmpnt.h"
+
+
+#ifdef _WINDOWS
+
+#define FILE_SEP '\\'
+
+#else /* UNIX */
+
+#define FILE_SEP '/'
+
+#endif /* UNIX */
+
+
+
+/*----------------------------------------------------------------------
+ Return pointer to last component of pathname.
+
+ Args: filename -- The pathname.
+
+ Result: Returned pointer points to last component in the input argument.
+ ----*/
+char *
+last_cmpnt(char *filename)
+{
+ char *p = NULL, *q = filename;
+
+ if(filename == NULL)
+ return(filename);
+
+ while((q = strchr(q, FILE_SEP)) != NULL)
+ if(*++q)
+ p = q;
+
+#ifdef _WINDOWS
+
+ if(!p && isalpha((unsigned char) *filename) && *(filename+1) == ':' && *(filename+2))
+ p = filename + 2;
+
+#endif
+
+ return(p);
+}
+
+
+/*
+ * Like our_mkdir but it makes subdirs as well as the final dir
+ */
+int
+our_mkpath(char *path, mode_t mode)
+{
+ char save, *q = path;
+
+#ifdef _WINDOWS
+ if(isalpha((unsigned char) q[0]) && q[1] == ':' && q[2])
+ q = path + 3;
+#endif
+
+ if(q == path && q[0] == FILE_SEP)
+ q = path + 1;
+
+ while((q = strchr(q, FILE_SEP)) != NULL){
+ save = *q;
+ *q = '\0';
+ if(can_access(path, ACCESS_EXISTS) != 0)
+ if(our_mkdir(path, mode) != 0){
+ *q = save;
+ return -1;
+ }
+
+ *q = save;
+ q++;
+ }
+
+ if(can_access(path, ACCESS_EXISTS) != 0 && our_mkdir(path, mode) != 0)
+ return -1;
+
+ return 0;
+}
diff --git a/pith/osdep/lstcmpnt.h b/pith/osdep/lstcmpnt.h
new file mode 100644
index 00000000..f2d9ec59
--- /dev/null
+++ b/pith/osdep/lstcmpnt.h
@@ -0,0 +1,28 @@
+/*
+ * $Id: lstcmpnt.h 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_OSDEP_LSTCMPNT_INCLUDED
+#define PITH_OSDEP_LSTCMPNT_INCLUDED
+
+
+/*
+ * Exported Prototypes
+ */
+char *last_cmpnt(char *);
+int our_mkpath(char *, mode_t);
+
+
+#endif /* PITH_OSDEP_LSTCMPNT_INCLUDED */
diff --git a/pith/osdep/makefile.wnt b/pith/osdep/makefile.wnt
new file mode 100644
index 00000000..5b0cc0c8
--- /dev/null
+++ b/pith/osdep/makefile.wnt
@@ -0,0 +1,65 @@
+# $Id: makefile.wnt 14098 2005-10-03 18:54:13Z jpf@u.washington.edu $
+#
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+#
+#
+# Makefile for WIN NT version of the libpith.lib
+#
+#
+CC=cl
+RM=del
+CP=copy
+RC=rc
+
+#includes symbol info for debugging
+CDEBUG= #-Zi -Od
+LDEBUG= /DEBUG /DEBUGTYPE:CV
+
+STDCFLAGS= -I..\..\include -I..\..\regex -nologo -MT -DWIN32 -DDOS -D_WINDOWS -DJOB_CONTROL -DMSC_MALLOC
+
+CFLAGS= $(CDEBUG) $(STDCFLAGS) $(NET) $(EXTRACFLAGS)
+
+LFLAGS= $(LDEBUG) $(EXTRALDFLAGS)
+
+RCFLAGS =
+
+LIBER=lib
+LIBARGS=/nologo /verbose
+
+HFILES= ../../include/system.h ../../include/general.h \
+ bldpath.h canaccess.h canonicl.h collate.h color.h coredump.h \
+ creatdir.h debugtime.h domnames.h err_desc.h fgetpos.h filesize.h \
+ fnexpand.h forkwait.h hostname.h lstcmpnt.h mimedisp.h pipe.h \
+ pithosd.h pw_stuff.h rename.h tempfile.h temp_nam.h \
+ writ_dir.h
+
+OFILES= bldpath.obj canaccess.obj canonicl.obj collate.obj color.obj coredump.obj \
+ creatdir.obj debugtime.obj domnames.obj err_desc.obj fgetpos.obj filesize.obj \
+ fnexpand.obj hostname.obj lstcmpnt.obj mimedisp.obj pipe.obj \
+ pw_stuff.obj rename.obj tempfile.obj temp_nam.obj \
+ writ_dir.obj
+
+all: libpithosd.lib
+
+.c.obj:
+ $(CC) -c $(CFLAGS) "$(MAKEDIR)"\$*.c
+
+$(OFILES): $(HFILES)
+
+libpithosd.lib: $(OFILES)
+ $(RM) libpithosd.lib || rem
+ $(LIBER) /out:libpithosd.lib $(OFILES)
+
+clean:
+ $(RM) *.lib
+ $(RM) *.obj
diff --git a/pith/osdep/mimedisp.c b/pith/osdep/mimedisp.c
new file mode 100644
index 00000000..69ff3337
--- /dev/null
+++ b/pith/osdep/mimedisp.c
@@ -0,0 +1,473 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: mimedisp.c 942 2008-03-04 18:21:33Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "../../c-client/fs.h"
+
+#ifdef _WINDOWS
+#include "../../pico/osdep/mswin.h"
+#endif
+
+#include "mimedisp.h"
+#include "../charconv/utf8.h"
+#ifdef OSX_TARGET
+#include <Security/AuthSession.h>
+#endif
+
+
+/*
+ * Internal prototypes
+ */
+#ifdef _WINDOWS
+int mswin_reg_viewer(LPTSTR mime_type, LPTSTR mime_ext,
+ char *cmd, int clen, int chk);
+int mswin_reg_mime_type(LPTSTR file_ext, LPTSTR mime_type, size_t mime_type_len);
+int mswin_reg_mime_ext(LPTSTR mime_type, LPTSTR file_ext, size_t file_ext_len);
+
+#endif /* WINDOWS */
+
+#if OSX_TARGET
+
+int osx_build_mime_type_cmd(char *, char *, int, int *);
+int osx_build_mime_ext_cmd(char *, char *, int, int *);
+
+#endif /* OSX_TARGET */
+
+
+
+/*
+ * Determine if there is an OS-specific mechanism for accessing
+ * MIME and extension data. In the general *nix case this is all
+ * done through mailcap and mime.types files.
+ *
+ * Returns: 0 if there is no support (most *nix cases)
+ * 1 if there is support (Mac OS X, Windows)
+ */
+int
+mime_os_specific_access(void)
+{
+#ifdef _WINDOWS
+ return 1;
+#elif OSX_TARGET
+# ifdef AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER
+ {
+ /*
+ * if we don't have the WidowSession then we should avoid using
+ * frameworks unless they call themselves daemon-safe
+ */
+ SecuritySessionId session_id;
+ SessionAttributeBits session_bits;
+
+ if((SessionGetInfo(callerSecuritySession, &session_id, &session_bits)
+ == errSessionSuccess)
+ && session_bits & sessionHasGraphicAccess)
+ return 1;
+ else
+ return 0;
+ }
+# else
+ return 0;
+# endif
+#else
+ return 0;
+#endif
+}
+
+
+/*
+ * Return the command based on either the mimetype or the file
+ * extension. Mime-type always takes precedence.
+ *
+ * mime_type - mime-type of the file we're looking at
+ * mime_ext - file extension given us by the mime data
+ * cmd - buffer to copy the resulting command into
+ * chk - whether or not we should check the file extension
+ *
+ * Returns: 1 on success, 0 on failure
+ */
+int
+mime_get_os_mimetype_command(char *mime_type, char *mime_ext, char *cmd,
+ int clen, int chk, int *sp_hndlp)
+{
+#ifdef _WINDOWS
+ int ret;
+ LPTSTR mime_type_lpt, mime_ext_lpt;
+
+ mime_type_lpt = utf8_to_lptstr(mime_type);
+ mime_ext_lpt = utf8_to_lptstr(mime_ext);
+
+ ret = mswin_reg_viewer(mime_type_lpt, mime_ext_lpt, cmd, clen, chk);
+
+ if(mime_type_lpt)
+ fs_give((void **) &mime_type_lpt);
+
+ if(mime_ext_lpt)
+ fs_give((void **) &mime_ext_lpt);
+
+ return ret;
+
+#elif OSX_TARGET
+
+ /*
+ * if we wanted to be more like PC-Pine, we'd try checking
+ * the mime-type of mime_ext and seeing if that matches
+ * with our mime-type, which is safe for opening
+ */
+ if(!mime_os_specific_access())
+ return(0);
+
+ /* don't want to use Mail or something for a part alpine is good at */
+ if(!strucmp(mime_type, "message/rfc822"))
+ return(0);
+
+ return(osx_build_mime_type_cmd(mime_type, cmd, clen, sp_hndlp)
+ || (chk && mime_ext && *mime_ext &&
+ osx_build_mime_ext_cmd(mime_ext, cmd, clen, sp_hndlp)));
+#else
+ return 0;
+#endif
+}
+
+
+/*
+ * Given a file extension, return the mime-type if there is one
+ *
+ * Returns: 1 on success, 0 on failure
+ */
+int
+mime_get_os_mimetype_from_ext(char *file_ext, char *mime_type, int mime_type_len)
+{
+#ifdef _WINDOWS
+ int ret;
+ LPTSTR x, file_ext_lpt, mime_type_lpt;
+
+ file_ext_lpt = utf8_to_lptstr(file_ext);
+
+ if(file_ext_lpt){
+ if(mime_type){
+ mime_type_lpt = (LPTSTR) fs_get(mime_type_len * sizeof(TCHAR));
+ mime_type_lpt[0] = '\0';
+ }
+ else
+ mime_type_lpt = NULL;
+ }
+
+ ret = mswin_reg_mime_type(file_ext_lpt, mime_type_lpt, (size_t) mime_type_len);
+
+ /* convert answer back to UTF-8 */
+ if(ret && mime_type_lpt && mime_type){
+ char *u;
+
+ u = lptstr_to_utf8(mime_type_lpt);
+ if(u){
+ strncpy(mime_type, u, mime_type_len);
+ mime_type[mime_type_len-1] = '\0';
+ fs_give((void **) &u);
+ }
+ }
+
+ if(file_ext_lpt)
+ fs_give((void **) &file_ext_lpt);
+
+ if(mime_type_lpt)
+ fs_give((void **) &mime_type_lpt);
+
+ return ret;
+
+#elif OSX_TARGET
+
+ if(!mime_os_specific_access())
+ return(0);
+#ifdef AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER
+ CFStringRef mime_ref = NULL, type_id_ref = NULL, ext_ref = NULL;
+ char buf[1024];
+
+ if(!file_ext || !*file_ext)
+ return 0;
+
+ /* This for if we built on OS X >= 10.3 but run on < 10.3 */
+ if(&UTTypeCreatePreferredIdentifierForTag == NULL)
+ return 0;
+ if((ext_ref = CFStringCreateWithCString(NULL, *file_ext == '.' ?
+ file_ext + 1 : file_ext,
+ kCFStringEncodingASCII)) == NULL)
+ return 0;
+ if((type_id_ref
+ = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension,
+ ext_ref, NULL)) == NULL)
+ return 0;
+
+ if((mime_ref = UTTypeCopyPreferredTagWithClass(type_id_ref,
+ kUTTagClassMIMEType)) == NULL)
+ return 0;
+ if(CFStringGetCString(mime_ref, mime_type,
+ (CFIndex)mime_type_len - 1,
+ kCFStringEncodingASCII) == false)
+ return 0;
+
+ mime_type[mime_type_len - 1] = '\0';
+
+ return 1;
+#else
+ return 0;
+#endif /* AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER */
+
+#else
+ return 0;
+#endif /* OSX_TARGET */
+}
+
+
+/*
+ * Given a mime-type, return the file extension if there is one
+ *
+ * Returns: 1 on success, 0 on failure
+ */
+int
+mime_get_os_ext_from_mimetype(char *mime_type, char *file_ext, int file_ext_len)
+{
+#ifdef _WINDOWS
+ int ret;
+ LPTSTR x, mime_type_lpt, file_ext_lpt;
+
+ mime_type_lpt = utf8_to_lptstr(mime_type);
+
+ if(mime_type_lpt){
+ if(file_ext){
+ file_ext_lpt = (LPTSTR) fs_get(file_ext_len * sizeof(TCHAR));
+ file_ext_lpt[0] = '\0';
+ }
+ else
+ file_ext_lpt = NULL;
+ }
+
+ ret = mswin_reg_mime_ext(mime_type_lpt, file_ext_lpt, (size_t) file_ext_len);
+
+ /* convert answer back to UTF-8 */
+ if(ret && file_ext_lpt && file_ext){
+ char *u;
+
+ u = lptstr_to_utf8(file_ext_lpt);
+ if(u){
+ strncpy(file_ext, u, file_ext_len);
+ file_ext[file_ext_len-1] = '\0';
+ fs_give((void **) &u);
+ }
+ }
+
+ if(mime_type_lpt)
+ fs_give((void **) &mime_type_lpt);
+
+ if(file_ext_lpt)
+ fs_give((void **) &file_ext_lpt);
+
+ return ret;
+
+#elif OSX_TARGET
+
+ if(!mime_os_specific_access())
+ return(0);
+#ifdef AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER
+ CFStringRef mime_ref = NULL, type_id_ref = NULL, ext_ref = NULL;
+
+ if(!mime_type || !*mime_type)
+ return 0;
+ /* This for if we built on OS X >= 10.3 but run on < 10.3 */
+ if(&UTTypeCreatePreferredIdentifierForTag == NULL)
+ return 0;
+ if((mime_ref = CFStringCreateWithCString(NULL, mime_type,
+ kCFStringEncodingASCII)) == NULL)
+ return 0;
+ if((type_id_ref
+ = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType,
+ mime_ref, NULL)) == NULL)
+ return 0;
+
+ if((ext_ref = UTTypeCopyPreferredTagWithClass(type_id_ref,
+ kUTTagClassFilenameExtension)) == NULL)
+ return 0;
+ if((CFStringGetCString(ext_ref, file_ext, (CFIndex)file_ext_len - 1,
+ kCFStringEncodingASCII)) == false)
+ return 0;
+
+ file_ext[file_ext_len - 1] = '\0';
+
+ return 1;
+#else
+ return 0;
+#endif /* AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER */
+
+#else
+ return 0;
+#endif /* OSX_TARGET */
+}
+
+
+
+#ifdef _WINDOWS
+
+/*
+ * mswin_reg_viewer -
+ */
+int
+mswin_reg_viewer(LPTSTR mime_type, LPTSTR mime_ext, char *cmd, int clen, int chk)
+{
+ TCHAR tmp[256];
+ LPTSTR ext;
+
+ tmp[0] = '\0';
+
+ /*
+ * Everything's based on the file extension.
+ * So, sniff at registry's mapping for the given MIME type/subtype
+ * and sniff at clues on how to launch it, or
+ * extension mapping and then
+ * look for clues that some app will handle it.
+ *
+ */
+ return(((mswin_reg_mime_ext(mime_type, ext = tmp, sizeof(tmp)/sizeof(TCHAR))
+ || ((ext = mime_ext) && *mime_ext
+ && ((mswin_reg_mime_type(mime_ext, tmp, sizeof(tmp)/sizeof(TCHAR))
+ && mime_type && !_tcsicmp(mime_type, tmp))
+ || mime_type && !_tcsicmp(mime_type, TEXT("application/octet-stream")))))
+ && MSWRShellCanOpen(ext, cmd, clen, 0) == TRUE)
+ || (chk && MSWRShellCanOpen(ext, cmd, clen, 1) == TRUE));
+}
+
+
+/*
+ * given a file name extension, fill in the provided buf with its
+ * corresponding MIME type
+ */
+int
+mswin_reg_mime_type(LPTSTR file_ext, LPTSTR mime_type, size_t mime_type_len)
+{
+ TCHAR buf[64];
+ DWORD len = mime_type_len;
+
+ if(file_ext[0] != '.'){
+ *buf = '.';
+ _tcsncpy(buf + 1, file_ext, sizeof(buf)/sizeof(TCHAR)-1);
+ buf[sizeof(buf)/sizeof(TCHAR)-1] = '\0';
+ file_ext = buf;
+ }
+
+ return(MSWRPeek(HKEY_CLASSES_ROOT, file_ext, TEXT("content type"),
+ mime_type, &len) == TRUE);
+}
+
+
+/*
+ * given a mime_type, fill in the provided buf with its
+ * corresponding file name extension
+ */
+int
+mswin_reg_mime_ext(LPTSTR mime_type, LPTSTR file_ext, size_t file_ext_len)
+{
+ TCHAR keybuf[128];
+ DWORD len = file_ext_len;
+
+ if(mime_type && _tcslen(mime_type) < 50){
+ _sntprintf(keybuf, sizeof(keybuf), TEXT("MIME\\Database\\Content Type\\%s"), mime_type);
+ return(MSWRPeek(HKEY_CLASSES_ROOT, keybuf,
+ TEXT("extension"), file_ext, &len) == TRUE);
+ }
+
+ return FALSE;
+}
+#endif /* _WINDOWS */
+
+
+
+#if OSX_TARGET
+/* returns: 1 if success, 0 if failure */
+int
+osx_build_mime_type_cmd(mime_type, cmd, cmdlen, sp_hndlp)
+ char *mime_type, *cmd;
+ int cmdlen, *sp_hndlp;
+{
+ int rv = 0;
+
+ if(!mime_os_specific_access())
+ return(0);
+#ifdef AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER
+ CFStringRef str_ref = NULL, ret_str_ref = NULL;
+ CFURLRef url_ref = NULL;
+
+ if(&LSCopyApplicationForMIMEType == NULL)
+ return 0;
+ if((str_ref = CFStringCreateWithCString(NULL, mime_type,
+ kCFStringEncodingASCII)) == NULL)
+ return 0;
+ if(LSCopyApplicationForMIMEType(str_ref, kLSRolesAll, &url_ref)
+ != kLSApplicationNotFoundErr){
+ if((ret_str_ref = CFURLGetString(url_ref)) == NULL)
+ return 0;
+ if(CFStringGetCString(ret_str_ref, cmd, (CFIndex)cmdlen,
+ kCFStringEncodingASCII) == false)
+ return 0;
+ if(sp_hndlp)
+ *sp_hndlp = 1;
+ rv = 1;
+ if(url_ref)
+ CFRelease(url_ref);
+ }
+#endif /* AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER */
+ return rv;
+}
+
+
+/* returns: 1 if success, 0 if failure */
+int
+osx_build_mime_ext_cmd(mime_ext, cmd, cmdlen, sp_hndlp)
+ char *mime_ext, *cmd;
+ int cmdlen, *sp_hndlp;
+{
+ int rv = 0;
+
+ if(!mime_os_specific_access())
+ return 0;
+
+#ifdef AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER
+ CFStringRef str_ref = NULL, ret_str_ref = NULL;
+ CFURLRef url_ref = NULL;
+
+ if((str_ref = CFStringCreateWithCString(NULL, (*mime_ext) == '.'
+ ? mime_ext+1 : mime_ext,
+ kCFStringEncodingASCII)) == NULL)
+ return 0;
+ if(LSGetApplicationForInfo(kLSUnknownType, kLSUnknownCreator,
+ str_ref, kLSRolesAll, NULL, &url_ref)
+ != kLSApplicationNotFoundErr){
+ if((ret_str_ref = CFURLGetString(url_ref)) == NULL)
+ return 0;
+ if(CFStringGetCString(ret_str_ref, cmd, (CFIndex)cmdlen,
+ kCFStringEncodingASCII) == false)
+ return 0;
+ if(sp_hndlp)
+ *sp_hndlp = 1;
+ rv = 1;
+ if(url_ref)
+ CFRelease(url_ref);
+ }
+#endif
+ return rv;
+}
+#endif /* OSX_TARGET */
diff --git a/pith/osdep/mimedisp.h b/pith/osdep/mimedisp.h
new file mode 100644
index 00000000..36ddb7b1
--- /dev/null
+++ b/pith/osdep/mimedisp.h
@@ -0,0 +1,28 @@
+/*
+ * $Id: mimedisp.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_OSDEP_MIMEDISP_INCLUDED
+#define PITH_OSDEP_MIMEDISP_INCLUDED
+
+
+/*
+ * Exported Prototypes
+ */
+int mime_os_specific_access(void);
+int mime_get_os_mimetype_command(char *, char *, char *, int, int, int *);
+int mime_get_os_mimetype_from_ext(char *, char *, int);
+int mime_get_os_ext_from_mimetype(char *, char *, int);
+
+#endif /* PITH_OSDEP_MIMEDISP_INCLUDED */
diff --git a/pith/osdep/pipe.c b/pith/osdep/pipe.c
new file mode 100644
index 00000000..2183f529
--- /dev/null
+++ b/pith/osdep/pipe.c
@@ -0,0 +1,811 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: pipe.c 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+
+#include "err_desc.h"
+
+#include "canaccess.h"
+#include "temp_nam.h"
+#include "forkwait.h"
+#include "pipe.h"
+#include "../charconv/utf8.h"
+#include "../charconv/filesys.h"
+#include "../debug.h"
+
+#ifdef _WINDOWS
+#include "../../pico/osdep/mswin.h"
+#endif
+
+
+
+/*======================================================================
+ pipe
+
+ Initiate I/O to and from a process. These functions used to be
+ similar to popen and pclose, but both an incoming stream and an
+ output file are provided.
+
+ ====*/
+
+
+
+/*
+ * Global's to helpsignal handler tell us child's status has changed...
+ */
+static pid_t child_pid;
+
+
+/*
+ * Internal Protos
+ */
+void zot_pipe(PIPE_S **);
+#ifdef _WINDOWS
+int pipe_mswin_exec_wrapper(char *, PIPE_S *, unsigned,
+ void (*)(PIPE_S *, int, void *),
+ void (*)(char *));
+#else /* UNIX */
+char *pipe_error_msg(char *, char *, char *);
+RETSIGTYPE pipe_alarm(int);
+#endif /* UNIX */
+
+
+
+
+/*----------------------------------------------------------------------
+ Spawn a child process and optionally connect read/write pipes to it
+
+ Args: command -- string to hand the shell
+ outfile -- address of pointer containing file to receive output
+ errfile -- address of pointer containing file to receive error output
+ mode -- mode for type of shell, signal protection etc...
+ Returns: pointer to alloc'd PIPE_S on success, NULL otherwise
+
+ The outfile is either NULL, a pointer to a NULL value, or a pointer
+ to the requested name for the output file. In the pointer-to-NULL case
+ the caller doesn't care about the name, but wants to see the pipe's
+ results so we make one up. It's up to the caller to make sure the
+ free storage containing the name is cleaned up.
+
+ Mode bits serve several purposes.
+ PIPE_WRITE tells us we need to open a pipe to write the child's
+ stdin.
+ PIPE_READ tells us we need to open a pipe to read from the child's
+ stdout/stderr. *NOTE* Having neither of the above set means
+ we're not setting up any pipes, just forking the child and exec'ing
+ the command. Also, this takes precedence over any named outfile.
+ PIPE_STDERR means we're to tie the childs stderr to the same place
+ stdout is going. *NOTE* This only makes sense then if PIPE_READ
+ or an outfile is provided. Also, this takes precedence over any
+ named errfile.
+ PIPE_RESET means we reset the terminal mode to what it was before
+ we started pine and then exec the command. In PC-Pine, _RESET
+ was a shortcut for just executing a command. We'll try to pay
+ attention to the above flags to make sure we do the right thing.
+ PIPE_PROT means to protect the child from the usual nasty signals
+ that might cause premature death. Otherwise, the default signals are
+ set so the child can deal with the nasty signals in its own way.
+ NOT USED UNDER WINDOWS
+ PIPE_NOSHELL means we're to exec the command without the aid of
+ a system shell. *NOTE* This negates the affect of PIPE_USER.
+ NOT USED UNDER WINDOWS
+ PIPE_USER means we're to try executing the command in the user's
+ shell. Right now we only look in the environment, but that may get
+ more sophisticated later.
+ NOT USED UNDER WINDOWS
+ PIPE_RUNNOW was added for WINDOWS for the case pipe is called to run
+ a shell program (like for url viewing). This is the only option
+ where we don't wait for child termination, and is only obeyed if
+ PIPE_WRITE and PIPE_READ aren't set
+ ----*/
+PIPE_S *
+open_system_pipe(char *command, char **outfile, char **errfile, int mode,
+ int timeout, void (*pipecb_f)(PIPE_S *, int, void *),
+ void (*piperr_f)(char *))
+{
+ PIPE_S *syspipe = NULL;
+#ifdef _WINDOWS
+ int exit_code = 0;
+ char cmdbuf[1024];
+ unsigned flags = 0;
+#else
+ char shellpath[MAXPATH+1], *shell;
+ int p[2], oparentd = -1, ochildd = -1, iparentd = -1, ichildd = -1;
+#endif
+
+#ifdef _WINDOWS
+ if(mode & PIPE_STDERR)
+ flags |= MSWIN_EAW_CAPT_STDERR;
+ /*
+ * It'll be a lot more difficult to support READing and WRITing.
+ * This was never supported, and there don't look to be any cases
+ * that set both of these flags anymore for win32.
+ *
+ * errfile could probably be supported pretty easily
+ */
+
+ if(errfile){
+ if(piperr_f)
+ (*piperr_f)("Pipe arg not yet supported: Error File");
+
+ return(NULL);
+ }
+
+
+ if((mode & PIPE_RUNNOW)
+ && !(mode & (PIPE_WRITE | PIPE_READ | PIPE_STDERR))){
+ if(mswin_shell_exec(command, NULL) == 0
+ && (syspipe = (PIPE_S *) malloc(sizeof(PIPE_S))) != NULL){
+ memset(syspipe, 0, sizeof(PIPE_S));
+ return(syspipe);
+ }
+
+ return(NULL);
+ }
+
+ strncpy(cmdbuf, command, sizeof(cmdbuf));
+ cmdbuf[sizeof(cmdbuf)-1] = '\0';
+
+ if((syspipe = (PIPE_S *) malloc(sizeof(PIPE_S))) == NULL)
+ return(NULL);
+
+ memset(syspipe, 0, sizeof(PIPE_S));
+ syspipe->mode = mode;
+ if(!outfile){
+ syspipe->deloutfile = 1;
+ if(mode & PIPE_READ){
+ syspipe->outfile = temp_nam(NULL, "po");
+ our_unlink(syspipe->outfile);
+ }
+ }
+ else{
+ if(!*outfile) /* asked for, but not named? */
+ *outfile = temp_nam(NULL, "po");
+
+ our_unlink(*outfile);
+ syspipe->outfile = (char *) malloc((strlen(*outfile)+1)*sizeof(char));
+ snprintf(syspipe->outfile, strlen(*outfile)+1, "%s", *outfile);
+ }
+
+ if(mode & PIPE_WRITE){
+ /*
+ * Create tmp file to write, spawn child in close_pipe
+ * after tmp file's written...
+ */
+ syspipe->infile = temp_nam(NULL, "pw");
+ syspipe->out.f = our_fopen(syspipe->infile, "wb");
+ syspipe->command = (char *) malloc((strlen(cmdbuf)+1)*sizeof(char));
+ snprintf(syspipe->command, strlen(cmdbuf)+1, "%s", cmdbuf);
+ dprint((1, "pipe write: %s", cmdbuf));
+ }
+ else if(mode & PIPE_READ){
+ /*
+ * Create a tmp file for command result, exec the command
+ * here into temp file, and return file pointer to it...
+ */
+ syspipe->command = (char *) malloc((strlen(cmdbuf)+1)*sizeof(char));
+ snprintf(syspipe->command, strlen(cmdbuf)+1, "%s", cmdbuf);
+ dprint((1, "pipe read: %s", cmdbuf));
+ if(pipe_mswin_exec_wrapper("pipe command", syspipe,
+ flags, pipecb_f, piperr_f)){
+ if(syspipe->outfile){
+ free((void *) syspipe->outfile);
+ syspipe->outfile = NULL;
+ }
+
+ zot_pipe(&syspipe);
+ }
+ else{
+ syspipe->in.f = our_fopen(syspipe->outfile, "rb");
+ syspipe->exit_code = exit_code;
+ }
+ }
+ else{
+ /* we just run the command taking outfile into account */
+ syspipe->command = (char *) malloc((strlen(cmdbuf)+1)*sizeof(char));
+ snprintf(syspipe->command, strlen(cmdbuf)+1, "%s", cmdbuf);
+ if(pipe_mswin_exec_wrapper("pipe command", syspipe,
+ flags, pipecb_f, piperr_f)){
+ if(syspipe->outfile){
+ free((void *) syspipe->outfile);
+ syspipe->outfile = NULL;
+ }
+
+ zot_pipe(&syspipe);
+ }
+ else
+ syspipe->exit_code = exit_code;
+ }
+
+#else /* !_WINDOWS */
+
+ if((syspipe = (PIPE_S *) malloc(sizeof(PIPE_S))) == NULL)
+ return(NULL);
+
+ memset(syspipe, 0, sizeof(PIPE_S));
+
+ syspipe->mode = mode;
+
+ /*
+ * If we're not using the shell's command parsing smarts, build
+ * argv by hand...
+ */
+ if(mode & PIPE_NOSHELL){
+ char **ap, *p;
+ size_t n;
+
+ /* parse the arguments into argv */
+ for(p = command; *p && isspace((unsigned char)(*p)); p++)
+ ; /* swallow leading ws */
+
+ if(*p){
+ int l = strlen(p);
+
+ if((syspipe->args = (char *) malloc((l + 1) * sizeof(char))) != NULL){
+ strncpy(syspipe->args, p, l);
+ syspipe->args[l] = '\0';
+ }
+ else{
+ if(piperr_f)
+ (*piperr_f)(pipe_error_msg("<null>", "execute",
+ "Can't allocate command string"));
+ zot_pipe(&syspipe);
+ return(NULL);
+ }
+ }
+ else{
+ if(piperr_f)
+ (*piperr_f)(pipe_error_msg("<null>", "execute",
+ "No command name found"));
+ zot_pipe(&syspipe);
+ return(NULL);
+ }
+
+ for(p = syspipe->args, n = 2; *p; p++) /* count the args */
+ if(isspace((unsigned char)(*p))
+ && *(p+1) && !isspace((unsigned char)(*(p+1))))
+ n++;
+
+ if ((syspipe->argv = ap = (char **)malloc(n * sizeof(char *))) == NULL){
+ zot_pipe(&syspipe);
+ return(NULL);
+ }
+
+ memset(syspipe->argv, 0, n * sizeof(char *));
+
+ for(p = syspipe->args; *p; ){ /* collect args */
+ while(*p && isspace((unsigned char)(*p)))
+ *p++ = '\0';
+
+ *ap++ = (*p) ? p : NULL;
+ while(*p && !isspace((unsigned char)(*p)))
+ p++;
+ }
+
+ /* make sure argv[0] exists in $PATH */
+ if(can_access_in_path(getenv("PATH"), syspipe->argv[0],
+ EXECUTE_ACCESS) < 0){
+ if(piperr_f)
+ (*piperr_f)(pipe_error_msg(syspipe->argv[0], "access",
+ error_description(errno)));
+ zot_pipe(&syspipe);
+ return(NULL);
+ }
+ }
+
+ /* fill in any output filenames */
+ if(!(mode & PIPE_READ)){
+ if(outfile && !*outfile)
+ *outfile = temp_nam(NULL, "pine_p"); /* asked for, but not named? */
+
+ if(errfile && !*errfile)
+ *errfile = temp_nam(NULL, "pine_p"); /* ditto */
+ }
+
+ /* create pipes */
+ if(mode & (PIPE_WRITE | PIPE_READ)){
+ if(mode & PIPE_WRITE){
+ pipe(p); /* alloc pipe to write child */
+ oparentd = p[STDOUT_FILENO];
+ ichildd = p[STDIN_FILENO];
+ }
+
+ if(mode & PIPE_READ){
+ pipe(p); /* alloc pipe to read child */
+ iparentd = p[STDIN_FILENO];
+ ochildd = p[STDOUT_FILENO];
+ }
+ }
+
+ if(pipecb_f) /* let caller prep display */
+ (*pipecb_f)(syspipe, OSB_PRE_OPEN, NULL);
+
+
+ if((syspipe->pid = vfork()) == 0){
+ /* reset child's handlers in requested fashion... */
+ (void)signal(SIGINT, (mode & PIPE_PROT) ? SIG_IGN : SIG_DFL);
+ (void)signal(SIGQUIT, (mode & PIPE_PROT) ? SIG_IGN : SIG_DFL);
+ (void)signal(SIGHUP, (mode & PIPE_PROT) ? SIG_IGN : SIG_DFL);
+#ifdef SIGCHLD
+ (void) signal(SIGCHLD, SIG_DFL);
+#endif
+
+ /* if parent isn't reading, and we have a filename to write */
+ if(!(mode & PIPE_READ) && outfile){ /* connect output to file */
+ int output = our_creat(*outfile, 0600);
+ dup2(output, STDOUT_FILENO);
+ if(mode & PIPE_STDERR)
+ dup2(output, STDERR_FILENO);
+ else if(errfile)
+ dup2(our_creat(*errfile, 0600), STDERR_FILENO);
+ }
+
+ if(mode & PIPE_WRITE){ /* connect process input */
+ close(oparentd);
+ dup2(ichildd, STDIN_FILENO); /* tie stdin to pipe */
+ close(ichildd);
+ }
+
+ if(mode & PIPE_READ){ /* connect process output */
+ close(iparentd);
+ dup2(ochildd, STDOUT_FILENO); /* tie std{out,err} to pipe */
+ if(mode & PIPE_STDERR)
+ dup2(ochildd, STDERR_FILENO);
+ else if(errfile)
+ dup2(our_creat(*errfile, 0600), STDERR_FILENO);
+
+ close(ochildd);
+ }
+
+ if(mode & PIPE_NOSHELL){
+ execvp(syspipe->argv[0], syspipe->argv);
+ }
+ else{
+ if(mode & PIPE_USER){
+ char *env, *sh;
+ if((env = getenv("SHELL")) && (sh = strrchr(env, '/'))){
+ shell = sh + 1;
+ strncpy(shellpath, env, sizeof(shellpath)-1);
+ shellpath[sizeof(shellpath)-1] = '\0';
+ }
+ else{
+ shell = "csh";
+ strncpy(shellpath, "/bin/csh", sizeof(shellpath)-1);
+ shellpath[sizeof(shellpath)-1] = '\0';
+ }
+ }
+ else{
+ shell = "sh";
+ strncpy(shellpath, "/bin/sh", sizeof(shellpath)-1);
+ shellpath[sizeof(shellpath)-1] = '\0';
+ }
+
+ execl(shellpath, shell, command ? "-c" : (char *)NULL, fname_to_locale(command), (char *)NULL);
+ }
+
+ fprintf(stderr, "Can't exec %s\nReason: %s",
+ command, error_description(errno));
+ _exit(-1);
+ }
+
+ if((child_pid = syspipe->pid) > 0){
+ syspipe->isig = signal(SIGINT, SIG_IGN); /* Reset handlers to make */
+ syspipe->qsig = signal(SIGQUIT, SIG_IGN); /* sure we don't come to */
+ syspipe->hsig = signal(SIGHUP, SIG_IGN); /* a premature end... */
+ if((syspipe->timeout = timeout) != 0){
+ syspipe->alrm = signal(SIGALRM, pipe_alarm);
+ syspipe->old_timeo = alarm(timeout);
+ }
+
+ if(mode & PIPE_WRITE){
+ close(ichildd);
+ if(mode & PIPE_DESC)
+ syspipe->out.d = oparentd;
+ else
+ syspipe->out.f = fdopen(oparentd, "w");
+ }
+
+ if(mode & PIPE_READ){
+ close(ochildd);
+ if(mode & PIPE_DESC)
+ syspipe->in.d = iparentd;
+ else
+ syspipe->in.f = fdopen(iparentd, "r");
+ }
+ }
+ else{
+ if(mode & (PIPE_WRITE | PIPE_READ)){
+ if(mode & PIPE_WRITE){
+ close(oparentd);
+ close(ichildd);
+ }
+
+ if(mode & PIPE_READ){
+ close(iparentd);
+ close(ochildd);
+ }
+ }
+
+ if(pipecb_f) /* let caller fixup display */
+ (*pipecb_f)(syspipe, OSB_POST_OPEN, NULL);
+
+ if(outfile && *outfile){
+ our_unlink(*outfile);
+ free((void *) *outfile);
+ *outfile = NULL;
+ }
+
+ if(errfile && *errfile){
+ our_unlink(*errfile);
+ free((void *) *errfile);
+ *errfile = NULL;
+ }
+
+ if(piperr_f)
+ (*piperr_f)(pipe_error_msg(command, "fork",
+ error_description(errno)));
+ zot_pipe(&syspipe);
+ }
+
+#endif /* UNIX */
+
+ return(syspipe);
+}
+
+
+
+#ifndef _WINDOWS
+/*----------------------------------------------------------------------
+ Return appropriate error message
+
+ Args: cmd -- command we were trying to exec
+ op -- operation leading up to the exec
+ res -- result of that operation
+
+ ----*/
+char *
+pipe_error_msg(char *cmd, char *op, char *res)
+{
+ static char ebuf[512];
+
+ snprintf(ebuf, 256, "Pipe can't %.256s \"%.32sb\": %.223s",
+ op ? op : "?", cmd ? cmd : "?", res ? res : "?");
+
+ return(ebuf);
+}
+#endif /* !_WINDOWS */
+
+
+/*----------------------------------------------------------------------
+ Free resources associated with the given pipe struct
+
+ Args: syspipe -- address of pointer to struct to clean up
+
+ ----*/
+void
+zot_pipe(PIPE_S **syspipe)
+{
+ if((*syspipe)->args){
+ free((void *) (*syspipe)->args);
+ (*syspipe)->args = NULL;
+ }
+
+ if((*syspipe)->argv){
+ free((void *) (*syspipe)->argv);
+ (*syspipe)->argv = NULL;
+ }
+
+ if((*syspipe)->tmp){
+ free((void *) (*syspipe)->tmp);
+ (*syspipe)->tmp = NULL;
+ }
+
+#ifdef _WINDOWS
+
+ if((*syspipe)->outfile){
+ free((void *) (*syspipe)->outfile);
+ (*syspipe)->outfile = NULL;
+ }
+
+ if((*syspipe)->command){
+ free((void *) (*syspipe)->command);
+ (*syspipe)->command = NULL;
+ }
+
+#endif /* _WINDOWS */
+
+ free((void *) *syspipe);
+ *syspipe = NULL;
+}
+
+
+
+/*
+ * Returns: 0 if all went well, -1 otherwise
+ */
+int
+pipe_close_write(PIPE_S *syspipe)
+{
+ int rv = 0;
+
+ if(!syspipe || !syspipe->out.f)
+ return -1;
+
+#ifdef _WINDOWS
+
+ {
+ unsigned flags = 0;
+
+ if(syspipe->mode & PIPE_STDERR)
+ flags |= MSWIN_EAW_CAPT_STDERR;
+
+ rv = fclose(syspipe->out.f);
+ syspipe->out.f = NULL;
+ if(syspipe->mode & PIPE_WRITE){
+ /*
+ * PIPE_WRITE should always be set if we're trying to close
+ * the write end.
+ * PIPE_WRITE can't start process till now, all the others
+ * will have already run
+ */
+ if(pipe_mswin_exec_wrapper("pipe command", syspipe,
+ flags, NULL, NULL))
+ /* some horrible error just occurred */
+ rv = -1;
+ else
+ syspipe->in.f = our_fopen(syspipe->outfile, "rb");
+ }
+ else
+ rv = -1;
+ }
+
+#else /* UNIX */
+
+ rv = fclose(syspipe->out.f) ? -1 : 0;
+ syspipe->out.f = NULL;
+
+#endif
+ return(rv);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Close pipe previously allocated and wait for child's death
+
+ Args: syspipe -- address of pointer to struct returned by open_system_pipe
+ exitval -- return exit status here.
+
+ Returns:
+ Two modes of return values for backcompat.
+ If exitval == NULL
+ returns exit status of child or -1 if invalid syspipe
+ If exitval != NULL
+ returns -1 if invalid syspipe or 0 if ok. In that case, exitval
+ of child is returned in exitval
+ ----*/
+int
+close_system_pipe(PIPE_S **syspipe, int *exitval, void (*pipecb_f) (PIPE_S *, int, void *))
+{
+#ifdef _WINDOWS
+ int rv = 0;
+ unsigned flags = 0;
+
+ if(!(syspipe && *syspipe))
+ return(-1);
+
+ if((*syspipe)->mode & PIPE_STDERR)
+ flags |= MSWIN_EAW_CAPT_STDERR;
+
+ if(((*syspipe)->mode & PIPE_WRITE) && (*syspipe)->out.f){
+ fclose((*syspipe)->out.f);
+ /*
+ * PIPE_WRITE can't start process till now, all the others
+ * will have already run
+ */
+ if(pipe_mswin_exec_wrapper("pipe command", (*syspipe),
+ flags, pipecb_f, NULL))
+ /* some horrible error just occurred */
+ rv = -1;
+ }
+ else if((*syspipe)->mode & PIPE_READ)
+ if((*syspipe)->in.f)
+ fclose((*syspipe)->in.f);
+
+ if(exitval){
+ *exitval = (*syspipe)->exit_code;
+ dprint((5, "Closed pipe: exitval=%d\n", *exitval));
+ }
+
+ if((*syspipe)->infile)
+ our_unlink((*syspipe)->infile);
+
+ if((*syspipe)->outfile && (*syspipe)->deloutfile)
+ our_unlink((*syspipe)->outfile);
+
+ if(rv != -1 && !exitval)
+ rv = (*syspipe)->exit_code;
+
+ zot_pipe(syspipe);
+
+#ifdef DEBUG
+ if(!exitval){
+ dprint((5, "Closed pipe: rv=%d\n", rv));
+ }
+#endif /* DEBUG */
+
+ return(rv);
+
+#else /* UNIX */
+ int status;
+
+ if(!(syspipe && *syspipe))
+ return -1;
+
+ if(((*syspipe)->mode) & PIPE_WRITE){
+ if(((*syspipe)->mode) & PIPE_DESC){
+ if((*syspipe)->out.d >= 0)
+ close((*syspipe)->out.d);
+ }
+ else if((*syspipe)->out.f)
+ fclose((*syspipe)->out.f);
+ }
+
+ if(((*syspipe)->mode) & PIPE_READ){
+ if(((*syspipe)->mode) & PIPE_DESC){
+ if((*syspipe)->in.d >= 0)
+ close((*syspipe)->in.d);
+ }
+ else if((*syspipe)->in.f)
+ fclose((*syspipe)->in.f);
+ }
+
+ if(pipecb_f)
+ (*pipecb_f)(*syspipe, OSB_PRE_CLOSE, NULL);
+
+ /* wait on the child */
+ (void) process_reap((*syspipe)->pid, &status, PR_NONE);
+
+ /* restore original handlers... */
+ (void) signal(SIGINT, (*syspipe)->isig);
+ (void) signal(SIGHUP, (*syspipe)->hsig);
+ (void) signal(SIGQUIT, (*syspipe)->qsig);
+
+ if((*syspipe)->timeout){
+ (void)signal(SIGALRM, (*syspipe)->alrm);
+ alarm((*syspipe)->old_timeo);
+ child_pid = 0;
+ }
+
+ if(pipecb_f)
+ (*pipecb_f)(*syspipe, OSB_POST_CLOSE, NULL);
+
+ zot_pipe(syspipe);
+
+ if(exitval){
+ *exitval = status;
+ return 0;
+ }
+ else{
+ return(status);
+ }
+#endif /* UNIX */
+}
+
+
+/*
+ * process_reap - manage child demise and return exit status
+ *
+ * Args: pid -- id of process to reap
+ * esp -- pointer to exist status
+ * flags -- special reaping considerations
+ *
+ * Returns:
+ * < 0 -- if there's a problem
+ * 0 -- if no child to reap
+ * > 0 -- process id of the child
+ */
+pid_t
+process_reap(pid_t pid, int *esp, int flags)
+{
+#ifdef _WINDOWS
+
+ return 0;
+
+#else /* UNIX */
+ WAITSTATUS_T wstatus;
+ pid_t rv;
+ int wflags;
+
+#if HAVE_WAITPID
+
+ wflags = 0;
+
+#ifdef WNOHANG
+ if(flags & PR_NOHANG)
+ wflags |= WNOHANG;
+#endif
+
+ while (((rv = waitpid(pid, &wstatus, wflags)) < 0) && (errno != ECHILD));
+
+#elif HAVE_WAIT4
+
+ wflags = 0;
+
+#ifdef WNOHANG
+ if(flags & PR_NOHANG)
+ wflags |= WNOHANG;
+#endif
+
+ while (((rv = wait4(pid,&wstatus,wflags,NULL)) < 0) && (errno != ECHILD));
+
+#elif HAVE_WAIT
+
+ while (((rv = wait(&wstatus)) != pid) && ((rv > 0) || (errno != ECHILD)));
+
+#else
+
+ /* BUG: BAIL */
+
+#endif
+
+ if(rv > 0)
+ *esp = (WIFEXITED(wstatus)) ? (int) WEXITSTATUS(wstatus) : -1;
+
+ return(rv);
+#endif /* UNIX */
+}
+
+
+#ifndef _WINDOWS
+RETSIGTYPE
+pipe_alarm(int sig)
+{
+ if(child_pid)
+ kill(child_pid, SIGINT);
+}
+#endif /* !_WINDOWS */
+
+
+#ifdef _WINDOWS
+/*
+ * Wrapper around mswin_exec_and_wait()
+ */
+int
+pipe_mswin_exec_wrapper(char *whatsit,
+ PIPE_S *syspipe, unsigned flags,
+ void (*pipecb_f)(PIPE_S *, int, void *),
+ void (*piperr_f)(char *))
+{
+ int rv;
+
+ flags |= MSWIN_EAW_CTRL_C_CANCELS;
+
+ if(pipecb_f)
+ (*pipecb_f)(syspipe, OSB_PRE_OPEN, NULL);
+
+ rv = mswin_exec_and_wait(whatsit, syspipe->command,
+ syspipe->infile, syspipe->outfile,
+ &syspipe->exit_code, flags);
+
+ if(pipecb_f)
+ (*pipecb_f)(syspipe, OSB_POST_OPEN, (void *)rv);
+
+ return rv;
+}
+#endif
diff --git a/pith/osdep/pipe.h b/pith/osdep/pipe.h
new file mode 100644
index 00000000..589fc839
--- /dev/null
+++ b/pith/osdep/pipe.h
@@ -0,0 +1,117 @@
+/*
+ * $Id: pipe.h 769 2007-10-24 00:15:40Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_OSDEP_PIPE_INCLUDED
+#define PITH_OSDEP_PIPE_INCLUDED
+
+/* Standard I/O File Descriptor Definitions */
+#ifndef STDIN_FILENO
+#define STDIN_FILENO 0
+#endif
+#ifndef STDOUT_FILENO
+#define STDOUT_FILENO 1
+#endif
+#ifndef STDERR_FILENO
+#define STDERR_FILENO 2
+#endif
+
+
+/*
+ * Flags for the pipe command routines...
+ */
+#define PIPE_WRITE 0x0001 /* set up pipe for reading */
+#define PIPE_READ 0x0002 /* set up pipe for reading */
+#define PIPE_NOSHELL 0x0004 /* don't exec in shell */
+#define PIPE_USER 0x0008 /* user mode */
+#define PIPE_STDERR 0x0010 /* stderr to child output */
+#define PIPE_PROT 0x0020 /* protected mode */
+#define PIPE_RESET 0x0040 /* reset terminal mode */
+#define PIPE_DESC 0x0080 /* no stdio desc wrapping */
+#define PIPE_SILENT 0x0100 /* no screen clear, etc */
+#define PIPE_RUNNOW 0x0200 /* don't wait for child (PC-Pine) */
+#define PIPE_RAW 0x0400 /* don't convert to locale */
+#define PIPE_NONEWMAIL 0x0800 /* don't call new_mail */
+
+#ifdef _WINDOWS
+/*
+ * Flags for mswin_exec_and_wait
+ */
+#define MSWIN_EAW_CAPT_STDERR 0x0001
+#define MSWIN_EAW_CTRL_C_CANCELS 0x0002
+#endif
+
+
+/*
+ * Reaper flags
+ */
+#define PR_NONE 0x0000
+#ifdef WNOHANG
+#define PR_NOHANG 0x0001
+#endif
+
+/*
+ * open_system_pipe callback so caller can insert code, typically interface
+ * stuff right before/after the fork and before/after wait
+ */
+#define OSB_PRE_OPEN 0x0001
+#define OSB_POST_OPEN 0x0002
+#define OSB_PRE_CLOSE 0x0004
+#define OSB_POST_CLOSE 0x0008
+
+/*
+ * stucture required for the pipe commands...
+ */
+typedef struct pipe_s {
+ pid_t pid; /* child's process id */
+ int mode, /* mode flags used to open */
+ timeout, /* wait this long for child */
+ old_timeo; /* previous active alarm */
+ RETSIGTYPE (*hsig)(int), /* previously installed... */
+ (*isig)(int), /* handlers */
+ (*qsig)(int),
+ (*alrm)(int),
+ (*chld)(int);
+ union {
+ FILE *f;
+ int d;
+ } in; /* input data handle */
+ union {
+ FILE *f;
+ int d;
+ } out; /* output data handle */
+ char **argv, /* any necessary args */
+ *args,
+ *tmp; /* pointer to stuff */
+#ifdef _WINDOWS
+ char *infile; /* file containing pipe's stdin */
+ char *outfile; /* file containing pipe's stdout */
+ char *command; /* command to execute */
+ int exit_code; /* proc rv if run right away */
+ int deloutfile; /* need to rm outfile at close */
+#endif
+} PIPE_S;
+
+
+/*
+ * Exported Prototypes
+ */
+PIPE_S *open_system_pipe(char *, char **, char **, int, int,
+ void (*)(PIPE_S *, int, void *), void (*)(char *));
+int close_system_pipe(PIPE_S **, int *, void (*)(PIPE_S *, int, void *));
+int pipe_close_write(PIPE_S *);
+pid_t process_reap(pid_t, int *, int);
+
+
+#endif /* PITH_OSDEP_PIPE_INCLUDED */
diff --git a/pith/osdep/pithosd.h b/pith/osdep/pithosd.h
new file mode 100644
index 00000000..adf71e8d
--- /dev/null
+++ b/pith/osdep/pithosd.h
@@ -0,0 +1,43 @@
+/*
+ * $Id: pithosd.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_PITHOSD_INCLUDED
+#define PITH_PITHOSD_INCLUDED
+
+#include "bldpath.h"
+#include "canaccess.h"
+#include "canonicl.h"
+#include "collate.h"
+#include "color.h"
+#include "coredump.h"
+#include "creatdir.h"
+#include "debugtime.h"
+#include "domnames.h"
+#include "err_desc.h"
+#include "fgetpos.h"
+#include "filesize.h"
+#include "fnexpand.h"
+#include "hostname.h"
+#include "lstcmpnt.h"
+#include "mimedisp.h"
+#include "pipe.h"
+#include "pw_stuff.h"
+#include "rename.h"
+#include "tempfile.h"
+#include "temp_nam.h"
+#include "writ_dir.h"
+
+
+#endif /* PITH_PITHOSD_INCLUDED */
diff --git a/pith/osdep/pw_stuff.c b/pith/osdep/pw_stuff.c
new file mode 100644
index 00000000..f8f779c5
--- /dev/null
+++ b/pith/osdep/pw_stuff.c
@@ -0,0 +1,207 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: pw_stuff.c 763 2007-10-23 23:37:34Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "../charconv/utf8.h"
+#include "../charconv/filesys.h"
+#include "pw_stuff.h"
+
+/*
+ * internal prototypes
+ */
+#ifndef _WINDOWS
+
+static char *gcos_name(char *, char *);
+
+
+/*----------------------------------------------------------------------
+ Pull the name out of the gcos field if we have that sort of /etc/passwd
+
+ Args: gcos_field -- The long name or GCOS field to be parsed
+ logname -- Replaces occurances of & with logname string
+
+ Result: returns pointer to buffer with name
+ ----*/
+static char *
+gcos_name(char *gcos_field, char *logname)
+{
+ static char fullname[MAX_FULLNAME+1];
+ register char *fncp, *gcoscp, *lncp, *end;
+
+ /*
+ * Full name is all chars up to first ',' (or whole gcos, if no ',').
+ * Replace any & with Logname.
+ */
+
+ for(fncp = fullname, gcoscp= gcos_field, end = fullname + MAX_FULLNAME;
+ (*gcoscp != ',' && *gcoscp != '\0' && fncp < end);
+ gcoscp++){
+
+ if(*gcoscp == '&'){
+ for(lncp = logname; *lncp && fncp < end; fncp++, lncp++)
+ *fncp = (lncp == logname) ? toupper((unsigned char) (*lncp))
+ : (*lncp);
+ }else
+ *fncp++ = *gcoscp;
+ }
+
+ *fncp = '\0';
+ return(fullname);
+}
+
+#endif /* !_WINDOWS */
+
+
+
+/*----------------------------------------------------------------------
+ Fill in homedir, login, and fullname for the logged in user.
+ These are all pointers to static storage so need to be copied
+ in the caller.
+
+ Args: ui -- struct pointer to pass back answers
+
+ Result: fills in the fields
+ ----*/
+void
+get_user_info(struct user_info *ui)
+{
+#ifndef _WINDOWS
+ struct passwd *unix_pwd;
+
+ unix_pwd = getpwuid(getuid());
+ if(unix_pwd == NULL) {
+ ui->homedir = (char *) malloc(sizeof(char));
+ ui->homedir[0] = '\0';
+ ui->login = (char *) malloc(sizeof(char));
+ ui->login[0] = '\0';
+ ui->fullname = (char *) malloc(sizeof(char));
+ ui->fullname[0] = '\0';
+ }else {
+ char *s;
+ size_t len;
+
+ len = strlen(fname_to_utf8(unix_pwd->pw_dir));
+ ui->homedir = (char *) malloc((len+1) * sizeof(char));
+ snprintf(ui->homedir, len+1, "%s", fname_to_utf8(unix_pwd->pw_dir));
+
+ len = strlen(fname_to_utf8(unix_pwd->pw_name));
+ ui->login = (char *) malloc((len+1) * sizeof(char));
+ snprintf(ui->login, len+1, "%s", fname_to_utf8(unix_pwd->pw_name));
+
+ if((s = gcos_name(unix_pwd->pw_gecos, unix_pwd->pw_name)) != NULL){
+ len = strlen(fname_to_utf8(s));
+ ui->fullname = (char *) malloc((len+1) * sizeof(char));
+ snprintf(ui->fullname, len+1, "%s", fname_to_utf8(s));
+ }
+ }
+
+#else /* _WINDOWS */
+ char buf[_MAX_PATH], *p, *q;
+ TCHAR lptstr_buf[_MAX_PATH];
+ int len = _MAX_PATH;
+
+ if(GetUserName(lptstr_buf, &len))
+ ui->login = lptstr_to_utf8(lptstr_buf);
+ else
+ ui->login = our_getenv("USERNAME");
+
+ if((p = our_getenv("HOMEDRIVE"))
+ && (q = our_getenv("HOMEPATH")))
+ snprintf(buf, sizeof(buf), "%s%s", p, q);
+ else
+ snprintf(buf, sizeof(buf), "%c:\\", '@' + _getdrive());
+
+ if(p)
+ free((void *)p);
+
+ if(q)
+ free((void *)q);
+
+ ui->homedir = (char *) malloc((strlen(buf)+1) * sizeof(char));
+ if(ui->homedir){
+ strncpy(ui->homedir, buf, strlen(buf));
+ ui->homedir[strlen(buf)] = '\0';
+ }
+
+ ui->fullname = (char *) malloc(sizeof(char));
+ if(ui->fullname)
+ ui->fullname[0] = '\0';
+#endif /* _WINDOWS */
+}
+
+
+/*----------------------------------------------------------------------
+ Look up a userid on the local system and return rfc822 address
+
+ Args: name -- possible login name on local system
+
+ Result: returns NULL or pointer to alloc'd string rfc822 address.
+ ----*/
+char *
+local_name_lookup(char *name)
+{
+#ifndef _WINDOWS
+ struct passwd *pw = getpwnam(name);
+
+ if(pw == NULL){
+ char *p;
+
+ for(p = name; *p; p++)
+ if(isupper((unsigned char)*p))
+ break;
+
+ /* try changing it to all lower case */
+ if(p && *p){
+ char lcase[256];
+ size_t l;
+
+ snprintf(lcase, sizeof(lcase), "%s", name);
+
+ l = strlen(name);
+ for(p = lcase; *p; p++)
+ if(isupper((unsigned char)*p))
+ *p = tolower((unsigned char)*p);
+
+ pw = getpwnam(lcase);
+
+ if(pw){
+ strncpy(name, lcase, l+1);
+ name[l] = '\0';
+ }
+ }
+ }
+
+ if(pw != NULL){
+ char *gn, *s = NULL;
+ size_t l;
+
+ if((gn = gcos_name(pw->pw_gecos, name)) != NULL
+ && (s = (char *) malloc(l = ((strlen(gn) + 1) * sizeof(char)))) != NULL)
+ snprintf(s, l, "%s", gn);
+
+ return(s);
+ }
+ else
+ return((char *) NULL);
+#else /* _WINDOWS */
+ return(NULL);
+#endif /* _WINDOWS */
+}
+
+
diff --git a/pith/osdep/pw_stuff.h b/pith/osdep/pw_stuff.h
new file mode 100644
index 00000000..7a1191b7
--- /dev/null
+++ b/pith/osdep/pw_stuff.h
@@ -0,0 +1,29 @@
+/*
+ * $Id: pw_stuff.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_OSDEP_PW_STUFF_INCLUDED
+#define PITH_OSDEP_PW_STUFF_INCLUDED
+
+#include "../user.h"
+
+
+/*
+ * Exported Prototypes
+ */
+void get_user_info(USER_S *);
+char *local_name_lookup(char *);
+
+
+#endif /* PITH_OSDEP_PW_STUFF_INCLUDED */
diff --git a/pith/osdep/rename.c b/pith/osdep/rename.c
new file mode 100644
index 00000000..8ca993b4
--- /dev/null
+++ b/pith/osdep/rename.c
@@ -0,0 +1,69 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: rename.c 761 2007-10-23 22:35:18Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include "err_desc.h"
+#include "../charconv/utf8.h"
+#include "../charconv/filesys.h"
+#include "rename.h"
+
+
+/*----------------------------------------------------------------------
+ Rename a file
+
+ Args: tmpfname -- Old name of file
+ fname -- New name of file
+
+ Result: File is renamed. Returns 0 on success, else -1 on error
+ and errno is valid.
+ ----*/
+int
+rename_file(char *tmpfname, char *fname)
+{
+#if HAVE_RENAME
+ return(our_rename(tmpfname, fname));
+#else
+# if defined(_WINDOWS)
+ int ret;
+
+ /*
+ * DOS rename doesn't unlink destination for us...
+ */
+ if((ret = our_unlink(fname)) && (errno == EPERM)){
+ ret = -5;
+ }
+ else{
+ ret = our_rename(tmpfname, fname);
+ if(ret)
+ ret = -1;
+ }
+
+ return(ret);
+# else
+ int status;
+
+ our_unlink(fname);
+ if ((status = link(tmpfname, fname)) != 0)
+ return(status);
+
+ our_unlink(tmpfname);
+ return(0);
+# endif
+#endif
+}
+
+
diff --git a/pith/osdep/rename.h b/pith/osdep/rename.h
new file mode 100644
index 00000000..e8472495
--- /dev/null
+++ b/pith/osdep/rename.h
@@ -0,0 +1,26 @@
+/*
+ * $Id: rename.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_OSDEP_RENAME_INCLUDED
+#define PITH_OSDEP_RENAME_INCLUDED
+
+
+/*
+ * Exported Prototypes
+ */
+int rename_file(char *, char *);
+
+
+#endif /* PITH_OSDEP_RENAME_INCLUDED */
diff --git a/pith/osdep/temp_nam.c b/pith/osdep/temp_nam.c
new file mode 100644
index 00000000..03136ea6
--- /dev/null
+++ b/pith/osdep/temp_nam.c
@@ -0,0 +1,345 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: temp_nam.c 1012 2008-03-26 00:44:22Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include "canaccess.h"
+#include "temp_nam.h"
+#include "../charconv/utf8.h"
+#include "../charconv/filesys.h"
+
+
+#ifdef _WINDOWS
+
+#include <process.h>
+
+#define ACCESSIBLE (WRITE_ACCESS)
+#define PATH_SEP "\\"
+
+#else /* UNIX */
+
+#define ACCESSIBLE (WRITE_ACCESS|EXECUTE_ACCESS)
+#define PATH_SEP "/"
+
+#endif /* UNIX */
+
+
+/*
+ * Internal Prototypes
+ */
+char *was_nonexistent_tmp_name(char *, size_t, char *);
+
+
+
+/*
+ * This routine is derived from BSD4.3 code,
+ * Copyright (c) 1987 Regents of the University of California.
+ * All rights reserved.
+ */
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)mktemp.c 5.7 (Berkeley) 6/27/88";
+#endif /* LIBC_SCCS and not lint */
+
+char *
+was_nonexistent_tmp_name(char *as, size_t aslen, char *ext)
+{
+ register char *start, *trv;
+ struct stat sbuf;
+ unsigned pid;
+ static unsigned n = 0;
+ int i;
+ int fd, tries = 0;
+ int f;
+ static unsigned long r = 0;
+
+ if(r == 0L)
+ r = (unsigned)getpid() + time((time_t *)0);
+
+ for(i = 5; i > 0; i--)
+ r = 1664525 * r + 1013904223;
+
+ pid = ((unsigned)getpid() * 100) + n++;
+
+ pid += (r % 50000L);
+
+ /* extra X's get set to 0's */
+ for(trv = as; *trv; ++trv)
+ ;
+
+ /*
+ * We should probably make the name random instead of having it
+ * be the pid.
+ */
+ while(*--trv == 'X'){
+ *trv = (pid % 10) + '0';
+ pid /= 10;
+ }
+
+ /* add the extension, enough room guaranteed by caller */
+ if(ext && *ext){
+ strncat(as, ".", aslen-strlen(as)-1);
+ as[aslen-1] = '\0';
+ strncat(as, ext, aslen-strlen(as)-1);
+ as[aslen-1] = '\0';
+ }
+
+ /*
+ * Check for write permission on target directory; if you have
+ * six X's and you can't write the directory, this will run for
+ * a *very* long time.
+ */
+ for(start = ++trv; trv > as && *trv != PATH_SEP[0]; --trv)
+ ;
+
+ if(*trv == PATH_SEP[0]){
+#ifdef _WINDOWS
+ char treplace;
+
+ if((trv - as == 2) && isalpha(as[0]) && as[1] == ':')
+ trv++;
+ treplace = *trv;
+ *trv = '\0';
+ if(our_stat(as==trv ? PATH_SEP : as, &sbuf) || !(sbuf.st_mode & S_IFDIR))
+ return((char *)NULL);
+
+ *trv = treplace;
+
+#else /* UNIX */
+
+ *trv = '\0';
+
+ if(our_stat(as==trv ? PATH_SEP : as, &sbuf) || !(sbuf.st_mode & S_IFDIR))
+ return((char *)NULL);
+
+ *trv = PATH_SEP[0];
+
+#endif
+ }
+ else if (our_stat(".", &sbuf) == -1)
+ return((char *)NULL);
+
+ for(;;){
+ /*
+ * Check with lstat to be sure we don't have
+ * a symlink. If lstat fails and no such file, then we
+ * have a winner. Otherwise, lstat shouldn't fail.
+ * If lstat succeeds, then skip it because it exists.
+ */
+#ifndef _WINDOWS
+ if(our_lstat(as, &sbuf)){ /* lstat failed */
+ if(errno == ENOENT){ /* no such file, success */
+#endif /* !_WINDOWS */
+ /*
+ * Create the file so that the
+ * evil ones don't have a chance to put something there
+ * that they can read or write before we create it
+ * ourselves.
+ */
+ f = O_CREAT|O_EXCL|O_WRONLY|O_BINARY;
+
+ if((fd=our_open(as, f, 0600)) >= 0 && close(fd) == 0)
+ return(as);
+ else if(++tries > 3) /* open failed unexpectedly */
+ return((char *)NULL);
+#ifndef _WINDOWS
+ }
+ else /* failed for unknown reason */
+ return((char *)NULL);
+ }
+#endif /* !_WINDOWS */
+
+ for(trv = start;;){
+ if(!*trv)
+ return((char *)NULL);
+
+ /*
+ * Change the digits from the initial values into
+ * lower case letters and try again.
+ */
+ if(*trv == 'z')
+ *trv++ = 'a';
+ else{
+ if(isdigit((unsigned char)*trv))
+ *trv = 'a';
+ else
+ ++*trv;
+
+ break;
+ }
+ }
+ }
+ /*NOTREACHED*/
+}
+
+
+/*
+ * This routine is derived from BSD4.3 code,
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ */
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)tmpnam.c 4.5 (Berkeley) 6/27/88";
+#endif /* LIBC_SCCS and not lint */
+/*----------------------------------------------------------------------
+ Return a unique file name in a given directory. This is not quite
+ the same as the usual tempnam() function, though it is similar.
+ We want it to use the TMPDIR/TMP/TEMP environment variable only if dir
+ is NULL, instead of using it regardless if it is set.
+ We also want it to be safer than tempnam().
+ If we return a filename, we are saying that the file did not exist
+ at the time this function was called (and it wasn't a symlink pointing
+ to a file that didn't exist, either).
+ If dir is NULL this is a temp file in a public directory. In that
+ case we create the file with permission 0600 before returning.
+
+ Args: dir -- The directory to create the name in
+ prefix -- Prefix of the name
+
+ Result: Malloc'd string equal to new name is returned. It must be free'd
+ by the caller. Returns the string on success and NULL on failure.
+ ----*/
+char *
+temp_nam(char *dir, char *prefix)
+{
+ return(temp_nam_ext(dir, prefix, NULL));
+}
+
+
+/*----------------------------------------------------------------------
+
+ Like temp_nam but create a unique name with an extension.
+
+ Result: Malloc'd string equal to new name is returned. It must be free'd
+ by the caller. Returns the string on success and NULL on failure.
+ ----*/
+char *
+temp_nam_ext(char *dir, char *prefix, char *ext)
+{
+ struct stat buf;
+ size_t l, ll;
+ char *f, *name;
+
+ if(ext == NULL)
+ ext = "";
+
+ if(!(name = (char *)malloc(MAXPATH * sizeof(char))))
+ return((char *)NULL);
+
+ if(!dir && (f = getenv("TMPDIR")) && !our_stat(f, &buf) &&
+ (buf.st_mode&S_IFMT) == S_IFDIR &&
+ !can_access(f, ACCESSIBLE)){
+ strncpy(name, f, MAXPATH-1);
+ name[MAXPATH-1] = '\0';
+ goto done;
+ }
+
+ if(!dir && (f = getenv("TMP")) && !our_stat(f, &buf) &&
+ (buf.st_mode&S_IFMT) == S_IFDIR &&
+ !can_access(f, ACCESSIBLE)){
+ strncpy(name, f, MAXPATH-1);
+ name[MAXPATH-1] = '\0';
+ goto done;
+ }
+
+ if(!dir && (f = getenv("TEMP")) && !our_stat(f, &buf) &&
+ (buf.st_mode&S_IFMT) == S_IFDIR &&
+ !can_access(f, ACCESSIBLE)){
+ strncpy(name, f, MAXPATH-1);
+ name[MAXPATH-1] = '\0';
+ goto done;
+ }
+
+ if(dir){
+ strncpy(name, dir, MAXPATH-1);
+ name[MAXPATH-1] = '\0';
+
+#ifdef _WINDOWS
+ if(!*dir || (isalpha(*dir) && *(dir+1) == ':' && !*(dir+2))){
+ strncat(name, PATH_SEP, MAXPATH-strlen(name)-1);
+ name[MAXPATH-1] = '\0';
+ }
+#endif
+
+ if(!our_stat(name, &buf)
+ && (buf.st_mode&S_IFMT) == S_IFDIR
+ && !can_access(name, ACCESSIBLE)){
+ strncpy(name, dir, MAXPATH-1);
+ name[MAXPATH-1] = '\0';
+ goto done;
+ }
+ }
+
+#ifndef P_tmpdir
+#ifdef _WINDOWS
+#define P_tmpdir "\\tmp"
+#else /* UNIX */
+#define P_tmpdir "/usr/tmp"
+#endif /* UNIX */
+#endif
+
+ if(!our_stat(P_tmpdir, &buf) &&
+ (buf.st_mode&S_IFMT) == S_IFDIR &&
+ !can_access(P_tmpdir, ACCESSIBLE)){
+ strncpy(name, P_tmpdir, MAXPATH-1);
+ name[MAXPATH-1] = '\0';
+ goto done;
+ }
+
+#ifndef _WINDOWS
+ if(!our_stat("/tmp", &buf) &&
+ (buf.st_mode&S_IFMT) == S_IFDIR &&
+ !can_access("/tmp", ACCESSIBLE)){
+ strncpy(name, "/tmp", MAXPATH-1);
+ name[MAXPATH-1] = '\0';
+ goto done;
+ }
+#endif
+
+ free((void *)name);
+ return((char *)NULL);
+
+done:
+ f = NULL;
+ if(name[0] && *((f = &name[l=strlen(name)]) - 1) != PATH_SEP[0] && l+1 < MAXPATH){
+ *f++ = PATH_SEP[0];
+ *f = '\0';
+ l++;
+ }
+
+ if(prefix && (ll = strlen(prefix)) && l+ll < MAXPATH){
+ strncpy(f, prefix, MAXPATH-(f-name));
+ name[MAXPATH-1] = '\0';
+ f += ll;
+ l += ll;
+ }
+
+ if(l+5+(ext[0] ? strlen(ext)+1 : 0) < MAXPATH){
+ strncpy(f, "XXXXX", MAXPATH-(f-name));
+ name[MAXPATH-1] = '\0';
+ }
+ else{
+ free((void *)name);
+ return((char *)NULL);
+ }
+
+ return(was_nonexistent_tmp_name(name, MAXPATH, ext));
+}
diff --git a/pith/osdep/temp_nam.h b/pith/osdep/temp_nam.h
new file mode 100644
index 00000000..fef56f33
--- /dev/null
+++ b/pith/osdep/temp_nam.h
@@ -0,0 +1,28 @@
+/*
+ * $Id: temp_nam.h 770 2007-10-24 00:23:09Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_OSDEP_TEMP_NAM_INCLUDED
+#define PITH_OSDEP_TEMP_NAM_INCLUDED
+
+
+/*
+ * Exported Prototypes
+ */
+char *temp_nam(char *, char *);
+char *temp_nam_ext(char *, char *, char *);
+
+
+#endif /* PITH_OSDEP_TEMP_NAM_INCLUDED */
diff --git a/pith/osdep/tempfile.c b/pith/osdep/tempfile.c
new file mode 100644
index 00000000..4aa83e26
--- /dev/null
+++ b/pith/osdep/tempfile.c
@@ -0,0 +1,55 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: tempfile.c 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+
+#if HAVE_TMPFILE
+#else
+#include "temp_nam.h"
+#endif
+
+#include "../charconv/filesys.h"
+
+#include "tempfile.h"
+
+
+/*----------------------------------------------------------------------
+ Create a temporary file, the name of which we don't care about
+and that goes away when it is closed. Just like ANSI C tmpfile.
+ ----*/
+FILE *
+create_tmpfile(void)
+{
+#if HAVE_TMPFILE
+ return(tmpfile());
+#else
+ char *file_name;
+ FILE *stream = NULL;
+
+ file_name = temp_nam(NULL, "pine-tmp");
+ if(file_name){
+ stream = our_fopen(file_name, "w+b");
+ our_unlink(file_name);
+ fs_give((void **) &file_name);
+ }
+
+ return(stream);
+#endif
+}
+
+
diff --git a/pith/osdep/tempfile.h b/pith/osdep/tempfile.h
new file mode 100644
index 00000000..22573f85
--- /dev/null
+++ b/pith/osdep/tempfile.h
@@ -0,0 +1,28 @@
+/*
+ * $Id: tempfile.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_OSDEP_TEMPFILE_INCLUDED
+#define PITH_OSDEP_TEMPFILE_INCLUDED
+
+
+
+/*
+ * Exported Prototypes
+ */
+FILE *create_tmpfile(void);
+
+
+
+#endif /* PITH_OSDEP_TEMPFILE_INCLUDED */
diff --git a/pith/osdep/writ_dir.c b/pith/osdep/writ_dir.c
new file mode 100644
index 00000000..955839c1
--- /dev/null
+++ b/pith/osdep/writ_dir.c
@@ -0,0 +1,54 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: writ_dir.c 761 2007-10-23 22:35:18Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include "../charconv/utf8.h"
+#include "../charconv/filesys.h"
+#include "canaccess.h"
+#include "writ_dir.h"
+
+
+/*----------------------------------------------------------------------
+ Check to see if a directory exists and is writable by us
+
+ Args: dir -- directory name
+
+ Result: returns 0 if it exists and is writable
+ 1 if it is a directory, but is not writable
+ 2 if it is not a directory
+ 3 it doesn't exist.
+ ----*/
+int
+is_writable_dir(char *dir)
+{
+ struct stat sb;
+
+ if(our_stat(dir, &sb) < 0)
+ /*--- It doesn't exist ---*/
+ return(3);
+
+ if(!(sb.st_mode & S_IFDIR))
+ /*---- it's not a directory ---*/
+ return(2);
+
+ if(can_access(dir, 07))
+ return(1);
+ else
+ return(0);
+}
+
+
diff --git a/pith/osdep/writ_dir.h b/pith/osdep/writ_dir.h
new file mode 100644
index 00000000..c3ed238e
--- /dev/null
+++ b/pith/osdep/writ_dir.h
@@ -0,0 +1,27 @@
+/*
+ * $Id: writ_dir.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_OSDEP_WRIT_DIR_INCLUDED
+#define PITH_OSDEP_WRIT_DIR_INCLUDED
+
+
+
+/*
+ * Exported Prototypes
+ */
+int is_writable_dir(char *);
+
+
+#endif /* PITH_OSDEP_WRIT_DIR_INCLUDED */
diff --git a/pith/pattern.c b/pith/pattern.c
new file mode 100644
index 00000000..84a32c41
--- /dev/null
+++ b/pith/pattern.c
@@ -0,0 +1,8226 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: pattern.c 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $";
+#endif
+/*
+ * ========================================================================
+ * Copyright 2006-2009 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/pattern.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/string.h"
+#include "../pith/msgno.h"
+#include "../pith/status.h"
+#include "../pith/list.h"
+#include "../pith/flag.h"
+#include "../pith/tempfile.h"
+#include "../pith/addrstring.h"
+#include "../pith/search.h"
+#include "../pith/mailcmd.h"
+#include "../pith/filter.h"
+#include "../pith/save.h"
+#include "../pith/mimedesc.h"
+#include "../pith/reply.h"
+#include "../pith/folder.h"
+#include "../pith/maillist.h"
+#include "../pith/sort.h"
+#include "../pith/copyaddr.h"
+#include "../pith/pipe.h"
+#include "../pith/list.h"
+#include "../pith/news.h"
+#include "../pith/util.h"
+#include "../pith/sequence.h"
+#include "../pith/detoken.h"
+#include "../pith/busy.h"
+#include "../pith/indxtype.h"
+#include "../pith/mailindx.h"
+#include "../pith/send.h"
+#include "../pith/icache.h"
+#include "../pith/ablookup.h"
+#include "../pith/keyword.h"
+
+
+/*
+ * Internal prototypes
+ */
+void open_any_patterns(long);
+void sub_open_any_patterns(long);
+void sub_close_patterns(long);
+int sub_any_patterns(long, PAT_STATE *);
+PAT_LINE_S *parse_pat_lit(char *);
+PAT_LINE_S *parse_pat_inherit(void);
+PAT_S *parse_pat(char *);
+void parse_patgrp_slash(char *, PATGRP_S *);
+void parse_action_slash(char *, ACTION_S *);
+ARBHDR_S *parse_arbhdr(char *);
+char *next_arb(char *);
+PAT_S *first_any_pattern(PAT_STATE *);
+PAT_S *last_any_pattern(PAT_STATE *);
+PAT_S *prev_any_pattern(PAT_STATE *);
+PAT_S *next_any_pattern(PAT_STATE *);
+int sub_write_patterns(long);
+int write_pattern_file(char **, PAT_LINE_S *);
+int write_pattern_lit(char **, PAT_LINE_S *);
+int write_pattern_inherit(char **, PAT_LINE_S *);
+char *data_for_patline(PAT_S *);
+int charsets_present_in_msg(MAILSTREAM *, unsigned long, STRLIST_S *);
+void collect_charsets_from_subj(ENVELOPE *, STRLIST_S **);
+void collect_charsets_from_body(BODY *, STRLIST_S **);
+SEARCHPGM *next_not(SEARCHPGM *);
+SEARCHOR *next_or(SEARCHOR **);
+void set_up_search_pgm(char *, PATTERN_S *, SEARCHPGM *);
+void add_type_to_pgm(char *, PATTERN_S *, SEARCHPGM *);
+void set_srch(char *, char *, SEARCHPGM *);
+void set_srch_hdr(char *, char *, SEARCHPGM *);
+void set_search_by_age(INTVL_S *, SEARCHPGM *, int);
+void set_search_by_size(INTVL_S *, SEARCHPGM *);
+int non_eh(char *);
+void add_eh(char **, char **, char *, int *);
+void set_extra_hdrs(char *);
+int is_ascii_string(char *);
+ACTION_S *combine_inherited_role_guts(ACTION_S *);
+int move_filtered_msgs(MAILSTREAM *, MSGNO_S *, char *, int, char *);
+void set_some_flags(MAILSTREAM *, MSGNO_S *, long, char **, char **, int, char *);
+
+
+/*
+ * optional hook for external-program filter test
+ */
+void (*pith_opt_filter_pattern_cmd)(char **, SEARCHSET *, MAILSTREAM *, long, INTVL_S *);
+
+
+void
+role_process_filters(void)
+{
+ int i;
+ MAILSTREAM *stream;
+ MSGNO_S *msgmap;
+
+ for(i = 0; i < ps_global->s_pool.nstream; i++){
+ stream = ps_global->s_pool.streams[i];
+ if(stream && pine_mail_ping(stream)){
+ msgmap = sp_msgmap(stream);
+ if(msgmap)
+ reprocess_filter_patterns(stream, msgmap, MI_REFILTERING);
+ }
+ }
+}
+
+
+int
+add_to_pattern(PAT_S *pat, long int rflags)
+{
+ PAT_LINE_S *new_patline, *patline;
+ PAT_S *new_pat;
+ PAT_STATE dummy;
+
+ if(!any_patterns(rflags, &dummy))
+ return(0);
+
+ /* need a new patline */
+ new_patline = (PAT_LINE_S *)fs_get(sizeof(*new_patline));
+ memset((void *)new_patline, 0, sizeof(*new_patline));
+ new_patline->type = Literal;
+ (*cur_pat_h)->dirtypinerc = 1;
+
+ /* and a copy of pat */
+ new_pat = copy_pat(pat);
+
+ /* tie together */
+ new_patline->first = new_patline->last = new_pat;
+ new_pat->patline = new_patline;
+
+
+ /*
+ * Manipulate bits directly in pattern list.
+ * Cur_pat_h is set by any_patterns.
+ */
+
+
+ /* find last patline */
+ for(patline = (*cur_pat_h)->patlinehead;
+ patline && patline->next;
+ patline = patline->next)
+ ;
+
+ /* add new patline to end of list */
+ if(patline){
+ patline->next = new_patline;
+ new_patline->prev = patline;
+ }
+ else
+ (*cur_pat_h)->patlinehead = new_patline;
+
+ return(1);
+}
+
+
+/*
+ * Does pattern quoting. Takes the string that the user sees and converts
+ * it to the config file string.
+ *
+ * Args: src -- The source string.
+ *
+ * The last arg to add_escapes causes \, and \\ to be replaced with hex
+ * versions of comma and backslash. That's so we can embed commas in
+ * list variables without having them act as separators. If the user wants
+ * a literal comma, they type backslash comma.
+ * If /, \, or " appear (other than the special cases in previous sentence)
+ * they are backslash-escaped like \/, \\, or \".
+ *
+ * Returns: An allocated string with quoting added.
+ *
+ * The caller is responsible for freeing the memory allocated for the answer.
+ */
+char *
+add_pat_escapes(char *src)
+{
+ return(add_escapes(src, "/\\\"", '\\', "", ",\\"));
+}
+
+
+/*
+ * Undoes the escape quoting done by add_pat_escapes.
+ *
+ * Args: src -- The source string.
+ *
+ * Returns: A string with backslash quoting removed or NULL. The string starts
+ * at src and goes until the end of src or until a / is reached. The
+ * / is not included in the string. /'s may be quoted by preceding
+ * them with a backslash (\) and \'s may also be quoted by
+ * preceding them with a \. In fact, \ quotes any character.
+ * Not quite, \nnn is octal escape, \xXX is hex escape.
+ * Hex escapes are undone but left with a backslash in front.
+ *
+ * The caller is responsible for freeing the memory allocated for the answer.
+ */
+char *
+remove_pat_escapes(char *src)
+{
+ char *ans = NULL, *q, *p;
+ int done = 0;
+
+ if(src){
+ p = q = (char *)fs_get(strlen(src) + 1);
+
+ while(!done){
+ switch(*src){
+ case '\\':
+ src++;
+ if(*src){
+ if(isdigit((unsigned char)*src)){ /* octal escape */
+ *p++ = '\\';
+ *p++ = (char)read_octal(&src);
+ }
+ else if((*src == 'x' || *src == 'X') &&
+ *(src+1) && *(src+2) && isxpair(src+1)){
+ *p++ = '\\';
+ *p++ = (char)read_hex(src+1);
+ src += 3;
+ }
+ else
+ *p++ = *src++;
+ }
+
+ break;
+
+ case '\0':
+ case '/':
+ done++;
+ break;
+
+ default:
+ *p++ = *src++;
+ break;
+ }
+ }
+
+ *p = '\0';
+
+ ans = cpystr(q);
+ fs_give((void **)&q);
+ }
+
+ return(ans);
+}
+
+
+/*
+ * This takes envelope data and adds the backslash escapes that the user
+ * would have been responsible for adding if editing manually.
+ * It just escapes commas and backslashes.
+ *
+ * Caller must free result.
+ */
+char *
+add_roletake_escapes(char *src)
+{
+ return(add_escapes(src, ",\\", '\\', "", ""));
+}
+
+/*
+ * This function only escapes commas.
+ */
+char *
+add_comma_escapes(char *src)
+{
+ return(add_escapes(src, ",", '\\', "", ""));
+}
+
+
+/*
+ * These are the global pattern handles which all of the pattern routines
+ * use. Once we open one of these we usually leave it open until exiting
+ * pine. The _any versions are only used if we are altering our configuration,
+ * the _ne (NonEmpty) versions are used routinely. We open the patterns by
+ * calling either nonempty_patterns (normal use) or any_patterns (config).
+ *
+ * There are eight different pinerc variables which contain patterns. They are
+ * patterns-filters2, patterns-roles, patterns-scores2, patterns-indexcolors,
+ * patterns-other, and the old patterns, patterns-filters, and patterns-scores.
+ * The first five are the active patterns variables and the old variable are
+ * kept around so that we can convert old patterns to new. The reason we
+ * split it into five separate variables is so that each can independently
+ * be controlled by the main pinerc or by the exception pinerc. The reason
+ * for the change to filters2 and scores2 was so we could change the semantics
+ * of how rules work when there are pieces in the rule that we don't
+ * understand. We added a rule to detect 8bitSubjects. So a user might have
+ * a filter that deletes messages with 8bitSubjects. The problem was that
+ * that same filter in a old patterns-filters pine would match because it
+ * would ignore the 8bitSubject part of the pattern and match on the rest.
+ * So we changed the semantics so that rules with unknown pieces would be
+ * ignored instead of used. We had to change variable names at the same time
+ * because we were adding the 8bit thing and the old pines are still out
+ * there. Filters and Scores can both be dangerous. Roles, Colors, and Other
+ * seem less dangerous so not worth adding a new variable for them.
+ *
+ * Each of the eight variables has its own handle and status variables below.
+ * That means that they operate independently.
+ *
+ * Looking at just a single one of those variables, it has four possible
+ * values. In normal use, we use the current_val of the variable to set
+ * up the patterns. We do that by calling nonempty_patterns() with the
+ * appropriate rflags. When editing configurations, we have the other two
+ * variables to deal with: main_user_val and post_user_val.
+ * We only ever deal with one of those at a time, so we re-use the variables.
+ * However, we do sometimes want to deal with one of those and at the same
+ * time refer to the current current_val. For example, if we are editing
+ * the post or main user_val for the filters variable, we still want
+ * to check for new mail. If we find new mail we'll want to call
+ * process_filter_patterns which uses the current_val for filter patterns.
+ * That means we have to provide for the case where we are using current_val
+ * at the same time as we're using one of the user_vals. That's why we have
+ * both the _ne variables (NonEmpty) and the _any variables.
+ *
+ * In any_patterns (and first_pattern...) use_flags may only be set to
+ * one value at a time, whereas rflags may be more than one value OR'd together.
+ */
+PAT_HANDLE **cur_pat_h;
+static PAT_HANDLE *pattern_h_roles_ne, *pattern_h_roles_any,
+ *pattern_h_scores_ne, *pattern_h_scores_any,
+ *pattern_h_filts_ne, *pattern_h_filts_any,
+ *pattern_h_filts_cfg,
+ *pattern_h_filts_ne, *pattern_h_filts_any,
+ *pattern_h_incol_ne, *pattern_h_incol_any,
+ *pattern_h_other_ne, *pattern_h_other_any,
+ *pattern_h_srch_ne, *pattern_h_srch_any,
+ *pattern_h_oldpat_ne, *pattern_h_oldpat_any;
+
+/*
+ * These contain the PAT_OPEN_MASK open status and the PAT_USE_MASK use status.
+ */
+static long *cur_pat_status;
+static long pat_status_roles_ne, pat_status_roles_any,
+ pat_status_scores_ne, pat_status_scores_any,
+ pat_status_filts_ne, pat_status_filts_any,
+ pat_status_incol_ne, pat_status_incol_any,
+ pat_status_other_ne, pat_status_other_any,
+ pat_status_srch_ne, pat_status_srch_any,
+ pat_status_oldpat_ne, pat_status_oldpat_any,
+ pat_status_oldfilt_ne, pat_status_oldfilt_any,
+ pat_status_oldscore_ne, pat_status_oldscore_any;
+
+#define SET_PATTYPE(rflags) \
+ set_pathandle(rflags); \
+ cur_pat_status = \
+ ((rflags) & PAT_USE_CURRENT) \
+ ? (((rflags) & ROLE_DO_INCOLS) ? &pat_status_incol_ne : \
+ ((rflags) & ROLE_DO_OTHER) ? &pat_status_other_ne : \
+ ((rflags) & ROLE_DO_FILTER) ? &pat_status_filts_ne : \
+ ((rflags) & ROLE_DO_SCORES) ? &pat_status_scores_ne : \
+ ((rflags) & ROLE_DO_ROLES) ? &pat_status_roles_ne : \
+ ((rflags) & ROLE_DO_SRCH) ? &pat_status_srch_ne : \
+ ((rflags) & ROLE_OLD_FILT) ? &pat_status_oldfilt_ne : \
+ ((rflags) & ROLE_OLD_SCORE) ? &pat_status_oldscore_ne :\
+ &pat_status_oldpat_ne) \
+ : (((rflags) & ROLE_DO_INCOLS) ? &pat_status_incol_any : \
+ ((rflags) & ROLE_DO_OTHER) ? &pat_status_other_any : \
+ ((rflags) & ROLE_DO_FILTER) ? &pat_status_filts_any : \
+ ((rflags) & ROLE_DO_SCORES) ? &pat_status_scores_any : \
+ ((rflags) & ROLE_DO_ROLES) ? &pat_status_roles_any : \
+ ((rflags) & ROLE_DO_SRCH) ? &pat_status_srch_any : \
+ ((rflags) & ROLE_OLD_FILT) ? &pat_status_oldfilt_any :\
+ ((rflags) & ROLE_OLD_SCORE) ? &pat_status_oldscore_any:\
+ &pat_status_oldpat_any);
+#define CANONICAL_RFLAGS(rflags) \
+ ((((rflags) & (ROLE_DO_ROLES | ROLE_REPLY | ROLE_FORWARD | ROLE_COMPOSE)) \
+ ? ROLE_DO_ROLES : 0) | \
+ (((rflags) & (ROLE_DO_INCOLS | ROLE_INCOL)) \
+ ? ROLE_DO_INCOLS : 0) | \
+ (((rflags) & (ROLE_DO_SCORES | ROLE_SCORE)) \
+ ? ROLE_DO_SCORES : 0) | \
+ (((rflags) & (ROLE_DO_FILTER)) \
+ ? ROLE_DO_FILTER : 0) | \
+ (((rflags) & (ROLE_DO_OTHER)) \
+ ? ROLE_DO_OTHER : 0) | \
+ (((rflags) & (ROLE_DO_SRCH)) \
+ ? ROLE_DO_SRCH : 0) | \
+ (((rflags) & (ROLE_OLD_FILT)) \
+ ? ROLE_OLD_FILT : 0) | \
+ (((rflags) & (ROLE_OLD_SCORE)) \
+ ? ROLE_OLD_SCORE : 0) | \
+ (((rflags) & (ROLE_OLD_PAT)) \
+ ? ROLE_OLD_PAT : 0))
+
+#define SETPGMSTATUS(val,yes,no) \
+ switch(val){ \
+ case PAT_STAT_YES: \
+ (yes) = 1; \
+ break; \
+ case PAT_STAT_NO: \
+ (no) = 1; \
+ break; \
+ case PAT_STAT_EITHER: \
+ default: \
+ break; \
+ }
+
+#define SET_STATUS(srchin,srchfor,assignto) \
+ {char *qq, *pp; \
+ int ii; \
+ NAMEVAL_S *vv; \
+ if((qq = srchstr(srchin, srchfor)) != NULL){ \
+ if((pp = remove_pat_escapes(qq+strlen(srchfor))) != NULL){ \
+ for(ii = 0; (vv = role_status_types(ii)); ii++) \
+ if(!strucmp(pp, vv->shortname)){ \
+ assignto = vv->value; \
+ break; \
+ } \
+ \
+ fs_give((void **)&pp); \
+ } \
+ } \
+ }
+
+#define SET_MSGSTATE(srchin,srchfor,assignto) \
+ {char *qq, *pp; \
+ int ii; \
+ NAMEVAL_S *vv; \
+ if((qq = srchstr(srchin, srchfor)) != NULL){ \
+ if((pp = remove_pat_escapes(qq+strlen(srchfor))) != NULL){ \
+ for(ii = 0; (vv = msg_state_types(ii)); ii++) \
+ if(!strucmp(pp, vv->shortname)){ \
+ assignto = vv->value; \
+ break; \
+ } \
+ \
+ fs_give((void **)&pp); \
+ } \
+ } \
+ }
+
+#define PATTERN_N (9)
+
+
+void
+set_pathandle(long int rflags)
+{
+ cur_pat_h = (rflags & PAT_USE_CURRENT)
+ ? ((rflags & ROLE_DO_INCOLS) ? &pattern_h_incol_ne :
+ (rflags & ROLE_DO_OTHER) ? &pattern_h_other_ne :
+ (rflags & ROLE_DO_FILTER) ? &pattern_h_filts_ne :
+ (rflags & ROLE_DO_SCORES) ? &pattern_h_scores_ne :
+ (rflags & ROLE_DO_ROLES) ? &pattern_h_roles_ne :
+ (rflags & ROLE_DO_SRCH) ? &pattern_h_srch_ne :
+ &pattern_h_oldpat_ne)
+ : ((rflags & PAT_USE_CHANGED)
+ ? &pattern_h_filts_cfg
+ : ((rflags & ROLE_DO_INCOLS) ? &pattern_h_incol_any :
+ (rflags & ROLE_DO_OTHER) ? &pattern_h_other_any :
+ (rflags & ROLE_DO_FILTER) ? &pattern_h_filts_any :
+ (rflags & ROLE_DO_SCORES) ? &pattern_h_scores_any :
+ (rflags & ROLE_DO_ROLES) ? &pattern_h_roles_any :
+ (rflags & ROLE_DO_SRCH) ? &pattern_h_srch_any :
+ &pattern_h_oldpat_any));
+}
+
+
+/*
+ * Rflags may be more than one pattern type OR'd together. It also contains
+ * the "use" parameter.
+ */
+void
+open_any_patterns(long int rflags)
+{
+ long canon_rflags;
+
+ dprint((7, "open_any_patterns(0x%x)\n", rflags));
+
+ canon_rflags = CANONICAL_RFLAGS(rflags);
+
+ if(canon_rflags & ROLE_DO_INCOLS)
+ sub_open_any_patterns(ROLE_DO_INCOLS | (rflags & PAT_USE_MASK));
+ if(canon_rflags & ROLE_DO_FILTER)
+ sub_open_any_patterns(ROLE_DO_FILTER | (rflags & PAT_USE_MASK));
+ if(canon_rflags & ROLE_DO_OTHER)
+ sub_open_any_patterns(ROLE_DO_OTHER | (rflags & PAT_USE_MASK));
+ if(canon_rflags & ROLE_DO_SCORES)
+ sub_open_any_patterns(ROLE_DO_SCORES | (rflags & PAT_USE_MASK));
+ if(canon_rflags & ROLE_DO_ROLES)
+ sub_open_any_patterns(ROLE_DO_ROLES | (rflags & PAT_USE_MASK));
+ if(canon_rflags & ROLE_DO_SRCH)
+ sub_open_any_patterns(ROLE_DO_SRCH | (rflags & PAT_USE_MASK));
+ if(canon_rflags & ROLE_OLD_FILT)
+ sub_open_any_patterns(ROLE_OLD_FILT | (rflags & PAT_USE_MASK));
+ if(canon_rflags & ROLE_OLD_SCORE)
+ sub_open_any_patterns(ROLE_OLD_SCORE | (rflags & PAT_USE_MASK));
+ if(canon_rflags & ROLE_OLD_PAT)
+ sub_open_any_patterns(ROLE_OLD_PAT | (rflags & PAT_USE_MASK));
+}
+
+
+/*
+ * This should only be called with a single pattern type (plus use flags).
+ * We assume that patterns of this type are closed before this is called.
+ * This always succeeds unless we run out of memory, in which case fs_get
+ * never returns.
+ */
+void
+sub_open_any_patterns(long int rflags)
+{
+ PAT_LINE_S *patline = NULL, *pl = NULL;
+ char **t = NULL;
+ struct variable *var;
+
+ SET_PATTYPE(rflags);
+
+ *cur_pat_h = (PAT_HANDLE *)fs_get(sizeof(**cur_pat_h));
+ memset((void *)*cur_pat_h, 0, sizeof(**cur_pat_h));
+
+ if(rflags & ROLE_DO_ROLES)
+ var = &ps_global->vars[V_PAT_ROLES];
+ else if(rflags & ROLE_DO_FILTER)
+ var = &ps_global->vars[V_PAT_FILTS];
+ else if(rflags & ROLE_DO_OTHER)
+ var = &ps_global->vars[V_PAT_OTHER];
+ else if(rflags & ROLE_DO_SCORES)
+ var = &ps_global->vars[V_PAT_SCORES];
+ else if(rflags & ROLE_DO_INCOLS)
+ var = &ps_global->vars[V_PAT_INCOLS];
+ else if(rflags & ROLE_DO_SRCH)
+ var = &ps_global->vars[V_PAT_SRCH];
+ else if(rflags & ROLE_OLD_FILT)
+ var = &ps_global->vars[V_PAT_FILTS_OLD];
+ else if(rflags & ROLE_OLD_SCORE)
+ var = &ps_global->vars[V_PAT_SCORES_OLD];
+ else if(rflags & ROLE_OLD_PAT)
+ var = &ps_global->vars[V_PATTERNS];
+
+ switch(rflags & PAT_USE_MASK){
+ case PAT_USE_CURRENT:
+ t = var->current_val.l;
+ break;
+ case PAT_USE_CHANGED:
+ /*
+ * some trickery to only use changed if actually changed.
+ * otherwise, use current_val
+ */
+ t = var->is_changed_val ? var->changed_val.l : var->current_val.l;
+ break;
+ case PAT_USE_MAIN:
+ t = var->main_user_val.l;
+ break;
+ case PAT_USE_POST:
+ t = var->post_user_val.l;
+ break;
+ }
+
+ if(t){
+ for(; t[0] && t[0][0]; t++){
+ if(*t && !strncmp("LIT:", *t, 4))
+ patline = parse_pat_lit(*t + 4);
+ else if(*t && !strncmp("FILE:", *t, 5))
+ patline = parse_pat_file(*t + 5);
+ else if(rflags & (PAT_USE_MAIN | PAT_USE_POST) &&
+ patline == NULL && *t && !strcmp(INHERIT, *t))
+ patline = parse_pat_inherit();
+ else
+ patline = NULL;
+
+ if(patline){
+ if(pl){
+ pl->next = patline;
+ patline->prev = pl;
+ pl = pl->next;
+ }
+ else{
+ (*cur_pat_h)->patlinehead = patline;
+ pl = patline;
+ }
+ }
+ else
+ q_status_message1(SM_ORDER, 0, 3,
+ "Invalid patterns line \"%.200s\"", *t);
+ }
+ }
+
+ *cur_pat_status = PAT_OPENED | (rflags & PAT_USE_MASK);
+}
+
+
+void
+close_every_pattern(void)
+{
+ close_patterns(ROLE_DO_INCOLS | ROLE_DO_FILTER | ROLE_DO_SCORES
+ | ROLE_DO_OTHER | ROLE_DO_ROLES | ROLE_DO_SRCH
+ | ROLE_OLD_FILT | ROLE_OLD_SCORE | ROLE_OLD_PAT
+ | PAT_USE_CURRENT);
+ /*
+ * Since there is only one set of variables for the other three uses
+ * we can just close any one of them. There can only be one open at
+ * a time.
+ */
+ close_patterns(ROLE_DO_INCOLS | ROLE_DO_FILTER | ROLE_DO_SCORES
+ | ROLE_DO_OTHER | ROLE_DO_ROLES | ROLE_DO_SRCH
+ | ROLE_OLD_FILT | ROLE_OLD_SCORE | ROLE_OLD_PAT
+ | PAT_USE_MAIN);
+}
+
+
+/*
+ * Can be called with more than one pattern type.
+ */
+void
+close_patterns(long int rflags)
+{
+ long canon_rflags;
+
+ dprint((7, "close_patterns(0x%x)\n", rflags));
+
+ canon_rflags = CANONICAL_RFLAGS(rflags);
+
+ if(canon_rflags & ROLE_DO_INCOLS)
+ sub_close_patterns(ROLE_DO_INCOLS | (rflags & PAT_USE_MASK));
+ if(canon_rflags & ROLE_DO_OTHER)
+ sub_close_patterns(ROLE_DO_OTHER | (rflags & PAT_USE_MASK));
+ if(canon_rflags & ROLE_DO_FILTER)
+ sub_close_patterns(ROLE_DO_FILTER | (rflags & PAT_USE_MASK));
+ if(canon_rflags & ROLE_DO_SCORES)
+ sub_close_patterns(ROLE_DO_SCORES | (rflags & PAT_USE_MASK));
+ if(canon_rflags & ROLE_DO_ROLES)
+ sub_close_patterns(ROLE_DO_ROLES | (rflags & PAT_USE_MASK));
+ if(canon_rflags & ROLE_DO_SRCH)
+ sub_close_patterns(ROLE_DO_SRCH | (rflags & PAT_USE_MASK));
+ if(canon_rflags & ROLE_OLD_FILT)
+ sub_close_patterns(ROLE_OLD_FILT | (rflags & PAT_USE_MASK));
+ if(canon_rflags & ROLE_OLD_SCORE)
+ sub_close_patterns(ROLE_OLD_SCORE | (rflags & PAT_USE_MASK));
+ if(canon_rflags & ROLE_OLD_PAT)
+ sub_close_patterns(ROLE_OLD_PAT | (rflags & PAT_USE_MASK));
+}
+
+
+/*
+ * Can be called with only a single pattern type.
+ */
+void
+sub_close_patterns(long int rflags)
+{
+ SET_PATTYPE(rflags);
+
+ if(*cur_pat_h != NULL){
+ free_patline(&(*cur_pat_h)->patlinehead);
+ fs_give((void **)cur_pat_h);
+ }
+
+ *cur_pat_status = PAT_CLOSED;
+
+ scores_are_used(SCOREUSE_INVALID);
+}
+
+
+/*
+ * Can be called with more than one pattern type.
+ * Nonempty always uses PAT_USE_CURRENT (the current_val).
+ */
+int
+nonempty_patterns(long int rflags, PAT_STATE *pstate)
+{
+ return(any_patterns((rflags & ROLE_MASK) | PAT_USE_CURRENT, pstate));
+}
+
+
+/*
+ * Initializes pstate and parses and sets up appropriate pattern variables.
+ * May be called with more than one pattern type OR'd together in rflags.
+ * Pstate will keep track of that and next_pattern et. al. will increment
+ * through all of those pattern types.
+ */
+int
+any_patterns(long int rflags, PAT_STATE *pstate)
+{
+ int ret = 0;
+ long canon_rflags;
+
+ dprint((7, "any_patterns(0x%x)\n", rflags));
+
+ memset((void *)pstate, 0, sizeof(*pstate));
+ pstate->rflags = rflags;
+
+ canon_rflags = CANONICAL_RFLAGS(pstate->rflags);
+
+ if(canon_rflags & ROLE_DO_INCOLS)
+ ret += sub_any_patterns(ROLE_DO_INCOLS, pstate);
+ if(canon_rflags & ROLE_DO_OTHER)
+ ret += sub_any_patterns(ROLE_DO_OTHER, pstate);
+ if(canon_rflags & ROLE_DO_FILTER)
+ ret += sub_any_patterns(ROLE_DO_FILTER, pstate);
+ if(canon_rflags & ROLE_DO_SCORES)
+ ret += sub_any_patterns(ROLE_DO_SCORES, pstate);
+ if(canon_rflags & ROLE_DO_ROLES)
+ ret += sub_any_patterns(ROLE_DO_ROLES, pstate);
+ if(canon_rflags & ROLE_DO_SRCH)
+ ret += sub_any_patterns(ROLE_DO_SRCH, pstate);
+ if(canon_rflags & ROLE_OLD_FILT)
+ ret += sub_any_patterns(ROLE_OLD_FILT, pstate);
+ if(canon_rflags & ROLE_OLD_SCORE)
+ ret += sub_any_patterns(ROLE_OLD_SCORE, pstate);
+ if(canon_rflags & ROLE_OLD_PAT)
+ ret += sub_any_patterns(ROLE_OLD_PAT, pstate);
+
+ return(ret);
+}
+
+
+int
+sub_any_patterns(long int rflags, PAT_STATE *pstate)
+{
+ SET_PATTYPE(rflags | (pstate->rflags & PAT_USE_MASK));
+
+ if(*cur_pat_h &&
+ (((pstate->rflags & PAT_USE_MASK) == PAT_USE_CURRENT &&
+ (*cur_pat_status & PAT_USE_MASK) != PAT_USE_CURRENT) ||
+ ((pstate->rflags & PAT_USE_MASK) != PAT_USE_CURRENT &&
+ ((*cur_pat_status & PAT_OPEN_MASK) != PAT_OPENED ||
+ (*cur_pat_status & PAT_USE_MASK) !=
+ (pstate->rflags & PAT_USE_MASK)))))
+ close_patterns(rflags | (pstate->rflags & PAT_USE_MASK));
+
+ /* open_any always succeeds */
+ if(!*cur_pat_h && ((*cur_pat_status & PAT_OPEN_MASK) == PAT_CLOSED))
+ open_any_patterns(rflags | (pstate->rflags & PAT_USE_MASK));
+
+ if(!*cur_pat_h){ /* impossible */
+ *cur_pat_status = PAT_CLOSED;
+ return(0);
+ }
+
+ /*
+ * Opening nonempty can fail. That just means there aren't any
+ * patterns of that type.
+ */
+ if((pstate->rflags & PAT_USE_MASK) == PAT_USE_CURRENT &&
+ !(*cur_pat_h)->patlinehead)
+ *cur_pat_status = (PAT_OPEN_FAILED | PAT_USE_CURRENT);
+
+ return(((*cur_pat_status & PAT_OPEN_MASK) == PAT_OPENED) ? 1 : 0);
+}
+
+
+int
+edit_pattern(PAT_S *newpat, int pos, long int rflags)
+{
+ PAT_S *oldpat;
+ PAT_LINE_S *tpatline;
+ int i;
+ PAT_STATE pstate;
+
+ if(!any_patterns(rflags, &pstate)) return(1);
+
+ for(i = 0, tpatline = (*cur_pat_h)->patlinehead;
+ i < pos && tpatline; tpatline = tpatline->next, i++);
+ if(i != pos) return(1);
+ oldpat = tpatline->first;
+ free_pat(&oldpat);
+ tpatline->first = tpatline->last = newpat;
+ newpat->patline = tpatline;
+ tpatline->dirty = 1;
+
+ (*cur_pat_h)->dirtypinerc = 1;
+ write_patterns(rflags);
+
+ return(0);
+}
+
+int
+add_pattern(PAT_S *newpat, long int rflags)
+{
+ PAT_LINE_S *tpatline, *newpatline;
+ PAT_STATE pstate;
+
+ any_patterns(rflags, &pstate);
+
+ for(tpatline = (*cur_pat_h)->patlinehead;
+ tpatline && tpatline->next ; tpatline = tpatline->next);
+ newpatline = (PAT_LINE_S *)fs_get(sizeof(PAT_LINE_S));
+ if(tpatline)
+ tpatline->next = newpatline;
+ else
+ (*cur_pat_h)->patlinehead = newpatline;
+ memset((void *)newpatline, 0, sizeof(PAT_LINE_S));
+ newpatline->prev = tpatline;
+ newpatline->first = newpatline->last = newpat;
+ newpatline->type = Literal;
+ newpat->patline = newpatline;
+ newpatline->dirty = 1;
+
+ (*cur_pat_h)->dirtypinerc = 1;
+ write_patterns(rflags);
+
+ return(0);
+}
+
+int
+delete_pattern(int pos, long int rflags)
+{
+ PAT_LINE_S *tpatline;
+ int i;
+ PAT_STATE pstate;
+
+ if(!any_patterns(rflags, &pstate)) return(1);
+
+ for(i = 0, tpatline = (*cur_pat_h)->patlinehead;
+ i < pos && tpatline; tpatline = tpatline->next, i++);
+ if(i != pos) return(1);
+
+ if(tpatline == (*cur_pat_h)->patlinehead)
+ (*cur_pat_h)->patlinehead = tpatline->next;
+ if(tpatline->prev) tpatline->prev->next = tpatline->next;
+ if(tpatline->next) tpatline->next->prev = tpatline->prev;
+ tpatline->prev = NULL;
+ tpatline->next = NULL;
+
+ free_patline(&tpatline);
+
+ (*cur_pat_h)->dirtypinerc = 1;
+ write_patterns(rflags);
+
+ return(0);
+}
+
+int
+shuffle_pattern(int pos, int up, long int rflags)
+{
+ PAT_LINE_S *tpatline, *shufpatline;
+ int i;
+ PAT_STATE pstate;
+
+ if(!any_patterns(rflags, &pstate)) return(1);
+
+ for(i = 0, tpatline = (*cur_pat_h)->patlinehead;
+ i < pos && tpatline; tpatline = tpatline->next, i++);
+ if(i != pos) return(1);
+
+ if(up == 1){
+ if(tpatline->prev == NULL) return(1);
+ shufpatline = tpatline->prev;
+ tpatline->prev = shufpatline->prev;
+ if(shufpatline->prev)
+ shufpatline->prev->next = tpatline;
+ if(tpatline->next)
+ tpatline->next->prev = shufpatline;
+ shufpatline->next = tpatline->next;
+ shufpatline->prev = tpatline;
+ tpatline->next = shufpatline;
+ if(shufpatline == (*cur_pat_h)->patlinehead)
+ (*cur_pat_h)->patlinehead = tpatline;
+ }
+ else if(up == -1){
+ if(tpatline->next == NULL) return(1);
+ shufpatline = tpatline->next;
+ tpatline->next = shufpatline->next;
+ if(shufpatline->next)
+ shufpatline->next->prev = tpatline;
+ if(tpatline->prev)
+ tpatline->prev->next = shufpatline;
+ shufpatline->prev = tpatline->prev;
+ shufpatline->next = tpatline;
+ tpatline->prev = shufpatline;
+ if(tpatline == (*cur_pat_h)->patlinehead)
+ (*cur_pat_h)->patlinehead = shufpatline;
+ }
+ else return(1);
+
+ shufpatline->dirty = 1;
+ tpatline->dirty = 1;
+
+ (*cur_pat_h)->dirtypinerc = 1;
+ write_patterns(rflags);
+
+ return(0);
+}
+
+PAT_LINE_S *
+parse_pat_lit(char *litpat)
+{
+ PAT_LINE_S *patline;
+ PAT_S *pat;
+
+ patline = (PAT_LINE_S *)fs_get(sizeof(*patline));
+ memset((void *)patline, 0, sizeof(*patline));
+ patline->type = Literal;
+
+
+ if((pat = parse_pat(litpat)) != NULL){
+ pat->patline = patline;
+ patline->first = pat;
+ patline->last = pat;
+ }
+
+ return(patline);
+}
+
+
+/*
+ * This always returns a patline even if we can't read the file. The patline
+ * returned will say readonly in the worst case and there will be no patterns.
+ * If the file doesn't exist, this creates it if possible.
+ */
+PAT_LINE_S *
+parse_pat_file(char *filename)
+{
+#define BUF_SIZE 5000
+ PAT_LINE_S *patline;
+ PAT_S *pat, *p;
+ char path[MAXPATH+1], buf[BUF_SIZE];
+ char *dir, *q;
+ FILE *fp;
+ int ok = 0, some_pats = 0;
+ struct variable *vars = ps_global->vars;
+
+ signature_path(filename, path, MAXPATH);
+
+ if(VAR_OPER_DIR && !in_dir(VAR_OPER_DIR, path)){
+ q_status_message1(SM_ORDER | SM_DING, 3, 4,
+ "Can't use Roles file outside of %.200s",
+ VAR_OPER_DIR);
+ return(NULL);
+ }
+
+ patline = (PAT_LINE_S *)fs_get(sizeof(*patline));
+ memset((void *)patline, 0, sizeof(*patline));
+ patline->type = File;
+ patline->filename = cpystr(filename);
+ patline->filepath = cpystr(path);
+
+ if((q = last_cmpnt(path)) != NULL){
+ int save;
+
+ save = *--q;
+ *q = '\0';
+ dir = cpystr(*path ? path : "/");
+ *q = save;
+ }
+ else
+ dir = cpystr(".");
+
+#if defined(DOS) || defined(OS2)
+ /*
+ * If the dir has become a drive letter and : (e.g. "c:")
+ * then append a "\". The library function access() in the
+ * win 16 version of MSC seems to require this.
+ */
+ if(isalpha((unsigned char) *dir)
+ && *(dir+1) == ':' && *(dir+2) == '\0'){
+ *(dir+2) = '\\';
+ *(dir+3) = '\0';
+ }
+#endif /* DOS || OS2 */
+
+ /*
+ * Even if we can edit the file itself, we aren't going
+ * to be able to change it unless we can also write in
+ * the directory that contains it (because we write into a
+ * temp file and then rename).
+ */
+ if(can_access(dir, EDIT_ACCESS) != 0)
+ patline->readonly = 1;
+
+ if(can_access(path, EDIT_ACCESS) == 0){
+ if(patline->readonly)
+ q_status_message1(SM_ORDER, 0, 3,
+ "Pattern file directory (%.200s) is ReadOnly", dir);
+ }
+ else if(can_access(path, READ_ACCESS) == 0)
+ patline->readonly = 1;
+
+ if(can_access(path, ACCESS_EXISTS) == 0){
+ if((fp = our_fopen(path, "rb")) != NULL){
+ /* Check to see if this is a valid patterns file */
+ if(fp_file_size(fp) <= 0L)
+ ok++;
+ else{
+ size_t len;
+
+ len = strlen(PATTERN_MAGIC);
+ if(fread(buf, sizeof(char), len+3, fp) == len+3){
+ buf[len+3] = '\0';
+ buf[len] = '\0';
+ if(strcmp(buf, PATTERN_MAGIC) == 0){
+ if(atoi(PATTERN_FILE_VERS) < atoi(buf + len + 1))
+ q_status_message1(SM_ORDER, 0, 4,
+ "Pattern file \"%.200s\" is made by newer Alpine, will try to use it anyway",
+ filename);
+
+ ok++;
+ some_pats++;
+ /* toss rest of first line */
+ (void)fgets(buf, BUF_SIZE, fp);
+ }
+ }
+ }
+
+ if(!ok){
+ patline->readonly = 1;
+ q_status_message1(SM_ORDER | SM_DING, 3, 4,
+ "\"%.200s\" is not a Pattern file", path);
+ }
+
+ p = NULL;
+ while(some_pats && fgets(buf, BUF_SIZE, fp) != NULL){
+ if((pat = parse_pat(buf)) != NULL){
+ pat->patline = patline;
+ if(!patline->first)
+ patline->first = pat;
+
+ patline->last = pat;
+
+ if(p){
+ p->next = pat;
+ pat->prev = p;
+ p = p->next;
+ }
+ else
+ p = pat;
+ }
+ }
+
+ (void)fclose(fp);
+ }
+ else{
+ patline->readonly = 1;
+ q_status_message2(SM_ORDER | SM_DING, 3, 4,
+ "Error \"%.200s\" reading pattern file \"%.200s\"",
+ error_description(errno), path);
+ }
+ }
+ else{ /* doesn't exist yet, try to create it */
+ if(patline->readonly)
+ q_status_message1(SM_ORDER, 0, 3,
+ "Pattern file directory (%.200s) is ReadOnly", dir);
+ else{
+ /*
+ * We try to create it by making up an empty patline and calling
+ * write_pattern_file.
+ */
+ patline->dirty = 1;
+ if(write_pattern_file(NULL, patline) != 0){
+ patline->readonly = 1;
+ patline->dirty = 0;
+ q_status_message1(SM_ORDER | SM_DING, 3, 4,
+ "Error creating pattern file \"%.200s\"",
+ path);
+ }
+ }
+ }
+
+ if(dir)
+ fs_give((void **)&dir);
+
+ return(patline);
+}
+
+
+PAT_LINE_S *
+parse_pat_inherit(void)
+{
+ PAT_LINE_S *patline;
+ PAT_S *pat;
+
+ patline = (PAT_LINE_S *)fs_get(sizeof(*patline));
+ memset((void *)patline, 0, sizeof(*patline));
+ patline->type = Inherit;
+
+ pat = (PAT_S *)fs_get(sizeof(*pat));
+ memset((void *)pat, 0, sizeof(*pat));
+ pat->inherit = 1;
+
+ pat->patline = patline;
+ patline->first = pat;
+ patline->last = pat;
+
+ return(patline);
+}
+
+
+/*
+ * There are three forms that a PATTERN_S has at various times. There is
+ * the actual PATTERN_S struct which is used internally and is used whenever
+ * we are actually doing something with the pattern, like filtering or
+ * something. There is the version that goes in the config file. And there
+ * is the version the user edits.
+ *
+ * To go between these three forms we have the helper routines
+ *
+ * pattern_to_config
+ * config_to_pattern
+ * pattern_to_editlist
+ * editlist_to_pattern
+ *
+ * Here's what is supposed to be happening. A PATTERN_S is a linked list
+ * of strings with nothing escaped. That is, a backslash or a comma is
+ * just in there as a backslash or comma.
+ *
+ * The version the user edits is very similar. Because we have historically
+ * used commas as separators the user has always had to enter a \, in order
+ * to put a real comma in one of the items. That is the only difference
+ * between a PATTERN_S string and the editlist strings. Note that backslashes
+ * themselves are not escaped. A backslash which is not followed by a comma
+ * is a backslash. It doesn't escape the following character. That's a bit
+ * odd, it is that way because most people will never know about this
+ * backslash stuff but PC-Pine users may have backslashes in folder names.
+ *
+ * The version that goes in the config file has a few transformations made.
+ * PATTERN_S intermediate_form Config string
+ * , \, \x2C
+ * \ \\ \x5C
+ * / \/
+ * " \"
+ *
+ * The commas are turned into hex commas so that we can tell the separators
+ * in the comma-separated lists from those commas.
+ * The backslashes are escaped because they escape commas.
+ * The /'s are escaped because they separate pattern pieces.
+ * The "'s are escaped because they are significant to parse_list when
+ * parsing the config file.
+ * hubert - 2004-04-01
+ * (date is only coincidental!)
+ *
+ * Addendum. The not's are handled separately from all the strings. Not sure
+ * why that is or if there is a good reason. Nevertheless, now is not the
+ * time to figure it out so leave it that way.
+ * hubert - 2004-07-14
+ */
+PAT_S *
+parse_pat(char *str)
+{
+ PAT_S *pat = NULL;
+ char *p, *q, *astr, *pstr;
+ int backslashed;
+#define PTRN "pattern="
+#define PTRNLEN 8
+#define ACTN "action="
+#define ACTNLEN 7
+
+ if(str)
+ removing_trailing_white_space(str);
+
+ if(!str || !*str || *str == '#')
+ return(pat);
+
+ pat = (PAT_S *)fs_get(sizeof(*pat));
+ memset((void *)pat, 0, sizeof(*pat));
+
+ if((p = srchstr(str, PTRN)) != NULL){
+ pat->patgrp = (PATGRP_S *)fs_get(sizeof(*pat->patgrp));
+ memset((void *)pat->patgrp, 0, sizeof(*pat->patgrp));
+ pat->patgrp->fldr_type = FLDR_DEFL;
+ pat->patgrp->inabook = IAB_DEFL;
+ pat->patgrp->cat_lim = -1L;
+
+ if((pstr = copy_quoted_string_asis(p+PTRNLEN)) != NULL){
+ /* move to next slash */
+ for(q=pstr, backslashed=0; *q; q++){
+ switch(*q){
+ case '\\':
+ backslashed = !backslashed;
+ break;
+
+ case '/':
+ if(!backslashed){
+ parse_patgrp_slash(q, pat->patgrp);
+ if(pat->patgrp->bogus && !pat->raw)
+ pat->raw = cpystr(str);
+ }
+
+ /* fall through */
+
+ default:
+ backslashed = 0;
+ break;
+ }
+ }
+
+ /* we always force a nickname */
+ if(!pat->patgrp->nick)
+ pat->patgrp->nick = cpystr("Alternate Role");
+
+ fs_give((void **)&pstr);
+ }
+ }
+
+ if((p = srchstr(str, ACTN)) != NULL){
+ pat->action = (ACTION_S *)fs_get(sizeof(*pat->action));
+ memset((void *)pat->action, 0, sizeof(*pat->action));
+ pat->action->startup_rule = IS_NOTSET;
+ pat->action->repl_type = ROLE_REPL_DEFL;
+ pat->action->forw_type = ROLE_FORW_DEFL;
+ pat->action->comp_type = ROLE_COMP_DEFL;
+ pat->action->nick = cpystr((pat->patgrp && pat->patgrp->nick
+ && pat->patgrp->nick[0])
+ ? pat->patgrp->nick : "Alternate Role");
+
+ if((astr = copy_quoted_string_asis(p+ACTNLEN)) != NULL){
+ /* move to next slash */
+ for(q=astr, backslashed=0; *q; q++){
+ switch(*q){
+ case '\\':
+ backslashed = !backslashed;
+ break;
+
+ case '/':
+ if(!backslashed){
+ parse_action_slash(q, pat->action);
+ if(pat->action->bogus && !pat->raw)
+ pat->raw = cpystr(str);
+ }
+
+ /* fall through */
+
+ default:
+ backslashed = 0;
+ break;
+ }
+ }
+
+ fs_give((void **)&astr);
+
+ if(!pat->action->is_a_score)
+ pat->action->scoreval = 0L;
+
+ if(pat->action->is_a_filter)
+ pat->action->kill = (pat->action->folder
+ || pat->action->kill == -1) ? 0 : 1;
+ else{
+ if(pat->action->folder)
+ free_pattern(&pat->action->folder);
+ }
+
+ if(!pat->action->is_a_role){
+ pat->action->repl_type = ROLE_NOTAROLE_DEFL;
+ pat->action->forw_type = ROLE_NOTAROLE_DEFL;
+ pat->action->comp_type = ROLE_NOTAROLE_DEFL;
+ if(pat->action->from)
+ mail_free_address(&pat->action->from);
+ if(pat->action->replyto)
+ mail_free_address(&pat->action->replyto);
+ if(pat->action->fcc)
+ fs_give((void **)&pat->action->fcc);
+ if(pat->action->litsig)
+ fs_give((void **)&pat->action->litsig);
+ if(pat->action->sig)
+ fs_give((void **)&pat->action->sig);
+ if(pat->action->template)
+ fs_give((void **)&pat->action->template);
+ if(pat->action->cstm)
+ free_list_array(&pat->action->cstm);
+ if(pat->action->smtp)
+ free_list_array(&pat->action->smtp);
+ if(pat->action->nntp)
+ free_list_array(&pat->action->nntp);
+ if(pat->action->inherit_nick)
+ fs_give((void **)&pat->action->inherit_nick);
+ }
+
+ if(!pat->action->is_a_incol){
+ if(pat->action->incol)
+ free_color_pair(&pat->action->incol);
+ }
+
+ if(!pat->action->is_a_other){
+ pat->action->sort_is_set = 0;
+ pat->action->sortorder = 0;
+ pat->action->revsort = 0;
+ pat->action->startup_rule = IS_NOTSET;
+ if(pat->action->index_format)
+ fs_give((void **)&pat->action->index_format);
+ }
+ }
+ }
+
+ return(pat);
+}
+
+
+/*
+ * Fill in one member of patgrp from str.
+ *
+ * The multiple constant strings are lame but it evolved this way from
+ * previous versions and isn't worth fixing.
+ */
+void
+parse_patgrp_slash(char *str, PATGRP_S *patgrp)
+{
+ char *p;
+
+ if(!patgrp)
+ panic("NULL patgrp to parse_patgrp_slash");
+ else if(!(str && *str)){
+ panic("NULL or empty string to parse_patgrp_slash");
+ patgrp->bogus = 1;
+ }
+ else if(!strncmp(str, "/NICK=", 6))
+ patgrp->nick = remove_pat_escapes(str+6);
+ else if(!strncmp(str, "/COMM=", 6))
+ patgrp->comment = remove_pat_escapes(str+6);
+ else if(!strncmp(str, "/TO=", 4) || !strncmp(str, "/!TO=", 5))
+ patgrp->to = parse_pattern("TO", str, 1);
+ else if(!strncmp(str, "/CC=", 4) || !strncmp(str, "/!CC=", 5))
+ patgrp->cc = parse_pattern("CC", str, 1);
+ else if(!strncmp(str, "/RECIP=", 7) || !strncmp(str, "/!RECIP=", 8))
+ patgrp->recip = parse_pattern("RECIP", str, 1);
+ else if(!strncmp(str, "/PARTIC=", 8) || !strncmp(str, "/!PARTIC=", 9))
+ patgrp->partic = parse_pattern("PARTIC", str, 1);
+ else if(!strncmp(str, "/FROM=", 6) || !strncmp(str, "/!FROM=", 7))
+ patgrp->from = parse_pattern("FROM", str, 1);
+ else if(!strncmp(str, "/SENDER=", 8) || !strncmp(str, "/!SENDER=", 9))
+ patgrp->sender = parse_pattern("SENDER", str, 1);
+ else if(!strncmp(str, "/NEWS=", 6) || !strncmp(str, "/!NEWS=", 7))
+ patgrp->news = parse_pattern("NEWS", str, 1);
+ else if(!strncmp(str, "/SUBJ=", 6) || !strncmp(str, "/!SUBJ=", 7))
+ patgrp->subj = parse_pattern("SUBJ", str, 1);
+ else if(!strncmp(str, "/ALL=", 5) || !strncmp(str, "/!ALL=", 6))
+ patgrp->alltext = parse_pattern("ALL", str, 1);
+ else if(!strncmp(str, "/BODY=", 6) || !strncmp(str, "/!BODY=", 7))
+ patgrp->bodytext = parse_pattern("BODY", str, 1);
+ else if(!strncmp(str, "/KEY=", 5) || !strncmp(str, "/!KEY=", 6))
+ patgrp->keyword = parse_pattern("KEY", str, 1);
+ else if(!strncmp(str, "/CHAR=", 6) || !strncmp(str, "/!CHAR=", 7))
+ patgrp->charsets = parse_pattern("CHAR", str, 1);
+ else if(!strncmp(str, "/FOLDER=", 8) || !strncmp(str, "/!FOLDER=", 9))
+ patgrp->folder = parse_pattern("FOLDER", str, 1);
+ else if(!strncmp(str, "/ABOOKS=", 8) || !strncmp(str, "/!ABOOKS=", 9))
+ patgrp->abooks = parse_pattern("ABOOKS", str, 1);
+ /*
+ * A problem with arbhdrs is that more than one of them can appear in
+ * the string. We come back here the second time, but we already took
+ * care of the whole thing on the first pass. Hence the check for
+ * arbhdr already set.
+ */
+ else if(!strncmp(str, "/ARB", 4) || !strncmp(str, "/!ARB", 5)
+ || !strncmp(str, "/EARB", 5) || !strncmp(str, "/!EARB", 6)){
+ if(!patgrp->arbhdr)
+ patgrp->arbhdr = parse_arbhdr(str);
+ /* else do nothing */
+ }
+ else if(!strncmp(str, "/SENTDATE=", 10))
+ patgrp->age_uses_sentdate = 1;
+ else if(!strncmp(str, "/SCOREI=", 8)){
+ if((p = remove_pat_escapes(str+8)) != NULL){
+ if((patgrp->score = parse_intvl(p)) != NULL)
+ patgrp->do_score = 1;
+
+ fs_give((void **)&p);
+ }
+ }
+ else if(!strncmp(str, "/AGE=", 5)){
+ if((p = remove_pat_escapes(str+5)) != NULL){
+ if((patgrp->age = parse_intvl(p)) != NULL)
+ patgrp->do_age = 1;
+
+ fs_give((void **)&p);
+ }
+ }
+ else if(!strncmp(str, "/SIZE=", 6)){
+ if((p = remove_pat_escapes(str+6)) != NULL){
+ if((patgrp->size = parse_intvl(p)) != NULL)
+ patgrp->do_size = 1;
+
+ fs_give((void **)&p);
+ }
+ }
+ else if(!strncmp(str, "/CATCMD=", 8)){
+ if((p = remove_pat_escapes(str+8)) != NULL){
+ int commas = 0;
+ char *q;
+
+ /* count elements in list */
+ for(q = p; q && *q; q++)
+ if(*q == ',')
+ commas++;
+
+ patgrp->category_cmd = parse_list(p, commas+1, PL_REMSURRQUOT,NULL);
+ fs_give((void **)&p);
+ }
+ }
+ else if(!strncmp(str, "/CATVAL=", 8)){
+ if((p = remove_pat_escapes(str+8)) != NULL){
+ if((patgrp->cat = parse_intvl(p)) != NULL)
+ patgrp->do_cat = 1;
+
+ fs_give((void **)&p);
+ }
+ }
+ else if(!strncmp(str, "/CATLIM=", 8)){
+ if((p = remove_pat_escapes(str+8)) != NULL){
+ long i;
+
+ i = atol(p);
+ patgrp->cat_lim = i;
+ fs_give((void **)&p);
+ }
+ }
+ else if(!strncmp(str, "/FLDTYPE=", 9)){
+ if((p = remove_pat_escapes(str+9)) != NULL){
+ int i;
+ NAMEVAL_S *v;
+
+ for(i = 0; (v = pat_fldr_types(i)); i++)
+ if(!strucmp(p, v->shortname)){
+ patgrp->fldr_type = v->value;
+ break;
+ }
+
+ fs_give((void **)&p);
+ }
+ }
+ else if(!strncmp(str, "/AFROM=", 7)){
+ if((p = remove_pat_escapes(str+7)) != NULL){
+ int i;
+ NAMEVAL_S *v;
+
+ for(i = 0; (v = inabook_fldr_types(i)); i++)
+ if(!strucmp(p, v->shortname)){
+ patgrp->inabook |= v->value;
+ break;
+ }
+
+ /* to match old semantics */
+ patgrp->inabook |= IAB_FROM;
+ patgrp->inabook |= IAB_REPLYTO;
+
+ fs_give((void **)&p);
+ }
+ }
+ else if(!strncmp(str, "/AFROMA=", 8)){
+ if((p = remove_pat_escapes(str+8)) != NULL){
+
+ /* make sure AFROMA comes after AFROM in config lines */
+ patgrp->inabook &= ~IAB_ADDR_MASK;
+
+ if(strchr(p, 'F'))
+ patgrp->inabook |= IAB_FROM;
+ if(strchr(p, 'R'))
+ patgrp->inabook |= IAB_REPLYTO;
+ if(strchr(p, 'S'))
+ patgrp->inabook |= IAB_SENDER;
+ if(strchr(p, 'T'))
+ patgrp->inabook |= IAB_TO;
+ if(strchr(p, 'C'))
+ patgrp->inabook |= IAB_CC;
+
+ fs_give((void **)&p);
+ }
+ }
+ else if(!strncmp(str, "/STATN=", 7)){
+ SET_STATUS(str,"/STATN=",patgrp->stat_new);
+ }
+ else if(!strncmp(str, "/STATR=", 7)){
+ SET_STATUS(str,"/STATR=",patgrp->stat_rec);
+ }
+ else if(!strncmp(str, "/STATI=", 7)){
+ SET_STATUS(str,"/STATI=",patgrp->stat_imp);
+ }
+ else if(!strncmp(str, "/STATA=", 7)){
+ SET_STATUS(str,"/STATA=",patgrp->stat_ans);
+ }
+ else if(!strncmp(str, "/STATD=", 7)){
+ SET_STATUS(str,"/STATD=",patgrp->stat_del);
+ }
+ else if(!strncmp(str, "/8BITS=", 7)){
+ SET_STATUS(str,"/8BITS=",patgrp->stat_8bitsubj);
+ }
+ else if(!strncmp(str, "/BOM=", 5)){
+ SET_STATUS(str,"/BOM=",patgrp->stat_bom);
+ }
+ else if(!strncmp(str, "/BOY=", 5)){
+ SET_STATUS(str,"/BOY=",patgrp->stat_boy);
+ }
+ else{
+ char save;
+
+ patgrp->bogus = 1;
+
+ if((p = strindex(str, '=')) != NULL){
+ save = *(p+1);
+ *(p+1) = '\0';
+ }
+
+ dprint((1,
+ "parse_patgrp_slash(%.20s): unrecognized in \"%s\"\n",
+ str ? str : "?",
+ (patgrp && patgrp->nick) ? patgrp->nick : ""));
+ q_status_message4(SM_ORDER, 1, 3,
+ "Warning: unrecognized pattern element \"%.20s\"%.20s%.20s%.20s",
+ str, patgrp->nick ? " in rule \"" : "",
+ patgrp->nick ? patgrp->nick : "", patgrp->nick ? "\"" : "");
+
+ if(p)
+ *(p+1) = save;
+ }
+}
+
+
+/*
+ * Fill in one member of action struct from str.
+ *
+ * The multiple constant strings are lame but it evolved this way from
+ * previous versions and isn't worth fixing.
+ */
+void
+parse_action_slash(char *str, ACTION_S *action)
+{
+ char *p;
+ int stateval, i;
+ NAMEVAL_S *v;
+
+ if(!action)
+ panic("NULL action to parse_action_slash");
+ else if(!(str && *str))
+ panic("NULL or empty string to parse_action_slash");
+ else if(!strncmp(str, "/ROLE=1", 7))
+ action->is_a_role = 1;
+ else if(!strncmp(str, "/OTHER=1", 8))
+ action->is_a_other = 1;
+ else if(!strncmp(str, "/ISINCOL=1", 10))
+ action->is_a_incol = 1;
+ else if(!strncmp(str, "/ISSRCH=1", 9))
+ action->is_a_srch = 1;
+ /*
+ * This is unfortunate. If a new filter is set to only set
+ * state bits it will be interpreted by an older pine which
+ * doesn't have that feature like a filter that is set to Delete.
+ * So we change the filter indicator to FILTER=2 to disable the
+ * filter for older versions.
+ */
+ else if(!strncmp(str, "/FILTER=1", 9) || !strncmp(str, "/FILTER=2", 9))
+ action->is_a_filter = 1;
+ else if(!strncmp(str, "/ISSCORE=1", 10))
+ action->is_a_score = 1;
+ else if(!strncmp(str, "/SCORE=", 7)){
+ if((p = remove_pat_escapes(str+7)) != NULL){
+ long i;
+
+ i = atol(p);
+ if(i >= SCORE_MIN && i <= SCORE_MAX)
+ action->scoreval = i;
+
+ fs_give((void **)&p);
+ }
+ }
+ else if(!strncmp(str, "/SCOREHDRTOK=", 13))
+ action->scorevalhdrtok = config_to_hdrtok(str+13);
+ else if(!strncmp(str, "/FOLDER=", 8))
+ action->folder = parse_pattern("FOLDER", str, 1);
+ else if(!strncmp(str, "/KEYSET=", 8))
+ action->keyword_set = parse_pattern("KEYSET", str, 1);
+ else if(!strncmp(str, "/KEYCLR=", 8))
+ action->keyword_clr = parse_pattern("KEYCLR", str, 1);
+ else if(!strncmp(str, "/NOKILL=", 8))
+ action->kill = -1;
+ else if(!strncmp(str, "/NOTDEL=", 8))
+ action->move_only_if_not_deleted = 1;
+ else if(!strncmp(str, "/NONTERM=", 9))
+ action->non_terminating = 1;
+ else if(!strncmp(str, "/STATI=", 7)){
+ stateval = ACT_STAT_LEAVE;
+ SET_MSGSTATE(str,"/STATI=",stateval);
+ switch(stateval){
+ case ACT_STAT_LEAVE:
+ break;
+ case ACT_STAT_SET:
+ action->state_setting_bits |= F_FLAG;
+ break;
+ case ACT_STAT_CLEAR:
+ action->state_setting_bits |= F_UNFLAG;
+ break;
+ }
+ }
+ else if(!strncmp(str, "/STATD=", 7)){
+ stateval = ACT_STAT_LEAVE;
+ SET_MSGSTATE(str,"/STATD=",stateval);
+ switch(stateval){
+ case ACT_STAT_LEAVE:
+ break;
+ case ACT_STAT_SET:
+ action->state_setting_bits |= F_DEL;
+ break;
+ case ACT_STAT_CLEAR:
+ action->state_setting_bits |= F_UNDEL;
+ break;
+ }
+ }
+ else if(!strncmp(str, "/STATA=", 7)){
+ stateval = ACT_STAT_LEAVE;
+ SET_MSGSTATE(str,"/STATA=",stateval);
+ switch(stateval){
+ case ACT_STAT_LEAVE:
+ break;
+ case ACT_STAT_SET:
+ action->state_setting_bits |= F_ANS;
+ break;
+ case ACT_STAT_CLEAR:
+ action->state_setting_bits |= F_UNANS;
+ break;
+ }
+ }
+ else if(!strncmp(str, "/STATN=", 7)){
+ stateval = ACT_STAT_LEAVE;
+ SET_MSGSTATE(str,"/STATN=",stateval);
+ switch(stateval){
+ case ACT_STAT_LEAVE:
+ break;
+ case ACT_STAT_SET:
+ action->state_setting_bits |= F_UNSEEN;
+ break;
+ case ACT_STAT_CLEAR:
+ action->state_setting_bits |= F_SEEN;
+ break;
+ }
+ }
+ else if(!strncmp(str, "/RTYPE=", 7)){
+ /* reply type */
+ action->repl_type = ROLE_REPL_DEFL;
+ if((p = remove_pat_escapes(str+7)) != NULL){
+ for(i = 0; (v = role_repl_types(i)); i++)
+ if(!strucmp(p, v->shortname)){
+ action->repl_type = v->value;
+ break;
+ }
+
+ fs_give((void **)&p);
+ }
+ }
+ else if(!strncmp(str, "/FTYPE=", 7)){
+ /* forward type */
+ action->forw_type = ROLE_FORW_DEFL;
+ if((p = remove_pat_escapes(str+7)) != NULL){
+ for(i = 0; (v = role_forw_types(i)); i++)
+ if(!strucmp(p, v->shortname)){
+ action->forw_type = v->value;
+ break;
+ }
+
+ fs_give((void **)&p);
+ }
+ }
+ else if(!strncmp(str, "/CTYPE=", 7)){
+ /* compose type */
+ action->comp_type = ROLE_COMP_DEFL;
+ if((p = remove_pat_escapes(str+7)) != NULL){
+ for(i = 0; (v = role_comp_types(i)); i++)
+ if(!strucmp(p, v->shortname)){
+ action->comp_type = v->value;
+ break;
+ }
+
+ fs_give((void **)&p);
+ }
+ }
+ else if(!strncmp(str, "/FROM=", 6)){
+ /* get the from */
+ if((p = remove_pat_escapes(str+6)) != NULL){
+ rfc822_parse_adrlist(&action->from, p,
+ ps_global->maildomain);
+ fs_give((void **)&p);
+ }
+ }
+ else if(!strncmp(str, "/REPL=", 6)){
+ /* get the reply-to */
+ if((p = remove_pat_escapes(str+6)) != NULL){
+ rfc822_parse_adrlist(&action->replyto, p,
+ ps_global->maildomain);
+ fs_give((void **)&p);
+ }
+ }
+ else if(!strncmp(str, "/FCC=", 5))
+ action->fcc = remove_pat_escapes(str+5);
+ else if(!strncmp(str, "/LSIG=", 6))
+ action->litsig = remove_pat_escapes(str+6);
+ else if(!strncmp(str, "/SIG=", 5))
+ action->sig = remove_pat_escapes(str+5);
+ else if(!strncmp(str, "/TEMPLATE=", 10))
+ action->template = remove_pat_escapes(str+10);
+ /* get the custom headers */
+ else if(!strncmp(str, "/CSTM=", 6)){
+ if((p = remove_pat_escapes(str+6)) != NULL){
+ int commas = 0;
+ char *q;
+
+ /* count elements in list */
+ for(q = p; q && *q; q++)
+ if(*q == ',')
+ commas++;
+
+ action->cstm = parse_list(p, commas+1, 0, NULL);
+ fs_give((void **)&p);
+ }
+ }
+ else if(!strncmp(str, "/SMTP=", 6)){
+ if((p = remove_pat_escapes(str+6)) != NULL){
+ int commas = 0;
+ char *q;
+
+ /* count elements in list */
+ for(q = p; q && *q; q++)
+ if(*q == ',')
+ commas++;
+
+ action->smtp = parse_list(p, commas+1, PL_REMSURRQUOT, NULL);
+ fs_give((void **)&p);
+ }
+ }
+ else if(!strncmp(str, "/NNTP=", 6)){
+ if((p = remove_pat_escapes(str+6)) != NULL){
+ int commas = 0;
+ char *q;
+
+ /* count elements in list */
+ for(q = p; q && *q; q++)
+ if(*q == ',')
+ commas++;
+
+ action->nntp = parse_list(p, commas+1, PL_REMSURRQUOT, NULL);
+ fs_give((void **)&p);
+ }
+ }
+ else if(!strncmp(str, "/INICK=", 7))
+ action->inherit_nick = remove_pat_escapes(str+7);
+ else if(!strncmp(str, "/INCOL=", 7)){
+ if((p = remove_pat_escapes(str+7)) != NULL){
+ char *fg = NULL, *bg = NULL, *z;
+
+ /*
+ * Color should look like
+ * /FG=white/BG=red
+ */
+ if((z = srchstr(p, "/FG=")) != NULL)
+ fg = remove_pat_escapes(z+4);
+ if((z = srchstr(p, "/BG=")) != NULL)
+ bg = remove_pat_escapes(z+4);
+
+ if(fg && *fg && bg && *bg)
+ action->incol = new_color_pair(fg, bg);
+
+ if(fg)
+ fs_give((void **)&fg);
+ if(bg)
+ fs_give((void **)&bg);
+ fs_give((void **)&p);
+ }
+ }
+ /* per-folder sort */
+ else if(!strncmp(str, "/SORT=", 6)){
+ if((p = remove_pat_escapes(str+6)) != NULL){
+ SortOrder def_sort;
+ int def_sort_rev;
+
+ if(decode_sort(p, &def_sort, &def_sort_rev) != -1){
+ action->sort_is_set = 1;
+ action->sortorder = def_sort;
+ action->revsort = (def_sort_rev ? 1 : 0);
+ }
+
+ fs_give((void **)&p);
+ }
+ }
+ /* per-folder index-format */
+ else if(!strncmp(str, "/IFORM=", 7))
+ action->index_format = remove_pat_escapes(str+7);
+ /* per-folder startup-rule */
+ else if(!strncmp(str, "/START=", 7)){
+ if((p = remove_pat_escapes(str+7)) != NULL){
+ for(i = 0; (v = startup_rules(i)); i++)
+ if(!strucmp(p, S_OR_L(v))){
+ action->startup_rule = v->value;
+ break;
+ }
+
+ fs_give((void **)&p);
+ }
+ }
+ else{
+ char save;
+
+ action->bogus = 1;
+
+ if((p = strindex(str, '=')) != NULL){
+ save = *(p+1);
+ *(p+1) = '\0';
+ }
+
+ dprint((1,
+ "parse_action_slash(%.20s): unrecognized in \"%s\"\n",
+ str ? str : "?",
+ (action && action->nick) ? action->nick : ""));
+ q_status_message4(SM_ORDER, 1, 3,
+ "Warning: unrecognized pattern action \"%.20s\"%.20s%.20s%.20s",
+ str, action->nick ? " in rule \"" : "",
+ action->nick ? action->nick : "", action->nick ? "\"" : "");
+
+ if(p)
+ *(p+1) = save;
+ }
+}
+
+
+/*
+ * Str looks like (min,max) or a comma-separated list of these.
+ *
+ * Parens are optional if unambiguous, whitespace is ignored.
+ * If min is left out it is -INF. If max is left out it is INF.
+ * If only one number and no comma number is min and max is INF.
+ *
+ * Returns the INTVL_S list.
+ */
+INTVL_S *
+parse_intvl(char *str)
+{
+ char *q;
+ long left, right;
+ INTVL_S *ret = NULL, **next;
+
+ if(!str)
+ return(ret);
+
+ q = str;
+
+ for(;;){
+ left = right = INTVL_UNDEF;
+
+ /* skip to first number */
+ while(isspace((unsigned char) *q) || *q == LPAREN)
+ q++;
+
+ /* min number */
+ if(*q == COMMA || !struncmp(q, "-INF", 4))
+ left = - INTVL_INF;
+ else if(*q == '-' || isdigit((unsigned char) *q))
+ left = atol(q);
+
+ if(left != INTVL_UNDEF){
+ /* skip to second number */
+ while(*q && *q != COMMA && *q != RPAREN)
+ q++;
+ if(*q == COMMA)
+ q++;
+ while(isspace((unsigned char) *q))
+ q++;
+
+ /* max number */
+ if(*q == '\0' || *q == RPAREN || !struncmp(q, "INF", 3))
+ right = INTVL_INF;
+ else if(*q == '-' || isdigit((unsigned char) *q))
+ right = atol(q);
+ }
+
+ if(left == INTVL_UNDEF || right == INTVL_UNDEF
+ || left > right){
+ if(left != INTVL_UNDEF || right != INTVL_UNDEF || *q){
+ if(left != INTVL_UNDEF && right != INTVL_UNDEF
+ && left > right)
+ q_status_message1(SM_ORDER, 3, 5,
+ _("Error: Interval \"%s\", min > max"), str);
+ else
+ q_status_message1(SM_ORDER, 3, 5,
+ _("Error: Interval \"%s\": syntax is (min,max)"), str);
+
+ if(ret)
+ free_intvl(&ret);
+
+ ret = NULL;
+ }
+
+ break;
+ }
+ else{
+ if(!ret){
+ ret = (INTVL_S *) fs_get(sizeof(*ret));
+ memset((void *) ret, 0, sizeof(*ret));
+ ret->imin = left;
+ ret->imax = right;
+ next = &ret->next;
+ }
+ else{
+ *next = (INTVL_S *) fs_get(sizeof(*ret));
+ memset((void *) *next, 0, sizeof(*ret));
+ (*next)->imin = left;
+ (*next)->imax = right;
+ next = &(*next)->next;
+ }
+
+ /* skip to next interval in list */
+ while(*q && *q != COMMA && *q != RPAREN)
+ q++;
+ if(*q == RPAREN)
+ q++;
+ while(*q && *q != COMMA)
+ q++;
+ if(*q == COMMA)
+ q++;
+ }
+ }
+
+ return(ret);
+}
+
+
+/*
+ * Returns string that looks like "(left,right),(left2,right2)".
+ * Caller is responsible for freeing memory.
+ */
+char *
+stringform_of_intvl(INTVL_S *intvl)
+{
+ char *res = NULL;
+
+ if(intvl && intvl->imin != INTVL_UNDEF && intvl->imax != INTVL_UNDEF
+ && intvl->imin <= intvl->imax){
+ char lbuf[20], rbuf[20], buf[45], *p;
+ INTVL_S *iv;
+ int count = 0;
+ size_t reslen;
+
+ /* find a max size and allocate it for the result */
+ for(iv = intvl;
+ (iv && iv->imin != INTVL_UNDEF && iv->imax != INTVL_UNDEF
+ && iv->imin <= iv->imax);
+ iv = iv->next)
+ count++;
+
+ reslen = count * 50 * sizeof(char);
+ res = (char *) fs_get(reslen+1);
+ memset((void *) res, 0, reslen+1);
+ p = res;
+
+ for(iv = intvl;
+ (iv && iv->imin != INTVL_UNDEF && iv->imax != INTVL_UNDEF
+ && iv->imin <= iv->imax);
+ iv = iv->next){
+
+ if(iv->imin == - INTVL_INF){
+ strncpy(lbuf, "-INF", sizeof(lbuf));
+ lbuf[sizeof(lbuf)-1] = '\0';
+ }
+ else
+ snprintf(lbuf, sizeof(lbuf), "%ld", iv->imin);
+
+ if(iv->imax == INTVL_INF){
+ strncpy(rbuf, "INF", sizeof(rbuf));
+ rbuf[sizeof(rbuf)-1] = '\0';
+ }
+ else
+ snprintf(rbuf, sizeof(rbuf), "%ld", iv->imax);
+
+ snprintf(buf, sizeof(buf), "%.1s(%.20s,%.20s)", (p == res) ? "" : ",",
+ lbuf, rbuf);
+
+ sstrncpy(&p, buf, reslen+1 -(p-res));
+ }
+
+ res[reslen] = '\0';
+ }
+
+ return(res);
+}
+
+
+char *
+hdrtok_to_stringform(HEADER_TOK_S *hdrtok)
+{
+ char buf[1024], nbuf[10];
+ char *res = NULL;
+ char *p, *ptr;
+
+ if(hdrtok){
+ snprintf(nbuf, sizeof(nbuf), "%d", hdrtok->fieldnum);
+ ptr = buf;
+ sstrncpy(&ptr, hdrtok->hdrname ? hdrtok->hdrname : "", sizeof(buf)-(ptr-buf));
+ sstrncpy(&ptr, "(", sizeof(buf)-(ptr-buf));
+ sstrncpy(&ptr, nbuf, sizeof(buf)-(ptr-buf));
+ sstrncpy(&ptr, ",\"", sizeof(buf)-(ptr-buf));
+ p = hdrtok->fieldseps;
+ while(p && *p){
+ if((*p == '\"' || *p == '\\') && ptr-buf < sizeof(buf))
+ *ptr++ = '\\';
+
+ if(ptr-buf < sizeof(buf))
+ *ptr++ = *p++;
+ }
+
+ sstrncpy(&ptr, "\")", sizeof(buf)-(ptr-buf));
+
+ if(ptr-buf < sizeof(buf))
+ *ptr = '\0';
+ else
+ buf[sizeof(buf)-1] = '\0';
+
+ res = cpystr(buf);
+ }
+
+ return(res);
+}
+
+
+HEADER_TOK_S *
+stringform_to_hdrtok(char *str)
+{
+ char *p, *q, *w, hdrname[200];
+ HEADER_TOK_S *hdrtok = NULL;
+
+ if(str && *str){
+ p = str;
+ hdrname[0] = '\0';
+ w = hdrname;
+
+ if(*p == '\"'){ /* quoted name */
+ p++;
+ while(w < hdrname + sizeof(hdrname)-1 && *p != '\"'){
+ if(*p == '\\')
+ p++;
+
+ *w++ = *p++;
+ }
+
+ *w = '\0';
+ if(*p == '\"')
+ p++;
+ }
+ else{
+ while(w < hdrname + sizeof(hdrname)-1 &&
+ !(!(*p & 0x80) && isspace((unsigned char)*p)) &&
+ *p != '(')
+ *w++ = *p++;
+
+ *w = '\0';
+ }
+
+ if(hdrname[0])
+ hdrtok = new_hdrtok(hdrname);
+
+ if(hdrtok){
+ if(*p == '('){
+ p++;
+
+ if(*p && isdigit((unsigned char) *p)){
+ q = p;
+ while(*p && isdigit((unsigned char) *p))
+ p++;
+
+ hdrtok->fieldnum = atoi(q);
+
+ if(*p == ','){
+ int j;
+
+ p++;
+ /* don't use default */
+ if(*p && *p != ')' && hdrtok->fieldseps){
+ hdrtok->fieldseps[0] = '\0';
+ hdrtok->fieldsepcnt = 0;
+ }
+
+ j = 0;
+ if(*p == '\"' && strchr(p+1, '\"')){ /* quoted */
+ p++;
+ while(*p && *p != '\"'){
+ if(hdrtok->fieldseps)
+ fs_resize((void **) &hdrtok->fieldseps, j+2);
+
+ if(*p == '\\' && *(p+1))
+ p++;
+
+ if(hdrtok->fieldseps){
+ hdrtok->fieldseps[j++] = *p++;
+ hdrtok->fieldseps[j] = '\0';
+ hdrtok->fieldsepcnt = j;
+ }
+ }
+ }
+ else{
+ while(*p && *p != ')'){
+ if(hdrtok->fieldseps)
+ fs_resize((void **) &hdrtok->fieldseps, j+2);
+
+ if(*p == '\\' && *(p+1))
+ p++;
+
+ if(hdrtok->fieldseps){
+ hdrtok->fieldseps[j++] = *p++;
+ hdrtok->fieldseps[j] = '\0';
+ hdrtok->fieldsepcnt = j;
+ }
+ }
+ }
+ }
+ else{
+ q_status_message(SM_ORDER | SM_DING, 3, 3, "Missing 2nd argument should be field number, a non-negative digit");
+ }
+ }
+ else{
+ q_status_message(SM_ORDER | SM_DING, 3, 3, "1st argument should be field number, a non-negative digit");
+ }
+ }
+ else{
+ q_status_message1(SM_ORDER | SM_DING, 3, 3, "Missing left parenthesis in %s", str);
+ }
+ }
+ else{
+ q_status_message1(SM_ORDER | SM_DING, 3, 3, "Missing header name in %s", str);
+ }
+ }
+
+ return(hdrtok);
+}
+
+
+char *
+hdrtok_to_config(HEADER_TOK_S *hdrtok)
+{
+ char *ptr, buf[1024], nbuf[10], *p1, *p2, *p3;
+ char *res = NULL;
+
+ if(hdrtok){
+ snprintf(nbuf, sizeof(nbuf), "%d", hdrtok->fieldnum);
+ memset(buf, 0, sizeof(buf));
+ ptr = buf;
+ sstrncpy(&ptr, "/HN=", sizeof(buf)-(ptr-buf));
+ sstrncpy(&ptr, (p1=add_pat_escapes(hdrtok->hdrname ? hdrtok->hdrname : "")), sizeof(buf)-(ptr-buf));
+ sstrncpy(&ptr, "/FN=", sizeof(buf)-(ptr-buf));
+ sstrncpy(&ptr, (p2=add_pat_escapes(nbuf)), sizeof(buf)-(ptr-buf));
+ sstrncpy(&ptr, "/FS=", sizeof(buf)-(ptr-buf));
+ sstrncpy(&ptr, (p3=add_pat_escapes(hdrtok->fieldseps ? hdrtok->fieldseps : "")), sizeof(buf)-(ptr-buf));
+
+ buf[sizeof(buf)-1] = '\0';
+ res = add_pat_escapes(buf);
+
+ if(p1)
+ fs_give((void **)&p1);
+
+ if(p2)
+ fs_give((void **)&p2);
+
+ if(p3)
+ fs_give((void **)&p3);
+ }
+
+ return(res);
+}
+
+
+HEADER_TOK_S *
+config_to_hdrtok(char *str)
+{
+ HEADER_TOK_S *hdrtok = NULL;
+ char *p, *q;
+ int j;
+
+ if(str && *str){
+ if((q = remove_pat_escapes(str)) != NULL){
+ char *hn = NULL, *fn = NULL, *fs = NULL, *z;
+
+ if((z = srchstr(q, "/HN=")) != NULL)
+ hn = remove_pat_escapes(z+4);
+ if((z = srchstr(q, "/FN=")) != NULL)
+ fn = remove_pat_escapes(z+4);
+ if((z = srchstr(q, "/FS=")) != NULL)
+ fs = remove_pat_escapes(z+4);
+
+ hdrtok = new_hdrtok(hn);
+ if(fn)
+ hdrtok->fieldnum = atoi(fn);
+
+ if(fs && *fs){
+ if(hdrtok->fieldseps){
+ hdrtok->fieldseps[0] = '\0';
+ hdrtok->fieldsepcnt = 0;
+ }
+
+ p = fs;
+ j = 0;
+ if(*p == '\"' && strchr(p+1, '\"')){
+ p++;
+ while(*p && *p != '\"'){
+ if(hdrtok->fieldseps)
+ fs_resize((void **) &hdrtok->fieldseps, j+2);
+
+ if(*p == '\\' && *(p+1))
+ p++;
+
+ if(hdrtok->fieldseps){
+ hdrtok->fieldseps[j++] = *p++;
+ hdrtok->fieldseps[j] = '\0';
+ hdrtok->fieldsepcnt = j;
+ }
+ }
+ }
+ else{
+ while(*p){
+ if(hdrtok->fieldseps)
+ fs_resize((void **) &hdrtok->fieldseps, j+2);
+
+ if(*p == '\\' && *(p+1))
+ p++;
+
+ if(hdrtok->fieldseps){
+ hdrtok->fieldseps[j++] = *p++;
+ hdrtok->fieldseps[j] = '\0';
+ hdrtok->fieldsepcnt = j;
+ }
+ }
+ }
+ }
+
+ if(hn)
+ fs_give((void **)&hn);
+ if(fn)
+ fs_give((void **)&fn);
+ if(fs)
+ fs_give((void **)&fs);
+
+ fs_give((void **)&q);
+ }
+ }
+
+ return(hdrtok);
+}
+
+
+/*
+ * Args -- flags - SCOREUSE_INVALID Mark scores_in_use invalid so that we'll
+ * recalculate if we want to use it again.
+ * - SCOREUSE_GET Return whether scores are being used or not.
+ *
+ * Returns -- 0 - Scores not being used at all.
+ * >0 - Scores are used. The return value consists of flag values
+ * OR'd together. Possible values are:
+ *
+ * SCOREUSE_INCOLS - scores needed for index line colors
+ * SCOREUSE_ROLES - scores needed for roles
+ * SCOREUSE_FILTERS - scores needed for filters
+ * SCOREUSE_OTHER - scores needed for other stuff
+ * SCOREUSE_INDEX - scores needed for index drawing
+ *
+ * SCOREUSE_STATEDEP - scores depend on message state
+ */
+int
+scores_are_used(int flags)
+{
+ static int scores_in_use = -1;
+ long type1, type2;
+ int scores_are_defined, scores_are_used_somewhere = 0;
+ PAT_STATE pstate1, pstate2;
+
+ if(flags & SCOREUSE_INVALID) /* mark invalid so we recalculate next time */
+ scores_in_use = -1;
+ else if(scores_in_use == -1){
+
+ /*
+ * Check the patterns to see if scores are potentially
+ * being used.
+ * The first_pattern() in the if checks whether there are any
+ * non-zero scorevals. The loop checks whether any patterns
+ * use those non-zero scorevals.
+ */
+ type1 = ROLE_SCORE;
+ type2 = (ROLE_REPLY | ROLE_FORWARD | ROLE_COMPOSE |
+ ROLE_INCOL | ROLE_DO_FILTER);
+ scores_are_defined = nonempty_patterns(type1, &pstate1)
+ && first_pattern(&pstate1);
+ if(scores_are_defined)
+ scores_are_used_somewhere =
+ ((nonempty_patterns(type2, &pstate2) && first_pattern(&pstate2))
+ || ps_global->a_format_contains_score
+ || mn_get_sort(ps_global->msgmap) == SortScore);
+
+ if(scores_are_used_somewhere){
+ PAT_S *pat;
+
+ /*
+ * Careful. nonempty_patterns() may call close_pattern()
+ * which will set scores_in_use to -1! So we have to be
+ * sure to reset it after we call nonempty_patterns().
+ */
+ scores_in_use = 0;
+ if(ps_global->a_format_contains_score
+ || mn_get_sort(ps_global->msgmap) == SortScore)
+ scores_in_use |= SCOREUSE_INDEX;
+
+ if(nonempty_patterns(type2, &pstate2))
+ for(pat = first_pattern(&pstate2);
+ pat;
+ pat = next_pattern(&pstate2))
+ if(pat->patgrp && !pat->patgrp->bogus && pat->patgrp->do_score){
+ if(pat->action && pat->action->is_a_incol)
+ scores_in_use |= SCOREUSE_INCOLS;
+ if(pat->action && pat->action->is_a_role)
+ scores_in_use |= SCOREUSE_ROLES;
+ if(pat->action && pat->action->is_a_filter)
+ scores_in_use |= SCOREUSE_FILTERS;
+ if(pat->action && pat->action->is_a_other)
+ scores_in_use |= SCOREUSE_OTHER;
+ }
+
+ /*
+ * Note whether scores depend on message state or not.
+ */
+ if(scores_in_use)
+ for(pat = first_pattern(&pstate1);
+ pat;
+ pat = next_pattern(&pstate1))
+ if(patgrp_depends_on_active_state(pat->patgrp)){
+ scores_in_use |= SCOREUSE_STATEDEP;
+ break;
+ }
+
+ }
+ else
+ scores_in_use = 0;
+ }
+
+ return((scores_in_use == -1) ? 0 : scores_in_use);
+}
+
+
+int
+patgrp_depends_on_state(PATGRP_S *patgrp)
+{
+ return(patgrp && (patgrp_depends_on_active_state(patgrp)
+ || patgrp->stat_rec != PAT_STAT_EITHER));
+}
+
+
+/*
+ * Recent doesn't count for this function because it doesn't change while
+ * the mailbox is open.
+ */
+int
+patgrp_depends_on_active_state(PATGRP_S *patgrp)
+{
+ return(patgrp && !patgrp->bogus
+ && (patgrp->stat_new != PAT_STAT_EITHER ||
+ patgrp->stat_del != PAT_STAT_EITHER ||
+ patgrp->stat_imp != PAT_STAT_EITHER ||
+ patgrp->stat_ans != PAT_STAT_EITHER ||
+ patgrp->keyword));
+}
+
+
+/*
+ * Look for label in str and return a pointer to parsed string.
+ * Actually, we look for "label=" or "!label=", the second means NOT.
+ * Converts from string from patterns file which looks like
+ * /NEWS=comp.mail.,comp.mail.pine/TO=...
+ * This is the string that came from pattern="string" with the pattern=
+ * and outer quotes removed.
+ * This converts the string to a PATTERN_S list and returns
+ * an allocated copy.
+ */
+PATTERN_S *
+parse_pattern(char *label, char *str, int hex_to_backslashed)
+{
+ char copy[50]; /* local copy of label */
+ char copynot[50]; /* local copy of label, NOT'ed */
+ char *q, *labeled_str;
+ PATTERN_S *head = NULL;
+
+ if(!label || !str)
+ return(NULL);
+
+ q = copy;
+ sstrncpy(&q, "/", sizeof(copy));
+ sstrncpy(&q, label, sizeof(copy) - (q-copy));
+ sstrncpy(&q, "=", sizeof(copy) - (q-copy));
+ copy[sizeof(copy)-1] = '\0';
+ q = copynot;
+ sstrncpy(&q, "/!", sizeof(copynot));
+ sstrncpy(&q, label, sizeof(copynot) - (q-copynot));
+ sstrncpy(&q, "=", sizeof(copynot) - (q-copynot));
+ copynot[sizeof(copynot)-1] = '\0';
+
+ if(hex_to_backslashed){
+ if((q = srchstr(str, copy)) != NULL){
+ head = config_to_pattern(q+strlen(copy));
+ }
+ else if((q = srchstr(str, copynot)) != NULL){
+ head = config_to_pattern(q+strlen(copynot));
+ head->not = 1;
+ }
+ }
+ else{
+ if((q = srchstr(str, copy)) != NULL){
+ if((labeled_str =
+ remove_backslash_escapes(q+strlen(copy))) != NULL){
+ head = string_to_pattern(labeled_str);
+ fs_give((void **)&labeled_str);
+ }
+ }
+ else if((q = srchstr(str, copynot)) != NULL){
+ if((labeled_str =
+ remove_backslash_escapes(q+strlen(copynot))) != NULL){
+ head = string_to_pattern(labeled_str);
+ head->not = 1;
+ fs_give((void **)&labeled_str);
+ }
+ }
+ }
+
+ return(head);
+}
+
+
+/*
+ * Look for /ARB's in str and return a pointer to parsed ARBHDR_S.
+ * Actually, we look for /!ARB and /!EARB as well. Those mean NOT.
+ * Converts from string from patterns file which looks like
+ * /ARB<fieldname1>=pattern/.../ARB<fieldname2>=pattern...
+ * This is the string that came from pattern="string" with the pattern=
+ * and outer quotes removed.
+ * This converts the string to a ARBHDR_S list and returns
+ * an allocated copy.
+ */
+ARBHDR_S *
+parse_arbhdr(char *str)
+{
+ char *q, *s, *equals, *noesc;
+ int not, empty, skip;
+ ARBHDR_S *ahdr = NULL, *a, *aa;
+ PATTERN_S *p = NULL;
+
+ if(!str)
+ return(NULL);
+
+ aa = NULL;
+ for(s = str; (q = next_arb(s)); s = q+1){
+ not = (q[1] == '!') ? 1 : 0;
+ empty = (q[not+1] == 'E') ? 1 : 0;
+ skip = 4 + not + empty;
+ if((noesc = remove_pat_escapes(q+skip)) != NULL){
+ if(*noesc != '=' && (equals = strindex(noesc, '=')) != NULL){
+ a = (ARBHDR_S *)fs_get(sizeof(*a));
+ memset((void *)a, 0, sizeof(*a));
+ *equals = '\0';
+ a->isemptyval = empty;
+ a->field = cpystr(noesc);
+ if(empty)
+ a->p = string_to_pattern("");
+ else if(*(equals+1) &&
+ (p = string_to_pattern(equals+1)) != NULL)
+ a->p = p;
+
+ if(not && a->p)
+ a->p->not = 1;
+
+ /* keep them in the same order */
+ if(aa){
+ aa->next = a;
+ aa = aa->next;
+ }
+ else{
+ ahdr = a;
+ aa = ahdr;
+ }
+ }
+
+ fs_give((void **)&noesc);
+ }
+ }
+
+ return(ahdr);
+}
+
+
+char *
+next_arb(char *start)
+{
+ char *q1, *q2, *q3, *q4, *p;
+
+ q1 = srchstr(start, "/ARB");
+ q2 = srchstr(start, "/!ARB");
+ q3 = srchstr(start, "/EARB");
+ q4 = srchstr(start, "/!EARB");
+
+ p = q1;
+ if(!p || (q2 && q2 < p))
+ p = q2;
+ if(!p || (q3 && q3 < p))
+ p = q3;
+ if(!p || (q4 && q4 < p))
+ p = q4;
+
+ return(p);
+}
+
+
+/*
+ * Converts a string to a PATTERN_S list and returns an
+ * allocated copy. The source string looks like
+ * string1,string2,...
+ * Commas and backslashes may be backslash-escaped in the original string
+ * in order to include actual commas and backslashes in the pattern.
+ * So \, is an actual comma and , is the separator character.
+ */
+PATTERN_S *
+string_to_pattern(char *str)
+{
+ char *q, *s, *workspace;
+ PATTERN_S *p, *head = NULL, **nextp;
+
+ if(!str)
+ return(head);
+
+ /*
+ * We want an empty string to cause an empty substring in the pattern
+ * instead of returning a NULL pattern. That can be used as a way to
+ * match any header. For example, if all the patterns but the news
+ * pattern were null and the news pattern was a substring of "" then
+ * we use that to match any message with a newsgroups header.
+ */
+ if(!*str){
+ head = (PATTERN_S *)fs_get(sizeof(*p));
+ memset((void *)head, 0, sizeof(*head));
+ head->substring = cpystr("");
+ }
+ else{
+ nextp = &head;
+ workspace = (char *)fs_get((strlen(str)+1) * sizeof(char));
+ s = workspace;
+ *s = '\0';
+ q = str;
+ do {
+ switch(*q){
+ case COMMA:
+ case '\0':
+ *s = '\0';
+ removing_leading_and_trailing_white_space(workspace);
+ p = (PATTERN_S *)fs_get(sizeof(*p));
+ memset((void *)p, 0, sizeof(*p));
+ p->substring = cpystr(workspace);
+
+ convert_possibly_encoded_str_to_utf8(&p->substring);
+
+ *nextp = p;
+ nextp = &p->next;
+ s = workspace;
+ *s = '\0';
+ break;
+
+ case BSLASH:
+ if(*(q+1) == COMMA || *(q+1) == BSLASH)
+ *s++ = *(++q);
+ else
+ *s++ = *q;
+
+ break;
+
+ default:
+ *s++ = *q;
+ break;
+ }
+ } while(*q++);
+
+ fs_give((void **)&workspace);
+ }
+
+ return(head);
+}
+
+
+/*
+ * Converts a PATTERN_S list to a string.
+ * The resulting string is allocated here and looks like
+ * string1,string2,...
+ * Commas and backslashes in the original pattern
+ * end up backslash-escaped in the string.
+ */
+char *
+pattern_to_string(PATTERN_S *pattern)
+{
+ PATTERN_S *p;
+ char *result = NULL, *q, *s;
+ size_t n;
+
+ if(!pattern)
+ return(result);
+
+ /* how much space is needed? */
+ n = 0;
+ for(p = pattern; p; p = p->next){
+ n += (p == pattern) ? 0 : 1;
+ for(s = p->substring; s && *s; s++){
+ if(*s == COMMA || *s == BSLASH)
+ n++;
+
+ n++;
+ }
+ }
+
+ q = result = (char *)fs_get(++n);
+ for(p = pattern; p; p = p->next){
+ if(p != pattern)
+ *q++ = COMMA;
+
+ for(s = p->substring; s && *s; s++){
+ if(*s == COMMA || *s == BSLASH)
+ *q++ = '\\';
+
+ *q++ = *s;
+ }
+ }
+
+ *q = '\0';
+
+ return(result);
+}
+
+
+/*
+ * Do the escaping necessary to take a string for a pattern into a comma-
+ * separated string with escapes suitable for the config file.
+ * Returns an allocated copy of that string.
+ * In particular
+ * , -> \, -> \x2C
+ * \ -> \\ -> \x5C
+ * " -> \"
+ * / -> \/
+ */
+char *
+pattern_to_config(PATTERN_S *pat)
+{
+ char *s, *res = NULL;
+
+ s = pattern_to_string(pat);
+ if(s){
+ res = add_pat_escapes(s);
+ fs_give((void **) &s);
+ }
+
+ return(res);
+}
+
+/*
+ * Opposite of pattern_to_config.
+ */
+PATTERN_S *
+config_to_pattern(char *str)
+{
+ char *s;
+ PATTERN_S *pat = NULL;
+
+ s = remove_pat_escapes(str);
+ if(s){
+ pat = string_to_pattern(s);
+ fs_give((void **) &s);
+ }
+
+ return(pat);
+}
+
+
+/*
+ * Converts an array of strings to a PATTERN_S list and returns an
+ * allocated copy.
+ * The list strings may not contain commas directly, because the UI turns
+ * those into separate list members. Instead, the user types \, and
+ * that backslash comma is converted to a comma here.
+ * It is a bit odd. Backslash itself is not escaped. A backslash which is
+ * not followed by a comma is a literal backslash, a backslash followed by
+ * a comma is a comma.
+ */
+PATTERN_S *
+editlist_to_pattern(char **list)
+{
+ PATTERN_S *head = NULL;
+
+ if(!(list && *list))
+ return(head);
+
+ /*
+ * We want an empty string to cause an empty substring in the pattern
+ * instead of returning a NULL pattern. That can be used as a way to
+ * match any header. For example, if all the patterns but the news
+ * pattern were null and the news pattern was a substring of "" then
+ * we use that to match any message with a newsgroups header.
+ */
+ if(!list[0][0]){
+ head = (PATTERN_S *) fs_get(sizeof(*head));
+ memset((void *) head, 0, sizeof(*head));
+ head->substring = cpystr("");
+ }
+ else{
+ char *str, *s, *q, *workspace = NULL;
+ size_t l = 0;
+ PATTERN_S *p, **nextp;
+ int i;
+
+ nextp = &head;
+ for(i = 0; (str = list[i]); i++){
+ if(str[0]){
+ if(!workspace){
+ l = strlen(str) + 1;
+ workspace = (char *) fs_get(l * sizeof(char));
+ }
+ else if(strlen(str) + 1 > l){
+ l = strlen(str) + 1;
+ fs_give((void **) &workspace);
+ workspace = (char *) fs_get(l * sizeof(char));
+ }
+
+ s = workspace;
+ *s = '\0';
+ q = str;
+ do {
+ switch(*q){
+ case '\0':
+ *s = '\0';
+ removing_leading_and_trailing_white_space(workspace);
+ p = (PATTERN_S *) fs_get(sizeof(*p));
+ memset((void *) p, 0, sizeof(*p));
+ p->substring = cpystr(workspace);
+ *nextp = p;
+ nextp = &p->next;
+ s = workspace;
+ *s = '\0';
+ break;
+
+ case BSLASH:
+ if(*(q+1) == COMMA)
+ *s++ = *(++q);
+ else
+ *s++ = *q;
+
+ break;
+
+ default:
+ *s++ = *q;
+ break;
+ }
+ } while(*q++);
+ }
+ }
+ }
+
+ return(head);
+}
+
+
+/*
+ * Converts a PATTERN_S to an array of strings and returns an allocated copy.
+ * Commas are converted to backslash-comma, because the text_tool UI uses
+ * commas to separate items.
+ * It is a bit odd. Backslash itself is not escaped. A backslash which is
+ * not followed by a comma is a literal backslash, a backslash followed by
+ * a comma is a comma.
+ */
+char **
+pattern_to_editlist(PATTERN_S *pat)
+{
+ int cnt, i;
+ PATTERN_S *p;
+ char **list = NULL;
+
+ if(!pat)
+ return(list);
+
+ /* how many in list? */
+ for(cnt = 0, p = pat; p; p = p->next)
+ cnt++;
+
+ list = (char **) fs_get((cnt + 1) * sizeof(*list));
+ memset((void *) list, 0, (cnt + 1) * sizeof(*list));
+
+ for(i = 0, p = pat; p; p = p->next, i++)
+ list[i] = add_comma_escapes(p->substring);
+
+ return(list);
+}
+
+
+PATGRP_S *
+nick_to_patgrp(char *nick, int rflags)
+{
+ PAT_S *pat;
+ PAT_STATE pstate;
+ PATGRP_S *patgrp = NULL;
+
+ if(!(nick && *nick
+ && nonempty_patterns(rflags, &pstate) && first_pattern(&pstate)))
+ return(patgrp);
+
+ for(pat = first_pattern(&pstate);
+ !patgrp && pat;
+ pat = next_pattern(&pstate))
+ if(pat->patgrp && pat->patgrp->nick && !strcmp(pat->patgrp->nick, nick))
+ patgrp = copy_patgrp(pat->patgrp);
+
+ return(patgrp);
+}
+
+
+/*
+ * Must be called with a pstate, we don't check for it.
+ * It respects the cur_rflag_num in pstate. That is, it doesn't start over
+ * at i=1, it starts at cur_rflag_num.
+ */
+PAT_S *
+first_any_pattern(PAT_STATE *pstate)
+{
+ PAT_LINE_S *patline = NULL;
+ int i;
+ long local_rflag;
+
+ /*
+ * The rest of pstate should be set before coming here.
+ * In particular, the rflags should be set by a call to nonempty_patterns
+ * or any_patterns, and cur_rflag_num should be set.
+ */
+ pstate->patlinecurrent = NULL;
+ pstate->patcurrent = NULL;
+
+ /*
+ * The order of these is important. It is the same as the order
+ * used for next_any_pattern and opposite of the order used by
+ * last and prev. For next_any's benefit, we allow cur_rflag_num to
+ * start us out past the first set.
+ */
+ for(i = pstate->cur_rflag_num; i <= PATTERN_N; i++){
+
+ local_rflag = 0L;
+
+ switch(i){
+ case 1:
+ local_rflag = ROLE_DO_SRCH & CANONICAL_RFLAGS(pstate->rflags);
+ break;
+
+ case 2:
+ local_rflag = ROLE_DO_INCOLS & CANONICAL_RFLAGS(pstate->rflags);
+ break;
+
+ case 3:
+ local_rflag = ROLE_DO_ROLES & CANONICAL_RFLAGS(pstate->rflags);
+ break;
+
+ case 4:
+ local_rflag = ROLE_DO_FILTER & CANONICAL_RFLAGS(pstate->rflags);
+ break;
+
+ case 5:
+ local_rflag = ROLE_DO_SCORES & CANONICAL_RFLAGS(pstate->rflags);
+ break;
+
+ case 6:
+ local_rflag = ROLE_DO_OTHER & CANONICAL_RFLAGS(pstate->rflags);
+ break;
+
+ case 7:
+ local_rflag = ROLE_OLD_FILT & CANONICAL_RFLAGS(pstate->rflags);
+ break;
+
+ case 8:
+ local_rflag = ROLE_OLD_SCORE & CANONICAL_RFLAGS(pstate->rflags);
+ break;
+
+ case PATTERN_N:
+ local_rflag = ROLE_OLD_PAT & CANONICAL_RFLAGS(pstate->rflags);
+ break;
+ }
+
+ if(local_rflag){
+ SET_PATTYPE(local_rflag | (pstate->rflags & PAT_USE_MASK));
+
+ if(*cur_pat_h){
+ /* Find first patline with a pat */
+ for(patline = (*cur_pat_h)->patlinehead;
+ patline && !patline->first;
+ patline = patline->next)
+ ;
+ }
+
+ if(patline){
+ pstate->cur_rflag_num = i;
+ pstate->patlinecurrent = patline;
+ pstate->patcurrent = patline->first;
+ }
+ }
+
+ if(pstate->patcurrent)
+ break;
+ }
+
+ return(pstate->patcurrent);
+}
+
+
+/*
+ * Return first pattern of the specified types. These types were set by a
+ * previous call to any_patterns or nonempty_patterns.
+ *
+ * Args -- pstate pattern state. This is set here and passed back for
+ * use by next_pattern. Must be non-null.
+ * It must have been initialized previously by a call to
+ * nonempty_patterns or any_patterns.
+ */
+PAT_S *
+first_pattern(PAT_STATE *pstate)
+{
+ PAT_S *pat;
+ long rflags;
+
+ pstate->cur_rflag_num = 1;
+
+ rflags = pstate->rflags;
+
+ for(pat = first_any_pattern(pstate);
+ pat && !((pat->action &&
+ ((rflags & ROLE_DO_ROLES && pat->action->is_a_role) ||
+ (rflags & (ROLE_DO_INCOLS|ROLE_INCOL) &&
+ pat->action->is_a_incol) ||
+ (rflags & ROLE_DO_OTHER && pat->action->is_a_other) ||
+ (rflags & ROLE_DO_SRCH && pat->action->is_a_srch) ||
+ (rflags & ROLE_DO_SCORES && pat->action->is_a_score) ||
+ (rflags & ROLE_SCORE && (pat->action->scoreval
+ || pat->action->scorevalhdrtok)) ||
+ (rflags & ROLE_DO_FILTER && pat->action->is_a_filter) ||
+ (rflags & ROLE_REPLY &&
+ (pat->action->repl_type == ROLE_REPL_YES ||
+ pat->action->repl_type == ROLE_REPL_NOCONF)) ||
+ (rflags & ROLE_FORWARD &&
+ (pat->action->forw_type == ROLE_FORW_YES ||
+ pat->action->forw_type == ROLE_FORW_NOCONF)) ||
+ (rflags & ROLE_COMPOSE &&
+ (pat->action->comp_type == ROLE_COMP_YES ||
+ pat->action->comp_type == ROLE_COMP_NOCONF)) ||
+ (rflags & ROLE_OLD_FILT) ||
+ (rflags & ROLE_OLD_SCORE) ||
+ (rflags & ROLE_OLD_PAT)))
+ ||
+ pat->inherit);
+ pat = next_any_pattern(pstate))
+ ;
+
+ return(pat);
+}
+
+
+/*
+ * Just like first_any_pattern.
+ */
+PAT_S *
+last_any_pattern(PAT_STATE *pstate)
+{
+ PAT_LINE_S *patline = NULL;
+ int i;
+ long local_rflag;
+
+ /*
+ * The rest of pstate should be set before coming here.
+ * In particular, the rflags should be set by a call to nonempty_patterns
+ * or any_patterns, and cur_rflag_num should be set.
+ */
+ pstate->patlinecurrent = NULL;
+ pstate->patcurrent = NULL;
+
+ for(i = pstate->cur_rflag_num; i >= 1; i--){
+
+ local_rflag = 0L;
+
+ switch(i){
+ case 1:
+ local_rflag = ROLE_DO_SRCH & CANONICAL_RFLAGS(pstate->rflags);
+ break;
+
+ case 2:
+ local_rflag = ROLE_DO_INCOLS & CANONICAL_RFLAGS(pstate->rflags);
+ break;
+
+ case 3:
+ local_rflag = ROLE_DO_ROLES & CANONICAL_RFLAGS(pstate->rflags);
+ break;
+
+ case 4:
+ local_rflag = ROLE_DO_FILTER & CANONICAL_RFLAGS(pstate->rflags);
+ break;
+
+ case 5:
+ local_rflag = ROLE_DO_SCORES & CANONICAL_RFLAGS(pstate->rflags);
+ break;
+
+ case 6:
+ local_rflag = ROLE_DO_OTHER & CANONICAL_RFLAGS(pstate->rflags);
+ break;
+
+ case 7:
+ local_rflag = ROLE_OLD_FILT & CANONICAL_RFLAGS(pstate->rflags);
+ break;
+
+ case 8:
+ local_rflag = ROLE_OLD_SCORE & CANONICAL_RFLAGS(pstate->rflags);
+ break;
+
+ case PATTERN_N:
+ local_rflag = ROLE_OLD_PAT & CANONICAL_RFLAGS(pstate->rflags);
+ break;
+ }
+
+ if(local_rflag){
+ SET_PATTYPE(local_rflag | (pstate->rflags & PAT_USE_MASK));
+
+ pstate->patlinecurrent = NULL;
+ pstate->patcurrent = NULL;
+
+ if(*cur_pat_h){
+ /* Find last patline with a pat */
+ for(patline = (*cur_pat_h)->patlinehead;
+ patline;
+ patline = patline->next)
+ if(patline->last)
+ pstate->patlinecurrent = patline;
+
+ if(pstate->patlinecurrent)
+ pstate->patcurrent = pstate->patlinecurrent->last;
+ }
+
+ if(pstate->patcurrent)
+ pstate->cur_rflag_num = i;
+
+ if(pstate->patcurrent)
+ break;
+ }
+ }
+
+ return(pstate->patcurrent);
+}
+
+
+/*
+ * Return last pattern of the specified types. These types were set by a
+ * previous call to any_patterns or nonempty_patterns.
+ *
+ * Args -- pstate pattern state. This is set here and passed back for
+ * use by prev_pattern. Must be non-null.
+ * It must have been initialized previously by a call to
+ * nonempty_patterns or any_patterns.
+ */
+PAT_S *
+last_pattern(PAT_STATE *pstate)
+{
+ PAT_S *pat;
+ long rflags;
+
+ pstate->cur_rflag_num = PATTERN_N;
+
+ rflags = pstate->rflags;
+
+ for(pat = last_any_pattern(pstate);
+ pat && !((pat->action &&
+ ((rflags & ROLE_DO_ROLES && pat->action->is_a_role) ||
+ (rflags & (ROLE_DO_INCOLS|ROLE_INCOL) &&
+ pat->action->is_a_incol) ||
+ (rflags & ROLE_DO_OTHER && pat->action->is_a_other) ||
+ (rflags & ROLE_DO_SRCH && pat->action->is_a_srch) ||
+ (rflags & ROLE_DO_SCORES && pat->action->is_a_score) ||
+ (rflags & ROLE_SCORE && (pat->action->scoreval
+ || pat->action->scorevalhdrtok)) ||
+ (rflags & ROLE_DO_FILTER && pat->action->is_a_filter) ||
+ (rflags & ROLE_REPLY &&
+ (pat->action->repl_type == ROLE_REPL_YES ||
+ pat->action->repl_type == ROLE_REPL_NOCONF)) ||
+ (rflags & ROLE_FORWARD &&
+ (pat->action->forw_type == ROLE_FORW_YES ||
+ pat->action->forw_type == ROLE_FORW_NOCONF)) ||
+ (rflags & ROLE_COMPOSE &&
+ (pat->action->comp_type == ROLE_COMP_YES ||
+ pat->action->comp_type == ROLE_COMP_NOCONF)) ||
+ (rflags & ROLE_OLD_FILT) ||
+ (rflags & ROLE_OLD_SCORE) ||
+ (rflags & ROLE_OLD_PAT)))
+ ||
+ pat->inherit);
+ pat = prev_any_pattern(pstate))
+ ;
+
+ return(pat);
+}
+
+
+/*
+ * This assumes that pstate is valid.
+ */
+PAT_S *
+next_any_pattern(PAT_STATE *pstate)
+{
+ PAT_LINE_S *patline;
+
+ if(pstate->patlinecurrent){
+ if(pstate->patcurrent && pstate->patcurrent->next)
+ pstate->patcurrent = pstate->patcurrent->next;
+ else{
+ /* Find next patline with a pat */
+ for(patline = pstate->patlinecurrent->next;
+ patline && !patline->first;
+ patline = patline->next)
+ ;
+
+ if(patline){
+ pstate->patlinecurrent = patline;
+ pstate->patcurrent = patline->first;
+ }
+ else{
+ pstate->patlinecurrent = NULL;
+ pstate->patcurrent = NULL;
+ }
+ }
+ }
+
+ /* we've reached the last, try the next rflag_num (the next pattern type) */
+ if(!pstate->patcurrent){
+ pstate->cur_rflag_num++;
+ pstate->patcurrent = first_any_pattern(pstate);
+ }
+
+ return(pstate->patcurrent);
+}
+
+
+/*
+ * Return next pattern of the specified types. These types were set by a
+ * previous call to any_patterns or nonempty_patterns.
+ *
+ * Args -- pstate pattern state. This is set by first_pattern or last_pattern.
+ */
+PAT_S *
+next_pattern(PAT_STATE *pstate)
+{
+ PAT_S *pat;
+ long rflags;
+
+ rflags = pstate->rflags;
+
+ for(pat = next_any_pattern(pstate);
+ pat && !((pat->action &&
+ ((rflags & ROLE_DO_ROLES && pat->action->is_a_role) ||
+ (rflags & (ROLE_DO_INCOLS|ROLE_INCOL) &&
+ pat->action->is_a_incol) ||
+ (rflags & ROLE_DO_OTHER && pat->action->is_a_other) ||
+ (rflags & ROLE_DO_SRCH && pat->action->is_a_srch) ||
+ (rflags & ROLE_DO_SCORES && pat->action->is_a_score) ||
+ (rflags & ROLE_SCORE && (pat->action->scoreval
+ || pat->action->scorevalhdrtok)) ||
+ (rflags & ROLE_DO_FILTER && pat->action->is_a_filter) ||
+ (rflags & ROLE_REPLY &&
+ (pat->action->repl_type == ROLE_REPL_YES ||
+ pat->action->repl_type == ROLE_REPL_NOCONF)) ||
+ (rflags & ROLE_FORWARD &&
+ (pat->action->forw_type == ROLE_FORW_YES ||
+ pat->action->forw_type == ROLE_FORW_NOCONF)) ||
+ (rflags & ROLE_COMPOSE &&
+ (pat->action->comp_type == ROLE_COMP_YES ||
+ pat->action->comp_type == ROLE_COMP_NOCONF)) ||
+ (rflags & ROLE_OLD_FILT) ||
+ (rflags & ROLE_OLD_SCORE) ||
+ (rflags & ROLE_OLD_PAT)))
+ ||
+ pat->inherit);
+ pat = next_any_pattern(pstate))
+ ;
+
+ return(pat);
+}
+
+
+/*
+ * This assumes that pstate is valid.
+ */
+PAT_S *
+prev_any_pattern(PAT_STATE *pstate)
+{
+ PAT_LINE_S *patline;
+
+ if(pstate->patlinecurrent){
+ if(pstate->patcurrent && pstate->patcurrent->prev)
+ pstate->patcurrent = pstate->patcurrent->prev;
+ else{
+ /* Find prev patline with a pat */
+ for(patline = pstate->patlinecurrent->prev;
+ patline && !patline->last;
+ patline = patline->prev)
+ ;
+
+ if(patline){
+ pstate->patlinecurrent = patline;
+ pstate->patcurrent = patline->last;
+ }
+ else{
+ pstate->patlinecurrent = NULL;
+ pstate->patcurrent = NULL;
+ }
+ }
+ }
+
+ if(!pstate->patcurrent){
+ pstate->cur_rflag_num--;
+ pstate->patcurrent = last_any_pattern(pstate);
+ }
+
+ return(pstate->patcurrent);
+}
+
+
+/*
+ * Return prev pattern of the specified types. These types were set by a
+ * previous call to any_patterns or nonempty_patterns.
+ *
+ * Args -- pstate pattern state. This is set by first_pattern or last_pattern.
+ */
+PAT_S *
+prev_pattern(PAT_STATE *pstate)
+{
+ PAT_S *pat;
+ long rflags;
+
+ rflags = pstate->rflags;
+
+ for(pat = prev_any_pattern(pstate);
+ pat && !((pat->action &&
+ ((rflags & ROLE_DO_ROLES && pat->action->is_a_role) ||
+ (rflags & (ROLE_DO_INCOLS|ROLE_INCOL) &&
+ pat->action->is_a_incol) ||
+ (rflags & ROLE_DO_OTHER && pat->action->is_a_other) ||
+ (rflags & ROLE_DO_SRCH && pat->action->is_a_srch) ||
+ (rflags & ROLE_DO_SCORES && pat->action->is_a_score) ||
+ (rflags & ROLE_SCORE && (pat->action->scoreval
+ || pat->action->scorevalhdrtok)) ||
+ (rflags & ROLE_DO_FILTER && pat->action->is_a_filter) ||
+ (rflags & ROLE_REPLY &&
+ (pat->action->repl_type == ROLE_REPL_YES ||
+ pat->action->repl_type == ROLE_REPL_NOCONF)) ||
+ (rflags & ROLE_FORWARD &&
+ (pat->action->forw_type == ROLE_FORW_YES ||
+ pat->action->forw_type == ROLE_FORW_NOCONF)) ||
+ (rflags & ROLE_COMPOSE &&
+ (pat->action->comp_type == ROLE_COMP_YES ||
+ pat->action->comp_type == ROLE_COMP_NOCONF)) ||
+ (rflags & ROLE_OLD_FILT) ||
+ (rflags & ROLE_OLD_SCORE) ||
+ (rflags & ROLE_OLD_PAT)))
+ ||
+ pat->inherit);
+ pat = prev_any_pattern(pstate))
+ ;
+
+ return(pat);
+}
+
+
+/*
+ * Rflags may be more than one pattern type OR'd together.
+ */
+int
+write_patterns(long int rflags)
+{
+ int canon_rflags;
+ int err = 0;
+
+ dprint((7, "write_patterns(0x%x)\n", rflags));
+
+ canon_rflags = CANONICAL_RFLAGS(rflags);
+
+ if(canon_rflags & ROLE_DO_INCOLS)
+ err += sub_write_patterns(ROLE_DO_INCOLS | (rflags & PAT_USE_MASK));
+ if(!err && canon_rflags & ROLE_DO_OTHER)
+ err += sub_write_patterns(ROLE_DO_OTHER | (rflags & PAT_USE_MASK));
+ if(!err && canon_rflags & ROLE_DO_FILTER)
+ err += sub_write_patterns(ROLE_DO_FILTER | (rflags & PAT_USE_MASK));
+ if(!err && canon_rflags & ROLE_DO_SCORES)
+ err += sub_write_patterns(ROLE_DO_SCORES | (rflags & PAT_USE_MASK));
+ if(!err && canon_rflags & ROLE_DO_ROLES)
+ err += sub_write_patterns(ROLE_DO_ROLES | (rflags & PAT_USE_MASK));
+ if(!err && canon_rflags & ROLE_DO_SRCH)
+ err += sub_write_patterns(ROLE_DO_SRCH | (rflags & PAT_USE_MASK));
+
+ if(!err && !(rflags & PAT_USE_CHANGED))
+ write_pinerc(ps_global, (rflags & PAT_USE_MAIN) ? Main : Post, WRP_NONE);
+
+ return(err);
+}
+
+
+int
+sub_write_patterns(long int rflags)
+{
+ int err = 0, lineno = 0;
+ char **lvalue = NULL;
+ PAT_LINE_S *patline;
+
+ SET_PATTYPE(rflags);
+
+ if(!(*cur_pat_h)){
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ "Unknown error saving patterns");
+ return(-1);
+ }
+
+ if((*cur_pat_h)->dirtypinerc){
+ /* Count how many lines will be in patterns variable */
+ for(patline = (*cur_pat_h)->patlinehead;
+ patline;
+ patline = patline->next)
+ lineno++;
+
+ lvalue = (char **)fs_get((lineno+1)*sizeof(char *));
+ memset(lvalue, 0, (lineno+1) * sizeof(char *));
+ }
+
+ for(patline = (*cur_pat_h)->patlinehead, lineno = 0;
+ !err && patline;
+ patline = patline->next, lineno++){
+ if(patline->type == File)
+ err = write_pattern_file((*cur_pat_h)->dirtypinerc
+ ? &lvalue[lineno] : NULL, patline);
+ else if(patline->type == Literal && (*cur_pat_h)->dirtypinerc)
+ err = write_pattern_lit(&lvalue[lineno], patline);
+ else if(patline->type == Inherit)
+ err = write_pattern_inherit((*cur_pat_h)->dirtypinerc
+ ? &lvalue[lineno] : NULL, patline);
+ }
+
+ if((*cur_pat_h)->dirtypinerc){
+ if(err)
+ free_list_array(&lvalue);
+ else{
+ char ***alval;
+ struct variable *var;
+
+ if(rflags & ROLE_DO_ROLES)
+ var = &ps_global->vars[V_PAT_ROLES];
+ else if(rflags & ROLE_DO_OTHER)
+ var = &ps_global->vars[V_PAT_OTHER];
+ else if(rflags & ROLE_DO_FILTER)
+ var = &ps_global->vars[V_PAT_FILTS];
+ else if(rflags & ROLE_DO_SCORES)
+ var = &ps_global->vars[V_PAT_SCORES];
+ else if(rflags & ROLE_DO_INCOLS)
+ var = &ps_global->vars[V_PAT_INCOLS];
+ else if(rflags & ROLE_DO_SRCH)
+ var = &ps_global->vars[V_PAT_SRCH];
+
+ alval = (rflags & PAT_USE_CHANGED) ? &(var->changed_val.l)
+ : ALVAL(var, (rflags & PAT_USE_MAIN) ? Main : Post);
+ if(*alval)
+ free_list_array(alval);
+
+ if(rflags & PAT_USE_CHANGED) var->is_changed_val = 1;
+
+ *alval = lvalue;
+
+ if(!(rflags & PAT_USE_CHANGED))
+ set_current_val(var, TRUE, TRUE);
+ }
+ }
+
+ if(!err)
+ (*cur_pat_h)->dirtypinerc = 0;
+
+ return(err);
+}
+
+
+/*
+ * Write pattern lines into a file.
+ *
+ * Args lvalue -- Pointer to char * to fill in variable value
+ * patline --
+ *
+ * Returns 0 -- all is ok, lvalue has been filled in, file has been written
+ * else -- error, lvalue untouched, file not written
+ */
+int
+write_pattern_file(char **lvalue, PAT_LINE_S *patline)
+{
+ char *p, *tfile;
+ int fd = -1, err = 0;
+ FILE *fp_new;
+ PAT_S *pat;
+
+ dprint((7, "write_pattern_file(%s)\n",
+ (patline && patline->filepath) ? patline->filepath : "?"));
+
+ if(lvalue){
+ size_t l;
+
+ l = strlen(patline->filename) + 5;
+ p = (char *) fs_get((l+1) * sizeof(char));
+ strncpy(p, "FILE:", l+1);
+ p[l] = '\0';
+ strncat(p, patline->filename, l+1-1-strlen(p));
+ p[l] = '\0';
+ *lvalue = p;
+ }
+
+ if(patline->readonly || !patline->dirty) /* doesn't need writing */
+ return(err);
+
+ /* Get a tempfile to write the patterns into */
+ if(((tfile = tempfile_in_same_dir(patline->filepath, ".pt", NULL)) == NULL)
+ || ((fd = our_open(tfile, O_TRUNC|O_WRONLY|O_CREAT|O_BINARY, 0600)) < 0)
+ || ((fp_new = fdopen(fd, "w")) == NULL)){
+ q_status_message1(SM_ORDER | SM_DING, 3, 4,
+ "Can't write in directory containing file \"%.200s\"",
+ patline->filepath);
+ if(tfile){
+ our_unlink(tfile);
+ fs_give((void **)&tfile);
+ }
+
+ if(fd >= 0)
+ close(fd);
+
+ return(-1);
+ }
+
+ dprint((9, "write_pattern_file: writing into %s\n",
+ tfile ? tfile : "?"));
+
+ if(fprintf(fp_new, "%s %s\n", PATTERN_MAGIC, PATTERN_FILE_VERS) == EOF)
+ err--;
+
+ for(pat = patline->first; !err && pat; pat = pat->next){
+ if((p = data_for_patline(pat)) != NULL){
+ if(fprintf(fp_new, "%s\n", p) == EOF)
+ err--;
+
+ fs_give((void **)&p);
+ }
+ }
+
+ if(err || fclose(fp_new) == EOF){
+ if(err)
+ (void)fclose(fp_new);
+
+ err--;
+ q_status_message2(SM_ORDER | SM_DING, 3, 4,
+ "I/O error: \"%.200s\": %.200s",
+ tfile, error_description(errno));
+ }
+
+ if(!err && rename_file(tfile, patline->filepath) < 0){
+ err--;
+ q_status_message3(SM_ORDER | SM_DING, 3, 4,
+ _("Error renaming \"%s\" to \"%s\": %s"),
+ tfile, patline->filepath, error_description(errno));
+ dprint((2,
+ "write_pattern_file: Error renaming (%s,%s): %s\n",
+ tfile ? tfile : "?",
+ (patline && patline->filepath) ? patline->filepath : "?",
+ error_description(errno)));
+ }
+
+ if(tfile){
+ our_unlink(tfile);
+ fs_give((void **)&tfile);
+ }
+
+ if(!err)
+ patline->dirty = 0;
+
+ return(err);
+}
+
+
+/*
+ * Write literal pattern lines into lvalue (pinerc variable).
+ *
+ * Args lvalue -- Pointer to char * to fill in variable value
+ * patline --
+ *
+ * Returns 0 -- all is ok, lvalue has been filled in, file has been written
+ * else -- error, lvalue untouched, file not written
+ */
+int
+write_pattern_lit(char **lvalue, PAT_LINE_S *patline)
+{
+ char *p = NULL;
+ int err = 0;
+ PAT_S *pat;
+
+ pat = patline ? patline->first : NULL;
+
+ if(pat && lvalue && (p = data_for_patline(pat)) != NULL){
+ size_t l;
+
+ l = strlen(p) + 4;
+ *lvalue = (char *) fs_get((l+1) * sizeof(char));
+ strncpy(*lvalue, "LIT:", l+1);
+ (*lvalue)[l] = '\0';
+ strncat(*lvalue, p, l+1-1-strlen(*lvalue));
+ (*lvalue)[l] = '\0';
+ }
+ else{
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Unknown error saving pattern variable"));
+ err--;
+ }
+
+ if(p)
+ fs_give((void **)&p);
+
+ return(err);
+}
+
+
+int
+write_pattern_inherit(char **lvalue, PAT_LINE_S *patline)
+{
+ int err = 0;
+
+ if(patline && patline->type == Inherit && lvalue)
+ *lvalue = cpystr(INHERIT);
+ else
+ err--;
+
+ return(err);
+}
+
+
+
+char *
+data_for_patline(PAT_S *pat)
+{
+ char *p = NULL, *q, *to_pat = NULL,
+ *news_pat = NULL, *from_pat = NULL,
+ *sender_pat = NULL, *cc_pat = NULL, *subj_pat = NULL,
+ *arb_pat = NULL, *fldr_type_pat = NULL, *fldr_pat = NULL,
+ *afrom_type_pat = NULL, *abooks_pat = NULL,
+ *alltext_pat = NULL, *scorei_pat = NULL, *recip_pat = NULL,
+ *keyword_pat = NULL, *charset_pat = NULL,
+ *bodytext_pat = NULL, *age_pat = NULL, *sentdate = NULL,
+ *size_pat = NULL,
+ *category_cmd = NULL, *category_pat = NULL,
+ *category_lim = NULL,
+ *partic_pat = NULL, *stat_new_val = NULL,
+ *stat_rec_val = NULL,
+ *stat_imp_val = NULL, *stat_del_val = NULL,
+ *stat_ans_val = NULL, *stat_8bit_val = NULL,
+ *stat_bom_val = NULL, *stat_boy_val = NULL,
+ *from_act = NULL, *replyto_act = NULL, *fcc_act = NULL,
+ *sig_act = NULL, *nick = NULL, *templ_act = NULL,
+ *litsig_act = NULL, *cstm_act = NULL, *smtp_act = NULL,
+ *nntp_act = NULL, *comment = NULL,
+ *repl_val = NULL, *forw_val = NULL, *comp_val = NULL,
+ *incol_act = NULL, *inherit_nick = NULL,
+ *score_act = NULL, *hdrtok_act = NULL,
+ *sort_act = NULL, *iform_act = NULL, *start_act = NULL,
+ *folder_act = NULL, *filt_ifnotdel = NULL,
+ *filt_nokill = NULL, *filt_del_val = NULL,
+ *filt_imp_val = NULL, *filt_ans_val = NULL,
+ *filt_new_val = NULL, *filt_nonterm = NULL,
+ *keyword_set = NULL, *keyword_clr = NULL;
+ int to_not = 0, news_not = 0, from_not = 0,
+ sender_not = 0, cc_not = 0, subj_not = 0,
+ partic_not = 0, recip_not = 0, alltext_not, bodytext_not,
+ keyword_not = 0, charset_not = 0;
+ size_t l;
+ ACTION_S *action = NULL;
+ NAMEVAL_S *f;
+
+ if(!pat)
+ return(p);
+
+ if((pat->patgrp && pat->patgrp->bogus)
+ || (pat->action && pat->action->bogus)){
+ if(pat->raw)
+ p = cpystr(pat->raw);
+
+ return(p);
+ }
+
+ if(pat->patgrp){
+ if(pat->patgrp->nick)
+ if((nick = add_pat_escapes(pat->patgrp->nick)) && !*nick)
+ fs_give((void **) &nick);
+
+ if(pat->patgrp->comment)
+ if((comment = add_pat_escapes(pat->patgrp->comment)) && !*comment)
+ fs_give((void **) &comment);
+
+ if(pat->patgrp->to){
+ to_pat = pattern_to_config(pat->patgrp->to);
+ to_not = pat->patgrp->to->not;
+ }
+
+ if(pat->patgrp->from){
+ from_pat = pattern_to_config(pat->patgrp->from);
+ from_not = pat->patgrp->from->not;
+ }
+
+ if(pat->patgrp->sender){
+ sender_pat = pattern_to_config(pat->patgrp->sender);
+ sender_not = pat->patgrp->sender->not;
+ }
+
+ if(pat->patgrp->cc){
+ cc_pat = pattern_to_config(pat->patgrp->cc);
+ cc_not = pat->patgrp->cc->not;
+ }
+
+ if(pat->patgrp->recip){
+ recip_pat = pattern_to_config(pat->patgrp->recip);
+ recip_not = pat->patgrp->recip->not;
+ }
+
+ if(pat->patgrp->partic){
+ partic_pat = pattern_to_config(pat->patgrp->partic);
+ partic_not = pat->patgrp->partic->not;
+ }
+
+ if(pat->patgrp->news){
+ news_pat = pattern_to_config(pat->patgrp->news);
+ news_not = pat->patgrp->news->not;
+ }
+
+ if(pat->patgrp->subj){
+ subj_pat = pattern_to_config(pat->patgrp->subj);
+ subj_not = pat->patgrp->subj->not;
+ }
+
+ if(pat->patgrp->alltext){
+ alltext_pat = pattern_to_config(pat->patgrp->alltext);
+ alltext_not = pat->patgrp->alltext->not;
+ }
+
+ if(pat->patgrp->bodytext){
+ bodytext_pat = pattern_to_config(pat->patgrp->bodytext);
+ bodytext_not = pat->patgrp->bodytext->not;
+ }
+
+ if(pat->patgrp->keyword){
+ keyword_pat = pattern_to_config(pat->patgrp->keyword);
+ keyword_not = pat->patgrp->keyword->not;
+ }
+
+ if(pat->patgrp->charsets){
+ charset_pat = pattern_to_config(pat->patgrp->charsets);
+ charset_not = pat->patgrp->charsets->not;
+ }
+
+ if(pat->patgrp->arbhdr){
+ ARBHDR_S *a;
+ char *p1 = NULL, *p2 = NULL, *p3 = NULL, *p4 = NULL;
+ int len = 0;
+
+ /* This is brute force dumb, but who cares? */
+ for(a = pat->patgrp->arbhdr; a; a = a->next){
+ if(a->field && a->field[0]){
+ p1 = pattern_to_string(a->p);
+ p1 = p1 ? p1 : cpystr("");
+ l = strlen(a->field)+strlen(p1)+1;
+ p2 = (char *) fs_get((l+1) * sizeof(char));
+ snprintf(p2, l+1, "%s=%s", a->field, p1);
+ p3 = add_pat_escapes(p2);
+ l = strlen(p3)+6;
+ p4 = (char *) fs_get((l+1) * sizeof(char));
+ snprintf(p4, l+1, "/%s%sARB%s",
+ (a->p && a->p->not) ? "!" : "",
+ a->isemptyval ? "E" : "", p3);
+ len += strlen(p4);
+
+ if(p1)
+ fs_give((void **)&p1);
+ if(p2)
+ fs_give((void **)&p2);
+ if(p3)
+ fs_give((void **)&p3);
+ if(p4)
+ fs_give((void **)&p4);
+ }
+ }
+
+ p = arb_pat = (char *)fs_get((len + 1) * sizeof(char));
+
+ for(a = pat->patgrp->arbhdr; a; a = a->next){
+ if(a->field && a->field[0]){
+ p1 = pattern_to_string(a->p);
+ p1 = p1 ? p1 : cpystr("");
+ l = strlen(a->field)+strlen(p1)+1;
+ p2 = (char *) fs_get((l+1) * sizeof(char));
+ snprintf(p2, l+1, "%s=%s", a->field, p1);
+ p3 = add_pat_escapes(p2);
+ l = strlen(p3)+6;
+ p4 = (char *) fs_get((l+1) * sizeof(char));
+ snprintf(p4, l+1, "/%s%sARB%s",
+ (a->p && a->p->not) ? "!" : "",
+ a->isemptyval ? "E" : "", p3);
+ sstrncpy(&p, p4, len+1-(p-arb_pat));
+
+ if(p1)
+ fs_give((void **)&p1);
+ if(p2)
+ fs_give((void **)&p2);
+ if(p3)
+ fs_give((void **)&p3);
+ if(p4)
+ fs_give((void **)&p4);
+ }
+ }
+
+ arb_pat[len] = '\0';
+ }
+
+ if(pat->patgrp->age_uses_sentdate)
+ sentdate = cpystr("/SENTDATE=1");
+
+ if(pat->patgrp->do_score){
+ p = stringform_of_intvl(pat->patgrp->score);
+ if(p){
+ scorei_pat = add_pat_escapes(p);
+ fs_give((void **)&p);
+ }
+ }
+
+ if(pat->patgrp->do_age){
+ p = stringform_of_intvl(pat->patgrp->age);
+ if(p){
+ age_pat = add_pat_escapes(p);
+ fs_give((void **)&p);
+ }
+ }
+
+ if(pat->patgrp->do_size){
+ p = stringform_of_intvl(pat->patgrp->size);
+ if(p){
+ size_pat = add_pat_escapes(p);
+ fs_give((void **)&p);
+ }
+ }
+
+ if(pat->patgrp->category_cmd && pat->patgrp->category_cmd[0]){
+ size_t sz;
+ char **l, *q;
+
+ /* concatenate into string with commas first */
+ sz = 0;
+ for(l = pat->patgrp->category_cmd; l[0] && l[0][0]; l++)
+ sz += strlen(l[0]) + 1;
+
+ if(sz){
+ char *p;
+ int first_one = 1;
+
+ q = (char *)fs_get(sz);
+ memset(q, 0, sz);
+ p = q;
+ for(l = pat->patgrp->category_cmd; l[0] && l[0][0]; l++){
+ if(!first_one)
+ sstrncpy(&p, ",", sz-(p-q));
+
+ first_one = 0;
+ sstrncpy(&p, l[0], sz-(p-q));
+ }
+
+ q[sz-1] = '\0';
+
+ category_cmd = add_pat_escapes(q);
+ fs_give((void **)&q);
+ }
+ }
+
+ if(pat->patgrp->do_cat){
+ p = stringform_of_intvl(pat->patgrp->cat);
+ if(p){
+ category_pat = add_pat_escapes(p);
+ fs_give((void **)&p);
+ }
+ }
+
+ if(pat->patgrp->cat_lim != -1L){
+ category_lim = (char *) fs_get(20 * sizeof(char));
+ snprintf(category_lim, 20, "%ld", pat->patgrp->cat_lim);
+ }
+
+ if((f = pat_fldr_types(pat->patgrp->fldr_type)) != NULL)
+ fldr_type_pat = f->shortname;
+
+ if(pat->patgrp->folder)
+ fldr_pat = pattern_to_config(pat->patgrp->folder);
+
+ if((f = inabook_fldr_types(pat->patgrp->inabook)) != NULL
+ && f->value != IAB_DEFL)
+ afrom_type_pat = f->shortname;
+
+ if(pat->patgrp->abooks)
+ abooks_pat = pattern_to_config(pat->patgrp->abooks);
+
+ if(pat->patgrp->stat_new != PAT_STAT_EITHER &&
+ (f = role_status_types(pat->patgrp->stat_new)) != NULL)
+ stat_new_val = f->shortname;
+
+ if(pat->patgrp->stat_rec != PAT_STAT_EITHER &&
+ (f = role_status_types(pat->patgrp->stat_rec)) != NULL)
+ stat_rec_val = f->shortname;
+
+ if(pat->patgrp->stat_del != PAT_STAT_EITHER &&
+ (f = role_status_types(pat->patgrp->stat_del)) != NULL)
+ stat_del_val = f->shortname;
+
+ if(pat->patgrp->stat_ans != PAT_STAT_EITHER &&
+ (f = role_status_types(pat->patgrp->stat_ans)) != NULL)
+ stat_ans_val = f->shortname;
+
+ if(pat->patgrp->stat_imp != PAT_STAT_EITHER &&
+ (f = role_status_types(pat->patgrp->stat_imp)) != NULL)
+ stat_imp_val = f->shortname;
+
+ if(pat->patgrp->stat_8bitsubj != PAT_STAT_EITHER &&
+ (f = role_status_types(pat->patgrp->stat_8bitsubj)) != NULL)
+ stat_8bit_val = f->shortname;
+
+ if(pat->patgrp->stat_bom != PAT_STAT_EITHER &&
+ (f = role_status_types(pat->patgrp->stat_bom)) != NULL)
+ stat_bom_val = f->shortname;
+
+ if(pat->patgrp->stat_boy != PAT_STAT_EITHER &&
+ (f = role_status_types(pat->patgrp->stat_boy)) != NULL)
+ stat_boy_val = f->shortname;
+ }
+
+ if(pat->action){
+ action = pat->action;
+
+ if(action->is_a_score){
+ if(action->scoreval != 0L &&
+ action->scoreval >= SCORE_MIN && action->scoreval <= SCORE_MAX){
+ score_act = (char *) fs_get(5 * sizeof(char));
+ snprintf(score_act, 5, "%ld", pat->action->scoreval);
+ }
+
+ if(action->scorevalhdrtok)
+ hdrtok_act = hdrtok_to_config(action->scorevalhdrtok);
+ }
+
+ if(action->is_a_role){
+ if(action->inherit_nick)
+ inherit_nick = add_pat_escapes(action->inherit_nick);
+ if(action->fcc)
+ fcc_act = add_pat_escapes(action->fcc);
+ if(action->litsig)
+ litsig_act = add_pat_escapes(action->litsig);
+ if(action->sig)
+ sig_act = add_pat_escapes(action->sig);
+ if(action->template)
+ templ_act = add_pat_escapes(action->template);
+
+ if(action->cstm){
+ size_t sz;
+ char **l, *q;
+
+ /* concatenate into string with commas first */
+ sz = 0;
+ for(l = action->cstm; l[0] && l[0][0]; l++)
+ sz += strlen(l[0]) + 1;
+
+ if(sz){
+ char *p;
+ int first_one = 1;
+
+ q = (char *)fs_get(sz);
+ memset(q, 0, sz);
+ p = q;
+ for(l = action->cstm; l[0] && l[0][0]; l++){
+ if((!struncmp(l[0], "from", 4) &&
+ (l[0][4] == ':' || l[0][4] == '\0')) ||
+ (!struncmp(l[0], "reply-to", 8) &&
+ (l[0][8] == ':' || l[0][8] == '\0')))
+ continue;
+
+ if(!first_one)
+ sstrncpy(&p, ",", sz-(p-q));
+
+ first_one = 0;
+ sstrncpy(&p, l[0], sz-(p-q));
+ }
+
+ q[sz-1] = '\0';
+
+ cstm_act = add_pat_escapes(q);
+ fs_give((void **)&q);
+ }
+ }
+
+ if(action->smtp){
+ size_t sz;
+ char **l, *q;
+
+ /* concatenate into string with commas first */
+ sz = 0;
+ for(l = action->smtp; l[0] && l[0][0]; l++)
+ sz += strlen(l[0]) + 1;
+
+ if(sz){
+ char *p;
+ int first_one = 1;
+
+ q = (char *)fs_get(sz);
+ memset(q, 0, sz);
+ p = q;
+ for(l = action->smtp; l[0] && l[0][0]; l++){
+ if(!first_one)
+ sstrncpy(&p, ",", sz-(p-q));
+
+ first_one = 0;
+ sstrncpy(&p, l[0], sz-(p-q));
+ }
+
+ q[sz-1] = '\0';
+
+ smtp_act = add_pat_escapes(q);
+ fs_give((void **)&q);
+ }
+ }
+
+ if(action->nntp){
+ size_t sz;
+ char **l, *q;
+
+ /* concatenate into string with commas first */
+ sz = 0;
+ for(l = action->nntp; l[0] && l[0][0]; l++)
+ sz += strlen(l[0]) + 1;
+
+ if(sz){
+ char *p;
+ int first_one = 1;
+
+ q = (char *)fs_get(sz);
+ memset(q, 0, sz);
+ p = q;
+ for(l = action->nntp; l[0] && l[0][0]; l++){
+ if(!first_one)
+ sstrncpy(&p, ",", sz-(p-q));
+
+ first_one = 0;
+ sstrncpy(&p, l[0], sz-(p-q));
+ }
+
+ q[sz-1] = '\0';
+
+ nntp_act = add_pat_escapes(q);
+ fs_give((void **)&q);
+ }
+ }
+
+ if((f = role_repl_types(action->repl_type)) != NULL)
+ repl_val = f->shortname;
+
+ if((f = role_forw_types(action->forw_type)) != NULL)
+ forw_val = f->shortname;
+
+ if((f = role_comp_types(action->comp_type)) != NULL)
+ comp_val = f->shortname;
+ }
+
+ if(action->is_a_incol && action->incol){
+ char *ptr, buf[256], *p1, *p2;
+
+ ptr = buf;
+ memset(buf, 0, sizeof(buf));
+ sstrncpy(&ptr, "/FG=", sizeof(buf)-(ptr-buf));
+ sstrncpy(&ptr, (p1=add_pat_escapes(action->incol->fg)), sizeof(buf)-(ptr-buf));
+ sstrncpy(&ptr, "/BG=", sizeof(buf)-(ptr-buf));
+ sstrncpy(&ptr, (p2=add_pat_escapes(action->incol->bg)), sizeof(buf)-(ptr-buf));
+ buf[sizeof(buf)-1] = '\0';
+ /* the colors will be doubly escaped */
+ incol_act = add_pat_escapes(buf);
+ if(p1)
+ fs_give((void **)&p1);
+
+ if(p2)
+ fs_give((void **)&p2);
+ }
+
+ if(action->is_a_other){
+ char buf[256];
+
+ if(action->sort_is_set){
+ snprintf(buf, sizeof(buf), "%.50s%.50s",
+ sort_name(action->sortorder),
+ action->revsort ? "/Reverse" : "");
+ sort_act = add_pat_escapes(buf);
+ }
+
+ if(action->index_format)
+ iform_act = add_pat_escapes(action->index_format);
+
+ if(action->startup_rule != IS_NOTSET &&
+ (f = startup_rules(action->startup_rule)) != NULL)
+ start_act = S_OR_L(f);
+ }
+
+ if(action->is_a_role && action->from){
+ char *bufp;
+ size_t len;
+
+ len = est_size(action->from);
+ bufp = (char *) fs_get(len * sizeof(char));
+ p = addr_string_mult(action->from, bufp, len);
+ if(p){
+ from_act = add_pat_escapes(p);
+ fs_give((void **)&p);
+ }
+ }
+
+ if(action->is_a_role && action->replyto){
+ char *bufp;
+ size_t len;
+
+ len = est_size(action->replyto);
+ bufp = (char *) fs_get(len * sizeof(char));
+ p = addr_string_mult(action->replyto, bufp, len);
+ if(p){
+ replyto_act = add_pat_escapes(p);
+ fs_give((void **)&p);
+ }
+ }
+
+ if(action->is_a_filter){
+ if(action->folder){
+ if((folder_act = pattern_to_config(action->folder)) != NULL){
+ if(action->move_only_if_not_deleted)
+ filt_ifnotdel = cpystr("/NOTDEL=1");
+ }
+ }
+
+ if(action->keyword_set)
+ keyword_set = pattern_to_config(action->keyword_set);
+
+ if(action->keyword_clr)
+ keyword_clr = pattern_to_config(action->keyword_clr);
+
+ if(!action->kill)
+ filt_nokill = cpystr("/NOKILL=1");
+
+ if(action->non_terminating)
+ filt_nonterm = cpystr("/NONTERM=1");
+
+ if(action->state_setting_bits){
+ char buf[256];
+ int dval, nval, ival, aval;
+
+ buf[0] = '\0';
+ p = buf;
+
+ convert_statebits_to_vals(action->state_setting_bits,
+ &dval, &aval, &ival, &nval);
+ if(dval != ACT_STAT_LEAVE &&
+ (f = msg_state_types(dval)) != NULL)
+ filt_del_val = f->shortname;
+
+ if(aval != ACT_STAT_LEAVE &&
+ (f = msg_state_types(aval)) != NULL)
+ filt_ans_val = f->shortname;
+
+ if(ival != ACT_STAT_LEAVE &&
+ (f = msg_state_types(ival)) != NULL)
+ filt_imp_val = f->shortname;
+
+ if(nval != ACT_STAT_LEAVE &&
+ (f = msg_state_types(nval)) != NULL)
+ filt_new_val = f->shortname;
+ }
+ }
+ }
+
+ l = strlen(nick ? nick : "Alternate Role") +
+ strlen(comment ? comment : "") +
+ strlen(to_pat ? to_pat : "") +
+ strlen(from_pat ? from_pat : "") +
+ strlen(sender_pat ? sender_pat : "") +
+ strlen(cc_pat ? cc_pat : "") +
+ strlen(recip_pat ? recip_pat : "") +
+ strlen(partic_pat ? partic_pat : "") +
+ strlen(news_pat ? news_pat : "") +
+ strlen(subj_pat ? subj_pat : "") +
+ strlen(alltext_pat ? alltext_pat : "") +
+ strlen(bodytext_pat ? bodytext_pat : "") +
+ strlen(arb_pat ? arb_pat : "") +
+ strlen(scorei_pat ? scorei_pat : "") +
+ strlen(keyword_pat ? keyword_pat : "") +
+ strlen(charset_pat ? charset_pat : "") +
+ strlen(age_pat ? age_pat : "") +
+ strlen(size_pat ? size_pat : "") +
+ strlen(category_cmd ? category_cmd : "") +
+ strlen(category_pat ? category_pat : "") +
+ strlen(category_lim ? category_lim : "") +
+ strlen(fldr_pat ? fldr_pat : "") +
+ strlen(abooks_pat ? abooks_pat : "") +
+ strlen(sentdate ? sentdate : "") +
+ strlen(inherit_nick ? inherit_nick : "") +
+ strlen(score_act ? score_act : "") +
+ strlen(hdrtok_act ? hdrtok_act : "") +
+ strlen(from_act ? from_act : "") +
+ strlen(replyto_act ? replyto_act : "") +
+ strlen(fcc_act ? fcc_act : "") +
+ strlen(litsig_act ? litsig_act : "") +
+ strlen(cstm_act ? cstm_act : "") +
+ strlen(smtp_act ? smtp_act : "") +
+ strlen(nntp_act ? nntp_act : "") +
+ strlen(sig_act ? sig_act : "") +
+ strlen(incol_act ? incol_act : "") +
+ strlen(sort_act ? sort_act : "") +
+ strlen(iform_act ? iform_act : "") +
+ strlen(start_act ? start_act : "") +
+ strlen(filt_ifnotdel ? filt_ifnotdel : "") +
+ strlen(filt_nokill ? filt_nokill : "") +
+ strlen(filt_nonterm ? filt_nonterm : "") +
+ (folder_act ? (strlen(folder_act) + 8) : 0) +
+ strlen(keyword_set ? keyword_set : "") +
+ strlen(keyword_clr ? keyword_clr : "") +
+ strlen(templ_act ? templ_act : "") + 540;
+ /*
+ * The +540 above is larger than needed but not everything is accounted
+ * for with the strlens.
+ */
+ p = (char *) fs_get(l * sizeof(char));
+
+ q = p;
+ sstrncpy(&q, "pattern=\"/NICK=", l-(q-p));
+
+ if(nick){
+ sstrncpy(&q, nick, l-(q-p));
+ fs_give((void **) &nick);
+ }
+ else
+ sstrncpy(&q, "Alternate Role", l-(q-p));
+
+ if(comment){
+ sstrncpy(&q, "/", l-(q-p));
+ sstrncpy(&q, "COMM=", l-(q-p));
+ sstrncpy(&q, comment, l-(q-p));
+ fs_give((void **) &comment);
+ }
+
+ if(to_pat){
+ sstrncpy(&q, "/", l-(q-p));
+ if(to_not)
+ sstrncpy(&q, "!", l-(q-p));
+
+ sstrncpy(&q, "TO=", l-(q-p));
+ sstrncpy(&q, to_pat, l-(q-p));
+ fs_give((void **) &to_pat);
+ }
+
+ if(from_pat){
+ sstrncpy(&q, "/", l-(q-p));
+ if(from_not)
+ sstrncpy(&q, "!", l-(q-p));
+
+ sstrncpy(&q, "FROM=", l-(q-p));
+ sstrncpy(&q, from_pat, l-(q-p));
+ fs_give((void **) &from_pat);
+ }
+
+ if(sender_pat){
+ sstrncpy(&q, "/", l-(q-p));
+ if(sender_not)
+ sstrncpy(&q, "!", l-(q-p));
+
+ sstrncpy(&q, "SENDER=", l-(q-p));
+ sstrncpy(&q, sender_pat, l-(q-p));
+ fs_give((void **) &sender_pat);
+ }
+
+ if(cc_pat){
+ sstrncpy(&q,"/", l-(q-p));
+ if(cc_not)
+ sstrncpy(&q, "!", l-(q-p));
+
+ sstrncpy(&q,"CC=", l-(q-p));
+ sstrncpy(&q, cc_pat, l-(q-p));
+ fs_give((void **) &cc_pat);
+ }
+
+ if(recip_pat){
+ sstrncpy(&q, "/", l-(q-p));
+ if(recip_not)
+ sstrncpy(&q, "!", l-(q-p));
+
+ sstrncpy(&q, "RECIP=", l-(q-p));
+ sstrncpy(&q, recip_pat, l-(q-p));
+ fs_give((void **) &recip_pat);
+ }
+
+ if(partic_pat){
+ sstrncpy(&q, "/", l-(q-p));
+ if(partic_not)
+ sstrncpy(&q, "!", l-(q-p));
+
+ sstrncpy(&q, "PARTIC=", l-(q-p));
+ sstrncpy(&q, partic_pat, l-(q-p));
+ fs_give((void **) &partic_pat);
+ }
+
+ if(news_pat){
+ sstrncpy(&q, "/", l-(q-p));
+ if(news_not)
+ sstrncpy(&q, "!", l-(q-p));
+
+ sstrncpy(&q, "NEWS=", l-(q-p));
+ sstrncpy(&q, news_pat, l-(q-p));
+ fs_give((void **) &news_pat);
+ }
+
+ if(subj_pat){
+ sstrncpy(&q, "/", l-(q-p));
+ if(subj_not)
+ sstrncpy(&q, "!", l-(q-p));
+
+ sstrncpy(&q, "SUBJ=", l-(q-p));
+ sstrncpy(&q, subj_pat, l-(q-p));
+ fs_give((void **)&subj_pat);
+ }
+
+ if(alltext_pat){
+ sstrncpy(&q, "/", l-(q-p));
+ if(alltext_not)
+ sstrncpy(&q, "!", l-(q-p));
+
+ sstrncpy(&q, "ALL=", l-(q-p));
+ sstrncpy(&q, alltext_pat, l-(q-p));
+ fs_give((void **) &alltext_pat);
+ }
+
+ if(bodytext_pat){
+ sstrncpy(&q, "/", l-(q-p));
+ if(bodytext_not)
+ sstrncpy(&q, "!", l-(q-p));
+
+ sstrncpy(&q, "BODY=", l-(q-p));
+ sstrncpy(&q, bodytext_pat, l-(q-p));
+ fs_give((void **) &bodytext_pat);
+ }
+
+ if(keyword_pat){
+ sstrncpy(&q, "/", l-(q-p));
+ if(keyword_not)
+ sstrncpy(&q, "!", l-(q-p));
+
+ sstrncpy(&q, "KEY=", l-(q-p));
+ sstrncpy(&q, keyword_pat, l-(q-p));
+ fs_give((void **) &keyword_pat);
+ }
+
+ if(charset_pat){
+ sstrncpy(&q, "/", l-(q-p));
+ if(charset_not)
+ sstrncpy(&q, "!", l-(q-p));
+
+ sstrncpy(&q, "CHAR=", l-(q-p));
+ sstrncpy(&q, charset_pat, l-(q-p));
+ fs_give((void **) &charset_pat);
+ }
+
+ if(arb_pat){
+ sstrncpy(&q, arb_pat, l-(q-p));
+ fs_give((void **)&arb_pat);
+ }
+
+ if(scorei_pat){
+ sstrncpy(&q, "/SCOREI=", l-(q-p));
+ sstrncpy(&q, scorei_pat, l-(q-p));
+ fs_give((void **) &scorei_pat);
+ }
+
+ if(age_pat){
+ sstrncpy(&q, "/AGE=", l-(q-p));
+ sstrncpy(&q, age_pat, l-(q-p));
+ fs_give((void **) &age_pat);
+ }
+
+ if(size_pat){
+ sstrncpy(&q, "/SIZE=", l-(q-p));
+ sstrncpy(&q, size_pat, l-(q-p));
+ fs_give((void **) &size_pat);
+ }
+
+ if(category_cmd){
+ sstrncpy(&q, "/CATCMD=", l-(q-p));
+ sstrncpy(&q, category_cmd, l-(q-p));
+ fs_give((void **) &category_cmd);
+ }
+
+ if(category_pat){
+ sstrncpy(&q, "/CATVAL=", l-(q-p));
+ sstrncpy(&q, category_pat, l-(q-p));
+ fs_give((void **) &category_pat);
+ }
+
+ if(category_lim){
+ sstrncpy(&q, "/CATLIM=", l-(q-p));
+ sstrncpy(&q, category_lim, l-(q-p));
+ fs_give((void **) &category_lim);
+ }
+
+ if(sentdate){
+ sstrncpy(&q, sentdate, l-(q-p));
+ fs_give((void **) &sentdate);
+ }
+
+ if(fldr_type_pat){
+ sstrncpy(&q, "/FLDTYPE=", l-(q-p));
+ sstrncpy(&q, fldr_type_pat, l-(q-p));
+ }
+
+ if(fldr_pat){
+ sstrncpy(&q, "/FOLDER=", l-(q-p));
+ sstrncpy(&q, fldr_pat, l-(q-p));
+ fs_give((void **) &fldr_pat);
+ }
+
+ if(afrom_type_pat){
+ sstrncpy(&q, "/AFROM=", l-(q-p));
+ sstrncpy(&q, afrom_type_pat, l-(q-p));
+
+ /*
+ * Add address types. If it is From or Reply-to
+ * leave this out so it will still work with pine.
+ */
+ if((pat->patgrp->inabook & IAB_FROM
+ && pat->patgrp->inabook & IAB_REPLYTO
+ && !(pat->patgrp->inabook & IAB_SENDER)
+ && !(pat->patgrp->inabook & IAB_TO)
+ && !(pat->patgrp->inabook & IAB_CC))
+ ||
+ (!(pat->patgrp->inabook & IAB_FROM)
+ && !(pat->patgrp->inabook & IAB_REPLYTO)
+ && !(pat->patgrp->inabook & IAB_SENDER)
+ && !(pat->patgrp->inabook & IAB_TO)
+ && !(pat->patgrp->inabook & IAB_CC))){
+ ; /* leave it out */
+ }
+ else{
+ sstrncpy(&q, "/AFROMA=", l-(q-p));
+ if(pat->patgrp->inabook & IAB_FROM)
+ sstrncpy(&q, "F", l-(q-p));
+
+ if(pat->patgrp->inabook & IAB_REPLYTO)
+ sstrncpy(&q, "R", l-(q-p));
+
+ if(pat->patgrp->inabook & IAB_SENDER)
+ sstrncpy(&q, "S", l-(q-p));
+
+ if(pat->patgrp->inabook & IAB_TO)
+ sstrncpy(&q, "T", l-(q-p));
+
+ if(pat->patgrp->inabook & IAB_CC)
+ sstrncpy(&q, "C", l-(q-p));
+ }
+ }
+
+ if(abooks_pat){
+ sstrncpy(&q, "/ABOOKS=", l-(q-p));
+ sstrncpy(&q, abooks_pat, l-(q-p));
+ fs_give((void **) &abooks_pat);
+ }
+
+ if(stat_new_val){
+ sstrncpy(&q, "/STATN=", l-(q-p));
+ sstrncpy(&q, stat_new_val, l-(q-p));
+ }
+
+ if(stat_rec_val){
+ sstrncpy(&q, "/STATR=", l-(q-p));
+ sstrncpy(&q, stat_rec_val, l-(q-p));
+ }
+
+ if(stat_del_val){
+ sstrncpy(&q, "/STATD=", l-(q-p));
+ sstrncpy(&q, stat_del_val, l-(q-p));
+ }
+
+ if(stat_imp_val){
+ sstrncpy(&q, "/STATI=", l-(q-p));
+ sstrncpy(&q, stat_imp_val, l-(q-p));
+ }
+
+ if(stat_ans_val){
+ sstrncpy(&q, "/STATA=", l-(q-p));
+ sstrncpy(&q, stat_ans_val, l-(q-p));
+ }
+
+ if(stat_8bit_val){
+ sstrncpy(&q, "/8BITS=", l-(q-p));
+ sstrncpy(&q, stat_8bit_val, l-(q-p));
+ }
+
+ if(stat_bom_val){
+ sstrncpy(&q, "/BOM=", l-(q-p));
+ sstrncpy(&q, stat_bom_val, l-(q-p));
+ }
+
+ if(stat_boy_val){
+ sstrncpy(&q, "/BOY=", l-(q-p));
+ sstrncpy(&q, stat_boy_val, l-(q-p));
+ }
+
+ sstrncpy(&q, "\" action=\"", l-(q-p));
+
+ if(inherit_nick && *inherit_nick){
+ sstrncpy(&q, "/INICK=", l-(q-p));
+ sstrncpy(&q, inherit_nick, l-(q-p));
+ fs_give((void **)&inherit_nick);
+ }
+
+ if(action){
+ if(action->is_a_role)
+ sstrncpy(&q, "/ROLE=1", l-(q-p));
+
+ if(action->is_a_incol)
+ sstrncpy(&q, "/ISINCOL=1", l-(q-p));
+
+ if(action->is_a_srch)
+ sstrncpy(&q, "/ISSRCH=1", l-(q-p));
+
+ if(action->is_a_score)
+ sstrncpy(&q, "/ISSCORE=1", l-(q-p));
+
+ if(action->is_a_filter){
+ /*
+ * Older pine will interpret a filter that has no folder
+ * as a Delete, even if we set it up here to be a Just Set
+ * State filter. Disable the filter for older versions in that
+ * case. If kill is set then Delete is what is supposed to
+ * happen, so that's ok. If folder is set then Move is what is
+ * supposed to happen, so ok.
+ */
+ if(!action->kill && !action->folder)
+ sstrncpy(&q, "/FILTER=2", l-(q-p));
+ else
+ sstrncpy(&q, "/FILTER=1", l-(q-p));
+ }
+
+ if(action->is_a_other)
+ sstrncpy(&q, "/OTHER=1", l-(q-p));
+ }
+
+ if(score_act){
+ sstrncpy(&q, "/SCORE=", l-(q-p));
+ sstrncpy(&q, score_act, l-(q-p));
+ fs_give((void **)&score_act);
+ }
+
+ if(hdrtok_act){
+ sstrncpy(&q, "/SCOREHDRTOK=", l-(q-p));
+ sstrncpy(&q, hdrtok_act, l-(q-p));
+ fs_give((void **)&hdrtok_act);
+ }
+
+ if(from_act){
+ sstrncpy(&q, "/FROM=", l-(q-p));
+ sstrncpy(&q, from_act, l-(q-p));
+ fs_give((void **) &from_act);
+ }
+
+ if(replyto_act){
+ sstrncpy(&q, "/REPL=", l-(q-p));
+ sstrncpy(&q, replyto_act, l-(q-p));
+ fs_give((void **)&replyto_act);
+ }
+
+ if(fcc_act){
+ sstrncpy(&q, "/FCC=", l-(q-p));
+ sstrncpy(&q, fcc_act, l-(q-p));
+ fs_give((void **)&fcc_act);
+ }
+
+ if(litsig_act){
+ sstrncpy(&q, "/LSIG=", l-(q-p));
+ sstrncpy(&q, litsig_act, l-(q-p));
+ fs_give((void **)&litsig_act);
+ }
+
+ if(sig_act){
+ sstrncpy(&q, "/SIG=", l-(q-p));
+ sstrncpy(&q, sig_act, l-(q-p));
+ fs_give((void **)&sig_act);
+ }
+
+ if(templ_act){
+ sstrncpy(&q, "/TEMPLATE=", l-(q-p));
+ sstrncpy(&q, templ_act, l-(q-p));
+ fs_give((void **)&templ_act);
+ }
+
+ if(cstm_act){
+ sstrncpy(&q, "/CSTM=", l-(q-p));
+ sstrncpy(&q, cstm_act, l-(q-p));
+ fs_give((void **)&cstm_act);
+ }
+
+ if(smtp_act){
+ sstrncpy(&q, "/SMTP=", l-(q-p));
+ sstrncpy(&q, smtp_act, l-(q-p));
+ fs_give((void **)&smtp_act);
+ }
+
+ if(nntp_act){
+ sstrncpy(&q, "/NNTP=", l-(q-p));
+ sstrncpy(&q, nntp_act, l-(q-p));
+ fs_give((void **)&nntp_act);
+ }
+
+ if(repl_val){
+ sstrncpy(&q, "/RTYPE=", l-(q-p));
+ sstrncpy(&q, repl_val, l-(q-p));
+ }
+
+ if(forw_val){
+ sstrncpy(&q, "/FTYPE=", l-(q-p));
+ sstrncpy(&q, forw_val, l-(q-p));
+ }
+
+ if(comp_val){
+ sstrncpy(&q, "/CTYPE=", l-(q-p));
+ sstrncpy(&q, comp_val, l-(q-p));
+ }
+
+ if(incol_act){
+ sstrncpy(&q, "/INCOL=", l-(q-p));
+ sstrncpy(&q, incol_act, l-(q-p));
+ fs_give((void **)&incol_act);
+ }
+
+ if(sort_act){
+ sstrncpy(&q, "/SORT=", l-(q-p));
+ sstrncpy(&q, sort_act, l-(q-p));
+ fs_give((void **)&sort_act);
+ }
+
+ if(iform_act){
+ sstrncpy(&q, "/IFORM=", l-(q-p));
+ sstrncpy(&q, iform_act, l-(q-p));
+ fs_give((void **)&iform_act);
+ }
+
+ if(start_act){
+ sstrncpy(&q, "/START=", l-(q-p));
+ sstrncpy(&q, start_act, l-(q-p));
+ }
+
+ if(folder_act){
+ sstrncpy(&q, "/FOLDER=", l-(q-p));
+ sstrncpy(&q, folder_act, l-(q-p));
+ fs_give((void **) &folder_act);
+ }
+
+ if(filt_ifnotdel){
+ sstrncpy(&q, filt_ifnotdel, l-(q-p));
+ fs_give((void **) &filt_ifnotdel);
+ }
+
+ if(filt_nonterm){
+ sstrncpy(&q, filt_nonterm, l-(q-p));
+ fs_give((void **) &filt_nonterm);
+ }
+
+ if(filt_nokill){
+ sstrncpy(&q, filt_nokill, l-(q-p));
+ fs_give((void **) &filt_nokill);
+ }
+
+ if(filt_new_val){
+ sstrncpy(&q, "/STATN=", l-(q-p));
+ sstrncpy(&q, filt_new_val, l-(q-p));
+ }
+
+ if(filt_del_val){
+ sstrncpy(&q, "/STATD=", l-(q-p));
+ sstrncpy(&q, filt_del_val, l-(q-p));
+ }
+
+ if(filt_imp_val){
+ sstrncpy(&q, "/STATI=", l-(q-p));
+ sstrncpy(&q, filt_imp_val, l-(q-p));
+ }
+
+ if(filt_ans_val){
+ sstrncpy(&q, "/STATA=", l-(q-p));
+ sstrncpy(&q, filt_ans_val, l-(q-p));
+ }
+
+ if(keyword_set){
+ sstrncpy(&q, "/KEYSET=", l-(q-p));
+ sstrncpy(&q, keyword_set, l-(q-p));
+ fs_give((void **) &keyword_set);
+ }
+
+ if(keyword_clr){
+ sstrncpy(&q, "/KEYCLR=", l-(q-p));
+ sstrncpy(&q, keyword_clr, l-(q-p));
+ fs_give((void **) &keyword_clr);
+ }
+
+ if(q-p < l)
+ *q++ = '\"';
+
+ if(q-p < l)
+ *q = '\0';
+
+ p[l-1] = '\0';
+
+ return(p);
+}
+
+
+void
+convert_statebits_to_vals(long int bits, int *dval, int *aval, int *ival, int *nval)
+{
+ if(dval)
+ *dval = ACT_STAT_LEAVE;
+ if(aval)
+ *aval = ACT_STAT_LEAVE;
+ if(ival)
+ *ival = ACT_STAT_LEAVE;
+ if(nval)
+ *nval = ACT_STAT_LEAVE;
+
+ if(ival){
+ if(bits & F_FLAG)
+ *ival = ACT_STAT_SET;
+ else if(bits & F_UNFLAG)
+ *ival = ACT_STAT_CLEAR;
+ }
+
+ if(aval){
+ if(bits & F_ANS)
+ *aval = ACT_STAT_SET;
+ else if(bits & F_UNANS)
+ *aval = ACT_STAT_CLEAR;
+ }
+
+ if(dval){
+ if(bits & F_DEL)
+ *dval = ACT_STAT_SET;
+ else if(bits & F_UNDEL)
+ *dval = ACT_STAT_CLEAR;
+ }
+
+ if(nval){
+ if(bits & F_UNSEEN)
+ *nval = ACT_STAT_SET;
+ else if(bits & F_SEEN)
+ *nval = ACT_STAT_CLEAR;
+ }
+}
+
+
+/*
+ * The "searched" bit will be set for each message which matches.
+ *
+ * Args: patgrp -- Pattern to search with
+ * stream --
+ * searchset -- Restrict search to this set
+ * section -- Searching a section of the message, not the whole thing
+ * get_score -- Function to return the score for a message
+ * flags -- Most of these are flags to mail_search_full. However, we
+ * overload the flags namespace and pass some flags of our
+ * own in here that we pick off before calling mail_search.
+ * Danger, danger, don't overlap with flag values defined
+ * for c-client (that we want to use). Flags that we will
+ * use here are:
+ * MP_IN_CCLIENT_CB
+ * If this is set we are in a callback from c-client
+ * because some imap data arrived. We don't want to
+ * call c-client again because it isn't re-entrant safe.
+ * This is only a problem if we need to get the text of
+ * a message to do the search, the envelope is cached
+ * already.
+ * MP_NOT
+ * We want a ! of the patgrp in the search.
+ * We also throw in SE_FREE for free, since we create
+ * the search program here.
+ *
+ * Returns: 1 if any message in the searchset matches this pattern
+ * 0 if no matches
+ * -1 if couldn't perform search because of no_fetch restriction
+ */
+int
+match_pattern(PATGRP_S *patgrp, MAILSTREAM *stream, SEARCHSET *searchset,
+ char *section, long int (*get_score)(MAILSTREAM *, long int),
+ long int flags)
+{
+ SEARCHPGM *pgm;
+ SEARCHSET *s;
+ MESSAGECACHE *mc;
+ long i, msgno = 0L;
+ int in_client_callback = 0, not = 0;
+
+ dprint((7, "match_pattern\n"));
+
+ /*
+ * Is the current folder the right type and possibly the right specific
+ * folder for a match?
+ */
+ if(!(patgrp && !patgrp->bogus && match_pattern_folder(patgrp, stream)))
+ return(0);
+
+ /*
+ * NULL searchset means that there is no message to compare against.
+ * This is a match if the folder type matches above (that gets
+ * us here), and there are no patterns to match against.
+ *
+ * It is not totally clear what should be done in the case of an empty
+ * search set. If there is search criteria, and someone does something
+ * that is not specific to any messages (composing from scratch,
+ * forwarding an attachment), then we can't be sure what a user would
+ * expect. The original way was to just use the role, which we'll
+ * preserve here.
+ */
+ if(!searchset)
+ return(1);
+
+ /*
+ * change by sderr : match_pattern_folder will sometimes
+ * accept NULL streams, but if we are not in a folder-type-only
+ * match test, we don't
+ */
+ if(!stream)
+ return(0);
+
+ if(flags & MP_IN_CCLIENT_CB){
+ in_client_callback++;
+ flags &= ~MP_IN_CCLIENT_CB;
+ }
+
+ if(flags & MP_NOT){
+ not++;
+ flags &= ~MP_NOT;
+ }
+
+ flags |= SE_FREE;
+
+ if(patgrp->stat_bom != PAT_STAT_EITHER){
+ if(patgrp->stat_bom == PAT_STAT_YES){
+ if(!ps_global->beginning_of_month){
+ return(0);
+ }
+ }
+ else if(patgrp->stat_bom == PAT_STAT_NO){
+ if(ps_global->beginning_of_month){
+ return(0);
+ }
+ }
+ }
+
+ if(patgrp->stat_boy != PAT_STAT_EITHER){
+ if(patgrp->stat_boy == PAT_STAT_YES){
+ if(!ps_global->beginning_of_year){
+ return(0);
+ }
+ }
+ else if(patgrp->stat_boy == PAT_STAT_NO){
+ if(ps_global->beginning_of_year){
+ return(0);
+ }
+ }
+ }
+
+ if(in_client_callback && is_imap_stream(stream)
+ && (patgrp->alltext || patgrp->bodytext))
+ return(-1);
+
+ pgm = match_pattern_srchpgm(patgrp, stream, searchset);
+ if(not && !(is_imap_stream(stream) && !modern_imap_stream(stream))){
+ SEARCHPGM *srchpgm;
+
+ srchpgm = pgm;
+ pgm = mail_newsearchpgm();
+ pgm->not = mail_newsearchpgmlist();
+ pgm->not->pgm = srchpgm;
+ }
+
+ if((patgrp->alltext || patgrp->bodytext)
+ && (!is_imap_stream(stream) || modern_imap_stream(stream)))
+ /*
+ * Cache isn't going to work. Search on server.
+ * Except that is likely to not work on an old imap server because
+ * the OR criteria won't work and we are likely to have some ORs.
+ * So turn off the NOSERVER flag (and search on server if remote)
+ * unless the server is an old server. It doesn't matter if we
+ * turn if off if it's not an imap stream, but we do it anyway.
+ */
+ flags &= ~SE_NOSERVER;
+
+ if(section){
+ /*
+ * Mail_search_full only searches the top-level msg. We want to
+ * search an attached msg instead. First do the stuff
+ * that mail_search_full would have done before calling
+ * mail_search_msg, then call mail_search_msg with a section number.
+ * Mail_search_msg does take a section number even though
+ * mail_search_full doesn't.
+ */
+
+ /*
+ * We'll only ever set section if the searchset is a single message.
+ */
+ if(pgm->msgno->next == NULL && pgm->msgno->first == pgm->msgno->last)
+ msgno = pgm->msgno->first;
+
+ for(i = 1L; i <= stream->nmsgs; i++)
+ if((mc = mail_elt(stream, i)) != NULL)
+ mc->searched = NIL;
+
+ if(mail_search_msg(stream,msgno,section,pgm)
+ && msgno > 0L && msgno <= stream->nmsgs
+ && (mc = mail_elt(stream, msgno)))
+ mc->searched = T;
+
+ if(flags & SE_FREE)
+ mail_free_searchpgm(&pgm);
+ }
+ else{
+ /*
+ * Here we could be checking on the return value to see if
+ * the search was "successful" or not. It may be the case
+ * that we'd want to stop trying filtering if we got some
+ * sort of error, but for now we would just continue on
+ * to the next filter.
+ */
+ pine_mail_search_full(stream, "UTF-8", pgm, flags);
+ }
+
+ /* we searched without the not, reverse it */
+ if(not && is_imap_stream(stream) && !modern_imap_stream(stream)){
+ for(msgno = 1L; msgno < mn_get_total(sp_msgmap(stream)); msgno++)
+ if(stream && msgno && msgno <= stream->nmsgs
+ && (mc=mail_elt(stream,msgno)) && mc->searched)
+ mc->searched = NIL;
+ else
+ mc->searched = T;
+ }
+
+ /* check scores */
+ if(get_score && scores_are_used(SCOREUSE_GET) && patgrp->do_score){
+ char *savebits;
+ SEARCHSET *ss;
+
+ /*
+ * Get_score may call build_header_line recursively (we may
+ * be in build_header_line now) so we have to preserve and
+ * restore the sequence bits.
+ */
+ savebits = (char *)fs_get((stream->nmsgs+1) * sizeof(char));
+
+ for(i = 1L; i <= stream->nmsgs; i++){
+ if((mc = mail_elt(stream, i)) != NULL){
+ savebits[i] = mc->sequence;
+ mc->sequence = 0;
+ }
+ }
+
+ /*
+ * Build a searchset which will get all the scores that we
+ * need but not more.
+ */
+ for(s = searchset; s; s = s->next)
+ for(msgno = s->first; msgno <= s->last; msgno++)
+ if(msgno > 0L && msgno <= stream->nmsgs
+ && (mc = mail_elt(stream, msgno)) && mc->searched
+ && get_msg_score(stream, msgno) == SCORE_UNDEF)
+ mc->sequence = 1;
+
+ if((ss = build_searchset(stream)) != NULL){
+ (void)calculate_some_scores(stream, ss, in_client_callback);
+ mail_free_searchset(&ss);
+ }
+
+ /*
+ * Now check the scores versus the score intervals to see if
+ * any of the messages which have matched up to this point can
+ * be tossed because they don't match the score interval.
+ */
+ for(s = searchset; s; s = s->next)
+ for(msgno = s->first; msgno <= s->last; msgno++)
+ if(msgno > 0L && msgno <= stream->nmsgs
+ && (mc = mail_elt(stream, msgno)) && mc->searched){
+ long score;
+
+ score = (*get_score)(stream, msgno);
+
+ /*
+ * If the score is outside all of the intervals,
+ * turn off the searched bit.
+ * So that means we check each interval and if
+ * it is inside any interval we stop and leave
+ * the bit set. If it is outside we keep checking.
+ */
+ if(score != SCORE_UNDEF){
+ INTVL_S *iv;
+
+ for(iv = patgrp->score; iv; iv = iv->next)
+ if(score >= iv->imin && score <= iv->imax)
+ break;
+
+ if(!iv)
+ mc->searched = NIL;
+ }
+ }
+
+ for(i = 1L; i <= stream->nmsgs; i++)
+ if((mc = mail_elt(stream, i)) != NULL)
+ mc->sequence = savebits[i];
+
+ fs_give((void **)&savebits);
+ }
+
+ /* if there are still matches, check for 8bit subject match */
+ if(patgrp->stat_8bitsubj != PAT_STAT_EITHER)
+ find_8bitsubj_in_messages(stream, searchset, patgrp->stat_8bitsubj, 1);
+
+ /* if there are still matches, check for charset matches */
+ if(patgrp->charsets)
+ find_charsets_in_messages(stream, searchset, patgrp, 1);
+
+ /* Still matches, check addrbook */
+ if(patgrp->inabook != IAB_EITHER)
+ address_in_abook(stream, searchset, patgrp->inabook, patgrp->abooks);
+
+ /* Still matches? Run the categorization command on each msg. */
+ if(pith_opt_filter_pattern_cmd)
+ (*pith_opt_filter_pattern_cmd)(patgrp->category_cmd, searchset, stream, patgrp->cat_lim, patgrp->cat);
+
+ for(s = searchset; s; s = s->next)
+ for(msgno = s->first; msgno > 0L && msgno <= s->last; msgno++)
+ if(msgno > 0L && msgno <= stream->nmsgs
+ && (mc = mail_elt(stream, msgno)) && mc->searched)
+ return(1);
+
+ return(0);
+}
+
+
+/*
+ * Look through messages in searchset to see if they contain 8bit
+ * characters in their subjects. All of the messages in
+ * searchset should initially have the searched bit set. Turn off the
+ * searched bit where appropriate.
+ */
+void
+find_8bitsubj_in_messages(MAILSTREAM *stream, SEARCHSET *searchset,
+ int stat_8bitsubj, int saveseqbits)
+{
+ char *savebits = NULL;
+ SEARCHSET *s, *ss = NULL;
+ MESSAGECACHE *mc;
+ long count = 0L;
+ unsigned long msgno;
+
+ /*
+ * If we are being called while in build_header_line we may
+ * call build_header_line recursively. So save and restore the
+ * sequence bits.
+ */
+ if(saveseqbits)
+ savebits = (char *) fs_get((stream->nmsgs+1) * sizeof(char));
+
+ for(msgno = 1L; msgno <= stream->nmsgs; msgno++){
+ if((mc = mail_elt(stream, msgno)) != NULL){
+ if(savebits)
+ savebits[msgno] = mc->sequence;
+
+ mc->sequence = 0;
+ }
+ }
+
+ /*
+ * Build a searchset so we can look at all the envelopes
+ * we need to look at but only those we need to look at.
+ * Everything with the searched bit set is still a
+ * possibility, so restrict to that set.
+ */
+
+ for(s = searchset; s; s = s->next)
+ for(msgno = s->first; msgno <= s->last; msgno++)
+ if(msgno > 0L && msgno <= stream->nmsgs
+ && (mc = mail_elt(stream, msgno)) && mc->searched){
+ mc->sequence = 1;
+ count++;
+ }
+
+ ss = build_searchset(stream);
+
+ if(count){
+ SEARCHSET **sset;
+
+ mail_parameters(NULL, SET_FETCHLOOKAHEADLIMIT, (void *) count);
+
+ /*
+ * This causes the lookahead to fetch precisely
+ * the messages we want (in the searchset) instead
+ * of just fetching the next 20 sequential
+ * messages. If the searching so far has caused
+ * a sparse searchset in a large mailbox, the
+ * difference can be substantial.
+ * This resets automatically after the first fetch.
+ */
+ sset = (SEARCHSET **) mail_parameters(stream,
+ GET_FETCHLOOKAHEAD,
+ (void *) stream);
+ if(sset)
+ *sset = ss;
+ }
+
+ for(s = ss; s; s = s->next){
+ for(msgno = s->first; msgno <= s->last; msgno++){
+ ENVELOPE *e;
+
+ if(!stream || msgno <= 0L || msgno > stream->nmsgs)
+ continue;
+
+ e = pine_mail_fetchenvelope(stream, msgno);
+ if(stat_8bitsubj == PAT_STAT_YES){
+ if(e && e->subject){
+ char *p;
+
+ for(p = e->subject; *p; p++)
+ if(*p & 0x80)
+ break;
+
+ if(!*p && msgno > 0L && msgno <= stream->nmsgs
+ && (mc = mail_elt(stream, msgno)))
+ mc->searched = NIL;
+ }
+ else if(msgno > 0L && msgno <= stream->nmsgs
+ && (mc = mail_elt(stream, msgno)))
+ mc->searched = NIL;
+ }
+ else if(stat_8bitsubj == PAT_STAT_NO){
+ if(e && e->subject){
+ char *p;
+
+ for(p = e->subject; *p; p++)
+ if(*p & 0x80)
+ break;
+
+ if(*p && msgno > 0L && msgno <= stream->nmsgs
+ && (mc = mail_elt(stream, msgno)))
+ mc->searched = NIL;
+ }
+ }
+ }
+ }
+
+ if(savebits){
+ for(msgno = 1L; msgno <= stream->nmsgs; msgno++)
+ if((mc = mail_elt(stream, msgno)) != NULL)
+ mc->sequence = savebits[msgno];
+
+ fs_give((void **) &savebits);
+ }
+
+ if(ss)
+ mail_free_searchset(&ss);
+}
+
+
+/*
+ * Look through messages in searchset to see if they contain any of the
+ * charsets or scripts listed in charsets pattern. All of the messages in
+ * searchset should initially have the searched bit set. Turn off the
+ * searched bit where appropriate.
+ */
+void
+find_charsets_in_messages(MAILSTREAM *stream, SEARCHSET *searchset,
+ PATGRP_S *patgrp, int saveseqbits)
+{
+ char *savebits = NULL;
+ unsigned long msgno;
+ long count = 0L;
+ MESSAGECACHE *mc;
+ SEARCHSET *s, *ss;
+
+ if(!stream || !patgrp)
+ return;
+
+ /*
+ * When we actually want to use charsets, we convert it into a list
+ * of charsets instead of the mixed list of scripts and charsets and
+ * we eliminate duplicates. This is more efficient when we actually
+ * do the lookups and compares.
+ */
+ if(!patgrp->charsets_list){
+ PATTERN_S *cs;
+ const CHARSET *cset;
+ STRLIST_S *sl = NULL, *newsl;
+ unsigned long scripts = 0L;
+ SCRIPT *script;
+
+ for(cs = patgrp->charsets; cs; cs = cs->next){
+ /*
+ * Run through the charsets pattern looking for
+ * scripts and set the corresponding script bits.
+ * If it isn't a script, it is a character set.
+ */
+ if(cs->substring && (script = utf8_script(cs->substring)))
+ scripts |= script->script;
+ else{
+ /* add it to list as a specific character set */
+ newsl = new_strlist(cs->substring);
+ if(compare_strlists_for_match(sl, newsl)) /* already in list */
+ free_strlist(&newsl);
+ else{
+ newsl->next = sl;
+ sl = newsl;
+ }
+ }
+ }
+
+ /*
+ * Now scripts has a bit set for each script the user
+ * specified in the charsets pattern. Go through all of
+ * the known charsets and include ones in these scripts.
+ */
+ if(scripts){
+ for(cset = utf8_charset(NIL); cset && cset->name; cset++){
+ if(cset->script & scripts){
+
+ /* filter this out of each script, not very useful */
+ if(!strucmp("ISO-2022-JP-2", cset->name)
+ || !strucmp("UTF-7", cset->name)
+ || !strucmp("UTF-8", cset->name))
+ continue;
+
+ /* add cset->name to the list */
+ newsl = new_strlist(cset->name);
+ if(compare_strlists_for_match(sl, newsl))
+ free_strlist(&newsl);
+ else{
+ newsl->next = sl;
+ sl = newsl;
+ }
+ }
+ }
+ }
+
+ patgrp->charsets_list = sl;
+ }
+
+ /*
+ * This may call build_header_line recursively because we may be in
+ * build_header_line now. So we have to preserve and restore the
+ * sequence bits since we want to use them here.
+ */
+ if(saveseqbits)
+ savebits = (char *) fs_get((stream->nmsgs+1) * sizeof(char));
+
+ for(msgno = 1L; msgno <= stream->nmsgs; msgno++){
+ if((mc = mail_elt(stream, msgno)) != NULL){
+ if(savebits)
+ savebits[msgno] = mc->sequence;
+
+ mc->sequence = 0;
+ }
+ }
+
+
+ /*
+ * Build a searchset so we can look at all the bodies
+ * we need to look at but only those we need to look at.
+ * Everything with the searched bit set is still a
+ * possibility, so restrict to that set.
+ */
+
+ for(s = searchset; s; s = s->next)
+ for(msgno = s->first; msgno <= s->last; msgno++)
+ if(msgno > 0L && msgno <= stream->nmsgs
+ && (mc = mail_elt(stream, msgno)) && mc->searched){
+ mc->sequence = 1;
+ count++;
+ }
+
+ ss = build_searchset(stream);
+
+ if(count){
+ SEARCHSET **sset;
+
+ mail_parameters(NULL, SET_FETCHLOOKAHEADLIMIT, (void *) count);
+
+ /*
+ * This causes the lookahead to fetch precisely
+ * the messages we want (in the searchset) instead
+ * of just fetching the next 20 sequential
+ * messages. If the searching so far has caused
+ * a sparse searchset in a large mailbox, the
+ * difference can be substantial.
+ * This resets automatically after the first fetch.
+ */
+ sset = (SEARCHSET **) mail_parameters(stream,
+ GET_FETCHLOOKAHEAD,
+ (void *) stream);
+ if(sset)
+ *sset = ss;
+ }
+
+ for(s = ss; s; s = s->next){
+ for(msgno = s->first; msgno <= s->last; msgno++){
+
+ if(msgno <= 0L || msgno > stream->nmsgs)
+ continue;
+
+ if(patgrp->charsets_list
+ && charsets_present_in_msg(stream,msgno,patgrp->charsets_list)){
+ if(patgrp->charsets->not){
+ if((mc = mail_elt(stream, msgno)))
+ mc->searched = NIL;
+ }
+ /* else leave it */
+ }
+ else{ /* charset isn't in message */
+ if(!patgrp->charsets->not){
+ if((mc = mail_elt(stream, msgno)))
+ mc->searched = NIL;
+ }
+ /* else leave it */
+ }
+ }
+ }
+
+ if(savebits){
+ for(msgno = 1L; msgno <= stream->nmsgs; msgno++)
+ if((mc = mail_elt(stream, msgno)) != NULL)
+ mc->sequence = savebits[msgno];
+
+ fs_give((void **) &savebits);
+ }
+
+ if(ss)
+ mail_free_searchset(&ss);
+}
+
+
+/*
+ * Look for any of the charsets in this particular message.
+ *
+ * Returns 1 if there is a match, 0 otherwise.
+ */
+int
+charsets_present_in_msg(MAILSTREAM *stream, long unsigned int rawmsgno, STRLIST_S *charsets)
+{
+ BODY *body = NULL;
+ ENVELOPE *env = NULL;
+ STRLIST_S *msg_charsets = NULL;
+ int ret = 0;
+
+ if(charsets && stream && rawmsgno > 0L && rawmsgno <= stream->nmsgs){
+ env = pine_mail_fetchstructure(stream, rawmsgno, &body);
+ collect_charsets_from_subj(env, &msg_charsets);
+ collect_charsets_from_body(body, &msg_charsets);
+ if(msg_charsets){
+ ret = compare_strlists_for_match(msg_charsets, charsets);
+ free_strlist(&msg_charsets);
+ }
+ }
+
+ return(ret);
+}
+
+
+void
+collect_charsets_from_subj(ENVELOPE *env, STRLIST_S **listptr)
+{
+ STRLIST_S *newsl;
+ char *text, *e;
+
+ if(listptr && env && env->subject){
+ /* find encoded word */
+ for(text = env->subject; *text; text++){
+ if((*text == '=') && (text[1] == '?') && isalpha(text[2]) &&
+ (e = strchr(text+2,'?'))){
+ *e = '\0'; /* tie off charset name */
+
+ newsl = new_strlist(text+2);
+ *e = '?';
+
+ if(compare_strlists_for_match(*listptr, newsl))
+ free_strlist(&newsl);
+ else{
+ newsl->next = *listptr;
+ *listptr = newsl;
+ }
+ }
+ }
+ }
+}
+
+
+/*
+ * Check for any of the charsets in any of the charset params in
+ * any of the text parts of the body of a message. Put them in the list
+ * pointed to by listptr.
+ */
+void
+collect_charsets_from_body(struct mail_bodystruct *body, STRLIST_S **listptr)
+{
+ PART *part;
+ char *cset;
+
+ if(listptr && body){
+ switch(body->type){
+ case TYPEMULTIPART:
+ for(part = body->nested.part; part; part = part->next)
+ collect_charsets_from_body(&part->body, listptr);
+
+ break;
+
+ case TYPEMESSAGE:
+ if(!strucmp(body->subtype, "RFC822")){
+ collect_charsets_from_subj(body->nested.msg->env, listptr);
+ collect_charsets_from_body(body->nested.msg->body, listptr);
+ break;
+ }
+ /* else fall through to text case */
+
+ case TYPETEXT:
+ cset = parameter_val(body->parameter, "charset");
+ if(cset){
+ STRLIST_S *newsl;
+
+ newsl = new_strlist(cset);
+
+ if(compare_strlists_for_match(*listptr, newsl))
+ free_strlist(&newsl);
+ else{
+ newsl->next = *listptr;
+ *listptr = newsl;
+ }
+
+ fs_give((void **) &cset);
+ }
+
+ break;
+
+ default: /* non-text terminal mode */
+ break;
+ }
+ }
+}
+
+
+/*
+ * If any of the names in list1 is the same as any of the names in list2
+ * then return 1, else return 0. Comparison is case independent.
+ */
+int
+compare_strlists_for_match(STRLIST_S *list1, STRLIST_S *list2)
+{
+ int ret = 0;
+ STRLIST_S *cs1, *cs2;
+
+ for(cs1 = list1; !ret && cs1; cs1 = cs1->next)
+ for(cs2 = list2; !ret && cs2; cs2 = cs2->next)
+ if(cs1->name && cs2->name && !strucmp(cs1->name, cs2->name))
+ ret = 1;
+
+ return(ret);
+}
+
+
+int
+match_pattern_folder(PATGRP_S *patgrp, MAILSTREAM *stream)
+{
+ int is_news;
+
+ /* change by sderr : we match FLDR_ANY even if stream is NULL */
+ return((patgrp->fldr_type == FLDR_ANY)
+ || (stream
+ && (((is_news = IS_NEWS(stream))
+ && patgrp->fldr_type == FLDR_NEWS)
+ || (!is_news && patgrp->fldr_type == FLDR_EMAIL)
+ || (patgrp->fldr_type == FLDR_SPECIFIC
+ && match_pattern_folder_specific(patgrp->folder,
+ stream, FOR_PATTERN)))));
+}
+
+
+/*
+ * Returns positive if this stream is open on one of the folders in the
+ * folders argument, 0 otherwise.
+ *
+ * If FOR_PATTERN is set, this interprets simple names as nicknames in
+ * the incoming collection, otherwise it treats simple names as being in
+ * the primary collection.
+ * If FOR_FILT is set, the folder names are detokenized before being used.
+ */
+int
+match_pattern_folder_specific(PATTERN_S *folders, MAILSTREAM *stream, int flags)
+{
+ PATTERN_S *p;
+ int match = 0;
+ char *patfolder, *free_this = NULL;
+
+ dprint((8, "match_pattern_folder_specific\n"));
+
+ if(!(stream && stream->mailbox && stream->mailbox[0]))
+ return(0);
+
+ /*
+ * For each of the folders in the pattern, see if we get
+ * a match. We're just looking for any match. If none match,
+ * we return 0, otherwise we fall through and check the rest
+ * of the pattern. The fact that the string is called "substring"
+ * is not meaningful. We're just using the convenient pattern
+ * structure to store a list of folder names. They aren't
+ * substrings of names, they are the whole name.
+ */
+ for(p = folders; !match && p; p = p->next){
+ free_this = NULL;
+ if(flags & FOR_FILTER)
+ patfolder = free_this = detoken_src(p->substring, FOR_FILT, NULL,
+ NULL, NULL, NULL);
+ else
+ patfolder = p->substring;
+
+ if(patfolder
+ && (!strucmp(patfolder, ps_global->inbox_name)
+ || !strcmp(patfolder, ps_global->VAR_INBOX_PATH))){
+ if(sp_flagged(stream, SP_INBOX))
+ match++;
+ }
+ else{
+ char *fname;
+ char *t, *streamfolder;
+ char tmp1[MAILTMPLEN], tmp2[MAX(MAILTMPLEN,NETMAXMBX)];
+ CONTEXT_S *cntxt = NULL;
+
+ if(flags & FOR_PATTERN){
+ /*
+ * See if patfolder is a nickname in the incoming collection.
+ * If so, use its real name instead.
+ */
+ if(patfolder[0] &&
+ (ps_global->context_list->use & CNTXT_INCMNG) &&
+ (fname = (folder_is_nick(patfolder,
+ FOLDERS(ps_global->context_list),
+ 0))))
+ patfolder = fname;
+ }
+ else{
+ char *save_ref = NULL;
+
+ /*
+ * If it's an absolute pathname, we treat is as a local file
+ * instead of interpreting it in the primary context.
+ */
+ if(!is_absolute_path(patfolder)
+ && !(cntxt = default_save_context(ps_global->context_list)))
+ cntxt = ps_global->context_list;
+
+ /*
+ * Because this check is independent of where the user is
+ * in the folder hierarchy and has nothing to do with that,
+ * we want to ignore the reference field built into the
+ * context. Zero it out temporarily here.
+ */
+ if(cntxt && cntxt->dir){
+ save_ref = cntxt->dir->ref;
+ cntxt->dir->ref = NULL;
+ }
+
+ patfolder = context_apply(tmp1, cntxt, patfolder, sizeof(tmp1));
+ if(save_ref)
+ cntxt->dir->ref = save_ref;
+ }
+
+ switch(patfolder[0]){
+ case '{':
+ if(stream->mailbox[0] == '{' &&
+ same_stream(patfolder, stream) &&
+ (streamfolder = strindex(&stream->mailbox[1], '}')) &&
+ (t = strindex(&patfolder[1], '}')) &&
+ (!strcmp(t+1, streamfolder+1) ||
+ (*(t+1) == '\0' && !strcmp("INBOX", streamfolder+1))))
+ match++;
+
+ break;
+
+ case '#':
+ if(!strcmp(patfolder, stream->mailbox))
+ match++;
+
+ break;
+
+ default:
+ t = (strlen(patfolder) < (MAILTMPLEN/2))
+ ? mailboxfile(tmp2, patfolder) : NULL;
+ if(t && *t && !strcmp(t, stream->mailbox))
+ match++;
+
+ break;
+ }
+ }
+
+ if(free_this)
+ fs_give((void **) &free_this);
+ }
+
+ return(match);
+}
+
+
+/*
+ * generate a search program corresponding to the provided patgrp
+ */
+SEARCHPGM *
+match_pattern_srchpgm(PATGRP_S *patgrp, MAILSTREAM *stream, SEARCHSET *searchset)
+{
+ SEARCHPGM *pgm, *tmppgm;
+ SEARCHOR *or;
+ SEARCHSET **sp;
+
+ pgm = mail_newsearchpgm();
+
+ sp = &pgm->msgno;
+ /* copy the searchset */
+ while(searchset){
+ SEARCHSET *s;
+
+ s = mail_newsearchset();
+ s->first = searchset->first;
+ s->last = searchset->last;
+ searchset = searchset->next;
+ *sp = s;
+ sp = &s->next;
+ }
+
+ if(!patgrp)
+ return(pgm);
+
+ if(patgrp->subj){
+ if(patgrp->subj->not)
+ tmppgm = next_not(pgm);
+ else
+ tmppgm = pgm;
+
+ set_up_search_pgm("subject", patgrp->subj, tmppgm);
+ }
+
+ if(patgrp->cc){
+ if(patgrp->cc->not)
+ tmppgm = next_not(pgm);
+ else
+ tmppgm = pgm;
+
+ set_up_search_pgm("cc", patgrp->cc, tmppgm);
+ }
+
+ if(patgrp->from){
+ if(patgrp->from->not)
+ tmppgm = next_not(pgm);
+ else
+ tmppgm = pgm;
+
+ set_up_search_pgm("from", patgrp->from, tmppgm);
+ }
+
+ if(patgrp->to){
+ if(patgrp->to->not)
+ tmppgm = next_not(pgm);
+ else
+ tmppgm = pgm;
+
+ set_up_search_pgm("to", patgrp->to, tmppgm);
+ }
+
+ if(patgrp->sender){
+ if(patgrp->sender->not)
+ tmppgm = next_not(pgm);
+ else
+ tmppgm = pgm;
+
+ set_up_search_pgm("sender", patgrp->sender, tmppgm);
+ }
+
+ if(patgrp->news){
+ if(patgrp->news->not)
+ tmppgm = next_not(pgm);
+ else
+ tmppgm = pgm;
+
+ set_up_search_pgm("newsgroups", patgrp->news, tmppgm);
+ }
+
+ /* To OR Cc */
+ if(patgrp->recip){
+ if(patgrp->recip->not)
+ tmppgm = next_not(pgm);
+ else
+ tmppgm = pgm;
+
+ or = next_or(&tmppgm->or);
+
+ set_up_search_pgm("to", patgrp->recip, or->first);
+ set_up_search_pgm("cc", patgrp->recip, or->second);
+ }
+
+ /* To OR Cc OR From */
+ if(patgrp->partic){
+ if(patgrp->partic->not)
+ tmppgm = next_not(pgm);
+ else
+ tmppgm = pgm;
+
+ or = next_or(&tmppgm->or);
+
+ set_up_search_pgm("to", patgrp->partic, or->first);
+
+ or->second->or = mail_newsearchor();
+ set_up_search_pgm("cc", patgrp->partic, or->second->or->first);
+ set_up_search_pgm("from", patgrp->partic, or->second->or->second);
+ }
+
+ if(patgrp->arbhdr){
+ ARBHDR_S *a;
+
+ for(a = patgrp->arbhdr; a; a = a->next)
+ if(a->field && a->field[0] && a->p){
+ if(a->p->not)
+ tmppgm = next_not(pgm);
+ else
+ tmppgm = pgm;
+
+ set_up_search_pgm(a->field, a->p, tmppgm);
+ }
+ }
+
+ if(patgrp->alltext){
+ if(patgrp->alltext->not)
+ tmppgm = next_not(pgm);
+ else
+ tmppgm = pgm;
+
+ set_up_search_pgm("alltext", patgrp->alltext, tmppgm);
+ }
+
+ if(patgrp->bodytext){
+ if(patgrp->bodytext->not)
+ tmppgm = next_not(pgm);
+ else
+ tmppgm = pgm;
+
+ set_up_search_pgm("bodytext", patgrp->bodytext, tmppgm);
+ }
+
+ if(patgrp->keyword){
+ PATTERN_S *p_old, *p_new, *new_pattern = NULL, **nextp;
+ char *q;
+
+ if(patgrp->keyword->not)
+ tmppgm = next_not(pgm);
+ else
+ tmppgm = pgm;
+
+ /*
+ * The keyword entries may be nicknames instead of the actual
+ * keywords, so those need to be converted to actual keywords.
+ *
+ * If we search for keywords that are not defined for a folder
+ * we may get error messages back that we don't want instead of
+ * just no match. We will build a replacement pattern here which
+ * contains only the defined subset of the keywords.
+ */
+
+ nextp = &new_pattern;
+
+ for(p_old = patgrp->keyword; p_old; p_old = p_old->next){
+ q = nick_to_keyword(p_old->substring);
+ if(user_flag_index(stream, q) >= 0){
+ p_new = (PATTERN_S *) fs_get(sizeof(*p_new));
+ memset(p_new, 0, sizeof(*p_new));
+ p_new->substring = cpystr(q);
+ *nextp = p_new;
+ nextp = &p_new->next;
+ }
+ }
+
+ /*
+ * If there are some matching keywords that are defined in
+ * the folder, then we are ok because we will match only if
+ * we match one of those. However, if the list is empty, then
+ * we can't just leave this part of the search program empty.
+ * That would result in a match instead of not a match.
+ * We can fake our way around the problem with NOT. If the
+ * list is empty we want the opposite, so we insert a NOT in
+ * front of an empty program. We may end up with NOT NOT if
+ * this was already NOT'd, but that's ok, too. Alternatively,
+ * we could undo the first NOT instead.
+ */
+
+ if(new_pattern){
+ set_up_search_pgm("keyword", new_pattern, tmppgm);
+ free_pattern(&new_pattern);
+ }
+ else
+ (void) next_not(tmppgm); /* add NOT of something that matches,
+ so the NOT thing doesn't match */
+ }
+
+ if(patgrp->do_age && patgrp->age){
+ INTVL_S *iv;
+ SEARCHOR *or;
+
+ tmppgm = pgm;
+
+ for(iv = patgrp->age; iv; iv = iv->next){
+ if(iv->next){
+ or = next_or(&tmppgm->or);
+ set_search_by_age(iv, or->first, patgrp->age_uses_sentdate);
+ tmppgm = or->second;
+ }
+ else
+ set_search_by_age(iv, tmppgm, patgrp->age_uses_sentdate);
+ }
+ }
+
+ if(patgrp->do_size && patgrp->size){
+ INTVL_S *iv;
+ SEARCHOR *or;
+
+ tmppgm = pgm;
+
+ for(iv = patgrp->size; iv; iv = iv->next){
+ if(iv->next){
+ or = next_or(&tmppgm->or);
+ set_search_by_size(iv, or->first);
+ tmppgm = or->second;
+ }
+ else
+ set_search_by_size(iv, tmppgm);
+ }
+ }
+
+ SETPGMSTATUS(patgrp->stat_new,pgm->unseen,pgm->seen);
+ SETPGMSTATUS(patgrp->stat_rec,pgm->recent,pgm->old);
+ SETPGMSTATUS(patgrp->stat_del,pgm->deleted,pgm->undeleted);
+ SETPGMSTATUS(patgrp->stat_imp,pgm->flagged,pgm->unflagged);
+ SETPGMSTATUS(patgrp->stat_ans,pgm->answered,pgm->unanswered);
+
+ return(pgm);
+}
+
+
+SEARCHPGM *
+next_not(SEARCHPGM *pgm)
+{
+ SEARCHPGMLIST *not, **not_ptr;
+
+ if(!pgm)
+ return(NULL);
+
+ /* find next unused not slot */
+ for(not = pgm->not; not && not->next; not = not->next)
+ ;
+
+ if(not)
+ not_ptr = &not->next;
+ else
+ not_ptr = &pgm->not;
+
+ /* allocate */
+ *not_ptr = mail_newsearchpgmlist();
+
+ return((*not_ptr)->pgm);
+}
+
+
+SEARCHOR *
+next_or(struct search_or **startingor)
+{
+ SEARCHOR *or, **or_ptr;
+
+ /* find next unused or slot */
+ for(or = (*startingor); or && or->next; or = or->next)
+ ;
+
+ if(or)
+ or_ptr = &or->next;
+ else
+ or_ptr = startingor;
+
+ /* allocate */
+ *or_ptr = mail_newsearchor();
+
+ return(*or_ptr);
+}
+
+
+void
+set_up_search_pgm(char *field, PATTERN_S *pattern, SEARCHPGM *pgm)
+{
+ SEARCHOR *or;
+
+ if(field && pattern && pgm){
+
+ /*
+ * To is special because we want to use the ReSent-To header instead
+ * of the To header if it exists. We set up something like:
+ *
+ * if((resent-to matches pat1 or pat2...)
+ * OR
+ * (<resent-to doesn't exist> AND (to matches pat1 or pat2...)))
+ *
+ * Some servers (Exchange, apparently) seem to have trouble with
+ * the search for the empty string to decide if the header exists
+ * or not. So, we will search for either the empty string OR the
+ * header with a SPACE in it. Some still have trouble with this
+ * so we are changing it to be off by default.
+ */
+ if(!strucmp(field, "to") && F_ON(F_USE_RESENTTO, ps_global)){
+ or = next_or(&pgm->or);
+
+ add_type_to_pgm("resent-to", pattern, or->first);
+
+ /* check for resent-to doesn't exist */
+ or->second->not = mail_newsearchpgmlist();
+
+ or->second->not->pgm->or = mail_newsearchor();
+ set_srch("resent-to", " ", or->second->not->pgm->or->first);
+ set_srch("resent-to", "", or->second->not->pgm->or->second);
+
+ /* now add the real To search to second */
+ add_type_to_pgm(field, pattern, or->second);
+ }
+ else
+ add_type_to_pgm(field, pattern, pgm);
+ }
+}
+
+
+void
+add_type_to_pgm(char *field, PATTERN_S *pattern, SEARCHPGM *pgm)
+{
+ PATTERN_S *p;
+ SEARCHOR *or;
+ SEARCHPGM *notpgm, *tpgm;
+ int cnt = 0;
+
+ if(field && pattern && pgm){
+ /*
+ * Here is a weird bit of logic. What we want here is simply
+ * A or B or C or D
+ * for all of the elements of pattern. Ors are a bit complicated.
+ * The list of ORs in the SEARCHPGM structure are ANDed together,
+ * not ORd together. It's for things like
+ * Subject A or B AND From C or D
+ * The Subject part would be one member of the OR list and the From
+ * part would be another member of the OR list. Instead we want
+ * a big OR which may have more than two members (first and second)
+ * but the structure just has two members. So we have to build an
+ * OR tree and we build it by going down one branch of the tree
+ * instead of by balancing the branches.
+ *
+ * or
+ * / \
+ * first==A second
+ * / \
+ * first==B second
+ * / \
+ * first==C second==D
+ *
+ * There is an additional problem. Some servers don't like deeply
+ * nested logic in the SEARCH command. The tree above produces a
+ * fairly deeply nested command if the user wants to match on
+ * several different From addresses or Subjects...
+ * We use the tried and true equation
+ *
+ * (A or B) == !(!A and !B)
+ *
+ * to change the deeply nested OR tree into ANDs which aren't nested.
+ * Right now we're only doing that if the nesting is fairly deep.
+ * We can think of some reasons to do that. First, we know that the
+ * OR thing works, that's what we've been using for a while and the
+ * only problem is the deep nesting. 2nd, it is easier to understand.
+ * 3rd, it looks dumb to use NOT NOT A instead of A.
+ * It is probably dumb to mix the two, but what the heck.
+ * Hubert 2003-04-02
+ */
+ for(p = pattern; p; p = p->next)
+ cnt++;
+
+ if(cnt < 10){ /* use ORs if count is low */
+ for(p = pattern; p; p = p->next){
+ if(p->next){
+ or = next_or(&pgm->or);
+
+ set_srch(field, p->substring ? p->substring : "", or->first);
+ pgm = or->second;
+ }
+ else
+ set_srch(field, p->substring ? p->substring : "", pgm);
+ }
+ }
+ else{ /* else use ANDs */
+ /* ( A or B or C ) <=> ! ( !A and !B and !C ) */
+
+ /* first, NOT of the whole thing */
+ notpgm = next_not(pgm);
+
+ /* then the not list is ANDed together */
+ for(p = pattern; p; p = p->next){
+ tpgm = next_not(notpgm);
+ set_srch(field, p->substring ? p->substring : "", tpgm);
+ }
+ }
+ }
+}
+
+
+void
+set_srch(char *field, char *value, SEARCHPGM *pgm)
+{
+ char *decoded;
+ STRINGLIST **list;
+
+ if(!(field && value && pgm))
+ return;
+
+ if(!strucmp(field, "subject"))
+ list = &pgm->subject;
+ else if(!strucmp(field, "from"))
+ list = &pgm->from;
+ else if(!strucmp(field, "to"))
+ list = &pgm->to;
+ else if(!strucmp(field, "cc"))
+ list = &pgm->cc;
+ else if(!strucmp(field, "sender"))
+ list = &pgm->sender;
+ else if(!strucmp(field, "reply-to"))
+ list = &pgm->reply_to;
+ else if(!strucmp(field, "in-reply-to"))
+ list = &pgm->in_reply_to;
+ else if(!strucmp(field, "message-id"))
+ list = &pgm->message_id;
+ else if(!strucmp(field, "newsgroups"))
+ list = &pgm->newsgroups;
+ else if(!strucmp(field, "followup-to"))
+ list = &pgm->followup_to;
+ else if(!strucmp(field, "alltext"))
+ list = &pgm->text;
+ else if(!strucmp(field, "bodytext"))
+ list = &pgm->body;
+ else if(!strucmp(field, "keyword"))
+ list = &pgm->keyword;
+ else{
+ set_srch_hdr(field, value, pgm);
+ return;
+ }
+
+ if(!list)
+ return;
+
+ *list = mail_newstringlist();
+ decoded = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, SIZEOF_20KBUF, value);
+
+ (*list)->text.data = (unsigned char *)cpystr(decoded);
+ (*list)->text.size = strlen(decoded);
+}
+
+
+void
+set_srch_hdr(char *field, char *value, SEARCHPGM *pgm)
+{
+ char *decoded;
+ SEARCHHEADER **hdr;
+
+ if(!(field && value && pgm))
+ return;
+
+ hdr = &pgm->header;
+ if(!hdr)
+ return;
+
+ decoded = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, value);
+ while(*hdr && (*hdr)->next)
+ *hdr = (*hdr)->next;
+
+ if(*hdr)
+ (*hdr)->next = mail_newsearchheader(field, decoded);
+ else
+ *hdr = mail_newsearchheader(field, decoded);
+}
+
+
+void
+set_search_by_age(INTVL_S *age, SEARCHPGM *pgm, int age_uses_sentdate)
+{
+ time_t now, comparetime;
+ struct tm *tm;
+ unsigned short i;
+
+ if(!(age && pgm))
+ return;
+
+ now = time(0);
+
+ if(age->imin >= 0L && age->imin == age->imax){
+ comparetime = now;
+ comparetime -= (age->imin * 86400L);
+ tm = localtime(&comparetime);
+ if(tm && tm->tm_year >= 70){
+ i = mail_shortdate(tm->tm_year - 70, tm->tm_mon + 1,
+ tm->tm_mday);
+ if(age_uses_sentdate)
+ pgm->senton = i;
+ else
+ pgm->on = i;
+ }
+ }
+ else{
+ /*
+ * The 20000's are just protecting against overflows.
+ * That's back past the start of email time, anyway.
+ */
+ if(age->imin > 0L && age->imin < 20000L){
+ comparetime = now;
+ comparetime -= ((age->imin - 1L) * 86400L);
+ tm = localtime(&comparetime);
+ if(tm && tm->tm_year >= 70){
+ i = mail_shortdate(tm->tm_year - 70, tm->tm_mon + 1,
+ tm->tm_mday);
+ if(age_uses_sentdate)
+ pgm->sentbefore = i;
+ else
+ pgm->before = i;
+ }
+ }
+
+ if(age->imax >= 0L && age->imax < 20000L){
+ comparetime = now;
+ comparetime -= (age->imax * 86400L);
+ tm = localtime(&comparetime);
+ if(tm && tm->tm_year >= 70){
+ i = mail_shortdate(tm->tm_year - 70, tm->tm_mon + 1,
+ tm->tm_mday);
+ if(age_uses_sentdate)
+ pgm->sentsince = i;
+ else
+ pgm->since = i;
+ }
+ }
+ }
+}
+
+
+void
+set_search_by_size(INTVL_S *size, SEARCHPGM *pgm)
+{
+ if(!(size && pgm))
+ return;
+
+ /*
+ * INTVL_S intervals include the endpoints, pgm larger and smaller
+ * do not include the endpoints.
+ */
+ if(size->imin != INTVL_UNDEF && size->imin > 0L)
+ pgm->larger = size->imin - 1L;
+
+ if(size->imax != INTVL_UNDEF && size->imax >= 0L && size->imax != INTVL_INF)
+ pgm->smaller = size->imax + 1L;
+}
+
+
+static char *extra_hdrs;
+
+/*
+ * Run through the patterns and note which headers we'll need to ask for
+ * which aren't normally asked for and so won't be cached.
+ */
+void
+calc_extra_hdrs(void)
+{
+ PAT_S *pat = NULL;
+ int alloced_size;
+ long type = (ROLE_INCOL | ROLE_SCORE);
+ ARBHDR_S *a;
+ PAT_STATE pstate;
+ char *q, *p = NULL, *hdrs[MLCMD_COUNT + 1], **pp;
+ INDEX_COL_S *cdesc;
+#define INITIALSIZE 1000
+
+ q = (char *)fs_get((INITIALSIZE+1) * sizeof(char));
+ q[0] = '\0';
+ alloced_size = INITIALSIZE;
+ p = q;
+
+ /*
+ * *ALWAYS* make sure Resent-To is in the set of
+ * extra headers getting fetched.
+ *
+ * This is because we *will* reference it when we're
+ * building header lines and thus want it fetched with
+ * the standard envelope data. Worse, in the IMAP case
+ * we're called back from c-client with the envelope data
+ * so we can format and display the index lines as they
+ * arrive, so we have to ensure the resent-to field
+ * is in the cache so we don't reenter c-client
+ * to look for it from the callback. Yeouch.
+ */
+ add_eh(&q, &p, "resent-to", &alloced_size);
+ add_eh(&q, &p, "resent-date", &alloced_size);
+ add_eh(&q, &p, "resent-from", &alloced_size);
+ add_eh(&q, &p, "resent-cc", &alloced_size);
+ add_eh(&q, &p, "resent-subject", &alloced_size);
+
+ /*
+ * Sniff at viewer-hdrs too so we can include them
+ * if there are any...
+ */
+ for(pp = ps_global->VAR_VIEW_HEADERS; pp && *pp; pp++)
+ if(non_eh(*pp))
+ add_eh(&q, &p, *pp, &alloced_size);
+
+ /*
+ * Be sure to ask for List management headers too
+ * since we'll offer their use in the message view
+ */
+ for(pp = rfc2369_hdrs(hdrs); *pp; pp++)
+ add_eh(&q, &p, *pp, &alloced_size);
+
+ if(nonempty_patterns(type, &pstate))
+ for(pat = first_pattern(&pstate);
+ pat;
+ pat = next_pattern(&pstate)){
+ /*
+ * This section wouldn't be necessary if sender was retreived
+ * from the envelope. But if not, we do need to add it.
+ */
+ if(pat->patgrp && pat->patgrp->sender)
+ add_eh(&q, &p, "sender", &alloced_size);
+
+ if(pat->patgrp && pat->patgrp->arbhdr)
+ for(a = pat->patgrp->arbhdr; a; a = a->next)
+ if(a->field && a->field[0] && a->p && non_eh(a->field))
+ add_eh(&q, &p, a->field, &alloced_size);
+ }
+
+ /*
+ * Check for use of HEADER or X-Priority in index-format.
+ */
+ for(cdesc = ps_global->index_disp_format; cdesc->ctype != iNothing; cdesc++){
+ if(cdesc->ctype == iHeader && cdesc->hdrtok && cdesc->hdrtok->hdrname
+ && cdesc->hdrtok->hdrname[0] && non_eh(cdesc->hdrtok->hdrname))
+ add_eh(&q, &p, cdesc->hdrtok->hdrname, &alloced_size);
+ else if(cdesc->ctype == iPrio
+ || cdesc->ctype == iPrioAlpha
+ || cdesc->ctype == iPrioBang)
+ add_eh(&q, &p, PRIORITYNAME, &alloced_size);
+ }
+
+ /*
+ * Check for use of scorevalhdrtok in scoring patterns.
+ */
+ type = ROLE_SCORE;
+ if(nonempty_patterns(type, &pstate))
+ for(pat = first_pattern(&pstate);
+ pat;
+ pat = next_pattern(&pstate)){
+ /*
+ * This section wouldn't be necessary if sender was retreived
+ * from the envelope. But if not, we do need to add it.
+ */
+ if(pat->action && pat->action->scorevalhdrtok
+ && pat->action->scorevalhdrtok->hdrname
+ && pat->action->scorevalhdrtok->hdrname[0]
+ && non_eh(pat->action->scorevalhdrtok->hdrname))
+ add_eh(&q, &p, pat->action->scorevalhdrtok->hdrname, &alloced_size);
+ }
+
+ set_extra_hdrs(q);
+ if(q)
+ fs_give((void **)&q);
+}
+
+
+int
+non_eh(char *field)
+{
+ char **t;
+ static char *existing[] = {"subject", "from", "to", "cc", "sender",
+ "reply-to", "in-reply-to", "message-id",
+ "path", "newsgroups", "followup-to",
+ "references", NULL};
+
+ /*
+ * If it is one of these, we should already have it
+ * from the envelope or from the extra headers c-client
+ * already adds to the list (hdrheader and hdrtrailer
+ * in imap4r1.c, Aug 99, slh).
+ */
+ for(t = existing; *t; t++)
+ if(!strucmp(field, *t))
+ return(FALSE);
+
+ return(TRUE);
+}
+
+
+/*
+ * Add field to extra headers string if not already there.
+ */
+void
+add_eh(char **start, char **ptr, char *field, int *asize)
+{
+ char *s;
+
+ /* already there? */
+ for(s = *start; (s = srchstr(s, field)) != NULL; s++)
+ if(s[strlen(field)] == SPACE || s[strlen(field)] == '\0')
+ return;
+
+ /* enough space for it? */
+ while(strlen(field) + (*ptr - *start) + 1 > *asize){
+ (*asize) *= 2;
+ fs_resize((void **)start, (*asize)+1);
+ *ptr = *start + strlen(*start);
+ }
+
+ if(*ptr > *start)
+ sstrncpy(ptr, " ", *asize-(*ptr - *start));
+
+ sstrncpy(ptr, field, *asize-(*ptr - *start));
+
+ (*start)[*asize] = '\0';
+}
+
+
+void
+set_extra_hdrs(char *hdrs)
+{
+ free_extra_hdrs();
+ if(hdrs && *hdrs)
+ extra_hdrs = cpystr(hdrs);
+}
+
+
+char *
+get_extra_hdrs(void)
+{
+ return(extra_hdrs);
+}
+
+
+void
+free_extra_hdrs(void)
+{
+ if(extra_hdrs)
+ fs_give((void **)&extra_hdrs);
+}
+
+
+int
+is_ascii_string(char *str)
+{
+ if(!str)
+ return(0);
+
+ while(*str && isascii(*str))
+ str++;
+
+ return(*str == '\0');
+}
+
+
+void
+free_patline(PAT_LINE_S **patline)
+{
+ if(patline && *patline){
+ free_patline(&(*patline)->next);
+ if((*patline)->filename)
+ fs_give((void **)&(*patline)->filename);
+ if((*patline)->filepath)
+ fs_give((void **)&(*patline)->filepath);
+ free_pat(&(*patline)->first);
+ fs_give((void **)patline);
+ }
+}
+
+
+void
+free_pat(PAT_S **pat)
+{
+ if(pat && *pat){
+ free_pat(&(*pat)->next);
+ free_patgrp(&(*pat)->patgrp);
+ free_action(&(*pat)->action);
+ if((*pat)->raw)
+ fs_give((void **)&(*pat)->raw);
+
+ fs_give((void **)pat);
+ }
+}
+
+
+void
+free_patgrp(PATGRP_S **patgrp)
+{
+ if(patgrp && *patgrp){
+ if((*patgrp)->nick)
+ fs_give((void **) &(*patgrp)->nick);
+
+ if((*patgrp)->comment)
+ fs_give((void **) &(*patgrp)->comment);
+
+ if((*patgrp)->category_cmd)
+ free_list_array(&(*patgrp)->category_cmd);
+
+ if((*patgrp)->charsets_list)
+ free_strlist(&(*patgrp)->charsets_list);
+
+ free_pattern(&(*patgrp)->to);
+ free_pattern(&(*patgrp)->cc);
+ free_pattern(&(*patgrp)->recip);
+ free_pattern(&(*patgrp)->partic);
+ free_pattern(&(*patgrp)->from);
+ free_pattern(&(*patgrp)->sender);
+ free_pattern(&(*patgrp)->news);
+ free_pattern(&(*patgrp)->subj);
+ free_pattern(&(*patgrp)->alltext);
+ free_pattern(&(*patgrp)->bodytext);
+ free_pattern(&(*patgrp)->keyword);
+ free_pattern(&(*patgrp)->charsets);
+ free_pattern(&(*patgrp)->folder);
+ free_arbhdr(&(*patgrp)->arbhdr);
+ free_intvl(&(*patgrp)->score);
+ free_intvl(&(*patgrp)->age);
+ fs_give((void **) patgrp);
+ }
+}
+
+
+void
+free_pattern(PATTERN_S **pattern)
+{
+ if(pattern && *pattern){
+ free_pattern(&(*pattern)->next);
+ if((*pattern)->substring)
+ fs_give((void **)&(*pattern)->substring);
+ fs_give((void **)pattern);
+ }
+}
+
+
+void
+free_arbhdr(ARBHDR_S **arbhdr)
+{
+ if(arbhdr && *arbhdr){
+ free_arbhdr(&(*arbhdr)->next);
+ if((*arbhdr)->field)
+ fs_give((void **)&(*arbhdr)->field);
+ free_pattern(&(*arbhdr)->p);
+ fs_give((void **)arbhdr);
+ }
+}
+
+
+void
+free_intvl(INTVL_S **intvl)
+{
+ if(intvl && *intvl){
+ free_intvl(&(*intvl)->next);
+ fs_give((void **) intvl);
+ }
+}
+
+
+void
+free_action(ACTION_S **action)
+{
+ if(action && *action){
+ if((*action)->from)
+ mail_free_address(&(*action)->from);
+ if((*action)->replyto)
+ mail_free_address(&(*action)->replyto);
+ if((*action)->fcc)
+ fs_give((void **)&(*action)->fcc);
+ if((*action)->litsig)
+ fs_give((void **)&(*action)->litsig);
+ if((*action)->sig)
+ fs_give((void **)&(*action)->sig);
+ if((*action)->template)
+ fs_give((void **)&(*action)->template);
+ if((*action)->scorevalhdrtok)
+ free_hdrtok(&(*action)->scorevalhdrtok);
+ if((*action)->cstm)
+ free_list_array(&(*action)->cstm);
+ if((*action)->smtp)
+ free_list_array(&(*action)->smtp);
+ if((*action)->nntp)
+ free_list_array(&(*action)->nntp);
+ if((*action)->nick)
+ fs_give((void **)&(*action)->nick);
+ if((*action)->inherit_nick)
+ fs_give((void **)&(*action)->inherit_nick);
+ if((*action)->incol)
+ free_color_pair(&(*action)->incol);
+ if((*action)->folder)
+ free_pattern(&(*action)->folder);
+ if((*action)->index_format)
+ fs_give((void **)&(*action)->index_format);
+ if((*action)->keyword_set)
+ free_pattern(&(*action)->keyword_set);
+ if((*action)->keyword_clr)
+ free_pattern(&(*action)->keyword_clr);
+
+ fs_give((void **)action);
+ }
+}
+
+
+/*
+ * Returns an allocated copy of the pat.
+ *
+ * Args pat -- the source pat
+ *
+ * Returns a copy of pat.
+ */
+PAT_S *
+copy_pat(PAT_S *pat)
+{
+ PAT_S *new_pat = NULL;
+
+ if(pat){
+ new_pat = (PAT_S *)fs_get(sizeof(*new_pat));
+ memset((void *)new_pat, 0, sizeof(*new_pat));
+
+ new_pat->patgrp = copy_patgrp(pat->patgrp);
+ new_pat->action = copy_action(pat->action);
+ }
+
+ return(new_pat);
+}
+
+
+/*
+ * Returns an allocated copy of the patgrp.
+ *
+ * Args patgrp -- the source patgrp
+ *
+ * Returns a copy of patgrp.
+ */
+PATGRP_S *
+copy_patgrp(PATGRP_S *patgrp)
+{
+ char *p;
+ PATGRP_S *new_patgrp = NULL;
+
+ if(patgrp){
+ new_patgrp = (PATGRP_S *)fs_get(sizeof(*new_patgrp));
+ memset((void *)new_patgrp, 0, sizeof(*new_patgrp));
+
+ if(patgrp->nick)
+ new_patgrp->nick = cpystr(patgrp->nick);
+
+ if(patgrp->comment)
+ new_patgrp->comment = cpystr(patgrp->comment);
+
+ if(patgrp->to){
+ p = pattern_to_string(patgrp->to);
+ new_patgrp->to = string_to_pattern(p);
+ fs_give((void **)&p);
+ new_patgrp->to->not = patgrp->to->not;
+ }
+
+ if(patgrp->from){
+ p = pattern_to_string(patgrp->from);
+ new_patgrp->from = string_to_pattern(p);
+ fs_give((void **)&p);
+ new_patgrp->from->not = patgrp->from->not;
+ }
+
+ if(patgrp->sender){
+ p = pattern_to_string(patgrp->sender);
+ new_patgrp->sender = string_to_pattern(p);
+ fs_give((void **)&p);
+ new_patgrp->sender->not = patgrp->sender->not;
+ }
+
+ if(patgrp->cc){
+ p = pattern_to_string(patgrp->cc);
+ new_patgrp->cc = string_to_pattern(p);
+ fs_give((void **)&p);
+ new_patgrp->cc->not = patgrp->cc->not;
+ }
+
+ if(patgrp->recip){
+ p = pattern_to_string(patgrp->recip);
+ new_patgrp->recip = string_to_pattern(p);
+ fs_give((void **)&p);
+ new_patgrp->recip->not = patgrp->recip->not;
+ }
+
+ if(patgrp->partic){
+ p = pattern_to_string(patgrp->partic);
+ new_patgrp->partic = string_to_pattern(p);
+ fs_give((void **)&p);
+ new_patgrp->partic->not = patgrp->partic->not;
+ }
+
+ if(patgrp->news){
+ p = pattern_to_string(patgrp->news);
+ new_patgrp->news = string_to_pattern(p);
+ fs_give((void **)&p);
+ new_patgrp->news->not = patgrp->news->not;
+ }
+
+ if(patgrp->subj){
+ p = pattern_to_string(patgrp->subj);
+ new_patgrp->subj = string_to_pattern(p);
+ fs_give((void **)&p);
+ new_patgrp->subj->not = patgrp->subj->not;
+ }
+
+ if(patgrp->alltext){
+ p = pattern_to_string(patgrp->alltext);
+ new_patgrp->alltext = string_to_pattern(p);
+ fs_give((void **)&p);
+ new_patgrp->alltext->not = patgrp->alltext->not;
+ }
+
+ if(patgrp->bodytext){
+ p = pattern_to_string(patgrp->bodytext);
+ new_patgrp->bodytext = string_to_pattern(p);
+ fs_give((void **)&p);
+ new_patgrp->bodytext->not = patgrp->bodytext->not;
+ }
+
+ if(patgrp->keyword){
+ p = pattern_to_string(patgrp->keyword);
+ new_patgrp->keyword = string_to_pattern(p);
+ fs_give((void **)&p);
+ new_patgrp->keyword->not = patgrp->keyword->not;
+ }
+
+ if(patgrp->charsets){
+ p = pattern_to_string(patgrp->charsets);
+ new_patgrp->charsets = string_to_pattern(p);
+ fs_give((void **)&p);
+ new_patgrp->charsets->not = patgrp->charsets->not;
+ }
+
+ if(patgrp->charsets_list)
+ new_patgrp->charsets_list = copy_strlist(patgrp->charsets_list);
+
+ if(patgrp->arbhdr){
+ ARBHDR_S *aa, *a, *new_a;
+
+ aa = NULL;
+ for(a = patgrp->arbhdr; a; a = a->next){
+ new_a = (ARBHDR_S *)fs_get(sizeof(*new_a));
+ memset((void *)new_a, 0, sizeof(*new_a));
+
+ if(a->field)
+ new_a->field = cpystr(a->field);
+
+ if(a->p){
+ p = pattern_to_string(a->p);
+ new_a->p = string_to_pattern(p);
+ fs_give((void **)&p);
+ new_a->p->not = a->p->not;
+ }
+
+ new_a->isemptyval = a->isemptyval;
+
+ if(aa){
+ aa->next = new_a;
+ aa = aa->next;
+ }
+ else{
+ new_patgrp->arbhdr = new_a;
+ aa = new_patgrp->arbhdr;
+ }
+ }
+ }
+
+ new_patgrp->fldr_type = patgrp->fldr_type;
+
+ if(patgrp->folder){
+ p = pattern_to_string(patgrp->folder);
+ new_patgrp->folder = string_to_pattern(p);
+ fs_give((void **)&p);
+ }
+
+ new_patgrp->inabook = patgrp->inabook;
+
+ if(patgrp->abooks){
+ p = pattern_to_string(patgrp->abooks);
+ new_patgrp->abooks = string_to_pattern(p);
+ fs_give((void **)&p);
+ }
+
+ new_patgrp->do_score = patgrp->do_score;
+ if(patgrp->score){
+ INTVL_S *intvl, *iv, *new_iv;
+
+ intvl = NULL;
+ for(iv = patgrp->score; iv; iv = iv->next){
+ new_iv = (INTVL_S *) fs_get(sizeof(*new_iv));
+ memset((void *) new_iv, 0, sizeof(*new_iv));
+
+ new_iv->imin = iv->imin;
+ new_iv->imax = iv->imax;
+
+ if(intvl){
+ intvl->next = new_iv;
+ intvl = intvl->next;
+ }
+ else{
+ new_patgrp->score = new_iv;
+ intvl = new_patgrp->score;
+ }
+ }
+ }
+
+ new_patgrp->do_age = patgrp->do_age;
+ if(patgrp->age){
+ INTVL_S *intvl, *iv, *new_iv;
+
+ intvl = NULL;
+ for(iv = patgrp->age; iv; iv = iv->next){
+ new_iv = (INTVL_S *) fs_get(sizeof(*new_iv));
+ memset((void *) new_iv, 0, sizeof(*new_iv));
+
+ new_iv->imin = iv->imin;
+ new_iv->imax = iv->imax;
+
+ if(intvl){
+ intvl->next = new_iv;
+ intvl = intvl->next;
+ }
+ else{
+ new_patgrp->age = new_iv;
+ intvl = new_patgrp->age;
+ }
+ }
+ }
+
+ new_patgrp->age_uses_sentdate = patgrp->age_uses_sentdate;
+
+ new_patgrp->do_size = patgrp->do_size;
+ if(patgrp->size){
+ INTVL_S *intvl, *iv, *new_iv;
+
+ intvl = NULL;
+ for(iv = patgrp->size; iv; iv = iv->next){
+ new_iv = (INTVL_S *) fs_get(sizeof(*new_iv));
+ memset((void *) new_iv, 0, sizeof(*new_iv));
+
+ new_iv->imin = iv->imin;
+ new_iv->imax = iv->imax;
+
+ if(intvl){
+ intvl->next = new_iv;
+ intvl = intvl->next;
+ }
+ else{
+ new_patgrp->size = new_iv;
+ intvl = new_patgrp->size;
+ }
+ }
+ }
+
+ new_patgrp->stat_new = patgrp->stat_new;
+ new_patgrp->stat_rec = patgrp->stat_rec;
+ new_patgrp->stat_del = patgrp->stat_del;
+ new_patgrp->stat_imp = patgrp->stat_imp;
+ new_patgrp->stat_ans = patgrp->stat_ans;
+
+ new_patgrp->stat_8bitsubj = patgrp->stat_8bitsubj;
+ new_patgrp->stat_bom = patgrp->stat_bom;
+ new_patgrp->stat_boy = patgrp->stat_boy;
+
+ new_patgrp->do_cat = patgrp->do_cat;
+ if(patgrp->cat){
+ INTVL_S *intvl, *iv, *new_iv;
+
+ intvl = NULL;
+ for(iv = patgrp->cat; iv; iv = iv->next){
+ new_iv = (INTVL_S *) fs_get(sizeof(*new_iv));
+ memset((void *) new_iv, 0, sizeof(*new_iv));
+
+ new_iv->imin = iv->imin;
+ new_iv->imax = iv->imax;
+
+ if(intvl){
+ intvl->next = new_iv;
+ intvl = intvl->next;
+ }
+ else{
+ new_patgrp->cat = new_iv;
+ intvl = new_patgrp->cat;
+ }
+ }
+ }
+
+ if(patgrp->category_cmd)
+ new_patgrp->category_cmd = copy_list_array(patgrp->category_cmd);
+ }
+
+ return(new_patgrp);
+}
+
+
+/*
+ * Returns an allocated copy of the action.
+ *
+ * Args action -- the source action
+ *
+ * Returns a copy of action.
+ */
+ACTION_S *
+copy_action(ACTION_S *action)
+{
+ ACTION_S *newaction = NULL;
+ char *p;
+
+ if(action){
+ newaction = (ACTION_S *)fs_get(sizeof(*newaction));
+ memset((void *)newaction, 0, sizeof(*newaction));
+
+ newaction->is_a_role = action->is_a_role;
+ newaction->is_a_incol = action->is_a_incol;
+ newaction->is_a_score = action->is_a_score;
+ newaction->is_a_filter = action->is_a_filter;
+ newaction->is_a_other = action->is_a_other;
+ newaction->is_a_srch = action->is_a_srch;
+ newaction->repl_type = action->repl_type;
+ newaction->forw_type = action->forw_type;
+ newaction->comp_type = action->comp_type;
+ newaction->scoreval = action->scoreval;
+ newaction->kill = action->kill;
+ newaction->state_setting_bits = action->state_setting_bits;
+ newaction->move_only_if_not_deleted = action->move_only_if_not_deleted;
+ newaction->non_terminating = action->non_terminating;
+ newaction->sort_is_set = action->sort_is_set;
+ newaction->sortorder = action->sortorder;
+ newaction->revsort = action->revsort;
+ newaction->startup_rule = action->startup_rule;
+
+ if(action->from)
+ newaction->from = copyaddrlist(action->from);
+ if(action->replyto)
+ newaction->replyto = copyaddrlist(action->replyto);
+ if(action->cstm)
+ newaction->cstm = copy_list_array(action->cstm);
+ if(action->smtp)
+ newaction->smtp = copy_list_array(action->smtp);
+ if(action->nntp)
+ newaction->nntp = copy_list_array(action->nntp);
+ if(action->fcc)
+ newaction->fcc = cpystr(action->fcc);
+ if(action->litsig)
+ newaction->litsig = cpystr(action->litsig);
+ if(action->sig)
+ newaction->sig = cpystr(action->sig);
+ if(action->template)
+ newaction->template = cpystr(action->template);
+ if(action->nick)
+ newaction->nick = cpystr(action->nick);
+ if(action->inherit_nick)
+ newaction->inherit_nick = cpystr(action->inherit_nick);
+ if(action->incol)
+ newaction->incol = new_color_pair(action->incol->fg,
+ action->incol->bg);
+ if(action->scorevalhdrtok){
+ newaction->scorevalhdrtok = new_hdrtok(action->scorevalhdrtok->hdrname);
+ if(action->scorevalhdrtok && action->scorevalhdrtok->fieldseps){
+ if(newaction->scorevalhdrtok->fieldseps)
+ fs_give((void **) &newaction->scorevalhdrtok->fieldseps);
+
+ newaction->scorevalhdrtok->fieldseps = cpystr(action->scorevalhdrtok->fieldseps);
+ }
+ }
+
+ if(action->folder){
+ p = pattern_to_string(action->folder);
+ newaction->folder = string_to_pattern(p);
+ fs_give((void **) &p);
+ }
+
+ if(action->keyword_set){
+ p = pattern_to_string(action->keyword_set);
+ newaction->keyword_set = string_to_pattern(p);
+ fs_give((void **) &p);
+ }
+
+ if(action->keyword_clr){
+ p = pattern_to_string(action->keyword_clr);
+ newaction->keyword_clr = string_to_pattern(p);
+ fs_give((void **) &p);
+ }
+
+ if(action->index_format)
+ newaction->index_format = cpystr(action->index_format);
+ }
+
+ return(newaction);
+}
+
+
+/*
+ * Given a role, return an allocated role. If this role inherits from
+ * another role, then do the correct inheriting so that the result is
+ * the role we want to use. The inheriting that is done is just the set
+ * of set- actions. This is for role stuff, no inheriting happens for scores
+ * or for colors.
+ *
+ * Args role -- The source role
+ *
+ * Returns a role.
+ */
+ACTION_S *
+combine_inherited_role(ACTION_S *role)
+{
+ PAT_STATE pstate;
+ PAT_S *pat;
+
+ /*
+ * Protect against loops in the role inheritance.
+ */
+ if(role && role->is_a_role && nonempty_patterns(ROLE_DO_ROLES, &pstate))
+ for(pat = first_pattern(&pstate); pat; pat = next_pattern(&pstate))
+ if(pat->action){
+ if(pat->action == role)
+ pat->action->been_here_before = 1;
+ else
+ pat->action->been_here_before = 0;
+ }
+
+ return(combine_inherited_role_guts(role));
+}
+
+
+ACTION_S *
+combine_inherited_role_guts(ACTION_S *role)
+{
+ ACTION_S *newrole = NULL, *inherit_role = NULL;
+ PAT_STATE pstate;
+
+ if(role && role->is_a_role){
+ newrole = (ACTION_S *)fs_get(sizeof(*newrole));
+ memset((void *)newrole, 0, sizeof(*newrole));
+
+ newrole->repl_type = role->repl_type;
+ newrole->forw_type = role->forw_type;
+ newrole->comp_type = role->comp_type;
+ newrole->is_a_role = role->is_a_role;
+
+ if(role->inherit_nick && role->inherit_nick[0] &&
+ nonempty_patterns(ROLE_DO_ROLES, &pstate)){
+ PAT_S *pat;
+
+ /* find the inherit_nick pattern */
+ for(pat = first_pattern(&pstate);
+ pat;
+ pat = next_pattern(&pstate)){
+ if(pat->patgrp &&
+ pat->patgrp->nick &&
+ !strucmp(role->inherit_nick, pat->patgrp->nick)){
+ /* found it, if it has a role, use it */
+ if(!pat->action->been_here_before){
+ pat->action->been_here_before = 1;
+ inherit_role = pat->action;
+ }
+
+ break;
+ }
+ }
+
+ /*
+ * inherit_role might inherit further from other roles.
+ * In any case, we copy it so that we'll consistently have
+ * an allocated copy.
+ */
+ if(inherit_role){
+ if(inherit_role->inherit_nick && inherit_role->inherit_nick[0])
+ inherit_role = combine_inherited_role_guts(inherit_role);
+ else
+ inherit_role = copy_action(inherit_role);
+ }
+ }
+
+ if(role->from)
+ newrole->from = copyaddrlist(role->from);
+ else if(inherit_role && inherit_role->from)
+ newrole->from = copyaddrlist(inherit_role->from);
+
+ if(role->replyto)
+ newrole->replyto = copyaddrlist(role->replyto);
+ else if(inherit_role && inherit_role->replyto)
+ newrole->replyto = copyaddrlist(inherit_role->replyto);
+
+ if(role->fcc)
+ newrole->fcc = cpystr(role->fcc);
+ else if(inherit_role && inherit_role->fcc)
+ newrole->fcc = cpystr(inherit_role->fcc);
+
+ if(role->litsig)
+ newrole->litsig = cpystr(role->litsig);
+ else if(inherit_role && inherit_role->litsig)
+ newrole->litsig = cpystr(inherit_role->litsig);
+
+ if(role->sig)
+ newrole->sig = cpystr(role->sig);
+ else if(inherit_role && inherit_role->sig)
+ newrole->sig = cpystr(inherit_role->sig);
+
+ if(role->template)
+ newrole->template = cpystr(role->template);
+ else if(inherit_role && inherit_role->template)
+ newrole->template = cpystr(inherit_role->template);
+
+ if(role->cstm)
+ newrole->cstm = copy_list_array(role->cstm);
+ else if(inherit_role && inherit_role->cstm)
+ newrole->cstm = copy_list_array(inherit_role->cstm);
+
+ if(role->smtp)
+ newrole->smtp = copy_list_array(role->smtp);
+ else if(inherit_role && inherit_role->smtp)
+ newrole->smtp = copy_list_array(inherit_role->smtp);
+
+ if(role->nntp)
+ newrole->nntp = copy_list_array(role->nntp);
+ else if(inherit_role && inherit_role->nntp)
+ newrole->nntp = copy_list_array(inherit_role->nntp);
+
+ if(role->nick)
+ newrole->nick = cpystr(role->nick);
+
+ if(inherit_role)
+ free_action(&inherit_role);
+ }
+
+ return(newrole);
+}
+
+
+void
+mail_expunge_prefilter(MAILSTREAM *stream, int flags)
+{
+ int sfdo_state = 0, /* Some Filter Depends On or Sets State */
+ sfdo_scores = 0, /* Some Filter Depends On Scores */
+ ssdo_state = 0; /* Some Score Depends On State */
+
+ if(!stream || !sp_flagged(stream, SP_LOCKED))
+ return;
+
+ /*
+ * An Expunge causes a re-examination of the filters to
+ * see if any state changes have caused new matches.
+ */
+
+ sfdo_scores = (scores_are_used(SCOREUSE_GET) & SCOREUSE_FILTERS);
+ if(sfdo_scores)
+ ssdo_state = (scores_are_used(SCOREUSE_GET) & SCOREUSE_STATEDEP);
+
+ if(!(sfdo_scores && ssdo_state))
+ sfdo_state = some_filter_depends_on_active_state();
+
+
+ if(sfdo_state || (sfdo_scores && ssdo_state)){
+ if(sfdo_scores && ssdo_state)
+ clear_folder_scores(stream);
+
+ reprocess_filter_patterns(stream, sp_msgmap(stream),
+ (flags & MI_CLOSING) |
+ MI_REFILTERING | MI_STATECHGONLY);
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Dispatch messages matching FILTER patterns.
+
+ Args:
+ stream -- mail stream serving messages
+ msgmap -- sequence to msgno mapping table
+ recent -- number of recent messages to check (but really only its
+ nonzeroness is used)
+
+ When we're done, any filtered messages are filtered and the message
+ mapping table has any filtered messages removed.
+ ---*/
+void
+process_filter_patterns(MAILSTREAM *stream, MSGNO_S *msgmap, long int recent)
+{
+ long i, n, raw;
+ imapuid_t uid;
+ int we_cancel = 0, any_msgs = 0, any_to_filter = 0;
+ int exbits, nt = 0, pending_actions = 0, for_debugging = 0;
+ int cleared_index_cache = 0;
+ long rflags = ROLE_DO_FILTER;
+ char *nick = NULL;
+ char busymsg[80];
+ MSGNO_S *tmpmap = NULL;
+ MESSAGECACHE *mc;
+ PAT_S *pat, *nextpat = NULL;
+ SEARCHPGM *pgm = NULL;
+ SEARCHSET *srchset = NULL;
+ long flags = (SE_NOPREFETCH|SE_FREE);
+ PAT_STATE pstate;
+
+ dprint((5, "process_filter_patterns(stream=%s, recent=%ld)\n",
+ !stream ? "<null>" :
+ sp_flagged(stream, SP_INBOX) ? "inbox" :
+ stream->original_mailbox ? stream->original_mailbox :
+ stream->mailbox ? stream->mailbox :
+ "?",
+ recent));
+
+ if(!msgmap || !stream)
+ return;
+
+ if(!recent)
+ sp_set_flags(stream, sp_flags(stream) | SP_FILTERED);
+
+ while(stream && stream->nmsgs && nonempty_patterns(rflags, &pstate)){
+
+ for_debugging++;
+ pending_actions = 0;
+ nextpat = NULL;
+
+ uid = mail_uid(stream, stream->nmsgs);
+
+ /*
+ * Some of the search stuff won't work on old servers so we
+ * get the data and search locally. Big performance hit.
+ */
+ if(is_imap_stream(stream) && !modern_imap_stream(stream))
+ flags |= SE_NOSERVER;
+
+ /*
+ * ignore all previously filtered messages
+ * and, if requested, anything not a recent
+ * arrival...
+ *
+ * Here we're using spare6 (MN_STMP), meaning we'll only
+ * search the ones with spare6 marked, new messages coming
+ * in will not be considered. There used to be orig_nmsgs,
+ * which kept track of this, but if a message gets expunged,
+ * then a new message could be lower than orig_nmsgs.
+ */
+ for(i = 1; i <= stream->nmsgs; i++)
+ if(msgno_exceptions(stream, i, "0", &exbits, FALSE)){
+ if(exbits & MSG_EX_FILTERED){
+ if((mc = mail_elt(stream, i)) != NULL)
+ mc->spare6 = 0;
+ }
+ else if(!recent || !(exbits & MSG_EX_TESTED)){
+ if((mc = mail_elt(stream, i)) != NULL)
+ mc->spare6 = 1;
+
+ any_to_filter++;
+ }
+ else if((mc = mail_elt(stream, i)) != NULL)
+ mc->spare6 = 0;
+ }
+ else{
+ if((mc = mail_elt(stream, i)) != NULL)
+ mc->spare6 = !recent;
+
+ any_to_filter += !recent;
+ }
+
+ if(!any_to_filter){
+ dprint((5, "No messages need filtering\n"));
+ }
+
+ /* Then start searching */
+ for(pat = first_pattern(&pstate); any_to_filter && pat; pat = nextpat){
+ nextpat = next_pattern(&pstate);
+ dprint((5,
+ "Trying filter \"%s\"\n",
+ (pat->patgrp && pat->patgrp->nick)
+ ? pat->patgrp->nick : "?"));
+ if(pat->patgrp && !pat->patgrp->bogus
+ && pat->action && !pat->action->bogus
+ && !trivial_patgrp(pat->patgrp)
+ && match_pattern_folder(pat->patgrp, stream)
+ && !match_pattern_folder_specific(pat->action->folder,
+ stream, FOR_FILTER)){
+
+ /*
+ * We could just keep track of spare6 accurately when
+ * we change the msgno_exceptions flags, but...
+ */
+ for(i = 1; i <= stream->nmsgs; i++){
+ if((mc=mail_elt(stream, i)) && mc->spare6){
+ if(msgno_exceptions(stream, i, "0", &exbits, FALSE)){
+ if(exbits & MSG_EX_FILTERED)
+ mc->sequence = 0;
+ else if(!recent || !(exbits & MSG_EX_TESTED))
+ mc->sequence = 1;
+ else
+ mc->sequence = 0;
+ }
+ else
+ mc->sequence = !recent;
+ }
+ else
+ mc->sequence = 0;
+ }
+
+ if(!(srchset = build_searchset(stream))){
+ dprint((5, "Empty searchset\n"));
+ continue; /* nothing to search, move on */
+ }
+
+#ifdef DEBUG
+ {SEARCHSET *s;
+ dprint((5, "searchset="));
+ for(s = srchset; s; s = s->next){
+ if(s->first == s->last || s->last == 0L){
+ dprint((5, " %ld", s->first));
+ }
+ else{
+ dprint((5, " %ld-%ld", s->first, s->last));
+ }
+ }
+ dprint((5, "\n"));
+ }
+#endif
+ nick = (pat && pat->patgrp && pat->patgrp->nick
+ && pat->patgrp->nick[0]) ? pat->patgrp->nick : NULL;
+ snprintf(busymsg, sizeof(busymsg), _("Processing filter \"%s\""),
+ nick ? nick : "?");
+
+ /*
+ * The strange last argument is so that the busy message
+ * won't come out until after a second if the user sets
+ * the feature to quell "filtering done". That's because
+ * they are presumably interested in the filtering actions
+ * themselves more than what is happening, so they'd
+ * rather see the action messages instead of the processing
+ * message. That's my theory anyway.
+ */
+ if(F_OFF(F_QUELL_FILTER_MSGS, ps_global))
+ any_msgs = we_cancel = busy_cue(busymsg, NULL,
+ F_ON(F_QUELL_FILTER_DONE_MSG, ps_global)
+ ? 1 : 0);
+
+ if(pat->patgrp->stat_bom != PAT_STAT_EITHER){
+ if(pat->patgrp->stat_bom == PAT_STAT_YES){
+ if(!ps_global->beginning_of_month){
+ dprint((5,
+ "Filter %s wants beginning of month and it isn't bom\n",
+ nick ? nick : "?"));
+ continue;
+ }
+ }
+ else if(pat->patgrp->stat_bom == PAT_STAT_NO){
+ if(ps_global->beginning_of_month){
+ dprint((5,
+ "Filter %s does not want beginning of month and it is bom\n",
+ nick ? nick : "?"));
+ continue;
+ }
+ }
+ }
+
+ if(pat->patgrp->stat_boy != PAT_STAT_EITHER){
+ if(pat->patgrp->stat_boy == PAT_STAT_YES){
+ if(!ps_global->beginning_of_year){
+ dprint((5,
+ "Filter %s wants beginning of year and it isn't boy\n",
+ nick ? nick : "?"));
+ continue;
+ }
+ }
+ else if(pat->patgrp->stat_boy == PAT_STAT_NO){
+ if(ps_global->beginning_of_year){
+ dprint((5,
+ "Filter %s does not want beginning of year and it is boy\n",
+ nick ? nick : "?"));
+ continue;
+ }
+ }
+ }
+
+ pgm = match_pattern_srchpgm(pat->patgrp, stream, srchset);
+
+ pine_mail_search_full(stream, "UTF-8", pgm, flags);
+
+ /* check scores */
+ if(scores_are_used(SCOREUSE_GET) & SCOREUSE_FILTERS &&
+ pat->patgrp->do_score){
+ SEARCHSET *s, *ss;
+
+ /*
+ * Build a searchset so we can get all the scores we
+ * need and only the scores we need efficiently.
+ */
+
+ for(i = 1; i <= stream->nmsgs; i++)
+ if((mc = mail_elt(stream, i)) != NULL)
+ mc->sequence = 0;
+
+ for(s = srchset; s; s = s->next)
+ for(i = s->first; i <= s->last; i++)
+ if(i > 0L && stream && i <= stream->nmsgs
+ && (mc=mail_elt(stream, i)) && mc->searched &&
+ get_msg_score(stream, i) == SCORE_UNDEF)
+ mc->sequence = 1;
+
+ if((ss = build_searchset(stream)) != NULL){
+ (void)calculate_some_scores(stream, ss, 0);
+ mail_free_searchset(&ss);
+ }
+
+ /*
+ * Now check the patterns which have matched so far
+ * to see if their score is in the score interval.
+ */
+ for(s = srchset; s; s = s->next)
+ for(i = s->first; i <= s->last; i++)
+ if(i > 0L && stream && i <= stream->nmsgs
+ && (mc=mail_elt(stream, i)) && mc->searched){
+ long score;
+
+ score = get_msg_score(stream, i);
+
+ /*
+ * If the score is outside all of the intervals,
+ * turn off the searched bit.
+ * So that means we check each interval and if
+ * it is inside any interval we stop and leave
+ * the bit set. If it is outside we keep checking.
+ */
+ if(score != SCORE_UNDEF){
+ INTVL_S *iv;
+
+ for(iv = pat->patgrp->score; iv; iv = iv->next)
+ if(score >= iv->imin && score <= iv->imax)
+ break;
+
+ if(!iv)
+ mc->searched = NIL;
+ }
+ }
+ }
+
+ /* check for 8bit subject match or not */
+ if(pat->patgrp->stat_8bitsubj != PAT_STAT_EITHER)
+ find_8bitsubj_in_messages(stream, srchset,
+ pat->patgrp->stat_8bitsubj, 0);
+
+ /* if there are still matches, check for charset matches */
+ if(pat->patgrp->charsets)
+ find_charsets_in_messages(stream, srchset, pat->patgrp, 0);
+
+ if(pat->patgrp->inabook != IAB_EITHER)
+ address_in_abook(stream, srchset, pat->patgrp->inabook, pat->patgrp->abooks);
+
+ /* Still matches? Run the categorization command on each msg. */
+ if(pith_opt_filter_pattern_cmd)
+ (*pith_opt_filter_pattern_cmd)(pat->patgrp->category_cmd, srchset, stream, pat->patgrp->cat_lim, pat->patgrp->cat);
+
+ if(we_cancel){
+ cancel_busy_cue(-1);
+ we_cancel = 0;
+ }
+
+ nt = pat->action->non_terminating;
+ pending_actions = MAX(nt, pending_actions);
+
+ /*
+ * Change some state bits.
+ * This used to only happen if kill was not set, but
+ * it can be useful to Delete a message even if killing.
+ * That way, it will show up in another pine that isn't
+ * running the same filter as Deleted, so the user won't
+ * bother looking at it. Hubert 2004-11-16
+ */
+ if(pat->action->state_setting_bits
+ || pat->action->keyword_set
+ || pat->action->keyword_clr){
+ tmpmap = NULL;
+ mn_init(&tmpmap, stream->nmsgs);
+
+ for(i = 1L, n = 0L; i <= stream->nmsgs; i++)
+ if((mc = mail_elt(stream, i)) && mc->searched
+ && !(msgno_exceptions(stream, i, "0", &exbits, FALSE)
+ && (exbits & MSG_EX_FILTERED))){
+ if(!n++){
+ mn_set_cur(tmpmap, i);
+ }
+ else{
+ mn_add_cur(tmpmap, i);
+ }
+ }
+
+ if(n){
+ long flagbits;
+ char **keywords_to_set = NULL,
+ **keywords_to_clr = NULL;
+ PATTERN_S *pp;
+ int cnt;
+
+ flagbits = pat->action->state_setting_bits;
+
+ if(pat->action->keyword_set){
+ for(cnt = 0, pp = pat->action->keyword_set;
+ pp; pp = pp->next)
+ cnt++;
+
+ keywords_to_set = (char **) fs_get((cnt+1) *
+ sizeof(*keywords_to_set));
+ memset(keywords_to_set, 0,
+ (cnt+1) * sizeof(*keywords_to_set));
+ for(cnt = 0, pp = pat->action->keyword_set;
+ pp; pp = pp->next){
+ char *q;
+
+ q = nick_to_keyword(pp->substring);
+ if(q && q[0])
+ keywords_to_set[cnt++] = cpystr(q);
+ }
+
+ flagbits |= F_KEYWORD;
+ }
+
+ if(pat->action->keyword_clr){
+ for(cnt = 0, pp = pat->action->keyword_clr;
+ pp; pp = pp->next)
+ cnt++;
+
+ keywords_to_clr = (char **) fs_get((cnt+1) *
+ sizeof(*keywords_to_clr));
+ memset(keywords_to_clr, 0,
+ (cnt+1) * sizeof(*keywords_to_clr));
+ for(cnt = 0, pp = pat->action->keyword_clr;
+ pp; pp = pp->next){
+ char *q;
+
+ q = nick_to_keyword(pp->substring);
+ if(q && q[0])
+ keywords_to_clr[cnt++] = cpystr(q);
+ }
+
+ flagbits |= F_UNKEYWORD;
+ }
+
+ set_some_flags(stream, tmpmap, flagbits,
+ keywords_to_set, keywords_to_clr, 1,
+ nick);
+ }
+
+ mn_give(&tmpmap);
+ }
+
+ /*
+ * The two halves of the if-else are almost the same and
+ * could probably be combined cleverly. The if clause
+ * is simply setting the MSG_EX_FILTERED bit, and leaving
+ * n set to zero. The msgno_exclude is not done in this case.
+ * The else clause excludes each message (because it is
+ * either filtered into nothing or moved to folder). The
+ * exclude messes with the msgmap and that changes max_msgno,
+ * so the loop control is a little tricky.
+ */
+ if(!(pat->action->kill || pat->action->folder)){
+ n = 0L;
+ for(i = 1L; i <= mn_get_total(msgmap); i++)
+ if((raw = mn_m2raw(msgmap, i)) > 0L
+ && stream && raw <= stream->nmsgs
+ && (mc = mail_elt(stream, raw)) && mc->searched){
+ dprint((5,
+ "FILTER matching \"%s\": msg %ld%s\n",
+ nick ? nick : "unnamed",
+ raw, nt ? " (dont stop)" : ""));
+ if(msgno_exceptions(stream, raw, "0", &exbits, FALSE))
+ exbits |= (nt ? MSG_EX_FILTONCE : MSG_EX_FILTERED);
+ else
+ exbits = (nt ? MSG_EX_FILTONCE : MSG_EX_FILTERED);
+
+ /*
+ * If this matched an earlier non-terminating rule
+ * we've been keeping track of that so that we can
+ * turn it into a permanent match at the end.
+ * However, now we've matched another rule that is
+ * terminating so we don't have to worry about it
+ * anymore. Turn off the flag.
+ */
+ if(!nt && exbits & MSG_EX_FILTONCE)
+ exbits ^= MSG_EX_FILTONCE;
+
+ exbits &= ~MSG_EX_STATECHG;
+
+ msgno_exceptions(stream, raw, "0", &exbits, TRUE);
+ }
+ }
+ else{
+ for(i = 1L, n = 0L; i <= mn_get_total(msgmap); )
+ if((raw = mn_m2raw(msgmap, i))
+ && raw > 0L && stream && raw <= stream->nmsgs
+ && (mc = mail_elt(stream, raw)) && mc->searched){
+ dprint((5,
+ "FILTER matching \"%s\": msg %ld %s%s\n",
+ nick ? nick : "unnamed",
+ raw, pat->action->folder ? "filed" : "killed",
+ nt ? " (dont stop)" : ""));
+ if(nt)
+ i++;
+ else{
+ if(!cleared_index_cache
+ && stream == ps_global->mail_stream){
+ cleared_index_cache = 1;
+ clear_index_cache(stream, 0);
+ }
+
+ msgno_exclude(stream, msgmap, i, 1);
+ /*
+ * If this message is new, decrement
+ * new_mail_count. Previously, the caller would
+ * do this by counting MN_EXCLUDE before and after,
+ * but the results weren't accurate in the case
+ * where new messages arrived while filtering,
+ * or the filtered message could have gotten
+ * expunged.
+ */
+ if(msgno_exceptions(stream, raw, "0", &exbits,
+ FALSE)
+ && (exbits & MSG_EX_RECENT)){
+ long l, ll;
+
+ l = sp_new_mail_count(stream);
+ ll = sp_recent_since_visited(stream);
+ dprint((5, "New message being filtered, decrement new_mail_count: %ld -> %ld\n", l, l-1L));
+ if(l > 0L)
+ sp_set_new_mail_count(stream, l-1L);
+ if(ll > 0L)
+ sp_set_recent_since_visited(stream, ll-1L);
+ }
+ }
+
+ if(msgno_exceptions(stream, raw, "0", &exbits, FALSE))
+ exbits |= (nt ? MSG_EX_FILTONCE : MSG_EX_FILTERED);
+ else
+ exbits = (nt ? MSG_EX_FILTONCE : MSG_EX_FILTERED);
+
+ /* set pending exclusion for later */
+ if(nt)
+ exbits |= MSG_EX_PEND_EXLD;
+
+ /*
+ * If this matched an earlier non-terminating rule
+ * we've been keeping track of that so that we can
+ * turn it into a permanent match at the end.
+ * However, now we've matched another rule that is
+ * terminating so we don't have to worry about it
+ * anymore. Turn off the flags.
+ */
+ if(!nt && exbits & MSG_EX_FILTONCE){
+ exbits ^= MSG_EX_FILTONCE;
+
+ /* we've already excluded it, too */
+ if(exbits & MSG_EX_PEND_EXLD)
+ exbits ^= MSG_EX_PEND_EXLD;
+ }
+
+ exbits &= ~MSG_EX_STATECHG;
+
+ msgno_exceptions(stream, raw, "0", &exbits, TRUE);
+ n++;
+ }
+ else
+ i++;
+ }
+
+ if(n && pat->action->folder){
+ PATTERN_S *p;
+ int err = 0;
+
+ tmpmap = NULL;
+ mn_init(&tmpmap, stream->nmsgs);
+
+ /*
+ * For everything matching msg that hasn't
+ * already been saved somewhere, do it...
+ */
+ for(i = 1L, n = 0L; i <= stream->nmsgs; i++)
+ if((mc = mail_elt(stream, i)) && mc->searched
+ && !(msgno_exceptions(stream, i, "0", &exbits, FALSE)
+ && (exbits & MSG_EX_FILED))){
+ if(!n++){
+ mn_set_cur(tmpmap, i);
+ }
+ else{
+ mn_add_cur(tmpmap, i);
+ }
+ }
+
+ /*
+ * Remove already deleted messages from the tmp
+ * message map.
+ * There is a bug with this. If a filter moves a
+ * message to another folder _and_ sets the deleted
+ * status, then the setting of the deleted status
+ * will already have happened above in set_some_flags.
+ * So if the move_only_if_not_deleted bit is set that
+ * message will never be moved. A workaround for the
+ * user is to not set the move-only-if-not-deleted
+ * option.
+ */
+ if(n && pat->action->move_only_if_not_deleted){
+ char *seq;
+ MSGNO_S *tmpmap2 = NULL;
+ long nn = 0L;
+ MESSAGECACHE *mc;
+
+ mn_init(&tmpmap2, stream->nmsgs);
+
+ /*
+ * First, make sure elts are valid for all the
+ * interesting messages.
+ */
+ if((seq = invalid_elt_sequence(stream, tmpmap)) != NULL){
+ pine_mail_fetch_flags(stream, seq, NIL);
+ fs_give((void **) &seq);
+ }
+
+ for(i = mn_first_cur(tmpmap); i > 0L;
+ i = mn_next_cur(tmpmap)){
+ mc = ((raw = mn_m2raw(tmpmap, i)) > 0L
+ && stream && raw <= stream->nmsgs)
+ ? mail_elt(stream, raw) : NULL;
+ if(mc && !mc->deleted){
+ if(!nn++){
+ mn_set_cur(tmpmap2, i);
+ }
+ else{
+ mn_add_cur(tmpmap2, i);
+ }
+ }
+ }
+
+ mn_give(&tmpmap);
+ tmpmap = tmpmap2;
+ n = nn;
+ }
+
+ if(n){
+ for(p = pat->action->folder; p; p = p->next){
+ int dval;
+ int flags_for_save;
+ char *processed_name;
+
+ /* does this filter set delete bit? ... */
+ convert_statebits_to_vals(pat->action->state_setting_bits, &dval, NULL, NULL, NULL);
+ /* ... if so, tell save not to fix it before copy */
+ flags_for_save = SV_FOR_FILT | SV_INBOXWOCNTXT |
+ (nt ? 0 : SV_DELETE) |
+ ((dval != ACT_STAT_SET) ? SV_FIX_DELS : 0);
+ processed_name = detoken_src(p->substring,
+ FOR_FILT, NULL,
+ NULL, NULL, NULL);
+ if(move_filtered_msgs(stream, tmpmap,
+ (processed_name && *processed_name)
+ ? processed_name : p->substring,
+ flags_for_save, nick)){
+ /*
+ * If we filtered into the current
+ * folder, chuck a ping down the
+ * stream so the user can notice it
+ * before the next new mail check...
+ */
+ if(ps_global->mail_stream
+ && ps_global->mail_stream != stream
+ && match_pattern_folder_specific(
+ pat->action->folder,
+ ps_global->mail_stream,
+ FOR_FILTER)){
+ (void) pine_mail_ping(ps_global->mail_stream);
+ }
+ }
+ else{
+ err = 1;
+ break;
+ }
+
+ if(processed_name)
+ fs_give((void **) &processed_name);
+ }
+
+ if(!err)
+ for(n = mn_first_cur(tmpmap);
+ n > 0L;
+ n = mn_next_cur(tmpmap)){
+
+ if(msgno_exceptions(stream, mn_m2raw(tmpmap, n),
+ "0", &exbits, FALSE))
+ exbits |= (nt ? MSG_EX_FILEONCE : MSG_EX_FILED);
+ else
+ exbits = (nt ? MSG_EX_FILEONCE : MSG_EX_FILED);
+
+ exbits &= ~MSG_EX_STATECHG;
+
+ msgno_exceptions(stream, mn_m2raw(tmpmap, n),
+ "0", &exbits, TRUE);
+ }
+ }
+
+ mn_give(&tmpmap);
+ }
+
+ mail_free_searchset(&srchset);
+ }
+
+ /*
+ * If this is the last rule,
+ * we make sure we delete messages that we delayed deleting
+ * in the save. We delayed so that the deletion wouldn't have
+ * an effect on later rules. We convert any temporary
+ * FILED (FILEONCE) and FILTERED (FILTONCE) flags
+ * (which were set by an earlier non-terminating rule)
+ * to permanent. We also exclude some messages from the view.
+ */
+ if(pending_actions && !nextpat){
+
+ pending_actions = 0;
+ tmpmap = NULL;
+ mn_init(&tmpmap, stream->nmsgs);
+
+ for(i = 1L, n = 0L; i <= mn_get_total(msgmap); i++){
+
+ raw = mn_m2raw(msgmap, i);
+ if(msgno_exceptions(stream, raw, "0", &exbits, FALSE)){
+ if(exbits & MSG_EX_FILEONCE){
+ if(!n++){
+ mn_set_cur(tmpmap, raw);
+ }
+ else{
+ mn_add_cur(tmpmap, raw);
+ }
+ }
+ }
+ }
+
+ if(n)
+ set_some_flags(stream, tmpmap, F_DEL, NULL, NULL, 0, NULL);
+
+ mn_give(&tmpmap);
+
+ for(i = 1L; i <= mn_get_total(msgmap); i++){
+ raw = mn_m2raw(msgmap, i);
+ if(msgno_exceptions(stream, raw, "0", &exbits, FALSE)){
+ if(exbits & MSG_EX_PEND_EXLD){
+ if(!cleared_index_cache
+ && stream == ps_global->mail_stream){
+ cleared_index_cache = 1;
+ clear_index_cache(stream, 0);
+ }
+
+ msgno_exclude(stream, msgmap, i, 1);
+ if(msgno_exceptions(stream, raw, "0",
+ &exbits, FALSE)
+ && (exbits & MSG_EX_RECENT)){
+ long l, ll;
+
+ /*
+ * If this message is new, decrement
+ * new_mail_count. See the above
+ * call to msgno_exclude.
+ */
+ l = sp_new_mail_count(stream);
+ ll = sp_recent_since_visited(stream);
+ dprint((5, "New message being filtered. Decrement new_mail_count: %ld -> %ld\n", l, l-1L));
+ if(l > 0L)
+ sp_set_new_mail_count(stream, l - 1L);
+ if(ll > 0L)
+ sp_set_recent_since_visited(stream, ll - 1L);
+ }
+
+ i--; /* to compensate for loop's i++ */
+ }
+
+ /* get rid of temporary flags */
+ if(exbits & (MSG_EX_FILTONCE | MSG_EX_FILEONCE |
+ MSG_EX_PEND_EXLD)){
+ if(exbits & MSG_EX_FILTONCE){
+ /* convert to permament */
+ exbits ^= MSG_EX_FILTONCE;
+ exbits |= MSG_EX_FILTERED;
+ }
+
+ /* convert to permament */
+ if(exbits & MSG_EX_FILEONCE){
+ exbits ^= MSG_EX_FILEONCE;
+ exbits |= MSG_EX_FILED;
+ }
+
+ if(exbits & MSG_EX_PEND_EXLD)
+ exbits ^= MSG_EX_PEND_EXLD;
+
+ exbits &= ~MSG_EX_STATECHG;
+
+ msgno_exceptions(stream, raw, "0", &exbits,TRUE);
+ }
+ }
+ }
+ }
+ }
+
+ /* New mail arrival means start over */
+ if(mail_uid(stream, stream->nmsgs) == uid)
+ break;
+ /* else, go again */
+
+ recent = 1; /* only check recent ones now */
+ }
+
+ if(!recent){
+ /* clear status change flags */
+ for(i = 1; i <= stream->nmsgs; i++){
+ if(msgno_exceptions(stream, i, "0", &exbits, FALSE)){
+ if(exbits & MSG_EX_STATECHG){
+ exbits &= ~MSG_EX_STATECHG;
+ msgno_exceptions(stream, i, "0", &exbits, TRUE);
+ }
+ }
+ }
+ }
+
+ /* clear any private "recent" flags and add TESTED flag */
+ for(i = 1; i <= stream->nmsgs; i++){
+ if(msgno_exceptions(stream, i, "0", &exbits, FALSE)){
+ if(exbits & MSG_EX_RECENT
+ || !(exbits & MSG_EX_TESTED)
+ || (!recent && exbits & MSG_EX_STATECHG)){
+ exbits &= ~MSG_EX_RECENT;
+ exbits |= MSG_EX_TESTED;
+ if(!recent)
+ exbits &= ~MSG_EX_STATECHG;
+
+ msgno_exceptions(stream, i, "0", &exbits, TRUE);
+ }
+ }
+ else{
+ exbits = MSG_EX_TESTED;
+ msgno_exceptions(stream, i, "0", &exbits, TRUE);
+ }
+
+ /* clear any stmp flags just in case */
+ if((mc = mail_elt(stream, i)) != NULL)
+ mc->spare6 = 0;
+ }
+
+ msgmap->flagged_stmp = 0L;
+
+ if(any_msgs && F_OFF(F_QUELL_FILTER_MSGS, ps_global)
+ && F_OFF(F_QUELL_FILTER_DONE_MSG, ps_global)){
+ q_status_message(SM_ORDER, 0, 1, _("filtering done"));
+ display_message('x');
+ }
+}
+
+
+/*
+ * Re-check the filters for matches because a change of message state may
+ * have changed the results.
+ */
+void
+reprocess_filter_patterns(MAILSTREAM *stream, MSGNO_S *msgmap, int flags)
+{
+ if(stream){
+ long i;
+ int exbits;
+
+ if(msgno_include(stream, msgmap, flags)
+ && stream == ps_global->mail_stream
+ && !(flags & MI_CLOSING)){
+ clear_index_cache(stream, 0);
+ refresh_sort(stream, msgmap, SRT_NON);
+ ps_global->mangled_header = 1;
+ }
+
+ /*
+ * Passing 1 in the last argument causes it to only look at the
+ * messages we included above, which should be only the ones we
+ * need to look at.
+ */
+ process_filter_patterns(stream, msgmap,
+ (flags & MI_STATECHGONLY) ? 1L : 0);
+
+ /* clear status change flags */
+ for(i = 1; i <= stream->nmsgs; i++){
+ if(msgno_exceptions(stream, i, "0", &exbits, FALSE)){
+ if(exbits & MSG_EX_STATECHG){
+ exbits &= ~MSG_EX_STATECHG;
+ msgno_exceptions(stream, i, "0", &exbits, TRUE);
+ }
+ }
+ }
+ }
+}
+
+
+/*
+ * When killing or filtering we don't want to match by mistake. So if
+ * a pattern has nothing set except the Current Folder Type (which is always
+ * set to something) we'll consider it to be trivial and not a match.
+ * match_pattern uses this to determine if there is a match, where it is
+ * just triggered on the Current Folder Type.
+ */
+int
+trivial_patgrp(PATGRP_S *patgrp)
+{
+ int ret = 1;
+
+ if(patgrp){
+ if(patgrp->subj || patgrp->cc || patgrp->from || patgrp->to ||
+ patgrp->sender || patgrp->news || patgrp->recip || patgrp->partic ||
+ patgrp->alltext || patgrp->bodytext)
+ ret = 0;
+
+ if(ret && patgrp->do_age)
+ ret = 0;
+
+ if(ret && patgrp->do_size)
+ ret = 0;
+
+ if(ret && patgrp->do_score)
+ ret = 0;
+
+ if(ret && patgrp->category_cmd && patgrp->category_cmd[0])
+ ret = 0;
+
+ if(ret && patgrp_depends_on_state(patgrp))
+ ret = 0;
+
+ if(ret && patgrp->stat_8bitsubj != PAT_STAT_EITHER)
+ ret = 0;
+
+ if(ret && patgrp->charsets)
+ ret = 0;
+
+ if(ret && patgrp->stat_bom != PAT_STAT_EITHER)
+ ret = 0;
+
+ if(ret && patgrp->stat_boy != PAT_STAT_EITHER)
+ ret = 0;
+
+ if(ret && patgrp->inabook != IAB_EITHER)
+ ret = 0;
+
+ if(ret && patgrp->arbhdr){
+ ARBHDR_S *a;
+
+ for(a = patgrp->arbhdr; a && ret; a = a->next)
+ if(a->field && a->field[0] && a->p)
+ ret = 0;
+ }
+ }
+
+ return(ret);
+}
+
+
+int
+some_filter_depends_on_active_state(void)
+{
+ long rflags = ROLE_DO_FILTER;
+ PAT_S *pat;
+ PAT_STATE pstate;
+ int ret = 0;
+
+ if(nonempty_patterns(rflags, &pstate)){
+
+ for(pat = first_pattern(&pstate);
+ pat && !ret;
+ pat = next_pattern(&pstate))
+ if(patgrp_depends_on_active_state(pat->patgrp))
+ ret++;
+ }
+
+ return(ret);
+}
+
+
+/*----------------------------------------------------------------------
+ Move all messages with sequence bit lit to dstfldr
+
+ Args: stream -- stream to use
+ msgmap -- map of messages to be moved
+ dstfldr -- folder to receive moved messages
+ flags_for_save
+
+ Returns: nonzero on success or on readonly stream
+ ----*/
+int
+move_filtered_msgs(MAILSTREAM *stream, MSGNO_S *msgmap, char *dstfldr,
+ int flags_for_save, char *nick)
+{
+ long n;
+ int we_cancel = 0, width;
+ CONTEXT_S *save_context = NULL;
+ char buf[MAX_SCREEN_COLS+1], sbuf[MAX_SCREEN_COLS+1];
+ char *save_ref = NULL;
+#define FILTMSG_MAX 30
+
+ if(!stream)
+ return 0;
+
+ if(READONLY_FOLDER(stream)){
+ dprint((1,
+ "Can't delete messages in readonly folder \"%s\"\n",
+ STREAMNAME(stream)));
+ q_status_message1(SM_ORDER, 1, 3,
+ _("Can't delete messages in readonly folder \"%s\""),
+ STREAMNAME(stream));
+ return 1;
+ }
+
+ buf[0] = '\0';
+
+ width = MAX(10, ps_global->ttyo ? ps_global->ttyo->screen_cols : 80);
+ snprintf(buf, sizeof(buf), "%.30s%.2sMoving %.10s filtered message%.2s to \"\"",
+ nick ? nick : "", nick ? ": " : "",
+ comatose(mn_total_cur(msgmap)), plural(mn_total_cur(msgmap)));
+ /* 2 is for brackets, 5 is for " DONE" in busy alarm */
+ width -= (strlen(buf) + 2 + 5);
+ snprintf(buf, sizeof(buf), "%.30s%.2sMoving %.10s filtered message%.2s to \"%s\"",
+ nick ? nick : "", nick ? ": " : "",
+ comatose(mn_total_cur(msgmap)), plural(mn_total_cur(msgmap)),
+ short_str(dstfldr, sbuf, sizeof(sbuf), width, FrontDots));
+
+ dprint((5, "%s\n", buf));
+
+ if(F_OFF(F_QUELL_FILTER_MSGS, ps_global))
+ we_cancel = busy_cue(buf, NULL, 0);
+
+ if(!is_absolute_path(dstfldr)
+ && !(save_context = default_save_context(ps_global->context_list)))
+ save_context = ps_global->context_list;
+
+ /*
+ * Because this save is happening independent of where the user is
+ * in the folder hierarchy and has nothing to do with that, we want
+ * to ignore the reference field built into the context. Zero it out
+ * temporarily here so it won't affect the results of context_apply
+ * in save.
+ *
+ * This might be a problem elsewhere, as well. The same thing as this
+ * is also done in match_pattern_folder_specific, which is also only
+ * called from within process_filter_patterns. But there could be
+ * others. We could have a separate function, something like
+ * copy_default_save_context(), that automatically zeroes out the
+ * reference field in the copy. However, some of the uses of
+ * default_save_context() require that a pointer into the actual
+ * context list is returned, so this would have to be done carefully.
+ * Besides, we don't know of any other problems so we'll just change
+ * these known cases for now.
+ */
+ if(save_context && save_context->dir){
+ save_ref = save_context->dir->ref;
+ save_context->dir->ref = NULL;
+ }
+
+ n = save(ps_global, stream, save_context, dstfldr, msgmap, flags_for_save);
+
+ if(save_ref)
+ save_context->dir->ref = save_ref;
+
+ if(n != mn_total_cur(msgmap)){
+ int exbits;
+ long x;
+
+ buf[0] = '\0';
+
+ /* Clear "filtered" flags for failed messages */
+ for(x = mn_first_cur(msgmap); x > 0L; x = mn_next_cur(msgmap))
+ if(n-- <= 0 && msgno_exceptions(stream, mn_m2raw(msgmap, x),
+ "0", &exbits, FALSE)){
+ exbits &= ~(MSG_EX_FILTONCE | MSG_EX_FILEONCE |
+ MSG_EX_FILTERED | MSG_EX_FILED);
+ msgno_exceptions(stream, mn_m2raw(msgmap, x),
+ "0", &exbits, TRUE);
+ }
+
+ /* then re-incorporate them into folder they belong */
+ (void) msgno_include(stream, sp_msgmap(stream), MI_NONE);
+ clear_index_cache(stream, 0);
+ refresh_sort(stream, sp_msgmap(stream), SRT_NON);
+ ps_global->mangled_header = 1;
+ }
+ else{
+ snprintf(buf, sizeof(buf), _("Filtered all %s message to \"%s\""),
+ comatose(n), dstfldr);
+ dprint((5, "%s\n", buf));
+ }
+
+ if(we_cancel)
+ cancel_busy_cue(buf[0] ? 0 : -1);
+
+ return(buf[0] != '\0');
+}
+
+
+/*----------------------------------------------------------------------
+ Move all messages with sequence bit lit to dstfldr
+
+ Args: stream -- stream to use
+ msgmap -- which messages to set
+ flagbits -- which flags to set or clear
+ kw_on -- keywords to set
+ kw_off -- keywords to clear
+ verbose -- 1 => busy alarm after 1 second
+ 2 => forced busy alarm
+ ----*/
+void
+set_some_flags(MAILSTREAM *stream, MSGNO_S *msgmap, long int flagbits,
+ char **kw_on, char **kw_off, int verbose, char *nick)
+{
+ long count = 0L, flipped_flags;
+ int we_cancel = 0;
+ char buf[150], *seq;
+
+ if(!stream)
+ return;
+
+ if(READONLY_FOLDER(stream)){
+ dprint((1, "Can't set flags in readonly folder \"%s\"\n",
+ STREAMNAME(stream)));
+ q_status_message1(SM_ORDER, 1, 3,
+ _("Can't set flags in readonly folder \"%s\""),
+ STREAMNAME(stream));
+ return;
+ }
+
+ /* use this to determine if anything needs to be done */
+ flipped_flags = ((flagbits & F_ANS) ? F_UNANS : 0) |
+ ((flagbits & F_UNANS) ? F_ANS : 0) |
+ ((flagbits & F_FLAG) ? F_UNFLAG : 0) |
+ ((flagbits & F_UNFLAG) ? F_FLAG : 0) |
+ ((flagbits & F_DEL) ? F_UNDEL : 0) |
+ ((flagbits & F_UNDEL) ? F_DEL : 0) |
+ ((flagbits & F_SEEN) ? F_UNSEEN : 0) |
+ ((flagbits & F_UNSEEN) ? F_SEEN : 0) |
+ ((flagbits & F_KEYWORD) ? F_UNKEYWORD : 0) |
+ ((flagbits & F_UNKEYWORD) ? F_KEYWORD : 0);
+ if((seq = currentf_sequence(stream, msgmap, flipped_flags, &count, 0,
+ kw_off, kw_on)) != NULL){
+ char *sets = NULL, *clears = NULL;
+ char *ps, *pc, **t;
+ size_t clen, slen;
+
+ /* allocate enough space for mail_flags arguments */
+ for(slen=100, t = kw_on; t && *t; t++)
+ slen += (strlen(*t) + 1);
+
+ sets = (char *) fs_get(slen * sizeof(*sets));
+
+ for(clen=100, t = kw_off; t && *t; t++)
+ clen += (strlen(*t) + 1);
+
+ clears = (char *) fs_get(clen * sizeof(*clears));
+
+ sets[0] = clears[0] = '\0';
+ ps = sets;
+ pc = clears;
+
+ snprintf(buf, sizeof(buf), "%.30s%.2sSetting flags in %.10s message%.10s",
+ nick ? nick : "", nick ? ": " : "",
+ comatose(count), plural(count));
+
+ if(F_OFF(F_QUELL_FILTER_MSGS, ps_global))
+ we_cancel = busy_cue(buf, NULL, verbose ? 0 : 1);
+
+ /*
+ * What's going on here? If we want to set more than one flag
+ * we can do it with a single roundtrip by combining the arguments
+ * into a single call and separating them with spaces.
+ */
+ if(flagbits & F_ANS)
+ sstrncpy(&ps, "\\ANSWERED", slen-(ps-sets));
+ if(flagbits & F_FLAG){
+ if(ps > sets)
+ sstrncpy(&ps, " ", slen-(ps-sets));
+
+ sstrncpy(&ps, "\\FLAGGED", slen-(ps-sets));
+ }
+ if(flagbits & F_DEL){
+ if(ps > sets)
+ sstrncpy(&ps, " ", slen-(ps-sets));
+
+ sstrncpy(&ps, "\\DELETED", slen-(ps-sets));
+ }
+ if(flagbits & F_SEEN){
+ if(ps > sets)
+ sstrncpy(&ps, " ", slen-(ps-sets));
+
+ sstrncpy(&ps, "\\SEEN", slen-(ps-sets));
+ }
+ if(flagbits & F_KEYWORD){
+ for(t = kw_on; t && *t; t++){
+ int i;
+
+ /*
+ * We may be able to tell that this will fail before
+ * we actually try it.
+ */
+ if(stream->kwd_create ||
+ (((i=user_flag_index(stream, *t)) >= 0) && i < NUSERFLAGS)){
+ if(ps > sets)
+ sstrncpy(&ps, " ", slen-(ps-sets));
+
+ sstrncpy(&ps, *t, slen-(ps-sets));
+ }
+ else{
+ int some_defined = 0;
+ static int msg_delivered = 0;
+
+ some_defined = some_user_flags_defined(stream);
+
+ if(msg_delivered++ < 2){
+ char b[200], c[200], *p;
+ int w;
+
+ if(some_defined){
+ snprintf(b, sizeof(b), "Can't set \"%.30s\". No more keywords in ", keyword_to_nick(*t));
+ w = MIN((ps_global->ttyo ? ps_global->ttyo->screen_cols : 80) - strlen(b) - 1 - 2, sizeof(c)-1);
+ p = short_str(STREAMNAME(stream), c, sizeof(c), w, FrontDots);
+ q_status_message2(SM_ORDER, 3, 3, "%s%s!", b, p);
+ }
+ else{
+ snprintf(b, sizeof(b), "Can't set \"%.30s\". Can't add keywords in ", keyword_to_nick(*t));
+ w = MIN((ps_global->ttyo ? ps_global->ttyo->screen_cols : 80) - strlen(b) - 1 - 2, sizeof(c)-1);
+ p = short_str(STREAMNAME(stream), c, sizeof(c), w, FrontDots);
+ q_status_message2(SM_ORDER, 3, 3, "%s%s!", b, p);
+ }
+ }
+
+ if(some_defined){
+ dprint((1, "Can't set keyword \"%s\". No more keywords allowed in %s\n", *t, stream->mailbox ? stream->mailbox : "target folder"));
+ }
+ else{
+ dprint((1, "Can't set keyword \"%s\". Can't add keywords in %s\n", *t, stream->mailbox ? stream->mailbox : "target folder"));
+ }
+ }
+ }
+ }
+
+ /* need a separate call for the clears */
+ if(flagbits & F_UNANS)
+ sstrncpy(&pc, "\\ANSWERED", clen-(pc-clears));
+ if(flagbits & F_UNFLAG){
+ if(pc > clears)
+ sstrncpy(&pc, " ", clen-(pc-clears));
+
+ sstrncpy(&pc, "\\FLAGGED", clen-(pc-clears));
+ }
+ if(flagbits & F_UNDEL){
+ if(pc > clears)
+ sstrncpy(&pc, " ", clen-(pc-clears));
+
+ sstrncpy(&pc, "\\DELETED", clen-(pc-clears));
+ }
+ if(flagbits & F_UNSEEN){
+ if(pc > clears)
+ sstrncpy(&pc, " ", clen-(pc-clears));
+
+ sstrncpy(&pc, "\\SEEN", clen-(pc-clears));
+ }
+ if(flagbits & F_UNKEYWORD){
+ for(t = kw_off; t && *t; t++){
+ if(pc > clears)
+ sstrncpy(&pc, " ", clen-(pc-clears));
+
+ sstrncpy(&pc, *t, clen-(pc-clears));
+ }
+ }
+
+
+ if(sets[0])
+ mail_flag(stream, seq, sets, ST_SET);
+
+ if(clears[0])
+ mail_flag(stream, seq, clears, 0L);
+
+ fs_give((void **) &sets);
+ fs_give((void **) &clears);
+ fs_give((void **) &seq);
+
+ if(we_cancel)
+ cancel_busy_cue(buf[0] ? 0 : -1);
+ }
+}
+
+
+/*
+ * Delete messages which are marked FILTERED and excluded.
+ * Messages which are FILTERED but not excluded are those that have had
+ * their state set by a filter pattern, but are to remain in the same
+ * folder.
+ */
+void
+delete_filtered_msgs(MAILSTREAM *stream)
+{
+ int exbits;
+ long i;
+ char *seq;
+ MESSAGECACHE *mc;
+
+ for(i = 1L; i <= stream->nmsgs; i++)
+ if(msgno_exceptions(stream, i, "0", &exbits, FALSE)
+ && (exbits & MSG_EX_FILTERED)
+ && get_lflag(stream, NULL, i, MN_EXLD)){
+ if((mc = mail_elt(stream, i)) != NULL)
+ mc->sequence = 1;
+ }
+ else if((mc = mail_elt(stream, i)) != NULL)
+ mc->sequence = 0;
+
+ if((seq = build_sequence(stream, NULL, NULL)) != NULL){
+ mail_flag(stream, seq, "\\DELETED", ST_SET | ST_SILENT);
+ fs_give((void **) &seq);
+ }
+}
diff --git a/pith/pattern.h b/pith/pattern.h
new file mode 100644
index 00000000..4b222d3e
--- /dev/null
+++ b/pith/pattern.h
@@ -0,0 +1,417 @@
+/*
+ * $Id: pattern.h 942 2008-03-04 18:21:33Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_PATTERN_INCLUDED
+#define PITH_PATTERN_INCLUDED
+
+
+#include "../pith/msgno.h"
+#include "../pith/sorttype.h"
+#include "../pith/string.h"
+#include "../pith/indxtype.h"
+
+
+/*
+ * This structure is used to contain strings which are matched against
+ * header fields. The match is a simple substring match. The match is
+ * an OR of all the patterns in the PATTERN_S list. That is,
+ * substring1_matches OR substring2_matches OR substring3_matches.
+ * If not is set in the _head_ of the PATTERN_S, it is a NOT of the
+ * whole pattern, that is,
+ * NOT (substring1_matches OR substring2_matches OR substring3_matches).
+ * The not variable is not meaningful except in the head member of the
+ * PATTERN_S list.
+ */
+typedef struct pattern_s {
+ int not; /* NOT of whole pattern */
+ char *substring;
+ struct pattern_s *next;
+} PATTERN_S;
+
+/*
+ * List of these is a list of arbitrary freetext headers and patterns.
+ * This may be part of a pattern group.
+ * The isemptyval bit is to keep track of the difference between an arb
+ * header with no value set and one with the empty value "" set. For the
+ * other builtin headers this difference is kept track of by whether or
+ * not the header is in the config file at all or not. Here we want to
+ * be able to add a header to the config file without necessarily giving
+ * it a value.
+ */
+typedef struct arbhdr_s {
+ char *field;
+ PATTERN_S *p;
+ int isemptyval;
+ struct arbhdr_s *next;
+} ARBHDR_S;
+
+/*
+ * A list of intervals of integers.
+ */
+typedef struct intvl_s {
+ long imin, imax;
+ struct intvl_s *next;
+} INTVL_S;
+
+/*
+ * A Pattern group gives characteristics of an envelope to match against. Any of
+ * the characteristics (to, from, ...) which is non-null must match for the
+ * whole thing to be considered a match. That is, it is an AND of all the
+ * non-null members.
+ */
+typedef struct patgrp_s {
+ char *nick;
+ char *comment; /* for user, not used for anything */
+ PATTERN_S *to,
+ *from,
+ *sender,
+ *cc,
+ *recip,
+ *partic,
+ *news,
+ *subj,
+ *alltext,
+ *bodytext,
+ *keyword,
+ *charsets;
+ STRLIST_S *charsets_list; /* used for efficiency, computed from charset */
+ ARBHDR_S *arbhdr; /* list of arbitrary hdrnames and patterns */
+ int fldr_type; /* see FLDR_* below */
+ PATTERN_S *folder; /* folder if type FLDR_SPECIFIC */
+ int inabook; /* see IAB_* below */
+ PATTERN_S *abooks;
+ int do_score;
+ INTVL_S *score;
+ int do_age;
+ INTVL_S *age; /* ages are in days */
+ int do_size;
+ INTVL_S *size;
+ int age_uses_sentdate; /* on or off */
+ int do_cat;
+ char **category_cmd;
+ INTVL_S *cat;
+ long cat_lim; /* -1 no limit 0 only headers */
+ int bogus; /* patgrp contains unknown stuff */
+ int stat_new, /* msg status is New (Unseen) */
+ stat_rec, /* msg status is Recent */
+ stat_del, /* msg status is Deleted */
+ stat_imp, /* msg is flagged Important */
+ stat_ans, /* msg is flagged Answered */
+ stat_8bitsubj, /* subject contains 8bit chars */
+ stat_bom, /* this is first pine run of the month */
+ stat_boy; /* this is first pine run of the year */
+} PATGRP_S;
+
+#define FLDR_ANY 0
+#define FLDR_NEWS 1
+#define FLDR_EMAIL 2
+#define FLDR_SPECIFIC 3
+
+#define FLDR_DEFL FLDR_EMAIL
+
+
+#define IAB_EITHER 0x0 /* don't care if in or not */
+
+#define IAB_TYPE_MASK 0xf
+#define IAB_YES 0x1 /* addresses in any abook */
+#define IAB_NO 0x2 /* " not " */
+#define IAB_SPEC_YES 0x3 /* addresses in specific abooks */
+#define IAB_SPEC_NO 0x4
+
+/*
+ * Warning about reply-to. We're using the c-client envelope reply-to which
+ * means if there isn't a real reply-to it uses the From!
+ */
+#define IAB_ADDR_MASK 0xff0
+#define IAB_FROM 0x10 /* from address included in list */
+#define IAB_REPLYTO 0x20 /* reply-to address included in list */
+#define IAB_SENDER 0x40 /* sender address included in list */
+#define IAB_TO 0x80 /* to address included in list */
+#define IAB_CC 0x100 /* cc address included in list */
+
+#define IAB_DEFL IAB_EITHER
+
+
+#define FILTER_STATE 0
+#define FILTER_KILL 1
+#define FILTER_FOLDER 2
+
+/*
+ * For the Status parts of a PATGRP_S. For example, stat_del is Deleted
+ * status. User sets EITHER means they don't care, it always matches.
+ * YES means it must be deleted to match. NO means it must not be deleted.
+ */
+#define PAT_STAT_EITHER 0 /* we don't care which, yes or no */
+#define PAT_STAT_YES 1 /* yes, this status is true */
+#define PAT_STAT_NO 2 /* no, this status is not true */
+
+/*
+ * For the State setting part of a filter action
+ */
+#define ACT_STAT_LEAVE 0 /* leave msg state alone */
+#define ACT_STAT_SET 1 /* set this part of msg state */
+#define ACT_STAT_CLEAR 2 /* clear this part of msg state */
+
+typedef struct action_s {
+ unsigned is_a_role:1; /* this is a role action */
+ unsigned is_a_incol:1; /* this is an index color action */
+ unsigned is_a_score:1; /* this is a score setting action */
+ unsigned is_a_filter:1; /* this is a filter action */
+ unsigned is_a_other:1; /* this is a miscellaneous action */
+ unsigned is_a_srch:1; /* this is for Select cmd, no action */
+ unsigned bogus:1; /* action contains unknown stuff */
+ unsigned been_here_before:1; /* inheritance loop prevention */
+ /* --- These are for roles --- */
+ ADDRESS *from; /* value to set for From */
+ ADDRESS *replyto; /* value to set for Reply-To */
+ char **cstm; /* custom headers */
+ char **smtp; /* custom SMTP server for this role */
+ char **nntp; /* custom NNTP server for this role */
+ char *fcc; /* value to set for Fcc */
+ char *litsig; /* value to set Literal Signature */
+ char *sig; /* value to set for Sig File */
+ char *template; /* value to set for Template */
+ char *nick; /* value to set for Nickname */
+ int repl_type; /* see ROLE_REPL_* below */
+ int forw_type; /* see ROLE_FORW_* below */
+ int comp_type; /* see ROLE_COMP_* below */
+ char *inherit_nick; /* pattern we inherit actions from */
+ /* --- This is for indexcoloring --- */
+ COLOR_PAIR *incol; /* colors for index line */
+ /* --- This is for scoring --- */
+ long scoreval;
+ HEADER_TOK_S *scorevalhdrtok;
+ /* --- These are for filtering --- */
+ int kill;
+ long state_setting_bits;
+ PATTERN_S *keyword_set; /* set these keywords */
+ PATTERN_S *keyword_clr; /* clear these keywords */
+ PATTERN_S *folder; /* folders to recv. filtered mail */
+ int move_only_if_not_deleted; /* on or off */
+ int non_terminating; /* on or off */
+ /* --- These are for other --- */
+ /* sort order of folder */
+ unsigned sort_is_set:1;
+ SortOrder sortorder; /* sorting order */
+ int revsort; /* whether or not to reverse sort */
+ /* Index format of folder */
+ char *index_format;
+ unsigned startup_rule;
+} ACTION_S;
+
+/* flags for first_pattern..., set_role_from_msg, and confirm_role() */
+#define PAT_CLOSED 0x00000000 /* closed */
+#define PAT_OPENED 0x00000001 /* opened successfully */
+#define PAT_OPEN_FAILED 0x00000002
+#define PAT_USE_CURRENT 0x00000010 /* use current_val to set up pattern */
+#define PAT_USE_CHANGED 0x00000020 /* use changed_val to set up pattern */
+#define PAT_USE_MAIN 0x00000040 /* use main_user_val */
+#define PAT_USE_POST 0x00000080 /* use post_user_val */
+#define ROLE_COMPOSE 0x00000100 /* roles with compose value != NO */
+#define ROLE_REPLY 0x00000200 /* roles with reply value != NO */
+#define ROLE_FORWARD 0x00000400 /* roles with forward value != NO */
+#define ROLE_INCOL 0x00000800 /* patterns with non-Normal colors */
+#define ROLE_SCORE 0x00001000 /* patterns with non-zero scorevals */
+#define ROLE_DO_ROLES 0x00010000 /* role patterns */
+#define ROLE_DO_INCOLS 0x00020000 /* index line color patterns */
+#define ROLE_DO_SCORES 0x00040000 /* set score patterns */
+#define ROLE_DO_FILTER 0x00080000 /* filter patterns */
+#define ROLE_DO_OTHER 0x00100000 /* miscellaneous patterns */
+#define ROLE_DO_SRCH 0x00200000 /* index line color patterns */
+#define ROLE_OLD_PAT 0x00400000 /* old patterns variable */
+#define ROLE_OLD_FILT 0x00800000 /* old patterns-filters variable */
+#define ROLE_OLD_SCORE 0x01000000 /* old patterns-scores variable */
+#define ROLE_CHANGES 0x02000000 /* start editing with changes
+ already registered */
+
+#define PAT_OPEN_MASK 0x0000000f
+#define PAT_USE_MASK 0x000000f0
+#define ROLE_MASK 0x00ffff00
+
+#define ROLE_REPL_NO 0 /* never use for reply */
+#define ROLE_REPL_YES 1 /* use for reply with confirmation */
+#define ROLE_REPL_NOCONF 2 /* use for reply without confirmation */
+#define ROLE_FORW_NO 0 /* ... forward ... */
+#define ROLE_FORW_YES 1
+#define ROLE_FORW_NOCONF 2
+#define ROLE_COMP_NO 0 /* ... compose ... */
+#define ROLE_COMP_YES 1
+#define ROLE_COMP_NOCONF 2
+
+#define ROLE_REPL_DEFL ROLE_REPL_YES /* default reply value */
+#define ROLE_FORW_DEFL ROLE_FORW_YES /* default forward value */
+#define ROLE_COMP_DEFL ROLE_COMP_NO /* default compose value */
+#define ROLE_NOTAROLE_DEFL ROLE_COMP_NO
+
+#define INTVL_INF (2147483646L)
+#define INTVL_UNDEF (INTVL_INF + 1L)
+#define SCORE_UNDEF INTVL_UNDEF
+#define SCORE_MIN (-100)
+#define SCORE_MAX (100)
+#define SCOREUSE_GET 0x000
+#define SCOREUSE_INVALID 0x001 /* will recalculate scores_in_use next time */
+#define SCOREUSE_ROLES 0x010 /* scores are used for roles */
+#define SCOREUSE_INCOLS 0x020 /* scores are used for index line colors */
+#define SCOREUSE_FILTERS 0x040 /* scores are used for filters */
+#define SCOREUSE_OTHER 0x080 /* scores are used for miscellaneous stuff */
+#define SCOREUSE_INDEX 0x100 /* scores are used in index-format */
+#define SCOREUSE_STATEDEP 0x200 /* scores depend on message state */
+
+/*
+ * A message is compared with a pattern group to see if it matches.
+ * If it does match, then there are actions which are taken.
+ */
+typedef struct pat_s {
+ PATGRP_S *patgrp;
+ ACTION_S *action;
+ struct pat_line_s *patline; /* pat_line that goes with this pat */
+ char *raw;
+ unsigned inherit:1;
+ struct pat_s *next;
+ struct pat_s *prev;
+} PAT_S;
+
+typedef enum {TypeNotSet = 0, Literal, File, Inherit} PAT_TYPE;
+
+/*
+ * There's one of these for each line in the pinerc variable.
+ * Normal type=Literal patterns have a patline with both first and last
+ * pointing to the pattern. Type File has one patline for the file and first
+ * and last point to the first and last patterns in the file.
+ * The patterns aren't linked into one giant list, the patlines are.
+ * To traverse all the patterns you have to go through the patline list
+ * and then for each patline go from first to last through the patterns.
+ * That's what next_pattern and friends do.
+ */
+typedef struct pat_line_s {
+ PAT_TYPE type;
+ PAT_S *first; /* 1st pattern in list belonging to this line */
+ PAT_S *last;
+ char *filename; /* If type File, the filename */
+ char *filepath;
+ unsigned readonly:1;
+ unsigned dirty:1; /* needs to be written back to storage */
+ struct pat_line_s *next;
+ struct pat_line_s *prev;
+} PAT_LINE_S;
+
+typedef struct pat_handle {
+ PAT_LINE_S *patlinehead; /* list of in-core, parsed pat lines */
+ unsigned dirtypinerc:1; /* needs to be written */
+} PAT_HANDLE;
+
+typedef struct pat_state {
+ long rflags;
+ int cur_rflag_num;
+ PAT_LINE_S *patlinecurrent;
+ PAT_S *patcurrent; /* current pat within patline */
+} PAT_STATE;
+
+#define PATTERN_MAGIC "P#Pats"
+#define PATTERN_FILE_VERS "01"
+
+
+/*
+ * This is a little dangerous. We're passing flags to match_pattern and
+ * peeling some of them off for our own use while passing the rest on
+ * to mail_search_full. So we need to define ours so they don't overlap
+ * with the c-client flags that can be passed to mail_search_full.
+ * We could formalize it with mrc.
+ */
+#define MP_IN_CCLIENT_CB 0x10000 /* we're in a c-client callback! */
+#define MP_NOT 0x20000 /* use ! of patgrp for search */
+
+
+/* match_pattern_folder_specific flags */
+#define FOR_PATTERN 0x01
+#define FOR_FILTER 0x02
+#define FOR_OPTIONSCREEN 0x04
+
+
+extern PAT_HANDLE **cur_pat_h;
+
+
+/* exported protoypes */
+void role_process_filters(void);
+int add_to_pattern(PAT_S *, long);
+char *add_pat_escapes(char *);
+char *remove_pat_escapes(char *);
+char *add_roletake_escapes(char *);
+char *add_comma_escapes(char *);
+void set_pathandle(long);
+void close_every_pattern(void);
+void close_patterns(long);
+int nonempty_patterns(long, PAT_STATE *);
+int any_patterns(long, PAT_STATE *);
+int edit_pattern(PAT_S *, int, long);
+int add_pattern(PAT_S *, long);
+int delete_pattern(int, long);
+int shuffle_pattern(int, int, long);
+PAT_LINE_S *parse_pat_file(char *);
+INTVL_S *parse_intvl(char *);
+char *stringform_of_intvl(INTVL_S *);
+char *hdrtok_to_stringform(HEADER_TOK_S *);
+HEADER_TOK_S *stringform_to_hdrtok(char *);
+char *hdrtok_to_config(HEADER_TOK_S *);
+HEADER_TOK_S *config_to_hdrtok(char *);
+int scores_are_used(int);
+int patgrp_depends_on_state(PATGRP_S *);
+int patgrp_depends_on_active_state(PATGRP_S *);
+PATTERN_S *parse_pattern(char *, char *, int);
+PATTERN_S *string_to_pattern(char *);
+char *pattern_to_string(PATTERN_S *);
+char *pattern_to_config(PATTERN_S *);
+PATTERN_S *config_to_pattern(char *);
+PATTERN_S *editlist_to_pattern(char **);
+char **pattern_to_editlist(PATTERN_S *);
+PATGRP_S *nick_to_patgrp(char *, int);
+PAT_S *first_pattern(PAT_STATE *);
+PAT_S *last_pattern(PAT_STATE *);
+PAT_S *prev_pattern(PAT_STATE *);
+PAT_S *next_pattern(PAT_STATE *);
+int write_patterns(long);
+void convert_statebits_to_vals(long, int *, int *, int *, int *);
+int match_pattern(PATGRP_S *, MAILSTREAM *, SEARCHSET *,char *,
+ long (*)(MAILSTREAM *, long), long);
+void find_8bitsubj_in_messages(MAILSTREAM *, SEARCHSET *, int, int);
+void find_charsets_in_messages(MAILSTREAM *, SEARCHSET *, PATGRP_S *, int);
+int compare_strlists_for_match(STRLIST_S *, STRLIST_S *);
+int match_pattern_folder(PATGRP_S *, MAILSTREAM *);
+int match_pattern_folder_specific(PATTERN_S *, MAILSTREAM *, int);
+SEARCHPGM *match_pattern_srchpgm(PATGRP_S *, MAILSTREAM *, SEARCHSET *);
+void calc_extra_hdrs(void);
+char *get_extra_hdrs(void);
+void free_extra_hdrs(void);
+void free_pat(PAT_S **);
+void free_pattern(PATTERN_S **);
+void free_action(ACTION_S **);
+PAT_S *copy_pat(PAT_S *);
+PATGRP_S *copy_patgrp(PATGRP_S *);
+ACTION_S *copy_action(ACTION_S *);
+ACTION_S *combine_inherited_role(ACTION_S *);
+void mail_expunge_prefilter(MAILSTREAM *, int);
+void process_filter_patterns(MAILSTREAM *, MSGNO_S *, long);
+void reprocess_filter_patterns(MAILSTREAM *, MSGNO_S *, int);
+int trivial_patgrp(PATGRP_S *);
+void free_patgrp(PATGRP_S **);
+void free_patline(PAT_LINE_S **patline);
+int some_filter_depends_on_active_state(void);
+void delete_filtered_msgs(MAILSTREAM *);
+void free_intvl(INTVL_S **);
+void free_arbhdr(ARBHDR_S **);
+
+
+#endif /* PITH_PATTERN_INCLUDED */
diff --git a/pith/pine.hlp b/pith/pine.hlp
new file mode 100644
index 00000000..3af1062e
--- /dev/null
+++ b/pith/pine.hlp
@@ -0,0 +1,35307 @@
+# $Id: pine.hlp 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+#
+# T H E A L P I N E M E S S A G E S Y S T E M
+#
+#/* ========================================================================
+# * Copyright 2006-2008 University of Washington
+# * Copyright 2013 Eduardo Chappa
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * ========================================================================
+# */
+#
+ Help text for the Alpine mailer
+
+This file is in a format created to be turned into text strings in a C
+program.
+
+There are two shell scripts that run on this. Cmplhelp.sh is the
+first and turns this into a C file (helptext.c) of text strings that
+are compiled and linked. The other program, cmplhlp2.sh, turns this
+into a .h file (helptext.h) with extern string definitions of the
+strings in the .c file. The code that actually processes these files
+while alpine is running is in help.c
+
+The lines with "===== xxxx ====" divide the different help screens. The
+xxx is the name of the variable that strings will be put in, which are
+also declared in helptext.h.
+
+# is a comment
+
+Help text screen text can be either plain text OR HTML. The latter is
+denoted by the first line starting with "<HTML>". The former is simply
+displayed as it's formatted here.
+
+HTML is limited to simple formatting ala HTML 2.0. No forms, or tables.
+In addition a small set of tools are are available to customize the HTML
+screen's text:
+
+1a) Default and function key bindings are separated like this:
+
+<!--chtml if pinemode="function_key"-->
+ Function key bindings here
+<!--chtml else-->
+ Default key bindings here
+<!--chtml endif-->
+
+
+1b) A way to distinguish HTML text that is to be displayed when
+ pine is running vs. when the text is served up outside Alpine
+ (someday) can be done via:
+
+<!--chtml if pinemode="running"-->
+ Text displayed when viewed within a running pine session
+<!--chtml else-->
+ Text displayed when HTML viewed outside pine (using chtml aware server)
+<!--chtml endif-->
+
+1c) A way to distinguish HTML text that is to be displayed under
+ PC-Alpine vs. not is available via:
+
+<!--chtml if pinemode="os_windows"-->
+ Text displayed under PC-Alpine
+<!--chtml else-->
+ Text displayed otherwise
+<!--chtml endif-->
+
+WARNING ABOUT CHTML "if-else-endif" CLAUSES: They don't nest.
+
+2a) Several "server side include" commented elements are supported:
+
+<!--#include file="textfile"-->
+
+The file "textfile" will be inserted into the HTML text directly.
+Alpine does no formatting of the text. At some point we might want to
+look at the first line for <HTML> but not today.
+
+2b) Various bits of Alpine's running state can be inserted into the
+HTML text as well using the special comment:
+
+<!--#echo var="variable"-->
+
+Where "variable" is one of either:
+
+ ALPINE_VERSION
+ ALPINE_REVISION
+ ALPINE_COMPILE_DATE
+ ALPINE_TODAYS_DATE
+ C_CLIENT_VERSION
+ _LOCAL_FULLNAME_
+ _LOCAL_ADDRESS_
+ _BUGS_FULLNAME_
+ _BUGS_ADDRESS_
+ CURRENT_DIR
+ HOME_DIR
+ PINE_CONF_PATH
+ PINE_CONF_FIXED_PATH
+ PINE_INFO_PATH
+ MAIL_SPOOL_PATH
+ MAIL_SPOOL_LOCK_PATH
+ VAR_<VARNAME> - where <VARNAME> is config variable name
+ FEAT_<FEATURENAME> - where <FEATURENAME> is config feature name
+
+3) The URL scheme "X-Alpine-Gripe:" is available to insert links to
+ pine's composer such that various debugging data can be attached to the
+ message. Aside from normal email addresses, this can be set to
+ either "_LOCAL_ADDRESS_" for the configured local help address, or
+ "_BUGS_ADDRESS_" for the configured local bug reporting address.
+ Aside from the special tokens above, the default behavior only differs
+ from "mailto:" by the insertion of a special Subject: prefix that
+ consists of a randomly-generated token for tracking purposes.
+ Several optional parameters can be included to control what is
+ attached or offered for attachment to the message:
+
+ ?config -- Automatically attaches the user's configuration
+ information to the trouble report
+ ?keys -- Automatically attaches the user's most recent
+ keystrokes
+ ?curmsg -- Causes the user to get an offer to attach the
+ current message to the trouble report
+ ?local -- Automatically attaches the result of the script
+ defined by VAR_BUGS_EXTRAS
+
+For HTML-format sections, the help screen dividers "===== xxxx ====" must
+contain one and only one space after the first and before the second set of
+equal signs.
+
+Note to authors of this file: to mark sections that need further revision,
+please use the text string on the following line consistently so that it is
+easy to find those places again in this file:
+*revision needed*
+
+NOTE: Several sections of help text which weren't being used were removed
+at RCS version number 4.122. In particular, there were some placeholders
+with help text for the config screen and the composer that didn't have any
+reasonable place to be called from.
+Dummy change to get revision in pine.hlp
+
+============= h_revision =================
+$Id: pine.hlp 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+============= h_news =================
+<HTML>
+<HEAD>
+<TITLE>RELEASE NOTES for Alpine</TITLE>
+</HEAD>
+<BODY>
+<H1>Alpine Release Notes</H1>
+<DIV ALIGN=CENTER>
+Version <!--#echo var="ALPINE_VERSION"-->(<!--#echo var="ALPINE_REVISION"-->)
+<BR>
+<!--chtml if pinemode="running"-->
+(built <!--#echo var=ALPINE_COMPILE_DATE-->)
+<!--chtml endif-->
+<BR>Copyright 2006-2008 University of Washington
+<BR>Copyright 2013 Eduardo Chappa
+</DIV>
+
+<P>
+Alpine is an &quot;Alternatively Licensed Program for Internet
+News and Email&quot; produced until 2008 by the University of Washington.
+It is intended to be an easy-to-use program for
+sending, receiving, and filing Internet electronic mail messages and
+bulletin board (Netnews) messages. Alpine is designed to run on a wide
+variety of Unix&reg; operating systems. A version for Microsoft Windows&reg;
+is available as is a world wide web based version designed to run under the
+Apache web server.
+
+
+<H2>New in Alpine <!--#echo var="ALPINE_VERSION"-->(<!--#echo var="ALPINE_REVISION"-->)</H2>
+
+Version <!--#echo var="ALPINE_VERSION"-->(<!--#echo var="ALPINE_REVISION"-->)
+addresses bugs found in previous releases and has a few additions as well.
+
+<P>
+Additions include:
+<P>
+
+<UL>
+ <LI> Quota report for IMAP folders that support it (press the &quot;@&quot; command in the index screen of such folder).
+ <LI> Search a folder for the content of any header with the &quot;;&quot; command.
+ <LI> Foreign characters are decoded correctly in IMAP folders.
+ <LI> Question about breaking connection to slow servers includes their name.
+ <LI> Internal x-alpine-help: resource locator for sending links to internal help.
+ <LI> OpenSuse: Alpine find location of OpenSSL certificates.
+ <LI> Cygwin: Alpine builds without need of patch.
+ <LI> Recognition of proper mime type for docx, xlsx, and pptx files.
+ <LI> When composing a message, Alpine will create a new thread when the subject is erased.
+ <LI> Add support for strong encryption of password file when S/MIME is built in.
+</UL>
+
+<P>
+Bugs that have been addressed include:
+<P>
+
+<UL>
+ <LI> Alpine will close a folder after confirming with user their intention and not reopen it.
+ <LI> Double allocation of memory in Pico.
+ <LI> Alpine does not give warning of message sent and posted upon receipt by email of message posted in newsgroup.
+ <LI> Handling of STYLE html parameter may make Alpine not display the content of a message.
+ <LI> Not recognition of environment variables in some options.
+ <LI> Not display of login prompt during initial keystrokes.
+ <LI> justification of long urls breaks them.
+ <LI> Incorrect New Mail message when envelope is not available.
+ <LI> Inorrect display of PREFDATE, PREFDATETIME and PREFTIME tokens.
+ <LI> Crash when resizing the screen after display of LDAP search.
+ <LI> Crash when redrawing screen while opening a remote folder collection.
+ <LI> Infinite loop in scrolltool function during notification of new mail.
+ <LI> No repaint of the screen was done when the SMARTDATE token is used in the index screen after midnight.
+ <LI> No display of signed and encrypted S/MIME messages.
+ <LI> Alpine will not build with OpenSSL.
+ <LI> Crash for double locking in calls to c-client.
+ <LI> Bad recognition of mime-encoded text may make Alpine not print the subject of a message.
+ <LI> Ignore the references header when threading messages
+ <LI> No update of colors in index screen after update to addressbook.
+</UL>
+
+<P>
+Version 2.01 addresses bugs found in previous releases and has a few
+additions as well.
+
+<P>
+Additions include:
+<P>
+
+<UL>
+ <LI> Fixed non-ASCII web alpine handling
+ <LI> Added web alpine help.
+ <LI> Allow web alpine inbox on second IMAP server.
+ <LI> Allow web alpine config reset after bad inbox-path gets set.
+ <LI> Added web alpine ability to create group contact from contact list members.
+ <LI> Backed out web alpine coercing of default sort-key of arrival to date/reverse.
+ <LI> Tidied up web alpine script layout.
+ <LI> Fixed web alpine status message ordering
+ <LI> Added web alpine Fcc setting via Contacts in Compose
+ <LI> Fixed web alpine autodraft attachment issues
+ <LI> Fixed web alpine problems with recent count maintenance
+ <LI> Fixed web alpine newmail arrival display in message list
+ <LI> Added web alpine confirmation to folder create for Move/Copy
+ <LI> Added web alpine user-domain support
+ <LI> Fixed web alpine to support INBOX-within-a-collection deletion
+</UL>
+
+<P>
+Bugs that have been addressed include:
+<P>
+
+<UL>
+ <LI> In web alpine fixed delete all selected within a search result to reorient
+ correctly to whole-mailbox message list.
+ <LI> Fixed web alpine delete in view page to be sensitive to sort
+ <LI> Fixed web alpine open of folder within directory from folder manager page.
+ <LI> Fixed web alpine open of folder within directory from left column's recent cache.
+ <LI> Fixed web alpine problems with spaces in special folder names like Drafts
+ <LI> Fixed web alpine adding contacts from message list and view
+ <LI> Fixed web alpine create of non-existent fcc
+ <LI> Remove mistakenly left debugger statement in web alpine javascript.
+ <LI> Some UNIX alpine build problems fixed
+ <LI> Crash in pico and pilot when nl_langinfo returned something unrecognizable
+ or NULL. Add recognition of "646" to nl_langinfo wrapper. This is returned
+ by locale charmap call on some Solaris 8 systems.
+ <LI> MacOS Keychain logins were not working correctly with generic host names, like
+ imap.gmail.com, as opposed to specific instances like rx-in-209.google.com, causing
+ new password requests when not needed
+ <LI> Possible crash in WhereIs command while in FOLDER LIST when cursor is located on the
+ last folder in the list
+ <LI> Change to S/MIME get_x509_subject_email so that X509v3 alternative names are
+ looked for along with the email address
+ <LI> Changes to configure to get spellcheck options with work with arguments.
+ <LI> Add change from Mark Crispin of panda.com to at least minimally handle non-ascii hostname
+ returned by gethostname (iPhone can do this)
+ <LI> Fixed a bug that prevents a filter that moves a message into a local folder
+ from also setting the DELETE flag in that moved message. Fix from Eduardo Chappa.
+ <LI> Changed size of shellpath in open_system_pipe from 32 to MAXPATH. Fix from
+ Jake Scott of marganstanley.com.
+ <LI> Buffer overflow bug in c-client's tmail/dmail, fix from Mark Crispin. This
+ is not used in alpine.
+ <LI> Imapd server crash from unguarded fs_give in IDLE code, fix from Crispin.
+ Apparently this causes RIM Blackberry BIS service problems. This is not
+ used in alpine.
+ <LI> Tmail uninitialized pointer fix from Neil Hoggarth. Not used in alpine.
+ <LI> Buffer overflow possibility in RFC822BUFFER routines in c-client library.
+ Fix from Ludwig Nussel of SUSE and from Crispin.
+ <LI> Include whole filename in export filename history
+ <LI> Fix display bug in pico when Replace command is canceled. Fix from Eduardo Chappa.
+</UL>
+
+<P>
+Version 2.00
+addressed bugs found in previous releases and had a few additions as well.
+<P>
+Additions included:
+<P>
+
+<UL>
+ <LI> Redesigned Web Alpine interface
+ <LI> Experimental <A HREF="h_mainhelp_smime">S/MIME support</A> added
+ in UNIX versions of Alpine
+ <LI> Attempt to include the attachment filename as part of the name of the
+ temporary file used when viewing an attachment with an external program.
+ Add some randomness to that filename to make it hard to predict the filename.
+ Add a filename extension, usually derived from the type/subtype, to the
+ temporary filename. This was previously done only on Windows and MacOS X.
+ <LI> Enhance address completion in the composer (TAB command) so that it looks
+ through nicknames, fullnames, and addresses from the address book; addresses
+ from the message being viewed, if any; and the results from
+ <A HREF="h_direct_config">LDAP Directory Server</A>
+ lookups for all of the defined directory servers that have the
+ <A HREF="h_config_ldap_opts_impl">&quot;Use-Implicitly-From-Composer&quot;</A>
+ feature set.
+ <LI> Make the default character set setting more liberal in what it will accept
+ from the UNIX nl_langinfo call and the various values of LANG seen in the wild
+ <LI> Remove the Alpine revision number from the titlebar in released versions
+ while leaving it there in snapshot versions
+ <LI> Add a <A HREF="h_config_quell_asterisks">feature</A> to suppress the
+ display of asterisks when you type a password for Alpine
+ <LI> Add line wrapping when displaying <EM>PRE</EM> formatted sections of HTML
+ <LI> When the
+ <A HREF="h_config_dates_to_local"><!--#echo var="FEAT_convert-dates-to-localtime"--></A>
+ feature is turned on convert not only the dates in the index screen but also
+ the dates in the MESSAGE VIEW
+</UL>
+
+<P>
+Bugs addressed in the 2.00 release included:
+<P>
+
+<UL>
+ <LI> Crash when using tab-completion for selecting a Save filename
+ <LI> Make Web Alpine help text images relative for more portability
+ <LI> Fixed attach save of html parts in Web Alpine
+ <LI> Viewing, printing, exporting, replying, and bouncing of message
+ digests was broken. Replying and bouncing should not have been
+ allowed at all for digests. It would be nice to have a more standard
+ index-like view of a message digest but that has not been addressed
+ with this minor bug fix.
+ <LI> Adjust wrapping of HTML messages so that the margins specified by
+ <A HREF="h_config_viewer_margin_left"><!--#echo var="VAR_viewer-margin-left"--></A> and
+ <A HREF="h_config_viewer_margin_right"><!--#echo var="VAR_viewer-margin-right"--></A>
+ are observed correctly
+ <LI> Interrupt of WhereIs command in index was broken
+ <LI> The <A HREF="h_config_unk_char_set"><!--#echo var="VAR_unknown-character-set"--></A>
+ option did not work correctly interpreting unknown characters in message headers
+ <LI> Long address lines could cause blank continuation lines
+ <LI> Save to a local default INBOX failed if the primary collection was also local,
+ which it is by default. The save went to ~/mail/inbox instead.
+ <LI> Make a default save folder of &quot;inbox&quot; always mean the real
+ inbox, not the inbox in the primary collection
+ <LI> Address book entries with lots of addresses would be truncated when
+ entered in the composer with a screen size wider than 270 or so charcters
+ <LI> Some fields in the index screen were truncated when the screen width was
+ wider than 256 characters
+ <LI> Crash when TABing to next folder, the next folder with new mail is a POP
+ folder, and there is a more than 10 minute pause between typing the TAB
+ and typing the Yes
+</UL>
+
+<P>
+Version 1.10(962)
+addressed bugs found in previous releases and had a few additions as well.
+<P>
+Additions included:
+<P>
+
+<UL>
+ <LI> Add the possibility of setting a default role
+ (see <A HREF="h_role_select">Roles Screen</A>)
+ which may be convenient if your work flow involves acting in one
+ role for a while then switching to another role and staying in the
+ new role for another period of time
+ <LI> When Saving and the IMAP server problem &quot;Message to save shrank!&quot;
+ is encountered, ask the user if he or she wants to continue with the
+ risky Save anyway instead of aborting. This may be helpful if your
+ IMAP server is broken in this way but be aware that it is possible there
+ was a real error instead of just a broken server implementation.
+ <LI> Some configure adjustments for Kerberos detection and
+ for SCO OpenServer 5 support
+ <LI> Hide INBOX in a collection if it also appears as an
+ <A HREF="h_config_enable_incoming">Incoming Folder</A>
+ <LI> Show asterisks for feedback when the user is typing a password
+ <LI> Performance improvement for threading of large folders
+ <LI> Previously, the search used to find
+ Pattern matches containing To patterns searched for both To
+ and Resent-To headers. The relatively complicated search this
+ produces causes problems when using some deficient IMAP servers.
+ Now the default is to look only for To headers and ignore the
+ presence of Resent-To. The previous behavior may be restored
+ with the <A HREF="h_config_use_resentto"><!--#echo var="FEAT_use-resent-to-in-rules"--></A> feature.
+ <LI> Add an
+ <A HREF="h_config_unk_char_set"><!--#echo var="VAR_unknown-character-set"--></A>
+ to help with reading malformed unlabeled messages
+ <LI><A HREF="h_config_suppress_user_agent"><!--#echo var="FEAT_suppress-user-agent-when-sending"--></A> option added
+ <LI> Map some Shift-LeftArrow escape sequences to LeftArrow
+ <LI> Add feature <A HREF="h_config_warn_if_fcc_blank"><!--#echo var="FEAT_warn-if-blank-fcc"--></A>
+</UL>
+
+<P>
+Bugs addressed in the 1.10(962) release included:
+<P>
+
+<UL>
+ <LI> Crash when encountering certain errors from an SMTP server
+ <LI> Crash in composer caused by overflow in replace_pat()
+ <LI> Hang when authenticating to an SMTP server that fails with a
+ &quot;connection disconnected&quot; error
+ <LI> Bug in handling of trailing tab character in flowed text
+ <LI> Security enhancement for mailcap parameter substitution
+ <LI> <A HREF="h_config_strip_sigdashes"><!--#echo var="FEAT_strip-from-sigdashes-on-reply"--></A>
+ did not work if the message being replied to was not flowed text
+ and <A HREF="h_config_quell_flowed_text"><!--#echo var="FEAT_quell-flowed-text"--></A>
+ was not turned on
+ <LI> Don't allow printer to be changed through hidden config screen
+ if system administrator didn't want it to be allowed
+ <LI> Attempts are sometimes made to set the Forwarded flag when alpine
+ should know that it won't work, causing error messages to appear
+ <LI> A <A HREF="h_config_reply_indent_string"><!--#echo var="VAR_reply-indent-string"--></A>
+ of double-quote double-quote didn't work right
+ <LI> Quoting wasn't being done to protect special characters from the
+ MacOS X shell when
+ <A HREF="h_config_browser"><!--#echo var="VAR_url-viewers"--></A>
+ was not defined
+ <LI> On MacOS X message attachments should be shown internally instead of
+ being shown using the Mail application
+ <LI> When replying to a message with a charset of X-UNKNOWN Alpine would
+ sometimes set the outgoing charset to X-UNKNOWN, making the result
+ unreadable
+ <LI> When the sending of a message failed lines with leading spaces had one
+ additional space inserted in each of those lines when the user
+ was returned to the composer
+ <LI> The <A HREF="h_index_cmd_whereis">WhereIs</A> command missed some index lines
+ that contained non-ascii characters because it was truncating the
+ line being searched so that it was shorter than what was visible on
+ the screen
+ <LI> When composing, an attachment with a long name that causes wrapping in
+ just the wrong place would generate an error and cause the send
+ of the attachment to fail
+ <LI> After calling the file browser to attach a file in the composer, a resize
+ of the window when back in the composer would redraw the last screen that
+ had been shown in the browser instead of the current composer screen
+ <LI> Possible crash in index screen when encountering unparseable addresses
+ or when using one of the PRIORITY tokens or the HEADER token in the
+ <a href="h_config_index_format"><!--#echo var="VAR_index-format"--></a>
+ <LI> Problems with Header Color editing if the configuration option
+ <a href="h_config_customhdr_color"><!--#echo var="VAR_viewer-hdr-colors"--></a>
+ was inadvertently changed to the Empty Value in the hidden config screen
+ <LI> When resuming the final postponed message from an Exchange server the user
+ could get a certificate validation failure because alpine was trying
+ to validate the canonical name of the folder instead of the name the
+ user used
+ <LI> Windows line endings in a mimetypes file on a Unix system cause a
+ failure to find a match
+ <LI> Make matching of extension names case independent in mimetypes files
+ <LI> Windows dialog boxes for entering text were not working correctly
+ <LI> Replying to or Forwarding multipart/alternative messages which had a
+ single text/html part did not work well
+ <LI> Printing the print confirmation page caused a crash
+ <LI> A To line with a long, quoted personal name could display incorrectly
+ if it was close to the same width as the screen
+ <LI> When <A HREF="h_config_enable_incoming_checking"><!--#echo var="FEAT_enable-incoming-folders-checking"--></A>
+ and <A HREF="h_config_incoming_checking_total"><!--#echo var="FEAT_incoming-checking-includes-total"--></A>
+ are turned on hide (0/0) when the folder is empty
+ <LI> Folder completion while Saving didn't work if the collection being
+ saved to was the local home directory
+</UL>
+
+<P>
+Version 1.00
+was an evolutionary release based on
+<A HREF="http://www.washington.edu/pine/">Pine</A>, which was also
+developed at the University of Washington.
+It is upwards-compatible for existing Pine users.
+
+<P>
+Changes included:
+<P>
+<UL>
+ <LI> Ground-up reorganization of source code around addition
+ of "pith/" core routine library.
+ <LI> Fundamental improvement in Alpine's internal text handling, which
+ is now based exclusively on Unicode. This allows displaying incoming
+ messages and producing outgoing messages in many different languages.
+ <LI> Ground-up reorganization of build and install procedures
+ based on GNU Build System's autotools. NOTE, the included IMAP library
+ build is not based on autotools, so some features will not work. However,
+ it should get built automatically during the Alpine build process.
+ <LI> Web-based version included built on TCL designed to run under
+ a suitable CGI-supporting web server such as Apache.
+</UL>
+
+<P>
+
+Details on changes in previous (prerelease) versions of Alpine
+may be found at the following URL:
+<P>
+<CENTER><SAMP><A HREF="http://www.washington.edu/alpine/changes.html">http://www.washington.edu/alpine/changes.html</A></SAMP></CENTER>
+<P>
+
+<HR WIDTH="75%"><P>
+
+<H2>Getting Help</H2>
+<DL>
+<DT>Online Help</DT>
+<DD>
+Every Alpine screen and command has associated help text
+accessible via the &quot;?&quot; key (or Ctrl-G in text-input contexts).
+</DD>
+
+<DT>Web Help</DT>
+<DD>
+The most current source of information about Alpine,
+including new version availability, is the web page at
+<P>
+<CENTER><SAMP><A HREF="http://www.washington.edu/alpine/">http://www.washington.edu/alpine/</A></SAMP></CENTER>
+</DD>
+</DL>
+
+Frequently Asked Questions (and answers) may be found at the following
+URL:
+<P>
+<CENTER><SAMP><A HREF="http://www.washington.edu/alpine/faq/">http://www.washington.edu/alpine/faq/</A></SAMP></CENTER>
+<P>
+
+<HR WIDTH="75%"><P>
+
+<H2>Additional Information</H2>
+
+General Alpine configuration information can be found
+<A HREF="h_news_config">here</A>.
+<P>
+This is revision (<!--#echo var="ALPINE_REVISION"-->) of the Alpine software.
+Alpine mailbox and <A HREF="http://www.washington.edu/imap/">IMAP</A> server
+access is provided by the IMAP Toolkit Environment (c-client library)
+version <!--#echo var="C_CLIENT_VERSION"-->.
+<P>
+Alpine was developed by the Office of Computing &amp; Communications at
+the University of Washington in Seattle. A more complete list of
+principal players and key contributors can be found on the credits Web
+page at
+
+<P>
+<CENTER><A HREF="http://www.washington.edu/alpine/credits.html">http://www.washington.edu/alpine/credits</A></CENTER>
+
+<P>
+Alpine Copyright 2006-2008 University of Washington,
+Copyright 2013 Eduardo Chappa.
+
+<P>
+Additional legal notices can be found <A HREF="h_news_legal">here</A>
+or at the web URL:
+
+<P>
+<CENTER><A HREF="http://www.washington.edu/alpine/overview/legal.html">http://www.washington.edu/alpine/overview/legal</A></CENTER>
+
+<P>
+&lt;End of Release Notes&gt;
+</BODY>
+</HTML>
+====== h_tls_failure_details ======
+<HTML>
+<HEAD>
+<TITLE>Certificate Validation Details</TITLE>
+</HEAD>
+<BODY>
+<H1>Certificate Validation Details</H1>
+
+This screen gives details as to why the certificate validation failed: the
+name of the desired server system; the reason for failure; and the name on
+the certificate. This is primarily of interest to experts.
+
+<P>
+&lt;End of help&gt;
+</BODY>
+</HTML>
+====== h_tls_failure ======
+<HTML>
+<HEAD>
+<TITLE>TLS or SSL Failure</TITLE>
+</HEAD>
+<BODY>
+<H1>TLS or SSL Failure</H1>
+
+An attempt was made to establish a secure, encrypted connection to the
+server system using either Transport Layer Security (TLS) or the older
+Secure Sockets Layer (SSL). This attempt failed.
+
+<P>
+You should contact your server system management for more assistance.
+The problem is probably at the server system and not in Alpine or your local
+system. The text in this screen may be helpful for the server system
+management in debugging the problem,
+
+<P>
+&lt;End of help&gt;
+</BODY>
+</HTML>
+====== h_tls_validation_failure ======
+<HTML>
+<HEAD>
+<TITLE>TLS and SSL Certificate Validation Failures</TITLE>
+</HEAD>
+<BODY>
+<H1>TLS and SSL Certificate Validation Failures</H1>
+
+An attempt was made to establish a secure, encrypted connection to the
+server system using either Transport Layer Security (TLS) or the older
+Secure Sockets Layer (SSL).
+
+<P>
+An important part of this procedure is server certificate validation. A
+server certificate is an &quot;electronic identification card&quot; for the server
+system that is signed by a well-known certificate authority (CA). Alpine
+compares the server system identity in the server certificate with the
+name of the server system to which it is trying to connect. Alpine also
+verifies that the CA signature is authentic.
+
+<P>
+Some common failure conditions are:
+<P>
+
+<UL>
+ <LI> [UNIX Alpine] Self signed certificate. This means that the server system
+signed its own certificate. This does not necessarily indicate anything
+bad; the server operators may simply have elected to not purchase a
+signed certificate from a certificate authority.
+
+ <LI> [UNIX Alpine] Unable to get local issuer certificate. This means that
+the signature on the server system is from an unknown certificate authority.
+It can also mean that no certificate authority certificates have been
+installed on the local UNIX system.
+
+ <LI> [PC Alpine] Self-signed certificate or untrusted authority. This is
+the same as either of the above two conditions in UNIX Alpine. Note that
+Windows systems typically have a full set of certificate authority
+certificates installed, so it is more likely to be a self-signed
+certificate than an unknown certificate authority.
+
+ <LI> Server name does not match certificate. This means that the server
+presented a proper signed certificate for a name other than the desired
+name.
+</UL>
+
+<P>
+Any of these conditions can indicate that you are being attacked and have
+been directed to an imposter server that will record your password and
+your private mail messages. It can also mean something innocuous.
+
+<P>
+If you are certain that the problem is innocuous, you can append the
+option
+
+<P>
+<CENTER><SAMP>/novalidate-cert</SAMP></CENTER>
+<P>
+
+to the server system name where it appears in your configuration (e.g. the
+<A HREF="h_config_inbox_path"><!--#echo var="VAR_inbox-path"--></A>,
+a folder-collection, or a news or SMTP server). This will
+disable certificate validation. On the other hand, if you are attacked,
+you will get no warning if you do this.
+
+<P>
+&lt;End of Cert Validation Failures help&gt;
+</BODY>
+</HTML>
+====== h_release_tlscerts ======
+<HTML>
+<HEAD>
+<TITLE>TLS and SSL usage note</TITLE>
+</HEAD>
+<BODY>
+<H1>TLS and SSL usage note</H1>
+
+<P>
+When using Alpine from Unix or Windows 2000,
+server certificates must be signed by a trusted certificate authority.
+You may relax this requirement (at the cost of some security) by using
+the
+<A HREF="h_folder_server_syntax">NoValidate-Cert</A>
+modifier in the mailbox name.
+
+<P>
+<CENTER><SAMP>{foo.example.com/novalidate-cert}INBOX</SAMP></CENTER>
+<P>
+
+The fully-qualified host name of the server should be used
+so that it matches the host name in the server certificate.
+<P>
+Here is an example of a host specification that directs Alpine to use
+the SSL port (993) and an encrypted data stream.
+<P>
+<CENTER><SAMP>{foo.example.com/ssl}INBOX</SAMP></CENTER>
+<P>
+&lt;End of TLS usage help&gt;
+</BODY>
+</HTML>
+====== h_news_config ======
+<HTML>
+<HEAD>
+<TITLE>Alpine Configuration</TITLE>
+</HEAD>
+<BODY>
+<H1>Alpine Configuration</H1>
+
+<H2>Using Environment Variables</H2>
+
+The values of Alpine configuration options may include environment variables
+that are replaced by the value of the variable at the time Alpine is run
+(and also at the time the config option is changed).
+The syntax to use environment variables is a subset of the common Unix
+shell dollar-syntax.
+For example, if
+
+<P><CENTER><SAMP>$VAR</SAMP></CENTER><P>
+
+appears in the value of an Alpine configuration option it is looked up in the
+environent (using getenv(&quot;VAR&quot;)) and its
+looked-up value replaces the <SAMP>$VAR</SAMP> part of the option value.
+To include a literal dollar sign you may precede the dollar sign with another
+dollar sign.
+In other words, if the text
+
+<P><CENTER><SAMP>$$text</SAMP></CENTER><P>
+
+is the value of a configuration option, it will be expanded to
+
+<P><CENTER><SAMP>$text</SAMP></CENTER><P>
+
+and no environment lookup will be done.
+For Unix Alpine it will also work to use a backslash character to
+escape the special meaning of the dollar sign, but $$ is preferable since
+it works for both PC-Alpine and Unix Alpine, allowing the configuration option
+to be in a shared configuration file.
+<P>
+
+This all sounds more complicated than it actually is.
+An example may make it clearer.
+Unfortunately, the way in which environment variables are set is OS-dependent
+and command shell-dependent.
+In some Unix command shells you may use
+
+<P><CENTER><SAMP>PERSNAME="Fred Flintstone"</SAMP></CENTER><P>
+ <CENTER><SAMP>export PERSNAME</SAMP></CENTER><P>
+
+Now, if you use Alpine's Setup/Config screen to set
+
+<P><CENTER><SAMP><!--#echo var="VAR_personal-name"-->=$PERSNAME</SAMP></CENTER><P>
+
+the <SAMP>$PERSNAME</SAMP> would be replaced by <SAMP>Fred Flintstone</SAMP>
+so that this would be equivalent to
+
+<P><CENTER><SAMP><!--#echo var="VAR_personal-name"-->=Fred Flintstone</SAMP></CENTER><P>
+
+Note, environment variable substitution happens after configuration
+options that are lists are split into the separate elements of the list,
+so a single environment variable can't contain a list of values.
+
+<P>
+The environment variable doesn't have to be the only thing
+after the equal sign.
+However, if the name of the variable is not at the end of the line or
+followed by a space (so that you can tell where the variable name ends),
+it must be enclosed in curly braces like
+
+<P><CENTER><SAMP>${VAR}</SAMP></CENTER><P>
+
+It is always ok to use the braces even if you don't need to.
+<P>
+It is also possible to set a default value for an environment variable.
+This default value will be used if the environment variable is not
+set (that is, if getenv(&quot;VAR&quot;) returns NULL).
+The syntax used to set a default value is
+
+<P><CENTER><SAMP>${VAR:-default value}</SAMP></CENTER><P>
+
+If the config file contains
+
+<P><CENTER><SAMP>personal-name=${VAR:-Fred Flintstone}</SAMP></CENTER><P>
+
+then when Alpine is run <SAMP>VAR</SAMP> will be looked up in the environment.
+If <SAMP>VAR</SAMP> is found then <SAMP>personal-name</SAMP> will have
+the value that <SAMP>VAR</SAMP> was set to, otherwise,
+<SAMP>personal-name</SAMP> will be set to <SAMP>Fred Flintstone</SAMP>,
+the default value.
+(Note that the variable is called &quot;personal-name&quot; in the config
+file but is displayed in the config screen as
+&quot;<!--#echo var="VAR_personal-name"-->&quot;.
+In general, the value that goes into a config file is never exactly the
+same as the value you see on the screen.)
+
+<P>
+An example where an environment variable might be useful is the
+variable <SAMP>Inbox-Path</SAMP> in the global configuration file.
+Suppose most users used the server
+
+<P><CENTER><SAMP>imapserver.example.com</SAMP></CENTER><P>
+
+but that there were some exceptions who used
+
+<P><CENTER><SAMP>altimapserver.example.com</SAMP></CENTER><P>
+
+In this case, the system manager might include the following line in
+the systemwide default Alpine configuration file
+
+<P><CENTER><SAMP>Inbox-Path=${IMAPSERVER:-imapserver.example.com}</SAMP></CENTER><P>
+
+For the exceptional users adding
+
+<P><CENTER><SAMP>IMAPSERVER=altimapserver.example.com</SAMP></CENTER><P>
+
+to their environment should work.
+<P>
+Another example might be the case where a user has to use a different
+SMTP server from work and from home.
+The setup might be something as simple as
+
+<P><CENTER><SAMP>smtp-server=$SMTP</SAMP></CENTER><P>
+
+or perhaps a default value could be given.
+Note that, as mentioned above, the variable <SAMP>SMTP</SAMP> cannot contain
+a list of SMTP servers.
+<P>
+
+<H2>Configuration precedence</H2>
+
+There are several levels of Alpine configuration. Configuration values at
+a given level override corresponding values at lower levels. In order of
+increasing precedence:
+<P>
+<UL>
+ <LI> built-in defaults
+ <LI> system-wide
+<!--chtml if pinemode="os_windows"-->
+ config file from command line or provided
+ by "PINECONF" environment variable
+<!--chtml else-->
+ pine.conf file
+<!--chtml endif-->
+ <LI> personal configuration file
+ <LI> personal exceptions configuration file
+ <LI> command-line options
+ <!--chtml if pinemode="os_windows"--><!--chtml else-->
+ <LI> system-wide pine.conf.fixed file<!--chtml endif-->
+</UL>
+<P>
+The values in both the personal configuration file and the
+<A HREF="h_config_exceptions">exceptions</A>
+configuration file may be set using the Setup command.
+Setup/Config is the command to change most of the personal configuration
+options.
+The other Setup subcommands are also used to change the configuration,
+for example, Setup/AddressBook, Setup/Rules, and so on.
+Changing the personal exceptions configuration is very similar.
+To change a value in the Config screen you would use the command
+Setup/eXceptions/Config.
+Likewise for the other Setup subcommands (Setup/eXceptions/Rules and so on).
+<P>
+There are a couple exceptions to the rule that configuration values are replaced
+by the value of the same option in a higher-precedence file.
+The Feature-List variable has values that are additive, but can be
+negated by prepending &quot;no-&quot; in front of an individual feature name.
+So for features, each individual feature's value is replaced by the value
+of the same feature in a higher-precedence file.
+Note that this is done automatically for you when you change these values via
+the Setup/Config command.
+The other exception to the <EM>replace</EM> semantics happens when you
+use <A HREF="h_config_inheritance">configuration inheritance</A>
+for option lists.
+<P>
+
+<H2>File name defaults</H2>
+
+Notes:<P>
+
+<BR> &lt;exe dir&gt; = directory where pine.exe found.
+<BR> &lt;pinerc dir&gt; = directory where pinerc found.
+<BR> # = default file name is overridable in pinerc.
+<BR> $HOME, if not explicitly set, defaults to root of the current drive.
+<BR> $MAILCAPS, if set, is used in lieu of the default mailcap search paths.
+<BR> + between the mailcap paths implies that the two files are combined.
+<BR> ; between other default paths implies that the first one found is used.
+</P>
+Alpine looks for most support files in the same directory it finds its
+personal configuration file (pinerc). The -p command-line flag may be
+used to specify a particular path name for the pinerc file. If a
+pinerc file does not exist, it will be created (if directory permissions
+allow). In PC-Alpine, if -p or $PINERC are not defined, Alpine will look
+in $HOME&#92;PINE and the directory containing the PINE.EXE. If a PINERC
+file does not exist in either one, it will create one in the first of those
+two directories that is writable. In detail:
+<PRE>
+
+PC-Alpine:
+
+ executable &lt;DOS search path&gt;&#92;pine.exe
+ help index &lt;exe dir&gt;&#92;pine.ndx
+ help text &lt;exe dir&gt;&#92;pine.hlp
+
+ pers config $PINERC ; $HOME&#92;pine&#92;PINERC ; &lt;exe dir&gt;&#92;PINERC
+ except config $PINERCEX ; $HOME&#92;pine&#92;PINERCEX ; &lt;exe dir&gt;&#92;PINERCEX
+ global cfg $PINECONF
+
+ debug &lt;pinerc dir&gt;&#92;pinedebg.txtN
+ crash &lt;pinerc dir&gt;&#92;pinecrsh.txt
+ signature# &lt;pinerc dir&gt;&#92;pine.sig
+ addressbook# &lt;pinerc dir&gt;&#92;addrbook
+ mailcap# &lt;pinerc dir&gt;&#92;mailcap + &lt;exe dir&gt;&#92;mailcap
+ mimetypes# &lt;pinerc dir&gt;&#92;mimetype + &lt;exe dir&gt;&#92;mimetype
+ newsrc# $HOME&#92;newsrc (if exists, else) &lt;pinerc dir&gt;&#92;newsrc
+ sentmail# $HOME&#92;mail&#92;sentmail.mtx
+ postponed# $HOME&#92;mail&#92;postpond.mtx
+ interrupted $HOME&#92;mail&#92;intruptd
+
+Unix Alpine:
+
+ executable &lt;Unix search path&gt;/pine
+ persnl cfg ~/.pinerc
+ except cfg ~/.pinercex
+ global cfg <!--#echo var="PINE_CONF_PATH"-->
+ fixed cfg <!--#echo var="PINE_CONF_FIXED_PATH"-->
+ local help <!--#echo var="PINE_INFO_PATH"-->
+
+ interrupted ~/.pine-interrupted-mail
+ debug ~/.pine-debugN
+ crash ~/.pine-crash
+ newsrc# ~/.newsrc
+ signature# &lt;pinerc dir&gt;/.signature
+ addressbook# &lt;pinerc dir&gt;/.addressbook
+ postponed# ~/mail/postponed-msgs
+ sentmail# ~/mail/sent-mail
+ mailcap# ~/.mailcap + /etc/mailcap
+ + /usr/etc/mailcap + /usr/local/etc/mailcap
+ mimetypes# ~/.mime.types + /etc/mime.types + /usr/local/lib/mime.types
+
+ news-spool varies across Unix flavors, e.g. /var/spool/news or /usr/spool/news
+ active-news varies across Unix flavors, e.g. /usr/lib/news/active
+ lock files /tmp/.<!--#echo var="MAIL_SPOOL_LOCK_PATH"-->
+ inbox <!--#echo var="MAIL_SPOOL_PATH"-->
+ password /etc/passwd
+
+Unix Alpine and PC-Alpine:
+
+ .ab* remote addressbook support files
+ a[1-9]* temporary (while Alpine is running) addressbook files
+
+</PRE>
+<P>
+
+<H2>Mailcap files</H2>
+
+Alpine honors the mailcap configuration system for specifying external
+programs for handling attachments. The mailcap file maps MIME attachment
+types to the external programs loaded on your system that can display
+and/or print the file. A sample mailcap file comes bundled with the Alpine
+distribution. It includes comments that explain the syntax you need to
+use for mailcap. With the mailcap file, any program (mail readers,
+newsreaders, WWW clients) can use the same configuration for handling
+MIME-encoded data.
+<P>
+
+<H2>MIME-Types files</H2>
+
+Alpine uses mime-types files (.mime.types or MIMETYPE) to determine
+what Content-Type to use for labeling an attached file, based on
+the file extension. That is, this file provides a mapping between
+filename extensions and MIME content-types.
+<P>
+
+<H2>Environment variables</H2>
+
+PC-Alpine uses the following environment variables:
+<DL>
+<DT>PINERC</DT>
+<DD>Optional path to pinerc file.</DD>
+<DT>PINERCEX</DT>
+<DD>Optional path to personal exceptions configuration file.</DD>
+<DT>PINECONF</DT>
+<DD>Optional path to global pine config file.</DD>
+<DT>HOME</DT>
+<DT>TMPDIR, TMP, or TEMP</DT>
+<DT>COMSPEC</DT>
+<DT>MAILCAPS</DT>
+<DD>A <B>semicolon</B> delimited list of path names to mailcap files.</DD>
+<DT>USER_DICTIONARY</DT>
+<DD>Used to specify the file to contain the user's spell check
+dictionary. The default is <SAMP>DICT.U</SAMP> in the same
+directory as the <SAMP>SPELL32.DLL</SAMP></DD>
+</DL>
+
+Unix Alpine uses the following environment variables:
+<DL>
+<DT>TERM</DT>
+<DD>Tells Alpine what kind of terminal is being used.</DD>
+<DT>DISPLAY</DT>
+<DD>Determines if Alpine will try to display IMAGE attachments.</DD>
+<DT>SHELL</DT>
+<DD>If not set, default is &quot;/bin/sh&quot;.</DD>
+<DT>TMPDIR, TMP, or TEMP</DT>
+<DT>MAILCAPS</DT>
+<DD>A <B>colon</B> delimited list of path names to mailcap files.</DD>
+</DL>
+<!--chtml if pinemode="os_windows"-->
+<P>
+<H2>Common PC-Alpine Configuration Problems</H2>
+
+<H3>Configuration settings aren't being saved</H3>
+
+<P>This problem can happen if you run pine from one directory and
+then decide to move your pine directory to another location. PC-Alpine
+stores certain variables, including the configuration location, in the
+Windows Registry (which you shouldn't ever need to manually edit). There
+are a couple of ways to go about removing or resetting the values in the
+registry.
+
+<P>
+1) Run PC-Alpine's registry value deletion command. This can be done by
+running: &quot;&lt;your&nbsp;pine&nbsp;directory&gt;&#92;pine.exe&nbsp;-registry&nbsp;clear&quot; from the DOS
+prompt. You could create a shortcut to pine.exe and change the &quot;Target&quot;
+value to the above command.
+
+<P>
+2) Tell PC-Alpine where to look for the configuration file. Configuration
+information is stored in a file called the PINERC. With the &quot;-p&nbsp;PINERC&quot;
+option, you can tell PC-Alpine the location of your pinerc. An example of
+this would be to run: &quot;&lt;your&nbsp;pine&nbsp;directory&gt;&#92;pine.exe&nbsp;-p&nbsp;C:&#92;pine&#92;mypinerc&quot;.
+Again, you can use the DOS prompt or the shortcut method explained in (1).
+
+<P>
+Additionally, there is the &quot;-registry&nbsp;set&quot; option, which will actively
+set registry values to the current setting, and is therefore useful with
+the &quot;-p&nbsp;PINERC&quot; option.
+
+<!--chtml endif-->
+<P>
+&lt;End of Configuration Notes&gt;
+</BODY>
+</HTML>
+====== h_news_legal ======
+<html>
+<head>
+<TITLE>Alpine Legal Notices</TITLE>
+</head>
+<body>
+
+<H1>Alpine Legal Notices</H1>
+
+Alpine and its constituent programs are covered by the Apache License Version 2.0.
+
+
+<P>
+&lt;End of Alpine Legal Notices&gt;
+</BODY>
+</HTML>
+===== h_info_on_mbox =====
+<HTML>
+<HEAD>
+<TITLE>Information on mbox driver</TITLE>
+</HEAD>
+<BODY>
+<H1>Information on &quot;Missing Mail&quot; and the &quot;mbox&quot; driver</H1>
+
+Beginning with Pine 4.00 (Pine came before Alpine)
+a new INBOX access method is
+available as part of the standard configuration. It is called the
+&quot;mbox&quot; driver and it works like this:<P>
+
+<P>
+<BLOCKQUOTE>
+If the file &quot;mbox&quot; exists in the user's home directory, and
+is in Unix mailbox format, then when INBOX is opened this file will be
+selected as INBOX instead of the mail spool file. Messages will be
+automatically transferred from the mail spool file into the mbox
+file.
+</BLOCKQUOTE>
+
+<P>
+The advantage of this method is that, after new mail has been copied
+from the system mail spool, all subsequent access is confined to the
+user's home directory, which is desirable on some systems. However, a
+possible disadvantage is that mail tools other than those from the
+University of Washington will not know to look for mail in the user's
+mbox file. For example, POP or IMAP servers other than those from the
+University of Washington, and many &quot;new mail notification&quot;
+programs may not work as expected with this driver enabled.<P>
+
+To disable this behavior, either remove/rename the &quot;mbox&quot;
+file or find the <A HREF="h_config_disable_drivers"><!--#echo var="VAR_disable-these-drivers"--></A>
+option in Setup/Config
+and add &quot;mbox&quot; to it:
+<P>
+<CENTER><SAMP><!--#echo var="VAR_disable-these-drivers"-->=mbox</SAMP></CENTER>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_info_on_locking =====
+<HTML>
+<HEAD>
+<TITLE>FAQs on Alpine Locking</TITLE>
+</HEAD>
+<BODY>
+<H1>What Systems Managers Need to Know about Alpine File Locking</H1>
+
+There is an extensive section on locking in the Alpine technical notes;
+this information is intended to provide answers to some common questions:<P>
+<OL>
+<LI> Why did locking change in Pine 4.00?<BR>
+The actual locking mechanisms did not change in 4.00.
+What changed is that when one particular locking mechanism used by Alpine
+fails, Alpine now issues a warning message. Prior to Pine 4.00, the locking
+failure would occur, but no warning was issued.<P>
+
+<LI> Is this what the &quot;Mailbox vulnerable&quot; message is about?<BR>
+Yes. It means that Alpine was unable to create a lockfile in the
+spool directory, generally because of overly restrictive protections on the
+spool directory. The correct permissions on the spool directory for
+running Alpine are 1777, i.e. read-write-execute permission for everyone,
+with the sticky-bit set, so only owners of a file can delete them.<P>
+
+<LI> Why does Alpine require that the mail spool directory have 1777
+ protections?<BR>
+Alpine was designed to run without special privileges. This means that in
+order to create a lockfile in the spool directory, it is necessary to have
+the spool directory permissions be world-writable.<P>
+
+<LI> Can't you create the lockfile somewhere else?<BR>
+No. The lockfile in question must be in the mail spool directory, because
+that's where the mail delivery program expects to find it, and the purpose
+of the file is to coordinate access between the mail client (Alpine) and the
+mail delivery program.<P>
+
+<LI> Isn't having the spool directory world-writable a big security risk?<BR>
+No. Remember that the individual mail files in the spool directory are
+NOT world-writable, only the containing directory. Setting the &quot;sticky
+bit&quot; -- indicated by the &quot;1&quot; before the &quot;777&quot; mode
+-- means that only the owner of the file (or root) can delete files in the
+directory. So the only bad behavior that is invited by the 1777 mode is that
+anyone could
+create a random file in the spool directory. If the spool directory is
+under quota control along with home directories, there is little incentive
+for anyone to do this, and even without quotas a periodic scan for
+non-mail files usually takes care of the problem. <P>
+
+<LI> Why not run Alpine as setgid mail?<BR>
+Alpine was never designed to run with privileges, and to do so introduces a
+significant security vulnerability. For example, if a user suspends Alpine,
+the resulting shell will have group privileges. This is one example of
+why we strongly recommend against running Alpine as a privileged program.
+In addition, a &quot;privileged mailer &quot paradigm would mean that normal
+users
+could not test Alpine versions or other mailers that had not been installed
+by the system administrators.<P>
+
+
+<LI> Are there any alternatives to creating .lock files in the spool dir?<BR>
+There are, but they all have different sets of tradeoffs, and not all will
+work on all systems. Some examples:<UL>
+ <LI> Use lock system calls. Works fine on a few systems, provided mail
+ spool is local. Doesn't work reliably if NFS is used.
+ Doesn't work unless <B>all</B> the mail programs accessing the spool dir
+ use the same calls.
+ <LI> Deliver mail to user's home directory. An excellent solution, highly
+ recommended -- but one which is incompatible with some &quot;legacy&quot;
+mail tools that always look in the spool directory for the mail.
+</UL><P>
+
+<LI> Are these spool directory lock files the only kinds of locks used by
+ Alpine?<BR>
+No. Alpine also creates lockfiles in the /tmp directory. For normal Unix
+mailbox format folders, these are used to coordinate access between
+multiple Alpine sessions. <P>
+
+<LI> What about the
+<A HREF="h_config_quell_lock_failure_warnings">&quot;<!--#echo var="FEAT_quell-lock-failure-warnings"-->&quot;</A> feature added in Pine 4.01?<BR>
+This is for people who are content to live dangerously, or who have
+specific knowledge that the spool directory lockfiles are superfluous on
+their system (because both Alpine and the mail delivery program are using
+system call file locking in a context that works reliably, e.g. not NFS.)<P>
+
+<LI> Where can I find more details on how Alpine locking works?<BR>
+See the Alpine Technical Notes.<P>
+
+</OL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_finding_help ====
+<HTML>
+<HEAD>
+<TITLE>Finding more information and requesting help</TITLE>
+</HEAD>
+<BODY>
+<H1>Places to Look for More Answers</H1>
+If you have questions about or problems with Alpine that you cannot resolve
+after consulting the program's internal, context-sensitive help screens, here
+are additional information resources that you may find helpful:
+<P>
+<UL>
+ <LI> Alpine's top-level <A HREF="main_menu_tx">MAIN MENU HELP</A>.<P>
+
+ <LI> Alpine's <A HREF="h_help_index">Help Index</A>.<P>
+
+ <LI> Alpine's internal <A HREF="h_news">Release Notes</A>. They contain a
+listing of changes in Alpine <!--#echo var="ALPINE_VERSION"-->
+ since the last version, which may be useful for you to be aware of,
+<B>especially</B> if a &quot;problem&quot; you are encountering is actually
+a change in the way an aspect of Alpine works. There, you will also find notes
+on Alpine configuration.<P>
+
+ <LI> The Alpine Information Center (maintained by the University of
+ Washington) World Wide Web site contains, among other things
+ <UL>
+ <LI>a collection of Frequently Asked Questions (and answers!) about Alpine
+ <LI>an overview of the basics for beginning Alpine users
+ <LI>Technical Notes for systems administrators
+ <LI>archives (including a searchable index) of the alpine-info
+mailing list, on which matters of interest to systems/email administrators,
+developers, trainers, user support personnel, and others involved with Alpine
+messaging on a &quot;technical&quot; level are discussed.
+</UL>
+ The Alpine Information Center can be accessed with a WWW browser at:<P>
+ <CENTER><A HREF="http://www.washington.edu/alpine/">http://www.washington.edu/alpine/</A></CENTER>
+</UL>
+<P><HR WIDTH="75%">
+<H1>Requesting help</H1>
+If the internal help, the Release Notes, the Alpine Information Center, and your
+local online and print resources do not help you resolve a problem, please
+start by contacting your local computer support staff and asking for help.
+<p>
+This is especially true if:
+<ul>
+ <li>You suddenly have trouble sending or receiving mail.
+ <li>You receive a "disk quota exceeded" message.
+ <li>You have forgotten your password.
+ <li>You think your account may have been compromised.
+ <li>You need help viewing an attachment.
+ <li>You need to know how to configure your:
+ <A HREF="h_config_nntp_server">NNTP (news) server</A>,
+ <A HREF="h_config_smtp_server">SMTP (sending mail) server</A>,
+ <A HREF="h_config_ldap_server">LDAP (directory lookup) server</A>, or
+ <A HREF="h_config_inbox_path">INBOX (incoming mail) path</A>.
+ <li>You want to know what alternative editors or spellcheckers you may be able to use.
+ <li>You want to block email from a particular person.
+ <li>You're going on vacation and need to autorespond to incoming mail.
+ <li>You want to automatically file or filter incoming messages.
+</ul>
+
+In all of these cases,
+you should contact <B>your</B> support staff, because <B>only they</B>
+will be able to assist you effectively. Your support staff may be, depending on who
+provides you with the email account you use Alpine with, for example:<UL>
+<LI> the computing help desk of (a department of) your university, school,
+employer, ... ; or
+<LI> the customer service center of your Internet Service Provider; or
+<LI> the friendly volunteer helpers of your Freenet; or
+<LI> the person who setup your computer and internet connection.
+</UL>
+
+Due to the large number of Alpine installations worldwide, and because we
+receive no funding for it, the Alpine development team <B>cannot provide
+individual support services outside the University of Washington</B>.
+<P>
+If you have no local computing support to turn to, the worldwide <b>comp.mail.pine</b>
+newsgroup can be a valuable source of information and assistance for Alpine
+user issues.
+<P>
+For systems/email administrators, developers, trainers, user support
+personnel, and others involved with Alpine messaging on a &quot;technical&quot;
+level, the mailing list alpine-info is available; for information on
+subscribing and posting to it, see
+<P>
+<CENTER><A HREF="http://www.washington.edu/alpine/alpine-info/subscribing.html">http://www.washington.edu/alpine/alpine-info/subscribing.html</A></CENTER>
+<P>
+
+Regardless of whom you are asking for help with Alpine, remember
+to provide as much detail as you can about the
+nature of any problem you are encountering, such as
+<UL>
+<LI>when it first occurred;
+<LI>what, if anything, happened that might have brought it about;
+<LI>whether it still persists;
+<LI>whether it is reproducible, and if so, how;
+<LI>what, if anything, you already tried to solve it.
+</UL>
+It may also be helpful if you specify what version of Alpine you are using
+<!--chtml if pinemode="running"-->
+-- this is <!--#echo var="ALPINE_VERSION"--> --
+<!--chtml endif-->
+and on what system, and when the copy of Alpine you are using was created
+<!--chtml if pinemode="running"-->
+-- for this copy: <!--#echo var=ALPINE_COMPILE_DATE-->
+<!--chtml endif-->
+
+<!--chtml if pinemode="running"-->
+<P>
+When the Alpine program you are currently using was installed, a support
+contact email address may have been set up; in that case, you can simply select
+this link now to send a message to it:<BR>
+<A HREF="X-Alpine-Gripe:_LOCAL_ADDRESS_?local"><!--#echo var="_LOCAL_FULLNAME_"--></A><P>
+<!--chtml endif-->
+<!--chtml if [ -r PINE_INFO_PATH ]-->
+<HR WIDTH="75%">Local Support Contacts:<P>
+<!--#include file="PINE_INFO_PATH"-->
+<HR WIDTH="75%">
+<!--chtml endif-->
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== new_user_greeting ======
+<HTML>
+<HEAD>
+<TITLE>NEW USER GREETING</TITLE>
+</HEAD>
+<BODY>
+<CENTER>&lt;&lt;&lt;This message will appear only once&gt;&gt;&gt;</CENTER>
+<BR>
+<H1>Welcome to Alpine ... a Program for Internet News and Email</H1>
+We hope you will explore Alpine's many capabilities. From the MAIN MENU,
+select Setup/Config to see many of the options available to you. Also note
+that all screens have context-sensitive help text available.<P>
+<!--chtml if pinemode="phone_home"-->
+SPECIAL REQUEST:
+This software is made available as a public service of the
+University of Washington in Seattle. We are no longer actively developing
+the software, but it is still helpful to us to have an idea of how many
+people are using Alpine. Are you willing to be counted as an Alpine user? Pressing
+<A HREF="X-Alpine-Phone-Home:">Return</A>
+will send an anonymous (meaning, your real email address will not be revealed)
+message to the Alpine team at the University of Washington for purposes of tallying.
+<P>
+<!--To Exit this screen and continue your Alpine session press "E".-->
+<!--chtml else-->
+To Exit this screen and continue your Alpine session press "Return".
+<!--chtml endif-->
+</BODY>
+</HTML>
+===== new_alpine_user_greeting ======
+<HTML>
+<HEAD>
+<TITLE>NEW ALPINE USER GREETING</TITLE>
+</HEAD>
+<BODY>
+<CENTER>&lt;&lt;&lt;This message will appear only once&gt;&gt;&gt;</CENTER>
+<BR>
+<H1>Welcome to Alpine ... a Program for Internet News and Email</H1>
+Your Alpine configuration file indicates that you may have used Pine before
+but not Alpine.
+If you are familiar with the way Pine works, you should be comfortable
+using Alpine.
+Your Pine configuration file is automatically used for Alpine.
+The Release Notes may be viewed by pressing
+&quot;R&quot; now or while in the MAIN MENU.
+<P>
+<!--chtml if pinemode="phone_home"-->
+SPECIAL REQUEST:
+This software is made available as a public service of the
+University of Washington in Seattle. We are no longer actively developing
+the software, but it is still helpful to us to have an idea of how many
+people are using Alpine. Are you willing to be counted as an Alpine user? Pressing
+<A HREF="X-Alpine-Phone-Home:">Return</A>
+will send an anonymous (meaning, your real email address will not be revealed)
+message to the Alpine team at the University of Washington for purposes of tallying.
+<P>
+<!--To Exit this screen and continue your Alpine session press "E".-->
+<!--chtml else-->
+To Exit this screen and continue your Alpine session press "Return".
+<!--chtml endif-->
+</BODY>
+</HTML>
+===== new_version_greeting ======
+<HTML>
+<HEAD>
+<TITLE>NEW VERSION GREETING</TITLE>
+</HEAD>
+<BODY>
+<CENTER>&lt;&lt;&lt;This message will appear only once&gt;&gt;&gt;</CENTER>
+<BR>
+<H1>Welcome to Alpine version <!--#echo var="ALPINE_VERSION"-->!</H1>
+Your Alpine configuration file indicates that you may not have used
+this version of Alpine before. This version's significant changes are
+documented in the Release Notes, which may be viewed by pressing
+&quot;R&quot; now or while in the MAIN MENU.
+<P>
+<!--chtml if pinemode="phone_home"-->
+SPECIAL REQUEST:
+This software is made available as a public service of the
+University of Washington in Seattle. We are no longer actively developing
+the software, but it is still helpful to us to have an idea of how many
+people are using Alpine. Are you willing to be counted as an Alpine user? Pressing
+<A HREF="X-Alpine-Phone-Home:">Return</A>
+will send an anonymous (meaning, your real email address will not be revealed)
+message to the Alpine team at the University of Washington for purposes of tallying.
+
+<!--To Exit this screen and continue your Alpine session press "E".-->
+<!--chtml else-->
+To Exit this screen and continue your Alpine session press "Return".
+<!--chtml endif-->
+</BODY>
+</HTML>
+
+===== main_menu_tx ======
+<HTML>
+<HEAD>
+<TITLE>GENERAL INFORMATION ON THE ALPINE MESSAGE SYSTEM</TITLE>
+</HEAD>
+<BODY>
+<H1>GENERAL INFORMATION ON THE ALPINE MESSAGE SYSTEM</H1>
+<DIV ALIGN=CENTER>
+Version <!--#echo var="ALPINE_VERSION"-->
+<!--chtml if pinemode="running"-->
+<BR>(built <!--#echo var=ALPINE_COMPILE_DATE-->)
+<!--chtml endif-->
+</DIV>
+<CENTER>Copyright 2006-2008 University of Washington
+<BR> Copyright 2013 Eduardo Chappa
+</CENTER>
+
+<P>
+When you are viewing a help screen, there may be links to
+other topics highlighted (in Reverse video) in the text.
+Here is an example.
+The word &quot;Introduction&quot; in the TABLE OF CONTENTS below should be
+highlighted.
+If you type carriage return (or V for View Link, see the commands at the
+bottom of the screen) you will be taken to a new help screen to view the
+Introduction.
+The commands at the bottom of the screen should then include
+&quot;P Prev Help&quot;.
+If you type &quot;P&quot; you will end up back here.
+If you type &quot;E&quot; for Exit, you will be back out of help and returned
+to the place you were in Alpine when you entered Help.
+In this case, you would go back to the MAIN MENU.
+There are also other links that are highlighted in bold (or the color used
+by your terminal to display bold).
+The items after the Introduction in the TABLE OF CONTENTS are all examples
+of such links.
+In order to view those links, you first have to make the link you want
+to view the current link.
+The &quot;NextLink&quot; and &quot;PrevLink&quot; commands
+(see bottom of screen) can do that for you.
+<P>
+
+<H2>TABLE OF CONTENTS</H2>
+<OL>
+ <LI> <A HREF="h_mainhelp_intro">Introduction</A>
+ <LI> <A HREF="h_mainhelp_pinehelp">Alpine Help</A>
+<!--chtml if [ -r PINE_INFO_PATH ]-->
+ <LI> <A HREF="h_mainhelp_localsupport">Local Support Contacts</A>
+<!--chtml endif-->
+ <LI> <A HREF="h_mainhelp_cmds">Giving Commands in Alpine</A>
+ <LI> <A HREF="h_mainhelp_config">Alpine Configuration</A>
+ <LI> <A HREF="h_mainhelp_status">Titlebar Line</A>
+ <LI> <A HREF="h_mainhelp_mainmenu">Main Menu</A>
+ <LI> <A HREF="h_mainhelp_index">Index of Messages</A>
+ <LI> <A HREF="h_mainhelp_reading">Reading Messages</A>
+ <LI> <A HREF="h_mainhelp_composing">Composing Messages</A>
+ <LI> <A HREF="h_mainhelp_readingnews">Reading News</A>
+ <LI> <A HREF="h_mainhelp_abooks">Address Books</A>
+ <LI> <A HREF="h_mainhelp_ldap">LDAP Directories</A>
+ <LI> <A HREF="h_mainhelp_folders">Folders</A>
+ <LI> <A HREF="h_mainhelp_collections">Collection Lists</A>
+ <LI> <A HREF="h_mainhelp_aggops">Aggregate Operations</A>
+ <LI> <A HREF="h_mainhelp_color">Color Setup</A>
+ <LI> <A HREF="h_mainhelp_filtering">Filtering</A>
+ <LI> <A HREF="h_mainhelp_roles">Roles</A>
+ <LI> <A HREF="h_mainhelp_patterns">Patterns</A>
+ <LI> <A HREF="h_mainhelp_keywords">Keywords (or Flags, or Labels)</A>
+ <LI> <A HREF="h_mainhelp_mouse">Using a Mouse</A>
+ <LI> <A HREF="h_mainhelp_cmdlineopts">Command Line Options</A>
+ <LI> <A HREF="h_mainhelp_securing">Securing Your Alpine Session</A>
+ <LI> <A HREF="h_mainhelp_smime">S/MIME</A>
+ <LI> <A HREF="h_mainhelp_problems">Reporting Problems</A>
+ <LI> <A HREF="X-Alpine-Config:">Show Supported Options in this Alpine</A>
+ <LI> <A HREF="h_help_index">Index to Alpine's Online Help</A>
+</OL>
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_mainhelp_intro ======
+<HTML>
+<HEAD>
+<TITLE>Introduction</TITLE>
+</HEAD>
+<BODY>
+<H1>Introduction</H1>
+
+Alpine is an &quot;Alternatively Licensed Program for Internet
+News and Email&quot; produced until 2008 by the University of Washington.
+It is intended to be an easy-to-use program for
+sending, receiving, and filing Internet electronic mail messages and
+bulletin board (Netnews/Usenet) messages. Alpine supports the following
+Internet protocols and specifications: SMTP (Simple Mail Transport Protocol),
+NNTP (Network News Transport Protocol), MIME (Multipurpose Internet Mail
+Extensions), IMAP (Internet Message Access Protocol), and LDAP (Lightweight
+Directory Access Protocol).<p>
+
+Although originally designed for inexperienced email users, Alpine has
+evolved to support many advanced features. There are an ever-growing
+number of configuration and personal-preference options, though which of
+them are available to you is determined by your local system managers.
+
+<H2>WHAT ALPINE DOES...</H2>
+
+Alpine is a &quot;mail user agent&quot; (MUA), which is a program that
+allows you to
+compose and read messages using Internet mail standards. (Whether you
+can correspond with others on the Internet depends on whether or not your
+computer is connected to the Internet.) Alpine also allows reading and
+posting messages on the Internet &quot;net news&quot; system, provided
+that your site operates a suitable news server.
+
+<H2>WHAT ALPINE DOES NOT DO...</H2>
+
+A &quot;mail user agent&quot; such as Alpine is just one part of a
+messaging system. Here are some things that are <B>not</B> done by Alpine,
+but require other programs:<P>
+<UL>
+ <LI> Actual relaying of email... which is done by &quot;message transfer
+agents&quot;.
+ <LI> Vacation messages... automatically responding to incoming messages
+ <LI> Anything to do with &quot;talk&quot;... which has nothing to do with
+email.
+ <LI> Anything to do with &quot;irc&quot;... which has nothing to do with email.
+ <LI> List processing... resending one message to a list of recipients.
+</UL>
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+
+===== h_mainhelp_pinehelp ======
+<HTML>
+<HEAD>
+<TITLE>Alpine Help</TITLE>
+</HEAD>
+<BODY>
+<H1>Alpine Help</H1>
+
+Alpine help is generally context-sensitive. In other words, each Alpine screen you
+use will have its own help text, explaining the choices available for that
+screen. This general help section, on the other hand, attempts to give an
+overall picture of what Alpine is capable of doing, as well as pointers to
+additional help sections about specific topics.<p>
+
+Much of the help text contains links to further help topics, similar to
+how the World Wide Web works.
+You may choose a link to view using the &quot;NextLink&quot; and
+&quot;PrevLink&quot; commands to change the link that is highlighted.
+The &quot;View Link&quot; command will then show you the highlighted link.
+Similar to the Back button in a web browser, the &quot;Prev Help&quot; command
+will return you to where you were before viewing the link, and &quot;Exit Help&quot
+will return you to the location in Alpine before you asked for help.
+For example, if you are reading this text in Alpine you may return to the
+help table of contents with the &quot;Prev Help&quot; command or you may view the
+Release notes link in the next paragraph and then return here with
+&quot;Prev Help&quot;.
+<P>
+
+In addition to this general help on Alpine, <A HREF="h_news">Release Notes</A>
+on the current Alpine version are also available from the MAIN MENU: Press
+<!--chtml if pinemode="function_key"-->
+&quot;F9&quot;
+<!--chtml else-->
+&quot;R&quot;
+<!--chtml endif-->
+to browse the release notes. These include changes since the last release,
+configuration information, the history of the Alpine
+project, credits, and legal notices.
+
+Alpine files and documentation are available via FTP or WWW:
+<P>
+<CENTER><SAMP><A
+HREF="ftp://ftp.cac.washington.edu/alpine/">ftp://ftp.cac.washington.edu/alpine/</A>
+</SAMP></CENTER>
+or
+<CENTER><SAMP><A
+HREF="http://www.washington.edu/alpine/">http://www.washington.edu/alpine/</A></SAMP
+></CENTER>
+<P>
+
+If you would like to print <EM>all</EM> of Alpine's internal help text
+(not recommended) for a little light bedtime reading, then press
+<!--chtml if pinemode="function_key"-->
+&quot;F12&quot;
+<!--chtml else-->
+&quot;Z&quot;
+<!--chtml endif-->
+now. (This assumes that the
+copy of Alpine you are using has been properly configured for printing
+at your site.)
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_mainhelp_localsupport ======
+<HTML>
+<HEAD>
+<TITLE>Local Support Contacts</TITLE>
+</HEAD>
+<BODY>
+<H1>Local Support Contacts</H1>
+
+<!--chtml if [ -r PINE_INFO_PATH ]-->
+<!--#include file="PINE_INFO_PATH"-->
+<!--chtml else-->
+No Local Support Contacts configured.
+<!--chtml endif-->
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+
+===== h_mainhelp_cmds ======
+<HTML>
+<HEAD>
+<TITLE>Giving Commands in Alpine</TITLE>
+</HEAD>
+<BODY>
+<H1>Giving Commands in Alpine</H1>
+
+Unless configured otherwise
+(<A HREF="h_config_blank_keymenu"><!--#echo var="FEAT_disable-keymenu"--></A>)
+the bottom two lines of the screen are always used to list the
+commands you can give. You press the keys that are highlighted to give
+the command. The commands for getting help and going back to the main
+menu are always present (except when viewing help as you are now).
+<!--chtml if pinemode="function_key"-->
+<!--chtml else-->
+<p>
+Pressing O (meaning &quot;Other Commands&quot;) changes the keys
+you see at the bottom of any screen. In some cases there are 3 or
+even 4 different
+sets of keys that can be seen by using the O key. <EM>All commands are
+active</EM>, even if they are not currently showing at the bottom of your
+screen. In other words, you <EM>never</EM> need to press the O key, except to
+remind yourself of the proper key to press to perform an operation.
+
+<H2>Control Key Commands</H2>
+When composing mail, and in a few other places, in Alpine you
+have to use Control keys. This means pressing the Control key (usually labeled
+&quot;Ctrl&quot;) and the
+letter indicated at the same time. Usually, this is shown with a
+&quot;^&quot; in front of the letter. On some systems, certain control
+characters are intercepted before they get to Alpine. As a work-around,
+you can press the ESCAPE key twice followed by the desired key. For
+example, if Ctrl-O (^O) does not work on your system, try typing
+&quot;ESC ESC O&quot;.
+<!--chtml endif-->
+<H2>Paging Up and Down</H2>
+The &quot;+&quot; and &quot;-&quot; keys are used for
+moving to the next or previous page. The space bar is a synonym for
+&quot;+&quot;. You may also use Ctrl-V to page down and Ctrl-Y to page
+up as you do in the message composer. On screens with a WhereIs (search)
+command, W or Ctrl-W followed by Ctrl-V will move to the bottom of the
+message or list, and W or Ctrl-W followed by Ctrl-Y will move to the top
+of the message or list.
+
+<H2>Return Key</H2>
+The return key is usually a synonym for a frequently used
+command. When viewing a message, there is currently not a default
+command, so RETURN does nothing; when in the index, it is synonymous with
+&quot;view msg&quot;. In the key menu at the bottom of the screen, whatever is
+enclosed in square brackets [] is the same as the return key.
+
+<H2>Control Keys Not Used By Alpine</H2>
+Most commands in Alpine are single letters, with -- we hope -- some mnemonic
+value, but in places where Alpine is expecting text input, e.g. in the composer or
+at prompts for file/folder names, control keys must be used for editing and
+navigation functions.
+<P>
+
+Alpine has used nearly all the control keys available. There are, however,
+certain control keys that are reserved by other programs or for technical
+reasons. Alpine does not use any of these keys:
+<DL>
+ <DT>Ctrl-S</DT> <DD>Used by Unix as &quot;stop output&quot;</DD>
+ <DT>Ctrl-Q</DT> <DD>Used by Unix as &quot;resume output&quot;</DD>
+ <DT>Ctrl-]</DT> <DD>Often used by Telnet as escape key</DD>
+</DL>
+<P>
+Note: Ctrl-S and Ctrl-Q can be subject to
+<A HREF="h_special_xon_xoff">special handling</A>.
+<P>
+In addition, while the ESC key alone is not used for command input,
+Alpine will recognize two consecutive ESC key presses followed by a letter
+key as a substitute for control key input. For example, the control key
+<SAMP>Ctrl-X</SAMP> can alternatively be entered using the
+three keystrokes: <SAMP>ESC&nbsp;ESC&nbsp;x</SAMP>.
+This is useful if the communication program you are using
+(e.g. Telnet) has its own, conflicting, idea of what certain control
+characters mean.
+
+
+<H2>Repainting the Screen</H2>
+Sometimes what is displayed on the screen will be
+incorrect due to noise on the phone line or other causes and you will want
+to repaint the whole screen to make it correct. You can use the Ctrl-L
+command to do this. It never hurts to do it when in doubt.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_mainhelp_status ======
+<HTML>
+<HEAD>
+<TITLE>Titlebar Line</TITLE>
+</HEAD>
+<BODY>
+<H1>Titlebar Line</H1>
+
+The top line of the screen is Alpine's titlebar line. It will always display
+the current version of Alpine and will also convey information about the
+status of the program. This is where you look to find out what
+collection, folder and message number is active and where you are in Alpine.
+<P>
+
+If the titlebar line says &quot;READONLY&quot; it means that the open folder
+(typically your INBOX) is &quot;locked&quot; by another mail session --
+most likely a more recent session of Alpine has taken the INBOX lock.
+<P>
+
+If the titlebar line says &quot;CLOSED&quot; it means that you are trying to
+access a
+folder on a remote mail server, and for some reason, communication with
+the mail server has either been lost, or never successfully established.
+This can be a result of trying to open a non-existent folder, or one
+stored on an invalid or non-operational server, or it can mean that Alpine
+has been suspended for more that 30 minutes while accessing a remote mail
+server.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_mainhelp_mainmenu ======
+<HTML>
+<HEAD>
+<TITLE>Main Menu</TITLE>
+</HEAD>
+<BODY>
+<H1>Main Menu</H1>
+
+The Main Menu lists Alpine's main options.
+The key or keys you must type to enter your
+choice are to the left of each option or command name.
+You can type either uppercase or lowercase letters,
+and you should not press &lt;Return&gt; after typing the
+letter (unless you are specifically asking for the default,
+highlighted command).
+<P>
+
+From the Main Menu you can choose to read online help, write (compose) and
+send a message, look at an index of your mail messages, open or maintain
+your mail folders, update your address book, configure Alpine, and quit Alpine.
+There are additional options listed at
+the bottom of the screen as well.
+
+<P>
+The Help command usually returns context-sensitive help information.
+However, in the Main Menu you get the most general help, which includes
+a Table of Contents.
+The last entry in the Table of Contents is an Index of help topics,
+so this is a good place to go if you are having trouble finding how
+to do something.
+
+<H2>Main Menu Commands</H2>
+The Alpine main menu lists the most common Alpine functions. A <a
+href="h_main_menu_commands">full list of these
+commands</a> and what they do is available.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_mainhelp_abooks ======
+<HTML>
+<HEAD>
+<TITLE>Address Books</TITLE>
+</HEAD>
+<BODY>
+<H1>Address Books</H1>
+
+
+As you use email, you can build a list of your regular email correspondents
+in your Alpine
+Address Book. At the Alpine MAIN MENU, press A to see the Address Book List
+screen. Your
+personal address book will be highlighted. Press &lt;Return&gt; to view it.
+You can use the address book to store email addresses for individuals or
+groups, to create easily
+remembered &quot;nicknames&quot; for these addresses, and to quickly retrieve an email
+address when you are composing a message.
+<P>
+There are two ways to add addresses to your address book: you can add them
+manually or take them from messages (by pressing T to access the Take command).
+With either method, you specify nicknames for your correspondents. A single
+address book entry (or nickname) can point to just one email address, or, it can
+point to more than one. When it points to more than one, it is called a
+distribution list. Each distribution list has a nickname, a full name, and a
+list of addresses. These
+addresses may be actual addresses, other nicknames in your address book, or
+other
+distribution lists.
+
+<P>
+Additional information is available in Alpine's online help:
+<ul>
+ <li><a href="h_abook_opened">The Alpine Address Book</a></li>
+</ul>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_mainhelp_ldap ======
+<HTML>
+<HEAD>
+<TITLE>LDAP</TITLE>
+</HEAD>
+<BODY>
+<H1>LDAP</H1>
+
+LDAP (Lightweight Directory Access Protocol) is a standard means of accessing
+an organization's shared
+directories. Essentially, using LDAP, Alpine is able to find email addresses in
+large address
+books, rather like the White Pages provided by the phone company. As an Alpine
+user, it is not
+necessary to know much about how this works, only how to use it and how to
+configure
+it.
+<P>
+More information on configuring LDAP is available in Alpine's online help:
+<ul>
+ <li><a href="h_direct_config">Setup LDAP Directory Servers</a></li>
+</ul>
+<P>
+Additional help on using LDAP in Alpine is also available:
+<ul>
+ <li><a href="h_ldap_view">LDAP Response View Explained</a></li>
+</ul>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_mainhelp_index ======
+<HTML>
+<HEAD>
+<TITLE>Index of Messages</TITLE>
+</HEAD>
+<BODY>
+<H1>Index of Messages</H1>
+
+In Alpine's message index, the selected message is highlighted. The first
+column on the left is blank, shows a &quot;+&quot; if the message was
+sent directly to you (i.e., it is not a
+copy or from a list), or a &quot;-&quot; if you were explicitly Cc'd.
+<P>
+The second column may be blank, or it may contain:
+<ul>
+ <li>"N" if the message is new (unread), </li>
+ <li>"A" if you have answered the message (using the Reply command), </li>
+ <li>"D" if you have marked the message for deletion.</li>
+</ul>
+
+<P>
+Note: If you answer a message as well as mark it deleted (in either order),
+you will only see the &quot;D&quot;.
+
+<P>
+The rest of the columns in the message line show you the message
+number, date sent, sender, size, and subject. For details, press ? (Help).
+The behavior and appearance of the Index screen is highly configurable.
+In the Setup/Config screen search (with the WhereIs command) for options
+that contain the words &quot;index&quot; or &quot;thread&quot; to see
+many of the configuration possibilities.
+In particular, the
+&quot;<A HREF="h_config_index_format"><!--#echo var="VAR_index-format"--></A>&quot;
+option may be used to configure the look of the standard MESSAGE INDEX lines
+in many different ways.
+Find <!--#echo var="VAR_index-format"--> in the Setup/Config screen and
+read the help text there for more information.
+<P>
+Most of the commands you need to handle your messages are visible at the
+bottom of the screen, and you can press O (OTHER CMDS) to see additional
+commands that are available.
+You do not need to see these &quot;other commands&quot;
+on the screen to use them. That is, you never need to press O as a prefix
+for any other command.
+
+<P>
+Additional information is available in Alpine's online help:
+<ul>
+ <li><a href="h_mail_index">Message Index Commands</a></li>
+</ul>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_mainhelp_reading ======
+<HTML>
+<HEAD>
+<TITLE>Reading Messages</TITLE>
+</HEAD>
+<BODY>
+<H1>Reading Messages</H1>
+
+The message text screen shows you the text of the message along with
+its header. If a message has attachments, those will be listed (but not
+displayed) also. The titlebar line displays information about the currently
+open message, folder and collection. You see the name of the collection
+(if there is one) in angle brackets, then the name of the folder, then the
+message number and finally the position within the current message (in
+percent). If the message is marked for deletion
+&quot;DEL&quot; will appear in the
+upper right as well.
+
+<P>
+As with every Alpine screen, the bottom two lines show you the commands
+available.
+
+<P>Additional information is available in Alpine's online help:
+<ul>
+ <li><a href="h_mail_view">Message Text Screen</a></li>
+ <li><a href="h_attachment_screen">Attachment Index Screen Explained</a></li>
+ <li><a href="h_mail_text_att_view">Attachment View Screen Explained</a></li>
+</ul>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_mainhelp_composing ======
+<HTML>
+<HEAD>
+<TITLE>Composing Messages</TITLE>
+</HEAD>
+<BODY>
+<H1>Composing Messages</H1>
+
+To write a message, press C (Compose). You see the Compose Message
+screen, which is divided into two parts: the header area and the message
+text area. The header area is where information on the recipient (the To:
+field) and the subject line go, while the message text area contains the
+actual text of the email message. Different commands are available to you
+when your cursor is in different areas on this screen. To see additional
+help on commands in either the message text or header area, type
+<Control>G (Get help).
+
+<P>
+To move around, use the arrow keys or Ctrl-N (Next line) and Ctrl-P
+(Previous line); to correct typing errors, use &lt;Backspace&gt; or &lt;Delete&gt;.
+
+<P>The following information from Alpine's online help may prove useful:
+<ul>
+ <li><a href="h_composer_to">Message Header Commands</a></li>
+ <li><a href="h_compose_richhdr">Rich Header Command</a></li>
+ <li><a href="h_composer">Composer Commands</a></li>
+ <li><a href="h_edit_nav_cmds">Composer Editing Commands</a></li>
+ <li><a href="h_config_change_your_from">Changing your From Address</a></li>
+ <li><a href="h_compose_send">Send Command</a></li>
+ <li><a href="h_compose_spell">Spell Check Command</a></li>
+</ul>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_mainhelp_collections ======
+<HTML>
+<HEAD>
+<TITLE>Collection Lists</TITLE>
+</HEAD>
+<BODY>
+<H1>Collection Lists</H1>
+
+Collection lists are Alpine's way of organizing groups of folders. Each
+"collection" can reside on a different server, for example, and contain a
+different group of mail folders.
+
+<P>
+For more information on this, see:
+<ul>
+ <li><a href="h_what_are_collections">Folder Collections Explained</a></li>
+</ul>
+<P>
+Additional information relating to collection lists is also available in
+Alpine's online
+help:
+<ul>
+ <li><a href="h_collection_maint">Setup Collection List Screen</a></li>
+ <li><a href="h_collection_screen">Collection List Screen</a></li>
+</ul>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_mainhelp_folders ======
+<HTML>
+<HEAD>
+<TITLE>Folders</TITLE>
+</HEAD>
+<BODY>
+<H1>Folders</H1>
+
+Messages can quickly accumulate in your INBOX folder. If you use email
+often, you soon could have hundreds. You need to delete messages you do
+not want, and you can use folders to organize messages you wish to save. A
+folder is a collection of one or more messages that are stored (just like
+the messages in your INBOX) so you can access and manage them.
+
+<P>
+You can organize your email messages into different folders by topic,
+correspondent, date, or any other category that is meaningful to you. You
+can create your own folders, and Alpine automatically provides three:
+<ul>
+ <li>The INBOX folder: messages sent to you are listed in this folder.
+ When you first start Alpine and go to the Message Index screen, you are
+looking at the list of messages in your INBOX folder. Every incoming
+message remains in your INBOX until you delete it or save it in another
+folder. </li>
+ <li>The sent-mail folder: copies of messages you send are stored in this
+folder. This is
+convenient if you cannot remember whether you actually sent a message and want
+to check, or
+if you want to send a message again.</li>
+ <li>The saved-messages folder: copies of messages you save are stored in this
+folder
+unless you choose to save them to other folders you create yourself.</li>
+</ul>
+
+<P>
+More information about folders is available in Alpine's online help:
+<ul>
+ <li><a href="h_folder_open">Explanation of Folder Selection</a></li>
+ <li><a href="h_folder_maint">Help for Folder List</a></li>
+ <li><a href="h_valid_folder_names">Explanation of Valid Folder Names</a></li>
+ <li><a href="h_folder_fcc">Folder Select for Fcc ("sent-mail")
+Explained</a></li>
+ <li><a href="h_folder_save">Folder Select for Save Explained</a></li>
+</ul>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_mainhelp_color ======
+<HTML>
+<HEAD>
+<TITLE>Color</TITLE>
+</HEAD>
+<BODY>
+<H1>Color</H1>
+
+If the terminal emulator you are using is capable of displaying color or if
+you are using PC-Alpine, then it is possible to set up Alpine so that various
+parts of the display will be shown in colors you configure. This is done
+using the Setup Color screen, available from the MAIN MENU by selecting
+the Setup command followed by &quot;K&quot; for Kolor (because &quot;C&quot;
+stands for Config in this context).
+
+<P>
+For example, you may color things like the titlebar, the current item,
+the keymenu, and the status messages.
+You may also color lines in the index, and headers and quoted text in the
+MESSAGE TEXT screen.
+You use the Color Setup screen for configuring most of this, but you must
+use the IndexColor setup for coloring whole index lines.
+These are available from the MAIN MENU under Setup/Kolor and Setup/Rules/IndexColor.
+
+<P>
+The following entries in Alpine's online help provide additional information
+about how to use color:
+<UL>
+ <LI> <A HREF="h_color_setup">Color Setup screen</A>
+ <LI> <A HREF="h_rules_incols">Index Line Color</A>
+ <LI> <A HREF="h_config_quote_color">quoted text</A> in message view
+ <LI> <A HREF="h_config_customhdr_pattern">text associated with user-defined headers</A> in message view
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_mainhelp_mouse ======
+<HTML>
+<HEAD>
+<TITLE>Using a Mouse</TITLE>
+</HEAD>
+<BODY>
+<H1>Using a Mouse</H1>
+
+If you are using PC-Alpine mouse support is turned on automatically.
+If you are using UNIX Alpine within an X terminal window or within
+a terminal emulator that supports an xterm-style mouse, then you may
+turn on support for the mouse with the feature
+<A HREF="h_config_enable_mouse"><!--#echo var="FEAT_enable-mouse-in-xterm"--></A>.
+For UNIX Alpine you will also need to set the $DISPLAY environment variable.
+<P>
+PC-Alpine offers considerable mouse support. You can view what is
+&quot;clickable&quot; by dragging your mouse over any screen; when the
+arrow cursor changes into a hand, you found something. Mouse-click
+possibilities include navigating between screens and folders and
+double-clicking on hyperlinks to open your Web browser.
+Context-sensitive pop-up menus appear with a right-click on your PC-Alpine
+screen. Examples of right-click options include &quot;copy&quot; after
+selecting text to copy and &quot;View in New Window&quot; when you click
+on a particular message in the Message Index. The menu choices available
+to you will vary based upon what screen is open, where on the screen your
+cursor is located, and even what action you have already taken.
+Within a folder, you may set the &quot;Important&quot; flag on any
+message.
+<P>
+X terminal mouse support is more limited but still quite powerful.
+As with PC-Alpine, clicking on any of the commands in the keymenu at
+the bottom of the screen will execute that command as if you typed it.
+Double-clicking on a link, for example the link to the
+<!--#echo var="FEAT_enable-mouse-in-xterm"--> feature in the paragraph above,
+will take you to that link.
+Double-clicking on an index line will view the message, and so on.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_mainhelp_keywords ======
+<HTML>
+<HEAD>
+<TITLE>Keywords</TITLE>
+</HEAD>
+<BODY>
+<H1>Keywords</H1>
+
+Within a folder, you may set the &quot;Important&quot; flag on any
+message.
+This doesn't have any system-defined meaning and is only called
+the Important flag because many users use it to signify that a message
+is important to them in some way.
+<P>
+You may also define your own set of keywords.
+You might know these as user defined flags or as labels.
+These are similar to the Important flag but you choose the names for yourself.
+<P>
+Alpine will only display keywords that
+have been added by you in the Flag Details screen or
+that have been configured by you using the Setup/Config option
+<A HREF="h_config_keywords"><!--#echo var="VAR_keywords"--></A>.
+Keywords set by other means (for example, by another email client) will not
+show up in Alpine unless you configure Alpine to know about them.
+They will show up in the Flag Details screen, but will not show up, for example,
+in the index line.
+
+<P>
+The following entries in Alpine's online help provide additional information
+about how to use keywords:
+<ul>
+ <li><A HREF="h_config_keywords"><!--#echo var="VAR_keywords"--> config option</A></li>
+ <li><A HREF="h_common_flag">Flag command to set keywords</A></li>
+</ul>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_mainhelp_roles ======
+<HTML>
+<HEAD>
+<TITLE>Roles</TITLE>
+</HEAD>
+<BODY>
+<H1>Roles</H1>
+
+You may play different roles depending on who you are replying to. For
+example, if you are replying to a message addressed to help-desk you may
+be acting as a Help Desk Worker. That role may require that you use a
+different return address and/or a different signature.
+
+<P>
+To configure roles, go to the MAIN MENU and use the Setup command
+followed by &quot;Rules&quot; and then &quot;Roles&quot;.
+The following entries in Alpine's online help provide additional information
+about how to
+use roles:
+<ul>
+ <li><a href="h_rules_roles">Setup Roles Screen</a></li>
+ <li><a href="h_role_select">Roles Screen</a></li>
+</ul>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_mainhelp_filtering ======
+<HTML>
+<HEAD>
+<TITLE>Filtering</TITLE>
+</HEAD>
+<BODY>
+<H1>Filtering</H1>
+
+The software that actually delivers mail (the stuff that happens
+before Alpine is involved) for you is in a better position to do mail filtering
+than Alpine itself.
+If possible, you may want to look into using that sort of mail filtering to
+deliver mail to different folders, delete it, or forward it.
+However, if you'd like Alpine to help with this, Alpine's filtering is for you.
+
+<P>
+Filtering is a way to automatically move certain messages from one folder
+to another or to automatically delete messages.
+You may also automatically set the state (Important, New, Deleted, Answered) of messages
+and set <A HREF="h_config_keywords"><!--#echo var="VAR_keywords"--></A> for messages.
+Alpine doesn't have the ability to forward mail to another address or
+to deliver vacation messages.
+
+<P>
+To configure filtering, go to the MAIN MENU and use the Setup command
+followed by &quot;Rules&quot; and then &quot;Filters&quot;.
+The following entries in Alpine's online help provide additional information
+about how to use filtering:
+<UL>
+ <LI> <A HREF="h_rules_filter">Filtering Setup screen</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_mainhelp_patterns ======
+<HTML>
+<HEAD>
+<TITLE>Patterns</TITLE>
+</HEAD>
+<BODY>
+<H1>Patterns</H1>
+
+Patterns are used with Roles, Filtering, Index Coloring,
+Scoring, Other Rules, and Search Rules, so it may help you to understand exactly how Patterns work.
+The following entries in Alpine's online help provide information
+about using Patterns:
+<UL>
+ <LI> <A HREF="h_rule_patterns">Patterns</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_mainhelp_cmdlineopts ======
+<HTML>
+<HEAD>
+<TITLE>Command Line Options</TITLE>
+</HEAD>
+<BODY>
+<H1>Command Line Options</H1>
+
+Alpine accepts a number of command line arguments, allowing you, for
+example, to start Alpine and immediately access a particular folder.
+Many of these arguments overlap with options in the Alpine configuration file.
+If there is a difference, then an option set on the command line takes
+precedence.
+Alpine expects command line arguments (other than addresses) to be
+preceded by a &quot;-&quot; (dash) as normally used by UNIX programs.
+A <a href="h_command_line_options">full list</a> of command line
+possibilities is available.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_mainhelp_config ======
+<HTML>
+<HEAD>
+<TITLE>Alpine Configuration</TITLE>
+</HEAD>
+<BODY>
+<H1>Alpine Configuration</H1>
+
+Unless it has been administratively disabled, the Setup command on the
+MAIN MENU has several subcommands that allow you to modify Alpine's behavior.
+The possible subcommands are for general Configuration settings,
+Printer settings, Changing your Password, Signature setup,
+AddressBook setup, Collection Lists setup, Rules (including Roles, Filters,
+Scores, Search, Indexcolor, and Other rules), LDAP Directory setup,
+and Color configuration.
+In particular, the &quot;Config&quot; subcommand has many features you may
+set or unset and many other configuration variables that may be set to change
+the way Alpine works.
+Every one of the hundreds of options available in that configuration settings
+screen has help text associated with it.
+You may read that text by moving the cursor to highlight the option and then
+typing the Help command.
+<P>
+These settings are stored in your personal
+&quot;pinerc&quot; configuration file (or, optionally, they may be stored
+<A HREF="h_config_remote_config">remotely</A>),
+but on shared systems these settings
+may be over-ridden by a system-wide control file (due to local site
+security or support policies). A global pine configuration file can also
+be used to set default values for all Alpine users on a particular system.
+Power users may be interested in splitting their personal configuration
+data into two pieces, a generic piece and
+<A HREF="h_config_exceptions">exceptions</A> which apply to
+a particular platform.
+They may also be interested in <A HREF="h_config_inheritance">configuration inheritance</A>.
+General Alpine configuration information can be found
+<A HREF="h_news_config">here</A>.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_mainhelp_aggops ======
+<HTML>
+<HEAD>
+<TITLE>Aggregate Operations</TITLE>
+</HEAD>
+<BODY>
+<H1>Aggregate Operations</H1>
+
+When you are in the MESSAGE INDEX, the available commands
+(for example, Delete, Undelete, Save, Reply, and so on)
+normally act on a single message.
+So, for example, if you press the Delete command, the currently highlighted
+message is marked Deleted.
+These commands that normally act on a single message may be applied to
+several messages at once instead.
+<P>
+By default this feature is turned on, but it could be administratively turned
+off to reduce complexity.
+The feature
+<A HREF="h_config_enable_agg_ops"><!--#echo var="FEAT_enable-aggregate-command-set"--></A>
+in the Setup/Config screen is used to turn it off or on.
+When this feature is turned on, the four commands &quot;Select&quot;,
+&quot;SelectCur&quot;, &quot;ZoomMode&quot;, and &quot;Apply&quot;
+are available.
+The two selection commands allow you to mark a set of
+messages as being &quot;selected&quot;.
+The &quot;ZoomMode&quot; command will toggle between
+displaying only the selected messages and displaying all the messages.
+The &quot;Apply&quot; command allows you to
+apply one of the regular MESSAGE INDEX commands to all of the selected
+messages instead of to only the highlighted message.
+<P>
+An example aggregate operation would be to catch up when reading
+a news group.
+That is, get rid of all the messages in the news group so that you can
+start fresh.
+The easiest way to do this in Alpine is to use aggregate operations.
+You want to Delete all of the messages in the group.
+You could start at the top and type &quot;D&quot; once for every message.
+A much faster method is to first Select all of the messages in the group,
+and then Delete all of them.
+This would take four keystrokes:
+<P>
+<CENTER><SAMP>; a (to select all messages)</SAMP></CENTER>
+<BR>
+<CENTER><SAMP>a d (to delete all selected messages)</SAMP></CENTER>
+<P>
+Another use of Select is to use it for searching for a particular message
+or set of messages in a large folder.
+You may know that the message was From a certain user.
+You could select all messages from that user to start, and use Zoom to
+look at only those messages.
+If there were still too many messages to look at you could Narrow the
+set of messages further by selecting from all of those messages only
+the ones that were after a certain date, or contained a particular phrase
+in the Subject, or were too a particular address, and so on.
+That may be the end of what you are doing, or you may want to use Apply to
+Save or Forward or Print all of the selected messages.
+<P>
+Some related help topics are
+<UL>
+<LI> <A HREF="h_index_cmd_select">Selecting: Select and WhereIs/Select</A>,
+<LI> <A HREF="h_config_enable_agg_ops"><!--#echo var="FEAT_enable-aggregate-command-set"--></A>,
+<LI> <A HREF="h_config_auto_unselect"><!--#echo var="FEAT_auto-unselect-after-apply"--></A>.
+<LI> <A HREF="h_config_auto_unzoom"><!--#echo var="FEAT_auto-unzoom-after-apply"--></A>,
+<LI> <A HREF="h_config_auto_zoom"><!--#echo var="FEAT_auto-zoom-after-select"--></A>, and
+<LI> <A HREF="h_config_select_wo_confirm"><!--#echo var="FEAT_select-without-confirm"--></A>.
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_mainhelp_readingnews ======
+<HTML>
+<HEAD>
+<TITLE>Reading News</TITLE>
+</HEAD>
+<BODY>
+<H1>Reading News</H1>
+
+<H2>Background</H2>
+Alpine can read and post to Internet (or USENET) newsgroups, using the same
+commands as for mail. Similar to mailing lists but existing on a larger scale,
+Usenet newsgroups allow groups of people with common interests to discuss
+particular topics. You might find newsgroups related to your career, or you
+might wish to check out the online discussion among the fans of your favorite
+television show.
+
+<H2>Configuring Alpine for Reading News</H2>
+Alpine often arrives
+pre-configured by your system administrator to automatically access the
+newsgroups offered by your organization, Internet Service Provider, or
+school. PC-Alpine users, and those attempting to customize Unix Alpine, will
+need additional details on <a href="h_configuring_news">how to
+configure Alpine to read news</a>.
+
+<H2>Accessing Newsgroups</H2>
+The first step in reading news is to access the newsgroups collections
+screen from Alpine. If everything is configured properly, you should be able
+to do this by first typing L (folder List), then selecting the folder
+collection listed as "News." The actual name of this collection may differ
+from system to system.
+
+<H2>Subscribing to Newsgroups</H2>
+
+Once you have accessed the news collection, you need to subscribe to a
+newsgroup that interests you. Subscribing to a newsgroup means that Alpine
+will keep a record of the newsgroups in which you are interested and which
+articles in those newsgroups have been read.
+
+<H2>Using Newsgroups</H2>
+Alpine uses the similar commands to read news as to read mail. For example,
+the D command marks messages as Deleted (or "Dismissed," if you prefer),
+and the R command Replies to a news posting. Basically, Alpine allows you to
+read news as if it were mail, so you don't need to change the way you
+interact with Alpine.
+<P>
+There is also additional Alpine help available on
+<A HREF="h_reading_news">how to use Alpine to read news</A>.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_mainhelp_securing ======
+<HTML>
+<HEAD>
+<TITLE>Securing your Alpine Session</TITLE>
+</HEAD>
+<BODY>
+<H1>Securing your Alpine Session</H1>
+
+By default, Alpine will attempt to connect to an IMAP server on the normal
+IMAP service port (143).
+If the Alpine you are using has been built to
+support &quot;Transport Layer Security&quot; (TLS)
+and &quot;Secure Sockets Layer&quot; (SSL)
+(check by clicking <A HREF="X-Alpine-Config:">here</A>),
+and the server offers the STARTTLS capability, then a secure (encrypted)
+session will be established.
+<P>
+When you are connected to a remote folder the titlebar will contain a plus sign
+in the far right column if the connection is encrypted using TLS or SSL.
+Similarly, when you are being prompted for a password a plus sign will appear in the prompt
+if the connection is encrypted.
+
+<H2>More Information on Alpine with SSL and TLS</H2>
+<UL>
+<LI> <A HREF="h_release_tlscerts">TLS and SSL Usage Note</A> </LI>
+<LI> <A HREF="h_folder_server_syntax">/SSL</A> option for older servers which support port 993 SSL but not TLS </LI>
+<LI> <A HREF="h_config_alt_auth"><!--#echo var="FEAT_try-alternative-authentication-driver-first"--></A> feature </LI>
+<LI> <A HREF="h_config_quell_ssl_largeblocks"><!--#echo var="FEAT_quell-ssl-largeblocks"--></A> PC-Alpine feature for working around OS SSL-problems</A> </LI>
+</UL>
+<H2>Here are some other security-related features and options</H2>
+<P>
+<UL>
+<LI> <A HREF="h_config_disable_password_caching"><!--#echo var="FEAT_disable-password-caching"--></A> feature to disable password caching </LI>
+<LI> <A HREF="h_config_mailcap_params"><!--#echo var="FEAT_enable-mailcap-param-substitution"--></A> feature </LI>
+<LI> <A HREF="h_config_disable_auths"><!--#echo var="VAR_disable-these-authenticators"--></A> option </LI>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_mainhelp_problems ======
+<HTML>
+<HEAD>
+<TITLE>Reporting Problems</TITLE>
+</HEAD>
+<BODY>
+<H1>Reporting Problems</H1>
+
+We ask that you first read the relevant help screens and then seek
+assistance from your own local support staff. Once you are sure that your
+difficulty is not a local configuration problem, you might look at the
+help section explaining where to look for
+<A HREF="h_finding_help">more information</A> and where to
+get assistance.
+<P>
+
+Please note: Alpine has been adapted to several other operating systems
+besides those directly supported by the University of Washington; see:
+<P>
+<CENTER><A HREF="http://www.washington.edu/alpine/overview/non-UW.html">http://www.washington.edu/alpine/overview/non-UW.html</A></CENTER>
+
+<P>
+Inquiries about these other ports (e.g., VMS and AmigaDOS) should be
+directed to the individual or group that did the adaptation.
+<P>
+
+<ADDRESS>
+ Alpine&nbsp;Development&nbsp;Team&nbsp;&lt;alpine-contact@u.washington.edu&gt;<BR>
+ Computing&nbsp;&amp;&nbsp;Communications<BR>
+ University&nbsp;of&nbsp;Washington<BR>
+ Seattle,&nbsp;WA&nbsp;98195<BR>
+</ADDRESS>
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_main_menu_commands ======
+<HTML>
+<HEAD>
+<TITLE>MAIN MENU COMMANDS</TITLE>
+</HEAD>
+<BODY>
+<H1>MAIN MENU COMMANDS</H1>
+
+<!--chtml if pinemode="function_key"-->
+&nbsp;Available&nbsp;Commands&nbsp;--
+&nbsp;Group&nbsp;1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Available&nbsp;Commands&nbsp;--&nbsp;Group&nbsp;2<BR>
+&nbsp;------------------------------
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;--------------------
+----------<BR>
+&nbsp;F1&nbsp;&nbsp;Show&nbsp;this&nbsp;help&nbsp;text&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F1&nbsp;&nbsp;Show&nbsp;this&nbsp;help&nbsp;text<BR>
+&nbsp;F2&nbsp;&nbsp;Show&nbsp;all&nbsp;other&nbsp;available&nbsp;commands&nbsp;&nbsp;&nbsp;F2&nbsp;&nbsp;Show&nbsp;other&nbsp;commands<BR>
+&nbsp;F3&nbsp;&nbsp;Quit&nbsp;Alpine<BR>
+&nbsp;F4&nbsp;&nbsp;Execute&nbsp;current&nbsp;MAIN&nbsp;MENU&nbsp;command&nbsp;&nbsp;&nbsp;F4&nbsp;&nbsp;<A
+HREF="h_common_compose">Compose</A>&nbsp;a&nbsp;message<BR>
+&nbsp;F5&nbsp;&nbsp;Select&nbsp;previous&nbsp;command&nbsp;up&nbsp;on&nbsp;menu&nbsp;&nbsp;F5&nbsp;&nbsp;<A
+HREF="h_common_folders">FOLDER&nbsp;LIST</A>&nbsp;screen<BR>
+&nbsp;F6&nbsp;&nbsp;Select&nbsp;next&nbsp;command&nbsp;down&nbsp;on&nbsp;menu&nbsp;&nbsp;&nbsp;&nbsp;F6&nbsp;&nbsp;<A
+HREF="h_common_goto">Goto</A>&nbsp;a&nbsp;specified&nbsp;folder<BR>
+&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;F7&nbsp;&nbsp;<A
+HREF="h_common_index">MESSAGE&nbsp;INDEX</A>&nbsp;screen<BR>
+&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;F8&nbsp;&nbsp;<A
+HREF="h_main_journal">Journal</A>&nbsp;of&nbsp;status&nbsp;messages<BR>
+&nbsp;F9&nbsp;&nbsp;Display&nbsp;<A
+HREF="h_main_release_notes">Release&nbsp;Notes</A>&nbsp;notes&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F9&nbsp;&nbsp;<A
+HREF="h_main_setup">SETUP</A>&nbsp;menus<BR>
+&nbsp;F10&nbsp;<A
+HREF="h_main_kblock">Lock&nbsp;Keyboard</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F10&nbsp;<A
+HREF="h_main_addrbook">ADDRESS&nbsp;BOOK</A>&nbsp;screen<BR>
+&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;F11&nbsp;<A
+HREF="h_common_role">Compose&nbsp;message&nbsp;using&nbsp;a&nbsp;role</a><BR>
+<!--chtml else-->
+&nbsp;General&nbsp;Alpine&nbsp;Commands&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Main&nbsp;Menu&nbsp;Screen&nbsp;Commands<BR>
+&nbsp;---------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;--------------------------<BR>
+&nbsp;?&nbsp;&nbsp;Show&nbsp;Help&nbsp;Text&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;O&nbsp;&nbsp;Show&nbsp;all&nbsp;Other&nbsp;available&nbsp;commands<BR>
+&nbsp;C&nbsp;&nbsp;<A
+HREF="h_common_compose">Compose</A>&nbsp;a&nbsp;message&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;P&nbsp;&nbsp;Select&nbsp;Previous&nbsp;command&nbsp;up&nbsp;on&nbsp;menu<BR>
+&nbsp;I&nbsp;&nbsp;<A
+HREF="h_common_index">MESSAGE&nbsp;INDEX</A>&nbsp;screen&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;N&nbsp;&nbsp;Select&nbsp;Next&nbsp;command&nbsp;down&nbsp;on&nbsp;menu<BR>
+&nbsp;L&nbsp;&nbsp;<A
+HREF="h_common_folders">FOLDER&nbsp;LIST</A>&nbsp;screen&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;R&nbsp;&nbsp;Display&nbsp;Alpine&nbsp;<A HREF="h_main_release_notes">Release&nbsp;Notes</A><BR>
+&nbsp;A&nbsp;&nbsp;<A
+HREF="h_main_addrbook">ADDRESS&nbsp;BOOK</A>&nbsp;screen&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;K&nbsp;&nbsp;<A
+HREF="h_main_kblock">Lock&nbsp;Keyboard</A><BR>
+&nbsp;S&nbsp;&nbsp;<A
+HREF="h_main_setup">SETUP</A>&nbsp;functions&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;G&nbsp;&nbsp;<A
+HREF="h_common_goto">Goto</A>&nbsp;a&nbsp;specified&nbsp;folder<BR>
+&nbsp;Q&nbsp;&nbsp;Quit&nbsp;Alpine&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;J&nbsp;&nbsp;<A HREF="h_main_journal">Journal</A>&nbsp;of&nbsp;status&nbsp;messages<BR>
+&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;<A
+HREF="h_common_role">Compose&nbsp;message&nbsp;using&nbsp;a&nbsp;role</a><BR>
+<!--chtml endif-->
+
+<P>
+NOTE:
+<OL>
+ <LI>For help on a particular command, highlight the bold text associated
+with it above and hit Return.
+ <LI> The availability of certain commands (e.g. some of the options under
+SETUP) is determined by Alpine configuration files and system capabilities.
+At some sites, certain commands may not be available due to security or
+support concerns.
+</OL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+
+===== h_command_line_options ======
+<HTML>
+<HEAD>
+<TITLE>COMMAND LINE OPTIONS</TITLE>
+</HEAD>
+<BODY>
+<H1>COMMAND LINE OPTIONS</H1>
+Possible starting arguments for Alpine:
+
+<DL COMPACT>
+
+<DT> <EM>[addresses]</EM>
+
+<DD> Send-to: If you give <EM>Alpine</EM> an argument or arguments which
+do not begin with a dash, <EM>Alpine</EM> treats them as email addresses.
+<EM>Alpine</EM> will startup in
+the composer with a message started to the addresses specified.
+Once the message is sent, the <EM>Alpine</EM> session closes.
+Standard input redirection is allowed.
+Separate multiple addresses with a space between them.
+Addresses are placed in the &quot;To&quot; field only.
+<P>
+
+<DT> &lt; <EM>file</EM>
+
+<DD> <EM>Alpine</EM> will startup in the composer with <EM>file</EM> read
+into the body of the message.
+Once the message is sent, the <EM>Alpine</EM> session closes.
+<P>
+
+<DT> -attach <EM>file</EM>
+
+<DD> Go directly into composer with given file attached.
+<P>
+
+<DT> -attachlist <EM>file-list</EM>
+
+<DD> Go directly into composer with given files attached.
+This must be the last option on the command line.
+<P>
+
+<DT> -attach_and_delete <EM>file</EM>
+
+<DD> Go directly into composer with given file attached, delete when finished.
+<P>
+
+<DT> -aux <EM>local_directory</EM>
+
+<DD> <EM>PC-Alpine</EM> only.
+This tells <EM>PC-Alpine</EM> the local directory to use for storing auxiliary
+files, like debug files, address books, and signature files. The pinerc may
+be local or remote.
+<P>
+
+<DT> -bail
+
+<DD> If the personal configuration file doesn't already exist, exit.
+This might be useful if the configuration file is accessed using some
+remote filesystem protocol. If the remote mount is missing this will cause
+<EM>Alpine</EM> to quit instead of creating a new pinerc.
+<P>
+
+<DT> -c <EM>n</EM>
+
+<DD> When used with the <CODE>-f</CODE> option, apply the <EM>n</EM>th context.
+This is used when there are multiple folder collections (contexts) and you
+want to open a folder not in the primary collection.
+<P>
+
+<DT> -conf
+
+<DD> Configuration: Prints a sample system configuration file to the
+screen or standard output. To generate an initial system configuration
+file, execute
+
+<PRE><CODE>
+ pine -conf > <!--#echo var="PINE_CONF_PATH"-->
+</CODE></PRE>
+<P>
+
+To generate a system configuration file using settings from an old
+system configuration file, execute
+
+<PRE><CODE>
+ pine -P old-pine.conf -conf > <!--#echo var="PINE_CONF_PATH"-->
+</CODE></PRE>
+<P>
+A system configuration file is not required.
+<P>
+
+<DT> -copy_abook &lt;<EM>local_abook_file</EM>&gt; &lt;<EM>remote_abook_folder</EM>&gt;
+
+<DD> Copy an address book file to a remote address book folder.
+If the remote folder doesn't exist, it will be created.
+If it exists but the first message in the folder isn't a remote address
+book header message, the copy will be aborted.
+This flag will not usually be used by a user.
+Instead, the user will create a remote address book from within <EM>Alpine</EM>
+and copy entries from the local address book by using aggregate Save in
+the address book screen.
+<P>
+
+<DT> -copy_pinerc &lt;<EM>local_pinerc_file</EM>&gt; &lt;<EM>remote_pinerc_folder</EM>&gt;
+
+<DD> Copy a pinerc configuration file to a remote pinerc folder.
+If the remote folder doesn't exist, it will be created.
+If it exists but the first message in the folder isn't a remote pinerc
+header message, the copy will be aborted.
+This flag may be useful to users who already have a local pinerc file and
+would like to convert it to a remote pinerc folder and use that instead.
+This gives a way to bootstrap that conversion without having to manually
+reset all of the variables in the remote pinerc folder.
+<P>
+
+<DT> -d <EM>debug-level</EM>
+
+<DD> Debug Level: Sets the level of debugging information written by
+<EM>Alpine</EM>.
+<EM>debug-level</EM> can be set to any integer 0-9.
+A debug level of 0 turns off debugging for the session.
+(Actually there are some levels higher than 9, but you probably don't
+want to see them.)
+<P>
+
+<DT> -d <EM>keywords</EM>
+
+<DD> You may use a more detailed version of the debugging flag to set
+the debug level in separate parts of <EM>Alpine</EM>.
+The possibilities are flush, timestamp, imap=0..4, tcp, numfiles=0..31, and
+verbose=0..9.
+<EM>Flush</EM> causes debugging information to be flushed immediately to
+the debug file as it is written.
+<EM>Verbose</EM> is the general debugging verbosity level.
+<EM>Timestamp</EM> causes timestamps to be added to the debug file, which
+is useful when you are trying to figure out what is responsible for delays.
+<EM>Numfiles</EM> sets the number of debug files saved.
+<EM>Imap</EM> sets the debug level for the debugging statements related
+to the conversation with the IMAP server, and more generally, for the
+debugging related to <EM>Alpine</EM>'s interaction with the C-Client library.
+<EM>Tcp</EM> turns on some TCP/IP debugging.
+<P>
+
+<DT> -f <EM>folder</EM>
+
+<DD> Startup folder: <EM>Alpine</EM> will open this folder in place
+of the standard INBOX.
+<P>
+
+<DT> -F <EM>file</EM>
+
+<DD> Open named text file for viewing and forwarding.
+<P>
+
+<DT> -h
+
+<DD> Help: Prints the list of available command-line arguments to the
+screen.
+<P>
+
+<DT> -i
+
+<DD> <EM>Alpine</EM> will start up in the FOLDER INDEX
+screen instead of the MAIN MENU.
+<P>
+
+Configuration equivalent: <EM><!--#echo var="VAR_initial-keystroke-list"-->=i</EM>.
+<P>
+
+<DT> -I <EM>a,b,c,...</EM>
+
+<DD> Initial Keystrokes: <EM>Alpine</EM> will execute this comma-separated
+sequence of commands upon startup.
+This allows users to get <EM>Alpine</EM> to start in any
+of its menus/screens.
+You cannot include any input to the composer in the initial keystrokes.
+The key &lt;Return&gt; is represented by a ``CR'' in
+the keystroke list; the spacebar is designated by the letters ``SPACE''.
+Control keys are two character sequences beginning with ``^'', such as
+``^I''.
+A tab character is ``TAB''.
+Function keys are ``F1'' - ``F12'' and the arrow keys are ``UP'',
+``DOWN'', ``LEFT'', and ``RIGHT''.
+A restriction is that you can't mix function keys and character keys in this
+list even though you can, in some cases, mix them when running <EM>Alpine</EM>.
+A user can always use only <EM>character</EM> keys in the startup list even
+if he or she is using <EM>function</EM> keys normally, or vice versa.
+If an element in this list is a string of characters surrounded by double
+quotes (&quot;) then it will be expanded into the individual characters in
+the string, excluding the double quotes.
+<P>
+
+Configuration equivalent: <EM><!--#echo var="VAR_initial-keystroke-list"--></EM>
+<P>
+
+<DT> -install
+
+<DD> For <EM>PC-Alpine</EM> only, this option prompts the user for
+some basic information to help with getting properly set up.
+<P>
+
+<DT> -k
+
+<DD> Function-Key Mode: When invoked in this way, <EM>Alpine</EM> expects
+the input of commands to be function-keys.
+Otherwise, commands are linked to the regular character keys.
+<P>
+
+Configuration equivalent: <EM><!--#echo var="FEAT_use-function-keys"--></EM> included in
+<EM>Feature-List</EM>.
+<P>
+
+<DT> -n <EM>n</EM>
+
+<DD> Message-Number: When specified, <EM>Alpine</EM> starts up in the
+FOLDER INDEX screen with the current message being the specified
+message number.
+<P>
+
+<DT> -nosplash
+
+<DD> <EM>PC-Alpine</EM> only.
+This tells <EM>PC-Alpine</EM> not to display the splash screen upon startup.
+This may be helpful for certain troubleshooting or terminal server scenarios.
+<P>
+
+<DT> -o <EM>folder</EM>
+
+<DD> Opens the INBOX (or a folder specified via the -f argument) ReadOnly.
+<P>
+
+<DT> -p <EM>pinerc</EM>
+
+<DD> Uses the named file as the personal configuration file instead of
+<EM>~/.pinerc</EM> or the default PINERC search sequence <EM>PC-Alpine</EM> uses.
+Alpinerc may be either a local file or a remote configuration folder.
+<P>
+
+<DT> -P <EM>pinerc</EM>
+
+<DD> Uses the named file as the system wide configuration file instead of
+<EM><!--#echo var="PINE_CONF_PATH"--></EM> on UNIX, or nothing on <EM>PC-Alpine</EM>.
+Alpinerc may be either a local file or a remote configuration folder.
+<P>
+
+<DT> -passfile <EM>passfile</EM>
+
+<DD> This tells <EM>Alpine</EM> what file should be used as the password file.
+This should be a fully-qualified filename.
+<P>
+
+<DT> -pinerc <EM>file</EM>
+
+<DD> Output fresh pinerc configuration to <EM>file</EM>, preserving the
+settings of variables that the user has made.
+Use <EM>file</EM> set to ``-'' to make output go to standard out.
+<P>
+
+<DT> -r
+
+<DD> Restricted Mode: For UNIX <EM>Alpine</EM> only.
+<EM>Alpine</EM> in restricted mode can only send email to itself.
+Save and export are limited.
+<P>
+
+<DT> -registry <EM>cmd</EM>
+
+<DD> For <EM>PC-Alpine</EM> only, this option affects the values of
+<EM>Alpine</EM>'s registry entries.
+Possible values for <EM>cmd</EM> are set, clear, and dump.
+<EM>Set</EM> will always reset <EM>Alpine</EM>'s registry
+entries according to its current settings.
+<EM>Clear</EM> will clear the registry values.
+<EM>Clearsilent</EM> will clear the registry values without any dialogs.
+<EM>Dump</EM> will display the values of current registry settings.
+Note that the dump command is currently disabled.
+Without the -registry option, <EM>PC-Alpine</EM> will write values into
+the registry only if there currently aren't any values set.
+<P>
+
+<DT> -sort <EM>key</EM>
+
+<DD> Sort-Key: Specifies the order messages will be displayed in for the
+FOLDER INDEX screen.
+<EM>Key</EM> can have the following values:
+arrival, date, subject, orderedsubj, thread, from, size, score, to, cc,
+arrival/reverse, date/reverse, subject/reverse, orderedsubj/reverse, thread/reverse,
+from/reverse, size/reverse, score/reverse, to/reverse, and cc/reverse.
+The default value is &quot;arrival&quot;.
+The <EM>key</EM> value reverse is equivalent to arrival/reverse.
+<P>
+
+Configuration equivalent: <EM><!--#echo var="VAR_sort-key"--></EM>.
+<P>
+
+<DT> -uninstall
+
+<DD> For <EM>PC-Alpine</EM> only, this option removes references to Alpine
+in Windows settings. The registry settings are removed and
+the password cache is cleared.
+<P>
+
+<DT> -url <EM>url</EM>
+
+<DD> Open the given URL.
+<P>
+
+<DT> -v
+
+<DD> Version: Print version information to the screen.
+<P>
+
+<DT> -x <EM>exceptions_config</EM>
+
+<DD> Configuration settings in the exceptions configuration override your normal
+default settings.
+<EM>Exceptions_config</EM> may be either a local file or a remote Alpine configuration folder.
+<P>
+
+<DT> -z
+
+<DD> Enable Suspend: When run with this flag, the key sequence ctrl-z
+will suspend the <EM>Alpine</EM> session.
+<P>
+
+Configuration equivalent: <EM><!--#echo var="FEAT_enable-suspend"--></EM> included in
+<EM>Feature-List</EM>.
+<P>
+
+<DT> -<EM>option</EM>=<EM>value</EM>
+
+<DD> Assign <EM>value</EM> to the config option <EM>option</EM>.
+For example, <EM>-signature-file=sig1</EM> or
+<EM>-Feature-List=signature-at-bottom</EM>.
+Note: Feature-List values are
+additive and features may be preceded with no- to turn them off.
+Also, as a special case, the &quot;Feature-List=&quot; part of that may be
+omitted. For example, <EM>-signature-at-bottom</EM> is equivalent to
+<EM>-Feature-List=signature-at-bottom</EM>.
+<P>
+
+</DL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_configuring_news ======
+<HTML>
+<HEAD>
+<TITLE>CONFIGURING NEWS</TITLE>
+</HEAD>
+<BODY>
+<H1>CONFIGURING NEWS</H1>
+Alpine can access news folders in any one of three different ways:
+<DL>
+<DT>REMOTE NNTP</DT>
+<DD>Using the Network News Transport Protocol (NNTP) to
+access news on a remote news server. In this case the newsrc file is
+stored on the machine where Alpine is running.
+
+<P>
+To specify a remote news-collection accessed via NNTP use the
+SETUP/collectionList screen's &quot;Add&quot; command. Set the
+Server: value to the NNTP server's hostname appended with the
+communication method &quot;/service=NNTP&quot;, and set the Path:
+value to the &quot;#news.&quot; namespace (without the quotes). See
+the &quot;<A HREF="h_composer_cntxt_server">Server:</A>&quot; field's
+help text for a more complete explanation of access method, and the
+&quot;<A HREF="h_composer_cntxt_path">Path:</A>&quot; field's help
+text for a more complete explanation of &quot;namespace&quot;.
+<P>
+Instead of specifying a news-collection, you may simply set the
+<A HREF="h_config_nntp_server">NNTP Server</A>
+option, which will cause Alpine to create a default news-collection for you.
+Another NNTP option that may be of interest is
+<A HREF="h_config_nntprange"><!--#echo var="VAR_nntp-range"--></A>.
+
+<DT>REMOTE IMAP</DT>
+<DD>Using the Internet Message Access Protocol (IMAP) to
+access news on a remote news server. In this case, your newsrc file is
+stored on the news server, in your home directory, so you must have an
+account on the news server, but you would be running Alpine on a different
+machine. The news server must be running an IMAPd server process.
+
+<P>
+To specify a remote news-collection accessed via IMAP use the
+SETUP/collectionList screen's &quot;Add&quot; command. Set the
+Server: value to the IMAP server's hostname, and set the Path: value
+to the &quot;#news.&quot; namespace (without the quotes). See the
+&quot;<A HREF="h_composer_cntxt_path">Path:</A>&quot; field's help
+text for a more complete explanation of &quot;namespace&quot;.
+
+</DD>
+
+<DT>LOCAL</DT>
+<DD>Using local file access to the news database. In this
+case, your newsrc file is stored on the news server, in your home
+directory, so you must have an account on the news server, and you would
+be running Alpine on the same machine.
+
+<P>
+To specify a local news-collection use the SETUP/collectionList
+screen's &quot;Add&quot; command. Leave the Server: value blank, and
+set the Path: value to the &quot;#news.&quot; namespace (without the
+quotes). See the &quot;<A HREF="h_composer_cntxt_path">Path:</A>&quot;
+field's help text for a more complete explanation of &quot;namespace&quot;.
+
+</DD>
+</DL>
+
+<P>
+
+NOTE: Should no news-collection be defined as above, Alpine will
+automatically create one using the Setup/Config screen's
+&quot;<!--#echo var="VAR_nntp-server"-->&quot; variable's value if defined. The collection
+will be created as a &quot;Remote NNTP&quot; as described above.
+
+<P>
+
+If you are a PC-Alpine user, either option 1 (NNTP) or option 2 (IMAP) is
+possible. If you don't have an account on the news server, or if the news
+server is not running an IMAP daemon, then you must use NNTP. (If you are not
+sure, ask your service provider, university, or company for help.) In
+this case, your Unix .newsrc file can be transferred to your PC. A good
+place to put it would be in the same directory as your PINERC file, under
+the name NEWSRC, but you can
+<A HREF="h_config_newsrc_path">specify a different location</A>
+via Alpine's Setup/Config screen.
+
+<P>
+Other configuration features related to news are
+<A HREF="h_config_8bit_nntp"><!--#echo var="FEAT_enable-8bit-nntp-posting"--></A>.
+<A HREF="h_config_compose_news_wo_conf"><!--#echo var="FEAT_compose-sets-newsgroup-without-confirm"--></A>,
+<A HREF="h_config_news_uses_recent"><!--#echo var="FEAT_news-approximates-new-status"--></A>,
+<A HREF="h_config_news_cross_deletes"><!--#echo var="FEAT_news-deletes-across-groups"--></A>,
+<A HREF="h_config_news_catchup"><!--#echo var="FEAT_news-offers-catchup-on-close"--></A>,
+<A HREF="h_config_post_wo_validation"><!--#echo var="FEAT_news-post-without-validation"--></A>,
+<A HREF="h_config_read_in_newsrc_order"><!--#echo var="FEAT_news-read-in-newsrc-order"--></A>, and
+<A HREF="h_config_quell_post_prompt"><!--#echo var="FEAT_quell-extra-post-prompt"--></A>.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_reading_news ======
+<HTML>
+<HEAD>
+<TITLE>READING NEWS</TITLE>
+</HEAD>
+<BODY>
+<H1>READING NEWS</H1>
+
+Alpine uses almost the same commands for manipulating news folders as for
+mail folders. This means, for example, that when you are done with a
+message, you would use &quot;D&quot; to mark it as Deleted (or Dismissed,
+if you prefer.) This &quot;mail-like&quot; behavior differs from that of
+most newsreaders, wherein a message is implicitly dismissed after you have
+looked at it once. We strongly believe that Alpine should offer as much
+consistency as possible between mail and news, so the mail paradigm --
+wherein a message does not magically disappear without explicit action by
+the user -- is used for news as well. <P>
+
+If you answer a message in a news folder, the index view will show the
+&quot;A&quot; flag as usual; but the industry standard file Alpine uses to
+keep track of what news as been read has no way of storing this flag, so
+it will not be preserved across sessions. The Deleted flag is the only
+one that is preserved when you leave and then return to a newsgroup. As an
+additional note on replies, when you Reply to a newsgroup message and say
+you want to reply to all recipients, Alpine will ask if you want to post the
+message to all the newsgroups listed in the original message. <P>
+
+If you would like Alpine to mark more-or-less recent news messages as
+&quot;New&quot;, then set the
+<A HREF="h_config_news_uses_recent">&quot;<!--#echo var="FEAT_news-approximates-new-status"-->&quot;</A>
+feature (which is set by default). This will cause messages after the last one you have marked as
+Deleted to appear with &quot;N&quot; status in the MESSAGE INDEX. The
+&quot;N&quot; status often makes it easier to distinguish later news
+articles from those you've previously seen, but not yet disposed of via
+the &quot;D&quot; key. Note that this is an approximation, not an exact
+record of which messages you have not seen.
+<P>
+
+A frequent operation in news-reading is &quot;catching up&quot; -- that
+is, getting rid of all the messages in the newsgroup so that you can
+&quot;start fresh.&quot; The easiest way to do this in Alpine is via the
+Select command. You would enter the following four keystrokes:
+<tt>;aad</tt> to select all messaged, and then apply the delete (or
+dismiss) command to all of them.
+
+<P>
+There are also additional details on
+<A HREF="h_configuring_news">configuring news</a>.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+
+====== h_help_index ======
+<HTML>
+<HEAD>
+<TITLE>Help Index</TITLE>
+</HEAD>
+<BODY>
+<H1>Help Index</H1>
+<ul>
+<li><a href="h_mainhelp_abooks">Address Books</a></li>
+<li><a href="h_abook_top">ADDRESS BOOK LIST COMMANDS</a>
+<li><a href="h_main_addrbook">Address Book Command</a>
+<li><a href="h_abook_view">Address Book View Explained</a>
+<li><a href="h_compose_addrcomplete">Address Completion</a>
+<li><a href="h_abook_select_listmode">Address Listmode Selection from Composer Explained</a>
+<li><a href="h_abook_select_checks">Address Selection from Composer Explained</a>
+<li><a href="h_composer_abook_comment">Addressbook Comment Explained</a>
+<li><a href="h_composer_abook_fcc">Addressbook Fcc Explained</a>
+<li><a href="h_composer_abook_add_folder">Addressbook Folder Name Field Explained</a>
+<li><a href="h_composer_abook_full">Addressbook Fullname Explained</a>
+<li><a href="h_composer_abook_add_nick">Addressbook NickName Field Explained</a>
+<li><a href="h_composer_abook_nick">Addressbook Nickname Explained</a>
+<li><a href="h_abook_select_addr">Addressbook Selection Explained</a>
+<li><a href="h_abook_select_top">Addressbook Selection Navigation Explained</a>
+<li><a href="h_composer_abook_add_server">Addressbook Server Name Field Explained</a>
+<li><a href="h_mainhelp_aggops">Aggregate Operations</a>
+<li><a href="h_mainhelp_config">Alpine Configuration</a>
+<li><a href="h_mainhelp_pinehelp">Alpine Help</a>
+<li><a href="h_news_legal">Alpine Legal Notices</a>
+<li><a href="h_compose_alted">Alt Editor Command</a>
+<li><a href="h_index_cmd_apply">Apply Command</a>
+<li><a href="h_attachment_screen">Attachment Index Screen Explained</a>
+<li><a href="h_mail_text_att_view">Attachment View Screen Explained</a>
+<li><a href="h_mainhelp_filtering">Blocking Messages</a>
+<li><a href="h_composer_browse">BROWSER</a>
+<li><a href="h_common_bounce">Bounce Command</a>
+<li><a href="h_compose_cancel">Cancel Command</a>
+<li><a href="h_config_change_your_from">Changing your From Address</a>
+<li><a href="h_collection_screen">COLLECTION LIST screen</a>
+<li><a href="h_mainhelp_color">Color</a>
+<li><a href="h_composer_ctrl_j">COMPOSER ATTACH</a>
+<li><a href="h_composer">COMPOSER COMMANDS</a>
+<li><a href="h_mainhelp_composing">Composing Messages</a>
+<li><a href="h_mainhelp_collections">Collection Lists</a>
+<li><a href="h_composer_cntxt_nick">Collection Nickname Explained</a>
+<li><a href="h_composer_cntxt_path">Collection Path: Explained</a>
+<li><a href="h_composer_cntxt_server">Collection Server: Explained</a>
+<li><a href="h_composer_cntxt_view">Collection View: Explained</a>
+<li><a href="h_mainhelp_cmdlineopts">Command Line Options</a>
+<li><a href="h_common_compose">Compose Command</a>
+<li><a href="h_edit_nav_cmds">Composer Editing Commands Explained</a>
+<li><a href="h_common_conditional_cmds">Conditional Commands</a>
+<li><a href="h_reply_token_conditionals">Conditional Inclusion of Text for <!--#echo var="VAR_reply-leadin"-->, Signatures, and Templates</a>
+<li><a href="h_mainhelp_config">Configuration</a>
+<li><a href="h_composer_custom_free">CUSTOMIZED HEADER FIELD</a>
+<li><a href="h_config_dflt_color">Default Color</a>
+<li><a href="h_common_delete">Delete and Undelete Commands</a>
+<li><a href="h_composer_qserv_cn">Directory Query Form Explained</a>
+<li><a href="h_special_list_commands">Email List Commands Explained</a>
+<li><a href="h_composer_search">Explanation of Composer Whereis Command </a>
+<li><a href="h_folder_open">Explanation of Folder Selection</a>
+<li><a href="h_special_xon_xoff">Explanation of Alpine's XOFF/XON Handling</a>
+<li><a href="h_valid_folder_names">Explanation of Valid Folder Names</a>
+<li><a href="h_ge_export">Export File Selection</a>
+<li><a href="h_ge_allparts">Export Message File Selection</a>
+<li><a href="h_index_cmd_expunge">Expunge/Exclude Command</a>
+<li><a href="h_info_on_locking">FAQs on Alpine Locking</a>
+<li><a href="h_config_allow_chg_from">FEATURE: <!--#echo var="FEAT_allow-changing-from"--></a>
+<li><a href="h_config_allow_talk">FEATURE: <!--#echo var="FEAT_allow-talk"--></a>
+<li><a href="h_config_alt_compose_menu">FEATURE: <!--#echo var="FEAT_alternate-compose-menu"--></a>
+<li><a href="h_config_alt_role_menu">FEATURE: <!--#echo var="FEAT_alternate-role-menu"--></a>
+<li><a href="h_config_force_low_speed">FEATURE: <!--#echo var="FEAT_assume-slow-link"--></a>
+<li><a href="h_config_auto_read_msgs">FEATURE: <!--#echo var="FEAT_auto-move-read-msgs"--></a>
+<li><a href="h_config_auto_open_unread">FEATURE: <!--#echo var="FEAT_auto-open-next-unread"--></a>
+<li><a href="h_config_auto_unselect">FEATURE: <!--#echo var="FEAT_auto-unselect-after-apply"--></a>
+<li><a href="h_config_auto_unzoom">FEATURE: <!--#echo var="FEAT_auto-unzoom-after-apply"--></a>
+<li><a href="h_config_auto_zoom">FEATURE: <!--#echo var="FEAT_auto-zoom-after-select"--></a>
+<li><a href="h_config_use_boring_spinner">FEATURE: <!--#echo var="FEAT_busy-cue-spinner-only"--></a>
+<li><a href="h_config_check_mail_onquit">FEATURE: <!--#echo var="FEAT_check-newmail-when-quitting"--></a>
+<li><a href="h_config_combined_abook_display">FEATURE: <!--#echo var="FEAT_combined-addrbook-display"--></a>
+<li><a href="h_config_combined_folder_display">FEATURE: <!--#echo var="FEAT_combined-folder-display"--></a>
+<li><a href="h_config_combined_subdir_display">FEATURE: <!--#echo var="FEAT_combined-subdirectory-display"--></a>
+<li><a href="h_config_cancel_confirm">FEATURE: <!--#echo var="FEAT_compose-cancel-confirm-uses-yes"--></a>
+<li><a href="h_config_lame_list_mode">FEATURE: <!--#echo var="FEAT_enable-lame-list-mode"--></a>
+<li><a href="h_config_compose_rejects_unqual">FEATURE: <!--#echo var="FEAT_compose-rejects-unqualified-addrs"--></a>
+<li><a href="h_config_send_filter_dflt">FEATURE: <!--#echo var="FEAT_compose-send-offers-first-filter"--></a>
+<li><a href="h_config_compose_news_wo_conf">FEATURE: <!--#echo var="FEAT_compose-sets-newsgroup-without-confirm"--></a>
+<li><a href="h_config_del_from_dot">FEATURE: <!--#echo var="FEAT_compose-cut-from-cursor"--></a>
+<li><a href="h_config_compose_maps_del">FEATURE: <!--#echo var="FEAT_compose-maps-delete-key-to-ctrl-d"--></a>
+<li><a href="h_config_confirm_role">FEATURE: <!--#echo var="FEAT_confirm-role-even-for-default"--></a>
+<li><a href="h_config_tab_no_prompt">FEATURE: <!--#echo var="FEAT_continue-tab-without-confirm"--></a>
+<li><a href="h_config_dates_to_local">FEATURE: <!--#echo var="FEAT_convert-dates-to-localtime"--></a>
+<li><a href="h_config_copy_to_to_from">FEATURE: <!--#echo var="FEAT_copy-to-address-to-from-if-it-is-us"--></a>
+<li><a href="h_config_del_skips_del">FEATURE: <!--#echo var="FEAT_delete-skips-deleted"--></a>
+<li><a href="h_config_disable_config_cmd">FEATURE: <!--#echo var="FEAT_disable-config-cmd"--></a>
+<li><a href="h_config_disable_index_locale_dates">FEATURE: <!--#echo var="FEAT_disable-index-locale-dates"--></a>
+<li><a href="h_config_disable_kb_lock">FEATURE: <!--#echo var="FEAT_disable-keyboard-lock-cmd"--></a>
+<li><a href="h_config_blank_keymenu">FEATURE: <!--#echo var="FEAT_disable-keymenu"--></a>
+<li><a href="h_config_disable_password_caching">FEATURE: <!--#echo var="FEAT_disable-password-caching"--></a>
+<li><a href="h_config_disable_password_cmd">FEATURE: <!--#echo var="FEAT_disable-password-cmd"--></a>
+<li><a href="h_config_disable_pipes_in_sigs">FEATURE: <!--#echo var="FEAT_disable-pipes-in-sigs"--></a>
+<li><a href="h_config_disable_pipes_in_templates">FEATURE: <!--#echo var="FEAT_disable-pipes-in-templates"--></a>
+<li><a href="h_config_disable_regex">FEATURE: <!--#echo var="FEAT_disable-regular-expression-matching-for-alternate-addresses"--></a>
+<li><a href="h_config_disable_roles_setup">FEATURE: <!--#echo var="FEAT_disable-roles-setup-cmd"--></a>
+<li><a href="h_config_disable_roles_sigedit">FEATURE: <!--#echo var="FEAT_disable-roles-sig-edit"--></a>
+<li><a href="h_config_disable_roles_templateedit">FEATURE: <!--#echo var="FEAT_disable-roles-template-edit"--></a>
+<li><a href="h_config_input_history">FEATURE: <!--#echo var="FEAT_disable-save-input-history"--></a>
+<li><a href="h_config_disable_collate">FEATURE: <!--#echo var="FEAT_disable-setlocale-collate"--></a>
+<li><a href="h_config_disable_shared">FEATURE: <!--#echo var="FEAT_disable-shared-namespaces"--></a>
+<li><a href="h_config_disable_signature_edit">FEATURE: <!--#echo var="FEAT_disable-signature-edit-cmd"--></a>
+<li><a href="h_config_take_fullname">FEATURE: <!--#echo var="FEAT_disable-take-fullname-in-addresses"--></a>
+<li><a href="h_config_take_lastfirst">FEATURE: <!--#echo var="FEAT_disable-take-last-comma-first"--></a>
+<li><a href="h_config_disable_reset_disp">FEATURE: <!--#echo var="FEAT_disable-terminal-reset-for-display-filters"--></a>
+<li><a href="h_config_disable_sender">FEATURE: <!--#echo var="FEAT_disable-sender"--></a>
+<li><a href="h_config_quell_dead_letter">FEATURE: <!--#echo var="FEAT_quell-dead-letter-on-cancel"--></a>
+<li><a href="h_config_quell_flowed_text">FEATURE: <!--#echo var="FEAT_quell-flowed-text"--></a>
+<li><a href="h_downgrade_multipart_to_text">FEATURE: <!--#echo var="FEAT_downgrade-multipart-to-text"--></a>
+<li><a href="h_config_8bit_smtp">FEATURE: <!--#echo var="FEAT_enable-8bit-esmtp-negotiation"--></a>
+<li><a href="h_config_8bit_nntp">FEATURE: <!--#echo var="FEAT_enable-8bit-nntp-posting"--></a>
+<li><a href="h_config_enable_agg_ops">FEATURE: <!--#echo var="FEAT_enable-aggregate-command-set"--></a>
+<li><a href="h_config_enable_alt_ed">FEATURE: <!--#echo var="FEAT_enable-alternate-editor-cmd"--></a>
+<li><a href="h_config_alt_ed_now">FEATURE: <!--#echo var="FEAT_enable-alternate-editor-implicitly"--></a>
+<li><a href="h_config_arrow_nav">FEATURE: <!--#echo var="FEAT_enable-arrow-navigation"--></a>
+<li><a href="h_config_relaxed_arrow_nav">FEATURE: <!--#echo var="FEAT_enable-arrow-navigation-relaxed"--></a>
+<li><a href="h_config_compose_bg_post">FEATURE: <!--#echo var="FEAT_enable-background-sending"--></a>
+<li><a href="h_config_enable_bounce">FEATURE: <!--#echo var="FEAT_enable-bounce-cmd"--></a>
+<li><a href="h_config_cruise_mode">FEATURE: <!--#echo var="FEAT_enable-cruise-mode"--></a>
+<li><a href="h_config_cruise_mode_delete">FEATURE: <!--#echo var="FEAT_enable-cruise-mode-delete"--></a>
+<li><a href="h_config_compose_dsn">FEATURE: <!--#echo var="FEAT_enable-delivery-status-notification"--></a>
+<li><a href="h_config_enable_dot_files">FEATURE: <!--#echo var="FEAT_enable-dot-files"--></a>
+<li><a href="h_config_enable_lessthan_exit">FEATURE: <!--#echo var="FEAT_enable-exit-via-lessthan-command"--></a>
+<li><a href="h_config_fast_recent">FEATURE: <!--#echo var="FEAT_enable-fast-recent-test"--></a>
+<li><a href="h_config_enable_flag">FEATURE: <!--#echo var="FEAT_enable-flag-cmd"--></a>
+<li><a href="h_config_flag_screen_default">FEATURE: <!--#echo var="FEAT_enable-flag-screen-implicitly"--></a>
+<li><a href="h_config_flag_screen_kw_shortcut">FEATURE: <!--#echo var="FEAT_enable-flag-screen-keyword-shortcut"--></a>
+<li><a href="h_config_enable_full_hdr_and_text">FEATURE: <!--#echo var="FEAT_enable-full-header-and-text"--></a>
+<li><a href="h_config_enable_full_hdr">FEATURE: <!--#echo var="FEAT_enable-full-header-cmd"--></a>
+<li><a href="h_config_allow_goto">FEATURE: <!--#echo var="FEAT_enable-goto-in-file-browser"--></a>
+<li><a href="h_config_enable_dot_folders">FEATURE: <!--#echo var="FEAT_enable-dot-folders"--></a>
+<li><a href="h_config_enable_incoming">FEATURE: <!--#echo var="FEAT_enable-incoming-folders"--></a>
+<li><a href="h_config_enable_incoming_checking">FEATURE: <!--#echo var="FEAT_enable-incoming-folders-checking"--></a>
+<li><a href="h_config_enable_jump">FEATURE: <!--#echo var="FEAT_enable-jump-shortcut"--></a>
+<li><a href="h_config_show_delay_cue">FEATURE: <!--#echo var="FEAT_enable-mail-check-cue"--></a>
+<li><a href="h_config_mailcap_params">FEATURE: <!--#echo var="FEAT_enable-mailcap-param-substitution"--></a>
+<li><a href="h_config_enable_mouse">FEATURE: <!--#echo var="FEAT_enable-mouse-in-xterm"--></a>
+<li><a href="h_config_enable_view_addresses">FEATURE: <!--#echo var="FEAT_enable-msg-view-addresses"--></a>
+<li><a href="h_config_enable_view_attach">FEATURE: <!--#echo var="FEAT_enable-msg-view-attachments"--></a>
+<li><a href="h_config_enable_view_arrows">FEATURE: <!--#echo var="FEAT_enable-msg-view-forced-arrows"--></a>
+<li><a href="h_config_enable_view_url">FEATURE: <!--#echo var="FEAT_enable-msg-view-urls"--></a>
+<li><a href="h_config_enable_view_web_host">FEATURE: <!--#echo var="FEAT_enable-msg-view-web-hostnames"--></a>
+<li><a href="h_config_enable_mulnewsrcs">FEATURE: <!--#echo var="FEAT_enable-multiple-newsrcs"--></a>
+<li><a href="h_config_enable_xterm_newmail">FEATURE: <!--#echo var="FEAT_enable-newmail-in-xterm-icon"--></a>
+<li><a href="h_config_enable_newmail_short_text">FEATURE: <!--#echo var="FEAT_enable-newmail-short-text-in-icon"--></a>
+<li><a href="h_config_sub_lists">FEATURE: <!--#echo var="FEAT_enable-partial-match-lists"--></a>
+<li><a href="h_config_enable_y_print">FEATURE: <!--#echo var="FEAT_enable-print-via-y-command"--></a>
+<li><a href="h_config_prefix_editing">FEATURE: <!--#echo var="FEAT_enable-reply-indent-string-editing"--></a>
+<li><a href="h_config_enable_search_and_repl">FEATURE: <!--#echo var="FEAT_enable-search-and-replace"--></a>
+<li><a href="h_config_sigdashes">FEATURE: <!--#echo var="FEAT_enable-sigdashes"--></a>
+<li><a href="h_config_new_thread_blank_subject">FEATURE: <!--#echo var="FEAT_new-thread-on-blank-subject"--></a>
+<li><a href="h_config_can_suspend">FEATURE: <!--#echo var="FEAT_enable-suspend"--></a>
+<li><a href="h_config_enable_tab_complete">FEATURE: <!--#echo var="FEAT_enable-tab-completion"--></a>
+<li><a href="h_config_enable_take_export">FEATURE: <!--#echo var="FEAT_enable-take-export"--></a>
+<li><a href="h_config_enable_role_take">FEATURE: <!--#echo var="FEAT_enable-rules-under-take"--></a>
+<li><a href="h_config_tray_icon">FEATURE: <!--#echo var="FEAT_enable-tray-icon"--></a>
+<li><a href="h_config_enable_pipe">FEATURE: <!--#echo var="FEAT_enable-unix-pipe-cmd"--></a>
+<li><a href="h_config_verbose_post">FEATURE: <!--#echo var="FEAT_enable-verbose-smtp-posting"--></a>
+<li><a href="h_config_expanded_addrbooks">FEATURE: <!--#echo var="FEAT_expanded-view-of-addressbooks"--></a>
+<li><a href="h_config_expanded_distlists">FEATURE: <!--#echo var="FEAT_expanded-view-of-distribution-lists"--></a>
+<li><a href="h_config_expanded_folders">FEATURE: <!--#echo var="FEAT_expanded-view-of-folders"--></a>
+<li><a href="h_config_expose_hidden_config">FEATURE: <!--#echo var="FEAT_expose-hidden-config"--></a>
+<li><a href="h_config_expunge_manually">FEATURE: <!--#echo var="FEAT_expunge-only-manually"--></a>
+<li><a href="h_config_auto_expunge">FEATURE: <!--#echo var="FEAT_expunge-without-confirm"--></a>
+<li><a href="h_config_full_auto_expunge">FEATURE: <!--#echo var="FEAT_expunge-without-confirm-everywhere"--></a>
+<li><a href="h_config_no_fcc_attach">FEATURE: <!--#echo var="FEAT_fcc-without-attachments"--></a>
+<li><a href="h_config_force_arrow">FEATURE: <!--#echo var="FEAT_force-arrow-cursor"--></a>
+<li><a href="h_config_forward_as_attachment">FEATURE: <!--#echo var="FEAT_forward-as-attachment"--></a>
+<li><a href="h_config_quell_empty_dirs">FEATURE: <!--#echo var="FEAT_quell-empty-directories"--></a>
+<li><a href="h_config_hide_nntp_path">FEATURE: <!--#echo var="FEAT_hide-nntp-path"--></a>
+<li><a href="h_config_attach_in_reply">FEATURE: <!--#echo var="FEAT_include-attachments-in-reply"--></a>
+<li><a href="h_config_fcc_on_bounce">FEATURE: <!--#echo var="FEAT_fcc-on-bounce"--></a>
+<li><a href="h_config_include_header">FEATURE: <!--#echo var="FEAT_include-header-in-reply"--></a>
+<li><a href="h_config_auto_include_reply">FEATURE: <!--#echo var="FEAT_include-text-in-reply"--></a>
+<li><a href="h_config_incoming_checking_total">FEATURE: <!--#echo var="FEAT_incoming-checking-includes-total"--></a>
+<li><a href="h_config_incoming_checking_recent">FEATURE: <!--#echo var="FEAT_incoming-checking-uses-recent"--></a>
+<li><a href="h_config_add_ldap">FEATURE: <!--#echo var="FEAT_ldap-result-to-addrbook-add"--></a>
+<li><a href="h_config_maildrops_preserve_state">FEATURE: <!--#echo var="FEAT_maildrops-preserve-state"--></a>
+<li><a href="h_config_mark_fcc_seen">FEATURE: <!--#echo var="FEAT_mark-fcc-seen"--></a>
+<li><a href="h_config_mark_for_cc">FEATURE: <!--#echo var="FEAT_mark-for-cc"--></a>
+<li><a href="h_config_mulnews_as_typed">FEATURE: <!--#echo var="FEAT_mult-newsrc-hostnames-as-typed"--></a>
+<li><a href="h_config_news_uses_recent">FEATURE: <!--#echo var="FEAT_news-approximates-new-status"--></a>
+<li><a href="h_config_news_cross_deletes">FEATURE: <!--#echo var="FEAT_news-deletes-across-groups"--></a>
+<li><a href="h_config_news_catchup">FEATURE: <!--#echo var="FEAT_news-offers-catchup-on-close"--></a>
+<li><a href="h_config_post_wo_validation">FEATURE: <!--#echo var="FEAT_news-post-without-validation"--></a>
+<li><a href="h_config_read_in_newsrc_order">FEATURE: <!--#echo var="FEAT_news-read-in-newsrc-order"--></a>
+<li><a href="h_config_nntp_search_uses_overview">FEATURE: <!--#echo var="FEAT_nntp-search-uses-overview"--></a>
+<li><a href="h_config_thread_sorts_by_arrival">FEATURE: <!--#echo var="FEAT_thread-sorts-by-arrival"--></a>
+<li><a href="h_config_expunge_inbox">FEATURE: <!--#echo var="FEAT_offer-expunge-of-inbox"--></a>
+<li><a href="h_config_expunge_stayopens">FEATURE: <!--#echo var="FEAT_offer-expunge-of-stayopen-folders"--></a>
+<li><a href="h_config_pass_c1_control">FEATURE: <!--#echo var="FEAT_pass-c1-control-characters-as-is"--></a>
+<li><a href="h_config_pass_control">FEATURE: <!--#echo var="FEAT_pass-control-characters-as-is"--></a>
+<li><a href="h_config_predict_nntp_server">FEATURE: <!--#echo var="FEAT_predict-nntp-server"--></a>
+<li><a href="h_config_prefer_plain_text">FEATURE: <!--#echo var="FEAT_prefer-plain-text"--></a>
+<li><a href="h_config_preopen_stayopens">FEATURE: <!--#echo var="FEAT_preopen-stayopen-folders"--></a>
+<li><a href="h_config_preserve_start_stop">FEATURE: <!--#echo var="FEAT_preserve-start-stop-characters"--></a>
+<li><a href="h_config_quell_folder_internal_msg">FEATURE: <!--#echo var="FEAT_quell-folder-internal-msg"--></a>
+<li><a href="h_config_quell_checks_comp">FEATURE: <!--#echo var="FEAT_quell-mailchecks-composing-except-inbox"--></a>
+<li><a href="h_config_quell_checks_comp_inbox">FEATURE: <!--#echo var="FEAT_quell-mailchecks-composing-inbox"--></a>
+<li><a href="h_config_quell_partial">FEATURE: <!--#echo var="FEAT_quell-partial-fetching"--></a>
+<li><a href="h_config_quell_local_lookup">FEATURE: <!--#echo var="FEAT_quell-user-lookup-in-passwd-file"--></a>
+<li><a href="h_config_ff_between_msgs">FEATURE: <!--#echo var="FEAT_print-formfeed-between-messages"--></a>
+<li><a href="h_config_print_from">FEATURE: <!--#echo var="FEAT_print-includes-from-line"--></a>
+<li><a href="h_config_print_index">FEATURE: <!--#echo var="FEAT_print-index-enabled"--></a>
+<li><a href="h_config_custom_print">FEATURE: <!--#echo var="FEAT_print-offers-custom-cmd-prompt"--></a>
+<li><a href="h_config_prune_uses_iso">FEATURE: <!--#echo var="FEAT_prune-uses-yyyy-mm"--></a>
+<li><a href="h_config_quell_personal_name_prompt">FEATURE: <!--#echo var="FEAT_quell-personal-name-prompt"--></a>
+<li><a href="h_config_quell_ssl_largeblocks">FEATURE: <!--#echo var="FEAT_quell-ssl-largeblocks"--></a>
+<li><a href="h_config_quell_user_id_prompt">FEATURE: <!--#echo var="FEAT_quell-user-id-prompt"--></a>
+<li><a href="h_config_quit_wo_confirm">FEATURE: <!--#echo var="FEAT_quit-without-confirm"--></a>
+<li><a href="h_config_quote_replace_noflow">FEATURE: <!--#echo var="FEAT_quote-replace-nonflowed"--></a>
+<li><a href="h_config_next_thrd_wo_confirm">FEATURE: <!--#echo var="FEAT_next-thread-without-confirm"--></a>
+<li><a href="h_config_auto_reply_to">FEATURE: <!--#echo var="FEAT_reply-always-uses-reply-to"--></a>
+<li><a href="h_config_inbox_no_confirm">FEATURE: <!--#echo var="FEAT_return-to-inbox-without-confirm"--></a>
+<li><a href="h_config_save_aggregates">FEATURE: <!--#echo var="FEAT_save-aggregates-copy-sequence"--></a>
+<li><a href="h_config_save_part_wo_confirm">FEATURE: <!--#echo var="FEAT_save-partial-msg-without-confirm"--></a>
+<li><a href="h_config_save_advances">FEATURE: <!--#echo var="FEAT_save-will-advance"--></a>
+<li><a href="h_config_save_wont_delete">FEATURE: <!--#echo var="FEAT_save-will-not-delete"--></a>
+<li><a href="h_config_quote_all_froms">FEATURE: <!--#echo var="FEAT_save-will-quote-leading-froms"--></a>
+<li><a href="h_config_scramble_message_id">FEATURE: <!--#echo var="FEAT_scramble-message-id"--></a>
+<li><a href="h_config_select_wo_confirm">FEATURE: <!--#echo var="FEAT_select-without-confirm"--></a>
+<li><a href="h_config_auto_fcc_only">FEATURE: <!--#echo var="FEAT_fcc-only-without-confirm"--></a>
+<li><a href="h_config_send_wo_confirm">FEATURE: <!--#echo var="FEAT_send-without-confirm"--></a>
+<li><a href="h_config_separate_fold_dir_view">FEATURE: <!--#echo var="FEAT_separate-folder-and-directory-entries"--></a>
+<li><a href="h_config_show_cursor">FEATURE: <!--#echo var="FEAT_show-cursor"--></a>
+<li><a href="h_config_textplain_int">FEATURE: <!--#echo var="FEAT_show-plain-text-internally"--></a>
+<li><a href="h_config_select_in_bold">FEATURE: <!--#echo var="FEAT_show-selected-in-boldface"--></a>
+<li><a href="h_config_show_sort">FEATURE: <!--#echo var="FEAT_show-sort"--></a>
+<li><a href="h_config_single_list">FEATURE: <!--#echo var="FEAT_single-column-folder-list"--></a>
+<li><a href="h_config_sig_at_bottom">FEATURE: <!--#echo var="FEAT_signature-at-bottom"--></a>
+<li><a href="h_config_slash_coll_entire">FEATURE: <!--#echo var="FEAT_slash-collapses-entire-thread"--></a>
+<li><a href="h_config_sort_fcc_alpha">FEATURE: <!--#echo var="FEAT_sort-default-fcc-alpha"--></a>
+<li><a href="h_config_sort_save_alpha">FEATURE: <!--#echo var="FEAT_sort-default-save-alpha"--></a>
+<li><a href="h_config_always_spell_check">FEATURE: <!--#echo var="FEAT_spell-check-before-sending"--></a>
+<li><a href="h_config_winpos_in_config">FEATURE: <!--#echo var="FEAT_store-window-position-in-config"--></a>
+<li><a href="h_config_strip_sigdashes">FEATURE: <!--#echo var="FEAT_strip-from-sigdashes-on-reply"--></a>
+<li><a href="h_config_strip_ws_before_send">FEATURE: <!--#echo var="FEAT_strip-whitespace-before-send"--></a>
+<li><a href="h_config_quells_asterisks">FEATURE: <!--#echo var="FEAT_suppress-asterisks-in-password-prompt"--></a>
+<li><a href="h_config_quell_attach_ext_warn">FEATURE: <!--#echo var="FEAT_quell-attachment-extension-warn"--></a>
+<li><a href="h_config_quell_attach_extra_prompt">FEATURE: <!--#echo var="FEAT_quell-attachment-extra-prompt"--></a>
+<li><a href="h_config_no_bezerk_zone">FEATURE: <!--#echo var="FEAT_quell-berkeley-format-timezone"--></a>
+<li><a href="h_config_quell_charset_warning">FEATURE: <!--#echo var="FEAT_quell-charset-warning"--></a>
+<li><a href="h_config_quell_content_id">FEATURE: <!--#echo var="FEAT_quell-content-id"--></a>
+<li><a href="h_config_quell_post_prompt">FEATURE: <!--#echo var="FEAT_quell-extra-post-prompt"--></a>
+<li><a href="h_config_quell_filtering_done_message">FEATURE: <!--#echo var="FEAT_quell-filtering-done-message"--></a>
+<li><a href="h_config_quell_filtering_messages">FEATURE: <!--#echo var="FEAT_quell-filtering-messages"--></a>
+<li><a href="h_config_quell_full_hdr_reset">FEATURE: <!--#echo var="FEAT_quell-full-header-auto-reset"--></a>
+<li><a href="h_config_quell_imap_env">FEATURE: <!--#echo var="FEAT_quell-imap-envelope-update"--></a>
+<li><a href="h_config_quell_lock_failure_warnings">FEATURE: <!--#echo var="FEAT_quell-lock-failure-warnings"--></a>
+<li><a href="h_config_quell_domain_warn">FEATURE: <!--#echo var="FEAT_quell-maildomain-warning"--></a>
+<li><a href="h_config_quell_news_env">FEATURE: <!--#echo var="FEAT_quell-news-envelope-update"--></a>
+<li><a href="h_config_quell_host_after_url">FEATURE: <!--#echo var="FEAT_quell-server-after-link-in-html"--></a>
+<li><a href="h_config_quell_beeps">FEATURE: <!--#echo var="FEAT_quell-status-message-beeping"--></a>
+<li><a href="h_config_quell_tz_comment">FEATURE: <!--#echo var="FEAT_quell-timezone-comment-when-sending"--></a>
+<li><a href="h_config_suppress_user_agent">FEATURE: <!--#echo var="FEAT_suppress-user-agent-when-sending"--></a>
+<li><a href="h_config_tab_checks_recent">FEATURE: <!--#echo var="FEAT_tab-checks-recent"--></a>
+<li><a href="h_config_tab_uses_unseen">FEATURE: <!--#echo var="FEAT_tab-uses-unseen-for-next-folder"--></a>
+<li><a href="h_config_tab_new_only">FEATURE: <!--#echo var="FEAT_tab-visits-next-new-message-only"--></a>
+<li><a href="h_config_termcap_wins">FEATURE: <!--#echo var="FEAT_termdef-takes-precedence"--></a>
+<li><a href="h_config_color_thrd_import">FEATURE: <!--#echo var="FEAT_thread-index-shows-important-color"--></a>
+<li><a href="h_config_alt_auth">FEATURE: <!--#echo var="FEAT_try-alternative-authentication-driver-first"--></a>
+<li><a href="h_config_unsel_wont_advance">FEATURE: <!--#echo var="FEAT_unselect-will-not-advance"--></a>
+<li><a href="h_config_use_current_dir">FEATURE: <!--#echo var="FEAT_use-current-dir"--></a>
+<li><a href="h_config_use_fk">FEATURE: <!--#echo var="FEAT_use-function-keys"--></a>
+<li><a href="h_config_use_reg_start_for_stayopen">FEATURE: <!--#echo var="FEAT_use-regular-startup-rule-for-stayopen-folders"--></a>
+<li><a href="h_config_use_resentto">FEATURE: <!--#echo var="FEAT_use-resent-to-in-rules"--></a>
+<li><a href="h_config_use_sender_not_x">FEATURE: <!--#echo var="FEAT_use-sender-not-x-sender"--></a>
+<li><a href="h_config_suspend_spawns">FEATURE: <!--#echo var="FEAT_use-subshell-for-suspend"--></a>
+<li><a href="h_config_use_system_translation">FEATURE: Use System Translation</a>
+<li><a href="h_config_vertical_list">FEATURE: <!--#echo var="FEAT_vertical-folder-list"--></a>
+<li><a href="h_config_warn_if_fcc_blank">FEATURE: <!--#echo var="FEAT_warn-if-blank-fcc"--></a>
+<li><a href="h_config_warn_if_subj_blank">FEATURE: <!--#echo var="FEAT_warn-if-blank-subject"--></a>
+<li><a href="h_config_warn_if_no_to_or_cc">FEATURE: <!--#echo var="FEAT_warn-if-blank-to-and-cc-and-newsgroups"--></a>
+<li><a href="h_mainhelp_filtering">Filtering</a>
+<li><a href="h_finding_help">Finding more information and requesting help</a>
+<li><a href="h_common_flag">Flag Command</a>
+<li><a href="h_config_quell_flowed_text">Flowed Text</a>
+<li><a href="h_mainhelp_folders">Folders</a>
+<li><a href="h_what_are_collections">Folder Collections Explained</a>
+<li><a href="h_common_folders">Folder List Command</a>
+<li><a href="h_folder_fcc">Folder Select for Fcc Explained</a>
+<li><a href="h_folder_save">Folder Select for Save Explained</a>
+<li><a href="h_folder_server_syntax">Folder Server Name Syntax</a>
+<li><a href="h_config_change_your_from">From Address, Changing</a>
+<li><a href="main_menu_tx">GENERAL INFORMATION ON THE ALPINE MESSAGE SYSTEM</a>
+<li><a href="h_pine_for_windows">GETTING HELP IN ALPINE</a>
+<li><a href="h_common_goto">Goto Command</a>
+<li><a href="h_common_hdrmode">HdrMode Command</a>
+<li><a href="h_mainhelp_pinehelp">Help</a>
+<li><a href="h_special_help_nav">Help Text Navigation Explained</a>
+<li><a href="h_folder_maint">Help for Folder List</a>
+<li><a href="h_valid_folder_names">IMAP</a>
+<li><a href="h_ge_import">Import File Selection</a>
+<li><a href="h_mainhelp_index">Index of Messages</a>
+<li><a href="h_composer_ins_m">INSERT MESSAGE</a>
+<li><a href="h_composer_ins">INSERT TEXT FILE</a>
+<li><a href="h_address_format">INTERNET EMAIL ADDRESS FORMAT</a>
+<li><a href="h_info_on_mbox">Information on mbox driver</a>
+<li><a href="h_mainhelp_intro">Introduction</a>
+<li><a href="h_main_journal">Journal Command</a>
+<li><a href="h_common_jump">Jump Command</a>
+<li><a href="h_compose_justify">Justify Command</a>
+<li><a href="h_main_kblock">Keyboard Lock Command</a>
+<li><a href="h_mainhelp_keywords">Keywords (or Flags, or Labels)</a>
+<li><a href="h_mainhelp_ldap">LDAP</a>
+<li><a href="h_config_ldap_opts_tls">LDAP FEATURE: Attempt-TLS-On-Connection</a>
+<li><a href="h_config_ldap_opts_nosub">LDAP FEATURE: Disable-Ad-Hoc-Space-Substitution</a>
+<li><a href="h_config_ldap_opts_rhs">LDAP FEATURE: Lookup-Addrbook-Contents</a>
+<li><a href="h_config_ldap_opts_tlsmust">LDAP FEATURE: Require-TLS-On-Connection</a>
+<li><a href="h_config_ldap_opts_ref">LDAP FEATURE: Save-Search-Criteria-Not-Result</a>
+<li><a href="h_config_ldap_opts_impl">LDAP FEATURE: Use-Implicitly-From-Composer</a>
+<li><a href="h_config_ldap_binddn">LDAP OPTION: Bind-DN</a>
+<li><a href="h_config_ldap_cust">LDAP OPTION: Custom-Search-Filter</a>
+<li><a href="h_config_ldap_email_attr">LDAP OPTION: EmailAttribute</a>
+<li><a href="h_config_ldap_gn_attr">LDAP OPTION: GivennameAttribute</a>
+<li><a href="h_config_ldap_server">LDAP OPTION: <!--#echo var="VAR_ldap-servers"--></a>
+<li><a href="h_config_ldap_cn_attr">LDAP OPTION: NameAttribute</a>
+<li><a href="h_config_ldap_nick">LDAP OPTION: Nickname</a>
+<li><a href="h_config_ldap_port">LDAP OPTION: Port</a>
+<li><a href="h_config_ldap_base">LDAP OPTION: Search-Base</a>
+<li><a href="h_config_ldap_searchrules">LDAP OPTION: Search-Rule</a>
+<li><a href="h_config_ldap_searchtypes">LDAP OPTION: Search-Type</a>
+<li><a href="h_config_ldap_size">LDAP OPTION: Sizelimit</a>
+<li><a href="h_config_ldap_sn_attr">LDAP OPTION: SurnameAttribute</a>
+<li><a href="h_config_ldap_time">LDAP OPTION: Timelimit</a>
+<li><a href="h_ldap_view">LDAP Response View Explained</a>
+<li><a href="h_maildrop">Mail Drop: What is it?</a>
+<li><a href="h_mainhelp_mainmenu">MAIN MENU</a>
+<li><a href="h_mail_index">MESSAGE INDEX COMMANDS</a>
+<li><a href="h_mail_view">MESSAGE TEXT SCREEN</a>
+<li><a href="h_compose_markcutpaste">Mark, Cut and Paste Commands</a>
+<li><a href="h_common_index">Message Index Command</a>
+<li><a href="h_mainhelp_mouse">Mouse</a>
+<li><a href="h_mainhelp_aggops">Multiple Message Operations</a>
+<li><a href="new_user_greeting">NEW USER GREETING</a>
+<li><a href="new_version_greeting">NEW VERSION GREETING</a>
+<li><a href="h_mainhelp_readingnews">News Reading</a>
+<li><a href="h_folder_subscribe">Newsgroup Subcribe Screen explained</a>
+<li><a href="h_folder_postnews">Newsgroup selecting for Posting explained</a>
+<li><a href="h_common_nextnew">NextNew Command</a>
+<li><a href="h_abook_select_nick">Nickname Selection Explained</a>
+<li><a href="h_mainhelp_readingnews">NNTP</a>
+<li><a href="h_config_address_book">OPTION: <!--#echo var="VAR_address-book"--></a>
+<li><a href="h_config_abook_formats">OPTION: <!--#echo var="VAR_addressbook-formats"--></a>
+<li><a href="h_config_ab_sort_rule">OPTION: <!--#echo var="VAR_addrbook-sort-rule"--></a></a>
+<li><a href="h_config_alt_addresses">OPTION: <!--#echo var="VAR_alt-addresses"--></a>
+<li><a href="h_config_active_msg_interval">OPTION: <!--#echo var="VAR_busy-cue-rate"--></a>
+<li><a href="h_config_color_style">OPTION: Color Style</a>
+<li><a href="h_config_wordseps">OPTION: <!--#echo var="VAR_composer-word-separators"--></a>
+<li><a href="h_config_composer_wrap_column">OPTION: <!--#echo var="VAR_composer-wrap-column"--></a>
+<li><a href="h_config_index_color_style">OPTION: <!--#echo var="VAR_current-indexline-style"--></a>
+<li><a href="h_config_cursor_style">OPTION: Cursor Style</a>
+<li><a href="h_config_custom_hdrs">OPTION: <!--#echo var="VAR_customized-hdrs"--></a>
+<li><a href="h_config_deadlets">OPTION: <!--#echo var="VAR_dead-letter-files"--></a>
+<li><a href="h_config_comp_hdrs">OPTION: <!--#echo var="VAR_default-composer-hdrs"--></a>
+<li><a href="h_config_default_fcc">OPTION: <!--#echo var="VAR_default-fcc"--></a>
+<li><a href="h_config_def_save_folder">OPTION: <!--#echo var="VAR_default-saved-msg-folder"--></a>
+<li><a href="h_config_disable_auths">OPTION: <!--#echo var="VAR_disable-these-authenticators"--></a>
+<li><a href="h_config_disable_drivers">OPTION: <!--#echo var="VAR_disable-these-drivers"--></a>
+<li><a href="h_config_char_set">OPTION: Display Character Set</a>
+<li><a href="h_config_display_filters">OPTION: <!--#echo var="VAR_display-filters"--></a>
+<li><a href="h_config_download_cmd">OPTION: <!--#echo var="VAR_download-command"--></a>
+<li><a href="h_config_download_prefix">OPTION: <!--#echo var="VAR_download-command-prefix"--></a>
+<li><a href="h_config_editor">OPTION: <!--#echo var="VAR_editor"--></a>
+<li><a href="h_config_empty_hdr_msg">OPTION: <!--#echo var="VAR_empty-header-message"--></a>
+<li><a href="h_config_fcc_rule">OPTION: <!--#echo var="VAR_fcc-name-rule"--></a>
+<li><a href="h_config_file_dir">OPTION: File Directory</a>
+<li><a href="h_config_folder_spec">OPTION: <!--#echo var="VAR_folder-collections"--></a>
+<li><a href="h_config_reopen_rule">OPTION: <!--#echo var="VAR_folder-reopen-rule"--></a>
+<li><a href="h_config_fld_sort_rule">OPTION: <!--#echo var="VAR_folder-sort-rule"--></a>
+<li><a href="h_config_font_char_set">OPTION: Font Character Set</a>
+<li><a href="h_config_font_name">OPTION: Font Name</a>
+<li><a href="h_config_font_size">OPTION: Font Size</a>
+<li><a href="h_config_font_style">OPTION: Font Style</a>
+<li><a href="h_config_form_folder">OPTION: <!--#echo var="VAR_form-letter-folder"--></a>
+<li><a href="h_config_glob_addrbook">OPTION: <!--#echo var="VAR_global-address-book"--></a>
+<li><a href="h_config_goto_default">OPTION: <!--#echo var="VAR_goto-default-rule"--></a>
+<li><a href="h_config_header_general_color">OPTION: Header General Color</a>
+<li><a href="h_config_image_viewer">OPTION: <!--#echo var="VAR_image-viewer"--></a>
+<li><a href="h_config_inbox_path">OPTION: <!--#echo var="VAR_inbox-path"--></a>
+<li><a href="h_config_archived_folders">OPTION: <!--#echo var="VAR_incoming-archive-folders"--></a>
+<li><a href="h_config_incoming_interv">OPTION: <!--#echo var="VAR_incoming-check-interval"--></a>
+<li><a href="h_config_incoming_second_interv">OPTION: <!--#echo var="VAR_incoming-check-interval-secondary"--></a>
+<li><a href="h_config_incoming_list">OPTION: <!--#echo var="VAR_incoming-check-list"--></a>
+<li><a href="h_config_incoming_timeo">OPTION: <!--#echo var="VAR_incoming-check-timeout"--></a>
+<li><a href="h_config_incoming_folders">OPTION: <!--#echo var="VAR_incoming-folders"--></a>
+<li><a href="h_config_inc_startup">OPTION: <!--#echo var="VAR_incoming-startup-rule"--></a>
+<li><a href="h_config_incunseen_color">OPTION: Incoming Unseen Color</a>
+<li><a href="h_config_index_arrow_color">OPTION: Index Arrow Color</a>
+<li><a href="h_config_index_color">OPTION: Index Colors</a>
+<li><a href="h_config_index_format">OPTION: <!--#echo var="VAR_index-format"--></a>
+<li><a href="h_config_index_from_color">OPTION: Index From Color</a>
+<li><a href="h_config_index_opening_color">OPTION: Index Opening Color</a>
+<li><a href="h_config_index_pri_color">OPTION: Index Priority Symbol Colors</a>
+<li><a href="h_config_index_subject_color">OPTION: Index Subject Color</a>
+<li><a href="h_config_init_cmd_list">OPTION: <!--#echo var="VAR_initial-keystroke-list"--></a>
+<li><a href="h_config_key_char_set">OPTION: Keyboard Character Set</a>
+<li><a href="h_config_keylabel_color">OPTION: KeyLabel Color</a>
+<li><a href="h_config_keyname_color">OPTION: KeyName Color</a>
+<li><a href="h_config_keywords">OPTION: <!--#echo var="VAR_keywords"--></a>
+<li><a href="h_config_kw_color">OPTION: Keyword Colors</a>
+<li><a href="h_config_kw_braces">OPTION: <!--#echo var="VAR_keyword-surrounding-chars"--></a>
+<li><a href="h_config_prune_date">OPTION: <!--#echo var="VAR_last-time-prune-questioned"--></a>
+<li><a href="h_config_last_vers">OPTION: <!--#echo var="VAR_last-version-used"--></a>
+<li><a href="h_config_literal_sig">OPTION: <!--#echo var="VAR_literal-signature"--></a>
+<li><a href="h_config_mailcheck">OPTION: <!--#echo var="VAR_mail-check-interval"--></a>
+<li><a href="h_config_mailchecknoncurr">OPTION: <!--#echo var="VAR_mail-check-interval-noncurrent"--></a>
+<li><a href="h_config_mailcap_path">OPTION: <!--#echo var="VAR_mailcap-search-path"--></a>
+<li><a href="h_config_maildropcheck">OPTION: <!--#echo var="VAR_maildrop-check-minimum"--></a>
+<li><a href="h_config_maxremstream">OPTION: <!--#echo var="VAR_max-remote-connections"--></a>
+<li><a href="h_config_metamsg_color">OPTION: Meta-Message Color</a>
+<li><a href="h_config_mimetype_path">OPTION: <!--#echo var="VAR_mimetype-search-path"--></a>
+<li><a href="h_config_new_ver_quell">OPTION: <!--#echo var="VAR_new-version-threshold"--></a>
+<li><a href="h_config_fifopath">OPTION: NewMail FIFO Path</a>
+<li><a href="h_config_newmailwidth">OPTION: <!--#echo var="VAR_newmail-window-width"--></a>
+<li><a href="h_config_news_active">OPTION: <!--#echo var="VAR_news-active-file-path"--></a>
+<li><a href="h_config_news_spec">OPTION: <!--#echo var="VAR_news-collections"--></a>
+<li><a href="h_config_news_spool">OPTION: <!--#echo var="VAR_news-spool-directory"--></a>
+<li><a href="h_config_newsrc_path">OPTION: <!--#echo var="VAR_newsrc-path"--></a>
+<li><a href="h_config_nntprange">OPTION: <!--#echo var="VAR_nntp-range"--></a>
+<li><a href="h_config_nntp_server">OPTION: <!--#echo var="VAR_nntp-server"--></HEAD></a>
+<li><a href="h_config_normal_color">OPTION: Normal Color</a>
+<li><a href="h_config_opening_sep">OPTION: <!--#echo var="VAR_opening-text-separator-chars"--></a>
+<li><a href="h_config_oper_dir">OPTION: <!--#echo var="VAR_operating-dir"--></a>
+<li><a href="h_config_pat_old">OPTION: Patterns</a>
+<li><a href="h_config_pat_filts">OPTION: <!--#echo var="VAR_patterns-filters2"--></a>
+<li><a href="h_config_pat_other">OPTION: <!--#echo var="VAR_patterns-other"--></a>
+<li><a href="h_config_pat_roles">OPTION: <!--#echo var="VAR_patterns-roles"--></a>
+<li><a href="h_config_pat_scores">OPTION: <!--#echo var="VAR_patterns-scores2"--></a>
+<li><a href="h_config_pers_name">OPTION: <!--#echo var="VAR_personal-name"--></a>
+<li><a href="h_config_print_cat">OPTION: <!--#echo var="VAR_personal-print-category"--></a>
+<li><a href="h_config_print_command">OPTION: <!--#echo var="VAR_personal-print-command"--></a>
+<li><a href="h_config_post_char_set">OPTION: <!--#echo var="VAR_posting-character-set"--></a>
+<li><a href="h_config_postponed_folder">OPTION: <!--#echo var="VAR_postponed-folder"--></a>
+<li><a href="h_config_print_font_char_set">OPTION: Print-Font-Char-Set</a>
+<li><a href="h_config_print_font_name">OPTION: Print-Font-Name</a>
+<li><a href="h_config_print_font_size">OPTION: Print-Font-Size</a>
+<li><a href="h_config_print_font_style">OPTION: Print-Font-Style</a>
+<li><a href="h_config_printer">OPTION: Printer</a>
+<li><a href="h_config_prompt_color">OPTION: Prompt Color</a>
+<li><a href="h_config_pruned_folders">OPTION: <!--#echo var="VAR_pruned-folders"--></a>
+<li><a href="h_config_pruning_rule">OPTION: <!--#echo var="VAR_pruning-rule"--></a>
+<li><a href="h_config_quote_color">OPTION: Quote Colors</a>
+<li><a href="h_config_quote_replace_string">OPTION: <!--#echo var="VAR_quote-replace-string"--></a>
+<li><a href="h_config_quote_suppression">OPTION: <!--#echo var="VAR_quote-suppression-threshold"--></a>
+<li><a href="h_config_read_message_folder">OPTION: <!--#echo var="VAR_read-message-folder"--></a>
+<li><a href="h_config_remote_abook_history">OPTION: <!--#echo var="VAR_remote-abook-history"--></a>
+<li><a href="h_config_abook_metafile">OPTION: <!--#echo var="VAR_remote-abook-metafile"--></a>
+<li><a href="h_config_remote_abook_validity">OPTION: <!--#echo var="VAR_remote-abook-validity"--></a>
+<li><a href="h_config_reply_indent_string">OPTION: <!--#echo var="VAR_reply-indent-string"--></a>
+<li><a href="h_config_reply_intro">OPTION: <!--#echo var="VAR_reply-leadin"--></a>
+<li><a href="h_config_reverse_color">OPTION: Reverse Color</a>
+<li><a href="h_config_rshcmd">OPTION: <!--#echo var="VAR_rsh-command"--></a>
+<li><a href="h_config_rsh_open_timeo">OPTION: <!--#echo var="VAR_rsh-open-timeout"--></a>
+<li><a href="h_config_rshpath">OPTION: <!--#echo var="VAR_rsh-path"--></a>
+<li><a href="h_config_saved_msg_name_rule">OPTION: <!--#echo var="VAR_saved-msg-name-rule"--></a>
+<li><a href="h_config_scroll_margin">OPTION: <!--#echo var="VAR_scroll-margin"--></a>
+<li><a href="h_config_slctbl_color">OPTION: Selectable Item Color</a>
+<li><a href="h_config_sending_filter">OPTION: <!--#echo var="VAR_sending-filters"--></a>
+<li><a href="h_config_sendmail_path">OPTION: <!--#echo var="VAR_sendmail-path"--></a>
+<li><a href="h_config_signature_color">OPTION: Signature Color</a>
+<li><a href="h_config_signature_file">OPTION: <!--#echo var="VAR_signature-file"--></a>
+<li><a href="h_config_smtp_server">OPTION: <!--#echo var="VAR_smtp-server"--></a>
+<li><a href="h_config_sort_key">OPTION: <!--#echo var="VAR_sort-key"--></a>
+<li><a href="h_config_speller">OPTION: <!--#echo var="VAR_speller"--></a>
+<li><a href="h_config_sshcmd">OPTION: <!--#echo var="VAR_ssh-command"--></a>
+<li><a href="h_config_ssh_open_timeo">OPTION: <!--#echo var="VAR_ssh-open-timeout"--></a>
+<li><a href="h_config_sshpath">OPTION: <!--#echo var="VAR_ssh-path"--></a>
+<li><a href="h_config_status_color">OPTION: Status Color</a>
+<li><a href="h_config_status_msg_delay">OPTION: <!--#echo var="VAR_status-message-delay"--></a>
+<li><a href="h_config_permlocked">OPTION: <!--#echo var="VAR_stay-open-folders"--></a>
+<li><a href="h_config_tcp_open_timeo">OPTION: <!--#echo var="VAR_tcp-open-timeout"--></a>
+<li><a href="h_config_tcp_query_timeo">OPTION: <!--#echo var="VAR_tcp-query-timeout"--></a>
+<li><a href="h_config_tcp_readwarn_timeo">OPTION: <!--#echo var="VAR_tcp-read-warning-timeout"--></a>
+<li><a href="h_config_tcp_writewarn_timeo">OPTION: <!--#echo var="VAR_tcp-write-warning-timeout"--></a>
+<li><a href="h_config_thread_disp_style">OPTION: <!--#echo var="VAR_threading-display-style"--></a>
+<li><a href="h_config_thread_exp_char">OPTION: <!--#echo var="VAR_threading-expanded-character"--></a>
+<li><a href="h_config_thread_index_style">OPTION: <!--#echo var="VAR_threading-index-style"--></a>
+<li><a href="h_config_thread_indicator_char">OPTION: <!--#echo var="VAR_threading-indicator-character"--></a>
+<li><a href="h_config_thread_lastreply_char">OPTION: <!--#echo var="VAR_threading-lastreply-character"--></a>
+<li><a href="h_config_title_color">OPTION: Title Color</a>
+<li><a href="h_config_titleclosed_color">OPTION: Title Closed Color</a>
+<li><a href="h_config_titlebar_color_style">OPTION: <!--#echo var="VAR_titlebar-color-style"--></a>
+<li><a href="h_config_unk_char_set">OPTION: <!--#echo var="VAR_unknown-character-set"--></a>
+<li><a href="h_config_upload_cmd">OPTION: <!--#echo var="VAR_upload-command"--></a>
+<li><a href="h_config_upload_prefix">OPTION: <!--#echo var="VAR_upload-command-prefix"--></a>
+<li><a href="h_config_browser">OPTION: <!--#echo var="VAR_url-viewers"--></a>
+<li><a href="h_config_domain_name">OPTION: <!--#echo var="VAR_use-only-domain-name"--></a>
+<li><a href="h_config_user_dom">OPTION: <!--#echo var="VAR_user-domain"--></a>
+<li><a href="h_config_user_id">OPTION: <!--#echo var="VAR_user-id"--></a>
+<li><a href="h_config_user_input_timeo">OPTION: <!--#echo var="VAR_user-input-timeout"--></a>
+<li><a href="h_config_viewer_headers">OPTION: <!--#echo var="VAR_viewer-hdrs"--></a>
+<li><a href="h_config_customhdr_pattern">OPTION: Viewer Header Color Pattern</a>
+<li><a href="h_config_customhdr_color">OPTION: <!--#echo var="VAR_viewer-hdr-colors"--></a>
+<li><a href="h_config_viewer_margin_left">OPTION: <!--#echo var="VAR_viewer-margin-left"--></a>
+<li><a href="h_config_viewer_margin_right">OPTION: <!--#echo var="VAR_viewer-margin-right"--></a>
+<li><a href="h_config_viewer_overlap">OPTION: <!--#echo var="VAR_viewer-overlap"--></a>
+<li><a href="h_config_window_position">OPTION: Window-Position</a>
+<li><a href="h_mainhelp_patterns">Patterns</a>
+<li><a href="h_config_role_abookfrom">PATTERNS: Address in Address Book</a>
+<li><a href="h_config_role_age">PATTERNS: Age Interval</a>
+<li><a href="h_config_role_alltextpat">PATTERNS: AllText Pattern</a>
+<li><a href="h_config_role_bom">PATTERNS: Beginning of Month</a>
+<li><a href="h_config_role_boy">PATTERNS: Beginning of Year</a>
+<li><a href="h_config_role_bodytextpat">PATTERNS: BodyText Pattern</a>
+<li><a href="h_config_role_cat_cmd">PATTERNS: Categorizer Command</a>
+<li><a href="h_config_role_cat_cmd_example">PATTERNS: Categorizer Command Example</a>
+<li><a href="h_config_role_ccpat">PATTERNS: Cc Pattern</a>
+<li><a href="h_config_role_cat_limit">PATTERNS: Character Limit</a>
+<li><a href="h_config_role_charsetpat">PATTERNS: Character Set Pattern</a>
+<li><a href="h_config_role_comment">PATTERNS: Comment</a>
+<li><a href="h_config_role_fldr_type">PATTERNS: Current Folder Type</a>
+<li><a href="h_config_role_cat_status">PATTERNS: Exit Status Interval</a>
+<li><a href="h_config_role_arbpat">PATTERNS: Extra Headers Pattern</a>
+<li><a href="h_config_role_frompat">PATTERNS: From Pattern</a>
+<li><a href="h_config_role_keywordpat">PATTERNS: Keyword Pattern</a>
+<li><a href="h_config_role_stat_ans">PATTERNS: Message Answered Status</a>
+<li><a href="h_config_role_stat_del">PATTERNS: Message Deleted Status</a>
+<li><a href="h_config_role_stat_imp">PATTERNS: Message Important Status</a>
+<li><a href="h_config_role_stat_new">PATTERNS: Message New Status</a>
+<li><a href="h_config_role_stat_recent">PATTERNS: Message Recent Status</a>
+<li><a href="h_config_role_newspat">PATTERNS: News Pattern</a>
+<li><a href="h_config_role_nick">PATTERNS: Nickname</a>
+<li><a href="h_config_role_particpat">PATTERNS: Participant Pattern</a>
+<li><a href="h_config_role_stat_8bitsubj">PATTERNS: Raw 8-bit in Subject</a>
+<li><a href="h_config_role_recippat">PATTERNS: Recipient Pattern</a>
+<li><a href="h_config_role_scorei">PATTERNS: Score Interval</a>
+<li><a href="h_config_role_senderpat">PATTERNS: Sender Pattern</a>
+<li><a href="h_config_role_size">PATTERNS: Size Interval</a>
+<li><a href="h_config_role_subjpat">PATTERNS: Subject Pattern</a>
+<li><a href="h_config_role_topat">PATTERNS: To Pattern</a>
+<li><a href="h_config_filt_opts_nonterm">PATTERNS FEATURE: Dont-Stop-Even-if-Rule-Matches</a>
+<li><a href="h_config_filt_opts_notdel">PATTERNS FEATURE: Move-Only-if-Not-Deleted</a>
+<li><a href="h_config_filt_opts_sentdate">PATTERNS FEATURE: Use-Date-Header-For-Age</a>
+<li><a href="h_config_filt_rule_type">PATTERNS FILTER ACTION: Filter Action</a>
+<li><a href="h_config_filter_kw_clr">PATTERNS FILTER ACTION: Clear These Keywords</a>
+<li><a href="h_config_filt_stat_ans">PATTERNS FILTER ACTION: Set Answered Status</a>
+<li><a href="h_config_filt_stat_del">PATTERNS FILTER ACTION: Set Deleted Status</a>
+<li><a href="h_config_filt_stat_imp">PATTERNS FILTER ACTION: Set Important Status</a>
+<li><a href="h_config_filt_stat_new">PATTERNS FILTER ACTION: Set New Status</a>
+<li><a href="h_config_filter_kw_set">PATTERNS FILTER ACTION: Set These Keywords</a>
+<li><a href="h_config_incol">PATTERNS INDEXCOLOR ACTION: Index Line Color</a>
+<li><a href="h_config_set_index_format">PATTERNS OTHER ACTION: Set Index Format</a>
+<li><a href="h_config_perfolder_sort">PATTERNS OTHER ACTION: Set Sort Order</a>
+<li><a href="h_config_other_startup">PATTERNS OTHER ACTION: Set Startup Rule</a>
+<li><a href="h_config_role_inick">PATTERNS ROLE ACTION: Initialize Values From Role</a>
+<li><a href="h_config_role_setfcc">PATTERNS ROLE ACTION: Set Fcc</a>
+<li><a href="h_config_role_setfrom">PATTERNS ROLE ACTION: Set From</a>
+<li><a href="h_config_role_setlitsig">PATTERNS ROLE ACTION: Set Literal Signature</a>
+<li><a href="h_config_role_setotherhdr">PATTERNS ROLE ACTION: Set Other Headers</a>
+<li><a href="h_config_role_setreplyto">PATTERNS ROLE ACTION: Set Reply-To</a>
+<li><a href="h_config_role_setsig">PATTERNS ROLE ACTION: Set Signature</a>
+<li><a href="h_config_role_settempl">PATTERNS ROLE ACTION: Set Template</a>
+<li><a href="h_config_role_usenntp">PATTERNS ROLE ACTION: Use NNTP Server</a>
+<li><a href="h_config_role_usesmtp">PATTERNS ROLE ACTION: Use SMTP Server</a>
+<li><a href="h_config_role_scoreval">PATTERNS SCORE ACTION: Score Value</a>
+<li><a href="h_config_role_scorehdrtok">PATTERNS SCORE ACTION: Score Value From Header</a>
+<li><a href="h_config_role_composeuse">PATTERNS USE: Compose Use</a>
+<li><a href="h_config_role_forwarduse">PATTERNS USE: Forward Use</a>
+<li><a href="h_config_role_replyuse">PATTERNS USE: Reply Use</a>
+<li><a href="h_pipe_command">Pipe Command SubOptions</a>
+<li><a href="h_common_pipe">Pipe Command</a>
+<li><a href="h_valid_folder_names">POP</a>
+<li><a href="h_common_postpone">Postpone Command</a>
+<li><a href="h_common_print">Print Command</a>
+<li><a href="h_mainhelp_readingnews">Reading News</a>
+<li><a href="h_news">RELEASE NOTES for Alpine</a>
+<li><a href="h_mainhelp_roles">Roles</a>
+<li><a href="h_role_select">ROLES SCREEN</a>
+<li><a href="h_compose_readfile">Read File Command</a>
+<li><a href="h_mainhelp_reading">Reading Messages</a>
+<li><a href="h_main_release_notes">Release Notes Command</a>
+<li><a href="h_common_reply">Reply and Forward Commands</a>
+<li><a href="h_compose_richhdr">Rich Header Command</a>
+<li><a href="h_common_role">Role Command</a>
+<li><a href="h_mainhelp_smime">S/MIME</a>
+<li><a href="h_index_cmd_select">Searching for Messages</a>
+<li><a href="h_address_display">SEARCH RESULTS INDEX</a>
+<li><a href="h_address_select">SEARCH RESULTS INDEX</a>
+<li><a href="h_simple_index">SELECT POSTPONED MESSAGE</a>
+<li><a href="h_abook_config">SETUP ADDRESS BOOKS SCREEN</a>
+<li><a href="h_collection_maint">SETUP COLLECTION LIST screen</a>
+<li><a href="h_color_setup">SETUP COLOR COMMANDS</a>
+<li><a href="h_direct_config">SETUP LDAP DIRECTORY SERVERS SCREEN</a>
+<li><a href="h_rules_roles">SETUP ROLES SCREEN</a>
+<li><a href="h_rules_incols">SETUP INDEX COLORS SCREEN</a>
+<li><a href="h_rules_filter">SETUP FILTERING SCREEN</a>
+<li><a href="h_rules_score">SETUP SCORING SCREEN</a>
+<li><a href="h_common_save">Save and Export Commands</a>
+<li><a href="h_mainhelp_securing">Securing Your Alpine Session</a>
+<li><a href="h_index_cmd_select">Selecting: Select and WhereIs/Select</a>
+<li><a href="h_compose_send">Send Command</a>
+<li><a href="h_folder_server_syntax">Server Name Syntax</a>
+<li><a href="h_main_setup">Setup Command</a>
+<li><a href="X-Alpine-Config:">Show Supported Options in this Alpine</a>
+<li><a href="h_composer_sigedit">Signature Editor Commands Explained</a>
+<li><a href="h_simple_text_view">Simple Text View Screen Explained</a>
+<li><a href="h_mainhelp_smime">S/MIME</a>
+<li><a href="h_config_smime_dont_do_smime">S/MIME FEATURE: <!--#echo var="FEAT_smime-dont-do-smime"--></a>
+<li><a href="h_config_smime_encrypt_by_default">S/MIME FEATURE: <!--#echo var="FEAT_smime-encrypt-by-default"--></a>
+<li><a href="h_config_smime_remember_passphrase">S/MIME FEATURE: <!--#echo var="FEAT_smime-remember-passphrase"--></a>
+<li><a href="h_config_smime_sign_by_default">S/MIME FEATURE: <!--#echo var="FEAT_smime-sign-by-default"--></a>
+<li><a href="h_config_smime_pubcerts_in_keychain">S/MIME FEATURE: <!--#echo var="FEAT_publiccerts-in-keychain"--></a>
+<li><a href="h_config_smime_cacertcon">S/MIME OPTION: <!--#echo var="VAR_smime-cacert-container"--></a>
+<li><a href="h_config_smime_cacertdir">S/MIME OPTION: <!--#echo var="VAR_smime-cacert-directory"--></a>
+<li><a href="h_config_smime_privkeycon">S/MIME OPTION: <!--#echo var="VAR_smime-private-key-container"--></a>
+<li><a href="h_config_smime_privkeydir">S/MIME OPTION: <!--#echo var="VAR_smime-private-key-directory"--></a>
+<li><a href="h_config_smime_pubcertcon">S/MIME OPTION: <!--#echo var="VAR_smime-public-cert-container"--></a>
+<li><a href="h_config_smime_pubcertdir">S/MIME OPTION: <!--#echo var="VAR_smime-public-cert-directory"--></a>
+<li><a href="h_config_smime_transfer_cacert_to_con">S/MIME: Transfer CA Certs to Container</a>
+<li><a href="h_config_smime_transfer_cacert_to_dir">S/MIME: Transfer CA Certs to Directory</a>
+<li><a href="h_config_smime_transfer_priv_to_con">S/MIME: Transfer Private Keys to Container</a>
+<li><a href="h_config_smime_transfer_priv_to_dir">S/MIME: Transfer Private Keys to Directory</a>
+<li><a href="h_config_smime_transfer_pub_to_con">S/MIME: Transfer Public Certs to Container</a>
+<li><a href="h_config_smime_transfer_pub_to_dir">S/MIME: Transfer Public Certs to Directory</a>
+<li><a href="h_index_cmd_sort">Sort Command</a>
+<li><a href="h_compose_spell">Spell Check Command</a>
+<li><a href="h_common_suspend">Suspend Command</a>
+<li><a href="h_compose_addrcomplete">THE MESSAGE COMPOSER'S ADDRESS COMPLETION</a>
+<li><a href="h_composer_attachment">THE MESSAGE COMPOSER'S ATTCHMNT FIELD</a>
+<li><a href="h_composer_bcc">THE MESSAGE COMPOSER'S BCC FIELD</a>
+<li><a href="h_composer_cc">THE MESSAGE COMPOSER'S CC FIELD</a>
+<li><a href="h_composer_from">THE MESSAGE COMPOSER'S FROM FIELD</a>
+<li><a href="h_composer_lcc">THE MESSAGE COMPOSER'S LCC FIELD</a>
+<li><a href="h_composer_news">THE MESSAGE COMPOSER'S NEWSGRPS LINE</a>
+<li><a href="h_composer_reply_to">THE MESSAGE COMPOSER'S REPLY-TO FIELD</a>
+<li><a href="h_composer_to">THE MESSAGE COMPOSER'S TO FIELD</a>
+<li><a href="h_abook_opened">THE ALPINE ADDRESS BOOK</a>
+<li><a href="h_abook_select_nicks_take">Take Address Nickname Selection Explained</a>
+<li><a href="h_takeaddr_screen">Take Address Screen Explained</a>
+<li><a href="h_common_take">TakeAddr Command</a>
+<li><a href="h_mainhelp_status">Titlebar Line</a>
+<li><a href="h_index_tokens">Tokens for Index and Replying</a>
+<li><a href="h_config_usenone_color">Use None Color</a>
+<li><a href="h_config_usenormal_color">Use Normal Color</a>
+<li><a href="h_config_usetransparent_color">Use Transparent Color</a>
+<li><a href="h_whatis_vcard">VCARD EXPLAINED</a>
+<li><a href="h_view_cmd_hilite">View Hilite and Next item/Previous item</a>
+<li><a href="h_view_cmd_viewattch">ViewAttch Command</a>
+<li><a href="h_index_cmd_whereis">WhereIs Command</a>
+<li><a href="h_view_cmd_whereis">WhereIs Command</a>
+<li><a href="h_index_cmd_zoom">ZoomMode Command</a>
+<li><a href="h_config_browser_xterm"><!--#echo var="VAR_url-viewers"--> and X windows applications</a>
+</UL>
+
+<P>
+&lt;End of Help Index&gt;
+</BODY>
+</HTML>
+
+
+============== h_config_remote_config =============
+<HTML>
+<HEAD>
+<TITLE>Remote Configuration</TITLE>
+</HEAD>
+<BODY>
+<H1>Remote Configuration</H1>
+
+You may use the command line argument &quot;-p pinerc&quot; to tell
+Alpine to use a non-default configuration file.
+There are two types of storage for configuration information.
+<EM>Local</EM> configuration files are used by default.
+These are just regular files on the UNIX system or on the PC.
+The file &quot;<CODE>.pinerc</CODE>&quot; is the default for Unix Alpine and the
+file &quot;<CODE>PINERC</CODE>&quot; is the default for PC-Alpine.
+<EM>Remote</EM> configuration folders are stored on an IMAP server.
+The advantage of using a remote configuration is that the same information
+may be accessed from multiple platforms.
+For example, if you use one computer at work and another at home, the same
+configuration could be used from both places.
+A configuration change from one place would be seen in both places.
+To use a remote configuration you simply give a
+<A HREF="h_valid_folder_names">remote folder name</A>
+as the argument to the &quot;-p&quot; command line option.
+The command line might look something like:
+<P>
+<CENTER><SAMP>pine -p {my.imap.server}remote_pinerc</SAMP></CENTER>
+<P>
+If there are special characters in the command shell you use, you may need to
+quote the last argument (to protect the curly braces from the shell).
+The command might look like:
+<P>
+<CENTER><SAMP>pine -p &quot;{my.imap.server}remote_pinerc&quot;</SAMP></CENTER>
+<P>
+You should choose a folder name for a folder that does not yet exist.
+It will be created containing an empty configuration.
+Do not use a folder that you wish to store regular mail messages in.
+<P>
+The Setup/RemoteConfigSetup command will help you convert from a local
+configuration to a remote configuration.
+It will create a remote configuration for you and copy your current local
+configuration to it.
+It will also help you convert local address books into remote address books
+and local signature files into literal signatures contained in the
+remote configuration file.
+<P>
+If the Setup/RemoteConfigSetup command doesn't do what you want, you
+may copy a local pinerc file to a remote configuration folder by hand
+by using the command line option &quot;-copy_pinerc&quot;.
+<P>
+Another command line option, which is somewhat related to remote
+configuration, is the option &quot;-x exceptions_config&quot;.
+The configuration settings in the exceptions configuration override
+your default settings.
+It may be useful to store the default configuration (the -p argument) remotely
+and to have the exceptions configuration stored in a local file.
+You might put generic configuration information in the remote configuration
+and platform-specific configuration on each platform in the exceptions
+configuration.
+The arguments to the &quot;-p&quot; and &quot;-x&quot; options
+can be either remote folders or local files.
+<P>
+There is another command line argument that works only with PC-Alpine and
+which may prove useful when using a remote configuration.
+The option &quot;-aux local_directory&quot; allows you to tell PC-Alpine where
+to store your local auxiliary files.
+This only has an effect if your configuration file is remote.
+Some examples of auxiliary files are debug files, address book files, and
+signature files.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+============== h_config_exceptions =============
+<HTML>
+<HEAD>
+<TITLE>Generic and Exceptional Configuration</TITLE>
+</HEAD>
+<BODY>
+<H1>Generic and Exceptional Configuration</H1>
+
+If you use Alpine from more than one platform it may be convenient
+to split your configuration information into two pieces, a generic piece
+and exceptions that apply to a particular platform.
+For example, suppose you use Alpine from home and from work.
+Most of your configuration settings are probably the
+same in both locations, so those settings belong in the generic settings
+configuration.
+However, you may use a different SMTP server and INBOX
+from home than you do from work.
+The
+<A HREF="h_config_smtp_server">&quot;<!--#echo var="VAR_smtp-server"-->&quot;</A>
+and
+<A HREF="h_config_inbox_path">&quot;<!--#echo var="VAR_inbox-path"-->&quot;</A>
+options could be
+part of your exceptional configuration so that they could be different in the
+two places.
+<P>
+The command line option &quot;-x exceptions_config&quot;
+may be used to split your configuration into generic and exceptional pieces.
+&quot;Exceptions_config&quot; may be either local or remote.
+The regular Alpine configuration file will contain the generic data, and
+&quot;exceptions_config&quot; will contain the exceptional data.
+<P>
+For Unix Alpine, if you don't have a &quot;-x&quot; command line option,
+Alpine will look for the file &quot;<CODE>.pinercex</CODE>&quot;
+in the same local directory that the regular config file is located in (usually
+the Unix home directory).
+If the regular config file is remote (because the command line option
+&quot;-p remote_config&quot; was used) then Unix Alpine looks in the Unix home
+directory for &quot;<CODE>.pinercex</CODE>&quot;.
+If the file does not already exist then no exceptions will be used.
+You can force exceptions to be used by using the &quot;-x&quot; option or
+by creating an empty &quot;<CODE>.pinercex</CODE>&quot; file.
+<P>
+For PC-Alpine, if you don't have a &quot;-x&quot; command line option,
+PC-Alpine will use the value of the
+environment variable <CODE>$PINERCEX</CODE>.
+If that is not set, PC-Alpine will look for
+the local file &quot;<CODE>PINERCEX</CODE>&quot;
+in the same local directory that the regular config file is located in.
+If the regular config file is remote (because the command line option
+&quot;-p remote_config&quot; was used) then PC-Alpine looks in the
+local directory specified by the &quot;-aux local_directory&quot; command
+line argument, or the directory <CODE>$HOME&#92;PINE</CODE>, or
+in the <CODE>&lt;PINE.EXE </CODE>directory<CODE>&gt;</CODE>.
+<P>
+When you have an exception configuration there is a new command
+in the Alpine Setup screen, Setup/eXceptions.
+It toggles between exceptions and the regular configuration.
+This is the usual way to make changes in your exceptional configuration data.
+For example, you would type &quot;S&quot; for Setup, &quot;X&quot; for
+eXception, then follow that with one of the Setup commands, like &quot;C&quot;
+for Config or &quot;K&quot; for Kolor.
+<P>
+For most people, splitting the configuration information into two pieces is
+going to be most useful if the generic information is accessed
+<A HREF="h_config_remote_config">remotely</A>).
+That data will be the same no matter where you access it from and if you
+change it that change will show up everywhere.
+The exceptional data will most commonly be in a local file, so that the
+contents may easily be different on each computing platform used.
+<P>
+If you already have a local configuration file with settings you like
+you may find that the command Setup/RemoteConfigSetup is useful
+in helping you convert to a remote configuration.
+The command line flag &quot;-copy_pinerc&quot;
+may also be useful.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+============== h_config_inheritance =============
+<HTML>
+<HEAD>
+<TITLE>Configuration Inheritance</TITLE>
+</HEAD>
+<BODY>
+<H1>Configuration Inheritance</H1>
+
+Configuration inheritance is a power user feature.
+It is confusing and not completely supported by the configuration
+user interface.
+We start with an explanation of how configuration works in hopes of making
+it easier to describe how inheritance works.
+<P>
+Alpine uses a hierarchy of configuration values from different locations.
+There are five ways in which each configuration option (configuration
+variable) can be set.
+In increasing order of precedence they are:
+<P>
+<OL>
+<LI> the system-wide configuration file.
+
+<LI> the personal configuration file
+
+<LI> the personal exceptions file
+
+<LI> a command line argument
+
+<LI> the system-wide <EM>fixed</EM> configuration file (Unix Alpine only)
+</OL>
+<P>
+The fixed configuration file is normally
+<CODE><!--#echo var="PINE_CONF_FIXED_PATH"--></CODE>.
+<P>
+The system-wide configuration file is normally
+<CODE><!--#echo var="PINE_CONF_PATH"--></CODE> for Unix Alpine and is normally not
+set for PC-Alpine.
+For PC-Alpine, if the environment variable <EM>$PINECONF</EM> is set, that
+is used for the system-wide configuration.
+This location can be set or changed on the command line with the -P flag.
+The system-wide configuration file can be either a local file or a
+remote configuration folder.
+<P>
+For Unix Alpine, the personal configuration file is normally the file
+<CODE>.pinerc</CODE> in the user's home directory.
+This can be changed with the -p command line flag.
+For PC-Alpine, the personal configuration file is in
+<CODE>$PINERC</CODE> or <CODE>&lt;AlpineRC registry value&gt;</CODE> or
+<CODE>$HOME&#92;PINE&#92;PINERC</CODE> or
+<CODE>&lt;PINE.EXE </CODE>dir<CODE>&gt;&#92;PINERC</CODE>.
+This can be changed with the -p command line flag.
+If -p is used, the configuration data may be in a local file or a remote config
+folder.
+<P>
+For Unix Alpine, the personal exceptions configuration file is named
+<CODE>.pinercex</CODE> and is in the same directory as the personal
+configuration file, if that configuration file is not remote, and is in
+the home directory if the personal configuration file is remote.
+If the file exists, then exceptions are turned on.
+If it doesn't, then you are not using exceptions.
+Alternatively, you may change the location of the exceptions configuration
+by using the command line argument &quot;-x &lt;exceptions_config&gt;&quot;.
+Like the personal configuration data, exceptions_config may be
+either local or remote.
+<P>
+For PC-Alpine, the personal exceptions configuration file is named
+<CODE>PINERCEX</CODE> and is in the same directory as the personal
+configuration file unless the personal configuration file is remote.
+In that case, it is in the local directory specfied by the
+&quot;-aux local_directory&quot; command line argument.
+(In the case that the personal configuration is remote and there is no
+&quot;-aux&quot; command line argument, Alpine searches for
+a PINERCEX file in the directory <CODE>$HOME&#92;PINE</CODE> and
+the directory <CODE>&lt;PINE.EXE </CODE>dir<CODE>&gt;</CODE>.)
+If the file exists, then exceptions are turned on.
+If it doesn't, then you are not using exceptions.
+You may change the location of the exceptions configuration
+by using the command line argument &quot;-x &lt;exceptions_config&gt;&quot;.
+or with the
+environment variable <CODE>$PINERCEX</CODE> (if there is no &quot;-x&quot;
+option).
+<P>
+To reiterate, the value of a configuration option is taken from the
+last location in the list above in which it is set.
+Or, thinking about it slightly differently, a default value for an option
+is established in the system-wide configuration file (or internally by Alpine
+if there is no value in the system-wide file).
+That default remains in effect until and unless it is overridden by a value in a
+location further down the list, in which case a new &quot;default&quot;
+value is established.
+As we continue down the list of locations we either retain the
+value at each step or establish a new value.
+The value that is still set after going through the whole list of
+configuration locations is the one that is used.
+<P>
+So, for example, if an option is set in the system-wide configuration
+file and in the personal configuration file, but is not set in the
+exceptions, on the command line, or in the fixed file; then the value
+from the personal configuration file is the one that is used.
+Or, if it is set in the system-wide config, in the personal config, not
+in the exceptions, but is set on the command line; then the value
+on the command line is used.
+<P>
+Finally we get to inheritance.
+For configuration options that are lists, like &quot;<!--#echo var="VAR_smtp-server"-->&quot; or
+&quot;<!--#echo var="VAR_incoming-folders"-->&quot;,
+the inheritance mechanism makes it possible to <EM>combine</EM>
+the values from different locations instead of <EM>replacing</EM> the value.
+This is true of all configuration lists other than the &quot;Feature-List&quot;,
+for which you may already set whatever you want at
+any configuration location (by using the &quot;no-&quot; prefix if
+necessary).
+<P>
+To use inheritance, set the first item in a configuration list to the
+token &quot;INHERIT&quot;, without the quotes.
+If the first item is &quot;INHERIT&quot;,
+then instead of replacing the default value established so far, the rest of
+the list is appended to the default value established so far and that is
+the new value.
+<P>
+Here is an example which may make it clearer. Suppose we have:
+<P>
+<PRE>
+ System-wide config : smtp-server = smtp1.corp.com, smtp2.corp.com
+ Personal config : smtp-server = INHERIT, mysmtp.home
+ Exceptions config : smtp-server = &lt;No Value Set&gt;
+ Command line : smtp-server = &lt;No Value Set&gt;
+ Fixed config : smtp-server = &lt;No Value Set&gt;
+</PRE>
+<P>
+
+This would result in an effective smtp-server option of
+<P>
+<PRE>
+ smtp-server = smtp1.corp.com, smtp2.corp.com, mysmtp.home
+</PRE>
+<P>
+The &quot;INHERIT&quot; token can be used in any of the configuration files
+and the effect cascades.
+For example, if we change the above example to:
+<P>
+<PRE>
+ System-wide config : smtp-server = smtp1.corp.com, smtp2.corp.com
+ Personal config : smtp-server = INHERIT, mysmtp.home
+ Exceptions config : smtp-server = INHERIT, yoursmtp.org
+ Command line : smtp-server = &lt;No Value Set&gt;
+ Fixed config : smtp-server = &lt;No Value Set&gt;
+</PRE>
+<P>
+
+This would result in:
+<P>
+<PRE>
+ smtp-server = smtp1.corp.com, smtp2.corp.com, mysmtp.home, yoursmtp.org
+</PRE>
+<P>
+Unset variables are skipped over (the default value is carried forward) so
+that, for example:
+<P>
+<PRE>
+ System-wide config : smtp-server = smtp1.corp.com, smtp2.corp.com
+ Personal config : smtp-server = &lt;No Value Set&gt;
+ Exceptions config : smtp-server = INHERIT, yoursmtp.org
+ Command line : smtp-server = &lt;No Value Set&gt;
+ Fixed config : smtp-server = &lt;No Value Set&gt;
+</PRE>
+<P>
+
+produces:
+<P>
+<PRE>
+ smtp-server = smtp1.corp.com, smtp2.corp.com, yoursmtp.org
+</PRE>
+<P>
+
+If any later configuration location has a value set (for a particular list
+option) which does <EM>not</EM> begin with &quot;INHERIT&quot;,
+then that value replaces whatever value has been defined up to that point.
+In other words, that cancels out any previous inheritance.
+<P>
+<PRE>
+ System-wide config : smtp-server = smtp1.corp.com, smtp2.corp.com
+ Personal config : smtp-server = INHERIT, mysmtp.org
+ Exceptions config : smtp-server = yoursmtp.org
+ Command line : smtp-server = &lt;No Value Set&gt;
+ Fixed config : smtp-server = &lt;No Value Set&gt;
+</PRE>
+<P>
+
+results in:
+<P>
+<PRE>
+ smtp-server = yoursmtp.org
+</PRE>
+<P>
+
+For some configuration options, like &quot;<!--#echo var="VAR_viewer-hdr-colors"-->&quot; or
+&quot;<!--#echo var="VAR_patterns-roles"-->&quot;, it is
+difficult to insert the value &quot;INHERIT&quot; into the list of values
+for the option using the normal Setup tools.
+In other words, the color setting screen (for example) does not
+provide a way to input the text &quot;INHERIT&quot; as the first
+item in the <!--#echo var="VAR_viewer-hdr-colors"--> option.
+The way to do this is to either edit the pinerc file directly and manually
+insert it, or turn
+on the <A HREF="h_config_expose_hidden_config"><!--#echo var="FEAT_expose-hidden-config"--></A>
+feature and insert it using the Setup/Config screen.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+============== h_special_xon_xoff =============
+<HTML>
+<HEAD>
+<TITLE>Explanation of Alpine's XOFF/XON Handling</TITLE>
+</HEAD>
+<BODY>
+<H1>XOFF/XON Handling within Alpine</H1>
+
+By default, Alpine treats Ctrl-S or Ctrl-Q (sometimes known as XOFF
+and XON) as normal characters, even though Alpine does not use them.
+However, the printer, modem, or communication software you are using may
+be configured for &quot;software flow control,&quot; which means that
+XON/XOFF must be treated as special characters by the operating system.
+If you see messages such as &quot;^S not defined for this screen&quot;,
+then your system is probably using software flow control. In this case
+you will need to set the
+<A HREF="h_config_preserve_start_stop">&quot;<!--#echo var="FEAT_preserve-start-stop-characters"-->&quot;</A>
+feature.
+<P>
+If you <EM>do</EM> set this
+feature, be advised that if you accidentally hit a Ctrl-S, Alpine will
+mysteriously freeze up with no warning. In this case, try typing a Ctrl-Q
+and see if that puts things right. Printing via the
+&quot;attached-to-ansi&quot; or
+&quot;attached-to-wyse&quot;
+option will automatically enable software
+flow-control handling for the duration of the printing.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+============= h_special_help_nav =============
+<HTML>
+<HEAD>
+<TITLE>Help Text Navigation Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Help Text Navigation Explained</H1>
+
+Alpine contains extensive context-sensitive help text. At any point,
+pressing the &quot;?&quot; key will bring up a page of help text
+explaining the options available to you. You can leave the help
+text screen and return to normal Alpine operation by pressing
+the
+<!--chtml if pinemode="function_key"-->
+F3 function
+<!--chtml else-->
+&quot;E&quot;
+<!--chtml endif-->
+key to Exit Help at any time.
+
+<P>
+Within the help screen you might find a word or phrase displayed in
+inverse text and others displayed in bold typeface. Such words and
+phrases are used to tell you Alpine has more information available on
+the topic they describe.
+The inverted text is the &quot;selected&quot; topic.
+Use the arrow keys, Ctrl-F, and Ctrl-B to change which of the phrases
+displayed in bold type
+is &quot;selected&quot;.
+Hit the Return key to display the information Alpine has available on that
+topic. While viewing such additional information, the
+<!--chtml if pinemode="function_key"-->
+F3 function
+<!--chtml else-->
+&quot;P&quot;
+<!--chtml endif-->
+key will return you to the previous help screen, and the
+<!--chtml if pinemode="function_key"-->
+F2 function
+<!--chtml else-->
+&quot;E&quot;
+<!--chtml endif-->
+key will Exit the Help system altogether.
+
+<P>
+The "N" command will tell you the internal name of the help text you are
+reading each time, so that you can send this name in the text of a message
+and create a direct link to that internal help using the x-pine-help URL
+scheme. For example, the direct link to this item is
+x-pine-help:h_special_help_nav. If you add this text to a message, then
+a person using Pine to read such message would have a direct link to this
+help text.
+
+<P>
+When you are finished reading this help text, you can press the
+<!--chtml if pinemode="function_key"-->
+F3 function
+<!--chtml else-->
+&quot;P&quot;
+<!--chtml endif-->
+key to return to the previously displayed help text.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+============= h_special_list_commands =============
+<HTML>
+<HEAD>
+<TITLE>Email List Commands Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Email List Commands Explained</H1>
+
+Electonic mail lists provide a way for like-minded users to join in
+discussions on topics of interest. The email list itself is
+represented by a
+single address that participants send messages to when they have
+something of interest to share with other members of the list. The
+receiving computer then, either automatically or after review by the
+list's owner (or moderator), sends a copy of that message to each
+member of the list.
+
+<P>
+Usually, subscribing and unsubscribing is done by sending requests in
+an email message to a special address setup to handle managing list
+membership. Often this is the name of the list followed by
+<I>-request</I>. This address is almost <EM>never</EM> the same as
+the address used to send messages to the list.
+
+<P>
+Unfortunately, email list participation commands are more a matter
+of convention than standard, and thus may vary from list to list.
+Increasingly, list management software is adding information to
+the copy of the postings as they're copied to the list members that
+explains how to do the various list management functions.
+
+<P>
+Alpine will recognize this information and offer the management commands
+they represent in a simple display. One or more of the following
+operations will be made available:
+
+<DL>
+<DT>Help</DT>
+<DD>
+A method to get help on subscribing, unsubscribing,
+an explanation of what the list is about, or special instructions
+for participation. This may be in the form of a reply in response
+to an email message, or instructions on how to connect to a Web site.
+</DD>
+
+<DT>Unsubscribe</DT>
+<DD>
+A method to get your email addressed removed from the list of
+recipients of the email list.
+</DD>
+
+<DT>Subscribe</DT>
+<DD>
+A method to get your email address added to the list of recipients
+of the email list. It may be in the form of a message sent to
+a special address or you may be connected to a web site.
+<DD>
+</DD>
+
+<DT>Post</DT>
+<DD>
+A method used to post to the email list. However, it might also
+indicate that no posting is allowed directly to the list.
+</DD>
+
+<DT>Owner</DT>
+<DD>
+A method to contact the list owner for special questions you might
+have regarding the list.
+</DD>
+
+<DT>Archive</DT>
+<DD>
+A method to view an archive of previous messages posted to the list.
+This may be in the form of a mail folder on a remote computer, an
+IMAP mailbox or even a Web site.
+</DD>
+</DL>
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+============= h_quota_command =============
+<HTML>
+<HEAD>
+<TITLE>Quota Screen Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Quota Screen Explained</H1>
+
+<P> This screen summarizes the quota report for this mailbox in the
+IMAP server. For each resource that you have a quota, this reports summarizes
+its use and limit.
+
+<P> Your IMAP server administrator may have set a quota based either on
+the total size of your mailbox (STORAGE), or the number of messages in
+your mailbox (MESSAGES), or some other criteria. This will be reported
+to you indicating the type of quota, its total use and its limit.
+
+<P> The report for STORAGE is reported in kibibytes (KiB). One kibibyte is
+1024 bytes. Each of the characters that you see in this help text is one
+byte, and this help text is about 1 kibibyte in size. Small messages sent
+by Alpine are normally less than 4 kibibytes in size (which includes
+headers and text). Other email programs may send messages with bigger
+sizes when they send messages, since they send plain text and an
+alternative part in HTML.
+
+<P> A convenient way to save space for the STORAGE type of quota is by
+deleting attachments. This is done on each individual message by pressing
+the &quot;V&quot; command while reading the message text, then moving the cursor
+to the position of the attachment that is to be deleted, then pressing
+&quot;D&quot; to delete such attachment, going back to reading the
+message with the &quot;&lt;&quot; command and pressing &quot;S&quot; to
+save the message in the same folder you are reading from. The saved
+message will not have the attachment that was marked deleted. Now you
+can delete and expunge the message with the unwanted attachment.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+============= h_mail_thread_index =============
+<HTML>
+<HEAD>
+<TITLE>THREAD INDEX COMMANDS</TITLE>
+</HEAD>
+<BODY>
+<H1>THREAD INDEX COMMANDS</H1>
+<!--chtml if pinemode="function_key"-->
+Available&nbsp;&nbsp;Commands&nbsp;--&nbsp;Group&nbsp;1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Available&nbsp;Commands&nbsp;--&nbsp;Group&nbsp;2<BR>
+-------------------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-----------------------------<BR>
+F1&nbsp;&nbsp;Show&nbsp;Help&nbsp;Text&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F1&nbsp;&nbsp;Show&nbsp;Help&nbsp;Text<BR>
+F2&nbsp;&nbsp;Toggle&nbsp;to&nbsp;see&nbsp;more&nbsp;commands&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F2&nbsp;&nbsp;Toggle&nbsp;to&nbsp;see&nbsp;more&nbsp;commands<BR>
+F3&nbsp;&nbsp;<A HREF="h_common_folders">FOLDER&nbsp;LIST</A>&nbsp;Screen&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F3&nbsp;&nbsp;MAIN&nbsp;MENU&nbsp;Screen<BR>
+F4&nbsp;&nbsp;View&nbsp;current&nbsp;thread&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F4&nbsp;&nbsp;Quit&nbsp;Alpine<BR>
+F5&nbsp;&nbsp;Move&nbsp;to&nbsp;previous&nbsp;thread&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F5&nbsp;&nbsp;<A HREF="h_common_compose">Compose</A>&nbsp;a&nbsp;message<BR>
+F6&nbsp;&nbsp;Move&nbsp;to&nbsp;next&nbsp;thread&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F6&nbsp;&nbsp;<A HREF="h_common_goto">Goto</A>&nbsp;a&nbsp;specified&nbsp;folder<BR>
+F7&nbsp;&nbsp;Show&nbsp;previous&nbsp;screen&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F7&nbsp;&nbsp;<A HREF="h_common_nextnew">Next&nbsp;new</A>&nbsp;thread<BR>
+F8&nbsp;&nbsp;Show&nbsp;next&nbsp;screen&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F8&nbsp;&nbsp;<A HREF="h_index_cmd_whereis">Whereis</A><BR>
+F9&nbsp;&nbsp;<A HREF="h_common_delete">Mark&nbsp;thread&nbsp;for&nbsp;deletion</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F9&nbsp;&nbsp;<A HREF="h_common_print">Print</A>&nbsp;index<BR>
+F10&nbsp;<A HREF="h_common_delete">Undelete</A>&nbsp;(remove&nbsp;delete&nbsp;mark)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F10&nbsp;<A HREF="h_common_take">Take&nbsp;Address</A>&nbsp;into&nbsp;address&nbsp;book<BR>
+F11&nbsp;<A HREF="h_common_reply">Reply</A>&nbsp;to&nbsp;thread&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F11&nbsp;<A HREF="h_common_save">Save</A>&nbsp;messages&nbsp;into&nbsp;an&nbsp;email&nbsp;folder<BR>
+F12&nbsp;<A HREF="h_common_reply">Forward</A>&nbsp;messages&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F12&nbsp;<A HREF="h_common_save">Export</A>&nbsp;messages&nbsp;into&nbsp;a&nbsp;plain&nbsp;file<BR>
+<BR>
+Available&nbsp;Commands&nbsp;--&nbsp;Group&nbsp;3<BR>
+-----------------------------<BR>
+F3&nbsp;&nbsp;<A HREF="h_index_cmd_expunge">Expunge</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F7&nbsp;<A HREF="h_index_cmd_sort">Sort</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F10&nbsp;<A HREF="h_common_bounce">Bounce</A>&nbsp;(remail)&nbsp;msg<BR>
+F5&nbsp;&nbsp;<A HREF="h_index_cmd_select">Select</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F8&nbsp;<A HREF="h_common_jump">Jump</A>&nbsp;to&nbsp;thread&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F11&nbsp;<A HREF="h_common_flag">Flag</A>&nbsp;messages&nbsp;as&nbsp;important<BR>
+F6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F9&nbsp;<A HREF="h_common_hdrmode">Full&nbsp;Header&nbsp;Mode</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F12&nbsp;<A HREF="h_common_pipe">Pipe</A>&nbsp;to&nbsp;a&nbsp;Unix&nbsp;command<BR>
+<BR>
+Available&nbsp;Commands&nbsp;--&nbsp;Group&nbsp;4<BR>
+-----------------------------<BR>
+F3&nbsp;&nbsp;Select&nbsp;Current&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F4&nbsp;&nbsp;<A HREF="h_index_cmd_zoom">Zoom</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F5&nbsp;&nbsp;COLLECTION&nbsp;LIST&nbsp;Screen
+F6&nbsp;&nbsp;<A HREF="h_common_role">Compose&nbsp;using&nbsp;a&nbsp;role</A><BR>
+<BR>
+<!--chtml else-->
+Navigating&nbsp;the&nbsp;List&nbsp;of&nbsp;Threads&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Operations&nbsp;on&nbsp;the&nbsp;Current&nbsp;Thread<BR>
+-------------------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;---------------------------------<BR>
+&nbsp;P&nbsp;&nbsp;&nbsp;Move&nbsp;to&nbsp;the&nbsp;previous&nbsp;thread&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&gt;&nbsp;&nbsp;View&nbsp;Thread&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;%&nbsp;&nbsp;<A HREF="h_common_print">Print</A><BR>
+&nbsp;N&nbsp;&nbsp;&nbsp;Move&nbsp;to&nbsp;the&nbsp;next&nbsp;thread&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;R&nbsp;&nbsp;<A HREF="h_common_reply">Reply</A>&nbsp;to&nbsp;thread&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F&nbsp;&nbsp;<A HREF="h_common_reply">Forward</A><BR>
+&nbsp;-&nbsp;&nbsp;&nbsp;Show&nbsp;previous&nbsp;screen&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;D&nbsp;&nbsp;<A HREF="h_common_delete">Mark&nbsp;thread&nbsp;for&nbsp;deletion</A><BR>
+Spc&nbsp;&nbsp;(space&nbsp;bar)&nbsp;Show&nbsp;next&nbsp;screen&nbsp;&nbsp;&nbsp;&nbsp;U&nbsp;&nbsp;<A HREF="h_common_delete">Undelete</A>&nbsp;(remove&nbsp;deletion&nbsp;mark)<BR>
+&nbsp;J&nbsp;&nbsp;&nbsp;<A HREF="h_common_jump">Jump</A>&nbsp;to&nbsp;a&nbsp;specific&nbsp;thread&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;T&nbsp;&nbsp;<A HREF="h_common_take">Take&nbsp;Address</A>&nbsp;into&nbsp;Address&nbsp;Book<BR>
+&nbsp;W&nbsp;&nbsp;&nbsp;<A HREF="h_index_cmd_whereis">Whereis</A>&nbsp;--&nbsp;search&nbsp;for&nbsp;a&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;S&nbsp;&nbsp;<A HREF="h_common_save">Save</A>&nbsp;into&nbsp;an&nbsp;email&nbsp;folder<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;specific&nbsp;thread&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;E&nbsp;&nbsp;<A HREF="h_common_save">Export</A>&nbsp;as&nbsp;a&nbsp;plain&nbsp;text&nbsp;file<BR>
+Tab&nbsp;&nbsp;<A HREF="h_common_nextnew">Next&nbsp;new</A>&nbsp;thread&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;B&nbsp;&nbsp;<A HREF="h_common_bounce">Bounce</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;&nbsp;<A HREF="h_common_flag">Flag</A><BR>
+&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;<A HREF="h_common_pipe">Pipe</A>&nbsp;to&nbsp;a&nbsp;Unix&nbsp;Command<BR>
+<BR>
+Miscellaneous&nbsp;Operations&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;General&nbsp;Alpine&nbsp;Commands<BR>
+------------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;---------------------<BR>
+&nbsp;G&nbsp;&nbsp;&nbsp;<A HREF="h_common_goto">Goto</A>&nbsp;a&nbsp;specified&nbsp;folder&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;O&nbsp;&nbsp;Show&nbsp;all&nbsp;other&nbsp;available&nbsp;commands<BR>
+&nbsp;$&nbsp;&nbsp;&nbsp;<A HREF="h_index_cmd_sort">Sort</A>&nbsp;order&nbsp;of&nbsp;index&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;?&nbsp;&nbsp;Show&nbsp;Helptext&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Q&nbsp;Quit&nbsp;Alpine<BR>
+&nbsp;H&nbsp;&nbsp;&nbsp;<A HREF="h_common_hdrmode">Full&nbsp;header&nbsp;mode</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;M&nbsp;&nbsp;MAIN&nbsp;MENU&nbsp;Screen&nbsp;&nbsp;&nbsp;&lt;&nbsp;<A HREF="h_common_folders">FOLDER&nbsp;LIST</A>&nbsp;Screen<BR>
+&nbsp;X&nbsp;&nbsp;&nbsp;<A HREF="h_index_cmd_expunge">Expunge/Exclude</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;C&nbsp;&nbsp;<A HREF="h_common_compose">Compose</A>&nbsp;a&nbsp;new&nbsp;message<BR>
+&nbsp;Z&nbsp;&nbsp;&nbsp;<A HREF="h_index_cmd_zoom">Zoom</A>&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;<A HREF="h_common_role">Compose&nbsp;using&nbsp;a&nbsp;role</A><BR>
+&nbsp;;&nbsp;&nbsp;&nbsp;<A HREF="h_index_cmd_select">Select</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;A&nbsp;<A HREF="h_index_cmd_apply">Apply</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;L&nbsp;&nbsp;COLLECTION&nbsp;LIST&nbsp;Screen<BR>
+&nbsp;:&nbsp;&nbsp;&nbsp;Select&nbsp;Messages&nbsp;in&nbsp;Current&nbsp;Thread<BR>
+<!--chtml endif-->
+<P>
+
+NOTE:
+<OL>
+ <LI>For help on a particular command, highlight the bold text associated
+with it above and hit Return.
+ <LI>Availability of certain commands depends on <A HREF="h_common_conditional_cmds">feature settings</A>.
+</OL>
+
+<H2>Description of the THREAD INDEX Screen</H2>
+
+The THREAD INDEX displays summary information from each
+thread (conversation) in the current folder.
+This is useful if you want to quickly
+scan new threads, or find a particular thread without having to go
+through the text of each message, or to quickly get rid of junk
+threads, etc.
+The current thread is always highlighted.
+Each line of the THREAD INDEX contains the following columns: <P>
+<DL>
+ <DT>STATUS:</DT>
+ <DD> The markings on the left side of the thread tell you about its
+status. You may see one or more of the following codes on any given
+thread:
+<UL>
+ <LI> &quot;D&quot; for Deleted. All of the messages in this thread are marked for deletion but not yet eXpunged from the folder.
+ <LI> &quot;A&quot; for Answered. All of the messages in this thread are marked answered.
+ <LI> &quot;N&quot; for New. At least one message in the thread is New (you haven't looked at it yet).
+ <LI> &quot;+&quot; for direct-to-you. The &quot;+&quot; indicates that a message in the thread was sent directly to your account, your copy is not part of a cc: or a mailing list.
+ <LI> &quot;-&quot; for cc-to-you. The &quot;-&quot; indicates that a
+ message in the thread was sent to you as a cc:. This symbol will only show up if
+ the feature
+ &quot;<A HREF="h_config_mark_for_cc"><!--#echo var="FEAT_mark-for-cc"--></A>&quot; is turned on (which is the default).
+ <LI> &quot;X&quot; for selected. You have selected at least one message in the thread by using the
+ &quot;select&quot; command. (Some systems may optionally allow selected
+ messages to be denoted by the index line being displayed in bold
+ type instead.)
+ <LI> &quot;*&quot; for Important. You have previously used the &quot;Flag&quot; command
+ to mark at least one message in this thread as &quot;important&quot;.
+</UL></DD><P>
+
+ <DT>THREAD NUMBER:</DT>
+ <DD>Threads in a folder are numbered, from one through the number
+of threads in the folder, to help you know where you are in the folder.
+</DD><P>
+
+ <DT>DATE STARTED:</DT>
+ <DD>The date the thread was started. This is actually from the Date header
+of the first message in the thread. It doesn't take different time zones
+into account.</DD><P>
+
+ <DT>WHO STARTED THE THREAD:</DT>
+ <DD>This is usually the name of the sender of the first message in the thread, taken from
+the From header of the message.
+If there is no personal name given in that
+address, then the email address is used instead.
+If the message is from you (or from one of your
+<A HREF="h_config_alt_addresses">alternate addresses</A>),
+then the recipient's name is shown here instead, with the characters
+&quot;To: &quot; inserted before the name.
+(The idea of this is that if you started the thread you would rather see who
+the mail was sent to instead of that the mail was from you.)
+In Newsgroups, if you are
+the sender and there are no email recipients, the newsgroup name will be
+listed after the &quot;To: &quot;.
+</DD><P>
+
+ <DT>SIZE:</DT>
+ <DD>The number in parentheses is the number of messages in the thread.</DD><P>
+
+ <DT>SUBJECT:</DT>
+ <DD>As much of the thread's subject line as will fit on the screen.
+This is the subject of the first message in the thread.</DD>
+</DL>
+
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+============= h_mail_index =============
+<HTML>
+<HEAD>
+<TITLE>MESSAGE INDEX COMMANDS</TITLE>
+</HEAD>
+<BODY>
+<H1>MESSAGE INDEX COMMANDS</H1>
+<!--chtml if pinemode="function_key"-->
+Available&nbsp;&nbsp;Commands&nbsp;--&nbsp;Group&nbsp;1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Available&nbsp;Commands&nbsp;--&nbsp;Group&nbsp;2<BR>
+-------------------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-----------------------------<BR>
+F1&nbsp;&nbsp;Show&nbsp;Help&nbsp;Text&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F1&nbsp;&nbsp;Show&nbsp;Help&nbsp;Text<BR>
+F2&nbsp;&nbsp;Toggle&nbsp;to&nbsp;see&nbsp;more&nbsp;commands&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F2&nbsp;&nbsp;Toggle&nbsp;to&nbsp;see&nbsp;more&nbsp;commands<BR>
+F3&nbsp;&nbsp;<A HREF="h_common_folders">FOLDER&nbsp;LIST</A>&nbsp;Screen&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F3&nbsp;&nbsp;MAIN&nbsp;MENU&nbsp;Screen<BR>
+F4&nbsp;&nbsp;View&nbsp;current&nbsp;message&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F4&nbsp;&nbsp;Quit&nbsp;Alpine<BR>
+F5&nbsp;&nbsp;Move&nbsp;to&nbsp;previous&nbsp;message&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F5&nbsp;&nbsp;<A HREF="h_common_compose">Compose</A>&nbsp;a&nbsp;message<BR>
+F6&nbsp;&nbsp;Move&nbsp;to&nbsp;next&nbsp;message&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F6&nbsp;&nbsp;<A HREF="h_common_goto">Goto</A>&nbsp;a&nbsp;specified&nbsp;folder<BR>
+F7&nbsp;&nbsp;Show&nbsp;previous&nbsp;screen&nbsp;of&nbsp;messages&nbsp;&nbsp;&nbsp;F7&nbsp;&nbsp;<A HREF="h_common_nextnew">Next&nbsp;new</A>&nbsp;message<BR>
+F8&nbsp;&nbsp;Show&nbsp;next&nbsp;screen&nbsp;of&nbsp;messages&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F8&nbsp;&nbsp;<A HREF="h_index_cmd_whereis">Whereis</A><BR>
+F9&nbsp;&nbsp;<A HREF="h_common_delete">Mark&nbsp;message&nbsp;for&nbsp;deletion</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F9&nbsp;&nbsp;<A HREF="h_common_print">Print</A>&nbsp;message<BR>
+F10&nbsp;<A HREF="h_common_delete">Undelete</A>&nbsp;(remove&nbsp;delete&nbsp;mark)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F10&nbsp;<A HREF="h_common_take">Take&nbsp;Address</A>&nbsp;into&nbsp;address&nbsp;book<BR>
+F11&nbsp;<A HREF="h_common_reply">Reply</A>&nbsp;to&nbsp;message&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F11&nbsp;<A HREF="h_common_save">Save</A>&nbsp;message&nbsp;into&nbsp;an&nbsp;email&nbsp;folder<BR>
+F12&nbsp;<A HREF="h_common_reply">Forward</A>&nbsp;message&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F12&nbsp;<A HREF="h_common_save">Export</A>&nbsp;message&nbsp;into&nbsp;a&nbsp;plain&nbsp;file<BR>
+<BR>
+Available&nbsp;Commands&nbsp;--&nbsp;Group&nbsp;3<BR>
+-----------------------------<BR>
+F3&nbsp;&nbsp;<A HREF="h_index_cmd_expunge">Expunge/Exclude</A>&nbsp;&nbsp;&nbsp;&nbsp;F7&nbsp;<A HREF="h_index_cmd_sort">Sort</A>&nbsp;order&nbsp;of&nbsp;index&nbsp;&nbsp;F10&nbsp;<A HREF="h_common_bounce">Bounce</A>&nbsp;(remail)&nbsp;msg<BR>
+F5&nbsp;&nbsp;<A HREF="h_index_cmd_select">Select</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F8&nbsp;<A HREF="h_common_jump">Jump</A>&nbsp;to&nbsp;message&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F11&nbsp;<A HREF="h_common_flag">Flag</A>&nbsp;message&nbsp;as&nbsp;important<BR>
+F6&nbsp;&nbsp;<A HREF="h_index_cmd_apply">Apply</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F9&nbsp;<A HREF="h_common_hdrmode">Full&nbsp;Header&nbsp;Mode</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F12&nbsp;<A HREF="h_common_pipe">Pipe</A>&nbsp;to&nbsp;a&nbsp;Unix&nbsp;command<BR>
+<BR>
+Available&nbsp;Commands&nbsp;--&nbsp;Group&nbsp;4<BR>
+-----------------------------<BR>
+F3&nbsp;&nbsp;Select&nbsp;Current&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F4&nbsp;&nbsp;<A HREF="h_index_cmd_zoom">Zoom</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F5&nbsp;&nbsp;COLLECTION&nbsp;LIST&nbsp;Screen
+F6&nbsp;&nbsp;<A HREF="h_common_role">Compose using a role</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F10&nbsp;<A HREF="h_index_collapse_expand">Collapse/Expand</A>&nbsp;Thread<BR>
+<BR>
+<!--chtml else-->
+Navigating&nbsp;the&nbsp;List&nbsp;of&nbsp;Messages&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Operations&nbsp;on&nbsp;the&nbsp;Current&nbsp;Message<BR>
+-------------------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;---------------------------------<BR>
+&nbsp;P&nbsp;&nbsp;&nbsp;Move&nbsp;to&nbsp;the&nbsp;previous&nbsp;message&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&gt;&nbsp;&nbsp;View&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;%&nbsp;&nbsp;<A HREF="h_common_print">Print</A><BR>
+&nbsp;N&nbsp;&nbsp;&nbsp;Move&nbsp;to&nbsp;the&nbsp;next&nbsp;message&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;R&nbsp;&nbsp;<A HREF="h_common_reply">Reply</A>&nbsp;to&nbsp;message&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F&nbsp;&nbsp;<A HREF="h_common_reply">Forward</A><BR>
+&nbsp;-&nbsp;&nbsp;&nbsp;Show&nbsp;previous&nbsp;screen&nbsp;of&nbsp;messages&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;D&nbsp;&nbsp;<A HREF="h_common_delete">Mark&nbsp;for&nbsp;deletion</A><BR>
+Spc&nbsp;&nbsp;(space&nbsp;bar)&nbsp;Show&nbsp;next&nbsp;screen&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;U&nbsp;&nbsp;<A HREF="h_common_delete">Undelete</A>&nbsp;(remove&nbsp;deletion&nbsp;mark)<BR>
+&nbsp;J&nbsp;&nbsp;&nbsp;<A HREF="h_common_jump">Jump</A>&nbsp;to&nbsp;a&nbsp;specific&nbsp;message&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;T&nbsp;&nbsp;<A HREF="h_common_take">Take&nbsp;Address</A>&nbsp;into&nbsp;Address&nbsp;Book<BR>
+&nbsp;W&nbsp;&nbsp;&nbsp;<A HREF="h_index_cmd_whereis">Whereis</A>&nbsp;--&nbsp;search&nbsp;for&nbsp;a&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;S&nbsp;&nbsp;<A HREF="h_common_save">Save</A>&nbsp;into&nbsp;an&nbsp;email&nbsp;folder<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;specific&nbsp;message&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;E&nbsp;&nbsp;<A HREF="h_common_save">Export</A>&nbsp;as&nbsp;a&nbsp;plain&nbsp;text&nbsp;file<BR>
+Tab&nbsp;&nbsp;<A HREF="h_common_nextnew">Next&nbsp;new</A>&nbsp;message&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;B&nbsp;&nbsp;<A HREF="h_common_bounce">Bounce</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;&nbsp;<A HREF="h_common_flag">Flag</A><BR>
+&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;<A HREF="h_common_pipe">Pipe</A>&nbsp;to&nbsp;a&nbsp;Unix&nbsp;Command<BR>
+Miscellaneous&nbsp;Operations<BR>
+------------------------<BR>
+&nbsp;G&nbsp;&nbsp;&nbsp;<A HREF="h_common_goto">Goto</A>&nbsp;a&nbsp;specified&nbsp;folder&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;General&nbsp;Alpine&nbsp;Commands<BR>
+&nbsp;$&nbsp;&nbsp;&nbsp;<A HREF="h_index_cmd_sort">Sort</A>&nbsp;order&nbsp;of&nbsp;index&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;---------------------<BR>
+&nbsp;H&nbsp;&nbsp;&nbsp;<A HREF="h_common_hdrmode">Full&nbsp;header&nbsp;mode</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;O&nbsp;&nbsp;Show&nbsp;all&nbsp;other&nbsp;available&nbsp;commands<BR>
+&nbsp;X&nbsp;&nbsp;&nbsp;<A HREF="h_index_cmd_expunge">Expunge/Exclude</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;?&nbsp;&nbsp;Show&nbsp;Help&nbsp;text&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Q&nbsp;Quit&nbsp;Alpine<BR>
+&nbsp;Z&nbsp;&nbsp;&nbsp;<A HREF="h_index_cmd_zoom">Zoom</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;M&nbsp;&nbsp;MAIN&nbsp;MENU&nbsp;Screen&nbsp;&nbsp;&nbsp;&lt;&nbsp;<A HREF="h_common_folders">FOLDER&nbsp;LIST</A>&nbsp;Screen<BR>
+&nbsp;;&nbsp;&nbsp;&nbsp;<A HREF="h_index_cmd_select">Select</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;A&nbsp;<A HREF="h_index_cmd_apply">Apply</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;C&nbsp;&nbsp;<A HREF="h_common_compose">Compose</A>&nbsp;a&nbsp;new&nbsp;message<BR>
+&nbsp;:&nbsp;&nbsp;&nbsp;Select&nbsp;Current&nbsp;message&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;&nbsp;<A HREF="h_common_role">Compose using a role</A><BR>
+&nbsp;/&nbsp;&nbsp;&nbsp;<A HREF="h_index_collapse_expand">Collapse/Expand</A>&nbsp;Thread&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;L&nbsp;&nbsp;COLLECTION&nbsp;LIST&nbsp;Screen<BR>
+<!--chtml endif-->
+<P>
+
+NOTE:
+<OL>
+ <LI>For help on a particular command, highlight the bold text associated
+with it above and hit Return.
+ <LI>Availability of certain commands depends on <A HREF="h_common_conditional_cmds">feature settings</A>.
+</OL>
+
+<H2>Description of the MESSAGE INDEX Screen</H2>
+
+The MESSAGE INDEX displays summary information from each
+message in the current folder.
+This is useful if you want to quickly
+scan new messages, or find a particular message without having to go
+through the text of each message, or to quickly get rid of junk
+messages, etc.
+<P>
+The current message is always highlighted
+and many commands operate on the current message.
+For example, the Delete command will delete the current message.
+If the folder is sorted by either Threads or OrderedSubject, then, depending
+on some of your configuration settings, a single line in the index may
+refer to an entire thread or to a subthread.
+If that is the case, then the commands that normally operate on the current
+message will operate on the thread or subthread instead.
+For example, the Delete command will delete the whole collapsed thread
+instead of just a single message.
+<P>
+Each line of the MESSAGE INDEX contains the following columns (by default --
+you can change this with the
+&quot;<A HREF="h_config_index_format"><!--#echo var="VAR_index-format"--></A>&quot; option
+in the SETUP CONFIGURATION screen): <P>
+<DL>
+ <DT>STATUS:</DT>
+ <DD> The markings on the left side of the message tell you about its
+status. You may see one or more of the following codes on any given
+message:
+<UL>
+ <LI> &quot;D&quot; for Deleted. You have marked this message for deletion but not
+ yet eXpunged the folder.
+ <LI> &quot;N&quot; for New. You have not looked at the text of the message yet.
+ <LI> &quot;A&quot; for Answered. Any time you reply to a message it is considered
+ to be answered.
+ <LI> &quot;F&quot; for Forwarded. Similar to Answered, this is set whenever you
+ forward a message.
+ <LI> &quot;+&quot; for direct-to-you. The &quot;+&quot; indicates that a message was sent
+ directly to your account, your copy is not part of a cc: or a
+ mailing list.
+ <LI> &quot;-&quot; for cc-to-you. The &quot;-&quot; indicates that a
+ message was sent to you as a cc:. This symbol will only show up if
+ the feature
+ &quot;<A HREF="h_config_mark_for_cc"><!--#echo var="FEAT_mark-for-cc"--></A>&quot; is turned on (which is the default).
+ <LI> &quot;X&quot; for selected. You have selected the message by using the
+ &quot;select&quot; command. (Some systems may optionally allow selected
+ messages to be denoted by the index line being displayed in bold
+ type.)
+ <LI> &quot;*&quot; for Important. You have previously used the &quot;Flag&quot; command
+ to mark this message as &quot;important&quot;.
+</UL></DD><P>
+
+ <DT>MESSAGE NUMBER:</DT>
+ <DD>Messages in a folder are numbered, from one through the number
+of messages in the folder, to help you know where you are in the folder.
+These numbers are always in increasing order, even if you sort the folder
+in a different way.</DD><P>
+
+ <DT>DATE SENT:</DT>
+ <DD>The date the message was sent. By default, messages are
+ordered by arrival time, not by date sent. Most of the time, arrival time
+and date sent (effectively departure time) are similar. Sometimes,
+however, the index will appear to be out of order because a message took a
+long time in delivery or because the sender is in a different time
+zone than you are. This date is just the date from the Date header
+field in the message.</DD><P>
+
+ <DT>WHO SENT THE MESSAGE:</DT>
+ <DD>This is usually the name of the sender of the message, taken from
+the From header of the message.
+If there is no personal name given in that
+address, then the email address is used instead.
+If the message is from you (or from one of your
+<A HREF="h_config_alt_addresses">alternate addresses</A>),
+then the recipient's name is shown here instead, with the characters
+&quot;To: &quot; inserted before the name.
+(The idea of this is that if you sent the mail you would rather see who
+the mail was sent to instead of that the mail was from you.
+This behavior may be changed by modifying the <!--#echo var="VAR_index-format"--> option mentioned
+above.
+In particular, use the FROM token or the FROMORTONOTNEWS token
+in place of the FROMORTO token.)
+In Newsgroups, if you are
+the sender and there are no email recipients, the newsgroup name will be
+listed after the &quot;To: &quot;. </DD><P>
+
+ <DT>SIZE:</DT>
+ <DD>The number in parentheses is the number of characters in the message.
+It may have a suffix of K, M, or G which means the number should be
+multiplied by one thousand, one million, or one billion to get the
+size of the message.</DD><P>
+
+ <DT>SUBJECT:</DT>
+ <DD>As much of the message's subject line as will fit on the screen.</DD>
+</DL>
+
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+============= h_mail_view ========================
+<HTML>
+<HEAD>
+<TITLE>MESSAGE TEXT SCREEN</TITLE>
+</HEAD>
+<BODY>
+<H1>MESSAGE TEXT SCREEN</H1>
+<!--chtml if pinemode="function_key"-->
+Available&nbsp;&nbsp;Commands&nbsp;--&nbsp;Group&nbsp;&nbsp;1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Available&nbsp;Commands&nbsp;--&nbsp;Group&nbsp;2<BR>
+-------------------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;------------------------------<BR>
+F1&nbsp;&nbsp;Show&nbsp;Help&nbsp;Text&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F1&nbsp;&nbsp;Show&nbsp;Help&nbsp;Text<BR>
+F2&nbsp;&nbsp;Toggle&nbsp;to&nbsp;see&nbsp;more&nbsp;commands&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F2&nbsp;&nbsp;Toggle&nbsp;to&nbsp;see&nbsp;more&nbsp;commands<BR>
+F3&nbsp;&nbsp;<A HREF="h_common_index">MESSAGE&nbsp;INDEX</A>&nbsp;Screen&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F3&nbsp;&nbsp;MAIN&nbsp;MENU&nbsp;Screen<BR>
+F4&nbsp;&nbsp;<A HREF="h_view_cmd_viewattch">View&nbsp;attachment</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F4&nbsp;&nbsp;Quit&nbsp;Alpine<BR>
+F5&nbsp;&nbsp;Display&nbsp;previous&nbsp;message&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F5&nbsp;&nbsp;<A HREF="h_common_folders">FOLDER&nbsp;LIST</A>&nbsp;Screen<BR>
+F6&nbsp;&nbsp;Display&nbsp;next&nbsp;message&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F6&nbsp;&nbsp;<A HREF="h_common_goto">Goto</A>&nbsp;a&nbsp;specified&nbsp;folder<BR>
+F7&nbsp;&nbsp;Previous&nbsp;screen&nbsp;of&nbsp;this&nbsp;message&nbsp;&nbsp;&nbsp;&nbsp;F7&nbsp;&nbsp;<A HREF="h_common_compose">Compose</A>&nbsp;message<BR>
+F8&nbsp;&nbsp;Next&nbsp;screen&nbsp;of&nbsp;this&nbsp;message&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F8&nbsp;&nbsp;<A HREF="h_view_cmd_whereis">Whereis</A><BR>
+F9&nbsp;&nbsp;<A HREF="h_common_delete">Mark&nbsp;message&nbsp;for&nbsp;deletion</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F9&nbsp;&nbsp;<A HREF="h_common_print">Print</A>&nbsp;message<BR>
+F10&nbsp;<A HREF="h_common_delete">Undelete</A>&nbsp;(remove&nbsp;delete&nbsp;mark)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F10&nbsp;<A HREF="h_common_take">Take&nbsp;Address</A>&nbsp;into&nbsp;address&nbsp;book<BR>
+F11&nbsp;<A HREF="h_common_reply">Reply</A>&nbsp;to&nbsp;message&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F11&nbsp;<A HREF="h_common_save">Save</A>&nbsp;message&nbsp;into&nbsp;an&nbsp;email&nbsp;folder<BR>
+F12&nbsp;<A HREF="h_common_reply">Forward</A>&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;F12&nbsp;<A HREF="h_common_save">Export</A>&nbsp;message&nbsp;into&nbsp;a&nbsp;plain&nbsp;file<BR>
+<BR>
+Available&nbsp;Commands&nbsp;--&nbsp;Group&nbsp;3<BR>
+------------------------------<BR>
+F1&nbsp;&nbsp;Show&nbsp;Help&nbsp;Text<BR>
+F2&nbsp;&nbsp;Toggle&nbsp;to&nbsp;see&nbsp;more&nbsp;commands<BR>
+F3&nbsp;&nbsp;<A HREF="h_view_cmd_hilite">View&nbsp;hilited</A><BR>
+F4&nbsp;&nbsp;Select&nbsp;current&nbsp;message<BR>
+F5&nbsp;&nbsp;Previous&nbsp;selectable&nbsp;item<BR>
+F6&nbsp;&nbsp;Next&nbsp;selectable&nbsp;item<BR>
+F7&nbsp;&nbsp;<A HREF="h_common_jump">Jump</A>&nbsp;to&nbsp;message&nbsp;number<BR>
+F8&nbsp;&nbsp;<A HREF="h_common_nextnew">Next&nbsp;new</A>&nbsp;message<BR>
+F9&nbsp;&nbsp;<A HREF="h_common_hdrmode">Display&nbsp;full&nbsp;headers</A><BR>
+F10&nbsp;<A HREF="h_common_bounce">Bounce</A>&nbsp;message<BR>
+F11&nbsp;<A HREF="h_common_flag">Flag</A>&nbsp;message<BR>
+F12&nbsp;<A HREF="h_common_pipe">Pipe</A>&nbsp;to&nbsp;a&nbsp;Unix&nbsp;command<BR>
+Available&nbsp;Commands&nbsp;--&nbsp;Group&nbsp;4<BR>
+F5&nbsp;&nbsp;<A HREF="h_common_role">Compose using a role</A><BR>
+<!--chtml else-->
+Operations&nbsp;on&nbsp;the&nbsp;Current&nbsp;Message<BR>
+---------------------------------<BR>
+<BR>
+&nbsp;-&nbsp;&nbsp;&nbsp;Show&nbsp;previous&nbsp;page&nbsp;of&nbsp;this&nbsp;msg&nbsp;&nbsp;&nbsp;&nbsp;S&nbsp;&nbsp;<A HREF="h_common_save">Save</A>&nbsp;into&nbsp;an&nbsp;email&nbsp;folder<BR>
+Spc&nbsp;&nbsp;(space&nbsp;bar)&nbsp;Show&nbsp;next&nbsp;page&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;E&nbsp;&nbsp;<A HREF="h_common_save">Export</A>&nbsp;as&nbsp;a&nbsp;plain&nbsp;text&nbsp;file<BR>
+&nbsp;&gt;&nbsp;&nbsp;&nbsp;<A HREF="h_view_cmd_viewattch">View&nbsp;attachment</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;B&nbsp;&nbsp;<A HREF="h_common_bounce">Bounce</A><BR>
+&nbsp;R&nbsp;&nbsp;&nbsp;<A HREF="h_common_reply">Reply</A>&nbsp;to&nbsp;message&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;F&nbsp;&nbsp;<A HREF="h_common_reply">Forward</A>&nbsp;message<BR>
+&nbsp;D&nbsp;&nbsp;&nbsp;<A HREF="h_common_delete">Mark&nbsp;for&nbsp;deletion</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Ret&nbsp;View&nbsp;<A HREF="h_view_cmd_hilite">hilited</A>&nbsp;item<BR>
+&nbsp;U&nbsp;&nbsp;&nbsp;<A HREF="h_common_delete">Undelete</A>&nbsp;(remove&nbsp;deletion&nbsp;mark)&nbsp;&nbsp;^F&nbsp;&nbsp;Select&nbsp;next&nbsp;<A HREF="h_view_cmd_hilite">hilited</A>&nbsp;item&nbsp;in&nbsp;message<BR>
+&nbsp;T&nbsp;&nbsp;&nbsp;<A HREF="h_common_take">Take&nbsp;Address</A>&nbsp;into&nbsp;Address&nbsp;Book&nbsp;&nbsp;&nbsp;^B&nbsp;&nbsp;Select&nbsp;previous&nbsp;<A HREF="h_view_cmd_hilite">hilited</A>&nbsp;item<BR>
+&nbsp;%&nbsp;&nbsp;&nbsp;<A HREF="h_common_print">Print</A>&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;<A HREF="h_common_flag">Flag</A>&nbsp;message<BR>
+&nbsp;W&nbsp;&nbsp;&nbsp;<A HREF="h_view_cmd_whereis">Whereis</A>:&nbsp;search&nbsp;for&nbsp;text&nbsp;in&nbsp;msg&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;<A HREF="h_common_pipe">Pipe</A>&nbsp;to&nbsp;a&nbsp;Unix&nbsp;command<BR>
+<BR>
+Navigating&nbsp;the&nbsp;List&nbsp;of&nbsp;Messages&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Other&nbsp;Commands<BR>
+-------------------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;----------------------------<BR>
+&nbsp;P&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Display&nbsp;previous&nbsp;message&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;G&nbsp;&nbsp;&nbsp;<A HREF="h_common_goto">Goto</A>&nbsp;a&nbsp;specified&nbsp;folder<BR>
+&nbsp;N&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Display&nbsp;next&nbsp;message&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;H&nbsp;&nbsp;&nbsp;<A HREF="h_common_hdrmode">Full&nbsp;header&nbsp;mode</A>&nbsp;on/off<BR>
+&nbsp;J&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="h_common_jump">Jump</A>&nbsp;to&nbsp;a&nbsp;specific&nbsp;message&nbsp;&nbsp;&nbsp;&nbsp;:&nbsp;&nbsp;&nbsp;Select&nbsp;Current&nbsp;message<BR>
+Tab&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="h_common_nextnew">Next&nbsp;new</A>&nbsp;message&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;A&nbsp;&nbsp;&nbsp;<A HREF="h_config_prefer_plain_text">Toggle&nbsp;Prefer&nbsp;Plain&nbsp;Text</A><BR>
+<BR>
+General&nbsp;Alpine&nbsp;Commands<BR>
+---------------------<BR>
+&nbsp;O&nbsp;&nbsp;Show&nbsp;all&nbsp;other&nbsp;available&nbsp;commands<BR>
+&nbsp;?&nbsp;&nbsp;Show&nbsp;Help&nbsp;text&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Q&nbsp;Quit&nbsp;Alpine<BR>
+&nbsp;M&nbsp;&nbsp;MAIN&nbsp;MENU&nbsp;Screen&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;L&nbsp;<A HREF="h_common_folders">FOLDER&nbsp;LIST</A>&nbsp;Screen&nbsp;(or&nbsp;COLLECTION&nbsp;LIST&nbsp;Screen)<BR>
+&nbsp;&lt;&nbsp;&nbsp;<A HREF="h_common_index">MESSAGE&nbsp;INDEX</A>&nbsp;Screen&nbsp;&nbsp;C&nbsp;<A HREF="h_common_compose">Compose</A>&nbsp;a&nbsp;new&nbsp;message<BR>
+&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;<A HREF="h_common_role">Compose&nbsp;using&nbsp;a&nbsp;role</A><BR>
+<!--chtml endif-->
+<P>
+
+NOTE:
+<OL>
+ <LI>For help on a particular command, highlight the bold text associated
+with it above and hit Return.
+ <LI>Availability of certain commands depends on <A HREF="h_common_conditional_cmds">feature settings</A>.
+</OL>
+
+<H2>Description of the MESSAGE TEXT Screen</H2>
+
+The top line of the view message screen displays status
+information about the currently open collection and folder and about the
+current message. It shows the name of the collection in angle brackets
+and then the name of the folder. The line also displays the number
+of messages in the folder, the number of the current message and the
+percentage of the current message that has been displayed on the screen.
+If the message is marked for deletion &quot;DEL&quot; will appear in the upper
+right corner.
+If the message has been answered (but not deleted) &quot;ANS&quot; will show
+in the corner.
+<P>
+
+NOTE: to rapidly move to the end of a message, hit the
+<!--chtml if pinemode="function_key"-->F8<!--chtml else-->W<!--chtml endif-->
+(or Ctrl-W) key followed
+by Ctrl-V. Similarly,
+<!--chtml if pinemode="function_key"-->F8<!--chtml else-->W<!--chtml endif-->
+followed by Ctrl-Y will take you to the beginning of
+a message.
+
+<H2>Explanation of Alternate Character Sets</H2>
+
+Alpine attempts to stay out of the way so that it won't prevent you from
+viewing mail in any character set. It will simply send the message to
+your display device. If the device is capable of displaying the
+message as it was written it will do so. If not, the display may be
+partially or totally incorrect.
+If the message contains characters that are not representable in your
+<A HREF="h_config_char_set">&quot;Display Character Set&quot;</A>
+variable in your configuration, then a warning message will be printed
+to your screen at the beginning of the message display.
+It is probably best to use UNIX Alpine in a terminal emulator
+capable of displaying UTF-8 characters.
+See <A HREF="h_config_char_set">Display Character Set</A> for a little
+more information about character set settings.
+
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_index_cmd_select =======
+<HTML>
+<HEAD>
+<TITLE>Selecting: Select and WhereIs/Select</TITLE>
+</HEAD>
+<BODY>
+<H1>Selecting: Select and WhereIs/Select</H1>
+
+Aggregate operations give you the ability to process a group of messages
+at once. Acting on multiple messages requires two steps: (1) selecting a
+set of messages and then; (2) applying a command to that set. The first
+part is handled by the select command. Select allows you to
+select messages based on their status (read, answered, etc.), contents,
+date, size, or keywords.
+You may also select based on one of your Rules or based on threads,
+and there are quick options to select a specific message or range of messages,
+to select the current message, or to select all messages.
+<P>
+
+We describe the various selection criteria briefly:
+<P>
+
+<DL>
+<DT>select All</DT>
+<DD> Marks all the messages in the folder as selected.
+</DD>
+
+<DT>select Cur</DT>
+<DD> Selects the currently highlighted message or currently highlighted
+set of messages if in a threaded view.
+</DD>
+
+<DT>select by Number</DT>
+<DD> Select by message number. This may be a comma-separated list instead or
+a single entry.
+Each element in the list may be either a single message number or a range
+of numbers with a dash between the lowest and highest member of the range.
+Some examples are 7 to select only message number 7; 2-5 to select messages
+2 through 5; and 2-5,7-9,11 to select messages 2, 3, 4, 5, 7, 8, 9, and 11.
+The word &quot;end&quot; may be used as a substitute for the highest numbered
+message in the folder.
+If in a separate thread index where the numbers refer to threads instead of
+to messages, then you will be selecting all of the messages in the
+referenced threads instead of selecting by message number.
+</DD>
+
+<DT>select by Date</DT>
+<DD> Select by either the date stored in the Date headers of each message,
+or by the date when the messages arrived.
+This does not adjust for different time zones, but just checks to see what
+day the message was sent on.
+You may type in a date. If you do, the date should be in the form
+<P><SAMP><CENTER>DD-Mon-YYYY</CENTER></SAMP><P>
+For example,
+<P><SAMP><CENTER>24-Nov-2004</CENTER></SAMP><P>
+or
+<P><SAMP><CENTER>09-Nov-2004</CENTER></SAMP><P>
+If the date you want is close to the current date, it is probably
+easier to use the &quot;^P&nbsp;Prev Day&quot; or &quot;^N&nbsp;Next Day&quot; commands to change the default date that
+is displayed, instead of typing in the date yourself.
+Or, the &quot;^X&nbsp;Cur Msg&quot; command may be used to fill in
+the date of the currently highlighted message.
+<P>
+There are six possible settings that are selected using the
+&quot;^W&nbsp;Toggle When&quot; command.
+Three of them select messages based on the Date headers.
+They are &quot;SENT SINCE&quot;, &quot;SENT BEFORE&quot;,
+and &quot;SENT ON&quot;.
+SINCE is all messages with the selected date or later.
+BEFORE is all messages earlier than the selected date (not including the day
+itself).
+ON is all messages sent on the selected date.
+The other three select messages in the same way but they use the arrival
+times of the messages instead of the Date headers included in the messages.
+Those three are &quot;ARRIVED SINCE&quot;, &quot;ARRIVED BEFORE&quot;,
+and &quot;ARRIVED ON&quot;.
+When you save a message from one folder to another the arrival time is
+preserved.
+</DD>
+
+<DT>select by Text</DT>
+<DD> Selects messages based on the message contents.
+This allows you to select a set of messages based on whether or not the
+message headers or message body contain specified text.
+You may look for text in the Subject, the From header,
+the To header, or the Cc header.
+You may also choose Recipient, which searches for the text in
+either the To or the Cc header;
+or Participant, which means To or Cc or From.
+Besides those specific header searches, you may also search the entire
+header and text of the message with &quot;All Text&quot;, or just the
+body of the message.
+<P>
+To search for the absence of text, first type the &quot;! Not&quot; command
+before typing the specific type of text search.
+For example, you could type &quot;!&quot; followed by &quot;S&quot; to
+search for all messages that do not contain a particular word in their
+Subjects.
+<P>
+If you choose a Subject search, you may use the subject from the current
+message by typing the &quot;^X Cur Subject&quot; command.
+You may then edit it further if you wish.
+For example, you might select the subject of a reply and edit the
+&quot;Re:&nbsp;&quot; off of the front of it in order to search for
+the original message being replied to.
+All of the other header searches allow you to use addresses from the
+headers of the current message if you want to.
+You may use the &quot;^T Cur To&quot;, &quot;^R Cur From&quot;, or
+&quot;^W Cur Cc&quot;.
+In each case, if there is more than one address, only the first is offered.
+</DD>
+
+<DT>select by Status</DT>
+<DD> Selects messages based on their status.
+You may select all New, Important, Deleted, Answered, Recent, or Unseen
+messages.
+Or, if you first type the &quot;! Not&quot; command, you get not New,
+or not Important, and so on.
+If you select Deleted messages, you will get all messages with their
+Deleted flag set.
+Likewise for Important messages, all messages that you have flagged as
+being Important with the
+<A HREF="h_common_flag">Flag</A> command.
+The &quot;New&quot; and &quot;Answered&quot; choices are a little bit odd
+because they try to match what you see on the screen by default.
+&quot;New&quot; is a shorthand for messages that are Unseen, Undeleted,
+and Unanswered.
+If you have looked at the message, or deleted it, or answered it; then it
+is not considered &quot;New &quot;.
+&quot;!&nbsp;New&quot; is the opposite of &quot;New&quot;.
+<P>
+&quot;Answered&quot; is another one that is a little different.
+It means that the message has been Answered <EM>and</EM> is not deleted.
+And to make it even more confusing, &quot;!&nbsp;Answered&quot; is not
+the opposite of &quot;Answered&quot;!
+Instead, &quot;!&nbsp;Answered&quot; stands for messages that are
+both Unanswered <EM>and</EM> not deleted.
+<P>
+The other two types were added later because the special nature of the
+New flag was not what was wanted by all users.
+New does match what you see in the index by default, but if you use
+the IMAPSTATUS or SHORTIMAPSTATUS token in the
+&quot;<A HREF="h_config_index_format"><!--#echo var="VAR_index-format"--></A>&quot; it may not
+be exactly what you want.
+&quot;Unseen&quot; simply selects all unseen messages, whether or not
+they are deleted or answered, and
+&quot;Recent&quot; selects all of the messages that have been added to
+the folder since you started this Alpine session.
+(That's not technically quite true. If there are multiple mail clients
+reading an IMAP mailbox, each message will be marked as Recent in only
+one of the client's sessions.)
+</DD>
+
+<DT>select by siZe</DT>
+<DD> Selects messages based on their size being smaller than or larger
+than the size you specify.
+The size is the number of bytes.
+You may use the suffix &quot;K&quot; or &quot;k&quot; to mean 1,000 times
+the number.
+For example, 7K is the same as 7000.
+The suffix &quot;M&quot; or &quot;m&quot; means 1,000,000 times the number,
+and the suffix &quot;G&quot; or &quot;g&quot; means 1,000,000,000 times.
+Use the &quot;^W&quot; command to toggle back and forth between Smaller
+and Larger.
+</DD>
+
+<DT>select by Keyword</DT>
+<DD> Selects messages that either have or do not have
+(using the &quot;!&nbsp;Not&quot; command)
+a particular <A HREF="h_config_keywords">Keyword</A> set.
+One way to select a keyword is to use the &quot;^T&nbsp;To&nbsp;List&quot;
+command to select one from your list of keywords.
+The
+<A HREF="h_config_flag_screen_kw_shortcut"><!--#echo var="FEAT_enable-flag-screen-keyword-shortcut"--></A> option allows selecting by Keyword initials if set.
+</DD>
+
+<DT>select by Rule</DT>
+<DD> Selects messages that either match or don't match
+(using the &quot;!&nbsp;Not&quot; command)
+one of the Rules you have defined.
+The most likely method of filling in the Rule is to use the
+&quot;^T&nbsp;To&nbsp;List&quot;
+command to select one of your Rules.
+All of the Rules you have defined will be in the list, including
+Rules for Searching, Indexcolors, Filtering, Roles, Score setting, and Other.
+They may not all make sense for this purpose, but they are all there for
+flexibility.
+You might find it useful to define some rules solely for the purpose
+of being used by the Select command.
+There is a special category for such Rules. They are called Search Rules.
+<P>
+Unfortunately, Alpine does not allow all possible Rules to be defined.
+For example, there is no logical OR operation.
+OR is accomplished in the Filter Rules or the other types of Rules by
+simply defining two rules, one that matches the first part of the OR
+and another that matches the second part.
+But you can't do that here, since you only have a single Rule to work with.
+Likewise, the order of Rules is usually important.
+For example, if the first Filter Rule (or Indexcolor rule or ...) matches
+a message, then that stops the search for a further match.
+This means that you may be confused if you try to use Select by Rule to
+check your Filter rules because the order is important when filtering but
+is not considered here.
+</DD>
+
+<DT>select by tHread</DT>
+<DD> Selects all of the messages in the current thread.
+</DD>
+</DL>
+
+After you have an initial selection, the next and subsequent selection
+commands modify the selection.
+The select command changes. It first gives
+you selection &quot;alteration&quot; options: &quot;unselect All&quot;,
+&quot;unselect Current&quot;,
+&quot;Broaden selection&quot; (implements a logical OR), and
+&quot;Narrow selection&quot; (implements a logical AND).
+After you choose either Broaden or Narrow, you then choose one of the
+selection criteria listed above (by Text or Number or ...).
+You may use select as many times as you wish to get the selected set right.
+<P>
+
+The WhereIs command has a feature (Ctrl-X) to
+select all the messages that match the WhereIs search. WhereIs searches
+through just the text that appears on the MESSAGE INDEX screen.
+This method is often slower than using the select command itself, unless the
+line you are looking for is not too far away in the index.
+<P>
+
+The availability of the aggregate operations commands is determined by the
+<A HREF="h_config_enable_agg_ops">&quot;<!--#echo var="FEAT_enable-aggregate-command-set"-->&quot;</A>
+Feature-List option in your Alpine
+configuration, which defaults to set.
+The features
+<A HREF="h_config_auto_zoom">&quot;<!--#echo var="FEAT_auto-zoom-after-select"-->&quot;</A>
+and
+<A HREF="h_config_select_wo_confirm">&quot;<!--#echo var="FEAT_select-without-confirm"-->&quot;</A>
+affect the behavior of the Select command.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_select_rule =======
+<HTML>
+<HEAD>
+<TITLE>Select: Rule</TITLE>
+</HEAD>
+<BODY>
+<H1>Select: Rule</H1>
+
+You are selecting messages that either match or don't match
+one of the Rules you have defined.
+You may either type the nickname of the Rule at the prompt, or use the
+&quot;^T&nbsp;To&nbsp;List&quot;
+command to select one of your Rules.
+All of the Rules you have defined will be in the list, including
+Rules for Indexcolors, Filtering, Roles, Score setting, and Other.
+They may not all make sense for this purpose, but they are all there for
+flexibility.
+Rules may be added by using the Setup/Rules screen off of the main Alpine
+menu.
+<P>
+Unfortunately, Alpine does not allow all possible Rules to be defined.
+For example, there is no logical OR operation.
+OR is accomplished in the Filter Rules or the other types of Rules by
+simply defining two rules, one that matches the first part of the OR
+and another that matches the second part.
+But you can't do that here, since you only have a single Rule to work with.
+Likewise, the order of Rules is usually important.
+For example, if the first Filter Rule (or Indexcolor rule or ...) matches
+a message, then that stops the search for a further match.
+This means that you may be confused if you try to use Select by Rule to
+check your Filter rules because the order is important when filtering but
+is not considered here.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_select_text =======
+<HTML>
+<HEAD>
+<TITLE>Select: Text</TITLE>
+</HEAD>
+<BODY>
+<H1>Select: Text</H1>
+
+You are selecting messages based on the contents of the message.
+This allows you to select a set of messages based on whether or not the
+message headers or message body contain specified text.
+You may look for text in the Subject, the From header,
+the To header, or the Cc header.
+You may also choose Recipient, which searches for the text in
+either the To or the Cc header;
+or Participant, which means either the To header, or the Cc header,
+or the From header.
+Besides those specific header searches, you may also search the entire
+header and text of the message with &quot;All Text&quot;, or just the
+body of the message with &quot;Body&quot;.
+<P>
+To search for the absence of text, first type the &quot;!&nbsp;Not&quot; command
+before typing the specific type of text search.
+For example, you could type &quot;!&quot; followed by &quot;S&quot; to
+search for all messages that do not contain a particular word in their
+Subjects.
+<P>
+If you choose a Subject search, you may use the subject from the current
+message by typing the &quot;^X Cur Subject&quot; command.
+You may then edit it further if you wish.
+For example, you might select the subject of a reply and edit the
+&quot;Re:&nbsp;&quot; off of the front of it in order to search for
+the original message being replied to.
+All of the other header searches allow you to use addresses from the
+headers of the current message if you want to.
+You may use the &quot;^T Cur To&quot;, &quot;^R Cur From&quot;, or
+&quot;^W Cur Cc&quot;.
+In each case, if there is more than one address, only the first is offered.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_select_status =======
+<HTML>
+<HEAD>
+<TITLE>Select: Status</TITLE>
+</HEAD>
+<BODY>
+<H1>Select: Status</H1>
+
+You are selecting messages based on the status of the message.
+For example, whether or not the message has been marked Deleted or Important,
+or whether or not it has been Answered or is New.
+If you first type the &quot;!&nbsp;Not&quot; command, you will get the
+opposite: not Deleted, not Important, and so on.
+<P>
+If you select Deleted messages, you will get all messages with their
+Deleted flag set.
+Likewise for Important messages, all messages that you have flagged as
+being Important with the
+<A HREF="h_common_flag">Flag</A> command.
+The &quot;New&quot; and &quot;Answered&quot; choices are a little bit odd
+because they try to match what you see on the screen by default.
+&quot;New&quot; is a shorthand for messages that are Unseen, Undeleted,
+and Unanswered.
+If you have looked at the message, or deleted it, or answered it; then it
+is not considered &quot;New &quot;.
+&quot;!&nbsp;New&quot; is the opposite of &quot;New&quot;.
+<P>
+&quot;Answered&quot; is another one that is a little different.
+It means that the message has been Answered <EM>and</EM> is not deleted.
+And to make it even more confusing, &quot;!&nbsp;Answered&quot; is not
+the opposite of &quot;Answered&quot;!
+Instead, &quot;!&nbsp;Answered&quot; stands for messages that are
+both Unanswered <EM>and</EM> not deleted.
+<P>
+(The New and Answered options may seem counter-intuitive.
+The reason it is done this way is
+because, by default, a Deleted message will show up with the &quot;D&quot;
+symbol in the MAIL INDEX screen even if it is New or Answered.
+The Delete symbol overrides the New and Answered symbols, because you
+usually don't care about the message anymore once you've deleted it.
+Similarly, you usually only care about whether a message is Answered or
+not if it is not Deleted.
+Once it is Deleted you've put it out of your mind.)
+<P>
+The other two options were added later because the special nature of the
+New flag was not what was wanted by all users.
+New does match what you see in the index by default, but if you use
+the IMAPSTATUS or SHORTIMAPSTATUS token in the
+&quot;<A HREF="h_config_index_format"><!--#echo var="VAR_index-format"--></A>&quot; it may not
+be exactly what you expect.
+&quot;Unseen&quot; simply selects all unseen messages, whether or not
+they are deleted or answered, and
+&quot;Recent&quot; selects all of the messages that have been added to
+the folder since you started this Alpine session.
+(That's not technically quite true. If there are multiple mail clients
+reading an IMAP mailbox, each message will be marked as Recent in only
+one of the client's sessions.
+That behavior can be convenienent for some purposes, like filtering, but
+it isn't usually what you expect when selecting.)
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_index_cmd_apply =======
+<HTML>
+<HEAD>
+<TITLE>Apply Command</TITLE>
+</HEAD>
+<BODY>
+<H1>Apply Command</H1>
+
+Apply
+(<!--chtml if pinemode="function_key"-->F6<!--chtml else-->A<!--chtml endif-->)
+is the second step of most aggregate operations. Apply
+becomes active any time there is a defined set of selected messages. The
+following commands can be applied to a selected message set: delete,
+undelete, reply, forward,
+<!--chtml if pinemode="os_windows"-->
+<!--chtml else-->
+pipe,
+<!--chtml endif-->
+print, take address, save, export, bounce, and flag.
+<P>
+
+The behavior of some of these commands in an aggregate sense is not easy to
+explain. Try them out to see what they do.
+The feature
+<A HREF="h_config_auto_unzoom">&quot;<!--#echo var="FEAT_auto-unzoom-after-apply"-->&quot;</A>
+affects the behavior of the Apply command, as does the feature
+<A HREF="h_config_auto_unselect">&quot;<!--#echo var="FEAT_auto-unselect-after-apply"-->&quot;</A>.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_index_cmd_zoom =======
+<HTML>
+<HEAD>
+<TITLE>ZoomMode Command</TITLE>
+</HEAD>
+<BODY>
+<H1>ZoomMode Command</H1>
+
+Another action you might want to take on a set of selected messages is to
+zoom in on them. Like apply, zoom only becomes active when messages have
+been selected.
+ZoomMode
+(<!--chtml if pinemode="function_key"-->F3<!--chtml else-->Z<!--chtml endif-->)
+is a toggle command that allows you to
+zoom-in (and only see the selected messages) and zoom-out (to see all
+messages in the folder). Neither apply nor zoom removes the markings that
+define the selected set; you need to use a select command in order
+to do that.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_index_collapse_expand =======
+<HTML>
+<HEAD>
+<TITLE>Collapse/Expand Command</TITLE>
+</HEAD>
+<BODY>
+<H1>Collapse/Expand Command</H1>
+
+The Collapse/Expand command is only available from the MESSAGE INDEX screen when
+the folder is sorted by either Threads or OrderedSubject, and the
+<A HREF="h_config_thread_disp_style"><!--#echo var="VAR_threading-display-style"--></A>
+is set to something other than &quot;none&quot;.
+By default, this command collapses or expands the subthread that starts at
+the currently highlighted message, if any.
+If the subthread is already collapsed, then this command expands it.
+If the subthread is expanded, then this command collapses it.
+If there are no more messages below the current message in the
+thread tree (that is, there are no replies to the current message) then
+this command does nothing.
+
+<P>
+The behavior of this command is affected by the option
+<A HREF="h_config_slash_coll_entire">&quot;<!--#echo var="FEAT_slash-collapses-entire-thread"-->&quot;</A>.
+Normally, this command Collapses or Expands the subthread that
+starts at the currently highlighted message.
+If the above option is set, then this command Collapses or Expands the
+entire current thread instead of just the subthread.
+The current thread is simply the top-level thread that contains the
+current message.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_index_cmd_sort =======
+<HTML>
+<HEAD>
+<TITLE>Sort Command</TITLE>
+</HEAD>
+<BODY>
+<H1>Sort Command</H1>
+
+In Alpine's generic configuration, messages are presented in the order in
+which they arrive. This default can be changed in the SETUP CONFIGURATION
+with the &quot;<A HREF="h_config_sort_key"><!--#echo var="VAR_sort-key"--></A>&quot; option.
+You can also re-sort the folder on demand with the sort
+(<!--chtml if pinemode="function_key"-->F7<!--chtml else-->$<!--chtml endif-->)
+command.
+Your sorting options are:
+<P>
+<UL>
+ <LI> <A HREF="h_index_sort_arrival">A</A>rrival
+ <LI> <A HREF="h_index_sort_date">D</A>ate
+ <LI> <A HREF="h_index_sort_subj">S</A>ubject
+ <LI> <A HREF="h_index_sort_ordsubj">O</A>rderedSubject
+ <LI> t<A HREF="h_index_sort_thread">H</A>read
+ <LI> <A HREF="h_index_sort_from">F</A>rom
+ <LI> si<A HREF="h_index_sort_size">Z</A>e
+ <LI> scor<A HREF="h_index_sort_score">E</A>,
+ <LI> <A HREF="h_index_sort_to">T</A>o
+ <LI> <A HREF="h_index_sort_cc">C</A>c
+</UL>
+
+<P>
+The Reverse option will toggle the order the index is currently
+sorted by, but will not change the relative sort order.
+
+<P>
+Sorting a folder does not actually rearrange the way the folder is saved,
+it just re-arranges how the messages are presented to you. This means
+that Alpine has to do the work of sorting every time you change sort order.
+Sometimes, especially with PC-Alpine or with large folders, this could take
+a while.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_index_sort_default =======
+<HTML>
+<HEAD>
+<TITLE>SORT OPTION: Default</TITLE>
+</HEAD>
+<BODY>
+<H1>SORT OPTION: Default</H1>
+
+The <EM>Default</EM> sort option just means to use the default sort order
+set in the
+<li><a href="h_config_sort_key"><!--#echo var="VAR_sort-key"--></a>
+option in Setup/Config.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_index_sort_arrival =======
+<HTML>
+<HEAD>
+<TITLE>SORT OPTION: Arrival</TITLE>
+</HEAD>
+<BODY>
+<H1>SORT OPTION: Arrival</H1>
+
+The <EM>Arrival</EM> sort option arranges messages in the MESSAGE&nbsp;INDEX
+in the order that they exist in the folder. This is usually the same as the
+order in which they arrived. This option is comparable to not sorting
+the messages at all.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_index_sort_date =======
+<HTML>
+<HEAD>
+<TITLE>SORT OPTION: Date</TITLE>
+</HEAD>
+<BODY>
+<H1>SORT OPTION: Date</H1>
+
+The <EM>Date</EM> sort option arranges messages in the MESSAGE&nbsp;INDEX
+according to the date and time they were
+sent.
+
+<P>
+On a folder like INBOX, sorting by &quot;Date&quot; should be almost
+identical to sorting by &quot;Arrival&quot;.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_index_sort_subj =======
+<HTML>
+<HEAD>
+<TITLE>SORT OPTION: Subject</TITLE>
+</HEAD>
+<BODY>
+<H1>SORT OPTION: Subject</H1>
+
+The <EM>Subject</EM> sort option arranges messages in the MESSAGE&nbsp;INDEX
+by subject.
+
+<P>
+Messages with the same subject are
+first grouped together, and then the groups of like-subject messages
+are arranged alphabetically.
+
+<P>
+Alpine ignores leading &quot;Re:&quot; and
+&quot;re:&quot; and trailing &quot;(fwd)&quot; when determining the
+likeness and alphabetical order of subject lines.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_index_sort_ordsubj =======
+<HTML>
+<HEAD>
+<TITLE>SORT OPTION: OrderedSubject</TITLE>
+</HEAD>
+<BODY>
+<H1>SORT OPTION: OrderedSubject</H1>
+
+The <EM>OrderedSubject</EM> sort option arranges messages in the
+MESSAGE&nbsp;INDEX by grouping all messages with the same subject
+together, similar to sort by <A HREF="h_index_sort_subj">S</A>ubject.
+
+<P>
+However, <EM>OrderedSubj</EM> then arranges the groups of like-subject
+messages by the date of the oldest message in the group.
+
+<P>
+This sort method provides for pseudo threading of conversations within
+a folder.
+You may want to try sorting by Thread instead.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_index_sort_thread =======
+<HTML>
+<HEAD>
+<TITLE>SORT OPTION: Thread</TITLE>
+</HEAD>
+<BODY>
+<H1>SORT OPTION: Thread</H1>
+
+The <EM>Thread</EM> sort option arranges messages in the
+MESSAGE&nbsp;INDEX by grouping all messages that indicate
+they are part of a conversation (discussion thread) taking
+place within a mailbox or newsgroup. This indication is
+based on information in the message's header -- specifically
+its <tt>References:</tt>, <tt>Message-ID:</tt>, and <tt>Subject:</tt> fields.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_index_sort_from =======
+<HTML>
+<HEAD>
+<TITLE>SORT OPTION: From</TITLE>
+</HEAD>
+<BODY>
+<H1>SORT OPTION: From</H1>
+
+The <EM>From</EM> sort option arranges messages in the MESSAGE&nbsp;INDEX
+by the name of the author of the message.
+
+<P>
+Messages with the same author are grouped together. Groups of
+messages are then put into alphabetical order according to message
+author.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_index_sort_size =======
+<HTML>
+<HEAD>
+<TITLE>SORT OPTION: Size</TITLE>
+</HEAD>
+<BODY>
+<H1>SORT OPTION: Size</H1>
+
+The <EM>Size</EM> sort option arranges messages in the MESSAGE&nbsp;INDEX
+by their relative sizes.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_index_sort_score =======
+<HTML>
+<HEAD>
+<TITLE>SORT OPTION: Score</TITLE>
+</HEAD>
+<BODY>
+<H1>SORT OPTION: Score</H1>
+
+The <EM>Score</EM> sort option arranges messages in the MESSAGE&nbsp;INDEX
+by their scores.
+
+<P>
+Messages with the same score are sorted in arrival order.
+Scores are something you create using the
+<A HREF="h_rules_score">&quot;SETUP SCORING&quot;</A> screen.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_index_sort_to =======
+<HTML>
+<HEAD>
+<TITLE>SORT OPTION: To</TITLE>
+</HEAD>
+<BODY>
+<H1>SORT OPTION: To</H1>
+
+The <EM>To</EM> sort option arranges messages in the MESSAGE&nbsp;INDEX
+by the names of the recipients of the message.
+
+<P>
+Messages with the same recipients are grouped together. Groups of
+messages are then put into alphabetical order according to message
+recipients.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_index_sort_cc =======
+<HTML>
+<HEAD>
+<TITLE>SORT OPTION: Cc</TITLE>
+</HEAD>
+<BODY>
+<H1>SORT OPTION: Cc</H1>
+
+The <EM>Cc</EM> sort option arranges messages in the MESSAGE&nbsp;INDEX by
+the names of the carbon copy addresses of the message.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_index_cmd_whereis =======
+<HTML>
+<HEAD>
+<TITLE>WhereIs Command</TITLE>
+</HEAD>
+<BODY>
+<H1>WhereIs Command</H1>
+
+The WhereIs
+(<!--chtml if pinemode="function_key"-->F8<!--chtml else-->W<!--chtml endif-->)
+command lets you search the MESSAGE INDEX for a word.
+It scans through whatever you see, usually the name of the author
+and the Subject line.
+WhereIs has special subcommands to let you find the beginning of the
+index (Ctrl-Y -- first message)
+or the end of the index (Ctrl-V -- last message).
+<P>
+Note that WhereIs only searches through the visible text on the screen.
+For example, if only part of the Subject of a message is shown because it
+is long, then only the visible portion of the Subject is searched.
+Also note that WhereIs does not &quot;see&quot; the
+&quot;X&quot; in column one of Index entries for selected messages
+so it can't be used to search for
+selected messages (use &quot;Zoom&quot; instead).
+<P>
+If the feature
+<A HREF="h_config_enable_agg_ops">&quot;<!--#echo var="FEAT_enable-aggregate-command-set"-->&quot;</A>
+is turned on,
+WhereIs can also be used as a quick way to select messages that match the
+string being searched for.
+Instead of typing carriage return to search for the next match, type
+Ctrl-X to select all matches.
+Once again, this only selects matches that are (or would be if the right
+index line was on the screen) visible.
+Truncated From lines or Subjects will cause matches to be missed.
+Although WhereIs is sometimes convenient for quick matching, the Select
+command is usually more powerful and usually faster.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_view_cmd_whereis =======
+<HTML>
+<HEAD>
+<TITLE>WhereIs Command</TITLE>
+</HEAD>
+<BODY>
+<H1>WhereIs Command</H1>
+
+The WhereIs
+(<!--chtml if pinemode="function_key"-->F8<!--chtml else-->W<!--chtml endif-->)
+command does a &quot;find in current message&quot; operation. You
+type in text and Alpine will try to find it in the message you are
+reading. WhereIs also has subcommands to jump to the beginning (Ctrl-Y)
+or end (Ctrl-V) of the message.
+That is, to rapidly move to the end of a message, hit the
+<!--chtml if pinemode="function_key"-->F8<!--chtml else-->W<!--chtml endif-->
+(or Ctrl-W) key followed
+by Ctrl-V. Similarly,
+<!--chtml if pinemode="function_key"-->F8<!--chtml else-->W<!--chtml endif-->
+followed by Ctrl-Y will take you to the beginning of a message.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_view_cmd_hilite =======
+<HTML>
+<HEAD>
+<TITLE>View Hilite and Next item/Previous item</TITLE>
+</HEAD>
+<BODY>
+<H1>View Hilite and Next item/Previous item</H1>
+
+Sometimes messages may be in the form of formatted HTML text
+or they may contain URLs or Web server hostnames.
+When any of the features
+<A HREF="h_config_enable_view_url">&quot;<!--#echo var="FEAT_enable-msg-view-urls"-->&quot;</A>,
+<A HREF="h_config_enable_view_web_host">&quot;<!--#echo var="FEAT_enable-msg-view-web-hostnames"-->&quot;</A>,
+<A HREF="h_config_enable_view_attach">&quot;<!--#echo var="FEAT_enable-msg-view-attachments"-->&quot;</A>,
+or
+<A HREF="h_config_enable_view_addresses">&quot;<!--#echo var="FEAT_enable-msg-view-addresses"-->&quot;</A>
+are enabled, Alpine will represent such selectable items in the text
+in bold typeface. One of the selectable items will be displayed in
+inverse video (highlighted). This is the &quot;currently selected&quot; item.
+Press the Return key to view the currently selected item.
+<P>
+
+The Up and Down Arrows keys can be used to change the selected item
+(also see the feature
+<A HREF="h_config_enable_view_arrows">&quot;<!--#echo var="FEAT_enable-msg-view-forced-arrows"-->&quot;</A>).
+If there are no selectable items in the direction of the arrow you
+pressed, Alpine will scroll the display in that direction until one
+becomes visible. To &quot;jump&quot; forwards/backwards among selectable
+items in the message text, use the Previous and Next item commands,
+<!--chtml if pinemode="function_key"-->F5 and F6
+<!--chtml else-->^B and ^F<!--chtml endif-->.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_view_cmd_viewattch =======
+<HTML>
+<HEAD>
+<TITLE>ViewAttch Command</TITLE>
+</HEAD>
+<BODY>
+<H1>ViewAttch Command</H1>
+
+
+The View/Save Attachment
+(<!--chtml if pinemode="function_key"-->F4<!--chtml else-->V<!--chtml endif-->)
+command allows you to handle MIME attachments to a message you have
+received. Alpine shows you a list of the message attachments -- you just
+choose the attachment you want. You may either view or save the
+selected attachment.
+
+<P>
+Because many attachments require external programs for display, there
+is some system configuration that has to happen before you can
+actually display attachments. Hopefully much of that will have been
+done already by your system administrator. MIME configuration is
+handled with the &quot;mailcap&quot; configuration file. (See the section
+on configuration in the
+<A HREF="h_news">release notes</A> for more information.)
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_index_cmd_expunge =======
+<HTML>
+<HEAD>
+<TITLE>Expunge/Exclude Command</TITLE>
+</HEAD>
+<BODY>
+<H1>Expunge/Exclude Command</H1>
+
+Expunge/Exclude
+(<!--chtml if pinemode="function_key"-->F3<!--chtml else-->X<!--chtml endif-->)
+is the command Alpine uses to actually remove all messages
+marked for deletion. With regular email files, expunge literally deletes
+the text from the current folder. With newsgroups or shared mailboxes,
+you don't have permission to actually remove the message, so it is an
+exclude -- Alpine removes the message from your view of the folder even
+though it is not technically gone.
+<P>
+
+The configuration features
+<A HREF="h_config_auto_expunge">&quot;<!--#echo var="FEAT_expunge-without-confirm"-->&quot;</A>
+and
+<A HREF="h_config_full_auto_expunge">&quot;<!--#echo var="FEAT_expunge-without-confirm-everywhere"-->&quot;</A>
+affect the behavior of the Expunge command.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_common_compose =======
+<HTML>
+<HEAD>
+<TITLE>Compose Command</TITLE>
+</HEAD>
+<BODY>
+<H1>Compose Command</H1>
+
+The Compose command takes you into the Alpine message composer where you
+can start a new message for sending. This is where you type in the
+message's text and specify its recipient list (the &quot;To:&quot;
+address), where copies should be directed (e.g., &quot;Fcc&quot;,
+&quot;Cc:&quot; or &quot;Bcc:&quot;), and which files, if any, should
+be attached to the message.
+
+<P>
+When you type this command, Alpine will also automatically check for any
+interrupted (i.e., a message that was being composed when your modem
+or network connection was broken) or previously postponed messages and
+offer you a chance to continue working on those.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_common_index =======
+<HTML>
+<HEAD>
+<TITLE>Message Index Command</TITLE>
+</HEAD>
+<BODY>
+<H1>Message Index Command</H1>
+
+The Index command takes you to the MESSAGE INDEX screen that displays a
+summary caption for each message in the currently-open folder. One
+message will be highlighted; this is the &quot;Current&quot; message.
+The message commands available from this screen (e.g. View, Reply,
+Forward, Delete, Print, Save, etc) apply to the current message.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_common_folders =======
+<HTML>
+<HEAD>
+<TITLE>Folder List Command</TITLE>
+</HEAD>
+<BODY>
+<H1>Folder List Command</H1>
+
+This Folder List command takes you to the FOLDER LIST screen that displays
+the names of all your message folders and allows you to view, rename,
+delete, and add folders. You can open (view) a different folder than the
+one currently open by highlighting the desired one (using the arrow keys
+or their control-key equivalents) and pressing RETURN.
+
+<P>
+If you have multiple folder collections defined (see the Help text for
+the FOLDER LIST screen to learn more about Collections), you may need
+to press Return to expand the collection and display all of the
+folders in it.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_main_addrbook =======
+<HTML>
+<HEAD>
+<TITLE>Address Book Command</TITLE>
+</HEAD>
+<BODY>
+<H1>Address Book Command</H1>
+
+This command, available only from the MAIN MENU, takes you
+to the ADDRESS BOOK management screen. From here, your personal address
+book(s) may be updated.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_main_setup =======
+<HTML>
+<HEAD>
+<TITLE>Setup Command</TITLE>
+</HEAD>
+<BODY>
+<H1>Setup Command</H1>
+
+The Setup command, available only from the MAIN MENU, prompts you for
+one of several configuration screens, including the SETUP CONFIGURATION
+screen, by which you may activate optional Alpine features.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_main_release_notes =======
+<HTML>
+<HEAD>
+<TITLE>Release Notes Command</TITLE>
+</HEAD>
+<BODY>
+<H1>Release Notes Command</H1>
+
+This command displays information about Alpine <!--#echo var="ALPINE_VERSION"-->,
+as well as pointers to further information such as history and legal notes.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_main_kblock =======
+<HTML>
+<HEAD>
+<TITLE>Keyboard Lock Command</TITLE>
+</HEAD>
+<BODY>
+<H1>Keyboard Lock Command</H1>
+
+This command allows your Alpine session to be protected
+during a temporary absence from your terminal.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_main_journal =======
+<HTML>
+<HEAD>
+<TITLE>Journal Command</TITLE>
+</HEAD>
+<BODY>
+<H1>Journal Command</H1>
+
+This command displays a list of all the status messages Alpine has
+displayed (on the third line from the bottom of the screen). This may
+be useful if a message disappeared before you had a chance to read it.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_common_role =======
+<HTML>
+<HEAD>
+<TITLE>Role Command</TITLE>
+</HEAD>
+<BODY>
+<H1>Role Command</H1>
+
+The Role command is similar to the Compose command except that it starts
+off by letting you select a <A HREF="h_rules_roles">role</A>
+to be used for the composition.
+You may set up alternate roles by using Setup/Rules/Roles.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_common_conditional_cmds =======
+<HTML>
+<HEAD>
+<TITLE>Conditional Commands</TITLE>
+</HEAD>
+<BODY>
+<H1>Conditional Commands</H1>
+
+The presence or absence of certain commands, particularly in the
+MESSAGE INDEX and MESSAGE TEXT screens, is determined by
+whether or not specific features are set in your Alpine configuration.
+(You can access the SETUP CONFIGURATION screen, where they are found, from
+Alpine's MAIN MENU.) To see if a desired command's availability is
+conditioned on a feature setting, see the command's help text (highlight
+the phrase associated with the command and hit Return).
+
+<P>
+Also note that some
+commands may be administratively disabled by your system manager;
+if they don't work, please check with your local help desk.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_common_pipe =======
+<HTML>
+<HEAD>
+<TITLE>Pipe Command</TITLE>
+</HEAD>
+<BODY>
+<H1>Pipe Command</H1>
+
+Pipe
+(<!--chtml if pinemode="function_key"-->F12<!--chtml else-->|<!--chtml endif-->)
+allows you to send a message to a specified Unix command for external
+processing.
+This command's availability is controlled by the
+<A HREF="h_config_enable_pipe">&quot;<!--#echo var="FEAT_enable-unix-pipe-cmd"-->&quot;</A>
+feature.
+By default, the processed text of the message is sent to the command
+you specify and the output is captured by Alpine and shown to you.
+When you run the pipe command, there are some sub-commands which may be
+used to alter this behavior.
+These sub-commands are described <A HREF="h_pipe_command">here</A>.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_common_goto =======
+<HTML>
+<HEAD>
+<TITLE>Goto Command</TITLE>
+</HEAD>
+<BODY>
+<H1>Goto Command</H1>
+
+Goto
+(<!--chtml if pinemode="function_key"-->F6<!--chtml else-->G<!--chtml endif-->)
+is the command that lets you bypass Alpine's folder selection screens
+and jump directly to a new folder. You can select any folder in the
+world: one in your current collection, one in a different collection or
+one in a collection you've never even used before.
+<P>
+
+Alpine will help you as much as possible to narrow in on the folder you want.
+However, if the folder is outside of your defined collections, you are
+going to have to enter the exact folder location using the correct
+<A HREF="h_valid_folder_names">syntax</A>
+for a remote folder and/or fully-qualified path name.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_common_nextnew =======
+<HTML>
+<HEAD>
+<TITLE>NextNew Command</TITLE>
+</HEAD>
+<BODY>
+<H1>NextNew Command</H1>
+
+When you press the TAB key, Alpine advances to the next
+&quot;interesting&quot; message.
+This will be the next message you have not seen before, or the next message
+you have flagged Important, whichever comes first.
+Unread messages that have been deleted are not considered interesting.
+(A note about reading news. Alpine expects you to &quot;Delete&quot; news
+articles after you have read them if you want to remove them from future
+consideration. See <A HREF="h_mainhelp_readingnews">Reading News</A> for
+more information.)
+
+<P>
+The NextNew command is affected by the feature
+<A HREF="h_config_tab_new_only">&quot;<!--#echo var="FEAT_tab-visits-next-new-message-only"-->&quot;</A>,
+which causes Alpine to only consider Unread messages interesting, not messages
+flagged Important.
+
+<P>
+This command behaves a little differently when it finds there are no more
+interesting messages left in the current folder.
+If the current folder is one of your Incoming Message Folders
+(<A HREF="h_config_enable_incoming">&quot;<!--#echo var="FEAT_enable-incoming-folders"-->&quot;</A>)
+or it is a newsgroup, then Alpine will try to find the next folder or
+newsgroup that contains <EM>Recent</EM> messages and will ask you
+if you want to open that folder.
+This behavior may be modified by using the
+<A HREF="h_config_tab_uses_unseen">&quot;<!--#echo var="FEAT_tab-uses-unseen-for-next-folder"-->&quot;</A>
+feature that causes Alpine to look for Unseen messages instead of Recent
+messages.
+The NextNew command's behavior is also affected by the configuration features
+<A HREF="h_config_auto_open_unread">&quot;<!--#echo var="FEAT_auto-open-next-unread"-->&quot;</A>,
+and
+<A HREF="h_config_tab_no_prompt">&quot;<!--#echo var="FEAT_continue-tab-without-confirm"-->&quot;</A>.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_common_jump =======
+<HTML>
+<HEAD>
+<TITLE>Jump Command</TITLE>
+</HEAD>
+<BODY>
+<H1>Jump Command</H1>
+
+This is Alpine's way of allowing you to go straight to a specific message.
+Just press &quot;J&quot; and then enter the message number. By default, Alpine is also
+configured such that typing in any number automatically jumps you to that
+message
+(<A HREF="h_config_enable_jump">&quot;<!--#echo var="FEAT_enable-jump-shortcut"-->&quot;</A>
+in the SETUP CONFIGURATION).
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_common_flag =======
+<HTML>
+<HEAD>
+<TITLE>Flag Command</TITLE>
+</HEAD>
+<BODY>
+<H1>Flag Command</H1>
+
+Flag
+(<!--chtml if pinemode="function_key"-->F11<!--chtml else-->*<!--chtml endif-->)
+is the command that allows users to manipulate the status flags that
+appear on the left side of the MESSAGE INDEX screen. The most common
+use of this is to mark a message as important. This is something of a
+note to yourself to get back to that message. You may also use the
+flag command to set (or unset) the flags that indicate that a message
+is new, answered, deleted, or forwarded.<P>
+
+Provided the mail server supports it,
+you may also manipulate user-defined keywords
+for a message using the flag command.
+These keywords will be available if you use the Flag Details screen that you
+can get to after typing the
+Flag (<!--chtml if pinemode="function_key"-->F11<!--chtml else-->*<!--chtml endif-->)
+command.
+They will be listed after the Important, New, Answered, Deleted , and Forwarded flags,
+which are always present.
+You may add new keywords by using the Add KW command from the Flag Details screen
+or by defining them in the <A HREF="h_config_keywords"><!--#echo var="VAR_keywords"--></A> option in the Setup/Config screen.
+
+The availability of the flag command is determined by the
+<A HREF="h_config_enable_flag">&quot;<!--#echo var="FEAT_enable-flag-cmd"-->&quot;</A>
+feature in your Alpine configuration. Also, it is possible that Flag could be
+administratively disabled by your system manager; if it doesn't work,
+please check with your local help desk before reporting a bug.
+The behavior of the flag command may be modified by the
+<A HREF="h_config_flag_screen_default">&quot;<!--#echo var="FEAT_enable-flag-screen-implicitly"-->&quot;</A> option or the
+<A HREF="h_config_flag_screen_kw_shortcut">&quot;<!--#echo var="FEAT_enable-flag-screen-keyword-shortcut"-->&quot;</A> option.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_common_hdrmode =======
+<HTML>
+<HEAD>
+<TITLE>HdrMode Command</TITLE>
+</HEAD>
+<BODY>
+<H1>HdrMode Command</H1>
+
+Every email message comes with some header lines that you normally
+don't see (and don't want to see).
+These include anywhere from 3-20 lines (or more) added by the
+Internet mail transport system to record the route your message took,
+for diagnostic purposes.
+These are normally of no import and simply
+add clutter, so Alpine suppresses them in the MESSAGE TEXT display.
+This also includes other non-standard headers the message may contain.
+If you want to see these headers, there is a way to reveal them.
+<P>
+
+The Header Mode
+(<!--chtml if pinemode="function_key"-->F9<!--chtml else-->H<!--chtml endif-->)
+command is a toggle that controls Alpine's handling of these header
+lines. Normally, full headers is &quot;off&quot; and you only see a
+few lines about who a message is to and who it is from. When you
+press
+(<!--chtml if pinemode="function_key"-->F9<!--chtml else-->H<!--chtml endif-->)
+to turn full headers on, Alpine will show you
+the normal header lines as well as delivery headers, comment headers,
+MIME headers, and any other headers present.
+<P>
+
+Several different Alpine commands honor the header mode -- it affects how
+messages are displayed, how they appear in forward and reply email, how
+they are printed, how they are saved, and how they are exported.
+<!--chtml if pinemode="os_windows"-->
+<!--chtml else-->
+The pipe command is also affected.
+<!--chtml endif-->
+<P>
+
+The presence or absence of the Header Mode command is determined by the
+<A HREF="h_config_enable_full_hdr">&quot;<!--#echo var="FEAT_enable-full-header-cmd"-->&quot;</A>
+Feature-List option in your Alpine configuration.
+
+<P>
+If you have also turned on the
+<A HREF="h_config_quote_suppression">&quot;Quote Suppression&quot;</A>
+option then the HdrMode command actually rotates through three states
+instead of just two.
+The first is the normal view with long quotes suppressed.
+The second is the normal view but with the long quotes included.
+The last enables the display of all headers in the message.
+When using Export, Pipe, Print, Forward, or Reply the quotes are
+never suppressed, so the first two states are identical.
+
+<P>
+The behavior of the Header Mode command may be altered slightly by
+turning on the
+<A HREF="h_config_quell_full_hdr_reset">&quot;<!--#echo var="FEAT_quell-full-header-auto-reset"-->&quot;</A>
+Feature-List option in your Alpine configuration.
+In particular, it will cause the Header Mode to be persistent when moving
+from message to message instead of resetting to the default for each message.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_common_print =======
+<HTML>
+<HEAD>
+<TITLE>Print Command</TITLE>
+</HEAD>
+<BODY>
+<H1>Print Command</H1>
+
+The Print
+(<!--chtml if pinemode="function_key"-->F9<!--chtml else-->%<!--chtml endif-->)
+command allows you to print a copy of a message.
+There are many SETUP CONFIGURATION features that affect the
+Print command, including
+<A HREF="h_config_enable_y_print">&quot;<!--#echo var="FEAT_enable-print-via-y-command"-->&quot;</A>,
+<A HREF="h_config_print_index">&quot;<!--#echo var="FEAT_print-index-enabled"-->&quot;</A>,
+<A HREF="h_config_custom_print">&quot;<!--#echo var="FEAT_print-offers-custom-cmd-prompt"-->&quot;</A>,
+<A HREF="h_config_print_from">&quot;<!--#echo var="FEAT_print-includes-from-line"-->&quot;</A>, and
+<A HREF="h_config_ff_between_msgs">&quot;<!--#echo var="FEAT_print-formfeed-between-messages"-->&quot;</A>.
+You set up for printing by using the Printer option of the Setup command
+on the MAIN MENU.
+<P>
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_common_take =======
+<HTML>
+<HEAD>
+<TITLE>TakeAddr Command</TITLE>
+</HEAD>
+<BODY>
+<H1>TakeAddr Command</H1>
+
+
+With the Take Address
+(<!--chtml if pinemode="function_key"-->F10<!--chtml else-->T<!--chtml endif-->)
+command, you can extract email addresses from an
+incoming message and save them in an address book. This is an easy way
+to add to your address book and avoid having to remember the email
+addresses of the people who write to you.
+<P>
+
+If the message is just to you individually, then you will only need to
+provide a nickname. If the message contains more than one email address,
+then you will see an address
+selection screen that lets you choose the address you want to save into
+your address book, or lets you choose several of them add to a
+personal distribution list.
+<P>
+
+Once you've added an entry to your address book, you can use it from the
+message composer by typing the nickname of the entry into one of the
+header fields (for example, into the To: field), or you can use ^T from
+the header field to select the entry from your address book.
+<P>
+
+If the configuration feature
+<A HREF="h_config_enable_role_take">&quot;<!--#echo var="FEAT_enable-rules-under-take"-->&quot;</A>
+is set, the behavior of the Take command is altered slightly.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_ge_import =======
+<HTML>
+<HEAD>
+<TITLE>Import File Selection</TITLE>
+</HEAD>
+<BODY>
+<H1>Import File Selection</H1>
+
+You are importing a file that you previously
+exported from Alpine.
+You are now being asked for the name of that file.
+The easiest way to select a file is probably with the &quot;^T&quot;
+&quot;To Files&quot; command.
+Alternatively, you may type in a file name.
+It may be an absolute pathname.
+Otherwise, it is a file located in your home directory
+or current working directory
+<!--chtml if pinemode="running"-->
+(which, at least for your current Alpine &quot;session,&quot;
+is &quot;<!--#echo var="CURRENT_DIR"-->&quot;)
+<!--chtml endif-->, depending on the
+<A HREF="h_config_use_current_dir">&quot;<!--#echo var="FEAT_use-current-dir"-->&quot;</A> option.
+In any case, you finish by typing a carriage return to accept the
+file name that is displayed.
+When the feature
+<A HREF="h_config_enable_tab_complete">&quot;<!--#echo var="FEAT_enable-tab-completion"-->&quot;</A>
+is turned on you may use TAB to complete partially typed in names.
+<P>
+You may cancel the import operation by typing &quot;^C&quot; after exiting
+this help.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_ge_allparts =======
+<HTML>
+<HEAD>
+<TITLE>Export Message File Selection</TITLE>
+</HEAD>
+<BODY>
+<H1>Export Message File Selection</H1>
+
+You are Exporting a message from an Alpine mail folder
+to a plain text file.
+You also have the option of exporting all of the attachments associated
+with the message.
+You are now being asked for the name of the file to export <EM>to</EM>.
+The easiest way to select a file is probably with the &quot;^T&quot;
+&quot;To Files&quot; subcommand.
+After returning from that subcommand you will still be allowed to
+edit the name you have selected.
+Alternatively, you may type in a file name.
+It may be an absolute pathname.
+Otherwise, it is a file located in your home directory
+or current working directory
+<!--chtml if pinemode="running"-->
+(which, at least for your current Alpine &quot;session,&quot;
+is &quot;<!--#echo var="CURRENT_DIR"-->&quot;)
+<!--chtml endif-->, depending on the
+<A HREF="h_config_use_current_dir">&quot;<!--#echo var="FEAT_use-current-dir"-->&quot;</A> option.
+In any case, you finish by typing a carriage return to accept the
+file name that is displayed.
+When the feature
+<A HREF="h_config_enable_tab_complete">&quot;<!--#echo var="FEAT_enable-tab-completion"-->&quot;</A>
+is turned on you may use TAB to complete partially typed in names.
+<P>
+The message you are exporting appears to have some attachments.
+If you wish to save <EM>all</EM> of the attachments at once,
+type the &quot;^P&quot; &quot;AllParts&quot; command to turn on
+saving of the attachments.
+You may turn it back off by typing &quot;^P&quot; again, which will now
+be labeled &quot;NoAllParts&quot; instead.
+If you want to save the parts the command displayed should be
+&quot;NoAllParts&quot;!
+When you choose to save attachments like this, the attachments will be saved
+in a newly created directory.
+That directory will have the same name as the file name you choose here,
+with the letters &quot;.d&quot; appended.
+If that directory already exists, then the letters &quot;.d_1&quot; will
+be tried, then &quot;.d_2&quot; and so on until a name that doesn't exist
+is found.
+For example, if you select the file name
+<P>
+<CENTER><SAMP>filename</SAMP></CENTER>
+<P>
+to export the message to, then the directory used for the attachments will be
+<P>
+<CENTER><SAMP>filename.d</SAMP></CENTER>
+<P>
+or perhaps
+<P>
+<CENTER><SAMP>filename.d_&lt;n&gt;</SAMP></CENTER>
+<P>
+The attachments will then be put into files inside that directory.
+The names for the attachment files will be derived from the attachments
+if possible.
+This is done in the same way as the default values are derived if you
+save them one at a time.
+(The &quot;filename&quot; parameter from the Content-Disposition header
+is the first choice. If that doesn't exist, the &quot;name&quot;
+parameter from the Content-Type header is used.)
+If a name for a particular attachment is not available, then the
+part number of the attachment is used, with the characters &quot;part_&quot;
+prepended.
+An example of that would be
+<P>
+<CENTER><SAMP>part_2.1</SAMP></CENTER>
+<P>
+If you want to save only some of the attachments or if you want more control
+over the directory and filename where an attachment is saved you may
+cancel out of this command and View the attachment list.
+From there you can save each attachment individually.
+<P>
+You may cancel the Export operation by typing &quot;^C&quot; after exiting
+this help.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_ge_export =======
+<HTML>
+<HEAD>
+<TITLE>Export File Selection</TITLE>
+</HEAD>
+<BODY>
+<H1>Export File Selection</H1>
+
+You are Exporting or Saving something from within the Alpine world
+(a message, an attachment, etc.)
+to a plain text file.
+You are now being asked for the name of the file to export <EM>to</EM>.
+The easiest way to select a file is probably with the &quot;^T&quot;
+&quot;To Files&quot; subcommand.
+After returning from that subcommand you will still be allowed to
+edit the name you have selected.
+Alternatively, you may type in a file name.
+It may be an absolute pathname.
+Otherwise, it is a file located in your home directory
+or current working directory
+<!--chtml if pinemode="running"-->
+(which, at least for your current Alpine &quot;session,&quot;
+is &quot;<!--#echo var="CURRENT_DIR"-->&quot;)
+<!--chtml endif-->, depending on the
+<A HREF="h_config_use_current_dir">&quot;<!--#echo var="FEAT_use-current-dir"-->&quot;</A> option.
+In any case, you finish by typing a carriage return to accept the
+file name that is displayed.
+When the feature
+<A HREF="h_config_enable_tab_complete">&quot;<!--#echo var="FEAT_enable-tab-completion"-->&quot;</A>
+is turned on you may use TAB to complete partially typed in names.
+<P>
+If the object you are exporting is a message with some attachments,
+you may wish to save all of the attachments by typing the &quot;^P&quot;
+&quot;AllParts&quot; command to turn on saving of the attachments.
+This subcommand will only be visible if the message actually has attachments.
+You may also View the attachment list and save individual attachments from
+there.
+<P>
+You may cancel the Export operation by typing &quot;^C&quot; after exiting
+this help.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_common_save =======
+<HTML>
+<HEAD>
+<TITLE>Save and Export Commands</TITLE>
+</HEAD>
+<BODY>
+<H1>Save and Export Commands</H1>
+
+Save
+(<!--chtml if pinemode="function_key"-->F11<!--chtml else-->S<!--chtml endif-->)
+and Export
+(<!--chtml if pinemode="function_key"-->F12<!--chtml else-->E<!--chtml endif-->)
+are the two alternatives Alpine gives you to keep a copy of the message
+you are reading. If you want to keep the message within Alpine's email
+world, use &quot;Save&quot;; if you want to use the message in another
+program, use &quot;Export&quot;.
+<P>
+
+When you Save a message, it is put into an existing folder or into a new
+folder in one of your existing folder collections. The message stays in
+email format and can be read by Alpine again. Alpine may use a special format
+for its mail folders -- never edit an Alpine folder by hand or with any
+program other than Alpine. The exact behavior of the Save command can be
+configured with the
+<A HREF="h_config_quote_all_froms">&quot;<!--#echo var="FEAT_save-will-quote-leading-froms"-->&quot;</A>,
+<A HREF="h_config_save_wont_delete">&quot;<!--#echo var="FEAT_save-will-not-delete"-->&quot;</A>,
+and
+<A HREF="h_config_save_advances">&quot;<!--#echo var="FEAT_save-will-advance"-->&quot;</A>
+feature list settings.
+The name of the folder offered as a default is controlled by the option
+<A HREF="h_config_saved_msg_name_rule">&quot;<!--#echo var="VAR_saved-msg-name-rule"-->&quot;</A>.
+<P>
+
+When you use Export, the message is placed in a plain text file in your
+home directory
+<!--chtml if pinemode="running"-->
+(which, in the present configuration of your system, is
+ &quot;<!--#echo var="HOME_DIR"-->&quot;)
+<!--chtml endif-->
+or current working directory
+<!--chtml if pinemode="running"-->
+(which, at least for your current Alpine &quot;session,&quot;
+is &quot;<!--#echo var="CURRENT_DIR"-->&quot;)
+<!--chtml endif-->, depending on the
+<A HREF="h_config_use_current_dir">&quot;<!--#echo var="FEAT_use-current-dir"-->&quot;</A>
+configuration setting. In the normal case, only minimal
+headers are exported with the message; however, if the full header mode
+(whose availability may be disabled by setting the feature
+<A HREF="h_config_enable_full_hdr">&quot;<!--#echo var="FEAT_enable-full-header-cmd"-->&quot;</A>
+in SETUP CONFIGURATION) is
+toggled on, then complete headers are exported along with the message
+text. (If you have any <A HREF="h_config_display_filters"><!--#echo var="VAR_display-filters"--></A>
+defined, they may affect the contents of the exported file.)
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_common_bounce =======
+<HTML>
+<HEAD>
+<TITLE>Bounce Command</TITLE>
+</HEAD>
+<BODY>
+<H1>Bounce Command</H1>
+
+The Bounce
+(<!--chtml if pinemode="function_key"-->F10<!--chtml else-->B<!--chtml endif-->)
+command allows you to re-send, or &quot;remail&quot;, a
+message, as if you were never in the loop. It is analogous to crossing
+out your address on a postal letter, writing a different address on the
+envelope, and putting it into the mailbox. Bounce is used primarily to
+redirect email that was sent to you in error.
+Also, some owners of email
+lists need the bounce command to handle list traffic.
+Bounce is not anonymous.
+A ReSent-From header is added to the message so that the recipient may
+tell that you Bounced it to them.
+<P>
+
+The presence or absence of the Bounce command is determined by the
+<A HREF="h_config_enable_bounce">&quot;<!--#echo var="FEAT_enable-bounce-cmd"-->&quot;</A>
+feature in your Alpine configuration.
+The feature
+<A HREF="h_config_fcc_on_bounce">&quot;<!--#echo var="FEAT_fcc-on-bounce"-->&quot;</A>
+affects the behavior of the Bounce command.
+Also, it is possible that Bounce could be
+administratively disabled by your system manager; if it doesn't work,
+please check with your local help desk before reporting a bug.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_common_reply =======
+<HTML>
+<HEAD>
+<TITLE>Reply and Forward Commands</TITLE>
+</HEAD>
+<BODY>
+
+<H1>Reply and Forward Commands</H1>
+
+Replying
+(<!--chtml if pinemode="function_key"-->F11<!--chtml else-->R<!--chtml endif-->)
+and Forwarding
+(<!--chtml if pinemode="function_key"-->F12<!--chtml else-->F<!--chtml endif-->)
+are your two alternatives for following up on the
+message you are reading. You would use reply if you want to get email
+back to the author of the message and/or the other people who have
+already seen it. You use forward if you want somebody new to see the
+message.
+<P>
+
+In the normal case, the only thing that you must supply when forwarding a
+message is the name/email address of the new recipient.
+Alpine will include the text of the forwarded message.
+Alpine will also include any attachments to the message.
+There is space above the forwarded text for you to include additional comments.
+<P>
+
+When replying, you usually have to answer some questions.
+If the message is to multiple people and/or specified with a Reply-To: header,
+then you will have to decide who should get the reply.
+You also need to decide whether or not to include the previous
+message in your reply.
+Some of this is configurable.
+Specifically, see the
+<A HREF="h_config_include_header">&quot;<!--#echo var="FEAT_include-header-in-reply"-->&quot;</A>,
+<A HREF="h_config_auto_include_reply">&quot;<!--#echo var="FEAT_include-text-in-reply"-->&quot;</A>,
+<A HREF="h_config_attach_in_reply">&quot;<!--#echo var="FEAT_include-attachments-in-reply"-->&quot;</A>,
+and
+<A HREF="h_config_auto_reply_to">&quot;<!--#echo var="FEAT_reply-always-uses-reply-to"-->&quot;</A>
+configuration features.
+<P>
+
+Both the Reply and Forward commands react to the full header mode toggle.
+If the full header mode is on, then all the header and delivery lines are
+included with the text of the message in your reply/forward.
+<P>
+
+Other configuration features that affect the Reply command are
+<A HREF="h_config_sig_at_bottom">&quot;<!--#echo var="FEAT_signature-at-bottom"-->&quot;</A>,
+<A HREF="h_config_sigdashes">&quot;<!--#echo var="FEAT_enable-sigdashes"-->&quot;</A>, and
+<A HREF="h_config_strip_sigdashes">&quot;<!--#echo var="FEAT_strip-from-sigdashes-on-reply"-->&quot;</A>.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_common_delete =======
+<HTML>
+<HEAD>
+<TITLE>Delete and Undelete Commands</TITLE>
+</HEAD>
+<BODY>
+<H1>Delete and Undelete Commands</H1>
+
+Delete
+(<!--chtml if pinemode="function_key"-->F9<!--chtml else-->D<!--chtml endif-->)
+and Undelete
+(<!--chtml if pinemode="function_key"-->F10<!--chtml else-->U<!--chtml endif-->)
+allow you to change the Deleted flag for the current message.
+Delete marks a message Deleted (turns on the Deleted flag) and Undelete
+removes the mark.
+In the MESSAGE INDEX, deleted messages have a &quot;D&quot; in the status field
+at the left hand edge of the index line.
+When viewing a deleted message, the letters &quot;DEL&quot; will be present
+in the upper right hand corner of the screen.
+Delete simply <EM>marks</EM> a message Deleted, it does not actually
+get rid of the message.
+The eXpunge command (available from the MESSAGE INDEX screen) actually
+removes all of the deleted messages in a folder.
+Once a message is eXpunged, it can't be retrieved.
+<P>
+
+The Delete command is affected by the setting of the configuration feature
+<A HREF="h_config_del_skips_del">&quot;<!--#echo var="FEAT_delete-skips-deleted"-->&quot;</A>.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_common_postpone =======
+<HTML>
+<HEAD>
+<TITLE>Postpone Command</TITLE>
+</HEAD>
+<BODY>
+<H1>Postpone Command</H1>
+
+The postpone
+<!--chtml if pinemode="function_key"-->(F11)<!--chtml else-->(^O)<!--chtml endif-->
+command allows you to temporarily stop working on the current
+message so you may read
+other messages or compose another message. When you want to resume a
+message later, start to compose and answer &quot;yes&quot; to the
+&quot;Continue postponed composition?&quot; question. You may
+postpone as many messages as you like.
+
+<P>
+Note: If a <A HREF="h_config_form_folder"><!--#echo var="VAR_form-letter-folder"--></A> is defined
+in the Setup/Config screen, then the Postpone command will prompt you for
+the folder in which to store your outgoing message.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_compose_cancel =======
+<HTML>
+<HEAD>
+<TITLE>Cancel Command</TITLE>
+</HEAD>
+<BODY>
+<H1>Cancel Command</H1>
+
+Cancel
+<!--chtml if pinemode="function_key"-->
+(F2)
+<!--chtml else-->
+(^C)
+<!--chtml endif-->
+
+The Cancel command returns you to Alpine's normal mail processing and
+causes the message currently under composition to be thrown out.
+The message text <EM>will be lost</EM>.
+
+<P>
+Note: Unless the <A HREF="h_config_quell_dead_letter"><!--#echo var="FEAT_quell-dead-letter-on-cancel"--></A> has been set, the text of the most recent composition cancelled
+will be preserved in the file named
+<!--chtml if pinemode="os_windows"-->
+&quot;DEADLETR&quot;.
+<!--chtml else-->
+&quot;dead.letter&quot; in your home directory.
+<!--chtml endif-->
+If you unintentionally cancel a message, look there for its text.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_compose_addrcomplete =======
+<HTML>
+<HEAD>
+<TITLE>Address Completion</TITLE>
+</HEAD>
+<BODY>
+<H1>Address Completion</H1>
+
+When entering addresses in the address fields of the composer (To, Cc, etc.)
+the TAB key may be used to help complete the address.
+Type a partial nickname and tap the TAB key to complete the typing.
+The unambiguous part of the name will be filled in automatically.
+Typing TAB twice in succession will bring up a selection list of possibilities,
+making it easy to find and choose the correct address.
+
+<P>
+The matching algorithm is rather ad hoc.
+The search starts with a search of your address book.
+It counts as a match if the nickname, address, or fullname field of an
+entry begins with the text typed in so far. It is also a match if
+a later word in the fullname (for example, the middle name or last name)
+begins with the entered text.
+<P>
+Next comes an LDAP search.
+The search will happen for any servers that have the
+<A HREF="h_config_ldap_opts_impl">&quot;Use-Implicitly-From-Composer&quot;</A>
+feature set. You can set or unset the feature for each server independently
+in the Setup/Directory screen.
+<P>
+Finally, if you are replying to or forwarding a message, that message is
+searched for likely candidate addresses that match the typed-in text.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_compose_richhdr =======
+<HTML>
+<HEAD>
+<TITLE>Rich Header Command</TITLE>
+</HEAD>
+<BODY>
+<H1>Rich Header Command</H1>
+
+The Rich Header command allows you to toggle between the list of
+all message headers available for editing and those that are most
+common.
+
+<P>
+Use this toggle to expose headers that are not normally visible by
+default.
+This set usually includes the
+&quot;Bcc:&quot;,
+&quot;Fcc:&quot;,
+&quot;Lcc:&quot;,
+and &quot;Newsgroups&quot;
+headers.
+If you are posting to a newsgroup the set of defaults is a little different.
+Obviously, in that case, the Newsgroups header is of interest so is not
+hidden.
+For news posting the hidden set includes the
+&quot;To:&quot;,
+&quot;Cc:&quot;,
+&quot;Bcc:&quot;,
+&quot;Fcc:&quot;,
+and &quot;Lcc:&quot;
+headers.
+You won't normally want to edit these, which is why they are hidden,
+but it is sometimes useful to be able to set them manually.
+
+<P>
+The default sets of headers listed above can be altered.
+Any header that you have added to the
+<A HREF="h_config_custom_hdrs"><!--#echo var="VAR_customized-hdrs"--></A>
+option, but not to the
+<A HREF="h_config_comp_hdrs"><!--#echo var="VAR_default-composer-hdrs"--></A>
+option will appear when you use the Rich Headers command to
+make the Rich Headers visible.
+(Headers listed in the <!--#echo var="VAR_default-composer-hdrs"--> list will be visible
+even without toggling the Rich Headers command.)
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_compose_send =======
+<HTML>
+<HEAD>
+<TITLE>Send Command</TITLE>
+</HEAD>
+<BODY>
+<H1>Send Command</H1>
+
+The Send command
+<!--chtml if pinemode="function_key"-->
+(F3)
+<!--chtml else-->
+(^X)
+<!--chtml endif-->
+tells Alpine you are finished composing.
+Before actually sending it, though, Alpine will ask you to confirm
+your intention, and, at the same time, redisplayed the message text
+with the recipients at the top of the screen to give you the opportunity
+to review and verify that the message is addressed to the people
+you intended.
+<P>
+If the feature
+<A HREF="h_config_send_wo_confirm">&quot;<!--#echo var="FEAT_send-without-confirm"-->&quot;</A> is set,
+then this confirmation prompt and any options it allows are skipped.
+
+<P>
+This confirmation prompt may also offer, depending
+on your particular Setup/Config, options allowing you to set
+<A HREF="h_config_compose_dsn">delivery status notifications</A>,
+include attachments in the &quot;Fcc&quot; (if you had previously
+specified that they <A HREF="h_config_no_fcc_attach">exclude attachments</A>,
+observe details of the
+<A HREF="h_config_verbose_post">message submission process</A>,
+choose the filter through which the
+<A HREF="h_config_sending_filter">outgoing text should first pass</A>,
+or turn of flowed text generation.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_compose_markcutpaste =======
+<HTML>
+<HEAD>
+<TITLE>Mark, Cut and Paste Commands</TITLE>
+</HEAD>
+<BODY>
+<H1>Mark, Cut and Paste Commands</H1>
+
+You can define a &quot;block&quot; of text, which can subsequently
+ be deleted or
+copied as a unit, by setting a mark at the start of the block (Ctrl-^) and
+then moving the cursor to the end of the desired text block. You can then
+&quot;cut&quot; the block out
+<!--chtml if pinemode="function_key"-->
+&quot;F9&quot;,
+<!--chtml else-->
+&quot;Ctrl-K&quot;,
+<!--chtml endif-->
+move the cursor, and &quot;paste&quot; it
+<!--chtml if pinemode="function_key"-->
+&quot;F10&quot;,
+<!--chtml else-->
+&quot;Ctrl-U&quot;,
+<!--chtml endif-->
+in the new location. Also, you can paste more than once, allowing you
+to use this feature to copy a block of text.<P>
+
+If you press
+<!--chtml if pinemode="function_key"-->
+&quot;F9&quot;
+<!--chtml else-->
+&quot;^K&quot;
+<!--chtml endif-->
+without having marked anything, Alpine will delete
+a single line. If you delete a group of lines together, Alpine keeps them
+in the same buffer, so
+<!--chtml if pinemode="function_key"-->
+F10
+<!--chtml else-->
+^U
+<!--chtml endif-->
+will restore them as a block. About
+terminology: Mark is shown as &quot;^^&quot;. The first &quot;^&quot; means you should
+hold down the &quot;Control&quot; key on your keyboard. The second &quot;^&quot; means
+&quot;type the character ^&quot;.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_compose_justify =======
+<HTML>
+<HEAD>
+<TITLE>Justify Command</TITLE>
+</HEAD>
+<BODY>
+<H1>Justify Command</H1>
+
+The Justify
+<!--chtml if pinemode="function_key"-->
+(F4)
+<!--chtml else-->
+(^J)
+<!--chtml endif-->
+command reformats the text in the paragraph the cursor is in.
+Paragraphs are separated by one blank line or a line beginning with a space.
+This is useful when you have been editing a paragraph and the lines become
+uneven. The text is left aligned or justified and the right is ragged. If
+the text is already justified as typed with auto-wrap, no justification will
+be done.
+
+<P>
+If you have set a <A HREF="h_compose_markcutpaste">mark</A> to select a
+block of text, the Justify command is modified.
+Instead of automatically justifying the current paragraph you will be
+asked if you want to justify the paragraph, justify the selected region,
+or adjust the quote level of the selected region.
+Adjusting the quote level only works if you are using standard
+&quot;&gt;&nbsp;&quot; or &quot;&gt;&quot; quotes, which is the default if you haven't
+changed &quot;<A HREF="h_config_reply_indent_string"><!--#echo var="VAR_reply-indent-string"--></A>&quot;.
+
+<P>
+When composing a reply containing included text, the justify command
+will reformat text to the right of the
+&quot;<A HREF="h_config_reply_indent_string"><!--#echo var="VAR_reply-indent-string"--></A>&quot;,
+adding or removing indented lines as needed. Paragraphs are separated
+by a blank line, a line containing only the <!--#echo var="VAR_reply-indent-string"-->, or a
+line containing the indent string and one or more blank spaces.
+Included text that was previously indented (or &quot;quoted&quot;) is
+not preserved.
+
+<P>
+Because of the introduction of <A HREF="h_config_quell_flowed_text">Flowed Text</A>
+in 1999 and its wide-spread adoption since then, you will usually be better off if you
+use the standard
+&quot;&gt;&nbsp;&quot; or &quot;&gt;&quot; quotes.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_compose_spell =======
+<HTML>
+<HEAD>
+<TITLE>Spell Check Command</TITLE>
+</HEAD>
+<BODY>
+<H1>Spell Check Command</H1>
+
+The &quot;To Spell&quot;
+<!--chtml if pinemode="function_key"-->
+(F12)
+<!--chtml else-->
+(^T)
+<!--chtml endif-->
+command calls an external spell checking program to look over the
+message you are composing. By default, Alpine uses
+<P>
+<CENTER><SAMP>aspell --dont-backup --mode=email check</SAMP></CENTER>
+<P>
+if it knows where to find &quot;aspell&quot;.
+If there is no &quot;aspell&quot; command available but the command &quot;ispell&quot; is available
+then the command used is
+<P>
+<CENTER><SAMP>ispell -l</SAMP></CENTER>
+<P>
+Otherwise, the ancient &quot;spell&quot; command is used.
+<P>
+For PC-Alpine, you must install the aspell library code that you
+may get from
+<A HREF="http://aspell.net/win32/">http://aspell.net/win32/</A>.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_compose_alted =======
+<HTML>
+<HEAD>
+<TITLE>Alt Editor Command</TITLE>
+</HEAD>
+<BODY>
+<H1>Alt Editor Command</H1>
+
+The &quot;Alt Editor&quot; command's availability depends on the
+Setup/Config variable &quot;<A HREF="h_config_editor"><!--#echo var="VAR_editor"--></A>&quot;.
+
+<P>
+When the variable specifies a valid editor on your system, this
+command will launch it with the current text of your message
+already filled in.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_compose_readfile =======
+<HTML>
+<HEAD>
+<TITLE>Read File Command</TITLE>
+</HEAD>
+<BODY>
+<H1>Read File Command</H1>
+
+The &quot;Read File&quot;
+<!--chtml if pinemode="function_key"-->
+(F5)
+<!--chtml else-->
+(^R)
+<!--chtml endif-->
+command allows you to copy in text from an existing file. You will be
+prompted for the name of a file to be inserted into the message. The file
+name is relative to your home directory
+<!--chtml if pinemode="running"-->
+(which, in the present configuration of your system, is
+ &quot;<!--#echo var="HOME_DIR"-->&quot;)
+<!--chtml endif-->
+or current working directory
+<!--chtml if pinemode="running"-->
+(which, at least for your current Alpine &quot;session,&quot;
+is &quot;<!--#echo var="CURRENT_DIR"-->&quot;)
+<!--chtml endif-->, depending on the
+<A HREF="h_config_use_current_dir">&quot;<!--#echo var="FEAT_use-current-dir"-->&quot;</A>
+configuration setting; or, the file name must be specified as a full path name
+<!--chtml if pinemode="os_windows"-->
+-- for example: &quot;A:&#92;PAPER.TXT&quot;
+<!--chtml else-->
+-- for example: &quot;/tmp/wisdom-of-the-day&quot;
+<!--chtml endif-->
+(without the quotation marks).
+
+<P>
+The file will be inserted where the cursor is located. <B>The
+file to be read must be on the same system as Alpine.</B> If you use Alpine on a
+Unix machine but have files on a PC or Mac, the files must be transferred
+to the system Alpine is running on before they can be read. Please ask your
+local computer support people about the correct way to transfer a file to
+your Alpine system.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_tray_icon =======
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-tray-icon"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-tray-icon"--></H1>
+
+PC-Alpine only.
+<P>
+This option restores a behavior of previous versions of PC-Alpine.
+These
+versions, when started, installed a PC-Alpine icon in the notification
+tray of Window's Taskbar. The primary use of this icon was to indicate
+new mail arrival by turning red (while the Taskbar icon remained green).
+Additionally, the icon now changes to yellow to signify that a mail folder
+has been closed unexpectedly.
+
+<P>
+Rather than add another icon to the Taskbar, this version of PC-Alpine will
+color its Taskbar entry's icon red (as well as the icon in the Window
+Title). This feature is only provided for backwards compatibility.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_common_suspend =======
+<HTML>
+<HEAD>
+<TITLE>Suspend Command</TITLE>
+</HEAD>
+<BODY>
+<H1>Suspend Command</H1>
+
+With the <A HREF="h_config_can_suspend"><!--#echo var="FEAT_enable-suspend"--></A> feature
+enabled, you can, at almost any time, temporarily halt your Alpine session,
+<!--chtml if pinemode="os_windows"-->
+minimizing it into an icon.
+<!--chtml else-->
+and return to your system prompt.
+<!--chtml endif-->
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_pipe_command =======
+<HTML>
+<HEAD>
+<TITLE>Pipe Command SubOptions</TITLE>
+</HEAD>
+<BODY>
+<H1>Pipe Command SubOptions</H1>
+
+By default, when you use the Pipe command, the processed text of the
+message is sent to the Unix command
+you specify and the output is captured by Alpine and shown to you.
+(This command is available in PC-Alpine, as well, but there aren't many
+Windows commands that work well with piping.)
+There are some sub-commands that may be used to alter this behavior.
+These are toggles that switch the behavior between two possibilities.
+They can be combined in any way you wish.
+<P>
+By default, the prompt at the bottom of the screen looks like
+<P>
+<CENTER><SAMP>Pipe message 37 to :</SAMP></CENTER>
+<P>
+or
+<P>
+<CENTER><SAMP>Pipe messages to :</SAMP></CENTER>
+<P>
+if you are piping more than one message.
+<P>
+The sub-command options are:
+<DL>
+ <DT>Shown Text or Raw Text</DT>
+ <DD>This option toggles between sending the shown (processed) text
+of the message to the Unix command, and sending the
+raw (unprocessed) text of the message to the Unix command.
+The default is to send the shown text.
+The raw version of the message will contain all of the headers and any
+MIME encoding that the message contains.
+If you've selected the Raw Text then the prompt will have the additional word
+&quot;RAW&quot; in it, like
+<P>
+<CENTER><SAMP>Pipe RAW messages to :</SAMP></CENTER>
+<P>
+You can experiment with this option by piping to something simple like the
+Unix &quot;cat&quot; command.
+ </DD>
+ <DT>Captured Output or Free Output</DT>
+ <DD>This option toggles between having Alpine capture the output of
+the Unix pipe command for display, and not capturing it.
+If the command you are piping to is a filter that will produce output
+you want to view, then you want to capture that output
+for display (the default).
+If the Unix command doesn't produce output or handles the display itself,
+then you want free output.
+When you've selected the Free Output option the prompt will change to
+<P>
+<CENTER><SAMP>Pipe messages to (uncaptured) :</SAMP></CENTER>
+<P>
+ </DD>
+ <DT>No Delimiter or With Delimiter</DT>
+ <DD>This option controls whether or not a Unix mailbox style delimiter
+will come before the text of the message.
+This is the delimiter used in the common Unix mailbox format.
+It's the single line that begins with the five characters
+&quot;From&quot; followed by a &lt;SPACE&gt; character.
+You'll usually only want to include this if the Unix command requires
+input in the format of a traditional Unix mailbox file.
+When you've selected the With Delimiter option the prompt will change to
+<P>
+<CENTER><SAMP>Pipe messages to (delimited) :</SAMP></CENTER>
+<P>
+ </DD>
+ <DT>To Same Pipe or To Individual Pipes</DT>
+ <DD>This option only shows up if you are running an aggregate
+pipe command.
+That is, the command was Apply Pipe, not just Pipe.
+You have the option of piping all of the selected messages through a
+single pipe to a single instance of the Unix command,
+or piping each individual message through a separate pipe to separate
+instances of the Unix command.
+The default is that all of the output will go through a single pipe
+to a single instance of the command.
+You can try this option with a command like &quot;less&quot;, with Free
+Output enabled.
+When you've selected the Individual Pipes option the prompt will change to
+<P>
+<CENTER><SAMP>Pipe messages to (new pipe) :</SAMP></CENTER>
+<P>
+ </DD>
+</DL>
+
+<P>
+As mentioned earlier, the options can be combined in any way you wish.
+You may leave them all off, turn them all on, or turn some of them on
+and some of them off.
+If you use the pipe command a second time in the same session the default
+options will be what you used the last time.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+========== h_emptydir_subfolder_name =========
+<HTML>
+<HEAD>
+<TITLE>ENTER SUBFOLDER NAME</TITLE>
+</HEAD>
+<BODY>
+<H1>Enter Subfolder Name</H1>
+
+<P>
+This is the name of a new subfolder in the directory you are creating.
+Because empty directories are hidden and therefore not useful, you must also
+create a subfolder in the directory you are creating in order that the
+directory remains visible.
+<P>
+Alternatively, you may turn off the configuration feature
+<A HREF="h_config_quell_empty_dirs"><!--#echo var="FEAT_quell-empty-directories"--></A>
+so that empty directories remain visible.
+If you do that, you will not be required to create the subfolder when you
+create a directory.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+========== h_incoming_add_folder_name =========
+<HTML>
+<HEAD>
+<TITLE>ENTER FOLDER NAME</TITLE>
+</HEAD>
+<BODY>
+<H1>Enter Folder Name</H1>
+
+<P>
+This is the name of the folder on the previously specified server.
+By default the folder name is interpreted as defining a section of your personal
+folder area. This area and how you specify it are defined by the
+server, if one is specified.
+
+<P>
+To define a folder outside the default area, prefix
+the path with the namespace to use when interpreting the
+given path. If a namespace is specified, the folder name begins with the
+sharp (#) character followed by the name of the namespace
+and then the namespace's path-element-delimiter. Aside from the
+name's format, namespaces can also imply access rights, content
+policy, audience, location, and, occasionally, access methods.
+
+<P>
+Each server exports its own set (possibly of size one) of
+namespaces.
+For a more detailed explanation read about
+<A HREF="h_folder_name_namespaces">Namespaces</A>.
+
+<P>
+To specify the default for INBOX on the server you can usually just enter
+&quot;INBOX&quot;, and the server will understand the special meaning of
+that word.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+========== h_incoming_add_folder_host =========
+<HTML>
+<HEAD>
+<TITLE>ENTER INCOMING FOLDER SERVER</TITLE>
+</HEAD>
+<BODY>
+<H1>Enter Incoming Folder Server</H1>
+
+You are being asked for the name of the server for use with this incoming
+folder.
+If the folder is on the machine where Alpine is running, then just enter
+RETURN without typing a server name.
+
+<P>
+If the folder is on an IMAP server then type the server's name followed
+by RETURN.
+You may use the ^X command if the server is the same as the server that
+the INBOX is on.
+
+<P>
+You may have to add optional extra information to the server name.
+For example, if the IMAP server is using a non-standard port number you
+would specify that by appending a colon (:) followed by the port number
+to the server name
+
+<P>
+<CENTER><SAMP>foo.example.com:port</SAMP></CENTER>
+<P>
+
+or you may need to specify a different protocol if the server is not an
+IMAP server. For example:
+
+<P>
+<CENTER><SAMP>foo.example.com/pop3</SAMP></CENTER>
+<P>
+
+for a POP server or
+
+<P>
+<CENTER><SAMP>foo.example.com/nntp</SAMP></CENTER>
+<P>
+
+for an NNTP news server.
+For an explanation of all of the possibilities, see
+<A HREF="h_folder_server_syntax">Server Name Syntax</A>
+for folders.
+
+<P>
+There is a special command (^W) if you want to set up a folder that gets its
+mail from a
+<A HREF="h_maildrop">Mail Drop</A>.
+If you type that command, you will be prompted for the information for
+both the Mail Drop folder and the destination folder.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+========== h_incoming_add_inbox =========
+<HTML>
+<HEAD>
+<TITLE>ENTER INBOX SERVER</TITLE>
+</HEAD>
+<BODY>
+<H1>Enter INBOX Server</H1>
+
+You are being asked for the name of the server for use with
+the INBOX folder.
+Type the server's name followed by RETURN.
+
+<P>
+You may have to add optional extra information to the server name.
+For example, if the server is using a non-standard port number you
+would specify that by appending a colon (:) followed by the port number
+to the server name
+
+<P>
+<CENTER><SAMP>foo.example.com:port</SAMP></CENTER>
+<P>
+
+or you may need to specify a different protocol if the server is not an
+IMAP server. For example:
+
+<P>
+<CENTER><SAMP>foo.example.com/pop3</SAMP></CENTER>
+<P>
+
+for a POP server.
+<P>
+
+For an explanation of all of the possibilities, see
+<A HREF="h_folder_server_syntax">Server Name Syntax</A>
+for folders.
+
+<P>
+If the INBOX folder is on the machine where Alpine is running, then just enter
+RETURN without typing a server name.
+
+<P>
+There is a special command (^W) if you want to set up a folder that gets its
+mail from a
+<A HREF="h_maildrop">Mail Drop</A>.
+If you type that command, you will be prompted for the information for
+both the Mail Drop folder and the destination folder, which will be used
+as your INBOX folder.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+========== h_incoming_add_maildrop_destn =========
+<HTML>
+<HEAD>
+<TITLE>ENTER DESTINATION SERVER</TITLE>
+</HEAD>
+<BODY>
+<H1>Enter Destination Server</H1>
+
+You are being asked for the name of the server where the destination
+folder is for use with this Mail Drop incoming folder.
+That is, you are using a Mail Drop for this incoming folder and
+you've already entered
+the server and folder name for the Mail Drop.
+Now you need to enter the server for the destination folder
+where the mail should be copied to.
+Mail will come from the Mail Drop and be copied to the destination folder.
+
+<P>
+Type the server's name followed by RETURN.
+If the folder is local to this computer, just type RETURN without entering
+a server name.
+
+<P>
+You may have to add optional extra information to the server name.
+For example, if the server is using a non-standard port number you
+would specify that by appending a colon (:) followed by the port number
+to the server name
+
+<P>
+<CENTER><SAMP>foo.example.com:port</SAMP></CENTER>
+<P>
+
+For an explanation of all of the possibilities, see
+<A HREF="h_folder_server_syntax">Server Name Syntax</A>
+for folders.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+========== h_inbox_add_maildrop_destn =========
+<HTML>
+<HEAD>
+<TITLE>ENTER DESTINATION SERVER</TITLE>
+</HEAD>
+<BODY>
+<H1>Enter Destination Server</H1>
+
+You are being asked for the name of the server where the destination
+folder is for use with your Mail Drop INBOX.
+That is, you are using a Mail Drop for your INBOX and you've already entered
+the server and folder name for the Mail Drop.
+Now you need to enter the server for the destination folder
+where the mail should be copied to.
+Mail will come from the Mail Drop and be copied to the destination folder.
+
+<P>
+Type the server's name followed by RETURN.
+If the folder is local to this computer, just type RETURN without entering
+a server name.
+
+<P>
+You may have to add optional extra information to the server name.
+For example, if the server is using a non-standard port number you
+would specify that by appending a colon (:) followed by the port number
+to the server name
+
+<P>
+<CENTER><SAMP>foo.example.com:port</SAMP></CENTER>
+<P>
+
+For an explanation of all of the possibilities, see
+<A HREF="h_folder_server_syntax">Server Name Syntax</A>
+for folders.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+========== h_inbox_add_maildrop =========
+<HTML>
+<HEAD>
+<TITLE>ENTER MAILDROP SERVER</TITLE>
+</HEAD>
+<BODY>
+<H1>Enter Mail Drop Server</H1>
+
+You are being asked for the name of the Mail Drop server for use with
+your INBOX.
+
+<P>
+Type the server's name followed by RETURN.
+
+<P>
+You may have to add optional extra information to the server name.
+For example, if the server is using a non-standard port number you
+would specify that by appending a colon (:) followed by the port number
+to the server name
+
+<P>
+<CENTER><SAMP>foo.example.com:port</SAMP></CENTER>
+<P>
+
+or you may need to specify a different protocol if the server is not an
+IMAP server. For example:
+
+<P>
+<CENTER><SAMP>foo.example.com/pop3</SAMP></CENTER>
+<P>
+
+for a POP server or
+
+<P>
+<CENTER><SAMP>foo.example.com/nntp</SAMP></CENTER>
+<P>
+
+for an NNTP news server.
+For an explanation of all of the possibilities, see
+<A HREF="h_folder_server_syntax">Server Name Syntax</A>
+for folders.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+========== h_incoming_add_maildrop =========
+<HTML>
+<HEAD>
+<TITLE>ENTER MAILDROP SERVER</TITLE>
+</HEAD>
+<BODY>
+<H1>Enter Mail Drop Server</H1>
+
+You are being asked for the name of the Mail Drop server for use with
+this incoming folder.
+
+<P>
+Type the server's name followed by RETURN.
+You may use the ^X command if the server is the same as the server that
+the INBOX is on.
+
+<P>
+You may have to add optional extra information to the server name.
+For example, if the server is using a non-standard port number you
+would specify that by appending a colon (:) followed by the port number
+to the server name
+
+<P>
+<CENTER><SAMP>foo.example.com:port</SAMP></CENTER>
+<P>
+
+or you may need to specify a different protocol if the server is not an
+IMAP server. For example:
+
+<P>
+<CENTER><SAMP>foo.example.com/pop3</SAMP></CENTER>
+<P>
+
+for a POP server or
+
+<P>
+<CENTER><SAMP>foo.example.com/nntp</SAMP></CENTER>
+<P>
+
+for an NNTP news server.
+For an explanation of all of the possibilities, see
+<A HREF="h_folder_server_syntax">Server Name Syntax</A>
+for folders.
+
+<P>
+If the Mail Drop folder is on the machine where Alpine is running, then just enter
+RETURN without typing a server name.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+========== h_maildrop =========
+<HTML>
+<HEAD>
+<TITLE>WHAT IS A MAIL DROP?</TITLE>
+</HEAD>
+<BODY>
+<H1>What is a Mail Drop?</H1>
+
+In some situaions it may make sense to have your mail delivered to one
+folder (the Mail Drop) and then when you want to read mail that has been
+delivered to the Mail Drop folder Alpine will move it to another
+destination folder.
+Often the Mail Drop will be a remote folder and messages will be moved from
+there to a local destination folder.
+
+<P>
+One example where this might make sense is if the Mail Drop folder is accessible
+only with the POP protocol.
+You could designate your POP inbox as the Mail Drop folder and have Alpine move
+mail from there to a local (on the same machine Alpine is running on)
+destination folder, where you'll read it.
+
+<P>
+A Mail Drop may only be used as your Inbox or as an
+<A HREF="h_config_enable_incoming">&quot;Incoming folder&quot;</A>.
+
+<P>
+There is no attempt to synchronize the contents of the destination folder
+with the contents of the Mail Drop folder.
+All that happens is that all of the messages in the Mail Drop folder are
+copied to the destination folder and then they are deleted and expunged (if possible)
+from the Mail Drop folder.
+The next time a check for new mail is made, any messages in the Mail
+Drop folder are once again copied to the destination folder and deleted
+and expunged from the Mail Drop folder.
+(If the Mail Drop folder is a news group, then the messages can't be
+expunged from the newsgroup. Instead, only Recent messages are copied from
+the newsgroup to the destination folder.)
+
+<P>
+Configuration of a Mail Drop is a little different from configuration of
+a folder that does not use a Mail Drop because you have to specify two
+folder names instead of one.
+The two folders may be any types of folders that Alpine can normally use.
+They don't have to be a remote folder and a local folder, that is
+simply the most common usage.
+When you use a Mail Drop folder Alpine will periodically re-open the Mail
+Drop to check for new mail.
+The new-mail checks will happen at the frequency set with the
+<A HREF="h_config_mailcheck"><!--#echo var="VAR_mail-check-interval"--></A> option,
+but with a minimum time
+(<A HREF="h_config_maildropcheck"><!--#echo var="VAR_maildrop-check-minimum"--></A>)
+between checks.
+Because of this minimum you may notice that new mail does not
+appear promptly when you expect it.
+The reason for this is to protect the server from over-zealous opening and
+closing of the Mail Drop folder.
+If the user initiates the check by typing ^L (Ctrl-L) or the Next command when at
+the end of the folder index, then the check will happen, regardless of how
+long it has been since the previous check.
+<P>
+If there is new mail, that mail will be copied to the destination folder
+and then will be deleted from the Mail Drop.
+Note that using a Mail Drop with a local destination folder does not make
+sense if you read mail from more than one machine, because the mail is
+downloaded to the destination folder (which is accessible from only one
+machine) and deleted from the Mail Drop.
+<P>
+The feature <A HREF="h_config_maildrops_preserve_state"><!--#echo var="FEAT_maildrops-preserve-state"--></A> modifies the operation of Mail Drops.
+
+<P>
+The actual syntax used by Alpine for a folder that uses a Mail Drop is:
+
+<P>
+<CENTER><SAMP>#move&lt;DELIM&gt;&lt;MailDropFolder&gt;&lt;DELIM&gt;&lt;DestinationFolder&gt;</SAMP></CENTER>
+<P>
+The brackets are not literal.
+<P>
+<CENTER><SAMP>&lt;DELIM&gt;</SAMP></CENTER>
+<P>
+is a single character that does not appear in the MailDropFolder name.
+If the name doesn't contain spaces then it can be a space character.
+The two folder names are full technical
+<A HREF="h_valid_folder_names">folder names</A>
+as used by Alpine.
+Here are a couple examples to give you an idea what is being talked about:
+
+<P>
+<CENTER><SAMP>#move&nbsp;{popserver.example.com/pop3}inbox&nbsp;localfolder</SAMP></CENTER>
+<P>
+<CENTER><SAMP>#move+{nntpserver.example.com/nntp}#news.comp.mail.pine+local&nbsp;folder</SAMP></CENTER>
+<P>
+
+A #move folder may only be used as an
+<A HREF="h_config_enable_incoming">&quot;Incoming folder&quot;</A> or
+an Inbox.
+When you are in the FOLDER LIST of Incoming Message Folders (after turning
+on the
+<A HREF="h_config_enable_incoming">&quot;<!--#echo var="FEAT_enable-incoming-folders"-->&quot;</A>
+option)
+the Add command has a subcommand &quot;Use Mail Drop&quot;
+which may be helpful for defining the folder in your Alpine configuration.
+The same is true when you edit the
+<A HREF="h_config_inbox_path"><!--#echo var="VAR_inbox-path"--></A>
+option in Setup/Config.
+Each of these configuration methods will also create the DestinationFolder
+if it doesn't already exist.
+If you are having problems, make sure the DestinationFolder exists.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+========== h_save =========
+<HTML>
+<HEAD>
+<TITLE>CHOOSE A FOLDER TO SAVE INTO</TITLE>
+</HEAD>
+<BODY>
+<H1>Choose a Folder to Save Into</H1>
+
+After Exiting from this help text,
+type the name of the folder you want to save into and press RETURN.
+<P>
+Press ^T to get a list of your folders to choose from.
+Press ^C to cancel the Save.
+<P>
+If you have Folder Collections defined you may use
+Ctrl-P (Previous collection) and Ctrl-N (Next collection) to switch
+the collection being saved to.
+<P>
+If Tab Completion is enabled (it is enabled by default)
+you may type a Tab character to have Alpine complete the folder name for you.
+<P>
+If Partial Match Lists is enabled (it is enabled by default) you may type
+Ctrl-X to get a list of matches to the prefix you've typed in so far.
+<P>
+If the Ctrl-R subcommand is present that means you can decide to Delete or
+not Delete the message you are saving after you save it.
+The label on that key gives the action to switch to.
+If it says Delete and you type Ctrl-R the label displayed will change to
+No Delete and the source message will be deleted after the save. If it
+says No Delete and you type Ctrl-R the label displayed will change to
+Delete and the message will not be deleted.
+You can control the default for the Delete parameter with the
+configuration feature <!--#echo var="FEAT_save-will-not-delete"-->.
+<P>
+Similarly, if the Ctrl-W subcommand is present that means you can decide
+to Preserve the order of the messages being saved or not.
+If it is labeled Preserve Order and you type Ctrl-W, the resulting Saved messages
+will be in the same order as you see them in the source folder now.
+The opposite action (which is usually the default) is that you don't care
+about the order.
+The Saved messages may or may not be in the same order in the destination folder.
+There may be a performance penalty for choosing to save the messages in order.
+You can control the default for the Preserve Order parameter with the
+configuration feature
+<!--#echo var="FEAT_save-aggregates-copy-sequence"-->.
+
+<P>
+If you haven't disabled the Save Input History and you've already done a
+Save earlier in this session then you may use the Up and Down arrows to retrieve
+a folder name used in a previous Save.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+============= h_simple_index ========================
+<HTML>
+<HEAD>
+<TITLE>SELECT POSTPONED MESSAGE</TITLE>
+</HEAD>
+<BODY>
+<H1>POSTPONED MESSAGE SELECTION COMMANDS</H1>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Navigating the List of Messages General Alpine Commands
+------------------------------- ---------------------
+ F5 Move to previous message F1 Show this help text
+ F6 Move to next message
+ F7 Show previous screen of messages
+ F8 Show next screen of messages
+
+Message Selection Commands
+--------------------------
+ F3 Exit the Message Select menu (canceling Send command)
+ F4 Select the currently highlighted message
+ F9 Mark the currently highlighted message as deleted
+ F10 Undelete (remove deletion mark from) the highlighted message
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigating the List of Messages General Alpine Commands
+------------------------------- ---------------------
+ P Move to previous message ? Show this help text
+ N Move to next message
+ - Show previous screen of messages
+ Spc (space bar) Show next screen of messages
+
+Message Selection Commands
+--------------------------
+ E Exit the Message Select menu (canceling Send command)
+ S Select the currently highlighted message
+ D Mark the currently highlighted message as deleted
+ U Undelete (remove deletion mark from) the highlighted message
+</PRE>
+<!--chtml endif-->
+<P>
+<H2>Description of the Select Postponed Message Screen</H2>
+
+This screen allows you to select one of several previously postponed
+messages in order to continue composition. Your options are very limited
+-- the screen is not meant to let you manipulate these messages. However,
+you may now delete messages from this list. Once you choose a message,
+Alpine reads it in and puts you into the regular message composer.
+<P>
+
+Messages do not stay in this postponed state automatically. If you select
+a message and then want to postpone it again, use the normal postpone
+(Ctrl-O) command in the composer.
+<P>
+
+If you exit this screen without selecting a message, the Compose command
+that got you here is canceled. Other than messages explicitly marked
+&quot;Deleted&quot;, no messages will be removed.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+============= h_collection_screen ========================
+<HTML>
+<HEAD>
+<TITLE>COLLECTION LIST screen</TITLE>
+</HEAD>
+<BODY>
+<H1>COLLECTION LIST screen</H1>
+
+The COLLECTION LIST screen is used to select one of your
+collection definitions to display the folders they contain. See
+<A HREF="h_what_are_collections">Folder Collections Explained</A> for
+detailed explanation of collections.<P>
+
+To manage your collection definitions (Add, Change, Delete, etc.), use
+the <A HREF="h_collection_maint">Setup/collectionList</A> command on Alpine's
+MAIN MENU.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+============= h_collection_maint ========================
+<HTML>
+<HEAD>
+<TITLE>SETUP COLLECTION LIST screen</TITLE>
+</HEAD>
+<BODY>
+<H1>SETUP COLLECTION LIST screen</H1>
+
+The SETUP COLLECTION LIST screen lets you manage your collection
+definitions. See
+<A HREF="h_what_are_collections">Folder Collections Explained</A>
+for detailed explanation of collections.<P>
+
+Maintenance commands include:
+<DL>
+<DT>Change
+<!--chtml if pinemode="function_key"-->
+(F4)
+<!--chtml else-->
+(C)
+<!--chtml endif-->
+</DT>
+
+<DD>Modify attributes of the selected collection definition.
+
+<DT>Add Cltn
+<!--chtml if pinemode="function_key"-->
+(F9)
+<!--chtml else-->
+(A)
+<!--chtml endif-->
+</DT>
+
+<DD>Create a new collection definition.
+</DD>
+
+<DT>Del Cltn
+<!--chtml if pinemode="function_key"-->
+(F10)
+<!--chtml else-->
+(D)
+<!--chtml endif-->
+</DT>
+
+<DD>Delete the selected collection definition.<BR>
+NOTE: The folders and directories referred to by the
+collection definition are <EM>NOT</EM> deleted. Folders must
+be deleted, if that's what you wish to do, from the
+<A HREF="h_folder_maint">FOLDER LIST screen</A>, which shows the
+individual folders in a collection.
+</DD>
+
+<DT>Shuffle
+<!--chtml if pinemode="function_key"-->
+(F11)
+<!--chtml else-->
+($)
+<!--chtml endif-->
+</DT>
+
+<DD>Change the order of the displayed collections. Alpine will offer
+to move the currently selected collection one position UP
+or DOWN.
+</DD>
+</DL>
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+============ h_what_are_collections ==========
+<HTML>
+<HEAD>
+<TITLE>Folder Collections Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Folder Collections Explained</H1>
+
+
+Those of you with simple mail configurations will just see a list of all the
+folders you have when choosing FOLDER LIST from Alpine's MAIN MENU.
+The special folders for INBOX, sent mail and saved messages
+will appear at the top of the list. All others are in alphabetical order.
+<P>
+If you
+or your system administrator have defined more than one collection or if
+you have a collection (for newsgroups or email folders) defined on your
+system, then you will see the COLLECTION LIST screen first when choosing
+FOLDER LIST from Alpine's MAIN MENU.
+<P>
+<H2>Why have multiple folder collections?</H2>
+<P>
+For Alpine users who only maintain email folders (and not too many) on one host,
+a single folder collection is probably sufficient.<P>
+
+However, people who have more than one email account - for example, one
+at their university, and one with their personal Internet Service Provider -
+will have different sets of folders on different hosts, and they may want to
+access them all from the same installation of Alpine, rather than use different
+software and/or log into other hosts to manipulate messages in different
+accounts. (If in doubt whether one of your email accounts can be accessed
+with Alpine, contact the technical support people for that account.) Even people
+who have only one email account on one host, but have dozens or
+hundreds of email folders, may want to arrange these folders together in a
+meaningful way.<BR>
+That is where multiple collections come in.
+
+<H2>Types of Collections</H2>
+<DL>
+<DT>INCOMING FOLDERS</DT>
+<DD>&quot;Incoming Message Folders&quot;
+is a special collection typically used to supplement your single INBOX.
+All the folders here are meant to be ones that receive incoming messages,
+which you intend to check more or less frequently.
+You may have multiple folders like this because you or your systems
+administrator have set up an external program or you may have set up
+Alpine to filter incoming
+messages into different folders, based on certain criteria such as
+sender, recipient, or subject; or because you have multiple accounts and
+wish to check their INBOXes easily. This collection is established by
+setting the
+<A HREF="h_config_enable_incoming">&quot;<!--#echo var="FEAT_enable-incoming-folders"-->&quot;</A>
+feature in the SETUP CONFIGURATION screen, which is accessed from the
+MAIN MENU.
+</DD>
+
+<DT>NEWS</DT>
+<DD>You can also define a collection specifically for
+newsgroups. Alpine does this for you implicitly when you
+<A HREF="h_config_nntp_server">define an NNTP Server</A>
+in your Alpine configuration. The news collection appears last in the
+COLLECTION LIST (though you can shuffle it up in the order of presentation),
+and Alpine knows not to save messages there.
+</DD>
+
+<DT>DEFAULT COLLECTION</DT>
+<DD>This is the default collection for your saved and sent messages folders.
+</DD>
+</DL>
+
+<P>
+
+<H2>Defining Collections</H2>
+<P>
+In the absence of any folder-collection definitions, Alpine will assume a
+single default folder collection.
+<!--chtml if pinemode="os_windows"-->
+<!--chtml else-->
+If necessary, Alpine will create the directory
+&quot;mail&quot; in your Unix home directory
+to hold your folders.
+<!--chtml endif-->
+<P>
+You can use the
+<A HREF="h_collection_maint">Setup/collectionList screen</A>, called up from
+the MAIN MENU, to manage your collection list.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_select_address_screen =====
+<HTML>
+<HEAD>
+<TITLE>SELECT AN ADDRESS SCREEN</TITLE>
+</HEAD>
+<BODY>
+<H1>SELECT AN ADDRESS SCREEN</H1>
+<H2>COMMANDS</H2>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Available Commands
+-------------------------------
+F1 Show Help Text
+F3 Exit without selecting anything
+F4 Select the highlighted address
+F5 Move highlight to previous address
+F6 Move highlight to next address
+F7 Previous page of addresses
+F8 Next page of addresses
+F11 Print
+F12 WhereIs
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigation General Alpine Commands
+----------------------- ---------------------
+ P Prev Address ? Display this help text
+ N Next Address E Exit without selecting anything
+ - Previous page % Print
+Spc (space bar) Next page
+ W WhereIs
+
+Select Command
+------------------------------------------------
+ S Select the highlighted address
+</PRE>
+<!--chtml endif-->
+
+<H2>Description of the Select Address Screen</H2>
+
+This screen gives you an easy way to select an address from all of
+the address book entries that match the prefix typed so far.
+
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_select_rule_screen =====
+<HTML>
+<HEAD>
+<TITLE>SELECT A RULE SCREEN</TITLE>
+</HEAD>
+<BODY>
+<H1>SELECT A RULE SCREEN</H1>
+<H2>COMMANDS</H2>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Available Commands
+-------------------------------
+F1 Show Help Text
+F3 Exit without selecting anything
+F4 Select the highlighted rule
+F5 Move highlight to previous rule
+F6 Move highlight to next rule
+F7 Previous page of rules
+F8 Next page of rules
+F11 Print
+F12 WhereIs
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigation General Alpine Commands
+----------------------- ---------------------
+ P Prev Rule ? Display this help text
+ N Next Rule E Exit without selecting anything
+ - Previous page % Print
+Spc (space bar) Next page
+ W WhereIs
+
+Select Command
+------------------------------------------------
+ S Select the highlighted rule
+</PRE>
+<!--chtml endif-->
+
+<H2>Description of the Select Rule Screen</H2>
+
+This screen just gives you an easy way to select a rule from all of your
+defined rules.
+The list of rules presented is the list of nicknames of all of the rules
+defined using Setup/Rules.
+For selecting messages, it is likely that the Indexcolor rules and possibly
+the Roles rules will be most useful.
+The others are there also, in case you find a use for them.
+<P>
+In order for this to be useful for selecting messages, the nicknames of
+the rules have to be different.
+Alpine actually just gets the nickname of the rule that you select and then
+looks up that rule using the nickname.
+So if there are duplicate nicknames, the first rule that has that
+nickname will be used.
+
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_select_priority_screen =====
+<HTML>
+<HEAD>
+<TITLE>SELECT A PRIORITY SCREEN</TITLE>
+</HEAD>
+<BODY>
+<H1>SELECT A PRIORITY SCREEN</H1>
+<H2>COMMANDS</H2>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Available Commands
+-------------------------------
+F1 Show Help Text
+F3 Exit without selecting anything
+F4 Select the highlighted priority
+F5 Move highlight to previous priority
+F6 Move highlight to next priority
+F7 Previous page of priorities
+F8 Next page of priorities
+F11 Print
+F12 WhereIs
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigation General Alpine Commands
+----------------------- ---------------------
+ P Prev Priority ? Display this help text
+ N Next Priority E Exit without selecting anything
+ - Previous page % Print
+Spc (space bar) Next page
+ W WhereIs
+
+Select Command
+------------------------------------------------
+ S Select the highlighted priority
+</PRE>
+<!--chtml endif-->
+
+<H2>Description of the Select Priority Screen</H2>
+
+This screen gives you a way to select a priority for the message you are sending.
+This priority will be placed in the non-standard X-Priority header of your outgoing mail.
+Some mail programs will display an indication of the priority level to
+the recipient of the message, some will ignore it.
+Even in cases where the mail programs of both the sender and the recipient
+agree on the meaning of this header, keep in mind that it is
+something that the sender sets so it is only an indication
+of the priority that the sender attaches to the mail.
+Alpine can be made to display an indication of this priority in incoming
+messages by use of one of the tokens
+(<A HREF="h_index_tokens">Tokens for Index and Replying</A>)
+PRIORITY, PRIORITYALPHA, or PRIORITY! in the
+<A HREF="h_config_index_format"><!--#echo var="VAR_index-format"--></A> option.
+
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_select_keyword_screen =====
+<HTML>
+<HEAD>
+<TITLE>SELECT A KEYWORD SCREEN</TITLE>
+</HEAD>
+<BODY>
+<H1>SELECT A KEYWORD SCREEN</H1>
+<H2>COMMANDS</H2>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Available Commands
+-------------------------------
+F1 Show Help Text
+F3 Exit without selecting anything
+F4 Select the highlighted keyword
+F5 Move highlight to previous keyword
+F6 Move highlight to next keyword
+F7 Previous page of keywords
+F8 Next page of keywords
+F11 Print
+F12 WhereIs
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigation General Alpine Commands
+----------------------- ---------------------
+ P Prev Keyword ? Display this help text
+ N Next Keyword E Exit without selecting anything
+ - Previous page % Print
+Spc (space bar) Next page
+ W WhereIs
+
+Select Command
+------------------------------------------------
+ S Select the highlighted keyword
+</PRE>
+<!--chtml endif-->
+
+<H2>Description of the Select Keyword Screen</H2>
+
+This screen just gives you an easy way to select a keyword.
+The list of keywords presented is the list of all keywords defined in your
+<A HREF="h_config_keywords"><!--#echo var="VAR_keywords"--></A> option.
+If you have given a keyword a nickname, that nickname is displayed
+instead of the actual keyword.
+
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_select_charset_screen =====
+<HTML>
+<HEAD>
+<TITLE>SELECT A CHARACTER SET SCREEN</TITLE>
+</HEAD>
+<BODY>
+<H1>SELECT A CHARACTER SET SCREEN</H1>
+<H2>COMMANDS</H2>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Available Commands
+-------------------------------
+F1 Show Help Text
+F3 Exit without selecting anything
+F4 Select the highlighted character set
+F5 Move highlight to previous character set
+F6 Move highlight to next character set
+F7 Previous page of character sets
+F8 Next page of character sets
+F11 Print
+F12 WhereIs
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigation General Alpine Commands
+----------------------- ---------------------
+ P Prev Character Set ? Display this help text
+ N Next Character Set E Exit without selecting anything
+ - Previous page % Print
+Spc (space bar) Next page
+ W WhereIs
+
+Select Command
+------------------------------------------------
+ S Select the highlighted character set
+</PRE>
+<!--chtml endif-->
+
+<H2>Description of the Select A Character Set Screen</H2>
+
+This screen just gives you an easy way to select a character set from the
+set of character sets Alpine knows about.
+The list presented will vary slightly depending on what option you are
+selecting the character set for.
+
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_select_multcharsets_screen =====
+<HTML>
+<HEAD>
+<TITLE>SELECT CHARACTER SETS SCREEN</TITLE>
+</HEAD>
+<BODY>
+<H1>SELECT CHARACTER SETS SCREEN</H1>
+<H2>COMMANDS</H2>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Available Commands
+-------------------------------
+F1 Show Help Text
+F3 Exit without selecting anything
+F4 Select the highlighted charset (or chosen charsets in ListMode)
+F5 Move highlight to previous charset
+F6 Move highlight to next charset
+F7 Previous page of charsets
+F8 Next page of charsets
+F9 Toggle choices when using ListMode
+F10 Turn on/off ListMode (makes it easy to choose multiple charsets)
+F11 Print
+F12 WhereIs
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigation General Alpine Commands
+----------------------- ---------------------
+ P Prev Charset ? Display this help text
+ N Next Charset E Exit without selecting anything
+ - Previous page % Print
+Spc (space bar) Next page
+ W WhereIs
+
+Select Command
+------------------------------------------------
+ S Select the highlighted charset (or chosen charsets in ListMode)
+ L Turn on ListMode (makes it easy to choose multiple charsets)
+ 1 Turn off ListMode
+ X Toggle choices when using ListMode
+</PRE>
+<!--chtml endif-->
+
+<H2>Description of the Select Character Set Screen</H2>
+
+This screen just gives you an easy way to select a character set or a list of
+character sets.
+The list of character sets presented is the list of all character sets known to
+Alpine.
+You may select other character sets by typing them in directly.
+
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_select_multkeyword_screen =====
+<HTML>
+<HEAD>
+<TITLE>SELECT KEYWORDS SCREEN</TITLE>
+</HEAD>
+<BODY>
+<H1>SELECT KEYWORDS SCREEN</H1>
+<H2>COMMANDS</H2>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Available Commands
+-------------------------------
+F1 Show Help Text
+F3 Exit without selecting anything
+F4 Select the highlighted keyword (or chosen keywords in ListMode)
+F5 Move highlight to previous keyword
+F6 Move highlight to next keyword
+F7 Previous page of keywords
+F8 Next page of keywords
+F9 Toggle choices when using ListMode
+F10 Turn on/off ListMode (makes it easy to choose multiple keywords)
+F11 Print
+F12 WhereIs
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigation General Alpine Commands
+----------------------- ---------------------
+ P Prev Keyword ? Display this help text
+ N Next Keyword E Exit without selecting anything
+ - Previous page % Print
+Spc (space bar) Next page
+ W WhereIs
+
+Select Command
+------------------------------------------------
+ S Select the highlighted keyword (or chosen keywords in ListMode)
+ L Turn on ListMode (makes it easy to choose multiple keywords)
+ 1 Turn off ListMode
+ X Toggle choices when using ListMode
+</PRE>
+<!--chtml endif-->
+
+<H2>Description of the Select Keyword Screen</H2>
+
+This screen just gives you an easy way to select a keyword or a list of
+keywords.
+The list of keywords presented is the list of all keywords defined in your
+<A HREF="h_config_keywords"><!--#echo var="VAR_keywords"--></A> option.
+If you have given a keyword a nickname, that nickname is displayed
+instead of the actual keyword.
+
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_select_incoming_to_monitor =====
+<HTML>
+<HEAD>
+<TITLE>SELECT FOLDERS TO CHECK SCREEN</TITLE>
+</HEAD>
+<BODY>
+<H1>SELECT FOLDERS TO CHECK SCREEN</H1>
+<H2>COMMANDS</H2>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Available Commands
+-------------------------------
+F1 Show Help Text
+F3 Exit without selecting anything
+F4 Select the marked folders
+F5 Move highlight to previous folder
+F6 Move highlight to next folder
+F7 Previous page of folders
+F8 Next page of folders
+F9 Toggle choices on or off
+F11 Print
+F12 WhereIs
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigation General Alpine Commands
+----------------------- ---------------------
+ P Prev Folder ? Display this help text
+ N Next Folder ^C exit without changing anything
+ - Previous page % Print
+Spc (space bar) Next page
+ W WhereIs
+
+Select Command
+------------------------------------------------
+ S Select the marked folders
+ X Toggle choices on or off
+</PRE>
+<!--chtml endif-->
+
+<H2>Description of the Select Folders to Check Screen</H2>
+
+This screen is only useful if the feature
+<A HREF="h_config_enable_incoming_checking"><!--#echo var="FEAT_enable-incoming-folders-checking"--></A>
+is set.
+By default, when you set that feature all of your incoming folders
+will be checked periodically for Unseen messages.
+By using this screen, you may restrict the set of monitored folders to
+a subset of all of the incoming folders.
+<P>
+Mark the folders you want to monitor for Unseen messages with
+an &quot;X&quot;.
+When you've finished marking all your selections use the Select
+command to register your choices.
+To return to the default of checking all incoming folders
+delete all folders or unmark all folders.
+
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_role_select =====
+<HTML>
+<HEAD>
+<TITLE>ROLES SCREEN</TITLE>
+</HEAD>
+<BODY>
+<H1>ROLES SCREEN</H1>
+<H2>ROLES COMMANDS</H2>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Available Commands
+-------------------------------
+F1 Show Help Text
+F3 Exit without a selection
+F4 Select a role to use in composition
+F5 Move to previous role
+F6 Move to next role
+F7 Previous page of roles
+F8 Next page of roles
+F11 Change Default Role
+F12 Whereis (search role nicknames)
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigation General Alpine Commands
+----------------------- ---------------------
+ P Prev Role ? Display this help text
+ N Next Role E Exit without a selection
+ - Previous page
+Spc (space bar) Next page
+ W WhereIs (search for word in role nicknames)
+
+Select Role Commands
+------------------------------------------------
+ [Return] Select highlighted role
+ D Change Default Role
+</PRE>
+<!--chtml endif-->
+
+<H2>Description of the Roles Screen</H2>
+
+With this screen you select a role to be used in the composition of a
+message.
+Use the Previous and Next commands to highlight the role you wish to
+use.
+When you type carriage return you will be placed in the composer using the highlighted role.
+<P>
+You don't have any non-default <A HREF="h_rules_roles">roles</A>
+available unless you set them up.
+You may do so by using the Setup/Rules command on the MAIN MENU.
+<P>
+By using the D command, you may set a default role that will persist until
+you change it or until you exit Alpine.
+The D command toggles through three states: set the default role, unset the
+default role, and leave the default role as it is.
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_role_abook_select =====
+<HTML>
+<HEAD>
+<TITLE>SELECT ADDRESS BOOK SCREEN</TITLE>
+</HEAD>
+<BODY>
+<H1>SELECT ADDRESS BOOK SCREEN</H1>
+<H2>COMMANDS</H2>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Available Commands
+-------------------------------
+F1 Show Help Text
+F3 Exit screen without selecting anything
+F4 Select highlighted address book
+F5 Move to previous address book
+F6 Move to next address book
+F7 Previous page of address books
+F8 Next page of address books
+F12 Whereis
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigation General Alpine Commands
+----------------------- ---------------------
+ P Previous addrbook ? Display this help text
+ N Next addrbook
+ - Previous page
+Spc (space bar) Next page
+ W WhereIs
+
+Select Role Commands
+------------------------------------------------
+ S Select highlighted address book
+ E Exit screen without selecting anything
+</PRE>
+<!--chtml endif-->
+
+<H2>Description of the Select Address Book Screen</H2>
+
+This screen helps you select one of your address books.
+Use the Previous and Next commands to highlight the address book you wish to
+select.
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======== h_rule_patterns =============
+<HTML>
+<HEAD>
+<TITLE>PATTERNS</TITLE>
+</HEAD>
+<BODY>
+<H1>PATTERNS</H1>
+Patterns are used with Roles, Filtering, Index Coloring,
+Scoring, Other Rules, and Search Rules.
+Patterns are compared with a message to see if there is a match.
+For Filtering, the messages being checked are all the messages in the
+folder, one at a time.
+For Index Line Coloring, each message that is visible on the screen is
+checked for matches with the Index Coloring Patterns.
+Roles are used with the Reply, Forward, and Compose commands.
+For Reply, the message used to compare the Pattern with is the message
+being replied to;
+for Forward, the message used to compare the Pattern with is the message
+being forwarded;
+and for Compose, there is no message, so the parts of the Pattern that depend
+on a message (everything other than Current Folder Type and the
+Beginning of Month and Year)
+are not used.
+Only the Current Folder Type matters for Compose (plus the Beginning of
+Month or Year, which you wouldn't usually use for a Role).
+For Scoring, the message being scored is compared with all of the Score
+Patterns, and the Score Values from the ones that match are added together to
+get the message's score.
+For Other Rules, there is no message. Only the Current Folder Type is checked
+for Other Rules.
+<P>
+Each Pattern has several possible parts, all of which are optional.
+In order for there to be a match, <EM>ALL</EM> of the
+<EM>defined</EM> parts of the Pattern must match the message.
+If a part is not defined it is considered a match, but note that a filtering
+Pattern must have at least one defined part or it will be ignored.
+For example, if the To pattern is not defined it will be
+displayed as
+<P>
+<CENTER>To pattern = &lt;No Value Set&gt;</CENTER>
+<P>
+That is considered a match because it is not defined.
+This means that the Pattern with nothing defined is a match if the
+Current Folder Type matches, but there is an exception that was mentioned
+in the previous paragraph.
+Because filtering is a potentially destructive action, filtering Patterns
+with nothing other than Current Folder Type defined are ignored.
+If you really want a filtering Pattern to match all messages (subject to
+Current Folder Type) the best way to do it is to define a Score interval
+that includes all possible scores.
+This would be the score interval <SAMP>(-INF,INF)</SAMP>.
+This can be used even if you haven't defined any rules to Set Scores.
+<P>
+There are six predefined header patterns called the To, From, Sender, Cc, News,
+and Subject patterns.
+Besides those six predefined header patterns, you may add
+additional header patterns with header fieldnames of your choosing.
+You add an extra header pattern by placing the cursor on one of the
+patterns while in the role editor and using the &quot;eXtraHdr&quot; command.
+The Recip pattern is a header pattern that stands for Recipient (To OR Cc)
+and the Partic pattern is a header pattern that stands for
+Participant (From OR To OR Cc).
+(Defining the Recip pattern does not have the same effect as defining both
+the To and Cc patterns. Recip is To <EM>OR</EM> Cc, not To <EM>AND</EM> Cc.)
+Similar to the header patterns are the AllText pattern and the BodyText pattern.
+Instead of comparing this pattern's text against only the contents of
+a particular header field, the text for the AllText pattern is compared
+with text anywhere in the message's header or body, and the text for the
+BodyText pattern is compared with text anywhere in the message's body.
+<P>
+Any of the header patterns, the AllText pattern, or the BodyText pattern may be negated with the
+&quot;!&quot; &quot;toggle NOT&quot; command.
+You can tell that <EM>NOT</EM> has been turned on by looking for the character
+&quot;!&quot; at the beginning of the pattern line.
+When the &quot;!&quot; is present, it reverses the meaning of the match.
+That is, if the pattern matches then it is considered to NOT be a match, and
+if it does not match it is considered to be a match.
+<P>
+Don't make the mistake of putting the &quot;!&quot; in the data field for
+a pattern.
+For example, if you type the characters &quot;!urgent&quot; into the Subject
+pattern, the pattern will look like:
+<P>
+<PRE>
+ Subject pattern = !urgent
+</PRE>
+<P>
+This means you want to match the 7 character sequence &quot;!urgent&quot;.
+In order to match messages that do not have &quot;urgent&quot; in
+their Subject field, first type the characters &quot;urgent&quot; followed
+by carriage return for the value of the Subject pattern, then negate it
+by typing the &quot;!&quot; command.
+It should look like
+<P>
+<PRE>
+ ! Subject pattern = urgent
+</PRE>
+<P>
+The contents of each of these header patterns (or the AllText or BodyText patterns) may
+be a complete email address, part of an address, or a random set of
+characters to match against.
+It may also be a list of such patterns, which means you
+are looking for a match against the first pattern in the list <EM>OR</EM>
+the second pattern <EM>OR</EM> the third and so on.
+For example, a Subject pattern equal to
+<P>
+<PRE>
+ Subject pattern = urgent
+ emergency
+ alert
+</PRE>
+<P>
+would match all messages with a subject that contained at least one
+of those words.
+It would also match subjects containing the words &quot;alerts&quot; or
+&quot;Urgently&quot;.
+<P>
+The same example with &quot;NOT&quot; turned on would be
+<P>
+<PRE>
+ ! Subject pattern = urgent
+ emergency
+ alert
+</PRE>
+<P>
+which would match all messages with a subject that did <EM>NOT</EM> contain any of
+those words.
+You can use the &quot;Add Value&quot; command to add new words to the list,
+or you can enter them as a comma-separated list.
+<P>
+(It is not possible to specify two patterns that must <EM>BOTH</EM> be
+present for a match.
+It is only possible to specify that <EM>EITHER</EM> pattern1 <EM>OR</EM>
+pattern2 must be present,
+and that is exactly what using a list does.)
+<P>
+The &quot;Current Folder Type&quot; and the &quot;Score Interval&quot; are
+also part of the Pattern, although the &quot;Score Interval&quot; is not used
+when checking for matches for Scoring.
+There are five similar settings that relate to the status of the message.
+These settings rely on the message being New or not, Deleted or not,
+Answered or not, Important or not, and Recent or not.
+There are also some other miscellaneous settings.
+The first is the Age of the message in days.
+Another is the Size of the message, in bytes.
+The third is a setting that detects whether or not the Subject of a
+message contains raw 8-bit characters (unencoded characters with the most
+significant bit set).
+There is a setting that detects whether or not this is the first time
+Alpine has been run this month (doesn't depend on individual messages),
+and another that detects whether or not this is the first time Alpine has
+been run this year.
+Other parts of the Pattern detect whether or not the From address of a
+message appears in your address book, whether or not certain keywords
+are set for a message, and whether or not certain character sets are
+used in a message.
+
+<H2>Parts of a Pattern</H2>
+
+<H3>Header patterns</H3>
+
+A header pattern is simply text that is searched for in the corresponding
+header field.
+For example, if a Pattern has a From header pattern with the value
+&quot;@company.com&quot;, then only messages that have a From header
+that contains the text &quot;@company.com&quot; will be possible
+matches.
+Matches don't have to be exact.
+For example, if the relevant field of a message contains the text
+&quot;mailbox@domain&quot; somewhere
+in it, then header patterns of &quot;box&quot;, or &quot;x@d&quot;, or
+&quot;mailbox@domain&quot; are all matches.
+<P>
+All parts of the Pattern must match so, for example,
+if a message matches a defined
+From pattern, it still must be checked against the other parts of the
+Pattern that have been defined.
+The To header pattern is a slightly special case.
+If the message being checked has a Resent-To header
+and the feature <A HREF="h_config_use_resentto"><!--#echo var="FEAT_use-resent-to-in-rules"--></A> is turned on, the addresses
+there are used in place of the addresses in the To header.
+This is only true for the To header.
+Resent-cc and Resent-From headers are never used unless you add them
+with the eXtraHdrs command.
+<P>
+The meaning of a header pattern may be negated with the
+&quot;!&quot; &quot;toggle NOT&quot; command.
+You can tell that <EM>NOT</EM> has been turned on by looking for the character
+&quot;!&quot; at the beginning of the pattern line.
+It would look something like
+<P>
+<PRE>
+ ! From pattern = susan@example.com
+</PRE>
+<P>
+When the &quot;!&quot; is present, it reverses the meaning of the match.
+<P>
+If you want to check for the presence of a header field but don't care
+about its value, then
+the empty pattern that you get by entering a pair of
+double quotes (&quot;&quot;) should match any message that
+has the corresponding header field.
+
+<H3><A NAME="pattern_alltext">AllText patterns</A></H3>
+
+AllText patterns are just like header patterns except that the text is
+searched for anywhere in the message's headers or body, not just in the
+contents of a particular header field.
+<P>
+
+<H3><A NAME="pattern_bodytext">BodyText patterns</A></H3>
+
+BodyText patterns are just like header patterns except that the text is
+searched for anywhere in the message's body, not just in the
+contents of a particular header field.
+<P>
+
+If there is more than one header pattern or AllText pattern or BodyText pattern
+for which you want to take the
+same action there is a shorthand notation that may be used.
+Any of these patterns may be a list of patterns instead of
+just a single pattern.
+If any one of the patterns in the list matches the message
+then it is considered a match.
+For example, if &quot;company1&quot; and &quot;company2&quot; both required
+you to use the same role when replying to messages, you might have
+a To pattern that looks like
+<P>
+<PRE>
+ To pattern = company1.com
+ company2.com
+</PRE>
+<P>
+This means that if the mail you are replying to was addressed to
+either &quot;anything@company1.com&quot; or &quot;anything@company2.com&quot;,
+then this Pattern is a match and the same actions will be taken.
+<P>
+The meaning of an AllText or BodyText pattern may be negated with the
+&quot;!&quot; &quot;toggle NOT&quot; command.
+You can tell that <EM>NOT</EM> has been turned on by looking for the character
+&quot;!&quot; at the beginning of the pattern line.
+When the &quot;!&quot; is present, it reverses the meaning of the match.
+<P>
+A technicality: Since comma is the character used to separate multiple values
+in any of the fields that may have multiple values (such as header patterns,
+AllText patterns, BodyText patterns, keywords, folder lists, and so on),
+you must escape comma with a
+backslash (&#92;) if you want to include a literal comma in one of those fields.
+In other words, if you type a backslash followed by a comma it will
+be interpreted as a comma by Alpine, instead of as a separator between
+pattern values.
+All other backslashes (those not followed by a comma) are literal
+backslashes and should not be escaped.
+It's unlikely you'll ever need to enter a literal comma or backslash in
+any of the patterns.
+
+<H3><A NAME="pattern_current_folder">Current Folder Type</A></H3>
+
+The &quot;Current Folder Type&quot; may be set to one of four different
+values: &quot;Any&quot;, &quot;News&quot;, &quot;Email&quot;, or
+&quot;Specific&quot;.
+If the value is set to &quot;News&quot;, then the
+Pattern will only match if the currently open folder is a newsgroup.
+The value &quot;Email&quot; only matches if the current folder is not news and
+the value &quot;Any&quot; causes any folder to match.
+If the value of &quot;Current Folder Type&quot; is set to &quot;Specific&quot;,
+then you must fill in a value for &quot;Folder&quot;, which is on the line
+below the &quot;Specific&quot; line.
+In this case you will only get a match if the currently open folder is
+the specific folder you list.
+You may give a list of folders instead of just a single
+folder name, in which case the Pattern will match if the open folder is
+any one of the folders in the list.
+The name of each folder in the list may be either &quot;INBOX&quot;,
+the technical specification
+of the folder (like what appears in your configuration file) or, if the
+folder is one of your incoming folders, it may be the nickname you've given
+the folder.
+Here are some samples of specific folder names:
+<P>
+<CENTER><SAMP>{monet.art.example.com}mail/art-class</SAMP></CENTER>
+<P>
+<CENTER><SAMP>{news.example.com/nntp}#news.comp.mail.pine</SAMP></CENTER>
+<P>
+<CENTER><SAMP>mail/local-folder</SAMP></CENTER>
+<P>
+The easiest way to fill in the &quot;Folder&quot; field is to use
+the &quot;T&quot; command that is available when the &quot;Folder&quot; line is
+highlighted, or to use the &quot;Take&quot; command with the configuration
+feature
+<A HREF="h_config_enable_role_take">&quot;<!--#echo var="FEAT_enable-rules-under-take"-->&quot;</A>
+turned on.
+<P>
+When reading a newsgroup, there may be a performance penalty
+incurred when collecting the information necessary to check whether
+or not a Pattern matches a message.
+For this reason, the default Current Folder Type is set to &quot;Email&quot;.
+If you have Patterns with a Current Folder Type of either
+&quot;Any&quot; or &quot;News&quot; and those Patterns are used for
+Index Line Coloring or Scoring, you may experience
+slower screen redrawing in the MESSAGE INDEX screen when in a newsgroup.
+
+<H3><A NAME="pattern_age_interval">Age Interval</A></H3>
+
+The &quot;Age Interval&quot; may be set to an interval of message
+ages that should be considered a match.
+Like the other parts of the Pattern, if it is unset it will be ignored.
+The Age Interval looks like
+<P>
+<CENTER><SAMP>(min_age,max_age)</SAMP></CENTER>
+<P>
+where &quot;min_age&quot; and &quot;max_age&quot; are integers greater
+than or equal to zero.
+The special value &quot;INF&quot; may be used for
+the max value. It represents infinity.
+<P>
+Actually, this option may be defined as a list of intervals instead
+of just a single interval.
+The list is separated by commas.
+It can look like
+<P>
+<CENTER><SAMP>(min_age1,max_age1),(min_age2,max_age2),...</SAMP></CENTER>
+<P>
+When there is an Age Interval defined, it is a match if the age, in days, of
+the message is contained in any of the intervals.
+The intervals include both endpoints.
+<P>
+Even though this option is called Age, it isn't actually
+the <EM>age</EM> of the message.
+Instead, it is how many days ago the message arrived in one of your folders.
+If the current time is a little past midnight, then a message that arrived
+just before midnight arrived yesterday, even though the message is only
+a few minutes old.
+By default, the date being used is not the date in the Date
+header of the message.
+It is the date that the message arrived in one of your folders.
+When you Save a message from one folder to another that arrival date
+is preserved.
+If you would like to use the date in the Date header that is possible.
+Turn on the option
+<A HREF="h_config_filt_opts_sentdate">&quot;Use-Date-Header-For-Age&quot;</A>
+near the bottom of the rule definition.
+A value of 0 is today, 1 is yesterday, 2 is the day before yesterday, and so on.
+
+<H3><A NAME="pattern_size_interval">Size Interval</A></H3>
+
+The &quot;Size Interval&quot; may be set to an interval of message
+sizes that should be considered a match.
+Like the other parts of the Pattern, if it is unset it will be ignored.
+The Size Interval looks like
+<P>
+<CENTER><SAMP>(min_size,max_size)</SAMP></CENTER>
+<P>
+where &quot;min_size&quot; and &quot;max_size&quot; are integers greater
+than or equal to zero.
+The special value &quot;INF&quot; may be used for
+the max value. It represents infinity.
+<P>
+Actually, this option may be defined as a list of intervals instead
+of just a single interval.
+The list is separated by commas.
+It can look like
+<P>
+<CENTER><SAMP>(min_size1,max_size1),(min_size2,max_size2),...</SAMP></CENTER>
+<P>
+When there is a Size Interval defined, it is a match if the size, in bytes, of
+the message is contained in any of the intervals.
+The intervals include both endpoints.
+
+<H3><A NAME="pattern_score_interval">Score Interval</A></H3>
+
+The &quot;Score Interval&quot; may be set to an interval of message
+scores that should be considered a match.
+Like the other parts of the Pattern, if it is unset it will be ignored.
+The Score Interval looks like
+<P>
+<CENTER><SAMP>(min_score,max_score)</SAMP></CENTER>
+<P>
+where &quot;min_score&quot; and &quot;max_score&quot; are positive or
+negative integers, with min_score less than or equal to max_score.
+The special values &quot;-INF&quot; and &quot;INF&quot; may be used for
+the min and max values to represent negative and positive infinity.
+<P>
+Actually, a list of intervals may be used if you wish.
+A list would look like
+<P>
+<CENTER><SAMP>(min_score1,max_score1),(min_score2,max_score2),...</SAMP></CENTER>
+<P>
+When there is a Score Interval defined, it is a match if the score for
+the message is contained in any of the intervals in the list.
+The intervals include the endpoints.
+The score for a message is calculated by looking at every Score rule defined and
+adding up the Score Values for the ones that match the message.
+When deciding whether or not a Pattern matches a message for purposes of
+calculating the score, the Score Interval is ignored.
+
+<H3><A NAME="pattern_message_status">Message Status</A></H3>
+
+There are five separate message status settings.
+By default, all five are set to the value &quot;Don't care&quot;, which
+will match any message.
+The value &quot;Yes&quot; means that the particular status must be true
+for a match, and the value &quot;No&quot; means that the particular
+status must not be true for a match.
+For example, one of the five Message Status settings is whether a message
+is marked Important or not.
+A &quot;Yes&quot; means that the message must be Important to be
+considered a match and &quot;No&quot; means that the message must not be
+Important to be considered a match.
+The same is true of the other four message status settings that depend
+on whether or not the message is New; whether the message has
+been Answered or not; whether the message has been Deleted or not, and
+whether the message is Recent or not.
+<P>
+The nomenclature for New and Recent is a bit confusing:
+<P>
+New means that the message is Unseen.
+It could have been in your mailbox for a long time but if you haven't looked
+at it, it is still considered New.
+That matches the default Alpine index display that shows an N for such a
+message.
+<P>
+Recent means that the message was added to this folder since the last time
+you opened the folder.
+Alpine also shows an N by default for these types of messages.
+If you were to run two copies of Alpine that opened a folder one right after
+the other, a message would only show up as Recent in (at most) the first
+Alpine session.
+
+<H3><A NAME="pattern_message_keywords">Message Keywords</A></H3>
+
+Keywords are similar to Message Status, but they are chosen by the user.
+Provided the mail server allows for it, you may add a set of possible keywords
+to a folder and then you may set those keywords or not for each message
+in the folder (see <A HREF="h_common_flag">Flag Command</A>).
+The syntax of this part of the Pattern is similar to the header patterns.
+It is a list of keywords.
+The Keyword part of the Pattern is a match if the message has any of
+the keywords in the list set.
+Like other parts of the Pattern, if this is unset it will be ignored.
+
+<H3><A NAME="pattern_message_charsets">Message Character Sets</A></H3>
+
+A message may use one or more character sets.
+This part of the Pattern matches messages that make use of one or more of
+the character sets specified in the pattern.
+It will be considered a match if a message uses any of the character
+sets in the list you give here.
+
+<P>
+Besides actual character set names (for example, ISO-8859-7, KOI8-R, or
+GB2312) you may also use some shorthand names that Alpine provides.
+These names are more understandable shorthand names for sets of
+character set names.
+Two examples are &quot;Cyrillic&quot; and &quot;Greek&quot;.
+Selecting one of these shorthand names is equivalent to selecting all of
+the character sets that make up the set.
+You can see all of these shorthand names and the lists of character sets
+they stand for by typing the &quot;T&quot; command with the Character
+Set pattern highlighted.
+The syntax of this part of the Pattern is similar to the header patterns
+and the Message Keywords pattern.
+It is a list of character sets (or shorthand names).
+The Character Set part of the Pattern is a match if the message uses any
+of the character sets in the list.
+Like other parts of the Pattern, if this is unset it will be ignored.
+
+<H3><A NAME="pattern_8bit_subject">Raw 8-bit in Subject</A></H3>
+
+It seems that lots of unwanted email contains unencoded 8-bit characters
+in the Subject.
+Normally, characters with the 8th bit set are not allowed in the Subject
+header unless they are MIME-encoded.
+This option gives you a way to match messages that have Subjects that
+contain unencoded 8-bit characters.
+By default, the value of this option is &quot;Don't care&quot;, which
+will match any message.
+The value &quot;Yes&quot; means that there must be raw 8-bit characters in
+the Subject of the message in order for there to be a match,
+and the value &quot;No&quot; is the opposite.
+Setting this option will affect performance in large folders because the
+subject of each message in the folder has to be checked.
+
+<H3><A NAME="pattern_bom">Beginning of Month</A></H3>
+
+This option gives you a way to take some action once per month.
+By default, the value of this option is &quot;Don't care&quot;, which
+will always match.
+The value &quot;Yes&quot; means that this must be the first time Alpine has
+been run this month in order to count as a match,
+and the value &quot;No&quot; is the opposite.
+
+<H3><A NAME="pattern_boy">Beginning of Year</A></H3>
+
+This option gives you a way to take some action once per year.
+By default, the value of this option is &quot;Don't care&quot;, which
+will always match.
+The value &quot;Yes&quot; means that this must be the first time Alpine has
+been run this year in order to count as a match,
+and the value &quot;No&quot; is the opposite.
+
+<H3><A NAME="pattern_abookfrom">Address in Address Books</A></H3>
+
+This option gives you a way to match messages that have an address
+that is in one of your address books.
+Only the simple entries in your address books are searched.
+Address book distribution lists are ignored!
+By default, the value of this option is &quot;Don't care&quot;, which
+will match any message.
+The value &quot;Yes, in any address book&quot; means the address
+from the message must be in at least one of your
+address books in order to be a match.
+The value &quot;No, not in any address book&quot;
+means none of the addresses may
+be in any of your address books in order to be a match.
+The values &quot;Yes, in specific address books&quot; and
+&quot;No, not in any of specific address books&quot; are similar but instead
+of depending on all address books you are allowed to give a list of address
+books to look in.
+The addresses from the message that are checked for are determined by the
+setting you have for &quot;Types of addresses to check for in address book&quot;.
+If you set this to &quot;From&quot; the From address from the message will
+be looked up in the address book.
+If you set it to only &quot;To&quot; then the To addresses will be used.
+If any of the To addresses are in the address book then it is considered
+a match for &quot;Yes&quot; or not a match for &quot;No&quot;.
+You could set it to both From and To, in which case all of the From and To
+addresses are used.
+The &quot;Reply-To&quot; and &quot;Sender&quot; cases are a little unusual.
+Due to deficiencies in our tools, Reply-To uses the Reply-To address if it
+exists or the From address if there is no Reply-To address.
+Same for the Sender address.
+Setting this option may affect performance in large folders because the
+From and Reply-To of each message in the folder have to be checked.
+
+<H3><A NAME="pattern_categorizer">Categorizer Command</A></H3>
+
+This is a command that is run with its standard input set to the message
+being checked and its standard output discarded.
+The full directory path should be specified.
+The command will be run and then its exit status will be checked against
+the Exit Status Interval, which defaults to just the value zero.
+If the exit status of the command falls in the interval, it is considered
+a match, otherwise it is not a match.
+<P>
+
+This option may actually be a list of commands.
+The first one that exists and is executable is used.
+That makes it possible to use the same configuration with Unix Alpine and
+PC-Alpine.
+<P>
+
+If none of the commands in the list exists and is executable then the rule
+is <EM>not</EM> a match.
+If it is possible that the command may not exist, you should be careful
+to structure your rules so that nothing destructive
+happens when the command does not exist.
+For example, you might have a filter that filters away spam when there is
+a match but does nothing when there is not a match.
+That would continue to work correctly if the command didn't exist.
+However, if you have a filter that filters away spam when there is not
+a match and keeps it when there is a match, that would filter everything
+if the categorizer command didn't exist.
+<P>
+Here is an <A HREF="h_config_role_cat_cmd_example">example</A>
+setup for the bogofilter filter.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_rules_roles =====
+<HTML>
+<HEAD>
+<TITLE>SETUP ROLES SCREEN</TITLE>
+</HEAD>
+<BODY>
+<H1>SETUP ROLES SCREEN</H1>
+<H2>SETUP ROLES COMMANDS</H2>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Available Commands -- Group 1 Available Commands -- Group 2
+------------------------------- ------------------------------
+F1 Show Help Text F1 Show Help Text
+F2 See commands in next group F2 See commands in next group
+F3 Back to MAIN Alpine menu
+F4 Change configuration for role
+F5 Move to previous role F5 Include file in role config
+F6 Move to next role F6 Exclude file from config
+F7 Previous page of roles
+F8 Next page of roles
+F9 Add new role F9 Replicate existing role
+F10 Delete existing role
+F11 Shuffle the order of roles
+F12 Whereis (search role nicknames)
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigation General Alpine Commands
+----------------------- ---------------------
+ P Prev Role ? Display this help text
+ N Next Role E Back to MAIN Alpine menu
+ - Previous page
+Spc (space bar) Next page
+ W WhereIs (search for word in role nicknames)
+
+Setup Roles Commands
+------------------------------------------------
+ A Add new role $ Shuffle the order of roles
+ D Delete existing role C Change configuration for highlighted role
+ R Replicate existing role
+ I Include file in role config X Exclude file from role config
+</PRE>
+<!--chtml endif-->
+
+<H2>Description of the Setup Roles Screen</H2>
+
+This screen lets you add, delete, modify, or change the order of the rules
+that determine the role you are playing when composing a message.
+<P>
+You may play different roles depending on who you are replying to.
+For example, if you are replying to a message addressed to help-desk you
+may be acting as a Help Desk Worker.
+That role may require that you use a different return address and/or
+a different signature.
+<P>
+Roles are optional.
+If you set up roles they work like this: Each role has a set of
+&quot;Uses&quot;, which indicate whether or not a role is eligible to be
+considered for a particular use; a &quot;Pattern&quot;,
+which is used to decide which of the eligible roles is used; and a set
+of &quot;Actions&quot;, which are taken when that role is used.
+When you reply to a message, the message you are replying to is compared
+with the Patterns of the roles marked as eligible for use when replying.
+The comparisons start with the first eligible role and keep going until there
+is a match.
+If a match is found, the matching role's Actions are taken.
+<P>
+It is also possible to set a default role and to change that role during
+your Alpine session.
+When you start Alpine no default role will be set.
+You may set or change the current default role by using the &quot;D&quot;
+command in the role selection screen.
+You'll see that screen while composing a message and being asked to select
+a role.
+An easy way to get to that screen is to use the <A HREF="h_common_role">Role Command</A> to
+compose a message.
+You may find a default role useful if you normally perform the duties of one
+of your roles for a while, then you switch to another role and stay in the
+new role for another period of time.
+It may be easier than using the Role Command to select the role each time you
+compose a message.
+
+<H2>Role Uses</H2>
+
+There are three types of use to be configured;
+one for Replying, one for Forwarding, and one for Composing.
+These indicate whether or not you want a role to be considered when you
+type the Reply, Forward, or Compose commands.
+(The Role command is an alternate form of the Compose command, and it is
+not affected by these settings.)
+Each of these Use types has three possible values.
+The value &quot;Never&quot;
+means that the role will never be considered as a candidate for use with
+the corresponding command.
+For example, if you set a role's Reply Use to Never, then when you Reply to
+a message, that role won't even be considered.
+(That isn't quite true. If the message you are replying to matches some other
+role that requires confirmation,
+then there will be a ^T command available which allows you to select a role
+from all of your roles, not just the reply-eligible roles.)
+<P>
+
+The options &quot;With confirmation&quot; and &quot;Without confirmation&quot;
+both mean that you do want to consider this role when using the corresponding
+command.
+For either of these settings the role's Pattern will
+be checked to see if it matches the message.
+For Reply Use, the message used to compare the Pattern with is the message
+being replied to.
+For Forward Use, the message used to compare the Pattern with is the message
+being forwarded.
+For Compose Use, there is no message, so the parts of the Pattern that depend
+on a message (everything other than Current Folder Type) are ignored.
+In all cases, the Current Folder Type is checked if defined.
+If there is a match then this role will either be used without confirmation
+or will be the default when confirmation is asked for, depending on
+which of the two options is selected.
+If confirmation is requested, you will have a chance to
+choose No Role instead of the offered role, or to
+change the role to any one of your other roles (with the ^T command).
+
+<H2>Role Patterns</H2>
+
+In order to determine whether or not a message matches a role the message is
+compared with the Role's Pattern.
+These Patterns are the same for use with Roles, Filtering, Index Coloring,
+Scoring, Other Rules, and Search Rules, so are described in only one place,
+&quot;<A HREF="h_rule_patterns">here</A>&quot;.
+<P>
+Since header patterns, AllText patterns, and BodyText patterns that are unset are ignored,
+a role that has all header patterns unset, the AllText pattern unset,
+the BodyText pattern unset,
+the Score Interval unset, and the Current Folder Type set to
+&quot;Any&quot; may be used as a default role.
+It should be put last in the list of roles since the matching
+starts at the beginning and proceeds until one of the roles is a match.
+If no roles at all match, then Alpine will
+use its regular methods of defining the role.
+If you wanted to, you could define a different &quot;default&quot; role
+for Replying, Forwarding, and Composing by setting the
+&quot;Use&quot; fields appropriately.
+
+<H2>Role Actions</H2>
+
+Once a role match is found, the role's Actions are taken.
+For each role there are several possible actions that may be defined.
+They are actions to set the From address, the Reply-To address,
+the Fcc, the Signature, the Template file, and Other Headers.
+
+<H3>Set From</H3>
+
+The From address is the address used on the From line of the message
+you are sending.
+
+<H3>Set Reply-To</H3>
+
+The Reply-To address is the address used on the Reply-To line of the message
+you are sending.
+You don't need a Reply-To address unless it is different from the From address.
+
+<H3>Set Other Headers</H3>
+
+If you want to set the value of the From or Reply-To headers, use
+the specific fields &quot;Set From&quot; or &quot;Set Reply-To&quot;.
+If you want to set the values of other headers, use this field.
+This field is similar to the
+<A HREF="h_config_custom_hdrs"><!--#echo var="VAR_customized-hdrs"--></A> configuration option.
+Each header you specify here must include the header tag
+(&quot;To:&quot;, &quot;Approved:&quot;, etc.)
+and may optionally include a value for that header.
+It is different from the <!--#echo var="VAR_customized-hdrs"--> in that the value you give
+for a header here will replace any value that already exists.
+For example, if you are Replying to a message there will be at least one
+address in the To header (the address you are Replying to).
+However, if you Reply using a role that sets the To header, that role's
+To header value will be used instead.
+
+<H3>Set Fcc</H3>
+
+The Fcc is used as the Fcc for the message you are sending.
+
+<H3>Set Signature or Set LiteralSig</H3>
+
+The Signature is the name of a file to be used as the signature file when
+this role is being used.
+If the name of the file has a vertical bar following it (|)
+then it is assumed that the file is a program that should be run to
+produce the signature.
+If the LiteralSig is set, then it is used instead of the signature file.
+LiteralSig is just a different way to store the signature.
+It is stored in the pine configuration file instead of in a separate
+signature file.
+If the <A HREF="h_config_literal_sig"><!--#echo var="VAR_literal-signature"--></A> option is defined
+either in the role or as the default signature in the Setup/Config screen,
+then the signature file is ignored.
+
+<H3>Set Template</H3>
+
+A Template is the name of a file to be included in the message when this
+role is being used.
+If the name of the file has a vertical bar following it (|)
+then it is assumed that the file is a program that should be run to
+produce the template.
+
+<P>
+Both signature files and template files may be stored remotely on an IMAP
+server.
+In order to do that you just give the file a remote name.
+This works just like the regular
+<A HREF="h_config_signature_file"><!--#echo var="VAR_signature-file"--></A>
+option that is configured from the Setup/Configuration screen.
+A remote signature file name might look like:
+<P>
+<CENTER><SAMP>{myimaphost.myschool.k12.wa.us}mail/sig3</SAMP></CENTER>
+<P>
+Once you have named the remote signature or template file you create its
+contents by using the &quot;F&quot; &quot;editFile&quot; command when the
+cursor is on the &quot;Set Signature&quot; or &quot;Set Template&quot;
+line of the role editor.
+
+<P>
+Both signature files and template files (or the output of signature programs
+and template file programs) may contain special tokens
+that are replaced with contents
+that depend on the message being replied to or forwarded.
+See the help for the individual fields inside the role editor for more
+information on tokens.
+
+<H3>Use SMTP Server</H3>
+
+If this field has a value, then it will be used as the SMTP server
+to send mail when this role is being used (unless the SMTP server variable
+is set in the system-wide fixed configuration file).
+It has the same semantics as the
+<A HREF="h_config_smtp_server">&quot;<!--#echo var="VAR_smtp-server"-->&quot;</A>
+variable in the Setup/Config screen.
+When you postpone the composition this SMTP server list will be saved
+with the postponed composition and it cannot be changed later.
+Because of that, you may want to make this a list of SMTP servers
+with the preferred server at the front of the list and alternate servers
+later in the list.
+
+<P>
+If any of the actions are left unset, then the action depends on what
+is present in the &quot;Initialize settings using role&quot; field.
+If you've listed the nickname of another one of your roles there, then the
+corresponding action from that role will be used here.
+If that action is also blank, or if there is no nickname specified,
+then Alpine will do whatever it normally does to set these actions.
+This depends on other configuration options and features you've set.
+
+<H2>Command Descriptions</H2>
+
+<H3>Add</H3>
+
+The Add command is used to add a new role definition to your set of
+roles.
+The new role will be added after the highlighted role.
+
+<H3>Delete</H3>
+
+The Delete command deletes the currently highlighted role.
+
+<H3>Change</H3>
+
+The Change command lets you edit the nickname, Uses, Pattern,
+and Actions of the currently highlighted role.
+
+<H3>Shuffle</H3>
+
+The Shuffle command allows you to change the order of the roles.
+You may move the currently highlighted role up or down in the list.
+The order of the roles is important since the roles are tested for a
+match starting with the first role and continuing until a match is found.
+You should place the roles with more specific Patterns near the beginning
+of the list, and those with more general Patterns near the end so that
+the more specific matches will happen when appropriate.
+
+<H3>Replicate</H3>
+
+The Replicate command is used to copy an existing role and modify it.
+The new role will be added after the highlighted role.
+
+<H3>IncludeFile</H3>
+
+The IncludeFile command allows you to add a roles file to your configuration.
+Usually, your roles will be contained in your Alpine configuration file.
+If you wish, some or all of your roles may be stored in a separate file.
+If a roles file already exists (maybe it was made by somebody else using
+Alpine), you may insert it before the currently highlighted role.
+You may also insert an empty file or a file that does not yet exist.
+Once you have an empty roles file in your configuration, you may use
+the Shuffle command to move roles into it.
+In fact, that's the only way to get the initial role into the file.
+
+<H3>eXcludeFile</H3>
+
+The eXcludeFile command removes a roles file from your roles configuration.
+A limitation of the program is that in order to exclude a roles file
+that file must have at least one role
+in it, otherwise you won't be able to highlight a line in the file.
+So you may have to add a dummy role to the file in order to exclude the file.
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_rules_other =====
+<HTML>
+<HEAD>
+<TITLE>SETUP OTHER RULES SCREEN</TITLE>
+</HEAD>
+<BODY>
+<H1>SETUP OTHER RULES SCREEN</H1>
+<H2>SETUP OTHER RULES COMMANDS</H2>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Available Commands -- Group 1 Available Commands -- Group 2
+------------------------------- ------------------------------
+F1 Show Help Text F1 Show Help Text
+F2 See commands in next group F2 See commands in next group
+F3 Back to MAIN Alpine menu
+F4 Change configuration for rule
+F5 Move to previous rule F5 Include file in rule config
+F6 Move to next rule F6 Exclude file from config
+F7 Previous page of rules
+F8 Next page of rules
+F9 Add new rule F9 Replicate existing rule
+F10 Delete existing rule
+F11 Shuffle the order of rules
+F12 Whereis (search rule nicknames)
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigation General Alpine Commands
+----------------------- ---------------------
+ P Prev rule ? Display this help text
+ N Next rule E Back to MAIN Alpine menu
+ - Previous page
+Spc (space bar) Next page
+ W WhereIs (search for word in rule nicknames)
+
+Setup Other Rules Commands
+------------------------------------------------
+ A Add new rule $ Shuffle the order of rules
+ D Delete existing rule C Change configuration for highlighted rule
+ R Replicate existing rule
+ I Include file in rule config X Exclude file from rule config
+</PRE>
+<!--chtml endif-->
+
+<H2>Description of the Setup Other Rules Screen</H2>
+
+This is where you may set various actions that do not fit well into the
+other Rules categories.
+
+<H2>Patterns</H2>
+
+Other Rules are a little different from the rest of the Rules because
+they depend only on the current folder, and not on a particular message.
+In order to determine whether or not a rule's actions should be applied
+the current folder is compared with the rule's Pattern, which consists
+of only the Current Folder Type.
+Current Folder Type works the same for Other Rules as it does for Roles,
+Filtering, Index Coloring, and Scoring.
+Keep in mind that the only part of the Pattern that applies to Other
+Rules is the Current Folder Type when looking at the description of
+Patterns given
+&quot;<A HREF="h_rule_patterns">here</A>&quot;.
+
+<H2>The Actions</H2>
+
+<H3>Set Sort Order</H3>
+
+When you enter a new folder, these rules will be checked to see if you
+have set a sort order that is different from your default sort order.
+The default is set in the Setup/Config screen with
+the &quot;<A HREF="h_config_sort_key"><!--#echo var="VAR_sort-key"--></A>&quot; option.
+If the Sort Order action is set, then the folder will be displayed sorted in
+that sort order instead of in the default order.
+<P>
+A possible point of confusion arises when you change the configuration
+of the Sort Order for the currently open folder.
+The folder will normally be re-sorted when you go back to viewing the
+index.
+However, if you have manually sorted the folder with the
+Sort
+(<!--chtml if pinemode="function_key"-->F7<!--chtml else-->$<!--chtml endif-->)
+command, then it will not be re-sorted until the next time it is opened.
+
+<H3>Set Index Format</H3>
+
+When you enter a new folder, these rules will be checked to see if you
+have set an Index Format that is different from your default Index Format,
+which is set with the
+<A HREF="h_config_index_format">&quot;<!--#echo var="VAR_index-format"-->&quot;</A> option.
+If so, the index will be displayed with this format instead of the default.
+
+<H3>Set Startup Rule</H3>
+
+When you enter a new folder, these rules will be checked to see if you
+have set a startup rule that is different from the default startup rule.
+The default for incoming folders is set in the Setup/Config screen with
+the &quot;<!--#echo var="VAR_incoming-startup-rule"-->&quot; option.
+The default for folders other than INBOX that are not part of your
+incoming collection
+(see <A HREF="h_config_enable_incoming"><!--#echo var="FEAT_enable-incoming-folders"--></A> feature)
+is to start with the last message in the folder.
+If the Startup Rule is set to something other than &quot;default&quot;,
+then the rule will determine which message will be the current message when
+the folder is first opened.
+<P>
+The various startup rule possibilities work the same here as they do in
+the incoming collection, so check
+<A HREF="h_config_inc_startup"><!--#echo var="VAR_incoming-startup-rule"--></A>
+for more help.
+
+<H2>Command Descriptions</H2>
+
+<H3>Add</H3>
+
+The Add command is used to add a new rule definition to your set of
+rules.
+The new rule will be added after the highlighted rule.
+
+<H3>Delete</H3>
+
+The Delete command deletes the currently highlighted rule.
+
+<H3>Change</H3>
+
+The Change command lets you edit the nickname, Pattern,
+and Action of the currently highlighted rule.
+
+<H3>Shuffle</H3>
+
+The Shuffle command allows you to change the order of the rules.
+You may move the currently highlighted rule up or down in the list.
+The order of the rules is important since the rules are tested for a
+match starting with the first rule and continuing until a match is found.
+You should place the rules with more specific Patterns near the beginning
+of the list, and those with more general Patterns near the end so that
+the more specific matches will happen when appropriate.
+
+<H3>Replicate</H3>
+
+The Replicate command is used to copy an existing rule definition and modify it.
+The new rule will be added after the highlighted rule.
+
+<H3>IncludeFile</H3>
+
+The IncludeFile command allows you to add a rules file to your configuration.
+Usually, your rules will be contained in your Alpine configuration file.
+If you wish, some or all of your rules may be stored in a separate file.
+If a rules file already exists (maybe it was made by somebody else using
+Alpine), you may insert it before the currently highlighted rule.
+You may also insert an empty file or a file that does not yet exist.
+Once you have an empty rules file in your configuration, you may use
+the Shuffle command to move rules into it.
+In fact, that's the only way to get the initial rule into the file.
+
+<H3>eXcludeFile</H3>
+
+The eXcludeFile command removes a rules file from your rules configuration.
+A limitation of the program is that in order to exclude a rules file
+that file must have at least one rule
+in it, otherwise you won't be able to highlight a line in the file.
+So you may have to add a dummy rule to the file in order to exclude the file.
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_rules_srch =====
+<HTML>
+<HEAD>
+<TITLE>SETUP SEARCH RULES SCREEN</TITLE>
+</HEAD>
+<BODY>
+<H1>SETUP SEARCH RULES SCREEN</H1>
+<H2>SETUP SEARCH RULES COMMANDS</H2>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Available Commands -- Group 1 Available Commands -- Group 2
+------------------------------- ------------------------------
+F1 Show Help Text F1 Show Help Text
+F2 See commands in next group F2 See commands in next group
+F3 Back to MAIN Alpine menu
+F4 Change configuration for rule
+F5 Move to previous rule F5 Include file in rule config
+F6 Move to next rule F6 Exclude file from config
+F7 Previous page of rules
+F8 Next page of rules
+F9 Add new rule F9 Replicate existing rule
+F10 Delete existing rule
+F11 Shuffle the order of rules
+F12 Whereis (search rule nicknames)
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigation General Alpine Commands
+----------------------- ---------------------
+ P Prev rule ? Display this help text
+ N Next rule E Back to MAIN Alpine menu
+ - Previous page
+Spc (space bar) Next page
+ W WhereIs (search for word in rule nicknames)
+
+Setup Search Rules Commands
+------------------------------------------------
+ A Add new rule $ Shuffle the order of rules
+ D Delete existing rule C Change configuration for highlighted rule
+ R Replicate existing rule
+ I Include file in rule config X Exclude file from rule config
+</PRE>
+<!--chtml endif-->
+
+<H2>Description of the Setup Search Rules Screen</H2>
+
+One of the commands that becomes available when that feature is turned on
+is the &quot;; Select&quot; command, which is used in the MESSAGE INDEX
+screen to select a set of messages.
+One way of selecting messages is to use a Rule.
+All of the messages that match (or don't match if you wish)
+a Rule's Pattern will be selected.
+<P>
+Any of your Rules may be used for this purpose.
+You might already have Rules set up for filtering, index line color, scores, or roles;
+and you may use any of those Rules with the Select command.
+However, you might find it more convenient to set up a separate set of Rules
+just for this purpose without having to worry about what other effects
+they may cause.
+That is the purpose of these Select Rules.
+
+<P>
+Each rule has a &quot;Pattern&quot;
+that is used to decide which messages are selected when you use it with
+the Select command.
+
+<H2>Patterns</H2>
+
+In order to determine whether or not a message should be selected
+the message is compared with the rule's Pattern.
+These Patterns are the same for use with Roles, Filtering, Index Coloring,
+Scoring, Other Rules, and Search Rules, so are described in only one place,
+&quot;<A HREF="h_rule_patterns">here</A>&quot;.
+
+<H2>Command Descriptions</H2>
+
+<H3>Add</H3>
+
+The Add command is used to add a new rule definition to your set of
+rules.
+The new rule will be added after the highlighted rule.
+
+<H3>Delete</H3>
+
+The Delete command deletes the currently highlighted rule.
+
+<H3>Change</H3>
+
+The Change command lets you edit the nickname and Pattern
+of the currently highlighted rule.
+
+<H3>Shuffle</H3>
+
+The Shuffle command allows you to change the order of the rules.
+This affects only the order they are presented in when you use the
+^T subcommand of the Select by Rule command.
+You may move the currently highlighted rule up or down in the list.
+
+<H3>Replicate</H3>
+
+The Replicate command is used to copy an existing rule definition and modify it.
+The new rule will be added after the highlighted rule.
+
+<H3>IncludeFile</H3>
+
+The IncludeFile command allows you to add a rules file to your configuration.
+Usually, your rules will be contained in your Alpine configuration file.
+If you wish, some or all of your rules may be stored in a separate file.
+If a rules file already exists (maybe it was made by somebody else using
+Alpine), you may insert it before the currently highlighted rule.
+You may also insert an empty file or a file that does not yet exist.
+Once you have an empty rules file in your configuration, you may use
+the Shuffle command to move rules into it.
+In fact, that's the only way to get the initial rule into the file.
+
+<H3>eXcludeFile</H3>
+
+The eXcludeFile command removes a rules file from your rules configuration.
+A limitation of the program is that in order to exclude a rules file
+that file must have at least one rule
+in it, otherwise you won't be able to highlight a line in the file.
+So you may have to add a dummy rule to the file in order to exclude the file.
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_rules_incols =====
+<HTML>
+<HEAD>
+<TITLE>SETUP INDEX LINE COLORS SCREEN</TITLE>
+</HEAD>
+<BODY>
+<H1>SETUP INDEX LINE COLORS SCREEN</H1>
+<H2>SETUP INDEX LINE COLORS COMMANDS</H2>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Available Commands -- Group 1 Available Commands -- Group 2
+------------------------------- ------------------------------
+F1 Show Help Text F1 Show Help Text
+F2 See commands in next group F2 See commands in next group
+F3 Back to MAIN Alpine menu
+F4 Change configuration for rule
+F5 Move to previous rule F5 Include file in rule config
+F6 Move to next rule F6 Exclude file from config
+F7 Previous page of rules
+F8 Next page of rules
+F9 Add new rule F9 Replicate existing rule
+F10 Delete existing rule
+F11 Shuffle the order of rules
+F12 Whereis (search rule nicknames)
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigation General Alpine Commands
+----------------------- ---------------------
+ P Prev rule ? Display this help text
+ N Next rule E Back to MAIN Alpine menu
+ - Previous page
+Spc (space bar) Next page
+ W WhereIs (search for word in rule nicknames)
+
+Setup Index Color Commands
+------------------------------------------------
+ A Add new rule $ Shuffle the order of rules
+ D Delete existing rule C Change configuration for highlighted rule
+ R Replicate existing rule
+ I Include file in rule config X Exclude file from rule config
+</PRE>
+<!--chtml endif-->
+
+<H2>Description of the Setup Index Line Colors Screen</H2>
+
+Index Line Color causes lines in the MESSAGE INDEX screen to be colored.
+This action is only available if your terminal is capable of displaying
+color and color display has been enabled with the
+<A HREF="h_config_color_style">Color Style</A> option within the
+Setup Color screen.
+(In PC-Alpine, color is always enabled so there is no option to turn on.)
+This screen lets you add, delete, modify, or change the order of the rules
+that cause the lines in the MESSAGE INDEX to be displayed in different
+colors.
+<P>
+Each rule has a &quot;Pattern&quot;,
+which is used to decide which of the rules is used; and the color that
+is used if the Pattern matches a particular message.
+
+<H2>Index Color Patterns</H2>
+
+In order to determine whether or not a message matches an Index Color Rule
+the message is compared with the rule's Pattern.
+These Patterns are the same for use with Roles, Filtering, Index Coloring,
+Scoring, Other Rules, and Search Rules, so are described in only one place,
+&quot;<A HREF="h_rule_patterns">here</A>&quot;.
+
+<P>
+If none of the Index Color rules is a match for a particular index line,
+then the color used is set using
+the <A HREF="h_color_setup">Setup Kolor</A> screen.
+
+<H2>Index Line Color</H2>
+
+This is the color that index lines are colored when there is a matching
+Pattern.
+This colors the whole index line, except possibly the status letters,
+which may be colored separately using
+the <A HREF="h_color_setup">Setup Kolor</A> screen.
+
+<H2>Command Descriptions</H2>
+
+<H3>Add</H3>
+
+The Add command is used to add a new rule definition to your set of
+rules.
+The new rule will be added after the highlighted rule.
+
+<H3>Delete</H3>
+
+The Delete command deletes the currently highlighted rule.
+
+<H3>Change</H3>
+
+The Change command lets you edit the nickname, Pattern,
+and Index Line Color of the currently highlighted rule.
+
+<H3>Shuffle</H3>
+
+The Shuffle command allows you to change the order of the rules.
+You may move the currently highlighted rule up or down in the list.
+The order of the rules is important since the rules are tested for a
+match starting with the first rule and continuing until a match is found.
+You should place the rules with more specific Patterns near the beginning
+of the list, and those with more general Patterns near the end so that
+the more specific matches will happen when appropriate.
+
+<H3>Replicate</H3>
+
+The Replicate command is used to copy an existing rule definition and modify it.
+The new rule will be added after the highlighted rule.
+
+<H3>IncludeFile</H3>
+
+The IncludeFile command allows you to add a rules file to your configuration.
+Usually, your rules will be contained in your Alpine configuration file.
+If you wish, some or all of your rules may be stored in a separate file.
+If a rules file already exists (maybe it was made by somebody else using
+Alpine), you may insert it before the currently highlighted rule.
+You may also insert an empty file or a file that does not yet exist.
+Once you have an empty rules file in your configuration, you may use
+the Shuffle command to move rules into it.
+In fact, that's the only way to get the initial rule into the file.
+
+<H3>eXcludeFile</H3>
+
+The eXcludeFile command removes a rules file from your rules configuration.
+A limitation of the program is that in order to exclude a rules file
+that file must have at least one rule
+in it, otherwise you won't be able to highlight a line in the file.
+So you may have to add a dummy rule to the file in order to exclude the file.
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_rules_filter =====
+<HTML>
+<HEAD>
+<TITLE>SETUP FILTERING SCREEN</TITLE>
+</HEAD>
+<BODY>
+<H1>SETUP FILTERING SCREEN</H1>
+<H2>SETUP FILTERING COMMANDS</H2>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Available Commands -- Group 1 Available Commands -- Group 2
+------------------------------- ------------------------------
+F1 Show Help Text F1 Show Help Text
+F2 See commands in next group F2 See commands in next group
+F3 Back to MAIN Alpine menu
+F4 Change configuration for filter
+F5 Move to previous filter F5 Include file in filter config
+F6 Move to next filter F6 Exclude file from config
+F7 Previous page of filters
+F8 Next page of filters
+F9 Add new filter F9 Replicate existing filter
+F10 Delete existing filter
+F11 Shuffle the order of filters
+F12 Whereis (search filter nicknames)
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigation General Alpine Commands
+----------------------- ---------------------
+ P Prev Filter ? Display this help text
+ N Next Filter E Back to MAIN Alpine menu
+ - Previous page
+Spc (space bar) Next page
+ W WhereIs (search for word in filter nicknames)
+
+Setup Filters Commands
+------------------------------------------------
+ A Add new filter $ Shuffle the order of filters
+ D Delete existing filter C Change configuration for highlighted filter
+ R Replicate existing filter
+ I Include file in filter config X Exclude file from filter config
+</PRE>
+<!--chtml endif-->
+
+<H2>Description of the Setup Filtering Screen</H2>
+
+This screen lets you add, delete, modify, or change the order of the rules
+that determine the filtering Alpine does on folders you view.
+<P>
+The software that actually delivers mail (the stuff that happens
+before Alpine is involved) for you is in a better position to do mail filtering
+than Alpine itself.
+If possible, you may want to look into using that sort of mail filtering to
+deliver mail to different folders, delete it, or forward it.
+However, if you'd like Alpine to help with this, Alpine's filtering is for you.
+<P>
+Filtering is a way to automatically move certain messages from one folder
+to another or to delete messages.
+It can also be used to set message status (Important, Deleted, New,
+Answered) and to set <A HREF="h_config_keywords"><!--#echo var="VAR_keywords"--></A> for messages.
+Alpine doesn't have the ability to forward mail to another address or
+to deliver vacation messages.
+<P>
+Each filtering rule has a &quot;Pattern&quot; and a &quot;Filter Action&quot;.
+When a folder is opened, when new mail arrives in an open folder, or
+when mail is Expunged from a folder; each
+message is compared with the Patterns of your filtering rules.
+The comparisons start with the first rule and keep going until there
+is a match.
+If a match is found, the message may be deleted or moved, depending on
+the setting of the Filter Action.
+If the message is not deleted, it may have its status altered.
+
+<P>
+<EM>NOTE:</EM>
+When setting up a Pattern used to delete messages,
+it is recommended that you test the Pattern first with a &quot;Move&quot;
+folder specified in
+case unintended matches occur. Messages that are deleted will be removed
+from the folder and <EM>unrecoverable</EM> from within Alpine after the
+next Expunge command or once the folder being filtered has been closed.
+
+<H2>Filter Patterns</H2>
+
+In order to determine whether or not a message matches a filter the message is
+compared with the Filter's Pattern.
+These Patterns are the same for use with Roles, Filtering, Index Coloring,
+Scoring, Other Rules, and Search Rules, so are described in only one place,
+&quot;<A HREF="h_rule_patterns">here</A>&quot;.
+<P>
+Since filtering is a potentially destructive action, if you have a filtering
+Pattern with nothing other than Current Folder Type set, that filtering
+rule is ignored.
+
+<H2>Filter Actions</H2>
+
+Once a filter match is found for a particular message, there are some actions
+that may be taken.
+First, the message may have its status changed.
+This is the same message status that you can manipulate manually using the
+<a href="h_common_flag">Flag Command</a>.
+There are always four elements of message status that you can control.
+You can set or clear the Important status, the New status, the Deleted
+status, and the Answered status.
+Of course, if the filter is going to delete the message,
+then there is no point in setting message status.
+You may also be able to set user-defined keywords for a message.
+Read a little about keywords in the help text for the
+<A HREF="h_common_flag">Flag</A> command.
+<P>
+Second, the filter may delete or move the message.
+Deleting the message marks it Deleted and removes it from view.
+It is effectively gone forever (though it technically is still there until
+the next expunge command, which may happen implicitly).
+Moving the message moves it from the open folder into the folder
+listed on the &quot;Folder List&quot; line of the filter configuration.
+If you list more than one folder name (separated by commas) then the message
+will be copied to each of those folders.
+In any case, if &quot;Delete&quot; or &quot;Move&quot; is set then the
+message is removed from the current folder.
+If you just want to set the messages status without deleting it from
+the folder, then set the filter action to
+&quot;Just Set Message Status&quot;.
+<P>
+(There is no way to do a Copy instead of a Move, due to the difficulties
+involved in keeping track of whether or not a message has
+already been copied.)
+
+<H2>Command Descriptions</H2>
+
+<H3>Add</H3>
+
+The Add command is used to add a new filter definition to your set of
+filters.
+The new filter will be added after the highlighted filter.
+
+<H3>Delete</H3>
+
+The Delete command deletes the currently highlighted filter.
+
+<H3>Change</H3>
+
+The Change command lets you edit the nickname, Pattern,
+and Folder of the currently highlighted filter.
+
+<H3>Shuffle</H3>
+
+The Shuffle command allows you to change the order of the filters.
+You may move the currently highlighted filter up or down in the list.
+The order of the filters is important since the filters are tested for a
+match starting with the first filter and continuing until a match is found.
+You should place the filters with more specific Patterns near the beginning
+of the list, and those with more general Patterns near the end so that
+the more specific matches will happen when appropriate.
+
+<H3>Replicate</H3>
+
+The Replicate command is used to copy an existing filter and modify it.
+The new filter will be added after the highlighted filter.
+
+<H3>IncludeFile</H3>
+
+The IncludeFile command allows you to add a filters file to your configuration.
+Usually, your filters will be contained in your Alpine configuration file.
+If you wish, some or all of your filters may be stored in a separate file.
+If a filters file already exists (maybe it was made by somebody else using
+Alpine), you may insert it before the currently highlighted filter.
+You may also insert an empty file or a file that does not yet exist.
+Once you have an empty filters file in your configuration, you may use
+the Shuffle command to move filters into it.
+In fact, that's the only way to get the initial filter into the file.
+
+<H3>eXcludeFile</H3>
+
+The eXcludeFile command removes a filters file from your filters configuration.
+A limitation of the program is that in order to exclude a filters file
+that file must have at least one filter
+in it, otherwise you won't be able to highlight a line in the file.
+So you may have to add a dummy filter to the file in order to exclude the file.
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+<H3>Performance Considerations</H3>
+The number and type of patterns being tested can
+adversely effect performance. Issues to be aware
+of include:
+<P>
+<UL>
+ <LI> The more filters you have defined the longer it will take to run down
+the list. Deleting unused filters is a good idea.
+ <LI> Filtering in newsgroups served by an NNTP server will be slow
+if your patterns include tests other than &quot;From:&quot;
+or &quot;Subject:&quot;.
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_rules_score =====
+<HTML>
+<HEAD>
+<TITLE>SETUP SCORING SCREEN</TITLE>
+</HEAD>
+<BODY>
+<H1>SETUP SCORING SCREEN</H1>
+<H2>SETUP SCORING COMMANDS</H2>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Available Commands -- Group 1 Available Commands -- Group 2
+------------------------------- ------------------------------
+F1 Show Help Text F1 Show Help Text
+F2 See commands in next group F2 See commands in next group
+F3 Back to MAIN Alpine menu
+F4 Change configuration for rule
+F5 Move to previous rule F5 Include file in rule config
+F6 Move to next rule F6 Exclude file from config
+F7 Previous page of rules
+F8 Next page of rules
+F9 Add new rule F9 Replicate existing rule
+F10 Delete existing rule
+F11 Shuffle the order of rules
+F12 Whereis (search rule nicknames)
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigation General Alpine Commands
+----------------------- ---------------------
+ P Prev rule ? Display this help text
+ N Next rule E Back to MAIN Alpine menu
+ - Previous page
+Spc (space bar) Next page
+ W WhereIs (search for word in rule nicknames)
+
+Setup Scoring Commands
+------------------------------------------------
+ A Add new rule $ Shuffle the order of rules
+ D Delete existing rule C Change configuration for highlighted rule
+ R Replicate existing rule
+ I Include file in rule config X Exclude file from rule config
+</PRE>
+<!--chtml endif-->
+
+<H2>Description of the Setup Scoring Screen</H2>
+
+Most people will not use scores at all, but if you do use them, here's how
+they work in Alpine.
+Using this screen, you may define Scoring rules.
+The score for a message is calculated by looking at every Score rule defined
+and adding up the Score Values for the ones that match the message.
+If there are no matches for a message, it has a score of zero.
+Message scores may be used a couple of ways in Alpine.
+
+<H3>Sorting by Score</H3>
+
+One of the methods you may use to sort message indexes is to sort by
+score.
+The scores of all the messages in a folder will be calculated and then
+the index will be ordered by placing the messages in order of ascending or
+descending score.
+
+<H3>Scores for use in Patterns</H3>
+
+The Patterns used for Roles, Index Line Coloring, and Filtering have a
+category labeled &quot;Score Interval&quot;.
+When a message is being compared with a Pattern to check for a match, if
+the Score Interval is set only messages that have a score somewhere in
+the interval are a match.
+
+<H2>Scoring Rule Patterns</H2>
+
+In order to determine whether or not a message matches a scoring rule
+the message is compared with the rule's Pattern.
+These Patterns are the same for use with Roles, Filtering, Index Coloring,
+Scoring, Other Rules, and Search Rules, so are described in only one place,
+&quot;<A HREF="h_rule_patterns">here</A>&quot;.
+
+<P>
+Actually, Scoring rule Patterns are slightly different from the other types of
+Patterns because Scoring rule Patterns don't contain a Score Interval.
+In other words, when calculating the score for a message, which is done
+by looking at the Scoring rule Patterns, scores aren't used.
+
+<H2>Score Value</H2>
+
+This is the value that will be added to the score for a message if the
+rule's Pattern is a match.
+Each individual Score Value is an integer between -100 and 100, and the
+values from matching rules are added together to get a message's score.
+There is also a way to extract the value from a particular header of each
+message. See the help text for Score Value for further information.
+
+<H2>Command Descriptions</H2>
+
+<H3>Add</H3>
+
+The Add command is used to add a new scoring rule definition.
+The new rule will be added after the highlighted rule.
+
+<H3>Delete</H3>
+
+The Delete command deletes the currently highlighted scoring rule.
+
+<H3>Change</H3>
+
+The Change command lets you edit the nickname, Pattern,
+and Score Value of the currently highlighted scoring rule.
+
+<H3>Shuffle</H3>
+
+The Shuffle command allows you to change the order of the scoring rules.
+You may move the currently highlighted rule up or down in the list.
+The order of the rules is important since the rules are tested for a
+match starting with the first rule and continuing until a match is found.
+You should place the rules with more specific Patterns near the beginning
+of the list, and those with more general Patterns near the end so that
+the more specific matches will happen when appropriate.
+
+<H3>Replicate</H3>
+
+The Replicate command is used to copy an existing rule and modify it.
+The new rule will be added after the highlighted rule.
+
+<H3>IncludeFile</H3>
+
+The IncludeFile command allows you to add a rules file to your configuration.
+Usually, your rules will be contained in your Alpine configuration file.
+If you wish, some or all of your rules may be stored in a separate file.
+If a rules file already exists (maybe it was made by somebody else using
+Alpine), you may insert it before the currently highlighted rule.
+You may also insert an empty file or a file that does not yet exist.
+Once you have an empty rules file in your configuration, you may use
+the Shuffle command to move rules into it.
+In fact, that's the only way to get the initial rule into the file.
+
+<H3>eXcludeFile</H3>
+
+The eXcludeFile command removes a rules file from your rules configuration.
+A limitation of the program is that in order to exclude a rules file
+that file must have at least one rule
+in it, otherwise you won't be able to highlight a line in the file.
+So you may have to add a dummy rule to the file in order to exclude the file.
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_direct_config =====
+<HTML>
+<HEAD>
+<TITLE>SETUP LDAP DIRECTORY SERVERS SCREEN</TITLE>
+</HEAD>
+<BODY>
+<H1>SETUP LDAP DIRECTORY SERVERS SCREEN</H1>
+<H2>SETUP LDAP DIRECTORY SERVERS COMMANDS</H2>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Available Commands
+-------------------------------
+F1 Show Help Text
+F3 Back to MAIN Alpine menu
+F4 Change configuration for directory server
+F5 Move to previous directory server
+F6 Move to next directory server
+F7 Previous page of directory servers
+F8 Next page of directory servers
+F9 Add new directory server
+F10 Delete existing directory server
+F11 Shuffle the order of directory servers
+F12 Whereis (search directory server titles)
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigation General Alpine Commands
+----------------------- ---------------------
+ P Prev Directory Server ? Display this help text
+ N Next Directory Server E Back to MAIN Alpine menu
+ - Previous page
+Spc (space bar) Next page
+ W WhereIs (search for word in directory server titles)
+
+Setup LDAP Directory Server Commands
+------------------------------------------------
+ A Add new directory server $ Shuffle the order of directory servers
+ D Delete existing dir server C Change configuration for highlighted server
+</PRE>
+<!--chtml endif-->
+
+<H2>Description of the Setup LDAP Directory Servers Screen</H2>
+
+This screen lets you add, delete, modify, or change the order of your
+directory servers. You may also set some optional behavior for each server.
+The &quot;Add Dir&quot; command brings up a blank form to
+fill in. You will have to supply at least the name of the LDAP server.
+You will often have to supply a search base to be used with that server,
+as well. Once the form has been brought up on your screen, there is help
+available for each of the options you may set.
+<P>
+The &quot;Del Dir&quot; command allows you to remove a directory server
+from your configuration.
+<P>
+The &quot;Change&quot; command is similar to the &quot;Add Dir&quot; command.
+The difference is that instead of bringing up a form for a new server
+configuration, you are changing the configuration of an existing entry.
+For example, you might want to correct a typing error, change a
+nickname, or change one of the options set for that server.
+<P>
+The &quot;Shuffle&quot; command is used to change the order of directory
+servers.
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+============= h_address_display ========================
+<HTML>
+<HEAD>
+<TITLE>SEARCH RESULTS INDEX</TITLE>
+</HEAD>
+<BODY>
+<H1>SEARCH RESULTS INDEX</H1>
+This screen shows the results, if any, of your Directory Server search.
+Commands (besides those for screen navigation) are:
+<DL>
+<DT>View
+<!--chtml if pinemode="function_key"-->
+(F4)
+<!--chtml else-->
+(V)
+<!--chtml endif-->
+</DT>
+<DD>See the full information for the selected entry.
+
+<DT>Compose
+<!--chtml if pinemode="function_key"-->
+(F9)
+<!--chtml else-->
+(C)
+<!--chtml endif--></DT>
+<DD>Compose a message with the selected entry as the recipient.
+
+<DT>Role
+<!--chtml if pinemode="function_key"-->
+(F2)
+<!--chtml else-->
+(#)
+<!--chtml endif--></DT>
+<DD>Compose a message with the selected entry as the recipient. This differs
+from Compose in that you may select a role before beginning your composition.
+
+<DT>Forward
+<!--chtml if pinemode="function_key"-->
+(F10)
+<!--chtml else-->
+(F)
+<!--chtml endif--></DT>
+<DD>Send the full information for the selected entry as an
+email message to someone else.
+
+<DT>Save
+<!--chtml if pinemode="function_key"-->
+(F11)
+<!--chtml else-->
+(S)
+<!--chtml endif-->
+</DT>
+<DD>Save to your address book:
+<UL>
+<LI>the result of the search (as just found through your query) for the
+selected entry; or
+<LI>the selected entry for repeated Directory Server searching when used
+in the future.
+</UL>
+or<BR>
+Export to a file (external to Alpine):
+<UL>
+<LI>the full information for the selected entry; or
+<LI>the email address from the selected entry; or
+<LI>the selected entry in <A HREF="h_whatis_vcard">vCard</A> format.
+</UL>
+<DT>WhereIs
+<!--chtml if pinemode="function_key"-->
+(F12)
+<!--chtml else-->
+(W)
+<!--chtml endif-->
+</DT>
+<DD>Search for text in the SEARCH RESULTS INDEX screen. (Searches only the
+displayed text, not the full records for each entry.)
+</DL>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+============= h_address_select ========================
+<HTML>
+<HEAD>
+<TITLE>SEARCH RESULTS INDEX</TITLE>
+</HEAD>
+<BODY>
+<H1>SEARCH RESULTS INDEX</H1>
+This screen shows the results, if any, of your Directory Server search.
+Commands (besides those for screen navigation) are:
+<DL>
+<DT>Select
+</DT>
+<DD>Select this entry for use.
+
+<DT>ExitSelect
+<DD>Exit without selecting any of the entries.
+
+<DT>WhereIs
+</DT>
+<DD>Search for text in the SEARCH RESULTS INDEX screen. (Searches only the
+displayed text, not the full records for each entry.)
+</DL>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_folder_maint =====
+<HTML>
+<HEAD>
+<TITLE>Help for Folder List</TITLE>
+</HEAD>
+<BODY>
+<H1>FOLDER LIST COMMANDS</H1>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Available Commands -- Group 1 Available Commands -- Group 2
+------------------------------- ------------------------------
+F1 Show Help Text F1 Show Help Text
+F2 See commands in next group F2 See commands in next group
+F3 MAIN MENU Screen F3 Quit Alpine
+F4 Select folder and view it F4 MAIN MENU Screen
+F5 Move to previous folder
+F6 Move to next folder F6 Specify a folder to go to
+F7 Show previous screen of listing F7 Show MESSAGE INDEX of current folder
+F8 Show next screen of listing F8 Compose a message
+F9 Add a new folder F9 Print folder listing
+F10 Delete selected folder
+F11 Rename selected folder
+F12 Whereis (search folder names)
+
+Available Commands -- Group 3
+F1 Show Help Text
+F2 See commands in next group
+F5 Go to next new message
+ (or count recent messages if <A HREF="h_config_tab_checks_recent"><!--#echo var="FEAT_tab-checks-recent"--></A> is set)
+F8 Compose a message using roles
+F9 Export folder to a file
+F10 Import the file back to a folder
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigating the Folder Screen Operations on the Selected Folder
+---------------------------- ---------------------------------
+ P Move to previous folder V View Index of selected folder
+ N Move to next folder D Delete
+ - Show previous page of listing R Rename
+Spc (space bar) Show next page E Export to file
+ U Import from file to folder
+
+FOLDER LIST Screen Commands General Alpine Command
+--------------------------- ---------------------
+ A Add a folder O Show all other available commands
+ G Specify a folder to go to ? Show Help text
+ I Show MESSAGE INDEX of current folder M MAIN MENU Screen
+ W Whereis (search folder names) Q Quit Alpine
+ % Print folder listing C Compose a message
+ # Compose a message using roles
+</PRE>
+<!--chtml endif-->
+<P>
+These commands are only available in the FOLDER LIST screen when the
+<A HREF="h_config_enable_agg_ops">&quot;<!--#echo var="FEAT_enable-aggregate-command-set"-->&quot;
+feature</A> is set in the SETUP CONFIGURATION screen:<DL>
+<DT>Select:</DT>
+<DD>Select folders by certain criteria:<UL>
+<LI>All: of limited use, since there is no Apply command.
+<LI>by Property: <UL>
+ <LI>folder contains messages not yet seen
+ <LI>folder contains new messages
+ <LI>folder contains exactly as many, more, or fewer messages
+than a given number
+ </UL>
+<LI>by Text: <UL>
+ <LI>contained in name of folder (Name Select)
+ <LI>contained in messages in folder (Content Select)
+ </UL>
+</UL></DD>
+
+<DT>Select current:</DT>
+<DD>Select the folder the cursor is on. (Can be used to &quot;manually&quot;
+add one or more folders to a set created with the Select command described
+above.)</DD>
+<DT>Zoom mode:</DT>
+<DD>Toggles display of only selected folders or all folders on and off.</DD>
+</DL>
+<P>
+If the feature
+<A HREF="h_config_tab_checks_recent">&quot;<!--#echo var="FEAT_tab-checks-recent"-->&quot;</A>
+is set then the TAB key will display the number of recent messages and
+the total number of messages in the highlighted folder.
+<P>
+The &quot;Export&quot; command causes the lowest common denominator style
+mailbox to be written to a file.
+If the file already exists, you are asked if you want to delete it.
+If you say No, then the operation is aborted.
+Export might be a reasonable way to store a backup or an archival copy of
+a folder.
+The exported-to file is a local file on the system where you are running Alpine.
+The &quot;Import&quot; command is the opposite of the Export command.
+It reads a file created by Export and asks where it should save it in your
+folders.
+This could be a new folder or an existing folder.
+If the folder already exists, the messages from the exported file will be
+appended to the folder.
+<P>
+<CENTER>Description of the FOLDER LIST Screen</CENTER>
+
+The purpose of the FOLDER LIST screen is to help you browse and manage
+the folders and directories (also known as &quot;hierarchy&quot;)
+contained within a collection.
+
+<P>
+Folders and directories are arranged alphabetically across lines of
+the screen. Directories, if present, are denoted by a special
+character at the end of the name known as the hierarchy delimiter
+(typically, &quot;/&quot;). By default, folders and directories are
+mixed together. The
+"<A HREF="h_config_fld_sort_rule"><!--#echo var="VAR_folder-sort-rule"--></A>"
+configuration option can be used to group directories toward the
+beginning or end of the list.
+
+<P>
+The Next/Prev Page commands help browse the list, the Next/Prev Fldr
+commands change the &quot;selected&quot; (i.e., highlighted) folder or
+directory, and the View Fldr/Dir commands will &quot;open&quot; the
+selected item. Folder and directory management is provided via the
+Rename, Delete and Add commands.
+
+<P><CENTER>About Folders</CENTER>
+What are Folders?<P>
+
+Folders are simply files where messages are kept. Every message has to be
+in a folder. Most every Alpine user starts out with 3 folders: an INBOX, a
+folder for sent mail and a folder for saved messages.<P>
+
+You may create as many other folders as you wish. They must be given
+names that can be filenames on the filesystem.
+<P>
+
+You can move messages from one folder to another by opening the original
+folder and saving messages into the other folder just as you can save
+message from your INBOX to any other folder.<P>
+
+Folders are typically just files in the filesystem. However, the files
+that are
+folders have some special formatting in them (so that Alpine knows where one
+message ends and another begins) and should <EM>not</EM> be edited outside of
+Alpine. If you want copies of your messages in text files that you can edit
+or otherwise manipulate, use the Export command to copy them from Alpine into
+your regular file area.
+
+<P>
+FOR MORE INFORMATION: See the section on
+<A HREF="h_valid_folder_names">Valid Folder Names</A>.
+<P>
+<CENTER>About Directories</CENTER>
+<P>
+A directory is simply a container used to group folders within a
+folder list. You can create as many directories as you like. And
+directories can even contain directories themselves.
+
+<P>
+SPECIAL NOTES: When accessing folders on an IMAP server, it is important
+to note that not all IMAP servers support directories. If you find that
+the Add command fails to offer the &quot;Create Directory&quot; subcommand,
+then it's likely that directories are not supported by the server serving
+in that collection.
+
+<P>
+Similarly, servers that do provide for directories may not do so in
+the same way. On some servers, for example, each folder name you
+create is at the same time capable of being a directory. When this
+happens, Alpine will display both the folder name and the name of the
+directory (with trailing hierarchy delimiter) in the folder list.
+
+<P>
+Another issue with IMAP access, though with a much smaller set of servers,
+is that not all servers accept the request to list out the available
+folders and directories in the same way. If you find yourself having
+trouble viewing folders on your server, you might investigate the
+&quot;<A HREF="h_config_lame_list_mode"><!--#echo var="FEAT_enable-lame-list-mode"--></A>&quot;
+feature.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+========= h_valid_folder_names ========
+<HTML>
+<HEAD>
+<TITLE>Explanation of Valid Folder Names</TITLE>
+</HEAD>
+<BODY>
+<H1>Folder Name Syntax Explained</H1>
+
+Once your folder collections are defined, you can usually refer to
+folders by their simple (unqualified) name, or pick from a FOLDER LIST
+display. However, understanding the complete syntax for folder names,
+both local and remote, is handy when using the Goto command and when
+you are adding new folder collections via the Setups/collectionList screen.
+<P>
+An Alpine folder name looks like
+
+<P>
+<CENTER><SAMP>[{&lt;remote-specification&gt;}][#&lt;namespace&gt;]&lt;namespace-specific-part&gt;</SAMP></CENTER>
+<P>
+
+The square brackets ([]) mean that the part is optional.
+
+<P>
+If there is no remote-specification, then the folder name is interpreted
+locally on the computer running Alpine.
+Local folder names depend on the operating system used by the computer
+running Alpine, as well as the configuration of that system. For example,
+&quot;C:&#92;PINE&#92;FOLDERS&#92;OCT-94&quot; might exist on a PC, and
+&quot;~/mail/september-1994&quot; might be a reasonable folder name on a
+system running Unix.
+
+<P>
+Alpine users have the option of using folders that are stored on some other
+computer. Alpine accesses remote folders via IMAP (the Internet Message
+Access Protocol), or in the case of news, via NNTP (the Network News
+Transport Protocol). To be able to access remote folders in Alpine, the
+remote host must be running the appropriate server software (imapd or
+nntpd) and you must correctly specify the name of the folder to Alpine,
+including the domain name of the remote machine. For example,
+<P>
+<CENTER><SAMP>&#123;monet.art.example.com}INBOX</SAMP></CENTER>
+<P>
+could be a remote folder specification, and so could
+<P>
+<CENTER><SAMP>&#123;unixhost.art.example.com}~/mail/september-1994</SAMP></CENTER>
+and
+<P>
+<CENTER><SAMP>&#123;winhost.art.example.com}&#92;mymail&#92;SEP-94</SAMP></CENTER>
+<P>
+Note that in the case of remote folders, the directory/file path in the specification is
+determined by the operating system of the remote computer, <B>not</B> by
+the operating system of the computer on which you are running Alpine.
+<P>
+As you can tell, the name of the computer is in &#123;} brackets
+followed immediately by the name of the folder. (In each of these cases the
+optional namespace is missing.) If, as in these
+examples, there is no remote access protocol specified, then IMAP is
+assumed. Check
+<A HREF="h_folder_server_syntax">here</A>
+for a more detailed look at what options can be placed between the brackets.
+If there are no brackets at all, then the folder name is interpreted locally
+on the computer on which you are running Alpine.
+
+<P>
+To the right of the brackets when a server name is present, or at the
+start of the foldername if no server is present, the sharp sign,
+&quot;#&quot;, holds special meaning. It indicates a folder name
+outside the area reserved for your personal folders. In fact, it's
+used to indicate both the name of the folder, and a special phrase
+telling Alpine how to interpret the name that follows.
+
+<P>
+So, for example, Alpine can be used to access a newsgroup that might be
+available on your computer using:
+<P>
+<CENTER><SAMP>#news.comp.mail.pine</SAMP></CENTER>
+<P>
+The sharp sign indicates the folder name is outside your personal
+folder area. The &quot;news.&quot; phrase after it tells Alpine to
+interpret the remainder of the name as a newsgroup.
+
+<P>
+Similarly, to access a newsgroup on your IMAP server, you might
+use something like:
+<P>
+<CENTER><SAMP>&#123;wharhol.art.example.com}#news.comp.mail.misc</SAMP></CENTER>
+
+<P>
+There are a number of such special phrases (or &quot;namespaces&quot;)
+available. For a more detailed explanation read about
+<A HREF="h_folder_name_namespaces">Namespaces</A>.
+
+<P>
+Note that &quot;INBOX&quot; has special meaning in both local and remote folder
+names. The name INBOX refers to your &quot;principal incoming
+message folder&quot; and will be mapped to the actual file name used for your
+INBOX on any given host. Therefore, a name like
+&quot;&#123;xxx.art.example.com}INBOX&quot; refers to whatever file is used to
+store incoming mail for you on that particular host.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_folder_name_namespaces =======
+<HTML>
+<HEAD>
+<TITLE>FOLDER NAME NAMESPACES EXPLAINED</TITLE>
+</HEAD>
+<BODY>
+<H1>Folder Name Namespaces Explained</H1>
+
+An Alpine folder name looks like
+
+<P>
+<CENTER><SAMP>[{&lt;remote-specification&gt;}][#&lt;namespace&gt;][&lt;namespace-specific-part&gt;]</SAMP></CENTER>
+<P>
+
+The local part of a folder name has an optional &quot;Namespace&quot; which
+tells Alpine how to interpret the rest of the name.
+
+<P>
+By default the folder name is interpreted as defining a section of your personal
+folder area. This area and how you specify it are defined by the
+server, if one is specified, or, typically, the home
+directory, if no server is defined.
+
+<P>
+If a namespace is specified, it begins with the
+sharp, &quot;#&quot;, character followed by the name of the namespace
+and then the namespace's path-element-delimiter. Aside from the
+path's format, namespaces can also imply access rights, content
+policy, audience, location, and, occasionally, access methods.
+
+<P>
+Each server exports its own set (possibly of size one) of
+namespaces. Hence, it's likely communication with your server's
+administrator will be required for specific configurations. Some of
+the more common namespaces, however, include:
+
+<DL>
+<DT>#news.</DT>
+<DD>This specifies a set of folders in the newsgroup namespace. Newsgroup
+names are hierarchically defined with each level delimited by a period.
+<P>
+<CENTER><SAMP>#news.comp.mail.pine</SAMP></CENTER>
+<P>
+</DD>
+<DT>#public/</DT>
+<DD>This specifies a folder area that the server may export to the general
+public.
+</DD>
+<DT>#shared/</DT>
+<DD>This specifies a folder area that the server may export to groups
+of users.
+</DD>
+<DT>#ftp/</DT>
+<DD>This specifies a folder area that is the same as that it may have
+exported via the &quot;File Transfer Protocol&quot;.
+</DD>
+<DT>#mh/</DT>
+<DD>This specifies the personal folder area associated with folders
+and directories that were created using the MH message handling system.
+</DD>
+<DT>#move/</DT>
+<DD>This namespace is interpreted locally by Alpine. It has an unusual interpretation and format.
+<P>
+<CENTER><SAMP>#move&lt;DELIM&gt;&lt;MailDropFolder&gt;&lt;DELIM&gt;&lt;DestinationFolder&gt;</SAMP></CENTER>
+<P>
+The #move namespace is followed by two folder names separated by a delimiter
+character.
+The delimiter character may be any character that does not appear in
+the MailDropFolder name.
+The meaning of #move is that mail will be copied from the MailDropFolder to
+the DestinationFolder and then deleted (if possible) from the MailDropFolder.
+Periodic checks at frequency
+<A HREF="h_config_mailcheck"><!--#echo var="VAR_mail-check-interval"--></A>, but with a minimum
+time between checks set by
+<A HREF="h_config_maildropcheck"><!--#echo var="VAR_maildrop-check-minimum"--></A>,
+are made for new mail arriving in the MailDropFolder.
+An example that copies mail from a POP inbox to a local folder follows
+<P>
+<CENTER><SAMP>#move+{popserver.example.com/pop3/ssl}inbox+local folder</SAMP></CENTER>
+<P>
+To you it appears that mail is being delivered to the local folder when it
+is copied from the MailDropFolder, and you read mail from the local folder.
+<P>
+Note that if the DestinationFolder does not exist then the messages are not
+copied from the MailDropFolder.
+A #move folder may only be used as an
+<A HREF="h_config_enable_incoming">&quot;Incoming folder&quot;</A> or
+an Inbox.
+When you are in the FOLDER LIST of Incoming Message Folders (after turning
+on the
+<A HREF="h_config_enable_incoming">&quot;<!--#echo var="FEAT_enable-incoming-folders"-->&quot;</A>
+option)
+the Add command has a subcommand &quot;Use Mail Drop&quot;
+which may be helpful for defining the folder in your Alpine configuration.
+The same is true when you edit the
+<A HREF="h_config_inbox_path"><!--#echo var="VAR_inbox-path"--></A>
+option in Setup/Config.
+Each of these configuration methods will also create the DestinationFolder
+if it doesn't already exist.
+If you are having problems, make sure the DestinationFolder exists.
+You may find some more useful information about Mail Drops at
+<A HREF="h_maildrop">What is a Mail Drop?</A>.
+</DD>
+</DL>
+<P>
+
+In addition, the server may support access to other user's folders,
+provided you have suitable permissions. Common methods use a prefix
+of either &quot;~<VAR>user</VAR>/&quot;, or &quot;/<VAR>user</VAR>/&quot; to
+indicate the root of the other user's folder area.
+
+<P>
+No, nothing's simple.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+============= h_whatis_vcard ========================
+<HTML>
+<HEAD>
+<TITLE>VCARD EXPLAINED</TITLE>
+</HEAD>
+<BODY>
+<H1>What is the vCard format?</H1>
+A &quot;vCard&quot; is a sort of electronic business card, for exchanging
+information about and among people and organizations electronically.
+More information about vCard can be found (as of May 1998) on the WWW site
+of the Internet Mail Consortium at the URL:
+<P>
+<CENTER><A HREF="http://www.imc.org/pdi/">http://www.imc.org/pdi/</A></CENTER>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_folder_open =====
+<HTML>
+<HEAD>
+<TITLE>Explanation of Folder Selection</TITLE>
+</HEAD>
+<BODY>
+<BR>
+<BR>
+This screen is designed to allow you to quickly and easily survey your
+folders and select one to open.
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Navigating the List of Folders General Alpine Commands
+------------------------------ ---------------------
+ P Move to previous folder ? Show this help text
+ N Move to next folder
+ - Show previous screen of folders
+Spc (space bar) Show next screen
+ W WhereIs (search folder names)
+
+Folder Selection Commands
+-------------------------
+ E Exit the Folder Select menu (without selecting a folder)
+ S Select the currently highlighted folder
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigating the List of Folders General Alpine Commands
+------------------------------ ---------------------
+F5 Move to previous folder F1 Show this help text
+F6 Move to next folder
+F7 Show previous screen of folders
+F8 Show next screen of folders
+F12 WhereIs (search folder names)
+
+Folder Selection Commands
+-------------------------
+F3 Exit the Folder Select menu (without selecting a folder)
+F4 Select the currently highlighted folder
+</PRE>
+<!--chtml endif-->
+<P>
+FOR MORE INFORMATION: See the section on
+<A HREF="h_valid_folder_names">Valid Folder Names</A>.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_folder_subscribe =====
+<HTML>
+<HEAD>
+<TITLE>Newsgroup Subcribe Screen explained</TITLE>
+</HEAD>
+<BODY>
+<H1>FOLDER SUBSCRIBE HELP</H1>
+
+This screen is designed to help you subscribe to newsgroups you are
+not currently subscribed to. The screen display is a list of all
+available newsgroups (or possibly a partial list if you specified a
+partial name when entering the screen). Groups you have already
+subscribed to have the letters &quot;SUB&quot; next to them. You may
+select a single new group to subscribe to by moving the cursor to that
+group and pressing &quot;S&quot; or carriage return. Alternatively,
+you may change into ListMode with the &quot;ListMode&quot; command.
+The display will change slightly so that each group has a checkbox in
+front of it. Use the cursor and the Set/Unset command to place an
+&quot;X&quot; in front of each newsgroup you wish to subscribe to.
+<P>
+
+When you are finished marking groups, the &quot;Subscribe&quot;
+command will subscribe you to those groups you have marked. Note, you
+may not unsubscribe to groups with this command. Instead of the
+&quot;A&quot; &quot;Subscribe&quot; command, use the &quot;D&quot;
+UnSbscrbe command.
+<P>
+
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Navigating the List of Newsgroups General Alpine Commands
+--------------------------------- ---------------------
+F5 Move to previous group F1 Show this help text
+F6 Move to next group
+F7 Show previous screen of groups
+F8 Show next screen of groups
+F12 WhereIs (search group names)
+F9 Use ListMode
+
+Group Selection Commands
+-------------------------
+F3 Exit the News Subscribe menu (without selecting any groups)
+F4 Subscribe to the currently highlighted newsgroup
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigating the List of Newsgroups General Alpine Commands
+--------------------------------- ---------------------
+ P Move to previous group ? Show this help text
+ N Move to next group
+ - Show previous screen of groups
+Spc (space bar) Show next screen
+ W WhereIs (search group names)
+ L Use ListMode
+
+Group Selection Commands
+-------------------------
+ E Exit the News Subscribe menu (without selecting any groups)
+ S Subscribe to the currently highlighted newsgroup
+</PRE>
+<!--chtml endif-->
+<P>
+When in ListMode, there is an additional command for marking groups to
+subscribe to:
+<P>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+ListMode Commands
+-------------------------
+F9 Set or unset the highlighted group
+</PRE>
+<!--chtml else-->
+<PRE>
+ListMode Commands
+-------------------------
+X Set or unset the highlighted group
+</PRE>
+<!--chtml endif-->
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_folder_postnews =====
+<HTML>
+<HEAD>
+<TITLE>Newsgroup selecting for Posting explained</TITLE>
+</HEAD>
+<BODY>
+This screen is designed to allow you to quickly and easily survey
+the available newsgroups and select one to post news to.
+<P>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Navigating the List of Newsgroups General Alpine Commands
+--------------------------------- ---------------------
+F5 Move to previous group F1 Show this help text
+F6 Move to next group
+F7 Show previous screen of groups
+F8 Show next screen of groups
+F12 WhereIs (search group names)
+
+Group Selection Commands
+-------------------------
+F3 Exit the Selection menu (without selecting a group)
+F4 Select the currently highlighted newsgroup
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigating the List of Newsgroups General Alpine Commands
+--------------------------------- ---------------------
+ P Move to previous group ? Show this help text
+ N Move to next group
+ - Show previous screen of groups
+Spc (space bar) Show next screen of groups
+ W WhereIs (search group names)
+
+Group Selection Commands
+-------------------------
+ E Exit the Selection menu (without selecting a group)
+ S Select the currently highlighted newsgroup
+</PRE>
+<!--chtml endif-->
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_folder_save =====
+<HTML>
+<HEAD>
+<TITLE>Folder Select for Save Explained</TITLE>
+</HEAD>
+<BODY>
+This screen is designed to allow you to quickly and easily survey your
+folders and select one to use for saving the current message.
+<P>
+
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Navigating the List of Folders General Alpine Commands
+------------------------------ ---------------------
+F5 Move to previous folder F1 Show this help text
+F6 Move to next folder
+F7 Show previous screen of folders
+F8 Show next screen of folders
+F12 WhereIs (search folder names)
+
+Folder Selection Commands
+-------------------------
+F3 Exit the Folder Select menu (without selecting a folder)
+F4 Select the currently highlighted folder
+F11 AddNew folder (just like Select, but you type in a new folder name)
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigating the List of Folders General Alpine Commands
+------------------------------ ---------------------
+ P Move to previous folder ? Show this help text
+ N Move to next folder
+ - Show previous screen of folders
+Spc (space bar) Show next screen of folders
+ W WhereIs (search folder names)
+
+Folder Selection Commands
+-------------------------
+ E Exit the Folder Select menu (without selecting a folder)
+ S Select the currently highlighted folder
+ A AddNew folder (just like Select, but you type in a new folder name)
+</PRE>
+<!--chtml endif-->
+<P>
+FOR MORE INFORMATION: See the section on
+<A HREF="h_valid_folder_names">Valid Folder Names</A>.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_folder_fcc =====
+<HTML>
+<HEAD>
+<TITLE>Folder Select for Fcc Explained</TITLE>
+</HEAD>
+<BODY>
+This screen is designed to allow you to quickly and easily survey your
+folders and select one to use as the file carbon copy (fcc) for the
+current message.
+<P>
+
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Navigating the List of Folders General Alpine Commands
+------------------------------ ---------------------
+F5 Move to previous folder F1 Show this help text
+F6 Move to next folder
+F7 Show previous screen of folders
+F8 Show next screen of folders
+F12 WhereIs (search folder names)
+
+Folder Selection Commands
+-------------------------
+F3 Exit the Folder Select menu (without selecting a folder)
+F4 Select the currently highlighted folder
+F11 AddNew folder (just like Select, but you type in a new folder name)
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigating the List of Folders General Alpine Commands
+------------------------------ ---------------------
+ P Move to previous folder ? Show this help text
+ N Move to next folder
+ - Show previous screen of folders
+Spc (space bar) Show next screen of folders
+ W WhereIs (search folder names)
+
+Folder Selection Commands
+-------------------------
+ E Exit the Folder Select menu (without selecting a folder)
+ S Select the currently highlighted folder
+ A AddNew folder (just like Select, but you type in a new folder name)
+</PRE>
+<!--chtml endif-->
+<P>
+FOR MORE INFORMATION: See the section on
+<A HREF="h_valid_folder_names">Valid Folder Names</A>.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_folder_pattern_roles =====
+<HTML>
+<HEAD>
+<TITLE>Folder Select for Current Folder Explained</TITLE>
+</HEAD>
+<BODY>
+This screen is designed to allow you to quickly and easily survey your
+folders and select one to use as the specific Current Folder
+in a Pattern.
+<P>
+
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Navigating the List of Folders General Alpine Commands
+------------------------------ ---------------------
+F5 Move to previous folder F1 Show this help text
+F6 Move to next folder
+F7 Show previous screen of folders
+F8 Show next screen of folders
+F12 WhereIs (search folder names)
+
+Folder Selection Commands
+-------------------------
+F3 Exit the Folder Select menu (without selecting a folder)
+F4 Select the currently highlighted folder
+F11 AddNew folder (just like Select, but you type in a new folder name)
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigating the List of Folders General Alpine Commands
+------------------------------ ---------------------
+ P Move to previous folder ? Show this help text
+ N Move to next folder
+ - Show previous screen of folders
+Spc (space bar) Show next screen of folders
+ W WhereIs (search folder names)
+
+Folder Selection Commands
+-------------------------
+ E Exit the Folder Select menu (without selecting a folder)
+ S Select the currently highlighted folder
+ A AddNew folder (just like Select, but you type in a new folder name)
+</PRE>
+<!--chtml endif-->
+<P>
+FOR MORE INFORMATION: See the section on
+<A HREF="h_valid_folder_names">Valid Folder Names</A>.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_folder_stayopen_folders =====
+<HTML>
+<HEAD>
+<TITLE>Folder Select Explained</TITLE>
+</HEAD>
+<BODY>
+This screen is designed to allow you to quickly and easily survey your
+folders and select one to use as a Stay-Open folder.
+<P>
+
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Navigating the List of Folders General Alpine Commands
+------------------------------ ---------------------
+F5 Move to previous folder F1 Show this help text
+F6 Move to next folder
+F7 Show previous screen of folders
+F8 Show next screen of folders
+F12 WhereIs (search folder names)
+
+Folder Selection Commands
+-------------------------
+F3 Exit the Folder Select menu (without selecting a folder)
+F4 Select the currently highlighted folder
+F11 AddNew folder (just like Select, but you type in a new folder name)
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigating the List of Folders General Alpine Commands
+------------------------------ ---------------------
+ P Move to previous folder ? Show this help text
+ N Move to next folder
+ - Show previous screen of folders
+Spc (space bar) Show next screen of folders
+ W WhereIs (search folder names)
+
+Folder Selection Commands
+-------------------------
+ E Exit the Folder Select menu (without selecting a folder)
+ S Select the currently highlighted folder
+ A AddNew folder (just like Select, but you type in a new folder name)
+</PRE>
+<!--chtml endif-->
+<P>
+FOR MORE INFORMATION: See the section on
+<A HREF="h_valid_folder_names">Valid Folder Names</A>.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_folder_action_roles =====
+<HTML>
+<HEAD>
+<TITLE>Folder Select Explained</TITLE>
+</HEAD>
+<BODY>
+This screen is designed to allow you to quickly and easily survey your
+folders and select one to use as the folder into which messages
+matching this filter will be moved.
+<P>
+
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Navigating the List of Folders General Alpine Commands
+------------------------------ ---------------------
+F5 Move to previous folder F1 Show this help text
+F6 Move to next folder
+F7 Show previous screen of folders
+F8 Show next screen of folders
+F12 WhereIs (search folder names)
+
+Folder Selection Commands
+-------------------------
+F3 Exit the Folder Select menu (without selecting a folder)
+F4 Select the currently highlighted folder
+F11 AddNew folder (just like Select, but you type in a new folder name)
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigating the List of Folders General Alpine Commands
+------------------------------ ---------------------
+ P Move to previous folder ? Show this help text
+ N Move to next folder
+ - Show previous screen of folders
+Spc (space bar) Show next screen of folders
+ W WhereIs (search folder names)
+
+Folder Selection Commands
+-------------------------
+ E Exit the Folder Select menu (without selecting a folder)
+ S Select the currently highlighted folder
+ A AddNew folder (just like Select, but you type in a new folder name)
+</PRE>
+<!--chtml endif-->
+<P>
+FOR MORE INFORMATION: See the section on
+<A HREF="h_valid_folder_names">Valid Folder Names</A>.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_abook_config =====
+<HTML>
+<HEAD>
+<TITLE>SETUP ADDRESS BOOKS SCREEN</TITLE>
+</HEAD>
+<BODY>
+<H1>SETUP ADDRESS BOOKS SCREEN</H1>
+<H2>SETUP ADDRESS BOOKS COMMANDS</H2>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Available Commands
+-------------------------------
+F1 Show Help Text
+F3 Back to MAIN Alpine menu
+F4 Change configuration for address book
+F5 Move to previous address book
+F6 Move to next address book
+F7 Previous page of address books
+F8 Next page of address books
+F9 Add new address book
+F10 Delete existing address book
+F11 Shuffle the order of address books
+F12 Whereis (search address book titles)
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigation General Alpine Commands
+----------------------- ---------------------
+ P Prev Address Book ? Display this help text
+ N Next Address Book E Back to MAIN Alpine menu
+ - Previous page
+Spc (space bar) Next page
+ W WhereIs (search for word in address book titles)
+
+Setup Address Books Commands
+------------------------------------------------
+ A Add new address book $ Shuffle the order of address books
+ D Delete existing address book C Change configuration for address book
+</PRE>
+<!--chtml endif-->
+
+<H2>Description of the Setup Address Books Screen</H2>
+
+This screen lets you add, delete, modify, or change the order of your
+address books. The &quot;Add Abook&quot; command brings up a blank form to
+fill in. If you are adding a remote address book on an IMAP server
+you should fill in the name of the IMAP server. Otherwise, leave
+that field blank. (Note that remote IMAP address books are an Alpine
+concept and are unlikely to interoperate with other mail clients.)
+For a remote address book, fill in the name of the remote folder
+in the Folder field. This should be a folder that is used only for
+this one purpose, not a general purpose folder you expect to store
+messages in.
+<P>If you are adding a local address book, fill in the
+Folder Name field with a local file name (e.g., .addressbook).
+<P>
+<B>Please note:</B> Remote address books stored on an IMAP server are
+of an entirely different format (namely, a special-purpose
+&quot;mail folder&quot;) than that of the local addressbook familiar
+to Alpine users. Therefore,
+you cannot use &quot;add a remote address book&quot; to make an existing
+Alpine .addressbook file you may have on a remote IMAP server accessible to
+Alpine running on a different host.
+<P>
+
+The &quot;Del Abook&quot; command allows you to remove an address book
+from your configuration. It will also ask you if you wish to remove
+the data for that address book, which would erase all traces of the
+address book if you answer Yes.
+<P>
+
+The &quot;Change&quot; command is similar to the &quot;Add Abook&quot; command.
+The difference is that instead of adding a new address book to your
+configuration, you are changing the configuration of an existing entry.
+For example, you might want to correct a typing error or change a
+nickname. The &quot;Change&quot; command is not a move command. If you
+change the folder name or server name the data will not be moved for you.
+<P>
+
+The &quot;Shuffle&quot; command is used for two purposes. If you shuffle
+an address book toward another address book in the same group then
+the order of those two address books will be swapped. If you shuffle
+the last Personal address book down towards the Global address book
+section, it will become a Global address book. If you shuffle
+the first Global address book up it will become a Personal address
+book. The main difference between Personal and Global address
+books is that Global address books are forced read-only.
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_abook_top =====
+<HTML>
+<HEAD>
+<TITLE>ADDRESS BOOK LIST COMMANDS</TITLE>
+</HEAD>
+<BODY>
+<H1>ADDRESS BOOK LIST COMMANDS</H1>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Available Commands -- Group 1 Available Commands -- Group 2
+------------------------------- ------------------------------
+F1 Show Help Text F1 Show Help Text
+F2 See commands in next group F2 See commands in next group
+F3 Exit to MAIN MENU F3 Quit Alpine
+F4 View/Edit selected address book
+F5 Move to previous address book F5 FOLDER LIST screen
+F6 Move to next address book F6 Specify a folder to go to
+F7 Previous page F7 MESSAGE INDEX screen
+F8 Next page F9 Print list of address books
+F12 Whereis (search for word)
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigation General Alpine Commands
+----------------------- ---------------------
+ P Previous Entry ? Display this help text
+ N Next Entry O Show all other available commands
+ - Previous page &lt; Back to MAIN Alpine menu
+Spc (space bar) Next page Q Quit Alpine
+ W WhereIs (search for word) L FOLDER LIST screen
+ G Specify a folder to go to
+Address Book Commands I MESSAGE INDEX screen
+------------------------------------------------
+ &gt; View/Edit selected address book
+ or
+ &gt; Search on selected directory server
+
+ % Print list of address books and directory servers
+</PRE>
+<!--chtml endif-->
+
+<H2>Description of the Address Book List Screen</H2>
+
+From this screen you may choose which address book you wish to view
+or edit. For more information on address books, view one of your
+address books (with
+<!--chtml if pinemode="function_key"-->
+F4
+<!--chtml else-->
+&quot;&gt;&quot;
+<!--chtml endif-->)
+and see the Help Text there.<P>
+
+You may also choose a directory server on which to search for entries.
+You do that by highlighting the directory server line and using
+<!--chtml if pinemode="function_key"-->
+F4
+<!--chtml else-->
+&quot;&gt;&quot;
+<!--chtml endif-->.<P>
+
+If you wish to define new address books or directory servers go to the Main
+menu and choose Setup. You may then either choose to setup AddressBooks or
+Directory (among other things). It's possible that the Directory option
+will not be there if the Alpine you are using does not contain LDAP directory
+lookup functionality.
+
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_abook_opened =====
+<HTML>
+<HEAD>
+<TITLE>THE ALPINE ADDRESS BOOK</TITLE>
+</HEAD>
+<BODY>
+<H1>THE ALPINE ADDRESS BOOK</H1>
+<H2>ADDRESS BOOK COMMANDS</H2>
+<PRE>
+<!--chtml if pinemode="function_key"-->
+Available Commands -- Group 1 Available Commands -- Group 2
+------------------------------- ------------------------------
+F1 Show Help Text F1 Show Help Text
+F2 See commands in next group F2 See commands in next group
+F3 Exit this screen F3 Quit Alpine
+F4 View/Edit selected entry F4 Go to MAIN MENU screen
+F5 Move to previous entry F5 FOLDER LIST screen
+F6 Move to next entry F6 Specify a folder to go to
+F7 Previous page of address book F7 MESSAGE INDEX screen
+F8 Next page of address book F8 Compose to entry using roles
+F9 Add new entry to address book F9 Print address book
+F10 Delete selected entry F10 TakeAddr to another addrbook
+F11 Compose to selected entry F11 Save or Export addrbook selections
+F12 Whereis (search address book) F12 Forward entry by mail
+
+Available Commands -- Group 3
+------------------------------
+F3 Select F6 Zoom (or unZoom)
+F5 Select Current F7 Apply Command to Selection
+<!--chtml else-->
+Address Book Navigation General Alpine Commands
+----------------------- ---------------------
+ P Prev Address ? Display this help text
+ N Next Address O Show all other available commands
+ - Previous page of address book M Back to MAIN MENU
+Spc (space bar) Next page Q Quit Alpine
+ W WhereIs (search for word C Compose message to selected addr
+ or name in address book) # Compose to addr using roles
+ &lt; To List of Address Books if L FOLDER LIST screen
+ more than one, else to MAIN G Specify a folder to go to
+ I MESSAGE INDEX screen
+
+Address Book Commands
+----------------------------------------------------
+ &gt; View/Update selected entry D Delete selected entries
+ % Print address book S Save or Export address book selections
+ F Forward entries by mail @ Add new entry to address book
+
+ ; Select command Z Toggle Zoom Mode
+ : Select highlighted entry A Apply command to selected entries
+
+<!--chtml endif-->
+</PRE>
+Note: The presence or absence of the final four commands above is
+controlled by the option
+<A HREF="h_config_enable_agg_ops">&quot;<!--#echo var="FEAT_enable-aggregate-command-set"-->&quot;</A>.
+<P>
+
+<H2>Description of the Address Book Screen</H2>
+
+This screen lets you edit and manage entries in your address book. It
+also acts as a short-cut for composing messages to people in the address
+book. When, from this screen, you press <!--chtml if pinemode="function_key"-->F11<!--chtml else-->&quot;C&quot;<!--chtml endif--> for ComposeTo, the
+message starts &quot;pre-addressed&quot; to whatever address book entry is
+currently selected. If you use the <!--chtml if pinemode="function_key"-->F8<!--chtml else-->&quot;#&quot;<!--chtml endif--> for Role, you may first select a
+role to use in your composition.
+<P>
+Alpine's address book helps you keep a list of addresses you send email to so
+you do not have to remember addresses that are often complex. Each entry
+in the address book has five fields, all of them optional. The three
+elements that are usually visible on the ADDRESS BOOK display, are: <DL>
+
+ <P><DT>NICKNAME: <DD>A short easy-to-remember label to identify the entry.
+ This is what you type in as you are addressing the message in the
+ composer. If there is a matching entry in your address book(s),
+ Alpine will extract the corresponding FullName and Address fields to
+ generate the actual address for your message.
+
+ <P><DT>FULLNAME: <DD>A longer field where you can put the full name
+of the
+ person or organization. Usually the full names are put in last
+ name first so they sort nicely in alphabetical order. Whatever
+ you put as the name here will appear on the message when it is
+ finally delivered. Examples:<PRE>
+ Garcia Marquez, Gabriel
+ Henscheid, Eckhard
+ Alpine-Info mailing list
+ Library materials renewal requests
+ Kim An-guk
+ &quot;George III, King of Great Britain, 1738-1820&quot;
+</PRE>
+(In the second-to-last example, no comma is used in the name so that
+the family name appears first in the address book and when the entry is
+used in the composer.
+In the last example, retaining the commas is intended;
+double-quotation marks surround the name to
+prevent the transposition of its parts when the entry is used in
+the composer.)
+
+ <P><DT>ADDRESS: <DD>This is the actual email address itself. This must be
+ a valid Internet address that conforms to the Internet message
+ header standard, RFC-822. (See also <A HREF="h_address_format">Explanation of Address formats</A>.)</DL>
+
+The two fields that aren't usually visible are:<DL>
+
+ <P><DT>FCC: <DD>The name of the folder you would like a copy of any outgoing
+ message to this address to be saved in. If this field is set, and
+ this address is the first one in the message's To: header, then
+ Alpine will use this folder name for the FCC in lieu of the normal
+ FCC folder name.
+
+ <P><DT>COMMENTS: <DD>This field contains arbitrary text for your convenience.
+ </DL>
+<P>
+Due to screen width limitations, these last two fields do not show up in
+the normal ADDRESS BOOK display. You may select the
+&quot;View/Update&quot; command to
+view or modify them. You may use the configuration variable
+<A HREF="h_config_abook_formats">&quot;<!--#echo var="VAR_addressbook-formats"-->&quot;</A>
+to add these fields to your ADDRESS BOOK
+display, or to modify the format of the display.
+
+<H2>Sorting the Address book</H2>
+
+By default, address book entries are sorted alphabetically on the full
+name with distribution lists sorted to the end. Sorting can be changed by
+resetting the address book sort rule in the Alpine SETUP CONFIGURATION screen
+--assuming you have &quot;write&quot; permission for the address book file.
+<P>
+Unlike the sorting of folders (which only changes presentation), sorting an
+address book actually changes the file as it is kept on the computer. For
+this reason you won't be able to sort a shared or system-wide address
+book.
+
+<H2>Adding New Entries</H2>
+
+The easiest way to add new entries to your address book is to use the
+&quot;TakeAddr&quot; command when viewing a message.
+This command allows you to take addresses from the header and body of the
+message and put them into your address book, without having to type
+them in.
+<P>
+
+To manually add a new entry from within the address book screen, use the AddNew
+(<!--chtml if pinemode="function_key"-->F9<!--chtml else-->&quot;@&quot;<!--chtml endif-->) command.
+Use this command both for adding a simple alias and for adding a
+distribution list.
+
+<H2>Distribution Lists</H2>
+
+Address book entries can be simple cases of aliases (a single nickname is linked
+to a single email address) or distribution lists (a single nickname
+pointing at more than one email address). Each distribution list has a
+nickname, a full name and a list of addresses. The addresses may be
+actual addresses or they may be other nicknames in your address book.
+They may even refer to other distribution lists.
+There's really no difference between a simple alias and a distribution list,
+other than the number of addresses.
+Therefore, you can turn a simple alias with one address into a distribution
+list simply by adding more addresses.
+To add entries to an existing list or alias
+use the View/Update (<!--chtml if pinemode="function_key"-->F4<!--chtml else-->&quot;&gt;&quot;<!--chtml endif-->) command. Delete (<!--chtml if pinemode="function_key"-->F10<!--chtml else-->&quot;D&quot;<!--chtml endif-->) will delete
+a single address from the list if the cursor is placed on the address;
+it will delete the entire distribution list if the cursor is on the
+nickname/fullname line. View/Update may also be used to delete addresses
+from a list.
+<P>
+Address field entries in distribution lists may take any one of three
+forms: a nickname existing in any of the defined address books, a normal
+address of the form &quot;jsmith@art.example.com&quot;, or a complete
+fullname/address combination, e.g. &quot;John Smith
+&lt;jsmith@art.example.com&gt;&quot;.
+<P>
+Distribution lists in Alpine address books can only be used by the person or
+people who have access to that address book. They are not usually used to
+implement discussion groups, but can be used to facilitate small
+discussion groups if all the participants have access to the same shared
+address book.
+
+<H2>FCC and Comments</H2>
+
+As mentioned above, each entry in the address book also has two other optional
+fields, Fcc and Comments. The command to look at or change either of these
+is the same View/Update command used for all of the fields (<!--chtml if pinemode="function_key"-->F4<!--chtml else-->&quot;&gt;&quot;<!--chtml endif-->). The
+Comments field is just for your own use. The Fcc field overrides the
+default Fcc if this address is the first one on the To line. The WhereIs
+command may be used to search for particular strings in the address book,
+including fields that are not visible (like Comment and Fcc by default).
+
+<H2>Aggregate Operations</H2>
+
+If the feature
+<A HREF="h_config_enable_agg_ops">&quot;<!--#echo var="FEAT_enable-aggregate-command-set"-->&quot;</A>
+is turned on (the default), then the four commands &quot;Select&quot;,
+&quot;Select Current&quot;, &quot;Zoom&quot;, and &quot;Apply&quot;
+are available. The two selection commands allow you to mark a set of
+address book entries as being selected. If you have more than one address
+book, the selections may be in more than one of those address books.
+The &quot;Zoom&quot; command will toggle between displaying only the selected
+entries and all of the entries. The &quot;Apply&quot; command allows you to
+apply one of the regular address book commands to all of the selected
+entries. Usually the address book commands apply to only the entry
+highlighted by the cursor. The &quot;Apply&quot; command works with the
+commands &quot;ComposeTo&quot;, &quot;Delete&quot;, &quot;Print&quot;,
+&quot;Save&quot;, &quot;Forward&quot;, and &quot;Role&quot;.
+
+<H2>Exporting and Forwarding Address book entries</H2>
+
+Under the save option, when you use the Export (<!--chtml if pinemode="function_key"-->F11<!--chtml else-->&quot;X&quot;<!--chtml endif-->) command, the currently highlighted
+address book entry is placed in a plain text file in your home directory
+<!--chtml if pinemode="running"-->
+(which, in the present configuration of your system, is
+ &quot;<!--#echo var="HOME_DIR"-->&quot;)
+<!--chtml endif-->
+or current working directory
+<!--chtml if pinemode="running"-->
+(which, at least for your current Alpine &quot;session,&quot;
+is &quot;<!--#echo var="CURRENT_DIR"-->&quot;)
+<!--chtml endif-->, depending on the
+<A HREF="h_config_use_current_dir">&quot;<!--#echo var="FEAT_use-current-dir"-->&quot;</A>
+configuration setting. If you have some entries selected and use the
+Apply (<!--chtml if pinemode="function_key"-->F7<!--chtml else-->&quot;A&quot;<!--chtml endif-->) Export command, all of the selected addresses will be
+placed in the text file.
+<P>
+When you use the Forward (<!--chtml if pinemode="function_key"-->F12<!--chtml else-->&quot;F&quot;<!--chtml endif-->) command, the currently highlighted
+address book entry is placed in a special attachment and you are put into
+the composer. You can fill in some comments in the body of the message,
+if you'd like, and send it to somebody else who uses Alpine. The recipient
+may use the TakeAddr command on that message to insert the address book
+entry you sent in their own address book. If you have some entries
+selected and use the Apply Forward command all of the selected entries
+will be forwarded in a single message. You may
+use Apply (<!--chtml if pinemode="function_key"-->F7<!--chtml else-->&quot;A&quot;<!--chtml endif-->) Forward to forward a copy of an entire address book.
+The recipient must be using Alpine in order to receive this correctly.
+One way for the recipient to handle this might be to create an empty
+address book and then &quot;Take&quot; your forwarded address book entries into
+that empty address book.
+
+<H2>Multiple and/or Site-Wide Address books</H2>
+
+You may have more than one personal address book. In addition, there may
+be one or more global address books. This capability allows you to have
+multiple personal address books (some of which may be shared) and it also
+allows system administrators to implement site-wide address books that
+contain entries for users on multiple machines within the organization.
+<P><DL>
+<DT>Searching
+ <DD> If you enter a nickname when composing a message, your
+ personal address books will be searched through in order, and then the
+ global address book(s) searched. If more than one address book has an entry
+ for the nickname, Alpine uses the first one that it finds, so an entry in
+ your personal address book would override a global address book entry. If
+ after searching all the address books there is still no match, (Unix) Alpine
+ then searches the local host password file on the assumption that you have
+ entered a local user name rather than an address book nickname.
+ You may change the search order of your address books with the <!--chtml if pinemode="function_key"-->F3<!--chtml else-->$<!--chtml endif--> Shuffle
+ command, but global address books are always searched after personal
+ address books.
+
+ <P><DT>Tab completion
+ <DD> If the
+ <A HREF="h_config_enable_tab_complete"><!--#echo var="FEAT_enable-tab-completion"--></A>
+ feature is turned on (the default) then the Tab key may be used
+ in the composer to complete partially typed nicknames in the To
+ or Cc lines. You type the first few letters of a nickname and then
+ press the Tab key. It there is only one nickname that matches it will
+ be filled in by Alpine. If there is more than one the unambiguous part
+ of the nicknames will be filled in. For example, if your address book or
+ books contains only the two entries &quot;barley&quot; and &quot;barbecue&quot;
+ beginning with the letters &quot;ba&quot;, then if you type &quot;ba&quot;
+ followed by a Tab character Alpine will fill in &quot;bar&quot; and stop.
+ If you then type a second Tab character you will be presented with a list
+ of matching nicknames to select from. Alternatively, you could type another
+ &quot;b&quot; resulting in &quot;barb&quot; and then a Tab would fill
+ in the entire &quot;barbecue&quot; entry.
+
+ <P><DT>Defining
+ <DD> You define multiple personal address books in the
+ <A HREF="h_abook_config">SETUP AddressBooks</A> screen, which you may reach
+ from the MAIN MENU.
+ You may add as many as you like. Global address books are usually
+ site-wide address books defined by the System administrator, but
+ you may define global address books of your own just like you define
+ personal address books.
+
+ <P><DT>Creating and updating
+ <DD> Personal address books are normally created empty
+ and populated by explicit additions from within Alpine, e.g. via the
+ TakeAddr command. Unlike personal address books, global address books may
+ not be modified/updated from within Alpine; that is, they are Read-Only.
+ Thus, global address books are created, populated and updated outside of
+ Alpine. They might be hand-edited, generated by a program from another
+ database, or by copying an existing address book. They might also be
+ some other user's personal address book, and so be modified normally by
+ that user but accessed Read-Only by you. See the Alpine Technical
+ Notes document (included in the Alpine distribution) for more information on
+ this.
+
+ <P><DT>Accessing
+ <DD>There are two different types of address books in Alpine.
+ A local address book is stored in a regular file and the normal file
+ access permissions apply. A remote address book is stored on an IMAP
+ server in a special folder that contains only messages pertaining to
+ that address book. The last message in the remote folder contains a
+ copy of the address book data, and that data is copied to a local cache
+ file in your home directory. From there it is accessed just like a local
+ address book. The name of the cache file is kept track of in a special
+ file called the
+ <A HREF="h_config_abook_metafile"><!--#echo var="VAR_remote-abook-metafile"--></A>,
+ the name of which is stored in
+ your Alpine configuration file the first time you use a remote address book.
+ Just as local Alpine address books use a format that only Alpine understands,
+ remote Alpine address books do the same and other mail reading programs
+ are unlikely to be able to understand them.<P>
+ While global address books are explicitly intended to be shared, there is
+ nothing to prevent you from sharing a personal address book with other
+ Alpine users. This might be useful in the case of a small workgroup.
+ However, it is recommended that updates to shared personal address books
+ be done when other Alpine users are not accessing the address book. Alpine
+ does not do any file-locking to manage concurrent updates to the
+ addressbook, but it does check to see if the file has been modified before
+ making any changes. Consequently, inadvertent concurrent updates will
+ only cause other Alpine users to have to restart their address book
+ operation, which will cause Alpine to reopen the updated file.
+
+ <P><DT>Converting to Remote
+ <DD>The easiest way to convert an existing local
+ address book into a remote address book is to create an empty new remote
+ personal address book by typing &quot;A&quot; to execute the
+ &quot;Add Pers Abook&quot; command in the SETUP Addressbook screen.
+ Make sure you add a <EM>personal</EM> address book, not a <EM>global</EM>
+ address book.
+ After you have added the empty
+ remote address book, go into the screen for the address book you wish
+ to copy and &quot;Select&quot; &quot;All&quot;.
+ This selects every entry in that
+ address book. Then type the command &quot;Apply Save&quot;.
+ You will be asked for the address book to save to. You may use ^P and ^N
+ to get to the new empty address book, then hit RETURN and the addresses
+ will be copied.
+ At this point you'll probably want to unselect all the entries in the local
+ address book before proceeding. You do that with
+ &quot;Select&quot; &quot;unselect All&quot;.
+
+</DL>
+<UL>
+<LI><A HREF="h_address_format">Explanation of Address formats</A>
+</UL>
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_abook_select_addr =====
+<HTML>
+<HEAD>
+<TITLE>Addressbook Selection Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>SELECT ADDRESS</H1>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Navigating the List of Messages General Alpine Commands
+------------------------------- ---------------------
+F5 Move to previous entry F1 Show this help text
+F6 Move to next entry
+F7 Show previous screen of address book
+F8 Show next screen of address book
+F12 WhereIs (search through address book)
+
+Address Selection Commands
+--------------------------
+F3 Exit the Address Select screen (without selecting an address)
+F4 Select the currently highlighted entry
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigating the List of Messages General Alpine Commands
+------------------------------- ---------------------
+ P Move to previous entry ? Show this help text
+ N Move to next entry
+ - Show previous screen of address book
+Spc (space bar) Show next screen of address book
+ W WhereIs (search through address book)
+
+Address Selection Commands
+--------------------------
+ E Exit the Address Select screen (without selecting an address)
+ S Select the currently highlighted entry
+</PRE>
+<!--chtml endif-->
+<P>
+
+This screen is designed to let you easily scan your address book(s) in
+order to select an entry for the message you are composing. You cannot
+edit your address book in any way at this time, for address book
+maintenance, select the address book command when not composing a message.
+<P>
+
+If you are composing a message and know the nickname of the person/list you
+want, you can bypass this screen by simply typing in the nickname on the
+appropriate header line (To:, Cc:, etc.) Exiting this screen without
+selecting an entry does not cancel your message.
+<P>
+
+FOR MORE INFORMATION on addressing see
+<A HREF="h_address_format">Explanation of Address formats</A>, and the
+<A HREF="h_abook_opened">Address Book Screen's</A>
+help text.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_abook_select_top =====
+<HTML>
+<HEAD>
+<TITLE>Addressbook Selection Navigation Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>NAVIGATING WHILE SELECTING ADDRESSES</H1>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Navigating the List of Address Books General Alpine Commands
+------------------------------- ---------------------
+F4 View the highlighted address book
+F5 Move to previous address book F1 Show this help text
+F6 Move to next address book
+F7 Show previous screen of address books
+F8 Show next screen of address books
+F12 WhereIs (search through address books)
+
+Address Selection Commands
+--------------------------
+F3 Exit the Address Select screen (without selecting an address)
+F4 Select the currently selected entries (if using ListMode)
+F9 Change to ListMode
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigating the List of Address Books General Alpine Commands
+------------------------------- ---------------------
+ &gt; View the highlighted address book
+ P Move to previous address book ? Show this help text
+ N Move to next address book
+ - Show previous screen of address books
+Spc (space bar) Show next screen of address books
+ W WhereIs (search through address books)
+
+Address Selection Commands
+--------------------------
+ E Exit the Address Select screen (without selecting an address)
+ S Select the currently selected entries (if using ListMode)
+ L Change to ListMode
+</PRE>
+<!--chtml endif-->
+<P>
+
+This screen is designed to let you easily scan your address book(s) in
+order to select entries for the message you are composing. You cannot
+edit your address book in any way at this time. For address book
+maintenance, select the address book command when not composing a message.
+<P>
+
+If you are composing a message and know the nickname of the person/list you
+want, you can bypass this screen by simply typing in the nickname on the
+appropriate header line (To:, Cc:, etc.) Exiting this screen without
+selecting an entry does not cancel your message.
+<P>
+
+The ListMode command will add a column at the left edge of the screen.
+You mark the entries that you wish to select with the &quot;X&quot; command.
+This allows you to choose more than one entry at a time.
+<P>
+
+An alternative method of composing a message to entries in your
+address book(s) is to first use the &quot;Select&quot; command from
+the address book maintenance screen and then the &quot;Apply&quot;
+&quot;ComposeTo&quot; command to start the composer composing to the
+selected entries.
+<P>
+
+FOR MORE INFORMATION on addressing see
+<A HREF="h_address_format">Explanation of Address formats</A>, and the
+<A HREF="h_abook_opened">Address Book Screen's</A>
+help text.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_abook_select_listmode =====
+<HTML>
+<HEAD>
+<TITLE>Address Listmode Selection from Composer Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>COMPOSER: SELECT ADDRESSES</H1>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Navigating the List of Messages General Alpine Commands
+------------------------------- ---------------------
+F5 Move to previous entry F1 Show this help text
+F6 Move to next entry
+F7 Show previous screen of address book
+F8 Show next screen of address book
+F12 WhereIs (search through address book)
+
+Address Selection Commands
+--------------------------
+F3 Exit the Address Select screen (without selecting an address)
+F4 Select the currently highlighted entry
+F9 Change to ListMode
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigating the List of Messages General Alpine Commands
+------------------------------- ---------------------
+ P Move to previous entry ? Show this help text
+ N Move to next entry
+ - Show previous screen of address book
+Spc (space bar) Show next screen of address book
+ W WhereIs (search through address book)
+
+Address Selection Commands
+--------------------------
+ E Exit the Address Select screen (without selecting an address)
+ S Select the currently highlighted entry
+ L Change to ListMode
+</PRE>
+<!--chtml endif-->
+<P>
+
+This screen is designed to let you easily scan your address book(s) in
+order to select entries for the message you are composing. You cannot
+edit your address book in any way at this time, for address book
+maintenance, select the address book command when not composing a message.
+<P>
+
+If you are composing a message and know the nickname of the person/list you
+want, you can bypass this screen by simply typing in the nickname on the
+appropriate header line (To:, Cc:, etc.) Exiting this screen without
+selecting an entry does not cancel your message.
+<P>
+
+The ListMode command will add a column at the left edge of the screen.
+You mark the entries that you wish to select with the &quot;X&quot; command.
+This allows you to choose more than one entry at a time.
+<P>
+
+An alternative method of composing a message to entries in your
+address book(s) is to first use the &quot;Select&quot; command from
+the address book maintenance screen and then the &quot;Apply&quot;
+&quot;ComposeTo&quot; command to start the composer composing to the
+selected entries.
+<P>
+
+FOR MORE INFORMATION on addressing see
+<A HREF="h_address_format">Explanation of Address formats</A>, and the
+<A HREF="h_abook_opened">Address Book Screen's</A>
+help text.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_abook_select_checks =====
+<HTML>
+<HEAD>
+<TITLE>Address Selection from Composer Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>COMPOSER: SELECT ADDRESSES</H1>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Navigating the List of Messages General Alpine Commands
+------------------------------- ---------------------
+F5 Move to previous entry F1 Show this help text
+F6 Move to next entry
+F7 Show previous screen of address book
+F8 Show next screen of address book
+F12 WhereIs (search through address book)
+
+Address Selection Commands
+--------------------------
+F3 Exit the Address Select screen (without selecting an address)
+F4 Select the currently highlighted entry
+F8 Either Sets or Unsets all entries in this address book
+F9 Set or Unset the highlighted entry
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigating the List of Messages General Alpine Commands
+------------------------------- ---------------------
+ P Move to previous entry ? Show this help text
+ N Move to next entry
+ - Show previous screen of address book
+Spc (space bar) Show next screen of address book
+ W WhereIs (search through address book)
+
+Address Selection Commands
+--------------------------
+ E Exit the Address Select screen (without selecting an address)
+ S Select the currently highlighted entry
+ X Set or Unset the highlighted entry
+ A Either Sets or Unsets all entries in this address book
+</PRE>
+<!--chtml endif-->
+<P>
+
+Mark the entries you wish to select with the &quot;X Set/Unset&quot;
+command. Type &quot;S Select&quot; to select all of the entries you
+have marked, just as if you had typed them in by hand.
+<P>
+
+An alternative method of composing a message to entries in your
+address book(s) is to first use the &quot;Select&quot; command from
+the address book maintenance screen and then the &quot;Apply&quot;
+&quot;ComposeTo&quot; command to start the composer composing to the
+selected entries.
+<P>
+
+FOR MORE INFORMATION on addressing see
+<A HREF="h_address_format">Explanation of Address formats</A>, and the
+<A HREF="h_abook_opened">Address Book Screen's</A>
+help text.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_abook_select_nicks_take =====
+<HTML>
+<HEAD>
+<TITLE>Take Address Nickname Selection Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>TAKEADDR: SELECT NICKNAME</H1>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Navigating the List of Messages General Alpine Commands
+------------------------------- ---------------------
+F5 Move to previous entry F1 Show this help text
+F6 Move to next entry
+F7 Show previous screen of address book
+F8 Show next screen of address book
+F12 WhereIs (search through address book)
+
+Message Selection Commands
+--------------------------
+F3 Exit the Nickname Select screen (without selecting an address)
+F4 Select the currently highlighted entry
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigating the List of Messages General Alpine Commands
+------------------------------- ---------------------
+ P Move to previous entry ? Show this help text
+ N Move to next entry
+ - Show previous screen of address book
+Spc (space bar) Show next screen of address book
+ W WhereIs (search through address book)
+
+Message Selection Commands
+--------------------------
+ E Exit the Nickname Select screen (without selecting an address)
+ S Select the currently highlighted entry
+</PRE>
+<!--chtml endif-->
+<P>
+
+This screen is designed to let you modify or add to an existing
+address book entry. You have already selected the name(s) and
+address(es) through &quot;Take Address&quot;. This screen simply lets
+you scan your address books and select the nickname to be
+changed/augmented. If you want to add a new entry, then you are in
+the wrong place-- Select &quot;Exit&quot; command.
+<P>
+
+FOR MORE INFORMATION on addressing see
+<A HREF="h_address_format">Explanation of Address formats</A>, and the
+<A HREF="h_abook_opened">Address Book Screen's</A>
+help text.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_abook_select_nick =====
+<HTML>
+<HEAD>
+<TITLE>Nickname Selection Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>SELECT NICKNAME</H1>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Navigating the List of Messages General Alpine Commands
+------------------------------- ---------------------
+F5 Move to previous entry F1 Show this help text
+F6 Move to next entry
+F7 Show previous screen of address book
+F8 Show next screen of address book
+F12 WhereIs (search through address book)
+
+Message Selection Commands
+--------------------------
+F3 Exit the Nickname Select screen (without selecting an address)
+F4 Select the currently highlighted entry
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigating the List of Messages General Alpine Commands
+------------------------------- ---------------------
+ P Move to previous entry ? Show this help text
+ N Move to next entry
+ - Show previous screen of address book
+Spc (space bar) Show next screen of address book
+ W WhereIs (search through address book)
+
+Message Selection Commands
+--------------------------
+ E Exit the Nickname Select screen (without selecting an address)
+ S Select the currently highlighted entry
+</PRE>
+<!--chtml endif-->
+<P>
+
+This screen is designed to let you look at the nicknames in your address
+books before choosing a new one.
+<P>
+
+FOR MORE INFORMATION on addressing see
+<A HREF="h_address_format">Explanation of Address formats</A>, and the
+<A HREF="h_abook_opened">Address Book Screen's</A>
+help text.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_takeaddr_screen =====
+<HTML>
+<HEAD>
+<TITLE>Take Address Screen Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>TAKE ADDRESS COMMANDS</H1>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Navigating the List of Addresses Address Selection Commands
+-------------------------------- --------------------------
+ F5 Move to previous entry F3 Exit without taking address
+ F6 Move to next entry F4 Take current address(es)
+ F7 Show previous page of address list
+ F8 Show next page of address list
+ F2 WhereIs (search list)
+ --------------
+Mode Toggle F9 Set/Unset current address
+----------- F10 Set all
+ F12 Toggle between List and single mode F11 Unset all
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigating the List of Addresses Address Selection Commands
+-------------------------------- --------------------------
+ P Move to previous entry &lt; Exit without taking address
+ N Move to next entry T Take address
+ - Show previous page of address list
+Spc (space bar) Show next page of address list
+ W WhereIs (search list) List Mode
+ ---------
+Single Mode X Set/Unset current address
+----------- A Set all addresses
+ L Switch to list mode U Unset all addresses
+ S Switch to single mode
+</PRE>
+<!--chtml endif-->
+
+<H2>Description of the Take Address Screen</H2>
+
+This screen is designed to let you select one or more address/name
+combinations from the current message and put them into your address book.
+The cursor is initially placed on the line with the message author.
+Other lines include the names of people and/or mailing lists who also
+received the message. Other people &quot;involved&quot; in the
+message (e.g. the person named as Reply-To:) are also listed here.
+<P>
+
+The simple case is adding a new, single entry into your address book. To
+do this, simply highlight the correct line and press
+<!--chtml if pinemode="function_key"-->
+F4.
+<!--chtml else-->
+&quot;T&quot;.
+<!--chtml endif-->
+To create a new list or add to an existing list, switch the screen display
+into List Mode by pressing
+<!--chtml if pinemode="function_key"-->
+F12.
+<!--chtml else-->
+&quot;L&quot;.
+<!--chtml endif-->
+In List Mode, you select the
+group of addresses you wish to manipulate by marking them with an
+&quot;X&quot;.
+The Set/Unset
+<!--chtml if pinemode="function_key"-->
+(F9)
+<!--chtml else-->
+(&quot;X&quot;)
+<!--chtml endif-->
+command will turn the &quot;X&quot; on for the
+highlighted address if it was off or turn it off if it was previously on.
+The SetAll command will select all of the addresses, and the UnSetAll
+command will turn off all the selections. Once you've gotten the
+selection the way you want it, you may create a new list by pressing
+<!--chtml if pinemode="function_key"-->
+F4.
+<!--chtml else-->
+&quot;T&quot;.
+<!--chtml endif-->
+<P>
+
+In both the simple and list cases, after choosing to take the address,
+you will be asked for the nickname of the entry. Typing in a new name
+creates the new entry/list. Entering an existing nickname will replace
+the entry (simple case) or add to the list (list case). Alternatively,
+you can press Ctrl-T at the nickname prompt and select an existing
+nickname from your address book.
+<P>
+
+You will normally start in Single Mode, unless you used the Apply command
+to startup the TakeAddr screen, in which case you will start in List Mode.
+You may switch between the two modes at any time. If you've already
+selected several addresses in List Mode, those will be remembered when you
+switch to Single Mode and then back to List Mode. The set of addresses
+that are pre-selected when you start in List Mode are the From addresses
+of all of the messages you are operating on. You may, of course, easily
+erase those selections with the UnSetAll command.
+<P>
+
+If you have more than one writable address book, you will be prompted for
+the name of the address book you wish to add the new entry to before
+anything else. You can use ^N and ^P to choose among the defined address
+books, or type in the address book name.
+<P>
+
+FOR MORE INFORMATION on addressing see
+<A HREF="h_address_format">Explanation of Address formats</A>, and the
+<A HREF="h_abook_opened">Address Book Screen's</A>
+help text.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_takeexport_screen =====
+<HTML>
+<HEAD>
+<TITLE>Take Export Screen Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>TAKE EXPORT COMMANDS</H1>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Navigating the List of Addresses Address Selection Commands
+-------------------------------- --------------------------
+ F5 Move to previous entry F3 Exit without taking address
+ F6 Move to next entry F4 Take current address(es)
+ F7 Show previous page of address list
+ F8 Show next page of address list
+ F2 WhereIs (search list)
+ --------------
+Mode Toggle F9 Set/Unset current address
+----------- F10 Set all
+ F12 Toggle between List and single mode F11 Unset all
+</PRE>
+<!--chtml else-->
+<PRE>
+Navigating the List of Addresses Address Selection Commands
+-------------------------------- --------------------------
+ P Move to previous entry &lt; Exit without taking address
+ N Move to next entry T Take address
+ - Show previous page of address list
+Spc (space bar) Show next page of address list
+ W WhereIs (search list) List Mode
+ ---------
+Single Mode X Set/Unset current address
+----------- A Set all addresses
+ L Switch to list mode U Unset all addresses
+ S Switch to single mode
+</PRE>
+<!--chtml endif-->
+
+<H2>Description of the Take Export Screen</H2>
+
+This screen is designed to let you select one or more addresses
+from the current message and put them into a file.
+Only the user@domain_name part of each address is put into the file.
+<P>
+
+To put a single entry into a file simply highlight the correct line and press
+<!--chtml if pinemode="function_key"-->
+F4.
+<!--chtml else-->
+&quot;T&quot;.
+<!--chtml endif-->
+To put more than one entry into a file
+switch the screen display
+into List Mode by pressing
+<!--chtml if pinemode="function_key"-->
+F12.
+<!--chtml else-->
+&quot;L&quot;.
+<!--chtml endif-->
+In List Mode, you select the
+group of addresses you wish to manipulate by marking them with an
+&quot;X&quot;.
+The Set/Unset
+<!--chtml if pinemode="function_key"-->
+(F9)
+<!--chtml else-->
+(&quot;X&quot;)
+<!--chtml endif-->
+command will turn the &quot;X&quot; on for the
+highlighted address if it was off or turn it off if it was previously on.
+The SetAll command will select all of the addresses, and the UnSetAll
+command will turn off all the selections. Once you've gotten the
+selection the way you want it, you may put the addresses in a file by typing
+<!--chtml if pinemode="function_key"-->
+F4.
+<!--chtml else-->
+&quot;T&quot;.
+<!--chtml endif-->
+<P>
+
+You will be asked for the name of a file to put the addresses in.
+If the file already exists, you will be asked whether you want to Overwrite
+(replace) the contents of the file or Append to the contents of the file.
+<P>
+
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+============= h_abook_view ========================
+<HTML>
+<HEAD>
+<TITLE>Address Book View Explained</TITLE>
+</HEAD>
+<BODY>
+This function allows you to view the contents of an address book entry. You
+can only view one entry at a time.
+<P>
+<DL>
+<DT>Help
+<!--chtml if pinemode="function_key"-->
+(F1)
+<!--chtml else-->
+(?)
+<!--chtml endif-->
+</DT>
+<DD>
+Display this help text.
+
+<DT>Abook
+<!--chtml if pinemode="function_key"-->
+(F3)
+<!--chtml else-->
+(&lt;)
+<!--chtml endif-->
+</DT>
+<DD>
+Go back to index of address book entries.
+
+<DT>Update
+<!--chtml if pinemode="function_key"-->
+(F4)
+<!--chtml else-->
+(U)
+<!--chtml endif-->
+</DT>
+<DD>
+Update (modify) this entry.
+
+<DT>ComposeTo
+<!--chtml if pinemode="function_key"-->
+(F5)
+<!--chtml else-->
+(C)
+<!--chtml endif-->
+</DT>
+<DD>Compose a message to the address(es) in this entry.
+
+<DT>Role
+<!--chtml if pinemode="function_key"-->
+(F6)
+<!--chtml else-->
+(#)
+<!--chtml endif-->
+</DT>
+<DD>Compose a message to the address(es) in this entry using roles.
+
+<DT>Prev Page
+<!--chtml if pinemode="function_key"-->
+(F7)
+<!--chtml else-->
+(-)
+<!--chtml endif-->
+</DT>
+<DD>
+Show the previous page of the current entry.
+
+<DT>Next Page
+<!--chtml if pinemode="function_key"-->
+(F8)
+<!--chtml else-->
+(Space)
+<!--chtml endif-->
+</DT>
+<DD>
+Show the next page of the current entry.
+
+<DT>Print
+<!--chtml if pinemode="function_key"-->
+(F9)
+<!--chtml else-->
+(%)
+<!--chtml endif-->
+</DT>
+<DD>Print the current entry. You can select the
+printer or the print command via the &quot;Setup&quot; command
+on the MAIN MENU.
+
+<DT>WhereIs
+<!--chtml if pinemode="function_key"-->
+(F10)
+<!--chtml else-->
+(W)
+<!--chtml endif-->
+</DT>
+<DD>Search the entry for a string of letters. If it is
+found, move to it. The string can be one word or a phrase.
+If there are multiple occurrences, the cursor moves to the
+first occurrence beyond the current cursor position.
+
+<DT>Fwd Email
+<!--chtml if pinemode="function_key"-->
+(F11)
+<!--chtml else-->
+(F)
+<!--chtml endif-->
+</DT>
+<DD>Begin composition of a new mail message with the displayed
+text already inserted in the message body.
+
+</DL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+============= h_ldap_view ========================
+<HTML>
+<HEAD>
+<TITLE>LDAP Response View Explained</TITLE>
+</HEAD>
+<BODY>
+This function allows you to view the contents of a directory entry. You
+can only view one entry at a time.
+<P>
+<DL>
+<DT>Help
+<!--chtml if pinemode="function_key"-->
+(F1)
+<!--chtml else-->
+(?)
+<!--chtml endif-->
+</DT>
+<DD>
+Display this help text.
+
+<DT>Results Index
+<!--chtml if pinemode="function_key"-->
+(F3)
+<!--chtml else-->
+(&lt;)
+<!--chtml endif-->
+</DT>
+<DD>Go back to index of search results.
+
+<DT>ComposeTo
+<!--chtml if pinemode="function_key"-->
+(F5)
+<!--chtml else-->
+(C)
+<!--chtml endif-->
+</DT>
+<DD>Compose a message to the address(es) in this entry.
+
+<DT>Role
+<!--chtml if pinemode="function_key"-->
+(F6)
+<!--chtml else-->
+(#)
+<!--chtml endif-->
+</DT>
+<DD>Compose a message to the address(es) in this entry using roles.
+
+<DT>Prev Page
+<!--chtml if pinemode="function_key"-->
+(F7)
+<!--chtml else-->
+(-)
+<!--chtml endif-->
+</DT>
+<DD>
+Show the previous page of the current entry.
+
+<DT>Next Page
+<!--chtml if pinemode="function_key"-->
+(F8)
+<!--chtml else-->
+(Space)
+<!--chtml endif-->
+</DT>
+<DD>
+Show the next page of the current entry.
+
+<DT>Print
+<!--chtml if pinemode="function_key"-->
+(F9)
+<!--chtml else-->
+(%)
+<!--chtml endif-->
+</DT>
+<DD>Print the current entry on paper. You can select the
+printer or the print command via the &quot;Setup&quot; command
+on the MAIN MENU.
+
+<DT>WhereIs
+<!--chtml if pinemode="function_key"-->
+(F10)
+<!--chtml else-->
+(W)
+<!--chtml endif-->
+</DT>
+<DD>Search the entry for a string of letters. If it is
+found, move to it. The string can be one word or a phrase.
+If there are multiple occurrences, the cursor moves to the
+first occurrence beyond the current cursor position.
+
+<DT>Fwd Email
+<!--chtml if pinemode="function_key"-->
+(F11)
+<!--chtml else-->
+(F)
+<!--chtml endif-->
+</DT>
+<DD>Begin composition of a new mail message with the displayed
+text already inserted in the message body.
+
+<DT>Save
+<!--chtml if pinemode="function_key"-->
+(F12)
+<!--chtml else-->
+(S)
+<!--chtml endif-->
+</DT>
+<DD>Save the displayed entry to one of your address books or export
+it to a file.
+</DL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_attachment_screen =====
+<HTML>
+<HEAD>
+<TITLE>Attachment Index Screen Explained</TITLE>
+</HEAD>
+<BODY>
+The &quot;ATTACHMENT INDEX&quot; displays a list of the current
+message's attachments, and allows various operations on them. The
+first attachment is usually the message text, but does not include the
+header portion of the message.
+<P>
+Available commands include:
+<P>
+<DL>
+
+<DT>Help</DT>
+<DD>Show this help text.
+
+<DT>Msg #<I>num</I></DT>
+<DD>Leave this screen without displaying or saving any attachments.
+
+<DT>View</DT>
+<DD>View the currently selected attachment.
+
+<DT>Prev Attach</DT>
+<DD>Move to previous attachment.
+
+<DT>Next Attach</DT>
+<DD>Move to next attachment.
+
+<DT>Prev Page</DT>
+<DD>Previous page of the listed attachments.
+
+<DT>Next Page</DT>
+<DD>Next page of the listed attachments.
+
+<DT>Delete</DT>
+<DD>Mark the currently selected attachment for Deletion.
+This does not modify the current message by deleting the attachment from
+it, but instead the delete flag <EM>only</EM> has an effect when saving
+the message to a folder.
+Attachments marked for deletion are not copied to the destination folder
+along with the rest of the message when it is saved.
+It is ok for the destination folder to be the same as the current folder.
+In addition, the delete mark <EM>only</EM> applies to this Alpine session.
+
+<DT>Undelete</DT>
+<DD>Turn off the Delete flag for the selected attachment.
+
+<DT>Save</DT>
+<DD>Save the selected attachment to a file. If the attachment is of
+type &quot;RFC822/Message&quot;, then the attachment will be saved to
+the specified mail folder.
+
+<DT>Export</DT>
+<DD>If the attachment is of
+type &quot;RFC822/Message&quot;, then &quot;Export&quot; is used to
+copy the message to a file in the same way this command works on
+messages in the MESSAGE INDEX and MESSAGE TEXT screens.
+
+<DT>Pipe</DT>
+<DD>Pipe the attachment contents into a UNIX command (if enabled).
+A description of the Pipe sub-commands is <A HREF="h_pipe_command">here</A>.
+
+<DT>WhereIs</DT>
+<DD>Find a matching string in the attachment list.
+
+<DT>AboutAttch</DT>
+<DD>Examine various aspects of the selected attachment.
+
+<DT>Print</DT>
+<DD>Print the selected attachment.
+
+<DT>Forward</DT>
+<DD>Forward the selected attachment as an attachment.
+</DL>
+
+<P>
+
+All attachments can be saved or piped into a UNIX command, but some may
+not be readily displayed by either Alpine or an external tool. In such
+cases, the reason why the message cannot be displayed is displayed on
+Alpine's message line.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+============= h_mail_text_att_view ========================
+<HTML>
+<HEAD>
+<TITLE>Attachment View Screen Explained</TITLE>
+</HEAD>
+<BODY>
+This function allows you to view the contents of a text attachment. You
+can only view one attachment at a time.
+<P>
+Available commands include:
+<P>
+<DL>
+
+<DT>Help</DT>
+<DD>Display this help text
+
+<DT>AttchIndex</DT>
+<DD>Leave viewer and return to the &quot;ATTACHMENT INDEX&quot; screen
+
+<DT>Prev Page</DT>
+<DD>Show the previous page of the current attachment.
+
+<DT>Next Page</DT>
+<DD>Show the next page of the current attachment by pressing the space bar.
+
+<DT>Delete</DT>
+<DD>Mark the viewed attachment for Deletion. The delete
+flag <EM>only</EM> has affect when saving the message to a folder.
+Attachments marked for deletion are exluded from the messsage when
+it is saved. In addition, the delete mark <EM>only</EM> applies to
+this Alpine session.
+
+<DT>Undelete</DT>
+<DD>Turn off the Delete flag for the selected attachment.
+
+<DT>Save</DT>
+<DD>Copy the current attachment to a file. If you just enter
+a filename, the attachment will be saved with that name in
+your home directory
+<!--chtml if pinemode="running"-->
+(which, in the present configuration of your system, is
+ &quot;<!--#echo var="HOME_DIR"-->&quot;)
+<!--chtml endif-->
+or current working directory
+<!--chtml if pinemode="running"-->
+(which, at least for your current Alpine &quot;session,&quot;
+is &quot;<!--#echo var="CURRENT_DIR"-->&quot;)
+<!--chtml endif-->, depending on the
+<A HREF="h_config_use_current_dir">&quot;<!--#echo var="FEAT_use-current-dir"-->&quot;</A>
+configuration setting. You may enter the full
+path and filename to save it in another directory instead.
+
+<DT>Export</DT>
+<DD>If the attachment is of
+type &quot;RFC822/Message&quot;, then &quot;Export&quot; is used to
+copy the message to a file in the same way this command works on
+messages in the MESSAGE INDEX and MESSAGE TEXT screens.
+(If you have any <A HREF="h_config_display_filters"><!--#echo var="VAR_display-filters"--></A>
+defined, they may affect the contents of the exported file.)
+
+<DT>Pipe</DT>
+<DD>Pipe the attachment contents into a UNIX command (if enabled)
+
+<DT>WhereIs</DT>
+<DD>Search the attachment for a string of letters. If it is
+found, move to it. The string can be one word or a phrase.
+If there are multiple occurrences, the cursor moves to the
+first occurrence beyond the current cursor position.
+
+<DT>Print</DT>
+<DD>Print the current attachment on paper. You can select the
+printer or the print command via the &quot;Setup&quot; command
+on the MAIN MENU.
+
+<DT>Forward</DT>
+<DD>Forward the selected attachment as an attachment.
+</DL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+============= h_journal ==============
+<HTML>
+<HEAD>
+<TITLE>Recent Message Journal Explained</TITLE>
+</HEAD>
+<BODY>
+
+The following commands are available on this screen:
+<P>
+<DL>
+<DT>Help</DT>
+<DD>Show this help text
+
+<DT>Exit</DT>
+<DD>Exit Viewer, and go back to mail processing
+
+<DT>Prev Page</DT>
+<DD>Show the previous page text
+
+<DT>Next Page</DT>
+<DD>Show the next page of text by pressing the space bar
+
+<DT>Print</DT>
+<DD>Print the displayed text on paper. You can select the
+printer or the print command via the &quot;Setup&quot; command
+on the MAIN MENU.
+
+<DT>Fwd Email</DT>
+<DD>Begin composition of a new mail message with the displayed
+text already inserted in the message body.
+
+<DT>Save</DT>
+<DD>Copy the displayed text to a file. If you just enter
+a filename, the text will be saved with that name in
+your
+home directory
+<!--chtml if pinemode="running"-->
+(which, in the present configuration of your system, is
+ &quot;<!--#echo var="HOME_DIR"-->&quot;)
+<!--chtml endif-->
+or current working directory
+<!--chtml if pinemode="running"-->
+(which, at least for your current Alpine &quot;session,&quot;
+is &quot;<!--#echo var="CURRENT_DIR"-->&quot;)
+<!--chtml endif-->, depending on the
+<A HREF="h_config_use_current_dir">&quot;<!--#echo var="FEAT_use-current-dir"-->&quot;</A>
+configuration setting. You may enter the full
+path and filename to save it in another directory instead.
+
+<DT>WhereIs</DT>
+<DD>Search the text for a string of letters. If it is
+found, move to it. The string can be one word or a phrase.
+If there are multiple occurrences, the cursor moves to the
+first occurrence beyond the current cursor position.
+</DL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+============= h_debugjournal ==============
+<HTML>
+<HEAD>
+<TITLE>Debug Journal Explained</TITLE>
+</HEAD>
+<BODY>
+
+The following commands are available on this screen:
+<P>
+<DL>
+<DT>Help</DT>
+<DD>Show this help text
+
+<DT>Exit</DT>
+<DD>Exit Viewer, and go back to mail processing
+
+<DT>Timestamps</DT>
+<DD>Turn on or off timestamps.
+
+<DT>DebugView</DT>
+<DD>Set the level of debugging you want to see. The level may be any number
+in the range 0-9. Higher numbers show more debugging detail. Note that the
+debugging information has already been captured. This setting just causes the
+debugging information that you see to be filtered. If you set this to
+the number &quot;5&quot; then you will be shown all of the debugging information
+at levels 5 and below.
+It's actually a bit more complicated than that. A fixed amount of memory
+is used to store the debug information.
+Since the amount of memory used is limited the debugging information
+has to be trimmed back when it gets too large.
+
+<DT>Prev Page</DT>
+<DD>Show the previous page text
+
+<DT>Next Page</DT>
+<DD>Show the next page of text by pressing the space bar
+
+<DT>Print</DT>
+<DD>Print the displayed text on paper. You can select the
+printer or the print command via the &quot;Setup&quot; command
+on the MAIN MENU.
+
+<DT>Fwd Email</DT>
+<DD>Begin composition of a new mail message with the displayed
+text already inserted in the message body.
+
+<DT>Save</DT>
+<DD>Copy the displayed text to a file. If you just enter
+a filename, the text will be saved with that name in
+your
+home directory
+<!--chtml if pinemode="running"-->
+(which, in the present configuration of your system, is
+ &quot;<!--#echo var="HOME_DIR"-->&quot;)
+<!--chtml endif-->
+or current working directory
+<!--chtml if pinemode="running"-->
+(which, at least for your current Alpine &quot;session,&quot;
+is &quot;<!--#echo var="CURRENT_DIR"-->&quot;)
+<!--chtml endif-->, depending on the
+<A HREF="h_config_use_current_dir">&quot;<!--#echo var="FEAT_use-current-dir"-->&quot;</A>
+configuration setting. You may enter the full
+path and filename to save it in another directory instead.
+
+<DT>WhereIs</DT>
+<DD>Search the text for a string of letters. If it is
+found, move to it. The string can be one word or a phrase.
+If there are multiple occurrences, the cursor moves to the
+first occurrence beyond the current cursor position.
+</DL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+============= h_simple_text_view ==============
+<HTML>
+<HEAD>
+<TITLE>Simple Text View Screen Explained</TITLE>
+</HEAD>
+<BODY>
+
+The following commands are available on this screen:
+<P>
+<DL>
+<DT>Help</DT>
+<DD>Show this help text
+
+<DT>Exit</DT>
+<DD>Exit Viewer, and go back to mail processing
+
+<DT>Prev Page</DT>
+<DD>Show the previous page text
+
+<DT>Next Page</DT>
+<DD>Show the next page of text by pressing the space bar
+
+<DT>Print</DT>
+<DD>Print the displayed text on paper. You can select the
+printer or the print command via the &quot;Setup&quot; command
+on the MAIN MENU.
+
+<DT>Fwd Email</DT>
+<DD>Begin composition of a new mail message with the displayed
+text already inserted in the message body.
+
+<DT>Save</DT>
+<DD>Copy the displayed text to a file. If you just enter
+a filename, the attachment will be saved with that name in
+your
+home directory
+<!--chtml if pinemode="running"-->
+(which, in the present configuration of your system, is
+ &quot;<!--#echo var="HOME_DIR"-->&quot;)
+<!--chtml endif-->
+or current working directory
+<!--chtml if pinemode="running"-->
+(which, at least for your current Alpine &quot;session,&quot;
+is &quot;<!--#echo var="CURRENT_DIR"-->&quot;)
+<!--chtml endif-->, depending on the
+<A HREF="h_config_use_current_dir">&quot;<!--#echo var="FEAT_use-current-dir"-->&quot;</A>
+configuration setting. You may enter the full
+path and filename to save it in another directory instead.
+
+<DT>WhereIs</DT>
+<DD>Search the attachment for a string of letters. If it is
+found, move to it. The string can be one word or a phrase.
+If there are multiple occurrences, the cursor moves to the
+first occurrence beyond the current cursor position.
+</DL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_pine_for_windows ========
+<HTML>
+<HEAD>
+<TITLE>GETTING HELP IN PC-ALPINE</TITLE>
+</HEAD>
+<BODY>
+<H1>Getting Help In PC-Alpine</H1>
+
+<P>
+PC-Alpine offers general and specific help text. From the <A
+HREF="main_menu_tx">MAIN MENU</A>, you will find an overview in the MAIN
+MENU HELP and the <A HREF="h_news">Release Notes</A>. On all screens,
+specific help for that screen is available from the toolbar Help menu or
+with the
+<!--chtml if pinemode="function_key"-->
+&quot;F1&quot; key.
+<!--chtml else-->
+&quot;?&quot; or &quot;Ctrl-G&quot; keys. &quot;Ctrl-G&quot; is used where
+typing &quot;?&quot; would be mistaken as entering text.
+<!--chtml endif-->
+
+<P>
+Although this version of Alpine is for Microsoft Windows, it is not
+considered a full &quot;Graphical User Interface&quot; application.
+Yet, many of the controls that Windows users are accustomed to seeing,
+such as scrollbars and toolbars, are available.
+
+<P>
+PC-Alpine offers considerable mouse support. You can view what is
+&quot;click-able&quot; by dragging your mouse over any screen; when the
+arrow cursor changes into a hand, you found something. Mouse-click
+possibilities include navigating between screens and folders and
+double-clicking on hyperlinks to open your Web browser.
+Context-sensitive pop-up menus appear with a right-click on your PC-Alpine
+screen. Examples of right-click options include &quot;copy&quot; after
+selecting text to copy and &quot;View in New Window&quot; when you click
+on a particular message in the Message Index. The menu choices available
+to you will vary based upon what screen is open, where on the screen your
+cursor is located, and even what action you have already taken.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_composer =====
+<HTML>
+<HEAD>
+<TITLE>COMPOSER COMMANDS</TITLE>
+</HEAD>
+<BODY>
+<H1>COMPOSER COMMANDS</H1>
+
+CURSOR&nbsp;MOTION&nbsp;KEYS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|EDITING&nbsp;KEYS<BR>
+&nbsp;&nbsp;^B&nbsp;(Left&nbsp;Arrow)&nbsp;&nbsp;&nbsp;Back&nbsp;character&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;^D&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Delete&nbsp;current&nbsp;character<BR>
+&nbsp;&nbsp;^F&nbsp;(Right&nbsp;Arrow)&nbsp;&nbsp;Forward&nbsp;character&nbsp;&nbsp;|&nbsp;^H&nbsp;(DEL)&nbsp;Delete&nbsp;previous&nbsp;character<BR>
+&nbsp;&nbsp;^P&nbsp;(Up&nbsp;Arrow)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Previous&nbsp;line&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;^^&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Set&nbsp;a&nbsp;<A HREF="h_compose_markcutpaste">mark</A><BR>
+&nbsp;&nbsp;^N&nbsp;(Down&nbsp;Arrow)&nbsp;&nbsp;&nbsp;Next&nbsp;line&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;<!--chtml if pinemode="function_key"-->F9<!--chtml else-->^K<!--chtml endif-->&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="h_compose_markcutpaste">Cut</A>&nbsp;marked&nbsp;text&nbsp;or<BR>
+&nbsp;&nbsp;^A&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Beginning&nbsp;of&nbsp;line&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;delete&nbsp;current&nbsp;line<BR>
+&nbsp;&nbsp;^E&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;End&nbsp;of&nbsp;line&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;<!--chtml if pinemode="function_key"-->F10<!--chtml else-->^U<!--chtml endif-->&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="h_compose_markcutpaste">Paste</A>&nbsp;text,&nbsp;undelete&nbsp;lines<BR>
+&nbsp;&nbsp;^Y&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Previous&nbsp;page&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cut&nbsp;with&nbsp;^K,&nbsp;or&nbsp;unjustify<BR>
+&nbsp;&nbsp;^V&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Next&nbsp;page&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|-------------------------------------<BR>
+&nbsp;&nbsp;^@&nbsp;(Ctrl-SPACE)&nbsp;&nbsp;&nbsp;Next&nbsp;word&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|SCREEN/COMPOSITION&nbsp;COMMANDS<BR>
+---------------------------------------|&nbsp;<!--chtml if pinemode="function_key"-->F6<!--chtml else-->^W<!--chtml endif-->&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="h_composer_search">Whereis</A>&nbsp;(search&nbsp;for&nbsp;string)<BR>
+MESSAGE&nbsp;COMMANDS&nbsp;|&nbsp;GENERAL&nbsp;COMMANDS&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;<!--chtml if pinemode="function_key"-->F12<!--chtml else-->^T&nbsp;<!--chtml endif-->&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="h_compose_spell">Spell&nbsp;checker</A><BR>
+&nbsp;<!--chtml if pinemode="function_key"-->F3<!--chtml else-->^C<!--chtml endif-->&nbsp;&nbsp;&nbsp;<A HREF="h_compose_cancel">Cancel</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;<!--chtml if pinemode="function_key"-->F1<!--chtml else-->^G<!--chtml endif-->&nbsp;&nbsp;&nbsp;&nbsp;Get&nbsp;help&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;<!--chtml if pinemode="function_key"-->F4<!--chtml else-->^J<!--chtml endif-->&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="h_compose_justify">Justify</A>&nbsp;paragraph<BR>
+&nbsp;<!--chtml if pinemode="function_key"-->F11<!--chtml else-->^O&nbsp;<!--chtml endif-->&nbsp;&nbsp;<A HREF="h_common_postpone">Postpone</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;^Z&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="h_common_suspend">Suspend</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;^L&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Redraw&nbsp;Screen<BR>
+&nbsp;<!--chtml if pinemode="function_key"-->F2<!--chtml else-->^X<!--chtml endif-->&nbsp;&nbsp;&nbsp;<A HREF="h_compose_send">Send</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;<!--chtml if pinemode="function_key"-->F6<!--chtml else-->^_<!--chtml endif-->&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="h_compose_alted">Alt.&nbsp;editor</A>&nbsp;&nbsp;|&nbsp;<!--chtml if pinemode="function_key"-->F5<!--chtml else-->^R<!--chtml endif-->&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="h_compose_readfile">Read&nbsp;in&nbsp;a&nbsp;file</A><BR>
+
+<P>
+NOTE:
+<OL>
+ <LI>For help on a particular command, highlight the bold text associated
+with it above and hit Return.
+ <LI> The availability of certain commands
+is determined by Alpine configuration files and system capabilities.
+At some sites, certain commands may not be available due to security or
+support concerns.
+ <LI>Alpine does not use the following keys: Ctrl-S, Ctrl-Q, Ctrl-],
+Ctrl-&#92;, ESC.
+ <LI>For special handling of Ctrl-S and Ctrl-Q see special comments regarding
+<A HREF="h_special_xon_xoff">&quot;XOFF/XON&quot;</A>.
+</OL>
+
+<P>
+HINT: To move rapidly to the bottom of a message you are composing,
+enter ^W^V. To go to the top, ^W^Y. These can be used in conjunction
+with the Mark and Cut commands to eliminate large amounts of unwanted
+text in a Reply.
+
+<H2>Description of Composer</H2>
+
+Alpine has a built-in editing program that allows you to compose messages
+without having to leave Alpine. The editor is designed to be very simple to
+use so that you can get started writing email right away.
+
+<P>
+Messages are usually just text, about 80 columns wide. Using upper and
+lower case is encouraged. On some systems the size limit of the message
+is about 100,000 characters, which is about 2,000 lines. You can include
+punctuation and special characters found on most keyboards, but you can't
+include characters with diacritical marks and certain special symbols.
+
+<P>
+Text automatically wraps as you type past the end of a line so you do not
+have to hit return. Using the
+&quot;<A HREF="h_compose_justify">Justify</A>&quot; command,
+you can also reformat text explicitly, perhaps after you have
+deleted some text.
+
+<P>
+You can include other text files with the
+&quot;<A HREF="h_compose_readfile">Read File</A>&quot; command,
+which will prompt you for the name of the file to insert at the
+current cursor postion.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_composer_browse =====
+<HTML>
+<HEAD>
+<TITLE>BROWSER</TITLE>
+</HEAD>
+<BODY>
+<H1>BROWSER</H1>
+This screen lets you browse your files and directories. To go to another
+directory (identified by &quot;(dir)&quot;), move the cursor to it and
+choose &quot;Select&quot; (the default choice on the menu);
+or choose &quot;Goto&quot; and enter the name of the directory.
+<!--chtml if pinemode="os_windows"-->
+<!--chtml else-->
+In Unix Alpine, you may use
+&quot;~&quot; to refer to your home directory or &quot;~user&quot; to refer
+to another's home directory.
+<!--chtml endif--><P>
+To select a file, move the cursor to it and
+choose &quot;Select&quot; (the default choice on the menu).
+<P>
+<UL>
+<LI>Note <B>if</B> you are currently using the BROWSER for choosing a file for
+inclusion in the
+message body (that is, you chose &quot;Read File&quot; with the cursor under
+the
+&quot;----- Message Text -----&quot; line
+while composing, then &quot;To Files&quot;): Since the file
+selected will become part of the message text, it must be in a format
+suitable for that (Alpine does not check!), such as a plain text file.
+Files of other formats (for example, graphics, databases, software
+programs) should be
+<B>attached</B> to the message instead --
+by moving the cursor in the COMPOSE MESSAGE screen into the
+message header area and pressing
+<!--chtml if pinemode="function_key"-->
+F6.
+<!--chtml else-->
+Ctrl-J.
+<!--chtml endif-->
+
+<P><LI>
+Note <B>if</B> you are currently using the BROWSER for saving a message
+attachment, or exporting a message, to a file: You can use the Add command to
+provide the name for a new file to save/export to, and then select that name
+to use it for the save/export operation. Back at the prompt
+&quot;EXPORT: Copy message to file in ...&quot; hit Enter, then choose
+either Overwrite or Append (it doesn't make a difference, since the file is
+so far empty). Note: If you cancel the
+operation at that point, the file created with the Add command will remain
+0 bytes in size.
+
+</UL>
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_composer_ins =====
+<HTML>
+<HEAD>
+<TITLE>INSERT TEXT FILE</TITLE>
+</HEAD>
+<BODY>
+<H1>INSERT TEXT FILE</H1>
+
+Use this function to insert a text file. The file name
+given can be an absolute file path name for your system
+<!--chtml if pinemode="os_windows"-->
+(for example, &quot;H:&#92;SIGFILES&#92;FULLINFO.TXT&quot;), a file
+with a relative pathname, or simply a file name without
+drive or directory specification.
+<!--chtml else-->
+(for example, &quot;/tmp/exported.earlier&quot; on Unix hosts),
+a file in your home directory, or a file path relative to your
+home directory. In Unix Alpine, you may use &quot;~&quot; to refer to
+your home directory or &quot;~user&quot; to refer to another
+account's home directory.
+<!--chtml endif-->
+<P>
+No wild card characters may be used.
+The file must reside on the system running Alpine.
+<P>
+If the
+<A HREF="h_config_use_current_dir">&quot;<!--#echo var="FEAT_use-current-dir"-->&quot;</A>
+feature is set, names are relative to your current working directory
+<!--chtml if pinemode="running"-->
+(which, at least for your current Alpine &quot;session,&quot;
+is &quot;<!--#echo var="CURRENT_DIR"-->&quot;)
+<!--chtml endif-->
+rather than your home directory
+<!--chtml if pinemode="running"-->
+(which, in the present configuration of your system, is
+ &quot;<!--#echo var="HOME_DIR"-->&quot;)
+<!--chtml endif-->
+.
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_composer_ins_m =====
+<HTML>
+<HEAD>
+<TITLE>INSERT MESSAGE</TITLE>
+</HEAD>
+<BODY>
+<H1>INSERT MESSAGE</H1>
+
+Type in the number of a message in the currently open folder to insert it
+into your message.
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_composer_search =====
+<HTML>
+<HEAD>
+<TITLE>Explanation of Composer Whereis Command </TITLE>
+</HEAD>
+<BODY>
+<H1>Help For Whereis Command</H1>
+
+Whereis is used to search the message for a word or part of a word.
+When searching in the composer, only the message part of your mail is
+searched, and the cursor is put on the first occurrence appearing
+after the location of the cursor. The search will wrap to the
+beginning of the message when it no longer finds matches in the
+remainder of the message.
+
+To search for the same string a second time, press
+<!--chtml if pinemode="function_key"-->
+&quot;F6&quot;
+<!--chtml else-->
+&quot;^W&quot;
+<!--chtml endif-->
+to begin search and then just press RETURN to accept the previous
+search string shown in square brackets rather than entering a new
+search string.<P>
+
+The &quot;Search&quot; prompt has several sub-command available:
+
+<DL>
+<DT>Get Help</DT>
+<DD> Takes you to this help page.
+
+<DT>Cancel</DT>
+<DD> Cancels the prompt. No search takes place.
+
+<DT>First Line</DT>
+<DD> Takes you back to the composer with the cursor on the first character
+of the first line of text.
+
+<DT>Last Line</DT>
+<DD> Takes you back to the composer with the cursor on the last character
+of the last line of text.
+
+<DT>Replace (Optional)</DT>
+<DD> This sub-command is enabled by the
+<A HREF="h_config_enable_search_and_repl">&quot;<!--#echo var="FEAT_enable-search-and-replace"-->&quot;</A>
+feature (which is on by default); see its help screen for details on how replacing works.
+
+</DL>
+
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_sigedit_search =====
+<HTML>
+<HEAD>
+<TITLE>Explanation of Whereis Command </TITLE>
+</HEAD>
+<BODY>
+<H1>Help For Whereis Command</H1>
+
+Whereis is used to search for a word or part of a word.
+When searching the cursor is put on the first occurrence appearing
+after the location of the cursor. The search will wrap to the
+beginning of the signature when it no longer finds matches in the
+remainder of the signature.
+
+To search for the same string a second time, press
+<!--chtml if pinemode="function_key"-->
+&quot;F6&quot;
+<!--chtml else-->
+&quot;^W&quot;
+<!--chtml endif-->
+to begin search and then just press RETURN to accept the previous
+search string shown in square brackets rather than entering a new
+search string.<P>
+
+The &quot;Search&quot; prompt has several sub-command available:
+
+<DL>
+<DT>Get Help</DT>
+<DD> Takes you to this help page.
+
+<DT>Cancel</DT>
+<DD> Cancels the prompt. No search takes place.
+
+<DT>First Line</DT>
+<DD> Takes you back to the composer with the cursor on the first character
+of the first line of text.
+
+<DT>Last Line</DT>
+<DD> Takes you back to the composer with the cursor on the last character
+of the last line of text.
+
+<DT>Replace (Optional)</DT>
+<DD> This sub-command is enabled by the
+<A HREF="h_config_enable_search_and_repl">&quot;<!--#echo var="FEAT_enable-search-and-replace"-->&quot;</A>
+feature (which is on by default); see its help screen for details on how replacing works.
+
+</DL>
+
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_composer_to ====
+<HTML>
+<HEAD>
+<TITLE>THE MESSAGE COMPOSER'S TO FIELD</TITLE>
+</HEAD>
+<BODY>
+<H1>THE MESSAGE COMPOSER'S TO FIELD</H1>
+
+<H2>The &quot;To:&quot; field</H2>
+The address you enter here must be a valid email address that is reachable
+from your site.
+
+<H2>Email Address Format</H2>
+You may enter a full name and email address,
+<!--chtml if pinemode="os_windows"-->
+<!--chtml else-->
+a local (meaning, on the same
+host as the one you are running Alpine on) username that Alpine will
+complete for you,
+<!--chtml endif-->
+the nickname of someone in a
+<A HREF="h_abook_opened">Alpine Address Book</A>, or a local
+mail alias defined by your system administrator. When you move the cursor
+out of this field, the nicknames will be expanded to the addresses in your
+address book, and the local usernames will be expanded to include the
+persons' actual names. You may enter as many addresses as you wish, but they
+must be separated by commas. You can move around this and other header fields
+with the arrow keys and use many of the usual composer editing keys.
+<P>
+<UL>
+<LI><A HREF="h_address_format">Explanation of Address formats</A>
+</UL>
+
+<P>
+<H2>MESSAGE HEADER COMMANDS</H2>
+<!--chtml if pinemode="function_key"-->
+CURSOR&nbsp;MOTION&nbsp;KEYS----------------------|EDITING&nbsp;KEYS-------------------------<BR>
+^B&nbsp;(Left&nbsp;Arrow)&nbsp;&nbsp;&nbsp;&nbsp;Back&nbsp;character&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;^D&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Delete&nbsp;current&nbsp;character<BR>
+^F&nbsp;(Right&nbsp;Arrow)&nbsp;&nbsp;&nbsp;Forward&nbsp;character&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;^H&nbsp;(DEL)&nbsp;Delete&nbsp;previous&nbsp;character<BR>
+^P&nbsp;(Up&nbsp;Arrow)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Previous&nbsp;line&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<BR>
+^N&nbsp;(Down&nbsp;Arrow)&nbsp;&nbsp;&nbsp;&nbsp;Next&nbsp;line&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;F9&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Cut&nbsp;marked&nbsp;text&nbsp;or<BR>
+^A&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Beginning&nbsp;of&nbsp;line&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;delete&nbsp;current&nbsp;line<BR>
+^E&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;End&nbsp;of&nbsp;line&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;F10&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Undelete&nbsp;line(s)<BR>
+F7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Previous&nbsp;page&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;^W&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="h_composer_search">Whereis</A>&nbsp;(search&nbsp;text)<BR>
+F8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Next&nbsp;page&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|-------------------------------------<BR>
+^@&nbsp;(Ctrl-SPACE)&nbsp;&nbsp;&nbsp;&nbsp;Next&nbsp;word&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|SCREEN/COMPOSITION&nbsp;COMMANDS<BR>
+----------------------------------------|<BR>
+MESSAGE&nbsp;COMMANDS&nbsp;&nbsp;|&nbsp;GENERAL&nbsp;COMMANDS&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;F12&nbsp;&nbsp;&nbsp;&nbsp;To&nbsp;Addressbook/Browser<BR>
+F3&nbsp;&nbsp;&nbsp;<A HREF="h_compose_cancel">Cancel</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;F1&nbsp;&nbsp;&nbsp;&nbsp;Get&nbsp;help&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;F4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Attach&nbsp;File<BR>
+F11&nbsp;&nbsp;<A HREF="h_common_postpone">Postpone</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;^Z&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="h_common_suspend">Suspend</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;^L&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Redraw&nbsp;Screen<BR>
+F2&nbsp;&nbsp;&nbsp;<A HREF="h_compose_send">Send</A>&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;F5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="h_compose_richhdr">Rich&nbsp;Headers</A><BR>
+<!--chtml else-->
+CURSOR&nbsp;MOTION&nbsp;KEYS----------------------|EDITING&nbsp;KEYS-------------------------<BR>
+^B&nbsp;(Left&nbsp;Arrow)&nbsp;&nbsp;&nbsp;&nbsp;Back&nbsp;character&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;^D&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Delete&nbsp;current&nbsp;character<BR>
+^F&nbsp;(Right&nbsp;Arrow)&nbsp;&nbsp;&nbsp;Forward&nbsp;character&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;^H&nbsp;(DEL)&nbsp;Delete&nbsp;previous&nbsp;character<BR>
+^P&nbsp;(Up&nbsp;Arrow)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Previous&nbsp;line&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;<BR>
+^N&nbsp;(Down&nbsp;Arrow)&nbsp;&nbsp;&nbsp;&nbsp;Next&nbsp;line&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;^K&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Cut&nbsp;marked&nbsp;text&nbsp;or<BR>
+^A&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Beginning&nbsp;of&nbsp;line&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;delete&nbsp;current&nbsp;line<BR>
+^E&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;End&nbsp;of&nbsp;line&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;^U&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Undelete&nbsp;line(s)<BR>
+^Y&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Previous&nbsp;page&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<BR>
+^V&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Next&nbsp;page&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|-------------------------------------<BR>
+^@&nbsp;(Ctrl-SPACE)&nbsp;&nbsp;&nbsp;&nbsp;Next&nbsp;word&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|SCREEN/COMPOSITION&nbsp;COMMANDS<BR>
+----------------------------------------|&nbsp;^R&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="h_compose_richhdr">Rich&nbsp;Headers</A><BR>
+MESSAGE&nbsp;COMMANDS&nbsp;&nbsp;|&nbsp;GENERAL&nbsp;COMMANDS&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;^T&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;To&nbsp;Addressbook/Browser<BR>
+^C&nbsp;&nbsp;&nbsp;<A HREF="h_compose_cancel">Cancel</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;^G&nbsp;&nbsp;&nbsp;&nbsp;Get&nbsp;help&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;^J&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Attach&nbsp;File<BR>
+^O&nbsp;&nbsp;&nbsp;<A HREF="h_common_postpone">Postpone</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;^Z&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="h_common_suspend">Suspend</A>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;^L&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Redraw&nbsp;Screen<BR>
+^X&nbsp;&nbsp;&nbsp;<A HREF="h_compose_send">Send</A>&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;TAB&nbsp;&nbsp;&nbsp;&nbsp;<A HREF="h_compose_addrcomplete">Address&nbsp;Completion</A><BR>
+<!--chtml endif-->
+
+<P>
+NOTE:
+<OL>
+ <LI>For help on a particular command, highlight the bold text associated
+with it above and hit Return.
+ <LI> The availability of certain commands
+is determined by Alpine configuration files and system capabilities.
+At some sites, certain commands may not be available due to security or
+support concerns.
+</OL>
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_composer_cc ====
+<HTML>
+<HEAD>
+<TITLE>THE MESSAGE COMPOSER'S CC FIELD</TITLE>
+</HEAD>
+<BODY>
+<H1>THE MESSAGE COMPOSER'S CC FIELD</H1>
+The Cc: field is just like the To: field, except it is used for addressees
+that you wish to send a &quot;carbon&quot; copy to. That is, the message is
+not directly meant directly &quot;for&quot; these recipients, but you wanted
+them to see the message. The only difference the recipients see is that their
+name is in the Cc: field, rather than the To: field.
+<P>
+For help with Cc: field editing
+commands, check the <A HREF="h_composer_to">Help for the To:</A> header.
+<P>
+<UL>
+<LI><A HREF="h_address_format">Explanation of Address formats</A>
+</UL>
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_composer_bcc ====
+<HTML>
+<HEAD>
+<TITLE>THE MESSAGE COMPOSER'S BCC FIELD</TITLE>
+</HEAD>
+<BODY>
+<H1>THE MESSAGE COMPOSER'S BCC FIELD</H1>
+The &quot;Bcc:&quot; (Blind carbon copy) header is used when you wish to send
+a copy of the message to one or more people whose addresses you do not
+wish disclosed, either to reduce clutter or for confidentiality.
+<P>
+The format of the Bcc: field is just the same as the To: and Cc: fields in
+the way the addresses are entered. The recipients listed here will
+receive a copy of the message, but --assuming your site's mail transport
+software is properly configured-- their addresses will not show up in the
+headers of the message, as delivered to all of the recipients. The To:
+and Cc: recipients will not know a copy was sent to the Bcc: recipients.
+<P>
+Note: if there is no To: or Cc: or Lcc: address in the message, Alpine
+will automatically generate and place in the To: field a pseudo-address of
+ &quot;undisclosed-recipients: ;&quot;
+or whatever string has been specified in the
+<A HREF="h_config_empty_hdr_msg">&quot;<!--#echo var="VAR_empty-header-message"-->&quot;</A>
+variable.
+<P>
+The reason for this is to avoid embarrassment caused by some Internet
+mail transfer software that interprets a &quot;missing&quot; To: header as
+an error and replaces it with an Apparently-to: header that may contain
+the addresses you entered on the Bcc: line. In addition, it may be
+less disconcerting to Bcc: recipients to see <B>something</B> in the To: field.
+<P>
+You can manipulate what text ends up on the (originally) empty To:
+field. Just remember to put a colon and semicolon at the end of the
+field, which is a special notation denoting that it is not a real address.
+<P>
+For information on message header editing
+commands, check the <A HREF="h_composer_to">Help for the To:</A> header.
+<P>
+<UL>
+<LI><A HREF="h_address_format">Explanation of Address formats</A>
+</UL>
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_composer_lcc ====
+<HTML>
+<HEAD>
+<TITLE>THE MESSAGE COMPOSER'S LCC FIELD</TITLE>
+</HEAD>
+<BODY>
+<H1>THE MESSAGE COMPOSER'S LCC FIELD</H1>
+The &quot;Lcc:&quot; (List carbon copy) header is intended to be used when
+you wish to send a message to a list of people but avoid having all
+of their addresses visible, in order to reduce clutter when the
+message is received.
+<P>
+It is similar to the
+<A HREF="h_composer_bcc">&quot;Bcc&quot; (Blind carbon copy) header</A>
+in that individual
+addressees are hidden, but Lcc is designed to work specifically with
+distribution lists you have created in your
+<A HREF="h_abook_opened">Alpine Address Book</A>. Placing
+the nickname of the list on the Lcc line will result in the full name of
+your Alpine Address Book list being placed on the To: line of the message,
+using a special notation that distinguishes it from a real address. You
+must leave the To: line blank for your list name to appear there.
+<P>
+For example, if you have this list entered in your Address Book:<PRE>
+
+ largo Key Largo List DISTRIBUTION LIST:
+ bogie@mgm.com
+ lauren@mgm.com
+ walter@mgm.com</PRE>
+
+
+and you enter &quot;largo&quot; on the Lcc: line while composing a message,
+the result is:<PRE>
+
+ To : Key Largo List: ;
+ Cc :
+ Bcc :
+ Fcc : sent-mail
+ Lcc : Key Largo List &lt;bogie@mgm.com&gt;,
+ lauren@mgm.com,
+ walter@mgm.com
+ Subject :</PRE>
+
+Each recipient listed on the Lcc: line receives a copy of the message
+without their address being visible (as though they were listed on the
+Bcc: line). The colon-semicolon notation used to put the full-name of the
+list on the To: line is a special address format that doesn't specify any
+actual addressees, but does give some information to the recipients of the
+message.
+<P>
+Note: if after entering an LCC, you delete the list name that is placed
+on the To: line, then recipients will see <PRE>
+ To: undisclosed-recipients: ;</PRE>
+
+(or whatever string is defined in the
+<A HREF="h_config_empty_hdr_msg">&quot;<!--#echo var="VAR_empty-header-message"-->&quot;</A>
+variable) just as in the BCC case.
+<P>
+For help with Lcc: field editing
+commands, check the <A HREF="h_composer_to">Help for the To:</A> header.
+<P>
+<UL>
+<LI><A HREF="h_address_format">Explanation of Address formats</A>
+</UL>
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_composer_from =======
+<HTML>
+<HEAD>
+<TITLE>THE MESSAGE COMPOSER'S FROM FIELD</TITLE>
+</HEAD>
+<BODY>
+<H1>THE MESSAGE COMPOSER'S FROM FIELD</H1>
+
+This header carries your return address. It is the address toward which
+replies (and often, future unrelated correspondence) will be directed,
+unless you have <A HREF="h_config_custom_hdrs">defined an optional
+&quot;Reply-To:&quot; header</A> in the SETUP CONFIGURATION screen. Make
+sure this address is correct.
+<P>
+For help with message header editing
+commands, check the <A HREF="h_composer_to">Help for the To:</A> header.
+<P>
+<UL>
+<LI><A HREF="h_address_format">Explanation of Address formats</A>
+</UL>
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_composer_reply_to =======
+<HTML>
+<HEAD>
+<TITLE>THE MESSAGE COMPOSER'S REPLY-TO FIELD</TITLE>
+</HEAD>
+<BODY>
+<H1>THE MESSAGE COMPOSER'S REPLY-TO FIELD</H1>
+
+Most people should not need this header. The Reply-To: header is used in
+cases where you would like replies to your messages to be directed to an
+address other than your normal &quot;From:&quot; address. This is atypical,
+but can happen when you use multiple machines and do not have the same account
+name on each one, or when you wish to direct certain replies to accounts
+or folders designated for specific classes of correspondence.
+<P>
+For help with message header editing
+commands, check the <A HREF="h_composer_to">Help for the To:</A> header.
+<P>
+<UL>
+<LI><A HREF="h_address_format">Explanation of Address formats</A>
+</UL>
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_composer_custom_addr ====
+<HTML>
+<HEAD>
+<TITLE>CUSTOMIZED HEADER FIELD</TITLE>
+</HEAD>
+<BODY>
+<H1>CUSTOMIZED HEADER FIELD</H1>
+This is a customized header, i.e. not one that is part of Alpine's normal
+set of Compose headers.
+<P>
+For help with message header editing
+commands, check the <A HREF="h_composer_to">Help for the To:</A> header.
+<P>
+<UL>
+<LI><A HREF="h_address_format">Explanation of Address formats</A>
+</UL>
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_composer_custom_free ====
+<HTML>
+<HEAD>
+<TITLE>CUSTOMIZED HEADER FIELD</TITLE>
+</HEAD>
+<BODY>
+<H1>CUSTOMIZED HEADER FIELD</H1>
+This is a customized header, i.e. not one that is part of Alpine's normal
+set of Compose headers.
+<P>
+This field consists of arbitrary text.
+<P>
+For help with message header editing
+commands, check the <A HREF="h_composer_to">Help for the To:</A> header.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_composer_news =====
+<HTML>
+<HEAD>
+<TITLE>THE MESSAGE COMPOSER'S NEWSGRPS LINE</TITLE>
+</HEAD>
+<BODY>
+<h1>THE MESSAGE COMPOSER'S NEWSGRPS LINE</h1>
+Use the newsgroups line to specify any and all USENET newsgroups to which
+your message should be posted. When composing a message from scratch, this
+line may be hidden. If so, just press the rich headers command
+(<!--chtml if pinemode="function_key"-->F5<!--chtml else-->^R<!--chtml endif-->)
+to make it visible.
+<P>
+<EM>Be aware</EM> that when you post to a newsgroup thousands of
+people will be reading your message. Also, you or your system manager
+must have defined an &quot;<!--#echo var="VAR_nntp-server"-->&quot; in your Alpine configuration
+in order for you to be able to post.
+<P>
+For help with message header editing
+commands, check the <A HREF="h_composer_to">Help for the To:</A> header.
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_composer_fcc ====
+<HTML>
+<HEADER>
+<TITLE>THE MESSAGE COMPOSER'S FCC FIELD</TITLE>
+</HEADER>
+<BODY>
+<H1>THE MESSAGE COMPOSER'S FCC FIELD</H1>
+The FCC (File Carbon Copy) specifies the folder used to keep a copy of
+each outgoing message. The default value can be configured with the
+&quot;<!--#echo var="VAR_default-fcc"-->&quot; and &quot;<!--#echo var="VAR_fcc-name-rule"-->&quot; options. You can change or remove
+the file carbon copy on any message you send by editing the FCC header.<p>
+
+You may type ^T to get a list of all your folders and select one to use as
+the FCC for this message.<P>
+
+For help with message header editing
+commands, check the <A HREF="h_composer_to">Help for the To:</A> header.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_composer_subject ====
+<HTML>
+<HEADER>
+<TITLE>THE MESSAGE COMPOSER'S SUBJECT FIELD</TITLE>
+</HEADER>
+<BODY>
+<H1>THE MESSAGE COMPOSER'S SUBJECT FIELD</H1>
+
+The subject header provides a place to enter a few words that summarize
+the topic of the message you are sending. You may leave this line blank,
+but it is considered a courtesy to use a meaningful subject.<p>
+
+For help with message header editing
+commands, check the <A HREF="h_composer_to">Help for the To:</A> header.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_composer_attachment ====
+<HTML>
+<HEAD>
+<TITLE>THE MESSAGE COMPOSER'S ATTCHMNT FIELD</TITLE>
+</HEAD>
+<BODY>
+<H1>THE MESSAGE COMPOSER'S ATTCHMNT FIELD</H1>
+
+The &quot;Attchmnt:&quot; field is where you specify what file or
+files you'd like attached to
+the message you are composing. Those files must reside on the machine
+running Alpine. If your file is on a PC or Mac and you run Alpine with an
+account on a Unix machine, you'll have to transfer it before attaching it.
+Contact local computer support people for assistance with transferring.
+
+<P>
+The file name
+given can be an absolute file path name for your system
+<!--chtml if pinemode="os_windows"-->
+(for example, &quot;H:&#92;SIGFILES&#92;FULLINFO.TXT&quot;), a file
+with a relative pathname, or simply a file name without
+drive or directory specification.
+<!--chtml else-->
+(for example, &quot;/tmp/exported.earlier&quot; on Unix hosts),
+a file in your home directory, or a file path relative to your
+home directory. In Unix Alpine, you may use &quot;~&quot; to refer to
+your home directory or &quot;~user&quot; to refer to another
+account's home directory.
+<!--chtml endif--><P>
+No wild card characters may be used.
+<P>If the
+<A HREF="h_config_use_current_dir">&quot;<!--#echo var="FEAT_use-current-dir"-->&quot;</A>
+feature is set, names are relative to your current working directory
+<!--chtml if pinemode="running"-->
+(which, at least for your current Alpine &quot;session,&quot;
+is &quot;<!--#echo var="CURRENT_DIR"-->&quot;)
+<!--chtml endif-->
+rather than your home directory
+<!--chtml if pinemode="running"-->
+(which, in the present configuration of your system, is
+ &quot;<!--#echo var="HOME_DIR"-->&quot;)
+<!--chtml endif-->
+.
+<P>
+Alpine uses MIME encoding for attachments, so binaries and files of any
+length can safely be delivered to any MIME-capable mail reading program.
+If you send an attachment to someone who does not have a MIME-capable mail
+reading program yet, then the main message text will be readable, but
+attachments (even attachments that are just plain text) are not.
+<P>
+
+Typing the filename on the Attchmnt: line achieves the same
+result as using the
+<!--chtml if pinemode="function_key"-->
+F6
+<!--chtml else-->
+Ctrl-J
+<!--chtml endif--> command.
+<P>
+
+If you Forward a message with attachments, you may delete them from your
+Forwarded message by editing the Attchmnt header line.
+<P>
+
+For help with message header editing
+commands, check the <A HREF="h_composer_to">Help for the To:</A> header.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_composer_ctrl_j ====
+<HTML>
+<HEAD>
+<TITLE>COMPOSER ATTACH</TITLE>
+</HEAD>
+<BODY>
+After the
+<!--chtml if pinemode="function_key"-->
+F6
+<!--chtml else-->
+Ctrl-J
+<!--chtml endif--> command:
+At the &quot;File to attach:&quot; prompt, enter the name of the
+existing file to attach to your message.
+When the feature
+<A HREF="h_config_enable_tab_complete">&quot;<!--#echo var="FEAT_enable-tab-completion"-->&quot;</A>
+is set
+you need only enter the beginning of the filename (enough of it to uniquely
+identify the file) and press TAB to complete it.
+
+Or, press ^T to use the BROWSER screen for
+selecting the file. <P>
+For more information on attaching files, see the help screen for the
+composer's
+<A HREF="h_composer_attachment">Attchmnt: field</A>, which is normally hidden,
+but can be revealed using the
+<!--chtml if pinemode="function_key"-->
+F5
+<!--chtml else-->
+Ctrl-R
+<!--chtml endif-->
+command with the cursor positioned above the
+&quot;----- Message Text -----&quot; line in the COMPOSE MESSAGE screen.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_edit_nav_cmds =========
+<HTML>
+<HEAD>
+<TITLE>Composer Editing Commands Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>EDITING and NAVIGATION COMMANDS</H1>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+CURSOR MOTION KEYS----------------------|EDITING KEYS-------------------------
+^B (Left Arrow) Back character | ^D Delete current character
+^F (Right Arrow) Forward character | ^H (DEL) Delete previous character
+^P (Up Arrow) Previous line |
+^N (Down Arrow) Next line | F9 Cut marked text or
+^A Beginning of line | delete current line
+^E End of line | F10 Undelete line(s)
+F7 Previous page |
+F8 Next page |-------------------------------------
+^@ (Ctrl-SPACE) Next word | MISCELLANEOUS COMMANDS
+----------------------------------------|
+EXIT COMMANDS | GENERAL COMMANDS | F12 To Addressbook
+F3 Cancel | F1 Get help | F12 RichView (expand lists)
+F2 eXit/save | ^Z Suspend | ^L Redraw Screen
+</PRE>
+<!--chtml else-->
+<PRE>
+CURSOR MOTION KEYS----------------------|EDITING KEYS-------------------------
+^B (Left Arrow) Back character | ^D Delete current character
+^F (Right Arrow) Forward character | ^H (DEL) Delete previous character
+^P (Up Arrow) Previous line |
+^N (Down Arrow) Next line | ^K Cut marked text or
+^A Beginning of line | delete current line
+^E End of line | ^U Undelete line(s)
+^Y Previous page |
+^V Next page |-------------------------------------
+^@ (Ctrl-SPACE) Next word | MISCELLANEOUS COMMANDS
+----------------------------------------|
+EXIT COMMANDS | GENERAL COMMANDS | ^T To Addressbook
+^C Cancel | ^G Get help | ^R RichView (expand lists)
+^X eXit/save | ^Z Suspend | ^L Redraw Screen
+</PRE>
+<!--chtml endif-->
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_composer_sigedit =====
+<HTML>
+<HEAD>
+<TITLE>Signature Editor Commands Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>SIGNATURE EDITOR COMMANDS</H1>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+CURSOR MOTION KEYS |EDITING KEYS
+ ^B (Left Arrow) Back character | ^D Delete current character
+ ^F (Right Arrow) Forward character | ^H (DEL) Delete previous character
+ ^P (Up Arrow) Previous line | ^^ Set a mark
+ ^N (Down Arrow) Next line | F9 Cut marked text or
+ ^A Beginning of line | delete current line
+ ^E End of line | F10 Paste text, undelete lines
+ F7 Previous page | cut with ^K, or unjustify
+ F8 Next page |-------------------------------------
+ ^@ (Ctrl-SPACE) Next word |SCREEN/COMPOSITION COMMANDS
+---------------------------------------| F6 Whereis (search for string)
+MESSAGE COMMANDS | GENERAL COMMANDS | F12 Spell checker
+ F3 Cancel | F1 Get help | F4 Justify paragraph
+ | ^Z Suspend | ^L Redraw Screen
+ F2 Send | F6 Alt. editor | F5 Read in a file
+</PRE>
+<!--chtml else-->
+<PRE>
+CURSOR MOTION KEYS |EDITING KEYS
+ ^B (Left Arrow) Back character | ^D Delete current character
+ ^F (Right Arrow) Forward character | ^H (DEL) Delete previous character
+ ^P (Up Arrow) Previous line | ^^ Set a mark
+ ^N (Down Arrow) Next line | ^K Cut marked text or
+ ^A Beginning of line | delete current line
+ ^E End of line | ^U Paste text, undelete lines
+ ^Y Previous page | cut with ^K, or unjustify
+ ^V Next page |-------------------------------------
+ ^@ (Ctrl-SPACE) Next word |SCREEN/COMPOSITION COMMANDS
+---------------------------------------| ^W <A HREF="h_composer_search">Whereis</A> (search text)
+MESSAGE COMMANDS | GENERAL COMMANDS | ^T Spell checker
+ ^C Cancel | ^G Get help | ^J <A HREF="h_compose_justify">Justify</A> paragraph
+ | ^Z <A HREF="h_common_suspend">Suspend</A> | ^L Redraw Screen
+ ^X Send | ^_ Alt. editor | ^R Read in a file
+</PRE>
+<!--chtml endif-->
+
+NOTE: The presence or absence of the following commands is determined
+by &quot;Feature-List&quot; options in your Alpine configuration. Also,
+some of these commands may be administratively disabled by your system
+manager; if they don't work, please check with your local help desk
+before reporting a bug.
+<P>
+<UL>
+ <LI>Suspend (suspends Alpine and gives a system prompt)
+ <LI>Alternate editor (allows you to compose with your own editor)
+</UL>
+<P>
+
+Alpine does not use the following keys: Ctrl-S, Ctrl-Q, Ctrl-],
+Ctrl-&#92;, ESC
+<P>
+
+NOTE: For special handling of Ctrl-S and Ctrl-Q see special comments regarding
+<A HREF="h_special_xon_xoff">&quot;XOFF/XON&quot;</A>.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_composer_commentedit =====
+<HTML>
+<HEAD>
+<TITLE>Comment Editor Commands Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>COMMENT EDITOR COMMANDS</H1>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+CURSOR MOTION KEYS |EDITING KEYS
+ ^B (Left Arrow) Back character | ^D Delete current character
+ ^F (Right Arrow) Forward character | ^H (DEL) Delete previous character
+ ^P (Up Arrow) Previous line | ^^ Set a mark
+ ^N (Down Arrow) Next line | F9 Cut marked text or
+ ^A Beginning of line | delete current line
+ ^E End of line | F10 Paste text, undelete lines
+ F7 Previous page | cut with ^K, or unjustify
+ F8 Next page |-------------------------------------
+ ^@ (Ctrl-SPACE) Next word |SCREEN/COMPOSITION COMMANDS
+---------------------------------------| F6 Whereis (search for string)
+MESSAGE COMMANDS | GENERAL COMMANDS | F12 Spell checker
+ F3 Cancel | F1 Get help | F4 Justify paragraph
+ | ^Z Suspend | ^L Redraw Screen
+ F2 Send | F6 Alt. editor | F5 Read in a file
+</PRE>
+<!--chtml else-->
+<PRE>
+CURSOR MOTION KEYS |EDITING KEYS
+ ^B (Left Arrow) Back character | ^D Delete current character
+ ^F (Right Arrow) Forward character | ^H (DEL) Delete previous character
+ ^P (Up Arrow) Previous line | ^^ Set a mark
+ ^N (Down Arrow) Next line | ^K Cut marked text or
+ ^A Beginning of line | delete current line
+ ^E End of line | ^U Paste text, undelete lines
+ ^Y Previous page | cut with ^K, or unjustify
+ ^V Next page |-------------------------------------
+ ^@ (Ctrl-SPACE) Next word |SCREEN/COMPOSITION COMMANDS
+---------------------------------------| ^W <A HREF="h_composer_search">Whereis</A> (search text)
+MESSAGE COMMANDS | GENERAL COMMANDS | ^T Spell checker
+ ^C Cancel | ^G Get help | ^J <A HREF="h_compose_justify">Justify</A> paragraph
+ | ^Z <A HREF="h_common_suspend">Suspend</A> | ^L Redraw Screen
+ ^X Send | ^_ Alt. editor | ^R Read in a file
+</PRE>
+<!--chtml endif-->
+
+NOTE: The presence or absence of the following commands is determined
+by &quot;Feature-List&quot; options in your Alpine configuration. Also,
+some of these commands may be administratively disabled by your system
+manager; if they don't work, please check with your local help desk
+before reporting a bug.
+<P>
+<UL>
+ <LI>Suspend (suspends Alpine and gives a system prompt)
+ <LI>Alternate editor (allows you to compose with your own editor)
+</UL>
+<P>
+
+Alpine does not use the following keys: Ctrl-S, Ctrl-Q, Ctrl-],
+Ctrl-&#92;, ESC
+<P>
+
+NOTE: For special handling of Ctrl-S and Ctrl-Q see special comments regarding
+<A HREF="h_special_xon_xoff">&quot;XOFF/XON&quot;</A>.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_composer_abook_nick =======
+<HTML>
+<HEAD>
+<TITLE>Addressbook Nickname Explained</TITLE>
+</HEAD>
+<BODY>
+This is a short nickname for this address book entry. If it is used in
+place of an address from the composer, the composer will fill in the
+address(es) for the entry that matches the nickname.
+<P>
+
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_composer_abook_full =======
+<HTML>
+<HEAD>
+<TITLE>Addressbook Fullname Explained</TITLE>
+</HEAD>
+<BODY>
+This is the full name field for this entry. If this is going to be a
+distribution list (more than one address), it should be a descriptive
+phrase describing the list. It will be included in the mail header if you
+put the list in the To: or CC: field, or in the To: line if you put the
+list in the Lcc: field. It's OK to leave this field blank (and OK to
+leave any of the other fields blank, too). If this address book entry is
+going to be a simple entry with just one address, then this field is the
+person's name. When you send mail to this entry, this is the field to the
+left of the brackets. That is, it is the most readable part of the
+address. For example, in the sample address:
+<PRE>
+ John Doe &lt;jdoe@some.domain&gt;
+</PRE>
+"John Doe" is the full name field. If you are sorting your address book
+with one of the options that uses full names, then it might be useful to
+enter the full name as "Last, First", for example:
+<PRE>
+ Doe, John
+</PRE>
+so that it will be sorted using Doe instead of John. This will be changed
+back into John Doe when you use it.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_composer_abook_fcc =======
+<HTML>
+<HEAD>
+<TITLE>Addressbook Fcc Explained</TITLE>
+</HEAD>
+<BODY>
+If this entry is the first one in the To: line of an outgoing message,
+this field will be used for the Fcc (File Carbon Copy) instead of whatever
+you would normally get (which depends on which
+<A HREF="h_config_saved_msg_name_rule">&quot;<!--#echo var="VAR_saved-msg-name-rule"-->&quot;</A>
+you've chosen).
+<P>
+If this field consists of two double quotes (&quot;&quot;) that tells Alpine
+that you don't want any Fcc associated with this entry.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_combined_abook_display =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_combined-addrbook-display"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_combined-addrbook-display"--></H1>
+
+This feature affects the address book display screens.
+Normally, expanding an address book from the ADDRESS BOOK LIST screen
+will cause the remaining address books and directory servers to disappear
+from the screen, leaving only the entries of the expanded address book.
+If this feature is set, then the other address books will remain on the screen,
+so that all of the address books can be present at once.
+
+<P>
+The way that commands work won't be changed.
+For example, the Select All command will select all of the entries in the
+current address book, not all of the entries in all of the address books.
+The WhereIs command will change a little.
+It will search through all of the text on the screen plus all of the entries
+from expanded address books.
+
+<P>
+When this feature is set, the setting of the feature
+<A HREF="h_config_expanded_addrbooks">&quot;<!--#echo var="FEAT_expanded-view-of-addressbooks"-->&quot;</A>
+has an effect.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_titlebar_color_style =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_titlebar-color-style"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_titlebar-color-style"--></H1>
+
+This option affects the colors used to display the titlebar (the top
+line on the screen) when viewing a message.
+
+<P>
+The available options include:
+<P>
+
+<DL>
+<DT>default</DT>
+<DD>The color of the titlebar will be the color you set for the
+<A HREF="h_config_title_color">Title Color</A>.
+The Title Color may be set by using the
+<A HREF="h_color_setup">Setup Kolor</A> screen.
+</DD>
+
+<DT>indexline</DT>
+<DD>The color of the titlebar will be the same as the color of the
+index line corresponding to the message being viewed.
+The rules that determine what color the index line will be may be set
+up by going to the Setup/Rules/Indexcolor screen.
+If the index line for a message is not colored explicitly by the
+Indexcolor rules, then the titlebar will be colored the same as for
+the &quot;default&quot; option above (which is not the same color that
+the index line itself will have).
+</DD>
+
+<DT>reverse-indexline</DT>
+<DD>This is similar to the &quot;indexline&quot; option except the
+foreground and background colors from the corresponding index line will
+be reversed.
+For example, if the index line color is red letters on a white background,
+then the titlebar will be white letters on a red background.
+If the index line for a message is not colored explicitly by the
+Indexcolor rules, then the titlebar will be colored the same as for
+the &quot;default&quot; option above (which is not the same color that
+the index line itself will have).
+</DD>
+</DL>
+
+<P>
+
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_index_color_style =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_current-indexline-style"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_current-indexline-style"--></H1>
+
+This option affects the colors used to display the current line in the
+MESSAGE INDEX screen.
+If you do not have Index Color Rules defined, then this option will
+have no effect in the index.
+Those Rules may be defined by going to the Setup/Rules/Indexcolor screen.
+<P>
+If the option
+<A HREF="h_config_enable_incoming_checking"><!--#echo var="FEAT_enable-incoming-folders-checking"--></A>
+is turned on and the
+<A HREF="h_config_incunseen_color">Incoming Unseen Color</A>
+is set to something other than the default, then
+this option also affects the color used to display the current folder
+in the Incoming FOLDER LIST screen.
+
+<P>
+The available options include:
+<P>
+
+<DL>
+<DT>flip-colors</DT>
+<DD>This is the default.
+If an index line is colored because it matches one of your
+Index Color Rules, then its colors will be reversed when it is the currently
+highlighted line.
+For example, if the line is normally red text on a blue background, then
+when it is the current line it will be drawn as blue text on a red background.
+<P>
+The rest of the option values all revert to this flip-colors behavior if
+there is no Reverse Color defined.
+</DD>
+
+<DT>reverse</DT>
+<DD>With this option the Reverse color is always used to highlight the
+current line.
+</DD>
+
+<DT>reverse-fg</DT>
+<DD>The foreground part of the Reverse Color is used to highlight
+the current line.
+If this would cause the text to be unreadable (because the foreground and
+background colors are the same) or if it would cause no change in the
+color of the index line, then the colors are flipped instead.
+<P>
+Some people think this works particularly well if you use different
+background colors to emphasize &quot;interesting&quot; lines,
+but always with the same Normal foreground color,
+and you use a different foreground color for the Reverse Color.
+</DD>
+
+<DT>reverse-fg-no-ambiguity</DT>
+<DD>With the &quot;reverse-fg&quot; rule above, it is possible that
+the resulting color will be exactly the same as the regular Reverse
+Color.
+That can lead to some possible confusion because an
+&quot;interesting&quot;
+line that is the current line will be displayed exactly the same as a
+non-interesting line that is current.
+You can't tell whether the line is just a regular current line or if it is
+an &quot;interesting&quot; current line by looking at the color.
+Setting the option to this value removes that ambiguity.
+It is the same as the &quot;reverse-fg&quot; setting unless the resulting
+interesting current line would look just like a non-interesting current line.
+In that case, the interesting line's colors are simply flipped (like in the
+default behavior).
+<P>
+As an alternative way to preserve the line's interestingness in this case,
+you may find that using both a different foreground and a different
+background color for the interesting line will help.
+</DD>
+
+<DT>reverse-bg</DT>
+<DD>The background part of the Reverse Color is used to highlight
+the current line.
+If this would cause the text to be unreadable (because the foreground and
+background colors are the same) or if it would cause no change in the
+color of the index line, then the colors are flipped instead.
+<P>
+Some people think this works particularly well if you use different
+foreground colors to emphasize &quot;interesting&quot; lines,
+but always with the same Normal background color,
+and you use a different background color for the Reverse Color.
+</DD>
+
+<DT>reverse-bg-no-ambiguity</DT>
+<DD>As with the &quot;reverse-fg&quot; case, the &quot;reverse-bg&quot;
+rule may also result in a color that is exactly the same as the regular
+Reverse Color.
+Setting the option to this value removes that ambiguity.
+It is the same as the &quot;reverse-bg&quot; setting unless the resulting
+current line has the same color as the Reverse Color.
+In that case, the interesting line's colors are simply flipped (like in the
+default behavior).
+</DD>
+</DL>
+
+<P>
+
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_expanded_addrbooks =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_expanded-view-of-addressbooks"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_expanded-view-of-addressbooks"--></H1>
+
+If multiple address books (either personal or global) are defined, and you
+wish to have them all expanded implicitly upon entering the ADDRESS BOOK
+screen, then set this feature. This feature will have no effect unless the
+feature
+<A HREF="h_config_combined_abook_display">&quot;<!--#echo var="FEAT_combined-addrbook-display"-->&quot;</A>
+is also set.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_combined_folder_display =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_combined-folder-display"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_combined-folder-display"--></H1>
+
+This feature affects the folder list display screens.
+Normally, each folder list is viewed within its collection only. This
+command allows folder lists to be viewed within a single screen that
+combines the contents of all collections.
+
+<P>
+The way that commands work won't be changed.
+For example, the Select All command will select all of the folders in the
+current collection, not all of the entries in all of the collections.
+The WhereIs command will change a little.
+It will search through all of the folders in the current collection as well
+as all the folder in any other expanded collection.
+
+<P>
+When this feature is set, the setting of the feature
+<A HREF="h_config_expanded_folders">&quot;<!--#echo var="FEAT_expanded-view-of-folders"-->&quot;</A>
+has an effect.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_combined_subdir_display =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_combined-subdirectory-display"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_combined-subdirectory-display"--></H1>
+
+This feature affects the Folder List screen when
+the
+<A HREF="h_config_combined_folder_display">&quot;<!--#echo var="FEAT_combined-folder-display"-->&quot;</A>
+feature is enabled. Normally, selecting a directory from the Folder
+List takes you into a new screen displaying only the contents of
+that directory.
+
+<P>
+Enabling this feature will cause the contents of the selected
+directory to be
+displayed within the boundaries of the &quot;Collection&quot; it
+is a part of. All previously displayed collections will remain
+in the screen.
+
+<P>
+The way that commands work won't be changed.
+For example, the Select All command will select all of the folders in the
+directory, as opposed to all of the entries in all of the collections.
+The WhereIs command will change a little.
+It will search through all of the folders in the current collection as well
+as all the folder in any other expanded collection.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_separate_fold_dir_view =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_separate-folder-and-directory-entries"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_separate-folder-and-directory-entries"--></H1>
+
+This feature affects folder collections wherein a folder
+and directory can have the same name. By default, Alpine displays them
+only once, denoting that it is both a folder and directory by appending
+the folder name with the hierarchy character enclosed
+in square brackets.
+
+
+<P>
+Enabling this feature will cause Alpine to display such names
+separately marking the name representing a directory with a trailing
+hierarchy delimiter (typically the slash, &quot;/&quot;, character).
+
+<P>
+The feature also alters the command set slightly. By default, the
+right-arrow descends into the directory, while hitting the Return key will
+cause the folder by that name to be opened.
+
+<P>
+With this feature set, the Return key will open the highlighted folder, or
+enter the highlighted directory.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_expanded_folders =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_expanded-view-of-folders"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_expanded-view-of-folders"--></H1>
+
+If multiple folder collections are defined, and you
+wish to have them all expanded implicitly upon entering the FOLDER LIST
+screen, then set this feature. This feature will have no effect unless the
+feature
+<A HREF="h_config_combined_folder_display">&quot;<!--#echo var="FEAT_combined-folder-display"-->&quot;</A>
+is also set.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_ldap_server =======
+<HTML>
+<HEAD>
+<TITLE>LDAP OPTION: <!--#echo var="VAR_ldap-servers"--></TITLE>
+</HEAD>
+<BODY>
+<H1>LDAP OPTION: <!--#echo var="VAR_ldap-servers"--></H1>
+This is the name of the host where an LDAP server is running.
+For redundancy, this may be a space-delimited set of server names, in which
+case the first server that answers is used.
+Each of the server names may be optionally followed by
+a colon and a port number.
+If this form is used then the port number configured below in the
+<EM>port</EM> field is not used.
+<P>
+To find out whether your organization has its own LDAP server,
+contact its computing support staff.
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_ldap_base =======
+<HTML>
+<HEAD>
+<TITLE>LDAP OPTION: Search-Base</TITLE>
+</HEAD>
+<BODY>
+<H1>LDAP OPTION: Search-Base</H1>
+
+This is the search base to be used on this server. It functions as a filter
+by restricting your searches in the LDAP server database
+to the specified contents of the specified fields. Without it, searches
+submitted to this directory server may fail. It might be something
+like:
+
+<PRE>
+ O = &lt;Your Organization Name&gt;, C = US
+</PRE>
+or it might be blank.
+(Some LDAP servers actually ignore anything specified here.)
+<P>
+If in doubt what parameters you should specify here,
+contact the maintainers of the LDAP server.
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_ldap_port =======
+<HTML>
+<HEAD>
+<TITLE>LDAP OPTION: Port</TITLE>
+</HEAD>
+<BODY>
+<H1>LDAP OPTION: Port</H1>
+
+This is the TCP port number to be used with this LDAP server. If you leave
+this blank port 389 will be used.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_ldap_nick =======
+<HTML>
+<HEAD>
+<TITLE>LDAP OPTION: Nickname</TITLE>
+</HEAD>
+<BODY>
+<H1>LDAP OPTION: Nickname</H1>
+
+This is a nickname to be used in displays. If you don't supply a
+nickname the server name
+(<A HREF="h_config_ldap_server">&quot;<!--#echo var="VAR_ldap-servers"-->&quot;</A>)
+will be used instead. This option is strictly for your convenience.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_ldap_binddn =======
+<HTML>
+<HEAD>
+<TITLE>LDAP OPTION: Bind-DN</TITLE>
+</HEAD>
+<BODY>
+<H1>LDAP OPTION: Bind-DN</H1>
+
+You may need to authenticate to the LDAP server before you are able to use it.
+This is the Distinguished Name to bind to when authenticating to this server.
+Try leaving this blank until you know you need it.
+<P>
+Alpine only knows about LDAP Simple authentication.
+It does not attempt LDAP SASL authentication.
+The DN and password will be sent in the clear unless TLS encryption is
+being used on this connection.
+Because of this, you may want to set the LDAP feature
+<A HREF="h_config_ldap_opts_tls">&quot;Attempt-TLS-On-Connection&quot;</A>
+or the feature
+<A HREF="h_config_ldap_opts_tlsmust">&quot;Require-TLS-On-Connection&quot;</A>
+if you are going to be providing a password.
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_ldap_opts_impl =======
+<HTML>
+<HEAD>
+<TITLE>LDAP FEATURE: Use-Implicitly-From-Composer</TITLE>
+</HEAD>
+<BODY>
+<H1>LDAP FEATURE: Use-Implicitly-From-Composer</H1>
+
+Set this to have lookups done to this server implicitly from the composer.
+If an address doesn't look like a fully-qualified address, it will be looked
+up in your address books, and if it doesn't match a nickname there, then it
+will be looked up on the LDAP servers that have this feature set.
+The lookups will also be done when using the address completion feature
+(TAB command) in the composer if any of the serves have this feature set.
+Also see the LDAP feature
+<A HREF="h_config_ldap_opts_rhs">&quot;Lookup-Addrbook-Contents&quot;</A>
+and the Setup/Config feature
+<A HREF="h_config_add_ldap">&quot;<!--#echo var="FEAT_ldap-result-to-addrbook-add"-->&quot;</A>.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_ldap_opts_tls =======
+<HTML>
+<HEAD>
+<TITLE>LDAP FEATURE: Attempt-TLS-On-Connection</TITLE>
+</HEAD>
+<BODY>
+<H1>LDAP FEATURE: Attempt-TLS-On-Connection</H1>
+
+When connecting to this server Alpine will attempt to use TLS encryption
+on the connection.
+Also see the closely related feature
+<A HREF="h_config_ldap_opts_tlsmust">&quot;Require-TLS-On-Connection&quot;</A>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_ldap_opts_tlsmust =======
+<HTML>
+<HEAD>
+<TITLE>LDAP FEATURE: Require-TLS-On-Connection</TITLE>
+</HEAD>
+<BODY>
+<H1>LDAP FEATURE: Require-TLS-On-Connection</H1>
+
+When connecting to this server Alpine will attempt to use TLS encryption
+on the connection.
+If the StartTLS operation fails then the connection will not be used.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_ldap_opts_rhs =====
+<HTML>
+<HEAD>
+<TITLE>LDAP FEATURE: Lookup-Addrbook-Contents</TITLE>
+</HEAD>
+<BODY>
+<H1>LDAP FEATURE: Lookup-Addrbook-Contents</H1>
+
+Normally implicit LDAP lookups from the composer are done only for the
+strings you type in from the composer screen. In other words, you type in
+something in the To or CC field and press return, then the string is looked up.
+First that string is looked up in your address books. If a match is found
+there, then the results of that match are looked up again. If you place
+a string in your address book that you want to have looked up on the LDAP
+directory server, you need to turn on this feature. If you set this feature
+for a server, you almost always will also want to set the
+<A HREF="h_config_ldap_opts_impl">&quot;Use-Implicitly-From-Composer&quot;</A>
+feature. An example might serve to best illustrate this feature.
+<P>
+If an LDAP lookup of &quot;William Clinton&quot; normally returns an
+entry with an
+address of pres@whitehouse.gov, then you might put an entry in your address
+book that looks like:
+<P>
+<CENTER><SAMP>Nickname = bill</SAMP></CENTER><BR>
+<CENTER><SAMP>Address = &quot;William Clinton&quot;</SAMP></CENTER>
+<P>
+Now, when you type &quot;bill&quot; into an
+address field in the composer Alpine will
+find the &quot;bill&quot; entry in your address book.
+It will replace &quot;bill&quot; with
+&quot;William Clinton&quot;.
+It will then search for an entry with that nickname
+in your address book and not find one. If this feature
+is set, Alpine will then attempt to lookup
+&quot;William Clinton&quot; on the LDAP server and find the entry with address
+pres@whitehouse.gov.
+<P>
+A better way to accomplish the same thing is probably to use the feature
+<A HREF="h_config_ldap_opts_ref">&quot;Save-Search-Criteria-Not-Result&quot;</A>.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_ldap_opts_ref =====
+<HTML>
+<HEAD>
+<TITLE>LDAP FEATURE: Save-Search-Criteria-Not-Result</TITLE>
+</HEAD>
+<BODY>
+<H1>LDAP FEATURE: Save-Search-Criteria-Not-Result</H1>
+
+Normally when you save the results of an LDAP directory lookup to your
+address book the results of the lookup are saved. If this feature is set
+and the entry being saved was found on this directory server, then the
+search criteria is saved instead of the results of the search. When this
+address book entry is used in the future, instead of copying the results
+from the address book the directory lookup will be done again. This could
+be useful if the copied result might become stale because the data on
+the directory server changes (for example, the entry's email address changes).
+You probably don't want to set this feature if the server is at all slow or
+unreliable.
+<P>
+The way this actually works is that instead of saving the email address
+in your address book, Alpine saves enough information to look up the same
+directory entry again. In particular, it saves the server name and the
+distinguished name of the entry. It's possible that the server administrators
+might change the format of distinguished names on the server, or that the
+entry might be removed from the server. If Alpine notices this, you will be warned
+and a backup copy of the email address will be used. You may want to create
+a new entry in this case, since you will get the annoying warning every
+time you use the old entry. You may do that by Saving the entry to a new
+nickname in the same address book. You will be asked whether or not you
+want to use the backup email address.
+<P>
+A related feature in the Setup/Config screen is
+<A HREF="h_config_add_ldap">&quot;<!--#echo var="FEAT_ldap-result-to-addrbook-add"-->&quot;</A>.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_ldap_opts_nosub =======
+<HTML>
+<HEAD>
+<TITLE>LDAP FEATURE: Disable-Ad-Hoc-Space-Substitution</TITLE>
+</HEAD>
+<BODY>
+<H1>LDAP FEATURE: Disable-Ad-Hoc-Space-Substitution</H1>
+
+Spaces in your input are normally handled specially.
+Each space character is replaced
+by
+<P>
+<CENTER><SAMP>*&nbsp;&lt;SPACE&gt;</SAMP></CENTER>
+<P>
+in the search query (but not by &quot;* &lt;SPACE&gt; *&quot;).
+The reason this is done is so the input string
+<P>
+<CENTER><SAMP>Greg Donald</SAMP></CENTER>
+<P>
+(which is converted to &quot;Greg* Donald&quot;) will match
+the names &quot;Greg Donald&quot;,
+&quot;Gregory Donald&quot;, &quot;Greg F. Donald&quot;, and
+&quot;Gregory F Donald&quot;; but it won't match &quot;Greg McDonald&quot;.
+If the &quot;Search-Rule&quot; you were using was &quot;begins-with&quot;,
+then it would also match the name &quot;Greg Donaldson&quot;.
+<P>
+Turning on this feature will disable this substitution.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_ldap_searchtypes =======
+<HTML>
+<HEAD>
+<TITLE>LDAP OPTION: Search-Type</TITLE>
+</HEAD>
+<BODY>
+<H1>LDAP OPTION: Search-Type</H1>
+
+This affects the way that LDAP searches are done.
+In particular, this tells the server where to look for the string to be matched.
+If set to &quot;name&quot; then the string that is being searched for will
+be compared with the string in the
+&quot;Name&quot; field on the server
+(technically, it is the &quot;commonname&quot; field on the server).
+&quot;Surname&quot; means we're looking for a
+match in the &quot;Surname&quot; field on the
+server (actually the &quot;sn&quot; field).
+&quot;Givenname&quot; really is &quot;givenname&quot;
+and &quot;email&quot; is the electronic mail address (this is actually the field
+called &quot;mail&quot; or &quot;electronicmail&quot; on the server).
+The other three types are combinations of
+the types listed so far. &quot;Name-or-email&quot;
+means the string should appear
+in either the &quot;name&quot; field OR the &quot;email&quot; field.
+Likewise, &quot;surname-or-givenname&quot;
+means &quot;surname&quot; OR &quot;givenname&quot;
+and &quot;sur-or-given-or-name-or-email&quot; means the obvious thing.
+<P>
+This search TYPE is combined with the
+search <A HREF="h_config_ldap_searchrules">RULE</A>
+to form the actual search query.
+<P>
+The usual default value for this
+option is &quot;sur-or-given-or-name-or-email&quot;.
+This type of search may be slow on some servers.
+Try &quot;name-or-email&quot;, which is often
+faster, or just &quot;name&quot; if the performance seems to be a problem.
+<P>
+Some servers have been configured with different attribute names for
+these four fields.
+In other words, instead of using the attribute name &quot;mail&quot;
+for the email address field, the server might be configured to use something
+else, for example, &quot;rfc822mail&quot; or &quot;internetemailaddress&quot;.
+Alpine can be configured to use these different attribute names by using
+the four configuration options:
+<P><UL>
+<LI><A HREF="h_config_ldap_email_attr">&quot;EmailAttribute&quot;</A>
+</UL>
+<P><UL>
+<LI><A HREF="h_config_ldap_cn_attr">&quot;NameAttribute&quot;</A>
+</UL>
+<P><UL>
+<LI><A HREF="h_config_ldap_sn_attr">&quot;SurnameAttribute&quot;</A>
+</UL>
+<P><UL>
+<LI><A HREF="h_config_ldap_gn_attr">&quot;GivennameAttribute&quot;</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_ldap_searchrules =======
+<HTML>
+<HEAD>
+<TITLE>LDAP OPTION: Search-Rule</TITLE>
+</HEAD>
+<BODY>
+<H1>LDAP OPTION: Search-Rule</H1>
+
+This affects the way that LDAP searches are done.
+If set to &quot;equals&quot; then
+only exact matches count.
+&quot;Contains&quot; means that the string you type in
+is a substring of what you are matching against.
+&quot;Begins-with&quot; and &quot;ends-with&quot;
+mean that the string starts or ends with the string you type in.
+<P>
+Spaces in your input are normally handled specially, but you can turn that
+special handling off with the
+<A HREF="h_config_ldap_opts_nosub">&quot;Disable-Ad-Hoc-Space-Substitution&quot;</A>
+feature.
+<P>
+The usual default value for this option is &quot;begins-with&quot;.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_ldap_email_attr =======
+<HTML>
+<HEAD>
+<TITLE>LDAP OPTION: EmailAttribute</TITLE>
+</HEAD>
+<BODY>
+<H1>LDAP OPTION: EmailAttribute</H1>
+
+This is the name of the attribute that is searched for when looking for
+an email address. The default value for this option is &quot;mail&quot; or
+&quot;electronicmail&quot;.
+If the server you are using uses a different attribute name for the email
+address, put that attribute name here.
+<P>
+This will affect the search filter used if your Search-Type is one that
+contains a search for &quot;email&quot;.
+It will also cause the attribute value matching this attribute name to be used
+as the email address when you look up an entry from the composer.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_ldap_sn_attr =======
+<HTML>
+<HEAD>
+<TITLE>LDAP OPTION: SurnameAttribute</TITLE>
+</HEAD>
+<BODY>
+<H1>LDAP OPTION: SurnameAttribute</H1>
+
+This is the name of the attribute that is searched for when looking for
+the surname of the entry. The default value for this option is &quot;sn&quot;.
+If the server you are using uses a different attribute name for the surname,
+put that attribute name here.
+This will affect the search filter used if your Search-Type is one that
+contains a search for &quot;surname&quot;.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_ldap_gn_attr =======
+<HTML>
+<HEAD>
+<TITLE>LDAP OPTION: GivennameAttribute</TITLE>
+</HEAD>
+<BODY>
+<H1>LDAP OPTION: GivennameAttribute</H1>
+
+This is the name of the attribute that is searched for when looking for
+the given name of the entry. The default value for this option is &quot;givenname&quot;.
+If the server you are using uses a different attribute name for the given name,
+put that attribute name here.
+This will affect the search filter used if your Search-Type is one that
+contains a search for &quot;givenname&quot;.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_ldap_cn_attr =======
+<HTML>
+<HEAD>
+<TITLE>LDAP OPTION: NameAttribute</TITLE>
+</HEAD>
+<BODY>
+<H1>LDAP OPTION: NameAttribute</H1>
+
+This is the name of the attribute that is searched for when looking for
+the name of the entry. The default value for this option is &quot;cn&quot;, which
+stands for common name.
+If the server you are using uses a different attribute name for the name,
+put that attribute name here.
+This will affect the search filter used if your Search-Type is one that
+contains a search for &quot;name&quot;.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_ldap_time =======
+<HTML>
+<HEAD>
+<TITLE>LDAP OPTION: Timelimit</TITLE>
+</HEAD>
+<BODY>
+<H1>LDAP OPTION: Timelimit</H1>
+
+This places a limit on the number of seconds the LDAP search will continue.
+The default is 30 seconds. A value of 0 means no limit. Note that some servers
+may place limits of their own on searches.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_ldap_size =======
+<HTML>
+<HEAD>
+<TITLE>LDAP OPTION: Sizelimit</TITLE>
+</HEAD>
+<BODY>
+<H1>LDAP OPTION: Sizelimit</H1>
+
+This places a limit on the number of entries returned by the LDAP server.
+A value of 0 means no limit. The default is 0. Note that some servers
+may place limits of their own on searches.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_ldap_cust =======
+<HTML>
+<HEAD>
+<TITLE>LDAP OPTION: Custom-Search-Filter</TITLE>
+</HEAD>
+<BODY>
+<H1>LDAP OPTION: Custom-Search-Filter</H1>
+
+This one is for advanced users only! If you define this, then the
+&quot;Search-Type&quot; and &quot;Search-Rule&quot; defined are both ignored.
+However, the feature
+<A HREF="h_config_ldap_opts_nosub">&quot;Disable-Ad-Hoc-Space-Substitution&quot;</A>
+is still in effect.
+That is, the space substitution will take place even in a custom filter unless
+you disable it.
+<P>
+If your LDAP service stops working and you suspect it might be because
+of your custom filter, just delete this filter and try using the
+&quot;Search-Type&quot; and &quot;Search-Rule&quot; instead.
+Another option that sometimes causes trouble is the
+<A HREF="h_config_ldap_base">&quot;Search-Base&quot;</A> option.
+<P>
+This variable may be set to the string representation of an LDAP search
+filter (see RFC1960). In the places where you want the address string to be
+substituted in, put a '%s' in this filter string. Here are some examples:
+<P>
+A &quot;Search-Type&quot; of &quot;name&quot; with &quot;Search-Rule&quot; of &quot;begins-with&quot;
+is equivalent to the &quot;Custom-Search-Filter&quot;
+<PRE>
+ (cn=%s*)
+</PRE>
+When you try to match against the string &quot;string&quot; the program replaces
+the &quot;%s&quot; with &quot;string&quot; (without the quotes). You may have multiple &quot;%s&quot;'s and
+they will all be replaced with the string. There is a limit of 10 &quot;%s&quot;'s.
+<P>
+A &quot;Search-Type&quot; of &quot;name-or-email&quot; with &quot;Search-Rule&quot;
+of &quot;contains&quot; is equivalent to
+<PRE>
+ (|(cn=*%s*)(mail=*%s*))
+</PRE>
+<P>
+If your server uses a different attribute <EM>name</EM> than
+Alpine uses by default,
+(for example, it uses &quot;rfc822mail&quot; instead of &quot;mail&quot;),
+then you may be able to use one or more of the four attribute configuration
+options instead of defining a custom filter:
+<P><UL>
+<LI><A HREF="h_config_ldap_email_attr">&quot;EmailAttribute&quot;</A>
+</UL>
+<P><UL>
+<LI><A HREF="h_config_ldap_cn_attr">&quot;NameAttribute&quot;</A>
+</UL>
+<P><UL>
+<LI><A HREF="h_config_ldap_sn_attr">&quot;SurnameAttribute&quot;</A>
+</UL>
+<P><UL>
+<LI><A HREF="h_config_ldap_gn_attr">&quot;GivennameAttribute&quot;</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_composer_abook_comment =======
+<HTML>
+<HEAD>
+<TITLE>Addressbook Comment Explained</TITLE>
+</HEAD>
+<BODY>
+This is a comment to help you remember what this entry is. The WhereIs
+command searches comments so that it is easier to find an entry with a comment
+you know about attached to it. This field is not used in the outgoing message.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_composer_abook_addrs =======
+<HTML>
+<HEAD>
+<TITLE>Addressbook Lists</TITLE>
+</HEAD>
+<BODY>
+<H1>Addressbook Lists</H1>
+
+This is a list of addresses to send to when sending to this address book
+entry. Each member of the list may be an address or another nickname from
+any of your address books. If it is an address, it is OK to include the
+full name field as well as the electronic address portion of that address.
+For example, the following are all legitimate entries in this field:
+
+<DL><DT>&nbsp;</DT>
+<DD>john&nbsp;&nbsp;&nbsp;&nbsp;(a nickname in your address book)
+<DD>jdoe@some.domain
+<DD>John Doe &lt;jdoe@some.domain&gt;
+</DL>
+
+The addresses should be listed separated by commas, just like you would
+enter them from the composer.
+
+<P>
+
+The only difference between a distribution list and a simple entry with a
+single address, is that a distribution list has more than one address
+listed in the Addresses: field, whereas a simple personal entry has just
+one address.
+
+<P>
+
+For individual address book entries, if there is a full name in the
+Fullname: field (filling in the Fullname: field is not required), it is
+used. If the full name is specified in the Address: field and not in the
+Fullname: field, then the full name from the Address: field is used.
+
+<P>
+
+If you type the nickname of a distribution list from one of your address
+books in the Lcc: field, then the full name of that list is used in the
+To: field. If you put a list in the To: or Cc: fields, that list will be
+expanded into all of its addresses. If the list has a full name, then
+that will appear at the beginning of the addresses.
+
+<DL><DT>&nbsp;</DT>
+<DD>Sewing Club &lt;john@somewhere&gt;, nancy@something.else, Sal
+&lt;sal@here.there&gt;
+</DL>
+
+If the first address in the distribution list also has a full name, then
+the list full name and that full name are combined into something like the
+following:
+
+<DL><DT>&nbsp;</DT>
+<DD>Sewing Club -- John Smith &lt;john@somewhere&gt;
+</DL>
+
+
+If you specify a list via Lcc, the full name is used in the To: line. If
+you specify a list in the To: or Cc: fields, then it uses the same method
+as for individual entries for filling in the full name.
+
+<P>
+
+For help with editing and navigation commands, check the Help for the
+Nickname: field.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_nick =======
+<HTML>
+<HEAD>
+<TITLE>Nickname Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Nickname Explained</H1>
+
+This is a nickname to help you.
+You should have a different nickname for each role you define.
+The nickname will be used in the SETUP ROLE RULES screen to allow you to
+pick a role to edit.
+It will also be used when you send a message to let you know you are
+sending with a different role than you use by default, and
+it will be useful for choosing a role when composing with the Role command
+or when composing with one of the Role Uses set to With Confirmation.
+This field is not used in the outgoing message.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_comment =======
+<HTML>
+<HEAD>
+<TITLE>Comment Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Comment Explained</H1>
+
+This is a comment to help you.
+This comment does not play any functional role, it is simply an optional
+comment to help you remember what the rule is for.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_other_nick =======
+<HTML>
+<HEAD>
+<TITLE>Nickname Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Nickname Explained</H1>
+
+This is a nickname to help you.
+You should have a different nickname for each rule you define.
+The nickname will be used in the SETUP OTHER RULES screen to allow you to
+pick a rule to edit.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_score_nick =======
+<HTML>
+<HEAD>
+<TITLE>Nickname Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Nickname Explained</H1>
+
+This is a nickname to help you.
+You should have a different nickname for each scoring rule you define.
+The nickname will be used in the SETUP SCORING RULES screen to allow you to
+pick a rule to edit.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_incol_nick =======
+<HTML>
+<HEAD>
+<TITLE>Nickname Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Nickname Explained</H1>
+
+This is a nickname to help you.
+You should have a different nickname for each color rule you define.
+The nickname will be used in the SETUP INDEX COLOR RULES screen to allow you to
+pick a rule to edit.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_filt_nick =======
+<HTML>
+<HEAD>
+<TITLE>Nickname Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Nickname Explained</H1>
+
+This is a nickname to help you.
+You should have a different nickname for each filtering rule you define.
+The nickname will be used in the SETUP FILTERING RULES screen to allow you to
+pick a rule to edit.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_score_topat =======
+<HTML>
+<HEAD>
+<TITLE>&quot;To:&quot; Pattern Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>&quot;To:&quot; Pattern Explained</H1>
+
+Any text you enter as the &quot;To pattern&quot;
+will be compared to the recipients from the To: line of
+the message being scored.
+When the text you entered matches
+all or part of the To: line of a message, then the Score Value
+you have specified will be added to the score for the message.
+(Any other non-blank parts of the Pattern must match, too.)
+<P>
+
+You may enter a complete email address, part of an address, or a
+list of addresses or partial addresses.
+For example:
+<P>
+
+<PRE>
+ To pattern = friend@public.com
+
+ To pattern = rated.net
+
+ To pattern = xxx@adults.com
+ admin@msn.com
+ fool@motleyfool.com
+</PRE>
+
+<P>
+Each of those are valid To patterns.
+<P>
+
+Messages match those patterns if any of the
+addresses in the To: line of the message contains the pattern.
+If the pattern is a list of patterns
+(like the last example above) then it is a match if any of the patterns in
+the list match any of the addresses in the To: line.
+(It is not possible to specify two addresses that must <EM>BOTH</EM> be
+present for a match.
+It is only possible to specify that <EM>EITHER</EM> address1 <EM>OR</EM>
+address2 must be present.
+That is exactly what using a list does.)
+<P>
+
+Some messages may be &quot;bounced&quot; to you, and will
+have a &quot;Resent-To:&quot; header line.
+If the message contains a Resent-To: line
+and the feature <A HREF="h_config_use_resentto"><!--#echo var="FEAT_use-resent-to-in-rules"--></A> is turned on,
+Alpine will look for
+matches to your &quot;To patterns&quot; there, and <EM>NOT</EM> in
+the original To: line.
+<P>
+
+When entering a pattern, you may choose an address from your address book
+with the &quot;T&quot; command.
+<P>
+
+It is possible to add a <EM>NOT</EM> to the To Pattern meaning with the
+&quot;!&quot; &quot;toggle NOT&quot; command.
+This changes the meaning of the To pattern so that it has the opposite meaning.
+It will be considered a match if there are no matches between the
+addresses in the To: line and the list of To patterns.
+<P>
+Don't make the mistake of putting the &quot;!&quot; in the data field for
+the pattern.
+For example, if you type the characters &quot;!frizzle&quot; into the To
+pattern, the pattern will look like:
+<P>
+<PRE>
+ To pattern = !frizzle
+</PRE>
+<P>
+This means you want to match the 8 character sequence &quot;!frizzle&quot;.
+In order to match messages that do not have &quot;frizzle&quot; in
+their To field, first type the characters &quot;frizzle&quot; followed
+by carriage return for the value of the To pattern, then negate it
+by typing the &quot;!&quot; command.
+It should end up looking like
+<P>
+<PRE>
+ ! To pattern = frizzle
+</PRE>
+<P>
+You are not limited to using the six standard header patterns that are
+normally shown (To, From, Sender, Cc, News, and Subject).
+You may add any other header to a Pattern by
+using the &quot;eXtraHdr&quot; command to specify a different
+message header line; and then the Add or Change command to fill in
+a pattern for the new header line, just like you would for a standard header.
+<P>
+A technicality: Since comma is the character used to separate multiple
+values in a pattern field, you have to escape comma with a backslash (&#92;) if
+you want to include a literal comma in the field.
+In other words, if you type a backslash followed by a comma it will
+be interpreted as a comma by Alpine, instead of as a separator between
+pattern values.
+All other backslashes are literal backslashes and should not be escaped.
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_incol_topat =======
+<HTML>
+<HEAD>
+<TITLE>&quot;To:&quot; Pattern Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>&quot;To:&quot; Pattern Explained</H1>
+
+Any text you enter as the &quot;To pattern&quot;
+will be compared to the recipients from the To: lines of
+the messages in the index.
+When the text you entered matches
+all or part of the To: line of a message, then the Index Line Color you have
+specified will be used for that line in the index.
+(Any other non-blank parts of the Pattern must match, too.)
+<P>
+
+You may enter a complete email address, part of an address, or a
+list of addresses or partial addresses.
+For example:
+<P>
+
+<PRE>
+ To pattern = friend@public.com
+ To pattern = rated.net
+ To pattern = xxx@adults.com
+ admin@msn.com
+ fool@motleyfool.com
+</PRE>
+
+<P>
+Each of those are valid To patterns.
+<P>
+
+Messages match those patterns if any of the
+addresses in the To: line of the message contains the pattern.
+If the pattern is a list of patterns
+(like the last example above) then it is a match if any of the patterns in
+the list match any of the addresses in the To: line.
+(It is not possible to specify two addresses that must <EM>BOTH</EM> be
+present for a match.
+It is only possible to specify that <EM>EITHER</EM> address1 <EM>OR</EM>
+address2 must be present.
+That is exactly what using a list does.)
+<P>
+
+Some messages may be &quot;bounced&quot; to you, and will
+have a &quot;Resent-To:&quot; header line.
+If the message contains a Resent-To: line
+and the feature <A HREF="h_config_use_resentto"><!--#echo var="FEAT_use-resent-to-in-rules"--></A> is turned on,
+Alpine will look for
+matches to your &quot;To patterns&quot; there, and <EM>NOT</EM> in
+the original To: line.
+<P>
+
+When entering a pattern, you may choose an address from your address book
+with the &quot;T&quot; command.
+<P>
+
+It is possible to add a <EM>NOT</EM> to the To Pattern meaning with the
+&quot;!&quot; &quot;toggle NOT&quot; command.
+This changes the meaning of the To pattern so that it has the opposite meaning.
+It will be considered a match if there are no matches between the
+addresses in the To: line and the list of To patterns.
+<P>
+Don't make the mistake of putting the &quot;!&quot; in the data field for
+the pattern.
+For example, if you type the characters &quot;!frizzle&quot; into the To
+pattern, the pattern will look like:
+<P>
+<PRE>
+ To pattern = !frizzle
+</PRE>
+<P>
+This means you want to match the 8 character sequence &quot;!frizzle&quot;.
+In order to match messages that do not have &quot;frizzle&quot; in
+their To field, first type the characters &quot;frizzle&quot; followed
+by carriage return for the value of the To pattern, then negate it
+by typing the &quot;!&quot; command.
+It should end up looking like
+<P>
+<PRE>
+ ! To pattern = frizzle
+</PRE>
+<P>
+
+You are not limited to using the six standard header patterns that are
+normally shown (To, From, Sender, Cc, News, and Subject).
+You may add any other header to a Pattern by
+using the &quot;eXtraHdr&quot; command to specify a different
+message header line; and then the Add or Change command to fill in
+a pattern for the new header line, just like you would for a standard header.
+<P>
+A technicality: Since comma is the character used to separate multiple
+values in a pattern field, you have to escape comma with a backslash (&#92;) if
+you want to include a literal comma in the field.
+In other words, if you type a backslash followed by a comma it will
+be interpreted as a comma by Alpine, instead of as a separator between
+pattern values.
+All other backslashes are literal backslashes and should not be escaped.
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_other_topat =======
+<HTML>
+<HEAD>
+<TITLE>&quot;To:&quot; Pattern Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>&quot;To:&quot; Pattern Explained</H1>
+
+For some of the OTHER RULES actions, there is no message that is being
+compared against.
+If that is the case, then only the Current Folder Type is checked.
+In particular, this To pattern is ignored.
+Actions that fall into this category include both
+Sort Order and Index Format.
+<P>
+A technicality: Since comma is the character used to separate multiple
+values in a pattern field, you have to escape comma with a backslash (&#92;) if
+you want to include a literal comma in the field.
+In other words, if you type a backslash followed by a comma it will
+be interpreted as a comma by Alpine, instead of as a separator between
+pattern values.
+All other backslashes are literal backslashes and should not be escaped.
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_filt_topat =======
+<HTML>
+<HEAD>
+<TITLE>&quot;To:&quot; Pattern Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>&quot;To:&quot; Pattern Explained</H1>
+
+Any text you enter as the &quot;To pattern&quot;
+will be compared to the recipients from the To: line of
+messages when Alpine opens folders.
+When the text you entered matches
+all or part of the To: line of a message, then the Filter Action you have
+specified will be carried out.
+(Any other non-blank parts of the Pattern must match, too.)
+<P>
+
+You may enter a complete email address, part of an address, or a
+list of addresses or partial addresses.
+For example:
+<P>
+
+<PRE>
+ To pattern = friend@public.com
+ To pattern = rated.net
+ To pattern = xxx@adults.com
+ admin@msn.com
+ fool@motleyfool.com
+</PRE>
+
+<P>
+Each of those are valid To patterns.
+<P>
+
+Messages match those patterns if any of the
+addresses in the To: line of the message contains the pattern.
+If the pattern is a list of patterns
+(like the last example above) then it is a match if any of the patterns in
+the list match any of the addresses in the To: line.
+(It is not possible to specify two addresses that must <EM>BOTH</EM> be
+present for a match.
+It is only possible to specify that <EM>EITHER</EM> address1 <EM>OR</EM>
+address2 must be present.
+That is exactly what using a list does.)
+<P>
+
+Some messages may be &quot;bounced&quot; to you, and will
+have a &quot;Resent-To:&quot; header line.
+If the message contains a Resent-To: line
+and the feature <A HREF="h_config_use_resentto"><!--#echo var="FEAT_use-resent-to-in-rules"--></A> is turned on,
+Alpine will look for
+matches to your &quot;To patterns&quot; there, and <EM>NOT</EM> in
+the original To: line.
+<P>
+
+When entering a pattern, you may choose an address from your address book
+with the &quot;T&quot; command.
+<P>
+
+It is possible to add a <EM>NOT</EM> to the To Pattern meaning with the
+&quot;!&quot; &quot;toggle NOT&quot; command.
+This changes the meaning of the To pattern so that it has the opposite meaning.
+It will be considered a match if there are no matches between the
+addresses in the To: line and the list of To patterns.
+<P>
+Don't make the mistake of putting the &quot;!&quot; in the data field for
+the pattern.
+For example, if you type the characters &quot;!frizzle&quot; into the To
+pattern, the pattern will look like:
+<P>
+<PRE>
+ To pattern = !frizzle
+</PRE>
+<P>
+This means you want to match the 8 character sequence &quot;!frizzle&quot;.
+In order to match messages that do not have &quot;frizzle&quot; in
+their To field, first type the characters &quot;frizzle&quot; followed
+by carriage return for the value of the To pattern, then negate it
+by typing the &quot;!&quot; command.
+It should end up looking like
+<P>
+<PRE>
+ ! To pattern = frizzle
+</PRE>
+<P>
+
+You are not limited to using the six standard header patterns that are
+normally shown (To, From, Sender, Cc, News, and Subject).
+You may add any other header to a Pattern by
+using the &quot;eXtraHdr&quot; command to specify a different
+message header line; and then the Add or Change command to fill in
+a pattern for the new header line, just like you would for a standard header.
+<P>
+A technicality: Since comma is the character used to separate multiple
+values in a pattern field, you have to escape comma with a backslash (&#92;) if
+you want to include a literal comma in the field.
+In other words, if you type a backslash followed by a comma it will
+be interpreted as a comma by Alpine, instead of as a separator between
+pattern values.
+All other backslashes are literal backslashes and should not be escaped.
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_topat =======
+<HTML>
+<HEAD>
+<TITLE>&quot;To:&quot; Pattern Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>&quot;To:&quot; Pattern Explained</H1>
+
+Any text you enter as the &quot;To pattern&quot;
+will be compared to the recipients from the To: line of
+the message being replied to or forwarded.
+(Any other non-blank parts of the Pattern must match, too.)
+In the case of the Compose command, this pattern and the other header
+patterns are ignored.
+<P>
+
+You may enter a complete email address, part of an address, or a
+list of addresses or partial addresses.
+For example:
+<P>
+
+<PRE>
+ To pattern = friend@public.com
+ To pattern = rated.net
+ To pattern = xxx@adults.com
+ admin@msn.com
+ fool@motleyfool.com
+</PRE>
+
+<P>
+Each of those are valid To patterns.
+<P>
+
+Messages match those patterns if any of the
+addresses in the To: line of the message contains the pattern.
+If the pattern is a list of patterns
+(like the last example above) then it is a match if any of the patterns in
+the list match any of the addresses in the To: line.
+(It is not possible to specify two addresses that must <EM>BOTH</EM> be
+present for a match.
+It is only possible to specify that <EM>EITHER</EM> address1 <EM>OR</EM>
+address2 must be present.
+That is exactly what using a list does.)
+<P>
+
+Some messages may be &quot;bounced&quot; to you, and will
+have a &quot;Resent-To:&quot; header line.
+If the message contains a Resent-To: line
+and the feature <A HREF="h_config_use_resentto"><!--#echo var="FEAT_use-resent-to-in-rules"--></A> is turned on,
+Alpine will look for
+matches to your &quot;To patterns&quot; there, and <EM>NOT</EM> in
+the original To: line.
+<P>
+
+When entering a pattern, you may choose an address from your address book
+with the &quot;T&quot; command.
+<P>
+
+It is possible to add a <EM>NOT</EM> to the To Pattern meaning with the
+&quot;!&quot; &quot;toggle NOT&quot; command.
+This changes the meaning of the To pattern so that it has the opposite meaning.
+It will be considered a match if there are no matches between the
+addresses in the To: line and the list of To patterns.
+<P>
+Don't make the mistake of putting the &quot;!&quot; in the data field for
+the pattern.
+For example, if you type the characters &quot;!frizzle&quot; into the To
+pattern, the pattern will look like:
+<P>
+<PRE>
+ To pattern = !frizzle
+</PRE>
+<P>
+This means you want to match the 8 character sequence &quot;!frizzle&quot;.
+In order to match messages that do not have &quot;frizzle&quot; in
+their To field, first type the characters &quot;frizzle&quot; followed
+by carriage return for the value of the To pattern, then negate it
+by typing the &quot;!&quot; command.
+It should end up looking like
+<P>
+<PRE>
+ ! To pattern = frizzle
+</PRE>
+<P>
+
+You are not limited to using the six standard header patterns that are
+normally shown (To, From, Sender, Cc, News, and Subject).
+You may add any other header to a Pattern by
+using the &quot;eXtraHdr&quot; command to specify a different
+message header line; and then the Add or Change command to fill in
+a pattern for the new header line, just like you would for a standard header.
+<P>
+A technicality: Since comma is the character used to separate multiple
+values in a pattern field, you have to escape comma with a backslash (&#92;) if
+you want to include a literal comma in the field.
+In other words, if you type a backslash followed by a comma it will
+be interpreted as a comma by Alpine, instead of as a separator between
+pattern values.
+All other backslashes are literal backslashes and should not be escaped.
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_frompat =======
+<HTML>
+<HEAD>
+<TITLE>&quot;From:&quot; Pattern Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>&quot;From:&quot; Pattern Explained</H1>
+
+This is just like the &quot;To pattern&quot; except that it is compared with
+the address in the From: line of the message
+instead of the addresses from the To: line.
+See the help for the To pattern for more information on header patterns.
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_senderpat =======
+<HTML>
+<HEAD>
+<TITLE>&quot;Sender:&quot; Pattern Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>&quot;Sender:&quot; Pattern Explained</H1>
+
+This is just like the &quot;To pattern&quot; except that it is compared with
+the address from the Sender: line of the message
+instead of the addresses from the To: line.
+See the help for the To pattern for more information on header patterns.
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_ccpat =======
+<HTML>
+<HEAD>
+<TITLE>&quot;Cc:&quot; Pattern Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>&quot;Cc:&quot; Pattern Explained</H1>
+
+This is just like the &quot;To pattern&quot; except that it is compared with
+the addresses from the Cc: line of the message
+instead of the addresses from the To: line.
+See the help for the To pattern for more information on header patterns.
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_recippat =======
+<HTML>
+<HEAD>
+<TITLE>Recipient Pattern Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Recipient Pattern Explained</H1>
+
+This is just like the &quot;To pattern&quot; except that it is compared with
+the addresses from both the To: line and the Cc: line of the
+message instead of just the addresses from the To: line.
+In other words, it is considered a match if the pattern matches
+<EM>EITHER</EM> an address in the To: line <EM>OR</EM> an address
+in the Cc: line.
+(Notice that defining the Recipient pattern does not have the same
+effect as defining both the To and Cc patterns.
+Recipient is To <EM>OR</EM> Cc; not To <EM>AND</EM> Cc.
+It is equivalent to having two different rules;
+one with a To pattern and the other with the same Cc pattern.)
+<P>
+A technicality: Since comma is the character used to separate multiple
+values in a pattern field, you have to escape comma with a backslash (&#92;) if
+you want to include a literal comma in the field.
+In other words, if you type a backslash followed by a comma it will
+be interpreted as a comma by Alpine, instead of as a separator between
+pattern values.
+All other backslashes are literal backslashes and should not be escaped.
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_particpat =======
+<HTML>
+<HEAD>
+<TITLE>Participant Pattern Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Participant Pattern Explained</H1>
+
+This is just like the &quot;To pattern&quot; except that it is compared with
+the addresses from the From: line, the To: line, and the Cc: line of the
+message instead of just the addresses from the To: line.
+In other words, it is considered a match if the pattern matches
+<EM>EITHER</EM> an address in the From: line, <EM>OR</EM> an address
+in the To: line, <EM>OR</EM> an address in the Cc: line.
+(Notice that defining the Participant pattern does not have the same
+effect as defining all of the From, To, and Cc patterns.
+Participant is From <EM>OR</EM> To <EM>OR</EM> Cc; not
+From <EM>AND</EM> To <EM>AND</EM> Cc.
+It is equivalent to having three different rules;
+one with a From pattern, another with the same To pattern, and a third with
+the same Cc pattern.)
+<P>
+A technicality: Since comma is the character used to separate multiple
+values in a pattern field, you have to escape comma with a backslash (&#92;) if
+you want to include a literal comma in the field.
+In other words, if you type a backslash followed by a comma it will
+be interpreted as a comma by Alpine, instead of as a separator between
+pattern values.
+All other backslashes are literal backslashes and should not be escaped.
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_newspat =======
+<HTML>
+<HEAD>
+<TITLE>News Pattern Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>News Pattern Explained</H1>
+
+If this pattern is non-blank, then for this rule to be considered a
+match, at least one of the newsgroups from
+the Newsgroups line of the message must match this pattern.
+If this pattern is a list of patterns, then at least one of the
+newsgroups must match at least one of the patterns.
+(Any other non-blank parts of the Pattern must match, too.)
+<P>
+It is possible to add a <EM>NOT</EM> to the News Pattern meaning with the
+&quot;!&quot; &quot;toggle NOT&quot; command.
+This changes the meaning of the News pattern so that it has the opposite meaning.
+It will be considered a match if there are no matches between the
+addresses in the Newsgroups: line and the list of News patterns.
+<P>
+Don't make the mistake of putting the &quot;!&quot; in the data field for
+the pattern.
+For example, if you type the characters &quot;!frizzle&quot; into the News
+pattern, the pattern will look like:
+<P>
+<PRE>
+ News pattern = !frizzle
+</PRE>
+<P>
+This means you want to match the 8 character sequence &quot;!frizzle&quot;.
+In order to match messages that do not have &quot;frizzle&quot; in
+their Newsgroups header, first type the characters &quot;frizzle&quot; followed
+by carriage return for the value of the News pattern, then negate it
+by typing the &quot;!&quot; command.
+It should end up looking like
+<P>
+<PRE>
+ ! News pattern = frizzle
+</PRE>
+<P>
+A technicality: Since comma is the character used to separate multiple
+values in a pattern field, you have to escape comma with a backslash (&#92;) if
+you want to include a literal comma in the field.
+In other words, if you type a backslash followed by a comma it will
+be interpreted as a comma by Alpine, instead of as a separator between
+pattern values.
+All other backslashes are literal backslashes and should not be escaped.
+<P>
+
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_subjpat =======
+<HTML>
+<HEAD>
+<TITLE>&quot;Subject:&quot; Pattern Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>&quot;Subject:&quot; Pattern Explained</H1>
+
+This is similar to the other parts of the Pattern.
+It is compared with
+the contents from the Subject of the message.
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+It is possible to add a <EM>NOT</EM> to the Subject Pattern meaning with the
+&quot;!&quot; &quot;toggle NOT&quot; command.
+This changes the meaning of the Subject pattern so that it has the opposite meaning.
+It will be considered a match if there are no matches between the
+text in the Subject: line and the list of Subject patterns.
+<P>
+
+If you wish to have a header pattern that is not one of the six standard
+header patterns, you may add it with the &quot;eXtraHdr&quot; command.
+<P>
+A technicality: Since comma is the character used to separate multiple
+values in a pattern field, you have to escape comma with a backslash (&#92;) if
+you want to include a literal comma in the field.
+In other words, if you type a backslash followed by a comma it will
+be interpreted as a comma by Alpine, instead of as a separator between
+pattern values.
+All other backslashes are literal backslashes and should not be escaped.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_alltextpat =======
+<HTML>
+<HEAD>
+<TITLE>AllText Pattern Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>AllText Pattern Explained</H1>
+
+This is similar to the header patterns.
+Instead of comparing with text in a particular header field it
+is compared with all of the text in the message header and body.
+<P>
+It is possible to add a <EM>NOT</EM> to the AllText Pattern meaning with the
+&quot;!&quot; &quot;toggle NOT&quot; command.
+This changes the meaning of the AllText pattern so that it has the opposite meaning.
+It will be considered a match if there are no matches between the
+text of the message and the list of AllText patterns.
+<P>
+Don't make the mistake of putting the &quot;!&quot; in the data field for
+the pattern.
+For example, if you type the characters &quot;!frizzle&quot; into the AllText
+pattern, the pattern will look like:
+<P>
+<PRE>
+ AllText pattern = !frizzle
+</PRE>
+<P>
+This means you want to match the 8 character sequence &quot;!frizzle&quot;.
+In order to match messages that do not have &quot;frizzle&quot; in
+the text of the message, first type the characters &quot;frizzle&quot; followed
+by carriage return for the value of the AllText pattern, then negate it
+by typing the &quot;!&quot; command.
+It should end up looking like
+<P>
+<PRE>
+ ! AllText pattern = frizzle
+</PRE>
+<P>
+It is possible that you may notice degraded performance when using
+AllText Patterns.
+<P>
+A technicality: Since comma is the character used to separate multiple
+values in a pattern field, you have to escape comma with a backslash (&#92;) if
+you want to include a literal comma in the field.
+In other words, if you type a backslash followed by a comma it will
+be interpreted as a comma by Alpine, instead of as a separator between
+pattern values.
+All other backslashes are literal backslashes and should not be escaped.
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_bodytextpat =======
+<HTML>
+<HEAD>
+<TITLE>BodyText Pattern Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>BodyText Pattern Explained</H1>
+
+This is similar to the header patterns.
+Instead of comparing with text in a particular header field it
+is compared with all of the text in the message body.
+<P>
+It is possible to add a <EM>NOT</EM> to the BodyText Pattern meaning with the
+&quot;!&quot; &quot;toggle NOT&quot; command.
+This changes the meaning of the BodyText pattern so that it has the opposite meaning.
+It will be considered a match if there are no matches between the
+text of the body of the message and the list of BodyText patterns.
+<P>
+Don't make the mistake of putting the &quot;!&quot; in the data field for
+the pattern.
+For example, if you type the characters &quot;!frizzle&quot; into the BodyText
+pattern, the pattern will look like:
+<P>
+<PRE>
+ BdyText pattern = !frizzle
+</PRE>
+<P>
+This means you want to match the 8 character sequence &quot;!frizzle&quot;.
+In order to match messages that do not have &quot;frizzle&quot; in
+their BodyText, first type the characters &quot;frizzle&quot; followed
+by carriage return for the value of the BodyText pattern, then negate it
+by typing the &quot;!&quot; command.
+It should end up looking like
+<P>
+<PRE>
+ ! BodyText pattern = frizzle
+</PRE>
+<P>
+It is possible that you may notice degraded performance when using
+BodyText Patterns.
+<P>
+A technicality: Since comma is the character used to separate multiple
+values in a pattern field, you have to escape comma with a backslash (&#92;) if
+you want to include a literal comma in the field.
+In other words, if you type a backslash followed by a comma it will
+be interpreted as a comma by Alpine, instead of as a separator between
+pattern values.
+All other backslashes are literal backslashes and should not be escaped.
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_charsetpat =======
+<HTML>
+<HEAD>
+<TITLE>Character Set Pattern Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Character Set Pattern Explained</H1>
+
+A message may use one or more character sets.
+This part of the Pattern matches messages that make use of
+certain specified character sets.
+It will be considered a match if a message uses any of the character
+sets in the list you give here.
+
+<P>
+When filling in a value for this field, you may use
+the &quot;T&quot; command, which presents you with a large list of
+possible character sets to choose from.
+You may also just type in the name of a character set, and it need not
+be one that Alpine knows about.
+
+<P>
+Besides actual character set names (for example, ISO-8859-7, KOI8-R, or
+GB2312) you may also use some shorthand names that Alpine provides.
+These names are more understandable shorthand names for sets of
+character set names.
+Two examples are &quot;Cyrillic&quot; and &quot;Greek&quot;.
+Selecting one of these shorthand names is equivalent to selecting all of
+the character sets that make up the set.
+You can see all of these shorthand names and the lists of character sets
+they stand for by typing the &quot;T&quot; command.
+
+<P>
+For the purposes of this Pattern,
+Alpine will search through a message for all of the text parts and
+collect the character sets declared for each part.
+It will also look in the Subject line for a character set used there.
+Alpine does not actually look at the text of the message or the text
+of the Subject to determine if a declared character set is actually
+used, it looks only at the declarations themselves in the MIME part headers
+and in the Subject.
+
+<P>
+It is possible to add a <EM>NOT</EM> to the Character Set Pattern meaning with the
+&quot;!&quot; &quot;toggle NOT&quot; command.
+This changes the meaning of the Character Set pattern so that
+it has the opposite meaning.
+It will be considered a match if none of the character sets in the
+list are used in a message.
+<P>
+Don't make the mistake of putting the &quot;!&quot; in the data field for
+the pattern.
+For example, if you type the characters &quot;!GB2312&quot; into the
+Character Set pattern, the pattern will look like:
+<P>
+<PRE>
+ Charset pattern = !GB2312
+</PRE>
+<P>
+This means you want to match the 7 character sequence &quot;!GB2312&quot;.
+In order to match messages that do not have the
+character set &quot;GB2312&quot;
+set, first type the characters &quot;GB2312&quot; followed
+by carriage return for the value of the Character Set pattern, then negate it
+by typing the &quot;!&quot; command.
+It should end up looking like
+<P>
+<PRE>
+ ! Charset pattern = GB2312
+</PRE>
+<P>
+A technicality: Since comma is the character used to separate multiple
+values in a pattern field, you have to escape comma with a backslash (&#92;) if
+you want to include a literal comma in the field.
+In other words, if you type a backslash followed by a comma it will
+be interpreted as a comma by Alpine, instead of as a separator between
+pattern values.
+All other backslashes are literal backslashes and should not be escaped.
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_keywordpat =======
+<HTML>
+<HEAD>
+<TITLE>Keyword Pattern Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Keyword Pattern Explained</H1>
+
+A folder may have user-defined keywords.
+These are similar to the Important flag, which the user may set using the
+Flag command.
+The difference is that the Important flag is always present for each folder.
+User-defined keywords are picked by the user.
+You may add new keywords by defining them in the
+<A HREF="h_config_keywords"><!--#echo var="VAR_keywords"--></A> option in the Setup/Config screen.
+After you have added a potential keyword with the <!--#echo var="VAR_keywords"--> option,
+the Flag command may be used to set or clear the keyword on individual messages.
+If you have given a keyword a nickname when configuring it,
+that nickname may be used instead of the actual keyword.
+
+<P>
+When filling in a value for this field, it may be easiest to use
+the &quot;T&quot; command, which presents you with a list of the keywords
+you have defined to choose from.
+
+<P>
+This part of the Pattern matches messages with certain keywords set.
+It will be considered a match if a message has any of the keywords in the
+list set.
+A keyword that you have not defined using the
+<A HREF="h_config_keywords"><!--#echo var="VAR_keywords"--></A> option in the Setup/Config screen
+will not be a match.
+
+<P>
+It is possible to add a <EM>NOT</EM> to the Keyword Pattern meaning with the
+&quot;!&quot; &quot;toggle NOT&quot; command.
+This changes the meaning of the Keyword pattern so that it has the opposite meaning.
+It will be considered a match if none of the keywords in the list are set
+for a message.
+A keyword that you have not defined using the
+<A HREF="h_config_keywords"><!--#echo var="VAR_keywords"--></A> option in the Setup/Config screen
+will not be a match, so a <EM>NOT</EM> of that keyword does match.
+<P>
+Don't make the mistake of putting the &quot;!&quot; in the data field for
+the pattern.
+For example, if you type the characters &quot;!frizzle&quot; into the Keyword
+pattern, the pattern will look like:
+<P>
+<PRE>
+ Keyword pattern = !frizzle
+</PRE>
+<P>
+This means you want to match the 8 character sequence &quot;!frizzle&quot;.
+In order to match messages that do not have the keyword &quot;frizzle&quot;
+set, first type the characters &quot;frizzle&quot; followed
+by carriage return for the value of the Keyword pattern, then negate it
+by typing the &quot;!&quot; command.
+It should end up looking like
+<P>
+<PRE>
+ ! Keyword pattern = frizzle
+</PRE>
+<P>
+A technicality: Since comma is the character used to separate multiple
+values in a pattern field, you have to escape comma with a backslash (&#92;) if
+you want to include a literal comma in the field.
+In other words, if you type a backslash followed by a comma it will
+be interpreted as a comma by Alpine, instead of as a separator between
+pattern values.
+All other backslashes are literal backslashes and should not be escaped.
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_arbpat =======
+<HTML>
+<HEAD>
+<TITLE>Extra Header Patterns Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Extra Header Patterns Explained</H1>
+
+The header patterns that come after the Participant pattern but before the
+AllText pattern are extra header patterns that you have added to a rule's
+Pattern. These are just like the other header patterns except that
+the contents of the particular header listed on the left hand side will
+be used for comparisons.
+<P>
+The &quot;eXtraHdr&quot; command may be used to add more of these
+header patterns to the rule you are editing.
+<P>
+The &quot;RemoveHdr&quot; command may be used to delete the highlighted
+extra header pattern from the rule you are editing.
+<P>
+It is possible to add a <EM>NOT</EM> to the Extra Header Pattern meaning with the
+&quot;!&quot; &quot;toggle NOT&quot; command.
+This changes the meaning of the pattern so that it has the opposite meaning.
+It will be considered a match if there are no matches between the
+text in the header line and the list of patterns.
+<P>
+Don't make the mistake of putting the &quot;!&quot; in the data field for
+the pattern.
+For example, if you type the characters &quot;!frizzle&quot; into the
+pattern, the pattern will look like:
+<P>
+<PRE>
+ Xyz pattern = !frizzle
+</PRE>
+<P>
+This means you want to match the 8 character sequence &quot;!frizzle&quot;.
+In order to match messages that do not have &quot;frizzle&quot; in
+their Xyz field, first type the characters &quot;frizzle&quot; followed
+by carriage return for the value of the pattern, then negate it
+by typing the &quot;!&quot; command.
+It should end up looking like
+<P>
+<PRE>
+ ! Xyz pattern = frizzle
+</PRE>
+
+<P>
+A technicality: Since comma is the character used to separate multiple
+values in a pattern field, you have to escape comma with a backslash (&#92;) if
+you want to include a literal comma in the field.
+In other words, if you type a backslash followed by a comma it will
+be interpreted as a comma by Alpine, instead of as a separator between
+pattern values.
+All other backslashes are literal backslashes and should not be escaped.
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_cat_cmd =======
+<HTML>
+<HEAD>
+<TITLE>Categorizer Command Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Categorizer Command Explained</H1>
+
+This is a command that is run with its standard input set to the message
+being checked and its standard output discarded.
+The full directory path should be specified.
+The command will be run and then its exit status will be checked against
+the Exit Status Interval, which defaults to just the value zero.
+If the exit status of the command falls in the interval, it is considered
+a match, otherwise it is not a match.
+<P>
+
+This option may actually be a list of commands.
+The first one that exists and is executable is used.
+That makes it possible to use the same configuration with Unix Alpine and
+PC-Alpine.
+<P>
+
+If none of the commands in the list exists and is executable then the rule
+is <EM>not</EM> a match.
+If it is possible that the command may not exist, you should be careful
+to structure your rules so that nothing destructive
+happens when the command does not exist.
+For example, you might have a filter that filters away spam when there is
+a match but does nothing when there is not a match.
+That would cause no harm if the command didn't exist.
+However, if you have a filter that filters away spam when there is not
+a match and keeps it when there is a match, that would filter everything
+if the categorizer command didn't exist.
+<P>
+Here is an <A HREF="h_config_role_cat_cmd_example">example</A>
+setup for the bogofilter filter.
+
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_cat_cmd_example =======
+<HTML>
+<HEAD>
+<TITLE>Categorizer Command Example</TITLE>
+</HEAD>
+<BODY>
+<H1>Categorizer Command Example</H1>
+
+Bogofilter
+(<A HREF="http://bogofilter.sourceforge.net/">http://bogofilter.sourceforge.net/</A>)
+is a mail filter that attempts to classify mail as spam or
+non-spam using statistical analysis of the message content.
+When run with no arguments and a message as standard input, it exits with
+exit status 0 if it thinks a message is spam and 1 if it thinks
+it is not spam.
+To use bogofilter as your Categorizer Command you would simply set Command to
+the pathname of the bogofilter program.
+For example,
+<P>
+<CENTER><SAMP>Command = /usr/local/bin/bogofilter</SAMP></CENTER>
+<P>
+Exit status of zero is what you are interested in, so you'd set the
+Exit Status Interval to
+<P>
+<CENTER><SAMP>Exit Status Interval = (0,0)</SAMP></CENTER>
+<P>
+
+In order to prevent downloading an entire huge message to check for spam, you
+might want to set the Character Limit to a few thousand characters (the
+assumption being that the spam will reveal itself in those characters)
+<P>
+<CENTER><SAMP>Character Limit = 50000</SAMP></CENTER>
+<P>
+
+You would probably use bogofilter in an Alpine Filter Rule, and have the action
+be to move the message to a spam folder.
+It would usually be wise to also check the &quot;Message is Recent&quot;
+part of the rule so that messages are only checked when they first arrive,
+and to restrict the Current Folder Type to just your INBOX.
+The reason for checking only Recent messages is to save the time it takes
+to run bogofilter on each message.
+As an experiment, you might start out by using this in an Indexcolor Rule
+instead of a Filter Rule.
+In that case, you probably wouldn't check the Recent checkbox.
+<P>
+The use described above assumes that you are somehow maintaining bogofilter's
+database of words associated with spam and non-spam messages.
+One way to start your database would be to select a bunch of spam messages
+in Alpine (you might Save spam messages to a special folder or use Alpine's
+Select command to select several) and then Apply
+(<A HREF="h_config_enable_agg_ops"><!--#echo var="FEAT_enable-aggregate-command-set"--></A>)
+a pipe command to the spam messages.
+For example, you could have a shell script or an alias
+called <EM>this_is_spam</EM>, which would simply be the command
+<P>
+<CENTER><SAMP>bogofilter -s</SAMP></CENTER>
+<P>
+
+It is probably best to use the pipe command's Raw Text, With Delimiter,
+and Free Output options,
+which are at the bottom of the screen when you type the pipe command.
+That's because bogofilter expects the raw message as input, and uses
+the Delimiters to tell when a new message starts.
+You would not need to use a separate pipe for each message, because
+bogofilter can handle multiple messages at once.
+<P>
+Similarly, you would select a group of non-spam messages
+and run them through a <EM>this_is_nonspam</EM> script
+that was something like
+<P>
+<CENTER><SAMP>bogofilter -n</SAMP></CENTER>
+<P>
+
+For the more adventurous, the next step might be to automate the upkeep of
+the bogofilter database.
+It might make more sense to have bogofilter be part of the delivery process,
+but it is also possible to do it entirely from within Alpine.
+Instead of using just plain &quot;bogofilter&quot; as the Categorizer Command,
+the &quot;-u&quot; argument will cause bogofilter to update the database.
+<P>
+<CENTER><SAMP>Command = /usr/local/bin/bogofilter -u</SAMP></CENTER>
+<P>
+You'd want a couple more aliases or shell scripts called something like
+<EM>change_to_spam</EM>
+<P>
+<CENTER><SAMP>bogofilter -Ns</SAMP></CENTER>
+<P>
+and
+<EM>change_to_nonspam</EM>
+<P>
+<CENTER><SAMP>bogofilter -Sn</SAMP></CENTER>
+<P>
+When you run across a message in your INBOX that should have been
+classified as spam you would pipe it to the change_to_spam script, and
+when you run across a message in your spam folder that should have been
+left in your INBOX you would pipe it through change_to_nonspam.
+
+<P>
+There is a technical problem with this approach.
+Alpine may check your filters more than once.
+In particular, every time you start Alpine the filters will be checked for
+each message.
+Also, if you have any filters that depend on message state (New, Deleted, etc.)
+then Alpine will recheck for matches in messages that have changed state
+at the time you close the folder and before expunging.
+This is usually ok.
+However, in this case it is a problem because the command
+<P>
+<CENTER><SAMP>Command = /usr/local/bin/bogofilter -u</SAMP></CENTER>
+<P>
+has the side effect of updating the database.
+So you run the risk of updating the database multiple times for a single
+message instead of updating it just once per message.
+There are some ways to work around this problem.
+What you need is a way to mark the message after you have run the filter.
+One way to mark messages is with the use of a keyword (say &quot;Bogo&quot;).
+Besides having the filter move the message to a spam folder, also have it
+set the Bogo keyword.
+(Note that you will have to set up the &quot;Bogo&quot; keyword in the
+<A HREF="h_config_keywords"><!--#echo var="VAR_keywords"--></A> option in Setup/Config.)
+This rule can only set the Bogo keyword for the messages that it matches.
+You will also need to add a second rule right after this one that
+matches all the messages that don't have the Bogo keyword set
+(put the keyword in the Keyword pattern and toggle
+the Not with the ! command)
+and takes the action of setting it.
+Then change the &quot;bogofilter -u&quot; rule so that it won't be a match
+(and so it won't re-run the bogofilter command) if the keyword is already
+set.
+<P>
+What you will end up with is a rule that runs &quot;bogofilter -u&quot;
+on all messages that don't have the Bogo keyword set.
+This will have the side effect of inserting that message in the bogofilter
+database, match or not.
+If this rule matches (it is spam), the Bogo keyword will be set and
+the message will be moved to a spam folder.
+If it does not match, the
+following rule will mark the message by turning on the keyword.
+This second rule should be a non-terminating
+(<A HREF="h_config_filt_opts_nonterm">Dont-Stop-Even-if-Rule-Matches</A>)
+rule so that it doesn't stop the filtering process before the rest of
+your rules are consulted.
+
+<P>
+In summary, the first rule is something like
+<PRE>
+ Nickname = bogofilter -u rule
+ Current Folder Type =
+ (*) Specific
+ Folder = INBOX
+
+ ! Keyword pattern = Bogo
+
+ External Categorizer Commands =
+ Command = /usr/local/bin/bogofilter -u
+ Exit Status Interval = (0,0)
+ Character Limit = <No Value Set: using "-1"> (optionally set this)
+
+ Filter Action =
+ (*) Move
+ Folder = spam
+
+ Set These Keywords = Bogo
+</PRE>
+<P>
+and the following rule is
+<PRE>
+ Nickname = Set Bogo Keyword
+ Current Folder Type =
+ (*) Specific
+ Folder = INBOX
+
+ ! Keyword pattern = Bogo
+
+ Filter Action =
+ (*) Just Set Message Status
+
+ Set These Keywords = Bogo
+
+ Features =
+ [X] dont-stop-even-if-rule-matches
+</PRE>
+<P>
+If it is possible for you to insert bogofilter in the delivery process instead
+of having it called from Alpine you could prevent having to wait
+for the bogofilter processing while you read your mail.
+You would have bogofilter add a header to the message at the time of delivery
+that identified it as spam or nonspam.
+With this method, you could avoid using a Categorizer Command while running Alpine,
+and just match on the header instead.
+You might still want to use the scripts mentioned above to initialize the
+database or to re-classify wrongly classified messages.
+
+<P>
+Finally, it isn't for the faint-hearted,
+but it is also possible to run bogofilter from PC-Alpine.
+You can install Cygwin from
+<A HREF="http://www.cygwin.com/">http://www.cygwin.com/</A> and
+then compile bogofilter in the cygwin environment, and run it from
+within PC-Alpine.
+You would end up with a Categorizer command that looked something like
+<P>
+<CENTER><SAMP>Command = C:&#92;cygwin&#92;bin&#92;bogofilter.exe -u</SAMP></CENTER>
+<P>
+Note that the &quot;.exe&quot; extension is explicit,
+and that the bogofilter.exe executable should be in the same directory
+as cygwin1.dll.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_cat_status =======
+<HTML>
+<HEAD>
+<TITLE>Exit Status Interval Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Exit Status Interval Explained</H1>
+
+The categorizer command is run and the result is the exit status of
+that command.
+If that exit status falls in the Exit Status Interval
+then it is considered a match, otherwise it is not a match.
+Of course for the entire rule to match, it must also be checked against
+the other defined parts of the Pattern.
+<P>
+The Exit Status Interval defaults to the single value 0 (zero).
+If you define it, it should be set to something like:
+<P>
+<CENTER><SAMP>(min_exit_value,max_exit_value)</SAMP></CENTER>
+<P>
+where &quot;min_exit_value&quot; and &quot;max_exit_value&quot; are integers.
+The special values &quot;INF&quot; and &quot;-INF&quot; may be used for large
+positive and negative integers.
+<P>
+Actually, a list of intervals may be used if you wish.
+A list would look like
+<P>
+<CENTER><SAMP>(min_exit_value1,max_exit_value1),(min_exit_value2,max_exit_value2),...</SAMP></CENTER>
+<P>
+When there is an Exit Status Interval defined, it is a match if the exit status
+of the categorizer command is contained in any of the intervals.
+The intervals include both endpoints.
+<P>
+The default interval is
+<P>
+<CENTER><SAMP>(0,0)</SAMP></CENTER>
+<P>
+and it matches only if the command exits with exit status equal to zero.
+
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_cat_limit =======
+<HTML>
+<HEAD>
+<TITLE>Character Limit Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Character Limit Explained</H1>
+
+Setting this option makes it possible to limit how much of the message
+is made available to the categorizer command as input.
+The default value (-1) means that the entire message is fed to the
+command.
+A value of 0 (zero) means that only the headers of the message are
+made available.
+A positive integer means that the headers plus that many characters from
+the body of the message are passed to the categorizer.
+
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_age =======
+<HTML>
+<HEAD>
+<TITLE>Age Interval Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Age Interval Explained</H1>
+
+The Age Interval, if defined, is part of the Pattern.
+If you use this, it should be set to something like:
+<P>
+<CENTER><SAMP>(min_age,max_age)</SAMP></CENTER>
+<P>
+where &quot;min_age&quot; and &quot;max_age&quot; are non-negative integers.
+The special value &quot;INF&quot; may be used for the max value.
+It represents infinity.
+<P>
+In rare cases it may be useful to use the more general form of the value,
+which is a comma-separated list of intervals.
+It would look something like:
+<P>
+<CENTER><SAMP>(min_age1,max_age1),(min_age2,max_age2),...</SAMP></CENTER>
+<P>
+When there is an Age Interval defined, it is a match if the age, in days, of
+the message is contained in the interval.
+The interval includes both endpoints.
+If the option is set to a list of intervals then it is a match if the
+age of the message is contained in any of the intervals.
+<P>
+Even though this option is called Age, it isn't actually
+the <EM>age</EM> of the message.
+Instead, it is how many days ago the message arrived in one of your folders.
+If the current time is a little past midnight, then a message that arrived
+just before midnight arrived yesterday, even though the message is only
+a few minutes old.
+By default, the date being used is not the date in the Date
+header of the message.
+It is the date that the message arrived in one of your folders.
+When you Save a message from one folder to another that arrival date
+is preserved.
+If you would like to use the date in the Date header that is possible.
+Turn on the option
+<A HREF="h_config_filt_opts_sentdate">&quot;Use-Date-Header-For-Age&quot;</A>
+near the bottom of the rule definition.
+<P>
+A value of 0 is today, 1 is yesterday, 2 is the day before yesterday, and so on.
+The age interval
+<P>
+<CENTER><SAMP>(2,2)</SAMP></CENTER>
+<P>
+matches all messages that arrived on the day before yesterday.
+The interval
+<P>
+<CENTER><SAMP>(180,INF)</SAMP></CENTER>
+<P>
+matches all messages that arrived at least 180 days before today.
+The interval
+<P>
+<CENTER><SAMP>(0,1)</SAMP></CENTER>
+<P>
+matches all messages that arrived today or yesterday.
+
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_size =======
+<HTML>
+<HEAD>
+<TITLE>Size Interval Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Size Interval Explained</H1>
+
+The Size Interval, if defined, is part of the Pattern.
+If you use this, it should be set to something like:
+<P>
+<CENTER><SAMP>(min_size,max_size)</SAMP></CENTER>
+<P>
+where &quot;min_size&quot; and &quot;max_size&quot; are non-negative integers.
+The special value &quot;INF&quot; may be used for the max value.
+It represents infinity.
+<P>
+In rare cases it may be useful to use the more general form of the value,
+which is a comma-separated list of intervals.
+It would look something like:
+<P>
+<CENTER><SAMP>(min_size1,max_size1),(min_size2,max_size2),...</SAMP></CENTER>
+<P>
+When there is a Size Interval defined, it is a match if the size of
+the message is contained in the interval.
+The interval includes both endpoints.
+If the option is set to a list of intervals then it is a match if the
+size of the message is contained in any of the intervals.
+<P>
+The size interval
+<P>
+<CENTER><SAMP>(10000,50000)</SAMP></CENTER>
+<P>
+matches all messages with sizes greater than or equal to 10000, and less
+than or equal to 50000.
+The interval
+<P>
+<CENTER><SAMP>(100000,INF)</SAMP></CENTER>
+<P>
+matches all messages with sizes greater than or equal to 100000.
+
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_scorei =======
+<HTML>
+<HEAD>
+<TITLE>Score Interval Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Score Interval Explained</H1>
+
+The Score Interval, if defined, is part of the Pattern.
+If you use this, it should be set to something like:
+<P>
+<CENTER><SAMP>(min_score,max_score)</SAMP></CENTER>
+<P>
+where &quot;min_score&quot; and &quot;max_score&quot; are integers between
+-32000 and 32000.
+The special values &quot;-INF&quot; and &quot;INF&quot; may be used for
+the min and max values.
+These represent negative and positive infinity.
+<P>
+Actually, the value may be a list of intervals rather than just a
+single interval if that is useful.
+The elements of the list are separated by commas like:
+<P>
+<CENTER><SAMP>(min_score1,max_score1),(min_score2,max_score2),...</SAMP></CENTER>
+<P>
+When there is a Score Interval defined, it is a match if the score for
+the message is contained in any of the intervals.
+The intervals include both endpoints.
+The score for a message is calculated by looking at every scoring rule
+defined and adding up the Score Values for the rules that match the message.
+Scoring rules are created using the
+<A HREF="h_rules_score">&quot;SETUP SCORING&quot;</A> screen.
+
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_fldr_type =======
+<HTML>
+<HEAD>
+<TITLE>Current Folder Type Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Current Folder Type Explained</H1>
+
+The Current Folder Type is part of the role's Pattern.
+It refers to the type of the currently open folder, which is the folder
+you were last looking at from the MESSAGE INDEX or MESSAGE TEXT screen.
+In order for a role to be considered a match, the current folder must
+be of the type you set here.
+The three types &quot;Any&quot;, &quot;News&quot;, and &quot;Email&quot; are
+all what you might think.
+<P>
+If the Current Folder Type for a role's Pattern is set to &quot;News&quot;, for
+example, then
+that role will only be a match if the current folder is a newsgroup and
+the rest of the Pattern matches.
+The value &quot;Specific&quot; may be used when you want to limit the match
+to a specific folder (not just a specific type of folder), or to a list of
+specific folders.
+<P>
+In order to match a specific folder you Select the &quot;Specific&quot;
+button <EM>AND</EM> fill in
+the name (or list of names) of
+the folder in the &quot;Folder List&quot; field.
+If the current folder is any of the folders in the list, that is considered
+a match.
+The name of each folder in the list may be either &quot;INBOX&quot;,
+the technical specification
+of the folder (like what appears in your configuration file) or, if the
+folder is one of your incoming folders, it may be the nickname you've given
+the folder.
+Here are a couple samples of specific folder names:
+<P>
+<CENTER><SAMP>{monet.art.example.com}mail/art-class</SAMP></CENTER>
+<P>
+<CENTER><SAMP>{news.example.com/nntp}#news.comp.mail.pine</SAMP></CENTER>
+<P>
+The easiest way to fill in the &quot;Folder List&quot; field is to use
+the &quot;T&quot; command that is available when the &quot;Folder List&quot; line is
+highlighted, or to use the &quot;Take&quot; command with the configuration
+feature
+<A HREF="h_config_enable_role_take">&quot;<!--#echo var="FEAT_enable-rules-under-take"-->&quot;</A>
+turned on.
+Note that you won't be able to edit the &quot;Folder List&quot; line unless the
+Current Folder Type is set to &quot;Specific&quot;, and any value that
+&quot;Folder List&quot; has is ignored unless the type
+is set to &quot;Specific&quot;.
+<P>
+When reading a newsgroup, there may be a performance penalty
+incurred when collecting the information necessary to check a Pattern.
+For this reason, the default Current Folder Type is set to &quot;Email&quot;.
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_filt_rule_type =======
+<HTML>
+<HEAD>
+<TITLE>Filter Action Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Filter Action Explained</H1>
+
+The Filter Action specifies the action to be taken when the Pattern is a
+match.
+It may be set to &quot;Delete&quot; &quot;Move&quot;, or
+&quot;Just Set Message Status&quot;.
+<P>
+If it is set to &quot;Delete&quot;, then the message that matches the
+Pattern will be deleted from the open folder.
+<P>
+If it is set to &quot;Move&quot;, then the name of the folder to which
+the matching message should be moved is given in the &quot;Folder List&quot; field on the
+next line of the screen.
+A list of folders separated by commas may be given, in which case the
+message will be copied to all of the folders in the list before it is
+deleted.
+<P>
+If it is set to neither of those two values (it is set to the value
+labeled &quot;Just Set Message Status&quot;) then the message status
+setting will happen
+but the message will not be deleted or moved.
+<P>
+If you are Moving a message you may also set Message Status if you wish.
+<P>
+The easiest way to fill in the &quot;Folder List&quot; field is to use
+the T command that is available when the &quot;Folder List&quot; line is
+highlighted.
+Note that you won't be able to edit the &quot;Folder List&quot; line unless the
+Filter Action is set to &quot;Move&quot;, and any value that
+&quot;Folder List&quot; has is ignored unless the type
+is set to &quot;Move&quot;.
+<P>
+There are a few tokens that may be used in the names in the Folder List.
+They are all related to the date on which the filtering is taking place.
+The tokens are words surrounded by underscores.
+For example, if you want your filter to move messages to a folder named
+<P><CENTER><SAMP>abc-year-mon</SAMP></CENTER><P>
+you could specify the folder as
+<P><CENTER><SAMP>abc-_CURYEAR_-_CURMONTHABBREV_</SAMP></CENTER><P>
+which would result in a file named something like
+<P><CENTER><SAMP>abc-2004-oct</SAMP></CENTER><P>
+or
+<P><CENTER><SAMP>abc-_CURYEAR2DIGIT_-_CURMONTH2DIGIT_</SAMP></CENTER><P>
+which would result in a file named something like
+<P><CENTER><SAMP>abc-04-10</SAMP></CENTER><P>
+The available tokens are listed
+<A HREF="h_index_tokens">here</A>.
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_score_fldr_type =======
+<HTML>
+<HEAD>
+<TITLE>Current Folder Type Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Current Folder Type Explained</H1>
+
+The Current Folder Type is part of the scoring rule's Pattern.
+It refers to the type of the folder that
+the message being scored is in.
+In order for a rule to be considered a match, the current folder must
+be of the type you set here.
+The three types &quot;Any&quot;, &quot;News&quot;, and &quot;Email&quot; are
+all what you might think.
+<P>
+If the Current Folder Type for a Pattern is set to &quot;News&quot;, for
+example, then
+that Pattern will only match if the current folder is a newsgroup and
+the rest of the Pattern matches.
+The value &quot;Specific&quot; may be used when you want to limit the match
+to a specific folder (not just a specific type of folder), or to a list of
+specific folders.
+<P>
+In order to match a specific folder you Select the &quot;Specific&quot;
+button <EM>AND</EM> fill in
+the name (or list of names) of
+the folder in the &quot;Folder List&quot; field.
+If the current folder is any of the folders in the list, that is considered
+a match.
+The name of each folder in the list may be either &quot;INBOX&quot;, the technical specification
+of the folder (like what appears in your configuration file) or, if the
+folder is one of your incoming folders, it may be the nickname you've given
+the folder.
+Here are a couple samples of specific folder names:
+<P>
+<CENTER><SAMP>{monet.art.example.com}mail/art-class</SAMP></CENTER>
+<P>
+<CENTER><SAMP>{news.example.com/nntp}#news.comp.mail.pine</SAMP></CENTER>
+<P>
+The easiest way to fill in the &quot;Folder List&quot; field is to use
+the T command that is available when the &quot;Folder List&quot; line is
+highlighted.
+Note that you won't be able to edit the &quot;Folder List&quot; line unless the
+Current Folder Type is set to &quot;Specific&quot;, and any value that
+&quot;Folder List&quot; has is ignored unless the type
+is set to &quot;Specific&quot;.
+<P>
+When reading a newsgroup, there may be a performance penalty
+incurred when collecting the information necessary to check a Pattern.
+For this reason, the default Current Folder Type is set to &quot;Email&quot;.
+For example, if you have Index Line Coloring rules that have Score Intervals
+defined then the scores for all the visible messages will need to be calculated.
+If some of your Scoring rules have
+a Current Folder Type of
+&quot;Any&quot; or &quot;News&quot; this may cause the MESSAGE INDEX
+screen to draw more slowly when in a newsgroup.
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_other_fldr_type =======
+<HTML>
+<HEAD>
+<TITLE>Current Folder Type Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Current Folder Type Explained</H1>
+
+The Current Folder Type is part of the rule's Pattern.
+It refers to the type of the folder being viewed.
+In order for a rule to be considered a match, the current folder must
+be of the type you set here.
+The three types &quot;Any&quot;, &quot;News&quot;, and &quot;Email&quot; are
+all what you might think.
+<P>
+If the Current Folder Type for a Pattern is set to &quot;News&quot;, for
+example, then
+that Pattern will only match if the current folder is a newsgroup.
+The value &quot;Specific&quot; may be used when you want to limit the match
+to a specific folder (not just a specific type of folder), or to a list of
+specific folders.
+<P>
+In order to match a specific folder you Select the &quot;Specific&quot;
+button <EM>AND</EM> fill in
+the name (or list of names) of
+the folder in the &quot;Folder List&quot; field.
+If the current folder is any of the folders in the list, that is considered
+a match.
+The name of each folder in the list may be either &quot;INBOX&quot;, the technical specification
+of the folder (like what appears in your configuration file) or, if the
+folder is one of your incoming folders, it may be the nickname you've given
+the folder.
+Here are a couple samples of specific folder names:
+<P>
+<CENTER><SAMP>{monet.art.example.com}mail/art-class</SAMP></CENTER>
+<P>
+<CENTER><SAMP>{news.example.com/nntp}#news.comp.mail.pine</SAMP></CENTER>
+<P>
+The easiest way to fill in the &quot;Folder List&quot; field is to use
+the T command that is available when the &quot;Folder List&quot; line is
+highlighted.
+Note that you won't be able to edit the &quot;Folder List&quot; line unless the
+Current Folder Type is set to &quot;Specific&quot;, and any value that
+&quot;Folder List&quot; has is ignored unless the type
+is set to &quot;Specific&quot;.
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_incol_fldr_type =======
+<HTML>
+<HEAD>
+<TITLE>Current Folder Type Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Current Folder Type Explained</H1>
+
+The Current Folder Type is part of the Line Coloring rule's Pattern.
+It refers to the type of the folder for which the MESSAGE INDEX is
+being viewed.
+In order for a rule to be considered a match, the current folder must
+be of the type you set here.
+The three types &quot;Any&quot;, &quot;News&quot;, and &quot;Email&quot; are
+all what you might think.
+<P>
+If the Current Folder Type for a Pattern is set to &quot;News&quot;, for
+example, then
+that Pattern will only match if the current folder is a newsgroup and
+the rest of the Pattern matches.
+The value &quot;Specific&quot; may be used when you want to limit the match
+to a specific folder (not just a specific type of folder), or to a list of
+specific folders.
+<P>
+In order to match a specific folder you Select the &quot;Specific&quot;
+button <EM>AND</EM> fill in
+the name (or list of names) of
+the folder in the &quot;Folder List&quot; field.
+If the current folder is any of the folders in the list, that is considered
+a match.
+The name of each folder in the list may be either &quot;INBOX&quot;, the technical specification
+of the folder (like what appears in your configuration file) or, if the
+folder is one of your incoming folders, it may be the nickname you've given
+the folder.
+Here are a couple samples of specific folder names:
+<P>
+<CENTER><SAMP>{monet.art.example.com}mail/art-class</SAMP></CENTER>
+<P>
+<CENTER><SAMP>{news.example.com/nntp}#news.comp.mail.pine</SAMP></CENTER>
+<P>
+The easiest way to fill in the &quot;Folder List&quot; field is to use
+the T command that is available when the &quot;Folder List&quot; line is
+highlighted.
+Note that you won't be able to edit the &quot;Folder List&quot; line unless the
+Current Folder Type is set to &quot;Specific&quot;, and any value that
+&quot;Folder List&quot; has is ignored unless the type
+is set to &quot;Specific&quot;.
+<P>
+When reading a newsgroup, there may be a performance penalty
+incurred when collecting the information necessary to check a Pattern.
+For this reason, the default Current Folder Type is set to &quot;Email&quot;.
+For example, a rule with a non-Normal Index Line Color
+and a Current Folder Type of
+&quot;Any&quot; or &quot;News&quot; may cause the MESSAGE INDEX
+screen to draw more slowly when in a newsgroup.
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_filt_fldr_type =======
+<HTML>
+<HEAD>
+<TITLE>Current Folder Type Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Current Folder Type Explained</H1>
+
+The Current Folder Type is part of the Filtering rule's Pattern.
+It refers to the type of the folder for which the filtering is being done.
+In order for a rule to be considered a match, the current folder must
+be of the type you set here.
+The three types &quot;Any&quot;, &quot;News&quot;, and &quot;Email&quot; are
+all what you might think.
+<P>
+If the Current Folder Type for a Pattern is set to &quot;News&quot;, for
+example, then
+that Pattern will only match if the current folder is a newsgroup and
+the rest of the Pattern matches.
+The value &quot;Specific&quot; may be used when you want to limit the match
+to a specific folder (not just a specific type of folder), or to a list of
+specific folders.
+<P>
+In order to match a specific folder you Select the &quot;Specific&quot;
+button <EM>AND</EM> fill in
+the name (or list of names) of
+the folder in the &quot;Folder List&quot; field.
+If the current folder is any of the folders in the list, that is considered
+a match.
+The name of each folder in the list may be either &quot;INBOX&quot;, the technical specification
+of the folder (like what appears in your configuration file) or, if the
+folder is one of your incoming folders, it may be the nickname you've given
+the folder.
+Here are a couple samples of specific folder names:
+<P>
+<CENTER><SAMP>{monet.art.example.com}mail/art-class</SAMP></CENTER>
+<P>
+<CENTER><SAMP>{news.example.com/nntp}#news.comp.mail.pine</SAMP></CENTER>
+<P>
+The easiest way to fill in the &quot;Folder List&quot; field is to use
+the T command that is available when the &quot;Folder List&quot; line is
+highlighted.
+Note that you won't be able to edit the &quot;Folder List&quot; line unless the
+Current Folder Type is set to &quot;Specific&quot;, and any value that
+&quot;Folder List&quot; has is ignored unless the type
+is set to &quot;Specific&quot;.
+<P>
+When reading a newsgroup, there may be a performance penalty
+incurred when collecting the information necessary to check a Pattern.
+For this reason, the default Current Folder Type is set to &quot;Email&quot;.
+For example, a rule with a Current Folder Type of either
+&quot;Any&quot; or &quot;News&quot; may cause the filtering to happen
+more slowly when opening a newsgroup.
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_stat_imp =======
+<HTML>
+<HEAD>
+<TITLE>Message Important Status Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Message Important Status Explained</H1>
+
+This part of the Pattern may have one of three possible values.
+The default value is &quot;Don't care&quot;, which matches any message.
+The other two values are &quot;Yes&quot;, which means the message must be
+flagged &quot;Important&quot; in order to be a match; or &quot;No&quot;, which
+means the message must <EM>not</EM> be flagged &quot;Important&quot; in order
+to be a match.
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_stat_new =======
+<HTML>
+<HEAD>
+<TITLE>Message New Status Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Message New Status Explained</H1>
+
+This part of the Pattern may have one of three possible values.
+The default value is &quot;Don't care&quot;, which matches any message.
+The other two values are &quot;Yes&quot;, which means the message must be
+&quot;New&quot; in order to be a match; or &quot;No&quot;, which
+means the message must <EM>not</EM> be &quot;New&quot; in order
+to be a match.
+&quot;New&quot; is the same as <EM>Unseen</EM> and not &quot;New&quot; is the
+same as <EM>Seen</EM>.
+<P>
+The nomenclature for New and Recent is a bit confusing:
+<P>
+New means that the message is Unseen.
+It could have been in your mailbox for a long time but if you haven't looked
+at it, it is still considered New.
+That matches the default Alpine index display that shows an N for such a
+message.
+<P>
+Recent means that the message was added to this folder since the last time
+you opened the folder.
+Alpine also shows an N by default for these types of messages.
+If you were to run two copies of Alpine that opened a folder one right after
+the other, a message would only show up as Recent in (at most) the first
+Alpine session.
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_stat_recent =======
+<HTML>
+<HEAD>
+<TITLE>Message Recent Status Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Message Recent Status Explained</H1>
+
+This part of the Pattern may have one of three possible values.
+The default value is &quot;Don't care&quot;, which matches any message.
+The other two values are &quot;Yes&quot;, which means the message must be
+&quot;Recent&quot; in order to be a match; or &quot;No&quot;, which
+means the message must <EM>not</EM> be &quot;Recent&quot; in order
+to be a match.
+&quot;Recent&quot; means that the message was added to the folder since
+the last time the folder was opened.
+If more than one mail client has the folder opened, the message will
+appear to be &quot;Recent&quot; to only one of the clients.
+<P>
+The nomenclature for New and Recent is a bit confusing:
+<P>
+New means that the message is Unseen.
+It could have been in your mailbox for a long time but if you haven't looked
+at it, it is still considered New.
+That matches the default Alpine index display that shows an N for such a
+message.
+<P>
+Recent means that the message was added to this folder since the last time
+you opened the folder.
+Alpine also shows an N by default for these types of messages.
+If you were to run two copies of Alpine that opened a folder one right after
+the other, a message would only show up as Recent in (at most) the first
+Alpine session.
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_stat_del =======
+<HTML>
+<HEAD>
+<TITLE>Message Deleted Status Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Message Deleted Status Explained</H1>
+
+This part of the Pattern may have one of three possible values.
+The default value is &quot;Don't care&quot;, which matches any message.
+The other two values are &quot;Yes&quot;, which means the message must be
+marked &quot;Deleted&quot; in order to be a match; or &quot;No&quot;, which
+means the message must <EM>not</EM> be marked &quot;Deleted&quot; in order
+to be a match.
+<P>
+If you are thinking of using this part of the Pattern as a way to prevent
+messages from being filtered more than once in a Filter Pattern,
+take a look at the Filter Option
+<A HREF="h_config_filt_opts_notdel">&quot;Move-Only-if-Not-Deleted&quot;</A>
+instead.
+It should work better than using this field since it will hide the filtered
+messages even if they are already Deleted.
+That option is at the bottom of the Filter configuration screen.
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_stat_ans =======
+<HTML>
+<HEAD>
+<TITLE>Message Answered Status Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Message Answered Status Explained</H1>
+
+This part of the Pattern may have one of three possible values.
+The default value is &quot;Don't care&quot;, which matches any message.
+The other two values are &quot;Yes&quot;, which means the message must be
+marked &quot;Answered&quot; in order to be a match; or &quot;No&quot;, which
+means the message must <EM>not</EM> be marked &quot;Answered&quot; in order
+to be a match.
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_abookfrom =======
+<HTML>
+<HEAD>
+<TITLE>Address in Address Book Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Address in Address Book Explained</H1>
+
+This option gives you a way to match messages that contain an address
+that is in one of your address books.
+Only the simple entries in your address books are searched.
+Address book distribution lists are ignored!
+<P>
+This part of the Pattern may have one of five possible values.
+The default value is &quot;Don't care&quot;, which matches any message.
+The value &quot;Yes, in any address book&quot; means at least one of the addresses
+from the message must be in at least one of your
+address books in order to be a match.
+The value &quot;No, not in any address book&quot;
+means none of the addresses may
+be in any of your address books in order to be a match.
+<P>
+The values &quot;Yes, in specific address books&quot; and
+&quot;No, not in any of specific address books&quot; are similar but instead
+of depending on all address books you are allowed to give a list of address
+books to look in.
+Usually this would be a single address book but it may be a
+list of address books as well.
+For each of these &quot;specific&quot; address book options you Select which
+of the Specific options you want (Yes or No) <EM>AND</EM> fill in the
+name (or list of names) of the address book in the
+&quot;Abook List&quot; field.
+The names to be used are those that appear in the ADDRESS BOOK LIST screen.
+The easiest way to fill in the Abook List field it to use
+the &quot;T&quot; command that is available when the &quot;Abook List&quot;
+line is highlighted.
+Note that you won't be able to edit the &quot;Abook List&quot; line unless the
+option is set to one of the two &quot;Specific&quot;, values.
+<P>
+The addresses from the message that are checked for are determined by the
+setting you have for &quot;Types of addresses to check for in address book&quot;.
+If you set this to &quot;From&quot; the From address from the message will
+be looked up in the address book.
+If you set it to &quot;To&quot; instead then the To addresses will be used.
+If any of the To addresses are in the address book then it is considered
+a match for &quot;Yes&quot; or not a match for &quot;No&quot;.
+You could set it to both From and To, in which case all of the From and To
+addresses are used.
+The &quot;Reply-To&quot; and &quot;Sender&quot; cases are a little unusual.
+Due to deficiencies in our tools, Reply-To uses the Reply-To address if it
+exists or the From address if there is no Reply-To address.
+Same for the Sender address.
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_inabook_from =======
+<HTML>
+<HEAD>
+<TITLE>From</TITLE>
+</HEAD>
+<BODY>
+<H1>From</H1>
+
+Setting the From line will cause the address from the From header line
+of the message to be checked for in the address book.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_inabook_replyto =======
+<HTML>
+<HEAD>
+<TITLE>Reply-To</TITLE>
+</HEAD>
+<BODY>
+<H1>Reply-To</H1>
+
+Setting the Reply-To line will cause the address from the Reply-To header line
+of the message to be checked for in the address book.
+However, if there is no Reply-To header line in the message the From header
+line will be used instead.
+We understand this is dumb but we don't have an easy way around it.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_inabook_sender =======
+<HTML>
+<HEAD>
+<TITLE>Sender</TITLE>
+</HEAD>
+<BODY>
+<H1>Sender</H1>
+
+Setting the Sender line will cause the address from the Sender header line
+of the message to be checked for in the address book.
+However, if there is no Sender header line in the message the From header
+line will be used instead.
+We understand this is dumb but we don't have an easy way around it.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_inabook_to =======
+<HTML>
+<HEAD>
+<TITLE>To</TITLE>
+</HEAD>
+<BODY>
+<H1>To</H1>
+
+Setting the To line will cause the address from the To header line
+of the message to be checked for in the address book.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_inabook_cc =======
+<HTML>
+<HEAD>
+<TITLE>CC</TITLE>
+</HEAD>
+<BODY>
+<H1>CC</H1>
+
+Setting the CC line will cause the address from the CC header line
+of the message to be checked for in the address book.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_stat_8bitsubj =======
+<HTML>
+<HEAD>
+<TITLE>Raw 8-bit in Subject Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Raw 8-bit in Subject Explained</H1>
+
+It seems that lots of unwanted email contains unencoded 8-bit characters
+in the Subject.
+Normally, characters with the 8th bit set are not allowed in the Subject
+header unless they are MIME-encoded.
+This option gives you a way to match messages that have Subjects that
+contain unencoded 8-bit characters.
+<P>
+This part of the Pattern may have one of three possible values.
+The default value is &quot;Don't care&quot;, which matches any message.
+The other two values are &quot;Yes&quot;, which means the Subject of
+the message must contain unencoded 8-bit characters (characters with the
+most significant bit set)
+in order to be a match; or &quot;No&quot;, which
+means the Subject must <EM>not</EM>
+contain unencoded 8-bit characters in order to be a match.
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_bom =======
+<HTML>
+<HEAD>
+<TITLE>Beginning of Month</TITLE>
+</HEAD>
+<BODY>
+<H1>Beginning of Month</H1>
+
+This option gives you a limited ability to take different actions depending on whether
+this is the first time Alpine has been run this month or not.
+Though it would be nice to have such an option available, this is not the
+same as whether or not this is the first time a paricular folder has been
+opened this month.
+If you want some action (probably Filtering) to take place in a folder each
+month, then you will need to be sure that the folder is opened during the
+first Alpine session of the month in order for this option to be helpful.
+<P>
+This part of the Pattern may have one of three possible values.
+The default value is &quot;Don't care&quot;, which matches any message.
+The other two values are &quot;Yes&quot;, which means this is the first
+time Alpine has been run this month;
+or &quot;No&quot;, which
+means this is not the first time Alpine has been run this month.
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Here are some technical details.
+The way that Alpine decides if it is the beginning of the month or not is
+to compare today's date with the date stored in the
+<A HREF="h_config_prune_date"><!--#echo var="VAR_last-time-prune-questioned"--></A>
+variable in the config file.
+If the month of today's date is later than the month stored in the variable,
+then this is considered to be the first time you have run Alpine this month, and
+that turns the Beginning of the Month option on.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_boy =======
+<HTML>
+<HEAD>
+<TITLE>Beginning of Year</TITLE>
+</HEAD>
+<BODY>
+<H1>Beginning of Year</H1>
+
+This option gives you a limited ability to take different actions depending on whether
+this is the first time Alpine has been run this year or not.
+Though it would be nice to have such an option available, this is not the
+same as whether or not this is the first time a paricular folder has been
+opened this year.
+If you want some action (probably Filtering) to take place in a folder each
+year, then you will need to be sure that the folder is opened during the
+first Alpine session of the year in order for this option to be helpful.
+<P>
+This part of the Pattern may have one of three possible values.
+The default value is &quot;Don't care&quot;, which matches any message.
+The other two values are &quot;Yes&quot;, which means this is the first
+time Alpine has been run this year;
+or &quot;No&quot;, which
+means this is not the first time Alpine has been run this year.
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Here are some technical details.
+The way that Alpine decides if it is the beginning of the year or not is
+to compare today's date with the date stored in the
+<A HREF="h_config_prune_date"><!--#echo var="VAR_last-time-prune-questioned"--></A>
+variable in the config file.
+If the year of today's date is later than the year stored in the variable,
+then this is considered to be the first time you have run Alpine this year, and
+that turns the Beginning of the Year option on.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_inick =======
+<HTML>
+<HEAD>
+<TITLE>Initialize Values From Role Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Initialize Values From Role Explained</H1>
+
+This is a power user feature.
+You will usually want to leave this field empty.
+The value of this field is the nickname of another one of your roles.
+The Action values from that other role
+are used as the initial values of the Action items for this role.
+If you put something in any of the action fields for this role, that will
+override whatever was in the corresponding field of the initializer role.
+<P>
+You might use this field if the &quot;Action&quot; part of one of your roles
+is something you want to use in more than one role.
+Instead of filling in those action values again for each role, you
+may give the nickname of the role where the values are filled in.
+It's just a shortcut way to define Role Actions.
+<P>
+Here's an example to help explain how this works.
+Suppose you have a role with nickname &quot;role1&quot; and role1 has
+(among other things)
+<P>
+<CENTER><SAMP>Set Reply-To = The Pres &lt;president@example.com&gt;</SAMP></CENTER>
+<P>
+set.
+If in &quot;role2&quot; you set &quot;Initialize settings using role&quot; to
+&quot;role1&quot;, then role2 will inherit the Set Reply-To value
+from role1 by default (and any of the other inheritable action values
+that are set).
+So if role2 had
+<P>
+<CENTER><SAMP>Set Reply-To = &lt;No Value Set&gt;</SAMP></CENTER>
+<P>
+defined, the Reply-To used with role2 would be &quot;The Pres &lt;president@example.com&gt;&quot;
+However, if role2 had
+<P>
+<CENTER><SAMP>Set Reply-To = VP &lt;vicepresident@example.com&gt;</SAMP></CENTER>
+<P>
+defined, then the Reply-To used with role2 would be &quot;VP &lt;vicepresident@example.com&gt;&quot; instead.
+<P>
+If you wish,
+you may choose a nickname from your list of roles by using the
+&quot;T&quot; command.
+If the role you are using to initialize also has a role it initializes from,
+then that initialization happens first.
+That is, inheritance works as expected with the grandparent and
+great-grandparent (and so on) roles having the expected effect.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_setfrom =======
+<HTML>
+<HEAD>
+<TITLE>Set From Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Set From Explained</H1>
+
+This describes part of the action to be taken if the Pattern for this
+role is a match.
+This field consists of a single address that will be used as the From
+address on the message you are sending.
+This should be a fully-qualified address like
+<P>
+<CENTER><SAMP>Full Name &lt;user@domain&gt;</SAMP></CENTER>
+<P>
+or just
+<P>
+<CENTER><SAMP>user@domain</SAMP></CENTER>
+<P>
+If you wish,
+you may choose an address from your address book with the
+&quot;T&quot; command.
+<P>
+If this is left blank, then your normal From address will be used.
+<P>
+You may also find it useful to add the changed From address to the
+<a href="h_config_alt_addresses"><!--#echo var="VAR_alt-addresses"--></a>
+configuration option.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_setreplyto =======
+<HTML>
+<HEAD>
+<TITLE>Set Reply-To Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Set Reply-To Explained</H1>
+
+This describes part of the action to be taken if the Pattern for this
+role is a match.
+This field consists of a single address that will be used as the Reply-To
+address on the message you are sending.
+This may be a fully-qualified address like
+<P>
+<CENTER><SAMP>Full Name &lt;user@domain&gt;</SAMP></CENTER>
+<P>
+or just
+<P>
+<CENTER><SAMP>user@domain</SAMP></CENTER>
+<P>
+If you wish,
+you may choose an address from your address book with the
+&quot;T&quot; command.
+<P>
+If this is left blank, then there won't be a Reply-To address unless
+you have configured one specially with the
+"<A HREF="h_config_custom_hdrs"><!--#echo var="VAR_customized-hdrs"--></A>"
+configuration option.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_setfcc =======
+<HTML>
+<HEAD>
+<TITLE>Set Fcc Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Set Fcc Explained</H1>
+
+This describes part of the action to be taken if the Pattern for this
+role is a match.
+This field consists of a single folder name that will be used in
+the Fcc field of the message you are sending.
+You may put anything here that you would normally type into the Fcc
+field from the composer.
+<P>
+In addition, an fcc of &quot;&quot; (two double quotation marks) means
+no Fcc.
+<P>
+A blank field here means that Alpine will use its normal rules for deciding
+the default value of the Fcc field.
+For many roles, perhaps most, it may make more sense for you to use the
+other Alpine facilities for setting the Fcc.
+In particular, if you want the Fcc to depend on who you are sending the
+message to then the <A HREF="h_config_fcc_rule">&quot;<!--#echo var="VAR_fcc-name-rule"-->&quot;</A>
+is probably more useful.
+In that case, you would want to leave the Fcc field here blank.
+However, if you have a role that depends on who the message you are replying
+to was From, or what address that message was sent to;
+then it might make sense to set the Fcc for that role here.
+<P>
+If you wish,
+you may choose a folder from your folder collections by using the
+&quot;T&quot; command.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_usesmtp =======
+<HTML>
+<HEAD>
+<TITLE>Use SMTP Server Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Use SMTP Server Explained</H1>
+
+This describes part of the action to be taken if the Pattern for this
+role is a match.
+If this field has a value, then it will be used as the SMTP server
+to send mail when this role is being used (unless the SMTP server variable
+is set in the system-wide fixed configuration file).
+It has the same semantics as the
+<A HREF="h_config_smtp_server">&quot;<!--#echo var="VAR_smtp-server"-->&quot;</A>
+variable in the Setup/Config screen.
+<P>
+If you are using this to post from home when you are at home and from
+work when you are at work you need to be careful about postponing messages.
+When you postpone a composition that was using a role with this variable
+set, the SMTP server list will be saved
+with the postponed composition.
+It cannot be changed later.
+Because of this, you may want to make this a list of SMTP servers
+with the preferred server at the front of the list and alternate servers
+later in the list.
+In your &quot;Home&quot; role you would put the home SMTP server first and
+the work SMTP server last.
+In your &quot;Work&quot; role you would put the work SMTP server first and
+the home SMTP server last.
+Then if you start a composition as &quot;Work&quot;, postpone
+it, and then later resume it from home the work SMTP server will fail but
+the home SMTP server later in the list will succeed.
+<P>
+You may be able to simplify things by making the regular
+<A HREF="h_config_smtp_server">&quot;<!--#echo var="VAR_smtp-server"-->&quot;</A>
+variable in the Setup/Config screen a list instead of using roles
+to set the SMTP server.
+
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_usenntp =======
+<HTML>
+<HEAD>
+<TITLE>Use NNTP Server Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Use NNTP Server Explained</H1>
+
+This describes part of the action to be taken if the Pattern for this
+role is a match.
+If this field has a value, then it will be used as the NNTP server
+to post to newsgroups when this role is being used (unless the NNTP server
+variable
+is set in the system-wide fixed configuration file).
+It has the same semantics as the
+<A HREF="h_config_nntp_server">&quot;<!--#echo var="VAR_nntp-server"-->&quot;</A>
+variable in the Setup/Config screen.
+<P>
+This role setting can facilitate posting to the right nntp server for someone
+who reads news from various news sources. The feature
+<A HREF="h_config_predict_nntp_server">&quot;<!--#echo var="FEAT_predict-nntp-server"-->&quot;</A>
+allows for setting the correct <!--#echo var="VAR_nntp-server"--> without having to individually
+set a role for that <!--#echo var="VAR_nntp-server"-->, but for greater flexibility, setting
+nntp servers for roles may be more desirable for some people.
+<P>
+If you are using this to post from home when you are at home and from
+work when you are at work you need to be careful about postponing messages.
+When you postpone a composition that was using a role with this variable
+set, the NNTP server list will be saved
+with the postponed composition.
+It cannot be changed later.
+Because of this, you may want to make this a list of NNTP servers
+with the preferred server at the front of the list and alternate servers
+later in the list.
+In your &quot;Home&quot; role you would put the home NNTP server first and
+the work NNTP server last.
+In your &quot;Work&quot; role you would put the work NNTP server first and
+the home NNTP server last.
+Then if you start a composition as &quot;Work&quot;, postpone
+it, and then later resume it from home the work NNTP server will fail but
+the home NNTP server later in the list will succeed.
+<P>
+You may be able to simplify things by making the regular
+<A HREF="h_config_nntp_server">&quot;<!--#echo var="VAR_nntp-server"-->&quot;</A>
+variable in the Setup/Config screen a list instead of using roles
+to set the NNTP server.
+
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_setotherhdr =======
+<HTML>
+<HEAD>
+<TITLE>Set Other Headers Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Set Other Headers Explained</H1>
+
+This describes part of the action to be taken if the Pattern for this
+role is a match.
+This field gives you a way to set values for headers besides
+&quot;From&quot; and &quot;Reply-To&quot;.
+If you want to set either of those, use the specific
+&quot;Set From&quot; and &quot;Set Reply-To&quot; settings above.
+<P>
+This field is similar to the
+"<A HREF="h_config_custom_hdrs"><!--#echo var="VAR_customized-hdrs"--></A>" option.
+Each header you specify here must include the header tag
+(&quot;To:&quot;, &quot;Approved:&quot;, etc.)
+and may optionally include a value for that header.
+In order to see these headers when you compose using this role you
+must use the rich header
+<!--chtml if pinemode="function_key"-->(F5)
+<!--chtml else-->(Ctrl-R)<!--chtml endif--> command.
+Here's an example that shows how you might set the To address.
+<P>
+<CENTER><SAMP>Set Other Hdrs = To: Full Name &lt;user@domain&gt;</SAMP></CENTER>
+<P>
+Headers set in this way are different from headers set with the
+<!--#echo var="VAR_customized-hdrs"--> option in that the value you give for a header here
+will replace any value that already exists.
+For example, if you are Replying to a message there will already be at
+least one address in the To header (the address you are Replying to).
+However, if you Reply using a role that sets the To header, that role's
+To header value will be used instead.
+<P>
+Limitation: Because commas are used to separate the list of
+Other Headers, it is not possible to have the value of a
+header contain a comma;
+nor is there currently an &quot;escape&quot; mechanism provided
+to make this work.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_setlitsig =======
+<HTML>
+<HEAD>
+<TITLE>Set Literal Signature Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Set Literal Signature Explained</H1>
+
+This describes part of the action to be taken if the Pattern for this
+role is a match.
+This field contains the actual text for your signature, as opposed to
+the name of a file containing your signature.
+If this is defined it takes precedence over any value set in the
+&quot;Set Signature&quot; field.
+<P>
+This is simply a different way to store the signature.
+The signature is stored inside your Alpine configuration file instead of in
+a separate file.
+Tokens work the same way they do with
+<A HREF="h_config_role_setsig">Set Signature</A>, so refer to the
+help text there for more information.
+<P>
+
+The two character sequence &#92;n (backslash followed by
+the character n) will be used to signify a line-break in your signature.
+You don't have to enter the &#92;n, but it will be visible in the
+CHANGE THIS ROLE RULE window after you are done editing the signature.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_setsig =======
+<HTML>
+<HEAD>
+<TITLE>Set Signature Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Set Signature Explained</H1>
+
+This describes part of the action to be taken if the Pattern for this
+role is a match.
+<P>
+If either the default <A HREF="h_config_literal_sig"><!--#echo var="VAR_literal-signature"--></A>
+option from Setup/Config
+or the &quot;Set LiteralSig&quot; option for this role are defined,
+then this option will be ignored.
+You can tell that that is the case because the value of this
+option will show up as
+<P>
+<CENTER><SAMP>&lt;Ignored: using LiteralSig instead&gt;</SAMP></CENTER>
+<P>
+You may either use all Literal Signatures (signatures stored in your
+configuration file) throughout Alpine, or all signature files.
+You can't mix the two.
+<P>
+This field consists of a filename that will be used as the signature
+file when using this role.
+<P>
+If the filename is followed by a vertical bar (|) then instead
+of reading the contents of the file the file is assumed to be a
+program that will produce the text to be used on its standard output.
+The program can't have any arguments and doesn't receive any input from Alpine,
+but the rest of the processing works as if the contents came from a file.
+<P>
+Instead of storing the data in a local file, the
+signature data may be stored remotely in an IMAP folder.
+In order to do this,
+you must use a remote name for the signature.
+A remote signature name might look like:
+<P>
+<CENTER><SAMP>{myimaphost.myschool.k12.wa.us}mail/sig3</SAMP></CENTER>
+<P>
+
+The syntax used here is the same as the syntax used for a remote
+<A HREF="h_config_signature_file"><!--#echo var="VAR_signature-file"--></A>.
+Note that you may not access an existing signature file remotely,
+you have to create a new <EM>folder</EM> that contains the signature data.
+If the name you use here for the signature data is a remote name, then when
+you edit the file using the &quot;F&quot; command the data will
+be saved remotely in the folder.
+You aren't required to do anything special to create the folder, it
+gets created if you use a remote name.
+
+<P>
+If you type &quot;F&quot; you may edit the contents of the file (as opposed to
+the name of the file) you have specified.
+If you type &quot;T&quot; you may use a browser to choose an existing filename.
+<P>
+Besides containing regular text, a signature file may also
+contain (or a signature program may produce) tokens that are replaced with text
+that depends on the message you are replying to or forwarding.
+The tokens all look like _word_ (a word surrounded by underscores).
+For example, if the token
+<P>
+<CENTER><SAMP>_DATE_</SAMP></CENTER>
+<P>
+is included in the text of the signature file, then when you reply to
+or forward a message, the token will be replaced with the actual date
+the message you are replying to or forwarding was sent.
+<P>
+If you use a role that has a signature file for a plain composition
+(that is, not a reply or forward) then there is no original message, so
+any tokens that depend on the message will be replaced with nothing.
+So if you want a signature file to be useful for new compositions it
+shouldn't include any of the tokens that depend on the message being
+replied to or forwarded.
+<P>
+The list of available tokens is
+<A HREF="h_index_tokens">here</A>.
+<P>
+Actually, for the adventurous, there is a way to conditionally include text based
+on whether or not a token would result in specific replacement text.
+For example, you could include some text based on whether or not
+the _NEWS_ token would result in any newsgroups if it was used.
+It's explained in detail
+<A HREF="h_reply_token_conditionals">here</A>.
+<P>
+In the very unlikely event that you want to include a literal token in
+a signature file, you must precede it with a backslash character.
+For example, to include the literal text _DATE_ you must actually use
+&#92;_DATE_.
+It is not possible to have a literal backslash followed by an expanded token.
+<P>
+A blank field here means that Alpine will use its normal rules for deciding
+which file (if any) to use for the signature file.
+<P>
+An alternate method for storing the signature is available in
+<A HREF="h_config_role_setlitsig">Set Literal Signature</A>.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_settempl =======
+<HTML>
+<HEAD>
+<TITLE>Set Template Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Set Template Explained</H1>
+
+This describes part of the action to be taken if the Pattern for this
+role is a match.
+This field consists of a filename that will be used as the template
+file when using this role.
+The template file is a file that is included at the top of the message you
+are composing.
+<P>
+If the filename is followed by a vertical bar (|) then instead
+of reading the contents of the file the file is assumed to be a
+program that will produce the text to be used on its standard output.
+The program can't have any arguments and doesn't receive any input from Alpine,
+but the rest of the processing works as if the contents came from a file.
+<P>
+Instead of storing the data in a local file, the
+template may be stored remotely in an IMAP folder.
+In order to do this,
+you must use a remote name for the template.
+A remote template name might look like:
+<P>
+<CENTER><SAMP>{myimaphost.myschool.k12.wa.us}mail/templ3</SAMP></CENTER>
+<P>
+
+The syntax used here is the same as the syntax used for a remote
+<A HREF="h_config_signature_file"><!--#echo var="VAR_signature-file"--></A>.
+Note that you may not access an existing template file remotely,
+you have to create a new <EM>folder</EM> that contains the template data.
+If the name you use here for the template is a remote name, then when
+you edit the file using the &quot;F&quot; command the data will
+be saved remotely in the folder.
+You aren't required to do anything special to create the folder, it
+gets created if you use a remote name.
+<P>
+If you type &quot;F&quot; you may edit the contents of the file (as opposed to
+the name of the file) you have specified.
+If you type &quot;T&quot; you may use a browser to choose an existing filename.
+<P>
+Besides containing regular text, the template file may also
+contain (or a template file program may produce) tokens that are replaced with text
+that depends on the message you are replying to or forwarding.
+The tokens all look like _word_ (a word surrounded by underscores).
+For example, if the token
+<P>
+<CENTER><SAMP>_DATE_</SAMP></CENTER>
+<P>
+is included in the text of the template file, then when you reply to
+or forward a message, the token will be replaced with the actual date
+the message you are replying to or forwarding was sent.
+<P>
+If you use a role that has a template file for a plain composition
+(that is, not a reply or forward) then there is no original message, so
+any tokens that depend on the message will be replaced with nothing.
+So if you want a template file to be useful for new compositions it
+shouldn't include any of the tokens that depend on the message being
+replied to or forwarded.
+<P>
+The list of available tokens is
+<A HREF="h_index_tokens">here</A>.
+<P>
+Actually, for the adventurous, there is a way to conditionally include text based
+on whether or not a token would result in specific replacement text.
+For example, you could include some text based on whether or not
+the _NEWS_ token would result in any newsgroups if it was used.
+It's explained in detail
+<A HREF="h_reply_token_conditionals">here</A>.
+<P>
+In the very unlikely event that you want to include a literal token in
+a template file, you must precede it with a backslash character.
+For example, to include the literal text _DATE_ you must actually use
+&#92;_DATE_.
+It is not possible to have a literal backslash followed by an expanded token.
+<P>
+A blank template field means that Alpine will not use a template file when
+this role is being used.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_filt_stat_imp =======
+<HTML>
+<HEAD>
+<TITLE>Set Important Status Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Set Important Status Explained</H1>
+
+This describes part of the action to be taken if the Pattern for this
+filter is a match.
+If set to &quot;Don't change it&quot; then this does nothing.
+If set to &quot;Set this state&quot; then the Important flag is set
+for the matching message.
+If set to &quot;Clear this state&quot; then the Important flag is cleared
+for the matching message.
+The important flag usually causes an asterisk to show up in the MESSAGE
+INDEX.
+It may also be useful when selecting a set of messages
+with the Select command.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_filt_stat_new =======
+<HTML>
+<HEAD>
+<TITLE>Set New Status Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Set New Status Explained</H1>
+
+This describes part of the action to be taken if the Pattern for this
+filter is a match.
+If set to &quot;Don't change it&quot; then this does nothing.
+If set to &quot;Set this state&quot; then the
+matching message is marked New.
+If set to &quot;Clear this state&quot; then the
+matching message is marked Seen.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_filt_stat_ans =======
+<HTML>
+<HEAD>
+<TITLE>Set Answered Status Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Set Answered Status Explained</H1>
+
+This describes part of the action to be taken if the Pattern for this
+filter is a match.
+If set to &quot;Don't change it&quot; then this does nothing.
+If set to &quot;Set this state&quot; then the Answered flag is set
+for the matching message.
+If set to &quot;Clear this state&quot; then the Answered flag is cleared
+for the matching message.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_filt_stat_del =======
+<HTML>
+<HEAD>
+<TITLE>Set Deleted Status Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Set Deleted Status Explained</H1>
+
+This describes part of the action to be taken if the Pattern for this
+filter is a match.
+If set to &quot;Don't change it&quot; then this does nothing.
+If set to &quot;Set this state&quot; then the
+matching message is marked Deleted.
+If set to &quot;Clear this state&quot; then the
+matching message is marked UnDeleted.
+<P>
+You should not use this option unless you are prepared to have matching
+messages expunged from the folder permanently.
+For example, if you type the Expunge command, this filter is applied
+before the expunge, so matching messages will be marked Deleted and then
+will be permanently expunged from the folder.
+However, since the index isn't redrawn in between the time that the message
+is marked Deleted and the time that you are asked to expunge, the only
+indication that you are expunging the message comes in the number of messages
+being expunged.
+The same thing may happen when you close a folder.
+It is also possible that an expunge not initiated by you will
+delete matching messages.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_scoreval =======
+<HTML>
+<HEAD>
+<TITLE>Score Value Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Score Value Explained</H1>
+
+A message's score is the sum of the Score Values from all of the Scoring rules
+with Patterns that match the message.
+The value you give here is the Score Value associated with this rule.
+A Score Value is an integer between -100 and 100, with the default
+value of zero.
+<P>
+Alternatively, if the
+<A HREF="h_config_role_scorehdrtok">&quot;Score From Header&quot;</A>
+field is defined
+(on the line right below the &quot;Score Value&quot; field)
+then the &quot;Score Value&quot; is ignored and
+the &quot;Score From Header&quot; field is used instead.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_scorehdrtok =======
+<HTML>
+<HEAD>
+<TITLE>Score Value From Header Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Score Value From Header Explained</H1>
+
+This option provides a way to use a number that appears in the headers of your
+messages as the message's score, or as a component of that score.
+If this field is defined then it is used instead of the &quot;Score Value&quot;.
+The idea behind this option is that there may be a score embedded in the
+headers of messages that has already been calculated outside of Alpine.
+For example, messages delivered to you may contain an &quot;X-Spam&quot; header and
+somewhere in that header there is a score.
+<P>
+The value for this option is the name of the header followed by parentheses
+with two arguments inside:
+<P>
+<CENTER><SAMP>HeaderName(field_number,field_separators)</SAMP></CENTER>
+<P>
+No space is allowed between the comma and the start of the field_separators.
+It would be interpreted as the first separator if it was there.
+Field 0 is the whole line, Field 1 is the data up to the first separator, Field 2
+starts after that and goes to the second separator, and so on.
+It's easier to explain with examples.
+<P>
+<CENTER><SAMP>X-Spam(2,&quot;&nbsp;&quot;)</SAMP></CENTER>
+<P>
+In the above example the header that is used is the &quot;X-Spam&quot; header.
+The value of that header (the part after the colon and the space) is split
+into fields separated by spaces.
+<P>
+<CENTER><SAMP>Field1 &lt;space&gt; Field2 &lt;space&gt; Field3 ...</SAMP></CENTER>
+<P>
+The second field is selected and converted to an integer. It only makes sense
+if Field2 really is an integer.
+<P>
+Here's an example of a SpamAssassin header.
+The exact look of the header will vary, but if your incoming mail
+contains headers that look like the following
+<P>
+<CENTER><SAMP>X-Spam-Status: Yes, hits=10.6 tagged_above=-999.0 required=7.0 tests=BAYE...</SAMP></CENTER>
+<P>
+you might want to use the hits value as a score.
+Since the score is an integer value you can't make use of the decimal part of
+the number, but
+you might split off the hits=10 part as a score by using the characters &quot;=&quot;
+and &quot;.&quot; as your separators.
+<P>
+<CENTER><SAMP>X-Spam-Status(2,&quot;=.&quot;)</SAMP></CENTER>
+<P>
+The first field starts with the Y in Yes and goes until the &quot;=&quot; after
+hits.
+The second field is &quot;10&quot; so the score value would be 10.
+<P>
+Another example we've seen has headers that look like
+<P>
+<CENTER><SAMP>X-Spam: Gauge=IIIIIII, Probability=7%, Report=...</SAMP></CENTER>
+<P>
+Because there are two equals before the 7% the value
+<P>
+<CENTER><SAMP>X-Spam(3,&quot;=%&quot;)</SAMP></CENTER>
+<P>
+should capture the probability as the score.
+<P>
+The Score From Header scoring value actually works just like the
+regular Score Value in that the rest of the pattern has to match before
+it is used and the scores from all the different scoring rules that
+match for a particular message are added together.
+When using the Score From Header method it may (or may not) make sense to
+use only a single scoring rule with a pattern that matches every message.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_replyuse =======
+<HTML>
+<HEAD>
+<TITLE>Reply Use Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Reply Use Explained</H1>
+
+This option determines how this particular role will be used when Replying
+to a message.
+There are three possible values for this option.
+The value &quot;Never&quot;
+means that this role will not be a candidate for use when Replying.
+The role's Pattern will not be checked for a match, however the role will
+be available to be manually switched to if there is a confirmation prompt.
+<P>
+
+The options &quot;With confirmation&quot; and &quot;Without confirmation&quot;
+mean that you do want to consider this role when Replying.
+For either of these settings, the role's Pattern will be compared with
+the message being replied to.
+If there is a match then this role will either be used without confirmation
+or will be the default when confirmation is asked for, depending on
+which of the two options is selected.
+If confirmation is requested, you will also have a chance to
+manually change the role to any one of your other roles.
+<P>
+
+You won't be prompted for confirmation if none of your role Patterns
+match the message being replied to.
+This is independent of the value of the current option.
+The <A HREF="h_config_confirm_role"><!--#echo var="FEAT_confirm-role-even-for-default"--></A>
+feature may be used to change this behavior.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_forwarduse =======
+<HTML>
+<HEAD>
+<TITLE>Forward Use Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Forward Use Explained</H1>
+
+This option determines how this particular role will be used when Forwarding
+a message.
+There are three possible values for this option.
+The value &quot;Never&quot;
+means that this role will not be a candidate for use when Forwarding.
+The role's Pattern will not be checked for a match, however the role will
+be available to be manually switched to if there is a confirmation prompt.
+<P>
+
+The options &quot;With confirmation&quot; and &quot;Without confirmation&quot;
+mean that you do want to consider this role when Forwarding.
+For either of these settings, the role's Pattern will be compared with
+the message being forwarded.
+If there is a match then this role will either be used without confirmation
+or will be the default when confirmation is asked for, depending on
+which of the two options is selected.
+If confirmation is requested, you will also have a chance to
+manually change the role to any one of your other roles.
+<P>
+
+You won't be prompted for confirmation if none of your role Patterns
+match the message being forwarded.
+This is independent of the value of the current option.
+The <A HREF="h_config_confirm_role"><!--#echo var="FEAT_confirm-role-even-for-default"--></A>
+feature may be used to change this behavior.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_role_composeuse =======
+<HTML>
+<HEAD>
+<TITLE>Compose Use Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Compose Use Explained</H1>
+
+This option determines how this particular role will be used when Composing
+a new message using the &quot;Compose&quot; command.
+This does not affect what happens when using the &quot;Role&quot; command
+to compose a new message.
+The &quot;Role&quot; command allows you to select a role from all of the
+roles you have defined, regardless of what Uses you've assigned to those
+roles.
+<P>
+
+There are three possible values for this option.
+The value &quot;Never&quot;
+means that this role will not be a candidate for use when Composing.
+The role's Current Folder Type will not be checked for a match, however the role
+will be available to be manually switched to if there is a confirmation prompt.
+<P>
+
+The options &quot;With confirmation&quot; and &quot;Without confirmation&quot;
+mean that you do want to consider this role when Composing.
+For either of these settings,
+the role's Current Folder Type will be checked (since there is no message
+to compare with, the rest of the Pattern is considered a match).
+If there is a match then this role will either be used without confirmation
+or will be the default when confirmation is asked for, depending on
+which of the two options is selected.
+If confirmation is requested, you will also have a chance to
+manually change the role to any one of your other roles.
+<P>
+
+When using the Compose command the role checking is a little different
+because there is no message being replied to or forwarded.
+Because of this the Current Folder Type is checked but the header pattern
+fields, the AllText pattern, the BodyText pattern, and the Score Interval are all ignored.
+A role is considered to be a match if it is a candidate for Compose Use and
+its Current Folder Type matches the currently open folder.
+This could be useful if you want to set a role based on the folder you
+are reading, or the type of folder you are reading.
+<P>
+
+You won't be prompted for confirmation if none of your role Patterns
+are a match.
+This is independent of the value of the current option.
+The <A HREF="h_config_confirm_role"><!--#echo var="FEAT_confirm-role-even-for-default"--></A>
+feature may be used to change this behavior.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_filter_folder =======
+<HTML>
+<HEAD>
+<TITLE>Filter Folder Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Filter Folder Explained</H1>
+
+When the Filter Action is set to &quot;Move&quot;,
+the folder or folders specified here will be used to store messages matching
+the provided pattern.
+
+<P>
+If you set the Filter Action to &quot;Move&quot; you must give a folder name
+here.
+
+<P>
+If you wish,
+you may choose a folder from your folder collections by using the
+&quot;T&quot; command.
+<P>
+Besides regular text, the folder name may also contain
+tokens that are replaced with text representing the current date
+when you run Alpine.
+For example, if the folder name you use is
+<P>
+<CENTER><SAMP>abc-_CURYEAR_-_CURMONTHABBREV_</SAMP></CENTER>
+<P>
+that is replaced with something like
+<P>
+<CENTER><SAMP>abc-2004-oct</SAMP></CENTER>
+<P>
+Or,
+<P>
+<CENTER><SAMP>abc-_CURYEAR2DIGIT_-_CURMONTH2DIGIT_</SAMP></CENTER>
+<P>
+becomes
+<P>
+<CENTER><SAMP>abc-04-10</SAMP></CENTER>
+<P>
+The token names must be surrounded by underscores in order to be recognized
+as tokens.
+The tokens that may be used are those that are derived from the current date.
+They're listed near the bottom of the list of tokens give
+<A HREF="h_index_tokens">here</A>.
+<P>
+Look &quot;<A HREF="h_rule_patterns">here</A>&quot;
+for more information on Patterns.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_filter_kw_set =======
+<HTML>
+<HEAD>
+<TITLE>Set These Keywords Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Set These Keywords Explained</H1>
+
+This describes part of the action to be taken if the Pattern for this
+filter is a match.
+Read a little about keywords in the help text for the
+<A HREF="h_common_flag">Flag</A> command.
+This option is a list of keywords that will be Set when there is a match.
+If you wish, you may choose keywords from the list of keywords you have
+defined with the &quot;T&quot; command.
+You may add new keywords by defining them in the
+<A HREF="h_config_keywords"><!--#echo var="VAR_keywords"--></A> option in the Setup/Config screen.
+If you have given a keyword a nickname when configuring it,
+that nickname may be used instead of the actual keyword.
+
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_filter_kw_clr =======
+<HTML>
+<HEAD>
+<TITLE>Clear These Keywords Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Clear These Keywords Explained</H1>
+
+This describes part of the action to be taken if the Pattern for this
+filter is a match.
+Read a little about keywords in the help text for the
+<A HREF="h_common_flag">Flag</A> command.
+This option is a list of keywords that will be Cleared when there is a match.
+If you wish, you may choose keywords from the list of keywords you have
+defined with the &quot;T&quot; command.
+You may add new keywords by defining them in the
+<A HREF="h_config_keywords"><!--#echo var="VAR_keywords"--></A> option in the Setup/Config screen.
+If you have given a keyword a nickname when configuring it,
+that nickname may be used instead of the actual keyword.
+
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_index_tokens =======
+<HTML>
+<HEAD>
+<TITLE>Tokens for Index and Replying</TITLE>
+</HEAD>
+<BODY>
+
+This set of special tokens may be used in the
+<A HREF="h_config_index_format">&quot;<!--#echo var="VAR_index-format"-->&quot;</A> option,
+in the <A HREF="h_config_reply_intro">&quot;<!--#echo var="VAR_reply-leadin"-->&quot;</A> option,
+in signature files,
+in template files used in
+<A HREF="h_rules_roles">&quot;roles&quot;</A>, and in the folder name
+that is the target of a Filter Rule.
+Some of them aren't available in all situations.
+<P>
+The tokens are used as they appear below for the &quot;<!--#echo var="VAR_index-format"-->&quot;
+option, but they must be surrounded by underscores for the
+&quot;<!--#echo var="VAR_reply-leadin"-->&quot; option, in signature and template files,
+and in the target of Filter Rules.
+<P>
+<P>
+
+<H1><EM>Tokens Available for all Cases (except Filter Rules)</EM></H1>
+
+<DL>
+<DT>SUBJECT</DT>
+<DD>
+This token represents the Subject the sender gave the message.
+Alternatives for use in the index screen are
+SUBJKEY, SUBJKEYINIT, SUBJECTTEXT, SUBJKEYTEXT, and SUBJKEYINITTEXT.
+You may color the subject text in the MESSAGE INDEX screen differently by using the
+<A HREF="h_config_index_subject_color">Index Subject Color</A> and the
+<A HREF="h_config_index_opening_color">Index Opening Color</A>
+options available from
+the <A HREF="h_color_setup">Setup Kolor</A> screen.
+</DD>
+
+<DT>FROM</DT>
+<DD>
+This token represents the personal name (or email address if the name
+is unavailable) of the person specified in the message's &quot;From:&quot;
+header field.
+You may color the from text in the MESSAGE INDEX screen differently by using the
+<A HREF="h_config_index_from_color">Index From Color</A>
+option available from
+the <A HREF="h_color_setup">Setup Kolor</A> screen.
+</DD>
+
+<DT>ADDRESS</DT>
+<DD>
+This is similar to the &quot;FROM&quot; token, only it is always the
+email address, never the personal name.
+For example, &quot;mailbox@domain&quot;.
+</DD>
+
+<DT>MAILBOX</DT>
+<DD>
+This is the same as the &quot;ADDRESS&quot; except that the
+domain part of the address is left off.
+For example, &quot;mailbox&quot;.
+</DD>
+
+<DT>SENDER</DT>
+<DD>
+This token represents the personal name (or email address) of the person
+listed in the message's &quot;Sender:&quot; header field.
+</DD>
+
+<DT>TO</DT>
+<DD>
+This token represents the personal names (or email addresses if the names
+are unavailable) of the persons specified in the
+message's &quot;To:&quot; header field.
+</DD>
+
+<DT>NEWSANDTO</DT>
+<DD>
+This token represents the newsgroups from the
+message's &quot;Newsgroups:&quot; header field <EM>and</EM>
+the personal names (or email addresses if the names
+are unavailable) of the persons specified in the
+message's &quot;To:&quot; header field.
+</DD>
+
+<DT>TOANDNEWS</DT>
+<DD>
+Same as &quot;NEWSANDTO&quot; except in the opposite order.
+</DD>
+
+<DT>NEWS</DT>
+<DD>
+This token represents the newsgroups from the
+message's &quot;Newsgroups:&quot; header field.
+</DD>
+
+<DT>CC</DT>
+<DD>
+This token represents the personal names (or email addresses if the names
+are unavailable) of the persons specified in the
+message's &quot;Cc:&quot; header field.
+</DD>
+
+<DT>RECIPS</DT>
+<DD>
+This token represents the personal names (or email addresses if the names
+are unavailable) of the persons specified in both the
+message's &quot;To:&quot; header field and
+the message's &quot;Cc:&quot; header field.
+</DD>
+
+<DT>NEWSANDRECIPS</DT>
+<DD>
+This token represents the newsgroups from the
+message's &quot;Newsgroups:&quot; header field <EM>and</EM>
+the personal names (or email addresses if the names
+are unavailable) of the persons specified in the
+message's &quot;To:&quot; and &quot;Cc:&quot; header fields.
+</DD>
+
+<DT>RECIPSANDNEWS</DT>
+<DD>
+Same as &quot;NEWSANDRECIPS&quot; except in the opposite order.
+</DD>
+
+<DT>INIT</DT>
+<DD>
+This token represents the initials from the personal name
+of the person specified in the message's &quot;From:&quot;
+header field.
+If there is no personal name, it is blank.
+</DD>
+
+<DT>DATE</DT>
+<DD>
+This token represents the date on which the message was sent, according
+to the &quot;Date&quot; header field.
+It has the format MMM DD. For example, &quot;Oct 23&quot;.
+The feature
+<A HREF="h_config_dates_to_local"><!--#echo var="FEAT_convert-dates-to-localtime"--></A>,
+which adjusts for the timezone the message was sent from,
+may have an affect on the value of this token as well as the values of
+all of the other DATE or TIME tokens.
+Some of the DATE and TIME tokens are displayed in a locale-specific
+way unless the option
+<A HREF="h_config_disable_index_locale_dates"><!--#echo var="FEAT_disable-index-locale-dates"--></A> is set.
+</DD>
+
+<DT>SMARTDATE</DT>
+<DD>
+This token represents the date on which the message was sent, according
+to the &quot;Date&quot; header field.
+It is &quot;Today&quot; if the message was sent today,
+&quot;Yesterday&quot; for yesterday,
+&quot;Wednesday&quot; if it was last Wednesday, and so on. If the
+message is from last year and is more than six months old it includes the year, as well.
+See the SMARTDATE alternatives below, as well.
+</DD>
+
+<DT>SMARTTIME</DT>
+<DD>
+This token represents the most relevant elements of the date on which
+the message was sent (according to the &quot;Date&quot; header field),
+in a compact form. If the message was sent today, only the time is used
+(e.g. &quot;9:22am&quot;, &quot;10:07pm&quot;); if it was sent during
+the past week, the day of the week and the hour are used
+(e.g. &quot;Wed09am&quot;, &quot;Thu10pm&quot;); other dates are
+given as date, month, and year (e.g. &quot;23Aug00&quot;,
+&quot;9Apr98&quot;).
+</DD>
+
+<DT>SMARTDATETIME</DT>
+<DD>
+This is a combination of SMARTDATE and SMARTTIME.
+It is SMARTDATE unless the SMARTDATE value is &quot;Today&quot;, in which
+case it is SMARTTIME.
+See the SMARTDATETIME alternatives below, as well.
+</DD>
+
+<DT>DATEISO</DT>
+<DD>
+This token represents the date on which the message was sent, according
+to the &quot;Date&quot; header field.
+It has the format YYYY-MM-DD. For example, &quot;1998-10-23&quot;.
+</DD>
+
+<DT>SHORTDATEISO</DT>
+<DD>
+This token represents the date on which the message was sent, according
+to the &quot;Date&quot; header field.
+It has the format YY-MM-DD. For example, &quot;98-10-23&quot;.
+</DD>
+
+<DT>SHORTDATE1</DT>
+<DD>
+This token represents the date on which the message was sent, according
+to the &quot;Date&quot; header field.
+It has the format MM/DD/YY. For example, &quot;10/23/98&quot;.
+</DD>
+
+<DT>SHORTDATE2</DT>
+<DD>
+This token represents the date on which the message was sent, according
+to the &quot;Date&quot; header field.
+It has the format DD/MM/YY. For example, &quot;23/10/98&quot;.
+</DD>
+
+<DT>SHORTDATE3</DT>
+<DD>
+This token represents the date on which the message was sent, according
+to the &quot;Date&quot; header field.
+It has the format DD.MM.YY. For example, &quot;23.10.98&quot;.
+</DD>
+
+<DT>SHORTDATE4</DT>
+<DD>
+This token represents the date on which the message was sent, according
+to the &quot;Date&quot; header field.
+It has the format YY.MM.DD. For example, &quot;98.10.23&quot;.
+</DD>
+
+<DT>LONGDATE</DT>
+<DD>
+This token represents the date on which the message was sent, according
+to the &quot;Date&quot; header field.
+It has the format MMM DD, YYYY. For example, &quot;Oct 23, 1998&quot;.
+</DD>
+
+<DT>SMARTDATE alternatives</DT>
+<DD>
+There are several versions of SMARTDATE that are all the same except
+for the way they format dates far in the past.
+SMARTDATE formats the date using the information from your locale settings
+to format the date string. It may end up formatting dates so that they look
+like DATEISO tokens, or SHORTDATE2 tokens, or something else entirely.
+The feature
+<A HREF="h_config_dates_to_local"><!--#echo var="FEAT_convert-dates-to-localtime"--></A>
+may have an affect on the values of these tokens.
+If you want more control you may use one of the following.
+ <DL>
+ <DT>SMARTDATE</DT> <DD>If the option
+<A HREF="h_config_disable_index_locale_dates"><!--#echo var="FEAT_disable-index-locale-dates"--></A> is not set
+then this will be locale specific. Control this with the
+LC_TIME locale setting on a UNIX system. On Windows
+the Regional Options control panel may be used to set the Short date
+format. At the programming level, the strftime routine is what Alpine
+uses to print the date.
+If the <!--#echo var="FEAT_disable-index-locale-dates"--> option is set then this is equivalent
+to SMARTDATES1.</DD>
+ <DT>SMARTDATEISO</DT> <DD>DATEISO format. See text above.</DD>
+ <DT>SMARTDATESHORTISO</DT> <DD>SHORTDATEISO format.</DD>
+ <DT>SMARTDATES1</DT> <DD>SHORTDATE1 format.</DD>
+ <DT>SMARTDATES2</DT> <DD>SHORTDATE2 format.</DD>
+ <DT>SMARTDATES3</DT> <DD>SHORTDATE3 format.</DD>
+ <DT>SMARTDATES4</DT> <DD>SHORTDATE4 format.</DD>
+ </DL>
+</DD>
+
+<DT>SMARTDATETIME alternatives</DT>
+<DD>
+There are several versions of SMARTDATETIME that are all very similar.
+The ones that end in 24 use a 24-hour clock for Today's messages instead
+of a 12-hour clock.
+The other variation is
+for the way they format dates far in the past.
+SMARTDATETIME and SMARTDATETIME24 format the date using the information from your locale settings
+to format the date string. It may end up formatting dates so that they look
+like DATEISO tokens, or SHORTDATE2 tokens, or something else entirely.
+The feature
+<A HREF="h_config_dates_to_local"><!--#echo var="FEAT_convert-dates-to-localtime"--></A>
+may have an affect on the values of these tokens.
+The possible choices are:
+ <DL>
+ <DT>SMARTDATETIME</DT> <DD>If the option
+<A HREF="h_config_disable_index_locale_dates"><!--#echo var="FEAT_disable-index-locale-dates"--></A> is not set
+then this will be locale specific. Control this with the
+LC_TIME locale setting on a UNIX system. On Windows
+the Regional Options control panel may be used to set the Short date
+format. At the programming level, the strftime routine is what Alpine
+uses to print the date.
+If the <!--#echo var="FEAT_disable-index-locale-dates"--> option is set then this is equivalent
+to SMARTDATETIMES1.</DD>
+ <DT>SMARTDATETIME24</DT> <DD>Use TIME24 for Today</DD>
+ <DT>SMARTDATETIMEISO</DT> <DD>DATEISO format. See text above.</DD>
+ <DT>SMARTDATETIMEISO24</DT> <DD>Use TIME24 for Today</DD>
+ <DT>SMARTDATETIMESHORTISO</DT> <DD>SHORTDATEISO format.</DD>
+ <DT>SMARTDATETIMESHORTISO24</DT> <DD>Use TIME24 for Today</DD>
+ <DT>SMARTDATETIMES1</DT> <DD>SHORTDATE1 format.</DD>
+ <DT>SMARTDATETIMES124</DT> <DD>Use TIME24 for Today</DD>
+ <DT>SMARTDATETIMES2</DT> <DD>SHORTDATE2 format.</DD>
+ <DT>SMARTDATETIMES224</DT> <DD>Use TIME24 for Today</DD>
+ <DT>SMARTDATETIMES3</DT> <DD>SHORTDATE3 format.</DD>
+ <DT>SMARTDATETIMES324</DT> <DD>Use TIME24 for Today</DD>
+ <DT>SMARTDATETIMES4</DT> <DD>SHORTDATE4 format.</DD>
+ <DT>SMARTDATETIMES424</DT> <DD>Use TIME24 for Today</DD>
+ </DL>
+</DD>
+
+<DT>DAYDATE</DT>
+<DD>
+This token represents the date on which the message was sent, according
+to the &quot;Date&quot; header field.
+It looks like &quot;Sat, 23 Oct 1998&quot;.
+This token is never converted in any locale-specific way.
+</DD>
+
+<DT>PREFDATE</DT>
+<DD>
+This token represents the date on which the message was sent, according
+to the &quot;Date&quot; header field.
+It is your operating system's idea of the preferred date representation for the current locale.
+Internally it uses the %x version of the date from the strftime routine.
+</DD>
+
+<DT>PREFTIME</DT>
+<DD>
+This token represents the time at which the message was sent, according
+to the &quot;Date&quot; header field.
+It is the preferred time representation for the current locale.
+Internally it uses the %X version of the time from the strftime routine.
+</DD>
+
+<DT>PREFDATETIME</DT>
+<DD>
+This token represents the date and time at which the message was sent, according
+to the &quot;Date&quot; header field.
+It is the preferred date and time representation for the current locale.
+Internally it uses the %c version of the time from the strftime routine.
+</DD>
+
+<DT>DAY</DT>
+<DD>
+This token represents the day of the month on which the message was sent,
+according to the &quot;Date&quot; header field.
+For example, &quot;23&quot; or &quot;9&quot;.
+</DD>
+
+<DT>DAY2DIGIT</DT>
+<DD>
+This token represents the day of the month on which the message was sent,
+according to the &quot;Date&quot; header field.
+For example, &quot;23&quot; or &quot;09&quot;.
+It is always 2 digits.
+</DD>
+
+<DT>DAYORDINAL</DT>
+<DD>
+This token represents the ordinal number that is the day of
+the month on which the message was sent,
+according to the &quot;Date&quot; header field.
+For example, &quot;23rd&quot; or &quot;9th&quot;.
+</DD>
+
+<DT>DAYOFWEEK</DT>
+<DD>
+This token represents the day of the week on which the message was sent,
+according to the &quot;Date&quot; header field.
+For example, &quot;Sunday&quot; or &quot;Wednesday&quot;.
+</DD>
+
+<DT>DAYOFWEEKABBREV</DT>
+<DD>
+This token represents the day of the week on which the message was sent,
+according to the &quot;Date&quot; header field.
+For example, &quot;Sun&quot; or &quot;Wed&quot;.
+</DD>
+
+<DT>MONTHABBREV</DT>
+<DD>
+This token represents the month the message was sent, according
+to the &quot;Date&quot; header field.
+For example, &quot;Oct&quot;.
+</DD>
+
+<DT>MONTHLONG</DT>
+<DD>
+This token represents the month in which the message was sent, according
+to the &quot;Date&quot; header field.
+For example, &quot;October&quot;.
+</DD>
+
+<DT>MONTH</DT>
+<DD>
+This token represents the month in which the message was sent, according
+to the &quot;Date&quot; header field.
+For example, &quot;10&quot; or &quot;9&quot;.
+</DD>
+
+<DT>MONTH2DIGIT</DT>
+<DD>
+This token represents the month in which the message was sent, according
+to the &quot;Date&quot; header field.
+For example, &quot;10&quot; or &quot;09&quot;.
+It is always 2 digits.
+</DD>
+
+<DT>YEAR</DT>
+<DD>
+This token represents the year the message was sent, according
+to the &quot;Date&quot; header field.
+For example, &quot;1998&quot; or &quot;2001&quot;.
+</DD>
+
+<DT>YEAR2DIGIT</DT>
+<DD>
+This token represents the year the message was sent, according
+to the &quot;Date&quot; header field.
+For example, &quot;98&quot; or &quot;01&quot;.
+It is always 2 digits.
+</DD>
+
+<DT>TIME24</DT>
+<DD>
+This token represents the time at which the message was sent, according
+to the &quot;Date&quot; header field.
+There is no adjustment made for different time zones, so you'll get
+the time the message was sent according to the time zone the sender
+was in.
+It has the format HH:MM. For example, &quot;17:28&quot;.
+</DD>
+
+<DT>TIME12</DT>
+<DD>
+This token represents the time at which the message was sent, according
+to the &quot;Date&quot; header field.
+This time is for a 12 hour clock.
+It has the format HH:MMpm.
+For example, &quot;5:28pm&quot; or &quot;11:13am&quot;.
+</DD>
+
+<DT>TIMEZONE</DT>
+<DD>
+This token represents the numeric timezone from
+the &quot;Date&quot; header field.
+It has the format [+-]HHMM. For example, &quot;-0800&quot;.
+</DD>
+
+</DL>
+
+<P>
+<H1><EM>Tokens Available Only for <!--#echo var="VAR_index-format"--></EM></H1>
+
+<DL>
+<DT>MSGNO</DT>
+<DD>
+This token represents the message's current position in the folder that,
+of course, may change as the folder is sorted or new mail arrives.
+</DD>
+
+<DT>STATUS</DT>
+<DD>
+This token represents a three character wide field displaying various
+aspects of the message's state.
+The first character is either blank,
+a '*' for message marked Important, or a '+' indicating a message
+addressed directly to you (as opposed to your having received it via a
+mailing list, for example).
+When the feature
+&quot;<A HREF="h_config_mark_for_cc"><!--#echo var="FEAT_mark-for-cc"--></A>&quot;
+is set, if the first character would have been
+blank then it will instead be a '-' if the message is cc'd to you.
+The second character is typically blank,
+though the arrow cursor may occupy it if either the
+&quot;<A HREF="h_config_force_low_speed"><!--#echo var="FEAT_assume-slow-link"--></A>&quot;
+or the
+&quot;<A HREF="h_config_force_arrow"><!--#echo var="FEAT_force-arrow-cursor"--></A>&quot; feature
+is set (or you actually are on a slow link).
+The third character is either '<A HREF="h_flag_deleted">D</A>' (Deleted),
+'<A HREF="h_flag_answered">A</A>' (Answered),
+'<A HREF="h_flag_forwarded">F</A>' (Forwarded),
+'<A HREF="h_flag_new">N</A>' (New), or blank.
+<P>
+If you are using a threaded view of the index and this message is at the
+top of a collapsed portion of a thread,
+then this token refers to all of the messages in the collapsed portion of
+the thread instead of just the top message.
+The first character will be a '*' if <EM>any</EM> of the messages in the thread
+are marked Important, else a '+' if any of the messages are addressed
+to you, else a '-' if any of the messages are cc'd to you.
+The third character will be a 'D' if <EM>all</EM> of the messages
+in the collapsed thread are marked deleted,
+an 'A' if <EM>all</EM> of the messages
+in the collapsed thread are marked answered,
+it will be an 'N' if any of
+the messages are undeleted and unseen, and it will be blank otherwise.
+</DD>
+
+<DT>FULLSTATUS</DT>
+<DD>
+This token represents a less abbreviated alternative
+to the &quot;STATUS&quot; token.
+It is six characters wide.
+The first character is '+', '-', or blank, the
+second blank, the third either '*' or blank, the fourth
+'<A HREF="h_flag_new">N</A>' or blank,
+the fifth '<A HREF="h_flag_answered">A</A>'
+or blank, and the sixth character is
+either '<A HREF="h_flag_deleted">D</A>' or
+blank.
+<P>
+If you are using a threaded view of the index and this message is at the
+top of a collapsed portion of a thread,
+then this token refers to all of the messages in the collapsed portion of
+the thread instead of just the top message.
+The first character is '+', '-', or blank depending on whether <EM>any</EM>
+of the messages in the collapsed thread are addressed to you or cc'd to you.
+The third character will be '*' if any of the messages are marked
+Important.
+The fourth character will be 'N' if all of the messages in the thread
+are New, else 'n' if some of the messages in the thread are New, else blank.
+The fifth character will be 'A' or 'a' or blank, and the sixth character
+will be 'D' or 'd' or blank.
+</DD>
+
+<DT>IMAPSTATUS</DT>
+<DD>
+This token represents an even less abbreviated alternative to the
+&quot;STATUS&quot; token.
+It differs from &quot;FULLSTATUS&quot; in only the fourth character, which is
+an 'N' if the message is new to this folder since the last time
+it was opened <EM>and</EM> it has not been viewed, an 'R' (Recent) if the message
+is new to the folder and has been viewed, a 'U' (Unseen) if the message is not
+new to the folder since it was last opened <EM>but</EM> has not been
+viewed, or a blank if the message has been in the folder since it was
+last opened and has been viewed.
+<P>
+If you are using a threaded view of the index and this message is at the
+top of a collapsed portion of a thread,
+then the fourth character will be
+'N' if all of the messages in the thread are unseen and recent;
+else 'n' if some of the messages in the thread are unseen and recent;
+else 'U' if all of the messages in the thread are unseen and not recent;
+else 'u' if some of the messages in the thread are unseen and not recent;
+else 'R' if all of the messages in the thread are seen and recent;
+else 'r' if some of the messages in the thread are seen and recent;
+else blank.
+</DD>
+
+<DT>SHORTIMAPSTATUS</DT>
+<DD>
+This is the same as the last four of the six characters of IMAPSTATUS,
+so the '+' To Me information will be missing.
+</DD>
+
+<DT>SIZE</DT>
+<DD>
+This token represents the total size, in bytes, of the message.
+If a &quot;K&quot; (Kilobyte)
+follows the number, the size is approximately 1,000
+times that many bytes (rounded to the nearest 1,000).
+If an &quot;M&quot; (Megabyte) follows the number, the size is approximately
+1,000,000 times that many bytes.
+Commas are not used in this field.
+This field is seven characters wide, including the enclosing parentheses.
+Sizes are rounded when &quot;K&quot; or &quot;M&quot; is present.
+The progression of sizes used looks like:
+
+<P>
+<CENTER><SAMP>0 1 ... 9999 10K ... 999K 1.0M ... 99.9M 100M ... 2000M</SAMP></CENTER>
+<P>
+</DD>
+
+<DT>SIZECOMMA</DT>
+<DD>
+This token represents the total size, in bytes, of the message.
+If a &quot;K&quot; (Kilobyte)
+follows the number, the size is approximately 1,000
+times that many bytes (rounded to the nearest 1,000).
+If an &quot;M&quot; (Megabyte) follows the number, the size is approximately
+1,000,000 times that many bytes.
+Commas are used if the number shown is 1,000 or greater.
+The SIZECOMMA field is one character wider than the SIZE field.
+Sizes are rounded when &quot;K&quot; or &quot;M&quot; is present.
+The progression of sizes used looks like:
+
+<P>
+<CENTER><SAMP>0 1 ... 99,999 100K ... 9,999K 10.0M ... 999.9M 1,000M ... 2,000M</SAMP></CENTER>
+<P>
+</DD>
+
+<DT>KSIZE</DT>
+<DD>
+This token represents the total size of the message, expressed in
+kilobytes or megabytes, as most appropriate.
+These are 1,024 byte kilobytes and 1,024 x 1,024 byte megabytes.
+The progression of sizes used looks like:
+
+<P>
+<CENTER><SAMP>0K 1K ... 1023K 1.0M ... 99.9M 100M ... 2047M</SAMP></CENTER>
+<P>
+</DD>
+
+<DT>SIZENARROW</DT>
+<DD>
+This token represents the total size, in bytes, of the message.
+If a &quot;K&quot; (Kilobyte)
+follows the number, the size is approximately 1,000
+times that many bytes.
+If an &quot;M&quot; (Megabyte) follows the number, the size is approximately
+1,000,000 times that many bytes.
+If a &quot;G&quot; (Gigabyte) follows the number, the size is approximately
+1,000,000,000 times that many bytes.
+This field uses only five characters of screen width, including the enclosing
+parentheses.
+The progression of sizes used looks like:
+
+<P>
+<CENTER><SAMP>0 1 ... 999 1K ... 99K .1M ... .9M 1M ... 99M .1G ... .9G 1G 2G</SAMP></CENTER>
+<P>
+</DD>
+
+<DT>DESCRIPSIZE</DT>
+<DD>
+This token is intended to represent a more useful description of the
+message than just its size, but it isn't very useful at this point.
+The plus sign in this view means there are attachments.
+Note that including this token in
+the &quot;<!--#echo var="VAR_index-format"-->&quot; could slow down the
+display a little while Alpine collects the necessary information.
+</DD>
+
+<DT>SUBJKEY</DT>
+<DD>
+This token is the same as the SUBJECT token unless keywords are set for
+the message.
+In that case, a list of keywords enclosed in braces will be prepended to
+the subject of the message.
+Only those keywords that you have defined in your
+<A HREF="h_config_keywords"><!--#echo var="VAR_keywords"--></A> option
+in Setup/Config are considered in the list.
+In other words, keywords that have been set by some other means, perhaps
+by another email program, won't show up unless included in
+<A HREF="h_config_keywords"><!--#echo var="VAR_keywords"--></A>.
+Having this set in the <!--#echo var="VAR_index-format"--> will also cause the keywords to be
+prepended to the subject in the MESSAGE TEXT screen.
+If you have given a keyword a nickname
+(<A HREF="h_config_keywords"><!--#echo var="VAR_keywords"--></A>), that nickname is displayed
+instead of the actual keyword.
+The <A HREF="h_config_kw_braces"><!--#echo var="VAR_keyword-surrounding-chars"--></A>
+option may be used to modify this token slightly.
+It is also possible to color keywords in the index using the
+Setup/Kolor screen (<A HREF="h_config_kw_color">Keyword Colors</A>).
+</DD>
+
+<DT>SUBJKEYINIT</DT>
+<DD>
+This token is the same as the SUBJKEY token except that instead of
+prepending a list of keywords to the subject, a list of first initials
+of keywords will be prepended instead.
+For example, if a message has the keywords <EM>Work</EM> and <EM>Now</EM>
+set (or Work and Now are the Alpine nicknames of keywords that are set)
+then the SUBJKEY token would cause a result like
+<P>
+<CENTER><SAMP>{Work Now} actual subject</SAMP></CENTER>
+<P>
+whereas the SUBJKEYINIT token would give
+<P>
+<CENTER><SAMP>{WN} actual subject</SAMP></CENTER>
+<P>
+Only those keywords that you have defined in your
+<A HREF="h_config_keywords"><!--#echo var="VAR_keywords"--></A> option
+in Setup/Config are considered in the list.
+In other words, keywords that have been set by some other means, perhaps
+by another email program, won't show up unless included in
+<A HREF="h_config_keywords"><!--#echo var="VAR_keywords"--></A>.
+The <A HREF="h_config_kw_braces"><!--#echo var="VAR_keyword-surrounding-chars"--></A>
+option may be used to modify this token slightly.
+It is also possible to color keywords in the index using the
+Setup/Kolor screen (<A HREF="h_config_kw_color">Keyword Colors</A>).
+</DD>
+
+<DT>SUBJECTTEXT</DT>
+<DD>
+Same as SUBJECT but if there is room in the Subject field for more text,
+the opening part of the text of the message is displayed after the subject.
+The time needed to fetch the text may cause a performance problem
+which can, of course, be avoided by using the SUBJECT version of
+the Subject instead.
+You may color this opening text differently by using the
+<A HREF="h_config_index_opening_color">Index Opening Color</A> option available from
+the <A HREF="h_color_setup">Setup Kolor</A> screen.
+You may adjust the characters that are displayed between the Subject and the
+opening text with the option
+<A HREF="h_config_opening_sep"><!--#echo var="VAR_opening-text-separator-chars"--></A>.
+</DD>
+
+<DT>SUBJKEYTEXT</DT>
+<DD>
+Same as SUBJKEY but with the opening message text.
+</DD>
+
+<DT>OPENINGTEXT</DT>
+<DD>
+This is similar to SUBJECTTEXT.
+Instead of combining the Subject and the opening text in a single
+field in the index screen this token allows you to allocate a
+separate column just for the opening text of the message.
+The time needed to fetch this text may cause a performance problem.
+You may color this opening text differently by using the
+<A HREF="h_config_index_opening_color">Index Opening Color</A> option available from
+the <A HREF="h_color_setup">Setup Kolor</A> screen.
+</DD>
+
+<DT>OPENINGTEXTNQ</DT>
+<DD>
+This is very similar to OPENINGTEXT.
+The NQ stands for No Quotes.
+The only difference is that quoted text (lines beginning with &gt;) is deleted.
+For some messages this may be confusing.
+For example, a message might have a line preceding some quoted
+text that reads something like &quot;On May 8th person A said.&quot;
+That no longer makes sense after the quoted text is deleted and it
+will appear that person A said whatever the text after the quote
+is, even though that is really person B talking.
+</DD>
+
+<DT>SUBJKEYINITTEXT</DT>
+<DD>
+Same as SUBJKEYINIT but with the opening message text.
+</DD>
+
+<DT>KEY</DT>
+<DD>
+This is a space-delimited list of keywords that are set for the message.
+Only those keywords that you have defined in your
+<A HREF="h_config_keywords"><!--#echo var="VAR_keywords"--></A> option
+in Setup/Config are considered in the list.
+In other words, keywords that have been set by some other means, perhaps
+by another email program, won't show up unless included in
+<A HREF="h_config_keywords"><!--#echo var="VAR_keywords"--></A>.
+If you have given a keyword a nickname
+(<A HREF="h_config_keywords"><!--#echo var="VAR_keywords"--></A>), that nickname is displayed
+instead of the actual keyword.
+It is also possible to color keywords in the index using the
+Setup/Kolor screen (<A HREF="h_config_kw_color">Keyword Colors</A>).
+This token defaults to an arbitrary width of 5.
+You should set it to whatever width suits you using something
+like KEY(17) in the <!--#echo var="VAR_index-format"-->.
+</DD>
+
+<DT>KEYINIT</DT>
+<DD>
+This is a list of keyword initials that are set for the message.
+If you have given a keyword a nickname
+(<A HREF="h_config_keywords"><!--#echo var="VAR_keywords"--></A>), the initial of that nickname
+is displayed instead of the initial of the actual keyword.
+It is also possible to color keyword initials in the index using the
+Setup/Kolor screen (<A HREF="h_config_kw_color">Keyword Colors</A>).
+This token defaults to an arbitrary width of 2.
+You should set it to whatever width suits you using something
+like KEYINIT(3) in the <!--#echo var="VAR_index-format"-->.
+</DD>
+
+<DT>PRIORITY</DT>
+<DD>
+The X-Priority header is a non-standard header that is used in a
+somewhat standard way by many mail programs.
+Alpine expects the value of this header to be a digit with a value
+from 1 to 5, with 1 being the highest priority and 5 the lowest priority.
+Since this priority is something that the sender sets it is only an indication
+of the priority that the sender attaches to the mail and it is therefore almost
+totally unreliable for use as a filtering criterion.
+This token will display the numeric value of the priority if it is between
+1 and 5.
+It will be suppressed (blank) if the value is 3, which is normal priority.
+This token may be colored with the
+<A HREF="h_config_index_pri_color">Index Priority Symbol Colors</A>.
+</DD>
+
+<DT>PRIORITYALPHA</DT>
+<DD>
+This is a more verbose interpretation of the X-Priority field.
+Once again nothing is displayed unless the value of the field
+is 1, 2, 4, or 5.
+The values displayed for those values are:
+<P>
+<TABLE>
+<TR> <TD>1</TD> <TD>Highest</TD> </TR>
+<TR> <TD>2</TD> <TD>High</TD> </TR>
+<TR> <TD>4</TD> <TD>Low</TD> </TR>
+<TR> <TD>5</TD> <TD>Lowest</TD> </TR>
+</TABLE>
+<P>
+This token may be colored with the
+<A HREF="h_config_index_pri_color">Index Priority Symbol Colors</A>.
+</DD>
+
+<DT>PRIORITY!</DT>
+<DD>
+This is a one character, non-numeric version of the X-Priority field.
+If the value of the X-Priority header is 1 or 2 an exclamation
+point is displayed.
+If the value is 4 or 5 a &quot;v&quot; (think down arrow) is displayed.
+This token may be colored with the
+<A HREF="h_config_index_pri_color">Index Priority Symbol Colors</A>.
+</DD>
+
+<DT>ATT</DT>
+<DD>
+This is a one column wide field that represents the number of attachments
+a message has. It will be blank if there are no attachments, a single
+digit for one to nine attachments, or an asterisk for more than nine.
+Note that including this token in
+the &quot;<!--#echo var="VAR_index-format"-->&quot; could slow down the
+display a little while Alpine collects the necessary information.
+</DD>
+
+<DT>FROMORTO</DT>
+<DD>
+This token represents <EM>either</EM> the personal name (or email address) of
+the person listed in the message's &quot;From:&quot; header
+field, <EM>or</EM>, if that address is yours or one of your
+<A HREF="h_config_alt_addresses">alternate addresses</A>,
+the first person specified in the
+message's &quot;To:&quot; header field
+with the prefix &quot;To: &quot; prepended.
+If the from address is yours and there is also no &quot;To&quot; address,
+Alpine will use the address on the &quot;Cc&quot; line.
+If there is no address there, either, Alpine will look for a newsgroup name
+from the &quot;Newsgroups&quot; header field and put
+that after the &quot;To: &quot; prefix.
+</DD>
+
+<DT>FROMORTONOTNEWS</DT>
+<DD>
+This is almost the same as <EM>FROMORTO</EM>.
+The difference is that newsgroups aren't considered.
+When a message is from you, doesn't have a To or Cc, and does have
+a Newsgroups header; this token will be your name instead of the name
+of the newsgroup (like it would be with FROMORTO).
+</DD>
+
+<DT>TEXT</DT>
+<DD>
+This is a different sort of token.
+It allows you to display a label within each index line.
+It will be the same fixed text for each line.
+It is different from all the other tokens in that there is no space column
+displayed after this token.
+Instead, it is butted up against the following field.
+It also has a different syntax.
+The text to display is given following a colon after the
+word &quot;TEXT&quot;.
+For example,
+<P>
+<CENTER><SAMP>TEXT:abc=</SAMP></CENTER>
+<P>
+would insert the literal text &quot;abc=&quot; (without the quotes)
+into the index display line.
+You must quote the text if it includes space characters, like
+<P>
+<CENTER><SAMP>TEXT:&quot;abc&nbsp;=&nbsp;&quot;</SAMP></CENTER>
+<P>
+</DD>
+
+<DT>HEADER</DT>
+<DD>
+This allows you to display the text from a particular header line in the
+message.
+The syntax for this token is substantially different from all the others
+in order that you might be able to display a portion of the text following
+a particular header.
+The header name you are interested in is given following a colon
+after the word &quot;HEADER&quot;.
+For example,
+<P>
+<CENTER><SAMP>HEADER:X-Spam</SAMP></CENTER>
+<P>
+would display the text of the X-Spam header, if any.
+Like for other index tokens a width field may (and probably should)
+follow this.
+<P>
+<CENTER><SAMP>HEADER:X-Spam(10)</SAMP></CENTER>
+<P>
+displays the first ten characters of the X-Spam header.
+Unlike other index tokens, the syntax for HEADER is more flexible.
+An optional second argument comes after a comma inside the parentheses.
+It specifies the &quot;field&quot; number.
+By default, the field separator is a space character.
+No extra space characters are allowed in the argument list.
+<P>
+<CENTER><SAMP>HEADER:X-Spam(10,2)</SAMP></CENTER>
+<P>
+would display the second field, left-justified, in a 10 character
+wide field.
+The second field would consist of all the text after the first space
+up to the next space or the end of the header.
+The default field number is zero, which stands for the entire line.
+There is also an optional third argument that is a list of field
+separators. It defaults to a space character.
+The example
+<P>
+<CENTER><SAMP>HEADER:X-Spam(10,2,:%&nbsp;)</SAMP></CENTER>
+<P>
+would cause the field separators to be any of colon, percent,
+or space (there is a space character between the percent and the
+right parenthesis).
+The first field runs from the start of the header value up to the first
+colon, percent, or space; the second goes from there to the next; and so on.
+In order to use a comma character as a field separator you must escape
+it by preceding it with a backslash (&#92;).
+The same is true of the backslash character itself.
+There is one further optional argument.
+It is an R or an L to specify right or left adjustment of the text
+within the field.
+The default is to left justify, however if you are displaying numbers
+you might prefer to right justify.
+<P>
+Here's an example of a SpamAssassin header.
+The exact look of the header will vary, but if your incoming mail
+contains headers that look like the following
+<P>
+<CENTER><SAMP>X-Spam-Status: Yes, hits=10.6 tagged_above=-999.0 required=7.0 tests=BAYE...</SAMP></CENTER>
+<P>
+you might want to display the hits value.
+The first field starts with the Y in Yes.
+To get what you're interested in you might use &quot;=&quot; and
+space as the field separators and display the third field, like
+<P>
+<CENTER><SAMP>HEADER:X-Spam-Status(4,3,=&nbsp;)</SAMP></CENTER>
+<P>
+or maybe you would break at the dot instead
+<P>
+<CENTER><SAMP>HEADER:X-Spam-Status(2,2,=.,R)</SAMP></CENTER>
+<P>
+Another example we've seen has headers that look like
+<P>
+<CENTER><SAMP>X-Spam: Gauge=IIIIIII, Probability=7%, Report=...</SAMP></CENTER>
+<P>
+Because there are two equals and a comma before the 7% and a comma
+after it, the token
+<P>
+<CENTER><SAMP>HEADER:X-Spam(3,4,=&#92;,,R)</SAMP></CENTER>
+<P>
+should display the probability (for example 7% or 83%) right justified
+in a 3-wide field.
+</DD>
+
+<DT>ARROW</DT>
+<DD>
+This gives an alternative way to display the current message in the
+MESSAGE INDEX screen.
+Usually the current message is indicated by the line being shown in
+reverse video.
+Instead, if the ARROW token is included in your <!--#echo var="VAR_index-format"-->,
+the current line will include an &quot;arrow&quot; that
+looks like
+<P>
+<CENTER><SAMP>-&gt;</SAMP></CENTER>
+<P>
+in the ARROW token's field.
+For all of the non-current messages, the ARROW field will be filled
+with blanks.
+If you use the fixed-field width feature the length of the &quot;arrow&quot;
+may be adjusted.
+The arrow will be drawn as width-1 dashes followed by a greater than sign.
+For example, if you use ARROW(3) you will get
+<P>
+<CENTER><SAMP>--&gt;</SAMP></CENTER>
+<P>
+and ARROW(1) will give you just
+<P>
+<CENTER><SAMP>&gt;</SAMP></CENTER>
+<P>
+It is also possible to set the color of the ARROW field.
+By default (and for non-current messages) the arrow is colored the same
+as the index line it is part of.
+You may set it to be another color with the
+<A HREF="h_config_index_arrow_color">Index Arrow Color</A> option available from
+the <A HREF="h_color_setup">Setup Kolor</A> screen.
+</DD>
+
+<DT>SCORE</DT>
+<DD>
+This gives the
+<a href="h_rules_score">score</a>
+of each message.
+This will be six columns wide to accomodate the widest possible score.
+You will probably want to use the <!--#echo var="VAR_index-format"--> fixed-field width feature
+to limit the width of the field to the widest score that
+you use (e.g. SCORE(3) if your scores are always between 0 and 999).
+If you have not defined any score rules the scores will all be zero.
+If any of your score rules contain AllText or BodyText patterns
+then including SCORE in the <!--#echo var="VAR_index-format"-->
+may slow down the display of the MESSAGE INDEX screen.
+</DD>
+</DL>
+
+<P>
+<H1><EM>Tokens Available for all but <!--#echo var="VAR_index-format"--></EM></H1>
+
+<DL>
+<DT>CURNEWS</DT>
+<DD>
+This token represents the current newsgroup if there is one.
+For example, &quot;comp.mail.pine&quot;.
+</DD>
+
+<DT>MSGID</DT>
+<DD>
+This token represents the message ID of the message.
+This token does not work with Filter Rule folder names.
+</DD>
+
+<DT>CURDATE</DT>
+<DD>
+This token represents the current date.
+It has the format MMM DD. For example, &quot;Oct 23&quot;.
+</DD>
+
+<DT>CURDATEISO</DT>
+<DD>
+This token represents the current date.
+It has the format YYYY-MM-DD. For example, &quot;1998-10-23&quot;.
+</DD>
+
+<DT>CURDATEISOS</DT>
+<DD>
+This token represents the current date.
+It has the format YY-MM-DD. For example, &quot;98-10-23&quot;.
+</DD>
+
+<DT>CURPREFDATE</DT>
+<DD>
+This token represents the current date.
+It is your operating system's idea of the preferred date representation for the current locale.
+Internally it uses the %x version of the date from the strftime routine.
+</DD>
+
+<DT>CURPREFTIME</DT>
+<DD>
+This token represents the current time.
+It is the preferred time representation for the current locale.
+Internally it uses the %X version of the time from the strftime routine.
+</DD>
+
+<DT>CURPREFDATETIME</DT>
+<DD>
+This token represents the current date and time.
+It is the preferred date and time representation for the current locale.
+Internally it uses the %c version of the time from the strftime routine.
+</DD>
+
+<DT>CURTIME24</DT>
+<DD>
+This token represents the current time.
+It has the format HH:MM. For example, &quot;17:28&quot;.
+</DD>
+
+<DT>CURTIME12</DT>
+<DD>
+This token represents the current time.
+This time is for a 12 hour clock.
+It has the format HH:MMpm.
+For example, &quot;5:28pm&quot; or &quot;11:13am&quot;.
+</DD>
+
+<DT>CURDAY</DT>
+<DD>
+This token represents the current day of the month.
+For example, &quot;23&quot; or &quot;9&quot;.
+</DD>
+
+<DT>CURDAY2DIGIT</DT>
+<DD>
+This token represents the current day of the month.
+For example, &quot;23&quot; or &quot;09&quot;.
+It is always 2 digits.
+</DD>
+
+<DT>CURDAYOFWEEK</DT>
+<DD>
+This token represents the current day of the week.
+For example, &quot;Sunday&quot; or &quot;Wednesday&quot;.
+</DD>
+
+<DT>CURDAYOFWEEKABBREV</DT>
+<DD>
+This token represents the current day of the week.
+For example, &quot;Sun&quot; or &quot;Wed&quot;.
+</DD>
+
+<DT>CURMONTH</DT>
+<DD>
+This token represents the current month.
+For example, &quot;10&quot; or &quot;9&quot;.
+</DD>
+
+<DT>CURMONTH2DIGIT</DT>
+<DD>
+This token represents the current month.
+For example, &quot;10&quot; or &quot;09&quot;.
+It is always 2 digits.
+</DD>
+
+<DT>CURMONTHLONG</DT>
+<DD>
+This token represents the current month.
+For example, &quot;October&quot;.
+</DD>
+
+<DT>CURMONTHABBREV</DT>
+<DD>
+This token represents the current month.
+For example, &quot;Oct&quot;.
+</DD>
+
+<DT>CURYEAR</DT>
+<DD>
+This token represents the current year.
+For example, &quot;1998&quot; or &quot;2001&quot;.
+</DD>
+
+<DT>CURYEAR2DIGIT</DT>
+<DD>
+This token represents the current year.
+For example, &quot;98&quot; or &quot;01&quot;.
+It is always 2 digits.
+</DD>
+
+<DT>LASTMONTH</DT>
+<DD>
+This token represents last month.
+For example, if this is November (the 11th month),
+it is equal to &quot;10&quot; or if this is October (the 10th month),
+it is &quot;9&quot;.
+It is possible that this and the other tokens beginning with LASTMONTH
+below could be useful when used with a Filtering Rule that
+has the &quot;Beginning of Month&quot; option set.
+</DD>
+
+<DT>LASTMONTH2DIGIT</DT>
+<DD>
+This token represents last month.
+For example, if this is November (the 11th month),
+it is equal to &quot;10&quot; or if this is October (the 10th month),
+it is &quot;09&quot;.
+It is always 2 digits.
+</DD>
+
+<DT>LASTMONTHLONG</DT>
+<DD>
+This token represents last month.
+For example, if this is November the value is &quot;October&quot;.
+</DD>
+
+<DT>LASTMONTHABBREV</DT>
+<DD>
+This token represents last month.
+For example, if this is November the value is &quot;Oct&quot;.
+</DD>
+
+<DT>LASTMONTHYEAR</DT>
+<DD>
+This token represents what the year was a month ago.
+For example, if this is October, 1998, it is &quot;1998&quot;.
+If this is January, 1998, it is &quot;1997&quot;.
+</DD>
+
+<DT>LASTMONTHYEAR2DIGIT</DT>
+<DD>
+This token represents what the year was a month ago.
+For example, if this is October, 1998, it is &quot;98&quot;.
+If this is January, 1998, it is &quot;97&quot;.
+</DD>
+
+<DT>LASTYEAR</DT>
+<DD>
+This token represents last year.
+For example, if this is 1998, it equals &quot;1997&quot;.
+It is possible that this
+could be useful when used with a Filtering Rule that
+has the &quot;Beginning of Year&quot; option set.
+</DD>
+
+<DT>LASTYEAR2DIGIT</DT>
+<DD>
+This token represents last year.
+For example, if this is 1998, it equals &quot;97&quot;.
+It is always 2 digits.
+</DD>
+
+<DT>ROLENICK</DT>
+<DD>
+This token represents the nickname of the
+role currently being used. If no role is being used,
+then no text will be printed for this token.
+This token does not work with Filter Rule folder names.
+</DD>
+</DL>
+
+<P>
+<H1><EM>Token Available Only for <!--#echo var="VAR_reply-leadin"--></EM></H1>
+See the help for the
+<A HREF="h_config_reply_intro">&quot;<!--#echo var="VAR_reply-leadin"-->&quot;</A> option
+to see why you might want to use this.
+Since the <!--#echo var="VAR_reply-leadin"--> contains free text this token
+must be surrounded by underscores when used.
+
+<DL>
+<DT>NEWLINE</DT>
+<DD>
+This is an end of line marker.
+</DD>
+</DL>
+
+<P>
+<H1><EM>Token Available Only for Templates and Signatures</EM></H1>
+
+<DL>
+<DT>CURSORPOS</DT>
+<DD>
+This token is different from the others.
+When it is replaced it is replaced with nothing, but it sets an Alpine
+internal variable that tells the composer to start with the cursor
+positioned at the position where this token was.
+If both the template file and the signature file contain
+a &quot;CURSORPOS&quot; token, then the position in the template file
+is used.
+If there is a template file and neither it nor the signature file contains
+a &quot;CURSORPOS&quot; token, then the cursor is positioned
+after the end of the contents of the
+template file when the composer starts up.
+</DD>
+</DL>
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_reply_token_conditionals =======
+<HTML>
+<HEAD>
+<TITLE>Conditional Inclusion of Text for <!--#echo var="VAR_reply-leadin"-->, Signatures, and Templates</TITLE>
+</HEAD>
+<BODY>
+<H1>Conditional Inclusion of Text for <!--#echo var="VAR_reply-leadin"-->, Signatures, and Templates</H1>
+
+Conditional text inclusion may be used with
+the <A HREF="h_config_reply_intro">&quot;<!--#echo var="VAR_reply-leadin"-->&quot;</A> option,
+in signature files, and in template files used in
+<A HREF="h_rules_roles">&quot;roles&quot;</A>.
+It may <EM>not</EM> be used with the
+<A HREF="h_config_index_format">&quot;<!--#echo var="VAR_index-format"-->&quot;</A> option.
+
+<P>
+There is a limited if-else capability for including text.
+The if-else condition is based
+on whether or not a given token would result in replacement text you
+specify.
+The syntax of this conditional inclusion is
+<P>
+<CENTER><SAMP>_token_(match_this, if_matched [ , if_not_matched ] )</SAMP></CENTER>
+<P>
+The left parenthesis must follow the underscore immediately, with no
+intervening space.
+It means the token is expanded and the results of that expansion are
+compared against the &quot;match_this&quot; argument.
+If there is an exact match, then the &quot;if_matched&quot; text is used
+as the replacement text.
+Otherwise, the &quot;if_not_matched&quot; text is used.
+One of the most useful values for the &quot;match_this&quot; argument is
+the empty string, &quot;&quot;.
+In that case the expansion is compared against the empty string.
+<P>
+Here's an example to make it clearer.
+This text could be included in one of your template files:
+<P>
+<CENTER><SAMP>_NEWS_(&quot;&quot;, &quot;I'm replying to email&quot;, &quot;I'm replying to news&quot;)</SAMP></CENTER>
+<P>
+If that is included in a template file that you are using while replying
+to a message (because you chose to use the role it was part of),
+and that message has a newsgroup header and a newsgroup in that header,
+then the text
+<P>
+<CENTER><SAMP>I'm replying to news</SAMP></CENTER>
+<P>
+will be included in the message you are about to compose.
+On the other hand, if the message you are replying to does not have
+a newsgroup, then the text
+<P>
+<CENTER><SAMP>I'm replying to email</SAMP></CENTER>
+<P>
+would be included instead.
+This would also work in signature files and in
+the &quot;<!--#echo var="VAR_reply-leadin"-->&quot; option.
+If the &quot;match_this&quot;, &quot;if_matched&quot;,
+or &quot;if_not_matched&quot; arguments contain
+spaces, parentheses, or commas;
+they have to be quoted with double quotation marks (like in the example
+above).
+If you want to include a literal quote (&quot;) in the text you must escape the
+quote by preceding it with a backslash (&#92;) character.
+If you want to include a literal backslash character you must escape it
+by preceding it with another backslash.
+<P>
+The comma followed by &quot;if_not_matched&quot; is optional.
+If there is no &quot;if_not_matched&quot;
+present then no text is included if the not_matched case is true.
+Here's another example:
+<P>
+<CENTER><SAMP>_NEWS_(&quot;&quot;, &quot;&quot;, &quot;This msg was seen in group: _NEWS_.&quot;)</SAMP></CENTER>
+<P>
+Here you can see that tokens may appear in the arguments.
+The same is true for tokens with the conditional parentheses.
+They may appear in arguments,
+though you do have to be careful to get the quoting and escaping of
+nested double quotes correct.
+If this was in the signature file being used and you were replying to a message
+sent to comp.mail.pine the resulting text would be:
+<P>
+<CENTER><SAMP>This msg was seen in group: comp.mail.pine.</SAMP></CENTER>
+<P>
+If you were replying to a message that wasn't sent to any newsgroup the
+resulting text would be a single blank line.
+The reason you'd get a blank line is because the end of the line is
+outside of the conditional, so is always included.
+If you wanted to get rid of that blank line you could do so by moving
+the end of line inside the conditional.
+In other words, it's ok to have multi-line
+&quot;if_matched&quot; or &quot;if_not_matched&quot; arguments in your
+template file.
+The text just continues until the next double quotation, even if it's not
+on the same line.
+<P>
+Here's an example for use in the &quot;<!--#echo var="VAR_reply-leadin"-->&quot;:
+<P>
+<CENTER><SAMP>On _DAYDATE_, _FROM__CURNEWS_(&quot;&quot;, &quot;&quot;, &quot;seen in _CURNEWS_,&quot;) wrote</SAMP></CENTER>
+<P>
+If this was in your <!--#echo var="VAR_reply-leadin"--> and you were replying to a message
+while reading the newsgroup comp.mail.pine the resulting text would be:
+<P>
+<CENTER><SAMP>On Sat, 24 Oct 1998, Fred Flintstone, seen in comp.mail.pine, wrote:</SAMP></CENTER>
+<P>
+If you were replying to a message while reading an email folder instead
+of a newsgroup the resulting leadin text would be
+<P>
+<CENTER><SAMP>On Sat, 24 Oct 1998, Fred Flintstone wrote:</SAMP></CENTER>
+<P>
+Here's one more (contrived) example illustrating a matching argument
+that is not the empty string.
+<P>
+<CENTER><SAMP>_SMARTDATE_("Today", _SMARTDATE_, "On _DATE_") _FROM_ wrote:</SAMP></CENTER>
+<P>
+If this was the value of your &quot;<!--#echo var="VAR_reply-leadin"-->&quot; option and you
+were replying to
+a message that was sent today, then the value of the &quot;<!--#echo var="VAR_reply-leadin"-->&quot;
+would be
+<P>
+<CENTER><SAMP>Today Fred Flintstone wrote:</SAMP></CENTER>
+<P>
+But if you were replying to a message sent on Oct. 27 (and that wasn't
+today) you would get
+<P>
+<CENTER><SAMP>On Oct 27 Fred Flintstone wrote:</SAMP></CENTER>
+<P>
+
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_composer_cntxt_nick =======
+<HTML>
+<HEAD>
+<TITLE>Collection Nickname Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Collection Edit Help -- Nickname Field</H1>
+
+This field is provided so you can add a short nickname to use when
+referring to this collection within Alpine. Spaces are allowed, and
+you don't need to use double-quotes. However, the double-quote
+character is not allowed.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_folder_server_syntax =======
+<HTML>
+<HEAD>
+<TITLE>Server Name Syntax</TITLE>
+</HEAD>
+<BODY>
+<H1>Server Name Syntax</H1>
+
+This help describes the syntax that may be used for server names
+that may be associated with remote folders or SMTP servers.
+
+<P>
+A server name is the hostname of the server.
+It's a good idea to use the host's fully-qualified network name.
+
+<P>
+<CENTER><SAMP>foo.example.com</SAMP></CENTER>
+<P>
+
+However, IP addresses are allowed if surrounded
+with square-brackets.
+
+<P>
+<CENTER><SAMP>[127.0.0.1]</SAMP></CENTER>
+<P>
+
+An optional network port number may be supplied by appending
+a colon (:) followed by the port number
+to the server name.
+By default, the IMAP port number, 143, is used.
+
+<P>
+<CENTER><SAMP>foo.example.com:port</SAMP></CENTER>
+<P>
+
+Besides server name and optional port number, various other optional
+parameters may be supplied that alter Alpine's interaction with the server.
+A parameter is supplied by appending a slash (/) character followed by
+the parameter's name and,
+depending on the particular parameter, the value assigned to that
+name, to the server name (and optional port number).
+Parameter names are <EM>not</EM> case sensitive.
+Currently supported parameters include:
+
+<DL>
+
+<DT>User</DT>
+<DD>This parameter requires an associated value, and is intended to
+provide the username identifier with which to establish the server
+connection.
+If your SMTP server offers SMTP AUTH authentication, adding this
+parameter to the
+<A HREF="h_config_smtp_server">&quot;<!--#echo var="VAR_smtp-server"-->&quot;</A>
+option will cause Alpine to attempt to authenticate to the server using the
+supplied username.
+Similarly, if your NNTP server offers NNTP &quot;AUTHINFO SASL&quot;
+or &quot;AUTHINFO USER&quot; authentication, adding this parameter to the
+<A HREF="h_config_nntp_server"><!--#echo var="VAR_nntp-server"--></A>
+option (or to the server name for any folder collection using NNTP)
+will cause Alpine to attempt
+to authenticate to the server using the supplied username.
+An example might be:
+
+<P>
+<CENTER><SAMP>/user=katie</SAMP></CENTER>
+<P>
+
+</DD>
+
+<DT>TLS</DT>
+<DD>
+Normally, when a new connection is made an attempt is made to
+negotiate a secure (encrypted) session using Transport Layer Security (TLS).
+If that fails then a non-encrypted connection will be attempted instead.
+This is a unary parameter indicating communication with the server must
+take place over a TLS connection. If the attempt to use TLS fails then
+this parameter will cause the connection to fail instead of falling
+back to an unsecure connection.
+
+<P>
+<CENTER><SAMP>/tls</SAMP></CENTER>
+<P>
+
+</DD>
+
+<DT>SSL</DT>
+<DD>
+This is a unary parameter indicating communication with the server should
+take place over a Secure Socket Layer connection. The server must support
+this method, and be prepared to accept connections on the appropriate
+port (993 by default).
+Alpine must be linked with an SSL library for this option to be operational.
+
+<P>
+<CENTER><SAMP>/ssl</SAMP></CENTER>
+<P>
+
+</DD>
+
+<DT>NoValidate-Cert</DT>
+<DD>Do not validate certificates (for TLS or SSL connections) from the server.
+This is needed if the server uses self-signed certificates or if Alpine
+cannot validate the certificate for some other known reason.
+<P>
+</DD>
+
+<DT>Anonymous</DT>
+<DD>This is a unary parameter (that means it does not have a value)
+indicating that the connection be logged in as
+&quot;anonymous&quot; rather than a specific user.
+Not all servers offer anonymous
+access; those which do generally only offer read-only access to certain
+&quot;public&quot; folders.
+
+<P>
+<CENTER><SAMP>/anonymous</SAMP></CENTER>
+<P>
+
+</DD>
+
+<DT>Secure</DT>
+<DD>This is a unary parameter indicating that the connection use the
+most secure authentication method mutually supported by Alpine and the
+server.
+Alpine is capable of authenticating connections to
+the server using several methods.
+By default, Alpine will attempt each
+method until either a connection is established or the
+list of methods is exhausted.
+This parameter causes Alpine to instead fail
+the connection if the first (generally most &quot;secure&quot;) method fails.
+
+<P>
+<CENTER><SAMP>/secure</SAMP></CENTER>
+<P>
+
+</DD>
+
+<DT>Submit</DT>
+<DD>This is a unary parameter for use with the
+<A HREF="h_config_smtp_server">&quot;<!--#echo var="VAR_smtp-server"-->&quot;</A> option.
+It indicates that the connection should be made to the Submit server
+(<A HREF="http://www.ietf.org/rfc/rfc2476.txt">RFC 3676</A>)
+(port 587) instead of the SMTP port (25).
+At the time this help was written the submit option was equivalent to
+specifying port 587.
+
+<P>
+<CENTER><SAMP>/submit</SAMP></CENTER>
+<P>
+or
+<P>
+<CENTER><SAMP>host:587</SAMP></CENTER>
+<P>
+
+</DD>
+
+<DT>Debug</DT>
+<DD>This is a unary parameter indicating that the connection be established
+in a verbose mode. Basically, it causes Alpine to log the communication with
+the server in Alpine's debug file.
+Normally, the pine -d command-line flag would be used instead.
+<P>
+</DD>
+
+<DT>NoRsh</DT>
+<DD>By default, Alpine attempts to login using &quot;rsh&quot;,
+the UNIX remote shell program.
+Including &quot;NoRsh&quot; will cause connections to this server to skip
+the &quot;rsh&quot; attempt.
+This might be useful to avoid long timeouts caused by rsh firewalls, for
+example.
+<P>
+</DD>
+
+<DT>Service</DT>
+<DD>This parameter requires an associated value. The default value is
+&quot;IMAP&quot; which indicates communication with the server based
+on the IMAP4rev1 protocol (defined in RFC 3501 -- see
+<A HREF="http://www.imap.org/docs/rfc3501.html">http://www.imap.org/docs/rfc3501.html</A>).</DD>
+
+Other service values include:
+ <DL>
+ <DT>NNTP</DT>
+ <DD>This value indicates communication with the server takes place via
+the Network News Transfer Protocol. Use this to define a collection
+of newsgroups on a remote news server. So
+
+<P>
+<CENTER><SAMP>/service=NNTP</SAMP></CENTER>
+<P>
+or just
+<P>
+<CENTER><SAMP>/NNTP</SAMP></CENTER>
+<P>
+
+is the way to specify NNTP access.
+<P>
+ </DD>
+
+ <DT>POP3</DT>
+ <DD>This value indicates communication with the server takes place via the
+Post Office Protocol 3 protocol.
+
+<P>
+<CENTER><SAMP>/service=POP3</SAMP></CENTER>
+<P>
+or just
+<P>
+<CENTER><SAMP>/POP3</SAMP></CENTER>
+<P>
+
+Note that there are several important issues
+to consider when selecting this option:
+<OL>
+ <LI> POP3 provides access to only your INBOX. In other words,
+secondary folders such as your &quot;saved-messages&quot; are inaccessible.
+ <LI> Alpine's implementation of POP3 does not follow the traditional POP
+model and will leave your mail on the server. Refer to the
+<A HREF="h_maildrop">Mail Drop</A> functionality for a possible way around this problem.
+ <LI> See the discussion about new-mail checking in <A HREF="h_config_reopen_rule">&quot;<!--#echo var="VAR_folder-reopen-rule"-->&quot;</A>.
+</OL>
+</DD>
+</DL>
+</DL>
+
+<P>
+Note that it is possible to include more than one parameter in a server
+specification by concatenating the parameters. For example:
+
+<P>
+<CENTER><SAMP>foo.example.com:port/user=katie/novalidate-cert/debug</SAMP></CENTER>
+<P>
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_composer_cntxt_server =======
+<HTML>
+<HEAD>
+<TITLE>Collection Server: Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Collection Edit Help -- Server Field</H1>
+
+This collection's &quot;Server:&quot; definition indicates the
+hostname of the server providing access to the folders in this
+collection.
+The syntax of this server name is the same as for other server names used
+in remote folder names in
+Alpine and is described
+<A HREF="h_folder_server_syntax">here</A>.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_composer_cntxt_path =======
+<HTML>
+<HEAD>
+<TITLE>Collection Path: Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Collection Edit Help -- Path Field</H1>
+
+The collection's &quot;Path:&quot; definition indicates the location
+of the folders in this collection. If the path or any of its components
+do not exist, Alpine will prompt you for their creation when exiting the
+Add/Change screen.
+
+<P>
+By default the path is interpreted as defining a section of your personal
+folder area. This area and how you specify it are defined by the
+server, if one is specified in the collection, or, typically, the home
+directory if no server is defined.
+
+<P>
+To define a collection outside the default &quot;area&quot;, prefix
+the path with the &quot;namespace&quot; to use when interpreting the
+given path. If a namespace is specified, the Path begins with the
+sharp, &quot;#&quot;, character followed by the name of the namespace
+and then the namespace's path-element-delimiter. Aside from the
+path's format, namespaces can also imply access rights, content
+policy, audience, location, and, occasionally, access methods.
+
+<P>
+Each server exports its own set (possibly of size one) of
+namespaces. Hence, it's likely communication with your server's
+administrator will be required for specific configurations. Some of
+the more common namespaces, however, include:
+
+<DL>
+<DT>#news.</DT>
+<DD>This specifies a set of folders in the newsgroup namespace. Newsgroup
+names are hierarchically defined with each level delimited by a period.
+</DD>
+<DT>#public/</DT>
+<DD>This specifies a folder area that the server may export to the general
+public.
+</DD>
+<DT>#shared/</DT>
+<DD>This specifies a folder area that the folder may export to groups
+of users.
+</DD>
+<DT>#ftp/</DT>
+<DD>This specifies a folder area that is the same as that it may have
+exported via the &quot;File Transfer Protocol&quot;.
+</DD>
+<DT>#mh/</DT>
+<DD>This specifies the personal folder area associated with folders
+and directories that were created using the MH message handling system.
+</DD>
+</DL>
+<P>
+
+In addition, the server may support access to other user's folders,
+provided you have suitable permissions. Common methods use a prefix
+of either &quot;~<VAR>user</VAR>/&quot;, or &quot;/<VAR>user</VAR>/&quot; to
+indicate the root of the other user's folder area.
+
+<P>
+No, nothing's simple.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_composer_cntxt_view =======
+<HTML>
+<HEAD>
+<TITLE>Collection View: Explained</TITLE>
+</HEAD>
+<BODY>
+<H1>Collection Edit Help -- View Field</H1>
+
+The collection's &quot;View:&quot; definition provides a way to limit
+the displayed list of folders within a collection. By default, only
+folders that contain the specified characters anywhere in their name
+are shown in the collection's folder list.
+
+<P>
+Additionally, you can use a wildcard character to better control
+the list of folders selected for display. The wildcard specifier is
+the star, &quot;*&quot;, character.
+
+<P>
+So, for example, to define a collection of all folders ending with
+&quot;c&quot;, you'd specify a view of &quot;*c&quot; (without the
+quote characters!). Or, similarly, to define a collection of folders
+whose names start with &quot;a&quot; and end with &quot;z&quot;, you'd
+specify a view of &quot;a*z&quot;.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_composer_abook_add_server =======
+<HTML>
+<HEAD>
+<TITLE>Addressbook Server Name Field Explained</TITLE>
+</HEAD>
+<BODY>
+This field should be left blank if the address book is stored in a regular
+file on this system. If it is a remote address book stored on an IMAP
+server then this is the name of that IMAP server.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_composer_abook_add_folder =======
+<HTML>
+<HEAD>
+<TITLE>Addressbook Folder Name Field Explained</TITLE>
+</HEAD>
+<BODY>
+For a remote address book (one for which the Server Name is filled in)
+this is the name of a folder on the remote server. The address book data
+will be stored in this folder. This folder should be used only for
+storing this single address book, not for other address books or for
+other messages.
+<P>
+For a local address book (one for which the Server Name is not filled in)
+this is the name of a file in which the address book will be stored.
+The file is in the same directory as the Alpine configuration file if the
+configuration file is local.
+If the configuration file is remote, then this will be in the home directory
+for Unix Alpine and in the directory specified by the
+&quot;-aux local_directory&quot; command line argument.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_composer_abook_add_nick =======
+<HTML>
+<HEAD>
+<TITLE>Addressbook NickName Field Explained</TITLE>
+</HEAD>
+<BODY>
+This is just an optional nickname for this address book. If present, it
+is used in some of the displays and error messages in the address book
+maintenance screens. It is for your convenience only and serves no
+other purpose.
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_composer_qserv_cn =======
+<HTML>
+<HEAD>
+<TITLE>Directory Query Form Explained</TITLE>
+</HEAD>
+<BODY>
+
+Fill in as many of these fields as you wish to narrow down your
+search. All the fields you fill in must match in order for an entry
+to be returned. You may use the wildcard character &quot;*&quot; in
+any of the fields, it matches any zero or more characters at that
+point in the string. There are no implicit wildcards, so the match is
+exact unless you include wildcards.
+<P>
+
+Note that if an attribute isn't present at all, then the match will fail.
+For example, if a server doesn't support the Locality attribute, then no
+matter what you put in the Locality field (other than leaving it empty)
+the search will fail.
+<P>
+
+This field, the Common Name field, is typically a person's full name.
+<P>
+
+<H1>EDITING and NAVIGATION COMMANDS</H1>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+CURSOR MOTION KEYS----------------------|EDITING KEYS-------------------------
+^B (Left Arrow) Back character | ^D Delete current character
+^F (Right Arrow) Forward character | ^H (DEL) Delete previous character
+^P (Up Arrow) Previous line |
+^N (Down Arrow) Next line | F9 Cut marked text or
+^A Beginning of line | delete current line
+^E End of line | F10 Undelete line(s)
+F7 Previous page |
+F8 Next page |-------------------------------------
+^@ (Ctrl-SPACE) Next word | MISCELLANEOUS COMMANDS
+----------------------------------------|
+EXIT COMMANDS | GENERAL COMMANDS | F5 Restore previous search
+F2 Cancel | F1 Get help |
+F3 Search | ^Z Suspend | ^L Redraw Screen
+</PRE>
+<!--chtml else-->
+<PRE>
+CURSOR MOTION KEYS----------------------|EDITING KEYS-------------------------
+^B (Left Arrow) Back character | ^D Delete current character
+^F (Right Arrow) Forward character | ^H (DEL) Delete previous character
+^P (Up Arrow) Previous line |
+^N (Down Arrow) Next line | ^K Cut marked text or
+^A Beginning of line | delete current line
+^E End of line | ^U Undelete line(s)
+^Y Previous page |
+^V Next page |-------------------------------------
+^@ (Ctrl-SPACE) Next word | MISCELLANEOUS COMMANDS
+----------------------------------------|
+EXIT COMMANDS | GENERAL COMMANDS | ^R Restore previous search
+^C Cancel | ^G Get help |
+^X Search | ^Z Suspend | ^L Redraw Screen
+</PRE>
+<!--chtml endif-->
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_composer_qserv_sn =======
+
+The Surname is usually the family name of a person.
+
+<End of help on this topic>
+======= h_composer_qserv_gn =======
+
+This is the part of a person's name that isn't the surname or initials.
+
+<End of help on this topic>
+======= h_composer_qserv_mail =======
+
+This is the email address of a person.
+
+<End of help on this topic>
+======= h_composer_qserv_org =======
+
+This is the organization a person belongs to.
+
+<End of help on this topic>
+======= h_composer_qserv_unit =======
+
+This is the organizational unit a person belongs to.
+
+<End of help on this topic>
+======= h_composer_qserv_country =======
+
+This is the country a person belongs to.
+
+<End of help on this topic>
+======= h_composer_qserv_state =======
+
+This is the state a person belongs to.
+
+<End of help on this topic>
+======= h_composer_qserv_locality =======
+
+This is the locality a person belongs to.
+
+<End of help on this topic>
+======= h_composer_qserv_custom =======
+
+This one is for advanced users only! If you put something in this field,
+then the rest of the fields are ignored.
+
+This field may be set to the string representation of an LDAP search
+filter (see RFC1960). Here are some examples:
+
+To search for an entry with a surname equal to "clinton" you could set
+the custom filter to:
+
+ (sn=clinton)
+
+This is equivalent to putting "clinton" in the SurName field.
+To search for an entry that has a surname that begins with "clint" and
+has a givenname equal to "william" you could use:
+
+ (&(sn=clint*)(givenname=william))
+
+This is equivalent to setting the SurName field to "clint*" and the
+GivenName field to "william".
+To search for an entry where either the common name OR the email address
+contains "abcde" you could use:
+
+ (|(cn=*abcde*)(mail=*abcde*))
+
+That isn't equivalent to anything you can do by setting the other fields
+because of the OR.
+
+<End of help on this topic>
+======= h_composer_qserv_qq =======
+
+This one is a little different from the rest of the categories. It causes
+a search to be formed from the configured search filter that you filled
+in when you added the directory server to your configuration. It can also
+be combined with the other fields if you'd like.
+
+<End of help on this topic>
+======= h_address_format =======
+<HTML>
+<HEAD>
+<TITLE>INTERNET EMAIL ADDRESS FORMAT</TITLE>
+</HEAD>
+<BODY>
+<H1>INTERNET EMAIL ADDRESS FORMAT</H1>
+
+A valid email address on the Internet has a username, an &quot;@&quot; sign,
+and then a domain, with no spaces.
+For example, jsmith@art.example.com might be the email address
+of a person
+with the username &quot;jsmith&quot; who has an account in the domain
+&quot;art.example.com&quot;. The number of dot-separated segments on the
+right of the &quot;@&quot; sign can vary - a shorter example would be
+isabelle@elsewhere.edu (the shortest possible form: here, only the
+organization's domain is specified after the &quot;@&quot; sign); a longer
+example would be
+ jsingh@shakti.edutech.example.com
+(here, the name of the host &quot;shakti&quot; in the domain
+edutech.example.com is also specified).
+<P>
+If you do not know the exact email address of someone you want to write
+to, ask them what it is using other means of communication than email; or
+use the tools for
+finding people's addresses that are available on the Internet.
+<P>
+If you are sending to someone on the same system as you are, you can leave
+the &quot;@&quot; sign and all the information to its right off of the
+address, and Alpine will fill it in automatically,
+unless the feature
+<A HREF="h_config_compose_rejects_unqual">&quot;<!--#echo var="FEAT_compose-rejects-unqualified-addrs"-->&quot;</A> is set in SETUP CONFIGURATION.
+<P>
+
+When an email address you send a message to is not reachable -- either because
+it is simply an incorrect address, or because email can temporarily not be
+delivered to it due to a technical problem on the way to or at the recipient's
+end -- you will almost always get an error notification email message back.
+<P>
+If you encounter problems with, or have questions about, email delivery or
+email address syntax, contact your local network computing consultants.
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_flag_user_flag =======
+<HTML>
+<HEAD>
+<TITLE>STATUS FLAG: User Defined Keyword</TITLE>
+</HEAD>
+<BODY>
+<H1>STATUS FLAG: User Defined Keyword</h1>
+
+This is a keyword that is defined for this folder.
+It was most likely defined by the owner of the folder.
+Alpine will not set or clear this flag on its own.
+
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_flag_important =======
+<html>
+<title>STATUS FLAG: Important</title>
+<body>
+<h1>STATUS FLAG: Important</h1>
+
+
+The <EM>Important</EM> flag, indicated by an asterisk in Alpine's
+MESSAGE&nbsp;INDEX
+screen, can only be set by the user, and is intended to be used in
+whatever fashion makes sense to you. You are the only one that can set or
+clear it.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_flag_new =======
+<html>
+<title>STATUS FLAG: New</title>
+<body>
+<h1>STATUS FLAG: New</h1>
+
+
+The <EM>New</EM> flag, indicated by the letter 'N' in Alpine's
+MESSAGE&nbsp;INDEX screen,
+is automatically set when messages are delivered to your Inbox (or other
+folder specified outside of Alpine). Likewise, it is cleared automatically
+the first time you read the message it is associated with.
+
+<P>
+Sometimes it's helpful in prioritizing your mail. For example, perhaps
+a message isn't weighty enough to assign it an <A HREF="h_flag_important">Important</A> flag, but
+you'd like to be reminded of it next time you read mail. This can be done
+easily by <A HREF="h_common_flag">explicitly</A> resetting the <EM>New</EM> flag.
+
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_flag_answered =======
+<html>
+<title>STATUS FLAG: Answered</title>
+<body>
+<h1>STATUS FLAG: Answered</h1>
+
+The <EM>Answered</EM> flag, indicated by the letter 'A' in Alpine's
+MESSAGE&nbsp;INDEX
+screen, is automatically set when you reply to a message. This flag is not
+automatically cleared.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_flag_forwarded =======
+<html>
+<title>STATUS FLAG: Forwarded</title>
+<body>
+<h1>STATUS FLAG: Forwarded</h1>
+
+The <EM>Forwarded</EM> flag, indicated by the letter 'F' in Alpine's
+MESSAGE&nbsp;INDEX
+screen, is automatically set when you forward a message. This flag is not
+automatically cleared.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_flag_deleted =======
+<html>
+<title>STATUS FLAG: Deleted</title>
+<body>
+<h1>STATUS FLAG: Deleted</h1>
+
+The <EM>Deleted</EM> flag, indicated by the letter 'D' in Alpine's
+MESSAGE&nbsp;INDEX
+screen, is set when you use the &quot;"D&nbsp;Delete&quot; command.
+It is cleared
+when you use the &quot;U&nbsp;Undelete&quot; command.
+
+<P>
+Messages marked with this flag will be permanently removed from
+the folder when you issue the <A HREF="h_index_cmd_expunge">Expunge</A>
+command, or
+when you indicate acceptance of their removal upon leaving the folder.
+
+<P>
+Note, there can be other actions implicit in the
+&quot;D&nbsp;Delete&quot; command,
+such as advancing to the next message, that may be momentarily undesirable.
+For this reason, it's sometimes useful to set or clear the <EM>Deleted</EM>
+flag <A HREF="h_common_flag">explicitly</A>.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_incoming_timeo ======
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_incoming-check-timeout"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_incoming-check-timeout"--></H1>
+
+This option has no effect unless the feature
+<A HREF="h_config_enable_incoming_checking"><!--#echo var="FEAT_enable-incoming-folders-checking"--></A>
+is set, which in turn has no effect unless
+<A HREF="h_config_enable_incoming">&quot;<!--#echo var="VAR_incoming-folders"-->&quot;</A>
+is set.
+<P>
+Sets the time in seconds that Alpine will
+attempt to open a network connection used for monitoring for Unseen
+messages in Incoming Folders. The default is 5.
+If a connection has not completed within this many seconds Alpine will
+give up and consider it a failed connection.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_incoming_interv ======
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_incoming-check-interval"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_incoming-check-interval"--></H1>
+
+This option has no effect unless the feature
+<A HREF="h_config_enable_incoming_checking"><!--#echo var="FEAT_enable-incoming-folders-checking"--></A>
+is set, which in turn has no effect unless
+<A HREF="h_config_enable_incoming">&quot;<!--#echo var="VAR_incoming-folders"-->&quot;</A>
+is set.
+<P>
+This option specifies, in seconds, how often Alpine will check
+for new mail and state changes in Incoming Folders when Incoming Folders
+Checking is turned on.
+The default is 3 minutes (180).
+This value applies only to folders that are local to the system that
+Alpine is running on or that are accessed using the IMAP protocol.
+The similar option
+<A HREF="h_config_incoming_second_interv"><!--#echo var="VAR_incoming-check-interval-secondary"--></A>
+applies to all other monitored folders.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_incoming_second_interv ======
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_incoming-check-interval-secondary"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_incoming-check-interval-secondary"--></H1>
+
+This option has no effect unless the feature
+<A HREF="h_config_enable_incoming_checking"><!--#echo var="FEAT_enable-incoming-folders-checking"--></A>
+is set, which in turn has no effect unless
+<A HREF="h_config_enable_incoming">&quot;<!--#echo var="VAR_incoming-folders"-->&quot;</A>
+is set.
+<P>
+This option together with the option
+<A HREF="h_config_incoming_interv"><!--#echo var="VAR_incoming-check-interval"--></A>
+specifies, in seconds, how often Alpine will check
+for new mail and state changes in Incoming Folders when Incoming Folders
+Checking is turned on.
+The default for this option is 3 minutes (180).
+For folders that are local to this system or
+that are accessed using the IMAP protocol
+the value of the option
+<A HREF="h_config_incoming_interv"><!--#echo var="VAR_incoming-check-interval"--></A>
+is used.
+For all other monitored folders, the value of this option is used.
+<P>
+The reason there are two separate options is because it is usually
+less expensive to check local and IMAP folders than it is to check
+other types, like POP or NNTP folders.
+You may want to set this secondary value to a higher number than
+the primary check interval.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_incoming_list ======
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_incoming-check-list"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_incoming-check-list"--></H1>
+
+This option has no effect unless the feature
+<A HREF="h_config_enable_incoming_checking"><!--#echo var="FEAT_enable-incoming-folders-checking"--></A>
+is set, which in turn has no effect unless
+<A HREF="h_config_enable_incoming">&quot;<!--#echo var="VAR_incoming-folders"-->&quot;</A>
+is set.
+<P>
+When monitoring the Incoming Message Folders for Unseen messages Alpine will
+normally monitor all Incoming Folders.
+You may use this option to restrict the list of monitored folders to a
+subset of all Incoming Folders.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_pers_name ======
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_personal-name"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_personal-name"--></H1>
+
+This value is used to determine the full name part of the "From" address
+on messages you send.
+<!--chtml if pinemode="os_windows"-->
+ PC-Alpine requires that this be set in order to properly construct the "From" address.
+<!--chtml else-->
+ If unset, Unix Alpine will obtain your full name from
+ the system password file. PC-Alpine, on the other hand, requires that this be set.
+<!--chtml endif-->
+<P>
+If you want to change the value of what gets included in the From header
+in messages you send (other than just the Personal Name)
+look <A HREF="h_config_change_your_from">here</A> for a description.
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_pruned_folders ======
+<html>
+<header>
+<title>OPTION: <!--#echo var="VAR_pruned-folders"--></title>
+</header>
+<body>
+<h1>OPTION: <!--#echo var="VAR_pruned-folders"--></h1>
+
+This variable allows you to define a list of one or more folders that
+Alpine will offer to prune for you in the same way it automatically offers
+to prune your "sent-mail" folder each month.
+Each folder in this list must be a folder in your default folder collection
+(the first folder collection if you have more than one), and it is just
+the relative name of the folder in the collection, not the fully-qualified name.
+It is similar to sent-mail.
+Instead of something like
+<P>
+<CENTER><SAMP><!--#echo var="VAR_pruned-folders"-->={servername}mail/folder</SAMP></CENTER>
+<P>
+the correct value to use would be
+<P>
+<CENTER><SAMP>folder</SAMP></CENTER>
+<P>
+There is an assumption here that your first collection is the folders in
+<P>
+<CENTER><SAMP>{servername}mail</SAMP></CENTER>
+<P>
+
+Once a month, for each folder listed, Alpine will offer to move
+the contents of the folder to a new folder of the same name but with
+the previous month's date appended. Alpine will then look for any such
+date-appended folder names created for a previous month, and offer each
+one it finds for deletion.
+<P>
+
+If you decline the first offer, no mail is moved and no new folder is
+created.
+<P>
+
+The new folders will be created
+in your default folder collection.
+<P>
+
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</body>
+</html>
+====== h_config_upload_cmd ======
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_upload-command"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_upload-command"--></H1>
+
+This option affects the behavior of the Composer's &quot;Read File&quot;
+(^R in the message body) and &quot;Attach File&quot; (^J in the header)
+commands. It specifies
+a Unix program name, and any necessary command line arguments, that Alpine can
+use to transfer files from your personal computer into messages that you are
+composing.<P>
+
+<B>Note:</B> this facility is intended for use with serial line transfer
+protocols, such as kermit, xmodem, or zmodem. It is <B>not</B> intended
+to work with TCP/IP file transfer programs such as ftp.<P>
+
+If a program is specified, the commands listed above are modified to offer a
+subcommand (^Y) to activate the transfer. Obviously, the Unix program
+specified here must match the transfer program or protocol available on the
+personal computer.<P>
+
+Alpine expects to exchange uploaded data via a file on your Unix system. When
+the specified upload program finishes, Alpine expects the uploaded data to be
+contained in this file.<P>
+
+When upload is invoked via the &quot;Read File&quot; subcommand, Alpine
+generates a
+temporary file name that it will pass to the specified Unix program. Alpine
+will read the resulting uploaded text from this file and then delete it when
+the upload command is finished.<P>
+
+When upload is invoked via the &quot;Attach File&quot; subcommand, Alpine will
+prompt
+you for the name of the file that is to contain the uploaded information that
+it is to attach. Alpine will attach this file to the composition, but will
+<B>not</B> delete this file after the upload command is finished.<P>
+
+The special token &quot;_FILE_&quot; may be included among the Unix program's
+command
+line arguments. Alpine will replace this symbol with the name of the file
+being used to exchange the uploaded information. This token allows you to
+position the file name where it is required in the Unix program's command
+line arguments.<P>
+
+If the &quot;_FILE_&quot; token is not present in the specified command, the
+temporary file's name is automatically appended to the specified Unix
+program. In other words, you don't need to use &quot;_FILE_&quot; if it is the
+<B>last</B> command line argument.
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_upload_prefix ======
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_upload-command-prefix"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_upload-command-prefix"--></H1>
+
+This option is used in conjunction with the <!--#echo var="VAR_upload-command"--> option.
+It defines text to be written to the terminal emulator (via standard output)
+immediately prior to starting upload command. This is useful for
+integrated serial line file transfer agents that permit command passing
+(e.g., Kermit's APC method).<P>
+
+The special token &quot;_FILE_&quot; may be included in the string specification.
+That symbol will be replaced with the (Alpine-created) name of the temporary
+file in which Alpine will expect to find the uploaded file.
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_download_cmd ======
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_download-command"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_download-command"--></H1>
+
+This option affects the behavior of the Export command. It specifies a Unix
+program name, and any necessary command line arguments, that Alpine can use to
+transfer the exported message to your personal computer's disk.<P>
+Note: this facility is intended for use with serial line transfer
+protocols, such as kermit, xmodem, or zmodem. It is <B>not</B> intended
+to work with TCP/IP file transfer programs such as ftp.<P>
+If a program is specified, the Export command is modified to offer a
+subcommand (^V) to activate the transfer (in lieu of saving it to
+ the machine where Alpine is running). Obviously, the Unix program
+specified here must match the transfer program or protocol available on the
+personal computer.<P>
+
+When this subcommand is selected and before Alpine invokes the specified Unix
+program, Alpine will create a temporary file containing the text of the
+exported message. Alpine uses this file to pass the exported message text to
+the specified Unix program.<P>
+
+The special token &quot;_FILE_&quot; may be included among the Unix program's command
+line arguments. Alpine will replace this symbol with the temporary file's name
+before executing the Unix program. This token allows you to position the
+file name where it is required in the Unix program's command line arguments.
+<P>
+If the &quot;_FILE_&quot; token is not present in the specified command, the
+temporary file's name is automatically appended to the specified Unix
+program. In other words, you don't need to use &quot;_FILE_&quot; if it is the
+<B>last</B> command line argument.
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_download_prefix ======
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_download-command-prefix"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_download-command-prefix"--></H1>
+
+This option is used in conjunction with the <!--#echo var="VAR_download-command"--> option.
+It defines text to be written to the terminal emulator (via standard output)
+immediately prior to starting the download command. This is useful for
+integrated serial line file transfer agents that permit command passing
+(e.g., Kermit's APC method).
+<P>
+The special token &quot;_FILE_&quot; may be included in the string
+specification.
+That symbol will be replaced with the (Alpine-created) name of the temporary
+file into which Alpine will place the message to be downloaded.
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_mailcap_path ======
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_mailcap-search-path"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_mailcap-search-path"--></H1>
+This variable is used to replace Alpine's default mailcap file search path.
+It takes one or more file names (full paths must be specified) in which to
+look for mail capability data. The default search path can be found in this
+<A HREF="h_news_config">Alpine Configuration</A> help, near the bottom.
+If there is more than one file name listed, list members should be delimited
+by
+<!--chtml if pinemode="os_windows"-->
+a semi-colon (;) under Windows; for example:<PRE>
+ C:&#92;MYCONFIG&#92;MAILCAP.TXT;H:&#92;NETCONFIG&#92;MAILCAP.TXT
+</PRE>
+<!--chtml else-->
+a colon (:) under UNIX; for example:<PRE>
+ ~/.mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap
+</PRE>
+<!--chtml endif-->
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_mimetype_path ======
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_mimetype-search-path"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_mimetype-search-path"--></H1>
+
+This variable is used to replace Alpine's default mime.types file search path.
+It takes one or more file names (full paths must be specified) in which to
+look for file-name-extension to MIME type mapping data. The default search
+path can be found in this
+<A HREF="h_news_config">Alpine Configuration</A> help.
+
+<P>
+
+If there is more than one file name listed, list members should be delimited
+by a colon (:) under UNIX and a semi-colon (;) under Windows.
+<P>
+&lt;End of help on this topic&gt;
+</BODY></HTML>
+====== h_config_set_att_ansi ======
+<HTML><HEAD>
+<TITLE>OPTION: Set printer to attached ansi printer</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Set printer to attached ansi printer</H1>
+
+Type "S" to set your printer to "attached-to-ansi".<BR>
+It is OK to include "attached-to-ansi" in your personal list below.
+<P>
+&lt;End of help on this topic&gt;
+</BODY></HTML>
+====== h_config_set_att_ansi2 ======
+<HTML><HEAD>
+<TITLE>OPTION: Set printer to attached ansi printer (no formfeed)</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Set printer to attached ansi printer (no formfeed)</H1>
+
+Type "S" to set your printer to "attached-to-ansi-no-formfeed".<BR>
+It is OK to include "attached-to-ansi-no-formfeed" in your personal
+list below.
+
+<P>
+
+This is the same as the "attached-to-ansi" option except that a
+formfeed character will not be appended to the end of the print job.
+If your printer already ejects the paper by itself at the end of the
+job, you may prefer the "no-formfeed" form of this printer so that you
+don't get an extra blank page between print jobs.
+<P>
+
+&lt;End of help on this topic&gt;
+</BODY></HTML>
+====== h_config_set_att_wyse ======
+<HTML><HEAD>
+<TITLE>OPTION: Set printer to attached Wyse60 printer</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Set printer to attached Wyse60 printer</H1>
+
+Type "S" to set your printer to "attached-to-wyse".<BR>
+It is OK to include "attached-to-wyse" in your personal list below.
+<P>
+This is very similar to "attached-to-ansi".
+The only difference is in the control characters sent to turn the printer
+on and off.
+The ansi version of the printer uses ESC LEFT_BRACKET 5 i
+to turn on the printer and ESC LEFT_BRACKET 4 i
+to turn it off.
+The Wyse version uses Ctrl-R for on, and Ctrl-T for off.
+&lt;End of help on this topic&gt;
+</BODY></HTML>
+====== h_config_set_att_wyse2 ======
+<HTML><HEAD>
+<TITLE>OPTION: Set printer to attached Wyse60 printer (no formfeed)</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Set printer to attached Wyse60 printer (no formfeed)</H1>
+
+Type "S" to set your printer to "attached-to-wyse-no-formfeed".<BR>
+It is OK to include "attached-to-wyse-no-formfeed" in your personal
+list below.
+
+<P>
+
+This is the same as the "attached-to-wyse" option except that a
+formfeed character will not be appended to the end of the print job.
+If your printer already ejects the paper by itself at the end of the
+job, you may prefer the "no-formfeed" form of this printer so that you
+don't get an extra blank page between print jobs.
+<P>
+
+&lt;End of help on this topic&gt;
+</BODY></HTML>
+====== h_config_set_stand_print ======
+<HTML>
+<HEAD>
+<TITLE>OPTION: Set default printer</TITLE>
+</HEAD>
+<H1>OPTION: Set default printer</H1>
+<BODY>
+Move to the printer you want and type "S" to set it to be your
+default printer. This list is not modifiable by you and has been
+set up by the system administrators. If there is more than one printer
+listed in the Command List, you will be able to cycle through that
+whole list at the time you print, starting with your default.
+It is OK to include entries from this Standard list in your personal
+list below.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_set_custom_print ======
+<HTML>
+<HEAD>
+<TITLE>OPTION: Set default printer</TITLE>
+</HEAD>
+<H1>OPTION: Set default printer</H1>
+<BODY>
+You may add as many print commands as you want to your personal list.
+Specify one of them as your default printer by moving to the printer
+you want and typing "S". If there is more than one printer listed
+in the Command List, you will be able to cycle through that list at
+the time you print, starting with your default. It is OK to include
+entries from the Standard list above or to include the command
+"attached-to-ansi", "attached-to-ansi-no-formfeed", "attached-to-wyse", or
+"attached-to-wyse-no-formfeed" as one of the entries here.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_user_id =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_user-id"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_user-id"--></H1>
+
+This value is used as part of the "From" address on messages you send.
+It is also the default login name for remote IMAP server access. Set this
+to the username part you want to appear on outgoing email.
+<P>
+If you want to change the value of what gets included in the From header
+in messages you send (other than just the User ID)
+look <A HREF="h_config_change_your_from">here</A> for a description.
+
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_user_dom =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_user-domain"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_user-domain"--></H1>
+
+This value specifies the domain part (right-hand side) of your return
+address on outgoing email and is also used as the default domain for email
+composed to a local user.
+<!--chtml if pinemode="os_windows"-->
+ This value is required for PC-Alpine. If you are unsure as to what this should be,
+ contact your local help desk, system administrator, or Internet Service Provider.
+<!--chtml else-->
+ If unset, Unix Alpine will obtain the domain from
+ the system. Often this value will be set for your whole site by the
+ system administrator.<P>
+<!--chtml endif-->
+If you set this, see also the <A HREF="h_config_quell_local_lookup">
+&quot;<!--#echo var="FEAT_quell-user-lookup-in-passwd-file"-->&quot;</A> feature.
+<P>
+If you want to change the value of what gets included in the From header
+in messages you send (other than just the User Domain)
+look <A HREF="h_config_change_your_from">here</A> for a description.
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_smtp_server =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_smtp-server"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_smtp-server"--></H1>
+This value specifies the name of one or more SMTP
+(Simple Mail Transfer Protocol) servers for sending mail.
+<!--chtml if pinemode="os_windows"-->
+You must have an SMTP server for use with PC-Alpine.
+SMTP servers are
+normally set up by a system administrator for use by all members of a given
+campus or department.
+Contact your local help desk to ask what SMTP
+servers you should use.
+<!--chtml else-->
+Unix Alpine users may not need to set an SMTP server.
+Alpine will attempt to execute the program (usually sendmail) that is used
+to insert mail into the mail system.
+If this works for you, you may leave this option blank.
+If there is an SMTP server running on the Unix host you may be able to
+improve sending performance slightly by setting the SMTP server option
+to &quot;localhost&quot; or to the actual name of the Unix host.
+<P>
+If the Unix host doesn't work the way Alpine was expecting you will need to
+set the value of this option.
+SMTP servers are
+normally set up by a system administrator for use by all members of a given
+campus or department.
+Contact your local help desk to ask what SMTP
+servers you should use.
+<!--chtml endif-->
+<P>
+Your SMTP server may offer SMTP AUTH authentication.
+It may even require it.
+If your SMTP server offers SMTP AUTH authentication you may specify a
+&quot;user&quot; name parameter to cause Alpine to attempt to authenticate.
+This parameter requires an associated value,
+the username identifier with which to establish the server
+connection.
+An example might be:
+
+<P>
+<CENTER><SAMP>smtpserver.example.com/user=katie</SAMP></CENTER>
+<P>
+
+If AUTH authentication is offered by the server, this will cause Alpine to
+attempt to use it.
+If AUTH authentication is not offered by the server, this will cause Alpine
+to fail sending with an error similar to:
+
+<P>
+<CENTER><SAMP>Error: SMTP authentication not available</SAMP></CENTER>
+<P>
+
+Another type of authentication that is used by some ISPs is called
+&quot;POP before SMTP&quot; or &quot;IMAP before SMTP&quot;,
+which means that you have to authenticate
+yourself to the POP or IMAP server by opening a mailbox before you
+can send mail.
+To do this, you usually only have to open your INBOX.
+
+<P>
+You may tell Alpine to use the
+<A HREF="http://www.ietf.org/rfc/rfc2476.txt">Message Submission</A>
+port (587) instead of the SMTP port (25) by including the &quot;submit&quot;
+parameter
+in this option.
+At this time &quot;/submit&quot; is simply equivalent to specifying
+port 587, though it may imply more than that at some point in the future.
+Some ISPs are blocking port 25 in order to reduce the amount of spam
+being sent to their users.
+You may find that the submit option allows you to get around such a block.
+
+<P>
+<CENTER><SAMP>smtpserver.example.com/submit</SAMP></CENTER>
+<P>
+
+To specify any non-standard port number on the SMTP server you may follow
+the hostname with a colon followed by the portnumber.
+
+<P>
+<CENTER><SAMP>smtpserver.example.com:12345</SAMP></CENTER>
+<P>
+
+Normally, when a connection is made to the Smtp-Server Alpine will attempt
+to negotiate a secure (encrypted) session using Transport Layer Security (TLS).
+If that fails then a non-encrypted connection will be attempted instead.
+You may specify that a TLS connection is required if you wish.
+If you append &quot;/tls&quot; to the name then the connection will fail
+instead of falling back to a non-secure connection.
+
+<P>
+<CENTER><SAMP>smtpserver.example.com/tls</SAMP></CENTER>
+<P>
+
+
+For more details about server name possibilities see
+<A HREF="h_folder_server_syntax">Server Name Syntax</A>.
+<P>
+
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_nntp_server =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_nntp-server"--></TITLE></HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_nntp-server"--></H1>
+
+This value specifies the name of one or more NNTP
+(Network News Transfer Protocol)
+servers for reading and posting USENET news.
+NNTP servers are normally
+set up by a system administrator for use by all members of a given campus
+or department.
+Contact your local help desk to ask what NNTP servers you should use.
+<!--chtml if pinemode="os_windows"--><!--chtml else-->
+Often Unix Alpine users will find that this variable has been
+set for the whole system (and they don't have to worry about it).
+<!--chtml endif-->
+When you define an NNTP server here, Alpine implicitly defines a news
+collection for you, assuming that server as the news server and assuming
+that you will use the NNTP protocol and a local newsrc configuration file
+for reading news.
+For more about reading news with Alpine, see
+<A HREF="h_reading_news">how to use Alpine to read news</A>.
+<P>
+Your NNTP server may offer NNTP &quot;AUTHINFO SASL&quot;
+or &quot;AUTHINFO USER&quot; authentication.
+It may even require it.
+If your NNTP server does offer such authentication you may specify a user name
+parameter to cause Alpine to attempt to authenticate.
+The same is true for the server name in a folder collection that uses NNTP.
+This parameter requires an associated value,
+the username identifier with which to establish the server connection.
+An example might be:
+
+<P>
+<CENTER><SAMP>nntpserver.example.com/user=katie</SAMP></CENTER>
+<P>
+
+If authentication is offered by the server, this will cause Alpine to
+attempt to use it.
+If authentication is not offered by the server, this will cause Alpine
+to fail with an error similar to:
+
+<P>
+<CENTER><SAMP>Error: NNTP authentication not available</SAMP></CENTER>
+<P>
+For more details about the server name possibilities see
+<A HREF="h_folder_server_syntax">Server Name Syntax</A>.
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_inbox_path =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_inbox-path"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_inbox-path"--></H1>
+
+This value overrides the default value of your INBOX name/path/location.
+<!--chtml if pinemode="os_windows"-->
+PC-Alpine users must specify an inbox path and it must be a folder on an
+IMAP server.
+<!--chtml else-->
+Unix and VMS Alpine users will often find that this variable
+has been pre-configured by your system administrator.
+<!--chtml endif-->
+You may be able to specify an alternate INBOX that is either a local folder
+or a folder on an IMAP server.
+<P>
+A typical remote <!--#echo var="VAR_inbox-path"--> entry would be: &#123;monet.art.example.com}INBOX
+where &quot;monet.art.example.com&quot; is replaced by the name of your IMAP
+mail server.
+<P>
+See the section on <A HREF="h_valid_folder_names">Valid Folder Names</A> for
+details on the syntax of folder definitions.
+<P>
+See <A HREF="h_info_on_mbox">Missing mail and the mbox driver</A> if your
+mail is disappearing.
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_change_your_from =====
+<HTML>
+<HEAD>
+<TITLE>How to Change your From Address</TITLE>
+</HEAD>
+<BODY>
+<H1>How to Change your From Address</H1>
+
+If the From address that Alpine includes in mail that you send is not correct,
+you may want to configure a different default value for the From address.
+You may follow these directions to change the default:
+
+<P>
+<UL>
+ <LI> Go to the Main Alpine Menu
+ <LI> From there type the Setup Command
+ <LI> From there type the Config Command
+</UL>
+
+<P>
+You've probably already seen this SETUP CONFIGURATION screen.
+If not, there are many options you may want to set here.
+To set the value of the From header you may use the
+<A href="h_config_custom_hdrs"><!--#echo var="VAR_customized-hdrs"--></A> option.
+Find it by scrolling down a few pages or use the WhereIs command to
+search for &quot;customized&quot;.
+You may want to read the help text associated with the option.
+<P>
+To add a custom From header, type the Add command and enter the
+full header line, including the leading &quot;From:&nbsp;&quot;.
+For example:
+<P>
+<CENTER><SAMP>From: Full Name &lt;user@example.com&gt;</SAMP></CENTER>
+<P>
+Now exit the Setup command and try sending mail to yourself to see
+what the From line looks like.
+<P>
+When you are in the composer you may edit the custom From line by typing
+Ctrl-R while your cursor is in the headers of the message and then moving
+to the From line and editing.
+If you want to leave the default value the same but add the possibility
+of being able to edit the header when you compose, add just the header
+name without a value.
+For example:
+<P>
+<CENTER><SAMP>From:</SAMP></CENTER>
+<P>
+If you change your From address you may also find it useful to add the
+changed From address to the
+<a href="h_config_alt_addresses"><!--#echo var="VAR_alt-addresses"--></a>
+configuration option.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_default_fcc =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_default-fcc"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_default-fcc"--></H1>
+
+This value specifies where a copy of outgoing mail should be saved. If
+this is not a path name, it will be in the default collection for saves.
+Any valid folder specification, local or IMAP, is allowed. This default
+folder carbon copy only applies when the
+<A HREF="h_config_fcc_rule">&quot;<!--#echo var="VAR_fcc-name-rule"-->&quot;</A>
+is set to use the default folder.
+<!--chtml if pinemode="os_windows"-->
+PC-Alpine default is &quot;SENTMAIL&quot; (normally stored as SENTMAIL.MTX)
+<!--chtml else-->
+Unix Alpine default
+is normally &quot;sent-mail&quot; in the default folder collection.
+<!--chtml endif-->
+<P>
+If you access your email through an IMAP server, especially if you often switch between Unix Alpine
+and PC-Alpine, or between various desktop email systems, you may want to set this to a folder on your
+IMAP server (remember that in order to later access this remote folder through Alpine, it
+must be in a folder collection. See <A HREF="h_what_are_collections">Folder Collections Explained</a>
+for more information). An example:<p>
+<CENTER><SAMP>{monet.art.example.com}mail/sent-mail</SAMP></CENTER>
+<P>
+To suppress saving of outgoing mail, set: <!--#echo var="VAR_default-fcc"-->=&quot;&quot;
+<P>
+See the section on <A HREF="h_valid_folder_names">Valid Folder Names</A> for
+details on the syntax of folder definitions.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_def_save_folder =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_default-saved-msg-folder"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_default-saved-msg-folder"--></H1>
+
+This option determines the default folder name for save-message operations
+(&quot;saves&quot;).
+<P>
+If this is not a path name, it will be in the default collection for saves.
+Any valid folder specification, local or IMAP, is allowed. This default
+folder only applies when the
+<A HREF="h_config_saved_msg_name_rule">&quot;<!--#echo var="VAR_saved-msg-name-rule"-->&quot;</A>
+doesn't override it.
+<!--chtml if pinemode="os_windows"-->
+PC-Alpine default is &quot;SAVEMAIL&quot; (normally stored as SAVEMAIL.MTX).
+<!--chtml else-->
+Unix Alpine default
+is normally &quot;saved-messages&quot; in the default folder collection.
+<!--chtml endif-->
+If you access your email through an IMAP server, especially if you often switch between Unix
+and PC-Alpine, or between various desktop email systems, you may want to set this to a folder on an
+IMAP server (remember that in order to later access this remote folder through Alpine, it
+should be in a folder collection. See <A HREF="h_what_are_collections">Folder Collections Explained</a>
+for more information). An example:<p>
+<CENTER><SAMP>{monet.art.example.com}mail/saved-messages</SAMP></CENTER>
+<P>
+See the section on <A HREF="h_valid_folder_names">Valid Folder Names</A> for
+details on the syntax of folder definitions.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_postponed_folder =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_postponed-folder"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_postponed-folder"--></H1>
+
+This value overrides the default name for the folder where postponed
+messages are saved. If this is not a path name, it will be in the default
+collection for message Saves. Any valid folder specification, local or
+remote, is allowed.
+PC-Alpine default
+is &quot;POSTPOND&quot; (stored as POSTPOND.MTX).
+The Unix Alpine default is normally &quot;postponed-msgs&quot;
+in the default collection.
+<P>
+Tip: If you are using different installations of (PC-)Alpine -- for example, PC-Alpine on your personal
+computer at home, and Unix Alpine on campus -- you can postpone a composition begun with one Alpine and
+resume it later with the other if you set this option to the <B>same folder on the same IMAP host</B>
+in all Alpine copies you use.
+(Remember that in order to later access this remote folder through Alpine, it must be in a folder
+collection. See <A HREF="h_what_are_collections">Folder Collections Extensions Explained</a>
+for more information). An
+example:<p>
+<CENTER><SAMP>{monet.art.example.com}mail/postponed-msgs</SAMP></CENTER>
+<P>
+See the section on <A HREF="h_valid_folder_names">Valid Folder Names</A> for
+details on the syntax of folder definitions.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_read_message_folder =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_read-message-folder"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_read-message-folder"--></H1>
+
+By virtue of specifying a folder name here, Alpine will be configured to
+save all messages that you have read during a session into the designated
+&quot;read messages&quot; folder. This allows you to more easily distinguish
+between your really new email (in your INBOX) and those that you have
+already read. Depending on how you define the
+<A HREF="h_config_auto_read_msgs">&quot;auto-move-read-messages&quot;</A>
+setting, you may or may not be asked when you quit
+Alpine if you want read messages to be moved to this folder. In either
+case, moving the messages means they will be deleted from your INBOX.
+<P>
+If this is not a path name, it will be in the default collection for
+saves. Any valid folder specification, local or remote (via IMAP), is
+allowed. There is no default for the name of the read message folder.
+<P>
+See the section on <A HREF="h_valid_folder_names">Valid Folder Names</A> for
+details on the syntax of folder definitions.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_form_folder =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_form-letter-folder"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_form-letter-folder"--></H1>
+
+A &quot;<!--#echo var="VAR_form-letter-folder"-->&quot; is a mail folder that is intended to
+contain messages that you have composed and that are intended to be
+sent in their original form repeatedly.
+
+<P>
+Setting this variable will alter Alpine's usual behavior when you
+execute the Compose command. Normally, Alpine offers a chance to
+continue a postponed or interrupted message should one or the other
+exist. When this variable is set to a folder name that exists, Alpine
+will also offer the chance to select a message from the folder to
+insert into the composer (much like when continuing a postponed message).
+The difference, however, is that Alpine will not automatically delete
+the selected message from the Form Letter Folder.
+<P>
+Setting this variable will also affect Alpine's behavior when you
+Postpone a message from the composer. Normally, Alpine simply stashes
+the message away in your
+&quot;<A HREF="h_config_postponed_folder"><!--#echo var="VAR_postponed-folder"--></A>&quot;.
+Regardless of the specified folder's existence, Alpine will ask which
+folder you intend the message to be stored in. Choose the
+&quot;F&quot; option to store the message in your Form Letter Folder.
+This is the most common way to add a message to the folder.
+
+<P>
+Another method of adding messages to the folder is via the Alpine
+composer's <SAMP>Fcc:</SAMP> field. If you are sending a message that
+you expect to send in the same form again, you can enter the Form
+Letter Folder's name in this field. Alpine, as usual, will copy the
+message as it's sent. Note, when you later select this message from
+your Form Letter Folder, it will have the same recipients as the original
+message.
+
+<P>
+To delete a message from the Form Letter Folder, you can either select
+the folder from a suitable FOLDER LIST screen, or use the Delete
+command in the MESSAGE INDEX offered when selecting from the folder as
+part of the Compose command. You can delete a Form Letter Folder just
+as any other folder from a suitable FOLDER LIST screen.
+
+<P>
+You may find that the <A HREF="h_rules_roles">&quot;Roles&quot;</A>
+facility can be used
+to replace the Form Letter Folder.
+
+<P>
+See the section on <A HREF="h_valid_folder_names">Valid Folder Names</A> for
+details on the syntax of folder definitions.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_archived_folders =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_incoming-archive-folders"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_incoming-archive-folders"--></H1>
+
+This is like
+<A HREF="h_config_read_message_folder"><!--#echo var="VAR_read-message-folder"--></A>,
+only more general. You may archive
+any of the folders in your incoming collection. This is a list of folder
+pairs, with the first separated from the second in the pair by a space.
+The first folder in a pair is the folder you want to archive, and the
+second folder is the folder that read messages from the first should be
+moved to. Depending on how you define the
+<A HREF="h_config_auto_read_msgs">&quot;auto-move-read-messages&quot;</A>
+setting, you may or may not be asked when you
+leave the first folder if you want read messages to be moved to the
+second folder. In either case, moving the messages means they will be
+deleted from the first folder.
+<P>
+The name of the first folder in each pair can be either the technical
+specification of the folder (like what appears in your configuration file)
+or (much easier) the nickname that you gave the folder when you made it
+an incoming folder.
+<p>
+For example:<p>
+<CENTER><SAMP>{monet.art.example.com}inbox {monet.art.example.com}mail/inbox-archive</SAMP></CENTER>
+<p>or, using nicknames:<p>
+<CENTER><SAMP>inbox inbox-archive</SAMP></CENTER>
+<P>
+If these are not path names, they will be in the default collection for
+saves. Any valid folder specification, local or remote (via IMAP), is
+allowed. There is no default.
+<P>
+See the section on <A HREF="h_valid_folder_names">Valid Folder Names</A> for
+details on the syntax of folder definitions.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_newsrc_path ======
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_newsrc-path"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_newsrc-path"--></H1>
+
+This option overrides the default name Alpine uses for your "newsrc" news
+status and subscription file. If set, Alpine will take this value as the
+full pathname for the desired newsrc file.<P>
+
+If this option is <B>not</B> set,
+<!--chtml if pinemode="os_windows"-->
+PC-Alpine looks first for $HOME&#92;NEWSRC (where $HOME defaults to the root
+of the current drive, e.g. &quot;C:&#92;&quot;) and then it looks in the same
+directory as your pinerc file for NEWSRC.
+<!--chtml else-->
+Unix Alpine looks for the file ~/.newsrc (that is, the file named .newsrc in
+your account's home directory).
+<!--chtml endif-->
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_literal_sig =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_literal-signature"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_literal-signature"--></H1>
+
+With this option your actual signature, as opposed to
+the name of a file containing your signature,
+is stored in the Alpine configuration file.
+If this is defined it takes precedence over the <!--#echo var="VAR_signature-file"--> option.
+<P>
+
+This is simply a different way to store the signature.
+The signature is stored inside your Alpine configuration file instead of in
+a separate file.
+Tokens work the same way they do with the
+<A HREF="h_config_signature_file"><!--#echo var="VAR_signature-file"--></A> so look there for
+help.
+<P>
+
+The Setup/Signature command on Alpine's MAIN MENU will edit
+the &quot;<!--#echo var="VAR_literal-signature"-->&quot; by default. However, if no
+&quot;<!--#echo var="VAR_literal-signature"-->&quot; is defined and the file named in the
+&quot;<!--#echo var="VAR_signature-file"-->&quot; option exists, then the latter will be used
+instead.
+<P>
+
+The two character sequence &#92;n (backslash followed by
+the character n) will be used to signify a line-break in your signature.
+You don't have to enter the &#92;n, but it will be visible in the
+SETUP CONFIGURATION window after you are done editing the signature.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_signature_file =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_signature-file"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_signature-file"--></H1>
+
+If a <A HREF="h_config_literal_sig"><!--#echo var="VAR_literal-signature"--></A> option is defined,
+then this &quot;<!--#echo var="VAR_signature-file"-->&quot; option will be ignored.
+You can tell that that is the case because the value of the
+&quot;<!--#echo var="VAR_signature-file"-->&quot; will show up as
+<P>
+<CENTER><SAMP>&lt;Ignored: using <!--#echo var="VAR_literal-signature"--> instead&gt;</SAMP></CENTER>
+<P>
+You may either use all Literal Signatures (signatures stored in your
+configuration file) throughout Alpine, or all signature files.
+You can't mix the two.
+<P>
+This is the name of a file that will be automatically inserted into
+outgoing messages.
+It typically contains information such as your
+name, email address and organizational affiliation.
+Alpine adds the
+signature into the message as soon as you enter the composer so you
+can choose to remove it or edit it on a message by message basis.
+Signature file placement in message replies is controlled by the
+&quot;<A HREF="h_config_sig_at_bottom"><!--#echo var="FEAT_signature-at-bottom"--></A>&quot;
+setting in the feature list.
+<P>
+
+The default file name is
+<!--chtml if pinemode="os_windows"-->
+&quot;PINE.SIG&quot; in the same directory as your PINERC file if your
+PINERC file is a local file.
+If your PINERC file is remote, then it will be in the directory specified
+by the &quot;-aux local_directory&quot; command line option.
+<!--chtml else-->
+&quot;.signature&quot;.
+<!--chtml endif-->
+<P>
+
+To create or edit your signature file choose Setup from the MAIN MENU
+and then select S for Signature (Main/Setup/Signature). This puts you
+into the Signature Editor where you can enter a <EM>few</EM> lines of
+text containing your identity and affiliation.
+
+<P>
+If the filename is followed by a vertical bar (|) then instead
+of reading the contents of the file the file is assumed to be a
+program that will produce the text to be used on its standard output.
+The program can't have any arguments and doesn't receive any input from Alpine,
+but the rest of the processing works as if the contents came from a file.
+
+<P>
+Instead of storing the data in a local file, the
+signature data may be stored remotely in an IMAP folder.
+In order to do this,
+you must use a remote name for the file.
+A remote <!--#echo var="VAR_signature-file"--> name might look like:
+<P>
+<CENTER><SAMP>{myimaphost.myschool.k12.wa.us}mail/signature</SAMP></CENTER>
+<P>
+
+The syntax used here is the same as the syntax used for remote configuration
+files from the command line.
+Note that you may not access an existing signature file remotely,
+you have to create a new <EM>folder</EM> that contains the signature data.
+If the name you use here for the signature file is a remote name, then when
+you edit the file from the Setup/Signature command the data will be stored
+remotely in the folder.
+You aren't required to do anything special to create the folder, it
+gets created automatically if you use a remote name.
+
+<P>
+Besides regular text, the signature file may also contain
+(or a signature program may produce) tokens that
+are replaced with text that usually depends on the message you are replying
+to or forwarding.
+For example, if the signature file contains the token
+<P>
+<CENTER><SAMP>_DATE_</SAMP></CENTER>
+<P>
+anywhere in the text, then that token is replaced by the date
+the message you are replying to or forwarding was sent.
+If it contains
+<P>
+<CENTER><SAMP>_CURDATE_</SAMP></CENTER>
+<P>
+that is replaced with the current date.
+The first is an example of a token that depends on the message you
+are replying to (or forwarding) and the second is an example which
+doesn't depend on anything other than the current date.
+You have to be a little careful with this facility since tokens that
+depend on the message you are replying to or forwarding will be replaced
+by nothing in the case where you are composing a new message from scratch.
+The use of <A HREF="h_rules_roles">&quot;roles&quot;</A> may help you
+in this respect.
+It allows you to use different signature files in different cases.
+<P>
+
+The list of tokens available for use in the signature file is
+<A HREF="h_index_tokens">here</A>.
+<P>
+
+Instead of, or along with the use of &quot;roles&quot; to give you
+different signature files in different situations, there is also
+a way to conditionally include text based
+on whether or not a token would result in specific replacement text.
+For example, you could include some text based on whether or not
+the _NEWS_ token would result in any newsgroups if it was used.
+This is explained in detail
+<A HREF="h_reply_token_conditionals">here</A>.
+This isn't for the faint of heart.
+<P>
+In the very unlikely event that you want to include a literal token
+in the signature you must precede it with a backslash character.
+For example,
+<P>
+<CENTER><SAMP>&#92;_DAYDATE_ = _DAYDATE_</SAMP></CENTER>
+<P>
+would produce something like
+<P>
+<CENTER><SAMP>_DAYDATE_ = Sat, 24 Oct 1998</SAMP></CENTER>
+<P>
+It is not possible to have a literal backslash followed by an expanded token.
+<P>
+An alternate method for storing the signature data is available by using the
+<A HREF="h_config_literal_sig"><!--#echo var="VAR_literal-signature"--></A> configuration option.
+This variable will be used by default.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_init_cmd_list =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_initial-keystroke-list"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_initial-keystroke-list"--></H1>
+
+The initial keystroke--or command--list option lets you start Alpine at
+any place you like.
+Whatever keystrokes you specify here will be executed
+by Alpine upon startup as a macro.
+The words SPACE, TAB, DOWN, UP, LEFT, and
+RIGHT indicate the pressing of those keys.
+CR indicates the pressing of the RETURN key.
+F1 through F12 represent the function keys, and ^ followed
+by a character indicates that key pressed along with the control key (in
+other words, ^P means Ctrl-P).
+As a shortcut notation, an element of the list may be several characters
+surrounded by double-quotes (").
+That will be expanded into the individual keystrokes
+(excluding the double-quote characters).
+For example, the quoted-string
+
+<P><CENTER>"ABC"</CENTER>
+
+<P>
+is interpreted the same as the three separate list members
+
+<P><CENTER>A and B and C</CENTER>
+
+<P>
+which is also the same as
+
+<P><CENTER>A,B,C</CENTER>
+
+<P>
+An example: To view message 1 on startup,
+you could use an <!--#echo var="VAR_initial-keystroke-list"--> equal to
+
+<P><CENTER>I,J,1,CR,V</CENTER>
+
+<P>
+An equivalent version of this is
+
+<P><CENTER>"IJ1",CR,V</CENTER>
+
+<P>
+Restrictions: You cannot pre-type into the composer with the initial
+keystroke list, and you cannot mix function key commands with letter
+commands.
+
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_comp_hdrs =====
+<html>
+<header>
+<title>OPTION: <!--#echo var="VAR_default-composer-hdrs"--></title>
+</header>
+<body>
+<h1>OPTION: <!--#echo var="VAR_default-composer-hdrs"--></h1>
+
+You can control which headers you want visible when composing outgoing
+email using this option.
+You can specify any of the regular set, any
+<A HREF="h_compose_richhdr">Rich Header</A>,
+or any <A HREF="h_config_custom_hdrs"><!--#echo var="VAR_customized-hdrs"--></A>
+that you have already defined.
+If you use this setting at all, you must specify all the
+headers you want to see, you can't just add to the regular header set.
+The default set is To:, Cc:, Attchmnt:, and Subject:.<p>
+
+Note that the "Newsgroups:" header will be abbreviated in the Composer
+display, but should be spelled out in full here.<p>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</body>
+</html>
+====== h_config_custom_hdrs =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_customized-hdrs"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_customized-hdrs"--></H1>
+
+You may add your own custom headers to outgoing messages.
+Each header you specify here must include the header tag
+(<A HREF="h_composer_reply_to">Reply-To:</A>, Approved:, etc.)
+and may optionally include a value for that header.
+If you want to see these custom headers each time you compose a message,
+you must add them to your
+<A HREF="h_config_comp_hdrs"><!--#echo var="VAR_default-composer-hdrs"--></A> list,
+otherwise they become part
+of the rich header set that you only see when you press the
+<A HREF="h_compose_richhdr">Rich Header</A>
+<!--chtml if pinemode="function_key"-->(F5)
+<!--chtml else-->(Ctrl-R)<!--chtml endif--> command.
+(If you are looking for a way to change which headers are <EM>displayed</EM>
+when you view a message, take a look at the
+<A HREF="h_config_viewer_headers"><!--#echo var="VAR_viewer-hdrs"--></A>
+option instead.)
+Here's an example that shows how you might set your From address
+<P>
+<CENTER><SAMP>From: Full Name &lt;user@example.com&gt;</SAMP></CENTER>
+<P>
+and another showing how you might set a Reply-To address
+<P>
+<CENTER><SAMP>Reply-To: user@example.com</SAMP></CENTER>
+<P>
+You may also set non-standard header values here.
+For example, you could add
+<P>
+<CENTER><SAMP>Organization: My Organization Name</SAMP></CENTER>
+<P>
+or even
+<P>
+<CENTER><SAMP>X-Favorite-Colors: Purple and Gold</SAMP></CENTER>
+<P>
+If you include a value after the colon then that header will be included
+in your outgoing messages unless you delete it before sending.
+If a header in the <!--#echo var="VAR_customized-hdrs"--> list has only a tag but no value, then
+it will not be included in outgoing messages unless you edit a value
+in manually.
+For example, if
+<P>
+<CENTER><SAMP>Reply-To:</SAMP></CENTER>
+<P>
+is in the list, then the Reply-To header will be available for editing
+but won't be included unless a value is added while in the composer.
+<P>
+It's actually a little more complicated than that.
+The values of headers that you set with the <!--#echo var="VAR_customized-hdrs"--> option are
+defaults.
+If the message you are about to compose already has a value for a header,
+that value is used instead of a value from your <!--#echo var="VAR_customized-hdrs"-->.
+For example, if you are Replying to a message the Subject field
+will already be filled in.
+In that case, if the <!--#echo var="VAR_customized-hdrs"--> list contains a Subject line, the
+custom subject will <EM>NOT</EM> be used.
+The subject derived from the subject of the message you are Replying
+to will be used instead.
+<P>
+It is also possible to make header setting even more complicated and more
+automatic by using
+<A HREF="h_rules_roles">Roles</A>,
+but if all you want to do is set a default value for a header, you don't
+need to think about Roles.
+<P>
+If you change your From address you may also find it useful to add the
+changed From address to the
+<a href="h_config_alt_addresses"><!--#echo var="VAR_alt-addresses"--></a>
+configuration option.
+<P>
+Limitation: Because commas are used to separate the list of
+<!--#echo var="VAR_customized-hdrs"-->, it is not possible to have the value of a
+header contain a comma.
+Nor is there currently an &quot;escape&quot; mechanism provided
+to make this work.
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_viewer_headers =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_viewer-hdrs"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_viewer-hdrs"--></H1>
+
+You may change the default list of headers that are viewed by listing
+the headers you want to view here. If the headers in your
+&quot;<!--#echo var="VAR_viewer-hdrs"-->&quot; list are present in the message, then they
+will be shown. The order of the headers you list will be honored. If
+the special value &quot;all-except&quot; is included as the first
+header in the &quot;<!--#echo var="VAR_viewer-hdrs"-->&quot; list, then all headers in the
+message except those in the list will be shown. The values are all
+case insensitive.
+
+<P>
+Note that once you put anything in the &quot;<!--#echo var="VAR_viewer-hdrs"-->&quot; list,
+then the original default headers are ignored. So, if you just wanted
+to add the header Organization to the list, you would have to list
+Organization plus all of the other headers originally in the default
+list. If you just included Organization and nothing else, then you
+would see only the Organization header, nothing else.
+
+<P>
+The default list of headers includes:
+<UL>
+ <LI>From
+ <LI>Resent-From
+ <LI>To
+ <LI>Resent-To
+ <LI>Cc
+ <LI>Resent-cc
+ <LI>Bcc
+ <LI>Newsgroups
+ <LI>Followup-To
+ <LI>Date
+ <LI>Resent-Date
+ <LI>Subject
+ <LI>Resent-Subject
+ <LI>Reply-To
+</UL>
+
+<P>
+If you are looking for a way to control which headers are included in
+outgoing mail and are visible or not in the composer, take a look at the
+options
+<A HREF="h_config_custom_hdrs"><!--#echo var="VAR_customized-hdrs"--></A>
+and <A HREF="h_config_comp_hdrs"><!--#echo var="VAR_default-composer-hdrs"--></A> instead of
+this option.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_viewer_margin_left =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_viewer-margin-left"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_viewer-margin-left"--></H1>
+
+This variable controls the left-hand vertical margin's width in
+Alpine's Message Viewing screen.
+Its value is the number of space characters preceding each displayed line.
+For consistency with
+<A HREF="h_config_viewer_margin_right"><!--#echo var="VAR_viewer-margin-right"--></A>,
+you may specify the column number to start in
+(column numbering begins with number 1)
+instead of the width of the margin by appending a lower case letter
+&quot;c&quot; to the number.
+For example, a value of &quot;2c&quot; means to start the text in column two,
+which is entirely equivalent to a value of &quot;1&quot;, which means to
+leave a margin of 1 space.
+<P>
+The default is a left margin of 0 (zero).
+Misconfigurations (for example, negative values or values with starting
+left columns greater than the ending right column)
+are silently ignored.
+If the number of columns for text between the <!--#echo var="VAR_viewer-margin-left"--> and
+the <!--#echo var="VAR_viewer-margin-right"--> is fewer than 8, then margins of zero will be used
+instead.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_viewer_margin_right =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_viewer-margin-right"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_viewer-margin-right"--></H1>
+
+This variable controls the right-hand vertical margin's width in
+Alpine's Message Viewing screen.
+Its value is the number of space characters following each displayed line.
+You may specify the column number to end the text in
+(column numbering begins with number 1)
+instead of the width of the margin by appending a lower case letter
+&quot;c&quot; to the number.
+For example, a value of &quot;76c&quot; means to end the text in column 76.
+If the screen is 80 characters wide, this is equivalent to a value
+of &quot;4&quot;, which means to leave a margin of 4 spaces.
+However, if you use different size screens at different times, then these
+two values are not equivalent.
+<P>
+The default right margin is 4.
+Misconfigurations (for example, negative values or values with starting
+left columns greater than the ending right column)
+are silently ignored.
+If the number of columns for text between the
+<A HREF="h_config_viewer_margin_left"><!--#echo var="VAR_viewer-margin-left"--></A> and
+the <!--#echo var="VAR_viewer-margin-right"--> is fewer than 8, then margins of zero will be used
+instead.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_quote_suppression =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_quote-suppression-threshold"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_quote-suppression-threshold"--></H1>
+
+This option should be used with care.
+It will cause some of the quoted text to be eliminated from the
+display when viewing a message in the MESSAGE TEXT screen.
+For example, if you set the <!--#echo var="VAR_quote-suppression-threshold"--> to the
+value &quot;5&quot;,
+this will cause quoted text that is longer than five lines to be truncated.
+Quoted text of five or fewer consecutive lines will be displayed in its entirety.
+Quoted text of more than six lines will have the first five lines displayed
+followed by a line that looks something like
+<P>
+<CENTER><SAMP>[ 12 lines of quoted text hidden from view ]</SAMP></CENTER>
+<P>
+As a special case, if exactly one line of quoted text would be hidden, the
+entire quote will be shown instead.
+So for the above example, quoted text that is exactly six lines long will
+will be shown in its entirety.
+(In other words, instead of hiding a single line and adding a line
+that announces that one line was hidden, the line is just shown.)
+<P>
+If the sender of a message has carefully chosen the quotes that he or she
+includes, hiding those quotes may change the meaning of the message.
+For that reason, Alpine requires that when you want to set the value of this
+variable to something less than four lines, you actually have to set it
+to the negative of that number.
+So if you want to set this option to &quot;3&quot;, you actually have to
+set it to &quot;-3&quot;.
+The only purpose of this is to get you to think about whether or not you
+really want to do this!
+If you want to delete all quoted text you set the value of this option
+to the special value &quot;-10&quot;.
+<P>
+The legal values for this option are
+<P>
+<TABLE>
+<TR>
+ <TD> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </TD>
+ <TD> Default, don't hide anything </TD>
+</TR>
+<TR>
+ <TD> &nbsp;-1,-2,-3&nbsp;&nbsp; </TD>
+ <TD> Suppress quote lines past 1, 2, or 3 lines </TD>
+</TR>
+<TR>
+ <TD> &nbsp;4,5,6,...&nbsp; </TD>
+ <TD> Suppress if more than that many lines </TD>
+</TR>
+<TR>
+ <TD> &nbsp;&nbsp;&nbsp;-10&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </TD>
+ <TD> Suppress all quoted lines </TD>
+</TR>
+</TABLE>
+<P>
+If you set this option to a non-default value you may sometimes wish to
+view the quoted text that is not shown.
+When this is the case, the
+<A HREF="h_common_hdrmode">HdrMode Command</A>
+may be used to show the hidden text.
+Typing the &quot;H&quot; command once will show the hidden text.
+Typing a second &quot;H&quot; will also turn on Full Header mode.
+The presence or absence of the HdrMode command is determined by the
+<A HREF="h_config_enable_full_hdr">&quot;<!--#echo var="FEAT_enable-full-header-cmd"-->&quot;</A>
+Feature-List option in your Alpine configuration, so you will want to
+be sure that is turned on if you use quote suppression.
+<P>
+For the purposes of this option, a quote is a line that begins with the
+character &quot;&gt;&quot;.
+<P>
+Quotes are only suppressed when displaying a message on the screen.
+The entire quote will be left intact when printing or forwarding or something
+similar.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_saved_msg_name_rule =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_saved-msg-name-rule"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_saved-msg-name-rule"--></H1>
+
+This option determines the default folder name when saving
+a message.
+
+<P>
+The default option is &quot;default-folder&quot;, which is the folder
+called &quot;saved-messages&quot; in Unix Alpine and
+&quot;savemail&quot; in PC-Alpine. To change the default folder, modify
+the Alpine option called
+<A HREF="h_config_def_save_folder">&quot;<!--#echo var="VAR_default-saved-msg-folder"-->&quot;</A>.
+
+<P>
+Choosing any of the &quot;by-&quot; options cause Alpine to attempt to
+get the chosen option's value for the message being saved (or for the
+first message being saved if using an aggregrate save).
+For example, if &quot;by-from&quot; is chosen, Alpine attempts to get the
+value of who the message came from (i.e. the from address).
+Alpine then attempts to save the message to a folder matching that value.
+If &quot;by-from&quot; is chosen and no value is obtained, Alpine uses
+&quot;by-sender&quot;.
+The opposite is also true.
+If &quot;by-recipient&quot; is chosen and the message was posted to a
+newsgroup, Alpine will use the newsgroup name.
+If &quot;by-replyto&quot; is chosen and no value is obtained, Alpine uses
+&quot;by-from&quot;.
+
+<P>
+If any of the &quot;by-realname&quot; options are chosen, Alpine will attempt
+to use the personal name part of the address instead of the mailbox part.
+If any of the &quot;by-nick&quot; options are chosen, the
+address is looked up in your address book and if found, the
+nickname for that entry is used.
+Only simple address book entries are checked, not distribution lists.
+Similarly, if any of the
+&quot;by-fcc&quot; options are chosen, the fcc from the corresponding
+address book entry is used.
+If by-realname, or the by-nick or by-fcc lookups result in no value,
+then if the chosen option ends with the &quot;then-from&quot;,
+&quot;then-sender&quot;, &quot;then-replyto&quot;,
+or &quot;then-recip&quot; suffix, Alpine
+reverts to the same behavior as &quot;by-from&quot;,
+&quot;by-sender&quot;, &quot;by-replyto&quot;, or &quot;by-recip&quot;
+depending on which option was specified.
+If the chosen option doesn't end with one of
+the &quot;then-&quot; suffixes, then Alpine reverts to the default
+folder when no match is found in the address book.
+
+<P>
+Choosing the option called &quot;last-folder-used&quot;, causes Alpine
+to save to the folder that you saved to the last time you saved a
+message. The first time you save a message in an Alpine session, Alpine
+attempts to save the message to the default folder.
+
+<P>
+Here is an example to make some of the options clearer.
+If the message is From
+<P>
+<CENTER><SAMP>Fred Flintstone &lt;flint@bedrock.org&gt;</SAMP></CENTER>
+<P>
+and this rule is set to &quot;by-from&quot;, then the default folder offered
+in the save dialog would be &quot;flint&quot;.
+<P>
+If this rule is set to &quot;by-realname-of-from&quot; then the default would
+be &quot;Fred Flintstone&quot;.
+<P>
+If this rule is set to &quot;by-nick-of-from&quot; then Alpine will search
+for the address &quot;flint@bedrock.org&quot; in your address book.
+If an entry is found and it has a nickname associated with it, that nickname
+will be offered as the default folder.
+If not, the default saved message folder will be offered as the default.
+<P>
+If this rule is set to &quot;by-fcc-of-from&quot; then Alpine will search
+for the address &quot;flint@bedrock.org&quot; in your address book.
+If an entry is found and it has an Fcc associated with it, that Fcc
+will be offered as the default folder.
+If not, the default saved message folder will be offered as the default.
+<P>
+If this rule is set to &quot;by-nick-of-from-then-from&quot; then Alpine will search
+for the address &quot;flint@bedrock.org&quot; in your address book.
+If an entry is found and it has a nickname associated with it, that nickname
+will be offered as the default folder.
+If it is not found (or has no nickname) then the default offered will be
+the same as it would be for the &quot;by-from&quot; rule.
+That is, it would be &quot;flint&quot;
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_fcc_rule =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_fcc-name-rule"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_fcc-name-rule"--></H1>
+
+This option determines the default name for folder carbon copy. Choose
+one:
+
+<DL>
+<DT>default-fcc</DT>
+<DD>This is the normal default, the value of which is set in the
+&quot;<!--#echo var="VAR_default-fcc"-->&quot; variable as specified earlier in this
+configuration.
+</DD>
+
+<DT>last-fcc-used</DT>
+<DD> Causes Alpine to use the folder that was last
+used in the fcc field
+</DD>
+
+<DT>by-nickname</DT>
+<DD>Means that Alpine will use the nickname
+from your address book that matches the first address in the To line.
+If there is no match, it will use the value of the
+&quot;<!--#echo var="VAR_default-fcc"-->&quot; variable.
+</DD>
+
+<DT>by-recipient</DT>
+<DD>Means Alpine will form a folder name
+based on the left hand side of the first address in the To line.
+</DD>
+
+<DT>by-nick-then-recip</DT>
+<DD>Means that it will use the
+matching nickname from your address book if there is one, otherwise it
+will extract the recipient name from the address and use that (like
+by-recipient).
+</DD>
+
+<DT>current-folder</DT>
+<DD>Causes a copy to be written to
+the currently open folder, unless that is the INBOX. In the case
+where the current folder is the INBOX, the &quot;<!--#echo var="VAR_default-fcc"-->&quot; is
+used instead.
+</DD>
+</DL>
+
+<P>
+Note: Whatever the fcc specified by the rule here, it will be
+over-ridden by any fcc entries you have in your address book.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_sort_key =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_sort-key"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_sort-key"--></H1>
+
+This option determines the order in which messages will be displayed in
+the MESSAGE INDEX screen. Choose from:
+<P>
+<UL>
+ <LI> <A HREF="h_index_sort_arrival">Arrival</A>
+ <LI> <A HREF="h_index_sort_date">Date</A>
+ <LI> <A HREF="h_index_sort_subj">Subject</A>
+ <LI> <A HREF="h_index_sort_ordsubj">OrderedSubj</A>
+ <LI> <A HREF="h_index_sort_thread">Thread</A>
+ <LI> <A HREF="h_index_sort_from">From</A>
+ <LI> <A HREF="h_index_sort_size">Size</A>
+ <LI> <A HREF="h_index_sort_score">Score</A>
+ <LI> <A HREF="h_index_sort_to">To</A>
+ <LI> <A HREF="h_index_sort_cc">Cc</A>
+</UL>
+
+<P>
+Each type of sort may also be reversed.
+Normal default is by &quot;Arrival&quot;.
+
+<P>
+A possible point of confusion arises when you change the configuration
+of the <!--#echo var="VAR_sort-key"-->.
+The folder will normally be re-sorted when you go back to viewing the
+index.
+However, if you have manually sorted the folder with the
+Sort
+(<!--chtml if pinemode="function_key"-->F7<!--chtml else-->$<!--chtml endif-->)
+command, then it will not be re-sorted until the next time it is opened.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_other_startup =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Set Startup Rule</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Set Startup Rule</H1>
+
+This option determines which message will be the <EM>current message</EM> when
+the folder is first opened.
+It works the same way that the option
+<A HREF="h_config_inc_startup"><!--#echo var="VAR_incoming-startup-rule"--></A>
+works, so look there for help.
+It may be used for any folder, not just incoming folders.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_perfolder_sort =====
+<HTML>
+<HEAD>
+<TITLE>Set Sort Order</TITLE>
+</HEAD>
+<BODY>
+<H1>Set Sort Order</H1>
+
+This option determines the order in which messages will be displayed in
+the MESSAGE INDEX screen when the Current Folder Type set in the
+Pattern is a match. Choose from:
+<P>
+<UL>
+ <LI> <A HREF="h_index_sort_default">Default</A>
+ <LI> <A HREF="h_index_sort_arrival">Arrival</A>
+ <LI> <A HREF="h_index_sort_date">Date</A>
+ <LI> <A HREF="h_index_sort_subj">Subject</A>
+ <LI> <A HREF="h_index_sort_ordsubj">OrderedSubj</A>
+ <LI> <A HREF="h_index_sort_thread">Thread</A>
+ <LI> <A HREF="h_index_sort_from">From</A>
+ <LI> <A HREF="h_index_sort_size">Size</A>
+ <LI> <A HREF="h_index_sort_score">Score</A>
+ <LI> <A HREF="h_index_sort_to">To</A>
+ <LI> <A HREF="h_index_sort_cc">Cc</A>
+</UL>
+
+<P>
+Each type of sort may also be reversed.
+Normal default is by &quot;Arrival&quot;.
+
+<P>
+A possible point of confusion arises when you change the configuration
+of the Sort Order for the currently open folder.
+The folder will normally be re-sorted when you go back to viewing the
+index.
+However, if you have manually sorted the folder with the
+Sort
+(<!--chtml if pinemode="function_key"-->F7<!--chtml else-->$<!--chtml endif-->)
+command, then it will not be re-sorted until the next time it is opened.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_fld_sort_rule =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_folder-sort-rule"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_folder-sort-rule"--></H1>
+
+This option controls the order in which folder list entries will be
+presented in the FOLDER LIST screen. Choose one of the following:
+
+<DL>
+<DT>Alphabetical</DT>
+<DD>sort by alphabetical name independent of type
+</DD>
+
+<DT>Alpha-with-dirs-last</DT>
+<DD>sort by alphabetical name grouping directory entries
+to the end of the list
+</DD>
+
+<DT>Alpha-with-dirs-first</DT>
+<DD>sort by alphabetical name grouping directory entries
+to the start of the list
+</DD>
+</DL>
+
+The normal default is &quot;Alphabetical&quot;.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_ab_sort_rule =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_addrbook-sort-rule"--></a></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_addrbook-sort-rule"--></a></H1>
+
+This option controls the order in which address book entries will be
+presented. Choose one of the following:
+
+<DL>
+<DT>fullname</DT>
+<DD>use fullname field, lists mixed in
+</DD>
+
+<DT>fullname-with-lists-last</DT>
+<DD>use fullname field, but put lists at end
+</DD>
+
+<DT>nickname</DT>
+<DD>use nickname field, lists mixed in
+</DD>
+
+<DT>nickname-with-lists-last</DT>
+<DD>use nickname field, but put lists at end
+</DD>
+
+<DT>dont-sort</DT>
+<DD>don't change order of file
+</DD>
+</DL>
+
+<P>
+The normal default is &quot;fullname-with-lists-last&quot;.
+If you use an address book from more than one computer and those
+computers sort the address book differently then the sort order
+will be the order where the last change to the address book was
+made.
+There are two reasons the sorting might be different on different
+systems.
+First, the <!--#echo var="VAR_addrbook-sort-rule"--></a> may be set differently in the two
+places.
+Second, the collation rules on the two computers may be different.
+For example, one system might ignore special characters while the other
+doesn't or one may sort upper and lower case letters together while
+the other doesn't.
+In any case, the order you see is the order on the system where the
+last change was made, for example by an address book edit or a
+Take Address command.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_post_char_set =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_posting-character-set"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_posting-character-set"--></H1>
+
+The <!--#echo var="VAR_posting-character-set"--> configuration option is used
+when sending messages.
+
+<P>
+
+When sending a message the text typed in the composer is
+labeled with the character set specified by this option.
+If the composed text is not fully representable in the
+specified <!--#echo var="VAR_posting-character-set"-->, then it is labeled as &quot;UTF-8.&quot
+instead;
+
+<P>
+Attachments are labeled with your
+<A HREF="h_config_key_char_set">&quot;Keyboard Character Set&quot;</A>.
+
+<P>
+Generally, there should be little need to set this option.
+If left unset, the
+default behavior is to label composed text as specifically as
+possible. That is, if the composed text has no non-ASCII characters,
+it is labeled as &quot;US-ASCII.&quot; Similarly, if it is composed of
+only ISO-8859-15 characters, it is labeled as such. Alpine will
+attempt to automatically detect a number of character sets including ISO-8859-15,
+ISO-8859-1, ISO-8859-2, VISCII, KOI8-R, KOI8-U, ISO-8859-7, ISO-8859-6,
+ISO-8859-8, TIS-620, ISO-2022-JP, GB2312, BIG5, and EUC-KR.
+If the message contains a mix of character sets,
+it is labeled as &quot;UTF-8.&quot;
+
+<P>
+
+This setting is provided to allow you to force a particular character set that
+Alpine does not automatically detect. For example, if a message is representable
+in more than one character set then Alpine may choose a different default
+than you want.
+Lastly, by setting this option explicitly to
+&quot;UTF-8&quot; all non-ASCII messages you send will be labeled as
+&quot;UTF-8&quot; instead of something more specific.
+
+<P>
+In the Setup/Config screen you may choose from a list of all the
+character sets Alpine knows about by using the &quot;T&quot; ToCharsets command.
+
+<P>
+The options
+<A HREF="h_config_char_set">&quot;Display Character Set&quot;</A>
+and <A HREF="h_config_key_char_set">&quot;Keyboard Character Set&quot;</A>
+are closely related.
+Setting the feature
+<A HREF="h_config_use_system_translation">&quot;Use System Translation&quot;</A>
+should cause this option to be ignored.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_unk_char_set =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_unknown-character-set"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_unknown-character-set"--></H1>
+
+The <!--#echo var="VAR_unknown-character-set"--> configuration option is used
+when reading or replying to messages.
+
+<P>
+
+A text message should either be made up of all US-ASCII characters
+or it should contain a charset label which tells the software which
+character set encoding to use to interpret the message.
+Sometimes a malformed message may be unlabeled but contain non-ascii text.
+This message is outside of the standards so any attempt to read it could fail.
+When Alpine attempts to read such a message it will try to interpret the
+text in the character set you specify here.
+For example, if you have correspondents who send you unlabeled messages that
+are usually made up of characters from the WINDOWS-1251 character set, setting
+this <!--#echo var="VAR_unknown-character-set"--> to <CODE>WINDOWS-1251</CODE> will
+allow you to read those messages.
+Of course, if the unlabeled message is actually in some other character set,
+then you may see garbage on your screen.
+<P>
+Instead of just unlabeled text, this option also affects text which is labeled
+with the charsets &quot;X-Unknown&quot; or &quot;US-ASCII&quot;.
+
+<P>
+In the Setup/Config screen you may choose from a list of all the
+character sets Alpine knows about by using the &quot;T&quot; ToCharsets command.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_char_set =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Display Character Set</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Display Character Set</H1>
+
+The Display Character Set configuration option is used when viewing messages.
+<P>
+Alpine uses Unicode characters internally and
+it is a goal for Alpine to handle email in many different languages.
+Alpine will properly display only left-to-right character sets
+in a fixed-width font. Specifically, Alpine assumes that a fixed-width
+font is in use, in the sense that
+characters are assumed to take up zero, one, or two character cell
+widths from left to right on the screen. This is true even in PC-Alpine.
+<P>
+
+Alpine recognizes some local character sets that are right-to-left
+(Arabic, Hebrew, and Thai) or not representable in a fixed-width font
+(Arabic) and properly converts texts in these character sets to/from
+Unicode; however, there are known display bugs with these character
+sets.
+<P>
+
+There are three possible configuration character settings and some
+environment variable settings that can affect how Alpine
+handles international characters.
+The first two of these are only available in UNIX Alpine.
+The three configuration options are
+Display Character Set,
+Keyboard Character Set, and
+<!--#echo var="VAR_posting-character-set"-->.
+The Keyboard Character Set defaults to being the same value
+as the Display Character Set, and that is usually correct, because
+the keyboard almost always produces characters in the same character set
+as the display displays.
+The Display Character Set is the character set that Alpine
+will attempt to use when sending characters to the display.
+<P>
+
+By default, the Display Character Set variable is not set and UNIX Alpine
+will attempt to get this information from the environment.
+In particular, the <CODE>nl_langinfo(CODESET)</CODE> call is used.
+This usually depends on the setting of the environment variables LANG or LC_CTYPE.
+An explicit configuration setting for Display Character Set will,
+of course, override any default setting.
+<P>
+For PC-Alpine the Display Character Set
+and the Keyboard Character Set
+are always equivalent to <CODE>UTF-8</CODE> and this is not settable.
+<P>
+
+It is probably best to use UNIX Alpine in a terminal emulator
+capable of displaying UTF-8 characters, since that will allow you to
+view just about any received text that is correctly formatted (note,
+however, the above comments about known index display bugs with certain
+character sets). You'll need to have an emulator that uses a UTF-8 font
+and you'll need to set up your environment to use a UTF-8 charmap. For
+example, on a Linux system you might include
+<P>
+<CENTER> <CODE>setenv LANG en_US.UTF-8</CODE> </CENTER>
+<P>
+
+or something similar in your UNIX startup files.
+You'd also have to select a UTF-8 font in your terminal emulator.
+<P>
+
+The types of values that the character set variables may be set to are
+<CODE>UTF-8</CODE>, <CODE>ISO-8859-1</CODE>, or <CODE>EUC-JP</CODE>.
+The <CODE>ISO-2022</CODE> character sets are not supported for input or
+for display, but as a special case, <CODE>ISO-2022-JP</CODE> is supported
+for use only as a <!--#echo var="VAR_posting-character-set"-->.
+In the Setup/Config screen you may choose from a list of all the
+character sets Alpine knows about by using the &quot;T&quot; ToCharsets command.
+Here is a list of many of the possible character sets:
+
+<P>
+<TABLE>
+<TR> <TD>UTF-8</TD> <TD>Unicode</TD> </TR>
+<TR> <TD>US-ASCII</TD> <TD>7 bit American English characters</TD> </TR>
+<TR> <TD>ISO-8859-1</TD> <TD>8 bit European "Latin 1" character set</TD> </TR>
+<TR> <TD>ISO-8859-2</TD> <TD>8 bit European "Latin 2" character set</TD> </TR>
+<TR> <TD>ISO-8859-3</TD> <TD>8 bit European "Latin 3" character set</TD> </TR>
+<TR> <TD>ISO-8859-4</TD> <TD>8 bit European "Latin 4" character set</TD> </TR>
+<TR> <TD>ISO-8859-5</TD> <TD>8 bit Latin and Cyrillic</TD> </TR>
+<TR> <TD>ISO-8859-6</TD> <TD>8 bit Latin and Arabic</TD> </TR>
+<TR> <TD>ISO-8859-7</TD> <TD>8 bit Latin and Greek</TD> </TR>
+<TR> <TD>ISO-8859-8</TD> <TD>8 bit Latin and Hebrew</TD> </TR>
+<TR> <TD>ISO-8859-9</TD> <TD>8 bit European "Latin 5" character set</TD> </TR>
+<TR> <TD>ISO-8859-10</TD> <TD>8 bit European "Latin 6" character set</TD> </TR>
+<TR> <TD>ISO-8859-11</TD> <TD>Latin and Thai</TD> </TR>
+<TR> <TD>ISO-8859-12</TD> <TD>Reserved</TD> </TR>
+<TR> <TD>ISO-8859-13</TD> <TD>8 bit European "Latin 7" character set</TD> </TR>
+<TR> <TD>ISO-8859-14</TD> <TD>8 bit European "Latin 8" character set</TD> </TR>
+<TR> <TD>ISO-8859-15</TD> <TD>8 bit European "Latin 9" character set</TD> </TR>
+<TR> <TD>ISO-8859-16</TD> <TD>8 bit European "Latin 10" character set</TD> </TR>
+<TR> <TD>KOI8-R</TD> <TD>8 bit Latin and Russian</TD> </TR>
+<TR> <TD>KOI8-U</TD> <TD>8 bit Latin and Ukranian</TD> </TR>
+<TR> <TD>WINDOWS-1251</TD> <TD>8 bit Latin and Russian</TD> </TR>
+<TR> <TD>TIS-620</TD> <TD>8 bit Latin and Thai</TD> </TR>
+<TR> <TD>VISCII</TD> <TD>8 bit Latin and Vietnamese</TD> </TR>
+<TR> <TD>GBK</TD> <TD>Latin and Chinese Simplified</TD> </TR>
+<TR> <TD>GB2312</TD> <TD>Latin and Chinese Simplified</TD> </TR>
+<TR> <TD>CN-GB</TD> <TD>Latin and Chinese Simplified</TD> </TR>
+<TR> <TD>BIG5</TD> <TD>Latin and Chinese Traditional</TD> </TR>
+<TR> <TD>BIG-5</TD> <TD>Latin and Chinese Traditional</TD> </TR>
+<TR> <TD>EUC-JP</TD> <TD>Latin and Japanese</TD> </TR>
+<TR> <TD>SHIFT-JIS</TD> <TD>Latin and Japanese</TD> </TR>
+<TR> <TD>EUC-KR</TD> <TD>Latin and Korean</TD> </TR>
+<TR> <TD>KSC5601</TD> <TD>Latin and Korean</TD> </TR>
+</TABLE>
+<P>
+
+When reading incoming email, Alpine understands many different
+character sets and is able to convert the incoming mail into Unicode.
+The Unicode will be converted to the Display Character Set
+for display on your terminal.
+Characters typed at the keyboard will be converted from the
+Keyboard Character Set to Unicode for Alpine's internal
+use.
+You may find that you can read some malformed messages that do not
+contain a character set label by setting the option
+<A HREF="h_config_unk_char_set">&quot;<!--#echo var="VAR_unknown-character-set"-->&quot;</A>.
+<P>
+
+The <!--#echo var="VAR_posting-character-set"--> is used when sending messages.
+The default behavior obtained by leaving this variable unset is usually
+what is wanted. In that default case, Alpine will attempt
+to label the message with the most specific character set from the
+rather arbitrary set
+<P>
+US-ASCII, ISO-8859-15,
+ISO-8859-1, ISO-8859-2, VISCII, KOI8-R, KOI8-U, ISO-8859-7, ISO-8859-6,
+ISO-8859-8, TIS-620, ISO-2022-JP, GB2312, BIG5, EUC-KR, and UTF-8.
+<P>
+
+For example, if the message is made up of only US-ASCII characters, it
+will be labeled US-ASCII. Otherwise, if it is all ISO-8859-15 characters,
+that will be the label. If that doesn't work the same is tried for the
+remaining members of the list.
+
+<P>
+It might make sense to set <!--#echo var="VAR_posting-character-set"--> to an
+explicit value instead.
+For example, if you usually send messages in Greek, setting this
+option to ISO-8859-7 will result in messages being labeled as
+US-ASCII if there are no non-ascii characters, ISO-8859-7 if there
+are only Greek characters, or UTF-8 if there are some characters
+that aren't representable in ISO-8859-7.
+Another possibility is to set this option explicitly to UTF-8.
+In that case
+Alpine labels only ascii messages as US-ASCII and all other
+messages as UTF-8.
+<P>
+
+The options
+<A HREF="h_config_post_char_set">&quot;<!--#echo var="VAR_posting-character-set"-->&quot;</A>
+and <A HREF="h_config_key_char_set">&quot;Keyboard Character Set&quot;</A>
+are closely related to this option.
+Setting the feature
+<A HREF="h_config_use_system_translation">&quot;Use System Translation&quot;</A>
+should cause this option to be ignored.
+
+<P>
+When displaying a message, Alpine compares this setting to the character
+set specified in the message. If not all of the
+characters in the message can be displayed using the Display Character Set
+then Alpine places an editorial
+comment in the displayed text (enclosed in square-brackets) indicating
+that some characters may not be displayed correctly.
+This comment may be eliminated by turning on the option
+<A HREF="h_config_quell_charset_warning"><!--#echo var="FEAT_quell-charset-warning"--></A>.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_key_char_set =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Keyboard Character Set</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Keyboard Character Set</H1>
+
+UNIX Alpine only.
+<P>
+The Keyboard Character Set identifies the character set of the characters
+coming from your keyboard.
+It defaults to having the same value as your
+<A HREF="h_config_char_set">&quot;Display Character Set&quot;</A>,
+which in turn defaults to a value obtained from your environment.
+It is unlikely that you will need to use this option, because the keyboard
+almost always produces the same kind of characters as the display displays.
+
+<P>
+This character set is also used when accessing files in your local
+file system.
+The names of the files are assumed to be in the same character set as
+what the keyboard produces, as well as the contents of the files.
+
+<P>
+In the Setup/Config screen you may choose from a list of all the
+character sets Alpine knows about by using the &quot;T&quot; ToCharsets command.
+
+<P>
+The options
+<A HREF="h_config_char_set">&quot;Display Character Set&quot;</A>
+and <A HREF="h_config_post_char_set">&quot;<!--#echo var="VAR_posting-character-set"-->&quot;</A>
+are closely related.
+Setting the feature
+<A HREF="h_config_use_system_translation">&quot;Use System Translation&quot;</A>
+should cause this option to be ignored.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_editor =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_editor"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_editor"--></H1>
+
+<!--#echo var="VAR_editor"--> specifies the program invoked by ^_ in the Composer. This is
+normally an alternative to Alpine's internal composer (Pico). You could use
+this setting to specify an alternate editor to use occasionally or if you
+have a favorite editor and want to use it all the time (see the
+<A HREF="h_config_alt_ed_now">&quot;<!--#echo var="FEAT_enable-alternate-editor-implicitly"-->&quot;</A> setting). <P>
+If you specify multiple editors for this option, ^_ will invoke the first one
+of those specified that exists and is executable. When specifying a program
+for use here, make sure that the format of the text it saves -- which, when
+you exit it, will become the message body in Alpine -- is appropriate
+for the body of an email message; avoid proprietary formats that may result in
+a message body that the recipient of your message will be unable to decipher.
+<P>
+<!--chtml if pinemode="os_windows"-->
+<!--chtml else-->
+If you are in doubt about what editors are available on your system, or which
+of them may be appropriate for specification here, ask your local computing
+support staff.
+<!--chtml endif-->
+<P>
+Note that if <a href="h_config_quell_flowed_text"><!--#echo var="FEAT_quell-flowed-text"--></a> is
+unset, outgoing text will be set as flowed. In most cases this will be fine,
+but if the editor has a &quot;flowed text&quot; mode, it would be best to
+use that.
+
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_speller =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_speller"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_speller"--></H1>
+
+UNIX Alpine only.
+<P>
+For PC-Alpine, you must install the aspell library code that you
+may get from
+<A HREF="http://aspell.net/win32/">http://aspell.net/win32/</A>.
+<P>
+This option affects the behavior of the ^T (spell check) command in the
+Composer. It specifies the program invoked by ^T in the Composer.
+By default, Alpine uses
+<P>
+<CENTER><SAMP>aspell --dont-backup --mode=email check</SAMP></CENTER>
+<P>
+if it knows where to find &quot;aspell&quot;.
+If there is no &quot;aspell&quot; command available but the command &quot;ispell&quot; is available
+then the command used is
+<P>
+<CENTER><SAMP>ispell -l</SAMP></CENTER>
+<P>
+Otherwise, the ancient &quot;spell&quot; command is used.
+<P>
+If you specify a value for this command (with full pathname) then that is what
+will be used instead of any of the defaults.
+When invoking this
+spell-checking program, Alpine appends a tempfile name (where the message is
+passed) to the command line. Alpine expects the speller to correct the
+spelling in that file. When you exit from that program Alpine will read the
+tempfile back into the composer.
+<P>
+Don't set this speller option to the standard Unix spell command.
+That won't work because spell works in a different way.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_display_filters =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_display-filters"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_display-filters"--></H1>
+
+This option defines a list of text-filtering commands (programs or
+scripts) that may be used to filter text portions of received messages
+prior to their use (e.g., presentation in the &quot;MESSAGE TEXT&quot;
+display screen, exporting to a text file).
+For security reasons, the full path name of the
+filter command must be specified.
+
+<P>
+The command is executed and the message is piped into its standard input.
+The standard output of the command is read back by Alpine. The
+&quot;_TMPFILE_&quot; token (see below) overrides this default behavior.
+
+<P>
+The filter's use is based on the configured &quot;trigger&quot; string. The
+format of a filter definition is:
+
+<P>
+<CENTER>&lt;trigger&gt; &lt;command&gt; &lt;arguments&gt;</CENTER>
+
+<P>
+You can specify as many filters as you wish, separating them with a comma.
+Each filter can have only one trigger and command. Thus, two trigger
+strings that invoke the same command require separate filter
+specifications.
+
+<P>
+The &quot;trigger&quot; is simply text that, if found in the message,
+will invoke the associated command. If the trigger contains any space
+characters, it must be placed within quotes. Likewise, should you
+wish a filter to be invoked unconditionally, define the trigger as the
+null string, &quot;&quot; (two consecutive double-quote characters). If the
+trigger string is found anywhere in the text of the message the filter
+is invoked. Placing the trigger text within the tokens defined below
+changes where within the text the trigger must be before considering
+it a match.
+
+<P>
+Trigger Modifying Tokens:
+<DL>
+<DT>_CHARSET(<VAR>string</VAR>)_</DT>
+<DD>This token tells Alpine to invoke the supplied command
+if the text is in a character set matching <VAR>string</VAR>
+(e.g., ISO-8859-2 or ISO-2022-JP).
+</DD>
+
+
+<DT>_LEADING(<VAR>string</VAR>)_</DT>
+<DD>This token tells Alpine to invoke the supplied command
+if the enclosed <VAR>string</VAR> is found to be the first
+non-whitespace text.
+<BR>NOTE: Quotes are necessary if <VAR>string</VAR> contains
+the space character.
+</DD>
+
+<DT>_BEGINNING(<VAR>string</VAR>)_</DT>
+<DD>This token tells Alpine to invoke the supplied command
+if the enclosed <VAR>string</VAR> is found at the beginning
+of any line in the text.
+<BR>NOTE: Quotes are necessary if <VAR>string</VAR> contains
+the space character.
+</DD>
+</DL>
+
+<P>
+The &quot;command&quot; and &quot;arguments&quot; portion is simply
+the command line to be invoked if the trigger string is found. Below
+are tokens that Alpine will recognize and replace with special values
+when the command is actually invoked.
+
+<P>
+Command Modifying Tokens:
+
+<DL>
+<DT>_TMPFILE_</DT>
+<DD>When the command is executed, this token is
+replaced with the path and name of the temporary
+file containing the text to be filtered. Alpine
+expects the filter to replace this data with the
+filter's result.
+
+<P>
+NOTE: Use of this token implies that the text to
+be filtered is not piped into standard input of the
+executed command and its standard output is ignored.
+Alpine restores the tty modes before invoking the
+filter in case the filter interacts with the user
+via its own standard input and output.
+</DD>
+
+<DT>_RESULTFILE_</DT>
+<DD>When the command is executed, this token is
+replaced with the path and name of a temporary
+file intended to contain a status message from the
+filter. Alpine displays this in the message status
+field.
+</DD>
+
+<DT>_DATAFILE_</DT>
+<DD>When the command is executed, this token is
+replaced with the path and name of a temporary
+file that Alpine creates once per session and deletes
+upon exit. The file is intended to be used by the
+filter to store state information between instances
+of the filter.
+</DD>
+
+<DT>_PREPENDKEY_</DT>
+<DD>When the command is executed, this token indicates that a random
+number will be passed down the input stream before the message text.
+This number could be used as a session key. It is sent in this way to
+improve security. The number is unique to the current Alpine session
+and is only generated once per session.
+</DD>
+</DL>
+
+<P>
+The feature
+<A HREF="h_config_disable_reset_disp"><!--#echo var="FEAT_disable-terminal-reset-for-display-filters"--></A> is related.
+<P>
+Performance caveat/considerations:
+<BR>
+Testing for the trigger and invoking the filter doesn't come for free.
+There is overhead associated with searching for the trigger string, testing
+for the filter's existence and actually piping the text through the filter.
+The impact can be reduced if the Trigger Modifying Tokens above are
+employed.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_sending_filter =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_sending-filters"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_sending-filters"--></H1>
+
+This option defines a list of text-filtering commands (programs and
+scripts) that may be selectively invoked to process a message just before
+it is sent. If set, the Composer's ^X (Send) command will allow you to
+select which filter (or none) to apply to the message before it is sent.
+For security reasons, the full path of the filter program must be
+specified.
+
+<P>
+Command Modifying Tokens:
+
+<DL>
+<DT>_RECIPIENTS_</DT>
+<DD>When the command is executed, this token is replaced
+with the space delimited list of recipients of the
+message being sent.
+</DD>
+
+<DT>_TMPFILE_</DT>
+<DD>
+When the command is executed, this token is
+replaced with the path and name of the temporary
+file containing the text to be filtered. Alpine
+expects the filter to replace this data with the
+filter's result.
+
+<P>
+NOTE: Use of this token implies that the text to
+be filtered is not piped into standard input of the
+executed command and its standard output is ignored.
+Alpine restores the tty modes before invoking the
+filter in case the filter interacts with the user
+via its own standard input and output.
+</DD>
+
+<DT>_RESULTFILE_</DT>
+<DD>When the command is executed, this token is
+replaced with the path and name of a temporary
+file intended to contain a status message from the
+filter. Alpine displays this in the message status
+field.
+</DD>
+
+<DT>_DATAFILE_</DT>
+<DD>When the command is executed, this token is replaced
+in the command line with the path and name of a
+temporary file that Alpine creates once per session
+and deletes upon exit. The file is intended to be
+used by the filter to store state information between
+instances of the filter.
+</DD>
+
+<DT>_PREPENDKEY_</DT>
+<DD>When the command is executed, this token indicates
+that a random number will be passed down the input
+stream before the message text. This number could
+be used as a session key. It is sent in this way
+to improve security. The number is unique to the
+current Alpine session and is only generated once per
+session.
+</DD>
+
+<DT>_INCLUDEALLHDRS_</DT>
+<DD>When the command is executed, this token indicates
+that the headers of the message will be passed down the input stream
+before the message text.
+</DD>
+
+<DT>_MIMETYPE_</DT>
+<DD>When the command is executed, this token is replaced in the
+command name with a temporary file name used to accept any new MIME
+Content-Type information necessitated by the output of the filter.
+Upon the filter's exit, if the file contains new MIME type
+information, Alpine verifies its format and replaces the outgoing
+message's MIME type information with that contained in the file. This
+is basically a cheap way of sending something other than Text/Plain.
+</DD>
+</DL>
+
+<P>
+NOTE: Only the body text, which is visible in the Composer, is piped
+through this filter. Attachments are not sent to the filter.
+<P>
+Sending filters are not used if the feature
+<A HREF="h_config_send_wo_confirm">&quot;<!--#echo var="FEAT_send-without-confirm"-->&quot;</A> is set.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_keywords =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_keywords"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_keywords"--></H1>
+
+You may define your own set of keywords and optionally set them on a
+message by message basis.
+These are similar to the &quot;Important&quot; flag which the user
+may set using the Flag command.
+The difference is that the Important flag is always present for each folder.
+User-defined keywords are chosen by the user.
+You may set up the list of possible keywords here, or you may add keywords
+from the Flag Details screen that you
+can get to after typing the
+Flag (<!--chtml if pinemode="function_key"-->F11<!--chtml else-->*<!--chtml endif-->)
+command.
+After the keywords have been defined,
+then you use the <A HREF="h_common_flag">Flag command</A>
+to set or clear the keywords in each message.
+The behavior of the flag command may be modified by using the
+<A HREF="h_config_flag_screen_default">&quot;<!--#echo var="FEAT_enable-flag-screen-implicitly"-->&quot;</A> option or the
+<A HREF="h_config_flag_screen_kw_shortcut">&quot;<!--#echo var="FEAT_enable-flag-screen-keyword-shortcut"-->&quot;</A> option.
+
+<P>
+Keywords may be used when Selecting messages (Select Keyword).
+Keywords may also be used in the Patterns of Rules (Filters, Indexcolors, etc).
+Filter Rules may be used to set keywords automatically.
+Keywords may be displayed as part of the Subject of a message by using
+the SUBJKEY or SUBJKEYINIT tokens in the
+<A HREF="h_config_index_format"><!--#echo var="VAR_index-format"--></A> option.
+The <A HREF="h_config_kw_braces"><!--#echo var="VAR_keyword-surrounding-chars"--></A>
+option may be used to modify the display of keywords using
+SUBJKEY and SUBJKEYINIT slightly.
+Keywords may also be displayed in a column of their own in the MESSAGE INDEX
+screen by using the KEY or KEYINIT tokens.
+It is also possible to color keywords in the index using the
+Setup/Kolor screen (<A HREF="h_config_kw_color">Keyword Colors</A>).
+Keywords are not supported by all mail servers.
+
+<P>
+You may give keywords nicknames if you wish.
+If the keyword definition you type in contains a SPACE character, then the
+actual value of the keyword is everything after the last SPACE and the
+nickname for that keyword is everything before the last SPACE.
+For example, suppose you are trying to interoperate with another email program
+that uses a particular keyword with an unpleasant name.
+Maybe it uses a keyword called
+<P>
+<CENTER><SAMP>VendorName.SoftwareName.08</SAMP></CENTER>
+<P>
+but for you that keyword means that the message is work-related.
+You could define a keyword to have the value
+<P>
+<CENTER><SAMP>Work VendorName.SoftwareName.08</SAMP></CENTER>
+<P>
+and then you would use the name &quot;Work&quot; when dealing with
+that keyword in Alpine.
+If you defined it as
+<P>
+<CENTER><SAMP>My Work VendorName.SoftwareName.08</SAMP></CENTER>
+<P>
+the nickname would be everything before the last SPACE, that is the nickname
+would be &quot;My Work&quot;.
+<P>
+Some commonly used keywords begin with dollar signs.
+This presents a slight complication, because the dollar sign is normally used
+to signify
+<A HREF="h_news_config">environment variable expansion</A>
+in the Alpine configuration.
+In order to specify a keyword that begins with a dollar sign you must
+precede the dollar sign with a second dollar sign to escape its special
+meaning.
+For example, if you want to include the keyword
+<P>
+<CENTER><SAMP>$Label1</SAMP></CENTER>
+<P>
+as one of your possible keywords, you must enter the text
+<P>
+<CENTER><SAMP>$$Label1</SAMP></CENTER>
+<P>
+
+instead.
+<P>
+There are a couple limitations.
+First, not all servers support keywords.
+Second, some servers (including the IMAP server included with Alpine)
+have a per folder limit on the number of keywords that may be defined.
+This count commonly includes every keyword you have ever used in the
+folder, even if it is no longer being used.
+In other words, you can add keywords but you cannot remove them easily.
+If you have changed keywords over the life of a folder and find that
+you have reached such a limit, one possible solution might be to copy
+all of the messages to a newly created folder (using Alpine) and then
+delete the original and rename the new folder.
+The reason this might work is that only the keywords currently set in
+any of the messages will be used in the new folder, hopefully putting you
+under the limit.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_alt_addresses =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_alt-addresses"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_alt-addresses"--></H1>
+
+This option provides a place for you to list alternate email addresses
+you may have.
+Each address in the list should be the actual email address part of an
+address, without the full name field or the angle brackets.
+For example:
+
+<P>
+<CENTER><SAMP>user@example.com</SAMP></CENTER>
+<P>
+
+The matching is case-insensitive, so this would match any of
+<SAMP>User@example.com</SAMP>, <SAMP>user@Example.Com</SAMP>, or
+<SAMP>USER@EXAMPLE.COM</SAMP> as well.
+
+<P>
+If set, the option affects the behavior of the Reply
+command and the &quot;+&quot; symbol in the MESSAGE INDEX, which denotes that
+a message has been addressed specifically to you.
+
+<P>
+In the default INDEX display
+the personal name (or email address) of
+the person listed in the message's &quot;From:&quot; header
+field is usually displayed except when that address is yours or one of your
+alternate addresses.
+In that case you will usually see the name of
+the first person specified in the
+message's &quot;To:&quot; header field
+with the prefix &quot;To: &quot; prepended.
+
+<P>
+With respect to Reply, the reply-to-all option will exclude addresses
+listed here.
+
+<P>
+The feature
+<A HREF="h_config_copy_to_to_from"><!--#echo var="FEAT_copy-to-address-to-from-if-it-is-us"--></A>
+is somewhat related to this option.
+
+<P>
+In addition to a list of actual addresses,
+you may use regular expressions (as used with egrep with the ignore case flag)
+to describe the addresses you want to match.
+Alpine will somewhat arbitrarily interpret your entry as a regular
+expression if it contains any of the characters
+*, |, +, ?, {, [, ^, $, or &#92;.
+Otherwise, it will be treated literally.
+The feature
+<a href="h_config_disable_regex"><!--#echo var="FEAT_disable-regular-expression-matching-for-alternate-addresses"--></a>
+may be used to turn off regular expression processing regardless of whether or not
+special characters appear in the entry.
+
+<P>
+A description of how regular expressions work is beyond the
+scope of this help text, but some examples follow.
+
+<P>
+The entry
+
+<P>
+<CENTER><SAMP>.*@example.com</SAMP></CENTER>
+<P>
+
+in the <!--#echo var="VAR_alt-addresses"--> list would mean that any
+address with a domain name of <SAMP>example.com</SAMP> (such as
+<SAMP>fred@example.com</SAMP> or <SAMP>wilma@example.com</SAMP>) will be considered
+one of your alternate addresses.
+Strictly speaking, the dot in <SAMP>example.com</SAMP> ought to be escaped with
+a backslash, as in <SAMP>example&#92;.com</SAMP>, and a dollar sign anchor ought
+to come at the end of the expression to prevent a match of <SAMP>example.com.org</SAMP>.
+Complicating things further, the dollar sign
+is special in the Alpine configuration (it signifies environment variable expansion)
+so the dollar sign should be doubled or backslash escaped for Alpine's sake.
+Quotes around the whole expression will not escape the dollar sign successfully.
+So this example should look like
+
+<P>
+<CENTER><SAMP>.*@example&#92;.com$$</SAMP></CENTER>
+<P>
+
+<P>
+The entry
+
+<P>
+<CENTER><SAMP>^fred[0-9]*@example.com$$</SAMP></CENTER>
+<P>
+
+would match
+<SAMP>fred3@example.com</SAMP> or <SAMP>fred17@example.com</SAMP> as well
+as <SAMP>fred@example.com</SAMP>.
+
+<P>
+You could match all addresses that look like
+<SAMP>fred+stuff@example.com</SAMP> for any value of <SAMP>stuff</SAMP> with the
+entry
+
+<P>
+<CENTER><SAMP>^fred&#92;+.*@example.com$$</SAMP></CENTER>
+<P>
+
+Notice that you have to escape the plus sign with a backslash because plus
+is a special character in regular expressions.
+If you wanted to match plain <SAMP>fred</SAMP> as well as <SAMP>fred+stuff</SAMP>
+the expression
+
+<P>
+<CENTER><SAMP>^fred(()|&#92;+.*)@example.com$$</SAMP></CENTER>
+<P>
+
+would do it, but it would be easier to just add fred@example.com as a
+separate entry.
+
+<P>
+One more example, a match of all first-level subdomains, is given by
+
+<P>
+<CENTER><SAMP>^fred@[[:alnum:]_-]*&#92;.example&#92;.com$$</SAMP></CENTER>
+<P>
+
+<P>
+Because the regular expression matching is based on an old library
+(<SAMP>hs_regex</SAMP>) the regular expressions might not work exactly as you expect,
+but they should be close.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_abook_formats =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_addressbook-formats"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_addressbook-formats"--></H1>
+
+This option specifies the format that address books are displayed in.
+Normally, address books are displayed with the nicknames in the first
+column, the fullnames in the second column, and addresses in the third
+column. The system figures out reasonable defaults for the widths of
+the columns. An address book may be given a different format by
+listing special tokens in the order you want them to display. The
+possible tokens are NICKNAME, FULLNAME, ADDRESS, FCC, and COMMENT.
+So, for example, to get the default behavior you could list
+
+<P>
+<CENTER><!--#echo var="VAR_addressbook-formats"-->=NICKNAME FULLNAME ADDRESS</CENTER>
+
+<P>
+(You can also use the token DEFAULT to get the default behavior for
+an address book format.)
+
+<P>
+The tokens are separated by spaces. &quot;<!--#echo var="VAR_addressbook-formats"-->&quot;
+is a list, so if you have more than one address book you may have a
+separate format for each by putting its format at the corresponding
+location in the &quot;<!--#echo var="VAR_addressbook-formats"-->&quot; list.
+
+<P>
+
+Listed first are the personal address books, then the global address
+books. So, if you have two personal address books and one global
+address book, you may have up to three formats in the
+&quot;<!--#echo var="VAR_addressbook-formats"-->&quot; list. If
+&quot;<!--#echo var="VAR_addressbook-formats"-->&quot; doesn't have as many elements as there
+are address books, the last element is used repeatedly.
+
+<P>
+
+Each of the tokens may also be optionally followed by parentheses with
+either a number or a percentage inside the parentheses. For example,
+<SAMP>FULLNAME(13)</SAMP> means to allocate 13 characters of space to
+the fullnames column, <SAMP>FULLNAME(20%)</SAMP> means to allocate 20%
+of the available space (the screen width minus the space for
+inter-column spaces) to the fullnames column, while plain
+<SAMP>FULLNAME</SAMP> means the system will attempt to figure out a
+reasonable number of columns.
+
+<P>
+There are always 2 spaces between every column, so if you use
+fixed column widths (like 13) you should remember to take that into
+account.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_set_index_format =====
+<HTML>
+<HEAD>
+<TITLE>Set Index Format</TITLE>
+</HEAD>
+<BODY>
+<H1>Set Index Format</H1>
+
+This option is used to customize the content of lines in the
+<A HREF="h_mail_index">MESSAGE INDEX screen</A>.
+This action works exactly like the regular
+&quot;<!--#echo var="VAR_index-format"-->&quot; option in the Setup/Config screen,
+except that you can have a folder-specific value for it if you specify it here.
+Consult the help for
+&quot;<A HREF="h_config_index_format"><!--#echo var="VAR_index-format"--></A>&quot;
+for more information.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_index_format =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_index-format"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_index-format"--></H1>
+
+This option is used to customize the content of lines in the
+<A HREF="h_mail_index">MESSAGE INDEX screen</A>. Each line is intended
+to convey some amount of immediately relevant information about each
+message in the current folder.
+<P>
+
+Alpine provides a pre-defined set of informational fields with
+reasonable column widths automatically computed. You can, however,
+replace this default set by listing special tokens in the order you
+want them displayed.
+<P>
+
+The list of available tokens is
+<A HREF="h_index_tokens">here</A>.
+<P>
+
+Spaces are used to separate listed tokens. Additionally, you can
+specify how much of the screen's width the token's associated data
+should occupy on the index line by appending to the token a pair of
+parentheses enclosing either a number or percentage. For example,
+&quot;SUBJECT(13)&quot; means to allocate 13 characters of space to the subject
+column, and &quot;SUBJECT(20%)&quot; means to
+allocate 20% of the available space
+to the subjects column, while plain &quot;SUBJECT&quot; means the system will
+attempt to figure out a reasonable amount of space.
+<P>
+
+There is always one space between every pair of columns, so if you use fixed
+column widths (like 13) you should remember to take that into account.
+Several of the fields are virtually fixed-width, so it doesn't make
+much sense to specify the width for them. The fields STATUS,
+FULLSTATUS, IMAPSTATUS, MSGNO, the DATE fields, SIZE,
+and DESCRIPSIZE all fall into that category.
+You <EM>may</EM> specify widths for those if you wish, but
+you're probably better off letting the system pick those widths. <P>
+
+<P>
+The default is equivalent to:
+
+<P>
+<CENTER><SAMP>STATUS MSGNO SMARTDATETIME24 FROMORTO(33%) SIZENARROW SUBJKEY(67%)</SAMP></CENTER>
+
+<P>
+This means that the four fields without percentages will be allocated
+first, and then 33% and 67% of the <EM>remaining</EM> space will go to
+the from and subject fields. If one of those two fields is specified
+as a percentage and the other is left for the system to choose, then
+the percentage is taken as an absolute percentage of the screen, not
+of the space remaining after allocating the first four columns. It
+doesn't usually make sense to do it that way. If you leave off all
+the widths, then the subject and from fields (if both are present) are
+allocated space in a 2 to 1 ratio, which is almost exactly the same as
+the default.
+
+<P>
+What you are most likely to do with this configuration option is to
+specify which fields appear at all, which order they appear in, and the
+percentage of screen that is used for the from and subject fields if you
+don't like the 2 to 1 default.
+
+<P>
+If you want to retain the default format that Pine 4.64 had, use
+
+<P>
+<CENTER><SAMP><!--#echo var="VAR_index-format"-->=STATUS MSGNO DATE FROMORTO(33%) SIZE SUBJKEY(67%)</SAMP></CENTER>
+<P>
+<EM>and</EM> set the feature
+<A HREF="h_config_disable_index_locale_dates"><!--#echo var="FEAT_disable-index-locale-dates"--></A>.
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_reply_intro =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_reply-leadin"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_reply-leadin"--></H1>
+
+This option is used to customize the content of the introduction line
+that is included when replying to a message and including the original
+message in the reply.
+The normal default (what you will get if you delete this variable) looks
+something like:
+<P>
+<CENTER><SAMP>On Sat, 24 Oct 1998, Fred Flintstone wrote:</SAMP></CENTER>
+<P>
+where the day of the week is only included if it is available in the
+original message.
+You can replace this default with text of your own.
+The text may contain tokens that are replaced with text
+that depends on the message you are replying to.
+For example, the default is equivalent to:
+<P>
+<CENTER><SAMP>On _DAYDATE_, _FROM_ wrote:</SAMP></CENTER>
+<P>
+
+Since this variable includes regular text mixed with special tokens
+the tokens have to be surrounded by underscore characters.
+For example, to use the token &quot;<SAMP>PREFDATE</SAMP>&quot;
+you would need to use &quot;<SAMP>_PREFDATE_</SAMP>&quot;,
+not &quot;<SAMP>PREFDATE</SAMP>&quot;.
+<P>
+The list of available tokens is
+<A HREF="h_index_tokens">here</A>.
+
+<P>
+By default, the text is all on a single line and is followed by a blank line.
+If your &quot;<!--#echo var="VAR_reply-leadin"-->&quot; turns out to be longer
+than 80 characters when replying to a particular message, it is shortened.
+However, if you use the token
+<P>
+<CENTER><SAMP>_NEWLINE_</SAMP></CENTER>
+<P>
+
+anywhere in the value, no end of line or blank line is appended, and no
+shortening is done.
+The _NEWLINE_ token may be used to get rid of the blank line following
+the text, to add more blank lines, or to form a multi-line
+&quot;<!--#echo var="VAR_reply-leadin"-->&quot;.
+To clarify how _NEWLINE_ works recall that the default value is:
+<P>
+<CENTER><SAMP>On _DAYDATE_, _FROM_ wrote:</SAMP></CENTER>
+<P>
+
+That is equivalent to
+<P>
+<CENTER><SAMP>On _DAYDATE_, _FROM_ wrote:_NEWLINE__NEWLINE_</SAMP></CENTER>
+<P>
+
+In the former case, two newlines are added automatically because
+no _NEWLINE_ token appears in the value of the option (for backwards
+compatibility). In the latter case, the newlines are explicit.
+If you want to remove the blank line that follows the
+&quot;<!--#echo var="VAR_reply-leadin"-->&quot; text use a single
+_NEWLINE_ token like
+<P>
+<CENTER><SAMP>On _DAYDATE_, _FROM_ wrote:_NEWLINE_</SAMP></CENTER>
+<P>
+
+Because of the backwards compatibility problem, it is not possible to
+remove all of the ends of lines, because then there will be no _NEWLINE_ tokens
+and that will cause the automatic adding of two newlines!
+If you want, you may embed newlines in the middle of the text, as well,
+producing a multi-line &quot;<!--#echo var="VAR_reply-leadin"-->&quot;.
+
+<P>
+By default, no attempt is made to localize the date.
+If you prefer a localized form you may find that one of the tokens
+_PREFDATE_ or _PREFDATETIME_ is a satisfactory substitute.
+If you want more control one of the many other date tokens, such as _DATEISO_,
+might be better.
+
+<P>
+For the adventurous, there is a way to conditionally include text based
+on whether or not a token would result in specific replacement text.
+For example, you could include some text based on whether or not
+the _NEWS_ token would result in any newsgroups if it was used.
+It's explained in detail
+<A HREF="h_reply_token_conditionals">here</A>.
+
+<P>
+In the very unlikely event that you want to include a literal token
+in the introduction line you must precede it with a backslash character.
+For example,
+<P>
+<CENTER><SAMP>&#92;_DAYDATE_ = _DAYDATE_</SAMP></CENTER>
+<P>
+would produce something like
+<P>
+<CENTER><SAMP>_DAYDATE_ = Sat, 24 Oct 1998</SAMP></CENTER>
+<P>
+It is not possible to have a literal backslash followed by an expanded token.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_remote_abook_history =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_remote-abook-history"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_remote-abook-history"--></H1>
+
+Sets how many extra copies of
+remote address book
+data will be kept in each remote address book folder.
+The default is three.
+These extra copies are simply old versions of the data. Each time a change
+is made a new copy of the address book data is appended to the folder. Old
+copies are trimmed, if possible, when Alpine exits.
+An old copy can be put back into use by
+deleting and expunging newer versions of the data from the folder.
+Don't delete the first message from the folder. It is a special header
+message for the remote address book and it must be there.
+This is to prevent regular folders from being used as remote address book
+folders and having their data destroyed.
+<P>
+This option is also used to determine how many extra copies of remote
+Alpine configuration files are kept.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_remote_abook_validity =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_remote-abook-validity"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_remote-abook-validity"--></H1>
+
+Sets the minimum number of minutes that a
+remote address book will be considered up to date.
+Whenever an entry contained in a remote address book is used,
+if more than this many minutes have
+passed since the last check the remote server will be queried to see if the
+address book has changed.
+If it has changed, the local copy is updated.
+The default value is five minutes.
+The special value of -1 means never check.
+The special value of zero means only check when the address book is first
+opened.
+<P>
+No matter what the value, the validity check is always done when the
+address book is about to be changed by the user.
+The check can be initiated manually by typing <EM>^L</EM> (Ctrl-L)
+while in the address book maintenance screen for the remote address book.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_user_input_timeo =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_user-input-timeout"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_user-input-timeout"--></H1>
+
+If this is set to an integer greater than zero, then this is the number
+of <EM>hours</EM> to wait for user input before Alpine times out.
+If Alpine is
+in the midst of composing a message or is waiting for user response to
+a question, then it will not timeout.
+However, if Alpine is sitting idle waiting for
+the user to tell it what to do next and the user does not give any
+input for this many hours, Alpine will exit.
+No expunging or moving of read
+messages will take place.
+It will exit similarly to the way it would exit
+if it received a hangup signal.
+This may be useful for cleaning up unused Alpine sessions that have been
+forgotten by their owners.
+The Alpine developers envision system administrators
+setting this to a value of several hours (24?) so that it won't surprise
+a user who didn't want to be disconnected.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_ssh_open_timeo =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_ssh-open-timeout"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_ssh-open-timeout"--></H1>
+
+Sets the time in seconds that Alpine will
+attempt to open a UNIX secure shell connection.
+The default is 15, the minimum non-zero value is 5,
+and the maximum is unlimited. If this is set to zero ssh connections
+will be completely disabled.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_rsh_open_timeo =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_rsh-open-timeout"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_rsh-open-timeout"--></H1>
+
+Sets the time in seconds that Alpine will
+attempt to open a UNIX remote shell connection.
+The default is 15, the minimum non-zero value is 5,
+and the maximum is unlimited. If this is set to zero rsh connections
+will be completely disabled.
+This might be useful if rsh connections will never work in your environment
+but are causing delays due to firewalls or some other reason.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_tcp_open_timeo =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_tcp-open-timeout"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_tcp-open-timeout"--></H1>
+
+Sets the time in seconds that Alpine will
+attempt to open a network connection. The default is 30, the minimum is 5,
+and the maximum is system defined (typically 75). If a connection has not
+completed within this many seconds Alpine will give up and consider it a
+failed connection.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_tcp_readwarn_timeo =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_tcp-read-warning-timeout"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_tcp-read-warning-timeout"--></H1>
+
+Sets the time in seconds that Alpine will
+wait for a network read before warning you that things are moving slowly
+and possibly giving you the option to break the connection.
+The default is 15 seconds. The minimum is 5 seconds and the maximumn is
+1000 seconds.
+<P>
+Related option: <A HREF="h_config_tcp_query_timeo"><!--#echo var="VAR_tcp-query-timeout"--></A>.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_tcp_writewarn_timeo =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_tcp-write-warning-timeout"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_tcp-write-warning-timeout"--></H1>
+
+Sets the time in seconds that Alpine will
+wait for a network write before warning you that things are moving slowly
+and possibly giving you the option to break the connection.
+The default is 0 which means it is unset. If set to a non-zero value, the
+minimum is 5 and the maximum is 1000.
+<P>
+Related option: <A HREF="h_config_tcp_query_timeo"><!--#echo var="VAR_tcp-query-timeout"--></A>.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_tcp_query_timeo =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_tcp-query-timeout"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_tcp-query-timeout"--></H1>
+
+When Alpine times out a network read or write it will normally just display
+a message saying &quot;Still waiting&quot;.
+However, if enough time has elapsed since it started waiting it will offer
+to let you break the connection.
+That amount of time is set by this option, which defaults to 60 seconds,
+has a minimum of 5 seconds, and a maximum of 1000 seconds.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_incoming_folders =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_incoming-folders"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_incoming-folders"--></H1>
+
+This is a list of one or more folders other than <EM>INBOX</EM> that
+may receive new messages.
+It is related to the
+<A HREF="h_config_enable_incoming">&quot;<!--#echo var="FEAT_enable-incoming-folders"-->&quot;</A>
+feature.
+This variable is normally manipulated with the Add, Delete, and Rename
+commands in the FOLDER LIST for the Incoming Message Folders collection.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_folder_spec =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_folder-collections"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_folder-collections"--></H1>
+
+This is a list of one or more collections where saved mail is stored.
+The first collection in this list is the default
+collection for <EM>Save</EM>s,
+including <A HREF="h_config_default_fcc"><!--#echo var="VAR_default-fcc"--></A>.
+<P>
+This variable is normally manipulated using the Setup/collectionList screen.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_news_spec =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_news-collections"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_news-collections"--></H1>
+
+This is a list of collections where news folders are located.
+<P>
+This variable is normally manipulated using the Setup/collectionList screen.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_address_book =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_address-book"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_address-book"--></H1>
+
+A list of personal address books.
+Each entry in the list is an
+optional nickname followed by a pathname or file name relative to the home
+directory.
+The nickname is separated from the rest of the line with whitespace.
+Instead of a local pathname or file name, a remote folder name can be given.
+This causes the address book to
+be a Remote address book.
+<P>
+Use the Setup/AddressBook screen to modify this variable.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_glob_addrbook =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_global-address-book"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_global-address-book"--></H1>
+
+A list of shared address books. Each entry in the list is an
+optional nickname followed by a pathname or file name relative to the home
+directory.
+A SPACE character separates the nickname from the rest of the line.
+Instead of a local pathname or file name, a remote folder name can be given.
+This causes the address book to
+be a Remote address book.
+Global address books are
+defined to be ReadOnly.
+<P>
+Use the Setup/AddressBook screen to modify this variable.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_last_vers =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_last-version-used"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_last-version-used"--></H1>
+
+This is set automatically by Alpine.
+It is used to keep track of the last version of Alpine that
+was run by the user.
+Whenever the version number increases, a new version message is printed out.
+This may not be set in the system-wide configuration files.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_printer =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Printer</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Printer</H1>
+
+Your default printer selection.
+<P>
+Use the Setup/Printer screen to modify this variable.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_print_cat =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_personal-print-category"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_personal-print-category"--></H1>
+
+This is an internal Alpine variable.
+It will be equal to 1, 2, or 3 depending on whether the default printer is
+attached, standard, or a personal print command.
+<P>
+Use the Setup/Printer screen to modify this variable.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_print_command =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_personal-print-command"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_personal-print-command"--></H1>
+
+List of personal print commands.
+<P>
+Use the Setup/Printer screen to modify this variable.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_pat_old =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Patterns</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Patterns</H1>
+
+The option Patterns is obsolete in Alpine and in Pine 4.50 and later, replaced by the
+options Patterns-Roles, Patterns-Filters, Patterns-Scores, Patterns-Indexcolors,
+and Patterns-Other.
+Patterns-Scores and Patterns-Filters have been replaced since then by
+Patterns-Scores2 and Patterns-Filters2.
+<P>
+Use the Setup/Rules screens to modify these variable.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_pat_roles =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_patterns-roles"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_patterns-roles"--></H1>
+
+List of rules used for roles.
+The option Patterns is obsolete in Alpine and in Pine 4.50 and later, replaced by this and
+other options.
+<P>
+Use the Setup/Rules/Roles screen to modify this variable.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_pat_filts =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_patterns-filters2"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_patterns-filters2"--></H1>
+
+List of rules used for filters.
+<P>
+Use the Setup/Rules/Filters screen to modify this variable.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_pat_scores =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_patterns-scores2"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_patterns-scores2"--></H1>
+
+List of rules used for scoring.
+<P>
+Use the Setup/Rules/SetScores screen to modify this variable.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_pat_other =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_patterns-other"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_patterns-other"--></H1>
+
+List of rules used for miscellaneous configuration.
+<P>
+Use the Setup/Rules/Other screen to modify this variable.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_pat_incols =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: patterns-indexcolors</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: patterns-indexcolors</H1>
+
+List of rules used for coloring lines in the index.
+<P>
+Use the Setup/Rules/Indexcolor screen to modify this variable.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_pat_srch =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: patterns-search</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: patterns-search</H1>
+
+List of rules used only for searching with the Select command in the MESSAGE INDEX.
+<P>
+Use the Setup/Rules/searCh screen to modify this variable.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_font_name =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Font Name</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Font Name</H1>
+
+PC-Alpine only.
+<P>
+Name of normal font.
+<P>
+Use the pulldown Config menu to modify this variable.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_font_size =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Font Size</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Font Size</H1>
+
+PC-Alpine only.
+<P>
+Size of normal font.
+<P>
+Use the pulldown Config menu to modify this variable.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_font_style =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Font Style</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Font Style</H1>
+
+PC-Alpine only.
+<P>
+Style of normal font.
+<P>
+Use the pulldown Config menu to modify this variable.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_font_char_set =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Font Character Set</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Font Character Set</H1>
+
+PC-Alpine only.
+<P>
+Character set of normal font.
+<P>
+Use the pulldown Config menu to modify this variable.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_print_font_name =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Print-Font-Name</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Print-Font-Name</H1>
+
+PC-Alpine only.
+<P>
+Name of printer font.
+<P>
+Use the pulldown Config menu to modify this variable.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_print_font_size =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Print-Font-Size</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Print-Font-Size</H1>
+
+PC-Alpine only.
+<P>
+Size of printer font.
+<P>
+Use the pulldown Config menu to modify this variable.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_print_font_style =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Print-Font-Style</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Print-Font-Style</H1>
+
+PC-Alpine only.
+<P>
+Style of printer font.
+<P>
+Use the pulldown Config menu to modify this variable.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_print_font_char_set =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Print-Font-Char-Set</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Print-Font-Char-Set</H1>
+
+PC-Alpine only.
+<P>
+Character set of printer font.
+<P>
+Use the pulldown Config menu to modify this variable.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_window_position =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Window-Position</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Window-Position</H1>
+
+PC-Alpine only.
+<P>
+Position on the screen of the Alpine window.
+<P>
+Alpine normally maintains this variable itself, and it is set automatically.
+This variable is provided to those who wish to use the same window position
+across different machines from the same configuration.
+<A HREF="h_config_winpos_in_config"><!--#echo var="FEAT_store-window-position-in-config"--></A>
+must also be set for this setting to be used.
+<P>
+The format for this variable is of the form: <CODE>CxR+X+Y</CODE>, where
+C is the number of columns, R is the number of rows, and X and Y specify the
+top left corner of the window.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_cursor_style =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Cursor Style</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Cursor Style</H1>
+
+PC-Alpine only.
+<P>
+Cursor style.
+<P>
+Use the pulldown Config menu to modify this variable.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_ldap_servers =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_ldap-servers"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_ldap-servers"--></H1>
+
+List of LDAP servers and associated data.
+<P>
+Use the Setup/Directory screen to modify this variable.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_sendmail_path =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_sendmail-path"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_sendmail-path"--></H1>
+
+This names the path to an
+alternative program, and any necessary arguments, to be used in posting
+mail messages. See the Technical notes for more information.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_oper_dir =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_operating-dir"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_operating-dir"--></H1>
+
+This names the root of the
+tree to which you are restricted when reading and writing folders and
+files. It is usually used in the system-wide,
+<EM>fixed</EM> configuration file.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_rshpath =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_rsh-path"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_rsh-path"--></H1>
+
+Sets the name of the command used to open a UNIX remote shell
+connection. The default is typically <CODE>/usr/ucb/rsh</CODE>.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_rshcmd =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_rsh-command"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_rsh-command"--></H1>
+
+Sets the format of the command used to
+open a UNIX remote shell connection. The default is
+"%s %s -l %s exec /etc/r%sd". All four "%s" entries MUST exist in the
+provided command. The first is for the command's pathname, the second is
+for the host to connnect to, the third is for the user to connect as, and
+the fourth is for the connection method (typically <CODE>imap</CODE>).
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_sshpath =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_ssh-path"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_ssh-path"--></H1>
+
+Sets the name of the command used to open a UNIX secure shell
+connection. The default is typically <CODE>/usr/bin/ssh</CODE>.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_sshcmd =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_ssh-command"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_ssh-command"--></H1>
+
+Sets the format of the command used to
+open a UNIX secure shell connection. The default is
+"%s %s -l %s exec /etc/r%sd". All four "%s" entries MUST exist in the
+provided command. The first is for the command's pathname, the second is
+for the host to connnect to, the third is for the user to connect as, and
+the fourth is for the connection method (typically <CODE>imap</CODE>).
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_new_ver_quell =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_new-version-threshold"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_new-version-threshold"--></H1>
+
+When a new version of Alpine is run for the first time it offers a
+special explanatory screen to the user upon startup. This option
+helps control when and if that special screen appears for users that
+have previously run Alpine. It takes as its value an Alpine version
+number. Alpine versions less than the specified value will supress this
+special screen while versions equal to or greater than that specified
+will behave normally.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_disable_drivers =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_disable-these-drivers"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_disable-these-drivers"--></H1>
+
+This variable is a list of mail drivers that will be disabled.
+The candidates for disabling are listed below.
+There may be more in the future if you compile Alpine with
+a newer version of the c-client library.
+<P>
+
+<UL>
+<LI> mbox
+<LI> mbx
+<LI> mh
+<LI> mmdf
+<LI> mtx
+<LI> mx
+<LI> news
+<LI> phile
+<LI> tenex
+<LI> unix
+</UL>
+<P>
+
+The <EM>mbox</EM> driver enables the following behavior: if there is a
+file called <CODE>mbox</CODE>
+in your home directory, and if that file is either empty or in Unix mailbox
+format, then every time you open <EM>INBOX</EM> the <EM>mbox</EM> driver
+will automatically transfer mail from the system mail spool directory into the
+<CODE>mbox</CODE> file and
+delete it from the spool directory. If you disable the <EM>mbox</EM> driver,
+this will not happen.
+<P>
+
+It is not recommended to disable the driver that supports the system default
+mailbox format. On most non-SCO systems, that driver is the
+<EM>unix</EM> driver.
+On most SCO systems, it is the <EM>mmdf</EM> driver.
+The system default driver may be
+configured to something else on your system; check with your system manager
+for additional information.
+<P>
+
+It is most likely not very useful for you to disable any of the drivers other
+than possibly <EM>mbox</EM>.
+You could disable some of the others if you know for
+certain that you don't need them but the performance gain in doing so
+is very modest.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_disable_auths =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_disable-these-authenticators"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_disable-these-authenticators"--></H1>
+
+This variable is a list of SASL (Simple Authentication and Security
+Layer) authenticators that will be disabled.
+SASL is a mechanism for
+authenticating to IMAP, POP3, SMTP, and other network servers.
+<P>
+
+Alpine matches its list of supported authenticators with the server to
+determine the most secure authenticator that is supported by both.
+If no matching authenticators are found, Alpine will revert to plaintext
+login (or, in the case of SMTP, will be unable to authenticate at all).
+<P>
+The candidates for disabling can be found <A HREF="X-Alpine-Config:">here</A>.
+<P>
+
+Normally, you will not disable any authenticators.
+There are two exceptions:
+<P>
+<OL>
+<LI> You use a broken server that advertises an authenticator,
+but does not actually implement it.
+<LI> You have a Kerberos-capable version of Alpine and the server is
+also Kerberos-capable, but you can not obtain Kerberos
+credentials on the server machine, thus you desire to disable
+GSSAPI (which in turn disables Alpine's Kerberos support).
+</OL>
+<P>
+It is never necessary to disable authenticators, since Alpine will try
+other authenticators before giving up.
+However, disabling the relevant authenticator avoids annoying error messages.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_abook_metafile =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_remote-abook-metafile"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_remote-abook-metafile"--></H1>
+
+This is usually set by Alpine and is the name of a file
+that contains data about
+remote address books and remote configuration files.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_composer_wrap_column =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_composer-wrap-column"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_composer-wrap-column"--></H1>
+
+
+This option specifies an aspect of Alpine's Composer. This gives the
+maximum width that auto-wrapped lines will have. It's also the maximum
+width of lines justified using the <A HREF="h_compose_justify">^J
+Justify</A> command. The normal default
+is &quot;74&quot;. The largest allowed setting is normally &quot;80&quot;
+in order to
+prevent very long lines from being sent in outgoing mail. When the mail
+is actually sent, trailing spaces will be stripped off of each line.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_deadlets =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_dead-letter-files"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_dead-letter-files"--></H1>
+
+
+This option affects Alpine's behavior when you cancel a message being
+composed. Alpine's usual behavior is to write the canceled message to
+a file named
+<!--chtml if pinemode="os_windows"-->
+&quot;DEADLETR&quot;,
+<!--chtml else-->
+&quot;dead.letter&quot; in your home directory,
+<!--chtml endif-->
+overwriting any previous message.
+<P>
+If you set this option to a value higher than one, then that many copies
+of dead letter files will be saved.
+For example, if you set this option to &quot;3&quot; then you may have
+files named
+<!--chtml if pinemode="os_windows"-->
+&quot;DEADLETR&quot;,
+&quot;DEADLETR2&quot;, and
+&quot;DEADLETR3&quot;.
+<!--chtml else-->
+&quot;dead.letter&quot;,
+&quot;dead.letter2&quot;, and
+&quot;dead.letter3&quot; in your home directory.
+<!--chtml endif-->
+In this example, the most recently cancelled message will be in
+<!--chtml if pinemode="os_windows"-->
+&quot;DEADLETR&quot;,
+<!--chtml else-->
+&quot;dead.letter&quot;,
+<!--chtml endif-->
+and the third most recently cancelled message will be in
+<!--chtml if pinemode="os_windows"-->
+&quot;DEADLETR3&quot;.
+<!--chtml else-->
+&quot;dead.letter3&quot;.
+<!--chtml endif-->
+The fourth most recently cancelled message will no longer be saved.
+
+<P>
+If you set this option to zero, then NO record of canceled messages is
+maintained.
+<P>
+If the feature
+<A HREF="h_config_quell_dead_letter"><!--#echo var="FEAT_quell-dead-letter-on-cancel"--></A>
+is set, that overrides whatever you set for this option.
+If this option had existed at the time, then the Quell feature would not
+have been added, but it is still there for backwards compatibility.
+So, in order for this option to have the desired effect, make sure the
+Quell feature is turned off.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_maxremstream =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_max-remote-connections"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_max-remote-connections"--></H1>
+
+This option affects low-level behavior of Alpine.
+The default value for this option is <EM>3</EM>.
+If your INBOX is accessed using the IMAP protocol
+from an IMAP server, that connection is kept open throughout the
+duration of your Alpine session, independent of the value of this option.
+The same is true of any
+<A HREF="h_config_permlocked">&quot;<!--#echo var="VAR_stay-open-folders"-->&quot;</A>
+you have defined.
+This option controls Alpine's behavior when connecting to remote IMAP folders
+other than your INBOX or your <!--#echo var="VAR_stay-open-folders"-->.
+It specifies the maximum number of remote IMAP connections (other than
+those mentioned above) that Alpine will use for accessing the rest of your
+folders.
+If you set this option to zero, you will turn off most remote connection
+re-use.
+It's difficult to understand exactly what this option does, and it is usually
+fine to leave it set to its default value.
+It is probably more likely that you will be interested in setting the
+<A HREF="h_config_permlocked">&quot;<!--#echo var="VAR_stay-open-folders"-->&quot;</A> option
+instead of changing the value of this option.
+A slightly longer explanation of what is going on with this option
+is given in the next paragraphs.
+
+<P>
+There are some time costs involved in opening and closing remote IMAP
+folders, the main costs being the time you have to wait for the connection
+to the server and the time for the folder to open.
+Opening a folder may involve not only the time the server takes to do its
+processing but time that Alpine uses to do filtering.
+These times can vary widely.
+They depend on how loaded the server is, how large
+the folder being opened is, and how you set up filtering, among other things.
+Once Alpine has opened a connection to a particular folder, it will attempt
+to keep that connection open in case you use it again.
+In order to do this,
+Alpine will attempt to use the <!--#echo var="VAR_max-remote-connections"--> (the value of
+this option) IMAP connections you have alloted for this purpose.
+<P>
+For example, suppose the value of this option is set to &quot;2&quot;.
+If your INBOX is accessed on a remote server using the IMAP protocol, that
+doesn't count as one of the remote connections but it is always kept open.
+If you then open another IMAP folder, that would be your first
+remote connection counted as one of the <!--#echo var="VAR_max-remote-connections"--> connections.
+If you open a third folder the second will be left open, in case you
+return to it.
+You won't be able to tell it has been left open.
+It will appear to be closed when you leave the folder but the connection
+will remain in the background.
+Now suppose you go back to the second folder (the first folder after the
+INBOX).
+A connection to that folder is still open so you won't have to wait
+for the startup time to open it.
+Meanwhile, the connection to the third folder will be left behind.
+Now, if you open a fourth folder, you will bump into the
+<!--#echo var="VAR_max-remote-connections"--> limit, because this will be the third folder other
+than INBOX and you have the option set to &quot;2&quot;.
+The connection that is being used for
+the third folder will be re-used for this new fourth folder.
+If you go back to the third folder after this, it is no longer already
+connected when you get there.
+You'll still save some time since Alpine will re-use the connection to the
+fourth folder and you have already logged in on that connection,
+but the folder will have to be re-opened from scratch.
+<P>
+If a folder is large and the startup cost is dominated by the time it takes
+to open that folder or to run filters on it, then it will pay to make the
+value of this option large enough to keep it open.
+On the other hand, if you only revisit a handful of folders or if
+the folders are small, then it might
+make more sense to keep this number small so that the reconnect
+time (the time to start up a new connection and authenticate)
+is eliminated instead.
+<P>
+You may also need to consider the impact on the server.
+On the surface, a larger number here may cause a larger impact on the
+server, since you will have more connections open to the server.
+On the other hand, not only will <EM>you</EM> be avoiding the startup costs
+associated with reopening a folder, but the <EM>server</EM> will be
+avoiding those costs as well.
+<P>
+When twenty five minutes pass without any active use of an IMAP connection
+being saved for possible re-use, that connection will be shut down,
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_permlocked =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_stay-open-folders"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_stay-open-folders"--></H1>
+
+This option affects low-level behavior of Alpine.
+There is no default value for this option.
+It is related to the options
+<A HREF="h_config_preopen_stayopens">&quot;<!--#echo var="FEAT_preopen-stayopen-folders"-->&quot;</A>,
+<A HREF="h_config_maxremstream">&quot;<!--#echo var="VAR_max-remote-connections"-->&quot;</A>,
+and <A HREF="h_config_expunge_stayopens">&quot;<!--#echo var="FEAT_offer-expunge-of-stayopen-folders"-->&quot;</A>.
+
+<P>
+Note: changes made to this list take effect the next time you open a
+folder in the list.
+
+<P>
+This is a list of folders that will be permanently kept open once they
+are first opened.
+The names in this list may be either the nickname of an Incoming folder
+or the full technical specification of a folder.
+The folders in this list need not be remote IMAP folders, they could usefully
+be local folders, as well.
+If a folder in the list is a newsgroup or is not accessed either locally
+or via IMAP, then the entry will be ignored.
+For example, folders accessed via NNTP or POP3 will not be kept open, since
+the way that new mail is found with those protocols involves closing and
+reopening the connection.
+<P>
+Once a Stay Open folder has been opened, new-mail checking will continue
+to happen on that folder for the rest of the Alpine session.
+Your INBOX is always implicitly included in this Stay-Open list and doesn't
+need to be added explicitly.
+<P>
+Another difference that you may notice between a Stay Open folder and a
+non-Stay Open folder is which message is selected as the current message
+when you enter the folder index.
+Normally, the starting position for an incoming folder (which most Stay Open
+folders will likely be) is controlled by the
+<A HREF="h_config_inc_startup"><!--#echo var="VAR_incoming-startup-rule"--></A>.
+However, if a folder is a Stay Open folder, when you re-enter the folder
+after the first time the current message will be the same as it was when
+you left the folder.
+An exception is made if you use the TAB command to get to the folder.
+In that case, the message number will be incremented by one from what it
+was when you left the folder.
+<P>
+The above special behavior is thought to be useful.
+However, it is special and different from what you might at first expect.
+The feature
+<A HREF="h_config_use_reg_start_for_stayopen"><!--#echo var="FEAT_use-regular-startup-rule-for-stayopen-folders"--></A>
+may be used to turn off this special treatment.
+<P>
+If the message that was current when you left the folder no longer exists,
+then the regular startup rule will be used instead.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_viewer_overlap =====
+<html>
+<header>
+<title>OPTION: <!--#echo var="VAR_viewer-overlap"--></title>
+</header>
+<body>
+<h1>OPTION: <!--#echo var="VAR_viewer-overlap"--></h1>
+
+This option specifies an aspect of Alpine's Message Viewing screen. When
+the space bar is used to page forward in a message, the number of lines
+specified by the &quot;<!--#echo var="VAR_viewer-overlap"-->&quot; variable will be repeated from the
+bottom of the screen. That is, if this was set to two lines, then the
+bottom two lines of the screen would be repeated on the top of the next
+screen. The normal default value is "2".<p>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</body>
+</html>
+====== h_config_scroll_margin =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_scroll-margin"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_scroll-margin"--></H1>
+
+This option controls when Alpine's line-by-line scrolling occurs.
+Typically, when a selected item is at the top or bottom screen edge
+and the UP or DOWN (and Ctrl-P or Ctrl-N) keys are struck, the
+displayed items are scrolled down or up by a single line.
+
+<P>
+This option allows you to tell Alpine the number of lines from the top and
+bottom screen edge that line-by-line paging should occcur. For example,
+setting this value to one (1) will cause Alpine to scroll the display
+vertically when you move to select an item on the display's top or
+bottom edge.
+
+<P>
+By default, this variable is zero, indicating that scrolling happens
+when you move up or down to select an item immediately off the display's
+top or bottom edge.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_wordseps =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_composer-word-separators"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_composer-word-separators"--></H1>
+
+This option affects how a &quot;word&quot; is defined in the composer.
+The definition of a word is used when using the Forward Word and Backward
+Word commands in the composer, as well as when using the spell checker.
+Whitespace is always considered a word separator.
+Punctuation (like question marks, periods, commas, and so on) is always
+a word separator if it comes at the end of a word.
+By default, a punctuation character that is in the middle of a word does
+not break up that word as long as the character before and the character
+after it are both alphanumeric.
+If you add a character to this option it will be considered a
+word separator even when it occurs in the middle of an alphanumeric word.
+For example, if you want to skip through each part of an address instead
+of skipping the whole address at once you might want to include &quot;@&quot;
+and &quot;.&quot; in this list.
+If you want the word-skipper to stop on each part of a UNIX filename you
+could add &quot;/&quot; to the list.
+The equal sign and dash are other possibilities you might find helpful.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_reply_indent_string =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_reply-indent-string"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_reply-indent-string"--></H1>
+
+This option specifies an aspect of Alpine's Reply command.
+When a message is replied to and the text of the message is included, the
+included text usually has the string &quot;&gt;&nbsp;&quot; prepended
+to each line indicating it is quoted text.
+(In case you haven't seen this before, &quot;string&quot; is a technical term
+that means chunk of text.)
+
+<P>
+Because of the introduction of <A HREF="h_config_quell_flowed_text">Flowed Text</A>
+in 1999 and its wide-spread adoption since then, you will usually be better off if you
+use one of the standard values,
+&quot;&gt;&nbsp;&quot; or &quot;&gt;&quot;, for this option.
+
+<P>
+This option specifies a different value for that string.
+If you wish to use a string that begins or ends with a space,
+enclose the string in double quotes.
+
+<P>
+Besides simple text, the prepended string can be based
+on the message being replied to.
+The following tokens are substituted for the message's corresponding value:
+
+<DL>
+<DT>_FROM_</DT>
+<DD>This token gets replaced with the message sender's &quot;username&quot;.
+If the name is longer than six characters, only the first six characters are
+used.
+</DD>
+
+<DT>_NICK_</DT>
+<DD>This token gets replaced with the nickname of the message sender's
+address as found in your addressbook.
+If no addressbook entry is found,
+Alpine replaces the characters &quot;_NICK_&quot; with nothing.
+If the nickname is longer than six characters, only the first six characters are
+used.
+</DD>
+
+<DT>_INIT_</DT>
+<DD>This token gets replaced with the initials of the sender of the message.
+</DD>
+
+</DL>
+
+NOTE: When the
+<A HREF="h_config_prefix_editing">&quot;<!--#echo var="FEAT_enable-reply-indent-string-editing"-->&quot;</A>
+feature is enabled, you are given the opportunity to edit the string, whether
+it is the default or one automatically generated using the above tokens.
+<P>
+If you change your <!--#echo var="VAR_reply-indent-string"-->
+so that it is not equal to the default value of &quot;&gt;&nbsp;&quot;, then
+quoted text will not be flowed
+(<A HREF="h_config_quell_flowed_text">Flowed Text</A>)
+when you reply.
+For this reason, we recommend that you leave your <!--#echo var="VAR_reply-indent-string"-->
+set to the default value.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_quote_replace_string =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_quote-replace-string"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_quote-replace-string"--></H1>
+
+This option specifies what string to use as a quote when <b>viewing</b> a
+message. The standard way of quoting messages when replying is the string
+&quot;&gt;&nbsp;&quot; (quote space).
+With this variable set, viewing a message will
+replace occurrences of
+&quot;&gt;&nbsp;&quot; and &quot;&gt;&quot; with the replacement string.
+This setting works best when
+<A HREF="h_config_reply_indent_string"><!--#echo var="VAR_reply-indent-string"--></A>
+or the equivalent setting in your correspondents' mail programs
+is set to the default
+&quot;&gt;&nbsp;&quot;, but it will also work fine with the
+<!--#echo var="VAR_reply-indent-string"--> set to &quot;&gt;&quot;.
+<P>
+By default, this setting will only work on messages that are flowed, which is
+the default way of sending messages for many mail clients including
+Alpine. Enable the feature
+<A HREF="h_config_quote_replace_noflow"><!--#echo var="FEAT_quote-replace-nonflowed"--></A>
+to also have quote-replacement performed on non-flowed messages.
+<P>
+
+Setting this option will replace &quot;&gt;&quot; and
+&quot;&gt;&nbsp;&quot; with the new setting. This string may include trailing
+spaces. To preserve those spaces enclose the full string in double quotes.
+<P>
+No padding to separate the text of the message from the quote string is
+added. This means that if you do not add trailing spaces to the value of
+this variable, text will be displayed right next to the quote string,
+which may be undesirable. This can be avoided by adding a new string
+separated by a space from your selection of quote string replacement. This
+last string will be used for padding. For example, setting this variable to
+<br>&quot;&gt;&quot; &quot; &quot; has the effect of setting
+&quot;&gt;&quot; as the <!--#echo var="VAR_quote-replace-string"-->, with the text padded by
+a space from the last quote string to make it more readable.
+<P>
+One possible setting for this variable could be
+&quot;&nbsp;&nbsp;&nbsp;&nbsp;&quot; (four spaces wrapped in quotes), which
+would have the effect of indenting each level of quoting four spaces and
+removing the &quot;&gt;&quot;'s. Different levels of quoting could be made
+more discernible by setting colors for quoted text.
+<P>
+Replying to or forwarding the viewed message will preserve the original
+formatting of the message, so quote-replacement will not be performed on
+messages that are being composed.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_empty_hdr_msg =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_empty-header-message"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_empty-header-message"--></H1>
+
+When sending, if both the To and Cc fields are empty and you
+are sending the message to a Bcc,
+Alpine will put a special address in the To line. The default value is:
+
+<P>
+<CENTER><SAMP>undisclosed-recipients:&nbsp;;</SAMP></CENTER>
+
+<P>
+The reason for this is to avoid embarrassment caused by some Internet
+mail transfer software that interprets a &quot;missing&quot;
+<SAMP>To:</SAMP> header as an error and replaces it with an
+<SAMP>Apparently-to:</SAMP> header that may contain the addresses you
+entered on the <SAMP>Bcc:</SAMP> line, defeating the purpose of the
+Bcc. You may change the part of this message that comes before the
+&quot;:&nbsp;;&quot; by setting the &quot;<!--#echo var="VAR_empty-header-message"-->&quot;
+variable to something else.
+
+<P>
+The normal default is &quot;undisclosed-recipients&quot;.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_status_msg_delay =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_status-message-delay"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_status-message-delay"--></H1>
+
+This option has evolved over time, causing the possible values to be
+counter-intuitive.
+Read carefully before you set this option.
+First we explain what the option does, then there is a longer discussion
+following that.
+<P>
+If this is set to zero, the default value, it has <EM>no</EM> effect.
+Positive and negative values serve two similar, but different purposes.
+<P>
+If it is set to a positive number, it causes the cursor to move to the
+status line whenever a status message is printed and pause there for this
+many seconds.
+It will probably only be useful if the
+<A HREF="h_config_show_cursor">&quot;<!--#echo var="FEAT_show-cursor"-->&quot;</A> feature is
+also turned on.
+Setting this option to a positive number can only be used to
+<EM>increase</EM> the status message delay.
+This may be useful for Braille displays, or other non-traditional displays.
+<P>
+If it is set to a negative number the interpretation is a bit complicated.
+Negative numbers are used to <EM>decrease</EM> the amount of delay Alpine uses to
+allow you to read important status messages.
+Of course, this may cause you to miss some important messages.
+If you see a message flash by but miss what it says you can use the
+Journal command from the MAIN MENU to read it.
+If you set this option to a negative value, the delay will be
+no more than one second less than the absolute value
+of the value you set.
+So if you set it to -1, the delay will be no more than zero seconds, no
+delay at all.
+If you set it to -2, the delay will be no more than 1 second.
+And so on, -3 is 2 seconds, -4 is 3 seconds, ...
+If the delay that Alpine would have used by default is less than this delay,
+then the smaller delay set by Alpine will be used.
+Setting this option to a negative value can only reduce the amount of
+delay, never increase it.
+<P>
+Here is a more detailed explanation.
+Status messages are the messages that show up spontaneously in the
+status message line, usually the third line from the bottom of the screen.
+By default, Alpine assigns each status message it produces a minimum
+display time.
+Some status messages have a minimum display time of zero.
+You can see an example of such a message by paging up in this help text
+until you reach the top of the screen.
+If you try to page past the top you will see the message
+<P>
+<CENTER><SAMP>[Already at start of help text]</SAMP></CENTER>
+<P>
+in the status line.
+If there is another more important use of the status message line this message
+might be replaced quickly, or it even might not be shown at all.
+However, if there is no reason to get rid of the message, it might stay
+there for several seconds while you read the help.
+An example where it is replaced immediately happens when you page up in
+the help text past the top of the screen, but then type the &quot;WhereIs&quot;
+command right after paging up.
+The message will disappear immediately without causing a delay (unless you
+have set this option to a positive value) to allow you to type input for
+the &quot;WhereIs&quot; command.
+Since it isn't a very important message, Alpine has set its minimum display
+time to zero seconds.
+<P>
+Other messages have minimum display times of three or more seconds.
+These are usually error messages that Alpine thinks you ought to see.
+For example, it might be a message about a failed Save or a failed folder open.
+It is often the case that this minimum display time won't delay you in
+any way because the status message line is not needed for another reason.
+However, there are times when Alpine has to delay what it is doing in
+order to display a status message for the minimum display time.
+This happens when a message is being displayed and Alpine wants to ask
+for input from the keyboard.
+For example, when you Save a message you use the status message line.
+You get a prompt there asking for the name of the folder to save to.
+If there is a status message being displayed that has not
+yet displayed for its minimum
+time Alpine will display that status message surrounded with the characters
+&gt; and &lt; to show you that it is delaying.
+That might happen, for example, if you tried to save to a folder that
+caused an error, then followed that immediately with another Save command.
+You might find yourself waiting for a status message like
+<P>
+<CENTER><SAMP>[&gt;Can't get write access to mailbox, access is readonly&lt;]</SAMP></CENTER>
+<P>
+to finish displaying for three seconds.
+If that is something you find happening to you frequently, you may use
+negative values of this option to decrease or eliminate that delay, at
+the risk of missing the message.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_active_msg_interval =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_busy-cue-rate"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_busy-cue-rate"--></H1>
+
+When Alpine is delayed for some reason it usually shows that
+something is happening with a small animated display in the status
+message line near the bottom of the screen.
+This option sets how frequently the characters (for example, a spinning bar)
+in the active status message lines are updated.
+At most, it can be set to be udpated 20 times per second.
+
+<P>
+Setting this value to zero will prevent display of the animations
+altogether.
+
+<P>
+The option <A HREF="h_config_use_boring_spinner"><!--#echo var="FEAT_busy-cue-spinner-only"--></A>
+can be used to remove the randomness from this animated display.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_mailchecknoncurr =====
+<HTML>
+<HEADER>
+<TITLE>OPTION: <!--#echo var="VAR_mail-check-interval-noncurrent"--></TITLE>
+</HEADER>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_mail-check-interval-noncurrent"--></H1>
+
+This option is closely related to the
+<A HREF="h_config_mailcheck">&quot;<!--#echo var="VAR_mail-check-interval"-->&quot;</A>
+option, as well as the
+<A HREF="h_config_quell_checks_comp">&quot;<!--#echo var="FEAT_quell-mailchecks-composing-except-inbox"-->&quot;</A> and
+<A HREF="h_config_quell_checks_comp_inbox">&quot;<!--#echo var="FEAT_quell-mailchecks-composing-inbox"-->&quot;</A> options.
+If the &quot;<!--#echo var="VAR_mail-check-interval"-->&quot; option is set to zero, then automatic
+new-mail checking is disabled and this option will have no effect.
+<P>
+Normally this option is set to zero, which means that the value used will be
+the same as the value for the &quot;<!--#echo var="VAR_mail-check-interval"-->&quot;.
+If you set this option to a value different from zero
+(usually larger than the value for &quot;<!--#echo var="VAR_mail-check-interval"-->&quot;)
+then that is the check interval that will be used
+for folders that are not the currently open folder or the INBOX.
+You may not even have any folders that are noncurrent and not the INBOX.
+If you do, it is likely that they are due to
+<A HREF="h_config_permlocked">&quot;<!--#echo var="VAR_stay-open-folders"-->&quot;</A>
+you have configured.
+This option also affects the rate of mail checking done on cached
+connections to folders you previously had open but are no longer actively
+using.
+You aren't expected to understand that last sentence, but if you are interested
+take a look at
+<A HREF="h_config_maxremstream">&quot;<!--#echo var="VAR_max-remote-connections"-->&quot;</A>
+and the related options.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_fifopath =====
+<HTML>
+<HEADER>
+<TITLE>OPTION: NewMail FIFO Path</TITLE>
+</HEADER>
+<BODY>
+<H1>OPTION: NewMail FIFO Path</H1>
+
+This option is only available in UNIX Alpine.
+However, there is a very similar feature built in to PC-Alpine.
+In PC-Alpine's Config menu at the top of the screen
+is an option called &quot;New Mail Window&quot;.
+<P>
+You may have Alpine create a FIFO special file (also called a named pipe) where
+it will send a one-line message each time a new message is received in
+the current folder, the INBOX, or any open
+<A HREF="h_config_permlocked"><!--#echo var="VAR_stay-open-folders"--></A>.
+To protect against two different Alpines both writing to the same FIFO, Alpine
+will only create the FIFO and write to it if it doesn't already exist.
+<P>
+A possible way to use this option would be to have a separate window
+on your screen running the command
+<P>
+<CENTER><SAMP>cat filename</SAMP></CENTER>
+<P>
+where &quot;filename&quot; is the name of the file given for this option.
+Because the file won't exist until after you start Alpine, you must <EM>first</EM>
+start Alpine and <EM>then</EM> run the &quot;cat&quot; command.
+You may be tempted to use &quot;tail -f filename&quot; to view the new
+mail log.
+However, the common implementations of the tail command will not do what you
+are hoping.
+<P>
+The width of the messages produced for the FIFO may be altered with the
+<A HREF="h_config_newmailwidth"><!--#echo var="VAR_newmail-window-width"--></A> option.
+<P>
+On some systems, fifos may only be created in a local filesystem.
+In other words, they may not be in NFS filesystems.
+This requirement is not universal.
+If the system you are using supports it, it should work.
+(It is often the case that your home directory is in an NFS filesystem.
+If that is the case, you might try using a file in the &quot;/tmp&quot;
+filesystem, which is usually a local filesytem.)
+Even when it is possible to use an NFS-mounted filesystem as a place to name
+the fifo (for example, your home directory), it will still be the case that
+the reader (probably the &quot;cat&quot; command) and the
+writer (Alpine) of the fifo must be running on the same system.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_newmailwidth =====
+<HTML>
+<HEADER>
+<TITLE>OPTION: <!--#echo var="VAR_newmail-window-width"--></TITLE>
+</HEADER>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_newmail-window-width"--></H1>
+
+For UNIX Alpine, this option is only useful if you have turned on the
+<A HREF="h_config_fifopath">NewMail FIFO Path</A> option.
+That option causes new mail messages to be sent to a fifo file.
+Those messages will be 80 characters wide by default.
+You can change the width of those messages by changing this option.
+For example, if you are reading those messages in another window you might
+want to set this width to the width of that other window.
+<P>
+If you are using PC-Alpine, it has an option in the Config menu to turn
+on the &quot;New Mail Window&quot;.
+This present option also controls the width of that window.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_mailcheck =====
+<HTML>
+<HEADER>
+<TITLE>OPTION: <!--#echo var="VAR_mail-check-interval"--></TITLE>
+</HEADER>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_mail-check-interval"--></H1>
+
+This option specifies, in seconds,
+how often Alpine will check for new mail.
+If set to zero, new-mail checking is disabled.
+(You can always manually force a new-mail check by typing ^L (Ctrl-L), which is also the command to refresh the screen, or by typing the Next command when the
+current message is the last message of the folder.)
+There is a minimum value for this option, normally 15 seconds.
+The default value is normally 150 seconds.
+The higher you set this option, the easier it is on the server.
+<P>
+There are some situations where automatic new-mail checking does not work.
+See the discussion about new-mail checking in
+<A HREF="h_config_reopen_rule">&quot;<!--#echo var="VAR_folder-reopen-rule"-->&quot;</A>.
+<P>
+The new-mail checking will not happen exactly at the frequency that you specify.
+For example, Alpine may elect to defer a non-INBOX mail check if you
+are busy typing.
+Or, it may check more frequently than you have specified if that is
+thought to be necessary to keep the server from closing the connection
+to the folder due to inactivity.
+If Alpine checks for new mail as a side effect of another command, it will reset
+the timer, so that new-mail checking may seem to happen irregularly instead of
+every X seconds like clockwork.
+<P>
+If you are anxious to know about new mail as soon as possible, set the check
+interval low, and you'll know about the new mail by approximately
+that amount of time after it arrives.
+If you aren't so worried about knowing right away, set this option to a
+higher value.
+That will save the server some processing time and may save you some of
+the time you spend waiting for new-mail checks to happen if you are
+dealing with a slow server or slow network connection.
+<P>
+If you suspect that new-mail checking is causing slow downs for you,
+you may want to look into the options
+<A HREF="h_config_quell_checks_comp">&quot;<!--#echo var="FEAT_quell-mailchecks-composing-except-inbox"-->&quot;</A>,
+<A HREF="h_config_quell_checks_comp_inbox">&quot;<!--#echo var="FEAT_quell-mailchecks-composing-inbox"-->&quot;</A> and
+<A HREF="h_config_mailchecknoncurr">&quot;<!--#echo var="VAR_mail-check-interval-noncurrent"-->&quot;</A>,
+which refine when mail checking is done.
+<P>
+If the mailbox being checked uses a <A HREF="h_maildrop">Mail Drop</A> then
+there is a minimum time
+(<A HREF="h_config_maildropcheck">&quot;<!--#echo var="VAR_maildrop-check-minimum"-->&quot;</A>)
+between new-mail checks.
+Because of this minimum you may notice that new mail does not
+appear promptly when you expect it.
+The reason for this is to protect the server from over-zealous opening and
+closing of the Mail Drop folder, since that is a costly operation.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_quell_checks_comp =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_quell-mailchecks-composing-except-inbox"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_quell-mailchecks-composing-except-inbox"--></H1>
+
+This option is closely related to the
+<A HREF="h_config_mailcheck">&quot;<!--#echo var="VAR_mail-check-interval"-->&quot;</A>
+option, the
+<A HREF="h_config_mailchecknoncurr">&quot;<!--#echo var="VAR_mail-check-interval-noncurrent"-->&quot;</A> option, and
+<A HREF="h_config_quell_checks_comp_inbox">&quot;<!--#echo var="FEAT_quell-mailchecks-composing-inbox"-->&quot;</A>.
+<P>
+If this option is set, then the normal new-mail checking that happens
+while you are composing will not happen for folders other than your
+INBOX (which depends on the setting
+of &quot;<!--#echo var="FEAT_quell-mailchecks-composing-inbox"-->&quot;).
+<P>
+You might want to set this option if you are experiencing delays while
+composing that you think might be related to the speed of the new-mail
+checks.
+<P>
+Even with this option turned on, an occasional new-mail check may be done
+in order to keep the server from killing the connection to the folder.
+For example, IMAP servers may remove a connection to a folder if there
+has been no activity on the connection for 30 minutes or more.
+Instead of letting that happen, Alpine will check for new mail before the
+30 minutes is up even though you have turned on this feature to quell
+those checks.
+<P>
+Besides new-mail checks, checkpoint operations on the folders
+will also be quelled when you set this option.
+The purpose of checkpointing is to write the changes to a folder out to
+disk periodically in order to avoid losing those changes when system or
+software problems occur.
+New-mail checking and checkpointing while you are not composing are not
+affected by this option.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_quell_checks_comp_inbox =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_quell-mailchecks-composing-inbox"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_quell-mailchecks-composing-inbox"--></H1>
+
+This option is closely related to the
+<A HREF="h_config_mailcheck">&quot;<!--#echo var="VAR_mail-check-interval"-->&quot;</A>
+option, the
+<A HREF="h_config_mailchecknoncurr">&quot;<!--#echo var="VAR_mail-check-interval-noncurrent"-->&quot;</A> option, and
+<A HREF="h_config_quell_checks_comp">&quot;<!--#echo var="FEAT_quell-mailchecks-composing-except-inbox"-->&quot;</A>.
+<P>
+If this option is set, then the normal new-mail checking that happens
+while you are composing will not happen for your INBOX.
+Checking of other folders is controlled in a similar way with the
+&quot;<!--#echo var="FEAT_quell-mailchecks-composing-except-inbox"-->&quot; option.
+<P>
+You might want to set this option if you are experiencing delays while
+composing that you think might be related to the speed of the new-mail
+checks.
+<P>
+Even with this option turned on, an occasional new-mail check may be done
+in order to keep the server from killing the connection to the folder.
+For example, IMAP servers may remove a connection to a folder if there
+has been no activity on the connection for 30 minutes or more.
+Instead of letting that happen, Alpine will check for new mail before the
+30 minutes is up even though you have turned on this feature to quell
+those checks.
+<P>
+Besides new-mail checks, checkpoint operations on the INBOX
+will also be quelled when you set this option.
+The purpose of checkpointing is to write the changes to a folder out to
+disk periodically in order to avoid losing those changes when system or
+software problems occur.
+New-mail checking and checkpointing while you are not composing are not
+affected by this option.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_maildropcheck =====
+<HTML>
+<HEADER>
+<TITLE>OPTION: <!--#echo var="VAR_maildrop-check-minimum"--></TITLE>
+</HEADER>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_maildrop-check-minimum"--></H1>
+
+New-mail checking for a
+<A HREF="h_maildrop">Mail Drop</A> is a little different from new
+mail checking for a regular folder.
+One of the differences is that the connection to the Mail Drop is not
+kept open and so the cost of checking
+(delay for you and additional load for the server) may be significant.
+Because of this additional cost we set a minimum time that
+must pass between checks.
+This minimum only applies to the automatic checking done by Alpine.
+If you force a check by typing ^L (Ctrl-L) or by typing the Next command when you are
+at the end of a folder index, then the check is done right away.
+<P>
+This option specifies, in seconds, the <EM>minimum</EM> time between Mail Drop
+new-mail checks.
+You may want to set this minimum high in order to avoid experiencing some
+of the delays associated with the checks.
+Note that the time between checks is still controlled by the regular
+<A HREF="h_config_mailcheck"><!--#echo var="VAR_mail-check-interval"--></A> option.
+When Alpine is about to do an automatic check for new mail (because
+the <!--#echo var="VAR_mail-check-interval"--> has expired) then if the time since the last
+new-mail check
+of any open Mail Drops has been greater than the <!--#echo var="VAR_maildrop-check-minimum"-->,
+the Mail Drop is checked for new mail as well.
+Therefore, it is only useful to set this option to a value that is higher
+than the <!--#echo var="VAR_mail-check-interval"-->.
+<P>
+If this option is set to zero, automatic Mail Drop new-mail
+checking is disabled.
+There is a minimum value, normally 60 seconds.
+The default value is normally 60 seconds as well.
+This applies to the INBOX and to the currently open folder if that is
+different from the INBOX.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_nntprange =====
+<HTML>
+<HEADER>
+<TITLE>OPTION: <!--#echo var="VAR_nntp-range"--></TITLE>
+</HEADER>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_nntp-range"--></H1>
+
+This option applies only to newsgroups accessed using the NNTP protocol.
+It does not, for example,
+apply to newsgroups accessed using an IMAP-to-NNTP proxy.
+
+<P>
+When you open a connection to a News server using the NNTP protocol, you
+normally have access to all of the articles in each newsgroup.
+If a server keeps a large backlog of messages it may speed performance
+some to restrict attention to only the newer messages in a group.
+This option allows you to set how many article numbers should be checked
+when opening a newsgroup.
+You can think of &quot;<!--#echo var="VAR_nntp-range"-->&quot; as specifying the maximum number
+of messages you ever want to see.
+For example, if you only ever wanted to look at the last 500 messages in each
+newsgroup you could set this option to 500.
+In actuality, it isn't quite that.
+Instead, for performance reasons, it specifies the range of article
+numbers to be checked, beginning
+with the highest numbered article and going backwards from there.
+If there are messages that have been canceled or deleted
+their article numbers are still counted as part of the range.
+<P>
+So, more precisely, setting the &quot;<!--#echo var="VAR_nntp-range"-->&quot; will cause article
+numbers
+<P><CENTER>last_article_number - <!--#echo var="VAR_nntp-range"--> + 1 through last_article_number</CENTER>
+<P>
+to be considered when reading a newsgroup.
+The number of messages that show up in your index will be less than or equal
+to the value of &quot;<!--#echo var="VAR_nntp-range"-->&quot;.
+
+<P>
+The purpose of this option is simply to speed up access when reading news.
+The speedup comes because Alpine can ignore all but the last <!--#echo var="VAR_nntp-range"--> article
+numbers, and can avoid downloading any information about the ignored articles.
+There is a cost you pay for this speedup.
+That cost is that there is no way for you to see those ignored articles.
+The articles that come before the range you specify are invisible to you and
+to Alpine, as if they did not exist at all.
+There is no way to see those messages using, for example, an unexclude command
+or something similar.
+The only way to see those articles is to set this option high enough (or
+set it to zero) and then to reopen the newsgroup.
+
+<P>
+If this option is set to 0 (which is also the default),
+then the range is unlimited.
+This option applies globally to all NNTP servers and to all newsgroups
+on those servers.
+There is no way to set different values for different newsgroups or servers.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_news_active =====
+<html>
+<header>
+<title>OPTION: <!--#echo var="VAR_news-active-file-path"--></title>
+</header>
+<body>
+<h1>OPTION: <!--#echo var="VAR_news-active-file-path"--></h1>
+
+This option tells Alpine where to look for the "active file" for newsgroups
+when accessing news locally, rather than via NNTP. The default path is
+usually "/usr/lib/news/active".<p>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</body>
+</html>
+====== h_config_news_spool =====
+<html>
+<header>
+<title>OPTION: <!--#echo var="VAR_news-spool-directory"--></title>
+</header>
+<body>
+<h1>OPTION: <!--#echo var="VAR_news-spool-directory"--></h1>
+
+This option tells Alpine where to look for the "news spool" for newsgroups
+when accessing news locally, rather than via NNTP. The default path is
+usually "/var/spool/news".<p>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</body>
+</html>
+====== h_config_image_viewer =====
+<html>
+<header>
+<title>OPTION: <!--#echo var="VAR_image-viewer"--></title>
+</header>
+<body>
+<h1>OPTION: <!--#echo var="VAR_image-viewer"--></h1>
+<body>
+This option specifies the program Alpine should call to view MIME
+attachments of type IMAGE (e.g. GIF or TIFF). The Image Viewer setting is
+no longer needed, but remains for backward compatibility. The more
+general method for associating external printing and viewing programs with
+specific MIME data types is to use the system's (or your personal)
+"mailcap" configuration file.<p>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</body>
+</html>
+====== h_config_domain_name =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_use-only-domain-name"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_use-only-domain-name"--></H1>
+
+This option is used only if the
+<A HREF="h_config_user_dom">&quot;<!--#echo var="VAR_user-domain"-->&quot;</A> option is <B>not</B>
+set. If set to &quot;Yes&quot; (and <!--#echo var="VAR_user-domain"--> is not used), then Alpine
+strips the hostname from your return (&quot;From&quot;) address and when
+completing unqualified addresses that you enter into the composer.
+<P>
+If you set this, see also the <A HREF="h_config_quell_local_lookup">
+&quot;<!--#echo var="FEAT_quell-user-lookup-in-passwd-file"-->&quot;</A> feature.
+
+
+<!--chtml if pinemode="os_windows"-->
+<P>This option is not applicable to PC-Alpine.
+<!--chtml else-->
+<P>
+<!--chtml endif-->
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_prune_date =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Last-Time-Prune Question</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Last-Time-Prune Question</H1>
+
+This value records the last time you were asked about deleting old
+sent-mail.
+It is set automatically by Alpine at the beginning of each month.
+In the past, if you wished to suppress the monthly sent-mail
+pruning feature, you could set this to a date in the future.
+This value is relative to the year 1900, so
+to set this, for example, to October 2005, use 105.10.
+<P>
+You can still do that if you wish, or you can use the
+<A HREF="h_config_pruning_rule"><!--#echo var="VAR_pruning-rule"--></A> option, which is probably
+a little more convenient to use.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_goto_default =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_goto-default-rule"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_goto-default-rule"--></H1>
+
+This value affects Alpine's behavior when you use the Goto command.
+Alpine's usual behavior has two parts. If your current folder is
+&quot;Inbox&quot;, Alpine will offer the last open folder as the
+default. If the current folder is other than &quot;Inbox&quot;,
+&quot;Inbox&quot; is offered as the default.
+
+<P>
+The available options include:
+
+<DL>
+
+ <DT>folder-in-first-collection</DT>
+
+ <DD> Alpine will offer the most recently visited folder in the default
+collection found in the &quot;Collection List&quot; screen as the default.
+</DD>
+
+ <DT> inbox-or-folder-in-first-collection</DT>
+
+ <DD> If the current folder is &quot;Inbox&quot;,
+Alpine will offer the most recently visited folder in the
+default collection found in the &quot;Collection List&quot; screen.
+If the current folder is other than &quot;Inbox&quot;,
+&quot;Inbox&quot; is offered as the default.
+</DD>
+
+ <DT> inbox-or-folder-in-recent-collection</DT>
+
+ <DD> This is Alpine's default behavior.
+If the current folder is &quot;Inbox&quot;,
+Alpine will offer the last open
+folder as the default.
+If the current folder is other than &quot;Inbox&quot;,
+&quot;Inbox&quot; is offered as the default.
+</DD>
+
+ <DT> first-collection-with-inbox-default</DT>
+
+ <DD> Instead of offering the most recently visited folder in the default
+collection, the default collection is offered but with &quot;Inbox&quot; as
+the default folder.
+If you type in a folder name it will be in the default collection.
+If you simply accept the default, however, your &quot;Inbox&quot; will be opened.
+</DD>
+
+ <DT> most-recent-folder</DT>
+
+ <DD> The last accepted value simply causes the most recently opened
+folder to be offered as the default regardless of the currently opened
+folder.
+</DD>
+</DL>
+
+<P>
+NOTE: The default while a newsgroup is open remains the same; the last
+open newsgroup.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_thread_lastreply_char =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_threading-lastreply-character"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_threading-lastreply-character"--></H1>
+
+The <!--#echo var="VAR_threading-lastreply-character"--> option has a small effect on the MESSAGE
+INDEX display when using a
+<A HREF="h_config_thread_disp_style"><!--#echo var="VAR_threading-display-style"--></A>
+of &quot;show-thread-structure&quot;, &quot;mutt-like&quot;, or
+&quot;show-structure-in-from&quot;; and sorting by Threads or OrderedSubject.
+The value of this option is a single character.
+This character is used instead of the vertical line character when there are
+no more replies directly to the parent of the current message.
+It can be used to &quot;round-off&quot; the bottom of the vertical line
+by setting it to a character such as a backslash (&#92;) or
+a backquote (&#96;).
+The default value of this option is the backslash character (&#92;).
+This option may not be set to the Empty Value.
+In that case, the default will be used instead.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_thread_indicator_char =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_threading-indicator-character"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_threading-indicator-character"--></H1>
+
+The <!--#echo var="VAR_threading-indicator-character"--> option has a small effect on the MESSAGE
+INDEX display when using a
+<A HREF="h_config_thread_disp_style"><!--#echo var="VAR_threading-display-style"--></A> other
+than &quot;none&quot; and sorting by Threads or OrderedSubject.
+The value of this option is a single character.
+This character is used to indicate that part of a thread (a conversation) is
+hidden beneath a message.
+The message could be expanded
+if desired with the &quot;/&quot; Collapse/Expand command.
+By default, the value of this option is the greater than sign (&gt;).
+<P>
+If this option is set to the Empty Value, then the column (and the following
+blank column) will be deleted from the display.
+
+<P>
+This option is closely related to the
+<A HREF="h_config_thread_exp_char"><!--#echo var="VAR_threading-expanded-character"--></A> option.
+Another similar option that affects the thread display is the
+<A HREF="h_config_thread_lastreply_char"><!--#echo var="VAR_threading-lastreply-character"--></A> option.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_thread_exp_char =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_threading-expanded-character"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_threading-expanded-character"--></H1>
+
+The <!--#echo var="VAR_threading-expanded-character"--> option has a small effect on the MESSAGE
+INDEX display when using a
+<A HREF="h_config_thread_disp_style"><!--#echo var="VAR_threading-display-style"--></A> other
+than &quot;none&quot;.
+The value of this option is a single character.
+This character is used to indicate that part of a thread has been expanded
+and could be collapsed if desired with
+the &quot;/&quot; Collapse/Expand command.
+By default, the value of this option is a dot (.).
+<P>
+If this option is set to the Empty Value, then the column (and the following
+blank column) will be deleted from the display.
+
+<P>
+This option is closely related to the
+<A HREF="h_config_thread_indicator_char"><!--#echo var="VAR_threading-indicator-character"--></A> option.
+Another similar option that affects the thread display is the
+<A HREF="h_config_thread_lastreply_char"><!--#echo var="VAR_threading-lastreply-character"--></A> option.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_thread_index_style =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_threading-index-style"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_threading-index-style"--></H1>
+
+When a folder is sorted by Threads or OrderedSubject,
+this option will affect the INDEX displays.
+<P>
+
+The possible values for this option are:
+
+<DL>
+<DT>regular-index-with-expanded-threads</DT>
+<DD>This is the default display.
+If the configuration option
+<A HREF="h_config_thread_disp_style">&quot;<!--#echo var="VAR_threading-display-style"-->&quot;</A>
+is set to something other than &quot;none&quot;, then this setting
+will cause Alpine to start off with a MESSAGE INDEX with all of
+the threads expanded.
+That is, each message will have a line in the MESSAGE INDEX display.
+The Collapse/Expand command (/) may be used to manually collapse or
+expand a thread or subthread (see also <A HREF="h_config_slash_coll_entire">&quot;<!--#echo var="FEAT_slash-collapses-entire-thread"-->&quot;</A>).
+<P>
+This setting affects the display when the folder is first threaded.
+The collapsed state may also be re-initialized by re-sorting the folder manually
+using the SortIndex command ($).
+After re-sorting the threads will once again all be expanded, even if you
+have previously collapsed some of them.
+<P>
+If &quot;<!--#echo var="VAR_threading-display-style"-->&quot; is set to &quot;none&quot;, then
+the display will be the regular default Alpine MESSAGE INDEX, but sorted
+in a different order.
+</DD>
+
+<DT>regular-index-with-collapsed-threads</DT>
+<DD>If the configuration option
+<A HREF="h_config_thread_disp_style">&quot;<!--#echo var="VAR_threading-display-style"-->&quot;</A>
+is set to something other than &quot;none&quot;, then this setting
+will cause Alpine to start out with all of the threads collapsed instead of
+starting out with all of the threads expanded.
+The Collapse/Expand command (/) may be used to manually collapse or
+expand a thread or subthread (see also <A HREF="h_config_slash_coll_entire">&quot;<!--#echo var="FEAT_slash-collapses-entire-thread"-->&quot;</A>).
+<P>
+This setting affects the display when the folder is first threaded.
+The collapsed state may also be re-initialized by re-sorting the folder manually
+using the SortIndex command ($).
+After re-sorting the threads will once again all be collapsed, even if you
+have previously expanded some of them.
+</DD>
+
+<DT>separate-index-screen-always</DT>
+<DD>With this setting and the next, you will see an index of threads
+instead of an
+index of messages, provided you have sorted by Threads or OrderedSubject.
+<P>
+The THREAD INDEX contains a '*' in the first column if any message in the thread
+is marked Important.
+If not, it contains a '+' if any message in the thread is to you.
+The second column is blank. The third column contains a 'D' if all of the
+messages in the thread are deleted.
+Otherwise, it contains an 'N' if any of the messages in the thread are New.
+<P>
+When you view a particular thread from the THREAD INDEX you will be
+in the MESSAGE INDEX display
+but the index will only contain messages from the thread you are viewing.
+</DD>
+
+<DT>separate-index-screen-except-for-single-messages</DT>
+<DD>This is very similar to the option above.
+When you are in the THREAD INDEX, one of the available commands
+is &quot;ViewThd&quot;.
+With the setting &quot;separate-index-screen-always&quot; (the option above)
+when you view a particular thread you will be in the
+MESSAGE INDEX display and the index will only contain messages from
+the thread you are viewing.
+If the thread you are viewing consists of a single message, the MESSAGE INDEX
+will be an index with only one message in it.
+If you use this &quot;separate-index-screen-except-for-single-messages&quot;
+setting instead, then that index that contains a single message
+will be skipped and you will go directly from the THREAD INDEX into the
+MESSAGE TEXT screen.
+</DD>
+
+</DL>
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_thread_disp_style =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_threading-display-style"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_threading-display-style"--></H1>
+
+When a folder is sorted by Threads or OrderedSubject,
+this option will affect the MESSAGE INDEX display.
+By default, Alpine will display the MESSAGE INDEX in the
+&quot;show-thread-structure&quot; style if a folder is sorted
+by Threads or OrderedSubject.
+<P>
+
+The possible values for this option are:
+
+<DL>
+<DT>none</DT>
+<DD>Regular index display.
+The same index line as would be displayed without threading is used.
+The only difference will be in the order of the messages.
+</DD>
+
+<DT>show-thread-structure</DT>
+<DD>Threaded Subjects will be indented and vertical bars and horizontal
+lines will be added to make it easier to see the relationships among
+the messages in a thread (a conversation).
+</DD>
+
+<DT>mutt-like</DT>
+<DD>This is the same as the option above except that the Subject
+is suppressed (is blank) if it matches the previous Subject in the thread.
+The name comes from the email client <A HREF="http://www.mutt.org/">Mutt</A>.
+Here is an example of what a mutt-like index might look like.
+In this example, the first column represents the message number, the
+<A HREF="h_config_thread_index_style"><!--#echo var="VAR_threading-index-style"--></A>
+is set to &quot;regular-index-with-expanded-threads&quot;, and the
+<A HREF="h_config_thread_lastreply_char"><!--#echo var="VAR_threading-lastreply-character"--></A>
+is set to a backslash:
+<PRE>
+&nbsp;&nbsp;&nbsp;1 Some topic
+&nbsp;&nbsp;&nbsp;&nbsp;2 . Subject original message in thread
+&nbsp;&nbsp;&nbsp;&nbsp;3 |-> reply to 2
+&nbsp;&nbsp;&nbsp;&nbsp;4 . |-> another reply to 2
+&nbsp;&nbsp;&nbsp;&nbsp;5 . | &#92;-> reply to 4
+&nbsp;&nbsp;&nbsp;&nbsp;6 . | &#92;-> reply to 5
+&nbsp;&nbsp;&nbsp;&nbsp;7 | &#92;-> reply to 6
+&nbsp;&nbsp;&nbsp;&nbsp;8 |-> another reply to 2
+&nbsp;&nbsp;&nbsp;&nbsp;9 . |->New subject another reply to 2 but with a New subject
+&nbsp;&nbsp;&nbsp;10 | |-> reply to 9
+&nbsp;&nbsp;&nbsp;11 | &#92;-> another reply to 9
+&nbsp;&nbsp;&nbsp;12 | &#92;-> reply to 11
+&nbsp;&nbsp;&nbsp;13 &#92;-> final reply to 2
+&nbsp;&nbsp;&nbsp;14 Next topic
+</PRE>
+</DD>
+
+<DT>indent-subject-1</DT>
+<DD>Threaded Subjects will be indented one space per level of the conversation.
+The bars and lines that show up in the show-thread-structure display will
+not be there with this style.
+</DD>
+
+<DT>indent-subject-2</DT>
+<DD>Same as above but indent two spaces per level instead of one space.
+</DD>
+
+<DT>indent-from-1</DT>
+<DD>Similar to indent-subject-1, except that instead of indenting the
+Subject field one space the From field of a thread will be indented one
+space per level of the conversation.
+</DD>
+
+<DT>indent-from-2</DT>
+<DD>Same as above but indent two spaces per level instead of one space.
+</DD>
+
+<DT>show-structure-in-from</DT>
+<DD>The structure of the thread is illustrated with indenting, vertical bars,
+and horizontal lines just like with the show-thread-structure option, but
+the From field is used to show the relationships instead of the Subject field.
+</DD>
+
+</DL>
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_pruning_rule =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_pruning-rule"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_pruning-rule"--></H1>
+
+By default, Alpine will ask at the beginning of each month whether or not
+you want to rename your sent-mail folder to a name like sent-mail-month-year.
+(See the feature <A HREF="h_config_prune_uses_iso"><!--#echo var="FEAT_prune-uses-yyyy-mm"--></A> to
+change the format of the folder to sent-mail-yyyy-mm.)
+It will also ask whether you would like to delete old sent-mail folders.
+If you have defined
+<A HREF="h_config_read_message_folder"><!--#echo var="VAR_read-message-folder"--></A>
+or
+<A HREF="h_config_pruned_folders"><!--#echo var="VAR_pruned-folders"--></A>
+Alpine will also ask about pruning those folders.
+<P>
+
+With this option you may provide an automatic answer to these questions.
+The default value is to ask you what you'd like to do.
+<P>
+
+The six possible values for this option are:
+
+<DL>
+<DT>ask about rename, ask about deleting</DT>
+<DD>This is the default.
+Alpine will ask whether you want to rename the folders and whether you
+want to delete each of the old folders.
+</DD>
+
+<DT>ask about rename, don't delete</DT>
+<DD>Alpine will ask whether you want to rename the folders, but won't
+ask about or delete old folders.
+</DD>
+
+<DT>always rename, ask about deleting</DT>
+<DD>This means you want to always answer yes and have Alpine automatically
+rename the folder if possible.
+You will also be asked about deleting old folders.
+</DD>
+
+<DT>always rename, don't delete</DT>
+<DD>This means you want to always answer yes and have Alpine automatically
+rename the folder if possible.
+There will be no deleting of old folders.
+</DD>
+
+<DT>don't rename, ask about deleting</DT>
+<DD>This means you want to always answer no.
+Alpine will not rename the folder.
+You will be asked about deleting old folders.
+</DD>
+
+<DT>don't rename, don't delete</DT>
+<DD>This means you want to always answer no.
+Alpine will not rename the folder.
+There will be no deleting of old folders, either.
+</DD>
+</DL>
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_reopen_rule =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_folder-reopen-rule"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_folder-reopen-rule"--></H1>
+
+Alpine normally checks for new mail in the currently open folder
+and in the INBOX every few <A HREF="h_config_mailcheck">minutes</A>.
+
+<P>
+There are some situations where automatic new-mail checking does not work.
+For example, if a mail folder is opened using the POP protocol or a newsgroup
+is being read using the NNTP protocol, then new-mail checking is disabled.
+
+<P>
+It may be possible to check for new mail in these cases by reopening the
+folder.
+Alpine does not do this for you automatically, but you may do the commands
+manually to cause this to happen.
+You reopen by going back to the folder list screen from the message
+index screen with the &quot;&lt;&quot; command,
+and then going back into the message index screen with
+the &quot;&gt;&quot; command.
+(Actually, any method you would normally use to open a folder will work the
+same as the &quot;&lt;&quot; followed by &quot;&gt;&quot; method.
+For example, the GoTo Folder command will work, or you may use L to go to the
+Folder List screen and Carriage Return to reopen the folder.)
+
+<P>
+There are some cases where Alpine knows that reopening the folder should
+be useful as a way to discover new mail.
+At the time of this writing, connections made using the POP protocol,
+news reading using the NNTP protocol, local news reading, and local
+ReadOnly folders that are in the traditional UNIX or the MMDF format all
+fall into this category.
+There are other cases where it <EM>may</EM> be a way to discover new mail, but Alpine
+has no way of knowing, so it might also just be an exercise in futility.
+All remote, ReadOnly folders other than those listed just above fall into this
+category.
+The setting of this option together with the type of folder
+controls how Alpine will react to the apparent attempt to reopen a folder.
+
+<P>
+If you don't reopen, then you will just be back in
+the message index with no change.
+You left the index and came back, but the folder remained &quot;open&quot;
+the whole time.
+However, if you do reopen the folder, the folder is closed and then reopened.
+In this case, the current state of the open folder is lost.
+The New status, Important and Answered flags,
+selected state, Zoom state, collapsed or expanded state of threads,
+current message number,
+and any other temporary state is all lost when the reopen happens.
+For POP folders (but not NNTP newsgroups) the Deleted flags are also lost.
+
+<P>
+In the possibilities listed below, the text says &quot;POP/NNTP&quot; in
+several places.
+That really implies the case where Alpine knows it is a good way to discover
+new mail, which is more than just POP and NNTP, but POP and NNTP are
+the cases of most interest.
+This option probably has more possible values than it deserves. They are:
+
+<DL>
+<DT>Always reopen</DT>
+<DD>Alpine will not ask whether you want to reopen but will just do the reopen
+whenever you type a command that implies a reopen, regardless of the
+access method.
+In other words, it is assumed you would always answer Yes if asked
+about reopening.
+</DD>
+
+<DT>Yes for POP/NNTP, Ask about other remote [Yes]</DT>
+<DD>Alpine will assume a Yes answer if the access method is POP or NNTP, but
+will ask you whether to reopen other remote folders,
+with a default answer of Yes.
+</DD>
+
+<DT>Yes for POP/NNTP, Ask about other remote [No]</DT>
+<DD>Alpine will assume a Yes answer if the access method is POP or NNTP, but
+will ask you whether to reopen other remote folders,
+with a default answer of No.
+</DD>
+
+<DT>Yes for POP/NNTP, No for other remote</DT>
+<DD>Alpine will assume a Yes answer if the access method is POP or NNTP, and
+will assume a No answer for all other remote folders.
+</DD>
+
+<DT>Always ask [Yes]</DT>
+<DD>Alpine will not differentiate based on access method.
+It will always ask for all remote folders, with a default answer of Yes.
+</DD>
+
+<DT>Always ask [No]</DT>
+<DD>Alpine will not differentiate based on access method.
+It will always ask for all remote folders, with a default answer of No.
+</DD>
+
+<DT>Ask about POP/NNTP [Yes], No for other remote</DT>
+<DD>Alpine will ask if the access method is POP or NNTP, with a default answer
+of Yes.
+It will never attempt to reopen other remote folders.
+</DD>
+
+<DT>Ask about POP/NNTP [No], No for other remote</DT>
+<DD>This is the default.
+Alpine will ask if the access method is POP or NNTP, with a default answer
+of No.
+It will never attempt to reopen other remote folders.
+</DD>
+
+<DT>Never reopen</DT>
+<DD>Alpine will never attempt to reopen already open folders.
+</DD>
+</DL>
+
+<P>
+Remember, wherever it says POP or NNTP above it really means POP or NNTP or
+any of the other situations where it is likely that reopening is a good way
+to discover new mail.
+
+<P>
+There is an alternative that may be of useful in some situations.
+Instead of manually checking for new mail you can set up a
+<A HREF="h_maildrop">Mail Drop</A>
+and automatically check for new mail.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_inc_startup =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_incoming-startup-rule"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_incoming-startup-rule"--></H1>
+
+This value affects Alpine's behavior when opening the &quot;INBOX&quot; or
+one of the &quot;INCOMING MESSAGE FOLDERS&quot;.
+It determines which message will be the <EM>current message</EM> when
+the folder is first opened.
+The default value is &quot;first-unseen&quot;.
+
+<P>
+The seven possible values for this option are:
+
+<DL>
+<DT>first-unseen</DT>
+<DD>The current message is set to the first
+unseen message that has not been marked deleted, or the last message if
+all of the messages have been seen previously.
+Messages which have not been seen or which have been seen but re-marked
+as New are considered unseen messages.
+See the note at the bottom of this help about newsgroups.
+</DD>
+
+<DT>first-recent</DT>
+<DD>Similar to the default, but rather than starting on the first
+unseen message Alpine starts on the first <EM>recent</EM> message.
+A message is recent if it arrived since the last time the folder was
+open. This value causes the current message to be set to the first
+recent message if there is one, otherwise to the last
+message in the folder.
+</DD>
+
+<DT>first-important</DT>
+<DD>This will result in the current message being set to the first
+message marked Important (but not Deleted).
+If no messages are marked Important, then it will be the last message.
+Messages are marked Important by <EM>you</EM>, not by the sender, using
+the
+<A HREF="h_common_flag">Flag command</A>.
+Or they may be marked Important by an Alpine
+<A HREF="h_mainhelp_filtering">Filter</A>
+that you have set up.
+</DD>
+
+<DT>first-important-or-unseen</DT>
+<DD>This selects the first of the first unseen and the first important
+messages.
+</DD>
+
+<DT>first-important-or-recent</DT>
+<DD>This selects the first of the first recent and the first important
+messages.
+</DD>
+
+<DT>first</DT>
+<DD>Simply starts you on the <EM>first</EM> undeleted message in the folder.
+If all messages are deleted you start on the last message.
+</DD>
+
+<DT>last</DT>
+<DD>Simply starts you on the <EM>last</EM> undeleted message in the folder
+If all messages are deleted you start on the last message.
+</DD>
+</DL>
+
+<P>
+NOTE: For newsgroups in the incoming collection, &quot;first-unseen&quot; and
+&quot;first-recent&quot; are the same and are affected by whether or not the
+feature
+<A HREF="h_config_news_uses_recent">&quot;<!--#echo var="FEAT_news-approximates-new-status"-->&quot;</A>
+is turned on.
+Also, there is no permanent storage in news for an Important flag.
+This means that no messages will be marked Important when a newsgroup is
+first opened.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_browser =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_url-viewers"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_url-viewers"--></H1>
+<!--chtml if pinemode="os_windows"-->
+PC-Alpine users do not need to enter anything here, unless:<UL>
+<LI> they want to override, for use with Alpine, the application defined
+in the Windows operating system for handling URLs; or
+<LI> they are (planning on) using the same configuration file with
+Unix Alpine.
+</UL>
+<P>
+Note that if using a viewer that has a space in its path, you should
+use the DOS name for that directory or file. Example:
+<PRE>
+url-viewer=C:&#92;Progra~1&#92;mozilla&#92;mozilla.exe
+</PRE>
+<HR><P>
+<!--chtml endif-->
+This option affects Alpine's handling of URLs that are found in
+messages you read. Normally, only URLs Alpine can handle directly
+are automatically offered for selection in the &quot;Message
+Text&quot; screen. When one or more applications
+capable of deciphering URLs on their command line are added here, Alpine
+will choose the first available to display URLs it cannot handle directly.
+A viewer's availability is based on its being specified with a <B>full
+directory path</B> and the evaluation of any optionally supplied
+parameters described below.
+
+<P>
+Additionally, to support various connection methods and applications, each
+entry in this list can optionally begin with one or more of
+the following special tokens. The allowed tokens include:
+
+<P>
+<DL>
+<DT>_TEST(<VAR>test-string</VAR>)_</DT>
+<DD>
+The <VAR>test-string</VAR> is a shell command that Alpine will run to
+evaluate a viewer's availability. The command specified by the test
+string is run and if its resulting exit status is non-zero, Alpine will
+not consider the associated viewer for use.
+</DD>
+
+<DT>_SCHEME(<VAR>scheme-list</VAR>)_</DT>
+<DD>
+The <VAR>scheme-list</VAR> is a list of one or more (comma-delimited)
+URL schemes that are to be used with the associated viewer. This is
+the way to configure Alpine to recognize URLs other than the built-in set.
+<P>
+It can also be used to override Alpine's built-in handlers.
+For example, you could specify &quot;news&quot; in the <VAR>scheme-list</VAR>,
+and Alpine would use (provided it passed all other criteria) the associated
+viewer when it encounterd a URL of the form &quot;news:comp.mail.pine&quot;.
+
+</DD>
+</DL>
+
+<P>
+By default, Alpine will simply append a space character followed by the
+selected URL prior to launching the command in your specified SHELL. You can
+optionally specify where in the command the selected URL should appear
+by using the &quot;_URL_&quot; token. All occurrences found in the command
+will be replaced with the selected URL before the command is handed
+to the shell. If such replacement occurs, the default appending of the
+selected URL does not take place.
+
+<P>
+NOTE: If the viewer you specify has any command-line arguments,
+including the &quot;_URL_&quot; token, you will need to add a
+double-quote character before the command path and after the last
+argument (see the &quot;lynx&quot; example below).
+
+<P>
+So, here are some example entries:
+<PRE>
+url-viewers = _TEST(&quot;test -n '$&#123;DISPLAY}'&quot;)_ /usr/local/bin/netscape
+ &quot;/usr/local/bin/lynx _URL_&quot;
+ C:&#92;BIN&#92;NETSCAPE.BAT
+</PRE>
+<P>
+This example shows that for the first viewer in the list to be used
+the environment variable &quot;DISPLAY&quot; must be defined. If it
+is, then the path and file &quot;/usr/local/bin/netscape&quot; must exist.
+If neither condition is met,
+then the path and file &quot;/usr/local/bin/lynx&quot; must exist.
+If it does, then the &quot;_URL_&quot; token is replaced by the selected URL.
+If the path to &quot;lynx&quot; is invalid,
+then the final path and file C:&#92;BIN&#92;NETSCAPE.BAT must exist.
+Note that the last
+entry is a DOS/Windows path. This is one way to support Alpine running
+on more than one architecture with the same configuration file.<P>
+<P>
+<!--chtml if pinemode="os_windows"-->
+<!--chtml else-->
+Note that depending on the type of browser used and the method of
+its invocation (such as whether it will open in a separate window) from
+the MESSAGE TEXT screen, the browser may &quot;supplant&quot;
+the MESSAGE TEXT screen, and you will have to quit the browser to return to
+it (for example, when using Lynx; to exit Lynx, use the &quot;Q&quot; command).
+In other words, launching the browser from Alpine may make Alpine
+&quot;disappear&quot; (although it is still &quot;running&quot;)
+until you close the browser again.<P>
+<UL><LI><A HREF="h_config_browser_xterm">Defining <!--#echo var="VAR_url-viewers"--> in an X windows
+environment: for advanced users and systems administrators</A>
+</UL>
+<!--chtml endif-->
+<P>If you are unsure what browsers are available on your system or how to
+specify them in Alpine's <!--#echo var="VAR_url-viewers"--> option for best usability, contact your
+local computing support staff.
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_browser_xterm =====
+<HTML>
+<HEAD>
+<TITLE><!--#echo var="VAR_url-viewers"--> and X windows applications</TITLE>
+</HEAD>
+<BODY>
+<H1>Defining <!--#echo var="VAR_url-viewers"--> in an X windows
+environment: for advanced users and systems administrators</H1>
+If you are using Alpine with an X-terminal (emulator) and want to define an
+X windows-based application in <!--#echo var="VAR_url-viewers"-->,
+you may want to do so in a manner that causes any <B>already</B>
+invoked viewer application to be used for viewing URLs you select from Alpine
+messages, and a <B>new</B> URL-viewer process to be
+started <B>only</B> if the same application has <B>not already</B>
+been launched -- for one reason, to avoid file-locking contentions among
+multiple invocations of the same URL-viewer application.
+(The example entries set in the help screen for the &quot;<!--#echo var="VAR_url-viewers"-->&quot;
+option does not do this.) A method of doing that would be:<OL>
+<LI> use
+the _TEST(<VAR>test-string</VAR>)_ token in the <B>first</B> entry to
+check (using commands appropriate for your Unix shell
+in place of <VAR>test-string</VAR>) for the presence of a
+lockfile created by the URL-viewer application -- which implies that the
+application is already running, though this is not foolproof.
+Following that in the same <!--#echo var="VAR_url-viewers"--> entry, specify the
+application with its appropriate command line option(s) to
+show the URL selected from the Alpine message in an already open window of
+that application, or perhaps in a new window of that application.
+
+<LI> In the
+<B>second</B> entry for the <!--#echo var="VAR_url-viewers"--> option, specify the same
+application without those command line options, but this time using the
+_TEST(...)_ token to check whether the environment variable &quot;DISPLAY&quot;
+is defined.
+<LI> If you will be using Alpine (with the same .pinerc file) outside of the X
+windows environment (for instance, using VT-100 terminal emulation), you
+may wish to specify a non-X windows URL-viewer application such as Lynx
+as the last entry.
+</OL><BR>
+How exactly you define your <!--#echo var="VAR_url-viewers"--> entries to do this will depend on
+the command shell, the URL-viewer application(s), and possibly the specific
+version of the latter, you are using.
+<P>
+Relevant command
+line options for the Netscape browser for showing URLs (selected from Alpine)
+when Netscape is already running are discussed in the document
+&quot;Remote Control of UNIX Netscape&quot;
+found at the URL (as of 12 Aug. 1998):
+<P>
+
+<CENTER><A HREF="http://home.netscape.com/newsref/std/x-remote.html">http://home.netscape.com/newsref/std/x-remote.html</A></CENTER>
+
+<P>(If the URL-viewer application is
+<B>not</B> running on the same host as Alpine, but being launched from an
+applications server, you may not be able to use the command line options for
+using an existing invocation of the application in Alpine's <!--#echo var="VAR_url-viewers"--> entry.)
+<P>
+<!--chtml if this-method="shown-to-work"-->
+An example using the Korn shell and the Netscape browser (first entry wrapped
+because of its length, but should all appear on one line):
+<P>
+url-viewers = _TEST("test -L /myhomedir/.netscape/lock")_ &quot;/usr/local/bin/netscape -remote 'openURL(_URL_, new-window)' &amp;&quot;<BR>
+
+_TEST(&quot;test -n '$&#123;DISPLAY}'&quot;)_ &quot;/usr/local/bin/netscape &amp;&quot;<BR>
+ &quot;/usr/local/bin/lynx '_URL_'&quot;
+<P>
+<!--chtml endif-->
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_enable_full_hdr =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-full-header-cmd"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-full-header-cmd"--></H1>
+
+This feature enables the &quot;H Full Headers&quot; command which toggles between
+the display of all headers in the message and the normal edited view of
+headers. The Full Header command also controls which headers are included
+for Export, Pipe, Print, Forward, and Reply functions. (For Reply, the
+Full Header mode will respect the
+<A HREF="h_config_include_header">&quot;Include-Headers-in-Reply&quot;</A>
+feature setting.)
+<P>
+If Full Header mode is turned on and you Forward a message, you will
+be asked if you'd like to forward the message as an attachment, as opposed
+to including the text of the message in the body of your new message.
+<P>
+If you have also turned on the
+<A HREF="h_config_quote_suppression">&quot;Quote Suppression&quot;</A>
+option then the Full Headers command actually rotates through three states
+instead of just two.
+The first is the normal view with long quotes suppressed.
+The second is the normal view but with the long quotes included.
+The last enables the display of all headers in the message.
+When using Export, Pipe, Print, Forward, or Reply the quotes are
+never suppressed, so the first two states are identical.
+<P>
+Normally, the Header Mode will reset
+to the default behavior when moving to a new message.
+The mode can be made to persist from message to message by setting the feature
+<A HREF="h_config_quell_full_hdr_reset"><!--#echo var="FEAT_quell-full-header-auto-reset"--></A>.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_enable_full_hdr_and_text =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-full-header-and-text"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-full-header-and-text"--></H1>
+
+This feature affects how the &quot;H Full Headers&quot; command displays
+message text. If set, the raw message text will be displayed. This
+especially affects MIME formatted email, where the entire MIME format
+will be displayed. This feature similarly affects how messages are
+included for the Export, Pipe, Print, Forward, and Reply functions.
+<P>
+When viewing a raw message that has attachments with this feature set,
+you will not be able to view attachments without first leaving full
+headers mode. This is because MIME parsing is not done on the raw message.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_enable_pipe =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-unix-pipe-cmd"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-unix-pipe-cmd"--></H1>
+
+This feature enables the "| Pipe" command that sends the current message
+to the specified command for external processing.
+<P>
+
+A short description of how the pipe command works is given
+<A HREF="h_pipe_command">here</A>.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_quell_full_hdr_reset =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_quell-full-header-auto-reset"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_quell-full-header-auto-reset"--></H1>
+
+The <A HREF="h_common_hdrmode">HdrMode Command</A>
+normally resets to the default state when switching to a new message.
+For example, if you've used the &quot;H&quot; command to turn on Full
+Headers for a message you are viewing, and then you type the Next command
+to look at the next message, the full headers will no longer be shown.
+Setting this feature disables that reset.
+Instead, the Header Mode remains the same from message to message.
+
+<P>
+The presence or absence of the HdrMode command is determined by the
+<A HREF="h_config_enable_full_hdr">&quot;<!--#echo var="FEAT_enable-full-header-cmd"-->&quot;</A>
+Feature-List option.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_enable_tab_complete =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-tab-completion"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-tab-completion"--></H1>
+
+This feature enables the TAB key when at a prompt for a filename. In this
+case, TAB will cause the partial name already entered to be automatically
+completed, provided the partial name is unambiguous.
+This feature is on by default.
+<P>
+Similarly, this feature also enables TAB completion of address book
+nicknames when at a prompt for a nickname,
+or when typing in an address field in the composer.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_quit_wo_confirm =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_quit-without-confirm"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_quit-without-confirm"--></H1>
+
+This feature controls whether or not Alpine will ask for confirmation when a
+Quit command is received.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_quote_replace_noflow =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_quote-replace-nonflowed"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_quote-replace-nonflowed"--></H1>
+
+This feature, which is only active when
+<A HREF="h_config_quote_replace_string"><!--#echo var="VAR_quote-replace-string"--></A> is
+also set,
+enables quote-replacement on non-flowed messages. It is off
+by default because a non-flowed message is more dependent on its format,
+and thus quote-replacement may cause less-than-pleasing results.
+Setting this feature will cause quote-replacement similar to that of flowed
+messages, but with the added possibility of long lines being wrapped
+into new lines if the Quote-Replacement-String is longer than the string
+it is replacing, which is &quot;&gt;&nbsp;&quot;.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_enable_jump =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-jump-shortcut"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-jump-shortcut"--></H1>
+
+When this feature is set you may enter a number (followed by RETURN)
+and jump to that message number, when in the MESSAGE INDEX or MESSAGE TEXT
+screens. In other words, it obviates the need for typing the "J" for the
+Jump command.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_enable_alt_ed =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-alternate-editor-cmd"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-alternate-editor-cmd"--></H1>
+
+If this feature is set (the default), and the
+<A HREF="h_config_editor">&quot;<!--#echo var="VAR_editor"-->&quot;</A> option
+<B>is not</B> set, entering
+the ^_ (Ctrl-underscore) key while composing a message will prompt you
+for the name of the editor you would like to use.
+<P>
+If the environment variable $EDITOR is set, its value will be offered as
+a default.
+<P>
+If the <A HREF="h_config_editor">&quot;<!--#echo var="VAR_editor"-->&quot;</A> option
+<B>is</B> set, the ^_ key will activate the specified
+editor without prompting, in which case it is not necessary to
+set the &quot;<!--#echo var="FEAT_enable-alternate-editor-cmd"-->&quot; feature.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_alt_ed_now =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-alternate-editor-implicitly"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-alternate-editor-implicitly"--></H1>
+
+If this feature and the <A HREF="h_config_editor">&quot;<!--#echo var="VAR_editor"-->&quot;</A>
+variable are both set, Alpine will
+automatically activate the specified editor when the cursor is moved from
+the header of the message being composed into the message text. For
+replies, the alternate editor will be activated immediately. If this
+feature is set but the &quot;<!--#echo var="VAR_editor"-->&quot; variable is not set, then Alpine will
+automatically ask for the name of an alternate editor when the cursor
+is moved out of the header being composed, or if a reply is being done.
+
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_enable_bounce =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-bounce-cmd"--></TITLE>
+</HEAD>
+<H1>FEATURE: <!--#echo var="FEAT_enable-bounce-cmd"--></H1>
+<BODY>
+
+Setting this feature enables the "B Bounce" command, which will prompt
+for an address and *remail* the message to the new recipient. This command
+is used to re-direct messages that you have received in error, or need to
+be redirected for some other reason (e.g. list moderation). The final
+recipient will see a header indicating that you have Resent the msg, but
+the message's From: header will show the original author of the message,
+and replies to it will go back to that author, and not to you.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_enable_agg_ops =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-aggregate-command-set"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-aggregate-command-set"--></H1>
+
+When this feature is set you may use the commands and subcommands that relate to
+performing operations on more than one message at a time. We call these
+&quot;aggregate operations&quot;. In particular, the
+<!--chtml if pinemode="function_key"-->&quot;F5
+<!--chtml else-->&quot;;
+<!--chtml endif--> Select&quot;,
+
+<!--chtml if pinemode="function_key"-->
+&quot;F6
+<!--chtml else-->
+&quot;A
+<!--chtml endif-->
+Apply&quot;, and
+<!--chtml if pinemode="function_key"-->
+&quot;F4
+<!--chtml else-->
+&quot;Z
+<!--chtml endif-->
+Zoom&quot; commands are enabled by this feature. Select is used to
+&quot;tag&quot; one or more messages meeting the specified criteria. Apply can
+then be used to apply any message command to all of the selected/tagged
+messages. Further, the Zoom command allows you to toggle the MESSAGE INDEX
+view between just those Selected and all messages in the folder.
+<P>
+This feature also enables the
+<!--chtml if pinemode="function_key"-->
+&quot;F7&quot;
+<!--chtml else-->
+&quot;^X&quot;
+<!--chtml endif-->
+
+subcommand in the MESSAGE INDEX
+WhereIs command that causes all messages matching the WhereIs argument to
+become selected; and the Select, Select Current, and ZoomMode commands in the
+<A HREF="h_folder_maint">FOLDER LIST screen</A>.
+<P>
+Some related help topics are
+<UL>
+<LI> <A HREF="h_mainhelp_aggops">Aggregate Operations</A>
+<LI> <A HREF="h_index_cmd_select">Selecting: Select and WhereIs/Select</A>,
+<LI> <A HREF="h_config_auto_unzoom"><!--#echo var="FEAT_auto-unzoom-after-apply"--></A>,
+<LI> <A HREF="h_config_auto_unselect"><!--#echo var="FEAT_auto-unselect-after-apply"--></A>.
+<LI> <A HREF="h_config_auto_zoom"><!--#echo var="FEAT_auto-zoom-after-select"--></A>, and
+<LI> <A HREF="h_config_select_wo_confirm"><!--#echo var="FEAT_select-without-confirm"--></A>.
+</UL>
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+
+====== h_config_enable_flag =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-flag-cmd"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-flag-cmd"--></H1>
+
+Setting this feature enables the
+<A HREF="h_common_flag">&quot;* Flag&quot;</A>
+command that allows you to
+manipulate the status flags associated with a message. By default, Flag
+will set the "Important" flag, which results in an asterisk being
+displayed in column one of the MESSAGE INDEX for such messages.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_flag_screen_default =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-flag-screen-implicitly"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-flag-screen-implicitly"--></H1>
+
+The feature modifies the behavior of the
+<a href="h_common_flag">Flag</a>
+command (provided it too is
+<A HREF="h_config_enable_flag">enabled</A>).
+By default, when the "* Flag" command is selected,
+Alpine offers a prompt to set one of several flags and also offers the
+option of entering the detailed flag manipulation screen via the "^T"
+key. Enabling this feature causes Alpine to immediately enter the detailed
+flag screen rather than first offer the simple prompt.
+The
+<A HREF="h_config_flag_screen_kw_shortcut"><!--#echo var="FEAT_enable-flag-screen-keyword-shortcut"--></A> option offers a slightly different way of setting keywords.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_flag_screen_kw_shortcut =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-flag-screen-keyword-shortcut"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-flag-screen-keyword-shortcut"--></H1>
+
+This feature modifies the behavior of the
+<a href="h_common_flag">Flag</a> command
+and the <A HREF="h_index_cmd_select">Select</A> command.
+This feature is set by default.
+When this feature is not set, when the "* Flag" command is selected,
+Alpine offers a prompt to set one of several flags and also offers the
+option of entering the detailed flag manipulation screen via the "^T"
+key.
+If you have
+<A HREF="h_config_keywords"><!--#echo var="VAR_keywords"--></A>
+defined, then enabling this feature adds a shortcut way to set or unset
+keywords.
+You use &quot;*&quot; followed by the first letter of a keyword (or the nickname of
+a keyword if you've given it a nickname) and that will set the keyword.
+<P>
+An example is easier to understand than the explanation.
+The flag command can always be used to set the system flags.
+For example, to set the Answered flag you would type
+<P>
+<CENTER><SAMP>* A</SAMP></CENTER>
+<P>
+Now suppose you have defined a keyword &quot;Work&quot; using the <!--#echo var="VAR_keywords"-->
+option in the Config screen.
+By default, to set a keyword like &quot;Work&quot; you would usually
+have to go to the Flag Details screen using
+the &quot;^T To Flag Details&quot; command.
+Instead, if you have enabled this feature, you may type
+<P>
+<CENTER><SAMP>* W</SAMP></CENTER>
+<P>
+to set the Work flag, or
+<P>
+<CENTER><SAMP>* ! W</SAMP></CENTER>
+<P>
+to unset it.
+Just like for the other flag setting commands, the case of the letter does
+not matter, so &quot;w&quot; or &quot;W&quot; both set the &quot;Work&quot;
+keyword.
+<P>
+Notice that you can only use this trick for one keyword that begins
+with &quot;W&quot;.
+If you happen to have a &quot;Work&quot; keyword and another keyword that is
+&quot;WIFI&quot; the &quot;* W&quot; command will set the first one in
+your list of keywords.
+Also, there are five letters that are reserved for system
+flags and the NOT command.
+If you type &quot;* A&quot; it will always set the Answered flag, not
+your &quot;Aardvark&quot; keyword.
+In order to set the &quot;Aardvark&quot; keyword you'll still have to use
+the Flag Details screen.
+<P>
+Because enabling the
+<A HREF="h_config_flag_screen_default"><!--#echo var="FEAT_enable-flag-screen-implicitly"--></A>
+option causes Alpine to skip directly to the Flag Details screen when the
+Flag command is used,
+setting it will cause this feature to have no effect at all.
+<P>
+Similarly, when Selecting by Keyword, setting this option will allow you
+to use Keyword initials instead of full keywords.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_can_suspend =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-suspend"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-suspend"--></H1>
+
+Setting this feature will allow you to type ^Z (Control Z) to
+<!--chtml if pinemode="os_windows"-->
+minimize Alpine into its icon, bringing into focus whatever
+application is running behind the PC-Alpine window.
+<!--chtml else-->
+temporarily suspend Alpine.
+
+<P>
+This does not exit Alpine, but puts it in the background to watch
+for new mail and such. Normally, you type a command, such
+as &quot;fg&quot; at your system prompt to return to your Alpine session.
+
+<P>
+The <A HREF="h_config_suspend_spawns"><!--#echo var="FEAT_use-subshell-for-suspend"--></A> feature
+adjusts whether Alpine is placed into the background of the shell its
+running in or starts a news shell.
+<!--chtml endif-->
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_take_lastfirst ======
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_disable-take-last-comma-first"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_disable-take-last-comma-first"--></H1>
+
+Normally, when TakeAddr is used to copy an address from a message into
+an address book entry, Alpine will attempt to rewrite the full name of the
+address in the form
+<P>
+
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Last, First<P>
+
+instead of<P>
+
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;First Last
+
+<P>
+It does this because many people find it useful to sort by Last name
+instead of First name. If this feature is set, then the TakeAddr command
+will not attempt to reverse the name in this manner.
+<P>
+&lt;End of help on this topic&gt;
+</BODY></HTML>
+====== h_config_disable_regex ======
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_disable-regular-expression-matching-for-alternate-addresses"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_disable-regular-expression-matching-for-alternate-addresses"--></H1>
+
+Normally, the
+<a href="h_config_alt_addresses"><!--#echo var="VAR_alt-addresses"--></a>
+option is interpreted as a regular expression.
+One type of address that might cause trouble is an address that
+contains a plus sign.
+If you want to have an address with a plus as one of your
+<!--#echo var="VAR_alt-addresses"-->
+and you don't want to use regular expressions, then setting this
+feature will cause Alpine to treat the addresses you list literally instead.
+<P>
+&lt;End of help on this topic&gt;
+</BODY></HTML>
+====== h_config_take_fullname ======
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_disable-take-fullname-in-addresses"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_disable-take-fullname-in-addresses"--></H1>
+
+Normally, when TakeAddr is used to copy an address or addresses
+from a message into an address book entry, Alpine will try to preserve
+the full name associated with each address in the list of addresses.
+The reason for this is so that if the entry is a list or later becomes a
+list, then information about the individual addresses in the list
+is preserved.
+If you would rather just have the simple addresses in the list of addresses,
+set this feature. For example, with the default setting you might
+see something like this in the ADDRESS BOOK editor after you type TakeAddr
+<P>
+<PRE>
+ Nickname : nick
+ Fullname : Bedrock Elders
+ Fcc :
+ Comment :
+ Addresses : Fred Flintstone &lt;flint@bedrock.org&gt;,
+ Barney Rubble &lt;rubble@bedrock.org&gt;
+</PRE>
+<P>
+but with this feature set it would look like
+<P>
+<PRE>
+ Nickname : nick
+ Fullname : Bedrock Elders
+ Fcc :
+ Comment :
+ Addresses : flint@bedrock.org,
+ rubble@bedrock.org
+</PRE>
+<P>
+instead. Note the difference in the Addresses field.
+<P>
+&lt;End of help on this topic&gt;
+</BODY></HTML>
+====== h_config_print_from ======
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_print-includes-from-line"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_print-includes-from-line"--></H1>
+
+If this feature is set, then the Berkeley-mail style From line is included
+at the start of each message that is printed. This line looks something
+like the following, with the address replaced by the address from the
+From line of the message being printed:
+<P>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;From user@domain.somewhere.com Mon May 13
+14:11:06 1998
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_expanded_distlists ======
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_expanded-view-of-distribution-lists"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_expanded-view-of-distribution-lists"--></H1>
+If this feature is set, then distribution lists in the address book
+screen will always be expanded automatically.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_compose_news_wo_conf ======
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_compose-sets-newsgroup-without-confirm"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_compose-sets-newsgroup-without-confirm"--></H1>
+This feature controls one aspect of Alpine's Composer. If you enter the
+composer while reading a newsgroup, you will normally be prompted to
+determine whether you intend the new message to be posted to the current
+newsgroup or not. If this feature is set, Alpine will not prompt you
+in this situation, and will assume that you do indeed wish to post
+to the newsgroup you are reading.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_compose_rejects_unqual ======
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_compose-rejects-unqualified-addrs"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_compose-rejects-unqualified-addrs"--></H1>
+
+This feature controls one aspect of the message composer; in particular,
+what happens when an unqualified name is entered into an address header.
+If set, unqualified names entered as addresses will be treated as errors
+unless they match an addressbook nickname. Alpine will not attempt to turn
+them into complete addresses by adding your local domain.<P>
+
+A complete (fully qualified) address is one containing a username followed
+by an &quot;@&quot; (&quot;at&quot;) symbol, followed by a domain name (e.g.
+&quot;jsmith@example.com&quot;). An unqualified name is one <B>without</B>
+the &quot;@&quot; symbol and domain name (e.g. &quot;jsmith&quot;).
+
+(See also <A HREF="h_address_format">Explanation of Address formats</A>.)
+
+<P>
+
+When you enter a fully qualified address, Alpine does not interpret or
+modify it, but simply passes it on to the mail-transport-agent (MTA) for
+your system. Alpine conforms to the Internet standards governing message
+headers and will not send an unqualifed name to the MTA. Therefore, when
+you enter an unqualified name, Alpine will normally attempt to turn it into
+a fully qualified address, first by checking to see if you have entered a
+matching nickname in your addressbook, or failing that, by simply adding
+your own domain to the name entered. So if your address is
+&quot;jsmith@example.com&quot; and you enter &quot;fred&quot;, then (assuming
+&quot;fred&quot; is not a nickname in your addressbook), Alpine will turn
+that into &quot;fred@example.com&quot;.<P>
+
+There are situations where it is not desirable for Alpine to interpret such
+unqualified names as valid (local) addresses. For example, if &quot;fred&quot;
+turned out to be a typo (intended to be an addressbook nickname), but
+there actually was a &quot;fred&quot; in your local domain, the message might
+be mis-delivered without your realizing it. In order to reduce the likelihood
+of such accidents, setting this feature will cause Alpine to treat such
+addresses as errors, and require that you explicitly enter the full local
+address (e.g. &quot;fred@example.com&quot;) or correct the name so that it
+matches an address book nickname.<P>
+
+Consider this a safety feature against mis-directed mail.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_quell_local_lookup ======
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_quell-user-lookup-in-passwd-file"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_quell-user-lookup-in-passwd-file"--></H1>
+
+This feature controls an aspect of Alpine's Composer, and if needed, will
+usually be set by your system manager in Alpine's system-wide configuration
+file. Specifically, if this feature is set, Alpine will not attempt to look
+in the system password file to find a Full Name for the entered address.
+<P>
+Normally, names you enter into address fields (e.g. To: or Cc:) are
+checked against your address book(s) to see if they match an address book
+nickname. Failing that, (in Unix Alpine) the name is then checked against
+the Unix password file. If the entered name matches a username in the
+system password file, Alpine extracts the corresponding Full Name information
+for that individual, and adds that to the address being entered.
+<P>
+However, password file matching can have surprising (incorrect) results if
+other users of the system do not receive mail at the domain you are using.
+That is, if either the
+<A HREF="h_config_user_dom">&quot;<!--#echo var="VAR_user-domain"-->&quot;</A> or
+<A HREF="h_config_domain_name">&quot;<!--#echo var="VAR_use-only-domain-name"-->&quot;</A>
+option
+is set such that the administrative domain of other users on the system
+isn't accurately reflected, Alpine should be told that a passwd file match
+is coincidental, and Full Name info will be incorrect. For example, a
+personal name from the password file could get falsely paired with the
+entered name as it is turned into an address in the configured domain.
+<P>
+If you are seeing this behavior, enabling this feature will prevent Unix
+Alpine from looking up names in the password file to find the Full Name
+for incomplete addresses you enter.<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_tab_checks_recent ======
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_tab-checks-recent"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_tab-checks-recent"--></H1>
+
+In a FOLDER LIST screen, the TAB key usually just changes which
+folder is highlighted.
+If this feature is set, then the TAB key will cause the number of
+recent messages and the total number of messages in the highlighted folder
+to be displayed instead.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_maildrops_preserve_state ======
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_maildrops-preserve-state"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_maildrops-preserve-state"--></H1>
+
+This feature affects the way <A HREF="h_maildrop">Mail Drops</A> work.
+Normally, when mail is moved from a Mail Drop folder to a destination
+folder, it is delivered as new mail.
+Any Seen/New, Answered, Important/Flagged state that has changed will be
+ignored.
+All of the mail will be considered unSeen, unAnswered, and unImportant after
+it is moved.
+<P>
+If this feature is set, then the state changes that have been made
+to the messages in the Mail Drop folder will be preserved.
+<P>
+In any case, messages that are already marked Deleted when the
+mail is to be moved from the Mail Drop will be ignored.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_preopen_stayopens ======
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_preopen-stayopen-folders"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_preopen-stayopen-folders"--></H1>
+
+This feature is related to the option
+<A HREF="h_config_permlocked">&quot;<!--#echo var="VAR_stay-open-folders"-->&quot;</A>.
+Normally, Stay Open folders are only opened on demand, when the user
+asks to open them.
+From then on they are kept open for the duration of the session.
+However, if this feature is set, then the Stay Open folders will all be
+opened at startup, at the same time that the INBOX is opened.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_expunge_inbox ======
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_offer-expunge-of-inbox"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_offer-expunge-of-inbox"--></H1>
+
+The INBOX is normally treated differently from regular folders in several
+ways.
+One of the differences is that the normal &quot;close&quot; sequence of
+events is deferred until Alpine is exited, instead of happening when you
+leave the INBOX to view another folder.
+The &quot;close&quot; sequence normally includes the Expunging
+of deleted messages
+(either automatically or after a prompt, controlled by the features
+<A HREF="h_config_auto_expunge">&quot;<!--#echo var="FEAT_expunge-without-confirm"-->&quot;</A>,
+<A HREF="h_config_full_auto_expunge">&quot;<!--#echo var="FEAT_expunge-without-confirm-everywhere"-->&quot;</A>, and
+<A HREF="h_config_expunge_manually"><!--#echo var="FEAT_expunge-only-manually"--></A>), and the
+handling of the
+<A HREF="h_config_read_message_folder"><!--#echo var="VAR_read-message-folder"--></A>.
+
+<P>
+If this feature is set the &quot;close&quot; sequence handling will take
+place every time you leave the INBOX.
+The INBOX will still be kept open, but the offer to Expunge and the archiving
+to the <!--#echo var="VAR_read-message-folder"-->
+will take place each time you leave the INBOX instead of only once at the
+end of the session.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_expunge_stayopens ======
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_offer-expunge-of-stayopen-folders"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_offer-expunge-of-stayopen-folders"--></H1>
+
+This feature is related to the option
+<A HREF="h_config_permlocked">&quot;<!--#echo var="VAR_stay-open-folders"-->&quot;</A>.
+Stay Open folders are treated differently from regular folders in several
+ways.
+One of the differences is that the normal &quot;close&quot; sequence of
+events is deferred until Alpine is exited, instead of happening when you
+leave the folder to view another folder.
+The &quot;close&quot; sequence normally includes the Expunging
+of deleted messages
+(either automatically or after a prompt, controlled by the features
+<A HREF="h_config_auto_expunge">&quot;<!--#echo var="FEAT_expunge-without-confirm"-->&quot;</A>,
+<A HREF="h_config_full_auto_expunge">&quot;<!--#echo var="FEAT_expunge-without-confirm-everywhere"-->&quot;</A>, and
+<A HREF="h_config_expunge_manually"><!--#echo var="FEAT_expunge-only-manually"--></A>), and the
+handling of
+<A HREF="h_config_archived_folders"><!--#echo var="VAR_incoming-archive-folders"--></A>.
+
+<P>
+If this feature is set the &quot;close&quot; sequence handling will take
+place when you leave the Stay Open folder.
+The folder will still be kept open, but the offer to Expunge and the archiving
+will take place each time you leave the folder instead of only once at the
+end of the session.
+This feature does not affect the INBOX, which will still only be processed
+when you exit Alpine.
+However, there is a similar feature that affects only the INBOX called
+<A HREF="h_config_expunge_inbox">&quot;<!--#echo var="FEAT_offer-expunge-of-inbox"-->&quot;</A>.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_preserve_start_stop ======
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_preserve-start-stop-characters"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_preserve-start-stop-characters"--></H1>
+
+This feature controls how special control key characters, typically
+Ctrl-S and Ctrl-Q, are interpreted when input to Alpine. These characters
+are known as the "stop" and "start" characters and are sometimes used in
+communications paths to control data flow between devices that operate at
+different speeds.
+
+<P>
+
+By default, Alpine turns the system's handling of these special characters
+off except during printing. However, if you see Alpine reporting input errors
+such as:
+<P>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[ Command "^Q" not defined for this screen.]
+<P>
+and, at the same time, see your display become garbled, then it is likely
+that setting this option will solve the problem. Be aware, though, that
+enabling this feature will also cause Alpine to ostensibly "hang"
+whenever the Ctrl-S key combination is entered as the system is now
+interpreting such input as a "stop output" command. To "start
+output" again, simply type Ctrl-Q.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_enable_incoming ======
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-incoming-folders"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-incoming-folders"--></H1>
+
+Alpine's Incoming Message Folders collection
+provides a convenient way to access multiple incoming folders.
+It is also useful if you have accounts on multiple computers.
+<P>
+
+If set, this feature defines a pseudo-folder collection called
+&quot;INCOMING MESSAGE FOLDERS&quot;. Initially, the only folder included
+in this collection will be your INBOX, which will no longer show up in
+your Default folder collection.
+<P>
+
+You may add more folders to the Incoming Message Folders collection by
+using the
+<!--chtml if pinemode="function_key"-->
+&quot;F10
+<!--chtml else-->
+&quot;A
+<!--chtml endif-->
+Add&quot; command in the FOLDER LIST screen. You will be prompted for
+the host the folder is stored on (which defaults to the same host used
+for your INBOX), a nickname, and the actual folder name. Once a set
+of Incoming Message Folders are defined, the TAB key (in MESSAGE INDEX
+or MESSAGE TEXT screens) may be used to scan the folders for those
+with Recent messages. If you add more folders to
+your <!--#echo var="VAR_incoming-folders"--> collection, turning this feature back off will have
+no effect.
+<P>
+NOTE: Normally the software that actually delivers mail (the stuff that happens
+before Alpine is involved) is in a better position to do delivery filtering
+than is Alpine itself.
+If possible, you may want to look at programs such as
+&quot;filter&quot; or &quot;procmail&quot;, which are examples of delivery
+filtering programs.
+If you'd prefer to have Alpine do the filtering for you, you may set that
+up.
+Look <A HREF="h_rules_filter">here</A> for help with Alpine filtering.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_enable_incoming_checking ======
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-incoming-folders-checking"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-incoming-folders-checking"--></H1>
+
+This feature is only operational if you have enabled the optional
+<A HREF="h_config_enable_incoming">&quot;<!--#echo var="VAR_incoming-folders"-->&quot;</A> collection.
+If you do have Incoming Message Folders and you also set this feature,
+then the number of Unseen messages in each folder will be displayed
+in the FOLDER LIST screen for the Incoming Message Folders.
+The number of Unseen messages in a folder will be displayed in parentheses
+to the right of the name of each folder.
+If there are no Unseen messages in a folder then only the name
+is displayed, not a set of parentheses with zero inside them.
+A redraw command, Ctrl-L, can be used in the FOLDER LIST screen for
+the Incoming Message Folders to cause an immediate update.
+<P>
+If a check for Unseen messages fails for a particular folder then Alpine
+will no longer attempt to check that folder for the duration of the
+session and this will be indicated by a question mark inside the
+parentheses.
+<P>
+The features
+<A HREF="h_config_incoming_checking_total"><!--#echo var="FEAT_incoming-checking-includes-total"--></A>,
+<A HREF="h_config_incoming_checking_recent"><!--#echo var="FEAT_incoming-checking-uses-recent"--></A>,
+<A HREF="h_config_incoming_list"><!--#echo var="VAR_incoming-check-list"--></A>,
+<A HREF="h_config_incoming_interv"><!--#echo var="VAR_incoming-check-interval"--></A>,
+<A HREF="h_config_incoming_second_interv"><!--#echo var="VAR_incoming-check-interval-secondary"--></A>, and
+<A HREF="h_config_incoming_timeo"><!--#echo var="VAR_incoming-check-timeout"--></A>
+all affect how this feature behaves.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_incoming_checking_total ======
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_incoming-checking-includes-total"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_incoming-checking-includes-total"--></H1>
+
+This option has no effect unless the feature
+<A HREF="h_config_enable_incoming_checking"><!--#echo var="FEAT_enable-incoming-folders-checking"--></A>
+is set, which in turn has no effect unless
+<A HREF="h_config_enable_incoming">&quot;<!--#echo var="VAR_incoming-folders"-->&quot;</A>
+is set.
+<P>
+When incoming folder checking is turned on the default is to display
+the number of unseen messages in each folder.
+More precisely, it is the number of undeleted unseen messages.
+Using this option you may also display the total number of messages
+in each folder.
+Instead of a single number representing the number of unseen messages
+you will get two numbers separated by a slash character.
+The first is the number of unseen messages and the second is the
+total number of messages.
+<P>
+You may also use the recent message count instead of the unseen message
+count by turning on the feature
+<A HREF="h_config_incoming_checking_recent"><!--#echo var="FEAT_incoming-checking-uses-recent"--></A>.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_incoming_checking_recent ======
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_incoming-checking-uses-recent"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_incoming-checking-uses-recent"--></H1>
+
+This option has no effect unless the feature
+<A HREF="h_config_enable_incoming_checking"><!--#echo var="FEAT_enable-incoming-folders-checking"--></A>
+is set, which in turn has no effect unless
+<A HREF="h_config_enable_incoming">&quot;<!--#echo var="VAR_incoming-folders"-->&quot;</A>
+is set.
+<P>
+When incoming folder checking is turned on the default is to display
+the number of unseen messages in each folder.
+More precisely, it is the number of undeleted unseen messages.
+Using this option you may display the number of recent messages instead
+of the number of unseen messages.
+A message is only counted as recent if this is the first session to
+see it, so the recent count might be less than the unseen count.
+The difference between the two would be accounted for by the unseen messages
+in the folder which were there previously but have not been looked at yet.
+<P>
+If you simultaneously run more than one email client at a time
+(for example, you run more than one Alpine in parallel) then turning
+this feature on can cause some confusion.
+The confusion stems from the fact that each message is only considered to be
+recent in one session.
+That means that the counts of new messages may be different in the two
+Alpines running side by side, because each incoming message will only be
+counted as recent in one of the two sessions.
+<P>
+You may also display the total number of messages
+in each folder by using the
+<A HREF="h_config_incoming_checking_total"><!--#echo var="FEAT_incoming-checking-includes-total"--></A>
+option.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_attach_in_reply ======
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_include-attachments-in-reply"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_include-attachments-in-reply"--></H1>
+
+This feature controls an aspect of Alpine's Reply command. If set, any MIME
+attachments that were part of the original message will automatically be
+included in the Reply.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_include_header =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_include-header-in-reply"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_include-header-in-reply"--></H1>
+
+This feature controls an aspect of Alpine's Reply command. If set, and the
+original message is being included in the reply, then headers from that
+message will also be part of the reply.
+<P>
+&lt;End of help on this topic&gt;
+</HEAD>
+</HTML>
+====== h_config_sig_at_bottom =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_signature-at-bottom"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_signature-at-bottom"--></H1>
+
+This feature controls an aspect of Alpine's Reply command. If this feature
+is set, and the original message is being included in the reply, then the
+contents of your signature file (if any) will be inserted after the included
+message.
+<P>
+This feature does not affect the results of a Forward command.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_sigdashes =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-sigdashes"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-sigdashes"--></H1>
+
+This feature enables support for the common USENET news convention
+of preceding a message signature with the special line consisting of
+the three characters &quot;--&nbsp;&quot; (i.e., dash, dash, and space).
+
+<P>
+When enabled and a
+&quot;<A HREF="h_config_signature_file"><!--#echo var="VAR_signature-file"--></A>&quot; exists,
+Alpine will insert the special line before including the file's text (unless
+the special line already exists somewhere in the file's text).
+
+<P>
+In addition, when you Reply or Followup to a message containing one of
+these special lines and choose to include its text, Alpine will observe
+the convention of not including text beyond the special line in your
+reply.
+If <A HREF="h_config_enable_full_hdr">&quot;Full Header&quot;</A>
+mode is enabled and turned on, then Alpine <EM>will</EM>
+include the text beyond the special line regardless of the setting of
+this feature.
+
+<P>
+See also &quot;<a href="h_config_strip_sigdashes"><!--#echo var="FEAT_strip-from-sigdashes-on-reply"--></a>&quot;
+for a related feature.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_new_thread_blank_subject =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_new-thread-on-blank-subject"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_new-thread-on-blank-subject"--></H1>
+
+When this feature is enabled (the default) Alpine will create a new thread
+every time that the subject line becomes empty at any time during composition.
+
+<P>
+This behavior is particularly useful in case you are replying to a message.
+Replying to a message causes the message to be in the same thread than the
+original message that is being replied to. However, many authors want to create
+a new message (in a different thread) while replying to a message, and they do
+this by changing the full subject, by first deleting the original subject and
+typing the new subject of the current message.
+
+<P>
+Enabling this feature causes that any time that the subject is deleted, the
+message being composed will be considered the first message of a new thread.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_strip_sigdashes =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_strip-from-sigdashes-on-reply"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_strip-from-sigdashes-on-reply"--></H1>
+
+This feature doesn't do anything if the feature
+&quot;<A HREF="h_config_sigdashes"><!--#echo var="FEAT_enable-sigdashes"--></A>&quot; is turned on.
+However, if the &quot;<!--#echo var="FEAT_enable-sigdashes"-->&quot; feature is not turned on,
+then turning on this feature enables support for the convention
+of not including text beyond the sigdashes line when Replying or Following
+up to a message and including the text of that message.
+If <A HREF="h_config_enable_full_hdr">&quot;Full Header&quot;</A>
+mode is enabled and turned on, then Alpine <EM>will</EM>
+include the text beyond the special line regardless of the setting of
+this feature.
+<P>
+In other words, this is a way to turn on the signature stripping behavior
+without also turning on the dashes-adding behavior.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_forward_as_attachment =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_forward-as-attachment"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_forward-as-attachment"--></H1>
+
+This feature affects the way forwarded message text is handled. When set, rather than
+include the text of the forwarded message below any additional text you provide in the
+composer, the forwarded message is attached in its entirety to the message you send.
+<P>
+This is useful in that it keeps the text you provide in the composer distinct from the
+text of the forwarded message. Similarly, it allows the recipient to
+conveniently operate on the forwarded message. For example, they might reply directly to
+the sender of the forwarded message, or process it as part of a spam report.
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_sub_lists =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-partial-match-lists"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-partial-match-lists"--></H1>
+
+This feature affects the subcommands available when Saving,
+or when Opening a new folder. If set, the subcommand ^X ListMatches will be
+available. This command allows you to type in a substring of the folder
+you are looking for and when you type ^X it will display all folders
+that contain that substring in their names.
+This feature is set by default.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_scramble_message_id =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_scramble-message-id"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_scramble-message-id"--></H1>
+
+Normally the Message-ID header that Alpine generates when sending a message
+contains the name of the computer from which the message is being sent.
+Some believe that this hostname could be used by spammers or could
+be used by others for nefarious purposes.
+If this feature is set, that name will be transformed with a simple
+Rot13 transformation.
+The result will still have the correct syntax for a Message-ID but the
+part of the MessageID that is often a domain name will not be an actual
+domain name because the letters will be scrambled.
+<P>
+It is possible (but unlikely?) that some spam detection
+software will use that as a reason to reject the mail as spam.
+It has also been reported that some spam detection software uses the
+fact that there are no dots after the &quot;@&quot; as a reason to reject
+messages.
+If your PC-Alpine Message-ID is using a name without a dot that is because
+that is what Windows thinks is your &quot;Full computer name&quot;.
+The method used to set this varies from one type of Windows to another but
+check under Settings -> Control Panel -> System and
+look for Network Identification or Computer Name or something similar.
+How to set it is beyond the scope of Alpine.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_downgrade_multipart_to_text =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_downgrade-multipart-to-text"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_downgrade-multipart-to-text"--></H1>
+
+<P>
+This feature affects Alpine's behavior when sending mail. Internet
+standards require Alpine to translate all non-ASCII characters in
+messages that it sends using MIME encoding. This encoding can be
+ostensibly broken for recipients if any agent between Alpine and the
+recipient, such as an email list expander, appends text to the
+message, such as list information or advertising. When sending such
+messages Alpine attempts to protect such encoding by placing extra
+MIME boundaries around the message text.
+<P>
+These extra boundaries are invisible to recipients that
+use MIME-aware email programs (the vast majority). However, if
+you correspond with users of email programs that are not MIME-aware,
+or do not handle the extra boundaries gracefully, you can
+use this feature to prevent Alpine from including the extra
+MIME information. Of course, it will increase the likelihood
+that non-ASCII text you send may appear corrupt to the recipient.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_show_sort =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_show-sort"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_show-sort"--></H1>
+
+If this feature is set and there is sufficient space on the screen,
+a short indication of the current sort order will be
+added in the titlebar (the top line on the screen), before the name
+of the folder.
+For example, with the default Arrival sort in effect,
+the display would have the characters
+
+<P><CENTER>[A]</CENTER><P>
+
+added between the title of the screen and the folder name.
+The letters are the same as the letters you may type to manually
+sort a folder with the SortIndex command ($).
+The letters in the table below are the ones that may show
+up in the titlebar line.
+<P>
+<TABLE>
+<TR> <TD> A </TD> <TD> <EM>A</EM>rrival </TD> </TR>
+<TR> <TD> S </TD> <TD> <EM>S</EM>ubject </TD> </TR>
+<TR> <TD> F </TD> <TD> <EM>F</EM>rom </TD> </TR>
+<TR> <TD> T </TD> <TD> <EM>T</EM>o </TD> </TR>
+<TR> <TD> C </TD> <TD> <EM>C</EM>c </TD> </TR>
+<TR> <TD> D </TD> <TD> <EM>D</EM>ate </TD> </TR>
+<TR> <TD> Z </TD> <TD> si<EM>Z</EM>e </TD> </TR>
+<TR> <TD> O </TD> <TD> <EM>O</EM>rderedsubject </TD> </TR>
+<TR> <TD> E </TD> <TD> scor<EM>E</EM> </TD> </TR>
+<TR> <TD> H </TD> <TD> t<EM>H</EM>read </TD> </TR>
+</TABLE>
+<P>
+If the sort order is Reversed, the letter above will be preceded by the letter
+&quot;R&quot;, for example
+
+<P><CENTER>[RS]</CENTER><P>
+
+means that a Reverse Subject sort is in effect.
+For the case where the sort is in Reverse Arrival order, the &quot;A&quot; is
+left out, and just an &quot;R&quot; is shown.
+
+<P><CENTER>[R]</CENTER>
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_disable_reset_disp =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_disable-terminal-reset-for-display-filters"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_disable-terminal-reset-for-display-filters"--></H1>
+
+UNIX Alpine only.
+<P>
+This feature affects Alpine's behavior when using
+<A HREF="h_config_display_filters"><!--#echo var="VAR_display-filters"--></A>.
+Normally, before the display filter is run, the terminal mode is reset
+to what it was before you started Alpine.
+This may be necessary if the filter requires the use of the terminal.
+For example, it may need to interact with you.
+If you set this feature, then the terminal mode will not be reset.
+One thing that turning on this feature should fix is the coloring of
+<A HREF="h_config_quote_color">quoted text</A> in the message view, which
+breaks because the terminal reset resets the color state of the terminal.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_disable_sender =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_disable-sender"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_disable-sender"--></H1>
+
+This feature affects Alpine's generation of the &quot;Sender:&quot; or
+<A HREF="h_config_use_sender_not_x">&quot;X-X-Sender&quot;</A>
+header fields.
+By default, Alpine will generate such a header in situations where the
+username or domain are not the same as
+the &quot;From:&quot; header on the message.
+With this feature set,
+no &quot;Sender:&quot; or &quot;X-X-Sender&quot; header will be generated.
+This may be desirable on a system that is virtually hosting many domains,
+and the sysadmin has other methods available for tracking a message to
+its originator.
+<P>
+See also <A HREF="h_config_allow_chg_from">&quot;<!--#echo var="FEAT_allow-changing-from"-->&quot;</A>.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_use_sender_not_x =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_use-sender-not-x-sender"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_use-sender-not-x-sender"--></H1>
+
+Normally Alpine adds a header line
+labeled &quot;X-X-Sender&quot;, if the sender is
+different from the From: line.
+The standard specifies that this header
+line should be labeled &quot;Sender&quot;, not &quot;X-X-Sender&quot;.
+Setting this feature causes
+&quot;Sender&quot; to be used instead of &quot;X-X-Sender&quot;.
+<P>
+See also <A HREF="h_config_disable_sender">&quot;<!--#echo var="FEAT_disable-sender"-->&quot;</A>.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_use_fk =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_use-function-keys"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_use-function-keys"--></H1>
+
+This feature specifies that Alpine will respond to function keys instead of
+the normal single-letter commands. In this mode, the key menus at the
+bottom of each screen will show function key designations instead of the
+normal mnemonic key.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_cancel_confirm =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_compose-cancel-confirm-uses-yes"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_compose-cancel-confirm-uses-yes"--></H1>
+
+This feature affects what happens when you type ^C to cancel a composition.
+By default, if you attempt to cancel a composition by typing ^C, you will be
+asked to confirm the cancellation by typing a &quot;C&quot;
+for <EM>C</EM>onfirm.
+It logically ought to be a &quot;Y&quot; for <EM>Y</EM>es, but that is
+risky because the &quot;^C Y&quot; needed to cancel a message
+is close (on the keyboard) to the &quot;^X Y&quot; needed to send a message.
+<P>
+If this feature is set the confirmation asked for
+will be a &quot;<EM>Y</EM>es&quot;
+instead of a &quot;<EM>C</EM>onfirm&quot; response.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_compose_maps_del =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_compose-maps-delete-key-to-ctrl-d"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_compose-maps-delete-key-to-ctrl-d"--></H1>
+
+This feature affects the behavior of the DELETE key.
+If set, Delete will be equivalent to ^D, and delete
+the current character. Normally Alpine defines the Delete key
+to be equivalent to ^H, which deletes the <EM>previous</EM>
+character.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_compose_bg_post =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-background-sending"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-background-sending"--></H1>
+
+This feature affects the behavior of Alpine's mail sending. If set, this
+feature enables a subcommand in the composer's "Send?" confirmation
+prompt. The subcommand allows you to tell Alpine to handle the actual
+posting in the background. While this feature usually allows posting
+to appear to happen very fast, it has no affect on the actual delivery
+time it takes a message to arrive at its destination.
+
+<P>
+Please Note:
+<OL>
+ <LI>This feature will have no effect if the feature
+ <A HREF="h_config_send_wo_confirm">&quot;<!--#echo var="FEAT_send-without-confirm"-->&quot;</A>
+ is set.
+ <LI>This feature isn't supported on all systems. All DOS and Windows,
+ as well as several Unix ports, do not recognize this feature.
+ <LI>Error handling is significantly different when this feature is
+ enabled. Any message posting failure results in the message
+ being appended to your &quot;Interrupted&quot; mail folder. When you
+ type the <A HREF="h_common_compose">C</A>ompose command,
+ Alpine will notice this folder and
+ offer to extract any messages contained. Upon continuing a
+ failed message, Alpine will display the nature of the failure
+ in the status message line.
+ <LI> <EM>WARNING</EM>: Under extreme conditions, it is possible
+ for message data to
+ get lost. <EM>Do</EM> <EM>not</EM> enable this feature
+ if you typically run close to any sort of disk-space limits or quotas.
+</OL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_compose_dsn =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-delivery-status-notification"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-delivery-status-notification"--></H1>
+
+This feature affects the behavior of Alpine's mail sending. If set, this
+feature enables a subcommand in the composer's &quot;Send?&quot; confirmation
+prompt. The subcommand allows you to tell Alpine to request the type of
+Delivery Status Notification (DSN) that you would like. Most users will
+be happy with the default, and need not enable this feature.
+<P>
+If the feature
+<A HREF="h_config_send_wo_confirm">&quot;<!--#echo var="FEAT_send-without-confirm"-->&quot;</A> is set,
+then this feature has no effect and the type of DSN is not selectable.
+
+<P>
+Turning on this feature and then turning on the DSNOpts from the send
+prompt reveals four on-off toggles at the bottom of the screen.
+The &quot;X&quot; command toggles between NoErrRets and ErrRets. NoErrRets requests
+that no notification be returned to you, even if there is a delivery
+failure. The &quot;D&quot; key toggles between Delay and NoDelay. This tells the
+server that you are willing (or not) to receive delay notifications, which
+happen when there is an unusual delay at some mail server (in that mail
+server's opinion). The &quot;S&quot; key toggles between Success and NoSuccess.
+Success requests that you be sent a DSN message when the message is
+successfully delivered to the recipients mailbox. Setting NoErrRets will
+automatically turn off Delay and Success notification, and will flip the
+toggles to show that. Similarly, turning on Delay and/or Success will
+automatically toggle the &quot;X&quot; key to ErrRets. The fourth command, the
+&quot;H&quot; key, toggles between RetHdrs and RetFull. RetFull requests that
+the full message be returned in any failed DSN. RetHdrs requests that
+only the headers be returned in any failed DSN. Notice that this command
+applies only to failed delivery status reports. For delay or success
+reports, the full message is never returned, only the headers are returned.
+
+<P>
+If you don't enable the DSN feature or if you don't turn it on for a
+particular message, the default is that you will be notified about failures,
+you might be notified about delays, and you won't be notified about
+successes. You will usually receive the full message back when there is
+a failure.
+
+<P>
+If you turn on the DSNOpts the default is to return as much information as
+possible to you. That is, by default, the Success and Delay options are
+turned on and the full message will be returned on failure.
+
+<P>
+The sending prompt will display the current DSN request (if any) in a
+shorthand form. It will be:
+
+<P><CENTER>[Never]</CENTER>
+
+<P>
+if you have requested NoErrRets. Otherwise, it will look something like:
+
+<P><CENTER>[FDS-Hdrs]</CENTER>
+
+<P>
+The &quot;F&quot; will always be there, indicating that you will be notified
+of failures. (Alpine doesn't provide a way to request no failure notification
+and at the same time request either success or delay notification. The only
+way to request no failure notifications is to request no notifications at
+all with NoErrRets.) The &quot;D&quot; and/or &quot;S&quot; will be present if you have
+requested Delay and/or Success notification. If one of those is missing,
+that means you are requesting no notification of the corresponding type.
+After the dash it will say either Hdrs or Full. Hdrs means to return only
+the headers and Full means to return the full message (applies to
+failure notifications only).
+
+<P>
+NOTE: This feature relies on your system's mail transport agent or
+configured
+<A HREF="h_config_smtp_server">&quot;<!--#echo var="VAR_smtp-server"-->&quot;</A>
+having the negotiation mechanism introduced in
+&quot;Extended SMTP&quot; (ESMTP) and the specific extension called
+&quot;Delivery Status Notification&quot; (DSN). If the mail tranport agent you
+are using doesn't support DSN, a short warning will be shown to you on
+the message line at the bottom of the screen after you send your message,
+but your message will have been sent anyway.
+
+<P>
+Note that DSNs don't provide a mechanism to request read receipts. That
+is, if you request notification on success you are notified when the
+message is delivered to the mailbox, not when the message is read.
+
+<P>
+ESMTP allows for graceful migration to upgraded mail transfer agents, but
+it is possible that this feature might cause problems for some servers.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_auto_zoom =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_auto-zoom-after-select"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_auto-zoom-after-select"--></H1>
+
+This feature affects the behavior of the Select command.
+If set, the select command will automatically perform a zoom
+after the select is complete.
+This feature is set by default.
+<P>
+Some related help topics are
+<UL>
+<LI> <A HREF="h_mainhelp_aggops">Aggregate Operations</A>
+<LI> <A HREF="h_index_cmd_select">Selecting: Select and WhereIs/Select</A>,
+<LI> <A HREF="h_config_enable_agg_ops"><!--#echo var="FEAT_enable-aggregate-command-set"--></A>,
+<LI> <A HREF="h_config_auto_unzoom"><!--#echo var="FEAT_auto-unzoom-after-apply"--></A>, and
+<LI> <A HREF="h_config_auto_unselect"><!--#echo var="FEAT_auto-unselect-after-apply"--></A>.
+<LI> <A HREF="h_config_select_wo_confirm"><!--#echo var="FEAT_select-without-confirm"--></A>.
+</UL>
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_auto_unzoom =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_auto-unzoom-after-apply"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_auto-unzoom-after-apply"--></H1>
+
+This feature affects the behavior of the Apply command. If set, and if
+you are currently looking at a Zoomed Index view of selected messages,
+the Apply command will do the operation you specify, but then will
+implicitly do an &quot;UnZoom&quot;, so that you will automatically be back in
+the normal Index view after the Apply.
+This feature is set by default.
+
+<P>
+Some related help topics are
+<UL>
+<LI> <A HREF="h_mainhelp_aggops">Aggregate Operations</A>
+<LI> <A HREF="h_index_cmd_select">Selecting: Select and WhereIs/Select</A>,
+<LI> <A HREF="h_config_enable_agg_ops"><!--#echo var="FEAT_enable-aggregate-command-set"--></A>,
+<LI> <A HREF="h_config_auto_unselect"><!--#echo var="FEAT_auto-unselect-after-apply"--></A>.
+<LI> <A HREF="h_config_auto_zoom"><!--#echo var="FEAT_auto-zoom-after-select"--></A>, and
+<LI> <A HREF="h_config_select_wo_confirm"><!--#echo var="FEAT_select-without-confirm"--></A>.
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_auto_unselect =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_auto-unselect-after-apply"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_auto-unselect-after-apply"--></H1>
+
+This feature affects the behavior of the Apply command. If set,
+the Apply command will do the operation you specify, but then will
+implicitly do an &quot;UnSelect All&quot;, so that you will automatically be back in
+the normal Index view after the Apply.
+
+<P>
+Some related help topics are
+<UL>
+<LI> <A HREF="h_mainhelp_aggops">Aggregate Operations</A>
+<LI> <A HREF="h_index_cmd_select">Selecting: Select and WhereIs/Select</A>,
+<LI> <A HREF="h_config_enable_agg_ops"><!--#echo var="FEAT_enable-aggregate-command-set"--></A>,
+<LI> <A HREF="h_config_auto_zoom"><!--#echo var="FEAT_auto-zoom-after-select"--></A>, and
+<LI> <A HREF="h_config_auto_unzoom"><!--#echo var="FEAT_auto-unzoom-after-apply"--></A>, and
+<LI> <A HREF="h_config_select_wo_confirm"><!--#echo var="FEAT_select-without-confirm"--></A>.
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_fast_recent =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-fast-recent-test"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-fast-recent-test"--></H1>
+
+This feature controls the behavior of the TAB key when traversing folders
+in the optional
+<A HREF="h_config_enable_incoming">&quot;<!--#echo var="VAR_incoming-folders"-->&quot;</A>
+collection or in optional <!--#echo var="VAR_news-collections"-->.
+
+<P>
+When the TAB
+(<A HREF="h_common_nextnew">NextNew</A>)
+key is pressed, the default behavior is to
+explicitly examine the status of the folder for the number of recent
+messages (messages delivered since the last time it was viewed).
+Depending on the size and number of messages in the folder, this test
+can be time consuming.
+
+<P>
+Enabling this feature will cause Alpine to only test for the existence of
+any recent messages rather than to obtain the count. This is much faster
+in many cases. The downside is that you're not given the number of recent
+messages when prompted to view the next folder.
+If the feature
+<A HREF="h_config_tab_uses_unseen">&quot;<!--#echo var="FEAT_tab-uses-unseen-for-next-folder"-->&quot;</A>
+is turned on, then the present feature will have no effect.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_arrow_nav =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-arrow-navigation"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-arrow-navigation"--></H1>
+
+This feature controls the behavior of the left and right arrow keys.
+If set, the left and right arrow keys will operate like the usual
+navigation keys &lt; and &gt;.
+This feature is set by default.
+
+<P>
+If you set this feature, and do not like the changed behavior of the up/down
+arrow
+keys when navigating through the FOLDER LIST screen --
+<B>first</B> from column to column, if more than one folder is
+displayed per row,
+and <B>then</B> from row to row -- you may either also wish to set the feature
+&quot;<A HREF="h_config_relaxed_arrow_nav"><!--#echo var="FEAT_enable-arrow-navigation-relaxed"--></A>&quot;,
+&quot;<A HREF="h_config_single_list"><!--#echo var="FEAT_single-column-folder-list"--></A>&quot;, or
+use the ^P/^N (instead of up/down arrow) keys to move up/down the list of
+folders in each column.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_relaxed_arrow_nav =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-arrow-navigation-relaxed"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-arrow-navigation-relaxed"--></H1>
+
+This feature controls the behavior of the left, right, up and down
+arrow keys in the FOLDER LIST screen when the &quot;<A
+HREF="h_config_arrow_nav"><!--#echo var="FEAT_enable-arrow-navigation"--></A>&quot; feature is
+set.
+This feature is set by default.
+
+<P>
+
+When this feature is set, the left and right
+arrow keys in the FOLDER LIST screen
+move the highlight bar to the left or right, and the up and
+down arrows move it up or down.
+
+<P>
+When the &quot;<!--#echo var="FEAT_enable-arrow-navigation"-->&quot; feature is set and this
+feature is not set;
+the left and right arrow keys in the Folder List screen strictly
+track the commands bound to the '&lt;' and '&gt;' keys, and the up
+and down arrow keys move the highlight bar to the previous and next
+folder or directory name.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_alt_compose_menu =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_alternate-compose-menu"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_alternate-compose-menu"--></H1>
+
+This feature controls the menu that is displayed when Compose is selected.
+If set, a list of options will be presented, with each option representing
+the type of composition that could be used. This feature is most useful for
+users who want to avoid being prompted with each option separately, or who
+want to avoid the checking of remote postponed or form letter folders.
+The possible types of composition are:
+
+<P>
+New, for starting a new composition. Note that if New is selected and roles
+are set, roles are checked for matches and applied according to the setting
+of the matching role.
+
+<P>
+Interrupted, for continuing an interrupted composition. This option is only
+offered if an interrupted message folder is detected.
+
+<P>
+Postponed, for continuing postponed compositions. This option is offered
+if a <!--#echo var="VAR_postponed-folder"--> is set in the config REGARDLESS OF whether or not
+the postponed folder actually exists. This option is especially handy
+for avoiding having to check for the existence of a remote postponed folder.
+
+<P>
+Form, for using form letters. This option is offered if the <!--#echo var="VAR_form-letter-folder"-->
+is set in the config, and is not checked for existence for reasons similar
+to those explained by the postponed option.
+
+<P>
+setRole, for selecting a role to apply to a composition.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_alt_role_menu =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_alternate-role-menu"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_alternate-role-menu"--></H1>
+
+Normally the <A HREF="h_common_role">Role Command</A> allows you to choose
+a role and compose a new message using that role.
+When this feature is set, the role command will first ask whether you want to
+Compose a new message, Forward the current message, Reply to the
+current message, or Bounce the current message.
+If you are not in the MESSAGE INDEX and are not viewing a message,
+then there is no current message and the question will be skipped.
+After you have chosen to Compose, Forward, Reply, or Bounce you will
+then choose the role to be used.
+<P>
+When Bouncing the &quot;Set From&quot; address is used for the
+Resent-From header, the &quot;Set Fcc&quot; value is used for the Fcc
+provided that the option
+<A HREF="h_config_fcc_on_bounce">&quot;<!--#echo var="FEAT_fcc-on-bounce"-->&quot;</A> is turned on,
+and the &quot;Use SMTP Server&quot; value is used for the SMTP server, if
+set.
+Other actions of the role are ignored when Bouncing.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_always_spell_check =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_spell-check-before-sending"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_spell-check-before-sending"--></H1>
+<P>
+When this feature is set, every composed message will be spell-checked before
+being sent.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_quell_asterisks =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_suppress-asterisks-in-password-prompt"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_suppress-asterisks-in-password-prompt"--></H1>
+<P>
+When you are running Alpine you will sometimes be asked for a password
+in a prompt on the third line from the bottom of the screen.
+Normally each password character you type will cause an asterisk to echo
+on the screen. That gives you some feedback to know that your typing is
+being recognized.
+There is a very slight security risk in doing it this way because someone
+watching over your shoulder might be able to see how many characters there
+are in your password.
+If you'd like to suppress the echoing of the asterisks set this feature.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_quell_flowed_text =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_quell-flowed-text"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_quell-flowed-text"--></H1>
+<P>
+Alpine generates flowed text where possible.
+The method for generating flowed text is defined by
+<A HREF="http://www.ietf.org/rfc/rfc3676.txt">RFC 3676</A>,
+the benefit of doing so is
+to send message text that can properly be viewed both on normal width displays
+and on displays with smaller or larger than normal screen widths.
+With flowed text, a space at the end of a line tells the receiving mail
+client that the following line belongs to the same paragraph.
+Quoted text will also be affected, with only the innermost
+level of &quot;&gt;&quot; quoting being followed by a space.
+However, if you have changed the
+<A HREF="h_config_reply_indent_string">&quot;<!--#echo var="VAR_reply-indent-string"-->&quot;</A>
+so that it is not equal to the default value of &quot;&gt;&nbsp;&quot;, then
+quoted text will not be flowed.
+For this reason, we recommend that you leave your
+&quot;<!--#echo var="VAR_reply-indent-string"-->&quot; set to the default.
+<P>
+This feature turns off the generation of flowed text, as it might be
+desired to more tightly control how a message is displayed on the receiving end.
+<P>
+If this feature is <EM>not</EM> set, you can control on a message by message
+basis whether or not flowed text is generated.
+You do this by typing ^V at the Send confirmation prompt that you get
+after typing ^X to send a message.
+^V is a toggle that turns flowing off and back on if typed again.
+If for some reason flowing cannot be done on a particular message, then the
+^V command will not be available.
+This would be the case, for example, if this feature was set, or if your
+&quot;<!--#echo var="VAR_reply-indent-string"-->&quot; was set to a non-default value.
+If the feature
+<A HREF="h_config_send_wo_confirm">&quot;<!--#echo var="FEAT_send-without-confirm"-->&quot;</A> is set,
+then the opportunity to control on a message by message basis
+whether or not flowed text is generated is lost.
+<P>
+When this feature is not set and you have typed ^V to turn off flowing,
+the Send confirmation prompt will change to look like
+<P>
+<CENTER><SAMP>Send message (not flowed)?</SAMP></CENTER>
+<P>
+<A HREF="h_config_strip_ws_before_send">&quot;<!--#echo var="FEAT_strip-whitespace-before-send"-->&quot;</A> will
+also turn off the sending of flowed text messages, but it differs in that
+it also trims all trailing white space from a message before sending it.
+<P>
+If alternate editors are used extensively, be aware that a message will still
+be sent flowed if this feature is unset. In most cases this will be fine,
+but if the editor has a &quot;flowed text&quot; mode, it would be best to
+use that.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_strip_ws_before_send =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_strip-whitespace-before-send"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_strip-whitespace-before-send"--></H1>
+<P>
+By default, trailing whitespace is not stripped from
+a message before sending. Trailing whitespace should have no effect on an
+email message, and in flowed text can aid in delimiting paragraphs.
+However, the old behavior of stripping trailing whitespace was in place
+to better deal with older clients that couldn't handle certain types of
+text encodings. This feature restores the old behavior
+<P>
+Trailing whitespace is of aid to flowed-text-formatted messages, which are
+generated by default but can be turned off via the
+<A HREF="h_config_quell_flowed_text">&quot;<!--#echo var="FEAT_quell-flowed-text"-->&quot;</A> feature.
+<!--#echo var="FEAT_strip-whitespace-before-send"--> also has the effect of turning off sending
+of flowed text.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_del_from_dot =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_compose-cut-from-cursor"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_compose-cut-from-cursor"--></H1>
+
+This feature controls the behavior of the Ctrl-K command in the composer.
+If set, ^K will cut from the current cursor position to the end of the line,
+rather than cutting the entire line.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_print_index =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_print-index-enabled"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_print-index-enabled"--></H1>
+
+This feature controls the behavior of the Print command when in the
+MESSAGE INDEX screen. If set, the print command will give you a prompt
+asking if you wish to print the message index, or the currently highlighted
+message. If not set, the message will be printed.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_allow_talk =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_allow-talk"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_allow-talk"--></H1>
+
+UNIX Alpine only.
+<P>
+By default, permission for others to &quot;talk&quot; to your terminal is turned
+off when you are running Alpine. When this feature is set, permission is
+instead turned on. If enabled, you may see unexpected messages in the
+middle of your Alpine screen from someone attempting to contact you via the
+&quot;talk&quot; program.
+
+<P>
+NOTE: The &quot;talk&quot; program has nothing to do with Alpine or email. The
+talk daemon on your system will attempt to print a message on your screen
+when someone else is trying to contact you. If you wish to see these
+messages while you are running Alpine, you should enable this feature.
+
+<P>
+If you do enable this feature and see a &quot;talk&quot; message, you must
+suspend or quit Alpine before you can respond.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_send_filter_dflt =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_compose-send-offers-first-filter"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_compose-send-offers-first-filter"--></H1>
+If you have <A HREF="h_config_sending_filter">&quot;<!--#echo var="VAR_sending-filters"-->&quot;</A>
+configured, setting this feature will cause
+the first filter in the <!--#echo var="VAR_sending-filters"--> list to be offered as the default
+instead of unfiltered, the usual default.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_custom_print =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_print-offers-custom-cmd-prompt"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_print-offers-custom-cmd-prompt"--></H1>
+
+When this feature is set, the print command will have an additional
+subcommand called "C CustomPrint". If selected, you will have
+the opportunity to enter any system print command --instead of being
+restricted to using those that have been previously configured in the
+printer setup menu.
+<P>
+
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_enable_dot_files =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-dot-files"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-dot-files"--></H1>
+
+When this feature is set, files beginning with dot (".") will be
+visible in the file browser. For example, you'll be able to select them
+when using the browser to add an attachment to a message.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_enable_dot_folders =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-dot-folders"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-dot-folders"--></H1>
+
+When this feature is set, folders beginning with dot (".") may be added
+and viewed.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_ff_between_msgs =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_print-formfeed-between-messages"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_print-formfeed-between-messages"--></H1>
+
+Setting this feature causes a formfeed to be printed between messages when
+printing multiple messages (with Apply Print command).
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_blank_keymenu =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_disable-keymenu"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_disable-keymenu"--></H1>
+
+If this feature is set the command key menu that normally appears on the
+bottom two lines of the screen will not usually be there. Asking for
+help with ^G or ? will cause the key menu to appear instead of causing
+the help message to come up. If you want to actually see the help text,
+another ^G or ? will show it to you. After the key menu has popped
+up with the help key it will remain there for an O for Other command but
+disappear if any other command is typed.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_enable_mouse =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-mouse-in-xterm"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-mouse-in-xterm"--></H1>
+
+This feature controls whether or not an X terminal mouse can be used with
+Alpine. If set, and the $DISPLAY variable indicates that an X terminal is
+being used, the left mouse button on the mouse can be used to select text
+or commands.
+Clicking on a command at the bottom of the screen will behave as if you had
+typed that command.
+Clicking on an index line will move the current message highlight to
+that line.
+Double-clicking on an index line will view the message.
+Double-clicking on a link will view the link.
+<P>
+This type of mouse support will also work in some terminal emulators which are
+not actually X terminals, but which have extra code to support the xterm
+style mouse.
+For those emulators you not only need to turn this feature on but you also
+have to set the $DISPLAY environment variable even though it isn't needed
+for your terminal.
+That will cause Alpine to think that it is an xterm and to properly interpret the
+escape sequences sent by the mouse.
+<P>
+Note: if this feature is set, the behavior of X terminal cut-and-paste is
+also modified. It is sometimes possible to hold the shift key down while clicking
+left or middle mouse buttons for the normal xterm cut/paste operations.
+There is also an Alpine command to toggle this mode on or off.
+The command is Ctrl-&#92; (Control-backslash).
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_enable_xterm_newmail =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-newmail-in-xterm-icon"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-newmail-in-xterm-icon"--></H1>
+
+This feature controls whether or not Alpine will attempt to announce new
+mail arrival when it is running in an X terminal window and that window
+is iconified. If set, and the $DISPLAY variable indicates that an X
+terminal is being used, Alpine will send appropriate escape sequences to
+the X terminal to modify the label on Alpine's icon to indicate that new
+mail has arrived. Alpine will also modify the Alpine window's title to
+indicate new mail.
+See also <a href="h_config_enable_newmail_short_text"><!--#echo var="FEAT_enable-newmail-short-text-in-icon"--></a>.
+<P>
+&lt;End of help on this topic&gt;
+</BODY></HTML>
+====== h_config_enable_newmail_short_text =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-newmail-short-text-in-icon"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-newmail-short-text-in-icon"--></H1>
+
+This feature controls the text to be displayed in an icon in the event
+of a new message arrival. Normally, the message will
+be the one that is displayed on the screen. This feature shortens the
+message to a count of the number of new messages in brackets. This may be
+more useful for those who use the window's title bar in the task bar as a
+new mail indicator. This feature is only useful if the
+<A HREF="h_config_enable_xterm_newmail"><!--#echo var="FEAT_enable-newmail-in-xterm-icon"--></A>
+feature is also set. Like the <!--#echo var="FEAT_enable-newmail-in-xterm-icon"-->
+feature, this feature is only relevant when run in an xterm environment.
+<P>
+&lt;End of help on this topic&gt;
+</BODY></HTML>
+====== h_config_copy_to_to_from =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_copy-to-address-to-from-if-it-is-us"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_copy-to-address-to-from-if-it-is-us"--></H1>
+
+This feature affects the From address used when Replying to a message.
+It is probably only useful if you have some
+<a href="h_config_alt_addresses"><!--#echo var="VAR_alt-addresses"--></a>
+defined.
+When enabled, it checks to see if any of the addresses in the To or Cc
+fields of the message you are replying to is one of your addresses.
+If it is, and there is only one of them, then that address is used as
+the From address in the message you are composing.
+In other words, you will be using a From address that is the same
+as the To address that was used to get the mail to you in the first place.
+
+<P>
+If a role is being used and it has a From address defined, that From address will
+be used rather than the one derived from this feature.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_prefix_editing =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-reply-indent-string-editing"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-reply-indent-string-editing"--></H1>
+
+This feature affects the Reply command's &quot;Include original message
+in Reply?&quot; prompt. When enabled, it causes the
+&quot;Edit Indent String&quot; sub-command to appear which allows
+you to edit the string Alpine would otherwise use to denote included
+text from the message being replied to.<P>
+
+Thus, you can change Alpine's default message quote character (usually
+an angle bracket) on a per message basis. So you could change your quoted message to
+look, for example, like this:<p>
+
+<pre>On Tues, 26 Jan 1999, John Q. Smith wrote:
+
+John: I just wanted to say hello and to congratulate you
+John: on a job well done!</pre><p>
+
+The configuration option
+<A HREF="h_config_reply_indent_string">&quot;<!--#echo var="VAR_reply-indent-string"-->&quot;</A>
+may be used to change what appears as the default string to be edited.
+<P>
+NOTE: Edited <!--#echo var="VAR_reply-indent-string"--> only apply to the message
+currently being replied to.
+<P>
+If you change your <!--#echo var="VAR_reply-indent-string"-->
+so that it is not equal to the default value of &quot;&gt;&nbsp;&quot;, then
+quoted text will not be flowed
+(<A HREF="h_config_quell_flowed_text">Flowed Text</A>)
+when you reply.
+For this reason, we recommend that you leave your <!--#echo var="VAR_reply-indent-string"-->
+set to the default value.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_enable_search_and_repl =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-search-and-replace"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-search-and-replace"--></H1>
+
+This feature modifies the behavior of Alpine's composer. Setting this
+feature causes Alpine to offer the "^R Replace" subcommand, which
+allows you to search and replace text strings in a message you are composing,
+inside the "^W Where is" command.
+
+<P>
+
+To search and replace text, first enter the text to be replaced at the
+"Search: " prompt. Then, rather than pressing Enter to just search for that
+text, press ^R, which turns the prompt into
+
+<P>
+
+Search (to replace):
+
+<P>
+
+and then press Enter. The cursor will highlight the first occurrence
+of the text string you entered, and the prompt will show:
+
+<P>
+
+Replace "<your text string>" with :
+
+<P>
+
+where <your text string> is what you entered at the previous prompt;
+here, enter the replacement text. To only replace the highlighted
+occurrence, simply press Enter now; to replace all occurrences in the
+message, press ^X (Repl All), then Enter. You will then be asked to confirm
+each replacement.
+
+<P>
+
+The command ^R toggles between "Replace" and "Don't Replace"; its subcommand
+^X toggles between "Replace All" and "Replace One."
+
+<P>
+
+If you previously searched for text in a message, it will be offered for
+re-use as part of the prompt, shown in [ ] brackets.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_enable_view_attach =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-msg-view-attachments"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-msg-view-attachments"--></H1>
+
+This feature modifies the behavior of Alpine's MESSAGE TEXT screen.
+Setting this feature causes Alpine to present attachments in boldface.
+The first available attachment is displayed in inverse. This is the
+"selected" attachment. Pressing RETURN will cause Alpine to display
+the selected attachment. Use the arrow keys to change which of the
+attachments displayed in boldface is the current selection.
+
+<P>
+
+Speaking of arrow keys, the Up and Down Arrows will select the next
+and previous attachments if one is available on the screen for selection.
+Otherwise, they will simply adjust the viewed text one line up or down.
+
+<P>
+
+Similarly, when selectable items are present in a message, the Ctrl-F key
+can be used to select the next item in the message independent of which
+portion of the viewed message is currently displayed. The Ctrl-B key can
+be used to select the previous item in the same way.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_enable_y_print =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-print-via-y-command"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-print-via-y-command"--></H1>
+
+This feature modifies the behavior of Alpine's Print command.
+<P>
+By default, Alpine's print command is available by pressing the "%" key.
+(This command is a substantial change from Pine versions before 4.00 --
+where the print command was "Y" -- based on numerous complaints about
+printing being invoked inadvertently, since Y also means "Yes.")
+
+<P>
+
+This feature is supplied to mitigate any disruption or anxiety users
+might feel as a result of this change.
+
+<P>
+
+Enabling this feature will cause Alpine to recognize both the old
+command, "Y" for Prynt, as well the new "%" method for invoking
+printing. Note, key menu labels are not changed as a result of
+enabling this feature.
+
+<P>
+
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_enable_lessthan_exit =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-exit-via-lessthan-command"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-exit-via-lessthan-command"--></H1>
+
+If this feature is set, then on screens where there is an Exit command
+but no < command, the < key will perform the same function as the Exit
+command.
+This feature is set by default.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_enable_view_url =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-msg-view-urls"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-msg-view-urls"--></H1>
+This feature modifies the behavior of Alpine's MESSAGE TEXT screen.
+When this feature is set (the default) Alpine will select possible URLs from the
+displayed text and display them in boldface for selection.
+<P>
+The first available URL is displayed in inverse. This is the
+&quot;selected&quot; URL. Pressing RETURN will cause Alpine to display
+the selected URL via either built-in means as with mailto:, imap:,
+news:, and nntp:, or via an external application as defined
+by the <A HREF="h_config_browser">&quot;url-viewer&quot;</A>
+variable.
+<P>
+Use the arrow keys to change which of the URLs displayed in boldface
+is the current selection.
+<P>
+Speaking of arrow keys, the Up and Down Arrows will select the next
+and previous URL if one is available on the screen for selection (unless
+you have set the feature
+<A HREF="h_config_enable_view_arrows">&quot;<!--#echo var="FEAT_enable-msg-view-forced-arrows"-->&quot;</A>).
+Otherwise, they will simply adjust the viewed text one line up or down.
+<P>
+Similarly, when selectable items are present in a message, the Ctrl-F
+key can be used to select the next item in the message independent
+of which portion of the viewed message is currently displayed. The
+Ctrl-B key can be used to select the previous item in the same way.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_enable_view_web_host =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-msg-view-web-hostnames"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-msg-view-web-hostnames"--></H1>
+
+This feature modifies the behavior of Alpine's MESSAGE TEXT screen.
+When this feature is set (the default) Alpine will select possible web hostnames
+from the displayed text and display them in boldface for selection.
+This can be useful when you receive messages referencing World Wide Web
+sites without the use of complete URLs; for example, specifying only
+&quot;www.washington.edu/alpine/&quot; (which will <B>not</B> become a
+selectable
+item by setting <A HREF="h_config_enable_view_url">&quot;<!--#echo var="FEAT_enable-msg-view-urls"-->&quot;</A>)
+rather than explicitly
+&quot;http://www.washington.edu/alpine/&quot;.
+<P>
+The first available hostname is displayed in inverse. This is the
+&quot;selected&quot; hostname. Pressing RETURN will cause Alpine to display
+the selected hostname via an external application as defined
+by the <A HREF="h_config_browser">&quot;url-viewer&quot;</A>
+variable.
+<P>
+Use the arrow keys (unless you have set the feature
+<A HREF="h_config_enable_view_arrows">&quot;<!--#echo var="FEAT_enable-msg-view-forced-arrows"-->&quot;</A>)
+to change which of the hostnames displayed in
+boldface is the current selection.
+<P>
+Similarly, when selectable web hostnames are present in a message, the Ctrl-F
+key can be used to select the next web hostname in the message independent
+of which portion of the viewed message is currently displayed. The
+Ctrl-B key can be used to select the previous web hostnames in the same way.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_enable_view_addresses =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-msg-view-addresses"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-msg-view-addresses"--></H1>
+
+This feature modifies the behavior of Alpine's MESSAGE TEXT screen.
+Setting this feature causes Alpine to select possible email addresses
+from the displayed text and display them in boldface for selection.
+
+<P>
+The first available email address is displayed in inverse. This is the
+&quot;selected&quot; address. Pressing RETURN will cause Alpine to enter
+the message composition screen with the To: field filled in with the
+selected address.
+<P>
+Use the arrow keys (unless you have set the feature
+<A HREF="h_config_enable_view_arrows">&quot;<!--#echo var="FEAT_enable-msg-view-forced-arrows"-->&quot;</A>)
+to change which of the hostnames displayed in
+boldface is the current selection.
+<P>
+Similarly, when selectable web hostnames are present in a message, the Ctrl-F
+key can be used to select the next web hostname in the message independent
+of which portion of the viewed message is currently displayed. The
+Ctrl-B key can be used to select the previous web hostnames in the same way.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_enable_view_arrows =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-msg-view-forced-arrows"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-msg-view-forced-arrows"--></H1>
+
+This feature modifies Up and Down arrow key behavior in Alpine's
+MESSAGE TEXT screen when selectable Attachments, URL's, or
+web-hostnames are presented. Alpine's usual behavior is to move to
+the next or previous selectable item if currently displayed or
+simply to adjust the screen view by one line.
+
+<P>
+
+Setting this feature causes the UP and Down arrow key to behave as
+if no selectable items were present in the message.
+
+<P>
+
+Note, the Ctrl-F (next selectable item) and Ctrl-B (previous selectable
+item) functionality is unchanged.
+<P>
+
+&lt;End of help on this topic&gt;
+</BODY>
+<HTML>
+====== h_config_quell_charset_warning =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_quell-charset-warning"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_quell-charset-warning"--></H1>
+
+By default, if the message you are viewing contains characters that are
+not representable in your
+<A HREF="h_config_char_set">&quot;Display Character Set&quot;</A>
+then Alpine will
+add a warning to the start of the displayed text.
+If this option is set, then that editorial message will be suppressed.
+<P>
+Setting this feature also suppresses the comment about the character set
+in header lines.
+For example, when viewing a message you might see
+<P>
+<CENTER><SAMP>From: &quot;[ISO-8859-2] Name&quot; &lt;address&gt;</SAMP></CENTER>
+<P>
+in the From header if your Character-Set is something other than ISO-8859-2.
+If you set this feature, the comment about the character set will
+no longer be there.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_quell_host_after_url =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_quell-server-after-link-in-html"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_quell-server-after-link-in-html"--></H1>
+
+By default, links in HTML text are displayed with the host the link
+references appended, within square brackets, to the link text. Alpine
+does this to help indicate where a link will take you, particularly when
+the link text might suggest a different destination.
+
+<P>
+Setting this feature will prevent the server name from being appended
+to the displayed text.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_prefer_plain_text =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_prefer-plain-text"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_prefer-plain-text"--></H1>
+
+A message being viewed may contain alternate versions of the same content.
+Those alternate versions are supposed to be ordered by the sending software such that the
+first alternative is the least preferred and the last alternative is the
+most preferred. Alpine will normally display the most-preferred version that
+it knows how to display. This is most often encountered where the two
+alternate versions are a plain text version and an HTML version, with the
+HTML version listed last as the most preferred.
+<P>
+If this option is set, then any plain text version will be preferred to
+all other versions.
+<P>
+When viewing a message there is a command &quot;A TogglePreferPlain&quot;,
+which will temporarily change the sense of this option.
+If this option is set you will first see the plain text version of a
+message.
+If you then type the &quot;A&quot; command, you will see the most preferred version,
+most likely HTML, instead.
+Typing the &quot;A&quot; command a second time will switch it back.
+Alternatively, if the present option is not set you will originally see
+the most preferred version of the message and typing &quot;A&quot; will switch to
+the plain text version.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_pass_control =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_pass-control-characters-as-is"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_pass-control-characters-as-is"--></H1>
+
+It is probably not useful to set this option.
+This is a legacy option left behind &quot;just in case&quot;.
+Multi-byte characters that have an octet that has the same
+value as a control character are permitted through whether or not
+this option is turned on.
+<P>
+This feature controls how certain characters contained in messages are
+displayed.
+If set, all characters in a message will be sent to the
+screen. Normally, control characters are displayed as shown below to
+avoid a garbled screen and to
+avoid inadvertently changing terminal setup parameters.
+Control characters are usually displayed as two character sequences like
+<P><CENTER><SAMP> ^C </SAMP></CENTER><P>
+for Control-C,
+<P><CENTER><SAMP> ^[ </SAMP></CENTER><P>
+for ESCAPE,
+<P><CENTER><SAMP> ^? </SAMP></CENTER><P>
+for DELETE, and
+<P><CENTER><SAMP> ~E </SAMP></CENTER><P>
+for the character with value 133 (0x85).
+(The DEL character is displayed as ^?, regular control characters are displayed
+as the character ^ followed by the character obtained by adding the
+five low-order bits of the character to 0x40, and the C1
+control characters 0x80 - 0x9F are displayed as the character ~ followed by the
+character obtained by adding the
+five low-order bits of the character to 0x40.)
+Sometimes, in cases where changing a single control character into a
+two-character sequence would confuse Alpine's display routines,
+a question mark is substituted for the control character.
+<P>
+If you wish to filter out regular control characters but pass the
+so-called C1 control characters (0x80 <= char < 0xA0) through unchanged, then
+you may leave this feature unset and set the feature <A HREF="h_config_pass_c1_control"><!--#echo var="FEAT_pass-c1-control-characters-as-is"--></A> instead.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_pass_c1_control =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_pass-c1-control-characters-as-is"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_pass-c1-control-characters-as-is"--></H1>
+
+It is probably not useful to set this option.
+This is a legacy option left behind &quot;just in case&quot;.
+Multi-byte characters that have an octet that has the same
+value as a control character are permitted through whether or not
+this option is turned on.
+<P>
+If the feature <A HREF="h_config_pass_control"><!--#echo var="FEAT_pass-control-characters-as-is"--></A>
+is set, then this feature has no effect.
+However, if you wish to filter out regular control characters but pass the
+so-called C1 control characters (0x80 <= char < 0xA0) through unchanged, then
+you may leave <A HREF="h_config_pass_control"><!--#echo var="FEAT_pass-control-characters-as-is"--></A>
+unset and set this feature.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_fcc_on_bounce =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_fcc-on-bounce"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_fcc-on-bounce"--></H1>
+
+This feature controls an aspect of Alpine's behavior when bouncing a
+message. If set, normal FCC ("File Carbon Copy") processing will be
+done, just as if you had composed a message to the address you are
+bouncing to. If not set, no FCC of the message will be saved.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_show_cursor =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_show-cursor"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_show-cursor"--></H1>
+
+This feature controls an aspect of Alpine's displays. If set, the system
+cursor will move to convenient locations in the displays. For example,
+to the beginning of the status field of the highlighted index line, or
+to the highlighted word after a successful WhereIs command. It is intended
+to draw your attention to an "interesting" spot on the screen.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_sort_fcc_alpha =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_sort-default-fcc-alpha"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_sort-default-fcc-alpha"--></H1>
+
+This feature controls an aspect of Alpine's FOLDER LIST screen.
+If set, the default Fcc folder will be sorted alphabetically with the other
+folders instead of appearing right after the INBOX.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_sort_save_alpha =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_sort-default-save-alpha"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_sort-default-save-alpha"--></H1>
+
+This feature controls an aspect of Alpine's FOLDER LIST screen.
+If set, the default save folder will be sorted alphabetically with the other
+folders instead of appearing right after the INBOX (and default FCC folder).
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_single_list =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_single-column-folder-list"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_single-column-folder-list"--></H1>
+
+This feature controls an aspect of Alpine's FOLDER LIST screen. If set,
+the folders will be listed one per line instead of several per line
+in the FOLDER LIST display.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_vertical_list =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_vertical-folder-list"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_vertical-folder-list"--></H1>
+
+This feature controls an aspect of Alpine's FOLDER LIST screen. If set,
+the folders will be listed alphabetically down the columns rather
+than across the columns as is the default.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_verbose_post =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-verbose-smtp-posting"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-verbose-smtp-posting"--></H1>
+This feature controls an aspect of Alpine's message sending. When enabled,
+Alpine will send a VERB (i.e., VERBose) command early in the posting process
+intended to cause the SMTP server to provide a more detailed account of
+the transaction. This feature is typically only useful to system
+administrators and other support personel as an aid in troublshooting
+problems.
+<P>
+Note, this feature relies on a specific capability of the system's mail
+transport agent or configured
+<A HREF="h_config_smtp_server">&quot;<!--#echo var="VAR_smtp-server"-->&quot;</A>.
+It is possible that this
+feature will cause problems for some tranport agents, and may result in
+sending failure. In addition, as the verbose output comes from the mail
+transport agent, it is likely to vary from one system to another.
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_auto_reply_to =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_reply-always-uses-reply-to"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_reply-always-uses-reply-to"--></H1>
+
+This feature controls an aspect of Alpine's Reply command. If set, Alpine
+will not prompt when a message being replied to contains a "Reply-To:"
+header value, but will simply use its value (as opposed to using the
+"From:" field's value).
+
+<P>
+
+Note: Using the "Reply-To:" address is usually the preferred behavior,
+however, some mailing list managers choose to place the list's address in
+the "Reply-To:" field of any message sent out to the list. In such
+cases, this feature makes it all too easy for personal replies to be
+inadvertently sent to the entire mail list, so be careful!
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_del_skips_del =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_delete-skips-deleted"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_delete-skips-deleted"--></H1>
+
+This feature controls an aspect of Alpine's Delete command. If set, this
+feature will cause the Delete command to advance past following messages that
+are marked deleted. In other words, pressing "D" will both mark the
+current message deleted and advance to the next message that is not marked
+deleted.
+This feature is set by default.
+<P>
+&lt;End of help on this topic&gt;
+</BODY></HTML>
+====== h_config_expunge_manually =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_expunge-only-manually"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_expunge-only-manually"--></H1>
+
+Normally, when you close a folder that contains deleted messages you are
+asked if you want to expunge those messages from the folder permanently.
+If this feature is set, you won't be asked and the deleted messages will
+remain in the folder.
+If you choose to set this feature you will have to expunge the
+messages manually using the eXpunge command, which you can use while
+in the MESSAGE INDEX screen.
+If you do not expunge deleted messages the size of your
+folder will continue to increase until you are out of disk space.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_auto_expunge =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_expunge-without-confirm"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_expunge-without-confirm"--></H1>
+
+This features controls an aspect of Alpine's eXpunge command. If set, you
+will <B>not</B> be prompted to confirm your intent before the expunge takes
+place.
+Actually, this is only true for the INBOX folder and for folders in the
+Incoming Folders collection. See the feature
+<A HREF="h_config_full_auto_expunge">&quot;<!--#echo var="FEAT_expunge-without-confirm-everywhere"-->&quot;</A>.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_full_auto_expunge =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_expunge-without-confirm-everywhere"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_expunge-without-confirm-everywhere"--></H1>
+
+This features controls an aspect of Alpine's eXpunge command. If set, you
+will <B>not</B> be prompted to confirm your intent before the expunge
+takes place. This feature sets this behavior for all folders, unlike the
+<A HREF="h_config_auto_expunge">&quot;<!--#echo var="FEAT_expunge-without-confirm"-->&quot;</A>
+feature that works only for incoming folders.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_auto_read_msgs =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_auto-move-read-msgs"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_auto-move-read-msgs"--></H1>
+This feature controls an aspect of Alpine's behavior upon quitting. If set,
+and the
+<A HREF="h_config_read_message_folder">&quot;<!--#echo var="VAR_read-message-folder"-->&quot;</A>
+option is also set, then Alpine will
+automatically transfer all read messages to the designated folder and mark
+them as deleted in the INBOX. Messages in the INBOX marked with an
+&quot;N&quot; (meaning New, or unseen) are not affected.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_auto_fcc_only =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_fcc-only-without-confirm"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_fcc-only-without-confirm"--></H1>
+This features controls an aspect of Alpine's composer.
+The only time this feature will be used is if you attempt to send mail
+that has no recipients but does have an Fcc.
+Normally, Alpine will ask if you really mean to copy the message only to
+the Fcc.
+That is, it asks if you really meant to have no recipients.
+If this feature is set, you
+will <B>not</B> be prompted to confirm your intent to make only a copy
+of a message with no recipients.
+<P>
+This feature is closely related to
+<A HREF="h_config_warn_if_no_to_or_cc"><!--#echo var="FEAT_warn-if-blank-to-and-cc-and-newsgroups"--></A>.
+The difference between this feature and that feature is that this feature
+considers a Bcc to be a recipient while that feature will ask for confirmation
+even if there is a Bcc when there is no To, Cc, or Newsgroup.
+The default values also differ. This feature defaults to asking the question
+and you have to turn it off.
+The <!--#echo var="FEAT_warn-if-blank-to-and-cc-and-newsgroups"--> feature defaults to not asking
+unless you turn it on.
+<P>
+
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_mark_fcc_seen =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_mark-fcc-seen"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_mark-fcc-seen"--></H1>
+
+This features controls the way Fccs (File carbon copies) are
+made of the messages you send.
+
+<P>
+Normally, when Alpine saves a copy of a message you sent as an Fcc, that
+copy will be marked as Unseen.
+When you look at the folder it was saved in the message will appear to
+be a New message until you read it.
+When this feature is enabled, the message will be marked as having
+been Seen.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_no_fcc_attach =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_fcc-without-attachments"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_fcc-without-attachments"--></H1>
+
+This features controls the way Fcc's (File carbon copies) are
+made of the messages you send.
+
+<P>
+Normally, Alpine saves an exact copy of your message as it was sent.
+When this feature is enabled, the &quot;body&quot; of the message
+you send (the text you type in the composer) is preserved in the
+copy as before, however all attachments are replaced with text
+explaining what had been sent rather than the attachments themselves.
+
+<P>
+This feature also affects Alpine's &quot;Send ?&quot; confirmation prompt
+in that a new &quot;^F Fcc Attchmnts&quot; option becomes available which
+allows you to interactively set whether or not attachments are saved
+to the Fcc'd copy.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_read_in_newsrc_order =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_news-read-in-newsrc-order"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_news-read-in-newsrc-order"--></H1>
+
+This feature controls the order in which newsgroups will be presented. If
+set, they will be presented in the same order as they occur in your
+<!--chtml if pinemode="os_windows"-->
+&quot;NEWSRC&quot;
+<!--chtml else-->
+&quot;.newsrc&quot;
+<!--chtml endif-->
+file (the default location of which can be changed with the
+<A HREF="h_config_newsrc_path">&quot;<!--#echo var="VAR_newsrc-path"-->&quot;</A> option).
+<P>
+If not set, the newsgroups will be presented in alphabetical order.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_quell_tz_comment =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_quell-timezone-comment-when-sending"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_quell-timezone-comment-when-sending"--></H1>
+
+Normally, when Alpine generates a Date header for outgoing mail,
+it will try to include the symbolic timezone at the end of the
+header inside parentheses.
+The symbolic timezone is often three characters long, but on
+some operating systems, it may be longer.
+Apparently there are some SMTP servers in the world that will reject an
+incoming message if it has a Date header longer than about 80 characters.
+If this feature is set, the symbolic timezone normally generated by
+Alpine will not be included.
+You probably don't need to worry about this feature unless you run into
+the problem described above.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_post_wo_validation =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_news-post-without-validation"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_news-post-without-validation"--></H1>
+
+This feature controls whether the NNTP server is queried as newsgroups
+are entered for posting. Validation over slow links (e.g. dialup using
+SLIP or PPP) can cause delays. Set this feature to eliminate such delays.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_send_wo_confirm =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_send-without-confirm"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_send-without-confirm"--></H1>
+
+By default, when you send or post a message you will be asked to confirm
+with a question that looks something like:
+
+<P>
+<CENTER><SAMP>Send message?</SAMP></CENTER>
+<P>
+
+If this feature is set, you
+will <B>not</B> be prompted to confirm your intent to send
+and your message will be sent.
+<P>
+If this feature is set it disables some possibilities and renders some
+other features meaningless.
+You will not be able to use
+<A HREF="h_config_sending_filter">Sending Filters</A>,
+Verbose sending mode,
+<A HREF="h_config_compose_bg_post">Background Sending</A>,
+<A HREF="h_config_compose_dsn">Delivery Status Notifications</A>,
+or ^V to turn off the generation of flowed text for this message.
+These options are normally available as suboptions in the Send prompt, but
+with no Send prompt the options are gone.
+
+<P>
+A somewhat related feature is
+<A HREF="h_config_quell_post_prompt">&quot;<!--#echo var="FEAT_quell-extra-post-prompt"-->&quot;</A>,
+which may be used to eliminate the extra confirmation
+question when posting to a newsgroup.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_quell_filtering_done_message =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_quell-filtering-done-message"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_quell-filtering-done-message"--></H1>
+
+If you use Filter Rules that move messages or set status of messages
+you sometimes see a message from Alpine that looks like
+
+<P>
+<CENTER><SAMP>filtering done</SAMP></CENTER>
+<P>
+
+If this feature is set, this message will be suppressed.
+If the feature
+<A HREF="h_config_quell_filtering_messages"><!--#echo var="FEAT_quell-filtering-messages"--></A>
+is set then this message will be suppressed regardless.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_quell_filtering_messages =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_quell-filtering-messages"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_quell-filtering-messages"--></H1>
+
+If you use Filter Rules that move messages or set status of messages
+you sometimes see messages from Alpine that look like
+
+<P>
+<CENTER><SAMP>&lt;filter name&gt;: Moving 2 filtered messages to &lt;folder name&gt;</SAMP></CENTER>
+<P>
+
+or
+
+<P>
+<CENTER><SAMP>&lt;filter name&gt;: Setting flags in 5 messages</SAMP></CENTER>
+<P>
+
+or
+
+<P>
+<CENTER><SAMP>Processing filter &lt;filter name&gt;</SAMP></CENTER>
+<P>
+
+If this feature is set, these messages will be suppressed.
+The feature
+<A HREF="h_config_quell_filtering_done_message"><!--#echo var="FEAT_quell-filtering-done-message"--></A>
+is related.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_quell_post_prompt =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_quell-extra-post-prompt"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_quell-extra-post-prompt"--></H1>
+
+By default, when you post a message to a newsgroup you are asked to confirm
+that you want to post with the question
+
+<P>
+<CENTER><SAMP>Posted message may go to thousands of readers. Really post?</SAMP></CENTER>
+<P>
+
+If this feature is set, you
+will <B>not</B> be prompted to confirm your intent to post to a newsgroup
+and your message will be posted.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_check_mail_onquit =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_check-newmail-when-quitting"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_check-newmail-when-quitting"--></H1>
+
+If this feature is set, Alpine will check for new mail after you give the
+Quit command.
+If new mail has arrived since the previous check, you will be notified
+and given the choice of quitting or not quitting.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_inbox_no_confirm =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_return-to-inbox-without-confirm"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_return-to-inbox-without-confirm"--></H1>
+
+Normally, when you use the TAB <A HREF="h_common_nextnew">NextNew</A>
+command and there are no more folders or newsgroups to visit, you are asked
+if you want to return to the INBOX.
+If this feature is set you will not be asked.
+It will be assumed that you do want to return to the INBOX.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_dates_to_local =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_convert-dates-to-localtime"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_convert-dates-to-localtime"--></H1>
+
+Normally, the message dates that you see in the
+MESSAGE INDEX and MESSAGE VIEW are displayed in the timezone they were sent from.
+For example, if a message was sent to you from a few timezones to the east
+it might appear that it was sent from the future;
+or if it was sent from somewhere to the west it might appear
+as if it is from yesterday even though it was sent only a few minutes ago.
+If this feature is set an attempt will be made to convert the dates
+to your local timezone to be displayed.
+<P>
+Note that this does not affect the results of Select by Date or of
+anything else other than these displayed dates.
+When viewing the message you may look at the original unconverted value of the Date
+header by using the <A HREF="h_common_hdrmode">HdrMode Command</A>.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_tab_no_prompt =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_continue-tab-without-confirm"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_continue-tab-without-confirm"--></H1>
+
+Normally, when you use the TAB <A HREF="h_common_nextnew">NextNew</A>
+command and there is a problem checking a folder, you are asked
+whether you want to continue with the search in the following folder or not.
+This question gives you a chance to stop the NextNew processing.
+(The checking problem might be caused by the fact that the folder does not
+exist, or by an authentication problem, or by a server problem
+of some sort.)
+<P>
+
+If this feature is set you will not be asked.
+It will be assumed that you do want to continue.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_input_history =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_disable-save-input-history"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_disable-save-input-history"--></H1>
+
+Many of the prompts that ask for input in the status line near the
+bottom of the screen will respond to Up Arrow and Down Arrow
+with the history of previous entries.
+For example, in the MESSAGE INDEX screen when you use the WhereIs
+command the text you entered will be remembered and can be recalled
+by using the Up Arrow key.
+Another example, when saving a message the folders saved to will
+be remembered and can be recalled using the arrow keys.
+<P>
+In the Save prompt, some users prefer that the Up and Down arrow keys
+be used for the Previous Collection and Next Collection commands
+instead of for a history of previous saves.
+If this option is set the Up and Down arrow keys will become synonyms for the
+Previous Collection and Next Collection (^P and ^N) commands in the
+prompt for the name of a folder to Save to or in the prompt for the
+name of a folder to GoTo.
+When this feature is not set (the default), ^P and ^N will change the
+collection and the arrow keys will show the history.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_confirm_role =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_confirm-role-even-for-default"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_confirm-role-even-for-default"--></H1>
+
+If you have roles, when you Reply to or Forward a message, or Compose
+a new message, Alpine
+will search through your roles for one that matches.
+Normally, if no matches are found you will be placed into the composer
+with no opportunity to select a role.
+If this feature is set, then you will be asked to confirm that you don't
+want a role.
+This will give you the opportunity to select a role (with the ^T command).
+If you confirm no role with a Return, you will be placed in
+the composer with no role.
+You may also confirm with either an &quot;N&quot; or a &quot;Y&quot;.
+These behave the same as if you pressed the Return.
+(The &quot;N&quot; and &quot;Y&quot; answers are available because they
+match what you might type if there was a role match.)
+<P>
+If you are using the alternate form of the Compose command called
+&quot;Role&quot;, then all of your roles will be available to you,
+independent of the value of this feauture and of the values set for all of
+Reply Use, Forward Use, and Compose Use.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_news_cross_deletes =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_news-deletes-across-groups"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_news-deletes-across-groups"--></H1>
+
+This feature controls what Alpine does when you delete a message in a
+newsgroup that appears in more than one newsgroup. Such a message
+is sometimes termed a &quot;crossposting&quot; in that it was posted
+across several newsgroups.
+
+<P>
+Alpine's default behavior when you delete such a message is to remove
+only the copy in the current newsgroup from view when you use the
+&quot;Exclude&quot; command or the next time you visit the newsgroup.
+
+<P>
+Enabling this feature causes Alpine to remove every occurrence of the
+message from all newsgroups it appears in and to which you are
+subscribed.
+
+<P>
+NOTE: As currently implemented, enabling this feature may increase the
+time it takes the Expunge command and newsgroup closing to complete.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_news_catchup =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_news-offers-catchup-on-close"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_news-offers-catchup-on-close"--></H1>
+
+This feature controls what Alpine does as it closes a newsgroup.
+When set, Alpine will offer to delete all messages from the newsgroup
+as you are quitting Alpine or opening a new folder.
+
+<P>
+This feature is useful if you typically read all the interesting messages
+in a newsgroup each time you open it. This feature saves you from
+having to delete each message in a newsgroup as you read it or from
+selecting all the messages and doing an
+<A HREF="h_config_enable_agg_ops">aggregate delete</A> before you
+move on to the next folder or newsgroup.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_next_thrd_wo_confirm =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_next-thread-without-confirm"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_next-thread-without-confirm"--></H1>
+
+This feature controls an aspect of Alpine's Next and Prev commands in
+the case where you are using one of the
+&quot;separate-index-screen&quot; styles for the configuration option
+<A HREF="h_config_thread_index_style">&quot;<!--#echo var="VAR_threading-index-style"-->&quot;</A>
+and currently have the folder sorted by a Threaded or OrderedSubject sort.
+When you are Viewing a particular thread you have a
+MESSAGE INDEX of only the messages in that thread.
+If you press the Next command with the last message in the thread highlighted
+you will normally be asked if you want to &quot;View next thread?&quot;,
+assuming there is a next thread to view.
+If this feature is set it will be assumed that you always want to view the
+next thread and you won't be asked to confirm that.
+Similarly, if the first message of the thread is highlighted and you
+press the Prev command, this feature will prevent the question
+&quot;View previous thread&quot;.
+<P>
+This feature only has an effect in the MESSAGE INDEX screen.
+If you then view a particular message from that screen and press the
+Next command, you will be sent to the next thread without being asked,
+independent of the setting of this feature.
+<P>
+The feature
+<A HREF="h_config_auto_open_unread">&quot;<!--#echo var="FEAT_auto-open-next-unread"-->&quot;</A> also has some similar effects.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_kw_braces =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_keyword-surrounding-chars"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_keyword-surrounding-chars"--></H1>
+
+This option controls a minor aspect of Alpine's MESSAGE INDEX and MESSAGE
+TEXT screens.
+If you have modified the
+&quot;<A HREF="h_config_index_format"><!--#echo var="VAR_index-format"--></A>&quot; option
+so that either the &quot;SUBJKEY&quot; or &quot;SUBJKEYINIT&quot; tokens
+are used to display keywords or their initials along with the Subject; then
+this option may be used to modify the resulting display slightly.
+By default, the keywords or initials displayed for these tokens will be
+surrounded with curly braces ({ and }) and a trailing space.
+For example, if keywords &quot;Work&quot; and &quot;Now&quot; are set for
+a message, the &quot;SUBJKEY&quot; token will normally look like
+<P>
+<CENTER><SAMP>{Work Now} actual subject</SAMP></CENTER>
+<P>
+and the SUBJKEYINIT token would look like
+<P>
+<CENTER><SAMP>{WN} actual subject</SAMP></CENTER>
+<P>
+The default character before the keywords is the left brace ({) and the
+default after the keywords is the right brace followed by a space (} ).
+<P>
+This option allows you to change that.
+You should set it to two values separated by a space.
+The values may be quoted if they include space characters.
+So, for example, the default value could be specified explicitly by setting this
+option to
+<P>
+<CENTER><SAMP><!--#echo var="VAR_keyword-surrounding-chars"-->="{" "}&nbsp;"</SAMP></CENTER>
+<P>
+The first part wouldn't need to be quoted (but it doesn't hurt).
+The second part does need the quotes because it includes a space character.
+If you wanted to change the braces to brackets you could use
+<P>
+<CENTER><SAMP><!--#echo var="VAR_keyword-surrounding-chars"-->="[" "]&nbsp;"</SAMP></CENTER>
+<P>
+Inside the quotes you can use backslash quote to mean quote, so
+<P>
+<CENTER><SAMP><!--#echo var="VAR_keyword-surrounding-chars"-->="&#92;"" "&#92;" "</SAMP></CENTER>
+<P>
+would produce
+<P>
+<CENTER><SAMP>"Work Now" actual subject</SAMP></CENTER>
+<P>
+It is also possible to color keywords in the index using the
+Setup/Kolor screen (<A HREF="h_config_kw_color">Keyword Colors</A>).
+<P>
+It is not possible to change the fact that a space character is used to
+separate the keywords if more than one keyword is set for a message.
+It is also not possible to change the fact that there are no separators
+between the keyword initials if more than one keyword is set.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_opening_sep =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_opening-text-separator-chars"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_opening-text-separator-chars"--></H1>
+
+This option controls a minor aspect of Alpine's MESSAGE INDEX screen.
+With some setups the text of the subject is followed
+by the opening text of the message if there is any room available in the index line.
+If you have configured your
+<A HREF="h_config_index_format"><!--#echo var="VAR_index-format"--></A> option
+to include one of the Subject tokens that causes this behavior
+(SUBJECTTEXT, SUBJKEYTEXT, or SUBJKEYINITTEXT), then this option may be used
+to modify what is displayed slightly.
+By default, the Subject is separated from the opening text of the message by
+the three characters space dash space;
+<P>
+<CENTER><SAMP>&quot;&nbsp;-&nbsp;&quot;</SAMP></CENTER>
+<P>
+Use this option to set it to something different.
+The value must be quoted if it includes any space characters.
+For example, the default value could be specified explicitly by setting this
+option to
+<P>
+<CENTER><SAMP><!--#echo var="VAR_opening-text-separator-chars"-->="&nbsp;-&nbsp;"</SAMP></CENTER>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_select_wo_confirm =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_select-without-confirm"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_select-without-confirm"--></H1>
+
+This feature controls an aspect of Alpine's Save, Export, and Goto commands.
+These commands all take text input to specify the name of the folder or
+file to be used, but allow you to press ^T for a list of possible names.
+If set, the selected name will be used immediately, without further
+opportunity to confirm or edit the name.
+<P>
+Some related help topics are
+<UL>
+<LI> <A HREF="h_mainhelp_aggops">Aggregate Operations</A>
+<LI> <A HREF="h_index_cmd_select">Selecting: Select and WhereIs/Select</A>,
+<LI> <A HREF="h_config_enable_agg_ops"><!--#echo var="FEAT_enable-aggregate-command-set"--></A>,
+<LI> <A HREF="h_config_auto_unselect"><!--#echo var="FEAT_auto-unselect-after-apply"--></A>.
+<LI> <A HREF="h_config_auto_unzoom"><!--#echo var="FEAT_auto-unzoom-after-apply"--></A>, and
+<LI> <A HREF="h_config_auto_zoom"><!--#echo var="FEAT_auto-zoom-after-select"--></A>.
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_save_part_wo_confirm =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_save-partial-msg-without-confirm"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_save-partial-msg-without-confirm"--></H1>
+
+This feature controls an aspect of Alpine's Save command.
+By default, when you Save a message that has some deleted parts, you will
+be asked to confirm that you want to Save with a prompt that looks like:
+<P>
+<CENTER><SAMP>Saved copy will NOT include entire message! Continue?</SAMP></CENTER>
+<P>
+If this feature is set, you will not be asked.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_use_resentto =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_use-resent-to-in-rules"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_use-resent-to-in-rules"--></H1>
+
+This feature is turned off by default because turning it on causes problems
+with some deficient IMAP servers.
+In Alpine <A HREF="h_mainhelp_filtering">Filters</A> and other types of Rules, if the
+<A HREF="h_rule_patterns">Pattern</A>
+contains a To header pattern and this feature is turned on,
+then a check is made in the message to see
+if a Resent-To header is present, and that is used instead of the To header.
+If this feature is not turned on, then the regular To header will always
+be used.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_use_reg_start_for_stayopen =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_use-regular-startup-rule-for-stayopen-folders"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_use-regular-startup-rule-for-stayopen-folders"--></H1>
+
+This feature affects which message is selected as the current message
+when you enter a
+<A HREF="h_config_permlocked">Stay Open</A> folder.
+<P>
+Normally, the starting position for an incoming folder (which most Stay Open
+folders will likely be) is controlled by the
+<A HREF="h_config_inc_startup"><!--#echo var="VAR_incoming-startup-rule"--></A>.
+However, if a folder is a Stay Open folder, when you re-enter the folder
+after the first time the current message will be the same as it was when
+you left the folder.
+An exception is made if you use the TAB command to get to the folder.
+In that case, the message number will be incremented by one from what it
+was when you left the folder.
+<P>
+The above special behavior is thought to be useful.
+However, it is special and different from what you might at first expect.
+If this feature is set, then Stay Open folders will not be treated specially
+as far as the startup rule is concerned.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_use_current_dir =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_use-current-dir"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_use-current-dir"--></H1>
+
+This feature controls an aspect of several commands.
+If set, your &quot;current working directory&quot;
+<!--chtml if pinemode="running"-->
+(which, at least for your current Alpine &quot;session,&quot;
+is &quot;<!--#echo var="CURRENT_DIR"-->&quot;)
+<!--chtml endif-->
+will be used instead of your home directory
+<!--chtml if pinemode="running"-->
+(which, in the present configuration of your system, is
+ &quot;<!--#echo var="HOME_DIR"-->&quot;)
+<!--chtml endif-->
+for all of the following operations:<UL>
+ <LI> Export in the MESSAGE INDEX and MESSAGE TEXT screens
+ <LI> Attachment Save in the MESSAGE TEXT and ATTACHMENT TEXT screens
+ <LI> <!--chtml if pinemode="function_key"-->F4
+ <!--chtml else-->Ctrl-R
+ <!--chtml endif--> file inclusion in the COMPOSER
+ <LI> <!--chtml if pinemode="function_key"-->F5
+ <!--chtml else-->Ctrl-J
+ <!--chtml endif--> file attachment in the COMPOSER
+</UL>
+<!--chtml if pinemode="os_windows"-->
+<P>
+If you are starting PC-Alpine from a desktop icon or the Start menu,
+you can set the &quot;current drive&quot;
+by specifying it in the &quot;Start in:&quot;
+box found in the Shortcut tab of the Properties.
+<!--chtml endif-->
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_save_wont_delete =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_save-will-not-delete"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_save-will-not-delete"--></H1>
+
+This feature controls one aspect of the Save command. If set, Save will
+not mark the message &quot;deleted&quot; (its default behavior) after
+it has been copied to the designated folder.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_use_boring_spinner =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_busy-cue-spinner-only"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_busy-cue-spinner-only"--></H1>
+
+When Alpine is delayed for some reason it usually shows that
+something is happening with a small animated display in the status
+message line near the bottom of the screen.
+Setting this feature will cause that animation to be the same
+each time instead of having Alpine choose a random animation.
+You may turn the animation off altogether by setting the
+<A HREF="h_config_active_msg_interval"><!--#echo var="VAR_busy-cue-rate"--></A>
+option to zero.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_unsel_wont_advance =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_unselect-will-not-advance"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_unselect-will-not-advance"--></H1>
+
+This feature controls one aspect of the Unselect Current message command.
+Normally, when the Unselect current message command (:) is typed when the
+current message is selected, the message will be unselected and the next
+message will become the current message.
+If this feature is set, the cursor will not advance to the next message.
+Instead, the current message will remain the current message after
+unselecting.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_prune_uses_iso =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_prune-uses-yyyy-mm"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_prune-uses-yyyy-mm"--></H1>
+
+By default, Alpine asks monthly whether or not you would like to rename
+some folders to a new name containing the date.
+It also asks whether or not you would like to delete some old folders.
+See the <A HREF="h_config_pruning_rule"><!--#echo var="VAR_pruning-rule"--></A> option for an
+explanation.
+
+<P>
+By default, the name used when renaming a folder looks like
+<P>
+<CENTER><SAMP>&lt;foldername&gt;-&lt;month&gt;-&lt;year&gt;</SAMP></CENTER>
+<P>
+For example, the first time you run Alpine in May of 2004,
+the folder &quot;sent-mail&quot; might be renamed to
+<P>
+<CENTER><SAMP>sent-mail-apr-2004</SAMP></CENTER>
+<P>
+If this feature is set, the name used will be of the form
+<P>
+<CENTER><SAMP>&lt;foldername&gt;-&lt;yyyy&gt;-&lt;mm&gt;</SAMP></CENTER>
+<P>
+where &quot;yyyy&quot; is the year and &quot;mm&quot; is the two-digit
+month (01, 02, ..., 12).
+For the April, 2004 example above, it would instead be
+<P>
+<CENTER><SAMP>sent-mail-2004-04</SAMP></CENTER>
+<P>
+because April is the 4th month of the year.
+A reason you might want to set this feature is so that the folders
+will sort in chronological order.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_save_advances =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_save-will-advance"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_save-will-advance"--></H1>
+
+This feature controls one aspect of the Save command. If set, Save will
+(in addition to copying the current message to the designated folder) also
+advance to the next message.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_force_arrow =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_force-arrow-cursor"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_force-arrow-cursor"--></H1>
+
+This feature affects Alpine's MESSAGE INDEX display routine.
+If set, the normal inverse-video cursor will be
+replaced by a simple &quot;arrow&quot; cursor, which normally occupies the
+second column of the index display.
+<P>
+This is the same index cursor you get if you turn on
+<A HREF="h_config_force_low_speed"><!--#echo var="FEAT_assume-slow-link"--></A>, but the index
+line coloring will still be present if this feature is turned on and
+<!--#echo var="FEAT_assume-slow-link"--> is off.
+<P>
+An alternative version of the Arrow cursor is available by including the
+<A HREF="h_config_index_arrow_color">ARROW</A>
+token in the
+<A HREF="h_config_index_format"><!--#echo var="VAR_index-format"--></A> option.
+<P>
+It ought to be the case that this feature also affects the ATTACHMENT INDEX,
+but that is not implemented.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_force_low_speed =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_assume-slow-link"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_assume-slow-link"--></H1>
+
+UNIX Alpine only.
+<P>
+This feature affects Alpine's display routines. If set, the normal
+inverse-video cursor (used to highlight the current item in a list) will be
+replaced by an &quot;arrow&quot; cursor and other
+screen update optimizations for
+low-speed links (e.g. 2400 bps dialup connections) will be activated.
+One of the optimizations is that colored index lines (set up with Indexcolor
+Rules) will not be colored.
+If you are just turning this feature on because you like using
+the &quot;arrow&quot; cursor you may have an arrow cursor with index line
+coloring by turning this feature off and the
+<A HREF="h_config_force_arrow"><!--#echo var="FEAT_force-arrow-cursor"--></A> on.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_show_delay_cue =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-mail-check-cue"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-mail-check-cue"--></H1>
+
+If set, this feature will cause an asterisk to appear in the upper
+left-hand corner of the screen whenever Alpine checks for new mail.
+Two asterisks whenever Alpine saves (checkpoints) the state of the current
+mailbox to disk.
+
+<!--chtml if pinemode="os_windows"-->
+<P>
+In addition, PC-Alpine will display a less-than symbol, '&lt;', when
+it is trying to open a network connection (e.g, to open your INBOX
+on an IMAP
+server) or read from the network connection. A greater-than symbol,
+will be displayed when PC-Alpine is trying to write to the network
+connection (e.g, sending a command to your IMAP server).
+<!--chtml endif-->
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_color_style =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Color Style</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Color Style</H1>
+
+UNIX Alpine only.
+<P>
+If the terminal or terminal emulator you are using is capable of displaying
+colors, this option controls whether or not color will be used in Alpine.
+If you turn color on and things are set up correctly,
+you should see color appear on the screen immmediately.
+Modern terminal emulators are usually capable of displaying colors.
+<P>
+The available options include:
+<P>
+
+<DL>
+<DT>no-color</DT>
+<DD>Don't use color.
+</DD>
+
+<DT>use-termdef</DT>
+<DD>In order to decide if your terminal is capable of color, Alpine looks in
+the terminal capabilities database, TERMINFO or TERMCAP, depending on
+how Alpine was compiled.
+This is a good option to choose if you switch between a color and a non-color
+terminal with the same Alpine configuration.
+Alpine will know to use color on the color terminal because it is described
+in the termcap entry, and Alpine will know to use black and white on the
+non-color terminal.
+The Alpine Technical Notes
+<CENTER><SAMP><A HREF="http://www.washington.edu/alpine/tech-notes/">http://www.washington.edu/alpine/tech-notes/</A></SAMP></CENTER>
+have more information on configuring a TERMCAP or TERMINFO
+entry for color Alpine.
+This is usually something a system administrator does.
+</DD>
+
+<DT>force-ansi-8color</DT>
+<DD>This is probably the setting that most people should use.
+Because setting up a termcap entry is confusing and because the
+terminal capabilities database is often not correctly configured for color,
+this choice and the next may be easier for you to use.
+If your terminal emulator responds to ANSI color escape sequences, which
+many do, this option will cause Alpine to believe your terminal will respond
+to the escape sequences that produce eight different foreground and background
+colors.
+The escape sequences used to set the foreground colors are
+
+ <P><CENTER>ESC&nbsp;[&nbsp;3&nbsp;&lt;color_number&gt;&nbsp;m</CENTER><P>
+
+where the color_number is an ASCII digit between 0 and 7.
+The numbers 0 through 7 should correspond to the colors black, red, green,
+yellow, blue, magenta, cyan, and white.
+Some terminal emulators use a pre-ANSI scheme that swaps
+the colors blue and red and the colors yellow and cyan.
+This will cause the default colors to be different, but other than that
+things should work fine.
+There is also a 9th color available, the last one shown, which is the default
+color from the terminal emulator.
+When used as a background color some people refer to this color as
+&quot;transparent&quot;, which is why the letters &quot;TRAN&quot; are
+shown in the color swatch of the SETUP COLOR screen.
+The foreground transparent color is shown as
+the color of the &quot;TRAN&quot; text.
+(The transparent color will not work correctly in a PC-Alpine configuration.)
+The escape sequences used to set the background colors are the same
+as for the foreground colors except a &quot;4&quot; replaces the &quot;3&quot;.
+The escape sequences for foreground and background default colors (transparent)
+are 39m and 49m.
+<P>
+Note: With the Tera Term terminal emulator this setting works well.
+You should also have the Tera Term &quot;Full color&quot; option turned OFF.
+You may find the &quot;Full color&quot; option in Tera Term's &quot;Setup&quot;
+menu, in the &quot;Window&quot; submenu.
+</DD>
+
+<DT>force-ansi-16color</DT>
+<DD>Many terminal emulators know about the same eight colors above
+plus eight more.
+This option attempts to use all 16 colors.
+The same escape sequences as for the eight-color terminal are used
+for the first eight colors.
+The escape sequences used to set foreground colors 8-15 are the same as
+for 0-7 except the &quot;3&quot; is replaced with a &quot;9&quot;.
+The background color sequences for colors 8-15 are the same as for 0-7
+except the &quot;4&quot; is replaced with &quot;10&quot;.
+You can tell if the 16 colors are working by turning on this option
+and then going into one of the color configuration screens, for example,
+the configuration screen for Normal Color.
+If you see 16 different colors to select from (plus a 17th for
+the transparent color), it's working.
+</DD>
+
+<DT>force-xterm-256color</DT>
+<DD>Some versions of xterm (and some other terminal emulators)
+have support for 256 colors.
+The escape sequences used to set the foreground colors are
+
+ <P><CENTER>ESC&nbsp;[&nbsp;38&nbsp;;&nbsp;5&nbsp;;&nbsp;&lt;color_number&gt;&nbsp;m</CENTER><P>
+
+where the color_number is an ASCII digit between 0 and 255.
+Background colors are the same with the 38 replaced with a 48.
+The numbers 0 through 15 are probably similar to the 16 color version
+above, then comes a 6x6x6 color cube, followed by 24 colors of gray.
+The terminal default (transparent) color is the 257th color at the bottom.
+Some terminal emulators will misinterpret these escape sequences causing
+the terminal to blink or overstrike characters or to do something else
+undesirable.
+<P>
+The PuTTY terminal emulator has an option called &quot;Allow terminal to
+use xterm 256-colour mode&quot; which allows PuTTY to work well with
+this 256-color setting.
+
+</DD>
+</DL>
+
+<P>
+The normal default is &quot;no-color&quot;.
+<P>
+
+Once you've turned on color you may set the
+colors of many objects on the screen individually.
+For example, you may add colors to the status letters on the MESSAGE
+INDEX page.
+Most categories of color that Alpine supports are configurable here.
+For example, &quot;Normal Color&quot;
+is the color used to display most of the text in Alpine, and
+&quot;Reverse Color&quot; is used to display highlighted text, such as the
+current message in the MESSAGE INDEX.
+<P>
+Lines in the MESSAGE INDEX may also be colored.
+Use Setup Rules to get to the Indexcolor configuration screen.
+<P>
+
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_disable_index_locale_dates =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_disable-index-locale-dates"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_disable-index-locale-dates"--></H1>
+
+This feature affects the display of dates in the MESSAGE INDEX.
+Normally an attempt is made to localize the dates
+used in the MESSAGE INDEX display to your locale.
+This is controlled with the
+LC_TIME locale setting on a UNIX system.
+On Windows the Regional Options control panel may be used to set the date format.
+At the programming level, Alpine is using the strftime routine
+to print the parts of a date.
+<P>
+If this feature is set, dates are displayed in English and
+with the conventions of the United States.
+<P>
+
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_auto_open_unread =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_auto-open-next-unread"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_auto-open-next-unread"--></H1>
+
+This feature controls the behavior of the TAB key when traversing folders
+in the optional
+<A HREF="h_config_enable_incoming">&quot;<!--#echo var="VAR_incoming-folders"-->&quot;</A>
+collection or in optional <!--#echo var="VAR_news-collections"-->.
+<P>
+When the TAB
+(<A HREF="h_common_nextnew">NextNew</A>)
+key is pressed, and there
+are no more unseen messages in the current (incoming message or news)
+folder, Alpine will search the list of folders in the current collection for
+one containing New or Recent (new since the last time the folder was
+opened) messages.
+This behavior may be modified slightly with the
+<A HREF="h_config_tab_uses_unseen">&quot;<!--#echo var="FEAT_tab-uses-unseen-for-next-folder"-->&quot;</A>
+feature that causes Alpine to look for Unseen messages instead of Recent
+messages.
+Normally, when such a folder is found, Alpine will ask
+whether you wish to open the folder. If this feature is set, Alpine will
+automatically open the folder without prompting.
+<P>
+This feature also affects some other similar situations.
+If you have a
+<A HREF="h_config_thread_index_style"><!--#echo var="VAR_threading-index-style"--></A>
+that is equal to one of the &quot;separate-&quot; values, and you are
+viewing a thread; then when you type the NextNew command and are at the
+end of the current thread you will automatically go to the next thread
+if this feature is set.
+By default, you would be asked if you want to view the next thread.
+You will also be asked at times whether or not you want to view the next
+thread after you delete the last message in the thread.
+Setting this feature will also cause that question to be skipped.
+<P>
+
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_auto_include_reply =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_include-text-in-reply"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_include-text-in-reply"--></H1>
+
+This feature controls an aspect of Alpine's Reply command. Normally, Alpine
+will ask whether you wish to include the original message in your reply.
+If this feature is set and the feature
+<A HREF="h_config_prefix_editing">&quot;<!--#echo var="FEAT_enable-reply-indent-string-editing"-->&quot;</A>
+is <EM>not</EM> set, then the original message will be included in the reply
+automatically, without prompting.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_select_in_bold =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_show-selected-in-boldface"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_show-selected-in-boldface"--></H1>
+
+This feature controls an aspect of Alpine's
+<A HREF="h_config_enable_agg_ops">&quot;aggregate operation&quot;</A>
+commands; in
+particular, the Select and WhereIs commands. Select and WhereIs (with the
+^X subcommand) will search the current folder for messages meeting a
+specified criteria, and &quot;tag&quot; the resulting messages with an
+&quot;X&quot; in the
+first column of the applicable lines in the MESSAGE INDEX. If this feature
+is set, instead of using the &quot;X&quot; to denote a selected message,
+Alpine will
+attempt to display those index lines in boldface. Whether this is
+preferable to the &quot;X&quot; will depend on personal taste and the type of
+terminal being used.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_alt_auth =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_try-alternative-authentication-driver-first"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_try-alternative-authentication-driver-first"--></H1>
+
+This feature affects how Alpine connects to IMAP servers.
+It's utility has largely been overtaken by events,
+but it may still be useful in some circumstances.
+If you only connect to modern IMAP servers that support
+&quot;TLS&quot; you can ignore this feature.
+
+<P>
+Details:
+
+<P>
+By default, Alpine will attempt to connect to an IMAP server on the
+normal IMAP service port (143), and if the server offers &quot;Transport Layer
+Security&quot; (TLS) and Alpine has been compiled with encryption capability,
+then a secure (encrypted) session will be negotiated.
+
+<P>
+With this feature enabled, before connecting on the normal IMAP port, Alpine
+will first attempt to connect to an alternate IMAP service port (993) used
+specifically for encrypted IMAP sessions via the Secure Sockets Layer
+(SSL) method.
+If the SSL attempt fails, Alpine will then try the default
+behavior described in the previous paragraph.
+
+<P>
+TLS negotiation on the normal port is preferred, and supersedes the use of
+SSL on port 993, but older servers may not provide TLS support.
+This feature may be convenient when accessing IMAP servers that do not support
+TLS, but do support SSL connections on port 993.
+However, it is important to understand that with this feature enabled,
+Alpine will <EM>attempt</EM> to make a secure connection if that is possible,
+but it will proceed to make an insecure connection if that is the only
+option offered by the server, or if the Alpine in question has been built
+without encryption capability.
+
+<P>
+Note that this feature specifies a per-user (or system-wide) default
+behavior, but host/folder specification flags may be used to control the
+behavior of any specific connection.
+This feature interacts with some of
+the possible host/folder path specification flags as follows:
+
+<P>
+The <SAMP>/tls</SAMP> host flag, for example,
+
+<P>
+<CENTER><SAMP>{foo.example.com/tls}INBOX</SAMP></CENTER>
+<P>
+will over-ride this feature for the specified host by bypassing the
+SSL connection attempt.
+Moreover, with <SAMP>/tls</SAMP> specified,
+the connection attempt will fail if the
+service on port 143 does not offer TLS support.
+
+<P>
+The <SAMP>/ssl</SAMP> host flag, for example,
+
+<P>
+<CENTER><SAMP>{foo.example.com/ssl}INBOX</SAMP></CENTER>
+<P>
+will insist on an SSL connection for the specified host,
+and will fail if the SSL service on port 993 is not available.
+Alpine will not subsequently retry a connection
+on port 143 if <SAMP>/ssl</SAMP> is specified.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_file_dir ======
+<HTML>
+<HEAD>
+<TITLE>OPTION: File Directory</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: File Directory</H1>
+
+PC-Alpine only.
+<P>
+This value affects the Composer's &quot;^J&nbsp;Attach&quot; command,
+the Attachment Index Screen's &quot;S&nbsp;Save&quot; command, and the
+Message Index's &quot;E&nbsp;Export&quot; command.
+
+<P>
+Normally, when a filename is supplied that lacks a leading &quot;path&quot;
+component, Alpine assumes the file exists in the user's home directory.
+Under Windows operating systems, this definition isn't always clear. This
+feature allows you to explictly set where Alpine should look for files
+without a leading path.
+
+<P>
+NOTE: this feature's value is ignored if either
+<A HREF="h_config_use_current_dir"><!--#echo var="FEAT_use-current-dir"--></A> feature
+is set or the PINERC has a value for the &quot;<!--#echo var="VAR_operating-dir"-->&quot; variable.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_quote_all_froms =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_save-will-quote-leading-froms"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_save-will-quote-leading-froms"--></H1>
+
+This feature controls an aspect of the Save command (and also the way
+outgoing messages are saved to an FCC folder). If set, Alpine will add
+a leading &quot;>&quot; character in front of message lines beginning with &quot;From&quot;
+when they are saved to another folder, including lines syntactically
+distinguishable from the type of message separator line commonly used on
+Unix systems.
+
+<P>
+The default behavior is that a &quot;>&quot; will be prepended only to lines
+beginning with &quot;From &quot; that might otherwise be confused with a message
+separator line on Unix systems. If pine is the only mail program you use,
+this default is reasonable. If another program you use has trouble
+displaying a message with an unquoted &quot;From &quot; saved by Alpine, you should
+enable this feature. This feature only applies to the common Unix mailbox
+format that uses message separator lines beginning with &quot;From &quot;. If
+Alpine has been configured to use a different mailbox format (possibly
+incompatible with other mail programs), then this issue does not arise,
+and the feature is irrelevant.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_normal_color =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Normal Color</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Normal Color</H1>
+
+Sets the color Alpine normally uses.
+The foreground color is the color of the actual character and the
+background color is the color of the area behind the character.
+By default this color is black characters on a white background.
+<P>
+<A HREF="h_color_setup">Descriptions of the available commands</A>
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_reverse_color =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Reverse Color</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Reverse Color</H1>
+
+Sets the color Alpine uses for reverse video characters.
+The foreground color is the color of the actual character and the
+background color is the color of the area behind the character.
+<P>
+<A HREF="h_color_setup">Descriptions of the available commands</A>
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_title_color =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Title Color</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Title Color</H1>
+
+Sets the color Alpine uses for the titlebar (the top line on the screen).
+The foreground color is the color of the actual character and the
+background color is the color of the area behind the character.
+By default, the Title Color is black characters on a yellow background.
+<P>
+The actual titlebar color may be different from the Title Color if
+the option
+<A HREF="h_config_titlebar_color_style">&quot;<!--#echo var="VAR_titlebar-color-style"-->&quot;</A>
+is set to some value other than the default.
+It may also be different if the current folder is closed and the
+<A HREF="h_config_titleclosed_color">Title Closed Color</A>
+color is set to something different from the Title Color.
+<P>
+<A HREF="h_color_setup">Descriptions of the available commands</A>
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_titleclosed_color =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Title Closed Color</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Title Closed Color</H1>
+
+Sets the color Alpine uses for the titlebar (the top line on the screen)
+when the current folder is closed.
+The foreground color is the color of the actual character and the
+background color is the color of the area behind the character.
+By default, the Title Color Closed Color is white characters on a red background.
+<P>
+By setting this color to something noticeable you will be alerted to the
+fact that the current folder is closed, perhaps unexpectedly.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_status_color =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Status Color</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Status Color</H1>
+
+Sets the color Alpine uses for status messages written to the message
+line near the bottom of the screen.
+The foreground color is the color of the actual character and the
+background color is the color of the area behind the character.
+By default, the Status Color is the same as the Reverse Color.
+<P>
+<A HREF="h_color_setup">Descriptions of the available commands</A>
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_index_opening_color =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Index Opening Color</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Index Opening Color</H1>
+
+With some setups the text of the subject is followed
+by the opening text of the message if there is any room available in the index line.
+If you have configured your
+<A HREF="h_config_index_format"><!--#echo var="VAR_index-format"--></A> option
+to include one of the Subject tokens that causes this behavior
+(SUBJECTTEXT, SUBJKEYTEXT, or SUBJKEYINITTEXT), you may set the color of
+this opening text with this option.
+This coloring takes place for all but the current index line, and the Opening
+Color appears to be in front of any color from an Index Color Rule.
+<P>
+By default the Index Opening Color is gray characters on a white background.
+
+<P>
+<A HREF="h_color_setup">Descriptions of the available commands</A>
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_index_pri_color =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Index Priority Symbol Colors</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Index Priority Symbol Colors</H1>
+
+The X-Priority header is a non-standard header that is used in a
+somewhat standard way by many mail programs.
+Alpine expects the value of this header to be a digit with a value
+from 1 to 5, with 1 being the highest priority and 5 the lowest priority.
+Alpine can be made to display an indication of this priority in
+messages by use of one of the tokens
+(<A HREF="h_index_tokens">Tokens for Index and Replying</A>)
+PRIORITY, PRIORITYALPHA, or PRIORITY! in the
+<A HREF="h_config_index_format"><!--#echo var="VAR_index-format"--></A> option.
+
+<P>
+You may set the color used to draw these tokens by use of the colors
+Index High Priority Symbol Color and Index Low Priority Symbol Color.
+This coloring takes place for all but the current index line, and the Priority
+Color appears to be in front of any color from an Index Color Rule.
+If the priority has a value of 1 or 2 the High Priority color will be
+used,
+and if the value is 4 or 5 the Low Priority color will be used.
+<P>
+If you don't set these colors the index line will be colored in the same color as
+the bulk of the index line.
+
+<P>
+<A HREF="h_color_setup">Descriptions of the available commands</A>
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_index_subject_color =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Index Subject Color</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Index Subject Color</H1>
+
+You may set the color used to draw the Subject part of the index line.
+This coloring takes place for all but the current index line, and the Subject
+Color appears to be in front of any color from an Index Color Rule.
+<P>
+If you don't set this color it will be colored in the same color as
+the bulk of the index line.
+
+<P>
+<A HREF="h_color_setup">Descriptions of the available commands</A>
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_index_from_color =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Index From Color</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Index From Color</H1>
+
+You may set the color used to draw the From part of the index line.
+This coloring takes place for all but the current index line, and the From
+Color appears to be in front of any color from an Index Color Rule.
+<P>
+If you don't set this color it will be colored in the same color as
+the bulk of the index line.
+
+<P>
+<A HREF="h_color_setup">Descriptions of the available commands</A>
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_index_arrow_color =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Index Arrow Color</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Index Arrow Color</H1>
+
+If you have configured your
+<A HREF="h_config_index_format"><!--#echo var="VAR_index-format"--></A> option
+to include the &quot;ARROW&quot; token, you may set the color of
+the arrow displayed with this option.
+If you don't set the color it will be colored in the same color as
+the bulk of the index line.
+
+<P>
+<A HREF="h_color_setup">Descriptions of the available commands</A>
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_index_color =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Index Colors</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Index Colors</H1>
+
+You may add color to the single character symbols that give the status
+of each message in the MESSAGE INDEX.
+By default the characters &quot;+&quot;, &quot;*&quot;, &quot;D&quot;,
+&quot;A&quot;, and &quot;N&quot; show up near the left hand side of the
+screen depending on whether the message is addressed to you, and whether
+the message is marked Important, is Deleted, is Answered, or is New.
+The color for each of those characters may be specified by setting the
+&quot;Index-to-me&quot; Symbol Color,
+the &quot;Index-important&quot; Symbol Color,
+the &quot;Index-deleted&quot; Symbol Color,
+the &quot;Index-answered&quot; Symbol Color,
+and the &quot;Index-new&quot; Symbol Color.
+There are also two other symbol colors called &quot;Index-recent&quot;
+and &quot;Index-unseen&quot;.
+These two colors will only be used if you have configured your
+&quot;<A HREF="h_config_index_format"><!--#echo var="VAR_index-format"--></A>&quot; option
+to include the &quot;IMAPSTATUS&quot; or &quot;SHORTIMAPSTATUS&quot; token.
+<P>
+The default colors for these symbols are:
+<TABLE>
+<TR> <TD> &nbsp;&nbsp;Index-to-me&nbsp;&nbsp;&nbsp;&nbsp; </TD> <TD> black on cyan </TD> </TR>
+<TR> <TD> &nbsp;&nbsp;Index-important </TD> <TD> white on bright red </TD> </TR>
+<TR> <TD> &nbsp;&nbsp;Index-deleted&nbsp;&nbsp; </TD> <TD> same as Normal Color </TD> </TR>
+<TR> <TD> &nbsp;&nbsp;Index-answered&nbsp; </TD> <TD> bright red on yellow </TD> </TR>
+<TR> <TD> &nbsp;&nbsp;Index-new&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </TD> <TD> white on magenta </TD> </TR>
+<TR> <TD> &nbsp;&nbsp;Index-recent&nbsp;&nbsp;&nbsp; </TD> <TD> same as Normal Color </TD> </TR>
+<TR> <TD> &nbsp;&nbsp;Index-unseen&nbsp;&nbsp;&nbsp; </TD> <TD> same as Normal Color </TD> </TR>
+</TABLE>
+<P>
+Besides coloring the message status symbols, you may also color the
+entire index line.
+This is done by using the
+<A HREF="h_rules_incols">SETUP INDEX LINE COLORS</A> screen, which you
+may get to with the commands <EM>S</EM>etup/<EM>R</EM>ules/<EM>I</EM>ndexcolor.
+When the entire line is colored that color will be &quot;behind&quot; the
+status symbol colors talked about in the paragraph above.
+<P>
+You may also color
+<A HREF="h_config_keywords"><!--#echo var="VAR_keywords"--></A>
+in the index using the
+Setup/Kolor screen (<A HREF="h_config_kw_color">Keyword Colors</A>);
+the <A HREF="h_config_index_arrow_color">ARROW</A> cursor;
+the Subject using
+<A HREF="h_config_index_subject_color">Index Subject Color</A>;
+the From field using
+<A HREF="h_config_index_from_color">Index From Color</A>;
+and the
+<A HREF="h_config_index_opening_color">Index Opening</A> text.
+<P>
+<A HREF="h_color_setup">Descriptions of the available commands</A>
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_metamsg_color =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Meta-Message Color</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Meta-Message Color</H1>
+
+Sets the color Alpine uses in the MESSAGE TEXT screen for messages to you
+that aren't part of the message itself.
+For example, an attachment that isn't shown might produce a meta
+message something like:
+<P>
+<CENTER><SAMP> [ Part 2, &quot;comment&quot; Text/PLAIN (Name: &quot;file&quot;) ]</SAMP></CENTER>
+<P>
+If you set the
+<A HREF="h_config_quote_suppression"><!--#echo var="VAR_quote-suppression-threshold"--></A>
+option you might see
+<P>
+<CENTER><SAMP>[ 12 lines of quoted text hidden from view ]</SAMP></CENTER>
+<P>
+Warnings about suspicious looking URLs in HTML will also be colored
+with this color.
+<P>
+The foreground color is the color of the actual character and the
+background color is the color of the area behind the character.
+By default, the Meta-Message Color is black characters on a yellow background.
+<P>
+<A HREF="h_color_setup">Descriptions of the available commands</A>
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_keylabel_color =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: KeyLabel Color</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: KeyLabel Color</H1>
+
+Sets the color Alpine uses for the labels of the keys in the two-line
+menu at the bottom of the screen.
+For example, some of the screens have a &quot;P PrevMsg&quot; command.
+This option sets the color used when displaying &quot;PrevMsg&quot;.
+The foreground color is the color of the actual character and the
+background color is the color of the area behind the character.
+By default, the KeyLabel Color is the same as the Normal Color.
+<P>
+WARNING: Some terminal emulators have the property that the screen will scroll
+down one line whenever a character is written to the character cell in the
+lower right corner of the screen.
+Alpine can usually avoid writing a character in that corner of the screen.
+However, if you have defined a KeyLabel Color then Alpine does have to write
+a character in that cell in order to color the cell correctly.
+If you find that your display sometimes scrolls up a line this could be
+the problem.
+The most obvious symptom is probably that the titlebar at the top of the
+screen scrolls off the screen.
+Try setting KeyLabel Color to Default to see if that fixes the problem.
+<P>
+<A HREF="h_color_setup">Descriptions of the available commands</A>
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_keyname_color =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: KeyName Color</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: KeyName Color</H1>
+
+Sets the color Alpine uses for the names of the keys in the two-line
+menu at the bottom of the screen.
+For example, some of the screens have a &quot;P PrevMsg&quot; command.
+This option sets the color used when displaying the &quot;P&quot;.
+The foreground color is the color of the actual character and the
+background color is the color of the area behind the character.
+By default, the KeyName Color is the same as the Reverse Color.
+<P>
+<A HREF="h_color_setup">Descriptions of the available commands</A>
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_slctbl_color =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Selectable Item Color</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Selectable Item Color</H1>
+
+Sets the color Alpine uses for selectable items, such as URLs.
+The foreground color is the color of the actual character and the
+background color is the color of the area behind the character.
+By default, the Selectable Item Color is the same as the Normal Color,
+except that it is bold.
+<P>
+<A HREF="h_color_setup">Descriptions of the available commands</A>
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_quote_color =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Quote Colors</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Quote Colors</H1>
+
+Sets the colors Alpine uses for coloring quoted text in the MESSAGE TEXT
+screen.
+If a line begins with a &gt; character (or space followed by &gt;)
+it is considered a quote.
+That line will be given the Quote1 Color (first level quote).
+If there is a second level of quoting then the Quote2 Color will be used.
+Alpine considers there to be a second level of quoting if that first &gt; is
+followed by another &gt; (or space followed by &gt;).
+If there are characters other than whitespace and &gt; signs, then it isn't
+considered another level of quoting.
+Similarly, if there is a third level of quoting the Quote3 Color will be
+used.
+If there are more levels after that the Quote Colors are re-used.
+If you define all three colors then it would repeat like Color1, Color2, Color3,
+Color1, Color2, Color3, ...
+If you only define the first two it would be
+Color1, Color2, Color1, Color2, ...
+If you define only the Quote1 Color, then the entire quote would be that
+color regardless of the quoting levels.
+By default, the Quote1 Color is black characters on a greenish-blue background;
+the Quote2 Color is black characters on a dull yellow background; and
+the Quote3 Color is black characters on a green background.
+<P>
+<A HREF="h_color_setup">Descriptions of the available commands</A>
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_incunseen_color =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Incoming Unseen Color</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Incoming Unseen Color</H1>
+
+If the option
+<A HREF="h_config_enable_incoming_checking"><!--#echo var="FEAT_enable-incoming-folders-checking"--></A>
+is turned on it is possible to highlight the folders that contain
+unseen messages by coloring them with this color.
+By default, this is the same as the Normal Color and no highlighting is done.
+<P>
+Usually the &quot;current&quot; folder (the folder the cursor is on)
+is highlighted using reverse video.
+If the current folder is colored because it contains unseen messages then
+the color used to show that it is also the current folder is controlled
+by the
+<A HREF="h_config_index_color_style"><!--#echo var="VAR_current-indexline-style"--></A>
+feature at the top of the SETUP COLOR screen.
+<P>
+<A HREF="h_color_setup">Descriptions of the available commands</A>
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_signature_color =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Signature Color</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Signature Color</H1>
+
+Sets the color Alpine uses for coloring the signature in the MESSAGE TEXT
+screen. According to USENET conventions, the signature is defined as the
+paragraph following the &quot;sigdashes&quot;, that is, the special line
+consisting of the three characters
+&quot;--&nbsp;&quot; (i.e., dash, dash, and space). Alpine allows for one
+empty line right after the sigdashes to be considered as part of the
+signature.
+By default, the Signature Color is blue characters on a white background.
+<P>
+<A HREF="h_color_setup">Descriptions of the available commands</A>
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_prompt_color =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Prompt Color</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Prompt Color</H1>
+
+Sets the color Alpine uses for confirmation prompts and questions that
+appear in the status line near the bottom of the screen.
+The foreground color is the color of the actual character and the
+background color is the color of the area behind the character.
+By default, the Prompt Color is the same as the Reverse Color.
+<P>
+<A HREF="h_color_setup">Descriptions of the available commands</A>
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_header_general_color =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Header General Color</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Header General Color</H1>
+
+Sets the color Alpine uses for the headers of a message in the MESSAGE TEXT
+screen.
+The foreground color is the color of the actual character and the
+background color is the color of the area behind the character.
+By default, this is the same as the Normal Color.
+<P>
+It is also possible to set the colors for specific header fields, for
+example the Subject, using
+<A HREF="h_config_customhdr_color"><!--#echo var="VAR_viewer-hdr-colors"--></A>.
+If both a Header General Color and a specific Viewer Header Color are set
+the specific color will override the general color, as you would expect.
+<P>
+<A HREF="h_color_setup">Descriptions of the available commands</A>
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_incol =====
+<HTML>
+<HEAD>
+<TITLE>Index Line Color</TITLE>
+</HEAD>
+<BODY>
+<H1>Index Line Color</H1>
+
+This option is used to set the color of a line in the index when the
+message for that line matches the Pattern.
+This colors the whole index line, except possibly the status letters,
+which may be colored separately using the
+<A HREF="h_color_setup">Setup Kolor</A> screen.
+The foreground color is the color of the actual characters and the
+background color is the color of the area behind the characters.
+<P>
+<A HREF="h_color_setup">Descriptions of the available commands</A>
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_usetransparent_color =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Use Transparent Color</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Use Transparent Color</H1>
+
+This is a special color supported by some terminal emulators.
+It is intended to result in the default foreground or background color
+from the terminal emulator.
+This is the color the terminal was displaying characters in before you started Alpine.
+The reason it is called Transparent is because you could set the foreground color
+to some specific color, like Red, and then set the background color to the
+Transparent Color. If it works as expected, the background color from the terminal
+window in which Alpine is running will show through but with the Red characters
+in the foreground.
+<P>
+<A HREF="h_color_setup">Descriptions of the available commands</A>
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_usenormal_color =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Use Normal Color</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Use Normal Color</H1>
+
+When you use this color value, the actual color used will be the same
+as the corresponding Normal Color.
+For example if your Normal Color is black on white and you set both
+the foreground and background colors here to use the Normal Color, you'll
+get black on white. If you later change the Normal Color to red on blue
+this color will also change to red on blue.
+<P>
+<A HREF="h_color_setup">Descriptions of the available commands</A>
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_usenone_color =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Use None Color</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Use None Color</H1>
+
+This is a special color that simply means to leave the color alone.
+It is useful for Index symbols and for Keyword Colors used in the Subject
+field of an index line.
+The most likely use is to set an explicit foreground color and then set
+the background color to the None Color.
+That will cause the symbol or keyword to be drawn in the foreground color
+with a background equal to whatever color the rest of the index line is already
+drawn in.
+You will see no visible effect unless you have assigned Indexcolor Rules to
+color index lines or you have set an actual color for the Reverse Color.
+<P>
+<A HREF="h_color_setup">Descriptions of the available commands</A>
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_dflt_color =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Default Color</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Default Color</H1>
+
+Setting default will cause the color to be the default color.
+Unsetting default is normally done by choosing a color, but in some cases
+you may want to declare the current default color to be your non-default
+choice.
+For example, the default Keyname Color is the same as the Reverse Color.
+Whenever the Reverse Color changes the Keyname Color will also change, unless
+you've changed it or unset the default box.
+<P>
+<A HREF="h_color_setup">Descriptions of the available commands</A>
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_bold_slctbl =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Bold</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Bold</H1>
+
+The color for this particular section may have the Bold attribute turned
+on or off.
+Setting bold will cause the characters to be bold.
+<P>
+<A HREF="h_color_setup">Descriptions of the available commands</A>
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_kw_color =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Keyword Colors</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Keyword Colors</H1>
+
+Sets the colors Alpine uses for Keyword fields in the MESSAGE INDEX screen.
+Keywords are displayed as part of the Subject by default.
+They are also displayed as part of the Subject if the tokens
+&quot;SUBJKEY&quot;, &quot;SUBJKEYTEXT&quot;, &quot;SUBJKEYINIT&quot;, or &quot;SUBJKEYINITTEXT&quot; are used in the
+<A HREF="h_config_index_format"><!--#echo var="VAR_index-format"--></A> option.
+Keywords may also be displayed in a column of their own in the MESSAGE INDEX
+screen by using the &quot;KEY&quot; or &quot;KEYINIT&quot; tokens.
+<P>
+For example, you might have set up a Keyword
+&quot;Work&quot; using the
+<A HREF="h_config_keywords"><!--#echo var="VAR_keywords"--></A> option in the Setup/Config screen.
+You could cause that Keyword to show up as a special color
+by setting up the Keyword Color using this option, and then including it
+in the MESSAGE INDEX screen using one of the tokens listed above in the
+<!--#echo var="VAR_index-format"-->.
+<P>
+<A HREF="h_color_setup">Descriptions of the available commands</A>
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_customhdr_color =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_viewer-hdr-colors"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_viewer-hdr-colors"--></H1>
+
+Sets the colors Alpine uses for specific header fields in the MESSAGE TEXT screen.
+For example, you may set the color of the Subject header or the From header.
+The foreground color is the color of the actual character and the
+background color is the color of the area behind the character.
+<P>
+In addition to setting the colors for particular headers (like the Subject)
+you may also set a color to be used for all headers unless overridden by a
+more specific Viewer Header Color.
+To do this use the
+<A HREF="h_config_header_general_color">Header General Color</A>.
+<P>
+For Header Colors,
+there is an additional line on the
+screen labeled &quot;Pattern to match&quot;.
+If you leave that blank, then the whole field for that header will
+be colored.
+However, if you give a pattern to match, the coloring will only take place
+if there is a match for that pattern in the value of the field.
+For example, if you are working on a color for the Subject header and
+you fill in a pattern of &quot;important&quot;, then only Subjects that
+contain the word &quot;important&quot; will be colored.
+<P>
+If the pattern you enter is a comma-separated list of patterns, then coloring
+happens if any of those patterns matches.
+<P>
+<A HREF="h_color_setup">Descriptions of the available commands</A>
+<P>
+Look <A HREF="h_edit_nav_cmds">here</A>
+to see the available Editing and Navigation commands.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_customhdr_pattern =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: Viewer Header Color Pattern</TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: Viewer Header Color Pattern</H1>
+
+If you leave this blank, then the whole field for the header will
+be colored.
+If you give a pattern to match, the coloring will only take place
+if there is a match for that pattern in the value of the field.
+For example, if you are working on a color for the Subject header and
+you fill in a pattern of &quot;important&quot;, then only Subjects that
+contain the word &quot;important&quot; will be colored.
+<P>
+For address headers (like From and To) and for the Newsgroups header,
+a pattern match will cause only the matched addresses or newsgroups to be
+colored.
+If there is no pattern to match, then all of the addresses or newsgroups
+in the relevant header will be colored.
+<P>
+The matching pattern may be a comma-separated list of patterns to match
+instead of a single pattern.
+For example, you could use the pattern &quot;important,urgent&quot; which would
+cause a match if either the word &quot;important&quot; or the word
+&quot;urgent&quot; appeared in the value of the header.
+You could list several comma-separated email addresses in the Header
+From Color pattern so that those addresses will be colored when any of
+them appear in the From header.
+<P>
+To add a new matching pattern or change the existing pattern use the
+<!--chtml if pinemode="function_key"-->
+&quot;F4&quot;
+<!--chtml else-->
+&quot;C&quot;
+<!--chtml endif-->
+&quot;Change&quot; command that is available when the &quot;Pattern to
+match&quot; line is highlighted.
+The
+<!--chtml if pinemode="function_key"-->
+&quot;F10&quot;
+<!--chtml else-->
+&quot;D&quot;
+<!--chtml endif-->
+&quot;Delete&quot; command may be used to quickly remove all patterns
+for a particular header.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_color_setup =====
+<HTML>
+<HEAD>
+<TITLE>SETUP COLOR COMMANDS</TITLE>
+</HEAD>
+<BODY>
+<H1>SETUP COLOR COMMANDS</H1>
+<!--chtml if pinemode="function_key"-->
+<PRE>
+Available Commands -- Group 1
+-------------------------------
+F1 Display this help text
+F2 Show other available commands
+F3 Exit to MAIN MENU
+F4 Select the highlighted foreground or background color
+F5 Move to previous line
+F6 Move to next line
+F7 Previous page
+F8 Next page
+F9 Add a config section for a header field
+F10 Restore all default colors (for all sections)
+F11 Print color configuration screen
+F12 Whereis (search for word)
+
+Available Commands -- Group 2
+-------------------------------
+F1 Display this help text
+F2 Show other available commands
+F5 Delete config section for highlighted header field
+F6 Shuffle the order of Header Color sections
+</PRE>
+<!--chtml else-->
+<PRE>
+General commands
+-------------------------------------------------
+ ? Display this help text E Exit back to MAIN MENU
+ P Previous Line N Next Line
+ - Previous page Spc (space bar) Next page
+ W WhereIs (search for word) % Print color configuration screen
+
+Color Setup Commands
+------------------------------------------------
+ * Select the highlighted foreground or background color
+ A Add a config section for a header field
+ D Delete config section for highlighted header field
+ R Restore all default colors (for all sections)
+ $ Shuffle the order of Header Color sections
+</PRE>
+<!--chtml endif-->
+
+<H2>Description of the Setup Color Screen</H2>
+
+From this screen you may turn on color and set the colors of
+various parts of the Alpine display.
+For help on turning on color move your cursor into the Color Style section
+at the top of the Setup Color screen and ask for help.
+
+<P>
+There are several sections in the Setup Color Screen.
+At the top are some settings that handle the style of color used
+with your terminal emulator (UNIX only), and some settings that
+control how the current indexline and the titlebar are colored.
+After that comes a long section called GENERAL COLORS that allows
+you to set the color of many elements in the Alpine screens.
+For example, the color of the titlebar, status messages,
+selectable links, quotes and signatures in messages, and so on.
+After that is a section called INDEX COLORS that allows you to
+set the colors of various pieces of the displayed index lines in
+the MESSAGE INDEX screen.
+The next section is HEADER COLORS. This is for coloring headers of
+messages in the MESSAGE TEXT screen in just about any way you would like.
+Finally, the KEYWORD COLORS section allows you to highlight
+<A HREF="h_config_keywords"><!--#echo var="VAR_keywords"--></A>
+in the MESSAGE INDEX screen.
+
+<P>
+To change a color, highlight the color you want to change (for example,
+the Status Color) by moving
+the cursor into it.
+You may want to read the help text for the color to see a brief desription
+of what you are coloring.
+Then press &quot;C&quot; for Change to set the color to something new.
+That will put you into a screen with two columns of colors, one for
+the foreground color and one for the background color.
+The foreground color is just the color you want the actual characters
+to be and the background color is the color of the rest of the rectangle
+behind the characters.
+Select the foreground and background colors desired by using the Next and
+Prev keys to highlight the color, and the * command to select it.
+<P>
+To set a color to its default value, set the X in the Default line at
+the bottom of the list of colors.
+<P>
+
+The HEADER COLORS section is a little bit different from the others.
+Besides coloring the specific fields that Alpine knows about, you may also
+color specific header fields when viewing a message in the MESSAGE TEXT
+screen.
+For example, you may color the Subject header a particular color.
+There are a few commands for use with headers.
+The &quot;AddHeader&quot; command adds a section to the color
+configuration screen that allows you to set the color for that header.
+You'll be asked for the name of the header field you want to color.
+If you wanted to color the Subject, you would answer
+with the word &quot;subject&quot;.
+Once you've added a header field, the color setting works just like the
+other color fields, except that there is an additional line on the
+configuration screen labeled &quot;Pattern to match&quot;.
+If you leave that blank, then the whole field for that header will always
+be colored.
+However, if you give a pattern to match, the coloring will only take place
+if there is a match for that pattern in the value of the field.
+For example, if you are working on a color for the Subject header and
+you fill in a pattern of &quot;important&quot;, then only Subjects that
+contain the word &quot;important&quot; will be colored.
+<P>
+The &quot;DeleteHdr&quot; command removes a header section from the
+configuration altogether.
+The &quot;Shuffle&quot; command changes the order of header sections.
+This is only necessary if you use header sections with pattern fields.
+For example, if you have two Subject sections, one with one pattern and
+another with another pattern, and the subject for a particular message
+happens to match both, then the color from the first match is used.
+<P>
+
+The command &quot;RestoreDefs&quot; will restore all of the default colors.
+Each section will change to the default value used for that section when
+color is first enabled.
+When you restore all default colors the color settings for the Header Colors
+will be unset (since that's the default), but the header fields you've
+added will remain so that you may easily reset them.
+In order to get rid of them completely you'd have to use
+the &quot;DeleteHdr&quot; command.
+
+<P>
+Remember that <A HREF="h_rules_incols">Index Line Colors</A>
+may be set with matching rules and that is configured separately from
+the rest of the color settings described here.
+It is configured in the Setup/Rules/Indexcolors section of the configuration screen
+instead of in the Setup/Kolor section.
+
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_news_uses_recent ======
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_news-approximates-new-status"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_news-approximates-new-status"--></H1>
+
+This feature causes certain messages to be marked as "New" in the
+MESSAGE INDEX of newsgroups.
+This feature is set by default.
+
+<P>
+
+When opening a newsgroup, Alpine will consult your "newsrc" file and
+determine the last message you have previously disposed of via the "D"
+key. If this feature is set, any subsequent messages will be shown in the
+Index with an "N", and the first of these messages will be highlighted.
+Although this is only an approximation of true "New" or "Unseen"
+status, it provides a useful cue to distinguish more-or-less recent
+messages from those you have seen previously, but are not yet ready to
+mark deleted.
+
+<P>
+
+Background: your "newsrc" file (used to store message status information
+for newsgroups) is only capable of storing a single flag, and Alpine uses
+this to record whether or not you are "done with" a message, as
+indicated by marking the message as "Deleted". Unfortunately, this
+means that Alpine has no way to record exactly which messages you have
+previously seen, so it normally does not show the "N" status flag for
+any messages in a newsgroup. This feature enables a starting
+*approximation* of seen/unseen status that may be useful.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_expose_hidden_config =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_expose-hidden-config"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_expose-hidden-config"--></H1>
+
+If set, this causes configuration options and features that are normally
+hidden from view to be editable in the Setup/Config screen.
+
+<P>
+The purpose of this feature is to allow you to change configuration
+features and variables that are normally hidden.
+This is particularly useful if you are using a remote configuration,
+where it is difficult to edit the contents manually, but it may also be used
+on a local pinerc configuration file.
+<P>
+If set, several configuration variables and features that are normally
+hidden from view will show up in the Setup/Configuration screen.
+They will be at the bottom of the configuration screen.
+You can find them by searching for the words &quot;hidden configuration&quot;.
+<P>
+
+Note that this is an advanced feature that should be used with care.
+The reason that this part of the configuration is normally hidden is because
+there is a significant potential for causing problems if you change these
+variables.
+If something breaks after a change try changing it back to see if that is
+what is causing the problem.
+There are also some variables that are normally hidden because they are
+manipulated through Alpine in other ways.
+For example, colors are normally set using the Setup/Kolors screen and
+the &quot;<!--#echo var="VAR_address-book"-->&quot; variable is normally set using
+the Setup/AddressBooks screen, so there is little reason to edit these directly.
+The &quot;<!--#echo var="VAR_incoming-folders"-->&quot; variable is normally changed by using
+the Add, Delete, and Rename commands in the FOLDER LIST screen,
+and the &quot;<!--#echo var="VAR_last-time-prune-questioned"-->&quot; variable is normally used
+internally by Alpine and not set directly by the user.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_disable_signature_edit =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_disable-signature-edit-cmd"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_disable-signature-edit-cmd"--></H1>
+
+If set, this disables the editing of signature files from within
+the Setup/Config screen.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_disable_roles_templateedit =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_disable-roles-template-edit"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_disable-roles-template-edit"--></H1>
+
+If set, this disables the editing of template files within the
+Role setup screen.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_disable_roles_sigedit =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_disable-roles-sig-edit"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_disable-roles-sig-edit"--></H1>
+
+If set, this disables the editing of signature files within the
+Role setup screen.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_disable_roles_setup =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_disable-roles-setup-cmd"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_disable-roles-setup-cmd"--></H1>
+
+If set, this disables the Setup/Rules/Roles command.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_disable_pipes_in_templates =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_disable-pipes-in-templates"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_disable-pipes-in-templates"--></H1>
+
+By default, if a template file name is followed by a vertical bar (|) then
+that causes the file to be executed to produce the text for the template.
+If this feature is set, then this is not allowed.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_disable_pipes_in_sigs =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_disable-pipes-in-sigs"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_disable-pipes-in-sigs"--></H1>
+
+By default, if a signature file name is followed by a vertical bar (|) then
+that causes the file to be executed to produce the text for the signature.
+If this feature is set, then this is not allowed.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_disable_password_cmd =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_disable-password-cmd"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_disable-password-cmd"--></H1>
+
+If set, then the Setup/Newpassword command is disabled.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_disable_password_caching =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_disable-password-caching"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_disable-password-caching"--></H1>
+
+Normally, loginname/password combinations are cached in Alpine so that
+you do not have to enter the same password more than once in a session.
+A disadvantage to this approach is that the password must be stored in
+the memory image of the running Alpine in order that it can be re-used.
+In the event that Alpine crashes and produces a core dump, and that core
+dump is readable by others, the loginname and password could be read
+from the core dump.
+<P>
+If this feature is set, then the passwords will not be cached and you
+will have to retype the password whenever Alpine needs it.
+Even with this feature set there is still some chance that the core
+file will contain a password, so care should be taken to make the
+core files unreadable.
+<P>
+NOTE: If PASSFILE caching is enabled, this does not disable it.
+That is a separate and independent feature.
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_disable_kb_lock =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_disable-keyboard-lock-cmd"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_disable-keyboard-lock-cmd"--></H1>
+
+If set, then the Keyboard Lock command is removed from the MAIN MENU.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_disable_config_cmd =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_disable-config-cmd"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_disable-config-cmd"--></H1>
+
+If set, then the Setup/Config screen is disabled.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_allow_chg_from =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_allow-changing-from"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_allow-changing-from"--></H1>
+
+This feature affects Alpine's handling of the &quot;From:&quot; header field
+in the "<A HREF="h_config_custom_hdrs"><!--#echo var="VAR_customized-hdrs"--></A>" configuration
+option.
+<P>
+If this feature is set then the From line can be changed just like
+all the other header fields that can be changed.
+This feature defaults to <EM>ON</EM>.
+<P>
+Even with this feature turned ON (the default) you will not be able
+to change the From header unless you add it to your list of
+<A HREF="h_config_custom_hdrs"><!--#echo var="VAR_customized-hdrs"--></A>.
+You may also want to change the
+<A HREF="h_config_comp_hdrs"><!--#echo var="VAR_default-composer-hdrs"--></A>
+if you want the From header to always show up in the composer without
+having to type the Rich Headers command first.
+<P>
+Note that many sites restrict the use of this feature in order to
+reduce the chance of falsified addresses and misdirected mail.
+If you want to change the value of what gets included in the From header
+in messages you send
+look <A HREF="h_config_change_your_from">here</A> for a description.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_disable_collate =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_disable-setlocale-collate"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_disable-setlocale-collate"--></H1>
+
+This is a hard to understand feature that should only be used in rare cases.
+Normally, the C function call
+<P>
+<CENTER><SAMP>setlocale(LC_COLLATE, &quot;&quot;)</SAMP></CENTER>
+<P>
+is used by Alpine.
+If you want to try turning it off,
+setting this feature will turn it off.
+This part of the locale has to do with the sort order
+of characters in your locale.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_quell_attach_extra_prompt =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_quell-attachment-extra-prompt"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_quell-attachment-extra-prompt"--></H1>
+
+By default, when you attempt to view an attachment externally
+from the &quot;Attachment View&quot; screen, you are asked if you
+really want to view the selected attachment.
+
+<P>
+If this feature is set, you will <B>not</B> be prompted to confirm
+your selection. Prior to Alpine and to Pine 4.50, the default behavior was to not
+prompt. This feature was added for those wanting to preserve that
+behavior (along with
+<A HREF="h_config_quell_attach_ext_warn"><!--#echo var="FEAT_quell-attachment-extension-warn"--></A>).
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_quell_attach_ext_warn =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_quell-attachment-extension-warn"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_quell-attachment-extension-warn"--></H1>
+
+<P>
+This feature suppresses the extra warning you can get when trying
+to view an attachment for which there is no mime-type match. Turning
+on this feature will just run the program according to extension
+instead of first warning the user that it will run according to the
+file's extension.
+<P>
+This feature can be used along side
+<A HREF="h_config_quell_attach_extra_prompt"><!--#echo var="FEAT_quell-attachment-extra-prompt"--></A>
+to preserve the behavior exhibited in Pine versions prior to Pine 4.50.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_mailcap_params =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-mailcap-param-substitution"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-mailcap-param-substitution"--></H1>
+
+If set, this will allow mailcap named parameter substitution to occur
+in mailcap entries.
+By default, this is turned off to prevent security problems that may occur
+with some incorrect mailcap configurations.
+For more information, see RFC1524 and look for "named parameters" in the
+text of the RFC.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_disable_shared =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_disable-shared-namespaces"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_disable-shared-namespaces"--></H1>
+
+If this feature is set, the automatic search for namespaces &quot;ftp&quot;,
+&quot;imapshared&quot;, and &quot;imappublic&quot; by the underlying library
+will be disabled.
+The reason this feature exists is because there are some implementations
+of system password lookup routines that are very slow when presented with
+a long loginname that does not exist.
+This feature could be set to prevent the delay at startup time when the
+names above are searched for in the password file.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_hide_nntp_path =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_hide-nntp-path"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_hide-nntp-path"--></H1>
+
+Normally the Path header that Alpine generates when posting to a newsgroup
+contains the name of the computer from which the message is being sent and
+the user name.
+Some believe that this information is used by spammers.
+If this feature is set, that information will be replaced with the text
+<P>
+<CENTER><SAMP>not-for-mail</SAMP></CENTER>
+<P>
+instead.
+<P>
+It should be noted that many servers being connected to will still reveal
+the information that this feature attempts to protect.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_no_bezerk_zone =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_quell-berkeley-format-timezone"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_quell-berkeley-format-timezone"--></H1>
+
+POSIX mandates a timezone in UNIX mailbox format folder delimiters
+(the line that begins with From <SPACE>).
+Some versions of Berkeley mail have trouble with this, and don't recognize
+the line as a message delimiter.
+If this feature is set, the timezone will be left off the delimiter line.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_quell_domain_warn =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_quell-maildomain-warning"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_quell-maildomain-warning"--></H1>
+
+When your configuration is set up so that your domain name contains no dots,
+it is usually a configuration error.
+By default, Alpine will warn you about this when you start it up.
+You will see a warning message that looks like
+
+<P>
+<CENTER><SAMP>Incomplete maildomain &quot;&lt;domain&gt;&quot;.</SAMP></CENTER>
+
+<P>
+If this feature is set, the warning is turned off.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_quell_imap_env =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_quell-imap-envelope-update"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_quell-imap-envelope-update"--></H1>
+
+In the MESSAGE INDEX screen, if the open folder is being accessed
+using IMAP, Alpine normally tries to paint the index lines on the screen
+as soon as the information arrives from the IMAP server.
+This means that the index information makes it onto the screen more quickly
+than it otherwise would.
+This sometimes results in behavior that bothers some users.
+For example, when paging to a new page of the index, it may be possible for
+the lines to be painted on the screen in a random order, rather than from
+top to bottom.
+<P>
+
+Setting this feature causes Alpine to wait for all of the information
+to be gathered before it paints the index screen.
+Once it collects all of the information, the screen will be painted quickly
+from top to bottom.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_quell_news_env =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_quell-news-envelope-update"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_quell-news-envelope-update"--></H1>
+
+In the MESSAGE INDEX screen, if the open folder is being accessed
+using NNTP (News), Alpine normally tries to paint the index lines on the screen
+as soon as the information arrives from the NNTP server.
+This means that the index information makes it onto the screen more quickly
+than it otherwise would.
+This sometimes results in behavior that bothers some users.
+For example, when paging to a new page of the index, it may be possible for
+the lines to be painted on the screen in a random order, rather than from
+top to bottom.
+<P>
+
+Setting this feature causes Alpine to wait for all of the information
+to be gathered before it paints the index screen.
+Once it collects all of the information, the screen will be painted quickly
+from top to bottom.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_quell_content_id =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_quell-content-id"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_quell-content-id"--></H1>
+
+This feature changes the behavior of Alpine when sending messages.
+It is intended to work around a bug in Microsoft's Outlook XP mail user
+agent.
+As of this writing, Microsoft has acknowledged the bug but
+has not added it to the Knowledge Base.
+We have been told that there will be a post-SP1 hotfix for Outlook XP.
+This particular bug has bug fix number OfficeQFE:4781.
+The nature of the bug is that messages with attachments that
+contain a Content-ID header (which standard Alpine attachments do)
+do not show the attachment indicator (a paperclip) when viewed with
+Outlook XP.
+So the user has no indication that the message contains an attachment.
+
+<P>
+If this feature is set then Alpine will remove most Content-ID headers
+before sending a message.
+If an attachment is of type MESSAGE, then the existing Content-ID headers
+inside the message will be left intact.
+This would only happen with Alpine if a message was forwarded as an attachment
+or if a message with a message attached was forwarded.
+Similarly if an attachment of type MULTIPART/ALTERNATIVE is forwarded,
+the Content-ID headers of the alternative parts will not be removed.
+
+<P>
+Because the Content-ID header is a standard part of MIME it is possible
+that setting this feature will break something.
+For example, if an attachment has a Content-ID header that is necessary
+for the correct functioning of that attachment, it is possible that Alpine
+may remove that header when the attachment is forwarded.
+However, it seems fairly safe at this time.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_winpos_in_config =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_store-window-position-in-config"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_store-window-position-in-config"--></H1>
+
+PC-Alpine only.
+<P>
+
+Normally, PC-Alpine will store its window size and position in the
+Windows Registry.
+This is convenient if you want to use the same remote
+configuration from more than one PC.
+If you use multiple configuration files to start PC-Alpine, you may want
+to store the window size and position in the configuration file instead
+of in the Registry.
+Setting this feature causes the value to be stored in
+<A HREF="h_config_window_position">Window-Position</A>.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_quell_ssl_largeblocks =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_quell-ssl-largeblocks"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_quell-ssl-largeblocks"--></H1>
+
+PC-Alpine only.
+<P>
+This feature changes the behavior of fetching messages
+and attachments so that the message data is fetched in chunks no larger
+than 12K bytes.
+This works around a bug in Microsoft's SSL/TLS support.
+Some versions of Microsoft SSL are not able to read full-sized (16K)
+SSL/TLS packets.
+Some servers will send such packets and this will
+cause PC-Alpine to crash with the error
+
+<P>
+<CENTER><SAMP>incomplete SecBuffer exceeds maximum buffer size</SAMP></CENTER>
+
+<P>
+Microsoft is aware of the problem and has developed a hotfix for it, it is
+discussed in article 300562 in the Microsoft Knowledge Base.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_quell_partial =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_quell-partial-fetching"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_quell-partial-fetching"--></H1>
+
+Partial fetching is a feature of the IMAP protocol.
+By default, Alpine
+will use partial fetching when copying the contents of a message or attachment
+from the IMAP server to Alpine.
+This means that the fetch will be done in many
+small chunks instead of one big chunk. The main benefit of this approach is
+that the fetch becomes interruptible. That is, the user can type <EM>^C</EM>
+to stop the fetch early. In some cases partial fetching may cause a performance
+problem so that the fetching of data takes significantly longer when partial
+fetching is used. Turning on this feature will turn off partial fetching.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_quell_personal_name_prompt =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_quell-personal-name-prompt"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_quell-personal-name-prompt"--></H1>
+
+PC-Alpine only. This feature quells the prompting for a
+<A HREF="h_config_pers_name">personal name</A>. This
+prompt normally happens before composing a message, and only happens when
+there is no personal name already set.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_quell_user_id_prompt =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_quell-user-id-prompt"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_quell-user-id-prompt"--></H1>
+
+PC-Alpine only. This feature quells the prompting for a
+<A HREF="h_config_user_id"><!--#echo var="VAR_user-id"--></A>
+if the information can be obtained from the login name used
+to open the INBOX. Normally, this prompt happens before composing
+a message, and only happens when there is no user-id already set
+in the configuration.
+<P>
+With this feature set, composing a message is only possible after
+establishing a connection to the INBOX.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_save_aggregates =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_save-aggregates-copy-sequence"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_save-aggregates-copy-sequence"--></H1>
+
+This feature will optimize an aggregate copy operation, if
+possible, by issuing a single IMAP <EM>COPY</EM> command with a
+list of the messages to be copied.
+This feature is set by default.
+This may reduce network traffic and elapsed time for the Save.
+<EM>However, many IMAP servers (including the UW IMAP server) do
+not preserve the order of messages when this optimization is applied.</EM>
+If this feature is not set,
+Alpine will copy each message individually and the order of the messages
+will be preserved.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_use_system_translation =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: Use System Translation</TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: Use System Translation</H1>
+
+UNIX Alpine only.
+<P>
+Alpine normally uses its own internal software to convert between the multi-byte
+representation of characters and the Unicode representation of those
+same characters.
+It converts from the multi-byte characters your keyboard produces to Unicode,
+and from Unicode to the multi-byte characters your display expects.
+Alpine also uses its own internal software to decide how much space on
+the screen a particular Unicode character will occupy.
+
+<P>
+Setting this feature tells Alpine to use the system-supplied routines to
+perform these tasks instead.
+In particular there are three tasks and three system routines that will
+be used for these tasks.
+
+<P>
+To convert from multi-byte to Unicode the routine
+
+<P>
+<CENTER><SAMP>mbstowcs</SAMP></CENTER>
+<P>
+
+is used.
+To convert from Unicode to multi-byte the routine
+
+<P>
+<CENTER><SAMP>wcrtomb</SAMP></CENTER>
+<P>
+
+is used.
+And to find the screen width a particular Unicode character will
+occupy the routine used is
+
+<P>
+<CENTER><SAMP>wcwidth</SAMP></CENTER>
+<P>
+
+This feature has been only lightly tested.
+The internal routines should normally be used unless you run into
+a problem that you think may be solved by using the system routines.
+Note that your environment needs to be set up for these
+routines to work correctly.
+In particular, the LANG or LC_CTYPE variable in your environment will
+need to be set.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_suspend_spawns =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_use-subshell-for-suspend"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_use-subshell-for-suspend"--></H1>
+
+This feature affects Alpine's behavior when process suspension is enabled
+and then activated via the Ctrl-Z key. Alpine suspension allows one to
+temporarily interact with the operating system command &quot;shell&quot;
+without
+quitting Alpine, and then subsequently resume the still-active Alpine session.
+<P>
+
+When the <A HREF="h_config_can_suspend">&quot;<!--#echo var="FEAT_enable-suspend"-->&quot;</A> feature
+is set and subsequently the Ctrl-Z key
+is pressed, Alpine will normally suspend itself and return temporary control
+to Alpine's parent shell process. However, if this feature is set, Alpine
+will instead create an inferior subshell process. This is useful when the
+parent process is not intended to be used interactively. Examples include
+invoking Alpine via the -e argument of the Unix &quot;xterm&quot; program,
+or via a menu system.<P>
+
+Note that one typically resumes a suspended Alpine by entering the Unix
+&quot;fg&quot; command, but if this feature is set, it will be necessary to
+enter the &quot;exit&quot; command instead.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_8bit_smtp =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-8bit-esmtp-negotiation"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-8bit-esmtp-negotiation"--></H1>
+
+This feature affects Alpine's behavior when sending mail.
+By default, this feature is set.
+Internet standards
+require that all electronic mail messages traversing the global Internet
+consist of 7bit ASCII characters unless a pair of cooperating mail
+transfer agents explicitly agree to allow 8bit messages. In general,
+then, exchanging messages in non-ASCII characters requires MIME encoding.
+<P>
+However, there are now Internet standards that allow for unencoded 8bit
+exchange of messages between cooperating systems. When this feature is set
+Alpine will try to negotiate unencoded 8bit transmission during the
+sending process. Should the negotiation fail, Alpine will fall back to its
+ordinary encoding rules.
+<P>
+Note, this feature relies on your system's mail transport agent or
+configured <A HREF="h_config_smtp_server">&quot;<!--#echo var="VAR_smtp-server"-->&quot;</A>
+having the negotiation mechanism introduced in
+&quot;Extended SMTP&quot; (ESMTP) and the specific extension called
+&quot;8BITMIME&quot;.
+<P>
+ESMTP allows for graceful migration to upgraded mail transfer agents, but
+it is possible that this feature might cause problems for some servers.
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_8bit_nntp =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-8bit-nntp-posting"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-8bit-nntp-posting"--></H1>
+
+This feature affects Alpine's behavior when posting news.
+
+<P>
+
+The Internet standard for exchanging USENET news messages (RFC-1036)
+specifies that USENET messages should conform to Internet mail standards
+and contain only 7bit characters, but much of the news transport software
+in use today is capable of successfully sending messages containing 8bit
+characters. Hence, many people believe that it is appropriate to send 8bit
+news messages without any MIME encoding.
+
+<P>
+
+Moreover, there is no Internet standard for explicitly negotiating 8bit
+transfer, as there is for Internet email. Therefore, Alpine provides the
+option of posting unencoded 8bit news messages, though not as the default.
+Setting this feature will turn OFF Alpine's MIME encoding of newsgroup
+postings that contain 8bit characters.
+
+<P>
+
+Note, articles may cross a path or pass through news transport software
+that is unsafe or even hostile to 8bit characters. At best this will only
+cause the posting to become garbled. The safest way to transmit 8bit
+characters is to leave Alpine's MIME encoding turned on, but recipients
+who lack MIME-aware tools are often annoyed when they receive MIME-encoded
+messages.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_mark_for_cc =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_mark-for-cc"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_mark-for-cc"--></H1>
+
+This feature affects Alpine's MESSAGE INDEX display.
+By default, a '+' is displayed in the first column if the
+message is addressed directly to you.
+When this feature is set and the message is not addressed to you, then a
+'-' character is displayed if the message is instead Cc'd directly
+to you.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_tab_uses_unseen =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_tab-uses-unseen-for-next-folder"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_tab-uses-unseen-for-next-folder"--></H1>
+
+This feature affects Alpine's behavior when using the TAB
+<A HREF="h_common_nextnew">NextNew Command</A>
+to move from one folder to the next.
+Alpine's usual behavior is to search for folders
+with <EM>Recent</EM> messages in them.
+Recent messages are messages that have arrived since the last time the
+folder was opened.
+
+<P>
+Setting this feature causes Alpine to search for <EM>Unseen</EM>
+messages instead of Recent messages.
+Unseen messages remain Unseen until you view them (or flag then as Seen with
+the <A HREF="h_common_flag">Flag Command</A>).
+Setting this feature allows you to locate messages you have not read
+instead of only recently received messages.
+When this feature is set, the feature
+<A HREF="h_config_fast_recent">&quot;<!--#echo var="FEAT_enable-fast-recent-test"-->&quot;</A>
+will have no effect, so the checking may be slower.
+
+<P>
+Another reason why you might want to use this feature is that Alpine sometimes
+opens folders implicitly behind the scenes, and this clears the
+Recent status of all messages in the folder.
+One example where this happens is when Saving or filtering a
+message to another folder.
+If that message has some <A HREF="h_config_keywords">keywords</A>
+set, then because of some shortcomings
+in the IMAP specification, the best way to ensure that those keywords are
+still set in the saved copy of the message is to open the folder and
+set the keywords explicitly.
+Because this clears the Recent status of all messages in that folder the
+folder will not be found by the NextNew command unless this feature is set.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_tab_new_only =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_tab-visits-next-new-message-only"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_tab-visits-next-new-message-only"--></H1>
+
+This feature affects Alpine's behavior when using the TAB key to move from
+one message to the next. Alpine's usual behavior is to select the next
+unread message or message flagged as "Important".
+
+<P>
+
+Setting this feature causes Alpine to skip the messages flagged as important,
+and select unread messages exclusively. Tab behavior when there are no
+new messages left to select remains unchanged.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_warn_if_subj_blank =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_warn-if-blank-subject"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_warn-if-blank-subject"--></H1>
+
+This feature affects Alpine's behavior when you send a message being
+composed.
+If this option is set, Alpine will check to see if the message about to be sent
+has a subject or not.
+If not, you will be asked if you want to send the message anyway.
+<P>
+
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_warn_if_fcc_blank =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_warn-if-blank-fcc"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_warn-if-blank-fcc"--></H1>
+
+This feature affects Alpine's behavior when you send a message being
+composed.
+If this option is set, Alpine will check to see if the message about to be sent
+has an Fcc or not.
+If not, you will be asked if you want to send the message anyway.
+<P>
+
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_warn_if_no_to_or_cc =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_warn-if-blank-to-and-cc-and-newsgroups"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_warn-if-blank-to-and-cc-and-newsgroups"--></H1>
+
+This feature affects Alpine's behavior when you send a message being
+composed.
+If this option is set, Alpine will check to see if the message about to be sent
+has either a To address, a Cc address, or a Newsgroup.
+If none of these is set,
+you will be asked if you want to send the message anyway.
+<P>
+
+This feature is closely related to
+<A HREF="h_config_auto_fcc_only"><!--#echo var="FEAT_fcc-only-without-confirm"--></A>.
+Alpine will normally ask if you want to copy a message only to the Fcc.
+This feature also applies to cases where there is a Bcc but still no To, Cc,
+or Newsgroup.
+If the <!--#echo var="FEAT_fcc-only-without-confirm"--> feature is set and you are sending a
+message with only an Fcc, then you won't be asked about sending with
+a blank To and Cc and Newsgroups header even if this feature is set.
+Similarly, if you have already been asked if you want to send to the Fcc
+only and you have answered Yes, then you won't be asked again about sending with
+blank To, Cc, and Newsgroups headers even if this feature is set.
+<P>
+
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_quell_dead_letter =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_quell-dead-letter-on-cancel"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_quell-dead-letter-on-cancel"--></H1>
+
+This feature affects Alpine's behavior when you cancel a message being
+composed. Alpine's usual behavior is to write the canceled message to
+a file named
+<!--chtml if pinemode="os_windows"-->
+&quot;DEADLETR&quot;,
+<!--chtml else-->
+&quot;dead.letter&quot; in your home directory,
+<!--chtml endif-->
+overwriting any previous message. Under
+some conditions (some routine), this can introduce a noticeable delay.
+Setting this feature will cause Alpine NOT to write canceled compositions
+into the file.
+<P>
+NOTE: Enabling this feature means NO record of canceled messages is
+maintained.
+<P>
+This feature affects the newer option
+<A HREF="h_config_deadlets"><!--#echo var="VAR_dead-letter-files"--></A>, which specifies the
+number of dead letter files to keep around.
+If this feature is set, then the <!--#echo var="VAR_dead-letter-files"--> option has no effect.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_quell_beeps =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_quell-status-message-beeping"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_quell-status-message-beeping"--></H1>
+
+This feature affects Alpine's behavior when it displays status message
+(e.g., Error complaints, New mail warnings, etc). Setting this feature
+will not affect the display of such messages, but will cause those that
+emit a beep to become silent.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_suppress_user_agent =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_suppress-user-agent-when-sending"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_suppress-user-agent-when-sending"--></H1>
+
+If this feature is set then Alpine will not generate a
+<CODE>User-Agent</CODE> header in outgoing messages.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_quell_lock_failure_warnings =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_quell-lock-failure-warnings"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_quell-lock-failure-warnings"--></H1>
+
+This feature affects Alpine's behavior when it encounters a problem
+acquiring a mail folder lock. Typically, a secondary file associated
+with the mail folder being opened is created as part of the locking
+process. On some systems, such file creation has been administratively
+precluded by the system configuration.
+<P>
+Alpine issues a warning when such failures occur, which can become bothersome
+if the system is configured to disallow such actions. Setting this
+feature causes Alpine to remain silent when this part of lock creation fails.
+<P>
+WARNING: systems that have been configured in a way that precludes locking
+introduce some risk of mail folder corruption when more than one program
+attempts to modify the mail folder. This is most likely to occur to one's
+INBOX or other incoming message folder.
+<P>
+See also <A HREF="h_info_on_locking">&quot;What Systems Managers Need to Know about Alpine File Locking&quot;</A>.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_enable_role_take ======
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-rules-under-take"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-rules-under-take"--></H1>
+
+Normally, the Take command takes addresses from a message and helps you
+put them into your Address Book.
+If you use Rules for Indexcolors, Roles, Filtering, or Scoring;
+you may find it useful
+to be able to Take information from a message's headers and put it into
+a new Rule.
+When this feature is set, you will be given an extra prompt that gives
+you the choice to Take into the Address Book or Take into a rule.
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_enable_take_export ======
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-take-export"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-take-export"--></H1>
+
+Normally, the Take command takes addresses from a message and helps you
+put them into your Address Book.
+When this feature is set, you will be given an extra prompt that gives you
+the choice to Take addresses into a file instead of your Address
+Book.
+Only the user@domain_name part of the address is put in the file.
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_quell_folder_internal_msg ======
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_quell-folder-internal-msg"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_quell-folder-internal-msg"--></H1>
+
+This feature determines whether or not Alpine will create
+&quot;pseudo messages&quot; in folders that are in standard Unix or
+MMDF format. <P>
+
+Alpine will normally create these pseudo messages when they are not already
+present in a standard Unix or MMDF folder. Their purpose is to record
+certain mailbox state data needed for correct IMAP and POP server
+operation, and also for Alpine to be able to mark messages as Answered when
+the Reply has been postponed.<P>
+
+Sites that do not use IMAP/POP for remote mail access, and that need to
+support mail tools that are adversely affected by the presence of the
+pseudo-messages (e.g. some mail notification tools) may enable this
+feature to tell Alpine not to create them. Note that Alpine's
+&quot;Answered&quot; flag
+capability will be adversely affected if this is done.<P>
+
+Note too that, even if this feature is enabled, Alpine will not remove
+pseudo-messages when it encounters them (e.g. those created by UW's imapd
+or ipopd servers.) This feature has no effect on folders that are not in
+standard Unix or MMDF format, as pseudo-messages are not needed in the
+other formats to record mailbox state information.
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_mulnews_as_typed ======
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_mult-newsrc-hostnames-as-typed"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_mult-newsrc-hostnames-as-typed"--></H1>
+
+This feature will be of little use to most users.
+It has no effect unless the feature
+<A HREF="h_config_enable_mulnewsrcs"><!--#echo var="FEAT_enable-multiple-newsrcs"--></A>
+is set.
+
+When the <!--#echo var="FEAT_enable-multiple-newsrcs"--> feature is set
+then the setting of this feature may have an effect on the names of the
+newsrc files used.
+Normally, the name of the news server will be canonicalized before it is
+used in the newsrc file name.
+For example, if you type the news server name
+
+<P>
+<CENTER><SAMP>servername</SAMP></CENTER>
+<P>
+
+it is likely that the canonical name will be something like
+
+<P>
+<CENTER><SAMP>servername.example.com</SAMP></CENTER>
+<P>
+
+Or it may be the case that
+
+<P>
+<CENTER><SAMP>servername.example.com</SAMP></CENTER>
+<P>
+
+is really an alias (a DNS CNAME) for
+
+<P>
+<CENTER><SAMP>othername.example.com</SAMP></CENTER>
+<P>
+
+If this feature is not set, then the canonicalized names will be used.
+If this feature is set, then the name you typed in (or put in your
+configuration) will be used.
+
+<P><UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL>
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_quell_empty_dirs ======
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_quell-empty-directories"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_quell-empty-directories"--></H1>
+
+This feature causes Alpine to remove from the display any directories
+that do not contain at least one file or directory. This can be useful
+to prevent overly cluttered folder lists when a collection is stored on
+a server that treats all names as both a folder and a directory.
+
+<P>
+Note, enabling this feature can cause surprising behavior! For example,
+you can still use Add to create a directory, but unless you immediately
+enter that directory and create a folder, that newly created directory
+may not be displayed next time you enter the folder list.
+
+<P>
+The description above is not quite correct.
+Only directories which potentially may hold messages are hidden if empty.
+That is, a directory which is really just a directory and is not selectable
+as a folder will not be hidden.
+Such directories can occur on servers that treat most names as both a folder
+and a directory.
+These directories are typically created implicitly when a folder is created
+inside a directory that does not yet exist.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_termcap_wins =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_termdef-takes-precedence"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_termdef-takes-precedence"--></H1>
+
+This feature may affect Alpine's low-level input routines. Termcap (or
+terminfo, depending on how your copy of Alpine was compiled and linked)
+is the name of the database that describes terminal capabilities. In
+particular, it describes the sequences of characters that various keys
+will emit.
+
+<P>
+An example would be the Up Arrow key on the keyboard. Up
+Arrow is not a distinct character on most Unix systems. When you press
+the Up Arrow key a short sequence of characters are produced. This
+sequence is supposed to be described in the termcap database by the
+&quot;ku&quot; capability (or by the &quot;kcuu1&quot; capability if you
+are using terminfo instead of termcap).
+
+<P>
+By default, Alpine defines some terminal
+escape sequences that are commonly used. For example, the sequence
+&quot;ESC&nbsp;O&nbsp;A&quot; is recognized as an Up Arrow key. The sequence
+&quot;ESC&nbsp;[&nbsp;A&quot;
+is also recognized as an Up Arrow key. These are chosen because common
+terminals like VT100's or ANSI standard terminals produce these
+sequences when you press the Up Arrow key.
+
+<P>
+If your system's termcap
+(terminfo) database assigns some other function to the sequence
+&quot;ESC&nbsp;O&nbsp;A&quot;
+it is usually ignored by Alpine. Also, if your termcap (terminfo)
+database assigns a sequence that doesn't begin with an escape
+character (<SAMP>ESC</SAMP>) it is usually ignored by Alpine.
+This usually works fine
+because most terminals emit the escape sequences that Alpine has defined
+by default. We have also found that it is usually better to have these
+defaults take precedence over the definitions contained in the database
+because the defaults are more likely to be correct than the database.
+
+<P>
+There are some terminals where this breaks down. If you want Alpine to
+believe the definitions given in your termcap (terminfo) database in
+preference to the defaults the Alpine itself sets up, then you may turn
+this feature on. Then, sequences of characters that are defined in
+both termcap (terminfo) and in Alpine's set of defaults will be
+interpreted the way that termcap (terminfo) says they should be
+interpreted. Also, if your terminal capabilities database assigns a
+sequence that doesn't begin with escape, it will not be ignored.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_cruise_mode =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-cruise-mode"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-cruise-mode"--></H1>
+
+This feature affects Alpine's behavior when you hit the
+&quot;Space&nbsp;Bar&quot; at
+the end of a displayed message. Typically, Alpine complains that the end
+of the text has already been reached. Setting this feature causes such
+keystrokes to be interpreted as if the &quot;Tab&quot; key had been hit, thus
+taking you to the next &quot;interesting&quot; message,
+or scanning ahead to the
+next incoming folder with &quot;interesting&quot; messages.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_cruise_mode_delete =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-cruise-mode-delete"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-cruise-mode-delete"--></H1>
+
+This feature modifies the behavior of Alpine's
+<A HREF="h_config_cruise_mode">&quot;<!--#echo var="FEAT_enable-cruise-mode"-->&quot;</A> feature.
+Setting this feature causes Alpine to implicitly delete read
+messages when it moves on to display the next &quot;interesting&quot; message.
+<P>
+NOTE: Beware when enabling this feature AND the
+<A HREF="h_config_auto_expunge">&quot;<!--#echo var="FEAT_expunge-without-confirm"-->&quot;</A>
+feature.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_slash_coll_entire =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_slash-collapses-entire-thread"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_slash-collapses-entire-thread"--></H1>
+
+The slash (/) command is available from the MESSAGE INDEX screen when
+the folder is sorted by either Threads or OrderedSubject, and the
+<A HREF="h_config_thread_disp_style"><!--#echo var="VAR_threading-display-style"--></A>
+is set to something other than &quot;none&quot;.
+Normally, the slash command Collapses or Expands the subthread that
+starts at the currently highlighted message, if any.
+If this option is set, then the slash command Collapses or Expands the
+<EM>entire</EM> current thread instead of just the subthread.
+The current thread is simply the top-level thread that contains the
+current message.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_color_thrd_import =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_thread-index-shows-important-color"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_thread-index-shows-important-color"--></H1>
+
+This option affects only the THREAD INDEX screen.
+Whether or not you ever see a THREAD INDEX screen depends on the setting
+of the configuration option
+<A HREF="h_config_thread_index_style">&quot;<!--#echo var="VAR_threading-index-style"-->&quot;</A>
+and on the sort order of the index.
+
+<P>
+If a message within a thread is flagged as Important
+and this option is set, then
+the entire line in the THREAD INDEX will be colored the color of the
+Index-important Symbol, which can be set using the
+<A HREF="h_color_setup">Setup Kolor</A> screen.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_allow_goto =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-goto-in-file-browser"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-goto-in-file-browser"--></H1>
+
+This feature modifies the behavior of Alpine's file browser. Setting this
+feature causes Alpine to offer the "G Goto" command in the file browser.
+That is the default.
+
+<P>
+
+The Goto command allows you to explicitly type in the desired directory.
+<P>
+&lt;End of help on this topic&gt;
+</BODY></HTML>
+====== h_config_add_ldap =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_ldap-result-to-addrbook-add"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_ldap-result-to-addrbook-add"--></H1>
+
+If both the Directory option
+<A HREF="h_config_ldap_opts_impl">&quot;Use-Implicitly-From-Composer&quot;</A>
+and this feature are set,
+then when an implicit directory lookup is done from the
+composer you will automatically be prompted to add the result of the
+directory lookup to your address book.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_patterns_compat_behavior =====
+<HTML>
+<HEAD>
+<TITLE>Rules Behavior Changes in Pine 4.50</TITLE>
+</HEAD>
+<BODY>
+<H1>Rules Behavior Changes in Pine 4.50</H1>
+
+In Alpine, Rules that contain unrecognized elements
+are ignored.
+In most cases, the unrecognized elements will be something that was
+added as a new Rules feature in a later version of Alpine.
+In versions of Pine <EM>prior</EM> to 4.50, Pine did <EM>not</EM>
+ignore rules that contained unrecognized elements.
+For example, a new element of Rules that was added in Pine 4.50 is
+Age interval.
+Suppose you add an Indexcolor rule, using version Pine 4.50 or later, that colors
+all messages older than a week red.
+Now, if you run Pine 4.44 using that same configuration file, it will not
+recognize the Age interval and so will just ignore it.
+That means that all messages will match that rule so all messages will
+be colored red when using Pine version 4.44.
+
+<P>
+This behavior was considered a bug so it is fixed in Alpine and Pine 4.50 and later.
+However, since the behavior still exists in versions prior to Pine 4.50 and
+since Filtering is a potentially destructive operation, another measure
+was taken to attempt to avoid unintentional Filtering of messages.
+The first time that you run Alpine or a Pine that is version 4.50 or greater,
+the rules in your Filters configuration variable (&quot;Patterns-Filters&quot;)
+will be copied to a new Filters configuration variable
+with a different name (&quot;Patterns-Filters2&quot;).
+From then on, Alpine will continue to use the new
+variable.
+Of course, Pine version 4.44 or lower will continue to use the old
+variable.
+That means that if you are using Alpine
+and also using a version of Pine that is older than 4.50, they will not
+share the configuration information about Filters.
+If you make a change in one version you won't see it in the other version.
+
+<P>
+Since Scoring can be used to trigger Filtering, the same thing has been
+done for Score rules.
+The old configuration variable name is (&quot;Patterns-Scores&quot;)
+and the new name is (&quot;Patterns-Scores2&quot;).
+The same is not true of Role, Indexcolor, and Other rules that are
+thought to be less harmful when a mistake is made.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_filt_opts_sentdate =======
+<HTML>
+<HEAD>
+<TITLE>PATTERN FEATURE: Use-Date-Header-For-Age</TITLE>
+</HEAD>
+<BODY>
+<H1>PATTERN FEATURE: Use-Date-Header-For-Age</H1>
+
+By default, the Age interval of a Pattern uses a message's time of
+arrival to compute the age of the message.
+If this feature is set, the date in the message's Date header will
+be used instead.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_filt_opts_notdel =======
+<HTML>
+<HEAD>
+<TITLE>FILTER FEATURE: Move-Only-if-Not-Deleted</TITLE>
+</HEAD>
+<BODY>
+<H1>FILTER FEATURE: Move-Only-if-Not-Deleted</H1>
+
+If this option is set then a message will be moved into the
+specified folder only if it is not marked for deletion.
+This is useful if you have multiple Alpine sessions running
+simultaneously and you don't want messages to be filtered into a
+folder more than once.
+It is also useful if you want to filter
+only the &quot;undeleted&quot; messages in a newsgroup into a folder.
+This method is not foolproof.
+There may be cases where a message
+gets marked deleted and so it is never filtered into the folder.
+For example, if you deleted it in another Alpine session or another mail
+program that didn't use the filtering rule.
+<P>
+This option has no effect if the Filter Action is not set to Move.
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_config_filt_opts_nonterm =======
+<HTML>
+<HEAD>
+<TITLE>FILTER FEATURE: Dont-Stop-Even-if-Rule-Matches</TITLE>
+</HEAD>
+<BODY>
+<H1>FILTER FEATURE: Dont-Stop-Even-if-Rule-Matches</H1>
+
+If this option is set then this is a non-terminating rule.
+Usually, for each message, Alpine searches through the Filter Rules until
+a match is found and then it performs the action associated with that rule.
+Rules following the match are not considered.
+If this option is set then the search for matches will continue at the next
+rule.
+<P>
+If a non-terminating rule matches then the actions associated with
+that rule, except for any implied deletion of the message, are performed
+before the match for the next rule is checked.
+For example, if the non-terminating rule sets the Important status, then that
+status will be set when the next rule is considered.
+However, if the non-terminating rule Moves the message, the message will
+actually be copied instead of copied and deleted so that it is still there
+for the next rule.
+A moved message is deleted after all the relevant rules have been checked.
+The name of the &quot;Move&quot; action is confusing in this case because
+a single message can be moved to more than one folder.
+It turns the Move into a Copy instead, but it is still followed by a deletion
+at the end.
+<P>
+This option may be useful if you want to have a single message filtered to
+two different folders because it matches two different Patterns.
+For example, suppose you normally filter messages to a particular mailing
+list into one folder, and messages addressed directly to you into a second
+folder.
+If a message is sent to both you and the list (and you can tell that by
+looking at the headers of the message) this option may give you a convenient
+way to capture a copy to each folder.
+(It may also cause you to capture two copies to each folder,
+depending on whether your mail system delivers one or two copies of the
+message to you and on how the list works.)
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+===== h_mainhelp_smime ======
+<HTML>
+<HEAD>
+<TITLE>S/MIME Overview</TITLE>
+</HEAD>
+<BODY>
+<H1>S/MIME Overview</H1>
+
+S/MIME is a standard for the public key encryption and signing of email.
+UNIX Alpine contains a basic implementation of S/MIME based on
+the <A HREF="http://www.openssl.org/">OpenSSL</A> libraries.
+To check if this version of Alpine supports S/MIME look at
+<A HREF="X-Alpine-Config:">Supported Options in this Alpine</A> and look
+for &quot;S/MIME&quot; under the &quot;Encryption&quot; heading.
+<P>
+Some limitations:
+<UL>
+ <LI> There is no PC-Alpine implementation.
+ <LI> There is no provision for checking for CRLs
+ (Certificate Revocation Lists) in Alpine.
+ <LI> This built-in S/MIME implementation is not compatible with and does not help with PGP.
+ <LI> There is no mechanism available for feeding either an entire incoming
+ or an entire outgoing message to an external
+ filter and using that external filter to do S/MIME or PGP processing.
+ <LI> Because the implementation currently uses OpenSSL, there is only a very
+ limited integration with the Mac OS Keychain (the storing and access of
+ public certificates).
+ <LI> There is no way to view or manipulate the lists of certificates from
+ within Alpine.
+</UL>
+<P>
+The S/MIME configuration screen is reached by going to the Main Menu and typing
+the &quot;S&nbsp;Setup&quot; command followed by &quot;M&nbsp;S/MIME&quot;.
+<P>
+
+<H2>S/MIME BASICS</H2>
+
+In order to digitally sign messages you send you must have a public/private key-pair.
+This may be obtained from a public Certificate Authority (CA) such as Thawte, Verisign, Comodo,
+or GoDaddy; or from a smaller CA such as a university which provides certificates for its
+users or a company which provides certificates for its workers.
+These certificates are bound to an email address, so the identity being verified is the
+email address not a person's name.
+<P>
+Mail is signed by using the sender's private key, which only the owner of the private key
+has access to.
+The signature is verified using the signer's public key, which anyone can
+have access to.
+With Alpine, the first time you receive a signed message the public key of the
+sender will be stored for future use.
+
+<P>
+Mail is encrypted using the recipient's public key and decrypted by
+the recipient with their private key.
+
+<P>
+You need a key of your own in order to sign outgoing messages and to have others
+encrypt messages sent to you.
+You do not need a key of your own to verify signed messages sent by others or to
+encrypt messages sent to others.
+
+<H2>ALPINE S/MIME CERTIFICATE STORAGE</H2>
+
+By default UNIX Alpine stores the certificates it uses in a directory in your
+home directory.
+The directory name is
+<P>
+<CENTER><SAMP>.alpine-smime</SAMP></CENTER>
+<P>
+Within that directory are three subdirectories.
+Each of the three subdirectories contains files with PEM-encoded contents,
+the default format for OpenSSL.
+The &quot;<SAMP>public</SAMP>&quot; directory contains public certificates.
+The files within that directory have names that are email addresses with the
+suffix &quot;<SAMP>.crt</SAMP>&quot; appended.
+An example filename is
+<P>
+<CENTER><SAMP>user@example.com.crt</SAMP></CENTER>
+<P>
+The &quot;<SAMP>private</SAMP>&quot; directory contains private keys, probably just one for
+your private key.
+These are also email addresses but with the suffix &quot;<SAMP>.key</SAMP>&quot; instead.
+The third directory is &quot;<SAMP>ca</SAMP>&quot; and it contains certificates for any Certificate
+Authorities that you want to trust but that aren't contained in the set of system CAs.
+Those files may have arbitrary names as long as they end with the
+suffix &quot;<SAMP>.crt</SAMP>&quot;.
+
+<H2>HOW TO SIGN AND ENCRYPT</H2>
+
+If you have a certificate you may sign outgoing messages.
+After typing the Ctrl-X command to send a message you will see the prompt
+<P>
+<CENTER><SAMP>Send message?</SAMP></CENTER>
+<P>
+Available subcommands include &quot;G&nbsp;Sign&quot; and &quot;E&nbsp;Encrypt&quot;.
+Typing the &quot;G&quot; command will change the prompt to
+<P>
+<CENTER><SAMP>Send message (Signed)?</SAMP></CENTER>
+<P>
+Typing the &quot;E&quot; command will change the prompt to
+<P>
+<CENTER><SAMP>Send message (Encrypted)?</SAMP></CENTER>
+<P>
+You may even type both to get
+<P>
+<CENTER><SAMP>Send message (Encrypted, Signed)?</SAMP></CENTER>
+<P>
+
+<H2>HOW TO READ SIGNED OR ENCRYPTED MESSAGES</H2>
+
+The reading of a signed message should not require any special action on
+your part.
+There should be an editorial addition at the start of the message which
+says either
+<P>
+<CENTER><SAMP>This message was cryptographically signed.</SAMP></CENTER>
+<P>
+or
+<P>
+<CENTER><SAMP>This message was cryptographically signed but the signature could not be verified.</SAMP></CENTER>
+<P>
+If an encrypted message is sent to you the encrypted text will not
+be shown.
+You will have to type the &quot;Ctrl-D&nbsp;Decrypt&quot; command (from the screen where
+you are viewing the message) and supply your passphrase when asked.
+<P>
+For a signed or encrypted message there is also a &quot;Ctrl-E&nbsp;Security&quot; command
+which gives you some information about the certificate used to sign or encrypt the message.
+
+<H2>MISCELLANEOUS</H2>
+
+You may have access to a private certificate in the PKCS12 format,
+which would sometimes be in a file with a &quot;.p12&quot; suffix.
+The UNIX shell command
+<P>
+<CENTER><SAMP>openssl pkcs12 -in file.p12 -out file.pem</SAMP></CENTER>
+<P>
+may work to convert that from the PKCS12 format to the PEM format.
+Then that file could be placed in the &quot;<SAMP>private</SAMP>&quot;
+directory with a filename of your email address followed by the
+suffix &quot;<SAMP>.key</SAMP>&quot;.
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_smime_pubcertdir =====
+<HTML>
+<HEAD>
+<TITLE>S/MIME OPTION: <!--#echo var="VAR_smime-public-cert-directory"--></TITLE>
+</HEAD>
+<BODY>
+<H1>S/MIME OPTION: <!--#echo var="VAR_smime-public-cert-directory"--></H1>
+
+UNIX Alpine only.
+<P>
+If the option
+<A HREF="h_config_smime_pubcertcon"><!--#echo var="VAR_smime-public-cert-container"--></A>
+is set then this option will have no effect.
+<P>
+Normally, Public Certificates for use with S/MIME will be stored in the directory
+which is the value of this option.
+Those certificates will be stored in PEM format, one certificate per file.
+The name of the file for the certificate corresponding to
+<P>
+<CENTER><SAMP>emailaddress</SAMP></CENTER>
+<P>
+should be
+<P>
+<CENTER><SAMP>emailaddress.crt</SAMP></CENTER>
+<P>
+For example, a file for user@example.com would be in the file
+<P>
+<CENTER><SAMP>user@example.com.crt</SAMP></CENTER>
+<P>
+in this directory.
+<P>
+Use the Setup/SMIME screen to modify this variable.
+<P>
+Typically, the public certificates that you have will come from S/MIME signed
+messages that are sent to you.
+Alpine will extract the public certificate from the signed message and store
+it in the certificates directory.
+These PEM format public certificates look something like:
+<PRE>
+-----BEGIN CERTIFICATE-----
+MIIFvTCCBKWgAwIBAgIQD4fYFHVI8T20yN4nus097DANBgkqhkiG9w0BAQUFADCB
+rjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
+Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
+...
+2b9KGqDyMWW/rjNnmpjzjT2ObGM7lRA8lke4FLOLajhrz4ogO3b4DFfAAM1VSZH8
+D6sOwOLJZkLY8FRsfk63K+2EMzA2+qAzMKupgeTLqXIf
+-----END CERTIFICATE-----
+</PRE>
+<P>
+<UL>
+<LI><A HREF="h_mainhelp_smime">General S/MIME help</A>
+</UL><P>
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_smime_pubcertcon =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_smime-public-cert-container"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_smime-public-cert-container"--></H1>
+
+UNIX Alpine only.
+<P>
+If this option is set it will be used instead of
+<A HREF="h_config_smime_pubcertdir"><!--#echo var="VAR_smime-public-cert-directory"--></A>.
+<P>
+This option gives you a way to store certificates remotely on an IMAP server
+instead of storing the certificates one per file locally.
+In order to do that you just give this option a remote folder name for a folder
+which does not yet exist.
+The name is similar to the name you might use for a remote configuration file.
+A remote folder name might look something like:
+<P>
+<CENTER><SAMP>{myimaphost.myschool.k12.wa.us}mail/publiccerts</SAMP></CENTER>
+<P>
+See
+<A HREF="h_valid_folder_names">Valid Folder Names</A> for more information
+about the syntax of folder names.
+<P>
+Use the Setup/SMIME screen to modify this variable.
+<P>
+<UL>
+<LI><A HREF="h_mainhelp_smime">General S/MIME help</A>
+</UL><P>
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_smime_privkeydir =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_smime-private-key-directory"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_smime-private-key-directory"--></H1>
+
+UNIX Alpine only.
+<P>
+In order to sign outgoing S/MIME messages you will need a
+personal digital ID certificate.
+You will usually get such a certificate from a certificate authority such as
+Thawte or CAcert.
+(In order to encrypt outgoing messages you don't need a personal digital ID, you
+need the public certificate of the recipient instead.)
+If the option
+<A HREF="h_config_smime_privkeycon"><!--#echo var="VAR_smime-private-key-container"--></A>
+is set then this option will have no effect.
+<P>
+Normally, Private Keys for use with S/MIME will be stored in the directory
+which is the value of this option.
+Those certificates will be stored in PEM format, one certificate per file.
+The name of the file for the certificate corresponding to your
+<P>
+<CENTER><SAMP>emailaddress</SAMP></CENTER>
+<P>
+should be
+<P>
+<CENTER><SAMP>emailaddress.key</SAMP></CENTER>
+<P>
+For example, if your address is user@example.com the name of the file would be
+<P>
+<CENTER><SAMP>user@example.com.key</SAMP></CENTER>
+<P>
+in this directory.
+<P>
+Use the Setup/SMIME screen to modify this variable.
+<P>
+Typically, the private key that you have will come from a Certificate
+Authority.
+The private key should be stored in a PEM format file that
+looks something like:
+<PRE>
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,2CBD328FD84CF5C6
+
+YBEXYLgLU9NJoc1V+vJ6UvcF08RX54S6jXsmgL0b5HGkudG6fhnmHkH7+UCvM5NI
+SXO/F8iuZDfs1VGG0NyitkFZ0Zn2vfaGovBvm15gx24b2xnZDLRB7/bNZkurnK5k
+VjAjZ2xXn2hFp2GJwqRdmxYNqsKGu52B99oti5HUWuZ2GFRaWjn5hYOqeApZE2uA
+...
+oSRqfI51UdSRt0tmGhHeTvybUVrHm9eKft8TTGf+qSBqzSc55CsmoVbRzw4Nfhix
+m+4TJybNGNfAgOctSkEyY/OCb49fRRQTCBZVIhzLGGmpYmkO55HbIA==
+-----END RSA PRIVATE KEY-----
+</PRE>
+<P>
+<UL>
+<LI><A HREF="h_mainhelp_smime">General S/MIME help</A>
+</UL><P>
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_smime_privkeycon =====
+<HTML>
+<HEAD>
+<TITLE>OPTION: <!--#echo var="VAR_smime-private-key-container"--></TITLE>
+</HEAD>
+<BODY>
+<H1>OPTION: <!--#echo var="VAR_smime-private-key-container"--></H1>
+
+UNIX Alpine only.
+<P>
+If this option is set it will be used instead of
+<A HREF="h_config_smime_privkeydir"><!--#echo var="VAR_smime-private-key-directory"--></A>.
+<P>
+This option gives you a way to store keys remotely on an IMAP server
+instead of storing the keys one per file locally.
+In order to do that you just give this option a remote folder name for a folder
+which does not yet exist.
+The name is similar to the name you might use for a remote configuration file.
+A remote folder name might look something like:
+<P>
+<CENTER><SAMP>{myimaphost.myschool.k12.wa.us}mail/privatekeys</SAMP></CENTER>
+<P>
+See
+<A HREF="h_valid_folder_names">Valid Folder Names</A> for more information
+about the syntax of folder names.
+<P>
+Use the Setup/SMIME screen to modify this variable.
+<P>
+<UL>
+<LI><A HREF="h_mainhelp_smime">General S/MIME help</A>
+</UL><P>
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_smime_cacertdir =====
+<HTML>
+<HEAD>
+<TITLE>S/MIME OPTION: <!--#echo var="VAR_smime-cacert-directory"--></TITLE>
+</HEAD>
+<BODY>
+<H1>S/MIME OPTION: <!--#echo var="VAR_smime-cacert-directory"--></H1>
+
+UNIX Alpine only.
+<P>
+If the option
+<A HREF="h_config_smime_cacertcon"><!--#echo var="VAR_smime-cacert-container"--></A>
+is set then this option will have no effect.
+<P>
+CACert is a shorthand name for certification authority certificate.
+Normally Alpine will use the CACerts that are located in the standard system
+location for CACerts.
+It may be the case that one of your correspondents has a Digital ID which has
+been signed by a certificate authority that is not in the regular set of system certificate
+authorities.
+You may supplement the system list by adding further certificates of your own.
+These should be stored in the directory
+which is the value of this option.
+The certificates will be stored in PEM format, one certificate per file.
+The names of the files can be anything ending in &quot;.crt&quot;.
+<P>
+Use the Setup/SMIME screen to modify this variable.
+<P>
+These PEM format CA certificates look very similar to your public
+certificates for particular email addresses
+(<A HREF="h_config_smime_pubcertdir"><!--#echo var="VAR_smime-public-cert-directory"--></A>).
+<P>
+<UL>
+<LI><A HREF="h_mainhelp_smime">General S/MIME help</A>
+</UL><P>
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_smime_cacertcon =====
+<HTML>
+<HEAD>
+<TITLE>S/MIME OPTION: <!--#echo var="VAR_smime-cacert-container"--></TITLE>
+</HEAD>
+<BODY>
+<H1>S/MIME OPTION: <!--#echo var="VAR_smime-cacert-container"--></H1>
+
+UNIX Alpine only.
+<P>
+If this option is set it will be used instead of
+<A HREF="h_config_smime_cacertdir"><!--#echo var="VAR_smime-cacert-directory"--></A>.
+<P>
+This option gives you a way to store certificates remotely on an IMAP server
+instead of storing the certificates one per file locally.
+In order to do that you just give this option a remote folder name for a folder
+which does not yet exist.
+The name is similar to the name you might use for a remote configuration file.
+A remote folder name might look something like:
+<P>
+<CENTER><SAMP>{myimaphost.myschool.k12.wa.us}mail/cacerts</SAMP></CENTER>
+<P>
+See
+<A HREF="h_valid_folder_names">Valid Folder Names</A> for more information
+about the syntax of folder names.
+<P>
+Use the Setup/SMIME screen to modify this variable.
+<P>
+<UL>
+<LI><A HREF="h_mainhelp_smime">General S/MIME help</A>
+</UL><P>
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+========== h_config_smime_sign_by_default ==========
+<HTML>
+<HEAD>
+<TITLE>S/MIME FEATURE: <!--#echo var="FEAT_smime-sign-by-default"--></TITLE>
+</HEAD>
+<BODY>
+<H1>S/MIME FEATURE: <!--#echo var="FEAT_smime-sign-by-default"--></H1>
+
+UNIX Alpine only.
+<P>
+This feature only has an effect if your version of Alpine includes
+support for S/MIME.
+It affects Alpine's behavior when you send a message.
+If this option is set, the &quot;Sign&quot; option will default to ON when sending messages.
+<P>
+Only the default value is affected.
+In any case, you may still toggle the Signing option on or off before sending
+with the &quot;G Sign&quot; command (provided you have a personal digital ID
+certificate).
+<P>
+<UL>
+<LI><A HREF="h_mainhelp_smime">General S/MIME help</A>
+</UL><P>
+<P>
+
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+========== h_config_smime_pubcerts_in_keychain ==========
+<HTML>
+<HEAD>
+<TITLE>S/MIME FEATURE: <!--#echo var="FEAT_publiccerts-in-keychain"--></TITLE>
+</HEAD>
+<BODY>
+<H1>S/MIME FEATURE: <!--#echo var="FEAT_publiccerts-in-keychain"--></H1>
+
+UNIX Alpine only.
+<P>
+If this feature is set the Mac OS X default keychain will be used as the place
+to store public certificates instead of a
+<A HREF="h_config_smime_pubcertdir"><!--#echo var="VAR_smime-public-cert-directory"--></A>
+or a
+<A HREF="h_config_smime_pubcertcon"><!--#echo var="VAR_smime-public-cert-container"--></A>.
+<P>
+<UL>
+<LI><A HREF="h_mainhelp_smime">General S/MIME help</A>
+</UL><P>
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+========== h_config_smime_dont_do_smime ==========
+<HTML>
+<HEAD>
+<TITLE>S/MIME FEATURE: <!--#echo var="FEAT_smime-dont-do-smime"--></TITLE>
+</HEAD>
+<BODY>
+<H1>S/MIME FEATURE: <!--#echo var="FEAT_smime-dont-do-smime"--></H1>
+
+UNIX Alpine only.
+<P>
+Setting this feature turns off all of Alpine's S/MIME support.
+You might want to set this if you are having trouble due to the S/MIME support.
+<P>
+<UL>
+<LI><A HREF="h_mainhelp_smime">General S/MIME help</A>
+</UL><P>
+<P>
+
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+========== h_config_smime_encrypt_by_default ==========
+<HTML>
+<HEAD>
+<TITLE>S/MIME FEATURE: <!--#echo var="FEAT_smime-encrypt-by-default"--></TITLE>
+</HEAD>
+<BODY>
+<H1>S/MIME FEATURE: <!--#echo var="FEAT_smime-encrypt-by-default"--></H1>
+
+UNIX Alpine only.
+<P>
+This feature only has an effect if your version of Alpine includes
+support for S/MIME.
+It affects Alpine's behavior when you send a message.
+If this option is set, the &quot;Encrypt&quot; option will default to ON when sending messages.
+<P>
+Only the default value is affected.
+In any case, you may still toggle the Encrypt option on or off before sending
+with the &quot;E Encrypt&quot; command (provided you have a the public digital ID
+for the recipient).
+<P>
+<UL>
+<LI><A HREF="h_mainhelp_smime">General S/MIME help</A>
+</UL><P>
+<P>
+
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+========== h_config_smime_remember_passphrase ==========
+<HTML>
+<HEAD>
+<TITLE>S/MIME FEATURE: <!--#echo var="FEAT_smime-remember-passphrase"--></TITLE>
+</HEAD>
+<BODY>
+<H1>S/MIME FEATURE: <!--#echo var="FEAT_smime-remember-passphrase"--></H1>
+
+UNIX Alpine only.
+<P>
+This feature only has an effect if your version of Alpine includes
+support for S/MIME.
+If this option is set, you will only have to enter your passphrase for your private key
+once during an Alpine session.
+<P>
+<UL>
+<LI><A HREF="h_mainhelp_smime">General S/MIME help</A>
+</UL><P>
+<P>
+
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_smime_transfer_pub_to_con =====
+<HTML>
+<HEAD>
+<TITLE>S/MIME: Transfer Public Certs to Container</TITLE>
+</HEAD>
+<BODY>
+<H1>S/MIME: Transfer Public Certs to Container</H1>
+
+UNIX Alpine only.
+<P>
+The Transfer command will copy the public certificates in your configured
+<A HREF="h_config_smime_pubcertdir"><!--#echo var="VAR_smime-public-cert-directory"--></A>
+to the container in your configured
+<A HREF="h_config_smime_pubcertcon"><!--#echo var="VAR_smime-public-cert-container"--></A>.
+This might be useful if you decide to switch from using a cert directory to a cert
+container.
+<P>
+Warning: Any previous contents in the container will be lost.
+<P>
+<UL>
+<LI><A HREF="h_mainhelp_smime">General S/MIME help</A>
+</UL><P>
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_smime_transfer_pub_to_dir =====
+<HTML>
+<HEAD>
+<TITLE>S/MIME: Transfer Public Certs to Directory</TITLE>
+</HEAD>
+<BODY>
+<H1>S/MIME: Transfer Public Certs to Directory</H1>
+
+UNIX Alpine only.
+<P>
+The Transfer command will copy the public certificates in your configured
+<A HREF="h_config_smime_pubcertcon"><!--#echo var="VAR_smime-public-cert-container"--></A>
+to the directory in your configured
+<A HREF="h_config_smime_pubcertdir"><!--#echo var="VAR_smime-public-cert-directory"--></A>.
+This might be useful if you decide to switch from using a cert container to a cert
+directory.
+<P>
+<UL>
+<LI><A HREF="h_mainhelp_smime">General S/MIME help</A>
+</UL><P>
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_smime_transfer_priv_to_con =====
+<HTML>
+<HEAD>
+<TITLE>S/MIME: Transfer Private Keys to Container</TITLE>
+</HEAD>
+<BODY>
+<H1>S/MIME: Transfer Private Keys to Container</H1>
+
+UNIX Alpine only.
+<P>
+The Transfer command will copy the private keys in your configured
+<A HREF="h_config_smime_privkeydir"><!--#echo var="VAR_smime-private-key-directory"--></A>.
+to the container in your configured
+<A HREF="h_config_smime_privkeydir"><!--#echo var="VAR_smime-private-key-container"--></A>.
+This might be useful if you decide to switch from using a key directory to a key
+container.
+<P>
+Warning: Any previous contents in the container will be lost.
+<P>
+<UL>
+<LI><A HREF="h_mainhelp_smime">General S/MIME help</A>
+</UL><P>
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_smime_transfer_priv_to_dir =====
+<HTML>
+<HEAD>
+<TITLE>S/MIME: Transfer Private Keys to Directory</TITLE>
+</HEAD>
+<BODY>
+<H1>S/MIME: Transfer Private Keys to Directory</H1>
+
+UNIX Alpine only.
+<P>
+The Transfer command will copy the private keys in your configured
+<A HREF="h_config_smime_privkeydir"><!--#echo var="VAR_smime-private-key-container"--></A>.
+to the directory in your configured
+<A HREF="h_config_smime_privkeydir"><!--#echo var="VAR_smime-private-key-directory"--></A>.
+This might be useful if you decide to switch from using a key container to a key
+directory.
+<P>
+<UL>
+<LI><A HREF="h_mainhelp_smime">General S/MIME help</A>
+</UL><P>
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_smime_transfer_cacert_to_con =====
+<HTML>
+<HEAD>
+<TITLE>S/MIME: Transfer CA Certs to Container</TITLE>
+</HEAD>
+<BODY>
+<H1>S/MIME: Transfer CA Certs to Container</H1>
+
+UNIX Alpine only.
+<P>
+The Transfer command will copy the CA certificates in your configured
+<A HREF="h_config_smime_cacertdir"><!--#echo var="VAR_smime-cacert-directory"--></A>
+to the container in your configured
+<A HREF="h_config_smime_cacertcon"><!--#echo var="VAR_smime-cacert-container"--></A>.
+This might be useful if you decide to switch from using a CA cert directory to a CA cert
+container.
+<P>
+Warning: Any previous contents in the container will be lost.
+<P>
+<UL>
+<LI><A HREF="h_mainhelp_smime">General S/MIME help</A>
+</UL><P>
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_smime_transfer_cacert_to_dir =====
+<HTML>
+<HEAD>
+<TITLE>S/MIME: Transfer CA Certs to Directory</TITLE>
+</HEAD>
+<BODY>
+<H1>S/MIME: Transfer CA Certs to Directory</H1>
+
+UNIX Alpine only.
+<P>
+The Transfer command will copy the CA certificates in your configured
+<A HREF="h_config_smime_cacertcon"><!--#echo var="VAR_smime-cacert-container"--></A>.
+to the directory in your configured
+<A HREF="h_config_smime_cacertdir"><!--#echo var="VAR_smime-cacert-directory"--></A>.
+This might be useful if you decide to switch from using a CA cert container to a CA cert
+directory.
+<P>
+<UL>
+<LI><A HREF="h_mainhelp_smime">General S/MIME help</A>
+</UL><P>
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_smime_transfer_pubcon_to_key =====
+<HTML>
+<HEAD>
+<TITLE>S/MIME: Transfer Public Certs to Keychain</TITLE>
+</HEAD>
+<BODY>
+<H1>S/MIME: Transfer Public Certs to Keychain</H1>
+
+Mac OS X Alpine only.
+<P>
+The Transfer command will copy the public certificates in your configured
+<A HREF="h_config_smime_pubcertcon"><!--#echo var="VAR_smime-public-cert-container"--></A>
+to your default Mac OS X Keychain.
+This might be useful if you decide to switch from using a cert container to using
+the Keychain to store your public certs, which you may do by using the
+feature
+<A HREF="h_config_smime_pubcerts_in_keychain">S/MIME FEATURE: <!--#echo var="FEAT_publiccerts-in-keychain"--></A>.
+<P>
+<UL>
+<LI><A HREF="h_mainhelp_smime">General S/MIME help</A>
+</UL><P>
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_smime_transfer_pubkey_to_con =====
+<HTML>
+<HEAD>
+<TITLE>S/MIME: Transfer Public Certs to Keychain</TITLE>
+</HEAD>
+<BODY>
+<H1>S/MIME: Transfer Public Certs to Keychain</H1>
+
+UNIX Alpine only.
+<P>
+The Transfer command will copy the public certificates in your configured
+<A HREF="h_config_smime_pubcertcon"><!--#echo var="VAR_smime-public-cert-container"--></A>
+to your default Mac OS X Keychain.
+This might be useful if you decide to switch from using a cert container to using
+the Keychain to store your public certs.
+<P>
+<UL>
+<LI><A HREF="h_mainhelp_smime">General S/MIME help</A>
+</UL><P>
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_lame_list_mode =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-lame-list-mode"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-lame-list-mode"--></H1>
+
+This feature modifies the method Alpine uses to ask your IMAP
+server for folder names to display in the FOLDER LIST screen.
+It is intended to compensate for a small set of IMAP servers that
+are programmed to ignore a part of the request, and thus respond
+to Alpine's query with nonsensical results.
+<P>
+
+If you find that Alpine is erroneously displaying blank folder lists,
+try enabling this feature.
+<P>
+
+NOTE: Enabling this feature has consequences for the Goto and Save
+commands. Many servers allow access to folders outside the area
+reserved for your personal folders via some reserved character,
+typically '#' (sharp), '~' (tilde) or '/' (slash). This mechanism
+allows, at the Goto and Save prompts, quick access to folders
+outside your personal folder collection without requiring a specific
+collection definition. This behavior will generally not be available
+when this feature is enabled.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_enable_mulnewsrcs =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_enable-multiple-newsrcs"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_enable-multiple-newsrcs"--></H1>
+
+This feature makes it so Alpine can use multiple newsrcs based on
+the news server being connected to, which allows for separate lists
+of subscribed-to newsgroups. When this feature is not set, there is only
+one list of newsgroups.
+<P>
+Under this feature, the name of a newsrc is based on the news server.
+For example, if your <a href="h_config_newsrc_path"><!--#echo var="VAR_newsrc-path"--></a>
+is set to &quot;.newsrc&quot;, and the news server you are connecting to is
+news.example.com, then the newsrc to be used is .newsrc-news.example.com.
+Setting this feature for the first time will allow for the option of using
+your old newsrc the next time you read news.
+<P>
+If this feature is set, then the feature
+<A HREF="h_config_mulnews_as_typed"><!--#echo var="FEAT_mult-newsrc-hostnames-as-typed"--></A>
+also may affect the name of the newsrc file that is used.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+======= h_ab_export_vcard =======
+<HTML>
+<HEAD>
+<TITLE>Address Book Export Format</TITLE>
+</HEAD>
+<BODY>
+<H1>Address Book Export Format</H1>
+
+You are exporting address book data from Alpine to a file outside of Alpine.
+You are being asked to choose the format of the export.
+Here are the choices:
+
+<DL>
+<DT><EM>A</EM>ddress List</DT>
+<DD>
+The addresses from the address book entries you are saving
+from will be saved one address per line.
+Address book lists (those with more than one address) will have
+all of their addresses saved separately.
+</DD>
+
+<DT><EM>V</EM>Card</DT>
+<DD>
+The entries will be saved in
+<A HREF="h_whatis_vcard">vCard</A> format.
+</DD>
+
+<DT><EM>T</EM>ab Separated</DT>
+<DD>
+The entries will be saved in tab-separated columns.
+There will be just 4 columns of data that correspond to Alpine's
+Nickname field, Full Name field, Address field, and Comment field.
+It might prove useful to Select only the Simple, non-List address book
+entries before Saving.
+</DD>
+
+<DT><EM>^C</EM> Cancel</DT>
+<DD>
+Cancel out of the Save.
+</DD>
+
+</DL>
+
+
+<P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_predict_nntp_server =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_predict-nntp-server"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_predict-nntp-server"--></H1>
+
+This feature allows Alpine to assume that the open NNTP server at the
+time of composition is the NNTP server to which the message should be
+posted. This is especially recommended when there are multiple News
+collections. If this feature is not set, Alpine will try to post to the first server in
+the <a href="h_config_nntp_server"><!--#echo var="VAR_nntp-server"--></a> variable. Setting
+this feature also negates the need to add News collection servers to
+the <!--#echo var="VAR_nntp-server"--> variable.
+<P>
+This feature can be especially handy when used in conjunction with
+<a href="h_config_enable_mulnewsrcs"><!--#echo var="FEAT_enable-multiple-newsrcs"--></a>.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_nntp_search_uses_overview =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_nntp-search-uses-overview"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_nntp-search-uses-overview"--></H1>
+
+This feature should probably be turned on unless it causes trouble.
+The results of the NNTP overview command (XOVER) may be used to help
+with some searches in news groups.
+It should result in quicker response time.
+Turning this feature on apparently causes search results which are
+different from what you would get with the feature turned off on some
+servers.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_thread_sorts_by_arrival =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_thread-sorts-by-arrival"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_thread-sorts-by-arrival"--></H1>
+
+This feature affects how a threading sort arranges threads. The default way
+to arrange threads is by the date of the earliest message in the thread.
+This feature arranges threads by the last message to arrive in a thread.
+<P>
+This feature causes old threads that get recent messages to sort to the bottom,
+where previously a message arrival to a thread would not rearrange the order of
+that thread.
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_textplain_int =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: <!--#echo var="FEAT_show-plain-text-internally"--></TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: <!--#echo var="FEAT_show-plain-text-internally"--></H1>
+
+This feature modifies the method Alpine uses to display Text/Plain
+MIME attachments from the Attachment Index screen. Normally, the
+&quot;View&quot; command searches for any externally defined (usually
+via the
+&quot;<A HREF="h_config_mailcap_path">Mailcap</A>&quot; file) viewer,
+and displays the selected text within that viewer.
+
+<P>
+Enabling this feature causes Alpine to ignore any external viewer
+settings and always display text with Alpine's internal viewer.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_wp_columns =====
+<HTML>
+<HEAD>
+<TITLE>WEB ALPINE OPTION: <!--#echo var="VAR_wp-columns"--></TITLE>
+</HEAD>
+<BODY>
+<H1>WEB ALPINE OPTION: <!--#echo var="VAR_wp-columns"--></H1>
+
+Web Alpine only.
+<P>
+This configuration setting specifies the number of horizontal characters
+used to format various WebAlpine pages. Smaller values will tend to reduce
+the amount of horizontal scrolling required to view pages within narrow
+browsers, such as those found on PDAs, and larger values will tend to
+spread more information across the page.
+
+<P>
+The Message List page uses the width to determine how many characters
+to assign each field. Note, a smaller value may result in a disproportionate
+amount of blank space between fields on each line. Similarly, a large
+value may result in cramped fields or horizontal scrolling.
+
+<P>
+The Message View page uses this value to determine when to wrap lines
+in displayed message text. Note, a smaller value may result in jagged
+right margins or confusing quoting. A larger value may cause lines of text to
+run beyond the browser's right edge, requiring horizontal scrolling.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_wp_state =====
+<HTML>
+<HEAD>
+<TITLE>WEB ALPINE OPTION: <!--#echo var="VAR_wp-state"--></TITLE>
+</HEAD>
+<BODY>
+<H1>WEB ALPINE OPTION: <!--#echo var="VAR_wp-state"--></H1>
+
+Web Alpine only.
+<P>
+Various aspects of cross-session state.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_wp_aggstate =====
+<HTML>
+<HEAD>
+<TITLE>WEB ALPINE OPTION: <!--#echo var="VAR_wp-aggstate"--></TITLE>
+</HEAD>
+<BODY>
+<H1>WEB ALPINE OPTION: <!--#echo var="VAR_wp-aggstate"--></H1>
+
+Web Alpine only.
+<P>
+Aggregate operations tab state.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_wp_indexlines =====
+<HTML>
+<HEAD>
+<TITLE>WEB ALPINE OPTION: <!--#echo var="VAR_wp-indexlines"--></TITLE>
+</HEAD>
+<BODY>
+<H1>WEB ALPINE OPTION: <!--#echo var="VAR_wp-indexlines"--></H1>
+
+Web Alpine only.
+<P>
+Number of index lines in table.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_wp_indexheight =====
+<HTML>
+<HEAD>
+<TITLE>WEB ALPINE OPTION: <!--#echo var="VAR_wp-indexheight"--></TITLE>
+</HEAD>
+<BODY>
+<H1>WEB ALPINE OPTION: <!--#echo var="VAR_wp-indexheight"--></H1>
+
+Web Alpine only.
+<P>
+Index table row height.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_rss_news =====
+<HTML>
+<HEAD>
+<TITLE>WEB ALPINE OPTION: <!--#echo var="VAR_rss_news"--></TITLE>
+</HEAD>
+<BODY>
+<H1>WEB ALPINE OPTION: <!--#echo var="VAR_rss-news"--></H1>
+
+Web Alpine only.
+<P>
+RSS News feed.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_rss_weather =====
+<HTML>
+<HEAD>
+<TITLE>WEB ALPINE OPTION: <!--#echo var="VAR_rss-weather"--></TITLE>
+</HEAD>
+<BODY>
+<H1>WEB ALPINE OPTION: <!--#echo var="VAR_rss-weather"--></H1>
+
+Web Alpine only.
+<P>
+RSS Weather feed.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_send_confirms_only_expanded =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: send-confirms-only-expanded</TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: send-confirms-only-expanded (Web Alpine Only)</H1>
+
+This Web Alpine option specifies whether or not a Send confirmations
+happens when a composed message is readied for sending or not. The
+default behavior is to not confirm that the nicknames were expanded to
+the intended addresses.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_enable_jump_command =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: enable-jump-command</TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: enable-jump-command (Web Alpine Only)</H1>
+
+This Web Alpine option specifies whether or not a Jump command is
+offered in the Message List and Message View pages. The command is
+implemented as an input field in the left column of the List and View
+screens.
+
+<P>
+When enabled and a number is entered in the input field while the
+Message List is displayed, the Message List is reframed with the
+specified message. While viewing a message, the message associated
+with the specified message number is displayed.
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_enable_newmail_sound =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: enable-newmail-sound</TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: enable-newmail-sound (Web Alpine Only)</H1>
+
+This Web Alpine option specifies whether or not a sound file is sent
+to the web browser along with the newmail notification message.
+
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_render_html_internally =====
+<HTML>
+<HEAD>
+<TITLE>FEATURE: render-html-internally</TITLE>
+</HEAD>
+<BODY>
+<H1>FEATURE: render-html-internally (Web Alpine Only)</H1>
+
+By default, Web Alpine will pass cleansed HTML text you receive in messages
+to the browser for display (rendering). This feature causes Web Alpine to convert
+the HTML text into plain text in the same way Unix and PC-Alpine do.
+
+
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_role_undo =====
+Yes, remember changes and exit back to list of roles; No, discard changes
+made in this screen; ^C, cancel exit and stay in this config screen.
+====== h_exit_editor =====
+S, save changes and exit from the editor; D, do not save changes but
+do exit from the editor; or ^C, cancel exit and stay in the editor.
+====== h_config_undo =====
+Yes, save changes and exit; No, exit without saving any changes made since
+entering this CONFIGURATION screen; ^C, cancel exit and stay in config screen.
+====== h_os_index_whereis =====
+Enter ^V or ^Y to go immediately to the last or first message in the index.
+Or, enter the match string followed by RETURN.
+====== h_os_index_whereis_agg =====
+Enter ^V or ^Y to go immediately to the last or first message in the index,
+Or, enter the match string followed by RETURN (or ^X to select all matches).
+=========== h_oe_add_full ==================
+Type the full name of the person being added and press the RETURN key.
+Press ^C to cancel addition.
+=========== h_oe_add_nick ==================
+Type a short nickname and press RETURN. A nickname is a short easy-to-
+remember word, name or initials like "joe", or "wcfields." ^C to cancel.
+========== h_oe_add_addr ================
+Type the e-mail address and press RETURN.
+Press ^C to cancel addition.
+========== h_oe_crlst_full ==============
+Type a long name or description for the list that you are creating and
+press RETURN. Press ^C to cancel creation of list.
+=========== h_oe_crlst_nick =============
+Type a nickname (short, easy-to-remember name or single word) for the list
+you are creating and press RETURN. Press ^C to cancel.
+========== h_oe_crlst_addr ==============
+Type an e-mail address, or a nickname already in the address book that you
+want to be part of this list and press RETURN.
+========== h_oe_adlst_addr =============
+Type an e-mail address or a nickname already in the address book that you
+want to add to this list and press RETURN.
+========== h_oe_editab_nick ============
+Change the nickname using the arrow keys and delete key. Press RETURN
+when done. Press ^C to cancel editing and leave the nickname as it was.
+========== h_oe_editab_full ============
+Change the full name using the arrow keys and delete key. Press RETURN
+when done. Press ^C to cancel editing and leave the full name as it was.
+========== h_oe_editab_addr ============
+Change the address using the arrow keys and delete key. Press RETURN
+when done. Press ^C to cancel editing and leave the address as it was.
+========== h_oe_editab_fcc ============
+Change the fcc using the arrow keys and delete key. Press RETURN when
+done. Press ^C to cancel editing and leave the fcc as it was.
+========== h_oe_editab_comment ============
+Change the comment field using the arrow keys and delete key. Press RETURN
+when done. Press ^C to cancel editing and leave the comment as it was.
+====== h_ab_forward =====
+Yes, expand nicknames and qualify local names with your current domain name;
+No, leave nicknames and local names as is; ^C, cancel.
+========== h_ab_export ==========
+Type the name of a file to write the addresses into and
+press RETURN. You may also specify an absolute path. Use ^C to cancel.
+========== h_ab_edit_a_field ==========
+Edit any of the fields of the currently selected entry by typing one of the
+letters at the bottom of the screen. Press ^C to cancel edit.
+====== h_ab_del_data_revert =====
+Press B to completely delete addrbook and revert to default, C to delete config
+and revert while leaving data, or D to only delete data (make it empty).
+====== h_ab_del_data_modify =====
+Press B to completely delete addrbook, C to delete configuration while leaving
+data, or D to delete data (make it empty) but leave config. ^C to cancel.
+====== h_ab_del_config_modify =====
+Yes, remove this address book from my configuration.
+No, make no changes now.
+====== h_ab_del_config_revert =====
+Yes, remove this address book from my config and revert to default.
+No, make no changes now.
+====== h_ab_del_default =====
+Yes, remove this default address book from my configuration.
+No, make no changes now.
+====== h_ab_really_delete =====
+Yes, delete the actual contents of the address book, not just the
+configuration. No, don't delete the data after all, cancel and start over.
+====== h_ab_del_ignore =====
+Press I to ignore all the default address books for this category. Press R to
+remove this one address book and add the others to your personal list.
+====== h_ab_del_dir_ignore =====
+Press I to ignore all the default directory servers for this category.
+Press R to remove this one server and add the others to your personal list.
+====== h_ab_copy_dups =====
+Yes, overwrite the existing entry.
+No, skip duplicates but save the rest. Press ^C to cancel.
+====== h_confirm_cancel =====
+Type C to Confirm that you want to abandon the message you are composing.
+Type N or ^C to cancel out of the cancel and keep composing.
+====== h_ab_text_or_vcard =====
+Text, start composer with displayed text already included.
+VCard, start composer with address book entry attached as a vCard. ^C cancels.
+====== h_ab_backup_or_ldap =====
+Backup, copy email address from entry and allow editing of it.
+LDAP, copy LDAP search criteria, do not allow editing of it. ^C cancels.
+====== h_ldap_text_or_vcard =====
+Text: export displayed text for selected entry. Address: export only the
+email address. VCard: export entry in vCard format. ^C cancels.
+====== h_ab_save_exp =====
+Save, save entry or entries to an address book.
+Export, save to file outside of pine. ^C cancels save.
+====== h_ab_add =====
+A, add a brand new entry to this address book.
+E, edit the entry that is currently highlighted. ^C to cancel.
+====== h_ab_shuf =====
+U, swap order of highlighted address book and the one above it.
+D, swap order of highlighted address book and the one below it. ^C to cancel.
+====== h_ab_shuf_up =====
+U, swap order of highlighted address book and the one above it.
+Press ^C to cancel.
+====== h_ab_shuf_down =====
+D, swap order of highlighted address book and the one below it.
+Press ^C to cancel.
+====== h_folder_prop =====
+Count is # of messages in the folder, Unseen means messages that have not
+been read, New means messages that were Recently added to the folder.
+====== h_role_shuf =====
+U, swap order of highlighted rule and the one above it.
+D, swap order of highlighted rule and the one below it. ^C to cancel.
+====== h_role_shuf_up =====
+U, swap order of highlighted rule and the one above it.
+Press ^C to cancel.
+====== h_role_shuf_down =====
+D, swap order of highlighted rule and the one below it.
+Press ^C to cancel.
+====== h_incoming_shuf =====
+B, swap order of highlighted directory and the one before it.
+F, swap order of highlighted directory and the one after it. ^C to cancel.
+====== h_incoming_shuf_up =====
+B, swap order of highlighted directory and the one before it.
+Press ^C to cancel.
+====== h_incoming_shuf_down =====
+F, swap order of highlighted directory and the one after it.
+Press ^C to cancel.
+====== h_dir_shuf =====
+U, swap order of highlighted directory and the one above it.
+D, swap order of highlighted directory and the one below it. ^C to cancel.
+====== h_dir_shuf_up =====
+U, swap order of highlighted directory and the one above it.
+Press ^C to cancel.
+====== h_dir_shuf_down =====
+D, swap order of highlighted directory and the one below it.
+Press ^C to cancel.
+====== h_hdrcolor_shuf =====
+U, swap order of highlighted Header Color and the one above it.
+D, swap order of highlighted Header Color and the one below it. ^C to cancel.
+====== h_hdrcolor_shuf_up =====
+U, swap order of highlighted Header Color and the one above it.
+Press ^C to cancel.
+====== h_hdrcolor_shuf_down =====
+D, swap order of highlighted Header Color and the one below it.
+Press ^C to cancel.
+========== h_oe_editab_al ============
+Change the address using the arrow keys and delete key. Press RETURN
+when done. Press ^C to cancel editing and leave the address as it was.
+========== h_dir_comp_search ===============
+Type a string to look for just like you would in the composer. Your configured
+rules for the servers with the implicit flag set will be used.
+========== h_oe_searchab ===============
+Type the word or name you want to search for and press RETURN. If you press
+RETURN without entering anything the word in [] will be searched for.
+========== h_oe_chooseabook ==========
+Choose the address book you want to save the new entry in.
+Use ^N or ^P to change address books. ^C to cancel.
+========== h_oe_takeaddr ==========
+Edit the e-mail address using the arrow and delete keys. Press RETURN
+when done. Press ^C to cancel adding this entry to the address book.
+========== h_oe_take_replace ==========
+Press R to replace the old entry with this new data. You will still have
+another chance to cancel. N to enter another nickname. ^C to cancel now.
+========== h_oe_take_replace_or_add ==========
+Press R to replace the old entry. Press A to add the selected addresses to
+the old existing list. N to enter another nickname. ^C to cancel now.
+========== h_oe_takename ==========
+Edit the full name to be correct using the arrow and delete keys. Press RETURN
+when done. Press ^C to cancel adding this entry to the address book.
+========== h_oe_takenick ==========
+Type a nickname (short easy-to-remember name, initials or single word) for this
+entry in the address book and press RETURN. Press ^C to cancel addition.
+========== h_oe_jump ==========
+Type the message number you want to jump to and press RETURN. The word "end"
+represents the last message. Press ^C to cancel jumping to another message.
+========== h_oe_jump_thd ==========
+Type the thread number you want to jump to and press RETURN. The word "end"
+represents the last thread. Press ^C to cancel jumping to another thread.
+========== h_oe_debuglevel ==========
+Higher number shows more debugging details.
+Press ^C if you want to cancel the change.
+========== h_oe_broach ==========
+Type the name of the folder you want to open and press RETURN. Press ^P/^N
+to go to the previous/next collections in the list. Press ^C to cancel goto.
+========== h_oe_foldsearch ==========
+Type the text you want to search for in foldernames and press RETURN. If you
+press RETURN without entering anything, any text in [] will be searched for.
+========== h_oe_foldrename ==========
+Change the old name of the folder to the new name using the arrow and
+delete keys and press RETURN. Press ^C to cancel rename.
+========== h_oe_login ==========
+Enter your login name for the host you are opening the mailbox on. Just press
+RETURN to use your login from this host as is, or edit it with delete key.
+========== h_oe_passwd ==========
+Type your password for the host and login shown as part of the prompt.
+Press ^C to cancel opening folder.
+========== h_oe_choosep ==========
+Enter the number associated with the printer you want to select. Press ^C to
+cancel the printer selection. The current selection is highlighted.
+========== h_oe_customp ==========
+Type the name of the Unix print command and press RETURN. Press ^C to
+cancel the printer selection.
+========== h_oe_searchview ==========
+Type the word or name you want to search for and press RETURN. If you press
+RETURN without entering anything the word in [] will be searched for.
+========== h_oe_keylock ==========
+The keyboard is in use and locked by another user. Only that user can
+unlock this keyboard by typing the password.
+========== h_wt_expire ==========
+At the beginning of each month Alpine offers to rename your current sent-mail
+folder to one named for the month so you have a sent-mail folder for each month
+========== h_wt_delete_old ==========
+It is the beginning of the month, and we need to conserve disk
+space. Please delete any sent-mail that you do not need.
+========== h_select_sort ==========
+Select the order for sorting the index by typing the capitalized letter.
+Arrival is by arrival in your mailbox; Date is by time/day message was sent.
+========== h_no_F_arg ============
+Enter name of file to be opened.
+
+========== h_sticky_personal_name ==========
+Type in your name as you want it to appear on outgoing email. This entry
+will be saved into your Alpine configuration file.
+========== h_sticky_inbox ============
+INBOX syntax is usually {complete.machine.name}INBOX
+This entry will be saved in your Alpine configuration file.
+========== h_sticky_smtp ============
+The name of the computer on your campus that relays your outgoing email
+to the Internet. This entry will be saved in your Alpine configuration file.
+========== h_sticky_user_id ==========
+The username or login-id part of your email address. This entry will be
+saved in your Alpine configuration file.
+========== h_sticky_domain ==========
+The domain part of your email address, NOT the name of your PC. This
+entry will be saved in your Alpine configuration file.
+========== h_bounce =========
+Enter the address or nickname of the intended recipient. Alpine will resend
+the message, which will retain the original author's From: address.
+========== h_incoming_add_folder_nickname =========
+Enter an (optional) nickname that will be used in lieu of the actual
+host and folder names in the FOLDER LIST display.
+========== h_anon_forward ==========
+Enter the address of your intended recipient, or ^C to cancel.
+Example: jsmith@somewhere.edu
+========== h_news_subscribe ==========
+Enter the name of the newsgroup to which you wish to subscribe,
+or ^C to cancel. Example: comp.mail.pine
+========== h_pipe_msg ==========
+Enter the name of the Unix command to which you wish to send this
+message, or ^C to cancel.
+========== h_pipe_attach ==========
+Enter the name of the Unix command to which you wish to send this
+attachment, or ^C to cancel.
+========== h_select_by_num ==========
+Enter a list of message numbers (or number ranges), or ^C to cancel. The word
+"end" represents the last message. Example: 2-5,7-9,11,19,35-end
+========== h_select_by_thrdnum ==========
+Enter a list of thread numbers (or number ranges), or ^C to cancel. The word
+"end" represents the last thread. Example: 2-5,7-9,11,19,35-end
+========== h_select_txt_from ==========
+Messages with From: headers containing the entered string will be selected.
+^C to cancel. ^G again to see original options.
+========== h_select_txt_not_from ==========
+Messages without From: headers containing the entered string will be selected.
+^C to cancel. ^G again to see original options.
+========== h_select_txt_to ==========
+Messages with To: headers containing the entered string will be selected.
+^C to cancel. ^G again to see original options.
+========== h_select_txt_not_to ==========
+Messages without To: headers containing the entered string will be selected.
+^C to cancel. ^G again to see original options.
+========== h_select_txt_cc ==========
+Messages with Cc: headers containing the entered string will be selected.
+^C to cancel. ^G again to see original options.
+========== h_select_txt_not_cc ==========
+Messages without Cc: headers containing the entered string will be selected.
+^C to cancel. ^G again to see original options.
+========== h_select_txt_subj ==========
+Messages with Subject: headers containing the entered string will be selected.
+^C to cancel. ^X enters Subject: line of current message.
+========== h_select_txt_not_subj ==========
+Messages without Subject headers containing the entered string will be selected.
+^C to cancel. ^X enters Subject: line of current message.
+========== h_select_txt_all ==========
+All messages containing the entered string will be selected. Headers and body,
+but not encoded attachments, will be compared. Enter ^C to cancel.
+========== h_select_txt_not_all ==========
+All messages that don't contain the entered string will be selected. Headers
+and body, but not encoded attachments, will be compared. Enter ^C to cancel.
+========== h_select_txt_body ==========
+All messages containing the entered string will be selected. Body text, but
+not headers or encoded attachments, will be compared. ^C to cancel.
+========== h_select_txt_not_body ==========
+All messages that don't contain the entered string will be selected. Body
+text, but not headers or encoded attachments, will be compared. ^C to cancel.
+========== h_select_txt_recip ==========
+Messages with Cc: or To: headers containing the entered string will be selected.
+^C to cancel. ^G again to see original options.
+========== h_select_txt_not_recip ==========
+Messages without Cc: or To: headers containing the string will be selected.
+^C to cancel. ^G again to see original options.
+========== h_select_txt_partic ==========
+Messages with Cc, To, or From headers containing the string will be selected.
+^C to cancel. ^G again to see original options.
+========== h_select_txt_not_partic ==========
+Messages without Cc, To, or From headers containing the string will be selected.
+^C to cancel. ^G again to see original options.
+========== h_select_date ==========
+If typed, date may be in DD-MMM-YYYY format (04-Jul-2006) or in ISO format
+(2006-07-04). ^P/^N also changes default date. ^X enters date of current msg.
+========== h_attach_index_whereis ==========
+Enter some text that appears in the Attachment Index entry for the desired
+attachment. The first attachment containing that text will be highlighted.
+========== h_kb_lock ==========
+Keystrokes entered here (up to a RETURN) comprise a password that must
+be entered again later in order to unlock the keyboard.
+========== h_compose_default ==========
+N, compose a new message. R, set a role.
+^C to cancel.
+========== h_untranslatable ==========
+Send using UTF-8 character set; Send but replace untranslatable characters
+with question marks; return to the composer; or cancel message altogether.
+========== h_compose_intrptd ==========
+N, compose a new msg. I, continue interrupted msg. R, set a role.
+^C to cancel.
+========== h_compose_postponed ==========
+N, compose a new message. P, continue postponed msg. R, set a role.
+^C to cancel.
+========== h_compose_intrptd_postponed ==========
+N, compose a new msg. I, continue interrupted msg. P, continue postponed msg.
+R, set a role. ^C to cancel.
+========== h_compose_form ==========
+N, compose a new message. F, use form letter. R, set a role.
+^C to cancel.
+========== h_compose_intrptd_form ==========
+N, compose a new msg. I, continue interrupted msg. F, use form letter.
+R, set a role. ^C to cancel.
+========== h_compose_postponed_form ==========
+N, compose a new message. P, continue postponed msg. F, use form letter.
+R, set a role. ^C to cancel.
+========== h_compose_intrptd_postponed_form ==========
+N, compose a new msg. I, continue interrupted msg. P, continue postponed msg.
+F, use form letter. R, set a role. ^C to cancel.
+========== h_config_context_del_except ==========
+If you delete the last exceptional collection you can only add it back by
+manually editing the exceptions config file.
+========== h_config_whereis ==========
+To move quickly to a particular line, enter a search string or
+^C to cancel.
+========== h_config_edit_scorei ==========
+Enter interval in the form (min,max). -INF and INF may be used to represent
+-infinity and infinity. ^C to cancel change. RETURN to accept change.
+========== h_config_add ==========
+Enter desired value; use normal editing keys to modify (e.g. ^K, ^D). Just
+pressing RETURN sets the Empty Value (this turns off any global default).
+========== h_config_add_custom_color ==========
+Enter a header fieldname. For example, "subject" or "from".
+
+========== h_config_add_pat_hdr ==========
+Enter a header fieldname. For example, "reply-to" or "organization" or
+any fieldname you want that isn't included already.
+========== h_config_print_opt_choice ==========
+You may edit either the initialization string (characters printed before
+printing starts) or the trailer string. Choose one or ^C to cancel.
+========== h_config_print_init ==========
+Enter a C-style string for this. You may use common backslash escapes like
+\\n for newline, \\ooo for octal character, and \\xhh for hex character.
+========== h_config_change ==========
+Edit the existing value using arrow keys, ^K to delete entire entry, ^D to
+delete current (highlighted) character, etc. Enter ^C to cancel change.
+========== h_config_replace_add ==========
+Replace ignores the current default, Add places the current default in your
+editing buffer as if you had typed it in.
+========== h_config_insert_after ==========
+Enter a nickname for this print command. (InsertBefore puts the new item
+before the current line, InsertAfter puts it after the current line.)
+========== h_config_print_cmd ==========
+Enter command to be executed for the printer. Use normal editing keys
+to modify, ^C to cancel, carriage return to accept current value.
+========== h_config_role_del ==========
+Answering Yes will remove this rule completely from your rules list.
+========== h_config_role_addfile ==========
+Type the name of a file to add to your configuration. You don't need to
+use a file, you may add rules directly (with Add) without using a file.
+========== h_config_role_delfile ==========
+Answering Yes will remove this rule file completely from your rules list.
+The rules data file itself will not be removed.
+========== h_config_print_del ==========
+Answering Yes will remove this printer completely from your printer list.
+========== h_config_print_name_cmd ==========
+You may edit the Nickname of this printer, the Command to be executed when
+printing, or change the Options associated with this printer.
+========== h_send_check_fcc ==========
+Yes, send message without an Fcc.
+No, return to composer.
+========== h_send_check_subj ==========
+Yes, send message without a Subject.
+No, return to composer.
+========== h_send_check_to_cc ==========
+Yes, send message without a To address, or a Cc address, or a Newsgroup.
+No, return to composer.
+========== h_send_fcc_only ==========
+Yes, copy message to Fcc only and send to NO recipients.
+No, return to composer.
+========== h_send_prompt ==========
+Yes, send the message.
+No or ^C, return to composer.
+========== h_send_prompt_flowed ==========
+Yes, send the message. No or ^C, return to composer.
+What's Flowed? See Do Not Send Flowed Text in config screen.
+========== h_send_prompt_dsn ==========
+Yes, send the message. No or ^C, return to composer.
+What's DSNOpts? See Enable Delivery Status Notification in config screen.
+========== h_send_prompt_dsn_flowed ==========
+Yes, send the message. No or ^C, return to composer. What's DSNOpts? See
+Enable Delivery Status Notification. What's Flowed? See Do Not Send Flowed Text.
+========== h_role_confirm ==========
+Yes, use displayed role. No, compose without a role.
+^C, cancel the message. ^T, select a role from your other eligible roles.
+========== h_norole_confirm ==========
+Return, compose without a role.
+^C, cancel the message. ^T, select a role from your eligible roles.
+========== h_custom_print ==========
+Enter a Unix command that accepts its data on standard input.
+Alpine will display any information the command sends to standard output.
+========== h_convert_abooks_and_sigs ==========
+You will be given the opportunity to convert address books and signature files
+to remote configurations.
+========== h_convert_abooks ==========
+You will be given the opportunity to convert address books to remote
+configurations.
+========== h_flag_keyword ==========
+Enter the name of the keyword you want to add for this folder.
+No spaces, parentheses, braces, percents or asterisks are allowed.
+========== h_select_keyword ==========
+Enter the keyword you want to match, or use ^T to select a keyword from a list
+of possible keywords for this folder. Use ! to look for non-matches instead.
+========== h_type_keyword ==========
+Enter the keyword you want to add. You may add a nickname in the next step.
+No spaces, parentheses, braces, percents or asterisks are allowed.
+========== h_type_keyword_nickname ==========
+Enter an optional nickname for the keyword you want to add.
+Type Carriage return to use the keyword name instead of a nickname.
+========== h_convert_sigs ==========
+You will be given the opportunity to convert signature files to remote
+configurations.
+========== h_convert_abook ==========
+Yes is fairly safe. You will be ADDing a remote address book that is a copy
+of the current address book. The current abook won't be removed automatically.
+========== h_convert_sig ==========
+Answering Yes copies the contents of the signature file into your Alpine
+configuration file. After that, the contents of the file will not be used.
+========== h_save_addman ==========
+Enter the simple name of the folder you want to add. Carriage return to
+accept what you have typed so far. ^C to get back to SELECT FOLDER screen.
+========== h_reopen_folder ==========
+Yes reopens the folder, as if you were starting over. This uncovers new mail.
+No leaves the folder index as it was without discovering new mail.
+========== h_convert_pinerc_server ==========
+This is the name of the host (computer) where the remote Alpine configuration
+will be stored. This should be an IMAP server that you have permission to use.
+========== h_convert_pinerc_folder ==========
+Enter the correct remote folder name. This folder is special and should
+contain only configuration data. It shouldn't contain other mail messages.
+========== h_role_compose ==========
+Compose a New message, Reply to current message, Forward current message, or
+Bounce message. Then you will be asked to choose one of your Roles to be used.
+========== h_save_size_changed ==========
+The reported size of a message is not the same as the actual size. Answer Yes
+to continue and hope for the best or No to Cancel the entire Save.
+========== h_select_by_larger_size ==========
+Enter a number or ^C to cancel. All messages greater than this many characters
+in size will be selected. Examples: 2176, 1.53K (1530), or 3M (3000000).
+========== h_select_by_smaller_size ==========
+Enter a number or ^C to cancel. All messages less than this many characters
+in size will be selected. Examples: 2176, 1.53K (1530), or 3M (3000000).
diff --git a/pith/pineelt.h b/pith/pineelt.h
new file mode 100644
index 00000000..e44ae37a
--- /dev/null
+++ b/pith/pineelt.h
@@ -0,0 +1,60 @@
+/*
+ * $Id: pineelt.h 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_PINEELT_INCLUDED
+#define PITH_PINEELT_INCLUDED
+
+
+#include "../pith/thread.h"
+#include "../pith/indxtype.h"
+
+
+/*
+ * The two structs below hold all knowledge regarding
+ * messages requiring special handling
+ */
+typedef struct part_exception_s {
+ char *partno;
+ int handling;
+ struct part_exception_s *next;
+} PARTEX_S;
+
+
+/*
+ * Pine's private per-message data stored on the c-client's elt
+ * spare pointer.
+ */
+typedef struct pine_elt {
+ PINETHRD_S *pthrd;
+ PARTEX_S *exceptions;
+ ICE_S *ice;
+ /* per-message pine state bits */
+ unsigned int hidden:1;
+ unsigned int excluded:1;
+ unsigned int selected:1;
+ unsigned int searched:1;
+ unsigned int unsorted:1;
+ unsigned int collapsed:1;
+ unsigned int colhid:1;
+ unsigned int colhid2:1;
+ unsigned int tmp:1;
+} PINELT_S;
+
+
+/* exported protoypes */
+void msgno_free_exceptions(PARTEX_S **);
+
+
+#endif /* PITH_PINEELT_INCLUDED */
diff --git a/pith/pipe.c b/pith/pipe.c
new file mode 100644
index 00000000..35549eef
--- /dev/null
+++ b/pith/pipe.c
@@ -0,0 +1,102 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: pipe.c 761 2007-10-23 22:35:18Z hubert@u.washington.edu $";
+#endif
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/pipe.h"
+#include "../pith/status.h"
+
+
+/* Internal prototypes */
+
+
+
+/*
+ * pipe_* functions introduced so out.f, in.f don't need to be used
+ * by whatever's setting up the pipe.
+ * Should be similar on unix and windows, but since we're faking piping
+ * in windows, closing the write pipe would signify that the child
+ * process can start running. This makes it possible for PIPE_WRITE
+ * and PIPE_READ flags to be simultaneously set across all platforms.
+ * Unix piping should eventually have NON_BLOCKING_IO stuff rolled up
+ * into it.
+ */
+
+int
+pipe_putc(int c, PIPE_S *syspipe)
+{
+ if(!syspipe || !syspipe->out.f)
+ return -1;
+
+ return(fputc(c, syspipe->out.f));
+}
+
+int
+pipe_puts(char *str, PIPE_S *syspipe)
+{
+ if(!syspipe || !syspipe->out.f)
+ return -1;
+
+ return(fputs(str, syspipe->out.f));
+}
+
+char *
+pipe_gets(char *str, int size, PIPE_S *syspipe)
+{
+ if(!syspipe || !syspipe->in.f)
+ return NULL;
+
+ return(fgets(str, size, syspipe->in.f));
+}
+
+int
+pipe_readc(unsigned char *c, PIPE_S *syspipe)
+{
+ int rv = 0;
+
+ if(!syspipe || !syspipe->in.f)
+ return -1;
+
+ do {
+ errno = 0;
+ clearerr(syspipe->in.f);
+ rv = fread(c, sizeof(unsigned char), (size_t)1, syspipe->in.f);
+ } while(!rv && ferror(syspipe->in.f) && errno == EINTR);
+
+ return(rv);
+}
+
+int
+pipe_writec(int c, PIPE_S *syspipe)
+{
+ unsigned char ch = (unsigned char)c;
+ int rv = 0;
+
+ if(!syspipe || !syspipe->out.f)
+ return -1;
+
+ do
+ rv = fwrite(&ch, sizeof(unsigned char), (size_t)1, syspipe->out.f);
+ while(!rv && ferror(syspipe->out.f) && errno == EINTR);
+
+ return(rv);
+}
+
+
+void
+pipe_report_error(char *errormsg)
+{
+ q_status_message(SM_ORDER, 3, 3, errormsg);
+}
diff --git a/pith/pipe.h b/pith/pipe.h
new file mode 100644
index 00000000..f4a7f64f
--- /dev/null
+++ b/pith/pipe.h
@@ -0,0 +1,33 @@
+/*
+ * $Id: pipe.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_PIPE_INCLUDED
+#define PITH_PIPE_INCLUDED
+
+
+#include "../pith/filter.h"
+#include "../pith/osdep/pipe.h"
+
+
+/* exported protoypes */
+int pipe_putc(int, PIPE_S *);
+int pipe_puts(char *, PIPE_S *);
+char *pipe_gets(char *, int, PIPE_S *);
+int pipe_readc(unsigned char *, PIPE_S *);
+int pipe_writec(int, PIPE_S *);
+void pipe_report_error(char *);
+
+
+#endif /* PITH_PIPE_INCLUDED */
diff --git a/pith/readfile.c b/pith/readfile.c
new file mode 100644
index 00000000..4fc9fa3e
--- /dev/null
+++ b/pith/readfile.c
@@ -0,0 +1,71 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: readfile.c 761 2007-10-23 22:35:18Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+
+#include "../pith/store.h"
+
+#include "readfile.h"
+
+
+/*----------------------------------------------------------------------
+ Read whole file into memory
+
+ Args: filename -- path name of file to read
+
+ Result: Returns pointer to malloced memory with the contents of the file
+ or NULL
+
+This won't work very well if the file has NULLs in it.
+ ----*/
+char *
+read_file(char *filename, int so_get_flags)
+{
+ STORE_S *in_file = NULL, *out_store = NULL;
+ unsigned char c;
+ char *return_text = NULL;
+
+ if((in_file = so_get(FileStar, filename, so_get_flags | READ_ACCESS))){
+
+
+ if(!(out_store = so_get(CharStar, NULL, EDIT_ACCESS))){
+ so_give(&in_file);
+ return NULL;
+ }
+
+ /*
+ * We're just using the READ_FROM_LOCALE flag to translate
+ * to UTF-8.
+ */
+ while(so_readc(&c, in_file))
+ so_writec(c, out_store);
+
+ if(in_file)
+ so_give(&in_file);
+
+ if(out_store){
+ return_text = (char *) so_text(out_store);
+ /* avoid freeing this */
+ if(out_store->txt)
+ out_store->txt = NULL;
+
+ so_give(&out_store);
+ }
+ }
+
+ return(return_text);
+}
diff --git a/pith/readfile.h b/pith/readfile.h
new file mode 100644
index 00000000..485144be
--- /dev/null
+++ b/pith/readfile.h
@@ -0,0 +1,35 @@
+/*
+ * $Id: readfile.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_READFILE_INCLUDED
+#define PITH_READFILE_INCLUDED
+
+
+/*
+ * Does the string s begin with the UTF-8 Byte Order Mark?
+ */
+#define BOM_UTF8(s) ((s) && (s)[0] && (s)[1] && (s)[2] \
+ && (unsigned char)(s)[0] == 0xef \
+ && (unsigned char)(s)[1] == 0xbb \
+ && (unsigned char)(s)[2] == 0xbf)
+
+
+/*
+ * Exported Prototypes
+ */
+char *read_file(char *, int);
+
+
+#endif /* PITH_READFILE_INCLUDED */
diff --git a/pith/remote.c b/pith/remote.c
new file mode 100644
index 00000000..40a7f055
--- /dev/null
+++ b/pith/remote.c
@@ -0,0 +1,2820 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: remote.c 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ remote.c
+ Implements remote IMAP config files (remote config, remote abook).
+ ====*/
+
+
+#include "../pith/headers.h"
+#include "../pith/remote.h"
+#include "../pith/conf.h"
+#include "../pith/imap.h"
+#include "../pith/msgno.h"
+#include "../pith/mailview.h"
+#include "../pith/status.h"
+#include "../pith/flag.h"
+#include "../pith/tempfile.h"
+#include "../pith/adrbklib.h"
+#include "../pith/detach.h"
+#include "../pith/filter.h"
+#include "../pith/stream.h"
+#include "../pith/options.h"
+#include "../pith/busy.h"
+#include "../pith/readfile.h"
+
+
+/*
+ * Internal prototypes
+ */
+REMDATA_META_S *rd_find_our_metadata(char *, unsigned long *);
+int rd_meta_is_broken(FILE *);
+int rd_add_hdr_msg(REMDATA_S *, char *);
+int rd_store_fake_hdrs(REMDATA_S *, char *, char *, char *);
+int rd_upgrade_cookies(REMDATA_S *, long, int);
+int rd_check_for_suspect_data(REMDATA_S *);
+
+
+char meta_prefix[] = ".ab";
+
+
+char *(*pith_opt_rd_metadata_name)(void);
+
+
+char *
+read_remote_pinerc(PINERC_S *prc, ParsePinerc which_vars)
+{
+ int try_cache, no_perm_create_pass = 0;
+ char *file = NULL;
+ unsigned flags;
+
+
+ dprint((7, "read_remote_pinerc \"%s\"\n",
+ prc->name ? prc->name : "?"));
+
+ /*
+ * We don't cache the pinerc, we always copy it.
+ *
+ * Don't store the config in a temporary file, just leave it
+ * in memory while using it.
+ * It is currently required that NO_PERM_CACHE be set if NO_FILE is set.
+ */
+ flags = (NO_PERM_CACHE | NO_FILE);
+
+create_the_remote_folder:
+
+ if(no_perm_create_pass){
+ if(prc->rd){
+ prc->rd->flags &= ~DO_REMTRIM;
+ rd_close_remdata(&prc->rd);
+ }
+
+ /* this will cause the remote folder to be created */
+ flags = 0;
+ }
+
+ /*
+ * We could parse the name here to find what type it is. So far we
+ * only have type RemImap.
+ */
+ prc->rd = rd_create_remote(RemImap, prc->name,
+ REMOTE_PINERC_SUBTYPE,
+ &flags, _("Error: "),
+ _(" Can't fetch remote configuration."));
+ if(!prc->rd)
+ goto bail_out;
+
+ /*
+ * On first use we just use a temp file instead of memory (NO_FILE).
+ * In other words, for our convenience, we don't turn NO_FILE back on
+ * here. Why is that convenient? Because of the stuff that happened in
+ * rd_create_remote when flags was set to zero.
+ */
+ if(no_perm_create_pass)
+ prc->rd->flags |= NO_PERM_CACHE;
+
+ try_cache = rd_read_metadata(prc->rd);
+
+ if(prc->rd->access == MaybeRorW){
+ if(prc->rd->read_status == 'R' ||
+ !(which_vars == ParsePers || which_vars == ParsePersPost)){
+ prc->rd->access = ReadOnly;
+ prc->rd->read_status = 'R';
+ }
+ else
+ prc->rd->access = ReadWrite;
+ }
+
+ if(prc->rd->access != NoExists){
+
+ rd_check_remvalid(prc->rd, 1L);
+
+ /*
+ * If the cached info says it is readonly but
+ * it looks like it's been fixed now, change it to readwrite.
+ */
+ if((which_vars == ParsePers || which_vars == ParsePersPost) &&
+ prc->rd->read_status == 'R'){
+ /*
+ * We go to this trouble since readonly pinercs
+ * are likely a mistake. They are usually supposed to be
+ * readwrite so we open it and check if it's been fixed.
+ */
+ rd_check_readonly_access(prc->rd);
+ if(prc->rd->read_status == 'W'){
+ prc->rd->access = ReadWrite;
+ prc->rd->flags |= REM_OUTOFDATE;
+ }
+ else
+ prc->rd->access = ReadOnly;
+ }
+
+ if(prc->rd->flags & REM_OUTOFDATE){
+ if(rd_update_local(prc->rd) != 0){
+ if(!no_perm_create_pass && prc->rd->flags & NO_PERM_CACHE
+ && !(prc->rd->flags & USER_SAID_NO)){
+ /*
+ * We don't check for the existence of the remote
+ * folder when this flag is turned on, so we could
+ * fail here because the remote folder doesn't exist.
+ * We try to create it.
+ */
+ no_perm_create_pass++;
+ goto create_the_remote_folder;
+ }
+
+ dprint((1,
+ "read_pinerc_remote: rd_update_local failed\n"));
+ /*
+ * Don't give up altogether. We still may be
+ * able to use a cached copy.
+ */
+ }
+ else{
+ dprint((7,
+ "%s: copied remote to local (%ld)\n",
+ prc->rd->rn ? prc->rd->rn : "?",
+ (long)prc->rd->last_use));
+ }
+ }
+
+ if(prc->rd->access == ReadWrite)
+ prc->rd->flags |= DO_REMTRIM;
+ }
+
+ /* If we couldn't get to remote folder, try using the cached copy */
+ if(prc->rd->access == NoExists || prc->rd->flags & REM_OUTOFDATE){
+ if(try_cache){
+ prc->rd->access = ReadOnly;
+ prc->rd->flags |= USE_OLD_CACHE;
+ q_status_message(SM_ORDER, 3, 4,
+ "Can't contact remote config server, using cached copy");
+ dprint((2,
+ "Can't open remote pinerc %s, using local cached copy %s readonly\n",
+ prc->rd->rn ? prc->rd->rn : "?",
+ prc->rd->lf ? prc->rd->lf : "?"));
+ }
+ else{
+ prc->rd->flags &= ~DO_REMTRIM;
+ goto bail_out;
+ }
+ }
+
+ if(prc->rd->flags & NO_FILE)
+ /* copy text, leave sonofile for later use */
+ file = cpystr((char *)so_text(prc->rd->sonofile));
+ else
+ file = read_file(prc->rd->lf, 0);
+
+bail_out:
+ if((which_vars == ParsePers || which_vars == ParsePersPost) &&
+ (!file || !prc->rd || prc->rd->access != ReadWrite)){
+ prc->readonly = 1;
+ if(prc == ps_global->prc)
+ ps_global->readonly_pinerc = 1;
+ }
+
+ return(file);
+}
+
+
+/*
+ * Check if the remote data folder exists and create an empty folder
+ * if it doesn't exist.
+ *
+ * Args - type -- The type of remote storage.
+ * remote_name --
+ * type_spec -- Type-specific data.
+ * flags --
+ * err_prefix -- Should usually end with a SPACE
+ * err_suffix -- Should usually begin with a SPACE
+ *
+ * Returns a pointer to a REMDATA_S with access set to either
+ * NoExists or MaybeRorW. On success, "so" will point to a storage object.
+ */
+REMDATA_S *
+rd_create_remote(RemType type, char *remote_name, char *type_spec,
+ unsigned int *flags, char *err_prefix, char *err_suffix)
+{
+ REMDATA_S *rd = NULL;
+ CONTEXT_S *dummy_cntxt = NULL;
+
+ dprint((7, "rd_create_remote \"%s\"\n",
+ remote_name ? remote_name : "?"));
+
+ rd = rd_new_remdata(type, remote_name, type_spec);
+ if(flags)
+ rd->flags = *flags;
+
+ switch(rd->type){
+ case RemImap:
+ if(rd->flags & NO_PERM_CACHE){
+ if(rd->rn && (rd->so = so_get(CharStar, NULL, WRITE_ACCESS))){
+ if(rd->flags & NO_FILE){
+ rd->sonofile = so_get(CharStar, NULL, WRITE_ACCESS);
+ if(!rd->sonofile)
+ rd->flags &= ~NO_FILE;
+ }
+
+ /*
+ * We're not going to check if it is there in this case,
+ * in order to save ourselves some round trips and
+ * connections. We'll just try to select it and then
+ * recover at that point if it isn't already there.
+ */
+ rd->flags |= REM_OUTOFDATE;
+ rd->access = MaybeRorW;
+ }
+
+ }
+ else{
+ /*
+ * Open_fcc creates the folder if it didn't already exist.
+ */
+ if(rd->rn && (rd->so = open_fcc(rd->rn, &dummy_cntxt, 1,
+ err_prefix, err_suffix)) != NULL){
+ /*
+ * We know the folder is there but don't know what access
+ * rights we have until we try to select it, which we don't
+ * want to do unless we have to. So delay evaluating.
+ */
+ rd->access = MaybeRorW;
+ }
+ }
+
+ break;
+
+ default:
+ q_status_message(SM_ORDER, 3,5, "rd_create_remote: type not supported");
+ break;
+ }
+
+ return(rd);
+}
+
+
+REMDATA_S *
+rd_new_remdata(RemType type, char *remote_name, char *type_spec)
+{
+ REMDATA_S *rd = NULL;
+
+ rd = (REMDATA_S *)fs_get(sizeof(*rd));
+ memset((void *)rd, 0, sizeof(*rd));
+
+ rd->type = type;
+ rd->access = NoExists;
+
+ if(remote_name)
+ rd->rn = cpystr(remote_name);
+
+ switch(rd->type){
+ case RemImap:
+ if(type_spec)
+ rd->t.i.special_hdr = cpystr(type_spec);
+
+ break;
+
+ default:
+ q_status_message(SM_ORDER, 3,5, "rd_new_remdata: type not supported");
+ break;
+ }
+
+ return(rd);
+}
+
+
+/*
+ * Closes the remote stream and frees.
+ */
+void
+rd_free_remdata(REMDATA_S **rd)
+{
+ if(rd && *rd){
+ rd_close_remote(*rd);
+
+ if((*rd)->rn)
+ fs_give((void **)&(*rd)->rn);
+
+ if((*rd)->lf)
+ fs_give((void **)&(*rd)->lf);
+
+ if((*rd)->so){
+ so_give(&(*rd)->so);
+ (*rd)->so = NULL;
+ }
+
+ if((*rd)->sonofile){
+ so_give(&(*rd)->sonofile);
+ (*rd)->sonofile = NULL;
+ }
+
+ switch((*rd)->type){
+ case RemImap:
+ if((*rd)->t.i.special_hdr)
+ fs_give((void **)&(*rd)->t.i.special_hdr);
+
+ if((*rd)->t.i.chk_date)
+ fs_give((void **)&(*rd)->t.i.chk_date);
+
+ break;
+
+ default:
+ q_status_message(SM_ORDER, 3, 5,
+ "rd_free_remdata: type not supported");
+ break;
+ }
+
+ fs_give((void **)rd);
+ }
+}
+
+
+/*
+ * Call this when finished with the remdata. This does the REMTRIM if the
+ * flag is set, the DEL_FILE if the flag is set. It also closes the stream
+ * and frees the rd.
+ */
+void
+rd_trim_remdata(REMDATA_S **rd)
+{
+ if(!(rd && *rd))
+ return;
+
+ switch((*rd)->type){
+ case RemImap:
+ /*
+ * Trim the number of saved copies of remote data history.
+ * The first message is a fake message that always
+ * stays there, then come ps_global->remote_abook_history messages
+ * which are each a revision of the data, then comes the active
+ * data.
+ */
+ if((*rd)->flags & DO_REMTRIM &&
+ !((*rd)->flags & REM_OUTOFDATE) &&
+ (*rd)->t.i.chk_nmsgs > ps_global->remote_abook_history + 2){
+
+ /* make sure stream is open */
+ rd_ping_stream(*rd);
+ rd_open_remote(*rd);
+
+ if(!rd_remote_is_readonly(*rd)){
+ if((*rd)->t.i.stream &&
+ (*rd)->t.i.stream->nmsgs >
+ ps_global->remote_abook_history + 2){
+ char sequence[20];
+ int user_deleted = 0;
+
+ /*
+ * If user manually deleted some, we'd better not delete
+ * any more.
+ */
+ if(count_flagged((*rd)->t.i.stream, F_DEL) == 0L){
+
+ dprint((4, " rd_trim: trimming remote: mark msgs 2-%ld deleted (%s)\n", (*rd)->t.i.stream->nmsgs - 1 - ps_global->remote_abook_history, (*rd)->rn ? (*rd)->rn : "?"));
+ snprintf(sequence, sizeof(sequence), "2:%ld",
+ (*rd)->t.i.stream->nmsgs - 1 - ps_global->remote_abook_history);
+ mail_flag((*rd)->t.i.stream, sequence,
+ "\\DELETED", ST_SET);
+ }
+ else
+ user_deleted++;
+
+ mail_expunge((*rd)->t.i.stream);
+
+ if(!user_deleted)
+ rd_update_metadata(*rd, NULL);
+ /* else
+ * don't update metafile because user is messing with
+ * the remote folder manually. We'd better re-read it next
+ * time. */
+ }
+ }
+
+ ps_global->noshow_error = 0;
+ }
+
+ break;
+
+ default:
+ q_status_message(SM_ORDER, 3,5, "rd_trim_remdata: type not supported");
+ break;
+ }
+}
+
+
+/*
+ * All done with this remote data. Trim the folder, close the
+ * stream, and free.
+ */
+void
+rd_close_remdata(REMDATA_S **rd)
+{
+ if(!(rd && *rd))
+ return;
+
+ rd_trim_remdata(rd);
+
+ if((*rd)->lf && (*rd)->flags & DEL_FILE)
+ our_unlink((*rd)->lf);
+
+ /* this closes the stream and frees memory */
+ rd_free_remdata(rd);
+}
+
+
+/*
+ * Looks in the metadata file for the cache line corresponding to rd and
+ * fills in data in rd.
+ *
+ * Return value -- 1 if it is likely that the filename we're returning
+ * is the permanent name of the local cache file and it may already have
+ * a cached copy of the data. This is to tell us if it makes sense to use
+ * the cached copy when we are unable to contact the remote server.
+ * 0 otherwise.
+ */
+int
+rd_read_metadata(REMDATA_S *rd)
+{
+ REMDATA_META_S *rab = NULL;
+ int try_cache = 0;
+
+ dprint((7, "rd_read_metadata \"%s\"\n",
+ (rd && rd->rn) ? rd->rn : "?"));
+
+ if(!rd)
+ return try_cache;
+
+ if(rd->flags & NO_PERM_CACHE)
+ rab = NULL;
+ else
+ rab = rd_find_our_metadata(rd->rn, &rd->flags);
+
+ if(!rab){
+ if(!rd->lf){
+ rd->flags |= (NO_META_UPDATE | REM_OUTOFDATE);
+ if(!(rd->flags & NO_FILE)){
+ rd->lf = temp_nam(NULL, "a6");
+ rd->flags |= DEL_FILE;
+ }
+
+ /* display error */
+ if(!(rd->flags & NO_PERM_CACHE))
+ display_message('x');
+
+ dprint((2, "using temp cache file %s\n",
+ rd->lf ? rd->lf : "<none>"));
+ }
+ }
+ else if(rab->local_cache_file){ /* A-OK, it was in the file already */
+ if(!is_absolute_path(rab->local_cache_file)){
+ char dir[MAXPATH+1], path[MAXPATH+1];
+ char *lc;
+
+ /*
+ * This should be the normal case. The file is stored as a
+ * filename in the pinerc dir, so that it can be
+ * accessed from the PC or from unix where the pathnames to
+ * get there will be different.
+ */
+
+ if((lc = last_cmpnt(ps_global->pinerc)) != NULL){
+ int to_copy;
+
+ to_copy = (lc - ps_global->pinerc > 1)
+ ? (lc - ps_global->pinerc - 1) : 1;
+ strncpy(dir, ps_global->pinerc, MIN(to_copy, sizeof(dir)-1));
+ dir[MIN(to_copy, sizeof(dir)-1)] = '\0';
+ }
+ else{
+ dir[0] = '.';
+ dir[1] = '\0';
+ }
+
+ build_path(path, dir, rab->local_cache_file, sizeof(path));
+ rd->lf = cpystr(path);
+ }
+ else{
+ rd->lf = rab->local_cache_file;
+ /* don't free this below, we're using it */
+ rab->local_cache_file = NULL;
+ }
+
+ rd->read_status = rab->read_status;
+
+ switch(rd->type){
+ case RemImap:
+ rd->t.i.chk_date = rab->date;
+ rab->date = NULL; /* don't free this below, we're using it */
+
+ dprint((7,
+ "in read_metadata, setting chk_date from metadata to ->%s<-\n",
+ rd->t.i.chk_date ? rd->t.i.chk_date : "?"));
+ rd->t.i.chk_nmsgs = rab->nmsgs;
+ rd->t.i.uidvalidity = rab->uidvalidity;
+ rd->t.i.uidnext = rab->uidnext;
+ rd->t.i.uid = rab->uid;
+ rd->t.i.chk_nmsgs = rab->nmsgs;
+ dprint((7,
+ "setting uid=%lu uidnext=%lu uidval=%lu read_stat=%c nmsgs=%lu\n",
+ rd->t.i.uid, rd->t.i.uidnext, rd->t.i.uidvalidity,
+ rd->read_status ? rd->read_status : '0',
+ rd->t.i.chk_nmsgs));
+ break;
+
+ default:
+ q_status_message(SM_ORDER, 3, 5,
+ "rd_read_metadata: type not supported");
+ break;
+ }
+
+ if(rd->t.i.chk_nmsgs > 0)
+ try_cache++; /* cache should be valid if we can't contact server */
+ }
+ /*
+ * The line for this data wasn't in the metadata file yet.
+ * Figure out what should go there and put it in.
+ */
+ else{
+ /*
+ * The local_cache_file is where we will store the cached local
+ * copy of the remote data.
+ */
+ rab->local_cache_file = tempfile_in_same_dir(ps_global->pinerc,
+ meta_prefix, NULL);
+ if(rab->local_cache_file){
+ rd->lf = rab->local_cache_file;
+ rd_write_metadata(rd, 0);
+ rab->local_cache_file = NULL;
+ }
+ else
+ rd->lf = temp_nam(NULL, "a7");
+ }
+
+ if(rab){
+ if(rab->local_cache_file)
+ fs_give((void **)&rab->local_cache_file);
+ if(rab->date)
+ fs_give((void **)&rab->date);
+ fs_give((void **)&rab);
+ }
+
+ return(try_cache);
+}
+
+
+/*
+ * Write out the contents of the metadata file.
+ *
+ * Each line should be: folder_name cache_file uidvalidity uidnext uid nmsgs
+ * read_status date
+ *
+ * If delete_it is set, then remove the line instead of updating it.
+ *
+ * We have to be careful with the metadata, because it exists in user's
+ * existing metadata files now, and it is oriented towards only RemImap.
+ * We would have to change the version number and the format of the lines
+ * in the file if we add another type.
+ */
+void
+rd_write_metadata(REMDATA_S *rd, int delete_it)
+{
+ char *tempfile;
+ FILE *fp_old = NULL, *fp_new = NULL;
+ char *p = NULL, *pinerc_dir = NULL, *metafile = NULL;
+ char *rel_filename, *key;
+ char line[MAILTMPLEN];
+ int fd;
+
+ dprint((7, "rd_write_metadata \"%s\"\n",
+ (rd && rd->rn) ? rd->rn : "?"));
+
+ if(!rd || rd->flags & NO_META_UPDATE)
+ return;
+
+ if(rd->type != RemImap){
+ q_status_message(SM_ORDER, 3, 5,
+ "rd_write_metadata: type not supported");
+ return;
+ }
+
+ dprint((9, " - rd_write_metadata: rn=%s lf=%s\n",
+ rd->rn ? rd->rn : "?", rd->lf ? rd->lf : "?"));
+
+ key = rd->rn;
+
+ if(!(pith_opt_rd_metadata_name && (metafile = (*pith_opt_rd_metadata_name)())))
+ goto io_err;
+
+ if(!(tempfile = tempfile_in_same_dir(metafile, "a9", &pinerc_dir)))
+ goto io_err;
+
+ if((fd = our_open(tempfile, O_TRUNC|O_WRONLY|O_CREAT|O_BINARY, 0600)) >= 0)
+ fp_new = fdopen(fd, "w");
+
+ if(pinerc_dir && rd->lf && strlen(rd->lf) > strlen(pinerc_dir))
+ rel_filename = rd->lf + strlen(pinerc_dir) + 1;
+ else
+ rel_filename = rd->lf;
+
+ if(pinerc_dir)
+ fs_give((void **)&pinerc_dir);
+
+ fp_old = our_fopen(metafile, "rb");
+
+ if(fp_new && fp_old){
+ /*
+ * Write the header line.
+ */
+ if(fprintf(fp_new, "%s %s Pine Metadata\n",
+ PMAGIC, METAFILE_VERSION_NUM) == EOF)
+ goto io_err;
+
+ while((p = fgets(line, sizeof(line), fp_old)) != NULL){
+ /*
+ * Skip the header line and any lines that don't begin
+ * with a "{".
+ */
+ if(line[0] != '{')
+ continue;
+
+ /* skip the old cache line for this key */
+ if(strncmp(line, key, strlen(key)) == 0 && line[strlen(key)] == TAB)
+ continue;
+
+ /* add this line to new version of file */
+ if(fputs(p, fp_new) == EOF)
+ goto io_err;
+ }
+ }
+
+ /* add the cache line for this key */
+ /* Warning: this is type RemImap specific right now! */
+ if(!delete_it){
+ if(!tempfile ||
+ !fp_old ||
+ !fp_new ||
+ p ||
+ fprintf(fp_new, "%s\t%s\t%lu\t%lu\t%lu\t%lu\t%c\t%s\n",
+ key ? key : "",
+ rel_filename ? rel_filename : "",
+ rd->t.i.uidvalidity, rd->t.i.uidnext, rd->t.i.uid,
+ rd->t.i.chk_nmsgs,
+ rd->read_status ? rd->read_status : '?',
+ rd->t.i.chk_date ? rd->t.i.chk_date : "no-match")
+ == EOF)
+ goto io_err;
+ }
+
+ if(fclose(fp_new) == EOF){
+ fp_new = NULL;
+ goto io_err;
+ }
+
+ if(fclose(fp_old) == EOF){
+ fp_old = NULL;
+ goto io_err;
+ }
+
+ if(rename_file(tempfile, metafile) < 0)
+ goto io_err;
+
+ if(tempfile)
+ fs_give((void **)&tempfile);
+
+ if(metafile)
+ fs_give((void **)&metafile);
+
+ return;
+
+io_err:
+ dprint((2, "io_err in rd_write_metadata(%s), tempfile=%s: %s\n",
+ metafile ? metafile : "<NULL>", tempfile ? tempfile : "<NULL>",
+ error_description(errno)));
+ q_status_message2(SM_ORDER, 3, 5,
+ "Trouble updating metafile %s, continuing (%s)",
+ metafile ? metafile : "<NULL>", error_description(errno));
+ if(tempfile){
+ our_unlink(tempfile);
+ fs_give((void **)&tempfile);
+ }
+ if(metafile)
+ fs_give((void **)&metafile);
+ if(fp_old)
+ (void)fclose(fp_old);
+ if(fp_new)
+ (void)fclose(fp_new);
+}
+
+
+void
+rd_update_metadata(REMDATA_S *rd, char *date)
+{
+ if(!rd)
+ return;
+
+ dprint((9, " - rd_update_metadata: rn=%s lf=%s\n",
+ rd->rn ? rd->rn : "?", rd->lf ? rd->lf : "<none>"));
+
+ switch(rd->type){
+ case RemImap:
+ if(rd->t.i.stream){
+ ps_global->noshow_error = 1;
+ rd_ping_stream(rd);
+ if(rd->t.i.stream){
+ /*
+ * If nmsgs < 2 then something is wrong. Maybe it is just
+ * that we haven't been told about the messages we've
+ * appended ourselves. Try closing and re-opening the stream
+ * to see those.
+ */
+ if(rd->t.i.stream->nmsgs < 2 ||
+ (rd->t.i.shouldbe_nmsgs &&
+ (rd->t.i.shouldbe_nmsgs != rd->t.i.stream->nmsgs))){
+ pine_mail_check(rd->t.i.stream);
+ if(rd->t.i.stream->nmsgs < 2 ||
+ (rd->t.i.shouldbe_nmsgs &&
+ (rd->t.i.shouldbe_nmsgs != rd->t.i.stream->nmsgs))){
+ rd_close_remote(rd);
+ rd_open_remote(rd);
+ }
+ }
+
+ rd->t.i.chk_nmsgs = rd->t.i.stream ? rd->t.i.stream->nmsgs : 0L;
+
+ /*
+ * If nmsgs < 2 something is wrong.
+ */
+ if(rd->t.i.chk_nmsgs < 2){
+ rd->t.i.uidvalidity = 0L;
+ rd->t.i.uid = 0L;
+ rd->t.i.uidnext = 0L;
+ }
+ else{
+ rd->t.i.uidvalidity = rd->t.i.stream->uid_validity;
+ rd->t.i.uid = mail_uid(rd->t.i.stream,
+ rd->t.i.stream->nmsgs);
+ /*
+ * Uid_last is not always valid. If the last known uid is
+ * greater than uid_last, go with it instead (uid+1).
+ * If our guess is wrong (too low), the penalty is not
+ * harsh. When the uidnexts don't match we open the
+ * mailbox to check the uid of the actual last message.
+ * If it was a false hit then we adjust uidnext so it
+ * will be correct the next time through.
+ */
+ rd->t.i.uidnext = MAX(rd->t.i.stream->uid_last,rd->t.i.uid)
+ + 1;
+ }
+ }
+
+ ps_global->noshow_error = 0;
+
+ /*
+ * Save the date so that we can check if it changed next time
+ * we go to write.
+ */
+ if(date){
+ if(rd->t.i.chk_date)
+ fs_give((void **)&rd->t.i.chk_date);
+
+ rd->t.i.chk_date = cpystr(date);
+ }
+
+ rd_write_metadata(rd, 0);
+ }
+
+ rd->t.i.shouldbe_nmsgs = 0;
+
+ break;
+
+ default:
+ q_status_message(SM_ORDER, 3, 5,
+ "rd_update_metadata: type not supported");
+ break;
+ }
+}
+
+
+
+
+REMDATA_META_S *
+rd_find_our_metadata(char *key, long unsigned int *flags)
+{
+ char *p, *q, *metafile = NULL;
+ char line[MAILTMPLEN];
+ REMDATA_META_S *rab = NULL;
+ FILE *fp;
+
+ dprint((9, "rd_find_our_metadata \"%s\"\n", key ? key : "?"));
+
+ if(!(key && *key))
+ return rab;
+
+ if(!(pith_opt_rd_metadata_name && (metafile = (*pith_opt_rd_metadata_name)()) != NULL))
+ return rab;
+
+ /*
+ * Open the metadata file and get some information out.
+ */
+ fp = our_fopen(metafile, "rb");
+ if(fp == NULL){
+ q_status_message2(SM_ORDER, 3, 5,
+ _("can't open metadata file %s, continuing (%s)"),
+ metafile, error_description(errno));
+ dprint((2,
+ "can't open existing metadata file %s: %s\n",
+ metafile ? metafile : "?", error_description(errno)));
+ if(flags)
+ (*flags) |= NO_META_UPDATE;
+
+ fs_give((void **)&metafile);
+
+ return rab;
+ }
+
+ /*
+ * If we make it to this point where we have opened the metadata file
+ * we return a structure (possibly empty) instead of just a NULL pointer.
+ */
+ rab = (REMDATA_META_S *)fs_get(sizeof(*rab));
+ memset(rab, 0, sizeof(*rab));
+
+ /*
+ * Check for header line. If it isn't there or is incorrect,
+ * return with the empty rab. This call also positions the file pointer
+ * past the header line.
+ */
+ if(rd_meta_is_broken(fp)){
+
+ dprint((2,
+ "metadata file is broken, creating new one: %s\n",
+ metafile ? metafile : "?"));
+
+ /* Make it size zero so we won't copy any bad stuff */
+ if(fp_file_size(fp) != 0){
+ int fd;
+
+ (void)fclose(fp);
+ our_unlink(metafile);
+
+ if((fd = our_open(metafile, O_TRUNC|O_WRONLY|O_CREAT|O_BINARY, 0600)) < 0){
+ q_status_message2(SM_ORDER, 3, 5,
+ _("can't create metadata file %s, continuing (%s)"),
+ metafile, error_description(errno));
+ dprint((2,
+ "can't create metadata file %s: %s\n",
+ metafile ? metafile : "?",
+ error_description(errno)));
+ fs_give((void **)&rab);
+ fs_give((void **)&metafile);
+ return NULL;
+ }
+
+ (void)close(fd);
+ }
+ else
+ (void)fclose(fp);
+
+ fs_give((void **)&metafile);
+ return(rab);
+ }
+
+ fs_give((void **)&metafile);
+
+ /* Look for our line */
+ while((p = fgets(line, sizeof(line), fp)) != NULL)
+ if(strncmp(line, key, strlen(key)) == 0 && line[strlen(key)] == TAB)
+ break;
+
+#define SKIP_TO_TAB(p) do{while(*p && *p != TAB)p++;}while(0)
+
+ /*
+ * The line should be TAB-separated with fields:
+ * folder_name cache_file uidvalidity uidnext uid nmsgs read_status date
+ * This part is highly RemImap-specific right now.
+ */
+ if(p){ /* Found the line, parse it. */
+ SKIP_TO_TAB(p); /* skip to TAB following folder_name */
+ if(*p == TAB){
+ q = ++p; /* q points to cache_file */
+ SKIP_TO_TAB(p); /* skip to TAB following cache_file */
+ if(*p == TAB){
+ *p = '\0';
+ rab->local_cache_file = cpystr(q);
+ q = ++p; /* q points to uidvalidity */
+ SKIP_TO_TAB(p); /* skip to TAB following uidvalidity */
+ if(*p == TAB){
+ *p = '\0';
+ rab->uidvalidity = strtoul(q,(char **)NULL,10);
+ q = ++p; /* q points to uidnext */
+ SKIP_TO_TAB(p); /* skip to TAB following uidnext */
+ if(*p == TAB){
+ *p = '\0';
+ rab->uidnext = strtoul(q,(char **)NULL,10);
+ q = ++p; /* q points to uid */
+ SKIP_TO_TAB(p); /* skip to TAB following uid */
+ if(*p == TAB){
+ *p = '\0';
+ rab->uid = strtoul(q,(char **)NULL,10);
+ q = ++p; /* q points to nmsgs */
+ SKIP_TO_TAB(p); /* skip to TAB following nmsgs */
+ if(*p == TAB){
+ *p = '\0';
+ rab->nmsgs = strtoul(q,(char **)NULL,10);
+ q = ++p; /* q points to read_status */
+ SKIP_TO_TAB(p); /* skip to TAB following read_status */
+ if(*p == TAB){
+ *p = '\0';
+ rab->read_status = *q; /* just a char, not whole string */
+ q = ++p; /* q points to date */
+ while(*p && *p != '\n' && *p != '\r') /* skip to newline */
+ p++;
+
+ *p = '\0';
+ rab->date = cpystr(q);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ (void)fclose(fp);
+ return(rab);
+}
+
+
+/*
+ * Returns: -1 if this doesn't look like a metafile or some error,
+ * or if it looks like a non-current metafile.
+ * 0 if it looks like a current metafile.
+ *
+ * A side effect is that the file pointer will be pointing to the second
+ * line if 0 is returned.
+ */
+int
+rd_meta_is_broken(FILE *fp)
+{
+ char buf[MAILTMPLEN];
+
+ if(fp == (FILE *)NULL)
+ return -1;
+
+ if(fp_file_size(fp) <
+ (long)(SIZEOF_PMAGIC + SIZEOF_SPACE + SIZEOF_VERSION_NUM))
+ return -1;
+
+ if(fseek(fp, (long)TO_FIND_HDR_PMAGIC, 0))
+ return -1;
+
+ /* check for magic */
+ if(fread(buf, sizeof(char), (unsigned)SIZEOF_PMAGIC, fp) != SIZEOF_PMAGIC)
+ return -1;
+
+ buf[SIZEOF_PMAGIC] = '\0';
+ if(strcmp(buf, PMAGIC) != 0)
+ return -1;
+
+ /*
+ * If we change to a new version, we may want to add code here to convert
+ * or to cleanup after the old version. For example, it might just
+ * remove the old cache files here.
+ */
+
+ /* check for matching version number */
+ if(fseek(fp, (long)TO_FIND_VERSION_NUM, 0))
+ return -1;
+
+ if(fread(buf, sizeof(char), (unsigned)SIZEOF_VERSION_NUM, fp) !=
+ SIZEOF_VERSION_NUM)
+ return -1;
+
+ buf[SIZEOF_VERSION_NUM] = '\0';
+ if(strcmp(buf, METAFILE_VERSION_NUM) != 0)
+ return -1;
+
+ /* Position file pointer to second line */
+ if(fseek(fp, (long)TO_FIND_HDR_PMAGIC, 0))
+ return -1;
+
+ if(fgets(buf, sizeof(buf), fp) == NULL)
+ return -1;
+
+ return 0;
+}
+
+
+/*
+ * Open a data stream to the remote data.
+ */
+void
+rd_open_remote(REMDATA_S *rd)
+{
+ long openmode = SP_USEPOOL | SP_TEMPUSE;
+
+ dprint((7, "rd_open_remote \"%s\"\n",
+ (rd && rd->rn) ? rd->rn : "?"));
+
+ if(!rd)
+ return;
+
+ if(rd_stream_exists(rd)){
+ rd->last_use = get_adj_time();
+ return;
+ }
+
+ switch(rd->type){
+ case RemImap:
+
+ if(rd->access == ReadOnly)
+ openmode |= OP_READONLY;
+
+ ps_global->noshow_error = 1;
+ rd->t.i.stream = context_open(NULL, NULL, rd->rn, openmode, NULL);
+ ps_global->noshow_error = 0;
+
+ /* Don't try to reopen if there was a problem (auth failure, etc.) */
+ if(!rd->t.i.stream)
+ rd->flags |= USER_SAID_NO; /* Caution: overloading USER_SAID_NO */
+
+ if(rd->t.i.stream && rd->t.i.stream->halfopen){
+ /* this is a failure */
+ rd_close_remote(rd);
+ }
+
+ if(rd->t.i.stream)
+ rd->last_use = get_adj_time();
+
+ break;
+
+ default:
+ q_status_message(SM_ORDER, 3, 5, "rd_open_remote: type not supported");
+ break;
+ }
+}
+
+
+/*
+ * Close a data stream to the remote data.
+ */
+void
+rd_close_remote(REMDATA_S *rd)
+{
+ if(!rd || !rd_stream_exists(rd))
+ return;
+
+ switch(rd->type){
+ case RemImap:
+ ps_global->noshow_error = 1;
+ pine_mail_close(rd->t.i.stream);
+ rd->t.i.stream = NULL;
+ ps_global->noshow_error = 0;
+ break;
+
+ default:
+ q_status_message(SM_ORDER, 3, 5, "rd_close_remote: type not supported");
+ break;
+ }
+}
+
+
+int
+rd_stream_exists(REMDATA_S *rd)
+{
+ if(!rd)
+ return(0);
+
+ switch(rd->type){
+ case RemImap:
+ return(rd->t.i.stream ? 1 : 0);
+
+ default:
+ q_status_message(SM_ORDER, 3,5, "rd_stream_exists: type not supported");
+ break;
+ }
+
+ return(0);
+}
+
+
+/*
+ * Ping the stream and return 1 if it is still alive.
+ */
+int
+rd_ping_stream(REMDATA_S *rd)
+{
+ int ret = 0;
+
+ dprint((7, "rd_ping_stream \"%s\"\n",
+ (rd && rd->rn) ? rd->rn : "?"));
+
+ if(!rd)
+ return(ret);
+
+ if(!rd_stream_exists(rd)){
+ switch(rd->type){
+ case RemImap:
+ /*
+ * If this stream is already actually open, officially open
+ * it and use it.
+ */
+ if(sp_stream_get(rd->rn, SP_MATCH)){
+ long openflags = SP_USEPOOL | SP_TEMPUSE;
+
+ if(rd->access == ReadOnly)
+ openflags |= OP_READONLY;
+
+ rd->t.i.stream = pine_mail_open(NULL, rd->rn, openflags, NULL);
+ }
+
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if(!rd_stream_exists(rd))
+ return(ret);
+
+ switch(rd->type){
+ case RemImap:
+ ps_global->noshow_error = 1;
+ if(pine_mail_ping(rd->t.i.stream))
+ ret = 1;
+ else
+ rd->t.i.stream = NULL;
+
+ ps_global->noshow_error = 0;
+ break;
+
+ default:
+ q_status_message(SM_ORDER, 3, 5, "rd_ping_stream: type not supported");
+ break;
+ }
+
+ return(ret);
+}
+
+
+/*
+ * Change readonly access to readwrite if appropriate. Call this if
+ * the remote data ought to be readwrite but it is marked readonly in
+ * the metadata.
+ */
+void
+rd_check_readonly_access(REMDATA_S *rd)
+{
+ if(rd && rd->read_status == 'R'){
+ switch(rd->type){
+ case RemImap:
+ rd_open_remote(rd);
+ if(rd->t.i.stream && !rd->t.i.stream->rdonly){
+ rd->read_status = 'W';
+ rd->access = ReadWrite;
+ }
+
+ break;
+
+ default:
+ q_status_message(SM_ORDER, 3, 5,
+ "rd_check_readonly_access: type not supported");
+ break;
+ }
+ }
+}
+
+
+/*
+ * Initialize remote data. The remote data is initialized
+ * with no data. On success, the local file will be empty and the remote
+ * folder (if type RemImap) will contain a header message and an empty
+ * data message.
+ * The remote folder already exists before we get here, though it may be empty.
+ *
+ * Returns -1 on failure
+ * 0 on success
+ */
+int
+rd_init_remote(REMDATA_S *rd, int add_only_first_msg)
+{
+ int err = 0;
+ char date[200];
+
+ dprint((7, "rd_init_remote \"%s\"\n",
+ (rd && rd->rn) ? rd->rn : "?"));
+
+ if(rd && rd->type != RemImap){
+ dprint((1, "rd_init_remote: type not supported\n"));
+ return -1;
+ }
+
+ /*
+ * The rest is currently type RemImap-specific.
+ */
+
+ if(!(rd->flags & NO_FILE || rd->lf) ||
+ !rd->rn || !rd->so || !rd->t.i.stream ||
+ !rd->t.i.special_hdr){
+ dprint((1,
+ "rd_init_remote: Unexpected error: %s is NULL\n",
+ !(rd->flags & NO_FILE || rd->lf) ? "localfile" :
+ !rd->rn ? "remotename" :
+ !rd->so ? "so" :
+ !rd->t.i.stream ? "stream" :
+ !rd->t.i.special_hdr ? "special_hdr" : "?"));
+ return -1;
+ }
+
+ /* already init'd */
+ if(rd->t.i.stream->nmsgs >= 2 ||
+ (rd->t.i.stream->nmsgs >= 1 && add_only_first_msg))
+ return err;
+
+ dprint((7, " - rd_init_remote(%s): %s\n",
+ rd->t.i.special_hdr ? rd->t.i.special_hdr : "?",
+ rd->rn ? rd->rn : "?"));
+
+ /*
+ * We want to protect the user who specifies an actual address book
+ * file on the remote system as a remote address book. For example,
+ * they might say address-book={host}.addressbook where .addressbook
+ * is just a file on host. Remote address books are folders, not
+ * files. We also want to protect the user who specifies an existing
+ * mail folder as a remote address book. We do that by requiring the
+ * first message in the folder to be our header message.
+ */
+
+ if(rd->t.i.stream->rdonly){
+ q_status_message1(SM_ORDER | SM_DING, 7, 10,
+ _("Can't initialize folder \"%s\" (write permission)"), rd->rn);
+ if(rd->t.i.stream->nmsgs > 0)
+ q_status_message(SM_ORDER | SM_DING, 7, 10,
+ _("Choose a new, unused folder for the remote data"));
+
+ dprint((1,
+ "Can't initialize remote folder \"%s\"\n",
+ rd->rn ? rd->rn : "?"));
+ dprint((1,
+ " No write permission for that remote folder.\n"));
+ dprint((1,
+ " Choose a new unused folder for the remote data.\n"));
+ err = -1;
+ }
+
+ if(!err){
+ if(rd->t.i.stream->nmsgs == 0){
+ int we_cancel;
+
+ we_cancel = busy_cue(_("Initializing remote data"), NULL, 1);
+ rfc822_date(date);
+ /*
+ * The first message in a remote data folder is a special
+ * header message. It is there as a way to explain what the
+ * folder is to users who open it trying to read it. It is also
+ * used as a consistency check so that we don't use a folder
+ * that was being used for something else as a remote
+ * data folder.
+ */
+ err = rd_add_hdr_msg(rd, date);
+ if(rd->t.i.stream->nmsgs == 0)
+ rd_ping_stream(rd);
+ if(rd->t.i.stream && rd->t.i.stream->nmsgs == 0)
+ pine_mail_check(rd->t.i.stream);
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+ }
+ else{
+ char *eptr = NULL;
+
+ err = rd_chk_for_hdr_msg(&(rd->t.i.stream), rd, &eptr);
+ if(err){
+ q_status_message1(SM_ORDER | SM_DING, 5, 5,
+ _("\"%s\" has invalid format, can't initialize"), rd->rn);
+
+ dprint((1,
+ "Can't initialize remote data \"%s\"\n",
+ rd->rn ? rd->rn : "?"));
+
+ if(eptr){
+ q_status_message1(SM_ORDER, 3, 5, "%s", eptr);
+ dprint((1, "%s\n", eptr ? eptr : "?"));
+ fs_give((void **)&eptr);
+ }
+ }
+ }
+ }
+
+ /*
+ * Add the second (empty) message.
+ */
+ if(!err && !add_only_first_msg){
+ char *tempfile = NULL;
+ int fd = -1;
+
+ if(rd->flags & NO_FILE){
+ if(so_truncate(rd->sonofile, 0L) == 0)
+ err = -1;
+ }
+ else{
+ if(!(tempfile = tempfile_in_same_dir(rd->lf, "a8", NULL))){
+ q_status_message1(SM_ORDER | SM_DING, 3, 5,
+ _("Error opening temporary file: %s"),
+ error_description(errno));
+ dprint((2, "init_remote: Error opening file: %s\n",
+ error_description(errno)));
+ err = -1;
+ }
+
+ if(!err &&
+ (fd = our_open(tempfile, O_TRUNC|O_WRONLY|O_CREAT|O_BINARY, 0600)) < 0){
+ q_status_message2(SM_ORDER | SM_DING, 3, 5,
+ "Error opening temporary file %.200s: %.200s",
+ tempfile, error_description(errno));
+ dprint((2,
+ "init_remote: Error opening temporary file: %s: %s\n",
+ tempfile ? tempfile : "?", error_description(errno)));
+ our_unlink(tempfile);
+ err = -1;
+ }
+ else
+ (void)close(fd);
+
+ if(!err && rename_file(tempfile, rd->lf) < 0){
+ q_status_message2(SM_ORDER | SM_DING, 3, 5,
+ "Error creating cache file %.200s: %.200s",
+ rd->lf, error_description(errno));
+ our_unlink(tempfile);
+ err = -1;
+ }
+
+ if(tempfile)
+ fs_give((void **)&tempfile);
+ }
+
+ if(!err){
+ err = rd_update_remote(rd, date);
+ if(err){
+ q_status_message1(SM_ORDER | SM_DING, 3, 5,
+ _("Error copying to remote folder: %s"),
+ error_description(errno));
+
+ q_status_message(SM_ORDER | SM_DING, 5, 5,
+ "Creation of remote data folder failed");
+ }
+ }
+ }
+
+ if(!err){
+ rd_update_metadata(rd, date);
+ /* turn off out of date flag */
+ rd->flags &= ~REM_OUTOFDATE;
+ }
+
+ return(err ? -1 : 0);
+}
+
+
+/*
+ * IMAP stream should already be open to a remote folder.
+ * Check the first message in the folder to be sure it is the right
+ * kind of message, not some message from some other folder.
+ *
+ * Returns 0 if ok, < 0 if invalid format.
+ *
+ */
+int
+rd_chk_for_hdr_msg(MAILSTREAM **streamp, REMDATA_S *rd, char **errmsg)
+{
+ char *fields[3], *values[3];
+ char *h;
+ int tried_again = 0;
+ int ret;
+ MAILSTREAM *st = NULL;
+
+ fields[0] = rd->t.i.special_hdr;
+ fields[1] = "received";
+ fields[2] = NULL;
+
+try_again:
+ ret = -1;
+
+ if(!streamp || !*streamp){
+ dprint((1, "rd_chk_for_hdr_msg: stream is null\n"));
+ }
+ else if((*streamp)->nmsgs == 0){
+ ret = -2;
+ dprint((1,
+ "rd_chk_for_hdr_msg: stream has nmsgs=0, try a ping\n"));
+ if(!pine_mail_ping(*streamp))
+ *streamp = NULL;
+
+ if(*streamp && (*streamp)->nmsgs == 0){
+ dprint((1,
+ "rd_chk_for_hdr_msg: still looks like nmsgs=0, try a check\n"));
+ pine_mail_check(*streamp);
+ }
+
+ if(*streamp && (*streamp)->nmsgs == 0){
+ dprint((1,
+ "rd_chk_for_hdr_msg: still nmsgs=0, try re-opening stream\n"));
+
+ if(rd_stream_exists(rd))
+ rd_close_remote(rd);
+
+ rd_open_remote(rd);
+ if(rd_stream_exists(rd))
+ st = rd->t.i.stream;
+ }
+
+ if(!st)
+ st = *streamp;
+
+ if(st && st->nmsgs == 0){
+ dprint((1,
+ "rd_chk_for_hdr_msg: can't see header message\n"));
+ }
+ }
+ else
+ st = *streamp;
+
+ if(st && st->nmsgs != 0
+ && (h=pine_fetchheader_lines(st, 1L, NULL, fields))){
+ simple_header_parse(h, fields, values);
+ ret = -3;
+ if(values[1])
+ ret = -4;
+ else if(values[0]){
+ rd->cookie = strtoul(values[0], (char **)NULL, 10);
+ if(rd->cookie == 0)
+ ret = -5;
+ else if(rd->cookie == 1){
+ if(rd->flags & COOKIE_ONE_OK || tried_again)
+ ret = 0;
+ else
+ ret = -6;
+ }
+ else if(rd->cookie > 1)
+ ret = 0;
+ }
+
+ if(values[0])
+ fs_give((void **)&values[0]);
+
+ if(values[1])
+ fs_give((void **)&values[1]);
+
+ fs_give((void **)&h);
+ }
+
+
+ if(ret && ret != -6 && errmsg){
+ *errmsg = (char *)fs_get(500 * sizeof(char));
+ (*errmsg)[0] = '\0';
+ }
+
+ if(ret == -1){
+ /* null stream */
+ if(errmsg)
+ snprintf(*errmsg, 500, _("Can't open remote address book \"%s\""), rd->rn);
+ }
+ else if(ret == -2){
+ /* no messages in folder */
+ if(errmsg)
+ snprintf(*errmsg, 500,
+ _("Error: no messages in remote address book \"%s\"!"),
+ rd->rn);
+ }
+ else if(ret == -3){
+ /* no cookie */
+ if(errmsg)
+ snprintf(*errmsg, 500,
+ "First msg in \"%s\" should have \"%s\" header",
+ rd->rn, rd->t.i.special_hdr);
+ }
+ else if(ret == -4){
+ /* Received header */
+ if(errmsg)
+ snprintf(*errmsg, 500,
+ _("Suspicious Received headers in first msg in \"%s\""),
+ rd->rn);
+ }
+ else if(ret == -5){
+
+ /* cookie is 0 */
+
+ /*
+ * This is a failure and should not happen, but we're not going to
+ * fail on this condition.
+ */
+ dprint((1, "Unexpected value in \"%s\" header of \"%s\"\n",
+ rd->t.i.special_hdr ? rd->t.i.special_hdr : "?",
+ rd->rn ? rd->rn : "?"));
+ ret = 0;
+ }
+ else if(ret == -6){
+ dprint((1,
+ "rd_chk_for_hdr_msg: cookie is 1, try to upgrade it\n"));
+
+ if(rd_remote_is_readonly(rd)){
+ dprint((1,
+ "rd_chk_for_hdr_msg: can't upgrade, readonly\n"));
+ ret = 0; /* stick with 1 */
+ }
+ else{
+ /* cookie is 1, upgrade it */
+ if(rd_upgrade_cookies(rd, st->nmsgs, 0) == 0){
+ /* now check again */
+ if(!tried_again){
+ tried_again++;
+ goto try_again;
+ }
+ }
+
+ /*
+ * This is actually a failure but we've decided that this
+ * failure is ok.
+ */
+ ret = 0;
+ }
+ }
+
+ if(errmsg && *errmsg)
+ dprint((1, "rd_chk_for_hdr_msg: %s\n", *errmsg));
+
+ return ret;
+}
+
+
+/*
+ * For remote data, this adds the explanatory header
+ * message to the remote IMAP folder.
+ *
+ * Args: rd -- Remote data handle
+ * date -- The date string to use
+ *
+ * Result: 0 success
+ * -1 failure
+ */
+int
+rd_add_hdr_msg(REMDATA_S *rd, char *date)
+{
+ int err = 0;
+
+ if(!rd|| rd->type != RemImap || !rd->rn || !rd->so || !rd->t.i.special_hdr){
+ dprint((1,
+ "rd_add_hdr_msg: Unexpected error: %s is NULL\n",
+ !rd ? "rd" :
+ !rd->rn ? "remotename" :
+ !rd->so ? "so" :
+ !rd->t.i.special_hdr ? "special_hdr" : "?"));
+ return -1;
+ }
+
+ err = rd_store_fake_hdrs(rd, "Header Message for Remote Data",
+ "plain", date);
+
+ /* Write the dummy message */
+ if(!strucmp(rd->t.i.special_hdr, REMOTE_ABOOK_SUBTYPE)){
+ if(!err && so_puts(rd->so,
+ "This folder contains a single Alpine addressbook.\015\012") == 0)
+ err = -1;
+ if(!err && so_puts(rd->so,
+ "This message is just an explanatory message.\015\012") == 0)
+ err = -1;
+ if(!err && so_puts(rd->so,
+ "The last message in the folder is the live addressbook data.\015\012") == 0)
+ err = -1;
+ if(!err && so_puts(rd->so,
+ "The rest of the messages contain previous revisions of the addressbook data.\015\012") == 0)
+ err = -1;
+ if(!err && so_puts(rd->so,
+ "To restore a previous revision just delete and expunge all of the messages\015\012") == 0)
+ err = -1;
+ if(!err && so_puts(rd->so,
+ "which come after it.\015\012") == 0)
+ err = -1;
+ }
+ else if(!strucmp(rd->t.i.special_hdr, REMOTE_PINERC_SUBTYPE)){
+ if(!err && so_puts(rd->so,
+ "This folder contains a Alpine config file.\015\012") == 0)
+ err = -1;
+ if(!err && so_puts(rd->so,
+ "This message is just an explanatory message.\015\012") == 0)
+ err = -1;
+ if(!err && so_puts(rd->so,
+ "The last message in the folder is the live config data.\015\012") == 0)
+ err = -1;
+ if(!err && so_puts(rd->so,
+ "The rest of the messages contain previous revisions of the data.\015\012") == 0)
+ err = -1;
+ if(!err && so_puts(rd->so,
+ "To restore a previous revision just delete and expunge all of the messages\015\012") == 0)
+ err = -1;
+ if(!err && so_puts(rd->so,
+ "which come after it.\015\012") == 0)
+ err = -1;
+ }
+ else{
+ if(!err && so_puts(rd->so,
+ "This folder contains remote Alpine data.\015\012") == 0)
+ err = -1;
+ if(!err && so_puts(rd->so,
+ "This message is just an explanatory message.\015\012") == 0)
+ err = -1;
+ if(!err && so_puts(rd->so,
+ "The last message in the folder is the live data.\015\012") == 0)
+ err = -1;
+ if(!err && so_puts(rd->so,
+ "The rest of the messages contain previous revisions of the data.\015\012") == 0)
+ err = -1;
+ if(!err && so_puts(rd->so,
+ "To restore a previous revision just delete and expunge all of the messages\015\012") == 0)
+ err = -1;
+ if(!err && so_puts(rd->so,
+ "which come after it.\015\012") == 0)
+ err = -1;
+ }
+
+ /* Take the message from "so" to the remote folder */
+ if(!err){
+ MAILSTREAM *st;
+
+ if((st = rd->t.i.stream) != NULL)
+ rd->t.i.shouldbe_nmsgs = rd->t.i.stream->nmsgs + 1;
+ else
+ st = adrbk_handy_stream(rd->rn);
+
+ err = write_fcc(rd->rn, NULL, rd->so, st, "remote data", NULL) ? 0 : -1;
+ }
+
+ return err;
+}
+
+
+/*
+ * Write some fake header lines into storage object rd->so.
+ *
+ * Args: rd
+ * subject -- subject to put in header
+ * subtype -- subtype to put in header
+ * date -- date to put in header
+ */
+int
+rd_store_fake_hdrs(REMDATA_S *rd, char *subject, char *subtype, char *date)
+{
+ ENVELOPE *fake_env;
+ BODY *fake_body;
+ ADDRESS *fake_from;
+ int err = 0;
+ char vers[50], *p;
+ unsigned long r = 0L;
+ RFC822BUFFER rbuf;
+
+ if(!rd|| rd->type != RemImap || !rd->so || !rd->t.i.special_hdr)
+ return -1;
+
+ fake_env = (ENVELOPE *)fs_get(sizeof(ENVELOPE));
+ memset(fake_env, 0, sizeof(ENVELOPE));
+ fake_body = (BODY *)fs_get(sizeof(BODY));
+ memset(fake_body, 0, sizeof(BODY));
+ fake_from = (ADDRESS *)fs_get(sizeof(ADDRESS));
+ memset(fake_from, 0, sizeof(ADDRESS));
+
+ fake_env->subject = cpystr(subject);
+ fake_env->date = (unsigned char *) cpystr(date);
+ fake_from->personal = cpystr("Pine Remote Data");
+ fake_from->mailbox = cpystr("nobody");
+ fake_from->host = cpystr("nowhere");
+ fake_env->from = fake_from;
+ fake_body->type = REMOTE_DATA_TYPE;
+ fake_body->subtype = cpystr(subtype);
+ set_parameter(&fake_body->parameter, "charset", "UTF-8");
+
+ if(rd->cookie > 0)
+ r = rd->cookie;
+
+ if(!r){
+ int i;
+
+ for(i = 100; i > 0 && r < 1000000; i--)
+ r = random();
+
+ if(r < 1000000)
+ r = 1712836L + getpid();
+
+ rd->cookie = r;
+ }
+
+ snprintf(vers, sizeof(vers), "%ld", r);
+
+ p = tmp_20k_buf;
+ *p = '\0';
+ rbuf.f = dummy_soutr;
+ rbuf.s = NULL;
+ rbuf.beg = p;
+ rbuf.cur = p;
+ rbuf.end = p+SIZEOF_20KBUF-1;
+ rfc822_output_header_line(&rbuf, rd->t.i.special_hdr, 0L, vers);
+ rfc822_output_header(&rbuf, fake_env, fake_body, NULL, 0L);
+ *rbuf.cur = '\0';
+
+ mail_free_envelope(&fake_env);
+ mail_free_body(&fake_body);
+
+ /* Write the fake headers */
+ if(so_puts(rd->so, tmp_20k_buf) == 0)
+ err = -1;
+
+ return err;
+}
+
+
+/*
+ * We have discovered that the data in the remote folder is suspicious.
+ * In some cases it is just because it is from an old version of pine.
+ * We have decided to update the data so that it won't look suspicious
+ * next time.
+ *
+ * Args -- only_update_last If set, that means to just add a new last message
+ * by calling rd_update_remote. Don't create a new
+ * header message and delete the old header message.
+ * nmsgs Not used if only_update_last is set
+ */
+int
+rd_upgrade_cookies(REMDATA_S *rd, long int nmsgs, int only_update_last)
+{
+ char date[200];
+ int err = 0;
+
+ /*
+ * We need to copy the data from the last message, add a new header
+ * message with a random cookie, add the data back in with the
+ * new cookie, and delete the old messages.
+ */
+
+ /* get data */
+ rd->flags |= COOKIE_ONE_OK;
+
+ /*
+ * The local copy may be newer than the remote copy. We don't want to
+ * blast the local copy in that case. The BELIEVE_CACHE flag tells us
+ * to not do the blasting.
+ */
+ if(rd->flags & BELIEVE_CACHE)
+ rd->flags &= ~BELIEVE_CACHE;
+ else{
+ dprint((1, "rd_upgrade_cookies: copy abook data\n"));
+ err = rd_update_local(rd);
+ }
+
+ if(!err && !only_update_last){
+ rd->cookie = 0; /* causes new cookie to be generated */
+ rfc822_date(date);
+ dprint((1, "rd_upgrade_cookies: add new hdr msg to end\n"));
+ err = rd_add_hdr_msg(rd, date);
+ }
+
+ if(!err){
+ dprint((1, "rd_upgrade_cookies: copy back data\n"));
+ err = rd_update_remote(rd, NULL);
+ }
+
+ rd->flags &= ~COOKIE_ONE_OK;
+
+ if(!err && !only_update_last){
+ char sequence[20];
+
+ /*
+ * We've created a new header message and added a new copy of the
+ * data after it. Only problem is that the new copy will have used
+ * the original header message to get its cookie (== 1) from. We
+ * could have deleted the original messages before the last step
+ * to get it right but that would delete all copies of the data
+ * temporarily. Delete now and then re-update.
+ */
+ rd_ping_stream(rd);
+ rd_open_remote(rd);
+ if(rd->t.i.stream && rd->t.i.stream->nmsgs >= nmsgs+2){
+ snprintf(sequence, sizeof(sequence), "1:%ld", nmsgs);
+ mail_flag(rd->t.i.stream, sequence, "\\DELETED", ST_SET);
+ mail_expunge(rd->t.i.stream);
+ err = rd_update_remote(rd, NULL);
+ }
+ }
+
+ return(err);
+}
+
+
+/*
+ * Copy remote data to local file. If needed, the remote data is initialized
+ * with no data. On success, the local file contains the remote data (which
+ * means it will be empty if we initialized).
+ *
+ * Returns != 0 on failure
+ * 0 on success
+ */
+int
+rd_update_local(REMDATA_S *rd)
+{
+ char *error;
+ STORE_S *store;
+ gf_io_t pc;
+ int i, we_cancel = 0;
+ BODY *body = NULL;
+ ENVELOPE *env;
+ char *tempfile = NULL;
+
+
+ if(!rd || !(rd->flags & NO_FILE || rd->lf) || !rd->rn){
+ dprint((1,
+ "rd_update_local: Unexpected error: %s is NULL\n",
+ !rd ? "rd" :
+ !(rd->flags & NO_FILE || rd->lf) ? "localfile" :
+ !rd->rn ? "remotename" : "?"));
+
+ return -1;
+ }
+
+ dprint((3, " - rd_update_local(%s): %s => %s\n",
+ rd->type == RemImap ? "Imap" : "?", rd->rn ? rd->rn : "?",
+ (rd->flags & NO_FILE) ? "<mem>" : (rd->lf ? rd->lf : "?")));
+
+ switch(rd->type){
+ case RemImap:
+ if(!rd->so || !rd->t.i.special_hdr){
+ dprint((1,
+ "rd_update_local: Unexpected error: %s is NULL\n",
+ !rd->so ? "so" :
+ !rd->t.i.special_hdr ? "special_hdr" : "?"));
+ return -1;
+ }
+
+ rd_open_remote(rd);
+ if(!rd_stream_exists(rd)){
+ if(rd->flags & NO_PERM_CACHE){
+ dprint((1,
+ "rd_update_local: backtrack, remote folder does not exist yet\n"));
+ }
+ else{
+ dprint((1,
+ "rd_update_local: Unexpected error: stream is NULL\n"));
+ }
+
+ return -1;
+ }
+
+ if(rd->t.i.stream){
+ char ebuf[500];
+ char *eptr = NULL;
+ int chk;
+
+ /* force ReadOnly */
+ if(rd->t.i.stream->rdonly){
+ rd->read_status = 'R';
+ rd->access = ReadOnly;
+ }
+ else
+ rd->read_status = 'W';
+
+ if(rd->t.i.stream->nmsgs < 2)
+ return(rd_init_remote(rd, 0));
+ else if(rd_chk_for_hdr_msg(&(rd->t.i.stream), rd, &eptr)){
+ q_status_message1(SM_ORDER | SM_DING, 5, 5,
+ _("Can't initialize \"%s\" (invalid format)"), rd->rn);
+
+ if(eptr){
+ q_status_message1(SM_ORDER, 3, 5, "%s", eptr);
+ dprint((1, "%s\n", eptr));
+ fs_give((void **)&eptr);
+ }
+
+ dprint((1,
+ "Can't initialize remote data \"%s\"\n",
+ rd->rn ? rd->rn : "?"));
+ return -1;
+ }
+
+ we_cancel = busy_cue(_("Copying remote data"), NULL, 1);
+
+ if(rd->flags & NO_FILE){
+ store = rd->sonofile;
+ so_truncate(store, 0L);
+ }
+ else{
+ if(!(tempfile = tempfile_in_same_dir(rd->lf, "a8", NULL))){
+ q_status_message1(SM_ORDER | SM_DING, 3, 5,
+ _("Error opening temporary file: %s"),
+ error_description(errno));
+ dprint((2,
+ "rd_update_local: Error opening temporary file: %s\n",
+ error_description(errno)));
+ return -1;
+ }
+
+ /* Copy the data into tempfile */
+ if((store = so_get(FileStar, tempfile, WRITE_ACCESS|OWNER_ONLY))
+ == NULL){
+ q_status_message2(SM_ORDER | SM_DING, 3, 5,
+ _("Error opening temporary file %s: %s"),
+ tempfile, error_description(errno));
+ dprint((2,
+ "rd_update_local: Error opening temporary file: %s: %s\n",
+ tempfile ? tempfile : "?",
+ error_description(errno)));
+ our_unlink(tempfile);
+ fs_give((void **)&tempfile);
+ return -1;
+ }
+ }
+
+ /*
+ * Copy from the last message in the folder.
+ */
+ if(!pine_mail_fetchstructure(rd->t.i.stream, rd->t.i.stream->nmsgs,
+ &body)){
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Can't access remote IMAP data"));
+ dprint((2, "Can't access remote IMAP data\n"));
+ if(tempfile){
+ our_unlink(tempfile);
+ fs_give((void **)&tempfile);
+ }
+
+ if(!(rd->flags & NO_FILE))
+ so_give(&store);
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ return -1;
+ }
+
+ if(!body ||
+ body->type != REMOTE_DATA_TYPE ||
+ !body->subtype ||
+ strucmp(body->subtype, rd->t.i.special_hdr)){
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Remote IMAP folder has wrong contents"));
+ dprint((2,
+ "Remote IMAP folder has wrong contents\n"));
+ if(tempfile){
+ our_unlink(tempfile);
+ fs_give((void **)&tempfile);
+ }
+
+ if(!(rd->flags & NO_FILE))
+ so_give(&store);
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ return -1;
+ }
+
+ if(!(env = pine_mail_fetchenvelope(rd->t.i.stream,
+ rd->t.i.stream->nmsgs))){
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Can't access check date in remote data"));
+ dprint((2,
+ "Can't access check date in remote data\n"));
+ if(tempfile){
+ our_unlink(tempfile);
+ fs_give((void **)&tempfile);
+ }
+
+ if(!(rd->flags & NO_FILE))
+ so_give(&store);
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ return -1;
+ }
+
+ if(rd && rd->flags & USER_SAID_YES)
+ chk = 0;
+ else
+ chk = rd_check_for_suspect_data(rd);
+
+ switch(chk){
+ case -1: /* suspicious data, user says abort */
+ if(tempfile){
+ our_unlink(tempfile);
+ fs_give((void **)&tempfile);
+ }
+
+ if(!(rd->flags & NO_FILE))
+ so_give(&store);
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ return -1;
+
+ case 1: /* suspicious data, user says go ahead */
+ if(rd_remote_is_readonly(rd)){
+ dprint((1,
+ "rd_update_local: can't upgrade, readonly\n"));
+ }
+ else
+ /* attempt to upgrade cookie in last message */
+ (void)rd_upgrade_cookies(rd, 0, 1);
+
+ break;
+
+ case 0: /* all is ok */
+ default:
+ break;
+ }
+
+
+ /* store may have been written to at this point, so we'll clear it out */
+ so_truncate(store, 0L);
+
+ gf_set_so_writec(&pc, store);
+
+ error = detach(rd->t.i.stream, rd->t.i.stream->nmsgs, "1", 0L,
+ NULL, pc, NULL, DT_NODFILTER);
+
+ gf_clear_so_writec(store);
+
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ if(!(rd->flags & NO_FILE)){
+ if(so_give(&store)){
+ snprintf(ebuf, sizeof(ebuf), _("Error writing temp file: %s"),
+ error_description(errno));
+ error = ebuf;
+ }
+ }
+
+ if(error){
+ q_status_message1(SM_ORDER | SM_DING, 3, 4,
+ _("%s: Error copying remote IMAP data"), error);
+ dprint((2, "rd_update_local: Error copying: %s\n",
+ error ? error : "?"));
+ if(tempfile){
+ our_unlink(tempfile);
+ fs_give((void **)&tempfile);
+ }
+
+ return -1;
+ }
+
+ if(tempfile && (i = rename_file(tempfile, rd->lf)) < 0){
+#ifdef _WINDOWS
+ if(i == -5){
+ q_status_message2(SM_ORDER | SM_DING, 3, 4,
+ _("Error updating local file: %s: %s"),
+ rd->lf, error_description(errno));
+ q_status_message(SM_ORDER, 3, 4,
+ _("Perhaps another process has the file open?"));
+ dprint((2,
+ "Rename_file(%s,%s): %s: returned -5, another process has file open?\n",
+ tempfile ? tempfile : "?",
+ rd->lf ? rd->lf : "?",
+ error_description(errno)));
+ }
+ else
+#endif /* _WINDOWS */
+ {
+ q_status_message2(SM_ORDER | SM_DING, 3, 5,
+ _("Error updating cache file %s: %s"),
+ rd->lf, error_description(errno));
+ dprint((2,
+ "Error updating cache file %s: rename(%s,%s): %s\n",
+ tempfile ? tempfile : "?",
+ tempfile ? tempfile : "?",
+ rd->lf ? rd->lf : "?",
+ error_description(errno)));
+ }
+
+ our_unlink(tempfile);
+ fs_give((void **)&tempfile);
+ return -1;
+ }
+
+ dprint((5,
+ "in rd_update_local, setting chk_date to ->%s<-\n",
+ env->date ? (char *)env->date : "?"));
+ rd_update_metadata(rd, (char *) env->date);
+
+ /* turn off out of date flag */
+ rd->flags &= ~REM_OUTOFDATE;
+
+ if(tempfile)
+ fs_give((void **)&tempfile);
+
+ return 0;
+ }
+ else{
+ q_status_message1(SM_ORDER | SM_DING, 5, 5,
+ _("Can't open remote IMAP folder \"%s\""), rd->rn);
+ dprint((1,
+ "Can't open remote IMAP folder \"%s\"\n",
+ rd->rn ? rd->rn : "?"));
+ rd->access = ReadOnly;
+ return -1;
+ }
+
+ break;
+
+ default:
+ dprint((1, "rd_update_local: Unsupported type\n"));
+ return -1;
+ }
+}
+
+
+/*
+ * Copy local data to remote folder.
+ *
+ * Args lf -- Local file name
+ * rn -- Remote name
+ * header_to_check -- Name of indicator header in remote folder
+ * imapstuff -- Structure holding info about connection
+ * returndate -- Pointer to the date that was stored in the remote folder
+ *
+ * Returns !=0 on failure
+ * 0 on success
+ */
+int
+rd_update_remote(REMDATA_S *rd, char *returndate)
+{
+ STORE_S *store;
+ int err = 0;
+ long openmode = SP_USEPOOL | SP_TEMPUSE;
+ MAILSTREAM *st;
+ char *eptr = NULL;
+
+ if(rd && rd->type != RemImap){
+ dprint((1, "rd_update_remote: type not supported\n"));
+ return -1;
+ }
+
+ if(!rd || !(rd->flags & NO_FILE || rd->lf) || !rd->rn ||
+ !rd->so || !rd->t.i.special_hdr){
+ dprint((1,
+ "rd_update_remote: Unexpected error: %s is NULL\n",
+ !rd ? "rd" :
+ !(rd->flags & NO_FILE || rd->lf) ? "localfile" :
+ !rd->rn ? "remotename" :
+ !rd->so ? "so" :
+ !rd->t.i.special_hdr ? "special_hdr" : "?"));
+ return -1;
+ }
+
+ dprint((7, " - rd_update_remote(%s): %s => %s\n",
+ rd->t.i.special_hdr ? rd->t.i.special_hdr : "?",
+ rd->lf ? rd->lf : "<mem>",
+ rd->rn ? rd->rn : "?"));
+
+ if(!(st = rd->t.i.stream))
+ st = adrbk_handy_stream(rd->rn);
+
+ if(!st)
+ st = rd->t.i.stream = context_open(NULL, NULL, rd->rn, openmode, NULL);
+
+ if(!st){
+ q_status_message1(SM_ORDER | SM_DING, 5, 5,
+ _("Can't open \"%s\" for copying"), rd->rn);
+ dprint((1,
+ "rd_update_remote: Can't open remote folder \"%s\" for copying\n",
+ rd->rn ? rd->rn : "?"));
+ return 1;
+ }
+
+ rd->last_use = get_adj_time();
+ err = rd_chk_for_hdr_msg(&st, rd, &eptr);
+ if(err){
+ q_status_message1(SM_ORDER | SM_DING, 5, 5,
+ _("\"%s\" has invalid format"), rd->rn);
+
+ if(eptr){
+ q_status_message1(SM_ORDER, 3, 5, "%s", eptr);
+ dprint((1, "%s\n", eptr));
+ fs_give((void **)&eptr);
+ }
+
+ dprint((1,
+ "rd_update_remote: \"%s\" has invalid format\n",
+ rd->rn ? rd->rn : "?"));
+ return 1;
+ }
+
+ errno = 0;
+
+ /*
+ * The data that will be going to the remote folder rn is
+ * written into the following storage object and then copied to
+ * the remote folder from there.
+ */
+
+ if(rd->flags & NO_FILE){
+ store = rd->sonofile;
+ if(store)
+ so_seek(store, 0L, 0); /* rewind */
+ }
+ else
+ store = so_get(FileStar, rd->lf, READ_ACCESS);
+
+ if(store != NULL){
+ char date[200];
+ unsigned char c;
+ unsigned char last_c = 0;
+
+ /* Reset the storage object, since we may have already used it. */
+ if(so_truncate(rd->so, 0L) == 0)
+ err = 1;
+
+ rfc822_date(date);
+ dprint((7,
+ "in rd_update_remote, storing date ->%s<-\n",
+ date ? date : "?"));
+ if(!err && rd_store_fake_hdrs(rd, "Pine Remote Data Container",
+ rd->t.i.special_hdr, date))
+ err = 1;
+
+ /* save the date for later comparisons */
+ if(!err && returndate)
+ strncpy(returndate, date, 100);
+
+ /* Write the data */
+ while(!err && so_readc(&c, store)){
+ /*
+ * C-client expects CRLF-terminated lines. Convert them
+ * as we copy into c-client. Read ahead isn't available.
+ * Leave CRLF as is, convert LF to CRLF, leave CR as is.
+ */
+ if(last_c != '\r' && c == '\n'){
+ /* Convert \n to CRFL */
+ if(so_writec('\r', rd->so) == 0 || so_writec('\n', rd->so) == 0)
+ err = 1;
+
+ last_c = 0;
+ }
+ else{
+ last_c = c;
+ if(so_writec((int) c, rd->so) == 0)
+ err = 1;
+ }
+ }
+
+ /*
+ * Take that message from so to the remote folder.
+ * We append to that folder and always
+ * use the final message as the active data.
+ */
+ if(!err){
+ MAILSTREAM *st;
+
+ if((st = rd->t.i.stream) != NULL)
+ rd->t.i.shouldbe_nmsgs = rd->t.i.stream->nmsgs + 1;
+ else
+ st = adrbk_handy_stream(rd->rn);
+
+ err = write_fcc(rd->rn, NULL, rd->so, st,
+ "remote data", NULL) ? 0 : 1;
+ }
+
+
+ if(!(rd->flags & NO_FILE))
+ so_give(&store);
+ }
+ else
+ err = -1;
+
+ if(err)
+ dprint((2, "error in rd_update_remote for %s => %s\n",
+ rd->lf ? rd->lf : "<mem>", rd->rn ? rd->rn : "?"));
+
+ return(err);
+}
+
+
+/*
+ * Check to see if the remote data has changed since we cached it.
+ *
+ * Args rd -- REMDATA handle
+ * do_it_now -- If > 0, check now regardless
+ * If = 0, check if time since last chk more than default
+ * If < 0, check if time since last chk more than -do_it_now
+ */
+void
+rd_check_remvalid(REMDATA_S *rd, long int do_it_now)
+{
+ time_t chk_interval;
+ long openmode = SP_USEPOOL | SP_TEMPUSE;
+ MAILSTREAM *stat_stream = NULL;
+ int we_cancel = 0, got_cmsgs = 0;
+ unsigned long current_nmsgs;
+
+ dprint((7, "- rd_check_remvalid(%s) -\n",
+ (rd && rd->rn) ? rd->rn : ""));
+
+ if(rd && rd->type != RemImap){
+ dprint((1, "rd_check_remvalid: type not supported\n"));
+ return;
+ }
+
+ if(!rd || rd->flags & REM_OUTOFDATE || rd->flags & USE_OLD_CACHE)
+ return;
+
+ if(!rd->t.i.chk_date){
+ dprint((2,
+ "rd_check_remvalid: remote data %s changed (chk_date)\n",
+ rd->rn));
+ rd->flags |= REM_OUTOFDATE;
+ return;
+ }
+
+ if(rd->t.i.chk_nmsgs <= 1){
+ dprint((2,
+ "rd_check_remvalid: remote data %s changed (chk_nmsgs <= 1)\n",
+ rd->rn ? rd->rn : "?"));
+ rd->flags |= REM_OUTOFDATE;
+ return;
+ }
+
+ if(do_it_now < 0L){
+ chk_interval = -1L * do_it_now;
+ do_it_now = 0L;
+ }
+ else
+ chk_interval = ps_global->remote_abook_validity * 60L;
+
+ /* too soon to check again */
+ if(!do_it_now &&
+ (chk_interval == 0L ||
+ get_adj_time() <= rd->last_valid_chk + chk_interval))
+ return;
+
+ if(rd->access == ReadOnly)
+ openmode |= OP_READONLY;
+
+ rd->last_valid_chk = get_adj_time();
+ mm_status_result.flags = 0L;
+
+ /* make sure the cache file is still there */
+ if(rd->lf && can_access(rd->lf, READ_ACCESS) != 0){
+ dprint((2,
+ "rd_check_remvalid: %s: cache file %s disappeared\n",
+ rd->rn ? rd->rn : "?",
+ rd->lf ? rd->lf : "?"));
+ rd->flags |= REM_OUTOFDATE;
+ return;
+ }
+
+ /*
+ * If the stream is open we should check there instead of using
+ * a STATUS command. Check to see if it is really still alive with the
+ * ping. It would be convenient if we could use a status command
+ * on the open stream but apparently that won't work everywhere.
+ */
+ rd_ping_stream(rd);
+
+try_looking_in_stream:
+
+ /*
+ * Get the current number of messages in the folder to
+ * compare with our saved number of messages.
+ */
+ current_nmsgs = 0;
+ if(rd->t.i.stream){
+ dprint((7, "using open remote data stream\n"));
+ rd->last_use = get_adj_time();
+ current_nmsgs = rd->t.i.stream->nmsgs;
+ got_cmsgs++;
+ }
+ else{
+
+ /*
+ * Try to use the imap status command
+ * to get the information as cheaply as possible.
+ * If NO_STATUSCMD is set we just bypass all this stuff.
+ */
+
+ if(!(rd->flags & NO_STATUSCMD))
+ stat_stream = adrbk_handy_stream(rd->rn);
+
+ /*
+ * If we don't have a stream to use for the status command we
+ * skip it and open the folder instead. Then, next time we want to
+ * check we'll probably be able to use that open stream instead of
+ * opening another one each time for the status command.
+ */
+ if(stat_stream){
+ if(!LEVELSTATUS(stat_stream)){
+ rd->flags |= NO_STATUSCMD;
+ dprint((2,
+ "rd_check_remvalid: remote data %s: server doesn't support status\n",
+ rd->rn ? rd->rn : "?"));
+ }
+ else{
+ /*
+ * This sure seems like a crock. We have to check to
+ * see if the stream is actually open to the folder
+ * we want to do the status on because c-client can't
+ * do a status on an open folder. In this case, we fake
+ * the status command results ourselves.
+ * If we're so unlucky as to get back a stream that will
+ * work for the status command while we also have another
+ * stream that is rd->rn and we don't pick up on that,
+ * too bad.
+ */
+ if(same_stream_and_mailbox(rd->rn, stat_stream)){
+ dprint((7,
+ "rd_check_remvalid: faking status\n"));
+ mm_status_result.flags = SA_MESSAGES | SA_UIDVALIDITY
+ | SA_UIDNEXT;
+ mm_status_result.messages = stat_stream->nmsgs;
+ mm_status_result.uidvalidity = stat_stream->uid_validity;
+ mm_status_result.uidnext = stat_stream->uid_last+1;
+ }
+ else{
+
+ dprint((7,
+ "rd_check_remvalid: trying status\n"));
+ ps_global->noshow_error = 1;
+ if(!pine_mail_status(stat_stream, rd->rn,
+ SA_UIDVALIDITY | SA_UIDNEXT | SA_MESSAGES)){
+ /* failed, mark it so we won't try again */
+ rd->flags |= NO_STATUSCMD;
+ dprint((2,
+ "rd_check_remvalid: addrbook %s: status command failed\n",
+ rd->rn ? rd->rn : "?"));
+ mm_status_result.flags = 0L;
+ }
+ }
+
+ ps_global->noshow_error = 0;
+ }
+ }
+
+ /* if we got back a result from the status command, use it */
+ if(mm_status_result.flags){
+ dprint((7,
+ "rd_check_remvalid: got status_result 0x%x\n",
+ mm_status_result.flags));
+ if(mm_status_result.flags & SA_MESSAGES){
+ current_nmsgs = mm_status_result.messages;
+ got_cmsgs++;
+ }
+ }
+ }
+
+ /*
+ * Check current_nmsgs versus what we think it should be.
+ * If they're different we know things have changed and we can
+ * return now. If they're the same we don't know.
+ */
+ if(got_cmsgs && current_nmsgs != rd->t.i.chk_nmsgs){
+ rd->flags |= REM_OUTOFDATE;
+ dprint((2,
+ "rd_check_remvalid: remote data %s changed (current msgs (%ld) != chk_nmsgs (%ld))\n",
+ rd->rn ? rd->rn : "?", current_nmsgs, rd->t.i.chk_nmsgs));
+ return;
+ }
+
+ /*
+ * Get the current uidvalidity and uidnext values from the
+ * folder to compare with our saved values.
+ */
+ if(rd->t.i.stream){
+ if(rd->t.i.stream->uid_validity == rd->t.i.uidvalidity){
+ /*
+ * Uid is valid so we just have to check whether or not the
+ * uid of the last message has changed or not and return.
+ */
+ if(mail_uid(rd->t.i.stream, rd->t.i.stream->nmsgs) != rd->t.i.uid){
+ /* uid has changed so we're out of date */
+ rd->flags |= REM_OUTOFDATE;
+ dprint((2,
+ "rd_check_remvalid: remote data %s changed (uid)\n",
+ rd->rn ? rd->rn : "?"));
+ }
+ else{
+ dprint((7,"rd_check_remvalid: uids match\n"));
+ }
+
+ return;
+ }
+ else{
+ /*
+ * If the uidvalidity changed that probably means it can't
+ * be relied on to be meaningful, so don't use it in the future.
+ */
+ rd->flags |= NO_STATUSCMD;
+ dprint((7,
+ "rd_check_remvalid: remote data %s uidvalidity changed, don't use uid\n",
+ rd->rn ? rd->rn : "?"));
+ }
+ }
+ else{ /* stream not open, use status results */
+ if(mm_status_result.flags & SA_UIDVALIDITY &&
+ mm_status_result.flags & SA_UIDNEXT &&
+ mm_status_result.uidvalidity == rd->t.i.uidvalidity){
+
+ /*
+ * Uids are valid.
+ */
+
+ if(mm_status_result.uidnext == rd->t.i.uidnext){
+ /*
+ * Uidnext valid and unchanged, so the folder is unchanged.
+ */
+ dprint((7, "rd_check_remvalid: uidnexts match\n"));
+ return;
+ }
+ else{ /* uidnext changed, folder _may_ have changed */
+
+ dprint((7,
+ "rd_check_remvalid: uidnexts don't match, ours=%lu status=%lu\n",
+ rd->t.i.uidnext, mm_status_result.uidnext));
+
+ /*
+ * Since c-client can't handle a status cmd on the selected
+ * mailbox, we may have had to guess at the value of uidnext,
+ * and we don't know for sure that this is a real change.
+ * We need to open the mailbox and find out for sure.
+ */
+ we_cancel = busy_cue(NULL, NULL, 1);
+ ps_global->noshow_error = 1;
+ if((rd->t.i.stream = context_open(NULL, NULL, rd->rn, openmode,
+ NULL)) != NULL){
+ imapuid_t last_msg_uid;
+
+ if(rd->t.i.stream->rdonly)
+ rd->read_status = 'R';
+
+ last_msg_uid = mail_uid(rd->t.i.stream,
+ rd->t.i.stream->nmsgs);
+ ps_global->noshow_error = 0;
+ rd->last_use = get_adj_time();
+ dprint((7,
+ "%s: opened to check uid (%ld)\n",
+ rd->rn ? rd->rn : "?", (long)rd->last_use));
+ if(last_msg_uid != rd->t.i.uid){ /* really did change */
+ rd->flags |= REM_OUTOFDATE;
+ dprint((2,
+ "rd_check_remvalid: remote data %s changed, our uid=%lu real uid=%lu\n",
+ rd->rn ? rd->rn : "?",
+ rd->t.i.uid, last_msg_uid));
+ }
+ else{ /* false hit */
+ /*
+ * The uid of the last message is the same as we
+ * remembered, so the problem is that our guess
+ * for the nextuid was wrong. It didn't actually
+ * change. Since we know the correct uidnext now we
+ * can reset that guess to the correct value for
+ * next time, avoiding this extra mail_open.
+ */
+ dprint((2,
+ "rd_check_remvalid: remote data %s false change: adjusting uidnext from %lu to %lu\n",
+ rd->rn ? rd->rn : "?",
+ rd->t.i.uidnext,
+ mm_status_result.uidnext));
+ rd->t.i.uidnext = mm_status_result.uidnext;
+ rd_write_metadata(rd, 0);
+ }
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ return;
+ }
+ else{
+ ps_global->noshow_error = 0;
+ dprint((2,
+ "rd_check_remvalid: couldn't open %s\n",
+ rd->rn ? rd->rn : "?"));
+ }
+ }
+ }
+ else{
+ /*
+ * If the status command doesn't return these or
+ * if the uidvalidity is changing that probably means it can't
+ * be relied on to be meaningful, so don't use it.
+ *
+ * We also come here if we don't have a stat_stream handy to
+ * look up the status. This happens, for example, if our
+ * address book is on a different server from the open mail
+ * folders. In that case, instead of opening a stream,
+ * doing a status command, and closing the stream, we open
+ * the stream and use it to check next time, too.
+ */
+ if(stat_stream){
+ rd->flags |= NO_STATUSCMD;
+ dprint((7,
+ "rd_check_remvalid: remote data %s don't use status\n",
+ rd->rn ? rd->rn : "?"));
+ }
+
+ dprint((7,
+ "opening remote data stream for validity check\n"));
+ we_cancel = busy_cue(NULL, NULL, 1);
+ ps_global->noshow_error = 1;
+ rd->t.i.stream = context_open(NULL, NULL, rd->rn, openmode,
+ NULL);
+ ps_global->noshow_error = 0;
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ we_cancel = 0;
+ if(rd->t.i.stream)
+ goto try_looking_in_stream;
+ else{
+ dprint((7,
+ "rd_check_remvalid: cannot open remote mailbox\n"));
+ return;
+ }
+ }
+ }
+
+ /*
+ * If we got here that means that we still don't know if the folder
+ * changed or not. If the stream is open then it must be the case that
+ * uidvalidity changed so we can't rely on using uids. If the stream
+ * isn't open, then either the status command didn't work or the
+ * uidvalidity changed. In any case, we need to fall back to looking
+ * inside the folder at the last message and checking whether or not the
+ * Date of the last message is the one we remembered.
+ */
+
+ dprint((7,
+ "rd_check_remvalid: falling back to Date check\n"));
+
+ /*
+ * Fall back to looking in the folder at the Date header.
+ */
+
+ if(!rd->t.i.stream)
+ we_cancel = busy_cue(NULL, NULL, 1);
+
+ ps_global->noshow_error = 1;
+ if(rd->t.i.stream ||
+ (rd->t.i.stream = context_open(NULL,NULL,rd->rn,openmode,NULL))){
+ ENVELOPE *env = NULL;
+
+ if(rd->t.i.stream->rdonly)
+ rd->read_status = 'R';
+
+ if(rd->t.i.stream->nmsgs != rd->t.i.chk_nmsgs){
+ rd->flags |= REM_OUTOFDATE;
+ dprint((2,
+ "rd_check_remvalid: remote data %s changed (expected nmsgs %ld, got %ld)\n",
+ rd->rn ? rd->rn : "?",
+ rd->t.i.chk_nmsgs, rd->t.i.stream->nmsgs));
+ }
+ else if(rd->t.i.stream->nmsgs > 1){
+ env = pine_mail_fetchenvelope(rd->t.i.stream,rd->t.i.stream->nmsgs);
+
+ if(!env || (env->date && strucmp((char *) env->date, rd->t.i.chk_date))){
+ rd->flags |= REM_OUTOFDATE;
+ dprint((2,
+ "rd_check_remvalid: remote data %s changed (%s)\n",
+ rd->rn ? rd->rn : "?", env ? "date" : "not enough msgs"));
+ }
+ }
+
+ rd->last_use = get_adj_time();
+ if(env)
+ dprint((7,
+ "%s: got envelope to check date (%ld)\n",
+ rd->rn ? rd->rn : "?", (long)rd->last_use));
+ }
+ /* else, we give up and go with the cache copy */
+
+ ps_global->noshow_error = 0;
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ dprint((7, "rd_check_remvalid: falling off end\n"));
+}
+
+
+/*
+ * Returns nonzero if remote data is currently readonly.
+ *
+ * Returns 0 -- apparently writeable
+ * 1 -- stream not open
+ * 2 -- stream open but not writeable
+ */
+int
+rd_remote_is_readonly(REMDATA_S *rd)
+{
+ if(!rd || rd->access == ReadOnly || !rd_stream_exists(rd))
+ return(1);
+
+ switch(rd->type){
+ case RemImap:
+ if(rd->t.i.stream->rdonly)
+ return(2);
+
+ break;
+
+ default:
+ q_status_message(SM_ORDER, 3, 5,
+ "rd_remote_is_readonly: type not supported");
+ break;
+ }
+
+ return(0);
+}
+
+
+/*
+ * Returns 0 if ok
+ * -1 if not ok and user says No
+ * 1 if not ok but user says Yes, ok to use it
+ */
+int
+rd_check_for_suspect_data(REMDATA_S *rd)
+{
+ int ans = -1;
+ char *fields[3], *values[3], *h;
+ unsigned long cookie;
+
+ if(!rd || rd->type != RemImap || !rd->so || !rd->rn || !rd->t.i.special_hdr)
+ return -1;
+
+ fields[0] = rd->t.i.special_hdr;
+ fields[1] = "received";
+ fields[2] = NULL;
+ cookie = 0L;
+ if((h=pine_fetchheader_lines(rd->t.i.stream, rd->t.i.stream->nmsgs,
+ NULL, fields)) != NULL){
+ simple_header_parse(h, fields, values);
+ if(values[1]) /* Received lines present! */
+ ans = rd_prompt_about_forged_remote_data(-1, rd, NULL);
+ else if(values[0]){
+ cookie = strtoul(values[0], (char **)NULL, 10);
+ if(cookie == rd->cookie) /* all's well */
+ ans = 0;
+ else
+ ans = rd_prompt_about_forged_remote_data(cookie > 1L
+ ? 100 : cookie,
+ rd, values[0]);
+ }
+ else
+ ans = rd_prompt_about_forged_remote_data(-2, rd, NULL);
+
+ if(values[0])
+ fs_give((void **)&values[0]);
+
+ if(values[1])
+ fs_give((void **)&values[1]);
+
+ fs_give((void **)&h);
+ }
+ else /* missing magic header */
+ ans = rd_prompt_about_forged_remote_data(-2, rd, NULL);
+
+ return ans;
+}
diff --git a/pith/remote.h b/pith/remote.h
new file mode 100644
index 00000000..621b6ed8
--- /dev/null
+++ b/pith/remote.h
@@ -0,0 +1,95 @@
+/*
+ * $Id: remote.h 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_REMOTE_INCLUDED
+#define PITH_REMOTE_INCLUDED
+
+
+#include "../pith/remtype.h"
+#include "../pith/conftype.h"
+
+
+#define REMOTE_DATA_TYPE TYPETEXT
+#define REMOTE_DATA_VERS_NUM 1
+#define REMOTE_ABOOK_SUBTYPE "x-pine-addrbook"
+#define REMOTE_PINERC_SUBTYPE "x-pine-pinerc"
+#define REMOTE_SIG_SUBTYPE "x-pine-sig"
+#define REMOTE_SMIME_SUBTYPE "x-pine-smime"
+
+#define PMAGIC "P#*E@"
+#define METAFILE_VERSION_NUM "01"
+#define SIZEOF_PMAGIC (5)
+#define SIZEOF_SPACE (1)
+#define SIZEOF_VERSION_NUM (2)
+#define TO_FIND_HDR_PMAGIC (0)
+#define TO_FIND_VERSION_NUM (TO_FIND_HDR_PMAGIC + SIZEOF_PMAGIC + SIZEOF_SPACE)
+
+
+/*
+ * For flags below. Also for address book flags in struct adrbk. Some of
+ * these flags are shared so they need to be in the same namespace.
+ */
+#define DEL_FILE 0x00001 /* remove addrbook file in adrbk_close */
+#define DO_REMTRIM 0x00004 /* trim remote data if needed and possible */
+#define USE_OLD_CACHE 0x00008 /* using cache copy, couldn't check if old */
+#define REM_OUTOFDATE 0x00010 /* remote data changed since cached */
+#define NO_STATUSCMD 0x00020 /* imap server doesn't have status command */
+#define NO_META_UPDATE 0x00040 /* don't try to update metafile */
+#define NO_PERM_CACHE 0x00080 /* remove temp cache files when done */
+#define NO_FILE 0x00100 /* use memory (sonofile) instead of a file */
+#define FILE_OUTOFDATE 0x00400 /* addrbook file discovered out of date */
+#define COOKIE_ONE_OK 0x02000 /* cookie with old value of one is ok */
+#define USER_SAID_NO 0x04000 /* user said no when asked about cookie */
+#define USER_SAID_YES 0x08000 /* user said yes when asked about cookie */
+#define BELIEVE_CACHE 0x10000 /* user said yes when asked about cookie */
+
+
+extern char meta_prefix[];
+
+
+/* exported protoypes */
+char *read_remote_pinerc(PINERC_S *, ParsePinerc);
+REMDATA_S *rd_create_remote(RemType, char *, char *, unsigned *, char *, char *);
+REMDATA_S *rd_new_remdata(RemType, char *, char *);
+void rd_free_remdata(REMDATA_S **);
+void rd_trim_remdata(REMDATA_S **);
+void rd_close_remdata(REMDATA_S **);
+int rd_read_metadata(REMDATA_S *);
+void rd_write_metadata(REMDATA_S *, int);
+void rd_update_metadata(REMDATA_S *, char *);
+void rd_open_remote(REMDATA_S *);
+void rd_close_remote(REMDATA_S *);
+int rd_stream_exists(REMDATA_S *);
+int rd_ping_stream(REMDATA_S *);
+void rd_check_readonly_access(REMDATA_S *);
+int rd_init_remote(REMDATA_S *, int);
+int rd_update_local(REMDATA_S *);
+int rd_update_remote(REMDATA_S *, char *);
+void rd_check_remvalid(REMDATA_S *, long);
+int rd_remote_is_readonly(REMDATA_S *);
+int rd_chk_for_hdr_msg(MAILSTREAM **, REMDATA_S *, char **);
+
+/* currently mandatory to implement stubs */
+
+/*
+ * This is used when we try to copy remote config data and find that
+ * the remote folder looks fishy. We ask the user what they want to
+ * do.
+ */
+int rd_prompt_about_forged_remote_data(int,REMDATA_S *, char *);
+
+
+#endif /* PITH_REMOTE_INCLUDED */
diff --git a/pith/remtype.h b/pith/remtype.h
new file mode 100644
index 00000000..a4e983bc
--- /dev/null
+++ b/pith/remtype.h
@@ -0,0 +1,60 @@
+/*
+ * $Id: remtype.h 764 2007-10-23 23:44:49Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_REMTYPE_INCLUDED
+#define PITH_REMTYPE_INCLUDED
+
+#include "../pith/store.h"
+
+
+typedef enum {Loc, RemImap} RemType;
+
+
+typedef enum {ReadOnly, ReadWrite, MaybeRorW, NoAccess, NoExists} AccessType;
+
+
+/* Remote data folder bookkeeping */
+typedef struct remote_data {
+ RemType type;
+ char *rn; /* remote name (name of folder) */
+ char *lf; /* name of local file */
+ STORE_S *sonofile; /* storage object which takes place of lf */
+ AccessType access; /* of remote folder */
+ time_t last_use; /* when remote was last accessed */
+ time_t last_valid_chk;/* when remote valid check was done */
+ time_t last_local_valid_chk;
+ STORE_S *so; /* storage object to use */
+ char read_status; /* R for readonly */
+ unsigned long flags;
+ unsigned long cookie;
+ union type_specific_data {
+ struct imap_remote_data {
+ char *special_hdr; /* header name for this type folder */
+ MAILSTREAM *stream; /* stream to use for remote folder */
+ char *chk_date; /* Date of last message */
+ unsigned long chk_nmsgs; /* Number of messages in folder */
+ unsigned long shouldbe_nmsgs; /* Number which should be in folder */
+ imapuid_t uidvalidity; /* UIDVALIDITY of folder */
+ imapuid_t uidnext; /* UIDNEXT of folder */
+ imapuid_t uid; /* UID of last message in folder */
+ }i;
+ }t;
+} REMDATA_S;
+
+
+/* exported protoypes */
+
+
+#endif /* PITH_REMTYPE_INCLUDED */
diff --git a/pith/repltype.h b/pith/repltype.h
new file mode 100644
index 00000000..b4ccbf46
--- /dev/null
+++ b/pith/repltype.h
@@ -0,0 +1,80 @@
+/*
+ * $Id: repltype.h 769 2007-10-24 00:15:40Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_REPLTYPE_INCLUDED
+#define PITH_REPLTYPE_INCLUDED
+
+
+/*
+ * Cursor position when resuming postponed message.
+ */
+typedef struct redraft_pos_s {
+ char *hdrname; /* header field name, : if in body */
+ long offset; /* offset into header or body */
+} REDRAFT_POS_S;
+
+
+/*
+ * Message Reply control structure
+ */
+typedef struct reply_s {
+ unsigned int pseudo:1;
+ unsigned int forw:1;
+ unsigned int msgno:1;
+ unsigned int uid:1;
+ unsigned int forwarded:1;
+ char *mailbox; /* mailbox handles are valid in */
+ char *origmbox; /* above is canonical name, this is orig */
+ char *prefix; /* string to prepend reply-to text */
+ char *orig_charset;
+ union {
+ long pico_flags; /* Flags to manage pico initialization */
+ struct { /* UID information */
+ imapuid_t validity; /* validity token */
+ imapuid_t *msgs; /* array of reply'd to msgs */
+ } uid;
+ } data;
+} REPLY_S;
+
+
+
+/*
+ * Flag definitions to help control reply header building
+ */
+#define RSF_NONE 0x00
+#define RSF_FORCE_REPLY_TO 0x01
+#define RSF_QUERY_REPLY_ALL 0x02
+#define RSF_FORCE_REPLY_ALL 0x04
+
+
+/*
+ * Flag definitions to help build forwarded bodies
+ */
+#define FWD_NONE 0
+#define FWD_ANON 1
+#define FWD_NESTED 2
+
+
+/*
+ * Flag definitions to control composition of forwarded subject
+ */
+#define FS_NONE 0
+#define FS_CONVERT_QUOTES 1
+
+
+/* exported protoypes */
+
+
+#endif /* PITH_REPLTYPE_INCLUDED */
diff --git a/pith/reply.c b/pith/reply.c
new file mode 100644
index 00000000..03468af3
--- /dev/null
+++ b/pith/reply.c
@@ -0,0 +1,3575 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: reply.c 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/reply.h"
+#include "../pith/send.h"
+#include "../pith/init.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/remote.h"
+#include "../pith/status.h"
+#include "../pith/mailview.h"
+#include "../pith/filter.h"
+#include "../pith/newmail.h"
+#include "../pith/bldaddr.h"
+#include "../pith/mailindx.h"
+#include "../pith/mimedesc.h"
+#include "../pith/detach.h"
+#include "../pith/help.h"
+#include "../pith/pipe.h"
+#include "../pith/addrstring.h"
+#include "../pith/news.h"
+#include "../pith/util.h"
+#include "../pith/pattern.h"
+#include "../pith/detoken.h"
+#include "../pith/stream.h"
+#include "../pith/busy.h"
+#include "../pith/readfile.h"
+#include "../pith/text.h"
+#include "../pith/list.h"
+#include "../pith/ablookup.h"
+#include "../pith/mailcmd.h"
+#include "../pith/margin.h"
+
+
+/*
+ * Internal prototypes
+ */
+void bounce_mask_header(char **, char *);
+
+
+int (*pith_opt_replyto_prompt)(void);
+int (*pith_opt_reply_to_all_prompt)(int *);
+
+
+/*
+ * standard type of storage object used for body parts...
+ */
+#define PART_SO_TYPE CharStar
+
+
+char *(*pith_opt_user_agent_prefix)(void);
+
+
+/*
+ * reply_harvest -
+ *
+ * Returns: 1 if addresses successfully copied
+ * 0 on user cancel or error
+ *
+ * Input flags:
+ * RSF_FORCE_REPLY_TO
+ * RSF_QUERY_REPLY_ALL
+ * RSF_FORCE_REPLY_ALL
+ *
+ * Output flags:
+ * RSF_FORCE_REPLY_ALL
+ *
+ */
+int
+reply_harvest(struct pine *ps, long int msgno, char *section, ENVELOPE *env,
+ struct mail_address **saved_from, struct mail_address **saved_to,
+ struct mail_address **saved_cc, struct mail_address **saved_resent,
+ int *flags)
+{
+ ADDRESS *ap, *ap2, *rep_address;
+ int ret = 0, sniff_resent = 0;
+ char *rep_field;
+
+ /*
+ * If Reply-To is same as From just treat it like it was From.
+ * Otherwise, always use the reply-to if we're replying to more
+ * than one msg or say ok to using it, even if it's us.
+ * If there's no reply-to or it's the same as the from, assume
+ * that the user doesn't want to reply to himself, unless there's
+ * nobody else.
+ */
+ if(env->reply_to && !addr_lists_same(env->reply_to, env->from)
+ && (F_ON(F_AUTO_REPLY_TO, ps_global)
+ || ((*flags) & RSF_FORCE_REPLY_TO)
+ || (pith_opt_replyto_prompt && (*pith_opt_replyto_prompt)() == 'y'))){
+ rep_field = "reply-to";
+ rep_address = env->reply_to;
+ }
+ else{
+ rep_field = "From";
+ rep_address = env->from;
+ }
+
+ ap = reply_cp_addr(ps, msgno, section, rep_field, *saved_from,
+ (ADDRESS *) NULL, rep_address, RCA_NOT_US);
+
+ if(ret == 'x') {
+ cmd_cancelled("Reply");
+ return(0);
+ }
+
+ reply_append_addr(saved_from, ap);
+
+ /*--------- check for other recipients ---------*/
+ if(((*flags) & (RSF_FORCE_REPLY_ALL | RSF_QUERY_REPLY_ALL))){
+
+ if((ap = reply_cp_addr(ps, msgno, section, "To", *saved_to,
+ *saved_from, env->to, RCA_NOT_US)) != NULL)
+ reply_append_addr(saved_to, ap);
+
+ if((ap = reply_cp_addr(ps, msgno, section, "Cc", *saved_cc,
+ *saved_from, env->cc, RCA_NOT_US)) != NULL)
+ reply_append_addr(saved_cc, ap);
+
+ /*
+ * In these cases, we either need to look at the resent headers
+ * to include in the reply-to-all, or to decide whether or not
+ * we need to ask the reply-to-all question.
+ */
+ if(((*flags) & RSF_FORCE_REPLY_ALL)
+ || (((*flags) & RSF_QUERY_REPLY_ALL)
+ && ((!(*saved_from) && !(*saved_cc))
+ || (*saved_from && !(*saved_to) && !(*saved_cc))))){
+
+ sniff_resent++;
+ if((ap2 = reply_resent(ps, msgno, section)) != NULL){
+ /*
+ * look for bogus addr entries and replace
+ */
+ if((ap = reply_cp_addr(ps, 0, NULL, NULL, *saved_resent,
+ *saved_from, ap2, RCA_NOT_US)) != NULL)
+
+ reply_append_addr(saved_resent, ap);
+
+ mail_free_address(&ap2);
+ }
+ }
+
+ /*
+ * It makes sense to ask reply-to-all now.
+ */
+ if(((*flags) & RSF_QUERY_REPLY_ALL)
+ && ((*saved_from && (*saved_to || *saved_cc || *saved_resent))
+ || (*saved_cc || *saved_resent))){
+ *flags &= ~RSF_QUERY_REPLY_ALL;
+ if(pith_opt_reply_to_all_prompt
+ && (*pith_opt_reply_to_all_prompt)(flags) < 0){
+ cmd_cancelled("Reply");
+ return(0);
+ }
+ }
+
+ /*
+ * If we just answered yes to the reply-to-all question and
+ * we still haven't collected the resent headers, do so now.
+ */
+ if(((*flags) & RSF_FORCE_REPLY_ALL) && !sniff_resent
+ && (ap2 = reply_resent(ps, msgno, section))){
+ /*
+ * look for bogus addr entries and replace
+ */
+ if((ap = reply_cp_addr(ps, 0, NULL, NULL, *saved_resent,
+ *saved_from, ap2, RCA_NOT_US)) != NULL)
+ reply_append_addr(saved_resent, ap);
+
+ mail_free_address(&ap2);
+ }
+ }
+
+ return(1);
+}
+
+
+/*----------------------------------------------------------------------
+ Return a pointer to a copy of the given address list
+ filtering out those already in the "mask" lists and ourself.
+
+Args: mask1 -- Don't copy if in this list
+ mask2 -- or if in this list
+ source -- List to be copied
+ us_too -- Don't filter out ourself.
+ flags -- RCA_NOT_US copy all addrs except for our own
+ RCA_ALL copy all addrs, including our own
+ RCA_ONLY_US copy only addrs that are our own
+
+ ---*/
+ADDRESS *
+reply_cp_addr(struct pine *ps, long int msgno, char *section, char *field,
+ struct mail_address *mask1, struct mail_address *mask2,
+ struct mail_address *source, int flags)
+{
+ ADDRESS *tmp1, *tmp2, *ret = NULL, **ret_tail;
+
+ /* can only choose one of these flags values */
+ assert(!((flags & RCA_ALL && flags & RCA_ONLY_US)
+ || (flags & RCA_ALL && flags & RCA_NOT_US)
+ || (flags & RCA_ONLY_US && flags & RCA_NOT_US)));
+
+ for(tmp1 = source; msgno && tmp1; tmp1 = tmp1->next)
+ if(tmp1->host && tmp1->host[0] == '.'){
+ char *h, *fields[2];
+
+ fields[0] = field;
+ fields[1] = NULL;
+ if((h = pine_fetchheader_lines(ps ? ps->mail_stream : NULL,
+ msgno, section, fields)) != NULL){
+ char *p, fname[32];
+ int q;
+
+ q = strlen(h);
+
+ strncpy(fname, field, sizeof(fname)-2);
+ fname[sizeof(fname)-2] = '\0';
+ strncat(fname, ":", sizeof(fname)-strlen(fname)-1);
+ fname[sizeof(fname)-1] = '\0';
+
+ for(p = h; (p = strstr(p, fname)) != NULL; )
+ rplstr(p, q-(p-h), strlen(fname), ""); /* strip field strings */
+
+ sqznewlines(h); /* blat out CR's & LF's */
+ for(p = h; (p = strchr(p, TAB)) != NULL; )
+ *p++ = ' '; /* turn TABs to whitespace */
+
+ if(*h){
+ long l;
+ size_t ll;
+
+ ret = (ADDRESS *) fs_get(sizeof(ADDRESS));
+ memset(ret, 0, sizeof(ADDRESS));
+
+ /* get rid of leading white space */
+ for(p = h; *p == SPACE; p++)
+ ;
+
+ if(p != h){
+ memmove(h, p, l = strlen(p));
+ h[l] = '\0';
+ }
+
+ /* base64 armor plate the gunk to protect against
+ * c-client quoting in output.
+ */
+ p = (char *) rfc822_binary(h, strlen(h),
+ (unsigned long *) &l);
+ sqznewlines(p);
+ fs_give((void **) &h);
+ /*
+ * Seems like the 4 ought to be a 2, but I'll leave it
+ * to be safe, in case something else adds 2 chars later.
+ */
+ ll = strlen(p) + 4;
+ ret->mailbox = (char *) fs_get(ll * sizeof(char));
+ snprintf(ret->mailbox, ll, "&%s", p);
+ ret->mailbox[ll-1] = '\0';
+ fs_give((void **) &p);
+ ret->host = cpystr(RAWFIELD);
+ }
+ }
+
+ return(ret);
+ }
+
+ ret_tail = &ret;
+ for(source = first_addr(source); source; source = source->next){
+ for(tmp1 = first_addr(mask1); tmp1; tmp1 = tmp1->next)
+ if(address_is_same(source, tmp1)) /* it is in mask1, skip it */
+ break;
+
+ for(tmp2 = first_addr(mask2); !tmp1 && tmp2; tmp2 = tmp2->next)
+ if(address_is_same(source, tmp2)) /* it is in mask2, skip it */
+ break;
+
+ /*
+ * If there's no match in masks and this address satisfies the
+ * flags requirement, copy it.
+ */
+ if(!tmp1 && !tmp2 /* no mask match */
+ && ((flags & RCA_ALL) /* including everybody */
+ || (flags & RCA_ONLY_US && address_is_us(source, ps))
+ || (flags & RCA_NOT_US && !address_is_us(source, ps)))){
+ tmp1 = source->next;
+ source->next = NULL; /* only copy one addr! */
+ *ret_tail = rfc822_cpy_adr(source);
+ ret_tail = &(*ret_tail)->next;
+ source->next = tmp1; /* restore rest of list */
+ }
+ }
+
+ return(ret);
+}
+
+
+ACTION_S *
+set_role_from_msg(struct pine *ps, long int rflags, long int msgno, char *section)
+{
+ ACTION_S *role = NULL;
+ PAT_S *pat = NULL;
+ SEARCHSET *ss = NULL;
+ PAT_STATE pstate;
+
+ if(!nonempty_patterns(rflags, &pstate))
+ return(role);
+
+ if(msgno > 0L){
+ ss = mail_newsearchset();
+ ss->first = ss->last = (unsigned long)msgno;
+ }
+
+ /* Go through the possible roles one at a time until we get a match. */
+ pat = first_pattern(&pstate);
+
+ /* calculate this message's score if needed */
+ if(ss && pat && scores_are_used(SCOREUSE_GET) & SCOREUSE_ROLES &&
+ get_msg_score(ps->mail_stream, msgno) == SCORE_UNDEF)
+ (void)calculate_some_scores(ps->mail_stream, ss, 0);
+
+ while(!role && pat){
+ if(match_pattern(pat->patgrp, ps->mail_stream, ss, section,
+ get_msg_score, SE_NOSERVER|SE_NOPREFETCH)){
+ if(!pat->action || pat->action->bogus)
+ break;
+
+ role = pat->action;
+ }
+ else
+ pat = next_pattern(&pstate);
+ }
+
+ if(ss)
+ mail_free_searchset(&ss);
+
+ return(role);
+}
+
+
+/*
+ * reply_seed - fill in reply header
+ *
+ */
+void
+reply_seed(struct pine *ps, ENVELOPE *outgoing, ENVELOPE *env,
+ struct mail_address *saved_from, struct mail_address *saved_to,
+ struct mail_address *saved_cc, struct mail_address *saved_resent,
+ char **fcc, int replytoall, char **errmsg)
+{
+ ADDRESS **to_tail, **cc_tail;
+
+ to_tail = &outgoing->to;
+ cc_tail = &outgoing->cc;
+
+ if(saved_from){
+ /* Put Reply-To or From in To. */
+ *to_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->to,
+ (ADDRESS *) NULL, saved_from, RCA_ALL);
+ /* and the rest in cc */
+ if(replytoall){
+ *cc_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc,
+ outgoing->to, saved_to, RCA_ALL);
+ while(*cc_tail) /* stay on last address */
+ cc_tail = &(*cc_tail)->next;
+
+ *cc_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc,
+ outgoing->to, saved_cc, RCA_ALL);
+ while(*cc_tail)
+ cc_tail = &(*cc_tail)->next;
+
+ *cc_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc,
+ outgoing->to, saved_resent, RCA_ALL);
+ }
+ }
+ else if(saved_to){
+ /* No From (maybe from us), put To in To. */
+ *to_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->to,
+ (ADDRESS *)NULL, saved_to, RCA_ALL);
+ /* and the rest in cc */
+ if(replytoall){
+ *cc_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc,
+ outgoing->to, saved_cc, RCA_ALL);
+ while(*cc_tail)
+ cc_tail = &(*cc_tail)->next;
+
+ *cc_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->cc,
+ outgoing->to, saved_resent, RCA_ALL);
+ }
+ }
+ else{
+ /* No From or To, put everything else in To if replytoall, */
+ if(replytoall){
+ *to_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->to,
+ (ADDRESS *) NULL, saved_cc, RCA_ALL);
+ while(*to_tail)
+ to_tail = &(*to_tail)->next;
+
+ *to_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->to,
+ (ADDRESS *) NULL, saved_resent, RCA_ALL);
+ }
+ /* else, reply to original From which must be us */
+ else{
+ /*
+ * Put self in To if in original From.
+ */
+ if(!outgoing->newsgroups)
+ *to_tail = reply_cp_addr(ps, 0, NULL, NULL, outgoing->to,
+ (ADDRESS *) NULL, env->from, RCA_ALL);
+ }
+ }
+
+ /* add any missing personal data */
+ reply_fish_personal(outgoing, env);
+
+ /* get fcc */
+ if(fcc && outgoing->to && outgoing->to->host[0] != '.'){
+ *fcc = get_fcc_based_on_to(outgoing->to);
+ }
+ else if(fcc && outgoing->newsgroups){
+ char *newsgroups_returned = NULL;
+ int rv;
+
+ rv = news_grouper(outgoing->newsgroups, &newsgroups_returned, errmsg, fcc, NULL);
+ if(rv != -1 &&
+ strcmp(outgoing->newsgroups, newsgroups_returned)){
+ fs_give((void **)&outgoing->newsgroups);
+ outgoing->newsgroups = newsgroups_returned;
+ }
+ else
+ fs_give((void **) &newsgroups_returned);
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Test the given address lists for equivalence
+
+Args: x -- First address list for comparison
+ y -- Second address for comparison
+
+ ---*/
+int
+addr_lists_same(struct mail_address *x, struct mail_address *y)
+{
+ for(x = first_addr(x), y = first_addr(y);
+ x && y;
+ x = first_addr(x->next), y = first_addr(y->next)){
+ if(!address_is_same(x, y))
+ return(0);
+ }
+
+ return(!x && !y); /* true if ran off both lists */
+}
+
+
+/*----------------------------------------------------------------------
+ Test the given address against those in the given envelope's to, cc
+
+Args: addr -- address for comparison
+ env -- envelope to compare against
+
+ ---*/
+int
+addr_in_env(struct mail_address *addr, ENVELOPE *env)
+{
+ ADDRESS *ap;
+
+ for(ap = env ? env->to : NULL; ap; ap = ap->next)
+ if(address_is_same(addr, ap))
+ return(1);
+
+ for(ap = env ? env->cc : NULL; ap; ap = ap->next)
+ if(address_is_same(addr, ap))
+ return(1);
+
+ return(0); /* not found! */
+}
+
+
+/*----------------------------------------------------------------------
+ Add missing personal info dest from src envelope
+
+Args: dest -- envelope to add personal info to
+ src -- envelope to get personal info from
+
+NOTE: This is just kind of a courtesy function. It's really not adding
+ anything needed to get the mail thru, but it is nice for the user
+ under some odd circumstances.
+ ---*/
+void
+reply_fish_personal(ENVELOPE *dest, ENVELOPE *src)
+{
+ ADDRESS *da, *sa;
+
+ for(da = dest ? dest->to : NULL; da; da = da->next){
+ if(da->personal && !da->personal[0])
+ fs_give((void **)&da->personal);
+
+ for(sa = src ? src->to : NULL; sa && !da->personal ; sa = sa->next)
+ if(address_is_same(da, sa) && sa->personal && sa->personal[0])
+ da->personal = cpystr(sa->personal);
+
+ for(sa = src ? src->cc : NULL; sa && !da->personal; sa = sa->next)
+ if(address_is_same(da, sa) && sa->personal && sa->personal[0])
+ da->personal = cpystr(sa->personal);
+ }
+
+ for(da = dest ? dest->cc : NULL; da; da = da->next){
+ if(da->personal && !da->personal[0])
+ fs_give((void **)&da->personal);
+
+ for(sa = src ? src->to : NULL; sa && !da->personal; sa = sa->next)
+ if(address_is_same(da, sa) && sa->personal && sa->personal[0])
+ da->personal = cpystr(sa->personal);
+
+ for(sa = src ? src->cc : NULL; sa && !da->personal; sa = sa->next)
+ if(address_is_same(da, sa) && sa->personal && sa->personal[0])
+ da->personal = cpystr(sa->personal);
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Given a header field and envelope, build "References: " header data
+
+Args:
+
+Returns:
+ ---*/
+char *
+reply_build_refs(ENVELOPE *env)
+{
+ int len, id_len, first_ref_len = 0, foldslop;
+ char *p, *refs = NULL, *h = env->references;
+ char *first_ref = NULL, *tail_refs = NULL;
+
+
+ if(!(env->message_id && (id_len = strlen(env->message_id))))
+ return(NULL);
+
+ if(h){
+ /*
+ * The length we have to work with doesn't seem to appear in any
+ * standards. Steve Jones says that in comp.news discussions he
+ * has seen 1024 as the longest length of a header value.
+ * In the inn news source we find MAXHEADERSIZE = 1024. It appears
+ * that is the maximum length of the header value, including
+ * newlines for folded lines (that is, the newlines are counted).
+ * We'll be conservative and figure every reference will take up a
+ * line of its own when we fold. We'll also count 2 for CRLF instead
+ * of just one for LF just to be safe. hubert 2001-jan
+ * J.B. Moreno <planb@newsreaders.com> says "The server limit is
+ * more commonly encountered at 999/1000 bytes [...]". So we'll
+ * back off to 999 instead of 1024.
+ */
+#define MAXHEADERSIZE (999)
+
+ /* count the total number of potential folds, max of 2 bytes each */
+ for(foldslop = 2, p = h; (p = strstr(p+1, "> <")); )
+ foldslop += 2;
+
+ if((len=strlen(h)) + 1+id_len + foldslop >= MAXHEADERSIZE
+ && (p = strstr(h, "> <"))){
+ /*
+ * If the references line is so long that we are going to have
+ * to delete some of the references, delete the 2nd, 3rd, ...
+ * We don't want to delete the first message in the thread.
+ */
+ p[1] = '\0';
+ first_ref = cpystr(h);
+ first_ref_len = strlen(first_ref)+1; /* len includes space */
+ p[1] = ' ';
+ tail_refs = p+2;
+ /* get rid of 2nd, 3rd, ... until it fits */
+ while((len=strlen(tail_refs)) + first_ref_len + 1+id_len +
+ foldslop >= MAXHEADERSIZE
+ && (p = strstr(tail_refs, "> <"))){
+ tail_refs = p + 2;
+ foldslop -= 2;
+ }
+
+ /*
+ * If the individual references are seriously long, somebody
+ * is messing with us and we don't care if it works right.
+ */
+ if((len=strlen(tail_refs)) + first_ref_len + 1+id_len +
+ foldslop >= MAXHEADERSIZE){
+ first_ref_len = len = 0;
+ tail_refs = NULL;
+ if(first_ref)
+ fs_give((void **)&first_ref);
+ }
+ }
+ else
+ tail_refs = h;
+
+ refs = (char *)fs_get((first_ref_len + 1+id_len + len + 1) *
+ sizeof(char));
+ snprintf(refs, first_ref_len + 1+id_len + len + 1, "%s%s%s%s%s",
+ first_ref ? first_ref : "",
+ first_ref ? " " : "",
+ tail_refs ? tail_refs : "",
+ tail_refs ? " " : "",
+ env->message_id);
+ refs[first_ref_len + 1+id_len + len] = '\0';
+ }
+
+ if(!refs && id_len)
+ refs = cpystr(env->message_id);
+
+ if(first_ref)
+ fs_give((void **)&first_ref);
+
+ return(refs);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Snoop for any Resent-* headers, and return an ADDRESS list
+
+Args: stream --
+ msgno --
+
+Returns: either NULL if no Resent-* or parsed ADDRESS struct list
+ ---*/
+ADDRESS *
+reply_resent(struct pine *pine_state, long int msgno, char *section)
+{
+#define RESENTFROM 0
+#define RESENTTO 1
+#define RESENTCC 2
+ ADDRESS *rlist = NULL, **a, **b;
+ char *hdrs, *values[RESENTCC+1];
+ int i;
+ static char *fields[] = {"Resent-From", "Resent-To", "Resent-Cc", NULL};
+ static char *fakedomain = "@";
+
+ if((hdrs = pine_fetchheader_lines(pine_state->mail_stream,
+ msgno, section, fields)) != NULL){
+ memset(values, 0, (RESENTCC+1) * sizeof(char *));
+ simple_header_parse(hdrs, fields, values);
+ for(i = RESENTFROM; i <= RESENTCC; i++)
+ rfc822_parse_adrlist(&rlist, values[i],
+ (F_ON(F_COMPOSE_REJECTS_UNQUAL, pine_state))
+ ? fakedomain : pine_state->maildomain);
+
+ /* pare dup's ... */
+ for(a = &rlist; *a; a = &(*a)->next) /* compare every address */
+ for(b = &(*a)->next; *b; ) /* to the others */
+ if(address_is_same(*a, *b)){
+ ADDRESS *t = *b;
+
+ if(!(*a)->personal){ /* preserve personal name */
+ (*a)->personal = (*b)->personal;
+ (*b)->personal = NULL;
+ }
+
+ *b = t->next;
+ t->next = NULL;
+ mail_free_address(&t);
+ }
+ else
+ b = &(*b)->next;
+ }
+
+ if(hdrs)
+ fs_give((void **) &hdrs);
+
+ return(rlist);
+}
+
+
+/*----------------------------------------------------------------------
+ Format and return subject suitable for the reply command
+
+Args: subject -- subject to build reply subject for
+ buf -- buffer to use for writing. If non supplied, alloc one.
+ buflen -- length of buf if supplied, else ignored
+
+Returns: with either "Re:" prepended or not, if already there.
+ returned string is allocated.
+ ---*/
+char *
+reply_subject(char *subject, char *buf, size_t buflen)
+{
+ size_t l = (subject && *subject) ? 4*strlen(subject) : 10;
+ char *tmp = fs_get(l + 1), *decoded, *p;
+
+ if(!buf){
+ buflen = l + 5;
+ buf = fs_get(buflen);
+ }
+
+ /* decode any 8bit into tmp buffer */
+ decoded = (char *) rfc1522_decode_to_utf8((unsigned char *)tmp, l+1, subject);
+
+ buf[0] = '\0';
+ if(decoded /* already "re:" ? */
+ && (decoded[0] == 'R' || decoded[0] == 'r')
+ && (decoded[1] == 'E' || decoded[1] == 'e')){
+
+ if(decoded[2] == ':')
+ snprintf(buf, buflen, "%.*s", buflen-1, subject);
+ else if((decoded[2] == '[') && (p = strchr(decoded, ']'))){
+ p++;
+ while(*p && isspace((unsigned char)*p)) p++;
+ if(p[0] == ':')
+ snprintf(buf, buflen, "%.*s", buflen-1, subject);
+ }
+ }
+
+ if(!buf[0])
+ snprintf(buf, buflen, "Re: %.*s", buflen-1,
+ (subject && *subject) ? subject : "your mail");
+
+ buf[buflen-1] = '\0';
+
+ fs_give((void **) &tmp);
+ return(buf);
+}
+
+
+/*----------------------------------------------------------------------
+ return initials for the given personal name
+
+Args: name -- Personal name to extract initials from
+
+Returns: pointer to name overwritten with initials
+ ---*/
+char *
+reply_quote_initials(char *name)
+{
+ char *s = name,
+ *w = name;
+
+ /* while there are still characters to look at */
+ while(s && *s){
+ /* skip to next initial */
+ while(*s && isspace((unsigned char) *s))
+ s++;
+
+ /* skip over cruft like single quotes */
+ while(*s && !isalnum((unsigned char) *s))
+ s++;
+
+ /* copy initial */
+ if(*s)
+ *w++ = *s++;
+
+ /* skip to end of this piece of name */
+ while(*s && !isspace((unsigned char) *s))
+ s++;
+ }
+
+ if(w)
+ *w = '\0';
+
+ return(name);
+}
+
+/*
+ * There is an assumption that MAX_SUBSTITUTION is <= the size of the
+ * tokens being substituted for (only in the size of buf below).
+ */
+#define MAX_SUBSTITUTION 6
+#define MAX_PREFIX 63
+static char *from_token = "_FROM_";
+static char *nick_token = "_NICK_";
+static char *init_token = "_INIT_";
+
+/*----------------------------------------------------------------------
+ return a quoting string, "> " by default, for replied text
+
+Args: env -- envelope of message being replied to
+
+Returns: malloc'd array containing quoting string, freed by caller
+ ---*/
+char *
+reply_quote_str(ENVELOPE *env)
+{
+ char *prefix, *repl, *p, buf[MAX_PREFIX+1], pbf[MAX_SUBSTITUTION+1];
+
+ strncpy(buf, ps_global->VAR_REPLY_STRING, sizeof(buf)-1);
+ buf[sizeof(buf)-1] = '\0';
+
+ /* set up the prefix to quote included text */
+ if((p = strstr(buf, from_token)) != NULL){
+ repl = (env && env->from && env->from->mailbox) ? env->from->mailbox
+ : "";
+ strncpy(pbf, repl, sizeof(pbf)-1);
+ pbf[sizeof(pbf)-1] = '\0';;
+ rplstr(p, sizeof(buf)-(p-buf), strlen(from_token), pbf);
+ }
+
+ if((p = strstr(buf, nick_token)) != NULL){
+ repl = (env &&
+ env->from &&
+ env->from &&
+ get_nickname_from_addr(env->from, tmp_20k_buf, 1000))
+ ? tmp_20k_buf : "";
+ strncpy(pbf, repl, sizeof(pbf)-1);
+ pbf[sizeof(pbf)-1] = '\0';;
+ rplstr(p, sizeof(buf)-(p-buf), strlen(nick_token), pbf);
+ }
+
+ if((p = strstr(buf, init_token)) != NULL){
+ char *q = NULL;
+ char buftmp[MAILTMPLEN];
+
+ snprintf(buftmp, sizeof(buftmp), "%.200s",
+ (env && env->from && env->from->personal) ? env->from->personal : "");
+ buftmp[sizeof(buftmp)-1] = '\0';
+
+ repl = (env && env->from && env->from->personal)
+ ? reply_quote_initials(q = cpystr((char *)rfc1522_decode_to_utf8(
+ (unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, buftmp)))
+ : "";
+
+ istrncpy(pbf, repl, sizeof(pbf)-1);
+ pbf[sizeof(pbf)-1] = '\0';;
+ rplstr(p, sizeof(buf)-(p-buf), strlen(init_token), pbf);
+ if(q)
+ fs_give((void **)&q);
+ }
+
+ prefix = removing_quotes(cpystr(buf));
+
+ return(prefix);
+}
+
+int
+reply_quote_str_contains_tokens(void)
+{
+ return(ps_global->VAR_REPLY_STRING && ps_global->VAR_REPLY_STRING[0] &&
+ (strstr(ps_global->VAR_REPLY_STRING, from_token) ||
+ strstr(ps_global->VAR_REPLY_STRING, nick_token) ||
+ strstr(ps_global->VAR_REPLY_STRING, init_token)));
+}
+
+
+/*----------------------------------------------------------------------
+ Build the body for the message number/part being replied to
+
+ Args:
+
+ Result: BODY structure suitable for sending
+
+ ----------------------------------------------------------------------*/
+BODY *
+reply_body(MAILSTREAM *stream, ENVELOPE *env, struct mail_bodystruct *orig_body,
+ long int msgno, char *sect_prefix, void *msgtext, char *prefix,
+ int plustext, ACTION_S *role, int toplevel, REDRAFT_POS_S **redraft_pos)
+{
+ char *p, *sig = NULL, *section, sect_buf[256];
+ BODY *body = NULL, *tmp_body = NULL;
+ PART *part;
+ gf_io_t pc;
+ int impl, template_len = 0, leave_cursor_at_top = 0, reply_raw_body = 0;
+
+ if(sect_prefix)
+ snprintf(section = sect_buf, sizeof(sect_buf), "%.*s.1", sizeof(sect_buf)-1, sect_prefix);
+ else
+ section = "1";
+
+ sect_buf[sizeof(sect_buf)-1] = '\0';
+
+ if(ps_global->full_header == 2
+ && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global))
+ reply_raw_body = 1;
+
+ gf_set_so_writec(&pc, (STORE_S *) msgtext);
+
+ if(toplevel){
+ char *filtered;
+
+ impl = 0;
+ filtered = detoken(role, env, 0,
+ F_ON(F_SIG_AT_BOTTOM, ps_global) ? 1 : 0,
+ 0, redraft_pos, &impl);
+ if(filtered){
+ if(*filtered){
+ so_puts((STORE_S *)msgtext, filtered);
+ if(impl == 1)
+ template_len = strlen(filtered);
+ else if(impl == 2)
+ leave_cursor_at_top++;
+ }
+
+ fs_give((void **)&filtered);
+ }
+ else
+ impl = 1;
+ }
+ else
+ impl = 1;
+
+ if(toplevel &&
+ (sig = reply_signature(role, env, redraft_pos, &impl)) &&
+ F_OFF(F_SIG_AT_BOTTOM, ps_global)){
+
+ /*
+ * If CURSORPOS was set explicitly in sig_file, and there was a
+ * template file before that, we need to adjust the offset by the
+ * length of the template file. However, if the template had
+ * a set CURSORPOS in it then impl was 2 before getting to the
+ * signature, so offset wouldn't have been reset by the signature
+ * CURSORPOS and offset would already be correct. That case will
+ * be ok here because template_len will be 0 and adding it does
+ * nothing. If template
+ * didn't have CURSORPOS in it, then impl was 1 and got set to 2
+ * by the CURSORPOS in the sig. In that case we have to adjust the
+ * offset. That's what the next line does. It adjusts it if
+ * template_len is nonzero and if CURSORPOS was set in sig_file.
+ */
+ if(impl == 2)
+ (*redraft_pos)->offset += template_len;
+
+ if(*sig)
+ so_puts((STORE_S *)msgtext, sig);
+
+ /*
+ * Set sig to NULL because we've already used it. If SIG_AT_BOTTOM
+ * is set, we won't have used it yet and want it to be non-NULL.
+ */
+ fs_give((void **)&sig);
+ }
+
+ /*
+ * Only put cursor in sig if there is a cursorpos there but not
+ * one in the template, and sig-at-bottom.
+ */
+ if(!(sig && impl == 2 && !leave_cursor_at_top))
+ leave_cursor_at_top++;
+
+ if(plustext){
+ if(!orig_body
+ || orig_body->type == TYPETEXT
+ || reply_raw_body
+ || F_OFF(F_ATTACHMENTS_IN_REPLY, ps_global)){
+ char *charset = NULL;
+
+ /*------ Simple text-only message ----*/
+ body = mail_newbody();
+ body->type = TYPETEXT;
+ body->contents.text.data = msgtext;
+ reply_delimiter(env, role, pc);
+ if(F_ON(F_INCLUDE_HEADER, ps_global))
+ reply_forward_header(stream, msgno, sect_prefix,
+ env, pc, prefix);
+
+ if(!orig_body || reply_raw_body || reply_body_text(orig_body, &tmp_body)){
+ BODY *bodyp = NULL;
+
+ bodyp = reply_raw_body ? NULL : tmp_body;
+
+ /*
+ * We set the charset in the outgoing message to the same
+ * as the one in the message we're replying to unless it
+ * is the unknown charset. We do that in order to attempt
+ * to preserve the same charset in the reply if possible.
+ * It may be safer to just set it to whatever we want instead
+ * but then the receiver may not be able to read it.
+ */
+ if(bodyp
+ && (charset = parameter_val(bodyp->parameter, "charset"))
+ && strucmp(charset, UNKNOWN_CHARSET))
+ set_parameter(&body->parameter, "charset", charset);
+
+ if(charset)
+ fs_give((void **) &charset);
+
+ get_body_part_text(stream, bodyp, msgno,
+ bodyp ? (p = body_partno(stream, msgno, bodyp))
+ : sect_prefix,
+ 0L, pc, prefix, NULL, GBPT_NONE);
+ if(bodyp && p)
+ fs_give((void **) &p);
+ }
+ else{
+ gf_puts(NEWLINE, pc);
+ gf_puts(" [NON-Text Body part not included]", pc);
+ gf_puts(NEWLINE, pc);
+ }
+ }
+ else if(orig_body->type == TYPEMULTIPART){
+ /*------ Message is Multipart ------*/
+ if(orig_body->subtype
+ && !strucmp(orig_body->subtype, "signed")
+ && orig_body->nested.part){
+ /* operate only on the signed part */
+ body = reply_body(stream, env,
+ &orig_body->nested.part->body,
+ msgno, section, msgtext, prefix,
+ plustext, role, 0, redraft_pos);
+ }
+ else if(orig_body->subtype
+ && !strucmp(orig_body->subtype, "alternative")){
+ /* Set up the simple text reply */
+ body = mail_newbody();
+ body->type = TYPETEXT;
+ body->contents.text.data = msgtext;
+
+ if(reply_body_text(orig_body, &tmp_body)){
+ reply_delimiter(env, role, pc);
+ if(F_ON(F_INCLUDE_HEADER, ps_global))
+ reply_forward_header(stream, msgno, sect_prefix,
+ env, pc, prefix);
+
+ get_body_part_text(stream, tmp_body, msgno,
+ p = body_partno(stream,msgno,tmp_body),
+ 0L, pc, prefix, NULL, GBPT_NONE);
+ if(p)
+ fs_give((void **) &p);
+ }
+ else
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ "No suitable multipart text found for inclusion!");
+ }
+ else{
+ body = copy_body(NULL, orig_body);
+
+ /*
+ * whatever subtype it is, demote it
+ * to plain old MIXED.
+ */
+ if(body->subtype)
+ fs_give((void **) &body->subtype);
+
+ body->subtype = cpystr("Mixed");
+
+ if(body->nested.part &&
+ body->nested.part->body.type == TYPETEXT) {
+ char *new_charset = NULL;
+
+ /*---- First part of the message is text -----*/
+ body->nested.part->body.contents.text.data = msgtext;
+ if(body->nested.part->body.subtype &&
+ strucmp(body->nested.part->body.subtype, "Plain")){
+ fs_give((void **)&body->nested.part->body.subtype);
+ body->nested.part->body.subtype = cpystr("Plain");
+ }
+ reply_delimiter(env, role, pc);
+ if(F_ON(F_INCLUDE_HEADER, ps_global))
+ reply_forward_header(stream, msgno, sect_prefix,
+ env, pc, prefix);
+
+ if(!(get_body_part_text(stream,
+ &orig_body->nested.part->body,
+ msgno, section, 0L, pc, prefix,
+ &new_charset, GBPT_NONE)
+ && fetch_contents(stream, msgno, sect_prefix, body)))
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Error including all message parts"));
+ else if(new_charset)
+ set_parameter(&body->nested.part->body.parameter, "charset", new_charset);
+ }
+ else if(orig_body->nested.part->body.type == TYPEMULTIPART
+ && orig_body->nested.part->body.subtype
+ && !strucmp(orig_body->nested.part->body.subtype,
+ "alternative")
+ && reply_body_text(&orig_body->nested.part->body,
+ &tmp_body)){
+ int partnum;
+
+ reply_delimiter(env, role, pc);
+ if(F_ON(F_INCLUDE_HEADER, ps_global))
+ reply_forward_header(stream, msgno, sect_prefix,
+ env, pc, prefix);
+
+ snprintf(sect_buf, sizeof(sect_buf), "%.*s%s%.*s",
+ sizeof(sect_buf)/2-2,
+ sect_prefix ? sect_prefix : "",
+ sect_prefix ? "." : "",
+ sizeof(sect_buf)/2-2,
+ p = partno(orig_body, tmp_body));
+ sect_buf[sizeof(sect_buf)-1] = '\0';
+ fs_give((void **) &p);
+ get_body_part_text(stream, tmp_body, msgno,
+ sect_buf, 0L, pc, prefix,
+ NULL, GBPT_NONE);
+
+ part = body->nested.part->next;
+ body->nested.part->next = NULL;
+ mail_free_body_part(&body->nested.part);
+ body->nested.part = mail_newbody_part();
+ body->nested.part->body.type = TYPETEXT;
+ body->nested.part->body.subtype = cpystr("Plain");
+ body->nested.part->body.contents.text.data = msgtext;
+ body->nested.part->next = part;
+
+ for(partnum = 2; part != NULL; part = part->next){
+ snprintf(sect_buf, sizeof(sect_buf), "%.*s%s%d",
+ sizeof(sect_buf)/2,
+ sect_prefix ? sect_prefix : "",
+ sect_prefix ? "." : "", partnum++);
+ sect_buf[sizeof(sect_buf)-1] = '\0';
+
+ if(!fetch_contents(stream, msgno,
+ sect_buf, &part->body)){
+ break;
+ }
+ }
+ }
+ else {
+ /*--- Fetch the original pieces ---*/
+ if(!fetch_contents(stream, msgno, sect_prefix, body))
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Error including all message parts"));
+
+ /*--- No text part, create a blank one ---*/
+ part = mail_newbody_part();
+ part->next = body->nested.part;
+ body->nested.part = part;
+ part->body.contents.text.data = msgtext;
+ }
+ }
+ }
+ else{
+ /*---- Single non-text message of some sort ----*/
+ body = mail_newbody();
+ body->type = TYPEMULTIPART;
+ part = mail_newbody_part();
+ body->nested.part = part;
+
+ /*--- The first part, a blank text part to be edited ---*/
+ part->body.type = TYPETEXT;
+ part->body.contents.text.data = msgtext;
+
+ /*--- The second part, what ever it is ---*/
+ part->next = mail_newbody_part();
+ part = part->next;
+ part->body.id = generate_message_id();
+ copy_body(&(part->body), orig_body);
+
+ /*
+ * the idea here is to fetch part into storage object
+ */
+ if((part->body.contents.text.data = (void *) so_get(PART_SO_TYPE,
+ NULL,EDIT_ACCESS)) != NULL){
+ if((p = pine_mail_fetch_body(stream, msgno, section,
+ &part->body.size.bytes, NIL)) != NULL){
+ so_nputs((STORE_S *)part->body.contents.text.data,
+ p, part->body.size.bytes);
+ }
+ else
+ mail_free_body(&body);
+ }
+ else
+ mail_free_body(&body);
+ }
+ }
+ else{
+ /*--------- No text included --------*/
+ body = mail_newbody();
+ body->type = TYPETEXT;
+ body->contents.text.data = msgtext;
+ }
+
+ if(!leave_cursor_at_top){
+ long cnt = 0L;
+ unsigned char c;
+
+ /* rewind and count chars to start of sig file */
+ so_seek((STORE_S *)msgtext, 0L, 0);
+ while(so_readc(&c, (STORE_S *)msgtext))
+ cnt++;
+
+ if(!*redraft_pos){
+ *redraft_pos = (REDRAFT_POS_S *)fs_get(sizeof(**redraft_pos));
+ memset((void *)*redraft_pos, 0,sizeof(**redraft_pos));
+ (*redraft_pos)->hdrname = cpystr(":");
+ }
+
+ /*
+ * If explicit cursor positioning in sig file,
+ * add offset to start of sig file plus offset into sig file.
+ * Else, just offset to start of sig file.
+ */
+ (*redraft_pos)->offset += cnt;
+ }
+
+ if(sig){
+ if(*sig)
+ so_puts((STORE_S *)msgtext, sig);
+
+ fs_give((void **)&sig);
+ }
+
+ gf_clear_so_writec((STORE_S *) msgtext);
+
+ return(body);
+}
+
+
+/*
+ * reply_part - first replyable multipart of a multipart.
+ */
+int
+reply_body_text(struct mail_bodystruct *body, struct mail_bodystruct **new_body)
+{
+ if(body){
+ switch(body->type){
+ case TYPETEXT :
+ *new_body = body;
+ return(1);
+
+ case TYPEMULTIPART :
+ if(body->subtype && !strucmp(body->subtype, "alternative")){
+ PART *part;
+ int got_one = 0;
+
+ if(ps_global->force_prefer_plain
+ || (!ps_global->force_no_prefer_plain
+ && F_ON(F_PREFER_PLAIN_TEXT, ps_global))){
+ for(part = body->nested.part; part; part = part->next)
+ if((!part->body.type || part->body.type == TYPETEXT)
+ && (!part->body.subtype
+ || !strucmp(part->body.subtype, "plain"))){
+ *new_body = &part->body;
+ return(1);
+ }
+ }
+
+ /*
+ * Else choose last alternative among plain or html parts.
+ * Perhaps we should really be using mime_show() to make this
+ * potentially more general than just plain or html.
+ */
+ for(part = body->nested.part; part; part = part->next){
+ if((!part->body.type || part->body.type == TYPETEXT)
+ && ((!part->body.subtype || !strucmp(part->body.subtype, "plain"))
+ ||
+ (part->body.subtype && !strucmp(part->body.subtype, "html")))){
+ got_one++;
+ *new_body = &part->body;
+ }
+ }
+
+ if(got_one)
+ return(1);
+ }
+ else if(body->nested.part)
+ /* NOTE: we're only interested in "first" part of mixed */
+ return(reply_body_text(&body->nested.part->body, new_body));
+
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return(0);
+}
+
+
+char *
+reply_signature(ACTION_S *role, ENVELOPE *env, REDRAFT_POS_S **redraft_pos, int *impl)
+{
+ char *sig;
+ size_t l;
+
+ sig = detoken(role, env,
+ 2, F_ON(F_SIG_AT_BOTTOM, ps_global) ? 0 : 1, 1,
+ redraft_pos, impl);
+
+ if(F_OFF(F_SIG_AT_BOTTOM, ps_global) && (!sig || !*sig)){
+ if(sig)
+ fs_give((void **)&sig);
+
+ l = 2 * strlen(NEWLINE);
+ sig = (char *)fs_get((l+1) * sizeof(char));
+ strncpy(sig, NEWLINE, l);
+ sig[l] = '\0';
+ strncat(sig, NEWLINE, l+1-1-strlen(sig));
+ sig[l] = '\0';
+ return(sig);
+ }
+
+ return(sig);
+}
+
+
+/*
+ * Buf is at least size maxlen+1
+ */
+void
+get_addr_data(ENVELOPE *env, IndexColType type, char *buf, size_t maxlen)
+{
+ ADDRESS *addr = NULL;
+ ADDRESS *last_to = NULL;
+ ADDRESS *first_addr = NULL, *second_addr = NULL;
+ ADDRESS *third_addr = NULL, *fourth_addr = NULL;
+ int cntaddr, l;
+ size_t orig_maxlen;
+ char *p;
+
+ buf[0] = '\0';
+
+ switch(type){
+ case iFrom:
+ addr = env ? env->from : NULL;
+ break;
+
+ case iTo:
+ addr = env ? env->to : NULL;
+ break;
+
+ case iCc:
+ addr = env ? env->cc : NULL;
+ break;
+
+ case iSender:
+ addr = env ? env->sender : NULL;
+ break;
+
+ /*
+ * Recips is To and Cc togeter. We hook the two adrlists together
+ * temporarily.
+ */
+ case iRecips:
+ addr = env ? env->to : NULL;
+ /* Find end of To list */
+ for(last_to = addr; last_to && last_to->next; last_to = last_to->next)
+ ;
+
+ /* Make the end of To list point to cc list */
+ if(last_to)
+ last_to->next = (env ? env->cc : NULL);
+
+ break;
+
+ /*
+ * Initials.
+ */
+ case iInit:
+ if(env && env->from && env->from->personal){
+ char *name, *initials = NULL;
+
+ name = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, env->from->personal);
+ if(name == env->from->personal){
+ strncpy(tmp_20k_buf, name, SIZEOF_20KBUF-1);
+ tmp_20k_buf[SIZEOF_20KBUF - 1] = '\0';
+ name = tmp_20k_buf;
+ }
+
+ if(name && *name){
+ initials = reply_quote_initials(name);
+ iutf8ncpy(buf, initials, maxlen);
+ buf[maxlen] = '\0';
+ }
+ }
+
+ return;
+
+ default:
+ break;
+ }
+
+ orig_maxlen = maxlen;
+
+ first_addr = addr;
+ /* skip over rest of c-client group addr */
+ if(first_addr && first_addr->mailbox && !first_addr->host){
+ for(second_addr = first_addr->next;
+ second_addr && second_addr->host;
+ second_addr = second_addr->next)
+ ;
+
+ if(second_addr && !second_addr->host)
+ second_addr = second_addr->next;
+ }
+ else if(!(first_addr && first_addr->host && first_addr->host[0] == '.'))
+ second_addr = first_addr ? first_addr->next : NULL;
+
+ if(second_addr && second_addr->mailbox && !second_addr->host){
+ for(third_addr = second_addr->next;
+ third_addr && third_addr->host;
+ third_addr = third_addr->next)
+ ;
+
+ if(third_addr && !third_addr->host)
+ third_addr = third_addr->next;
+ }
+ else if(!(second_addr && second_addr->host && second_addr->host[0] == '.'))
+ third_addr = second_addr ? second_addr->next : NULL;
+
+ if(third_addr && third_addr->mailbox && !third_addr->host){
+ for(fourth_addr = third_addr->next;
+ fourth_addr && fourth_addr->host;
+ fourth_addr = fourth_addr->next)
+ ;
+
+ if(fourth_addr && !fourth_addr->host)
+ fourth_addr = fourth_addr->next;
+ }
+ else if(!(third_addr && third_addr->host && third_addr->host[0] == '.'))
+ fourth_addr = third_addr ? third_addr->next : NULL;
+
+ /* Just attempting to make a nice display */
+ if(first_addr && ((first_addr->personal && first_addr->personal[0]) ||
+ (first_addr->mailbox && first_addr->mailbox[0]))){
+ if(second_addr){
+ if((second_addr->personal && second_addr->personal[0]) ||
+ (second_addr->mailbox && second_addr->mailbox[0])){
+ if(third_addr){
+ if((third_addr->personal && third_addr->personal[0]) ||
+ (third_addr->mailbox && third_addr->mailbox[0])){
+ if(fourth_addr)
+ cntaddr = 4;
+ else
+ cntaddr = 3;
+ }
+ else
+ cntaddr = -1;
+ }
+ else
+ cntaddr = 2;
+ }
+ else
+ cntaddr = -1;
+ }
+ else
+ cntaddr = 1;
+ }
+ else
+ cntaddr = -1;
+
+ p = buf;
+ if(cntaddr == 1)
+ a_little_addr_string(first_addr, p, maxlen);
+ else if(cntaddr == 2){
+ a_little_addr_string(first_addr, p, maxlen);
+ maxlen -= (l=strlen(p));
+ p += l;
+ if(maxlen > 7){
+ strncpy(p, " and ", maxlen);
+ maxlen -= 5;
+ p += 5;
+ a_little_addr_string(second_addr, p, maxlen);
+ }
+ }
+ else if(cntaddr == 3){
+ a_little_addr_string(first_addr, p, maxlen);
+ maxlen -= (l=strlen(p));
+ p += l;
+ if(maxlen > 7){
+ strncpy(p, ", ", maxlen);
+ maxlen -= 2;
+ p += 2;
+ a_little_addr_string(second_addr, p, maxlen);
+ maxlen -= (l=strlen(p));
+ p += l;
+ if(maxlen > 7){
+ strncpy(p, ", and ", maxlen);
+ maxlen -= 6;
+ p += 6;
+ a_little_addr_string(third_addr, p, maxlen);
+ }
+ }
+ }
+ else if(cntaddr > 3){
+ a_little_addr_string(first_addr, p, maxlen);
+ maxlen -= (l=strlen(p));
+ p += l;
+ if(maxlen > 7){
+ strncpy(p, ", ", maxlen);
+ maxlen -= 2;
+ p += 2;
+ a_little_addr_string(second_addr, p, maxlen);
+ maxlen -= (l=strlen(p));
+ p += l;
+ if(maxlen > 7){
+ strncpy(p, ", ", maxlen);
+ maxlen -= 2;
+ p += 2;
+ a_little_addr_string(third_addr, p, maxlen);
+ maxlen -= (l=strlen(p));
+ p += l;
+ if(maxlen >= 12)
+ strncpy(p, ", and others", maxlen);
+ else if(maxlen >= 3)
+ strncpy(p, "...", maxlen);
+ }
+ }
+ }
+ else if(addr){
+ char *a_string;
+
+ a_string = addr_list_string(addr, NULL, 0);
+ iutf8ncpy(buf, a_string, maxlen);
+
+ fs_give((void **)&a_string);
+ }
+
+ if(last_to)
+ last_to->next = NULL;
+
+ buf[orig_maxlen] = '\0';
+}
+
+
+/*
+ * Buf is at least size maxlen+1
+ */
+void
+get_news_data(ENVELOPE *env, IndexColType type, char *buf, size_t maxlen)
+{
+ int cntnews = 0, orig_maxlen;
+ char *news = NULL, *p, *q;
+
+ switch(type){
+ case iNews:
+ case iNewsAndTo:
+ case iToAndNews:
+ case iNewsAndRecips:
+ case iRecipsAndNews:
+ news = env ? env->newsgroups : NULL;
+ break;
+
+ case iCurNews:
+ if(ps_global->mail_stream && IS_NEWS(ps_global->mail_stream))
+ news = ps_global->cur_folder;
+
+ break;
+
+ default:
+ break;
+ }
+
+ orig_maxlen = maxlen;
+
+ if(news){
+ q = news;
+ while(isspace((unsigned char)*q))
+ q++;
+
+ if(*q)
+ cntnews++;
+
+ while((q = strindex(q, ',')) != NULL){
+ q++;
+ while(isspace((unsigned char)*q))
+ q++;
+
+ if(*q)
+ cntnews++;
+ else
+ break;
+ }
+ }
+
+ if(cntnews == 1){
+ istrncpy(buf, news, maxlen);
+ buf[maxlen] = '\0';
+ removing_leading_and_trailing_white_space(buf);
+ }
+ else if(cntnews == 2){
+ p = buf;
+ q = news;
+ while(isspace((unsigned char)*q))
+ q++;
+
+ while(maxlen > 0 && *q && !isspace((unsigned char)*q) && *q != ','){
+ *p++ = *q++;
+ maxlen--;
+ }
+
+ if(maxlen > 7){
+ strncpy(p, " and ", maxlen);
+ p += 5;
+ maxlen -= 5;
+ }
+
+ while(isspace((unsigned char)*q) || *q == ',')
+ q++;
+
+ while(maxlen > 0 && *q && !isspace((unsigned char)*q) && *q != ','){
+ *p++ = *q++;
+ maxlen--;
+ }
+
+ *p = '\0';
+
+ istrncpy(tmp_20k_buf, buf, 10000);
+ strncpy(buf, tmp_20k_buf, orig_maxlen);
+ }
+ else if(cntnews > 2){
+ char b[100];
+
+ p = buf;
+ q = news;
+ while(isspace((unsigned char)*q))
+ q++;
+
+ while(maxlen > 0 && *q && !isspace((unsigned char)*q) && *q != ','){
+ *p++ = *q++;
+ maxlen--;
+ }
+
+ *p = '\0';
+ snprintf(b, sizeof(b), " and %d other newsgroups", cntnews-1);
+ b[sizeof(b)-1] = '\0';
+ if(maxlen >= strlen(b))
+ strncpy(p, b, maxlen);
+ else if(maxlen >= 3)
+ strncpy(p, "...", maxlen);
+
+ buf[orig_maxlen] = '\0';
+
+ istrncpy(tmp_20k_buf, buf, 10000);
+ tmp_20k_buf[10000-1] = '\0';
+ strncpy(buf, tmp_20k_buf, orig_maxlen);
+ }
+
+ buf[orig_maxlen] = '\0';
+}
+
+
+/*
+ * Buf is at least size maxlen+1
+ */
+char *
+get_reply_data(ENVELOPE *env, ACTION_S *role, IndexColType type, char *buf, size_t maxlen)
+{
+ char *space = NULL;
+ IndexColType addrtype;
+
+ buf[0] = '\0';
+
+ switch(type){
+ case iRDate: case iSDate: case iSTime:
+ case iS1Date: case iS2Date: case iS3Date: case iS4Date:
+ case iSDateIso: case iSDateIsoS:
+ case iSDateS1: case iSDateS2: case iSDateS3: case iSDateS4:
+ case iSDateTime:
+ case iSDateTimeIso: case iSDateTimeIsoS:
+ case iSDateTimeS1: case iSDateTimeS2: case iSDateTimeS3: case iSDateTimeS4:
+ case iSDateTime24:
+ case iSDateTimeIso24: case iSDateTimeIsoS24:
+ case iSDateTimeS124: case iSDateTimeS224: case iSDateTimeS324: case iSDateTimeS424:
+ case iDateIso: case iDateIsoS: case iTime24: case iTime12:
+ case iDay: case iDayOrdinal: case iDay2Digit:
+ case iMonAbb: case iMonLong: case iMon: case iMon2Digit:
+ case iYear: case iYear2Digit:
+ case iDate: case iLDate:
+ case iTimezone: case iDayOfWeekAbb: case iDayOfWeek:
+ case iPrefDate: case iPrefTime: case iPrefDateTime:
+ if(env && env->date && env->date[0] && maxlen >= 20)
+ date_str((char *) env->date, type, 1, buf, maxlen+1, 0);
+
+ break;
+
+ case iCurDate:
+ case iCurDateIso:
+ case iCurDateIsoS:
+ case iCurTime24:
+ case iCurTime12:
+ case iCurDay:
+ case iCurDay2Digit:
+ case iCurDayOfWeek:
+ case iCurDayOfWeekAbb:
+ case iCurMon:
+ case iCurMon2Digit:
+ case iCurMonLong:
+ case iCurMonAbb:
+ case iCurYear:
+ case iCurYear2Digit:
+ case iCurPrefDate:
+ case iCurPrefDateTime:
+ case iCurPrefTime:
+ case iLstMon:
+ case iLstMon2Digit:
+ case iLstMonLong:
+ case iLstMonAbb:
+ case iLstMonYear:
+ case iLstMonYear2Digit:
+ case iLstYear:
+ case iLstYear2Digit:
+ if(maxlen >= 20)
+ date_str(NULL, type, 1, buf, maxlen+1, 0);
+
+ break;
+
+ case iFrom:
+ case iTo:
+ case iCc:
+ case iSender:
+ case iRecips:
+ case iInit:
+ get_addr_data(env, type, buf, maxlen);
+ break;
+
+ case iRoleNick:
+ if(role && role->nick){
+ strncpy(buf, role->nick, maxlen);
+ buf[maxlen] = '\0';
+ }
+ break;
+
+ case iNewLine:
+ if(maxlen >= strlen(NEWLINE)){
+ strncpy(buf, NEWLINE, maxlen);
+ buf[maxlen] = '\0';
+ }
+ break;
+
+ case iAddress:
+ case iMailbox:
+ if(env && env->from && env->from->mailbox && env->from->mailbox[0] &&
+ strlen(env->from->mailbox) <= maxlen){
+ strncpy(buf, env->from->mailbox, maxlen);
+ buf[maxlen] = '\0';
+ if(type == iAddress &&
+ env->from->host &&
+ env->from->host[0] &&
+ env->from->host[0] != '.' &&
+ strlen(buf) + strlen(env->from->host) + 1 <= maxlen){
+ strncat(buf, "@", maxlen+1-1-strlen(buf));
+ buf[maxlen] = '\0';
+ strncat(buf, env->from->host, maxlen+1-1-strlen(buf));
+ buf[maxlen] = '\0';
+ }
+ }
+
+ break;
+
+ case iNews:
+ case iCurNews:
+ get_news_data(env, type, buf, maxlen);
+ break;
+
+ case iToAndNews:
+ case iNewsAndTo:
+ case iRecipsAndNews:
+ case iNewsAndRecips:
+ if(type == iToAndNews || type == iNewsAndTo)
+ addrtype = iTo;
+ else
+ addrtype = iRecips;
+
+ if(env && env->newsgroups){
+ space = (char *)fs_get((maxlen+1) * sizeof(char));
+ get_news_data(env, type, space, maxlen);
+ }
+
+ get_addr_data(env, addrtype, buf, maxlen);
+
+ if(space && *space && *buf){
+ if(strlen(space) + strlen(buf) + 5 > maxlen){
+ if(strlen(space) > maxlen/2)
+ get_news_data(env, type, space, maxlen - strlen(buf) - 5);
+ else
+ get_addr_data(env, addrtype, buf, maxlen - strlen(space) - 5);
+ }
+
+ if(type == iToAndNews || type == iRecipsAndNews)
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s and %s", buf, space);
+ else
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s and %s", space, buf);
+
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+
+ strncpy(buf, tmp_20k_buf, maxlen);
+ buf[maxlen] = '\0';
+ }
+ else if(space && *space){
+ strncpy(buf, space, maxlen);
+ buf[maxlen] = '\0';
+ }
+
+ if(space)
+ fs_give((void **)&space);
+
+ break;
+
+ case iSubject:
+ if(env && env->subject){
+ size_t n, len;
+ unsigned char *p, *tmp = NULL;
+
+ if((n = 4*strlen(env->subject)) > SIZEOF_20KBUF-1){
+ len = n+1;
+ p = tmp = (unsigned char *)fs_get(len * sizeof(char));
+ }
+ else{
+ len = SIZEOF_20KBUF;
+ p = (unsigned char *)tmp_20k_buf;
+ }
+
+ istrncpy(buf, (char *)rfc1522_decode_to_utf8(p, len, env->subject), maxlen);
+
+ buf[maxlen] = '\0';
+
+ if(tmp)
+ fs_give((void **)&tmp);
+ }
+
+ break;
+
+ case iMsgID:
+ if(env && env->message_id){
+ strncpy(buf, env->message_id, maxlen);
+ buf[maxlen] = '\0';
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ buf[maxlen] = '\0';
+ return(buf);
+}
+
+
+/*
+ * reply_delimiter - output formatted reply delimiter for given envelope
+ * with supplied character writing function.
+ */
+void
+reply_delimiter(ENVELOPE *env, ACTION_S *role, gf_io_t pc)
+{
+#define MAX_DELIM 2000
+ char buf[MAX_DELIM+1];
+ char *p;
+ char *filtered = NULL;
+ int contains_newline_token = 0;
+
+
+ if(!env)
+ return;
+
+ strncpy(buf, ps_global->VAR_REPLY_INTRO, MAX_DELIM);
+ buf[MAX_DELIM] = '\0';
+ /* preserve exact default behavior from before */
+ if(!strcmp(buf, DEFAULT_REPLY_INTRO)){
+ struct date d;
+ int include_date;
+
+ parse_date((char *) env->date, &d);
+ include_date = !(d.day == -1 || d.month == -1 || d.year == -1);
+ if(include_date){
+ gf_puts("On ", pc); /* All delims have... */
+ if(d.wkday != -1){ /* "On day, date month year" */
+ gf_puts(day_abbrev(d.wkday), pc); /* in common */
+ gf_puts(", ", pc);
+ }
+
+ gf_puts(int2string(d.day), pc);
+ (*pc)(' ');
+ gf_puts(month_abbrev(d.month), pc);
+ (*pc)(' ');
+ gf_puts(int2string(d.year), pc);
+ }
+
+ if(env->from
+ && ((env->from->personal && env->from->personal[0])
+ || (env->from->mailbox && env->from->mailbox[0]))){
+ char buftmp[MAILTMPLEN];
+
+ a_little_addr_string(env->from, buftmp, sizeof(buftmp)-1);
+ if(include_date)
+ gf_puts(", ", pc);
+
+ gf_puts(buftmp, pc);
+ gf_puts(" wrote:", pc);
+ }
+ else{
+ if(include_date)
+ gf_puts(", it was written", pc);
+ else
+ gf_puts("It was written", pc);
+ }
+
+ }
+ else{
+ /*
+ * This is here for backwards compatibility. There didn't used
+ * to be a _NEWLINE_ token. The user would enter text that should
+ * all fit on one line and then that was followed by two newlines.
+ * Also, truncation occurs if it is long.
+ * Now, if _NEWLINE_ is not in the text, same thing still works
+ * the same. However, if _NEWLINE_ is in there, then all bets are
+ * off and the user is on his or her own. No automatic newlines
+ * are added, only those that come from the tokens. No truncation
+ * is done, the user is trusted to get it right. Newlines may be
+ * embedded so that the leadin is multi-line.
+ */
+ contains_newline_token = (strstr(buf, "_NEWLINE_") != NULL);
+ filtered = detoken_src(buf, FOR_REPLY_INTRO, env, role,
+ NULL, NULL);
+
+ /* try to truncate if too long */
+ if(!contains_newline_token && filtered && utf8_width(filtered) > 80){
+ int ended_with_colon = 0;
+ int ended_with_quote = 0;
+ int ended_with_quote_colon = 0;
+ int l;
+
+ l = strlen(filtered);
+
+ if(filtered[l-1] == ':'){
+ ended_with_colon = ':';
+ if(filtered[l-2] == QUOTE || filtered[l-2] == '\'')
+ ended_with_quote_colon = filtered[l-2];
+ }
+ else if(filtered[l-1] == QUOTE || filtered[l-1] == '\'')
+ ended_with_quote = filtered[l-1];
+
+ /* try to find space to break at */
+ for(p = &filtered[75]; p > &filtered[60] &&
+ !isspace((unsigned char)*p); p--)
+ ;
+
+ if(!isspace((unsigned char)*p))
+ p = &filtered[75];
+
+ *p++ = '.';
+ *p++ = '.';
+ *p++ = '.';
+ if(ended_with_quote_colon){
+ *p++ = ended_with_quote_colon;
+ *p++ = ':';
+ }
+ else if(ended_with_colon)
+ *p++ = ended_with_colon;
+ else if(ended_with_quote)
+ *p++ = ended_with_quote;
+
+ *p = '\0';
+ }
+
+ if(filtered && *filtered)
+ gf_puts(filtered, pc);
+ }
+
+ /* and end with two newlines unless no leadin at all */
+ if(!contains_newline_token){
+ if(!strcmp(buf, DEFAULT_REPLY_INTRO) || (filtered && *filtered)){
+ gf_puts(NEWLINE, pc);
+ gf_puts(NEWLINE, pc);
+ }
+ }
+
+ if(filtered)
+ fs_give((void **)&filtered);
+}
+
+
+void
+free_redraft_pos(REDRAFT_POS_S **redraft_pos)
+{
+ if(redraft_pos && *redraft_pos){
+ if((*redraft_pos)->hdrname)
+ fs_give((void **)&(*redraft_pos)->hdrname);
+
+ fs_give((void **)redraft_pos);
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Build the body for the message number/part being forwarded as ATTACHMENT
+
+ Args:
+
+ Result: PARTS suitably attached to body
+
+ ----------------------------------------------------------------------*/
+int
+forward_mime_msg(MAILSTREAM *stream, long int msgno, char *section, ENVELOPE *env, struct mail_body_part **partp, void *msgtext)
+{
+ char *tmp_text;
+ unsigned long len;
+ BODY *b;
+
+ *partp = mail_newbody_part();
+ b = &(*partp)->body;
+ b->type = TYPEMESSAGE;
+ b->id = generate_message_id();
+ b->description = cpystr("Forwarded Message");
+ b->nested.msg = mail_newmsg();
+ b->disposition.type = cpystr("inline");
+
+ /*---- Package each message in a storage object ----*/
+ if((b->contents.text.data = (void *) so_get(PART_SO_TYPE,NULL,EDIT_ACCESS))
+ && (tmp_text = mail_fetch_header(stream,msgno,section,NIL,NIL,FT_PEEK))
+ && *tmp_text){
+ so_puts((STORE_S *) b->contents.text.data, tmp_text);
+
+ b->size.bytes = strlen(tmp_text);
+ so_puts((STORE_S *) b->contents.text.data, "\015\012");
+ if((tmp_text = pine_mail_fetch_text (stream,msgno,section,&len,NIL)) != NULL){
+ so_nputs((STORE_S *)b->contents.text.data,tmp_text,(long) len);
+ b->size.bytes += len;
+ return(1);
+ }
+ }
+
+ return(0);
+}
+
+
+/*
+ * forward_delimiter - return delimiter for forwarded text
+ */
+void
+forward_delimiter(gf_io_t pc)
+{
+ gf_puts(NEWLINE, pc);
+ /* TRANSLATORS: When a message is forwarded by the user this is the
+ text that shows where the forwarded part of the message begins. */
+ gf_puts(_("---------- Forwarded message ----------"), pc);
+ gf_puts(NEWLINE, pc);
+}
+
+
+/*----------------------------------------------------------------------
+ Wrapper for header formatting tool
+
+ Args: stream --
+ msgno --
+ env --
+ pc --
+ prefix --
+
+ Result: header suitable for reply/forward text written using "pc"
+
+ ----------------------------------------------------------------------*/
+void
+reply_forward_header(MAILSTREAM *stream, long int msgno, char *part, ENVELOPE *env,
+ gf_io_t pc, char *prefix)
+{
+ int rv;
+ HEADER_S h;
+ char **list, **new_list = NULL;
+
+ list = ps_global->VAR_VIEW_HEADERS;
+
+ /*
+ * If VIEW_HEADERS is set, we should remove BCC from the list so that
+ * the user doesn't inadvertently forward the BCC header.
+ */
+ if(list && list[0]){
+ int i, cnt = 0;
+ char **p;
+
+ while(list[cnt++])
+ ;
+
+ p = new_list = (char **) fs_get((cnt+1) * sizeof(char *));
+
+ for(i=0; list[i]; i++)
+ if(strucmp(list[i], "bcc"))
+ *p++ = cpystr(list[i]);
+
+ *p = NULL;
+
+ if(new_list && new_list[0])
+ list = new_list;
+
+ }
+
+ HD_INIT(&h, list, ps_global->view_all_except, FE_DEFAULT & ~FE_BCC);
+ if((rv = format_header(stream, msgno, part, env, &h,
+ prefix, NULL, FM_NOINDENT, NULL, pc)) != 0){
+ if(rv == 1)
+ gf_puts(" [Error fetching message header data]", pc);
+ }
+ else
+ gf_puts(NEWLINE, pc); /* write header delimiter */
+
+ if(new_list)
+ free_list_array(&new_list);
+}
+
+
+/*----------------------------------------------------------------------
+ Build the subject for the message number being forwarded
+
+ Args: pine_state -- The usual pine structure
+ msgno -- The message number to build subject for
+
+ Result: malloc'd string containing new subject or NULL on error
+
+ ----------------------------------------------------------------------*/
+char *
+forward_subject(ENVELOPE *env, int flags)
+{
+ size_t l;
+ char *p, buftmp[MAILTMPLEN];
+
+ if(!env)
+ return(NULL);
+
+ dprint((9, "checking subject: \"%s\"\n",
+ env->subject ? env->subject : "NULL"));
+
+ if(env->subject && env->subject[0]){ /* add (fwd)? */
+ snprintf(buftmp, sizeof(buftmp), "%s", env->subject);
+ buftmp[sizeof(buftmp)-1] = '\0';
+ /* decode any 8bit (copy to the temp buffer if decoding doesn't) */
+ if(rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
+ SIZEOF_20KBUF, buftmp) == (unsigned char *) buftmp)
+ strncpy(tmp_20k_buf, buftmp, SIZEOF_20KBUF);
+
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+
+ removing_trailing_white_space(tmp_20k_buf);
+ if((l = strlen(tmp_20k_buf)) < 1000 &&
+ (l < 5 || strcmp(tmp_20k_buf+l-5,"(fwd)"))){
+ snprintf(tmp_20k_buf+2000, SIZEOF_20KBUF-2000, "%s (fwd)", tmp_20k_buf);
+ tmp_20k_buf[SIZEOF_20KBUF-2000-1] = '\0';
+ strncpy(tmp_20k_buf, tmp_20k_buf+2000, SIZEOF_20KBUF);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ }
+
+ /*
+ * HACK: composer can't handle embedded double quotes in attachment
+ * comments so we substitute two single quotes.
+ */
+ if(flags & FS_CONVERT_QUOTES)
+ while((p = strchr(tmp_20k_buf, QUOTE)) != NULL)
+ (void)rplstr(p, SIZEOF_20KBUF-(p-tmp_20k_buf), 1, "''");
+
+ return(cpystr(tmp_20k_buf));
+
+ }
+
+ return(cpystr("Forwarded mail...."));
+}
+
+
+/*----------------------------------------------------------------------
+ Build the body for the message number/part being forwarded
+
+ Args:
+
+ Result: BODY structure suitable for sending
+
+ ----------------------------------------------------------------------*/
+BODY *
+forward_body(MAILSTREAM *stream, ENVELOPE *env, struct mail_bodystruct *orig_body,
+ long int msgno, char *sect_prefix, void *msgtext, int flags)
+{
+ BODY *body = NULL, *text_body, *tmp_body;
+ PART *part;
+ gf_io_t pc;
+ char *tmp_text, *section, sect_buf[256];
+ int forward_raw_body = 0;
+
+ /*
+ * Check to see if messages got expunged out from underneath us. This
+ * could have happened during the prompt to the user asking whether to
+ * include the message as an attachment. Either the message is gone or
+ * it might be at a different sequence number. We'd better bail.
+ */
+ if(ps_global->full_header == 2
+ && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global))
+ forward_raw_body = 1;
+ if(sp_expunge_count(stream))
+ return(NULL);
+
+ if(sect_prefix && forward_raw_body == 0)
+ snprintf(section = sect_buf, sizeof(sect_buf), "%s.1", sect_prefix);
+ else if(sect_prefix && forward_raw_body)
+ section = sect_prefix;
+ else if(!sect_prefix && forward_raw_body)
+ section = NULL;
+ else
+ section = "1";
+
+ sect_buf[sizeof(sect_buf)-1] = '\0';
+
+ gf_set_so_writec(&pc, (STORE_S *) msgtext);
+ if(!orig_body || orig_body->type == TYPETEXT || forward_raw_body) {
+ char *charset = NULL;
+
+ /*---- Message has a single text part -----*/
+ body = mail_newbody();
+ body->type = TYPETEXT;
+ body->contents.text.data = msgtext;
+ if(orig_body
+ && (charset = parameter_val(orig_body->parameter, "charset")))
+ set_parameter(&body->parameter, "charset", charset);
+
+ if(charset)
+ fs_give((void **) &charset);
+
+ if(!(flags & FWD_ANON)){
+ forward_delimiter(pc);
+ reply_forward_header(stream, msgno, sect_prefix, env, pc, "");
+ }
+
+ if(!get_body_part_text(stream, forward_raw_body ? NULL : orig_body,
+ msgno, section, 0L, pc, NULL, NULL, GBPT_NONE)){
+ mail_free_body(&body);
+ return(NULL);
+ }
+ }
+ else if(orig_body->type == TYPEMULTIPART) {
+ if(orig_body->subtype && !strucmp(orig_body->subtype, "signed")
+ && orig_body->nested.part){
+ /* only operate on the signed data (not the signature) */
+ body = forward_body(stream, env, &orig_body->nested.part->body,
+ msgno, sect_prefix, msgtext, flags);
+ }
+ /*---- Message is multipart ----*/
+ else if(!(orig_body->subtype && !strucmp(orig_body->subtype,
+ "alternative")
+ && (body = forward_multi_alt(stream, env, orig_body, msgno,
+ sect_prefix, msgtext,
+ pc, flags)))){
+ /*--- Copy the body and entire structure ---*/
+ body = copy_body(NULL, orig_body);
+
+ /*
+ * whatever subtype it is, demote it
+ * to plain old MIXED.
+ */
+ if(body->subtype)
+ fs_give((void **) &body->subtype);
+
+ body->subtype = cpystr("Mixed");
+
+ /*--- The text part of the message ---*/
+ if(!body->nested.part){
+ q_status_message(SM_ORDER | SM_DING, 3, 6,
+ "Error referencing body part 1");
+ mail_free_body(&body);
+ }
+ else if(body->nested.part->body.type == TYPETEXT) {
+ char *new_charset = NULL;
+
+ /*--- The first part is text ----*/
+ text_body = &body->nested.part->body;
+ text_body->contents.text.data = msgtext;
+ if(text_body->subtype && strucmp(text_body->subtype, "Plain")){
+ /* this text is going to the composer, it should be Plain */
+ fs_give((void **)&text_body->subtype);
+ text_body->subtype = cpystr("PLAIN");
+ }
+ if(!(flags & FWD_ANON)){
+ forward_delimiter(pc);
+ reply_forward_header(stream, msgno,
+ sect_prefix, env, pc, "");
+ }
+
+ if(!(get_body_part_text(stream, &orig_body->nested.part->body,
+ msgno, section, 0L, pc,
+ NULL, &new_charset, GBPT_NONE)
+ && fetch_contents(stream, msgno, sect_prefix, body)))
+ mail_free_body(&body);
+ else if(new_charset)
+ set_parameter(&text_body->parameter, "charset", new_charset);
+
+/* BUG: ? matter that we're not setting body.size.bytes */
+ }
+ else if(body->nested.part->body.type == TYPEMULTIPART
+ && body->nested.part->body.subtype
+ && !strucmp(body->nested.part->body.subtype, "alternative")
+ && (tmp_body = forward_multi_alt(stream, env,
+ &body->nested.part->body,
+ msgno, sect_prefix,
+ msgtext, pc,
+ flags | FWD_NESTED))){
+ /* for the forward_multi_alt call above, we want to pass
+ * sect_prefix instead of section so we can obtain the header.
+ * Set the FWD_NESTED flag so we fetch the right body_part.
+ */
+ int partnum;
+
+ part = body->nested.part->next;
+ body->nested.part->next = NULL;
+ mail_free_body_part(&body->nested.part);
+ body->nested.part = mail_newbody_part();
+ body->nested.part->body = *tmp_body;
+ body->nested.part->next = part;
+
+ for(partnum = 2; part != NULL; part = part->next){
+ snprintf(sect_buf, sizeof(sect_buf), "%s%s%d",
+ sect_prefix ? sect_prefix : "",
+ sect_prefix ? "." : "", partnum++);
+ sect_buf[sizeof(sect_buf)-1] = '\0';
+
+ if(!fetch_contents(stream, msgno, sect_buf, &part->body)){
+ mail_free_body(&body);
+ break;
+ }
+ }
+ }
+ else {
+ if(fetch_contents(stream, msgno, sect_prefix, body)){
+ /*--- Create a new blank text part ---*/
+ part = mail_newbody_part();
+ part->next = body->nested.part;
+ body->nested.part = part;
+ part->body.contents.text.data = msgtext;
+ }
+ else
+ mail_free_body(&body);
+ }
+ }
+ }
+ else {
+ /*---- A single part message, not of type text ----*/
+ body = mail_newbody();
+ body->type = TYPEMULTIPART;
+ part = mail_newbody_part();
+ body->nested.part = part;
+
+ /*--- The first part, a blank text part to be edited ---*/
+ part->body.type = TYPETEXT;
+ part->body.contents.text.data = msgtext;
+
+ /*--- The second part, what ever it is ---*/
+ part->next = mail_newbody_part();
+ part = part->next;
+ part->body.id = generate_message_id();
+ copy_body(&(part->body), orig_body);
+
+ /*
+ * the idea here is to fetch part into storage object
+ */
+ if((part->body.contents.text.data = (void *) so_get(PART_SO_TYPE, NULL,
+ EDIT_ACCESS)) != NULL){
+ if((tmp_text = pine_mail_fetch_body(stream, msgno, section,
+ &part->body.size.bytes, NIL)) != NULL)
+ so_nputs((STORE_S *)part->body.contents.text.data, tmp_text,
+ part->body.size.bytes);
+ else
+ mail_free_body(&body);
+ }
+ else
+ mail_free_body(&body);
+ }
+
+ gf_clear_so_writec((STORE_S *) msgtext);
+
+ return(body);
+}
+
+
+
+/*
+ * bounce_msg_body - build body from specified message suitable
+ * for sending as bounced message
+ */
+char *
+bounce_msg_body(MAILSTREAM *stream,
+ long int rawno,
+ char *part,
+ char **to,
+ char *subject,
+ ENVELOPE **outgoingp,
+ BODY **bodyp,
+ int *seenp)
+{
+ char *h, *p, *errstr = NULL;
+ int i;
+ STORE_S *msgtext;
+ gf_io_t pc;
+
+ *outgoingp = mail_newenvelope();
+ (*outgoingp)->message_id = generate_message_id();
+ (*outgoingp)->subject = cpystr(subject ? subject : "Resent mail....");
+
+ /*
+ * Fill in destination if we were given one. If so, note that we
+ * call p_s_s() below such that it won't prompt...
+ */
+ if(to && *to){
+ static char *fakedomain = "@";
+ char *tmp_a_string;
+
+ /* rfc822_parse_adrlist feels free to destroy input so copy */
+ tmp_a_string = cpystr(*to);
+ rfc822_parse_adrlist(&(*outgoingp)->to, tmp_a_string,
+ (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global))
+ ? fakedomain : ps_global->maildomain);
+ fs_give((void **) &tmp_a_string);
+ }
+
+ /* build remail'd header */
+ if((h = mail_fetch_header(stream, rawno, part, NULL, 0, FT_PEEK)) != NULL){
+ for(p = h, i = 0; (p = strchr(p, ':')) != NULL; p++)
+ i++;
+
+ /* allocate it */
+ (*outgoingp)->remail = (char *) fs_get(strlen(h) + (2 * i) + 1);
+
+ /*
+ * copy it, "X-"ing out transport headers bothersome to
+ * software but potentially useful to the human recipient...
+ */
+ p = (*outgoingp)->remail;
+ bounce_mask_header(&p, h);
+ do
+ if(*h == '\015' && *(h+1) == '\012'){
+ *p++ = *h++; /* copy CR LF */
+ *p++ = *h++;
+ bounce_mask_header(&p, h);
+ }
+ while((*p++ = *h++) != '\0');
+ }
+ /* BUG: else complain? */
+
+ /* NOT bound for the composer, so no need for PicoText */
+ if(!(msgtext = so_get(CharStar, NULL, EDIT_ACCESS))){
+ mail_free_envelope(outgoingp);
+ return(_("Error allocating message text"));
+ }
+
+ /* mark object for special handling */
+ so_attr(msgtext, "rawbody", "1");
+
+ /*
+ * Build a fake body description. It's ignored by pine_rfc822_header,
+ * but we need to set it to something that makes set_mime_types
+ * not sniff it and pine_rfc822_output_body not re-encode it.
+ * Setting the encoding to (ENCMAX + 1) will work and shouldn't cause
+ * problems unless something tries to access body_encodings[] using
+ * it without proper precautions. We don't want to use ENCOTHER
+ * cause that tells set_mime_types to sniff it, and we don't want to
+ * use ENC8BIT since that tells pine_rfc822_output_body to qp-encode
+ * it. When there's time, it'd be nice to clean this interaction
+ * up...
+ */
+ *bodyp = mail_newbody();
+ (*bodyp)->type = TYPETEXT;
+ (*bodyp)->encoding = ENCMAX + 1;
+ (*bodyp)->subtype = cpystr("Plain");
+ (*bodyp)->contents.text.data = (void *) msgtext;
+ gf_set_so_writec(&pc, msgtext);
+
+ if(seenp && rawno > 0L && stream && rawno <= stream->nmsgs){
+ MESSAGECACHE *mc;
+
+ if((mc = mail_elt(stream, rawno)) != NULL)
+ *seenp = mc->seen;
+ }
+
+ /* pass NULL body to force mail_fetchtext */
+ if(!get_body_part_text(stream, NULL, rawno, part, 0L, pc, NULL, NULL, GBPT_NONE))
+ errstr = _("Error fetching message contents. Can't Bounce message");
+
+ gf_clear_so_writec(msgtext);
+
+ return(errstr);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Mask off any header entries we don't want xport software to see
+
+Args: d -- destination string pointer pointer
+ s -- source string pointer pointer
+
+ Postfix uses Delivered-To to detect loops.
+ Received line counting is also used to detect loops in places.
+
+ ----*/
+void
+bounce_mask_header(char **d, char *s)
+{
+ if(((*s == 'R' || *s == 'r')
+ && (!struncmp(s+1, "esent-", 6) || !struncmp(s+1, "eceived:", 8)
+ || !struncmp(s+1, "eturn-Path", 10)))
+ || !struncmp(s, "Delivered-To:", 13)){
+ *(*d)++ = 'X'; /* write mask */
+ *(*d)++ = '-';
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Fetch and format text for forwarding
+
+Args: stream -- Mail stream to fetch text from
+ body -- Body structure of message being forwarded
+ msg_no -- Message number of text for forward
+ part_no -- Part number of text to forward
+ partial -- If this is > 0 a partial fetch will be done and it will
+ be done using FT_PEEK so the message will remain unseen.
+ pc -- Function to write to
+ prefix -- Prefix for each line
+ ret_charset -- If we translate to another charset return that
+ new charset here
+
+Returns: true if OK, false if problem occured while filtering
+
+If the text is richtext, it will be converted to plain text, since there's
+no rich text editing capabilities in Pine (yet).
+
+It's up to calling routines to plug in signature appropriately
+
+As with all internal text, NVT end-of-line conventions are observed.
+DOESN'T sanity check the prefix given!!!
+ ----*/
+int
+get_body_part_text(MAILSTREAM *stream, struct mail_bodystruct *body,
+ long int msg_no, char *part_no, long partial, gf_io_t pc,
+ char *prefix, char **ret_charset, unsigned flags)
+{
+ int we_cancel = 0, dashdata, wrapflags = GFW_FORCOMPOSE, flow_res = 0;
+ FILTLIST_S filters[12];
+ long len;
+ char *err, *charset, *prefix_p = NULL;
+ int filtcnt = 0;
+ char *free_this = NULL;
+ DELQ_S dq;
+
+ memset(filters, 0, sizeof(filters));
+ if(ret_charset)
+ *ret_charset = NULL;
+
+ if(!pc_is_picotext(pc))
+ we_cancel = busy_cue(NULL, NULL, 1);
+
+ /* if null body, we must be talking to a non-IMAP2bis server.
+ * No MIME parsing provided, so we just grab the message text...
+ */
+ if(body == NULL){
+ char *text, *decode_error;
+ gf_io_t gc;
+ SourceType src = CharStar;
+ int rv = 0;
+
+ (void) pine_mail_fetchstructure(stream, msg_no, NULL);
+
+ if((text = pine_mail_fetch_text(stream, msg_no, part_no, NULL, 0)) != NULL){
+ gf_set_readc(&gc, text, (unsigned long)strlen(text), src, 0);
+
+ gf_filter_init(); /* no filters needed */
+ if(prefix)
+ gf_link_filter(gf_prefix, gf_prefix_opt(prefix));
+ if((decode_error = gf_pipe(gc, pc)) != NULL){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s [Formatting error: %s]%s",
+ NEWLINE, NEWLINE,
+ decode_error, NEWLINE);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ gf_puts(tmp_20k_buf, pc);
+ rv++;
+ }
+ }
+ else{
+ gf_puts(NEWLINE, pc);
+ gf_puts(_(" [ERROR fetching text of message]"), pc);
+ gf_puts(NEWLINE, pc);
+ gf_puts(NEWLINE, pc);
+ rv++;
+ }
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ return(rv == 0);
+ }
+
+ charset = parameter_val(body->parameter, "charset");
+
+ if(charset && strucmp(charset, "utf-8") && strucmp(charset, "us-ascii")){
+ if(ret_charset)
+ *ret_charset = "UTF-8";
+ }
+
+ /*
+ * just use detach, but add an auxiliary filter to insert prefix,
+ * and, perhaps, digest richtext
+ */
+ if(ps_global->full_header != 2
+ && !ps_global->postpone_no_flow
+ && (!body->subtype || !strucmp(body->subtype, "plain"))){
+ char *parmval;
+
+ flow_res = (F_OFF(F_QUELL_FLOWED_TEXT, ps_global)
+ && F_OFF(F_STRIP_WS_BEFORE_SEND, ps_global)
+ && (!prefix || (strucmp(prefix,"> ") == 0)
+ || strucmp(prefix, ">") == 0));
+ if((parmval = parameter_val(body->parameter,
+ "format")) != NULL){
+ if(!strucmp(parmval, "flowed")){
+ wrapflags |= GFW_FLOWED;
+
+ fs_give((void **) &parmval);
+ if((parmval = parameter_val(body->parameter, "delsp")) != NULL){
+ if(!strucmp(parmval, "yes")){
+ filters[filtcnt++].filter = gf_preflow;
+ wrapflags |= GFW_DELSP;
+ }
+
+ fs_give((void **) &parmval);
+ }
+
+ /*
+ * if there's no prefix we're forwarding text
+ * otherwise it's reply text. unless the user's
+ * tied our hands, alter the prefix to continue flowed
+ * formatting...
+ */
+ if(flow_res)
+ wrapflags |= GFW_FLOW_RESULT;
+
+ filters[filtcnt].filter = gf_wrap;
+ /*
+ * The 80 will cause longer lines than what is likely
+ * set by composer_fillcol, so we'll have longer
+ * quoted and forwarded lines than the lines we type.
+ * We're fine with that since the alternative is the
+ * possible wrapping of lines we shouldn't have, which
+ * is mitigated by this higher 80 limit.
+ *
+ * If we were to go back to using composer_fillcol,
+ * the correct value is composer_fillcol + 1, pine
+ * is off-by-one from pico.
+ */
+ filters[filtcnt++].data = gf_wrap_filter_opt(
+ MAX(MIN(ps_global->ttyo->screen_cols
+ - (prefix ? strlen(prefix) : 0),
+ 80 - (prefix ? strlen(prefix) : 0)),
+ 30), /* doesn't have to be 30 */
+ 990, /* 998 is the SMTP limit */
+ NULL, 0, wrapflags);
+ }
+ }
+
+ /*
+ * if not flowed, remove trailing whitespace to reduce
+ * confusion, since we're sending out as flowed if we
+ * can. At some future point, we might try
+ * plugging in a user-option-controlled heuristic
+ * flowing filter
+ *
+ * We also want to fold "> " quotes so we get the
+ * attributions correct.
+ */
+ if(flow_res && prefix && !strucmp(prefix, "> "))
+ *(prefix_p = prefix + 1) = '\0';
+
+ if(!(wrapflags & GFW_FLOWED)
+ && flow_res){
+ filters[filtcnt].filter = gf_line_test;
+ filters[filtcnt++].data = gf_line_test_opt(twsp_strip, NULL);
+
+ filters[filtcnt].filter = gf_line_test;
+ filters[filtcnt++].data = gf_line_test_opt(quote_fold, NULL);
+ }
+ }
+ else if(body->subtype){
+ int plain_opt = 1;
+
+ if(strucmp(body->subtype,"richtext") == 0){
+ filters[filtcnt].filter = gf_rich2plain;
+ filters[filtcnt++].data = gf_rich2plain_opt(&plain_opt);
+ }
+ else if(strucmp(body->subtype,"enriched") == 0){
+ filters[filtcnt].filter = gf_enriched2plain;
+ filters[filtcnt++].data = gf_enriched2plain_opt(&plain_opt);
+ }
+ else if(strucmp(body->subtype,"html") == 0){
+ if((flags & GBPT_HTML_OK) != GBPT_HTML_OK){
+ filters[filtcnt].filter = gf_html2plain;
+ filters[filtcnt++].data = gf_html2plain_opt(NULL,
+ ps_global->ttyo->screen_cols,
+ non_messageview_margin(),
+ NULL, NULL, GFHP_STRIPPED);
+ }
+ }
+ }
+
+ if(prefix){
+ if(ps_global->full_header != 2
+ && (F_ON(F_ENABLE_SIGDASHES, ps_global)
+ || F_ON(F_ENABLE_STRIP_SIGDASHES, ps_global))){
+ dashdata = 0;
+ filters[filtcnt].filter = gf_line_test;
+ filters[filtcnt++].data = gf_line_test_opt(sigdash_strip, &dashdata);
+ }
+
+ filters[filtcnt].filter = gf_prefix;
+ filters[filtcnt++].data = gf_prefix_opt(prefix);
+
+ if(wrapflags & GFW_FLOWED || flow_res){
+ filters[filtcnt].filter = gf_line_test;
+ filters[filtcnt++].data = gf_line_test_opt(post_quote_space, NULL);
+ }
+ }
+
+ if(flags & GBPT_DELQUOTES){
+ memset(&dq, 0, sizeof(dq));
+ dq.lines = Q_DEL_ALL;
+ dq.is_flowed = 0;
+ dq.indent_length = 0;
+ dq.saved_line = &free_this;
+ dq.handlesp = NULL;
+ dq.do_color = 0;
+ dq.delete_all = 1;
+
+ filters[filtcnt].filter = gf_line_test;
+ filters[filtcnt++].data = gf_line_test_opt(delete_quotes, &dq);
+ }
+
+ err = detach(stream, msg_no, part_no, partial, &len, pc,
+ filters[0].filter ? filters : NULL,
+ ((flags & GBPT_PEEK) ? FT_PEEK : 0)
+ | ((flags & GBPT_NOINTR) ? DT_NOINTR : 0));
+
+ if(free_this)
+ fs_give((void **) &free_this);
+
+ if(prefix_p)
+ *prefix_p = ' ';
+
+ if (err != (char *) NULL)
+ /* TRANSLATORS: The first arg is error text, the %ld is the message number */
+ q_status_message2(SM_ORDER, 3, 4, "%s: message number %ld",
+ err, (void *) msg_no);
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ return((int) len);
+}
+
+
+int
+quote_fold(long int linenum, char *line, LT_INS_S **ins, void *local)
+{
+ char *p;
+
+ if(*line == '>'){
+ for(p = line; *p; p++){
+ if(isspace((unsigned char) *p)){
+ if(*(p+1) == '>')
+ ins = gf_line_test_new_ins(ins, p, "", -1);
+ }
+ else if(*p != '>')
+ break;
+ }
+ }
+
+ return(0);
+}
+
+
+int
+twsp_strip(long int linenum, char *line, LT_INS_S **ins, void *local)
+{
+ char *p, *ws = NULL;
+
+ for(p = line; *p; p++){
+ /* don't strip trailing space on signature line */
+ if(*line == '-' && *(line+1) == '-' && *(line+2) == ' ' && !*(line+3))
+ break;
+
+ if(isspace((unsigned char) *p)){
+ if(!ws)
+ ws = p;
+ }
+ else
+ ws = NULL;
+ }
+
+ if(ws)
+ ins = gf_line_test_new_ins(ins, ws, "", -(p - ws));
+
+ return(0);
+}
+
+int
+post_quote_space(long int linenum, char *line, LT_INS_S **ins, void *local)
+{
+ char *p;
+
+ for(p = line; *p; p++)
+ if(*p != '>'){
+ if(p != line && *p != ' ')
+ ins = gf_line_test_new_ins(ins, p, " ", 1);
+
+ break;
+ }
+
+ return(0);
+}
+
+
+int
+sigdash_strip(long int linenum, char *line, LT_INS_S **ins, void *local)
+{
+ if(*((int *)local)
+ || (*line == '-' && *(line+1) == '-'
+ && *(line+2) == ' ' && !*(line+3))){
+ *((int *) local) = 1;
+ return(2); /* skip this line! */
+ }
+
+ return(0);
+}
+
+
+/*----------------------------------------------------------------------
+ return the c-client reference name for the given end_body part
+ ----*/
+char *
+body_partno(MAILSTREAM *stream, long int msgno, struct mail_bodystruct *end_body)
+{
+ BODY *body;
+
+ (void) pine_mail_fetchstructure(stream, msgno, &body);
+ return(partno(body, end_body));
+}
+
+
+/*----------------------------------------------------------------------
+ return the c-client reference name for the given end_body part
+ ----*/
+char *
+partno(struct mail_bodystruct *body, struct mail_bodystruct *end_body)
+{
+ PART *part;
+ int num = 0;
+ char tmp[64], *p = NULL;
+
+ if(body && body->type == TYPEMULTIPART) {
+ part = body->nested.part; /* first body part */
+
+ do { /* for each part */
+ num++;
+ if(&part->body == end_body || (p = partno(&part->body, end_body))){
+ snprintf(tmp, sizeof(tmp), "%d%s%.*s", num, (p) ? "." : "",
+ sizeof(tmp)-10, (p) ? p : "");
+ tmp[sizeof(tmp)-1] = '\0';
+ if(p)
+ fs_give((void **)&p);
+
+ return(cpystr(tmp));
+ }
+ } while ((part = part->next) != NULL); /* until done */
+
+ return(NULL);
+ }
+ else if(body && body->type == TYPEMESSAGE && body->subtype
+ && !strucmp(body->subtype, "rfc822")){
+ return(partno(body->nested.msg->body, end_body));
+ }
+
+ return((body == end_body) ? cpystr("1") : NULL);
+}
+
+
+/*----------------------------------------------------------------------
+ Fill in the contents of each body part
+
+Args: stream -- Stream the message is on
+ msgno -- Message number the body structure is for
+ section -- body section associated with body pointer
+ body -- Body pointer to fill in
+
+Result: 1 if all went OK, 0 if there was a problem
+
+This function copies the contents from an original message/body to
+a new message/body. It recurses down all multipart levels.
+
+If one or more part (but not all) can't be fetched, a status message
+will be queued.
+ ----*/
+int
+fetch_contents(MAILSTREAM *stream, long int msgno, char *section, struct mail_bodystruct *body)
+{
+ char *tp;
+ int got_one = 0;
+
+ if(!body->id)
+ body->id = generate_message_id();
+
+ if(body->type == TYPEMULTIPART){
+ char subsection[256], *subp;
+ int n, last_one = 10; /* remember worst case */
+ PART *part = body->nested.part;
+
+ if(!(part = body->nested.part))
+ return(0);
+
+ subp = subsection;
+ if(section && *section){
+ for(n = 0;
+ n < sizeof(subsection)-20 && (*subp = section[n]); n++, subp++)
+ ;
+
+ *subp++ = '.';
+ }
+
+ n = 1;
+ do {
+ snprintf(subp, sizeof(subsection)-(subp-subsection), "%d", n++);
+ subsection[sizeof(subsection)-1] = '\0';
+ got_one = fetch_contents(stream, msgno, subsection, &part->body);
+ last_one = MIN(last_one, got_one);
+ }
+ while((part = part->next) != NULL);
+
+ return(last_one);
+ }
+
+ if(body->contents.text.data)
+ return(1); /* already taken care of... */
+
+ if(body->type == TYPEMESSAGE){
+ if(body->subtype && strucmp(body->subtype,"external-body")){
+ /*
+ * the idea here is to fetch everything into storage objects
+ */
+ body->contents.text.data = (void *) so_get(PART_SO_TYPE, NULL,
+ EDIT_ACCESS);
+ if(body->contents.text.data
+ && (tp = pine_mail_fetch_body(stream, msgno, section,
+ &body->size.bytes, NIL))){
+ so_truncate((STORE_S *)body->contents.text.data,
+ body->size.bytes + 2048);
+ so_nputs((STORE_S *)body->contents.text.data, tp,
+ body->size.bytes);
+ got_one = 1;
+ }
+ else
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("Error fetching part %s"), section);
+ } else {
+ got_one = 1;
+ }
+ } else {
+ /*
+ * the idea here is to fetch everything into storage objects
+ * so, grab one, then fetch the body part
+ */
+ body->contents.text.data = (void *)so_get(PART_SO_TYPE,NULL,EDIT_ACCESS);
+ if(body->contents.text.data
+ && (tp=pine_mail_fetch_body(stream, msgno, section,
+ &body->size.bytes, NIL))){
+ so_truncate((STORE_S *)body->contents.text.data,
+ body->size.bytes + 2048);
+ so_nputs((STORE_S *)body->contents.text.data, tp,
+ body->size.bytes);
+ got_one = 1;
+ }
+ else
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("Error fetching part %s"), section);
+ }
+
+ return(got_one);
+}
+
+
+/*----------------------------------------------------------------------
+ Copy the body structure
+
+Args: new_body -- Pointer to already allocated body, or NULL, if none
+ old_body -- The Body to copy
+
+
+ This is traverses the body structure recursively copying all elements.
+The new_body parameter can be NULL in which case a new body is
+allocated. Alternatively it can point to an already allocated body
+structure. This is used when copying body parts since a PART includes a
+BODY. The contents fields are *not* filled in.
+ ----*/
+
+BODY *
+copy_body(struct mail_bodystruct *new_body, struct mail_bodystruct *old_body)
+{
+ if(old_body == NULL)
+ return(NULL);
+
+ if(new_body == NULL)
+ new_body = mail_newbody();
+
+ new_body->type = old_body->type;
+ new_body->encoding = old_body->encoding;
+
+ if(old_body->subtype)
+ new_body->subtype = cpystr(old_body->subtype);
+
+ new_body->parameter = copy_parameters(old_body->parameter);
+
+ if(old_body->id)
+ new_body->id = cpystr(old_body->id);
+
+ if(old_body->description)
+ new_body->description = cpystr(old_body->description);
+
+ if(old_body->disposition.type)
+ new_body->disposition.type = cpystr(old_body->disposition.type);
+
+ new_body->disposition.parameter
+ = copy_parameters(old_body->disposition.parameter);
+
+ new_body->size = old_body->size;
+
+ if(new_body->type == TYPEMESSAGE
+ && new_body->subtype && !strucmp(new_body->subtype, "rfc822")){
+ new_body->nested.msg = mail_newmsg();
+ new_body->nested.msg->body
+ = copy_body(NULL, old_body->nested.msg->body);
+ }
+ else if(new_body->type == TYPEMULTIPART) {
+ PART **new_partp, *old_part;
+
+ new_partp = &new_body->nested.part;
+ for(old_part = old_body->nested.part;
+ old_part != NULL;
+ old_part = old_part->next){
+ *new_partp = mail_newbody_part();
+ copy_body(&(*new_partp)->body, &old_part->body);
+ new_partp = &(*new_partp)->next;
+ }
+ }
+
+ return(new_body);
+}
+
+
+/*----------------------------------------------------------------------
+ Copy the MIME parameter list
+
+ Allocates storage for new part, and returns pointer to new paramter
+list. If old_p is NULL, NULL is returned.
+ ----*/
+PARAMETER *
+copy_parameters(PARAMETER *old_p)
+{
+ PARAMETER *new_p, *p1, *p2;
+
+ if(old_p == NULL)
+ return((PARAMETER *)NULL);
+
+ new_p = p2 = NULL;
+ for(p1 = old_p; p1 != NULL; p1 = p1->next){
+ set_parameter(&p2, p1->attribute, p1->value);
+ if(new_p == NULL)
+ new_p = p2;
+ }
+
+ return(new_p);
+}
+
+
+/*----------------------------------------------------------------------
+ Make a complete copy of an envelope and all it's fields
+
+Args: e -- the envelope to copy
+
+Result: returns the new envelope, or NULL, if the given envelope was NULL
+
+ ----*/
+
+ENVELOPE *
+copy_envelope(register ENVELOPE *e)
+{
+ register ENVELOPE *e2;
+
+ if(!e)
+ return(NULL);
+
+ e2 = mail_newenvelope();
+ e2->remail = e->remail ? cpystr(e->remail) : NULL;
+ e2->return_path = e->return_path ? rfc822_cpy_adr(e->return_path) : NULL;
+ e2->date = e->date ? (unsigned char *)cpystr((char *) e->date)
+ : NULL;
+ e2->from = e->from ? rfc822_cpy_adr(e->from) : NULL;
+ e2->sender = e->sender ? rfc822_cpy_adr(e->sender) : NULL;
+ e2->reply_to = e->reply_to ? rfc822_cpy_adr(e->reply_to) : NULL;
+ e2->subject = e->subject ? cpystr(e->subject) : NULL;
+ e2->to = e->to ? rfc822_cpy_adr(e->to) : NULL;
+ e2->cc = e->cc ? rfc822_cpy_adr(e->cc) : NULL;
+ e2->bcc = e->bcc ? rfc822_cpy_adr(e->bcc) : NULL;
+ e2->in_reply_to = e->in_reply_to ? cpystr(e->in_reply_to) : NULL;
+ e2->newsgroups = e->newsgroups ? cpystr(e->newsgroups) : NULL;
+ e2->message_id = e->message_id ? cpystr(e->message_id) : NULL;
+ e2->references = e->references ? cpystr(e->references) : NULL;
+ e2->followup_to = e->followup_to ? cpystr(e->references) : NULL;
+ return(e2);
+}
+
+
+/*----------------------------------------------------------------------
+ Generate the "In-reply-to" text from message header
+
+ Args: message -- Envelope of original message
+
+ Result: returns an alloc'd string or NULL if there is a problem
+ ----*/
+char *
+reply_in_reply_to(ENVELOPE *env)
+{
+ return((env && env->message_id) ? cpystr(env->message_id) : NULL);
+}
+
+
+/*----------------------------------------------------------------------
+ Generate a unique message id string.
+
+ Args: ps -- The usual pine structure
+
+ Result: Alloc'd unique string is returned
+
+Uniqueness is gaurenteed by using the host name, process id, date to the
+second and a single unique character
+*----------------------------------------------------------------------*/
+char *
+generate_message_id(void)
+{
+ static short osec = 0, cnt = 0;
+ char idbuf[128];
+ char *id;
+ time_t now;
+ struct tm *now_x;
+ char *hostpart = NULL;
+
+ now = time((time_t *)0);
+ now_x = localtime(&now);
+
+ if(now_x->tm_sec == osec)
+ cnt++;
+ else{
+ cnt = 0;
+ osec = now_x->tm_sec;
+ }
+
+ hostpart = F_ON(F_ROT13_MESSAGE_ID, ps_global)
+ ? rot13(ps_global->hostname)
+ : cpystr(ps_global->hostname);
+
+ if(!hostpart)
+ hostpart = cpystr("huh");
+
+ snprintf(idbuf, sizeof(idbuf), "<alpine.%.4s.%.20s.%02d%02d%02d%02d%02d%02d%X.%d@%.50s>",
+ SYSTYPE, ALPINE_VERSION, (now_x->tm_year) % 100, now_x->tm_mon + 1,
+ now_x->tm_mday, now_x->tm_hour, now_x->tm_min, now_x->tm_sec,
+ cnt, getpid(), hostpart);
+ idbuf[sizeof(idbuf)-1] = '\0';
+
+ id = cpystr(idbuf);
+
+ if(hostpart)
+ fs_give((void **) &hostpart);
+
+ return(id);
+}
+
+
+char *
+generate_user_agent(void)
+{
+ char buf[128];
+ char rev[128];
+
+ if(F_ON(F_QUELL_USERAGENT, ps_global))
+ return(NULL);
+
+ snprintf(buf, sizeof(buf),
+ "%sAlpine %s (%s %s)",
+ (pith_opt_user_agent_prefix) ? (*pith_opt_user_agent_prefix)() : "",
+ ALPINE_VERSION, SYSTYPE,
+ get_alpine_revision_string(rev, sizeof(rev)));
+
+ return(cpystr(buf));
+}
+
+
+char *
+rot13(char *src)
+{
+ char byte, cap, *p, *ret = NULL;
+
+ if(src && *src){
+ ret = (char *) fs_get((strlen(src)+1) * sizeof(char));
+ p = ret;
+ while((byte = *src++) != '\0'){
+ cap = byte & 32;
+ byte &= ~cap;
+ *p++ = ((byte >= 'A') && (byte <= 'Z')
+ ? ((byte - 'A' + 13) % 26 + 'A') : byte) | cap;
+ }
+
+ *p = '\0';
+ }
+
+ return(ret);
+}
+
+
+/*----------------------------------------------------------------------
+ Return the first true address pointer (modulo group syntax allowance)
+
+ Args: addr -- Address list
+
+ Result: First real address pointer, or NULL
+ ----------------------------------------------------------------------*/
+ADDRESS *
+first_addr(struct mail_address *addr)
+{
+ while(addr && !addr->host)
+ addr = addr->next;
+
+ return(addr);
+}
+
+
+/*----------------------------------------------------------------------
+ lit -- this is the source
+ prenewlines -- prefix the file contents with this many newlines
+ postnewlines -- postfix the file contents with this many newlines
+ is_sig -- this is a signature (not a template)
+ decode_constants -- change C-style constants into their values
+ ----*/
+char *
+get_signature_lit(char *lit, int prenewlines, int postnewlines, int is_sig, int decode_constants)
+{
+ char *sig = NULL;
+
+ /*
+ * Should make this smart enough not to do the copying and double
+ * allocation of space.
+ */
+ if(lit){
+ char *tmplit = NULL, *p, *q, *d, save;
+ size_t len;
+
+ if(decode_constants){
+ tmplit = (char *) fs_get((strlen(lit)+1) * sizeof(char));
+ tmplit[0] = '\0';
+ cstring_to_string(lit, tmplit);
+ }
+ else
+ tmplit = cpystr(lit);
+
+ len = strlen(tmplit) + 5 + (prenewlines+postnewlines) * strlen(NEWLINE);
+ sig = (char *) fs_get((len+1) * sizeof(char));
+ memset(sig, 0, len+1);
+ d = sig;
+ while(prenewlines--)
+ sstrncpy(&d, NEWLINE, len-(d-sig));
+
+ if(is_sig && F_ON(F_ENABLE_SIGDASHES, ps_global) &&
+ !sigdashes_are_present(tmplit)){
+ sstrncpy(&d, SIGDASHES, len-(d-sig));
+ sstrncpy(&d, NEWLINE, len-(d-sig));
+ }
+
+ sig[len] = '\0';
+
+ p = tmplit;
+ while(*p){
+ /* get a line */
+ q = strpbrk(p, "\n\r");
+ if(q){
+ save = *q;
+ *q = '\0';
+ }
+
+ /*
+ * Strip trailing space if we are doing a signature and
+ * this line is not sigdashes.
+ */
+ if(is_sig && strcmp(p, SIGDASHES))
+ removing_trailing_white_space(p);
+
+ while((d-sig) <= len && (*d = *p++) != '\0')
+ d++;
+
+ if(q){
+ if((d-sig) <= len)
+ *d++ = save;
+
+ p = q+1;
+ }
+ else
+ break;
+ }
+
+ while(postnewlines--)
+ sstrncpy(&d, NEWLINE, len-(d-sig));
+
+ sig[len] = '\0';
+
+ if((d-sig) <= len)
+ *d = '\0';
+
+ if(tmplit)
+ fs_give((void **) &tmplit);
+ }
+
+ return(sig);
+}
+
+
+int
+sigdashes_are_present(char *sig)
+{
+ char *p;
+
+ p = srchstr(sig, SIGDASHES);
+ while(p && !((p == sig || (p[-1] == '\n' || p[-1] == '\r')) &&
+ (p[3] == '\0' || p[3] == '\n' || p[3] == '\r')))
+ p = srchstr(p+1, SIGDASHES);
+
+ return(p ? 1 : 0);
+}
+
+
+/*----------------------------------------------------------------------
+ Acquire the pinerc defined signature file pathname
+
+ ----*/
+char *
+signature_path(char *sname, char *sbuf, size_t len)
+{
+ *sbuf = '\0';
+ if(sname && *sname){
+ size_t spl = strlen(sname);
+ if(IS_REMOTE(sname)){
+ if(spl < len - 1)
+ strncpy(sbuf, sname, len-1);
+ }
+ else if(is_absolute_path(sname)){
+ strncpy(sbuf, sname, len-1);
+ sbuf[len-1] = '\0';
+ fnexpand(sbuf, len);
+ }
+ else if(ps_global->VAR_OPER_DIR){
+ if(strlen(ps_global->VAR_OPER_DIR) + spl < len - 1)
+ build_path(sbuf, ps_global->VAR_OPER_DIR, sname, len);
+ }
+ else{
+ char *lc = last_cmpnt(ps_global->pinerc);
+
+ sbuf[0] = '\0';
+ if(lc != NULL){
+ strncpy(sbuf,ps_global->pinerc,MIN(len-1,lc-ps_global->pinerc));
+ sbuf[MIN(len-1,lc-ps_global->pinerc)] = '\0';
+ }
+
+ strncat(sbuf, sname, MAX(len-1-strlen(sbuf), 0));
+ sbuf[len-1] = '\0';
+ }
+ }
+
+ return(*sbuf ? sbuf : NULL);
+}
+
+
+char *
+simple_read_remote_file(char *name, char *subtype)
+{
+ int try_cache;
+ REMDATA_S *rd;
+ char *file = NULL;
+
+
+ dprint((7, "simple_read_remote_file(%s, %s)\n", name ? name : "?", subtype ? subtype : "?"));
+
+ /*
+ * We could parse the name here to find what type it is. So far we
+ * only have type RemImap.
+ */
+ rd = rd_create_remote(RemImap, name, subtype,
+ NULL, _("Error: "), _("Can't fetch remote configuration."));
+ if(!rd)
+ goto bail_out;
+
+ try_cache = rd_read_metadata(rd);
+
+ if(rd->access == MaybeRorW){
+ if(rd->read_status == 'R')
+ rd->access = ReadOnly;
+ else
+ rd->access = ReadWrite;
+ }
+
+ if(rd->access != NoExists){
+
+ rd_check_remvalid(rd, 1L);
+
+ /*
+ * If the cached info says it is readonly but
+ * it looks like it's been fixed now, change it to readwrite.
+ */
+ if(rd->read_status == 'R'){
+ /*
+ * We go to this trouble since readonly sigfiles
+ * are likely a mistake. They are usually supposed to be
+ * readwrite so we open it and check if it's been fixed.
+ */
+ rd_check_readonly_access(rd);
+ if(rd->read_status == 'W'){
+ rd->access = ReadWrite;
+ rd->flags |= REM_OUTOFDATE;
+ }
+ else
+ rd->access = ReadOnly;
+ }
+
+ if(rd->flags & REM_OUTOFDATE){
+ if(rd_update_local(rd) != 0){
+
+ dprint((1,
+ "simple_read_remote_file: rd_update_local failed\n"));
+ /*
+ * Don't give up altogether. We still may be
+ * able to use a cached copy.
+ */
+ }
+ else{
+ dprint((7,
+ "%s: copied remote to local (%ld)\n",
+ rd->rn ? rd->rn : "?", (long)rd->last_use));
+ }
+ }
+
+ if(rd->access == ReadWrite)
+ rd->flags |= DO_REMTRIM;
+ }
+
+ /* If we couldn't get to remote folder, try using the cached copy */
+ if(rd->access == NoExists || rd->flags & REM_OUTOFDATE){
+ if(try_cache){
+ rd->access = ReadOnly;
+ rd->flags |= USE_OLD_CACHE;
+ q_status_message(SM_ORDER, 3, 4,
+ "Can't contact remote server, using cached copy");
+ dprint((2,
+ "Can't open remote file %s, using local cached copy %s readonly\n",
+ rd->rn ? rd->rn : "?",
+ rd->lf ? rd->lf : "?"));
+ }
+ else{
+ rd->flags &= ~DO_REMTRIM;
+ goto bail_out;
+ }
+ }
+
+ file = read_file(rd->lf, READ_FROM_LOCALE);
+
+bail_out:
+ if(rd)
+ rd_close_remdata(&rd);
+
+ return(file);
+}
+
+
+/*----------------------------------------------------------------------
+ Build the body for the multipart/alternative part
+
+ Args:
+
+ Result:
+
+ ----------------------------------------------------------------------*/
+BODY *
+forward_multi_alt(MAILSTREAM *stream, ENVELOPE *env, struct mail_bodystruct *orig_body,
+ long int msgno, char *sect_prefix, void *msgtext, gf_io_t pc, int flags)
+{
+ BODY *body = NULL;
+ PART *part = NULL, *bestpart = NULL;
+ char tmp_buf[256];
+ char *new_charset = NULL;
+ int partnum, bestpartnum;
+
+ if(ps_global->force_prefer_plain
+ || (!ps_global->force_no_prefer_plain
+ && F_ON(F_PREFER_PLAIN_TEXT, ps_global))){
+ for(part = orig_body->nested.part, partnum = 1;
+ part;
+ part = part->next, partnum++)
+ if((!part->body.type || part->body.type == TYPETEXT)
+ && (!part->body.subtype
+ || !strucmp(part->body.subtype, "plain")))
+ break;
+ }
+
+ /*
+ * Else choose last alternative among plain or html parts.
+ * Perhaps we should really be using mime_show() to make this
+ * potentially more general than just plain or html.
+ */
+ if(!part){
+ for(part = orig_body->nested.part, partnum = 1;
+ part;
+ part = part->next, partnum++){
+ if((!part->body.type || part->body.type == TYPETEXT)
+ && ((!part->body.subtype || !strucmp(part->body.subtype, "plain"))
+ ||
+ (part->body.subtype && !strucmp(part->body.subtype, "html")))){
+ bestpart = part;
+ bestpartnum = partnum;
+ }
+ }
+
+ part = bestpart;
+ partnum = bestpartnum;
+ }
+
+ /*
+ * IF something's interesting insert it
+ * AND forget the rest of the multipart
+ */
+ if(part){
+ body = mail_newbody();
+ body->type = TYPETEXT;
+ body->contents.text.data = msgtext;
+
+ /* record character set, flowing, etc */
+ body->parameter = copy_parameters(part->body.parameter);
+ body->size.bytes = part->body.size.bytes;
+
+ if(!(flags & FWD_ANON)){
+ forward_delimiter(pc);
+ reply_forward_header(stream, msgno, sect_prefix, env, pc, "");
+ }
+
+ snprintf(tmp_buf, sizeof(tmp_buf), "%.*s%s%s%d",
+ sizeof(tmp_buf)/2, sect_prefix ? sect_prefix : "",
+ sect_prefix ? "." : "", flags & FWD_NESTED ? "1." : "",
+ partnum);
+ tmp_buf[sizeof(tmp_buf)-1] = '\0';
+ get_body_part_text(stream, &part->body, msgno, tmp_buf, 0L, pc,
+ NULL, &new_charset, GBPT_NONE);
+
+ /*
+ * get_body_part_text translated the data to a new charset.
+ * We need to record that fact in body.
+ */
+ if(new_charset)
+ set_parameter(&body->parameter, "charset", new_charset);
+ }
+ else
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ "No suitable part found. Forwarding as attachment");
+
+ return(body);
+}
+
+
+void
+reply_append_addr(struct mail_address **dest, struct mail_address *src)
+{
+ for( ; *dest; dest = &(*dest)->next)
+ ;
+
+ *dest = src;
+}
diff --git a/pith/reply.h b/pith/reply.h
new file mode 100644
index 00000000..a80257c8
--- /dev/null
+++ b/pith/reply.h
@@ -0,0 +1,106 @@
+/*
+ * $Id: reply.h 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_REPLY_INCLUDED
+#define PITH_REPLY_INCLUDED
+
+
+#define ALLOWED_SUBTYPE(st) (!strucmp((st), "plain") \
+ || !strucmp((st), "html") \
+ || !strucmp((st), "enriched") \
+ || !strucmp((st), "richtext"))
+
+/* flags for get_body_part_text */
+#define GBPT_NONE 0x00
+#define GBPT_PEEK 0x01
+#define GBPT_NOINTR 0x02
+#define GBPT_DELQUOTES 0x04
+#define GBPT_HTML_OK 0x08
+
+
+/* flags for reply_cp_addr */
+#define RCA_NONE 0x00
+#define RCA_NOT_US 0x01 /* copy addrs that aren't us */
+#define RCA_ONLY_US 0x02 /* copy only addrs that are us */
+#define RCA_ALL 0x04 /* copy all addrs, including us */
+
+
+#include "../pith/repltype.h"
+#include "../pith/filttype.h"
+#include "../pith/indxtype.h"
+#include "../pith/pattern.h"
+#include "../pith/state.h"
+#include "../pith/addrstring.h"
+
+
+/* exported protoypes */
+int reply_harvest(struct pine *, long, char *, ENVELOPE *, ADDRESS **,
+ ADDRESS **, ADDRESS **, ADDRESS **,int *);
+ADDRESS *reply_cp_addr(struct pine *, long, char *, char *,
+ ADDRESS *, ADDRESS *, ADDRESS *, int);
+void reply_append_addr(ADDRESS **, ADDRESS *);
+ACTION_S *set_role_from_msg(struct pine *, long, long, char *);
+void reply_seed(struct pine *, ENVELOPE *, ENVELOPE *, ADDRESS *, ADDRESS *,
+ ADDRESS *, ADDRESS *, char **, int, char **);
+int addr_lists_same(ADDRESS *, ADDRESS *);
+int addr_in_env(ADDRESS *, ENVELOPE *);
+void reply_fish_personal(ENVELOPE *, ENVELOPE *);
+char *reply_build_refs(ENVELOPE *);
+ADDRESS *reply_resent(struct pine *, long, char *);
+char *reply_subject(char *, char *, size_t);
+char *reply_quote_initials(char *);
+char *reply_quote_str(ENVELOPE *);
+int reply_quote_str_contains_tokens(void);
+BODY *reply_body(MAILSTREAM *, ENVELOPE *, BODY *, long, char *, void *,
+ char *, int, ACTION_S *, int, REDRAFT_POS_S **);
+int reply_body_text(BODY *, BODY **);
+char *reply_signature(ACTION_S *, ENVELOPE *, REDRAFT_POS_S **, int *);
+void get_addr_data(ENVELOPE *, IndexColType, char *, size_t);
+void get_news_data(ENVELOPE *, IndexColType, char *, size_t);
+char *get_reply_data(ENVELOPE *, ACTION_S *, IndexColType, char *, size_t);
+void reply_delimiter(ENVELOPE *, ACTION_S *, gf_io_t);
+void free_redraft_pos(REDRAFT_POS_S **);
+int forward_mime_msg(MAILSTREAM *, long, char *, ENVELOPE *, PART **, void *);
+void forward_delimiter(gf_io_t);
+void reply_forward_header(MAILSTREAM *, long, char *, ENVELOPE *, gf_io_t, char *);
+char *forward_subject(ENVELOPE *, int);
+BODY *forward_body(MAILSTREAM *, ENVELOPE *, BODY *, long, char *, void *, int);
+char *bounce_msg_body(MAILSTREAM *, long int, char *, char **, char *, ENVELOPE **, BODY **, int *);
+int get_body_part_text(MAILSTREAM *, BODY *, long, char *, long,
+ gf_io_t, char *, char **, unsigned);
+int quote_fold(long, char *, LT_INS_S **, void *);
+int twsp_strip(long, char *, LT_INS_S **, void *);
+int post_quote_space(long, char *, LT_INS_S **, void *);
+int sigdash_strip(long, char *, LT_INS_S **, void *);
+char *body_partno(MAILSTREAM *, long, BODY *);
+char *partno(BODY *, BODY *);
+int fetch_contents(MAILSTREAM *, long, char *, BODY *);
+BODY *copy_body(BODY *, BODY *);
+PARAMETER *copy_parameters(PARAMETER *);
+ENVELOPE *copy_envelope(ENVELOPE *);
+char *reply_in_reply_to(ENVELOPE *);
+char *generate_message_id(void);
+char *generate_user_agent(void);
+char *rot13(char *);
+ADDRESS *first_addr(ADDRESS *);
+char *get_signature_lit(char *, int, int, int, int);
+int sigdashes_are_present(char *);
+char *signature_path(char *, char *, size_t);
+char *simple_read_remote_file(char *, char *);
+BODY *forward_multi_alt(MAILSTREAM *, ENVELOPE *, BODY *, long, char *, void *, gf_io_t, int);
+
+
+#endif /* PITH_REPLY_INCLUDED */
diff --git a/pith/rfc2231.c b/pith/rfc2231.c
new file mode 100644
index 00000000..1aa0752f
--- /dev/null
+++ b/pith/rfc2231.c
@@ -0,0 +1,313 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: rfc2231.c 1012 2008-03-26 00:44:22Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/rfc2231.h"
+#include "../pith/mimedesc.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/store.h"
+#include "../pith/status.h"
+#include "../pith/send.h"
+#include "../pith/string.h"
+
+
+/*
+ * * * * * * * * * RFC 2231 support routines * * * * * * * *
+ */
+
+
+/* Useful def's */
+#define RFC2231_MAX 64
+
+
+char *
+rfc2231_get_param(PARAMETER *parms, char *name,
+ char **charset, char **lang)
+{
+ char *buf, *p;
+ int decode = 0, name_len, i;
+ unsigned n;
+
+ name_len = strlen(name);
+ for(; parms ; parms = parms->next)
+ if(!struncmp(name, parms->attribute, name_len)){
+ if(parms->attribute[name_len] == '*'){
+ for(p = &parms->attribute[name_len + 1], n = 0; *(p+n); n++)
+ ;
+
+ decode = *(p + n - 1) == '*';
+
+ if(isdigit((unsigned char) *p)){
+ char *pieces[RFC2231_MAX];
+ int count = 0, len;
+
+ memset(pieces, 0, RFC2231_MAX * sizeof(char *));
+
+ while(parms){
+ n = 0;
+ do
+ n = (n * 10) + (*p - '0');
+ while(isdigit(*++p) && n < RFC2231_MAX);
+
+ if(n < RFC2231_MAX){
+ pieces[n] = parms->value;
+ if(n > count)
+ count = n;
+ }
+ else{
+ q_status_message1(SM_ORDER | SM_DING, 0, 3,
+ "Invalid attachment parameter segment number: %.25s",
+ name);
+ return(NULL); /* Too many segments! */
+ }
+
+ while((parms = parms->next) != NULL)
+ if(!struncmp(name, parms->attribute, name_len)){
+ if(*(p = &parms->attribute[name_len]) == '*'
+ && isdigit((unsigned char) *++p))
+ break;
+ else
+ return(NULL); /* missing segment no.! */
+ }
+ }
+
+ for(i = len = 0; i <= count; i++)
+ if(pieces[i])
+ len += strlen(pieces[i]);
+ else{
+ q_status_message1(SM_ORDER | SM_DING, 0, 3,
+ "Missing attachment parameter sequence: %.25s",
+ name);
+
+ return(NULL); /* hole! */
+ }
+
+ buf = (char *) fs_get((len + 1) * sizeof(char));
+
+ for(i = len = 0; i <= count; i++){
+ if((n = *(p = pieces[i]) == '\"') != 0) /* quoted? */
+ p++;
+
+ while(*p && !(n && *p == '\"' && !*(p+1)))
+ buf[len++] = *p++;
+ }
+
+ buf[len] = '\0';
+ }
+ else
+ buf = cpystr(parms->value);
+
+ /* Do any RFC 2231 decoding? */
+ if(decode){
+ char *converted = NULL, cs[1000];
+
+ cs[0] = '\0';
+ n = 0;
+
+ if((p = strchr(buf, '\'')) != NULL){
+ n = (p - buf) + 1;
+ *p = '\0';
+ strncpy(cs, buf, sizeof(cs));
+ cs[sizeof(cs)-1] = '\0';
+ *p = '\'';
+ if(charset)
+ *charset = cpystr(cs);
+
+ if((p = strchr(&buf[n], '\'')) != NULL){
+ n = (p - buf) + 1;
+ if(lang){
+ *p = '\0';
+ *lang = cpystr(p);
+ *p = '\'';
+ }
+ }
+ }
+
+ if(n){
+ /* Suck out the charset & lang while decoding hex */
+ p = &buf[n];
+ for(i = 0; (buf[i] = *p) != '\0'; i++)
+ if(*p++ == '%' && isxpair(p)){
+ buf[i] = X2C(p);
+ p += 2;
+ }
+ }
+ else
+ fs_give((void **) &buf); /* problems!?! */
+
+ /*
+ * Callers will expect the returned value to be UTF-8
+ * text, so we may need to translate here.
+ */
+ if(buf)
+ converted = convert_to_utf8(buf, cs, 0);
+
+ if(converted && converted != buf){
+ fs_give((void **) &buf);
+ buf = converted;
+ }
+ }
+
+ return(buf);
+ }
+ else
+ return(cpystr(parms->value ? parms->value : ""));
+ }
+
+ return(NULL);
+}
+
+
+int
+rfc2231_output(STORE_S *so, char *attrib, char *value, char *specials, char *charset)
+{
+ int i, line = 0, encode = 0, quote = 0;
+
+ /*
+ * scan for hibit first since encoding clue has to
+ * come on first line if any parms are broken up...
+ */
+ for(i = 0; value && value[i]; i++)
+ if(value[i] & 0x80){
+ encode++;
+ break;
+ }
+
+ for(i = 0; ; i++){
+ if(!(value && value[i]) || i > 80){ /* flush! */
+ if((line++ && !so_puts(so, ";\015\012 "))
+ || !so_puts(so, attrib))
+ return(0);
+
+ if(value){
+ if(((value[i] || line > 1) /* more lines or already lines */
+ && !(so_writec('*', so)
+ && so_puts(so, int2string(line - 1))))
+ || (encode && !so_writec('*', so))
+ || !so_writec('=', so)
+ || (quote && !so_writec('\"', so))
+ || ((line == 1 && encode)
+ && !(so_puts(so, charset ? charset : UNKNOWN_CHARSET)
+ && so_puts(so, "''"))))
+ return(0);
+
+ while(i--){
+ if(*value & 0x80){
+ char tmp[3], *p;
+
+ p = tmp;
+ C2XPAIR(*value, p);
+ *p = '\0';
+ if(!(so_writec('%', so) && so_puts(so, tmp)))
+ return(0);
+ }
+ else if(((*value == '\\' || *value == '\"')
+ && !so_writec('\\', so))
+ || !so_writec(*value, so))
+ return(0);
+
+ value++;
+ }
+
+ if(quote && !so_writec('\"', so))
+ return(0);
+
+ if(*value) /* more? */
+ i = quote = 0; /* reset! */
+ else
+ return(1); /* done! */
+ }
+ else
+ return(1);
+ }
+
+ if(!quote && strchr(specials, value[i]))
+ quote++;
+ }
+}
+
+
+PARMLIST_S *
+rfc2231_newparmlist(PARAMETER *params)
+{
+ PARMLIST_S *p = NULL;
+
+ if(params){
+ p = (PARMLIST_S *) fs_get(sizeof(PARMLIST_S));
+ memset(p, 0, sizeof(PARMLIST_S));
+ p->list = params;
+ }
+
+ return(p);
+}
+
+
+void
+rfc2231_free_parmlist(PARMLIST_S **p)
+{
+ if(*p){
+ if((*p)->value)
+ fs_give((void **) &(*p)->value);
+
+ mail_free_body_parameter(&(*p)->seen);
+ fs_give((void **) p);
+ }
+}
+
+
+int
+rfc2231_list_params(PARMLIST_S *plist)
+{
+ PARAMETER *pp, **ppp;
+ int i;
+
+ if(plist->value)
+ fs_give((void **) &plist->value);
+
+ for(pp = plist->list; pp; pp = pp->next){
+ /* get a name */
+ for(i = 0; i < 32; i++)
+ if(!(plist->attrib[i] = pp->attribute[i]) || pp->attribute[i] == '*'){
+ plist->attrib[i] = '\0';
+
+ for(ppp = &plist->seen;
+ *ppp && strucmp((*ppp)->attribute, plist->attrib);
+ ppp = &(*ppp)->next)
+ ;
+
+ if(!*ppp){
+ plist->list = pp->next;
+ *ppp = mail_newbody_parameter(); /* add to seen list */
+ (*ppp)->attribute = cpystr(plist->attrib);
+ plist->value = parameter_val(pp,plist->attrib);
+ return(TRUE);
+ }
+
+ break;
+ }
+
+ if(i >= 32)
+ q_status_message1(SM_ORDER | SM_DING, 0, 3,
+ "Overly long attachment parameter ignored: %.25s...",
+ pp->attribute);
+ }
+
+
+ return(FALSE);
+}
diff --git a/pith/rfc2231.h b/pith/rfc2231.h
new file mode 100644
index 00000000..12915f39
--- /dev/null
+++ b/pith/rfc2231.h
@@ -0,0 +1,32 @@
+/*
+ * $Id: rfc2231.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_RFC2231_INCLUDED
+#define PITH_RFC2231_INCLUDED
+
+
+#include "../pith/atttype.h"
+#include "../pith/store.h"
+
+
+/* exported protoypes */
+char *rfc2231_get_param(PARAMETER *, char *, char **, char **);
+int rfc2231_output(STORE_S *, char *, char *, char *, char *);
+PARMLIST_S *rfc2231_newparmlist(PARAMETER *);
+void rfc2231_free_parmlist(PARMLIST_S **);
+int rfc2231_list_params(PARMLIST_S *);
+
+
+#endif /* PITH_RFC2231_INCLUDED */
diff --git a/pith/save.c b/pith/save.c
new file mode 100644
index 00000000..957e163b
--- /dev/null
+++ b/pith/save.c
@@ -0,0 +1,1777 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: save.c 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2009 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/save.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/mimedesc.h"
+#include "../pith/filter.h"
+#include "../pith/context.h"
+#include "../pith/folder.h"
+#include "../pith/copyaddr.h"
+#include "../pith/mailview.h"
+#include "../pith/mailcmd.h"
+#include "../pith/bldaddr.h"
+#include "../pith/flag.h"
+#include "../pith/status.h"
+#include "../pith/ablookup.h"
+#include "../pith/news.h"
+#include "../pith/util.h"
+#include "../pith/reply.h"
+#include "../pith/sequence.h"
+#include "../pith/stream.h"
+#include "../pith/options.h"
+
+
+/*
+ * Internal prototypes
+ */
+int save_ex_replace_body(char *, unsigned long *,BODY *,gf_io_t);
+int save_ex_output_body(MAILSTREAM *, long, char *, BODY *, unsigned long *, gf_io_t);
+int save_ex_mask_types(char *, unsigned long *, gf_io_t);
+int save_ex_explain_body(BODY *, unsigned long *, gf_io_t);
+int save_ex_explain_parts(BODY *, int, unsigned long *, gf_io_t);
+int save_ex_output_line(char *, unsigned long *, gf_io_t);
+
+
+/*
+ * pith hook
+ */
+int (*pith_opt_save_create_prompt)(CONTEXT_S *, char *, int);
+int (*pith_opt_save_size_changed_prompt)(long, int);
+
+
+
+/*----------------------------------------------------------------------
+ save_get_default - return default folder name for saving
+ ----*/
+char *
+save_get_default(struct pine *state, ENVELOPE *e, long int rawno,
+ char *section, CONTEXT_S **cntxt)
+{
+ int context_was_set;
+
+ if(!cntxt)
+ return("");
+
+ context_was_set = ((*cntxt) != NULL);
+
+ /* start with the default save context */
+ if(!(*cntxt)
+ && ((*cntxt) = default_save_context(state->context_list)) == NULL)
+ (*cntxt) = state->context_list;
+
+ if(!e || ps_global->save_msg_rule == SAV_RULE_LAST
+ || ps_global->save_msg_rule == SAV_RULE_DEFLT){
+ if(ps_global->save_msg_rule == SAV_RULE_LAST && ps_global->last_save_context){
+ if(!context_was_set)
+ (*cntxt) = ps_global->last_save_context;
+ }
+ else{
+ strncpy(ps_global->last_save_folder,
+ ps_global->VAR_DEFAULT_SAVE_FOLDER,
+ sizeof(ps_global->last_save_folder)-1);
+ ps_global->last_save_folder[sizeof(ps_global->last_save_folder)-1] = '\0';
+
+ /*
+ * If the user entered "inbox" as their default save folder it is very
+ * likely they meant the real inbox, not the inbox in the primary collection.
+ */
+ if(!context_was_set
+ && !strucmp(ps_global->inbox_name, ps_global->last_save_folder))
+ (*cntxt) = state->context_list;
+ }
+ }
+ else{
+ save_get_fldr_from_env(ps_global->last_save_folder,
+ sizeof(ps_global->last_save_folder),
+ e, state, rawno, section);
+ /* somebody expunged current message */
+ if(sp_expunge_count(ps_global->mail_stream))
+ return(NULL);
+ }
+
+ return(ps_global->last_save_folder);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Grope through envelope to find default folder name to save to
+
+ Args: fbuf -- Buffer to return result in
+ nfbuf -- Size of fbuf
+ e -- The envelope to look in
+ state -- Usual pine state
+ rawmsgno -- Raw c-client sequence number of message
+ section -- Mime section of header data (for message/rfc822)
+
+ Result: The appropriate default folder name is copied into fbuf.
+ ----*/
+void
+save_get_fldr_from_env(char *fbuf, int nfbuf, ENVELOPE *e, struct pine *state,
+ long int rawmsgno, char *section)
+{
+ char fakedomain[2];
+ ADDRESS *tmp_adr = NULL;
+ char buf[MAX(MAXFOLDER,MAX_NICKNAME) + 1];
+ char *bufp;
+ char *folder_name = NULL;
+ static char botch[] = "programmer botch, unknown message save rule";
+ unsigned save_msg_rule;
+
+ if(!e)
+ return;
+
+ /* copy this because we might change it below */
+ save_msg_rule = state->save_msg_rule;
+
+ /* first get the relevant address to base the folder name on */
+ switch(save_msg_rule){
+ case SAV_RULE_FROM:
+ case SAV_RULE_NICK_FROM:
+ case SAV_RULE_NICK_FROM_DEF:
+ case SAV_RULE_FCC_FROM:
+ case SAV_RULE_FCC_FROM_DEF:
+ case SAV_RULE_RN_FROM:
+ case SAV_RULE_RN_FROM_DEF:
+ tmp_adr = e->from ? copyaddr(e->from)
+ : e->sender ? copyaddr(e->sender) : NULL;
+ break;
+
+ case SAV_RULE_SENDER:
+ case SAV_RULE_NICK_SENDER:
+ case SAV_RULE_NICK_SENDER_DEF:
+ case SAV_RULE_FCC_SENDER:
+ case SAV_RULE_FCC_SENDER_DEF:
+ case SAV_RULE_RN_SENDER:
+ case SAV_RULE_RN_SENDER_DEF:
+ tmp_adr = e->sender ? copyaddr(e->sender)
+ : e->from ? copyaddr(e->from) : NULL;
+ break;
+
+ case SAV_RULE_REPLYTO:
+ case SAV_RULE_NICK_REPLYTO:
+ case SAV_RULE_NICK_REPLYTO_DEF:
+ case SAV_RULE_FCC_REPLYTO:
+ case SAV_RULE_FCC_REPLYTO_DEF:
+ case SAV_RULE_RN_REPLYTO:
+ case SAV_RULE_RN_REPLYTO_DEF:
+ tmp_adr = e->reply_to ? copyaddr(e->reply_to)
+ : e->from ? copyaddr(e->from)
+ : e->sender ? copyaddr(e->sender) : NULL;
+ break;
+
+ case SAV_RULE_RECIP:
+ case SAV_RULE_NICK_RECIP:
+ case SAV_RULE_NICK_RECIP_DEF:
+ case SAV_RULE_FCC_RECIP:
+ case SAV_RULE_FCC_RECIP_DEF:
+ case SAV_RULE_RN_RECIP:
+ case SAV_RULE_RN_RECIP_DEF:
+ /* news */
+ if(state->mail_stream && IS_NEWS(state->mail_stream)){
+ char *tmp_a_string, *ng_name;
+
+ fakedomain[0] = '@';
+ fakedomain[1] = '\0';
+
+ /* find the news group name */
+ if((ng_name = strstr(state->mail_stream->mailbox,"#news")) != NULL)
+ ng_name += 6;
+ else
+ ng_name = state->mail_stream->mailbox; /* shouldn't happen */
+
+ /* copy this string so rfc822_parse_adrlist can't blast it */
+ tmp_a_string = cpystr(ng_name);
+ /* make an adr */
+ rfc822_parse_adrlist(&tmp_adr, tmp_a_string, fakedomain);
+ fs_give((void **)&tmp_a_string);
+ if(tmp_adr && tmp_adr->host && tmp_adr->host[0] == '@')
+ tmp_adr->host[0] = '\0';
+ }
+ /* not news */
+ else{
+ static char *fields[] = {"Resent-To", NULL};
+ char *extras, *values[sizeof(fields)/sizeof(fields[0])];
+
+ extras = pine_fetchheader_lines(state->mail_stream, rawmsgno,
+ section, fields);
+ if(extras){
+ long i;
+
+ memset(values, 0, sizeof(fields));
+ simple_header_parse(extras, fields, values);
+ fs_give((void **)&extras);
+
+ for(i = 0; i < sizeof(fields)/sizeof(fields[0]); i++)
+ if(values[i]){
+ if(tmp_adr) /* take last matching value */
+ mail_free_address(&tmp_adr);
+
+ /* build a temporary address list */
+ fakedomain[0] = '@';
+ fakedomain[1] = '\0';
+ rfc822_parse_adrlist(&tmp_adr, values[i], fakedomain);
+ fs_give((void **)&values[i]);
+ }
+ }
+
+ if(!tmp_adr)
+ tmp_adr = e->to ? copyaddr(e->to) : NULL;
+ }
+
+ break;
+
+ default:
+ panic(botch);
+ break;
+ }
+
+ /* For that address, lookup the fcc or nickname from address book */
+ switch(save_msg_rule){
+ case SAV_RULE_NICK_FROM:
+ case SAV_RULE_NICK_SENDER:
+ case SAV_RULE_NICK_REPLYTO:
+ case SAV_RULE_NICK_RECIP:
+ case SAV_RULE_FCC_FROM:
+ case SAV_RULE_FCC_SENDER:
+ case SAV_RULE_FCC_REPLYTO:
+ case SAV_RULE_FCC_RECIP:
+ case SAV_RULE_NICK_FROM_DEF:
+ case SAV_RULE_NICK_SENDER_DEF:
+ case SAV_RULE_NICK_REPLYTO_DEF:
+ case SAV_RULE_NICK_RECIP_DEF:
+ case SAV_RULE_FCC_FROM_DEF:
+ case SAV_RULE_FCC_SENDER_DEF:
+ case SAV_RULE_FCC_REPLYTO_DEF:
+ case SAV_RULE_FCC_RECIP_DEF:
+ switch(save_msg_rule){
+ case SAV_RULE_NICK_FROM:
+ case SAV_RULE_NICK_SENDER:
+ case SAV_RULE_NICK_REPLYTO:
+ case SAV_RULE_NICK_RECIP:
+ case SAV_RULE_NICK_FROM_DEF:
+ case SAV_RULE_NICK_SENDER_DEF:
+ case SAV_RULE_NICK_REPLYTO_DEF:
+ case SAV_RULE_NICK_RECIP_DEF:
+ bufp = get_nickname_from_addr(tmp_adr, buf, sizeof(buf));
+ break;
+
+ case SAV_RULE_FCC_FROM:
+ case SAV_RULE_FCC_SENDER:
+ case SAV_RULE_FCC_REPLYTO:
+ case SAV_RULE_FCC_RECIP:
+ case SAV_RULE_FCC_FROM_DEF:
+ case SAV_RULE_FCC_SENDER_DEF:
+ case SAV_RULE_FCC_REPLYTO_DEF:
+ case SAV_RULE_FCC_RECIP_DEF:
+ bufp = get_fcc_from_addr(tmp_adr, buf, sizeof(buf));
+ break;
+ }
+
+ if(bufp && *bufp){
+ istrncpy(fbuf, bufp, nfbuf - 1);
+ fbuf[nfbuf - 1] = '\0';
+ }
+ else
+ /* fall back to non-nick/non-fcc version of rule */
+ switch(save_msg_rule){
+ case SAV_RULE_NICK_FROM:
+ case SAV_RULE_FCC_FROM:
+ save_msg_rule = SAV_RULE_FROM;
+ break;
+
+ case SAV_RULE_NICK_SENDER:
+ case SAV_RULE_FCC_SENDER:
+ save_msg_rule = SAV_RULE_SENDER;
+ break;
+
+ case SAV_RULE_NICK_REPLYTO:
+ case SAV_RULE_FCC_REPLYTO:
+ save_msg_rule = SAV_RULE_REPLYTO;
+ break;
+
+ case SAV_RULE_NICK_RECIP:
+ case SAV_RULE_FCC_RECIP:
+ save_msg_rule = SAV_RULE_RECIP;
+ break;
+
+ default:
+ istrncpy(fbuf, ps_global->VAR_DEFAULT_SAVE_FOLDER, nfbuf - 1);
+ fbuf[nfbuf - 1] = '\0';
+ break;
+ }
+
+ break;
+ }
+
+ /* Realname */
+ switch(save_msg_rule){
+ case SAV_RULE_RN_FROM_DEF:
+ case SAV_RULE_RN_FROM:
+ case SAV_RULE_RN_SENDER_DEF:
+ case SAV_RULE_RN_SENDER:
+ case SAV_RULE_RN_RECIP_DEF:
+ case SAV_RULE_RN_RECIP:
+ case SAV_RULE_RN_REPLYTO_DEF:
+ case SAV_RULE_RN_REPLYTO:
+ /* Fish out the realname */
+ if(tmp_adr && tmp_adr->personal && tmp_adr->personal[0])
+ folder_name = (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, tmp_adr->personal);
+
+ if(folder_name && folder_name[0]){
+ istrncpy(fbuf, folder_name, nfbuf - 1);
+ fbuf[nfbuf - 1] = '\0';
+ }
+ else{ /* fall back to other behaviors */
+ switch(save_msg_rule){
+ case SAV_RULE_RN_FROM:
+ save_msg_rule = SAV_RULE_FROM;
+ break;
+
+ case SAV_RULE_RN_SENDER:
+ save_msg_rule = SAV_RULE_SENDER;
+ break;
+
+ case SAV_RULE_RN_RECIP:
+ save_msg_rule = SAV_RULE_RECIP;
+ break;
+
+ case SAV_RULE_RN_REPLYTO:
+ save_msg_rule = SAV_RULE_REPLYTO;
+ break;
+
+ default:
+ istrncpy(fbuf, ps_global->VAR_DEFAULT_SAVE_FOLDER, nfbuf - 1);
+ fbuf[nfbuf - 1] = '\0';
+ break;
+ }
+ }
+
+ break;
+ }
+
+ /* get the username out of the mailbox for this address */
+ switch(save_msg_rule){
+ case SAV_RULE_FROM:
+ case SAV_RULE_SENDER:
+ case SAV_RULE_REPLYTO:
+ case SAV_RULE_RECIP:
+ /*
+ * Fish out the user's name from the mailbox portion of
+ * the address and put it in folder.
+ */
+ folder_name = (tmp_adr && tmp_adr->mailbox && tmp_adr->mailbox[0])
+ ? tmp_adr->mailbox : NULL;
+ if(!get_uname(folder_name, fbuf, nfbuf)){
+ istrncpy(fbuf, ps_global->VAR_DEFAULT_SAVE_FOLDER, nfbuf - 1);
+ fbuf[nfbuf - 1] = '\0';
+ }
+
+ break;
+ }
+
+ if(tmp_adr)
+ mail_free_address(&tmp_adr);
+}
+
+
+/*----------------------------------------------------------------------
+ Do the work of actually saving messages to a folder
+
+ Args: state -- pine state struct (for stream pointers)
+ stream -- source stream, which msgmap refers to
+ context -- context to interpret name in if not fully qualified
+ folder -- The folder to save the message in
+ msgmap -- message map of currently selected messages
+ flgs -- Possible bits are
+ SV_DELETE - delete after saving
+ SV_FOR_FILT - called from filtering function, not save
+ SV_FIX_DELS - remove Del mark before saving
+ SV_INBOXWOCNTXT - "inbox" is interpreted without context making
+ it the one-true inbox instead
+
+ Result: Returns number of messages saved
+
+ Note: There's a bit going on here; temporary clearing of deleted flags
+ since they are *not* preserved, picking or creating the stream for
+ copy or append, and dealing with errors...
+ We try to preserve user keywords by setting them in the destination.
+ ----*/
+long
+save(struct pine *state, MAILSTREAM *stream, CONTEXT_S *context, char *folder,
+ MSGNO_S *msgmap, int flgs)
+{
+ int rv, rc, j, our_stream = 0, cancelled = 0;
+ int delete, filter, k, worry_about_keywords = 0;
+ char *save_folder, *seq, *flags = NULL, date[64], tmp[MAILTMPLEN];
+ long i, nmsgs, rawno;
+ size_t len;
+ STORE_S *so = NULL;
+ MAILSTREAM *save_stream = NULL;
+ MESSAGECACHE *mc;
+
+ delete = flgs & SV_DELETE;
+ filter = flgs & SV_FOR_FILT;
+
+ if(strucmp(folder, state->inbox_name) == 0 && flgs & SV_INBOXWOCNTXT){
+ save_folder = state->VAR_INBOX_PATH;
+ context = NULL;
+ }
+ else
+ save_folder = folder;
+
+ /*
+ * Because the COPY/APPEND command doesn't always create keywords when they
+ * aren't already defined in a mailbox, we need to ensure that the keywords
+ * exist in the destination (are defined and settable) before we do the copies.
+ * Here's what the code is doing
+ *
+ * If we have keywords set in the source messages
+ * Add a dummy message to destination mailbox
+ *
+ * for each keyword that is set in the set of messages we're saving
+ * set the keyword in that message (thus creating it)
+ *
+ * remember deleted messages
+ * undelete them
+ * delete dummy message
+ * expunge
+ * delete remembered messages
+ *
+ * After that the assumption is that the keywords will be saved by a
+ * COPY command. We need to set the flags string ourself for appends.
+ */
+
+ /* are any keywords set in the source messages? */
+ for(i = mn_first_cur(msgmap); !worry_about_keywords && i > 0L; i = mn_next_cur(msgmap)){
+ rawno = mn_m2raw(msgmap, i);
+ mc = (rawno > 0L && stream && rawno <= stream->nmsgs)
+ ? mail_elt(stream, rawno) : NULL;
+ if(mc && mc->user_flags)
+ worry_about_keywords++;
+ }
+
+ if(worry_about_keywords){
+ MAILSTREAM *dstn_stream = NULL;
+ int already_open = 0;
+ int we_blocked_reuse = 0;
+
+ /*
+ * Possible problem created by our stream re-use
+ * strategy. If we are going to open a new stream
+ * here, we want to be sure not to re-use the
+ * stream we are saving _from_, so take it out of the
+ * re-use pool before we call open.
+ */
+ if(sp_flagged(stream, SP_USEPOOL)){
+ we_blocked_reuse++;
+ sp_unflag(stream, SP_USEPOOL);
+ }
+
+ /* see if there is a stream open already */
+ if(!dstn_stream){
+ dstn_stream = context_already_open_stream(context,
+ save_folder,
+ AOS_RW_ONLY);
+ already_open = dstn_stream ? 1 : 0;
+ }
+
+ if(!dstn_stream)
+ dstn_stream = context_open(context, NULL,
+ save_folder,
+ SP_USEPOOL | SP_TEMPUSE,
+ NULL);
+
+ if(dstn_stream && dstn_stream->kwd_create){
+ imapuid_t dummy_uid = 0L;
+ long dummy_msgno, delete_count;
+ int expbits, set;
+ char *flags = NULL;
+ char *user_flag_name, *duser_flag_name;
+
+ /* find keywords that need to be defined */
+ for(k = 0; stream_to_user_flag_name(stream, k); k++){
+ user_flag_name = stream_to_user_flag_name(stream, k);
+ if(user_flag_name && user_flag_name[0]){
+ /* is this flag set in any of the save set? */
+ for(set = 0, i = mn_first_cur(msgmap);
+ !set && i > 0L;
+ i = mn_next_cur(msgmap)){
+ rawno = mn_m2raw(msgmap, i);
+ if(user_flag_is_set(stream, rawno, user_flag_name))
+ set++;
+ }
+
+ if(set){
+ /*
+ * The flag may already be defined in this
+ * mailbox. Check for that first.
+ */
+ for(j = 0; stream_to_user_flag_name(dstn_stream, j); j++){
+ duser_flag_name = stream_to_user_flag_name(dstn_stream, j);
+ if(duser_flag_name && duser_flag_name[0]
+ && !strucmp(duser_flag_name, user_flag_name)){
+ set = 0;
+ break;
+ }
+ }
+ }
+
+ if(set){
+ if(flags == NULL){
+ len = strlen(user_flag_name) + 1;
+ flags = (char *) fs_get((len+1) * sizeof(char));
+ snprintf(flags, len+1, "%s ", user_flag_name);
+ }
+ else{
+ char *p;
+ size_t newlen;
+
+ newlen = strlen(user_flag_name) + 1;
+ len += newlen;
+ fs_resize((void **) &flags, (len+1) * sizeof(char));
+ p = flags + strlen(flags);
+ snprintf(p, newlen+1, "%s ", user_flag_name);
+ }
+ }
+ }
+ }
+
+ if(flags){
+ char *p;
+ size_t newlen;
+ STRING msg;
+ char dummymsg[1000];
+ char *id = NULL;
+ char *idused;
+ appenduid_t *au;
+
+ newlen = strlen("\\DELETED");
+ len += newlen;
+ fs_resize((void **) &flags, (len+1) * sizeof(char));
+ p = flags + strlen(flags);
+ snprintf(p, newlen+1, "%s", "\\DELETED");
+
+ id = generate_message_id();
+ idused = id ? id : "<xyz>";
+ snprintf(dummymsg, sizeof(dummymsg), "Date: Thu, 18 May 2006 00:00 -0700\r\nFrom: dummy@example.com\r\nSubject: dummy\r\nMessage-ID: %s\r\n\r\ndummy\r\n", idused);
+
+ /*
+ * We need to get the uid of the message we are about to
+ * append so that we can delete it when we're done and
+ * so we don't affect other messages.
+ */
+
+ if(is_imap_stream(dstn_stream) && LEVELUIDPLUS (dstn_stream)){
+ au = mail_parameters(NIL, GET_APPENDUID, NIL);
+ mail_parameters(NIL, SET_APPENDUID, (void *) appenduid_cb);
+ }
+
+ INIT(&msg, mail_string, (void *) dummymsg, strlen(dummymsg));
+ if(pine_mail_append(dstn_stream, dstn_stream->mailbox, &msg)){
+
+ (void) pine_mail_ping(dstn_stream);
+
+ if(is_imap_stream(dstn_stream) && LEVELUIDPLUS (dstn_stream))
+ dummy_uid = get_last_append_uid();
+
+ if(dummy_uid == 0L){
+ dummy_msgno = get_msgno_by_msg_id(dstn_stream, idused,
+ sp_msgmap(dstn_stream));
+ if(dummy_msgno <= 0L || dummy_msgno > dstn_stream->nmsgs)
+ dummy_msgno = dstn_stream->nmsgs;
+
+ rawno = mn_m2raw(sp_msgmap(dstn_stream), dummy_msgno);
+ if(rawno > 0L && rawno <= dstn_stream->nmsgs)
+ dummy_uid = mail_uid(dstn_stream, rawno);
+
+ if(dummy_uid == 0L)
+ dummy_msgno = dstn_stream->nmsgs;
+ }
+
+ /*
+ * We need to remember which messages are deleted,
+ * undelete them, do the expunge, then delete them again.
+ */
+ delete_count = count_flagged(dstn_stream, F_DEL);
+ if(delete_count){
+ for(i = 1L; i <= dstn_stream->nmsgs; i++)
+ if(((mc = mail_elt(dstn_stream, i)) && mc->valid && mc->deleted)
+ || (mc && !mc->valid && mc->searched)){
+ mc->sequence = 1;
+ expbits = MSG_EX_DELETE;
+ msgno_exceptions(dstn_stream, i, "0", &expbits, TRUE);
+ }
+ else if((mc = mail_elt(dstn_stream, i)) != NULL)
+ mc->sequence = 0;
+
+ if((seq = build_sequence(dstn_stream, NULL, NULL)) != NULL){
+ mail_flag(dstn_stream, seq, "\\DELETED", ST_SILENT);
+ fs_give((void **) &seq);
+ }
+ }
+
+ if(dummy_uid > 0L)
+ mail_flag(dstn_stream, ulong2string(dummy_uid),
+ flags, ST_SET | ST_UID | ST_SILENT);
+ else
+ mail_flag(dstn_stream, ulong2string(dummy_msgno),
+ flags, ST_SET | ST_SILENT);
+
+ ps_global->mm_log_error = 0;
+ ps_global->expunge_in_progress = 1;
+ mail_expunge(dstn_stream);
+ ps_global->expunge_in_progress = 0;
+
+ if(delete_count){
+ for(i = 1L; i <= dstn_stream->nmsgs; i++)
+ if((mc = mail_elt(dstn_stream, i)) != NULL){
+ mc->sequence
+ = (msgno_exceptions(dstn_stream, i, "0", &expbits, FALSE)
+ && (expbits & MSG_EX_DELETE));
+
+ /*
+ * Remove the EX_DELETE bit in case we're still using
+ * this stream.
+ */
+ if(mc->sequence){
+ expbits &= ~MSG_EX_DELETE;
+ msgno_exceptions(dstn_stream, i, "0", &expbits, TRUE);
+ }
+ }
+
+ if((seq = build_sequence(dstn_stream, NULL, NULL)) != NULL){
+ mail_flag(dstn_stream, seq, "\\DELETED", ST_SET | ST_SILENT);
+ fs_give((void **) &seq);
+ }
+ }
+ }
+
+ if(is_imap_stream(dstn_stream) && LEVELUIDPLUS (dstn_stream))
+ mail_parameters(NIL, SET_APPENDUID, (void *) au);
+
+ if(id)
+ fs_give((void **) &id);
+
+ fs_give((void **) &flags);
+ }
+ }
+
+ if(dstn_stream && !already_open)
+ pine_mail_close(dstn_stream);
+
+ if(we_blocked_reuse)
+ sp_set_flags(stream, sp_flags(stream) | SP_USEPOOL);
+ }
+
+ /*
+ * If any of the messages have exceptional attachment handling
+ * we have to fall thru below to do the APPEND by hand...
+ */
+ if(!msgno_any_deletedparts(stream, msgmap)){
+ int loc_to_loc = 0;
+
+ /*
+ * Compare the current stream (the save's source) and the stream
+ * the destination folder will need...
+ */
+ context_apply(tmp, context, save_folder, sizeof(tmp));
+
+ /*
+ * If we're going to be doing a cross-format copy we're better off
+ * using the else code below that knows how to do multi-append.
+ * The part in the if is leaving save_stream set to NULL in the
+ * case that the stream is local and the folder is local and they
+ * are different formats (like unix and tenex). That will cause us
+ * to fall thru to the APPEND case which is faster than using
+ * copy which will use our imap_proxycopy which doesn't know
+ * about multiappend.
+ */
+ loc_to_loc = stream && stream->dtb
+ && stream->dtb->flags & DR_LOCAL && !IS_REMOTE(tmp);
+ if(!loc_to_loc || (stream->dtb->valid && (*stream->dtb->valid)(tmp)))
+ save_stream = loc_to_loc ? stream
+ : context_same_stream(context, save_folder, stream);
+ }
+
+ /* if needed, this'll get set in mm_notify */
+ ps_global->try_to_create = 0;
+ rv = rc = 0;
+ nmsgs = 0L;
+
+ /*
+ * At this point, if we have a save_stream, then none of the messages
+ * being saved involve special handling that would require our use
+ * of mail_append, so go with mail_copy since in the IMAP case it
+ * means no data on the wire...
+ */
+ if(save_stream){
+ char *dseq = NULL, *oseq;
+
+ if((flgs & SV_FIX_DELS) &&
+ (dseq = currentf_sequence(stream, msgmap, F_DEL, NULL,
+ 0, NULL, NULL)))
+ mail_flag(stream, dseq, "\\DELETED", 0L);
+
+ seq = currentf_sequence(stream, msgmap, 0L, &nmsgs, 0, NULL, NULL);
+ if(!(flgs & SV_PRESERVE)
+ && (F_ON(F_AGG_SEQ_COPY, ps_global)
+ || (mn_get_sort(msgmap) == SortArrival && !mn_get_revsort(msgmap)))){
+
+ /*
+ * currentf_sequence() above lit all the elt "sequence"
+ * bits of the interesting messages. Now, build a sequence
+ * that preserves sort order...
+ */
+ oseq = build_sequence(stream, msgmap, &nmsgs);
+ }
+ else{
+ oseq = NULL; /* no single sequence! */
+ nmsgs = 0L;
+ i = mn_first_cur(msgmap); /* set first to copy */
+ }
+
+ do{
+ while(!(rv = (int) context_copy(context, save_stream,
+ oseq ? oseq : long2string(mn_m2raw(msgmap, i)),
+ save_folder))){
+ if(rc++ || !ps_global->try_to_create) /* abysmal failure! */
+ break; /* c-client returned error? */
+
+ if((context && context->use & CNTXT_INCMNG)
+ && context_isambig(save_folder)){
+ q_status_message(SM_ORDER, 3, 5,
+ _("Can only save to existing folders in Incoming Collection"));
+ break;
+ }
+
+ ps_global->try_to_create = 0; /* reset for next time */
+ if((j = create_for_save(context, save_folder)) < 1){
+ if(j < 0)
+ cancelled = 1; /* user cancels */
+
+ break;
+ }
+ }
+
+ if(rv){ /* failure or finished? */
+ if(oseq) /* all done? */
+ break;
+ else
+ nmsgs++;
+ }
+ else{ /* failure! */
+ if(oseq)
+ nmsgs = 0L; /* nothing copy'd */
+
+ break;
+ }
+ }
+ while((i = mn_next_cur(msgmap)) > 0L);
+
+ if(rv && delete) /* delete those saved */
+ mail_flag(stream, seq, "\\DELETED", ST_SET);
+ else if(dseq) /* or restore previous state */
+ mail_flag(stream, dseq, "\\DELETED", ST_SET);
+
+ if(dseq) /* clean up */
+ fs_give((void **)&dseq);
+
+ if(oseq)
+ fs_give((void **)&oseq);
+
+ fs_give((void **)&seq);
+ }
+ else{
+ /*
+ * Special handling requires mail_append. See if we can
+ * re-use stream source messages are on...
+ */
+ save_stream = context_same_stream(context, save_folder, stream);
+
+ /*
+ * IF the destination's REMOTE, open a stream here so c-client
+ * doesn't have to open it for each aggregate save...
+ */
+ if(!save_stream){
+ if(context_apply(tmp, context, save_folder, sizeof(tmp))[0] == '{'
+ && (save_stream = context_open(context, NULL,
+ save_folder,
+ OP_HALFOPEN | SP_USEPOOL | SP_TEMPUSE,
+ NULL)))
+ our_stream = 1;
+ }
+
+ /*
+ * Allocate a storage object to temporarily store the message
+ * object in. Below it'll get mapped into a c-client STRING struct
+ * in preparation for handing off to context_append...
+ */
+ if(!(so = so_get(CharStar, NULL, WRITE_ACCESS))){
+ dprint((1, "Can't allocate store for save: %s\n",
+ error_description(errno)));
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ "Problem creating space for message text.");
+ }
+
+ /*
+ * get a sequence of invalid elt's so we can get their flags...
+ */
+ if((seq = invalid_elt_sequence(stream, msgmap)) != NULL){
+ mail_fetch_fast(stream, seq, 0L);
+ fs_give((void **) &seq);
+ }
+
+ /*
+ * If we're supposed set the deleted flag, clear the elt bit
+ * we'll use to build the sequence later...
+ */
+ if(delete)
+ for(i = 1L; i <= stream->nmsgs; i++)
+ if((mc = mail_elt(stream, i)) != NULL)
+ mc->sequence = 0;
+
+ nmsgs = 0L;
+
+ if(pith_opt_save_size_changed_prompt)
+ (*pith_opt_save_size_changed_prompt)(0L, SSCP_INIT);
+
+ /*
+ * if there is more than one message, do multiappend.
+ * otherwise, we can use our already open stream.
+ */
+ if(!save_stream || !is_imap_stream(save_stream) ||
+ (LEVELMULTIAPPEND(save_stream) && mn_total_cur(msgmap) > 1)){
+ APPENDPACKAGE pkg;
+ STRING msg;
+
+ pkg.stream = stream;
+ /* tell save_fetch_append_cb whether or not to leave deleted flag */
+ pkg.flags = flgs & SV_FIX_DELS ? NULL : cpystr("\\DELETED");
+ pkg.date = date;
+ pkg.msg = &msg;
+ pkg.msgmap = msgmap;
+
+ if ((pkg.so = so) && ((pkg.msgno = mn_first_cur(msgmap)) > 0L)) {
+ so_truncate(so, 0L);
+
+ /*
+ * we've gotta make sure this is a stream that we've
+ * opened ourselves.
+ */
+ rc = 0;
+ while(!(rv = context_append_multiple(context,
+ our_stream ? save_stream
+ : NULL, save_folder,
+ save_fetch_append_cb,
+ &pkg,
+ stream))) {
+
+ if(rc++ || !ps_global->try_to_create)
+ break;
+ if((context && context->use & CNTXT_INCMNG)
+ && context_isambig(save_folder)){
+ q_status_message(SM_ORDER, 3, 5,
+ _("Can only save to existing folders in Incoming Collection"));
+ break;
+ }
+
+ ps_global->try_to_create = 0;
+ if((j = create_for_save(context, save_folder)) < 1){
+ if(j < 0)
+ cancelled = 1;
+ break;
+ }
+ }
+
+ if(pkg.flags)
+ fs_give((void **) &pkg.flags);
+
+ ps_global->noshow_error = 0;
+
+ if(rv){
+ /*
+ * Success! Count it, and if it's not already deleted and
+ * it's supposed to be, mark it to get deleted later...
+ */
+ for(i = mn_first_cur(msgmap); so && i > 0L;
+ i = mn_next_cur(msgmap)){
+ nmsgs++;
+ if(delete){
+ rawno = mn_m2raw(msgmap, i);
+ mc = (rawno > 0L && stream && rawno <= stream->nmsgs)
+ ? mail_elt(stream, rawno) : NULL;
+ if(mc && !mc->deleted)
+ mc->sequence = 1; /* mark for later deletion */
+ }
+ }
+ }
+ }
+ else
+ cancelled = 1; /* No messages to append! */
+
+ if(sp_expunge_count(stream))
+ cancelled = 1; /* All bets are off! */
+ }
+ else
+ for(i = mn_first_cur(msgmap); so && i > 0L; i = mn_next_cur(msgmap)){
+ int preserve_these_flags;
+
+ so_truncate(so, 0L);
+
+ rawno = mn_m2raw(msgmap, i);
+ mc = (rawno > 0L && stream && rawno <= stream->nmsgs)
+ ? mail_elt(stream, rawno) : NULL;
+
+ /* always preserve these flags */
+ preserve_these_flags = F_ANS|F_FWD|F_FLAG|F_SEEN|F_KEYWORD;
+ /* maybe preserve deleted flag */
+ preserve_these_flags |= flgs & SV_FIX_DELS ? 0 : F_DEL;
+ flags = flag_string(stream, rawno, preserve_these_flags);
+
+ if(mc && mc->day)
+ mail_date(date, mc);
+ else
+ *date = '\0';
+
+ rv = save_fetch_append(stream, mn_m2raw(msgmap, i),
+ NULL, save_stream, save_folder, context,
+ mc ? mc->rfc822_size : 0L, flags, date, so);
+
+ if(flags)
+ fs_give((void **) &flags);
+
+ if(sp_expunge_count(stream))
+ rv = -1; /* All bets are off! */
+
+ if(rv == 1){
+ /*
+ * Success! Count it, and if it's not already deleted and
+ * it's supposed to be, mark it to get deleted later...
+ */
+ nmsgs++;
+ if(delete){
+ rawno = mn_m2raw(msgmap, i);
+ mc = (rawno > 0L && stream && rawno <= stream->nmsgs)
+ ? mail_elt(stream, rawno) : NULL;
+ if(mc && !mc->deleted)
+ mc->sequence = 1; /* mark for later deletion */
+ }
+ }
+ else{
+ if(rv == -1)
+ cancelled = 1; /* else horrendous failure */
+
+ break;
+ }
+ }
+
+ if(pith_opt_save_size_changed_prompt)
+ (*pith_opt_save_size_changed_prompt)(0L, SSCP_END);
+
+ if(our_stream)
+ pine_mail_close(save_stream);
+
+ if(so)
+ so_give(&so);
+
+ if(delete && (seq = build_sequence(stream, NULL, NULL))){
+ mail_flag(stream, seq, "\\DELETED", ST_SET);
+ fs_give((void **)&seq);
+ }
+ }
+
+ ps_global->try_to_create = 0; /* reset for next time */
+ if(!cancelled && nmsgs < mn_total_cur(msgmap)){
+ dprint((1, "FAILED save of msg %s (c-client sequence #)\n",
+ long2string(mn_m2raw(msgmap, mn_get_cur(msgmap)))));
+ if((mn_total_cur(msgmap) > 1L) && nmsgs != 0){
+ /* this shouldn't happen cause it should be all or nothing */
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,
+ "%ld of %ld messages saved before error occurred",
+ nmsgs, mn_total_cur(msgmap));
+ dprint((1, "\t%s\n", tmp_20k_buf));
+ q_status_message(SM_ORDER, 3, 5, tmp_20k_buf);
+ }
+ else if(mn_total_cur(msgmap) == 1){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,
+ "%s to folder \"%s\" FAILED",
+ filter ? "Filter" : "Save",
+ strsquish(tmp_20k_buf+500, 500, folder, 35));
+ dprint((1, "\t%s\n", tmp_20k_buf));
+ q_status_message(SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,
+ "%s of %s messages to folder \"%s\" FAILED",
+ filter ? "Filter" : "Save", comatose(mn_total_cur(msgmap)),
+ strsquish(tmp_20k_buf+500, 500, folder, 35));
+ dprint((1, "\t%s\n", tmp_20k_buf));
+ q_status_message(SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
+ }
+ }
+
+ return(nmsgs);
+}
+
+
+/* Append message callback
+ * Accepts: MAIL stream
+ * append data package
+ * pointer to return initial flags
+ * pointer to return message internal date
+ * pointer to return stringstruct of message or NIL to stop
+ * Returns: T if success (have message or stop), NIL if error
+ */
+
+long save_fetch_append_cb(MAILSTREAM *stream, void *data, char **flags,
+ char **date, STRING **message)
+{
+ unsigned long size = 0;
+ APPENDPACKAGE *pkg = (APPENDPACKAGE *) data;
+ MESSAGECACHE *mc;
+ char *fetch;
+ int rc;
+ unsigned long raw, hlen, tlen, mlen;
+
+ if(pkg->so && (pkg->msgno > 0L)) {
+ raw = mn_m2raw(pkg->msgmap, pkg->msgno);
+ mc = (raw > 0L && pkg->stream && raw <= pkg->stream->nmsgs)
+ ? mail_elt(pkg->stream, raw) : NULL;
+ if(mc){
+ int preserve_these_flags = F_ANS|F_FWD|F_FLAG|F_SEEN|F_KEYWORD;
+
+ size = mc->rfc822_size;
+
+ /*
+ * If the caller wants us to preserve the state of the
+ * \DELETED flag then pkg->flags will be \DELETED, otherwise
+ * let it be undeleted. Fix from Eduardo Chappa.
+ */
+ if(pkg->flags){
+ if(strstr(pkg->flags,"\\DELETED"))
+ preserve_these_flags |= F_DEL;
+
+ /* not used anymore */
+ fs_give((void **) &pkg->flags);
+ }
+
+ pkg->flags = flag_string(pkg->stream, raw, preserve_these_flags);
+ }
+
+ if(mc && mc->day)
+ mail_date(pkg->date, mc);
+ else
+ *pkg->date = '\0';
+ if((fetch = mail_fetch_header(pkg->stream, raw, NULL, NULL, &hlen,
+ FT_PEEK)) != NULL){
+ if(!*pkg->date)
+ saved_date(pkg->date, fetch);
+ }
+ else
+ return(0); /* fetchtext writes error */
+
+ rc = MSG_EX_DELETE; /* "rc" overloaded */
+ if(msgno_exceptions(pkg->stream, raw, NULL, &rc, 0)){
+ char section[64];
+ int failure = 0;
+ BODY *body;
+ gf_io_t pc;
+
+ size = 0; /* all bets off, abort sanity test */
+ gf_set_so_writec(&pc, pkg->so);
+
+ section[0] = '\0';
+ if(!pine_mail_fetch_structure(pkg->stream, raw, &body, 0))
+ return(0);
+
+ if(msgno_part_deleted(pkg->stream, raw, "")){
+ tlen = 0;
+ failure = !save_ex_replace_body(fetch, &hlen, body, pc);
+ }
+ else
+ failure = !(so_nputs(pkg->so, fetch, (long) hlen)
+ && save_ex_output_body(pkg->stream, raw, section,
+ body, &tlen, pc));
+
+ gf_clear_so_writec(pkg->so);
+
+ if(failure)
+ return(0);
+
+ q_status_message(SM_ORDER, 3, 3,
+ /* TRANSLATORS: A warning to user that the message parts
+ they deleted will not be included in the copy they
+ are now saving to. */
+ _("NOTE: Deleted message parts NOT included in saved copy!"));
+
+ }
+ else{
+ if(!so_nputs(pkg->so, fetch, (long) hlen))
+ return(0);
+
+ fetch = pine_mail_fetch_text(pkg->stream, raw, NULL, &tlen, FT_PEEK);
+
+ if(!(fetch && so_nputs(pkg->so, fetch, tlen)))
+ return(0);
+ }
+
+ so_seek(pkg->so, 0L, 0); /* rewind just in case */
+ mlen = hlen + tlen;
+
+ if(size && mlen < size){
+ char buf[128];
+
+ if(sp_dead_stream(pkg->stream)){
+ snprintf(buf, sizeof(buf), _("Cannot save because current folder is Closed"));
+ q_status_message(SM_ORDER | SM_DING, 3, 4, buf);
+ dprint((1, "save_fetch_append_cb: %s\n", buf));
+ return(0);
+ }
+ else{
+ if(pith_opt_save_size_changed_prompt
+ && (*pith_opt_save_size_changed_prompt)(mn_raw2m(pkg->msgmap, raw), 0) == 'y'){
+ snprintf(buf, sizeof(buf),
+ "Message to save shrank! (raw msg# %ld: %ld -> %ld): User says continue",
+ raw, size, mlen);
+ dprint((1, "save_fetch_append_cb: %s", buf));
+ snprintf(buf, sizeof(buf),
+ "Message to save shrank: source msg # %ld may be saved incorrectly",
+ mn_raw2m(pkg->msgmap, raw));
+ q_status_message(SM_ORDER, 0, 3, buf);
+ }
+ else{
+ snprintf(buf, sizeof(buf),
+ "Message to save shrank: raw msg # %ld went from announced size %ld to actual size %ld",
+ raw, size, mlen);
+ dprint((1, "save_fetch_append_cb: %s", buf));
+ return(0);
+ }
+ }
+ }
+
+ INIT(pkg->msg, mail_string, (void *)so_text(pkg->so), mlen);
+ *message = pkg->msg;
+ /* Next message */
+ pkg->msgno = mn_next_cur(pkg->msgmap);
+ }
+ else /* No more messages */
+ *message = NIL;
+
+ *flags = pkg->flags;
+ *date = (pkg->date && *pkg->date) ? pkg->date : NIL;
+ return LONGT; /* Return success */
+}
+
+
+/*----------------------------------------------------------------------
+ FETCH an rfc822 message header and body and APPEND to destination folder
+
+ Args:
+
+
+ Result:
+
+ ----*/
+int
+save_fetch_append(MAILSTREAM *stream, long int raw, char *sect,
+ MAILSTREAM *save_stream, char *save_folder, CONTEXT_S *context,
+ long unsigned int size, char *flags, char *date, STORE_S *so)
+{
+ int rc, rv, old_imap_server = 0;
+ long j;
+ char *fetch;
+ unsigned long hlen, tlen, mlen;
+ STRING msg;
+
+ if((fetch = mail_fetch_header(stream, raw, sect, NULL, &hlen, FT_PEEK)) != NULL){
+ /*
+ * If there's no date string, then caller found the
+ * MESSAGECACHE for this message element didn't already have it.
+ * So, parse the "internal date" by hand since fetchstructure
+ * hasn't been called yet for this particular message, and
+ * we don't want to call it now just to get the date since
+ * the full header has what we want. Likewise, don't even
+ * think about calling mail_fetchfast either since it also
+ * wants to load mc->rfc822_size (which we could actually
+ * use but...), which under some drivers is *very*
+ * expensive to acquire (can you say NNTP?)...
+ */
+ if(!*date)
+ saved_date(date, fetch);
+ }
+ else
+ return(0); /* fetchtext writes error */
+
+ rc = MSG_EX_DELETE; /* "rc" overloaded */
+ if(msgno_exceptions(stream, raw, NULL, &rc, 0)){
+ char section[64];
+ int failure = 0;
+ BODY *body;
+ gf_io_t pc;
+
+ size = 0; /* all bets off, abort sanity test */
+ gf_set_so_writec(&pc, so);
+
+ if(sect && *sect){
+ snprintf(section, sizeof(section), "%s.1", sect);
+ if(!(body = mail_body(stream, raw, (unsigned char *) section)))
+ return(0);
+ }
+ else{
+ section[0] = '\0';
+ if(!pine_mail_fetch_structure(stream, raw, &body, 0))
+ return(0);
+ }
+
+ /*
+ * Walk the MIME structure looking for exceptional segments,
+ * writing them in the requested fashion.
+ *
+ * First, though, check for the easy case...
+ */
+ if(msgno_part_deleted(stream, raw, sect ? sect : "")){
+ tlen = 0;
+ failure = !save_ex_replace_body(fetch, &hlen, body, pc);
+ }
+ else{
+ /*
+ * Otherwise, roll up your sleeves and get to work...
+ * start by writing msg header and then the processed body
+ */
+ failure = !(so_nputs(so, fetch, (long) hlen)
+ && save_ex_output_body(stream, raw, section,
+ body, &tlen, pc));
+ }
+
+ gf_clear_so_writec(so);
+
+ if(failure)
+ return(0);
+
+ q_status_message(SM_ORDER, 3, 3,
+ _("NOTE: Deleted message parts NOT included in saved copy!"));
+
+ }
+ else{
+ /* First, write the header we just fetched... */
+ if(!so_nputs(so, fetch, (long) hlen))
+ return(0);
+
+ old_imap_server = is_imap_stream(stream) && !modern_imap_stream(stream);
+
+ /* Second, go fetch the corresponding text... */
+ fetch = pine_mail_fetch_text(stream, raw, sect, &tlen,
+ !old_imap_server ? FT_PEEK : 0);
+
+ /*
+ * Special handling in case we're fetching a Message/rfc822
+ * segment and we're talking to an old server...
+ */
+ if(fetch && *fetch == '\0' && sect && (hlen + tlen) != size){
+ so_seek(so, 0L, 0);
+ fetch = pine_mail_fetch_body(stream, raw, sect, &tlen, 0L);
+ }
+
+ /*
+ * Pre IMAP4 servers can't do a non-peeking mail_fetch_text,
+ * so if the message we are saving from was originally unseen,
+ * we have to change it back to unseen. Flags contains the
+ * string "SEEN" if the original message was seen.
+ */
+ if(old_imap_server && (!flags || !srchstr(flags,"SEEN"))){
+ char seq[20];
+
+ strncpy(seq, long2string(raw), sizeof(seq));
+ seq[sizeof(seq)-1] = '\0';
+ mail_flag(stream, seq, "\\SEEN", 0);
+ }
+
+ /* If fetch succeeded, write the result */
+ if(!(fetch && so_nputs(so, fetch, tlen)))
+ return(0);
+ }
+
+ so_seek(so, 0L, 0); /* rewind just in case */
+
+ /*
+ * Set up a c-client string driver so we can hand the
+ * collected text down to mail_append.
+ *
+ * NOTE: We only test the size if and only if we already
+ * have it. See, in some drivers, especially under
+ * dos, its too expensive to get the size (full
+ * header and body text fetch plus MIME parse), so
+ * we only verify the size if we already know it.
+ */
+ mlen = hlen + tlen;
+
+ if(size && mlen < size){
+ char buf[128];
+
+ if(sp_dead_stream(stream)){
+ snprintf(buf, sizeof(buf), _("Cannot save because current folder is Closed"));
+ q_status_message(SM_ORDER | SM_DING, 3, 4, buf);
+ dprint((1, "save_fetch_append: %s", buf));
+ return(0);
+ }
+ else{
+ if(pith_opt_save_size_changed_prompt
+ && (*pith_opt_save_size_changed_prompt)(mn_raw2m(sp_msgmap(stream), raw), 0) == 'y'){
+ snprintf(buf, sizeof(buf),
+ "Message to save shrank! (raw msg# %ld: %ld -> %ld): User says continue",
+ raw, size, mlen);
+ dprint((1, "save_fetch_append: %s", buf));
+ snprintf(buf, sizeof(buf),
+ "Message to save shrank: source msg # %ld may be saved incorrectly",
+ mn_raw2m(sp_msgmap(stream), raw));
+ q_status_message(SM_ORDER, 0, 3, buf);
+ }
+ else{
+ snprintf(buf, sizeof(buf),
+ "Message to save shrank: source raw msg # %ld went from announced size %ld to actual size %ld",
+ raw, size, mlen);
+ dprint((1, "save_fetch_append: %s", buf));
+ return(0);
+ }
+ }
+ }
+
+ INIT(&msg, mail_string, (void *)so_text(so), mlen);
+
+ rc = 0;
+ while(!(rv = (int) context_append_full(context, save_stream,
+ save_folder, flags,
+ *date ? date : NULL,
+ &msg))){
+ if(rc++ || !ps_global->try_to_create) /* abysmal failure! */
+ break; /* c-client returned error? */
+
+ if(context && (context->use & CNTXT_INCMNG)
+ && context_isambig(save_folder)){
+ q_status_message(SM_ORDER, 3, 5,
+ _("Can only save to existing folders in Incoming Collection"));
+ break;
+ }
+
+ ps_global->try_to_create = 0; /* reset for next time */
+ if((j = create_for_save(context, save_folder)) < 1){
+ if(j < 0)
+ rv = -1; /* user cancelled */
+
+ break;
+ }
+
+ SETPOS((&msg), 0L); /* reset string driver */
+ }
+
+ return(rv);
+}
+
+
+/*
+ * save_ex_replace_body -
+ *
+ * NOTE : hlen points to a cell that has the byte count of "hdr" on entry
+ * *BUT* which is to contain the count of written bytes on exit
+ */
+int
+save_ex_replace_body(char *hdr, long unsigned int *hlen, struct mail_bodystruct *body, gf_io_t pc)
+{
+ unsigned long len;
+
+ /*
+ * "X-" out the given MIME headers unless they're
+ * the same as we're going to substitute...
+ */
+ if(body->type == TYPETEXT
+ && (!body->subtype || !strucmp(body->subtype, "plain"))
+ && body->encoding == ENC7BIT){
+ if(!gf_nputs(hdr, *hlen, pc)) /* write out header */
+ return(0);
+ }
+ else{
+ int bol = 1;
+
+ /*
+ * write header, "X-"ing out transport headers bothersome to
+ * software but potentially useful to the human recipient...
+ */
+ for(len = *hlen; len; len--){
+ if(bol){
+ unsigned long n;
+
+ bol = 0;
+ if(save_ex_mask_types(hdr, &n, pc))
+ *hlen += n; /* add what we inserted */
+ else
+ break;
+ }
+
+ if(*hdr == '\015' && *(hdr+1) == '\012'){
+ bol++; /* remember beginning of line */
+ len--; /* account for LF */
+ if(gf_nputs(hdr, 2, pc))
+ hdr += 2;
+ else
+ break;
+ }
+ else if(!(*pc)(*hdr++))
+ break;
+ }
+
+ if(len) /* bytes remain! */
+ return(0);
+ }
+
+ /* Now, blat out explanatory text as the body... */
+ if(save_ex_explain_body(body, &len, pc)){
+ *hlen += len;
+ return(1);
+ }
+ else
+ return(0);
+}
+
+
+int
+save_ex_output_body(MAILSTREAM *stream, long int raw, char *section,
+ struct mail_bodystruct *body, long unsigned int *len, gf_io_t pc)
+{
+ char *txtp, newsect[128];
+ unsigned long ilen;
+
+ txtp = mail_fetch_mime(stream, raw, section, len, FT_PEEK);
+
+ if(msgno_part_deleted(stream, raw, section))
+ return(save_ex_replace_body(txtp, len, body, pc));
+
+ if(body->type == TYPEMULTIPART){
+ char *subsect, boundary[128];
+ int n, blen;
+ PART *part = body->nested.part;
+ PARAMETER *param;
+
+ /* Locate supplied multipart boundary */
+ for (param = body->parameter; param; param = param->next)
+ if (!strucmp(param->attribute, "boundary")){
+ snprintf(boundary, sizeof(boundary), "--%.*s\015\012", sizeof(boundary)-10,
+ param->value);
+ blen = strlen(boundary);
+ break;
+ }
+
+ if(!param){
+ q_status_message(SM_ORDER|SM_DING, 3, 3, "Missing MIME boundary");
+ return(0);
+ }
+
+ /*
+ * BUG: if multi/digest and a message deleted (which we'll
+ * change to text/plain), we need to coerce this composite
+ * type to multi/mixed !!
+ */
+ if(!gf_nputs(txtp, *len, pc)) /* write MIME header */
+ return(0);
+
+ /* Prepare to specify sub-sections */
+ strncpy(newsect, section, sizeof(newsect));
+ newsect[sizeof(newsect)-1] = '\0';
+ subsect = &newsect[n = strlen(newsect)];
+ if(n > 2 && !strcmp(&newsect[n-2], ".0"))
+ subsect--;
+ else if(n){
+ if((subsect-newsect) < sizeof(newsect))
+ *subsect++ = '.';
+ }
+
+ n = 1;
+ do { /* for each part */
+ strncpy(subsect, int2string(n++), sizeof(newsect)-(subsect-newsect));
+ newsect[sizeof(newsect)-1] = '\0';
+ if(gf_puts(boundary, pc)
+ && save_ex_output_body(stream, raw, newsect,
+ &part->body, &ilen, pc))
+ *len += blen + ilen;
+ else
+ return(0);
+ }
+ while ((part = part->next) != NULL); /* until done */
+
+ snprintf(boundary, sizeof(boundary), "--%.*s--\015\012", sizeof(boundary)-10,param->value);
+ *len += blen + 2;
+ return(gf_puts(boundary, pc));
+ }
+
+ /* Start by writing the part's MIME header */
+ if(!gf_nputs(txtp, *len, pc))
+ return(0);
+
+ if(body->type == TYPEMESSAGE
+ && (!body->subtype || !strucmp(body->subtype, "rfc822"))){
+ /* write RFC 822 message's header */
+ if((txtp = mail_fetch_header(stream,raw,section,NULL,&ilen,FT_PEEK))
+ && gf_nputs(txtp, ilen, pc))
+ *len += ilen;
+ else
+ return(0);
+
+ /* then go deal with its body parts */
+ snprintf(newsect, sizeof(newsect), "%.10s%s%s", section, section ? "." : "",
+ (body->nested.msg->body->type == TYPEMULTIPART) ? "0" : "1");
+ if(save_ex_output_body(stream, raw, newsect,
+ body->nested.msg->body, &ilen, pc)){
+ *len += ilen;
+ return(1);
+ }
+
+ return(0);
+ }
+
+ /* Write corresponding body part */
+ if((txtp = pine_mail_fetch_body(stream, raw, section, &ilen, FT_PEEK))
+ && gf_nputs(txtp, (long) ilen, pc) && gf_puts("\015\012", pc)){
+ *len += ilen + 2;
+ return(1);
+ }
+
+ return(0);
+}
+
+
+/*----------------------------------------------------------------------
+ Mask off any header entries we don't want to show up in exceptional saves
+
+Args: hdr -- pointer to start of a header line
+ pc -- function to write the prefix
+
+ ----*/
+int
+save_ex_mask_types(char *hdr, long unsigned int *len, gf_io_t pc)
+{
+ char *s = NULL;
+
+ if(!struncmp(hdr, "content-type:", 13))
+ s = "Content-Type: Text/Plain; charset=US-ASCII\015\012X-";
+ else if(!struncmp(hdr, "content-description:", 20))
+ s = "Content-Description: Deleted Attachment\015\012X-";
+ else if(!struncmp(hdr, "content-transfer-encoding:", 26)
+ || !struncmp(hdr, "content-disposition:", 20))
+ s = "X-";
+
+ return((*len = s ? strlen(s) : 0) ? gf_puts(s, pc) : 1);
+}
+
+
+int
+save_ex_explain_body(struct mail_bodystruct *body, long unsigned int *len, gf_io_t pc)
+{
+ unsigned long ilen;
+ char **blurbp;
+ static char *blurb[] = {
+ N_("The following attachment was DELETED when this message was saved:"),
+ NULL
+ };
+
+ *len = 0;
+ for(blurbp = blurb; *blurbp; blurbp++)
+ if(save_ex_output_line(_(*blurbp), &ilen, pc))
+ *len += ilen;
+ else
+ return(0);
+
+ if(!save_ex_explain_parts(body, 0, &ilen, pc))
+ return(0);
+
+ *len += ilen;
+ return(1);
+}
+
+
+int
+save_ex_explain_parts(struct mail_bodystruct *body, int depth, long unsigned int *len, gf_io_t pc)
+{
+ char tmp[MAILTMPLEN], buftmp[MAILTMPLEN];
+ unsigned long ilen;
+ char *name = parameter_val(body->parameter, "name");
+
+ if(body->type == TYPEMULTIPART) { /* multipart gets special handling */
+ PART *part = body->nested.part; /* first body part */
+
+ *len = 0;
+ if(body->description && *body->description){
+ snprintf(tmp, sizeof(tmp), "%*.*sA %s/%.*s%.10s%.100s%.10s segment described",
+ depth, depth, " ", body_type_names(body->type),
+ sizeof(tmp)-300, body->subtype ? body->subtype : "Unknown",
+ name ? " (Name=\"" : "",
+ name ? name : "",
+ name ? "\")" : "");
+ if(!save_ex_output_line(tmp, len, pc))
+ return(0);
+
+ snprintf(buftmp, sizeof(buftmp), "%.75s", body->description);
+ snprintf(tmp, sizeof(tmp), "%*.*s as \"%.50s\" containing:", depth, depth, " ",
+ (char *) rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, buftmp));
+ }
+ else{
+ snprintf(tmp, sizeof(tmp), "%*.*sA %s/%.*s%.10s%.100s%.10s segment containing:",
+ depth, depth, " ",
+ body_type_names(body->type),
+ sizeof(tmp)-300, body->subtype ? body->subtype : "Unknown",
+ name ? " (Name=\"" : "",
+ name ? name : "",
+ name ? "\")" : "");
+ }
+
+ if(save_ex_output_line(tmp, &ilen, pc))
+ *len += ilen;
+ else
+ return(0);
+
+ depth++;
+ do /* for each part */
+ if(save_ex_explain_parts(&part->body, depth, &ilen, pc))
+ *len += ilen;
+ else
+ return(0);
+ while ((part = part->next) != NULL); /* until done */
+ }
+ else{
+ snprintf(tmp, sizeof(tmp), "%*.*sA %s/%.*s%.10s%.100s%.10s segment of about %s bytes%s",
+ depth, depth, " ",
+ body_type_names(body->type),
+ sizeof(tmp)-300, body->subtype ? body->subtype : "Unknown",
+ name ? " (Name=\"" : "",
+ name ? name : "",
+ name ? "\")" : "",
+ comatose((body->encoding == ENCBASE64)
+ ? ((body->size.bytes * 3)/4)
+ : body->size.bytes),
+ body->description ? "," : ".");
+ if(!save_ex_output_line(tmp, len, pc))
+ return(0);
+
+ if(body->description && *body->description){
+ snprintf(buftmp, sizeof(buftmp), "%.75s", body->description);
+ snprintf(tmp, sizeof(tmp), "%*.*s described as \"%.*s\"", depth, depth, " ",
+ sizeof(tmp)-100,
+ (char *) rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, buftmp));
+ if(save_ex_output_line(tmp, &ilen, pc))
+ *len += ilen;
+ else
+ return(0);
+ }
+ }
+
+ return(1);
+}
+
+
+int
+save_ex_output_line(char *line, long unsigned int *len, gf_io_t pc)
+{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, " [ %-*.*s ]\015\012", 68, 68, line);
+ *len = strlen(tmp_20k_buf);
+ return(gf_puts(tmp_20k_buf, pc));
+}
+
+
+/*----------------------------------------------------------------------
+ Save() helper function to create canonical date string from given header
+
+ Args: date -- buf to recieve canonical date string
+ header -- rfc822 header to fish date string from
+
+ Result: date filled with canonicalized date in header, or null string
+ ----*/
+void
+saved_date(char *date, char *header)
+{
+ char *d, *p, c;
+ MESSAGECACHE elt;
+
+ *date = '\0';
+ if((toupper((unsigned char)(*(d = header)))
+ == 'D' && !strncmp(d, "date:", 5))
+ || (d = srchstr(header, "\015\012date:"))){
+ for(d += 7; *d == ' '; d++)
+ ; /* skip white space */
+
+ if((p = strstr(d, "\015\012")) != NULL){
+ for(; p > d && *p == ' '; p--)
+ ; /* skip white space */
+
+ c = *p;
+ *p = '\0'; /* tie off internal date */
+ }
+
+ if(mail_parse_date(&elt, (unsigned char *) d)) /* normalize it */
+ mail_date(date, &elt);
+
+ if(p) /* restore header */
+ *p = c;
+ }
+}
+
+
+MAILSTREAM *
+save_msg_stream(CONTEXT_S *context, char *folder, int *we_opened)
+{
+ char tmp[MAILTMPLEN];
+ MAILSTREAM *save_stream = NULL;
+
+ if(IS_REMOTE(context_apply(tmp, context, folder, sizeof(tmp)))
+ && !(save_stream = sp_stream_get(tmp, SP_MATCH))
+ && !(save_stream = sp_stream_get(tmp, SP_SAME))){
+ if((save_stream = context_open(context, NULL, folder,
+ OP_HALFOPEN | SP_USEPOOL | SP_TEMPUSE,
+ NULL)) != NULL)
+ *we_opened = 1;
+ }
+
+ return(save_stream);
+}
+
+
+/*----------------------------------------------------------------------
+ Offer to create a non-existant folder to copy message[s] into
+
+ Args: context -- context to create folder in
+ folder -- name of folder to create
+
+ Result: 0 if create failed (c-client writes error)
+ 1 if create successful
+ -1 if user declines to create folder
+ ----*/
+int
+create_for_save(CONTEXT_S *context, char *folder)
+{
+ int ret;
+
+ if(pith_opt_save_create_prompt
+ && (ret = (*pith_opt_save_create_prompt)(context, folder, 1)) != 1)
+ return(ret);
+
+ return(context_create(context, NULL, folder));
+}
diff --git a/pith/save.h b/pith/save.h
new file mode 100644
index 00000000..6a6f1873
--- /dev/null
+++ b/pith/save.h
@@ -0,0 +1,54 @@
+/*
+ * $Id: save.h 942 2008-03-04 18:21:33Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_SAVE_INCLUDED
+#define PITH_SAVE_INCLUDED
+
+
+#include "../pith/savetype.h"
+#include "../pith/context.h"
+#include "../pith/msgno.h"
+#include "../pith/state.h"
+#include "../pith/store.h"
+
+
+#define SV_DELETE 0x1 /* delete source msg after save */
+#define SV_FOR_FILT 0x2 /* save called from filtering routine, */
+ /* just different status messages */
+#define SV_FIX_DELS 0x4 /* remove Del mark before saving */
+#define SV_INBOXWOCNTXT 0x8 /* interpret "inbox" as one true inbox */
+#define SV_PRESERVE 0x10 /* preserve order */
+
+
+#define SSCP_NONE 0x0
+#define SSCP_INIT 0x1
+#define SSCP_END 0x2
+#define SSCP_ANSWER_IS_YES 0x4
+
+
+/* exported protoypes */
+char *save_get_default(struct pine *, ENVELOPE *, long, char *, CONTEXT_S **);
+void save_get_fldr_from_env(char *, int, ENVELOPE *, struct pine *, long, char *);
+long save(struct pine *, MAILSTREAM *, CONTEXT_S *, char *, MSGNO_S *, int);
+long save_fetch_append_cb(MAILSTREAM *, void *, char **, char **, STRING **);
+int save_fetch_append(MAILSTREAM *, long, char *, MAILSTREAM *, char *, CONTEXT_S *,
+ unsigned long, char *, char *, STORE_S *);
+void saved_date(char *, char *);
+MAILSTREAM *save_msg_stream(CONTEXT_S *, char *, int *);
+int create_for_save(CONTEXT_S *, char *);
+
+
+#endif /* PITH_SAVE_INCLUDED */
diff --git a/pith/savetype.h b/pith/savetype.h
new file mode 100644
index 00000000..5e7cb74c
--- /dev/null
+++ b/pith/savetype.h
@@ -0,0 +1,38 @@
+/*
+ * $Id: savetype.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_SAVETYPE_INCLUDED
+#define PITH_SAVETYPE_INCLUDED
+
+
+#include "../pith/msgno.h"
+#include "../pith/store.h"
+
+
+typedef struct append_package {
+ MAILSTREAM *stream;
+ char *flags;
+ char *date;
+ STRING *msg;
+ MSGNO_S *msgmap;
+ long msgno;
+ STORE_S *so;
+} APPENDPACKAGE;
+
+
+/* exported protoypes */
+
+
+#endif /* PITH_SAVETYPE_INCLUDED */
diff --git a/pith/search.c b/pith/search.c
new file mode 100644
index 00000000..23e2f353
--- /dev/null
+++ b/pith/search.c
@@ -0,0 +1,70 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: search.c 854 2007-12-07 17:44:43Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/search.h"
+
+
+SEARCHSET *
+build_searchset(MAILSTREAM *stream)
+{
+ long i, run;
+ SEARCHSET *ret_s = NULL, **set;
+ MESSAGECACHE *mc;
+
+ if(!stream)
+ return(NULL);
+
+ for(i = 1L, set = &ret_s, run = 0L; i <= stream->nmsgs; i++){
+ if(!((mc = mail_elt(stream, i)) && mc->sequence)){ /* end of run */
+ if(run){ /* run in progress */
+ set = &(*set)->next;
+ run = 0L;
+ }
+ }
+ else if(run++){ /* next in run */
+ (*set)->last = i;
+ }
+ else{ /* start of new run */
+ *set = mail_newsearchset();
+ /*
+ * Leave off (*set)->last until we get more than one msg
+ * in the run, to avoid 607:607 in SEARCH.
+ */
+ (*set)->first = (*set)->last = i;
+ }
+ }
+
+ return(ret_s);
+}
+
+
+int
+in_searchset(SEARCHSET *srch, long unsigned int num)
+{
+ SEARCHSET *s;
+ unsigned long i;
+
+ if(srch)
+ for(s = srch; s; s = s->next)
+ for(i = s->first; i <= s->last; i++){
+ if(i == num)
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/pith/search.h b/pith/search.h
new file mode 100644
index 00000000..d1da3897
--- /dev/null
+++ b/pith/search.h
@@ -0,0 +1,25 @@
+/*
+ * $Id: search.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_SEARCH_INCLUDED
+#define PITH_SEARCH_INCLUDED
+
+
+/* exported protoypes */
+SEARCHSET *build_searchset(MAILSTREAM *);
+int in_searchset(SEARCHSET *, unsigned long);
+
+
+#endif /* PITH_SEARCH_INCLUDED */
diff --git a/pith/send.c b/pith/send.c
new file mode 100644
index 00000000..a0c60439
--- /dev/null
+++ b/pith/send.c
@@ -0,0 +1,5901 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: send.c 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/send.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/store.h"
+#include "../pith/mimedesc.h"
+#include "../pith/context.h"
+#include "../pith/status.h"
+#include "../pith/folder.h"
+#include "../pith/bldaddr.h"
+#include "../pith/pipe.h"
+#include "../pith/mailview.h"
+#include "../pith/mailindx.h"
+#include "../pith/list.h"
+#include "../pith/filter.h"
+#include "../pith/reply.h"
+#include "../pith/addrstring.h"
+#include "../pith/rfc2231.h"
+#include "../pith/stream.h"
+#include "../pith/util.h"
+#include "../pith/adrbklib.h"
+#include "../pith/options.h"
+#include "../pith/busy.h"
+#include "../pith/text.h"
+#include "../pith/imap.h"
+#include "../pith/ablookup.h"
+#include "../pith/sort.h"
+#include "../pith/smime.h"
+
+#include "../c-client/smtp.h"
+#include "../c-client/nntp.h"
+
+
+/* this is used in pine_send and pine_simple_send */
+/* name::type::canedit::writehdr::localcopy::rcptto */
+PINEFIELD pf_template[] = {
+ {"X-Auth-Received", FreeText, 0, 1, 1, 0}, /* N_AUTHRCVD */
+ {"From", Address, 0, 1, 1, 0},
+ {"Reply-To", Address, 0, 1, 1, 0},
+ {TONAME, Address, 1, 1, 1, 1},
+ {CCNAME, Address, 1, 1, 1, 1},
+ {"bcc", Address, 1, 0, 1, 1},
+ {"Newsgroups", FreeText, 1, 1, 1, 0},
+ {"Fcc", Fcc, 1, 0, 0, 0},
+ {"Lcc", Address, 1, 0, 1, 1},
+ {"Attchmnt", Attachment, 1, 1, 1, 0},
+ {SUBJNAME, Subject, 1, 1, 1, 0},
+ {"References", FreeText, 0, 1, 1, 0},
+ {"Date", FreeText, 0, 1, 1, 0},
+ {"In-Reply-To", FreeText, 0, 1, 1, 0},
+ {"Message-ID", FreeText, 0, 1, 1, 0},
+ {PRIORITYNAME, FreeText, 0, 1, 1, 0},
+ {"User-Agent", FreeText, 0, 1, 1, 0},
+ {"To", Address, 0, 0, 0, 0}, /* N_NOBODY */
+ {"X-Post-Error",FreeText, 0, 0, 0, 0}, /* N_POSTERR */
+ {"X-Reply-UID", FreeText, 0, 0, 0, 0}, /* N_RPLUID */
+ {"X-Reply-Mbox",FreeText, 0, 0, 0, 0}, /* N_RPLMBOX */
+ {"X-SMTP-Server",FreeText, 0, 0, 0, 0}, /* N_SMTP */
+ {"X-NNTP-Server",FreeText, 0, 0, 0, 0}, /* N_NNTP */
+ {"X-Cursor-Pos",FreeText, 0, 0, 0, 0}, /* N_CURPOS */
+ {"X-Our-ReplyTo",FreeText, 0, 0, 0, 0}, /* N_OURREPLYTO */
+ {OUR_HDRS_LIST, FreeText, 0, 0, 0, 0}, /* N_OURHDRS */
+#if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
+ {"X-X-Sender", Address, 0, 1, 1, 0},
+#endif
+ {NULL, FreeText}
+};
+
+
+PRIORITY_S priorities[] = {
+ {1, "Highest"},
+ {2, "High"},
+ {3, "Normal"},
+ {4, "Low"},
+ {5, "Lowest"},
+ {0, NULL}
+};
+
+
+#define ctrl(c) ((c) & 0x1f)
+
+/* which message part to test for xliteration */
+typedef enum {MsgBody, HdrText} MsgPart;
+
+
+/*
+ * Internal prototypes
+ */
+long post_rfc822_output(char *, ENVELOPE *, BODY *, soutr_t, TCPSTREAM *, long);
+int l_flush_net(int);
+int l_putc(int);
+int pine_write_header_line(char *, char *, STORE_S *);
+int pine_write_params(PARAMETER *, STORE_S *);
+char *tidy_smtp_mess(char *, char *, char *, size_t);
+int lmc_body_header_line(char *, int);
+int lmc_body_header_finish(void);
+int pwbh_finish(int, STORE_S *);
+int sent_percent(void);
+unsigned short *setup_avoid_table(void);
+#ifndef _WINDOWS
+int mta_handoff(METAENV *, BODY *, char *, size_t, void (*)(char *, int),
+ void (*)(PIPE_S *, int, void *));
+char *post_handoff(METAENV *, BODY *, char *, size_t, void (*)(char *, int),
+ void (*)(PIPE_S *, int, void *));
+char *mta_parse_post(METAENV *, BODY *, char *, char *, size_t, void (*)(char *, int),
+ void (*)(PIPE_S *, int, void *));
+long pine_pipe_soutr_nl(void *, char *);
+#endif
+char *smtp_command(char *, size_t);
+void *piped_smtp_open(char *, char *, unsigned long);
+void *piped_aopen(NETMBX *, char *, char *);
+long piped_soutr(void *, char *);
+long piped_sout(void *, char *, unsigned long);
+char *piped_getline(void *);
+void piped_close(void *);
+void piped_abort(void *);
+char *piped_host(void *);
+unsigned long piped_port(void *);
+char *posting_characterset(void *, char *, MsgPart);
+int body_is_translatable(void *, char *);
+int text_is_translatable(void *, char *);
+int dummy_putc(int);
+unsigned long *init_charsetchecker(char *);
+int representable_in_charset(unsigned long, char *);
+char *most_preferred_charset(unsigned long);
+
+/*
+ * Storage object where the FCC (or postponed msg) is to be written.
+ * This is amazingly bogus. Much work was done to put messages
+ * together and encode them as they went to the tmp file for sendmail
+ * or into the SMTP slot (especially for for DOS, to prevent a temporary
+ * file (and needlessly copying the message).
+ *
+ * HOWEVER, since there's no piping into c-client routines
+ * (particularly mail_append() which copies the fcc), the fcc will have
+ * to be copied to disk. This global tells pine's copy of the rfc822
+ * output functions where to also write the message bytes for the fcc.
+ * With piping in the c-client we could just have two pipes to shove
+ * down rather than messing with damn copies. FIX THIS!
+ *
+ * The function open_fcc, locates the actual folder and creates it if
+ * requested before any mailing or posting is done.
+ */
+struct local_message_copy lmc;
+
+
+/*
+ * Locally global pointer to stream used for sending/posting.
+ * It's also used to indicate when/if we write the Bcc: field in
+ * the header.
+ */
+static SENDSTREAM *sending_stream = NULL;
+
+
+static struct hooks {
+ void *rfc822_out; /* Message outputter */
+} sending_hooks;
+
+
+static FILE *verbose_send_output = NULL;
+static long send_bytes_sent, send_bytes_to_send;
+static METAENV *send_header = NULL;
+
+/*
+ * Hooks for prompts and confirmations
+ */
+int (*pith_opt_daemon_confirm)(void);
+
+
+static NETDRIVER piped_io = {
+ piped_smtp_open, /* open a connection */
+ piped_aopen, /* open an authenticated connection */
+ piped_getline, /* get a line */
+ NULL, /* get a buffer */
+ piped_soutr, /* output pushed data */
+ piped_sout, /* output string */
+ piped_close, /* close connection */
+ piped_host, /* return host name */
+ piped_host, /* remotehost */
+ piped_port, /* return port number */
+ piped_host /* return local host (NOTE: same as host!) */
+};
+
+
+/*
+ * Since c-client preallocates, it's necessary here to define a limit
+ * such that we don't blow up in c-client (see rfc822_address_line()).
+ */
+#define MAX_SINGLE_ADDR MAILTMPLEN
+
+#define AVOID_2022_JP_FOR_PUNC "AVOID_2022_JP_FOR_PUNC"
+
+
+/*
+ * Phone home hash controls
+ */
+#define PH_HASHBITS 24
+#define PH_MAXHASH (1<<(PH_HASHBITS))
+
+
+/*
+ * postponed_stream - return stream associated with postponed messages
+ * in argument.
+ */
+int
+postponed_stream(MAILSTREAM **streamp, char *mbox, char *type, int checknmsgs)
+{
+ MAILSTREAM *stream = NULL;
+ CONTEXT_S *p_cntxt = NULL;
+ char *p, *q, tmp[MAILTMPLEN], *fullname = NULL;
+ int exists;
+
+ if(!(streamp && mbox))
+ return(0);
+
+ *streamp = NULL;
+
+ /*
+ * find default context to look for folder...
+ *
+ * The "mbox" is assumed to be local if we're given what looks
+ * like an absolute path. This is different from Goto/Save
+ * where we do alot of work to interpret paths relative to the
+ * server. This reason is to support all the pre-4.00 pinerc'
+ * that specified a path and because there's yet to be a way
+ * in c-client to specify otherwise in the face of a remote
+ * context.
+ */
+ if(!is_absolute_path(mbox)
+ && !(p_cntxt = default_save_context(ps_global->context_list)))
+ p_cntxt = ps_global->context_list;
+
+ /* check to see if the folder exists, the user wants to continue
+ * and that we can actually read something in...
+ */
+ exists = folder_name_exists(p_cntxt, mbox, &fullname);
+ if(fullname)
+ mbox = fullname;
+
+ if(exists & FEX_ISFILE){
+ context_apply(tmp, p_cntxt, mbox, sizeof(tmp));
+ if(!(IS_REMOTE(tmp) || is_absolute_path(tmp))){
+ /*
+ * The mbox is relative to the home directory.
+ * Make it absolute so we can compare it to
+ * stream->mailbox.
+ */
+ build_path(tmp_20k_buf, ps_global->ui.homedir, tmp,
+ SIZEOF_20KBUF);
+ strncpy(tmp, tmp_20k_buf, sizeof(tmp));
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+
+ if((stream = ps_global->mail_stream)
+ && !(stream->mailbox
+ && ((*tmp != '{' && !strcmp(tmp, stream->mailbox))
+ || (*tmp == '{'
+ && same_stream(tmp, stream)
+ && (p = strchr(tmp, '}'))
+ && (q = strchr(stream->mailbox,'}'))
+ && !strcmp(p + 1, q + 1)))))
+ stream = NULL;
+
+ if(!stream){
+ stream = context_open(p_cntxt, NULL, mbox,
+ SP_USEPOOL|SP_TEMPUSE, NULL);
+ if(stream && !stream->halfopen){
+ if(stream->nmsgs > 0)
+ refresh_sort(stream, sp_msgmap(stream), SRT_NON);
+
+ if(checknmsgs && stream->nmsgs < 1){
+ pine_mail_close(stream);
+ exists = 0;
+ stream = NULL;
+ }
+ }
+ else{
+ q_status_message2(SM_ORDER | SM_DING, 3, 3,
+ _("Can't open %s mailbox: %s"), type, mbox);
+ if(stream)
+ pine_mail_close(stream);
+
+ exists = 0;
+ stream = NULL;
+ }
+ }
+ }
+ else{
+ if(F_ON(F_ALT_COMPOSE_MENU, ps_global)){
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("%s message folder doesn't exist!"), type);
+ }
+ }
+
+ if(fullname)
+ fs_give((void **) &fullname);
+
+ *streamp = stream;
+
+ return(exists);
+}
+
+
+int
+redraft_work(MAILSTREAM **streamp, long int cont_msg, ENVELOPE **outgoing,
+ struct mail_bodystruct **body, char **fcc, char **lcc,
+ REPLY_S **reply, REDRAFT_POS_S **redraft_pos, PINEFIELD **custom,
+ ACTION_S **role, int flags, STORE_S *so)
+{
+ MAILSTREAM *stream;
+ ENVELOPE *e = NULL;
+ BODY *b;
+ PART *part;
+ PINEFIELD *pf;
+ gf_io_t pc;
+ char *extras, **fields, **values, *p;
+ char *hdrs[2], *h, *charset;
+ char **smtp_servers = NULL, **nntp_servers = NULL;
+ int i, pine_generated = 0, our_replyto = 0;
+ int added_to_role = 0;
+ unsigned gbpt_flags = GBPT_NONE;
+ MESSAGECACHE *mc;
+
+ if(!(streamp && *streamp))
+ return(redraft_cleanup(streamp, TRUE, flags));
+
+ stream = *streamp;
+
+ if(flags & REDRAFT_HTML)
+ gbpt_flags |= GBPT_HTML_OK;
+
+ /* grok any user-defined or non-c-client headers */
+ if((e = pine_mail_fetchstructure(stream, cont_msg, &b)) != NULL){
+
+ /*
+ * The custom headers to look for in the suspended message should
+ * have been stored in the X-Our-Headers header. So first we get
+ * that list. If we can't find it (version that stored the
+ * message < 4.30) then we use the global custom list.
+ */
+ hdrs[0] = OUR_HDRS_LIST;
+ hdrs[1] = NULL;
+ if((h = pine_fetchheader_lines(stream, cont_msg, NULL, hdrs)) != NULL){
+ int commas = 0;
+ char **list;
+ char *hdrval = NULL;
+
+ if((hdrval = strindex(h, ':')) != NULL){
+ for(hdrval++; *hdrval && isspace((unsigned char)*hdrval);
+ hdrval++)
+ ;
+ }
+
+ /* count elements in list */
+ for(p = hdrval; p && *p; p++)
+ if(*p == ',')
+ commas++;
+
+ if(hdrval && (list = parse_list(hdrval,commas+1,0,NULL)) != NULL){
+
+ *custom = parse_custom_hdrs(list, Replace);
+ add_defaults_from_list(*custom,
+ ps_global->VAR_CUSTOM_HDRS);
+ free_list_array(&list);
+ }
+
+ if(*custom && !(*custom)->name){
+ free_customs(*custom);
+ *custom = NULL;
+ }
+
+ fs_give((void **)&h);
+ }
+
+ if(!*custom)
+ *custom = parse_custom_hdrs(ps_global->VAR_CUSTOM_HDRS, UseAsDef);
+
+#define INDEX_FCC 0
+#define INDEX_POSTERR 1
+#define INDEX_REPLYUID 2
+#define INDEX_REPLYMBOX 3
+#define INDEX_SMTP 4
+#define INDEX_NNTP 5
+#define INDEX_CURSORPOS 6
+#define INDEX_OUR_REPLYTO 7
+#define INDEX_LCC 8 /* MUST REMAIN LAST FIELD DECLARED */
+#define FIELD_COUNT 9
+
+ i = count_custom_hdrs_pf(*custom,1) + FIELD_COUNT + 1;
+
+ /*
+ * Having these two fields separated isn't the slickest, but
+ * converting the pointer array for fetchheader_lines() to
+ * a list of structures or some such for simple_header_parse()
+ * is too goonie. We could do something like re-use c-client's
+ * PARAMETER struct which is a simple char * pairing, but that
+ * doesn't make sense to pass to fetchheader_lines()...
+ */
+ fields = (char **) fs_get((size_t) i * sizeof(char *));
+ values = (char **) fs_get((size_t) i * sizeof(char *));
+ memset(fields, 0, (size_t) i * sizeof(char *));
+ memset(values, 0, (size_t) i * sizeof(char *));
+
+ fields[i = INDEX_FCC] = "Fcc"; /* Fcc: special case */
+ fields[++i] = "X-Post-Error"; /* posting errors too */
+ fields[++i] = "X-Reply-UID"; /* Reply'd to msg's UID */
+ fields[++i] = "X-Reply-Mbox"; /* Reply'd to msg's Mailbox */
+ fields[++i] = "X-SMTP-Server";/* SMTP server to use */
+ fields[++i] = "X-NNTP-Server";/* NNTP server to use */
+ fields[++i] = "X-Cursor-Pos"; /* Cursor position */
+ fields[++i] = "X-Our-ReplyTo"; /* ReplyTo is real */
+ fields[++i] = "Lcc"; /* Lcc: too... */
+ if(++i != FIELD_COUNT)
+ panic("Fix FIELD_COUNT");
+
+ for(pf = *custom; pf && pf->name; pf = pf->next)
+ if(!pf->standard)
+ fields[i++] = pf->name; /* assign custom fields */
+
+ if((extras = pine_fetchheader_lines(stream, cont_msg, NULL,fields)) != NULL){
+ simple_header_parse(extras, fields, values);
+ fs_give((void **) &extras);
+
+ /*
+ * translate RFC 1522 strings,
+ * starting with "Lcc" field
+ */
+ for(i = INDEX_LCC; fields[i]; i++)
+ if(values[i]){
+ size_t len;
+ char *bufp, *biggerbuf = NULL;
+
+ if((len=4*strlen(values[i])) > SIZEOF_20KBUF-1){
+ len++;
+ biggerbuf = (char *)fs_get(len * sizeof(char));
+ bufp = biggerbuf;
+ }
+ else{
+ bufp = tmp_20k_buf;
+ len = SIZEOF_20KBUF;
+ }
+
+ p = (char *)rfc1522_decode_to_utf8((unsigned char*)bufp, len, values[i]);
+
+ if(p == tmp_20k_buf){
+ fs_give((void **)&values[i]);
+ values[i] = cpystr(p);
+ }
+
+ if(biggerbuf)
+ fs_give((void **)&biggerbuf);
+ }
+
+ for(pf = *custom, i = FIELD_COUNT;
+ pf && pf->name;
+ pf = pf->next){
+ if(pf->standard){
+ /*
+ * Because the value is already in the envelope.
+ */
+ pf->cstmtype = NoMatch;
+ continue;
+ }
+
+ if(values[i]){ /* use this instead of default */
+ if(pf->textbuf)
+ fs_give((void **)&pf->textbuf);
+
+ pf->textbuf = values[i]; /* freed in pine_send! */
+ }
+ else if(pf->textbuf) /* was erased before postpone */
+ fs_give((void **)&pf->textbuf);
+
+ i++;
+ }
+
+ if(values[INDEX_FCC]) /* If "Fcc:" was there... */
+ pine_generated = 1; /* we put it there? */
+
+ /*
+ * Since c-client fills in the reply_to field in the envelope
+ * even if there isn't a Reply-To header in the message we
+ * have to work around that. When we postpone we add
+ * a second header that has value "Empty" if there really
+ * was a Reply-To and it was empty. It has the
+ * value "Full" if we put the Reply-To contents there
+ * intentionally (and it isn't empty).
+ */
+ if(values[INDEX_OUR_REPLYTO]){
+ if(values[INDEX_OUR_REPLYTO][0] == 'E')
+ our_replyto = 'E'; /* we put an empty one there */
+ else if(values[INDEX_OUR_REPLYTO][0] == 'F')
+ our_replyto = 'F'; /* we put it there */
+
+ fs_give((void **) &values[INDEX_OUR_REPLYTO]);
+ }
+
+ if(fcc) /* fcc: special case... */
+ *fcc = values[INDEX_FCC] ? values[INDEX_FCC] : cpystr("");
+ else if(values[INDEX_FCC])
+ fs_give((void **) &values[INDEX_FCC]);
+
+ if(values[INDEX_POSTERR]){ /* x-post-error?!?1 */
+ q_status_message(SM_ORDER|SM_DING, 4, 4,
+ values[INDEX_POSTERR]);
+ fs_give((void **) &values[INDEX_POSTERR]);
+ }
+
+ if(values[INDEX_REPLYUID]){
+ if(reply)
+ *reply = build_reply_uid(values[INDEX_REPLYUID]);
+
+ fs_give((void **) &values[INDEX_REPLYUID]);
+
+ if(values[INDEX_REPLYMBOX] && reply && *reply)
+ (*reply)->origmbox = cpystr(values[INDEX_REPLYMBOX]);
+
+ if(reply && *reply && !(*reply)->origmbox && (*reply)->mailbox)
+ (*reply)->origmbox = cpystr((*reply)->mailbox);
+ }
+
+ if(values[INDEX_REPLYMBOX])
+ fs_give((void **) &values[INDEX_REPLYMBOX]);
+
+ if(values[INDEX_SMTP]){
+ char *q;
+ size_t cnt = 0;
+
+ /*
+ * Turn the space delimited list of smtp servers into
+ * a char ** list.
+ */
+ p = values[INDEX_SMTP];
+ do{
+ if(!*p || isspace((unsigned char) *p))
+ cnt++;
+ } while(*p++);
+
+ smtp_servers = (char **) fs_get((cnt+1) * sizeof(char *));
+ memset(smtp_servers, 0, (cnt+1) * sizeof(char *));
+
+ cnt = 0;
+ q = p = values[INDEX_SMTP];
+ do{
+ if(!*p || isspace((unsigned char) *p)){
+ if(*p){
+ *p = '\0';
+ smtp_servers[cnt++] = cpystr(q);
+ *p = ' ';
+ q = p+1;
+ }
+ else
+ smtp_servers[cnt++] = cpystr(q);
+ }
+ } while(*p++);
+
+ fs_give((void **) &values[INDEX_SMTP]);
+ }
+
+ if(values[INDEX_NNTP]){
+ char *q;
+ size_t cnt = 0;
+
+ /*
+ * Turn the space delimited list of smtp nntp into
+ * a char ** list.
+ */
+ p = values[INDEX_NNTP];
+ do{
+ if(!*p || isspace((unsigned char) *p))
+ cnt++;
+ } while(*p++);
+
+ nntp_servers = (char **) fs_get((cnt+1) * sizeof(char *));
+ memset(nntp_servers, 0, (cnt+1) * sizeof(char *));
+
+ cnt = 0;
+ q = p = values[INDEX_NNTP];
+ do{
+ if(!*p || isspace((unsigned char) *p)){
+ if(*p){
+ *p = '\0';
+ nntp_servers[cnt++] = cpystr(q);
+ *p = ' ';
+ q = p+1;
+ }
+ else
+ nntp_servers[cnt++] = cpystr(q);
+ }
+ } while(*p++);
+
+ fs_give((void **) &values[INDEX_NNTP]);
+ }
+
+ if(values[INDEX_CURSORPOS]){
+ /*
+ * The redraft cursor position is written as two fields
+ * separated by a space. First comes the name of the
+ * header field we're in, or just a ":" if we're in the
+ * body. Then comes the offset into that header or into
+ * the body.
+ */
+ if(redraft_pos){
+ char *q1, *q2;
+
+ *redraft_pos
+ = (REDRAFT_POS_S *)fs_get(sizeof(REDRAFT_POS_S));
+ (*redraft_pos)->offset = 0L;
+
+ q1 = skip_white_space(values[INDEX_CURSORPOS]);
+ if(*q1 && (q2 = strindex(q1, SPACE))){
+ *q2 = '\0';
+ (*redraft_pos)->hdrname = cpystr(q1);
+ q1 = skip_white_space(q2+1);
+ if(*q1)
+ (*redraft_pos)->offset = atol(q1);
+ }
+ else
+ (*redraft_pos)->hdrname = cpystr(":");
+ }
+
+ fs_give((void **) &values[INDEX_CURSORPOS]);
+ }
+
+ if(lcc)
+ *lcc = values[INDEX_LCC];
+ else
+ fs_give((void **) &values[INDEX_LCC]);
+ }
+
+ fs_give((void **)&fields);
+ fs_give((void **)&values);
+
+ *outgoing = copy_envelope(e);
+
+ /*
+ * If the postponed message has a From which is different from
+ * the default, it is either because allow-changing-from is on
+ * or because there was a role with a from that allowed it to happen.
+ * If allow-changing-from is not on, put this back in a role
+ * so that it will be allowed again in pine_send.
+ */
+ if(role && *role == NULL &&
+ !ps_global->never_allow_changing_from &&
+ *outgoing){
+ /*
+ * Now check to see if the from is different from default from.
+ */
+ ADDRESS *deffrom;
+
+ deffrom = generate_from();
+ if(!((*outgoing)->from &&
+ address_is_same(deffrom, (*outgoing)->from) &&
+ ((!(deffrom->personal && deffrom->personal[0]) &&
+ !((*outgoing)->from->personal &&
+ (*outgoing)->from->personal[0])) ||
+ (deffrom->personal && (*outgoing)->from->personal &&
+ !strcmp(deffrom->personal, (*outgoing)->from->personal))))){
+
+ *role = (ACTION_S *)fs_get(sizeof(**role));
+ memset((void *)*role, 0, sizeof(**role));
+ if(!(*outgoing)->from)
+ (*outgoing)->from = mail_newaddr();
+
+ (*role)->from = (*outgoing)->from;
+ (*outgoing)->from = NULL;
+ added_to_role++;
+ }
+
+ mail_free_address(&deffrom);
+ }
+
+ /*
+ * Look at each empty address and see if the user has specified
+ * a default for that field or not. If they have, that means
+ * they have erased it before postponing, so they won't want
+ * the default to come back. If they haven't specified a default,
+ * then the default should be generated in pine_send. We prevent
+ * the default from being assigned by assigning an empty address
+ * to the variable here.
+ *
+ * BUG: We should do this for custom Address headers, too, but
+ * there isn't such a thing yet.
+ */
+ if(!(*outgoing)->to && hdr_is_in_list("to", *custom))
+ (*outgoing)->to = mail_newaddr();
+ if(!(*outgoing)->cc && hdr_is_in_list("cc", *custom))
+ (*outgoing)->cc = mail_newaddr();
+ if(!(*outgoing)->bcc && hdr_is_in_list("bcc", *custom))
+ (*outgoing)->bcc = mail_newaddr();
+
+ if(our_replyto == 'E'){
+ /* user erased reply-to before postponing */
+ if((*outgoing)->reply_to)
+ mail_free_address(&(*outgoing)->reply_to);
+
+ /*
+ * If empty is not the normal default, make the outgoing
+ * reply_to be an emtpy address. If it is default, leave it
+ * as NULL and the default will be used.
+ */
+ if(hdr_is_in_list("reply-to", *custom)){
+ PINEFIELD pf;
+
+ pf.name = "reply-to";
+ set_default_hdrval(&pf, *custom);
+ if(pf.textbuf){
+ if(pf.textbuf[0]) /* empty is not default */
+ (*outgoing)->reply_to = mail_newaddr();
+
+ fs_give((void **)&pf.textbuf);
+ }
+ }
+ }
+ else if(our_replyto == 'F'){
+ int add_to_role = 0;
+
+ /*
+ * The reply-to is real. If it is different from the default
+ * reply-to, put it in the role so that it will show up when
+ * the user edits.
+ */
+ if(hdr_is_in_list("reply-to", *custom)){
+ PINEFIELD pf;
+ char *str;
+
+ pf.name = "reply-to";
+ set_default_hdrval(&pf, *custom);
+ if(pf.textbuf && pf.textbuf[0]){
+ if((str = addr_list_string((*outgoing)->reply_to,NULL,1)) != NULL){
+ if(!strcmp(str, pf.textbuf)){
+ /* standard value, leave it alone */
+ ;
+ }
+ else /* not standard, put in role */
+ add_to_role++;
+
+ fs_give((void **)&str);
+ }
+ }
+ else /* not standard, put in role */
+ add_to_role++;
+
+ if(pf.textbuf)
+ fs_give((void **)&pf.textbuf);
+ }
+ else /* not standard, put in role */
+ add_to_role++;
+
+ if(add_to_role && role && (*role == NULL || added_to_role)){
+ if(*role == NULL){
+ added_to_role++;
+ *role = (ACTION_S *)fs_get(sizeof(**role));
+ memset((void *)*role, 0, sizeof(**role));
+ }
+
+ (*role)->replyto = (*outgoing)->reply_to;
+ (*outgoing)->reply_to = NULL;
+ }
+ }
+ else{
+ /* this is a bogus c-client generated replyto */
+ if((*outgoing)->reply_to)
+ mail_free_address(&(*outgoing)->reply_to);
+ }
+
+ if((smtp_servers || nntp_servers)
+ && role && (*role == NULL || added_to_role)){
+ if(*role == NULL){
+ *role = (ACTION_S *)fs_get(sizeof(**role));
+ memset((void *)*role, 0, sizeof(**role));
+ }
+
+ if(smtp_servers)
+ (*role)->smtp = smtp_servers;
+ if(nntp_servers)
+ (*role)->nntp = nntp_servers;
+ }
+
+ if(!(*outgoing)->subject && hdr_is_in_list("subject", *custom))
+ (*outgoing)->subject = cpystr("");
+
+ if(!pine_generated){
+ /*
+ * Now, this is interesting. We should have found
+ * the "fcc:" field if pine wrote the message being
+ * redrafted. Hence, we probably can't trust the
+ * "originator" type fields, so we'll blast them and let
+ * them get set later in pine_send. This should allow
+ * folks with custom or edited From's and such to still
+ * use redraft reasonably, without inadvertently sending
+ * messages that appear to be "From" others...
+ */
+ if((*outgoing)->from)
+ mail_free_address(&(*outgoing)->from);
+
+ /*
+ * Ditto for Reply-To and Sender...
+ */
+ if((*outgoing)->reply_to)
+ mail_free_address(&(*outgoing)->reply_to);
+
+ if((*outgoing)->sender)
+ mail_free_address(&(*outgoing)->sender);
+ }
+
+ if(!pine_generated || !(flags & REDRAFT_DEL)){
+
+ /*
+ * Generate a fresh message id for pretty much the same
+ * reason From and such got wacked...
+ * Also, if we're coming from a form letter, we need to
+ * generate a different id each time.
+ */
+ if((*outgoing)->message_id)
+ fs_give((void **)&(*outgoing)->message_id);
+
+ (*outgoing)->message_id = generate_message_id();
+ }
+
+ if(b && b->type != TYPETEXT){
+ if(b->type == TYPEMULTIPART){
+ if(strucmp(b->subtype, "mixed")){
+ q_status_message1(SM_INFO, 3, 4,
+ "Converting Multipart/%s to Multipart/Mixed",
+ b->subtype);
+ fs_give((void **)&b->subtype);
+ b->subtype = cpystr("mixed");
+ }
+ }
+ else{
+ q_status_message2(SM_ORDER | SM_DING, 3, 4,
+ "Unable to resume type %s/%s message",
+ body_types[b->type], b->subtype);
+ return(redraft_cleanup(streamp, TRUE, flags));
+ }
+ }
+
+ gf_set_so_writec(&pc, so);
+
+ if(b && b->type != TYPETEXT){ /* already TYPEMULTIPART */
+ *body = copy_body(NULL, b);
+ part = (*body)->nested.part;
+ part->body.contents.text.data = (void *)so;
+ set_mime_type_by_grope(&part->body);
+ if(part->body.type != TYPETEXT){
+ q_status_message2(SM_ORDER | SM_DING, 3, 4,
+ "Unable to resume; first part is non-text: %s/%s",
+ body_types[part->body.type],
+ part->body.subtype);
+ return(redraft_cleanup(streamp, TRUE, flags));
+ }
+
+ if((charset = parameter_val(part->body.parameter,"charset")) != NULL){
+ /* let outgoing routines decide on charset */
+ if(!strucmp(charset, "US-ASCII") || !strucmp(charset, "UTF-8"))
+ set_parameter(&part->body.parameter, "charset", NULL);
+
+ fs_give((void **) &charset);
+ }
+
+ ps_global->postpone_no_flow = 1;
+
+ get_body_part_text(stream, &b->nested.part->body,
+ cont_msg, "1", 0L, pc, NULL, NULL, gbpt_flags);
+ ps_global->postpone_no_flow = 0;
+
+ if(!fetch_contents(stream, cont_msg, NULL, *body))
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Error including all message parts"));
+ }
+ else{
+ *body = mail_newbody();
+ (*body)->type = TYPETEXT;
+ if(b->subtype)
+ (*body)->subtype = cpystr(b->subtype);
+
+ if((charset = parameter_val(b->parameter,"charset")) != NULL){
+ /* let outgoing routines decide on charset */
+ if(!strucmp(charset, "US-ASCII") || !strucmp(charset, "UTF-8"))
+ fs_give((void **) &charset);
+ else{
+ (*body)->parameter = mail_newbody_parameter();
+ (*body)->parameter->attribute = cpystr("charset");
+ if(utf8_charset(charset)){
+ fs_give((void **) &charset);
+ (*body)->parameter->value = cpystr("UTF-8");
+ }
+ else
+ (*body)->parameter->value = charset;
+ }
+ }
+
+ (*body)->contents.text.data = (void *)so;
+ ps_global->postpone_no_flow = 1;
+ get_body_part_text(stream, b, cont_msg, "1", 0L, pc,
+ NULL, NULL, gbpt_flags);
+ ps_global->postpone_no_flow = 0;
+ }
+
+ gf_clear_so_writec(so);
+
+ /* We have what we want, blast this message... */
+ if((flags & REDRAFT_DEL)
+ && cont_msg > 0L && stream && cont_msg <= stream->nmsgs
+ && (mc = mail_elt(stream, cont_msg)) && !mc->deleted)
+ mail_flag(stream, long2string(cont_msg), "\\DELETED", ST_SET);
+ }
+ else
+ return(redraft_cleanup(streamp, TRUE, flags));
+
+ return(redraft_cleanup(streamp, FALSE, flags));
+}
+
+
+/*----------------------------------------------------------------------
+ Clear deleted messages from given stream and expunge if necessary
+
+Args: stream --
+ problem --
+
+ ----*/
+int
+redraft_cleanup(MAILSTREAM **streamp, int problem, int flags)
+{
+ MAILSTREAM *stream;
+
+ if(!(streamp && *streamp))
+ return(0);
+
+ if(!problem && streamp && (stream = *streamp)){
+ if(stream->nmsgs){
+ ps_global->expunge_in_progress = 1;
+ mail_expunge(stream); /* clean out deleted */
+ ps_global->expunge_in_progress = 0;
+ }
+
+ if(!stream->nmsgs){ /* close and delete folder */
+ int do_the_broach = 0;
+ char *mbox = NULL;
+
+ if(stream){
+ if(stream->original_mailbox && stream->original_mailbox[0])
+ mbox = cpystr(stream->original_mailbox);
+ else if(stream->mailbox && stream->mailbox[0])
+ mbox = cpystr(stream->mailbox);
+ }
+
+ /* if it is current, we have to change folders */
+ if(stream == ps_global->mail_stream)
+ do_the_broach++;
+
+ /*
+ * This is the stream to the empty postponed-msgs folder.
+ * We are going to delete the folder in a second. It is
+ * probably preferable to unselect the mailbox and leave
+ * this stream open for re-use instead of actually closing it,
+ * so we do that if possible.
+ */
+ if(is_imap_stream(stream) && LEVELUNSELECT(stream)){
+ /*
+ * This does the UNSELECT on the stream. A NULL
+ * return should mean that something went wrong and
+ * a mail_close already happened, so that should have
+ * cleaned things up in the callback.
+ */
+ if((stream=mail_open(stream, stream->mailbox,
+ OP_HALFOPEN | (stream->debug ? OP_DEBUG : NIL))) != NULL){
+ /* now close it so it is put into the stream cache */
+ sp_set_flags(stream, sp_flags(stream) | SP_TEMPUSE);
+ pine_mail_close(stream);
+ }
+ }
+ else
+ pine_mail_actually_close(stream);
+
+ *streamp = NULL;
+
+ if(do_the_broach){
+ ps_global->mail_stream = NULL; /* already closed above */
+ }
+
+ if(mbox && !pine_mail_delete(NULL, mbox))
+ q_status_message1(SM_ORDER|SM_DING, 3, 3,
+ /* TRANSLATORS: Arg is a mailbox name */
+ _("Can't delete %s"), mbox);
+
+ if(mbox)
+ fs_give((void **) &mbox);
+ }
+ }
+
+ return(!problem);
+}
+
+
+/*----------------------------------------------------------------------
+ Parse the given header text for any given fields
+
+Args: text -- Text to parse for fcc and attachments refs
+ fields -- array of field names to look for
+ values -- array of pointer to save values to, returned NULL if
+ fields isn't in text.
+
+This function simply looks for the given fields in the given header
+text string.
+NOTE: newlines are expected CRLF, and we'll ignore continuations
+ ----*/
+void
+simple_header_parse(char *text, char **fields, char **values)
+{
+ int i, n;
+ char *p, *t;
+
+ for(i = 0; fields[i]; i++)
+ values[i] = NULL; /* clear values array */
+
+ /*---- Loop until the end of the header ----*/
+ for(p = text; *p; ){
+ for(i = 0; fields[i]; i++) /* find matching field? */
+ if(!struncmp(p, fields[i], (n=strlen(fields[i]))) && p[n] == ':'){
+ for(p += n + 1; *p; p++){ /* find start of value */
+ if(*p == '\015' && *(p+1) == '\012'
+ && !isspace((unsigned char) *(p+2)))
+ break;
+
+ if(!isspace((unsigned char) *p))
+ break; /* order here is key... */
+ }
+
+ if(!values[i]){ /* if we haven't already */
+ values[i] = fs_get(strlen(text) + 1);
+ values[i][0] = '\0'; /* alloc space for it */
+ }
+
+ if(*p && *p != '\015'){ /* non-blank value. */
+ t = values[i] + (values[i][0] ? strlen(values[i]) : 0);
+ while(*p){ /* check for cont'n lines */
+ if(*p == '\015' && *(p+1) == '\012'){
+ if(isspace((unsigned char) *(p+2))){
+ p += 2;
+ continue;
+ }
+ else
+ break;
+ }
+
+ *t++ = *p++;
+ }
+
+ *t = '\0';
+ }
+
+ break;
+ }
+
+ /* Skip to end of line, what ever it was */
+ for(; *p ; p++)
+ if(*p == '\015' && *(p+1) == '\012'){
+ p += 2;
+ break;
+ }
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ build a fresh REPLY_S from the given string (see pine_send for format)
+
+Args: s -- "X-Reply-UID" header value
+
+Returns: filled in REPLY_S or NULL on parse error
+ ----*/
+REPLY_S *
+build_reply_uid(char *s)
+{
+ char *p, *prefix = NULL, *val, *seq, *mbox;
+ int i, nseq, forwarded = 0;
+ REPLY_S *reply = NULL;
+
+ /* FORMAT: (n prefix)(n validity uidlist)mailbox */
+ /* if 'n prefix' is empty, uid list represents forwarded msgs */
+ if(*s == '('){
+ if(*(p = s + 1) == ')'){
+ forwarded = 1;
+ }
+ else{
+ for(; isdigit(*p); p++)
+ ;
+
+ if(*p == ' '){
+ *p++ = '\0';
+
+ if((i = atoi(s+1)) && i < strlen(p)){
+ prefix = p;
+ *(p += i) = '\0';
+ }
+ }
+ else
+ return(NULL);
+ }
+
+ if(*++p == '(' && *++p){
+ for(seq = p; isdigit(*p); p++)
+ ;
+
+ if(*p == ' '){
+ *p++ = '\0';
+ for(val = p; isdigit(*p); p++)
+ ;
+
+ if(*p == ' '){
+ *p++ = '\0';
+
+ if((nseq = atoi(seq)) && isdigit(*(seq = p))
+ && (p = strchr(p, ')')) && *(mbox = ++p)){
+ imapuid_t *uidl;
+
+ uidl = (imapuid_t *) fs_get ((nseq+1)*sizeof(imapuid_t));
+ for(i = 0; i < nseq; i++)
+ if((p = strchr(seq,',')) != NULL){
+ *p = '\0';
+ if((uidl[i]= strtoul(seq,NULL,10)) != 0)
+ seq = ++p;
+ else
+ break;
+ }
+ else if((p = strchr(seq, ')')) != NULL){
+ if((uidl[i] = strtoul(seq,NULL,10)) != 0)
+ i++;
+
+ break;
+ }
+
+ if(i == nseq){
+ reply = (REPLY_S *)fs_get(sizeof(REPLY_S));
+ memset(reply, 0, sizeof(REPLY_S));
+ reply->uid = 1;
+ reply->data.uid.validity = strtoul(val, NULL, 10);
+ if(forwarded)
+ reply->forwarded = 1;
+ else
+ reply->prefix = cpystr(prefix);
+
+ reply->mailbox = cpystr(mbox);
+ uidl[nseq] = 0;
+ reply->data.uid.msgs = uidl;
+ }
+ else
+ fs_give((void **) &uidl);
+ }
+ }
+ }
+ }
+ }
+
+ return(reply);
+}
+
+
+/*
+ * pine_new_env - allocate a new METAENV and fill it in.
+ */
+METAENV *
+pine_new_env(ENVELOPE *outgoing, char **fccp, char ***tobufpp, PINEFIELD *custom)
+{
+ int cnt, i, stdcnt;
+ char *p;
+ PINEFIELD *pfields, *pf, **sending_order;
+ METAENV *header;
+
+ header = (METAENV *) fs_get(sizeof(METAENV));
+
+ /* how many fields are there? */
+ for(cnt = 0; pf_template && pf_template[cnt].name; cnt++)
+ ;
+
+ stdcnt = cnt;
+
+ for(pf = custom; pf; pf = pf->next)
+ cnt++;
+
+ /* temporary PINEFIELD array */
+ i = (cnt + 1) * sizeof(PINEFIELD);
+ pfields = (PINEFIELD *)fs_get((size_t) i);
+ memset(pfields, 0, (size_t) i);
+
+ i = (cnt + 1) * sizeof(PINEFIELD *);
+ sending_order = (PINEFIELD **)fs_get((size_t) i);
+ memset(sending_order, 0, (size_t) i);
+
+ header->env = outgoing;
+ header->local = pfields;
+ header->custom = custom;
+ header->sending_order = sending_order;
+
+#if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
+# define NN 4
+#else
+# define NN 3
+#endif
+
+ /* initialize pfield */
+ pf = pfields;
+ for(i=0; i < stdcnt; i++, pf++){
+
+ pf->name = cpystr(pf_template[i].name);
+ if(i == N_SENDER && F_ON(F_USE_SENDER_NOT_X, ps_global))
+ /* slide string over so it is Sender instead of X-X-Sender */
+ for(p=pf->name; *(p+1); p++)
+ *p = *(p+4);
+
+ pf->type = pf_template[i].type;
+ pf->canedit = pf_template[i].canedit;
+ pf->rcptto = pf_template[i].rcptto;
+ pf->writehdr = pf_template[i].writehdr;
+ pf->localcopy = pf_template[i].localcopy;
+ pf->extdata = NULL; /* unused */
+ pf->next = pf + 1;
+
+ switch(pf->type){
+ case FreeText:
+ switch(i){
+ case N_AUTHRCVD:
+ sending_order[0] = pf;
+ break;
+
+ case N_NEWS:
+ pf->text = &outgoing->newsgroups;
+ sending_order[1] = pf;
+ break;
+
+ case N_DATE:
+ pf->text = (char **) &outgoing->date;
+ sending_order[2] = pf;
+ break;
+
+ case N_INREPLY:
+ pf->text = &outgoing->in_reply_to;
+ sending_order[NN+9] = pf;
+ break;
+
+ case N_MSGID:
+ pf->text = &outgoing->message_id;
+ sending_order[NN+10] = pf;
+ break;
+
+ case N_REF: /* won't be used here */
+ sending_order[NN+11] = pf;
+ break;
+
+ case N_PRIORITY:
+ sending_order[NN+12] = pf;
+ break;
+
+ case N_USERAGENT:
+ pf->text = &pf->textbuf;
+ pf->textbuf = generate_user_agent();
+ sending_order[NN+13] = pf;
+ break;
+
+ case N_POSTERR: /* won't be used here */
+ sending_order[NN+14] = pf;
+ break;
+
+ case N_RPLUID: /* won't be used here */
+ sending_order[NN+15] = pf;
+ break;
+
+ case N_RPLMBOX: /* won't be used here */
+ sending_order[NN+16] = pf;
+ break;
+
+ case N_SMTP: /* won't be used here */
+ sending_order[NN+17] = pf;
+ break;
+
+ case N_NNTP: /* won't be used here */
+ sending_order[NN+18] = pf;
+ break;
+
+ case N_CURPOS: /* won't be used here */
+ sending_order[NN+19] = pf;
+ break;
+
+ case N_OURREPLYTO: /* won't be used here */
+ sending_order[NN+20] = pf;
+ break;
+
+ case N_OURHDRS: /* won't be used here */
+ sending_order[NN+21] = pf;
+ break;
+
+ default:
+ q_status_message1(SM_ORDER,3,3,
+ "Internal error: 1)FreeText header %s", comatose(i));
+ break;
+ }
+
+ break;
+
+ case Attachment:
+ break;
+
+ case Address:
+ switch(i){
+ case N_FROM:
+ sending_order[3] = pf;
+ pf->addr = &outgoing->from;
+ break;
+
+ case N_TO:
+ sending_order[NN+2] = pf;
+ pf->addr = &outgoing->to;
+ if(tobufpp)
+ (*tobufpp) = &pf->scratch;
+
+ break;
+
+ case N_CC:
+ sending_order[NN+3] = pf;
+ pf->addr = &outgoing->cc;
+ break;
+
+ case N_BCC:
+ sending_order[NN+4] = pf;
+ pf->addr = &outgoing->bcc;
+ break;
+
+ case N_REPLYTO:
+ sending_order[NN+1] = pf;
+ pf->addr = &outgoing->reply_to;
+ break;
+
+ case N_LCC: /* won't be used here */
+ sending_order[NN+7] = pf;
+ break;
+
+#if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
+ case N_SENDER:
+ sending_order[4] = pf;
+ pf->addr = &outgoing->sender;
+ break;
+#endif
+
+ case N_NOBODY: /* won't be used here */
+ sending_order[NN+5] = pf;
+ break;
+
+ default:
+ q_status_message1(SM_ORDER,3,3,
+ "Internal error: Address header %s", comatose(i));
+ break;
+ }
+ break;
+
+ case Fcc:
+ sending_order[NN+8] = pf;
+ pf->text = fccp;
+ break;
+
+ case Subject:
+ sending_order[NN+6] = pf;
+ pf->text = &outgoing->subject;
+ break;
+
+ default:
+ q_status_message1(SM_ORDER,3,3,
+ "Unknown header type %d in pine_new_send", (void *)pf->type);
+ break;
+ }
+ }
+
+ if(((--pf)->next = custom) != NULL){
+ i--;
+
+ /*
+ * NOTE: "i" is assumed to now index first custom field in sending
+ * order.
+ */
+ for(pf = pf->next; pf && pf->name; pf = pf->next){
+ if(pf->standard)
+ continue;
+
+ pf->canedit = 1;
+ pf->rcptto = 0;
+ pf->writehdr = 1;
+ pf->localcopy = 1;
+
+ switch(pf->type){
+ case Address:
+ if(pf->addr){ /* better be set */
+ char *addr = NULL;
+ BuildTo bldto;
+
+ bldto.type = Str;
+ bldto.arg.str = pf->textbuf;
+
+ sending_order[i++] = pf;
+ /* change default text into an ADDRESS */
+ /* strip quotes around whole default */
+ removing_trailing_white_space(pf->textbuf);
+ (void)removing_double_quotes(pf->textbuf);
+ build_address_internal(bldto, &addr, NULL, NULL, NULL, NULL, NULL, 0, NULL);
+ rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
+ fs_give((void **)&addr);
+ if(pf->textbuf)
+ fs_give((void **)&pf->textbuf);
+ }
+
+ break;
+
+ case FreeText:
+ sending_order[i++] = pf;
+ pf->text = &pf->textbuf;
+ break;
+
+ default:
+ q_status_message1(SM_ORDER,0,7,"Unknown custom header type %d",
+ (void *)pf->type);
+ break;
+ }
+ }
+ }
+
+
+ return(header);
+}
+
+
+void
+pine_free_env(METAENV **menv)
+{
+ int cnt;
+
+
+ if((*menv)->local){
+ for(cnt = 0; pf_template && pf_template[cnt].name; cnt++)
+ ;
+
+ for(; cnt >= 0; cnt--){
+ if((*menv)->local[cnt].textbuf)
+ fs_give((void **) &(*menv)->local[cnt].textbuf);
+
+ fs_give((void **) &(*menv)->local[cnt].name);
+ }
+
+ fs_give((void **) &(*menv)->local);
+ }
+
+ if((*menv)->sending_order)
+ fs_give((void **) &(*menv)->sending_order);
+
+ fs_give((void **) menv);
+}
+
+
+/*----------------------------------------------------------------------
+ Check for addresses the user is not permitted to send to, or probably
+ doesn't want to send to
+
+Returns: 0 if OK
+ 1 if there are only empty groups
+ -1 if the message shouldn't be sent
+
+Queues a message indicating what happened
+ ---*/
+int
+check_addresses(METAENV *header)
+{
+ PINEFIELD *pf;
+ ADDRESS *a;
+ int send_daemon = 0, rv = CA_EMPTY;
+
+ /*---- Is he/she trying to send mail to the mailer-daemon ----*/
+ for(pf = header->local; pf && pf->name; pf = pf->next)
+ if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr)
+ for(a = *pf->addr; a != NULL; a = a->next){
+ if(a->host && (a->host[0] == '.'
+ || (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global)
+ && a->host[0] == '@'))){
+ q_status_message2(SM_ORDER, 4, 7,
+ /* TRANSLATORS: First arg is the address we can't
+ send to, second arg is "not in addressbook". */
+ _("Can't send to address %s: %s"),
+ a->mailbox,
+ (a->host[0] == '.')
+ ? a->host
+ : _("not in addressbook"));
+ return(CA_BAD);
+ }
+ else if(ps_global->restricted
+ && !address_is_us(*pf->addr, ps_global)){
+ q_status_message(SM_ORDER, 3, 3,
+ "Restricted demo version of Alpine. You may only send mail to yourself");
+ return(CA_BAD);
+ }
+ else if(a->mailbox && strucmp(a->mailbox, "mailer-daemon") == 0 && !send_daemon){
+ send_daemon = 1;
+ rv = (pith_opt_daemon_confirm && (*pith_opt_daemon_confirm)()) ? CA_OK : CA_BAD;
+ }
+ else if(a->mailbox && a->host){
+ rv = CA_OK;
+ }
+ }
+
+ return(rv);
+}
+
+
+/*
+ * If this isn't general enough we can modify it. The value passed in
+ * is expected to be one of the desc settings from the priorities array,
+ * like "High". The header value is X-Priority: 2 (High)
+ * or something similar. If value doesn't match any of the values then
+ * the actual value is used instead.
+ */
+PINEFIELD *
+set_priority_header(METAENV *header, char *value)
+{
+ PINEFIELD *pf;
+
+ for(pf = header->local; pf && pf->name; pf = pf->next)
+ if(pf->type == FreeText && !strcmp(pf->name, PRIORITYNAME))
+ break;
+
+ if(pf){
+ if(pf->textbuf)
+ fs_give((void **) &pf->textbuf);
+
+ if(value){
+ PRIORITY_S *p;
+
+ for(p = priorities; p && p->desc; p++)
+ if(!strcmp(p->desc, value))
+ break;
+
+ if(p && p->desc){
+ char buf[100];
+
+ snprintf(buf, sizeof(buf), "%d (%s)", p->val, p->desc);
+ pf->textbuf = cpystr(buf);
+ }
+ else
+ pf->textbuf = cpystr(value);
+ }
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Set answered flags for messages specified by reply structure
+
+Args: reply --
+
+Returns: with appropriate flags set and index cache entries suitably tweeked
+----*/
+void
+update_answered_flags(REPLY_S *reply)
+{
+ char *seq = NULL, *p;
+ long i, ourstream = 0, we_cancel = 0;
+ MAILSTREAM *stream = NULL;
+
+ /* nothing to flip in a pseudo reply */
+ if(reply && (reply->msgno || reply->uid)){
+ int j;
+ MAILSTREAM *m;
+
+ /*
+ * If an established stream will do, use it, else
+ * build one unless we have an array of msgno's...
+ *
+ * I was just mimicking what was already here. I don't really
+ * understand why we use strcmp instead of same_stream_and_mailbox().
+ * Or sp_stream_get(reply->mailbox, SP_MATCH).
+ * Hubert 2003-07-09
+ */
+ for(j = 0; !stream && j < ps_global->s_pool.nstream; j++){
+ m = ps_global->s_pool.streams[j];
+ if(m && reply->mailbox && m->mailbox
+ && !strcmp(reply->mailbox, m->mailbox))
+ stream = m;
+ }
+
+ if(!stream && reply->msgno)
+ return;
+
+ /*
+ * This is here only for people who ran pine4.42 and are
+ * processing postponed mail from 4.42 now. Pine4.42 saved the
+ * original mailbox name in the canonical name's position in
+ * the postponed-msgs folder so it won't match the canonical
+ * name from the stream.
+ */
+ if(!stream && (!reply->origmbox ||
+ (reply->mailbox &&
+ !strcmp(reply->origmbox, reply->mailbox))))
+ stream = sp_stream_get(reply->mailbox, SP_MATCH);
+
+ /* TRANSLATORS: program is busy updating the Answered flags so warns user */
+ we_cancel = busy_cue(_("Updating \"Answered\" Flags"), NULL, 0);
+ if(!stream){
+ if((stream = pine_mail_open(NULL,
+ reply->origmbox ? reply->origmbox
+ : reply->mailbox,
+ OP_SILENT | SP_USEPOOL | SP_TEMPUSE,
+ NULL)) != NULL){
+ ourstream++;
+ }
+ else{
+ if(we_cancel)
+ cancel_busy_cue(0);
+
+ return;
+ }
+ }
+
+ if(stream->uid_validity == reply->data.uid.validity){
+ for(i = 0L, p = tmp_20k_buf; reply->data.uid.msgs[i]; i++){
+ if(i){
+ sstrncpy(&p, ",", SIZEOF_20KBUF-(p-tmp_20k_buf));
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ }
+
+ sstrncpy(&p, ulong2string(reply->data.uid.msgs[i]),
+ SIZEOF_20KBUF-(p-tmp_20k_buf));
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ }
+
+ if(reply->forwarded){
+ /*
+ * $Forwarded is a regular keyword so we only try to
+ * set it if the stream allows keywords.
+ * We could mess up if the stream has keywords but just
+ * isn't allowing anymore and $Forwarded already exists,
+ * but what are the odds?
+ */
+ if(stream && stream->kwd_create)
+ mail_flag(stream, seq = cpystr(tmp_20k_buf),
+ FORWARDED_FLAG,
+ ST_SET | ((reply->uid) ? ST_UID : 0L));
+ }
+ else
+ mail_flag(stream, seq = cpystr(tmp_20k_buf),
+ "\\ANSWERED",
+ ST_SET | ((reply->uid) ? ST_UID : 0L));
+
+ if(seq)
+ fs_give((void **)&seq);
+ }
+
+ if(ourstream)
+ pine_mail_close(stream); /* clean up dangling stream */
+
+ if(we_cancel)
+ cancel_busy_cue(0);
+ }
+}
+
+
+/*
+ * phone_home_from - make phone home request's from address IMpersonal.
+ * Doesn't include user's personal name.
+ */
+ADDRESS *
+phone_home_from(void)
+{
+ ADDRESS *addr = mail_newaddr();
+ char tmp[32];
+
+ /* garble up mailbox name */
+ snprintf(tmp, sizeof(tmp), "hash_%08u", phone_home_hash(ps_global->VAR_USER_ID));
+ tmp[sizeof(tmp)-1] = '\0';
+ addr->mailbox = cpystr(tmp);
+ addr->host = cpystr(ps_global->maildomain);
+ return(addr);
+}
+
+
+/*
+ * one-way-hash a username into an 8-digit decimal number
+ *
+ * Corey Satten, corey@cac.washington.edu, 7/15/98
+ */
+unsigned int
+phone_home_hash(char *s)
+{
+ unsigned int h;
+
+ for (h=0; *s; ++s) {
+ if (h & 1)
+ h = (h>>1) | (PH_MAXHASH/2);
+ else
+ h = (h>>1);
+
+ h = ((h+1) * ((unsigned char) *s)) & (PH_MAXHASH - 1);
+ }
+
+ return (h);
+}
+
+
+/*----------------------------------------------------------------------
+ Call the mailer, SMTP, sendmail or whatever
+
+Args: header -- full header (envelope and local parts) of message to send
+ body -- The full body of the message including text
+ alt_smtp_servers --
+ verbosefile -- non-null means caller wants verbose interaction and the resulting
+ output file name to be returned
+
+Returns: -1 if failed, 1 if succeeded
+----*/
+int
+call_mailer(METAENV *header, struct mail_bodystruct *body, char **alt_smtp_servers,
+ int flags, void (*bigresult_f)(char *, int),
+ void (*pipecb_f)(PIPE_S *, int, void *))
+{
+ char error_buf[200], *error_mess = NULL, *postcmd;
+ ADDRESS *a;
+ ENVELOPE *fake_env = NULL;
+ int addr_error_count, we_cancel = 0;
+ long smtp_opts = 0L;
+ char *verbose_file = NULL;
+ BODY *bp = NULL;
+ PINEFIELD *pf;
+ BODY *origBody = body;
+
+ dprint((4, "Sending mail...\n"));
+
+ /* Check for any recipients */
+ for(pf = header->local; pf && pf->name; pf = pf->next)
+ if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr)
+ break;
+
+ if(!pf){
+ q_status_message(SM_ORDER,3,3,
+ _("Can't send message. No recipients specified!"));
+ return(0);
+ }
+
+#ifdef SMIME
+ if(ps_global->smime && (ps_global->smime->do_encrypt || ps_global->smime->do_sign)){
+ int result;
+
+ STORE_S *so = lmc.so;
+ lmc.so = NULL;
+
+ result = 1;
+
+ if(ps_global->smime->do_encrypt)
+ result = encrypt_outgoing_message(header, &body);
+
+ /* need to free new body from encrypt if sign fails? */
+ if(result && ps_global->smime->do_sign)
+ result = sign_outgoing_message(header, &body, ps_global->smime->do_encrypt);
+
+ lmc.so = so;
+
+ if(!result)
+ return 0;
+ }
+#endif
+
+ /* set up counts and such to keep track sent percentage */
+ send_bytes_sent = 0;
+ gf_filter_init(); /* zero piped byte count, 'n */
+ send_bytes_to_send = send_body_size(body); /* count body bytes */
+ ps_global->c_client_error[0] = error_buf[0] = '\0';
+ we_cancel = busy_cue(_("Sending mail"),
+ send_bytes_to_send ? sent_percent : NULL, 0);
+
+#ifndef _WINDOWS
+
+ /* try posting via local "<mta> <-t>" if specified */
+ if(mta_handoff(header, body, error_buf, sizeof(error_buf), bigresult_f, pipecb_f)){
+ if(error_buf[0])
+ error_mess = error_buf;
+
+ goto done;
+ }
+
+#endif
+
+ /*
+ * If the user's asked for it, and we find that the first text
+ * part (attachments all get b64'd) is non-7bit, ask for 8BITMIME.
+ */
+ if(F_ON(F_ENABLE_8BIT, ps_global) && (bp = first_text_8bit(body)))
+ smtp_opts |= SOP_8BITMIME;
+
+#ifdef DEBUG
+#ifndef DEBUGJOURNAL
+ if(debug > 5 || (flags & CM_VERBOSE))
+#endif
+ smtp_opts |= SOP_DEBUG;
+#endif
+
+ if(flags & (CM_DSN_NEVER | CM_DSN_DELAY | CM_DSN_SUCCESS | CM_DSN_FULL)){
+ smtp_opts |= SOP_DSN;
+ if(!(flags & CM_DSN_NEVER)){ /* if never, don't turn others on */
+ if(flags & CM_DSN_DELAY)
+ smtp_opts |= SOP_DSN_NOTIFY_DELAY;
+ if(flags & CM_DSN_SUCCESS)
+ smtp_opts |= SOP_DSN_NOTIFY_SUCCESS;
+
+ /*
+ * If it isn't Never, then we're always going to let them
+ * know about failures. This means we don't allow for the
+ * possibility of setting delay or success without failure.
+ */
+ smtp_opts |= SOP_DSN_NOTIFY_FAILURE;
+
+ if(flags & CM_DSN_FULL)
+ smtp_opts |= SOP_DSN_RETURN_FULL;
+ }
+ }
+
+
+ /*
+ * Set global header pointer so post_rfc822_output can get at it when
+ * it's called back from c-client's sending routine...
+ */
+ send_header = header;
+
+ /*
+ * Fabricate a fake ENVELOPE to hand c-client's SMTP engine.
+ * The purpose is to give smtp_mail the list for SMTP RCPT when
+ * there are recipients in pine's METAENV that are outside c-client's
+ * envelope.
+ *
+ * NOTE: If there aren't any, don't bother. Dealt with it below.
+ */
+ for(pf = header->local; pf && pf->name; pf = pf->next)
+ if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr
+ && !(*pf->addr == header->env->to || *pf->addr == header->env->cc
+ || *pf->addr == header->env->bcc))
+ break;
+
+ if(pf && pf->name){
+ ADDRESS **tail;
+
+ fake_env = (ENVELOPE *)fs_get(sizeof(ENVELOPE));
+ memset(fake_env, 0, sizeof(ENVELOPE));
+ fake_env->return_path = rfc822_cpy_adr(header->env->return_path);
+ tail = &(fake_env->to);
+ for(pf = header->local; pf && pf->name; pf = pf->next)
+ if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr){
+ *tail = rfc822_cpy_adr(*pf->addr);
+ while(*tail)
+ tail = &((*tail)->next);
+ }
+ }
+
+ /*
+ * Install our rfc822 output routine
+ */
+ sending_hooks.rfc822_out = mail_parameters(NULL, GET_RFC822OUTPUT, NULL);
+ (void)mail_parameters(NULL, SET_RFC822OUTPUT, (void *)post_rfc822_output);
+
+ /*
+ * Allow for verbose posting
+ */
+ (void) mail_parameters(NULL, SET_SMTPVERBOSE,
+ (void *) pine_smtp_verbose_out);
+
+ /*
+ * We do this because we want mm_log to put the error message into
+ * c_client_error instead of showing it itself.
+ */
+ ps_global->noshow_error = 1;
+
+ /*
+ * OK, who posts what? We tried an mta_handoff above, but there
+ * was either none specified or we decided not to use it. So,
+ * if there's an smtp-server defined anywhere,
+ */
+ if(alt_smtp_servers && alt_smtp_servers[0] && alt_smtp_servers[0][0]){
+ /*---------- SMTP ----------*/
+ dprint((4, "call_mailer: via TCP (%s)\n",
+ alt_smtp_servers[0]));
+ TIME_STAMP("smtp-open start (tcp)", 1);
+ sending_stream = smtp_open(alt_smtp_servers, smtp_opts);
+ }
+ else if(ps_global->VAR_SMTP_SERVER && ps_global->VAR_SMTP_SERVER[0]
+ && ps_global->VAR_SMTP_SERVER[0][0]){
+ /*---------- SMTP ----------*/
+ dprint((4, "call_mailer: via TCP\n"));
+ TIME_STAMP("smtp-open start (tcp)", 1);
+ sending_stream = smtp_open(ps_global->VAR_SMTP_SERVER, smtp_opts);
+ }
+ else if((postcmd = smtp_command(ps_global->c_client_error, sizeof(ps_global->c_client_error))) != NULL){
+ char *cmdlist[2];
+
+ /*----- Send via LOCAL SMTP agent ------*/
+ dprint((4, "call_mailer: via \"%s\"\n", postcmd));
+
+ TIME_STAMP("smtp-open start (pipe)", 1);
+ fs_give((void **) &postcmd);
+ cmdlist[0] = "localhost";
+ cmdlist[1] = NULL;
+ sending_stream = smtp_open_full(&piped_io, cmdlist, "smtp",
+ SMTPTCPPORT, smtp_opts);
+/* BUG: should provide separate stderr output! */
+ }
+
+ ps_global->noshow_error = 0;
+
+ TIME_STAMP("smtp open", 1);
+ if(sending_stream){
+ unsigned short save_encoding, added_encoding;
+
+ dprint((1, "Opened SMTP server \"%s\"\n",
+ net_host(sending_stream->netstream)
+ ? net_host(sending_stream->netstream) : "?"));
+
+ if(flags & CM_VERBOSE){
+ TIME_STAMP("verbose start", 1);
+ if((verbose_file = temp_nam(NULL, "sd")) != NULL){
+ if((verbose_send_output = our_fopen(verbose_file, "w")) != NULL){
+ if(!smtp_verbose(sending_stream)){
+ snprintf(error_mess = error_buf, sizeof(error_buf),
+ "Mail not sent. VERBOSE mode error%s%.50s.",
+ (sending_stream && sending_stream->reply)
+ ? ": ": "",
+ (sending_stream && sending_stream->reply)
+ ? sending_stream->reply : "");
+ error_buf[sizeof(error_buf)-1] = '\0';
+ }
+ }
+ else{
+ our_unlink(verbose_file);
+ strncpy(error_mess = error_buf,
+ "Can't open tmp file for VERBOSE mode.", sizeof(error_buf));
+ error_buf[sizeof(error_buf)-1] = '\0';
+ }
+ }
+ else{
+ strncpy(error_mess = error_buf,
+ "Can't create tmp file name for VERBOSE mode.", sizeof(error_buf));
+ error_buf[sizeof(error_buf)-1] = '\0';
+ }
+
+ TIME_STAMP("verbose end", 1);
+ }
+
+ /*
+ * Before we actually send data, see if we have to protect
+ * the first text body part from getting encoded. We protect
+ * it from getting encoded in "pine_rfc822_output_body" by
+ * temporarily inventing a synonym for ENC8BIT...
+ * This works like so:
+ * Suppose bp->encoding is set to ENC8BIT.
+ * We change that here to some unused value (added_encoding) and
+ * set body_encodings[added_encoding] to "8BIT".
+ * Then post_rfc822_output is called which calls
+ * pine_rfc822_output_body. Inside that routine
+ * pine_write_body_header writes out the encoding for the
+ * part. Normally it would see encoding == ENC8BIT and it would
+ * change that to QUOTED-PRINTABLE, but since encoding has been
+ * set to added_encoding it uses body_encodings[added_encoding]
+ * which is "8BIT" instead. Then the actual body is written by
+ * pine_write_body_header which does not do the gf_8bit_qp
+ * filtering because encoding != ENC8BIT (instead it's equal
+ * to added_encoding).
+ */
+ if(bp && sending_stream->protocol.esmtp.eightbit.ok
+ && sending_stream->protocol.esmtp.eightbit.want){
+ int i;
+
+ for(i = 0; (i <= ENCMAX) && body_encodings[i]; i++)
+ ;
+
+ if(i > ENCMAX){ /* no empty encoding slots! */
+ bp = NULL;
+ }
+ else {
+ added_encoding = i;
+ body_encodings[added_encoding] = body_encodings[ENC8BIT];
+ save_encoding = bp->encoding;
+ bp->encoding = added_encoding;
+ }
+ }
+
+ if(sending_stream->protocol.esmtp.ok
+ && sending_stream->protocol.esmtp.dsn.want
+ && !sending_stream->protocol.esmtp.dsn.ok)
+ q_status_message(SM_ORDER,3,3,
+ _("Delivery Status Notification not available from this server."));
+
+ TIME_STAMP("smtp start", 1);
+ if(!error_mess && !smtp_mail(sending_stream, "MAIL",
+ fake_env ? fake_env : header->env, body)){
+
+ snprintf(error_buf, sizeof(error_buf),
+ _("Mail not sent. Sending error%s%s"),
+ (sending_stream && sending_stream->reply) ? ": ": ".",
+ (sending_stream && sending_stream->reply)
+ ? sending_stream->reply : "");
+ error_buf[sizeof(error_buf)-1] = '\0';
+ dprint((1, error_buf));
+ addr_error_count = 0;
+ if(fake_env){
+ for(a = fake_env->to; a != NULL; a = a->next)
+ if(a->error != NULL){
+ if(addr_error_count++ < MAX_ADDR_ERROR){
+
+ /*
+ * Too complicated to figure out which header line
+ * has the error in the fake_env case, so just
+ * leave cursor at default.
+ */
+
+
+ if(error_mess) /* previous error? */
+ q_status_message(SM_ORDER, 4, 7, error_mess);
+
+ error_mess = tidy_smtp_mess(a->error,
+ _("Mail not sent: %.80s"),
+ error_buf, sizeof(error_buf));
+ }
+
+ dprint((1, "Send Error: \"%s\"\n",
+ a->error));
+ }
+ }
+ else{
+ for(pf = header->local; pf && pf->name; pf = pf->next)
+ if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr)
+ for(a = *pf->addr; a != NULL; a = a->next)
+ if(a->error != NULL){
+ if(addr_error_count++ < MAX_ADDR_ERROR){
+
+ if(error_mess) /* previous error? */
+ q_status_message(SM_ORDER, 4, 7, error_mess);
+
+ error_mess = tidy_smtp_mess(a->error,
+ _("Mail not sent: %.80s"),
+ error_buf, sizeof(error_buf));
+ }
+
+ dprint((1, "Send Error: \"%s\"\n",
+ a->error));
+ }
+ }
+
+ if(!error_mess)
+ error_mess = error_buf;
+ }
+
+ /* repair modified "body_encodings" array? */
+ if(bp && sending_stream->protocol.esmtp.eightbit.ok
+ && sending_stream->protocol.esmtp.eightbit.want){
+ body_encodings[added_encoding] = NULL;
+ bp->encoding = save_encoding;
+ }
+
+ TIME_STAMP("smtp closing", 1);
+ smtp_close(sending_stream);
+ sending_stream = NULL;
+ TIME_STAMP("smtp done", 1);
+ }
+ else if(!error_mess){
+ snprintf(error_mess = error_buf, sizeof(error_buf), _("Error sending%.2s%.80s"),
+ ps_global->c_client_error[0] ? ": " : "",
+ ps_global->c_client_error);
+ error_buf[sizeof(error_buf)-1] = '\0';
+ }
+
+ if(verbose_file){
+ if(verbose_send_output){
+ TIME_STAMP("verbose start", 1);
+ fclose(verbose_send_output);
+ verbose_send_output = NULL;
+ q_status_message(SM_ORDER, 0, 3, "Verbose SMTP output received");
+
+ if(bigresult_f)
+ (*bigresult_f)(verbose_file, CM_BR_VERBOSE);
+
+ TIME_STAMP("verbose end", 1);
+ }
+
+ fs_give((void **)&verbose_file);
+ }
+
+ /*
+ * Restore original 822 emitter...
+ */
+ (void) mail_parameters(NULL, SET_RFC822OUTPUT, sending_hooks.rfc822_out);
+
+ if(fake_env)
+ mail_free_envelope(&fake_env);
+
+ done:
+
+#ifdef SMIME
+ /* Free replacement encrypted body */
+ if(F_OFF(F_DONT_DO_SMIME, ps_global) && body != origBody){
+
+ if(body->type == TYPEMULTIPART){
+ /* Just get rid of first part, it's actually origBody */
+ void *x = body->nested.part;
+
+ body->nested.part = body->nested.part->next;
+
+ fs_give(&x);
+ }
+
+ pine_free_body(&body);
+ }
+#endif
+
+ if(we_cancel)
+ cancel_busy_cue(0);
+
+ TIME_STAMP("call_mailer done", 1);
+ /*-------- Did message make it ? ----------*/
+ if(error_mess){
+ /*---- Error sending mail -----*/
+ if(lmc.so && !lmc.all_written)
+ so_give(&lmc.so);
+
+ if(error_mess){
+ q_status_message(SM_ORDER | SM_DING, 4, 7, error_mess);
+ dprint((1, "call_mailer ERROR: %s\n", error_mess));
+ }
+
+ return(-1);
+ }
+ else{
+ lmc.all_written = 1;
+ return(1);
+ }
+}
+
+
+/*
+ * write_postponed - exported method to write the given message
+ * to the postponed folder
+ */
+int
+write_postponed(METAENV *header, struct mail_bodystruct *body)
+{
+ char **pp, *folder;
+ int rv = 0, sz;
+ CONTEXT_S *fcc_cntxt = NULL;
+ PINEFIELD *pf;
+ static char *writem[] = {"To", "References", "Fcc", "X-Reply-UID", NULL};
+
+ if(!ps_global->VAR_POSTPONED_FOLDER
+ || !ps_global->VAR_POSTPONED_FOLDER[0]){
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ _("No postponed file defined"));
+ return(-1);
+ }
+
+ folder = cpystr(ps_global->VAR_POSTPONED_FOLDER);
+
+ lmc.all_written = lmc.text_written = lmc.text_only = 0;
+
+ lmc.so = open_fcc(folder, &fcc_cntxt, 1, NULL, NULL);
+
+ if(lmc.so){
+ /* BUG: writem sufficient ? */
+ for(pf = header->local; pf && pf->name; pf = pf->next)
+ for(pp = writem; *pp; pp++)
+ if(!strucmp(pf->name, *pp)){
+ pf->localcopy = 1;
+ pf->writehdr = 1;
+ break;
+ }
+
+ /*
+ * Work around c-client reply-to bug. C-client will
+ * return a reply_to in an envelope even if there is
+ * no reply-to header field. We want to note here whether
+ * the reply-to is real or not.
+ */
+ if(header->env->reply_to || hdr_is_in_list("reply-to", header->custom))
+ for(pf = header->local; pf; pf = pf->next)
+ if(!strcmp(pf->name, "Reply-To")){
+ pf->writehdr = 1;
+ pf->localcopy = 1;
+ if(header->env->reply_to)
+ pf->textbuf = cpystr("Full");
+ else
+ pf->textbuf = cpystr("Empty");
+ }
+
+ /*
+ * Write the list of custom headers to the
+ * X-Our-Headers header so that we can recover the
+ * list in redraft.
+ */
+ sz = 0;
+ for(pf = header->custom; pf && pf->name; pf = pf->next)
+ sz += strlen(pf->name) + 1;
+
+ if(sz){
+ int i;
+ char *pstart, *pend;
+
+ for(i = 0, pf = header->local; i != N_OURHDRS; i++, pf = pf->next)
+ ;
+
+ pf->writehdr = 1;
+ pf->localcopy = 1;
+ pf->textbuf = pstart = pend = (char *) fs_get(sz + 1);
+ pf->text = &pf->textbuf;
+ pf->textbuf[sz] = '\0'; /* tie off overflow */
+ /* note: "pf" overloaded */
+ for(pf = header->custom; pf && pf->name; pf = pf->next){
+ int r = sz - (pend - pstart); /* remaining buffer */
+
+ if(r > 0 && r != sz){
+ r--;
+ *pend++ = ',';
+ }
+
+ sstrncpy(&pend, pf->name, r);
+ }
+ }
+
+ if(pine_rfc822_output(header, body, NULL, NULL) < 0
+ || write_fcc(folder, fcc_cntxt, lmc.so, NULL, "postponed message", NULL) < 0)
+ rv = -1;
+
+ so_give(&lmc.so);
+ }
+ else {
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ "Can't allocate internal storage: %s ",
+ error_description(errno));
+ rv = -1;
+ }
+
+ fs_give((void **) &folder);
+ return(rv);
+}
+
+
+int
+commence_fcc(char *fcc, CONTEXT_S **fcc_cntxt, int forced)
+{
+ if(fcc && *fcc){
+ lmc.all_written = lmc.text_written = 0;
+ lmc.text_only = F_ON(F_NO_FCC_ATTACH, ps_global) != 0;
+ return((lmc.so = open_fcc(fcc, fcc_cntxt, 0, NULL, NULL)) != NULL);
+ }
+ else
+ lmc.so = NULL;
+
+ return(TRUE);
+}
+
+
+int
+wrapup_fcc(char *fcc, CONTEXT_S *fcc_cntxt, METAENV *header, struct mail_bodystruct *body)
+{
+ int rv = TRUE;
+
+ if(lmc.so){
+ if(!header || pine_rfc822_output(header, body, NULL, NULL)){
+ char label[50];
+
+ strncpy(label, "Fcc", sizeof(label));
+ label[sizeof(label)-1] = '\0';
+ if(strcmp(fcc, ps_global->VAR_DEFAULT_FCC)){
+ snprintf(label + 3, sizeof(label)-3, " to %.40s", fcc);
+ label[sizeof(label)-1] = '\0';
+ }
+
+ rv = write_fcc(fcc,fcc_cntxt,lmc.so,NULL,NULL,NULL);
+ }
+ else{
+ rv = FALSE;
+ }
+
+ so_give(&lmc.so);
+ }
+
+ return(rv);
+}
+
+
+/*----------------------------------------------------------------------
+ Checks to make sure the fcc is available and can be opened
+
+Args: fcc -- the name of the fcc to create. It can't be NULL.
+ fcc_cntxt -- Returns the context the fcc is in.
+ force -- supress user option prompt
+
+Returns allocated storage object on success, NULL on failure
+ ----*/
+STORE_S *
+open_fcc(char *fcc, CONTEXT_S **fcc_cntxt, int force, char *err_prefix, char *err_suffix)
+{
+ int exists, ok = 0;
+
+ ps_global->mm_log_error = 0;
+
+ /*
+ * check for fcc's existance...
+ */
+ TIME_STAMP("open_fcc start", 1);
+ if(!is_absolute_path(fcc) && context_isambig(fcc)
+ && (strucmp(ps_global->inbox_name, fcc) != 0)){
+ int flip_dot = 0;
+
+ /*
+ * Don't want to preclude a user from Fcc'ing a .name'd folder
+ */
+ if(F_OFF(F_ENABLE_DOT_FOLDERS, ps_global)){
+ flip_dot = 1;
+ F_TURN_ON(F_ENABLE_DOT_FOLDERS, ps_global);
+ }
+
+ /*
+ * We only want to set the "context" if fcc is an ambiguous
+ * name. Otherwise, our "relativeness" rules for contexts
+ * (implemented in context.c) might cause the name to be
+ * interpreted in the wrong context...
+ */
+ if(!(*fcc_cntxt || (*fcc_cntxt = default_save_context(ps_global->context_list))))
+ *fcc_cntxt = ps_global->context_list;
+
+ build_folder_list(NULL, *fcc_cntxt, fcc, NULL, BFL_FLDRONLY);
+ if(folder_index(fcc, *fcc_cntxt, FI_FOLDER) < 0){
+ if(force
+ || (pith_opt_save_create_prompt
+ && (*pith_opt_save_create_prompt)(*fcc_cntxt, fcc, 0) > 0)){
+
+ ps_global->noshow_error = 1;
+
+ if(context_create(*fcc_cntxt, NULL, fcc))
+ ok++;
+
+ ps_global->noshow_error = 0;
+ }
+ else
+ ok--; /* declined! */
+ }
+ else
+ ok++; /* found! */
+
+ if(flip_dot)
+ F_TURN_OFF(F_ENABLE_DOT_FOLDERS, ps_global);
+
+ free_folder_list(*fcc_cntxt);
+ }
+ else if((exists = folder_exists(NULL, fcc)) != FEX_ERROR){
+ if(exists & (FEX_ISFILE | FEX_ISDIR)){
+ ok++;
+ }
+ else{
+ if(force
+ || (pith_opt_save_create_prompt
+ && (*pith_opt_save_create_prompt)(NULL, fcc, 0) > 0)){
+
+ ps_global->mm_log_error = 0;
+ ps_global->noshow_error = 1;
+
+ ok = pine_mail_create(NULL, fcc) != 0L;
+
+ ps_global->noshow_error = 0;
+ }
+ else
+ ok--; /* declined! */
+ }
+ }
+
+ TIME_STAMP("open_fcc done.", 1);
+ if(ok > 0){
+ return(so_get(FCC_SOURCE, NULL, WRITE_ACCESS));
+ }
+ else{
+ int l1, l2, l3, wid, w;
+ char *errstr, tmp[MAILTMPLEN];
+ char *s1, *s2;
+
+ if(ok == 0){
+ if(ps_global->mm_log_error){
+ s1 = err_prefix ? err_prefix : "Fcc Error: ";
+ s2 = err_suffix ? err_suffix : " Message NOT sent or copied.";
+
+ l1 = strlen(s1);
+ l2 = strlen(s2);
+ l3 = strlen(ps_global->c_client_error);
+ wid = (ps_global->ttyo && ps_global->ttyo->screen_cols > 0)
+ ? ps_global->ttyo->screen_cols : 80;
+ w = wid - l1 - l2 - 5;
+
+ snprintf(errstr = tmp, sizeof(tmp),
+ "%.99s\"%.*s%.99s\".%.99s",
+ s1,
+ (l3 > w) ? MAX(w-3,0) : MAX(w,0),
+ ps_global->c_client_error,
+ (l3 > w) ? "..." : "",
+ s2);
+ tmp[sizeof(tmp)-1] = '\0';
+
+ }
+ else
+ errstr = _("Fcc creation error. Message NOT sent or copied.");
+ }
+ else
+ errstr = _("Fcc creation rejected. Message NOT sent or copied.");
+
+ q_status_message(SM_ORDER | SM_DING, 3, 3, errstr);
+ }
+
+ return(NULL);
+}
+
+
+/*----------------------------------------------------------------------
+ mail_append() the fcc accumulated in temp_storage to proper destination
+
+Args: fcc -- name of folder
+ fcc_cntxt -- context for folder
+ temp_storage -- String of file where Fcc has been accumulated
+
+This copies the string of file to the actual folder, which might be IMAP
+or a disk folder. The temp_storage is freed after it is written.
+An error message is produced if this fails.
+ ----*/
+int
+write_fcc(char *fcc, CONTEXT_S *fcc_cntxt, STORE_S *tmp_storage,
+ MAILSTREAM *stream, char *label, char *flags)
+{
+ STRING msg;
+ CONTEXT_S *cntxt;
+ int we_cancel = 0;
+
+ if(!tmp_storage)
+ return(0);
+
+ TIME_STAMP("write_fcc start.", 1);
+ dprint((4, "Writing %s\n", (label && *label) ? label : ""));
+ if(label && *label){
+ char msg_buf[80];
+
+ strncpy(msg_buf, "Writing ", sizeof(msg_buf));
+ msg_buf[sizeof(msg_buf)-1] = '\0';
+ strncat(msg_buf, label, sizeof(msg_buf)-10);
+ we_cancel = busy_cue(msg_buf, NULL, 0);
+ }
+ else
+ we_cancel = busy_cue(NULL, NULL, 1);
+
+ so_seek(tmp_storage, 0L, 0);
+
+/*
+ * Before changing this note that these lines depend on the
+ * definition of FCC_SOURCE.
+ */
+ INIT(&msg, mail_string, (void *)so_text(tmp_storage),
+ strlen((char *)so_text(tmp_storage)));
+
+ cntxt = fcc_cntxt;
+
+ if(!context_append_full(cntxt, stream, fcc, flags, NULL, &msg)){
+ cancel_busy_cue(-1);
+ we_cancel = 0;
+
+ q_status_message1(SM_ORDER | SM_DING, 3, 5,
+ "Write to \"%s\" FAILED!!!", fcc);
+ dprint((1, "ERROR appending %s in \"%s\"",
+ fcc ? fcc : "?",
+ (cntxt && cntxt->context) ? cntxt->context : "NULL"));
+ return(0);
+ }
+
+ if(we_cancel)
+ cancel_busy_cue(label ? 0 : -1);
+
+ dprint((4, "done.\n"));
+ TIME_STAMP("write_fcc done.", 1);
+ return(1);
+}
+
+
+/*
+ * first_text_8bit - return TRUE if somewhere in the body 8BIT data's
+ * contained.
+ */
+BODY *
+first_text_8bit(struct mail_bodystruct *body)
+{
+ if(body->type == TYPEMULTIPART) /* advance to first contained part */
+ body = &body->nested.part->body;
+
+ return((body->type == TYPETEXT && body->encoding != ENC7BIT)
+ ? body : NULL);
+}
+
+
+/*
+ * Build and return the "From:" address for outbound messages from
+ * global data...
+ */
+ADDRESS *
+generate_from(void)
+{
+ ADDRESS *addr = mail_newaddr();
+ if(ps_global->VAR_PERSONAL_NAME){
+ addr->personal = cpystr(ps_global->VAR_PERSONAL_NAME);
+ removing_leading_and_trailing_white_space(addr->personal);
+ if(addr->personal[0] == '\0')
+ fs_give((void **)&addr->personal);
+ }
+
+ addr->mailbox = cpystr(ps_global->VAR_USER_ID);
+ addr->host = cpystr(ps_global->maildomain);
+ removing_leading_and_trailing_white_space(addr->mailbox);
+ removing_leading_and_trailing_white_space(addr->host);
+ return(addr);
+}
+
+
+/*
+ * set_mime_type_by_grope - sniff the given storage object to determine its
+ * type, subtype, encoding, and charset
+ *
+ * "Type" and "encoding" must be set before calling this routine.
+ * If "type" is set to something other than TYPEOTHER on entry,
+ * then that is the "type" we wish to use. Same for "encoding"
+ * using ENCOTHER instead of TYPEOTHER. Otherwise, we
+ * figure them out here. If "type" is already set, we also
+ * leave subtype alone. If not, we figure out subtype here.
+ * There is a chance that we will upgrade encoding to a "higher"
+ * level. For example, if it comes in as 7BIT we may change
+ * that to 8BIT if we find a From_ we want to escape.
+ * We may also set the charset attribute if the type is TEXT.
+ *
+ * NOTE: this is rather inefficient if the store object is a CharStar
+ * but the win is all types are handled the same
+ */
+void
+set_mime_type_by_grope(struct mail_bodystruct *body)
+{
+#define RBUFSZ (8193)
+ unsigned char *buf, *p, *bol;
+ register size_t n;
+ long max_line = 0L,
+ eight_bit_chars = 0L,
+ line_so_far = 0L,
+ len = 0L;
+ STORE_S *so = (STORE_S *)body->contents.text.data;
+ unsigned short new_encoding = ENCOTHER;
+ int we_cancel = 0;
+#ifdef ENCODE_FROMS
+ short froms = 0, dots = 0,
+ bmap = 0x1, dmap = 0x1;
+#endif
+
+ we_cancel = busy_cue(NULL, NULL, 1);
+
+ buf = (unsigned char *)fs_get(RBUFSZ);
+ so_seek(so, 0L, 0);
+
+ for(n = 0; n < RBUFSZ-1 && so_readc(&buf[n], so) != 0; n++)
+ ;
+
+ buf[n] = '\0';
+
+ if(n){ /* check first few bytes to look for magic numbers */
+ if(body->type == TYPEOTHER){
+ if(buf[0] == 'G' && buf[1] == 'I' && buf[2] == 'F'){
+ body->type = TYPEIMAGE;
+ body->subtype = cpystr("GIF");
+ }
+ else if((n > 9) && buf[0] == 0xFF && buf[1] == 0xD8
+ && buf[2] == 0xFF && buf[3] == 0xE0
+ && !strncmp((char *)&buf[6], "JFIF", 4)){
+ body->type = TYPEIMAGE;
+ body->subtype = cpystr("JPEG");
+ }
+ else if((buf[0] == 'M' && buf[1] == 'M')
+ || (buf[0] == 'I' && buf[1] == 'I')){
+ body->type = TYPEIMAGE;
+ body->subtype = cpystr("TIFF");
+ }
+ else if((buf[0] == '%' && buf[1] == '!')
+ || (buf[0] == '\004' && buf[1] == '%' && buf[2] == '!')){
+ body->type = TYPEAPPLICATION;
+ body->subtype = cpystr("PostScript");
+ }
+ else if(buf[0] == '%' && !strncmp((char *)buf+1, "PDF-", 4)){
+ body->type = TYPEAPPLICATION;
+ body->subtype = cpystr("PDF");
+ }
+ else if(buf[0] == '.' && !strncmp((char *)buf+1, "snd", 3)){
+ body->type = TYPEAUDIO;
+ body->subtype = cpystr("Basic");
+ }
+ else if((n > 3) && buf[0] == 0x00 && buf[1] == 0x05
+ && buf[2] == 0x16 && buf[3] == 0x00){
+ body->type = TYPEAPPLICATION;
+ body->subtype = cpystr("APPLEFILE");
+ }
+ else if((n > 3) && buf[0] == 0x50 && buf[1] == 0x4b
+ && buf[2] == 0x03 && buf[3] == 0x04){
+ body->type = TYPEAPPLICATION;
+ body->subtype = cpystr("ZIP");
+ }
+
+ /*
+ * if type was set above, but no encoding specified, go
+ * ahead and make it BASE64...
+ */
+ if(body->type != TYPEOTHER && body->encoding == ENCOTHER)
+ body->encoding = ENCBINARY;
+ }
+ }
+ else{
+ /* PROBLEM !!! */
+ if(body->type == TYPEOTHER){
+ body->type = TYPEAPPLICATION;
+ body->subtype = cpystr("octet-stream");
+ if(body->encoding == ENCOTHER)
+ body->encoding = ENCBINARY;
+ }
+ }
+
+ if (body->encoding == ENCOTHER || body->type == TYPEOTHER){
+#if defined(DOS) || defined(OS2) /* for binary file detection */
+ int lastchar = '\0';
+#define BREAKOUT 300 /* a value that a character can't be */
+#endif
+
+ p = bol = buf;
+ len = n;
+ while (n--){
+/* Some people don't like quoted-printable caused by leading Froms */
+#ifdef ENCODE_FROMS
+ Find_Froms(froms, dots, bmap, dmap, *p);
+#endif
+ if(*p == '\n'){
+ max_line = MAX(max_line, line_so_far + p - bol);
+ bol = NULL; /* clear beginning of line */
+ line_so_far = 0L; /* clear line count */
+#if defined(DOS) || defined(OS2)
+ /* LF with no CR!! */
+ if(lastchar != '\r') /* must be non-text data! */
+ lastchar = BREAKOUT;
+#endif
+ }
+ else if(*p & 0x80){
+ eight_bit_chars++;
+ }
+ else if(!*p){
+ /* NULL found. Unless we're told otherwise, must be binary */
+ if(body->type == TYPEOTHER){
+ body->type = TYPEAPPLICATION;
+ body->subtype = cpystr("octet-stream");
+ }
+
+ /*
+ * The "TYPETEXT" here handles the case that the NULL
+ * comes from imported text generated by some external
+ * editor that permits or inserts NULLS. Otherwise,
+ * assume it's a binary segment...
+ */
+ new_encoding = (body->type==TYPETEXT) ? ENC8BIT : ENCBINARY;
+
+ /*
+ * Since we've already set encoding, count this as a
+ * hi bit char and continue. The reason is that if this
+ * is text, there may be a high percentage of encoded
+ * characters, so base64 may get set below...
+ */
+ if(body->type == TYPETEXT)
+ eight_bit_chars++;
+ else
+ break;
+ }
+
+#if defined(DOS) || defined(OS2) /* for binary file detection */
+ if(lastchar != BREAKOUT)
+ lastchar = *p;
+#endif
+
+ /* read another buffer in */
+ if(n == 0){
+ if(bol)
+ line_so_far += p - bol;
+
+ for (n = 0; n < RBUFSZ-1 && so_readc(&buf[n], so) != 0; n++)
+ ;
+
+ len += n;
+ p = buf;
+ }
+ else
+ p++;
+
+ /*
+ * If there's no beginning-of-line pointer, then we must
+ * have seen an end-of-line. Set bol to the start of the
+ * new line...
+ */
+ if(!bol)
+ bol = p;
+
+#if defined(DOS) || defined(OS2) /* for binary file detection */
+ /* either a lone \r or lone \n indicate binary file */
+ if(lastchar == '\r' || lastchar == BREAKOUT){
+ if(lastchar == BREAKOUT || n == 0 || *p != '\n'){
+ if(body->type == TYPEOTHER){
+ body->type = TYPEAPPLICATION;
+ body->subtype = cpystr("octet-stream");
+ }
+
+ new_encoding = ENCBINARY;
+ break;
+ }
+ }
+#endif
+ }
+ }
+
+ /* stash away for later */
+ so_attr(so, "maxline", long2string(max_line));
+
+ if(body->encoding == ENCOTHER || body->type == TYPEOTHER){
+ /*
+ * Since the type or encoding aren't set yet, fall thru a
+ * series of tests to make sure an adequate type and
+ * encoding are set...
+ */
+
+ if(max_line >= 1000L){ /* 1000 comes from rfc821 */
+ if(body->type == TYPEOTHER){
+ /*
+ * Since the types not set, then we didn't find a NULL.
+ * If there's no NULL, then this is likely text. However,
+ * since we can't be *completely* sure, we set it to
+ * the generic type.
+ */
+ body->type = TYPEAPPLICATION;
+ body->subtype = cpystr("octet-stream");
+ }
+
+ if(new_encoding != ENCBINARY)
+ /*
+ * As with NULL handling, if we're told it's text,
+ * qp-encode it, else it gets base 64...
+ */
+ new_encoding = (body->type == TYPETEXT) ? ENC8BIT : ENCBINARY;
+ }
+
+ if(eight_bit_chars == 0L){
+ if(body->type == TYPEOTHER)
+ body->type = TYPETEXT;
+
+ if(new_encoding == ENCOTHER)
+ new_encoding = ENC7BIT; /* short lines, no 8 bit */
+ }
+ else if(len <= 3000L || (eight_bit_chars * 100L)/len < 30L){
+ /*
+ * The 30% threshold is based on qp encoded readability
+ * on non-MIME UA's.
+ */
+ if(body->type == TYPEOTHER)
+ body->type = TYPETEXT;
+
+ if(new_encoding != ENCBINARY)
+ new_encoding = ENC8BIT; /* short lines, < 30% 8 bit chars */
+ }
+ else{
+ if(body->type == TYPEOTHER){
+ body->type = TYPEAPPLICATION;
+ body->subtype = cpystr("octet-stream");
+ }
+
+ /*
+ * Apply maximal encoding regardless of previous
+ * setting. This segment's either not text, or is
+ * unlikely to be readable with > 30% of the
+ * text encoded anyway, so we might as well save space...
+ */
+ new_encoding = ENCBINARY; /* > 30% 8 bit chars */
+ }
+ }
+
+#ifdef ENCODE_FROMS
+ /* If there were From_'s at the beginning of a line or standalone dots */
+ if((froms || dots) && new_encoding != ENCBINARY)
+ new_encoding = ENC8BIT;
+#endif
+
+ /* Set the subtype */
+ if(body->subtype == NULL)
+ body->subtype = cpystr(rfc822_default_subtype(body->type));
+
+ if(body->encoding == ENCOTHER)
+ body->encoding = new_encoding;
+
+ fs_give((void **)&buf);
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+}
+
+
+/*
+ * Call this to set the charset of an attachment we have
+ * created. If the attachment contains any non-ascii characters
+ * then we'll set the charset to the passed in charset, otherwise
+ * we'll make it us-ascii.
+ */
+void
+set_charset_possibly_to_ascii(struct mail_bodystruct *body, char *charset)
+{
+ unsigned char c;
+ int can_be_ascii = 1;
+ STORE_S *so = (STORE_S *)body->contents.text.data;
+ int we_cancel = 0;
+
+ if(!body || body->type != TYPETEXT)
+ return;
+
+ we_cancel = busy_cue(NULL, NULL, 1);
+
+ so_seek(so, 0L, 0);
+
+ while(can_be_ascii && so_readc(&c, so))
+ if(!c || c & 0x80)
+ can_be_ascii--;
+
+ if(can_be_ascii)
+ set_parameter(&body->parameter, "charset", "US-ASCII");
+ else if(charset && *charset && strucmp(charset, "US-ASCII"))
+ set_parameter(&body->parameter, "charset", charset);
+ else{
+ /*
+ * Else we don't know. There are non ascii characters but we either
+ * don't have a charset to set it to or that charset is just us_ascii,
+ * which is impossible. So we label it unknown. An alternative would
+ * have been to strip the high bits instead and label it ascii.
+ */
+ set_parameter(&body->parameter, "charset", UNKNOWN_CHARSET);
+ }
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+}
+
+
+/*
+ * since encoding happens on the way out the door, this is basically
+ * just needed to handle TYPEMULTIPART
+ */
+void
+pine_encode_body (struct mail_bodystruct *body)
+{
+ PART *part;
+
+ dprint((4, "-- pine_encode_body: %d\n", body ? body->type : 0));
+ if (body) switch (body->type) {
+ char *freethis;
+
+ case TYPEMULTIPART: /* multi-part */
+ if(!(freethis=parameter_val(body->parameter, "BOUNDARY"))){
+ char tmp[MAILTMPLEN]; /* make cookie not in BASE64 or QUOTEPRINT*/
+
+ snprintf (tmp,sizeof(tmp),"%ld-%ld-%ld=:%ld",gethostid (),random (),(long) time (0),
+ (long) getpid ());
+ tmp[sizeof(tmp)-1] = '\0';
+ set_parameter(&body->parameter, "BOUNDARY", tmp);
+ }
+
+ if(freethis)
+ fs_give((void **) &freethis);
+
+ part = body->nested.part; /* encode body parts */
+ do pine_encode_body (&part->body);
+ while ((part = part->next) != NULL); /* until done */
+ break;
+
+ case TYPETEXT :
+ /*
+ * If the part is text we edited, then it is UTF-8.
+ * The user may be asking us to send it as something else
+ * or we may want to downconvert to a more-specific characterset.
+ * Mark it for conversion here so the right MIME header's written.
+ * Do conversion pine_rfc822_output_body.
+ * Attachments are left as is.
+ */
+ if(body->contents.text.data
+ && so_attr((STORE_S *) body->contents.text.data, "edited", NULL)){
+ char *charset, *posting_charset, *lp;
+
+ if(!((charset = parameter_val(body->parameter, "charset"))
+ && !strucmp(charset, UNKNOWN_CHARSET))
+ && (posting_charset = posting_characterset(body, charset, MsgBody))){
+
+ set_parameter(&body->parameter, "charset", posting_charset);
+
+ /*
+ * Fix iso-2022-jp encoding to ENC7BIT since it's escape based
+ * and doesn't use anything but ASCII characters.
+ * Why is it not ENC7BIT already? Because when we set the encoding
+ * in set_mime_type_by_grope we were groping through UTF-8 text
+ * not 2022 text. Not only that, but we didn't know at that point
+ * that it wouldn't stay UTF-8 when we sent it, which would require
+ * encoding.
+ */
+ if(!strucmp(posting_charset, "iso-2022-jp")
+ && (lp = so_attr((STORE_S *) body->contents.text.data, "maxline", NULL))
+ && strlen(lp) < 4)
+ body->encoding = ENC7BIT;
+ }
+
+ if(charset)
+ fs_give((void **)&charset);
+ }
+
+ break;
+
+/* case MESSAGE: */ /* here for documentation */
+ /* Encapsulated messages are always treated as text objects at this point.
+ This means that you must replace body->contents.msg with
+ body->contents.text, which probably involves copying
+ body->contents.msg.text to body->contents.text */
+ default: /* all else has some encoding */
+ /*
+ * but we'll delay encoding it until the message is on the way
+ * into the mail slot...
+ */
+ break;
+ }
+}
+
+
+/*
+ * pine_header_line - simple wrapper around c-client call to contain
+ * repeated code, and to write fcc if required.
+ */
+int
+pine_header_line(char *field, METAENV *header, char *text, soutr_t f, void *s,
+ int writehdr, int localcopy)
+{
+ int ret = 1;
+ int big = 10000;
+ char *value, *folded = NULL, *cs;
+ char *converted;
+
+ if(!text)
+ return 1;
+
+ converted = utf8_to_charset(text, cs = posting_characterset(text, NULL, HdrText), 0);
+
+ if(converted){
+ if(cs && !strucmp(cs, "us-ascii"))
+ value = converted;
+ else
+ value = encode_header_value(tmp_20k_buf, SIZEOF_20KBUF,
+ (unsigned char *) converted, cs,
+ encode_whole_header(field, header));
+
+ if(value && value == converted){ /* no encoding was done, have to fold */
+ int fold_by, len;
+ char *actual_field;
+
+ len = ((header && header->env && header->env->remail)
+ ? strlen("ReSent-") : 0) +
+ (field ? strlen(field) : 0) + 2;
+
+ actual_field = (char *)fs_get((len+1) * sizeof(char));
+ snprintf(actual_field, len+1, "%s%s: ",
+ (header && header->env && header->env->remail) ? "ReSent-" : "",
+ field ? field : "");
+ actual_field[len] = '\0';
+
+ /*
+ * We were folding everything except message-id, but that wasn't
+ * sufficient. Since 822 only allows folding where linear-white-space
+ * is allowed we'd need a smarter folder than "fold" to do it. So,
+ * instead of inventing that smarter folder (which would have to
+ * know 822 syntax)
+ *
+ * We could just alloc space and copy the actual_field followed by
+ * the value into it, but since that's what fold does anyway we'll
+ * waste some cpu time and use fold with a big fold parameter.
+ *
+ * We upped the references folding from 75 to 256 because we were
+ * encountering longer-than-75 message ids, and to break one line
+ * in references is to break them all.
+ */
+ if(field && !strucmp("Subject", field))
+ fold_by = 75;
+ else if(field && !strucmp("References", field))
+ fold_by = 256;
+ else
+ fold_by = big;
+
+ folded = fold(value, fold_by, big, actual_field, " ", FLD_CRLF);
+
+ if(actual_field)
+ fs_give((void **)&actual_field);
+ }
+ else if(value){ /* encoding was done */
+ RFC822BUFFER rbuf;
+ size_t ll;
+
+ /*
+ * rfc1522_encode already inserted continuation lines and did
+ * the necessary folding so we don't have to do it. Let
+ * rfc822_header_line add the trailing crlf and the resent- if
+ * necessary. The 20 could actually be a 12.
+ */
+ ll = strlen(field) + strlen(value) + 20;
+ folded = (char *) fs_get(ll * sizeof(char));
+ *folded = '\0';
+ rbuf.f = dummy_soutr;
+ rbuf.s = NULL;
+ rbuf.beg = folded;
+ rbuf.cur = folded;
+ rbuf.end = folded+ll-1;
+ rfc822_output_header_line(&rbuf, field,
+ (header && header->env && header->env->remail) ? LONGT : 0L, value);
+ *rbuf.cur = '\0';
+ }
+
+ if(value && folded){
+ if(writehdr && f)
+ ret = (*f)(s, folded);
+
+ if(ret && localcopy && lmc.so && !lmc.all_written)
+ ret = so_puts(lmc.so, folded);
+ }
+
+ if(folded)
+ fs_give((void **)&folded);
+
+ if(converted && converted != text)
+ fs_give((void **) &converted);
+ }
+ else
+ ret = 0;
+
+ return(ret);
+}
+
+
+/*
+ * Do appropriate encoding of text header lines.
+ * For some field types (those that consist of 822 *text) we just encode
+ * the whole thing. For structured fields we encode only within comments
+ * if possible.
+ *
+ * Args d -- Destination buffer if needed. (tmp_20k_buf)
+ * s -- Source string.
+ * charset -- Charset to encode with.
+ * encode_all -- If set, encode the whole string. If not, try to encode
+ * only within comments if possible.
+ *
+ * Returns S is returned if no encoding is done. D is returned if encoding
+ * was needed.
+ */
+char *
+encode_header_value(char *d, size_t dlen, unsigned char *s, char *charset, int encode_all)
+{
+ char *p, *q, *r, *start_of_comment = NULL, *value = NULL;
+ int in_comment = 0;
+
+ if(!s)
+ return((char *)s);
+
+ if(dlen < SIZEOF_20KBUF)
+ panic("bad call to encode_header_value");
+
+ if(!encode_all){
+ /*
+ * We don't have to worry about keeping track of quoted-strings because
+ * none of these fields which aren't addresses contain quoted-strings.
+ * We do keep track of escaped parens inside of comments and comment
+ * nesting.
+ */
+ p = d+7000;
+ for(q = (char *)s; *q; q++){
+ switch(*q){
+ case LPAREN:
+ if(in_comment++ == 0)
+ start_of_comment = q;
+
+ break;
+
+ case RPAREN:
+ if(--in_comment == 0){
+ /* encode the comment, excluding the outer parens */
+ if(p-d < dlen-1)
+ *p++ = LPAREN;
+
+ *q = '\0';
+ r = rfc1522_encode(d+14000, dlen-14000,
+ (unsigned char *)start_of_comment+1,
+ charset);
+ if(r != start_of_comment+1)
+ value = d+7000; /* some encoding was done */
+
+ start_of_comment = NULL;
+ if(r)
+ sstrncpy(&p, r, dlen-1-(p-d));
+
+ *q = RPAREN;
+ if(p-d < dlen-1)
+ *p++ = *q;
+ }
+ else if(in_comment < 0){
+ in_comment = 0;
+ if(p-d < dlen-1)
+ *p++ = *q;
+ }
+
+ break;
+
+ case BSLASH:
+ if(!in_comment && *(q+1)){
+ if(p-d < dlen-2){
+ *p++ = *q++;
+ *p++ = *q;
+ }
+ }
+
+ break;
+
+ default:
+ if(!in_comment && p-d < dlen-1)
+ *p++ = *q;
+
+ break;
+ }
+ }
+
+ if(value){
+ /* Unterminated comment (wasn't really a comment) */
+ if(start_of_comment)
+ sstrncpy(&p, start_of_comment, dlen-1-(p-d));
+
+ *p = '\0';
+ }
+ }
+
+ /*
+ * We have to check if there is anything that needs to be encoded that
+ * wasn't in a comment. If there is, we'd better just start over and
+ * encode the whole thing. So, if no encoding has been done within
+ * comments, or if encoding is needed both within and outside of
+ * comments, then we encode the whole thing. Otherwise, go with
+ * the version that has only comments encoded.
+ */
+ if(!value || rfc1522_encode(d, dlen,
+ (unsigned char *)value, charset) != value)
+ return(rfc1522_encode(d, dlen, s, charset));
+ else{
+ strncpy(d, value, dlen-1);
+ d[dlen-1] = '\0';
+ return(d);
+ }
+}
+
+
+/*
+ * pine_address_line - write a header field containing addresses,
+ * one by one (so there's no buffer limit), and
+ * wrapping where necessary.
+ * Note: we use c-client functions to properly build the text string,
+ * but have to screw around with pointers to fool c-client functions
+ * into not blatting all the text into a single buffer. Yeah, I know.
+ */
+int
+pine_address_line(char *field, METAENV *header, struct mail_address *alist,
+ soutr_t f, void *s, int writehdr, int localcopy)
+{
+ char tmp[MAX_SINGLE_ADDR], *tmpptr = NULL;
+ size_t alloced = 0, sz;
+ char *delim, *ptmp, *mtmp, buftmp[MAILTMPLEN];
+ char *converted, *cs;
+ ADDRESS *atmp;
+ int i, count;
+ int in_group = 0, was_start_of_group = 0, fix_lcc = 0, failed = 0;
+ RFC822BUFFER rbuf;
+ static char comma[] = ", ";
+ static char end_group[] = ";";
+#define no_comma (&comma[1])
+
+ if(!alist) /* nothing in field! */
+ return(1);
+
+ if(!alist->host && alist->mailbox){ /* c-client group convention */
+ in_group++;
+ was_start_of_group++;
+ /* encode mailbox of group */
+ mtmp = alist->mailbox;
+ if(mtmp){
+ snprintf(buftmp, sizeof(buftmp), "%s", mtmp);
+ buftmp[sizeof(buftmp)-1] = '\0';
+ converted = utf8_to_charset(buftmp, cs = posting_characterset(buftmp, NULL, HdrText), 0);
+ if(converted){
+ alist->mailbox = cpystr(rfc1522_encode(tmp_20k_buf, SIZEOF_20KBUF,
+ (unsigned char *) converted, cs));
+ if(converted && converted != buftmp)
+ fs_give((void **) &converted);
+ }
+ else{
+ failed++;
+ goto bail_out;
+ }
+ }
+ }
+ else
+ mtmp = NULL;
+
+ ptmp = alist->personal; /* remember personal name */
+ /* make sure personal name is encoded */
+ if(ptmp){
+ snprintf(buftmp, sizeof(buftmp), "%s", ptmp);
+ buftmp[sizeof(buftmp)-1] = '\0';
+ converted = utf8_to_charset(buftmp, cs = posting_characterset(buftmp, NULL, HdrText), 0);
+ if(converted){
+ alist->personal = cpystr(rfc1522_encode(tmp_20k_buf, SIZEOF_20KBUF,
+ (unsigned char *) converted, cs));
+ if(converted && converted != buftmp)
+ fs_give((void **) &converted);
+ }
+ else{
+ failed++;
+ goto bail_out;
+ }
+ }
+
+ atmp = alist->next;
+ alist->next = NULL; /* digest only first address! */
+
+ /* use automatic buffer unless it isn't big enough */
+ if((alloced = est_size(alist)) > sizeof(tmp)){
+ tmpptr = (char *)fs_get(alloced);
+ sz = alloced;
+ }
+ else{
+ tmpptr = tmp;
+ sz = sizeof(tmp);
+ }
+
+ rbuf.f = dummy_soutr;
+ rbuf.s = NULL;
+ rbuf.beg = tmpptr;
+ rbuf.cur = tmpptr;
+ rbuf.end = tmpptr+sz-1;
+ rfc822_output_address_line(&rbuf, field,
+ (header && header->env && header->env->remail) ? LONGT : 0L, alist, NULL);
+ *rbuf.cur = '\0';
+
+ alist->next = atmp; /* restore pointer to next addr */
+
+ if(alist->personal && alist->personal != ptmp)
+ fs_give((void **) &alist->personal);
+
+ alist->personal = ptmp; /* in case it changed, restore name */
+
+ if(mtmp){
+ if(alist->mailbox && alist->mailbox != mtmp)
+ fs_give((void **) &alist->mailbox);
+
+ alist->mailbox = mtmp;
+ }
+
+ if((count = strlen(tmpptr)) > 2){ /* back over CRLF */
+ count -= 2;
+ tmpptr[count] = '\0';
+ }
+
+ /*
+ * If there is no sending_stream and we are writing the Lcc header,
+ * then we are piping it to sendmail -t which expects it to be a bcc,
+ * not lcc.
+ *
+ * When we write it to the fcc or postponed (the lmc.so),
+ * we want it to be lcc, not bcc, so we put it back.
+ */
+ if(!sending_stream && writehdr && struncmp("lcc:", tmpptr, 4) == 0)
+ fix_lcc = 1;
+
+ if(writehdr && f && *tmpptr){
+ if(fix_lcc)
+ tmpptr[0] = 'b';
+
+ failed = !(*f)(s, tmpptr);
+ if(fix_lcc)
+ tmpptr[0] = 'L';
+
+ if(failed)
+ goto bail_out;
+ }
+
+ if(localcopy && lmc.so &&
+ !lmc.all_written && *tmpptr && !so_puts(lmc.so, tmpptr))
+ goto bail_out;
+
+ for(alist = atmp; alist; alist = alist->next){
+ delim = comma;
+ /* account for c-client's representation of group names */
+ if(in_group){
+ if(!alist->host){ /* end of group */
+ in_group = 0;
+ was_start_of_group = 0;
+ /*
+ * Rfc822_write_address no longer writes out the end of group
+ * unless the whole group address is passed to it, so we do
+ * it ourselves.
+ */
+ delim = end_group;
+ }
+ else if(!localcopy || !lmc.so || lmc.all_written)
+ continue;
+ }
+ /* start of new group, print phrase below */
+ else if(!alist->host && alist->mailbox){
+ in_group++;
+ was_start_of_group++;
+ }
+
+ /* no comma before first address in group syntax */
+ if(was_start_of_group && alist->host){
+ delim = no_comma;
+ was_start_of_group = 0;
+ }
+
+ /* write delimiter */
+ if(((!in_group||was_start_of_group) && writehdr && f && !(*f)(s, delim))
+ || (localcopy && lmc.so && !lmc.all_written
+ && !so_puts(lmc.so, delim)))
+ goto bail_out;
+
+ ptmp = alist->personal; /* remember personal name */
+ snprintf(buftmp, sizeof(buftmp), "%.200s", ptmp ? ptmp : "");
+ buftmp[sizeof(buftmp)-1] = '\0';
+ converted = utf8_to_charset(buftmp, cs = posting_characterset(buftmp, NULL, HdrText), 0);
+ if(converted){
+ alist->personal = cpystr(rfc1522_encode(tmp_20k_buf, SIZEOF_20KBUF,
+ (unsigned char *) converted, cs));
+ if(converted && converted != buftmp)
+ fs_give((void **) &converted);
+ }
+ else{
+ failed++;
+ goto bail_out;
+ }
+
+ atmp = alist->next;
+ alist->next = NULL; /* tie off linked list */
+ if((i = est_size(alist)) > MAX(sizeof(tmp), alloced)){
+ alloced = i;
+ sz = alloced;
+ fs_resize((void **)&tmpptr, alloced);
+ }
+
+ *tmpptr = '\0';
+ /* make sure we don't write out group end with rfc822_write_address */
+ if(alist->host || alist->mailbox){
+ rbuf.f = dummy_soutr;
+ rbuf.s = NULL;
+ rbuf.beg = tmpptr;
+ rbuf.cur = tmpptr;
+ rbuf.end = tmpptr+sz-1;
+ rfc822_output_address_list(&rbuf, alist, 0L, NULL);
+ *rbuf.cur = '\0';
+ }
+
+ alist->next = atmp; /* restore next pointer */
+
+ if(alist->personal && alist->personal != ptmp)
+ fs_give((void **) &alist->personal);
+
+ alist->personal = ptmp; /* in case it changed, restore name */
+
+ /*
+ * BUG
+ * With group syntax addresses we no longer have two identical
+ * streams of output. Instead, for the fcc/postpone copy we include
+ * all of the addresses inside the :; of the group, and for the
+ * mail we're sending we don't include them. That means we aren't
+ * correctly keeping track of the column to wrap in, below. That is,
+ * we are keeping track of the fcc copy but we aren't keeping track
+ * of the regular copy. It could result in too long or too short
+ * lines. Should almost never come up since group addresses are almost
+ * never followed by other addresses in the same header, and even
+ * when they are, you have to go out of your way to get the headers
+ * messed up.
+ */
+ if(count + 2 + (i = strlen(tmpptr)) > 78){ /* wrap long lines... */
+ count = i + 4;
+ if((!in_group && writehdr && f && !(*f)(s, "\015\012 "))
+ || (localcopy && lmc.so && !lmc.all_written &&
+ !so_puts(lmc.so, "\015\012 ")))
+ goto bail_out;
+ }
+ else
+ count += i + 2;
+
+ if(((!in_group || was_start_of_group)
+ && writehdr && *tmpptr && f && !(*f)(s, tmpptr))
+ || (localcopy && lmc.so && !lmc.all_written
+ && *tmpptr && !so_puts(lmc.so, tmpptr)))
+ goto bail_out;
+ }
+
+bail_out:
+ if(tmpptr && tmpptr != tmp)
+ fs_give((void **)&tmpptr);
+
+ if(failed)
+ return(0);
+
+ return((writehdr && f ? (*f)(s, "\015\012") : 1)
+ && ((localcopy && lmc.so
+ && !lmc.all_written) ? so_puts(lmc.so, "\015\012") : 1));
+}
+
+
+/*
+ * mutated pine version of c-client's rfc822_header() function.
+ * changed to call pine-wrapped header and address functions
+ * so we don't have to limit the header size to a fixed buffer.
+ * This function also calls pine's body_header write function
+ * because encoding is delayed until output_body() is called.
+ */
+long
+pine_rfc822_header(METAENV *header, struct mail_bodystruct *body, soutr_t f, void *s)
+{
+ PINEFIELD *pf;
+ int j;
+
+ if(header->env->remail){ /* if remailing */
+ long i = strlen (header->env->remail);
+ if(i > 4 && header->env->remail[i-4] == '\015')
+ header->env->remail[i-2] = '\0'; /* flush extra blank line */
+
+ if((f && !(*f)(s, header->env->remail))
+ || (lmc.so && !lmc.all_written
+ && !so_puts(lmc.so, header->env->remail)))
+ return(0L); /* start with remail header */
+ }
+
+ j = 0;
+ for(pf = header->sending_order[j]; pf; pf = header->sending_order[++j]){
+ switch(pf->type){
+ /*
+ * Warning: This is confusing. The 2nd to last argument used to
+ * be just pf->writehdr. We want Bcc lines to be written out
+ * if we are handing off to a sendmail temp file but not if we
+ * are talking smtp, so bcc's writehdr is set to 0 and
+ * pine_address_line was sending if writehdr OR !sending_stream.
+ * That works as long as we want to write everything when
+ * !sending_stream (an mta handoff to sendmail). But then we
+ * added the undisclosed recipients line which should only get
+ * written if writehdr is set, and not when we pass to a
+ * sendmail temp file. So pine_address_line has been changed
+ * so it bases its decision solely on the writehdr passed to it,
+ * and the logic that worries about Bcc and sending_stream
+ * was moved up to the caller (here) to decide when to set it.
+ *
+ * So we have:
+ * undisclosed recipients:; This will just be written
+ * if writehdr was set and not
+ * otherwise, nothing magical.
+ *** We may want to change this, because sendmail -t doesn't handle
+ *** the empty group syntax well unless it has been configured to
+ *** do so. It isn't configured by default, or in any of the
+ *** sendmail v8 configs. So we may want to not write this line
+ *** if we're doing an mta_handoff (!sending_stream).
+ *
+ * !sending_stream (which means a handoff to a sendmail -t)
+ * bcc or lcc both set the arg so they'll get written
+ * (There is also Lcc hocus pocus in pine_address_line
+ * which converts the Lcc: to Bcc: for sendmail
+ * processing.)
+ * sending_stream (which means an smtp handoff)
+ * bcc and lcc will never have writehdr set, so
+ * will never be written (They both do have rcptto set,
+ * so they both do cause RCPT TO commands.)
+ *
+ * The localcopy is independent of sending_stream and is just
+ * written if it is set for all of these.
+ */
+ case Address:
+ if(!pine_address_line(pf->name,
+ header,
+ pf->addr ? *pf->addr : NULL,
+ f,
+ s,
+ (!strucmp("bcc",pf->name ? pf->name : "")
+ || !strucmp("Lcc",pf->name ? pf->name : ""))
+ ? !sending_stream
+ : pf->writehdr,
+ pf->localcopy))
+ return(0L);
+
+ break;
+
+ case Fcc:
+ case FreeText:
+ case Subject:
+ if(!pine_header_line(pf->name, header,
+ pf->text ? *pf->text : NULL,
+ f, s, pf->writehdr, pf->localcopy))
+ return(0L);
+
+ break;
+
+ default:
+ q_status_message1(SM_ORDER,3,7,"Unknown header type: %.200s",
+ pf->name);
+ break;
+ }
+ }
+
+
+#if (defined(DOS) || defined(OS2)) && !defined(NOAUTH)
+ /*
+ * Add comforting "X-" header line indicating what sort of
+ * authenticity the receiver can expect...
+ */
+ if(F_OFF(F_DISABLE_SENDER, ps_global)){
+ NETMBX netmbox;
+ char sstring[MAILTMPLEN], *label; /* place to write */
+ MAILSTREAM *m;
+ int i, anonymous = 1;
+
+ for(i = 0; anonymous && i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ if(m && sp_flagged(m, SP_LOCKED)
+ && mail_valid_net_parse(m->mailbox, &netmbox)
+ && !netmbox.anoflag)
+ anonymous = 0;
+ }
+
+ if(!anonymous){
+ char last_char = netmbox.host[strlen(netmbox.host) - 1],
+ *user = (*netmbox.user)
+ ? netmbox.user
+ : cached_user_name(netmbox.mailbox);
+ snprintf(sstring, sizeof(sstring), "%.300s@%s%.300s%s", user ? user : "NULL",
+ isdigit((unsigned char)last_char) ? "[" : "",
+ netmbox.host,
+ isdigit((unsigned char) last_char) ? "]" : "");
+ sstring[sizeof(sstring)-1] = '\0';
+ label = "X-X-Sender"; /* Jeez. */
+ if(F_ON(F_USE_SENDER_NOT_X,ps_global))
+ label += 4;
+ }
+ else{
+ strncpy(sstring,"UNAuthenticated Sender", sizeof(sstring));
+ sstring[sizeof(sstring)-1] = '\0';
+ label = "X-Warning";
+ }
+
+ if(!pine_header_line(label, header, sstring, f, s, 1, 1))
+ return(0L);
+ }
+#endif
+
+ if(body && !header->env->remail){ /* not if remail or no body */
+ if((f && !(*f)(s, MIME_VER))
+ || (lmc.so && !lmc.all_written && !so_puts(lmc.so, MIME_VER))
+ || !pine_write_body_header(body, f, s))
+ return(0L);
+ }
+ else{ /* write terminating newline */
+ if((f && !(*f)(s, "\015\012"))
+ || (lmc.so && !lmc.all_written && !so_puts(lmc.so, "\015\012")))
+ return(0L);
+ }
+
+ return(1L);
+}
+
+
+/*
+ * pine_rfc822_output - pine's version of c-client call. Necessary here
+ * since we're not using its structures as intended!
+ */
+long
+pine_rfc822_output(METAENV *header, struct mail_bodystruct *body, soutr_t f, void *s)
+{
+ int we_cancel = 0;
+ long retval;
+
+ dprint((4, "-- pine_rfc822_output\n"));
+
+ we_cancel = busy_cue(NULL, NULL, 1);
+ pine_encode_body(body); /* encode body as necessary */
+ /* build and output RFC822 header, output body */
+ retval = pine_rfc822_header(header, body, f, s)
+ && (body ? pine_rfc822_output_body(body, f, s) : 1L);
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ return(retval);
+}
+
+
+/*
+ * post_rfc822_output - cloak for pine's 822 output routine. Since
+ * we can't pass opaque envelope thru c-client posting
+ * logic, we need to wrap the real output inside
+ * something that c-client knows how to call.
+ */
+long
+post_rfc822_output(char *tmp,
+ ENVELOPE *env,
+ struct mail_bodystruct *body,
+ soutr_t f,
+ void *s,
+ long int ok8bit)
+{
+ return(pine_rfc822_output(send_header, body, f, s));
+}
+
+
+/*
+ * posting_characterset- determine what transliteration is reasonable
+ * for posting the given non-ascii messsage data.
+ *
+ * preferred_charset is the charset the original data was labeled in.
+ * If we can keep that we do.
+ *
+ * Returns: always returns the preferred character set.
+ */
+char *
+posting_characterset(void *data, char *preferred_charset, MsgPart mp)
+{
+ unsigned long *charsetmap = NULL;
+ unsigned long validbitmap;
+ static char *ascii = "US-ASCII";
+ static char *utf8 = "UTF-8";
+ int notcjk = 0;
+
+ if(!ps_global->post_utf8){
+ validbitmap = 0;
+
+ if(mp == HdrText){
+ char *text = NULL;
+ UCS *ucs = NULL, *ucsp;
+
+ text = (char *) data;
+
+ /* convert text in header to UCS characters */
+ if(text)
+ ucsp = ucs = utf8_to_ucs4_cpystr(text);
+
+ if(!(ucs && *ucs))
+ return(ascii);
+
+ /*
+ * After the while loop is done the validbitmap has
+ * a 1 bit for all the character sets that can
+ * represent all of the characters of this header.
+ */
+ charsetmap = init_charsetchecker(preferred_charset);
+
+ if(!charsetmap)
+ return(utf8);
+
+ validbitmap = ~0;
+ while((validbitmap & ~0x1) && (*ucsp)){
+ if(*ucsp > 0xffff){
+ fs_give((void **) &ucs);
+ return(utf8);
+ }
+
+ validbitmap &= charsetmap[(unsigned long) (*ucsp++)];
+ }
+
+ fs_give((void **) &ucs);
+
+ notcjk = validbitmap & 0x1;
+ validbitmap &= ~0x1;
+
+ if(!validbitmap)
+ return(utf8);
+ }
+ else{
+ struct mail_bodystruct *body = NULL;
+ STORE_S *the_text = NULL;
+ int outchars;
+ unsigned char c;
+ UCS ucs;
+ CBUF_S cbuf;
+
+ cbuf.cbuf[0] = '\0';
+ cbuf.cbufp = cbuf.cbuf;
+ cbuf.cbufend = cbuf.cbuf;
+
+ body = (struct mail_bodystruct *) data;
+
+ if(body && body->type == TYPEMULTIPART)
+ body = &body->nested.part->body;
+
+ if(body && body->type == TYPETEXT)
+ the_text = (STORE_S *) body->contents.text.data;
+
+ if(!the_text)
+ return(ascii);
+
+ so_seek(the_text, 0L, 0); /* rewind */
+
+ charsetmap = init_charsetchecker(preferred_charset);
+
+ if(!charsetmap)
+ return(utf8);
+
+ validbitmap = ~0;
+
+ /*
+ * Read a stream of UTF-8 characters from the_text
+ * and convert them to UCS-4 characters for the translatable
+ * test.
+ */
+ while((validbitmap & ~0x1) && so_readc(&c, the_text)){
+ if((outchars = utf8_to_ucs4_oneatatime(c, &cbuf, &ucs, NULL)) > 0){
+ /* got a ucs character */
+ if(ucs > 0xffff)
+ return(utf8);
+
+ validbitmap &= charsetmap[(unsigned long) ucs];
+ }
+ }
+
+ notcjk = validbitmap & 0x1;
+ validbitmap &= ~0x1;
+
+ if(!validbitmap)
+ return(utf8);
+ }
+
+ /* user chooses something other than UTF-8 */
+ if(strucmp(ps_global->posting_charmap, utf8)){
+ /*
+ * If we're to post in other than UTF-8, and it can be
+ * transliterated without losing fidelity, do it.
+ * Else, use UTF-8.
+ */
+
+ /* if ascii works, always use that */
+ if(representable_in_charset(validbitmap, ascii))
+ return(ascii);
+
+ /* does the user's posting character set work? */
+ if(representable_in_charset(validbitmap, ps_global->posting_charmap))
+ return(ps_global->posting_charmap);
+
+ /* this is the charset the message we are replying to was in */
+ if(preferred_charset
+ && strucmp(preferred_charset, ascii)
+ && representable_in_charset(validbitmap, preferred_charset))
+ return(preferred_charset);
+
+ /* else, use UTF-8 */
+
+ }
+ /* user chooses nothing, going with the default */
+ else if(ps_global->vars[V_POST_CHAR_SET].main_user_val.p == NULL
+ && ps_global->vars[V_POST_CHAR_SET].post_user_val.p == NULL
+ && ps_global->vars[V_POST_CHAR_SET].fixed_val.p == NULL){
+ char *most_preferred;
+
+ /*
+ * In this case the user didn't specify a posting character set
+ * and we will choose the most-specific one from our list.
+ */
+
+ /* ascii is best */
+ if(representable_in_charset(validbitmap, ascii))
+ return(ascii);
+
+ /* Can we keep the original from the message we're replying to? */
+ if(preferred_charset
+ && strucmp(preferred_charset, ascii)
+ && representable_in_charset(validbitmap, preferred_charset))
+ return(preferred_charset);
+
+ /* choose the best of the rest */
+ most_preferred = most_preferred_charset(validbitmap);
+ if(!most_preferred)
+ return(utf8);
+
+ /*
+ * If the text we're labeling contains something like
+ * smart quotes but no CJK characters, then instead of
+ * labeling it as ISO-2022-JP we want to use UTF-8.
+ */
+ if(notcjk){
+ const CHARSET *cs;
+
+ cs = utf8_charset(most_preferred);
+ if(!cs
+ || cs->script == SC_CHINESE_SIMPLIFIED
+ || cs->script == SC_CHINESE_TRADITIONAL
+ || cs->script == SC_JAPANESE
+ || cs->script == SC_KOREAN)
+ return(utf8);
+ }
+
+ return(most_preferred);
+ }
+ /* user explicitly chooses UTF-8 */
+ else{
+ /* if ascii works, always use that */
+ if(representable_in_charset(validbitmap, ascii))
+ return(ascii);
+
+ /* else, use UTF-8 */
+
+ }
+ }
+
+ return(utf8);
+}
+
+
+static char **charsetlist = NULL;
+static int items_in_charsetlist = 0;
+static unsigned long *charsetmap = NULL;
+
+static char *downgrades[] = {
+ "US-ASCII",
+ "ISO-8859-15",
+ "ISO-8859-1",
+ "ISO-8859-2",
+ "VISCII",
+ "KOI8-R",
+ "KOI8-U",
+ "ISO-8859-7",
+ "ISO-8859-6",
+ "ISO-8859-8",
+ "TIS-620",
+ "ISO-2022-JP",
+ "GB2312",
+ "BIG5",
+ "EUC-KR"
+};
+
+
+unsigned long *
+init_charsetchecker(char *preferred_charset)
+{
+ int i, count = 0, reset = 0;
+ char *ascii = "US-ASCII";
+ char *utf8 = "UTF-8";
+
+ /*
+ * When user doesn't set a posting character set posting_charmap ends up
+ * set to UTF-8. That also happens if user sets UTF-8 explicitly.
+ * That's where the strange set of if-else's come from.
+ */
+
+ /* user chooses something other than UTF-8 */
+ if(strucmp(ps_global->posting_charmap, utf8)){
+ count++; /* US-ASCII */
+ if(items_in_charsetlist < 1 || strucmp(charsetlist[0], ascii))
+ reset++;
+
+ /* if posting_charmap is valid, include it in list */
+ if(ps_global->posting_charmap && ps_global->posting_charmap[0]
+ && strucmp(ps_global->posting_charmap, ascii)
+ && strucmp(ps_global->posting_charmap, utf8)
+ && utf8_charset(ps_global->posting_charmap)){
+ count++;
+ if(!reset
+ && (items_in_charsetlist < count
+ || strucmp(charsetlist[count-1], ps_global->posting_charmap)))
+ reset++;
+ }
+
+ if(preferred_charset && preferred_charset[0]
+ && strucmp(preferred_charset, ascii)
+ && strucmp(preferred_charset, utf8)
+ && (count < 2 || strucmp(preferred_charset, ps_global->posting_charmap))){
+ count++;
+ if(!reset
+ && (items_in_charsetlist < count
+ || strucmp(charsetlist[count-1], preferred_charset)))
+ reset++;
+ }
+
+ if(items_in_charsetlist != count)
+ reset++;
+
+ if(reset){
+ if(charsetlist)
+ free_list_array(&charsetlist);
+
+ items_in_charsetlist = count;
+ charsetlist = (char **) fs_get((count + 1) * sizeof(char *));
+
+ i = 0;
+ charsetlist[i++] = cpystr(ascii);
+
+ if(ps_global->posting_charmap && ps_global->posting_charmap[0]
+ && strucmp(ps_global->posting_charmap, ascii)
+ && strucmp(ps_global->posting_charmap, utf8)
+ && utf8_charset(ps_global->posting_charmap))
+ charsetlist[i++] = cpystr(ps_global->posting_charmap);
+
+ if(preferred_charset && preferred_charset[0]
+ && strucmp(preferred_charset, ascii)
+ && strucmp(preferred_charset, utf8)
+ && (i < 2 || strucmp(preferred_charset, ps_global->posting_charmap)))
+ charsetlist[i++] = cpystr(preferred_charset);
+
+ charsetlist[i] = NULL;
+ }
+ }
+ /* user chooses nothing, going with the default */
+ else if(ps_global->vars[V_POST_CHAR_SET].main_user_val.p == NULL
+ && ps_global->vars[V_POST_CHAR_SET].post_user_val.p == NULL
+ && ps_global->vars[V_POST_CHAR_SET].fixed_val.p == NULL){
+ int add_preferred = 0;
+
+ /* does preferred_charset have to be added to the list? */
+ if(preferred_charset && preferred_charset[0] && strucmp(preferred_charset, utf8)){
+ add_preferred = 1;
+ for(i = 0; add_preferred && i < sizeof(downgrades)/sizeof(downgrades[0]); i++)
+ if(!strucmp(downgrades[i], preferred_charset))
+ add_preferred = 0;
+ }
+
+ if(add_preferred){
+ /* existing list is right size already */
+ if(items_in_charsetlist == sizeof(downgrades)/sizeof(downgrades[0]) + 1){
+ /* just check to see if last list item is the preferred_charset */
+ if(strucmp(preferred_charset, charsetlist[items_in_charsetlist-1])){
+ /* no, fix it */
+ reset++;
+ fs_give((void **) &charsetlist[items_in_charsetlist-1]);
+ charsetlist[items_in_charsetlist-1] = cpystr(preferred_charset);
+ }
+ }
+ else{
+ reset++;
+ if(charsetlist)
+ free_list_array(&charsetlist);
+
+ count = sizeof(downgrades)/sizeof(downgrades[0]) + 1;
+ items_in_charsetlist = count;
+ charsetlist = (char **) fs_get((count + 1) * sizeof(char *));
+ for(i = 0; i < sizeof(downgrades)/sizeof(downgrades[0]); i++)
+ charsetlist[i] = cpystr(downgrades[i]);
+
+ charsetlist[i++] = cpystr(preferred_charset);
+ charsetlist[i] = NULL;
+ }
+ }
+ else{
+ /* if list is same size as downgrades, consider it good */
+ if(items_in_charsetlist != sizeof(downgrades)/sizeof(downgrades[0]))
+ reset++;
+
+ if(reset){
+ if(charsetlist)
+ free_list_array(&charsetlist);
+
+ count = sizeof(downgrades)/sizeof(downgrades[0]);
+ items_in_charsetlist = count;
+ charsetlist = (char **) fs_get((count + 1) * sizeof(char *));
+ for(i = 0; i < sizeof(downgrades)/sizeof(downgrades[0]); i++)
+ charsetlist[i] = cpystr(downgrades[i]);
+
+ charsetlist[i] = NULL;
+ }
+ }
+ }
+ /* user explicitly chooses UTF-8 */
+ else{
+ /* include possibility of ascii even if they explicitly ask for UTF-8 */
+ count++; /* US-ASCII */
+ if(items_in_charsetlist < 1 || strucmp(charsetlist[0], ascii))
+ reset++;
+
+ if(items_in_charsetlist != count)
+ reset++;
+
+ if(reset){
+ if(charsetlist)
+ free_list_array(&charsetlist);
+
+ /* the list is just ascii and nothing else */
+ items_in_charsetlist = count;
+ charsetlist = (char **) fs_get((count + 1) * sizeof(char *));
+
+ i = 0;
+ charsetlist[i++] = cpystr(ascii);
+ charsetlist[i] = NULL;
+ }
+ }
+
+
+ if(reset){
+ if(charsetmap)
+ fs_give((void **) &charsetmap);
+
+ if(charsetlist)
+ charsetmap = utf8_csvalidmap(charsetlist);
+ }
+
+ return(charsetmap);
+}
+
+
+/* total reset */
+void
+free_charsetchecker(void)
+{
+ if(charsetlist)
+ free_list_array(&charsetlist);
+
+ items_in_charsetlist = 0;
+
+ if(charsetmap)
+ fs_give((void **) &charsetmap);
+}
+
+
+int
+representable_in_charset(unsigned long validbitmap, char *charset)
+{
+ int i, done = 0, ret = 0;
+ unsigned long j;
+
+ if(!(charset && charset[0]))
+ return ret;
+
+ if(!strucmp(charset, "UTF-8"))
+ return 1;
+
+ for(i = 0; !done && i < items_in_charsetlist; i++){
+ if(!strucmp(charset, charsetlist[i])){
+ j = 1;
+ j <<= (i+1);
+ done++;
+ if(validbitmap & j)
+ ret = 1;
+ }
+ }
+
+ return ret;
+}
+
+
+char *
+most_preferred_charset(unsigned long validbitmap)
+{
+ unsigned long bm;
+ unsigned long rm;
+ int index;
+
+ if(!(validbitmap && items_in_charsetlist > 0))
+ return("UTF-8");
+
+ /* careful, find_rightmost_bit modifies the bitmap */
+ bm = validbitmap;
+ rm = find_rightmost_bit(&bm);
+ index = MIN(MAX(rm-1,0), items_in_charsetlist-1);
+
+ return(charsetlist[index]);
+}
+
+
+/*
+ * Set parameter to new value.
+ */
+void
+set_parameter(PARAMETER **param, char *paramname, char *new_value)
+{
+ PARAMETER *pm;
+
+ if(!param || !(paramname && *paramname))
+ return;
+
+ if(*param == NULL){
+ pm = (*param) = mail_newbody_parameter();
+ pm->attribute = cpystr(paramname);
+ }
+ else{
+ int nomatch;
+
+ for(pm = *param;
+ (nomatch=strucmp(pm->attribute, paramname)) && pm->next != NULL;
+ pm = pm->next)
+ ;/* searching for paramname parameter */
+
+ if(nomatch){ /* add charset parameter */
+ pm->next = mail_newbody_parameter();
+ pm = pm->next;
+ pm->attribute = cpystr(paramname);
+ }
+ /* else pm is existing paramname parameter */
+ }
+
+ if(pm){
+ if(!(pm->value && new_value && !strcmp(pm->value, new_value))){
+ if(pm->value)
+ fs_give((void **) &pm->value);
+
+ if(new_value)
+ pm->value = cpystr(new_value);
+ }
+ }
+}
+
+
+/*----------------------------------------------------------------------
+ Remove the leading digits from SMTP error messages
+ -----*/
+char *
+tidy_smtp_mess(char *error, char *printstring, char *outbuf, size_t outbuflen)
+{
+ while(isdigit((unsigned char)*error) || isspace((unsigned char)*error) ||
+ (*error == '.' && isdigit((unsigned char)*(error+1))))
+ error++;
+
+ snprintf(outbuf, outbuflen, printstring, error);
+ outbuf[outbuflen-1] = '\0';
+ return(outbuf);
+}
+
+
+/*
+ * Local globals pine's body output routine needs
+ */
+static soutr_t l_f;
+static TCPSTREAM *l_stream;
+static unsigned c_in_buf = 0;
+
+/*
+ * def to make our pipe write's more friendly
+ */
+#ifdef PIPE_MAX
+#if PIPE_MAX > 20000
+#undef PIPE_MAX
+#endif
+#endif
+
+#ifndef PIPE_MAX
+#define PIPE_MAX 1024
+#endif
+
+
+/*
+ * l_flust_net - empties gf_io terminal function's buffer
+ */
+int
+l_flush_net(int force)
+{
+ if(c_in_buf && c_in_buf < SIZEOF_20KBUF){
+ char *p = &tmp_20k_buf[0], *lp = NULL, c = '\0';
+
+ tmp_20k_buf[c_in_buf] = '\0';
+ if(!force){
+ /*
+ * The start of each write is expected to be the start of a
+ * "record" (i.e., a CRLF terminated line). Make sure that is true
+ * else we might screw up SMTP dot quoting...
+ */
+ for(p = tmp_20k_buf, lp = NULL;
+ (p = strstr(p, "\015\012")) != NULL;
+ lp = (p += 2))
+ ;
+
+
+ if(!lp && c_in_buf > 2) /* no CRLF! */
+ for(p = &tmp_20k_buf[c_in_buf] - 2;
+ p > &tmp_20k_buf[0] && *p == '.';
+ p--) /* find last non-dot */
+ ;
+
+ if(lp && *lp && lp >= tmp_20k_buf && lp < tmp_20k_buf + SIZEOF_20KBUF){
+ /* snippet remains */
+ c = *lp;
+ *lp = '\0';
+ }
+ }
+
+ if((l_f && !(*l_f)(l_stream, tmp_20k_buf))
+ || (lmc.so && !lmc.all_written
+ && !(lmc.text_only && lmc.text_written)
+ && !so_puts(lmc.so, tmp_20k_buf)))
+ return(0);
+
+ c_in_buf = 0;
+ if(lp && lp >= tmp_20k_buf && lp < tmp_20k_buf + SIZEOF_20KBUF && (*lp = c)) /* Shift text left? */
+ while(c_in_buf < SIZEOF_20KBUF && (tmp_20k_buf[c_in_buf] = *lp))
+ c_in_buf++, lp++;
+ }
+
+ return(1);
+}
+
+
+/*
+ * l_putc - gf_io terminal function that calls smtp's soutr_t function.
+ *
+ */
+int
+l_putc(int c)
+{
+ if(c_in_buf >= 0 && c_in_buf < SIZEOF_20KBUF)
+ tmp_20k_buf[c_in_buf++] = (char) c;
+
+ return((c_in_buf >= PIPE_MAX) ? l_flush_net(FALSE) : TRUE);
+}
+
+
+
+/*
+ * pine_rfc822_output_body - pine's version of c-client call. Again,
+ * necessary since c-client doesn't know about how
+ * we're treating attachments
+ */
+long
+pine_rfc822_output_body(struct mail_bodystruct *body, soutr_t f, void *s)
+{
+ PART *part;
+ PARAMETER *param;
+ char *t, *cookie = NIL, *encode_error;
+ char tmp[MAILTMPLEN];
+ int add_trailing_crlf;
+ LOC_2022_JP ljp;
+ gf_io_t gc;
+
+ dprint((4, "-- pine_rfc822_output_body: %d\n",
+ body ? body->type : 0));
+ if(body->type == TYPEMULTIPART) { /* multipart gets special handling */
+ part = body->nested.part; /* first body part */
+ /* find cookie */
+ for (param = body->parameter; param && !cookie; param = param->next)
+ if (!strucmp (param->attribute,"BOUNDARY")) cookie = param->value;
+ if (!cookie) cookie = "-"; /* yucky default */
+
+ /*
+ * Output a bit of text before the first multipart delimiter
+ * to warn unsuspecting users of non-mime-aware ua's that
+ * they should expect weirdness...
+ */
+ if(f && !(*f)(s, " This message is in MIME format. The first part should be readable text,\015\012 while the remaining parts are likely unreadable without MIME-aware tools.\015\012\015\012"))
+ return(0);
+
+ do { /* for each part */
+ /* build cookie */
+ snprintf (tmp, sizeof(tmp), "--%s\015\012", cookie);
+ tmp[sizeof(tmp)-1] = '\0';
+ /* append cookie,mini-hdr,contents */
+ if((f && !(*f)(s, tmp))
+ || (lmc.so && !lmc.all_written && !so_puts(lmc.so, tmp))
+ || !pine_write_body_header(&part->body,f,s)
+ || !pine_rfc822_output_body (&part->body,f,s))
+ return(0);
+ } while ((part = part->next) != NULL); /* until done */
+ /* output trailing cookie */
+ snprintf (t = tmp, sizeof(tmp), "--%s--",cookie);
+ tmp[sizeof(tmp)-1] = '\0';
+ if(lmc.so && !lmc.all_written){
+ so_puts(lmc.so, t);
+ so_puts(lmc.so, "\015\012");
+ }
+
+ return(f ? ((*f) (s,t) && (*f) (s,"\015\012")) : 1);
+ }
+
+ l_f = f; /* set up for writing chars... */
+ l_stream = s; /* out other end of pipe... */
+ gf_filter_init();
+ dprint((4, "-- pine_rfc822_output_body: segment %ld bytes\n",
+ body->size.bytes));
+
+ if(body->contents.text.data)
+ gf_set_so_readc(&gc, (STORE_S *) body->contents.text.data);
+ else
+ return(1);
+
+ /*
+ * Don't add trailing line if it is ExternalText, which already guarantees
+ * a trailing newline.
+ */
+ add_trailing_crlf = !(((STORE_S *) body->contents.text.data)->src == ExternalText);
+
+ so_seek((STORE_S *) body->contents.text.data, 0L, 0);
+
+ if(body->type != TYPEMESSAGE){ /* NOT encapsulated message */
+ char *charset;
+
+ if(body->type == TYPETEXT
+ && so_attr((STORE_S *) body->contents.text.data, "edited", NULL)
+ && (charset = parameter_val(body->parameter, "charset"))){
+ if(strucmp(charset, "utf-8") && strucmp(charset, "us-ascii")){
+ if(!strucmp(charset, "iso-2022-jp")){
+ ljp.report_err = 0;
+ gf_link_filter(gf_line_test,
+ gf_line_test_opt(translate_utf8_to_2022_jp,&ljp));
+ }
+ else{
+ void *table = utf8_rmap(charset);
+
+ if(table){
+ gf_link_filter(gf_convert_utf8_charset,
+ gf_convert_utf8_charset_opt(table,0));
+ }
+ else{
+ /* else, just send it? */
+ set_parameter(&body->parameter, "charset", "UTF-8");
+ }
+ }
+ }
+
+ fs_give((void **)&charset);
+ }
+
+ /*
+ * Convert text pieces to canonical form
+ * BEFORE applying any encoding (rfc1341: appendix G)...
+ * NOTE: almost all filters expect CRLF newlines
+ */
+ if(body->type == TYPETEXT
+ && body->encoding != ENCBASE64
+ && !so_attr((STORE_S *) body->contents.text.data, "rawbody", NULL)){
+ gf_link_filter(gf_local_nvtnl, NULL);
+ }
+
+ switch (body->encoding) { /* all else needs filtering */
+ case ENC8BIT: /* encode 8BIT into QUOTED-PRINTABLE */
+ gf_link_filter(gf_8bit_qp, NULL);
+ break;
+
+ case ENCBINARY: /* encode binary into BASE64 */
+ gf_link_filter(gf_binary_b64, NULL);
+ break;
+
+ default: /* otherwise text */
+ break;
+ }
+ }
+
+ if((encode_error = gf_pipe(gc, l_putc)) != NULL){ /* shove body part down pipe */
+ q_status_message1(SM_ORDER | SM_DING, 3, 4,
+ _("Encoding Error \"%s\""), encode_error);
+ display_message('x');
+ }
+
+ gf_clear_so_readc((STORE_S *) body->contents.text.data);
+
+ if(encode_error || !l_flush_net(TRUE))
+ return(0);
+
+ send_bytes_sent += gf_bytes_piped();
+ so_release((STORE_S *)body->contents.text.data);
+
+ if(lmc.so && !lmc.all_written && lmc.text_only){
+ if(lmc.text_written){ /* we have some splainin' to do */
+ char tmp[MAILTMPLEN];
+ char *name = NULL;
+
+ if(!(so_puts(lmc.so,_("The following attachment was sent,\015\012"))
+ && so_puts(lmc.so,_("but NOT saved in the Fcc copy:\015\012"))))
+ return(0);
+
+ /*
+ * BUG: If this name is not ascii it's going to cause trouble.
+ */
+ name = parameter_val(body->parameter, "name");
+ snprintf(tmp, sizeof(tmp),
+ " A %s/%s%s%s%s segment of about %s bytes.\015\012",
+ body_type_names(body->type),
+ body->subtype ? body->subtype : "Unknown",
+ name ? " (Name=\"" : "",
+ name ? name : "",
+ name ? "\")" : "",
+ comatose(body->size.bytes));
+ tmp[sizeof(tmp)-1] = '\0';
+ if(name)
+ fs_give((void **)&name);
+
+ if(!so_puts(lmc.so, tmp))
+ return(0);
+ }
+ else /* suppress everything after first text part */
+ lmc.text_written = (body->type == TYPETEXT
+ && (!body->subtype
+ || !strucmp(body->subtype, "plain")));
+ }
+
+ if(add_trailing_crlf)
+ return((f ? (*f)(s, "\015\012") : 1) /* output final stuff */
+ && ((lmc.so && !lmc.all_written) ? so_puts(lmc.so,"\015\012") : 1));
+ else
+ return(1);
+}
+
+
+/*
+ * pine_write_body_header - another c-client clone. This time
+ * so the final encoding labels get set
+ * correctly since it hasn't happened yet,
+ * and to be paranoid about line lengths.
+ *
+ * Returns: TRUE/nonzero on success, zero on error
+ */
+int
+pine_write_body_header(struct mail_bodystruct *body, soutr_t f, void *s)
+{
+ char tmp[MAILTMPLEN];
+ RFC822BUFFER rbuf;
+ int i;
+ unsigned char c;
+ STRINGLIST *stl;
+ STORE_S *so;
+ extern const char *tspecials;
+
+ if((so = so_get(CharStar, NULL, WRITE_ACCESS)) != NULL){
+ if(!(so_puts(so, "Content-Type: ")
+ && so_puts(so, body_types[body->type])
+ && so_puts(so, "/")
+ && so_puts(so, body->subtype
+ ? body->subtype
+ : rfc822_default_subtype (body->type))))
+ return(pwbh_finish(0, so));
+
+ if(body->parameter){
+ if(!pine_write_params(body->parameter, so))
+ return(pwbh_finish(0, so));
+ }
+ else if(!so_puts(so, "; CHARSET=US-ASCII"))
+ return(pwbh_finish(0, so));
+
+ if(!so_puts(so, "\015\012"))
+ return(pwbh_finish(0, so));
+
+ if ((body->encoding /* note: encoding 7BIT never output! */
+ && !(so_puts(so, "Content-Transfer-Encoding: ")
+ && so_puts(so, body_encodings[(body->encoding==ENCBINARY)
+ ? ENCBASE64
+ : (body->encoding == ENC8BIT)
+ ? ENCQUOTEDPRINTABLE
+ : (body->encoding <= ENCMAX)
+ ? body->encoding
+ : ENCOTHER])
+ && so_puts(so, "\015\012")))
+ /*
+ * If requested, strip Content-ID headers that don't look like they
+ * are needed. Microsoft's Outlook XP has a bug that causes it to
+ * not show that there is an attachment when there is a Content-ID
+ * header present on that attachment.
+ *
+ * If user has quell-content-id turned on, don't output content-id
+ * unless it is of type message/external-body.
+ * Since this code doesn't look inside messages being forwarded
+ * type message content-ids will remain as is and type multipart
+ * alternative will remain as is. We don't create those on our
+ * own. If we did, we'd have to worry about getting this right.
+ */
+ || (body->id && (F_OFF(F_QUELL_CONTENT_ID, ps_global)
+ || (body->type == TYPEMESSAGE
+ && body->subtype
+ && !strucmp(body->subtype, "external-body")))
+ && !(so_puts(so, "Content-ID: ") && so_puts(so, body->id)
+ && so_puts(so, "\015\012")))
+ || (body->description
+ && strlen(body->description) < 5000 /* arbitrary! */
+ && !pine_write_header_line("Content-Description: ", body->description, so))
+ || (body->md5
+ && !(so_puts(so, "Content-MD5: ")
+ && so_puts(so, body->md5)
+ && so_puts(so, "\015\012"))))
+ return(pwbh_finish(0, so));
+
+ if ((stl = body->language) != NULL) {
+ if(!so_puts(so, "Content-Language: "))
+ return(pwbh_finish(0, so));
+
+ do {
+ if(strlen((char *)stl->text.data) > 500) /* arbitrary! */
+ return(pwbh_finish(0, so));
+
+ tmp[0] = '\0';
+ rbuf.f = dummy_soutr;
+ rbuf.s = NULL;
+ rbuf.beg = tmp;
+ rbuf.cur = tmp;
+ rbuf.end = tmp+sizeof(tmp)-1;
+ rfc822_output_cat(&rbuf, (char *)stl->text.data, tspecials);
+ *rbuf.cur = '\0';
+
+ if(!so_puts(so, tmp)
+ || ((stl = stl->next) && !so_puts(so, ", ")))
+ return(pwbh_finish(0, so));
+ }
+ while (stl);
+
+ if(!so_puts(so, "\015\012"))
+ return(pwbh_finish(0, so));
+ }
+
+ if (body->disposition.type) {
+ if(!(so_puts(so, "Content-Disposition: ")
+ && so_puts(so, body->disposition.type)))
+ return(pwbh_finish(0, so));
+
+ if(!pine_write_params(body->disposition.parameter, so))
+ return(pwbh_finish(0, so));
+
+ if(!so_puts(so, "\015\012"))
+ return(pwbh_finish(0, so));
+ }
+
+ /* copy out of so, a line at a time (or less than a K)
+ * and send it down the pike
+ */
+ so_seek(so, 0L, 0);
+ i = 0;
+ while(so_readc(&c, so))
+ if((tmp[i++] = c) == '\012' || i > sizeof(tmp) - 3){
+ tmp[i] = '\0';
+ if((f && !(*f)(s, tmp))
+ || !lmc_body_header_line(tmp, i <= sizeof(tmp) - 3))
+ return(pwbh_finish(0, so));
+
+ i = 0;
+ }
+
+ /* Finally, write blank line */
+ if((f && !(*f)(s, "\015\012")) || !lmc_body_header_finish())
+ return(pwbh_finish(0, so));
+
+ return(pwbh_finish(i == 0, so)); /* better of ended on LF */
+ }
+
+ return(0);
+}
+
+
+/*
+ * pine_write_header - convert, encode (if needed) and
+ * write "header-name: field-body"
+ */
+int
+pine_write_header_line(char *hdr, char *val, STORE_S *so)
+{
+ char *cv, *cs, *vp;
+ int rv;
+
+ cs = posting_characterset(val, NULL, HdrText);
+ cv = utf8_to_charset(val, cs, 0);
+ vp = rfc1522_encode(tmp_20k_buf, SIZEOF_20KBUF,
+ (unsigned char *) cv, cs);
+
+ rv = (so_puts(so, hdr) && so_puts(so, vp) && so_puts(so, "\015\012"));
+
+ if(cv && cv != val)
+ fs_give((void **) &cv);
+
+
+ return(rv);
+}
+
+
+/*
+ * pine_write_param - convert, encode and write MIME header-field parameters
+ */
+int
+pine_write_params(PARAMETER *param, STORE_S *so)
+{
+ for(; param; param = param->next){
+ int rv;
+ char *cv, *cs;
+ extern const char *tspecials;
+
+ cs = posting_characterset(param->value, NULL, HdrText);
+ cv = utf8_to_charset(param->value, cs, 0);
+ rv = (so_puts(so, "; ")
+ && rfc2231_output(so, param->attribute, cv, (char *) tspecials, cs));
+
+ if(cv && cv != param->value)
+ fs_give((void **) &cv);
+
+ if(!rv)
+ return(0);
+ }
+
+ return(1);
+}
+
+
+
+int
+lmc_body_header_line(char *line, int beginning)
+{
+ if(lmc.so && !lmc.all_written){
+ if(beginning && lmc.text_only && lmc.text_written
+ && (!struncmp(line, "content-type:", 13)
+ || !struncmp(line, "content-transfer-encoding:", 26)
+ || !struncmp(line, "content-disposition:", 20))){
+ /*
+ * "comment out" the real values since our comment isn't
+ * likely the same type, disposition nor encoding...
+ */
+ if(!so_puts(lmc.so, "X-"))
+ return(FALSE);
+ }
+
+ return(so_puts(lmc.so, line));
+ }
+
+ return(TRUE);
+}
+
+
+int
+lmc_body_header_finish(void)
+{
+ if(lmc.so && !lmc.all_written){
+ if(lmc.text_only && lmc.text_written
+ && !so_puts(lmc.so, "Content-Type: TEXT/PLAIN\015\012"))
+ return(FALSE);
+
+ return(so_puts(lmc.so, "\015\012"));
+ }
+
+ return(TRUE);
+}
+
+
+
+int
+pwbh_finish(int rv, STORE_S *so)
+{
+ if(so)
+ so_give(&so);
+
+ return(rv);
+}
+
+
+/*
+ * pine_free_body - c-client call wrapper so the body data pointer we
+ * we're using in a way c-client doesn't know about
+ * gets free'd appropriately.
+ */
+void
+pine_free_body(struct mail_bodystruct **body)
+{
+ /*
+ * Preempt c-client's contents.text.data clean up since we've
+ * usurped it's meaning for our own purposes...
+ */
+ pine_free_body_data (*body);
+
+ /* Then let c-client handle the rest... */
+ mail_free_body(body);
+}
+
+
+/*
+ * pine_free_body_data - free pine's interpretations of the body part's
+ * data pointer.
+ */
+void
+pine_free_body_data(struct mail_bodystruct *body)
+{
+ if(body){
+ if(body->type == TYPEMULTIPART){
+ PART *part = body->nested.part;
+ do /* for each part */
+ pine_free_body_data(&part->body);
+ while ((part = part->next) != NULL); /* until done */
+ }
+ else if(body->contents.text.data)
+ so_give((STORE_S **) &body->contents.text.data);
+ }
+}
+
+
+long
+send_body_size(struct mail_bodystruct *body)
+{
+ long l = 0L;
+ PART *part;
+
+ if(body->type == TYPEMULTIPART) { /* multipart gets special handling */
+ part = body->nested.part; /* first body part */
+ do /* for each part */
+ l += send_body_size(&part->body);
+ while ((part = part->next) != NULL); /* until done */
+ return(l);
+ }
+
+ return(l + body->size.bytes);
+}
+
+
+int
+sent_percent(void)
+{
+ int i = (int) (((send_bytes_sent + gf_bytes_piped()) * 100)
+ / send_bytes_to_send);
+ return(MIN(i, 100));
+}
+
+
+/*
+ * pine_smtp_verbose_out - write
+ */
+void
+pine_smtp_verbose_out(char *s)
+{
+#ifdef _WINDOWS
+ LPTSTR slpt;
+#endif
+ if(verbose_send_output && s){
+ char *p, last = '\0';
+
+ for(p = s; *p; p++)
+ if(*p == '\015')
+ *p = ' ';
+ else
+ last = *p;
+
+#ifdef _WINDOWS
+ /*
+ * The stream is opened in Unicode mode, so we need to fix the
+ * argument to fputs.
+ */
+ slpt = utf8_to_lptstr((LPSTR) s);
+ if(slpt){
+ _fputts(slpt, verbose_send_output);
+ fs_give((void **) &slpt);
+ }
+
+ if(last != '\012')
+ _fputtc(L'\n', verbose_send_output);
+#else
+ fputs(s, verbose_send_output);
+ if(last != '\012')
+ fputc('\n', verbose_send_output);
+#endif
+ }
+
+}
+
+
+/*
+ * pine_header_forbidden - is this name a "forbidden" header?
+ *
+ * name - the header name to check
+ * We don't allow user to change these.
+ */
+int
+pine_header_forbidden(char *name)
+{
+ char **p;
+ static char *forbidden_headers[] = {
+ "sender",
+ "x-sender",
+ "x-x-sender",
+ "date",
+ "received",
+ "message-id",
+ "in-reply-to",
+ "path",
+ "resent-message-id",
+ "resent-date",
+ "resent-from",
+ "resent-sender",
+ "resent-to",
+ "resent-cc",
+ "resent-reply-to",
+ "mime-version",
+ "content-type",
+ "x-priority",
+ "user-agent",
+ NULL
+ };
+
+ for(p = forbidden_headers; *p; p++)
+ if(!strucmp(name, *p))
+ break;
+
+ return((*p) ? 1 : 0);
+}
+
+
+/*
+ * hdr_is_in_list - is there a custom value for this header?
+ *
+ * hdr - the header name to check
+ * custom - the list to check in
+ * Returns 1 if there is a custom value, 0 otherwise.
+ */
+int
+hdr_is_in_list(char *hdr, PINEFIELD *custom)
+{
+ PINEFIELD *pf;
+
+ for(pf = custom; pf && pf->name; pf = pf->next)
+ if(strucmp(pf->name, hdr) == 0)
+ return 1;
+
+ return 0;
+}
+
+
+/*
+ * count_custom_hdrs_pf - returns number of custom headers in arg
+ * custom -- the list to be counted
+ * only_nonstandard -- only count headers which aren't standard pine headers
+ */
+int
+count_custom_hdrs_pf(PINEFIELD *custom, int only_nonstandard)
+{
+ int ret = 0;
+
+ for(; custom && custom->name; custom = custom->next)
+ if(!only_nonstandard || !custom->standard)
+ ret++;
+
+ return(ret);
+}
+
+
+/*
+ * count_custom_hdrs_list - returns number of custom headers in arg
+ */
+int
+count_custom_hdrs_list(char **list)
+{
+ char **p;
+ char *q = NULL;
+ char *name;
+ char *t;
+ char save;
+ int ret = 0;
+
+ if(list){
+ for(p = list; (q = *p) != NULL; p++){
+ if(q[0]){
+ /* remove leading whitespace */
+ name = skip_white_space(q);
+
+ /* look for colon or space or end */
+ for(t = name;
+ *t && !isspace((unsigned char)*t) && *t != ':'; t++)
+ ;/* do nothing */
+
+ save = *t;
+ *t = '\0';
+ if(!pine_header_forbidden(name))
+ ret++;
+
+ *t = save;
+ }
+ }
+ }
+
+ return(ret);
+}
+
+
+/*
+ * set_default_hdrval - put the user's default value for this header
+ * into pf->textbuf.
+ * setthis - the pinefield to be set
+ * custom - where to look for the default
+ */
+CustomType
+set_default_hdrval(PINEFIELD *setthis, PINEFIELD *custom)
+{
+ PINEFIELD *pf;
+ CustomType ret = NoMatch;
+
+ if(!setthis || !setthis->name){
+ q_status_message(SM_ORDER,3,7,"Internal error setting default header");
+ return(ret);
+ }
+
+ setthis->textbuf = NULL;
+
+ for(pf = custom; pf && pf->name; pf = pf->next){
+ if(strucmp(pf->name, setthis->name) != 0)
+ continue;
+
+ ret = pf->cstmtype;
+
+ /* turn on editing */
+ if(strucmp(pf->name, "From") == 0 || strucmp(pf->name, "Reply-To") == 0)
+ setthis->canedit = 1;
+
+ if(pf->val)
+ setthis->textbuf = cpystr(pf->val);
+ }
+
+ if(!setthis->textbuf)
+ setthis->textbuf = cpystr("");
+
+ return(ret);
+}
+
+
+/*
+ * pine_header_standard - is this name a "standard" header?
+ *
+ * name - the header name to check
+ */
+FieldType
+pine_header_standard(char *name)
+{
+ int i;
+
+ /* check to see if this is a standard header */
+ for(i = 0; pf_template[i].name; i++)
+ if(!strucmp(name, pf_template[i].name))
+ return(pf_template[i].type);
+
+ return(TypeUnknown);
+}
+
+
+/*
+ * customized_hdr_setup - setup the PINEFIELDS for all the customized headers
+ * Allocates space for each name and addr ptr.
+ * Allocates space for default in textbuf, even if empty.
+ *
+ * head - the first PINEFIELD to fill in
+ * list - the list to parse
+ */
+void
+customized_hdr_setup(PINEFIELD *head, char **list, CustomType cstmtype)
+{
+ char **p, *q, *t, *name, *value, save;
+ PINEFIELD *pf;
+
+ pf = head;
+
+ if(list){
+ for(p = list; (q = *p) != NULL; p++){
+
+ if(q[0]){
+
+ /* anything after leading whitespace? */
+ if(!*(name = skip_white_space(q)))
+ continue;
+
+ /* look for colon or space or end */
+ for(t = name;
+ *t && !isspace((unsigned char)*t) && *t != ':'; t++)
+ ;/* do nothing */
+
+ /* if there is a space in the field-name, skip it */
+ if(isspace((unsigned char)*t)){
+ q_status_message1(SM_ORDER, 3, 3,
+ _("Space not allowed in header name (%s)"),
+ name);
+ continue;
+ }
+
+ save = *t;
+ *t = '\0';
+
+ /* Don't allow any of the forbidden headers. */
+ if(pine_header_forbidden(name)){
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("Not allowed to change header \"%s\""),
+ name);
+
+ *t = save;
+ continue;
+ }
+
+ if(pf){
+ if(pine_header_standard(name) != TypeUnknown)
+ pf->standard = 1;
+
+ pf->name = cpystr(name);
+ pf->type = FreeText;
+ pf->cstmtype = cstmtype;
+ pf->next = pf+1;
+
+#ifdef OLDWAY
+ /*
+ * Some mailers apparently break if we change
+ * user@domain into Fred <user@domain> for
+ * return-receipt-to,
+ * so we'll just call this a FreeText field, too.
+ */
+ /*
+ * For now, all custom headers are FreeText except for
+ * this one that we happen to know about. We might
+ * have to add some syntax to the config option so that
+ * people can tell us their custom header takes addresses.
+ */
+ if(!strucmp(pf->name, "Return-Receipt-to")){
+ pf->type = Address;
+ pf->addr = (ADDRESS **)fs_get(sizeof(ADDRESS *));
+ *pf->addr = (ADDRESS *)NULL;
+ }
+#endif /* OLDWAY */
+
+ *t = save;
+
+ /* remove space between name and colon */
+ value = skip_white_space(t);
+
+ /* give them an alloc'd default, even if empty */
+ pf->textbuf = cpystr((*value == ':')
+ ? skip_white_space(++value) : "");
+ if(pf->textbuf && pf->textbuf[0])
+ pf->val = cpystr(pf->textbuf);
+
+ pf++;
+ }
+ else
+ *t = save;
+ }
+ }
+ }
+
+ /* fix last next pointer */
+ if(head && pf != head)
+ (pf-1)->next = NULL;
+}
+
+
+/*
+ * add_defaults_from_list - the PINEFIELDS list given by "head" is already
+ * setup except that it doesn't have default values.
+ * Add those defaults if they exist in "list".
+ *
+ * head - the first PINEFIELD to add a default to
+ * list - the list to get the defaults from
+ */
+void
+add_defaults_from_list(PINEFIELD *head, char **list)
+{
+ char **p, *q, *t, *name, *value, save;
+ PINEFIELD *pf;
+
+ for(pf = head; pf && list; pf = pf->next){
+ if(!pf->name)
+ continue;
+
+ for(p = list; (q = *p) != NULL; p++){
+
+ if(q[0]){
+
+ /* anything after leading whitespace? */
+ if(!*(name = skip_white_space(q)))
+ continue;
+
+ /* look for colon or space or end */
+ for(t = name;
+ *t && !isspace((unsigned char)*t) && *t != ':'; t++)
+ ;/* do nothing */
+
+ /* if there is a space in the field-name, skip it */
+ if(isspace((unsigned char)*t))
+ continue;
+
+ save = *t;
+ *t = '\0';
+
+ if(strucmp(name, pf->name) != 0){
+ *t = save;
+ continue;
+ }
+
+ *t = save;
+
+ /*
+ * Found the right header. See if it has a default
+ * value set.
+ */
+
+ /* remove space between name and colon */
+ value = skip_white_space(t);
+
+ if(*value == ':'){
+ char *defval;
+
+ defval = skip_white_space(++value);
+ if(defval && *defval){
+ if(pf->val)
+ fs_give((void **)&pf->val);
+
+ pf->val = cpystr(defval);
+ }
+ }
+
+ break; /* on to next pf */
+ }
+ }
+ }
+}
+
+
+/*
+ * parse_custom_hdrs - allocate PINEFIELDS for custom headers
+ * fill in the defaults
+ * Args - list -- The list to parse.
+ * cstmtype -- Fill the in cstmtype field with this value
+ */
+PINEFIELD *
+parse_custom_hdrs(char **list, CustomType cstmtype)
+{
+ PINEFIELD *pfields;
+ int i;
+
+ /*
+ * add one for possible use by fcc
+ * What is this "possible use"? I don't see it. I don't
+ * think it exists anymore. I think +1 would be ok. hubert 2000-08-02
+ */
+ i = (count_custom_hdrs_list(list) + 2) * sizeof(PINEFIELD);
+ pfields = (PINEFIELD *)fs_get((size_t) i);
+ memset(pfields, 0, (size_t) i);
+
+ /* set up the custom header pfields */
+ customized_hdr_setup(pfields, list, cstmtype);
+
+ return(pfields);
+}
+
+
+/*
+ * Combine the two lists of headers into one list which is allocated here
+ * and freed by the caller. Eliminate duplicates with values from the role
+ * taking precedence over values from the default.
+ */
+PINEFIELD *
+combine_custom_headers(PINEFIELD *dflthdrs, PINEFIELD *rolehdrs)
+{
+ PINEFIELD *pfields, *pf, *pf2;
+ int max_hdrs, i;
+
+ max_hdrs = count_custom_hdrs_pf(rolehdrs,0) +
+ count_custom_hdrs_pf(dflthdrs,0);
+
+ pfields = (PINEFIELD *)fs_get((size_t)(max_hdrs+1)*sizeof(PINEFIELD));
+ memset(pfields, 0, (size_t)(max_hdrs+1)*sizeof(PINEFIELD));
+
+ pf = pfields;
+ for(pf2 = rolehdrs; pf2 && pf2->name; pf2 = pf2->next){
+ pf->name = pf2->name ? cpystr(pf2->name) : NULL;
+ pf->type = pf2->type;
+ pf->cstmtype = pf2->cstmtype;
+ pf->textbuf = pf2->textbuf ? cpystr(pf2->textbuf) : NULL;
+ pf->val = pf2->val ? cpystr(pf2->val) : NULL;
+ pf->standard = pf2->standard;
+ pf->next = pf+1;
+ pf++;
+ }
+
+ /* if these aren't already there, add them */
+ for(pf2 = dflthdrs; pf2 && pf2->name; pf2 = pf2->next){
+ /* check for already there */
+ for(i = 0;
+ pfields[i].name && strucmp(pfields[i].name, pf2->name);
+ i++)
+ ;
+
+ if(!pfields[i].name){ /* this is a new one */
+ pf->name = pf2->name ? cpystr(pf2->name) : NULL;
+ pf->type = pf2->type;
+ pf->cstmtype = pf2->cstmtype;
+ pf->textbuf = pf2->textbuf ? cpystr(pf2->textbuf) : NULL;
+ pf->val = pf2->val ? cpystr(pf2->val) : NULL;
+ pf->standard = pf2->standard;
+ pf->next = pf+1;
+ pf++;
+ }
+ }
+
+ /* fix last next pointer */
+ if(pf != pfields)
+ (pf-1)->next = NULL;
+
+ return(pfields);
+}
+
+
+/*
+ * free_customs - free misc. resources associated with custom header fields
+ *
+ * pf - pointer to first custom field
+ */
+void
+free_customs(PINEFIELD *head)
+{
+ PINEFIELD *pf;
+
+ for(pf = head; pf && pf->name; pf = pf->next){
+
+ fs_give((void **)&pf->name);
+
+ if(pf->val)
+ fs_give((void **)&pf->val);
+
+ /* only true for FreeText */
+ if(pf->textbuf)
+ fs_give((void **)&pf->textbuf);
+
+ /* only true for Address */
+ if(pf->addr && *pf->addr)
+ mail_free_address(pf->addr);
+ }
+
+ fs_give((void **)&head);
+}
+
+
+/*
+ * encode_whole_header
+ *
+ * Returns 1 if whole value should be encoded
+ * 0 to encode only within comments
+ */
+int
+encode_whole_header(char *field, METAENV *header)
+{
+ int retval = 0;
+ PINEFIELD *pf;
+
+ if(field && (!strucmp(field, "Subject") ||
+ !strucmp(field, "Comment") ||
+ !struncmp(field, "X-", 2)))
+ retval++;
+ else if(field && *field && header && header->custom){
+ for(pf = header->custom; pf && pf->name; pf = pf->next){
+
+ if(!pf->standard && !strucmp(pf->name, field)){
+ retval++;
+ break;
+ }
+ }
+ }
+
+ return(retval);
+}
+
+
+/*----------------------------------------------------------------------
+ Post news via NNTP or inews
+
+Args: env -- envelope of message to post
+ body -- body of message to post
+
+Returns: -1 if failed or cancelled, 1 if succeeded
+
+WARNING: This call function has the side effect of writing the message
+ to the lmc.so object.
+ ----*/
+int
+news_poster(METAENV *header, struct mail_bodystruct *body, char **alt_nntp_servers,
+ void (*pipecb_f)(PIPE_S *, int, void *))
+{
+ char *error_mess, error_buf[200], **news_servers;
+ char **servers_to_use;
+ int we_cancel = 0, server_no = 0, done_posting = 0;
+ void *orig_822_output;
+ BODY *bp = NULL;
+
+ error_buf[0] = '\0';
+ we_cancel = busy_cue("Posting news", NULL, 0);
+
+ dprint((4, "Posting: [%s]\n",
+ (header && header->env && header->env->newsgroups)
+ ? header->env->newsgroups : "?"));
+
+ if((alt_nntp_servers && alt_nntp_servers[0] && alt_nntp_servers[0][0])
+ || (ps_global->VAR_NNTP_SERVER && ps_global->VAR_NNTP_SERVER[0]
+ && ps_global->VAR_NNTP_SERVER[0][0])){
+ /*---------- NNTP server defined ----------*/
+ error_mess = NULL;
+ servers_to_use = (alt_nntp_servers && alt_nntp_servers[0]
+ && alt_nntp_servers[0][0])
+ ? alt_nntp_servers : ps_global->VAR_NNTP_SERVER;
+
+ /*
+ * Install our rfc822 output routine
+ */
+ orig_822_output = mail_parameters(NULL, GET_RFC822OUTPUT, NULL);
+ (void) mail_parameters(NULL, SET_RFC822OUTPUT,
+ (void *)post_rfc822_output);
+
+ server_no = 0;
+ news_servers = (char **)fs_get(2 * sizeof(char *));
+ news_servers[1] = NULL;
+ while(!done_posting && servers_to_use[server_no] &&
+ servers_to_use[server_no][0]){
+ news_servers[0] = servers_to_use[server_no];
+ ps_global->noshow_error = 1;
+#ifdef DEBUG
+ sending_stream = nntp_open(news_servers, debug ? NOP_DEBUG : 0L);
+#else
+ sending_stream = nntp_open(news_servers, 0L);
+#endif
+ ps_global->noshow_error = 0;
+
+ if(sending_stream != NULL) {
+ unsigned short save_encoding, added_encoding;
+
+ /*
+ * Fake that we've got clearance from the transport agent
+ * for 8bit transport for the benefit of our output routines...
+ */
+ if(F_ON(F_ENABLE_8BIT_NNTP, ps_global)
+ && (bp = first_text_8bit(body))){
+ int i;
+
+ for(i = 0; (i <= ENCMAX) && body_encodings[i]; i++)
+ ;
+
+ if(i > ENCMAX){ /* no empty encoding slots! */
+ bp = NULL;
+ }
+ else {
+ added_encoding = i;
+ body_encodings[added_encoding] = body_encodings[ENC8BIT];
+ save_encoding = bp->encoding;
+ bp->encoding = added_encoding;
+ }
+ }
+
+ /*
+ * Set global header pointer so we can get at it later...
+ */
+ send_header = header;
+ ps_global->noshow_error = 1;
+ if(nntp_mail(sending_stream, header->env, body) == 0)
+ snprintf(error_mess = error_buf, sizeof(error_buf),
+ _("Error posting message: %s"),
+ sending_stream->reply);
+ else{
+ done_posting = 1;
+ error_buf[0] = '\0';
+ error_mess = NULL;
+ }
+
+ error_buf[sizeof(error_buf)-1] = '\0';
+ smtp_close(sending_stream);
+ ps_global->noshow_error = 0;
+ sending_stream = NULL;
+ if(F_ON(F_ENABLE_8BIT_NNTP, ps_global) && bp){
+ body_encodings[added_encoding] = NULL;
+ bp->encoding = save_encoding;
+ }
+
+ } else {
+ /*---- Open of NNTP connection failed ------ */
+ snprintf(error_mess = error_buf, sizeof(error_buf),
+ _("Error connecting to news server: %s"),
+ ps_global->c_client_error);
+ error_buf[sizeof(error_buf)-1] = '\0';
+ dprint((1, error_buf));
+ }
+ server_no++;
+ }
+ fs_give((void **)&news_servers);
+ (void) mail_parameters (NULL, SET_RFC822OUTPUT, orig_822_output);
+ } else {
+ /*----- Post via local mechanism -------*/
+#ifdef _WINDOWS
+ snprintf(error_mess = error_buf, sizeof(error_buf),
+ _("Can't post, NNTP-server must be defined!"));
+#else /* UNIX */
+ error_mess = post_handoff(header, body, error_buf, sizeof(error_buf), NULL,
+ pipecb_f);
+#endif
+ }
+
+ if(we_cancel)
+ cancel_busy_cue(0);
+
+ if(error_mess){
+ if(lmc.so && !lmc.all_written)
+ so_give(&lmc.so); /* clean up any fcc data */
+
+ q_status_message(SM_ORDER | SM_DING, 4, 5, error_mess);
+ return(-1);
+ }
+
+ lmc.all_written = 1;
+ return(1);
+}
+
+
+/* ----------------------------------------------------------------------
+ Figure out command to start local SMTP agent
+
+ Args: errbuf -- buffer for reporting errors (assumed non-NULL)
+
+ Returns an alloc'd copy of the local SMTP agent invocation or NULL
+
+ ----*/
+char *
+smtp_command(char *errbuf, size_t errbuflen)
+{
+#ifdef _WINDOWS
+ if(!(ps_global->VAR_SMTP_SERVER && ps_global->VAR_SMTP_SERVER[0]
+ && ps_global->VAR_SMTP_SERVER[0][0]))
+ strncpy(errbuf,_("SMTP-server must be defined!"),errbuflen);
+
+ errbuf[errbuflen-1] = '\0';
+#else /* UNIX */
+# if defined(SENDMAIL) && defined(SENDMAILFLAGS)
+ char tmp[256];
+
+ snprintf(tmp, sizeof(tmp), "%.*s %.*s", (sizeof(tmp)-3)/2, SENDMAIL,
+ (sizeof(tmp)-3)/2, SENDMAILFLAGS);
+ return(cpystr(tmp));
+# else
+ strncpy(errbuf, _("No default posting command."), errbuflen);
+ errbuf[errbuflen-1] = '\0';
+# endif
+#endif
+
+ return(NULL);
+}
+
+
+
+#ifndef _WINDOWS
+/*----------------------------------------------------------------------
+ Hand off given message to local posting agent
+
+ Args: envelope -- The envelope for the BCC and debugging
+ header -- The text of the message header
+ errbuf -- buffer for reporting errors (assumed non-NULL)
+ len -- Length of errbuf
+
+ ----*/
+int
+mta_handoff(METAENV *header, struct mail_bodystruct *body,
+ char *errbuf, size_t len,
+ void (*bigresult_f) (char *, int),
+ void (*pipecb_f)(PIPE_S *, int, void *))
+{
+#ifdef DF_SENDMAIL_PATH
+ char cmd_buf[256];
+#endif
+ char *cmd = NULL;
+
+ /*
+ * A bit of complicated policy implemented here.
+ * There are two posting variables sendmail-path and smtp-server.
+ * Precedence is in that order.
+ * They can be set one of 4 ways: fixed, command-line, user, or globally.
+ * Precedence is in that order.
+ * Said differently, the order goes something like what's below.
+ *
+ * NOTE: the fixed/command-line/user precendence handling is also
+ * indicated by what's pointed to by ps_global->VAR_*, but since
+ * that also includes the global defaults, it's not sufficient.
+ */
+
+ if(ps_global->FIX_SENDMAIL_PATH
+ && ps_global->FIX_SENDMAIL_PATH[0]){
+ cmd = ps_global->FIX_SENDMAIL_PATH;
+ }
+ else if(!(ps_global->FIX_SMTP_SERVER
+ && ps_global->FIX_SMTP_SERVER[0])){
+ if(ps_global->COM_SENDMAIL_PATH
+ && ps_global->COM_SENDMAIL_PATH[0]){
+ cmd = ps_global->COM_SENDMAIL_PATH;
+ }
+ else if(!(ps_global->COM_SMTP_SERVER
+ && ps_global->COM_SMTP_SERVER[0])){
+ if((ps_global->vars[V_SENDMAIL_PATH].post_user_val.p
+ && ps_global->vars[V_SENDMAIL_PATH].post_user_val.p[0]) ||
+ (ps_global->vars[V_SENDMAIL_PATH].main_user_val.p
+ && ps_global->vars[V_SENDMAIL_PATH].main_user_val.p[0])){
+ if(ps_global->vars[V_SENDMAIL_PATH].post_user_val.p
+ && ps_global->vars[V_SENDMAIL_PATH].post_user_val.p[0])
+ cmd = ps_global->vars[V_SENDMAIL_PATH].post_user_val.p;
+ else
+ cmd = ps_global->vars[V_SENDMAIL_PATH].main_user_val.p;
+ }
+ else if(!((ps_global->vars[V_SMTP_SERVER].post_user_val.l
+ && ps_global->vars[V_SMTP_SERVER].post_user_val.l[0]) ||
+ (ps_global->vars[V_SMTP_SERVER].main_user_val.l
+ && ps_global->vars[V_SMTP_SERVER].main_user_val.l[0]))){
+ if(ps_global->GLO_SENDMAIL_PATH
+ && ps_global->GLO_SENDMAIL_PATH[0]){
+ cmd = ps_global->GLO_SENDMAIL_PATH;
+ }
+#ifdef DF_SENDMAIL_PATH
+ /*
+ * This defines the default method of posting. So,
+ * unless we're told otherwise use it...
+ */
+ else if(!(ps_global->GLO_SMTP_SERVER
+ && ps_global->GLO_SMTP_SERVER[0])){
+ strncpy(cmd = cmd_buf, DF_SENDMAIL_PATH, sizeof(cmd_buf)-1);
+ cmd_buf[sizeof(cmd_buf)-1] = '\0';
+ }
+#endif
+ }
+ }
+ }
+
+ *errbuf = '\0';
+ if(cmd){
+ dprint((4, "call_mailer via cmd: %s\n", cmd ? cmd : "?"));
+
+ (void) mta_parse_post(header, body, cmd, errbuf, len, bigresult_f, pipecb_f);
+ return(1);
+ }
+ else
+ return(0);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Hand off given message to local posting agent
+
+ Args: envelope -- The envelope for the BCC and debugging
+ header -- The text of the message header
+ errbuf -- buffer for reporting errors (assumed non-NULL)
+ errbuflen -- Length of errbuf
+
+ Fork off mailer process and pipe the message into it
+ Called to post news via Inews when NNTP is unavailable
+
+ ----*/
+char *
+post_handoff(METAENV *header, struct mail_bodystruct *body, char *errbuf,
+ size_t errbuflen,
+ void (*bigresult_f) (char *, int),
+ void (*pipecb_f)(PIPE_S *, int, void *))
+{
+ char *err = NULL;
+#ifdef SENDNEWS
+ char *s;
+ char tmp[200];
+
+ if(s = strstr(header->env->date," (")) /* fix the date format for news */
+ *s = '\0';
+
+ if(err = mta_parse_post(header, body, SENDNEWS, errbuf, errbuflen, bigresult_f, pipecb_f)){
+ strncpy(tmp, err, sizeof(tmp)-1);
+ tmp[sizeof(tmp)-1] = '\0';
+ snprintf(err = errbuf, errbuflen, _("News not posted: \"%s\": %s"),
+ SENDNEWS, tmp);
+ }
+
+ if(s)
+ *s = ' '; /* restore the date */
+
+#else /* !SENDNEWS */ /* this is the default case */
+ strncpy(err = errbuf, _("Can't post, NNTP-server must be defined!"), errbuflen-1);
+ err[errbuflen-1] = '\0';
+#endif /* !SENDNEWS */
+ return(err);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Hand off message to local MTA; it parses recipients from 822 header
+
+ Args: header -- struct containing header data
+ body -- struct containing message body data
+ cmd -- command to use for handoff (%s says where file should go)
+ errs -- pointer to buf to hold errors
+
+ ----*/
+char *
+mta_parse_post(METAENV *header, struct mail_bodystruct *body, char *cmd,
+ char *errs, size_t errslen, void (*bigresult_f)(char *, int),
+ void (*pipecb_f)(PIPE_S *, int, void *))
+{
+ char *result = NULL;
+ PIPE_S *pipe;
+
+ dprint((1, "=== mta_parse_post(%s) ===\n", cmd ? cmd : "?"));
+
+ if((pipe = open_system_pipe(cmd, &result, NULL,
+ PIPE_STDERR|PIPE_WRITE|PIPE_PROT|PIPE_NOSHELL|PIPE_DESC,
+ 0, pipecb_f, pipe_report_error)) != NULL){
+ if(!pine_rfc822_output(header, body, pine_pipe_soutr_nl,
+ (TCPSTREAM *) pipe)){
+ strncpy(errs, _("Error posting."), errslen-1);
+ errs[errslen-1] = '\0';
+ }
+
+ if(close_system_pipe(&pipe, NULL, pipecb_f) && !*errs){
+ snprintf(errs, errslen, _("Posting program %s returned error"), cmd);
+ if(result && bigresult_f)
+ (*bigresult_f)(result, CM_BR_ERROR);
+ }
+ }
+ else
+ snprintf(errs, errslen, _("Error running \"%s\""), cmd);
+
+ if(result){
+ our_unlink(result);
+ fs_give((void **)&result);
+ }
+
+ return(*errs ? errs : NULL);
+}
+
+
+/*
+ * pine_pipe_soutr - Replacement for tcp_soutr that writes one of our
+ * pipes rather than a tcp stream
+ */
+long
+pine_pipe_soutr_nl (void *stream, char *s)
+{
+ long rv = T;
+ char *p;
+ size_t n;
+
+ while(*s && rv){
+ if((n = (p = strstr(s, "\015\012")) ? p - s : strlen(s)) != 0)
+ while((rv = write(((PIPE_S *)stream)->out.d, s, n)) != n)
+ if(rv < 0){
+ if(errno != EINTR){
+ rv = 0;
+ break;
+ }
+ }
+ else{
+ s += rv;
+ n -= rv;
+ }
+
+ if(p && rv){
+ s = p + 2; /* write UNIX EOL */
+ while((rv = write(((PIPE_S *)stream)->out.d,"\n",1)) != 1)
+ if(rv < 0 && errno != EINTR){
+ rv = 0;
+ break;
+ }
+ }
+ else
+ break;
+ }
+
+ return(rv);
+}
+#endif
+
+
+/**************** "PIPE" READING POSTING I/O ROUTINES ****************/
+
+
+/*
+ * helpful def's
+ */
+#define S(X) ((PIPE_S *)(X))
+#define GETBUFLEN (4 * MAILTMPLEN)
+
+
+void *
+piped_smtp_open (char *host, char *service, long unsigned int port)
+{
+ char *postcmd;
+ void *rv = NULL;
+
+ if(strucmp(host, "localhost")){
+ char tmp[MAILTMPLEN];
+
+ snprintf(tmp, sizeof(tmp), _("Unexpected hostname for piped SMTP: %.*s"),
+ sizeof(tmp)-50, host);
+ tmp[sizeof(tmp)-1] = '\0';
+ mm_log(tmp, ERROR);
+ }
+ else if((postcmd = smtp_command(ps_global->c_client_error, sizeof(ps_global->c_client_error))) != NULL){
+ rv = open_system_pipe(postcmd, NULL, NULL,
+ PIPE_READ|PIPE_STDERR|PIPE_WRITE|PIPE_PROT|PIPE_NOSHELL|PIPE_DESC,
+ 0, NULL, pipe_report_error);
+ fs_give((void **) &postcmd);
+ }
+ else
+ mm_log(ps_global->c_client_error, ERROR);
+
+ return(rv);
+}
+
+
+void *
+piped_aopen (NETMBX *mb, char *service, char *user)
+{
+ return(NULL);
+}
+
+
+/*
+ * piped_soutr - Replacement for tcp_soutr that writes one of our
+ * pipes rather than a tcp stream
+ */
+long
+piped_soutr (void *stream, char *s)
+{
+ return(piped_sout(stream, s, strlen(s)));
+}
+
+
+/*
+ * piped_sout - Replacement for tcp_soutr that writes one of our
+ * pipes rather than a tcp stream
+ */
+long
+piped_sout (void *stream, char *s, long unsigned int size)
+{
+ int i, o;
+
+ if(S(stream)->out.d < 0)
+ return(0L);
+
+ if((i = (int) size) != 0){
+ while((o = write(S(stream)->out.d, s, i)) != i)
+ if(o < 0){
+ if(errno != EINTR){
+ piped_abort(stream);
+ return(0L);
+ }
+ }
+ else{
+ s += o; /* try again, fix up counts */
+ i -= o;
+ }
+ }
+
+ return(1L);
+}
+
+
+/*
+ * piped_getline - Replacement for tcp_getline that reads one
+ * of our pipes rather than a tcp pipe
+ *
+ * C-client expects that the \r\n will be stripped off.
+ */
+char *
+piped_getline (void *stream)
+{
+ static int cnt;
+ static char *ptr;
+ int n, m;
+ char *ret, *s, *sp, c = '\0', d;
+
+ if(S(stream)->in.d < 0)
+ return(NULL);
+
+ if(!S(stream)->tmp){ /* initialize! */
+ /* alloc space to collect input (freed in close_system_pipe) */
+ S(stream)->tmp = (char *) fs_get(GETBUFLEN);
+ memset(S(stream)->tmp, 0, GETBUFLEN);
+ cnt = -1;
+ }
+
+ while(cnt < 0){
+ while((cnt = read(S(stream)->in.d, S(stream)->tmp, GETBUFLEN)) < 0)
+ if(errno != EINTR){
+ piped_abort(stream);
+ return(NULL);
+ }
+
+ if(cnt == 0){
+ piped_abort(stream);
+ return(NULL);
+ }
+
+ ptr = S(stream)->tmp;
+ }
+
+ s = ptr;
+ n = 0;
+ while(cnt--){
+ d = *ptr++;
+ if((c == '\015') && (d == '\012')){
+ ret = (char *)fs_get (n--);
+ memcpy(ret, s, n);
+ ret[n] = '\0';
+ return(ret);
+ }
+
+ n++;
+ c = d;
+ }
+ /* copy partial string from buffer */
+ memcpy((ret = sp = (char *) fs_get (n)), s, n);
+ /* get more data */
+ while(cnt < 0){
+ memset(S(stream)->tmp, 0, GETBUFLEN);
+ while((cnt = read(S(stream)->in.d, S(stream)->tmp, GETBUFLEN)) < 0)
+ if(errno != EINTR){
+ fs_give((void **) &ret);
+ piped_abort(stream);
+ return(NULL);
+ }
+
+ if(cnt == 0){
+ if(n > 0)
+ ret[n-1] = '\0'; /* to try to get error message logged */
+ else{
+ piped_abort(stream);
+ return(NULL);
+ }
+ }
+
+ ptr = S(stream)->tmp;
+ }
+
+ if(c == '\015' && *ptr == '\012'){
+ ptr++;
+ cnt--;
+ ret[n - 1] = '\0'; /* tie off string with null */
+ }
+ else if ((s = piped_getline(stream)) != NULL) {
+ ret = (char *) fs_get(n + 1 + (m = strlen (s)));
+ memcpy(ret, sp, n); /* copy first part */
+ memcpy(ret + n, s, m); /* and second part */
+ fs_give((void **) &sp); /* flush first part */
+ fs_give((void **) &s); /* flush second part */
+ ret[n + m] = '\0'; /* tie off string with null */
+ }
+
+ return(ret);
+}
+
+
+/*
+ * piped_close - Replacement for tcp_close that closes pipes to our
+ * child rather than a tcp connection
+ */
+void
+piped_close(void *stream)
+{
+ /*
+ * Uninstall our hooks into smtp_send since it's being used by
+ * the nntp driver as well...
+ */
+ (void) close_system_pipe((PIPE_S **) &stream, NULL, NULL);
+}
+
+
+/*
+ * piped_abort - close down the pipe we were using to post
+ */
+void
+piped_abort(void *stream)
+{
+ if(S(stream)->in.d >= 0){
+ close(S(stream)->in.d);
+ S(stream)->in.d = -1;
+ }
+
+ if(S(stream)->out.d){
+ close(S(stream)->out.d);
+ S(stream)->out.d = -1;
+ }
+}
+
+
+char *
+piped_host(void *stream)
+{
+ return(ps_global->hostname ? ps_global->hostname : "localhost");
+}
+
+
+unsigned long
+piped_port(void *stream)
+{
+ return(0L);
+}
diff --git a/pith/send.h b/pith/send.h
new file mode 100644
index 00000000..69d763fc
--- /dev/null
+++ b/pith/send.h
@@ -0,0 +1,271 @@
+/*
+ * $Id: send.h 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_SEND_INCLUDED
+#define PITH_SEND_INCLUDED
+
+
+#include "../pith/context.h"
+#include "../pith/pattern.h"
+#include "../pith/repltype.h"
+#include "../pith/store.h"
+#include "../pith/osdep/pipe.h"
+
+
+#ifndef TCPSTREAM
+#define TCPSTREAM void
+#endif
+
+#define MIME_VER "MIME-Version: 1.0\015\012"
+
+#define UNKNOWN_CHARSET "X-UNKNOWN"
+
+#define OUR_HDRS_LIST "X-Our-Headers"
+
+
+/*
+ * Redraft flags...
+ */
+#define REDRAFT_NONE 0
+#define REDRAFT_PPND 0x01
+#define REDRAFT_DEL 0x02
+#define REDRAFT_HTML 0x04
+
+
+/*
+ * Child posting control structure
+ */
+typedef struct post_s {
+ int pid; /* proc id of child doing posting */
+ int status; /* child's exit status */
+ char *fcc; /* fcc we may have copied */
+} POST_S;
+
+
+/*------------------------------
+ Structures and enum used to expand the envelope structure to
+ support user defined headers. PINEFIELDs are sort of used for two
+ different purposes. The main use is to store information about headers
+ in pine_send. There is a pf for every header. It is also used for the
+ related task of parsing the customized-hdrs and role->cstm headers and
+ storing information about those.
+ ----*/
+
+typedef enum {FreeText, Address, Fcc,
+ Attachment, Subject, TypeUnknown} FieldType;
+typedef enum {NoMatch = 0, /* no match for this header */
+ UseAsDef=1, /* use only if no value set yet */
+ Combine=2, /* combine if News, To, Cc, Bcc, else
+ replace existing value */
+ Replace=3} CustomType; /* replace existing value */
+
+typedef struct pine_field {
+ char *name; /* field's literal name */
+ FieldType type; /* field's type */
+ unsigned canedit:1; /* allow editing of this field */
+ unsigned writehdr:1; /* write rfc822 header for field */
+ unsigned localcopy:1; /* copy to fcc or postponed */
+ unsigned rcptto:1; /* send to this if type Address */
+ unsigned posterr:1; /* posting error occured in field */
+ /* the next three fields are used only for customized-hdrs and friends */
+ unsigned standard:1; /* this hdr already in pf_template */
+ CustomType cstmtype; /* for customized-hdrs and r->cstm */
+ char *val; /* field's config'd value */
+ ADDRESS **addr; /* used if type is Address */
+ char *scratch; /* scratch pointer for Address type */
+ char **text; /* field's free text form */
+ char *textbuf; /* need to free this when done */
+ void *extdata; /* hook for extra data pointer */
+ struct pine_field *next; /* next pine field */
+} PINEFIELD;
+
+
+typedef struct {
+ ENVELOPE *env; /* standard c-client envelope */
+ PINEFIELD *local; /* this is all of the headers */
+ PINEFIELD *custom; /* ptr to start of custom headers */
+ PINEFIELD **sending_order; /* array giving writing order of hdrs */
+} METAENV;
+
+
+/*
+ * Return values for check_address()
+ */
+#define CA_OK 0 /* Address is OK */
+#define CA_EMPTY 1 /* Address is OK, but no deliverable addrs */
+#define CA_BAD -1 /* Address is bogus */
+
+
+
+/*
+ * call_mailer bigresult_f flags
+ */
+#define CM_BR_VERBOSE 0x01
+#define CM_BR_ERROR 0x02
+
+
+/*
+ * call_mailer flags
+ */
+#define CM_NONE 0x00
+#define CM_VERBOSE 0x01 /* request verbose mode */
+#define CM_DSN_NEVER 0x02 /* request NO DSN */
+#define CM_DSN_DELAY 0x04 /* DSN delay notification */
+#define CM_DSN_SUCCESS 0x08 /* DSN success notification */
+#define CM_DSN_FULL 0x10 /* DSN full notificiation */
+#define CM_DSN_SHOW 0x20 /* show DSN result (place holder) */
+
+
+#ifdef DEBUG_PLUS
+#define TIME_STAMP(str, l) { \
+ struct timeval tp; \
+ struct timezone tzp; \
+ if(gettimeofday(&tp, &tzp) == 0) \
+ dprint((l, \
+ "\nKACHUNK (%s) : %.8s.%3.3ld\n", \
+ str, ctime(&tp.tv_sec) + 11, \
+ tp.tv_usec));\
+ }
+#else /* !DEBUG_PLUS */
+#define TIME_STAMP(str, l)
+#endif /* !DEBUG_PLUS */
+
+/*
+ * Most number of errors call_mailer should report
+ */
+#define MAX_ADDR_ERROR 2 /* Only display 2 address errors */
+
+
+#define FCC_SOURCE CharStar
+
+
+struct local_message_copy {
+ STORE_S *so;
+ unsigned text_only:1;
+ unsigned all_written:1;
+ unsigned text_written:1;
+};
+
+
+#define TONAME "To"
+#define CCNAME "cc"
+#define SUBJNAME "Subject"
+#define PRIORITYNAME "X-Priority"
+
+
+/*
+ * Note, these are in the same order in the he_template and pf_template arrays.
+ * The typedef is just a way to get these variables automatically defined in order.
+ */
+typedef enum { N_AUTHRCVD = 0
+ , N_FROM
+ , N_REPLYTO
+ , N_TO
+ , N_CC
+ , N_BCC
+ , N_NEWS
+ , N_FCC
+ , N_LCC
+ , N_ATTCH
+ , N_SUBJ
+ , N_REF
+ , N_DATE
+ , N_INREPLY
+ , N_MSGID
+ , N_PRIORITY
+ , N_USERAGENT
+ , N_NOBODY
+ , N_POSTERR
+ , N_RPLUID
+ , N_RPLMBOX
+ , N_SMTP
+ , N_NNTP
+ , N_CURPOS
+ , N_OURREPLYTO
+ , N_OURHDRS
+ , N_SENDER
+} dummy_enum;
+
+
+typedef struct {
+ int val;
+ char *desc;
+} PRIORITY_S;
+
+
+/* ugly globals we'd like to get rid of */
+extern struct local_message_copy lmc;
+extern char verbose_requested;
+extern unsigned char dsn_requested;
+extern PRIORITY_S priorities[];
+extern PINEFIELD pf_template[];
+
+
+/* exported protoypes */
+int postponed_stream(MAILSTREAM **, char *, char *, int);
+int redraft_work(MAILSTREAM **, long, ENVELOPE **, BODY **, char **, char **,
+ REPLY_S **, REDRAFT_POS_S **, PINEFIELD **, ACTION_S **, int, STORE_S *);
+int redraft_cleanup(MAILSTREAM **, int, int);
+void simple_header_parse(char *, char **, char **);
+REPLY_S *build_reply_uid(char *);
+METAENV *pine_new_env(ENVELOPE *, char **, char ***, PINEFIELD *);
+void pine_free_env(METAENV **);
+int check_addresses(METAENV *);
+void update_answered_flags(REPLY_S *);
+ADDRESS *phone_home_from(void);
+unsigned int phone_home_hash(char *);
+int call_mailer(METAENV *, BODY *, char **, int, void (*)(char *, int),
+ void (*)(PIPE_S *, int, void *));
+int write_postponed(METAENV *, BODY *);
+int commence_fcc(char *, CONTEXT_S **, int);
+int wrapup_fcc(char *, CONTEXT_S *, METAENV *, BODY *);
+STORE_S *open_fcc(char *, CONTEXT_S **, int, char *, char *);
+int write_fcc(char *, CONTEXT_S *, STORE_S *, MAILSTREAM *, char *, char *);
+BODY *first_text_8bit(BODY *);
+ADDRESS *generate_from(void);
+void set_mime_type_by_grope(BODY *);
+void set_charset_possibly_to_ascii(BODY *, char *);
+void set_parameter(PARAMETER **, char *, char *);
+void pine_encode_body(BODY *);
+int pine_header_line(char *, METAENV *, char *, soutr_t, TCPSTREAM *, int, int);
+char *encode_header_value(char *, size_t, unsigned char *, char *, int);
+int pine_address_line(char *, METAENV *, ADDRESS *, soutr_t, TCPSTREAM *, int, int);
+long pine_rfc822_header(METAENV *, BODY *, soutr_t, TCPSTREAM *);
+long pine_rfc822_output(METAENV *, BODY *, soutr_t, TCPSTREAM *);
+void pine_free_body(BODY **);
+long send_body_size(BODY *);
+void pine_smtp_verbose_out(char *);
+int pine_header_forbidden(char *);
+int hdr_is_in_list(char *, PINEFIELD *);
+int count_custom_hdrs_pf(PINEFIELD *, int);
+int count_custom_hdrs_list(char **);
+CustomType set_default_hdrval(PINEFIELD *, PINEFIELD *);
+FieldType pine_header_standard(char *);
+void customized_hdr_setup(PINEFIELD *, char **, CustomType);
+void add_defaults_from_list(PINEFIELD *, char **);
+PINEFIELD *parse_custom_hdrs(char **, CustomType);
+PINEFIELD *combine_custom_headers(PINEFIELD *, PINEFIELD *);
+void free_customs(PINEFIELD *);
+int encode_whole_header(char *, METAENV *);
+int news_poster(METAENV *, BODY *, char **, void (*)(PIPE_S *, int, void *));
+PINEFIELD *set_priority_header(METAENV *header, char *value);
+void pine_free_body_data(BODY *);
+void free_charsetchecker(void);
+long pine_rfc822_output_body(BODY *,soutr_t,TCPSTREAM *);
+int pine_write_body_header(BODY *, soutr_t, TCPSTREAM *);
+
+
+#endif /* PITH_SEND_INCLUDED */
diff --git a/pith/sequence.c b/pith/sequence.c
new file mode 100644
index 00000000..577041dc
--- /dev/null
+++ b/pith/sequence.c
@@ -0,0 +1,460 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: sequence.c 1012 2008-03-26 00:44:22Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/sequence.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/msgno.h"
+#include "../pith/flag.h"
+#include "../pith/thread.h"
+#include "../pith/icache.h"
+
+
+/*
+ * Internal prototypes
+ */
+
+
+/*----------------------------------------------------------------------
+ Build comma delimited list of selected messages
+
+ Args: stream -- mail stream to use for flag testing
+ msgmap -- message number struct of to build selected messages in
+ count -- pointer to place to write number of comma delimited
+ mark -- mark manually undeleted so we don't refilter right away
+
+ Returns: malloc'd string containing sequence, else NULL if
+ no messages in msgmap with local "selected" flag.
+ ----*/
+char *
+selected_sequence(MAILSTREAM *stream, MSGNO_S *msgmap, long int *count, int mark)
+{
+ long i;
+ MESSAGECACHE *mc;
+
+ if(!stream)
+ return(NULL);
+
+ /*
+ * The plan here is to use the c-client elt's "sequence" bit
+ * to work around any orderings or exclusions in pine's internal
+ * mapping that might cause the sequence to be artificially
+ * lengthy. It's probably cheaper to run down the elt list
+ * twice rather than call nm_raw2m() for each message as
+ * we run down the elt list once...
+ */
+ for(i = 1L; i <= stream->nmsgs; i++)
+ if((mc = mail_elt(stream, i)) != NULL)
+ mc->sequence = 0;
+
+ for(i = 1L; i <= mn_get_total(msgmap); i++)
+ if(get_lflag(stream, msgmap, i, MN_SLCT)){
+ long rawno;
+ int exbits = 0;
+
+ /*
+ * Forget we knew about it, and set "add to sequence"
+ * bit...
+ */
+ clear_index_cache_ent(stream, i, 0);
+ rawno = mn_m2raw(msgmap, i);
+ if(rawno > 0L && rawno <= stream->nmsgs
+ && (mc = mail_elt(stream, rawno)))
+ mc->sequence = 1;
+
+ /*
+ * Mark this message manually flagged so we don't re-filter it
+ * with a filter which only sets flags.
+ */
+ if(mark){
+ if(msgno_exceptions(stream, rawno, "0", &exbits, FALSE))
+ exbits |= MSG_EX_MANUNDEL;
+ else
+ exbits = MSG_EX_MANUNDEL;
+
+ msgno_exceptions(stream, rawno, "0", &exbits, TRUE);
+ }
+ }
+
+ return(build_sequence(stream, NULL, count));
+}
+
+
+/*----------------------------------------------------------------------
+ Build comma delimited list of messages current in msgmap which have all
+ flags matching the arguments
+
+ Args: stream -- mail stream to use for flag testing
+ msgmap -- only consider messages selected in this msgmap
+ flag -- flags to match against
+ count -- pointer to place to return number of comma delimited
+ mark -- mark index cache entry changed, and count state change
+ kw_on -- if flag contains F_KEYWORD, this is
+ the array of keywords to be checked
+ kw_off -- if flag contains F_UNKEYWORD, this is
+ the array of keywords to be checked
+
+ Returns: malloc'd string containing sequence, else NULL if
+ no messages in msgmap with local "selected" flag (a flag
+ of zero means all current msgs).
+ ----*/
+char *
+currentf_sequence(MAILSTREAM *stream, MSGNO_S *msgmap, long int flag,
+ long int *count, int mark, char **kw_on, char **kw_off)
+{
+ char *seq, **t;
+ long i, rawno;
+ int exbits;
+ MESSAGECACHE *mc;
+
+ if(!stream)
+ return(NULL);
+
+ /* First, make sure elts are valid for all the interesting messages */
+ if((seq = invalid_elt_sequence(stream, msgmap)) != NULL){
+ pine_mail_fetch_flags(stream, seq, NIL);
+ fs_give((void **) &seq);
+ }
+
+ for(i = 1L; i <= stream->nmsgs; i++)
+ if((mc = mail_elt(stream, i)) != NULL)
+ mc->sequence = 0; /* clear "sequence" bits */
+
+ for(i = mn_first_cur(msgmap); i > 0L; i = mn_next_cur(msgmap)){
+ /* if not already set, go on... */
+ rawno = mn_m2raw(msgmap, i);
+ mc = (rawno > 0L && rawno <= stream->nmsgs)
+ ? mail_elt(stream, rawno) : NULL;
+ if(!mc)
+ continue;
+
+ if((flag == 0)
+ || ((flag & F_DEL) && mc->deleted)
+ || ((flag & F_UNDEL) && !mc->deleted)
+ || ((flag & F_SEEN) && mc->seen)
+ || ((flag & F_UNSEEN) && !mc->seen)
+ || ((flag & F_ANS) && mc->answered)
+ || ((flag & F_UNANS) && !mc->answered)
+ || ((flag & F_FLAG) && mc->flagged)
+ || ((flag & F_UNFLAG) && !mc->flagged)){
+ mc->sequence = 1; /* set "sequence" flag */
+ }
+
+ /* check for forwarded status */
+ if(!mc->sequence
+ && (flag & F_FWD)
+ && user_flag_is_set(stream, rawno, FORWARDED_FLAG)){
+ mc->sequence = 1;
+ }
+
+ if(!mc->sequence
+ && (flag & F_UNFWD)
+ && !user_flag_is_set(stream, rawno, FORWARDED_FLAG)){
+ mc->sequence = 1;
+ }
+
+ /* check for user keywords or not */
+ if(!mc->sequence && flag & F_KEYWORD && kw_on){
+ for(t = kw_on; !mc->sequence && *t; t++)
+ if(user_flag_is_set(stream, rawno, *t))
+ mc->sequence = 1;
+ }
+ else if(!mc->sequence && flag & F_UNKEYWORD && kw_off){
+ for(t = kw_off; !mc->sequence && *t; t++)
+ if(!user_flag_is_set(stream, rawno, *t))
+ mc->sequence = 1;
+ }
+
+ if(mc->sequence){
+ if(mark){
+ if(THRD_INDX()){
+ PINETHRD_S *thrd;
+ long t;
+
+ /* clear thread index line instead of index index line */
+ thrd = fetch_thread(stream, mn_m2raw(msgmap, i));
+ if(thrd && thrd->top
+ && (thrd=fetch_thread(stream,thrd->top))
+ && (t = mn_raw2m(msgmap, thrd->rawno)))
+ clear_index_cache_ent(stream, t, 0);
+ }
+ else
+ clear_index_cache_ent(stream, i, 0);/* force new index line */
+
+ /*
+ * Mark this message manually flagged so we don't re-filter it
+ * with a filter which only sets flags.
+ */
+ exbits = 0;
+ if(msgno_exceptions(stream, rawno, "0", &exbits, FALSE))
+ exbits |= MSG_EX_MANUNDEL;
+ else
+ exbits = MSG_EX_MANUNDEL;
+
+ msgno_exceptions(stream, rawno, "0", &exbits, TRUE);
+ }
+ }
+ }
+
+ return(build_sequence(stream, NULL, count));
+}
+
+
+/*----------------------------------------------------------------------
+ Return sequence numbers of messages with invalid MESSAGECACHEs
+
+ Args: stream -- mail stream to use for flag testing
+ msgmap -- message number struct of to build selected messages in
+
+ Returns: malloc'd string containing sequence, else NULL if
+ no messages in msgmap with local "selected" flag (a flag
+ of zero means all current msgs).
+ ----*/
+char *
+invalid_elt_sequence(MAILSTREAM *stream, MSGNO_S *msgmap)
+{
+ long i, rawno;
+ MESSAGECACHE *mc;
+
+ if(!stream)
+ return(NULL);
+
+ for(i = 1L; i <= stream->nmsgs; i++)
+ if((mc = mail_elt(stream, i)) != NULL)
+ mc->sequence = 0; /* clear "sequence" bits */
+
+ for(i = mn_first_cur(msgmap); i > 0L; i = mn_next_cur(msgmap))
+ if((rawno = mn_m2raw(msgmap, i)) > 0L && rawno <= stream->nmsgs
+ && (mc = mail_elt(stream, rawno)) && !mc->valid)
+ mc->sequence = 1;
+
+ return(build_sequence(stream, NULL, NULL));
+}
+
+
+/*----------------------------------------------------------------------
+ Build comma delimited list of messages with elt "sequence" bit set
+
+ Args: stream -- mail stream to use for flag testing
+ msgmap -- struct containing sort to build sequence in
+ count -- pointer to place to write number of comma delimited
+ NOTE: if non-zero, it's a clue as to how many messages
+ have the sequence bit lit.
+
+ Returns: malloc'd string containing sequence, else NULL if
+ no messages in msgmap with elt's "sequence" bit set
+ ----*/
+char *
+build_sequence(MAILSTREAM *stream, MSGNO_S *msgmap, long int *count)
+{
+#define SEQ_INCREMENT 128
+ long n = 0L, i, x, lastn = 0L, runstart = 0L;
+ size_t size = SEQ_INCREMENT;
+ char *seq = NULL, *p;
+ MESSAGECACHE *mc;
+
+ if(!stream)
+ return(NULL);
+
+ if(count){
+ if(*count > 0L)
+ size = MAX(size, MIN((*count) * 4, 16384));
+
+ *count = 0L;
+ }
+
+ for(x = 1L; x <= stream->nmsgs; x++){
+ if(msgmap){
+ if((i = mn_m2raw(msgmap, x)) == 0L)
+ continue;
+ }
+ else
+ i = x;
+
+ if(i > 0L && i <= stream->nmsgs
+ && (mc = mail_elt(stream, i)) && mc->sequence){
+ n++;
+ if(!seq) /* initialize if needed */
+ seq = p = fs_get(size);
+
+ /*
+ * This code will coalesce the ascending runs of
+ * sequence numbers, but fails to break sequences
+ * into a reasonably sensible length for imapd's to
+ * swallow (reasonable addtition to c-client?)...
+ */
+ if(lastn){ /* if may be in a run */
+ if(lastn + 1L == i){ /* and its the next raw num */
+ lastn = i; /* skip writing anything... */
+ continue;
+ }
+ else if(runstart != lastn){
+ *p++ = (runstart + 1L == lastn) ? ',' : ':';
+ sstrncpy(&p, long2string(lastn), size-(p-seq));
+ } /* wrote end of run */
+ }
+
+ runstart = lastn = i; /* remember last raw num */
+
+ if(n > 1L && (p-seq) < size) /* !first num, write delim */
+ *p++ = ',';
+
+ if(size - (p - seq) < 16){ /* room for two more nums? */
+ size_t offset = p - seq; /* grow the sequence array */
+ size += SEQ_INCREMENT;
+ fs_resize((void **)&seq, size);
+ p = seq + offset;
+ }
+
+ sstrncpy(&p, long2string(i), size-(p-seq)); /* write raw number */
+ }
+ }
+
+ if(lastn && runstart != lastn){ /* were in a run? */
+ if((p-seq) < size) /* !first num, write delim */
+ *p++ = (runstart + 1L == lastn) ? ',' : ':';
+
+ sstrncpy(&p, long2string(lastn), size-(p-seq)); /* write the trailing num */
+ }
+
+ if(seq && (p-seq) < size) /* if sequence, tie it off */
+ *p = '\0';
+
+ if(seq)
+ seq[size-1] = '\0';
+
+ if(count)
+ *count = n;
+
+ return(seq);
+}
+
+
+/*----------------------------------------------------------------------
+ If any messages flagged "selected", fake the "currently selected" array
+
+ Args: map -- message number struct of to build selected messages in
+
+ OK folks, here's the tradeoff: either all the functions have to
+ know if the user want's to deal with the "current" hilited message
+ or the list of currently "selected" messages, *or* we just
+ wrap the call to these functions with some glue that tweeks
+ what these functions see as the "current" message list, and let them
+ do their thing.
+ ----*/
+int
+pseudo_selected(MAILSTREAM *stream, MSGNO_S *map)
+{
+ long i, later = 0L;
+
+ if(any_lflagged(map, MN_SLCT)){
+ map->hilited = mn_m2raw(map, mn_get_cur(map));
+
+ for(i = 1L; i <= mn_get_total(map); i++)
+ if(get_lflag(stream, map, i, MN_SLCT)){
+ if(!later++){
+ mn_set_cur(map, i);
+ }
+ else{
+ mn_add_cur(map, i);
+ }
+ }
+
+ return(1);
+ }
+
+ return(0);
+}
+
+
+/*----------------------------------------------------------------------
+ Antidote for the monkey business committed above
+
+ Args: map -- message number struct of to build selected messages in
+
+ ----*/
+void
+restore_selected(MSGNO_S *map)
+{
+ if(map->hilited){
+ mn_reset_cur(map, mn_raw2m(map, map->hilited));
+ map->hilited = 0L;
+ }
+}
+
+
+/*
+ * Return a search set which can be used to limit the search to a smaller set,
+ * for performance reasons. The caller must still work correctly if we return
+ * the whole set (or NULL) here, because we may not be able to send the full
+ * search set over IMAP. In cases where the search set description is getting
+ * too large we send a search set which contains all of the relevant messages.
+ * It may contain more.
+ *
+ * Args stream
+ * narrow -- If set, we are narrowing our selection (restrict to
+ * messages with MN_SLCT already set) or if not set,
+ * we are broadening (so we may look only at messages
+ * with MN_SLCT not set)
+ *
+ * Returns - allocated search set or NULL. Caller is responsible for freeing it.
+ */
+SEARCHSET *
+limiting_searchset(MAILSTREAM *stream, int narrow)
+{
+ long n, run;
+ int cnt = 0;
+ SEARCHSET *full_set = NULL, **set;
+
+ /*
+ * If we're talking to anything other than a server older than
+ * imap 4rev1, build a searchset otherwise it'll choke.
+ */
+ if(!(is_imap_stream(stream) && !modern_imap_stream(stream))){
+ for(n = 1L, set = &full_set, run = 0L; n <= stream->nmsgs; n++)
+ /* test for end of run */
+ if(get_lflag(stream, NULL, n, MN_EXLD)
+ || (narrow && !get_lflag(stream, NULL, n, MN_SLCT))
+ || (!narrow && get_lflag(stream, NULL, n, MN_SLCT))){
+ if(run){ /* previous selected? */
+ set = &(*set)->next;
+ run = 0L;
+ }
+ }
+ else if(run++){ /* next in run */
+ (*set)->last = n;
+ }
+ else{ /* start of run */
+ *set = mail_newsearchset();
+ (*set)->first = (*set)->last = n;
+
+ /*
+ * Make this last set cover the rest of the messages.
+ * We could be fancier about this but it probably isn't
+ * worth the trouble.
+ */
+ if(++cnt > 100){
+ (*set)->last = stream->nmsgs;
+ break;
+ }
+ }
+ }
+
+ return(full_set);
+}
diff --git a/pith/sequence.h b/pith/sequence.h
new file mode 100644
index 00000000..49305e0b
--- /dev/null
+++ b/pith/sequence.h
@@ -0,0 +1,33 @@
+/*
+ * $Id: sequence.h 1012 2008-03-26 00:44:22Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_SEQUENCE_INCLUDED
+#define PITH_SEQUENCE_INCLUDED
+
+
+#include "../pith/msgno.h"
+
+
+/* exported protoypes */
+char *selected_sequence(MAILSTREAM *, MSGNO_S *, long *, int);
+char *currentf_sequence(MAILSTREAM *, MSGNO_S *, long, long *, int, char **, char **);
+char *invalid_elt_sequence(MAILSTREAM *, MSGNO_S *);
+char *build_sequence(MAILSTREAM *, MSGNO_S *, long *);
+int pseudo_selected(MAILSTREAM *, MSGNO_S *);
+void restore_selected(MSGNO_S *);
+SEARCHSET *limiting_searchset(MAILSTREAM *, int);
+
+
+#endif /* PITH_SEQUENCE_INCLUDED */
diff --git a/pith/signal.h b/pith/signal.h
new file mode 100644
index 00000000..c55e351f
--- /dev/null
+++ b/pith/signal.h
@@ -0,0 +1,29 @@
+/*
+ * $Id: signal.h 1025 2008-04-08 22:59:38Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_SIGNAL_INCLUDED
+#define PITH_SIGNAL_INCLUDED
+
+
+/* exported protoypes */
+
+/* currently mandatory to implement stubs */
+
+int intr_handling_on(void);
+void intr_handling_off(void);
+
+
+#endif /* PITH_SIGNAL_INCLUDED */
diff --git a/pith/smime.c b/pith/smime.c
new file mode 100644
index 00000000..dc332a2a
--- /dev/null
+++ b/pith/smime.c
@@ -0,0 +1,2377 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: smime.c 1176 2008-09-29 21:16:42Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*
+ * This is based on a contribution from Jonathan Paisley
+ *
+ * File: smime.c
+ * Author: paisleyj@dcs.gla.ac.uk
+ * Date: 01/2001
+ */
+
+
+#include "../pith/headers.h"
+
+#ifdef SMIME
+
+#include "../pith/osdep/canaccess.h"
+#include "../pith/helptext.h"
+#include "../pith/store.h"
+#include "../pith/status.h"
+#include "../pith/detach.h"
+#include "../pith/conf.h"
+#include "../pith/smkeys.h"
+#include "../pith/smime.h"
+#include "../pith/mailpart.h"
+#include "../pith/reply.h"
+#include "../pith/tempfile.h"
+#include "../pith/readfile.h"
+#include "../pith/remote.h"
+
+#include <openssl/buffer.h>
+
+
+typedef enum {Public, Private, CACert} WhichCerts;
+
+
+/* internal prototypes */
+static void forget_private_keys(void);
+static int app_RAND_load_file(const char *file);
+static void openssl_extra_randomness(void);
+static int app_RAND_write_file(const char *file);
+static void smime_init(void);
+static const char *openssl_error_string(void);
+static void create_local_cache(char *base, BODY *b);
+static BIO *raw_part_to_bio(long msgno, const char *section);
+static long rfc822_output_func(void *b, char *string);
+static int load_private_key(PERSONAL_CERT *pcert);
+static void setup_pkcs7_body_for_signature(BODY *b, char *description,
+ char *type, char *filename);
+static BIO *body_to_bio(BODY *body);
+static BIO *bio_from_store(STORE_S *store);
+static STORE_S *get_part_contents(long msgno, const char *section);
+static PKCS7 *get_pkcs7_from_part(long msgno, const char *section);
+static int do_signature_verify(PKCS7 *p7, BIO *in, BIO *out);
+static int do_detached_signature_verify(BODY *b, long msgno, char *section);
+static PERSONAL_CERT *find_certificate_matching_pkcs7(PKCS7 *p7);
+static int do_decoding(BODY *b, long msgno, const char *section);
+static void free_smime_struct(SMIME_STUFF_S **smime);
+static void setup_storage_locations(void);
+static int copy_dir_to_container(WhichCerts which);
+static int copy_container_to_dir(WhichCerts which);
+
+
+int (*pith_opt_smime_get_passphrase)(void);
+
+
+static X509_STORE *s_cert_store;
+
+/* State management for randomness functions below */
+static int seeded = 0;
+static int egdsocket = 0;
+
+
+/*
+ * Forget any cached private keys
+ */
+static void
+forget_private_keys(void)
+{
+ PERSONAL_CERT *pcert;
+ size_t len;
+ volatile char *p;
+
+ dprint((9, "forget_private_keys()"));
+ if(ps_global->smime){
+ for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
+ pcert;
+ pcert=pcert->next){
+
+ if(pcert->key){
+ EVP_PKEY_free(pcert->key);
+ pcert->key = NULL;
+ }
+ }
+
+ ps_global->smime->entered_passphrase = 0;
+ len = sizeof(ps_global->smime->passphrase);
+ p = ps_global->smime->passphrase;
+
+ while(len-- > 0)
+ *p++ = '\0';
+ }
+}
+
+
+/*
+ * taken from openssl/apps/app_rand.c
+ */
+static int
+app_RAND_load_file(const char *file)
+{
+ char buffer[200];
+
+ if(file == NULL)
+ file = RAND_file_name(buffer, sizeof buffer);
+ else if(RAND_egd(file) > 0){
+ /* we try if the given filename is an EGD socket.
+ if it is, we don't write anything back to the file. */
+ egdsocket = 1;
+ return 1;
+ }
+
+ if(file == NULL || !RAND_load_file(file, -1)){
+ if(RAND_status() == 0){
+ dprint((1, "unable to load 'random state'\n"));
+ dprint((1, "This means that the random number generator has not been seeded\n"));
+ dprint((1, "with much random data.\n"));
+ }
+
+ return 0;
+ }
+
+ seeded = 1;
+ return 1;
+}
+
+
+/*
+ * copied and fiddled from imap/src/osdep/unix/auth_ssl.c
+ */
+static void
+openssl_extra_randomness(void)
+{
+#if !defined(WIN32)
+ int fd;
+ unsigned long i;
+ char *tf = NULL;
+ char tmp[MAXPATH];
+ struct stat sbuf;
+ /* if system doesn't have /dev/urandom */
+ if(stat ("/dev/urandom", &sbuf)){
+ tmp[0] = '0';
+ tf = temp_nam(NULL, NULL);
+ if(tf){
+ strncpy(tmp, tf, sizeof(tmp));
+ tmp[sizeof(tmp)-1] = '\0';
+ fs_give((void **) &tf);
+ }
+
+ if((fd = open(tmp, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0)
+ i = (unsigned long) tmp;
+ else{
+ unlink(tmp); /* don't need the file */
+ fstat(fd, &sbuf); /* get information about the file */
+ i = sbuf.st_ino; /* remember its inode */
+ close(fd); /* or its descriptor */
+ }
+ /* not great but it'll have to do */
+ snprintf(tmp+strlen(tmp), sizeof(tmp)-strlen(tmp), "%.80s%lx%lx%lx",
+ tcp_serverhost (),i,
+ (unsigned long) (time (0) ^ gethostid ()),
+ (unsigned long) getpid ());
+ RAND_seed(tmp, strlen(tmp));
+ }
+#endif
+}
+
+
+/* taken from openssl/apps/app_rand.c */
+static int
+app_RAND_write_file(const char *file)
+{
+ char buffer[200];
+
+ if(egdsocket || !seeded)
+ /*
+ * If we did not manage to read the seed file,
+ * we should not write a low-entropy seed file back --
+ * it would suppress a crucial warning the next time
+ * we want to use it.
+ */
+ return 0;
+
+ if(file == NULL)
+ file = RAND_file_name(buffer, sizeof buffer);
+
+ if(file == NULL || !RAND_write_file(file)){
+ dprint((1, "unable to write 'random state'\n"));
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/* Installed as an atexit() handler to save the random data */
+void
+smime_deinit(void)
+{
+ dprint((9, "smime_deinit()"));
+ app_RAND_write_file(NULL);
+ free_smime_struct(&ps_global->smime);
+}
+
+
+/* Initialise openssl stuff if needed */
+static void
+smime_init(void)
+{
+ if(F_OFF(F_DONT_DO_SMIME, ps_global) && !(ps_global->smime && ps_global->smime->inited)){
+
+ dprint((9, "smime_init()"));
+ if(!ps_global->smime)
+ ps_global->smime = new_smime_struct();
+
+ setup_storage_locations();
+
+ s_cert_store = get_ca_store();
+
+ OpenSSL_add_all_algorithms();
+ ERR_load_crypto_strings();
+
+ app_RAND_load_file(NULL);
+
+ openssl_extra_randomness();
+ ps_global->smime->inited = 1;
+ }
+
+ ERR_clear_error();
+}
+
+
+static void
+setup_storage_locations(void)
+{
+ int publiccertcontainer = 0, privatekeycontainer = 0, cacertcontainer = 0;
+ char path[MAXPATH+1], *contents;
+
+ if(!ps_global->smime)
+ return;
+
+#ifdef APPLEKEYCHAIN
+ if(F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
+ ps_global->smime->publictype = Keychain;
+ }
+ else{
+#endif /* APPLEKEYCHAIN */
+ /* Public certificates in a container */
+ if(ps_global->VAR_PUBLICCERT_CONTAINER && ps_global->VAR_PUBLICCERT_CONTAINER[0]){
+
+ publiccertcontainer = 1;
+ contents = NULL;
+ path[0] = '\0';
+ if(!signature_path(ps_global->VAR_PUBLICCERT_CONTAINER, path, MAXPATH))
+ publiccertcontainer = 0;
+
+ if(publiccertcontainer && !IS_REMOTE(path)
+ && ps_global->VAR_OPER_DIR
+ && !in_dir(ps_global->VAR_OPER_DIR, path)){
+ q_status_message2(SM_ORDER | SM_DING, 3, 4,
+ /* TRANSLATORS: First arg is the directory name, second is
+ the file user wants to read but can't. */
+ _("Can't read file outside %s: %s"),
+ ps_global->VAR_OPER_DIR, path);
+ publiccertcontainer = 0;
+ }
+
+ if(publiccertcontainer
+ && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
+ if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
+ &&
+ !(contents = read_file(path, READ_FROM_LOCALE)))
+ publiccertcontainer = 0;
+ }
+
+ if(publiccertcontainer && path[0]){
+ ps_global->smime->publictype = Container;
+ ps_global->smime->publicpath = cpystr(path);
+
+ if(contents){
+ ps_global->smime->publiccontent = contents;
+ ps_global->smime->publiccertlist = mem_to_certlist(contents);
+ }
+ }
+ }
+
+ /* Public certificates in a directory of files */
+ if(!publiccertcontainer){
+ ps_global->smime->publictype = Directory;
+
+ path[0] = '\0';
+ if(!(signature_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
+ && !IS_REMOTE(path)))
+ ps_global->smime->publictype = Nada;
+ else if(can_access(path, ACCESS_EXISTS)){
+ if(our_mkpath(path, 0700)){
+ q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
+ ps_global->smime->publictype = Nada;
+ }
+ }
+
+ if(ps_global->smime->publictype == Directory)
+ ps_global->smime->publicpath = cpystr(path);
+ }
+
+#ifdef APPLEKEYCHAIN
+ }
+#endif /* APPLEKEYCHAIN */
+
+ /* private keys in a container */
+ if(ps_global->VAR_PRIVATEKEY_CONTAINER && ps_global->VAR_PRIVATEKEY_CONTAINER[0]){
+
+ privatekeycontainer = 1;
+ contents = NULL;
+ path[0] = '\0';
+ if(!signature_path(ps_global->VAR_PRIVATEKEY_CONTAINER, path, MAXPATH))
+ privatekeycontainer = 0;
+
+ if(privatekeycontainer && !IS_REMOTE(path)
+ && ps_global->VAR_OPER_DIR
+ && !in_dir(ps_global->VAR_OPER_DIR, path)){
+ q_status_message2(SM_ORDER | SM_DING, 3, 4,
+ /* TRANSLATORS: First arg is the directory name, second is
+ the file user wants to read but can't. */
+ _("Can't read file outside %s: %s"),
+ ps_global->VAR_OPER_DIR, path);
+ privatekeycontainer = 0;
+ }
+
+ if(privatekeycontainer
+ && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
+ if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
+ &&
+ !(contents = read_file(path, READ_FROM_LOCALE)))
+ privatekeycontainer = 0;
+ }
+
+ if(privatekeycontainer && path[0]){
+ ps_global->smime->privatetype = Container;
+ ps_global->smime->privatepath = cpystr(path);
+
+ if(contents){
+ ps_global->smime->privatecontent = contents;
+ ps_global->smime->personal_certs = mem_to_personal_certs(contents);
+ }
+ }
+ }
+
+ /* private keys in a directory of files */
+ if(!privatekeycontainer){
+ PERSONAL_CERT *result = NULL;
+
+ ps_global->smime->privatetype = Directory;
+
+ path[0] = '\0';
+ if(!(signature_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
+ && !IS_REMOTE(path)))
+ ps_global->smime->privatetype = Nada;
+ else if(can_access(path, ACCESS_EXISTS)){
+ if(our_mkpath(path, 0700)){
+ q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
+ ps_global->smime->privatetype = Nada;
+ }
+ }
+
+ if(ps_global->smime->privatetype == Directory){
+ char buf2[MAXPATH];
+ struct dirent *d;
+ DIR *dirp;
+
+ ps_global->smime->privatepath = cpystr(path);
+ dirp = opendir(path);
+ if(dirp){
+
+ while((d=readdir(dirp)) != NULL){
+ X509 *cert;
+ size_t ll;
+
+ if((ll=strlen(d->d_name)) && ll > 4 && !strcmp(d->d_name+ll-4, ".key")){
+
+ /* copy file name to temp buffer */
+ strncpy(buf2, d->d_name, sizeof(buf2)-1);
+ buf2[sizeof(buf2)-1] = '\0';
+ /* chop off ".key" trailier */
+ buf2[strlen(buf2)-4] = 0;
+ /* Look for certificate */
+ cert = get_cert_for(buf2);
+
+ if(cert){
+ PERSONAL_CERT *pc;
+
+ /* create a new PERSONAL_CERT, fill it in */
+
+ pc = (PERSONAL_CERT *) fs_get(sizeof(*pc));
+ pc->cert = cert;
+ pc->name = cpystr(buf2);
+
+ /* Try to load the key with an empty password */
+ pc->key = load_key(pc, "");
+
+ pc->next = result;
+ result = pc;
+ }
+ }
+ }
+
+ closedir(dirp);
+ }
+ }
+
+ ps_global->smime->personal_certs = result;
+ }
+
+ /* extra cacerts in a container */
+ if(ps_global->VAR_CACERT_CONTAINER && ps_global->VAR_CACERT_CONTAINER[0]){
+
+ cacertcontainer = 1;
+ contents = NULL;
+ path[0] = '\0';
+ if(!signature_path(ps_global->VAR_CACERT_CONTAINER, path, MAXPATH))
+ cacertcontainer = 0;
+
+ if(cacertcontainer && !IS_REMOTE(path)
+ && ps_global->VAR_OPER_DIR
+ && !in_dir(ps_global->VAR_OPER_DIR, path)){
+ q_status_message2(SM_ORDER | SM_DING, 3, 4,
+ /* TRANSLATORS: First arg is the directory name, second is
+ the file user wants to read but can't. */
+ _("Can't read file outside %s: %s"),
+ ps_global->VAR_OPER_DIR, path);
+ cacertcontainer = 0;
+ }
+
+ if(cacertcontainer
+ && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
+ if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
+ &&
+ !(contents = read_file(path, READ_FROM_LOCALE)))
+ cacertcontainer = 0;
+ }
+
+ if(cacertcontainer && path[0]){
+ ps_global->smime->catype = Container;
+ ps_global->smime->capath = cpystr(path);
+ ps_global->smime->cacontent = contents;
+ }
+ }
+
+ if(!cacertcontainer){
+ ps_global->smime->catype = Directory;
+
+ path[0] = '\0';
+ if(!(signature_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
+ && !IS_REMOTE(path)))
+ ps_global->smime->catype = Nada;
+ else if(can_access(path, ACCESS_EXISTS)){
+ if(our_mkpath(path, 0700)){
+ q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
+ ps_global->smime->catype = Nada;
+ }
+ }
+
+ if(ps_global->smime->catype == Directory)
+ ps_global->smime->capath = cpystr(path);
+ }
+}
+
+
+int
+copy_publiccert_dir_to_container(void)
+{
+ return(copy_dir_to_container(Public));
+}
+
+
+int
+copy_publiccert_container_to_dir(void)
+{
+ return(copy_container_to_dir(Public));
+}
+
+
+int
+copy_privatecert_dir_to_container(void)
+{
+ return(copy_dir_to_container(Private));
+}
+
+
+int
+copy_privatecert_container_to_dir(void)
+{
+ return(copy_container_to_dir(Private));
+}
+
+
+int
+copy_cacert_dir_to_container(void)
+{
+ return(copy_dir_to_container(CACert));
+}
+
+
+int
+copy_cacert_container_to_dir(void)
+{
+ return(copy_container_to_dir(CACert));
+}
+
+
+/*
+ * returns 0 on success, -1 on failure
+ */
+int
+copy_dir_to_container(WhichCerts which)
+{
+ int ret = 0;
+ BIO *bio_out = NULL, *bio_in = NULL;
+ char srcpath[MAXPATH+1], dstpath[MAXPATH+1], emailaddr[MAXPATH], file[MAXPATH], line[4096];
+ char *tempfile = NULL;
+ DIR *dirp;
+ struct dirent *d;
+ REMDATA_S *rd = NULL;
+ char *configdir = NULL;
+ char *configpath = NULL;
+ char *filesuffix = NULL;
+
+ dprint((9, "copy_dir_to_container(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
+ smime_init();
+
+ srcpath[0] = '\0';
+ dstpath[0] = '\0';
+ file[0] = '\0';
+ emailaddr[0] = '\0';
+
+ if(which == Public){
+ configdir = ps_global->VAR_PUBLICCERT_DIR;
+ configpath = ps_global->smime->publicpath;
+ filesuffix = ".crt";
+ }
+ else if(which == Private){
+ configdir = ps_global->VAR_PRIVATEKEY_DIR;
+ configpath = ps_global->smime->privatepath;
+ filesuffix = ".key";
+ }
+ else if(which == CACert){
+ configdir = ps_global->VAR_CACERT_DIR;
+ configpath = ps_global->smime->capath;
+ filesuffix = ".crt";
+ }
+
+ if(!(configdir && configdir[0])){
+ q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
+ return -1;
+ }
+
+ if(!(configpath && configpath[0])){
+#ifdef APPLEKEYCHAIN
+ if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
+ q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
+ return -1;
+ }
+#endif /* APPLEKEYCHAIN */
+ q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
+ return -1;
+ }
+
+ if(!(filesuffix && strlen(filesuffix) == 4)){
+ return -1;
+ }
+
+
+ /*
+ * If there is a legit directory to read from set up the
+ * container file to write to.
+ */
+ if(signature_path(configdir, srcpath, MAXPATH) && !IS_REMOTE(srcpath)){
+
+ if(IS_REMOTE(configpath)){
+ rd = rd_create_remote(RemImap, configpath, REMOTE_SMIME_SUBTYPE,
+ NULL, "Error: ",
+ _("Can't access remote smime configuration."));
+ if(!rd)
+ return -1;
+
+ (void) rd_read_metadata(rd);
+
+ if(rd->access == MaybeRorW){
+ if(rd->read_status == 'R')
+ rd->access = ReadOnly;
+ else
+ rd->access = ReadWrite;
+ }
+
+ if(rd->access != NoExists){
+
+ rd_check_remvalid(rd, 1L);
+
+ /*
+ * If the cached info says it is readonly but
+ * it looks like it's been fixed now, change it to readwrite.
+ */
+ if(rd->read_status == 'R'){
+ rd_check_readonly_access(rd);
+ if(rd->read_status == 'W'){
+ rd->access = ReadWrite;
+ rd->flags |= REM_OUTOFDATE;
+ }
+ else
+ rd->access = ReadOnly;
+ }
+ }
+
+ if(rd->flags & REM_OUTOFDATE){
+ if(rd_update_local(rd) != 0){
+
+ dprint((1, "copy_dir_to_container: rd_update_local failed\n"));
+ rd_close_remdata(&rd);
+ return -1;
+ }
+ }
+ else
+ rd_open_remote(rd);
+
+ if(rd->access != ReadWrite || rd_remote_is_readonly(rd)){
+ rd_close_remdata(&rd);
+ return -1;
+ }
+
+ rd->flags |= DO_REMTRIM;
+
+ strncpy(dstpath, rd->lf, sizeof(dstpath)-1);
+ dstpath[sizeof(dstpath)-1] = '\0';
+ }
+ else{
+ strncpy(dstpath, configpath, sizeof(dstpath)-1);
+ dstpath[sizeof(dstpath)-1] = '\0';
+ }
+
+ /*
+ * dstpath is either the local Container file or the local cache file
+ * for the remote Container file.
+ */
+ tempfile = tempfile_in_same_dir(dstpath, "az", NULL);
+ }
+
+ /*
+ * If there is a legit directory to read from and a tempfile
+ * to write to we continue.
+ */
+ if(tempfile && (bio_out=BIO_new_file(tempfile, "w")) != NULL){
+
+ dirp = opendir(srcpath);
+ if(dirp){
+
+ while((d=readdir(dirp)) && !ret){
+ size_t ll;
+
+ if((ll=strlen(d->d_name)) && ll > 4 && !strcmp(d->d_name+ll-4, filesuffix)){
+
+ /* copy file name to temp buffer */
+ strncpy(emailaddr, d->d_name, sizeof(emailaddr)-1);
+ emailaddr[sizeof(emailaddr)-1] = '\0';
+ /* chop off suffix trailier */
+ emailaddr[strlen(emailaddr)-4] = 0;
+
+ /*
+ * This is the separator between the contents of
+ * different files.
+ */
+ if(which == CACert){
+ if(!((BIO_puts(bio_out, CACERTSTORELEADER) > 0)
+ && (BIO_puts(bio_out, emailaddr) > 0)
+ && (BIO_puts(bio_out, "\n") > 0)))
+ ret = -1;
+ }
+ else{
+ if(!((BIO_puts(bio_out, EMAILADDRLEADER) > 0)
+ && (BIO_puts(bio_out, emailaddr) > 0)
+ && (BIO_puts(bio_out, "\n") > 0)))
+ ret = -1;
+ }
+
+ /* read then write contents of file */
+ build_path(file, srcpath, d->d_name, sizeof(file));
+ if(!(bio_in = BIO_new_file(file, "r")))
+ ret = -1;
+
+ if(!ret){
+ int good_stuff = 0;
+
+ while(BIO_gets(bio_in, line, sizeof(line)) > 0){
+ if(strncmp("-----BEGIN", line, strlen("-----BEGIN")) == 0)
+ good_stuff = 1;
+
+ if(good_stuff)
+ BIO_puts(bio_out, line);
+
+ if(strncmp("-----END", line, strlen("-----END")) == 0)
+ good_stuff = 0;
+ }
+ }
+
+ BIO_free(bio_in);
+ }
+ }
+
+ closedir(dirp);
+ }
+
+ BIO_free(bio_out);
+
+ if(!ret){
+ if(rename_file(tempfile, dstpath) < 0){
+ q_status_message2(SM_ORDER, 3, 3,
+ _("Can't rename %s to %s"), tempfile, dstpath);
+ ret = -1;
+ }
+
+ /* if the container is remote, copy it */
+ if(!ret && IS_REMOTE(configpath)){
+ int e;
+ char datebuf[200];
+
+ datebuf[0] = '\0';
+
+ if((e = rd_update_remote(rd, datebuf)) != 0){
+ if(e == -1){
+ q_status_message2(SM_ORDER | SM_DING, 3, 5,
+ _("Error opening temporary smime file %s: %s"),
+ rd->lf, error_description(errno));
+ dprint((1,
+ "write_remote_smime: error opening temp file %s\n",
+ rd->lf ? rd->lf : "?"));
+ }
+ else{
+ q_status_message2(SM_ORDER | SM_DING, 3, 5,
+ _("Error copying to %s: %s"),
+ rd->rn, error_description(errno));
+ dprint((1,
+ "write_remote_smime: error copying from %s to %s\n",
+ rd->lf ? rd->lf : "?", rd->rn ? rd->rn : "?"));
+ }
+
+ q_status_message(SM_ORDER | SM_DING, 5, 5,
+ _("Copy of smime key to remote folder failed, NOT saved remotely"));
+ }
+ else{
+ rd_update_metadata(rd, datebuf);
+ rd->read_status = 'W';
+ }
+
+ rd_close_remdata(&rd);
+ }
+ }
+ }
+
+ if(tempfile)
+ fs_give((void **) &tempfile);
+
+ return ret;
+}
+
+
+/*
+ * returns 0 on success, -1 on failure
+ */
+int
+copy_container_to_dir(WhichCerts which)
+{
+ char path[MAXPATH+1], file[MAXPATH+1], buf[MAXPATH+1];
+ char iobuf[4096];
+ char *contents = NULL;
+ char *leader = NULL;
+ char *filesuffix = NULL;
+ char *configdir = NULL;
+ char *configpath = NULL;
+ char *tempfile = NULL;
+ char *p, *q, *line, *name, *certtext, *save_p;
+ int len;
+ BIO *in, *out;
+
+ dprint((9, "copy_container_to_dir(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
+ smime_init();
+
+ path[0] = '\0';
+
+ if(which == Public){
+ leader = EMAILADDRLEADER;
+ contents = ps_global->smime->publiccontent;
+ configdir = ps_global->VAR_PUBLICCERT_DIR;
+ configpath = ps_global->smime->publicpath;
+ filesuffix = ".crt";
+ if(!(configpath && configpath[0])){
+#ifdef APPLEKEYCHAIN
+ if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
+ q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
+ return -1;
+ }
+#endif /* APPLEKEYCHAIN */
+ q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
+ return -1;
+ }
+
+ fs_give((void **) &ps_global->smime->publicpath);
+
+ path[0] = '\0';
+ if(!(signature_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
+ && !IS_REMOTE(path))){
+ q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
+ return -1;
+ }
+
+ if(can_access(path, ACCESS_EXISTS)){
+ if(our_mkpath(path, 0700)){
+ q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
+ return -1;
+ }
+ }
+
+ ps_global->smime->publicpath = cpystr(path);
+ configpath = ps_global->smime->publicpath;
+ }
+ else if(which == Private){
+ leader = EMAILADDRLEADER;
+ contents = ps_global->smime->privatecontent;
+ configdir = ps_global->VAR_PRIVATEKEY_DIR;
+ configpath = ps_global->smime->privatepath;
+ filesuffix = ".key";
+ if(!(configpath && configpath[0])){
+ q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
+ return -1;
+ }
+
+ fs_give((void **) &ps_global->smime->privatepath);
+
+ path[0] = '\0';
+ if(!(signature_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
+ && !IS_REMOTE(path))){
+ q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
+ return -1;
+ }
+
+ if(can_access(path, ACCESS_EXISTS)){
+ if(our_mkpath(path, 0700)){
+ q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
+ return -1;
+ }
+ }
+
+ ps_global->smime->privatepath = cpystr(path);
+ configpath = ps_global->smime->privatepath;
+ }
+ else if(which == CACert){
+ leader = CACERTSTORELEADER;
+ contents = ps_global->smime->cacontent;
+ configdir = ps_global->VAR_CACERT_DIR;
+ configpath = ps_global->smime->capath;
+ filesuffix = ".crt";
+ if(!(configpath && configpath[0])){
+ q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
+ return -1;
+ }
+
+ fs_give((void **) &ps_global->smime->capath);
+
+ path[0] = '\0';
+ if(!(signature_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
+ && !IS_REMOTE(path))){
+ q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
+ return -1;
+ }
+
+ if(can_access(path, ACCESS_EXISTS)){
+ if(our_mkpath(path, 0700)){
+ q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
+ return -1;
+ }
+ }
+
+ ps_global->smime->capath = cpystr(path);
+ configpath = ps_global->smime->capath;
+ }
+
+ if(!(configdir && configdir[0])){
+ q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
+ return -1;
+ }
+
+ if(!(configpath && configpath[0])){
+ q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
+ return -1;
+ }
+
+ if(!(filesuffix && strlen(filesuffix) == 4)){
+ return -1;
+ }
+
+
+ if(contents && *contents){
+ for(p = contents; *p != '\0';){
+ line = p;
+
+ while(*p && *p != '\n')
+ p++;
+
+ save_p = NULL;
+ if(*p == '\n'){
+ save_p = p;
+ *p++ = '\0';
+ }
+
+ if(strncmp(leader, line, strlen(leader)) == 0){
+ name = line + strlen(leader);
+ certtext = p;
+ if(strncmp("-----BEGIN", certtext, strlen("-----BEGIN")) == 0){
+ if((q = strstr(certtext, leader)) != NULL){
+ p = q;
+ }
+ else{ /* end of file */
+ q = certtext + strlen(certtext);
+ p = q;
+ }
+
+ strncpy(buf, name, sizeof(buf)-5);
+ buf[sizeof(buf)-5] = '\0';
+ strncat(buf, filesuffix, 5);
+ build_path(file, configpath, buf, sizeof(file));
+
+ in = BIO_new_mem_buf(certtext, q-certtext);
+ if(in){
+ tempfile = tempfile_in_same_dir(file, "az", NULL);
+ out = NULL;
+ if(tempfile)
+ out = BIO_new_file(tempfile, "w");
+
+ if(out){
+ while((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
+ BIO_write(out, iobuf, len);
+
+ BIO_free(out);
+
+ if(rename_file(tempfile, file) < 0){
+ q_status_message2(SM_ORDER, 3, 3,
+ _("Can't rename %s to %s"),
+ tempfile, file);
+ return -1;
+ }
+
+ fs_give((void **) &tempfile);
+ }
+
+ BIO_free(in);
+ }
+ }
+ }
+
+ if(save_p)
+ *save_p = '\n';
+ }
+ }
+
+ return 0;
+}
+
+
+#ifdef APPLEKEYCHAIN
+
+int
+copy_publiccert_container_to_keychain(void)
+{
+ /* NOT IMPLEMNTED */
+ return -1;
+}
+
+int
+copy_publiccert_keychain_to_container(void)
+{
+ /* NOT IMPLEMNTED */
+ return -1;
+}
+
+#endif /* APPLEKEYCHAIN */
+
+
+/*
+ * Get a pointer to a string describing the most recent OpenSSL error.
+ * It's statically allocated, so don't change or attempt to free it.
+ */
+static const char *
+openssl_error_string(void)
+{
+ char *errs;
+ const char *data = NULL;
+ long errn;
+
+ errn = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
+ errs = (char*) ERR_reason_error_string(errn);
+
+ if(errs)
+ return errs;
+ else if(data)
+ return data;
+
+ return "unknown error";
+}
+
+
+/* Return true if the body looks like a PKCS7 object */
+int
+is_pkcs7_body(BODY *body)
+{
+ int result;
+
+ result = body->type==TYPEAPPLICATION &&
+ body->subtype &&
+ (strucmp(body->subtype,"pkcs7-mime")==0 ||
+ strucmp(body->subtype,"x-pkcs7-mime")==0 ||
+ strucmp(body->subtype,"pkcs7-signature")==0 ||
+ strucmp(body->subtype,"x-pkcs7-signature")==0);
+
+ return result;
+}
+
+
+#ifdef notdef
+/*
+ * Somewhat useful debug utility to dump the contents of a BIO to a file.
+ * Note that a memory BIO will have its contents eliminated after they
+ * are read so this will break the next step.
+ */
+static void
+dump_bio_to_file(BIO *in, char *filename)
+{
+ char iobuf[4096];
+ int len;
+ BIO *out;
+
+ out = BIO_new_file(filename, "w");
+
+ if(out){
+ if(BIO_method_type(in) != BIO_TYPE_MEM)
+ BIO_reset(in);
+
+ while((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
+ BIO_write(out, iobuf, len);
+
+ BIO_free(out);
+ }
+
+ BIO_reset(in);
+}
+#endif
+
+
+/*
+ * Recursively stash a pointer to the decrypted data in our
+ * manufactured body.
+ */
+static void
+create_local_cache(char *base, BODY *b)
+{
+ if(b->type==TYPEMULTIPART){
+ PART *p;
+
+#if 0
+ cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
+#else
+ /*
+ * We don't really want to copy the real body contents. It shouldn't be
+ * used, and in the case of a message with attachments, we'll be
+ * duplicating the files multiple times.
+ */
+ cpytxt(&b->contents.text, "BODY UNAVAILABLE", 16);
+#endif
+
+ for(p=b->nested.part; p; p=p->next)
+ create_local_cache(base, (BODY*) p);
+ }
+ else{
+ cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
+ }
+}
+
+
+static long
+rfc822_output_func(void *b, char *string)
+{
+ BIO *bio = (BIO *) b;
+
+ return((BIO_puts(bio, string) > 0) ? 1L : 0L);
+}
+
+
+/*
+ * Attempt to load the private key for the given PERSONAL_CERT.
+ * This sets the appropriate passphrase globals in order to
+ * interact with the user correctly.
+ */
+static int
+load_private_key(PERSONAL_CERT *pcert)
+{
+ if(!pcert->key){
+
+ /* Try empty password by default */
+ char *password = "";
+
+ if(ps_global->smime
+ && (ps_global->smime->need_passphrase
+ || ps_global->smime->entered_passphrase)){
+ /* We've already been in here and discovered we need a different password */
+
+ if(ps_global->smime->entered_passphrase)
+ password = (char *) ps_global->smime->passphrase; /* already entered */
+ else
+ return 0;
+ }
+
+ ERR_clear_error();
+
+ if(!(pcert->key = load_key(pcert, password))){
+ long err = ERR_get_error();
+
+ /* Couldn't load key... */
+
+ if(ps_global->smime && ps_global->smime->entered_passphrase){
+
+ /* The user got the password wrong maybe? */
+
+ if((ERR_GET_LIB(err)==ERR_LIB_EVP && ERR_GET_REASON(err)==EVP_R_BAD_DECRYPT) ||
+ (ERR_GET_LIB(err)==ERR_LIB_PEM && ERR_GET_REASON(err)==PEM_R_BAD_DECRYPT))
+ q_status_message(SM_ORDER | SM_DING, 4, 4, _("Incorrect passphrase"));
+ else
+ q_status_message1(SM_ORDER, 4, 4, _("Couldn't read key: %s"),(char*)openssl_error_string());
+
+ /* This passphrase is no good; forget it */
+ ps_global->smime->entered_passphrase = 0;
+ }
+
+ /* Indicate to the UI that we need re-entry (see mailcmd.c:process_cmd())*/
+ if(ps_global->smime)
+ ps_global->smime->need_passphrase = 1;
+
+ if(ps_global->smime){
+ if(ps_global->smime->passphrase_emailaddr)
+ fs_give((void **) &ps_global->smime->passphrase_emailaddr);
+
+ ps_global->smime->passphrase_emailaddr = get_x509_subject_email(pcert->cert);
+ }
+
+ return 0;
+ }
+ else{
+ /* This key will be cached, so we won't be called again */
+ if(ps_global->smime){
+ ps_global->smime->entered_passphrase = 0;
+ ps_global->smime->need_passphrase = 0;
+ }
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void
+setup_pkcs7_body_for_signature(BODY *b, char *description, char *type, char *filename)
+{
+ b->type = TYPEAPPLICATION;
+ b->subtype = cpystr(type);
+ b->encoding = ENCBINARY;
+ b->description = cpystr(description);
+
+ b->disposition.type = cpystr("attachment");
+ set_parameter(&b->disposition.parameter, "filename", filename);
+
+ set_parameter(&b->parameter, "name", filename);
+}
+
+
+/*
+ * Look for a personal certificate matching the
+ * given address
+ */
+PERSONAL_CERT *
+match_personal_cert_to_email(ADDRESS *a)
+{
+ PERSONAL_CERT *pcert = NULL;
+ char buf[MAXPATH];
+ char *email;
+
+ if(!a || !a->mailbox || !a->host)
+ return NULL;
+
+ snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
+
+ if(ps_global->smime){
+ for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
+ pcert;
+ pcert=pcert->next){
+
+ if(!pcert->cert)
+ continue;
+
+ email = get_x509_subject_email(pcert->cert);
+
+ if(email && strucmp(email,buf)==0){
+ fs_give((void**) &email);
+ break;
+ }
+
+ fs_give((void**) &email);
+ }
+ }
+
+ return pcert;
+}
+
+
+/*
+ * Look for a personal certificate matching the from
+ * (or reply_to? in the given envelope)
+ */
+PERSONAL_CERT *
+match_personal_cert(ENVELOPE *env)
+{
+ PERSONAL_CERT *pcert;
+
+ pcert = match_personal_cert_to_email(env->reply_to);
+ if(!pcert)
+ pcert = match_personal_cert_to_email(env->from);
+
+ return pcert;
+}
+
+
+/*
+ * Flatten the given body into its MIME representation.
+ * Return the result in a BIO.
+ */
+static BIO *
+body_to_bio(BODY *body)
+{
+ BIO *bio = NULL;
+ int len;
+
+ bio = BIO_new(BIO_s_mem());
+ if(!bio)
+ return NULL;
+
+ pine_encode_body(body); /* this attaches random boundary strings to multiparts */
+ pine_write_body_header(body, rfc822_output_func, bio);
+ pine_rfc822_output_body(body, rfc822_output_func, bio);
+
+ /*
+ * Now need to truncate by two characters since the above
+ * appends CRLF.
+ */
+ if((len=BIO_ctrl_pending(bio)) > 1){
+ BUF_MEM *biobuf = NULL;
+
+ BIO_get_mem_ptr(bio, &biobuf);
+ if(biobuf){
+ BUF_MEM_grow(biobuf, len-2); /* remove CRLF */
+ }
+ }
+
+ return bio;
+}
+
+
+static BIO *
+bio_from_store(STORE_S *store)
+{
+ BIO *ret = NULL;
+
+ if(store && store->src == BioType && store->txt){
+ ret = (BIO *) store->txt;
+ }
+
+ return(ret);
+}
+
+/*
+ * Encrypt file; given a path (char *) fp, replace the file
+ * by an encrypted version of it. If (char *) text is not null, then
+ * replace the text of (char *) fp by the encrypted version of (char *) text.
+ */
+int
+encrypt_file(char *fp, char *text)
+{
+ const EVP_CIPHER *cipher = NULL;
+ STACK_OF(X509) *encerts = NULL;
+ X509 *cert;
+ PERSONAL_CERT *pcert;
+ BIO *in;
+ PKCS7 *p7 = NULL;
+ FILE *fpp;
+ int rv = 0;
+
+ smime_init();
+ if((pcert = ps_global->smime->personal_certs) == NULL)
+ return 0;
+
+ cipher = EVP_aes_256_cbc();
+ encerts = sk_X509_new_null();
+
+ if((cert = get_cert_for(pcert->name)) != NULL)
+ sk_X509_push(encerts, cert);
+ else
+ goto end;
+
+ if(text){
+ in = BIO_new(BIO_s_mem());
+ if(in == NULL)
+ goto end;
+ (void) BIO_reset(in);
+ BIO_puts(in, text);
+ }
+ else{
+ if(!(in = BIO_new_file(fp, "rb")))
+ goto end;
+
+ BIO_read_filename(in, fp);
+ }
+
+ if((p7 = PKCS7_encrypt(encerts, in, cipher, 0)) == NULL)
+ goto end;
+ BIO_set_close(in, BIO_CLOSE);
+ BIO_free(in);
+ if(!(in = BIO_new_file(fp, "w")))
+ goto end;
+ BIO_reset(in);
+ rv = PEM_write_bio_PKCS7(in, p7);
+ BIO_flush(in);
+
+end:
+ BIO_free(in);
+ PKCS7_free(p7);
+ sk_X509_pop_free(encerts, X509_free);
+
+ return rv;
+}
+
+/*
+ * Encrypt a message on the way out. Called from call_mailer in send.c
+ * The body may be reallocated.
+ */
+int
+encrypt_outgoing_message(METAENV *header, BODY **bodyP)
+{
+ PKCS7 *p7 = NULL;
+ BIO *in = NULL;
+ BIO *out = NULL;
+ const EVP_CIPHER *cipher = NULL;
+ STACK_OF(X509) *encerts = NULL;
+ STORE_S *outs = NULL;
+ PINEFIELD *pf;
+ ADDRESS *a;
+ BODY *body = *bodyP;
+ BODY *newBody = NULL;
+ int result = 0;
+
+ dprint((9, "encrypt_outgoing_message()"));
+ smime_init();
+
+ cipher = EVP_des_cbc();
+
+ encerts = sk_X509_new_null();
+
+ /* Look for a certificate for each of the recipients */
+ for(pf = header->local; pf && pf->name; pf = pf->next)
+ if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr){
+ for(a=*pf->addr; a; a=a->next){
+ X509 *cert;
+ char buf[MAXPATH];
+
+ snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
+
+ cert = get_cert_for(buf);
+ if(cert)
+ sk_X509_push(encerts,cert);
+ else{
+ q_status_message2(SM_ORDER, 1, 1,
+ _("Unable to find certificate for <%s@%s>"),
+ a->mailbox, a->host);
+ goto end;
+ }
+ }
+ }
+
+
+ in = body_to_bio(body);
+
+ p7 = PKCS7_encrypt(encerts, in, cipher, 0);
+
+ outs = so_get(BioType, NULL, EDIT_ACCESS);
+ out = bio_from_store(outs);
+
+ i2d_PKCS7_bio(out, p7);
+ (void) BIO_flush(out);
+
+ so_seek(outs, 0, SEEK_SET);
+
+ newBody = mail_newbody();
+
+ newBody->type = TYPEAPPLICATION;
+ newBody->subtype = cpystr("pkcs7-mime");
+ newBody->encoding = ENCBINARY;
+
+ newBody->disposition.type = cpystr("attachment");
+ set_parameter(&newBody->disposition.parameter, "filename", "smime.p7m");
+
+ newBody->description = cpystr("S/MIME Encrypted Message");
+ set_parameter(&newBody->parameter, "smime-type", "enveloped-data");
+ set_parameter(&newBody->parameter, "name", "smime.p7m");
+
+ newBody->contents.text.data = (unsigned char *) outs;
+
+ *bodyP = newBody;
+
+ result = 1;
+
+end:
+
+ BIO_free(in);
+ PKCS7_free(p7);
+ sk_X509_pop_free(encerts, X509_free);
+
+ dprint((9, "encrypt_outgoing_message returns %d", result));
+ return result;
+}
+
+
+/*
+ * Plonk the contents (mime headers and body) of the given
+ * section of a message to a BIO_s_mem BIO object.
+ */
+static BIO *
+raw_part_to_bio(long msgno, const char *section)
+{
+ unsigned long len;
+ char *text;
+ BIO *bio;
+
+ bio = BIO_new(BIO_s_mem());
+
+ if(bio){
+
+ (void) BIO_reset(bio);
+
+ /* First grab headers of the chap */
+ text = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) section, &len, 0);
+
+ if(text){
+ BIO_write(bio, text, len);
+
+ /** Now grab actual body */
+ text = mail_fetch_body (ps_global->mail_stream, msgno, (char*) section, &len, 0);
+ if(text){
+ BIO_write(bio, text, len);
+ }
+ else{
+ BIO_free(bio);
+ bio = NULL;
+ }
+
+ }
+ else{
+ BIO_free(bio);
+ bio = NULL;
+ }
+ }
+
+ return bio;
+}
+
+
+/*
+ Get (and decode) the body of the given section of msg
+ */
+static STORE_S*
+get_part_contents(long msgno, const char *section)
+{
+ long len;
+ gf_io_t pc;
+ STORE_S *store = NULL;
+ char *err;
+
+ store = so_get(CharStar, NULL, EDIT_ACCESS);
+ if(store){
+ gf_set_so_writec(&pc,store);
+
+ err = detach(ps_global->mail_stream, msgno, (char*) section, 0L, &len, pc, NULL, 0L);
+
+ gf_clear_so_writec(store);
+
+ so_seek(store, 0, SEEK_SET);
+
+ if(err)
+ so_give(&store);
+ }
+
+ return store;
+}
+
+
+static PKCS7 *
+get_pkcs7_from_part(long msgno,const char *section)
+{
+ STORE_S *store = NULL;
+ PKCS7 *p7 = NULL;
+ BIO *in = NULL;
+
+ store = get_part_contents(msgno, section);
+
+ if(store){
+ if(store->src == CharStar){
+ int len;
+
+ /*
+ * We're reaching inside the STORE_S structure. We should
+ * probably have a way to get the length, instead.
+ */
+ len = (int) (store->eod - store->dp);
+ in = BIO_new_mem_buf(store->txt, len);
+ }
+ else{ /* just copy it */
+ unsigned char c;
+
+ in = BIO_new(BIO_s_mem());
+ (void) BIO_reset(in);
+
+ so_seek(store, 0L, 0);
+ while(so_readc(&c, store)){
+ BIO_write(in, &c, 1);
+ }
+ }
+
+ if(in){
+/* dump_bio_to_file(in, "/tmp/decoded-signature"); */
+ if((p7=d2i_PKCS7_bio(in,NULL)) == NULL){
+ /* error */
+ }
+
+ BIO_free(in);
+ }
+
+ so_give(&store);
+ }
+
+ return p7;
+}
+
+
+/*
+ * Try to verify a signature.
+ *
+ * p7 - the pkcs7 object to verify
+ * in - the plain data to verify (NULL if not detached)
+ * out - BIO to which to write the opaque data
+ */
+static int
+do_signature_verify(PKCS7 *p7, BIO *in, BIO *out)
+{
+ STACK_OF(X509) *otherCerts = NULL;
+ int result;
+ const char *data;
+ long err;
+
+#if 0
+ dump_bio_to_file(in,"/tmp/verified-data");
+#endif
+
+ if(!s_cert_store){
+ q_status_message(SM_ORDER | SM_DING, 2, 2,
+ _("Couldn't verify S/MIME signature: No CA Certs were loaded"));
+
+ return -1;
+ }
+
+ result = PKCS7_verify(p7, otherCerts, s_cert_store, in, out, 0);
+
+ if(result){
+ q_status_message(SM_ORDER, 1, 1, _("S/MIME signature verified ok"));
+ }
+ else{
+ err = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
+
+ if(out && err==ERR_PACK(ERR_LIB_PKCS7,PKCS7_F_PKCS7_VERIFY,PKCS7_R_CERTIFICATE_VERIFY_ERROR)){
+
+ /* Retry verification so we can get the plain text */
+ /* Might be better to reimplement PKCS7_verify here? */
+
+ PKCS7_verify(p7, otherCerts, s_cert_store, in, out, PKCS7_NOVERIFY);
+ }
+
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("Couldn't verify S/MIME signature: %s"), (char*) openssl_error_string());
+ }
+
+ /* now try to extract the certificates of any signers */
+ {
+ STACK_OF(X509) *signers;
+ int i;
+
+ signers = PKCS7_get0_signers(p7, NULL, 0);
+
+ if(signers)
+ for(i=0; i<sk_X509_num(signers); i++){
+ char *email;
+ X509 *x = sk_X509_value(signers,i);
+ X509 *cert;
+
+ if(!x)
+ continue;
+
+ email = get_x509_subject_email(x);
+
+ if(email){
+ cert = get_cert_for(email);
+ if(cert)
+ X509_free(cert);
+ else
+ save_cert_for(email, x);
+
+ fs_give((void**) &email);
+ }
+ }
+
+ sk_X509_free(signers);
+ }
+
+ return result;
+}
+
+
+void
+free_smime_body_sparep(void **sparep)
+{
+ if(sparep && *sparep){
+ PKCS7_free((PKCS7 *) (*sparep));
+ *sparep = NULL;
+ }
+}
+
+
+/*
+ * Given a multipart body of type multipart/signed, attempt to verify it.
+ * Returns non-zero if the body was changed.
+ */
+static int
+do_detached_signature_verify(BODY *b, long msgno, char *section)
+{
+ PKCS7 *p7 = NULL;
+ BIO *in = NULL;
+ PART *p;
+ int result, modified_the_body = 0;
+ char newSec[100];
+ char *what_we_did;
+
+ dprint((9, "do_detached_signature_verify(msgno=%ld type=%d subtype=%s section=%s)", msgno, b->type, b->subtype ? b->subtype : "NULL", (section && *section) ? section : (section != NULL) ? "Top" : "NULL"));
+ smime_init();
+
+ snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
+ in = raw_part_to_bio(msgno, newSec);
+
+ if(in){
+
+ snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : "");
+ p7 = get_pkcs7_from_part(msgno, newSec);
+
+ if(!p7)
+ goto end;
+
+ result = do_signature_verify(p7, in, NULL);
+
+ if(b->subtype)
+ fs_give((void**) &b->subtype);
+
+ b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
+ b->encoding = ENC8BIT;
+
+ if(b->description)
+ fs_give ((void**) &b->description);
+
+ what_we_did = result ? _("This message was cryptographically signed.") :
+ _("This message was cryptographically signed but the signature could not be verified.");
+
+ b->description = cpystr(what_we_did);
+
+ b->sparep = p7;
+ p7 = NULL;
+
+ p = b->nested.part;
+
+ /* p is signed plaintext */
+ if(p && p->next)
+ mail_free_body_part(&p->next); /* hide the pkcs7 from the viewer */
+
+ BIO_free(in);
+
+ modified_the_body = 1;
+ }
+
+end:
+ PKCS7_free(p7);
+
+ return modified_the_body;
+}
+
+
+PERSONAL_CERT *
+find_certificate_matching_recip_info(PKCS7_RECIP_INFO *ri)
+{
+ PERSONAL_CERT *x = NULL;
+
+ if(ps_global->smime){
+ for(x = (PERSONAL_CERT *) ps_global->smime->personal_certs; x; x=x->next){
+ X509 *mine;
+
+ mine = x->cert;
+
+ if(!X509_NAME_cmp(ri->issuer_and_serial->issuer,mine->cert_info->issuer) &&
+ !ASN1_INTEGER_cmp(ri->issuer_and_serial->serial,mine->cert_info->serialNumber)){
+ break;
+ }
+ }
+ }
+
+ return x;
+}
+
+
+static PERSONAL_CERT *
+find_certificate_matching_pkcs7(PKCS7 *p7)
+{
+ int i;
+ STACK_OF(PKCS7_RECIP_INFO) *recips;
+ PERSONAL_CERT *x = NULL;
+
+ recips = p7->d.enveloped->recipientinfo;
+
+ for(i=0; i<sk_PKCS7_RECIP_INFO_num(recips); i++){
+ PKCS7_RECIP_INFO *ri;
+
+ ri = sk_PKCS7_RECIP_INFO_value(recips, i);
+
+ if((x=find_certificate_matching_recip_info(ri))!=0){
+ break;
+ }
+ }
+
+ return x;
+}
+
+/* decrypt an encrypted file.
+ Args: fp - the path to the encrypted file.
+ rv - a code that thells the caller what happened inside the function
+ Returns the decoded text allocated in a char *, whose memory must be
+ freed by caller
+ */
+
+char *
+decrypt_file(char *fp, int *rv)
+{
+ PKCS7 *p7 = NULL;
+ char *text, *tmp;
+ BIO *in = NULL, *out = NULL;
+ EVP_PKEY *pkey = NULL, *key = NULL;
+ PERSONAL_CERT *pcert = NULL;
+ X509 *recip, *cert;
+ STORE_S *outs = NULL, *store, *ins;
+ int i, j;
+ long unsigned int len;
+ void *ret;
+
+ smime_init();
+
+ if((text = read_file(fp, 0)) == NULL)
+ return NULL;
+
+ tmp = fs_get(strlen(text) + (strlen(text) << 6) + 1);
+ for(j = 0, i = strlen("-----BEGIN PKCS7-----") + 1; text[i] != '\0'
+ && text[i] != '-'; j++, i++)
+ tmp[j] = text[i];
+ tmp[j] = '\0';
+
+ ret = rfc822_base64(tmp, strlen(tmp), &len);
+
+ if((in = BIO_new_mem_buf((char *)ret, len)) != NULL){
+ p7 = d2i_PKCS7_bio(in, NULL);
+ BIO_free(in);
+ }
+
+ if(text) fs_give((void **)&text);
+ if(ret) fs_give((void **)&ret);
+
+ if((pcert = ps_global->smime->personal_certs) == NULL)
+ goto end;
+
+ if((i = load_private_key(pcert)) == 0
+ && ps_global->smime
+ && ps_global->smime->need_passphrase
+ && !ps_global->smime->already_auto_asked)
+ for(; i == 0;){
+ ps_global->smime->already_auto_asked = 1;
+ if(pith_opt_smime_get_passphrase){
+ switch((*pith_opt_smime_get_passphrase)()){
+ case 0 : i = load_private_key(pcert);
+ break;
+
+ case 1 : i = -1;
+ break;
+
+ default: break; /* repeat until we cancel */
+ }
+ }
+ else
+ i = -2;
+ }
+
+ if(rv) *rv = i;
+
+ if((key = pcert->key) == NULL)
+ goto end;
+
+ recip = get_cert_for(pcert->name);
+ out = BIO_new(BIO_s_mem());
+ (void) BIO_reset(out);
+
+ i = PKCS7_decrypt(p7, key, recip, out, 0);
+
+ if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
+ forget_private_keys();
+
+ if(i == 0){
+ q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
+ (char*) openssl_error_string());
+ goto end;
+ }
+
+ BIO_get_mem_data(out, &tmp);
+
+ text = cpystr(tmp);
+ BIO_free(out);
+
+end:
+ PKCS7_free(p7);
+
+ return text;
+}
+
+/*
+ * Try to decode (decrypt or verify a signature) a PKCS7 body
+ * Returns non-zero if something was changed.
+ */
+static int
+do_decoding(BODY *b, long msgno, const char *section)
+{
+ int modified_the_body = 0;
+ BIO *out = NULL;
+ PKCS7 *p7 = NULL;
+ X509 *recip = NULL;
+ EVP_PKEY *key = NULL;
+ PERSONAL_CERT *pcert = NULL;
+ char *what_we_did = "";
+ char null[1];
+
+ dprint((9, "do_decoding(msgno=%ld type=%d subtype=%s section=%s)", msgno, b->type, b->subtype ? b->subtype : "NULL", (section && *section) ? section : (section != NULL) ? "Top" : "NULL"));
+ null[0] = '\0';
+ smime_init();
+
+ /*
+ * Extract binary data from part to an in-memory store
+ */
+
+ if(b->sparep){ /* already done */
+ p7 = (PKCS7*) b->sparep;
+ }
+ else{
+
+ p7 = get_pkcs7_from_part(msgno, section && *section ? section : "1");
+ if(!p7){
+ q_status_message1(SM_ORDER, 2, 2, "Couldn't load PKCS7 object: %s",
+ (char*) openssl_error_string());
+ goto end;
+ }
+
+ /*
+ * Save the PKCS7 object for later dealings by the user interface.
+ * It will be cleaned up when the body is garbage collected.
+ */
+ b->sparep = p7;
+ }
+
+ if(PKCS7_type_is_signed(p7)){
+ int sigok;
+
+ out = BIO_new(BIO_s_mem());
+ (void) BIO_reset(out);
+ BIO_puts(out, "MIME-Version: 1.0\r\n"); /* needed so rfc822_parse_msg_full believes it's MIME */
+
+ sigok = do_signature_verify(p7, NULL, out);
+
+ /* shouldn't really duplicate these messages */
+ what_we_did = sigok ? _("This message was cryptographically signed.") :
+ _("This message was cryptographically signed but the signature could not be verified.");
+
+ /* make sure it's null terminated */
+ BIO_write(out, null, 1);
+ }
+ else if(!PKCS7_type_is_enveloped(p7)){
+ q_status_message(SM_ORDER, 1, 1, "PKCS7 object not recognised.");
+ goto end;
+ }
+ else{ /* It *is* enveloped */
+ int decrypt_result;
+
+ what_we_did = _("This message was encrypted.");
+
+ /* now need to find a cert that can decrypt this */
+ pcert = find_certificate_matching_pkcs7(p7);
+
+ if(!pcert){
+ q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to decrypt."));
+ goto end;
+ }
+
+ recip = pcert->cert;
+
+ if(!load_private_key(pcert)
+ && ps_global->smime
+ && ps_global->smime->need_passphrase
+ && !ps_global->smime->already_auto_asked){
+ /* Couldn't load key with blank password, ask user */
+ ps_global->smime->already_auto_asked = 1;
+ if(pith_opt_smime_get_passphrase){
+ (*pith_opt_smime_get_passphrase)();
+ load_private_key(pcert);
+ }
+ }
+
+ key = pcert->key;
+ if(!key)
+ goto end;
+
+ out = BIO_new(BIO_s_mem());
+ (void) BIO_reset(out);
+ BIO_puts(out, "MIME-Version: 1.0\r\n");
+
+ decrypt_result = PKCS7_decrypt(p7, key, recip, out, 0);
+
+ if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
+ forget_private_keys();
+
+ if(!decrypt_result){
+ q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
+ (char*) openssl_error_string());
+ goto end;
+ }
+
+ BIO_write(out, null, 1);
+ }
+
+ /*
+ * We've now produced a flattened MIME object in BIO out.
+ * It needs to be turned back into a BODY.
+ */
+
+ if(out){
+ BODY *body;
+ ENVELOPE *env;
+ char *h = NULL;
+ char *bstart;
+ STRING s;
+ BUF_MEM *bptr = NULL;
+
+ BIO_get_mem_ptr(out, &bptr);
+ if(bptr)
+ h = bptr->data;
+
+ /* look for start of body */
+ bstart = strstr(h, "\r\n\r\n");
+
+ if(!bstart){
+ q_status_message(SM_ORDER, 3, 3, _("Encrypted data couldn't be parsed."));
+ }
+ else{
+ bstart += 4; /* skip over CRLF*2 */
+
+ INIT(&s, mail_string, bstart, strlen(bstart));
+ rfc822_parse_msg_full(&env, &body, h, bstart-h-2, &s, BADHOST, 0, 0);
+ mail_free_envelope(&env); /* Don't care about this */
+
+ /*
+ * Now convert original body (application/pkcs7-mime)
+ * to a multipart body with one sub-part (the decrypted body).
+ * Note that the sub-part may also be multipart!
+ */
+
+ b->type = TYPEMULTIPART;
+ if(b->subtype)
+ fs_give((void**) &b->subtype);
+
+ /*
+ * This subtype is used in mailview.c to annotate the display of
+ * encrypted or signed messages. We know for sure then that it's a PKCS7
+ * part because the sparep field is set to the PKCS7 object (see above).
+ */
+ b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
+ b->encoding = ENC8BIT;
+
+ if(b->description)
+ fs_give((void**) &b->description);
+
+ b->description = cpystr(what_we_did);
+
+ if(b->disposition.type)
+ fs_give((void **) &b->disposition.type);
+
+ if(b->contents.text.data)
+ fs_give((void **) &b->contents.text.data);
+
+ if(b->parameter)
+ mail_free_body_parameter(&b->parameter);
+
+ /* Allocate mem for the sub-part, and copy over the contents of our parsed body */
+ b->nested.part = fs_get(sizeof(PART));
+ b->nested.part->body = *body;
+ b->nested.part->next = NULL;
+
+ fs_give((void**) &body);
+
+ /*
+ * IMPORTANT BIT: set the body->contents.text.data elements to contain the decrypted
+ * data. Otherwise, it'll try to load it from the original data. Eek.
+ */
+ create_local_cache(bstart, &b->nested.part->body);
+
+ modified_the_body = 1;
+ }
+ }
+
+end:
+ if(out)
+ BIO_free(out);
+
+ return modified_the_body;
+}
+
+
+/*
+ * Recursively handle PKCS7 bodies in our message.
+ *
+ * Returns non-zero if some fiddling was done.
+ */
+static int
+do_fiddle_smime_message(BODY *b, long msgno, char *section)
+{
+ int modified_the_body = 0;
+
+ if(!b)
+ return 0;
+
+ dprint((9, "do_fiddle_smime_message(msgno=%ld type=%d subtype=%s section=%s)", msgno, b->type, b->subtype ? b->subtype : "NULL", (section && *section) ? section : (section != NULL) ? "Top" : "NULL"));
+
+ if(is_pkcs7_body(b)){
+
+ if(do_decoding(b, msgno, section)){
+ /*
+ * b should now be a multipart message:
+ * fiddle it too in case it's been multiply-encrypted!
+ */
+
+ /* fallthru */
+ modified_the_body = 1;
+ }
+ }
+
+ if(b->type==TYPEMULTIPART || MIME_MSG(b->type, b->subtype)){
+
+ PART *p;
+ int partNum;
+ char newSec[100];
+
+ if(MIME_MULT_SIGNED(b->type, b->subtype)){
+
+
+ /*
+ * Ahah. We have a multipart signed entity.
+ *
+ * Multipart/signed
+ * part 1 (signed thing)
+ * part 2 (the pkcs7 signature)
+ *
+ * We're going to convert that to
+ *
+ * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
+ * part 1 (signed thing)
+ * part 2 has been freed
+ *
+ * We also extract the signature from part 2 and save it
+ * in the multipart body->sparep, and we add a description
+ * in the multipart body->description.
+ *
+ *
+ * The results of a decrypted message will be similar. It
+ * will be
+ *
+ * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
+ * part 1 (decrypted thing)
+ */
+
+ modified_the_body += do_detached_signature_verify(b, msgno, section);
+ }
+ else if(MIME_MSG(b->type, b->subtype)){
+ modified_the_body += do_fiddle_smime_message(b->nested.msg->body, msgno, section);
+ }
+ else{
+
+ for(p=b->nested.part,partNum=1; p; p=p->next,partNum++){
+
+ /* Append part number to the section string */
+
+ snprintf(newSec, sizeof(newSec), "%s%s%d", section, *section ? "." : "", partNum);
+
+ modified_the_body += do_fiddle_smime_message(&p->body, msgno, newSec);
+ }
+ }
+ }
+
+ return modified_the_body;
+}
+
+
+/*
+ * Fiddle a message in-place by decrypting/verifying S/MIME entities.
+ * Returns non-zero if something was changed.
+ */
+int
+fiddle_smime_message(BODY *b, long msgno)
+{
+ return do_fiddle_smime_message(b, msgno, "");
+}
+
+
+/********************************************************************************/
+
+
+/*
+ * Output a string in a distinctive style
+ */
+void
+gf_puts_uline(char *txt, gf_io_t pc)
+{
+ pc(TAG_EMBED); pc(TAG_BOLDON);
+ gf_puts(txt, pc);
+ pc(TAG_EMBED); pc(TAG_BOLDOFF);
+}
+
+
+/*
+ * Sign a message. Called from call_mailer in send.c.
+ *
+ * This takes the header for the outgoing message as well as a pointer
+ * to the current body (which may be reallocated).
+ */
+int
+sign_outgoing_message(METAENV *header, BODY **bodyP, int dont_detach)
+{
+ STORE_S *outs = NULL;
+ BODY *body = *bodyP;
+ BODY *newBody = NULL;
+ PART *p1 = NULL;
+ PART *p2 = NULL;
+ PERSONAL_CERT *pcert;
+ BIO *in = NULL;
+ BIO *out = NULL;
+ PKCS7 *p7 = NULL;
+ int result = 0;
+ int flags = dont_detach ? 0 : PKCS7_DETACHED;
+
+ dprint((9, "sign_outgoing_message()"));
+
+ smime_init();
+
+ /* Look for a private key matching the sender address... */
+
+ pcert = match_personal_cert(header->env);
+
+ if(!pcert){
+ q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to sign."));
+ goto end;
+ }
+
+ if(!load_private_key(pcert) && ps_global->smime && ps_global->smime->need_passphrase){
+ /* Couldn't load key with blank password, try again */
+ if(pith_opt_smime_get_passphrase){
+ (*pith_opt_smime_get_passphrase)();
+ load_private_key(pcert);
+ }
+ }
+
+ if(!pcert->key)
+ goto end;
+
+ in = body_to_bio(body);
+
+#ifdef notdef
+ dump_bio_to_file(in,"/tmp/signed-data");
+#endif
+
+ p7 = PKCS7_sign(pcert->cert, pcert->key, NULL, in, flags);
+
+ if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
+ forget_private_keys();
+
+ if(!p7){
+ q_status_message(SM_ORDER, 1, 1, _("Error creating signed object."));
+ goto end;
+ }
+
+ outs = so_get(BioType, NULL, EDIT_ACCESS);
+ out = bio_from_store(outs);
+
+ i2d_PKCS7_bio(out, p7);
+ (void) BIO_flush(out);
+
+ so_seek(outs, 0, SEEK_SET);
+
+ if((flags&PKCS7_DETACHED)==0){
+
+ /* the simple case: the signed data is in the pkcs7 object */
+
+ newBody = mail_newbody();
+
+ setup_pkcs7_body_for_signature(newBody, "S/MIME Cryptographically Signed Message", "pkcs7-mime", "smime.p7m");
+
+ newBody->contents.text.data = (unsigned char *) outs;
+ *bodyP = newBody;
+
+ result = 1;
+ }
+ else{
+
+ /*
+ * OK.
+ * We have to create a new body as follows:
+ *
+ * multipart/signed; blah blah blah
+ * reference to existing body
+ *
+ * pkcs7 object
+ */
+
+ newBody = mail_newbody();
+
+ newBody->type = TYPEMULTIPART;
+ newBody->subtype = cpystr("signed");
+ newBody->encoding = ENC7BIT;
+
+ set_parameter(&newBody->parameter, "protocol", "application/pkcs7-signature");
+ set_parameter(&newBody->parameter, "micalg", "sha1");
+
+ p1 = mail_newbody_part();
+ p2 = mail_newbody_part();
+
+ /*
+ * This is nasty. We're just copying the body in here,
+ * but since our newBody is freed at the end of call_mailer,
+ * we mustn't let this body (the original one) be freed twice.
+ */
+ p1->body = *body; /* ARRGH. This is special cased at the end of call_mailer */
+
+ p1->next = p2;
+
+ setup_pkcs7_body_for_signature(&p2->body, "S/MIME Cryptographic Signature", "pkcs7-signature", "smime.p7s");
+ p2->body.contents.text.data = (unsigned char *) outs;
+
+ newBody->nested.part = p1;
+
+ *bodyP = newBody;
+
+ result = 1;
+ }
+
+end:
+
+ PKCS7_free(p7);
+ BIO_free(in);
+
+ dprint((9, "sign_outgoing_message returns %d", result));
+ return result;
+}
+
+
+SMIME_STUFF_S *
+new_smime_struct(void)
+{
+ SMIME_STUFF_S *ret = NULL;
+
+ ret = (SMIME_STUFF_S *) fs_get(sizeof(*ret));
+ memset((void *) ret, 0, sizeof(*ret));
+ ret->publictype = Nada;
+
+ return ret;
+}
+
+
+static void
+free_smime_struct(SMIME_STUFF_S **smime)
+{
+ if(smime && *smime){
+ if((*smime)->passphrase_emailaddr)
+ fs_give((void **) &(*smime)->passphrase_emailaddr);
+
+ if((*smime)->publicpath)
+ fs_give((void **) &(*smime)->publicpath);
+
+ if((*smime)->publiccertlist)
+ free_certlist(&(*smime)->publiccertlist);
+
+ if((*smime)->publiccontent)
+ fs_give((void **) &(*smime)->publiccontent);
+
+ if((*smime)->privatepath)
+ fs_give((void **) &(*smime)->privatepath);
+
+ if((*smime)->personal_certs){
+ PERSONAL_CERT *pc;
+
+ pc = (PERSONAL_CERT *) (*smime)->personal_certs;
+ free_personal_certs(&pc);
+ (*smime)->personal_certs = NULL;
+ }
+
+ if((*smime)->privatecontent)
+ fs_give((void **) &(*smime)->privatecontent);
+
+ if((*smime)->capath)
+ fs_give((void **) &(*smime)->capath);
+
+ if((*smime)->cacontent)
+ fs_give((void **) &(*smime)->cacontent);
+
+ fs_give((void **) smime);
+ }
+}
+
+#endif /* SMIME */
diff --git a/pith/smime.h b/pith/smime.h
new file mode 100644
index 00000000..5a89b372
--- /dev/null
+++ b/pith/smime.h
@@ -0,0 +1,58 @@
+/*
+ * $Id: smime.h 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifdef SMIME
+#ifndef PITH_SMIME_INCLUDED
+#define PITH_SMIME_INCLUDED
+
+
+#include "../pith/state.h"
+#include "../pith/send.h"
+#include "../pith/filttype.h"
+#include "../pith/smkeys.h"
+
+#include <openssl/rand.h>
+#include <openssl/err.h>
+
+
+#define OUR_PKCS7_ENCLOSURE_SUBTYPE "x-pkcs7-enclosure"
+
+
+/* exported protoypes */
+int encrypt_file(char *fp, char *text);
+char *decrypt_file(char *fp, int *rv);
+int is_pkcs7_body(BODY *b);
+int fiddle_smime_message(BODY *b, long msgno);
+int encrypt_outgoing_message(METAENV *header, BODY **bodyP);
+void free_smime_body_sparep(void **sparep);
+int sign_outgoing_message(METAENV *header, BODY **bodyP, int dont_detach);
+void gf_puts_uline(char *txt, gf_io_t pc);
+PERSONAL_CERT *find_certificate_matching_recip_info(PKCS7_RECIP_INFO *ri);
+void smime_deinit(void);
+SMIME_STUFF_S *new_smime_struct(void);
+int copy_publiccert_dir_to_container(void);
+int copy_publiccert_container_to_dir(void);
+int copy_privatecert_dir_to_container(void);
+int copy_privatecert_container_to_dir(void);
+int copy_cacert_dir_to_container(void);
+int copy_cacert_container_to_dir(void);
+#ifdef APPLEKEYCHAIN
+int copy_publiccert_container_to_keychain(void);
+int copy_publiccert_keychain_to_container(void);
+#endif /* APPLEKEYCHAIN */
+
+
+#endif /* PITH_SMIME_INCLUDED */
+#endif /* SMIME */
diff --git a/pith/smkeys.c b/pith/smkeys.c
new file mode 100644
index 00000000..5a827070
--- /dev/null
+++ b/pith/smkeys.c
@@ -0,0 +1,925 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: smkeys.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*
+ * This is based on a contribution from Jonathan Paisley, see smime.c
+ */
+
+
+#include "../pith/headers.h"
+
+#ifdef SMIME
+
+#include "../pith/status.h"
+#include "../pith/conf.h"
+#include "../pith/remote.h"
+#include "../pith/tempfile.h"
+#include "../pith/busy.h"
+#include "../pith/osdep/lstcmpnt.h"
+#include "smkeys.h"
+
+#ifdef APPLEKEYCHAIN
+#include <Security/SecKeychain.h>
+#include <Security/SecKeychainItem.h>
+#include <Security/SecKeychainSearch.h>
+#include <Security/SecCertificate.h>
+#endif /* APPLEKEYCHAIN */
+
+
+/* internal prototypes */
+static char *emailstrclean(char *string);
+static int add_certs_in_dir(X509_LOOKUP *lookup, char *path);
+static int certlist_to_file(char *filename, CertList *certlist);
+static int mem_add_extra_cacerts(char *contents, X509_LOOKUP *lookup);
+
+
+/*
+ * Remove leading whitespace, trailing whitespace and convert
+ * to lowercase. Also remove slash characters
+ *
+ * Args: s, -- The string to clean
+ *
+ * Result: the cleaned string
+ */
+static char *
+emailstrclean(char *string)
+{
+ char *s = string, *sc = NULL, *p = NULL;
+
+ for(; *s; s++){ /* single pass */
+ if(!isspace((unsigned char) (*s))){
+ p = NULL; /* not start of blanks */
+ if(!sc) /* first non-blank? */
+ sc = string; /* start copying */
+ }
+ else if(!p) /* it's OK if sc == NULL */
+ p = sc; /* start of blanks? */
+
+ if(sc && *s!='/' && *s!='\\') /* if copying, copy */
+ *sc++ = isupper((unsigned char) (*s))
+ ? (unsigned char) tolower((unsigned char) (*s))
+ : (unsigned char) (*s);
+ }
+
+ if(p) /* if ending blanks */
+ *p = '\0'; /* tie off beginning */
+ else if(!sc) /* never saw a non-blank */
+ *string = '\0'; /* so tie whole thing off */
+
+ return(string);
+}
+
+
+/*
+ * Add a lookup for each "*.crt" file in the given directory.
+ */
+static int
+add_certs_in_dir(X509_LOOKUP *lookup, char *path)
+{
+ char buf[MAXPATH];
+ struct direct *d;
+ DIR *dirp;
+ int ret = 0;
+
+ dirp = opendir(path);
+ if(dirp){
+
+ while(!ret && (d=readdir(dirp)) != NULL){
+ if(srchrstr(d->d_name, ".crt")){
+ build_path(buf, path, d->d_name, sizeof(buf));
+
+ if(!X509_LOOKUP_load_file(lookup, buf, X509_FILETYPE_PEM)){
+ q_status_message1(SM_ORDER, 3, 3, _("Error loading file %s"), buf);
+ ret = -1;
+ }
+ }
+
+ }
+
+ closedir(dirp);
+ }
+
+ return ret;
+}
+
+
+/*
+ * Get an X509_STORE. This consists of the system
+ * certs directory and any certificates in the user's
+ * ~/.alpine-smime/ca directory.
+ */
+X509_STORE *
+get_ca_store(void)
+{
+ X509_LOOKUP *lookup;
+ X509_STORE *store = NULL;
+
+ dprint((9, "get_ca_store()"));
+
+ if(!(store=X509_STORE_new())){
+ dprint((9, "X509_STORE_new() failed"));
+ return store;
+ }
+
+ if(!(lookup=X509_STORE_add_lookup(store, X509_LOOKUP_file()))){
+ dprint((9, "X509_STORE_add_lookup() failed"));
+ X509_STORE_free(store);
+ return NULL;
+ }
+
+ if(ps_global->smime && ps_global->smime->catype == Container
+ && ps_global->smime->cacontent){
+
+ if(!mem_add_extra_cacerts(ps_global->smime->cacontent, lookup)){
+ X509_STORE_free(store);
+ return NULL;
+ }
+ }
+ else if(ps_global->smime && ps_global->smime->catype == Directory
+ && ps_global->smime->capath){
+ if(add_certs_in_dir(lookup, ps_global->smime->capath) < 0){
+ X509_STORE_free(store);
+ return NULL;
+ }
+ }
+
+ if(!(lookup=X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir()))){
+ X509_STORE_free(store);
+ return NULL;
+ }
+
+#ifdef SMIME_SSLCERTS
+ dprint((9, "get_ca_store(): adding cacerts from %s", SMIME_SSLCERTS));
+ X509_LOOKUP_add_dir(lookup, SMIME_SSLCERTS, X509_FILETYPE_PEM);
+#endif
+
+ return store;
+}
+
+
+EVP_PKEY *
+load_key(PERSONAL_CERT *pc, char *pass)
+{
+ BIO *in;
+ EVP_PKEY *key = NULL;
+ char buf[MAXPATH], file[MAXPATH];
+
+ if(!(ps_global->smime && pc && pc->name))
+ return key;
+
+ if(ps_global->smime->privatetype == Container){
+ char *q;
+
+ if(pc->keytext && (q = strstr(pc->keytext, "-----END")) != NULL){
+ while(*q && *q != '\n')
+ q++;
+
+ if(*q == '\n')
+ q++;
+
+ if((in = BIO_new_mem_buf(pc->keytext, q-pc->keytext)) != NULL){
+ key = PEM_read_bio_PrivateKey(in, NULL, NULL, pass);
+ BIO_free(in);
+ }
+ }
+ }
+ else if(ps_global->smime->privatetype == Directory){
+ /* filename is path/name.key */
+ strncpy(buf, pc->name, sizeof(buf)-5);
+ buf[sizeof(buf)-5] = '\0';
+ strncat(buf, ".key", 5);
+ build_path(file, ps_global->smime->privatepath, buf, sizeof(file));
+
+ if(!(in = BIO_new_file(file, "r")))
+ return NULL;
+
+ key = PEM_read_bio_PrivateKey(in, NULL, NULL, pass);
+ BIO_free(in);
+ }
+
+ return key;
+}
+
+
+#ifdef notdef
+static char *
+get_x509_name_entry(const char *key, X509_NAME *name)
+{
+ int i, c, n;
+ char buf[256];
+ char *id;
+
+ if(!name)
+ return NULL;
+
+ c = X509_NAME_entry_count(name);
+
+ for(i=0; i<c; i++){
+ X509_NAME_ENTRY *e;
+
+ e = X509_NAME_get_entry(name, i);
+ if(!e)
+ continue;
+
+ buf[0] = 0;
+ id = buf;
+
+ n = OBJ_obj2nid(e->object);
+ if((n == NID_undef) || ((id=(char*) OBJ_nid2sn(n)) == NULL)){
+ i2t_ASN1_OBJECT(buf, sizeof(buf), e->object);
+ id = buf;
+ }
+
+ if((strucmp(id, "email")==0) || (strucmp(id, "emailAddress")==0)){
+ X509_NAME_get_text_by_OBJ(name, e->object, buf, sizeof(buf)-1);
+ return cpystr(buf);
+ }
+ }
+
+ return NULL;
+}
+
+
+char *
+get_x509_subject_email(X509 *x)
+{
+ char* result;
+ result = get_x509_name_entry("email", X509_get_subject_name(x));
+ if( !result ){
+ result = get_x509_name_entry("emailAddress", X509_get_subject_name(x));
+ }
+
+ return result;
+}
+#endif /* notdef */
+
+#include <openssl/x509v3.h>
+/*
+ * This newer version is from Adrian Vogel. It looks for the email
+ * address not only in the email address field, but also in an
+ * X509v3 extension field, Subject Altenative Name.
+ */
+char *
+get_x509_subject_email(X509 *x)
+{
+ char *result = NULL;
+ STACK_OF(OPENSSL_STRING) *emails = X509_get1_email(x);
+ if (sk_OPENSSL_STRING_num(emails) > 0) {
+ /* take the first one on the stack */
+ result = cpystr(sk_OPENSSL_STRING_value(emails, 0));
+ }
+ X509_email_free(emails);
+ return result;
+}
+
+
+/*
+ * Save the certificate for the given email address in
+ * ~/.alpine-smime/public.
+ *
+ * Should consider the security hazards in making a file with
+ * the email address that has come from the certificate.
+ *
+ * The argument email is destroyed.
+ */
+void
+save_cert_for(char *email, X509 *cert)
+{
+ if(!ps_global->smime)
+ return;
+
+ dprint((9, "save_cert_for(%s)", email ? email : "?"));
+ emailstrclean(email);
+
+ if(ps_global->smime->publictype == Keychain){
+#ifdef APPLEKEYCHAIN
+
+ OSStatus rc;
+ SecCertificateRef secCertificateRef;
+ CSSM_DATA certData;
+
+ memset((void *) &certData, 0, sizeof(certData));
+ memset((void *) &secCertificateRef, 0, sizeof(secCertificateRef));
+
+ /* convert OpenSSL X509 cert data to MacOS certData */
+ if((certData.Length = i2d_X509(cert, &(certData.Data))) > 0){
+
+ /*
+ * Put that certData into a SecCertificateRef.
+ * Version 3 should work for versions 1-3.
+ */
+ if(!(rc=SecCertificateCreateFromData(&certData,
+ CSSM_CERT_X_509v3,
+ CSSM_CERT_ENCODING_DER,
+ &secCertificateRef))){
+
+ /* add it to the default keychain */
+ if(!(rc=SecCertificateAddToKeychain(secCertificateRef, NULL))){
+ /* ok */
+ }
+ else if(rc == errSecDuplicateItem){
+ dprint((9, "save_cert_for: certificate for %s already in keychain", email));
+ }
+ else{
+ dprint((9, "SecCertificateAddToKeychain failed"));
+ }
+ }
+ else{
+ dprint((9, "SecCertificateCreateFromData failed"));
+ }
+ }
+ else{
+ dprint((9, "i2d_X509 failed"));
+ }
+
+#endif /* APPLEKEYCHAIN */
+ }
+ else if(ps_global->smime->publictype == Container){
+ REMDATA_S *rd = NULL;
+ char path[MAXPATH];
+ char *tempfile = NULL;
+ int err = 0;
+
+ add_to_end_of_certlist(&ps_global->smime->publiccertlist, email, X509_dup(cert));
+
+ if(!ps_global->smime->publicpath)
+ return;
+
+ if(IS_REMOTE(ps_global->smime->publicpath)){
+ rd = rd_create_remote(RemImap, ps_global->smime->publicpath, REMOTE_SMIME_SUBTYPE,
+ NULL, "Error: ",
+ _("Can't access remote smime configuration."));
+ if(!rd)
+ return;
+
+ (void) rd_read_metadata(rd);
+
+ if(rd->access == MaybeRorW){
+ if(rd->read_status == 'R')
+ rd->access = ReadOnly;
+ else
+ rd->access = ReadWrite;
+ }
+
+ if(rd->access != NoExists){
+
+ rd_check_remvalid(rd, 1L);
+
+ /*
+ * If the cached info says it is readonly but
+ * it looks like it's been fixed now, change it to readwrite.
+ */
+ if(rd->read_status == 'R'){
+ rd_check_readonly_access(rd);
+ if(rd->read_status == 'W'){
+ rd->access = ReadWrite;
+ rd->flags |= REM_OUTOFDATE;
+ }
+ else
+ rd->access = ReadOnly;
+ }
+ }
+
+ if(rd->flags & REM_OUTOFDATE){
+ if(rd_update_local(rd) != 0){
+
+ dprint((1, "save_cert_for: rd_update_local failed\n"));
+ rd_close_remdata(&rd);
+ return;
+ }
+ }
+ else
+ rd_open_remote(rd);
+
+ if(rd->access != ReadWrite || rd_remote_is_readonly(rd)){
+ rd_close_remdata(&rd);
+ return;
+ }
+
+ rd->flags |= DO_REMTRIM;
+
+ strncpy(path, rd->lf, sizeof(path)-1);
+ path[sizeof(path)-1] = '\0';
+ }
+ else{
+ strncpy(path, ps_global->smime->publicpath, sizeof(path)-1);
+ path[sizeof(path)-1] = '\0';
+ }
+
+ tempfile = tempfile_in_same_dir(path, "az", NULL);
+ if(tempfile){
+ if(certlist_to_file(tempfile, ps_global->smime->publiccertlist))
+ err++;
+
+ if(!err){
+ if(rename_file(tempfile, path) < 0){
+ q_status_message2(SM_ORDER, 3, 3,
+ _("Can't rename %s to %s"), tempfile, path);
+ err++;
+ }
+ }
+
+ if(!err && IS_REMOTE(ps_global->smime->publicpath)){
+ int e, we_cancel;
+ char datebuf[200];
+
+ datebuf[0] = '\0';
+
+ we_cancel = busy_cue(_("Copying to remote smime container"), NULL, 1);
+ if((e = rd_update_remote(rd, datebuf)) != 0){
+ if(e == -1){
+ q_status_message2(SM_ORDER | SM_DING, 3, 5,
+ _("Error opening temporary smime file %s: %s"),
+ rd->lf, error_description(errno));
+ dprint((1,
+ "write_remote_smime: error opening temp file %s\n",
+ rd->lf ? rd->lf : "?"));
+ }
+ else{
+ q_status_message2(SM_ORDER | SM_DING, 3, 5,
+ _("Error copying to %s: %s"),
+ rd->rn, error_description(errno));
+ dprint((1,
+ "write_remote_smime: error copying from %s to %s\n",
+ rd->lf ? rd->lf : "?", rd->rn ? rd->rn : "?"));
+ }
+
+ q_status_message(SM_ORDER | SM_DING, 5, 5,
+ _("Copy of smime cert to remote folder failed, changes NOT saved remotely"));
+ }
+ else{
+ rd_update_metadata(rd, datebuf);
+ rd->read_status = 'W';
+ }
+
+ rd_close_remdata(&rd);
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+ }
+
+ fs_give((void **) &tempfile);
+ }
+ }
+ else if(ps_global->smime->publictype == Directory){
+ char certfilename[MAXPATH];
+ BIO *bio_out;
+
+ build_path(certfilename, ps_global->smime->publicpath,
+ email, sizeof(certfilename));
+ strncat(certfilename, ".crt", sizeof(certfilename)-1-strlen(certfilename));
+ certfilename[sizeof(certfilename)-1] = 0;
+
+ bio_out = BIO_new_file(certfilename, "w");
+ if(bio_out){
+ PEM_write_bio_X509(bio_out, cert);
+ BIO_free(bio_out);
+ q_status_message1(SM_ORDER, 1, 1, _("Saved certificate for <%s>"), email);
+ }
+ else{
+ q_status_message1(SM_ORDER, 1, 1, _("Couldn't save certificate for <%s>"), email);
+ }
+ }
+}
+
+
+/*
+ * Try to retrieve the certificate for the given email address.
+ * The caller should free the cert.
+ */
+X509 *
+get_cert_for(char *email)
+{
+ char *path;
+ char certfilename[MAXPATH];
+ char emailaddr[MAXPATH];
+ X509 *cert = NULL;
+ BIO *in;
+
+ if(!ps_global->smime)
+ return cert;
+
+ dprint((9, "get_cert_for(%s)", email ? email : "?"));
+
+ strncpy(emailaddr, email, sizeof(emailaddr)-1);
+ emailaddr[sizeof(emailaddr)-1] = 0;
+
+ /* clean it up (lowercase, space removal) */
+ emailstrclean(emailaddr);
+
+ if(ps_global->smime->publictype == Keychain){
+#ifdef APPLEKEYCHAIN
+
+ OSStatus rc;
+ SecKeychainItemRef itemRef = nil;
+ SecKeychainAttributeList attrList;
+ SecKeychainAttribute attrib;
+ SecKeychainSearchRef searchRef = nil;
+ CSSM_DATA certData;
+
+ /* low-level form of MacOS data */
+ memset((void *) &certData, 0, sizeof(certData));
+
+ attrList.count = 1;
+ attrList.attr = &attrib;
+
+ /* kSecAlias means email address for a certificate */
+ attrib.tag = kSecAlias;
+ attrib.data = emailaddr;
+ attrib.length = strlen(attrib.data);
+
+ /* Find the certificate in the default keychain */
+ if(!(rc=SecKeychainSearchCreateFromAttributes(NULL,
+ kSecCertificateItemClass,
+ &attrList,
+ &searchRef))){
+
+ if(!(rc=SecKeychainSearchCopyNext(searchRef, &itemRef))){
+
+ /* extract the data portion of the certificate */
+ if(!(rc=SecCertificateGetData((SecCertificateRef) itemRef, &certData))){
+
+ /*
+ * Convert it from MacOS form to OpenSSL form.
+ * The input is certData from above and the output
+ * is the X509 *cert.
+ */
+ if(!d2i_X509(&cert, &(certData.Data), certData.Length)){
+ dprint((9, "d2i_X509 failed"));
+ }
+ }
+ else{
+ dprint((9, "SecCertificateGetData failed"));
+ }
+ }
+ else if(rc == errSecItemNotFound){
+ dprint((9, "get_cert_for: Public cert for %s not found", emailaddr));
+ }
+ else{
+ dprint((9, "SecKeychainSearchCopyNext failed"));
+ }
+ }
+ else{
+ dprint((9, "SecKeychainSearchCreateFromAttributes failed"));
+ }
+
+ if(searchRef)
+ CFRelease(searchRef);
+
+#endif /* APPLEKEYCHAIN */
+ }
+ else if(ps_global->smime->publictype == Container){
+ if(ps_global->smime->publiccertlist){
+ CertList *cl;
+
+ for(cl = ps_global->smime->publiccertlist; cl; cl = cl->next){
+ if(cl->name && !strucmp(emailaddr, cl->name))
+ break;
+ }
+
+ if(cl)
+ cert = X509_dup((X509 *) cl->x509_cert);
+ }
+ }
+ else if(ps_global->smime->publictype == Directory){
+ path = ps_global->smime->publicpath;
+ build_path(certfilename, path, emailaddr, sizeof(certfilename));
+ strncat(certfilename, ".crt", sizeof(certfilename)-1-strlen(certfilename));
+ certfilename[sizeof(certfilename)-1] = 0;
+
+ if((in = BIO_new_file(certfilename, "r"))!=0){
+
+ cert = PEM_read_bio_X509(in, NULL, NULL, NULL);
+
+ if(cert){
+ /* could check email addr in cert matches */
+ }
+
+ BIO_free(in);
+ }
+ }
+
+ return cert;
+}
+
+
+PERSONAL_CERT *
+mem_to_personal_certs(char *contents)
+{
+ PERSONAL_CERT *result = NULL;
+ char *p, *q, *line, *name, *keytext, *save_p;
+ X509 *cert = NULL;
+
+ if(contents && *contents){
+ for(p = contents; *p != '\0';){
+ line = p;
+
+ while(*p && *p != '\n')
+ p++;
+
+ save_p = NULL;
+ if(*p == '\n'){
+ save_p = p;
+ *p++ = '\0';
+ }
+
+ if(strncmp(EMAILADDRLEADER, line, strlen(EMAILADDRLEADER)) == 0){
+ name = line + strlen(EMAILADDRLEADER);
+ cert = get_cert_for(name);
+ keytext = p;
+
+ /* advance p past this record */
+ if((q = strstr(keytext, "-----END")) != NULL){
+ while(*q && *q != '\n')
+ q++;
+
+ if(*q == '\n')
+ q++;
+
+ p = q;
+ }
+ else{
+ p = p + strlen(p);
+ q_status_message(SM_ORDER | SM_DING, 3, 3, _("Error in privatekey container, missing END"));
+ }
+
+ if(cert){
+ PERSONAL_CERT *pc;
+
+ pc = (PERSONAL_CERT *) fs_get(sizeof(*pc));
+ pc->cert = cert;
+ pc->name = cpystr(name);
+ pc->keytext = keytext; /* a pointer into contents */
+
+ pc->key = load_key(pc, "");
+
+ pc->next = result;
+ result = pc;
+ }
+ }
+
+ if(save_p)
+ *save_p = '\n';
+ }
+ }
+
+ return result;
+}
+
+
+CertList *
+mem_to_certlist(char *contents)
+{
+ CertList *ret = NULL;
+ char *p, *q, *line, *name, *certtext, *save_p;
+ X509 *cert = NULL;
+ BIO *in;
+
+ if(contents && *contents){
+ for(p = contents; *p != '\0';){
+ line = p;
+
+ while(*p && *p != '\n')
+ p++;
+
+ save_p = NULL;
+ if(*p == '\n'){
+ save_p = p;
+ *p++ = '\0';
+ }
+
+ if(strncmp(EMAILADDRLEADER, line, strlen(EMAILADDRLEADER)) == 0){
+ name = line + strlen(EMAILADDRLEADER);
+ cert = NULL;
+ certtext = p;
+ if(strncmp("-----BEGIN", certtext, strlen("-----BEGIN")) == 0){
+ if((q = strstr(certtext, "-----END")) != NULL){
+ while(*q && *q != '\n')
+ q++;
+
+ if(*q == '\n')
+ q++;
+
+ p = q;
+
+ if((in = BIO_new_mem_buf(certtext, q-certtext)) != 0){
+ cert = PEM_read_bio_X509(in, NULL, NULL, NULL);
+
+ BIO_free(in);
+ }
+ }
+ }
+ else{
+ p = p + strlen(p);
+ q_status_message1(SM_ORDER | SM_DING, 3, 3, _("Error in publiccert container, missing BEGIN, certtext=%s"), certtext);
+ }
+
+ if(name && cert){
+ add_to_end_of_certlist(&ret, name, cert);
+ }
+ }
+
+ if(save_p)
+ *save_p = '\n';
+ }
+ }
+
+ return ret;
+}
+
+
+/*
+ * Add the CACert Container contents into the CACert store.
+ *
+ * Returns > 0 for success, 0 for failure
+ */
+int
+mem_add_extra_cacerts(char *contents, X509_LOOKUP *lookup)
+{
+ char *p, *q, *line, *certtext, *save_p;
+ BIO *in, *out;
+ int len, failed = 0;
+ char *tempfile;
+ char iobuf[4096];
+
+ /*
+ * The most straight-forward way to do this is to write
+ * the container contents to a temp file and then load the
+ * contents of the file with X509_LOOKUP_load_file(), like
+ * is done in add_certs_in_dir(). What we don't know is if
+ * each file should consist of one cacert or if they can all
+ * just be jammed together into one file. To be safe, we'll use
+ * one file per and do each in a separate operation.
+ */
+
+ if(contents && *contents){
+ for(p = contents; *p != '\0';){
+ line = p;
+
+ while(*p && *p != '\n')
+ p++;
+
+ save_p = NULL;
+ if(*p == '\n'){
+ save_p = p;
+ *p++ = '\0';
+ }
+
+ /* look for separator line */
+ if(strncmp(CACERTSTORELEADER, line, strlen(CACERTSTORELEADER)) == 0){
+ /* certtext is the content that should go in a file */
+ certtext = p;
+ if(strncmp("-----BEGIN", certtext, strlen("-----BEGIN")) == 0){
+ if((q = strstr(certtext, CACERTSTORELEADER)) != NULL){
+ p = q;
+ }
+ else{ /* end of file */
+ q = certtext + strlen(certtext);
+ p = q;
+ }
+
+ in = BIO_new_mem_buf(certtext, q-certtext);
+ if(in){
+ tempfile = temp_nam(NULL, "az");
+ out = NULL;
+ if(tempfile)
+ out = BIO_new_file(tempfile, "w");
+
+ if(out){
+ while((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
+ BIO_write(out, iobuf, len);
+
+ BIO_free(out);
+ if(!X509_LOOKUP_load_file(lookup, tempfile, X509_FILETYPE_PEM))
+ failed++;
+
+ fs_give((void **) &tempfile);
+ }
+
+ BIO_free(in);
+ }
+ }
+ else{
+ p = p + strlen(p);
+ q_status_message1(SM_ORDER | SM_DING, 3, 3, _("Error in cacert container, missing BEGIN, certtext=%s"), certtext);
+ }
+ }
+ else{
+ p = p + strlen(p);
+ q_status_message1(SM_ORDER | SM_DING, 3, 3, _("Error in cacert container, missing separator, line=%s"), line);
+ }
+
+ if(save_p)
+ *save_p = '\n';
+ }
+ }
+
+ return(!failed);
+}
+
+
+int
+certlist_to_file(char *filename, CertList *certlist)
+{
+ CertList *cl;
+ BIO *bio_out = NULL;
+ int ret = -1;
+
+ if(filename && (bio_out=BIO_new_file(filename, "w")) != NULL){
+ ret = 0;
+ for(cl = certlist; cl; cl = cl->next){
+ if(cl->name && cl->name[0] && cl->x509_cert){
+ if(!((BIO_puts(bio_out, EMAILADDRLEADER) > 0)
+ && (BIO_puts(bio_out, cl->name) > 0)
+ && (BIO_puts(bio_out, "\n") > 0)))
+ ret = -1;
+
+ if(!PEM_write_bio_X509(bio_out, (X509 *) cl->x509_cert))
+ ret = -1;
+ }
+ }
+
+ BIO_free(bio_out);
+ }
+
+ return ret;
+}
+
+
+void
+add_to_end_of_certlist(CertList **cl, char *name, X509 *cert)
+{
+ CertList *new, *clp;
+
+ if(!cl)
+ return;
+
+ new = (CertList *) fs_get(sizeof(*new));
+ memset((void *) new, 0, sizeof(*new));
+ new->x509_cert = cert;
+ new->name = name ? cpystr(name) : NULL;
+
+ if(!*cl){
+ *cl = new;
+ }
+ else{
+ for(clp = (*cl); clp->next; clp = clp->next)
+ ;
+
+ clp->next = new;
+ }
+}
+
+
+void
+free_certlist(CertList **cl)
+{
+ if(cl && *cl){
+ free_certlist(&(*cl)->next);
+ if((*cl)->name)
+ fs_give((void **) &(*cl)->name);
+
+ if((*cl)->x509_cert)
+ X509_free((X509 *) (*cl)->x509_cert);
+
+ fs_give((void **) cl);
+ }
+}
+
+
+void
+free_personal_certs(PERSONAL_CERT **pc)
+{
+ if(pc && *pc){
+ free_personal_certs(&(*pc)->next);
+ if((*pc)->name)
+ fs_give((void **) &(*pc)->name);
+
+ if((*pc)->name)
+ fs_give((void **) &(*pc)->name);
+
+ if((*pc)->cert)
+ X509_free((*pc)->cert);
+
+ if((*pc)->key)
+ EVP_PKEY_free((*pc)->key);
+
+ fs_give((void **) pc);
+ }
+}
+
+#endif /* SMIME */
diff --git a/pith/smkeys.h b/pith/smkeys.h
new file mode 100644
index 00000000..0c6db8eb
--- /dev/null
+++ b/pith/smkeys.h
@@ -0,0 +1,61 @@
+/*
+ * $Id: smkeys.h 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifdef SMIME
+#ifndef PITH_SMKEYS_INCLUDED
+#define PITH_SMKEYS_INCLUDED
+
+
+#include "../pith/state.h"
+#include "../pith/send.h"
+
+#include <openssl/objects.h>
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+#include <openssl/pkcs7.h>
+#include <openssl/pem.h>
+#include <openssl/err.h>
+#include <openssl/bio.h>
+
+
+#define EMAILADDRLEADER "emailAddress="
+#define CACERTSTORELEADER "cacert="
+
+
+typedef struct personal_cert {
+ X509 *cert;
+ EVP_PKEY *key;
+ char *name;
+ char *keytext;
+ struct personal_cert *next;
+} PERSONAL_CERT;
+
+
+/* exported protoypes */
+X509_STORE *get_ca_store(void);
+PERSONAL_CERT *get_personal_certs(char *d);
+X509 *get_cert_for(char *email);
+void save_cert_for(char *email, X509 *cert);
+char *get_x509_subject_email(X509 *x);
+EVP_PKEY *load_key(PERSONAL_CERT *pc, char *pass);
+CertList *mem_to_certlist(char *contents);
+void add_to_end_of_certlist(CertList **cl, char *name, X509 *cert);
+void free_certlist(CertList **cl);
+PERSONAL_CERT *mem_to_personal_certs(char *contents);
+void free_personal_certs(PERSONAL_CERT **pc);
+
+
+#endif /* PITH_SMKEYS_INCLUDED */
+#endif /* SMIME */
diff --git a/pith/sort.c b/pith/sort.c
new file mode 100644
index 00000000..68a9c10c
--- /dev/null
+++ b/pith/sort.c
@@ -0,0 +1,710 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: sort.c 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/sort.h"
+#include "../pith/state.h"
+#include "../pith/status.h"
+#include "../pith/msgno.h"
+#include "../pith/flag.h"
+#include "../pith/pineelt.h"
+#include "../pith/thread.h"
+#include "../pith/search.h"
+#include "../pith/pattern.h"
+#include "../pith/util.h"
+#include "../pith/signal.h"
+#include "../pith/busy.h"
+#include "../pith/icache.h"
+
+
+/*
+ * global place to store mail_sort and mail_thread results
+ */
+struct global_sort_data g_sort;
+
+
+/*
+ * Internal prototypes
+ */
+void sort_sort_callback(MAILSTREAM *, unsigned long *, unsigned long);
+int percent_sorted(void);
+int pine_compare_long(const qsort_t *, const qsort_t *);
+int pine_compare_long_rev(const qsort_t *, const qsort_t *);
+int pine_compare_scores(const qsort_t *, const qsort_t *);
+void build_score_array(MAILSTREAM *, MSGNO_S *);
+void free_score_array(void);
+
+
+/*----------------------------------------------------------------------
+ Map sort types to names
+ ----*/
+char *
+sort_name(SortOrder so)
+{
+ /*
+ * Make sure the first upper case letter of any new sort name is
+ * unique. The command char and label for sort selection is
+ * derived from this name and its first upper case character.
+ * See mailcmd.c:select_sort().
+ */
+ return((so == SortArrival) ? "Arrival" :
+ (so == SortDate) ? "Date" :
+ (so == SortSubject) ? "Subject" :
+ (so == SortCc) ? "Cc" :
+ (so == SortFrom) ? "From" :
+ (so == SortTo) ? "To" :
+ (so == SortSize) ? "siZe" :
+ (so == SortSubject2) ? "OrderedSubj" :
+ (so == SortScore) ? "scorE" :
+ (so == SortThread) ? "tHread" :
+ "BOTCH");
+}
+
+
+/*----------------------------------------------------------------------
+ Sort the current folder into the order set in the msgmap
+
+Args: msgmap --
+ new_sort --
+ new_rev --
+
+ The idea of the deferred sort is to let the user interrupt a long sort
+ and have a chance to do a different command, such as a sort by arrival
+ or a Goto. The next newmail call will increment the deferred variable,
+ then the user may do a command, then the newmail call after that
+ causes the sort to happen if it is still needed.
+ ----*/
+void
+sort_folder(MAILSTREAM *stream, MSGNO_S *msgmap, SortOrder new_sort,
+ int new_rev, unsigned int flags)
+{
+ long raw_current, i, j;
+ unsigned long *sort = NULL;
+ int we_cancel = 0;
+ char sort_msg[MAX_SCREEN_COLS+1];
+ SortOrder current_sort;
+ int current_rev;
+ MESSAGECACHE *mc;
+
+ dprint((2, "Sorting by %s%s\n",
+ sort_name(new_sort), new_rev ? "/reverse" : ""));
+
+ if(!msgmap)
+ return;
+
+ current_sort = mn_get_sort(msgmap);
+ current_rev = mn_get_revsort(msgmap);
+ /*
+ * If we were previously threading (spare == 1) and now we're switching
+ * sorts (other than just a rev switch) then erase the information
+ * about the threaded state (collapsed and so forth).
+ */
+ if(stream && stream->spare && (current_sort != new_sort))
+ erase_threading_info(stream, msgmap);
+
+ if(mn_get_total(msgmap) <= 1L
+ && !(mn_get_total(msgmap) == 1L
+ && (new_sort == SortThread || new_sort == SortSubject2))){
+ mn_set_sort(msgmap, new_sort);
+ mn_set_revsort(msgmap, new_rev);
+ if(!mn_get_mansort(msgmap))
+ mn_set_mansort(msgmap, (flags & SRT_MAN) ? 1 : 0);
+
+ return;
+ }
+
+ raw_current = mn_m2raw(msgmap, mn_get_cur(msgmap));
+
+ if(new_sort == SortArrival){
+ /*
+ * NOTE: RE c-client sorting, our idea of arrival is really
+ * just the natural sequence order. C-client, and probably
+ * rightly so, considers "arrival" the order based on the
+ * message's internal date. This is more costly to compute
+ * since it means touching the message store (say "nntp"?),
+ * so we just preempt it here.
+ *
+ * Someday c-client will support "unsorted" and do what
+ * we're doing here. That day this gets scrapped.
+ */
+
+ mn_set_sort(msgmap, new_sort);
+ mn_set_revsort(msgmap, new_rev);
+
+ if(current_sort != new_sort || current_rev != new_rev ||
+ any_lflagged(msgmap, MN_EXLD))
+ clear_index_cache(stream, 0);
+
+ if(any_lflagged(msgmap, MN_EXLD)){
+ /*
+ * BEWARE: "exclusion" may leave holes in the unsorted sort order
+ * so we have to do a real sort if that is the case.
+ */
+ qsort(msgmap->sort+1, (size_t) mn_get_total(msgmap),
+ sizeof(long),
+ new_rev ? pine_compare_long_rev : pine_compare_long);
+ }
+ else if(mn_get_total(msgmap) > 0L){
+ if(new_rev){
+ clear_index_cache(stream, 0);
+ for(i = 1L, j = mn_get_total(msgmap); j >= 1; i++, j--)
+ msgmap->sort[i] = j;
+ }
+ else
+ for(i = 1L; i <= mn_get_total(msgmap); i++)
+ msgmap->sort[i] = i;
+ }
+
+ /* reset the inverse array */
+ msgno_reset_isort(msgmap);
+ }
+ else if(new_sort == SortScore){
+
+ /*
+ * We have to build a temporary array which maps raw msgno to
+ * score. We use the index cache machinery to build the array.
+ */
+
+ mn_set_sort(msgmap, new_sort);
+ mn_set_revsort(msgmap, new_rev);
+
+ if(flags & SRT_VRB){
+ /* TRANSLATORS: tell user they are waiting for Sorting of %s, the foldername */
+ snprintf(sort_msg, sizeof(sort_msg), _("Sorting \"%s\""),
+ strsquish(tmp_20k_buf + 500, 500, ps_global->cur_folder,
+ ps_global->ttyo->screen_cols - 20));
+ we_cancel = busy_cue(sort_msg, NULL, 0);
+ }
+
+ /*
+ * We do this so that we don't have to lookup the scores with function
+ * calls for each qsort compare.
+ */
+ build_score_array(stream, msgmap);
+
+ qsort(msgmap->sort+1, (size_t) mn_get_total(msgmap),
+ sizeof(long), pine_compare_scores);
+ free_score_array();
+ clear_index_cache(stream, 0);
+
+ if(we_cancel)
+ cancel_busy_cue(1);
+
+ /*
+ * Flip the sort if necessary (cheaper to do it once than for
+ * every comparison in pine_compare_scores.
+ */
+ if(new_rev && mn_get_total(msgmap) > 1L){
+ long *ep = &msgmap->sort[mn_get_total(msgmap)],
+ *sp = &msgmap->sort[1], tmp;
+
+ do{
+ tmp = *sp;
+ *sp++ = *ep;
+ *ep-- = tmp;
+ }
+ while(ep > sp);
+ }
+
+ /* reset the inverse array */
+ msgno_reset_isort(msgmap);
+ }
+ else{
+
+ mn_set_sort(msgmap, new_sort);
+ mn_set_revsort(msgmap, new_rev);
+ clear_index_cache(stream, 0);
+
+ if(flags & SRT_VRB){
+ int (*sort_func)() = NULL;
+
+ /*
+ * IMAP sort doesn't give us any way to get progress,
+ * so just spin the bar rather than show zero percent
+ * forever while a slow sort's going on...
+ */
+ if(!(stream && stream->dtb && stream->dtb->name
+ && !strucmp(stream->dtb->name, "imap")
+ && LEVELSORT(stream)))
+ sort_func = percent_sorted;
+
+ snprintf(sort_msg, sizeof(sort_msg), _("Sorting \"%s\""),
+ strsquish(tmp_20k_buf + 500, 500, ps_global->cur_folder,
+ ps_global->ttyo->screen_cols - 20));
+ we_cancel = busy_cue(sort_msg, sort_func, 0);
+ }
+
+ /*
+ * Limit the sort/thread if messages are hidden from view
+ * by lighting searched bit of every interesting msg in
+ * the folder and call c-client thread/sort to do the dirty work.
+ *
+ * Unfortunately it isn't that easy. IMAP servers are not able to
+ * handle medium to large sized sequence sets (more than 1000
+ * characters in the command line breaks some) so we have to try
+ * to handle it locally. By lighting the searched bits and
+ * providing a NULL search program we get a special c-client
+ * interface. This means that c-client will attempt to send the
+ * sequence set with the SORT or THREAD but it may get back
+ * a BAD response because of long command lines. In that case,
+ * if it is a SORT call, c-client will issue the full SORT
+ * without the sequence sets and will then filter the results
+ * locally. So sort_sort_callback will see the correctly
+ * filtered results. If it is a mail_thread call, a similar thing
+ * will be done. If a BAD is received, then there is no way to
+ * easily filter the results. C-client (in this special case where
+ * we provide a NULL search program) will set tree->num to zero
+ * for nodes of the thread tree which were supposed to be
+ * filtered out of the thread. Then pine, in sort_thread_callback,
+ * will treat those as dummy nodes (nodes which are part of the
+ * tree logically but where we don't have access to the messages).
+ * This will give us a different answer than we would have gotten
+ * if the restricted thread would have worked, but it's something.
+ *
+ * It isn't in general possible to give some shorter search set
+ * in place of the long sequence set because the long sequence set
+ * may be the result of several filter rules or of excluded
+ * messages in news (in this 2nd case maybe we could give
+ * a shorter search set).
+ *
+ * We note also that the too-long commands in imap is a general
+ * imap deficiency. It comes up in particular also in SEARCH
+ * commands. Pine likes to exclude the hidden messages from the
+ * SEARCH. SEARCH will be handled transparently by the local
+ * c-client by first issuing the full SEARCH command, if that
+ * comes back with a BAD and there is a pgm->msgno at the top
+ * level of pgm, then c-client will re-issue the SEARCH command
+ * but without the msgno sequence set in hopes that the resulting
+ * command will now be short enough, and then it will filter out
+ * the sequence set locally. If that doesn't work, it will
+ * download the messages and do the SEARCH locally. That is
+ * controllable by a flag bit.
+ */
+ for(i = 1L; i <= stream->nmsgs; i++)
+ if((mc = mail_elt(stream, i)) != NULL)
+ mc->searched = !get_lflag(stream, NULL, i, MN_EXLD);
+
+ g_sort.msgmap = msgmap;
+ if(new_sort == SortThread || new_sort == SortSubject2){
+ THREADNODE *thread;
+
+ /*
+ * Install callback to collect thread results
+ * and update sort mapping. Problem this solves
+ * is that of receiving exists/expunged events
+ * within sort/thread response. Since we update
+ * the sorted table within those handlers, we
+ * can get out of sync when we replace possibly
+ * stale sort/thread results once the function
+ * call's returned. Make sense? Thought so.
+ */
+ mail_parameters(NULL, SET_THREADRESULTS,
+ (void *) sort_thread_callback);
+
+ thread = mail_thread(stream,
+ (new_sort == SortThread)
+ ? "REFERENCES" : "ORDEREDSUBJECT",
+ NULL, NULL, 0L);
+
+ mail_parameters(NULL, SET_THREADRESULTS, (void *) NULL);
+
+ if(!thread){
+ new_sort = current_sort;
+ new_rev = current_rev;
+ q_status_message1(SM_ORDER, 3, 3,
+ "Sort Failed! Restored %.200s sort.",
+ sort_name(new_sort));
+ }
+
+ if(thread)
+ mail_free_threadnode(&thread);
+ }
+ else{
+ /*
+ * Set up the sort program.
+ * NOTE: we deal with reverse bit below.
+ */
+ g_sort.prog = mail_newsortpgm();
+ g_sort.prog->function = (new_sort == SortSubject)
+ ? SORTSUBJECT
+ : (new_sort == SortFrom)
+ ? SORTFROM
+ : (new_sort == SortTo)
+ ? SORTTO
+ : (new_sort == SortCc)
+ ? SORTCC
+ : (new_sort == SortDate)
+ ? SORTDATE
+ : (new_sort == SortSize)
+ ? SORTSIZE
+ : SORTARRIVAL;
+
+ mail_parameters(NULL, SET_SORTRESULTS,
+ (void *) sort_sort_callback);
+
+ /* Where the rubber meets the road. */
+ sort = mail_sort(stream, NULL, NULL, g_sort.prog, 0L);
+
+ mail_parameters(NULL, SET_SORTRESULTS, (void *) NULL);
+
+ if(!sort){
+ new_sort = current_sort;
+ new_rev = current_rev;
+ q_status_message1(SM_ORDER, 3, 3,
+ "Sort Failed! Restored %s sort.",
+ sort_name(new_sort));
+ }
+
+ if(sort)
+ fs_give((void **) &sort);
+
+ mail_free_sortpgm(&g_sort.prog);
+ }
+
+ if(we_cancel)
+ cancel_busy_cue(1);
+
+ /*
+ * Flip the sort if necessary (cheaper to do it once than for
+ * every comparison underneath mail_sort()
+ */
+ if(new_rev && mn_get_total(msgmap) > 1L){
+ long *ep = &msgmap->sort[mn_get_total(msgmap)],
+ *sp = &msgmap->sort[1], tmp;
+
+ do{
+ tmp = *sp;
+ *sp++ = *ep;
+ *ep-- = tmp;
+ }
+ while(ep > sp);
+
+ /* reset the inverse array */
+ msgno_reset_isort(msgmap);
+
+ /*
+ * Flip the thread numbers around.
+ * This puts us in a weird state that requires keeping track
+ * of. The direction of the thread list hasn't changed, but the
+ * thrdnos have and the display direction has.
+ *
+ * For Sort thrdno 1 thread list head
+ * thrdno 2 |
+ * thrdno . v nextthd this direction
+ * thrdno .
+ * thrdno n thread list tail
+ *
+ * Rev Sort thrdno 1 thread list tail
+ * thrdno 2
+ * thrdno . ^ nextthd this direction
+ * thrdno . |
+ * thrdno n thread list head
+ */
+ if(new_sort == SortThread || new_sort == SortSubject2){
+ PINETHRD_S *thrd;
+
+ thrd = fetch_head_thread(stream);
+ for(j = msgmap->max_thrdno; thrd && j >= 1L; j--){
+ thrd->thrdno = j;
+
+ if(thrd->nextthd)
+ thrd = fetch_thread(stream,
+ thrd->nextthd);
+ else
+ thrd = NULL;
+ }
+ }
+ }
+ }
+
+ /* Fix up sort structure */
+ mn_set_sort(msgmap, new_sort);
+ mn_set_revsort(msgmap, new_rev);
+ /*
+ * Once a folder has been sorted manually, we continue treating it
+ * as manually sorted until it is closed.
+ */
+ if(!mn_get_mansort(msgmap))
+ mn_set_mansort(msgmap, (flags & SRT_MAN) ? 1 : 0);
+
+ if(!msgmap->hilited){
+ /*
+ * If current is hidden, change current to visible parent.
+ * It can only be hidden if we are threading.
+ *
+ * Don't do this if hilited is set, because it means we're in the
+ * middle of an aggregate op, and this will mess up our selection.
+ * "hilited" means we've done a pseudo_selected, which we'll later
+ * fix with restore_selected.
+ */
+ if(THREADING())
+ mn_reset_cur(msgmap, first_sorted_flagged(new_rev ? F_NONE : F_SRCHBACK,
+ stream,
+ mn_raw2m(msgmap, raw_current),
+ FSF_SKIP_CHID));
+ else
+ mn_reset_cur(msgmap, mn_raw2m(msgmap, raw_current));
+ }
+
+ msgmap->top = -1L;
+
+ if(!sp_mail_box_changed(stream))
+ sp_set_unsorted_newmail(stream, 0);
+
+ /*
+ * Turn off the MN_USOR flag. Don't bother going through the
+ * function call and the message number mappings.
+ */
+ if(THREADING()){
+ PINELT_S *pelt;
+
+ for(i = 1L; i <= stream->nmsgs; i++)
+ if((mc = mail_elt(stream, i)) != NULL && (pelt = mc->sparep) != NULL)
+ pelt->unsorted = 0;
+ }
+}
+
+
+void
+sort_sort_callback(MAILSTREAM *stream, long unsigned int *list, long unsigned int nmsgs)
+{
+ long i;
+
+ dprint((2, "sort_sort_callback\n"));
+
+ if(mn_get_total(g_sort.msgmap) < nmsgs)
+ panic("Message count shrank after sort!");
+
+ /* copy ulongs to array of longs */
+ for(i = nmsgs; i > 0; i--)
+ g_sort.msgmap->sort[i] = list ? (long) list[i-1] : i-1;
+
+ /* reset the inverse array */
+ msgno_reset_isort(g_sort.msgmap);
+
+ dprint((2, "sort_sort_callback done\n"));
+}
+
+
+/*
+ * Return value for use by progress bar.
+ */
+int
+percent_sorted(void)
+{
+ /*
+ * C-client's sort routine exports two types of status
+ * indicators. One's the progress thru loading the cache (typically
+ * the elephantine bulk of the delay, and the progress thru the
+ * actual sort (typically qsort). Our job is to balance the two
+ */
+
+ return(g_sort.prog && g_sort.prog->nmsgs
+ ? (((((g_sort.prog->progress.cached * 100)
+ / g_sort.prog->nmsgs) * 9) / 10)
+ + (((g_sort.prog->progress.sorted) * 10)
+ / g_sort.prog->nmsgs))
+ : 0);
+}
+
+
+/*
+ * This is only used at startup time. It sets ps->def_sort and
+ * ps->def_sort_rev. The syntax of the sort_spec is type[/reverse].
+ * A reverse without a type is the same as arrival/reverse. A blank
+ * argument also means arrival/reverse.
+ */
+int
+decode_sort(char *sort_spec, SortOrder *def_sort, int *def_sort_rev)
+{
+ char *sep;
+ char *fix_this = NULL;
+ int x, reverse;
+
+ if(!sort_spec || !*sort_spec){
+ *def_sort = SortArrival;
+ *def_sort_rev = 0;
+ return(0);
+ }
+
+ if(struncmp(sort_spec, "reverse", strlen(sort_spec)) == 0){
+ *def_sort = SortArrival;
+ *def_sort_rev = 1;
+ return(0);
+ }
+
+ reverse = 0;
+ if((sep = strindex(sort_spec, '/')) != NULL){
+ *sep = '\0';
+ fix_this = sep;
+ sep++;
+ if(struncmp(sep, "reverse", strlen(sep)) == 0)
+ reverse = 1;
+ else{
+ *fix_this = '/';
+ return(-1);
+ }
+ }
+
+ for(x = 0; ps_global->sort_types[x] != EndofList; x++)
+ if(struncmp(sort_name(ps_global->sort_types[x]),
+ sort_spec, strlen(sort_spec)) == 0)
+ break;
+
+ if(fix_this)
+ *fix_this = '/';
+
+ if(ps_global->sort_types[x] == EndofList)
+ return(-1);
+
+ *def_sort = ps_global->sort_types[x];
+ *def_sort_rev = reverse;
+ return(0);
+}
+
+
+/*----------------------------------------------------------------------
+ Compare raw message numbers
+ ----*/
+int
+pine_compare_long(const qsort_t *a, const qsort_t *b)
+{
+ long *mess_a = (long *)a, *mess_b = (long *)b, mdiff;
+
+ return((mdiff = *mess_a - *mess_b) ? ((mdiff > 0L) ? 1 : -1) : 0);
+}
+
+/*
+ * reverse version of pine_compare_long
+ */
+int
+pine_compare_long_rev(const qsort_t *a, const qsort_t *b)
+{
+ long *mess_a = (long *)a, *mess_b = (long *)b, mdiff;
+
+ return((mdiff = *mess_a - *mess_b) ? ((mdiff < 0L) ? 1 : -1) : 0);
+}
+
+
+long *g_score_arr;
+
+/*
+ * This calculate all of the scores and also puts them into a temporary array
+ * for speed when sorting.
+ */
+void
+build_score_array(MAILSTREAM *stream, MSGNO_S *msgmap)
+{
+ SEARCHSET *searchset;
+ long msgno, cnt, nmsgs, rawmsgno;
+ long score;
+ MESSAGECACHE *mc;
+
+ nmsgs = mn_get_total(msgmap);
+ g_score_arr = (long *) fs_get((nmsgs+1) * sizeof(score));
+ memset(g_score_arr, 0, (nmsgs+1) * sizeof(score));
+
+ /*
+ * Build a searchset that contains everything except those that have
+ * already been looked up.
+ */
+
+ for(msgno=1L; msgno <= stream->nmsgs; msgno++)
+ if((mc = mail_elt(stream, msgno)) != NULL)
+ mc->sequence = 0;
+
+ for(cnt=0L, msgno=1L; msgno <= nmsgs; msgno++){
+ rawmsgno = mn_m2raw(msgmap, msgno);
+ if(get_msg_score(stream, rawmsgno) == SCORE_UNDEF
+ && rawmsgno > 0L && stream && rawmsgno <= stream->nmsgs
+ && (mc = mail_elt(stream, rawmsgno))){
+ mc->sequence = 1;
+ cnt++;
+ }
+ }
+
+ if(cnt){
+ searchset = build_searchset(stream);
+ (void)calculate_some_scores(stream, searchset, 0);
+ mail_free_searchset(&searchset);
+ }
+
+ /*
+ * Copy scores to g_score_arr. They should all be defined now but if
+ * they aren't assign score zero.
+ */
+ for(rawmsgno = 1L; rawmsgno <= nmsgs; rawmsgno++){
+ score = get_msg_score(stream, rawmsgno);
+ g_score_arr[rawmsgno] = (score == SCORE_UNDEF) ? 0L : score;
+ }
+}
+
+
+void
+free_score_array(void)
+{
+ if(g_score_arr)
+ fs_give((void **) &g_score_arr);
+}
+
+
+/*----------------------------------------------------------------------
+ Compare scores
+ ----*/
+int
+pine_compare_scores(const qsort_t *a, const qsort_t *b)
+{
+ long *mess_a = (long *)a, *mess_b = (long *)b, mdiff;
+ long sdiff;
+
+ return((sdiff = g_score_arr[*mess_a] - g_score_arr[*mess_b])
+ ? ((sdiff > 0L) ? 1 : -1)
+ : ((mdiff = *mess_a - *mess_b) ? ((mdiff > 0) ? 1 : -1) : 0));
+}
+
+
+void
+reset_sort_order(unsigned int flags)
+{
+ long rflags = ROLE_DO_OTHER;
+ PAT_STATE pstate;
+ PAT_S *pat;
+ SortOrder the_sort_order;
+ int sort_is_rev;
+
+ /* set default order */
+ the_sort_order = ps_global->def_sort;
+ sort_is_rev = ps_global->def_sort_rev;
+
+ if(ps_global->mail_stream && nonempty_patterns(rflags, &pstate)){
+ for(pat = first_pattern(&pstate); pat; pat = next_pattern(&pstate)){
+ if(match_pattern(pat->patgrp, ps_global->mail_stream, NULL,
+ NULL, NULL, SE_NOSERVER|SE_NOPREFETCH))
+ break;
+ }
+
+ if(pat && pat->action && !pat->action->bogus
+ && pat->action->sort_is_set){
+ the_sort_order = pat->action->sortorder;
+ sort_is_rev = pat->action->revsort;
+ }
+ }
+
+ sort_folder(ps_global->mail_stream, ps_global->msgmap,
+ the_sort_order, sort_is_rev, flags);
+}
diff --git a/pith/sort.h b/pith/sort.h
new file mode 100644
index 00000000..ce383a04
--- /dev/null
+++ b/pith/sort.h
@@ -0,0 +1,49 @@
+/*
+ * $Id: sort.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_SORT_INCLUDED
+#define PITH_SORT_INCLUDED
+
+
+#include "../pith/sorttype.h"
+#include "../pith/msgno.h"
+
+
+#define refresh_sort(S,M,F) sort_folder((S), (M), mn_get_sort(M), \
+ mn_get_revsort(M), (F))
+
+struct global_sort_data {
+ MSGNO_S *msgmap;
+ SORTPGM *prog;
+};
+
+
+/* sort flags */
+#define SRT_NON 0x0 /* None; no options set */
+#define SRT_VRB 0x1 /* Verbose */
+#define SRT_MAN 0x2 /* Sorted manually since opened */
+
+
+extern struct global_sort_data g_sort;
+
+
+/* exported protoypes */
+char *sort_name(SortOrder);
+void sort_folder(MAILSTREAM *, MSGNO_S *, SortOrder, int, unsigned);
+int decode_sort(char *, SortOrder *, int *);
+void reset_sort_order(unsigned);
+
+
+#endif /* PITH_SORT_INCLUDED */
diff --git a/pith/sorttype.h b/pith/sorttype.h
new file mode 100644
index 00000000..9d487ec7
--- /dev/null
+++ b/pith/sorttype.h
@@ -0,0 +1,32 @@
+/*
+ * $Id: sorttype.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_SORTTYPE_INCLUDED
+#define PITH_SORTTYPE_INCLUDED
+
+
+/*
+ * Code exists that is sensitive to this order. Don't change
+ * it unless you know what you're doing.
+ */
+typedef enum {SortSubject = 0, SortArrival, SortFrom, SortTo,
+ SortCc, SortDate, SortSize,
+ SortSubject2, SortScore, SortThread, EndofList} SortOrder;
+
+
+/* exported protoypes */
+
+
+#endif /* PITH_SORTTYPE_INCLUDED */
diff --git a/pith/state.c b/pith/state.c
new file mode 100644
index 00000000..e88c27f6
--- /dev/null
+++ b/pith/state.c
@@ -0,0 +1,318 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: state.c 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ state.c
+ Implements the Pine state management routines
+ ====*/
+
+
+#include "../pith/headers.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/init.h"
+#include "../pith/sort.h"
+#include "../pith/atttype.h"
+#include "../pith/util.h"
+#include "../pith/mailindx.h"
+#include "../pith/remote.h"
+#include "../pith/list.h"
+#include "../pith/smime.h"
+
+
+/*
+ * Globals referenced throughout pine...
+ */
+struct pine *ps_global; /* THE global variable! */
+
+#ifdef DEBUG
+/*
+ * Debug level and output file defined here, referenced globally.
+ * The debug file is opened and initialized below...
+ */
+int debug = DEFAULT_DEBUG;
+#endif
+
+
+/*----------------------------------------------------------------------
+ General use big buffer. It is used in the following places:
+ compose_mail: while parsing header of postponed message
+ append_message2: while writing header into folder
+ q_status_messageX: while doing printf formatting
+ addr_book: Used to return expanded address in. (Can only use here
+ because mm_log doesn't q_status on PARSE errors !)
+ alpine.c: When address specified on command line
+ init.c: When expanding variable values
+ and many many more...
+
+ ----*/
+char tmp_20k_buf[SIZEOF_20KBUF];
+
+
+/*
+ * new_pine_struct - allocate and fill in with default values a new pine struct
+ */
+struct pine *
+new_pine_struct(void)
+{
+ struct pine *p;
+
+ p = (struct pine *)fs_get(sizeof (struct pine));
+ memset((void *) p, 0, sizeof(struct pine));
+ p->def_sort = SortArrival;
+ p->sort_types[0] = SortSubject;
+ p->sort_types[1] = SortArrival;
+ p->sort_types[2] = SortFrom;
+ p->sort_types[3] = SortTo;
+ p->sort_types[4] = SortCc;
+ p->sort_types[5] = SortDate;
+ p->sort_types[6] = SortSize;
+ p->sort_types[7] = SortSubject2;
+ p->sort_types[8] = SortScore;
+ p->sort_types[9] = SortThread;
+ p->sort_types[10] = EndofList;
+#ifdef SMIME
+ /*
+ * We need to have access to p->smime even before calling
+ * smime_init() so that we can set do_encrypt and do_sign.
+ */
+ p->smime = new_smime_struct();
+#endif /* SMIME */
+ p->atmts = (ATTACH_S *) fs_get(sizeof(ATTACH_S));
+ p->atmts_allocated = 1;
+ p->atmts->description = NULL;
+ p->low_speed = 1;
+ p->init_context = -1;
+ msgno_init(&p->msgmap, 0L, SortArrival, 0);
+ init_init_vars(p);
+
+ return(p);
+}
+
+
+
+/*
+ * free_pine_struct -- free up allocated data in pine struct and then the
+ * struct itself
+ */
+void
+free_pine_struct(struct pine **pps)
+{
+ if(!(pps && (*pps)))
+ return;
+
+ if((*pps)->hostname != NULL)
+ fs_give((void **)&(*pps)->hostname);
+
+ if((*pps)->localdomain != NULL)
+ fs_give((void **)&(*pps)->localdomain);
+
+ if((*pps)->ttyo != NULL)
+ fs_give((void **)&(*pps)->ttyo);
+
+ if((*pps)->home_dir != NULL)
+ fs_give((void **)&(*pps)->home_dir);
+
+ if((*pps)->folders_dir != NULL)
+ fs_give((void **)&(*pps)->folders_dir);
+
+ if((*pps)->ui.homedir)
+ fs_give((void **)&(*pps)->ui.homedir);
+
+ if((*pps)->ui.login)
+ fs_give((void **)&(*pps)->ui.login);
+
+ if((*pps)->ui.fullname)
+ fs_give((void **)&(*pps)->ui.fullname);
+
+ free_index_format(&(*pps)->index_disp_format);
+
+ if((*pps)->conv_table){
+ if((*pps)->conv_table->table)
+ fs_give((void **) &(*pps)->conv_table->table);
+
+ if((*pps)->conv_table->from_charset)
+ fs_give((void **) &(*pps)->conv_table->from_charset);
+
+ if((*pps)->conv_table->to_charset)
+ fs_give((void **) &(*pps)->conv_table->to_charset);
+
+ fs_give((void **)&(*pps)->conv_table);
+ }
+
+ if((*pps)->pinerc)
+ fs_give((void **)&(*pps)->pinerc);
+
+#if defined(DOS) || defined(OS2)
+ if((*pps)->pine_dir)
+ fs_give((void **)&(*pps)->pine_dir);
+
+ if((*pps)->aux_files_dir)
+ fs_give((void **)&(*pps)->aux_files_dir);
+#endif
+
+ if((*pps)->display_charmap)
+ fs_give((void **)&(*pps)->display_charmap);
+
+ if((*pps)->keyboard_charmap)
+ fs_give((void **)&(*pps)->keyboard_charmap);
+
+ if((*pps)->posting_charmap)
+ fs_give((void **)&(*pps)->posting_charmap);
+
+#ifdef PASSFILE
+ if((*pps)->passfile)
+ fs_give((void **)&(*pps)->passfile);
+#endif /* PASSFILE */
+
+ if((*pps)->hdr_colors)
+ free_spec_colors(&(*pps)->hdr_colors);
+
+ if((*pps)->keywords)
+ free_keyword_list(&(*pps)->keywords);
+
+ if((*pps)->kw_colors)
+ free_spec_colors(&(*pps)->kw_colors);
+
+ if((*pps)->atmts){
+ int i;
+
+ for(i = 0; (*pps)->atmts[i].description; i++){
+ fs_give((void **) &(*pps)->atmts[i].description);
+ fs_give((void **) &(*pps)->atmts[i].number);
+ }
+
+ fs_give((void **) &(*pps)->atmts);
+ }
+
+ if((*pps)->msgmap)
+ msgno_give(&(*pps)->msgmap);
+
+ free_vars(*pps);
+
+ fs_give((void **) pps);
+}
+
+
+void
+free_pinerc_strings(struct pine **pps)
+{
+ if((*pps)->prc){
+ if((*pps)->prc->outstanding_pinerc_changes)
+ write_pinerc((*pps), Main, WRP_NONE);
+
+ if((*pps)->prc->rd)
+ rd_close_remdata(&(*pps)->prc->rd);
+
+ free_pinerc_s(&(*pps)->prc);
+ }
+
+ if((*pps)->pconf)
+ free_pinerc_s(&(*pps)->pconf);
+
+ if((*pps)->post_prc){
+ if((*pps)->post_prc->outstanding_pinerc_changes)
+ write_pinerc((*pps), Post, WRP_NONE);
+
+ if((*pps)->post_prc->rd)
+ rd_close_remdata(&(*pps)->post_prc->rd);
+
+ free_pinerc_s(&(*pps)->post_prc);
+ }
+}
+
+
+/*
+ * free_vars -- give back resources acquired when we defined the
+ * variables list
+ */
+void
+free_vars(struct pine *ps)
+{
+ register int i;
+
+ for(i = 0; ps && i <= V_LAST_VAR; i++)
+ free_variable_values(&ps->vars[i]);
+}
+
+
+void
+free_variable_values(struct variable *var)
+{
+ if(var){
+ if(var->is_list){
+ free_list_array(&var->current_val.l);
+ free_list_array(&var->main_user_val.l);
+ free_list_array(&var->post_user_val.l);
+ free_list_array(&var->global_val.l);
+ free_list_array(&var->fixed_val.l);
+ free_list_array(&var->cmdline_val.l);
+ }
+ else{
+ if(var->current_val.p)
+ fs_give((void **)&var->current_val.p);
+ if(var->main_user_val.p)
+ fs_give((void **)&var->main_user_val.p);
+ if(var->post_user_val.p)
+ fs_give((void **)&var->post_user_val.p);
+ if(var->global_val.p)
+ fs_give((void **)&var->global_val.p);
+ if(var->fixed_val.p)
+ fs_give((void **)&var->fixed_val.p);
+ if(var->cmdline_val.p)
+ fs_give((void **)&var->cmdline_val.p);
+ }
+ }
+}
+
+
+PINERC_S *
+new_pinerc_s(char *name)
+{
+ PINERC_S *prc = NULL;
+
+ if(name){
+ prc = (PINERC_S *)fs_get(sizeof(*prc));
+ memset((void *)prc, 0, sizeof(*prc));
+ prc->name = cpystr(name);
+ if(IS_REMOTE(name))
+ prc->type = RemImap;
+ else
+ prc->type = Loc;
+ }
+
+ return(prc);
+}
+
+
+void
+free_pinerc_s(PINERC_S **prc)
+{
+ if(prc && *prc){
+ if((*prc)->name)
+ fs_give((void **)&(*prc)->name);
+
+ if((*prc)->rd)
+ rd_free_remdata(&(*prc)->rd);
+
+ if((*prc)->pinerc_lines)
+ free_pinerc_lines(&(*prc)->pinerc_lines);
+
+ fs_give((void **)prc);
+ }
+}
diff --git a/pith/state.h b/pith/state.h
new file mode 100644
index 00000000..93db4f4b
--- /dev/null
+++ b/pith/state.h
@@ -0,0 +1,374 @@
+/*
+ * $Id: state.h 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_STATE_INCLUDED
+#define PITH_STATE_INCLUDED
+
+
+#include "../pith/conftype.h"
+#include "../pith/indxtype.h"
+#include "../pith/bitmap.h"
+#include "../pith/charset.h"
+#include "../pith/context.h"
+#include "../pith/keyword.h"
+#include "../pith/atttype.h"
+#include "../pith/msgno.h"
+#include "../pith/pattern.h"
+#include "../pith/pipe.h"
+#include "../pith/send.h"
+#include "../pith/sorttype.h"
+#include "../pith/stream.h"
+#include "../pith/color.h"
+#include "../pith/user.h"
+
+
+/*
+ * Printing control structure
+ */
+typedef struct print_ctrl {
+#ifndef DOS
+ PIPE_S *pipe; /* control struct for pipe to write text */
+ FILE *fp; /* file pointer to write printed text into */
+ char *result; /* file containing print command's output */
+#endif
+#ifdef OS2
+ int ispipe;
+#endif
+ int err; /* bit indicating something went awry */
+} PRINT_S;
+
+
+/*
+ * Keeps track of display dimensions
+ */
+struct ttyo {
+ int screen_rows,
+ screen_cols,
+ header_rows, /* number of rows for titlebar and whitespace */
+ footer_rows; /* number of rows for status and keymenu */
+};
+
+/*
+ * HEADER_ROWS is always 2. 1 for the titlebar and 1 for the
+ * blank line after the titlebar. We should probably make it go down
+ * to 0 when the screen shrinks but instead we're just figuring out
+ * if there is enough room by looking at screen_rows.
+ * FOOTER_ROWS is either 3 or 1. Normally it is 3, 2 for the keymenu plus 1
+ * for the status line. If the NoKeyMenu command has been given, then it is 1.
+ */
+#define HEADER_ROWS(X) ((X)->ttyo->header_rows)
+#define FOOTER_ROWS(X) ((X)->ttyo->footer_rows)
+
+
+/*----------------------------------------------------------------------
+ This structure sort of takes the place of global variables or perhaps
+is the global variable. (It can be accessed globally as ps_global. One
+advantage to this is that as soon as you see a reference to the structure
+you know it is a global variable.
+ In general it is treated as global by the lower level and utility
+routines, but it is not treated so by the main screen driving routines.
+Each of them receives it as an argument and then sets ps_global to the
+argument they received. This is sort of with the thought that things
+might be coupled more loosely one day and that Pine might run where there
+is more than one window and more than one instance. But we haven't kept
+up with this convention very well.
+ ----*/
+
+struct pine {
+ void (*next_screen)(struct pine *); /* See loop at end of main() for how */
+ void (*prev_screen)(struct pine *); /* these are used... */
+ void (*redrawer)(void); /* NULL means stay in current screen */
+
+ CONTEXT_S *context_list; /* list of user defined contexts */
+ CONTEXT_S *context_current; /* default open context */
+ CONTEXT_S *context_last; /* most recently open context */
+
+ SP_S s_pool; /* stream pool */
+
+ char inbox_name[MAXFOLDER+1];
+ char pine_pre_vers[10]; /* highest version previously run */
+ char vers_internal[10];
+
+ MAILSTREAM *mail_stream; /* ptr to current folder stream */
+ MSGNO_S *msgmap; /* ptr to current message map */
+
+ unsigned read_predicted:1;
+
+ char cur_folder[MAXPATH+1];
+ QUOTALIST *quota;
+ char last_unambig_folder[MAXPATH+1];
+ char last_save_folder[MAXPATH+1];
+ CONTEXT_S *last_save_context;
+ ATTACH_S *atmts;
+ int atmts_allocated;
+ int remote_abook_validity; /* minutes, -1=never, 0=only on opens */
+
+ INDEX_COL_S *index_disp_format;
+
+ char *folders_dir;
+
+ unsigned mangled_footer:1; /* footer needs repainting */
+ unsigned mangled_header:1; /* header needs repainting */
+ unsigned mangled_body:1; /* body of screen needs repainting */
+ unsigned mangled_screen:1; /* whole screen needs repainting */
+
+ unsigned in_init_seq:1; /* executing initial cmd list */
+ unsigned save_in_init_seq:1;
+ unsigned dont_use_init_cmds:1; /* use keyboard input when true */
+
+ unsigned give_fixed_warning:1; /* warn user about "fixed" vars */
+ unsigned fix_fixed_warning:1; /* offer to fix it */
+
+ unsigned user_says_cancel:1; /* user typed ^C to abort open */
+
+ unsigned unseen_in_view:1;
+ unsigned start_in_context:1; /* start fldr_scrn in current cntxt */
+ unsigned def_sort_rev:1; /* true if reverse sort is default */
+ unsigned restricted:1;
+
+ unsigned save_msg_rule:5;
+ unsigned fcc_rule:3;
+ unsigned ab_sort_rule:3;
+ unsigned color_style:3;
+ unsigned index_color_style:3;
+ unsigned titlebar_color_style:3;
+ unsigned fld_sort_rule:3;
+ unsigned inc_startup_rule:3;
+ unsigned pruning_rule:3;
+ unsigned reopen_rule:4;
+ unsigned goto_default_rule:3;
+ unsigned thread_disp_style:3;
+ unsigned thread_index_style:3;
+
+ unsigned full_header:2; /* display full headers */
+ /* 0 means normal */
+ /* 1 means display all quoted text */
+ /* 2 means full headers */
+ unsigned some_quoting_was_suppressed:1;
+ unsigned orig_use_fkeys:1;
+ unsigned try_to_create:1; /* Save should try mail_create */
+ unsigned low_speed:1; /* various opt's 4 low connect speed */
+ unsigned postpone_no_flow:1; /* don't set flowed when we postpone */
+ /* and don't reflow when we resume. */
+ unsigned mm_log_error:1;
+ unsigned show_new_version:1;
+ unsigned pre441:1;
+ unsigned first_time_user:1;
+ unsigned intr_pending:1; /* received SIGINT and haven't acted */
+ unsigned expunge_in_progress:1; /* don't want to re-enter c-client */
+ unsigned never_allow_changing_from:1; /* not even for roles */
+ unsigned newthread:1; /* start a new thread on composing */
+
+ unsigned readonly_pinerc:1;
+ unsigned view_all_except:1;
+ unsigned start_in_index:1; /* cmd line flag modified on startup */
+ unsigned noshow_error:1; /* c-client error callback controls */
+ unsigned noshow_warn:1;
+ unsigned noshow_timeout:1;
+ unsigned conceal_sensitive_debugging:1;
+ unsigned turn_off_threading_temporarily:1;
+ unsigned view_skipped_index:1;
+ unsigned a_format_contains_score:1;
+ unsigned ugly_consider_advancing_bit:1;
+ unsigned dont_count_flagchanges:1;
+ unsigned in_folder_screen:1;
+ unsigned noticed_change_in_unseen:1;
+ unsigned first_open_was_attempted:1;
+ unsigned force_prefer_plain:1;
+ unsigned force_no_prefer_plain:1;
+
+ unsigned phone_home:1;
+ unsigned painted_body_on_startup:1;
+ unsigned painted_footer_on_startup:1;
+ unsigned open_readonly_on_startup:1;
+ unsigned exit_if_no_pinerc:1;
+ unsigned pass_ctrl_chars:1;
+ unsigned pass_c1_ctrl_chars:1;
+ unsigned display_keywords_in_subject:1;
+ unsigned display_keywordinits_in_subject:1;
+ unsigned beginning_of_month:1;
+ unsigned beginning_of_year:1;
+
+ unsigned viewer_overlap:8;
+ unsigned scroll_margin:8;
+ unsigned remote_abook_history:8;
+
+#if defined(DOS) || defined(OS2)
+ unsigned blank_user_id:1;
+ unsigned blank_personal_name:1;
+ unsigned blank_user_domain:1;
+#ifdef _WINDOWS
+ unsigned update_registry:2;
+ unsigned install_flag:1;
+#endif
+#endif
+
+ unsigned debug_malloc:6;
+ unsigned debug_timestamp:1;
+ unsigned debug_flush:1;
+ unsigned debug_tcp:1;
+ unsigned debug_imap:3;
+ unsigned debug_nfiles:5;
+ unsigned debugmem:1;
+#ifdef LOCAL_PASSWD_CACHE
+ unsigned nowrite_password_cache:1;
+#endif
+
+ unsigned convert_sigs:1;
+ unsigned dump_supported_options:1;
+
+ unsigned noexpunge_on_close:1;
+
+ unsigned no_newmail_check_from_optionally_enter:1;
+
+ unsigned post_utf8:1;
+
+ unsigned start_entry; /* cmd line arg: msg # to start on */
+
+ bitmap_t feature_list; /* a bitmap of all the features */
+ char **feat_list_back_compat;
+
+ SPEC_COLOR_S *hdr_colors; /* list of configed colors for view */
+
+ short init_context;
+
+ int *initial_cmds; /* cmds to execute on startup */
+ int *free_initial_cmds; /* used to free when done */
+
+ char c_client_error[300]; /* when nowhow_error is set and PARSE */
+
+ struct ttyo *ttyo;
+
+ USER_S ui; /* system derived user info */
+
+ POST_S *post;
+
+ char *home_dir,
+ *hostname, /* Fully qualified hostname */
+ *localdomain, /* The (DNS) domain this host resides in */
+ *userdomain, /* The per user domain from .pinerc or */
+ *maildomain, /* Domain name for most uses */
+#if defined(DOS) || defined(OS2)
+ *pine_dir, /* argv[0] as provided by DOS */
+ *aux_files_dir, /* User's auxiliary files directory */
+#endif
+#ifdef PASSFILE
+ *passfile,
+#endif /* PASSFILE */
+ *pinerc, /* Location of user's pinerc */
+ *exceptions, /* Location of user's exceptions */
+ *pine_name; /* name we were invoked under */
+ PINERC_S *prc, /* structure for personal pinerc */
+ *post_prc, /* structure for post-loaded pinerc */
+ *pconf; /* structure for global pinerc */
+
+ EditWhich ew_for_except_vars;
+ EditWhich ew_for_role_take;
+ EditWhich ew_for_score_take;
+ EditWhich ew_for_filter_take;
+ EditWhich ew_for_incol_take;
+ EditWhich ew_for_other_take;
+ EditWhich ew_for_srch_take;
+
+ SortOrder def_sort, /* Default sort type */
+ sort_types[22];
+
+ int last_expire_year, last_expire_month;
+
+ int printer_category;
+
+ int status_msg_delay;
+
+ int active_status_interval;
+
+ int composer_fillcol;
+
+ int nmw_width;
+
+ int hours_to_timeout;
+
+ int tcp_query_timeout;
+
+ int inc_check_timeout;
+ int inc_check_interval; /* for local and IMAP */
+ int inc_second_check_interval; /* for other */
+
+ time_t check_interval_for_noncurr;
+
+ time_t last_nextitem_forcechk;
+
+ MAILSTREAM *cur_uid_stream;
+ imapuid_t cur_uid;
+
+ int deadlets;
+
+ int quote_suppression_threshold;
+
+ char *display_charmap; /* needs to be freed */
+ char *keyboard_charmap; /* needs to be freed */
+ void *input_cs;
+
+ char *posting_charmap; /* needs to be freed */
+
+ CONV_TABLE *conv_table;
+
+ /*
+ * Optional tools Pine Data Engine caller might provide
+ */
+ struct {
+ char *(*display_filter)(char *, STORE_S *, gf_io_t, FILTLIST_S *);
+ char *(*display_filter_trigger)(BODY *, char *, size_t);
+ } tools;
+
+ KEYWORD_S *keywords;
+ SPEC_COLOR_S *kw_colors;
+
+ ACTION_S *default_role; /* pointer to one of regular roles */
+
+ char last_error[500];
+ INIT_ERR_S *init_errs;
+
+ PRINT_S *print;
+
+#ifdef SMIME
+ SMIME_STUFF_S *smime;
+#endif /* SMIME */
+
+ struct variable *vars;
+};
+
+
+/*----------------------------------------------------------------------
+ The few global variables we use in Pine Data Engine
+ ----*/
+
+extern struct pine *ps_global;
+
+#define SIZEOF_20KBUF (20480)
+extern char tmp_20k_buf[];
+
+
+/* exported protoypes */
+struct pine *new_pine_struct(void);
+void free_pine_struct(struct pine **);
+void free_pinerc_strings(struct pine **);
+void free_vars(struct pine *);
+void free_variable_values(struct variable *);
+PINERC_S *new_pinerc_s(char *);
+void free_pinerc_s(PINERC_S **);
+
+
+#endif /* PITH_STATE_INCLUDED */
diff --git a/pith/status.c b/pith/status.c
new file mode 100644
index 00000000..b2c8dc00
--- /dev/null
+++ b/pith/status.c
@@ -0,0 +1,163 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: status.c 761 2007-10-23 22:35:18Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/status.h"
+#include "../pith/state.h"
+
+
+/*----------------------------------------------------------------------
+ Put a message with 1 printf argument on queue for status line
+
+ Args: min_t -- minimum time to display message for
+ max_t -- minimum time to display message for
+ s -- printf style control string
+ a -- argument for printf
+
+ Result: message queued
+ ----*/
+
+/*VARARGS1*/
+void
+q_status_message1(int flags, int min_t, int max_t, char *s, void *a)
+{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, s, a);
+ q_status_message(flags, min_t, max_t, tmp_20k_buf);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Put a message with 2 printf argument on queue for status line
+
+ Args: min_t -- minimum time to display message for
+ max_t -- maximum time to display message for
+ s -- printf style control string
+ a1 -- argument for printf
+ a2 -- argument for printf
+
+ Result: message queued
+ ---*/
+
+/*VARARGS1*/
+void
+q_status_message2(int flags, int min_t, int max_t, char *s, void *a1, void *a2)
+{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, s, a1, a2);
+ q_status_message(flags, min_t, max_t, tmp_20k_buf);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Put a message with 3 printf argument on queue for status line
+
+ Args: min_t -- minimum time to display message for
+ max_t -- maximum time to display message for
+ s -- printf style control string
+ a1 -- argument for printf
+ a2 -- argument for printf
+ a3 -- argument for printf
+
+ Result: message queued
+ ---*/
+
+/*VARARGS1*/
+void
+q_status_message3(int flags, int min_t, int max_t, char *s, void *a1, void *a2, void *a3)
+{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, s, a1, a2, a3);
+ q_status_message(flags, min_t, max_t, tmp_20k_buf);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Put a message with 4 printf argument on queue for status line
+
+
+ Args: min_t -- minimum time to display message for
+ max_t -- maximum time to display message for
+ s -- printf style control string
+ a1 -- argument for printf
+ a2 -- argument for printf
+ a3 -- argument for printf
+ a4 -- argument for printf
+
+ Result: message queued
+ ----------------------------------------------------------------------*/
+/*VARARGS1*/
+void
+q_status_message4(int flags, int min_t, int max_t, char *s, void *a1, void *a2, void *a3, void *a4)
+{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, s, a1, a2, a3, a4);
+ q_status_message(flags, min_t, max_t, tmp_20k_buf);
+}
+
+
+/*VARARGS1*/
+void
+q_status_message5(int flags, int min_t, int max_t, char *s, void *a1, void *a2, void *a3, void *a4, void *a5)
+{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, s, a1, a2, a3, a4, a5);
+ q_status_message(flags, min_t, max_t, tmp_20k_buf);
+}
+
+
+/*VARARGS1*/
+void
+q_status_message6(int flags, int min_t, int max_t, char *s, void *a1, void *a2, void *a3, void *a4, void *a5, void *a6)
+{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, s, a1, a2, a3, a4, a5, a6);
+ q_status_message(flags, min_t, max_t, tmp_20k_buf);
+}
+
+
+/*----------------------------------------------------------------------
+ Put a message with 7 printf argument on queue for status line
+
+
+ Args: min_t -- minimum time to display message for
+ max_t -- maximum time to display message for
+ s -- printf style control string
+ a1 -- argument for printf
+ a2 -- argument for printf
+ a3 -- argument for printf
+ a4 -- argument for printf
+ a5 -- argument for printf
+ a6 -- argument for printf
+ a7 -- argument for printf
+
+
+ Result: message queued
+ ----------------------------------------------------------------------*/
+/*VARARGS1*/
+void
+q_status_message7(int flags, int min_t, int max_t, char *s, void *a1, void *a2, void *a3, void *a4, void *a5, void *a6, void *a7)
+{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, s, a1, a2, a3, a4, a5, a6, a7);
+ q_status_message(flags, min_t, max_t, tmp_20k_buf);
+}
+
+
+/*VARARGS1*/
+void
+q_status_message8(int flags, int min_t, int max_t, char *s, void *a1, void *a2, void *a3, void *a4, void *a5, void *a6, void *a7, void *a8)
+{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, s, a1, a2, a3, a4, a5, a6, a7, a8);
+ q_status_message(flags, min_t, max_t, tmp_20k_buf);
+}
diff --git a/pith/status.h b/pith/status.h
new file mode 100644
index 00000000..1673edd5
--- /dev/null
+++ b/pith/status.h
@@ -0,0 +1,54 @@
+/*
+ * $Id: status.h 770 2007-10-24 00:23:09Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_STATUS_INCLUDED
+#define PITH_STATUS_INCLUDED
+
+
+#include <general.h>
+
+
+/*
+ * Status message types
+ */
+#define SM_DING 0x01 /* ring bell when displayed */
+#define SM_ASYNC 0x02 /* display any time */
+#define SM_ORDER 0x04 /* ordered, flush on prompt */
+#define SM_MODAL 0x08 /* display, wait for user */
+#define SM_INFO 0x10 /* Handy, but discardable */
+
+
+/* exported protoypes */
+void q_status_message1(int, int, int, char *, void *);
+void q_status_message2(int, int, int, char *, void *, void *);
+void q_status_message3(int, int, int, char *, void *, void *, void *);
+void q_status_message4(int, int, int, char *, void *, void *, void *, void *);
+void q_status_message5(int, int, int, char *, void *, void *, void *, void *, void *);
+void q_status_message6(int, int, int, char *, void *, void *, void *, void *, void *, void *);
+void q_status_message7(int, int, int, char *, void *, void *,
+ void *, void *, void *, void *, void *);
+void q_status_message8(int, int, int, char *, void *, void *,
+ void *, void *, void *, void *, void *, void *);
+
+
+/* currently mandatory to implement stubs */
+void q_status_message(int, int, int, char *);
+int status_message_remaining(void);
+int display_message(UCS);
+void flush_status_messages(int);
+
+
+#endif /* PITH_STATUS_INCLUDED */
diff --git a/pith/store.c b/pith/store.c
new file mode 100644
index 00000000..e8508257
--- /dev/null
+++ b/pith/store.c
@@ -0,0 +1,1021 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: store.c 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*
+ * GENERALIZED STORAGE FUNCTIONS. Idea is to allow creation of
+ * storage objects that can be written into and read from without
+ * the caller knowing if the storage is core or in a file
+ * or whatever.
+ */
+
+
+#include "../pith/headers.h"
+#include "../pith/store.h"
+#include "../pith/status.h"
+#include "../pith/state.h"
+#include "../pico/keydefs.h"
+#ifdef SMIME
+#include <openssl/buffer.h>
+#endif /* SMIME */
+
+
+/*
+ * Internal prototypes
+ */
+void *so_file_open(STORE_S *);
+int so_cs_writec(int, STORE_S *);
+int so_cs_writec_locale(int, STORE_S *);
+int so_file_writec(int, STORE_S *);
+int so_file_writec_locale(int, STORE_S *);
+int so_cs_readc(unsigned char *, STORE_S *);
+int so_cs_readc_locale(unsigned char *, STORE_S *);
+int so_cs_readc_getchar(unsigned char *c, void *extraarg);
+int so_file_readc(unsigned char *, STORE_S *);
+int so_file_readc_locale(unsigned char *, STORE_S *);
+int so_file_readc_getchar(unsigned char *c, void *extraarg);
+int so_cs_puts(STORE_S *, char *);
+int so_cs_puts_locale(STORE_S *, char *);
+int so_file_puts(STORE_S *, char *);
+int so_file_puts_locale(STORE_S *, char *);
+int so_reaquire(STORE_S *);
+#ifdef _WINDOWS
+int so_file_readc_windows(unsigned char *, STORE_S *);
+#endif /* _WINDOWS */
+#ifdef SMIME
+int so_bio_writec(int, STORE_S *);
+int so_bio_readc(unsigned char *, STORE_S *);
+int so_bio_puts(STORE_S *, char *);
+#endif /* SMIME */
+
+
+/*
+ * place holders for externally defined storage object driver
+ */
+static struct externalstoreobjectdata {
+ STORE_S *(*get)(void);
+ int (*give)(STORE_S **);
+ int (*writec)(int, STORE_S *);
+ int (*readc)(unsigned char *, STORE_S *);
+ int (*puts)(STORE_S *, char *);
+ int (*seek)(STORE_S *, long, int);
+ int (*truncate)(STORE_S *, off_t);
+ int (*tell)(STORE_S *);
+} etsod;
+
+
+#define MSIZE_INIT 8192
+#define MSIZE_INC 4096
+
+
+#ifdef S_IREAD
+#define OP_MD_USER (S_IREAD | S_IWRITE)
+#else
+#define OP_MD_USER 0600
+#endif
+
+#ifdef S_IRUSR
+#define OP_MD_ALL (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | \
+ S_IROTH | S_IWOTH)
+#else
+#define OP_MD_ALL 0666
+#endif
+
+
+/*
+ * allocate resources associated with the specified type of
+ * storage. If requesting a named file object, open it for
+ * appending, else just open a temp file.
+ *
+ * return the filled in storage object
+ */
+STORE_S *
+so_get(SourceType source, char *name, int rtype)
+ /* requested storage type */
+ /* file name */
+ /* file access type */
+{
+ STORE_S *so = (STORE_S *)fs_get(sizeof(STORE_S));
+
+ memset(so, 0, sizeof(STORE_S));
+ so->flags |= rtype;
+
+ so->cb.cbuf[0] = '\0';
+ so->cb.cbufp = so->cb.cbuf;
+ so->cb.cbufend = so->cb.cbuf;
+
+ if(name) /* stash the name */
+ so->name = cpystr(name);
+#ifdef DOS
+ else if(source == TmpFileStar || source == FileStar){
+ /*
+ * Coerce to TmpFileStar. The MSC library's "tmpfile()"
+ * doesn't observe the "TMP" or "TEMP" environment vars and
+ * always wants to write "\". This is problematic in shared,
+ * networked environments.
+ */
+ source = TmpFileStar;
+ so->name = temp_nam(NULL, "pi");
+ }
+#else
+ else if(source == TmpFileStar) /* make one up! */
+ so->name = temp_nam(NULL, "pine-tmp");
+#endif
+
+ so->src = source;
+ if(so->src == FileStar || so->src == TmpFileStar){
+#ifdef _WINDOWS
+ so->writec = so_file_writec;
+ so->readc = (rtype & READ_FROM_LOCALE) ? so_file_readc_windows
+ : so_file_readc;
+#else /* UNIX */
+ so->writec = (rtype & WRITE_TO_LOCALE) ? so_file_writec_locale
+ : so_file_writec;
+ so->readc = (rtype & READ_FROM_LOCALE) ? so_file_readc_locale
+ : so_file_readc;
+#endif /* UNIX */
+ so->puts = (rtype & WRITE_TO_LOCALE) ? so_file_puts_locale
+ : so_file_puts;
+
+ /*
+ * The reason for both FileStar and TmpFileStar types is
+ * that, named or unnamed, TmpFileStar's are unlinked
+ * when the object is given back to the system. This is
+ * useful for keeping us from running out of file pointers as
+ * the pointer associated with the object can be temporarily
+ * returned to the system without destroying the object.
+ *
+ * The programmer is warned to be careful not to assign the
+ * TmpFileStar type to any files that are expected to remain
+ * after the dust has settled!
+ */
+ if(so->name){
+ if(!(so->txt = so_file_open(so))){
+ dprint((1, "so_get error: %s : %s",
+ so->name ? so->name : "?",
+ error_description(errno)));
+ if(source == TmpFileStar)
+ our_unlink(so->name);
+
+ fs_give((void **)&so->name);
+ fs_give((void **)&so); /* so freed & set to NULL */
+ }
+ }
+ else{
+ if(!(so->txt = (void *) create_tmpfile())){
+ dprint((1, "so_get error: tmpfile : %s",
+ error_description(errno)));
+ fs_give((void **)&so); /* so freed & set to NULL */
+ }
+ }
+ }
+ else if(so->src == ExternalText && etsod.get){
+ so->writec = etsod.writec;
+ so->readc = etsod.readc;
+ so->puts = etsod.puts;
+ if(!(so->txt = (*etsod.get)())){
+ dprint((1, "so_get error: external driver allocation error"));
+
+ if(so->name)
+ fs_give((void **)&so->name);
+
+ fs_give((void **)&so); /* so freed & set to NULL */
+ }
+ }
+#ifdef SMIME
+ else if(so->src == BioType){
+ so->writec = so_bio_writec;
+ so->readc = so_bio_readc;
+ so->puts = so_bio_puts;
+
+ if(!(so->txt = BIO_new(BIO_s_mem()))){
+ dprint((1, "so_get error: BIO driver allocation error"));
+
+ if(so->name)
+ fs_give((void **) &so->name);
+
+ fs_give((void **) &so); /* so freed & set to NULL */
+ }
+ }
+#endif /* SMIME */
+ else{
+ so->writec = (rtype & WRITE_TO_LOCALE) ? so_cs_writec_locale
+ : so_cs_writec;
+ so->readc = (rtype & READ_FROM_LOCALE) ? so_cs_readc_locale
+ : so_cs_readc;
+ so->puts = (rtype & WRITE_TO_LOCALE) ? so_cs_puts_locale
+ : so_cs_puts;
+
+ so->txt = (void *)fs_get((size_t) MSIZE_INIT * sizeof(char));
+ so->dp = so->eod = (unsigned char *) so->txt;
+ so->eot = so->dp + MSIZE_INIT;
+ memset(so->eod, 0, so->eot - so->eod);
+ }
+
+ return(so);
+}
+
+
+/*
+ * so_give - free resources associated with a storage object and then
+ * the object itself.
+ */
+int
+so_give(STORE_S **so)
+{
+ int ret = 0;
+
+ if(!(so && (*so)))
+ return(ret);
+
+ if((*so)->src == FileStar || (*so)->src == TmpFileStar){
+ if((*so)->txt) /* disassociate from storage */
+ ret = fclose((FILE *)(*so)->txt) == EOF ? -1 : 0;
+
+ if((*so)->name && (*so)->src == TmpFileStar)
+ our_unlink((*so)->name); /* really disassociate! */
+ }
+ else if((*so)->txt && (*so)->src == ExternalText){
+ if(etsod.give)
+ (*etsod.give)((*so)->txt);
+ }
+#ifdef SMIME
+ else if((*so)->txt && (*so)->src == BioType){
+ BIO *b = (BIO *) (*so)->txt;
+
+ BIO_free(b);
+ }
+#endif /* SMIME */
+ else if((*so)->txt)
+ fs_give((void **)&((*so)->txt));
+
+ if((*so)->name)
+ fs_give((void **)&((*so)->name)); /* blast the name */
+
+ /* release attribute list */
+ mail_free_body_parameter(&(*so)->attr);
+
+ fs_give((void **)so); /* release the object */
+
+ return(ret);
+}
+
+
+/*
+ * so_register_external_driver - hokey way to get pico-dependent storage object
+ * support out of pith library
+ */
+void
+so_register_external_driver(STORE_S *(*get)(void),
+ int (*give)(STORE_S **),
+ int (*writec)(int, STORE_S *),
+ int (*readc)(unsigned char *, STORE_S *),
+ int (*puts)(STORE_S *, char *),
+ int (*seek)(STORE_S *, long int, int),
+ int (*truncate)(STORE_S *, off_t),
+ int (*tell)(STORE_S *))
+{
+ memset(&etsod, 0, sizeof(etsod));
+ if(get)
+ etsod.get = get;
+
+ if(give)
+ etsod.give = give;
+
+ if(writec)
+ etsod.writec = writec;
+
+ if(readc)
+ etsod.readc = readc;
+
+ if(puts)
+ etsod.puts = puts;
+
+ if(seek)
+ etsod.seek = seek;
+
+ if(truncate)
+ etsod.truncate = truncate;
+
+ if(tell)
+ etsod.tell = tell;
+}
+
+
+void *
+so_file_open(STORE_S *so)
+{
+ char *type = ((so->flags) & WRITE_ACCESS) ? "a+" : "r";
+ int flags, fd,
+ mode = (((so->flags) & OWNER_ONLY) || so->src == TmpFileStar)
+ ? OP_MD_USER : OP_MD_ALL;
+
+ /*
+ * Careful. EDIT_ACCESS and WRITE_TO_LOCALE/READ_FROM_LOCALE will
+ * not work together.
+ */
+ if(so->flags & WRITE_ACCESS){
+ flags = O_RDWR | O_CREAT | O_APPEND | O_BINARY;
+ }
+ else{
+ flags = O_RDONLY;
+ if(so->flags & READ_FROM_LOCALE){
+ flags |= _O_WTEXT;
+ }
+ else{
+ flags |= O_BINARY;
+ }
+ }
+
+ /*
+ * Use open instead of fopen so we can make temp files private.
+ * I believe the "b" or "t" in the type isn't necessary in the fdopen call
+ * because we already set O_BINARY or _O_U8TEXT or _O_WTEXT in the open.
+ */
+ return(((fd = our_open(so->name, flags, mode)) > -1)
+ ? (so->txt = (void *) fdopen(fd, type)) : NULL);
+}
+
+
+/*
+ * put a character into the specified storage object,
+ * expanding if neccessary
+ *
+ * return 1 on success and 0 on failure
+ */
+int
+so_cs_writec(int c, STORE_S *so)
+{
+ if(so->dp >= so->eot){
+ size_t incr;
+ size_t cur_o = so->dp - (unsigned char *) so->txt;
+ size_t data_o = so->eod - (unsigned char *) so->txt;
+ size_t size = (so->eot - (unsigned char *) so->txt);
+
+ /*
+ * We estimate the size we're going to need at the beginning
+ * so it shouldn't normally happen that we run out of space and
+ * come here. If we do come here, it is probably because there
+ * are lots of handles in the message or lots of quote coloring.
+ * In either case, the overhead of those is high so we don't want
+ * to keep adding little bits and resizing. Add 50% each time
+ * instead.
+ */
+ incr = MAX(size/2, MSIZE_INC);
+ size += incr;
+
+ fs_resize(&so->txt, size * sizeof(char));
+ so->dp = (unsigned char *) so->txt + cur_o;
+ so->eod = (unsigned char *) so->txt + data_o;
+ so->eot = (unsigned char *) so->txt + size;
+ memset(so->eod, 0, so->eot - so->eod);
+ }
+
+ *so->dp++ = (unsigned char) c;
+ if(so->dp > so->eod)
+ so->eod = so->dp;
+
+ return(1);
+}
+
+
+/*
+ * The locale version converts from UTF-8 to user's locale charset
+ * before writing the characters.
+ */
+int
+so_cs_writec_locale(int c, STORE_S *so)
+{
+ int rv = 1;
+ int i, outchars;
+ unsigned char obuf[MAX(MB_LEN_MAX,32)];
+
+ if((outchars = utf8_to_locale(c, &so->cb, obuf, sizeof(obuf))) != 0){
+ for(i = 0; i < outchars; i++)
+ if(so_cs_writec(obuf[i], so) != 1){
+ rv = 0;
+ break;
+ }
+ }
+
+ return(rv);
+}
+
+
+int
+so_file_writec(int c, STORE_S *so)
+{
+ unsigned char ch = (unsigned char) c;
+ int rv = 0;
+
+ if(so->txt || so_reaquire(so))
+ do
+ rv = fwrite(&ch,sizeof(unsigned char),(size_t)1,(FILE *)so->txt);
+ while(!rv && ferror((FILE *)so->txt) && errno == EINTR);
+
+ return(rv);
+}
+
+
+/*
+ * The locale version converts from UTF-8 to user's locale charset
+ * before writing the characters.
+ */
+int
+so_file_writec_locale(int c, STORE_S *so)
+{
+ int rv = 1;
+ int i, outchars;
+ unsigned char obuf[MAX(MB_LEN_MAX,32)];
+
+ if((outchars = utf8_to_locale(c, &so->cb, obuf, sizeof(obuf))) != 0){
+ for(i = 0; i < outchars; i++)
+ if(so_file_writec(obuf[i], so) != 1){
+ rv = 0;
+ break;
+ }
+ }
+
+ return(rv);
+}
+
+
+/*
+ * get a character from the specified storage object.
+ *
+ * return 1 on success and 0 on failure
+ */
+int
+so_cs_readc(unsigned char *c, STORE_S *so)
+{
+ return((so->dp < so->eod) ? *c = *(so->dp)++, 1 : 0);
+}
+
+
+/*
+ * The locale version converts from user's locale charset to UTF-8
+ * after reading the characters and before returning to the caller.
+ */
+int
+so_cs_readc_locale(unsigned char *c, STORE_S *so)
+{
+ return(generic_readc_locale(c, so_cs_readc_getchar, so, &so->cb));
+}
+
+
+int
+so_cs_readc_getchar(unsigned char *c, void *extraarg)
+{
+ STORE_S *so;
+
+ so = (STORE_S *) extraarg;
+ return(so_cs_readc(c, so));
+}
+
+
+int
+so_file_readc(unsigned char *c, STORE_S *so)
+{
+ int rv = 0;
+
+ if(so->txt || so_reaquire(so))
+ do
+ rv = fread(c, sizeof(char), (size_t)1, (FILE *)so->txt);
+ while(!rv && ferror((FILE *)so->txt) && errno == EINTR);
+
+ return(rv);
+}
+
+
+/*
+ * The locale version converts from user's locale charset to UTF-8
+ * after reading the characters and before returning to the caller.
+ */
+int
+so_file_readc_locale(unsigned char *c, STORE_S *so)
+{
+ return(generic_readc_locale(c, so_file_readc_getchar, so, &so->cb));
+}
+
+
+int
+so_file_readc_getchar(unsigned char *c, void *extraarg)
+{
+ STORE_S *so;
+
+ so = (STORE_S *) extraarg;
+ return(so_file_readc(c, so));
+}
+
+
+#ifdef _WINDOWS
+/*
+ * Read unicode characters from windows filesystem and return
+ * them as a stream of UTF-8 characters. The stream is assumed
+ * opened so that it will know how to put together the unicode.
+ */
+int
+so_file_readc_windows(unsigned char *c, STORE_S *so)
+{
+ int rv = 0;
+ UCS ucs;
+
+ /* already got some from previous call? */
+ if(so->cb.cbufend > so->cb.cbuf){
+ *c = *so->cb.cbufp;
+ so->cb.cbufp++;
+ rv++;
+ if(so->cb.cbufp >= so->cb.cbufend){
+ so->cb.cbufend = so->cb.cbuf;
+ so->cb.cbufp = so->cb.cbuf;
+ }
+
+ return(rv);
+ }
+
+ if(so->txt || so_reaquire(so)){
+ /* windows only so second arg is ignored */
+ ucs = read_a_wide_char((FILE *) so->txt, NULL);
+ rv = (ucs == CCONV_EOF) ? 0 : 1;
+ }
+
+ if(rv){
+ /*
+ * Now we need to convert the UCS character to UTF-8
+ * and dole out the UTF-8 one char at a time.
+ */
+ so->cb.cbufend = utf8_put(so->cb.cbuf, (unsigned long) ucs);
+ so->cb.cbufp = so->cb.cbuf;
+ if(so->cb.cbufend > so->cb.cbuf){
+ *c = *so->cb.cbufp;
+ so->cb.cbufp++;
+ if(so->cb.cbufp >= so->cb.cbufend){
+ so->cb.cbufend = so->cb.cbuf;
+ so->cb.cbufp = so->cb.cbuf;
+ }
+ }
+ else
+ *c = '?';
+ }
+
+ return(rv);
+}
+#endif /* _WINDOWS */
+
+
+/*
+ * write a string into the specified storage object,
+ * expanding if necessary (and cheating if the object
+ * happens to be a file!)
+ *
+ * return 1 on success and 0 on failure
+ */
+int
+so_cs_puts(STORE_S *so, char *s)
+{
+ int slen = strlen(s);
+
+ if(so->dp + slen >= so->eot){
+ register size_t cur_o = so->dp - (unsigned char *) so->txt;
+ register size_t data_o = so->eod - (unsigned char *) so->txt;
+ register size_t len = so->eot - (unsigned char *) so->txt;
+ while(len <= cur_o + slen + 1){
+ size_t incr;
+
+ incr = MAX(len/2, MSIZE_INC);
+ len += incr;
+ }
+
+ fs_resize(&so->txt, len * sizeof(char));
+ so->dp = (unsigned char *)so->txt + cur_o;
+ so->eod = (unsigned char *)so->txt + data_o;
+ so->eot = (unsigned char *)so->txt + len;
+ memset(so->eod, 0, so->eot - so->eod);
+ }
+
+ memcpy(so->dp, s, slen);
+ so->dp += slen;
+ if(so->dp > so->eod)
+ so->eod = so->dp;
+
+ return(1);
+}
+
+
+int
+so_cs_puts_locale(STORE_S *so, char *s)
+{
+ int slen = strlen(s);
+
+ while(slen--)
+ if(!so_cs_writec_locale((unsigned char) *s++, so))
+ return(0);
+
+ return(1);
+}
+
+
+int
+so_file_puts(STORE_S *so, char *s)
+{
+ int rv = *s ? 0 : 1;
+
+ if(!rv && (so->txt || so_reaquire(so)))
+ do
+ rv = fwrite(s, strlen(s)*sizeof(char), (size_t)1, (FILE *)so->txt);
+ while(!rv && ferror((FILE *)so->txt) && errno == EINTR);
+
+ return(rv);
+}
+
+
+int
+so_file_puts_locale(STORE_S *so, char *s)
+{
+ int slen = strlen(s);
+
+ while(slen--)
+ if(!so_file_writec_locale((unsigned char) *s++, so))
+ return(0);
+
+ return(1);
+}
+
+
+#ifdef SMIME
+/*
+ * put a character into the specified storage object,
+ * expanding if neccessary
+ *
+ * return 1 on success and 0 on failure
+ */
+int
+so_bio_writec(int c, STORE_S *so)
+{
+ if(so->txt && so->src == BioType){
+ unsigned char ch[1];
+ BIO *b = (BIO *) so->txt;
+
+ ch[0] = (unsigned char) (c & 0xff);
+
+ if(BIO_write(b, ch, 1) >= 1)
+ return(1);
+ }
+
+ return(0);
+}
+
+
+int
+so_bio_readc(unsigned char *c, STORE_S *so)
+{
+ if(so->txt && so->src == BioType){
+ unsigned char ch[1];
+ BIO *b = (BIO *) so->txt;
+
+ if(BIO_read(b, ch, 1) >= 1){
+ *c = ch[0];
+ return(1);
+ }
+ }
+
+ return(0);
+}
+
+
+/*
+ * write a string into the specified storage object,
+ * expanding if necessary (and cheating if the object
+ * happens to be a file!)
+ *
+ * return 1 on success and 0 on failure
+ */
+int
+so_bio_puts(STORE_S *so, char *s)
+{
+
+ if(so->txt && so->src == BioType){
+ BIO *b = (BIO *) so->txt;
+ int slen = strlen(s);
+
+ if(BIO_puts(b, s) >= slen)
+ return(1);
+ }
+
+ return(1);
+}
+#endif /* SMIME */
+
+
+/*
+ *
+ */
+int
+so_nputs(STORE_S *so, char *s, long int n)
+{
+ while(n--)
+ if(!so_writec((unsigned char) *s++, so))
+ return(0); /* ERROR putting char ! */
+
+ return(1);
+}
+
+
+/*
+ * Position the storage object's pointer to the given offset
+ * from the start of the object's data.
+ */
+int
+so_seek(STORE_S *so, long int pos, int orig)
+{
+ if(so->src == CharStar){
+ switch(orig){
+ case 0 : /* SEEK_SET */
+ return((pos < so->eod - (unsigned char *) so->txt)
+ ? so->dp = (unsigned char *)so->txt + pos, 0 : -1);
+ case 1 : /* SEEK_CUR */
+ return((pos > 0)
+ ? ((pos < so->eod - so->dp) ? so->dp += pos, 0: -1)
+ : ((pos < 0)
+ ? ((-pos < so->dp - (unsigned char *)so->txt)
+ ? so->dp += pos, 0 : -1)
+ : 0));
+ case 2 : /* SEEK_END */
+ return((pos > 0)
+ ? -1
+ : ((-pos <= so->eod - (unsigned char *) so->txt)
+ ? so->dp = so->eod + pos, 0 : -1));
+ default :
+ return(-1);
+ }
+ }
+ else if(so->src == ExternalText){
+ if(etsod.seek)
+ return((*etsod.seek)(so->txt, pos, orig));
+
+ fatal("programmer botch: unsupported so_seek call");
+ /*NOTREACHED*/
+ return(0); /* suppress dumb compiler warnings */
+ }
+#ifdef SMIME
+ else if(so->src == BioType){
+ BIO *b = (BIO *) so->txt;
+
+ if(b && BIO_method_type(b) != BIO_TYPE_MEM)
+ (void) BIO_reset(b);
+
+ return(0);
+ }
+#endif /* SMIME */
+ else /* FileStar or TmpFileStar */
+ return((so->txt || so_reaquire(so))
+ ? fseek((FILE *)so->txt,pos,orig)
+ : -1);
+}
+
+
+/*
+ * Change the given storage object's size to that specified. If size
+ * is less than the current size, the internal pointer is adjusted and
+ * all previous data beyond the given size is lost.
+ *
+ * Returns 0 on failure.
+ */
+int
+so_truncate(STORE_S *so, long int size)
+{
+ if(so->src == CharStar){
+ if(so->eod < (unsigned char *) so->txt + size){ /* alloc! */
+ unsigned char *newtxt = (unsigned char *) so->txt;
+ register size_t len = so->eot - (unsigned char *) so->txt;
+
+ while(len <= size)
+ len += MSIZE_INC; /* need to resize! */
+
+ if(len > so->eot - (unsigned char *) newtxt){
+ fs_resize((void **) &newtxt, len * sizeof(char));
+ so->eot = newtxt + len;
+ so->eod = newtxt + (so->eod - (unsigned char *) so->txt);
+ memset(so->eod, 0, so->eot - so->eod);
+ }
+
+ so->eod = newtxt + size;
+ so->dp = newtxt + (so->dp - (unsigned char *) so->txt);
+ so->txt = newtxt;
+ }
+ else if(so->eod > (unsigned char *) so->txt + size){
+ if(so->dp > (so->eod = (unsigned char *)so->txt + size))
+ so->dp = so->eod;
+
+ memset(so->eod, 0, so->eot - so->eod);
+ }
+
+ return(1);
+ }
+ else if(so->src == ExternalText){
+ if(etsod.truncate)
+ return((*etsod.truncate)(so, (off_t) size));
+
+ fatal("programmer botch: unsupported so_truncate call");
+ /*NOTREACHED*/
+ return(0); /* suppress dumb compiler warnings */
+ }
+#ifdef SMIME
+ else if(so->src == BioType){
+ fatal("programmer botch: unsupported so_truncate call for BioType");
+ /*NOTREACHED*/
+ return(0); /* suppress dumb compiler warnings */
+
+#ifdef notdef
+ long len;
+ BIO *b = (BIO *) so->txt;
+
+ if(b){
+ BUF_MEM *biobuf = NULL;
+
+ BIO_get_mem_ptr(b, &biobuf);
+ if(biobuf){
+ BUF_MEM_grow(biobuf, size);
+ return(1);
+ }
+ }
+
+ return(0);
+#endif /* notdef */
+ }
+#endif /* SMIME */
+ else /* FileStar or TmpFileStar */
+ return(fflush((FILE *) so->txt) != EOF
+ && fseek((FILE *) so->txt, size, 0) == 0
+ && ftruncate(fileno((FILE *)so->txt), (off_t) size) == 0);
+}
+
+
+/*
+ * Report given storage object's position indicator.
+ * Returns 0 on failure.
+ */
+long
+so_tell(STORE_S *so)
+{
+ if(so->src == CharStar){
+ return((long) (so->dp - (unsigned char *) so->txt));
+ }
+ else if(so->src == ExternalText){
+ if(etsod.tell)
+ return((*etsod.tell)(so));
+
+ fatal("programmer botch: unsupported so_tell call");
+ /*NOTREACHED*/
+ return(0); /* suppress dumb compiler warnings */
+ }
+#ifdef SMIME
+ else if(so->src == BioType){
+ fatal("programmer botch: unsupported so_tell call for BioType");
+ /*NOTREACHED*/
+ return(0); /* suppress dumb compiler warnings */
+ }
+#endif /* SMIME */
+ else /* FileStar or TmpFileStar */
+ return(ftell((FILE *) so->txt));
+}
+
+
+/*
+ * so_attr - hook to hang random attributes onto the storage object
+ */
+char *
+so_attr(STORE_S *so, char *key, char *value)
+{
+ if(so && key){
+ if(value){
+ PARAMETER **pp = &so->attr;
+
+ while(1){
+ if(*pp){
+ if((*pp)->attribute && !strcmp(key, (*pp)->attribute)){
+ if((*pp)->value)
+ fs_give((void **)&(*pp)->value);
+
+ break;
+ }
+
+ pp = &(*pp)->next;
+ }
+ else{
+ *pp = mail_newbody_parameter();
+ (*pp)->attribute = cpystr(key);
+ break;
+ }
+ }
+
+ return((*pp)->value = cpystr(value));
+ }
+ else{
+ PARAMETER *p;
+
+ for(p = so->attr; p; p = p->next)
+ if(p->attribute && !strcmp(key, p->attribute))
+ return(p->value);
+ }
+ }
+
+ return(NULL);
+}
+
+
+/*
+ * so_release - a rather misnamed function. the idea is to release
+ * what system resources we can (e.g., open files).
+ * while maintaining a reference to it.
+ * it's up to the functions that deal with this object
+ * next to re-aquire those resources.
+ */
+int
+so_release(STORE_S *so)
+{
+ if(so->txt && so->name && (so->src == FileStar || so->src == TmpFileStar)){
+ if(fget_pos((FILE *)so->txt, (fpos_t *)&(so->used)) == 0){
+ fclose((FILE *)so->txt); /* free the handle! */
+ so->txt = NULL;
+ }
+ }
+
+ return(1);
+}
+
+
+/*
+ * so_reaquire - get any previously released system resources we
+ * may need for the given storage object.
+ * NOTE: at the moment, only FILE * types of objects are
+ * effected, so it only needs to be called before
+ * references to them.
+ *
+ */
+int
+so_reaquire(STORE_S *so)
+{
+ int rv = 1;
+
+ if(!so->txt && (so->src == FileStar || so->src == TmpFileStar)){
+ if(!(so->txt = so_file_open(so))){
+ q_status_message2(SM_ORDER,3,5, "ERROR reopening %.200s : %.200s",
+ so->name, error_description(errno));
+ rv = 0;
+ }
+ else if(fset_pos((FILE *)so->txt, (fpos_t *)&(so->used))){
+ q_status_message2(SM_ORDER, 3, 5,
+ "ERROR positioning in %.200s : %.200s",
+ so->name, error_description(errno));
+ rv = 0;
+ }
+ }
+
+ return(rv);
+}
+
+
+/*
+ * so_text - return a pointer to the text the store object passed
+ */
+void *
+so_text(STORE_S *so)
+{
+ return((so) ? so->txt : NULL);
+}
+
+
+/*
+ * Similar to fgets but reading from a storage object.
+ */
+char *
+so_fgets(STORE_S *so, char *s, size_t size)
+{
+ unsigned char c;
+ char *p = s;
+
+ while(--size > 0 && so_readc(&c, so) > 0){
+ *p++ = (char) c;
+ if(c == '\n')
+ break;
+ }
+
+ *p = '\0';
+
+ return((p>s) ? s : NULL);
+}
diff --git a/pith/store.h b/pith/store.h
new file mode 100644
index 00000000..5496c452
--- /dev/null
+++ b/pith/store.h
@@ -0,0 +1,80 @@
+/*
+ * $Id: store.h 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_STORE_INCLUDED
+#define PITH_STORE_INCLUDED
+
+
+#include "../pith/filttype.h"
+#ifdef SMIME
+#include <openssl/bio.h>
+#endif /* SMIME */
+
+
+typedef enum {CharStarStar, CharStar, FileStar,
+#ifdef SMIME
+ BioType,
+#endif /* SMIME */
+ TmpFileStar, PipeStar, ExternalText} SourceType;
+
+
+/*
+ * typedef used by storage object routines
+ */
+
+typedef struct store_object {
+ unsigned char *dp; /* current position in data */
+ unsigned char *eod; /* end of current data */
+ void *txt; /* container's data */
+ unsigned char *eot; /* end of space alloc'd for data */
+ int (*writec)(int, struct store_object *);
+ int (*readc)(unsigned char *, struct store_object *);
+ int (*puts)(struct store_object *, char *);
+ fpos_t used; /* amount of object in use */
+ char *name; /* optional object name */
+ SourceType src; /* what we're copying into */
+ short flags; /* flags relating to object use */
+ CBUF_S cb;
+ PARAMETER *attr; /* attribute list */
+} STORE_S;
+
+#define so_writec(c, so) ((*(so)->writec)((c), (so)))
+#define so_readc(c, so) ((*(so)->readc)((c), (so)))
+#define so_puts(so, s) ((*(so)->puts)((so), (s)))
+
+
+/* exported protoypes */
+STORE_S *so_get(SourceType, char *, int);
+int so_give(STORE_S **);
+int so_nputs(STORE_S *, char *, long);
+int so_seek(STORE_S *, long, int);
+int so_truncate(STORE_S *, long);
+long so_tell(STORE_S *);
+char *so_attr(STORE_S *, char *, char *);
+int so_release(STORE_S *);
+void *so_text(STORE_S *);
+char *so_fgets(STORE_S *, char *, size_t);
+void so_register_external_driver(STORE_S *(*)(void),
+ int (*)(STORE_S **),
+ int (*)(int, STORE_S *),
+ int (*)(unsigned char *, STORE_S *),
+ int (*)(STORE_S *, char *),
+ int (*)(STORE_S *, long, int),
+ int (*)(STORE_S *, off_t),
+ int (*)(STORE_S *));
+
+
+#endif /* PITH_STORE_INCLUDED */
diff --git a/pith/stream.c b/pith/stream.c
new file mode 100644
index 00000000..f13fd73f
--- /dev/null
+++ b/pith/stream.c
@@ -0,0 +1,3392 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: stream.c 1012 2008-03-26 00:44:22Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ stream.c
+ Implements the Pine mail stream management routines
+ and c-client wrapper functions
+ ====*/
+
+
+#include "../pith/headers.h"
+#include "../pith/stream.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/flag.h"
+#include "../pith/msgno.h"
+#include "../pith/adrbklib.h"
+#include "../pith/status.h"
+#include "../pith/newmail.h"
+#include "../pith/detach.h"
+#include "../pith/folder.h"
+#include "../pith/mailcmd.h"
+#include "../pith/util.h"
+#include "../pith/news.h"
+#include "../pith/sequence.h"
+#include "../pith/options.h"
+#include "../pith/mimedesc.h"
+
+
+void (*pith_opt_closing_stream)(MAILSTREAM *);
+
+
+/*
+ * Internal prototypes
+ */
+void reset_stream_view_state(MAILSTREAM *);
+void carefully_reset_sp_flags(MAILSTREAM *, unsigned long);
+char *partial_text_gets(readfn_t, void *, unsigned long, GETS_DATA *);
+void mail_list_internal(MAILSTREAM *, char *, char *);
+int recent_activity(MAILSTREAM *);
+int hibit_in_searchpgm(SEARCHPGM *);
+int hibit_in_strlist(STRINGLIST *);
+int hibit_in_header(SEARCHHEADER *);
+int hibit_in_sizedtext(SIZEDTEXT *);
+int sp_nusepool_notperm(void);
+int sp_add(MAILSTREAM *, int);
+void sp_delete(MAILSTREAM *);
+void sp_free(PER_STREAM_S **);
+
+
+static FETCH_READC_S *g_pft_desc;
+
+
+MAILSTATUS *pine_cached_status; /* implement status for #move folder */
+
+
+
+/*
+ * Pine wrapper around mail_open. If we have the PREFER_ALT_AUTH flag turned
+ * on, we need to set the TRYALT flag before trying the open.
+ * This routine manages the stream pool, too. It tries to re-use existing
+ * streams instead of opening new ones, or maybe it will leave one open and
+ * use a new one if that seems to make more sense. Pine_mail_close leaves
+ * streams open so that they may be re-used. Each pine_mail_open should have
+ * a matching pine_mail_close (or possible pine_mail_actually_close) somewhere
+ * that goes with it.
+ *
+ * Args:
+ * stream -- A possible stream for recycling. This isn't usually the
+ * way recycling happens. Usually it is automatic.
+ * mailbox -- The mailbox to be opened.
+ * openflags -- Flags passed here to modify the behavior.
+ * retflags -- Flags returned from here. SP_MATCH will be lit if that is
+ * what happened. If SP_MATCH is lit then SP_LOCKED may also
+ * be lit if the matched stream was already locked when
+ * we got here.
+ */
+MAILSTREAM *
+pine_mail_open(MAILSTREAM *stream, char *mailbox, long int openflags, long int *retflags)
+{
+ MAILSTREAM *retstream = NULL;
+ DRIVER *d;
+ int permlocked = 0, is_inbox = 0, usepool = 0, tempuse = 0, uf = 0;
+ unsigned long flags;
+ char **lock_these, *target = NULL;
+ static unsigned long streamcounter = 0;
+
+ dprint((7,
+ "pine_mail_open: opening \"%s\"%s openflags=0x%x %s%s%s%s%s%s%s%s%s (%s)\n",
+ mailbox ? mailbox : "(NULL)",
+ stream ? "" : " (stream was NULL)",
+ openflags,
+ openflags & OP_HALFOPEN ? " OP_HALFOPEN" : "",
+ openflags & OP_READONLY ? " OP_READONLY" : "",
+ openflags & OP_SILENT ? " OP_SILENT" : "",
+ openflags & OP_DEBUG ? " OP_DEBUG" : "",
+ openflags & SP_PERMLOCKED ? " SP_PERMLOCKED" : "",
+ openflags & SP_INBOX ? " SP_INBOX" : "",
+ openflags & SP_USERFLDR ? " SP_USERFLDR" : "",
+ openflags & SP_USEPOOL ? " SP_USEPOOL" : "",
+ openflags & SP_TEMPUSE ? " SP_TEMPUSE" : "",
+ debug_time(1, ps_global->debug_timestamp)));
+
+ if(retflags)
+ *retflags = 0L;
+
+ if(ps_global->user_says_cancel){
+ dprint((7, "pine_mail_open: cancelled by user\n"));
+ return(retstream);
+ }
+
+ is_inbox = openflags & SP_INBOX;
+ uf = openflags & SP_USERFLDR;
+
+ /* inbox is still special, assume that we want to permlock it */
+ permlocked = (is_inbox || openflags & SP_PERMLOCKED) ? 1 : 0;
+
+ /* check to see if user wants this folder permlocked */
+ for(lock_these = ps_global->VAR_PERMLOCKED;
+ uf && !permlocked && lock_these && *lock_these; lock_these++){
+ char *p = NULL, *dummy = NULL, *lt, *lname, *mname;
+ char tmp1[MAILTMPLEN], tmp2[MAILTMPLEN];
+
+ /* there isn't really a pair, it just dequotes for us */
+ get_pair(*lock_these, &dummy, &p, 0, 0);
+
+ /*
+ * Check to see if this is an incoming nickname and replace it
+ * with the full name.
+ */
+ if(!(p && ps_global->context_list
+ && ps_global->context_list->use & CNTXT_INCMNG
+ && (lt=folder_is_nick(p, FOLDERS(ps_global->context_list), 0))))
+ lt = p;
+
+ if(dummy)
+ fs_give((void **) &dummy);
+
+ if(lt && mailbox
+ && (same_remote_mailboxes(mailbox, lt)
+ ||
+ (!IS_REMOTE(mailbox)
+ && (lname=mailboxfile(tmp1, lt))
+ && (mname=mailboxfile(tmp2, mailbox))
+ && !strcmp(lname, mname))))
+ permlocked++;
+
+ if(p)
+ fs_give((void **) &p);
+ }
+
+ /*
+ * Only cache if remote, not nntp, not pop, and caller asked us to.
+ * It might make sense to do some caching for nntp and pop, as well, but
+ * we aren't doing it right now. For example, an nntp stream open to
+ * one group could be reused for another group. An open pop stream could
+ * be used for mail_copy_full.
+ *
+ * An implication of doing only imap here is that sp_stream_get will only
+ * be concerned with imap streams.
+ */
+ if((d = mail_valid (NIL, mailbox, (char *) NIL)) && !strcmp(d->name, "imap")){
+ usepool = openflags & SP_USEPOOL;
+ tempuse = openflags & SP_TEMPUSE;
+ }
+ else{
+ if(IS_REMOTE(mailbox)){
+ dprint((9, "pine_mail_open: not cacheable: %s\n", !d ? "no driver?" : d->name ? d->name : "?" ));
+ }
+ else{
+ if(permlocked || (openflags & OP_READONLY)){
+ /*
+ * This is a strange case. We want to allow stay-open local
+ * folders, but they don't fit into the rest of the framework
+ * well. So we'll look for it being already open in this case
+ * and special-case it (the already_open_stream() case
+ * below).
+ */
+ dprint((9,
+ "pine_mail_open: not cacheable: not remote, but check for local stream\n"));
+ }
+ else{
+ dprint((9,
+ "pine_mail_open: not cacheable: not remote\n"));
+ }
+ }
+ }
+
+ /* If driver doesn't support halfopen, just open it. */
+ if(d && (openflags & OP_HALFOPEN) && !(d->flags & DR_HALFOPEN)){
+ openflags &= ~OP_HALFOPEN;
+ dprint((9,
+ "pine_mail_open: turning off OP_HALFOPEN flag\n"));
+ }
+
+ /*
+ * Some of the flags are pine's, the rest are meant for mail_open.
+ * We've noted the pine flags, now remove them before we call mail_open.
+ */
+ openflags &= ~(SP_USEPOOL | SP_TEMPUSE | SP_INBOX
+ | SP_PERMLOCKED | SP_USERFLDR);
+
+#ifdef DEBUG
+ if(ps_global->debug_imap > 3 || ps_global->debugmem)
+ openflags |= OP_DEBUG;
+#endif
+
+ if(F_ON(F_PREFER_ALT_AUTH, ps_global)){
+ if((d = mail_valid (NIL, mailbox, (char *) NIL))
+ && !strcmp(d->name, "imap"))
+ openflags |= OP_TRYALT;
+ }
+
+ if(F_ON(F_ENABLE_MULNEWSRCS, ps_global)){
+ char source[MAILTMPLEN];
+ if(check_for_move_mbox(mailbox, source, sizeof(source), &target)){
+ DRIVER *d;
+ if((d = mail_valid(NIL, source, (char *) NIL))
+ && (!strcmp(d->name, "news")
+ || !strcmp(d->name, "nntp")))
+ openflags |= OP_MULNEWSRC;
+ }
+ else if((d = mail_valid(NIL, mailbox, (char *) NIL))
+ && !strcmp(d->name, "nntp"))
+ openflags |= OP_MULNEWSRC;
+ }
+
+ /*
+ * One of the problems is that the new-style stream caching (the
+ * sp_stream_get stuff) may conflict with some of the old-style caching
+ * (the passed in argument stream) that is still in the code. We should
+ * probably eliminate the old-style caching, but some of it is still useful,
+ * especially if it deals with something other than IMAP. We want to prevent
+ * mistakes caused by conflicts between the two styles. In particular, we
+ * don't want to have a new-style cached stream re-opened because of the
+ * old-style caching code. This can happen if a stream is passed in that
+ * is not useable, and then a new stream is opened because the passed in
+ * stream causes us to bypass the new caching code. Play it safe. If it
+ * is an IMAP stream, just close it. This should leave it in the new-style
+ * cache anyway, causing no loss. Maybe not if the cache wasn't large
+ * enough to have it in there in the first place, in which case we get
+ * a possibly unnecessary close and open. If it isn't IMAP we still have
+ * to worry about it because it will cause us to bypass the caching code.
+ * So if the stream isn't IMAP but the mailbox we're opening is, close it.
+ * The immediate alternative would be to try to emulate the code in
+ * mail_open that checks whether it is re-usable or not, but that is
+ * dangerous if that code changes on us.
+ */
+ if(stream){
+ if(is_imap_stream(stream)
+ || ((d = mail_valid (NIL, mailbox, (char *) NIL))
+ && !strcmp(d->name, "imap"))){
+ if(is_imap_stream(stream)){
+ dprint((7,
+ "pine_mail_open: closing passed in IMAP stream %s\n",
+ stream->mailbox ? stream->mailbox : "?"));
+ }
+ else{
+ dprint((7,
+ "pine_mail_open: closing passed in non-IMAP stream %s\n",
+ stream->mailbox ? stream->mailbox : "?"));
+ }
+
+ pine_mail_close(stream);
+ stream = NULL;
+ }
+ }
+
+ /*
+ * Maildrops are special. The mailbox name will be a #move name. If the
+ * target of the maildrop is an IMAP folder we want to be sure it isn't
+ * already open in another cached stream, to avoid double opens. This
+ * could have happened if the user opened it manually as the target
+ * instead of as a maildrop. It could also be a side-effect of marking
+ * an answered flag after a reply.
+ */
+ target = NULL;
+ if(check_for_move_mbox(mailbox, NULL, 0, &target)){
+ MAILSTREAM *targetstream = NULL;
+
+ if((d = mail_valid (NIL, target, (char *) NIL)) && !strcmp(d->name, "imap")){
+ targetstream = sp_stream_get(target, SP_MATCH | SP_RO_OK);
+ if(targetstream){
+ dprint((9, "pine_mail_open: close previously opened target of maildrop\n"));
+ pine_mail_actually_close(targetstream);
+ }
+ }
+ }
+
+ if((usepool && !stream && permlocked)
+ || (!usepool && (permlocked || (openflags & OP_READONLY))
+ && (retstream = already_open_stream(mailbox, AOS_NONE)))){
+ if(retstream)
+ stream = retstream;
+ else
+ stream = sp_stream_get(mailbox,
+ SP_MATCH | ((openflags & OP_READONLY) ? SP_RO_OK : 0));
+ if(stream){
+ flags = SP_LOCKED
+ | (usepool ? SP_USEPOOL : 0)
+ | (permlocked ? SP_PERMLOCKED : 0)
+ | (is_inbox ? SP_INBOX : 0)
+ | (uf ? SP_USERFLDR : 0)
+ | (tempuse ? SP_TEMPUSE : 0);
+
+ /*
+ * If the stream wasn't already locked, then we reset it so it
+ * looks like we are reopening it. We have to worry about recent
+ * messages since they will still be recent, if that affects us.
+ */
+ if(!(sp_flags(stream) & SP_LOCKED))
+ reset_stream_view_state(stream);
+
+ if(retflags){
+ *retflags |= SP_MATCH;
+ if(sp_flags(stream) & SP_LOCKED)
+ *retflags |= SP_LOCKED;
+ }
+
+ if(sp_flags(stream) & SP_LOCKED
+ && sp_flags(stream) & SP_USERFLDR
+ && !(flags & SP_USERFLDR)){
+ sp_set_ref_cnt(stream, sp_ref_cnt(stream)+1);
+ dprint((7,
+ "pine_mail_open: permlocked: ref cnt up to %d\n",
+ sp_ref_cnt(stream)));
+ }
+ else if(sp_ref_cnt(stream) <= 0){
+ sp_set_ref_cnt(stream, 1);
+ dprint((7,
+ "pine_mail_open: permexact: ref cnt set to %d\n",
+ sp_ref_cnt(stream)));
+ }
+
+ carefully_reset_sp_flags(stream, flags);
+
+ if(stream->silent && !(openflags & OP_SILENT))
+ stream->silent = NIL;
+
+ dprint((9, "pine_mail_open: stream was already open\n"));
+ if(stream && stream->dtb && stream->dtb->name
+ && !strcmp(stream->dtb->name, "imap")){
+ dprint((7, "pine_mail_open: next TAG %08lx\n",
+ stream->gensym));
+ }
+
+ return(stream);
+ }
+ }
+
+ if(usepool && !stream){
+ /*
+ * First, we look for an exact match, a stream which is already
+ * open to the mailbox we are trying to re-open, and we use that.
+ * Skip permlocked only because we did it above already.
+ */
+ if(!permlocked)
+ stream = sp_stream_get(mailbox,
+ SP_MATCH | ((openflags & OP_READONLY) ? SP_RO_OK : 0));
+
+ if(stream){
+ flags = SP_LOCKED
+ | (usepool ? SP_USEPOOL : 0)
+ | (permlocked ? SP_PERMLOCKED : 0)
+ | (is_inbox ? SP_INBOX : 0)
+ | (uf ? SP_USERFLDR : 0)
+ | (tempuse ? SP_TEMPUSE : 0);
+
+ /*
+ * If the stream wasn't already locked, then we reset it so it
+ * looks like we are reopening it. We have to worry about recent
+ * messages since they will still be recent, if that affects us.
+ */
+ if(!(sp_flags(stream) & SP_LOCKED))
+ reset_stream_view_state(stream);
+
+ if(retflags){
+ *retflags |= SP_MATCH;
+ if(sp_flags(stream) & SP_LOCKED)
+ *retflags |= SP_LOCKED;
+ }
+
+ if(sp_flags(stream) & SP_LOCKED
+ && sp_flags(stream) & SP_USERFLDR
+ && !(flags & SP_USERFLDR)){
+ sp_set_ref_cnt(stream, sp_ref_cnt(stream)+1);
+ dprint((7,
+ "pine_mail_open: matched: ref cnt up to %d\n",
+ sp_ref_cnt(stream)));
+ }
+ else if(sp_ref_cnt(stream) <= 0){
+ sp_set_ref_cnt(stream, 1);
+ dprint((7,
+ "pine_mail_open: exact: ref cnt set to %d\n",
+ sp_ref_cnt(stream)));
+ }
+
+ carefully_reset_sp_flags(stream, flags);
+
+ /*
+ * We may be re-using a stream that was previously open
+ * with OP_SILENT and now we don't want OP_SILENT.
+ * We don't turn !silent into silent because the !silentness
+ * could be important in the original context (for example,
+ * silent suppresses mm_expunged calls).
+ *
+ * WARNING: we're messing with c-client internals.
+ */
+ if(stream->silent && !(openflags & OP_SILENT))
+ stream->silent = NIL;
+
+ dprint((9, "pine_mail_open: stream already open\n"));
+ if(stream && stream->dtb && stream->dtb->name
+ && !strcmp(stream->dtb->name, "imap")){
+ dprint((7, "pine_mail_open: next TAG %08lx\n",
+ stream->gensym));
+ }
+
+ return(stream);
+ }
+
+ /*
+ * No exact match, look for a stream which is open to the same
+ * server and marked for TEMPUSE.
+ */
+ stream = sp_stream_get(mailbox, SP_SAME | SP_TEMPUSE);
+ if(stream){
+ dprint((9,
+ "pine_mail_open: attempting to re-use TEMP stream\n"));
+ }
+ /*
+ * No SAME/TEMPUSE stream so we look to see if there is an
+ * open slot available (we're not yet at max_remstream). If there
+ * is an open slot, we'll just open a new stream and put it there.
+ * If not, we'll go inside this conditional.
+ */
+ else if(!permlocked
+ && sp_nusepool_notperm() >= ps_global->s_pool.max_remstream){
+ dprint((9,
+ "pine_mail_open: no empty slots\n"));
+ /*
+ * No empty remote slots available. See if there is a
+ * TEMPUSE stream that is open but to the wrong server.
+ */
+ stream = sp_stream_get(mailbox, SP_TEMPUSE);
+ if(stream){
+ /*
+ * We will close this stream and use the empty slot
+ * that that creates.
+ */
+ dprint((9,
+ "pine_mail_open: close a TEMPUSE stream and re-use that slot\n"));
+ pine_mail_actually_close(stream);
+ stream = NULL;
+ }
+ else{
+
+ /*
+ * Still no luck. Look for a stream open to the same
+ * server that is just not locked. This would be a
+ * stream that might be reusable in the future, but we
+ * need it now instead.
+ */
+ stream = sp_stream_get(mailbox, SP_SAME | SP_UNLOCKED);
+ if(stream){
+ dprint((9,
+ "pine_mail_open: attempting to re-use stream\n"));
+ }
+ else{
+ /*
+ * We'll take any UNLOCKED stream and re-use it.
+ */
+ stream = sp_stream_get(mailbox, 0);
+ if(stream){
+ /*
+ * We will close this stream and use the empty slot
+ * that that creates.
+ */
+ dprint((9,
+ "pine_mail_open: close an UNLOCKED stream and re-use the slot\n"));
+ pine_mail_actually_close(stream);
+ stream = NULL;
+ }
+ else{
+ if(ps_global->s_pool.max_remstream){
+ dprint((9, "pine_mail_open: all USEPOOL slots full of LOCKED streams, nothing to use\n"));
+ }
+ else{
+ dprint((9, "pine_mail_open: no caching, max_remstream == 0\n"));
+ }
+
+ usepool = 0;
+ tempuse = 0;
+ if(permlocked){
+ permlocked = 0;
+ dprint((2,
+ "pine_mail_open5: Can't mark folder Stay-Open: at max-remote limit\n"));
+ q_status_message1(SM_ORDER, 3, 5,
+ "Can't mark folder Stay-Open: reached max-remote limit (%s)",
+ comatose((long) ps_global->s_pool.max_remstream));
+ }
+ }
+ }
+ }
+ }
+ else{
+ dprint((9,
+ "pine_mail_open: there is an empty slot to use\n"));
+ }
+
+ /*
+ * We'll make an assumption here. If we were asked to halfopen a
+ * stream then we'll assume that the caller doesn't really care if
+ * the stream is halfopen or if it happens to be open to some mailbox
+ * already. They are just saying halfopen because they don't need to
+ * SELECT a mailbox. That's the assumption, anyway.
+ */
+ if(openflags & OP_HALFOPEN && stream){
+ dprint((9,
+ "pine_mail_open: asked for HALFOPEN so returning stream as is\n"));
+ sp_set_ref_cnt(stream, sp_ref_cnt(stream)+1);
+ dprint((7,
+ "pine_mail_open: halfopen: ref cnt up to %d\n",
+ sp_ref_cnt(stream)));
+ if(stream && stream->dtb && stream->dtb->name
+ && !strcmp(stream->dtb->name, "imap")){
+ dprint((7, "pine_mail_open: next TAG %08lx\n",
+ stream->gensym));
+ }
+
+ if(stream->silent && !(openflags & OP_SILENT))
+ stream->silent = NIL;
+
+ return(stream);
+ }
+
+ /*
+ * We're going to SELECT another folder with this open stream.
+ */
+ if(stream){
+ /*
+ * We will have just pinged the stream to make sure it is
+ * still alive. That ping may have discovered some new mail.
+ * Before unselecting the folder, we should process the filters
+ * for that new mail.
+ */
+ if(!sp_flagged(stream, SP_LOCKED)
+ && !sp_flagged(stream, SP_USERFLDR)
+ && sp_new_mail_count(stream))
+ process_filter_patterns(stream, sp_msgmap(stream),
+ sp_new_mail_count(stream));
+
+ if(stream && stream->dtb && stream->dtb->name
+ && !strcmp(stream->dtb->name, "imap")){
+ dprint((7,
+ "pine_mail_open: cancel idle timer: TAG %08lx (%s)\n",
+ stream->gensym, debug_time(1, ps_global->debug_timestamp)));
+ }
+
+ /*
+ * We need to reset the counters and everything.
+ * The easiest way to do that is just to delete all of the
+ * sp_s data and let the open fill it in correctly for
+ * the new folder.
+ */
+ sp_free((PER_STREAM_S **) &stream->sparep);
+ }
+ }
+
+ /*
+ * When we pass a stream to mail_open, it will either re-use it or
+ * close it.
+ */
+ retstream = mail_open(stream, mailbox, openflags);
+
+ if(retstream){
+
+ dprint((7, "pine_mail_open: mail_open returns stream:\n original_mailbox=%s\n mailbox=%s\n driver=%s rdonly=%d halfopen=%d secure=%d nmsgs=%ld recent=%ld\n", retstream->original_mailbox ? retstream->original_mailbox : "?", retstream->mailbox ? retstream->mailbox : "?", (retstream->dtb && retstream->dtb->name) ? retstream->dtb->name : "?", retstream->rdonly, retstream->halfopen, retstream->secure, retstream->nmsgs, retstream->recent));
+
+ /*
+ * So it is easier to figure out which command goes with which
+ * stream when debugging, change the tag associated with each stream.
+ * Of course, this will come after the SELECT, so the startup IMAP
+ * commands will have confusing tags.
+ */
+ if(retstream != stream && retstream->dtb && retstream->dtb->name
+ && !strcmp(retstream->dtb->name, "imap")){
+ retstream->gensym += (streamcounter * 0x1000000);
+ streamcounter = (streamcounter + 1) % (8 * 16);
+ }
+
+ if(retstream && retstream->dtb && retstream->dtb->name
+ && !strcmp(retstream->dtb->name, "imap")){
+ dprint((7, "pine_mail_open: next TAG %08lx\n",
+ retstream->gensym));
+ }
+
+ /*
+ * Catch the case where our test up above (where usepool was set)
+ * did not notice that this was news, but that we can tell once
+ * we've opened the stream. (One such case would be an imap proxy
+ * to an nntp server.) Remove it from being cached here. There was
+ * a possible penalty for not noticing sooner. If all the usepool
+ * slots were full, we will have closed one of the UNLOCKED streams
+ * above, preventing us from future re-use of that stream.
+ * We could figure out how to do the test better with just the
+ * name. We could open the stream and then close the other one
+ * after the fact. Or we could just not worry about it since it is
+ * rare.
+ */
+ if(IS_NEWS(retstream)){
+ usepool = 0;
+ tempuse = 0;
+ }
+
+ /* make sure the message map has been instantiated */
+ (void) sp_msgmap(retstream);
+
+ /*
+ * If a referral during the mail_open above causes a stream
+ * re-use which involves a BYE on an IMAP stream, then that
+ * BYE will have caused an mm_notify which will have
+ * instantiated the sp_data and set dead_stream! Trust that
+ * it is alive up to here and reset it to zero.
+ */
+ sp_set_dead_stream(retstream, 0);
+
+ flags = SP_LOCKED
+ | (usepool ? SP_USEPOOL : 0)
+ | (permlocked ? SP_PERMLOCKED : 0)
+ | (is_inbox ? SP_INBOX : 0)
+ | (uf ? SP_USERFLDR : 0)
+ | (tempuse ? SP_TEMPUSE : 0);
+
+ sp_flag(retstream, flags);
+ sp_set_recent_since_visited(retstream, retstream->recent);
+
+ /* initialize the reference count */
+ sp_set_ref_cnt(retstream, 1);
+ dprint((7, "pine_mail_open: reset: ref cnt set to %d\n",
+ sp_ref_cnt(retstream)));
+
+ if(sp_add(retstream, usepool) != 0 && usepool){
+ usepool = 0;
+ tempuse = 0;
+ flags = SP_LOCKED
+ | (usepool ? SP_USEPOOL : 0)
+ | (permlocked ? SP_PERMLOCKED : 0)
+ | (is_inbox ? SP_INBOX : 0)
+ | (uf ? SP_USERFLDR : 0)
+ | (tempuse ? SP_TEMPUSE : 0);
+
+ sp_flag(retstream, flags);
+ (void) sp_add(retstream, usepool);
+ }
+
+
+ /*
+ * When opening a newsgroup, c-client marks the messages up to the
+ * last Deleted as Unseen. If the feature news-approximates-new-status
+ * is on, we'd rather they be treated as Seen. That way, selecting New
+ * messages will give us the ones past the last Deleted. So we're going
+ * to change them to Seen. Since Seen is a session flag for news, making
+ * this change won't have any permanent effect. C-client also marks the
+ * messages after the last deleted Recent, which is the bit of
+ * information we'll use to find the messages we want to change.
+ */
+ if(F_ON(F_FAKE_NEW_IN_NEWS, ps_global) &&
+ retstream->nmsgs > 0 && IS_NEWS(retstream)){
+ char *seq;
+ long i, mflags = ST_SET;
+ MESSAGECACHE *mc;
+
+ /*
+ * Search for !recent messages to set the searched bit for
+ * those messages we want to change. Then we'll flip the bits.
+ */
+ (void)count_flagged(retstream, F_UNRECENT);
+
+ for(i = 1L; i <= retstream->nmsgs; i++)
+ if((mc = mail_elt(retstream,i)) && mc->searched)
+ mc->sequence = 1;
+ else
+ mc->sequence = 0;
+
+ if(!is_imap_stream(retstream))
+ mflags |= ST_SILENT;
+ if((seq = build_sequence(retstream, NULL, NULL)) != NULL){
+ mail_flag(retstream, seq, "\\SEEN", mflags);
+ fs_give((void **)&seq);
+ }
+ }
+ }
+
+ return(retstream);
+}
+
+
+void
+reset_stream_view_state(MAILSTREAM *stream)
+{
+ MSGNO_S *mm;
+
+ if(!stream)
+ return;
+
+ mm = sp_msgmap(stream);
+
+ if(!mm)
+ return;
+
+ sp_set_viewing_a_thread(stream, 0);
+ sp_set_need_to_rethread(stream, 0);
+
+ mn_reset_cur(mm, stream->nmsgs > 0L ? stream->nmsgs : 0L); /* default */
+
+ mm->visible_threads = -1L;
+ mm->top = 0L;
+ mm->max_thrdno = 0L;
+ mm->top_after_thrd = 0L;
+
+ mn_set_mansort(mm, 0);
+
+ /*
+ * Get rid of zooming and selections, but leave filtering flags. All the
+ * flag counts and everything should still be correct because set_lflag
+ * preserves them correctly.
+ */
+ if(any_lflagged(mm, MN_SLCT | MN_HIDE)){
+ long i;
+
+ for(i = 1L; i <= mn_get_total(mm); i++)
+ set_lflag(stream, mm, i, MN_SLCT | MN_HIDE, 0);
+ }
+
+ /*
+ * We could try to set up a default sort order, but the caller is going
+ * to re-sort anyway if they are interested in sorting. So we won't do
+ * it here.
+ */
+}
+
+
+/*
+ * We have to be careful when we change the flags of an already
+ * open stream, because there may be more than one section of
+ * code actively using the stream.
+ * We allow turning on (but not off) SP_LOCKED
+ * SP_PERMLOCKED
+ * SP_USERFLDR
+ * SP_INBOX
+ * We allow turning off (but not on) SP_TEMPUSE
+ */
+void
+carefully_reset_sp_flags(MAILSTREAM *stream, long unsigned int flags)
+{
+ if(sp_flags(stream) != flags){
+ /* allow turning on but not off */
+ if(sp_flags(stream) & SP_LOCKED && !(flags & SP_LOCKED))
+ flags |= SP_LOCKED;
+
+ if(sp_flags(stream) & SP_PERMLOCKED && !(flags & SP_PERMLOCKED))
+ flags |= SP_PERMLOCKED;
+
+ if(sp_flags(stream) & SP_USERFLDR && !(flags & SP_USERFLDR))
+ flags |= SP_USERFLDR;
+
+ if(sp_flags(stream) & SP_INBOX && !(flags & SP_INBOX))
+ flags |= SP_INBOX;
+
+
+ /* allow turning off but not on */
+ if(!(sp_flags(stream) & SP_TEMPUSE) && flags & SP_TEMPUSE)
+ flags &= ~SP_TEMPUSE;
+
+
+ /* leave the way they already are */
+ if((sp_flags(stream) & SP_FILTERED) != (flags & SP_FILTERED))
+ flags = (flags & ~SP_FILTERED) | (sp_flags(stream) & SP_FILTERED);
+
+
+ if(sp_flags(stream) != flags)
+ sp_flag(stream, flags);
+ }
+}
+
+
+/*
+ * Pine wrapper around mail_create. If we have the PREFER_ALT_AUTH flag turned
+ * on we don't want to pass a NULL stream to c-client because it will open
+ * a non-ssl connection when we want it to be ssl.
+ */
+long
+pine_mail_create(MAILSTREAM *stream, char *mailbox)
+{
+ MAILSTREAM *ourstream = NULL;
+ long return_val;
+ long openflags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
+ char source[MAILTMPLEN], *target = NULL;
+ DRIVER *d;
+
+ dprint((7, "pine_mail_create: creating \"%s\"%s\n",
+ mailbox ? mailbox : "(NULL)",
+ stream ? "" : " (stream was NULL)"));
+
+ if(check_for_move_mbox(mailbox, source, sizeof(source), &target)){
+ mailbox = target;
+ dprint((7,
+ "pine_mail_create: #move special case, creating \"%s\"\n",
+ mailbox ? mailbox : "(NULL)"));
+ }
+
+ /*
+ * We don't really need this anymore, since we are now using IMAPTRYALT.
+ * We'll leave it since it works.
+ */
+ if((F_ON(F_PREFER_ALT_AUTH, ps_global)
+ || (ps_global->debug_imap > 3 || ps_global->debugmem))){
+
+ if((d = mail_valid (NIL, mailbox, (char *) NIL))
+ && !strcmp(d->name, "imap")){
+
+ if(F_ON(F_PREFER_ALT_AUTH, ps_global))
+ openflags |= OP_TRYALT;
+ }
+ }
+
+ if(!stream)
+ stream = sp_stream_get(mailbox, SP_MATCH);
+ if(!stream)
+ stream = sp_stream_get(mailbox, SP_SAME);
+
+ if(!stream){
+ /*
+ * It is only useful to open a stream in the imap case.
+ */
+ if((d = mail_valid (NIL, mailbox, (char *) NIL))
+ && !strcmp(d->name, "imap")){
+
+ stream = pine_mail_open(NULL, mailbox, openflags, NULL);
+ ourstream = stream;
+ }
+ }
+
+ return_val = mail_create(stream, mailbox);
+
+ if(ourstream)
+ pine_mail_close(ourstream);
+
+ return(return_val);
+}
+
+
+/*
+ * Pine wrapper around mail_delete.
+ */
+long
+pine_mail_delete(MAILSTREAM *stream, char *mailbox)
+{
+ MAILSTREAM *ourstream = NULL;
+ long return_val;
+ long openflags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
+ char source[MAILTMPLEN], *target = NULL;
+ DRIVER *d;
+
+ dprint((7, "pine_mail_delete: deleting \"%s\"%s\n",
+ mailbox ? mailbox : "(NULL)",
+ stream ? "" : " (stream was NULL)"));
+
+ if(check_for_move_mbox(mailbox, source, sizeof(source), &target)){
+ mailbox = target;
+ dprint((7,
+ "pine_mail_delete: #move special case, deleting \"%s\"\n",
+ mailbox ? mailbox : "(NULL)"));
+ }
+
+ /*
+ * We don't really need this anymore, since we are now using IMAPTRYALT.
+ */
+ if((F_ON(F_PREFER_ALT_AUTH, ps_global)
+ || (ps_global->debug_imap > 3 || ps_global->debugmem))){
+
+ if((d = mail_valid (NIL, mailbox, (char *) NIL))
+ && !strcmp(d->name, "imap")){
+
+ if(F_ON(F_PREFER_ALT_AUTH, ps_global))
+ openflags |= OP_TRYALT;
+ }
+ }
+
+ /* oops, we seem to be deleting a selected stream */
+ if(!stream && (stream = sp_stream_get(mailbox, SP_MATCH))){
+ pine_mail_actually_close(stream);
+ stream = NULL;
+ }
+
+ if(!stream)
+ stream = sp_stream_get(mailbox, SP_SAME);
+
+ if(!stream){
+ /*
+ * It is only useful to open a stream in the imap case.
+ */
+ if((d = mail_valid (NIL, mailbox, (char *) NIL))
+ && !strcmp(d->name, "imap")){
+
+ stream = pine_mail_open(NULL, mailbox, openflags, NULL);
+ ourstream = stream;
+ }
+ }
+
+ return_val = mail_delete(stream, mailbox);
+
+ if(ourstream)
+ pine_mail_close(ourstream);
+
+ return(return_val);
+}
+
+
+/*
+ * Pine wrapper around mail_append.
+ */
+long
+pine_mail_append_full(MAILSTREAM *stream, char *mailbox, char *flags, char *date, STRING *message)
+{
+ MAILSTREAM *ourstream = NULL;
+ long return_val;
+ long openflags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
+ char source[MAILTMPLEN], *target = NULL;
+ char mailbox_nodelim[MAILTMPLEN];
+ int delim;
+ DRIVER *d;
+
+ dprint((7, "pine_mail_append_full: appending to \"%s\"%s\n",
+ mailbox ? mailbox : "(NULL)",
+ stream ? "" : " (stream was NULL)"));
+
+ /* strip delimiter, it has no meaning in an APPEND and could cause trouble */
+ if(mailbox && (delim = get_folder_delimiter(mailbox)) != '\0'){
+ size_t len;
+
+ len = strlen(mailbox);
+ if(mailbox[len-1] == delim){
+ strncpy(mailbox_nodelim, mailbox, MIN(len-1,sizeof(mailbox_nodelim)-1));
+ mailbox_nodelim[MIN(len-1,sizeof(mailbox_nodelim)-1)] = '\0';
+ mailbox = mailbox_nodelim;
+ }
+ }
+
+ if(check_for_move_mbox(mailbox, source, sizeof(source), &target)){
+ mailbox = target;
+ dprint((7,
+ "pine_mail_append_full: #move special case, appending to \"%s\"\n",
+ mailbox ? mailbox : "(NULL)"));
+ }
+
+ /*
+ * We don't really need this anymore, since we are now using IMAPTRYALT.
+ */
+ if((F_ON(F_PREFER_ALT_AUTH, ps_global)
+ || (ps_global->debug_imap > 3 || ps_global->debugmem))){
+
+ if((d = mail_valid (NIL, mailbox, (char *) NIL))
+ && !strcmp(d->name, "imap")){
+
+ if(F_ON(F_PREFER_ALT_AUTH, ps_global))
+ openflags |= OP_TRYALT;
+ }
+ }
+
+ if(!stream)
+ stream = sp_stream_get(mailbox, SP_MATCH);
+ if(!stream)
+ stream = sp_stream_get(mailbox, SP_SAME);
+
+ if(!stream){
+ /*
+ * It is only useful to open a stream in the imap case.
+ */
+ if((d = mail_valid (NIL, mailbox, (char *) NIL))
+ && !strcmp(d->name, "imap")){
+
+ stream = pine_mail_open(NULL, mailbox, openflags, NULL);
+ ourstream = stream;
+ }
+ }
+
+ return_val = mail_append_full(stream, mailbox, flags, date, message);
+
+ if(ourstream)
+ pine_mail_close(ourstream);
+
+ return(return_val);
+}
+
+
+/*
+ * Pine wrapper around mail_append.
+ */
+long
+pine_mail_append_multiple(MAILSTREAM *stream, char *mailbox, append_t af,
+ APPENDPACKAGE *data, MAILSTREAM *not_this_stream)
+{
+ MAILSTREAM *ourstream = NULL;
+ long return_val;
+ long openflags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
+ char source[MAILTMPLEN], *target = NULL;
+ DRIVER *d;
+ int we_blocked_reuse = 0;
+
+ dprint((7, "pine_mail_append_multiple: appending to \"%s\"%s\n",
+ mailbox ? mailbox : "(NULL)",
+ stream ? "" : " (stream was NULL)"));
+
+ if(check_for_move_mbox(mailbox, source, sizeof(source), &target)){
+ mailbox = target;
+ dprint((7,
+ "pine_mail_append_multiple: #move special case, appending to \"%s\"\n",
+ mailbox ? mailbox : "(NULL)"));
+ }
+
+ if((F_ON(F_PREFER_ALT_AUTH, ps_global)
+ || (ps_global->debug_imap > 3 || ps_global->debugmem))){
+
+ if((d = mail_valid (NIL, mailbox, (char *) NIL))
+ && !strcmp(d->name, "imap")){
+
+ if(F_ON(F_PREFER_ALT_AUTH, ps_global))
+ openflags |= OP_TRYALT;
+ }
+ }
+
+ /*
+ * We have to be careful re-using streams for multiappend, because of
+ * the way it works. We call into c-client below but part of the call
+ * is data containing a callback function to us to supply the data to
+ * be appended. That function may need to get the data from the server.
+ * If that uses the same stream as we're trying to append on, we're
+ * in trouble. We can't call back into c-client from c-client on the same
+ * stream. (Just think about it, we're in the middle of an APPEND command.
+ * We can't issue a FETCH before the APPEND completes in order to complete
+ * the APPEND.) We can re-use a stream if it is a different stream from
+ * the one we are reading from, so that's what the not_this_stream
+ * stuff is for. If we mark it !SP_USEPOOL, it won't get reused.
+ */
+ if(sp_flagged(not_this_stream, SP_USEPOOL)){
+ we_blocked_reuse++;
+ sp_unflag(not_this_stream, SP_USEPOOL);
+ }
+
+ if(!stream)
+ stream = sp_stream_get(mailbox, SP_MATCH);
+ if(!stream)
+ stream = sp_stream_get(mailbox, SP_SAME);
+
+ if(!stream){
+ /*
+ * It is only useful to open a stream in the imap case.
+ */
+ if((d = mail_valid (NIL, mailbox, (char *) NIL))
+ && !strcmp(d->name, "imap")){
+
+ stream = pine_mail_open(NULL, mailbox, openflags, NULL);
+ ourstream = stream;
+ }
+ }
+
+ if(we_blocked_reuse)
+ sp_set_flags(not_this_stream, sp_flags(not_this_stream) | SP_USEPOOL);
+
+ return_val = mail_append_multiple(stream, mailbox, af, (void *) data);
+
+ if(ourstream)
+ pine_mail_close(ourstream);
+
+ return(return_val);
+}
+
+
+/*
+ * Pine wrapper around mail_copy.
+ */
+long
+pine_mail_copy_full(MAILSTREAM *stream, char *sequence, char *mailbox, long int options)
+{
+ MAILSTREAM *ourstream = NULL;
+ long return_val;
+ long openflags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
+ char source[MAILTMPLEN], *target = NULL;
+ char mailbox_nodelim[MAILTMPLEN];
+ int delim;
+ DRIVER *d;
+
+ dprint((7, "pine_mail_copy_full: copying to \"%s\"%s\n",
+ mailbox ? mailbox : "(NULL)",
+ stream ? "" : " (stream was NULL)"));
+
+ /* strip delimiter, it has no meaning in a COPY and could cause trouble */
+ if(mailbox && (delim = get_folder_delimiter(mailbox)) != '\0'){
+ size_t len;
+
+ len = strlen(mailbox);
+ if(mailbox[len-1] == delim){
+ strncpy(mailbox_nodelim, mailbox, MIN(len-1,sizeof(mailbox_nodelim)-1));
+ mailbox_nodelim[MIN(len-1,sizeof(mailbox_nodelim)-1)] = '\0';
+ mailbox = mailbox_nodelim;
+ }
+ }
+
+ if(check_for_move_mbox(mailbox, source, sizeof(source), &target)){
+ mailbox = target;
+ dprint((7,
+ "pine_mail_copy_full: #move special case, copying to \"%s\"\n",
+ mailbox ? mailbox : "(NULL)"));
+ }
+
+ /*
+ * We don't really need this anymore, since we are now using IMAPTRYALT.
+ */
+ if((F_ON(F_PREFER_ALT_AUTH, ps_global)
+ || (ps_global->debug_imap > 3 || ps_global->debugmem))){
+
+ if((d = mail_valid (NIL, mailbox, (char *) NIL))
+ && !strcmp(d->name, "imap")){
+
+ if(F_ON(F_PREFER_ALT_AUTH, ps_global))
+ openflags |= OP_TRYALT;
+ }
+ }
+
+ if(!stream)
+ stream = sp_stream_get(mailbox, SP_MATCH);
+ if(!stream)
+ stream = sp_stream_get(mailbox, SP_SAME);
+
+ if(!stream){
+ /*
+ * It is only useful to open a stream in the imap case.
+ * Actually, mail_copy_full is the case where it might also be
+ * useful to provide a stream in the nntp and pop3 cases. If we
+ * cache such streams, then we will probably want to open one
+ * here so that it gets cached.
+ */
+ if((d = mail_valid (NIL, mailbox, (char *) NIL))
+ && !strcmp(d->name, "imap")){
+
+ stream = pine_mail_open(NULL, mailbox, openflags, NULL);
+ ourstream = stream;
+ }
+ }
+
+ return_val = mail_copy_full(stream, sequence, mailbox, options);
+
+ if(ourstream)
+ pine_mail_close(ourstream);
+
+ return(return_val);
+}
+
+
+/*
+ * Pine wrapper around mail_rename.
+ */
+long
+pine_mail_rename(MAILSTREAM *stream, char *old, char *new)
+{
+ MAILSTREAM *ourstream = NULL;
+ long return_val;
+ long openflags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
+ DRIVER *d;
+
+ dprint((7, "pine_mail_rename(%s,%s)\n", old ? old : "",
+ new ? new : ""));
+
+ /*
+ * We don't really need this anymore, since we are now using IMAPTRYALT.
+ */
+ if((F_ON(F_PREFER_ALT_AUTH, ps_global)
+ || (ps_global->debug_imap > 3 || ps_global->debugmem))){
+
+ if((d = mail_valid (NIL, old, (char *) NIL))
+ && !strcmp(d->name, "imap")){
+
+ if(F_ON(F_PREFER_ALT_AUTH, ps_global))
+ openflags |= OP_TRYALT;
+ }
+ }
+
+ /* oops, we seem to be renaming a selected stream */
+ if(!stream && (stream = sp_stream_get(old, SP_MATCH))){
+ pine_mail_actually_close(stream);
+ stream = NULL;
+ }
+
+ if(!stream)
+ stream = sp_stream_get(old, SP_SAME);
+
+ if(!stream){
+ /*
+ * It is only useful to open a stream in the imap case.
+ */
+ if((d = mail_valid (NIL, old, (char *) NIL))
+ && !strcmp(d->name, "imap")){
+
+ stream = pine_mail_open(NULL, old, openflags, NULL);
+ ourstream = stream;
+ }
+ }
+
+ return_val = mail_rename(stream, old, new);
+
+ if(ourstream)
+ pine_mail_close(ourstream);
+
+ return(return_val);
+}
+
+
+/*----------------------------------------------------------------------
+ Our mail_close wrapper to clean up anything on the mailstream we may have
+ added to it. mostly in the unused bits of the elt's.
+ ----*/
+void
+pine_mail_close(MAILSTREAM *stream)
+{
+ unsigned long uid_last, last_uid;
+ int refcnt;
+
+ if(!stream)
+ return;
+
+ dprint((7, "pine_mail_close: %s (%s)\n",
+ stream && stream->mailbox ? stream->mailbox : "(NULL)",
+ debug_time(1, ps_global->debug_timestamp)));
+
+ if(sp_flagged(stream, SP_USEPOOL) && !sp_dead_stream(stream)){
+
+ refcnt = sp_ref_cnt(stream);
+ dprint((7, "pine_mail_close: ref cnt is %d\n", refcnt));
+
+ /*
+ * Instead of checkpointing here, which takes time that the user
+ * definitely notices, we checkpoint in new_mail at the next
+ * opportune time, hopefully when the user is idle.
+ */
+#if 0
+ if(sp_flagged(stream, SP_LOCKED) && sp_flagged(stream, SP_USERFLDR)
+ && !stream->halfopen && refcnt <= 1){
+ if(changes_to_checkpoint(stream))
+ pine_mail_check(stream);
+ else{
+ dprint((7,
+ "pine_mail_close: dont think we need to checkpoint\n"));
+ }
+ }
+#endif
+
+ /*
+ * Uid_last is valid when we first open a stream, but not always
+ * valid after that. So if we know the last uid should be higher
+ * than uid_last (!#%) use that instead.
+ */
+ uid_last = stream->uid_last;
+ if(stream->nmsgs > 0L
+ && (last_uid=mail_uid(stream,stream->nmsgs)) > uid_last)
+ uid_last = last_uid;
+
+ sp_set_saved_uid_validity(stream, stream->uid_validity);
+ sp_set_saved_uid_last(stream, uid_last);
+
+ /*
+ * If the reference count is down to 0, unlock it.
+ * In any case, don't actually do any real closing.
+ */
+ if(refcnt > 0)
+ sp_set_ref_cnt(stream, refcnt-1);
+
+ refcnt = sp_ref_cnt(stream);
+ dprint((7, "pine_mail_close: ref cnt is %d\n", refcnt));
+ if(refcnt <= 0){
+ dprint((7,
+ "pine_mail_close: unlocking: start idle timer: TAG %08lx (%s)\n",
+ stream->gensym, debug_time(1, ps_global->debug_timestamp)));
+ sp_set_last_use_time(stream, time(0));
+
+ /*
+ * Logically, we ought to be unflagging SP_INBOX, too. However,
+ * the filtering code uses SP_INBOX when deciding if it should
+ * filter some things, and we keep filtering after the mailbox
+ * is closed. So leave SP_INBOX alone. This (the closing of INBOX)
+ * usually only happens in goodnight_gracey when we're
+ * shutting everything down.
+ */
+ sp_unflag(stream, SP_LOCKED | SP_PERMLOCKED | SP_USERFLDR);
+ }
+ else{
+ dprint((7, "pine_mail_close: ref cnt is now %d\n",
+ refcnt));
+ }
+ }
+ else{
+ dprint((7, "pine_mail_close: %s\n",
+ sp_flagged(stream, SP_USEPOOL) ? "dead stream" : "no pool"));
+
+ refcnt = sp_ref_cnt(stream);
+ dprint((7, "pine_mail_close: ref cnt is %d\n", refcnt));
+
+ /*
+ * If the reference count is down to 0, unlock it.
+ * In any case, don't actually do any real closing.
+ */
+ if(refcnt > 0)
+ sp_set_ref_cnt(stream, refcnt-1);
+
+ refcnt = sp_ref_cnt(stream);
+ if(refcnt <= 0){
+ pine_mail_actually_close(stream);
+ }
+ else{
+ dprint((7, "pine_mail_close: ref cnt is now %d\n",
+ refcnt));
+ }
+
+ }
+}
+
+
+void
+pine_mail_actually_close(MAILSTREAM *stream)
+{
+ if(!stream)
+ return;
+
+ if(!sp_closing(stream)){
+ dprint((7, "pine_mail_actually_close: %s (%s)\n",
+ stream && stream->mailbox ? stream->mailbox : "(NULL)",
+ debug_time(1, ps_global->debug_timestamp)));
+
+ sp_set_closing(stream, 1);
+
+ if(!sp_flagged(stream, SP_LOCKED)
+ && !sp_flagged(stream, SP_USERFLDR)
+ && !sp_dead_stream(stream)
+ && sp_new_mail_count(stream))
+ process_filter_patterns(stream, sp_msgmap(stream),
+ sp_new_mail_count(stream));
+ sp_delete(stream);
+
+ /*
+ * let sp_free_callback() free the sp_s stuff and the callbacks to
+ * free_pine_elt free the per-elt pine stuff.
+ */
+ mail_close(stream);
+ }
+}
+
+
+/*
+ * If we haven't used a stream for a while, we may want to logout.
+ */
+void
+maybe_kill_old_stream(MAILSTREAM *stream)
+{
+#define KILL_IF_IDLE_TIME (25 * 60)
+ if(stream
+ && !sp_flagged(stream, SP_LOCKED)
+ && !sp_flagged(stream, SP_USERFLDR)
+ && time(0) - sp_last_use_time(stream) > KILL_IF_IDLE_TIME){
+
+ dprint((7,
+ "killing idle stream: %s (%s): idle timer = %ld secs\n",
+ stream && stream->mailbox ? stream->mailbox : "(NULL)",
+ debug_time(1, ps_global->debug_timestamp),
+ (long) (time(0)-sp_last_use_time(stream))));
+
+ /*
+ * Another thing we could do here instead is to unselect the
+ * mailbox, leaving the stream open to the server.
+ */
+ pine_mail_actually_close(stream);
+ }
+}
+
+
+/*
+ * Catch searches that don't need to go to the server.
+ * (Not anymore, now c-client does this for us.)
+ */
+long
+pine_mail_search_full(MAILSTREAM *stream, char *charset,
+ SEARCHPGM *pgm, long int flags)
+{
+ /*
+ * The charset should either be UTF-8 or NULL for alpine.
+ * If it is UTF-8 we may be able to change it to NULL if
+ * everything in the search is actually ascii. We try to
+ * do this because not all servers understand the CHARSET
+ * parameter.
+ */
+ if(charset && !strucmp(charset, "utf-8")
+ && is_imap_stream(stream) && !hibit_in_searchpgm(pgm))
+ charset = NULL;
+
+ if(F_ON(F_NNTP_SEARCH_USES_OVERVIEW, ps_global))
+ flags |= SO_OVERVIEW;
+
+ return(stream ? mail_search_full(stream, charset, pgm, flags) : NIL);
+}
+
+
+int
+hibit_in_searchpgm(SEARCHPGM *pgm)
+{
+ SEARCHOR *or;
+ SEARCHPGMLIST *not;
+
+ if(!pgm)
+ return 0;
+
+ if((pgm->subject && hibit_in_strlist(pgm->subject))
+ || (pgm->text && hibit_in_strlist(pgm->text))
+ || (pgm->body && hibit_in_strlist(pgm->body))
+ || (pgm->cc && hibit_in_strlist(pgm->cc))
+ || (pgm->from && hibit_in_strlist(pgm->from))
+ || (pgm->to && hibit_in_strlist(pgm->to))
+ || (pgm->bcc && hibit_in_strlist(pgm->bcc))
+ || (pgm->keyword && hibit_in_strlist(pgm->keyword))
+ || (pgm->unkeyword && hibit_in_strlist(pgm->return_path))
+ || (pgm->sender && hibit_in_strlist(pgm->sender))
+ || (pgm->reply_to && hibit_in_strlist(pgm->reply_to))
+ || (pgm->in_reply_to && hibit_in_strlist(pgm->in_reply_to))
+ || (pgm->message_id && hibit_in_strlist(pgm->message_id))
+ || (pgm->newsgroups && hibit_in_strlist(pgm->newsgroups))
+ || (pgm->followup_to && hibit_in_strlist(pgm->followup_to))
+ || (pgm->references && hibit_in_strlist(pgm->references))
+ || (pgm->header && hibit_in_header(pgm->header)))
+ return 1;
+
+ for(or = pgm->or; or; or = or->next)
+ if(hibit_in_searchpgm(or->first) || hibit_in_searchpgm(or->second))
+ return 1;
+
+ for(not = pgm->not; not; not = not->next)
+ if(hibit_in_searchpgm(not->pgm))
+ return 1;
+
+ return 0;
+}
+
+
+int
+hibit_in_strlist(STRINGLIST *sl)
+{
+ for(; sl; sl = sl->next)
+ if(hibit_in_sizedtext(&sl->text))
+ return 1;
+
+ return 0;
+}
+
+
+int
+hibit_in_header(SEARCHHEADER *header)
+{
+ SEARCHHEADER *hdr;
+
+ for(hdr = header; hdr; hdr = hdr->next)
+ if(hibit_in_sizedtext(&hdr->line) || hibit_in_sizedtext(&hdr->text))
+ return 1;
+
+ return 0;
+}
+
+
+int
+hibit_in_sizedtext(SIZEDTEXT *st)
+{
+ unsigned char *p, *end;
+
+ p = st ? st->data : NULL;
+ if(p)
+ for(end = p + st->size; p < end; p++)
+ if(*p & 0x80)
+ return 1;
+
+ return 0;
+}
+
+
+void
+pine_mail_fetch_flags(MAILSTREAM *stream, char *sequence, long int flags)
+{
+ ps_global->dont_count_flagchanges = 1;
+ mail_fetch_flags(stream, sequence, flags);
+ ps_global->dont_count_flagchanges = 0;
+}
+
+
+ENVELOPE *
+pine_mail_fetchenvelope(MAILSTREAM *stream, long unsigned int msgno)
+{
+ ENVELOPE *env = NULL;
+
+ ps_global->dont_count_flagchanges = 1;
+ if(stream && msgno > 0L && msgno <= stream->nmsgs)
+ env = mail_fetchenvelope(stream, msgno);
+
+ ps_global->dont_count_flagchanges = 0;
+ return(env);
+}
+
+
+ENVELOPE *
+pine_mail_fetch_structure(MAILSTREAM *stream, long unsigned int msgno,
+ struct mail_bodystruct **body, long int flags)
+{
+ ENVELOPE *env = NULL;
+
+ ps_global->dont_count_flagchanges = 1;
+ if(stream && (flags & FT_UID || (msgno > 0L && msgno <= stream->nmsgs)))
+ env = mail_fetch_structure(stream, msgno, body, flags);
+
+ ps_global->dont_count_flagchanges = 0;
+ return(env);
+}
+
+
+ENVELOPE *
+pine_mail_fetchstructure(MAILSTREAM *stream, long unsigned int msgno, struct mail_bodystruct **body)
+{
+ ENVELOPE *env = NULL;
+
+ ps_global->dont_count_flagchanges = 1;
+ if(stream && msgno > 0L && msgno <= stream->nmsgs)
+ env = mail_fetchstructure(stream, msgno, body);
+
+ ps_global->dont_count_flagchanges = 0;
+ return(env);
+}
+
+
+/*
+ * Wrapper around mail_fetch_body.
+ * Currently only used to turn on partial fetching if trying
+ * to work around the microsoft ssl bug.
+ */
+char *
+pine_mail_fetch_body(MAILSTREAM *stream, long unsigned int msgno, char *section,
+ long unsigned int *len, long int flags)
+{
+#ifdef _WINDOWS
+ if(F_ON(F_QUELL_SSL_LARGEBLOCKS, ps_global))
+ return(pine_mail_partial_fetch_wrapper(stream, msgno,
+ section, len, flags, 0, NULL, 0));
+ else
+#endif
+ return(mail_fetch_body(stream, msgno, section, len, flags));
+}
+
+/*
+ * Wrapper around mail_fetch_text.
+ * Currently the only purpose this wrapper serves is to turn
+ * on partial fetching for quell-ssl-largeblocks.
+ */
+char *
+pine_mail_fetch_text(MAILSTREAM *stream, long unsigned int msgno, char *section,
+ long unsigned int *len, long int flags)
+{
+#ifdef _WINDOWS
+ if(F_ON(F_QUELL_SSL_LARGEBLOCKS, ps_global))
+ return(pine_mail_partial_fetch_wrapper(stream, msgno,
+ section, len, flags,
+ 0, NULL, 1));
+ else
+#endif
+ return(mail_fetch_text(stream, msgno, section, len, flags));
+}
+
+
+/*
+ * Determine whether to do partial-fetching or not, and do it
+ * args - same as c-client functions being wrapped around
+ * get_n_bytes - try to partial fetch for the first n bytes.
+ * makes no guarantees, might wind up fetching
+ * the entire text anyway.
+ * str_to_free - pointer to string to free if we only get
+ * (non-cachable) partial text (required for
+ * get_n_bytes).
+ * is_text_fetch -
+ * set, tells us to do mail_fetch_text and mail_partial_text
+ * unset, tells us to do mail_fetch_body and mail_partial_body
+ */
+char *
+pine_mail_partial_fetch_wrapper(MAILSTREAM *stream, long unsigned int msgno,
+ char *section, long unsigned int *len,
+ long int flags, long unsigned int get_n_bytes,
+ char **str_to_free, int is_text_fetch)
+{
+ BODY *body;
+ unsigned long size, firstbyte, lastbyte;
+ void *old_gets;
+ FETCH_READC_S *pftc;
+ char imap_cache_section[MAILTMPLEN];
+ SIZEDTEXT new_text;
+ MESSAGECACHE *mc;
+ char *(*fetch_full)(MAILSTREAM *, unsigned long, char *,
+ unsigned long *, long);
+ long (*fetch_partial)(MAILSTREAM *, unsigned long, char *,
+ unsigned long, unsigned long, long);
+
+ fetch_full = is_text_fetch ? mail_fetch_text : mail_fetch_body;
+ fetch_partial = is_text_fetch ? mail_partial_text : mail_partial_body;
+ if(str_to_free)
+ *str_to_free = NULL;
+#ifdef _WINDOWS
+ if(F_ON(F_QUELL_SSL_LARGEBLOCKS, ps_global) || get_n_bytes){
+#else
+ if(get_n_bytes){
+#endif /* _WINDOWS */
+ if(section && *section)
+ body = mail_body(stream, msgno, (unsigned char *) section);
+ else
+ pine_mail_fetch_structure(stream, msgno, &body, flags);
+ if(!body)
+ return NULL;
+ if(body->type != TYPEMULTIPART)
+ size = body->size.bytes;
+ else if((!section || !*section) && msgno > 0L
+ && stream && msgno <= stream->nmsgs
+ && (mc = mail_elt(stream, msgno)))
+ size = mc->rfc822_size; /* upper bound */
+ else /* just a guess, can't get actual size */
+ size = fcc_size_guess(body) + 2048;
+
+ /*
+ * imap_cache, originally intended for c-client internal use,
+ * takes a section argument that is different from one we
+ * would pass to mail_body. Typically in this function
+ * section is NULL, which translates to "TEXT", but in other
+ * cases we would want to append ".TEXT" to the section
+ */
+ if(is_text_fetch)
+ snprintf(imap_cache_section, sizeof(imap_cache_section), "%.*s%sTEXT", MAILTMPLEN - 10,
+ section && *section ? section : "",
+ section && *section ? "." : "");
+ else
+ snprintf(imap_cache_section, sizeof(imap_cache_section), "%.*s", MAILTMPLEN - 10,
+ section && *section ? section : "");
+
+ if(modern_imap_stream(stream)
+#ifdef _WINDOWS
+ && ((size > AVOID_MICROSOFT_SSL_CHUNKING_BUG)
+ || (get_n_bytes && size > get_n_bytes))
+#else
+ && (get_n_bytes && size > get_n_bytes)
+#endif /* _WINDOWS */
+ && !imap_cache(stream, msgno, imap_cache_section,
+ NULL, NULL)){
+ if(get_n_bytes == 0){
+ dprint((8,
+ "fetch_wrapper: doing partial fetching to work around microsoft bug\n"));
+ }
+ else{
+ dprint((8,
+ "fetch_wrapper: partial fetching due to %lu get_n_bytes\n", get_n_bytes));
+ }
+ pftc = (FETCH_READC_S *)fs_get(sizeof(FETCH_READC_S));
+ memset(g_pft_desc = pftc, 0, sizeof(FETCH_READC_S));
+#ifdef _WINDOWS
+ if(F_ON(F_QUELL_SSL_LARGEBLOCKS, ps_global)){
+ if(get_n_bytes)
+ pftc->chunksize = MIN(get_n_bytes,
+ AVOID_MICROSOFT_SSL_CHUNKING_BUG);
+ else
+ pftc->chunksize = AVOID_MICROSOFT_SSL_CHUNKING_BUG;
+ }
+ else
+#endif /* _WINDOWS */
+ pftc->chunksize = get_n_bytes;
+
+ pftc->chunk = (char *) fs_get((pftc->chunksize+1)
+ * sizeof(char));
+ pftc->cache = so_get(CharStar, NULL, EDIT_ACCESS);
+ pftc->read = 0L;
+ so_truncate(pftc->cache, size + 1);
+ old_gets = mail_parameters(stream, GET_GETS, (void *)NULL);
+ mail_parameters(stream, SET_GETS, (void *) partial_text_gets);
+ /* start fetching */
+ do{
+ firstbyte = pftc->read ;
+ lastbyte = firstbyte + pftc->chunksize;
+ if(get_n_bytes > firstbyte && get_n_bytes < lastbyte){
+ pftc->chunksize = get_n_bytes - firstbyte;
+ lastbyte = get_n_bytes;
+ }
+ (*fetch_partial)(stream, msgno, section, firstbyte,
+ pftc->chunksize, flags);
+
+ if(pftc->read != lastbyte)
+ break;
+ } while((pftc->read == lastbyte)
+ && (!get_n_bytes || (pftc->read < get_n_bytes)));
+ dprint((8,
+ "fetch_wrapper: anticipated size=%lu read=%lu\n",
+ size, pftc->read));
+ mail_parameters(stream, SET_GETS, old_gets);
+ new_text.size = pftc->read;
+ new_text.data = (unsigned char *)so_text(pftc->cache);
+
+ if(get_n_bytes && pftc->read == get_n_bytes){
+ /*
+ * don't write to cache if we're only dealing with
+ * partial text.
+ */
+ if(!str_to_free)
+ panic("Programmer botch: partial fetch attempt w/o string pointer");
+ else
+ *str_to_free = (char *) new_text.data;
+ }
+ else {
+ /* ugh, rewrite string in case it got stomped on last call */
+ if(is_text_fetch)
+ snprintf(imap_cache_section, sizeof(imap_cache_section), "%.*s%sTEXT", MAILTMPLEN - 10,
+ section && *section ? section : "",
+ section && *section ? "." : "");
+ else
+ snprintf(imap_cache_section, sizeof(imap_cache_section), "%.*s", MAILTMPLEN - 10,
+ section && *section ? section : "");
+
+ imap_cache(stream, msgno, imap_cache_section, NULL, &new_text);
+ }
+
+ pftc->cache->txt = (void *)NULL;
+ so_give(&pftc->cache);
+ fs_give((void **)&pftc->chunk);
+ fs_give((void **)&pftc);
+ g_pft_desc = NULL;
+ if(len)
+ *len = new_text.size;
+ return ((char *)new_text.data);
+ }
+ else
+ return((*fetch_full)(stream, msgno, section, len, flags));
+ }
+ else
+ return((*fetch_full)(stream, msgno, section, len, flags));
+}
+
+/*
+ * c-client callback that handles getting the text
+ */
+char *
+partial_text_gets(readfn_t f, void *stream, long unsigned int size, GETS_DATA *md)
+{
+ unsigned long n;
+
+ n = MIN(g_pft_desc->chunksize, size);
+ g_pft_desc->read +=n;
+
+ (*f) (stream, n, g_pft_desc->chunk);
+
+ if(g_pft_desc->cache)
+ so_nputs(g_pft_desc->cache, g_pft_desc->chunk, (long) n);
+
+
+ return(NULL);
+}
+
+
+/*
+ * Pings the stream. Returns 0 if the stream is dead, non-zero otherwise.
+ */
+long
+pine_mail_ping(MAILSTREAM *stream)
+{
+ time_t now;
+ long ret = 0L;
+
+ if(!sp_dead_stream(stream)){
+ ret = mail_ping(stream);
+ if(ret && sp_dead_stream(stream))
+ ret = 0L;
+ }
+
+ if(ret){
+ now = time(0);
+ sp_set_last_ping(stream, now);
+ sp_set_last_expunged_reaper(stream, now);
+ }
+
+ return(ret);
+}
+
+
+void
+pine_mail_check(MAILSTREAM *stream)
+{
+ reset_check_point(stream);
+ mail_check(stream);
+}
+
+
+/*
+ * Unlike mail_list, this version returns a value. The returned value is
+ * TRUE if the stream is opened ok, and FALSE if we failed opening the
+ * stream. This allows us to differentiate between a LIST which returns
+ * no matches and a failure opening the stream. We do this by pre-opening
+ * the stream ourselves.
+ */
+int
+pine_mail_list(MAILSTREAM *stream, char *ref, char *pat, unsigned int *options)
+{
+ int we_open = 0;
+ char *halfopen_target;
+ char source[MAILTMPLEN], *target = NULL;
+ MAILSTREAM *ourstream = NULL;
+
+ dprint((7, "pine_mail_list: ref=%s pat=%s%s\n",
+ ref ? ref : "(NULL)",
+ pat ? pat : "(NULL)",
+ stream ? "" : " (stream was NULL)"));
+
+ if((!ref && check_for_move_mbox(pat, source, sizeof(source), &target))
+ ||
+ check_for_move_mbox(ref, source, sizeof(source), &target)){
+ ref = NIL;
+ pat = target;
+ if(options)
+ *options |= PML_IS_MOVE_MBOX;
+
+ dprint((7,
+ "pine_mail_list: #move special case, listing \"%s\"%s\n",
+ pat ? pat : "(NULL)",
+ stream ? "" : " (stream was NULL)"));
+ }
+
+ if(!stream && ((pat && *pat == '{') || (ref && *ref == '{'))){
+ we_open++;
+ if(pat && *pat == '{'){
+ ref = NIL;
+ halfopen_target = pat;
+ }
+ else
+ halfopen_target = ref;
+ }
+
+ if(we_open){
+ long flags = (OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE);
+
+ stream = sp_stream_get(halfopen_target, SP_MATCH);
+ if(!stream)
+ stream = sp_stream_get(halfopen_target, SP_SAME);
+
+ if(!stream){
+ DRIVER *d;
+
+ /*
+ * POP is a special case. We don't need to have a stream
+ * to call mail_list for a POP name. The else part is the
+ * POP part.
+ */
+ if((d = mail_valid(NIL, halfopen_target, (char *) NIL))
+ && (d->flags & DR_HALFOPEN)){
+ stream = pine_mail_open(NIL, halfopen_target, flags, NULL);
+ ourstream = stream;
+ if(!stream)
+ return(FALSE);
+ }
+ else
+ stream = NULL;
+ }
+
+ mail_list_internal(stream, ref, pat);
+ }
+ else
+ mail_list_internal(stream, ref, pat);
+
+ if(ourstream)
+ pine_mail_close(ourstream);
+
+ return(TRUE);
+}
+
+
+/*
+ * mail_list_internal -- A monument to software religion and those who
+ * would force it upon us.
+ */
+void
+mail_list_internal(MAILSTREAM *s, char *r, char *p)
+{
+ if(F_ON(F_FIX_BROKEN_LIST, ps_global)
+ && ((s && s->mailbox && *s->mailbox == '{')
+ || (!s && ((r && *r == '{') || (p && *p == '{'))))){
+ char tmp[2*MAILTMPLEN];
+
+ snprintf(tmp, sizeof(tmp), "%.*s%.*s", sizeof(tmp)/2-1, r ? r : "",
+ sizeof(tmp)/2-1, p);
+ mail_list(s, "", tmp);
+ }
+ else
+ mail_list(s, r, p);
+}
+
+
+long
+pine_mail_status(MAILSTREAM *stream, char *mailbox, long int flags)
+{
+ return(pine_mail_status_full(stream, mailbox, flags, NULL, NULL));
+}
+
+
+long
+pine_mail_status_full(MAILSTREAM *stream, char *mailbox, long int flags,
+ imapuid_t *uidvalidity, imapuid_t *uidnext)
+{
+ char source[MAILTMPLEN], *target = NULL;
+ long ret = NIL;
+ MAILSTATUS cache, status;
+ MAILSTREAM *ourstream = NULL;
+
+ if(check_for_move_mbox(mailbox, source, sizeof(source), &target)){
+ memset(&status, 0, sizeof(status));
+ memset(&cache, 0, sizeof(cache));
+
+ /* tell mm_status() to write partial return here */
+ pine_cached_status = &status;
+
+ flags |= (SA_UIDVALIDITY | SA_UIDNEXT | SA_MESSAGES);
+
+ /* do status of destination */
+
+ stream = sp_stream_get(target, SP_SAME);
+
+ /* should never be news, don't worry about mulnewrsc flag*/
+ if((ret = pine_mail_status_full(stream, target, flags, uidvalidity,
+ uidnext))
+ && !status.recent){
+
+ /* do status of source */
+ pine_cached_status = &cache;
+ stream = sp_stream_get(source, SP_SAME);
+
+ if(!stream){
+ DRIVER *d;
+
+ if((d = mail_valid (NIL, source, (char *) NIL))
+ && !strcmp(d->name, "imap")){
+ long openflags =OP_HALFOPEN|OP_SILENT|SP_USEPOOL|SP_TEMPUSE;
+
+ if(F_ON(F_PREFER_ALT_AUTH, ps_global))
+ openflags |= OP_TRYALT;
+
+ stream = pine_mail_open(NULL, source, openflags, NULL);
+ ourstream = stream;
+ }
+ else if(F_ON(F_ENABLE_MULNEWSRCS, ps_global)
+ && d && (!strucmp(d->name, "news")
+ || !strucmp(d->name, "nntp")))
+ flags |= SA_MULNEWSRC;
+
+ }
+
+ if(!ps_global->user_says_cancel && mail_status(stream, source, flags)){
+ DRIVER *d;
+ int is_news = 0;
+
+ /*
+ * If the target has recent messages, then we don't come
+ * through here. We just use the answer from the target.
+ *
+ * If not, then we leave the target results in "status" and
+ * run a mail_status on the source that puts its results
+ * in "cache". Combine the results from cache with the
+ * results that were already in status.
+ *
+ * We count all messages in the source mailbox as recent and
+ * unseen, even if they are not recent or unseen in the source,
+ * because they will be recent and unseen in the target
+ * when we open it. (Not quite true. It is possible that some
+ * messages from a POP server will end up seen instead
+ * of unseen.
+ * It is also possible that it is correct. If we add unseen, or
+ * if we add messages, we could get it wrong. As far as I
+ * can tell, Pine doesn't ever even use status.unseen, so it
+ * is currently academic anyway.) Hubert 2003-03-05
+ * (Does now 2004-06-02 in next_folder.)
+ *
+ * However, we don't want to count all messages as recent if
+ * the source mailbox is NNTP or NEWS, because we won't be
+ * deleting those messages from the source.
+ * We only count recent.
+ *
+ * There are other cases that are trouble. One in particular
+ * is an IMAP-to-NNTP proxy, where the messages can't be removed
+ * from the mailbox but they can be deleted. If we count
+ * messages in the source as being recent and it turns out they
+ * were all deleted already, then we incorrectly say the folder
+ * has recent messages when it doesn't. We can recover from that
+ * case at some cost by actually opening the folder the first
+ * time if there are not recents, and then checking to see if
+ * everything is deleted. Subsequently, we store the uid values
+ * (which are returned by status) so that we can know if the
+ * mailbox changed or not. The problem being solved is that
+ * the TAB command indicates new messages in a folder when there
+ * really aren't any. An alternative would be to use the is_news
+ * half of the if-else in all cases. A problem with that is
+ * that there could be non-recent messages sitting in the
+ * source mailbox that we never discover. Hubert 2003-03-28
+ */
+
+ if((d = mail_valid (NIL, source, (char *) NIL))
+ && (!strcmp(d->name, "nntp") || !strcmp(d->name, "news")))
+ is_news++;
+
+ if(is_news && cache.flags & SA_RECENT){
+ status.messages += cache.recent;
+ status.recent += cache.recent;
+ status.unseen += cache.recent;
+ status.uidnext += cache.recent;
+ }
+ else{
+ if(uidvalidity && *uidvalidity
+ && uidnext && *uidnext
+ && cache.flags & SA_UIDVALIDITY
+ && cache.uidvalidity == *uidvalidity
+ && cache.flags & SA_UIDNEXT
+ && cache.uidnext == *uidnext){
+ ; /* nothing changed in source mailbox */
+ }
+ else if(cache.flags & SA_RECENT && cache.recent){
+ status.messages += cache.recent;
+ status.recent += cache.recent;
+ status.unseen += cache.recent;
+ status.uidnext += cache.recent;
+ }
+ else if(!(cache.flags & SA_MESSAGES) || cache.messages){
+ long openflags = OP_SILENT | SP_USEPOOL | SP_TEMPUSE;
+ long delete_count, not_deleted = 0L;
+
+ /* Actually open it up and check */
+ if(F_ON(F_PREFER_ALT_AUTH, ps_global))
+ openflags |= OP_TRYALT;
+
+ if(!ourstream)
+ stream = NULL;
+
+ if(ourstream
+ && !same_stream_and_mailbox(source, ourstream)){
+ pine_mail_close(ourstream);
+ ourstream = stream = NULL;
+ }
+
+ if(!stream){
+ stream = pine_mail_open(stream, source, openflags,
+ NULL);
+ ourstream = stream;
+ }
+
+ if(stream){
+ delete_count = count_flagged(stream, F_DEL);
+ not_deleted = stream->nmsgs - delete_count;
+ }
+
+ status.messages += not_deleted;
+ status.recent += not_deleted;
+ status.unseen += not_deleted;
+ status.uidnext += not_deleted;
+ }
+
+ if(uidvalidity && cache.flags & SA_UIDVALIDITY)
+ *uidvalidity = cache.uidvalidity;
+
+ if(uidnext && cache.flags & SA_UIDNEXT)
+ *uidnext = cache.uidnext;
+ }
+ }
+ }
+
+ /*
+ * Do the regular mm_status callback which we've been intercepting
+ * into different locations above.
+ */
+ pine_cached_status = NIL;
+ if(ret)
+ mm_status(NULL, mailbox, &status);
+ }
+ else{
+ if(!stream){
+ DRIVER *d;
+
+ if((d = mail_valid (NIL, mailbox, (char *) NIL))
+ && !strcmp(d->name, "imap")){
+ long openflags = OP_HALFOPEN|OP_SILENT|SP_USEPOOL|SP_TEMPUSE;
+
+ if(F_ON(F_PREFER_ALT_AUTH, ps_global))
+ openflags |= OP_TRYALT;
+
+ /*
+ * We just use this to find the answer.
+ * We're asking for trouble if we do a STATUS on a
+ * selected mailbox. I don't believe this happens in pine.
+ * It does now (2004-06-02) in next_folder if the
+ * F_TAB_USES_UNSEEN option is set and the folder was
+ * already opened.
+ */
+ stream = sp_stream_get(mailbox, SP_MATCH);
+ if(stream){
+ memset(&status, 0, sizeof(status));
+ if(flags & SA_MESSAGES){
+ status.flags |= SA_MESSAGES;
+ status.messages = stream->nmsgs;
+ }
+
+ if(flags & SA_RECENT){
+ status.flags |= SA_RECENT;
+ status.recent = stream->recent;
+ }
+
+ if(flags & SA_UNSEEN){
+ long i;
+ SEARCHPGM *srchpgm;
+ MESSAGECACHE *mc;
+
+ srchpgm = mail_newsearchpgm();
+ srchpgm->unseen = 1;
+
+ pine_mail_search_full(stream, NULL, srchpgm,
+ SE_NOPREFETCH | SE_FREE);
+ status.flags |= SA_UNSEEN;
+ status.unseen = 0L;
+ for(i = 1L; i <= stream->nmsgs; i++)
+ if((mc = mail_elt(stream, i)) && mc->searched)
+ status.unseen++;
+ }
+
+ if(flags & SA_UIDVALIDITY){
+ status.flags |= SA_UIDVALIDITY;
+ status.uidvalidity = stream->uid_validity;
+ }
+
+ if(flags & SA_UIDNEXT){
+ status.flags |= SA_UIDNEXT;
+ status.uidnext = stream->uid_last + 1L;
+ }
+
+ mm_status(NULL, mailbox, &status);
+ return T; /* that's what c-client returns when success */
+ }
+
+ if(!stream)
+ stream = sp_stream_get(mailbox, SP_SAME);
+
+ if(!stream){
+ stream = pine_mail_open(NULL, mailbox, openflags, NULL);
+ ourstream = stream;
+ }
+ }
+ else if(F_ON(F_ENABLE_MULNEWSRCS, ps_global)
+ && d && (!strucmp(d->name, "news")
+ || !strucmp(d->name, "nntp")))
+ flags |= SA_MULNEWSRC;
+ }
+
+ if(!ps_global->user_says_cancel)
+ ret = mail_status(stream, mailbox, flags); /* non #move case */
+ }
+
+ if(ourstream)
+ pine_mail_close(ourstream);
+
+ return ret;
+}
+
+
+/*
+ * Check for a mailbox name that is a legitimate #move mailbox.
+ *
+ * Args mbox -- The mailbox name to check
+ * sourcebuf -- Copy the source mailbox name into this buffer
+ * sbuflen -- Length of sourcebuf
+ * targetptr -- Set the pointer this points to to point to the
+ * target mailbox name in the original string
+ *
+ * Returns 1 - is a #move mailbox
+ * 0 - not
+ */
+int
+check_for_move_mbox(char *mbox, char *sourcebuf, size_t sbuflen, char **targetptr)
+{
+ char delim, *s;
+ int i;
+
+ if(mbox && (mbox[0] == '#')
+ && ((mbox[1] == 'M') || (mbox[1] == 'm'))
+ && ((mbox[2] == 'O') || (mbox[2] == 'o'))
+ && ((mbox[3] == 'V') || (mbox[3] == 'v'))
+ && ((mbox[4] == 'E') || (mbox[4] == 'e'))
+ && (delim = mbox[5])
+ && (s = strchr(mbox+6, delim))
+ && (i = s++ - (mbox + 6))
+ && (!sourcebuf || i < sbuflen)){
+
+ if(sourcebuf){
+ strncpy(sourcebuf, mbox+6, i); /* copy source mailbox name */
+ sourcebuf[i] = '\0';
+ }
+
+ if(targetptr)
+ *targetptr = s;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Checks through stream cache for a stream pointer already open to
+ * this mailbox, read/write. Very similar to sp_stream_get, but we want
+ * to look at all streams, not just imap streams.
+ * Right now it is very specialized. If we want to use it more generally,
+ * generalize it or combine it with sp_stream_get somehow.
+ */
+MAILSTREAM *
+already_open_stream(char *mailbox, int flags)
+{
+ int i;
+ MAILSTREAM *m;
+
+ if(!mailbox)
+ return(NULL);
+
+ if(*mailbox == '{'){
+ for(i = 0; i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ if(m && !(flags & AOS_RW_ONLY && m->rdonly)
+ && (*m->mailbox == '{') && !sp_dead_stream(m)
+ && same_stream_and_mailbox(mailbox, m))
+ return(m);
+ }
+ }
+ else{
+ char *cn, tmp[MAILTMPLEN];
+
+ cn = mailboxfile(tmp, mailbox);
+ for(i = 0; i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ if(m && !(flags & AOS_RW_ONLY && m->rdonly)
+ && (*m->mailbox != '{') && !sp_dead_stream(m)
+ && ((cn && *cn && !strcmp(cn, m->mailbox))
+ || !strcmp(mailbox, m->original_mailbox)
+ || !strcmp(mailbox, m->mailbox)))
+ return(m);
+ }
+ }
+
+ return(NULL);
+}
+
+
+void
+pine_imap_cmd_happened(MAILSTREAM *stream, char *cmd, long int flags)
+{
+ dprint((9, "imap_cmd(%s, %s, 0x%lx)\n",
+ STREAMNAME(stream), cmd ? cmd : "?", flags));
+
+ if(cmd && !strucmp(cmd, "CHECK"))
+ reset_check_point(stream);
+
+ if(is_imap_stream(stream)){
+ time_t now;
+
+ now = time(0);
+ sp_set_last_ping(stream, now);
+ sp_set_last_activity(stream, now);
+ if(!(flags & SC_EXPUNGEDEFERRED))
+ sp_set_last_expunged_reaper(stream, now);
+ }
+}
+
+
+/*
+ * Tells us whether we ought to check for a dead stream or not.
+ * We assume that we ought to check if it is not IMAP and if it is IMAP we
+ * don't need to check if the last activity was within the last 5 minutes.
+ */
+int
+recent_activity(MAILSTREAM *stream)
+{
+ if(is_imap_stream(stream) && !sp_dead_stream(stream)
+ && (time(0) - sp_last_activity(stream) < 5L * 60L))
+ return 1;
+ else
+ return 0;
+}
+
+void
+sp_cleanup_dead_streams(void)
+{
+ int i;
+ MAILSTREAM *m;
+
+ (void) streams_died(); /* tell user in case they don't know yet */
+
+ for(i = 0; i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ if(m && sp_dead_stream(m))
+ pine_mail_close(m);
+ }
+}
+
+
+/*
+ * Returns 0 if stream flags not set, non-zero if they are.
+ */
+int
+sp_flagged(MAILSTREAM *stream, long unsigned int flags)
+{
+ return(sp_flags(stream) & flags);
+}
+
+
+void
+sp_set_fldr(MAILSTREAM *stream, char *folder)
+{
+ PER_STREAM_S **pss;
+
+ pss = sp_data(stream);
+ if(pss && *pss){
+ if((*pss)->fldr)
+ fs_give((void **) &(*pss)->fldr);
+
+ if(folder)
+ (*pss)->fldr = cpystr(folder);
+ }
+}
+
+
+void
+sp_set_saved_cur_msg_id(MAILSTREAM *stream, char *id)
+{
+ PER_STREAM_S **pss;
+
+ pss = sp_data(stream);
+ if(pss && *pss){
+ if((*pss)->saved_cur_msg_id)
+ fs_give((void **) &(*pss)->saved_cur_msg_id);
+
+ if(id)
+ (*pss)->saved_cur_msg_id = cpystr(id);
+ }
+}
+
+
+/*
+ * Sets flags absolutely, erasing old flags.
+ */
+void
+sp_flag(MAILSTREAM *stream, long unsigned int flags)
+{
+ if(!stream)
+ return;
+
+ dprint((9, "sp_flag(%s, 0x%x): %s%s%s%s%s%s%s%s\n",
+ (stream && stream->mailbox) ? stream->mailbox : "?",
+ flags,
+ flags ? "set" : "clear",
+ (flags & SP_LOCKED) ? " SP_LOCKED" : "",
+ (flags & SP_PERMLOCKED) ? " SP_PERMLOCKED" : "",
+ (flags & SP_INBOX) ? " SP_INBOX" : "",
+ (flags & SP_USERFLDR) ? " SP_USERFLDR" : "",
+ (flags & SP_USEPOOL) ? " SP_USEPOOL" : "",
+ (flags & SP_TEMPUSE) ? " SP_TEMPUSE" : "",
+ !flags ? " ALL" : ""));
+
+ sp_set_flags(stream, flags);
+}
+
+
+/*
+ * Clear individual stream flags.
+ */
+void
+sp_unflag(MAILSTREAM *stream, long unsigned int flags)
+{
+ if(!stream || !flags)
+ return;
+
+ dprint((9, "sp_unflag(%s, 0x%x): unset%s%s%s%s%s%s\n",
+ (stream && stream->mailbox) ? stream->mailbox : "?",
+ flags,
+ (flags & SP_LOCKED) ? " SP_LOCKED" : "",
+ (flags & SP_PERMLOCKED) ? " SP_PERMLOCKED" : "",
+ (flags & SP_INBOX) ? " SP_INBOX" : "",
+ (flags & SP_USERFLDR) ? " SP_USERFLDR" : "",
+ (flags & SP_USEPOOL) ? " SP_USEPOOL" : "",
+ (flags & SP_TEMPUSE) ? " SP_TEMPUSE" : ""));
+
+ sp_set_flags(stream, sp_flags(stream) & ~flags);
+
+ flags = sp_flags(stream);
+ dprint((9, "sp_unflag(%s, 0x%x): result:%s%s%s%s%s%s\n",
+ (stream && stream->mailbox) ? stream->mailbox : "?",
+ flags,
+ (flags & SP_LOCKED) ? " SP_LOCKED" : "",
+ (flags & SP_PERMLOCKED) ? " SP_PERMLOCKED" : "",
+ (flags & SP_INBOX) ? " SP_INBOX" : "",
+ (flags & SP_USERFLDR) ? " SP_USERFLDR" : "",
+ (flags & SP_USEPOOL) ? " SP_USEPOOL" : "",
+ (flags & SP_TEMPUSE) ? " SP_TEMPUSE" : ""));
+}
+
+
+/*
+ * Set dead stream indicator and close if not locked.
+ */
+void
+sp_mark_stream_dead(MAILSTREAM *stream)
+{
+ if(!stream)
+ return;
+
+ dprint((9, "sp_mark_stream_dead(%s)\n",
+ (stream && stream->mailbox) ? stream->mailbox : "?"));
+
+ /*
+ * If the stream isn't locked, it is no longer useful. Get rid of it.
+ */
+ if(!sp_flagged(stream, SP_LOCKED))
+ pine_mail_actually_close(stream);
+ else{
+ /*
+ * If it is locked, then we have to worry about references to it
+ * that still exist. For example, it might be a permlocked stream
+ * or it might be the current stream. We need to let it be discovered
+ * by those referencers instead of just eliminating it, so that they
+ * can clean up the mess they need to clean up.
+ */
+ sp_set_dead_stream(stream, 1);
+ }
+}
+
+
+/*
+ * Returns the number of streams in the stream pool which are
+ * SP_USEPOOL but not SP_PERMLOCKED.
+ */
+int
+sp_nusepool_notperm(void)
+{
+ int i, cnt = 0;
+ MAILSTREAM *m;
+
+ for(i = 0; i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ if(sp_flagged(m, SP_USEPOOL) && !sp_flagged(m, SP_PERMLOCKED))
+ cnt++;
+ }
+
+ return(cnt);
+}
+
+
+/*
+ * Returns the number of folders that the user has marked to be PERMLOCKED
+ * folders (plus INBOX) that are remote IMAP folders.
+ *
+ * This routine depends on the fact that VAR_INBOX_PATH, VAR_PERMLOCKED,
+ * and the ps_global->context_list are correctly set.
+ */
+int
+sp_nremote_permlocked(void)
+{
+ int cnt = 0;
+ char **lock_these, *p = NULL, *dummy = NULL, *lt;
+ DRIVER *d;
+
+ /* first check if INBOX is remote */
+ lt = ps_global->VAR_INBOX_PATH;
+ if(lt && (d=mail_valid(NIL, lt, (char *) NIL))
+ && !strcmp(d->name, "imap"))
+ cnt++;
+
+ /* then count the user-specified permlocked folders */
+ for(lock_these = ps_global->VAR_PERMLOCKED; lock_these && *lock_these;
+ lock_these++){
+
+ /*
+ * Skip inbox, already done above. Should do this better so that we
+ * catch the case where the user puts the technical spec of the inbox
+ * in the list, or where the user lists one folder twice.
+ */
+ if(*lock_these && !strucmp(*lock_these, ps_global->inbox_name))
+ continue;
+
+ /* there isn't really a pair, it just dequotes for us */
+ get_pair(*lock_these, &dummy, &p, 0, 0);
+
+ /*
+ * Check to see if this is an incoming nickname and replace it
+ * with the full name.
+ */
+ if(!(p && ps_global->context_list
+ && ps_global->context_list->use & CNTXT_INCMNG
+ && (lt=folder_is_nick(p, FOLDERS(ps_global->context_list),
+ FN_WHOLE_NAME))))
+ lt = p;
+
+ if(dummy)
+ fs_give((void **) &dummy);
+
+ if(lt && (d=mail_valid(NIL, lt, (char *) NIL))
+ && !strcmp(d->name, "imap"))
+ cnt++;
+
+ if(p)
+ fs_give((void **) &p);
+ }
+
+ return(cnt);
+}
+
+
+/*
+ * Look for an already open stream that can be used for a new purpose.
+ * (Note that we only look through streams flagged SP_USEPOOL.)
+ *
+ * Args: mailbox
+ * flags
+ *
+ * Flags is a set of values or'd together which tells us what the request
+ * is looking for.
+ *
+ * Returns: a live stream from the stream pool or NULL.
+ */
+MAILSTREAM *
+sp_stream_get(char *mailbox, long unsigned int flags)
+{
+ int i;
+ MAILSTREAM *m;
+
+ dprint((7, "sp_stream_get(%s):%s%s%s%s%s\n",
+ mailbox ? mailbox : "?",
+ (flags & SP_MATCH) ? " SP_MATCH" : "",
+ (flags & SP_RO_OK) ? " SP_RO_OK" : "",
+ (flags & SP_SAME) ? " SP_SAME" : "",
+ (flags & SP_UNLOCKED) ? " SP_UNLOCKED" : "",
+ (flags & SP_TEMPUSE) ? " SP_TEMPUSE" : ""));
+
+ /* look for stream already open to this mailbox */
+ if(flags & SP_MATCH){
+ for(i = 0; i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ if(m && sp_flagged(m, SP_USEPOOL)
+ && (!m->rdonly || (flags & SP_RO_OK)) && !sp_dead_stream(m)
+ && same_stream_and_mailbox(mailbox, m)){
+ if((sp_flagged(m, SP_LOCKED) && recent_activity(m))
+ || pine_mail_ping(m)){
+ dprint((7,
+ "sp_stream_get: found exact match, slot %d\n", i));
+ if(!sp_flagged(m, SP_LOCKED)){
+ dprint((7,
+ "reset idle timer1: next TAG %08lx (%s)\n",
+ m->gensym,
+ debug_time(1, ps_global->debug_timestamp)));
+ sp_set_last_use_time(m, time(0));
+ }
+
+ return(m);
+ }
+
+ sp_mark_stream_dead(m);
+ }
+ }
+ }
+
+ /*
+ * SP_SAME will not match if an SP_MATCH match would have worked.
+ * If the caller is interested in SP_MATCH streams as well as SP_SAME
+ * streams then the caller should make two separate calls to this
+ * routine.
+ */
+ if(flags & SP_SAME){
+ /*
+ * If the flags arg does not have either SP_TEMPUSE or SP_UNLOCKED
+ * set, then we'll accept any stream, even if locked.
+ * We want to prefer the LOCKED case so that we don't have to ping.
+ */
+ if(!(flags & SP_UNLOCKED) && !(flags & SP_TEMPUSE)){
+ for(i = 0; i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ if(m && sp_flagged(m, SP_USEPOOL)
+ && sp_flagged(m, SP_LOCKED) && !sp_dead_stream(m)
+ && same_stream(mailbox, m)
+ && !same_stream_and_mailbox(mailbox, m)){
+ if(recent_activity(m) || pine_mail_ping(m)){
+ dprint((7,
+ "sp_stream_get: found SAME match, slot %d\n", i));
+ return(m);
+ }
+
+ sp_mark_stream_dead(m);
+ }
+ }
+
+ /* consider the unlocked streams */
+ for(i = 0; i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ if(m && sp_flagged(m, SP_USEPOOL)
+ && !sp_flagged(m, SP_LOCKED) && !sp_dead_stream(m)
+ && same_stream(mailbox, m)
+ && !same_stream_and_mailbox(mailbox, m)){
+ /* always ping unlocked streams */
+ if(pine_mail_ping(m)){
+ dprint((7,
+ "sp_stream_get: found SAME match, slot %d\n", i));
+ dprint((7,
+ "reset idle timer4: next TAG %08lx (%s)\n",
+ m->gensym,
+ debug_time(1, ps_global->debug_timestamp)));
+ sp_set_last_use_time(m, time(0));
+
+ return(m);
+ }
+
+ sp_mark_stream_dead(m);
+ }
+ }
+ }
+
+ /*
+ * Prefer streams marked SP_TEMPUSE and not LOCKED.
+ * If SP_TEMPUSE is set in the flags arg then this is the
+ * only loop we try.
+ */
+ for(i = 0; i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ if(m && sp_flagged(m, SP_USEPOOL) && sp_flagged(m, SP_TEMPUSE)
+ && !sp_flagged(m, SP_LOCKED) && !sp_dead_stream(m)
+ && same_stream(mailbox, m)
+ && !same_stream_and_mailbox(mailbox, m)){
+ if(pine_mail_ping(m)){
+ dprint((7,
+ "sp_stream_get: found SAME/TEMPUSE match, slot %d\n", i));
+ dprint((7,
+ "reset idle timer2: next TAG %08lx (%s)\n",
+ m->gensym,
+ debug_time(1, ps_global->debug_timestamp)));
+ sp_set_last_use_time(m, time(0));
+ return(m);
+ }
+
+ sp_mark_stream_dead(m);
+ }
+ }
+
+ /*
+ * If SP_TEMPUSE is not set in the flags arg but SP_UNLOCKED is,
+ * then we will consider
+ * streams which are not marked SP_TEMPUSE (but are still not
+ * locked). We go through these in reverse order so that we'll get
+ * the last one added instead of the first one. It's not clear if
+ * that is a good idea or if a more complex search would somehow
+ * be better. Maybe we should use a round-robin sort of search
+ * here so that we don't leave behind unused streams. Or maybe
+ * we should keep track of when we used it and look for the LRU stream.
+ */
+ if(!(flags & SP_TEMPUSE)){
+ for(i = ps_global->s_pool.nstream - 1; i >= 0; i--){
+ m = ps_global->s_pool.streams[i];
+ if(m && sp_flagged(m, SP_USEPOOL)
+ && !sp_flagged(m, SP_LOCKED) && !sp_dead_stream(m)
+ && same_stream(mailbox, m)
+ && !same_stream_and_mailbox(mailbox, m)){
+ if(pine_mail_ping(m)){
+ dprint((7,
+ "sp_stream_get: found SAME/UNLOCKED match, slot %d\n", i));
+ dprint((7,
+ "reset idle timer3: next TAG %08lx (%s)\n",
+ m->gensym,
+ debug_time(1, ps_global->debug_timestamp)));
+ sp_set_last_use_time(m, time(0));
+ return(m);
+ }
+
+ sp_mark_stream_dead(m);
+ }
+ }
+ }
+ }
+
+ /*
+ * If we can't find a useful stream to use in pine_mail_open, we may
+ * want to re-use one that is not actively being used even though it
+ * is not on the same server. We'll have to close it and then re-use
+ * the slot.
+ */
+ if(!(flags & (SP_SAME | SP_MATCH))){
+ /*
+ * Prefer streams marked SP_TEMPUSE and not LOCKED.
+ * If SP_TEMPUSE is set in the flags arg then this is the
+ * only loop we try.
+ */
+ for(i = 0; i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ if(m && sp_flagged(m, SP_USEPOOL) && sp_flagged(m, SP_TEMPUSE)
+ && !sp_flagged(m, SP_LOCKED)){
+ dprint((7,
+ "sp_stream_get: found Not-SAME/TEMPUSE match, slot %d\n", i));
+ /*
+ * We ping it in case there is new mail that we should
+ * pass through our filters. Pine_mail_actually_close will
+ * do that.
+ */
+ (void) pine_mail_ping(m);
+ return(m);
+ }
+ }
+
+ /*
+ * If SP_TEMPUSE is not set in the flags arg, then we will consider
+ * streams which are not marked SP_TEMPUSE (but are still not
+ * locked). Maybe we should use a round-robin sort of search
+ * here so that we don't leave behind unused streams. Or maybe
+ * we should keep track of when we used it and look for the LRU stream.
+ */
+ if(!(flags & SP_TEMPUSE)){
+ for(i = ps_global->s_pool.nstream - 1; i >= 0; i--){
+ m = ps_global->s_pool.streams[i];
+ if(m && sp_flagged(m, SP_USEPOOL) && !sp_flagged(m, SP_LOCKED)){
+ dprint((7,
+ "sp_stream_get: found Not-SAME/UNLOCKED match, slot %d\n", i));
+ /*
+ * We ping it in case there is new mail that we should
+ * pass through our filters. Pine_mail_actually_close will
+ * do that.
+ */
+ (void) pine_mail_ping(m);
+ return(m);
+ }
+ }
+ }
+ }
+
+ dprint((7, "sp_stream_get: no match found\n"));
+
+ return(NULL);
+}
+
+
+void
+sp_end(void)
+{
+ int i;
+ MAILSTREAM *m;
+
+ dprint((7, "sp_end\n"));
+
+ for(i = 0; i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ if(m)
+ pine_mail_actually_close(m);
+ }
+
+ if(ps_global->s_pool.streams)
+ fs_give((void **) &ps_global->s_pool.streams);
+
+ ps_global->s_pool.nstream = 0;
+}
+
+
+/*
+ * Find a vacant slot to put this new stream in.
+ * We are willing to close and kick out another stream as long as it isn't
+ * LOCKED. However, we may find that there is no place to put this one
+ * because all the slots are used and locked. For now, we'll return -1
+ * in that case and leave the new stream out of the pool.
+ */
+int
+sp_add(MAILSTREAM *stream, int usepool)
+{
+ int i, slot = -1;
+ MAILSTREAM *m;
+
+ dprint((7, "sp_add(%s, %d)\n",
+ (stream && stream->mailbox) ? stream->mailbox : "?", usepool));
+
+ if(!stream){
+ dprint((7, "sp_add: NULL stream\n"));
+ return -1;
+ }
+
+ /* If this stream is already there, don't add it again */
+ for(i = 0; i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ if(m == stream){
+ slot = i;
+ dprint((7,
+ "sp_add: stream was already in slot %d\n", slot));
+ return 0;
+ }
+ }
+
+ if(usepool && !sp_flagged(stream, SP_PERMLOCKED)
+ && sp_nusepool_notperm() >= ps_global->s_pool.max_remstream){
+ dprint((7,
+ "sp_add: reached max implicit SP_USEPOOL of %d\n",
+ ps_global->s_pool.max_remstream));
+ return -1;
+ }
+
+ /* Look for an unused slot */
+ for(i = 0; i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ if(!m){
+ slot = i;
+ dprint((7,
+ "sp_add: using empty slot %d\n", slot));
+ break;
+ }
+ }
+
+ /* else, allocate more space */
+ if(slot < 0){
+ ps_global->s_pool.nstream++;
+ slot = ps_global->s_pool.nstream - 1;
+ if(ps_global->s_pool.streams){
+ fs_resize((void **) &ps_global->s_pool.streams,
+ ps_global->s_pool.nstream *
+ sizeof(*ps_global->s_pool.streams));
+ ps_global->s_pool.streams[slot] = NULL;
+ }
+ else{
+ ps_global->s_pool.streams =
+ (MAILSTREAM **) fs_get(ps_global->s_pool.nstream *
+ sizeof(*ps_global->s_pool.streams));
+ memset(ps_global->s_pool.streams, 0,
+ ps_global->s_pool.nstream *
+ sizeof(*ps_global->s_pool.streams));
+ }
+
+ dprint((7,
+ "sp_add: allocate more space, using new slot %d\n", slot));
+ }
+
+ if(slot >= 0 && slot < ps_global->s_pool.nstream){
+ ps_global->s_pool.streams[slot] = stream;
+ return 0;
+ }
+ else{
+ dprint((7, "sp_add: failed to find a slot!\n"));
+ return -1;
+ }
+}
+
+
+/*
+ * Simply remove this stream from the stream pool.
+ */
+void
+sp_delete(MAILSTREAM *stream)
+{
+ int i;
+ MAILSTREAM *m;
+
+ if(!stream)
+ return;
+
+ dprint((7, "sp_delete(%s)\n",
+ (stream && stream->mailbox) ? stream->mailbox : "?"));
+
+ /*
+ * There are some global stream pointers that we have to worry
+ * about before deleting the stream.
+ */
+
+ /* first, mail_stream is the global currently open folder */
+ if(ps_global->mail_stream == stream)
+ ps_global->mail_stream = NULL;
+
+ /* remote address books may have open stream pointers */
+ note_closed_adrbk_stream(stream);
+
+ if(pith_opt_closing_stream)
+ (*pith_opt_closing_stream)(stream);
+
+ for(i = 0; i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ if(m == stream){
+ ps_global->s_pool.streams[i] = NULL;
+ dprint((7,
+ "sp_delete: stream removed from slot %d\n", i));
+ return;
+ }
+ }
+}
+
+
+/*
+ * Returns 1 if any locked userfldr is dead, 0 if all alive.
+ */
+int
+sp_a_locked_stream_is_dead(void)
+{
+ int i, ret = 0;
+ MAILSTREAM *m;
+
+ for(i = 0; !ret && i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR)
+ && sp_dead_stream(m))
+ ret++;
+ }
+
+ return(ret);
+}
+
+
+/*
+ * Returns 1 if any locked stream is changed, 0 otherwise
+ */
+int
+sp_a_locked_stream_changed(void)
+{
+ int i, ret = 0;
+ MAILSTREAM *m;
+
+ for(i = 0; !ret && i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR)
+ && sp_mail_box_changed(m))
+ ret++;
+ }
+
+ return(ret);
+}
+
+
+/*
+ * Returns the inbox stream or NULL.
+ */
+MAILSTREAM *
+sp_inbox_stream(void)
+{
+ int i;
+ MAILSTREAM *m, *ret = NULL;
+
+ for(i = 0; !ret && i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ if(m && sp_flagged(m, SP_INBOX))
+ ret = m;
+ }
+
+ return(ret);
+}
+
+
+/*
+ * Make sure that the sp_data per-stream data storage area exists.
+ *
+ * Returns a handle to the sp_data data unless stream is NULL,
+ * in which case NULL is returned
+ */
+PER_STREAM_S **
+sp_data(MAILSTREAM *stream)
+{
+ PER_STREAM_S **pss = NULL;
+
+ if(stream){
+ if(*(pss = (PER_STREAM_S **) &stream->sparep) == NULL){
+ *pss = (PER_STREAM_S *) fs_get(sizeof(PER_STREAM_S));
+ memset(*pss, 0, sizeof(PER_STREAM_S));
+ reset_check_point(stream);
+ }
+ }
+
+ return(pss);
+}
+
+
+/*
+ * Returns a pointer to the msgmap associated with the argument stream.
+ *
+ * If the PER_STREAM_S data or the msgmap does not already exist, it will be
+ * created.
+ */
+MSGNO_S *
+sp_msgmap(MAILSTREAM *stream)
+{
+ MSGNO_S **msgmap = NULL;
+ PER_STREAM_S **pss = NULL;
+
+ pss = sp_data(stream);
+
+ if(pss && *pss
+ && (*(msgmap = (MSGNO_S **) &(*pss)->msgmap) == NULL))
+ mn_init(msgmap, stream->nmsgs);
+
+ return(msgmap ? *msgmap : NULL);
+}
+
+
+void
+sp_free_callback(void **sparep)
+{
+ PER_STREAM_S **pss;
+ MAILSTREAM *stream = NULL, *m;
+ int i;
+
+ pss = (PER_STREAM_S **) sparep;
+
+ if(pss && *pss){
+ /*
+ * It is possible that this has been called from c-client when
+ * we weren't expecting it. We need to clean up the stream pool
+ * entries if the stream that goes with this pointer is in the
+ * stream pool somewhere.
+ */
+ for(i = 0; !stream && i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ if(sparep && *sparep && m && m->sparep == *sparep)
+ stream = m;
+ }
+
+ if(stream){
+ if(ps_global->mail_stream == stream)
+ ps_global->mail_stream = NULL;
+
+ sp_delete(stream);
+ }
+
+ sp_free(pss);
+ }
+}
+
+
+/*
+ * Free the data but don't mess with the stream pool.
+ */
+void
+sp_free(PER_STREAM_S **pss)
+{
+ if(pss && *pss){
+ if((*pss)->msgmap){
+ if(ps_global->msgmap == (*pss)->msgmap)
+ ps_global->msgmap = NULL;
+
+ mn_give(&(*pss)->msgmap);
+ }
+
+ if((*pss)->fldr)
+ fs_give((void **) &(*pss)->fldr);
+
+ fs_give((void **) pss);
+ }
+}
+
+
+
+/*----------------------------------------------------------------------
+ See if stream can be used for a mailbox name
+
+ Accepts: mailbox name
+ candidate stream
+ Returns: stream if it can be used, else NIL
+
+ This is called to weed out unnecessary use of c-client streams. In other
+ words, to help facilitate re-use of streams.
+
+ This code is very similar to the same_remote_mailboxes code below, which
+ is used in pine_mail_open. That code compares two mailbox names. One is
+ usually from the config file and the other is either from the config file
+ or is typed in. Here and in same_stream_and_mailbox below, we're comparing
+ an open stream to a name instead of two names. We could conceivably use
+ same_remote_mailboxes to compare stream->mailbox to name, but it isn't
+ exactly the same and the differences may be important. Some stuff that
+ happens here seems wrong, but it isn't easy to fix.
+ Having !mb_n.port count as a match to any mb_s.port isn't right. It should
+ only match if mb_s.port is equal to the default, but the default isn't
+ something that is available to us. The same thing is done in c-client in
+ the mail_usable_network_stream() routine, and it isn't right there, either.
+ The semantics of a missing user are also suspect, because just like with
+ port, a default is used.
+ ----*/
+MAILSTREAM *
+same_stream(char *name, MAILSTREAM *stream)
+{
+ NETMBX mb_s, mb_n, mb_o;
+
+ if(stream && stream->mailbox && *stream->mailbox && name && *name
+ && !(sp_dead_stream(stream))
+ && mail_valid_net_parse(stream->mailbox, &mb_s)
+ && mail_valid_net_parse(stream->original_mailbox, &mb_o)
+ && mail_valid_net_parse(name, &mb_n)
+ && !strucmp(mb_n.service, mb_s.service)
+ && (!strucmp(mb_n.host, mb_o.host) /* s is already canonical */
+ || !strucmp(canonical_name(mb_n.host), mb_s.host))
+ && (!mb_n.port || mb_n.port == mb_s.port)
+ && mb_n.anoflag == stream->anonymous
+ && ((mb_n.user && *mb_n.user &&
+ mb_s.user && !strcmp(mb_n.user, mb_s.user))
+ ||
+ ((!mb_n.user || !*mb_n.user)
+ && mb_s.user
+ && ((ps_global->VAR_USER_ID
+ && !strcmp(ps_global->VAR_USER_ID, mb_s.user))
+ ||
+ (!ps_global->VAR_USER_ID
+ && ps_global->ui.login[0]
+ && !strcmp(ps_global->ui.login, mb_s.user))))
+ ||
+ (!((mb_n.user && *mb_n.user) || (mb_s.user && *mb_s.user))
+ && stream->anonymous))
+ && (struncmp(mb_n.service, "imap", 4) ? 1 : strcmp(imap_host(stream), ".NO-IMAP-CONNECTION."))){
+ dprint((7, "same_stream: name->%s == stream->%s: yes\n",
+ name ? name : "?",
+ (stream && stream->mailbox) ? stream->mailbox : "NULL"));
+ return(stream);
+ }
+
+ dprint((7, "same_stream: name->%s == stream->%s: no dice\n",
+ name ? name : "?",
+ (stream && stream->mailbox) ? stream->mailbox : "NULL"));
+ return(NULL);
+}
+
+
+
+/*----------------------------------------------------------------------
+ See if this stream has the named mailbox selected.
+
+ Accepts: mailbox name
+ candidate stream
+ Returns: stream if it can be used, else NIL
+ ----*/
+MAILSTREAM *
+same_stream_and_mailbox(char *name, MAILSTREAM *stream)
+{
+ NETMBX mb_s, mb_n;
+
+ if(same_stream(name, stream)
+ && mail_valid_net_parse(stream->mailbox, &mb_s)
+ && mail_valid_net_parse(name, &mb_n)
+ && (mb_n.mailbox && mb_s.mailbox
+ && (!strcmp(mb_n.mailbox,mb_s.mailbox) /* case depend except INBOX */
+ || (!strucmp(mb_n.mailbox,"INBOX")
+ && !strucmp(mb_s.mailbox,"INBOX"))))){
+ dprint((7,
+ "same_stream_and_mailbox: name->%s == stream->%s: yes\n",
+ name ? name : "?",
+ (stream && stream->mailbox) ? stream->mailbox : "NULL"));
+ return(stream);
+ }
+
+ dprint((7,
+ "same_stream_and_mailbox: name->%s == stream->%s: no dice\n",
+ name ? name : "?",
+ (stream && stream->mailbox) ? stream->mailbox : "NULL"));
+ return(NULL);
+}
+
+/*
+ * Args -- name1 and name2 are remote mailbox names.
+ *
+ * Returns -- True if names refer to same mailbox accessed in same way
+ * False if not
+ *
+ * This has some very similar code to same_stream_and_mailbox but we're not
+ * quite ready to discard the differences.
+ * The treatment of the port and the user is not quite the same.
+ */
+int
+same_remote_mailboxes(char *name1, char *name2)
+{
+ NETMBX mb1, mb2;
+ char *cn1;
+
+ /*
+ * Probably we should allow !port equal to default port, but we don't
+ * know how to get the default port. To match what c-client does we
+ * allow !port to be equal to anything.
+ */
+ return(name1 && IS_REMOTE(name1)
+ && name2 && IS_REMOTE(name2)
+ && mail_valid_net_parse(name1, &mb1)
+ && mail_valid_net_parse(name2, &mb2)
+ && !strucmp(mb1.service, mb2.service)
+ && (!strucmp(mb1.host, mb2.host) /* just to save DNS lookups */
+ || !strucmp(cn1=canonical_name(mb1.host), mb2.host)
+ || !strucmp(cn1, canonical_name(mb2.host)))
+ && (!mb1.port || !mb2.port || mb1.port == mb2.port)
+ && mb1.anoflag == mb2.anoflag
+ && mb1.mailbox && mb2.mailbox
+ && (!strcmp(mb1.mailbox, mb2.mailbox)
+ || (!strucmp(mb1.mailbox,"INBOX")
+ && !strucmp(mb2.mailbox,"INBOX")))
+ && ((mb1.user && *mb1.user && mb2.user && *mb2.user
+ && !strcmp(mb1.user, mb2.user))
+ ||
+ (!(mb1.user && *mb1.user) && !(mb2.user && *mb2.user))
+ ||
+ (!(mb1.user && *mb1.user)
+ && ((ps_global->VAR_USER_ID
+ && !strcmp(ps_global->VAR_USER_ID, mb2.user))
+ ||
+ (!ps_global->VAR_USER_ID
+ && ps_global->ui.login[0]
+ && !strcmp(ps_global->ui.login, mb2.user))))
+ ||
+ (!(mb2.user && *mb2.user)
+ && ((ps_global->VAR_USER_ID
+ && !strcmp(ps_global->VAR_USER_ID, mb1.user))
+ ||
+ (!ps_global->VAR_USER_ID
+ && ps_global->ui.login[0]
+ && !strcmp(ps_global->ui.login, mb1.user))))));
+}
+
+
+int
+is_imap_stream(MAILSTREAM *stream)
+{
+ return(stream && stream->dtb && stream->dtb->name
+ && !strcmp(stream->dtb->name, "imap"));
+}
+
+
+int
+modern_imap_stream(MAILSTREAM *stream)
+{
+ return(is_imap_stream(stream) && LEVELIMAP4rev1(stream));
+}
+
+
+/*----------------------------------------------------------------------
+ Check and see if all the stream are alive
+
+Returns: 0 if there was no change
+ >0 if streams have died since last call
+
+Also outputs a message that the streams have died
+ ----*/
+int
+streams_died(void)
+{
+ int rv = 0;
+ int i;
+ MAILSTREAM *m;
+ char *folder;
+
+ for(i = 0; i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ if(m && sp_dead_stream(m)){
+ if(sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR)){
+ if(!sp_noticed_dead_stream(m)){
+ rv++;
+ sp_set_noticed_dead_stream(m, 1);
+ folder = STREAMNAME(m);
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("MAIL FOLDER \"%s\" CLOSED DUE TO ACCESS ERROR"),
+ short_str(pretty_fn(folder) ? pretty_fn(folder) : "?",
+ tmp_20k_buf+1000, SIZEOF_20KBUF-1000, 35, FrontDots));
+ dprint((6, "streams_died: locked: \"%s\"\n",
+ folder));
+ if(rv == 1){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Folder \"%s\" is Closed"),
+ short_str(pretty_fn(folder) ? pretty_fn(folder) : "?",
+ tmp_20k_buf+1000, SIZEOF_20KBUF-1000, 35, FrontDots));
+ if(pith_opt_icon_text)
+ (*pith_opt_icon_text)(tmp_20k_buf, IT_MCLOSED);
+ }
+ }
+ }
+ else{
+ if(!sp_noticed_dead_stream(m)){
+ sp_set_noticed_dead_stream(m, 1);
+ folder = STREAMNAME(m);
+ /*
+ * If a cached stream died and then we tried to use it
+ * it could cause problems. We could warn about it here
+ * but it may be confusing because it might be
+ * unrelated to what the user is doing and not cause
+ * any problem at all.
+ */
+#if 0
+ if(sp_flagged(m, SP_USEPOOL))
+ q_status_message(SM_ORDER, 3, 3,
+ "Warning: Possible problem accessing remote data, connection died.");
+#endif
+
+ dprint((6, "streams_died: not locked: \"%s\"\n",
+ folder));
+ }
+
+ pine_mail_actually_close(m);
+ }
+ }
+ }
+
+ return(rv);
+}
+
+
+/* Some stream is locked checks to see if there is any stream for which we
+ * are in a callback from c-client
+ */
+
+int
+some_stream_is_locked(void)
+{
+ int rv = 0, i;
+ MAILSTREAM *m;
+
+ for(i = 0; rv == 0 && i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ if(m && m->lock)
+ rv++;
+ }
+
+ return(rv);
+}
+
+/*
+ * Very simple version of appenduid_cb until we need something
+ * more complex.
+ */
+
+static imapuid_t last_append_uid;
+
+void
+appenduid_cb(char *mailbox,unsigned long uidvalidity, SEARCHSET *set)
+{
+ last_append_uid = set ? set->first : 0L;
+}
+
+
+imapuid_t
+get_last_append_uid(void)
+{
+ return last_append_uid;
+}
+
+
+/*
+ * mail_cmd_stream - return a stream suitable for mail_lsub,
+ * mail_subscribe, and mail_unsubscribe
+ *
+ */
+MAILSTREAM *
+mail_cmd_stream(CONTEXT_S *context, int *closeit)
+{
+ char tmp[MAILTMPLEN];
+
+ *closeit = 1;
+ (void) context_apply(tmp, context, "x", sizeof(tmp));
+
+ return(pine_mail_open(NULL, tmp,
+ OP_HALFOPEN | OP_SILENT | SP_USEPOOL | SP_TEMPUSE,
+ NULL));
+}
+
+
+/*
+ * This is so we can replace the old rfc822_ routines like rfc822_header_line
+ * with the new version that checks bounds, like rfc822_output_header_line.
+ * This routine is called when would be a bounds overflow, which we simply log
+ * and go on with the truncated data.
+ */
+long
+dummy_soutr(void *stream, char *string)
+{
+ dprint((2, "dummy_soutr unexpected call, caught overflow\n"));
+ return LONGT;
+}
diff --git a/pith/stream.h b/pith/stream.h
new file mode 100644
index 00000000..eafe6f24
--- /dev/null
+++ b/pith/stream.h
@@ -0,0 +1,471 @@
+/*
+ * $Id: stream.h 768 2007-10-24 00:10:03Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_STREAM_INCLUDED
+#define PITH_STREAM_INCLUDED
+
+
+#include "../pith/context.h"
+#include "../pith/msgno.h"
+#include "../pith/savetype.h"
+
+
+#define AOS_NONE 0x00 /* alredy_open_stream: no flag */
+#define AOS_RW_ONLY 0x01 /* don't match readonly streams */
+
+/* pine_mail_list flags */
+#define PML_IS_MOVE_MBOX 0x01
+
+
+/*
+ * The stream pool is where we keep pointers to open streams. Some of them are
+ * actively being used, some are connected to a folder but aren't actively
+ * in use, some are random temporary use streams left open for possible
+ * re-use. Each open stream should be in the streams array, which is of
+ * size nstream altogether. Streams which are not to be re-used (don't have
+ * the flag SP_USEPOOL set) are in the array anyway.
+ */
+
+/*
+ * Structure holds global information about the stream pool. The per-stream
+ * information is stored in a PER_STREAM_S struct attached to each stream.
+ */
+typedef struct stream_pool {
+ int max_remstream; /* max implicitly cached remote streams */
+ int nstream; /* size of streams array */
+ MAILSTREAM **streams; /* the array of streams in stream pool */
+} SP_S;
+
+/*
+ * Pine's private per-stream data stored on the c-client's stream
+ * spare pointer.
+ */
+typedef struct pine_per_stream_data {
+ MSGNO_S *msgmap;
+ CONTEXT_S *context; /* context fldr was interpreted in */
+ char *fldr; /* folder name, alloced copy */
+ unsigned long flags;
+ unsigned long icache_flags;
+ int ref_cnt;
+ long new_mail_count; /* new mail since the last new_mail check */
+ long mail_since_cmd; /* new mail since last key pressed */
+ long expunge_count;
+ long first_unseen;
+ long recent_since_visited;
+ int check_cnt;
+ time_t first_status_change;
+ time_t last_ping; /* Keeps track of when the last */
+ /* command was. The command wasn't */
+ /* necessarily a ping. */
+ time_t last_expunged_reaper; /* Some IMAP commands defer the */
+ /* return of EXPUNGE responses. */
+ /* This is the time of the last */
+ /* command which did not defer. */
+ time_t last_chkpnt_done;
+ time_t last_use_time;
+ time_t last_activity;
+ imapuid_t saved_uid_validity;
+ imapuid_t saved_uid_last;
+ char *saved_cur_msg_id;
+ unsigned unsorted_newmail:1;
+ unsigned need_to_rethread:1;
+ unsigned io_error_on_stream:1;
+ unsigned mail_box_changed:1;
+ unsigned viewing_a_thread:1;
+ unsigned dead_stream:1;
+ unsigned noticed_dead_stream:1;
+ unsigned closing:1;
+} PER_STREAM_S;
+
+/*
+ * Complicated set of flags for stream pool cache.
+ * LOCKED, PERMLOCKED, TEMPUSE, and USEPOOL are flags stored in the stream
+ * flags of the PER_STREAM_S structure.
+ *
+ * SP_LOCKED -- In use, don't re-use this.
+ * That isn't a good description of SP_LOCKED. Every time
+ * we pine_mail_open a stream it is SP_LOCKED and a ref_cnt
+ * is incremented. Pine_mail_close decrements the ref_cnt
+ * and unlocks it when we get to zero.
+ * SP_PERMLOCKED -- Should always be kept open, like INBOX. Right now the
+ * only significance of this is that the expunge_and_close
+ * won't happen if this is set (like the way INBOX works).
+ * If a stream is PERMLOCKED it should no doubt be LOCKED
+ * as well (it isn't done implicitly in the tests).
+ * SP_INBOX -- This stream is open on the INBOX.
+ * SP_USERFLDR -- This stream was opened by the user explicitly, not
+ * implicitly like would happen with a save or a remote
+ * address book, etc.
+ * SP_FILTERED -- This stream was opened by the user explicitly and
+ * filtered.
+ * SP_TEMPUSE -- If a stream is not SP_LOCKED, that says we can re-use
+ * it if need be but we should prefer to use another unused
+ * slot if there is one. If a stream is marked TEMPUSE we
+ * should consider re-using it before we consider re-using
+ * a stream which is not LOCKED but not marked TEMPUSE.
+ * This flag is not only stored in the PER_STREAM_S flags,
+ * it is also an input argument to sp_stream_get.
+ * It may make sense to mark a stream both SP_LOCKED and
+ * SP_TEMPUSE. That way, when we close the stream it will
+ * be SP_TEMPUSE and more re-usable than if we didn't.
+ * SP_USEPOOL -- Passed to pine_mail_open, it means to consider the
+ * stream pool when opening and to put it into the stream
+ * pool after we open it. If this is not set when we open,
+ * we do an honest open and an honest close when we close.
+ *
+ * These flags are input flags to sp_stream_get.
+ * SP_MATCH -- We're looking for a stream that is already open on
+ * this mailbox. This is good if we are reopening the
+ * same mailbox we already had opened.
+ * SP_SAME -- We're looking for a stream that is open to the same
+ * server. For example, we might want to do a STATUS
+ * command or a DELETE. We could use any stream that
+ * is already open for this. Unless SP_MATCH is also
+ * set, this will not return exact matches. (For example,
+ * it is a bad idea to do a STATUS command on an already
+ * selected mailbox. There may be locking problems if you
+ * try to delete a folder that is selected...)
+ * SP_TEMPUSE -- The checking for SP_SAME streams is controlled by these
+ * SP_UNLOCKED two flags. If SP_TEMPUSE is set then we will only match
+ * streams which are marked TEMPUSE and not LOCKED.
+ * If TEMPUSE is not set but UNLOCKED is, then we will
+ * match on any same stream that is not locked. We'll choose
+ * SP_TEMPUSE streams in preference to those that aren't
+ * SP_TEMPUSE. If neither SP_TEMPUSE or SP_UNLOCKED is set,
+ * then we'll consider any stream, even if it is locked.
+ * We'll still prefer TEMPUSE first, then UNLOCKED, then any.
+ *
+ * Careful with the values of these flags. Some of them should really be
+ * in separate name spaces, but we've combined all of them for convenience.
+ * In particular, SP_USERFLDR, SP_INBOX, SP_USEPOOL, and SP_TEMPUSE are
+ * all passed in the pine_mail_open flags argument, alongside OP_DEBUG and
+ * friends from c-client. So they have to have different values than
+ * those OP_ flags. SP_PERMLOCKED was passed at one time but isn't anymore.
+ * Still, include it in the careful set. C-client reserves the bits
+ * 0xff000000 for client flags.
+ */
+
+#define SP_USERFLDR 0x01000000
+#define SP_INBOX 0x02000000
+#define SP_USEPOOL 0x04000000
+#define SP_TEMPUSE 0x08000000
+
+#define SP_PERMLOCKED 0x10000000
+#define SP_LOCKED 0x20000000
+
+#define SP_MATCH 0x00100000
+#define SP_SAME 0x00200000
+#define SP_UNLOCKED 0x00400000
+#define SP_FILTERED 0x00800000
+#define SP_RO_OK 0x01000000 /* Readonly stream ok for SP_MATCH */
+
+/* these are for icache_flags */
+#define SP_NEED_FORMAT_SETUP 0x01
+#define SP_FORMAT_INCLUDES_MSGNO 0x02
+#define SP_FORMAT_INCLUDES_SMARTDATE 0x04
+
+
+/* access value of first_unseen, but don't set it with this */
+#define sp_first_unseen(stream) \
+ ((sp_data(stream) && *sp_data(stream)) \
+ ? (*sp_data(stream))->first_unseen : 0L)
+
+/* use this to set it */
+#define sp_set_first_unseen(stream,val) do{ \
+ if(sp_data(stream) && *sp_data(stream)) \
+ (*sp_data(stream))->first_unseen = (val);}while(0)
+
+#define sp_flags(stream) \
+ ((sp_data(stream) && *sp_data(stream)) \
+ ? (*sp_data(stream))->flags : 0L)
+
+#define sp_set_flags(stream,val) do{ \
+ if(sp_data(stream) && *sp_data(stream)) \
+ (*sp_data(stream))->flags = (val);}while(0)
+
+#define sp_icache_flags(stream) \
+ ((sp_data(stream) && *sp_data(stream)) \
+ ? (*sp_data(stream))->icache_flags : 0L)
+
+#define sp_set_icache_flags(stream,val) do{ \
+ if(sp_data(stream) && *sp_data(stream)) \
+ (*sp_data(stream))->icache_flags = (val);}while(0)
+
+#define sp_ref_cnt(stream) \
+ ((sp_data(stream) && *sp_data(stream)) \
+ ? (*sp_data(stream))->ref_cnt : 0L)
+
+#define sp_set_ref_cnt(stream,val) do{ \
+ if(sp_data(stream) && *sp_data(stream)) \
+ (*sp_data(stream))->ref_cnt = (val);}while(0)
+
+#define sp_expunge_count(stream) \
+ ((sp_data(stream) && *sp_data(stream)) \
+ ? (*sp_data(stream))->expunge_count : 0L)
+
+#define sp_set_expunge_count(stream,val) do{ \
+ if(sp_data(stream) && *sp_data(stream)) \
+ (*sp_data(stream))->expunge_count = (val);}while(0)
+
+#define sp_new_mail_count(stream) \
+ ((sp_data(stream) && *sp_data(stream)) \
+ ? (*sp_data(stream))->new_mail_count : 0L)
+
+#define sp_set_new_mail_count(stream,val) do{ \
+ if(sp_data(stream) && *sp_data(stream)) \
+ (*sp_data(stream))->new_mail_count = (val);}while(0)
+
+#define sp_mail_since_cmd(stream) \
+ ((sp_data(stream) && *sp_data(stream)) \
+ ? (*sp_data(stream))->mail_since_cmd : 0L)
+
+#define sp_set_mail_since_cmd(stream,val) do{ \
+ if(sp_data(stream) && *sp_data(stream)) \
+ (*sp_data(stream))->mail_since_cmd = (val);}while(0)
+
+#define sp_recent_since_visited(stream) \
+ ((sp_data(stream) && *sp_data(stream)) \
+ ? (*sp_data(stream))->recent_since_visited : 0L)
+
+#define sp_set_recent_since_visited(stream,val) do{ \
+ if(sp_data(stream) && *sp_data(stream)) \
+ (*sp_data(stream))->recent_since_visited = (val);}while(0)
+
+#define sp_check_cnt(stream) \
+ ((sp_data(stream) && *sp_data(stream)) \
+ ? (*sp_data(stream))->check_cnt : 0L)
+
+#define sp_set_check_cnt(stream,val) do{ \
+ if(sp_data(stream) && *sp_data(stream)) \
+ (*sp_data(stream))->check_cnt = (val);}while(0)
+
+#define sp_first_status_change(stream) \
+ ((sp_data(stream) && *sp_data(stream)) \
+ ? (*sp_data(stream))->first_status_change : 0L)
+
+#define sp_set_first_status_change(stream,val) do{ \
+ if(sp_data(stream) && *sp_data(stream)) \
+ (*sp_data(stream))->first_status_change = (val);}while(0)
+
+#define sp_last_ping(stream) \
+ ((sp_data(stream) && *sp_data(stream)) \
+ ? (*sp_data(stream))->last_ping : 0L)
+
+#define sp_set_last_ping(stream,val) do{ \
+ if(sp_data(stream) && *sp_data(stream)) \
+ (*sp_data(stream))->last_ping = (val);}while(0)
+
+#define sp_last_expunged_reaper(stream) \
+ ((sp_data(stream) && *sp_data(stream)) \
+ ? (*sp_data(stream))->last_expunged_reaper : 0L)
+
+#define sp_set_last_expunged_reaper(stream,val) do{ \
+ if(sp_data(stream) && *sp_data(stream)) \
+ (*sp_data(stream))->last_expunged_reaper = (val);}while(0)
+
+#define sp_last_chkpnt_done(stream) \
+ ((sp_data(stream) && *sp_data(stream)) \
+ ? (*sp_data(stream))->last_chkpnt_done : 0L)
+
+#define sp_set_last_chkpnt_done(stream,val) do{ \
+ if(sp_data(stream) && *sp_data(stream)) \
+ (*sp_data(stream))->last_chkpnt_done = (val);}while(0)
+
+#define sp_last_use_time(stream) \
+ ((sp_data(stream) && *sp_data(stream)) \
+ ? (*sp_data(stream))->last_use_time : 0L)
+
+#define sp_set_last_use_time(stream,val) do{ \
+ if(sp_data(stream) && *sp_data(stream)) \
+ (*sp_data(stream))->last_use_time = (val);}while(0)
+
+#define sp_last_activity(stream) \
+ ((sp_data(stream) && *sp_data(stream)) \
+ ? (*sp_data(stream))->last_activity : 0L)
+
+#define sp_set_last_activity(stream,val) do{ \
+ if(sp_data(stream) && *sp_data(stream)) \
+ (*sp_data(stream))->last_activity = (val);}while(0)
+
+#define sp_saved_uid_validity(stream) \
+ ((sp_data(stream) && *sp_data(stream)) \
+ ? (*sp_data(stream))->saved_uid_validity : 0L)
+
+#define sp_set_saved_uid_validity(stream,val) do{ \
+ if(sp_data(stream) && *sp_data(stream)) \
+ (*sp_data(stream))->saved_uid_validity = (val);}while(0)
+
+#define sp_saved_uid_last(stream) \
+ ((sp_data(stream) && *sp_data(stream)) \
+ ? (*sp_data(stream))->saved_uid_last : 0L)
+
+#define sp_set_saved_uid_last(stream,val) do{ \
+ if(sp_data(stream) && *sp_data(stream)) \
+ (*sp_data(stream))->saved_uid_last = (val);}while(0)
+
+#define sp_mail_box_changed(stream) \
+ ((sp_data(stream) && *sp_data(stream)) \
+ ? (*sp_data(stream))->mail_box_changed : 0L)
+
+#define sp_set_mail_box_changed(stream,val) do{ \
+ if(sp_data(stream) && *sp_data(stream)) \
+ (*sp_data(stream))->mail_box_changed = (val);}while(0)
+
+#define sp_unsorted_newmail(stream) \
+ ((sp_data(stream) && *sp_data(stream)) \
+ ? (*sp_data(stream))->unsorted_newmail : 0L)
+
+#define sp_set_unsorted_newmail(stream,val) do{ \
+ if(sp_data(stream) && *sp_data(stream)) \
+ (*sp_data(stream))->unsorted_newmail = (val);}while(0)
+
+#define sp_need_to_rethread(stream) \
+ ((sp_data(stream) && *sp_data(stream)) \
+ ? (*sp_data(stream))->need_to_rethread : 0L)
+
+#define sp_set_need_to_rethread(stream,val) do{ \
+ if(sp_data(stream) && *sp_data(stream)) \
+ (*sp_data(stream))->need_to_rethread = (val);}while(0)
+
+#define sp_viewing_a_thread(stream) \
+ ((sp_data(stream) && *sp_data(stream)) \
+ ? (*sp_data(stream))->viewing_a_thread : 0L)
+
+#define sp_set_viewing_a_thread(stream,val) do{ \
+ if(sp_data(stream) && *sp_data(stream)) \
+ (*sp_data(stream))->viewing_a_thread = (val);}while(0)
+
+#define sp_dead_stream(stream) \
+ ((sp_data(stream) && *sp_data(stream)) \
+ ? (*sp_data(stream))->dead_stream : 0L)
+
+#define sp_set_dead_stream(stream,val) do{ \
+ if(sp_data(stream) && *sp_data(stream)) \
+ (*sp_data(stream))->dead_stream = (val);}while(0)
+
+#define sp_noticed_dead_stream(stream) \
+ ((sp_data(stream) && *sp_data(stream)) \
+ ? (*sp_data(stream))->noticed_dead_stream : 0L)
+
+#define sp_set_noticed_dead_stream(stream,val) do{ \
+ if(sp_data(stream) && *sp_data(stream)) \
+ (*sp_data(stream))->noticed_dead_stream = (val);}while(0)
+
+#define sp_closing(stream) \
+ ((sp_data(stream) && *sp_data(stream)) \
+ ? (*sp_data(stream))->closing : 0L)
+
+#define sp_set_closing(stream,val) do{ \
+ if(sp_data(stream) && *sp_data(stream)) \
+ (*sp_data(stream))->closing = (val);}while(0)
+
+#define sp_io_error_on_stream(stream) \
+ ((sp_data(stream) && *sp_data(stream)) \
+ ? (*sp_data(stream))->io_error_on_stream : 0L)
+
+#define sp_set_io_error_on_stream(stream,val) do{ \
+ if(sp_data(stream) && *sp_data(stream)) \
+ (*sp_data(stream))->io_error_on_stream = (val);}while(0)
+
+#define sp_fldr(stream) \
+ ((sp_data(stream) && *sp_data(stream)) \
+ ? (*sp_data(stream))->fldr : (char *) NULL)
+
+#define sp_saved_cur_msg_id(stream) \
+ ((sp_data(stream) && *sp_data(stream)) \
+ ? (*sp_data(stream))->saved_cur_msg_id : (char *) NULL)
+
+#define sp_context(stream) \
+ ((sp_data(stream) && *sp_data(stream)) \
+ ? (*sp_data(stream))->context : 0L)
+
+#define sp_set_context(stream,val) do{ \
+ if(sp_data(stream) && *sp_data(stream)) \
+ (*sp_data(stream))->context = (val);}while(0)
+
+
+extern MAILSTATUS *pine_cached_status;
+
+
+/*
+ * Macros to help fetch specific fields
+ */
+#define pine_fetchheader_lines(S,N,M,F) pine_fetch_header(S,N,M,F,0L)
+#define pine_fetchheader_lines_not(S,N,M,F) pine_fetch_header(S,N,M,F,FT_NOT)
+
+
+/* exported protoypes */
+MAILSTREAM *pine_mail_open(MAILSTREAM *, char *, long, long *);
+long pine_mail_create(MAILSTREAM *, char *);
+long pine_mail_delete(MAILSTREAM *, char *);
+long pine_mail_append_full(MAILSTREAM *, char *, char *, char *, STRING *);
+#define pine_mail_append(stream,mailbox,message) \
+ pine_mail_append_full(stream,mailbox,NULL,NULL,message)
+long pine_mail_append_multiple(MAILSTREAM *, char *, append_t, APPENDPACKAGE *, MAILSTREAM *);
+long pine_mail_copy_full(MAILSTREAM *, char *, char *, long);
+#define pine_mail_copy(stream,sequence,mailbox) \
+ pine_mail_copy_full(stream,sequence,mailbox,0)
+long pine_mail_rename(MAILSTREAM *, char *, char *);
+void pine_mail_close(MAILSTREAM *);
+void pine_mail_actually_close(MAILSTREAM *);
+void maybe_kill_old_stream(MAILSTREAM *);
+long pine_mail_search_full(MAILSTREAM *, char *, SEARCHPGM *, long);
+void pine_mail_fetch_flags(MAILSTREAM *, char *, long);
+ENVELOPE *pine_mail_fetchenvelope(MAILSTREAM *, unsigned long);
+ENVELOPE *pine_mail_fetch_structure(MAILSTREAM *, unsigned long, BODY **, long);
+ENVELOPE *pine_mail_fetchstructure(MAILSTREAM *, unsigned long, BODY **);
+char *pine_mail_fetch_body(MAILSTREAM *, unsigned long, char *, unsigned long *, long);
+char *pine_mail_fetch_text(MAILSTREAM *, unsigned long, char *, unsigned long *, long);
+char *pine_mail_partial_fetch_wrapper(MAILSTREAM *, unsigned long, char *, unsigned long *,
+ long, unsigned long, char **, int);
+long pine_mail_ping(MAILSTREAM *);
+void pine_mail_check(MAILSTREAM *);
+int pine_mail_list(MAILSTREAM *, char *, char *, unsigned *);
+long pine_mail_status(MAILSTREAM *, char *, long);
+long pine_mail_status_full(MAILSTREAM *, char *, long, imapuid_t *, imapuid_t *);
+int check_for_move_mbox(char *, char *, size_t, char **);
+MAILSTREAM *already_open_stream(char *, int);
+void pine_imap_cmd_happened(MAILSTREAM *, char *, long);
+void sp_cleanup_dead_streams(void);
+int sp_flagged(MAILSTREAM *, unsigned long);
+void sp_set_fldr(MAILSTREAM *, char *);
+void sp_set_saved_cur_msg_id(MAILSTREAM *, char *);
+void sp_flag(MAILSTREAM *, unsigned long);
+void sp_unflag(MAILSTREAM *, unsigned long);
+void sp_mark_stream_dead(MAILSTREAM *);
+int sp_nremote_permlocked(void);
+MAILSTREAM *sp_stream_get(char *, unsigned long);
+void sp_end(void);
+int sp_a_locked_stream_is_dead(void);
+int sp_a_locked_stream_changed(void);
+MAILSTREAM *sp_inbox_stream(void);
+PER_STREAM_S **sp_data(MAILSTREAM *);
+MSGNO_S *sp_msgmap(MAILSTREAM *);
+void sp_free_callback(void **);
+MAILSTREAM *same_stream(char *, MAILSTREAM *);
+MAILSTREAM *same_stream_and_mailbox(char *, MAILSTREAM *);
+int same_remote_mailboxes(char *, char *);
+int is_imap_stream(MAILSTREAM *);
+int modern_imap_stream(MAILSTREAM *);
+int streams_died(void);
+int some_stream_is_locked(void);
+void appenduid_cb(char *mailbox,unsigned long uidvalidity, SEARCHSET *set);
+imapuid_t get_last_append_uid(void);
+MAILSTREAM *mail_cmd_stream(CONTEXT_S *, int *);
+long dummy_soutr(void *, char *);
+
+
+#endif /* PITH_STREAM_INCLUDED */
diff --git a/pith/string.c b/pith/string.c
new file mode 100644
index 00000000..84717c3e
--- /dev/null
+++ b/pith/string.c
@@ -0,0 +1,2862 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: string.c 910 2008-01-14 22:28:38Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ string.c
+ Misc extra and useful string functions
+ - rplstr replace a substring with another string
+ - sqzspaces Squeeze out the extra blanks in a string
+ - sqznewlines Squeeze out \n and \r.
+ - removing_trailing_white_space
+ - short_str Replace part of string with ... for display
+ - removing_leading_white_space
+ Remove leading or trailing white space
+ - removing_double_quotes
+ Remove surrounding double quotes
+ - strclean
+ both of above plus convert to lower case
+ - skip_white_space
+ return pointer to first non-white-space char
+ - skip_to_white_space
+ return pointer to first white-space char
+ - srchstr Search a string for first occurrence of a sub string
+ - srchrstr Search a string for last occurrence of a sub string
+ - strindex Replacement for strchr/index
+ - strrindex Replacement for strrchr/rindex
+ - sstrncpy Copy one string onto another, advancing dest'n pointer
+ - istrncpy Copy n chars between bufs, making ctrl chars harmless
+ - month_abbrev Return three letter abbreviations for months
+ - month_num Calculate month number from month/year string
+ - cannon_date Formalize format of a some what formatted date
+ - repeat_char Returns a string n chars long
+ - fold Inserts newlines for folding at whitespace.
+ - byte_string Format number of bytes with Kb, Mb, Gb or bytes
+ - enth-string Format number i.e. 1: 1st, 983: 983rd....
+ - string_to_cstring Convert string to C-style constant string with \'s
+ - cstring_to_hexstring Convert cstring to hex string
+ - cstring_to_string Convert C-style string to string
+ - add_backslash_escapes Escape / and \ with \
+ - remove_backslash_escapes Undo the \ escaping, and stop string at /.
+
+ ====*/
+
+#include "../pith/headers.h"
+#include "../pith/string.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/escapes.h"
+#include "../pith/util.h"
+
+
+void char_to_octal_triple(int, char *);
+char *dollar_escape_dollars(char *);
+time_t date_to_local_time_t(char *date);
+
+
+
+/*----------------------------------------------------------------------
+ Replace dl characters in one string with another given string
+
+ args: os -- pointer into output string
+ oslen -- size of output string starting at os
+ dl -- the number of character to delete starting at os
+ is -- The string to insert
+
+ Result: returns pointer in originl string to end of string just inserted
+ ---*/
+char *
+rplstr(char *os, size_t oslen, int dl, char *is)
+{
+ char *x1, *x2, *x3;
+ int diff;
+
+ if(os == NULL)
+ return(NULL);
+
+ x1 = os + strlen(os);
+
+ /* can't delete more characters than there are */
+ if(dl > x1 - os)
+ dl = x1 - os;
+
+ x2 = is;
+ if(is != NULL)
+ x2 = is + strlen(is);
+
+ diff = (x2 - is) - dl;
+
+ if(diff < 0){ /* String shrinks */
+ x3 = os;
+ if(is != NULL)
+ for(x2 = is; *x2; *x3++ = *x2++) /* copy new string in */
+ ;
+
+ for(x2 = x3 - diff; *x2; *x3++ = *x2++) /* shift for delete */
+ ;
+
+ *x3 = *x2; /* the null */
+ }
+ else{ /* String grows */
+ /* make room for insert */
+ x3 = x1 + diff;
+ if(x3 >= os + oslen) /* just protecting ourselves */
+ x3 = os + oslen - 1;
+
+ for(; x3 >= os + (x2 - is); *x3-- = *x1--); /* shift*/
+ ;
+
+ if(is != NULL)
+ for(x1 = os, x2 = is; *x2 ; *x1++ = *x2++)
+ ;
+
+ while(*x3) x3++;
+ }
+
+ os[oslen-1] = '\0';
+
+ return(x3);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Squeeze out blanks
+ ----------------------------------------------------------------------*/
+void
+sqzspaces(char *string)
+{
+ char *p = string;
+
+ while((*string = *p++) != '\0') /* while something to copy */
+ if(!isspace((unsigned char)*string)) /* only really copy if non-blank */
+ string++;
+}
+
+
+
+/*----------------------------------------------------------------------
+ Squeeze out CR's and LF's
+ ----------------------------------------------------------------------*/
+void
+sqznewlines(char *string)
+{
+ char *p = string;
+
+ while((*string = *p++) != '\0') /* while something to copy */
+ if(*string != '\r' && *string != '\n') /* only copy if non-newline */
+ string++;
+}
+
+
+
+/*----------------------------------------------------------------------
+ Remove leading white space from a string in place
+
+ Args: string -- string to remove space from
+ ----*/
+void
+removing_leading_white_space(char *string)
+{
+ register char *p;
+
+ if(!string || !*string || !isspace(*string))
+ return;
+
+ for(p = string; *p; p++) /* find the first non-blank */
+ if(!isspace((unsigned char) *p)){
+ while((*string++ = *p++) != '\0') /* copy back from there... */
+ ;
+
+ return;
+ }
+}
+
+
+
+/*----------------------------------------------------------------------
+ Remove trailing white space from a string in place
+
+ Args: string -- string to remove space from
+ ----*/
+void
+removing_trailing_white_space(char *string)
+{
+ char *p = NULL;
+
+ if(!string || !*string)
+ return;
+
+ for(; *string; string++) /* remember start of whitespace */
+ p = (!isspace((unsigned char)*string)) ? NULL : (!p) ? string : p;
+
+ if(p) /* if whitespace, blast it */
+ *p = '\0';
+}
+
+
+void
+removing_leading_and_trailing_white_space(char *string)
+{
+ register char *p, *q = NULL;
+
+ if(!string || !*string)
+ return;
+
+ for(p = string; *p; p++) /* find the first non-blank */
+ if(!isspace((unsigned char)*p)){
+ if(p == string){ /* don't have to copy in this case */
+ for(; *string; string++)
+ q = (!isspace((unsigned char)*string)) ? NULL : (!q) ? string : q;
+ }
+ else{
+ for(; (*string = *p++) != '\0'; string++)
+ q = (!isspace((unsigned char)*string)) ? NULL : (!q) ? string : q;
+ }
+
+ if(q)
+ *q = '\0';
+
+ return;
+ }
+
+ if(*string != '\0')
+ *string = '\0';
+}
+
+
+/*----------------------------------------------------------------------
+ Remove one set of double quotes surrounding string in place
+ Returns 1 if quotes were removed
+
+ Args: string -- string to remove quotes from
+ ----*/
+int
+removing_double_quotes(char *string)
+{
+ register char *p;
+ int ret = 0;
+
+ if(string && string[0] == '"' && string[1] != '\0'){
+ p = string + strlen(string) - 1;
+ if(*p == '"'){
+ ret++;
+ *p = '\0';
+ for(p = string; *p; p++)
+ *p = *(p+1);
+ }
+ }
+
+ return(ret);
+}
+
+
+
+/*----------------------------------------------------------------------
+ return a pointer to first non-whitespace char in string
+
+ Args: string -- string to scan
+ ----*/
+char *
+skip_white_space(char *string)
+{
+ while(*string && isspace((unsigned char) *string))
+ string++;
+
+ return(string);
+}
+
+
+
+/*----------------------------------------------------------------------
+ return a pointer to first whitespace char in string
+
+ Args: string -- string to scan
+ ----*/
+char *
+skip_to_white_space(char *string)
+{
+ while(*string && !isspace((unsigned char) *string))
+ string++;
+
+ return(string);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Remove quotes from a string in place
+
+ Args: string -- string to remove quotes from
+ Rreturns: string passed us, but with quotes gone
+ ----*/
+char *
+removing_quotes(char *string)
+{
+ char *p, *q;
+
+ if(*(p = q = string) == '\"'){
+ q++;
+ do
+ if(*q == '\"' || *q == '\\')
+ q++;
+ while((*p++ = *q++) != '\0');
+ }
+
+ return(string);
+}
+
+
+
+/*---------------------------------------------------
+ Remove leading whitespace, trailing whitespace and convert
+ to lowercase
+
+ Args: s, -- The string to clean
+
+ Result: the cleaned string
+ ----*/
+char *
+strclean(char *string)
+{
+ char *s = string, *sc = NULL, *p = NULL;
+
+ for(; *s; s++){ /* single pass */
+ if(!isspace((unsigned char)*s)){
+ p = NULL; /* not start of blanks */
+ if(!sc) /* first non-blank? */
+ sc = string; /* start copying */
+ }
+ else if(!p) /* it's OK if sc == NULL */
+ p = sc; /* start of blanks? */
+
+ if(sc) /* if copying, copy */
+ *sc++ = isupper((unsigned char)(*s))
+ ? (unsigned char)tolower((unsigned char)(*s))
+ : (unsigned char)(*s);
+ }
+
+ if(p) /* if ending blanks */
+ *p = '\0'; /* tie off beginning */
+ else if(!sc) /* never saw a non-blank */
+ *string = '\0'; /* so tie whole thing off */
+
+ return(string);
+}
+
+
+/*
+ * Returns a pointer to a short version of the string.
+ * If src is not longer than wid, pointer points to src.
+ * If longer than wid, a version which is wid long is made in
+ * buf and the pointer points there.
+ *
+ * Wid refers to UTF-8 screen width, not strlen width.
+ *
+ * Args src -- The string to be shortened
+ * buf -- A place to put the short version
+ * wid -- Desired width of shortened string
+ * where -- Where should the dots be in the shortened string. Can be
+ * FrontDots, MidDots, EndDots.
+ *
+ * FrontDots ...stuvwxyz
+ * EndDots abcdefgh...
+ * MidDots abcd...wxyz
+ */
+char *
+short_str(char *src, char *buf, size_t buflen, int wid, WhereDots where)
+{
+ char *ans;
+ unsigned alen, first, second;
+
+ if(wid <= 0){
+ ans = buf;
+ if(buflen > 0)
+ buf[0] = '\0';
+ }
+ else if((alen = utf8_width(src)) <= wid)
+ ans = src;
+ else{
+ ans = buf;
+ if(wid < 5){
+ if(buflen > wid){
+ strncpy(buf, "....", buflen);
+ buf[wid] = '\0';
+ }
+ }
+ else{
+ char *q;
+ unsigned got_width;
+
+ /*
+ * first == length of preellipsis text
+ * second == length of postellipsis text
+ */
+ if(where == FrontDots){
+ first = 0;
+ second = wid - 3;
+ }
+ else if(where == MidDots){
+ first = (wid - 3)/2;
+ second = wid - 3 - first;
+ }
+ else if(where == EndDots){
+ first = wid - 3;
+ second = 0;
+ }
+
+ q = buf;
+ if(first){
+ q += utf8_to_width(q, src, buflen, first, &got_width);
+ if(got_width != first){
+ if(second)
+ second++;
+ else
+ while(got_width < first && buflen-(q-buf) > 0)
+ *q++ = '.';
+ }
+ }
+
+ if(buflen - (q-buf) > 3){
+ strncpy(q, "...", buflen - (q-buf));
+ buf[buflen-1] = '\0';
+ q += strlen(q);
+ }
+
+ if(second){
+ char *p;
+
+ p = utf8_count_back_width(src, src+strlen(src), second, &got_width);
+ if(buflen - (q-buf) > strlen(p)){
+ strncpy(q, p, buflen - (q-buf));
+ buf[buflen-1] = '\0';
+ q += strlen(q);
+ }
+ }
+
+ if(buflen - (q-buf) > 0)
+ *q = '\0';
+
+ buf[buflen-1] = '\0';
+ }
+ }
+
+ return(ans);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Search one string for another
+
+ Args: haystack -- The string to search in, the larger string
+ needle -- The string to search for, the smaller string
+
+ Search for first occurrence of needle in the haystack, and return a pointer
+ into the string haystack when it is found. The text we are dealing with is
+ UTF-8. We'd like the search to be case-independent but we're not sure what
+ that means for UTF-8. We're not even sure what matching means. We're not going
+ to worry about composed characters and canonical forms and anything like that
+ for now. Instead, we'll do the case-independent thing for ascii but exact
+ equality for the rest of the character space.
+ ----*/
+char *
+srchstr(char *haystack, char *needle)
+{
+ char *p, *q;
+
+#define CMPNOCASE(x, y) (((isascii((unsigned char) (x)) && isupper((unsigned char) (x))) \
+ ? tolower((unsigned char) (x)) \
+ : (unsigned char) (x)) \
+ == ((isascii((unsigned char) (y)) && isupper((unsigned char) (y))) \
+ ? tolower((unsigned char) (y)) \
+ : (unsigned char) (y)))
+
+ if(needle && haystack)
+ for(; *haystack; haystack++)
+ for(p = needle, q = haystack; ; p++, q++){
+ if(!*p)
+ return(haystack); /* winner! */
+ else if(!*q)
+ return(NULL); /* len(needle) > len(haystack)! */
+ else if(*p != *q && !CMPNOCASE(*p, *q))
+ break;
+ }
+
+ return(NULL);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Search one string for another, from right
+
+ Args: is -- The string to search in, the larger string
+ ss -- The string to search for, the smaller string
+
+ Search for last occurrence of ss in the is, and return a pointer
+ into the string is when it is found. The search is case indepedent.
+ ----*/
+
+char *
+srchrstr(register char *is, register char *ss)
+{
+ register char *sx, *sy;
+ char *ss_store, *rv;
+ char *begin_is;
+ char temp[251];
+
+ if(is == NULL || ss == NULL)
+ return(NULL);
+
+ if(strlen(ss) > sizeof(temp) - 2)
+ ss_store = (char *)fs_get(strlen(ss) + 1);
+ else
+ ss_store = temp;
+
+ for(sx = ss, sy = ss_store; *sx != '\0' ; sx++, sy++)
+ *sy = isupper((unsigned char)(*sx))
+ ? (unsigned char)tolower((unsigned char)(*sx))
+ : (unsigned char)(*sx);
+ *sy = *sx;
+
+ begin_is = is;
+ is = is + strlen(is) - strlen(ss_store);
+ rv = NULL;
+ while(is >= begin_is){
+ for(sx = is, sy = ss_store;
+ ((*sx == *sy)
+ || ((isupper((unsigned char)(*sx))
+ ? (unsigned char)tolower((unsigned char)(*sx))
+ : (unsigned char)(*sx)) == (unsigned char)(*sy))) && *sy;
+ sx++, sy++)
+ ;
+
+ if(!*sy){
+ rv = is;
+ break;
+ }
+
+ is--;
+ }
+
+ if(ss_store != temp)
+ fs_give((void **)&ss_store);
+
+ return(rv);
+}
+
+
+
+/*----------------------------------------------------------------------
+ A replacement for strchr or index ...
+
+ Returns a pointer to the first occurrence of the character
+ 'ch' in the specified string or NULL if it doesn't occur
+
+ ....so we don't have to worry if it's there or not. We bring our own.
+If we really care about efficiency and think the local one is more
+efficient the local one can be used, but most of the things that take
+a long time are in the c-client and not in pine.
+ ----*/
+char *
+strindex(char *buffer, int ch)
+{
+ do
+ if(*buffer == ch)
+ return(buffer);
+ while (*buffer++ != '\0');
+
+ return(NULL);
+}
+
+
+/* Returns a pointer to the last occurrence of the character
+ * 'ch' in the specified string or NULL if it doesn't occur
+ */
+char *
+strrindex(char *buffer, int ch)
+{
+ char *address = NULL;
+
+ do
+ if(*buffer == ch)
+ address = buffer;
+ while (*buffer++ != '\0');
+ return(address);
+}
+
+
+/*----------------------------------------------------------------------
+ copy at most n chars of the UTF-8 source string onto the destination string
+ returning pointer to start of destination and converting any undisplayable
+ characters to harmless character equivalents.
+ ----*/
+char *
+iutf8ncpy(char *d, char *s, int n)
+{
+ register int i;
+
+ if(!d || !s)
+ return(NULL);
+
+ /*
+ * BUG: this needs to get improved to actually count the
+ * character "cell" positions. For now, at least don't break
+ * a multi-byte character.
+ */
+ for(i = 0; i < n && (d[i] = *s) != '\0'; s++, i++)
+ if((unsigned char)(*s) < 0x80 && FILTER_THIS(*s)){
+ if(i+1 < n){
+ d[i] = '^';
+ d[++i] = (*s == 0x7f) ? '?' : *s + '@';
+ }
+ else{
+ d[i] = '\0';
+ break; /* don't fit */
+ }
+ }
+ else if(*s & 0x80){
+ /* multi-byte character */
+ if((*s & 0xE0) == 0xC0){
+ if(i+1 < n){
+ if(((d[++i] = *++s) & 0xC0) != 0x80){
+ d[i] = '\0';
+ break; /* bogus utf-8 */
+ }
+ }
+ else{
+ d[i] = '\0';
+ break; /* too long */
+ }
+ }
+ else if((*s & 0xF0) == 0xE0){
+ if(i+2 < n){
+ if(!(((d[++i] = *++s) & 0xC0) == 0x80
+ && ((d[++i] = *++s) & 0xC0) == 0x80)){
+ d[i] = '\0';
+ break; /* bogus utf-8 */
+ }
+ }
+ else{
+ d[i] = '\0';
+ break; /* won't fit */
+ }
+ }
+ else if((*s & 0xF8) == 0xF0){
+ if(i+3 < n){
+ if(!(((d[++i] = *++s) & 0xC0) == 0x80
+ && ((d[++i] = *++s) & 0xC0) == 0x80
+ && ((d[++i] = *++s) & 0xC0) == 0x80)){
+ d[i] = '\0';
+ break; /* bogus utf-8 */
+ }
+ }
+ else{
+ d[i] = '\0';
+ break; /* won't fit */
+ }
+ }
+ else if((*s & 0xFC) == 0xF8){
+ if(i+4 < n){
+ if(!(((d[++i] = *++s) & 0xC0) == 0x80
+ && ((d[++i] = *++s) & 0xC0) == 0x80
+ && ((d[++i] = *++s) & 0xC0) == 0x80
+ && ((d[++i] = *++s) & 0xC0) == 0x80)){
+ d[i] = '\0';
+ break; /* bogus utf-8 */
+ }
+ }
+ else{
+ d[i] = '\0';
+ break; /* won't fit */
+ }
+ }
+ else if((*s & 0xFE) == 0xFC){
+ if(i+5 < n){
+ if(!(((d[++i] = *++s) & 0xC0) == 0x80
+ && ((d[++i] = *++s) & 0xC0) == 0x80
+ && ((d[++i] = *++s) & 0xC0) == 0x80
+ && ((d[++i] = *++s) & 0xC0) == 0x80
+ && ((d[++i] = *++s) & 0xC0) == 0x80)){
+ d[i] = '\0';
+ break; /* bogus utf-8 */
+ }
+ }
+ else{
+ d[i] = '\0';
+ break; /* won't fit */
+ }
+ }
+ else{
+ d[i] = '\0';
+ break; /* don't fit */
+ }
+ }
+
+ return(d);
+}
+
+
+/*----------------------------------------------------------------------
+ copy at most n chars of the source string onto the destination string
+ returning pointer to start of destination and converting any undisplayable
+ characters to harmless character equivalents.
+ ----*/
+char *
+istrncpy(char *d, char *s, int n)
+{
+ char *rv = d;
+ unsigned char c;
+
+ if(!d || !s)
+ return(NULL);
+
+ do
+ if(*s && (unsigned char)(*s) < 0x80 && FILTER_THIS(*s)
+ && !(*(s+1) && *s == ESCAPE && match_escapes(s+1))){
+ if(n-- > 0){
+ c = (unsigned char) *s;
+ *d++ = c >= 0x80 ? '~' : '^';
+
+ if(n-- > 0){
+ s++;
+ *d = (c == 0x7f) ? '?' : (c & 0x1f) + '@';
+ }
+ }
+ }
+ else{
+ if(n-- > 0)
+ *d = *s++;
+ }
+ while(n > 0 && *d++);
+
+ return(rv);
+}
+
+
+char *xdays[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL};
+
+char *
+month_abbrev(int month_num)
+{
+ static char *xmonths[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
+ if(month_num < 1 || month_num > 12)
+ return("xxx");
+ return(xmonths[month_num - 1]);
+}
+
+char *
+month_abbrev_locale(int month_num)
+{
+#ifndef DISABLE_LOCALE_DATES
+ if(F_OFF(F_DISABLE_INDEX_LOCALE_DATES, ps_global)){
+ if(month_num < 1 || month_num > 12)
+ return("xxx");
+ else{
+ static char buf[20];
+ struct tm tm;
+
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_year = 107;
+ tm.tm_mon = month_num-1;
+ our_strftime(buf, sizeof(buf), "%b", &tm);
+
+ /*
+ * If it is all digits, then use the English
+ * words instead. Look for
+ * "<digit>"
+ * "<digit><digit>" or
+ * "<space><digit>"
+ */
+ if((buf[0] && !(buf[0] & 0x80)
+ && isdigit((unsigned char)buf[0]) && !buf[1])
+ ||
+ (buf[0] && !(buf[0] & 0x80)
+ && (isdigit((unsigned char)buf[0]) || buf[0] == ' ')
+ && buf[1] && !(buf[1] & 0x80)
+ && isdigit((unsigned char)buf[1]) && !buf[2]))
+ return(month_abbrev(month_num));
+
+ /*
+ * If buf[0] is a digit then assume that there should be a leading
+ * space if it leads off with a single digit.
+ */
+ if(buf[0] && !(buf[0] & 0x80) && isdigit((unsigned char) buf[0])
+ && !(buf[1] && !(buf[1] & 0x80) && isdigit((unsigned char) buf[1]))){
+ char *p;
+
+ /* insert space at start of buf */
+ p = buf+strlen(buf) + 1;
+ if(p > buf + sizeof(buf) - 1)
+ p = buf + sizeof(buf) - 1;
+
+ for(; p > buf; p--)
+ *p = *(p-1);
+
+ buf[0] = ' ';
+ }
+
+ return(buf);
+ }
+ }
+ else
+ return(month_abbrev(month_num));
+#else /* DISABLE_LOCALE_DATES */
+ return(month_abbrev(month_num));
+#endif /* DISABLE_LOCALE_DATES */
+}
+
+char *
+month_name(int month_num)
+{
+ static char *months[] = {"January", "February", "March", "April",
+ "May", "June", "July", "August", "September", "October",
+ "November", "December", NULL};
+ if(month_num < 1 || month_num > 12)
+ return("");
+ return(months[month_num - 1]);
+}
+
+char *
+month_name_locale(int month_num)
+{
+#ifndef DISABLE_LOCALE_DATES
+ if(F_OFF(F_DISABLE_INDEX_LOCALE_DATES, ps_global)){
+ if(month_num < 1 || month_num > 12)
+ return("");
+ else{
+ static char buf[20];
+ struct tm tm;
+
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_year = 107;
+ tm.tm_mon = month_num-1;
+ our_strftime(buf, sizeof(buf), "%B", &tm);
+ return(buf);
+ }
+ }
+ else
+ return(month_name(month_num));
+#else /* DISABLE_LOCALE_DATES */
+ return(month_name(month_num));
+#endif /* DISABLE_LOCALE_DATES */
+}
+
+
+char *
+day_abbrev(int day_of_week)
+{
+ if(day_of_week < 0 || day_of_week > 6)
+ return("???");
+ return(xdays[day_of_week]);
+}
+
+char *
+day_abbrev_locale(int day_of_week)
+{
+#ifndef DISABLE_LOCALE_DATES
+ if(F_OFF(F_DISABLE_INDEX_LOCALE_DATES, ps_global)){
+ if(day_of_week < 0 || day_of_week > 6)
+ return("???");
+ else{
+ static char buf[20];
+ struct tm tm;
+
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_wday = day_of_week;
+ our_strftime(buf, sizeof(buf), "%a", &tm);
+ return(buf);
+ }
+ }
+ else
+ return(day_abbrev(day_of_week));
+#else /* DISABLE_LOCALE_DATES */
+ return(day_abbrev(day_of_week));
+#endif /* DISABLE_LOCALE_DATES */
+}
+
+char *
+day_name(int day_of_week)
+{
+ static char *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
+ "Thursday", "Friday", "Saturday", NULL};
+ if(day_of_week < 0 || day_of_week > 6)
+ return("");
+ return(days[day_of_week]);
+}
+
+char *
+day_name_locale(int day_of_week)
+{
+#ifndef DISABLE_LOCALE_DATES
+ if(F_OFF(F_DISABLE_INDEX_LOCALE_DATES, ps_global)){
+ if(day_of_week < 0 || day_of_week > 6)
+ return("");
+ else{
+ static char buf[20];
+ struct tm tm;
+
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_wday = day_of_week;
+ our_strftime(buf, sizeof(buf), "%A", &tm);
+ return(buf);
+ }
+ }
+ else
+ return(day_name(day_of_week));
+#else /* DISABLE_LOCALE_DATES */
+ return(day_name(day_of_week));
+#endif /* DISABLE_LOCALE_DATES */
+}
+
+
+size_t
+our_strftime(char *dst, size_t dst_size, char *format, struct tm *tm)
+{
+#ifdef _WINDOWS
+ LPTSTR lptbuf, lptformat;
+ char *u;
+
+ lptbuf = (LPTSTR) fs_get(dst_size * sizeof(TCHAR));
+ lptbuf[0] = '\0';
+ lptformat = utf8_to_lptstr((LPSTR) format);
+
+ _tcsftime(lptbuf, dst_size, lptformat, tm);
+ u = lptstr_to_utf8(lptbuf);
+ if(u){
+ strncpy(dst, u, dst_size);
+ dst[dst_size-1] = '\0';
+ fs_give((void **) &u);
+ }
+
+ return(strlen(dst));
+#else
+ return(strftime(dst, dst_size, format, tm));
+#endif
+}
+
+
+/*----------------------------------------------------------------------
+ Return month number of month named in string
+
+ Args: s -- string with 3 letter month abbreviation of form mmm-yyyy
+
+ Result: Returns month number with January, year 1900, 2000... being 0;
+ -1 if no month/year is matched
+ ----*/
+int
+month_num(char *s)
+{
+ int month = -1, year;
+ int i;
+
+ if(F_ON(F_PRUNE_USES_ISO,ps_global)){
+ char save, *p;
+ char digmon[3];
+
+ if(s && strlen(s) > 4 && s[4] == '-'){
+ save = s[4];
+ s[4] = '\0';
+ year = atoi(s);
+ s[4] = save;
+ if(year == 0)
+ return(-1);
+
+ p = s + 5;
+ for(i = 0; i < 12; i++){
+ digmon[0] = ((i+1) < 10) ? '0' : '1';
+ digmon[1] = '0' + (i+1) % 10;
+ digmon[2] = '\0';
+ if(strcmp(digmon, p) == 0)
+ break;
+ }
+
+ if(i == 12)
+ return(-1);
+
+ month = year * 12 + i;
+ }
+ }
+ else{
+ if(s && strlen(s) > 3 && s[3] == '-'){
+ for(i = 0; i < 12; i++){
+ if(struncmp(month_abbrev(i+1), s, 3) == 0)
+ break;
+ }
+
+ if(i == 12)
+ return(-1);
+
+ year = atoi(s + 4);
+ if(year == 0)
+ return(-1);
+
+ month = year * 12 + i;
+ }
+ }
+
+ return(month);
+}
+
+
+/*
+ * Structure containing all knowledge of symbolic time zones.
+ * To add support for a given time zone, add it here, but make sure
+ * the zone name is in upper case.
+ */
+static struct {
+ char *zone;
+ short len,
+ hour_offset,
+ min_offset;
+} known_zones[] = {
+ {"PST", 3, -8, 0}, /* Pacific Standard */
+ {"PDT", 3, -7, 0}, /* Pacific Daylight */
+ {"MST", 3, -7, 0}, /* Mountain Standard */
+ {"MDT", 3, -6, 0}, /* Mountain Daylight */
+ {"CST", 3, -6, 0}, /* Central Standard */
+ {"CDT", 3, -5, 0}, /* Central Daylight */
+ {"EST", 3, -5, 0}, /* Eastern Standard */
+ {"EDT", 3, -4, 0}, /* Eastern Daylight */
+ {"JST", 3, 9, 0}, /* Japan Standard */
+ {"GMT", 3, 0, 0}, /* Universal Time */
+ {"UT", 2, 0, 0}, /* Universal Time */
+#ifdef IST_MEANS_ISREAL
+ {"IST", 3, 2, 0}, /* Israel Standard */
+#else
+#ifdef IST_MEANS_INDIA
+ {"IST", 3, 5, 30}, /* India Standard */
+#endif
+#endif
+ {NULL, 0, 0},
+};
+
+/*----------------------------------------------------------------------
+ Parse date in or near RFC-822 format into the date structure
+
+Args: given_date -- The input string to parse
+ d -- Pointer to a struct date to place the result in
+
+Returns nothing
+
+The following date fomrats are accepted:
+ WKDAY DD MM YY HH:MM:SS ZZ
+ DD MM YY HH:MM:SS ZZ
+ WKDAY DD MM HH:MM:SS YY ZZ
+ DD MM HH:MM:SS YY ZZ
+ DD MM WKDAY HH:MM:SS YY ZZ
+ DD MM WKDAY YY MM HH:MM:SS ZZ
+
+All leading, intervening and trailing spaces tabs and commas are ignored.
+The prefered formats are the first or second ones. If a field is unparsable
+it's value is left as -1.
+
+ ----*/
+void
+parse_date(char *given_date, struct date *d)
+{
+ char *p, **i, *q;
+ int month, n;
+
+ d->sec = -1;
+ d->minute= -1;
+ d->hour = -1;
+ d->day = -1;
+ d->month = -1;
+ d->year = -1;
+ d->wkday = -1;
+ d->hours_off_gmt = -1;
+ d->min_off_gmt = -1;
+
+ if(given_date == NULL)
+ return;
+
+ p = given_date;
+ while(*p && isspace((unsigned char)*p))
+ p++;
+
+ /* Start with weekday? */
+ if((q=strchr(p, ',')) != NULL){
+
+ if(q - p == 3){
+ *q = '\0';
+ for(i = xdays; *i != NULL; i++)
+ if(strucmp(p, *i) == 0) /* Match first 3 letters */
+ break;
+
+ *q = ',';
+
+ if(*i != NULL) {
+ /* Started with week day */
+ d->wkday = i - xdays;
+ }
+ }
+
+ p = q+1;
+ while(*p && isspace((unsigned char)*p))
+ p++;
+ }
+ else if((q=strchr(p, ' ')) != NULL && q - p == 3){
+ *q = '\0';
+ for(i = xdays; *i != NULL; i++)
+ if(strucmp(p, *i) == 0) /* Match first 3 letters */
+ break;
+
+ *q = ' ';
+
+ if(*i != NULL) {
+ /* Started with week day */
+ d->wkday = i - xdays;
+ p = q+1;
+ while(*p && isspace((unsigned char)*p))
+ p++;
+ }
+ }
+
+ if(isdigit((unsigned char)*p)) {
+ d->day = atoi(p);
+ while(*p && isdigit((unsigned char)*p))
+ p++;
+ while(*p && (*p == '-' || *p == ',' || isspace((unsigned char)*p)))
+ p++;
+ }
+ for(month = 1; month <= 12; month++)
+ if(struncmp(p, month_abbrev(month), 3) == 0)
+ break;
+ if(month < 13) {
+ d->month = month;
+
+ }
+ /* Move over month, (or whatever is there) */
+ while(*p && !isspace((unsigned char)*p) && *p != ',' && *p != '-')
+ p++;
+ while(*p && (isspace((unsigned char)*p) || *p == ',' || *p == '-'))
+ p++;
+
+ /* Check again for day */
+ if(isdigit((unsigned char)*p) && d->day == -1) {
+ d->day = atoi(p);
+ while(*p && isdigit((unsigned char)*p))
+ p++;
+ while(*p && (*p == '-' || *p == ',' || isspace((unsigned char)*p)))
+ p++;
+ }
+
+ /*-- Check for time --*/
+ for(q = p; *q && isdigit((unsigned char)*q); q++);
+ if(*q == ':') {
+ /* It's the time (out of place) */
+ d->hour = atoi(p);
+ while(*p && *p != ':' && !isspace((unsigned char)*p))
+ p++;
+ if(*p == ':') {
+ p++;
+ d->minute = atoi(p);
+ while(*p && *p != ':' && !isspace((unsigned char)*p))
+ p++;
+ if(*p == ':') {
+ d->sec = atoi(p);
+ while(*p && !isspace((unsigned char)*p))
+ p++;
+ }
+ }
+ while(*p && isspace((unsigned char)*p))
+ p++;
+ }
+
+
+ /* Get the year 0-49 is 2000-2049; 50-100 is 1950-1999 and
+ 101-9999 is 101-9999 */
+ if(isdigit((unsigned char)*p)) {
+ d->year = atoi(p);
+ if(d->year < 50)
+ d->year += 2000;
+ else if(d->year < 100)
+ d->year += 1900;
+ while(*p && isdigit((unsigned char)*p))
+ p++;
+ while(*p && (*p == '-' || *p == ',' || isspace((unsigned char)*p)))
+ p++;
+ } else {
+ /* Something wierd, skip it and try to resynch */
+ while(*p && !isspace((unsigned char)*p) && *p != ',' && *p != '-')
+ p++;
+ while(*p && (isspace((unsigned char)*p) || *p == ',' || *p == '-'))
+ p++;
+ }
+
+ /*-- Now get hours minutes, seconds and ignore tenths --*/
+ for(q = p; *q && isdigit((unsigned char)*q); q++);
+ if(*q == ':' && d->hour == -1) {
+ d->hour = atoi(p);
+ while(*p && *p != ':' && !isspace((unsigned char)*p))
+ p++;
+ if(*p == ':') {
+ p++;
+ d->minute = atoi(p);
+ while(*p && *p != ':' && !isspace((unsigned char)*p))
+ p++;
+ if(*p == ':') {
+ p++;
+ d->sec = atoi(p);
+ while(*p && !isspace((unsigned char)*p))
+ p++;
+ }
+ }
+ }
+ while(*p && isspace((unsigned char)*p))
+ p++;
+
+
+ /*-- The time zone --*/
+ d->hours_off_gmt = 0;
+ d->min_off_gmt = 0;
+ if(*p) {
+ if((*p == '+' || *p == '-')
+ && isdigit((unsigned char)p[1])
+ && isdigit((unsigned char)p[2])
+ && isdigit((unsigned char)p[3])
+ && isdigit((unsigned char)p[4])
+ && !isdigit((unsigned char)p[5])) {
+ char tmp[3];
+ d->min_off_gmt = d->hours_off_gmt = (*p == '+' ? 1 : -1);
+ p++;
+ tmp[0] = *p++;
+ tmp[1] = *p++;
+ tmp[2] = '\0';
+ d->hours_off_gmt *= atoi(tmp);
+ tmp[0] = *p++;
+ tmp[1] = *p++;
+ tmp[2] = '\0';
+ d->min_off_gmt *= atoi(tmp);
+ } else {
+ for(n = 0; known_zones[n].zone; n++)
+ if(struncmp(p, known_zones[n].zone, known_zones[n].len) == 0){
+ d->hours_off_gmt = (int) known_zones[n].hour_offset;
+ d->min_off_gmt = (int) known_zones[n].min_offset;
+ break;
+ }
+ }
+ }
+
+ if(d->wkday == -1){
+ MESSAGECACHE elt;
+ struct tm *tm;
+ time_t t;
+
+ /*
+ * Not sure why we aren't just using this from the gitgo, but
+ * since not sure will just use it to repair wkday.
+ */
+ if(mail_parse_date(&elt, (unsigned char *) given_date)){
+ t = mail_longdate(&elt);
+ tm = localtime(&t);
+
+ if(tm)
+ d->wkday = tm->tm_wday;
+ }
+ }
+}
+
+
+char *
+convert_date_to_local(char *date)
+{
+ struct tm *tm;
+ time_t ltime;
+ static char datebuf[26];
+
+ ltime = date_to_local_time_t(date);
+ if(ltime == (time_t) -1)
+ return(date);
+
+ tm = localtime(&ltime);
+
+ if(tm == NULL)
+ return(date);
+
+ snprintf(datebuf, sizeof(datebuf), "%.3s, %d %.3s %d %02d:%02d:%02d",
+ day_abbrev(tm->tm_wday), tm->tm_mday, month_abbrev(tm->tm_mon+1),
+ tm->tm_year+1900, tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ return(datebuf);
+}
+
+
+time_t
+date_to_local_time_t(char *date)
+{
+ time_t ourtime;
+ struct tm theirtime;
+ struct date d;
+ static int zone = 1000000; /* initialize timezone offset */
+ static int dst;
+
+ if(zone == 1000000){
+ int julian;
+ struct tm *tm;
+ time_t now;
+
+ zone = 0;
+ /* find difference between gmtime and localtime, from c-client do_date */
+ now = time((time_t *) 0);
+ if(now != (time_t) -1){
+ tm = gmtime(&now);
+ if(tm != NULL){
+ zone = tm->tm_hour * 60 + tm->tm_min; /* minutes */
+ julian = tm->tm_yday;
+
+ tm = localtime(&now);
+ dst = tm->tm_isdst; /* for converting back to our time */
+
+ zone = tm->tm_hour * 60 + tm->tm_min - zone;
+ if((julian = tm->tm_yday - julian) != 0)
+ zone += ((julian < 0) == (abs(julian) == 1)) ? -24*60 : 24*60;
+
+ zone *= 60; /* change to seconds */
+ }
+ }
+ }
+
+ parse_date(date, &d);
+
+ /* put d into struct tm so we can use mktime */
+ memset(&theirtime, 0, sizeof(theirtime));
+ theirtime.tm_year = d.year - 1900;
+ theirtime.tm_mon = d.month - 1;
+ theirtime.tm_mday = d.day;
+ theirtime.tm_hour = d.hour - d.hours_off_gmt;
+ theirtime.tm_min = d.minute - d.min_off_gmt;
+ theirtime.tm_sec = d.sec;
+
+ theirtime.tm_isdst = dst;
+
+ ourtime = mktime(&theirtime); /* still theirtime, actually */
+
+ /* convert to the time we want to show */
+ if(ourtime != (time_t) -1)
+ ourtime += zone;
+
+ return(ourtime);
+}
+
+
+/*----------------------------------------------------------------------
+ Create a little string of blanks of the specified length.
+ Max n is MAX_SCREEN_COLS. Can use up to e repeat_char results at once.
+ ----*/
+char *
+repeat_char(int n, int c)
+{
+ static char bb[3][MAX_SCREEN_COLS+1];
+ static int whichbb = 0;
+ char *b;
+
+ whichbb = (whichbb + 1) % 3;
+ b = bb[whichbb];
+
+ if(n > sizeof(bb[0]))
+ n = sizeof(bb[0]) - 1;
+
+ bb[whichbb][n--] = '\0';
+ while(n >= 0)
+ bb[whichbb][n--] = c;
+
+ return(bb[whichbb]);
+}
+
+
+/*----------------------------------------------------------------------
+ Format number as amount of bytes, appending Kb, Mb, Gb, bytes
+
+ Args: bytes -- number of bytes to format
+
+ Returns pointer to static string. The numbers are divided to produce a
+nice string with precision of about 2-4 digits
+ ----*/
+char *
+byte_string(long int bytes)
+{
+ char *a, aa[5];
+ char *abbrevs = "GMK";
+ long i, ones, tenths;
+ static char string[10];
+
+ ones = 0L;
+ tenths = 0L;
+
+ if(bytes == 0L){
+ strncpy(string, "0 bytes", sizeof(string));
+ string[sizeof(string)-1] = '\0';
+ }
+ else {
+ for(a = abbrevs, i = 1000000000; i >= 1; i /= 1000, a++) {
+ if(bytes > i) {
+ ones = bytes/i;
+ if(ones < 10L && i > 10L)
+ tenths = (bytes - (ones * i)) / (i / 10L);
+ break;
+ }
+ }
+
+ aa[0] = *a; aa[1] = '\0';
+
+ if(tenths == 0)
+ snprintf(string, sizeof(string), "%ld%s%s", ones, aa, *a ? "B" : "bytes");
+ else
+ snprintf(string, sizeof(string), "%ld.%ld%s%s", ones, tenths, aa, *a ? "B" : "bytes");
+ }
+
+ return(string);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Print a string corresponding to the number given:
+ 1st, 2nd, 3rd, 105th, 92342nd....
+ ----*/
+
+char *
+enth_string(int i)
+{
+ static char enth[10];
+
+ enth[0] = '\0';
+
+ switch (i % 10) {
+
+ case 1:
+ if( (i % 100 ) == 11)
+ snprintf(enth, sizeof(enth),"%dth", i);
+ else
+ snprintf(enth, sizeof(enth),"%dst", i);
+ break;
+
+ case 2:
+ if ((i % 100) == 12)
+ snprintf(enth, sizeof(enth), "%dth",i);
+ else
+ snprintf(enth, sizeof(enth), "%dnd",i);
+ break;
+
+ case 3:
+ if(( i % 100) == 13)
+ snprintf(enth, sizeof(enth), "%dth",i);
+ else
+ snprintf(enth, sizeof(enth), "%drd",i);
+ break;
+
+ default:
+ snprintf(enth, sizeof(enth),"%dth",i);
+ break;
+ }
+ return(enth);
+}
+
+
+/*
+ * Inserts newlines for folding at whitespace.
+ *
+ * Args src -- The source text.
+ * width -- Approximately where the fold should happen.
+ * maxwidth -- Maximum width we want to fold at.
+ * first_indent -- String to use as indent on first line.
+ * indent -- String to use as indent for subsequent folded lines.
+ * flags -- FLD_CRLF End of line is \r\n instead of \n.
+ * FLD_PWS PreserveWhiteSpace when folding. This is
+ * for vcard folding where CRLF SPACE is
+ * removed when unfolding, so we need to
+ * leave the space in. With rfc2822 unfolding
+ * only the CRLF is removed when unfolding.
+ *
+ * Returns An allocated string which caller should free.
+ */
+char *
+fold(char *src, int width, int maxwidth, char *first_indent, char *indent, unsigned int flags)
+{
+ char *next_piece, *res, *p;
+ int i, len = 0, starting_point, winner, eol, this_width;
+ int indent1 = 0, /* width of first_indent */
+ indent2 = 0, /* width of indent */
+ nbindent2 = 0, /* number of bytes in indent */
+ nb = 0; /* number of bytes needed */
+ int cr, preserve_ws;
+ char save_char;
+ char *endptr = NULL;
+ unsigned shorter, longer;
+ unsigned got_width;
+
+ cr = (flags & FLD_CRLF);
+ preserve_ws = (flags & FLD_PWS);
+
+ if(indent){
+ indent2 = (int) utf8_width(indent);
+ nbindent2 = strlen(indent);
+ }
+
+ if(first_indent){
+ indent1 = (int) utf8_width(first_indent);
+ nb = strlen(first_indent);
+ }
+
+ len = indent1;
+ next_piece = src;
+ eol = cr ? 2 : 1;
+ if(!src || !*src)
+ nb += eol;
+
+ /*
+ * We can't tell how much space is going to be needed without actually
+ * passing through the data to see.
+ */
+ while(next_piece && *next_piece){
+ if(next_piece != src && indent2){
+ len += indent2;
+ nb += nbindent2;
+ }
+
+ this_width = (int) utf8_width(next_piece);
+ if(this_width + len <= width){
+ nb += (strlen(next_piece) + eol);
+ break;
+ }
+ else{ /* fold it */
+ starting_point = width - len; /* space left on this line */
+ /* find a good folding spot */
+ winner = -1;
+ for(i = 0;
+ winner == -1 && (starting_point - i > 5 || i < maxwidth - width);
+ i++){
+
+ if((shorter=starting_point-i) > 5){
+ endptr = utf8_count_forw_width(next_piece, shorter, &got_width);
+ if(endptr && got_width == shorter && isspace((unsigned char) *endptr))
+ winner = (int) shorter;
+ }
+
+ if(winner == -1
+ && (longer=starting_point+i) && i < maxwidth - width){
+ endptr = utf8_count_forw_width(next_piece, longer, &got_width);
+ if(endptr && got_width == longer && isspace((unsigned char) *endptr))
+ winner = (int) longer;
+ }
+ }
+
+ if(winner == -1){ /* if no good folding spot, fold at width */
+ winner = starting_point;
+ endptr = NULL;
+ }
+
+ if(endptr == NULL){
+ endptr = utf8_count_forw_width(next_piece, (unsigned) winner, &got_width);
+ winner = (int) got_width;
+ }
+
+ nb += ((endptr - next_piece) + eol);
+ next_piece = endptr;
+ if(!preserve_ws && isspace((unsigned char) next_piece[0]))
+ next_piece++;
+ }
+
+ len = 0;
+ }
+
+ res = (char *) fs_get((nb+1) * sizeof(char));
+ p = res;
+ sstrncpy(&p, first_indent, nb+1-(p-res));
+ len = indent1;
+ next_piece = src;
+
+ while(next_piece && *next_piece){
+ if(next_piece != src && indent2){
+ sstrncpy(&p, indent, nb+1-(p-res));
+ len += indent2;
+ }
+
+ this_width = (int) utf8_width(next_piece);
+ if(this_width + len <= width){
+ sstrncpy(&p, next_piece, nb+1-(p-res));
+ if(cr && p-res < nb+1)
+ *p++ = '\r';
+
+ if(p-res < nb+1)
+ *p++ = '\n';
+
+ break;
+ }
+ else{ /* fold it */
+ starting_point = width - len; /* space left on this line */
+ /* find a good folding spot */
+ winner = -1;
+ for(i = 0;
+ winner == -1 && (starting_point - i > 5 || i < maxwidth - width);
+ i++){
+
+ if((shorter=starting_point-i) > 5){
+ endptr = utf8_count_forw_width(next_piece, shorter, &got_width);
+ if(endptr && got_width == shorter && isspace((unsigned char) *endptr))
+ winner = (int) shorter;
+ }
+
+ if(winner == -1
+ && (longer=starting_point+i) && i < maxwidth - width){
+ endptr = utf8_count_forw_width(next_piece, longer, &got_width);
+ if(endptr && got_width == longer && isspace((unsigned char) *endptr))
+ winner = (int) longer;
+ }
+ }
+
+ if(winner == -1){ /* if no good folding spot, fold at width */
+ winner = starting_point;
+ endptr = NULL;
+ }
+
+ if(endptr == NULL){
+ endptr = utf8_count_forw_width(next_piece, (unsigned) winner, &got_width);
+ winner = (int) got_width;
+ }
+
+ if(endptr){
+ save_char = *endptr;
+ *endptr = '\0';
+ sstrncpy(&p, next_piece, nb+1-(p-res));
+ *endptr = save_char;
+ next_piece = endptr;
+ }
+
+ if(cr && p-res < nb+1)
+ *p++ = '\r';
+
+ if(p-res < nb+1)
+ *p++ = '\n';
+
+ if(!preserve_ws && isspace((unsigned char) next_piece[0]))
+ next_piece++;
+ }
+
+ len = 0;
+ }
+
+ if(!src || !*src){
+ if(cr && p-res < nb+1)
+ *p++ = '\r';
+
+ if(p-res < nb+1)
+ *p++ = '\n';
+ }
+
+ if(p-res < nb+1)
+ *p = '\0';
+
+ res[nb] = '\0';
+
+ return(res);
+}
+
+
+/*
+ * strsquish - fancifies a string into the given buffer if it's too
+ * long to fit in the given width
+ */
+char *
+strsquish(char *buf, size_t buflen, char *src, int width)
+{
+ /*
+ * Replace strsquish() with calls to short_str().
+ */
+ if(width > 14)
+ return(short_str(src, buf, buflen, width, MidDots));
+ else
+ return(short_str(src, buf, buflen, width, FrontDots));
+}
+
+
+char *
+long2string(long int l)
+{
+ static char string[20];
+
+ snprintf(string, sizeof(string), "%ld", l);
+ return(string);
+}
+
+
+char *
+ulong2string(unsigned long int l)
+{
+ static char string[20];
+
+ snprintf(string, sizeof(string), "%lu", l);
+ return(string);
+}
+
+
+char *
+int2string(int i)
+{
+ static char string[20];
+
+ snprintf(string, sizeof(string), "%d", i);
+ return(string);
+}
+
+
+/*
+ * strtoval - convert the given string to a positive integer.
+ */
+char *
+strtoval(char *s, int *val, int minmum, int maxmum, int otherok, char *errbuf,
+ size_t errbuflen, char *varname)
+{
+ int i = 0, neg = 1;
+ char *p = s, *errstr = NULL;
+
+ removing_leading_and_trailing_white_space(p);
+ for(; *p; p++)
+ if(isdigit((unsigned char) *p)){
+ i = (i * 10) + (*p - '0');
+ }
+ else if(*p == '-' && i == 0){
+ neg = -1;
+ }
+ else{
+ snprintf(errstr = errbuf, errbuflen,
+ "Non-numeric value ('%c' in \"%.8s\") in %s. Using \"%d\"",
+ *p, s, varname, *val);
+ return(errbuf);
+ }
+
+ i *= neg;
+
+ /* range describes acceptable values */
+ if(maxmum > minmum && (i < minmum || i > maxmum) && i != otherok)
+ snprintf(errstr = errbuf, errbuflen,
+ "%s of %d not supported (M%s %d). Using \"%d\"",
+ varname, i, (i > maxmum) ? "ax" : "in",
+ (i > maxmum) ? maxmum : minmum, *val);
+ /* range describes unacceptable values */
+ else if(minmum > maxmum && !(i < maxmum || i > minmum))
+ snprintf(errstr = errbuf, errbuflen, "%s of %d not supported. Using \"%d\"",
+ varname, i, *val);
+ else
+ *val = i;
+
+ return(errstr);
+}
+
+
+/*
+ * strtolval - convert the given string to a positive _long_ integer.
+ */
+char *
+strtolval(char *s, long int *val, long int minmum, long int maxmum, long int otherok,
+ char *errbuf, size_t errbuflen, char *varname)
+{
+ long i = 0, neg = 1L;
+ char *p = s, *errstr = NULL;
+
+ removing_leading_and_trailing_white_space(p);
+ for(; *p; p++)
+ if(isdigit((unsigned char) *p)){
+ i = (i * 10L) + (*p - '0');
+ }
+ else if(*p == '-' && i == 0L){
+ neg = -1L;
+ }
+ else{
+ snprintf(errstr = errbuf, errbuflen,
+ "Non-numeric value ('%c' in \"%.8s\") in %s. Using \"%ld\"",
+ *p, s, varname, *val);
+ return(errbuf);
+ }
+
+ i *= neg;
+
+ /* range describes acceptable values */
+ if(maxmum > minmum && (i < minmum || i > maxmum) && i != otherok)
+ snprintf(errstr = errbuf, errbuflen,
+ "%s of %ld not supported (M%s %ld). Using \"%ld\"",
+ varname, i, (i > maxmum) ? "ax" : "in",
+ (i > maxmum) ? maxmum : minmum, *val);
+ /* range describes unacceptable values */
+ else if(minmum > maxmum && !(i < maxmum || i > minmum))
+ snprintf(errstr = errbuf, errbuflen, "%s of %ld not supported. Using \"%ld\"",
+ varname, i, *val);
+ else
+ *val = i;
+
+ return(errstr);
+}
+
+
+/*
+ * Function to parse the given string into two space-delimited fields
+ * Quotes may be used to surround labels or values with spaces in them.
+ * Backslash negates the special meaning of a quote.
+ * Unescaping of backslashes only happens if the pair member is quoted,
+ * this provides for backwards compatibility.
+ *
+ * Args -- string -- the source string
+ * label -- the first half of the string, a return value
+ * value -- the last half of the string, a return value
+ * firstws -- if set, the halves are delimited by the first unquoted
+ * whitespace, else by the last unquoted whitespace
+ * strip_internal_label_quotes -- unescaped quotes in the middle of the label
+ * are removed. This is useful for vars
+ * like display-filters and url-viewers
+ * which may require quoting of an arg
+ * inside of a _TOKEN_.
+ */
+void
+get_pair(char *string, char **label, char **value, int firstws, int strip_internal_label_quotes)
+{
+ char *p, *q, *tmp, *token = NULL;
+ int quoted = 0;
+
+ *label = *value = NULL;
+
+ /*
+ * This for loop just finds the beginning of the value. If firstws
+ * is set, then it begins after the first whitespace. Otherwise, it begins
+ * after the last whitespace. Quoted whitespace doesn't count as
+ * whitespace. If there is no unquoted whitespace, then there is no
+ * label, there's just a value.
+ */
+ for(p = string; p && *p;){
+ if(*p == '"') /* quoted label? */
+ quoted = (quoted) ? 0 : 1;
+
+ if(*p == '\\' && *(p+1) == '"') /* escaped quote? */
+ p++; /* skip it... */
+
+ if(isspace((unsigned char)*p) && !quoted){ /* if space, */
+ while(*++p && isspace((unsigned char)*p)) /* move past it */
+ ;
+
+ if(!firstws || !token)
+ token = p; /* remember start of text */
+ }
+ else
+ p++;
+ }
+
+ if(token){ /* copy label */
+ *label = p = (char *)fs_get(((token - string) + 1) * sizeof(char));
+
+ /* make a copy of the string */
+ tmp = (char *)fs_get(((token - string) + 1) * sizeof(char));
+ strncpy(tmp, string, token - string);
+ tmp[token-string] = '\0';
+
+ removing_leading_and_trailing_white_space(tmp);
+ quoted = removing_double_quotes(tmp);
+
+ for(q = tmp; *q; q++){
+ if(quoted && *q == '\\' && (*(q+1) == '"' || *(q+1) == '\\'))
+ *p++ = *++q;
+ else if(!(strip_internal_label_quotes && *q == '"'))
+ *p++ = *q;
+ }
+
+ *p = '\0'; /* tie off label */
+ fs_give((void **)&tmp);
+ if(*label == '\0')
+ fs_give((void **)label);
+ }
+ else
+ token = string;
+
+ if(token){ /* copy value */
+ *value = p = (char *)fs_get((strlen(token) + 1) * sizeof(char));
+
+ tmp = cpystr(token);
+ removing_leading_and_trailing_white_space(tmp);
+ quoted = removing_double_quotes(tmp);
+
+ for(q = tmp; *q ; q++){
+ if(quoted && *q == '\\' && (*(q+1) == '"' || *(q+1) == '\\'))
+ *p++ = *++q;
+ else
+ *p++ = *q;
+ }
+
+ *p = '\0'; /* tie off value */
+ fs_give((void **)&tmp);
+ }
+}
+
+
+/*
+ * This is sort of the inverse of get_pair.
+ *
+ * Args -- label -- the first half of the string
+ * value -- the last half of the string
+ *
+ * Returns -- an allocated string which is "label" SPACE "value"
+ *
+ * Label and value are quoted separately. If quoting is needed (they contain
+ * whitespace) then backslash escaping is done inside the quotes for
+ * " and for \. If quoting is not needed, no escaping is done.
+ */
+char *
+put_pair(char *label, char *value)
+{
+ char *result, *lab = label, *val = value;
+ size_t l;
+
+ if(label && *label)
+ lab = quote_if_needed(label);
+
+ if(value && *value)
+ val = quote_if_needed(value);
+
+ l = strlen(lab) + strlen(val) +1;
+ result = (char *) fs_get((l+1) * sizeof(char));
+
+ snprintf(result, l+1, "%s%s%s",
+ lab ? lab : "",
+ (lab && lab[0] && val && val[0]) ? " " : "",
+ val ? val : "");
+
+ if(lab && lab != label)
+ fs_give((void **)&lab);
+ if(val && val != value)
+ fs_give((void **)&val);
+
+ return(result);
+}
+
+
+/*
+ * This is for put_pair type uses. It returns either an allocated
+ * string which is the quoted src string or it returns a pointer to
+ * the src string if no quoting is needed.
+ */
+char *
+quote_if_needed(char *src)
+{
+ char *result = src, *qsrc = NULL;
+
+ if(src && *src){
+ /* need quoting? */
+ if(strpbrk(src, " \t") != NULL)
+ qsrc = add_escapes(src, "\\\"", '\\', "", "");
+
+ if(qsrc && !*qsrc)
+ fs_give((void **)&qsrc);
+
+ if(qsrc){
+ size_t l;
+
+ l = strlen(qsrc)+2;
+ result = (char *) fs_get((l+1) * sizeof(char));
+ snprintf(result, l+1, "\"%s\"", qsrc);
+ fs_give((void **)&qsrc);
+ }
+ }
+
+ return(result);
+}
+
+
+/*
+ * Convert a 1, 2, or 3-digit octal string into an 8-bit character.
+ * Only the first three characters of s will be used, and it is ok not
+ * to null-terminate it.
+ */
+int
+read_octal(char **s)
+{
+ register int i, j;
+
+ i = 0;
+ for(j = 0; j < 3 && **s >= '0' && **s < '8' ; (*s)++, j++)
+ i = (i * 8) + (int)(unsigned char)**s - '0';
+
+ return(i);
+}
+
+
+/*
+ * Convert two consecutive HEX digits to an integer. First two
+ * chars pointed to by "s" MUST already be tested for hexness.
+ */
+int
+read_hex(char *s)
+{
+ return(X2C(s));
+}
+
+
+/*
+ * Given a character c, put the 3-digit ascii octal value of that char
+ * in the 2nd argument, which must be at least 3 in length.
+ */
+void
+char_to_octal_triple(int c, char *octal)
+{
+ c &= 0xff;
+
+ octal[2] = (c % 8) + '0';
+ c /= 8;
+ octal[1] = (c % 8) + '0';
+ c /= 8;
+ octal[0] = c + '0';
+}
+
+
+/*
+ * Convert in memory string s to a C-style string, with backslash escapes
+ * like they're used in C character constants.
+ * Also convert leading spaces because read_pinerc deletes those
+ * if not quoted.
+ *
+ * Returns allocated C string version of s.
+ */
+char *
+string_to_cstring(char *s)
+{
+ char *b, *p;
+ int n, i, all_space_so_far = 1;
+
+ if(!s)
+ return(cpystr(""));
+
+ n = 20;
+ b = (char *)fs_get((n+1) * sizeof(char));
+ p = b;
+ *p = '\0';
+ i = 0;
+
+ while(*s){
+ if(*s != SPACE)
+ all_space_so_far = 0;
+
+ if(i + 4 > n){
+ /*
+ * The output string may overflow the output buffer.
+ * Make more room.
+ */
+ n += 20;
+ fs_resize((void **)&b, (n+1) * sizeof(char));
+ p = &b[i];
+ }
+ else{
+ switch(*s){
+ case '\n':
+ *p++ = '\\';
+ *p++ = 'n';
+ i += 2;
+ break;
+
+ case '\r':
+ *p++ = '\\';
+ *p++ = 'r';
+ i += 2;
+ break;
+
+ case '\t':
+ *p++ = '\\';
+ *p++ = 't';
+ i += 2;
+ break;
+
+ case '\b':
+ *p++ = '\\';
+ *p++ = 'b';
+ i += 2;
+ break;
+
+ case '\f':
+ *p++ = '\\';
+ *p++ = 'f';
+ i += 2;
+ break;
+
+ case '\\':
+ *p++ = '\\';
+ *p++ = '\\';
+ i += 2;
+ break;
+
+ case SPACE:
+ if(all_space_so_far){ /* use octal output */
+ *p++ = '\\';
+ char_to_octal_triple(*s, p);
+ p += 3;
+ i += 4;
+ break;
+ }
+ else{
+ /* fall through */
+ }
+
+
+ default:
+ if(*s >= SPACE && *s < '~' && *s != '\"' && *s != '$'){
+ *p++ = *s;
+ i++;
+ }
+ else{ /* use octal output */
+ *p++ = '\\';
+ char_to_octal_triple(*s, p);
+ p += 3;
+ i += 4;
+ }
+
+ break;
+ }
+
+ s++;
+ }
+ }
+
+ *p = '\0';
+ return(b);
+}
+
+
+/*
+ * Convert C-style string, with backslash escapes, into a hex string, two
+ * hex digits per character.
+ *
+ * Returns allocated hexstring version of s.
+ */
+char *
+cstring_to_hexstring(char *s)
+{
+ char *b, *p;
+ int n, i, c;
+
+ if(!s)
+ return(cpystr(""));
+
+ n = 20;
+ b = (char *)fs_get((n+1) * sizeof(char));
+ p = b;
+ *p = '\0';
+ i = 0;
+
+ while(*s){
+ if(i + 2 > n){
+ /*
+ * The output string may overflow the output buffer.
+ * Make more room.
+ */
+ n += 20;
+ fs_resize((void **)&b, (n+1) * sizeof(char));
+ p = &b[i];
+ }
+ else{
+ if(*s == '\\'){
+ s++;
+ switch(*s){
+ case 'n':
+ c = '\n';
+ C2XPAIR(c, p);
+ i += 2;
+ s++;
+ break;
+
+ case 'r':
+ c = '\r';
+ C2XPAIR(c, p);
+ i += 2;
+ s++;
+ break;
+
+ case 't':
+ c = '\t';
+ C2XPAIR(c, p);
+ i += 2;
+ s++;
+ break;
+
+ case 'v':
+ c = '\v';
+ C2XPAIR(c, p);
+ i += 2;
+ s++;
+ break;
+
+ case 'b':
+ c = '\b';
+ C2XPAIR(c, p);
+ i += 2;
+ s++;
+ break;
+
+ case 'f':
+ c = '\f';
+ C2XPAIR(c, p);
+ i += 2;
+ s++;
+ break;
+
+ case 'a':
+ c = '\007';
+ C2XPAIR(c, p);
+ i += 2;
+ s++;
+ break;
+
+ case '\\':
+ c = '\\';
+ C2XPAIR(c, p);
+ i += 2;
+ s++;
+ break;
+
+ case '?':
+ c = '?';
+ C2XPAIR(c, p);
+ i += 2;
+ s++;
+ break;
+
+ case '\'':
+ c = '\'';
+ C2XPAIR(c, p);
+ i += 2;
+ s++;
+ break;
+
+ case '\"':
+ c = '\"';
+ C2XPAIR(c, p);
+ i += 2;
+ s++;
+ break;
+
+ case 0: /* reached end of s too early */
+ c = 0;
+ C2XPAIR(c, p);
+ i += 2;
+ s++;
+ break;
+
+ /* hex number */
+ case 'x':
+ s++;
+ if(isxpair(s)){
+ c = X2C(s);
+ s += 2;
+ }
+ else if(isxdigit((unsigned char)*s)){
+ c = XDIGIT2C(*s);
+ s++;
+ }
+ else
+ c = 0;
+
+ C2XPAIR(c, p);
+ i += 2;
+
+ break;
+
+ /* octal number */
+ default:
+ c = read_octal(&s);
+ C2XPAIR(c, p);
+ i += 2;
+
+ break;
+ }
+ }
+ else{
+ C2XPAIR(*s, p);
+ i += 2;
+ s++;
+ }
+ }
+ }
+
+ *p = '\0';
+ return(b);
+}
+
+
+/*
+ * Convert C-style string, with backslash escapes, into a regular string.
+ * Result goes in dst, which should be as big as src.
+ *
+ */
+void
+cstring_to_string(char *src, char *dst)
+{
+ char *p;
+ int c;
+
+ dst[0] = '\0';
+ if(!src)
+ return;
+
+ p = dst;
+
+ while(*src){
+ if(*src == '\\'){
+ src++;
+ switch(*src){
+ case 'n':
+ *p++ = '\n';
+ src++;
+ break;
+
+ case 'r':
+ *p++ = '\r';
+ src++;
+ break;
+
+ case 't':
+ *p++ = '\t';
+ src++;
+ break;
+
+ case 'v':
+ *p++ = '\v';
+ src++;
+ break;
+
+ case 'b':
+ *p++ = '\b';
+ src++;
+ break;
+
+ case 'f':
+ *p++ = '\f';
+ src++;
+ break;
+
+ case 'a':
+ *p++ = '\007';
+ src++;
+ break;
+
+ case '\\':
+ *p++ = '\\';
+ src++;
+ break;
+
+ case '?':
+ *p++ = '?';
+ src++;
+ break;
+
+ case '\'':
+ *p++ = '\'';
+ src++;
+ break;
+
+ case '\"':
+ *p++ = '\"';
+ src++;
+ break;
+
+ case 0: /* reached end of s too early */
+ src++;
+ break;
+
+ /* hex number */
+ case 'x':
+ src++;
+ if(isxpair(src)){
+ c = X2C(src);
+ src += 2;
+ }
+ else if(isxdigit((unsigned char)*src)){
+ c = XDIGIT2C(*src);
+ src++;
+ }
+ else
+ c = 0;
+
+ *p++ = c;
+
+ break;
+
+ /* octal number */
+ default:
+ c = read_octal(&src);
+ *p++ = c;
+ break;
+ }
+ }
+ else
+ *p++ = *src++;
+ }
+
+ *p = '\0';
+}
+
+
+/*
+ * Quotes /'s and \'s with \
+ *
+ * Args: src -- The source string.
+ *
+ * Returns: A string with backslash quoting added. Any / in the string is
+ * replaced with \/ and any \ is replaced with \\, and any
+ * " is replaced with \".
+ *
+ * The caller is responsible for freeing the memory allocated for the answer.
+ */
+char *
+add_backslash_escapes(char *src)
+{
+ return(add_escapes(src, "/\\\"", '\\', "", ""));
+}
+
+
+/*
+ * Undoes backslash quoting of source string.
+ *
+ * Args: src -- The source string.
+ *
+ * Returns: A string with backslash quoting removed or NULL. The string starts
+ * at src and goes until the end of src or until a / is reached. The
+ * / is not included in the string. /'s may be quoted by preceding
+ * them with a backslash (\) and \'s may also be quoted by
+ * preceding them with a \. In fact, \ quotes any character.
+ * Not quite, \nnn is octal escape, \xXX is hex escape.
+ *
+ * The caller is responsible for freeing the memory allocated for the answer.
+ */
+char *
+remove_backslash_escapes(char *src)
+{
+ char *ans = NULL, *q, *p;
+ int done = 0;
+
+ if(src){
+ p = q = (char *)fs_get(strlen(src) + 1);
+
+ while(!done){
+ switch(*src){
+ case '\\':
+ src++;
+ if(*src){
+ if(isdigit((unsigned char)*src))
+ *p++ = (char)read_octal(&src);
+ else if((*src == 'x' || *src == 'X') &&
+ *(src+1) && *(src+2) && isxpair(src+1)){
+ *p++ = (char)read_hex(src+1);
+ src += 3;
+ }
+ else
+ *p++ = *src++;
+ }
+
+ break;
+
+ case '\0':
+ case '/':
+ done++;
+ break;
+
+ default:
+ *p++ = *src++;
+ break;
+ }
+ }
+
+ *p = '\0';
+
+ ans = cpystr(q);
+ fs_give((void **)&q);
+ }
+
+ return(ans);
+}
+
+
+/*
+ * Quote values for viewer-hdr-colors. We quote backslash, comma, and slash.
+ * Also replaces $ with $$.
+ *
+ * Args: src -- The source string.
+ *
+ * Returns: A string with backslash quoting added.
+ *
+ * The caller is responsible for freeing the memory allocated for the answer.
+ */
+char *
+add_viewerhdr_escapes(char *src)
+{
+ char *tmp, *ans = NULL;
+
+ tmp = add_escapes(src, "/\\", '\\', ",", "");
+
+ if(tmp){
+ ans = dollar_escape_dollars(tmp);
+ fs_give((void **) &tmp);
+ }
+
+ return(ans);
+}
+
+
+/*
+ * Quote dollar sign by preceding it with another dollar sign. We use $$
+ * instead of \$ so that it will work for both PC-Pine and unix.
+ *
+ * Args: src -- The source string.
+ *
+ * Returns: A string with $$ quoting added.
+ *
+ * The caller is responsible for freeing the memory allocated for the answer.
+ */
+char *
+dollar_escape_dollars(char *src)
+{
+ return(add_escapes(src, "$", '$', "", ""));
+}
+
+
+/*
+ * This adds the quoting for vcard backslash quoting.
+ * That is, commas are backslashed, backslashes are backslashed,
+ * semicolons are backslashed, and CRLFs are \n'd.
+ * This is thought to be correct for draft-ietf-asid-mime-vcard-06.txt, Apr 98.
+ */
+char *
+vcard_escape(char *src)
+{
+ char *p, *q;
+
+ q = add_escapes(src, ";,\\", '\\', "", "");
+ if(q){
+ /* now do CRLF -> \n in place */
+ for(p = q; *p != '\0'; p++)
+ if(*p == '\r' && *(p+1) == '\n'){
+ *p++ = '\\';
+ *p = 'n';
+ }
+ }
+
+ return(q);
+}
+
+
+/*
+ * This undoes the vcard backslash quoting.
+ *
+ * In particular, it turns \n into newline, \, into ',', \\ into \, \; -> ;.
+ * In fact, \<anything_else> is also turned into <anything_else>. The ID
+ * isn't clear on this.
+ *
+ * The caller is responsible for freeing the memory allocated for the answer.
+ */
+char *
+vcard_unescape(char *src)
+{
+ char *ans = NULL, *p;
+ int done = 0;
+
+ if(src){
+ p = ans = (char *)fs_get(strlen(src) + 1);
+
+ while(!done){
+ switch(*src){
+ case '\\':
+ src++;
+ if(*src == 'n' || *src == 'N'){
+ *p++ = '\n';
+ src++;
+ }
+ else if(*src)
+ *p++ = *src++;
+
+ break;
+
+ case '\0':
+ done++;
+ break;
+
+ default:
+ *p++ = *src++;
+ break;
+ }
+ }
+
+ *p = '\0';
+ }
+
+ return(ans);
+}
+
+
+/*
+ * Turn folded lines into long lines in place.
+ *
+ * CRLF whitespace sequences are removed, the space is not preserved.
+ */
+void
+vcard_unfold(char *string)
+{
+ char *p = string;
+
+ while(*string) /* while something to copy */
+ if(*string == '\r' &&
+ *(string+1) == '\n' &&
+ (*(string+2) == SPACE || *(string+2) == TAB))
+ string += 3;
+ else
+ *p++ = *string++;
+
+ *p = '\0';
+}
+
+
+/*
+ * Quote specified chars with escape char.
+ *
+ * Args: src -- The source string.
+ * quote_these_chars -- Array of chars to quote
+ * quoting_char -- The quoting char to be used (e.g., \)
+ * hex_these_chars -- Array of chars to hex escape
+ * hex_these_quoted_chars -- Array of chars to hex escape if they are
+ * already quoted with quoting_char (that is,
+ * turn \, into hex comma)
+ *
+ * Returns: An allocated copy of string with quoting added.
+ * The caller is responsible for freeing the memory allocated for the answer.
+ */
+char *
+add_escapes(char *src, char *quote_these_chars, int quoting_char,
+ char *hex_these_chars, char *hex_these_quoted_chars)
+{
+ char *ans = NULL;
+
+ if(!quote_these_chars)
+ panic("bad arg to add_escapes");
+
+ if(src){
+ char *q, *p, *qchar;
+
+ p = q = (char *)fs_get(2*strlen(src) + 1);
+
+ while(*src){
+ if(*src == quoting_char)
+ for(qchar = hex_these_quoted_chars; *qchar != '\0'; qchar++)
+ if(*(src+1) == *qchar)
+ break;
+
+ if(*src == quoting_char && *qchar){
+ src++; /* skip quoting_char */
+ *p++ = '\\';
+ *p++ = 'x';
+ C2XPAIR(*src, p);
+ src++; /* skip quoted char */
+ }
+ else{
+ for(qchar = quote_these_chars; *qchar != '\0'; qchar++)
+ if(*src == *qchar)
+ break;
+
+ if(*qchar){ /* *src is a char to be quoted */
+ *p++ = quoting_char;
+ *p++ = *src++;
+ }
+ else{
+ for(qchar = hex_these_chars; *qchar != '\0'; qchar++)
+ if(*src == *qchar)
+ break;
+
+ if(*qchar){ /* *src is a char to be escaped */
+ *p++ = '\\';
+ *p++ = 'x';
+ C2XPAIR(*src, p);
+ src++;
+ }
+ else /* a regular char */
+ *p++ = *src++;
+ }
+ }
+
+ }
+
+ *p = '\0';
+
+ ans = cpystr(q);
+ fs_give((void **)&q);
+ }
+
+ return(ans);
+}
+
+
+/*
+ * Copy a string enclosed in "" without fixing \" or \\. Skip past \"
+ * but copy it as is, removing only the enclosing quotes.
+ */
+char *
+copy_quoted_string_asis(char *src)
+{
+ char *q, *p;
+ int done = 0, quotes = 0;
+
+ if(src){
+ p = q = (char *)fs_get(strlen(src) + 1);
+
+ while(!done){
+ switch(*src){
+ case QUOTE:
+ if(++quotes == 2)
+ done++;
+ else
+ src++;
+
+ break;
+
+ case BSLASH: /* don't count \" as a quote, just copy */
+ if(*(src+1) == QUOTE){
+ if(quotes == 1){
+ *p++ = *src;
+ *p++ = *(src+1);
+ }
+
+ src += 2;
+ }
+ else{
+ if(quotes == 1)
+ *p++ = *src;
+
+ src++;
+ }
+
+ break;
+
+ case '\0':
+ fs_give((void **)&q);
+ return(NULL);
+
+ default:
+ if(quotes == 1)
+ *p++ = *src;
+
+ src++;
+
+ break;
+ }
+ }
+
+ *p = '\0';
+ }
+
+ return(q);
+}
+
+
+/*
+ * isxpair -- return true if the first two chars in string are
+ * hexidecimal characters
+ */
+int
+isxpair(char *s)
+{
+ return(isxdigit((unsigned char) *s) && isxdigit((unsigned char) *(s+1)));
+}
+
+
+
+
+
+/*
+ * * * * * * * something to help managing lists of strings * * * * * * * *
+ */
+
+
+STRLIST_S *
+new_strlist(char *name)
+{
+ STRLIST_S *sp = (STRLIST_S *) fs_get(sizeof(STRLIST_S));
+ memset(sp, 0, sizeof(STRLIST_S));
+ if(name)
+ sp->name = cpystr(name);
+
+ return(sp);
+}
+
+
+STRLIST_S *
+copy_strlist(STRLIST_S *src)
+{
+ STRLIST_S *ret = NULL, *sl, *ss, *new_sl;
+
+ if(src){
+ ss = NULL;
+ for(sl = src; sl; sl = sl->next){
+ new_sl = (STRLIST_S *) fs_get(sizeof(*new_sl));
+ memset((void *) new_sl, 0, sizeof(*new_sl));
+ if(sl->name)
+ new_sl->name = cpystr(sl->name);
+
+ if(ss){
+ ss->next = new_sl;
+ ss = ss->next;
+ }
+ else{
+ ret = new_sl;
+ ss = ret;
+ }
+ }
+ }
+
+ return(ret);
+}
+
+
+/*
+ * Add the second list to the end of the first.
+ */
+void
+combine_strlists(STRLIST_S **first, STRLIST_S *second)
+{
+ STRLIST_S *sl;
+
+ if(!second)
+ return;
+
+ if(first){
+ if(*first){
+ for(sl = *first; sl->next; sl = sl->next)
+ ;
+
+ sl->next = second;
+ }
+ else
+ *first = second;
+ }
+}
+
+
+void
+free_strlist(STRLIST_S **strp)
+{
+ if(strp && *strp){
+ if((*strp)->next)
+ free_strlist(&(*strp)->next);
+
+ if((*strp)->name)
+ fs_give((void **) &(*strp)->name);
+
+ fs_give((void **) strp);
+ }
+}
diff --git a/pith/string.h b/pith/string.h
new file mode 100644
index 00000000..11c4d45f
--- /dev/null
+++ b/pith/string.h
@@ -0,0 +1,151 @@
+/*
+ * $Id: string.h 769 2007-10-24 00:15:40Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_STRING_INCLUDED
+#define PITH_STRING_INCLUDED
+
+
+/*
+ * Hex conversion aids
+ */
+#define HEX_ARRAY "0123456789ABCDEF"
+#define HEX_CHAR1(C) HEX_ARRAY[((C) & 0xf0) >> 4]
+#define HEX_CHAR2(C) HEX_ARRAY[(C) & 0xf]
+
+#define XDIGIT2C(C) ((C) - (isdigit((unsigned char) (C)) \
+ ? '0' : (isupper((unsigned char)(C))? '7' : 'W')))
+
+#define X2C(S) ((XDIGIT2C(*(S)) << 4) | XDIGIT2C(*((S)+1)))
+
+#define C2XPAIR(C, S) { \
+ *(S)++ = HEX_CHAR1(C); \
+ *(S)++ = HEX_CHAR2(C); \
+ }
+
+
+/* for flags to fold() routine */
+#define FLD_NONE 0x00
+#define FLD_CRLF 0x01 /* use CRLF end of line instead of LF */
+#define FLD_PWS 0x02 /* preserve whitespace when folding */
+
+
+typedef enum {FrontDots, MidDots, EndDots} WhereDots;
+
+
+/*
+ * Macro to help determine when we need to filter out chars
+ * from message index or headers...
+ */
+#define FILTER_THIS(c) (((((unsigned char) (c) < 0x20 \
+ || (unsigned char) (c) == 0x7f) \
+ && !ps_global->pass_ctrl_chars) \
+ || (((unsigned char) (c) >= 0x80 \
+ && (unsigned char) (c) < 0xA0) \
+ && !ps_global->pass_ctrl_chars \
+ && !ps_global->pass_c1_ctrl_chars)) \
+ && !((c) == SPACE \
+ || (c) == TAB \
+ || (c) == '\016' \
+ || (c) == '\017'))
+
+
+/*
+ * Keeps track of selected folders between instances of
+ * the folder list screen.
+ */
+typedef struct name_list {
+ char *name;
+ struct name_list *next;
+} STRLIST_S;
+
+
+struct date {
+ int sec, minute, hour, day, month,
+ year, hours_off_gmt, min_off_gmt, wkday;
+};
+
+
+/* just a convenient place to put these so everything can access */
+#define BUILDER_SCREEN_MANGLED 0x1
+#define BUILDER_MESSAGE_DISPLAYED 0x2
+#define BUILDER_FOOTER_MANGLED 0x4
+
+
+/* exported protoypes */
+char *rplstr(char *, size_t, int, char *);
+void sqzspaces(char *);
+void sqznewlines(char *);
+void removing_leading_white_space(char *);
+void removing_trailing_white_space(char *);
+void removing_leading_and_trailing_white_space(char *);
+int removing_double_quotes(char *);
+char *skip_white_space(char *);
+char *skip_to_white_space(char *);
+char *removing_quotes(char *);
+char *strclean(char *);
+char *short_str(char *, char *, size_t, int, WhereDots);
+char *srchstr(char *, char *);
+char *srchrstr(char *, char *);
+char *strindex(char *, int);
+char *strrindex(char *, int);
+char *iutf8ncpy(char *, char *, int);
+char *istrncpy(char *, char *, int);
+char *month_abbrev(int);
+char *month_abbrev_locale(int);
+char *month_name(int);
+char *month_name_locale(int);
+char *day_abbrev(int);
+char *day_abbrev_locale(int);
+char *day_name(int);
+char *day_name_locale(int);
+size_t our_strftime(char *, size_t, char *, struct tm *);
+int month_num(char *);
+void parse_date(char *, struct date *);
+char *convert_date_to_local(char *);
+char *repeat_char(int, int);
+char *byte_string(long);
+char *enth_string(int);
+char *fold(char *, int, int, char *, char *, unsigned);
+char *strsquish(char *, size_t, char *, int);
+char *long2string(long);
+char *ulong2string(unsigned long);
+char *int2string(int);
+char *strtoval(char *, int *, int, int, int, char *, size_t, char *);
+char *strtolval(char *, long *, long, long, long, char *, size_t, char *);
+void get_pair(char *, char **, char **, int, int);
+char *put_pair(char *, char *);
+char *quote_if_needed(char *);
+int read_hex(char *);
+char *string_to_cstring(char *);
+char *cstring_to_hexstring(char *);
+void cstring_to_string(char *, char *);
+char *add_backslash_escapes(char *);
+char *remove_backslash_escapes(char *);
+char *add_viewerhdr_escapes(char *);
+char *vcard_escape(char *);
+char *vcard_unescape(char *);
+void vcard_unfold(char *);
+char *add_escapes(char *, char *, int, char *, char *);
+char *copy_quoted_string_asis(char *);
+int isxpair(char *);
+STRLIST_S *new_strlist(char *);
+STRLIST_S *copy_strlist(STRLIST_S *);
+void combine_strlists(STRLIST_S **, STRLIST_S *);
+void free_strlist(STRLIST_S **);
+int read_octal(char **);
+
+
+#endif /* PITH_STRING_INCLUDED */
diff --git a/pith/strlst.c b/pith/strlst.c
new file mode 100644
index 00000000..52b28bca
--- /dev/null
+++ b/pith/strlst.c
@@ -0,0 +1,51 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: strlst.c 761 2007-10-23 22:35:18Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+
+ strlst.c
+ Implements STRINGLIST creation destruction routines
+
+ ====*/
+
+
+#include "../pith/headers.h"
+#include "../pith/strlst.h"
+
+
+STRINGLIST *
+new_strlst(char **l)
+{
+ STRINGLIST *sl = mail_newstringlist();
+
+ sl->text.data = (unsigned char *) (*l);
+ sl->text.size = strlen(*l);
+ sl->next = (*++l) ? new_strlst(l) : NULL;
+ return(sl);
+}
+
+
+void
+free_strlst(struct string_list **sl)
+{
+ if(*sl){
+ if((*sl)->next)
+ free_strlst(&(*sl)->next);
+
+ fs_give((void **) sl);
+ }
+}
diff --git a/pith/strlst.h b/pith/strlst.h
new file mode 100644
index 00000000..df1bf856
--- /dev/null
+++ b/pith/strlst.h
@@ -0,0 +1,25 @@
+/*
+ * $Id: strlst.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_STRLST_INCLUDED
+#define PITH_STRLST_INCLUDED
+
+
+/* exported protoypes */
+STRINGLIST *new_strlst(char **);
+void free_strlst(STRINGLIST **);
+
+
+#endif /* PITH_STRLST_INCLUDED */
diff --git a/pith/takeaddr.c b/pith/takeaddr.c
new file mode 100644
index 00000000..3dec5147
--- /dev/null
+++ b/pith/takeaddr.c
@@ -0,0 +1,2228 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: takeaddr.c 1012 2008-03-26 00:44:22Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ takeaddr.c
+ Mostly support for Take Address command.
+ ====*/
+
+
+#include "../pith/headers.h"
+#include "../pith/takeaddr.h"
+#include "../pith/conf.h"
+#include "../pith/bldaddr.h"
+#include "../pith/adrbklib.h"
+#include "../pith/copyaddr.h"
+#include "../pith/addrstring.h"
+#include "../pith/status.h"
+#include "../pith/mailview.h"
+#include "../pith/reply.h"
+#include "../pith/url.h"
+#include "../pith/mailpart.h"
+#include "../pith/sequence.h"
+#include "../pith/stream.h"
+#include "../pith/busy.h"
+#include "../pith/ablookup.h"
+#include "../pith/list.h"
+
+
+static char *fakedomain = "@";
+long msgno_for_pico_callback;
+BODY *body_for_pico_callback = NULL;
+ENVELOPE *env_for_pico_callback = NULL;
+
+
+/*
+ * Previous selectable TA line.
+ * skips over the elements with skip_it set
+ */
+TA_S *
+pre_sel_taline(TA_S *current)
+{
+ TA_S *p;
+
+ if(!current)
+ return NULL;
+
+ p = current->prev;
+ while(p && p->skip_it)
+ p = p->prev;
+
+ return(p);
+}
+
+
+/*
+ * Previous TA line, selectable or just printable.
+ * skips over the elements with skip_it set
+ */
+TA_S *
+pre_taline(TA_S *current)
+{
+ TA_S *p;
+
+ if(!current)
+ return NULL;
+
+ p = current->prev;
+ while(p && (p->skip_it && !p->print))
+ p = p->prev;
+
+ return(p);
+}
+
+
+/*
+ * Next selectable TA line.
+ * skips over the elements with skip_it set
+ */
+TA_S *
+next_sel_taline(TA_S *current)
+{
+ TA_S *p;
+
+ if(!current)
+ return NULL;
+
+ p = current->next;
+ while(p && p->skip_it)
+ p = p->next;
+
+ return(p);
+}
+
+
+/*
+ * Next TA line, including print only lines.
+ * skips over the elements with skip_it set unless they are print lines
+ */
+TA_S *
+next_taline(TA_S *current)
+{
+ TA_S *p;
+
+ if(!current)
+ return NULL;
+
+ p = current->next;
+ while(p && (p->skip_it && !p->print))
+ p = p->next;
+
+ return(p);
+}
+
+
+/*
+ * Mark all of the addresses with a check.
+ *
+ * Args: f_line -- the first ta line
+ *
+ * Returns the number of lines checked.
+ */
+int
+ta_mark_all(TA_S *f_line)
+{
+ TA_S *ctmp;
+ int how_many_selected = 0;
+
+ for(ctmp = f_line; ctmp; ctmp = next_sel_taline(ctmp)){
+ ctmp->checked = 1;
+ how_many_selected++;
+ }
+
+ return(how_many_selected);
+}
+
+
+/*
+ * Does the takeaddr list consist of a single address?
+ *
+ * Args: f_line -- the first ta line
+ *
+ * Returns 1 if only one address and 0 otherwise.
+ */
+int
+is_talist_of_one(TA_S *f_line)
+{
+ if(f_line == NULL)
+ return 0;
+
+ /* there is at least one, see if there are two */
+ if(next_sel_taline(f_line) != NULL)
+ return 0;
+
+ return 1;
+}
+
+
+/*
+ * Turn off all of the check marks.
+ *
+ * Args: f_line -- the first ta line
+ *
+ * Returns the number of lines checked (0).
+ */
+int
+ta_unmark_all(TA_S *f_line)
+{
+ TA_S *ctmp;
+
+ for(ctmp = f_line; ctmp; ctmp = ctmp->next)
+ ctmp->checked = 0;
+
+ return 0;
+}
+
+
+/*
+ * new_taline - create new TA_S, zero it out, and insert it after current.
+ * NOTE current gets set to the new TA_S, too!
+ */
+TA_S *
+new_taline(TA_S **current)
+{
+ TA_S *p;
+
+ p = (TA_S *)fs_get(sizeof(TA_S));
+ memset((void *)p, 0, sizeof(TA_S));
+ if(current){
+ if(*current){
+ p->next = (*current)->next;
+ (*current)->next = p;
+ p->prev = *current;
+ if(p->next)
+ p->next->prev = p;
+ }
+
+ *current = p;
+ }
+
+ return(p);
+}
+
+
+void
+free_taline(TA_S **p)
+{
+ if(p && *p){
+ if((*p)->addr)
+ mail_free_address(&(*p)->addr);
+
+ if((*p)->strvalue)
+ fs_give((void **)&(*p)->strvalue);
+
+ if((*p)->nickname)
+ fs_give((void **)&(*p)->nickname);
+
+ if((*p)->fullname)
+ fs_give((void **)&(*p)->fullname);
+
+ if((*p)->fcc)
+ fs_give((void **)&(*p)->fcc);
+
+ if((*p)->comment)
+ fs_give((void **)&(*p)->comment);
+
+ if((*p)->prev)
+ (*p)->prev->next = (*p)->next;
+
+ if((*p)->next)
+ (*p)->next->prev = (*p)->prev;
+
+ fs_give((void **)p);
+ }
+}
+
+
+void
+free_talines(TA_S **ta_list)
+{
+ TA_S *current, *ctmp;
+
+ if(ta_list && (current = (*ta_list))){
+ while(current->prev)
+ current = current->prev;
+
+ while(current){
+ ctmp = current->next;
+ free_taline(&current);
+ current = ctmp;
+ }
+
+ *ta_list = NULL;
+ }
+}
+
+
+void
+free_ltline(LINES_TO_TAKE **p)
+{
+ if(p && *p){
+ if((*p)->printval)
+ fs_give((void **)&(*p)->printval);
+
+ if((*p)->exportval)
+ fs_give((void **)&(*p)->exportval);
+
+ if((*p)->prev)
+ (*p)->prev->next = (*p)->next;
+
+ if((*p)->next)
+ (*p)->next->prev = (*p)->prev;
+
+ fs_give((void **)p);
+ }
+}
+
+
+void
+free_ltlines(LINES_TO_TAKE **lt_list)
+{
+ LINES_TO_TAKE *current, *ctmp;
+
+ if(lt_list && (current = (*lt_list))){
+ while(current->prev)
+ current = current->prev;
+
+ while(current){
+ ctmp = current->next;
+ free_ltline(&current);
+ current = ctmp;
+ }
+
+ *lt_list = NULL;
+ }
+}
+
+
+/*
+ * Return the first selectable TakeAddr line.
+ *
+ * Args: q -- any line in the list
+ */
+TA_S *
+first_sel_taline(TA_S *q)
+{
+ TA_S *first;
+
+ if(q == NULL)
+ return NULL;
+
+ first = NULL;
+ /* back up to the head of the list */
+ while(q){
+ first = q;
+ q = pre_sel_taline(q);
+ }
+
+ /*
+ * If first->skip_it, that means we were already past the first
+ * legitimate line, so we have to look in the other direction.
+ */
+ if(first->skip_it)
+ first = next_sel_taline(first);
+
+ return(first);
+}
+
+
+/*
+ * Return the last selectable TakeAddr line.
+ *
+ * Args: q -- any line in the list
+ */
+TA_S *
+last_sel_taline(TA_S *q)
+{
+ TA_S *last;
+
+ if(q == NULL)
+ return NULL;
+
+ last = NULL;
+ /* go to the end of the list */
+ while(q){
+ last = q;
+ q = next_sel_taline(q);
+ }
+
+ /*
+ * If last->skip_it, that means we were already past the last
+ * legitimate line, so we have to look in the other direction.
+ */
+ if(last->skip_it)
+ last = pre_sel_taline(last);
+
+ return(last);
+}
+
+
+/*
+ * Return the first TakeAddr line, selectable or just printable.
+ *
+ * Args: q -- any line in the list
+ */
+TA_S *
+first_taline(TA_S *q)
+{
+ TA_S *first;
+
+ if(q == NULL)
+ return NULL;
+
+ first = NULL;
+ /* back up to the head of the list */
+ while(q){
+ first = q;
+ q = pre_taline(q);
+ }
+
+ /*
+ * If first->skip_it, that means we were already past the first
+ * legitimate line, so we have to look in the other direction.
+ */
+ if(first->skip_it && !first->print)
+ first = next_taline(first);
+
+ return(first);
+}
+
+
+/*
+ * Find the first TakeAddr line which is checked, beginning with the
+ * passed in line.
+ *
+ * Args: head -- A passed in TakeAddr line, usually will be the first
+ *
+ * Result: returns a pointer to the first checked line.
+ * NULL if no such line
+ */
+TA_S *
+first_checked(TA_S *head)
+{
+ TA_S *p;
+
+ p = head;
+
+ for(p = head; p; p = next_sel_taline(p))
+ if(p->checked && !p->skip_it)
+ break;
+
+ return(p);
+}
+
+
+/*
+ * Form a list of strings which are addresses to go in a list.
+ * These are entries in a list, so can be full rfc822 addresses.
+ * The strings are allocated here.
+ *
+ * Args: head -- A passed in TakeAddr line, usually will be the first
+ *
+ * Result: returns an allocated array of pointers to allocated strings
+ */
+char **
+list_of_checked(TA_S *head)
+{
+ TA_S *p;
+ int count;
+ char **list, **cur;
+ ADDRESS *a;
+
+ /* first count them */
+ for(p = head, count = 0; p; p = next_sel_taline(p)){
+ if(p->checked && !p->skip_it){
+ if(p->frwrded){
+ /*
+ * Remove fullname, fcc, comment, and nickname since not
+ * appropriate for list values.
+ */
+ if(p->fullname)
+ fs_give((void **)&p->fullname);
+ if(p->fcc)
+ fs_give((void **)&p->fcc);
+ if(p->comment)
+ fs_give((void **)&p->comment);
+ if(p->nickname)
+ fs_give((void **)&p->nickname);
+
+ for(a = p->addr; a; a = a->next)
+ count++;
+ }
+ else{
+ /*
+ * Don't even attempt to include bogus addresses in
+ * the list. If the user wants to get at those, they
+ * have to try taking only that single address.
+ */
+ if(p->addr && p->addr->host && p->addr->host[0] == '.')
+ p->skip_it = 1;
+ else
+ count++;
+ }
+ }
+ }
+
+ /* allocate pointers */
+ list = (char **)fs_get((count + 1) * sizeof(char *));
+ memset((void *)list, 0, (count + 1) * sizeof(char *));
+
+ cur = list;
+
+ /* allocate and point to address strings */
+ for(p = head; p; p = next_sel_taline(p)){
+ if(p->checked && !p->skip_it){
+ if(p->frwrded)
+ for(a = p->addr; a; a = a->next){
+ ADDRESS *next_addr;
+ char *bufp;
+ size_t len;
+
+ next_addr = a->next;
+ a->next = NULL;
+ len = est_size(a);
+ bufp = (char *) fs_get(len * sizeof(char));
+ *cur++ = cpystr(addr_string(a, bufp, len));
+ a->next = next_addr;
+ fs_give((void **)&bufp);
+ }
+ else
+ *cur++ = cpystr(p->strvalue);
+ }
+ }
+
+ return(list);
+}
+
+/* jpf: This used to be the guts of cmd_take_addr, but I made this
+ * function so I could generalize with WP. It still has some display
+ * stuff that we need make sure not to do when in WP mode.
+ *
+ * Set things up for when we take addresses
+ *
+ * Args: ps -- pine state
+ * msgmap -- the MessageMap
+ * ta_ret -- the take addr lines
+ * selected_num -- the number of selectable addresses
+ * flags -- takeaddr flags, like whether or not
+ * we're doing aggs, and whether to do prompts
+ *
+ * Returns: -1 on "failure"
+ */
+int
+set_up_takeaddr(int cmd, struct pine *ps, MSGNO_S *msgmap, TA_S **ta_ret,
+ int *selected_num, int flags, int (*att_addr_f)(TA_S *, int))
+{
+ long i;
+ ENVELOPE *env;
+ int how_many_selected = 0,
+ added, rtype = 0,
+ we_cancel = 0,
+ special_processing = 0,
+ select_froms = 0;
+ TA_S *current = NULL,
+ *prev_comment_line,
+ *ta;
+ BODY **body_h,
+ *special_body = NULL,
+ *body = NULL;
+
+ dprint((2, "\n - taking address into address book - \n"));
+
+ if(!(cmd == 'a' || cmd == 'e'))
+ return -1;
+
+ if((flags & TA_AGG) && !pseudo_selected(ps_global->mail_stream, msgmap))
+ return -1;
+
+ if(mn_get_total(msgmap) > 0 && mn_total_cur(msgmap) == 1)
+ special_processing = 1;
+
+ if(flags & TA_AGG)
+ select_froms = 1;
+
+ /* this is a non-selectable label */
+ current = fill_in_ta(&current, (ADDRESS *) NULL, 0,
+ /* TRANSLATORS: This is a heading describing some addresses the
+ user will have the chance to choose from. */
+ _(" These entries are taken from the attachments "));
+ prev_comment_line = current;
+
+ /*
+ * Add addresses from special attachments for each message.
+ */
+ added = 0;
+ for(i = mn_first_cur(msgmap); i > 0L; i = mn_next_cur(msgmap)){
+ env = pine_mail_fetchstructure(ps->mail_stream, mn_m2raw(msgmap, i), &body);
+ if(!env){
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Can't take address into address book. Error accessing folder"));
+ rtype = -1;
+ goto doneskee;
+ }
+
+ added += process_vcard_atts(ps->mail_stream, mn_m2raw(msgmap, i),
+ body, body, NULL, &current);
+ }
+
+ if(!(flags & TA_AGG)
+ && added > 1
+ && att_addr_f
+ && (*att_addr_f)(current, added) <= 0)
+ goto doneskee;
+
+ if(!(flags & TA_NOPROMPT))
+ we_cancel = busy_cue(NULL, NULL, 1);
+
+ /*
+ * add a comment line to separate attachment address lines
+ * from header address lines
+ */
+ if(added > 0){
+ current = fill_in_ta(&current, (ADDRESS *) NULL, 0,
+ /* TRANSLATORS: msg is message */
+ _(" These entries are taken from the msg headers "));
+ prev_comment_line = current;
+ how_many_selected += added;
+ select_froms = 0;
+ }
+ else{ /* turn off header comment, and no separator comment */
+ prev_comment_line->print = 0;
+ prev_comment_line = NULL;
+ }
+
+ /*
+ * Add addresses from headers of messages.
+ */
+ for(i = mn_first_cur(msgmap); i > 0L; i = mn_next_cur(msgmap)){
+
+ if(special_processing)
+ body_h = &special_body;
+ else
+ body_h = NULL;
+
+ env = pine_mail_fetchstructure(ps->mail_stream, mn_m2raw(msgmap, i),
+ body_h);
+ if(!env){
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ _("Can't take address into address book. Error accessing folder"));
+ rtype = -1;
+ goto doneskee;
+ }
+
+ added = add_addresses_to_talist(ps, i, "from", &current,
+ env->from, select_froms);
+ if(select_froms)
+ how_many_selected += added;
+
+ if(!address_is_same(env->from, env->reply_to))
+ (void)add_addresses_to_talist(ps, i, "reply-to", &current,
+ env->reply_to, 0);
+
+ if(!address_is_same(env->from, env->sender))
+ (void)add_addresses_to_talist(ps, i, "sender", &current,
+ env->sender, 0);
+
+ (void)add_addresses_to_talist(ps, i, "to", &current, env->to, 0);
+ (void)add_addresses_to_talist(ps, i, "cc", &current, env->cc, 0);
+ (void)add_addresses_to_talist(ps, i, "bcc", &current, env->bcc, 0);
+ }
+
+ /*
+ * Check to see if we added an explanatory line about the
+ * header addresses but no header addresses. If so, remove the
+ * explanatory line.
+ */
+ if(prev_comment_line){
+ how_many_selected -=
+ eliminate_dups_and_us(first_sel_taline(current));
+ for(ta = prev_comment_line->next; ta; ta = ta->next)
+ if(!ta->skip_it)
+ break;
+
+ /* all entries were skip_it entries, turn off print */
+ if(!ta){
+ prev_comment_line->print = 0;
+ prev_comment_line = NULL;
+ }
+ }
+
+ /*
+ * If there's only one message we let the user view it using ^T
+ * from the header editor.
+ */
+ if(!(flags & TA_NOPROMPT) && special_processing && env && special_body){
+ msgno_for_pico_callback = mn_m2raw(msgmap, mn_first_cur(msgmap));
+ body_for_pico_callback = special_body;
+ env_for_pico_callback = env;
+ }
+ else{
+ env_for_pico_callback = NULL;
+ body_for_pico_callback = NULL;
+ }
+
+ current = fill_in_ta(&current, (ADDRESS *)NULL, 0,
+ _(" Below this line are some possibilities taken from the text of the msg "));
+ prev_comment_line = current;
+
+ /*
+ * Add addresses from the text of the body.
+ */
+ added = 0;
+ for(i = mn_first_cur(msgmap); i > 0L; i = mn_next_cur(msgmap)){
+
+ body = NULL;
+ env = pine_mail_fetchstructure(ps->mail_stream, mn_m2raw(msgmap, i),
+ &body);
+ if(env && body)
+ added += grab_addrs_from_body(ps->mail_stream,
+ mn_m2raw(msgmap, i),
+ body, &current);
+ }
+
+ if(added == 0){
+ prev_comment_line->print = 0;
+ prev_comment_line = NULL;
+ }
+
+ /*
+ * Check to see if all we added are dups.
+ * If so, remove the comment line.
+ */
+ if(prev_comment_line){
+ how_many_selected -= eliminate_dups_and_us(first_sel_taline(current));
+ for(ta = prev_comment_line->next; ta; ta = ta->next)
+ if(!ta->skip_it)
+ break;
+
+ /* all entries were skip_it entries, turn off print */
+ if(!ta)
+ prev_comment_line->print = 0;
+ }
+
+ if(we_cancel)
+ cancel_busy_cue(-1);
+
+doneskee:
+ env_for_pico_callback = NULL;
+ body_for_pico_callback = NULL;
+
+ ps->mangled_screen = 1;
+
+ if(flags & TA_AGG)
+ restore_selected(msgmap);
+
+ *ta_ret = current;
+
+ if(selected_num)
+ *selected_num = how_many_selected;
+
+ return(rtype);
+}
+
+
+int
+convert_ta_to_lines(TA_S *ta_list, LINES_TO_TAKE **old_current)
+{
+ ADDRESS *a;
+ TA_S *ta;
+ LINES_TO_TAKE *new_current;
+ char *exportval, *printval;
+ int ret = 0;
+
+ for(ta = first_sel_taline(ta_list);
+ ta;
+ ta = next_sel_taline(ta)){
+ if(ta->skip_it)
+ continue;
+
+ if(ta->frwrded){
+ if(ta->fullname)
+ fs_give((void **)&ta->fullname);
+ if(ta->fcc)
+ fs_give((void **)&ta->fcc);
+ if(ta->comment)
+ fs_give((void **)&ta->comment);
+ if(ta->nickname)
+ fs_give((void **)&ta->nickname);
+ }
+ else if(ta->addr && ta->addr->host && ta->addr->host[0] == '.')
+ ta->skip_it = 1;
+
+ if(ta->frwrded){
+ for(a = ta->addr; a; a = a->next){
+ ADDRESS *next_addr;
+
+ if(a->host && a->host[0] == '.')
+ continue;
+
+ next_addr = a->next;
+ a->next = NULL;
+
+ exportval = cpystr(simple_addr_string(a, tmp_20k_buf,
+ SIZEOF_20KBUF));
+ if(!exportval || !exportval[0]){
+ if(exportval)
+ fs_give((void **)&exportval);
+
+ a->next = next_addr;
+ continue;
+ }
+
+ printval = addr_list_string(a, NULL, 0);
+ if(!printval || !printval[0]){
+ if(printval)
+ fs_give((void **)&printval);
+
+ printval = cpystr(exportval);
+ }
+
+ new_current = new_ltline(old_current);
+ new_current->flags = LT_NONE;
+
+ new_current->printval = printval;
+ new_current->exportval = exportval;
+ a->next = next_addr;
+ ret++;
+ }
+ }
+ else{
+ if(ta->addr){
+ exportval = cpystr(simple_addr_string(ta->addr, tmp_20k_buf,
+ SIZEOF_20KBUF));
+ if(exportval && exportval[0]){
+ new_current = new_ltline(old_current);
+ new_current->flags = LT_NONE;
+
+ new_current->exportval = exportval;
+
+ if(ta->strvalue && ta->strvalue[0])
+ new_current->printval = cpystr(ta->strvalue);
+ else
+ new_current->printval = cpystr(new_current->exportval);
+
+ ret++;
+ }
+ else if(exportval)
+ fs_give((void **)&exportval);
+ }
+ }
+ }
+
+ return(ret);
+}
+
+
+/*
+ * new_ltline - create new LINES_TO_TAKE, zero it out,
+ * and insert it after current.
+ * NOTE current gets set to the new current.
+ */
+LINES_TO_TAKE *
+new_ltline(LINES_TO_TAKE **current)
+{
+ LINES_TO_TAKE *p;
+
+ p = (LINES_TO_TAKE *)fs_get(sizeof(LINES_TO_TAKE));
+ memset((void *)p, 0, sizeof(LINES_TO_TAKE));
+ if(current){
+ if(*current){
+ p->next = (*current)->next;
+ (*current)->next = p;
+ p->prev = *current;
+ if(p->next)
+ p->next->prev = p;
+ }
+
+ *current = p;
+ }
+
+ return(p);
+}
+
+
+int
+add_addresses_to_talist(struct pine *ps, long int msgno, char *field,
+ TA_S **old_current, struct mail_address *adrlist, int checked)
+{
+ ADDRESS *addr;
+ int added = 0;
+
+ for(addr = adrlist; addr; addr = addr->next){
+ if(addr->host && addr->host[0] == '.'){
+ char *h, *fields[2];
+
+ fields[0] = field;
+ fields[1] = NULL;
+ if((h = pine_fetchheader_lines(ps->mail_stream, msgno,NULL,fields)) != NULL){
+ char *p, fname[32];
+ int q;
+
+ q = strlen(h);
+
+ snprintf(fname, sizeof(fname), "%s:", field);
+ for(p = h; (p = strstr(p, fname)) != NULL; )
+ rplstr(p, q-(p-h), strlen(fname), ""); /* strip field strings */
+
+ sqznewlines(h); /* blat out CR's & LF's */
+ for(p = h; (p = strchr(p, TAB)) != NULL; )
+ *p++ = ' '; /* turn TABs to space */
+
+ if(*h){
+ unsigned long l;
+ ADDRESS *new_addr;
+ size_t ll;
+
+ new_addr = (ADDRESS *)fs_get(sizeof(ADDRESS));
+ memset(new_addr, 0, sizeof(ADDRESS));
+
+ /*
+ * Base64 armor plate the gunk to protect against
+ * c-client quoting in output.
+ */
+ p = (char *)rfc822_binary((void *)h,
+ (unsigned long)strlen(h), &l);
+ sqznewlines(p);
+ ll = strlen(p) + 3;
+ new_addr->mailbox = (char *) fs_get((ll+1) * sizeof(char));
+ snprintf(new_addr->mailbox, ll+1, "&%s", p);
+ fs_give((void **)&p);
+ new_addr->host = cpystr(RAWFIELD);
+ fill_in_ta(old_current, new_addr, 0, (char *)NULL);
+ mail_free_address(&new_addr);
+ }
+
+ fs_give((void **)&h);
+ }
+
+ return(0);
+ }
+ }
+
+ /* no syntax errors in list, add them all */
+ for(addr = adrlist; addr; addr = addr->next){
+ fill_in_ta(old_current, addr, checked, (char *)NULL);
+ added++;
+ }
+
+ return(added);
+}
+
+
+/*
+ * Look for Text/directory attachments and process them.
+ * Return number of lines added to the ta_list.
+ */
+int
+process_vcard_atts(MAILSTREAM *stream, long int msgno,
+ struct mail_bodystruct *root, struct mail_bodystruct *body,
+ char *partnum, TA_S **ta_list)
+{
+ char *addrs, /* comma-separated list of addresses */
+ *value,
+ *encoded,
+ *escval,
+ *nickname,
+ *fullname,
+ *struct_name,
+ *fcc,
+ *tag,
+ *decoded,
+ *num = NULL,
+ *defaulttype = NULL,
+ *charset = NULL,
+ *altcharset,
+ *comma,
+ *p,
+ *comment,
+ *title,
+ **lines = NULL,
+ **ll;
+ size_t space;
+ int used = 0,
+ is_encoded = 0,
+ vcard_nesting = 0,
+ selected = 0;
+ PART *part;
+ PARAMETER *parm;
+ static char a_comma = ',';
+
+ /*
+ * Look through all the subparts searching for our special type.
+ */
+ if(body && body->type == TYPEMULTIPART){
+ for(part = body->nested.part; part; part = part->next)
+ selected += process_vcard_atts(stream, msgno, root, &part->body,
+ partnum, ta_list);
+ }
+ /* only look in rfc822 subtype of type message */
+ else if(body && body->type == TYPEMESSAGE
+ && body->subtype && !strucmp(body->subtype, "rfc822")){
+ selected += process_vcard_atts(stream, msgno, root,
+ body->nested.msg->body,
+ partnum, ta_list);
+ }
+ /* found one (TYPEAPPLICATION for back compat.) */
+ else if(body && MIME_VCARD(body->type,body->subtype)){
+
+ dprint((4, "\n - found abook attachment to process - \n"));
+
+ /*
+ * The Text/directory type has a possible parameter called
+ * "defaulttype" that we need to look for (this is from an old draft
+ * and is supported only for backwards compatibility). There is
+ * also a possible default "charset". (Default charset is also from
+ * an old draft and is no longer allowed.) We don't care about any of
+ * the other parameters.
+ */
+ for(parm = body->parameter; parm; parm = parm->next)
+ if(!strucmp("defaulttype", parm->attribute))
+ break;
+
+ if(parm)
+ defaulttype = parm->value;
+
+ /* ...and look for possible charset parameter */
+ for(parm = body->parameter; parm; parm = parm->next)
+ if(!strucmp("charset", parm->attribute))
+ break;
+
+ if(parm)
+ charset = parm->value;
+
+ num = partnum ? cpystr(partnum) : partno(root, body);
+ lines = detach_vcard_att(stream, msgno, body, num);
+ if(num)
+ fs_give((void **)&num);
+
+ nickname = fullname = comment = title = fcc = struct_name = NULL;
+#define CHUNK (size_t)500
+ space = CHUNK;
+ /* make comma-separated list of email addresses in addrs */
+ addrs = (char *)fs_get((space+1) * sizeof(char));
+ *addrs = '\0';
+ for(ll = lines; ll && *ll; ll++){
+ altcharset = NULL;
+ decoded = NULL;
+ escval = NULL;
+ is_encoded = 0;
+ value = getaltcharset(*ll, &tag, &altcharset, &is_encoded);
+
+ if(!value || !tag){
+ if(tag)
+ fs_give((void **)&tag);
+
+ if(altcharset)
+ fs_give((void **)&altcharset);
+
+ continue;
+ }
+
+ if(is_encoded){
+ unsigned long length;
+
+ decoded = (char *)rfc822_base64((unsigned char *)value,
+ (unsigned long)strlen(value),
+ &length);
+ if(decoded){
+ decoded[length] = '\0';
+ value = decoded;
+ }
+ else
+ value = "<malformed B64 data>";
+ }
+
+ /* support default tag (back compat.) */
+ if(*tag == '\0' && defaulttype){
+ fs_give((void **)&tag);
+ tag = cpystr(defaulttype);
+ }
+
+ if(!strucmp(tag, "begin")){ /* vCard BEGIN */
+ vcard_nesting++;
+ }
+ else if(!strucmp(tag, "end")){ /* vCard END */
+ if(--vcard_nesting == 0){
+ if(title){
+ if(!comment)
+ comment = title;
+ else
+ fs_give((void **)&title);
+ }
+
+ /* add this entry */
+ selected += vcard_to_ta(addrs, fullname, struct_name,
+ nickname, comment, fcc, ta_list);
+ if(addrs)
+ *addrs = '\0';
+
+ used = 0;
+ nickname = fullname = comment = title = fcc = NULL;
+ struct_name = NULL;
+ }
+ }
+ /* add another address to addrs */
+ else if(!strucmp(tag, "email")){
+ if(*value){
+ escval = vcard_unescape(value);
+ encoded = encode_fullname_of_addrstring(escval,
+ (altcharset && *altcharset) ? altcharset
+ : (charset && *charset)
+ ? charset
+ : ps_global->posting_charmap);
+ if(encoded){
+ /* allocate more space */
+ if((used + strlen(encoded) + 1) > space){
+ space += CHUNK;
+ fs_resize((void **)&addrs, (space+1)*sizeof(char));
+ }
+
+ if(*addrs){
+ strncat(addrs, ",", space+1-1-strlen(addrs));
+ addrs[space-1] = '\0';
+ }
+
+ strncat(addrs, encoded, space+1-1-strlen(addrs));
+ addrs[space-1] = '\0';
+ used += (strlen(encoded) + 1);
+ fs_give((void **)&encoded);
+ }
+ }
+ }
+ else if(!strucmp(tag, "note") || !strucmp(tag, "misc")){
+ if(*value){
+ escval = vcard_unescape(value);
+ encoded = rfc1522_encode(tmp_20k_buf, SIZEOF_20KBUF,
+ (unsigned char *)escval,
+ (altcharset && *altcharset) ? altcharset
+ : (charset && *charset)
+ ? charset
+ : ps_global->posting_charmap);
+ if(encoded){
+ /* Last one wins */
+ if(comment)
+ fs_give((void **)&comment);
+
+ comment = cpystr(encoded);
+ }
+ }
+ }
+ else if(!strucmp(tag, "title")){
+ if(*value){
+ escval = vcard_unescape(value);
+ encoded = rfc1522_encode(tmp_20k_buf, SIZEOF_20KBUF,
+ (unsigned char *)escval,
+ (altcharset && *altcharset) ? altcharset
+ : (charset && *charset)
+ ? charset
+ : ps_global->posting_charmap);
+ if(encoded){
+ /* Last one wins */
+ if(title)
+ fs_give((void **)&title);
+
+ title = cpystr(encoded);
+ }
+ }
+ }
+ else if(!strucmp(tag, "x-fcc")){
+ if(*value){
+ escval = vcard_unescape(value);
+ encoded = rfc1522_encode(tmp_20k_buf, SIZEOF_20KBUF,
+ (unsigned char *)escval,
+ (altcharset && *altcharset) ? altcharset
+ : (charset && *charset)
+ ? charset
+ : ps_global->posting_charmap);
+ if(encoded){
+ /* Last one wins */
+ if(fcc)
+ fs_give((void **)&fcc);
+
+ fcc = cpystr(encoded);
+ }
+ }
+ }
+ else if(!strucmp(tag, "fn") || !strucmp(tag, "cn")){
+ if(*value){
+ escval = vcard_unescape(value);
+ encoded = rfc1522_encode(tmp_20k_buf, SIZEOF_20KBUF,
+ (unsigned char *)escval,
+ (altcharset && *altcharset) ? altcharset
+ : (charset && *charset)
+ ? charset
+ : ps_global->posting_charmap);
+ if(encoded){
+ /* Last one wins */
+ if(fullname)
+ fs_give((void **)&fullname);
+
+ fullname = cpystr(encoded);
+ }
+ }
+ }
+ else if(!strucmp(tag, "n")){
+ /*
+ * This is a structured name field. It has up to five
+ * fields separated by ';'. The fields are Family Name (which
+ * we'll take to be Last name for our purposes), Given Name
+ * (First name for us), additional names, prefixes, and
+ * suffixes. We'll ignore prefixes and suffixes.
+ *
+ * If we find one of these records we'll use it in preference
+ * to the formatted name field (fn).
+ */
+ if(*value && *value != ';'){
+ char *last, *first, *middle = NULL, *rest = NULL;
+ char *esclast, *escfirst, *escmiddle;
+ static char a_semi = ';';
+
+ last = value;
+
+ first = NULL;
+ p = last;
+ while(p && (first = strindex(p, a_semi)) != NULL){
+ if(first > last && first[-1] != '\\')
+ break;
+ else
+ p = first + 1;
+ }
+
+ if(first)
+ *first = '\0';
+
+ if(first && *(first+1) && *(first+1) != a_semi){
+ first++;
+
+ middle = NULL;
+ p = first;
+ while(p && (middle = strindex(p, a_semi)) != NULL){
+ if(middle > first && middle[-1] != '\\')
+ break;
+ else
+ p = middle + 1;
+ }
+
+ if(middle)
+ *middle = '\0';
+
+ if(middle && *(middle+1) && *(middle+1) != a_semi){
+ middle++;
+
+ rest = NULL;
+ p = middle;
+ while(p && (rest = strindex(p, a_semi)) != NULL){
+ if(rest > middle && rest[-1] != '\\')
+ break;
+ else
+ p = rest + 1;
+ }
+
+ /* we don't care about the rest */
+ if(rest)
+ *rest = '\0';
+ }
+ else
+ middle = NULL;
+ }
+ else
+ first = NULL;
+
+ /*
+ * Structured name pieces can have multiple values.
+ * We're just going to take the first value in each part.
+ * Skip excaped commas, though.
+ */
+ p = last;
+ while(p && (comma = strindex(p, a_comma)) != NULL){
+ if(comma > last && comma[-1] != '\\'){
+ *comma = '\0';
+ break;
+ }
+ else
+ p = comma + 1;
+ }
+
+ p = first;
+ while(p && (comma = strindex(p, a_comma)) != NULL){
+ if(comma > first && comma[-1] != '\\'){
+ *comma = '\0';
+ break;
+ }
+ else
+ p = comma + 1;
+ }
+
+ p = middle;
+ while(p && (comma = strindex(p, a_comma)) != NULL){
+ if(comma > middle && comma[-1] != '\\'){
+ *comma = '\0';
+ break;
+ }
+ else
+ p = comma + 1;
+ }
+
+ esclast = vcard_unescape(last);
+ escfirst = vcard_unescape(first);
+ escmiddle = vcard_unescape(middle);
+ snprintf(tmp_20k_buf+10000, SIZEOF_20KBUF-10000, "%s%s%s%s%s",
+ esclast ? esclast : "",
+ escfirst ? ", " : "",
+ escfirst ? escfirst : "",
+ escmiddle ? " " : "",
+ escmiddle ? escmiddle : "");
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+
+ encoded = rfc1522_encode(tmp_20k_buf, SIZEOF_20KBUF-10000,
+ (unsigned char *)tmp_20k_buf+10000,
+ (altcharset && *altcharset) ? altcharset
+ : (charset && *charset)
+ ? charset
+ : ps_global->posting_charmap);
+ tmp_20k_buf[SIZEOF_20KBUF-10000-1] = '\0';
+
+ if(esclast && *esclast && escfirst && *escfirst){
+ if(struct_name)
+ fs_give((void **)&struct_name);
+
+ struct_name = cpystr(encoded);
+ }
+ else{
+ /* in case we don't get a fullname better than this */
+ if(!fullname)
+ fullname = cpystr(encoded);
+ }
+
+ if(esclast)
+ fs_give((void **)&esclast);
+ if(escfirst)
+ fs_give((void **)&escfirst);
+ if(escmiddle)
+ fs_give((void **)&escmiddle);
+ }
+ }
+ /* suggested nickname */
+ else if(!strucmp(tag, "nickname") || !strucmp(tag, "x-nickname")){
+ if(*value){
+ /* Last one wins */
+ if(nickname)
+ fs_give((void **)&nickname);
+
+ /*
+ * Nickname can have multiple values. We're just
+ * going to take the first. Skip escaped commas, though.
+ */
+ p = value;
+ while(p && (comma = strindex(p, a_comma)) != NULL){
+ if(comma > value && comma[-1] != '\\'){
+ *comma = '\0';
+ break;
+ }
+ else
+ p = comma + 1;
+ }
+
+ nickname = vcard_unescape(value);
+ }
+ }
+
+ if(tag)
+ fs_give((void **)&tag);
+
+ if(altcharset)
+ fs_give((void **)&altcharset);
+
+ if(decoded)
+ fs_give((void **)&decoded);
+
+ if(escval)
+ fs_give((void **)&escval);
+ }
+
+ if(title){
+ if(!comment)
+ comment = title;
+ else
+ fs_give((void **)&title);
+ }
+
+ if(fullname || struct_name || nickname || fcc
+ || comment || (addrs && *addrs))
+ selected += vcard_to_ta(addrs, fullname, struct_name, nickname,
+ comment, fcc, ta_list);
+
+ if(addrs)
+ fs_give((void **)&addrs);
+
+ free_list_array(&lines);
+ }
+
+ return(selected);
+}
+
+
+int
+cmp_swoop_list(const qsort_t *a1, const qsort_t *a2)
+{
+ SWOOP_S *x = (SWOOP_S *)a1;
+ SWOOP_S *y = (SWOOP_S *)a2;
+
+ if(x->dup){
+ if(y->dup)
+ return((x->dst_enum > y->dst_enum) ? -1
+ : (x->dst_enum == y->dst_enum) ? 0 : 1);
+ else
+ return(-1);
+ }
+ else if(y->dup)
+ return(1);
+ else
+ return((x > y) ? -1 : (x == y) ? 0 : 1); /* doesn't matter */
+}
+
+
+
+/*
+ * Take the split out contents of a vCard entry and turn them into a TA.
+ *
+ * Always returns 1, the number of TAs added to ta_list.
+ */
+int
+vcard_to_ta(char *addrs, char *fullname, char *better_fullname, char *nickname,
+ char *comment, char *fcc, TA_S **ta_list)
+{
+ ADDRESS *addrlist = NULL;
+
+ /*
+ * Parse it into an addrlist, which is what fill_in_ta wants
+ * to see.
+ */
+ rfc822_parse_adrlist(&addrlist, addrs, fakedomain);
+ if(!addrlist)
+ addrlist = mail_newaddr(); /* empty addr, to make right thing happen */
+
+ *ta_list = fill_in_ta(ta_list, addrlist, 1,
+ fullname ? fullname
+ : better_fullname ? better_fullname
+ : "Forwarded Entry");
+ (*ta_list)->nickname = nickname ? nickname : cpystr("");
+ (*ta_list)->comment = comment;
+ (*ta_list)->fcc = fcc;
+
+ /*
+ * We are tempted to want to call switch_to_last_comma_first() here when
+ * we don't have a better_fullname (which is already last, first).
+ * But, since this is the way it was sent to us we should probably leave
+ * it alone. That means that if we use a fullname culled from the
+ * body of a message, or from a header, or from an "N" vcard record,
+ * we will make it be Last, First; but if we use one from an "FN" record
+ * we won't.
+ */
+ (*ta_list)->fullname = better_fullname ? better_fullname
+ : fullname;
+
+ if((*ta_list)->nickname)
+ convert_possibly_encoded_str_to_utf8(&(*ta_list)->nickname);
+
+ if((*ta_list)->comment)
+ convert_possibly_encoded_str_to_utf8(&(*ta_list)->comment);
+
+ if((*ta_list)->fcc)
+ convert_possibly_encoded_str_to_utf8(&(*ta_list)->fcc);
+
+ if((*ta_list)->fullname)
+ convert_possibly_encoded_str_to_utf8(&(*ta_list)->fullname);
+
+ /*
+ * The TA list will free its members but we have to take care of this
+ * extra one that isn't assigned to a TA member.
+ */
+ if(better_fullname && fullname)
+ fs_give((void **)&fullname);
+
+ if(addrlist)
+ mail_free_address(&addrlist);
+
+ return(1);
+}
+
+
+/*
+ * Look through line which is supposed to look like
+ *
+ * type;charset=iso-8859-1;encoding=b: value
+ *
+ * Type might be email, or nickname, ... It is optional because of the
+ * defaulttype parameter (there isn't such a thing as defaulttype parameters
+ * as of Nov. 96 draft). The semicolon and colon are special chars. Each
+ * parameter in this line is a semicolon followed by the parameter type "="
+ * the parameter value. Parameters are optional, too. There is always a colon,
+ * followed by value. Whitespace can be everywhere up to where value starts.
+ * There is also an optional <group> "." preceding the type, which we will
+ * ignore. It's for grouping related lines.
+ * (As of July, 97 draft, it is no longer permissible to include a charset
+ * parameter in each line. Only one is permitted in the content-type mime hdr.)
+ * (Also as of July, 97 draft, all white space is supposed to be significant.
+ * We will continue to skip white space surrounding '=' and so forth for
+ * backwards compatibility and because it does no harm in almost all cases.)
+ * (As of Nov, 97 draft, some white space is not significant. That is, it is
+ * allowed in some places.)
+ *
+ *
+ * Args: line -- the line to look at
+ * type -- this is a return value, and is an allocated copy of
+ * the type, freed by the caller. For example, "email".
+ * alt -- this is a return value, and is an allocated copy of
+ * the value of the alternate charset, to be freed by
+ * the caller. For example, this might be "iso-8859-2".
+ * encoded -- this is a return value, and is set to 1 if the value
+ * is b-encoded
+ *
+ * Return value: a pointer to the start of value, or NULL if the syntax
+ * isn't right. It's possible for value to be equal to "".
+ */
+char *
+getaltcharset(char *line, char **type, char **alt, int *encoded)
+{
+ char *p, *q, *left_semi, *group_dot, *colon;
+ char *start_of_enc, *start_of_cset, tmpsave;
+ static char *cset = "charset";
+ static char *enc = "encoding";
+
+ if(type)
+ *type = NULL;
+
+ if(alt)
+ *alt = NULL;
+
+ if(encoded)
+ *encoded = 0;
+
+ colon = strindex(line, ':');
+ if(!colon)
+ return NULL;
+
+ left_semi = strindex(line, ';');
+ if(left_semi && left_semi > colon)
+ left_semi = NULL;
+
+ group_dot = strindex(line, '.');
+ if(group_dot && (group_dot > colon || (left_semi && group_dot > left_semi)))
+ group_dot = NULL;
+
+ /*
+ * Type is everything up to the semicolon, or the colon if no semicolon.
+ * However, we want to skip optional <group> ".".
+ */
+ if(type){
+ q = left_semi ? left_semi : colon;
+ tmpsave = *q;
+ *q = '\0';
+ *type = cpystr(group_dot ? group_dot+1 : line);
+ *q = tmpsave;
+ sqzspaces(*type);
+ }
+
+ if(left_semi && alt
+ && (p = srchstr(left_semi+1, cset))
+ && p < colon){
+ p += strlen(cset);
+ p = skip_white_space(p);
+ if(*p++ == '='){
+ p = skip_white_space(p);
+ if(p < colon){
+ start_of_cset = p;
+ p++;
+ while(p < colon && !isspace((unsigned char)*p) && *p != ';')
+ p++;
+
+ tmpsave = *p;
+ *p = '\0';
+ *alt = cpystr(start_of_cset);
+ *p = tmpsave;
+ }
+ }
+ }
+
+ if(encoded && left_semi
+ && (p = srchstr(left_semi+1, enc))
+ && p < colon){
+ p += strlen(enc);
+ p = skip_white_space(p);
+ if(*p++ == '='){
+ p = skip_white_space(p);
+ if(p < colon){
+ start_of_enc = p;
+ p++;
+ while(p < colon && !isspace((unsigned char)*p) && *p != ';')
+ p++;
+
+ if(*start_of_enc == 'b' && start_of_enc + 1 == p)
+ *encoded = 1;
+ }
+ }
+ }
+
+ p = colon + 1;
+
+ return(skip_white_space(p));
+}
+
+
+/*
+ * Return incoming_fullname in Last, First form.
+ * The passed in fullname should already be in UTF-8.
+ * If there is already a comma, we add quotes around the fullname so we can
+ * tell it isn't a Last, First comma but a real comma in the Fullname.
+ *
+ * Args: incoming_fullname --
+ * new_full -- passed in pointer to place to put new fullname
+ * nbuf -- size of new_full
+ * Returns: new_full
+ */
+void
+switch_to_last_comma_first(char *incoming_fullname, char *new_full, size_t nbuf)
+{
+ char save_value;
+ char *save_end, *nf, *inc, *p = NULL;
+
+ if(nbuf < 1)
+ return;
+
+ if(incoming_fullname == NULL){
+ new_full[0] = '\0';
+ return;
+ }
+
+ /* get rid of leading white space */
+ for(inc = incoming_fullname; *inc && isspace((unsigned char)*inc); inc++)
+ ;/* do nothing */
+
+ /* get rid of trailing white space */
+ for(p = inc + strlen(inc) - 1; *p && p >= inc &&
+ isspace((unsigned char)*p); p--)
+ ;/* do nothing */
+
+ save_end = ++p;
+ if(save_end == inc){
+ new_full[0] = '\0';
+ return;
+ }
+
+ save_value = *save_end; /* so we don't alter incoming_fullname */
+ *save_end = '\0';
+ nf = new_full;
+ memset(new_full, 0, nbuf); /* need this the way it is done below */
+
+ if(strindex(inc, ',') != NULL){
+ int add_quotes = 0;
+
+ /*
+ * Quote already existing comma.
+ *
+ * We'll get this wrong if it is already quoted but the quote
+ * doesn't start right at the beginning.
+ */
+ if(inc[0] != '"'){
+ add_quotes++;
+ if(nf-new_full < nbuf-1)
+ *nf++ = '"';
+ }
+
+ strncpy(nf, inc, MIN(nbuf - (add_quotes ? 3 : 1), nbuf-(nf-new_full)-1));
+ new_full[nbuf-1] = '\0';
+ if(add_quotes){
+ strncat(nf, "\"", nbuf-(nf-new_full)-1);
+ new_full[nbuf-1] = '\0';
+ }
+ }
+ else if(strindex(inc, SPACE)){
+ char *last, *end;
+
+ end = new_full + nbuf - 1;
+
+ /*
+ * Switch First Middle Last to Last, First Middle
+ */
+
+ /* last points to last word, which does exist */
+ last = skip_white_space(strrindex(inc, SPACE));
+
+ /* copy Last */
+ for(p = last; *p && nf < end-2 && nf-new_full < nbuf; *nf++ = *p++)
+ ;/* do nothing */
+
+ if(nf-new_full < nbuf-1)
+ *nf++ = ',';
+
+ if(nf-new_full < nbuf-1)
+ *nf++ = SPACE;
+
+ /* copy First Middle */
+ for(p = inc; p < last && nf < end && nf-new_full < nbuf-1; *nf++ = *p++)
+ ;/* do nothing */
+
+ new_full[nbuf-1] = '\0';
+
+ removing_trailing_white_space(new_full);
+ }
+ else
+ strncpy(new_full, inc, nbuf-1);
+
+ new_full[nbuf-1] = '\0';
+
+ *save_end = save_value;
+}
+
+
+/*
+ * Fetch this body part, content-decode it if necessary, split it into
+ * lines, and return the lines in an allocated array, which is freed
+ * by the caller. Folded lines are replaced by single long lines.
+ */
+char **
+detach_vcard_att(MAILSTREAM *stream, long int msgno, struct mail_bodystruct *body, char *partnum)
+{
+ unsigned long length;
+ char *text = NULL, /* raw text */
+ *dtext = NULL, /* content-decoded text */
+ **res = NULL, /* array of decoded lines */
+ *lptr, /* line pointer */
+ *next_line;
+ int i, count;
+
+ /* make our own copy of text so we can change it */
+ text = cpystr(mail_fetchbody_full(stream, msgno, partnum, &length,FT_PEEK));
+ text[length] = '\0';
+
+ /* decode the text */
+ switch(body->encoding){
+ default:
+ case ENC7BIT:
+ case ENC8BIT:
+ case ENCBINARY:
+ dtext = text;
+ break;
+
+ case ENCBASE64:
+ dtext = (char *)rfc822_base64((unsigned char *)text,
+ (unsigned long)strlen(text),
+ &length);
+ if(dtext)
+ dtext[length] = '\0';
+ else
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ "Malformed B64 data in address book attachment.");
+
+ if(text)
+ fs_give((void **)&text);
+
+ break;
+
+ case ENCQUOTEDPRINTABLE:
+ dtext = (char *)rfc822_qprint((unsigned char *)text,
+ (unsigned long)strlen(text),
+ &length);
+ if(dtext)
+ dtext[length] = '\0';
+ else
+ q_status_message(SM_ORDER | SM_DING, 3, 4,
+ "Malformed QP data in address book attachment.");
+
+ if(text)
+ fs_give((void **)&text);
+
+ break;
+ }
+
+ /* count the lines */
+ next_line = dtext;
+ count = 0;
+ for(lptr = next_line; lptr && *lptr; lptr = next_line){
+ for(next_line = lptr; *next_line; next_line++){
+ /*
+ * Find end of current line.
+ */
+ if(*next_line == '\r' && *(next_line+1) == '\n'){
+ next_line += 2;
+ /* not a folded line, count it */
+ if(!*next_line || (*next_line != SPACE && *next_line != TAB))
+ break;
+ }
+ }
+
+ count++;
+ }
+
+ /* allocate space for resulting array of lines */
+ res = (char **)fs_get((count + 1) * sizeof(char *));
+ memset((void *)res, 0, (count + 1) * sizeof(char *));
+ next_line = dtext;
+ for(i=0, lptr=next_line; lptr && *lptr && i < count; lptr=next_line, i++){
+ /*
+ * Move next_line to start of next line and null terminate
+ * current line.
+ */
+ for(next_line = lptr; *next_line; next_line++){
+ /*
+ * Find end of current line.
+ */
+ if(*next_line == '\r' && *(next_line+1) == '\n'){
+ next_line += 2;
+ /* not a folded line, terminate it */
+ if(!*next_line || (*next_line != SPACE && *next_line != TAB)){
+ *(next_line-2) = '\0';
+ break;
+ }
+ }
+ }
+
+ /* turn folded lines into long lines in place */
+ vcard_unfold(lptr);
+ res[i] = cpystr(lptr);
+ }
+
+ if(dtext)
+ fs_give((void **)&dtext);
+
+ res[count] = '\0';
+ return(res);
+}
+
+
+/*
+ * Look for possible addresses in the first text part of a message for
+ * use by TakeAddr command.
+ * Returns the number of TA lines added.
+ */
+int
+grab_addrs_from_body(MAILSTREAM *stream, long int msgno,
+ struct mail_bodystruct *body, TA_S **ta_list)
+{
+#define MAXLINESZ 2000
+ char line[MAXLINESZ + 1];
+ STORE_S *so;
+ gf_io_t pc;
+ SourceType src = CharStar;
+ int added = 0, failure;
+
+ dprint((9, "\n - grab_addrs_from_body - \n"));
+
+ /*
+ * If it is text/plain or it is multipart with a first part of text/plain,
+ * we want to continue, else forget it.
+ */
+ if(!((body->type == TYPETEXT && body->subtype
+ && ALLOWED_SUBTYPE(body->subtype))
+ ||
+ (body->type == TYPEMULTIPART && body->nested.part
+ && body->nested.part->body.type == TYPETEXT
+ && ALLOWED_SUBTYPE(body->nested.part->body.subtype))))
+ return 0;
+
+#ifdef DOS
+ src = TmpFileStar;
+#endif
+
+ if((so = so_get(src, NULL, EDIT_ACCESS)) == NULL)
+ return 0;
+
+ gf_set_so_writec(&pc, so);
+
+ failure = !get_body_part_text(stream, body, msgno, "1", 0L, pc,
+ NULL, NULL, GBPT_NONE);
+
+ gf_clear_so_writec(so);
+
+ if(failure){
+ so_give(&so);
+ return 0;
+ }
+
+ so_seek(so, 0L, 0);
+
+ while(get_line_of_message(so, line, sizeof(line))){
+ ADDRESS *addr;
+ char save_end, *start, *tmp_a_string, *tmp_personal, *p;
+ int n;
+
+ /* process each @ in the line */
+ for(p = (char *) line; (start = mail_addr_scan(p, &n)); p = start + n){
+
+ tmp_personal = NULL;
+
+ if(start > line && *(start-1) == '<' && *(start+n) == '>'){
+ int words, in_quote;
+ char *fn_start;
+
+ /*
+ * Take a shot at looking for full name
+ * If we can find a colon maybe we've got a header line
+ * embedded in the body.
+ * This is real ad hoc.
+ */
+
+ /*
+ * Go back until we run into a character that probably
+ * isn't a fullname character.
+ */
+ fn_start = start-1;
+ in_quote = words = 0;
+ while(fn_start > p && (in_quote || !(*(fn_start-1) == ':'
+ || *(fn_start-1) == ';' || *(fn_start-1) == ','))){
+ fn_start--;
+ if(!in_quote && isspace((unsigned char)*fn_start))
+ words++;
+ else if(*fn_start == '"' &&
+ (fn_start == p || *(fn_start-1) != '\\')){
+ if(in_quote){
+ in_quote = 0;
+ break;
+ }
+ else
+ in_quote = 1;
+ }
+
+ if(words == 5)
+ break;
+ }
+
+ /* wasn't a real quote, forget about fullname */
+ if(in_quote)
+ fn_start = start-1;
+
+ /* Skip forward over the white space. */
+ while(isspace((unsigned char)*fn_start) || *fn_start == '(')
+ fn_start++;
+
+ /*
+ * Make sure the first word is capitalized.
+ * (just so it is more likely it is a name)
+ */
+ while(fn_start < start-1 &&
+ !(isupper(*fn_start) || *fn_start == '"')){
+ if(*fn_start == '('){
+ fn_start++;
+ continue;
+ }
+
+ /* skip forward over this word */
+ while(fn_start < start-1 &&
+ !isspace((unsigned char)*fn_start))
+ fn_start++;
+
+ while(fn_start < start-1 &&
+ isspace((unsigned char)*fn_start))
+ fn_start++;
+ }
+
+ if(fn_start < start-1){
+ char *fn_end, save_fn_end;
+
+ /* remove white space between fullname and start */
+ fn_end = start-1;
+ while(fn_end > fn_start
+ && isspace((unsigned char)*(fn_end - 1)))
+ fn_end--;
+
+ save_fn_end = *fn_end;
+ *fn_end = '\0';
+
+ /* remove matching quotes */
+ if(*fn_start == '"' && *(fn_end-1) == '"'){
+ fn_start++;
+ *fn_end = save_fn_end;
+ fn_end--;
+ save_fn_end = *fn_end;
+ *fn_end = '\0';
+ }
+
+ /* copy fullname */
+ if(*fn_start)
+ tmp_personal = cpystr(fn_start);
+
+ *fn_end = save_fn_end;
+ }
+ }
+
+
+ save_end = *(start+n);
+ *(start+n) = '\0';
+ /* rfc822_parse_adrlist feels free to destroy input so send copy */
+ tmp_a_string = cpystr(start);
+ *(start+n) = save_end;
+ addr = NULL;
+ ps_global->c_client_error[0] = '\0';
+ rfc822_parse_adrlist(&addr, tmp_a_string, fakedomain);
+ if(tmp_a_string)
+ fs_give((void **)&tmp_a_string);
+
+ if(tmp_personal){
+ if(addr){
+ if(addr->personal)
+ fs_give((void **)&addr->personal);
+
+ addr->personal = tmp_personal;
+ }
+ else
+ fs_give((void **)&tmp_personal);
+ }
+
+ if((addr && addr->host && addr->host[0] == '@') ||
+ ps_global->c_client_error[0]){
+ mail_free_address(&addr);
+ continue;
+ }
+
+ if(addr && addr->mailbox && addr->host){
+ added++;
+ *ta_list = fill_in_ta(ta_list, addr, 0, (char *)NULL);
+ }
+
+ if(addr)
+ mail_free_address(&addr);
+ }
+ }
+
+ so_give(&so);
+ return(added);
+}
+
+
+/*
+ * Get the next line of the object pointed to by source.
+ * Skips empty lines.
+ * Linebuf is a passed in buffer to put the line in.
+ * Linebuf is null terminated and \r and \n chars are removed.
+ * 0 is returned when there is no more input.
+ */
+int
+get_line_of_message(STORE_S *source, char *linebuf, int linebuflen)
+{
+ int pos = 0;
+ unsigned char c;
+
+ if(linebuflen < 2)
+ return 0;
+
+ while(so_readc(&c, source)){
+ if(c == '\n' || c == '\r'){
+ if(pos > 0)
+ break;
+ }
+ else{
+ linebuf[pos++] = c;
+ if(pos >= linebuflen - 2)
+ break;
+ }
+ }
+
+ linebuf[pos] = '\0';
+
+ return(pos);
+}
+
+
+/*
+ * Inserts a new entry based on addr in the TA list.
+ * Makes sure everything is UTF-8. That is,
+ * Values used for strvalue will be converted to UTF-8 first.
+ * Personal name fields of addr args will be converted to UTF-8.
+ *
+ * Args: old_current -- the TA list
+ * addr -- the address for this line
+ * checked -- start this line out checked
+ * print_string -- if non-null, this line is informational and is just
+ * printed out, it can't be selected
+ */
+TA_S *
+fill_in_ta(TA_S **old_current, struct mail_address *addr, int checked, char *print_string)
+{
+ TA_S *new_current;
+
+ /* c-client convention for group syntax, which we don't want to deal with */
+ if(!print_string && (!addr || !addr->mailbox || !addr->host))
+ new_current = *old_current;
+ else{
+
+ new_current = new_taline(old_current);
+ if(print_string && addr){ /* implied List when here */
+ new_current->frwrded = 1;
+ new_current->skip_it = 0;
+ new_current->print = 0;
+ new_current->checked = checked != 0;
+ new_current->addr = copyaddrlist(addr);
+ decode_addr_names_to_utf8(new_current->addr);
+ new_current->strvalue = cpystr(print_string);
+ convert_possibly_encoded_str_to_utf8(&new_current->strvalue);
+ }
+ else if(print_string){
+ new_current->frwrded = 0;
+ new_current->skip_it = 1;
+ new_current->print = 1;
+ new_current->checked = 0;
+ new_current->addr = 0;
+ new_current->strvalue = cpystr(print_string);
+ convert_possibly_encoded_str_to_utf8(&new_current->strvalue);
+ }
+ else{
+ new_current->frwrded = 0;
+ new_current->skip_it = 0;
+ new_current->print = 0;
+ new_current->checked = checked != 0;
+ new_current->addr = copyaddr(addr);
+ decode_addr_names_to_utf8(new_current->addr);
+ if(addr->host[0] == '.')
+ new_current->strvalue = cpystr("Error in address (ok to try Take anyway)");
+ else{
+ char *bufp;
+ size_t len;
+
+ len = est_size(new_current->addr);
+ bufp = (char *) fs_get(len * sizeof(char));
+ new_current->strvalue = cpystr(addr_string(new_current->addr, bufp, len));
+ fs_give((void **) &bufp);
+ }
+
+ convert_possibly_encoded_str_to_utf8(&new_current->strvalue);
+ }
+ }
+
+ return(new_current);
+}
+
+
+int
+eliminate_dups_and_us(TA_S *list)
+{
+ return(eliminate_dups_and_maybe_us(list, 1));
+}
+
+
+int
+eliminate_dups_but_not_us(TA_S *list)
+{
+ return(eliminate_dups_and_maybe_us(list, 0));
+}
+
+
+/*
+ * Look for dups in list and mark them so we'll skip them.
+ *
+ * We also throw out any addresses that are us (if us_too is set), since
+ * we won't usually want to take ourselves to the addrbook.
+ * On the otherhand, if there is nothing but us, we leave it.
+ *
+ * Don't toss out forwarded entries.
+ *
+ * Returns the number of dups eliminated that were also selected.
+ */
+int
+eliminate_dups_and_maybe_us(TA_S *list, int us_too)
+{
+ ADDRESS *a, *b;
+ TA_S *ta, *tb;
+ int eliminated = 0;
+
+ /* toss dupes */
+ for(ta = list; ta; ta = ta->next){
+
+ if(ta->skip_it || ta->frwrded) /* already tossed or forwarded */
+ continue;
+
+ a = ta->addr;
+
+ /* Check addr "a" versus tail of the list */
+ for(tb = ta->next; tb; tb = tb->next){
+ if(tb->skip_it || tb->frwrded) /* already tossed or forwarded */
+ continue;
+
+ b = tb->addr;
+ if(dup_addrs(a, b)){
+ if(ta->checked || !(tb->checked)){
+ /* throw out b */
+ if(tb->checked)
+ eliminated++;
+
+ tb->skip_it = 1;
+ }
+ else{ /* tb->checked */
+ /* throw out a */
+ ta->skip_it = 1;
+ break;
+ }
+ }
+ }
+ }
+
+ if(us_too){
+ /* check whether all remaining addrs are us */
+ for(ta = list; ta; ta = ta->next){
+
+ if(ta->skip_it) /* already tossed */
+ continue;
+
+ if(ta->frwrded) /* forwarded entry, so not us */
+ break;
+
+ a = ta->addr;
+
+ if(!address_is_us(a, ps_global))
+ break;
+ }
+
+ /*
+ * if at least one address that isn't us, remove all of us from
+ * the list
+ */
+ if(ta){
+ for(ta = list; ta; ta = ta->next){
+
+ if(ta->skip_it || ta->frwrded) /* already tossed or forwarded */
+ continue;
+
+ a = ta->addr;
+
+ if(address_is_us(a, ps_global)){
+ if(ta->checked)
+ eliminated++;
+
+ /* throw out a */
+ ta->skip_it = 1;
+ }
+ }
+ }
+ }
+
+ return(eliminated);
+}
+
+
+/*
+ * Returns 1 if x and y match, 0 if not.
+ */
+int
+dup_addrs(struct mail_address *x, struct mail_address *y)
+{
+ return(x && y && strucmp(x->mailbox, y->mailbox) == 0
+ && strucmp(x->host, y->host) == 0);
+}
diff --git a/pith/takeaddr.h b/pith/takeaddr.h
new file mode 100644
index 00000000..2476d386
--- /dev/null
+++ b/pith/takeaddr.h
@@ -0,0 +1,114 @@
+/*
+ * $Id: takeaddr.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_TAKEADDR_INCLUDED
+#define PITH_TAKEADDR_INCLUDED
+
+
+#include "../pith/adrbklib.h"
+#include "../pith/msgno.h"
+#include "../pith/state.h"
+#include "../pith/store.h"
+
+
+/*
+ * Information used to paint and maintain a line on the TakeAddr screen
+ */
+typedef struct takeaddr_line {
+ unsigned int checked:1;/* addr is selected */
+ unsigned int skip_it:1;/* skip this one */
+ unsigned int print:1; /* for printing only */
+ unsigned int frwrded:1;/* forwarded from another pine */
+ char *strvalue; /* alloc'd value string */
+ ADDRESS *addr; /* original ADDRESS this line came from */
+ char *nickname; /* The first TA may carry this extra */
+ char *fullname; /* information, or, */
+ char *fcc; /* all vcard ta's have it. */
+ char *comment;
+ struct takeaddr_line *next, *prev;
+} TA_S;
+
+typedef struct a_list {
+ int dup;
+ adrbk_cntr_t dst_enum;
+ TA_S *ta;
+} SWOOP_S;
+
+typedef struct lines_to_take {
+ char *printval;
+ char *exportval;
+ int flags;
+ struct lines_to_take *next, *prev;
+} LINES_TO_TAKE;
+
+#define LT_NONE 0x00
+#define LT_NOSELECT 0x01
+
+
+/*
+ * Flag definitions to control takeaddr behavior
+ */
+#define TA_NONE 0
+#define TA_AGG 1 /* if set, use the aggregate operation */
+#define TA_NOPROMPT 2 /* if set, we aren't in interactive mode */
+
+
+/* these should really be in ../pine */
+#define RS_NONE 0x00 /* rule_setup_type flags */
+#define RS_RULES 0x01 /* include Rules as option */
+#define RS_INCADDR 0x02 /* include Addrbook as option */
+#define RS_INCEXP 0x04 /* include Export as option */
+#define RS_INCFILTNOW 0x08 /* filter now */
+
+
+/* exported prototypes */
+int set_up_takeaddr(int, struct pine *, MSGNO_S *, TA_S **,
+ int *, int, int (*)(TA_S *, int));
+TA_S *pre_sel_taline(TA_S *);
+TA_S *pre_taline(TA_S *);
+TA_S *next_sel_taline(TA_S *);
+TA_S *next_taline(TA_S *);
+int ta_mark_all(TA_S *);
+int is_talist_of_one(TA_S *);
+int ta_unmark_all(TA_S *);
+TA_S *new_taline(TA_S **);
+void free_taline(TA_S **);
+void free_talines(TA_S **);
+void free_ltline(LINES_TO_TAKE **);
+void free_ltlines(LINES_TO_TAKE **);
+TA_S *first_sel_taline(TA_S *);
+TA_S *last_sel_taline(TA_S *);
+TA_S *first_taline(TA_S *);
+TA_S *first_checked(TA_S *);
+char **list_of_checked(TA_S *);
+int convert_ta_to_lines(TA_S *, LINES_TO_TAKE **);
+LINES_TO_TAKE *new_ltline(LINES_TO_TAKE **);
+int add_addresses_to_talist(struct pine *, long, char *, TA_S **, ADDRESS *, int);
+int process_vcard_atts(MAILSTREAM *, long, BODY *, BODY *, char *, TA_S **);
+int cmp_swoop_list(const qsort_t *, const qsort_t *);
+int vcard_to_ta(char *, char *, char *, char *, char *, char *, TA_S **);
+char *getaltcharset(char *, char **, char **, int *);
+void switch_to_last_comma_first(char *, char *, size_t);
+char **detach_vcard_att(MAILSTREAM *, long, BODY *, char *);
+int grab_addrs_from_body(MAILSTREAM *,long,BODY *,TA_S **);
+int get_line_of_message(STORE_S *, char *, int);
+TA_S *fill_in_ta(TA_S **, ADDRESS *, int, char *);
+int eliminate_dups_and_us(TA_S *);
+int eliminate_dups_but_not_us(TA_S *);
+int eliminate_dups_and_maybe_us(TA_S *, int);
+int dup_addrs(ADDRESS *, ADDRESS *);
+
+
+#endif /* PITH_TAKEADDR_INCLUDED */
diff --git a/pith/tempfile.c b/pith/tempfile.c
new file mode 100644
index 00000000..efe7c586
--- /dev/null
+++ b/pith/tempfile.c
@@ -0,0 +1,87 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: tempfile.c 770 2007-10-24 00:23:09Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/tempfile.h"
+
+
+/*
+ * Return the name of a file in the same directory as filename.
+ * Same as temp_nam except it figures out a name in the same directory.
+ * It also returns the name of the directory in ret_dir if ret_dir is
+ * not NULL. That has to be freed by caller. If return is not NULL the
+ * empty file has been created.
+ */
+char *
+tempfile_in_same_dir(char *filename, char *prefix, char **ret_dir)
+{
+#ifndef MAXPATH
+#define MAXPATH 1000 /* Longest file path we can deal with */
+#endif
+ char dir[MAXPATH+1];
+ char *dirp = NULL;
+ char *ret_file = NULL;
+
+ if(filename){
+ char *lc;
+
+ if((lc = last_cmpnt(filename)) != NULL){
+ int to_copy;
+
+ to_copy = (lc - filename > 1) ? (lc - filename - 1) : 1;
+ strncpy(dir, filename, MIN(to_copy, sizeof(dir)-1));
+ dir[MIN(to_copy, sizeof(dir)-1)] = '\0';
+ }
+ else{
+ dir[0] = '.';
+ dir[1] = '\0';
+ }
+
+ dirp = dir;
+ }
+
+
+ /* temp_nam creates ret_file */
+ ret_file = temp_nam(dirp, prefix);
+
+ /*
+ * If temp_nam can't write in dirp it puts the file in a temp directory
+ * anyway. We don't want that to happen to us.
+ */
+ if(dirp && ret_file && !in_dir(dirp, ret_file)){
+ our_unlink(ret_file);
+ fs_give((void **)&ret_file); /* sets it to NULL */
+ }
+
+ if(ret_file && ret_dir && dirp)
+ *ret_dir = cpystr(dirp);
+
+
+ return(ret_file);
+}
+
+
+/*
+ * Returns non-zero if dir is a prefix of path.
+ * zero if dir is not a prefix of path, or if dir is empty.
+ */
+int
+in_dir(char *dir, char *path)
+{
+ return(*dir ? !strncmp(dir, path, strlen(dir)) : 0);
+}
diff --git a/pith/tempfile.h b/pith/tempfile.h
new file mode 100644
index 00000000..c3255f75
--- /dev/null
+++ b/pith/tempfile.h
@@ -0,0 +1,26 @@
+/*
+ * $Id: tempfile.h 770 2007-10-24 00:23:09Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_TEMPFILE_INCLUDED
+#define PITH_TEMPFILE_INCLUDED
+
+
+/* exported protoypes */
+char *tempfile_in_same_dir(char *, char *, char **);
+int in_dir(char *, char *);
+
+
+#endif /* PITH_TEMPFILE_INCLUDED */
diff --git a/pith/text.c b/pith/text.c
new file mode 100644
index 00000000..5de53e51
--- /dev/null
+++ b/pith/text.c
@@ -0,0 +1,964 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: text.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+
+ text.c
+ Implements message text fetching and decoding
+
+ ====*/
+
+
+#include "../pith/headers.h"
+#include "../pith/text.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+#include "../pith/handle.h"
+#include "../pith/margin.h"
+#include "../pith/editorial.h"
+#include "../pith/color.h"
+#include "../pith/filter.h"
+#include "../pith/charset.h"
+#include "../pith/status.h"
+#include "../pith/mailview.h"
+#include "../pith/mimedesc.h"
+#include "../pith/detach.h"
+
+
+/* internal prototypes */
+int charset_editorial(char *, long, HANDLE_S **, int, int, int, gf_io_t);
+int replace_quotes(long, char *, LT_INS_S **, void *);
+void mark_handles_in_line(char *, HANDLE_S **, int);
+void clear_html_risk(void);
+void set_html_risk(void);
+int get_html_risk(void);
+
+
+#define CHARSET_DISCLAIMER_1 \
+ "The following text is in the \"%.40s\" character set.\015\012Your "
+#define CHARSET_DISCLAIMER_2 "display is set"
+#define CHARSET_DISCLAIMER_3 \
+ " for the \"%.40s\" character set. \015\012Some %.40scharacters may be displayed incorrectly."
+#define ENCODING_DISCLAIMER \
+ "The following text contains the unknown encoding type \"%.20s\". \015\012Some or all of the text may be displayed incorrectly."
+#define HTML_WARNING \
+ "The following HTML text may contain deceptive links. Carefully note the destination URL before visiting any links."
+
+
+/*
+ * HTML risk state holder.
+ */
+static int _html_risk;
+
+
+
+/*----------------------------------------------------------------------
+ Handle fetching and filtering a text message segment to be displayed
+ by scrolltool or printed or exported or piped.
+
+Args: att -- segment to fetch
+ msgno -- message number segment is a part of
+ pc -- function to write characters from segment with
+ style -- Indicates special handling for error messages
+ flags -- Indicates special necessary handling
+
+Returns: 1 if errors encountered, 0 if everything went A-OK
+
+ ----*/
+int
+decode_text(ATTACH_S *att,
+ long int msgno,
+ gf_io_t pc,
+ HANDLE_S **handlesp,
+ DetachErrStyle style,
+ int flags)
+{
+ FILTLIST_S filters[14];
+ char *err, *charset;
+ int filtcnt = 0, error_found = 0, column, wrapit;
+ int is_in_sig = OUT_SIG_BLOCK;
+ int is_flowed_msg = 0;
+ int is_delsp_yes = 0;
+ int filt_only_c0 = 0;
+ char *parmval;
+ char *free_this = NULL;
+ STORE_S *warn_so = NULL;
+ DELQ_S dq;
+ URL_HILITE_S uh;
+
+ column = (flags & FM_DISPLAY) ? ps_global->ttyo->screen_cols : 80;
+
+ if(!(flags & FM_DISPLAY))
+ flags |= FM_NOINDENT;
+
+ wrapit = column;
+
+ memset(filters, 0, sizeof(filters));
+
+ /* charset the body part is in */
+ charset = parameter_val(att->body->parameter, "charset");
+
+ /* determined if it's flowed, affects wrapping and quote coloring */
+ if(att->body->type == TYPETEXT
+ && !strucmp(att->body->subtype, "plain")
+ && (parmval = parameter_val(att->body->parameter, "format"))){
+ if(!strucmp(parmval, "flowed"))
+ is_flowed_msg = 1;
+ fs_give((void **) &parmval);
+
+ if(is_flowed_msg){
+ if((parmval = parameter_val(att->body->parameter, "delsp")) != NULL){
+ if(!strucmp(parmval, "yes"))
+ is_delsp_yes = 1;
+
+ fs_give((void **) &parmval);
+ }
+ }
+ }
+
+ if(!ps_global->pass_ctrl_chars){
+ filters[filtcnt++].filter = gf_escape_filter;
+ filters[filtcnt].filter = gf_control_filter;
+
+ filt_only_c0 = 1;
+ filters[filtcnt++].data = gf_control_filter_opt(&filt_only_c0);
+ }
+
+ if(flags & FM_DISPLAY)
+ filters[filtcnt++].filter = gf_tag_filter;
+
+ /*
+ * if it's just plain old text, look for url's
+ */
+ if(!(att->body->subtype && strucmp(att->body->subtype, "plain"))){
+ struct variable *vars = ps_global->vars;
+
+ if((F_ON(F_VIEW_SEL_URL, ps_global)
+ || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
+ || F_ON(F_SCAN_ADDR, ps_global))
+ && handlesp){
+
+ /*
+ * The url_hilite filter really ought to come
+ * after flowing, because flowing with the DelSp=yes parameter
+ * can reassemble broken urls back into identifiable urls.
+ * We add the preflow filter to do only the reassembly part
+ * of the flowing so that we can spot the urls.
+ * At this time (2005-03-29) we know that Apple Mail does
+ * send mail like this sometimes. This filter removes the
+ * sequence SP CRLF if that seems safe.
+ */
+ if(ps_global->full_header != 2 && is_delsp_yes)
+ filters[filtcnt++].filter = gf_preflow;
+
+ filters[filtcnt].filter = gf_line_test;
+ filters[filtcnt++].data = gf_line_test_opt(url_hilite,
+ gf_url_hilite_opt(&uh,handlesp,0));
+ }
+
+ /*
+ * First, paint the signature.
+ * Disclaimers noted below for coloring quotes apply here as well.
+ */
+ if((flags & FM_DISPLAY)
+ && !(flags & FM_NOCOLOR)
+ && pico_usingcolor()
+ && VAR_SIGNATURE_FORE_COLOR
+ && VAR_SIGNATURE_BACK_COLOR){
+ filters[filtcnt].filter = gf_line_test;
+ filters[filtcnt++].data = gf_line_test_opt(color_signature,
+ &is_in_sig);
+ }
+
+ /*
+ * Gotta be careful with this. The color_a_quote filter adds color
+ * to the beginning and end of the line. This will break some
+ * line_test-style filters which come after it. For example, if they
+ * are looking for something at the start of a line (like color_a_quote
+ * itself). I guess we could fix that by ignoring tags at the
+ * beginning of the line when doing the search.
+ */
+ if((flags & FM_DISPLAY)
+ && !(flags & FM_NOCOLOR)
+ && pico_usingcolor()
+ && VAR_QUOTE1_FORE_COLOR
+ && VAR_QUOTE1_BACK_COLOR){
+ filters[filtcnt].filter = gf_line_test;
+ filters[filtcnt++].data = gf_line_test_opt(color_a_quote,
+ &is_flowed_msg);
+ }
+ }
+ else if(!strucmp(att->body->subtype, "richtext")){
+ int plain_opt;
+
+ plain_opt = !(flags&FM_DISPLAY);
+
+ /* maybe strip everything! */
+ filters[filtcnt].filter = gf_rich2plain;
+ filters[filtcnt++].data = gf_rich2plain_opt(&plain_opt);
+ /* width to use for file or printer */
+ if(wrapit - 5 > 0)
+ wrapit -= 5;
+ }
+ else if(!strucmp(att->body->subtype, "enriched")){
+ int plain_opt;
+
+ plain_opt = !(flags&FM_DISPLAY);
+
+ filters[filtcnt].filter = gf_enriched2plain;
+ filters[filtcnt++].data = gf_enriched2plain_opt(&plain_opt);
+ /* width to use for file or printer */
+ if(wrapit - 5 > 0)
+ wrapit -= 5;
+ }
+ else if(!strucmp(att->body->subtype, "html") && ps_global->full_header < 2){
+/*BUG: sniff the params for "version=2.0" ala draft-ietf-html-spec-01 */
+ int opts = 0;
+
+ clear_html_risk();
+
+ if(flags & FM_DISPLAY){
+ gf_io_t warn_pc;
+
+ if(handlesp){ /* pass on handles awareness */
+ opts |= GFHP_HANDLES;
+
+ if(F_OFF(F_QUELL_HOST_AFTER_URL, ps_global) && !(flags & FM_HIDESERVER))
+ opts |= GFHP_SHOW_SERVER;
+ }
+
+ if(!(flags & FM_NOEDITORIAL)){
+ warn_so = so_get(CharStar, NULL, EDIT_ACCESS);
+ gf_set_so_writec(&warn_pc, warn_so);
+ format_editorial(HTML_WARNING, column, flags, handlesp, warn_pc);
+ gf_clear_so_writec(warn_so);
+ so_puts(warn_so, "\015\012");
+ so_writec('\0', warn_so);
+ }
+ }
+ else
+ opts |= GFHP_STRIPPED; /* don't embed anything! */
+
+ if(flags & FM_NOHTMLREL)
+ opts |= GFHP_NO_RELATIVE;
+
+ if(flags & FM_HTMLRELATED)
+ opts |= GFHP_RELATED_CONTENT;
+
+ if(flags & FM_HTML)
+ opts |= GFHP_HTML;
+
+ if(flags & FM_HTMLIMAGES)
+ opts |= GFHP_HTML_IMAGES;
+
+ wrapit = 0; /* wrap already handled! */
+ filters[filtcnt].filter = gf_html2plain;
+ filters[filtcnt++].data = gf_html2plain_opt(NULL, column,
+ (flags & (FM_NOINDENT | FM_HTML))
+ ? 0
+ : format_view_margin(),
+ handlesp, set_html_risk, opts);
+
+ if(warn_so){
+ filters[filtcnt].filter = gf_prepend_editorial;
+ filters[filtcnt++].data = gf_prepend_editorial_opt(get_html_risk,
+ (char *) so_text(warn_so));
+ }
+ }
+
+ /*
+ * If the message is not flowed, we do the quote suppression before
+ * the wrapping, because the wrapping does not preserve the quote
+ * characters at the beginnings of the lines in that case.
+ * Otherwise, we defer until after the wrapping.
+ *
+ * Also, this is a good place to do quote-replacement on nonflowed
+ * messages because no other filters depend on the "> ".
+ * Quote-replacement is easier in the flowed case and occurs
+ * automatically in the flowed wrapping filter.
+ */
+ if(!is_flowed_msg
+ && ps_global->full_header == 0
+ && !(att->body->subtype && strucmp(att->body->subtype, "plain"))
+ && (flags & FM_DISPLAY)){
+ if(ps_global->quote_suppression_threshold != 0){
+ memset(&dq, 0, sizeof(dq));
+ dq.lines = ps_global->quote_suppression_threshold;
+ dq.is_flowed = is_flowed_msg;
+ dq.indent_length = 0; /* indent didn't happen yet */
+ dq.saved_line = &free_this;
+ dq.handlesp = handlesp;
+ dq.do_color = (!(flags & FM_NOCOLOR) && pico_usingcolor());
+
+ filters[filtcnt].filter = gf_line_test;
+ filters[filtcnt++].data = gf_line_test_opt(delete_quotes, &dq);
+ }
+ if(ps_global->VAR_QUOTE_REPLACE_STRING
+ && F_ON(F_QUOTE_REPLACE_NOFLOW, ps_global)){
+ filters[filtcnt].filter = gf_line_test;
+ filters[filtcnt++].data = gf_line_test_opt(replace_quotes, NULL);
+ }
+ }
+
+ if(wrapit && !(flags & FM_NOWRAP)){
+ int wrapflags = (flags & FM_DISPLAY) ? (GFW_HANDLES|GFW_SOFTHYPHEN)
+ : GFW_NONE;
+
+ if(flags & FM_DISPLAY
+ && !(flags & FM_NOCOLOR)
+ && pico_usingcolor())
+ wrapflags |= GFW_USECOLOR;
+
+ /* text/flowed (RFC 2646 + draft)? */
+ if(ps_global->full_header != 2 && is_flowed_msg){
+ wrapflags |= GFW_FLOWED;
+
+ if(is_delsp_yes)
+ wrapflags |= GFW_DELSP;
+ }
+
+ filters[filtcnt].filter = gf_wrap;
+ filters[filtcnt++].data = gf_wrap_filter_opt(wrapit, column,
+ (flags & FM_NOINDENT)
+ ? 0
+ : format_view_margin(),
+ 0, wrapflags);
+ }
+
+ /*
+ * This has to come after wrapping has happened because the user tells
+ * us how many quoted lines to display, and we want that number to be
+ * the wrapped lines, not the pre-wrapped lines. We do it before the
+ * wrapping if the message is not flowed, because the wrapping does not
+ * preserve the quote characters at the beginnings of the lines in that
+ * case.
+ */
+ if(is_flowed_msg
+ && ps_global->full_header == 0
+ && !(att->body->subtype && strucmp(att->body->subtype, "plain"))
+ && (flags & FM_DISPLAY)
+ && ps_global->quote_suppression_threshold != 0){
+ memset(&dq, 0, sizeof(dq));
+ dq.lines = ps_global->quote_suppression_threshold;
+ dq.is_flowed = is_flowed_msg;
+ dq.indent_length = (wrapit && !(flags & FM_NOWRAP)
+ && !(flags & FM_NOINDENT))
+ ? format_view_margin()[0]
+ : 0;
+ dq.saved_line = &free_this;
+ dq.handlesp = handlesp;
+ dq.do_color = (!(flags & FM_NOCOLOR) && pico_usingcolor());
+
+ filters[filtcnt].filter = gf_line_test;
+ filters[filtcnt++].data = gf_line_test_opt(delete_quotes, &dq);
+ }
+
+ if(charset){
+ if(F_OFF(F_QUELL_CHARSET_WARNING, ps_global)
+ && !(flags & FM_NOEDITORIAL)){
+ int rv = TRUE;
+ CONV_TABLE *ct = NULL;
+
+ /*
+ * Need editorial if message charset is not ascii
+ * and the display charset is not either the same
+ * as the message charset or UTF-8.
+ */
+ if(strucmp(charset, "us-ascii")
+ && !((ps_global->display_charmap
+ && !strucmp(charset, ps_global->display_charmap))
+ || !strucmp("UTF-8", ps_global->display_charmap))){
+
+ /*
+ * This is probably overkill. We're just using this
+ * conversion_table routine to get at the quality.
+ */
+ if(ps_global->display_charmap)
+ ct = conversion_table(charset, ps_global->display_charmap);
+
+ rv = charset_editorial(charset, msgno, handlesp, flags,
+ ct ? ct->quality : CV_NO_TRANSLATE_POSSIBLE,
+ column, pc);
+ }
+ if(!rv)
+ goto write_error;
+ }
+
+ fs_give((void **) &charset);
+ }
+
+ /* Format editorial comment about unknown encoding */
+ if(att->body->encoding > ENCQUOTEDPRINTABLE){
+ char buf[2048];
+
+ snprintf(buf, sizeof(buf), ENCODING_DISCLAIMER, body_encodings[att->body->encoding]);
+
+ if(!(format_editorial(buf, column, flags, handlesp, pc) == NULL
+ && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
+ goto write_error;
+ }
+
+ err = detach(ps_global->mail_stream, msgno, att->number, 0L,
+ NULL, pc, filtcnt ? filters : NULL, 0);
+
+ delete_unused_handles(handlesp);
+
+ if(free_this)
+ fs_give((void **) &free_this);
+
+ if(warn_so)
+ so_give(&warn_so);
+
+ if(err) {
+ error_found++;
+ if(style == QStatus) {
+ q_status_message1(SM_ORDER, 3, 4, "%.200s", err);
+ } else if(style == InLine) {
+ char buftmp[MAILTMPLEN];
+
+ snprintf(buftmp, sizeof(buftmp), "%s",
+ att->body->description ? att->body->description : "");
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s [Error: %s] %c%s%c%s%s",
+ NEWLINE, err,
+ att->body->description ? '\"' : ' ',
+ att->body->description ? buftmp : "",
+ att->body->description ? '\"' : ' ',
+ NEWLINE, NEWLINE);
+ if(!gf_puts(tmp_20k_buf, pc))
+ goto write_error;
+ }
+ }
+
+ if(att->body->subtype
+ && (!strucmp(att->body->subtype, "richtext")
+ || !strucmp(att->body->subtype, "enriched"))
+ && !(flags & FM_DISPLAY)){
+ if(!gf_puts(NEWLINE, pc) || !gf_puts(NEWLINE, pc))
+ goto write_error;
+ }
+
+ return(error_found);
+
+ write_error:
+ if(style == QStatus)
+ q_status_message1(SM_ORDER, 3, 4, "Error writing message: %.200s",
+ error_description(errno));
+
+ return(1);
+}
+
+
+
+/*
+ * Format editorial comment about charset mismatch
+ */
+int
+charset_editorial(char *charset, long int msgno, HANDLE_S **handlesp, int flags,
+ int quality, int width, gf_io_t pc)
+{
+ char *p, color[64], buf[2048];
+ int i, n;
+ HANDLE_S *h = NULL;
+
+ snprintf(buf, sizeof(buf), CHARSET_DISCLAIMER_1, charset ? charset : "US-ASCII");
+ p = &buf[strlen(buf)];
+
+ if(!(ps_global->display_charmap
+ && strucmp(ps_global->display_charmap, "US-ASCII"))
+ && handlesp
+ && (flags & FM_DISPLAY) == FM_DISPLAY){
+ h = new_handle(handlesp);
+ h->type = URL;
+ h->h.url.path = cpystr("x-alpine-help:h_config_char_set");
+
+ /*
+ * Because this filter comes after the delete_quotes filter, we need
+ * to tell delete_quotes that this handle is used, so it won't
+ * delete it. This is a dangerous thing.
+ */
+ h->is_used = 1;
+
+ if(!(flags & FM_NOCOLOR)
+ && scroll_handle_start_color(color, sizeof(color), &n)){
+ for(i = 0; i < n; i++)
+ if(p-buf < sizeof(buf))
+ *p++ = color[i];
+ }
+ else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
+ if(p-buf+1 < sizeof(buf)){
+ *p++ = TAG_EMBED;
+ *p++ = TAG_BOLDON;
+ }
+ }
+
+ if(p-buf+1 < sizeof(buf)){
+ *p++ = TAG_EMBED;
+ *p++ = TAG_HANDLE;
+ }
+
+ snprintf(p + 1, sizeof(buf)-(p+1-buf), "%d", h->key);
+ if(p-buf < sizeof(buf))
+ *p = strlen(p + 1);
+
+ p += (*p + 1);
+ }
+
+ sstrncpy(&p, CHARSET_DISCLAIMER_2, sizeof(buf)-(p-buf));
+
+ if(h){
+ /* in case it was the current selection */
+ if(p-buf+1 < sizeof(buf)){
+ *p++ = TAG_EMBED;
+ *p++ = TAG_INVOFF;
+ }
+
+ if(scroll_handle_end_color(color, sizeof(color), &n, 0)){
+ for(i = 0; i < n; i++)
+ if(p-buf < sizeof(buf))
+ *p++ = color[i];
+ }
+ else{
+ if(p-buf+1 < sizeof(buf)){
+ *p++ = TAG_EMBED;
+ *p++ = TAG_BOLDOFF;
+ }
+ }
+
+ if(p-buf < sizeof(buf))
+ *p = '\0';
+ }
+
+ snprintf(p, sizeof(buf)-(p-buf), CHARSET_DISCLAIMER_3,
+ ps_global->display_charmap ? ps_global->display_charmap : "US-ASCII",
+ (quality == CV_LOSES_SPECIAL_CHARS) ? "special " : "");
+
+ buf[sizeof(buf)-1] = '\0';
+
+ return(format_editorial(buf, width, flags, handlesp, pc) == NULL
+ && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc));
+}
+
+
+
+
+/*
+ * This one is a little more complicated because it comes late in the
+ * filtering sequence after colors have been added and url's hilited and
+ * so on. So if it wants to look at the beginning of the line it has to
+ * wade through some stuff done by the other filters first. This one is
+ * skipping the leading indent added by the viewer-margin stuff and leading
+ * tags.
+ */
+int
+delete_quotes(long int linenum, char *line, LT_INS_S **ins, void *local)
+{
+ DELQ_S *dq;
+ char *lp;
+ int i, lines, not_a_quote = 0;
+ size_t len;
+
+ dq = (DELQ_S *) local;
+ if(!dq)
+ return(0);
+
+ if(dq->lines > 0 || dq->lines == Q_DEL_ALL){
+
+ lines = (dq->lines == Q_DEL_ALL) ? 0 : dq->lines;
+
+ /*
+ * First determine if this line is part of a quote.
+ */
+
+ /* skip over indent added by viewer-margin-left */
+ lp = line;
+ for(i = dq->indent_length; i > 0 && !not_a_quote && *lp; i--)
+ if(*lp++ != SPACE)
+ not_a_quote++;
+
+ /* skip over leading tags */
+ while(!not_a_quote
+ && ((unsigned char) (*lp) == (unsigned char) TAG_EMBED)){
+ switch(*++lp){
+ case '\0':
+ not_a_quote++;
+ break;
+
+ default:
+ ++lp;
+ break;
+
+ case TAG_FGCOLOR:
+ case TAG_BGCOLOR:
+ case TAG_HANDLE:
+ switch(*lp){
+ case TAG_FGCOLOR:
+ case TAG_BGCOLOR:
+ len = RGBLEN + 1;
+ break;
+
+ case TAG_HANDLE:
+ len = *++lp + 1;
+ break;
+ }
+
+ if(strlen(lp) < len)
+ not_a_quote++;
+ else
+ lp += len;
+
+ break;
+
+ case TAG_EMBED:
+ break;
+ }
+ }
+
+ /* skip over whitespace */
+ if(!dq->is_flowed)
+ while(isspace((unsigned char) *lp))
+ lp++;
+
+ /* check first character to see if it is a quote */
+ if(!not_a_quote && *lp != '>')
+ not_a_quote++;
+
+ if(not_a_quote){
+ if(dq->in_quote > lines+1 && !dq->delete_all){
+ char tmp[500];
+ COLOR_PAIR *col = NULL;
+ char cestart[2 * RGBLEN + 5];
+ char ceend[2 * RGBLEN + 5];
+
+ /*
+ * Display how many lines were hidden.
+ */
+
+ cestart[0] = ceend[0] = '\0';
+ if(dq->do_color
+ && ps_global->VAR_METAMSG_FORE_COLOR
+ && ps_global->VAR_METAMSG_BACK_COLOR
+ && (col = new_color_pair(ps_global->VAR_METAMSG_FORE_COLOR,
+ ps_global->VAR_METAMSG_BACK_COLOR)))
+ if(!pico_is_good_colorpair(col))
+ free_color_pair(&col);
+
+ if(col){
+ strncpy(cestart, color_embed(col->fg, col->bg), sizeof(cestart));
+ cestart[sizeof(cestart)-1] = '\0';
+ strncpy(ceend, color_embed(ps_global->VAR_NORM_FORE_COLOR,
+ ps_global->VAR_NORM_BACK_COLOR), sizeof(ceend));
+ ceend[sizeof(ceend)-1] = '\0';
+ free_color_pair(&col);
+ }
+
+ snprintf(tmp, sizeof(tmp),
+ "%s[ %s%d lines of quoted text hidden from view%s ]\r\n",
+ repeat_char(dq->indent_length, SPACE),
+ cestart, dq->in_quote - lines, ceend);
+ if(strlen(tmp)-strlen(cestart)-strlen(ceend)-2 > ps_global->ttyo->screen_cols){
+
+ snprintf(tmp, sizeof(tmp), "%s[ %s%d lines of quoted text hidden%s ]\r\n",
+ repeat_char(dq->indent_length, SPACE),
+ cestart, dq->in_quote - lines, ceend);
+
+ if(strlen(tmp)-strlen(cestart)-strlen(ceend)-2 > ps_global->ttyo->screen_cols){
+ snprintf(tmp, sizeof(tmp), "%s[ %s%d lines hidden%s ]\r\n",
+ repeat_char(dq->indent_length, SPACE),
+ cestart, dq->in_quote - lines, ceend);
+
+ if(strlen(tmp)-strlen(cestart)-strlen(ceend)-2 > ps_global->ttyo->screen_cols){
+ snprintf(tmp, sizeof(tmp), "%s[ %s%d hidden%s ]\r\n",
+ repeat_char(dq->indent_length, SPACE),
+ cestart, dq->in_quote - lines, ceend);
+
+ if(strlen(tmp)-strlen(cestart)-strlen(ceend)-2 > ps_global->ttyo->screen_cols){
+ snprintf(tmp, sizeof(tmp), "%s[...]\r\n",
+ repeat_char(dq->indent_length, SPACE));
+
+ if(strlen(tmp)-2 > ps_global->ttyo->screen_cols){
+ snprintf(tmp, sizeof(tmp), "%s...\r\n",
+ repeat_char(dq->indent_length, SPACE));
+
+ if(strlen(tmp)-2 > ps_global->ttyo->screen_cols){
+ snprintf(tmp, sizeof(tmp), "%s\r\n",
+ repeat_char(MIN(ps_global->ttyo->screen_cols,3),
+ '.'));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ ins = gf_line_test_new_ins(ins, line, tmp, strlen(tmp));
+ mark_handles_in_line(line, dq->handlesp, 1);
+ ps_global->some_quoting_was_suppressed = 1;
+ }
+ else if(dq->in_quote == lines+1
+ && dq->saved_line && *dq->saved_line){
+ /*
+ * We would have only had to delete a single line. Instead
+ * of deleting it, just show it.
+ */
+ ins = gf_line_test_new_ins(ins, line,
+ *dq->saved_line,
+ strlen(*dq->saved_line));
+ mark_handles_in_line(*dq->saved_line, dq->handlesp, 1);
+ }
+ else
+ mark_handles_in_line(line, dq->handlesp, 1);
+
+ if(dq->saved_line && *dq->saved_line)
+ fs_give((void **) dq->saved_line);
+
+ dq->in_quote = 0;
+ }
+ else{
+ dq->in_quote++; /* count it */
+ if(dq->in_quote > lines){
+ /*
+ * If the hidden part is a single line we'll show it instead.
+ */
+ if(dq->in_quote == lines+1 && !dq->delete_all){
+ if(dq->saved_line && *dq->saved_line)
+ fs_give((void **) dq->saved_line);
+
+ *dq->saved_line = fs_get(strlen(line) + 3);
+ snprintf(*dq->saved_line, strlen(line)+3, "%s\r\n", line);
+ }
+
+ mark_handles_in_line(line, dq->handlesp, 0);
+ /* skip it, at least for now */
+ return(2);
+ }
+
+ mark_handles_in_line(line, dq->handlesp, 1);
+ }
+ }
+
+ return(0);
+}
+
+
+/*
+ * This is a line-at-a-time filter that replaces incoming UTF-8 text with
+ * ISO-2022-JP text.
+ */
+int
+translate_utf8_to_2022_jp(long int linenum, char *line, LT_INS_S **ins, void *local)
+{
+ LOC_2022_JP *lpj;
+ int ret = 0;
+
+ lpj = (LOC_2022_JP *) local;
+
+ if(line && line[0]){
+ char *converted;
+ size_t len;
+
+ converted = utf8_to_charset(line, "ISO-2022-JP", lpj ? lpj->report_err : 0);
+
+ if(!converted)
+ ret = -1;
+
+ if(converted && converted != line){
+ /* delete the old line and replace it with the translation */
+ len = strlen(line);
+ ins = gf_line_test_new_ins(ins, line, "", -((int) len));
+ ins = gf_line_test_new_ins(ins, line+len, converted, strlen(converted));
+ fs_give((void **) &converted);
+ }
+ }
+
+ return ret;
+}
+
+
+/*
+ * Replace quotes of nonflowed messages. This needs to happen
+ * towards the end of filtering because a lot of prior filters
+ * depend on "> ", such as quote coloring and suppression.
+ * Quotes are already colored here, so we have to account for
+ * that formatting.
+ */
+int
+replace_quotes(long int linenum, char *line, LT_INS_S **ins, void *local)
+{
+ char *lp = line, *prefix = NULL, *last_prefix = NULL;
+ int no_more_quotes = 0, len, saw_quote = 0;
+
+ if(ps_global->VAR_QUOTE_REPLACE_STRING){
+ get_pair(ps_global->VAR_QUOTE_REPLACE_STRING, &prefix, &last_prefix, 0, 0);
+ if(!prefix && last_prefix){
+ prefix = last_prefix;
+ last_prefix = NULL;
+ }
+ }
+ else
+ return(0);
+
+ while(isspace((unsigned char)(*lp)))
+ lp++;
+ while(*lp && !no_more_quotes){
+ switch(*lp++){
+ case '>':
+ if(*lp == ' '){
+ ins = gf_line_test_new_ins(ins, lp - 1, "", -2);
+ lp++;
+ }
+ else
+ ins = gf_line_test_new_ins(ins, lp - 1, "", -1);
+ if(strlen(prefix))
+ ins = gf_line_test_new_ins(ins, lp, prefix, strlen(prefix));
+ saw_quote = 1;
+ break;
+ case TAG_EMBED:
+ switch(*lp++){
+ case TAG_FGCOLOR:
+ case TAG_BGCOLOR:
+ len = RGBLEN;
+ break;
+ case TAG_HANDLE:
+ len = *lp++ + 1;
+ break;
+ }
+ if(strlen(lp) < len)
+ no_more_quotes = 1;
+ else
+ lp += len;
+ break;
+ case ' ':
+ case '\t':
+ break;
+ default:
+ if(saw_quote && last_prefix)
+ ins = gf_line_test_new_ins(ins, lp - 1, last_prefix, strlen(last_prefix));
+ no_more_quotes = 1;
+ break;
+ }
+ }
+ if(prefix)
+ fs_give((void **)&prefix);
+ if(last_prefix)
+ fs_give((void **)&last_prefix);
+ return(0);
+}
+
+
+/*
+ * We need to delete handles that we are removing from the displayed
+ * text. But there is a complication. Some handles appear at more than
+ * one location in the text. So we keep track of which handles we're
+ * actually using as we go, then at the end we delete the ones we
+ * didn't use.
+ *
+ * Args line -- the text line, which includes tags
+ * handlesp -- handles pointer
+ * used -- If 1, set handles in this line to used
+ * if 0, leave handles in this line set as they already are
+ */
+void
+mark_handles_in_line(char *line, HANDLE_S **handlesp, int used)
+{
+ unsigned char c;
+ size_t length;
+ int key, n;
+ HANDLE_S *h;
+
+ if(!(line && handlesp && *handlesp))
+ return;
+
+ length = strlen(line);
+
+ /* mimic code in PutLine0n8b */
+ while(length--){
+
+ c = (unsigned char) *line++;
+
+ if(c == (unsigned char) TAG_EMBED && length){
+
+ length--;
+
+ switch(*line++){
+ case TAG_HANDLE :
+ length -= *line + 1; /* key length plus length tag */
+
+ for(key = 0, n = *line++; n; n--)
+ key = (key * 10) + (*line++ - '0');
+
+ h = get_handle(*handlesp, key);
+ if(h){
+ h->using_is_used = 1;
+ /*
+ * If it's already marked is_used, leave it marked
+ * that way. We only call this function with used = 0
+ * in order to set using_is_used.
+ */
+ h->is_used = (h->is_used || used) ? 1 : 0;
+ }
+
+ break;
+
+ case TAG_FGCOLOR :
+ if(length < RGBLEN){
+ length = 0;
+ break;
+ }
+
+ length -= RGBLEN;
+ line += RGBLEN;
+ break;
+
+ case TAG_BGCOLOR :
+ if(length < RGBLEN){
+ length = 0;
+ break;
+ }
+
+ length -= RGBLEN;
+ line += RGBLEN;
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+}
+
+
+/*
+ * parsed html risk state functions
+ */
+void
+clear_html_risk(void)
+{
+ _html_risk = 0;
+}
+
+void
+set_html_risk(void)
+{
+ _html_risk = 1;
+}
+
+int
+get_html_risk(void)
+{
+ return(_html_risk);
+}
diff --git a/pith/text.h b/pith/text.h
new file mode 100644
index 00000000..1813e3f0
--- /dev/null
+++ b/pith/text.h
@@ -0,0 +1,58 @@
+/*
+ * $Id: text.h 768 2007-10-24 00:10:03Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_TEXT_INCLUDED
+#define PITH_TEXT_INCLUDED
+
+
+#include "../pith/atttype.h"
+#include "../pith/filter.h"
+#include "../pith/handle.h"
+
+
+typedef enum {InLine, QStatus} DetachErrStyle;
+
+
+typedef struct local_for_2022_jp_trans {
+ int report_err;
+} LOC_2022_JP;
+
+
+/*
+ * If the number of lines in the quote is equal to lines or less, then
+ * we show the quote. If the number of lines in the quote is more than lines,
+ * then we show lines-1 of the quote followed by one line of editorial
+ * comment.
+ */
+typedef struct del_q_s {
+ int lines; /* show this many lines (counting editorial) */
+ int indent_length; /* skip over this indent */
+ int is_flowed; /* message is labelled flowed */
+ int do_color;
+ int delete_all; /* delete quotes completely, no comments */
+ HANDLE_S **handlesp;
+ int in_quote; /* dynamic data */
+ char **saved_line; /* " */
+} DELQ_S;
+
+
+/* exported protoypes */
+int decode_text(ATTACH_S *, long, gf_io_t, HANDLE_S **, DetachErrStyle, int);
+int translate_utf8_to_2022_jp(long, char *, LT_INS_S **, void *);
+int delete_quotes(long, char *, LT_INS_S **, void *);
+
+
+#endif /* PITH_TEXT_INCLUDED */
diff --git a/pith/thread.c b/pith/thread.c
new file mode 100644
index 00000000..ff9bff54
--- /dev/null
+++ b/pith/thread.c
@@ -0,0 +1,1561 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: thread.c 942 2008-03-04 18:21:33Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/thread.h"
+#include "../pith/flag.h"
+#include "../pith/icache.h"
+#include "../pith/mailindx.h"
+#include "../pith/msgno.h"
+#include "../pith/sort.h"
+#include "../pith/pineelt.h"
+#include "../pith/status.h"
+#include "../pith/news.h"
+#include "../pith/search.h"
+#include "../pith/mailcmd.h"
+#include "../pith/ablookup.h"
+
+
+/*
+ * Internal prototypes
+ */
+long *sort_thread_flatten(THREADNODE *, MAILSTREAM *, long *,
+ char *, long, PINETHRD_S *, unsigned);
+void make_thrdflags_consistent(MAILSTREAM *, MSGNO_S *, PINETHRD_S *, int);
+THREADNODE *collapse_threadnode_tree(THREADNODE *);
+THREADNODE *collapse_threadnode_tree_sorted(THREADNODE *);
+THREADNODE *sort_threads_and_collapse(THREADNODE *);
+THREADNODE *insert_tree_in_place(THREADNODE *, THREADNODE *);
+unsigned long branch_greatest_num(THREADNODE *, int);
+long calculate_visible_threads(MAILSTREAM *);
+
+
+PINETHRD_S *
+fetch_thread(MAILSTREAM *stream, long unsigned int rawno)
+{
+ MESSAGECACHE *mc;
+ PINELT_S *pelt;
+ PINETHRD_S *thrd = NULL;
+
+ if(stream && rawno > 0L && rawno <= stream->nmsgs
+ && !sp_need_to_rethread(stream)){
+ mc = (rawno > 0L && stream && rawno <= stream->nmsgs)
+ ? mail_elt(stream, rawno) : NULL;
+ if(mc && (pelt = (PINELT_S *) mc->sparep))
+ thrd = pelt->pthrd;
+ }
+
+ return(thrd);
+}
+
+
+PINETHRD_S *
+fetch_head_thread(MAILSTREAM *stream)
+{
+ unsigned long rawno;
+ PINETHRD_S *thrd = NULL;
+
+ if(stream){
+ /* first find any thread */
+ for(rawno = 1L; !thrd && rawno <= stream->nmsgs; rawno++)
+ thrd = fetch_thread(stream, rawno);
+
+ if(thrd && thrd->head)
+ thrd = fetch_thread(stream, thrd->head);
+ }
+
+ return(thrd);
+}
+
+
+/*
+ * Set flag f to v for all messages in thrd.
+ *
+ * Watch out when calling this. The thrd->branch is not part of thrd.
+ * Branch is a sibling to thrd, not a child. Zero out branch before calling
+ * or call on thrd->next and worry about thrd separately.
+ * Ok to call it on top-level thread which has no branch already.
+ */
+void
+set_flags_for_thread(MAILSTREAM *stream, MSGNO_S *msgmap, int f, PINETHRD_S *thrd, int v)
+{
+ PINETHRD_S *nthrd, *bthrd;
+
+ if(!(stream && thrd && msgmap))
+ return;
+
+ set_lflag(stream, msgmap, mn_raw2m(msgmap, thrd->rawno), f, v);
+
+ if(thrd->next){
+ nthrd = fetch_thread(stream, thrd->next);
+ if(nthrd)
+ set_flags_for_thread(stream, msgmap, f, nthrd, v);
+ }
+
+ if(thrd->branch){
+ bthrd = fetch_thread(stream, thrd->branch);
+ if(bthrd)
+ set_flags_for_thread(stream, msgmap, f, bthrd, v);
+ }
+}
+
+
+void
+erase_threading_info(MAILSTREAM *stream, MSGNO_S *msgmap)
+{
+ unsigned long n;
+ MESSAGECACHE *mc;
+ PINELT_S *peltp;
+
+ if(!(stream && stream->spare))
+ return;
+
+ ps_global->view_skipped_index = 0;
+ sp_set_viewing_a_thread(stream, 0);
+
+ if(THRD_INDX())
+ setup_for_thread_index_screen();
+ else
+ setup_for_index_index_screen();
+
+ stream->spare = 0;
+
+ for(n = 1L; n <= stream->nmsgs; n++){
+ set_lflag(stream, msgmap, mn_raw2m(msgmap, n),
+ MN_COLL | MN_CHID | MN_CHID2, 0);
+ mc = mail_elt(stream, n);
+ if(mc && mc->sparep){
+ peltp = (PINELT_S *) mc->sparep;
+ if(peltp->pthrd)
+ fs_give((void **) &peltp->pthrd);
+ }
+ }
+}
+
+
+void
+sort_thread_callback(MAILSTREAM *stream, THREADNODE *tree)
+{
+ THREADNODE *collapsed_tree = NULL;
+ PINETHRD_S *thrd = NULL;
+ unsigned long msgno, rawno;
+ int un_view_thread = 0;
+ long raw_current;
+ char *dup_chk = NULL;
+
+
+ dprint((2, "sort_thread_callback\n"));
+
+ g_sort.msgmap->max_thrdno = 0L;
+
+ /*
+ * Eliminate dummy nodes from tree and collapse the tree in a logical
+ * way. If the dummy node is at the top-level, then its children are
+ * promoted to the top-level as separate threads.
+ */
+ if(F_ON(F_THREAD_SORTS_BY_ARRIVAL, ps_global))
+ collapsed_tree = collapse_threadnode_tree_sorted(tree);
+ else
+ collapsed_tree = collapse_threadnode_tree(tree);
+
+ /* dup_chk is like sort with an origin of 1 */
+ dup_chk = (char *) fs_get((mn_get_nmsgs(g_sort.msgmap)+1) * sizeof(char));
+ memset(dup_chk, 0, (mn_get_nmsgs(g_sort.msgmap)+1) * sizeof(char));
+
+ memset(&g_sort.msgmap->sort[1], 0, mn_get_total(g_sort.msgmap) * sizeof(long));
+
+ (void) sort_thread_flatten(collapsed_tree, stream,
+ &g_sort.msgmap->sort[1],
+ dup_chk, mn_get_nmsgs(g_sort.msgmap),
+ NULL, THD_TOP);
+
+ /* reset the inverse array */
+ msgno_reset_isort(g_sort.msgmap);
+
+ if(dup_chk)
+ fs_give((void **) &dup_chk);
+
+ if(collapsed_tree)
+ mail_free_threadnode(&collapsed_tree);
+
+ if(any_lflagged(g_sort.msgmap, MN_HIDE))
+ g_sort.msgmap->visible_threads = calculate_visible_threads(stream);
+ else
+ g_sort.msgmap->visible_threads = g_sort.msgmap->max_thrdno;
+
+ raw_current = mn_m2raw(g_sort.msgmap, mn_get_cur(g_sort.msgmap));
+
+ sp_set_need_to_rethread(stream, 0);
+
+ /*
+ * Set appropriate bits to start out collapsed if desired. We use the
+ * stream spare bit to tell us if we've done this before for this
+ * stream.
+ */
+ if(!stream->spare
+ && (COLL_THRDS() || SEP_THRDINDX())
+ && mn_get_total(g_sort.msgmap) > 1L){
+
+ collapse_threads(stream, g_sort.msgmap, NULL);
+ }
+ else if(stream->spare){
+
+ /*
+ * If we're doing auto collapse then new threads need to have
+ * their collapse bit set. This happens below if we're in the
+ * thread index, but if we're in the regular index with auto
+ * collapse we have to look for these.
+ */
+ if(any_lflagged(g_sort.msgmap, MN_USOR)){
+ if(COLL_THRDS()){
+ for(msgno = 1L; msgno <= mn_get_total(g_sort.msgmap); msgno++){
+ rawno = mn_m2raw(g_sort.msgmap, msgno);
+ if(get_lflag(stream, NULL, rawno, MN_USOR)){
+ thrd = fetch_thread(stream, rawno);
+
+ /*
+ * Node is new, unsorted, top-level thread,
+ * and we're using auto collapse.
+ */
+ if(thrd && !thrd->parent)
+ set_lflag(stream, g_sort.msgmap, msgno, MN_COLL, 1);
+
+ /*
+ * If a parent is collapsed, clear that parent's
+ * index cache entry. This is only necessary if
+ * the parent's index display can depend on its
+ * children, of course.
+ */
+ if(thrd && thrd->parent){
+ thrd = fetch_thread(stream, thrd->parent);
+ while(thrd){
+ long t;
+
+ if(get_lflag(stream, NULL, thrd->rawno, MN_COLL)
+ && (t = mn_raw2m(g_sort.msgmap,
+ (long) thrd->rawno)))
+ clear_index_cache_ent(stream, t, 0);
+
+ if(thrd->parent)
+ thrd = fetch_thread(stream, thrd->parent);
+ else
+ thrd = NULL;
+ }
+ }
+
+ }
+ }
+ }
+
+ set_lflags(stream, g_sort.msgmap, MN_USOR, 0);
+ }
+
+ if(sp_viewing_a_thread(stream)){
+ if(any_lflagged(g_sort.msgmap, MN_CHID2)){
+ /* current should be part of viewed thread */
+ if(get_lflag(stream, NULL, raw_current, MN_CHID2)){
+ thrd = fetch_thread(stream, raw_current);
+ if(thrd && thrd->top && thrd->top != thrd->rawno)
+ thrd = fetch_thread(stream, thrd->top);
+
+ if(thrd){
+ /*
+ * For messages that are part of thread set MN_CHID2
+ * and for messages that aren't part of the thread
+ * clear MN_CHID2. Easiest is to just do it instead
+ * of checking if it is true first.
+ */
+ set_lflags(stream, g_sort.msgmap, MN_CHID2, 0);
+ set_thread_lflags(stream, thrd, g_sort.msgmap,
+ MN_CHID2, 1);
+
+ /*
+ * Outside of the viewed thread everything else
+ * should be collapsed at the top-levels.
+ */
+ collapse_threads(stream, g_sort.msgmap, thrd);
+
+ /*
+ * Inside of the thread, the top of the thread
+ * can't be hidden, the rest are hidden if a
+ * parent somewhere above them is collapsed.
+ * There can be collapse points that are hidden
+ * inside of the tree. They remain collapsed even
+ * if the parent above them uncollapses.
+ */
+ msgno = mn_raw2m(g_sort.msgmap, (long) thrd->rawno);
+ if(msgno)
+ set_lflag(stream, g_sort.msgmap, msgno, MN_CHID, 0);
+
+ if(thrd->next){
+ PINETHRD_S *nthrd;
+
+ nthrd = fetch_thread(stream, thrd->next);
+ if(nthrd)
+ make_thrdflags_consistent(stream, g_sort.msgmap,
+ nthrd,
+ get_lflag(stream, NULL,
+ thrd->rawno,
+ MN_COLL));
+ }
+ }
+ else
+ un_view_thread++;
+ }
+ else
+ un_view_thread++;
+ }
+ else
+ un_view_thread++;
+
+ if(un_view_thread){
+ set_lflags(stream, g_sort.msgmap, MN_CHID2, 0);
+ unview_thread(ps_global, stream, g_sort.msgmap);
+ }
+ else{
+ mn_reset_cur(g_sort.msgmap,
+ mn_raw2m(g_sort.msgmap, raw_current));
+ view_thread(ps_global, stream, g_sort.msgmap, 0);
+ }
+ }
+ else if(SEP_THRDINDX()){
+ set_lflags(stream, g_sort.msgmap, MN_CHID2, 0);
+ collapse_threads(stream, g_sort.msgmap, NULL);
+ }
+ else{
+ thrd = fetch_head_thread(stream);
+ while(thrd){
+ /*
+ * The top-level threads aren't hidden by collapse.
+ */
+ msgno = mn_raw2m(g_sort.msgmap, thrd->rawno);
+ if(msgno)
+ set_lflag(stream, g_sort.msgmap, msgno, MN_CHID, 0);
+
+ if(thrd->next){
+ PINETHRD_S *nthrd;
+
+ nthrd = fetch_thread(stream, thrd->next);
+ if(nthrd)
+ make_thrdflags_consistent(stream, g_sort.msgmap,
+ nthrd,
+ get_lflag(stream, NULL,
+ thrd->rawno,
+ MN_COLL));
+ }
+
+ if(thrd->nextthd)
+ thrd = fetch_thread(stream, thrd->nextthd);
+ else
+ thrd = NULL;
+ }
+ }
+ }
+
+ stream->spare = 1;
+
+ dprint((2, "sort_thread_callback done\n"));
+}
+
+
+void
+collapse_threads(MAILSTREAM *stream, MSGNO_S *msgmap, PINETHRD_S *not_this_thread)
+{
+ PINETHRD_S *thrd = NULL, *nthrd;
+ unsigned long msgno;
+
+ dprint((9, "collapse_threads\n"));
+
+ thrd = fetch_head_thread(stream);
+ while(thrd){
+ if(thrd != not_this_thread){
+ msgno = mn_raw2m(g_sort.msgmap, thrd->rawno);
+
+ /* set collapsed bit */
+ if(msgno){
+ set_lflag(stream, g_sort.msgmap, msgno, MN_COLL, 1);
+ set_lflag(stream, g_sort.msgmap, msgno, MN_CHID, 0);
+ }
+
+ /* hide its children */
+ if(thrd->next && (nthrd = fetch_thread(stream, thrd->next)))
+ set_thread_subtree(stream, nthrd, msgmap, 1, MN_CHID);
+ }
+
+ if(thrd->nextthd)
+ thrd = fetch_thread(stream, thrd->nextthd);
+ else
+ thrd = NULL;
+ }
+
+ dprint((9, "collapse_threads done\n"));
+}
+
+
+void
+make_thrdflags_consistent(MAILSTREAM *stream, MSGNO_S *msgmap, PINETHRD_S *thrd,
+ int a_parent_is_collapsed)
+{
+ PINETHRD_S *nthrd, *bthrd;
+ unsigned long msgno;
+
+ if(!thrd)
+ return;
+
+ msgno = mn_raw2m(msgmap, thrd->rawno);
+
+ if(a_parent_is_collapsed){
+ /* if some parent is collapsed, we should be hidden */
+ if(msgno)
+ set_lflag(stream, msgmap, msgno, MN_CHID, 1);
+ }
+ else{
+ /* no parent is collapsed so we are not hidden */
+ if(msgno)
+ set_lflag(stream, msgmap, msgno, MN_CHID, 0);
+ }
+
+ if(thrd->next){
+ nthrd = fetch_thread(stream, thrd->next);
+ if(nthrd)
+ make_thrdflags_consistent(stream, msgmap, nthrd,
+ a_parent_is_collapsed
+ ? a_parent_is_collapsed
+ : get_lflag(stream, NULL, thrd->rawno,
+ MN_COLL));
+ }
+
+ if(thrd->branch){
+ bthrd = fetch_thread(stream, thrd->branch);
+ if(bthrd)
+ make_thrdflags_consistent(stream, msgmap, bthrd,
+ a_parent_is_collapsed);
+ }
+}
+
+
+long
+calculate_visible_threads(MAILSTREAM *stream)
+{
+ PINETHRD_S *thrd = NULL;
+ long vis = 0L;
+
+ thrd = fetch_head_thread(stream);
+ while(thrd){
+ vis += (thread_has_some_visible(stream, thrd) ? 1 : 0);
+
+ if(thrd->nextthd)
+ thrd = fetch_thread(stream, thrd->nextthd);
+ else
+ thrd = NULL;
+ }
+
+ return(vis);
+}
+
+
+/*
+ * This routine does a couple things. The input is the THREADNODE node
+ * that we get from c-client because of the THREAD command. The rest of
+ * the arguments are used to help guide this function through its
+ * recursive steps. One thing it does is to place the sort order in
+ * the array initially pointed to by the entry argument. All it is doing
+ * is walking the tree in the next then branch order you see below and
+ * incrementing the entry number one for each node. The other thing it
+ * is doing at the same time is to create a PINETHRD_S tree from the
+ * THREADNODE tree. The two trees are completely equivalent but the
+ * PINETHRD_S version has additional back pointers and parent pointers
+ * and so on to make it easier for alpine to deal with it. Each node
+ * of that tree is tied to the data associated with a particular message
+ * by the msgno_thread_info() call, so that we can go from a message
+ * number to the place in the thread tree that message sits.
+ */
+long *
+sort_thread_flatten(THREADNODE *node, MAILSTREAM *stream,
+ long *entry, char *dup_chk, long maxno,
+ PINETHRD_S *thrd, unsigned int flags)
+{
+ PINETHRD_S *newthrd = NULL;
+
+ if(node){
+ if(node->num > 0L && node->num <= maxno){ /* holes happen */
+ if(!dup_chk[node->num]){ /* not a duplicate */
+ *entry = node->num;
+ dup_chk[node->num] = 1;
+
+ /*
+ * Build a richer threading structure that will help us paint
+ * and operate on threads and subthreads.
+ */
+ newthrd = msgno_thread_info(stream, node->num, thrd, flags);
+ if(newthrd){
+ entry++;
+
+ if(node->next)
+ entry = sort_thread_flatten(node->next, stream,
+ entry, dup_chk, maxno,
+ newthrd, THD_NEXT);
+
+ if(node->branch)
+ entry = sort_thread_flatten(node->branch, stream,
+ entry, dup_chk, maxno,
+ newthrd,
+ (flags == THD_TOP) ? THD_TOP
+ : THD_BRANCH);
+ }
+ }
+ }
+ }
+
+ return(entry);
+}
+
+
+/*
+ * Make a copy of c-client's THREAD tree while eliminating dummy nodes.
+ */
+THREADNODE *
+collapse_threadnode_tree(THREADNODE *tree)
+{
+ THREADNODE *newtree = NULL;
+
+ if(tree){
+ if(tree->num){
+ newtree = mail_newthreadnode(NULL);
+ newtree->num = tree->num;
+ if(tree->next)
+ newtree->next = collapse_threadnode_tree(tree->next);
+
+ if(tree->branch)
+ newtree->branch = collapse_threadnode_tree(tree->branch);
+ }
+ else{
+ if(tree->next)
+ newtree = collapse_threadnode_tree(tree->next);
+
+ if(tree->branch){
+ if(newtree){
+ THREADNODE *last_branch = NULL;
+
+ /*
+ * Next moved up to replace "tree" in the tree.
+ * If next has no branches, then we want to branch off
+ * of next. If next has branches, we want to branch off
+ * of the last of those branches instead.
+ */
+ last_branch = newtree;
+ while(last_branch->branch)
+ last_branch = last_branch->branch;
+
+ last_branch->branch = collapse_threadnode_tree(tree->branch);
+ }
+ else
+ newtree = collapse_threadnode_tree(tree->branch);
+ }
+ }
+ }
+
+ return(newtree);
+}
+
+
+/*
+ * Like collapse_threadnode_tree, we collapse the dummy nodes.
+ * In addition we rearrange the threads by order of arrival of
+ * the last message in the thread, rather than the first message
+ * in the thread.
+ */
+THREADNODE *
+collapse_threadnode_tree_sorted(THREADNODE *tree)
+{
+ THREADNODE *sorted_tree = NULL;
+
+ sorted_tree = sort_threads_and_collapse(tree);
+
+ /*
+ * We used to eliminate top-level dummy nodes here so that
+ * orphans would still get sorted together, but we changed
+ * to sort the orphans themselves as top-level threads.
+ *
+ * It might be a matter of choice how they get sorted, but
+ * we'll try doing it this way and not add another feature.
+ */
+
+ return(sorted_tree);
+}
+
+/*
+ * Recurse through the tree, sorting each top-level branch by the
+ * greatest num in the thread.
+ */
+THREADNODE *
+sort_threads_and_collapse(THREADNODE *tree)
+{
+ THREADNODE *newtree = NULL, *newbranchtree = NULL, *newtreefree = NULL;
+
+ if(tree){
+ newtree = mail_newthreadnode(NULL);
+ newtree->num = tree->num;
+
+ /*
+ * Only sort at the top level. Individual threads can
+ * rely on collapse_threadnode_tree
+ */
+ if(tree->next)
+ newtree->next = collapse_threadnode_tree(tree->next);
+
+ if(tree->branch){
+ /*
+ * This recursive call returns an already re-sorted tree.
+ * With that, we can loop through and inject ourselves
+ * where we fit in with that sort, and pass back to the
+ * caller to inject themselves.
+ */
+ newbranchtree = sort_threads_and_collapse(tree->branch);
+ }
+
+ if(newtree->num)
+ newtree = insert_tree_in_place(newtree, newbranchtree);
+ else{
+ /*
+ * If top node is a dummy, here is where we collapse it.
+ */
+ newtreefree = newtree;
+ newtree = insert_tree_in_place(newtree->next, newbranchtree);
+ newtreefree->next = NULL;
+ mail_free_threadnode(&newtreefree);
+ }
+ }
+
+ return(newtree);
+}
+
+/*
+ * Recursively insert each of the top-level nodes in newtree in their place
+ * in tree according to which tree has the most recent arrival
+ */
+THREADNODE *
+insert_tree_in_place(THREADNODE *newtree, THREADNODE *tree)
+{
+ THREADNODE *node = NULL;
+ unsigned long newtree_greatest_num = 0;
+ if(newtree->branch){
+ node = newtree->branch;
+ newtree->branch = NULL;
+ tree = insert_tree_in_place(node, tree);
+ }
+
+ newtree_greatest_num = branch_greatest_num(newtree, 0);
+
+ if(tree){
+ /*
+ * Since tree is already sorted, we can insert when we find something
+ * newtree is less than
+ */
+ if(newtree_greatest_num < branch_greatest_num(tree, 0))
+ newtree->branch = tree;
+ else {
+ for(node = tree; node->branch; node = node->branch){
+ if(newtree_greatest_num < branch_greatest_num(node->branch, 0)){
+ newtree->branch = node->branch;
+ node->branch = newtree;
+ break;
+ }
+ }
+ if(!node->branch)
+ node->branch = newtree;
+
+ newtree = tree;
+ }
+ }
+
+ return(newtree);
+}
+
+/*
+ * Given a thread, return the greatest num in the tree.
+ * is_subthread tells us not to recurse through branches, so
+ * we can split the top level into threads.
+ */
+unsigned long
+branch_greatest_num(THREADNODE *tree, int is_subthread)
+{
+ unsigned long ret, branch_ret;
+
+ ret = tree->num;
+
+ if(tree->next && (branch_ret = branch_greatest_num(tree->next, 1)) > ret)
+ ret = branch_ret;
+ if(is_subthread && tree->branch &&
+ (branch_ret = branch_greatest_num(tree->branch, 1)) > ret)
+ ret = branch_ret;
+
+ return ret;
+}
+
+
+/*
+ * Args stream -- the usual
+ * rawno -- the raw msg num associated with this new node
+ * attached_to_thrd -- the PINETHRD_S node that this is either a next or branch
+ * off of
+ * flags --
+ */
+PINETHRD_S *
+msgno_thread_info(MAILSTREAM *stream, long unsigned int rawno,
+ PINETHRD_S *attached_to_thrd, unsigned int flags)
+{
+ PINELT_S **peltp;
+ MESSAGECACHE *mc;
+
+ if(!stream || rawno < 1L || rawno > stream->nmsgs)
+ return NULL;
+
+ /*
+ * any private elt data yet?
+ */
+ if((mc = mail_elt(stream, rawno))
+ && (*(peltp = (PINELT_S **) &mc->sparep) == NULL)){
+ *peltp = (PINELT_S *) fs_get(sizeof(PINELT_S));
+ memset(*peltp, 0, sizeof(PINELT_S));
+ }
+
+ if((*peltp)->pthrd == NULL)
+ (*peltp)->pthrd = (PINETHRD_S *) fs_get(sizeof(PINETHRD_S));
+
+ memset((*peltp)->pthrd, 0, sizeof(PINETHRD_S));
+
+ (*peltp)->pthrd->rawno = rawno;
+
+ if(attached_to_thrd)
+ (*peltp)->pthrd->head = attached_to_thrd->head;
+ else
+ (*peltp)->pthrd->head = (*peltp)->pthrd->rawno; /* it's me */
+
+ if(flags == THD_TOP){
+ /*
+ * We can tell this thread is a top-level thread because it doesn't
+ * have a parent.
+ */
+ (*peltp)->pthrd->top = (*peltp)->pthrd->rawno; /* I am a top */
+ if(attached_to_thrd){
+ attached_to_thrd->nextthd = (*peltp)->pthrd->rawno;
+ (*peltp)->pthrd->prevthd = attached_to_thrd->rawno;
+ (*peltp)->pthrd->thrdno = attached_to_thrd->thrdno + 1L;
+ }
+ else
+ (*peltp)->pthrd->thrdno = 1L; /* 1st thread */
+
+ g_sort.msgmap->max_thrdno = (*peltp)->pthrd->thrdno;
+ }
+ else if(flags == THD_NEXT){
+ if(attached_to_thrd){
+ attached_to_thrd->next = (*peltp)->pthrd->rawno;
+ (*peltp)->pthrd->parent = attached_to_thrd->rawno;
+ (*peltp)->pthrd->top = attached_to_thrd->top;
+ }
+ }
+ else if(flags == THD_BRANCH){
+ if(attached_to_thrd){
+ attached_to_thrd->branch = (*peltp)->pthrd->rawno;
+ (*peltp)->pthrd->parent = attached_to_thrd->parent;
+ (*peltp)->pthrd->top = attached_to_thrd->top;
+ }
+ }
+
+ return((*peltp)->pthrd);
+}
+
+
+/*
+ * Collapse or expand a threading subtree. Not called from separate thread
+ * index.
+ */
+void
+collapse_or_expand(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap,
+ long unsigned int msgno)
+{
+ int collapsed, adjust_current = 0;
+ PINETHRD_S *thrd = NULL, *nthrd;
+ unsigned long rawno;
+
+ if(!stream)
+ return;
+
+ /*
+ * If msgno is a good msgno, then we collapse or expand the subthread
+ * which begins at msgno. If msgno is 0, we collapse or expand the
+ * entire current thread.
+ */
+
+ if(msgno > 0L && msgno <= mn_get_total(msgmap)){
+ rawno = mn_m2raw(msgmap, msgno);
+ if(rawno)
+ thrd = fetch_thread(stream, rawno);
+ }
+ else if(msgno == 0L){
+ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
+ if(rawno)
+ thrd = fetch_thread(stream, rawno);
+
+ if(thrd && thrd->top != thrd->rawno){
+ adjust_current++;
+ thrd = fetch_thread(stream, thrd->top);
+
+ /*
+ * Special case. If the user is collapsing the entire thread
+ * (msgno == 0), and we are in a Zoomed view, and the top of
+ * the entire thread is not part of the Zoomed view, then watch
+ * out. If we were to collapse the entire thread it would just
+ * disappear, because the top is not in the Zoom. Therefore,
+ * don't allow it. Do what the user probably wants, which is to
+ * collapse the thread at that point instead of the entire thread,
+ * leaving behind the top of the subthread to expand if needed.
+ * In other words, treat it as if they didn't have the
+ * F_SLASH_COLL_ENTIRE feature set.
+ */
+ collapsed = get_lflag(stream, NULL, thrd->rawno, MN_COLL)
+ && thrd->next;
+
+ if(!collapsed && get_lflag(stream, NULL, thrd->rawno, MN_HIDE))
+ thrd = fetch_thread(stream, rawno);
+ }
+ }
+
+
+ if(!thrd)
+ return;
+
+ collapsed = get_lflag(stream, NULL, thrd->rawno, MN_COLL) && thrd->next;
+
+ if(collapsed){
+ msgno = mn_raw2m(msgmap, thrd->rawno);
+ if(msgno > 0L && msgno <= mn_get_total(msgmap)){
+ set_lflag(stream, msgmap, msgno, MN_COLL, 0);
+ if(thrd->next){
+ if((nthrd = fetch_thread(stream, thrd->next)) != NULL)
+ set_thread_subtree(stream, nthrd, msgmap, 0, MN_CHID);
+
+ clear_index_cache_ent(stream, msgno, 0);
+ }
+ }
+ }
+ else if(thrd && thrd->next){
+ msgno = mn_raw2m(msgmap, thrd->rawno);
+ if(msgno > 0L && msgno <= mn_get_total(msgmap)){
+ set_lflag(stream, msgmap, msgno, MN_COLL, 1);
+ if((nthrd = fetch_thread(stream, thrd->next)) != NULL)
+ set_thread_subtree(stream, nthrd, msgmap, 1, MN_CHID);
+
+ clear_index_cache_ent(stream, msgno, 0);
+ }
+ }
+ else
+ q_status_message(SM_ORDER, 0, 1,
+ _("No thread to collapse or expand on this line"));
+
+ /* if current is hidden, adjust */
+ if(adjust_current)
+ adjust_cur_to_visible(stream, msgmap);
+}
+
+
+/*
+ * Select the messages in a subthread. If all of the messages are already
+ * selected, unselect them. This routine is a bit strange because it
+ * doesn't set the MN_SLCT bit. Instead, it sets MN_STMP in apply_command
+ * and then thread_command copies the MN_STMP messages back to MN_SLCT.
+ */
+void
+select_thread_stmp(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap)
+{
+ PINETHRD_S *thrd;
+ unsigned long rawno, in_thread, set_in_thread, save_branch;
+
+ /* ugly bit means the same thing as return of 1 from individual_select */
+ state->ugly_consider_advancing_bit = 0;
+
+ if(!(stream && msgmap))
+ return;
+
+ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
+ if(rawno)
+ thrd = fetch_thread(stream, rawno);
+
+ if(!thrd)
+ return;
+
+ /* run through thrd to see if it is all selected */
+ save_branch = thrd->branch;
+ thrd->branch = 0L;
+ if((set_in_thread = count_lflags_in_thread(stream, thrd, msgmap, MN_STMP))
+ == (in_thread = count_lflags_in_thread(stream, thrd, msgmap, MN_NONE))){
+ /*
+ * If everything is selected, the first unselect should cause
+ * an autozoom. In order to trigger the right code in
+ * thread_command()
+ * copy_lflags()
+ * we set the MN_HIDE bit on the current message here.
+ */
+ if(F_ON(F_AUTO_ZOOM, state) && !any_lflagged(msgmap, MN_HIDE)
+ && any_lflagged(msgmap, MN_STMP) == mn_get_total(msgmap))
+ set_lflag(stream, msgmap, mn_get_cur(msgmap), MN_HIDE, 1);
+ set_thread_lflags(stream, thrd, msgmap, MN_STMP, 0);
+ }
+ else{
+ set_thread_lflags(stream, thrd, msgmap, MN_STMP, 1);
+ state->ugly_consider_advancing_bit = 1;
+ }
+
+ thrd->branch = save_branch;
+
+ if(set_in_thread == in_thread)
+ q_status_message1(SM_ORDER, 0, 3, _("Unselected %s messages in thread"),
+ comatose((long) in_thread));
+ else if(set_in_thread == 0)
+ q_status_message1(SM_ORDER, 0, 3, _("Selected %s messages in thread"),
+ comatose((long) in_thread));
+ else
+ q_status_message1(SM_ORDER, 0, 3,
+ _("Selected %s more messages in thread"),
+ comatose((long) (in_thread-set_in_thread)));
+}
+
+
+/*
+ * Count how many of this system flag in this thread subtree.
+ * If flags == 0 count the messages in the thread.
+ *
+ * Watch out when calling this. The thrd->branch is not part of thrd.
+ * Branch is a sibling to thrd, not a child. Zero out branch before calling
+ * or call on thrd->next and worry about thrd separately.
+ * Ok to call it on top-level thread which has no branch already.
+ */
+unsigned long
+count_flags_in_thread(MAILSTREAM *stream, PINETHRD_S *thrd, long int flags)
+{
+ unsigned long count = 0;
+ PINETHRD_S *nthrd, *bthrd;
+ MESSAGECACHE *mc;
+
+ if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs)
+ return count;
+
+ if(thrd->next){
+ nthrd = fetch_thread(stream, thrd->next);
+ if(nthrd)
+ count += count_flags_in_thread(stream, nthrd, flags);
+ }
+
+ if(thrd->branch){
+ bthrd = fetch_thread(stream, thrd->branch);
+ if(bthrd)
+ count += count_flags_in_thread(stream, bthrd, flags);
+ }
+
+ mc = (thrd && thrd->rawno > 0L && stream && thrd->rawno <= stream->nmsgs)
+ ? mail_elt(stream, thrd->rawno) : NULL;
+ if(mc && mc->valid && FLAG_MATCH(flags, mc, stream))
+ count++;
+
+ return count;
+}
+
+
+/*
+ * Count how many of this local flag in this thread subtree.
+ * If flags == MN_NONE then we just count the messages instead of whether
+ * the messages have a flag set.
+ *
+ * Watch out when calling this. The thrd->branch is not part of thrd.
+ * Branch is a sibling to thrd, not a child. Zero out branch before calling
+ * or call on thrd->next and worry about thrd separately.
+ * Ok to call it on top-level thread which has no branch already.
+ */
+unsigned long
+count_lflags_in_thread(MAILSTREAM *stream, PINETHRD_S *thrd, MSGNO_S *msgmap, int flags)
+{
+ unsigned long count = 0;
+ PINETHRD_S *nthrd, *bthrd;
+
+ if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs)
+ return count;
+
+ if(thrd->next){
+ nthrd = fetch_thread(stream, thrd->next);
+ if(nthrd)
+ count += count_lflags_in_thread(stream, nthrd, msgmap, flags);
+ }
+
+ if(thrd->branch){
+ bthrd = fetch_thread(stream, thrd->branch);
+ if(bthrd)
+ count += count_lflags_in_thread(stream, bthrd, msgmap,flags);
+ }
+
+ if(flags == MN_NONE)
+ count++;
+ else
+ count += get_lflag(stream, msgmap, mn_raw2m(msgmap, thrd->rawno), flags);
+
+ return count;
+}
+
+
+/*
+ * Special-purpose for performance improvement.
+ */
+int
+thread_has_some_visible(MAILSTREAM *stream, PINETHRD_S *thrd)
+{
+ PINETHRD_S *nthrd, *bthrd;
+
+ if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs)
+ return 0;
+
+ if(get_lflag(stream, NULL, thrd->rawno, MN_HIDE) == 0)
+ return 1;
+
+ if(thrd->next){
+ nthrd = fetch_thread(stream, thrd->next);
+ if(nthrd && thread_has_some_visible(stream, nthrd))
+ return 1;
+ }
+
+ if(thrd->branch){
+ bthrd = fetch_thread(stream, thrd->branch);
+ if(bthrd && thread_has_some_visible(stream, bthrd))
+ return 1;
+ }
+
+ return 0;
+}
+
+
+int
+mark_msgs_in_thread(MAILSTREAM *stream, PINETHRD_S *thrd, MSGNO_S *msgmap)
+{
+ int count = 0;
+ PINETHRD_S *nthrd, *bthrd;
+ MESSAGECACHE *mc;
+
+ if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs)
+ return count;
+
+ if(thrd->next){
+ nthrd = fetch_thread(stream, thrd->next);
+ if(nthrd)
+ count += mark_msgs_in_thread(stream, nthrd, msgmap);
+ }
+
+ if(thrd->branch){
+ bthrd = fetch_thread(stream, thrd->branch);
+ if(bthrd)
+ count += mark_msgs_in_thread(stream, bthrd, msgmap);
+ }
+
+ if(stream && thrd->rawno >= 1L && thrd->rawno <= stream->nmsgs &&
+ (mc = mail_elt(stream,thrd->rawno))
+ && !mc->sequence
+ && !mc->private.msg.env){
+ mc->sequence = 1;
+ count++;
+ }
+
+ return count;
+}
+
+
+/*
+ * This sets or clears flags for the messages at this node and below in
+ * a tree.
+ *
+ * Watch out when calling this. The thrd->branch is not part of thrd.
+ * Branch is a sibling to thrd, not a child. Zero out branch before calling
+ * or call on thrd->next and worry about thrd separately.
+ * Ok to call it on top-level thread which has no branch already.
+ */
+void
+set_thread_lflags(MAILSTREAM *stream, PINETHRD_S *thrd, MSGNO_S *msgmap, int flags, int v)
+
+
+
+ /* flags to set or clear */
+ /* set or clear? */
+{
+ unsigned long msgno;
+ PINETHRD_S *nthrd, *bthrd;
+
+ if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs)
+ return;
+
+ msgno = mn_raw2m(msgmap, thrd->rawno);
+
+ set_lflag(stream, msgmap, msgno, flags, v);
+
+ /*
+ * Careful, performance hack. This should logically be a separate
+ * operation on the thread but it is convenient to stick it in here.
+ *
+ * When we back out of viewing a thread to the separate-thread-index
+ * we may leave behind some cached hlines that aren't quite right
+ * because they were collapsed. In particular, the plus_col character
+ * may be wrong. Instead of trying to figure out what it should be just
+ * clear the cache entries for the this thread when we come back in
+ * to view it again.
+ */
+ if(msgno > 0L && flags == MN_CHID2 && v == 1)
+ clear_index_cache_ent(stream, msgno, 0);
+
+ if(thrd->next){
+ nthrd = fetch_thread(stream, thrd->next);
+ if(nthrd)
+ set_thread_lflags(stream, nthrd, msgmap, flags, v);
+ }
+
+ if(thrd->branch){
+ bthrd = fetch_thread(stream, thrd->branch);
+ if(bthrd)
+ set_thread_lflags(stream, bthrd, msgmap, flags, v);
+ }
+}
+
+
+char
+status_symbol_for_thread(MAILSTREAM *stream, PINETHRD_S *thrd, IndexColType type)
+{
+ char status = ' ';
+ unsigned long save_branch, cnt, tot_in_thrd;
+
+ if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs)
+ return status;
+
+ save_branch = thrd->branch;
+ thrd->branch = 0L; /* branch is a sibling, not part of thread */
+
+ /*
+ * This is D if all of thread is deleted,
+ * else A if all of thread is answered,
+ * else F if all of thread is forwarded,
+ * else N if any unseen and not deleted,
+ * else blank.
+ */
+ if(type == iStatus){
+ tot_in_thrd = count_flags_in_thread(stream, thrd, F_NONE);
+ /* all deleted */
+ if(count_flags_in_thread(stream, thrd, F_DEL) == tot_in_thrd)
+ status = 'D';
+ /* all answered */
+ else if(count_flags_in_thread(stream, thrd, F_ANS) == tot_in_thrd)
+ status = 'A';
+ /* all forwarded */
+ else if(count_flags_in_thread(stream, thrd, F_FWD) == tot_in_thrd)
+ status = 'F';
+ /* or any new and not deleted */
+ else if((!IS_NEWS(stream)
+ || F_ON(F_FAKE_NEW_IN_NEWS, ps_global))
+ && count_flags_in_thread(stream, thrd, F_UNDEL|F_UNSEEN))
+ status = 'N';
+ }
+ else if(type == iFStatus){
+ if(!IS_NEWS(stream) || F_ON(F_FAKE_NEW_IN_NEWS, ps_global)){
+ tot_in_thrd = count_flags_in_thread(stream, thrd, F_NONE);
+ cnt = count_flags_in_thread(stream, thrd, F_UNSEEN);
+ if(cnt)
+ status = (cnt == tot_in_thrd) ? 'N' : 'n';
+ }
+ }
+ else if(type == iIStatus || type == iSIStatus){
+ tot_in_thrd = count_flags_in_thread(stream, thrd, F_NONE);
+
+ /* unseen and recent */
+ cnt = count_flags_in_thread(stream, thrd, F_RECENT|F_UNSEEN);
+ if(cnt)
+ status = (cnt == tot_in_thrd) ? 'N' : 'n';
+ else{
+ /* unseen and !recent */
+ cnt = count_flags_in_thread(stream, thrd, F_UNSEEN);
+ if(cnt)
+ status = (cnt == tot_in_thrd) ? 'U' : 'u';
+ else{
+ /* seen and recent */
+ cnt = count_flags_in_thread(stream, thrd, F_RECENT|F_SEEN);
+ if(cnt)
+ status = (cnt == tot_in_thrd) ? 'R' : 'r';
+ }
+ }
+ }
+
+ thrd->branch = save_branch;
+
+ return status;
+}
+
+
+/*
+ * Symbol is * if some message in thread is important,
+ * + if some message is to us,
+ * - if mark-for-cc and some message is cc to us, else blank.
+ */
+char
+to_us_symbol_for_thread(MAILSTREAM *stream, PINETHRD_S *thrd, int consider_flagged)
+{
+ char to_us = ' ';
+ char branch_to_us = ' ';
+ PINETHRD_S *nthrd, *bthrd;
+ MESSAGECACHE *mc;
+
+ if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs)
+ return to_us;
+
+ if(thrd->next){
+ nthrd = fetch_thread(stream, thrd->next);
+ if(nthrd)
+ to_us = to_us_symbol_for_thread(stream, nthrd, consider_flagged);
+ }
+
+ if(((consider_flagged && to_us != '*') || (!consider_flagged && to_us != '+'))
+ && thrd->branch){
+ bthrd = fetch_thread(stream, thrd->branch);
+ if(bthrd)
+ branch_to_us = to_us_symbol_for_thread(stream, bthrd, consider_flagged);
+
+ /* use branch to_us symbol if it has higher priority than what we have so far */
+ if(to_us == ' '){
+ if(branch_to_us == '-' || branch_to_us == '+' || branch_to_us == '*')
+ to_us = branch_to_us;
+ }
+ else if(to_us == '-'){
+ if(branch_to_us == '+' || branch_to_us == '*')
+ to_us = branch_to_us;
+ }
+ else if(to_us == '+'){
+ if(branch_to_us == '*')
+ to_us = branch_to_us;
+ }
+ }
+
+ if((consider_flagged && to_us != '*') || (!consider_flagged && to_us != '+')){
+ if(consider_flagged && thrd && thrd->rawno > 0L
+ && stream && thrd->rawno <= stream->nmsgs
+ && (mc = mail_elt(stream, thrd->rawno))
+ && FLAG_MATCH(F_FLAG, mc, stream))
+ to_us = '*';
+ else if(to_us != '+' && !IS_NEWS(stream)){
+ INDEXDATA_S idata;
+ MESSAGECACHE *mc;
+ ADDRESS *addr;
+
+ memset(&idata, 0, sizeof(INDEXDATA_S));
+ idata.stream = stream;
+ idata.rawno = thrd->rawno;
+ idata.msgno = mn_raw2m(sp_msgmap(stream), idata.rawno);
+ if(idata.rawno > 0L && stream && idata.rawno <= stream->nmsgs
+ && (mc = mail_elt(stream, idata.rawno))){
+ idata.size = mc->rfc822_size;
+ index_data_env(&idata,
+ pine_mail_fetchenvelope(stream, idata.rawno));
+ }
+ else
+ idata.bogus = 2;
+
+ for(addr = fetch_to(&idata); addr; addr = addr->next)
+ if(address_is_us(addr, ps_global)){
+ to_us = '+';
+ break;
+ }
+
+ if(to_us != '+' && resent_to_us(&idata))
+ to_us = '+';
+
+ if(to_us == ' ' && F_ON(F_MARK_FOR_CC,ps_global))
+ for(addr = fetch_cc(&idata); addr; addr = addr->next)
+ if(address_is_us(addr, ps_global)){
+ to_us = '-';
+ break;
+ }
+ }
+ }
+
+ return to_us;
+}
+
+
+/*
+ * This sets or clears flags for the messages at this node and below in
+ * a tree. It doesn't just blindly do it, perhaps it should. Instead,
+ * when un-hiding a subtree it leaves the sub-subtree hidden if a node
+ * is collapsed.
+ *
+ * Watch out when calling this. The thrd->branch is not part of thrd.
+ * Branch is a sibling to thrd, not a child. Zero out branch before calling
+ * or call on thrd->next and worry about thrd separately.
+ * Ok to call it on top-level thread which has no branch already.
+ */
+void
+set_thread_subtree(MAILSTREAM *stream, PINETHRD_S *thrd, MSGNO_S *msgmap, int v, int flags)
+
+
+
+ /* set or clear? */
+ /* flags to set or clear */
+{
+ int hiding;
+ unsigned long msgno;
+ PINETHRD_S *nthrd, *bthrd;
+
+ hiding = (flags == MN_CHID) && v;
+
+ if(!thrd || !stream || thrd->rawno < 1L || thrd->rawno > stream->nmsgs)
+ return;
+
+ msgno = mn_raw2m(msgmap, thrd->rawno);
+
+ set_lflag(stream, msgmap, msgno, flags, v);
+
+ if(thrd->next && (hiding || !get_lflag(stream,NULL,thrd->rawno,MN_COLL))){
+ nthrd = fetch_thread(stream, thrd->next);
+ if(nthrd)
+ set_thread_subtree(stream, nthrd, msgmap, v, flags);
+ }
+
+ if(thrd->branch){
+ bthrd = fetch_thread(stream, thrd->branch);
+ if(bthrd)
+ set_thread_subtree(stream, bthrd, msgmap, v, flags);
+ }
+}
+
+
+/*
+ * View a thread. Move from the thread index screen to a message index
+ * screen for the current thread.
+ *
+ * set_lflags - Set the local flags appropriately to start viewing
+ * the thread. We would not want to set this if we are
+ * already viewing the thread (and expunge or new mail
+ * happened) and we want to preserve the collapsed state
+ * of the subthreads.
+ */
+int
+view_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap, int set_lflags)
+{
+ PINETHRD_S *thrd = NULL;
+ unsigned long rawno, cur;
+
+ if(!any_messages(msgmap, NULL, "to View"))
+ return 0;
+
+ if(!(stream && msgmap))
+ return 0;
+
+ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
+ if(rawno)
+ thrd = fetch_thread(stream, rawno);
+
+ if(thrd && thrd->top && thrd->top != thrd->rawno)
+ thrd = fetch_thread(stream, thrd->top);
+
+ if(!thrd)
+ return 0;
+
+ /*
+ * Clear hidden and collapsed flag for this thread.
+ * And set CHID2.
+ * Don't have to worry about there being a branch because
+ * this is a toplevel thread.
+ */
+ if(set_lflags){
+ set_thread_lflags(stream, thrd, msgmap, MN_COLL | MN_CHID, 0);
+ set_thread_lflags(stream, thrd, msgmap, MN_CHID2, 1);
+ }
+
+ /*
+ * If this is one of those wacky users who like to sort backwards
+ * they would probably prefer that the current message be the last
+ * one in the thread (the one highest up the screen).
+ */
+ if(mn_get_revsort(msgmap)){
+ cur = mn_get_cur(msgmap);
+ while(cur > 1L && get_lflag(stream, msgmap, cur-1L, MN_CHID2))
+ cur--;
+
+ if(cur != mn_get_cur(msgmap))
+ mn_set_cur(msgmap, cur);
+ }
+
+ /* first message in thread might be hidden if zoomed */
+ if(any_lflagged(msgmap, MN_HIDE)){
+ cur = mn_get_cur(msgmap);
+ while(get_lflag(stream, msgmap, cur, MN_HIDE))
+ cur++;
+
+ if(cur != mn_get_cur(msgmap))
+ mn_set_cur(msgmap, cur);
+ }
+
+ msgmap->top = mn_get_cur(msgmap);
+ sp_set_viewing_a_thread(stream, 1);
+
+ state->mangled_screen = 1;
+ setup_for_index_index_screen();
+
+ return 1;
+}
+
+
+int
+unview_thread(struct pine *state, MAILSTREAM *stream, MSGNO_S *msgmap)
+{
+ PINETHRD_S *thrd = NULL, *topthrd = NULL;
+ unsigned long rawno;
+
+ if(!(stream && msgmap))
+ return 0;
+
+ rawno = mn_m2raw(msgmap, mn_get_cur(msgmap));
+ if(rawno)
+ thrd = fetch_thread(stream, rawno);
+
+ if(thrd && thrd->top)
+ topthrd = fetch_thread(stream, thrd->top);
+
+ if(!topthrd)
+ return 0;
+
+ /* hide this thread */
+ set_thread_lflags(stream, topthrd, msgmap, MN_CHID, 1);
+
+ /* clear special CHID2 flags for this thread */
+ set_thread_lflags(stream, topthrd, msgmap, MN_CHID2, 0);
+
+ /* clear CHID for top-level message and set COLL */
+ set_lflag(stream, msgmap, mn_raw2m(msgmap, topthrd->rawno), MN_CHID, 0);
+ set_lflag(stream, msgmap, mn_raw2m(msgmap, topthrd->rawno), MN_COLL, 1);
+
+ mn_set_cur(msgmap, mn_raw2m(msgmap, topthrd->rawno));
+ sp_set_viewing_a_thread(stream, 0);
+ setup_for_thread_index_screen();
+
+ return 1;
+}
+
+
+PINETHRD_S *
+find_thread_by_number(MAILSTREAM *stream, MSGNO_S *msgmap, long int target, PINETHRD_S *startthrd)
+{
+ PINETHRD_S *thrd = NULL;
+
+ if(!(stream && msgmap))
+ return(thrd);
+
+ thrd = startthrd;
+
+ if(!thrd || !(thrd->prevthd || thrd->nextthd))
+ thrd = fetch_thread(stream, mn_m2raw(msgmap, mn_get_cur(msgmap)));
+
+ if(thrd && !(thrd->prevthd || thrd->nextthd) && thrd->head)
+ thrd = fetch_thread(stream, thrd->head);
+
+ if(thrd){
+ /* go forward from here */
+ if(thrd->thrdno < target){
+ while(thrd){
+ if(thrd->thrdno == target)
+ break;
+
+ if(mn_get_revsort(msgmap) && thrd->prevthd)
+ thrd = fetch_thread(stream, thrd->prevthd);
+ else if(!mn_get_revsort(msgmap) && thrd->nextthd)
+ thrd = fetch_thread(stream, thrd->nextthd);
+ else
+ thrd = NULL;
+ }
+ }
+ /* back up from here */
+ else if(thrd->thrdno > target
+ && (mn_get_revsort(msgmap)
+ || (thrd->thrdno - target) < (target - 1L))){
+ while(thrd){
+ if(thrd->thrdno == target)
+ break;
+
+ if(mn_get_revsort(msgmap) && thrd->nextthd)
+ thrd = fetch_thread(stream, thrd->nextthd);
+ else if(!mn_get_revsort(msgmap) && thrd->prevthd)
+ thrd = fetch_thread(stream, thrd->prevthd);
+ else
+ thrd = NULL;
+ }
+ }
+ /* go forward from head */
+ else if(thrd->thrdno > target){
+ if(thrd->head){
+ thrd = fetch_thread(stream, thrd->head);
+ while(thrd){
+ if(thrd->thrdno == target)
+ break;
+
+ if(thrd->nextthd)
+ thrd = fetch_thread(stream, thrd->nextthd);
+ else
+ thrd = NULL;
+ }
+ }
+ }
+ }
+
+ return(thrd);
+}
+
+
+/*
+ * Set search bit for every message in a thread.
+ *
+ * Watch out when calling this. The thrd->branch is not part of thrd.
+ * Branch is a sibling to thrd, not a child. Zero out branch before calling
+ * or call on thrd->next and worry about thrd separately. Top-level threads
+ * already have a branch equal to zero.
+ *
+ * If msgset is non-NULL, then only set the search bit for a message if that
+ * message is included in the msgset.
+ */
+void
+set_search_bit_for_thread(MAILSTREAM *stream, PINETHRD_S *thrd, SEARCHSET **msgset)
+{
+ PINETHRD_S *nthrd, *bthrd;
+
+ if(!(stream && thrd))
+ return;
+
+ if(thrd->rawno > 0L && thrd->rawno <= stream->nmsgs
+ && (!(msgset && *msgset) || in_searchset(*msgset, thrd->rawno)))
+ mm_searched(stream, thrd->rawno);
+
+ if(thrd->next){
+ nthrd = fetch_thread(stream, thrd->next);
+ if(nthrd)
+ set_search_bit_for_thread(stream, nthrd, msgset);
+ }
+
+ if(thrd->branch){
+ bthrd = fetch_thread(stream, thrd->branch);
+ if(bthrd)
+ set_search_bit_for_thread(stream, bthrd, msgset);
+ }
+}
diff --git a/pith/thread.h b/pith/thread.h
new file mode 100644
index 00000000..40b09bb0
--- /dev/null
+++ b/pith/thread.h
@@ -0,0 +1,111 @@
+/*
+ * $Id: thread.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_THREAD_INCLUDED
+#define PITH_THREAD_INCLUDED
+
+
+#include "../pith/msgno.h"
+#include "../pith/indxtype.h"
+#include "../pith/state.h"
+#include "../pith/conf.h"
+
+
+#define THD_TOP 0x0000 /* start of an individual thread */
+#define THD_NEXT 0x0001
+#define THD_BRANCH 0x0004
+
+typedef struct pine_thrd {
+ unsigned long rawno; /* raw msgno of this message */
+ unsigned long thrdno; /* thread number */
+ unsigned long flags;
+ unsigned long next; /* msgno of first reply to us */
+ unsigned long branch; /* like THREADNODE branch, next replier */
+ unsigned long parent; /* message that this is a reply to */
+ unsigned long nextthd; /* next thread, only tops have this */
+ unsigned long prevthd; /* previous thread, only tops have this */
+ unsigned long top; /* top of this thread */
+ unsigned long head; /* head of the whole thread list */
+} PINETHRD_S;
+
+
+/*
+ * Some macros useful for threading
+ */
+
+/* Sort is a threaded sort */
+#define SORT_IS_THREADED(msgmap) \
+ (mn_get_sort(msgmap) == SortThread \
+ || mn_get_sort(msgmap) == SortSubject2)
+
+#define SEP_THRDINDX() \
+ (ps_global->thread_index_style == THRDINDX_SEP \
+ || ps_global->thread_index_style == THRDINDX_SEP_AUTO)
+
+#define COLL_THRDS() \
+ (ps_global->thread_index_style == THRDINDX_COLL)
+
+#define THRD_AUTO_VIEW() \
+ (ps_global->thread_index_style == THRDINDX_SEP_AUTO)
+
+/* We are threading now, pay attention to all the other variables */
+#define THREADING() \
+ (!ps_global->turn_off_threading_temporarily \
+ && SORT_IS_THREADED(ps_global->msgmap) \
+ && (SEP_THRDINDX() \
+ || ps_global->thread_disp_style != THREAD_NONE))
+
+/* If we were to view the folder, we would get a thread index */
+#define THRD_INDX_ENABLED() \
+ (SEP_THRDINDX() \
+ && THREADING())
+
+/* We are in the thread index (or would be if we weren't in an index menu) */
+#define THRD_INDX() \
+ (THRD_INDX_ENABLED() \
+ && !sp_viewing_a_thread(ps_global->mail_stream))
+
+/* The thread command ought to work now */
+#define THRD_COLLAPSE_ENABLE() \
+ (THREADING() \
+ && !THRD_INDX() \
+ && ps_global->thread_disp_style != THREAD_NONE)
+
+
+/* exported protoypes */
+PINETHRD_S *fetch_thread(MAILSTREAM *, unsigned long);
+PINETHRD_S *fetch_head_thread(MAILSTREAM *);
+void set_flags_for_thread(MAILSTREAM *, MSGNO_S *, int, PINETHRD_S *, int);
+void erase_threading_info(MAILSTREAM *, MSGNO_S *);
+void sort_thread_callback(MAILSTREAM *, THREADNODE *);
+void collapse_threads(MAILSTREAM *, MSGNO_S *, PINETHRD_S *);
+PINETHRD_S *msgno_thread_info(MAILSTREAM *, unsigned long, PINETHRD_S *, unsigned);
+void collapse_or_expand(struct pine *, MAILSTREAM *, MSGNO_S *, unsigned long);
+void select_thread_stmp(struct pine *, MAILSTREAM *, MSGNO_S *);
+unsigned long count_flags_in_thread(MAILSTREAM *, PINETHRD_S *, long);
+unsigned long count_lflags_in_thread(MAILSTREAM *, PINETHRD_S *, MSGNO_S *, int);
+int thread_has_some_visible(MAILSTREAM *, PINETHRD_S *);
+int mark_msgs_in_thread(MAILSTREAM *, PINETHRD_S *, MSGNO_S *);
+void set_thread_lflags(MAILSTREAM *, PINETHRD_S *, MSGNO_S *, int, int);
+char status_symbol_for_thread(MAILSTREAM *, PINETHRD_S *, IndexColType);
+char to_us_symbol_for_thread(MAILSTREAM *, PINETHRD_S *, int);
+void set_thread_subtree(MAILSTREAM *, PINETHRD_S *, MSGNO_S *, int, int);
+int view_thread(struct pine *, MAILSTREAM *, MSGNO_S *, int);
+int unview_thread(struct pine *, MAILSTREAM *, MSGNO_S *);
+PINETHRD_S *find_thread_by_number(MAILSTREAM *, MSGNO_S *, long, PINETHRD_S *);
+void set_search_bit_for_thread(MAILSTREAM *, PINETHRD_S *, SEARCHSET **);
+
+
+#endif /* PITH_THREAD_INCLUDED */
diff --git a/pith/url.c b/pith/url.c
new file mode 100644
index 00000000..173cb879
--- /dev/null
+++ b/pith/url.c
@@ -0,0 +1,542 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: url.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/url.h"
+#include "../pith/mailview.h"
+#include "../pith/string.h"
+
+/*
+ * Internal prototypes
+ */
+char *rfc1738_scheme_part(char *);
+int rfc1738uchar(char *);
+int rfc1738xchar(char *);
+
+
+/*
+ * * * * * * * * * RFC 1738 support routines * * * * * * * *
+ */
+
+
+/*
+ * Various helpful definitions
+ */
+#define RFC1738_SAFE "$-_.+" /* "safe" */
+#define RFC1738_EXTRA "!*'()," /* "extra" */
+#define RFC1738_RSVP ";/?:@&=" /* "reserved" */
+#define RFC1738_NEWS "-.+_" /* valid for "news:" URL */
+#define RFC1738_FUDGE "#{}|\\^~[]" /* Unsafe, but popular */
+#define RFC1738_ESC(S) (*(S) == '%' && isxpair((S) + 1))
+
+
+/*
+ * rfc1738_scan -- Scan the given line for possible URLs as defined
+ * in RFC1738
+ */
+char *
+rfc1738_scan(char *line, int *len)
+{
+ char *colon, *start, *end;
+ int n;
+
+ /* process each : in the line */
+ for(; (colon = strindex(line, ':')) != NULL; line = end){
+ end = colon + 1;
+ if(colon == line) /* zero length scheme? */
+ continue;
+
+ /*
+ * Valid URL (ala RFC1738 BNF)? First, first look to the
+ * left to make sure there are valid "scheme" chars...
+ */
+ start = colon - 1;
+ while(1)
+ if(!(isdigit((unsigned char) *start)
+ || isalpha((unsigned char) *start)
+ || strchr("+-.", *start))){
+ start++; /* advance over bogus char */
+ break;
+ }
+ else if(start > line)
+ start--;
+ else
+ break;
+
+ /*
+ * Make sure everyhing up to the colon is a known scheme...
+ */
+ if(start && (n = colon - start) && !isdigit((unsigned char) *start)
+ && (((n == 3
+ && (*start == 'F' || *start == 'f')
+ && !struncmp(start+1, "tp", 2))
+ || (n == 4
+ && (((*start == 'H' || *start == 'h')
+ && !struncmp(start + 1, "ttp", 3))
+ || ((*start == 'N' || *start == 'n')
+ && !struncmp(start + 1, "ews", 3))
+ || ((*start == 'N' || *start == 'n')
+ && !struncmp(start + 1, "ntp", 3))
+ || ((*start == 'W' || *start == 'w')
+ && !struncmp(start + 1, "ais", 3))
+#ifdef ENABLE_LDAP
+ || ((*start == 'L' || *start == 'l')
+ && !struncmp(start + 1, "dap", 3))
+#endif
+ || ((*start == 'I' || *start == 'i')
+ && !struncmp(start + 1, "map", 3))
+ || ((*start == 'F' || *start == 'f')
+ && !struncmp(start + 1, "ile", 3))))
+ || (n == 5
+ && (*start == 'H' || *start == 'h')
+ && !struncmp(start+1, "ttps", 4))
+ || (n == 6
+ && (((*start == 'G' || *start == 'g')
+ && !struncmp(start+1, "opher", 5))
+ || ((*start == 'M' || *start == 'm')
+ && !struncmp(start + 1, "ailto", 5))
+ || ((*start == 'T' || *start == 't')
+ && !struncmp(start + 1, "elnet", 5))))
+ || (n == 8
+ && (*start == 'P' || *start == 'p')
+ && !struncmp(start + 1, "rospero", 7))
+ || (n == 11
+ && (*start == 'x' || *start == 'X')
+ && !struncmp(start + 1, "-pine-help", 10))
+ || (n == 13
+ && (*start == 'x' || *start == 'X')
+ && !struncmp(start + 1, "-alpine-help", 12)))
+ || url_external_specific_handler(start, n))){
+ /*
+ * Second, make sure that everything to the right of the
+ * colon is valid for a "schemepart"...
+ */
+
+ if((end = rfc1738_scheme_part(colon + 1)) - colon > 1){
+ int i, j;
+
+ /* make sure something useful follows colon */
+ for(i = 0, j = end - colon; i < j; i++)
+ if(!strchr(RFC1738_RSVP, colon[i]))
+ break;
+
+ if(i != j){
+ *len = end - start;
+
+ /*
+ * Special case handling for comma.
+ * See the problem is comma's valid, but if it's the
+ * last character in the url, it's likely intended
+ * as a delimiter in the text rather part of the URL.
+ * In most cases any way, that's why we have the
+ * exception.
+ */
+ if(*(end - 1) == ','
+ || (*(end - 1) == '.' && (!*end || *end == ' ')))
+ (*len)--;
+
+ if(*len - (colon - start) > 0)
+ return(start);
+ }
+ }
+ }
+ }
+
+ return(NULL);
+}
+
+
+/*
+ * rfc1738_scheme_part - make sure what's to the right of the
+ * colon is valid
+ *
+ * NOTE: we have a problem matching closing parens when users
+ * bracket the url in parens. So, lets try terminating our
+ * match on any closing paren that doesn't have a coresponding
+ * open-paren.
+ */
+char *
+rfc1738_scheme_part(char *s)
+{
+ int n, paren = 0, bracket = 0;
+
+ while(1)
+ switch(*s){
+ default :
+ if((n = rfc1738xchar(s)) != 0){
+ s += n;
+ break;
+ }
+
+ case '\0' :
+ return(s);
+
+ case '[' :
+ bracket++;
+ s++;
+ break;
+
+ case ']' :
+ if(bracket--){
+ s++;
+ break;
+ }
+
+ return(s);
+
+ case '(' :
+ paren++;
+ s++;
+ break;
+
+ case ')' :
+ if(paren--){
+ s++;
+ break;
+ }
+
+ return(s);
+ }
+}
+
+
+
+/*
+ * rfc1738_str - convert rfc1738 escaped octets in place
+ */
+char *
+rfc1738_str(char *s)
+{
+ register char *p = s, *q = s;
+
+ while(1)
+ switch(*q = *p++){
+ case '%' :
+ if(isxpair(p)){
+ *q = X2C(p);
+ p += 2;
+ }
+
+ default :
+ q++;
+ break;
+
+ case '\0':
+ return(s);
+ }
+}
+
+
+/*
+ * rfc1738uchar - returns TRUE if the given char fits RFC 1738 "uchar" BNF
+ */
+int
+rfc1738uchar(char *s)
+{
+ return((RFC1738_ESC(s)) /* "escape" */
+ ? 2
+ : (isalnum((unsigned char) *s) /* alphanumeric */
+ || strchr(RFC1738_SAFE, *s) /* other special stuff */
+ || strchr(RFC1738_EXTRA, *s)));
+}
+
+
+/*
+ * rfc1738xchar - returns TRUE if the given char fits RFC 1738 "xchar" BNF
+ */
+int
+rfc1738xchar(char *s)
+{
+ int n;
+
+ return((n = rfc1738uchar(s))
+ ? n
+ : (strchr(RFC1738_RSVP, *s) != NULL
+ || strchr(RFC1738_FUDGE, *s)));
+}
+
+
+/*
+ * rfc1738_num - return long value of a string of digits, possibly escaped
+ */
+unsigned long
+rfc1738_num(char **s)
+{
+ register char *p = *s;
+ unsigned long n = 0L;
+
+ for(; *p; p++)
+ if(*p == '%' && isxpair(p+1)){
+ int c = X2C(p+1);
+ if(isdigit((unsigned char) c)){
+ n = (c - '0') + (n * 10);
+ p += 2;
+ }
+ else
+ break;
+ }
+ else if(isdigit((unsigned char) *p))
+ n = (*p - '0') + (n * 10);
+ else
+ break;
+
+ *s = p;
+ return(n);
+}
+
+
+int
+rfc1738_group(char *s)
+{
+ return(isalnum((unsigned char) *s)
+ || RFC1738_ESC(s)
+ || strchr(RFC1738_NEWS, *s));
+}
+
+
+/*
+ * Encode (hexify) a mailto url.
+ *
+ * Args s -- src url
+ *
+ * Returns An allocated string which is suitably encoded.
+ * Result should be freed by caller.
+ *
+ * Since we don't know here which characters are reserved characters (? and &)
+ * for use in delimiting the pieces of the url and which are just those
+ * characters contained in the data that should be encoded, we always encode
+ * them. That's because we know we don't use those as reserved characters.
+ * If you do use those as reserved characters you have to encode each part
+ * separately.
+ */
+char *
+rfc1738_encode_mailto(char *s)
+{
+ char *d, *ret = NULL;
+
+ if(s){
+ /* Worst case, encode every character */
+ ret = d = (char *)fs_get((3*strlen(s) + 1) * sizeof(char));
+ while(*s){
+ if(isalnum((unsigned char)*s)
+ || strchr(RFC1738_SAFE, *s)
+ || strchr(RFC1738_EXTRA, *s))
+ *d++ = *s++;
+ else{
+ *d++ = '%';
+ C2XPAIR(*s, d);
+ s++;
+ }
+ }
+
+ *d = '\0';
+ }
+
+ return(ret);
+}
+
+
+/*
+ * * * * * * * * * RFC 1808 support routines * * * * * * * *
+ */
+
+
+int
+rfc1808_tokens(char *url, char **scheme, char **net_loc, char **path,
+ char **parms, char **query, char **frag)
+{
+ char *p, *q, *start, *tmp = cpystr(url);
+
+ start = tmp;
+ if((p = strchr(start, '#')) != NULL){ /* fragment spec? */
+ *p++ = '\0';
+ if(*p)
+ *frag = cpystr(p);
+ }
+
+ if((p = strchr(start, ':')) && p != start){ /* scheme part? */
+ for(q = start; q < p; q++)
+ if(!(isdigit((unsigned char) *q)
+ || isalpha((unsigned char) *q)
+ || strchr("+-.", *q)))
+ break;
+
+ if(p == q){
+ *p++ = '\0';
+ *scheme = cpystr(start);
+ start = p;
+ }
+ }
+
+ if(*start == '/' && *(start+1) == '/'){ /* net_loc */
+ if((p = strchr(start+2, '/')) != NULL)
+ *p++ = '\0';
+
+ *net_loc = cpystr(start+2);
+ if(p)
+ start = p;
+ else *start = '\0'; /* End of parse */
+ }
+
+ if((p = strchr(start, '?')) != NULL){
+ *p++ = '\0';
+ *query = cpystr(p);
+ }
+
+ if((p = strchr(start, ';')) != NULL){
+ *p++ = '\0';
+ *parms = cpystr(p);
+ }
+
+ if(*start)
+ *path = cpystr(start);
+
+ fs_give((void **) &tmp);
+
+ return(1);
+}
+
+
+
+/*
+ * web_host_scan -- Scan the given line for possible web host names
+ *
+ * NOTE: scan below is limited to DNS names ala RFC1034
+ */
+char *
+web_host_scan(char *line, int *len)
+{
+ char *end, last = '\0';
+
+ for(; *line; last = *line++)
+ if((*line == 'w' || *line == 'W')
+ && (!last || !(isalnum((unsigned char) last)
+ || last == '.' || last == '-' || last == '/'))
+ && (((*(line + 1) == 'w' || *(line + 1) == 'W') /* "www." */
+ && (*(line + 2) == 'w' || *(line + 2) == 'W'))
+ || ((*(line + 1) == 'e' || *(line + 1) == 'E') /* "web." */
+ && (*(line + 2) == 'b' || *(line + 2) == 'B')))
+ && (*(line + 3) == '.')){
+ end = rfc1738_scheme_part(line + 3);
+ if((*len = end - line) > ((*(line+3) == '.') ? 4 : 3)){
+ /* Dread comma exception, see note in rfc1738_scan */
+ if(strchr(",:", *(line + (*len) - 1))
+ || (*(line + (*len) - 1) == '.'
+ && (!*(line + (*len)) || *(line + (*len)) == ' ')))
+ (*len)--;
+
+ return(line);
+ }
+ else
+ line += 3;
+ }
+
+ return(NULL);
+}
+
+
+/*
+ * mail_addr_scan -- Scan the given line for possible RFC822 addr-spec's
+ *
+ * NOTE: Well, OK, not strictly addr-specs since there's alot of junk
+ * we're tying to sift thru and we'd like to minimize false-pos
+ * matches.
+ */
+char *
+mail_addr_scan(char *line, int *len)
+{
+ char *amp, *start, *end;
+/*
+ * This list is not the whole standards-based list, this is just a list
+ * of likely email address characters. We don't want to include everything
+ * because punctuation in the text might get mixed in with the address.
+ */
+#define NONALPHANUMOK ".-_+%/="
+
+ /* process each : in the line */
+ for(; (amp = strindex(line, '@')) != NULL; line = end){
+ end = amp + 1;
+ /* zero length addr? */
+ if(amp == line || !(isalnum((unsigned char) *(start = amp - 1))
+ || strchr(NONALPHANUMOK, *start)))
+ continue;
+
+ /*
+ * Valid address (ala RFC822 BNF)? First, first look to the
+ * left to make sure there are valid "scheme" chars...
+ */
+ while(1)
+ /* NOTE: we're not doing quoted-strings */
+ if(!(isalnum((unsigned char) *start) || strchr(NONALPHANUMOK, *start))){
+ /* advance over bogus char, and erase leading punctuation */
+ for(start++; *start && strchr(NONALPHANUMOK, *start); start++)
+ ;
+
+ break;
+ }
+ else if(start > line)
+ start--;
+ else
+ break;
+
+ /*
+ * Make sure everyhing up to the colon is a known scheme...
+ */
+ if(start && (amp - start) > 0){
+ /*
+ * Second, make sure that everything to the right of
+ * amp is valid for a "domain"...
+ */
+ if(*(end = amp + 1) == '['){ /* domain literal */
+ int dots = 3;
+
+ for(++end; *end ; end++)
+ if(*end == ']'){
+ if(!dots){
+ *len = end - start + 1;
+ return(start);
+ }
+ else
+ break; /* bogus */
+ }
+ else if(*end == '.'){
+ if(--dots < 0)
+ break; /* bogus */
+ }
+ else if(!isdigit((unsigned char) *end))
+ break; /* bogus */
+ }
+ else if(isalnum((unsigned char) *end)){ /* domain name? */
+ for(++end; ; end++)
+ if(!(*end && (isalnum((unsigned char) *end)
+ || *end == '-'
+ || *end == '.'
+ || *end == '_'))){
+ /* can't end with dash, dot or underscore */
+ while(!isalnum((unsigned char) *(end - 1)))
+ end--;
+
+ *len = end - start;
+ return(start);
+ }
+ }
+ }
+ }
+
+ return(NULL);
+}
diff --git a/pith/url.h b/pith/url.h
new file mode 100644
index 00000000..a1936c89
--- /dev/null
+++ b/pith/url.h
@@ -0,0 +1,31 @@
+/*
+ * $Id: url.h 764 2007-10-23 23:44:49Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_URL_INCLUDED
+#define PITH_URL_INCLUDED
+
+
+/* exported protoypes */
+char *rfc1738_scan(char *, int *);
+char *rfc1738_str(char *);
+unsigned long rfc1738_num(char **);
+int rfc1738_group(char *);
+char *rfc1738_encode_mailto(char *);
+int rfc1808_tokens(char *, char **, char **, char **, char **, char **, char **);
+char *web_host_scan(char *, int *);
+char *mail_addr_scan(char *, int *);
+
+
+#endif /* PITH_URL_INCLUDED */
diff --git a/pith/user.h b/pith/user.h
new file mode 100644
index 00000000..7372996c
--- /dev/null
+++ b/pith/user.h
@@ -0,0 +1,30 @@
+/*
+ * $Id: user.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_USER_INCLUDED
+#define PITH_USER_INCLUDED
+
+
+/*
+ * used to store system derived user info
+ */
+typedef struct user_info {
+ char *homedir;
+ char *login;
+ char *fullname;
+} USER_S;
+
+
+#endif /* PITH_USER_INCLUDED */
diff --git a/pith/util.c b/pith/util.c
new file mode 100644
index 00000000..56f586da
--- /dev/null
+++ b/pith/util.c
@@ -0,0 +1,40 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: util.c 761 2007-10-23 22:35:18Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include "../pith/headers.h"
+#include "../pith/util.h"
+
+
+/*
+ * Internal prototypes
+ */
+
+
+/*
+ * Allocate space for an int and copy val into it.
+ */
+int *
+cpyint(int val)
+{
+ int *ip;
+
+ ip = (int *)fs_get(sizeof(int));
+
+ *ip = val;
+
+ return(ip);
+}
diff --git a/pith/util.h b/pith/util.h
new file mode 100644
index 00000000..defbca6c
--- /dev/null
+++ b/pith/util.h
@@ -0,0 +1,68 @@
+/*
+ * $Id: util.h 761 2007-10-23 22:35:18Z hubert@u.washington.edu $
+ *
+ * ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef PITH_UTIL_INCLUDED
+#define PITH_UTIL_INCLUDED
+
+
+#include "../pith/news.h"
+
+
+#define plural(n) ((n) == 1 ? "" : "s")
+
+
+#define READONLY_FOLDER(S) ((S) && (S)->rdonly && !IS_NEWS(S))
+
+#define STREAMNAME(S) (((S) && sp_fldr(S) && sp_fldr(S)[0]) \
+ ? sp_fldr(S) \
+ : ((S) && (S)->mailbox && (S)->mailbox[0]) \
+ ? (S)->mailbox \
+ : "?")
+
+
+/*
+ * Simple, handy macro to determine if folder name is remote
+ * (on an imap server)
+ */
+#define IS_REMOTE(X) (*(X) == '{' && *((X) + 1) && *((X) + 1) != '}' \
+ && strchr(((X) + 2), '}'))
+
+
+/* (0,0) is upper left */
+typedef struct screen_position {
+ int row;
+ int col;
+} Pos;
+
+
+#define SCREEN_FUN_NULL ((void (*)(struct pine *)) NULL)
+
+
+/* exported protoypes */
+int *cpyint(int);
+
+/* currently mandatory to implement stubs */
+
+/* called when we detect a serious program error */
+void panic(char *);
+
+/* called when testing to see if panic state is in effect */
+int panicking(void);
+
+/* logs or prints a message then exits */
+void exceptional_exit(char *, int);
+
+
+#endif /* PITH_UTIL_INCLUDED */
diff --git a/po/ChangeLog b/po/ChangeLog
new file mode 100644
index 00000000..5d6bc693
--- /dev/null
+++ b/po/ChangeLog
@@ -0,0 +1,12 @@
+2007-10-05 gettextize <bug-gnu-gettext@gnu.org>
+
+ * Makefile.in.in: New file, from gettext-0.14.6.
+ * boldquot.sed: New file, from gettext-0.14.6.
+ * en@boldquot.header: New file, from gettext-0.14.6.
+ * en@quot.header: New file, from gettext-0.14.6.
+ * insert-header.sin: New file, from gettext-0.14.6.
+ * quot.sed: New file, from gettext-0.14.6.
+ * remove-potcdate.sin: New file, from gettext-0.14.6.
+ * Rules-quot: New file, from gettext-0.14.6.
+ * POTFILES.in: New file.
+
diff --git a/po/Makefile.in b/po/Makefile.in
new file mode 100644
index 00000000..bf134228
--- /dev/null
+++ b/po/Makefile.in
@@ -0,0 +1,384 @@
+# Makefile for PO directory in any package using GNU gettext.
+# Copyright (C) 1995-1997, 2000-2005 by Ulrich Drepper <drepper@gnu.ai.mit.edu>
+#
+# This file can be copied and used freely without restrictions. It can
+# be used in projects which are not available under the GNU General Public
+# License but which still want to provide support for the GNU gettext
+# functionality.
+# Please note that the actual code of GNU gettext is covered by the GNU
+# General Public License and is *not* in the public domain.
+#
+# Origin: gettext-0.14.4
+
+PACKAGE = alpine
+VERSION = 2.10
+PACKAGE_BUGREPORT = chappa@gmx.com
+
+SHELL = /bin/sh
+
+
+srcdir = .
+top_srcdir = ..
+
+
+prefix = /usr/local
+exec_prefix = ${prefix}
+datadir = ${prefix}/share
+localedir = $(datadir)/locale
+gettextsrcdir = $(datadir)/gettext/po
+
+INSTALL = /usr/bin/install -c
+INSTALL_DATA = ${INSTALL} -m 644
+MKINSTALLDIRS = $(top_builddir)/./mkinstalldirs
+mkinstalldirs = $(SHELL) $(MKINSTALLDIRS)
+
+GMSGFMT = /usr/bin/msgfmt
+MSGFMT = /usr/bin/msgfmt
+XGETTEXT = /usr/bin/xgettext
+MSGMERGE = msgmerge
+MSGMERGE_UPDATE = /usr/bin/msgmerge --update
+MSGINIT = msginit
+MSGCONV = msgconv
+MSGFILTER = msgfilter
+
+POFILES = @POFILES@
+GMOFILES = @GMOFILES@
+UPDATEPOFILES = @UPDATEPOFILES@
+DUMMYPOFILES = @DUMMYPOFILES@
+DISTFILES.common = Makefile.in.in remove-potcdate.sin \
+$(DISTFILES.common.extra1) $(DISTFILES.common.extra2) $(DISTFILES.common.extra3)
+DISTFILES = $(DISTFILES.common) Makevars POTFILES.in \
+$(POFILES) $(GMOFILES) \
+$(DISTFILES.extra1) $(DISTFILES.extra2) $(DISTFILES.extra3)
+
+POTFILES = \
+
+CATALOGS = @CATALOGS@
+
+# Makevars gets inserted here. (Don't remove this line!)
+
+.SUFFIXES:
+.SUFFIXES: .po .gmo .mo .sed .sin .nop .po-create .po-update
+
+.po.mo:
+ @echo "$(MSGFMT) -c -o $@ $<"; \
+ $(MSGFMT) -c -o t-$@ $< && mv t-$@ $@
+
+.po.gmo:
+ @lang=`echo $* | sed -e 's,.*/,,'`; \
+ test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \
+ echo "$${cdcmd}rm -f $${lang}.gmo && $(GMSGFMT) -c --statistics -o $${lang}.gmo $${lang}.po"; \
+ cd $(srcdir) && rm -f $${lang}.gmo && $(GMSGFMT) -c --statistics -o t-$${lang}.gmo $${lang}.po && mv t-$${lang}.gmo $${lang}.gmo
+
+.sin.sed:
+ sed -e '/^#/d' $< > t-$@
+ mv t-$@ $@
+
+
+all: all-yes
+
+all-yes: stamp-po
+all-no:
+
+# $(srcdir)/$(DOMAIN).pot is only created when needed. When xgettext finds no
+# internationalized messages, no $(srcdir)/$(DOMAIN).pot is created (because
+# we don't want to bother translators with empty POT files). We assume that
+# LINGUAS is empty in this case, i.e. $(POFILES) and $(GMOFILES) are empty.
+# In this case, stamp-po is a nop (i.e. a phony target).
+
+# stamp-po is a timestamp denoting the last time at which the CATALOGS have
+# been loosely updated. Its purpose is that when a developer or translator
+# checks out the package via CVS, and the $(DOMAIN).pot file is not in CVS,
+# "make" will update the $(DOMAIN).pot and the $(CATALOGS), but subsequent
+# invocations of "make" will do nothing. This timestamp would not be necessary
+# if updating the $(CATALOGS) would always touch them; however, the rule for
+# $(POFILES) has been designed to not touch files that don't need to be
+# changed.
+stamp-po: $(srcdir)/$(DOMAIN).pot
+ test ! -f $(srcdir)/$(DOMAIN).pot || \
+ test -z "$(GMOFILES)" || $(MAKE) $(GMOFILES)
+ @test ! -f $(srcdir)/$(DOMAIN).pot || { \
+ echo "touch stamp-po" && \
+ echo timestamp > stamp-poT && \
+ mv stamp-poT stamp-po; \
+ }
+
+# Note: Target 'all' must not depend on target '$(DOMAIN).pot-update',
+# otherwise packages like GCC can not be built if only parts of the source
+# have been downloaded.
+
+# This target rebuilds $(DOMAIN).pot; it is an expensive operation.
+# Note that $(DOMAIN).pot is not touched if it doesn't need to be changed.
+$(DOMAIN).pot-update: $(POTFILES) $(srcdir)/POTFILES.in remove-potcdate.sed
+ if test -n '$(MSGID_BUGS_ADDRESS)' || test '$(PACKAGE_BUGREPORT)' = '@'PACKAGE_BUGREPORT'@'; then \
+ msgid_bugs_address='$(MSGID_BUGS_ADDRESS)'; \
+ else \
+ msgid_bugs_address='$(PACKAGE_BUGREPORT)'; \
+ fi; \
+ $(XGETTEXT) --default-domain=$(DOMAIN) --directory=$(top_srcdir) \
+ --add-comments=TRANSLATORS: $(XGETTEXT_OPTIONS) \
+ --files-from=$(srcdir)/POTFILES.in \
+ --copyright-holder='$(COPYRIGHT_HOLDER)' \
+ --msgid-bugs-address="$$msgid_bugs_address"
+ test ! -f $(DOMAIN).po || { \
+ if test -f $(srcdir)/$(DOMAIN).pot; then \
+ sed -f remove-potcdate.sed < $(srcdir)/$(DOMAIN).pot > $(DOMAIN).1po && \
+ sed -f remove-potcdate.sed < $(DOMAIN).po > $(DOMAIN).2po && \
+ if cmp $(DOMAIN).1po $(DOMAIN).2po >/dev/null 2>&1; then \
+ rm -f $(DOMAIN).1po $(DOMAIN).2po $(DOMAIN).po; \
+ else \
+ rm -f $(DOMAIN).1po $(DOMAIN).2po $(srcdir)/$(DOMAIN).pot && \
+ mv $(DOMAIN).po $(srcdir)/$(DOMAIN).pot; \
+ fi; \
+ else \
+ mv $(DOMAIN).po $(srcdir)/$(DOMAIN).pot; \
+ fi; \
+ }
+
+# This rule has no dependencies: we don't need to update $(DOMAIN).pot at
+# every "make" invocation, only create it when it is missing.
+# Only "make $(DOMAIN).pot-update" or "make dist" will force an update.
+$(srcdir)/$(DOMAIN).pot:
+ $(MAKE) $(DOMAIN).pot-update
+
+# This target rebuilds a PO file if $(DOMAIN).pot has changed.
+# Note that a PO file is not touched if it doesn't need to be changed.
+$(POFILES): $(srcdir)/$(DOMAIN).pot
+ @lang=`echo $@ | sed -e 's,.*/,,' -e 's/\.po$$//'`; \
+ if test -f "$(srcdir)/$${lang}.po"; then \
+ test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \
+ echo "$${cdcmd}$(MSGMERGE_UPDATE) $${lang}.po $(DOMAIN).pot"; \
+ cd $(srcdir) && $(MSGMERGE_UPDATE) $${lang}.po $(DOMAIN).pot; \
+ else \
+ $(MAKE) $${lang}.po-create; \
+ fi
+
+
+install: install-exec install-data
+install-exec:
+install-data: install-data-yes
+ if test "$(PACKAGE)" = "gettext-tools"; then \
+ $(mkinstalldirs) $(DESTDIR)$(gettextsrcdir); \
+ for file in $(DISTFILES.common) Makevars.template; do \
+ $(INSTALL_DATA) $(srcdir)/$$file \
+ $(DESTDIR)$(gettextsrcdir)/$$file; \
+ done; \
+ for file in Makevars; do \
+ rm -f $(DESTDIR)$(gettextsrcdir)/$$file; \
+ done; \
+ else \
+ : ; \
+ fi
+install-data-no: all
+install-data-yes: all
+ $(mkinstalldirs) $(DESTDIR)$(datadir)
+ @catalogs='$(CATALOGS)'; \
+ for cat in $$catalogs; do \
+ cat=`basename $$cat`; \
+ lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \
+ dir=$(localedir)/$$lang/LC_MESSAGES; \
+ $(mkinstalldirs) $(DESTDIR)$$dir; \
+ if test -r $$cat; then realcat=$$cat; else realcat=$(srcdir)/$$cat; fi; \
+ $(INSTALL_DATA) $$realcat $(DESTDIR)$$dir/$(DOMAIN).mo; \
+ echo "installing $$realcat as $(DESTDIR)$$dir/$(DOMAIN).mo"; \
+ for lc in '' $(EXTRA_LOCALE_CATEGORIES); do \
+ if test -n "$$lc"; then \
+ if (cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc 2>/dev/null) | grep ' -> ' >/dev/null; then \
+ link=`cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc | sed -e 's/^.* -> //'`; \
+ mv $(DESTDIR)$(localedir)/$$lang/$$lc $(DESTDIR)$(localedir)/$$lang/$$lc.old; \
+ mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \
+ (cd $(DESTDIR)$(localedir)/$$lang/$$lc.old && \
+ for file in *; do \
+ if test -f $$file; then \
+ ln -s ../$$link/$$file $(DESTDIR)$(localedir)/$$lang/$$lc/$$file; \
+ fi; \
+ done); \
+ rm -f $(DESTDIR)$(localedir)/$$lang/$$lc.old; \
+ else \
+ if test -d $(DESTDIR)$(localedir)/$$lang/$$lc; then \
+ :; \
+ else \
+ rm -f $(DESTDIR)$(localedir)/$$lang/$$lc; \
+ mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \
+ fi; \
+ fi; \
+ rm -f $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \
+ ln -s ../LC_MESSAGES/$(DOMAIN).mo $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo 2>/dev/null || \
+ ln $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(DOMAIN).mo $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo 2>/dev/null || \
+ cp -p $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(DOMAIN).mo $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \
+ echo "installing $$realcat link as $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo"; \
+ fi; \
+ done; \
+ done
+
+install-strip: install
+
+installdirs: installdirs-exec installdirs-data
+installdirs-exec:
+installdirs-data: installdirs-data-yes
+ if test "$(PACKAGE)" = "gettext-tools"; then \
+ $(mkinstalldirs) $(DESTDIR)$(gettextsrcdir); \
+ else \
+ : ; \
+ fi
+installdirs-data-no:
+installdirs-data-yes:
+ $(mkinstalldirs) $(DESTDIR)$(datadir)
+ @catalogs='$(CATALOGS)'; \
+ for cat in $$catalogs; do \
+ cat=`basename $$cat`; \
+ lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \
+ dir=$(localedir)/$$lang/LC_MESSAGES; \
+ $(mkinstalldirs) $(DESTDIR)$$dir; \
+ for lc in '' $(EXTRA_LOCALE_CATEGORIES); do \
+ if test -n "$$lc"; then \
+ if (cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc 2>/dev/null) | grep ' -> ' >/dev/null; then \
+ link=`cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc | sed -e 's/^.* -> //'`; \
+ mv $(DESTDIR)$(localedir)/$$lang/$$lc $(DESTDIR)$(localedir)/$$lang/$$lc.old; \
+ mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \
+ (cd $(DESTDIR)$(localedir)/$$lang/$$lc.old && \
+ for file in *; do \
+ if test -f $$file; then \
+ ln -s ../$$link/$$file $(DESTDIR)$(localedir)/$$lang/$$lc/$$file; \
+ fi; \
+ done); \
+ rm -f $(DESTDIR)$(localedir)/$$lang/$$lc.old; \
+ else \
+ if test -d $(DESTDIR)$(localedir)/$$lang/$$lc; then \
+ :; \
+ else \
+ rm -f $(DESTDIR)$(localedir)/$$lang/$$lc; \
+ mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \
+ fi; \
+ fi; \
+ fi; \
+ done; \
+ done
+
+# Define this as empty until I found a useful application.
+installcheck:
+
+uninstall: uninstall-exec uninstall-data
+uninstall-exec:
+uninstall-data: uninstall-data-yes
+ if test "$(PACKAGE)" = "gettext-tools"; then \
+ for file in $(DISTFILES.common) Makevars.template; do \
+ rm -f $(DESTDIR)$(gettextsrcdir)/$$file; \
+ done; \
+ else \
+ : ; \
+ fi
+uninstall-data-no:
+uninstall-data-yes:
+ catalogs='$(CATALOGS)'; \
+ for cat in $$catalogs; do \
+ cat=`basename $$cat`; \
+ lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \
+ for lc in LC_MESSAGES $(EXTRA_LOCALE_CATEGORIES); do \
+ rm -f $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \
+ done; \
+ done
+
+check: all
+
+info dvi ps pdf html tags TAGS ctags CTAGS ID:
+
+mostlyclean:
+ rm -f remove-potcdate.sed
+ rm -f stamp-poT
+ rm -f core core.* $(DOMAIN).po $(DOMAIN).1po $(DOMAIN).2po *.new.po
+ rm -fr *.o
+
+clean: mostlyclean
+
+distclean: clean
+ rm -f Makefile Makefile.in POTFILES *.mo
+
+maintainer-clean: distclean
+ @echo "This command is intended for maintainers to use;"
+ @echo "it deletes files that may require special tools to rebuild."
+ rm -f stamp-po $(GMOFILES)
+
+distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
+dist distdir:
+ $(MAKE) update-po
+ @$(MAKE) dist2
+# This is a separate target because 'update-po' must be executed before.
+dist2: stamp-po $(DISTFILES)
+ dists="$(DISTFILES)"; \
+ if test "$(PACKAGE)" = "gettext-tools"; then \
+ dists="$$dists Makevars.template"; \
+ fi; \
+ if test -f $(srcdir)/$(DOMAIN).pot; then \
+ dists="$$dists $(DOMAIN).pot stamp-po"; \
+ fi; \
+ if test -f $(srcdir)/ChangeLog; then \
+ dists="$$dists ChangeLog"; \
+ fi; \
+ for i in 0 1 2 3 4 5 6 7 8 9; do \
+ if test -f $(srcdir)/ChangeLog.$$i; then \
+ dists="$$dists ChangeLog.$$i"; \
+ fi; \
+ done; \
+ if test -f $(srcdir)/LINGUAS; then dists="$$dists LINGUAS"; fi; \
+ for file in $$dists; do \
+ if test -f $$file; then \
+ cp -p $$file $(distdir) || exit 1; \
+ else \
+ cp -p $(srcdir)/$$file $(distdir) || exit 1; \
+ fi; \
+ done
+
+update-po: Makefile
+ $(MAKE) $(DOMAIN).pot-update
+ test -z "$(UPDATEPOFILES)" || $(MAKE) $(UPDATEPOFILES)
+ $(MAKE) update-gmo
+
+# General rule for creating PO files.
+
+.nop.po-create:
+ @lang=`echo $@ | sed -e 's/\.po-create$$//'`; \
+ echo "File $$lang.po does not exist. If you are a translator, you can create it through 'msginit'." 1>&2; \
+ exit 1
+
+# General rule for updating PO files.
+
+.nop.po-update:
+ @lang=`echo $@ | sed -e 's/\.po-update$$//'`; \
+ if test "$(PACKAGE)" = "gettext-tools"; then PATH=`pwd`/../src:$$PATH; fi; \
+ tmpdir=`pwd`; \
+ echo "$$lang:"; \
+ test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \
+ echo "$${cdcmd}$(MSGMERGE) $$lang.po $(DOMAIN).pot -o $$lang.new.po"; \
+ cd $(srcdir); \
+ if $(MSGMERGE) $$lang.po $(DOMAIN).pot -o $$tmpdir/$$lang.new.po; then \
+ if cmp $$lang.po $$tmpdir/$$lang.new.po >/dev/null 2>&1; then \
+ rm -f $$tmpdir/$$lang.new.po; \
+ else \
+ if mv -f $$tmpdir/$$lang.new.po $$lang.po; then \
+ :; \
+ else \
+ echo "msgmerge for $$lang.po failed: cannot move $$tmpdir/$$lang.new.po to $$lang.po" 1>&2; \
+ exit 1; \
+ fi; \
+ fi; \
+ else \
+ echo "msgmerge for $$lang.po failed!" 1>&2; \
+ rm -f $$tmpdir/$$lang.new.po; \
+ fi
+
+$(DUMMYPOFILES):
+
+update-gmo: Makefile $(GMOFILES)
+ @:
+
+Makefile: Makefile.in.in Makevars $(top_builddir)/config.status @POMAKEFILEDEPS@
+ cd $(top_builddir) \
+ && CONFIG_FILES=$(subdir)/$@.in CONFIG_HEADERS= \
+ $(SHELL) ./config.status
+
+force:
+
+# Tell versions [3.59,3.63) of GNU make not to export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/po/Makefile.in.in b/po/Makefile.in.in
new file mode 100644
index 00000000..5022b8b1
--- /dev/null
+++ b/po/Makefile.in.in
@@ -0,0 +1,403 @@
+# Makefile for PO directory in any package using GNU gettext.
+# Copyright (C) 1995-1997, 2000-2006 by Ulrich Drepper <drepper@gnu.ai.mit.edu>
+#
+# This file can be copied and used freely without restrictions. It can
+# be used in projects which are not available under the GNU General Public
+# License but which still want to provide support for the GNU gettext
+# functionality.
+# Please note that the actual code of GNU gettext is covered by the GNU
+# General Public License and is *not* in the public domain.
+#
+# Origin: gettext-0.16
+
+PACKAGE = @PACKAGE@
+VERSION = @VERSION@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+
+SHELL = /bin/sh
+@SET_MAKE@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+datarootdir = @datarootdir@
+datadir = @datadir@
+localedir = @localedir@
+gettextsrcdir = $(datadir)/gettext/po
+
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+
+# We use $(mkdir_p).
+# In automake <= 1.9.x, $(mkdir_p) is defined either as "mkdir -p --" or as
+# "$(mkinstalldirs)" or as "$(install_sh) -d". For these automake versions,
+# @install_sh@ does not start with $(SHELL), so we add it.
+# In automake >= 1.10, @mkdir_p@ is derived from ${MKDIR_P}, which is defined
+# either as "/path/to/mkdir -p" or ".../install-sh -c -d". For these automake
+# versions, $(mkinstalldirs) and $(install_sh) are unused.
+mkinstalldirs = $(SHELL) @install_sh@ -d
+install_sh = $(SHELL) @install_sh@
+MKDIR_P = @MKDIR_P@
+mkdir_p = @mkdir_p@
+
+GMSGFMT_ = @GMSGFMT@
+GMSGFMT_no = @GMSGFMT@
+GMSGFMT_yes = @GMSGFMT_015@
+GMSGFMT = $(GMSGFMT_$(USE_MSGCTXT))
+MSGFMT_ = @MSGFMT@
+MSGFMT_no = @MSGFMT@
+MSGFMT_yes = @MSGFMT_015@
+MSGFMT = $(MSGFMT_$(USE_MSGCTXT))
+XGETTEXT_ = @XGETTEXT@
+XGETTEXT_no = @XGETTEXT@
+XGETTEXT_yes = @XGETTEXT_015@
+XGETTEXT = $(XGETTEXT_$(USE_MSGCTXT))
+MSGMERGE = msgmerge
+MSGMERGE_UPDATE = @MSGMERGE@ --update
+MSGINIT = msginit
+MSGCONV = msgconv
+MSGFILTER = msgfilter
+
+POFILES = @POFILES@
+GMOFILES = @GMOFILES@
+UPDATEPOFILES = @UPDATEPOFILES@
+DUMMYPOFILES = @DUMMYPOFILES@
+DISTFILES.common = Makefile.in.in remove-potcdate.sin \
+$(DISTFILES.common.extra1) $(DISTFILES.common.extra2) $(DISTFILES.common.extra3)
+DISTFILES = $(DISTFILES.common) Makevars POTFILES.in \
+$(POFILES) $(GMOFILES) \
+$(DISTFILES.extra1) $(DISTFILES.extra2) $(DISTFILES.extra3)
+
+POTFILES = \
+
+CATALOGS = @CATALOGS@
+
+# Makevars gets inserted here. (Don't remove this line!)
+
+.SUFFIXES:
+.SUFFIXES: .po .gmo .mo .sed .sin .nop .po-create .po-update
+
+.po.mo:
+ @echo "$(MSGFMT) -c -o $@ $<"; \
+ $(MSGFMT) -c -o t-$@ $< && mv t-$@ $@
+
+.po.gmo:
+ @lang=`echo $* | sed -e 's,.*/,,'`; \
+ test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \
+ echo "$${cdcmd}rm -f $${lang}.gmo && $(GMSGFMT) -c --statistics -o $${lang}.gmo $${lang}.po"; \
+ cd $(srcdir) && rm -f $${lang}.gmo && $(GMSGFMT) -c --statistics -o t-$${lang}.gmo $${lang}.po && mv t-$${lang}.gmo $${lang}.gmo
+
+.sin.sed:
+ sed -e '/^#/d' $< > t-$@
+ mv t-$@ $@
+
+
+all: all-@USE_NLS@
+
+all-yes: stamp-po
+all-no:
+
+# $(srcdir)/$(DOMAIN).pot is only created when needed. When xgettext finds no
+# internationalized messages, no $(srcdir)/$(DOMAIN).pot is created (because
+# we don't want to bother translators with empty POT files). We assume that
+# LINGUAS is empty in this case, i.e. $(POFILES) and $(GMOFILES) are empty.
+# In this case, stamp-po is a nop (i.e. a phony target).
+
+# stamp-po is a timestamp denoting the last time at which the CATALOGS have
+# been loosely updated. Its purpose is that when a developer or translator
+# checks out the package via CVS, and the $(DOMAIN).pot file is not in CVS,
+# "make" will update the $(DOMAIN).pot and the $(CATALOGS), but subsequent
+# invocations of "make" will do nothing. This timestamp would not be necessary
+# if updating the $(CATALOGS) would always touch them; however, the rule for
+# $(POFILES) has been designed to not touch files that don't need to be
+# changed.
+stamp-po: $(srcdir)/$(DOMAIN).pot
+ test ! -f $(srcdir)/$(DOMAIN).pot || \
+ test -z "$(GMOFILES)" || $(MAKE) $(GMOFILES)
+ @test ! -f $(srcdir)/$(DOMAIN).pot || { \
+ echo "touch stamp-po" && \
+ echo timestamp > stamp-poT && \
+ mv stamp-poT stamp-po; \
+ }
+
+# Note: Target 'all' must not depend on target '$(DOMAIN).pot-update',
+# otherwise packages like GCC can not be built if only parts of the source
+# have been downloaded.
+
+# This target rebuilds $(DOMAIN).pot; it is an expensive operation.
+# Note that $(DOMAIN).pot is not touched if it doesn't need to be changed.
+$(DOMAIN).pot-update: $(POTFILES) $(srcdir)/POTFILES.in remove-potcdate.sed
+ if test -n '$(MSGID_BUGS_ADDRESS)' || test '$(PACKAGE_BUGREPORT)' = '@'PACKAGE_BUGREPORT'@'; then \
+ msgid_bugs_address='$(MSGID_BUGS_ADDRESS)'; \
+ else \
+ msgid_bugs_address='$(PACKAGE_BUGREPORT)'; \
+ fi; \
+ $(XGETTEXT) --default-domain=$(DOMAIN) --directory=$(top_srcdir) \
+ --add-comments=TRANSLATORS: $(XGETTEXT_OPTIONS) \
+ --files-from=$(srcdir)/POTFILES.in \
+ --copyright-holder='$(COPYRIGHT_HOLDER)' \
+ --msgid-bugs-address="$$msgid_bugs_address"
+ test ! -f $(DOMAIN).po || { \
+ if test -f $(srcdir)/$(DOMAIN).pot; then \
+ sed -f remove-potcdate.sed < $(srcdir)/$(DOMAIN).pot > $(DOMAIN).1po && \
+ sed -f remove-potcdate.sed < $(DOMAIN).po > $(DOMAIN).2po && \
+ if cmp $(DOMAIN).1po $(DOMAIN).2po >/dev/null 2>&1; then \
+ rm -f $(DOMAIN).1po $(DOMAIN).2po $(DOMAIN).po; \
+ else \
+ rm -f $(DOMAIN).1po $(DOMAIN).2po $(srcdir)/$(DOMAIN).pot && \
+ mv $(DOMAIN).po $(srcdir)/$(DOMAIN).pot; \
+ fi; \
+ else \
+ mv $(DOMAIN).po $(srcdir)/$(DOMAIN).pot; \
+ fi; \
+ }
+
+# This rule has no dependencies: we don't need to update $(DOMAIN).pot at
+# every "make" invocation, only create it when it is missing.
+# Only "make $(DOMAIN).pot-update" or "make dist" will force an update.
+$(srcdir)/$(DOMAIN).pot:
+ $(MAKE) $(DOMAIN).pot-update
+
+# This target rebuilds a PO file if $(DOMAIN).pot has changed.
+# Note that a PO file is not touched if it doesn't need to be changed.
+$(POFILES): $(srcdir)/$(DOMAIN).pot
+ @lang=`echo $@ | sed -e 's,.*/,,' -e 's/\.po$$//'`; \
+ if test -f "$(srcdir)/$${lang}.po"; then \
+ test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \
+ echo "$${cdcmd}$(MSGMERGE_UPDATE) $${lang}.po $(DOMAIN).pot"; \
+ cd $(srcdir) && $(MSGMERGE_UPDATE) $${lang}.po $(DOMAIN).pot; \
+ else \
+ $(MAKE) $${lang}.po-create; \
+ fi
+
+
+install: install-exec install-data
+install-exec:
+install-data: install-data-@USE_NLS@
+ if test "$(PACKAGE)" = "gettext-tools"; then \
+ $(mkdir_p) $(DESTDIR)$(gettextsrcdir); \
+ for file in $(DISTFILES.common) Makevars.template; do \
+ $(INSTALL_DATA) $(srcdir)/$$file \
+ $(DESTDIR)$(gettextsrcdir)/$$file; \
+ done; \
+ for file in Makevars; do \
+ rm -f $(DESTDIR)$(gettextsrcdir)/$$file; \
+ done; \
+ else \
+ : ; \
+ fi
+install-data-no: all
+install-data-yes: all
+ $(mkdir_p) $(DESTDIR)$(datadir)
+ @catalogs='$(CATALOGS)'; \
+ for cat in $$catalogs; do \
+ cat=`basename $$cat`; \
+ lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \
+ dir=$(localedir)/$$lang/LC_MESSAGES; \
+ $(mkdir_p) $(DESTDIR)$$dir; \
+ if test -r $$cat; then realcat=$$cat; else realcat=$(srcdir)/$$cat; fi; \
+ $(INSTALL_DATA) $$realcat $(DESTDIR)$$dir/$(DOMAIN).mo; \
+ echo "installing $$realcat as $(DESTDIR)$$dir/$(DOMAIN).mo"; \
+ for lc in '' $(EXTRA_LOCALE_CATEGORIES); do \
+ if test -n "$$lc"; then \
+ if (cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc 2>/dev/null) | grep ' -> ' >/dev/null; then \
+ link=`cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc | sed -e 's/^.* -> //'`; \
+ mv $(DESTDIR)$(localedir)/$$lang/$$lc $(DESTDIR)$(localedir)/$$lang/$$lc.old; \
+ mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \
+ (cd $(DESTDIR)$(localedir)/$$lang/$$lc.old && \
+ for file in *; do \
+ if test -f $$file; then \
+ ln -s ../$$link/$$file $(DESTDIR)$(localedir)/$$lang/$$lc/$$file; \
+ fi; \
+ done); \
+ rm -f $(DESTDIR)$(localedir)/$$lang/$$lc.old; \
+ else \
+ if test -d $(DESTDIR)$(localedir)/$$lang/$$lc; then \
+ :; \
+ else \
+ rm -f $(DESTDIR)$(localedir)/$$lang/$$lc; \
+ mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \
+ fi; \
+ fi; \
+ rm -f $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \
+ ln -s ../LC_MESSAGES/$(DOMAIN).mo $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo 2>/dev/null || \
+ ln $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(DOMAIN).mo $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo 2>/dev/null || \
+ cp -p $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(DOMAIN).mo $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \
+ echo "installing $$realcat link as $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo"; \
+ fi; \
+ done; \
+ done
+
+install-strip: install
+
+installdirs: installdirs-exec installdirs-data
+installdirs-exec:
+installdirs-data: installdirs-data-@USE_NLS@
+ if test "$(PACKAGE)" = "gettext-tools"; then \
+ $(mkdir_p) $(DESTDIR)$(gettextsrcdir); \
+ else \
+ : ; \
+ fi
+installdirs-data-no:
+installdirs-data-yes:
+ $(mkdir_p) $(DESTDIR)$(datadir)
+ @catalogs='$(CATALOGS)'; \
+ for cat in $$catalogs; do \
+ cat=`basename $$cat`; \
+ lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \
+ dir=$(localedir)/$$lang/LC_MESSAGES; \
+ $(mkdir_p) $(DESTDIR)$$dir; \
+ for lc in '' $(EXTRA_LOCALE_CATEGORIES); do \
+ if test -n "$$lc"; then \
+ if (cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc 2>/dev/null) | grep ' -> ' >/dev/null; then \
+ link=`cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc | sed -e 's/^.* -> //'`; \
+ mv $(DESTDIR)$(localedir)/$$lang/$$lc $(DESTDIR)$(localedir)/$$lang/$$lc.old; \
+ mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \
+ (cd $(DESTDIR)$(localedir)/$$lang/$$lc.old && \
+ for file in *; do \
+ if test -f $$file; then \
+ ln -s ../$$link/$$file $(DESTDIR)$(localedir)/$$lang/$$lc/$$file; \
+ fi; \
+ done); \
+ rm -f $(DESTDIR)$(localedir)/$$lang/$$lc.old; \
+ else \
+ if test -d $(DESTDIR)$(localedir)/$$lang/$$lc; then \
+ :; \
+ else \
+ rm -f $(DESTDIR)$(localedir)/$$lang/$$lc; \
+ mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \
+ fi; \
+ fi; \
+ fi; \
+ done; \
+ done
+
+# Define this as empty until I found a useful application.
+installcheck:
+
+uninstall: uninstall-exec uninstall-data
+uninstall-exec:
+uninstall-data: uninstall-data-@USE_NLS@
+ if test "$(PACKAGE)" = "gettext-tools"; then \
+ for file in $(DISTFILES.common) Makevars.template; do \
+ rm -f $(DESTDIR)$(gettextsrcdir)/$$file; \
+ done; \
+ else \
+ : ; \
+ fi
+uninstall-data-no:
+uninstall-data-yes:
+ catalogs='$(CATALOGS)'; \
+ for cat in $$catalogs; do \
+ cat=`basename $$cat`; \
+ lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \
+ for lc in LC_MESSAGES $(EXTRA_LOCALE_CATEGORIES); do \
+ rm -f $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \
+ done; \
+ done
+
+check: all
+
+info dvi ps pdf html tags TAGS ctags CTAGS ID:
+
+mostlyclean:
+ rm -f remove-potcdate.sed
+ rm -f stamp-poT
+ rm -f core core.* $(DOMAIN).po $(DOMAIN).1po $(DOMAIN).2po *.new.po
+ rm -fr *.o
+
+clean: mostlyclean
+
+distclean: clean
+ rm -f Makefile Makefile.in POTFILES *.mo
+
+maintainer-clean: distclean
+ @echo "This command is intended for maintainers to use;"
+ @echo "it deletes files that may require special tools to rebuild."
+ rm -f stamp-po $(GMOFILES)
+
+distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
+dist distdir:
+ $(MAKE) update-po
+ @$(MAKE) dist2
+# This is a separate target because 'update-po' must be executed before.
+dist2: stamp-po $(DISTFILES)
+ dists="$(DISTFILES)"; \
+ if test "$(PACKAGE)" = "gettext-tools"; then \
+ dists="$$dists Makevars.template"; \
+ fi; \
+ if test -f $(srcdir)/$(DOMAIN).pot; then \
+ dists="$$dists $(DOMAIN).pot stamp-po"; \
+ fi; \
+ if test -f $(srcdir)/ChangeLog; then \
+ dists="$$dists ChangeLog"; \
+ fi; \
+ for i in 0 1 2 3 4 5 6 7 8 9; do \
+ if test -f $(srcdir)/ChangeLog.$$i; then \
+ dists="$$dists ChangeLog.$$i"; \
+ fi; \
+ done; \
+ if test -f $(srcdir)/LINGUAS; then dists="$$dists LINGUAS"; fi; \
+ for file in $$dists; do \
+ if test -f $$file; then \
+ cp -p $$file $(distdir) || exit 1; \
+ else \
+ cp -p $(srcdir)/$$file $(distdir) || exit 1; \
+ fi; \
+ done
+
+update-po: Makefile
+ $(MAKE) $(DOMAIN).pot-update
+ test -z "$(UPDATEPOFILES)" || $(MAKE) $(UPDATEPOFILES)
+ $(MAKE) update-gmo
+
+# General rule for creating PO files.
+
+.nop.po-create:
+ @lang=`echo $@ | sed -e 's/\.po-create$$//'`; \
+ echo "File $$lang.po does not exist. If you are a translator, you can create it through 'msginit'." 1>&2; \
+ exit 1
+
+# General rule for updating PO files.
+
+.nop.po-update:
+ @lang=`echo $@ | sed -e 's/\.po-update$$//'`; \
+ if test "$(PACKAGE)" = "gettext-tools"; then PATH=`pwd`/../src:$$PATH; fi; \
+ tmpdir=`pwd`; \
+ echo "$$lang:"; \
+ test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \
+ echo "$${cdcmd}$(MSGMERGE) $$lang.po $(DOMAIN).pot -o $$lang.new.po"; \
+ cd $(srcdir); \
+ if $(MSGMERGE) $$lang.po $(DOMAIN).pot -o $$tmpdir/$$lang.new.po; then \
+ if cmp $$lang.po $$tmpdir/$$lang.new.po >/dev/null 2>&1; then \
+ rm -f $$tmpdir/$$lang.new.po; \
+ else \
+ if mv -f $$tmpdir/$$lang.new.po $$lang.po; then \
+ :; \
+ else \
+ echo "msgmerge for $$lang.po failed: cannot move $$tmpdir/$$lang.new.po to $$lang.po" 1>&2; \
+ exit 1; \
+ fi; \
+ fi; \
+ else \
+ echo "msgmerge for $$lang.po failed!" 1>&2; \
+ rm -f $$tmpdir/$$lang.new.po; \
+ fi
+
+$(DUMMYPOFILES):
+
+update-gmo: Makefile $(GMOFILES)
+ @:
+
+Makefile: Makefile.in.in Makevars $(top_builddir)/config.status @POMAKEFILEDEPS@
+ cd $(top_builddir) \
+ && $(SHELL) ./config.status $(subdir)/$@.in po-directories
+
+force:
+
+# Tell versions [3.59,3.63) of GNU make not to export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/po/Makevars b/po/Makevars
new file mode 100644
index 00000000..24318814
--- /dev/null
+++ b/po/Makevars
@@ -0,0 +1,41 @@
+# Makefile variables for PO directory in any package using GNU gettext.
+
+# Usually the message domain is the same as the package name.
+DOMAIN = $(PACKAGE)
+
+# These two variables depend on the location of this directory.
+subdir = po
+top_builddir = ..
+
+# These options get passed to xgettext.
+XGETTEXT_OPTIONS = --keyword=_ --keyword=N_
+
+# This is the copyright holder that gets inserted into the header of the
+# $(DOMAIN).pot file. Set this to the copyright holder of the surrounding
+# package. (Note that the msgstr strings, extracted from the package's
+# sources, belong to the copyright holder of the package.) Translators are
+# expected to transfer the copyright for their translations to this person
+# or entity, or to disclaim their copyright. The empty string stands for
+# the public domain; in this case the translators are expected to disclaim
+# their copyright.
+COPYRIGHT_HOLDER = University of Washington
+
+# This is the email address or URL to which the translators shall report
+# bugs in the untranslated strings:
+# - Strings which are not entire sentences, see the maintainer guidelines
+# in the GNU gettext documentation, section 'Preparing Strings'.
+# - Strings which use unclear terms or require additional context to be
+# understood.
+# - Strings which make invalid assumptions about notation of date, time or
+# money.
+# - Pluralisation problems.
+# - Incorrect English spelling.
+# - Incorrect formatting.
+# It can be your email address, or a mailing list address where translators
+# can write to without being subscribed, or the URL of a web page through
+# which the translators can contact you.
+MSGID_BUGS_ADDRESS = alpine-contact@u.washington.edu
+
+# This is the list of locale categories, beyond LC_MESSAGES, for which the
+# message catalogs shall be used. It is usually empty.
+EXTRA_LOCALE_CATEGORIES =
diff --git a/po/Makevars.template b/po/Makevars.template
new file mode 100644
index 00000000..32692ab4
--- /dev/null
+++ b/po/Makevars.template
@@ -0,0 +1,41 @@
+# Makefile variables for PO directory in any package using GNU gettext.
+
+# Usually the message domain is the same as the package name.
+DOMAIN = $(PACKAGE)
+
+# These two variables depend on the location of this directory.
+subdir = po
+top_builddir = ..
+
+# These options get passed to xgettext.
+XGETTEXT_OPTIONS = --keyword=_ --keyword=N_
+
+# This is the copyright holder that gets inserted into the header of the
+# $(DOMAIN).pot file. Set this to the copyright holder of the surrounding
+# package. (Note that the msgstr strings, extracted from the package's
+# sources, belong to the copyright holder of the package.) Translators are
+# expected to transfer the copyright for their translations to this person
+# or entity, or to disclaim their copyright. The empty string stands for
+# the public domain; in this case the translators are expected to disclaim
+# their copyright.
+COPYRIGHT_HOLDER = Free Software Foundation, Inc.
+
+# This is the email address or URL to which the translators shall report
+# bugs in the untranslated strings:
+# - Strings which are not entire sentences, see the maintainer guidelines
+# in the GNU gettext documentation, section 'Preparing Strings'.
+# - Strings which use unclear terms or require additional context to be
+# understood.
+# - Strings which make invalid assumptions about notation of date, time or
+# money.
+# - Pluralisation problems.
+# - Incorrect English spelling.
+# - Incorrect formatting.
+# It can be your email address, or a mailing list address where translators
+# can write to without being subscribed, or the URL of a web page through
+# which the translators can contact you.
+MSGID_BUGS_ADDRESS =
+
+# This is the list of locale categories, beyond LC_MESSAGES, for which the
+# message catalogs shall be used. It is usually empty.
+EXTRA_LOCALE_CATEGORIES =
diff --git a/po/POTFILES.in b/po/POTFILES.in
new file mode 100644
index 00000000..c549d00e
--- /dev/null
+++ b/po/POTFILES.in
@@ -0,0 +1,191 @@
+# List of source files which contain translatable strings.
+alpine/osdep/termout.wnt.c
+alpine/osdep/termin.wnt.c
+alpine/osdep/termin.gen.c
+alpine/osdep/termout.gen.c
+alpine/osdep/termin.unx.c
+alpine/osdep/termout.unx.c
+alpine/osdep/fltrname.c
+alpine/osdep/diskquot.non.c
+alpine/osdep/execview.c
+alpine/osdep/chnge_pw.c
+alpine/osdep/mswinver.c
+alpine/osdep/jobcntrl.c
+alpine/osdep/debuging.c
+alpine/osdep/print.c
+alpine/init.c
+alpine/rpdump.c
+alpine/arg.c
+alpine/mailview.c
+alpine/keymenu.c
+alpine/dispfilt.c
+alpine/takeaddr.c
+alpine/ldapconf.c
+alpine/send.c
+alpine/radio.c
+alpine/mailcmd.c
+alpine/titlebar.c
+alpine/adrbkcmd.c
+alpine/alpine.c
+alpine/folder.c
+alpine/busy.c
+alpine/remote.c
+alpine/pipe.c
+alpine/setup.c
+alpine/after.c
+alpine/flagmaint.c
+alpine/confscroll.c
+alpine/mailindx.c
+alpine/kblock.c
+alpine/roleconf.c
+alpine/listsel.c
+alpine/status.c
+alpine/addrbook.c
+alpine/reply.c
+alpine/mailpart.c
+alpine/pattern.c
+alpine/signal.c
+alpine/rpload.c
+alpine/colorconf.c
+alpine/context.c
+alpine/pine-use.c
+alpine/imap.c
+alpine/newuser.c
+alpine/help.c
+alpine/print.c
+alpine/smime.c
+pico/osdep/mswin_tw.c
+pico/osdep/msdlg.c
+pico/osdep/read.c
+pico/osdep/chkpoint.c
+pico/osdep/fsync.c
+pico/osdep/newmail.c
+pico/osdep/getkey.c
+pico/osdep/mswin_spell.c
+pico/osdep/signals.c
+pico/osdep/shell.c
+pico/osdep/terminal.c
+pico/osdep/color.c
+pico/osdep/filesys.c
+pico/osdep/spell.c
+pico/osdep/tty.c
+pico/osdep/popen.c
+pico/osdep/truncate.c
+pico/osdep/altedit.c
+pico/osdep/mouse.c
+pico/osdep/raw.c
+pico/osdep/mswin_aspell.c
+pico/osdep/mswin.c
+pico/attach.c
+pico/display.c
+pico/basic.c
+pico/region.c
+pico/pico.c
+pico/blddate.c
+pico/browse.c
+pico/search.c
+pico/fileio.c
+pico/window.c
+pico/buffer.c
+pico/mswinver.c
+pico/word.c
+pico/bind.c
+pico/msmem.c
+pico/file.c
+pico/random.c
+pico/main.c
+pico/utf8stub.c
+pico/pilot.c
+pico/line.c
+pico/composer.c
+pith/charconv/filesys.c
+pith/charconv/utf8.c
+pith/osdep/temp_nam.c
+pith/osdep/fnexpand.c
+pith/osdep/domnames.c
+pith/osdep/tempfile.c
+pith/osdep/fgetpos.c
+pith/osdep/mimedisp.c
+pith/osdep/filesize.c
+pith/osdep/collate.c
+pith/osdep/err_desc.c
+pith/osdep/canonicl.c
+pith/osdep/pw_stuff.c
+pith/osdep/lstcmpnt.c
+pith/osdep/debugtime.c
+pith/osdep/rename.c
+pith/osdep/hostname.c
+pith/osdep/bldpath.c
+pith/osdep/color.c
+pith/osdep/coredump.c
+pith/osdep/canaccess.c
+pith/osdep/writ_dir.c
+pith/osdep/creatdir.c
+pith/osdep/pipe.c
+pith/mailview.c
+pith/init.c
+pith/takeaddr.c
+pith/keyword.c
+pith/text.c
+pith/conf.c
+pith/detach.c
+pith/mailcap.c
+pith/sequence.c
+pith/list.c
+pith/folder.c
+pith/icache.c
+pith/color.c
+pith/mimedesc.c
+pith/sort.c
+pith/bldaddr.c
+pith/mailindx.c
+pith/string.c
+pith/helpindx.c
+pith/ablookup.c
+pith/adjtime.c
+pith/maillist.c
+pith/escapes.c
+pith/copyaddr.c
+pith/thread.c
+pith/pattern.c
+pith/state.c
+pith/rfc2231.c
+pith/imap.c
+pith/help_h_gen.c
+pith/abdlc.c
+pith/tempfile.c
+pith/margin.c
+pith/util.c
+pith/msgno.c
+pith/send.c
+pith/charset.c
+pith/mailcmd.c
+pith/hist.c
+pith/adrbklib.c
+pith/readfile.c
+pith/news.c
+pith/editorial.c
+pith/help_c_gen.c
+pith/filter.c
+pith/mimetype.c
+pith/remote.c
+pith/store.c
+pith/pipe.c
+pith/addrstring.c
+pith/flag.c
+pith/ldap.c
+pith/status.c
+pith/addrbook.c
+pith/detoken.c
+pith/reply.c
+pith/stream.c
+pith/newmail.c
+pith/save.c
+pith/url.c
+pith/search.c
+pith/context.c
+pith/help.c
+pith/handle.c
+pith/strlst.c
+pith/smime.c
+pith/smkeys.c
diff --git a/po/Rules-quot b/po/Rules-quot
new file mode 100644
index 00000000..9c2a995e
--- /dev/null
+++ b/po/Rules-quot
@@ -0,0 +1,47 @@
+# Special Makefile rules for English message catalogs with quotation marks.
+
+DISTFILES.common.extra1 = quot.sed boldquot.sed en@quot.header en@boldquot.header insert-header.sin Rules-quot
+
+.SUFFIXES: .insert-header .po-update-en
+
+en@quot.po-create:
+ $(MAKE) en@quot.po-update
+en@boldquot.po-create:
+ $(MAKE) en@boldquot.po-update
+
+en@quot.po-update: en@quot.po-update-en
+en@boldquot.po-update: en@boldquot.po-update-en
+
+.insert-header.po-update-en:
+ @lang=`echo $@ | sed -e 's/\.po-update-en$$//'`; \
+ if test "$(PACKAGE)" = "gettext"; then PATH=`pwd`/../src:$$PATH; GETTEXTLIBDIR=`cd $(top_srcdir)/src && pwd`; export GETTEXTLIBDIR; fi; \
+ tmpdir=`pwd`; \
+ echo "$$lang:"; \
+ ll=`echo $$lang | sed -e 's/@.*//'`; \
+ LC_ALL=C; export LC_ALL; \
+ cd $(srcdir); \
+ if $(MSGINIT) -i $(DOMAIN).pot --no-translator -l $$ll -o - 2>/dev/null | sed -f $$tmpdir/$$lang.insert-header | $(MSGCONV) -t UTF-8 | $(MSGFILTER) sed -f `echo $$lang | sed -e 's/.*@//'`.sed 2>/dev/null > $$tmpdir/$$lang.new.po; then \
+ if cmp $$lang.po $$tmpdir/$$lang.new.po >/dev/null 2>&1; then \
+ rm -f $$tmpdir/$$lang.new.po; \
+ else \
+ if mv -f $$tmpdir/$$lang.new.po $$lang.po; then \
+ :; \
+ else \
+ echo "creation of $$lang.po failed: cannot move $$tmpdir/$$lang.new.po to $$lang.po" 1>&2; \
+ exit 1; \
+ fi; \
+ fi; \
+ else \
+ echo "creation of $$lang.po failed!" 1>&2; \
+ rm -f $$tmpdir/$$lang.new.po; \
+ fi
+
+en@quot.insert-header: insert-header.sin
+ sed -e '/^#/d' -e 's/HEADER/en@quot.header/g' $(srcdir)/insert-header.sin > en@quot.insert-header
+
+en@boldquot.insert-header: insert-header.sin
+ sed -e '/^#/d' -e 's/HEADER/en@boldquot.header/g' $(srcdir)/insert-header.sin > en@boldquot.insert-header
+
+mostlyclean: mostlyclean-quot
+mostlyclean-quot:
+ rm -f *.insert-header
diff --git a/po/alpine.pot b/po/alpine.pot
new file mode 100644
index 00000000..1e17ca9a
--- /dev/null
+++ b/po/alpine.pot
@@ -0,0 +1,12549 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR University of Washington
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: alpine-contact@u.washington.edu\n"
+"POT-Creation-Date: 2008-06-03 12:14-0700\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#. TRANSLATORS: these are command labels for printing screen
+#. TRANSLATORS: command names, editURL means user gets to edit a URL if they
+#. want, editApp is edit application where they edit the application used to
+#. view a URL
+#: alpine/osdep/print.c:108 alpine/mailview.c:724 alpine/send.c:1288
+#: alpine/send.c:4511 alpine/radio.c:49 alpine/mailcmd.c:804
+#: alpine/mailcmd.c:977 alpine/mailcmd.c:1090 alpine/reply.c:845
+#: alpine/reply.c:977 pico/display.c:1357 pico/display.c:1386
+#: pico/display.c:1403
+msgid "Yes"
+msgstr ""
+
+#: alpine/osdep/print.c:109 alpine/mailview.c:725 alpine/keymenu.c:555
+#: alpine/keymenu.c:570 alpine/takeaddr.c:580 alpine/takeaddr.c:602
+#: alpine/send.c:1293 alpine/send.c:4516 alpine/send.c:5156 alpine/radio.c:50
+#: alpine/mailcmd.c:805 alpine/mailcmd.c:978 alpine/mailcmd.c:1091
+#: alpine/titlebar.c:535 alpine/reply.c:978 pico/display.c:1361
+#: pico/display.c:1386 pico/display.c:1410
+msgid "No"
+msgstr ""
+
+#. TRANSLATORS: go to Previous Printer in list
+#: alpine/osdep/print.c:111
+msgid "Prev Printer"
+msgstr ""
+
+#: alpine/osdep/print.c:112
+msgid "Next Printer"
+msgstr ""
+
+#. TRANSLATORS: use Custom Print command
+#: alpine/osdep/print.c:115
+msgid "CustomPrint"
+msgstr ""
+
+#. TRANSLATORS: Print something1 using something2.
+#. For example, Print configuration using printer three.
+#: alpine/osdep/print.c:199
+#, c-format
+msgid "Print %s using \"%s\" ? "
+msgstr ""
+
+#. TRANSLATORS: This is a list of errors printed when something goes wrong
+#. early on with the argument list. Be careful not to change literal
+#. option names mentioned in the strings.
+#: alpine/arg.c:53
+msgid "missing argument for option \"-pinerc\" (use - for standard out)"
+msgstr ""
+
+#: alpine/arg.c:55
+msgid "missing argument for option \"-aux\""
+msgstr ""
+
+#: alpine/arg.c:58
+msgid "missing argument for option \"-passfile\""
+msgstr ""
+
+#: alpine/arg.c:59
+msgid "argument to \"-passfile\" should be fully-qualified"
+msgstr ""
+
+#: alpine/arg.c:61
+msgid "missing argument for option \"-sort\""
+msgstr ""
+
+#: alpine/arg.c:62
+#, c-format
+msgid "missing argument for flag \"%c\""
+msgstr ""
+
+#: alpine/arg.c:63
+#, c-format
+msgid "Non numeric argument for flag \"%c\""
+msgstr ""
+
+#: alpine/arg.c:64
+#, c-format
+msgid "Non numeric argument for \"%s\""
+msgstr ""
+
+#: alpine/arg.c:65
+msgid "missing URL for \"-url\""
+msgstr ""
+
+#: alpine/arg.c:66
+#, c-format
+msgid "missing attachment for \"%s\""
+msgstr ""
+
+#: alpine/arg.c:67
+#, c-format
+msgid "conflicting action: \"%s\""
+msgstr ""
+
+#. TRANSLATORS: An error message about an unknown flag (option)
+#. on the command line
+#: alpine/arg.c:68 pico/main.c:84 pico/pilot.c:51
+#, c-format
+msgid "unknown flag \"%c\""
+msgstr ""
+
+#: alpine/arg.c:69
+#, c-format
+msgid "-I argument \"%s\": %s"
+msgstr ""
+
+#: alpine/arg.c:70
+#, c-format
+msgid "-d argument \"%s\": %s"
+msgstr ""
+
+#: alpine/arg.c:72
+msgid ""
+"missing argument for option \"-copy_pinerc\"\n"
+"Usage: pine -copy_pinerc <local_pinerc> <remote_pinerc>"
+msgstr ""
+
+#: alpine/arg.c:73
+msgid ""
+"missing argument for option \"-copy_abook\"\n"
+"Usage: pine -copy_abook <local_abook> <remote_abook>"
+msgstr ""
+
+#: alpine/arg.c:77
+msgid "Possible Starting Arguments for Alpine program:"
+msgstr ""
+
+#: alpine/arg.c:79
+msgid " Argument\tMeaning"
+msgstr ""
+
+#: alpine/arg.c:80
+msgid " <addrs>...\tGo directly into composer sending to given address"
+msgstr ""
+
+#: alpine/arg.c:81
+msgid "\t\tList multiple addresses with a single space between them."
+msgstr ""
+
+#: alpine/arg.c:82
+msgid "\t\tStandard input redirection is allowed with addresses."
+msgstr ""
+
+#: alpine/arg.c:83
+msgid "\t\tNote: Places addresses in the \"To\" field only."
+msgstr ""
+
+#: alpine/arg.c:84
+msgid " -attach <file>\tGo directly into composer with given file"
+msgstr ""
+
+#: alpine/arg.c:85
+msgid " -attachlist <file-list>"
+msgstr ""
+
+#: alpine/arg.c:86
+msgid " -attach_and_delete <file>"
+msgstr ""
+
+#: alpine/arg.c:87
+msgid "\t\tGo to composer, attach file, delete when finished"
+msgstr ""
+
+#: alpine/arg.c:88
+msgid "\t\tNote: Attach options can't be used if -f, -F"
+msgstr ""
+
+#: alpine/arg.c:89
+msgid "\t\tadded to Attachment list. Attachlist must be the last"
+msgstr ""
+
+#: alpine/arg.c:90
+msgid "\t\toption on the command line"
+msgstr ""
+
+#: alpine/arg.c:91
+msgid " -bail\t\tExit if pinerc file doesn't already exist"
+msgstr ""
+
+#: alpine/arg.c:93
+msgid " -d n\t\tDebug - set debug level to 'n', or use the following:"
+msgstr ""
+
+#: alpine/arg.c:94
+msgid ""
+" -d keywords...\tflush,timestamp,imap=0..4,tcp,numfiles=0..31,verbose=0..9"
+msgstr ""
+
+#: alpine/arg.c:96
+msgid " -f <folder>\tFolder - give folder name to open"
+msgstr ""
+
+#: alpine/arg.c:97
+msgid " -c <number>\tContext - which context to apply to -f arg"
+msgstr ""
+
+#: alpine/arg.c:98
+msgid " -F <file>\tFile - give file name to open and page through and"
+msgstr ""
+
+#: alpine/arg.c:99
+msgid "\t\tforward as email."
+msgstr ""
+
+#: alpine/arg.c:100
+msgid " -h \t\tHelp - give this list of options"
+msgstr ""
+
+#: alpine/arg.c:101
+msgid " -k \t\tKeys - Force use of function keys"
+msgstr ""
+
+#: alpine/arg.c:102
+msgid " -z \t\tSuspend - allow use of ^Z suspension"
+msgstr ""
+
+#: alpine/arg.c:103
+msgid " -r \t\tRestricted - can only send mail to oneself"
+msgstr ""
+
+#: alpine/arg.c:104
+msgid " -sort <sort>\tSort - Specify sort order of folder:"
+msgstr ""
+
+#: alpine/arg.c:105
+msgid "\t\t\tarrival, subject, threaded, orderedsubject, date,"
+msgstr ""
+
+#: alpine/arg.c:106
+msgid "\t\t\tfrom, size, score, to, cc, /reverse"
+msgstr ""
+
+#: alpine/arg.c:107
+msgid " -i\t\tIndex - Go directly to index, bypassing main menu"
+msgstr ""
+
+#: alpine/arg.c:108
+msgid " -I <keystroke_list> Initial keystrokes to be executed"
+msgstr ""
+
+#: alpine/arg.c:109
+msgid " -n <number>\tEntry in index to begin on"
+msgstr ""
+
+#: alpine/arg.c:110
+msgid " -o \t\tReadOnly - Open first folder read-only"
+msgstr ""
+
+#: alpine/arg.c:111
+msgid " -conf\t\tConfiguration - Print out fresh global configuration. The"
+msgstr ""
+
+#: alpine/arg.c:112
+msgid "\t\tvalues of your global configuration affect all Alpine users"
+msgstr ""
+
+#: alpine/arg.c:113
+msgid "\t\ton your system unless they have overridden the values in their"
+msgstr ""
+
+#: alpine/arg.c:114
+msgid "\t\tpinerc files."
+msgstr ""
+
+#: alpine/arg.c:115
+msgid ""
+" -pinerc <file>\tConfiguration - Put fresh pinerc configuration in <file>"
+msgstr ""
+
+#: alpine/arg.c:116
+msgid " -p <pinerc>\tUse alternate .pinerc file"
+msgstr ""
+
+#: alpine/arg.c:118
+msgid " -P <pine.conf>\tUse alternate pine.conf file"
+msgstr ""
+
+#: alpine/arg.c:120
+msgid " -aux <aux_files_dir>\tUse this with remote pinerc"
+msgstr ""
+
+#: alpine/arg.c:121
+msgid " -P <pine.conf>\tUse pine.conf file for default settings"
+msgstr ""
+
+#: alpine/arg.c:122
+msgid " -nosplash \tDisable the PC-Alpine splash screen"
+msgstr ""
+
+#: alpine/arg.c:126
+msgid " -erase_stored_passwords\tEliminate any stored passwords"
+msgstr ""
+
+#: alpine/arg.c:130
+msgid ""
+" -passfile <fully_qualified_filename>\tSet the password file to something "
+"other"
+msgstr ""
+
+#: alpine/arg.c:131
+msgid "\t\tthan the default"
+msgstr ""
+
+#: alpine/arg.c:135
+msgid ""
+" -nowrite_password_cache\tRead from a password cache if there is one, but"
+msgstr ""
+
+#: alpine/arg.c:136
+msgid "\t\t\t\tnever offer to write a password to the cache"
+msgstr ""
+
+#: alpine/arg.c:139
+msgid " -x <config>\tUse configuration exceptions in <config>."
+msgstr ""
+
+#: alpine/arg.c:140
+msgid "\t\tExceptions are used to override your default pinerc"
+msgstr ""
+
+#: alpine/arg.c:141
+msgid "\t\tsettings for a particular platform, can be a local file or"
+msgstr ""
+
+#: alpine/arg.c:142
+msgid "\t\ta remote folder."
+msgstr ""
+
+#: alpine/arg.c:143
+msgid " -v \t\tVersion - show version information"
+msgstr ""
+
+#: alpine/arg.c:144
+msgid " -version\tVersion - show version information"
+msgstr ""
+
+#: alpine/arg.c:145
+msgid " -supported\tList supported options"
+msgstr ""
+
+#: alpine/arg.c:146
+msgid " -url <url>\tOpen the given URL"
+msgstr ""
+
+#: alpine/arg.c:147
+msgid "\t\tNote: Can't be used if -f, -F"
+msgstr ""
+
+#: alpine/arg.c:148
+msgid "\t\tStandard input redirection is not allowed with URLs."
+msgstr ""
+
+#: alpine/arg.c:149
+msgid "\t\tFor mailto URLs, 'body='text should be used in place of"
+msgstr ""
+
+#: alpine/arg.c:150
+msgid "\t\tinput redirection."
+msgstr ""
+
+#: alpine/arg.c:151
+msgid ""
+" -copy_pinerc <local_pinerc> <remote_pinerc> copy local pinerc to remote"
+msgstr ""
+
+#: alpine/arg.c:152
+msgid ""
+" -copy_abook <local_abook> <remote_abook> copy local addressbook to "
+"remote"
+msgstr ""
+
+#: alpine/arg.c:153
+msgid " -convert_sigs -p <pinerc> convert signatures to literal signatures"
+msgstr ""
+
+#: alpine/arg.c:155
+msgid " -install \tPrompt for some basic setup information"
+msgstr ""
+
+#: alpine/arg.c:156
+msgid " -uninstall \tRemove traces of Alpine from Windows system settings"
+msgstr ""
+
+#: alpine/arg.c:157
+msgid " -registry <cmd>\tWhere cmd is set,noset,clear,clearsilent,dump"
+msgstr ""
+
+#: alpine/arg.c:506
+msgid "Alpine related Registry values removed."
+msgstr ""
+
+#: alpine/arg.c:510
+msgid "Not all Alpine related Registry values could be removed"
+msgstr ""
+
+#: alpine/arg.c:528
+msgid "unknown registry command"
+msgstr ""
+
+#: alpine/arg.c:817
+msgid "May only have one of -conf and -pinerc"
+msgstr ""
+
+#: alpine/arg.c:970
+#, c-format
+msgid "unknown debug keyword \"%s\""
+msgstr ""
+
+#: alpine/arg.c:1020
+#, c-format
+msgid "unknown flag \"d\", debugging not compiled in"
+msgstr ""
+
+#: alpine/arg.c:1110
+msgid "Argument Error"
+msgstr ""
+
+#: alpine/arg.c:1170
+#, c-format
+msgid "Missing \"=\" after -%s\n"
+msgstr ""
+
+#: alpine/arg.c:1206
+#, c-format
+msgid "Option \"%s\" is obsolete\n"
+msgstr ""
+
+#: alpine/arg.c:1208
+#, c-format
+msgid "Option \"%s\" is not user settable\n"
+msgstr ""
+
+#: alpine/mailview.c:137
+msgid ""
+"Selectable items in text -- Use Up/Down Arrows to choose, Return to view"
+msgstr ""
+
+#: alpine/mailview.c:139
+msgid "No selected item displayed -- Use PrevPage to bring choice into view"
+msgstr ""
+
+#: alpine/mailview.c:141
+msgid "No selected item displayed -- Use NextPage to bring choice into view"
+msgstr ""
+
+#: alpine/mailview.c:247
+msgid "Screen too small to view message"
+msgstr ""
+
+#: alpine/mailview.c:264
+msgid "No messages to read!"
+msgstr ""
+
+#. TRANSLATORS: a screen title
+#. TRANSLATORS: this is a screen title
+#: alpine/mailview.c:377 alpine/adrbkcmd.c:820
+msgid "MESSAGE TEXT"
+msgstr ""
+
+#: alpine/mailview.c:381
+msgid "HELP FOR MESSAGE TEXT VIEW"
+msgstr ""
+
+#: alpine/mailview.c:728
+msgid "editURL"
+msgstr ""
+
+#: alpine/mailview.c:729
+msgid "editApp"
+msgstr ""
+
+#: alpine/mailview.c:755
+msgid "URL-Viewer is disabled by sys-admin"
+msgstr ""
+
+#. TRANSLATORS: a question
+#: alpine/mailview.c:760
+msgid "No URL-Viewer application defined. Define now"
+msgstr ""
+
+#: alpine/mailview.c:771
+msgid "Web Browser: "
+msgstr ""
+
+#: alpine/mailview.c:805
+#, c-format
+msgid "Browser not found: %s"
+msgstr ""
+
+#: alpine/mailview.c:874
+msgid "Edit URL: "
+msgstr ""
+
+#: alpine/mailview.c:912
+msgid "Viewer Command: "
+msgstr ""
+
+#: alpine/mailview.c:1538
+msgid "VIEWER command completed"
+msgstr ""
+
+#. TRANSLATORS: Cannot start command : <command name>
+#: alpine/mailview.c:1543
+#, c-format
+msgid "Cannot start command : %s"
+msgstr ""
+
+#: alpine/mailview.c:1551
+#, c-format
+msgid "\"URL-Viewer\" not defined: Can't open %s"
+msgstr ""
+
+#: alpine/mailview.c:1874
+msgid "Can't create space for composer"
+msgstr ""
+
+#. TRANSLATORS: these are errors in news group URLs
+#: alpine/mailview.c:2049
+msgid "Invalid newsgroup specified"
+msgstr ""
+
+#: alpine/mailview.c:2058
+msgid "No newsgroup specified"
+msgstr ""
+
+#: alpine/mailview.c:2061
+msgid "No server specified"
+msgstr ""
+
+#: alpine/mailview.c:2096
+msgid "Couldn't find specified article number"
+msgstr ""
+
+#. TRANSLATORS: this is a warning that the file URL can cause programs to run which may
+#. be a security problem. We are asking the user to confirm that they want to do this.
+#: alpine/mailview.c:2195
+msgid "\"file\" URL may cause programs to be run on your system. Run anyway"
+msgstr ""
+
+#: alpine/mailview.c:2208
+msgid "No viewer for \"file\" URL. VIEWER command cancelled"
+msgstr ""
+
+#: alpine/mailview.c:2212
+msgid "VIEWER command cancelled"
+msgstr ""
+
+#: alpine/mailview.c:2748
+msgid "No help text currently available"
+msgstr ""
+
+#: alpine/mailview.c:2803 alpine/mailview.c:3007
+#, c-format
+msgid "Already at start of %s"
+msgstr ""
+
+#: alpine/mailview.c:2826 alpine/mailview.c:2935
+#, c-format
+msgid "Already at end of %s"
+msgstr ""
+
+#: alpine/mailview.c:3042
+#, c-format
+msgid "Already on last item in %s"
+msgstr ""
+
+#: alpine/mailview.c:3077
+#, c-format
+msgid "Already on first item in %s"
+msgstr ""
+
+#: alpine/mailview.c:3201 alpine/confscroll.c:1258 alpine/addrbook.c:5769
+msgid "Current line contains the only match"
+msgstr ""
+
+#: alpine/mailview.c:3256 alpine/takeaddr.c:1636 alpine/confscroll.c:1273
+#: alpine/mailindx.c:3182 alpine/addrbook.c:5761 alpine/mailpart.c:782
+msgid "Word not found"
+msgstr ""
+
+#: alpine/mailview.c:3662
+msgid "First Line"
+msgstr ""
+
+#: alpine/mailview.c:3663
+msgid "Last Line"
+msgstr ""
+
+#: alpine/mailview.c:3682 alpine/mailindx.c:2978
+#, c-format
+msgid "Word to search for [%s] : "
+msgstr ""
+
+#: alpine/mailview.c:3715
+msgid "Searched to First Line."
+msgstr ""
+
+#: alpine/mailview.c:3721
+msgid "Searched to Last Line."
+msgstr ""
+
+#: alpine/mailview.c:5097 alpine/mailview.c:5118 alpine/adrbkcmd.c:155
+#: alpine/adrbkcmd.c:241 alpine/adrbkcmd.c:673 alpine/adrbkcmd.c:804
+#: alpine/alpine.c:2337
+msgid "Error allocating space."
+msgstr ""
+
+#. TRANSLATORS: Justify is to reformat a paragraph automatically
+#. TRANSLATORS: Del Char is Delete Character
+#: alpine/keymenu.c:55 alpine/keymenu.c:918 alpine/radio.c:375
+#: pico/display.c:86 pico/display.c:1359 pico/display.c:1642 pico/word.c:578
+#: pico/composer.c:134
+msgid "Cancel"
+msgstr ""
+
+#. TRANSLATORS: go to Previous Entry
+#: alpine/keymenu.c:78
+msgid "PrevEntry"
+msgstr ""
+
+#. TRANSLATORS: go to Next Entry
+#: alpine/keymenu.c:80
+msgid "NextEntry"
+msgstr ""
+
+#. TRANSLATORS: Select this Entry
+#. TRANSLATORS: Select something, choose something
+#: alpine/keymenu.c:104 alpine/keymenu.c:126 alpine/keymenu.c:345
+#: alpine/keymenu.c:375 alpine/keymenu.c:404 alpine/keymenu.c:430
+#: alpine/keymenu.c:462 alpine/keymenu.c:636 alpine/keymenu.c:666
+#: alpine/keymenu.c:714 alpine/keymenu.c:1240 alpine/keymenu.c:1257
+#: alpine/keymenu.c:1458 alpine/keymenu.c:1474 alpine/keymenu.c:1490
+#: alpine/keymenu.c:1519 alpine/keymenu.c:1548 alpine/keymenu.c:1577
+#: alpine/keymenu.c:1606 alpine/keymenu.c:1635 alpine/keymenu.c:1666
+#: alpine/keymenu.c:1697 alpine/keymenu.c:2270 alpine/keymenu.c:2302
+#: alpine/keymenu.c:2319 alpine/keymenu.c:2335 alpine/keymenu.c:2353
+#: alpine/keymenu.c:2382 alpine/keymenu.c:2411 alpine/keymenu.c:2502
+#: alpine/mailcmd.c:7052 alpine/folder.c:3018 alpine/roleconf.c:165
+#: alpine/addrbook.c:2451 alpine/addrbook.c:2455 alpine/addrbook.c:2460
+#: alpine/addrbook.c:2489 pico/browse.c:2203
+msgid "Select"
+msgstr ""
+
+#. TRANSLATORS: Apply a command to several objects at once
+#: alpine/keymenu.c:106 alpine/keymenu.c:637 alpine/keymenu.c:715
+msgid "Apply"
+msgstr ""
+
+#. TRANSLATORS: Select Current entry
+#. TRANSLATORS: Select current item
+#. TRANSLATORS: Select the current item
+#: alpine/keymenu.c:108 alpine/keymenu.c:347 alpine/keymenu.c:647
+#: alpine/keymenu.c:725 alpine/keymenu.c:855
+msgid "SelectCur"
+msgstr ""
+
+#. TRANSLATORS: Zoom refers to zooming in on a smaller set of
+#. items, like with a camera zoom lense
+#: alpine/keymenu.c:111 alpine/keymenu.c:344 alpine/keymenu.c:648
+#: alpine/keymenu.c:726
+msgid "ZoomMode"
+msgstr ""
+
+#: alpine/keymenu.c:125 alpine/keymenu.c:1332 alpine/keymenu.c:1349
+#: alpine/keymenu.c:1457 alpine/keymenu.c:1489 alpine/keymenu.c:1547
+#: alpine/keymenu.c:1605 alpine/keymenu.c:1714 pico/display.c:69
+#: pico/display.c:1116
+msgid "Exit"
+msgstr ""
+
+#. TRANSLATORS: go to Previous entry
+#: alpine/keymenu.c:128 alpine/keymenu.c:1459 alpine/keymenu.c:1475
+#: alpine/keymenu.c:1491 alpine/keymenu.c:1520 alpine/keymenu.c:1549
+#: alpine/keymenu.c:1578 alpine/keymenu.c:1607 alpine/keymenu.c:1636
+msgid "Prev"
+msgstr ""
+
+#: alpine/keymenu.c:129 alpine/keymenu.c:1460 alpine/keymenu.c:1476
+#: alpine/keymenu.c:1492 alpine/keymenu.c:1521 alpine/keymenu.c:1550
+#: alpine/keymenu.c:1579 alpine/keymenu.c:1608 alpine/keymenu.c:1637
+msgid "Next"
+msgstr ""
+
+#. TRANSLATORS: Abook is an abbreviation for Address Book
+#: alpine/keymenu.c:143
+msgid "Abook"
+msgstr ""
+
+#: alpine/keymenu.c:144
+msgid "Update"
+msgstr ""
+
+#. TRANSLATORS: ComposeTo means to start editing a new message to
+#. this address book entry
+#. TRANSLATORS: compose a message to the current address
+#. TRANSLATORS: Compose a message to be sent to the current address
+#. book entry
+#: alpine/keymenu.c:147 alpine/keymenu.c:197 alpine/keymenu.c:1279
+#: alpine/keymenu.c:1309 alpine/addrbook.c:2601 alpine/addrbook.c:2672
+msgid "ComposeTo"
+msgstr ""
+
+#. TRANSLATORS: abbreviation for Forward as Email
+#: alpine/keymenu.c:154
+msgid "Fwd Email"
+msgstr ""
+
+#. TRANSLATORS: View the highlighted link, for example, a URL
+#: alpine/keymenu.c:160 alpine/keymenu.c:208
+msgid "ViewLink"
+msgstr ""
+
+#. TRANSLATORS: go to the previous link, for example, the previous URL
+#: alpine/keymenu.c:163 alpine/keymenu.c:210 alpine/keymenu.c:479
+msgid "PrevLink"
+msgstr ""
+
+#: alpine/keymenu.c:164 alpine/keymenu.c:211 alpine/keymenu.c:480
+msgid "NextLink"
+msgstr ""
+
+#: alpine/keymenu.c:177 alpine/keymenu.c:506 alpine/keymenu.c:890
+#: alpine/keymenu.c:1302
+msgid "Exit Viewer"
+msgstr ""
+
+#: alpine/keymenu.c:186 alpine/keymenu.c:515 alpine/keymenu.c:562
+#: alpine/keymenu.c:578 alpine/keymenu.c:755 alpine/keymenu.c:899
+#: alpine/keymenu.c:1022 alpine/takeaddr.c:65 alpine/mailcmd.c:176
+#: alpine/adrbkcmd.c:5237 alpine/adrbkcmd.c:5268
+msgid "Save"
+msgstr ""
+
+#. TRANSLATORS: go back to the index of results instead of
+#. viewing this particular entry
+#: alpine/keymenu.c:195
+msgid "Results Index"
+msgstr ""
+
+#: alpine/keymenu.c:224 alpine/folder.c:2826 alpine/addrbook.c:2585
+#: alpine/addrbook.c:2643 alpine/addrbook.c:2665 alpine/addrbook.c:2684
+msgid "Main Menu"
+msgstr ""
+
+#. TRANSLATORS: View this Collection of folders
+#. TRANSLATORS: View this collection
+#: alpine/keymenu.c:226 alpine/keymenu.c:288 alpine/keymenu.c:305
+#: alpine/folder.c:2863 alpine/folder.c:3033
+msgid "View Cltn"
+msgstr ""
+
+#. TRANSLATORS: a command name for a particular key
+#: alpine/keymenu.c:255 alpine/keymenu.c:938 alpine/keymenu.c:1425
+#: alpine/keymenu.c:1731 alpine/addrbook.c:2366
+msgid "Exit Setup"
+msgstr ""
+
+#. TRANSLATORS: Change is a command meaning Change some item,
+#. or Edit some item to change it
+#: alpine/keymenu.c:256 alpine/keymenu.c:1426 alpine/keymenu.c:1675
+#: alpine/keymenu.c:1732 alpine/keymenu.c:2286 alpine/keymenu.c:2442
+#: alpine/keymenu.c:2472 alpine/keymenu.c:2518 alpine/addrbook.c:2396
+msgid "Change"
+msgstr ""
+
+#: alpine/keymenu.c:262
+msgid "Add Cltn"
+msgstr ""
+
+#. TRANSLATORS: Shuffle refers to shuffling the order of things,
+#. that is, changing the order
+#: alpine/keymenu.c:265 alpine/keymenu.c:355 alpine/keymenu.c:1436
+#: alpine/keymenu.c:1740 alpine/keymenu.c:1777 alpine/keymenu.c:1806
+#: alpine/addrbook.c:2392
+msgid "Shuffle"
+msgstr ""
+
+#. TRANSLATORS: Command to Exit the Select screen. This would
+#. be a way to make no Selection and go back to where you
+#. came from.
+#: alpine/keymenu.c:285 alpine/keymenu.c:303 alpine/keymenu.c:368
+#: alpine/keymenu.c:397 alpine/keymenu.c:461 alpine/keymenu.c:664
+#: alpine/keymenu.c:1238 alpine/keymenu.c:1264 alpine/folder.c:2971
+#: alpine/addrbook.c:2415
+msgid "ExitSelect"
+msgstr ""
+
+#. TRANSLATORS: make an addition, for example add a new folder
+#. or a new entry in an address book
+#: alpine/keymenu.c:329 alpine/keymenu.c:1738 alpine/takeaddr.c:601
+#: alpine/folder.c:1220 pico/browse.c:2205
+msgid "Add"
+msgstr ""
+
+#. TRANSLATORS: change the name of something
+#: alpine/keymenu.c:332 alpine/folder.c:5176 pico/browse.c:117
+msgid "Rename"
+msgstr ""
+
+#: alpine/keymenu.c:339 alpine/folder.c:2850
+msgid "View Fldr"
+msgstr ""
+
+#: alpine/keymenu.c:343 alpine/keymenu.c:767 alpine/keymenu.c:813
+#: alpine/mailcmd.c:174
+msgid "Print"
+msgstr ""
+
+#. TRANSLATORS: Import refers to bringing something in from
+#. outside of alpine's normal world
+#: alpine/keymenu.c:360
+msgid "Import"
+msgstr ""
+
+#. TRANSLATORS: Add a new entry (this is a command)
+#: alpine/keymenu.c:406 alpine/addrbook.c:2595 alpine/addrbook.c:2653
+msgid "AddNew"
+msgstr ""
+
+#. TRANSLATORS: Subscribe to a news group
+#: alpine/keymenu.c:427 alpine/folder.c:2141 alpine/folder.c:3077
+msgid "Subscribe"
+msgstr ""
+
+#. TRANSLATORS: Exit Subscribe screen
+#: alpine/keymenu.c:429
+msgid "ExitSubscb"
+msgstr ""
+
+#. TRANSLATORS: List Mode in alpine is where you can select not just
+#. one of something but you can select a whole list of something, for
+#. example a whole list of addresses to send to.
+#: alpine/keymenu.c:438
+msgid "List Mode"
+msgstr ""
+
+#: alpine/keymenu.c:484
+msgid "Print All"
+msgstr ""
+
+#. TRANSLATORS: Continue means to keep going. The user is paused to read
+#. something and has to tell us to continue when they are finished.
+#: alpine/keymenu.c:538
+msgid "Continue"
+msgstr ""
+
+#: alpine/keymenu.c:553 alpine/keymenu.c:569
+msgid "Yes, continue"
+msgstr ""
+
+#: alpine/keymenu.c:554 alpine/imap.c:1523
+msgid "Details"
+msgstr ""
+
+#. TRANSLATORS: Exit Command List
+#: alpine/keymenu.c:586
+msgid "Exit CmdList"
+msgstr ""
+
+#: alpine/keymenu.c:587
+msgid "Try Command"
+msgstr ""
+
+#. TRANSLATORS: go to Previous Command in list
+#: alpine/keymenu.c:590
+msgid "Prev Cmd"
+msgstr ""
+
+#. TRANSLATORS: go to Next Command in list
+#: alpine/keymenu.c:592
+msgid "Next Cmd"
+msgstr ""
+
+#. TRANSLATORS: this stands for unexclude which is the opposite
+#. of the exclude command. Exclude eliminates some messages from
+#. the view and unexclude gets them back.
+#: alpine/keymenu.c:635 alpine/keymenu.c:713
+msgid "unXclude"
+msgstr ""
+
+#. TRANSLATORS: toggles a collapsed view or an expanded view
+#. of a message thread on and off
+#: alpine/keymenu.c:656 alpine/keymenu.c:732
+msgid "Collapse/Expand"
+msgstr ""
+
+#. TRANSLATORS: go to the Folder List
+#: alpine/keymenu.c:682 alpine/mailindx.c:136
+msgid "FldrList"
+msgstr ""
+
+#. TRANSLATORS: View a Thread of messages
+#: alpine/keymenu.c:684
+msgid "ViewThd"
+msgstr ""
+
+#. TRANSLATORS: go to the Previous Thread
+#: alpine/keymenu.c:687
+msgid "PrevThd"
+msgstr ""
+
+#. TRANSLATORS: go to the Next Thread
+#: alpine/keymenu.c:689
+msgid "NextThd"
+msgstr ""
+
+#: alpine/keymenu.c:742 alpine/keymenu.c:1272 alpine/keymenu.c:1303
+#: alpine/folder.c:882 alpine/folder.c:970 pico/browse.c:2197
+msgid "View"
+msgstr ""
+
+#. TRANSLATORS: go to Previous Attachment
+#: alpine/keymenu.c:745
+msgid "PrevAttch"
+msgstr ""
+
+#. TRANSLATORS: go to Next Attachment
+#: alpine/keymenu.c:748
+msgid "NextAtch"
+msgstr ""
+
+#. TRANSLATORS: About Attachment, a short description of the attachment
+#: alpine/keymenu.c:765
+msgid "AboutAttch"
+msgstr ""
+
+#. TRANSLATORS: View highlighted URL
+#. TRANSLATORS: View the highlighted URL
+#: alpine/keymenu.c:792 alpine/keymenu.c:852
+msgid "View Hilite"
+msgstr ""
+
+#. TRANSLATORS: go to Previous URL
+#. TRANSLATORS: go to previous URL
+#: alpine/keymenu.c:795 alpine/keymenu.c:857
+msgid "Prev URL"
+msgstr ""
+
+#. TRANSLATORS: go to Next URL
+#. TRANSLATORS: go to next URL
+#: alpine/keymenu.c:797 alpine/keymenu.c:859
+msgid "Next URL"
+msgstr ""
+
+#. TRANSLATORS: go to Message Index
+#: alpine/keymenu.c:824
+msgid "MsgIndex"
+msgstr ""
+
+#. TRANSLATORS: View the Attachment
+#: alpine/keymenu.c:826
+msgid "ViewAttch"
+msgstr ""
+
+#: alpine/keymenu.c:872
+msgid "TogglePreferPlain"
+msgstr ""
+
+#: alpine/keymenu.c:917 alpine/radio.c:287 alpine/radio.c:367
+msgid "Help"
+msgstr ""
+
+#. TRANSLATORS: The user is entering characters, for example, the
+#. name of a folder. Accept means the user is done and wants to
+#. accept what is currently displayed.
+#: alpine/keymenu.c:923
+msgid "Accept"
+msgstr ""
+
+#: alpine/keymenu.c:939
+msgid "Printer"
+msgstr ""
+
+#. TRANSLATORS: Change password
+#: alpine/keymenu.c:941
+msgid "Newpassword"
+msgstr ""
+
+#. TRANSLATORS: Configure Alpine
+#: alpine/keymenu.c:943 alpine/adrbkcmd.c:3177
+msgid "Config"
+msgstr ""
+
+#. TRANSLATORS: Edit signature block
+#: alpine/keymenu.c:945
+msgid "Signature"
+msgstr ""
+
+#. TRANSLATORS: configure address books
+#: alpine/keymenu.c:947
+msgid "AddressBooks"
+msgstr ""
+
+#. TRANSLATORS: configure collection lists
+#: alpine/keymenu.c:949
+msgid "collectionList"
+msgstr ""
+
+#. TRANSLATORS: configure rules, an alpine concept
+#: alpine/keymenu.c:951
+msgid "Rules"
+msgstr ""
+
+#. TRANSLATORS: configure directory servers
+#: alpine/keymenu.c:953 alpine/folder.c:4301
+msgid "Directory"
+msgstr ""
+
+#. TRANSLATORS: configure color
+#: alpine/keymenu.c:955
+msgid "Kolor"
+msgstr ""
+
+#. TRANSLATORS: remote configuration setup
+#: alpine/keymenu.c:961
+msgid "RemoteConfigSetup"
+msgstr ""
+
+#. TRANSLATORS: configure S/MIME
+#: alpine/keymenu.c:963
+msgid "S/Mime"
+msgstr ""
+
+#. TRANSLATORS: go to Previous Command in list
+#: alpine/keymenu.c:980
+msgid "PrevCmd"
+msgstr ""
+
+#: alpine/keymenu.c:981
+msgid "NextCmd"
+msgstr ""
+
+#. TRANSLATORS: show release notes
+#: alpine/keymenu.c:985 alpine/keymenu.c:1053
+msgid "RelNotes"
+msgstr ""
+
+#. TRANSLATORS: lock keyboard
+#: alpine/keymenu.c:987
+msgid "KBLock"
+msgstr ""
+
+#: alpine/keymenu.c:997 alpine/mailcmd.c:5892
+msgid "Index"
+msgstr ""
+
+#. TRANSLATORS: go to the Journal. The Journal shows past
+#. messages that alpine has shown the user.
+#: alpine/keymenu.c:1000
+msgid "Journal"
+msgstr ""
+
+#. TRANSLATORS: go to the Setup screen
+#: alpine/keymenu.c:1002
+msgid "Setup"
+msgstr ""
+
+#. TRANSLATORS: go to the address book screen
+#: alpine/keymenu.c:1004
+msgid "AddrBook"
+msgstr ""
+
+#: alpine/keymenu.c:1013
+msgid "Quit Viewer"
+msgstr ""
+
+#. TRANSLATORS: Alpine asks the user to be counted when they
+#. first start using alpine.
+#: alpine/keymenu.c:1045
+msgid "Be Counted!"
+msgstr ""
+
+#: alpine/keymenu.c:1074
+msgid "Finished"
+msgstr ""
+
+#. TRANSLATORS: Take this address into the address book
+#: alpine/keymenu.c:1092 alpine/keymenu.c:1110 alpine/keymenu.c:2548
+#: alpine/keymenu.c:2564
+msgid "Take"
+msgstr ""
+
+#. TRANSLATORS: Set/Unset means that this particular command
+#. will toggle between setting something (turning it on) and
+#. unsetting it (turning it off). For example, it might be
+#. a program option that can be turned on or off or it might
+#. be a way to mark which addresses to send a message to.
+#: alpine/keymenu.c:1097 alpine/keymenu.c:1553 alpine/keymenu.c:1582
+#: alpine/keymenu.c:1611 alpine/keymenu.c:1640 alpine/keymenu.c:2569
+#: alpine/folder.c:3070 alpine/addrbook.c:2517 pico/browse.c:2210
+msgid "Set/Unset"
+msgstr ""
+
+#: alpine/keymenu.c:1098 alpine/keymenu.c:2570
+msgid "SetAll"
+msgstr ""
+
+#: alpine/keymenu.c:1099 alpine/keymenu.c:2571
+msgid "UnSetAll"
+msgstr ""
+
+#. TRANSLATORS: The Take Address screen has a Single mode and a
+#. List mode. This command causes us to go into Single mode.
+#: alpine/keymenu.c:1102 alpine/keymenu.c:1554 alpine/keymenu.c:1583
+#: alpine/keymenu.c:2572
+msgid "SinglMode"
+msgstr ""
+
+#. TRANSLATORS: The Take Address screen has a Single mode and a
+#. List mode. This command causes us to go into List mode.
+#. TRANSLATORS: List mode is a type of screen in pine that
+#. allows the user to select several of something. This is
+#. the name of the command to go into the List mode style
+#. of operating.
+#: alpine/keymenu.c:1120 alpine/keymenu.c:1496 alpine/keymenu.c:1525
+#: alpine/keymenu.c:2556 alpine/addrbook.c:2526 pico/browse.c:2214
+msgid "ListMode"
+msgstr ""
+
+#: alpine/keymenu.c:1126
+msgid "Stop Waiting"
+msgstr ""
+
+#. TRANSLATORS: Change Value
+#: alpine/keymenu.c:1145 alpine/keymenu.c:1367 alpine/keymenu.c:1396
+#: alpine/keymenu.c:1763 alpine/keymenu.c:1792 alpine/keymenu.c:1821
+#: alpine/keymenu.c:1852 alpine/keymenu.c:1881 alpine/keymenu.c:1911
+#: alpine/keymenu.c:1941 alpine/keymenu.c:1971 alpine/keymenu.c:2000
+#: alpine/keymenu.c:2046 alpine/keymenu.c:2076 alpine/keymenu.c:2106
+#: alpine/keymenu.c:2135 alpine/keymenu.c:2165 alpine/keymenu.c:2194
+#: alpine/keymenu.c:2224
+msgid "Change Val"
+msgstr ""
+
+#. TRANSLATORS: Delete Value
+#: alpine/keymenu.c:1152 alpine/keymenu.c:1373 alpine/keymenu.c:1402
+#: alpine/keymenu.c:1769 alpine/keymenu.c:1798 alpine/keymenu.c:1827
+#: alpine/keymenu.c:1858 alpine/keymenu.c:1887 alpine/keymenu.c:1917
+#: alpine/keymenu.c:1947 alpine/keymenu.c:1977 alpine/keymenu.c:2006
+#: alpine/keymenu.c:2052 alpine/keymenu.c:2082 alpine/keymenu.c:2112
+#: alpine/keymenu.c:2141 alpine/keymenu.c:2171 alpine/keymenu.c:2200
+#: alpine/keymenu.c:2230
+msgid "Delete Val"
+msgstr ""
+
+#: alpine/keymenu.c:1221
+msgid "Add KW"
+msgstr ""
+
+#. TRANSLATORS: Exit from the Flags screen
+#: alpine/keymenu.c:1223
+msgid "Exit Flags"
+msgstr ""
+
+#. TRANSLATORS: go to address book list
+#: alpine/keymenu.c:1256 alpine/keymenu.c:1271 alpine/addrbook.c:2435
+#: alpine/addrbook.c:2638
+msgid "AddressBkList"
+msgstr ""
+
+#: alpine/keymenu.c:1372 alpine/keymenu.c:1401 alpine/keymenu.c:1768
+#: alpine/keymenu.c:1797 alpine/keymenu.c:1826 alpine/keymenu.c:1857
+#: alpine/keymenu.c:1886 alpine/keymenu.c:1916 alpine/keymenu.c:1946
+#: alpine/keymenu.c:1976 alpine/keymenu.c:2005 alpine/keymenu.c:2051
+#: alpine/keymenu.c:2081 alpine/keymenu.c:2111 alpine/keymenu.c:2140
+#: alpine/keymenu.c:2170 alpine/keymenu.c:2199 alpine/keymenu.c:2229
+msgid "Add Value"
+msgstr ""
+
+#. TRANSLATORS: go to list of keywords
+#: alpine/keymenu.c:1411
+msgid "ToCharsets"
+msgstr ""
+
+#. TRANSLATORS: go to previous LDAP directory server in the list
+#: alpine/keymenu.c:1428
+msgid "PrevDir"
+msgstr ""
+
+#: alpine/keymenu.c:1429
+msgid "NextDir"
+msgstr ""
+
+#. TRANSLATORS: add a directory server to configuration
+#: alpine/keymenu.c:1433
+msgid "Add Dir"
+msgstr ""
+
+#. TRANSLATORS: delete a directory
+#: alpine/keymenu.c:1435
+msgid "Del Dir"
+msgstr ""
+
+#: alpine/keymenu.c:1473 alpine/keymenu.c:1518 alpine/keymenu.c:1576
+#: alpine/keymenu.c:1634
+msgid "exit"
+msgstr ""
+
+#. TRANSLATORS: add a printer to configuration
+#: alpine/keymenu.c:1672
+msgid "Add Printer"
+msgstr ""
+
+#. TRANSLATORS: delete a printer from configuration
+#: alpine/keymenu.c:1674
+msgid "DeletePrint"
+msgstr ""
+
+#. TRANSLATORS: go to previous Role in list
+#: alpine/keymenu.c:1717
+msgid "PrevRole"
+msgstr ""
+
+#: alpine/keymenu.c:1718
+msgid "NextRole"
+msgstr ""
+
+#. TRANSLATORS: go to previous Rule in list
+#: alpine/keymenu.c:1734
+msgid "PrevRule"
+msgstr ""
+
+#: alpine/keymenu.c:1735
+msgid "NextRule"
+msgstr ""
+
+#: alpine/keymenu.c:1739 alpine/addrbook.c:2597 alpine/addrbook.c:2655
+#: pico/browse.c:114
+msgid "Delete"
+msgstr ""
+
+#. TRANSLATORS: Include a File from filesystem
+#: alpine/keymenu.c:1748
+msgid "IncludeFile"
+msgstr ""
+
+#: alpine/keymenu.c:1749
+msgid "eXcludeFile"
+msgstr ""
+
+#: alpine/keymenu.c:1752
+msgid "Replicate"
+msgstr ""
+
+#. TRANSLATORS: go to list of folders
+#: alpine/keymenu.c:1807 alpine/keymenu.c:2150 alpine/keymenu.c:2179
+#: alpine/mailcmd.c:5314
+msgid "ToFldrs"
+msgstr ""
+
+#. TRANSLATORS: go to list of Files
+#: alpine/keymenu.c:1836 alpine/keymenu.c:1866
+msgid "ToFiles"
+msgstr ""
+
+#. TRANSLATORS: edit a file
+#: alpine/keymenu.c:1838
+msgid "editFile"
+msgstr ""
+
+#. TRANSLATORS: go to list of keywords
+#: alpine/keymenu.c:1896 alpine/keymenu.c:1925
+msgid "ToKeywords"
+msgstr ""
+
+#. TRANSLATORS: toggle between NOT and not NOT, turn NOT on or off
+#: alpine/keymenu.c:1928 alpine/keymenu.c:1958 alpine/keymenu.c:2017
+#: alpine/keymenu.c:2063 alpine/keymenu.c:2092
+msgid "toggle NOT"
+msgstr ""
+
+#. TRANSLATORS: go to list of character sets
+#: alpine/keymenu.c:1956
+msgid "ToCharSets"
+msgstr ""
+
+#. TRANSLATORS: add extra headers to list
+#: alpine/keymenu.c:2016 alpine/keymenu.c:2030 alpine/keymenu.c:2062
+#: alpine/keymenu.c:2091
+msgid "eXtraHdr"
+msgstr ""
+
+#. TRANSLATORS: go to address book to get address
+#: alpine/keymenu.c:2061 alpine/keymenu.c:2120
+msgid "ToAddrBk"
+msgstr ""
+
+#. TRANSLATORS: remove a header we previously added
+#: alpine/keymenu.c:2095
+msgid "RemoveHdr"
+msgstr ""
+
+#. TRANSLATORS: go to list of nicknames
+#: alpine/keymenu.c:2209
+msgid "ToNicks"
+msgstr ""
+
+#. TRANSLATORS: go to list of address books
+#: alpine/keymenu.c:2239
+msgid "ToAbookList"
+msgstr ""
+
+#. TRANSLATORS: go to color configuration screen
+#: alpine/keymenu.c:2301 alpine/keymenu.c:2318 alpine/keymenu.c:2334
+#: alpine/keymenu.c:2352 alpine/keymenu.c:2381 alpine/keymenu.c:2410
+msgid "To Colors"
+msgstr ""
+
+#: alpine/keymenu.c:2358 alpine/keymenu.c:2387 alpine/keymenu.c:2416
+msgid "Customize"
+msgstr ""
+
+#: alpine/keymenu.c:2447 alpine/keymenu.c:2477 alpine/keymenu.c:2523
+msgid "AddHeader"
+msgstr ""
+
+#. TRANSLATORS: restore defaults
+#: alpine/keymenu.c:2449 alpine/keymenu.c:2478 alpine/keymenu.c:2524
+msgid "RestoreDefs"
+msgstr ""
+
+#: alpine/keymenu.c:2486
+msgid "DeleteHdr"
+msgstr ""
+
+#. TRANSLATORS: shuffle headers (change the order of headers)
+#: alpine/keymenu.c:2488
+msgid "ShuffleHdr"
+msgstr ""
+
+#. TRANSLATORS: exit the Take Address screen
+#: alpine/keymenu.c:2547 alpine/keymenu.c:2563
+msgid "ExitTake"
+msgstr ""
+
+#: alpine/keymenu.c:2609
+msgid "Transfer"
+msgstr ""
+
+#: alpine/takeaddr.c:66 alpine/mailcmd.c:177 alpine/adrbkcmd.c:5238
+#: alpine/mailpart.c:432
+msgid "Export"
+msgstr ""
+
+#. TRANSLATORS: To AddrBk is a command that takes the user to
+#. the address book screen to select an entry from there.
+#: alpine/takeaddr.c:137 alpine/send.c:1097 alpine/adrbkcmd.c:845
+#: alpine/adrbkcmd.c:860
+msgid "To AddrBk"
+msgstr ""
+
+#. TRANSLATORS: command asks alpine to complete the name when tab is typed
+#. TRANSLATORS: Complete is a verb, complete the name of a folder
+#: alpine/takeaddr.c:143 alpine/send.c:1103 alpine/mailcmd.c:2699
+#: alpine/mailcmd.c:3473 alpine/mailcmd.c:3884 alpine/mailcmd.c:5337
+#: alpine/adrbkcmd.c:4241 alpine/folder.c:3570 alpine/folder.c:3704
+#: alpine/folder.c:4812 alpine/mailpart.c:1323 alpine/mailpart.c:1732
+#: alpine/mailpart.c:1811 pico/attach.c:73 pico/file.c:157 pico/file.c:579
+msgid "Complete"
+msgstr ""
+
+#: alpine/takeaddr.c:175
+#, c-format
+msgid "Already an entry with nickname \"%s\""
+msgstr ""
+
+#. TRANSLATORS: This is sort of an error, something unexpected has
+#. happened and alpine is re-initializing the address book.
+#. TRANSLATORS: a warning message telling the user that the address book
+#. is being reset (re-sychronized, restarted)
+#: alpine/takeaddr.c:421 alpine/adrbkcmd.c:1469 alpine/addrbook.c:1527
+#: alpine/addrbook.c:1557 alpine/addrbook.c:1589 alpine/addrbook.c:1630
+#: alpine/addrbook.c:1670 alpine/addrbook.c:1712 alpine/addrbook.c:1755
+#: alpine/addrbook.c:1803 alpine/addrbook.c:1845 alpine/addrbook.c:1885
+#: alpine/addrbook.c:1929 alpine/addrbook.c:1977 pith/ablookup.c:80
+#: pith/ablookup.c:151 pith/ablookup.c:275
+msgid "Resetting address book..."
+msgstr ""
+
+#: alpine/takeaddr.c:526
+#, c-format
+msgid "Warning: address exists with %s%s, continue "
+msgstr ""
+
+#: alpine/takeaddr.c:558
+#, c-format
+msgid "Enter new or existing nickname (one word and easy to remember): "
+msgstr ""
+
+#: alpine/takeaddr.c:571
+#, c-format
+msgid "Already an entry %s in address book!"
+msgstr ""
+
+#: alpine/takeaddr.c:579 alpine/takeaddr.c:600 alpine/confscroll.c:1698
+#: alpine/print.c:746 pico/search.c:613
+msgid "Replace"
+msgstr ""
+
+#: alpine/takeaddr.c:583
+#, c-format
+msgid "Entry %s (%s) exists, replace ? "
+msgstr ""
+
+#: alpine/takeaddr.c:606
+#, c-format
+msgid "%s %s (%s) exists, replace or add addresses to it ? "
+msgstr ""
+
+#: alpine/takeaddr.c:647
+msgid "Address book is at maximum size, cancelled."
+msgstr ""
+
+#: alpine/takeaddr.c:790
+#, c-format
+msgid "All of the addresses are already included in \"%s\""
+msgstr ""
+
+#: alpine/takeaddr.c:868 alpine/takeaddr.c:1324
+msgid "Address book addition cancelled"
+msgstr ""
+
+#: alpine/takeaddr.c:896
+msgid "No address book configured!"
+msgstr ""
+
+#: alpine/takeaddr.c:917
+msgid "Can't open address book!"
+msgstr ""
+
+#. TRANSLATORS: Address book can be viewed but not changed
+#: alpine/takeaddr.c:923 alpine/adrbkcmd.c:414 alpine/adrbkcmd.c:432
+#: alpine/addrbook.c:2132 alpine/addrbook.c:3358 alpine/addrbook.c:4109
+msgid "AddressBook is Read Only"
+msgstr ""
+
+#: alpine/takeaddr.c:926 alpine/addrbook.c:2135
+msgid "AddressBook not accessible, permission denied"
+msgstr ""
+
+#: alpine/takeaddr.c:955
+msgid "Prev AddrBook"
+msgstr ""
+
+#: alpine/takeaddr.c:956
+msgid "Next AddrBook"
+msgstr ""
+
+#: alpine/takeaddr.c:1008
+#, c-format
+msgid "No addressbook \"%s\""
+msgstr ""
+
+#: alpine/takeaddr.c:1159 alpine/takeaddr.c:1473
+msgid "List mode: Use \"X\" to mark addresses to be included in list"
+msgstr ""
+
+#: alpine/takeaddr.c:1162 alpine/takeaddr.c:1468
+msgid "Single mode: Use \"P\" or \"N\" to select desired address"
+msgstr ""
+
+#: alpine/takeaddr.c:1318
+msgid "HELP FOR TAKE ADDRESS SCREEN"
+msgstr ""
+
+#: alpine/takeaddr.c:1342 alpine/addrbook.c:3807
+msgid "Already on last line."
+msgstr ""
+
+#: alpine/takeaddr.c:1350 alpine/addrbook.c:3749
+msgid "Already on first line."
+msgstr ""
+
+#: alpine/takeaddr.c:1366 alpine/addrbook.c:4043
+msgid "Already on last page."
+msgstr ""
+
+#: alpine/takeaddr.c:1393 alpine/addrbook.c:4007
+msgid "Already on first page."
+msgstr ""
+
+#: alpine/takeaddr.c:1525 alpine/takeaddr.c:2652
+msgid "save"
+msgstr ""
+
+#. TRANSLATORS: go to Top of screen
+#: alpine/takeaddr.c:1560 alpine/confscroll.c:1052
+msgid "Top"
+msgstr ""
+
+#: alpine/takeaddr.c:1561 alpine/confscroll.c:1053
+msgid "Bottom"
+msgstr ""
+
+#: alpine/takeaddr.c:1569
+#, c-format
+msgid "Word to find %s%.*s%s: "
+msgstr ""
+
+#. TRANSLATORS: Searched to ... is the result of the search, searched
+#. to top means the search went past the bottom of the screen and
+#. wrapped back around to the top.
+#: alpine/takeaddr.c:1618 alpine/confscroll.c:1233
+msgid "Searched to top"
+msgstr ""
+
+#: alpine/takeaddr.c:1622 alpine/confscroll.c:1247
+msgid "Searched to bottom"
+msgstr ""
+
+#: alpine/takeaddr.c:1626 alpine/confscroll.c:1250 alpine/mailpart.c:772
+msgid "WhereIs cancelled"
+msgstr ""
+
+#: alpine/takeaddr.c:1631 alpine/addrbook.c:5766
+msgid "Search wrapped to beginning"
+msgstr ""
+
+#: alpine/takeaddr.c:1631 alpine/confscroll.c:1269
+msgid "Word found"
+msgstr ""
+
+#: alpine/takeaddr.c:1659
+msgid "No addresses marked for taking. Use ExitTake to leave TakeAddr screen"
+msgstr ""
+
+#: alpine/takeaddr.c:1999
+msgid "Can't find anything to export"
+msgstr ""
+
+#: alpine/takeaddr.c:2021 alpine/adrbkcmd.c:1577
+msgid "take"
+msgstr ""
+
+#: alpine/takeaddr.c:2465 alpine/takeaddr.c:2592
+msgid "Nothing to save, cancelled"
+msgstr ""
+
+#: alpine/takeaddr.c:2496
+msgid "Backup"
+msgstr ""
+
+#: alpine/takeaddr.c:2497
+msgid "LDAP"
+msgstr ""
+
+#: alpine/takeaddr.c:2502
+msgid "Copy backup address or retain LDAP search criteria ? "
+msgstr ""
+
+#: alpine/takeaddr.c:2611 alpine/takeaddr.c:3041 alpine/adrbkcmd.c:5248
+#, c-format
+msgid "Save to address book or Export to filesystem ? "
+msgstr ""
+
+#: alpine/takeaddr.c:2617 alpine/takeaddr.c:3047 alpine/adrbkcmd.c:5254
+#: alpine/adrbkcmd.c:5447 alpine/adrbkcmd.c:5612
+msgid "Address book save cancelled"
+msgstr ""
+
+#: alpine/takeaddr.c:2664
+msgid "Save cancelled: no entries in attachment"
+msgstr ""
+
+#: alpine/takeaddr.c:2681 alpine/adrbkcmd.c:4177
+msgid "Address List"
+msgstr ""
+
+#. TRANSLATORS: A VCard is kind of like an electronic business card. It is not
+#. something specific to alpine, it is universal.
+#: alpine/takeaddr.c:2682 alpine/takeaddr.c:3271 alpine/adrbkcmd.c:381
+#: alpine/adrbkcmd.c:4178
+msgid "VCard"
+msgstr ""
+
+#: alpine/takeaddr.c:2693
+msgid "Export list of addresses or vCard text ? "
+msgstr ""
+
+#: alpine/takeaddr.c:2699 alpine/takeaddr.c:3281 alpine/adrbkcmd.c:4208
+#: alpine/adrbkcmd.c:4254
+msgid "Address book export cancelled"
+msgstr ""
+
+#: alpine/takeaddr.c:2710
+msgid "can't happen in export_vcard_att"
+msgstr ""
+
+#: alpine/takeaddr.c:2746
+msgid "No addresses to export"
+msgstr ""
+
+#: alpine/takeaddr.c:2752 alpine/takeaddr.c:2860
+msgid "Nothing to export"
+msgstr ""
+
+#: alpine/takeaddr.c:2754 alpine/takeaddr.c:2854 alpine/takeaddr.c:3287
+#: alpine/takeaddr.c:3301 alpine/takeaddr.c:3319 alpine/mailcmd.c:3888
+#: alpine/adrbkcmd.c:6955 alpine/adrbkcmd.c:7146 alpine/reply.c:2203
+#: alpine/mailpart.c:2817
+msgid "Error allocating space"
+msgstr ""
+
+#: alpine/takeaddr.c:2765
+msgid "HELP FOR TAKE EXPORT SCREEN"
+msgstr ""
+
+#: alpine/takeaddr.c:2804
+msgid "No lines to export"
+msgstr ""
+
+#: alpine/takeaddr.c:2807
+msgid "Take Export"
+msgstr ""
+
+#: alpine/takeaddr.c:2858
+msgid "Use \"X\" to mark selections"
+msgstr ""
+
+#. TRANSLATORS: Text refers to plain old text, probably the text of
+#. an email message
+#: alpine/takeaddr.c:3269 alpine/mailcmd.c:154 alpine/mailcmd.c:194
+#: alpine/adrbkcmd.c:378 alpine/folder.c:3115
+msgid "Text"
+msgstr ""
+
+#. TRANSLATORS: Addresses refers to email Addresses
+#: alpine/takeaddr.c:3270 alpine/adrbkcmd.c:199 alpine/adrbkcmd.c:859
+#: alpine/adrbkcmd.c:1070 alpine/adrbkcmd.c:5920 alpine/adrbkcmd.c:5934
+#: alpine/adrbkcmd.c:5943 alpine/adrbkcmd.c:5954 alpine/adrbkcmd.c:5961
+msgid "Addresses"
+msgstr ""
+
+#: alpine/takeaddr.c:3275
+msgid "Export text of entry, address, or VCard format ? "
+msgstr ""
+
+#: alpine/ldapconf.c:59
+msgid "HELP FOR SEARCH RESULTS INDEX"
+msgstr ""
+
+#: alpine/ldapconf.c:61
+msgid "Use Add to add a directory server"
+msgstr ""
+
+#: alpine/ldapconf.c:68
+msgid "Directory Server on "
+msgstr ""
+
+#: alpine/ldapconf.c:381
+#, c-format
+msgid "(%d email addresses)"
+msgstr ""
+
+#: alpine/ldapconf.c:399
+msgid "<No Email Address Available>"
+msgstr ""
+
+#. TRANSLATORS: No matches returned for an LDAP search
+#: alpine/ldapconf.c:441
+#, c-format
+msgid "%s, No Matches Returned"
+msgstr ""
+
+#: alpine/ldapconf.c:444
+msgid "No Matches"
+msgstr ""
+
+#. TRANSLATORS: a request for user to choose Exit after they read text
+#: alpine/ldapconf.c:449
+msgid " -- Choose Exit ]"
+msgstr ""
+
+#. TRANSLATORS: Print something1 using something2.
+#. "this" is something1
+#: alpine/ldapconf.c:473
+msgid "this"
+msgstr ""
+
+#: alpine/ldapconf.c:553
+msgid "No email address available for this entry; choose another or ExitSelect"
+msgstr ""
+
+#: alpine/ldapconf.c:634
+msgid "Bad Server Config, Delete this"
+msgstr ""
+
+#: alpine/ldapconf.c:681
+msgid "Exception Setup not implemented for directory"
+msgstr ""
+
+#: alpine/ldapconf.c:724
+msgid "SETUP DIRECTORY SERVERS"
+msgstr ""
+
+#. TRANSLATORS: Print something1 using something2.
+#. servers is something1
+#: alpine/ldapconf.c:724 alpine/ldapconf.c:1760
+msgid "servers"
+msgstr ""
+
+#: alpine/ldapconf.c:740 alpine/roleconf.c:1095
+msgid "Nothing to Delete, use Add"
+msgstr ""
+
+#: alpine/ldapconf.c:766 alpine/roleconf.c:1116
+msgid "Nothing to Shuffle, use Add"
+msgstr ""
+
+#: alpine/ldapconf.c:905
+msgid "Add cancelled, no server name"
+msgstr ""
+
+#: alpine/ldapconf.c:937
+msgid "Shuffle only makes sense when there is more than one server in list"
+msgstr ""
+
+#. TRANSLATORS: shuffle something Up or Down in a list
+#: alpine/ldapconf.c:948 alpine/adrbkcmd.c:3513 alpine/confscroll.c:2318
+#: alpine/roleconf.c:1685 alpine/colorconf.c:2148 alpine/context.c:603
+msgid "Up"
+msgstr ""
+
+#: alpine/ldapconf.c:953 alpine/adrbkcmd.c:3518 alpine/confscroll.c:2323
+#: alpine/roleconf.c:1690 alpine/colorconf.c:2153 alpine/context.c:621
+msgid "Down"
+msgstr ""
+
+#: alpine/ldapconf.c:967 alpine/adrbkcmd.c:3566 alpine/roleconf.c:1762
+#: alpine/colorconf.c:2166 alpine/context.c:630
+msgid "UP"
+msgstr ""
+
+#: alpine/ldapconf.c:969 alpine/adrbkcmd.c:3568 alpine/roleconf.c:1764
+#: alpine/colorconf.c:2168 alpine/context.c:632
+msgid "DOWN"
+msgstr ""
+
+#: alpine/ldapconf.c:1018 alpine/adrbkcmd.c:3628
+msgid "Shuffle cancelled: couldn't save configuration file"
+msgstr ""
+
+#: alpine/ldapconf.c:1074
+msgid "No change, cancelled"
+msgstr ""
+
+#: alpine/ldapconf.c:1077
+msgid "Change cancelled, use Delete if you want to remove this server"
+msgstr ""
+
+#. TRANSLATORS: Features is a section title in the LDAP configuration screen. Following
+#. this are a list of features or options that can be turned on or off.
+#: alpine/ldapconf.c:1336 alpine/roleconf.c:4602
+msgid "Features"
+msgstr ""
+
+#: alpine/ldapconf.c:1362
+msgid "HELP FOR LDAP SERVER"
+msgstr ""
+
+#: alpine/ldapconf.c:1378
+msgid "HELP FOR SERVER SEARCH BASE"
+msgstr ""
+
+#: alpine/ldapconf.c:1392
+msgid "HELP FOR PORT NUMBER"
+msgstr ""
+
+#: alpine/ldapconf.c:1407
+msgid "HELP FOR SERVER BIND DN"
+msgstr ""
+
+#: alpine/ldapconf.c:1421
+msgid "HELP FOR SERVER NICKNAME"
+msgstr ""
+
+#: alpine/ldapconf.c:1479
+msgid "HELP FOR LDAP FEATURES"
+msgstr ""
+
+#: alpine/ldapconf.c:1558
+msgid "HELP FOR SEARCH TYPE"
+msgstr ""
+
+#: alpine/ldapconf.c:1622
+msgid "HELP FOR SEARCH RULE"
+msgstr ""
+
+#: alpine/ldapconf.c:1646
+msgid "HELP FOR EMAIL ATTRIBUTE NAME"
+msgstr ""
+
+#: alpine/ldapconf.c:1660
+msgid "HELP FOR NAME ATTRIBUTE NAME"
+msgstr ""
+
+#: alpine/ldapconf.c:1674
+msgid "HELP FOR SURNAME ATTRIBUTE NAME"
+msgstr ""
+
+#: alpine/ldapconf.c:1688
+msgid "HELP FOR GIVEN NAME ATTRIBUTE NAME"
+msgstr ""
+
+#: alpine/ldapconf.c:1707
+msgid "HELP FOR SERVER TIMELIMIT"
+msgstr ""
+
+#: alpine/ldapconf.c:1723
+msgid "HELP FOR SERVER SIZELIMIT"
+msgstr ""
+
+#: alpine/ldapconf.c:1741
+msgid "HELP FOR CUSTOM SEARCH FILTER"
+msgstr ""
+
+#: alpine/ldapconf.c:2022
+msgid "Delete (unused) directory servers "
+msgstr ""
+
+#: alpine/ldapconf.c:2030
+msgid "Can't delete sys-admin defined value"
+msgstr ""
+
+#. TRANSLATORS: Ignore All means ignore all of the default values,
+#. and Remove One means just remove this one default value.
+#: alpine/ldapconf.c:2056 alpine/adrbkcmd.c:3121
+msgid "Ignore All"
+msgstr ""
+
+#: alpine/ldapconf.c:2057 alpine/adrbkcmd.c:3122
+msgid "Remove One"
+msgstr ""
+
+#: alpine/ldapconf.c:2060
+msgid "Ignore all default directory servers or just remove this one ? "
+msgstr ""
+
+#: alpine/ldapconf.c:2066
+#, c-format
+msgid "Really delete %s \"%s\" from directory servers "
+msgstr ""
+
+#: alpine/ldapconf.c:2167
+msgid "Reverting to default directory server"
+msgstr ""
+
+#: alpine/ldapconf.c:2207
+msgid "Server not deleted"
+msgstr ""
+
+#: alpine/ldapconf.c:2227 alpine/ldapconf.c:2282
+msgid "HELP FOR DIRECTORY SERVER CONFIGURATION"
+msgstr ""
+
+#: alpine/send.c:144
+msgid "Continue INTERRUPTED composition (answering \"n\" won't erase it)"
+msgstr ""
+
+#: alpine/send.c:146
+msgid "Continue postponed composition (answering \"No\" won't erase it)"
+msgstr ""
+
+#: alpine/send.c:148
+msgid "Start composition from Form Letter Folder"
+msgstr ""
+
+#: alpine/send.c:150
+msgid "Save to Postponed or Form letter folder? "
+msgstr ""
+
+#: alpine/send.c:152
+msgid "Posted message may go to thousands of readers. Really post"
+msgstr ""
+
+#: alpine/send.c:154
+msgid "Deleted messages will be removed from folder after use. Proceed"
+msgstr ""
+
+#: alpine/send.c:505 alpine/send.c:547 alpine/send.c:585 alpine/send.c:853
+msgid "Composition cancelled"
+msgstr ""
+
+#: alpine/send.c:512
+#, c-format
+msgid "Can't open Interrupted mailbox: %s"
+msgstr ""
+
+#: alpine/send.c:593
+msgid "Form letter folder doesn't exist!"
+msgstr ""
+
+#: alpine/send.c:646 alpine/adrbkcmd.c:4561 alpine/reply.c:1754
+#, c-format
+msgid "Composing using role \"%s\""
+msgstr ""
+
+#: alpine/send.c:666 alpine/mailpart.c:1699
+msgid "Problem creating space for message text."
+msgstr ""
+
+#: alpine/send.c:800
+msgid "Empty folder! No messages really postponed!"
+msgstr ""
+
+#: alpine/send.c:802
+msgid "Empty folder! No messages really interrupted!"
+msgstr ""
+
+#: alpine/send.c:840
+msgid "Undelete messages to remain postponed, and then continue message"
+msgstr ""
+
+#: alpine/send.c:842
+msgid "Undelete form letters you want to keep, and then continue message"
+msgstr ""
+
+#: alpine/send.c:891
+#, c-format
+msgid "%s folder unavailable while background posting"
+msgstr ""
+
+#: alpine/send.c:1083 alpine/send.c:2297
+msgid ""
+"Site policy doesn't allow changing From address so role's From has no effect"
+msgstr ""
+
+#: alpine/send.c:1160
+msgid "FORWARD (as e-mail) to : "
+msgstr ""
+
+#: alpine/send.c:1161
+msgid "BOUNCE (redirect) message to : "
+msgstr ""
+
+#. TRANSLATORS: several possible key labels follow
+#: alpine/send.c:1371 alpine/send.c:4783
+msgid "Normal"
+msgstr ""
+
+#: alpine/send.c:1371 alpine/send.c:4783
+msgid "Verbose"
+msgstr ""
+
+#: alpine/send.c:1377 alpine/send.c:4796
+msgid "NoDelay"
+msgstr ""
+
+#: alpine/send.c:1377 alpine/send.c:4796
+msgid "Delay"
+msgstr ""
+
+#: alpine/send.c:1381 alpine/send.c:4799
+msgid "NoSuccess"
+msgstr ""
+
+#: alpine/send.c:1381 alpine/send.c:4799
+msgid "Success"
+msgstr ""
+
+#: alpine/send.c:1385 alpine/send.c:4802
+msgid "ErrRets"
+msgstr ""
+
+#: alpine/send.c:1385 alpine/send.c:4802
+msgid "NoErrRets"
+msgstr ""
+
+#: alpine/send.c:1389 alpine/send.c:4805
+msgid "RetHdrs"
+msgstr ""
+
+#: alpine/send.c:1389 alpine/send.c:4805
+msgid "RetFull"
+msgstr ""
+
+#: alpine/send.c:1439
+#, c-format
+msgid "DSN requested[%s%s%s%s]"
+msgstr ""
+
+#: alpine/send.c:1441
+msgid "Never"
+msgstr ""
+
+#: alpine/send.c:1503 alpine/send.c:3615
+msgid "No recipients specified!"
+msgstr ""
+
+#: alpine/send.c:1513
+msgid "Use of a role-defined smtp-server is administratively prohibited"
+msgstr ""
+
+#: alpine/send.c:1528 alpine/send.c:1571
+msgid "Message sent"
+msgstr ""
+
+#: alpine/send.c:1553 alpine/send.c:3855 alpine/send.c:3949
+msgid "Fcc Failed!. No message saved."
+msgstr ""
+
+#: alpine/send.c:1600 alpine/send.c:1628
+msgid "Send cancelled"
+msgstr ""
+
+#: alpine/send.c:1606
+#, c-format
+msgid "Error in address: %s"
+msgstr ""
+
+#: alpine/send.c:1619
+msgid "No addressee! No e-mail sent."
+msgstr ""
+
+#: alpine/send.c:1880
+#, c-format
+msgid "Post to current newsgroup (%s)"
+msgstr ""
+
+#: alpine/send.c:1923
+msgid "Message cancelled"
+msgstr ""
+
+#: alpine/send.c:2714
+msgid "Not allowed to change header \"From\""
+msgstr ""
+
+#: alpine/send.c:3113
+msgid "Not allowed to postpone message until addresses are qualified"
+msgstr ""
+
+#: alpine/send.c:3216 pith/send.c:2168
+msgid "No postponed file defined"
+msgstr ""
+
+#: alpine/send.c:3559
+msgid "Composition saved to Form Letter Folder. Select Compose to send."
+msgstr ""
+
+#: alpine/send.c:3562
+msgid "Composition postponed. Select Compose to resume."
+msgstr ""
+
+#: alpine/send.c:3573
+#, c-format
+msgid "Message cancelled%s%s%s"
+msgstr ""
+
+#: alpine/send.c:3581
+msgid "Continuing composition. Message not postponed or sent"
+msgstr ""
+
+#: alpine/send.c:3598
+msgid "Message not posted"
+msgstr ""
+
+#: alpine/send.c:3607
+msgid "No recipients, really copy only to Fcc "
+msgstr ""
+
+#: alpine/send.c:3631
+msgid "No To, Cc, or Newsgroup specified, send anyway "
+msgstr ""
+
+#: alpine/send.c:3714
+#, c-format
+msgid "Problem filtering! Nothing sent%s."
+msgstr ""
+
+#: alpine/send.c:3878
+msgid "Message handed off for posting"
+msgstr ""
+
+#: alpine/send.c:4126
+msgid "Postponed Folder"
+msgstr ""
+
+#: alpine/send.c:4127
+msgid "Form Letter Folder"
+msgstr ""
+
+#: alpine/send.c:4524
+msgid "Prev Filter"
+msgstr ""
+
+#: alpine/send.c:4529
+msgid "Next Filter"
+msgstr ""
+
+#: alpine/send.c:4572
+msgid "Priority"
+msgstr ""
+
+#: alpine/send.c:4600
+msgid "DSNOpts"
+msgstr ""
+
+#: alpine/send.c:4780
+msgid "NoFlow"
+msgstr ""
+
+#: alpine/send.c:4780
+msgid "Flow"
+msgstr ""
+
+#: alpine/send.c:4787
+msgid "Foreground"
+msgstr ""
+
+#: alpine/send.c:4787
+msgid "Background"
+msgstr ""
+
+#: alpine/send.c:4790
+msgid "Fcc Attchmnts"
+msgstr ""
+
+#: alpine/send.c:4791
+msgid "No Fcc Atmts "
+msgstr ""
+
+#: alpine/send.c:4838
+msgid "No Message Sent"
+msgstr ""
+
+#: alpine/send.c:4842
+msgid "Send Cancelled"
+msgstr ""
+
+#: alpine/send.c:5004
+msgid "NONE - No X-Priority header included"
+msgstr ""
+
+#. TRANSLATORS: SELECT A PRIORITY is a screen title
+#. TRANSLATORS: Print something1 using something2.
+#. "priorities" is something1
+#: alpine/send.c:5012
+msgid "SELECT A PRIORITY"
+msgstr ""
+
+#: alpine/send.c:5013
+msgid "priorities"
+msgstr ""
+
+#: alpine/send.c:5014
+msgid "HELP FOR SELECTING A PRIORITY"
+msgstr ""
+
+#: alpine/send.c:5018 alpine/context.c:754
+msgid "No change"
+msgstr ""
+
+#: alpine/send.c:5066
+#, c-format
+msgid "File %s attached as type %s/%s"
+msgstr ""
+
+#: alpine/send.c:5137 alpine/mailpart.c:3752
+msgid "Error opening pipe"
+msgstr ""
+
+#: alpine/send.c:5152
+msgid ""
+"Cancel message (answering \"Confirm\" will abandon your mail message) ? "
+msgstr ""
+
+#: alpine/send.c:5155
+msgid "Confirm"
+msgstr ""
+
+#: alpine/send.c:5172
+msgid " Type \"C\" to cancel message "
+msgstr ""
+
+#: alpine/send.c:5430
+#, c-format
+msgid "Problem filtering: %s"
+msgstr ""
+
+#: alpine/send.c:6014
+#, c-format
+msgid "Error \"%s\", couldn't attach file \"%s\""
+msgstr ""
+
+#: alpine/send.c:6219
+msgid "Can't post while posting!"
+msgstr ""
+
+#: alpine/send.c:6251
+msgid "News postings MUST have a subject! Please add one!"
+msgstr ""
+
+#: alpine/send.c:6941
+#, c-format
+msgid "User-id for From address : "
+msgstr ""
+
+#: alpine/send.c:6963
+msgid "Send cancelled (User-id must be provided before sending)"
+msgstr ""
+
+#: alpine/send.c:6968
+#, c-format
+msgid "Preserve %.*s as \"user-id\" in PINERC"
+msgstr ""
+
+#: alpine/send.c:6986
+#, c-format
+msgid "Personal name for From address : "
+msgstr ""
+
+#: alpine/send.c:7007
+#, c-format
+msgid "Preserve %.*s as \"personal-name\" in PINERC"
+msgstr ""
+
+#: alpine/send.c:7038
+#, c-format
+msgid "Host/domain for From address : "
+msgstr ""
+
+#: alpine/send.c:7060
+msgid "Send cancelled (Host/domain name must be provided before sending)"
+msgstr ""
+
+#: alpine/send.c:7065
+#, c-format
+msgid "Preserve %.*s as \"user-domain\" in PINERC"
+msgstr ""
+
+#: alpine/send.c:7098
+#, c-format
+msgid "SMTP server to forward message : "
+msgstr ""
+
+#: alpine/send.c:7120
+msgid "Send cancelled (SMTP server must be provided before sending)"
+msgstr ""
+
+#. TRANSLATORS: This is the name of the command that will show
+#. which other commands are available. 12 commands are shown at
+#. the bottom of the screen, this command would show the next set
+#. of 12
+#: alpine/radio.c:817 alpine/addrbook.c:2560
+msgid "OTHER CMDS"
+msgstr ""
+
+#: alpine/mailcmd.c:123
+msgid "ALTER message selection : "
+msgstr ""
+
+#. TRANSLATORS: these are keymenu names for selecting. Broaden selection means
+#. we will add more messages to the selection, Narrow selection means we will
+#. remove some selections (like a logical AND instead of logical OR), and Flip
+#. Selected means that all the messages that are currently selected become unselected,
+#. and all the unselected messages become selected.
+#: alpine/mailcmd.c:130
+msgid "unselect All"
+msgstr ""
+
+#: alpine/mailcmd.c:132
+msgid "Broaden selctn"
+msgstr ""
+
+#: alpine/mailcmd.c:133
+msgid "Narrow selctn"
+msgstr ""
+
+#: alpine/mailcmd.c:134
+msgid "Flip selected"
+msgstr ""
+
+#. TRANSLATORS: very short descriptions of message selection criteria. Select Cur
+#. means select the currently highlighted message; select by Number is by message
+#. number; Status is by status of the message, for example the message might be
+#. New or it might be Unseen or marked Important; Size has the Z upper case because
+#. it is a Z command; Keyword is an alpine keyword that has been set by the user;
+#. and Rule is an alpine rule
+#. TRANSLATORS: keymenu descriptions, select all folders, current folder, select
+#. based on folder properties, or select based on text contents in folders
+#: alpine/mailcmd.c:150 alpine/mailcmd.c:189 alpine/folder.c:3112
+msgid "select All"
+msgstr ""
+
+#: alpine/mailcmd.c:151 alpine/mailcmd.c:6615 alpine/folder.c:3113
+#: alpine/folder.c:3127
+msgid "select Cur"
+msgstr ""
+
+#: alpine/mailcmd.c:152 alpine/mailcmd.c:192
+msgid "Number"
+msgstr ""
+
+#: alpine/mailcmd.c:153 alpine/mailcmd.c:193
+msgid "Date"
+msgstr ""
+
+#: alpine/mailcmd.c:155 alpine/mailcmd.c:195
+msgid "Status"
+msgstr ""
+
+#: alpine/mailcmd.c:156 alpine/mailcmd.c:196
+msgid "siZe"
+msgstr ""
+
+#: alpine/mailcmd.c:157 alpine/mailcmd.c:197
+msgid "Keyword"
+msgstr ""
+
+#: alpine/mailcmd.c:158 alpine/mailcmd.c:198
+msgid "Rule"
+msgstr ""
+
+#: alpine/mailcmd.c:159 alpine/mailcmd.c:199
+msgid "tHread"
+msgstr ""
+
+#. TRANSLATORS: these are operations we can do on a set of selected messages.
+#. Del is Delete; Undel is Undelete; TakeAddr means to Take some Addresses into
+#. the address book; Save means to save the messages into another alpine folder;
+#. Export means to copy the messages to a file outside of alpine, external to
+#. alpine's world.
+#: alpine/mailcmd.c:170
+msgid "Del"
+msgstr ""
+
+#: alpine/mailcmd.c:171
+msgid "Undel"
+msgstr ""
+
+#: alpine/mailcmd.c:172 alpine/mailcmd.c:261 alpine/mailcmd.c:2576
+#: alpine/reply.c:832 alpine/reply.c:1127
+msgid "Reply"
+msgstr ""
+
+#: alpine/mailcmd.c:173 alpine/mailcmd.c:262 alpine/mailcmd.c:2575
+#: alpine/folder.c:6463 alpine/reply.c:834
+msgid "Forward"
+msgstr ""
+
+#: alpine/mailcmd.c:175
+msgid "TakeAddr"
+msgstr ""
+
+#. TRANSLATORS: select currrently highlighted message Thread
+#: alpine/mailcmd.c:191 alpine/mailcmd.c:6610
+msgid "select Curthrd"
+msgstr ""
+
+#: alpine/mailcmd.c:205
+msgid "Select New, Deleted, Answered, Forwarded, or Important messages ? "
+msgstr ""
+
+#: alpine/mailcmd.c:207
+msgid ""
+"Select NOT New, NOT Deleted, NOT Answered, NOT Forwarded or NOT Important "
+"msgs ? "
+msgstr ""
+
+#. TRANSLATORS: When selecting messages by message Status these are the
+#. different types of Status you can select on. Is the message New, Recent,
+#. and so on. Not means flip the meaning of the selection to the opposite
+#. thing, so message is not New or not Important.
+#. TRANSLATORS: these are types of flags (markers) that the user can
+#. set. For example, they can flag the message as an important message.
+#: alpine/mailcmd.c:213 alpine/mailcmd.c:295 alpine/mailcmd.c:1648
+#: alpine/mailcmd.c:9323
+msgid "New"
+msgstr ""
+
+#: alpine/mailcmd.c:214 alpine/mailcmd.c:296 alpine/mailcmd.c:1647
+#: alpine/mailcmd.c:9322
+msgid "Important"
+msgstr ""
+
+#: alpine/mailcmd.c:215 alpine/mailcmd.c:297 alpine/mailcmd.c:1651
+#: alpine/mailcmd.c:9325 alpine/confscroll.c:2741 alpine/confscroll.c:2968
+#: alpine/colorconf.c:1224
+msgid "Deleted"
+msgstr ""
+
+#: alpine/mailcmd.c:216 alpine/mailcmd.c:298 alpine/mailcmd.c:1649
+#: alpine/mailcmd.c:9324
+msgid "Answered"
+msgstr ""
+
+#: alpine/mailcmd.c:217 alpine/mailcmd.c:299 alpine/mailcmd.c:1650
+msgid "Forwarded"
+msgstr ""
+
+#: alpine/mailcmd.c:219 alpine/mailcmd.c:252 alpine/mailcmd.c:288
+#: alpine/mailcmd.c:300
+msgid "Not"
+msgstr ""
+
+#: alpine/mailcmd.c:221 alpine/folder.c:3169
+msgid "Recent"
+msgstr ""
+
+#: alpine/mailcmd.c:222 alpine/folder.c:3175
+msgid "Unseen"
+msgstr ""
+
+#. TRANSLATORS: options when selecting messages by Date
+#: alpine/mailcmd.c:230
+msgid "Prev Day"
+msgstr ""
+
+#: alpine/mailcmd.c:231
+msgid "Next Day"
+msgstr ""
+
+#: alpine/mailcmd.c:232
+msgid "Cur Msg"
+msgstr ""
+
+#: alpine/mailcmd.c:233
+msgid "Toggle When"
+msgstr ""
+
+#: alpine/mailcmd.c:241
+msgid ""
+"Select based on To, From, Cc, Recip, Partic, Subject fields or All msg "
+"text ? "
+msgstr ""
+
+#: alpine/mailcmd.c:243
+msgid ""
+"Select based on NOT To, From, Cc, Recip, Partic, Subject or All msg text ? "
+msgstr ""
+
+#. TRANSLATORS: Select messages based on the text contained in the From line, or
+#. the Subject line, and so on.
+#: alpine/mailcmd.c:247
+msgid "From"
+msgstr ""
+
+#: alpine/mailcmd.c:248
+msgid "Subject"
+msgstr ""
+
+#: alpine/mailcmd.c:249
+msgid "To"
+msgstr ""
+
+#: alpine/mailcmd.c:250
+msgid "All Text"
+msgstr ""
+
+#: alpine/mailcmd.c:251
+msgid "Cc"
+msgstr ""
+
+#: alpine/mailcmd.c:253
+msgid "Recipient"
+msgstr ""
+
+#: alpine/mailcmd.c:254
+msgid "Participant"
+msgstr ""
+
+#: alpine/mailcmd.c:255
+msgid "Body"
+msgstr ""
+
+#: alpine/mailcmd.c:260 alpine/reply.c:836
+msgid "Compose"
+msgstr ""
+
+#: alpine/mailcmd.c:263 alpine/mailcmd.c:2577 alpine/mailcmd.c:7034
+msgid "Bounce"
+msgstr ""
+
+#: alpine/mailcmd.c:268
+msgid "Enter comma-delimited list of numbers (dash between ranges): "
+msgstr ""
+
+#: alpine/mailcmd.c:271
+msgid "Select messages with size larger than: "
+msgstr ""
+
+#: alpine/mailcmd.c:274
+msgid "Select messages with size smaller than: "
+msgstr ""
+
+#: alpine/mailcmd.c:276
+msgid "Larger"
+msgstr ""
+
+#: alpine/mailcmd.c:277
+msgid "Smaller"
+msgstr ""
+
+#: alpine/mailcmd.c:286
+msgid "To List"
+msgstr ""
+
+#: alpine/mailcmd.c:301
+msgid "To Flag Details"
+msgstr ""
+
+#: alpine/mailcmd.c:358
+msgid "HELP FOR MESSAGE INDEX"
+msgstr ""
+
+#: alpine/mailcmd.c:360
+msgid "HELP FOR MESSAGE TEXT"
+msgstr ""
+
+#: alpine/mailcmd.c:361
+msgid "HELP FOR THREAD INDEX"
+msgstr ""
+
+#: alpine/mailcmd.c:409 alpine/mailcmd.c:475
+#, c-format
+msgid "Already on first %s in Zoomed Index"
+msgstr ""
+
+#: alpine/mailcmd.c:410 alpine/mailcmd.c:476 alpine/mailcmd.c:490
+msgid "thread"
+msgstr ""
+
+#: alpine/mailcmd.c:410 alpine/mailcmd.c:476 alpine/mailcmd.c:490
+#: alpine/mailcmd.c:1154
+msgid "message"
+msgstr ""
+
+#: alpine/mailcmd.c:416
+msgid "View previous thread"
+msgstr ""
+
+#: alpine/mailcmd.c:421
+msgid "Viewing previous thread"
+msgstr ""
+
+#: alpine/mailcmd.c:489
+#, c-format
+msgid "Already on first %s"
+msgstr ""
+
+#: alpine/mailcmd.c:525
+msgid "View next thread"
+msgstr ""
+
+#: alpine/mailcmd.c:530
+msgid "Viewing next thread"
+msgstr ""
+
+#: alpine/mailcmd.c:587
+msgid "Expand collapsed thread to see more messages"
+msgstr ""
+
+#: alpine/mailcmd.c:605
+msgid ". Press TAB for next folder."
+msgstr ""
+
+#: alpine/mailcmd.c:608
+msgid ". No more folders to TAB to."
+msgstr ""
+
+#: alpine/mailcmd.c:806 alpine/mailcmd.c:1092
+msgid "NextNew"
+msgstr ""
+
+#: alpine/mailcmd.c:811
+#, c-format
+msgid "View thread number %s? "
+msgstr ""
+
+#: alpine/mailcmd.c:815
+#, c-format
+msgid "View message in thread number %s? "
+msgstr ""
+
+#: alpine/mailcmd.c:979
+msgid "To Inbox"
+msgstr ""
+
+#: alpine/mailcmd.c:989
+#, c-format
+msgid "No more incoming folders. Return to \"%s\"? "
+msgstr ""
+
+#: alpine/mailcmd.c:991
+#, c-format
+msgid "No more news groups. Return to \"%s\"? "
+msgstr ""
+
+#: alpine/mailcmd.c:1015
+msgid "No more incoming folders"
+msgstr ""
+
+#: alpine/mailcmd.c:1017
+msgid "No more news groups"
+msgstr ""
+
+#: alpine/mailcmd.c:1145 alpine/mailindx.c:2914
+msgid "Index Zoom Mode is now off"
+msgstr ""
+
+#: alpine/mailcmd.c:1151
+#, c-format
+msgid "In Zoomed Index of %s%s%s%s. Use \"Z\" to restore regular Index"
+msgstr ""
+
+#: alpine/mailcmd.c:1154
+msgid "threads"
+msgstr ""
+
+#: alpine/mailcmd.c:1159
+msgid "All messages selected, so not entering Index Zoom Mode"
+msgstr ""
+
+#: alpine/mailcmd.c:1213
+msgid "Unexclude not available for mail folders"
+msgstr ""
+
+#: alpine/mailcmd.c:1413 alpine/mailpart.c:521 alpine/mailpart.c:2608
+msgid "Display of full headers is now off."
+msgstr ""
+
+#: alpine/mailcmd.c:1418 alpine/mailpart.c:526 alpine/mailpart.c:2613
+#, c-format
+msgid "Quotes displayed, use %s to see full headers"
+msgstr ""
+
+#: alpine/mailcmd.c:1424 alpine/mailpart.c:532 alpine/mailpart.c:2619
+msgid "Display of full headers is now on."
+msgstr ""
+
+#: alpine/mailcmd.c:1633
+msgid " Set desired flags for current message below. An 'X' means set"
+msgstr ""
+
+#: alpine/mailcmd.c:1634
+msgid " it, and a ' ' means to unset it. Choose \"Exit\" when finished."
+msgstr ""
+
+#: alpine/mailcmd.c:1639
+msgid " Set desired flags below for selected messages. A '?' means to"
+msgstr ""
+
+#: alpine/mailcmd.c:1640
+msgid " leave the flag unchanged, 'X' means to set it, and a ' ' means"
+msgstr ""
+
+#: alpine/mailcmd.c:1641
+msgid " to unset it. Use the \"Return\" key to toggle, and choose"
+msgstr ""
+
+#: alpine/mailcmd.c:1642
+msgid " \"Exit\" when finished."
+msgstr ""
+
+#: alpine/mailcmd.c:1727 alpine/mailcmd.c:1729
+msgid "User-defined Keywords from Setup/Config"
+msgstr ""
+
+#: alpine/mailcmd.c:1762 alpine/mailcmd.c:1764
+msgid "Other keywords in the mailbox that are not user-defined"
+msgstr ""
+
+#: alpine/mailcmd.c:1838
+msgid "Error accessing message data"
+msgstr ""
+
+#: alpine/mailcmd.c:2064
+msgid "No more keywords allowed in this folder!"
+msgstr ""
+
+#: alpine/mailcmd.c:2067
+msgid "Cannot add keywords for this folder so cannot set Forwarded flag"
+msgstr ""
+
+#: alpine/mailcmd.c:2070
+msgid "Cannot add keywords for this folder"
+msgstr ""
+
+#: alpine/mailcmd.c:2105
+msgid "No flags changed."
+msgstr ""
+
+#: alpine/mailcmd.c:2128
+msgid "Flag New, Deleted, Answered, Forwarded or Important ? "
+msgstr ""
+
+#: alpine/mailcmd.c:2130
+msgid "Flag New, Deleted, Answered, Forwarded, Important or Keyword initial ? "
+msgstr ""
+
+#: alpine/mailcmd.c:2132
+msgid "Flag !New, !Deleted, !Answered, !Forwarded, or !Important ? "
+msgstr ""
+
+#: alpine/mailcmd.c:2134
+msgid ""
+"Flag !New, !Deleted, !Answered, !Forwarded, !Important or !Keyword initial ? "
+msgstr ""
+
+#: alpine/mailcmd.c:2381
+msgid "Saved copy will NOT include entire message! Continue"
+msgstr ""
+
+#: alpine/mailcmd.c:2398
+msgid "Can't save message. Error accessing folder"
+msgstr ""
+
+#: alpine/mailcmd.c:2428
+msgid "Saving"
+msgstr ""
+
+#: alpine/mailcmd.c:2546
+msgid "Compose, Forward, Reply, or Bounce? "
+msgstr ""
+
+#: alpine/mailcmd.c:2552 alpine/roleconf.c:143
+msgid "No roles available. Use Setup/Rules to add roles."
+msgstr ""
+
+#: alpine/mailcmd.c:2577
+msgid "Composition"
+msgstr ""
+
+#. TRANSLATORS: command means go to Folders list
+#. TRANSLATORS: To Folders
+#: alpine/mailcmd.c:2680 alpine/adrbkcmd.c:854
+msgid "To Fldrs"
+msgstr ""
+
+#: alpine/mailcmd.c:2686 alpine/mailcmd.c:5320
+msgid "Prev Collection"
+msgstr ""
+
+#: alpine/mailcmd.c:2691 alpine/mailcmd.c:5325
+msgid "Next Collection"
+msgstr ""
+
+#. TRANSLATORS: list all the matches
+#: alpine/mailcmd.c:2707 alpine/mailcmd.c:3482 alpine/mailcmd.c:5344
+msgid "ListMatches"
+msgstr ""
+
+#: alpine/mailcmd.c:2829 alpine/mailcmd.c:5457
+msgid "Error reading folder name"
+msgstr ""
+
+#: alpine/mailcmd.c:2860 alpine/mailcmd.c:5532 alpine/mailcmd.c:5543
+#, c-format
+msgid "Problem accessing folder \"%s\""
+msgstr ""
+
+#: alpine/mailcmd.c:2909 alpine/folder.c:2586
+#, c-format
+msgid "\"%s\" is a directory"
+msgstr ""
+
+#: alpine/mailcmd.c:2955
+msgid "HELP FOR SAVE"
+msgstr ""
+
+#: alpine/mailcmd.c:3101
+#, c-format
+msgid "\"%.15s%s\" doesn't exist - Add it in FOLDER LIST screen"
+msgstr ""
+
+#: alpine/mailcmd.c:3108
+#, c-format
+msgid "Folder \"%.15s%s\" in <%.15s%s> doesn't exist. Create"
+msgstr ""
+
+#: alpine/mailcmd.c:3115
+#, c-format
+msgid "Folder \"%.40s%s\" doesn't exist. Create"
+msgstr ""
+
+#: alpine/mailcmd.c:3194
+msgid "Can't expunge. Folder is read-only"
+msgstr ""
+
+#: alpine/mailcmd.c:3243
+msgid "Expunging"
+msgstr ""
+
+#: alpine/mailcmd.c:3275
+#, c-format
+msgid "No messages expunged from folder \"%s\""
+msgstr ""
+
+#: alpine/mailcmd.c:3279
+msgid "No messages marked deleted. No messages expunged."
+msgstr ""
+
+#: alpine/mailcmd.c:3457 alpine/mailcmd.c:3876 alpine/adrbkcmd.c:4173
+#: alpine/folder.c:3561 alpine/folder.c:3696 alpine/mailpart.c:1297
+#: alpine/mailpart.c:1724 alpine/mailpart.c:1803 pico/attach.c:68
+#: pico/file.c:131 pico/file.c:576
+msgid "To Files"
+msgstr ""
+
+#. TRANSLATORS: this is an abbreviation for Download Messages
+#: alpine/mailcmd.c:3465 alpine/mailpart.c:1315
+msgid "Downld Msg"
+msgstr ""
+
+#: alpine/mailcmd.c:3535 alpine/mailcmd.c:3971 alpine/adrbkcmd.c:4259
+#: alpine/folder.c:3593 alpine/mailpart.c:1752 alpine/mailpart.c:1829
+#, c-format
+msgid "Can't export to file outside of %s"
+msgstr ""
+
+#: alpine/mailcmd.c:3596
+msgid "Error running download command"
+msgstr ""
+
+#: alpine/mailcmd.c:3609
+msgid "Download Command Completed"
+msgstr ""
+
+#. TRANSLATORS: error opening file "<filename>" to export message: <error text>
+#. TRANSLATORS: Error opening file <filename> to export message: <error text>
+#: alpine/mailcmd.c:3627 alpine/mailpart.c:1781
+#, c-format
+msgid "Error opening file \"%s\" to export message: %s"
+msgstr ""
+
+#: alpine/mailcmd.c:3639
+msgid "Can't export message. Error accessing mail folder"
+msgstr ""
+
+#. TRANSLATORS: Error exporting to <filename>: <error text>
+#: alpine/mailcmd.c:3680 alpine/adrbkcmd.c:4464
+#, c-format
+msgid "Error exporting to \"%s\" : %s"
+msgstr ""
+
+#: alpine/mailcmd.c:3706
+#, c-format
+msgid "Can't save attachments, filename too long: %s"
+msgstr ""
+
+#: alpine/mailcmd.c:3747
+msgid "Problem saving attachments"
+msgstr ""
+
+#: alpine/mailcmd.c:3754
+#, c-format
+msgid "Problem saving attachments: %s: %s"
+msgstr ""
+
+#: alpine/mailcmd.c:3765
+msgid "Problem reading message"
+msgstr ""
+
+#: alpine/mailcmd.c:3816
+msgid "Problems saving attachments"
+msgstr ""
+
+#. TRANSLATORS: Saved <how many> attachements to <directory name>
+#: alpine/mailcmd.c:3822
+#, c-format
+msgid "Saved %s attachments to %s"
+msgstr ""
+
+#: alpine/mailcmd.c:3825
+msgid "No attachments to save"
+msgstr ""
+
+#. TRANSLATORS: Problem saving to <filename>: <error text>
+#: alpine/mailcmd.c:3924 alpine/mailcmd.c:3934
+#, c-format
+msgid "Problem saving to \"%s\": %s"
+msgstr ""
+
+#: alpine/mailcmd.c:3941
+#, c-format
+msgid "Error opening file \"%s\" for export: %s"
+msgstr ""
+
+#. TRANSLATORS: Export all attachment parts
+#: alpine/mailcmd.c:4073 alpine/mailcmd.c:4498
+msgid "AllParts"
+msgstr ""
+
+#: alpine/mailcmd.c:4270
+msgid "HELP FOR IMPORT FILE SELECT"
+msgstr ""
+
+#: alpine/mailcmd.c:4272 alpine/mailcmd.c:4274
+msgid "HELP FOR EXPORT FILE SELECT"
+msgstr ""
+
+#: alpine/mailcmd.c:4503
+msgid "NoAllParts"
+msgstr ""
+
+#: alpine/mailcmd.c:4576
+#, c-format
+msgid "Error expanding file name: \"%s\" unknown user"
+msgstr ""
+
+#. TRANSLATORS: asking user if they want to overwrite (replace contents of)
+#. a file or append to the end of the file
+#: alpine/mailcmd.c:4628
+msgid "Overwrite"
+msgstr ""
+
+#: alpine/mailcmd.c:4629
+msgid "Append"
+msgstr ""
+
+#. TRANSLATORS: asking user whether to overwrite a file or not,
+#. File <filename> already exists. Overwrite it ?
+#: alpine/mailcmd.c:4639
+#, c-format
+msgid "File \"%s%s\" already exists. Overwrite it "
+msgstr ""
+
+#. TRANSLATORS: Cannot remove old <filename>: <error text>
+#: alpine/mailcmd.c:4650
+#, c-format
+msgid "Cannot remove old %s: %s"
+msgstr ""
+
+#. TRANSLATORS: File <filename> already exists. Overwrite or append to it ?
+#: alpine/mailcmd.c:4663
+#, c-format
+msgid "File \"%s%s\" already exists. Overwrite or append to it ? "
+msgstr ""
+
+#. TRANSLATORS: Warning: Cannot truncate old <filename>: <error text>
+#: alpine/mailcmd.c:4677
+#, c-format
+msgid "Warning: Cannot truncate old %s: %s"
+msgstr ""
+
+#. TRANSLATORS: go to First Message
+#: alpine/mailcmd.c:4818 alpine/mailindx.c:2946
+msgid "First Msg"
+msgstr ""
+
+#: alpine/mailcmd.c:4819 alpine/mailindx.c:2947
+msgid "Last Msg"
+msgstr ""
+
+#: alpine/mailcmd.c:4902 alpine/mailcmd.c:5160
+msgid "Invalid number entered. Use only digits 0-9"
+msgstr ""
+
+#: alpine/mailcmd.c:5167
+msgid "Number should be >= 0"
+msgstr ""
+
+#: alpine/mailcmd.c:5170
+#, c-format
+msgid "Maximum is %s"
+msgstr ""
+
+#: alpine/mailcmd.c:5331
+msgid "INBOX"
+msgstr ""
+
+#: alpine/mailcmd.c:5547
+#, c-format
+msgid "Can't find Incoming Folder: %s"
+msgstr ""
+
+#: alpine/mailcmd.c:5551
+#, c-format
+msgid "Can't find folder \"%s\" in %s"
+msgstr ""
+
+#: alpine/mailcmd.c:5555
+#, c-format
+msgid "Can't find folder \"%s\""
+msgstr ""
+
+#. TRANSLATORS: This is a question, Print Index ?
+#: alpine/mailcmd.c:5898
+msgid "Print Index"
+msgstr ""
+
+#: alpine/mailcmd.c:5961
+msgid "Error printing folder index"
+msgstr ""
+
+#: alpine/mailcmd.c:5990
+msgid "Error printing message"
+msgstr ""
+
+#: alpine/mailcmd.c:6120
+msgid "Shown Text"
+msgstr ""
+
+#: alpine/mailcmd.c:6120
+msgid "Raw Text"
+msgstr ""
+
+#: alpine/mailcmd.c:6121
+msgid "Free Output"
+msgstr ""
+
+#: alpine/mailcmd.c:6121
+msgid "Capture Output"
+msgstr ""
+
+#: alpine/mailcmd.c:6122
+msgid "No Delimiter"
+msgstr ""
+
+#: alpine/mailcmd.c:6122
+msgid "With Delimiter"
+msgstr ""
+
+#: alpine/mailcmd.c:6124
+msgid "To Same Pipe"
+msgstr ""
+
+#: alpine/mailcmd.c:6124
+msgid "To Individual Pipes"
+msgstr ""
+
+#: alpine/mailcmd.c:6150
+msgid "Internal problem encountered"
+msgstr ""
+
+#: alpine/mailcmd.c:6273
+#, c-format
+msgid "Internal Error: %s"
+msgstr ""
+
+#: alpine/mailcmd.c:6296
+msgid "Error piping message"
+msgstr ""
+
+#: alpine/mailcmd.c:6306
+msgid "Pipe command completed"
+msgstr ""
+
+#: alpine/mailcmd.c:6320
+msgid "HELP FOR PIPE COMMAND"
+msgstr ""
+
+#: alpine/mailcmd.c:6390
+msgid " has information associated with it "
+msgstr ""
+
+#: alpine/mailcmd.c:6391
+msgid "that explains how to participate in an email list. An "
+msgstr ""
+
+#: alpine/mailcmd.c:6392
+msgid "email list is represented by a single email address that "
+msgstr ""
+
+#: alpine/mailcmd.c:6393
+msgid "users sharing a common interest can send messages to (known "
+msgstr ""
+
+#: alpine/mailcmd.c:6394
+msgid "as posting) which are then redistributed to all members "
+msgstr ""
+
+#: alpine/mailcmd.c:6395
+msgid "of the list (sometimes after review by a moderator)."
+msgstr ""
+
+#: alpine/mailcmd.c:6396
+msgid "<P>List participation commands in this message include:"
+msgstr ""
+
+#: alpine/mailcmd.c:6441
+msgid "With the provided comment:<P><BLOCKQUOTE>"
+msgstr ""
+
+#: alpine/mailcmd.c:6450
+msgid "Posting is <EM>not</EM> allowed on this list"
+msgstr ""
+
+#: alpine/mailcmd.c:6534
+msgid "MAIL LIST COMMANDS"
+msgstr ""
+
+#: alpine/mailcmd.c:6538
+msgid "HELP FOR LIST COMMANDS"
+msgstr ""
+
+#: alpine/mailcmd.c:6610
+msgid "unselect Curthrd"
+msgstr ""
+
+#: alpine/mailcmd.c:6615 alpine/folder.c:3127
+msgid "unselect Cur"
+msgstr ""
+
+#: alpine/mailcmd.c:6660
+msgid "HELP FOR SELECT"
+msgstr ""
+
+#: alpine/mailcmd.c:6835
+msgid "No change resulted. Matching messages already selected."
+msgstr ""
+
+#: alpine/mailcmd.c:6838
+#, c-format
+msgid "Select failed. No %smessages selected."
+msgstr ""
+
+#: alpine/mailcmd.c:6839
+msgid "additional "
+msgstr ""
+
+#: alpine/mailcmd.c:6854
+#, c-format
+msgid "Select matched %s message%s!"
+msgstr ""
+
+#: alpine/mailcmd.c:7020
+msgid "Flag"
+msgstr ""
+
+#: alpine/mailcmd.c:7027
+msgid "Pipe"
+msgstr ""
+
+#: alpine/mailcmd.c:7042
+msgid "Collapse"
+msgstr ""
+
+#: alpine/mailcmd.c:7043 alpine/addrbook.c:2502 alpine/addrbook.c:2724
+msgid "Expand"
+msgstr ""
+
+#: alpine/mailcmd.c:7050
+msgid "UnSelect"
+msgstr ""
+
+#: alpine/mailcmd.c:7233 alpine/mailcmd.c:7385
+#, c-format
+msgid "Invalid number range, missing number before \"-\": %s"
+msgstr ""
+
+#: alpine/mailcmd.c:7243
+#, c-format
+msgid "Invalid message number: %s"
+msgstr ""
+
+#: alpine/mailcmd.c:7252 alpine/mailcmd.c:7282
+#, c-format
+msgid "\"%s\" out of message number range"
+msgstr ""
+
+#: alpine/mailcmd.c:7272 alpine/mailcmd.c:7425
+#, c-format
+msgid "Invalid number range, missing number after \"-\": %s"
+msgstr ""
+
+#: alpine/mailcmd.c:7293 alpine/mailcmd.c:7446
+#, c-format
+msgid "Invalid reverse message number range: %s-%s"
+msgstr ""
+
+#: alpine/mailcmd.c:7395
+#, c-format
+msgid "Invalid thread number: %s"
+msgstr ""
+
+#: alpine/mailcmd.c:7404 alpine/mailcmd.c:7435
+#, c-format
+msgid "\"%s\" out of thread number range"
+msgstr ""
+
+#: alpine/mailcmd.c:7637 alpine/mailcmd.c:7953 alpine/mailcmd.c:8106
+#: alpine/mailcmd.c:8204 alpine/mailcmd.c:8291 alpine/mailcmd.c:8512
+msgid "Selecting"
+msgstr ""
+
+#: alpine/mailcmd.c:7651
+#, c-format
+msgid "Invalid date entered: %s"
+msgstr ""
+
+#: alpine/mailcmd.c:7683
+msgid "[Match_My_Addresses]"
+msgstr ""
+
+#: alpine/mailcmd.c:7684
+msgid "[Don't_Match_My_Addresses]"
+msgstr ""
+
+#. TRANSLATORS: use Current To Address
+#: alpine/mailcmd.c:7723
+msgid "Cur To"
+msgstr ""
+
+#. TRANSLATORS: use Current From Address
+#: alpine/mailcmd.c:7728
+msgid "Cur From"
+msgstr ""
+
+#. TRANSLATORS: use Current Cc Address
+#: alpine/mailcmd.c:7733
+msgid "Cur Cc"
+msgstr ""
+
+#. TRANSLATORS: Match Me means match my address
+#: alpine/mailcmd.c:7738
+msgid "Match Me"
+msgstr ""
+
+#. TRANSLATORS: use Current Subject
+#: alpine/mailcmd.c:7751
+msgid "Cur Subject"
+msgstr ""
+
+#: alpine/mailcmd.c:8028 alpine/mailcmd.c:8047
+#, c-format
+msgid "Invalid size entered: %s"
+msgstr ""
+
+#: alpine/mailcmd.c:8189
+msgid "HELP FOR SELECT BASED ON STATUS"
+msgstr ""
+
+#: alpine/mailcmd.c:8242
+msgid "Rule to NOT match: "
+msgstr ""
+
+#: alpine/mailcmd.c:8243
+msgid "Rule to match: "
+msgstr ""
+
+#: alpine/mailcmd.c:8260
+msgid "HELP FOR SELECT BY RULE"
+msgstr ""
+
+#: alpine/mailcmd.c:8325
+msgid "No rules available. Use Setup/Rules to add some."
+msgstr ""
+
+#: alpine/mailcmd.c:8337
+msgid "No rules defined, use Setup/Rules"
+msgstr ""
+
+#. TRANSLATORS: SELECT A RULE is a screen title
+#. TRANSLATORS: Print something1 using something2.
+#. "rules" is something1
+#: alpine/mailcmd.c:8351
+msgid "SELECT A RULE"
+msgstr ""
+
+#. TRANSLATORS: Print something1 using something2.
+#. "rules" is something1
+#: alpine/mailcmd.c:8352 alpine/roleconf.c:376
+msgid "rules"
+msgstr ""
+
+#: alpine/mailcmd.c:8353
+msgid "HELP FOR SELECTING A RULE NICKNAME"
+msgstr ""
+
+#: alpine/mailcmd.c:8436
+msgid "Keyword (or keyword initial) to NOT match: "
+msgstr ""
+
+#: alpine/mailcmd.c:8438
+msgid "Keyword (or keyword initial) to match: "
+msgstr ""
+
+#: alpine/mailcmd.c:8442
+msgid "Keyword to NOT match: "
+msgstr ""
+
+#: alpine/mailcmd.c:8444
+msgid "Keyword to match: "
+msgstr ""
+
+#: alpine/mailcmd.c:8549
+msgid "No keywords defined, use \"keywords\" option in Setup/Config"
+msgstr ""
+
+#. TRANSLATORS: SELECT A KEYWORD is a screen title
+#. TRANSLATORS: Print something1 using something2.
+#. "keywords" is something1
+#: alpine/mailcmd.c:8562
+msgid "SELECT A KEYWORD"
+msgstr ""
+
+#: alpine/mailcmd.c:8563 alpine/mailcmd.c:8614
+msgid "keywords"
+msgstr ""
+
+#: alpine/mailcmd.c:8564
+msgid "HELP FOR SELECTING A KEYWORD"
+msgstr ""
+
+#: alpine/mailcmd.c:8614
+msgid "SELECT KEYWORDS"
+msgstr ""
+
+#: alpine/mailcmd.c:8616
+msgid "HELP FOR SELECTING KEYWORDS"
+msgstr ""
+
+#: alpine/mailcmd.c:8664
+msgid "No charsets found? Enter charset manually."
+msgstr ""
+
+#. TRANSLATORS: SELECT A CHARACTER SET is a screen title
+#. TRANSLATORS: Print something1 using something2.
+#. "character sets" is something1
+#: alpine/mailcmd.c:8685
+msgid "SELECT A CHARACTER SET"
+msgstr ""
+
+#: alpine/mailcmd.c:8686 alpine/mailcmd.c:8860
+msgid "character sets"
+msgstr ""
+
+#: alpine/mailcmd.c:8687
+msgid "HELP FOR SELECTING A CHARACTER SET"
+msgstr ""
+
+#: alpine/mailcmd.c:8797
+msgid "Scripts representing groups of related character sets"
+msgstr ""
+
+#: alpine/mailcmd.c:8827
+msgid "Individual character sets, may be mixed with scripts"
+msgstr ""
+
+#: alpine/mailcmd.c:8860
+msgid "SELECT CHARACTER SETS"
+msgstr ""
+
+#: alpine/mailcmd.c:8862
+msgid "HELP FOR SELECTING CHARACTER SETS"
+msgstr ""
+
+#: alpine/mailcmd.c:8920
+msgid "Choose type of sort : "
+msgstr ""
+
+#: alpine/mailcmd.c:8922
+msgid "Choose type of sort, or 'R' to reverse current sort : "
+msgstr ""
+
+#: alpine/mailcmd.c:8942
+msgid "Reverse"
+msgstr ""
+
+#. TRANSLATORS: In titlebar, Thd is an abbreviation for Thread, Msg for Message.
+#. They are used when there isn't enough space so need to be short.
+#. The formatting isn't very flexible. These come before the number
+#. of the message or thread, as in
+#. Message 17
+#. when reading message number 17.
+#: alpine/titlebar.c:517 alpine/titlebar.c:527
+msgid "Thd"
+msgstr ""
+
+#: alpine/titlebar.c:517 alpine/titlebar.c:527
+msgid "Thread"
+msgstr ""
+
+#: alpine/titlebar.c:519
+msgid "Msg"
+msgstr ""
+
+#: alpine/titlebar.c:519
+msgid "Message"
+msgstr ""
+
+#. TRANSLATORS: it might say READONLY or CLOSED in the titlebar, referring to
+#. the current folder.
+#: alpine/titlebar.c:674
+msgid "(CLOSED)"
+msgstr ""
+
+#: alpine/titlebar.c:677
+msgid "(READONLY)"
+msgstr ""
+
+#. TRANSLATORS: the name of the open folder follows this in the titlebar
+#: alpine/titlebar.c:762
+msgid "Folder: "
+msgstr ""
+
+#: alpine/adrbkcmd.c:145
+msgid "Error reading entry"
+msgstr ""
+
+#: alpine/adrbkcmd.c:150
+msgid "Nothing to view"
+msgstr ""
+
+#. TRANSLATORS: Nickname is a shorthand name for something
+#. TRANSLATORS: these are the headings for setting up a collection of
+#. folders, PATH is a filesystem path, VIEW is sort of a technical
+#. term that can be used to restrict the View to fewer folders
+#: alpine/adrbkcmd.c:159 alpine/adrbkcmd.c:162 alpine/adrbkcmd.c:566
+#: alpine/adrbkcmd.c:842 alpine/adrbkcmd.c:1025 alpine/adrbkcmd.c:1033
+#: alpine/adrbkcmd.c:1601 alpine/adrbkcmd.c:1862 alpine/adrbkcmd.c:5743
+#: alpine/adrbkcmd.c:5855 alpine/folder.c:873 alpine/folder.c:943
+#: alpine/folder.c:948 alpine/roleconf.c:2927
+msgid "Nickname"
+msgstr ""
+
+#. TRANSLATORS: Full name is the name that goes with an email address.
+#. For example, in
+#. Fred Flintstone <fred@bedrock.org>
+#. Fred Flintstone is the Full Name.
+#: alpine/adrbkcmd.c:173 alpine/adrbkcmd.c:583 alpine/adrbkcmd.c:847
+#: alpine/adrbkcmd.c:1043 alpine/adrbkcmd.c:5863
+msgid "Fullname"
+msgstr ""
+
+#. TRANSLATORS: Fcc is an abbreviation for File carbon copy. It is like
+#. a cc which is a copy of a message that goes to somebody other than the
+#. main recipient, only this is a copy of a message which is put into a
+#. file on the user's computer.
+#: alpine/adrbkcmd.c:184 alpine/adrbkcmd.c:1051 alpine/adrbkcmd.c:5871
+msgid "Fcc"
+msgstr ""
+
+#. TRANSLATORS: %s is the error message
+#: alpine/adrbkcmd.c:272
+#, c-format
+msgid "Can't format entry: %s"
+msgstr ""
+
+#: alpine/adrbkcmd.c:282 alpine/adrbkcmd.c:746 alpine/adrbkcmd.c:818
+#: alpine/adrbkcmd.c:6931
+msgid "expanded entry"
+msgstr ""
+
+#. TRANSLATORS: a screen title. We are viewing an address book
+#. TRANSLATORS: screen titles
+#: alpine/adrbkcmd.c:292 alpine/adrbkcmd.c:966
+#, c-format
+msgid "ADDRESS BOOK (View)"
+msgstr ""
+
+#. TRANSLATORS: help screen title
+#: alpine/adrbkcmd.c:299
+msgid "HELP FOR ADDRESS BOOK VIEW"
+msgstr ""
+
+#. TRANSLATORS: The address book was changed by some other
+#. process. The user is being told that their change (their
+#. Update) has been canceled and they should try again.
+#: alpine/adrbkcmd.c:425
+msgid "Address book changed. Update cancelled. Try again."
+msgstr ""
+
+#: alpine/adrbkcmd.c:454
+msgid "update"
+msgstr ""
+
+#. TRANSLATORS: A question with two choices for the answer. Forward
+#. as text means to include the text of the message being forwarded
+#. in the new message. Forward as a Vcard attachment means to
+#. attach it to the message in a special format so it is recognizable
+#. as a Vcard.
+#: alpine/adrbkcmd.c:504
+msgid "Forward as text or forward as Vcard attachment ? "
+msgstr ""
+
+#: alpine/adrbkcmd.c:509 alpine/adrbkcmd.c:4666
+msgid "Address book forward cancelled"
+msgstr ""
+
+#: alpine/adrbkcmd.c:567 alpine/adrbkcmd.c:581
+msgid "Address"
+msgstr ""
+
+#. TRANSLATORS: screen title for viewing an address book entry in Rich
+#. view mode. Rich just means it is expanded to include everything.
+#: alpine/adrbkcmd.c:749
+msgid "ADDRESS BOOK (Rich View)"
+msgstr ""
+
+#: alpine/adrbkcmd.c:848 alpine/adrbkcmd.c:857
+msgid "To Message"
+msgstr ""
+
+#. TRANSLATORS: a File Copy is a copy of a sent message saved in a regular
+#. file on the computer's disk
+#: alpine/adrbkcmd.c:852
+msgid "FileCopy"
+msgstr ""
+
+#: alpine/adrbkcmd.c:856 alpine/adrbkcmd.c:1059 alpine/adrbkcmd.c:5890
+#: alpine/roleconf.c:2946
+msgid "Comment"
+msgstr ""
+
+#: alpine/adrbkcmd.c:962
+msgid "RichView"
+msgstr ""
+
+#: alpine/adrbkcmd.c:968
+#, c-format
+msgid "ADDRESS BOOK (%c%s)"
+msgstr ""
+
+#: alpine/adrbkcmd.c:997
+msgid ""
+"\n"
+" Fill in the fields. It is ok to leave fields blank."
+msgstr ""
+
+#: alpine/adrbkcmd.c:999
+msgid ""
+"\n"
+" To form a list, just enter multiple comma-separated addresses."
+msgstr ""
+
+#. TRANSLATORS: Same here, but a different version of the screen.
+#: alpine/adrbkcmd.c:1004
+msgid ""
+"\n"
+" Change any of the fields. It is ok to leave fields blank."
+msgstr ""
+
+#: alpine/adrbkcmd.c:1007
+msgid ""
+"\n"
+" Since this entry does a directory lookup you may not edit the address field."
+msgstr ""
+
+#: alpine/adrbkcmd.c:1010
+msgid ""
+"\n"
+" Additional comma-separated addresses may be entered in the address field."
+msgstr ""
+
+#: alpine/adrbkcmd.c:1014
+msgid ""
+"\n"
+" Press \"^X\" to save the entry, \"^C\" to cancel, \"^G\" for help."
+msgstr ""
+
+#: alpine/adrbkcmd.c:1016
+msgid ""
+"\n"
+" If you want to use quotation marks inside the Fullname field, it is best"
+msgstr ""
+
+#: alpine/adrbkcmd.c:1018
+msgid ""
+"\n"
+" to use single quotation marks; for example: George 'Husky' Washington."
+msgstr ""
+
+#: alpine/adrbkcmd.c:1146
+#, c-format
+msgid "Address book %s cancelled"
+msgstr ""
+
+#: alpine/adrbkcmd.c:1173
+msgid "Warning: entry has no addresses"
+msgstr ""
+
+#: alpine/adrbkcmd.c:1252 alpine/adrbkcmd.c:6467
+#, c-format
+msgid "Error updating address book: %s"
+msgstr ""
+
+#. TRANSLATORS: The first %s is the nickname the user is
+#. trying to use that exists in another address book.
+#. The second %s is the name of the other address book.
+#. The third %s is " as " because it will say something
+#. like also exists in <name> as <description>.
+#: alpine/adrbkcmd.c:1275
+#, c-format
+msgid "Warning! Nickname %s also exists in \"%s\"%s%s"
+msgstr ""
+
+#: alpine/adrbkcmd.c:1277
+msgid " as "
+msgstr ""
+
+#. TRANSLATORS: The %s is the nickname of an entry that is already
+#. in the address book
+#: alpine/adrbkcmd.c:1372
+#, c-format
+msgid "\"%s\" already in address book."
+msgstr ""
+
+#. TRANSLATORS: A question
+#: alpine/adrbkcmd.c:1524
+msgid "Exit and save changes "
+msgstr ""
+
+#: alpine/adrbkcmd.c:1529 alpine/folder.c:1206
+msgid "Use ^C to abandon changes you've made"
+msgstr ""
+
+#. TRANSLATORS: A question. The %s is a noun describing what is being cancelled.
+#: alpine/adrbkcmd.c:1555 alpine/folder.c:1241
+#, c-format
+msgid "Cancel %s (answering \"Yes\" will abandon any changes made) "
+msgstr ""
+
+#: alpine/adrbkcmd.c:1584
+msgid "changes"
+msgstr ""
+
+#: alpine/adrbkcmd.c:1595 alpine/folder.c:876 alpine/folder.c:955
+msgid "Server"
+msgstr ""
+
+#: alpine/adrbkcmd.c:1598 alpine/folder.c:4306
+msgid "Folder"
+msgstr ""
+
+#: alpine/adrbkcmd.c:1633
+msgid "New address book added. Use \"$\" to adjust order"
+msgstr ""
+
+#. TRANSLATORS: Change was the name of the command the user was
+#. trying to perform. It is what was cancelled.
+#: alpine/adrbkcmd.c:1702
+msgid "Change cancelled: config file not changeable"
+msgstr ""
+
+#. TRANSLATORS: Add was the command that is being cancelled.
+#: alpine/adrbkcmd.c:1705
+msgid "Add cancelled: config file not changeable"
+msgstr ""
+
+#. TRANSLATORS: Operation was cancelled because the system management
+#. does not allow the changing of global address books
+#: alpine/adrbkcmd.c:1717
+msgid "Cancelled: Sys. Mgmt. does not allow changing global address books"
+msgstr ""
+
+#: alpine/adrbkcmd.c:1719
+msgid "Cancelled: Sys. Mgmt. does not allow changing address books"
+msgstr ""
+
+#. TRANSLATORS: screen title
+#: alpine/adrbkcmd.c:1747
+msgid "CHANGE ADDRESS BOOK"
+msgstr ""
+
+#. TRANSLATORS: screen title
+#: alpine/adrbkcmd.c:1750
+msgid "ADD ADDRESS BOOK"
+msgstr ""
+
+#. TRANSLATORS: The next few lines go together to explain how to add
+#. the address book entry the user is working on.
+#: alpine/adrbkcmd.c:1766
+msgid ""
+" To add a local address book that will be accessed *only* by Alpine running\n"
+" on this machine, leave the server field blank."
+msgstr ""
+
+#: alpine/adrbkcmd.c:1768
+msgid ""
+" To add an address book that will be accessed by IMAP, fill in the\n"
+" server name."
+msgstr ""
+
+#: alpine/adrbkcmd.c:1770
+msgid ""
+" (NOTE: An address book cannot be accessed by IMAP from\n"
+" one Alpine and as a local file from another. It is either always accessed\n"
+" by IMAP or it is a local address book which is never accessed by IMAP.)"
+msgstr ""
+
+#: alpine/adrbkcmd.c:1772
+msgid " In the Folder field, type the remote folder name or local file name."
+msgstr ""
+
+#: alpine/adrbkcmd.c:1774
+msgid ""
+" In the Nickname field, give the address book a nickname or leave it blank."
+msgstr ""
+
+#: alpine/adrbkcmd.c:1776
+msgid " To get help specific to an item, press ^G."
+msgstr ""
+
+#: alpine/adrbkcmd.c:1778
+msgid " To exit and save the configuration, press ^X. To cancel, press ^C."
+msgstr ""
+
+#: alpine/adrbkcmd.c:1842 alpine/adrbkcmd.c:1848
+msgid "Server Name"
+msgstr ""
+
+#: alpine/adrbkcmd.c:1855
+msgid "Folder Name"
+msgstr ""
+
+#: alpine/adrbkcmd.c:1884 alpine/adrbkcmd.c:1965
+msgid "Address book change is cancelled"
+msgstr ""
+
+#: alpine/adrbkcmd.c:1886 alpine/adrbkcmd.c:1967
+msgid "Address book add is cancelled"
+msgstr ""
+
+#: alpine/adrbkcmd.c:1895
+msgid "No change: Address book change is cancelled"
+msgstr ""
+
+#: alpine/adrbkcmd.c:1897
+msgid "No change: Address book add is cancelled"
+msgstr ""
+
+#: alpine/adrbkcmd.c:1993
+msgid "Change cancelled: couldn't save configuration file"
+msgstr ""
+
+#: alpine/adrbkcmd.c:1995
+msgid "Add cancelled: couldn't save configuration file"
+msgstr ""
+
+#: alpine/adrbkcmd.c:2114
+#, c-format
+msgid "Convert addressbook%s to a remote addrbook "
+msgstr ""
+
+#. TRANSLATORS: The user is defining an address book which will be
+#. stored on another server. This is called a remote addrbook and
+#. this is a question asking for the name of the server.
+#: alpine/adrbkcmd.c:2140
+#, c-format
+msgid "Name of server to contain remote addrbook : "
+msgstr ""
+
+#: alpine/adrbkcmd.c:2207
+msgid "You now have a chance to change the name of the remote addrbook..."
+msgstr ""
+
+#: alpine/adrbkcmd.c:2256 alpine/adrbkcmd.c:2410 alpine/adrbkcmd.c:2518
+#: alpine/adrbkcmd.c:2593 alpine/adrbkcmd.c:2864 alpine/mailpart.c:2343
+#: alpine/mailpart.c:2394 alpine/mailpart.c:2850
+msgid "Error allocating space for message."
+msgstr ""
+
+#. TRANSLATORS: Several lines in a row here that go together.
+#: alpine/adrbkcmd.c:2261
+#, c-format
+msgid ""
+"\n"
+"Your addressbook%s has been copied to the"
+msgstr ""
+
+#: alpine/adrbkcmd.c:2264
+msgid ""
+"\n"
+"remote folder \""
+msgstr ""
+
+#: alpine/adrbkcmd.c:2267
+msgid ""
+"\n"
+"A definition for this remote address book has been added to your list"
+msgstr ""
+
+#: alpine/adrbkcmd.c:2268
+msgid ""
+"\n"
+"of address books. The definition for the address book it was copied"
+msgstr ""
+
+#: alpine/adrbkcmd.c:2269
+msgid ""
+"\n"
+"from is also still there. You may want to remove that after you"
+msgstr ""
+
+#: alpine/adrbkcmd.c:2270
+msgid ""
+"\n"
+"are confident that the new address book is complete and working."
+msgstr ""
+
+#: alpine/adrbkcmd.c:2271
+msgid ""
+"\n"
+"Use the Setup/AddressBooks command to do that.\n"
+msgstr ""
+
+#: alpine/adrbkcmd.c:2276
+msgid "Remote Address Book Information"
+msgstr ""
+
+#. TRANSLATORS: a screen title
+#: alpine/adrbkcmd.c:2278
+msgid "ABOUT REMOTE ABOOK"
+msgstr ""
+
+#. TRANSLATORS: A literal sig is a way to store a signature
+#. in alpine. It isn't a very descriptive name. Instead of
+#. storing it in its own file it is stored in the configuration
+#. file.
+#: alpine/adrbkcmd.c:2385
+#, c-format
+msgid "Convert signature file \"%s\" to a literal sig "
+msgstr ""
+
+#. TRANSLATORS: following lines go together
+#: alpine/adrbkcmd.c:2416
+#, c-format
+msgid ""
+"\n"
+"Your signature file \"%s\" has been converted"
+msgstr ""
+
+#. TRANSLATORS: several lines that go together
+#: alpine/adrbkcmd.c:2420 alpine/adrbkcmd.c:2534
+msgid ""
+"\n"
+"to a literal signature, which means it is contained in your"
+msgstr ""
+
+#: alpine/adrbkcmd.c:2422 alpine/adrbkcmd.c:2536
+msgid ""
+"\n"
+"Alpine configuration instead of being in a file of its own."
+msgstr ""
+
+#: alpine/adrbkcmd.c:2424 alpine/adrbkcmd.c:2538
+msgid ""
+"\n"
+"If that configuration is copied to a remote folder then the"
+msgstr ""
+
+#: alpine/adrbkcmd.c:2426 alpine/adrbkcmd.c:2540
+msgid ""
+"\n"
+"signature will be available remotely also."
+msgstr ""
+
+#: alpine/adrbkcmd.c:2428 alpine/adrbkcmd.c:2542
+msgid ""
+"\n"
+"Changes to the signature file itself will no longer have any"
+msgstr ""
+
+#: alpine/adrbkcmd.c:2430
+msgid ""
+"\n"
+"effect on Alpine but you may still edit the signature with the"
+msgstr ""
+
+#: alpine/adrbkcmd.c:2432
+msgid ""
+"\n"
+"Setup/Signature command.\n"
+msgstr ""
+
+#: alpine/adrbkcmd.c:2437 alpine/adrbkcmd.c:2552
+msgid "Literal Signature Information"
+msgstr ""
+
+#. TRANSLATORS: screen title
+#. TRANSLATORS: a screen title
+#: alpine/adrbkcmd.c:2439 alpine/adrbkcmd.c:2554
+msgid "ABOUT LITERAL SIG"
+msgstr ""
+
+#. TRANSLATORS: asking whether a signature file should be converted to what
+#. we call a literal signature, which is one contained in the regular
+#. configuration file. Think of the set of 4 %s arguments as a
+#. single argument which is the name of the signature file.
+#: alpine/adrbkcmd.c:2474
+#, c-format
+msgid "Convert signature file \"%s\"%s%s%s to a literal sig "
+msgstr ""
+
+#. TRANSLATORS: Keep the %s's together, they are sort of
+#. the name of the file.
+#: alpine/adrbkcmd.c:2525
+#, c-format
+msgid "Your signature file \"%s\"%s%s%s has been converted"
+msgstr ""
+
+#: alpine/adrbkcmd.c:2544
+msgid ""
+"\n"
+"effect on Alpine. You may edit the signature with the"
+msgstr ""
+
+#: alpine/adrbkcmd.c:2546
+msgid ""
+"\n"
+"Setup/Rules/Roles command.\n"
+msgstr ""
+
+#. TRANSLATORS: config is an abbreviation for configuration
+#: alpine/adrbkcmd.c:2567
+msgid "Error writing rules config."
+msgstr ""
+
+#. TRANSLATORS: sig is signature
+#: alpine/adrbkcmd.c:2571
+#, c-format
+msgid "Error converting role sig\n"
+msgstr ""
+
+#. TRANSLATORS: several lines that go together
+#: alpine/adrbkcmd.c:2598
+msgid ""
+"\n"
+"Some of your Rules are contained in Rule files instead of being directly"
+msgstr ""
+
+#: alpine/adrbkcmd.c:2599
+msgid ""
+"\n"
+"contained in your Alpine configuration file. To make those rules"
+msgstr ""
+
+#: alpine/adrbkcmd.c:2600
+msgid ""
+"\n"
+"available remotely you will need to move them out of the files."
+msgstr ""
+
+#: alpine/adrbkcmd.c:2601
+msgid ""
+"\n"
+"That can be done using the Shuffle command in the appropriate"
+msgstr ""
+
+#: alpine/adrbkcmd.c:2602
+msgid ""
+"\n"
+"Setup/Rules subcommands.\n"
+msgstr ""
+
+#: alpine/adrbkcmd.c:2607
+msgid "Rule Files Information"
+msgstr ""
+
+#. TRANSLATORS: a screen title
+#: alpine/adrbkcmd.c:2609
+msgid "ABOUT RULE FILES"
+msgstr ""
+
+#: alpine/adrbkcmd.c:2632
+msgid "eXceptions does not make sense with this command"
+msgstr ""
+
+#. TRANSLATORS: AddressBooks is Address Books
+#: alpine/adrbkcmd.c:2655
+#, c-format
+msgid "Config is already remote, convert AddressBooks and signature files "
+msgstr ""
+
+#: alpine/adrbkcmd.c:2657
+#, c-format
+msgid "Config is already remote, convert AddressBooks "
+msgstr ""
+
+#: alpine/adrbkcmd.c:2659
+#, c-format
+msgid "Config is already remote, convert signature files "
+msgstr ""
+
+#: alpine/adrbkcmd.c:2724
+msgid "Cannot copy config file since it is already remote."
+msgstr ""
+
+#: alpine/adrbkcmd.c:2744
+#, c-format
+msgid "Name of server to contain remote Alpine config : "
+msgstr ""
+
+#: alpine/adrbkcmd.c:2806
+#, c-format
+msgid "Folder to contain remote config : "
+msgstr ""
+
+#. TRANSLATORS: several lines that go together
+#: alpine/adrbkcmd.c:2869
+msgid ""
+"\n"
+"You may want to save a copy of this information!"
+msgstr ""
+
+#: alpine/adrbkcmd.c:2870
+msgid ""
+"\n"
+"\n"
+"Your Alpine configuration data has been copied to"
+msgstr ""
+
+#: alpine/adrbkcmd.c:2874
+msgid ""
+"\n"
+"To use that remote configuration from this computer you will"
+msgstr ""
+
+#: alpine/adrbkcmd.c:2875
+msgid ""
+"\n"
+"have to change the way you start Alpine by using the command line option"
+msgstr ""
+
+#: alpine/adrbkcmd.c:2876
+msgid ""
+"\n"
+"\"alpine -p <remote_folder>\". The command should probably be"
+msgstr ""
+
+#: alpine/adrbkcmd.c:2882
+msgid ""
+"\n"
+"With PC-Alpine, you may want to create a shortcut which"
+msgstr ""
+
+#: alpine/adrbkcmd.c:2883
+msgid ""
+"\n"
+"has the required arguments."
+msgstr ""
+
+#: alpine/adrbkcmd.c:2889
+msgid ""
+"\n"
+"The quotes are there around the last argument to protect the special"
+msgstr ""
+
+#: alpine/adrbkcmd.c:2890
+msgid ""
+"\n"
+"characters in the folder name (like braces) from the command shell"
+msgstr ""
+
+#: alpine/adrbkcmd.c:2891
+msgid ""
+"\n"
+"you use. If you are not running Alpine from a command shell which knows"
+msgstr ""
+
+#: alpine/adrbkcmd.c:2892
+msgid ""
+"\n"
+"about quoting, it is possible you will have to remove those quotes"
+msgstr ""
+
+#: alpine/adrbkcmd.c:2893
+msgid ""
+"\n"
+"from the command. For example, if you also use PC-Alpine you will probably"
+msgstr ""
+
+#: alpine/adrbkcmd.c:2894
+msgid ""
+"\n"
+"want to create a shortcut, and you would not need the quotes there."
+msgstr ""
+
+#: alpine/adrbkcmd.c:2895
+msgid ""
+"\n"
+"Without the quotes, the command might look like"
+msgstr ""
+
+#: alpine/adrbkcmd.c:2900
+msgid ""
+"\n"
+"Consider creating an alias or shell script to execute this command to make"
+msgstr ""
+
+#: alpine/adrbkcmd.c:2901
+msgid ""
+"\n"
+"it more convenient."
+msgstr ""
+
+#: alpine/adrbkcmd.c:2903
+msgid ""
+"\n"
+"\n"
+"If you want to use your new remote configuration for this session, quit"
+msgstr ""
+
+#: alpine/adrbkcmd.c:2904
+msgid ""
+"\n"
+"Alpine now and restart with the changed command line options mentioned "
+"above.\n"
+msgstr ""
+
+#: alpine/adrbkcmd.c:2909
+msgid "Remote Config Information"
+msgstr ""
+
+#. TRANSLATORS: a screen title
+#: alpine/adrbkcmd.c:2911
+msgid "ABOUT REMOTE CONFIG"
+msgstr ""
+
+#. TRANSLATORS: Double quote refers to the " character
+#: alpine/adrbkcmd.c:2982
+msgid "Double quote not allowed in nickname"
+msgstr ""
+
+#: alpine/adrbkcmd.c:2995
+msgid "Nickname is already being used"
+msgstr ""
+
+#: alpine/adrbkcmd.c:3044
+msgid "Delete cancelled: config file not changeable"
+msgstr ""
+
+#: alpine/adrbkcmd.c:3062 alpine/adrbkcmd.c:3526 alpine/adrbkcmd.c:3669
+msgid ""
+"Cancelled: Sys. Mgmt. does not allow changing global address book config"
+msgstr ""
+
+#: alpine/adrbkcmd.c:3065 alpine/adrbkcmd.c:3546
+msgid "Cancelled: Sys. Mgmt. does not allow changing address book config"
+msgstr ""
+
+#. TRANSLATORS: %s is an adjective modifying address books
+#: alpine/adrbkcmd.c:3127
+#, c-format
+msgid "Ignore all default %s address books or just remove this one ? "
+msgstr ""
+
+#. TRANSLATORS: global or personal address books
+#: alpine/adrbkcmd.c:3129
+msgid "global"
+msgstr ""
+
+#: alpine/adrbkcmd.c:3129
+msgid "personal"
+msgstr ""
+
+#: alpine/adrbkcmd.c:3143 alpine/adrbkcmd.c:3155 alpine/adrbkcmd.c:3202
+#: alpine/adrbkcmd.c:3223 alpine/adrbkcmd.c:3285 alpine/folder.c:5517
+#: alpine/folder.c:5554
+msgid "Delete cancelled"
+msgstr ""
+
+#. TRANSLATORS: a question
+#: alpine/adrbkcmd.c:3150
+msgid "Delete this default address book from config "
+msgstr ""
+
+#. TRANSLATORS: user is asked whether to remove just data files, just configuration,
+#. or both for an address book.
+#: alpine/adrbkcmd.c:3176
+msgid "Data"
+msgstr ""
+
+#: alpine/adrbkcmd.c:3178 alpine/reply.c:1128
+msgid "Both"
+msgstr ""
+
+#: alpine/adrbkcmd.c:3181
+msgid "Delete data, config, or both ? "
+msgstr ""
+
+#. TRANSLATORS: a question
+#: alpine/adrbkcmd.c:3213
+msgid "Delete configuration for highlighted addressbook "
+msgstr ""
+
+#. TRANSLATORS: %s is an error message
+#: alpine/adrbkcmd.c:3253
+#, c-format
+msgid "Can't delete data: %s"
+msgstr ""
+
+#: alpine/adrbkcmd.c:3255
+msgid "Can't delete address book data"
+msgstr ""
+
+#: alpine/adrbkcmd.c:3275
+#, c-format
+msgid ""
+"About to delete the contents of address book (%ld entries), really delete "
+msgstr ""
+
+#: alpine/adrbkcmd.c:3352
+#, c-format
+msgid "Trouble deleting data %s%s%s%s"
+msgstr ""
+
+#: alpine/adrbkcmd.c:3371
+msgid "Addressbook data deleted"
+msgstr ""
+
+#: alpine/adrbkcmd.c:3438
+msgid "Delete cancelled: couldn't save pine configuration file"
+msgstr ""
+
+#. TRANSLATORS: the %s may be "global " or nothing
+#: alpine/adrbkcmd.c:3450
+#, c-format
+msgid "Reverting to default %saddress books"
+msgstr ""
+
+#: alpine/adrbkcmd.c:3451
+msgid "global "
+msgstr ""
+
+#: alpine/adrbkcmd.c:3502
+msgid "Shuffle cancelled: config file not changeable"
+msgstr ""
+
+#: alpine/adrbkcmd.c:3564
+#, c-format
+msgid "Shuffle \"%s\" %s%s%s ? "
+msgstr ""
+
+#: alpine/adrbkcmd.c:3586
+msgid "Shuffle cancelled"
+msgstr ""
+
+#: alpine/adrbkcmd.c:3645
+msgid "Shuffle cancelled: highlight entry you wish to shuffle"
+msgstr ""
+
+#: alpine/adrbkcmd.c:3662
+msgid ""
+"Cancelled: Sys. Mgmt. does not allow changing address book configuration"
+msgstr ""
+
+#. TRANSLATORS: TabSep is a command key label meaning Tab Separated List
+#: alpine/adrbkcmd.c:4180
+msgid "TabSep"
+msgstr ""
+
+#: alpine/adrbkcmd.c:4193
+msgid "Export list of addresses, vCard format, or Tab Separated ? "
+msgstr ""
+
+#. TRANSLATORS: a screen title
+#: alpine/adrbkcmd.c:4198
+msgid "HELP FOR EXPORT FORMAT"
+msgstr ""
+
+#: alpine/adrbkcmd.c:4247 alpine/addrbook.c:7271
+msgid "addresses"
+msgstr ""
+
+#: alpine/adrbkcmd.c:4247
+msgid "address"
+msgstr ""
+
+#: alpine/adrbkcmd.c:4248
+msgid "EXPORT"
+msgstr ""
+
+#: alpine/adrbkcmd.c:4271
+#, c-format
+msgid "Error opening file \"%s\" for address export: %s"
+msgstr ""
+
+#: alpine/adrbkcmd.c:4391
+msgid "List is empty, nothing to export!"
+msgstr ""
+
+#: alpine/adrbkcmd.c:4520
+msgid "Trouble accessing current entry"
+msgstr ""
+
+#: alpine/adrbkcmd.c:4580 alpine/adrbkcmd.c:4723
+msgid "Problem creating space for message text"
+msgstr ""
+
+#: alpine/adrbkcmd.c:4663
+msgid "Expand nicknames"
+msgstr ""
+
+#: alpine/adrbkcmd.c:4729
+msgid "FORWARDING ADDRESS BOOK ENTRY"
+msgstr ""
+
+#: alpine/adrbkcmd.c:5242
+#, c-format
+msgid "Save highlighted entry to address book or Export to filesystem ? "
+msgstr ""
+
+#: alpine/adrbkcmd.c:5244
+#, c-format
+msgid "Save selected entries to address book or Export to filesystem ? "
+msgstr ""
+
+#: alpine/adrbkcmd.c:5246
+#, c-format
+msgid "Save selected entry to address book or Export to filesystem ? "
+msgstr ""
+
+#: alpine/adrbkcmd.c:5307
+#, c-format
+msgid "Can't re-open address book %s to save from"
+msgstr ""
+
+#: alpine/adrbkcmd.c:5365
+msgid "No current entry to save"
+msgstr ""
+
+#: alpine/adrbkcmd.c:5408
+#, c-format
+msgid "Saved %d entries to %s"
+msgstr ""
+
+#: alpine/adrbkcmd.c:5410
+#, c-format
+msgid "Saved %d entry to %s"
+msgstr ""
+
+#: alpine/adrbkcmd.c:5427
+#, c-format
+msgid "Entry with nickname \"%.*s\" already exists, replace "
+msgstr ""
+
+#: alpine/adrbkcmd.c:5431
+#, c-format
+msgid "Nicknames \"%.*s\" and \"%.*s\" already exist, replace "
+msgstr ""
+
+#: alpine/adrbkcmd.c:5434
+#, c-format
+msgid "%d of the nicknames already exist, replace "
+msgstr ""
+
+#: alpine/adrbkcmd.c:5477
+msgid "Saving entries"
+msgstr ""
+
+#: alpine/adrbkcmd.c:5491 alpine/adrbkcmd.c:5576
+#, c-format
+msgid "Error saving entry: %s"
+msgstr ""
+
+#: alpine/adrbkcmd.c:5510
+#, c-format
+msgid "Error replacing entry in %s: %s"
+msgstr ""
+
+#: alpine/adrbkcmd.c:5574
+#, c-format
+msgid "Error saving %s: %s"
+msgstr ""
+
+#: alpine/adrbkcmd.c:5610
+msgid "Save only partially completed"
+msgstr ""
+
+#: alpine/adrbkcmd.c:5689
+msgid "AddressBook"
+msgstr ""
+
+#: alpine/adrbkcmd.c:5690
+msgid "Entry"
+msgstr ""
+
+#: alpine/adrbkcmd.c:5693
+msgid "Print Address Book or just this Entry? "
+msgstr ""
+
+#: alpine/adrbkcmd.c:5697 alpine/adrbkcmd.c:5782
+msgid "Address book print cancelled"
+msgstr ""
+
+#: alpine/adrbkcmd.c:5715
+msgid "selected entries"
+msgstr ""
+
+#: alpine/adrbkcmd.c:5718
+msgid "address book list"
+msgstr ""
+
+#: alpine/adrbkcmd.c:5720
+msgid "entry"
+msgstr ""
+
+#: alpine/adrbkcmd.c:5722
+msgid "address book"
+msgstr ""
+
+#. TRANSLATORS: a screen title for address book screen. Almost all of
+#. the translatable strings which are all upper case are screen titles.
+#: alpine/adrbkcmd.c:6086 alpine/addrbook.c:1536 alpine/addrbook.c:2287
+#, c-format
+msgid "ADDRESS BOOK"
+msgstr ""
+
+#: alpine/adrbkcmd.c:6153
+#, c-format
+msgid "Really delete %d selected entries"
+msgstr ""
+
+#: alpine/adrbkcmd.c:6239
+msgid "Deletions completed"
+msgstr ""
+
+#: alpine/adrbkcmd.c:6360
+#, c-format
+msgid "Really delete \"%s\""
+msgstr ""
+
+#: alpine/adrbkcmd.c:6368
+#, c-format
+msgid "Really delete ENTIRE list \"%s\""
+msgstr ""
+
+#: alpine/adrbkcmd.c:6374
+#, c-format
+msgid "Really delete \"%s\" from list"
+msgstr ""
+
+#: alpine/adrbkcmd.c:6450
+msgid "Entry deleted, address book updated"
+msgstr ""
+
+#: alpine/adrbkcmd.c:6479
+msgid "Entry not deleted"
+msgstr ""
+
+#. TRANSLATORS: go to more complex search screen
+#: alpine/adrbkcmd.c:6599
+msgid "To complex search"
+msgstr ""
+
+#: alpine/adrbkcmd.c:6608
+msgid "No LDAP server available for lookup"
+msgstr ""
+
+#: alpine/adrbkcmd.c:6618
+msgid "String to search for : "
+msgstr ""
+
+#: alpine/adrbkcmd.c:6630 alpine/alpine.c:813 pico/pico.c:942
+msgid "Cancelled"
+msgstr ""
+
+#: alpine/adrbkcmd.c:6642 alpine/mailindx.c:3083 alpine/addrbook.c:2472
+#: alpine/addrbook.c:2725
+msgid "Search"
+msgstr ""
+
+#: alpine/adrbkcmd.c:6646
+msgid "Restore"
+msgstr ""
+
+#: alpine/adrbkcmd.c:6649
+msgid "SEARCH DIRECTORY SERVER"
+msgstr ""
+
+#: alpine/adrbkcmd.c:6667
+msgid ""
+"\n"
+" Fill in some of the fields above to create a query."
+msgstr ""
+
+#: alpine/adrbkcmd.c:6669
+msgid ""
+"\n"
+" The match will be for the exact string unless you include wildcards (*)."
+msgstr ""
+
+#: alpine/adrbkcmd.c:6671
+msgid ""
+"\n"
+" All filled-in fields must match in order to be counted as a match."
+msgstr ""
+
+#: alpine/adrbkcmd.c:6673
+msgid ""
+"\n"
+" Press \"^R\" to restore previous query values (if you've queried "
+"previously)."
+msgstr ""
+
+#: alpine/adrbkcmd.c:6675
+msgid ""
+"\n"
+" \"^G\" for help specific to each item. \"^X\" to make the query, or \"^C\" "
+"to cancel."
+msgstr ""
+
+#: alpine/adrbkcmd.c:6756
+msgid "Search strings too long"
+msgstr ""
+
+#: alpine/adrbkcmd.c:6859 pith/ldap.c:196
+msgid "Missing hostname in LDAP address"
+msgstr ""
+
+#: alpine/adrbkcmd.c:6874 pith/ldap.c:221
+#, c-format
+msgid "No email address available for \"%s\""
+msgstr ""
+
+#: alpine/adrbkcmd.c:6933
+msgid "DIRECTORY ENTRY"
+msgstr ""
+
+#: alpine/adrbkcmd.c:6937
+msgid "HELP FOR DIRECTORY VIEW"
+msgstr ""
+
+#: alpine/adrbkcmd.c:7112
+msgid "No address to compose to"
+msgstr ""
+
+#: alpine/adrbkcmd.c:7580
+#, c-format
+msgid "No host in %s"
+msgstr ""
+
+#: alpine/adrbkcmd.c:7588
+msgid "Searching for LDAP url"
+msgstr ""
+
+#: alpine/adrbkcmd.c:7602
+msgid "LDAP search failed: can't initialize"
+msgstr ""
+
+#: alpine/adrbkcmd.c:7626
+#, c-format
+msgid "LDAP search failed: %s"
+msgstr ""
+
+#: alpine/adrbkcmd.c:7643
+msgid "No matches found for url"
+msgstr ""
+
+#. TRANSLATORS: sigs refers to signatures, which the user was trying to convert
+#: alpine/alpine.c:475
+#, c-format
+msgid "trouble converting sigs\n"
+msgstr ""
+
+#: alpine/alpine.c:588
+#, c-format
+msgid "Failed to disable mail driver \"%s\": name not found"
+msgstr ""
+
+#: alpine/alpine.c:609
+#, c-format
+msgid "Failed to disable SASL authenticator \"%s\": name not found"
+msgstr ""
+
+#: alpine/alpine.c:642
+#, c-format
+msgid "Who are you? (Unable to look up login name)\n"
+msgstr ""
+
+#: alpine/alpine.c:663
+#, c-format
+msgid ""
+"Can't access terminal or input is not a terminal. Redirection of\n"
+"standard input is not allowed. For example \"pine < file\" doesn't work.\n"
+"%c"
+msgstr ""
+
+#: alpine/alpine.c:675
+#, c-format
+msgid "Terminal type (environment variable TERM) not set.\n"
+msgstr ""
+
+#: alpine/alpine.c:678
+#, c-format
+msgid "Terminal type \"%s\" is unknown.\n"
+msgstr ""
+
+#: alpine/alpine.c:681
+#, c-format
+msgid "Can't open terminal capabilities database.\n"
+msgstr ""
+
+#: alpine/alpine.c:684
+#, c-format
+msgid ""
+"Your terminal, of type \"%s\", is lacking functions needed to run alpine.\n"
+msgstr ""
+
+#: alpine/alpine.c:719
+msgid "Warning: sensitive authentication data included in debug file"
+msgstr ""
+
+#. TRANSLATORS: file is computer data
+#: alpine/alpine.c:801
+msgid "File to open : "
+msgstr ""
+
+#. TRANSLATORS: file is computer data
+#: alpine/alpine.c:828
+msgid "No file to open"
+msgstr ""
+
+#: alpine/alpine.c:844
+#, c-format
+msgid "Problem reading standard input: %s"
+msgstr ""
+
+#. TRANSLATORS: file is computer file being read by user
+#: alpine/alpine.c:869
+msgid "file"
+msgstr ""
+
+#. TRANSLATORS: this is in the title bar at top of screen
+#: alpine/alpine.c:871
+msgid "FILE VIEW"
+msgstr ""
+
+#: alpine/alpine.c:884
+#, c-format
+msgid "Can't display \"%s\": %s"
+msgstr ""
+
+#: alpine/alpine.c:885
+msgid "Standard Input"
+msgstr ""
+
+#. TRANSLATORS: refers to bad email address
+#: alpine/alpine.c:941
+#, c-format
+msgid "Bad address: %s"
+msgstr ""
+
+#: alpine/alpine.c:992
+msgid "Only mailto URLs are allowed with file attachments"
+msgstr ""
+
+#: alpine/alpine.c:1001
+#, c-format
+msgid "Unrecognized URL \"%s\""
+msgstr ""
+
+#. TRANSLATORS: Initial Keystroke List is the literal name of an option
+#: alpine/alpine.c:1023
+msgid "Executing Initial Keystroke List......"
+msgstr ""
+
+#: alpine/alpine.c:1071
+msgid "Please wait, opening mail folder......"
+msgstr ""
+
+#: alpine/alpine.c:1124
+msgid "No folder to open"
+msgstr ""
+
+#: alpine/alpine.c:1156
+#, c-format
+msgid "Unable to open folder \"%s\""
+msgstr ""
+
+#: alpine/alpine.c:1181
+msgid "No inbox! Folder to open as inbox : "
+msgstr ""
+
+#: alpine/alpine.c:1193
+msgid "Folder open cancelled"
+msgstr ""
+
+#. TRANSLATORS: Inbox-Path and PINERC are literal, not to be translated
+#: alpine/alpine.c:1206
+msgid "Preserve folder as \"Inbox-Path\" in PINERC"
+msgstr ""
+
+#: alpine/alpine.c:1227 pith/mailcmd.c:884
+msgid "No folder opened"
+msgstr ""
+
+#: alpine/alpine.c:1271
+msgid "Use Compose command to continue interrupted message."
+msgstr ""
+
+#: alpine/alpine.c:1280
+#, c-format
+msgid "WARNING! Over your disk quota by %s bytes (%s)"
+msgstr ""
+
+#. TRANSLATORS: config is an abbreviation for configuration
+#: alpine/alpine.c:1293
+msgid ""
+"Note: some of your config options conflict with site policy and are ignored"
+msgstr ""
+
+#. TRANSLATORS: Pruned-Folders is literal
+#: alpine/alpine.c:1298
+msgid "Note: ignoring Pruned-Folders outside of default collection for saves"
+msgstr ""
+
+#: alpine/alpine.c:1304
+msgid ""
+"Note: Mail-Check-Interval=0 may cause IMAP server connection to time out"
+msgstr ""
+
+#: alpine/alpine.c:1484
+#, c-format
+msgid " %s HELP - Get help using Alpine"
+msgstr ""
+
+#: alpine/alpine.c:1486
+#, c-format
+msgid " %s COMPOSE MESSAGE - Compose and send%s a message"
+msgstr ""
+
+#. TRANSLATORS: We think of sending an email message or posting a news message.
+#. The message is shown as Compose and send/post a message
+#: alpine/alpine.c:1489
+msgid "/post"
+msgstr ""
+
+#: alpine/alpine.c:1490
+#, c-format
+msgid " %s MESSAGE INDEX - View messages in current folder"
+msgstr ""
+
+#: alpine/alpine.c:1492
+#, c-format
+msgid " %s FOLDER LIST - Select a folder%s to view"
+msgstr ""
+
+#. TRANSLATORS: When news is supported the message above becomes
+#. Select a folder OR news group to view
+#: alpine/alpine.c:1495
+msgid " OR news group"
+msgstr ""
+
+#: alpine/alpine.c:1496
+#, c-format
+msgid " %s ADDRESS BOOK - Update address book"
+msgstr ""
+
+#: alpine/alpine.c:1498
+#, c-format
+msgid " %s SETUP - Configure Alpine Options"
+msgstr ""
+
+#. TRANSLATORS: final Main menu line
+#: alpine/alpine.c:1501
+#, c-format
+msgid " %s QUIT - Leave the Alpine program"
+msgstr ""
+
+#: alpine/alpine.c:1698
+#, c-format
+msgid "Debug level set to %s"
+msgstr ""
+
+#. TRANSLATORS: This is a screen title
+#: alpine/alpine.c:1733
+msgid "HELP FOR MAIN MENU"
+msgstr ""
+
+#. TRANSLATORS: list refers to list of commands in main menu
+#: alpine/alpine.c:1762
+msgid "Already at top of list"
+msgstr ""
+
+#: alpine/alpine.c:1778
+msgid "Already at bottom of list"
+msgstr ""
+
+#. TRANSLATORS: This is a screen title
+#: alpine/alpine.c:1786
+msgid "ALPINE RELEASE NOTES"
+msgstr ""
+
+#. TRANSLATORS: screen title
+#: alpine/alpine.c:2051
+msgid "MAIN MENU"
+msgstr ""
+
+#. TRANSLATORS: screen title
+#: alpine/alpine.c:2286
+msgid "SETUP EXCEPTIONS"
+msgstr ""
+
+#: alpine/alpine.c:2292
+msgid "not eXceptions"
+msgstr ""
+
+#. TRANSLATORS: screen title
+#: alpine/alpine.c:2297 alpine/alpine.c:2492
+msgid "SETUP"
+msgstr ""
+
+#: alpine/alpine.c:2300 alpine/alpine.c:2520 alpine/alpine.c:2523
+msgid "eXceptions"
+msgstr ""
+
+#: alpine/alpine.c:2311
+msgid ""
+"Need argument \"-x <except_config>\" or \"PINERCEX\" file to use eXceptions"
+msgstr ""
+
+#: alpine/alpine.c:2313
+msgid ""
+"Need argument \"-x <except_config>\" or \".pinercex\" file to use eXceptions"
+msgstr ""
+
+#. TRANSLATORS: starting here we have a whole screen of help text
+#: alpine/alpine.c:2369
+msgid ""
+"This is the Setup screen for Alpine. Choose from the following commands:\n"
+msgstr ""
+
+#: alpine/alpine.c:2372
+msgid "(E) Exit Setup:\n"
+msgstr ""
+
+#: alpine/alpine.c:2373
+msgid " This puts you back at the Main Menu.\n"
+msgstr ""
+
+#: alpine/alpine.c:2377 alpine/alpine.c:2469
+msgid "(X) eXceptions:\n"
+msgstr ""
+
+#: alpine/alpine.c:2378 alpine/alpine.c:2470
+msgid ""
+" This command is different from the rest. It is not actually a command\n"
+msgstr ""
+
+#: alpine/alpine.c:2379 alpine/alpine.c:2471
+msgid ""
+" itself. Instead, it is a toggle which modifies the behavior of the\n"
+msgstr ""
+
+#: alpine/alpine.c:2380 alpine/alpine.c:2472
+msgid ""
+" other commands. You toggle Exceptions editing on and off with this\n"
+msgstr ""
+
+#: alpine/alpine.c:2381 alpine/alpine.c:2473
+msgid ""
+" command. When it is off you will be editing (changing) your regular\n"
+msgstr ""
+
+#: alpine/alpine.c:2382 alpine/alpine.c:2474
+msgid ""
+" configuration file. When it is on you will be editing your exceptions\n"
+msgstr ""
+
+#: alpine/alpine.c:2383 alpine/alpine.c:2475
+msgid ""
+" configuration file. For example, you might want to type the command \n"
+msgstr ""
+
+#: alpine/alpine.c:2384 alpine/alpine.c:2476
+msgid ""
+" \"eXceptions\" followed by \"Kolor\" to setup different screen colors\n"
+msgstr ""
+
+#: alpine/alpine.c:2385 alpine/alpine.c:2477
+msgid " on a particular platform.\n"
+msgstr ""
+
+#: alpine/alpine.c:2386 alpine/alpine.c:2462
+msgid ""
+" (Note: this command does not show up on the keymenu at the bottom of\n"
+msgstr ""
+
+#: alpine/alpine.c:2387 alpine/alpine.c:2463
+msgid ""
+" the screen unless you press \"O\" for \"Other Commands\" --but you "
+"don't\n"
+msgstr ""
+
+#: alpine/alpine.c:2388 alpine/alpine.c:2464
+msgid " need to press the \"O\" in order to invoke the command.)\n"
+msgstr ""
+
+#: alpine/alpine.c:2393
+msgid "(P) Printer:\n"
+msgstr ""
+
+#: alpine/alpine.c:2394
+msgid " Allows you to set a default printer and to define custom\n"
+msgstr ""
+
+#: alpine/alpine.c:2395
+msgid " print commands.\n"
+msgstr ""
+
+#: alpine/alpine.c:2400
+msgid "(N) Newpassword:\n"
+msgstr ""
+
+#: alpine/alpine.c:2401
+msgid " Change your password.\n"
+msgstr ""
+
+#: alpine/alpine.c:2406
+msgid "(C) Config:\n"
+msgstr ""
+
+#: alpine/alpine.c:2407
+msgid " Allows you to set or unset many features of Alpine.\n"
+msgstr ""
+
+#: alpine/alpine.c:2408
+msgid " You may also set the values of many options with this command.\n"
+msgstr ""
+
+#: alpine/alpine.c:2413
+msgid "(S) Signature:\n"
+msgstr ""
+
+#: alpine/alpine.c:2414
+msgid " Enter or edit a custom signature which will\n"
+msgstr ""
+
+#: alpine/alpine.c:2415
+msgid " be included with each new message you send.\n"
+msgstr ""
+
+#: alpine/alpine.c:2419
+msgid "(A) AddressBooks:\n"
+msgstr ""
+
+#: alpine/alpine.c:2420
+msgid " Define a non-default address book.\n"
+msgstr ""
+
+#: alpine/alpine.c:2423
+msgid "(L) collectionLists:\n"
+msgstr ""
+
+#: alpine/alpine.c:2424
+msgid ""
+" You may define groups of folders to help you better organize your mail.\n"
+msgstr ""
+
+#: alpine/alpine.c:2427
+msgid "(R) Rules:\n"
+msgstr ""
+
+#: alpine/alpine.c:2428
+msgid " This has up to six sub-categories: Roles, Index Colors, Filters,\n"
+msgstr ""
+
+#: alpine/alpine.c:2429
+msgid " SetScores, Search, and Other. If the Index Colors option is\n"
+msgstr ""
+
+#: alpine/alpine.c:2430
+msgid " missing you may turn it on (if possible) with Setup/Kolor.\n"
+msgstr ""
+
+#: alpine/alpine.c:2431
+msgid ""
+" If Roles is missing it has probably been administratively disabled.\n"
+msgstr ""
+
+#: alpine/alpine.c:2435
+msgid "(D) Directory:\n"
+msgstr ""
+
+#: alpine/alpine.c:2436
+msgid ""
+" Define an LDAP Directory server for Alpine's use. A directory server is\n"
+msgstr ""
+
+#: alpine/alpine.c:2437
+msgid " similar to an address book, but it is usually maintained by an\n"
+msgstr ""
+
+#: alpine/alpine.c:2438
+msgid " organization. It is similar to a telephone directory.\n"
+msgstr ""
+
+#: alpine/alpine.c:2442
+msgid "(K) Kolor:\n"
+msgstr ""
+
+#: alpine/alpine.c:2443
+msgid ""
+" Set custom colors for various parts of the Alpine screens. For example, "
+"the\n"
+msgstr ""
+
+#: alpine/alpine.c:2444
+msgid ""
+" command key labels, the titlebar at the top of each page, and quoted\n"
+msgstr ""
+
+#: alpine/alpine.c:2445
+msgid " sections of messages you are viewing.\n"
+msgstr ""
+
+#: alpine/alpine.c:2449
+msgid "(M) S/MIME:\n"
+msgstr ""
+
+#: alpine/alpine.c:2450
+msgid " Setup for using S/MIME to verify signed messages, decrypt\n"
+msgstr ""
+
+#: alpine/alpine.c:2451
+msgid " encrypted messages, and to sign or encrypt outgoing messages.\n"
+msgstr ""
+
+#: alpine/alpine.c:2455
+msgid "(Z) RemoteConfigSetup:\n"
+msgstr ""
+
+#: alpine/alpine.c:2456
+msgid ""
+" This is a command you will probably only want to use once, if at all.\n"
+msgstr ""
+
+#: alpine/alpine.c:2457
+msgid ""
+" It helps you transfer your Alpine configuration data to an IMAP server,\n"
+msgstr ""
+
+#: alpine/alpine.c:2458
+msgid ""
+" where it will be accessible from any of the computers you read mail\n"
+msgstr ""
+
+#: alpine/alpine.c:2459
+msgid ""
+" from (using Alpine). The idea behind a remote configuration is that you\n"
+msgstr ""
+
+#: alpine/alpine.c:2460
+msgid ""
+" can change your configuration in one place and have that change show\n"
+msgstr ""
+
+#: alpine/alpine.c:2461
+msgid " up on all of the computers you use.\n"
+msgstr ""
+
+#: alpine/alpine.c:2478
+msgid ""
+" (Note: this command does not do anything unless you have a "
+"configuration\n"
+msgstr ""
+
+#: alpine/alpine.c:2479
+msgid ""
+" with exceptions enabled (you don't have that). Common ways to enable an\n"
+msgstr ""
+
+#: alpine/alpine.c:2480
+msgid ""
+" exceptions config are the command line argument \"-x <exception_config>"
+"\";\n"
+msgstr ""
+
+#: alpine/alpine.c:2481
+msgid ""
+" or the existence of the file \".pinercex\" for Unix Alpine, or \"PINERCEX"
+"\")\n"
+msgstr ""
+
+#: alpine/alpine.c:2482
+msgid " for PC-Alpine.)\n"
+msgstr ""
+
+#: alpine/alpine.c:2483
+msgid ""
+" (Another note: this command does not show up on the keymenu at the "
+"bottom\n"
+msgstr ""
+
+#: alpine/alpine.c:2484
+msgid ""
+" of the screen unless you press \"O\" for \"Other Commands\" --but you\n"
+msgstr ""
+
+#: alpine/alpine.c:2485
+msgid " don't need to press the \"O\" in order to invoke the command.)\n"
+msgstr ""
+
+#: alpine/alpine.c:2491
+msgid "Information About Setup Command"
+msgstr ""
+
+#: alpine/alpine.c:2613
+msgid "Config file not changeable"
+msgstr ""
+
+#: alpine/alpine.c:2618
+msgid "Problem accessing configuration"
+msgstr ""
+
+#: alpine/alpine.c:2627 alpine/alpine.c:2649
+msgid "SIGNATURE EDITOR"
+msgstr ""
+
+#: alpine/alpine.c:2697
+msgid "Type of rule setup : "
+msgstr ""
+
+#: alpine/alpine.c:2716
+msgid "Try turning on color with the Setup/Kolor command."
+msgstr ""
+
+#: alpine/alpine.c:2759
+msgid "Password changing not configured for this version of Alpine."
+msgstr ""
+
+#: alpine/alpine.c:3094
+msgid "Quit even though new mail just arrived"
+msgstr ""
+
+#: alpine/alpine.c:3103
+msgid "Really quit Alpine"
+msgstr ""
+
+#: alpine/alpine.c:3127
+msgid "Alpine finished"
+msgstr ""
+
+#: alpine/alpine.c:3337
+#, c-format
+msgid ""
+"No more available memory.\n"
+"Alpine Exiting"
+msgstr ""
+
+#: alpine/alpine.c:3339
+#, c-format
+msgid ""
+"Problem detected: \"%s\".\n"
+"Alpine Exiting."
+msgstr ""
+
+#: alpine/alpine.c:3553
+msgid "PC-Alpine MAIN MENU Help"
+msgstr ""
+
+#: alpine/folder.c:62
+msgid "Enter newsgroup name (or partial name to get a list): "
+msgstr ""
+
+#: alpine/folder.c:63
+msgid "Use \"X\" to mark selections in list mode"
+msgstr ""
+
+#: alpine/folder.c:64
+msgid "ALTER folder selection : "
+msgstr ""
+
+#: alpine/folder.c:65
+msgid "Select by folder Name or Contents ? "
+msgstr ""
+
+#: alpine/folder.c:66
+msgid "Select by which folder property ? "
+msgstr ""
+
+#: alpine/folder.c:68
+msgid "Folder and directory of the same name will be deleted. Continue"
+msgstr ""
+
+#. TRANSLATORS: The all upper case things are screen titles
+#: alpine/folder.c:251
+msgid "FOLDER LIST"
+msgstr ""
+
+#: alpine/folder.c:254
+msgid "HELP FOR FOLDERS"
+msgstr ""
+
+#: alpine/folder.c:358
+#, c-format
+msgid "SETUP EXCEPTIONS COLLECTION LIST"
+msgstr ""
+
+#: alpine/folder.c:359
+#, c-format
+msgid "HELP FOR SETUP EXCEPTIONS COLLECTIONS"
+msgstr ""
+
+#: alpine/folder.c:362
+#, c-format
+msgid "SETUP COLLECTION LIST"
+msgstr ""
+
+#: alpine/folder.c:363
+#, c-format
+msgid "HELP FOR SETUP COLLECTIONS"
+msgstr ""
+
+#. TRANSLATORS: Print something1 using something2.
+#. contexts is something1
+#: alpine/folder.c:370 alpine/folder.c:850
+msgid "contexts"
+msgstr ""
+
+#: alpine/folder.c:411
+msgid "GOTO: SELECT FOLDER"
+msgstr ""
+
+#: alpine/folder.c:414
+msgid "HELP FOR OPENING FOLDERS"
+msgstr ""
+
+#: alpine/folder.c:423 alpine/folder.c:482
+msgid "All folders displayed for Incoming Collection"
+msgstr ""
+
+#: alpine/folder.c:470
+msgid "SAVE: SELECT FOLDER"
+msgstr ""
+
+#: alpine/folder.c:473
+msgid "HELP FOR SAVING MESSAGES TO FOLDERS"
+msgstr ""
+
+#: alpine/folder.c:527
+msgid "SUBSCRIBE: SELECT FOLDER"
+msgstr ""
+
+#: alpine/folder.c:530
+msgid "HELP SELECTING NEWSGROUP TO SUBSCRIBE TO"
+msgstr ""
+
+#: alpine/folder.c:567
+msgid "NEWS: SELECT GROUP"
+msgstr ""
+
+#: alpine/folder.c:570
+msgid "HELP FOR SELECTING NEWSGROUP TO POST TO"
+msgstr ""
+
+#: alpine/folder.c:649
+#, c-format
+msgid "List of folders matching \"%s*\""
+msgstr ""
+
+#: alpine/folder.c:681
+msgid "FCC: SELECT FOLDER"
+msgstr ""
+
+#: alpine/folder.c:684
+msgid "HELP FOR SELECTING THE FCC"
+msgstr ""
+
+#: alpine/folder.c:755
+msgid "SELECT FOLDER"
+msgstr ""
+
+#: alpine/folder.c:760
+msgid "HELP FOR SELECTING CURRENT FOLDER"
+msgstr ""
+
+#: alpine/folder.c:764 alpine/folder.c:768
+msgid "HELP FOR SELECTING FOLDER"
+msgstr ""
+
+#: alpine/folder.c:849
+msgid "COLLECTION LIST"
+msgstr ""
+
+#: alpine/folder.c:854
+msgid "HELP FOR COLLECTION LIST"
+msgstr ""
+
+#: alpine/folder.c:879 alpine/folder.c:963
+msgid "Path"
+msgstr ""
+
+#: alpine/folder.c:912
+#, c-format
+msgid "FOLDER COLLECTION %s"
+msgstr ""
+
+#: alpine/folder.c:923
+msgid ""
+"\n"
+" Fill in the fields above to add a Folder Collection to your"
+msgstr ""
+
+#: alpine/folder.c:925
+msgid ""
+"\n"
+" COLLECTION LIST screen."
+msgstr ""
+
+#: alpine/folder.c:927
+msgid ""
+"\n"
+" Use the \"^G\" command to get help specific to each item, and"
+msgstr ""
+
+#: alpine/folder.c:929
+msgid ""
+"\n"
+" use \"^X\" when finished."
+msgstr ""
+
+#: alpine/folder.c:1188
+msgid "Problem testing for directory existence"
+msgstr ""
+
+#: alpine/folder.c:1194
+#, c-format
+msgid "Exit and save changes"
+msgstr ""
+
+#: alpine/folder.c:1196
+#, c-format
+msgid "Exit, saving changes and creating Path"
+msgstr ""
+
+#: alpine/folder.c:1227 pico/browse.c:2199
+msgid "Edit"
+msgstr ""
+
+#: alpine/folder.c:1239
+msgid "Cancel Add (answering \"Yes\" will abandon any changes made) "
+msgstr ""
+
+#: alpine/folder.c:1242
+#, c-format
+msgid "%s Cancelled) "
+msgstr ""
+
+#: alpine/folder.c:1307
+msgid "Fetching default directory"
+msgstr ""
+
+#: alpine/folder.c:1351
+#, c-format
+msgid "Already subscribed to \"%s\""
+msgstr ""
+
+#: alpine/folder.c:1395
+msgid "Fetching folder data"
+msgstr ""
+
+#: alpine/folder.c:1820
+msgid "[No Folders in Collection]"
+msgstr ""
+
+#: alpine/folder.c:1837
+msgid "[Select Here to See Expanded List]"
+msgstr ""
+
+#: alpine/folder.c:2152
+msgid "Already in List Mode"
+msgstr ""
+
+#: alpine/folder.c:2156
+msgid "No Folders! Can't enter List Mode"
+msgstr ""
+
+#: alpine/folder.c:2182
+msgid "No Folders! Nothing to View"
+msgstr ""
+
+#: alpine/folder.c:2335 alpine/folder.c:5125
+msgid "Empty folder collection. No folder to rename!"
+msgstr ""
+
+#: alpine/folder.c:2345
+msgid "Empty folder collection. No folder to delete!"
+msgstr ""
+
+#: alpine/folder.c:2382
+msgid "May only shuffle Incoming-Folders."
+msgstr ""
+
+#: alpine/folder.c:2385
+msgid "No folders to shuffle."
+msgstr ""
+
+#: alpine/folder.c:2388
+msgid "Shuffle only makes sense with more than one folder."
+msgstr ""
+
+#: alpine/folder.c:2494
+msgid "Empty folder collection. No folder to select!"
+msgstr ""
+
+#: alpine/folder.c:2510
+msgid "Folder List Zoom mode is now off"
+msgstr ""
+
+#: alpine/folder.c:2514
+#, c-format
+msgid "In Zoomed list of %s folders. Use \"Z\" to restore regular list"
+msgstr ""
+
+#: alpine/folder.c:2536
+msgid "No selected folders to Zoom on"
+msgstr ""
+
+#: alpine/folder.c:2539
+msgid "No Folders to Zoom on!"
+msgstr ""
+
+#. TRANSLATORS: we are asking for confirmation about abandonding selections
+#. in the address book.
+#: alpine/folder.c:2559 alpine/addrbook.c:3001
+msgid "Really abandon your selections "
+msgstr ""
+
+#: alpine/folder.c:2582
+msgid "Invalid Folder Name"
+msgstr ""
+
+#: alpine/folder.c:2601
+#, c-format
+msgid "%lu total message%s, %lu of them recent"
+msgstr ""
+
+#: alpine/folder.c:2605
+#, c-format
+msgid "%s: Trouble checking for recent mail"
+msgstr ""
+
+#: alpine/folder.c:2612
+msgid "No folder to check! Can't get recent info"
+msgstr ""
+
+#: alpine/folder.c:2706 alpine/folder.c:3673
+msgid "Empty folder list!"
+msgstr ""
+
+#: alpine/folder.c:2756
+msgid "Name of new folder : "
+msgstr ""
+
+#: alpine/folder.c:2768 alpine/folder.c:4336 alpine/folder.c:5202
+msgid "Folder name can't begin with dot"
+msgstr ""
+
+#: alpine/folder.c:2771 alpine/folder.c:4339
+#, c-format
+msgid "Config feature \"%s\" enables names beginning with dot"
+msgstr ""
+
+#: alpine/folder.c:2779
+#, c-format
+msgid "Can't add folder named %s"
+msgstr ""
+
+#. TRANSLATORS: go to parent directory one level up
+#: alpine/folder.c:2816 alpine/folder.c:2931
+msgid "ParentDir"
+msgstr ""
+
+#. TRANSLATORS: go to Collection List
+#: alpine/folder.c:2822 alpine/folder.c:2950
+msgid "ClctnList"
+msgstr ""
+
+#: alpine/folder.c:2840 alpine/folder.c:3004 alpine/folder.c:3011
+msgid "View Dir"
+msgstr ""
+
+#. TRANSLATORS: New Messages
+#: alpine/folder.c:2893
+msgid "NewMsgs"
+msgstr ""
+
+#: alpine/folder.c:3114
+msgid "Properties"
+msgstr ""
+
+#: alpine/folder.c:3145
+#, c-format
+msgid "Select \"%s\" not supported in Incoming Folders"
+msgstr ""
+
+#: alpine/folder.c:3159 alpine/folder.c:3246
+msgid "Unsupported Select option"
+msgstr ""
+
+#: alpine/folder.c:3282
+msgid "No change resulted"
+msgstr ""
+
+#: alpine/folder.c:3283
+msgid "No messages in intersection"
+msgstr ""
+
+#: alpine/folder.c:3288
+msgid "No change resulted. Matching folders already selected."
+msgstr ""
+
+#: alpine/folder.c:3382
+#, c-format
+msgid "Now in directory: %s"
+msgstr ""
+
+#: alpine/folder.c:3387
+msgid "Returned to collection's top directory"
+msgstr ""
+
+#: alpine/folder.c:3505 pith/mailcmd.c:566
+#, c-format
+msgid "Can't find Incoming Folder %s."
+msgstr ""
+
+#: alpine/folder.c:3620
+msgid "Error creating file"
+msgstr ""
+
+#: alpine/folder.c:3635
+msgid "Copying folder"
+msgstr ""
+
+#: alpine/folder.c:3648
+#, c-format
+msgid "Error exporting to %s"
+msgstr ""
+
+#: alpine/folder.c:3659
+#, c-format
+msgid "No messages in %s to export"
+msgstr ""
+
+#: alpine/folder.c:3662
+msgid "Can't open folder for exporting"
+msgstr ""
+
+#: alpine/folder.c:3726
+#, c-format
+msgid "Can't import file outside of %s"
+msgstr ""
+
+#: alpine/folder.c:3786
+#, c-format
+msgid "Error importing to %s"
+msgstr ""
+
+#: alpine/folder.c:3792
+#, c-format
+msgid "No messages in file %s"
+msgstr ""
+
+#: alpine/folder.c:3796
+#, c-format
+msgid "Can't open file %s for import"
+msgstr ""
+
+#: alpine/folder.c:3919
+msgid "Cancelled: config file not editable"
+msgstr ""
+
+#: alpine/folder.c:4020
+msgid "Use Inbox Host"
+msgstr ""
+
+#. TRANSLATORS: a mail drop is a place where mail is copied to so you
+#. can read it.
+#: alpine/folder.c:4035
+msgid "Use a Mail Drop"
+msgstr ""
+
+#: alpine/folder.c:4041
+msgid "Do Not use a Mail Drop"
+msgstr ""
+
+#: alpine/folder.c:4050
+#, c-format
+msgid "Name of Mail Drop server : "
+msgstr ""
+
+#: alpine/folder.c:4052
+#, c-format
+msgid "Name of server to contain destination folder : "
+msgstr ""
+
+#: alpine/folder.c:4054
+#, c-format
+msgid "Name of Inbox server : "
+msgstr ""
+
+#: alpine/folder.c:4056
+#, c-format
+msgid "Name of server to contain added folder : "
+msgstr ""
+
+#: alpine/folder.c:4141 alpine/folder.c:4144
+msgid "HELP FOR DESTINATION SERVER "
+msgstr ""
+
+#: alpine/folder.c:4146 alpine/folder.c:4149
+msgid "HELP FOR MAILDROP NAME "
+msgstr ""
+
+#: alpine/folder.c:4152
+msgid "HELP FOR INBOX SERVER "
+msgstr ""
+
+#: alpine/folder.c:4155
+msgid "HELP FOR SERVER NAME "
+msgstr ""
+
+#: alpine/folder.c:4184 alpine/folder.c:4378 alpine/folder.c:4502
+msgid "INBOX change cancelled"
+msgstr ""
+
+#: alpine/folder.c:4185 alpine/folder.c:4379 alpine/folder.c:4452
+#: alpine/folder.c:4503
+msgid "Addition of new folder cancelled"
+msgstr ""
+
+#: alpine/folder.c:4299
+msgid "Create Folder"
+msgstr ""
+
+#: alpine/folder.c:4304
+msgid "Create Directory"
+msgstr ""
+
+#: alpine/folder.c:4326
+msgid "#move folders may only be the INBOX or in the Incoming Collection"
+msgstr ""
+
+#: alpine/folder.c:4361
+msgid "Can't have trailing directory delimiters!"
+msgstr ""
+
+#: alpine/folder.c:4373
+msgid "HELP FOR FOLDER NAME "
+msgstr ""
+
+#: alpine/folder.c:4439
+msgid "A subfolder name is required, there is no default subfolder name"
+msgstr ""
+
+#: alpine/folder.c:4448
+msgid "HELP FOR SUBFOLDER NAME "
+msgstr ""
+
+#: alpine/folder.c:4465
+#, c-format
+msgid "Cannot add folder %s in current context"
+msgstr ""
+
+#: alpine/folder.c:4479
+#, c-format
+msgid "Nickname for folder \"%s\" : "
+msgstr ""
+
+#: alpine/folder.c:4492
+#, c-format
+msgid "Nickname cannot be \"%s\""
+msgstr ""
+
+#: alpine/folder.c:4535
+#, c-format
+msgid "Incoming folder \"%s\" already exists"
+msgstr ""
+
+#: alpine/folder.c:4565
+#, c-format
+msgid "Error checking for %s"
+msgstr ""
+
+#: alpine/folder.c:4806
+msgid "To All Grps"
+msgstr ""
+
+#: alpine/folder.c:4840
+msgid "Fetching newsgroup list"
+msgstr ""
+
+#: alpine/folder.c:4875
+msgid "No group substring to match! Use ^T to list all news groups."
+msgstr ""
+
+#: alpine/folder.c:4929
+msgid "No groups to select from!"
+msgstr ""
+
+#: alpine/folder.c:4932
+#, c-format
+msgid "News group \"%s\" didn't match any existing groups"
+msgstr ""
+
+#: alpine/folder.c:4978
+msgid "Subscribe cancelled"
+msgstr ""
+
+#: alpine/folder.c:5008 alpine/folder.c:5056 alpine/folder.c:5073
+#, c-format
+msgid "Error subscribing to \"%s\""
+msgstr ""
+
+#: alpine/folder.c:5039
+msgid "Subscriptions failed, subscribed to no new groups"
+msgstr ""
+
+#: alpine/folder.c:5079
+#, c-format
+msgid "Subscribed to \"%s\""
+msgstr ""
+
+#: alpine/folder.c:5120
+msgid "Can't rename news groups!"
+msgstr ""
+
+#: alpine/folder.c:5133
+#, c-format
+msgid "Can't change special folder name \"%s\""
+msgstr ""
+
+#: alpine/folder.c:5163
+msgid "Rename cancelled: folder not in editable config file"
+msgstr ""
+
+#: alpine/folder.c:5178
+msgid "nickname"
+msgstr ""
+
+#: alpine/folder.c:5180 pith/bldaddr.c:657
+msgid "directory"
+msgstr ""
+
+#: alpine/folder.c:5180
+msgid "folder"
+msgstr ""
+
+#: alpine/folder.c:5205
+msgid "Config feature \"s\" enables names beginning with dot"
+msgstr ""
+
+#: alpine/folder.c:5214
+#, c-format
+msgid "Folder \"%s\" already exists"
+msgstr ""
+
+#: alpine/folder.c:5223
+#, c-format
+msgid "Can't rename incoming folder to %s"
+msgstr ""
+
+#: alpine/folder.c:5226
+#, c-format
+msgid "Can't rename folder to %s"
+msgstr ""
+
+#: alpine/folder.c:5250
+msgid "Folder rename cancelled"
+msgstr ""
+
+#: alpine/folder.c:5396
+#, c-format
+msgid "Really unsubscribe from \"%s\""
+msgstr ""
+
+#: alpine/folder.c:5417
+#, c-format
+msgid "Error unsubscribing from \"%s\""
+msgstr ""
+
+#: alpine/folder.c:5475
+#, c-format
+msgid "Can't delete special folder \"%s\"."
+msgstr ""
+
+#: alpine/folder.c:5481
+msgid "Deletion cancelled: folder not in editable config file"
+msgstr ""
+
+#: alpine/folder.c:5506
+#, c-format
+msgid "Can't delete non-empty directory \"%s\"%s."
+msgstr ""
+
+#: alpine/folder.c:5518
+msgid "No folder deleted"
+msgstr ""
+
+#: alpine/folder.c:5525
+msgid "Nickname only"
+msgstr ""
+
+#: alpine/folder.c:5526
+msgid "Both Folder and Nickname"
+msgstr ""
+
+#: alpine/folder.c:5529
+msgid "DELETE only Nickname or Both nickname and folder? "
+msgstr ""
+
+#: alpine/folder.c:5555
+msgid "Nothing deleted"
+msgstr ""
+
+#: alpine/folder.c:5685
+msgid "folder list"
+msgstr ""
+
+#: alpine/folder.c:5700
+#, c-format
+msgid "String in folder %s to match : "
+msgstr ""
+
+#: alpine/folder.c:5733
+msgid "Name Select"
+msgstr ""
+
+#: alpine/folder.c:5734
+msgid "Content Select"
+msgstr ""
+
+#: alpine/folder.c:5740
+msgid "Select \"Text\" not supported in Incoming Folders"
+msgstr ""
+
+#: alpine/folder.c:5872
+msgid "Unseen msgs"
+msgstr ""
+
+#: alpine/folder.c:5873
+msgid "New msgs"
+msgstr ""
+
+#: alpine/folder.c:5874
+msgid "msg Count"
+msgstr ""
+
+#: alpine/folder.c:5880
+msgid "Select \"Properties\" not supported in Incoming Folders"
+msgstr ""
+
+#: alpine/folder.c:5948
+msgid "Toggle Comparison"
+msgstr ""
+
+#: alpine/folder.c:6348
+#, c-format
+msgid "Check of folder %s failed. Continue "
+msgstr ""
+
+#: alpine/folder.c:6350 alpine/folder.c:6353
+#, c-format
+msgid "Check of %s failed. Continue "
+msgstr ""
+
+#: alpine/folder.c:6355
+#, c-format
+msgid "Check failed. Continue "
+msgstr ""
+
+#: alpine/folder.c:6414
+msgid "Cannot shuffle INBOX"
+msgstr ""
+
+#: alpine/folder.c:6445
+msgid "Shuffle cancelled: config file not editable"
+msgstr ""
+
+#: alpine/folder.c:6458
+msgid "Back"
+msgstr ""
+
+#: alpine/folder.c:6482
+msgid "Shuffle cancelled: unexpected trouble shuffling"
+msgstr ""
+
+#: alpine/folder.c:6496
+msgid "Cannot shuffle from one config file to another."
+msgstr ""
+
+#: alpine/folder.c:6670
+msgid "Can't post messages, NNTP server needs to be configured"
+msgstr ""
+
+#: alpine/folder.c:6677
+msgid "Getting full list of groups for posting"
+msgstr ""
+
+#. TRANSLATORS: the first %s is the folder type, it may be address book or
+#. configuration. The second %s is the folder name. Of course, the HTML
+#. tags should be left as is.
+#: alpine/remote.c:106
+#, c-format
+msgid ""
+"<HTML><P>The data in the remote %s folder<P><CENTER>%s</CENTER><P>looks "
+"suspicious. The reason for the suspicion is<P><CENTER>"
+msgstr ""
+
+#: alpine/remote.c:112
+#, c-format
+msgid ""
+"header \"%s\" is missing</CENTER><P>The special header \"%s\" is missing "
+"from the last message in the folder. This indicates that something is wrong. "
+"You should probably answer \"No\" so that you don't use the corrupt data. "
+"Then you should investigate further."
+msgstr ""
+
+#: alpine/remote.c:118
+msgid ""
+"\"Received\" headers detected</CENTER><P>The last message in the folder "
+"contains \"Received\" headers. This usually indicates that the message was "
+"put there by the mail delivery system. Alpine does not add those Received "
+"headers. You should probably answer \"No\" so that you don't use the corrupt "
+"data. Then you should investigate further."
+msgstr ""
+
+#: alpine/remote.c:121
+#, c-format
+msgid ""
+"Unexpected value for header \"%s\"</CENTER><P>The special header \"%s\" in "
+"the last message in the folder has an unexpected value (%s) after it. This "
+"probably indicates that something is wrong. This value would not normally be "
+"put there by Alpine. You should probably answer \"No\" so that you don't use "
+"the corrupt data. Then you should investigate further."
+msgstr ""
+
+#: alpine/remote.c:127
+#, c-format
+msgid ""
+"Unexpected value for header \"%s\"</CENTER><P>The special header \"%s\" in "
+"the last message in the folder has an unexpected value (1) after it. It "
+"appears that it may have been put there by a Pine with a version number less "
+"than 4.50. If you believe that you have changed this data with an older Pine "
+"more recently than you've changed it with this version of Alpine, then you "
+"can probably safely answer \"Yes\". If you do not understand why this has "
+"happened, you should probably answer \"No\" so that you don't use the "
+"corrupt data. Then you should investigate further."
+msgstr ""
+
+#: alpine/remote.c:133
+#, c-format
+msgid ""
+"Unexpected value for header \"%s\"</CENTER><P>The special header \"%s\" in "
+"the last message in the folder has an unexpected value (%s) after it. This "
+"is the right sort of value that Alpine would normally put there, but it "
+"doesn't match the value from the first message in the folder. This may "
+"indicate that something is wrong. Unless you understand why this has "
+"happened, you should probably answer \"No\" so that you don't use the "
+"corrupt data. Then you should investigate further."
+msgstr ""
+
+#: alpine/remote.c:156
+msgid "REMOTE DATA FORGERY WARNING"
+msgstr ""
+
+#: alpine/remote.c:181
+#, c-format
+msgid ""
+"\n"
+"The data in the remote %s folder\n"
+"\n"
+" %s\n"
+"\n"
+"looks suspicious. The reason for the suspicion is\n"
+"\n"
+" "
+msgstr ""
+
+#: alpine/remote.c:187
+#, c-format
+msgid ""
+"header \"%s\" is missing\n"
+"\n"
+"The special header \"%s\" is missing from the last message\n"
+"in the folder. This indicates that something is wrong.\n"
+"You should probably answer \"No\" so that you don't use the corrupt data.\n"
+"Then you should investigate further.\n"
+"\n"
+msgstr ""
+
+#: alpine/remote.c:192
+#, c-format
+msgid ""
+"\"Received\" headers detected\n"
+"\n"
+"The last message in the folder contains \"Received\" headers.\n"
+"This usually indicates that the message was put there by the\n"
+"mail delivery system. Alpine does not add those Received headers.\n"
+"You should probably answer \"No\" so that you don't use the corrupt data.\n"
+"Then you should investigate further.\n"
+"\n"
+msgstr ""
+
+#: alpine/remote.c:196
+#, c-format
+msgid ""
+"Unexpected value for header \"%s\"\n"
+"\n"
+"The special header \"%s\" in the last message in the folder\n"
+"has an unexpected value (%s) after it. This probably\n"
+"indicates that something is wrong. This value would not normally be put\n"
+"there by Alpine. You should probably answer \"No\" so that you don't use\n"
+"the corrupt data. Then you should investigate further.\n"
+"\n"
+msgstr ""
+
+#: alpine/remote.c:201
+#, c-format
+msgid ""
+"Unexpected value for header \"%s\"\n"
+"\n"
+"The special header \"%s\" in the last message in the folder\n"
+"has an unexpected value (1) after it. It appears that it may have been\n"
+"put there by a Pine with a version number less than 4.50.\n"
+"If you believe that you have changed this data with an older Pine more\n"
+"recently than you've changed it with this version of Alpine, then you can\n"
+"probably safely answer \"Yes\". If you do not understand why this has\n"
+"happened, you should probably answer \"No\" so that you don't use the\n"
+"corrupt data. Then you should investigate further.\n"
+"\n"
+msgstr ""
+
+#: alpine/remote.c:206
+#, c-format
+msgid ""
+"Unexpected value for header \"%s\"\n"
+"\n"
+"The special header \"%s\" in the last message in the folder\n"
+"has an unexpected\n"
+"value (%s) after it. This is\n"
+"the right sort of value that Alpine would normally put there, but it\n"
+"doesn't match the value from the first message in the folder. This may\n"
+"indicate that something is wrong. Unless you understand why this has "
+"happened,\n"
+"you should probably answer \"No\" so that you don't use the\n"
+"corrupt data. Then you should investigate further.\n"
+"\n"
+msgstr ""
+
+#: alpine/remote.c:213
+#, c-format
+msgid "Suspicious data in \"%s\": Continue anyway "
+msgstr ""
+
+#: alpine/remote.c:226
+#, c-format
+msgid "Can't open remote %s"
+msgstr ""
+
+#. TRANSLATORS: The argument is the command name being piped to.
+#: alpine/pipe.c:144
+#, c-format
+msgid "Error opening pipe: %s"
+msgstr ""
+
+#: alpine/setup.c:48
+msgid "SETUP CONFIGURATION"
+msgstr ""
+
+#: alpine/setup.c:49
+msgid "SETUP CONFIGURATION EXCEPTIONS"
+msgstr ""
+
+#. TRANSLATORS: Print something1 using something2.
+#. configuration is something1
+#: alpine/setup.c:436 alpine/flagmaint.c:167 alpine/colorconf.c:106
+#: alpine/colorconf.c:2782 alpine/smime.c:612
+msgid "configuration"
+msgstr ""
+
+#: alpine/setup.c:526 alpine/confscroll.c:1861
+msgid "Leave unset and use default "
+msgstr ""
+
+#: alpine/setup.c:688 alpine/setup.c:835
+#, c-format
+msgid "Turn on incoming folders with Config feature \"%s\""
+msgstr ""
+
+#: alpine/setup.c:707 alpine/setup.c:728 alpine/setup.c:740
+msgid "Using default, monitor all incoming folders"
+msgstr ""
+
+#: alpine/setup.c:726
+msgid "List is unchanged"
+msgstr ""
+
+#: alpine/setup.c:823
+msgid "Incoming folders to be monitored"
+msgstr ""
+
+#: alpine/setup.c:841
+msgid "SELECT FOLDERS TO MONITOR"
+msgstr ""
+
+#: alpine/setup.c:841
+msgid "folders"
+msgstr ""
+
+#: alpine/setup.c:843
+msgid "HELP FOR SELECTING FOLDERS"
+msgstr ""
+
+#. TRANSLATORS: Set and Rule Values are the headings for an option
+#. that can take one of several values. Underneath the Set heading
+#. will be a column where one possibility is turned on (is Set).
+#. The other column will be very short descriptions of what
+#. the possibilities are (the Rule Values).
+#: alpine/flagmaint.c:116 alpine/confscroll.c:248
+msgid "Set"
+msgstr ""
+
+#: alpine/flagmaint.c:116
+msgid "Flag/Keyword Name"
+msgstr ""
+
+#: alpine/flagmaint.c:166
+msgid "FLAG MAINTENANCE"
+msgstr ""
+
+#: alpine/flagmaint.c:194
+msgid "Keyword to be added : "
+msgstr ""
+
+#: alpine/flagmaint.c:212
+msgid "Keyword already configured, changing nickname"
+msgstr ""
+
+#: alpine/flagmaint.c:217
+#, c-format
+msgid "Optional nickname for \"%s\" : "
+msgstr ""
+
+#: alpine/confscroll.c:55
+msgid "HELP FOR SETUP CONFIGURATION"
+msgstr ""
+
+#. TRANSLATORS: Empty Value is what is shown in the configuration
+#. screen when the user not only does not set an option but also
+#. wants to explicitly not use the default value. Empty value means
+#. an option with no value.
+#: alpine/confscroll.c:61
+msgid "Empty Value"
+msgstr ""
+
+#: alpine/confscroll.c:62
+msgid "<Empty Value>"
+msgstr ""
+
+#. TRANSLATORS: No Value set is similar to Empty Value, but the
+#. user has not explicitly decided to not use the default. It is
+#. just an option which the user has left at the default value.
+#: alpine/confscroll.c:66
+msgid "No Value Set"
+msgstr ""
+
+#. TRANSLATORS: Value is Fixed is what is displayed in the config
+#. screen when the system managers have set an option to a specific
+#. value and they don't allow the user to change it. The value
+#. is fixed to a certain value. This isn't the same word as
+#. Repaired, it means Unchanging.
+#: alpine/confscroll.c:72
+msgid "Value is Fixed"
+msgstr ""
+
+#: alpine/confscroll.c:248
+msgid "Rule Values"
+msgstr ""
+
+#: alpine/confscroll.c:470
+msgid "Screen too small"
+msgstr ""
+
+#. TRANSLATORS: "Config file not changeable," is what replaces the %s
+#: alpine/confscroll.c:477 alpine/confscroll.c:1322
+#, c-format
+msgid "%s can't change options or settings"
+msgstr ""
+
+#: alpine/confscroll.c:479 alpine/confscroll.c:1324
+msgid "Config file not changeable,"
+msgstr ""
+
+#: alpine/confscroll.c:666 alpine/roleconf.c:6537
+msgid "No help yet."
+msgstr ""
+
+#: alpine/confscroll.c:740 alpine/confscroll.c:826
+msgid "Already at end of screen"
+msgstr ""
+
+#: alpine/confscroll.c:782 alpine/confscroll.c:871
+msgid "Already at start of screen"
+msgstr ""
+
+#: alpine/confscroll.c:1256
+msgid "Current item contains the only match"
+msgstr ""
+
+#: alpine/confscroll.c:1261
+msgid "Search wrapped: word found in text above current line"
+msgstr ""
+
+#: alpine/confscroll.c:1262
+msgid "Search wrapped: word found in text below current line"
+msgstr ""
+
+#: alpine/confscroll.c:1264
+msgid "Search wrapped to beginning: word found"
+msgstr ""
+
+#: alpine/confscroll.c:1267
+msgid "Word found in text above current line"
+msgstr ""
+
+#: alpine/confscroll.c:1268
+msgid "Word found in text below current line"
+msgstr ""
+
+#: alpine/confscroll.c:1290
+msgid "Moved to top"
+msgstr ""
+
+#: alpine/confscroll.c:1305
+msgid "Moved to bottom"
+msgstr ""
+
+#. TRANSLATORS: Command <command letter> not defined here.
+#. Leave the trailing %s which might be a parenthetical
+#. remark.
+#: alpine/confscroll.c:1339
+#, c-format
+msgid "Command \"%s\" not defined here.%s"
+msgstr ""
+
+#: alpine/confscroll.c:1639
+msgid "Decrease"
+msgstr ""
+
+#: alpine/confscroll.c:1643
+msgid "Increase"
+msgstr ""
+
+#: alpine/confscroll.c:1662
+msgid "Only single value allowed. Use \"Change\"."
+msgstr ""
+
+#: alpine/confscroll.c:1688
+msgid "Enter text to insert before \"%.*w\": "
+msgstr ""
+
+#: alpine/confscroll.c:1702 alpine/print.c:750
+msgid "Add To"
+msgstr ""
+
+#: alpine/confscroll.c:1704 alpine/print.c:752
+msgid "Replace or Add To default value ? "
+msgstr ""
+
+#: alpine/confscroll.c:1725 alpine/confscroll.c:1753
+#, c-format
+msgid "Enter the numeric text to be added : "
+msgstr ""
+
+#: alpine/confscroll.c:1727 alpine/confscroll.c:1755
+#, c-format
+msgid "Enter the text to be added : "
+msgstr ""
+
+#: alpine/confscroll.c:1739
+#, c-format
+msgid "Enter the numeric replacement text : "
+msgstr ""
+
+#: alpine/confscroll.c:1741
+#, c-format
+msgid "Enter the replacement text : "
+msgstr ""
+
+#. TRANSLATORS: Insert new item before current item
+#: alpine/confscroll.c:1774 alpine/print.c:803
+msgid "InsertBefore"
+msgstr ""
+
+#: alpine/confscroll.c:1774 alpine/print.c:803
+msgid "InsertAfter"
+msgstr ""
+
+#: alpine/confscroll.c:1835 alpine/confscroll.c:1843 alpine/print.c:892
+#, c-format
+msgid "Can't add %s to list"
+msgstr ""
+
+#: alpine/confscroll.c:1851 alpine/confscroll.c:2223
+msgid "Entry must be numeric"
+msgstr ""
+
+#: alpine/confscroll.c:1895
+#, c-format
+msgid "Enter text to insert after \"%.*s\": "
+msgstr ""
+
+#: alpine/confscroll.c:1897
+#, c-format
+msgid "Enter text to insert before \"%.*s\": "
+msgstr ""
+
+#: alpine/confscroll.c:1918 alpine/confscroll.c:2255
+#, c-format
+msgid "Minimum value is %s"
+msgstr ""
+
+#: alpine/confscroll.c:1943 alpine/confscroll.c:2272
+#, c-format
+msgid "Maximum value is %s"
+msgstr ""
+
+#: alpine/confscroll.c:1969 alpine/confscroll.c:1983 alpine/print.c:919
+#, c-format
+msgid "Override default with %s"
+msgstr ""
+
+#: alpine/confscroll.c:1999 alpine/print.c:934
+msgid "No set value to delete"
+msgstr ""
+
+#: alpine/confscroll.c:2003
+#, c-format
+msgid "Delete (unused) %s from %s "
+msgstr ""
+
+#: alpine/confscroll.c:2015
+#, c-format
+msgid "Really delete %s%s from %s "
+msgstr ""
+
+#: alpine/confscroll.c:2051
+msgid "Value not deleted"
+msgstr ""
+
+#: alpine/confscroll.c:2115
+#, c-format
+msgid "Change field %s list entry : "
+msgstr ""
+
+#: alpine/confscroll.c:2122
+#, c-format
+msgid "Change numeric field %s value : "
+msgstr ""
+
+#: alpine/confscroll.c:2124
+#, c-format
+msgid "Change field %s value : "
+msgstr ""
+
+#: alpine/confscroll.c:2294
+msgid "Can't shuffle single-valued setting"
+msgstr ""
+
+#: alpine/confscroll.c:2309
+msgid "Shuffle only makes sense when there is more than one value defined"
+msgstr ""
+
+#: alpine/confscroll.c:2738 alpine/confscroll.c:2965 alpine/colorconf.c:1220
+msgid "Delete old unused personal option setting"
+msgstr ""
+
+#: alpine/confscroll.c:4266
+msgid "<Ignored: using Literal-Signature instead>"
+msgstr ""
+
+#. TRANSLATORS: In the configuration screen, telling the user we
+#. can't change this option because the system administrator
+#. prohibits it.
+#: alpine/confscroll.c:4520 pith/conf.c:6849
+msgid "Can't change value fixed by sys-admin."
+msgstr ""
+
+#: alpine/confscroll.c:4785
+#, c-format
+msgid "Warning: \"%s\" is overridden in your exceptions configuration"
+msgstr ""
+
+#: alpine/confscroll.c:4811
+msgid "FIXING PINERC"
+msgstr ""
+
+#: alpine/confscroll.c:4815
+msgid "Some of your options conflict with site policy. Investigate"
+msgstr ""
+
+#: alpine/confscroll.c:4839
+#, c-format
+msgid "Your setting for %s is "
+msgstr ""
+
+#: alpine/confscroll.c:4859 alpine/confscroll.c:4868 alpine/confscroll.c:4872
+#, c-format
+msgid "Your setting for %s is %s"
+msgstr ""
+
+#: alpine/confscroll.c:4928 alpine/confscroll.c:4932
+msgid "ON"
+msgstr ""
+
+#: alpine/confscroll.c:4928 alpine/confscroll.c:4932
+msgid "OFF"
+msgstr ""
+
+#: alpine/confscroll.c:5016
+msgid "Using default value"
+msgstr ""
+
+#: alpine/confscroll.c:5027
+#, c-format
+msgid "User-Domain (%s) cannot contain \"@\"; using %s"
+msgstr ""
+
+#: alpine/confscroll.c:5036
+#, c-format
+msgid "User-domain (%s) cannot contain \"@\"; deleting"
+msgstr ""
+
+#: alpine/confscroll.c:5107 alpine/confscroll.c:5120 alpine/confscroll.c:5133
+#: alpine/confscroll.c:5140 pith/conf.c:6958
+msgid "This option has no effect without Enable-Incoming-Folders-Checking"
+msgstr ""
+
+#: alpine/confscroll.c:5175
+msgid "Initial command changes will affect your next Alpine session."
+msgstr ""
+
+#: alpine/confscroll.c:5228 alpine/confscroll.c:5244 pith/conf.c:6709
+msgid "This change has no effect because feature Use-System-Translation is on"
+msgstr ""
+
+#: alpine/confscroll.c:5298
+#, c-format
+msgid "Ignoring Quote-Suppression-Threshold value of %s, see help"
+msgstr ""
+
+#: alpine/confscroll.c:5323
+msgid "Active Example"
+msgstr ""
+
+#: alpine/confscroll.c:5393 alpine/roleconf.c:5207
+#, c-format
+msgid "Warning: Sig file can't be outside of %s"
+msgstr ""
+
+#: alpine/confscroll.c:5427
+msgid ""
+"Warning: automatic new mail checking and mailbox checkpointing is disabled"
+msgstr ""
+
+#: alpine/confscroll.c:5430
+msgid ""
+"Warning: Mail-Check-Interval=0 may cause IMAP server connection to time out"
+msgstr ""
+
+#: alpine/confscroll.c:5547
+#, c-format
+msgid "\"%s\" can't be Empty, using default"
+msgstr ""
+
+#: alpine/confscroll.c:5598
+#, c-format
+msgid "Can't set \"%s\" to that value, see Setup/Printer"
+msgstr ""
+
+#: alpine/confscroll.c:5637
+#, c-format
+msgid "Score Value must be in range %s to %s"
+msgstr ""
+
+#: alpine/confscroll.c:5682
+#, c-format
+msgid "Changes%s%s will affect your next Alpine session."
+msgstr ""
+
+#: alpine/confscroll.c:5693
+msgid "Timeout changes will affect your next Alpine session."
+msgstr ""
+
+#. TRANSLATORS: these are some screen titles
+#: alpine/mailindx.c:103
+msgid "THREAD INDEX"
+msgstr ""
+
+#: alpine/mailindx.c:106
+msgid "MESSAGE INDEX"
+msgstr ""
+
+#: alpine/mailindx.c:107
+msgid "ZOOMED MESSAGE INDEX"
+msgstr ""
+
+#: alpine/mailindx.c:109
+msgid "COMPOSE: SELECT INTERRUPTED"
+msgstr ""
+
+#: alpine/mailindx.c:112
+msgid "COMPOSE: SELECT FORM LETTER"
+msgstr ""
+
+#: alpine/mailindx.c:113
+msgid "COMPOSE: SELECT POSTPONED"
+msgstr ""
+
+#: alpine/mailindx.c:131
+msgid "ThrdIndex"
+msgstr ""
+
+#: alpine/mailindx.c:164
+msgid "eXclude"
+msgstr ""
+
+#: alpine/mailindx.c:169
+msgid "eXpunge"
+msgstr ""
+
+#: alpine/mailindx.c:222
+msgid "No folder is currently open"
+msgstr ""
+
+#: alpine/mailindx.c:622
+msgid "First Index page"
+msgstr ""
+
+#: alpine/mailindx.c:636
+msgid "Already at start of Index"
+msgstr ""
+
+#: alpine/mailindx.c:656
+msgid "Last Index page"
+msgstr ""
+
+#: alpine/mailindx.c:665
+msgid "Already at end of Index"
+msgstr ""
+
+#: alpine/mailindx.c:694
+msgid "First Index Page"
+msgstr ""
+
+#: alpine/mailindx.c:715
+msgid "Last Index Page"
+msgstr ""
+
+#: alpine/mailindx.c:1071
+msgid "HELP FOR SELECTING INTERRUPTED MSG"
+msgstr ""
+
+#: alpine/mailindx.c:1072
+msgid "HELP FOR SELECTING POSTPONED MSG"
+msgstr ""
+
+#: alpine/mailindx.c:1099
+#, c-format
+msgid "Message %s deleted"
+msgstr ""
+
+#: alpine/mailindx.c:1099
+#, c-format
+msgid "Message %s already deleted"
+msgstr ""
+
+#: alpine/mailindx.c:1128
+#, c-format
+msgid "Message %s UNdeleted"
+msgstr ""
+
+#: alpine/mailindx.c:1128
+#, c-format
+msgid "Message %s NOT deleted"
+msgstr ""
+
+#: alpine/mailindx.c:2849
+msgid "Remember the \"O\" command is always optional"
+msgstr ""
+
+#: alpine/mailindx.c:2984
+msgid "Select Matches"
+msgstr ""
+
+#: alpine/mailindx.c:3020
+msgid "Searched to First Message."
+msgstr ""
+
+#: alpine/mailindx.c:3036
+msgid "Searched to Last Message."
+msgstr ""
+
+#: alpine/mailindx.c:3162
+#, c-format
+msgid "Search cancelled.%s"
+msgstr ""
+
+#: alpine/mailindx.c:3163
+msgid " Selected set may be incomplete."
+msgstr ""
+
+#: alpine/mailindx.c:3172
+#, c-format
+msgid "%s messages found matching word"
+msgstr ""
+
+#: alpine/mailindx.c:3176
+#, c-format
+msgid "Word found%s"
+msgstr ""
+
+#: alpine/mailindx.c:3177
+msgid ". Search wrapped to beginning"
+msgstr ""
+
+#: alpine/mailindx.c:3178
+msgid ". Current line contains only match"
+msgstr ""
+
+#: alpine/kblock.c:45 alpine/kblock.c:64
+msgid "KEYBOARD LOCK"
+msgstr ""
+
+#: alpine/kblock.c:50
+msgid "You may lock this keyboard so that no one else can access your mail"
+msgstr ""
+
+#: alpine/kblock.c:52
+msgid "while you are away. The screen will be locked after entering the "
+msgstr ""
+
+#: alpine/kblock.c:54
+msgid "password to be used for unlocking the keyboard when you return."
+msgstr ""
+
+#: alpine/kblock.c:68
+#, c-format
+msgid "This keyboard is locked by %s <%s>."
+msgstr ""
+
+#: alpine/kblock.c:69
+msgid "To unlock, enter password used to lock the keyboard."
+msgstr ""
+
+#: alpine/kblock.c:119
+#, c-format
+msgid "Retype password to LOCK keyboard (Yes, again) : "
+msgstr ""
+
+#: alpine/kblock.c:121
+#, c-format
+msgid "Retype password to LOCK keyboard : "
+msgstr ""
+
+#: alpine/kblock.c:123
+#, c-format
+msgid "Enter password to LOCK keyboard : "
+msgstr ""
+
+#: alpine/kblock.c:132 alpine/kblock.c:152
+msgid "Keyboard lock cancelled"
+msgstr ""
+
+#: alpine/kblock.c:145
+msgid "Mismatch with initial password: keyboard lock cancelled"
+msgstr ""
+
+#: alpine/kblock.c:150
+msgid "Really lock keyboard with entered password"
+msgstr ""
+
+#: alpine/kblock.c:166
+msgid "Password to UNLOCK doesn't match password used to LOCK"
+msgstr ""
+
+#: alpine/kblock.c:175
+msgid "Enter password to UNLOCK keyboard : "
+msgstr ""
+
+#: alpine/kblock.c:190
+msgid "Keyboard Unlocked"
+msgstr ""
+
+#: alpine/roleconf.c:53
+msgid "HELP FOR ARBITRARY HEADER PATTERNS"
+msgstr ""
+
+#: alpine/roleconf.c:54
+msgid "Add Extra Headers"
+msgstr ""
+
+#: alpine/roleconf.c:157
+msgid "ForwardAs"
+msgstr ""
+
+#: alpine/roleconf.c:158
+msgid "ReplyAs"
+msgstr ""
+
+#: alpine/roleconf.c:159
+msgid "ComposeAs"
+msgstr ""
+
+#: alpine/roleconf.c:159
+msgid "BounceAs"
+msgstr ""
+
+#: alpine/roleconf.c:173
+msgid "HELP FOR SELECTING A ROLE TO BOUNCE AS"
+msgstr ""
+
+#: alpine/roleconf.c:175
+msgid "HELP FOR SELECTING A ROLE TO COMPOSE AS"
+msgstr ""
+
+#: alpine/roleconf.c:177
+msgid "HELP FOR SELECTING A ROLE"
+msgstr ""
+
+#. TRANSLATORS: Print something1 using something2.
+#. "roles" is something1
+#: alpine/roleconf.c:204
+msgid "SELECT ROLE"
+msgstr ""
+
+#. TRANSLATORS: Print something1 using something2.
+#. "roles" is something1
+#: alpine/roleconf.c:205 alpine/roleconf.c:4910
+msgid "roles"
+msgstr ""
+
+#: alpine/roleconf.c:242
+msgid "Default role will be changed to the role you Select"
+msgstr ""
+
+#: alpine/roleconf.c:245
+msgid "Default role will be set to the role you Select"
+msgstr ""
+
+#: alpine/roleconf.c:248
+msgid "Default role will be unset"
+msgstr ""
+
+#: alpine/roleconf.c:252
+msgid "Default role will remain unchanged"
+msgstr ""
+
+#: alpine/roleconf.c:254
+msgid "Default role will remain unset"
+msgstr ""
+
+#: alpine/roleconf.c:340
+#, c-format
+msgid "Warning: \"%sRules\" are overridden in your exceptions configuration"
+msgstr ""
+
+#: alpine/roleconf.c:363
+msgid "Unexpected problem: config file modified externally?"
+msgstr ""
+
+#: alpine/roleconf.c:365
+#, c-format
+msgid "Perhaps a newer version of pine was used to set variable \"%s\"?"
+msgstr ""
+
+#: alpine/roleconf.c:481
+msgid "problem getting addresses from message"
+msgstr ""
+
+#: alpine/roleconf.c:529
+msgid "problem accessing rules"
+msgstr ""
+
+#: alpine/roleconf.c:639 alpine/roleconf.c:1183 alpine/roleconf.c:1337
+#: alpine/roleconf.c:1468
+msgid "Site policy doesn't allow changing From address so From is ignored"
+msgstr ""
+
+#: alpine/roleconf.c:649 alpine/roleconf.c:1196 alpine/roleconf.c:1350
+msgid "Warning: The nickname of the new role is already in use."
+msgstr ""
+
+#: alpine/roleconf.c:997 alpine/roleconf.c:1061
+#, c-format
+msgid "HELP FOR %sRULE CONFIGURATION"
+msgstr ""
+
+#: alpine/roleconf.c:1062
+#, c-format
+msgid "Use Add to add a %sRule"
+msgstr ""
+
+#: alpine/roleconf.c:1138
+msgid "Nothing to Replicate, use Add"
+msgstr ""
+
+#: alpine/roleconf.c:1171 alpine/roleconf.c:1299
+msgid "Can't add rule to ReadOnly file"
+msgstr ""
+
+#: alpine/roleconf.c:1453
+msgid "Can't change ReadOnly rule"
+msgstr ""
+
+#: alpine/roleconf.c:1478
+msgid "Warning: The nickname of this role is also used for another role."
+msgstr ""
+
+#: alpine/roleconf.c:1545
+msgid "Can't delete ReadOnly rule"
+msgstr ""
+
+#: alpine/roleconf.c:1549
+#, c-format
+msgid "Really delete %srule"
+msgstr ""
+
+#: alpine/roleconf.c:1559
+msgid "Rule not deleted"
+msgstr ""
+
+#: alpine/roleconf.c:1676
+msgid "Shuffle only makes sense when there is more than one rule defined"
+msgstr ""
+
+#: alpine/roleconf.c:1695
+msgid "Before File"
+msgstr ""
+
+#: alpine/roleconf.c:1700
+msgid "After File"
+msgstr ""
+
+#: alpine/roleconf.c:1747
+msgid "Can't change ReadOnly file"
+msgstr ""
+
+#: alpine/roleconf.c:1767
+msgid "BEFORE"
+msgstr ""
+
+#: alpine/roleconf.c:1771
+msgid "AFTER"
+msgstr ""
+
+#: alpine/roleconf.c:1788
+msgid "Can't shuffle into ReadOnly file"
+msgstr ""
+
+#: alpine/roleconf.c:1899
+msgid "Current rule is already part of a file. Move outside any files first."
+msgstr ""
+
+#: alpine/roleconf.c:2057
+msgid "Current rule is not part of a file. Use Delete to remove current rule"
+msgstr ""
+
+#: alpine/roleconf.c:2061
+#, c-format
+msgid "Really remove rule file \"%s\" from rules config "
+msgstr ""
+
+#: alpine/roleconf.c:2138
+msgid "Rule file not removed"
+msgstr ""
+
+#. TRANSLATORS: These next 4 are subheading for sections of a configuration screen.
+#: alpine/roleconf.c:2792
+msgid " CURRENT FOLDER CONDITIONS BEGIN HERE "
+msgstr ""
+
+#: alpine/roleconf.c:2794
+msgid " ACTIONS BEGIN HERE "
+msgstr ""
+
+#: alpine/roleconf.c:2795
+msgid " USES BEGIN HERE "
+msgstr ""
+
+#: alpine/roleconf.c:2796
+msgid " OPTIONS BEGIN HERE "
+msgstr ""
+
+#: alpine/roleconf.c:2797
+msgid "Subject pattern"
+msgstr ""
+
+#: alpine/roleconf.c:2798
+msgid "Use SMTP Server"
+msgstr ""
+
+#: alpine/roleconf.c:2799
+msgid "Exit Status Interval"
+msgstr ""
+
+#: alpine/roleconf.c:2909
+#, c-format
+msgid "Rule contains unknown %s element, possibly from newer Alpine"
+msgstr ""
+
+#: alpine/roleconf.c:2914
+msgid "Editing with this version of Alpine will destroy information"
+msgstr ""
+
+#. TRANSLATORS: Quite a few of the translations to follow are from the
+#. rules editing screens. These are mostly headings of individual categories
+#. of criteria which can be set in a rule.
+#: alpine/roleconf.c:2957
+msgid "To pattern"
+msgstr ""
+
+#: alpine/roleconf.c:2959
+msgid "From pattern"
+msgstr ""
+
+#: alpine/roleconf.c:2961
+msgid "Sender pattern"
+msgstr ""
+
+#: alpine/roleconf.c:2963
+msgid "Cc pattern"
+msgstr ""
+
+#: alpine/roleconf.c:2965
+msgid "News pattern"
+msgstr ""
+
+#. TRANSLATORS: Recip is an abbreviation for Recipients which stands for
+#. all of the recipients of a message.
+#: alpine/roleconf.c:2971
+msgid "Recip pattern"
+msgstr ""
+
+#. TRANSLATORS: Partic is an abbreviation for Participants which stands for
+#. all of the recipients plus the sender of a message.
+#: alpine/roleconf.c:2975
+msgid "Partic pattern"
+msgstr ""
+
+#. TRANSLATORS: AllText means all of the text of a message
+#: alpine/roleconf.c:2978
+msgid "AllText pattern"
+msgstr ""
+
+#. TRANSLATORS: BdyText means the text of a message but not the text in the headers
+#: alpine/roleconf.c:2981
+msgid "BdyText pattern"
+msgstr ""
+
+#: alpine/roleconf.c:2984
+msgid "Keyword pattern"
+msgstr ""
+
+#: alpine/roleconf.c:2986
+msgid "Charset pattern"
+msgstr ""
+
+#. TRANSLATORS: Age interval is a setting for how old the message is.
+#: alpine/roleconf.c:2991
+msgid "Age interval"
+msgstr ""
+
+#: alpine/roleconf.c:3002
+msgid "Size interval"
+msgstr ""
+
+#. TRANSLATORS: Score is an alpine concept where the score can be kept for a
+#. message to see if it is a message you want to look at.
+#: alpine/roleconf.c:3015
+msgid "Score interval"
+msgstr ""
+
+#: alpine/roleconf.c:3026
+msgid "Message is Deleted?"
+msgstr ""
+
+#: alpine/roleconf.c:3034
+msgid "Message is New (Unseen)?"
+msgstr ""
+
+#: alpine/roleconf.c:3042
+msgid "Message is Important?"
+msgstr ""
+
+#: alpine/roleconf.c:3050
+msgid "Message is Answered?"
+msgstr ""
+
+#: alpine/roleconf.c:3058
+msgid "Subject contains raw 8-bit?"
+msgstr ""
+
+#: alpine/roleconf.c:3066
+msgid "Message is Recent?"
+msgstr ""
+
+#: alpine/roleconf.c:3074
+msgid "Beginning of Month?"
+msgstr ""
+
+#: alpine/roleconf.c:3082
+msgid "Beginning of Year?"
+msgstr ""
+
+#. TRANSLATORS: these are actions that might be taken by the rule
+#: alpine/roleconf.c:3094
+msgid "Set Deleted Status"
+msgstr ""
+
+#: alpine/roleconf.c:3102
+msgid "Set New Status"
+msgstr ""
+
+#: alpine/roleconf.c:3110
+msgid "Set Important Status"
+msgstr ""
+
+#: alpine/roleconf.c:3118
+msgid "Set Answered Status"
+msgstr ""
+
+#: alpine/roleconf.c:3125
+msgid "Initialize settings using role"
+msgstr ""
+
+#: alpine/roleconf.c:3134
+msgid "Current Folder Type"
+msgstr ""
+
+#: alpine/roleconf.c:3141 alpine/roleconf.c:4403
+msgid "Folder List"
+msgstr ""
+
+#: alpine/roleconf.c:3156
+msgid "Address in address book?"
+msgstr ""
+
+#. TRANSLATORS: Abook is an abbreviation for Address Book
+#: alpine/roleconf.c:3164
+msgid "Abook List"
+msgstr ""
+
+#: alpine/roleconf.c:3172
+msgid "External Categorizer Commands"
+msgstr ""
+
+#: alpine/roleconf.c:3195
+msgid "Character Limit"
+msgstr ""
+
+#: alpine/roleconf.c:3209
+msgid "Set From"
+msgstr ""
+
+#: alpine/roleconf.c:3226
+msgid "Set Reply-To"
+msgstr ""
+
+#: alpine/roleconf.c:3243
+msgid "Set Fcc"
+msgstr ""
+
+#: alpine/roleconf.c:3250
+msgid "Set Sort Order"
+msgstr ""
+
+#: alpine/roleconf.c:3264
+msgid "Set Index Format"
+msgstr ""
+
+#: alpine/roleconf.c:3277
+msgid "Set Startup Rule"
+msgstr ""
+
+#. TRANSLATORS: LiteralSig is a way to keep the signature in the configuration
+#. file instead of in a separate Signature file.
+#: alpine/roleconf.c:3294
+msgid "Set LiteralSig"
+msgstr ""
+
+#: alpine/roleconf.c:3301
+msgid "Set Signature"
+msgstr ""
+
+#. TRANSLATORS: A template is a skeleton of a message to be used
+#. for composing a new message
+#: alpine/roleconf.c:3310
+msgid "Set Template"
+msgstr ""
+
+#. TRANSLATORS: Hdrs is an abbreviation for Headers
+#: alpine/roleconf.c:3318
+msgid "Set Other Hdrs"
+msgstr ""
+
+#: alpine/roleconf.c:3334
+msgid "Use NNTP Server"
+msgstr ""
+
+#: alpine/roleconf.c:3343
+msgid "Score Value"
+msgstr ""
+
+#: alpine/roleconf.c:3360
+msgid "Score From Header"
+msgstr ""
+
+#. TRANSLATORS: For these, Use is a now. This part of the rule describes how
+#. it will be used when Replying so it is the Reply Use
+#: alpine/roleconf.c:3373
+msgid "Reply Use"
+msgstr ""
+
+#: alpine/roleconf.c:3381
+msgid "Forward Use"
+msgstr ""
+
+#: alpine/roleconf.c:3388
+msgid "Compose Use"
+msgstr ""
+
+#: alpine/roleconf.c:3435
+msgid "HELP FOR NICKNAME"
+msgstr ""
+
+#: alpine/roleconf.c:3458
+msgid "HELP FOR COMMENT"
+msgstr ""
+
+#: alpine/roleconf.c:3534
+msgid "HELP FOR CURRENT FOLDER TYPE"
+msgstr ""
+
+#: alpine/roleconf.c:3567
+msgid "HELP FOR FOLDER LIST"
+msgstr ""
+
+#. TRANSLATORS: The %s is replaced with one of the 4 or 5 words below, CURRENT,
+#. SCORED, and so on.
+#: alpine/roleconf.c:3582
+#, c-format
+msgid " %s MESSAGE CONDITIONS BEGIN HERE "
+msgstr ""
+
+#: alpine/roleconf.c:3583 alpine/roleconf.c:3586
+msgid "CURRENT"
+msgstr ""
+
+#: alpine/roleconf.c:3584
+msgid "SCORED"
+msgstr ""
+
+#: alpine/roleconf.c:3585
+msgid "COLORED"
+msgstr ""
+
+#: alpine/roleconf.c:3586
+msgid "FILTERED"
+msgstr ""
+
+#: alpine/roleconf.c:3610
+msgid "HELP FOR TO PATTERN"
+msgstr ""
+
+#: alpine/roleconf.c:3614
+msgid "HELP FOR FROM PATTERN"
+msgstr ""
+
+#: alpine/roleconf.c:3618
+msgid "HELP FOR SENDER PATTERN"
+msgstr ""
+
+#: alpine/roleconf.c:3622
+msgid "HELP FOR CC PATTERN"
+msgstr ""
+
+#: alpine/roleconf.c:3626
+msgid "HELP FOR NEWS PATTERN"
+msgstr ""
+
+#: alpine/roleconf.c:3630
+msgid "HELP FOR SUBJECT PATTERN"
+msgstr ""
+
+#: alpine/roleconf.c:3634
+msgid "HELP FOR RECIPIENT PATTERN"
+msgstr ""
+
+#: alpine/roleconf.c:3638
+msgid "HELP FOR PARTICIPANT PATTERN"
+msgstr ""
+
+#: alpine/roleconf.c:3676
+msgid "HELP FOR EXTRA HEADERS"
+msgstr ""
+
+#: alpine/roleconf.c:3685
+msgid "HELP FOR ALL TEXT PATTERN"
+msgstr ""
+
+#: alpine/roleconf.c:3689
+msgid "HELP FOR BODY TEXT PATTERN"
+msgstr ""
+
+#: alpine/roleconf.c:3694
+msgid "HELP FOR AGE INTERVAL"
+msgstr ""
+
+#: alpine/roleconf.c:3708
+msgid "HELP FOR SIZE INTERVAL"
+msgstr ""
+
+#: alpine/roleconf.c:3723
+msgid "HELP FOR SCORE INTERVAL"
+msgstr ""
+
+#: alpine/roleconf.c:3738
+msgid "HELP FOR KEYWORD PATTERN"
+msgstr ""
+
+#: alpine/roleconf.c:3744
+msgid "HELP FOR CHARACTER SET PATTERN"
+msgstr ""
+
+#: alpine/roleconf.c:3750
+msgid "HELP FOR IMPORTANT STATUS"
+msgstr ""
+
+#: alpine/roleconf.c:3753
+msgid "HELP FOR NEW STATUS"
+msgstr ""
+
+#: alpine/roleconf.c:3756
+msgid "HELP FOR RECENT STATUS"
+msgstr ""
+
+#: alpine/roleconf.c:3759
+msgid "HELP FOR DELETED STATUS"
+msgstr ""
+
+#: alpine/roleconf.c:3762
+msgid "HELP FOR ANSWERED STATUS"
+msgstr ""
+
+#: alpine/roleconf.c:3765
+msgid "HELP FOR 8-BIT SUBJECT"
+msgstr ""
+
+#: alpine/roleconf.c:3768
+msgid "HELP FOR BEGINNING OF MONTH"
+msgstr ""
+
+#: alpine/roleconf.c:3771
+msgid "HELP FOR BEGINNING OF YEAR"
+msgstr ""
+
+#: alpine/roleconf.c:3819
+msgid "HELP FOR ADDRESS IN ADDRESS BOOK"
+msgstr ""
+
+#: alpine/roleconf.c:3843
+msgid "HELP FOR ABOOK LIST"
+msgstr ""
+
+#: alpine/roleconf.c:3848
+msgid "Types of addresses to check for in address book"
+msgstr ""
+
+#: alpine/roleconf.c:3918
+msgid "HELP FOR ADDRESS TYPES"
+msgstr ""
+
+#: alpine/roleconf.c:3970
+msgid "HELP FOR CATEGORIZER COMMAND"
+msgstr ""
+
+#: alpine/roleconf.c:4003
+msgid "HELP FOR CATEGORIZER EXIT STATUS"
+msgstr ""
+
+#: alpine/roleconf.c:4018
+msgid "HELP FOR CHARACTER LIMIT"
+msgstr ""
+
+#: alpine/roleconf.c:4066
+msgid "HELP FOR INITIAL SET NICKNAME"
+msgstr ""
+
+#: alpine/roleconf.c:4080
+msgid "HELP FOR SET FROM ACTION"
+msgstr ""
+
+#: alpine/roleconf.c:4094
+msgid "HELP FOR SET REPLY-TO ACTION"
+msgstr ""
+
+#: alpine/roleconf.c:4108
+msgid "HELP FOR SET FCC ACTION"
+msgstr ""
+
+#: alpine/roleconf.c:4122
+msgid "HELP FOR SET LITERAL SIGNATURE ACTION"
+msgstr ""
+
+#: alpine/roleconf.c:4136
+msgid "HELP FOR SET SIGNATURE ACTION"
+msgstr ""
+
+#: alpine/roleconf.c:4154
+msgid "HELP FOR SET TEMPLATE ACTION"
+msgstr ""
+
+#: alpine/roleconf.c:4172
+msgid "HELP FOR SET OTHER HEADERS ACTION"
+msgstr ""
+
+#: alpine/roleconf.c:4204
+msgid "HELP FOR SMTP SERVER ACTION"
+msgstr ""
+
+#: alpine/roleconf.c:4236
+msgid "HELP FOR NNTP SERVER ACTION"
+msgstr ""
+
+#: alpine/roleconf.c:4281
+msgid "HELP FOR SCORE VALUE ACTION"
+msgstr ""
+
+#: alpine/roleconf.c:4300
+msgid "HELP FOR SCORE VALUE FROM HEADER ACTION"
+msgstr ""
+
+#: alpine/roleconf.c:4336
+msgid "Filter Action"
+msgstr ""
+
+#: alpine/roleconf.c:4388
+msgid "HELP FOR FILTER ACTION"
+msgstr ""
+
+#: alpine/roleconf.c:4409
+msgid "HELP FOR FILTER FOLDER NAME"
+msgstr ""
+
+#: alpine/roleconf.c:4414
+msgid "HELP FOR SET IMPORTANT STATUS"
+msgstr ""
+
+#: alpine/roleconf.c:4416
+msgid "HELP FOR SET NEW STATUS"
+msgstr ""
+
+#: alpine/roleconf.c:4418
+msgid "HELP FOR SET DELETED STATUS"
+msgstr ""
+
+#: alpine/roleconf.c:4420
+msgid "HELP FOR SET ANSWERED STATUS"
+msgstr ""
+
+#: alpine/roleconf.c:4427
+msgid "Set These Keywords"
+msgstr ""
+
+#: alpine/roleconf.c:4431
+msgid "HELP FOR KEYWORDS TO BE SET"
+msgstr ""
+
+#: alpine/roleconf.c:4436
+msgid "Clear These Keywords"
+msgstr ""
+
+#: alpine/roleconf.c:4440
+msgid "HELP FOR KEYWORDS TO BE CLEARED"
+msgstr ""
+
+#: alpine/roleconf.c:4519
+msgid "HELP FOR SET INDEX FORMAT ACTION"
+msgstr ""
+
+#: alpine/roleconf.c:4553
+msgid "Index Line Color ="
+msgstr ""
+
+#: alpine/roleconf.c:4667
+msgid "HELP FOR FILTER FEATURES"
+msgstr ""
+
+#: alpine/roleconf.c:4758
+msgid "HELP FOR ROLE REPLY USE"
+msgstr ""
+
+#: alpine/roleconf.c:4822
+msgid "HELP FOR ROLE FORWARD USE"
+msgstr ""
+
+#: alpine/roleconf.c:4886
+msgid "HELP FOR ROLE COMPOSE USE"
+msgstr ""
+
+#: alpine/roleconf.c:5215
+#, c-format
+msgid "Warning: Template file can't be outside of %s"
+msgstr ""
+
+#: alpine/roleconf.c:5224
+#, c-format
+msgid "Warning: Folder can't be outside of %s"
+msgstr ""
+
+#: alpine/roleconf.c:5344
+#, c-format
+msgid "Warning: command must be absolute path: \"%s\""
+msgstr ""
+
+#: alpine/roleconf.c:6332 alpine/roleconf.c:6432 alpine/roleconf.c:6689
+msgid "Set a valid Filter Action before Exiting"
+msgstr ""
+
+#: alpine/roleconf.c:6335 alpine/roleconf.c:6434
+msgid "Set a valid Specific Folder before Exiting"
+msgstr ""
+
+#: alpine/roleconf.c:6364
+#, c-format
+msgid "Folder \"%s\" in <%s> doesn't exist. Create"
+msgstr ""
+
+#: alpine/roleconf.c:6375
+#, c-format
+msgid "Folder \"%s\" in <%s> doesn't exist. Exit and save anyway"
+msgstr ""
+
+#: alpine/roleconf.c:6384
+#, c-format
+msgid "Folder \"%s\" doesn't exist. Create"
+msgstr ""
+
+#: alpine/roleconf.c:6392
+#, c-format
+msgid "Folder \"%s\" doesn't exist. Exit and save anyway"
+msgstr ""
+
+#: alpine/roleconf.c:6401
+msgid "Folder created"
+msgstr ""
+
+#: alpine/roleconf.c:6411
+msgid "Folder not created"
+msgstr ""
+
+#: alpine/roleconf.c:6414 pico/pico.c:774
+msgid "Exit cancelled"
+msgstr ""
+
+#: alpine/roleconf.c:6529
+#, c-format
+msgid "Nickname \"%s\" is already in use"
+msgstr ""
+
+#: alpine/roleconf.c:6540
+#, c-format
+msgid "Not adding nickname to %s list"
+msgstr ""
+
+#: alpine/roleconf.c:6656
+msgid "Commit changes (\"Yes\" means matching messages will be deleted)"
+msgstr ""
+
+#: alpine/roleconf.c:6665 alpine/roleconf.c:6669
+msgid "Ok, messages matching that Pattern will be deleted"
+msgstr ""
+
+#: alpine/roleconf.c:6712
+msgid "No filter changes saved"
+msgstr ""
+
+#: alpine/roleconf.c:6718 alpine/roleconf.c:7150
+msgid "Changes not yet saved"
+msgstr ""
+
+#: alpine/roleconf.c:7145
+msgid "No changes saved"
+msgstr ""
+
+#: alpine/roleconf.c:7401 alpine/colorconf.c:1906
+msgid "Enter the name of the header field to be added: "
+msgstr ""
+
+#: alpine/roleconf.c:7538
+#, c-format
+msgid "Really remove \"%s\" pattern from this rule"
+msgstr ""
+
+#: alpine/listsel.c:233
+msgid "Nothing selected, use Exit to exit without a selection."
+msgstr ""
+
+#: alpine/status.c:1275
+#, c-format
+msgid "Command \"%s\" not allowed. Press RETURN to continue Alpine."
+msgstr ""
+
+#. TRANSLATORS: This is a placeholder for a list of addresses in address book
+#: alpine/addrbook.c:102
+msgid "[ Address List ]"
+msgstr ""
+
+#. TRANSLATORS: This is an empty address list
+#: alpine/addrbook.c:104
+msgid "[ Empty ]"
+msgstr ""
+
+#: alpine/addrbook.c:105
+msgid "[ No Selected Entries in this Address Book ]"
+msgstr ""
+
+#. TRANSLATORS: Move here means to move the cursor to this line of the screen
+#: alpine/addrbook.c:107
+msgid " [ Move here to add a Personal Address Book ]"
+msgstr ""
+
+#. TRANSLATORS: A global address book is a shared address book
+#: alpine/addrbook.c:109
+msgid " [ Move here to add a Global Address Book ]"
+msgstr ""
+
+#. TRANSLATORS: A heading for an address book distribution list
+#: alpine/addrbook.c:111
+msgid "DISTRIBUTION LIST:"
+msgstr ""
+
+#: alpine/addrbook.c:112
+msgid "[ No Address Book Configured ]"
+msgstr ""
+
+#. TRANSLATORS: Select here means to move the cursor to this line and then type
+#. the Select command.
+#: alpine/addrbook.c:115
+msgid "[ Select Here to See Expanded List ]"
+msgstr ""
+
+#. TRANSLATORS: continuation line meaning there is more on the next page
+#: alpine/addrbook.c:737 alpine/addrbook.c:738
+msgid "(continued)"
+msgstr ""
+
+#: alpine/addrbook.c:1548
+msgid "Exception Setup not implemented for address books"
+msgstr ""
+
+#. TRANSLATORS: A screen title
+#: alpine/addrbook.c:1565
+msgid "SETUP ADDRESS BOOKS"
+msgstr ""
+
+#. TRANSLATORS: a screen title
+#: alpine/addrbook.c:1598 alpine/addrbook.c:1638 alpine/addrbook.c:1678
+msgid "SELECT ADDRESS"
+msgstr ""
+
+#. TRANSLATORS: a screen title
+#: alpine/addrbook.c:1721
+msgid "COMPOSER: SELECT ADDRESS"
+msgstr ""
+
+#. TRANSLATORS: a screen title, user is composing a message and should select
+#. a distribution list from a list of addresses in the address book.
+#: alpine/addrbook.c:1769
+msgid "COMPOSER: SELECT LIST"
+msgstr ""
+
+#. TRANSLATORS: a screen title
+#: alpine/addrbook.c:1812
+msgid "ADDRESS BOOK (Update): SELECT ADDRESSES"
+msgstr ""
+
+#: alpine/addrbook.c:1853
+msgid "SELECT ADDRESSES"
+msgstr ""
+
+#. TRANSLATORS: a screen title
+#: alpine/addrbook.c:1894
+msgid "TAKEADDR: SELECT NICKNAME"
+msgstr ""
+
+#. TRANSLATORS: a screen title, user selecting a nickname from the address book
+#: alpine/addrbook.c:1944 alpine/addrbook.c:1985
+msgid "SELECT NICKNAME"
+msgstr ""
+
+#: alpine/addrbook.c:2117
+msgid "No Address Book Configured"
+msgstr ""
+
+#: alpine/addrbook.c:2125
+msgid "No Address Book Configured, Use SETUP Addressbook screen"
+msgstr ""
+
+#: alpine/addrbook.c:2140
+msgid "Screen too small to use Address book"
+msgstr ""
+
+#: alpine/addrbook.c:2285
+#, c-format
+msgid "ADDRESS BOOKS"
+msgstr ""
+
+#. TRANSLATORS: a screen title, the %s arguments are for a custom part
+#. of the title. Move them as a group. There are two normal cases.
+#. Either ADDRESS BOOK LIST when a list of names of address books is
+#. displayed, or ADDRESS BOOK <name of address book> when one is open.
+#: alpine/addrbook.c:2294
+#, c-format
+msgid "ADDRESS BOOK%s%s%s"
+msgstr ""
+
+#. TRANSLATORS: This is the LIST that goes with title above
+#: alpine/addrbook.c:2296
+msgid " LIST"
+msgstr ""
+
+#. TRANSLATORS: Add Global Address book (one defined by someone else)
+#: alpine/addrbook.c:2380 alpine/addrbook.c:2400
+msgid "Add Glob Abook"
+msgstr ""
+
+#. TRANSLATORS: Add Personal Address book
+#: alpine/addrbook.c:2382 alpine/addrbook.c:2401
+msgid "Add Pers Abook"
+msgstr ""
+
+#. TRANSLATORS: Delete Address book command
+#: alpine/addrbook.c:2388
+msgid "Del Abook"
+msgstr ""
+
+#. TRANSLATORS: Unexpand is the opposite of Expand.
+#. We might expand an address book distribution
+#. list to see all the members of the list.
+#: alpine/addrbook.c:2439 alpine/addrbook.c:2579 alpine/addrbook.c:2630
+msgid "Unexpand"
+msgstr ""
+
+#. TRANSLATORS: View this address book
+#: alpine/addrbook.c:2481 alpine/addrbook.c:2732
+msgid "ViewAbook"
+msgstr ""
+
+#. TRANSLATORS: when selecting from a list of items
+#. the unsetall (unset all) means to start over
+#. with nothing selected.
+#. The set all command means select everything
+#. in the list.
+#: alpine/addrbook.c:2539
+msgid "unsetAll"
+msgstr ""
+
+#: alpine/addrbook.c:2539
+msgid "setAll"
+msgstr ""
+
+#: alpine/addrbook.c:2604 alpine/addrbook.c:2675
+msgid "Role"
+msgstr ""
+
+#. TRANSLATORS: In the address book the user can
+#. view a particular address book entry. It is
+#. called View/Update because the way to update
+#. an entry is to first view it and then there
+#. will be an opportunity to update it from there.
+#: alpine/addrbook.c:2731
+msgid "View/Update"
+msgstr ""
+
+#: alpine/addrbook.c:2871
+msgid "HELP ON CONFIGURING ADDRESS BOOKS"
+msgstr ""
+
+#: alpine/addrbook.c:2874 alpine/addrbook.c:2877
+msgid "HELP ON ADDRESS BOOK"
+msgstr ""
+
+#: alpine/addrbook.c:3028
+#, c-format
+msgid "AddressBook %s is Read Only"
+msgstr ""
+
+#: alpine/addrbook.c:3060
+msgid "Cannot access address book."
+msgstr ""
+
+#: alpine/addrbook.c:3122
+msgid "No entries in address book. Use ExitSelect to leave address books"
+msgstr ""
+
+#: alpine/addrbook.c:3145
+msgid "Use \"X\" to mark addresses or lists"
+msgstr ""
+
+#: alpine/addrbook.c:3231
+msgid "You may only select lists for Lcc, use Bcc for other addresses"
+msgstr ""
+
+#: alpine/addrbook.c:3318
+msgid "Use select to select an address or addresses from address books"
+msgstr ""
+
+#: alpine/addrbook.c:3320
+msgid "No address selected"
+msgstr ""
+
+#: alpine/addrbook.c:3351
+msgid "Address book changed. AddNew cancelled. Try again."
+msgstr ""
+
+#: alpine/addrbook.c:3367
+msgid "add"
+msgstr ""
+
+#: alpine/addrbook.c:3510
+#, c-format
+msgid "Missing \"}\" in config: %s"
+msgstr ""
+
+#. TRANSLATORS: the user tried to change the current line of the address
+#. book but the line could not be changed
+#: alpine/addrbook.c:3548
+msgid "Not a changeable line"
+msgstr ""
+
+#: alpine/addrbook.c:3556
+msgid "Nothing to delete"
+msgstr ""
+
+#. TRANSLATORS: This is just comforting confirmation that the
+#. address book being deleted was successfully deleted
+#: alpine/addrbook.c:3629
+msgid "Address book deleted"
+msgstr ""
+
+#: alpine/addrbook.c:3648
+msgid "Highlight entry you wish to shuffle"
+msgstr ""
+
+#: alpine/addrbook.c:3721
+msgid "Shuffle failed"
+msgstr ""
+
+#: alpine/addrbook.c:3722
+msgid "Address books shuffled"
+msgstr ""
+
+#: alpine/addrbook.c:4092
+msgid "Address book changed. Delete cancelled. Try again."
+msgstr ""
+
+#: alpine/addrbook.c:4099
+msgid "No entries to delete"
+msgstr ""
+
+#: alpine/addrbook.c:4161 alpine/addrbook.c:4218 alpine/addrbook.c:4365
+#: alpine/addrbook.c:4405
+msgid "No entries to select"
+msgstr ""
+
+#: alpine/addrbook.c:4180
+msgid "You may only select whole lists for Lcc"
+msgstr ""
+
+#: alpine/addrbook.c:4183
+msgid "You may only select lists for Lcc, use Bcc for personal entries"
+msgstr ""
+
+#: alpine/addrbook.c:4205 alpine/addrbook.c:4563
+msgid "You may not select list members, only whole lists or personal entries"
+msgstr ""
+
+#: alpine/addrbook.c:4209 alpine/addrbook.c:4567
+msgid "You may only select addresses or lists"
+msgstr ""
+
+#: alpine/addrbook.c:4278
+msgid "Use \"X\" to select addresses or lists"
+msgstr ""
+
+#: alpine/addrbook.c:4372
+msgid "Select is only available from within an expanded address book"
+msgstr ""
+
+#: alpine/addrbook.c:4467
+msgid "Zoom Mode is now off, no entries selected"
+msgstr ""
+
+#: alpine/addrbook.c:4580
+msgid "Zoom Mode is now off"
+msgstr ""
+
+#: alpine/addrbook.c:4622
+msgid "No selected entries to apply command to"
+msgstr ""
+
+#: alpine/addrbook.c:4673
+msgid "No entries to save"
+msgstr ""
+
+#: alpine/addrbook.c:4695
+msgid "No entries to forward"
+msgstr ""
+
+#: alpine/addrbook.c:4710
+msgid "Nothing to forward"
+msgstr ""
+
+#: alpine/addrbook.c:4717
+msgid "Can only forward whole entries"
+msgstr ""
+
+#: alpine/addrbook.c:4754
+msgid "Command \"E\" not defined. Use \"View/Update\" to edit an entry"
+msgstr ""
+
+#: alpine/addrbook.c:4760
+msgid "Command \"S\" not defined. Use \"AddNew\" to create a list"
+msgstr ""
+
+#: alpine/addrbook.c:4765
+msgid "Command \"Z\" not defined. Use \"View/Update\" to add to a list"
+msgstr ""
+
+#: alpine/addrbook.c:4799
+msgid "Zoom Mode is now on"
+msgstr ""
+
+#: alpine/addrbook.c:4847
+msgid "No selected entries to zoom on"
+msgstr ""
+
+#: alpine/addrbook.c:4909
+msgid "No address books configured, use Setup"
+msgstr ""
+
+#: alpine/addrbook.c:4911
+msgid "Address Book is Empty"
+msgstr ""
+
+#: alpine/addrbook.c:4913
+msgid "Distribution List is Empty"
+msgstr ""
+
+#: alpine/addrbook.c:4930
+msgid "Address Book not expanded, use \">\" to expand"
+msgstr ""
+
+#: alpine/addrbook.c:4932
+msgid "Distribution List not expanded, use \">\" to expand"
+msgstr ""
+
+#: alpine/addrbook.c:5758
+msgid "Address book search cancelled"
+msgstr ""
+
+#: alpine/addrbook.c:6327
+#, c-format
+msgid "Word to search for [%.*s]: "
+msgstr ""
+
+#. TRANSLATORS: User is searching in address book. One of the options is to
+#. search for the First Address
+#: alpine/addrbook.c:6342
+msgid "First Adr"
+msgstr ""
+
+#. TRANSLATORS: Last Address
+#: alpine/addrbook.c:6348
+msgid "Last Adr"
+msgstr ""
+
+#: alpine/addrbook.c:6395
+msgid "Searched to first entry"
+msgstr ""
+
+#: alpine/addrbook.c:6399 alpine/addrbook.c:6412
+msgid "No entries"
+msgstr ""
+
+#: alpine/addrbook.c:6408
+msgid "Searched to last entry"
+msgstr ""
+
+#. TRANSLATORS: These "matched" messages are advisory messages explaining
+#. how a search command matched an entry
+#: alpine/addrbook.c:6509
+#, c-format
+msgid "Also matched string in %s other fields"
+msgstr ""
+
+#: alpine/addrbook.c:6512
+#, c-format
+msgid "Matched string in %s fields"
+msgstr ""
+
+#: alpine/addrbook.c:6518
+msgid "Also matched string in Nickname field"
+msgstr ""
+
+#: alpine/addrbook.c:6520
+msgid "Also matched string in Fullname field"
+msgstr ""
+
+#: alpine/addrbook.c:6522
+msgid "Also matched string in Address field"
+msgstr ""
+
+#: alpine/addrbook.c:6524
+msgid "Also matched string in Fcc field"
+msgstr ""
+
+#: alpine/addrbook.c:6526
+msgid "Also matched string in Comment field"
+msgstr ""
+
+#: alpine/addrbook.c:6528
+msgid "Also matched string in list member address"
+msgstr ""
+
+#: alpine/addrbook.c:6530
+msgid "Also matched string in ?"
+msgstr ""
+
+#: alpine/addrbook.c:6534
+msgid "Matched string in Nickname field"
+msgstr ""
+
+#: alpine/addrbook.c:6536
+msgid "Matched string in Fullname field"
+msgstr ""
+
+#: alpine/addrbook.c:6538
+msgid "Matched string in Address field"
+msgstr ""
+
+#: alpine/addrbook.c:6540
+msgid "Matched string in Fcc field"
+msgstr ""
+
+#: alpine/addrbook.c:6542
+msgid "Matched string in Comment field"
+msgstr ""
+
+#: alpine/addrbook.c:6544
+msgid "Matched string in list member address"
+msgstr ""
+
+#: alpine/addrbook.c:6546
+msgid "Matched string in ?"
+msgstr ""
+
+#: alpine/addrbook.c:6895
+#, c-format
+msgid "%s not allowed in nicknames"
+msgstr ""
+
+#: alpine/addrbook.c:6897
+msgid "Blank spaces"
+msgstr ""
+
+#: alpine/addrbook.c:6899
+msgid "Commas"
+msgstr ""
+
+#: alpine/addrbook.c:6901
+msgid "Quotes"
+msgstr ""
+
+#: alpine/addrbook.c:6925
+msgid "HELP FOR SELECTING AN ADDRESS BOOK"
+msgstr ""
+
+#. TRANSLATORS: Print something1 using something2.
+#. abooks is something1
+#: alpine/addrbook.c:6958
+msgid "SELECT ADDRESS BOOK"
+msgstr ""
+
+#: alpine/addrbook.c:6958
+msgid "abooks"
+msgstr ""
+
+#: alpine/addrbook.c:6961
+msgid "No address books defined!"
+msgstr ""
+
+#: alpine/addrbook.c:7270
+msgid "SELECT AN ADDRESS"
+msgstr ""
+
+#: alpine/addrbook.c:7273
+msgid "HELP FOR SELECTING AN ADDRESS"
+msgstr ""
+
+#: alpine/addrbook.c:7425
+msgid "Alpine CONFIGURING ADDRESS BOOKS Help"
+msgstr ""
+
+#: alpine/addrbook.c:7426
+msgid "Alpine ADDRESS_BOOK Help"
+msgstr ""
+
+#: alpine/reply.c:157 alpine/reply.c:200 alpine/reply.c:543 alpine/reply.c:665
+#, c-format
+msgid "Error fetching message %s. Can't reply to it."
+msgstr ""
+
+#: alpine/reply.c:415 alpine/mailpart.c:3433
+#, c-format
+msgid "Replying using role \"%s\" and To as From"
+msgstr ""
+
+#: alpine/reply.c:418 alpine/mailpart.c:3436
+#, c-format
+msgid "Replying using role \"%s\""
+msgstr ""
+
+#: alpine/reply.c:421 alpine/mailpart.c:3439
+msgid "Replying using incoming To as outgoing From"
+msgstr ""
+
+#: alpine/reply.c:440 alpine/mailpart.c:3449
+msgid "Warning: no valid addresses to reply to!"
+msgstr ""
+
+#: alpine/reply.c:450 alpine/reply.c:1392 alpine/reply.c:1787
+#: alpine/mailpart.c:3108 alpine/mailpart.c:3267 alpine/mailpart.c:3506
+#: pith/reply.c:2432
+msgid "Error allocating message text"
+msgstr ""
+
+#: alpine/reply.c:563
+msgid "WARNING! Attachments not included in multiple reply."
+msgstr ""
+
+#: alpine/reply.c:582
+msgid "Multipart with no leading text part."
+msgstr ""
+
+#: alpine/reply.c:588
+msgid "Non-text message not included."
+msgstr ""
+
+#: alpine/reply.c:696
+msgid "COMPOSE MESSAGE REPLY"
+msgstr ""
+
+#: alpine/reply.c:848
+msgid "No, use default role"
+msgstr ""
+
+#: alpine/reply.c:850
+msgid "No, use default settings"
+msgstr ""
+
+#: alpine/reply.c:852
+msgid "To Select Alternate Role"
+msgstr ""
+
+#. TRANSLATORS: This is something like Use role <nickname of role> for Reply?
+#: alpine/reply.c:856
+#, c-format
+msgid "Use role \"%s\" for %s? "
+msgstr ""
+
+#: alpine/reply.c:861
+#, c-format
+msgid "Use role \"<a role without a nickname>\" for %s? "
+msgstr ""
+
+#: alpine/reply.c:870
+msgid "To Select Role"
+msgstr ""
+
+#: alpine/reply.c:872
+#, c-format
+msgid "Press Return to %s using %s role, or ^T to select a role "
+msgstr ""
+
+#: alpine/reply.c:873
+msgid "default"
+msgstr ""
+
+#: alpine/reply.c:873
+msgid "no"
+msgstr ""
+
+#. TRANSLATORS: The final three %s's can probably be safely ignored
+#: alpine/reply.c:990
+#, c-format
+msgid "Include %s original messages in Reply%s%s%s? "
+msgstr ""
+
+#: alpine/reply.c:996
+#, c-format
+msgid "Include original message in Reply%s%s%s? "
+msgstr ""
+
+#: alpine/reply.c:1005
+msgid "Edit Indent String"
+msgstr ""
+
+#: alpine/reply.c:1105
+msgid "Replying to Poster as specified in \"Followup-To\""
+msgstr ""
+
+#: alpine/reply.c:1126
+msgid "Follow-up"
+msgstr ""
+
+#: alpine/reply.c:1137
+msgid "Follow-up to news group(s), Reply via email to author or Both? "
+msgstr ""
+
+#: alpine/reply.c:1162
+msgid "Posting to specified Followup-To groups"
+msgstr ""
+
+#: alpine/reply.c:1169
+msgid "Replying to message that MAY or MAY NOT have been posted to newsgroup"
+msgstr ""
+
+#: alpine/reply.c:1201
+msgid "Pipes for signatures are administratively disabled"
+msgstr ""
+
+#: alpine/reply.c:1206
+msgid "Pipes for templates are administratively disabled"
+msgstr ""
+
+#. TRANSLATORS: First arg is the directory name, second is
+#. the file user wants to read but can't.
+#: alpine/reply.c:1220 pith/smime.c:278 pith/smime.c:340 pith/smime.c:444
+#, c-format
+msgid "Can't read file outside %s: %s"
+msgstr ""
+
+#: alpine/reply.c:1255
+#, c-format
+msgid "Can't get file: %s"
+msgstr ""
+
+#: alpine/reply.c:1264
+#, c-format
+msgid "Error running program \"%s\"%s"
+msgstr ""
+
+#: alpine/reply.c:1293
+#, c-format
+msgid "Error running program \"%s\""
+msgstr ""
+
+#. TRANSLATORS: Arg is a program name
+#: alpine/reply.c:1304
+#, c-format
+msgid "Can't execute \"%s\": Permission denied"
+msgstr ""
+
+#. TRANSLATORS: First arg is error description, 2nd is
+#. filename
+#: alpine/reply.c:1315
+#, c-format
+msgid "Error \"%s\" reading file \"%s\""
+msgstr ""
+
+#: alpine/reply.c:1380 alpine/reply.c:1566 alpine/reply.c:1618
+#: alpine/mailpart.c:3271
+#, c-format
+msgid "Error fetching message %s. Can't forward it."
+msgstr ""
+
+#: alpine/reply.c:1399
+msgid "Forward messages as a MIME digest"
+msgstr ""
+
+#: alpine/reply.c:1401 alpine/mailpart.c:3145
+msgid "Forward message as an attachment"
+msgstr ""
+
+#: alpine/reply.c:1445 alpine/mailpart.c:3037 alpine/mailpart.c:3163
+#, c-format
+msgid "Forwarding using role \"%s\""
+msgstr ""
+
+#: alpine/reply.c:1489
+msgid "Insufficient memory for message text"
+msgstr ""
+
+#: alpine/reply.c:1585
+msgid "WARNING! Attachments not included in multiple forward."
+msgstr ""
+
+#: alpine/reply.c:1603
+msgid "Multipart with no leading text part!"
+msgstr ""
+
+#: alpine/reply.c:1608
+msgid "Non-text message not included!"
+msgstr ""
+
+#: alpine/reply.c:1700 alpine/mailpart.c:3103 alpine/mailpart.c:3262
+msgid "Error fetching message contents. Can't forward message."
+msgstr ""
+
+#: alpine/reply.c:1777
+#, c-format
+msgid "Error reading text \"%s\""
+msgstr ""
+
+#: alpine/reply.c:1819
+#, c-format
+msgid "BOUNCE (redirect) %ld messages to : "
+msgstr ""
+
+#: alpine/reply.c:1823
+#, c-format
+msgid "Send %ld messages "
+msgstr ""
+
+#: alpine/reply.c:1838
+msgid "Can't fetch Subject for Bounce"
+msgstr ""
+
+#: alpine/reply.c:1919
+msgid "No signature file defined."
+msgstr ""
+
+#: alpine/reply.c:1924
+msgid "Can't access remote configuration."
+msgstr ""
+
+#: alpine/reply.c:1926
+msgid "Error attempting to edit remote configuration"
+msgstr ""
+
+#: alpine/reply.c:1962
+msgid "Can't access remote sig"
+msgstr ""
+
+#: alpine/reply.c:1970
+msgid "Can't get write permission for remote sig"
+msgstr ""
+
+#: alpine/reply.c:2010
+#, c-format
+msgid "Can't edit file outside of %s"
+msgstr ""
+
+#: alpine/reply.c:2019
+msgid "Error allocating space for file"
+msgstr ""
+
+#: alpine/reply.c:2030
+#, c-format
+msgid "Error editing \"%s\": %s"
+msgstr ""
+
+#: alpine/reply.c:2044
+#, c-format
+msgid "Error reading file: \"%s\""
+msgstr ""
+
+#: alpine/reply.c:2084 alpine/reply.c:2094
+#, c-format
+msgid "Error writing file: \"%s\""
+msgstr ""
+
+#: alpine/reply.c:2110
+#, c-format
+msgid "Error opening temporary sig file %s: %s"
+msgstr ""
+
+#: alpine/reply.c:2118 pith/smime.c:753 pith/smkeys.c:434
+#, c-format
+msgid "Error copying to %s: %s"
+msgstr ""
+
+#: alpine/reply.c:2126
+msgid "Copy of sig to remote folder failed, changes NOT saved remotely"
+msgstr ""
+
+#: alpine/reply.c:2140
+#, c-format
+msgid "Error writing \"%s\""
+msgstr ""
+
+#: alpine/reply.c:2233
+msgid "Edit Cancelled"
+msgstr ""
+
+#: alpine/reply.c:2274
+msgid "Save changes"
+msgstr ""
+
+#: alpine/reply.c:2275
+msgid "Don't save changes"
+msgstr ""
+
+#: alpine/reply.c:2283
+msgid "Exit editor? "
+msgstr ""
+
+#: alpine/reply.c:2290
+msgid "No Changes Saved"
+msgstr ""
+
+#: alpine/reply.c:2294
+msgid "Exit Cancelled"
+msgstr ""
+
+#: alpine/mailpart.c:192
+msgid "Screen too small to view attachment"
+msgstr ""
+
+#: alpine/mailpart.c:199
+msgid "Can only view one message's attachments at a time."
+msgstr ""
+
+#: alpine/mailpart.c:204
+#, c-format
+msgid "Message %s has only one part (the message body), and no attachments."
+msgstr ""
+
+#: alpine/mailpart.c:237
+#, c-format
+msgid "Malformed message: %s"
+msgstr ""
+
+#: alpine/mailpart.c:499
+msgid "HELP FOR ATTACHMENT INDEX"
+msgstr ""
+
+#: alpine/mailpart.c:559
+msgid "Already on last attachment"
+msgstr ""
+
+#: alpine/mailpart.c:567
+msgid "Already on first attachment"
+msgstr ""
+
+#: alpine/mailpart.c:581
+msgid "Already on last page of attachments"
+msgstr ""
+
+#: alpine/mailpart.c:601
+msgid "Already on first page of attachments"
+msgstr ""
+
+#: alpine/mailpart.c:1158
+msgid "ATTACHMENT INDEX"
+msgstr ""
+
+#: alpine/mailpart.c:1284
+#, c-format
+msgid "Can't Export %s. Use \"Save\" to write file, \"<\" to leave index."
+msgstr ""
+
+#: alpine/mailpart.c:1343
+#, c-format
+msgid "Can't save to file outside of %s"
+msgstr ""
+
+#. TRANSLATORS: Error opening destination <filename>: <error text>
+#: alpine/mailpart.c:1459
+#, c-format
+msgid "Error opening destination %s: %s"
+msgstr ""
+
+#. TRANSLATORS: <error text>: Error writing attachment to <filename>
+#: alpine/mailpart.c:1486
+#, c-format
+msgid "%s: Error writing attachment to \"%s\""
+msgstr ""
+
+#: alpine/mailpart.c:1565
+#, c-format
+msgid "Attached Msg (part %s) "
+msgstr ""
+
+#: alpine/mailpart.c:1600
+#, c-format
+msgid "Attached message (part %s) saved to \"%s\""
+msgstr ""
+
+#: alpine/mailpart.c:1666
+#, c-format
+msgid "Attached digest (part %s) saved to \"%s\""
+msgstr ""
+
+#. TRANSLATORS: Message Attachment (a screen title)
+#: alpine/mailpart.c:1740
+msgid "MSG ATTACHMENT"
+msgstr ""
+
+#: alpine/mailpart.c:1766
+#, c-format
+msgid "Attached message (part %s) %s to \"%s\""
+msgstr ""
+
+#: alpine/mailpart.c:1769
+msgid "overwritten"
+msgstr ""
+
+#: alpine/mailpart.c:1770
+msgid "appended"
+msgstr ""
+
+#: alpine/mailpart.c:1770
+msgid "written"
+msgstr ""
+
+#: alpine/mailpart.c:1775
+#, c-format
+msgid "Error writing %s: %s"
+msgstr ""
+
+#: alpine/mailpart.c:1817
+msgid "DIGEST ATTACHMENT"
+msgstr ""
+
+#: alpine/mailpart.c:1858
+#, c-format
+msgid "Error exporting: %s"
+msgstr ""
+
+#: alpine/mailpart.c:1860
+#, c-format
+msgid "%s messages exported before error occurred"
+msgstr ""
+
+#: alpine/mailpart.c:1874
+#, c-format
+msgid "Error opening file \"%s\" to export digest: %s"
+msgstr ""
+
+#: alpine/mailpart.c:1940
+#, c-format
+msgid "Error printing message %s, part %s"
+msgstr ""
+
+#: alpine/mailpart.c:1968
+#, c-format
+msgid "Can't print digest: %s"
+msgstr ""
+
+#. TRANSLATORS: Don't know how to display <certain type> attachments. <might say Try Save.>
+#: alpine/mailpart.c:2008
+#, c-format
+msgid "Don't know how to display %s%s%s attachments.%s"
+msgstr ""
+
+#: alpine/mailpart.c:2012
+msgid " Try Save."
+msgstr ""
+
+#: alpine/mailpart.c:2015
+#, c-format
+msgid "Don't know how to unpack \"%s\" encoding"
+msgstr ""
+
+#: alpine/mailpart.c:2028
+#, c-format
+msgid "Can't display Multipart/%s"
+msgstr ""
+
+#: alpine/mailpart.c:2033
+msgid "Can't display unknown Multipart Subtype"
+msgstr ""
+
+#: alpine/mailpart.c:2056
+msgid "View selected Attachment"
+msgstr ""
+
+#: alpine/mailpart.c:2143
+#, c-format
+msgid "Error \"%s\", Can't create temporary file"
+msgstr ""
+
+#: alpine/mailpart.c:2150
+#, c-format
+msgid "Error \"%s\", Can't write file %s"
+msgstr ""
+
+#: alpine/mailpart.c:2252
+#, c-format
+msgid "Cannot display %s attachment"
+msgstr ""
+
+#: alpine/mailpart.c:2311 alpine/mailpart.c:2735
+msgid "Error allocating space for attachment."
+msgstr ""
+
+#. TRANSLATORS: User is viewing a message and all of
+#. the header text is being shown.
+#: alpine/mailpart.c:2352 alpine/mailpart.c:2452 pith/mailview.c:174
+msgid "Full header mode ON. All header text being included"
+msgstr ""
+
+#: alpine/mailpart.c:2423 alpine/mailpart.c:2435
+#, c-format
+msgid "Can't format digest: %s"
+msgstr ""
+
+#: alpine/mailpart.c:2454
+msgid "ATTACHED MESSAGES"
+msgstr ""
+
+#: alpine/mailpart.c:2478
+msgid "HELP FOR ATTACHED TEXT VIEW"
+msgstr ""
+
+#: alpine/mailpart.c:2484
+msgid "MsgText"
+msgstr ""
+
+#: alpine/mailpart.c:2487
+msgid "AttchIndex"
+msgstr ""
+
+#: alpine/mailpart.c:2728
+msgid "Error accessing attachment."
+msgstr ""
+
+#: alpine/mailpart.c:2800
+msgid ""
+"This is a vCard which has been forwarded to you. You may add parts of it to "
+"your address book with the Save command. You will have a chance to edit it "
+"before committing it to your address book."
+msgstr ""
+
+#: alpine/mailpart.c:2801
+msgid ""
+"This is a vCard which has been forwarded to you. You may add the entries to "
+"your address book with the Save command."
+msgstr ""
+
+#: alpine/mailpart.c:2810
+msgid "ADDRESS BOOK ATTACHMENT"
+msgstr ""
+
+#: alpine/mailpart.c:2823
+#, c-format
+msgid "Can't format entry : %s"
+msgstr ""
+
+#: alpine/mailpart.c:2980
+msgid "ABOUT ATTACHMENT"
+msgstr ""
+
+#: alpine/mailpart.c:2982
+msgid "HELP FOR \"ABOUT ATTACHMENT\""
+msgstr ""
+
+#: alpine/mailpart.c:3498
+msgid "Error building message body"
+msgstr ""
+
+#: alpine/mailpart.c:3740
+#, c-format
+msgid "Error detaching for pipe: %s"
+msgstr ""
+
+#: alpine/mailpart.c:3744
+msgid "PIPE ATTACHMENT (ERROR)"
+msgstr ""
+
+#: alpine/mailpart.c:3745
+msgid "PIPE ATTACHMENT"
+msgstr ""
+
+#: alpine/mailpart.c:3779
+#, c-format
+msgid "Part %s will be omitted only if message is Saved"
+msgstr ""
+
+#: alpine/mailpart.c:3784
+#, c-format
+msgid "Part %s already deleted"
+msgstr ""
+
+#: alpine/mailpart.c:3805
+#, c-format
+msgid "Part %s already UNdeleted"
+msgstr ""
+
+#: alpine/signal.c:266
+#, c-format
+msgid ""
+"\n"
+"\n"
+"Alpine timed out (No user input for %d %s)\n"
+msgstr ""
+
+#: alpine/signal.c:301
+#, c-format
+msgid ""
+"\n"
+"\n"
+"Alpine finished. Received terminate signal\n"
+"\n"
+msgstr ""
+
+#: alpine/signal.c:418
+#, c-format
+msgid "Another email program is accessing %s. Session now Read-Only."
+msgstr ""
+
+#: alpine/signal.c:786
+msgid "Alpine suspension not enabled - see help text"
+msgstr ""
+
+#: alpine/signal.c:867
+#, c-format
+msgid "Error loading \"%s\""
+msgstr ""
+
+#: alpine/signal.c:872
+msgid "Suspended for too long, IMAP connection broken"
+msgstr ""
+
+#: alpine/signal.c:885
+#, c-format
+msgid ""
+"\n"
+"Alpine suspended. Give the \"%s\" command to come back.\n"
+msgstr ""
+
+#: alpine/signal.c:895
+msgid "Warning: Your IMAP connection will be closed if Alpine"
+msgstr ""
+
+#: alpine/signal.c:896
+msgid "is suspended for more than 30 minutes\n"
+msgstr ""
+
+#: alpine/colorconf.c:102 alpine/colorconf.c:2780
+msgid "SETUP COLOR EXCEPTIONS"
+msgstr ""
+
+#: alpine/colorconf.c:103 alpine/colorconf.c:2781
+msgid "SETUP COLOR"
+msgstr ""
+
+#: alpine/colorconf.c:120
+msgid "conf_scroll_screen bad ret in color_config"
+msgstr ""
+
+#: alpine/colorconf.c:400
+msgid "Current Indexline Style"
+msgstr ""
+
+#: alpine/colorconf.c:415
+msgid "Titlebar Color Style"
+msgstr ""
+
+#: alpine/colorconf.c:432
+msgid "GENERAL COLORS"
+msgstr ""
+
+#: alpine/colorconf.c:461
+msgid "INDEX COLORS"
+msgstr ""
+
+#: alpine/colorconf.c:500
+msgid "HEADER COLORS"
+msgstr ""
+
+#: alpine/colorconf.c:686
+msgid "[ Use Setup/Config command to add Keywords ]"
+msgstr ""
+
+#: alpine/colorconf.c:708
+msgid " (overridden by exceptions)"
+msgstr ""
+
+#: alpine/colorconf.c:710
+msgid " (more in exceptions)"
+msgstr ""
+
+#: alpine/colorconf.c:716
+msgid " (more in main config)"
+msgstr ""
+
+#: alpine/colorconf.c:799
+msgid "HELP FOR SETTING UP COLOR"
+msgstr ""
+
+#: alpine/colorconf.c:1797
+msgid "Really restore all colors to default values"
+msgstr ""
+
+#: alpine/colorconf.c:2017
+msgid "Can't delete this color setting"
+msgstr ""
+
+#: alpine/colorconf.c:2021
+msgid "Really delete header color from config"
+msgstr ""
+
+#: alpine/colorconf.c:2117
+msgid "header color deleted"
+msgstr ""
+
+#: alpine/colorconf.c:2123
+msgid "Can't shuffle this color setting"
+msgstr ""
+
+#: alpine/colorconf.c:2139
+msgid ""
+"Shuffle only makes sense when there is more than one Header Color defined"
+msgstr ""
+
+#: alpine/colorconf.c:2165
+#, c-format
+msgid "Shuffle %s%s%s ? "
+msgstr ""
+
+#: alpine/colorconf.c:2262
+msgid "Header Colors shuffled"
+msgstr ""
+
+#: alpine/colorconf.c:2689
+msgid "Pattern to match ="
+msgstr ""
+
+#: alpine/context.c:202
+msgid ""
+"No exceptions to edit. First collection exception must be set by editing file"
+msgstr ""
+
+#: alpine/context.c:294
+msgid "Really delete last exceptional collection"
+msgstr ""
+
+#: alpine/context.c:313 alpine/context.c:337
+msgid "Trouble saving change, cancelled"
+msgstr ""
+
+#: alpine/context.c:318
+msgid "Deleted last Folder-Collection, reverting to default"
+msgstr ""
+
+#: alpine/context.c:324
+msgid "Deleted default Folder-Collection, reverting back to default"
+msgstr ""
+
+#: alpine/context.c:344
+msgid "Deleted last News-Collection, reverting to default"
+msgstr ""
+
+#: alpine/context.c:352
+msgid "Deleted default News-Collection, reverting back to default"
+msgstr ""
+
+#: alpine/context.c:557
+msgid "Error adding new collection"
+msgstr ""
+
+#: alpine/context.c:566
+msgid "New collection added. Use \"$\" to adjust order."
+msgstr ""
+
+#: alpine/context.c:629
+#, c-format
+msgid "Shuffle selected context %s%s%s? "
+msgstr ""
+
+#: alpine/context.c:694
+msgid "Error deleting shuffled context"
+msgstr ""
+
+#: alpine/context.c:700
+msgid "Trouble shuffling, cancelled"
+msgstr ""
+
+#: alpine/context.c:708
+msgid "Collections shuffled"
+msgstr ""
+
+#. TRANSLATORS: Sorry, can't move news group collections above email collections
+#: alpine/context.c:714
+msgid "Sorry, cannot Shuffle news to top"
+msgstr ""
+
+#: alpine/context.c:716
+msgid "Sorry, nothing to Shuffle"
+msgstr ""
+
+#: alpine/context.c:762
+msgid "Collection list entry updated"
+msgstr ""
+
+#: alpine/context.c:801
+msgid "Sorry, cannot Delete causing news to move to top"
+msgstr ""
+
+#: alpine/context.c:805
+#, c-format
+msgid "Delete the collection definition for \"%s\""
+msgstr ""
+
+#: alpine/context.c:838
+msgid "Error deleting renamed context"
+msgstr ""
+
+#: alpine/context.c:842
+msgid "Collection deleted"
+msgstr ""
+
+#: alpine/context.c:847
+msgid "No collections deleted"
+msgstr ""
+
+#: alpine/imap.c:188
+#, c-format
+msgid "Alert received while accessing \"%s\": %s"
+msgstr ""
+
+#. TRANSLATORS: when logging in, this text is added to the prompt to show
+#. that the password will be sent unencrypted over the network. This is
+#. just a warning message that gets added parenthetically when the user
+#. is asked for a password.
+#: alpine/imap.c:340
+msgid " (INSECURE)"
+msgstr ""
+
+#. TRANSLATORS: Retrying is shown when the user is being asked for a password
+#. after having already failed at least once.
+#: alpine/imap.c:343
+msgid "Retrying - "
+msgstr ""
+
+#. TRANSLATORS: A label for the hostname that the user is logging in on
+#: alpine/imap.c:345
+msgid "HOST"
+msgstr ""
+
+#. TRANSLATORS: user is logging in as a particular user (a particular
+#. login name), this is just labelling that user name.
+#: alpine/imap.c:348
+msgid "USER"
+msgstr ""
+
+#. TRANSLATORS: user is being asked to type in their login name
+#: alpine/imap.c:596
+msgid "ENTER LOGIN NAME"
+msgstr ""
+
+#. TRANSLATORS: An abbreviated form of ENTER LOGIN NAME because
+#. longer version doesn't fit on screen
+#: alpine/imap.c:630
+msgid "LOGIN"
+msgstr ""
+
+#. TRANSLATORS: user is being asked to type in their password
+#: alpine/imap.c:847
+msgid "ENTER PASSWORD"
+msgstr ""
+
+#. TRANSLATORS: An abbreviated form of ENTER PASSWORD
+#: alpine/imap.c:898
+msgid "PASSWORD"
+msgstr ""
+
+#: alpine/imap.c:1195
+#, c-format
+msgid "Waited %s seconds for server reply. Break connection to server"
+msgstr ""
+
+#: alpine/imap.c:1257
+#, c-format
+msgid "Waited %s seconds for server reply. Break connection to server? "
+msgstr ""
+
+#: alpine/imap.c:1276
+#, c-format
+msgid "Waited %s seconds for server reply. Still Waiting..."
+msgstr ""
+
+#: alpine/imap.c:1344
+msgid "There was a failure validating the SSL/TLS certificate for the server"
+msgstr ""
+
+#: alpine/imap.c:1351 alpine/imap.c:1612
+msgid "The reason for the failure was"
+msgstr ""
+
+#: alpine/imap.c:1374
+msgid ""
+"We have not verified the identity of your server. If you ignore this "
+"certificate validation problem and continue, you could end up connecting to "
+"an imposter server."
+msgstr ""
+
+#: alpine/imap.c:1377
+msgid ""
+"If the certificate validation failure was expected and permanent you may "
+"avoid seeing this warning message in the future by adding the option"
+msgstr ""
+
+#: alpine/imap.c:1384
+msgid ""
+"to the name of the folder you attempted to access. In other words, wherever "
+"you see the characters"
+msgstr ""
+
+#: alpine/imap.c:1391 alpine/imap.c:1682
+msgid "in your configuration, replace those characters with"
+msgstr ""
+
+#: alpine/imap.c:1399
+msgid ""
+"Answer \"Yes\" to ignore the warning and continue, \"No\" to cancel the open "
+"of this folder."
+msgstr ""
+
+#: alpine/imap.c:1418 alpine/imap.c:1718 alpine/help.c:199
+msgid "help text"
+msgstr ""
+
+#: alpine/imap.c:1419
+msgid "SSL/TLS CERTIFICATE VALIDATION FAILURE"
+msgstr ""
+
+#: alpine/imap.c:1427
+msgid "HELP FOR CERT VALIDATION FAILURE"
+msgstr ""
+
+#: alpine/imap.c:1454
+msgid "SSL/TLS certificate validation failure"
+msgstr ""
+
+#: alpine/imap.c:1457
+msgid ": Continue anyway "
+msgstr ""
+
+#: alpine/imap.c:1464
+#, c-format
+msgid "Open of %s cancelled"
+msgstr ""
+
+#: alpine/imap.c:1486
+#, c-format
+msgid "Rename newsrc \"%s%s\" for use as new host-specific newsrc"
+msgstr ""
+
+#: alpine/imap.c:1506
+msgid "Error allocating space for details."
+msgstr ""
+
+#: alpine/imap.c:1510
+msgid ""
+"Host given by user:\n"
+"\n"
+" "
+msgstr ""
+
+#: alpine/imap.c:1512
+msgid ""
+"\n"
+"\n"
+"Reason for failure:\n"
+"\n"
+" "
+msgstr ""
+
+#: alpine/imap.c:1514
+msgid ""
+"\n"
+"\n"
+"Certificate being verified:\n"
+"\n"
+msgstr ""
+
+#: alpine/imap.c:1524
+msgid "CERT VALIDATION DETAILS"
+msgstr ""
+
+#: alpine/imap.c:1529
+msgid "HELP FOR CERT VALIDATION DETAILS"
+msgstr ""
+
+#: alpine/imap.c:1576
+#, c-format
+msgid "SSL/TLS failure for %s: %s"
+msgstr ""
+
+#: alpine/imap.c:1589
+msgid "There was an SSL/TLS failure for the server"
+msgstr ""
+
+#: alpine/imap.c:1635
+msgid ""
+"This is just an informational message. With the current setup, SSL/TLS will "
+"not work. If this error re-occurs every time you run Alpine, your current "
+"setup is not compatible with the configuration of your mail server. You may "
+"want to add the option"
+msgstr ""
+
+#: alpine/imap.c:1658
+msgid ""
+"to the name of the mail server you are attempting to access. In other words, "
+"wherever you see the characters"
+msgstr ""
+
+#: alpine/imap.c:1709
+msgid "Type RETURN to continue."
+msgstr ""
+
+#: alpine/imap.c:1719
+msgid "SSL/TLS FAILURE"
+msgstr ""
+
+#: alpine/imap.c:1727
+msgid "HELP FOR TLS/SSL FAILURE"
+msgstr ""
+
+#: alpine/imap.c:2496
+msgid "Erase previously preserved passwords"
+msgstr ""
+
+#: alpine/imap.c:2561 alpine/imap.c:2575
+msgid "Preserve password for next login"
+msgstr ""
+
+#: alpine/imap.c:2581
+msgid "Stop \"Preserve passwords?\" prompts by deleting Alpine Keychain entry"
+msgstr ""
+
+#: alpine/imap.c:2588
+msgid ""
+"Restart \"Preserve passwords?\" prompts by deleting Alpine Keychain entry"
+msgstr ""
+
+#: alpine/imap.c:2594
+msgid "Preserve password on DISK for next login"
+msgstr ""
+
+#: alpine/help.c:224 pico/display.c:1790
+msgid "HELP"
+msgstr ""
+
+#: alpine/help.c:235
+msgid "HELP TEXT"
+msgstr ""
+
+#: alpine/help.c:243
+msgid "HELP FOR HELP TEXT"
+msgstr ""
+
+#: alpine/help.c:252 alpine/help.c:263
+msgid "Prev Help"
+msgstr ""
+
+#: alpine/help.c:257 alpine/help.c:272 pico/bind.c:178
+msgid "Exit Help"
+msgstr ""
+
+#. TRANSLATORS: this is the label of a command where
+#. the user is asking for Help about the Help command
+#: alpine/help.c:279
+msgid "Help Help"
+msgstr ""
+
+#: alpine/help.c:438
+msgid "View Link"
+msgstr ""
+
+#: alpine/help.c:490
+#, c-format
+msgid "Printing Error: %s"
+msgstr ""
+
+#: alpine/help.c:503
+msgid "all 150+ pages of help text"
+msgstr ""
+
+#: alpine/help.c:508
+msgid "Print of all help cancelled"
+msgstr ""
+
+#: alpine/help.c:598
+msgid "HELP SUB-SECTION"
+msgstr ""
+
+#: alpine/help.c:606 alpine/help.c:631
+#, c-format
+msgid "Unrecognized Internal help: \"%s\""
+msgstr ""
+
+#. TRANSLATORS: Help for configuration
+#: alpine/help.c:621
+msgid "HELP CONFIG"
+msgstr ""
+
+#. TRANSLATORS: command label asking pine to include time stamps in output
+#: alpine/help.c:650
+msgid "Timestamps"
+msgstr ""
+
+#. TRANSLATORS: do not include time stamps in output
+#: alpine/help.c:652
+msgid "NoTimestamps"
+msgstr ""
+
+#: alpine/help.c:671
+msgid "Failed allocating memory"
+msgstr ""
+
+#: alpine/help.c:675
+msgid "Turning off new messages while reviewing"
+msgstr ""
+
+#: alpine/help.c:724
+msgid "**** Journal entries prior to this point have been trimmed. ****\n"
+msgstr ""
+
+#: alpine/help.c:732
+msgid "**** Debug 0-4 entries prior to this point have been trimmed. ****\n"
+msgstr ""
+
+#: alpine/help.c:740
+msgid "**** Debug 5-9 entries prior to this point have been trimmed. ****\n"
+msgstr ""
+
+#: alpine/help.c:814
+msgid "journal"
+msgstr ""
+
+#. TRANSLATORS: these are some screen titles
+#: alpine/help.c:826
+msgid "HELP FOR DEBUG JOURNAL"
+msgstr ""
+
+#: alpine/help.c:828
+msgid "REVIEW DEBUGGING"
+msgstr ""
+
+#: alpine/help.c:831 alpine/help.c:838
+msgid "HELP FOR JOURNAL"
+msgstr ""
+
+#: alpine/help.c:833 alpine/help.c:840
+msgid "REVIEW RECENT MESSAGES"
+msgstr ""
+
+#. TRANSLATORS: shows what numeric level Debug output is displayed at
+#: alpine/help.c:850
+#, c-format
+msgid "Debug (%d)"
+msgstr ""
+
+#. TRANSLATORS: include debug output in journal
+#: alpine/help.c:853
+msgid "DebugView"
+msgstr ""
+
+#: alpine/help.c:920
+msgid "COMPOSE TO LOCAL SUPPORT"
+msgstr ""
+
+#: alpine/help.c:927
+msgid "REQUEST FOR ASSISTANCE"
+msgstr ""
+
+#: alpine/print.c:67
+msgid "Exception Setup not implemented for printer"
+msgstr ""
+
+#: alpine/print.c:112
+msgid "You may \"Select\" a print command as your default printer."
+msgstr ""
+
+#: alpine/print.c:125
+msgid "You may also add custom print commands to the list in the"
+msgstr ""
+
+#: alpine/print.c:138
+msgid "\"Personally selected print command\" section below."
+msgstr ""
+
+#: alpine/print.c:171
+msgid " Printer attached to IBM PC or compatible, Macintosh"
+msgstr ""
+
+#: alpine/print.c:183
+msgid "This may not work with all attached printers, and will depend on the"
+msgstr ""
+
+#: alpine/print.c:193
+msgid "terminal emulation/communications software in use. It is known to work"
+msgstr ""
+
+#: alpine/print.c:288
+msgid " Standard UNIX print command"
+msgstr ""
+
+#: alpine/print.c:305
+msgid "Using this option may require setting your \"PRINTER\" or \"LPDEST\""
+msgstr ""
+
+#: alpine/print.c:320
+msgid "environment variable using the standard UNIX utilities."
+msgstr ""
+
+#: alpine/print.c:335 alpine/print.c:462 alpine/print.c:482
+msgid "Printer List:"
+msgstr ""
+
+#: alpine/print.c:368
+msgid " Personally selected print command"
+msgstr ""
+
+#: alpine/print.c:386
+msgid "The text to be printed will be piped into the command given here. The"
+msgstr ""
+
+#: alpine/print.c:401
+msgid ""
+"command is in the 2nd column, the printer name is in the first column. Some"
+msgstr ""
+
+#: alpine/print.c:416
+msgid ""
+"examples are: \"prt\", \"lpr\", \"lp\", or \"enscript\". The command may be "
+"given"
+msgstr ""
+
+#: alpine/print.c:431
+msgid "with options, for example \"enscript -2 -r\" or \"lpr -Plpacc170\". The"
+msgstr ""
+
+#: alpine/print.c:446
+msgid ""
+"commands and options on your system may be different from these examples."
+msgstr ""
+
+#: alpine/print.c:497
+msgid "SETUP PRINTER EXCEPTIONS"
+msgstr ""
+
+#: alpine/print.c:498
+msgid "SETUP PRINTER"
+msgstr ""
+
+#: alpine/print.c:499
+msgid "printer config"
+msgstr ""
+
+#: alpine/print.c:603 alpine/print.c:616 alpine/print.c:629 alpine/print.c:642
+#: alpine/print.c:655
+msgid "Trouble setting default printer"
+msgstr ""
+
+#: alpine/print.c:738
+msgid "Enter printer name : "
+msgstr ""
+
+#: alpine/print.c:770 alpine/print.c:789
+msgid "Enter name of printer to be added : "
+msgstr ""
+
+#: alpine/print.c:776
+msgid "Enter the name for replacement printer : "
+msgstr ""
+
+#: alpine/print.c:837
+msgid "Enter command for printer : "
+msgstr ""
+
+#: alpine/print.c:877 alpine/print.c:1046
+msgid "No commas allowed in command"
+msgstr ""
+
+#: alpine/print.c:940
+#, c-format
+msgid "Delete (unused) printer %s "
+msgstr ""
+
+#: alpine/print.c:946
+#, c-format
+msgid "Really delete item %s from printer list "
+msgstr ""
+
+#: alpine/print.c:958
+msgid "Printer not deleted"
+msgstr ""
+
+#: alpine/print.c:981 pith/ldap.c:1737
+msgid "Name"
+msgstr ""
+
+#: alpine/print.c:985 pith/mailcmd.c:129
+msgid "Command"
+msgstr ""
+
+#: alpine/print.c:989
+msgid "Options"
+msgstr ""
+
+#. TRANSLATORS: this is a question with three choices
+#: alpine/print.c:992
+msgid "Change Name or Command or Options ? "
+msgstr ""
+
+#: alpine/print.c:1007
+msgid "Change command : "
+msgstr ""
+
+#: alpine/print.c:1070
+msgid "Change name : "
+msgstr ""
+
+#: alpine/print.c:1124
+msgid "Init"
+msgstr ""
+
+#: alpine/print.c:1128
+msgid "Trailer"
+msgstr ""
+
+#: alpine/print.c:1130
+msgid "Change Init string or Trailer string ? "
+msgstr ""
+
+#: alpine/print.c:1146
+#, c-format
+msgid "Change INIT string : "
+msgstr ""
+
+#: alpine/print.c:1148
+#, c-format
+msgid "Change TRAILER string : "
+msgstr ""
+
+#: alpine/smime.c:74
+#, c-format
+msgid "Enter passphrase for <%s>: "
+msgstr ""
+
+#: alpine/smime.c:110
+msgid "Can only view one message's information at a time."
+msgstr ""
+
+#: alpine/smime.c:120
+msgid "Can't fetch body of message."
+msgstr ""
+
+#: alpine/smime.c:200 pith/smime.c:1642 pith/smime.c:1768
+msgid "This message was cryptographically signed."
+msgstr ""
+
+#: alpine/smime.c:209
+#, c-format
+msgid "Certificate%s used for signing"
+msgstr ""
+
+#: alpine/smime.c:234 pith/smime.c:1781
+msgid "This message was encrypted."
+msgstr ""
+
+#: alpine/smime.c:244
+msgid "The algorithm used to encrypt was "
+msgstr ""
+
+#: alpine/smime.c:257
+#, c-format
+msgid "Certificate%s for decrypting"
+msgstr ""
+
+#: alpine/smime.c:288
+msgid "No certificate capable of decrypting could be found."
+msgstr ""
+
+#: alpine/smime.c:389
+msgid "Couldn't find certificate info."
+msgstr ""
+
+#: alpine/smime.c:608
+msgid "SETUP S/MIME EXCEPTIONS"
+msgstr ""
+
+#: alpine/smime.c:609
+msgid "SETUP S/MIME"
+msgstr ""
+
+#: alpine/smime.c:626
+msgid "conf_scroll_screen bad ret in smime_config"
+msgstr ""
+
+#: alpine/smime.c:768
+msgid "Mac OS X specific features"
+msgstr ""
+
+#: alpine/smime.c:791
+msgid ""
+"Be careful with the following commands, they REPLACE contents in the target"
+msgstr ""
+
+#: alpine/smime.c:805
+msgid "Transfer public certs FROM directory TO container"
+msgstr ""
+
+#: alpine/smime.c:813
+msgid "Transfer private keys FROM directory TO container"
+msgstr ""
+
+#: alpine/smime.c:821
+msgid "Transfer CA certs FROM directory TO container"
+msgstr ""
+
+#: alpine/smime.c:832
+msgid "Transfer public certs FROM container TO directory"
+msgstr ""
+
+#: alpine/smime.c:840
+msgid "Transfer private keys FROM container TO directory"
+msgstr ""
+
+#: alpine/smime.c:848
+msgid "Transfer CA certs FROM container TO directory"
+msgstr ""
+
+#: alpine/smime.c:861
+msgid "Transfer public certs FROM container TO keychain"
+msgstr ""
+
+#: alpine/smime.c:869
+msgid "Transfer public certs FROM keychain TO container"
+msgstr ""
+
+#: alpine/smime.c:887 alpine/smime.c:965
+msgid "Public certs transferred to container"
+msgstr ""
+
+#: alpine/smime.c:889 alpine/smime.c:900 alpine/smime.c:911 alpine/smime.c:922
+#: alpine/smime.c:933 alpine/smime.c:944
+msgid "Problem transferring certs"
+msgstr ""
+
+#: alpine/smime.c:898
+msgid "Public certs transferred to directory, delete Container config to use"
+msgstr ""
+
+#: alpine/smime.c:909
+msgid "Private keys transferred to container"
+msgstr ""
+
+#: alpine/smime.c:920
+msgid "Private keys transferred to directory, delete Container config to use"
+msgstr ""
+
+#: alpine/smime.c:931
+msgid "CA certs transferred to container"
+msgstr ""
+
+#: alpine/smime.c:942
+msgid "CA certs transferred to directory, delete Container config to use"
+msgstr ""
+
+#: alpine/smime.c:954
+msgid "Public certs transferred to keychain"
+msgstr ""
+
+#: alpine/smime.c:956 alpine/smime.c:967
+msgid "Command not implemented yet"
+msgstr ""
+
+#: pico/osdep/mswin_spell.c:57 pico/osdep/spell.c:98
+msgid "Checking spelling..."
+msgstr ""
+
+#: pico/osdep/mswin_spell.c:68
+msgid "Spelling: initializing Aspell failed"
+msgstr ""
+
+#: pico/osdep/mswin_spell.c:109 pico/osdep/spell.c:257
+msgid "Done checking spelling"
+msgstr ""
+
+#: pico/osdep/mswin_spell.c:292
+msgid "Spelling: lptstr_to_ucs4() failed"
+msgstr ""
+
+#: pico/osdep/mswin_spell.c:531
+msgid "Spelling: speller_check_word() failed"
+msgstr ""
+
+#. TRANSLATORS: Some help text. The ~ characters cause
+#. the characters they are in front of to be bold.
+#: pico/osdep/spell.c:49 pico/osdep/spell.c:67
+msgid "Spell Check Help"
+msgstr ""
+
+#: pico/osdep/spell.c:51
+msgid " The spell checker examines all words in the text. It then"
+msgstr ""
+
+#: pico/osdep/spell.c:52
+msgid " offers each misspelled word for correction while simultaneously"
+msgstr ""
+
+#: pico/osdep/spell.c:53
+msgid " highlighting it in the text. To leave a word unchanged simply"
+msgstr ""
+
+#: pico/osdep/spell.c:54
+msgid ""
+"~ hit ~R~e~t~u~r~n at the edit prompt. If a word has been corrected,"
+msgstr ""
+
+#: pico/osdep/spell.c:55
+msgid ""
+" each occurrence of the incorrect word is offered for replacement."
+msgstr ""
+
+#: pico/osdep/spell.c:57
+msgid ""
+"~ Spell checking can be cancelled at any time by typing ~^~C (~F~3)"
+msgstr ""
+
+#: pico/osdep/spell.c:58
+msgid " after exiting help."
+msgstr ""
+
+#: pico/osdep/spell.c:60 pico/osdep/spell.c:78
+msgid "End of Spell Check Help"
+msgstr ""
+
+#: pico/osdep/spell.c:69
+msgid "\tThe spell checker examines all words in the text. It then"
+msgstr ""
+
+#: pico/osdep/spell.c:70
+msgid "\toffers each misspelled word for correction while simultaneously"
+msgstr ""
+
+#: pico/osdep/spell.c:71
+msgid "\thighlighting it in the text. To leave a word unchanged simply"
+msgstr ""
+
+#: pico/osdep/spell.c:72
+msgid "\thit Return at the edit prompt. If a word has been corrected,"
+msgstr ""
+
+#: pico/osdep/spell.c:73
+msgid "\teach occurrence of the incorrect word is offered for replacement."
+msgstr ""
+
+#: pico/osdep/spell.c:75
+msgid "\tSpell checking can be cancelled at any time by typing ^C (F3)"
+msgstr ""
+
+#: pico/osdep/spell.c:76
+msgid "\tafter exiting help."
+msgstr ""
+
+#: pico/osdep/spell.c:104
+msgid "Can't write temp file for spell checker"
+msgstr ""
+
+#: pico/osdep/spell.c:144
+#, c-format
+msgid "Spell-checking file \"%s\" not found"
+msgstr ""
+
+#: pico/osdep/spell.c:151
+msgid "Can't fork spell checker"
+msgstr ""
+
+#: pico/osdep/spell.c:185
+#, c-format
+msgid "Replace \"%s\" with \"%s\""
+msgstr ""
+
+#: pico/osdep/spell.c:195
+msgid "Edit a replacement: "
+msgstr ""
+
+#: pico/osdep/spell.c:214
+msgid "Spell Checking Cancelled"
+msgstr ""
+
+#: pico/osdep/spell.c:224 pico/osdep/spell.c:231
+msgid "Help with Spelling Checker"
+msgstr ""
+
+#. TRANSLATORS: Read File is a prompt for the name of
+#. a file to be read into the composer.
+#: pico/attach.c:91 pico/display.c:67 pico/display.c:83
+msgid "Read File"
+msgstr ""
+
+#: pico/attach.c:91
+msgid "RcvUpload"
+msgstr ""
+
+#: pico/attach.c:97
+msgid "Name to give uploaded attachment: "
+msgstr ""
+
+#. TRANSLATORS: User is being asked for the name
+#. of the file they want to attach to a message.
+#: pico/attach.c:100
+msgid "File to attach: "
+msgstr ""
+
+#. TRANSLATORS: This is a prompt for a comment about the file
+#. they have attached.
+#: pico/attach.c:106
+msgid "Attachment comment: "
+msgstr ""
+
+#: pico/attach.c:114
+msgid "Attach Help"
+msgstr ""
+
+#: pico/attach.c:319 pico/attach.c:329 pico/attach.c:884 pico/attach.c:902
+msgid "home directory"
+msgstr ""
+
+#. TRANSLATORS: the %s is replaced with the name of a directory
+#: pico/attach.c:322
+#, c-format
+msgid "Restricted mode allows attachments from %s only: too many ..'s"
+msgstr ""
+
+#: pico/attach.c:331
+#, c-format
+msgid "Restricted mode allows attachments from %s only"
+msgstr ""
+
+#: pico/attach.c:404
+msgid "Attach cancelled"
+msgstr ""
+
+#: pico/attach.c:849 pico/attach.c:1051
+#, c-format
+msgid "Attchmnt: '%s' not allowed in file name"
+msgstr ""
+
+#: pico/attach.c:886
+#, c-format
+msgid "Attachments allowed only from %s: too many ..'s"
+msgstr ""
+
+#: pico/attach.c:903
+#, c-format
+msgid "Attachments allowed only from %s"
+msgstr ""
+
+#: pico/attach.c:973
+#, c-format
+msgid "Attchmnt: Expected ']' after \"%s\""
+msgstr ""
+
+#: pico/attach.c:1094
+#, c-format
+msgid "Attchmnt: Expected '(' or '\"' after %s"
+msgstr ""
+
+#: pico/attach.c:1153
+#, c-format
+msgid "Attchmnt: Size field missing ')': \"%s\""
+msgstr ""
+
+#: pico/attach.c:1172
+msgid "Attchmnt: Malformed comment, quotes required"
+msgstr ""
+
+#: pico/attach.c:1194
+msgid "Attchmnt: Closing quote required at end of comment"
+msgstr ""
+
+#: pico/attach.c:1238
+msgid "Attchmnt: Comma must separate attachments"
+msgstr ""
+
+#. TRANSLATORS: command key labels, Send means send the message
+#. we are currently working on.
+#: pico/display.c:66 pico/display.c:82 pico/display.c:1622 pico/browse.c:112
+#: pico/composer.c:126
+msgid "Get Help"
+msgstr ""
+
+#: pico/display.c:66
+msgid "WriteOut"
+msgstr ""
+
+#. TRANSLATORS: Previous Page
+#: pico/display.c:67 pico/display.c:83 pico/browse.c:113 pico/bind.c:245
+msgid "Prev Pg"
+msgstr ""
+
+#: pico/display.c:68 pico/display.c:84
+msgid "Cut Text"
+msgstr ""
+
+#: pico/display.c:68
+msgid "Cur Pos"
+msgstr ""
+
+#: pico/display.c:69 pico/display.c:86
+msgid "Justify"
+msgstr ""
+
+#: pico/display.c:70 pico/display.c:1123 pico/browse.c:116
+msgid "Where is"
+msgstr ""
+
+#. TRANSLATORS: Next Page, a command key label
+#: pico/display.c:70 pico/display.c:87 pico/browse.c:116 pico/bind.c:237
+msgid "Next Pg"
+msgstr ""
+
+#: pico/display.c:73 pico/display.c:90
+msgid "To Spell"
+msgstr ""
+
+#: pico/display.c:75 pico/display.c:92 pico/composer.c:134
+msgid "Del Char"
+msgstr ""
+
+#: pico/display.c:84 pico/display.c:1120 pico/composer.c:132
+#: pico/composer.c:4401
+msgid "Postpone"
+msgstr ""
+
+#. TRANSLATORS: UnJustify means undo the previous
+#. Justify command.
+#: pico/display.c:792 pico/display.c:1126
+msgid "UnJustify"
+msgstr ""
+
+#: pico/display.c:794 pico/display.c:807
+msgid "Can now UnJustify!"
+msgstr ""
+
+#: pico/display.c:799 pico/display.c:1127
+msgid "UnCut Text"
+msgstr ""
+
+#: pico/display.c:1116 pico/composer.c:126 pico/composer.c:4364
+msgid "Send"
+msgstr ""
+
+#: pico/display.c:1122
+msgid "Alt Edit"
+msgstr ""
+
+#: pico/display.c:1396 pico/display.c:1743 pico/word.c:599
+msgid "ABORT"
+msgstr ""
+
+#: pico/basic.c:946
+msgid "Xterm mouse tracking on!"
+msgstr ""
+
+#: pico/basic.c:947
+msgid "Xterm mouse tracking off!"
+msgstr ""
+
+#: pico/basic.c:950
+msgid "Xterm mouse tracking still off ($DISPLAY variable set?)"
+msgstr ""
+
+#: pico/pico.c:341
+msgid "Please make the screen bigger."
+msgstr ""
+
+#. TRANSLATORS: The user typed the Cancel command and was
+#. asked to confirm that. Instead they canceled the cancel
+#. command.
+#: pico/pico.c:652
+msgid "Cancel Cancelled"
+msgstr ""
+
+#: pico/pico.c:661
+msgid "Cancel message (answering \"Yes\" will abandon your mail message)"
+msgstr ""
+
+#: pico/pico.c:663
+msgid "Cancel Edit (and abandon changes)"
+msgstr ""
+
+#: pico/pico.c:664
+msgid "Cancel Edit"
+msgstr ""
+
+#: pico/pico.c:671
+msgid "Cancel Cancelled"
+msgstr ""
+
+#: pico/pico.c:712
+msgid "Problem with attachments! Fix errors or delete attachments."
+msgstr ""
+
+#. TRANSLATORS: buffer is the in-memory copy of a file
+#: pico/pico.c:761
+msgid "Save modified buffer (ANSWERING \"No\" WILL DESTROY CHANGES)"
+msgstr ""
+
+#: pico/pico.c:1915
+#, c-format
+msgid "Attached dropped file \"%s\""
+msgstr ""
+
+#: pico/pico.c:1917
+#, c-format
+msgid "Inserted dropped file \"%s\""
+msgstr ""
+
+#. TRANSLATORS: an error message when trying to create a file
+#: pico/blddate.c:27
+#, c-format
+msgid "can't create '%s'\n"
+msgstr ""
+
+#: pico/browse.c:114
+msgid "Copy"
+msgstr ""
+
+#. TRANSLATORS: The next several lines go together. The ~ characters
+#. should be left in front of the characters they cause to be bold.
+#: pico/browse.c:172
+msgid "Help for Browse Command"
+msgstr ""
+
+#: pico/browse.c:174
+msgid " Pico's file browser is used to select a file from the"
+msgstr ""
+
+#: pico/browse.c:175
+msgid " file system for inclusion in the edited text."
+msgstr ""
+
+#: pico/browse.c:177
+msgid "~ Both directories and files are displayed. Press ~S"
+msgstr ""
+
+#: pico/browse.c:178
+msgid "~ or ~R~e~t~u~r~n to select a file or directory. When a file"
+msgstr ""
+
+#: pico/browse.c:179
+msgid " is selected during the \"Read File\" command, it is"
+msgstr ""
+
+#: pico/browse.c:180
+msgid " inserted into edited text. Answering \"yes\" to the"
+msgstr ""
+
+#: pico/browse.c:181
+msgid " verification question after a directory is selected causes"
+msgstr ""
+
+#: pico/browse.c:182
+msgid " the contents of that directory to be displayed for selection."
+msgstr ""
+
+#: pico/browse.c:184
+msgid " The file named \"..\" is special, and means the \"parent\""
+msgstr ""
+
+#: pico/browse.c:185
+msgid " of the directory being displayed. Select this directory"
+msgstr ""
+
+#: pico/browse.c:186
+msgid " to move upward in the directory tree."
+msgstr ""
+
+#: pico/browse.c:188
+msgid "End of Browser Help."
+msgstr ""
+
+#. TRANSLATORS: Some more help text
+#: pico/browse.c:198
+msgid "Help for Pilot (PIne's Looker-upper Of Things"
+msgstr ""
+
+#: pico/browse.c:200
+msgid ""
+" Pilot is a simple, display-oriented file system browser based on the"
+msgstr ""
+
+#: pico/browse.c:201
+msgid ""
+"~ Alpine message system composer. As with Alpine, commands are "
+"displayed at"
+msgstr ""
+
+#: pico/browse.c:202
+msgid ""
+"~ the bottom of the screen, and context-sensitive help is provided."
+msgstr ""
+
+#: pico/browse.c:204
+msgid ""
+"~ Pilot displays the current working directory at the top of the "
+"screen."
+msgstr ""
+
+#: pico/browse.c:205
+msgid ""
+"~ The directory's contents are displayed in columns of file name, file"
+msgstr ""
+
+#: pico/browse.c:206
+msgid ""
+"~ size pairs. Names that are directories are indicated by the name"
+msgstr ""
+
+#: pico/browse.c:207
+msgid ""
+"~ ~(~d~i~r~) in place of the file size. The parent of the current "
+"working"
+msgstr ""
+
+#: pico/browse.c:208
+msgid ""
+"~ directory is indicated by the file name ~.~. and size of ~"
+"(~p~a~r~e~n~t~ ~d~i~r~)."
+msgstr ""
+
+#: pico/browse.c:209
+msgid ""
+"~ File names that are symbolic links to other files are displayed "
+"with a"
+msgstr ""
+
+#: pico/browse.c:210
+msgid "~ file size of ~-~-."
+msgstr ""
+
+#: pico/browse.c:212
+msgid " The following function keys are available in Pilot:"
+msgstr ""
+
+#: pico/browse.c:214
+msgid "~ ~? Display this help text."
+msgstr ""
+
+#: pico/browse.c:215
+msgid "~ ~Q Quit Pilot."
+msgstr ""
+
+#: pico/browse.c:217
+msgid ""
+"~ ~V View the currently selected file or open the selected "
+"directory."
+msgstr ""
+
+#: pico/browse.c:218
+msgid ""
+"~ Note: Pilot invokes ~p~i~n~e ~-~F, or the program defined "
+"by the ~P~A~G~E~R"
+msgstr ""
+
+#: pico/browse.c:219
+msgid "~ environment variable, to view the file."
+msgstr ""
+
+#: pico/browse.c:220
+msgid "~ ~L Launch an external application program."
+msgstr ""
+
+#: pico/browse.c:222
+msgid "~ ~W Search for a file by name."
+msgstr ""
+
+#: pico/browse.c:223
+msgid "~ ~- Scroll up one page."
+msgstr ""
+
+#: pico/browse.c:224
+msgid "~ ~S~p~a~c~e Scroll down one page."
+msgstr ""
+
+#: pico/browse.c:225
+msgid "~ ~N,~^~F Move forward (right) one column."
+msgstr ""
+
+#: pico/browse.c:226
+msgid "~ ~P,~^~B Move back (left) one column."
+msgstr ""
+
+#: pico/browse.c:227
+msgid "~ ~^~N Move down one row."
+msgstr ""
+
+#: pico/browse.c:228
+msgid "~ ~^~P Move up one row."
+msgstr ""
+
+#: pico/browse.c:230
+msgid "~ ~D Delete the selected file."
+msgstr ""
+
+#: pico/browse.c:231
+msgid "~ ~R Rename the selected file or directory."
+msgstr ""
+
+#: pico/browse.c:232
+msgid "~ ~C Copy the selected file."
+msgstr ""
+
+#: pico/browse.c:233
+msgid "~ ~E Edit the selected file."
+msgstr ""
+
+#: pico/browse.c:234
+msgid ""
+"~ Note: Pilot invokes ~p~i~c~o, or the program defined by the "
+"~E~D~I~T~O~R"
+msgstr ""
+
+#: pico/browse.c:235
+msgid "~ environment variable, to edit the file."
+msgstr ""
+
+#: pico/browse.c:237
+msgid "End of Pilot Help."
+msgstr ""
+
+#: pico/browse.c:330
+#, c-format
+msgid "Can't read outside of %s in restricted mode"
+msgstr ""
+
+#: pico/browse.c:434 pico/main.c:415
+msgid "You may possibly have new mail."
+msgstr ""
+
+#: pico/browse.c:710
+msgid "Can't edit non-text file. Try Launch."
+msgstr ""
+
+#: pico/browse.c:747
+msgid "Type L command to use ListMode"
+msgstr ""
+
+#: pico/browse.c:755
+msgid "Can't Set directories"
+msgstr ""
+
+#: pico/browse.c:776
+msgid "Already in ListMode"
+msgstr ""
+
+#: pico/browse.c:801
+msgid "Add Name"
+msgstr ""
+
+#: pico/browse.c:805
+msgid "Command to execute: "
+msgstr ""
+
+#: pico/browse.c:809 pico/browse.c:972 pico/browse.c:1054 pico/browse.c:1179
+#: pico/browse.c:1307 pico/browse.c:1600
+msgid "No help yet!"
+msgstr ""
+
+#: pico/browse.c:821
+msgid "Command cancelled"
+msgstr ""
+
+#: pico/browse.c:829
+msgid "No command specified"
+msgstr ""
+
+#: pico/browse.c:848
+msgid "Can't delete a directory"
+msgstr ""
+
+#: pico/browse.c:853
+msgid "Delete not allowed in restricted mode"
+msgstr ""
+
+#: pico/browse.c:864
+msgid "File is write protected! OVERRIDE"
+msgstr ""
+
+#. TRANSLATORS: This is a question, Delete file <filename>
+#: pico/browse.c:869
+#, c-format
+msgid "Delete file \"%.*s\""
+msgstr ""
+
+#: pico/browse.c:872
+msgid "File CANNOT be UNdeleted! Really delete"
+msgstr ""
+
+#: pico/browse.c:878
+msgid "Delete Cancelled"
+msgstr ""
+
+#: pico/browse.c:879
+msgid "File Not Deleted"
+msgstr ""
+
+#: pico/browse.c:888
+#, c-format
+msgid "Delete Failed: %s"
+msgstr ""
+
+#: pico/browse.c:938 pico/browse.c:947
+msgid "Help for Browsing"
+msgstr ""
+
+#: pico/browse.c:945
+msgid "Browser Help"
+msgstr ""
+
+#. TRANSLATORS: A prompt asking for a directory to
+#. move into
+#: pico/browse.c:967
+msgid "Directory to go to: "
+msgstr ""
+
+#: pico/browse.c:980
+msgid "Goto cancelled"
+msgstr ""
+
+#: pico/browse.c:994
+#, c-format
+msgid "Invalid Directory: %s"
+msgstr ""
+
+#: pico/browse.c:999
+msgid "Restricted mode browsing limited to home directory"
+msgstr ""
+
+#: pico/browse.c:1005
+#, c-format
+msgid " Can't go outside of %s in restricted mode"
+msgstr ""
+
+#: pico/browse.c:1023
+#, c-format
+msgid "Not a directory: \"%s\""
+msgstr ""
+
+#: pico/browse.c:1037
+msgid "Add not allowed in restricted mode"
+msgstr ""
+
+#: pico/browse.c:1051
+msgid "Name of file to add: "
+msgstr ""
+
+#: pico/browse.c:1062
+msgid "Add File Cancelled"
+msgstr ""
+
+#: pico/browse.c:1077
+msgid "No file named. Add Cancelled."
+msgstr ""
+
+#: pico/browse.c:1082 pico/browse.c:1208 pico/browse.c:1328
+msgid "Too many ..'s in name"
+msgstr ""
+
+#: pico/browse.c:1088
+#, c-format
+msgid "Restricted mode allows Add in %s only"
+msgstr ""
+
+#: pico/browse.c:1094
+#, c-format
+msgid "File \"%.*s\" already exists!"
+msgstr ""
+
+#: pico/browse.c:1111
+#, c-format
+msgid "Added File \"%s\""
+msgstr ""
+
+#: pico/browse.c:1114 pico/browse.c:1248 pico/browse.c:1363
+msgid "Problems refiguring browser"
+msgstr ""
+
+#: pico/browse.c:1162
+msgid "Can't copy a directory"
+msgstr ""
+
+#: pico/browse.c:1167
+msgid "Copy not allowed in restricted mode"
+msgstr ""
+
+#: pico/browse.c:1176
+msgid "Name of new copy: "
+msgstr ""
+
+#: pico/browse.c:1187
+msgid "Make Copy Cancelled"
+msgstr ""
+
+#: pico/browse.c:1198
+msgid "No destination, file not copied"
+msgstr ""
+
+#: pico/browse.c:1203
+msgid "Can't copy file on to itself!"
+msgstr ""
+
+#: pico/browse.c:1214
+#, c-format
+msgid "Restricted mode allows Copy in %s only"
+msgstr ""
+
+#. TRANSLATORS: A question: File <filename> exists! Replace?
+#: pico/browse.c:1221 pico/browse.c:1342
+#, c-format
+msgid "File \"%.*s\" exists! OVERWRITE"
+msgstr ""
+
+#: pico/browse.c:1225
+msgid "Make copy cancelled"
+msgstr ""
+
+#: pico/browse.c:1226
+msgid "File Not Renamed"
+msgstr ""
+
+#: pico/browse.c:1245
+#, c-format
+msgid "File copied to %s"
+msgstr ""
+
+#: pico/browse.c:1290
+msgid "Can't rename \"..\""
+msgstr ""
+
+#: pico/browse.c:1295
+msgid "Rename not allowed in restricted mode"
+msgstr ""
+
+#: pico/browse.c:1304
+msgid "Rename file to: "
+msgstr ""
+
+#: pico/browse.c:1315 pico/browse.c:1347
+msgid "Rename cancelled"
+msgstr ""
+
+#: pico/browse.c:1334
+#, c-format
+msgid "Restricted mode allows Rename in %s only"
+msgstr ""
+
+#: pico/browse.c:1348
+msgid "Not Renamed"
+msgstr ""
+
+#: pico/browse.c:1359
+#, c-format
+msgid "Rename Failed: %s"
+msgstr ""
+
+#: pico/browse.c:1421
+#, c-format
+msgid "Can't visit %s in restricted mode"
+msgstr ""
+
+#: pico/browse.c:1448
+msgid "Can't move up a directory"
+msgstr ""
+
+#: pico/browse.c:1496
+#, c-format
+msgid "Problem finding dir \"%s\""
+msgstr ""
+
+#: pico/browse.c:1537
+msgid "No files are selected, use \"X\" to mark files for selection"
+msgstr ""
+
+#: pico/browse.c:1598
+msgid "File name to find"
+msgstr ""
+
+#: pico/browse.c:1631
+msgid "Searched to end of directory"
+msgstr ""
+
+#: pico/browse.c:1634
+msgid "Searched to start of directory"
+msgstr ""
+
+#: pico/browse.c:1653
+msgid "Whereis cancelled"
+msgstr ""
+
+#: pico/browse.c:1684 pico/search.c:298
+#, c-format
+msgid "\"%s\" not found"
+msgstr ""
+
+#. TRANSLATORS: Brwsr is an abbreviation for Browser, which is
+#. a command used to look through something
+#: pico/browse.c:2190
+msgid "Quit"
+msgstr ""
+
+#: pico/browse.c:2190
+msgid "Exit Brwsr"
+msgstr ""
+
+#: pico/browse.c:2192
+msgid "Goto"
+msgstr ""
+
+#: pico/browse.c:2195
+msgid "Launch"
+msgstr ""
+
+#: pico/browse.c:2853
+msgid "Can't display non-text file. Try \"Launch\"."
+msgstr ""
+
+#: pico/browse.c:2860
+#, c-format
+msgid "Can't read file: %s"
+msgstr ""
+
+#: pico/browse.c:2867
+#, c-format
+msgid "Can't open file: %s"
+msgstr ""
+
+#. TRANSLATORS: Some help text that goes together in a group.
+#: pico/search.c:64
+msgid "Help for Search Command"
+msgstr ""
+
+#: pico/search.c:66
+msgid " Enter the words or characters you would like to search"
+msgstr ""
+
+#: pico/search.c:67
+msgid "~ for, then press ~R~e~t~u~r~n. The search then takes place."
+msgstr ""
+
+#: pico/search.c:68
+msgid " When the characters or words that you entered "
+msgstr ""
+
+#: pico/search.c:69
+msgid " are found, the buffer will be redisplayed with the cursor "
+msgstr ""
+
+#: pico/search.c:70
+msgid " at the beginning of the selected text."
+msgstr ""
+
+#: pico/search.c:72
+msgid " The most recent string for which a search was made is"
+msgstr ""
+
+#: pico/search.c:73
+msgid " displayed in the \"Search\" prompt between the square"
+msgstr ""
+
+#: pico/search.c:74
+msgid " brackets. This string is the default search prompt."
+msgstr ""
+
+#: pico/search.c:75
+msgid "~ Hitting only ~R~e~t~u~r~n or at the prompt will cause the"
+msgstr ""
+
+#: pico/search.c:76
+msgid " search to be made with the default value."
+msgstr ""
+
+#: pico/search.c:78
+msgid " The text search is not case sensitive, and will examine the"
+msgstr ""
+
+#: pico/search.c:79
+msgid " entire message."
+msgstr ""
+
+#: pico/search.c:81
+msgid " Should the search fail, a message will be displayed."
+msgstr ""
+
+#: pico/search.c:83
+msgid "End of Search Help."
+msgstr ""
+
+#: pico/search.c:146 pico/search.c:153 pico/search.c:437 pico/search.c:444
+msgid "Help for Searching"
+msgstr ""
+
+#: pico/search.c:171
+msgid "Search to Line Number : "
+msgstr ""
+
+#: pico/search.c:173
+msgid "Search to Line Number Cancelled"
+msgstr ""
+
+#: pico/search.c:177
+msgid "Line number must be greater than zero"
+msgstr ""
+
+#: pico/search.c:181
+msgid "Line number must contain only digits"
+msgstr ""
+
+#: pico/search.c:240
+msgid "Search Cancelled"
+msgstr ""
+
+#. TRANSLATORS: abbreviation for Replace All occurences
+#: pico/search.c:340 pico/search.c:455
+msgid "Repl All"
+msgstr ""
+
+#. TRANSLATORS: Replace just one occurence
+#: pico/search.c:460
+msgid "Repl One"
+msgstr ""
+
+#: pico/search.c:467
+msgid "Replacement Cancelled"
+msgstr ""
+
+#: pico/search.c:580
+#, c-format
+msgid "No more matches for \"%s\""
+msgstr ""
+
+#: pico/search.c:584
+msgid "No more matches"
+msgstr ""
+
+#: pico/search.c:603 pico/search.c:772
+msgid "FirstLine"
+msgstr ""
+
+#: pico/search.c:608 pico/search.c:777
+msgid "LastLine"
+msgstr ""
+
+#: pico/search.c:613
+msgid "No Replace"
+msgstr ""
+
+#: pico/search.c:619 pico/search.c:783
+msgid "LineNumber"
+msgstr ""
+
+#. TRANSLATORS: Start of paragraph
+#: pico/search.c:625 pico/search.c:788
+msgid "Start of Para"
+msgstr ""
+
+#: pico/search.c:630 pico/search.c:793
+msgid "End of Para"
+msgstr ""
+
+#. TRANSLATORS: Instead of justifying (formatting) just a
+#. single paragraph, Full Justify justifies the entire
+#. message.
+#: pico/search.c:638 pico/search.c:798
+msgid "FullJustify"
+msgstr ""
+
+#: pico/search.c:726
+msgid "No Line Number"
+msgstr ""
+
+#. TRANSLATORS: A question asking whether to forget about
+#. the changes and revert to the unchanged version.
+#: pico/buffer.c:155
+msgid "Discard changes"
+msgstr ""
+
+#: pico/word.c:576 pico/word.c:608
+msgid "Region"
+msgstr ""
+
+#: pico/word.c:580 pico/word.c:615
+msgid "Paragraph"
+msgstr ""
+
+#: pico/word.c:582 pico/word.c:624
+msgid "Quotelevel"
+msgstr ""
+
+#. TRANSLATORS: The next several lines go together as help text.
+#. Leave the ~ characters where they are, they cause the following
+#. character to be printed in boldface as long as the first character
+#. of the line is also a ~.
+#: pico/bind.c:43
+msgid " Pico Help Text"
+msgstr ""
+
+#: pico/bind.c:45
+msgid " Pico is designed to be a simple, easy-to-use text editor with a"
+msgstr ""
+
+#: pico/bind.c:46
+msgid ""
+" layout very similar to the Alpine mailer. The status line at the"
+msgstr ""
+
+#: pico/bind.c:47
+msgid " top of the display shows pico's version, the current file being"
+msgstr ""
+
+#: pico/bind.c:48
+msgid " edited and whether or not there are outstanding modifications"
+msgstr ""
+
+#: pico/bind.c:49
+msgid ""
+" that have not been saved. The third line from the bottom is used"
+msgstr ""
+
+#: pico/bind.c:50
+msgid ""
+" to report informational messages and for additional command input."
+msgstr ""
+
+#: pico/bind.c:51
+msgid " The bottom two lines list the available editing commands."
+msgstr ""
+
+#: pico/bind.c:53
+msgid " Each character typed is automatically inserted into the buffer"
+msgstr ""
+
+#: pico/bind.c:54
+msgid " at the current cursor position. Editing commands and cursor"
+msgstr ""
+
+#: pico/bind.c:55
+msgid " movement (besides arrow keys) are given to pico by typing"
+msgstr ""
+
+#: pico/bind.c:56
+msgid " special control-key sequences. A caret, '^', is used to denote"
+msgstr ""
+
+#: pico/bind.c:57
+msgid ""
+"~ the control key, sometimes marked \"CTRL\", so the ~C~T~R~L~-~q key"
+msgstr ""
+
+#: pico/bind.c:58
+msgid "~ combination is written as ~^~Q."
+msgstr ""
+
+#: pico/bind.c:60
+msgid ""
+" The following functions are available in pico (where applicable,"
+msgstr ""
+
+#: pico/bind.c:61
+msgid " corresponding function key commands are in parentheses)."
+msgstr ""
+
+#: pico/bind.c:63
+msgid "~ ~^~G (~F~1) Display this help text."
+msgstr ""
+
+#: pico/bind.c:65
+msgid "~ ~^~F move Forward a character."
+msgstr ""
+
+#: pico/bind.c:66
+msgid "~ ~^~B move Backward a character."
+msgstr ""
+
+#: pico/bind.c:67
+msgid "~ ~^~P move to the Previous line."
+msgstr ""
+
+#: pico/bind.c:68
+msgid "~ ~^~N move to the Next line."
+msgstr ""
+
+#: pico/bind.c:69
+msgid "~ ~^~A move to the beginning of the current line."
+msgstr ""
+
+#: pico/bind.c:70
+msgid "~ ~^~E move to the End of the current line."
+msgstr ""
+
+#: pico/bind.c:71
+msgid "~ ~^~V (~F~8) move forward a page of text."
+msgstr ""
+
+#: pico/bind.c:72
+msgid "~ ~^~Y (~F~7) move backward a page of text."
+msgstr ""
+
+#: pico/bind.c:74
+msgid "~ ~^~W (~F~6) Search for (where is) text, neglecting case."
+msgstr ""
+
+#: pico/bind.c:75
+msgid "~ ~^~L Refresh the display."
+msgstr ""
+
+#: pico/bind.c:77
+msgid "~ ~^~D Delete the character at the cursor position."
+msgstr ""
+
+#: pico/bind.c:78
+msgid ""
+"~ ~^~^ Mark cursor position as beginning of selected text."
+msgstr ""
+
+#: pico/bind.c:79
+msgid " Note: Setting mark when already set unselects text."
+msgstr ""
+
+#: pico/bind.c:80
+msgid ""
+"~ ~^~K (~F~9) Cut selected text (displayed in inverse characters)."
+msgstr ""
+
+#: pico/bind.c:81
+msgid " Note: The selected text's boundary on the cursor side"
+msgstr ""
+
+#: pico/bind.c:82
+msgid " ends at the left edge of the cursor. So, with "
+msgstr ""
+
+#: pico/bind.c:83
+msgid " selected text to the left of the cursor, the "
+msgstr ""
+
+#: pico/bind.c:84
+msgid " character under the cursor is not selected."
+msgstr ""
+
+#: pico/bind.c:85
+msgid "~ ~^~U (~F~1~0) Uncut (paste) last cut text inserting it at the"
+msgstr ""
+
+#: pico/bind.c:86
+msgid " current cursor position."
+msgstr ""
+
+#: pico/bind.c:87
+msgid "~ ~^~I Insert a tab at the current cursor position."
+msgstr ""
+
+#: pico/bind.c:89
+msgid "~ ~^~J (~F~4) Format (justify) the current paragraph."
+msgstr ""
+
+#: pico/bind.c:90
+msgid ""
+" Note: paragraphs delimited by blank lines or indentation."
+msgstr ""
+
+#: pico/bind.c:91
+msgid "~ ~^~T (~F~1~2) To invoke the spelling checker"
+msgstr ""
+
+#: pico/bind.c:92
+msgid "~ ~^~C (~F~1~1) Report current cursor position"
+msgstr ""
+
+#: pico/bind.c:94
+msgid ""
+"~ ~^~R (~F~5) Insert an external file at the current cursor "
+"position."
+msgstr ""
+
+#: pico/bind.c:95
+msgid "~ ~^~O (~F~3) Output the current buffer to a file, saving it."
+msgstr ""
+
+#: pico/bind.c:96
+msgid "~ ~^~X (~F~2) Exit pico, saving buffer."
+msgstr ""
+
+#: pico/bind.c:98
+msgid " End of Help."
+msgstr ""
+
+#: pico/bind.c:140
+msgid "Help for the Alpine Composer"
+msgstr ""
+
+#: pico/bind.c:141
+msgid "Help for Signature Editor"
+msgstr ""
+
+#. TRANSLATORS: Pico is the name of a program
+#: pico/bind.c:164
+msgid "Help for Pico"
+msgstr ""
+
+#: pico/file.c:50
+msgid "Read file: "
+msgstr ""
+
+#: pico/file.c:54
+msgid "File reading disabled in secure mode"
+msgstr ""
+
+#: pico/file.c:59
+msgid "No file name entered"
+msgstr ""
+
+#: pico/file.c:65 pico/main.c:326
+#, c-format
+msgid "Can't read file from outside of %s"
+msgstr ""
+
+#. TRANSLATORS: several lines of help text
+#: pico/file.c:76
+msgid "Insert File Help Text"
+msgstr ""
+
+#: pico/file.c:78
+msgid " Type in a file name to have it inserted into your editing"
+msgstr ""
+
+#: pico/file.c:79
+msgid " buffer between the line that the cursor is currently on"
+msgstr ""
+
+#: pico/file.c:80
+msgid " and the line directly below it. You may abort this by "
+msgstr ""
+
+#: pico/file.c:81
+msgid "~ typing the ~F~2 (~^~C) key after exiting help."
+msgstr ""
+
+#: pico/file.c:83
+msgid "End of Insert File Help"
+msgstr ""
+
+#. TRANSLATORS: several lines of help text
+#: pico/file.c:90
+msgid "Write File Help Text"
+msgstr ""
+
+#: pico/file.c:92
+msgid " Type in a file name to have it written out, thus saving"
+msgstr ""
+
+#: pico/file.c:93
+msgid " your buffer, to a file. You can abort this by typing "
+msgstr ""
+
+#: pico/file.c:94
+msgid "~ the ~F~2 (~^~C) key after exiting help."
+msgstr ""
+
+#: pico/file.c:96
+msgid "End of Write File Help"
+msgstr ""
+
+#. TRANSLATORS: Insert File is a key label for a command
+#. that inserts a file into a message being composed.
+#. InsertMsg means Insert Message and it inserts a different
+#. message into the message being composed.
+#: pico/file.c:141
+msgid "InsertFile"
+msgstr ""
+
+#: pico/file.c:141
+msgid "InsertMsg"
+msgstr ""
+
+#: pico/file.c:178
+#, c-format
+msgid "Message %s included"
+msgstr ""
+
+#: pico/file.c:184
+msgid "Can't insert file in restricted mode"
+msgstr ""
+
+#: pico/file.c:191
+#, c-format
+msgid "Can't insert file from outside of %s: too many ..'s"
+msgstr ""
+
+#: pico/file.c:198
+#, c-format
+msgid "Can't insert file from outside of %s"
+msgstr ""
+
+#: pico/file.c:225
+msgid "Can't select messages yet!"
+msgstr ""
+
+#: pico/file.c:261
+msgid "Can't insert in restricted mode"
+msgstr ""
+
+#: pico/file.c:306
+msgid "Restricted mode disallows uploaded command"
+msgstr ""
+
+#: pico/file.c:339 pico/file.c:346
+msgid "Help for Insert File"
+msgstr ""
+
+#: pico/file.c:494
+msgid "New file"
+msgstr ""
+
+#: pico/file.c:501
+msgid "Reading file"
+msgstr ""
+
+#: pico/file.c:511
+#, c-format
+msgid "Read %ld lines"
+msgstr ""
+
+#: pico/file.c:513
+#, c-format
+msgid "Read 1 line"
+msgstr ""
+
+#. TRANSLATORS: Asking for name of file to write data into
+#: pico/file.c:584
+msgid "File Name to write : "
+msgstr ""
+
+#: pico/file.c:596
+#, c-format
+msgid "Can't write outside of %s: too many ..'s"
+msgstr ""
+
+#: pico/file.c:604
+#, c-format
+msgid "Can't write outside of %s"
+msgstr ""
+
+#: pico/file.c:748
+#, c-format
+msgid "File \"%s\" exists, OVERWRITE"
+msgstr ""
+
+#: pico/file.c:780 pico/file.c:827
+#, c-format
+msgid "Wrote %s lines"
+msgstr ""
+
+#: pico/file.c:783 pico/file.c:830
+msgid "Wrote 1 line"
+msgstr ""
+
+#: pico/file.c:810
+msgid "No file name"
+msgstr ""
+
+#: pico/file.c:955
+#, c-format
+msgid "Inserting %s."
+msgstr ""
+
+#: pico/file.c:968
+#, c-format
+msgid "Inserted %ld lines"
+msgstr ""
+
+#: pico/file.c:970
+#, c-format
+msgid "Inserted 1 line"
+msgstr ""
+
+#. TRANSLATORS: error message about command line
+#: pico/main.c:86 pico/pilot.c:52
+#, c-format
+msgid "missing or empty argument to \"%c\" flag"
+msgstr ""
+
+#: pico/main.c:87 pico/pilot.c:53
+#, c-format
+msgid "non numeric argument for \"%c\" flag"
+msgstr ""
+
+#: pico/main.c:88 pico/pilot.c:54
+#, c-format
+msgid "missing color for \"%s\" flag"
+msgstr ""
+
+#: pico/main.c:89 pico/pilot.c:55
+#, c-format
+msgid "missing character set for \"%s\" flag"
+msgstr ""
+
+#: pico/main.c:90 pico/pilot.c:56
+#, c-format
+msgid "input character set \"%s\" is unsupported"
+msgstr ""
+
+#: pico/main.c:91 pico/pilot.c:57
+#, c-format
+msgid "output character set \"%s\" is unsupported"
+msgstr ""
+
+#. TRANSLATORS: little help printed out when incorrect arguments
+#. are given to pico program.
+#: pico/main.c:96
+msgid "Possible Starting Arguments for Pico editor:"
+msgstr ""
+
+#: pico/main.c:98 pico/pilot.c:64
+msgid "\tArgument\t\tMeaning"
+msgstr ""
+
+#: pico/main.c:99
+msgid "\t -e \t\tComplete - allow file name completion"
+msgstr ""
+
+#: pico/main.c:100
+msgid "\t -k \t\tCut - let ^K cut from cursor position to end of line"
+msgstr ""
+
+#: pico/main.c:101 pico/pilot.c:65
+msgid "\t -a \t\tShowDot - show dot files in file browser"
+msgstr ""
+
+#: pico/main.c:102 pico/pilot.c:66
+msgid "\t -j \t\tGoto - allow 'Goto' command in file browser"
+msgstr ""
+
+#: pico/main.c:103 pico/pilot.c:67
+msgid "\t -g \t\tShow - show cursor in file browser"
+msgstr ""
+
+#: pico/main.c:104 pico/pilot.c:68
+msgid "\t -m \t\tMouse - turn on mouse support"
+msgstr ""
+
+#: pico/main.c:105 pico/pilot.c:70
+msgid "\t -x \t\tNoKeyhelp - suppress keyhelp"
+msgstr ""
+
+#: pico/main.c:106
+msgid ""
+"\t -p \t\tPreserveStartStop - preserve \"start\"(^Q) and \"stop\"(^S) "
+"characters"
+msgstr ""
+
+#: pico/main.c:107 pico/pilot.c:71
+msgid ""
+"\t -q \t\tTermdefWins - termcap or terminfo takes precedence over defaults"
+msgstr ""
+
+#: pico/main.c:108
+msgid ""
+"\t -Q <quotestr> \tSet quote string (eg. \"> \") esp. for composing email"
+msgstr ""
+
+#: pico/main.c:109
+msgid "\t -d \t\tRebind - let delete key delete current character"
+msgstr ""
+
+#: pico/main.c:110 pico/pilot.c:72
+msgid "\t -f \t\tKeys - force use of function keys"
+msgstr ""
+
+#: pico/main.c:111
+msgid "\t -b \t\tReplace - allow search and replace"
+msgstr ""
+
+#: pico/main.c:112 pico/pilot.c:73
+msgid "\t -h \t\tHelp - give this list of options"
+msgstr ""
+
+#: pico/main.c:113
+msgid "\t -r[#cols] \tFill - set fill column to #cols columns, default=72"
+msgstr ""
+
+#: pico/main.c:114 pico/pilot.c:79
+msgid "\t -n[#s] \tMail - notify about new mail every #s seconds, default=180"
+msgstr ""
+
+#: pico/main.c:115
+msgid "\t -s <speller> \tSpeller - specify alternative speller"
+msgstr ""
+
+#: pico/main.c:116 pico/pilot.c:80
+msgid "\t -t \t\tShutdown - enable special shutdown mode"
+msgstr ""
+
+#: pico/main.c:117 pico/pilot.c:81
+msgid "\t -o <dir>\tOperation - specify the operating directory"
+msgstr ""
+
+#: pico/main.c:118 pico/pilot.c:82
+msgid "\t -z \t\tSuspend - allow use of ^Z suspension"
+msgstr ""
+
+#: pico/main.c:119
+msgid "\t -w \t\tNoWrap - turn off word wrap"
+msgstr ""
+
+#: pico/main.c:120
+msgid "\t -W <wordseps> \tSet word separators other than whitespace"
+msgstr ""
+
+#: pico/main.c:122 pico/pilot.c:75
+msgid ""
+"\t -dcs <display_character_set> \tdefault uses LANG or LC_CTYPE from "
+"environment"
+msgstr ""
+
+#: pico/main.c:123 pico/pilot.c:76
+msgid "\t -kcs <keyboard_character_set> \tdefaults to display_character_set"
+msgstr ""
+
+#: pico/main.c:124 pico/pilot.c:77
+msgid "\t -syscs\t\tuse system-supplied translation routines"
+msgstr ""
+
+#: pico/main.c:127 pico/pilot.c:84
+msgid "\t -cnf color \tforeground color"
+msgstr ""
+
+#: pico/main.c:128 pico/pilot.c:85
+msgid "\t -cnb color \tbackground color"
+msgstr ""
+
+#: pico/main.c:129 pico/pilot.c:86
+msgid "\t -crf color \treverse foreground color"
+msgstr ""
+
+#: pico/main.c:130 pico/pilot.c:87
+msgid "\t -crb color \treverse background color"
+msgstr ""
+
+#: pico/main.c:132
+msgid "\t +[line#] \tLine - start on line# line, default=1"
+msgstr ""
+
+#: pico/main.c:133
+msgid "\t -v \t\tView - view file"
+msgstr ""
+
+#: pico/main.c:134 pico/pilot.c:89
+msgid "\t -no_setlocale_collate\tdo not do setlocale(LC_COLLATE)"
+msgstr ""
+
+#: pico/main.c:135
+msgid "\t -version\tPico version number"
+msgstr ""
+
+#: pico/main.c:137
+msgid "\t All arguments may be followed by a file name to display."
+msgstr ""
+
+#: pico/main.c:357 pico/pilot.c:215
+#, c-format
+msgid "Checking for new mail every %s seconds"
+msgstr ""
+
+#: pico/main.c:843 pico/pilot.c:462
+#, c-format
+msgid "Argument Error: %.200s"
+msgstr ""
+
+#. TRANSLATORS: little help printed out when incorrect arguments are
+#. given for pilot program.
+#: pico/pilot.c:62
+msgid "Possible Starting Arguments for Pilot file browser:"
+msgstr ""
+
+#: pico/pilot.c:69
+msgid "\t -v \t\tOneColumn - use single column display"
+msgstr ""
+
+#: pico/pilot.c:91
+msgid "\t All arguments may be followed by a directory name to start in."
+msgstr ""
+
+#. TRANSLATORS: Rich Headers is a command to display more headers. It
+#. is triggered with the ^R key. PrvPg stands for Previous Page.
+#: pico/composer.c:129 pico/composer.c:4385
+msgid "Rich Hdr"
+msgstr ""
+
+#: pico/composer.c:129
+msgid "PrvPg/Top"
+msgstr ""
+
+#. TRANSLATORS: Cut Line means remove a line. Postpone means to save
+#. a message being composed so that it can be worked on later.
+#: pico/composer.c:132
+msgid "Cut Line"
+msgstr ""
+
+#. TRANSLATORS: Next Page
+#: pico/composer.c:136
+msgid "Attach"
+msgstr ""
+
+#: pico/composer.c:136
+msgid "NxtPg/End"
+msgstr ""
+
+#. TRANSLATORS: Undelete a line that was just deleted
+#: pico/composer.c:138
+msgid "UnDel Line"
+msgstr ""
+
+#: pico/composer.c:743
+msgid "Problem with attachments. Postpone anyway?"
+msgstr ""
+
+#: pico/composer.c:1706
+#, c-format
+msgid "Can't move beyond top of %s"
+msgstr ""
+
+#: pico/composer.c:1927
+msgid "Please make the screen larger."
+msgstr ""
+
+#: pico/composer.c:2001 pico/composer.c:2223 pico/composer.c:2237
+msgid "Can't edit attachment number!"
+msgstr ""
+
+#. TRANSLATORS: Killing text is deleting it and
+#. Unkilling text is undeleting killed text.
+#: pico/composer.c:2174
+msgid "Problem Unkilling text"
+msgstr ""
+
+#: pico/composer.c:4392
+msgid "Edit File"
+msgstr ""
+
+#: pith/charconv/utf8.c:1934
+#, c-format
+msgid "Display character set \"%s\" is ignored when using system translation"
+msgstr ""
+
+#: pith/charconv/utf8.c:1946
+#, c-format
+msgid "Keyboard character set \"%s\" is ignored when using system translation"
+msgstr ""
+
+#. TRANSLATORS: The first argument is the name of the character
+#. set the user is trying to use (which is unsupported by alpine).
+#. The second argument is " (except for posting)" if they are
+#. trying to use ISO-2022-JP for something other than posting.
+#: pith/charconv/utf8.c:1994 pith/charconv/utf8.c:2032
+#, c-format
+msgid "Character set \"%s\" is unsupported%s, using US-ASCII"
+msgstr ""
+
+#: pith/charconv/utf8.c:1996 pith/charconv/utf8.c:2034
+msgid " (except for posting)"
+msgstr ""
+
+#: pith/charconv/utf8.c:2048
+msgid "Help, can't figure out display character set or even use US-ASCII."
+msgstr ""
+
+#. TRANSLATORS: User is viewing a message and all the quoted text is
+#. being shown.
+#: pith/mailview.c:169
+msgid "All quoted text being included"
+msgstr ""
+
+#: pith/mailview.c:206
+#, c-format
+msgid "Error writing message: %s"
+msgstr ""
+
+#. TRANSLATORS: There was an error putting together a message for
+#. viewing. The arg is the description of the error.
+#: pith/mailview.c:314 pith/mailview.c:2151
+#, c-format
+msgid "Formatting error: %s"
+msgstr ""
+
+#: pith/mailview.c:327 pith/reply.c:2570
+msgid " [ERROR fetching text of message]"
+msgstr ""
+
+#. TRANSLATORS: A label
+#: pith/mailview.c:583 pith/mailview.c:596
+msgid "Parts/Attachments:"
+msgstr ""
+
+#: pith/init.c:138
+#, c-format
+msgid "Folders directory name is longer than %d\n"
+msgstr ""
+
+#: pith/init.c:139
+#, c-format
+msgid "Directory name: \"%s/%s\"\n"
+msgstr ""
+
+#: pith/init.c:264
+#, c-format
+msgid "Incomplete maildomain \"%s\"."
+msgstr ""
+
+#: pith/init.c:268
+msgid "Return address in mail you send may be incorrect."
+msgstr ""
+
+#: pith/init.c:288
+#, c-format
+msgid "No host name or domain name set\n"
+msgstr ""
+
+#. TRANSLATORS: arg is a filename
+#: pith/init.c:526
+#, c-format
+msgid "Renaming \"%s\" at start of month"
+msgstr ""
+
+#. TRANSLATORS: 1st arg is filename, 2nd is error message
+#: pith/init.c:532
+#, c-format
+msgid "Error renaming \"%s\": %s"
+msgstr ""
+
+#. TRANSLATORS: This is a heading describing some addresses the
+#. user will have the chance to choose from.
+#: pith/takeaddr.c:562
+msgid " These entries are taken from the attachments "
+msgstr ""
+
+#: pith/takeaddr.c:573 pith/takeaddr.c:625
+msgid "Can't take address into address book. Error accessing folder"
+msgstr ""
+
+#. TRANSLATORS: msg is message
+#: pith/takeaddr.c:598
+msgid " These entries are taken from the msg headers "
+msgstr ""
+
+#: pith/takeaddr.c:682
+msgid " Below this line are some possibilities taken from the text of the msg "
+msgstr ""
+
+#. TRANSLATORS: -bail is a literal option name, don't change it.
+#: pith/conf.c:1724
+msgid "Exiting because -bail option is set and config file doesn't exist."
+msgstr ""
+
+#: pith/conf.c:2484
+msgid "Only using first character of threading-indicator-character option"
+msgstr ""
+
+#: pith/conf.c:2491
+msgid "Only using first character of threading-expanded-character option"
+msgstr ""
+
+#: pith/conf.c:2501
+msgid "Only using first character of threading-lastreply-character option"
+msgstr ""
+
+#. TRANSLATORS: section heading in configuration screen
+#: pith/conf.c:2628
+msgid "Advanced User Preferences"
+msgstr ""
+
+#. TRANSLATORS: section heading in configuration screen
+#: pith/conf.c:2631
+msgid "Folder Preferences"
+msgstr ""
+
+#. TRANSLATORS: section heading in configuration screen
+#: pith/conf.c:2634
+msgid "Address Book Preferences"
+msgstr ""
+
+#. TRANSLATORS: section heading in configuration screen
+#: pith/conf.c:2637
+msgid "Composer Preferences"
+msgstr ""
+
+#. TRANSLATORS: section heading in configuration screen
+#: pith/conf.c:2640
+msgid "News Preferences"
+msgstr ""
+
+#. TRANSLATORS: section heading in configuration screen
+#: pith/conf.c:2643
+msgid "Viewer Preferences"
+msgstr ""
+
+#. TRANSLATORS: section heading in configuration screen
+#: pith/conf.c:2646
+msgid "Advanced Command Preferences"
+msgstr ""
+
+#. TRANSLATORS: section heading in configuration screen
+#: pith/conf.c:2649
+msgid "Printer Preferences"
+msgstr ""
+
+#. TRANSLATORS: section heading in configuration screen
+#: pith/conf.c:2652
+msgid "Reply Preferences"
+msgstr ""
+
+#. TRANSLATORS: section heading in configuration screen
+#: pith/conf.c:2655
+msgid "Sending Preferences"
+msgstr ""
+
+#. TRANSLATORS: section heading in configuration screen
+#: pith/conf.c:2658
+msgid "Message Index Preferences"
+msgstr ""
+
+#: pith/conf.c:4956
+msgid "Unable to read or write remote configuration"
+msgstr ""
+
+#: pith/conf.c:5478
+msgid "Can't access remote config, changes NOT saved!"
+msgstr ""
+
+#: pith/conf.c:5504
+#, c-format
+msgid "No write permission for remote config %.200s, changes NOT saved!"
+msgstr ""
+
+#: pith/conf.c:5510
+msgid "Remote config changed, aborting our change to avoid damage..."
+msgstr ""
+
+#: pith/conf.c:5562
+#, c-format
+msgid "Can't modify configuration file \"%.200s\": ReadOnly"
+msgstr ""
+
+#. TRANSLATORS: first argument is a filename, second
+#. arg is the text of the error message
+#: pith/conf.c:5592
+#, c-format
+msgid "Error creating \"%.200s\" : %.200s"
+msgstr ""
+
+#: pith/conf.c:5809
+msgid "Copying to remote config"
+msgstr ""
+
+#: pith/conf.c:5817 pith/adrbklib.c:3396
+#, c-format
+msgid "Error copying to %.200s: %.200s"
+msgstr ""
+
+#: pith/conf.c:5821
+msgid "Copy of config to remote folder failed, changes NOT saved remotely"
+msgstr ""
+
+#: pith/conf.c:5856
+#, c-format
+msgid "Error saving configuration in \"%.200s\": %.200s"
+msgstr ""
+
+#. TRANSLATORS: The %s is either "Postload " or nothing. A Postload config file
+#. is a type of config file.
+#: pith/conf.c:5970
+#, c-format
+msgid "Must quit Alpine to change %sconfig file."
+msgstr ""
+
+#: pith/conf.c:6720
+#, c-format
+msgid "Posting-Character set \"%s\" is unsupported, using UTF-8"
+msgstr ""
+
+#: pith/conf.c:6735
+msgid "(fixed)"
+msgstr ""
+
+#: pith/conf.c:6736
+msgid "(default)"
+msgstr ""
+
+#: pith/conf.c:6737
+msgid "(overridden)"
+msgstr ""
+
+#: pith/conf.c:6950
+msgid "This option has no effect without Enable-Incoming-Folders"
+msgstr ""
+
+#: pith/conf.c:7129
+#, c-format
+msgid "Posting-Character-Set \"%s\" is unsupported, using UTF-8"
+msgstr ""
+
+#: pith/conf.c:7142
+msgid "Posting-Character-Set is ignored with Use-System-Translation turned on"
+msgstr ""
+
+#: pith/conf.c:7300
+#, c-format
+msgid "No local file specified"
+msgstr ""
+
+#: pith/conf.c:7305
+#, c-format
+msgid "No remote folder specified"
+msgstr ""
+
+#: pith/conf.c:7310
+#, c-format
+msgid "Remote folder name \"%s\" %s"
+msgstr ""
+
+#: pith/conf.c:7311
+msgid "must begin with \"{\""
+msgstr ""
+
+#: pith/conf.c:7311
+msgid "not valid"
+msgstr ""
+
+#: pith/conf.c:7316
+#, c-format
+msgid "First argument \"%s\" must be a local filename"
+msgstr ""
+
+#: pith/conf.c:7322
+#, c-format
+msgid "Local file \"%s\" does not exist"
+msgstr ""
+
+#: pith/conf.c:7327
+#, c-format
+msgid "Can't read local file \"%s\": %s"
+msgstr ""
+
+#: pith/conf.c:7337 pith/remote.c:97 pith/reply.c:3389
+msgid "Error: "
+msgstr ""
+
+#: pith/conf.c:7337
+msgid "Can't copy to remote folder."
+msgstr ""
+
+#: pith/conf.c:7340
+#, c-format
+msgid "Can't create \"%s\""
+msgstr ""
+
+#: pith/conf.c:7355
+#, c-format
+msgid "Can't open remote folder \"%s\""
+msgstr ""
+
+#: pith/conf.c:7361
+#, c-format
+msgid "Remote folder \"%s\" is readonly"
+msgstr ""
+
+#: pith/conf.c:7374
+#, c-format
+msgid "Failed initializing remote folder \"%s\", check debug file"
+msgstr ""
+
+#: pith/conf.c:7395
+#, c-format
+msgid "Error copying to remote folder \"%s\""
+msgstr ""
+
+#: pith/conf.c:7894
+msgid "Supported features in this Alpine"
+msgstr ""
+
+#. TRANSLATORS: headings
+#: pith/conf.c:7932
+msgid "Encryption:"
+msgstr ""
+
+#: pith/conf.c:7935
+msgid " TLS and SSL"
+msgstr ""
+
+#: pith/conf.c:7937
+msgid " None (no TLS or SSL)"
+msgstr ""
+
+#: pith/conf.c:7947
+msgid "Authenticators:"
+msgstr ""
+
+#: pith/conf.c:7961
+msgid "Mailbox drivers:"
+msgstr ""
+
+#: pith/conf.c:7976
+msgid "Directories:"
+msgstr ""
+
+#: pith/conf.c:7992
+msgid ""
+"Authenticators may be disabled because of the \"disable-these-authenticators"
+"\" hidden config option. Mailbox drivers may be disabled because of the "
+"\"disable-these-drivers\" hidden config option."
+msgstr ""
+
+#: pith/conf.c:7996
+msgid ""
+"Authenticators may be disabled because of the \"disable-these-authenticators"
+"\""
+msgstr ""
+
+#: pith/conf.c:7998
+msgid "hidden config option. Mailbox drivers may be disabled because of the"
+msgstr ""
+
+#: pith/conf.c:8000
+msgid "\"disable-these-drivers\" hidden config option."
+msgstr ""
+
+#: pith/detach.c:165
+msgid "Can't find body for requested message"
+msgstr ""
+
+#: pith/detach.c:276
+msgid "Formatting error: no space to make copy, no display filters used"
+msgstr ""
+
+#: pith/detach.c:633 pith/detach.c:785
+msgid "Message size does not match expected size, continuing..."
+msgstr ""
+
+#. TRANSLATORS: data transfer was interrupted by something
+#: pith/detach.c:699
+msgid "Transfer interrupted!"
+msgstr ""
+
+#: pith/detach.c:789
+msgid "Server returns zero bytes, Quell-Partial-Fetch feature may help"
+msgstr ""
+
+#. TRANSLATORS: a label
+#: pith/folder.c:557
+msgid "Incoming Message Folders"
+msgstr ""
+
+#: pith/folder.c:584
+msgid "No folder collections defined"
+msgstr ""
+
+#: pith/folder.c:2319 pith/mailcmd.c:1937
+msgid "new"
+msgstr ""
+
+#: pith/folder.c:2323 pith/mailcmd.c:1934
+msgid "unseen"
+msgstr ""
+
+#. TRANSLATORS: This is the description of an attachment that isn't being
+#. shown but that can be viewed or saved.
+#: pith/mimedesc.c:820
+msgid "Not Shown. Use the \"V\" command to view or save to address book."
+msgstr ""
+
+#. TRANSLATORS: This is the description of an attachment that isn't being
+#. shown but that can be viewed or saved.
+#: pith/mimedesc.c:825
+msgid "Not Shown. Use the \"V\" command to view or save this part."
+msgstr ""
+
+#: pith/mimedesc.c:839
+msgid "Unable to print this part."
+msgstr ""
+
+#. TRANSLATORS: tell user they are waiting for Sorting of %s, the foldername
+#: pith/sort.c:186 pith/sort.c:244
+#, c-format
+msgid "Sorting \"%s\""
+msgstr ""
+
+#: pith/bldaddr.c:337
+msgid "Loop or Duplicate detected in addressbook!"
+msgstr ""
+
+#. TRANSLATORS: The first %s is a mailbox, the second is either
+#. directory or addressbook
+#: pith/bldaddr.c:655
+#, c-format
+msgid "Address for \"%s\" not in %s"
+msgstr ""
+
+#: pith/bldaddr.c:658
+msgid "addressbook"
+msgstr ""
+
+#: pith/mailindx.c:631
+#, c-format
+msgid "Unrecognized word in index-format: %s"
+msgstr ""
+
+#: pith/mailindx.c:878
+msgid "Configured \"index-format\" unrecognizable. Using default."
+msgstr ""
+
+#: pith/mailindx.c:2024 pith/mailindx.c:3213
+msgid "[ No Message Text Available ]"
+msgstr ""
+
+#: pith/mailindx.c:4133
+msgid "Today"
+msgstr ""
+
+#: pith/mailindx.c:4635
+msgid "Yesterday"
+msgstr ""
+
+#: pith/mailindx.c:4639
+msgid "Tomorrow"
+msgstr ""
+
+#: pith/mailindx.c:4641
+#, c-format
+msgid "Next %.3s!"
+msgstr ""
+
+#: pith/thread.c:869
+msgid "No thread to collapse or expand on this line"
+msgstr ""
+
+#: pith/thread.c:927
+#, c-format
+msgid "Unselected %s messages in thread"
+msgstr ""
+
+#: pith/thread.c:930
+#, c-format
+msgid "Selected %s messages in thread"
+msgstr ""
+
+#: pith/thread.c:934
+#, c-format
+msgid "Selected %s more messages in thread"
+msgstr ""
+
+#: pith/pattern.c:1863
+#, c-format
+msgid "Error: Interval \"%s\", min > max"
+msgstr ""
+
+#: pith/pattern.c:1866
+#, c-format
+msgid "Error: Interval \"%s\": syntax is (min,max)"
+msgstr ""
+
+#: pith/pattern.c:3448
+#, c-format
+msgid "Error renaming \"%s\" to \"%s\": %s"
+msgstr ""
+
+#: pith/pattern.c:3499
+msgid "Unknown error saving pattern variable"
+msgstr ""
+
+#: pith/pattern.c:7132
+#, c-format
+msgid "Processing filter \"%s\""
+msgstr ""
+
+#: pith/pattern.c:7751
+msgid "filtering done"
+msgstr ""
+
+#: pith/pattern.c:7908
+#, c-format
+msgid "Can't delete messages in readonly folder \"%s\""
+msgstr ""
+
+#: pith/pattern.c:7986
+#, c-format
+msgid "Filtered all %s message to \"%s\""
+msgstr ""
+
+#: pith/pattern.c:8024
+#, c-format
+msgid "Can't set flags in readonly folder \"%s\""
+msgstr ""
+
+#: pith/abdlc.c:42
+msgid "[ Permission Denied ]"
+msgstr ""
+
+#. TRANSLATORS: This is a heading referring to something that is readable
+#. but not writeable.
+#: pith/abdlc.c:45
+msgid " (ReadOnly)"
+msgstr ""
+
+#. TRANSLATORS: Not readable
+#: pith/abdlc.c:47
+msgid " (Un-readable)"
+msgstr ""
+
+#. TRANSLATORS: Directories, as in LDAP Directories. A heading.
+#: pith/abdlc.c:49
+msgid "Directories"
+msgstr ""
+
+#: pith/abdlc.c:449
+msgid "Addrbook changed unexpectedly, re-syncing..."
+msgstr ""
+
+#: pith/abdlc.c:451
+msgid "addrbook changed while we had it open?, re-sync\n"
+msgstr ""
+
+#: pith/abdlc.c:809 pith/abdlc.c:1180 pith/abdlc.c:1938 pith/abdlc.c:2041
+msgid "Bug in addrbook, not supposed to happen, re-syncing..."
+msgstr ""
+
+#: pith/send.c:296
+#, c-format
+msgid "Can't open %s mailbox: %s"
+msgstr ""
+
+#: pith/send.c:308
+#, c-format
+msgid "%s message folder doesn't exist!"
+msgstr ""
+
+#: pith/send.c:887 pith/reply.c:1071 pith/reply.c:1126
+msgid "Error including all message parts"
+msgstr ""
+
+#. TRANSLATORS: Arg is a mailbox name
+#: pith/send.c:1003
+#, c-format
+msgid "Can't delete %s"
+msgstr ""
+
+#. TRANSLATORS: First arg is the address we can't
+#. send to, second arg is "not in addressbook".
+#: pith/send.c:1499
+#, c-format
+msgid "Can't send to address %s: %s"
+msgstr ""
+
+#: pith/send.c:1503
+msgid "not in addressbook"
+msgstr ""
+
+#. TRANSLATORS: program is busy updating the Answered flags so warns user
+#: pith/send.c:1616
+msgid "Updating \"Answered\" Flags"
+msgstr ""
+
+#: pith/send.c:1753
+msgid "Can't send message. No recipients specified!"
+msgstr ""
+
+#: pith/send.c:1785
+msgid "Sending mail"
+msgstr ""
+
+#: pith/send.c:2004
+msgid "Delivery Status Notification not available from this server."
+msgstr ""
+
+#: pith/send.c:2011
+#, c-format
+msgid "Mail not sent. Sending error%s%s"
+msgstr ""
+
+#: pith/send.c:2034 pith/send.c:2053
+#, c-format
+msgid "Mail not sent: %.80s"
+msgstr ""
+
+#: pith/send.c:2079
+#, c-format
+msgid "Error sending%.2s%.80s"
+msgstr ""
+
+#: pith/send.c:2418
+msgid "Fcc creation error. Message NOT sent or copied."
+msgstr ""
+
+#: pith/send.c:2421
+msgid "Fcc creation rejected. Message NOT sent or copied."
+msgstr ""
+
+#: pith/send.c:4364
+#, c-format
+msgid "Encoding Error \"%s\""
+msgstr ""
+
+#: pith/send.c:4381
+msgid "The following attachment was sent,\r\n"
+msgstr ""
+
+#: pith/send.c:4382
+msgid "but NOT saved in the Fcc copy:\r\n"
+msgstr ""
+
+#: pith/send.c:4977
+#, c-format
+msgid "Space not allowed in header name (%s)"
+msgstr ""
+
+#: pith/send.c:4988
+#, c-format
+msgid "Not allowed to change header \"%s\""
+msgstr ""
+
+#: pith/send.c:5356
+#, c-format
+msgid "Error posting message: %s"
+msgstr ""
+
+#: pith/send.c:5376
+#, c-format
+msgid "Error connecting to news server: %s"
+msgstr ""
+
+#: pith/send.c:5389 pith/send.c:5576
+#, c-format
+msgid "Can't post, NNTP-server must be defined!"
+msgstr ""
+
+#: pith/send.c:5426
+msgid "SMTP-server must be defined!"
+msgstr ""
+
+#: pith/send.c:5437
+msgid "No default posting command."
+msgstr ""
+
+#: pith/send.c:5568
+#, c-format
+msgid "News not posted: \"%s\": %s"
+msgstr ""
+
+#: pith/send.c:5608
+msgid "Error posting."
+msgstr ""
+
+#: pith/send.c:5613
+#, c-format
+msgid "Posting program %s returned error"
+msgstr ""
+
+#: pith/send.c:5619
+#, c-format
+msgid "Error running \"%s\""
+msgstr ""
+
+#: pith/send.c:5691
+#, c-format
+msgid "Unexpected hostname for piped SMTP: %.*s"
+msgstr ""
+
+#. TRANSLATORS: Arg is replaced with the command name or the word Command
+#: pith/mailcmd.c:129
+#, c-format
+msgid "%s cancelled"
+msgstr ""
+
+#: pith/mailcmd.c:264
+msgid "Deletion mark removed, message won't be deleted"
+msgstr ""
+
+#: pith/mailcmd.c:265
+msgid "Message not marked for deletion; no action taken"
+msgstr ""
+
+#: pith/mailcmd.c:269
+#, c-format
+msgid "Deletion mark removed from %s message%s"
+msgstr ""
+
+#: pith/mailcmd.c:669
+#, c-format
+msgid "Opened folder \"%s\" with %s messages%s"
+msgstr ""
+
+#: pith/mailcmd.c:670
+#, c-format
+msgid "Opened folder \"%s\" with %s message%s"
+msgstr ""
+
+#: pith/mailcmd.c:1930
+msgid "recent"
+msgstr ""
+
+#. TRANSLATORS: warning to user that we're busy selecting messages
+#: pith/mailcmd.c:2416
+msgid "Busy Selecting"
+msgstr ""
+
+#: pith/adrbklib.c:274
+msgid "Can't contact remote address book server, using cached copy"
+msgstr ""
+
+#. TRANSLATORS: A temporary file for the address book can't
+#. be opened.
+#: pith/adrbklib.c:488
+#, c-format
+msgid "Temp addrbook file can't be opened: %s"
+msgstr ""
+
+#: pith/adrbklib.c:493
+msgid "Address book doesn't exist"
+msgstr ""
+
+#: pith/adrbklib.c:502
+#, c-format
+msgid "Address book %.200s doesn't exist, creating"
+msgstr ""
+
+#. TRANSLATORS: The address book was changed by something else so alpine
+#. is not making the change the user wanted to make to avoid damaging
+#. the address book.
+#: pith/adrbklib.c:2924
+msgid ""
+"Addrbook changed by another process, aborting our change to avoid damage..."
+msgstr ""
+
+#: pith/adrbklib.c:2953
+msgid "Can't access remote addrbook, aborting change..."
+msgstr ""
+
+#: pith/adrbklib.c:2981
+msgid "No write permission for remote addrbook, aborting change..."
+msgstr ""
+
+#: pith/adrbklib.c:2985
+msgid "Remote addrbook changed, aborting our change to avoid damage..."
+msgstr ""
+
+#: pith/adrbklib.c:3042
+msgid "Saving address book"
+msgstr ""
+
+#: pith/adrbklib.c:3245
+#, c-format
+msgid "Can't replace address book %.200sfile \"%.200s\""
+msgstr ""
+
+#: pith/adrbklib.c:3249
+msgid ""
+"If another Alpine is running, quit that Alpine before updating address book."
+msgstr ""
+
+#: pith/adrbklib.c:3324
+#, c-format
+msgid "Replace of \"%.200s\" failed, trying %.200s"
+msgstr ""
+
+#: pith/adrbklib.c:3339
+#, c-format
+msgid "Copy of addrbook to \"%.200s\" failed, changes NOT saved!"
+msgstr ""
+
+#: pith/adrbklib.c:3377
+msgid "Copying to remote addressbook"
+msgstr ""
+
+#: pith/adrbklib.c:3388
+#, c-format
+msgid "Error opening temporary addrbook file %.200s: %.200s"
+msgstr ""
+
+#: pith/adrbklib.c:3405
+msgid "Copy of addrbook to remote folder failed, changes NOT saved remotely"
+msgstr ""
+
+#: pith/adrbklib.c:3443
+msgid "Interrupt! Reverting to previous version"
+msgstr ""
+
+#: pith/adrbklib.c:4745
+msgid "Sorting address book"
+msgstr ""
+
+#: pith/adrbklib.c:4767
+msgid "Address book sort cancelled, using old order for now"
+msgstr ""
+
+#: pith/adrbklib.c:5441
+#, c-format
+msgid "Error opening/creating address book %.200s"
+msgstr ""
+
+#. TRANSLATORS: groups refers to news groups
+#: pith/news.c:242
+msgid "Can't validate groups. No servers defined"
+msgstr ""
+
+#. TRANSLATORS: groups refers to news groups
+#: pith/news.c:244 pith/news.c:251
+msgid "Can't validate groups. No servers responding"
+msgstr ""
+
+#: pith/news.c:249
+msgid "No servers defined for posting to newsgroups"
+msgstr ""
+
+#: pith/filter.c:1134 pith/filter.c:1142
+msgid "Pipe command returned error."
+msgstr ""
+
+#: pith/filter.c:1147
+msgid "Error setting up pipe command."
+msgstr ""
+
+#: pith/filter.c:1541 pith/filter.c:1566
+msgid "Warning: Non-hexadecimal character in QP encoding!"
+msgstr ""
+
+#. TRANSLATORS: error while translating from one
+#. character set to another, for example from UTF-8
+#. to ISO-2022-JP or something like that.
+#: pith/filter.c:1851 pith/filter.c:10524
+msgid "translation error"
+msgstr ""
+
+#: pith/remote.c:98
+msgid " Can't fetch remote configuration."
+msgstr ""
+
+#: pith/remote.c:857
+#, c-format
+msgid "can't open metadata file %s, continuing (%s)"
+msgstr ""
+
+#: pith/remote.c:897
+#, c-format
+msgid "can't create metadata file %s, continuing (%s)"
+msgstr ""
+
+#: pith/remote.c:1287
+#, c-format
+msgid "Can't initialize folder \"%s\" (write permission)"
+msgstr ""
+
+#: pith/remote.c:1290
+msgid "Choose a new, unused folder for the remote data"
+msgstr ""
+
+#: pith/remote.c:1306
+msgid "Initializing remote data"
+msgstr ""
+
+#: pith/remote.c:1331
+#, c-format
+msgid "\"%s\" has invalid format, can't initialize"
+msgstr ""
+
+#: pith/remote.c:1360 pith/remote.c:1946
+#, c-format
+msgid "Error opening temporary file: %s"
+msgstr ""
+
+#: pith/remote.c:1397
+#, c-format
+msgid "Error copying to remote folder: %s"
+msgstr ""
+
+#: pith/remote.c:1517
+#, c-format
+msgid "Can't open remote address book \"%s\""
+msgstr ""
+
+#: pith/remote.c:1523
+#, c-format
+msgid "Error: no messages in remote address book \"%s\"!"
+msgstr ""
+
+#: pith/remote.c:1537
+#, c-format
+msgid "Suspicious Received headers in first msg in \"%s\""
+msgstr ""
+
+#: pith/remote.c:1923
+#, c-format
+msgid "Can't initialize \"%s\" (invalid format)"
+msgstr ""
+
+#: pith/remote.c:1937
+msgid "Copying remote data"
+msgstr ""
+
+#: pith/remote.c:1958
+#, c-format
+msgid "Error opening temporary file %s: %s"
+msgstr ""
+
+#: pith/remote.c:1976
+msgid "Can't access remote IMAP data"
+msgstr ""
+
+#: pith/remote.c:1997
+msgid "Remote IMAP folder has wrong contents"
+msgstr ""
+
+#: pith/remote.c:2017
+msgid "Can't access check date in remote data"
+msgstr ""
+
+#: pith/remote.c:2087
+#, c-format
+msgid "Error writing temp file: %s"
+msgstr ""
+
+#: pith/remote.c:2095
+#, c-format
+msgid "%s: Error copying remote IMAP data"
+msgstr ""
+
+#: pith/remote.c:2110
+#, c-format
+msgid "Error updating local file: %s: %s"
+msgstr ""
+
+#: pith/remote.c:2113
+msgid "Perhaps another process has the file open?"
+msgstr ""
+
+#: pith/remote.c:2124
+#, c-format
+msgid "Error updating cache file %s: %s"
+msgstr ""
+
+#: pith/remote.c:2154
+#, c-format
+msgid "Can't open remote IMAP folder \"%s\""
+msgstr ""
+
+#: pith/remote.c:2222
+#, c-format
+msgid "Can't open \"%s\" for copying"
+msgstr ""
+
+#: pith/remote.c:2233
+#, c-format
+msgid "\"%s\" has invalid format"
+msgstr ""
+
+#: pith/ldap.c:115
+msgid "Directory lookup failed, using backup email address"
+msgstr ""
+
+#: pith/ldap.c:127
+msgid "Directory lookup failed, no backup email address available"
+msgstr ""
+
+#: pith/ldap.c:167
+#, c-format
+msgid "Warning: current address different from saved address (%s)"
+msgstr ""
+
+#: pith/ldap.c:210
+msgid "Missing username in LDAP address"
+msgstr ""
+
+#. TRANSLATORS: All of the three args together are an error message
+#: pith/ldap.c:482
+#, c-format
+msgid "Access to LDAP server failed: %s%s(%s)"
+msgstr ""
+
+#: pith/ldap.c:594
+msgid "Invalid password"
+msgstr ""
+
+#: pith/ldap.c:598
+#, c-format
+msgid "LDAP server failed: %s%s%s%s"
+msgstr ""
+
+#: pith/ldap.c:941 pith/ldap.c:1023
+#, c-format
+msgid "LDAP search failed: %s%s%s%s"
+msgstr ""
+
+#: pith/ldap.c:978
+#, c-format
+msgid "LDAP partial results: %s%s%s%s"
+msgstr ""
+
+#: pith/ldap.c:1113
+#, c-format
+msgid "SELECT ONE ADDRESS%s%s%s"
+msgstr ""
+
+#: pith/ldap.c:1146
+msgid "None of the names matched on directory server has an email address"
+msgstr ""
+
+#: pith/ldap.c:1158
+msgid "No matches on directory server"
+msgstr ""
+
+#: pith/ldap.c:1731 pith/ldap.c:1739
+msgid "Email Address"
+msgstr ""
+
+#: pith/ldap.c:1733
+msgid "Surname"
+msgstr ""
+
+#: pith/ldap.c:1735
+msgid "Given Name"
+msgstr ""
+
+#: pith/ldap.c:1741
+msgid "Organization"
+msgstr ""
+
+#: pith/ldap.c:1742
+msgid "Unit"
+msgstr ""
+
+#: pith/ldap.c:1743
+msgid "Country"
+msgstr ""
+
+#: pith/ldap.c:1744
+msgid "State or Province"
+msgstr ""
+
+#: pith/ldap.c:1745
+msgid "Locality"
+msgstr ""
+
+#: pith/ldap.c:1746
+msgid "Object Class"
+msgstr ""
+
+#: pith/ldap.c:1747
+msgid "Title"
+msgstr ""
+
+#: pith/ldap.c:1748
+msgid "Department"
+msgstr ""
+
+#: pith/ldap.c:1749
+msgid "Postal Address"
+msgstr ""
+
+#: pith/ldap.c:1750
+msgid "Home Address"
+msgstr ""
+
+#: pith/ldap.c:1751
+msgid "Mail Stop"
+msgstr ""
+
+#: pith/ldap.c:1752
+msgid "Voice Telephone"
+msgstr ""
+
+#: pith/ldap.c:1753
+msgid "Home Telephone"
+msgstr ""
+
+#: pith/ldap.c:1754
+msgid "Office Telephone"
+msgstr ""
+
+#: pith/ldap.c:1755
+msgid "FAX Telephone"
+msgstr ""
+
+#: pith/ldap.c:1756
+msgid "Mobile Telephone"
+msgstr ""
+
+#: pith/ldap.c:1757
+msgid "Pager"
+msgstr ""
+
+#: pith/ldap.c:1758
+msgid "Room Number"
+msgstr ""
+
+#: pith/ldap.c:1759
+msgid "User ID"
+msgstr ""
+
+#. TRANSLATORS: an informative error message
+#: pith/addrbook.c:159
+#, c-format
+msgid "Ignoring unrecognized word \"%s\" in Address-Book-Formats"
+msgstr ""
+
+#: pith/addrbook.c:231
+msgid "Address-Book-Formats has no recognizable words, using default format"
+msgstr ""
+
+#. TRANSLATORS: When a message is forwarded by the user this is the
+#. text that shows where the forwarded part of the message begins.
+#: pith/reply.c:2034
+msgid "---------- Forwarded message ----------"
+msgstr ""
+
+#: pith/reply.c:2466
+msgid "Error fetching message contents. Can't Bounce message"
+msgstr ""
+
+#: pith/reply.c:2948 pith/reply.c:2969
+#, c-format
+msgid "Error fetching part %s"
+msgstr ""
+
+#: pith/reply.c:3389
+msgid "Can't fetch remote configuration."
+msgstr ""
+
+#: pith/stream.c:3277
+#, c-format
+msgid "MAIL FOLDER \"%s\" CLOSED DUE TO ACCESS ERROR"
+msgstr ""
+
+#: pith/stream.c:3283
+#, c-format
+msgid "Folder \"%s\" is Closed"
+msgstr ""
+
+#. TRANSLATORS: The argument is the number of new messages
+#: pith/newmail.c:829
+#, c-format
+msgid "%ld new messages!"
+msgstr ""
+
+#. TRANSLATORS: The argument is either " to you" or nothing
+#: pith/newmail.c:832
+#, c-format
+msgid "New mail%s!"
+msgstr ""
+
+#: pith/newmail.c:833
+msgid " to you"
+msgstr ""
+
+#: pith/newmail.c:839
+#, c-format
+msgid "%ld messages saved to folder \"%.80s\""
+msgstr ""
+
+#: pith/newmail.c:842
+#, c-format
+msgid "Mail saved to folder \"%.80s\""
+msgstr ""
+
+#: pith/newmail.c:849
+#, c-format
+msgid "%ld messages saved to folder \"...%.80s\""
+msgstr ""
+
+#: pith/newmail.c:852
+#, c-format
+msgid "Mail saved to folder \"...%.80s\""
+msgstr ""
+
+#: pith/save.c:762 pith/save.c:892 pith/save.c:1351
+msgid "Can only save to existing folders in Incoming Collection"
+msgstr ""
+
+#. TRANSLATORS: A warning to user that the message parts
+#. they deleted will not be included in the copy they
+#. are now saving to.
+#: pith/save.c:1104 pith/save.c:1251
+msgid "NOTE: Deleted message parts NOT included in saved copy!"
+msgstr ""
+
+#: pith/save.c:1124 pith/save.c:1311
+#, c-format
+msgid "Cannot save because current folder is Closed"
+msgstr ""
+
+#: pith/save.c:1570
+msgid "The following attachment was DELETED when this message was saved:"
+msgstr ""
+
+#. TRANSLATORS: User is restricted to operating within a certain directory
+#: pith/context.c:334
+#, c-format
+msgid "Not allowed outside of %.150s"
+msgstr ""
+
+#: pith/smime.c:312 pith/smime.c:376 pith/smime.c:473 pith/smime.c:831
+#: pith/smime.c:861 pith/smime.c:891
+#, c-format
+msgid "Can't create directory %s"
+msgstr ""
+
+#: pith/smime.c:568 pith/smime.c:901
+msgid "Directory not defined"
+msgstr ""
+
+#: pith/smime.c:575 pith/smime.c:812
+msgid "Turn off the Keychain feature above first"
+msgstr ""
+
+#: pith/smime.c:579 pith/smime.c:816 pith/smime.c:846 pith/smime.c:876
+#: pith/smime.c:906
+msgid "Container path is not defined"
+msgstr ""
+
+#: pith/smime.c:597 pith/smkeys.c:345
+msgid "Can't access remote smime configuration."
+msgstr ""
+
+#: pith/smime.c:731 pith/smime.c:960 pith/smkeys.c:411
+#, c-format
+msgid "Can't rename %s to %s"
+msgstr ""
+
+#: pith/smime.c:745 pith/smkeys.c:426
+#, c-format
+msgid "Error opening temporary smime file %s: %s"
+msgstr ""
+
+#: pith/smime.c:761
+msgid "Copy of smime key to remote folder failed, NOT saved remotely"
+msgstr ""
+
+#: pith/smime.c:825 pith/smime.c:855 pith/smime.c:885
+msgid "Directory is not defined"
+msgstr ""
+
+#: pith/smime.c:1145
+msgid "Wrong password"
+msgstr ""
+
+#: pith/smime.c:1147
+#, c-format
+msgid "Couldn't read key: %s"
+msgstr ""
+
+#: pith/smime.c:1340
+#, c-format
+msgid "Unable to find certificate for <%s@%s>"
+msgstr ""
+
+#: pith/smime.c:1532
+msgid "Couldn't verify S/MIME signature: No CA Certs were loaded"
+msgstr ""
+
+#: pith/smime.c:1540
+msgid "S/MIME signature verified ok"
+msgstr ""
+
+#: pith/smime.c:1554
+#, c-format
+msgid "Couldn't verify S/MIME signature: %s"
+msgstr ""
+
+#: pith/smime.c:1643 pith/smime.c:1769
+msgid ""
+"This message was cryptographically signed but the signature could not be "
+"verified."
+msgstr ""
+
+#: pith/smime.c:1787
+msgid "Couldn't find the certificate needed to decrypt."
+msgstr ""
+
+#: pith/smime.c:1809
+#, c-format
+msgid "Error decrypting: %s"
+msgstr ""
+
+#: pith/smime.c:1838
+msgid "Encrypted data couldn't be parsed."
+msgstr ""
+
+#: pith/smime.c:2041
+msgid "Couldn't find the certificate needed to sign."
+msgstr ""
+
+#: pith/smime.c:2068
+msgid "Error creating signed object."
+msgstr ""
+
+#: pith/smkeys.c:107
+#, c-format
+msgid "Error loading file %s"
+msgstr ""
+
+#: pith/smkeys.c:422
+msgid "Copying to remote smime container"
+msgstr ""
+
+#: pith/smkeys.c:442
+msgid "Copy of smime cert to remote folder failed, changes NOT saved remotely"
+msgstr ""
+
+#: pith/smkeys.c:471
+#, c-format
+msgid "Saved certificate for <%s>"
+msgstr ""
+
+#: pith/smkeys.c:474
+#, c-format
+msgid "Couldn't save certificate for <%s>"
+msgstr ""
+
+#: pith/smkeys.c:637
+msgid "Error in privatekey container, missing END"
+msgstr ""
+
+#: pith/smkeys.c:708
+#, c-format
+msgid "Error in publiccert container, missing BEGIN, certtext=%s"
+msgstr ""
+
+#: pith/smkeys.c:798
+#, c-format
+msgid "Error in cacert container, missing BEGIN, certtext=%s"
+msgstr ""
+
+#: pith/smkeys.c:803
+#, c-format
+msgid "Error in cacert container, missing separator, line=%s"
+msgstr ""
diff --git a/po/boldquot.sed b/po/boldquot.sed
new file mode 100644
index 00000000..4b937aa5
--- /dev/null
+++ b/po/boldquot.sed
@@ -0,0 +1,10 @@
+s/"\([^"]*\)"/“\1â€/g
+s/`\([^`']*\)'/‘\1’/g
+s/ '\([^`']*\)' / ‘\1’ /g
+s/ '\([^`']*\)'$/ ‘\1’/g
+s/^'\([^`']*\)' /‘\1’ /g
+s/“â€/""/g
+s/“/“/g
+s/â€/â€/g
+s/‘/‘/g
+s/’/’/g
diff --git a/po/en@boldquot.header b/po/en@boldquot.header
new file mode 100644
index 00000000..fedb6a06
--- /dev/null
+++ b/po/en@boldquot.header
@@ -0,0 +1,25 @@
+# All this catalog "translates" are quotation characters.
+# The msgids must be ASCII and therefore cannot contain real quotation
+# characters, only substitutes like grave accent (0x60), apostrophe (0x27)
+# and double quote (0x22). These substitutes look strange; see
+# http://www.cl.cam.ac.uk/~mgk25/ucs/quotes.html
+#
+# This catalog translates grave accent (0x60) and apostrophe (0x27) to
+# left single quotation mark (U+2018) and right single quotation mark (U+2019).
+# It also translates pairs of apostrophe (0x27) to
+# left single quotation mark (U+2018) and right single quotation mark (U+2019)
+# and pairs of quotation mark (0x22) to
+# left double quotation mark (U+201C) and right double quotation mark (U+201D).
+#
+# When output to an UTF-8 terminal, the quotation characters appear perfectly.
+# When output to an ISO-8859-1 terminal, the single quotation marks are
+# transliterated to apostrophes (by iconv in glibc 2.2 or newer) or to
+# grave/acute accent (by libiconv), and the double quotation marks are
+# transliterated to 0x22.
+# When output to an ASCII terminal, the single quotation marks are
+# transliterated to apostrophes, and the double quotation marks are
+# transliterated to 0x22.
+#
+# This catalog furthermore displays the text between the quotation marks in
+# bold face, assuming the VT100/XTerm escape sequences.
+#
diff --git a/po/en@quot.header b/po/en@quot.header
new file mode 100644
index 00000000..a9647fc3
--- /dev/null
+++ b/po/en@quot.header
@@ -0,0 +1,22 @@
+# All this catalog "translates" are quotation characters.
+# The msgids must be ASCII and therefore cannot contain real quotation
+# characters, only substitutes like grave accent (0x60), apostrophe (0x27)
+# and double quote (0x22). These substitutes look strange; see
+# http://www.cl.cam.ac.uk/~mgk25/ucs/quotes.html
+#
+# This catalog translates grave accent (0x60) and apostrophe (0x27) to
+# left single quotation mark (U+2018) and right single quotation mark (U+2019).
+# It also translates pairs of apostrophe (0x27) to
+# left single quotation mark (U+2018) and right single quotation mark (U+2019)
+# and pairs of quotation mark (0x22) to
+# left double quotation mark (U+201C) and right double quotation mark (U+201D).
+#
+# When output to an UTF-8 terminal, the quotation characters appear perfectly.
+# When output to an ISO-8859-1 terminal, the single quotation marks are
+# transliterated to apostrophes (by iconv in glibc 2.2 or newer) or to
+# grave/acute accent (by libiconv), and the double quotation marks are
+# transliterated to 0x22.
+# When output to an ASCII terminal, the single quotation marks are
+# transliterated to apostrophes, and the double quotation marks are
+# transliterated to 0x22.
+#
diff --git a/po/howto b/po/howto
new file mode 100644
index 00000000..839c5b06
--- /dev/null
+++ b/po/howto
@@ -0,0 +1,27 @@
+xgettext -d alpine -c --keyword=_ --keyword=N_ -o po/alpine.pot \
+ alpine/*.[ch] pith/*.[ch] pico/*.[ch] \
+ alpine/osdep/*.[ch] pith/osdep/*.[ch] pith/charconv/*.[ch] \
+ pico/osdep/*.[ch]
+
+Usually a translator would do msginit or msgmerge, I think...
+
+Either,
+ msginit -l ja_JP -i po/alpine.pot -o po/ja.po
+
+(or maybe msgmerge if trying to preserve old translations)
+ msgmerge -o po/ja.po po/ja.po po/alpine.pot
+
+Edit po/ja.po to add translations, then
+
+Probably this would get saved in po/ja/LC_MESSAGES/ja.po, I suppose.
+I was putting it here while testing. See the bindtextdomain call
+in pine/pine.c
+
+msgfmt -c -v -o locale/ja/LC_MESSAGES/alpine.mo po/ja.po
+
+NOTE: I had trouble getting ngettext to work correctly. Maybe I
+didn't understand exactly how it was supposed to get set up.
+It is also a GNU-only extension as far as I can tell. Another
+problem with ngettext is that when ENABLE_NLS is not defined
+the English text will not be correct. So let's not use ngettext
+for now.
diff --git a/po/insert-header.sin b/po/insert-header.sin
new file mode 100644
index 00000000..b26de01f
--- /dev/null
+++ b/po/insert-header.sin
@@ -0,0 +1,23 @@
+# Sed script that inserts the file called HEADER before the header entry.
+#
+# At each occurrence of a line starting with "msgid ", we execute the following
+# commands. At the first occurrence, insert the file. At the following
+# occurrences, do nothing. The distinction between the first and the following
+# occurrences is achieved by looking at the hold space.
+/^msgid /{
+x
+# Test if the hold space is empty.
+s/m/m/
+ta
+# Yes it was empty. First occurrence. Read the file.
+r HEADER
+# Output the file's contents by reading the next line. But don't lose the
+# current line while doing this.
+g
+N
+bb
+:a
+# The hold space was nonempty. Following occurrences. Do nothing.
+x
+:b
+}
diff --git a/po/quot.sed b/po/quot.sed
new file mode 100644
index 00000000..0122c463
--- /dev/null
+++ b/po/quot.sed
@@ -0,0 +1,6 @@
+s/"\([^"]*\)"/“\1â€/g
+s/`\([^`']*\)'/‘\1’/g
+s/ '\([^`']*\)' / ‘\1’ /g
+s/ '\([^`']*\)'$/ ‘\1’/g
+s/^'\([^`']*\)' /‘\1’ /g
+s/“â€/""/g
diff --git a/po/remove-potcdate.sin b/po/remove-potcdate.sin
new file mode 100644
index 00000000..2436c49e
--- /dev/null
+++ b/po/remove-potcdate.sin
@@ -0,0 +1,19 @@
+# Sed script that remove the POT-Creation-Date line in the header entry
+# from a POT file.
+#
+# The distinction between the first and the following occurrences of the
+# pattern is achieved by looking at the hold space.
+/^"POT-Creation-Date: .*"$/{
+x
+# Test if the hold space is empty.
+s/P/P/
+ta
+# Yes it was empty. First occurrence. Remove the line.
+g
+d
+bb
+:a
+# The hold space was nonempty. Following occurrences. Do nothing.
+x
+:b
+}
diff --git a/regex/Makefile.am b/regex/Makefile.am
new file mode 100644
index 00000000..78e7c197
--- /dev/null
+++ b/regex/Makefile.am
@@ -0,0 +1,19 @@
+## Process this file with automake to produce Makefile.in
+## Use aclocal -I m4; automake
+
+# ========================================================================
+# Copyright 2006-2007 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+noinst_LIBRARIES = libregex.a
+
+libregex_a_SOURCES = regcomp.c regerror.c regexec.c regfree.c
+
+AM_CPPFLAGS = -I@top_builddir@/include -I@top_srcdir@/include
diff --git a/regex/Makefile.in b/regex/Makefile.in
new file mode 100644
index 00000000..632bba20
--- /dev/null
+++ b/regex/Makefile.in
@@ -0,0 +1,530 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# ========================================================================
+# Copyright 2006-2007 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = regex
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/acx_pthread.m4 \
+ $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
+ $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/VERSION \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/include/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+ARFLAGS = cru
+libregex_a_AR = $(AR) $(ARFLAGS)
+libregex_a_LIBADD =
+am_libregex_a_OBJECTS = regcomp.$(OBJEXT) regerror.$(OBJEXT) \
+ regexec.$(OBJEXT) regfree.$(OBJEXT)
+libregex_a_OBJECTS = $(am_libregex_a_OBJECTS)
+DEFAULT_INCLUDES =
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+SOURCES = $(libregex_a_SOURCES)
+DIST_SOURCES = $(libregex_a_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_CFLAGS = @AM_CFLAGS@
+AM_LDFLAGS = @AM_LDFLAGS@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CP = @CP@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+C_CLIENT_CFLAGS = @C_CLIENT_CFLAGS@
+C_CLIENT_GCCOPTLEVEL = @C_CLIENT_GCCOPTLEVEL@
+C_CLIENT_LDFLAGS = @C_CLIENT_LDFLAGS@
+C_CLIENT_SPECIALS = @C_CLIENT_SPECIALS@
+C_CLIENT_TARGET = @C_CLIENT_TARGET@
+C_CLIENT_WITH_IPV6 = @C_CLIENT_WITH_IPV6@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISPELLPROG = @ISPELLPROG@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN = @LN@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKE = @MAKE@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NPA_PROG = @NPA_PROG@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+POSUB = @POSUB@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+PWPROG = @PWPROG@
+RANLIB = @RANLIB@
+REGEX_BUILD = @REGEX_BUILD@
+RM = @RM@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPELLPROG = @SPELLPROG@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEB_BINDIR = @WEB_BINDIR@
+WEB_BUILD = @WEB_BUILD@
+WEB_PUBCOOKIE_BUILD = @WEB_PUBCOOKIE_BUILD@
+WEB_PUBCOOKIE_LIB = @WEB_PUBCOOKIE_LIB@
+WEB_PUBCOOKIE_LINK = @WEB_PUBCOOKIE_LINK@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+acx_pthread_config = @acx_pthread_config@
+alpine_interactive_spellcheck = @alpine_interactive_spellcheck@
+alpine_simple_spellcheck = @alpine_simple_spellcheck@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+noinst_LIBRARIES = libregex.a
+libregex_a_SOURCES = regcomp.c regerror.c regexec.c regfree.c
+AM_CPPFLAGS = -I@top_builddir@/include -I@top_srcdir@/include
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign regex/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign regex/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLIBRARIES:
+ -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+libregex.a: $(libregex_a_OBJECTS) $(libregex_a_DEPENDENCIES)
+ -rm -f libregex.a
+ $(libregex_a_AR) libregex.a $(libregex_a_OBJECTS) $(libregex_a_LIBADD)
+ $(RANLIB) libregex.a
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regcomp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regerror.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regexec.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regfree.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LIBRARIES)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libtool clean-noinstLIBRARIES ctags distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags uninstall uninstall-am
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/regex/ReadMe b/regex/ReadMe
new file mode 100644
index 00000000..992581b8
--- /dev/null
+++ b/regex/ReadMe
@@ -0,0 +1,39 @@
+The only changes to the source files here are changes to
+keep the more picky modern compilers happy.
+
+The code in this directory is
+
+ Copyright (c) 1992 Henry Spencer.
+ Copyright (c) 1992, 1993
+ The Regents of the University of California. All rights reserved.
+
+ This code is derived from software contributed to Berkeley by
+ Henry Spencer of the University of Toronto.
+
+ 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.
diff --git a/regex/cclass.h b/regex/cclass.h
new file mode 100644
index 00000000..cabe7faf
--- /dev/null
+++ b/regex/cclass.h
@@ -0,0 +1,70 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994 Henry Spencer.
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Henry Spencer.
+ *
+ * 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.
+ *
+ * @(#)cclass.h 8.3 (Berkeley) 3/20/94
+ */
+
+/* character-class table */
+static struct cclass {
+ char *name;
+ char *chars;
+ char *multis;
+} cclasses[] = {
+ {"alnum", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\
+0123456789", ""},
+ {"alpha", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
+ ""},
+ {"blank", " \t", ""},
+ {"cntrl", "\007\b\t\n\v\f\r\1\2\3\4\5\6\16\17\20\21\22\23\24\
+\25\26\27\30\31\32\33\34\35\36\37\177", ""},
+ {"digit", "0123456789", ""},
+ {"graph", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\
+0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~",
+ ""},
+ {"lower", "abcdefghijklmnopqrstuvwxyz",
+ ""},
+ {"print", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\
+0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ ",
+ ""},
+ {"punct", "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~",
+ ""},
+ {"space", "\t\n\v\f\r ", ""},
+ {"upper", "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
+ ""},
+ {"xdigit", "0123456789ABCDEFabcdef",
+ ""},
+ {NULL, 0, ""}
+};
diff --git a/regex/cname.h b/regex/cname.h
new file mode 100644
index 00000000..c6ba15bd
--- /dev/null
+++ b/regex/cname.h
@@ -0,0 +1,141 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994 Henry Spencer.
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Henry Spencer.
+ *
+ * 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.
+ *
+ * @(#)cname.h 8.3 (Berkeley) 3/20/94
+ */
+
+/* character-name table */
+static struct cname {
+ char *name;
+ char code;
+} cnames[] = {
+ {"NUL", '\0'},
+ {"SOH", '\001'},
+ {"STX", '\002'},
+ {"ETX", '\003'},
+ {"EOT", '\004'},
+ {"ENQ", '\005'},
+ {"ACK", '\006'},
+ {"BEL", '\007'},
+ {"alert", '\007'},
+ {"BS", '\010'},
+ {"backspace", '\b'},
+ {"HT", '\011'},
+ {"tab", '\t'},
+ {"LF", '\012'},
+ {"newline", '\n'},
+ {"VT", '\013'},
+ {"vertical-tab", '\v'},
+ {"FF", '\014'},
+ {"form-feed", '\f'},
+ {"CR", '\015'},
+ {"carriage-return", '\r'},
+ {"SO", '\016'},
+ {"SI", '\017'},
+ {"DLE", '\020'},
+ {"DC1", '\021'},
+ {"DC2", '\022'},
+ {"DC3", '\023'},
+ {"DC4", '\024'},
+ {"NAK", '\025'},
+ {"SYN", '\026'},
+ {"ETB", '\027'},
+ {"CAN", '\030'},
+ {"EM", '\031'},
+ {"SUB", '\032'},
+ {"ESC", '\033'},
+ {"IS4", '\034'},
+ {"FS", '\034'},
+ {"IS3", '\035'},
+ {"GS", '\035'},
+ {"IS2", '\036'},
+ {"RS", '\036'},
+ {"IS1", '\037'},
+ {"US", '\037'},
+ {"space", ' '},
+ {"exclamation-mark", '!'},
+ {"quotation-mark", '"'},
+ {"number-sign", '#'},
+ {"dollar-sign", '$'},
+ {"percent-sign", '%'},
+ {"ampersand", '&'},
+ {"apostrophe", '\''},
+ {"left-parenthesis", '('},
+ {"right-parenthesis", ')'},
+ {"asterisk", '*'},
+ {"plus-sign", '+'},
+ {"comma", ','},
+ {"hyphen", '-'},
+ {"hyphen-minus", '-'},
+ {"period", '.'},
+ {"full-stop", '.'},
+ {"slash", '/'},
+ {"solidus", '/'},
+ {"zero", '0'},
+ {"one", '1'},
+ {"two", '2'},
+ {"three", '3'},
+ {"four", '4'},
+ {"five", '5'},
+ {"six", '6'},
+ {"seven", '7'},
+ {"eight", '8'},
+ {"nine", '9'},
+ {"colon", ':'},
+ {"semicolon", ';'},
+ {"less-than-sign", '<'},
+ {"equals-sign", '='},
+ {"greater-than-sign", '>'},
+ {"question-mark", '?'},
+ {"commercial-at", '@'},
+ {"left-square-bracket", '['},
+ {"backslash", '\\'},
+ {"reverse-solidus", '\\'},
+ {"right-square-bracket", ']'},
+ {"circumflex", '^'},
+ {"circumflex-accent", '^'},
+ {"underscore", '_'},
+ {"low-line", '_'},
+ {"grave-accent", '`'},
+ {"left-brace", '{'},
+ {"left-curly-bracket", '{'},
+ {"vertical-line", '|'},
+ {"right-brace", '}'},
+ {"right-curly-bracket", '}'},
+ {"tilde", '~'},
+ {"DEL", '\177'},
+ {NULL, 0}
+};
diff --git a/regex/engine.c b/regex/engine.c
new file mode 100644
index 00000000..e9bb5d4b
--- /dev/null
+++ b/regex/engine.c
@@ -0,0 +1,1053 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994 Henry Spencer.
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Henry Spencer.
+ *
+ * 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.
+ *
+ * @(#)engine.c 8.5 (Berkeley) 3/20/94
+ */
+
+/*
+ * The matching engine and friends. This file is #included by regexec.c
+ * after suitable #defines of a variety of macros used herein, so that
+ * different state representations can be used without duplicating masses
+ * of code.
+ */
+
+#ifdef SNAMES
+#define matcher smatcher
+#define fast sfast
+#define slow sslow
+#define dissect sdissect
+#define backref sbackref
+#define step sstep
+#define print sprint
+#define at sat
+#define match smat
+#endif
+#ifdef LNAMES
+#define matcher lmatcher
+#define fast lfast
+#define slow lslow
+#define dissect ldissect
+#define backref lbackref
+#define step lstep
+#define print lprint
+#define at lat
+#define match lmat
+#endif
+
+/* another structure passed up and down to avoid zillions of parameters */
+struct match {
+ struct re_guts *g;
+ int eflags;
+ regmatch_t *pmatch; /* [nsub+1] (0 element unused) */
+ char *offp; /* offsets work from here */
+ char *beginp; /* start of string -- virtual NUL precedes */
+ char *endp; /* end of string -- virtual NUL here */
+ char *coldp; /* can be no match starting before here */
+ char **lastpos; /* [nplus+1] */
+ STATEVARS;
+ states st; /* current states */
+ states fresh; /* states for a fresh start */
+ states tmp; /* temporary */
+ states empty; /* empty set of states */
+};
+
+/* ========= begin header generated by ./mkh ========= */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* === engine.c === */
+static int matcher __P((struct re_guts *g, char *string, size_t nmatch, regmatch_t pmatch[], int eflags));
+static char *dissect __P((struct match *m, char *start, char *stop, sopno startst, sopno stopst));
+static char *backref __P((struct match *m, char *start, char *stop, sopno startst, sopno stopst, sopno lev));
+static char *fast __P((struct match *m, char *start, char *stop, sopno startst, sopno stopst));
+static char *slow __P((struct match *m, char *start, char *stop, sopno startst, sopno stopst));
+static states step __P((struct re_guts *g, sopno start, sopno stop, states bef, int ch, states aft));
+#define BOL (OUT+1)
+#define EOL (BOL+1)
+#define BOLEOL (BOL+2)
+#define NOTHING (BOL+3)
+#define BOW (BOL+4)
+#define EOW (BOL+5)
+#define CODEMAX (BOL+5) /* highest code used */
+#define NONCHAR(c) ((c) > CHAR_MAX)
+#define NNONCHAR (CODEMAX-CHAR_MAX)
+#ifdef REDEBUG
+static void print __P((struct match *m, char *caption, states st, int ch, FILE *d));
+#endif
+#ifdef REDEBUG
+static void at __P((struct match *m, char *title, char *start, char *stop, sopno startst, sopno stopst));
+#endif
+#ifdef REDEBUG
+static char *pchar __P((int ch));
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+/* ========= end header generated by ./mkh ========= */
+
+#ifdef REDEBUG
+#define SP(t, s, c) print(m, t, s, c, stdout)
+#define AT(t, p1, p2, s1, s2) at(m, t, p1, p2, s1, s2)
+#define NOTE(str) { if (m->eflags&REG_TRACE) printf("=%s\n", (str)); }
+#else
+#define SP(t, s, c) /* nothing */
+#define AT(t, p1, p2, s1, s2) /* nothing */
+#define NOTE(s) /* nothing */
+#endif
+
+/*
+ - matcher - the actual matching engine
+ == static int matcher(register struct re_guts *g, char *string, \
+ == size_t nmatch, regmatch_t pmatch[], int eflags);
+ */
+static int /* 0 success, REG_NOMATCH failure */
+matcher(struct re_guts *g, char *string, size_t nmatch, regmatch_t pmatch[], int eflags)
+{
+ register char *endp;
+ register int i;
+ struct match mv;
+ register struct match *m = &mv;
+ register char *dp;
+ const register sopno gf = g->firststate+1; /* +1 for OEND */
+ const register sopno gl = g->laststate;
+ char *start;
+ char *stop;
+
+ /* simplify the situation where possible */
+ if (g->cflags&REG_NOSUB)
+ nmatch = 0;
+ if (eflags&REG_STARTEND) {
+ start = string + pmatch[0].rm_so;
+ stop = string + pmatch[0].rm_eo;
+ } else {
+ start = string;
+ stop = start + strlen(start);
+ }
+ if (stop < start)
+ return(REG_INVARG);
+
+ /* prescreening; this does wonders for this rather slow code */
+ if (g->must != NULL) {
+ for (dp = start; dp < stop; dp++)
+ if (*dp == g->must[0] && stop - dp >= g->mlen &&
+ memcmp(dp, g->must, (size_t)g->mlen) == 0)
+ break;
+ if (dp == stop) /* we didn't find g->must */
+ return(REG_NOMATCH);
+ }
+
+ /* match struct setup */
+ m->g = g;
+ m->eflags = eflags;
+ m->pmatch = NULL;
+ m->lastpos = NULL;
+ m->offp = string;
+ m->beginp = start;
+ m->endp = stop;
+ STATESETUP(m, 4);
+ SETUP(m->st);
+ SETUP(m->fresh);
+ SETUP(m->tmp);
+ SETUP(m->empty);
+ CLEAR(m->empty);
+
+ /* this loop does only one repetition except for backrefs */
+ for (;;) {
+ endp = fast(m, start, stop, gf, gl);
+ if (endp == NULL) { /* a miss */
+ STATETEARDOWN(m);
+ return(REG_NOMATCH);
+ }
+ if (nmatch == 0 && !g->backrefs)
+ break; /* no further info needed */
+
+ /* where? */
+ assert(m->coldp != NULL);
+ for (;;) {
+ NOTE("finding start");
+ endp = slow(m, m->coldp, stop, gf, gl);
+ if (endp != NULL)
+ break;
+ assert(m->coldp < m->endp);
+ m->coldp++;
+ }
+ if (nmatch == 1 && !g->backrefs)
+ break; /* no further info needed */
+
+ /* oh my, he wants the subexpressions... */
+ if (m->pmatch == NULL)
+ m->pmatch = (regmatch_t *)malloc((m->g->nsub + 1) *
+ sizeof(regmatch_t));
+ if (m->pmatch == NULL) {
+ STATETEARDOWN(m);
+ return(REG_ESPACE);
+ }
+ for (i = 1; i <= m->g->nsub; i++)
+ m->pmatch[i].rm_so = m->pmatch[i].rm_eo = -1;
+ if (!g->backrefs && !(m->eflags&REG_BACKR)) {
+ NOTE("dissecting");
+ dp = dissect(m, m->coldp, endp, gf, gl);
+ } else {
+ if (g->nplus > 0 && m->lastpos == NULL)
+ m->lastpos = (char **)malloc((g->nplus+1) *
+ sizeof(char *));
+ if (g->nplus > 0 && m->lastpos == NULL) {
+ free(m->pmatch);
+ STATETEARDOWN(m);
+ return(REG_ESPACE);
+ }
+ NOTE("backref dissect");
+ dp = backref(m, m->coldp, endp, gf, gl, (sopno)0);
+ }
+ if (dp != NULL)
+ break;
+
+ /* uh-oh... we couldn't find a subexpression-level match */
+ assert(g->backrefs); /* must be back references doing it */
+ assert(g->nplus == 0 || m->lastpos != NULL);
+ for (;;) {
+ if (dp != NULL || endp <= m->coldp)
+ break; /* defeat */
+ NOTE("backoff");
+ endp = slow(m, m->coldp, endp-1, gf, gl);
+ if (endp == NULL)
+ break; /* defeat */
+ /* try it on a shorter possibility */
+#ifndef NDEBUG
+ for (i = 1; i <= m->g->nsub; i++) {
+ assert(m->pmatch[i].rm_so == -1);
+ assert(m->pmatch[i].rm_eo == -1);
+ }
+#endif
+ NOTE("backoff dissect");
+ dp = backref(m, m->coldp, endp, gf, gl, (sopno)0);
+ }
+ assert(dp == NULL || dp == endp);
+ if (dp != NULL) /* found a shorter one */
+ break;
+
+ /* despite initial appearances, there is no match here */
+ NOTE("false alarm");
+ start = m->coldp + 1; /* recycle starting later */
+ assert(start <= stop);
+ }
+
+ /* fill in the details if requested */
+ if (nmatch > 0) {
+ pmatch[0].rm_so = m->coldp - m->offp;
+ pmatch[0].rm_eo = endp - m->offp;
+ }
+ if (nmatch > 1) {
+ assert(m->pmatch != NULL);
+ for (i = 1; i < nmatch; i++)
+ if (i <= m->g->nsub)
+ pmatch[i] = m->pmatch[i];
+ else {
+ pmatch[i].rm_so = -1;
+ pmatch[i].rm_eo = -1;
+ }
+ }
+
+ if (m->pmatch != NULL)
+ free((char *)m->pmatch);
+ if (m->lastpos != NULL)
+ free((char *)m->lastpos);
+ STATETEARDOWN(m);
+ return(0);
+}
+
+/*
+ - dissect - figure out what matched what, no back references
+ == static char *dissect(register struct match *m, char *start, \
+ == char *stop, sopno startst, sopno stopst);
+ */
+static char * /* == stop (success) always */
+dissect(struct match *m, char *start, char *stop, sopno startst, sopno stopst)
+{
+ register int i;
+ register sopno ss; /* start sop of current subRE */
+ register sopno es; /* end sop of current subRE */
+ register char *sp; /* start of string matched by it */
+ register char *stp; /* string matched by it cannot pass here */
+ register char *rest; /* start of rest of string */
+ register char *tail; /* string unmatched by rest of RE */
+ register sopno ssub; /* start sop of subsubRE */
+ register sopno esub; /* end sop of subsubRE */
+ register char *ssp; /* start of string matched by subsubRE */
+ register char *sep; /* end of string matched by subsubRE */
+ register char *oldssp; /* previous ssp */
+ register char *dp;
+
+ AT("diss", start, stop, startst, stopst);
+ sp = start;
+ for (ss = startst; ss < stopst; ss = es) {
+ /* identify end of subRE */
+ es = ss;
+ switch (OP(m->g->strip[es])) {
+ case OPLUS_:
+ case OQUEST_:
+ es += OPND(m->g->strip[es]);
+ break;
+ case OCH_:
+ while (OP(m->g->strip[es]) != O_CH)
+ es += OPND(m->g->strip[es]);
+ break;
+ }
+ es++;
+
+ /* figure out what it matched */
+ switch (OP(m->g->strip[ss])) {
+ case OEND:
+ assert(nope);
+ break;
+ case OCHAR:
+ sp++;
+ break;
+ case OBOL:
+ case OEOL:
+ case OBOW:
+ case OEOW:
+ break;
+ case OANY:
+ case OANYOF:
+ sp++;
+ break;
+ case OBACK_:
+ case O_BACK:
+ assert(nope);
+ break;
+ /* cases where length of match is hard to find */
+ case OQUEST_:
+ stp = stop;
+ for (;;) {
+ /* how long could this one be? */
+ rest = slow(m, sp, stp, ss, es);
+ assert(rest != NULL); /* it did match */
+ /* could the rest match the rest? */
+ tail = slow(m, rest, stop, es, stopst);
+ if (tail == stop)
+ break; /* yes! */
+ /* no -- try a shorter match for this one */
+ stp = rest - 1;
+ assert(stp >= sp); /* it did work */
+ }
+ ssub = ss + 1;
+ esub = es - 1;
+ /* did innards match? */
+ if (slow(m, sp, rest, ssub, esub) != NULL) {
+ dp = dissect(m, sp, rest, ssub, esub);
+ assert(dp == rest);
+ } else /* no */
+ assert(sp == rest);
+ sp = rest;
+ break;
+ case OPLUS_:
+ stp = stop;
+ for (;;) {
+ /* how long could this one be? */
+ rest = slow(m, sp, stp, ss, es);
+ assert(rest != NULL); /* it did match */
+ /* could the rest match the rest? */
+ tail = slow(m, rest, stop, es, stopst);
+ if (tail == stop)
+ break; /* yes! */
+ /* no -- try a shorter match for this one */
+ stp = rest - 1;
+ assert(stp >= sp); /* it did work */
+ }
+ ssub = ss + 1;
+ esub = es - 1;
+ ssp = sp;
+ oldssp = ssp;
+ for (;;) { /* find last match of innards */
+ sep = slow(m, ssp, rest, ssub, esub);
+ if (sep == NULL || sep == ssp)
+ break; /* failed or matched null */
+ oldssp = ssp; /* on to next try */
+ ssp = sep;
+ }
+ if (sep == NULL) {
+ /* last successful match */
+ sep = ssp;
+ ssp = oldssp;
+ }
+ assert(sep == rest); /* must exhaust substring */
+ assert(slow(m, ssp, sep, ssub, esub) == rest);
+ dp = dissect(m, ssp, sep, ssub, esub);
+ assert(dp == sep);
+ sp = rest;
+ break;
+ case OCH_:
+ stp = stop;
+ for (;;) {
+ /* how long could this one be? */
+ rest = slow(m, sp, stp, ss, es);
+ assert(rest != NULL); /* it did match */
+ /* could the rest match the rest? */
+ tail = slow(m, rest, stop, es, stopst);
+ if (tail == stop)
+ break; /* yes! */
+ /* no -- try a shorter match for this one */
+ stp = rest - 1;
+ assert(stp >= sp); /* it did work */
+ }
+ ssub = ss + 1;
+ esub = ss + OPND(m->g->strip[ss]) - 1;
+ assert(OP(m->g->strip[esub]) == OOR1);
+ for (;;) { /* find first matching branch */
+ if (slow(m, sp, rest, ssub, esub) == rest)
+ break; /* it matched all of it */
+ /* that one missed, try next one */
+ assert(OP(m->g->strip[esub]) == OOR1);
+ esub++;
+ assert(OP(m->g->strip[esub]) == OOR2);
+ ssub = esub + 1;
+ esub += OPND(m->g->strip[esub]);
+ if (OP(m->g->strip[esub]) == OOR2)
+ esub--;
+ else
+ assert(OP(m->g->strip[esub]) == O_CH);
+ }
+ dp = dissect(m, sp, rest, ssub, esub);
+ assert(dp == rest);
+ sp = rest;
+ break;
+ case O_PLUS:
+ case O_QUEST:
+ case OOR1:
+ case OOR2:
+ case O_CH:
+ assert(nope);
+ break;
+ case OLPAREN:
+ i = OPND(m->g->strip[ss]);
+ assert(0 < i && i <= m->g->nsub);
+ m->pmatch[i].rm_so = sp - m->offp;
+ break;
+ case ORPAREN:
+ i = OPND(m->g->strip[ss]);
+ assert(0 < i && i <= m->g->nsub);
+ m->pmatch[i].rm_eo = sp - m->offp;
+ break;
+ default: /* uh oh */
+ assert(nope);
+ break;
+ }
+ }
+
+ assert(sp == stop);
+ return(sp);
+}
+
+/*
+ - backref - figure out what matched what, figuring in back references
+ == static char *backref(register struct match *m, char *start, \
+ == char *stop, sopno startst, sopno stopst, sopno lev);
+ */
+static char * /* == stop (success) or NULL (failure) */
+backref(struct match *m, char *start, char *stop, sopno startst, sopno stopst, sopno lev)
+{
+ register int i;
+ register sopno ss; /* start sop of current subRE */
+ register char *sp; /* start of string matched by it */
+ register sopno ssub; /* start sop of subsubRE */
+ register sopno esub; /* end sop of subsubRE */
+ register char *ssp; /* start of string matched by subsubRE */
+ register char *dp;
+ register size_t len;
+ register int hard;
+ register sop s;
+ register regoff_t offsave;
+ register cset *cs;
+
+ AT("back", start, stop, startst, stopst);
+ sp = start;
+
+ /* get as far as we can with easy stuff */
+ hard = 0;
+ for (ss = startst; !hard && ss < stopst; ss++)
+ switch (OP(s = m->g->strip[ss])) {
+ case OCHAR:
+ if (sp == stop || *sp++ != (char)OPND(s))
+ return(NULL);
+ break;
+ case OANY:
+ if (sp == stop)
+ return(NULL);
+ sp++;
+ break;
+ case OANYOF:
+ cs = &m->g->sets[OPND(s)];
+ if (sp == stop || !CHIN(cs, *sp++))
+ return(NULL);
+ break;
+ case OBOL:
+ if ( (sp == m->beginp && !(m->eflags&REG_NOTBOL)) ||
+ (sp < m->endp && *(sp-1) == '\n' &&
+ (m->g->cflags&REG_NEWLINE)) )
+ { /* yes */ }
+ else
+ return(NULL);
+ break;
+ case OEOL:
+ if ( (sp == m->endp && !(m->eflags&REG_NOTEOL)) ||
+ (sp < m->endp && *sp == '\n' &&
+ (m->g->cflags&REG_NEWLINE)) )
+ { /* yes */ }
+ else
+ return(NULL);
+ break;
+ case OBOW:
+ if (( (sp == m->beginp && !(m->eflags&REG_NOTBOL)) ||
+ (sp < m->endp && *(sp-1) == '\n' &&
+ (m->g->cflags&REG_NEWLINE)) ||
+ (sp > m->beginp &&
+ !ISWORD(*(sp-1))) ) &&
+ (sp < m->endp && ISWORD(*sp)) )
+ { /* yes */ }
+ else
+ return(NULL);
+ break;
+ case OEOW:
+ if (( (sp == m->endp && !(m->eflags&REG_NOTEOL)) ||
+ (sp < m->endp && *sp == '\n' &&
+ (m->g->cflags&REG_NEWLINE)) ||
+ (sp < m->endp && !ISWORD(*sp)) ) &&
+ (sp > m->beginp && ISWORD(*(sp-1))) )
+ { /* yes */ }
+ else
+ return(NULL);
+ break;
+ case O_QUEST:
+ break;
+ case OOR1: /* matches null but needs to skip */
+ ss++;
+ s = m->g->strip[ss];
+ do {
+ assert(OP(s) == OOR2);
+ ss += OPND(s);
+ } while (OP(s = m->g->strip[ss]) != O_CH);
+ /* note that the ss++ gets us past the O_CH */
+ break;
+ default: /* have to make a choice */
+ hard = 1;
+ break;
+ }
+ if (!hard) { /* that was it! */
+ if (sp != stop)
+ return(NULL);
+ return(sp);
+ }
+ ss--; /* adjust for the for's final increment */
+
+ /* the hard stuff */
+ AT("hard", sp, stop, ss, stopst);
+ s = m->g->strip[ss];
+ switch (OP(s)) {
+ case OBACK_: /* the vilest depths */
+ i = OPND(s);
+ assert(0 < i && i <= m->g->nsub);
+ if (m->pmatch[i].rm_eo == -1)
+ return(NULL);
+ assert(m->pmatch[i].rm_so != -1);
+ len = m->pmatch[i].rm_eo - m->pmatch[i].rm_so;
+ assert(stop - m->beginp >= len);
+ if (sp > stop - len)
+ return(NULL); /* not enough left to match */
+ ssp = m->offp + m->pmatch[i].rm_so;
+ if (memcmp(sp, ssp, len) != 0)
+ return(NULL);
+ while (m->g->strip[ss] != SOP(O_BACK, i))
+ ss++;
+ return(backref(m, sp+len, stop, ss+1, stopst, lev));
+ break;
+ case OQUEST_: /* to null or not */
+ dp = backref(m, sp, stop, ss+1, stopst, lev);
+ if (dp != NULL)
+ return(dp); /* not */
+ return(backref(m, sp, stop, ss+OPND(s)+1, stopst, lev));
+ break;
+ case OPLUS_:
+ assert(m->lastpos != NULL);
+ assert(lev+1 <= m->g->nplus);
+ m->lastpos[lev+1] = sp;
+ return(backref(m, sp, stop, ss+1, stopst, lev+1));
+ break;
+ case O_PLUS:
+ if (sp == m->lastpos[lev]) /* last pass matched null */
+ return(backref(m, sp, stop, ss+1, stopst, lev-1));
+ /* try another pass */
+ m->lastpos[lev] = sp;
+ dp = backref(m, sp, stop, ss-OPND(s)+1, stopst, lev);
+ if (dp == NULL)
+ return(backref(m, sp, stop, ss+1, stopst, lev-1));
+ else
+ return(dp);
+ break;
+ case OCH_: /* find the right one, if any */
+ ssub = ss + 1;
+ esub = ss + OPND(s) - 1;
+ assert(OP(m->g->strip[esub]) == OOR1);
+ for (;;) { /* find first matching branch */
+ dp = backref(m, sp, stop, ssub, esub, lev);
+ if (dp != NULL)
+ return(dp);
+ /* that one missed, try next one */
+ if (OP(m->g->strip[esub]) == O_CH)
+ return(NULL); /* there is none */
+ esub++;
+ assert(OP(m->g->strip[esub]) == OOR2);
+ ssub = esub + 1;
+ esub += OPND(m->g->strip[esub]);
+ if (OP(m->g->strip[esub]) == OOR2)
+ esub--;
+ else
+ assert(OP(m->g->strip[esub]) == O_CH);
+ }
+ break;
+ case OLPAREN: /* must undo assignment if rest fails */
+ i = OPND(s);
+ assert(0 < i && i <= m->g->nsub);
+ offsave = m->pmatch[i].rm_so;
+ m->pmatch[i].rm_so = sp - m->offp;
+ dp = backref(m, sp, stop, ss+1, stopst, lev);
+ if (dp != NULL)
+ return(dp);
+ m->pmatch[i].rm_so = offsave;
+ return(NULL);
+ break;
+ case ORPAREN: /* must undo assignment if rest fails */
+ i = OPND(s);
+ assert(0 < i && i <= m->g->nsub);
+ offsave = m->pmatch[i].rm_eo;
+ m->pmatch[i].rm_eo = sp - m->offp;
+ dp = backref(m, sp, stop, ss+1, stopst, lev);
+ if (dp != NULL)
+ return(dp);
+ m->pmatch[i].rm_eo = offsave;
+ return(NULL);
+ break;
+ default: /* uh oh */
+ assert(nope);
+ break;
+ }
+
+ /* "can't happen" */
+ assert(nope);
+ /* NOTREACHED */
+ return(NULL);
+}
+
+/*
+ - fast - step through the string at top speed
+ == static char *fast(register struct match *m, char *start, \
+ == char *stop, sopno startst, sopno stopst);
+ */
+static char * /* where tentative match ended, or NULL */
+fast(struct match *m, char *start, char *stop, sopno startst, sopno stopst)
+{
+ register states st = m->st;
+ register states fresh = m->fresh;
+ register states tmp = m->tmp;
+ register char *p = start;
+ register int c = (start == m->beginp) ? OUT : *(start-1);
+ register int lastc; /* previous c */
+ register int flagch;
+ register int i;
+ register char *coldp; /* last p after which no match was underway */
+
+ CLEAR(st);
+ SET1(st, startst);
+ st = step(m->g, startst, stopst, st, NOTHING, st);
+ ASSIGN(fresh, st);
+ SP("start", st, *p);
+ coldp = NULL;
+ for (;;) {
+ /* next character */
+ lastc = c;
+ c = (p == m->endp) ? OUT : *p;
+ if (EQ(st, fresh))
+ coldp = p;
+
+ /* is there an EOL and/or BOL between lastc and c? */
+ flagch = '\0';
+ i = 0;
+ if ( (lastc == '\n' && m->g->cflags&REG_NEWLINE) ||
+ (lastc == OUT && !(m->eflags&REG_NOTBOL)) ) {
+ flagch = BOL;
+ i = m->g->nbol;
+ }
+ if ( (c == '\n' && m->g->cflags&REG_NEWLINE) ||
+ (c == OUT && !(m->eflags&REG_NOTEOL)) ) {
+ flagch = (flagch == BOL) ? BOLEOL : EOL;
+ i += m->g->neol;
+ }
+ if (i != 0) {
+ for (; i > 0; i--)
+ st = step(m->g, startst, stopst, st, flagch, st);
+ SP("boleol", st, c);
+ }
+
+ /* how about a word boundary? */
+ if ( (flagch == BOL || (lastc != OUT && !ISWORD(lastc))) &&
+ (c != OUT && ISWORD(c)) ) {
+ flagch = BOW;
+ }
+ if ( (lastc != OUT && ISWORD(lastc)) &&
+ (flagch == EOL || (c != OUT && !ISWORD(c))) ) {
+ flagch = EOW;
+ }
+ if (flagch == BOW || flagch == EOW) {
+ st = step(m->g, startst, stopst, st, flagch, st);
+ SP("boweow", st, c);
+ }
+
+ /* are we done? */
+ if (ISSET(st, stopst) || p == stop)
+ break; /* NOTE BREAK OUT */
+
+ /* no, we must deal with this character */
+ ASSIGN(tmp, st);
+ ASSIGN(st, fresh);
+ assert(c != OUT);
+ st = step(m->g, startst, stopst, tmp, c, st);
+ SP("aft", st, c);
+ assert(EQ(step(m->g, startst, stopst, st, NOTHING, st), st));
+ p++;
+ }
+
+ assert(coldp != NULL);
+ m->coldp = coldp;
+ if (ISSET(st, stopst))
+ return(p+1);
+ else
+ return(NULL);
+}
+
+/*
+ - slow - step through the string more deliberately
+ == static char *slow(register struct match *m, char *start, \
+ == char *stop, sopno startst, sopno stopst);
+ */
+static char * /* where it ended */
+slow(struct match *m, char *start, char *stop, sopno startst, sopno stopst)
+{
+ register states st = m->st;
+ register states empty = m->empty;
+ register states tmp = m->tmp;
+ register char *p = start;
+ register int c = (start == m->beginp) ? OUT : *(start-1);
+ register int lastc; /* previous c */
+ register int flagch;
+ register int i;
+ register char *matchp; /* last p at which a match ended */
+
+ AT("slow", start, stop, startst, stopst);
+ CLEAR(st);
+ SET1(st, startst);
+ SP("sstart", st, *p);
+ st = step(m->g, startst, stopst, st, NOTHING, st);
+ matchp = NULL;
+ for (;;) {
+ /* next character */
+ lastc = c;
+ c = (p == m->endp) ? OUT : *p;
+
+ /* is there an EOL and/or BOL between lastc and c? */
+ flagch = '\0';
+ i = 0;
+ if ( (lastc == '\n' && m->g->cflags&REG_NEWLINE) ||
+ (lastc == OUT && !(m->eflags&REG_NOTBOL)) ) {
+ flagch = BOL;
+ i = m->g->nbol;
+ }
+ if ( (c == '\n' && m->g->cflags&REG_NEWLINE) ||
+ (c == OUT && !(m->eflags&REG_NOTEOL)) ) {
+ flagch = (flagch == BOL) ? BOLEOL : EOL;
+ i += m->g->neol;
+ }
+ if (i != 0) {
+ for (; i > 0; i--)
+ st = step(m->g, startst, stopst, st, flagch, st);
+ SP("sboleol", st, c);
+ }
+
+ /* how about a word boundary? */
+ if ( (flagch == BOL || (lastc != OUT && !ISWORD(lastc))) &&
+ (c != OUT && ISWORD(c)) ) {
+ flagch = BOW;
+ }
+ if ( (lastc != OUT && ISWORD(lastc)) &&
+ (flagch == EOL || (c != OUT && !ISWORD(c))) ) {
+ flagch = EOW;
+ }
+ if (flagch == BOW || flagch == EOW) {
+ st = step(m->g, startst, stopst, st, flagch, st);
+ SP("sboweow", st, c);
+ }
+
+ /* are we done? */
+ if (ISSET(st, stopst))
+ matchp = p;
+ if (EQ(st, empty) || p == stop)
+ break; /* NOTE BREAK OUT */
+
+ /* no, we must deal with this character */
+ ASSIGN(tmp, st);
+ ASSIGN(st, empty);
+ assert(c != OUT);
+ st = step(m->g, startst, stopst, tmp, c, st);
+ SP("saft", st, c);
+ assert(EQ(step(m->g, startst, stopst, st, NOTHING, st), st));
+ p++;
+ }
+
+ return(matchp);
+}
+
+
+/*
+ - step - map set of states reachable before char to set reachable after
+ == static states step(register struct re_guts *g, sopno start, sopno stop, \
+ == register states bef, int ch, register states aft);
+ == #define BOL (OUT+1)
+ == #define EOL (BOL+1)
+ == #define BOLEOL (BOL+2)
+ == #define NOTHING (BOL+3)
+ == #define BOW (BOL+4)
+ == #define EOW (BOL+5)
+ == #define CODEMAX (BOL+5) // highest code used
+ == #define NONCHAR(c) ((c) > CHAR_MAX)
+ == #define NNONCHAR (CODEMAX-CHAR_MAX)
+ */
+static states
+step(struct re_guts *g,
+ sopno start, /* start state within strip */
+ sopno stop, /* state after stop state within strip */
+ states bef, /* states reachable before */
+ int ch, /* character or NONCHAR code */
+ states aft) /* states already known reachable after */
+{
+ register cset *cs;
+ register sop s;
+ register sopno pc;
+ register onestate here; /* note, macros know this name */
+ register sopno look;
+ register int i;
+
+ for (pc = start, INIT(here, pc); pc != stop; pc++, INC(here)) {
+ s = g->strip[pc];
+ switch (OP(s)) {
+ case OEND:
+ assert(pc == stop-1);
+ break;
+ case OCHAR:
+ /* only characters can match */
+ assert(!NONCHAR(ch) || ch != (char)OPND(s));
+ if (ch == (char)OPND(s))
+ FWD(aft, bef, 1);
+ break;
+ case OBOL:
+ if (ch == BOL || ch == BOLEOL)
+ FWD(aft, bef, 1);
+ break;
+ case OEOL:
+ if (ch == EOL || ch == BOLEOL)
+ FWD(aft, bef, 1);
+ break;
+ case OBOW:
+ if (ch == BOW)
+ FWD(aft, bef, 1);
+ break;
+ case OEOW:
+ if (ch == EOW)
+ FWD(aft, bef, 1);
+ break;
+ case OANY:
+ if (!NONCHAR(ch))
+ FWD(aft, bef, 1);
+ break;
+ case OANYOF:
+ cs = &g->sets[OPND(s)];
+ if (!NONCHAR(ch) && CHIN(cs, ch))
+ FWD(aft, bef, 1);
+ break;
+ case OBACK_: /* ignored here */
+ case O_BACK:
+ FWD(aft, aft, 1);
+ break;
+ case OPLUS_: /* forward, this is just an empty */
+ FWD(aft, aft, 1);
+ break;
+ case O_PLUS: /* both forward and back */
+ FWD(aft, aft, 1);
+ i = ISSETBACK(aft, OPND(s));
+ BACK(aft, aft, OPND(s));
+ if (!i && ISSETBACK(aft, OPND(s))) {
+ /* oho, must reconsider loop body */
+ pc -= OPND(s) + 1;
+ INIT(here, pc);
+ }
+ break;
+ case OQUEST_: /* two branches, both forward */
+ FWD(aft, aft, 1);
+ FWD(aft, aft, OPND(s));
+ break;
+ case O_QUEST: /* just an empty */
+ FWD(aft, aft, 1);
+ break;
+ case OLPAREN: /* not significant here */
+ case ORPAREN:
+ FWD(aft, aft, 1);
+ break;
+ case OCH_: /* mark the first two branches */
+ FWD(aft, aft, 1);
+ assert(OP(g->strip[pc+OPND(s)]) == OOR2);
+ FWD(aft, aft, OPND(s));
+ break;
+ case OOR1: /* done a branch, find the O_CH */
+ if (ISSTATEIN(aft, here)) {
+ for (look = 1;
+ OP(s = g->strip[pc+look]) != O_CH;
+ look += OPND(s))
+ assert(OP(s) == OOR2);
+ FWD(aft, aft, look);
+ }
+ break;
+ case OOR2: /* propagate OCH_'s marking */
+ FWD(aft, aft, 1);
+ if (OP(g->strip[pc+OPND(s)]) != O_CH) {
+ assert(OP(g->strip[pc+OPND(s)]) == OOR2);
+ FWD(aft, aft, OPND(s));
+ }
+ break;
+ case O_CH: /* just empty */
+ FWD(aft, aft, 1);
+ break;
+ default: /* ooooops... */
+ assert(nope);
+ break;
+ }
+ }
+
+ return(aft);
+}
+
+#ifdef REDEBUG
+/*
+ - print - print a set of states
+ == #ifdef REDEBUG
+ == static void print(struct match *m, char *caption, states st, \
+ == int ch, FILE *d);
+ == #endif
+ */
+static void
+print(struct match *m, char *caption, states st, int ch, FILE *d)
+{
+ register struct re_guts *g = m->g;
+ register int i;
+ register int first = 1;
+
+ if (!(m->eflags&REG_TRACE))
+ return;
+
+ fprintf(d, "%s", caption);
+ if (ch != '\0')
+ fprintf(d, " %s", pchar(ch));
+ for (i = 0; i < g->nstates; i++)
+ if (ISSET(st, i)) {
+ fprintf(d, "%s%d", (first) ? "\t" : ", ", i);
+ first = 0;
+ }
+ fprintf(d, "\n");
+}
+
+/*
+ - at - print current situation
+ == #ifdef REDEBUG
+ == static void at(struct match *m, char *title, char *start, char *stop, \
+ == sopno startst, sopno stopst);
+ == #endif
+ */
+static void
+at(struct match *m, char *title, char *start, char *stop, sopno startst, sopno stopst)
+{
+ if (!(m->eflags&REG_TRACE))
+ return;
+
+ printf("%s %s-", title, pchar(*start));
+ printf("%s ", pchar(*stop));
+ printf("%ld-%ld\n", (long)startst, (long)stopst);
+}
+
+#ifndef PCHARDONE
+#define PCHARDONE /* never again */
+/*
+ - pchar - make a character printable
+ == #ifdef REDEBUG
+ == static char *pchar(int ch);
+ == #endif
+ *
+ * Is this identical to regchar() over in debug.c? Well, yes. But a
+ * duplicate here avoids having a debugging-capable regexec.o tied to
+ * a matching debug.o, and this is convenient. It all disappears in
+ * the non-debug compilation anyway, so it doesn't matter much.
+ */
+static char * /* -> representation */
+pchar(int ch)
+{
+ static char pbuf[10];
+
+ if (isprint(ch) || ch == ' ')
+ sprintf(pbuf, "%c", ch);
+ else
+ sprintf(pbuf, "\\%o", ch);
+ return(pbuf);
+}
+#endif
+#endif
+
+#undef matcher
+#undef fast
+#undef slow
+#undef dissect
+#undef backref
+#undef step
+#undef print
+#undef at
+#undef match
diff --git a/regex/makefile.wnt b/regex/makefile.wnt
new file mode 100644
index 00000000..3d08888c
--- /dev/null
+++ b/regex/makefile.wnt
@@ -0,0 +1,58 @@
+# $Id: makefile.wnt 14098 2005-10-03 18:54:13Z jpf@u.washington.edu $
+#
+# ========================================================================
+# Copyright 2006-2007 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+#
+#
+# Makefile for WIN NT version of libpithrgx.lib
+#
+#
+CC=cl
+RM=del
+CP=copy
+RC=rc
+
+#includes symbol info for debugging
+CDEBUG= #-Zi -Od
+LDEBUG= /DEBUG /DEBUGTYPE:CV
+
+STDCFLAGS= -I..\include -nologo -MT -DWIN32 -DDOS -D_WINDOWS -DJOB_CONTROL -DMSC_MALLOC
+
+CFLAGS= $(CDEBUG) $(STDCFLAGS) $(NET) $(EXTRACFLAGS)
+
+LFLAGS= $(LDEBUG) $(EXTRALDFLAGS)
+
+RCFLAGS =
+
+# switches for library building
+LIBER=lib
+LIBARGS=/nologo /verbose
+
+HFILES= ../include/system.h ../include/general.h \
+ cclass.h cname.h regex2.h regex.h utils.h
+
+OFILES= regcomp.obj regerror.obj regexec.obj regfree.obj
+
+all: libregex.lib
+
+.c.obj:
+ $(CC) -c $(CFLAGS) "$(MAKEDIR)"\$*.c
+
+$(OFILES): $(HFILES)
+
+libregex.lib: $(OFILES)
+ $(RM) libregex.lib || rem
+ $(LIBER) /out:libregex.lib $(OFILES)
+
+clean:
+ $(RM) *.lib
+ $(RM) *.obj
diff --git a/regex/re_format.doc b/regex/re_format.doc
new file mode 100644
index 00000000..7a8acd7d
--- /dev/null
+++ b/regex/re_format.doc
@@ -0,0 +1,264 @@
+
+
+
+RE_FORMAT(7) Device and Network Interfaces RE_FORMAT(7)
+
+
+
+NAME
+ re_format - POSIX 1003.2 regular expressions
+
+DESCRIPTION
+ Regular expressions (``RE''s), as defined in POSIX 1003.2,
+ come in two forms: modern REs (roughly those of egrep;
+ 1003.2 calls these ``extended'' REs) and obsolete REs
+ (roughly those of ed; 1003.2 ``basic'' REs). Obsolete REs
+ mostly exist for backward compatibility in some old pro-
+ grams; they will be discussed at the end. 1003.2 leaves
+ some aspects of RE syntax and semantics open; ` - ' marks
+ decisions on these aspects that may not be fully portable to
+ other 1003.2 implementations.
+
+ A (modern) RE is one- or more non-empty- branches, separated
+ by `|'. It matches anything that matches one of the
+ branches.
+
+ A branch is one- or more pieces, concatenated. It matches a
+ match for the first, followed by a match for the second,
+ etc.
+
+ A piece is an atom possibly followed by a single- `*', `+',
+ `?', or bound. An atom followed by `*' matches a sequence
+ of 0 or more matches of the atom. An atom followed by `+'
+ matches a sequence of 1 or more matches of the atom. An
+ atom followed by `?' matches a sequence of 0 or 1 matches of
+ the atom.
+
+ A bound is `{' followed by an unsigned decimal integer, pos-
+ sibly followed by `,' possibly followed by another unsigned
+ decimal integer, always followed by `}'. The integers must
+ lie between 0 and RE_DUP_MAX (255-) inclusive, and if there
+ are two of them, the first may not exceed the second. An
+ atom followed by a bound containing one integer i and no
+ comma matches a sequence of exactly i matches of the atom.
+ An atom followed by a bound containing one integer i and a
+ comma matches a sequence of i or more matches of the atom.
+ An atom followed by a bound containing two integers i and j
+ matches a sequence of i through j (inclusive) matches of the
+ atom.
+
+ An atom is a regular expression enclosed in `()' (matching a
+ match for the regular expression), an empty set of `()'
+ (matching the null string) - , a bracket expression (see
+ below), `.' (matching any single character), `^' (matching
+ the null string at the beginning of a line), `$' (matching
+ the null string at the end of a line), a `\' followed by one
+ of the characters `^.[$()|*+?{\' (matching that character
+ taken as an ordinary character), a `\' followed by any other
+ character- (matching that character taken as an ordinary
+ character, as if the `\' had not been present-), or a single
+
+
+
+SunOS 5.5 Last change: March 20, 1994 1
+
+
+
+
+
+
+RE_FORMAT(7) Device and Network Interfaces RE_FORMAT(7)
+
+
+
+ character with no other significance (matching that charac-
+ ter). A `{' followed by a character other than a digit is
+ an ordinary character, not the beginning of a bound-. It is
+ illegal to end an RE with `\'.
+
+ A bracket expression is a list of characters enclosed in
+ `[]'. It normally matches any single character from the
+ list (but see below). If the list begins with `^', it
+ matches any single character (but see below) not from the
+ rest of the list. If two characters in the list are
+ separated by ` -', this is shorthand for the full range of
+ characters between those two (inclusive) in the collating
+ sequence, e.g. `[0-9]' in ASCII matches any decimal digit.
+ It is illegal- for two ranges to share an endpoint, e.g.
+ `a-c-e'. Ranges are very collating-sequence-dependent, and
+ portable programs should avoid relying on them.
+
+ To include a literal `]' in the list, make it the first
+ character (following a possible `^'). To include a literal
+ `-', make it the first or last character, or the second end-
+ point of a range. To use a literal `-' as the first end-
+ point of a range, enclose it in `[.' and `.]' to make it a
+ collating element (see below). With the exception of these
+ and some combinations using `[' (see next paragraphs), all
+ other special characters, including `\', lose their special
+ significance within a bracket expression.
+
+ Within a bracket expression, a collating element (a charac-
+ ter, a multi-character sequence that collates as if it were
+ a single character, or a collating-sequence name for either)
+ enclosed in `[.' and `.]' stands for the sequence of charac-
+ ters of that collating element. The sequence is a single
+ element of the bracket expression's list. A bracket expres-
+ sion containing a multi-character collating element can thus
+ match more than one character, e.g. if the collating
+ sequence includes a `ch' collating element, then the RE
+ `[[.ch.]]*c' matches the first five characters of `chchcc'.
+
+ Within a bracket expression, a collating element enclosed in
+ `[=' and `=]' is an equivalence class, standing for the
+ sequences of characters of all collating elements equivalent
+ to that one, including itself. (If there are no other
+ equivalent collating elements, the treatment is as if the
+ enclosing delimiters were `[.' and `.]'.) For example, if o
+ and ^ are the members of an equivalence class, then
+ `[[=o=]]', `[[=^=]]', and `[o^]' are all synonymous. An
+ equivalence class may not- be an endpoint of a range.
+
+ Within a bracket expression, the name of a character class
+ enclosed in `[:' and `:]' stands for the list of all charac-
+ ters belonging to that class. Standard character class
+ names are:
+
+
+
+SunOS 5.5 Last change: March 20, 1994 2
+
+
+
+
+
+
+RE_FORMAT(7) Device and Network Interfaces RE_FORMAT(7)
+
+
+
+ alnum digit punct
+ alpha graph space
+ blank lower upper
+ cntrl print xdigit
+
+ These stand for the character classes defined in ctype(3).
+ A locale may provide others. A character class may not be
+ used as an endpoint of a range.
+
+ There are two special cases- of bracket expressions: the
+ bracket expressions `[[:<:]]' and `[[:>:]]' match the null
+ string at the beginning and end of a word respectively. A
+ word is defined as a sequence of word characters which is
+ neither preceded nor followed by word characters. A word
+ character is an alnum character (as defined by ctype(3)) or
+ an underscore. This is an extension, compatible with but
+ not specified by POSIX 1003.2, and should be used with cau-
+ tion in software intended to be portable to other systems.
+
+ In the event that an RE could match more than one substring
+ of a given string, the RE matches the one starting earliest
+ in the string. If the RE could match more than one sub-
+ string starting at that point, it matches the longest.
+ Subexpressions also match the longest possible substrings,
+ subject to the constraint that the whole match be as long as
+ possible, with subexpressions starting earlier in the RE
+ taking priority over ones starting later. Note that
+ higher-level subexpressions thus take priority over their
+ lower-level component subexpressions.
+
+ Match lengths are measured in characters, not collating ele-
+ ments. A null string is considered longer than no match at
+ all. For example, `bb*' matches the three middle characters
+ of `abbbc', `(wee|week)(knights|nights)' matches all ten
+ characters of `weeknights', when `(.*).*' is matched against
+ `abc' the parenthesized subexpression matches all three
+ characters, and when `(a*)*' is matched against `bc' both
+ the whole RE and the parenthesized subexpression match the
+ null string.
+
+ If case-independent matching is specified, the effect is
+ much as if all case distinctions had vanished from the
+ alphabet. When an alphabetic that exists in multiple cases
+ appears as an ordinary character outside a bracket expres-
+ sion, it is effectively transformed into a bracket expres-
+ sion containing both cases, e.g. `x' becomes `[xX]'. When
+ it appears inside a bracket expression, all case counter-
+ parts of it are added to the bracket expression, so that
+ (e.g.) `[x]' becomes `[xX]' and `[^x]' becomes `[^xX]'.
+
+ No particular limit is imposed on the length of REs-. Pro-
+ grams intended to be portable should not employ REs longer
+
+
+
+SunOS 5.5 Last change: March 20, 1994 3
+
+
+
+
+
+
+RE_FORMAT(7) Device and Network Interfaces RE_FORMAT(7)
+
+
+
+ than 256 bytes, as an implementation can refuse to accept
+ such REs and remain POSIX-compliant.
+
+ Obsolete (``basic'') regular expressions differ in several
+ respects. `|', `+', and `?' are ordinary characters and
+ there is no equivalent for their functionality. The delim-
+ iters for bounds are `\{' and `\}', with `{' and `}' by
+ themselves ordinary characters. The parentheses for nested
+ subexpressions are `\(' and `\)', with `(' and `)' by them-
+ selves ordinary characters. `^' is an ordinary character
+ except at the beginning of the RE or- the beginning of a
+ parenthesized subexpression, `$' is an ordinary character
+ except at the end of the RE or- the end of a parenthesized
+ subexpression, and `*' is an ordinary character if it
+ appears at the beginning of the RE or the beginning of a
+ parenthesized subexpression (after a possible leading `^').
+ Finally, there is one new type of atom, a back reference:
+ `\' followed by a non-zero decimal digit d matches the same
+ sequence of characters matched by the dth parenthesized
+ subexpression (numbering subexpressions by the positions of
+ their opening parentheses, left to right), so that (e.g.)
+ `\([bc]\)\1' matches `bb' or `cc' but not `bc'.
+
+SEE ALSO
+ regex(3)
+
+ POSIX 1003.2, section 2.8 (Regular Expression Notation).
+
+BUGS
+ Having two kinds of REs is a botch.
+
+ The current 1003.2 spec says that `)' is an ordinary charac-
+ ter in the absence of an unmatched `('; this was an uninten-
+ tional result of a wording error, and change is likely.
+ Avoid relying on it.
+
+ Back references are a dreadful botch, posing major problems
+ for efficient implementations. They are also somewhat
+ vaguely defined (does `a\(\(b\)*\2\)*d' match `abbbd'?).
+ Avoid using them.
+
+ 1003.2's specification of case-independent matching is
+ vague. The ``one case implies all cases'' definition given
+ above is current consensus among implementors as to the
+ right interpretation.
+
+ The syntax for word boundaries is incredibly ugly.
+
+
+
+
+
+
+
+
+SunOS 5.5 Last change: March 20, 1994 4
+
+
+
diff --git a/regex/regcomp.c b/regex/regcomp.c
new file mode 100644
index 00000000..0747190a
--- /dev/null
+++ b/regex/regcomp.c
@@ -0,0 +1,1621 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994 Henry Spencer.
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Henry Spencer.
+ *
+ * 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.
+ *
+ * @(#)regcomp.c 8.5 (Berkeley) 3/20/94
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)regcomp.c 8.5 (Berkeley) 3/20/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdlib.h>
+#include "regex.h"
+
+#include "utils.h"
+#include "regex2.h"
+
+#include "cclass.h"
+#include "cname.h"
+
+/*
+ * parse structure, passed up and down to avoid global variables and
+ * other clumsinesses
+ */
+struct parse {
+ char *next; /* next character in RE */
+ char *end; /* end of string (-> NUL normally) */
+ int error; /* has an error been seen? */
+ sop *strip; /* malloced strip */
+ sopno ssize; /* malloced strip size (allocated) */
+ sopno slen; /* malloced strip length (used) */
+ int ncsalloc; /* number of csets allocated */
+ struct re_guts *g;
+# define NPAREN 10 /* we need to remember () 1-9 for back refs */
+ sopno pbegin[NPAREN]; /* -> ( ([0] unused) */
+ sopno pend[NPAREN]; /* -> ) ([0] unused) */
+};
+
+/* ========= begin header generated by ./mkh ========= */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* === regcomp.c === */
+static void p_ere __P((struct parse *p, int stop));
+static void p_ere_exp __P((struct parse *p));
+static void p_str __P((struct parse *p));
+static void p_bre __P((struct parse *p, int end1, int end2));
+static int p_simp_re __P((struct parse *p, int starordinary));
+static int p_count __P((struct parse *p));
+static void p_bracket __P((struct parse *p));
+static void p_b_term __P((struct parse *p, cset *cs));
+static void p_b_cclass __P((struct parse *p, cset *cs));
+static void p_b_eclass __P((struct parse *p, cset *cs));
+static char p_b_symbol __P((struct parse *p));
+static char p_b_coll_elem __P((struct parse *p, int endc));
+static char othercase __P((int ch));
+static void bothcases __P((struct parse *p, int ch));
+static void ordinary __P((struct parse *p, int ch));
+static void nonnewline __P((struct parse *p));
+static void repeat __P((struct parse *p, sopno start, int from, int to));
+static int seterr __P((struct parse *p, int e));
+static cset *allocset __P((struct parse *p));
+static void freeset __P((struct parse *p, cset *cs));
+static int freezeset __P((struct parse *p, cset *cs));
+static int firstch __P((struct parse *p, cset *cs));
+static int nch __P((struct parse *p, cset *cs));
+static void mcadd __P((struct parse *p, cset *cs, char *cp));
+static void mcsub __P((cset *cs, char *cp));
+static int mcin __P((cset *cs, char *cp));
+static char *mcfind __P((cset *cs, char *cp));
+static void mcinvert __P((struct parse *p, cset *cs));
+static void mccase __P((struct parse *p, cset *cs));
+static int isinsets __P((struct re_guts *g, int c));
+static int samesets __P((struct re_guts *g, int c1, int c2));
+static void categorize __P((struct parse *p, struct re_guts *g));
+static sopno dupl __P((struct parse *p, sopno start, sopno finish));
+static void doemit __P((struct parse *p, sop op, size_t opnd));
+static void doinsert __P((struct parse *p, sop op, size_t opnd, sopno pos));
+static void dofwd __P((struct parse *p, sopno pos, sop value));
+static void enlarge __P((struct parse *p, sopno size));
+static void stripsnug __P((struct parse *p, struct re_guts *g));
+static void findmust __P((struct parse *p, struct re_guts *g));
+static sopno pluscount __P((struct parse *p, struct re_guts *g));
+
+#ifdef __cplusplus
+}
+#endif
+/* ========= end header generated by ./mkh ========= */
+
+static char nuls[10]; /* place to point scanner in event of error */
+
+/*
+ * macros for use with parse structure
+ * BEWARE: these know that the parse structure is named `p' !!!
+ */
+#define PEEK() (*p->next)
+#define PEEK2() (*(p->next+1))
+#define MORE() (p->next < p->end)
+#define MORE2() (p->next+1 < p->end)
+#define SEE(c) (MORE() && PEEK() == (c))
+#define SEETWO(a, b) (MORE() && MORE2() && PEEK() == (a) && PEEK2() == (b))
+#define EAT(c) ((SEE(c)) ? (NEXT(), 1) : 0)
+#define EATTWO(a, b) ((SEETWO(a, b)) ? (NEXT2(), 1) : 0)
+#define NEXT() (p->next++)
+#define NEXT2() (p->next += 2)
+#define NEXTn(n) (p->next += (n))
+#define GETNEXT() (*p->next++)
+#define SETERROR(e) seterr(p, (e))
+#define REQUIRE(co, e) ((void) ((co) || SETERROR(e)))
+#define MUSTSEE(c, e) (REQUIRE(MORE() && PEEK() == (c), e))
+#define MUSTEAT(c, e) (REQUIRE(MORE() && GETNEXT() == (c), e))
+#define MUSTNOTSEE(c, e) (REQUIRE(!MORE() || PEEK() != (c), e))
+#define EMIT(op, sopnd) doemit(p, (sop)(op), (size_t)(sopnd))
+#define INSERT(op, pos) doinsert(p, (sop)(op), HERE()-(pos)+1, pos)
+#define AHEAD(pos) dofwd(p, pos, HERE()-(pos))
+#define ASTERN(sop, pos) EMIT(sop, HERE()-pos)
+#define HERE() (p->slen)
+#define THERE() (p->slen - 1)
+#define THERETHERE() (p->slen - 2)
+#define DROP(n) (p->slen -= (n))
+
+#ifndef NDEBUG
+static int never = 0; /* for use in asserts; shuts lint up */
+#else
+#define never 0 /* some <assert.h>s have bugs too */
+#endif
+
+/*
+ - regcomp - interface for parser and compilation
+ = extern int regcomp(regex_t *, const char *, int);
+ = #define REG_BASIC 0000
+ = #define REG_EXTENDED 0001
+ = #define REG_ICASE 0002
+ = #define REG_NOSUB 0004
+ = #define REG_NEWLINE 0010
+ = #define REG_NOSPEC 0020
+ = #define REG_PEND 0040
+ = #define REG_DUMP 0200
+ */
+int __stdcall /* 0 success, otherwise REG_something */
+regcomp(regex_t *preg, const char *pattern, int cflags)
+{
+ struct parse pa;
+ register struct re_guts *g;
+ register struct parse *p = &pa;
+ register int i;
+ register size_t len;
+#ifdef REDEBUG
+# define GOODFLAGS(f) (f)
+#else
+# define GOODFLAGS(f) ((f)&~REG_DUMP)
+#endif
+
+ cflags = GOODFLAGS(cflags);
+ if ((cflags&REG_EXTENDED) && (cflags&REG_NOSPEC))
+ return(REG_INVARG);
+
+ if (cflags&REG_PEND) {
+ if (preg->re_endp < pattern)
+ return(REG_INVARG);
+ len = preg->re_endp - pattern;
+ } else
+ len = strlen((char *)pattern);
+
+ /* do the mallocs early so failure handling is easy */
+ g = (struct re_guts *)malloc(sizeof(struct re_guts) +
+ (NC-1)*sizeof(cat_t));
+ if (g == NULL)
+ return(REG_ESPACE);
+ p->ssize = len/(size_t)2*(size_t)3 + (size_t)1; /* ugh */
+ p->strip = (sop *)malloc(p->ssize * sizeof(sop));
+ p->slen = 0;
+ if (p->strip == NULL) {
+ free((char *)g);
+ return(REG_ESPACE);
+ }
+
+ /* set things up */
+ p->g = g;
+ p->next = (char *)pattern; /* convenience; we do not modify it */
+ p->end = p->next + len;
+ p->error = 0;
+ p->ncsalloc = 0;
+ for (i = 0; i < NPAREN; i++) {
+ p->pbegin[i] = 0;
+ p->pend[i] = 0;
+ }
+ g->csetsize = NC;
+ g->sets = NULL;
+ g->setbits = NULL;
+ g->ncsets = 0;
+ g->cflags = cflags;
+ g->iflags = 0;
+ g->nbol = 0;
+ g->neol = 0;
+ g->must = NULL;
+ g->mlen = 0;
+ g->nsub = 0;
+ g->ncategories = 1; /* category 0 is "everything else" */
+ g->categories = &g->catspace[-(CHAR_MIN)];
+ (void) memset((char *)g->catspace, 0, NC*sizeof(cat_t));
+ g->backrefs = 0;
+
+ /* do it */
+ EMIT(OEND, 0);
+ g->firststate = THERE();
+ if (cflags&REG_EXTENDED)
+ p_ere(p, OUT);
+ else if (cflags&REG_NOSPEC)
+ p_str(p);
+ else
+ p_bre(p, OUT, OUT);
+ EMIT(OEND, 0);
+ g->laststate = THERE();
+
+ /* tidy up loose ends and fill things in */
+ categorize(p, g);
+ stripsnug(p, g);
+ findmust(p, g);
+ g->nplus = pluscount(p, g);
+ g->magic = MAGIC2;
+ preg->re_nsub = g->nsub;
+ preg->re_g = g;
+ preg->re_magic = MAGIC1;
+#ifndef REDEBUG
+ /* not debugging, so can't rely on the assert() in regexec() */
+ if (g->iflags&BAD)
+ SETERROR(REG_ASSERT);
+#endif
+
+ /* win or lose, we're done */
+ if (p->error != 0) /* lose */
+ regfree(preg);
+ return(p->error);
+}
+
+/*
+ - p_ere - ERE parser top level, concatenation and alternation
+ == static void p_ere(register struct parse *p, int stop);
+ */
+static void
+p_ere(struct parse *p,
+ int stop) /* character this ERE should end at */
+{
+ register char c;
+ register sopno prevback;
+ register sopno prevfwd;
+ register sopno conc;
+ register int first = 1; /* is this the first alternative? */
+
+ for (;;) {
+ /* do a bunch of concatenated expressions */
+ conc = HERE();
+ while (MORE() && (c = PEEK()) != '|' && c != stop)
+ p_ere_exp(p);
+ REQUIRE(HERE() != conc, REG_EMPTY); /* require nonempty */
+
+ if (!EAT('|'))
+ break; /* NOTE BREAK OUT */
+
+ if (first) {
+ INSERT(OCH_, conc); /* offset is wrong */
+ prevfwd = conc;
+ prevback = conc;
+ first = 0;
+ }
+ ASTERN(OOR1, prevback);
+ prevback = THERE();
+ AHEAD(prevfwd); /* fix previous offset */
+ prevfwd = HERE();
+ EMIT(OOR2, 0); /* offset is very wrong */
+ }
+
+ if (!first) { /* tail-end fixups */
+ AHEAD(prevfwd);
+ ASTERN(O_CH, prevback);
+ }
+
+ assert(!MORE() || SEE(stop));
+}
+
+/*
+ - p_ere_exp - parse one subERE, an atom possibly followed by a repetition op
+ == static void p_ere_exp(register struct parse *p);
+ */
+static void
+p_ere_exp(struct parse *p)
+{
+ register char c;
+ register sopno pos;
+ register int count;
+ register int count2;
+ register sopno subno;
+ int wascaret = 0;
+
+ assert(MORE()); /* caller should have ensured this */
+ c = GETNEXT();
+
+ pos = HERE();
+ switch (c) {
+ case '(':
+ REQUIRE(MORE(), REG_EPAREN);
+ p->g->nsub++;
+ subno = p->g->nsub;
+ if (subno < NPAREN)
+ p->pbegin[subno] = HERE();
+ EMIT(OLPAREN, subno);
+ if (!SEE(')'))
+ p_ere(p, ')');
+ if (subno < NPAREN) {
+ p->pend[subno] = HERE();
+ assert(p->pend[subno] != 0);
+ }
+ EMIT(ORPAREN, subno);
+ MUSTEAT(')', REG_EPAREN);
+ break;
+#ifndef POSIX_MISTAKE
+ case ')': /* happens only if no current unmatched ( */
+ /*
+ * You may ask, why the ifndef? Because I didn't notice
+ * this until slightly too late for 1003.2, and none of the
+ * other 1003.2 regular-expression reviewers noticed it at
+ * all. So an unmatched ) is legal POSIX, at least until
+ * we can get it fixed.
+ */
+ SETERROR(REG_EPAREN);
+ break;
+#endif
+ case '^':
+ EMIT(OBOL, 0);
+ p->g->iflags |= USEBOL;
+ p->g->nbol++;
+ wascaret = 1;
+ break;
+ case '$':
+ EMIT(OEOL, 0);
+ p->g->iflags |= USEEOL;
+ p->g->neol++;
+ break;
+ case '|':
+ SETERROR(REG_EMPTY);
+ break;
+ case '*':
+ case '+':
+ case '?':
+ SETERROR(REG_BADRPT);
+ break;
+ case '.':
+ if (p->g->cflags&REG_NEWLINE)
+ nonnewline(p);
+ else
+ EMIT(OANY, 0);
+ break;
+ case '[':
+ p_bracket(p);
+ break;
+ case '\\':
+ REQUIRE(MORE(), REG_EESCAPE);
+ c = GETNEXT();
+ ordinary(p, c);
+ break;
+ case '{': /* okay as ordinary except if digit follows */
+ REQUIRE(!MORE() || !isdigit(PEEK()), REG_BADRPT);
+ /* FALLTHROUGH */
+ default:
+ ordinary(p, c);
+ break;
+ }
+
+ if (!MORE())
+ return;
+ c = PEEK();
+ /* we call { a repetition if followed by a digit */
+ if (!( c == '*' || c == '+' || c == '?' ||
+ (c == '{' && MORE2() && isdigit(PEEK2())) ))
+ return; /* no repetition, we're done */
+ NEXT();
+
+ REQUIRE(!wascaret, REG_BADRPT);
+ switch (c) {
+ case '*': /* implemented as +? */
+ /* this case does not require the (y|) trick, noKLUDGE */
+ INSERT(OPLUS_, pos);
+ ASTERN(O_PLUS, pos);
+ INSERT(OQUEST_, pos);
+ ASTERN(O_QUEST, pos);
+ break;
+ case '+':
+ INSERT(OPLUS_, pos);
+ ASTERN(O_PLUS, pos);
+ break;
+ case '?':
+ /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */
+ INSERT(OCH_, pos); /* offset slightly wrong */
+ ASTERN(OOR1, pos); /* this one's right */
+ AHEAD(pos); /* fix the OCH_ */
+ EMIT(OOR2, 0); /* offset very wrong... */
+ AHEAD(THERE()); /* ...so fix it */
+ ASTERN(O_CH, THERETHERE());
+ break;
+ case '{':
+ count = p_count(p);
+ if (EAT(',')) {
+ if (isdigit(PEEK())) {
+ count2 = p_count(p);
+ REQUIRE(count <= count2, REG_BADBR);
+ } else /* single number with comma */
+ count2 = INFINITY;
+ } else /* just a single number */
+ count2 = count;
+ repeat(p, pos, count, count2);
+ if (!EAT('}')) { /* error heuristics */
+ while (MORE() && PEEK() != '}')
+ NEXT();
+ REQUIRE(MORE(), REG_EBRACE);
+ SETERROR(REG_BADBR);
+ }
+ break;
+ }
+
+ if (!MORE())
+ return;
+ c = PEEK();
+ if (!( c == '*' || c == '+' || c == '?' ||
+ (c == '{' && MORE2() && isdigit(PEEK2())) ) )
+ return;
+ SETERROR(REG_BADRPT);
+}
+
+/*
+ - p_str - string (no metacharacters) "parser"
+ == static void p_str(register struct parse *p);
+ */
+static void
+p_str(struct parse *p)
+{
+ REQUIRE(MORE(), REG_EMPTY);
+ while (MORE())
+ ordinary(p, GETNEXT());
+}
+
+/*
+ - p_bre - BRE parser top level, anchoring and concatenation
+ == static void p_bre(register struct parse *p, register int end1, \
+ == register int end2);
+ * Giving end1 as OUT essentially eliminates the end1/end2 check.
+ *
+ * This implementation is a bit of a kludge, in that a trailing $ is first
+ * taken as an ordinary character and then revised to be an anchor. The
+ * only undesirable side effect is that '$' gets included as a character
+ * category in such cases. This is fairly harmless; not worth fixing.
+ * The amount of lookahead needed to avoid this kludge is excessive.
+ */
+static void
+p_bre(struct parse *p,
+ int end1, /* first terminating character */
+ int end2) /* second terminating character */
+{
+ register sopno start = HERE();
+ register int first = 1; /* first subexpression? */
+ register int wasdollar = 0;
+
+ if (EAT('^')) {
+ EMIT(OBOL, 0);
+ p->g->iflags |= USEBOL;
+ p->g->nbol++;
+ }
+ while (MORE() && !SEETWO(end1, end2)) {
+ wasdollar = p_simp_re(p, first);
+ first = 0;
+ }
+ if (wasdollar) { /* oops, that was a trailing anchor */
+ DROP(1);
+ EMIT(OEOL, 0);
+ p->g->iflags |= USEEOL;
+ p->g->neol++;
+ }
+
+ REQUIRE(HERE() != start, REG_EMPTY); /* require nonempty */
+}
+
+/*
+ - p_simp_re - parse a simple RE, an atom possibly followed by a repetition
+ == static int p_simp_re(register struct parse *p, int starordinary);
+ */
+static int /* was the simple RE an unbackslashed $? */
+p_simp_re(struct parse *p,
+ int starordinary) /* is a leading * an ordinary character? */
+{
+ register int c;
+ register int count;
+ register int count2;
+ register sopno pos;
+ register int i;
+ register sopno subno;
+# define BACKSL (1<<CHAR_BIT)
+
+ pos = HERE(); /* repetion op, if any, covers from here */
+
+ assert(MORE()); /* caller should have ensured this */
+ c = GETNEXT();
+ if (c == '\\') {
+ REQUIRE(MORE(), REG_EESCAPE);
+ c = BACKSL | (unsigned char)GETNEXT();
+ }
+ switch (c) {
+ case '.':
+ if (p->g->cflags&REG_NEWLINE)
+ nonnewline(p);
+ else
+ EMIT(OANY, 0);
+ break;
+ case '[':
+ p_bracket(p);
+ break;
+ case BACKSL|'{':
+ SETERROR(REG_BADRPT);
+ break;
+ case BACKSL|'(':
+ p->g->nsub++;
+ subno = p->g->nsub;
+ if (subno < NPAREN)
+ p->pbegin[subno] = HERE();
+ EMIT(OLPAREN, subno);
+ /* the MORE here is an error heuristic */
+ if (MORE() && !SEETWO('\\', ')'))
+ p_bre(p, '\\', ')');
+ if (subno < NPAREN) {
+ p->pend[subno] = HERE();
+ assert(p->pend[subno] != 0);
+ }
+ EMIT(ORPAREN, subno);
+ REQUIRE(EATTWO('\\', ')'), REG_EPAREN);
+ break;
+ case BACKSL|')': /* should not get here -- must be user */
+ case BACKSL|'}':
+ SETERROR(REG_EPAREN);
+ break;
+ case BACKSL|'1':
+ case BACKSL|'2':
+ case BACKSL|'3':
+ case BACKSL|'4':
+ case BACKSL|'5':
+ case BACKSL|'6':
+ case BACKSL|'7':
+ case BACKSL|'8':
+ case BACKSL|'9':
+ i = (c&~BACKSL) - '0';
+ assert(i < NPAREN);
+ if (p->pend[i] != 0) {
+ assert(i <= p->g->nsub);
+ EMIT(OBACK_, i);
+ assert(p->pbegin[i] != 0);
+ assert(OP(p->strip[p->pbegin[i]]) == OLPAREN);
+ assert(OP(p->strip[p->pend[i]]) == ORPAREN);
+ (void) dupl(p, p->pbegin[i]+1, p->pend[i]);
+ EMIT(O_BACK, i);
+ } else
+ SETERROR(REG_ESUBREG);
+ p->g->backrefs = 1;
+ break;
+ case '*':
+ REQUIRE(starordinary, REG_BADRPT);
+ /* FALLTHROUGH */
+ default:
+ ordinary(p, c &~ BACKSL);
+ break;
+ }
+
+ if (EAT('*')) { /* implemented as +? */
+ /* this case does not require the (y|) trick, noKLUDGE */
+ INSERT(OPLUS_, pos);
+ ASTERN(O_PLUS, pos);
+ INSERT(OQUEST_, pos);
+ ASTERN(O_QUEST, pos);
+ } else if (EATTWO('\\', '{')) {
+ count = p_count(p);
+ if (EAT(',')) {
+ if (MORE() && isdigit(PEEK())) {
+ count2 = p_count(p);
+ REQUIRE(count <= count2, REG_BADBR);
+ } else /* single number with comma */
+ count2 = INFINITY;
+ } else /* just a single number */
+ count2 = count;
+ repeat(p, pos, count, count2);
+ if (!EATTWO('\\', '}')) { /* error heuristics */
+ while (MORE() && !SEETWO('\\', '}'))
+ NEXT();
+ REQUIRE(MORE(), REG_EBRACE);
+ SETERROR(REG_BADBR);
+ }
+ } else if (c == (unsigned char)'$') /* $ (but not \$) ends it */
+ return(1);
+
+ return(0);
+}
+
+/*
+ - p_count - parse a repetition count
+ == static int p_count(register struct parse *p);
+ */
+static int /* the value */
+p_count(struct parse *p)
+{
+ register int count = 0;
+ register int ndigits = 0;
+
+ while (MORE() && isdigit(PEEK()) && count <= DUPMAX) {
+ count = count*10 + (GETNEXT() - '0');
+ ndigits++;
+ }
+
+ REQUIRE(ndigits > 0 && count <= DUPMAX, REG_BADBR);
+ return(count);
+}
+
+/*
+ - p_bracket - parse a bracketed character list
+ == static void p_bracket(register struct parse *p);
+ *
+ * Note a significant property of this code: if the allocset() did SETERROR,
+ * no set operations are done.
+ */
+static void
+p_bracket(struct parse *p)
+{
+ register cset *cs = allocset(p);
+ register int invert = 0;
+
+ /* Dept of Truly Sickening Special-Case Kludges */
+ if (p->next + 5 < p->end && strncmp(p->next, "[:<:]]", 6) == 0) {
+ EMIT(OBOW, 0);
+ NEXTn(6);
+ return;
+ }
+ if (p->next + 5 < p->end && strncmp(p->next, "[:>:]]", 6) == 0) {
+ EMIT(OEOW, 0);
+ NEXTn(6);
+ return;
+ }
+
+ if (EAT('^'))
+ invert++; /* make note to invert set at end */
+ if (EAT(']'))
+ CHadd(cs, ']');
+ else if (EAT('-'))
+ CHadd(cs, '-');
+ while (MORE() && PEEK() != ']' && !SEETWO('-', ']'))
+ p_b_term(p, cs);
+ if (EAT('-'))
+ CHadd(cs, '-');
+ MUSTEAT(']', REG_EBRACK);
+
+ if (p->error != 0) /* don't mess things up further */
+ return;
+
+ if (p->g->cflags&REG_ICASE) {
+ register int i;
+ register int ci;
+
+ for (i = p->g->csetsize - 1; i >= 0; i--)
+ if (CHIN(cs, i) && isalpha(i)) {
+ ci = othercase(i);
+ if (ci != i)
+ CHadd(cs, ci);
+ }
+ if (cs->multis != NULL)
+ mccase(p, cs);
+ }
+ if (invert) {
+ register int i;
+
+ for (i = p->g->csetsize - 1; i >= 0; i--)
+ if (CHIN(cs, i))
+ CHsub(cs, i);
+ else
+ CHadd(cs, i);
+ if (p->g->cflags&REG_NEWLINE)
+ CHsub(cs, '\n');
+ if (cs->multis != NULL)
+ mcinvert(p, cs);
+ }
+
+ assert(cs->multis == NULL); /* xxx */
+
+ if (nch(p, cs) == 1) { /* optimize singleton sets */
+ ordinary(p, firstch(p, cs));
+ freeset(p, cs);
+ } else
+ EMIT(OANYOF, freezeset(p, cs));
+}
+
+/*
+ - p_b_term - parse one term of a bracketed character list
+ == static void p_b_term(register struct parse *p, register cset *cs);
+ */
+static void
+p_b_term(struct parse *p, cset *cs)
+{
+ register char c;
+ register char start, finish;
+ register int i;
+
+ /* classify what we've got */
+ switch ((MORE()) ? PEEK() : '\0') {
+ case '[':
+ c = (MORE2()) ? PEEK2() : '\0';
+ break;
+ case '-':
+ SETERROR(REG_ERANGE);
+ return; /* NOTE RETURN */
+ break;
+ default:
+ c = '\0';
+ break;
+ }
+
+ switch (c) {
+ case ':': /* character class */
+ NEXT2();
+ REQUIRE(MORE(), REG_EBRACK);
+ c = PEEK();
+ REQUIRE(c != '-' && c != ']', REG_ECTYPE);
+ p_b_cclass(p, cs);
+ REQUIRE(MORE(), REG_EBRACK);
+ REQUIRE(EATTWO(':', ']'), REG_ECTYPE);
+ break;
+ case '=': /* equivalence class */
+ NEXT2();
+ REQUIRE(MORE(), REG_EBRACK);
+ c = PEEK();
+ REQUIRE(c != '-' && c != ']', REG_ECOLLATE);
+ p_b_eclass(p, cs);
+ REQUIRE(MORE(), REG_EBRACK);
+ REQUIRE(EATTWO('=', ']'), REG_ECOLLATE);
+ break;
+ default: /* symbol, ordinary character, or range */
+/* xxx revision needed for multichar stuff */
+ start = p_b_symbol(p);
+ if (SEE('-') && MORE2() && PEEK2() != ']') {
+ /* range */
+ NEXT();
+ if (EAT('-'))
+ finish = '-';
+ else
+ finish = p_b_symbol(p);
+ } else
+ finish = start;
+/* xxx what about signed chars here... */
+ REQUIRE(start <= finish, REG_ERANGE);
+ for (i = start; i <= finish; i++)
+ CHadd(cs, i);
+ break;
+ }
+}
+
+/*
+ - p_b_cclass - parse a character-class name and deal with it
+ == static void p_b_cclass(register struct parse *p, register cset *cs);
+ */
+static void
+p_b_cclass(struct parse *p, cset *cs)
+{
+ register char *sp = p->next;
+ register struct cclass *cp;
+ register size_t len;
+ register char *u;
+ register char c;
+
+ while (MORE() && isalpha(PEEK()))
+ NEXT();
+ len = p->next - sp;
+ for (cp = cclasses; cp->name != NULL; cp++)
+ if (strncmp(cp->name, sp, len) == 0 && cp->name[len] == '\0')
+ break;
+ if (cp->name == NULL) {
+ /* oops, didn't find it */
+ SETERROR(REG_ECTYPE);
+ return;
+ }
+
+ u = cp->chars;
+ while ((c = *u++) != '\0')
+ CHadd(cs, c);
+ for (u = cp->multis; *u != '\0'; u += strlen(u) + 1)
+ MCadd(p, cs, u);
+}
+
+/*
+ - p_b_eclass - parse an equivalence-class name and deal with it
+ == static void p_b_eclass(register struct parse *p, register cset *cs);
+ *
+ * This implementation is incomplete. xxx
+ */
+static void
+p_b_eclass(struct parse *p, cset *cs)
+{
+ register char c;
+
+ c = p_b_coll_elem(p, '=');
+ CHadd(cs, c);
+}
+
+/*
+ - p_b_symbol - parse a character or [..]ed multicharacter collating symbol
+ == static char p_b_symbol(register struct parse *p);
+ */
+static char /* value of symbol */
+p_b_symbol(struct parse *p)
+{
+ register char value;
+
+ REQUIRE(MORE(), REG_EBRACK);
+ if (!EATTWO('[', '.'))
+ return(GETNEXT());
+
+ /* collating symbol */
+ value = p_b_coll_elem(p, '.');
+ REQUIRE(EATTWO('.', ']'), REG_ECOLLATE);
+ return(value);
+}
+
+/*
+ - p_b_coll_elem - parse a collating-element name and look it up
+ == static char p_b_coll_elem(register struct parse *p, int endc);
+ */
+static char /* value of collating element */
+p_b_coll_elem(struct parse *p,
+ int endc) /* name ended by endc,']' */
+{
+ register char *sp = p->next;
+ register struct cname *cp;
+ register int len;
+
+ while (MORE() && !SEETWO(endc, ']'))
+ NEXT();
+ if (!MORE()) {
+ SETERROR(REG_EBRACK);
+ return(0);
+ }
+ len = p->next - sp;
+ for (cp = cnames; cp->name != NULL; cp++)
+ if (strncmp(cp->name, sp, len) == 0 && cp->name[len] == '\0')
+ return(cp->code); /* known name */
+ if (len == 1)
+ return(*sp); /* single character */
+ SETERROR(REG_ECOLLATE); /* neither */
+ return(0);
+}
+
+/*
+ - othercase - return the case counterpart of an alphabetic
+ == static char othercase(int ch);
+ */
+static char /* if no counterpart, return ch */
+othercase(int ch)
+{
+ assert(isalpha(ch));
+ if (isupper(ch))
+ return(tolower(ch));
+ else if (islower(ch))
+ return(toupper(ch));
+ else /* peculiar, but could happen */
+ return(ch);
+}
+
+/*
+ - bothcases - emit a dualcase version of a two-case character
+ == static void bothcases(register struct parse *p, int ch);
+ *
+ * Boy, is this implementation ever a kludge...
+ */
+static void
+bothcases(struct parse *p, int ch)
+{
+ register char *oldnext = p->next;
+ register char *oldend = p->end;
+ char bracket[3];
+
+ assert(othercase(ch) != ch); /* p_bracket() would recurse */
+ p->next = bracket;
+ p->end = bracket+2;
+ bracket[0] = ch;
+ bracket[1] = ']';
+ bracket[2] = '\0';
+ p_bracket(p);
+ assert(p->next == bracket+2);
+ p->next = oldnext;
+ p->end = oldend;
+}
+
+/*
+ - ordinary - emit an ordinary character
+ == static void ordinary(register struct parse *p, register int ch);
+ */
+static void
+ordinary(struct parse *p, int ch)
+{
+ register cat_t *cap = p->g->categories;
+
+ if ((p->g->cflags&REG_ICASE) && isalpha(ch) && othercase(ch) != ch)
+ bothcases(p, ch);
+ else {
+ EMIT(OCHAR, (unsigned char)ch);
+ if (cap[ch] == 0)
+ cap[ch] = p->g->ncategories++;
+ }
+}
+
+/*
+ - nonnewline - emit REG_NEWLINE version of OANY
+ == static void nonnewline(register struct parse *p);
+ *
+ * Boy, is this implementation ever a kludge...
+ */
+static void
+nonnewline(struct parse *p)
+{
+ register char *oldnext = p->next;
+ register char *oldend = p->end;
+ char bracket[4];
+
+ p->next = bracket;
+ p->end = bracket+3;
+ bracket[0] = '^';
+ bracket[1] = '\n';
+ bracket[2] = ']';
+ bracket[3] = '\0';
+ p_bracket(p);
+ assert(p->next == bracket+3);
+ p->next = oldnext;
+ p->end = oldend;
+}
+
+/*
+ - repeat - generate code for a bounded repetition, recursively if needed
+ == static void repeat(register struct parse *p, sopno start, int from, int to);
+ */
+static void
+repeat(struct parse *p,
+ sopno start, /* operand from here to end of strip */
+ int from, /* repeated from this number */
+ int to) /* to this number of times (maybe INFINITY) */
+{
+ register sopno finish = HERE();
+# define N 2
+# define INF 3
+# define REP(f, t) ((f)*8 + (t))
+# define MAP(n) (((n) <= 1) ? (n) : ((n) == INFINITY) ? INF : N)
+ register sopno copy;
+
+ if (p->error != 0) /* head off possible runaway recursion */
+ return;
+
+ assert(from <= to);
+
+ switch (REP(MAP(from), MAP(to))) {
+ case REP(0, 0): /* must be user doing this */
+ DROP(finish-start); /* drop the operand */
+ break;
+ case REP(0, 1): /* as x{1,1}? */
+ case REP(0, N): /* as x{1,n}? */
+ case REP(0, INF): /* as x{1,}? */
+ /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */
+ INSERT(OCH_, start); /* offset is wrong... */
+ repeat(p, start+1, 1, to);
+ ASTERN(OOR1, start);
+ AHEAD(start); /* ... fix it */
+ EMIT(OOR2, 0);
+ AHEAD(THERE());
+ ASTERN(O_CH, THERETHERE());
+ break;
+ case REP(1, 1): /* trivial case */
+ /* done */
+ break;
+ case REP(1, N): /* as x?x{1,n-1} */
+ /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */
+ INSERT(OCH_, start);
+ ASTERN(OOR1, start);
+ AHEAD(start);
+ EMIT(OOR2, 0); /* offset very wrong... */
+ AHEAD(THERE()); /* ...so fix it */
+ ASTERN(O_CH, THERETHERE());
+ copy = dupl(p, start+1, finish+1);
+ assert(copy == finish+4);
+ repeat(p, copy, 1, to-1);
+ break;
+ case REP(1, INF): /* as x+ */
+ INSERT(OPLUS_, start);
+ ASTERN(O_PLUS, start);
+ break;
+ case REP(N, N): /* as xx{m-1,n-1} */
+ copy = dupl(p, start, finish);
+ repeat(p, copy, from-1, to-1);
+ break;
+ case REP(N, INF): /* as xx{n-1,INF} */
+ copy = dupl(p, start, finish);
+ repeat(p, copy, from-1, to);
+ break;
+ default: /* "can't happen" */
+ SETERROR(REG_ASSERT); /* just in case */
+ break;
+ }
+}
+
+/*
+ - seterr - set an error condition
+ == static int seterr(register struct parse *p, int e);
+ */
+static int /* useless but makes type checking happy */
+seterr(struct parse *p, int e)
+{
+ if (p->error == 0) /* keep earliest error condition */
+ p->error = e;
+ p->next = nuls; /* try to bring things to a halt */
+ p->end = nuls;
+ return(0); /* make the return value well-defined */
+}
+
+/*
+ - allocset - allocate a set of characters for []
+ == static cset *allocset(register struct parse *p);
+ */
+static cset *
+allocset(struct parse *p)
+{
+ register int no = p->g->ncsets++;
+ register size_t nc;
+ register size_t nbytes;
+ register cset *cs;
+ register size_t css = (size_t)p->g->csetsize;
+ register int i;
+
+ if (no >= p->ncsalloc) { /* need another column of space */
+ p->ncsalloc += CHAR_BIT;
+ nc = p->ncsalloc;
+ assert(nc % CHAR_BIT == 0);
+ nbytes = nc / CHAR_BIT * css;
+ if (p->g->sets == NULL)
+ p->g->sets = (cset *)malloc(nc * sizeof(cset));
+ else
+ p->g->sets = (cset *)realloc((char *)p->g->sets,
+ nc * sizeof(cset));
+ if (p->g->setbits == NULL)
+ p->g->setbits = (uch *)malloc(nbytes);
+ else {
+ p->g->setbits = (uch *)realloc((char *)p->g->setbits,
+ nbytes);
+ /* xxx this isn't right if setbits is now NULL */
+ for (i = 0; i < no; i++)
+ p->g->sets[i].ptr = p->g->setbits + css*(i/CHAR_BIT);
+ }
+ if (p->g->sets != NULL && p->g->setbits != NULL)
+ (void) memset((char *)p->g->setbits + (nbytes - css),
+ 0, css);
+ else {
+ no = 0;
+ SETERROR(REG_ESPACE);
+ /* caller's responsibility not to do set ops */
+ }
+ }
+
+ assert(p->g->sets != NULL); /* xxx */
+ cs = &p->g->sets[no];
+ cs->ptr = p->g->setbits + css*((no)/CHAR_BIT);
+ cs->mask = 1 << ((no) % CHAR_BIT);
+ cs->hash = 0;
+ cs->smultis = 0;
+ cs->multis = NULL;
+
+ return(cs);
+}
+
+/*
+ - freeset - free a now-unused set
+ == static void freeset(register struct parse *p, register cset *cs);
+ */
+static void
+freeset(struct parse *p, cset *cs)
+{
+ register int i;
+ register cset *top = &p->g->sets[p->g->ncsets];
+ register size_t css = (size_t)p->g->csetsize;
+
+ for (i = 0; i < css; i++)
+ CHsub(cs, i);
+ if (cs == top-1) /* recover only the easy case */
+ p->g->ncsets--;
+}
+
+/*
+ - freezeset - final processing on a set of characters
+ == static int freezeset(register struct parse *p, register cset *cs);
+ *
+ * The main task here is merging identical sets. This is usually a waste
+ * of time (although the hash code minimizes the overhead), but can win
+ * big if REG_ICASE is being used. REG_ICASE, by the way, is why the hash
+ * is done using addition rather than xor -- all ASCII [aA] sets xor to
+ * the same value!
+ */
+static int /* set number */
+freezeset(struct parse *p, cset *cs)
+{
+ register uch h = cs->hash;
+ register int i;
+ register cset *top = &p->g->sets[p->g->ncsets];
+ register cset *cs2;
+ register size_t css = (size_t)p->g->csetsize;
+
+ /* look for an earlier one which is the same */
+ for (cs2 = &p->g->sets[0]; cs2 < top; cs2++)
+ if (cs2->hash == h && cs2 != cs) {
+ /* maybe */
+ for (i = 0; i < css; i++)
+ if (!!CHIN(cs2, i) != !!CHIN(cs, i))
+ break; /* no */
+ if (i == css)
+ break; /* yes */
+ }
+
+ if (cs2 < top) { /* found one */
+ freeset(p, cs);
+ cs = cs2;
+ }
+
+ return((int)(cs - p->g->sets));
+}
+
+/*
+ - firstch - return first character in a set (which must have at least one)
+ == static int firstch(register struct parse *p, register cset *cs);
+ */
+static int /* character; there is no "none" value */
+firstch(struct parse *p, cset *cs)
+{
+ register int i;
+ register size_t css = (size_t)p->g->csetsize;
+
+ for (i = 0; i < css; i++)
+ if (CHIN(cs, i))
+ return((char)i);
+ assert(never);
+ return(0); /* arbitrary */
+}
+
+/*
+ - nch - number of characters in a set
+ == static int nch(register struct parse *p, register cset *cs);
+ */
+static int
+nch(struct parse *p, cset *cs)
+{
+ register int i;
+ register size_t css = (size_t)p->g->csetsize;
+ register int n = 0;
+
+ for (i = 0; i < css; i++)
+ if (CHIN(cs, i))
+ n++;
+ return(n);
+}
+
+/*
+ - mcadd - add a collating element to a cset
+ == static void mcadd(register struct parse *p, register cset *cs, \
+ == register char *cp);
+ */
+static void
+mcadd(struct parse *p, cset *cs, char *cp)
+{
+ register size_t oldend = cs->smultis;
+
+ cs->smultis += strlen(cp) + 1;
+ if (cs->multis == NULL)
+ cs->multis = malloc(cs->smultis);
+ else
+ cs->multis = realloc(cs->multis, cs->smultis);
+ if (cs->multis == NULL) {
+ SETERROR(REG_ESPACE);
+ return;
+ }
+
+ (void) strcpy(cs->multis + oldend - 1, cp);
+ cs->multis[cs->smultis - 1] = '\0';
+}
+
+/*
+ - mcsub - subtract a collating element from a cset
+ == static void mcsub(register cset *cs, register char *cp);
+ */
+static void
+mcsub(cset *cs, char *cp)
+{
+ register char *fp = mcfind(cs, cp);
+ register size_t len = strlen(fp);
+
+ assert(fp != NULL);
+ (void) memmove(fp, fp + len + 1,
+ cs->smultis - (fp + len + 1 - cs->multis));
+ cs->smultis -= len;
+
+ if (cs->smultis == 0) {
+ free(cs->multis);
+ cs->multis = NULL;
+ return;
+ }
+
+ cs->multis = realloc(cs->multis, cs->smultis);
+ assert(cs->multis != NULL);
+}
+
+/*
+ - mcin - is a collating element in a cset?
+ == static int mcin(register cset *cs, register char *cp);
+ */
+static int
+mcin(cset *cs, char *cp)
+{
+ return(mcfind(cs, cp) != NULL);
+}
+
+/*
+ - mcfind - find a collating element in a cset
+ == static char *mcfind(register cset *cs, register char *cp);
+ */
+static char *
+mcfind(cset *cs, char *cp)
+{
+ register char *p;
+
+ if (cs->multis == NULL)
+ return(NULL);
+ for (p = cs->multis; *p != '\0'; p += strlen(p) + 1)
+ if (strcmp(cp, p) == 0)
+ return(p);
+ return(NULL);
+}
+
+/*
+ - mcinvert - invert the list of collating elements in a cset
+ == static void mcinvert(register struct parse *p, register cset *cs);
+ *
+ * This would have to know the set of possibilities. Implementation
+ * is deferred.
+ */
+static void
+mcinvert(struct parse *p, cset *cs)
+{
+ assert(cs->multis == NULL); /* xxx */
+}
+
+/*
+ - mccase - add case counterparts of the list of collating elements in a cset
+ == static void mccase(register struct parse *p, register cset *cs);
+ *
+ * This would have to know the set of possibilities. Implementation
+ * is deferred.
+ */
+static void
+mccase(struct parse *p, cset *cs)
+{
+ assert(cs->multis == NULL); /* xxx */
+}
+
+/*
+ - isinsets - is this character in any sets?
+ == static int isinsets(register struct re_guts *g, int c);
+ */
+static int /* predicate */
+isinsets(struct re_guts *g, int c)
+{
+ register uch *col;
+ register int i;
+ register int ncols = (g->ncsets+(CHAR_BIT-1)) / CHAR_BIT;
+ register unsigned uc = (unsigned char)c;
+
+ for (i = 0, col = g->setbits; i < ncols; i++, col += g->csetsize)
+ if (col[uc] != 0)
+ return(1);
+ return(0);
+}
+
+/*
+ - samesets - are these two characters in exactly the same sets?
+ == static int samesets(register struct re_guts *g, int c1, int c2);
+ */
+static int /* predicate */
+samesets(struct re_guts *g, int c1, int c2)
+{
+ register uch *col;
+ register int i;
+ register int ncols = (g->ncsets+(CHAR_BIT-1)) / CHAR_BIT;
+ register unsigned uc1 = (unsigned char)c1;
+ register unsigned uc2 = (unsigned char)c2;
+
+ for (i = 0, col = g->setbits; i < ncols; i++, col += g->csetsize)
+ if (col[uc1] != col[uc2])
+ return(0);
+ return(1);
+}
+
+/*
+ - categorize - sort out character categories
+ == static void categorize(struct parse *p, register struct re_guts *g);
+ */
+static void
+categorize(struct parse *p, struct re_guts *g)
+{
+ register cat_t *cats = g->categories;
+ register int c;
+ register int c2;
+ register cat_t cat;
+
+ /* avoid making error situations worse */
+ if (p->error != 0)
+ return;
+
+ for (c = CHAR_MIN; c <= CHAR_MAX; c++)
+ if (cats[c] == 0 && isinsets(g, c)) {
+ cat = g->ncategories++;
+ cats[c] = cat;
+ for (c2 = c+1; c2 <= CHAR_MAX; c2++)
+ if (cats[c2] == 0 && samesets(g, c, c2))
+ cats[c2] = cat;
+ }
+}
+
+/*
+ - dupl - emit a duplicate of a bunch of sops
+ == static sopno dupl(register struct parse *p, sopno start, sopno finish);
+ */
+static sopno /* start of duplicate */
+dupl(struct parse *p,
+ sopno start, /* from here */
+ sopno finish) /* to this less one */
+{
+ register sopno ret = HERE();
+ register sopno len = finish - start;
+
+ assert(finish >= start);
+ if (len == 0)
+ return(ret);
+ enlarge(p, p->ssize + len); /* this many unexpected additions */
+ assert(p->ssize >= p->slen + len);
+ (void) memcpy((char *)(p->strip + p->slen),
+ (char *)(p->strip + start), (size_t)len*sizeof(sop));
+ p->slen += len;
+ return(ret);
+}
+
+/*
+ - doemit - emit a strip operator
+ == static void doemit(register struct parse *p, sop op, size_t opnd);
+ *
+ * It might seem better to implement this as a macro with a function as
+ * hard-case backup, but it's just too big and messy unless there are
+ * some changes to the data structures. Maybe later.
+ */
+static void
+doemit(struct parse *p, sop op, size_t opnd)
+{
+ /* avoid making error situations worse */
+ if (p->error != 0)
+ return;
+
+ /* deal with oversize operands ("can't happen", more or less) */
+ assert(opnd < 1<<OPSHIFT);
+
+ /* deal with undersized strip */
+ if (p->slen >= p->ssize)
+ enlarge(p, (p->ssize+1) / 2 * 3); /* +50% */
+ assert(p->slen < p->ssize);
+
+ /* finally, it's all reduced to the easy case */
+ p->strip[p->slen++] = SOP(op, opnd);
+}
+
+/*
+ - doinsert - insert a sop into the strip
+ == static void doinsert(register struct parse *p, sop op, size_t opnd, sopno pos);
+ */
+static void
+doinsert(struct parse *p, sop op, size_t opnd, sopno pos)
+{
+ register sopno sn;
+ register sop s;
+ register int i;
+
+ /* avoid making error situations worse */
+ if (p->error != 0)
+ return;
+
+ sn = HERE();
+ EMIT(op, opnd); /* do checks, ensure space */
+ assert(HERE() == sn+1);
+ s = p->strip[sn];
+
+ /* adjust paren pointers */
+ assert(pos > 0);
+ for (i = 1; i < NPAREN; i++) {
+ if (p->pbegin[i] >= pos) {
+ p->pbegin[i]++;
+ }
+ if (p->pend[i] >= pos) {
+ p->pend[i]++;
+ }
+ }
+
+ memmove((char *)&p->strip[pos+1], (char *)&p->strip[pos],
+ (HERE()-pos-1)*sizeof(sop));
+ p->strip[pos] = s;
+}
+
+/*
+ - dofwd - complete a forward reference
+ == static void dofwd(register struct parse *p, sopno pos, sop value);
+ */
+static void
+dofwd(struct parse *p, sopno pos, sop value)
+{
+ /* avoid making error situations worse */
+ if (p->error != 0)
+ return;
+
+ assert(value < 1<<OPSHIFT);
+ p->strip[pos] = OP(p->strip[pos]) | value;
+}
+
+/*
+ - enlarge - enlarge the strip
+ == static void enlarge(register struct parse *p, sopno size);
+ */
+static void
+enlarge(struct parse *p, sopno size)
+{
+ register sop *sp;
+
+ if (p->ssize >= size)
+ return;
+
+ sp = (sop *)realloc(p->strip, size*sizeof(sop));
+ if (sp == NULL) {
+ SETERROR(REG_ESPACE);
+ return;
+ }
+ p->strip = sp;
+ p->ssize = size;
+}
+
+/*
+ - stripsnug - compact the strip
+ == static void stripsnug(register struct parse *p, register struct re_guts *g);
+ */
+static void
+stripsnug(struct parse *p, struct re_guts *g)
+{
+ g->nstates = p->slen;
+ g->strip = (sop *)realloc((char *)p->strip, p->slen * sizeof(sop));
+ if (g->strip == NULL) {
+ SETERROR(REG_ESPACE);
+ g->strip = p->strip;
+ }
+}
+
+/*
+ - findmust - fill in must and mlen with longest mandatory literal string
+ == static void findmust(register struct parse *p, register struct re_guts *g);
+ *
+ * This algorithm could do fancy things like analyzing the operands of |
+ * for common subsequences. Someday. This code is simple and finds most
+ * of the interesting cases.
+ *
+ * Note that must and mlen got initialized during setup.
+ */
+static void
+findmust(struct parse *p, struct re_guts *g)
+{
+ register sop *scan;
+ sop *start;
+ register sop *newstart;
+ register sopno newlen;
+ register sop s;
+ register char *cp;
+ register sopno i;
+
+ /* avoid making error situations worse */
+ if (p->error != 0)
+ return;
+
+ /* find the longest OCHAR sequence in strip */
+ newlen = 0;
+ scan = g->strip + 1;
+ do {
+ s = *scan++;
+ switch (OP(s)) {
+ case OCHAR: /* sequence member */
+ if (newlen == 0) /* new sequence */
+ newstart = scan - 1;
+ newlen++;
+ break;
+ case OPLUS_: /* things that don't break one */
+ case OLPAREN:
+ case ORPAREN:
+ break;
+ case OQUEST_: /* things that must be skipped */
+ case OCH_:
+ scan--;
+ do {
+ scan += OPND(s);
+ s = *scan;
+ /* assert() interferes w debug printouts */
+ if (OP(s) != O_QUEST && OP(s) != O_CH &&
+ OP(s) != OOR2) {
+ g->iflags |= BAD;
+ return;
+ }
+ } while (OP(s) != O_QUEST && OP(s) != O_CH);
+ /* fallthrough */
+ default: /* things that break a sequence */
+ if (newlen > g->mlen) { /* ends one */
+ start = newstart;
+ g->mlen = newlen;
+ }
+ newlen = 0;
+ break;
+ }
+ } while (OP(s) != OEND);
+
+ if (g->mlen == 0) /* there isn't one */
+ return;
+
+ /* turn it into a character string */
+ g->must = malloc((size_t)g->mlen + 1);
+ if (g->must == NULL) { /* argh; just forget it */
+ g->mlen = 0;
+ return;
+ }
+ cp = g->must;
+ scan = start;
+ for (i = g->mlen; i > 0; i--) {
+ while (OP(s = *scan++) != OCHAR)
+ continue;
+ assert(cp < g->must + g->mlen);
+ *cp++ = (char)OPND(s);
+ }
+ assert(cp == g->must + g->mlen);
+ *cp++ = '\0'; /* just on general principles */
+}
+
+/*
+ - pluscount - count + nesting
+ == static sopno pluscount(register struct parse *p, register struct re_guts *g);
+ */
+static sopno /* nesting depth */
+pluscount(struct parse *p, struct re_guts *g)
+{
+ register sop *scan;
+ register sop s;
+ register sopno plusnest = 0;
+ register sopno maxnest = 0;
+
+ if (p->error != 0)
+ return(0); /* there may not be an OEND */
+
+ scan = g->strip + 1;
+ do {
+ s = *scan++;
+ switch (OP(s)) {
+ case OPLUS_:
+ plusnest++;
+ break;
+ case O_PLUS:
+ if (plusnest > maxnest)
+ maxnest = plusnest;
+ plusnest--;
+ break;
+ }
+ } while (OP(s) != OEND);
+ if (plusnest != 0)
+ g->iflags |= BAD;
+ return(maxnest);
+}
diff --git a/regex/regerror.c b/regex/regerror.c
new file mode 100644
index 00000000..7b1cecf4
--- /dev/null
+++ b/regex/regerror.c
@@ -0,0 +1,172 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994 Henry Spencer.
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Henry Spencer.
+ *
+ * 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.
+ *
+ * @(#)regerror.c 8.4 (Berkeley) 3/20/94
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)regerror.c 8.4 (Berkeley) 3/20/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdlib.h>
+#include "regex.h"
+
+#include "utils.h"
+
+/* ========= begin header generated by ./mkh ========= */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* === regerror.c === */
+static char *regatoi __P((const regex_t *preg, char *localbuf));
+
+#ifdef __cplusplus
+}
+#endif
+/* ========= end header generated by ./mkh ========= */
+/*
+ = #define REG_NOMATCH 1
+ = #define REG_BADPAT 2
+ = #define REG_ECOLLATE 3
+ = #define REG_ECTYPE 4
+ = #define REG_EESCAPE 5
+ = #define REG_ESUBREG 6
+ = #define REG_EBRACK 7
+ = #define REG_EPAREN 8
+ = #define REG_EBRACE 9
+ = #define REG_BADBR 10
+ = #define REG_ERANGE 11
+ = #define REG_ESPACE 12
+ = #define REG_BADRPT 13
+ = #define REG_EMPTY 14
+ = #define REG_ASSERT 15
+ = #define REG_INVARG 16
+ = #define REG_ATOI 255 // convert name to number (!)
+ = #define REG_ITOA 0400 // convert number to name (!)
+ */
+static struct rerr {
+ int code;
+ char *name;
+ char *explain;
+} rerrs[] = {
+ {REG_NOMATCH, "REG_NOMATCH", "regexec() failed to match"},
+ {REG_BADPAT, "REG_BADPAT", "invalid regular expression"},
+ {REG_ECOLLATE, "REG_ECOLLATE", "invalid collating element"},
+ {REG_ECTYPE, "REG_ECTYPE", "invalid character class"},
+ {REG_EESCAPE, "REG_EESCAPE", "trailing backslash (\\)"},
+ {REG_ESUBREG, "REG_ESUBREG", "invalid backreference number"},
+ {REG_EBRACK, "REG_EBRACK", "brackets ([ ]) not balanced"},
+ {REG_EPAREN, "REG_EPAREN", "parentheses not balanced"},
+ {REG_EBRACE, "REG_EBRACE", "braces not balanced"},
+ {REG_BADBR, "REG_BADBR", "invalid repetition count(s)"},
+ {REG_ERANGE, "REG_ERANGE", "invalid character range"},
+ {REG_ESPACE, "REG_ESPACE", "out of memory"},
+ {REG_BADRPT, "REG_BADRPT", "repetition-operator operand invalid"},
+ {REG_EMPTY, "REG_EMPTY", "empty (sub)expression"},
+ {REG_ASSERT, "REG_ASSERT", "\"can't happen\" -- you found a bug"},
+ {REG_INVARG, "REG_INVARG", "invalid argument to regex routine"},
+ {0, "", "*** unknown regexp error code ***"}
+};
+
+/*
+ - regerror - the interface to error numbers
+ = extern size_t regerror(int, const regex_t *, char *, size_t);
+ */
+/* ARGSUSED */
+size_t __stdcall
+regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size)
+{
+ register struct rerr *r;
+ register size_t len;
+ register int target = errcode &~ REG_ITOA;
+ register char *s;
+ char convbuf[50];
+
+ if (errcode == REG_ATOI)
+ s = regatoi(preg, convbuf);
+ else {
+ for (r = rerrs; r->code != 0; r++)
+ if (r->code == target)
+ break;
+
+ if (errcode&REG_ITOA) {
+ if (r->code != 0)
+ (void) strcpy(convbuf, r->name);
+ else
+ sprintf(convbuf, "REG_0x%x", target);
+ assert(strlen(convbuf) < sizeof(convbuf));
+ s = convbuf;
+ } else
+ s = r->explain;
+ }
+
+ len = strlen(s) + 1;
+ if (errbuf_size > 0) {
+ if (errbuf_size > len)
+ (void) strcpy(errbuf, s);
+ else {
+ (void) strncpy(errbuf, s, errbuf_size-1);
+ errbuf[errbuf_size-1] = '\0';
+ }
+ }
+
+ return(len);
+}
+
+/*
+ - regatoi - internal routine to implement REG_ATOI
+ == static char *regatoi(const regex_t *preg, char *localbuf);
+ */
+static char *
+regatoi(const regex_t *preg, char *localbuf)
+{
+ register struct rerr *r;
+
+ for (r = rerrs; r->code != 0; r++)
+ if (strcmp(r->name, preg->re_endp) == 0)
+ break;
+ if (r->code == 0)
+ return("0");
+
+ sprintf(localbuf, "%d", r->code);
+ return(localbuf);
+}
diff --git a/regex/regex.doc b/regex/regex.doc
new file mode 100644
index 00000000..100be4fb
--- /dev/null
+++ b/regex/regex.doc
@@ -0,0 +1,462 @@
+
+
+
+REGEX(3) C Library Functions REGEX(3)
+
+
+
+NAME
+ regcomp, regexec, regerror, regfree - regular-expression
+ library
+
+SYNOPSIS
+ #include <sys/types.h>
+ #include <regex.h>
+
+ int regcomp(regex_t *preg, const char *pattern, int cflags);
+
+ int regexec(const regex_t *preg, const char *string,
+ size_t nmatch, regmatch_t pmatch[], int eflags);
+
+ size_t regerror(int errcode, const regex_t *preg,
+ char *errbuf, size_t errbuf_size);
+
+ void regfree(regex_t *preg);
+
+DESCRIPTION
+ These routines implement POSIX 1003.2 regular expressions
+ (``RE''s); see re_format(7). Regcomp compiles an RE written
+ as a string into an internal form, regexec matches that
+ internal form against a string and reports results, regerror
+ transforms error codes from either into human-readable mes-
+ sages, and regfree frees any dynamically-allocated storage
+ used by the internal form of an RE.
+
+ The header <regex.h> declares two structure types, regex_t
+ and regmatch_t, the former for compiled internal forms and
+ the latter for match reporting. It also declares the four
+ functions, a type regoff_t, and a number of constants with
+ names starting with ``REG_''.
+
+ Regcomp compiles the regular expression contained in the
+ pattern string, subject to the flags in cflags, and places
+ the results in the regex_t structure pointed to by preg.
+ Cflags is the bitwise OR of zero or more of the following
+ flags:
+
+ REG_EXTENDED Compile modern (``extended'') REs, rather than
+ the obsolete (``basic'') REs that are the
+ default.
+
+ REG_BASIC This is a synonym for 0, provided as a coun-
+ terpart to REG_EXTENDED to improve readabil-
+ ity.
+
+ REG_NOSPEC Compile with recognition of all special char-
+ acters turned off. All characters are thus
+ considered ordinary, so the ``RE'' is a
+ literal string. This is an extension, compa-
+ tible with but not specified by POSIX 1003.2,
+
+
+
+SunOS 5.5 Last change: March 20, 1994 1
+
+
+
+
+
+
+REGEX(3) C Library Functions REGEX(3)
+
+
+
+ and should be used with caution in software
+ intended to be portable to other systems.
+ REG_EXTENDED and REG_NOSPEC may not be used in
+ the same call to regcomp.
+
+ REG_ICASE Compile for matching that ignores upper/lower
+ case distinctions. See re_format(7).
+
+ REG_NOSUB Compile for matching that need only report
+ success or failure, not what was matched.
+
+ REG_NEWLINE Compile for newline-sensitive matching. By
+ default, newline is a completely ordinary
+ character with no special meaning in either
+ REs or strings. With this flag, `[^' bracket
+ expressions and `.' never match newline, a `^'
+ anchor matches the null string after any new-
+ line in the string in addition to its normal
+ function, and the `$' anchor matches the null
+ string before any newline in the string in
+ addition to its normal function.
+
+ REG_PEND The regular expression ends, not at the first
+ NUL, but just before the character pointed to
+ by the re_endp member of the structure pointed
+ to by preg. The re_endp member is of type
+ const char *. This flag permits inclusion of
+ NULs in the RE; they are considered ordinary
+ characters. This is an extension, compatible
+ with but not specified by POSIX 1003.2, and
+ should be used with caution in software
+ intended to be portable to other systems.
+
+ When successful, regcomp returns 0 and fills in the struc-
+ ture pointed to by preg. One member of that structure
+ (other than re_endp) is publicized: re_nsub, of type
+ size_t, contains the number of parenthesized subexpressions
+ within the RE (except that the value of this member is unde-
+ fined if the REG_NOSUB flag was used). If regcomp fails, it
+ returns a non-zero error code; see DIAGNOSTICS.
+
+ Regexec matches the compiled RE pointed to by preg against
+ the string, subject to the flags in eflags, and reports
+ results using nmatch, pmatch, and the returned value. The
+ RE must have been compiled by a previous invocation of
+ regcomp. The compiled form is not altered during execution
+ of regexec, so a single compiled RE can be used simultane-
+ ously by multiple threads.
+
+ By default, the NUL-terminated string pointed to by string
+ is considered to be the text of an entire line, minus any
+ terminating newline. The eflags argument is the bitwise OR
+
+
+
+SunOS 5.5 Last change: March 20, 1994 2
+
+
+
+
+
+
+REGEX(3) C Library Functions REGEX(3)
+
+
+
+ of zero or more of the following flags:
+
+ REG_NOTBOL The first character of the string is not the
+ beginning of a line, so the `^' anchor should
+ not match before it. This does not affect the
+ behavior of newlines under REG_NEWLINE.
+
+ REG_NOTEOL The NUL terminating the string does not end a
+ line, so the `$' anchor should not match
+ before it. This does not affect the behavior
+ of newlines under REG_NEWLINE.
+
+ REG_STARTEND The string is considered to start at string +
+ pmatch[0].rm_so and to have a terminating NUL
+ located at string + pmatch[0].rm_eo (there
+ need not actually be a NUL at that location),
+ regardless of the value of nmatch. See below
+ for the definition of pmatch and nmatch. This
+ is an extension, compatible with but not
+ specified by POSIX 1003.2, and should be used
+ with caution in software intended to be port-
+ able to other systems. Note that a non-zero
+ rm_so does not imply REG_NOTBOL; REG_STARTEND
+ affects only the location of the string, not
+ how it is matched.
+
+ See re_format(7) for a discussion of what is matched in
+ situations where an RE or a portion thereof could match any
+ of several substrings of string.
+
+ Normally, regexec returns 0 for success and the non-zero
+ code REG_NOMATCH for failure. Other non-zero error codes
+ may be returned in exceptional situations; see DIAGNOSTICS.
+
+ If REG_NOSUB was specified in the compilation of the RE, or
+ if nmatch is 0, regexec ignores the pmatch argument (but see
+ below for the case where REG_STARTEND is specified). Other-
+ wise, pmatch points to an array of nmatch structures of type
+ regmatch_t. Such a structure has at least the members rm_so
+ and rm_eo, both of type regoff_t (a signed arithmetic type
+ at least as large as an off_t and a ssize_t), containing
+ respectively the offset of the first character of a sub-
+ string and the offset of the first character after the end
+ of the substring. Offsets are measured from the beginning
+ of the string argument given to regexec. An empty substring
+ is denoted by equal offsets, both indicating the character
+ following the empty substring.
+
+ The 0th member of the pmatch array is filled in to indicate
+ what substring of string was matched by the entire RE.
+ Remaining members report what substring was matched by
+ parenthesized subexpressions within the RE; member i reports
+
+
+
+SunOS 5.5 Last change: March 20, 1994 3
+
+
+
+
+
+
+REGEX(3) C Library Functions REGEX(3)
+
+
+
+ subexpression i, with subexpressions counted (starting at 1)
+ by the order of their opening parentheses in the RE, left to
+ right. Unused entries in the array-corresponding either to
+ subexpressions that did not participate in the match at all,
+ or to subexpressions that do not exist in the RE (that is,
+ i > preg->re_nsub)-have both rm_so and rm_eo set to -1. If
+ a subexpression participated in the match several times, the
+ reported substring is the last one it matched. (Note, as an
+ example in particular, that when the RE `(b*)+' matches
+ `bbb', the parenthesized subexpression matches each of the
+ three `b's and then an infinite number of empty strings fol-
+ lowing the last `b', so the reported substring is one of the
+ empties.)
+
+ If REG_STARTEND is specified, pmatch must point to at least
+ one regmatch_t (even if nmatch is 0 or REG_NOSUB was speci-
+ fied), to hold the input offsets for REG_STARTEND. Use for
+ output is still entirely controlled by nmatch; if nmatch is
+ 0 or REG_NOSUB was specified, the value of pmatch[0] will
+ not be changed by a successful regexec.
+
+ Regerror maps a non-zero errcode from either regcomp or
+ regexec to a human-readable, printable message. If preg is
+ non-NULL, the error code should have arisen from use of the
+ regex_t pointed to by preg, and if the error code came from
+ regcomp, it should have been the result from the most recent
+ regcomp using that regex_t. (Regerror may be able to supply
+ a more detailed message using information from the regex_t.)
+ Regerror places the NUL-terminated message into the buffer
+ pointed to by errbuf, limiting the length (including the
+ NUL) to at most errbuf_size bytes. If the whole message
+ won't fit, as much of it as will fit before the terminating
+ NUL is supplied. In any case, the returned value is the
+ size of buffer needed to hold the whole message (including
+ terminating NUL). If errbuf_size is 0, errbuf is ignored
+ but the return value is still correct.
+
+ If the errcode given to regerror is first ORed with
+ REG_ITOA, the ``message'' that results is the printable name
+ of the error code, e.g. ``REG_NOMATCH'', rather than an
+ explanation thereof. If errcode is REG_ATOI, then preg
+ shall be non-NULL and the re_endp member of the structure it
+ points to must point to the printable name of an error code;
+ in this case, the result in errbuf is the decimal digits of
+ the numeric value of the error code (0 if the name is not
+ recognized). REG_ITOA and REG_ATOI are intended primarily
+ as debugging facilities; they are extensions, compatible
+ with but not specified by POSIX 1003.2, and should be used
+ with caution in software intended to be portable to other
+ systems. Be warned also that they are considered experimen-
+ tal and changes are possible.
+
+
+
+
+SunOS 5.5 Last change: March 20, 1994 4
+
+
+
+
+
+
+REGEX(3) C Library Functions REGEX(3)
+
+
+
+ Regfree frees any dynamically-allocated storage associated
+ with the compiled RE pointed to by preg. The remaining
+ regex_t is no longer a valid compiled RE and the effect of
+ supplying it to regexec or regerror is undefined.
+
+ None of these functions references global variables except
+ for tables of constants; all are safe for use from multiple
+ threads if the arguments are safe.
+
+IMPLEMENTATION CHOICES
+ There are a number of decisions that 1003.2 leaves up to the
+ implementor, either by explicitly saying ``undefined'' or by
+ virtue of them being forbidden by the RE grammar. This
+ implementation treats them as follows.
+
+ See re_format(7) for a discussion of the definition of
+ case-independent matching.
+
+ There is no particular limit on the length of REs, except
+ insofar as memory is limited. Memory usage is approximately
+ linear in RE size, and largely insensitive to RE complexity,
+ except for bounded repetitions. See BUGS for one short RE
+ using them that will run almost any system out of memory.
+
+ A backslashed character other than one specifically given a
+ magic meaning by 1003.2 (such magic meanings occur only in
+ obsolete [``basic''] REs) is taken as an ordinary character.
+
+ Any unmatched [ is a REG_EBRACK error.
+
+ Equivalence classes cannot begin or end bracket-expression
+ ranges. The endpoint of one range cannot begin another.
+
+ RE_DUP_MAX, the limit on repetition counts in bounded
+ repetitions, is 255.
+
+ A repetition operator (?, *, +, or bounds) cannot follow
+ another repetition operator. A repetition operator cannot
+ begin an expression or subexpression or follow `^' or `|'.
+
+ `|' cannot appear first or last in a (sub)expression or
+ after another `|', i.e. an operand of `|' cannot be an empty
+ subexpression. An empty parenthesized subexpression, `()',
+ is legal and matches an empty (sub)string. An empty string
+ is not a legal RE.
+
+ A `{' followed by a digit is considered the beginning of
+ bounds for a bounded repetition, which must then follow the
+ syntax for bounds. A `{' not followed by a digit is con-
+ sidered an ordinary character.
+
+
+
+
+
+SunOS 5.5 Last change: March 20, 1994 5
+
+
+
+
+
+
+REGEX(3) C Library Functions REGEX(3)
+
+
+
+ `^' and `$' beginning and ending subexpressions in obsolete
+ (``basic'') REs are anchors, not ordinary characters.
+
+SEE ALSO
+ grep(1), re_format(7)
+
+ POSIX 1003.2, sections 2.8 (Regular Expression Notation) and
+ B.5 (C Binding for Regular Expression Matching).
+
+DIAGNOSTICS
+ Non-zero error codes from regcomp and regexec include the
+ following:
+
+ REG_NOMATCH regexec() failed to match
+ REG_BADPAT invalid regular expression
+ REG_ECOLLATE invalid collating element
+ REG_ECTYPE invalid character class
+ REG_EESCAPE \ applied to unescapable character
+ REG_ESUBREG invalid backreference number
+ REG_EBRACK brackets [ ] not balanced
+ REG_EPAREN parentheses ( ) not balanced
+ REG_EBRACE braces { } not balanced
+ REG_BADBR invalid repetition count(s) in { }
+ REG_ERANGE invalid character range in [ ]
+ REG_ESPACE ran out of memory
+ REG_BADRPT ?, *, or + operand invalid
+ REG_EMPTY empty (sub)expression
+ REG_ASSERT ``can't happen''-you found a bug
+ REG_INVARG invalid argument, e.g. negative-length string
+
+HISTORY
+ Originally written by Henry Spencer. Altered for inclusion
+ in the 4.4BSD distribution.
+
+BUGS
+ This is an alpha release with known defects. Please report
+ problems.
+
+ There is one known functionality bug. The implementation of
+ internationalization is incomplete: the locale is always
+ assumed to be the default one of 1003.2, and only the col-
+ lating elements etc. of that locale are available.
+
+ The back-reference code is subtle and doubts linger about
+ its correctness in complex cases.
+
+ Regexec performance is poor. This will improve with later
+ releases. Nmatch exceeding 0 is expensive; nmatch exceeding
+ 1 is worse. Regexec is largely insensitive to RE complexity
+ except that back references are massively expensive. RE
+ length does matter; in particular, there is a strong speed
+ bonus for keeping RE length under about 30 characters, with
+
+
+
+SunOS 5.5 Last change: March 20, 1994 6
+
+
+
+
+
+
+REGEX(3) C Library Functions REGEX(3)
+
+
+
+ most special characters counting roughly double.
+
+ Regcomp implements bounded repetitions by macro expansion,
+ which is costly in time and space if counts are large or
+ bounded repetitions are nested. An RE like, say,
+ `((((a{1,100}){1,100}){1,100}){1,100}){1,100}' will (eventu-
+ ally) run almost any existing machine out of swap space.
+
+ There are suspected problems with response to obscure error
+ conditions. Notably, certain kinds of internal overflow,
+ produced only by truly enormous REs or by multiply nested
+ bounded repetitions, are probably not handled well.
+
+ Due to a mistake in 1003.2, things like `a)b' are legal REs
+ because `)' is a special character only in the presence of a
+ previous unmatched `('. This can't be fixed until the spec
+ is fixed.
+
+ The standard's definition of back references is vague. For
+ example, does `a\(\(b\)*\2\)*d' match `abbbd'? Until the
+ standard is clarified, behavior in such cases should not be
+ relied on.
+
+ The implementation of word-boundary matching is a bit of a
+ kludge, and bugs may lurk in combinations of word-boundary
+ matching and anchoring.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+SunOS 5.5 Last change: March 20, 1994 7
+
+
+
diff --git a/regex/regex.h b/regex/regex.h
new file mode 100644
index 00000000..8d11536e
--- /dev/null
+++ b/regex/regex.h
@@ -0,0 +1,117 @@
+
+
+/*-
+ * Copyright (c) 1992 Henry Spencer.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Henry Spencer of the University of Toronto.
+ *
+ * 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.
+ *
+ * @(#)regex.h 8.2 (Berkeley) 1/3/94
+ */
+
+#ifndef _REGEX_H_
+#define _REGEX_H_
+
+#ifdef WIN32
+#include <sys/types.h>
+#define __const
+#define __BEGIN_DECLS
+#define __END_DECLS
+#define __P(_X) _X
+#else
+#include <sys/cdefs.h>
+#define __stdcall
+#endif
+
+/* types */
+typedef off_t regoff_t;
+
+typedef struct {
+ int re_magic;
+ size_t re_nsub; /* number of parenthesized subexpressions */
+ __const char *re_endp; /* end pointer for REG_PEND */
+ struct re_guts *re_g; /* none of your business :-) */
+} regex_t;
+
+typedef struct {
+ regoff_t rm_so; /* start of match */
+ regoff_t rm_eo; /* end of match */
+} regmatch_t;
+
+/* regcomp() flags */
+#define REG_BASIC 0000
+#define REG_EXTENDED 0001
+#define REG_ICASE 0002
+#define REG_NOSUB 0004
+#define REG_NEWLINE 0010
+#define REG_NOSPEC 0020
+#define REG_PEND 0040
+#define REG_DUMP 0200
+
+/* regerror() flags */
+#define REG_NOMATCH 1
+#define REG_BADPAT 2
+#define REG_ECOLLATE 3
+#define REG_ECTYPE 4
+#define REG_EESCAPE 5
+#define REG_ESUBREG 6
+#define REG_EBRACK 7
+#define REG_EPAREN 8
+#define REG_EBRACE 9
+#define REG_BADBR 10
+#define REG_ERANGE 11
+#define REG_ESPACE 12
+#define REG_BADRPT 13
+#define REG_EMPTY 14
+#define REG_ASSERT 15
+#define REG_INVARG 16
+#define REG_ATOI 255 /* convert name to number (!) */
+#define REG_ITOA 0400 /* convert number to name (!) */
+
+/* regexec() flags */
+#define REG_NOTBOL 00001
+#define REG_NOTEOL 00002
+#define REG_STARTEND 00004
+#define REG_TRACE 00400 /* tracing of execution */
+#define REG_LARGE 01000 /* force large representation */
+#define REG_BACKR 02000 /* force use of backref code */
+
+__BEGIN_DECLS
+int __stdcall regcomp __P((regex_t *, const char *, int));
+size_t __stdcall regerror __P((int, const regex_t *, char *, size_t));
+int __stdcall regexec __P((const regex_t *,
+ const char *, size_t, regmatch_t [], int));
+void __stdcall regfree __P((regex_t *));
+__END_DECLS
+
+#endif /* !_REGEX_H_ */
diff --git a/regex/regex2.h b/regex/regex2.h
new file mode 100644
index 00000000..64b62121
--- /dev/null
+++ b/regex/regex2.h
@@ -0,0 +1,173 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994 Henry Spencer.
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Henry Spencer.
+ *
+ * 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.
+ *
+ * @(#)regex2.h 8.4 (Berkeley) 3/20/94
+ */
+
+/*
+ * First, the stuff that ends up in the outside-world include file
+ = typedef off_t regoff_t;
+ = typedef struct {
+ = int re_magic;
+ = size_t re_nsub; // number of parenthesized subexpressions
+ = const char *re_endp; // end pointer for REG_PEND
+ = struct re_guts *re_g; // none of your business :-)
+ = } regex_t;
+ = typedef struct {
+ = regoff_t rm_so; // start of match
+ = regoff_t rm_eo; // end of match
+ = } regmatch_t;
+ */
+/*
+ * internals of regex_t
+ */
+#define MAGIC1 ((('r'^0200)<<8) | 'e')
+
+/*
+ * The internal representation is a *strip*, a sequence of
+ * operators ending with an endmarker. (Some terminology etc. is a
+ * historical relic of earlier versions which used multiple strips.)
+ * Certain oddities in the representation are there to permit running
+ * the machinery backwards; in particular, any deviation from sequential
+ * flow must be marked at both its source and its destination. Some
+ * fine points:
+ *
+ * - OPLUS_ and O_PLUS are *inside* the loop they create.
+ * - OQUEST_ and O_QUEST are *outside* the bypass they create.
+ * - OCH_ and O_CH are *outside* the multi-way branch they create, while
+ * OOR1 and OOR2 are respectively the end and the beginning of one of
+ * the branches. Note that there is an implicit OOR2 following OCH_
+ * and an implicit OOR1 preceding O_CH.
+ *
+ * In state representations, an operator's bit is on to signify a state
+ * immediately *preceding* "execution" of that operator.
+ */
+typedef unsigned long sop; /* strip operator */
+typedef long sopno;
+#define OPRMASK 0xf8000000
+#define OPDMASK 0x07ffffff
+#define OPSHIFT ((unsigned)27)
+#define OP(n) ((n)&OPRMASK)
+#define OPND(n) ((n)&OPDMASK)
+#define SOP(op, opnd) ((op)|(opnd))
+/* operators meaning operand */
+/* (back, fwd are offsets) */
+#define OEND (1<<OPSHIFT) /* endmarker - */
+#define OCHAR (2<<OPSHIFT) /* character unsigned char */
+#define OBOL (3<<OPSHIFT) /* left anchor - */
+#define OEOL (4<<OPSHIFT) /* right anchor - */
+#define OANY (5<<OPSHIFT) /* . - */
+#define OANYOF (6<<OPSHIFT) /* [...] set number */
+#define OBACK_ (7<<OPSHIFT) /* begin \d paren number */
+#define O_BACK (8<<OPSHIFT) /* end \d paren number */
+#define OPLUS_ (9<<OPSHIFT) /* + prefix fwd to suffix */
+#define O_PLUS (10<<OPSHIFT) /* + suffix back to prefix */
+#define OQUEST_ (11<<OPSHIFT) /* ? prefix fwd to suffix */
+#define O_QUEST (12<<OPSHIFT) /* ? suffix back to prefix */
+#define OLPAREN (13<<OPSHIFT) /* ( fwd to ) */
+#define ORPAREN (14<<OPSHIFT) /* ) back to ( */
+#define OCH_ (15<<OPSHIFT) /* begin choice fwd to OOR2 */
+#define OOR1 (16<<OPSHIFT) /* | pt. 1 back to OOR1 or OCH_ */
+#define OOR2 (17<<OPSHIFT) /* | pt. 2 fwd to OOR2 or O_CH */
+#define O_CH (18<<OPSHIFT) /* end choice back to OOR1 */
+#define OBOW (19<<OPSHIFT) /* begin word - */
+#define OEOW (20<<OPSHIFT) /* end word - */
+
+/*
+ * Structure for [] character-set representation. Character sets are
+ * done as bit vectors, grouped 8 to a byte vector for compactness.
+ * The individual set therefore has both a pointer to the byte vector
+ * and a mask to pick out the relevant bit of each byte. A hash code
+ * simplifies testing whether two sets could be identical.
+ *
+ * This will get trickier for multicharacter collating elements. As
+ * preliminary hooks for dealing with such things, we also carry along
+ * a string of multi-character elements, and decide the size of the
+ * vectors at run time.
+ */
+typedef struct {
+ uch *ptr; /* -> uch [csetsize] */
+ uch mask; /* bit within array */
+ uch hash; /* hash code */
+ size_t smultis;
+ char *multis; /* -> char[smulti] ab\0cd\0ef\0\0 */
+} cset;
+/* note that CHadd and CHsub are unsafe, and CHIN doesn't yield 0/1 */
+#define CHadd(cs, c) ((cs)->ptr[(uch)(c)] |= (cs)->mask, (cs)->hash += (c))
+#define CHsub(cs, c) ((cs)->ptr[(uch)(c)] &= ~(cs)->mask, (cs)->hash -= (c))
+#define CHIN(cs, c) ((cs)->ptr[(uch)(c)] & (cs)->mask)
+#define MCadd(p, cs, cp) mcadd(p, cs, cp) /* regcomp() internal fns */
+#define MCsub(p, cs, cp) mcsub(p, cs, cp)
+#define MCin(p, cs, cp) mcin(p, cs, cp)
+
+/* stuff for character categories */
+typedef unsigned char cat_t;
+
+/*
+ * main compiled-expression structure
+ */
+struct re_guts {
+ int magic;
+# define MAGIC2 ((('R'^0200)<<8)|'E')
+ sop *strip; /* malloced area for strip */
+ int csetsize; /* number of bits in a cset vector */
+ int ncsets; /* number of csets in use */
+ cset *sets; /* -> cset [ncsets] */
+ uch *setbits; /* -> uch[csetsize][ncsets/CHAR_BIT] */
+ int cflags; /* copy of regcomp() cflags argument */
+ sopno nstates; /* = number of sops */
+ sopno firststate; /* the initial OEND (normally 0) */
+ sopno laststate; /* the final OEND */
+ int iflags; /* internal flags */
+# define USEBOL 01 /* used ^ */
+# define USEEOL 02 /* used $ */
+# define BAD 04 /* something wrong */
+ int nbol; /* number of ^ used */
+ int neol; /* number of $ used */
+ int ncategories; /* how many character categories */
+ cat_t *categories; /* ->catspace[-CHAR_MIN] */
+ char *must; /* match must contain this string */
+ int mlen; /* length of must */
+ size_t nsub; /* copy of re_nsub */
+ int backrefs; /* does it use back references? */
+ sopno nplus; /* how deep does it nest +s? */
+ /* catspace must be last */
+ cat_t catspace[1]; /* actually [NC] */
+};
+
+/* misc utilities */
+#define OUT (CHAR_MAX+1) /* a non-character value */
+#define ISWORD(c) (isalnum(c) || (c) == '_')
diff --git a/regex/regexec.c b/regex/regexec.c
new file mode 100644
index 00000000..0c112e53
--- /dev/null
+++ b/regex/regexec.c
@@ -0,0 +1,176 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994 Henry Spencer.
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Henry Spencer.
+ *
+ * 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.
+ *
+ * @(#)regexec.c 8.3 (Berkeley) 3/20/94
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)regexec.c 8.3 (Berkeley) 3/20/94";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * the outer shell of regexec()
+ *
+ * This file includes engine.c *twice*, after muchos fiddling with the
+ * macros that code uses. This lets the same code operate on two different
+ * representations for state sets.
+ */
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <ctype.h>
+#include "regex.h"
+
+#include "utils.h"
+#include "regex2.h"
+
+static int nope = 0; /* for use in asserts; shuts lint up */
+
+/* macros for manipulating states, small version */
+#define states long
+#define states1 states /* for later use in regexec() decision */
+#define CLEAR(v) ((v) = 0)
+#define SET0(v, n) ((v) &= ~(1 << (n)))
+#define SET1(v, n) ((v) |= 1 << (n))
+#define ISSET(v, n) ((v) & (1 << (n)))
+#define ASSIGN(d, s) ((d) = (s))
+#define EQ(a, b) ((a) == (b))
+#define STATEVARS int dummy /* dummy version */
+#define STATESETUP(m, n) /* nothing */
+#define STATETEARDOWN(m) /* nothing */
+#define SETUP(v) ((v) = 0)
+#define onestate int
+#define INIT(o, n) ((o) = (unsigned)1 << (n))
+#define INC(o) ((o) <<= 1)
+#define ISSTATEIN(v, o) ((v) & (o))
+/* some abbreviations; note that some of these know variable names! */
+/* do "if I'm here, I can also be there" etc without branches */
+#define FWD(dst, src, n) ((dst) |= ((unsigned)(src)&(here)) << (n))
+#define BACK(dst, src, n) ((dst) |= ((unsigned)(src)&(here)) >> (n))
+#define ISSETBACK(v, n) ((v) & ((unsigned)here >> (n)))
+/* function names */
+#define SNAMES /* engine.c looks after details */
+
+#include "engine.c"
+
+/* now undo things */
+#undef states
+#undef CLEAR
+#undef SET0
+#undef SET1
+#undef ISSET
+#undef ASSIGN
+#undef EQ
+#undef STATEVARS
+#undef STATESETUP
+#undef STATETEARDOWN
+#undef SETUP
+#undef onestate
+#undef INIT
+#undef INC
+#undef ISSTATEIN
+#undef FWD
+#undef BACK
+#undef ISSETBACK
+#undef SNAMES
+
+/* macros for manipulating states, large version */
+#define states char *
+#define CLEAR(v) memset(v, 0, m->g->nstates)
+#define SET0(v, n) ((v)[n] = 0)
+#define SET1(v, n) ((v)[n] = 1)
+#define ISSET(v, n) ((v)[n])
+#define ASSIGN(d, s) memcpy(d, s, m->g->nstates)
+#define EQ(a, b) (memcmp(a, b, m->g->nstates) == 0)
+#define STATEVARS int vn; char *space
+#define STATESETUP(m, nv) { (m)->space = malloc((nv)*(m)->g->nstates); \
+ if ((m)->space == NULL) return(REG_ESPACE); \
+ (m)->vn = 0; }
+#define STATETEARDOWN(m) { free((m)->space); }
+#define SETUP(v) ((v) = &m->space[m->vn++ * m->g->nstates])
+#define onestate int
+#define INIT(o, n) ((o) = (n))
+#define INC(o) ((o)++)
+#define ISSTATEIN(v, o) ((v)[o])
+/* some abbreviations; note that some of these know variable names! */
+/* do "if I'm here, I can also be there" etc without branches */
+#define FWD(dst, src, n) ((dst)[here+(n)] |= (src)[here])
+#define BACK(dst, src, n) ((dst)[here-(n)] |= (src)[here])
+#define ISSETBACK(v, n) ((v)[here - (n)])
+/* function names */
+#define LNAMES /* flag */
+
+#include "engine.c"
+
+/*
+ - regexec - interface for matching
+ = extern int regexec(const regex_t *, const char *, size_t, \
+ = regmatch_t [], int);
+ = #define REG_NOTBOL 00001
+ = #define REG_NOTEOL 00002
+ = #define REG_STARTEND 00004
+ = #define REG_TRACE 00400 // tracing of execution
+ = #define REG_LARGE 01000 // force large representation
+ = #define REG_BACKR 02000 // force use of backref code
+ *
+ * We put this here so we can exploit knowledge of the state representation
+ * when choosing which matcher to call. Also, by this point the matchers
+ * have been prototyped.
+ */
+int __stdcall /* 0 success, REG_NOMATCH failure */
+regexec(const regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags)
+{
+ register struct re_guts *g = preg->re_g;
+#ifdef REDEBUG
+# define GOODFLAGS(f) (f)
+#else
+# define GOODFLAGS(f) ((f)&(REG_NOTBOL|REG_NOTEOL|REG_STARTEND))
+#endif
+
+ if (preg->re_magic != MAGIC1 || g->magic != MAGIC2)
+ return(REG_BADPAT);
+ assert(!(g->iflags&BAD));
+ if (g->iflags&BAD) /* backstop for no-debug case */
+ return(REG_BADPAT);
+ eflags = GOODFLAGS(eflags);
+
+ if (g->nstates <= CHAR_BIT*sizeof(states1) && !(eflags&REG_LARGE))
+ return(smatcher(g, (char *)string, nmatch, pmatch, eflags));
+ else
+ return(lmatcher(g, (char *)string, nmatch, pmatch, eflags));
+}
diff --git a/regex/regfree.c b/regex/regfree.c
new file mode 100644
index 00000000..a53341fc
--- /dev/null
+++ b/regex/regfree.c
@@ -0,0 +1,79 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994 Henry Spencer.
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Henry Spencer.
+ *
+ * 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.
+ *
+ * @(#)regfree.c 8.3 (Berkeley) 3/20/94
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)regfree.c 8.3 (Berkeley) 3/20/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "regex.h"
+
+#include "utils.h"
+#include "regex2.h"
+
+/*
+ - regfree - free everything
+ = extern void regfree(regex_t *);
+ */
+void __stdcall
+regfree(regex_t *preg)
+{
+ register struct re_guts *g;
+
+ if (preg->re_magic != MAGIC1) /* oops */
+ return; /* nice to complain, but hard */
+
+ g = preg->re_g;
+ if (g == NULL || g->magic != MAGIC2) /* oops again */
+ return;
+ preg->re_magic = 0; /* mark it invalid */
+ g->magic = 0; /* mark it invalid */
+
+ if (g->strip != NULL)
+ free((char *)g->strip);
+ if (g->sets != NULL)
+ free((char *)g->sets);
+ if (g->setbits != NULL)
+ free((char *)g->setbits);
+ if (g->must != NULL)
+ free(g->must);
+ free((char *)g);
+}
diff --git a/regex/utils.h b/regex/utils.h
new file mode 100644
index 00000000..efe9836c
--- /dev/null
+++ b/regex/utils.h
@@ -0,0 +1,60 @@
+/*-
+ * Copyright (c) 1992, 1993, 1994 Henry Spencer.
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Henry Spencer.
+ *
+ * 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.
+ *
+ * @(#)utils.h 8.3 (Berkeley) 3/20/94
+ */
+
+/* utility definitions */
+#ifndef _POSIX2_RE_DUP_MAX
+#define _POSIX2_RE_DUP_MAX 255
+#endif
+#define DUPMAX _POSIX2_RE_DUP_MAX /* xxx is this right? */
+#define INFINITY (DUPMAX + 1)
+#define NC (CHAR_MAX - CHAR_MIN + 1)
+typedef unsigned char uch;
+
+/* switch off assertions (if not already off) if no REDEBUG */
+#ifndef REDEBUG
+#ifndef NDEBUG
+#define NDEBUG /* no assertions please */
+#endif
+#endif
+#include <assert.h>
+
+/* for old systems with bcopy() but no memmove() */
+#ifdef USEBCOPY
+#define memmove(d, s, c) bcopy(s, d, c)
+#endif
diff --git a/web/INSTALL b/web/INSTALL
new file mode 100644
index 00000000..62ce69d6
--- /dev/null
+++ b/web/INSTALL
@@ -0,0 +1,361 @@
+alpine.tar.z web/INSTALL
+/* ========================================================================
+ * Copyright 2006-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+BUILDING AND INSTALLING WEB ALPINE
+----------------------------------
+
+This file provides brief instructions for building, installing and
+configuring the Web Alpine application
+
+Web Alpine's binary components are built along with the other Alpine
+Mail System components. If the build process completed, that is the
+commands ./configure and make completed without error, then you are
+nearly good to go.
+
+Unlike the other Alpine components, however, Web Alpine does not use
+the "make install" method of installation. Between the various Web
+Alpine pieces, web site layout and web server configuration,
+variability and administrative preference is too great to be reliably
+automated at this time.
+
+For more information on the how's and why's of Web Alpine consult the
+somewhat more technically complete treatment in
+web/cgi/alpine/help/tech-notes.html.
+
+At some point online FAQs and such may be available. If you find
+anything missing, incomplete, or otherwise unclear please send a note
+to <alpine-contact@cac.washington.edu>.
+
+
+WEB ALPINE LAYOUT
+-----------------
+
+The Web Alpine package is distributed as part of the Alpine Mail System.
+The source for the various components can be found in the "web/"
+directory arranged, for the most part, by function.
+
+ src/
+ alpined.d/
+ source for Web Alpine's binary components: the
+ per-user/per-session serverette and the small library used for
+ inter-tcl script communication.
+
+ pubcookie/
+ sources for various components required to provide
+ pubcookie web-login support
+
+ cgi.tcl-1.10/
+ Tcl library used to help coordinate web page generation
+
+ cgi/
+ CGI scripts used to generate Web Alpine pages, typically synonymous
+ with the web server's document root. It, in turn, contains:
+
+ alpine/
+ Meat and potatoes of the Web Alpine Application.
+
+ alpine-2.0/
+ Meat and potatoes of the Web Alpine 2.0 Application
+
+ session/
+ Alpine session management scripts used to login, establish
+ an alpine session, logout and aquire IMAP server credentials
+ as needed. These scripts are distinct from the alpine/
+ scripts in order to properly scope the session key.
+
+ images/
+ Various images and icons
+
+ pub/
+ Scripts that are accessed outside the scope of the Web Alpine
+ session key.
+
+ sounds/
+ Sounds files that might be referenced by Web Alpine
+
+ config/
+ general Web Alpine and default host configurations
+
+ bin/
+ binary executables providing services to the CGI scripts
+
+ lib/
+ binary and script routines used by both CGI scripts
+ and binary utilities
+
+For a more thorough discussion of the distribution's layout and
+Web Alpine components see cgi/alpine/help/tech-notes.html.
+
+
+BUILDING WEB ALPINE'S BINARY COMPONENTS
+---------------------------------------
+
+For the most part, Web Alpine's binary components were built
+automatically along with the rest of the Alpine Mail System.
+
+If configure reports that it could not locate suitable TCL libraries
+and header files, then it is likely that the components necessary for
+Web Alpine were not built. Locating and installing a TCL development
+environment appropriate for your system should get the build back on
+track. Note, even though a tclsh interpreter may be available on the
+command line, tools necessary to build TCL applications may need to be
+installed separately.
+
+If you plan to use UW pubcookie for browser-based network login,
+please review src/pubcookie/README. Be sure the Web Alpine Mail
+System was configured with the "--with-pubcookie" AND --with-web-bin=
+options set. The latter is set to the directory that will eventually
+contain Web Alpine's binary components. For the example system
+described in the next section, you would add:
+
+ --with-pubcookie --with-web-bin=/usr/local/libexec/alpine/bin
+
+to the configure script's command line.
+
+
+ACQUIRING EXTERNAL LIBRARIES
+----------------------------
+
+Web Alpine 2.0 makes heavy use of the functionality provided by the
+The Yahoo! User Interface Library (YUI). By default, Web Alpine is
+configured to generate pages that cause user's browser to request the
+necessary library files directly from Yahoo servers.
+
+Web Alpine can be easily configured to generate pages with references
+to a local copy of the YUI libraries.
+
+First, you will need to download the YUI libararies from:
+
+ http://developer.yahoo.com/yui/download/
+
+They are made available to Web Alpine 2.0 scripts thru the symbolic
+link:
+
+ web/cgi/alpine-2.0/lib/yui
+
+Simply install the downloaded library in the directory specified by
+the symbolic link, or change the link to refer to the intalled
+location.
+
+Second, you will need to change the _wp(yui) configuration setting
+in
+
+ web/config/alpine.tcl
+
+to reference the new location.
+
+
+INSTALLING WEB ALPINE COMPONENTS
+--------------------------------
+
+Unfortunately, due to the variety of web server requirements and
+configurations, Web Alpine installation must be done by hand and
+requires several steps. To illustrate the procedure, a generic Fedora
+Core 8 system with standard httpd package installed is used as an
+example. On other systems, the general ideas are the same but the
+specific file locations and server configuation settings will likely
+vary. Note also that your system may have an additional security
+layer installed, such as selinux, that may require extra configuration
+that is beyond the scope of this explanation.
+
+The first step is to build and configure the tools Web Alpine needs to
+generate pages and access mail data. The following commands will put
+those tools where they need to be within the web/ directory structure.
+
+ 1. % cd web/src
+
+ 2. % make
+
+ 3. % make install
+
+Second, the web/ directory tree needs to be made available to the web
+server. On the example system, start by moving the web/ directory
+tree into a more system-visible location. We'll also change the name
+to reflect the current version number (for this example, 1.00) to help
+keep future upgrades isolated. This command will likely require
+elevated privileges using either sudo or after becoming root.
+
+ 4. % cd ../..
+
+ 5. % sudo mv web /usr/local/libexec/alpine-2.00
+
+Next, for simplicity, create a generically named symbolic link as a
+synonym for the version-specific directory.
+
+ 6. % cd /usr/local/libexec
+
+ 7. % sudo ln -s alpine-2.00 alpine
+
+After that, make the scripts that actually generate the user visible
+portion of Web Alpine available to the web server.
+
+ 8. % cd /var/www
+
+ 9. % sudo ln -s /usr/local/libexec/alpine/cgi ./alpine
+
+Now adjust the web server's configuration so that it can effectively
+provide Web Alpine pages to connecting browsers by editing httpd's
+configuration file.
+
+ 10. % sudo vi /etc/httpd/conf/httpd.conf
+
+ After the section that starts with <Directory /var/www/html> and ends
+ with </Directory>, add the lines:
+
+ #
+ # This sets up Web Alpine
+ #
+ <Directory "/var/www/alpine">
+ Options FollowSymLinks ExecCGI -Indexes
+ AllowOverride All
+ Order allow,deny
+ Allow from all
+ </Directory>
+
+If you intend for your web server to provide Web Alpine pages
+exclusively, then simply edit the DocumentRoot to the directory
+defined above:
+
+ DocumentRoot /var/www/alpine
+
+If your web server offers pages other than Web Alpine, specify a
+prefix the web server should use for referencing Web Alpine pages by
+adding this line before the <Directory> entry specified above:
+
+ Alias /webmail/ "/var/www/alpine/"
+
+After saving httpd.conf with these small additions, it's time to
+adjust Web Alpine's configuration.
+
+First, be sure the symbolic link "/usr/local/libexec/alpine/bin/tclsh"
+points to the tclsh interpreter for your system. The default should
+work for the example system.
+
+Then edit the Web Alpine configuration file to configure appropriate
+settings for your environment.
+
+ 11. % sudo vi /usr/local/libexec/alpine/config/alpine.tcl
+
+ The config file is itself a Tcl script, and the settings are
+ simply Tcl variable settings. Most are settings of elements
+ within the "_wp" array.
+
+ Starting from the top, skim the various configuration
+ settings. The primary one's to be aware of include:
+
+ admin email address offered in error pages
+ associated with problems that likely
+ require system administrator attention
+
+ helpdesk email address offered in help pages and
+ some error pages as a place to report
+ problems or get more information
+
+ comments email address offered in help pages
+ as place to send general comments on
+ web alpine
+
+ urlprefix directory or path defined in the httpd.conf's
+ "Alias" setting. In example above, set this to
+ "webmail". If DocumentRoot set as above, set this
+ to {}.
+
+ fileroot file system path to directory that contains cgi/,
+ config/, bin/, and lib/ directories. In example
+ above, set this to /usr/local/libexec/alpine.
+
+ Continue scanning the list, and adjust as needed. Most defaults
+ should be fine. Until you come to:
+
+ ispell full path to ispell application if installed
+
+ ssl_safe_domains
+ a performance setting that allows for relatively
+ safe disabling of SSL for connections that we know
+ are reasonably safe from sniffing. For our campus
+ web alpine installation, browsers associated with the
+ campus dial-in pools connecting to our servers
+ offer this kind of connection. Be careful.
+
+ flexserver
+ determines whether or not web alpine offers the option
+ of connecting to a user-defined IMAP server on
+ the greeting page.
+
+ hosts an array of default configurations that
+ correspond to default web alpine config files
+ in the config/ directory. these are what
+ is offered on the greeting page as the option
+ list of servers to connect to.
+
+ And, probably lastly:
+
+ cgi_mail_relay
+ the server used to send out script errors that are
+ so heinous that no web page error could be generated
+
+
+The final step is to restart httpd and give it a try! Using a browser
+pointed to your server's https port, try connecting to the alpine/
+directory.
+
+If you run into problems, rest assured you have our sympathies.
+Because of the various components that must be coordinated, errors can
+be difficult to resolve. The good news is, once initially configured
+and working the system is reasonably stable. As for debugging, with
+luck, the error response reported in the browser will point in a
+useful direction. If not, check httpd access and error logs to verify
+paths and check for exceptional conditions. Next, check syslog's
+maillog for any exceptional reports issued by the alpined serverette.
+Depending on the type of error, you may also have to consult the IMAP
+server's logs for clues.
+
+
+COLLECTED GOTCHAS AND SO FORTH
+------------------------------
+
+First, it is strongly encourage that Web Alpine be run on a web server
+that does not have general user accounts. The primary reason is to
+maintain the privacy of the Web Alpine session key. Steps are taken
+to minimize the risk and consequences of session key exposure, but
+there are risks nonetheless.
+
+For the most part, the default Web Alpine application settings should
+require little adjustment for your particular environment. These
+settings are in the web/config/pine.conf file which uses the same
+format as alpine's pinerc file. The most likely setting to adjust is
+"smtp-server."
+
+By default, Web Alpine sends via SMTP to the localhost's SMTP port.
+This setting can easily be adjusted by setting the smtp-server in
+web/config/pine.conf to one or more external servers. Web Alpine can
+also be directed to post to a local process by setting the
+sendmail-path variable. Be aware, however, posting to a local process
+(e.g., sendmail, postfix, etc), will likely require you to grant
+trusted mail user privilege to the userid associated with the web
+server process. Without such privilege, the SMTP envelope From will
+be set to the web server's userid which causes all externally bounced
+mail to be returned to the mailbox associated with the web server
+userid.
+
+
+FURTHER INFORMATION
+-------------------
+
+See the Web Alpine technical notes for more detailed descriptions of
+what's going on and why. If you have any questions or comments drop
+us a note <alpine-contact@cac.washington.edu>.
+
+--
+$Id: INSTALL 1169 2008-08-27 06:42:06Z hubert@u.washington.edu $
diff --git a/web/README b/web/README
new file mode 100644
index 00000000..64d19168
--- /dev/null
+++ b/web/README
@@ -0,0 +1,81 @@
+/* ========================================================================
+ * Copyright 2006-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+WEB ALPINE
+----------
+
+Web Alpine is the web-based component of the Alpine Mail System. It
+rests on the same distributed email technology behind Alpine, but is
+accessible from almost any browser on the World Wide Web.
+
+Web Alpine uses TCL to provide the programmatic interface between
+the Alpine-provided message store and the cgi-served web pages.
+
+
+REQUIRED SOFTWARE
+-----------------
+
+ Base system:
+ C compiler
+ Tcl scripting package and development tools
+ OpenSSL
+ Apache web server (though, presumably any CGI supporting
+ web server should work)
+
+ Optional:
+ ispell application
+ Used to support composer spell check functinoality.
+ Featured not offered if ispell not present.
+
+ openLDAP
+ Provides LDAP query services to support login
+ personalization and user-session LDAP query support.
+
+ UW Pubcookie
+ Provides central web-login management. Note, additional
+ configuration and installation steps are required. See
+ src/pubcookie/INSTALL for specific instructions.
+
+ Kerberos 5
+ Required by UW-Pubcookie to provide authentication and
+ proxy authorization for IMAP server access. Proxy
+ authentication only tested under UW imapd.
+
+
+
+BUILING AND INSTALLING WEB ALPINE
+---------------------------------
+
+See the INSTALL file for basic instructions.
+
+
+DOCUMENTATION
+-------------
+
+User documentation is contained within the application and is
+reasonably complete. Technical documentation is extremely rough and
+likely unsatisfying. It's probably worth reading, but ultimately
+configuration or other questions are best answered by reviewing the
+source (which itself, while working reasonably, is somewhat spotilly
+documented as well).
+
+
+SUPPORT AND FEEDBACK
+--------------------
+
+Send comments and questions to <alpine-contact@cac.washington.edu>.
+
+
+--
+alpine.tar.z web/README
+$Id: README 890 2007-12-21 05:34:43Z hubert@u.washington.edu $
diff --git a/web/bin/README b/web/bin/README
new file mode 100644
index 00000000..a6f5fa90
--- /dev/null
+++ b/web/bin/README
@@ -0,0 +1,20 @@
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+
+ this directory holds binaries for use by the web alpine cgi.
+ the alpined is the serverlet that provides mailbox
+ data and session management to the web alpine scripts.
+
+ make sure alpined resides in this directory.
+
+ make sure tclsh points to the tclsh binary for your system.
+
diff --git a/web/bin/alpine.tcl b/web/bin/alpine.tcl
new file mode 120000
index 00000000..976166f1
--- /dev/null
+++ b/web/bin/alpine.tcl
@@ -0,0 +1 @@
+../config/alpine.tcl \ No newline at end of file
diff --git a/web/bin/launch.tcl b/web/bin/launch.tcl
new file mode 100755
index 00000000..8751c33f
--- /dev/null
+++ b/web/bin/launch.tcl
@@ -0,0 +1,51 @@
+#!./tclsh
+# $Id: launch.tcl 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# Generate a session key, create the connection points (fifos),
+# spawn the mail engine and then hand the session key to the
+# caller
+
+# Source config information
+source ./alpine.tcl
+
+# generate session id
+WPValidId
+
+if {[info exists env(REMOTE_USER)]} {
+ set servlet $_wp(pc_servlet)
+ set env(LOGNAME) $env(REMOTE_USER)
+} else {
+ set servlet $_wp(servlet)
+}
+
+set cmd "exec -- echo $_wp(sockname) | [file join $_wp(bin) $servlet]"
+
+# set debug level and configure dmalloc
+#append cmd " -d -d -d -d -d -d -d"
+#set env(DMALLOC_OPTIONS) "check-fence,check-heap,check-blank,log=/tmp/logfile.%d"
+
+if {[catch {eval $cmd} errmsg]} {
+ puts stderr "Unable to Launch servlet: $errmsg"
+ exit 1
+} elseif {[info exists env(REMOTE_ADDR)] && [string length $env(REMOTE_ADDR)]} {
+ catch {WPCmd PEInfo set wp_client $env(REMOTE_ADDR)}
+} elseif {[info exists env(REMOTE_HOST)] && [string length $env(REMOTE_HOST)]} {
+ catch {WPCmd PEInfo set wp_client $env(REMOTE_HOST)}
+}
+
+if {$_wp(debug) > 0} {
+ WPCmd PEDebug level $_wp(debug)
+}
+
+puts $_wp(sessid)
+exit 0
diff --git a/web/bin/tclsh b/web/bin/tclsh
new file mode 120000
index 00000000..89acb765
--- /dev/null
+++ b/web/bin/tclsh
@@ -0,0 +1 @@
+/usr/bin/tclsh \ No newline at end of file
diff --git a/web/bin/usage.tcl b/web/bin/usage.tcl
new file mode 100755
index 00000000..8b597951
--- /dev/null
+++ b/web/bin/usage.tcl
@@ -0,0 +1,24 @@
+#!./tclsh
+# $Id: usage.tcl 1169 2008-08-27 06:42:06Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# Return mail store usage numbers on stdout separated by a space
+# First number is amount of usage
+# Second is total amount of space available
+# Integer values. Unit are megabytes (MB).
+
+set cmd "exec -- /usr/local/bin/dmq -u [lindex $argv 0]"
+if {0 == [catch {eval $cmd} result]} {
+ if {[regexp {^[0-9]+[ \t]+([0-9]+)\.[0-9]*[ \t]+([0-9]+)$} $result dummy usage total]} {
+ puts stdout "$usage $total"
+ }
+}
diff --git a/web/cgi/.htaccess b/web/cgi/.htaccess
new file mode 100644
index 00000000..b59de05b
--- /dev/null
+++ b/web/cgi/.htaccess
@@ -0,0 +1,32 @@
+#
+# Tweek server to encourage caching and set appropriate type for icons
+#
+DirectoryIndex greeting.tcl
+
+AddType image/x-icon .ico
+
+AddHandler cgi-script tcl
+
+<IfModule mod_headers.c>
+
+<Files *.gif>
+Header append Cache-Control "public"
+</Files>
+
+<Files *.jpg>
+Header append Cache-Control "public"
+</Files>
+
+<Files *.jpeg>
+Header append Cache-Control "public"
+</Files>
+
+<Files *.js>
+Header append Cache-Control "public"
+</Files>
+
+<Files *.css>
+Header append Cache-Control "public"
+</Files>
+
+</IfModule>
diff --git a/web/cgi/alpine.tcl b/web/cgi/alpine.tcl
new file mode 120000
index 00000000..976166f1
--- /dev/null
+++ b/web/cgi/alpine.tcl
@@ -0,0 +1 @@
+../config/alpine.tcl \ No newline at end of file
diff --git a/web/cgi/alpine/1.0/addrbook.tcl b/web/cgi/alpine/1.0/addrbook.tcl
new file mode 100755
index 00000000..fcaf4f04
--- /dev/null
+++ b/web/cgi/alpine/1.0/addrbook.tcl
@@ -0,0 +1,583 @@
+# $Id: addrbook.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# addrbook.tcl
+#
+# Purpose: CGI script to generate html output associated with address
+# book entry and collection management
+#
+# Input:
+set abook_vars {
+ {op {} "view"}
+ {field {} "none"}
+ {uid "" 0}
+ {oncancel "" "fr_main.tcl"}
+ {reload}
+}
+
+# Output:
+#
+# HTML/CSS data representing the address book
+
+
+# Command Menu definition for Message View Screen
+set addr_menu {
+}
+
+set common_menu {
+ {
+ {expr {$view}}
+ {
+ {
+ # * * * * Ubiquitous INBOX link * * * *
+ if {[string compare inbox [string tolower [WPCmd PEMailbox mailboxname]]]} {
+ cgi_put [cgi_url INBOX open.tcl?folder=INBOX&colid=0&cid=[WPCmd PEInfo key] target=_top class=navbar]
+ } else {
+ cgi_put [cgi_url INBOX fr_main.tcl target=_top class=navbar]
+ }
+ }
+ }
+ }
+ {
+ {expr {$view}}
+ {
+ {
+ # * * * * FOLDER LIST * * * *
+ cgi_puts [cgi_url "Folder List" "wp.tcl?page=folders&cid=[WPCmd PEInfo key]" target=_top class=navbar]
+ }
+ }
+ }
+ {
+ {expr {$view}}
+ {
+ {
+ # * * * * COMPOSE * * * *
+ cgi_puts [cgi_url Compose wp.tcl?page=compose&oncancel=addrbook&cid=[WPCmd PEInfo key] target=_top class=navbar]
+ }
+ }
+ }
+ {
+ {expr {$view}}
+ {
+ {
+ # * * * * RESUME * * * *
+ cgi_puts [cgi_url Resume wp.tcl?page=resume&oncancel=addrbook&cid=[WPCmd PEInfo key] class=navbar]
+ }
+ }
+ }
+ {
+ {expr {$browse}}
+ {
+ {
+ # * * * * USE ADDRESSES * * * *
+ cgi_submit_button "address=Address" class="navtext"
+ }
+ }
+ }
+ {
+ {expr {$browse}}
+ {
+ {
+ # * * * * CANCEL * * * *
+ cgi_submit_button "cancel=Cancel" class="navtext"
+ }
+ }
+ }
+ {
+ {expr {0 && $browse}}
+ {
+ {
+ # * * * * Address/Cancel * * * *
+ cgi_submit_button doit=Done class="navbar"
+ cgi_br
+ cgi_select addrop class=navtext {
+ cgi_option "Action..." value=null
+ cgi_option Address value=address
+ cgi_option Cancel value=cancel
+ }
+ }
+ }
+ }
+}
+
+
+## read vars
+foreach item $abook_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} errstr]} {
+ error [list _action "Impart Variable" $errstr]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+# perform any requested actions
+
+# preserve vars that my have been overridden with cgi parms
+
+set cid [WPCmd PEInfo key]
+WPCmd PEAddress safecheck
+
+set view [expr {[string compare $op "view"] == 0}]
+set browse [expr {[string compare $op "browse"] == 0}]
+
+if {$view} {
+ if {[catch {WPNewMail $reload} newmail]} {
+ error [list _action "new mail" $newmail]
+ }
+
+ if {[WPCmd PEInfo ldapenabled] == 1} {
+ if {[catch {WPCmd PELdap directories} directories] || [llength $directories] <= 0} {
+ catch {unset directories}
+ } else {
+ for {set i 0} {$i < [llength $directories]} {incr i} {
+ lappend exclusions document.ldapsearch${i}.srchstr
+ }
+ }
+ }
+}
+
+# paint the page
+cgi_http_head {
+ WPStdHttpHdrs
+}
+
+cgi_html {
+ cgi_head {
+ set onload "onLoad="
+ set onunload "onUnload="
+
+ if {[info exists _wp(exitonclose)] && $view} {
+ WPExitOnClose
+ append onload "wpLoad();"
+ append onunload "wpUnLoad();"
+ }
+
+ if {$view} {
+ set normalreload [cgi_buffer {WPHtmlHdrReload "[file join $_wp(appdir) $_wp(ui1dir) wp.tcl?page=addrbook]"}]
+ if {[info exists _wp(exitonclose)]} {
+ WPStdHtmlHdr "Address Book View"
+ cgi_script type="text/javascript" language="JavaScript" {
+ cgi_put "function indexReloadTimer(t){"
+ cgi_put " reloadtimer = window.setInterval('wpLink(); window.location.replace(\\'[cgi_root]/$_wp(appdir)/$_wp(ui1dir)/wp.tcl?page=addrbook&reload=1\\')', t * 1000);"
+ cgi_puts "}"
+ }
+
+ append onload "indexReloadTimer($_wp(refresh));"
+
+ cgi_noscript {
+ cgi_puts $normalreload
+ }
+ } else {
+ cgi_puts $normalreload
+ }
+ }
+
+ WPStyleSheets
+ cgi_puts "<style type='text/css'>"
+ if {$browse} {
+ cgi_puts ".navbtn { color: white ; font-family: geneva, arial, sans-serif ; font-size: 9pt ; letter-spacing: 0pt ; text-decoration: underline ; background: transparent; border: 0 ; text-align: left }"
+ } elseif {$view} {
+ cgi_puts ".gradient { background-image: url('[WPimg indexhdr]') ; background-repeat: repeat-x }"
+ }
+ cgi_puts "</style>"
+
+ if {$_wp(keybindings)} {
+ set kequiv {
+ {{i} {top.location = 'fr_main.tcl'}}
+ {{l} {top.location = 'wp.tcl?page=folders'}}
+ {{?} {top.location = 'wp.tcl?page=help&oncancel=addrbook'}}
+ }
+
+ lappend kequiv [list {c} "top.location = 'wp.tcl?page=compose&oncancel=addrbook&cid=$cid'"]
+
+ if {![info exists exclusions]} {
+ set exclusions ""
+ }
+
+ append onload [WPTFKeyEquiv $kequiv $exclusions]
+ }
+ }
+
+ cgi_body bgcolor=$_wp(bordercolor) background=[file join $_wp(imagepath) logo $_wp(logodir) back.gif] "style=\"background-repeat: repeat-x\"" $onload $onunload {
+
+ if {$view} {
+ catch {WPCmd PEInfo set help_context addrbook}
+ } else {
+ catch {WPCmd PEInfo set help_context addrbrowse}
+ }
+
+ set books [WPCmd PEAddress books]
+ set entrylist {}
+ set entryexists 0
+ foreach book $books {
+ set entries [WPCmd PEAddress list [lindex $book 0]]
+ if {[llength $entries] > 0} {
+ incr entryexists
+ }
+ lappend entrylist $entries
+ }
+
+ if {$view} {
+ WPTFTitle "Address Books" $newmail 0 addrbook
+ }
+
+ cgi_table border=0 cellspacing=0 cellpadding=0 width="100%" height="100%" {
+ if {$browse} {
+ cgi_puts "<form action=\"[cgi_root]/$_wp(appdir)/$_wp(ui1dir)/wp.tcl\" name=addrchoice method=get target=_top>"
+ cgi_text "page=addrpick" type=hidden notab
+ cgi_text "field=$field" type=hidden notab
+ cgi_text "restore=1" type=hidden notab
+ cgi_text "cid=[WPCmd PEInfo key]" type=hidden notab
+ }
+ cgi_table_row {
+ #
+ # next comes the menu down the left side
+ #
+ cgi_table_data valign=top rowspan=4 class=navbar {
+ cgi_table bgcolor=$_wp(menucolor) border=0 cellspacing=0 cellpadding=0 width=112 {
+ if {!$browse} {
+ cgi_table_row {
+ cgi_table_data class=navbar "style=\"padding: 6 0 6 4\"" {
+ cgi_puts [cgi_span "style=font-weight: bold" "Current Folder"]
+ cgi_division align=center "style=\"margin-top:4;margin-bottom:4\"" {
+ set mbn [WPCmd PEMailbox mailboxname]
+ if {[string length $mbn] > 16} {
+ set mbn "[string range $mbn 0 14]..."
+ }
+
+ cgi_put [cgi_url $mbn fr_main.tcl target=_top class=navbar]
+ switch -exact -- [WPCmd PEMailbox state] {
+ readonly {
+ cgi_br
+ #cgi_put [cgi_span "style=color: black; border: 1px solid red; background-color: pink; font-weight: bold" "Read Only"]
+ cgi_put [cgi_span "style=color: pink; font-weight: bold" "(Read Only)"]
+ }
+ closed {
+ cgi_br
+ #cgi_put [cgi_span "style=color: black; border: 1px solid red; background-color: pink; font-weight: bold" "Closed"]
+ cgi_put [cgi_span "style=color: pink; font-weight: bold" "(Closed)"]
+ }
+ ok -
+ default {}
+ }
+
+ cgi_br
+ }
+
+ cgi_hr "width=75%"
+ }
+ }
+ }
+
+ if {$view && [llength $books] == 1 && [lindex [lindex $books 0] 3]} {
+ cgi_table_row {
+ cgi_table_data class=navbar "style=\"padding-left: 4\"" {
+ # * * * * ADD ENTRY * * * *
+ cgi_puts [cgi_url "Add Entry" wp.tcl?page=addredit&add=1&book=0&cid=[WPCmd PEInfo key] class=navbar]
+ }
+ }
+ cgi_table_row {
+ cgi_table_data class=navbar {
+ cgi_put [cgi_nbspace]
+ }
+ }
+ }
+
+ # next comes the menu down the left side, with suitable
+ cgi_table_row {
+ if {$view} {
+ lappend common_menu {{cgi_put [cgi_nbspace]}}
+ lappend common_menu [list {} [list {cgi_puts [cgi_url "Configure" wp.tcl?page=conf_process&newconf=1&oncancel=addrbook&cid=[WPCmd PEInfo key] class=navbar target=_top]}]]
+ lappend common_menu [list {} [list {cgi_puts [cgi_url "Get Help" wp.tcl?page=help&oncancel=addrbook target=_top class=navbar]}]]
+ lappend common_menu {{cgi_put [cgi_nbspace]}}
+
+ if {[WPCmd PEInfo feature quit-without-confirm]} {
+ lappend common_menu [list {} [list {cgi_puts [cgi_url "Quit $_wp(appname)" $_wp(serverpath)/session/logout.tcl?cid=[WPCmd PEInfo key]&sessid=$sessid class=navbar]}]]
+ } else {
+ lappend common_menu [list {} [list {cgi_puts [cgi_url "Quit $_wp(appname)" wp.tcl?page=quit&cid=[WPCmd PEInfo key] target=_top class=navbar]}]]
+ }
+ }
+
+
+ eval {
+ cgi_table_data $_wp(menuargs) class=navbar "style=\"padding: 0 0 10 2\"" {
+ WPTFCommandMenu addr_menu common_menu
+ }
+ }
+ }
+ }
+ }
+
+ cgi_table_data valign=top width=100% class=dialog {
+ set n 1
+ set bookno 0
+ foreach book $books {
+ cgi_table border=0 cellspacing=0 cellpadding=0 width="100%" {
+ if {$browse} {
+ cgi_table_row {
+ cgi_table_data width="100%" colspan=16 class=dialog align=center valign=middle {
+ cgi_table cellpadding=4 width="70%" {
+ cgi_table_row {
+ cgi_table_data align=center {
+ cgi_puts "Check the box next to the addresses you'd like to add to the [cgi_bold "[string toupper [string range $field 0 0]][string range $field 1 end]:"] field, then click [cgi_italic Address] button."
+ }
+ }
+ }
+ }
+ }
+ }
+
+ set format [WPCmd PEAddress format [lindex $book 0]]
+ set name [lindex $book 1]
+ set readwrite [lindex $book 3]
+ if {[llength $books] > 1} {
+ if {[string index $name 0] == "\{"} {
+ if {$readwrite} {
+ set name "Remote Address Book"
+ } else {
+ set name "Global Address Book"
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data height=35 valign=middle colspan=[expr ([llength $format] + 1) * 2] nowrap {
+ if {$readwrite || $browse} {
+ cgi_puts "[cgi_font size=+1 [cgi_bold $name]]"
+ } else {
+ cgi_table border=0 cellspacing=0 cellpadding=0 width=100% {
+ cgi_table_row {
+ cgi_table_data align=left {
+ cgi_puts "[cgi_font size=+1 [cgi_bold $name]]"
+ }
+ cgi_table_data align=right {
+ cgi_puts "[cgi_bold "(Read Only)"]"
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if {$view && $readwrite} {
+ cgi_table_row {
+ cgi_table_data nowrap height=40 {
+ cgi_put [cgi_url "Add New Entry" wp.tcl?page=addredit&add=1&book=${bookno}&cid=[WPCmd PEInfo key]]
+ }
+ }
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data {
+ cgi_table border=0 cellspacing=0 cellpadding=0 width="100%" {
+ set linenum 1
+ set nonick 1
+ set nfields [llength $format]
+
+ if {$view} {
+ cgi_table_row class=\"gradient\" {
+ foreach f $format {
+ cgi_table_data {
+ cgi_put [cgi_img [WPimg dot2] border=0 width=3 height=24]
+ }
+ cgi_table_data align=left class=indexhdr {
+ switch [lindex $f 0] {
+ nick {
+ cgi_put "Nickname (Click to edit entry)"
+ }
+ full {
+ cgi_put "Full Name"
+ }
+ addr {
+ cgi_put "Address (Click to Compose To)"
+ }
+ }
+ }
+ }
+ }
+ }
+
+ for {set i 0} {$i < $nfields && $nonick} {incr i} {
+ if {[string compare "nick" [lindex [lindex $format $i] 0]] == 0} {
+ set nonick 0
+ }
+ }
+ set entries [lindex $entrylist [lindex $book 0]]
+ if {[llength $entries] == 0} {
+ cgi_table_row {
+ cgi_table_data colspan=16 align=center {
+ cgi_puts [cgi_italic "This address book is currently empty."]
+ }
+ }
+ }
+ set aindex 0
+ foreach entry $entries {
+ if {[incr linenum] % 2} {
+ set bgcolor #ffffff
+ } else {
+ set bgcolor #eeeeee
+ }
+ cgi_table_row bgcolor=$bgcolor {
+ set nick [lindex [lindex $entry 0] 0]
+ set safenick [WPPercentQuote $nick]
+
+ set nfields [llength $format]
+ if {$browse} {
+ cgi_table_data valign=top nowrap {
+ cgi_checkbox "nickList=[lindex $book 0].$aindex.[lindex [lindex $entry 0] 0]" style="background-color:$bgcolor"
+ }
+ }
+ for {set i 0} {$i < $nfields} {incr i} {
+ set field [lindex $format $i]
+ set data [lindex $entry [expr $i + 1]]
+
+ if {$view} {
+ cgi_table_data nowrap {
+ if {$nonick && $i == 0} {
+ set data [cgi_url $data "wp.tcl?page=addredit&nick=${safenick}&book=${bookno}&ai=${aindex}"]
+ }
+ cgi_puts [cgi_nbspace][cgi_nbspace][cgi_nbspace][cgi_nbspace]
+ }
+ }
+
+ cgi_table_data valign=top nowrap {
+ switch -- [lindex $field 0] {
+ addr {
+ switch -- [lindex [lindex $entry 0] 1] {
+ single {
+ regsub -all "<" $data "\\&lt;" data
+ regsub -all ">" $data "\\&gt;" data
+ if {$view} {
+ set data [cgi_url $data "wp.tcl?page=compose&nickto=${safenick}&book=${bookno}&ai=${aindex}&oncancel=addrbook&cid=[WPCmd PEInfo key]"]
+ }
+ cgi_puts [cgi_font size=-1 face=courier $data]
+ }
+ list {
+ cgi_table {
+ cgi_table_row {
+ cgi_table_data {
+ foreach addr $data {
+ regsub -all "<" $addr "\\&lt;" addr
+ regsub -all ">" $addr "\\&gt;" addr
+ if {$view} {
+ set addr [cgi_url $addr "wp.tcl?page=compose&nickto=${safenick}&book=${bookno}&ai=${aindex}&oncancel=addrbook&cid=[WPCmd PEInfo key]"]
+ }
+ cgi_puts [cgi_font size=-1 face=courier $addr]
+ cgi_br
+ }
+ if {$view} {
+ cgi_puts "</a>"
+ }
+ }
+ }
+ }
+ }
+ default {
+ cgi_puts "Unknown entry type"
+ }
+ }
+ }
+ nick {
+ regsub -all "<" $data "\\&lt;" data
+ regsub -all ">" $data "\\&gt;" data
+ if {$view} {
+ if {[string length $data]} {
+ set text "$data"
+ } else {
+ set text "\[Edit\]"
+ }
+
+ cgi_puts [cgi_url $text "wp.tcl?page=addredit&nick=${safenick}&book=${bookno}&ai=${aindex}"]
+ } else {
+ if {![string length $nick]} {
+ set nick [cgi_nbspace]
+ }
+
+ cgi_puts "$nick"
+ }
+ }
+ default {
+ regsub -all "<" $data "\\&lt;" data
+ regsub -all ">" $data "\\&gt;" data
+ if {[string compare "$data" ""] == 0} {
+ cgi_puts "&nbsp;"
+ } else {
+ cgi_puts "$data"
+ }
+ }
+ }
+ }
+ }
+ }
+ incr aindex
+ }
+ }
+ }
+ }
+ }
+ incr bookno
+ }
+ if {$browse} {
+ cgi_puts "</form>"
+ }
+
+ if {[info exists directories]} {
+ cgi_table border=0 cellspacing=0 cellpadding=4 width="100%" "style=\"padding-top:10\"" {
+ for {set i 0} {$i < [llength $directories]} {incr i} {
+ set directory [lindex $directories $i]
+ set nick [lindex $directory 0]
+ set server [lindex $directory 1]
+ if {[string length $nick]} {
+ set ref $nick
+ } elseif {[string length $server]} {
+ set ref "<$server>"
+ } else {
+ set ref "some server"
+ }
+
+ cgi_table_row {
+ cgi_table_data colspan=3 valign=middle nowrap {
+ cgi_puts "[cgi_font size=+1 [cgi_bold "Directory server [cgi_quote_html $ref]"]]"
+ }
+ }
+
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=post enctype=multipart/form-data name=ldapsearch${i} {
+ cgi_text "sessid=$_wp(sessid)" type=hidden notab
+ cgi_text "page=ldapquery" type=hidden notab
+
+ cgi_text "searchtype=0" type=hidden notab
+ cgi_text "op=view" type=hidden notab
+ cgi_text "dir=$i" type=hidden notab
+
+ cgi_table_row {
+ cgi_table_data class=dialog align=center valign=middle "style=\"background-color:white\"" {
+ cgi_puts "Search Directory :"
+ cgi_text "srchstr=" size=35
+ cgi_submit_button "search=Search"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ cgi_table_row {
+ cgi_table_data height=200 class=dialog {
+ cgi_puts [cgi_nbspace]
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/addredit.tcl b/web/cgi/alpine/1.0/addredit.tcl
new file mode 100755
index 00000000..8aecda2e
--- /dev/null
+++ b/web/cgi/alpine/1.0/addredit.tcl
@@ -0,0 +1,224 @@
+#!./tclsh
+# $Id: addredit.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# addredit.tcl
+#
+# Purpose: CGI script to generate html form used to view/set
+# individual addressbook entries
+#
+# Input:
+set ae_vars {
+ {book {} -1}
+ {nick {} ""}
+ {add {} 0}
+ {fn {} ""}
+ {addrs {} ""}
+ {fcc {} ""}
+ {comment {} ""}
+ {take {} 0}
+ {newnick {} ""}
+ {ai {} -1}
+}
+
+set ae_fields {
+ {0 "newnick" "Nickname<sup class=notice>*</sup>"}
+ {1 "fn" "Full Name&nbsp;"}
+ {2 "addrs" "Addresses<sup class=notice>*</sup>"}
+ {3 "fcc" "Fcc&nbsp;"}
+ {4 "comment" "Comments&nbsp;"}
+}
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+# Output:
+#
+#
+
+set ae_menu {
+ {
+ {}
+ {
+ {
+ cgi_image_button help=[WPimg help_trans] border=0 alt="Help"
+ }
+ }
+ }
+}
+
+WPEval $ae_vars {
+
+ if {$book < 0} {
+ if {[catch {WPCmd PEInfo set ae_help_state} ae_help_state] == 0} {
+ foreach v $ae_help_state {
+ eval set [lindex $v 0] [list [lindex $v 1]]
+ }
+
+ set addrinfo [list "$newnick" "$fn" [list "$addrs"] "$fcc" "$comment"]
+ WPCmd PEInfo unset ae_help_state
+ } else {
+ return [list _action "Web Alpine" "Unspecified Address Book"]
+ }
+ }
+
+ catch {WPCmd PEInfo unset ae_help_state}
+
+ if {![info exists addrinfo]} {
+ if {$take != 0} {
+ set addrinfo [list "$newnick" "$fn" [list "$addrs"] "$fcc" "$comment"]
+ } elseif {$add == 0} {
+ if {[catch {WPCmd PEAddress fullentry $book $nick $ai} addrinfo]} {
+ if {[string length $addrinfo]} {
+ set entryerror "Address Error: $addrinfo"
+ } else {
+ set entryerror "Nickname $nick does not exist"
+ }
+
+ set addrinfo [list "" "" [list ""] "" ""]
+ }
+ } else {
+ set addrinfo [list "" "" [list ""] "" ""]
+ }
+ }
+
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "Addressbook Update"
+ WPStyleSheets
+ }
+
+ if {$take == 1} {
+ set onload "onLoad=document.addredit.newnick.focus()"
+ } else {
+ set onload ""
+ }
+
+ cgi_body BGCOLOR="$_wp(bordercolor)" $onload {
+ set books [WPCmd PEAddress books]
+ set readwrite [lindex [lindex $books $book] 3]
+
+ catch {WPCmd PEInfo set help_context addredit}
+ cgi_table border=0 cellspacing=0 cellpadding=0 width="100%" height="100%" {
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=post enctype=multipart/form-data name=addredit target=_top {
+ cgi_table_row {
+ #
+ # next comes the menu down the left side
+ #
+ eval {
+ cgi_table_data $_wp(menuargs) rowspan=1000 {
+ WPTFCommandMenu ae_menu {}
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data valign=top width="100%" class=dialog {
+ if {[info exists entryerror]} {
+ cgi_division class=notice align=center {
+ cgi_puts $entryerror
+ }
+ }
+
+
+ if {$take == 1} {
+ lappend tmptxt "Edit the new entry below as neccessary (note, some are required). To create a list entry, simply add each desired address to the [cgi_italic Addresses] field separated by a comma."
+ lappend tmptxt "When finished, click [cgi_italic Save] to update your address book, or [cgi_italic Cancel] to return to the message view."
+ } elseif {$add == 1} {
+ lappend tmptxt "The address book entry editor is used to create a new address book entry. Fill in the fields as desired below (note, some are required). To create a list entry, simply add each desired address to the [cgi_italic Addresses] field separated by a comma."
+ lappend tmptxt "When finished, click [cgi_italic Save] to update your address book, or [cgi_italic Cancel] to return to your unchanged address book."
+ } elseif {$readwrite == 0} {
+ set tmptxt "These are the current settings for the selected entry"
+ } else {
+ lappend tmptxt "The address book entry editor is used to edit an existing address book entry. Edit the fields as desired below (note, some are required)."
+ lappend tmptxt " then click [cgi_italic Save] to update your address book, or [cgi_italic Cancel] to return to your unchanged address book."
+ }
+
+ cgi_table align=center width=75% cellpadding=10 border=0 {
+ foreach t $tmptxt {
+ cgi_table_row {
+ cgi_table_data align=center {
+ cgi_puts $t
+ }
+ }
+ }
+ }
+
+ cgi_table border=0 cellspacing=0 cellpadding=5 align=center {
+ cgi_text "page=addrsave" type=hidden notab
+
+ if {$take == 1} {
+ cgi_text "oncancel=main" type=hidden notab
+ cgi_text "take=1" type=hidden notab
+ } else {
+ cgi_text "oncancel=addrbook" type=hidden notab
+ }
+
+ cgi_text "book=$book" type=hidden notab
+ if {$add == 0} {cgi_text "nick=$nick" type=hidden notab}
+ if {$add != 0} {cgi_text "add=1" type=hidden notab}
+ cgi_text "ai=${ai}" type=hidden notab
+ cgi_text "cid=[WPCmd PEInfo key]" type=hidden notab
+
+ foreach fieldval $ae_fields {
+ cgi_table_row {
+ cgi_table_data valign=top align=right width="30%" class=dialog {
+ # cgi_puts [cgi_font face=tahoma,verdana,geneva size=+1 "[lindex $fieldval 2]:"]
+ cgi_puts [cgi_bold "[lindex $fieldval 2]:"]
+ }
+ cgi_table_data align=left {
+ switch -regexp [lindex $fieldval 1] {
+ ^addrs$ {
+ set addrvals [lindex $addrinfo [lindex $fieldval 0]]
+ set line [join $addrvals ", "]
+ cgi_text "[lindex $fieldval 1]=${line}" size=50
+ }
+ default {
+ cgi_text "[lindex $fieldval 1]=[lindex $addrinfo [lindex $fieldval 0]]" size=50
+ }
+ }
+ }
+ }
+ }
+ cgi_table_row {
+ cgi_table_data align=right {
+ cgi_puts [cgi_font class=notice size=-1 "* Required field"]
+ }
+ }
+ cgi_table_row {
+ cgi_table_data align=center colspan=2 {
+ if {$readwrite} {
+ cgi_submit_button "save=Save Entry"
+ cgi_put [cgi_img [WPimg dot2] border=0 alt="" width=10]
+ }
+
+ if {$readwrite && $add == 0 && $take == 0} {
+ cgi_submit_button "delete=Delete Entry"
+ cgi_put [cgi_img [WPimg dot2] border=0 alt="" width=10]
+ }
+
+ cgi_submit_button "cancel=Cancel"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/addrpick.tcl b/web/cgi/alpine/1.0/addrpick.tcl
new file mode 100755
index 00000000..a71f55d6
--- /dev/null
+++ b/web/cgi/alpine/1.0/addrpick.tcl
@@ -0,0 +1,134 @@
+# $Id: addrpick.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# addrpick.tcl
+#
+# Purpose: CGI script to handle address book choices from the
+# via the addrbook browser generated form
+#
+# Input:
+set pick_vars {
+ {cid "Missing Command ID"}
+ {field "Missing Field Name"}
+ {addrop {} ""}
+ {cancel {} 0}
+}
+
+# Output:
+
+
+# read vars
+foreach item $pick_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} result]} {
+ error [list _action "Impart Variable" $result]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+if {$cid != [WPCmd PEInfo key]} {
+ catch {WPCmd PEInfo statmsg "Invalid Command ID"}
+}
+
+WPLoadCGIVarAs nickList listonicks
+set usenewfcc 0
+
+if {$cancel == 1 || [string compare cancel [string tolower $cancel]] == 0 || [string compare cancel [string tolower $addrop]] == 0} {
+} elseif {[string length $listonicks] > 0} {
+ foreach nick $listonicks {
+ # determine which book
+ if {[regexp {^([0-9]+)\.([0-9]+)\.(.*)$} $nick dummy book ai nick]} {
+ if {[catch {WPCmd PEAddress entry $book $nick $ai} result]} {
+ regsub -all "'" $result "\\'" result
+ WPCmd PEInfo statmsg $result
+ } else {
+ set resaddr [lindex $result 0]
+ regsub -all "'" $resaddr "\\'" resaddr
+ if {[info exists newaddrs]} {
+ append newaddrs ", "
+ }
+ if {[string compare $field "to"] == 0 && [string length [lindex $result 1]]} {
+ # arbitrarily the last returned entry with an fcc wins
+ set fcc [lindex $result 1]
+ if {[string compare $fcc "\"\""] == 0} {
+ set fcc ""
+ }
+ regsub -all "'" $fcc "\\'" fcc
+ set usenewfcc 1
+ }
+
+ append newaddrs $resaddr
+ }
+ } else {
+ WPCmd PEInfo statmsg "Malformed entry request: $addr"
+ }
+ }
+
+ if {[info exists newaddrs]} {
+ if {[catch {WPCmd PEInfo set suspended_composition} msgdata] == 0} {
+ for {set i 0} {$i < [llength $msgdata]} {incr i} {
+ if {[string compare [string tolower [lindex [lindex $msgdata $i] 0]] $field] == 0} {
+ if {[string length [lindex [lindex $msgdata $i] 1]]} {
+ set newfield [list $field "[lindex [lindex $msgdata $i] 1], $newaddrs"]
+ } else {
+ set newfield [list $field $newaddrs]
+ }
+ break
+ }
+ }
+
+ if {$usenewfcc} {
+ for {set j 0} {$j < [llength $msgdata]} {incr j} {
+ if {[string compare fcc [string tolower [lindex [lindex $msgdata $j] 0]]] == 0} {
+ set fcc_index $j
+ break
+ }
+ }
+
+ set savedef [WPTFSaveDefault 0]
+ set colid [lindex $savedef 0]
+ if {[info exists fcc_index]} {
+ if {[string compare $fcc [lindex [lindex [lindex $msgdata $fcc_index] 1] 1]]} {
+ lappend msgdata [list postoption [list fcc-set-by-addrbook 1]]
+ }
+
+ set msgdata [lreplace $msgdata $fcc_index $fcc_index [list Fcc [list $colid $fcc]]]
+ } else {
+ lappend msgdata [list Fcc [list $colid $fcc]]
+ lappend msgdata [list postoption [list fcc-set-by-addrbook 1]]
+ }
+ }
+
+ if {[info exists newfield]} {
+ set msgdata2 [lreplace $msgdata $i $i $newfield]
+ if {[catch {WPCmd PEInfo set suspended_composition $msgdata2} result] == 0} {
+ unset result
+ }
+ } else {
+ lappend msgdata [list $field $newaddrs]
+ if {[catch {WPCmd PEInfo set suspended_composition $msgdata} result] == 0} {
+ unset result
+ }
+ }
+
+ if {[info exists result]} {
+ WPCmd PEInfo statmsg "Cannot Update $field field: $result"
+ }
+ } else {
+ WPCmd PEInfo statmsg "Cannot change Message Data: $msgdata"
+ }
+ }
+}
+
+source [WPTFScript compose]
diff --git a/web/cgi/alpine/1.0/addrsave.tcl b/web/cgi/alpine/1.0/addrsave.tcl
new file mode 100755
index 00000000..37a70ebe
--- /dev/null
+++ b/web/cgi/alpine/1.0/addrsave.tcl
@@ -0,0 +1,244 @@
+# $Id: addrsave.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# addrsave.tcl
+#
+# Purpose: CGI script to handle address book change/save
+# via addredit generated form
+#
+# Input:
+set abs_vars {
+ {cid "Missing Command ID"}
+ {save {} 0}
+ {delete {} 0}
+ {replace {} 0}
+ {compose {} 0}
+ {take {} 0}
+ {help {} 0}
+ {cancel {} 0}
+ {oncancel {} ""}
+}
+
+# Output:
+#
+
+## read vars
+foreach item $abs_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} result]} {
+ error [list _action "Impart Variable" $result]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+if {$cid != [WPCmd PEInfo key]} {
+ catch {WPCmd PEInfo statmsg "Invalid Command ID"}
+}
+
+if {$help != 0} {
+ set save_vars {
+ {book {} 0}
+ {newnick {} ""}
+ {nick {} ""}
+ {ai {} 0}
+ {fn {} ""}
+ {addrs {} ""}
+ {fcc {} ""}
+ {comment {} ""}
+ {add {} 0}
+ {take {} 0}
+ }
+
+ foreach i $abs_vars {
+ lappend ae_help_state [list [lindex $i 0] [subst $[lindex $i 0]]]
+ }
+
+ foreach i $save_vars {
+ eval WPImport $i
+ lappend ae_help_state [list [lindex $i 0] [subst $[lindex $i 0]]]
+ }
+
+ WPCmd PEInfo set ae_help_state $ae_help_state
+
+ set _cgi_uservar(topic) takeedit
+ set _cgi_uservar(oncancel) addredit
+ if {$take} {
+ set _cgi_uservar(index) none
+ }
+
+ set src help
+
+} elseif {$save == 1 || [string compare [string tolower $save] "save entry"] == 0} {
+ WPLoadCGIVar book
+ WPLoadCGIVar newnick
+ WPLoadCGIVar ai
+ WPLoadCGIVar fn
+ WPLoadCGIVar addrs
+ WPLoadCGIVar fcc
+ WPLoadCGIVar comment
+ if {[catch {cgi_import_cookie add}] != 0} {set add 0}
+ if {[catch {cgi_import_cookie nick}] != 0} {set nick ""}
+
+ if {![string length $newnick]} {
+ WPCmd PEInfo statmsg "No entry created. New entries must include a Nickname."
+ set src takeedit
+ } else {
+ if {!$take || [catch {WPCmd PEAddress entry $book $newnick $ai} result] || ![llength $result]} {
+ set result ""
+ if {[catch {WPCmd PEAddress edit $book $newnick $ai $fn $addrs $fcc $comment $add $nick} result]} {
+ catch {WPCmd PEInfo statmsg "Address Set Failure: $result"}
+ set adderr $result
+ } elseif {[string length $result]} {
+ catch {WPCmd PEInfo statmsg "$result"}
+ set adderr $result
+ }
+
+ if {$take == 1} {
+ if {[info exists adderr]} {
+ WPCmd PEInfo statmsg "No address book entry changed: $adderr"
+ } else {
+ WPCmd PEInfo statmsg "New address book entry \"$newnick\" created."
+ }
+ }
+ } else {
+ set src takesame
+ }
+
+ if {$take == 1 && ![info exists src]} {
+ set src $oncancel
+ }
+ }
+} elseif {$delete == 1 || [string compare [string tolower $delete] "delete entry"] == 0} {
+ WPLoadCGIVar book
+ WPLoadCGIVar nick
+ WPLoadCGIVar ai
+
+ set result ""
+ if {[catch {WPCmd PEAddress delete $book $nick $ai} result]} {
+ catch {WPCmd PEInfo statmsg "Address Set Failure $result"}
+ } elseif {[string compare $result ""]} {
+ catch {WPCmd PEInfo statmsg "$result"}
+ }
+} elseif {[string compare [string tolower $replace] "replace entry"] == 0} {
+
+ WPLoadCGIVar book
+ WPLoadCGIVar newnick
+ WPLoadCGIVar ai
+ WPLoadCGIVar fn
+ WPLoadCGIVar addrs
+ WPLoadCGIVar fcc
+ WPLoadCGIVar comment
+ if {[catch {cgi_import_cookie add}] != 0} {set add 0}
+ if {[catch {cgi_import_cookie nick}] != 0} {set nick ""}
+
+ if {![string length $newnick]} {
+ WPCmd PEInfo statmsg "No entry created. New entries must include a Nickname."
+ set src takeedit
+ } else {
+ if {[catch {WPCmd PEAddress delete $book $newnick $ai} result]} {
+ set adderr $result
+ } else {
+ set result ""
+ if {[catch {WPCmd PEAddress edit $book $newnick $ai $fn $addrs $fcc $comment $add $nick} result]} {
+ catch {WPCmd PEInfo statmsg "Address Set Failure result"}
+ set adderr $result
+ } elseif {[string length $result]} {
+ catch {WPCmd PEInfo statmsg "$result"}
+ set adderr $result
+ }
+ }
+
+ if {$take == 1} {
+ if {[info exists adderr]} {
+ WPCmd PEInfo statmsg "No address book entry changed: $adderr."
+ } else {
+ WPCmd PEInfo statmsg "Address book entry \"$newnick\" replaced."
+ }
+
+ if {![info exists src]} {
+ set src $oncancel
+ }
+ }
+ }
+} elseif {[string compare [string tolower $replace] "add to entry"] == 0} {
+ WPLoadCGIVar book
+ WPLoadCGIVar newnick
+ WPLoadCGIVar ai
+ WPLoadCGIVar fn
+ WPLoadCGIVar addrs
+ WPLoadCGIVar fcc
+ WPLoadCGIVar comment
+ if {[catch {cgi_import_cookie add}] != 0} {set add 0}
+ if {[catch {cgi_import_cookie nick}] != 0} {set nick ""}
+
+ if {![string length $newnick]} {
+ WPCmd PEInfo statmsg "No entry created. New entries must include a Nickname."
+ set src takeedit
+ } else {
+ if {[catch {WPCmd PEAddress fullentry $book $newnick $ai} result] || ![llength $result]} {
+ if {[string length $result]} {
+ set adderr $result
+ } else {
+ set adderr "No pre-existing entry"
+ }
+ } else {
+ set fn [lindex $result 1]
+ set newaddrs [join [lindex $result 2] ","]
+ append newaddrs ", $addrs"
+ set fcc [lindex $result 3]
+ set comment [lindex $result 4]
+ set result ""
+ if {[catch {WPCmd PEAddress edit $book $newnick $ai $fn $newaddrs $fcc $comment 0 $newnick} result]} {
+ set adderr $result
+ } elseif {[string length $result]} {
+ catch {WPCmd PEInfo statmsg "$result"}
+ set adderr $result
+ }
+ }
+
+ if {$take == 1} {
+ if {[info exists adderr]} {
+ WPCmd PEInfo statmsg "No address book entry created: $adderr."
+ } else {
+ WPCmd PEInfo statmsg "Address book entry \"$newnick\" appended"
+ }
+
+ if {![info exists src]} {
+ set src $oncancel
+ }
+ }
+ }
+} elseif {[string compare [string tolower $replace] edit] == 0} {
+ set src takeedit
+} elseif {$compose == 1} {
+ set oncancel addrbook
+ set src compose
+} elseif {$cancel == 1 || [string compare [string tolower $cancel] cancel] == 0} {
+ if {$take == 1} {
+ set act "Take Address"
+ } else {
+ set act "Address Edit"
+ }
+
+ catch {WPCmd PEInfo statmsg "$act cancelled. Address book unchanged."}
+ set src $oncancel
+} else {
+ catch {WPCmd PEInfo statmsg "Unknown Address Book Operation"}
+}
+
+if {![info exists src]} {
+ set src addrbook
+}
+
+source [WPTFScript $src]
diff --git a/web/cgi/alpine/1.0/alpine.tcl b/web/cgi/alpine/1.0/alpine.tcl
new file mode 120000
index 00000000..5ad8d42f
--- /dev/null
+++ b/web/cgi/alpine/1.0/alpine.tcl
@@ -0,0 +1 @@
+../alpine.tcl \ No newline at end of file
diff --git a/web/cgi/alpine/1.0/attach.tcl b/web/cgi/alpine/1.0/attach.tcl
new file mode 100755
index 00000000..e0423171
--- /dev/null
+++ b/web/cgi/alpine/1.0/attach.tcl
@@ -0,0 +1,96 @@
+# $Id: attach.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# attach.tcl
+#
+# Purpose: CGI script to handle attaching attachment
+# to composition via queryattach generated form
+#
+# Input:
+set attach_vars {
+ {cid "Missing Command ID"}
+ {file "Missing File Upload Data"}
+ {attachop "" ""}
+ {cancel "" ""}
+}
+
+# Output:
+#
+
+## read vars
+foreach item $attach_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} result]} {
+ error [list _action "Impart Variable" $result]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+if {$cancel == 1 || [string compare cancel [string tolower $cancel]] == 0 || [string compare cancel [string tolower $attachop]] == 0} {
+} else {
+
+ if {$cid != [WPCmd PEInfo key]} {
+ catch {WPCmd PEInfo statmsg "Invalid Command ID"}
+ }
+
+ if {[string length [lindex $file 1]]} {
+ if {[catch {cgi_import description}]} {
+ set description ""
+ }
+
+ # "file" is a list: local_file remote_file content-type/content-subtype
+ # trim path from file name on remote system
+ # since we can't be certain what the delimiter is,
+ # try the usual suspects
+ set delims [list "\\" "/" ":"]
+ set native [lindex $file 1]
+ if {[string length $native]} {
+ foreach delim $delims {
+ if {[set crop [string last $delim $native]] >= 0} {
+ set native [string range $native [expr {$crop + 1}] [string length $native]]
+ break;
+ }
+ }
+
+ regsub -all "'" $native "\\'" jsnative
+
+ if {0 == [string length [lindex $file 2]]} {
+ set conttype [list text plain]
+ } else {
+ set conttype [split [lindex $file 2] "/"]
+ }
+
+ set id [WPCmd PECompose attach [lindex $file 0] [lindex $conttype 0] [lindex $conttype 1] $native $description]
+
+ catch {unset style}
+ set restore 1
+
+ if {[catch {WPCmd PEInfo lappend suspended_composition [list attach $id]} result]} {
+ WPCmd PEInfo statmsg "Cannot append attachment info, nothing attached"
+ }
+
+ set fsize [file size [lindex $file 0]]
+ if {$fsize <= 0} {
+ WPCmd PEInfo statmsg "Attachment $jsnative empty or nonexistant"
+ }
+ } else {
+ WPCmd PEInfo statmsg "Requested attachment does not exist"
+ }
+ } else {
+ catch {file delete [lindex $file 0]}
+ WPCmd PEInfo statmsg "Empty file name, nothing attached"
+ }
+}
+
+source [WPTFScript compose]
diff --git a/web/cgi/alpine/1.0/cledit.tcl b/web/cgi/alpine/1.0/cledit.tcl
new file mode 100755
index 00000000..dabf6044
--- /dev/null
+++ b/web/cgi/alpine/1.0/cledit.tcl
@@ -0,0 +1,232 @@
+#!./tclsh
+# $Id: cledit.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+#
+# cledit.tcl
+#
+# Purpose: CGI script to generate html form editing of a single collection
+
+# read config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+set cledit_vars {
+ {cid "Missing Command ID"}
+ {oncancel "Missing oncancel"}
+ {cl {} 0}
+ {add {} 0}
+ {errtext {} 0}
+ {nick {} ""}
+ {server {} ""}
+ {ssl {} 1}
+ {user {} ""}
+ {stype {} "imap"}
+ {path {} ""}
+ {view {} ""}
+ {onclecancel {} ""}
+}
+
+## read vars
+foreach item $cledit_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} result]} {
+ if {[llength $item] > 2} {
+ set [lindex $item 0] [lindex $item 2]
+ } else {
+ error [list _action [lindex $item 1] $result]
+ }
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+set cledit_menu {
+ {
+ {}
+ {
+ {
+ # * * * * OK * * * *
+ if {$add} {
+ cgi_image_button cle_save=[WPimg but_save] border=0 alt="Save Config"
+ } else {
+ cgi_image_button cle_save=[WPimg but_save] border=0 alt="Save Config"
+ }
+ }
+ }
+ }
+ {
+ {}
+ {
+ {
+ # * * * * CANCEL * * * *
+ if {[string length $onclecancel]} {
+ cgi_puts [WPMenuURL "wp.tcl?page=$onclecancel&cid=$cid&oncancel=$oncancel" "" [cgi_img [WPimg but_cancel] border=0 alt="Cancel"] "" target=_top]
+ } else {
+ cgi_puts [WPMenuURL "wp.tcl?page=$oncancel&cid=$cid" "" [cgi_img [WPimg but_cancel] border=0 alt="Cancel"] "" target=_top]
+ }
+ }
+ }
+ }
+}
+
+cgi_http_head {
+ WPStdHttpHdrs
+}
+
+cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "Collection List Configuration"
+ WPStyleSheets
+ }
+
+ cgi_body BGCOLOR="$_wp(bordercolor)" {
+
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=get name=clconfig target=_top {
+ cgi_text "cid=$cid" type=hidden notab
+ cgi_text "page=conf_process" type=hidden notab
+ cgi_text "cp_op=clconfig" type=hidden notab
+ cgi_text "oncancel=$oncancel" type=hidden notab
+ cgi_text "cl=$cl" type=hidden notab
+ cgi_text "add=$add" type=hidden notab
+ cgi_table border=0 cellspacing=0 cellpadding=2 width="100%" height="100%" {
+ cgi_table_row {
+ #
+ # next comes the menu down the left side
+ #
+ eval {
+ cgi_table_data $_wp(menuargs) rowspan=4 {
+ WPTFCommandMenu cledit_menu {}
+ }
+ }
+ }
+
+ #
+ # In main body of screen goe confg list
+ #
+ cgi_table_row {
+ cgi_table_data valign=top width="100%" class=dialog {
+ if {$add == 0 && $errtext == 0} {
+ set item [WPCmd PEConfig clextended $cl]
+ set nick [lindex $item 0]
+ set server [lindex $item 2]
+ set path [lindex $item 3]
+ set view [lindex $item 4]
+ set user ""
+ set stype "imap"
+ set ssl 0
+ }
+ set server_flags {
+ ssl
+ user
+ imap
+ }
+ # jpf: took out nntp, service, and pop3
+ foreach flag $server_flags {
+ if {[regexp -nocase "^\[^/\]*(/\[^/\]*)*(/$flag\[^/\]*)(/\[^/\]*)*" $server match junk1 flagset junk2]} {
+ set regprob 1
+ if {[regexp -nocase "^/$flag\$" $flagset]} {
+ set regprob 0
+ if {[string compare [string tolower $flag] "ssl"] == 0} {
+ set ssl 1
+ } elseif {[regexp -nocase "(imap|nntp|pop3)" $flag]} {
+ set stype "$flag"
+ } else {
+ set regprob 1
+ }
+ } elseif {[regexp -nocase "^/user=(.*)\$" $flagset match tuser]} {
+ set user "$tuser"
+ set regprob 0
+ } elseif {[regexp -nocase "^/service=(.*)\$" $flagset match tuser]} {
+ if {[regexp -nocase "(imap|nntp|pop3)" $tuser]} {
+ set stype "$stype"
+ } else {set regprob 1}
+ } else {
+ set regprob 1
+ }
+ if {$regprob == 0} {
+ regsub -nocase "^(\[^/\]*)(/\[^/\]*)*(/$flag\[^/\]*)(/\[^/\]*)*" $server "\\1\\2\\4" server
+ }
+ }
+ }
+ cgi_table border=0 cellspacing=0 cellpadding=5 {
+ cgi_table_row {
+ cgi_table_data align=right {
+ cgi_puts "[cgi_font size=+1 "Nickname[cgi_nbspace]: "]"
+ }
+ cgi_table_data align=left {
+ cgi_text "nick=$nick" size=40 notab
+ }
+ }
+ cgi_table_row {
+ cgi_table_data align=right valign=top {
+ cgi_puts "[cgi_font size=+1 "Server[cgi_nbspace]: "]"
+ }
+ cgi_table_data align=left {
+ cgi_table border=0 cellspacing=0 cellpadding=0 width="100%" {
+ cgi_table_row {
+ cgi_table_data colspan=2 {
+ cgi_text "server=$server" size=40 notab
+ }
+ }
+ cgi_table_row {
+ cgi_table_data {
+ cgi_puts "[cgi_font size=-1 "SSL"]"
+ if {$ssl == 1} {
+ set checked checked
+ } else {
+ set checked ""
+ }
+ cgi_checkbox "ssl" style=background-color:$_wp(dialogcolor) $checked
+ }
+ cgi_table_data align=right {
+ cgi_puts "[cgi_font size=-1 "User:[cgi_nbspace]"]"
+ cgi_text "user=$user" size=10 notab
+ }
+ cgi_text "stype=imap" type=hidden notab
+ }
+ }
+ }
+ }
+ cgi_table_row {
+ cgi_table_data align=right {
+ cgi_puts "[cgi_font size=+1 "Path[cgi_nbspace]: "]"
+ }
+ if {[string compare "nntp" $stype] == 0 && [regexp -nocase {^#news\.(.*)$} $path match rempath]} {
+ set path "$rempath"
+ }
+ cgi_table_data align=left {
+ cgi_text "path=$path" size=40 notab
+ }
+ }
+ cgi_table_row {
+ cgi_table_data align=right {
+ cgi_puts "[cgi_font size=+1 "View[cgi_nbspace]: "]"
+ }
+ cgi_table_data align=left {
+ cgi_text "view=$view" size=40 notab
+ }
+ }
+ }
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data height=200 class=dialog {
+ cgi_puts [cgi_nbspace]
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/cmdfunc.tcl b/web/cgi/alpine/1.0/cmdfunc.tcl
new file mode 100755
index 00000000..949baf0f
--- /dev/null
+++ b/web/cgi/alpine/1.0/cmdfunc.tcl
@@ -0,0 +1,639 @@
+#!./tclsh
+# $Id: cmdfunc.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# cmdfunc.tcl
+#
+# Purpose: CGI script to serve as single location for menu/command
+# function definitions
+#
+# OPTIMIZE: have servlet interpreter grok/exec these?
+#
+# Input:
+
+# Output:
+#
+
+proc WPTFTitle {{context {some page}} {newmail {}} {nologo 0} {aboutcancel {}}} {
+ global _wp
+
+ cgi_table border=0 cellspacing=0 cellpadding=0 width="100%" class=title {
+ cgi_table_row {
+ if {!$nologo} {
+ cgi_table_data valign=top align=left height=$_wp(titleheight) {
+
+ if {[string length $aboutcancel]} {
+ cgi_put [cgi_url [cgi_imglink smalllogo] wp.tcl?page=help&topic=about&oncancel=$aboutcancel class=navbar target=_top]
+ } else {
+ cgi_put [cgi_imglink smalllogo]
+ }
+ }
+ }
+
+ # work in new mail here
+ if {[llength $newmail]} {
+ cgi_table_data align=center {
+ WPTFStatusTable $newmail
+ }
+ }
+
+ cgi_table_data align=right valign=middle height=$_wp(titleheight) {
+ cgi_put [cgi_span "style=margin-right: 8; color: $_wp(titlecolor)" "$context"]
+ }
+ }
+ }
+}
+
+proc WPTFStatusTable {msgs {iconlink {0}} {style {}}} {
+ global _wp
+
+ cgi_table width=100% border=0 cellpadding=0 cellspacing=0 $style {
+ cgi_table_row align=right {
+
+ if {[info exists _wp(statusicons)] && $_wp(statusicons)} {
+ set img [cgi_imglink bang]
+ set snd ""
+ foreach m $msgs {
+ if {[string length [lindex $m 1]]} {
+ set img [cgi_imglink [lindex $m 1]]
+ if {$iconlink && [string length [lindex $m 2]]} {
+ set img [cgi_url $img wp.tcl?page=view&uid=[lindex $m 2] target=body]
+ }
+
+ set snd [lindex $m 3]
+ break
+ }
+ }
+
+ cgi_table_data {
+ cgi_puts ${img}${snd}
+ }
+ }
+
+ cgi_table_data align=center class="statustext" {
+ set i 0
+
+ foreach m $msgs {
+
+ if {[array exists lastmsg] && [info exists lastmsg([lindex $m 0])]} {
+ incr lastmsg([lindex $m 0])
+ continue
+ }
+
+ if {0 == [string compare [string range [lindex $m 0] 0 20] "Alert received while "]} {
+ set style "style=border: 1px solid red ; background-color: pink; padding: 2; width: 80%;"
+ } elseif {!([info exists _wp(statusicons)] && $_wp(statusicons))} {
+ set style "style=color: white ; background-color: $_wp(menucolor); padding-left:8px; padding-right:8px; white-space: nowrap;"
+ } else {
+ set style
+ }
+
+ if {$iconlink && [string length [lindex $m 2]] && !([info exists _wp(statusicons)] && $_wp(statusicons))} {
+ set txt [cgi_url [lindex $m 0] wp.tcl?page=fr_view&uid=[lindex $m 2] target=body "style=text-decoration: none; color: white"]
+ } else {
+ set txt [lindex $m 0]
+ }
+
+ cgi_division "style=\"padding-bottom: 1px\"" {
+ cgi_puts [cgi_span $style $txt]
+ }
+
+ set lastmsg([lindex $m 0]) 1
+ }
+ }
+
+ if {[info exists _wp(statusicons)] && $_wp(statusicons)} {
+ cgi_table_data align=left {
+ cgi_puts $img
+ }
+ }
+ }
+ }
+}
+
+
+proc WPTFImageButton {args} {
+ return [cgi_buffer {eval cgi_image_button $args border=0}]
+}
+
+proc WPTFCommandMenu {s_menu c_menu} {
+ global _wp
+
+ set clist {}
+ if {[string length $s_menu]} {
+ upvar $s_menu specificmenu
+ if {[llength $specificmenu]} {
+ lappend clist $specificmenu
+ }
+ }
+
+ if {[string length $c_menu]} {
+ upvar $c_menu commonmenu
+ if {[llength $commonmenu]} {
+ if {[llength $clist]} {
+ lappend clist {}
+ }
+ lappend clist $commonmenu
+ }
+ }
+
+ cgi_table border=0 bgcolor=$_wp(menucolor) cellpadding=0 cellspacing=0 width=112 "style=\"padding: 8 0 8 4\"" {
+ foreach menulist $clist {
+ switch [llength $menulist] {
+ 0 {
+ cgi_table_row {
+ cgi_table_data {
+ cgi_hr "width=75%"
+ }
+ }
+ }
+ default {
+ foreach item $menulist {
+ if {[llength $item] == 0} {
+ cgi_table_row {
+ cgi_table_data {
+ cgi_hr "width=75%"
+ }
+ }
+ continue
+ }
+ if {[llength $item] == 1} {
+ cgi_table_row {
+ cgi_table_data {
+ eval [lindex $item 0]
+ }
+ }
+ continue
+ }
+ if {[string length [lindex $item 0]]} {
+ if {[uplevel [lindex $item 0]] == 0} {
+ continue
+ }
+ }
+
+ cgi_table_row {
+ foreach l [lindex $item 1] {
+ cgi_table_data align=left valign=middle class=navbar {
+ uplevel $l
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+proc WPTFScript {scrpt {dflt ""}} {
+ global _wp
+
+ switch -- $scrpt {
+ main {
+ set src main.tcl
+ }
+ index {
+ set src index.tcl
+ WPCmd PEInfo set wp_body_script $src
+ }
+ view {
+ set src view.tcl
+ WPCmd PEInfo set wp_body_script $src
+ }
+ body {
+ if {[catch {WPCmd PEInfo set wp_body_script} src]} {
+ set src index.tcl
+ catch {WPCmd PEInfo set wp_body_script $src}
+ }
+ }
+ fr_view {
+ set src do_view.tcl
+ }
+ quit {
+ set src fr_queryquit.tcl
+ }
+ folders {
+ set src folders.tcl
+ }
+ fldrbrowse {
+ set src fldrbrowse.tcl
+ }
+ fldrsavenew {
+ set src fldrsavenew.tcl
+ }
+ fldrdel {
+ set src fr_querydelfldr.tcl
+ }
+ compose {
+ set src fr_compose.tcl
+ }
+ addrbrowse {
+ set src fr_addrbrowse.tcl
+ }
+ savebrowse {
+ set src fr_fldrbrowse.tcl
+ }
+ savecreate {
+ set src fr_fldrsavenew.tcl
+ }
+ take {
+ set src fr_take.tcl
+ }
+ takeedit {
+ set src fr_takeedit.tcl
+ }
+ takesame {
+ set src fr_takesame.tcl
+ }
+ ldapbrowse {
+ set src fr_ldapbrowse.tcl
+ }
+ addrbook {
+ set src addrbook.tcl
+ }
+ tconfig {
+ set src tconfig.tcl
+ }
+ cledit {
+ set src cledit.tcl
+ }
+ filtedit {
+ set src filtedit.tcl
+ }
+ conf_process {
+ set src conf_process.tcl
+ }
+ resume {
+ set src fr_resume.tcl
+ }
+ spell {
+ set src fr_spellcheck.tcl
+ }
+ auth {
+ set src fr_queryauth.tcl
+ }
+ expunge {
+ set src fr_queryexpunge.tcl
+ }
+ askattach {
+ set src fr_queryattach.tcl
+ }
+ ldapquery {
+ set src fr_ldapquery.tcl
+ }
+ querycreate {
+ set src fr_querycreate.tcl
+ }
+ queryprune {
+ set src fr_queryprune.tcl
+ }
+ attach {
+ set src attach.tcl
+ }
+ dosend {
+ set src dosend.tcl
+ }
+ docancel {
+ set src docancel.tcl
+ }
+ help {
+ set src fr_help.tcl
+ }
+ split {
+ set src fr_split.tcl
+ }
+ default {
+ if {[regexp {.*\.tcl$} $scrpt s]} {
+ set src $scrpt
+ } elseif {[string length $dflt]} {
+ set src $dflt
+ } else {
+ error "Unrecognized script abbreviation: $scrpt"
+ }
+ }
+ }
+
+ return [file join $_wp(cgipath) $_wp(appdir) $_wp(ui1dir) $src]
+}
+
+proc WPTFSaveDefault {{uid 0}} {
+ # "size" rather than "number" to work around temporary alpined bug
+ if {$uid == 0
+ || [catch {WPCmd PEMessage $uid size} n]
+ || $n == 0
+ || [catch {WPCmd PEMessage $uid savedefault} savedefault]} {
+ if {[WPCmd PEFolder isincoming 0]} {
+ set colid 1
+ } else {
+ set colid 0
+ }
+
+ return [list $colid "saved-messages"]
+ }
+
+ return $savedefault
+}
+
+if {$_wp(keybindings)} {
+ proc WPTFKeyEquiv {kl {exclusions {}} {frame window}} {
+ if {[llength $kl] > 0} {
+ WPStdScripts
+
+ append js "function bindListener(o,f)\{"
+ if {[isW3C]} {
+ append js "o.addEventListener('keypress',f, false);\n"
+ set cancelkeystroke "e.preventDefault(); return false;"
+ } elseif {[isIE]} {
+ append js "o.onkeydown = f;\n"
+ set cancelkeystroke "return false;"
+ } else {
+ append js "o.onkeydown = f;"
+ append js "o.captureEvents(Event.KEYDOWN);\n"
+ set cancelkeystroke "return false;"
+ }
+ append js "\}\n"
+
+ append js "function nobubble(e)\{"
+ if {[isW3C]} {
+ append js " e.stopPropagation();"
+ } elseif {[isIE]} {
+ append js " event.cancelBubble = true;"
+ }
+ append js "\}\n"
+
+ append js "function keyed(e)\{"
+ if {[isW3C] && [llength $exclusions]} {
+ set ex ""
+ foreach o $exclusions {
+ if {[string length $ex]} {
+ append ex " || "
+ }
+
+ append ex "e.target == $o"
+ }
+ append js "if (e.target && ($ex)) return true;"
+ }
+ append js " var c = getKeyStr(e);"
+ append js " if(getControlKey(e))\{"
+ append js " switch(c)\{"
+ append js " case 'n' : window.status = 'New window creation disabled in WebPine.' ; $cancelkeystroke"
+ append js " \}"
+ append js " \}"
+ append js " else"
+ append js " switch(c)\{"
+ foreach kb $kl {
+ append js " case '[lindex $kb 0]' : ${frame}.webpinelink = 1; [lindex $kb 1] ; $cancelkeystroke"
+ }
+
+ append js " \}\}\n"
+
+ set onload "bindListener(document,keyed);"
+
+ if {![isW3C]} {
+ foreach o $exclusions {
+ append onload "bindListener($o,nobubble);"
+ }
+ }
+
+ cgi_javascript {
+ cgi_puts $js
+ }
+
+ return $onload
+ }
+ }
+}
+
+# add given folder name to the cache of saved-to folders
+proc WPTFAddSaveCache {f_name} {
+ global _wp
+
+ if {[catch {WPSessionState save_cache} flist] == 0} {
+ if {[set i [lsearch -exact $flist $f_name]] < 0} {
+ set flist [lrange $flist 0 [expr {$_wp(save_cache_max) - 2}]]
+ } else {
+ set flist [lreplace $flist $i $i]
+ }
+
+ set flist [linsert $flist 0 $f_name]
+ } else {
+ set flist [list $f_name]
+ }
+
+ catch {WPSessionState save_cache $flist}
+}
+
+# return the list of cached saved-to folders and make sure given
+# default is somewhere in the list
+proc WPTFGetSaveCache {{def_name ""}} {
+
+ if {![string length $def_name]} {
+ set savedef [WPTFSaveDefault 0]
+ set def_name [lindex $savedef 1]
+ }
+
+ set seen ""
+
+ if {[catch {WPSessionState save_cache} flist] == 0} {
+ foreach f $flist {
+ if {[string compare $def_name $f] == 0} {
+ set def_listed 1
+ }
+
+ if {[string length $f] && [lsearch -exact $seen $f] < 0} {
+ lappend options $f
+ lappend options $f
+ lappend seen $f
+ }
+ }
+ }
+
+ if {!([info exists options] && [info exists def_listed])} {
+ lappend options $def_name
+ lappend options $def_name
+ }
+
+ if {[catch {WPCmd set wp_cache_folder} wp_cache_folder]
+ || [string compare $wp_cache_folder [WPCmd PEMailbox mailboxname]]} {
+ # move default to top on new folder
+ switch -- [set x [lsearch -exact $options $def_name]] {
+ 0 { }
+ default {
+ if {$x > 0} {
+ set options [lreplace $options $x [expr {$x + 1}]]
+ }
+
+ set options [linsert $options 0 $def_name]
+ set options [linsert $options 0 $def_name]
+ }
+ }
+
+ catch {WPCmd set wp_cache_folder [WPCmd PEMailbox mailboxname]}
+ }
+
+ lappend options "\[ folder I type in \]"
+ lappend options "__folder__prompt__"
+
+ lappend options "\[ folder in my folder list \]"
+ lappend options "__folder__list__"
+
+ return $options
+}
+
+# add given folder name to the visited folder cache
+proc WPTFAddFolderCache {f_col f_name} {
+ global _wp
+
+ if {$f_col != 0 || [string compare [string tolower $f_name] inbox]} {
+ if {0 == [catch {WPSessionState folder_cache} flist]} {
+
+ if {[catch {WPSessionState left_column_folders} fln]} {
+ set fln $_wp(fldr_cache_def)
+ }
+
+ for {set i 0} {$i < [llength $flist]} {incr i} {
+ set f [lindex $flist $i]
+ if {$f_col == [lindex $f 0] && 0 == [string compare [lindex $f 1] $f_name]} {
+ break
+ }
+ }
+
+ if {$i >= [llength $flist]} {
+ set flist [lrange $flist 0 $fln]
+ } else {
+ set flist [lreplace $flist $i $i]
+ }
+
+ set flist [linsert $flist 0 [list $f_col $f_name]]
+ # let users of data know it's changed (cheaper than hash)
+ WPScriptVersion common 1
+ } else {
+ catch {unset flist}
+ lappend flist [list $f_col $f_name] [list [WPTFSaveDefault 0]]
+ # ditto
+ WPScriptVersion common 1
+ }
+
+ catch {WPSessionState folder_cache $flist}
+ }
+}
+
+# return the list of cached visited folders and make sure given
+# default is somewhere in the list
+proc WPTFGetFolderCache {} {
+ if {[catch {WPSessionState folder_cache} flist]} {
+ catch {unset flist}
+ lappend flist [WPTFSaveDefault 0]
+ catch {WPSessionState folder_cache $flist}
+ }
+
+ return $flist
+}
+
+proc WPExitOnClose {{frame window}} {
+ global _wp
+
+ cgi_script type="text/javascript" language="JavaScript" {
+ cgi_put "function wpLink(){"
+ cgi_put " ${frame}.webpinelink = 1;"
+ cgi_put " return true;"
+ cgi_puts "}"
+ cgi_put "function wpLoad(){"
+ cgi_put " ${frame}.webpinelink = 0;"
+ cgi_puts "}"
+ cgi_put "function wpUnLoad(){"
+ cgi_put " if(!${frame}.webpinelink){"
+ cgi_put " window.open('[cgi_root]/$_wp(appdir)/$_wp(ui1dir)/ripcord.tcl?t=10&cid=[WPCmd PEInfo key]','Depart','width=350,height=200');"
+ cgi_put " }"
+ cgi_puts "}"
+ }
+
+ uplevel 1 {
+
+ # tweak some cgi_* procs for global effect
+ if {0 == [catch {rename cgi_url _wp_orig_cgi_url}]} {
+ proc cgi_url {args} {
+ lappend newargs [lindex $args 0]
+ foreach a [lrange $args 1 end] {
+ if {[regexp "^(onClick)=(.*)" $a dummy attr str]} {
+ set onclicked 1
+ lappend newargs "${attr}=wpLink();${str}"
+ } else {
+ lappend newargs $a
+ }
+ }
+
+ if {![info exists onclicked]} {
+ lappend newargs "onClick=wpLink();"
+ }
+
+ return [eval "_wp_orig_cgi_url $newargs"]
+ }
+ }
+
+ if {0 == [catch {rename cgi_area _wp_orig_cgi_area}]} {
+ proc cgi_area {args} {
+ lappend newargs [lindex $args 0]
+ foreach a [lrange $args 1 end] {
+ if {[regexp "^(onClick)=(.*)" $a dummy attr str]} {
+ set onclicked 1
+ lappend newargs "${attr}=\"wpLink();${str}\""
+ } else {
+ lappend newargs $a
+ }
+ }
+
+ if {![info exists onclicked]} {
+ lappend newargs "onClick=\"return wpLink();\""
+ }
+
+ return [eval "_wp_orig_cgi_area $newargs"]
+ }
+ }
+
+ if {0 == [catch {rename cgi_form _wp_orig_cgi_form}]} {
+ proc cgi_form {args} {
+ foreach a [lrange $args 0 [expr [llength $args]-2]] {
+ if {[regexp "^onSubmit=(.*)" $a dummy str]} {
+ set onsubmitted 1
+ lappend newargs "onSubmit=wpLink(); ${str}"
+ } else {
+ lappend newargs $a
+ }
+ }
+
+ if {![info exists onsubmitted]} {
+ lappend newargs "onSubmit=return wpLink();"
+ }
+
+ lappend newargs [lindex $args end]
+
+ uplevel 1 "_wp_orig_cgi_form $newargs"
+ }
+ }
+ }
+}
+
+proc WPTFIndexWidthRatio {fields field} {
+ # should be formula based on total fields
+ # and number of "wider" fields
+ switch [string toupper $field] {
+ TO -
+ FROM -
+ FROMORTO -
+ FROMORTONOTNEWS -
+ RECIPS -
+ SENDER -
+ SUBJECT { return 1.25 }
+ default { return 1 }
+ }
+}
diff --git a/web/cgi/alpine/1.0/common.tcl b/web/cgi/alpine/1.0/common.tcl
new file mode 100755
index 00000000..4a540cc8
--- /dev/null
+++ b/web/cgi/alpine/1.0/common.tcl
@@ -0,0 +1,289 @@
+#!./tclsh
+# $Id: common.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# common.tcl
+#
+# Purpose: CGI script snippet to generate html output associated
+# with the WebPine message view/index ops frame
+#
+# Input:
+set ops_vars {
+}
+
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+set padleft 3px
+
+#
+# Command Menu definition for Message View Screen
+#
+set view_menu {
+}
+
+set common_menu {
+ {
+ {expr {0}}
+ {
+ {
+ # * * * * UBIQUITOUS INBOX LINK * * * *
+ if {[string compare inbox [string tolower [WPCmd PEMailbox mailboxname]]]} {
+ cgi_put [cgi_url INBOX open.tcl?folder=INBOX&colid=0&cid=[WPCmd PEInfo key] target=_top class=navbar]
+ } else {
+ cgi_put [cgi_url INBOX fr_index.tcl target=spec class=navbar]
+ }
+ }
+ }
+ }
+ {
+ {expr {0}}
+ {
+ {
+ # * * * * FOLDER LIST * * * *
+ cgi_puts [cgi_url "Folder List" "wp.tcl?page=folders&cid=[WPCmd PEInfo key]" target=_top class=navbar]
+ }
+ }
+ }
+ {
+ {}
+ {
+ {
+ # * * * * COMPOSE * * * *
+ cgi_puts [cgi_url Compose wp.tcl?page=compose&oncancel=main.tcl&cid=[WPCmd PEInfo key] target=_top class=navbar]
+ }
+ }
+ }
+ {
+ {}
+ {
+ {
+ # * * * * RESUME * * * *
+ cgi_puts [cgi_url Resume wp.tcl?page=resume&oncancel=main.tcl&cid=[WPCmd PEInfo key] target=_top class=navbar]
+ }
+ }
+ }
+ {
+ {}
+ {
+ {
+ # * * * * Addr books * * * *
+ cgi_puts [cgi_url "Address Book" wp.tcl?page=addrbook&oncancel=main.tcl target=_top class=navbar]
+ }
+ }
+ }
+}
+
+
+WPEval $ops_vars {
+ cgi_http_head {
+ WPStdHttpHdrs {} 60
+ }
+
+ cgi_html {
+ cgi_head {
+ if {[info exists _wp(exitonclose)]} {
+ WPExitOnClose top.spec.body
+ }
+
+ WPStyleSheets
+ cgi_script type="text/javascript" language="JavaScript" {
+ cgi_puts "function flip(n){"
+ cgi_put " var d = top.spec.body.document;"
+ cgi_put " var f = d.index;"
+ cgi_put " if(f && document.implementation){"
+ cgi_put " var e = d.createElement('input');"
+ cgi_put " var ver = navigator.appVersion;"
+ cgi_put " if(!((ver.indexOf('MSIE')+1) && (ver.indexOf('Macintosh')+1))) e.type = 'hidden';"
+ cgi_put " e.name = 'bod_'+n;"
+ cgi_put " e.value = '1';"
+ cgi_put " f.appendChild(e);"
+ cgi_put " f.submit();"
+ cgi_put " return false;"
+ cgi_puts " }"
+ cgi_puts " return true;"
+ cgi_puts "}"
+ }
+
+ if {$_wp(keybindings)} {
+ set kequiv {
+ {{l} {top.location = 'wp.tcl?page=folders'}}
+ {{a} {top.location = 'wp.tcl?page=addrbook'}}
+ {{?} {top.location = 'wp.tcl?page=help'}}
+ {{n} {if(flip('next')) top.spec.body.location = 'wp.tcl?page=body&bod_next=1'}}
+ {{p} {if(flip('prev')) top.spec.body.location = 'wp.tcl?page=body&bod_prev=1'}}
+ {{i} {if(top.spec.cmds) top.spec.location = 'fr_index.tcl'}}
+ {{s} {if(top.spec.cmds) top.spec.cmds.document.saveform.f_name.focus()}}
+ {{d} {if(top.spec.cmds) top.spec.cmds.document.delform.op[0].click()}}
+ {{u} {if(top.spec.cmds) top.spec.cmds.document.delform.op[1].click()}}
+ {{r} {if(top.spec.cmds) top.spec.cmds.document.replform.op.click()}}
+ {{f} {if(top.spec.cmds) top.spec.cmds.document.forwform.op.click()}}
+ {{ } {if(top.spec.body.document.index && flip('next')) top.spec.body.location = 'wp.tcl?page=body&bod_next=1'}}
+ {{-} {if(top.spec.body.document.index && flip('prev')) top.spec.body.location = 'wp.tcl?page=body&bod_prev=1'}}
+ {{z} {if(top.spec.body.document.index && top.spec.body.document.index.zoom) top.spec.body.document.index.zoom.click()}}
+ }
+
+ lappend kequiv [list {c} "top.location = 'wp.tcl?page=compose&oncancel=main.tcl&cid=[WPCmd PEInfo key]'"]
+
+ if {[WPCmd PEInfo feature enable-full-header-cmd]} {
+ lappend kequiv [list {h} "if(top.spec.cmds.document.saveform) top.spec.body.location = 'wp.tcl?page=view&fullhdr=flip'"]
+ }
+
+ set onload "onLoad=[WPTFKeyEquiv $kequiv {} top.spec.body] top.gen.focus();"
+ }
+ }
+
+ cgi_body bgcolor=$_wp(bordercolor) $onload {
+
+ cgi_put [cgi_url [cgi_imglink smalllogo] wp.tcl?page=help&topic=about class=navbar target=_top]
+
+ cgi_br
+
+ cgi_division class=navbar "style=\"background-color: $_wp(menucolor)\"" {
+ cgi_division "style=\"padding: 8px 0 2px $padleft\"" {
+ cgi_puts [cgi_span "style=font-weight: bold" "Current Folder"]
+ }
+
+ set mbn [WPCmd PEMailbox mailboxname]
+ if {[string length $mbn] > 16} {
+ set mbn "[string range $mbn 0 14]..."
+ }
+
+ cgi_division align=center {
+ cgi_put [cgi_url $mbn fr_index.tcl target=spec class=navbar]
+
+ switch -exact -- [WPCmd PEMailbox state] {
+ readonly {
+ cgi_br
+ cgi_put [cgi_span "style=color: yellow; font-weight: bold" "(Read Only)"]
+ }
+ closed {
+ cgi_br
+ cgi_put [cgi_span "style=color: yellow; font-weight: bold" "(Closed)"]
+ }
+ ok -
+ default {}
+ }
+ }
+
+ cgi_hr "width=75%" "style=\"margin-top:8px\""
+
+ # Common Navigation controls
+ cgi_division align=center "style=\"padding-bottom: 4px\"" {
+ cgi_put [cgi_img [WPimg but_rnd_block] border=0 "usemap=#nav" "alt=Navigation Controls"]
+ cgi_map nav {
+ cgi_area shape=rect coords=0,0,37,38 "href=wp.tcl?page=body&bod_prev=1" target=body "onClick=\"return flip('prev')\"" "alt=Previous"
+ cgi_area shape=rect coords=0,40,32,74 "href=wp.tcl?page=body&bod_next=1" target=body "onClick=\"return flip('next')\"" "alt=Next"
+ cgi_area shape=rect coords=50,0,82,38 "href=wp.tcl?page=body&bod_first=1" target=body "onClick=\"return flip('first')\"" "alt=First"
+ cgi_area shape=rect coords=50,40,82,74 "href=wp.tcl?page=body&bod_last=1" target=body "onClick=\"return flip('last')\"" "alt=Last"
+ }
+
+ # Jump option
+ if {[WPCmd PEInfo feature enable-jump-cmd]} {
+ cgi_br
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=get target=body name=goform "style=margin-top:6" {
+ cgi_text "page=body" type=hidden notab
+ cgi_text gonum= class=navtext size=4 maxlength=6 "onClick=this.select()"
+ if {0} {
+ cgi_br
+ cgi_submit_button "goto=Jump to Msg \#" class=navtext "style=margin-right:2;margin-top:6"
+ } else {
+ cgi_submit_button "goto=Jump" class=navtext "style=margin-right:2"
+ }
+ }
+ }
+ }
+
+ cgi_hr "width=75%"
+
+ cgi_division "style=\"padding: 0 0 6px $padleft\"" {
+
+ if {[catch {WPSessionState left_column_folders} fln]} {
+ set fln $_wp(fldr_cache_def)
+ }
+
+ if {$fln > 0} {
+ cgi_puts [cgi_span "style=font-weight: bold" "Folders"]
+ cgi_division "style=\"padding-left: 4px\"" {
+ # UBIQUITOUS INBOX LINK
+ if {[string compare inbox [string tolower [WPCmd PEMailbox mailboxname]]]} {
+ cgi_put [cgi_url INBOX open.tcl?folder=INBOX&colid=0&cid=[WPCmd PEInfo key] target=_top class=navbar]
+ } else {
+ cgi_put [cgi_url INBOX fr_index.tcl target=spec class=navbar]
+ }
+
+ set n 0
+ set fc [WPTFGetFolderCache]
+ for {set i 0} {$i < [llength $fc] && $i < $fln} {incr i} {
+ set f [lindex $fc $i]
+
+ if {0 == [catch {WPCmd PEFolder exists [lindex $f 0] [lindex $f 1]} result] && $result} {
+ cgi_br
+
+ set fn [lindex $f 1]
+ if {[string length $fn] > 15} {
+ set fn "...[string range $fn end-15 end]"
+ }
+
+ if {[string compare [lindex $f 1] [WPCmd PEMailbox mailboxname]]} {
+ cgi_put [cgi_url $fn "open.tcl?folder=[lindex $f 1]&colid=[lindex $f 0]&cid=[WPCmd PEInfo key]" target=_top class=navbar]
+ } else {
+ cgi_put [cgi_url $fn fr_index.tcl target=spec class=navbar]
+ }
+
+ if {[incr n] >= $fln} {
+ break
+ }
+ }
+ }
+
+ cgi_br
+ cgi_puts [cgi_url "More Folders..." "wp.tcl?page=folders&cid=[WPCmd PEInfo key]" target=_top class=navbar]
+ }
+ } else {
+ cgi_puts [cgi_url "Folder List" "wp.tcl?page=folders&cid=[WPCmd PEInfo key]" target=_top class=navbar]
+ }
+ }
+
+ cgi_hr "width=75%"
+
+ # Common Operations
+ cgi_division "style=\"padding: 0 0 0 $padleft\"" {
+ # * * * * COMPOSE * * * *
+ cgi_puts [cgi_url Compose wp.tcl?page=compose&oncancel=main.tcl&cid=[WPCmd PEInfo key] target=_top class=navbar]
+ cgi_br
+ # * * * * RESUME * * * *
+ cgi_puts [cgi_url Resume wp.tcl?page=resume&oncancel=main.tcl&cid=[WPCmd PEInfo key] target=_top class=navbar]
+ cgi_br
+ # * * * * Addr books * * * *
+ cgi_puts [cgi_url "Address Book" wp.tcl?page=addrbook&oncancel=main.tcl target=_top class=navbar]
+ }
+
+ cgi_division "style=\"padding: 12px 0 0 $padleft\"" {
+ cgi_put [cgi_url "Configure" wp.tcl?page=conf_process&newconf=1&oncancel=main.tcl&cid=[WPCmd PEInfo key] class=navbar target=_top]
+ cgi_br
+ cgi_put [cgi_url "Get Help" wp.tcl?page=help class=navbar target=_top]
+ }
+
+ cgi_division "style=\"padding: 12px 0 10px $padleft\"" {
+ if {[WPCmd PEInfo feature quit-without-confirm]} {
+ cgi_puts [cgi_url "Quit $_wp(appname)" $_wp(serverpath)/session/logout.tcl?cid=[WPCmd PEInfo key]&sessid=$sessid target=_top class=navbar]
+ } else {
+ cgi_puts [cgi_url "Quit $_wp(appname)" wp.tcl?page=quit&cid=[WPCmd PEInfo key] target=_top class=navbar]
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/compose.tcl b/web/cgi/alpine/1.0/compose.tcl
new file mode 100755
index 00000000..d5a6af3d
--- /dev/null
+++ b/web/cgi/alpine/1.0/compose.tcl
@@ -0,0 +1,804 @@
+#!./tclsh
+# $Id: compose.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# compose.tcl
+#
+# Purpose: TCL script to generate html form representing
+# a message composition.
+
+# Input:
+set compose_vars {
+ {uid "" 0}
+ {style "" ""}
+ {nickto "" ""}
+ {book "" 0}
+ {ai "" -1}
+ {notice "" ""}
+ {repall "" 0}
+ {reptext "" 0}
+ {repqstr "" ""}
+ {f_name "" ""}
+ {f_colid "" 0}
+ {cid "Missing Command ID"}
+ {spell "" ""}
+ {oncancel "" "main.tcl"}
+}
+
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+# Output:
+#
+
+set bgcolor $_wp(dialogcolor)
+set buttoncolor "ffcc66"
+
+# set the default for the compose form. Modern browsers should submit
+# in the character set of the HTML form.
+set default_charset "UTF-8"
+
+#set cancelmsg "Really Cancel your Message (and destroy its text)"
+
+set msgs(cancel) "Cancel message (answering OK will abandon your mail message)"
+
+set msgs(noattach) {[ Message has no attachments ]}
+
+set defaultheaders {to cc subject attach}
+
+set fccname ""
+
+proc fieldname {name} {
+ regsub -all -- {-} [string tolower $name] {_} fieldname
+ return $fieldname
+}
+
+proc default_fcc {itemval} {
+ global fccdefault f_name f_colid
+
+ if {[catch {WPCmd PEFolder collections} collections]} {
+ set collections ""
+ }
+
+ # fcc's itemval should be a list of collection-id and folder-name
+ if {[llength $itemval] == 2} {
+ set fcccol [lindex $itemval 0]
+ set fccname [lindex $itemval 1]
+ } elseif {[string length $f_name]} {
+ set fcccol $f_colid
+ set fccname $f_name
+ } elseif {[info exists fccdefault] || [catch {WPCmd PECompose fccdefault} fccdefault] == 0} {
+ set fcccol [lindex $fccdefault 0]
+ set fccname [lindex $fccdefault 1]
+ unset fccdefault
+ } else {
+ set fccname sent-mail
+ if {[llength $collections] > 1} {
+ set fcccol 1
+ } else {
+ set fcccol 0
+ }
+ }
+
+ return [list $fccname $fcccol $collections]
+}
+
+proc rowfield {item itemval style} {
+ global _wp msgs attachments bgcolor postoption fccname cols
+
+ set litem [string tolower $item]
+
+ if {$cols == 80} {
+ set textsize "60%"
+ } else {
+ set textsize $cols
+ }
+
+ cgi_table_row class=body $style bgcolor="$bgcolor" {
+ cgi_table_data align=left class=comphdr width=60 {
+ switch -- [string tolower $item] {
+ attach {
+ #cgi_submit_button "queryattach=Attach" class="comphdrbtn"
+ cgi_put Attachments
+ }
+ x_fcc {
+ cgi_submit_button "br_fcc=Fcc" class=comphdrbtn
+ }
+ default {
+ cgi_put $item
+ }
+ }
+ }
+
+ cgi_table_data align=center class=comphdr "style=\"padding: 0px 6px 0px 2px\"" {
+ cgi_put [cgi_bold ":"]
+ }
+
+ cgi_table_data align=left valign=middle class=comptext "style=\"padding: 2px 0px\"" nowrap {
+ switch -- [string tolower $item] {
+ attach {
+ cgi_table border=0 cellpadding=0 cellspacing=0 width="100%" {
+ cgi_table_row {
+ set alist ""
+ cgi_table_data align=left {
+ if {[info exists attachments] && [set n [llength $attachments]]} {
+ cgi_table border=0 cellpadding=0 cellspacing=0 width="100%" {
+ # format: {ID TITLE SIZE TYPE}
+ set i 1
+ foreach a $attachments {
+ cgi_table_row {
+ if {$n > 1} {
+ cgi_table_data {
+ cgi_puts [cgi_font face=Helvetica "$i)"]
+ }
+ incr i
+ }
+ cgi_table_data valign=middle align=left {
+ set id [lindex $a 0]
+ cgi_puts [cgi_font face=Helvetica "[lindex $a 1] ([lindex $a 2] bytes) [lindex $a 3]"]
+ lappend alist $id
+ }
+ cgi_table_data align=right height=23 width=23 {
+ cgi_image_button detach_${id}=[WPimg but_remove] border=0 align=right alt="Delete Attachment"
+ }
+ }
+ }
+ }
+ } else {
+ cgi_put [cgi_font size="-1" face=Helvetica $msgs(noattach)]
+ }
+
+ cgi_text attachments=[join $alist ","] type=hidden notab
+ }
+
+ cgi_table_data align=right width=70 {
+ cgi_submit_button "queryattach=Attach" class=compbtn "style=\"width:64\""
+ }
+ }
+ }
+ }
+ cc -
+ bcc -
+ reply-to -
+ to {
+ set fn [fieldname $item]
+ cgi_text $fn=${itemval} size=$textsize "style=\"padding: 2px\""
+ cgi_image_button "br_${fn}=[WPimg book]" class=compbtn "alt=Address Book" "style=\"vertical-align:text-bottom\""
+ cgi_submit_button "ex_${fn}=Expand" class=compbtn "style=\"vertical-align:base-line\""
+ }
+ fcc {
+ set deffcc [default_fcc $itemval]
+
+ set fccname [lindex $deffcc 0]
+ set fcccol [lindex $deffcc 1]
+ set collections [lindex $deffcc 2]
+
+ cgi_table border=0 cellpadding=0 cellspacing=0 width="100%" {
+ cgi_table_row {
+ cgi_table_data {
+ if {[llength $collections] > 1} {
+ cgi_text [fieldname $item]=$fccname size=38 onFocus=this.select()
+ #cgi_put [cgi_font face=Helvetica "in[cgi_nbspace]collection:[cgi_nbspace]"]
+ cgi_put [cgi_font class=comphdr in][cgi_nbspace]
+ cgi_select colid align=center {
+ for {set j 0} {$j < [llength $collections]} {incr j} {
+ if {$j == $fcccol} {
+ set selected selected
+ } else {
+ set selected {}
+ }
+
+ set colname [lindex [lindex $collections $j] 1]
+ if {[string length $colname] > 15} {
+ set colname "[string range $colname 0 12]..."
+ }
+
+ cgi_option $colname value=$j $selected
+ }
+ }
+ } else {
+ cgi_text [fieldname $item]=$fccname size=$textsize maxlength=1024 onFocus=this.select()
+ cgi_text "colid=0" type=hidden notab
+ }
+ }
+
+ cgi_table_data align=right {
+ cgi_submit_button "br_fcc=Browse" class="compbtn"
+ }
+ }
+ }
+ }
+ default {
+ cgi_text [fieldname $item]=${itemval} size=$textsize maxlength=1024
+ }
+ }
+ }
+ }
+
+ switch -- [string tolower $item] {
+ attach {
+ cgi_table_row class=smallhdr {
+ cgi_table_data colspan=2 {
+ cgi_put [cgi_img [WPimg dot]]
+ }
+
+ cgi_table_data {
+ cgi_table cellpadding=0 cellspacing=0 border=0 {
+ cgi_table_row {
+ cgi_table_data valign=middle class=smallhdr {
+ # complicated cause prompt and feature are reverse sense
+ if {[info exists postoption(fcc-without-attachments)]} {
+ if {$postoption(fcc-without-attachments)} {
+ set checked ""
+ } else {
+ set checked checked
+ }
+ } elseif {[WPCmd PEInfo feature "fcc-without-attachments"]} {
+ set checked ""
+ } else {
+ set checked checked
+ }
+
+ cgi_checkbox fccattach=1 class=smallhdr id="fcccb" $checked
+ }
+ cgi_table_data valign=middle class=smallhdr {
+ set blurb "Include attachments in copy of message saved to Fcc"
+ if {[string length $fccname]} {
+ append blurb " (i.e., \"$fccname\")"
+ }
+
+ cgi_put [cgi_span "style=cursor: pointer" onclick="flipCheck('fcccb')" $blurb]
+ }
+ }
+ }
+ }
+ }
+ }
+ default {}
+ }
+}
+
+set compose_menu {
+ {
+ {expr [info exists _wp(ispell)] && [file executable $_wp(ispell)]}
+ {
+ {
+ # * * * * SPELLCHECK * * * *
+ cgi_put [cgi_img [WPimg dot] width=4]
+ cgi_submit_button "check=Spell Check" class=navtext "style=\"margin-top:8\""
+ }
+ }
+ }
+ {
+ {}
+ {
+ {
+ # * * * * HELP * * * *
+ cgi_put [cgi_img [WPimg dot] width=4]
+ cgi_submit_button "help=Get Help" class=navtext "style=\"margin-top:8;margin-bottom:8\""
+ }
+ }
+ }
+}
+
+set compose2_menu {
+ {
+ {}
+ {
+ {
+ # * * * * HELP * * * *
+ cgi_put [cgi_img [WPimg dot] width=4]
+ cgi_submit_button "help=Get Help" class=navtext
+ }
+ }
+ }
+}
+
+WPEval $compose_vars {
+
+ if {$cid != [WPCmd PEInfo key]} {
+ error [list _close "Invalid Command ID"]
+ }
+
+ set cols [WPCmd PEConfig columns]
+
+ switch -- $style {
+ Reply {
+ set title "Reply to Message [WPCmd PEMessage $uid number]"
+
+ if {[catch {cgi_import part}]} {
+ set part ""
+ }
+
+ foreach h [WPCmd PEMessage $uid replyheaders $part] {
+ set hdrvals([fieldname [lindex $h 0]]) [lindex $h 1]
+ }
+
+ if {!$repall} {
+ catch {unset hdrvals(cc)}
+ }
+
+ if {[WPCmd PEInfo feature quell-format-flowed] == 0} {
+ set flowed 1
+ }
+
+ if {$reptext} {
+ set replytext [WPCmd PEMessage $uid replytext $repqstr $part]
+ set body [join [lindex $replytext 0] "\r\n"]
+ if {[WPCmd PEInfo feature include-attachments-in-reply]} {
+ set attachments [lindex $replytext 1]
+ }
+ } else {
+ set body ""
+ foreach line [WPCmd PEInfo signature] {
+ append body "$line\r\n"
+ }
+ }
+
+ catch {WPCmd set help_context reply}
+ }
+ Forward {
+ set title "Forward Message [WPCmd PEMessage $uid number]"
+
+ if {[catch {cgi_import part}]} {
+ set part ""
+ }
+
+ foreach h [WPCmd PEMessage $uid forwardheaders $part] {
+ set hdrvals([fieldname [lindex $h 0]]) [lindex $h 1]
+ }
+
+ foreach line [WPCmd PEInfo signature] {
+ append body "$line\r\n"
+ }
+
+ if {[WPCmd PEInfo feature quell-format-flowed] == 0} {
+ set flowed 1
+ }
+
+ set forwardtext [WPCmd PEMessage $uid forwardtext $part]
+ append body [join [lindex $forwardtext 0] "\r\n"]
+
+ set attachments [lindex $forwardtext 1]
+ catch {WPCmd set help_context forward}
+ }
+ Postponed {
+ set title "Postponed Message"
+ set postponed [WPCmd PEPostpone extract $uid]
+
+ foreach h [lindex $postponed 0] {
+ append hdrvals([fieldname [lindex $h 0]]) [lindex $h 1]
+ }
+
+ set body [join [lindex $postponed 1] "\r\n"]
+ set attachments [lindex $postponed 2]
+
+ foreach opt [lindex $postponed 3] {
+ switch [lindex $opt 0] {
+ charset {
+ set charset [lindex $opt 1]
+ }
+ }
+ }
+
+ unset postponed
+ catch {WPCmd set help_context compose}
+ }
+ default {
+ if {![info exists title]} {
+ set title "Compose New Message"
+ }
+
+ if {[catch {cgi_import ldap}] == 0
+ && [string compare $ldap 1] == 0
+ && [catch {cgi_import qn}] == 0
+ && [catch {cgi_import si}] == 0
+ && [catch {cgi_import ni}] == 0
+ && [catch {WPCmd PELdap ldapext $qn "${si}.${ni}"} leinfo] == 0} {
+ foreach item [lindex $leinfo 1] {
+ switch -- [string tolower [lindex $item 0]] {
+ "email address" {
+ if {[catch {cgi_import ei}] == 0} {
+ set ldap_email [lindex [lindex $item 1] $ei]
+ } else {
+ set ldap_email [lindex [lindex $item 1] 0]
+ }
+ }
+ name {
+ set ldap_name [lindex [lindex $item 1] 0]
+ }
+ "fax telephone" {
+ set ldap_fax [lindex [lindex $item 1] 0]
+ }
+ }
+ }
+
+ # put it all together
+ if {[catch {cgi_import fax}] == 0 && [string compare $fax yes] == 0} {
+ set n {[0-9]}
+ set n3 $n$n$n
+ set n4 $n$n$n$n
+ if {[info exists ldap_name]
+ && [regexp "^\\\+1 ($n3) ($n3)-($n4)\$" $ldap_fax dummy areacode prefix number]
+ && [lsearch -exact {206 425} $areacode] >= 0} {
+ regsub -all { } $ldap_name {_} ldap_fax_name
+ set hdrvals(to) "\"Fax to ${ldap_name}\" <${ldap_fax_name}@${areacode}-${prefix}-${number}.fax.cac.washington.edu>"
+ set hdrvals(subject) "FAX: "
+ }
+ } elseif {[info exists ldap_email]} {
+ if {[info exists ldap_name]} {
+ set hdrvals(to) "\"$ldap_name\" <$ldap_email>"
+ } else {
+ set hdrvals(to) $ldap_email
+ }
+ }
+ }
+
+ if {[WPCmd PEInfo feature quell-format-flowed] == 0} {
+ set flowed 1
+ }
+
+ if {![info exists body] || [string length $body] == 0} {
+ if {([info exists msgdata] && [llength $msgdata]) || [catch {WPCmd set suspended_composition} msgdata] == 0} {
+ set attachments {}
+ foreach e $msgdata {
+ switch -- [string tolower [lindex $e 0]] {
+ "" {
+ # do nothing, empty field
+ }
+ attach {
+ if {[catch {WPCmd PECompose attachinfo [lindex $e 1]} ai]} {
+ WPCmd PEInfo statmsg $ai
+ } else {
+ lappend attachments [list [lindex $e 1] [lindex $ai 0] [lindex $ai 1] [lindex $ai 2]]
+ # attachment description is in [lindex $ai 3]
+ }
+ }
+ body {
+ catch {unset body}
+ if {[string compare $style Spell] == 0} {
+ if {[catch {cgi_import_as spell spell} result] == 0} {
+ if {[string compare $spell Cancel] == 0} {
+ WPCmd PEInfo statmsg "Spell Check Cancelled"
+ } elseif {[catch {WPCmd set wp_spellresult} spellresult] == 0} {
+ set oldbody [lindex $e 1]
+ foreach l $spellresult {
+ set oldline [lindex $oldbody [lindex $l 0]]
+ set newline ""
+ set offset 0
+ foreach w [lindex $l 1] {
+ set o [lindex $w 0]
+ append newline "[string range $oldline $offset [expr {$o - 1}]][lindex $w 2]"
+ set offset [expr {$o + [lindex $w 1]}]
+ }
+
+ append newline [string range $oldline $offset end]
+
+ set newlines([lindex $l 0]) $newline
+ }
+
+ for {set n 0} {$n < [llength $oldbody]} {incr n} {
+ if {[info exists newlines($n)]} {
+ append body $newlines($n)
+ } else {
+ append body [lindex $oldbody $n]
+ }
+
+ append body "\r\n"
+ }
+ } else {
+ WPCmd PEInfo statmsg "No corrections present, Nothing changed."
+ }
+ }
+
+ catch {WPCmd PEInfo unset wp_spellresult}
+ }
+
+ if {![info exists body]} {
+ set body [join [lindex $e 1] "\r\n"]
+ }
+ }
+ fcc {
+ if {[string length $f_name]} {
+ set hdrvals(fcc) [list $f_colid $f_name]
+ } else {
+ set hdrvals(fcc) [lindex $e 1]
+ }
+ }
+ postoption {
+ set opt [lindex $e 1]
+ switch -- [lindex $opt 0] {
+ charset {
+ set charset [lindex $opt 1]
+ }
+ default {
+ set postoption([lindex $opt 0]) [lindex $opt 1]
+ }
+ }
+ }
+ default {
+ set hdrvals([fieldname [lindex $e 0]]) [lindex $e 1]
+ }
+ }
+ }
+
+ if {![info exists body]} {
+ set body ""
+ }
+ } else {
+ set body ""
+ foreach line [WPCmd PEInfo signature] {
+ append body "$line\r\n"
+ }
+
+ if {[string length $nickto] != 0 || $ai != -1} {
+ if {[catch {WPCmd PEAddress entry $book $nickto $ai} entryval] == 0} {
+ set addrlist [lindex $entryval 0]
+ set newfcc [lindex $entryval 1]
+ if {[string length $newfcc]} {
+ global fccdefault
+ if {[string compare $newfcc "\"\""] == 0} {
+ set newfcc ""
+ }
+ if {[catch {WPCmd PEFolder collections} collections]} {
+ set collections ""
+ }
+ set fccdefault [list [expr {[llength $collections] > 1 ? 1 : 0}] $newfcc]
+ }
+ } else {
+ set addrlist $nickto
+ }
+ # bug: where's "to" supposed to come from?
+ if {0 && [string length $to]} {
+ append to ", ${addrlist}"
+ } else {
+ set to $addrlist
+ }
+ }
+
+ WPCmd PECompose noattach
+ }
+ }
+
+ catch {WPCmd set help_context compose}
+ }
+ }
+
+ if {[catch {WPCmd PECompose userhdrs} headers]} {
+ error [list _action "Header Retrieval Failure" $headers]
+ }
+
+ catch {fconfigure stdout -encoding binary}
+
+ if {![info exists charset]} {
+ set charset $default_charset
+ }
+
+ cgi_http_head {
+ WPStdHttpHdrs "text/html; charset=\"$charset\""
+ }
+
+ cgi_html {
+ cgi_head {
+ cgi_http_equiv Content-Type "text/html; charset=$charset"
+
+ WPStdHtmlHdr "Compose Message"
+ WPStdScripts
+ WPStyleSheets
+
+ cgi_put "<style type='text/css'>"
+ cgi_put ".comptext { background-color: $bgcolor ; font-family: courier, serif ; font-size: 10pt }"
+ cgi_put ".comphdr { background-color: $bgcolor ; font-family: arial, sans-serif ; font-size: 10pt ; font-weight: bold }"
+ cgi_put ".comphdrbtn { background-color: $bgcolor ; font-family: arial, sans-serif ; font-size: 10pt ; font-weight: bold ; text-decoration: underline }"
+ cgi_put ".comphdrbtnx { background-color: $buttoncolor ; font-family: arial, sans-serif ; font-size: 10pt ; font-weight: bold ; text-decorationx: underline }"
+ cgi_put ".compbtn { font-family: arial, sans-serif ; font-size: 9pt }"
+ cgi_put ".attachlist { width: 300 }"
+ cgi_put ".smallhdr { background-color: $bgcolor ; font-family: arial, sans-serif ; font-size: 8pt }"
+ cgi_puts "</style>"
+
+ cgi_javascript {
+ cgi_put "function setop(i){"
+ cgi_put " eval('document.compose.sendop\['+i+'\].checked = true');"
+ cgi_put " return false;"
+ cgi_put "}"
+ }
+ }
+
+ cgi_body BGCOLOR="$_wp(bordercolor)" onLoad=document.compose.to.focus() {
+ if {[info exists comments]} {
+ cgi_puts "Diag: "
+ foreach c $comments {
+ cgi_html_comment $c
+ }
+ }
+
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=post name=compose enctype=multipart/form-data target=_top {
+ cgi_table border=0 cellspacing=0 cellpadding=0 width=100% {
+ cgi_table_row {
+ lappend msglist [list $notice]
+ # next comes the menu down the left side
+ eval {
+ cgi_table_data $_wp(menuargs) rowspan=2 {
+
+ cgi_division class=navbar "style=background-color:$_wp(menucolor)" {
+ # * * * * SEND * * * *
+ cgi_puts "<fieldset>"
+ set doneverb " OK "
+
+ cgi_puts [cgi_span class=navtext "When finished, choose action below, then click [cgi_italic $doneverb].[cgi_nl][cgi_nl]"]
+
+ cgi_radio_button sendop=send id="RB1"
+ cgi_put [cgi_span class=navtext "style=color:white; cursor: pointer" onclick=\"flipCheck('RB1')\" "Send"]
+ cgi_br
+ cgi_radio_button sendop=postpone id="RB2"
+ cgi_put [cgi_span class=navtext "style=color:white; cursor: pointer" onclick=\"flipCheck('RB2')\" "Postpone"]
+ cgi_br
+ cgi_radio_button sendop=cancel id="RB3"
+ cgi_put [cgi_span class=navtext "style=color:white; cursor: pointer" onclick=\"flipCheck('RB3')\" "Cancel"]
+ cgi_br
+ cgi_br
+ #cgi_image_button action=[WPimg but_s_do] border=0 alt="Do"
+ cgi_division "style=padding-bottom:4" align=center {
+ cgi_submit_button "action=$doneverb" class=navtext
+ }
+ cgi_puts "</fieldset>"
+ }
+
+ WPTFCommandMenu compose_menu {}
+ }
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data border=0 bgcolor=$bgcolor {
+ cgi_table {
+ cgi_table_row {
+ cgi_table_data {
+ cgi_table border=0 cellspacing=0 cellpadding=0 align=left {
+ cgi_text "cid=[WPCmd PEInfo key]" type=hidden notab
+ cgi_text "sessid=$sessid" type=hidden notab
+ cgi_text "page=post" type=hidden notab
+ cgi_text "postpost=$oncancel" type=hidden notab
+ if {[string length $repqstr]} {
+ cgi_text "repqstr=$repqstr" type=hidden notab
+ }
+
+ if {0 && [info exists charset]} {
+ cgi_text "form_charset=$charset" type=hidden notab
+ # character encoding suport: the idea is to plant a known
+ # char in the given charset and see what comes back to
+ # post.tcl from the browser. Why is it again the @#*$!
+ # browser can't just tell us?
+ foreach tc {#8364 #1066 thorn tcedil #65509} {
+ cgi_puts "<input name=\"ke_$tc\" value=&${tc}; type=hidden notab>"
+ }
+ }
+
+ if {[info exists flowed]} {
+ cgi_text "form_flowed=yes" type=hidden notab
+ }
+
+ foreach field [WPCmd PECompose syshdrs] {
+ set hdr [fieldname [lindex $field 0]]
+ if {[info exists hdrvals($hdr)]} {
+ cgi_text "${hdr}=$hdrvals($hdr)" type=hidden notab
+ }
+ }
+
+ if {[catch {WPCmd set wp_extra_hdrs} extras] == 0 && $extras == 0} {
+ set extrahdrs {}
+ }
+
+ foreach field $headers {
+ set item [lindex $field 0]
+
+ if {[string length $item] == 0} {
+ continue
+ }
+
+ set itemvaldef [lindex $field 1]
+ set litem [string tolower $item]
+ set fn [fieldname $item]
+
+ if {[info exists hdrvals($fn)]} {
+ set itemval $hdrvals($fn)
+ } elseif {[info exists $litem] && [string length [subst $$litem]]} {
+ set itemval [subst $$litem]
+ } elseif {[string length $itemvaldef]} {
+ set itemval $itemvaldef
+ } else {
+ set itemval ""
+ }
+
+ if {[catch {WPCmd PECompose composehdrs} h] == 0 && [llength $h] > 0} {
+ set display_headers [string tolower $h]
+ } else {
+ set display_headers $defaultheaders
+ }
+
+ if {![info exists extrahdrs]} {
+ if {[info exists postoption(fcc-set-by-addrbook)]} {
+ if {[lsearch -exact $display_headers fcc] < 0} {
+ lappend display_headers fcc
+ }
+ }
+ }
+
+ if {[info exists attachments] && [llength $attachments]
+ && [lsearch -exact $display_headers attach] < 0} {
+ lappend display_headers attach
+ }
+
+ if {[lsearch -exact $display_headers [string tolower $item]] >= 0} {
+ rowfield $item $itemval $style
+ } elseif {[info exists extrahdrs]} {
+ lappend extrahdrs [list $item $itemval $style]
+ } else {
+ switch -- [string tolower $item] {
+ fcc {
+ set deffcc [default_fcc $itemval]
+
+ set fccname [lindex $deffcc 0]
+ set fcccol [lindex $deffcc 1]
+
+ cgi_text [fieldname $item]=$fccname type=hidden notab
+ cgi_text "colid=$fcccol" type=hidden notab
+ }
+ default {
+ cgi_text "[fieldname $item]=$itemval" type=hidden notab
+ }
+ }
+ }
+ }
+
+ if {[info exists extrahdrs]} {
+ cgi_table_row class=body bgcolor="$bgcolor" {
+ cgi_table_data colspan=4 align=left class=comptext {
+ cgi_image_button extrahdrs=[WPimg hdrless] border=0 alt="Hide Extra Headers"
+ }
+ }
+
+ foreach pb $extrahdrs {
+ rowfield [lindex $pb 0] [lindex $pb 1] [lindex $pb 2]
+ }
+ } else {
+ cgi_table_row class=body bgcolor="$bgcolor" {
+ cgi_table_data colspan=4 align=left class=comptext {
+ cgi_image_button extrahdrs=[WPimg hdrmore] border=0 alt="Show Extra Headers"
+ }
+ }
+ }
+ }
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data {
+ cgi_table border=0 cellspacing=0 cellpadding=0 width="100%" {
+ cgi_table_row class=body bgcolor="$bgcolor" {
+ cgi_table_data colspan=4 align=center class=comptext {
+ cgi_textarea body=${body} rows=20 cols=$cols wrap=physical class=view "style=\"padding: 2px\""
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/comview.tcl b/web/cgi/alpine/1.0/comview.tcl
new file mode 100755
index 00000000..01e1af80
--- /dev/null
+++ b/web/cgi/alpine/1.0/comview.tcl
@@ -0,0 +1,184 @@
+#!./tclsh
+# $Id: comview.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# comview.tcl
+#
+# Purpose: CGI script to produce the common view commands frame
+
+# Input:
+set comview_vars {
+ {f_colid {} [WPCmd PEFolder isincoming 0]}
+ {f_name {} "saved-messages"}
+}
+
+# inherit global config
+source ./alpine.tcl
+source ./cmdfunc.tcl
+
+WPEval $comview_vars {
+ cgi_http_head {
+ WPStdHttpHdrs {} 60
+ }
+
+ cgi_html {
+ cgi_head {
+ if {[info exists _wp(exitonclose)]} {
+ WPExitOnClose top.spec.body
+ }
+
+ WPStyleSheets
+ cgi_put "<style type='text/css'>"
+ cgi_put ".viewop { font-family: arial, sans-serif; font-size: 7pt }"
+ cgi_puts "</style>"
+ if {$_wp(keybindings)} {
+ set kequiv {
+ {{?} {top.location = 'wp.tcl?page=help'}}
+ {{l} {top.location = 'wp.tcl?page=folders'}}
+ {{a} {top.location = 'wp.tcl?page=addrbook'}}
+ {{n} {top.spec.body.location = 'wp.tcl?page=view&bod_next=1'}}
+ {{p} {top.spec.body.location = 'wp.tcl?page=view&bod_prev=1'}}
+ {{i} {top.spec.location = 'fr_index.tcl'}}
+ {{s} {document.saveform.f_name.focus()}}
+ {{d} {document.delform.op[0].click()}}
+ {{u} {document.delform.op[1].click()}}
+ {{r} {document.replform.op.click()}}
+ {{f} {document.forwform.op.click()}}
+ }
+
+ lappend kequiv [list {c} "top.location = 'wp.tcl?page=compose&oncancel=main.tcl&cid=[WPCmd PEInfo key]'"]
+
+ if {[WPCmd PEInfo feature enable-full-header-cmd]} {
+ lappend kequiv [list {h} "top.spec.body.location = 'wp.tcl?page=view&fullhdr=flip'"]
+ }
+
+ set onload "onLoad=[WPTFKeyEquiv $kequiv document.saveform.f_name top.spec.body]"
+ }
+ }
+
+ cgi_body bgcolor=$_wp(bordercolor) background=[file join $_wp(imagepath) logo $_wp(logodir) back.gif] "style=\"background-repeat: repeat-x\"" $onload {
+ cgi_table class=ops cellpadding=0 cellspacing=0 border=0 width="100%" height=24 {
+ cgi_table_row {
+ cgi_table_data valign=middle align=left nowrap class=viewop {
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=get target=_top name=replform {
+ cgi_text "page=view" type=hidden notab
+ cgi_text "cid=[WPCmd PEInfo key]" type=hidden notab
+ cgi_text "oncancel=main.tcl" type=hidden notab
+ cgi_text "postpost=fr_main.tcl" type=hidden notab
+
+ cgi_table border=0 class=ops cellpadding=0 cellspacing=0 class=viewop {
+ cgi_table_row {
+ cgi_table_data class=viewop rowspan=2 {
+ # * * * * REPLY * * * *
+ cgi_submit_button op=Reply class="viewop" "style=\"vertical-align: middle; margin-left: 4\""
+ }
+ cgi_table_data class=viewop {
+ cgi_checkbox "repall=1" style=vertical-align:middle
+ cgi_put "To All[cgi_nbspace]"
+ }
+ }
+ cgi_table_row {
+ cgi_table_data class=viewop rowspan=2 {
+ cgi_checkbox "reptext=1" checked style=vertical-align:middle
+ cgi_put "Include text"
+ }
+ }
+ }
+ }
+ }
+
+ cgi_table_data valign=middle align=center {
+ cgi_put [cgi_img [WPimg blackdot] width=1 height=26]
+ }
+
+ cgi_table_data valign=middle align=center nowrap {
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=get target=_top name=forwform {
+ cgi_text "page=view" type=hidden notab
+ cgi_text "cid=[WPCmd PEInfo key]" type=hidden notab
+ cgi_text "oncancel=main.tcl" type=hidden notab
+ cgi_text "postpost=fr_main.tcl" type=hidden notab
+
+ # * * * * FORWARD * * * *
+ cgi_submit_button op=Forward class="viewop"
+ }
+ }
+
+ cgi_table_data valign=middle align=center {
+ cgi_put [cgi_img [WPimg blackdot] width=1 height=26]
+ }
+
+ cgi_table_data valign=middle align=center nowrap class=viewop {
+ cgi_table class=ops cellpadding=0 cellspacing=0 border=0 class=viewop {
+ cgi_table_row {
+ cgi_table_data class=viewop {
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=get target=spec name=saveform {
+ cgi_text "page=fr_view" type=hidden notab
+ cgi_text "cid=[WPCmd PEInfo key]" type=hidden notab
+ cgi_text "sid=[clock seconds]" type=hidden notab
+
+ # * * * * Save * * * *
+ cgi_submit_button "save=Save" class="viewop"
+ cgi_put "[cgi_nbspace]to "
+
+ cgi_text f_colid=$f_colid type=hidden notab
+ cgi_text op=save type=hidden notab
+
+ cgi_select f_name class=viewop style=vertical-align:middle "onchange=document.saveform.save.click(); return false;" {
+ foreach {oname oval} [WPTFGetSaveCache] {
+ cgi_option $oname value=$oval
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ cgi_table_data valign=middle align=center {
+ cgi_put [cgi_img [WPimg blackdot] width=1 height=26]
+ }
+
+ cgi_table_data valign=middle align=center nowrap {
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=get target=_top name=take {
+ cgi_text "page=view" type=hidden notab
+ cgi_text "cid=[WPCmd PEInfo key]" type=hidden notab
+ cgi_submit_button op=Take class="viewop"
+ }
+ }
+
+ cgi_table_data valign=middle align=center {
+ cgi_put [cgi_img [WPimg blackdot] width=1 height=26]
+ }
+
+ cgi_table_data valign=middle align=right nowrap {
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=get target=body name=delform {
+ cgi_text "page=view" type=hidden notab
+ cgi_text "cid=[WPCmd PEInfo key]" type=hidden notab
+
+ # * * * * UNDELETE * * * *
+ cgi_submit_button op=Delete class="viewop"
+
+ # * * * * UNDELETE * * * *
+ cgi_submit_button op=Undelete class="viewop" "style=\"margin-right: 4\""
+
+ # * * * * ANTISPAM * * * *
+ if {([info exists _wp(spamaddr)] && [string length $_wp(spamaddr)])
+ || ([info exists _wp(spamfolder)] && [string length $_wp(spamfolder)])} {
+ cgi_submit_button "op=Report Spam" class="viewop" "style=\"margin-right: 4; color: white; background-color: black\""
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/conf_process.tcl b/web/cgi/alpine/1.0/conf_process.tcl
new file mode 100755
index 00000000..a8170c4e
--- /dev/null
+++ b/web/cgi/alpine/1.0/conf_process.tcl
@@ -0,0 +1,1358 @@
+#!./tclsh
+# $Id: conf_process.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# conf_process.tcl
+#
+# Purpose: CGI script to perform various message/mailbox
+# oriented operations
+
+source genvars.tcl
+source filter.tcl
+
+set cfs_vars {
+ {cid "Missing Command ID"}
+ {oncancel "Missing oncancel"}
+ {cp_op {} noop}
+ {save {} 0}
+ {delete {} 0}
+ {compose {} 0}
+ {cancel {} 0}
+ {gtab {} 0}
+ {mltab {} 0}
+ {mvtab {} 0}
+ {ctab {} 0}
+ {abtab {} 0}
+ {ftab {} 0}
+ {rtab {} 0}
+ {wv {} ""}
+ {varlistadd {} ""}
+ {newconf {} 0}
+}
+
+## read vars
+foreach item $cfs_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} result]} {
+ error [list _action "Import Variable" $result]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+proc wpGetVar {_var {valid ""}} {
+ upvar $_var var
+
+ if {[catch {cgi_import_as $_var var} result]} {
+ error [list _action "Import Var $_var" $result]
+ }
+
+ if {[string length $valid]} {
+ switch -exact -- $valid {
+ _INTEGER_ {
+ if {[string is integer -strict $var] != 1} {
+ error [list _action "Invalid Input" "Non-Numeric Value for $_var"]
+ }
+ }
+ default {
+ if {[lsearch -exact $valid $var] < 0} {
+ error [list _action "Invalid Input" "Unrecognized Value $var for $_var"]
+ }
+ }
+ }
+ }
+}
+
+proc wpGetVarAs {_var _varas} {
+ upvar $_varas varas
+
+ if {[catch {cgi_import_as $_var varas} result]} {
+ set varas ""
+ }
+}
+
+if {$cid != [WPCmd PEInfo key]} {
+ catch {WPCmd PEInfo statmsg "Invalid Command ID"}
+}
+
+proc wpGetGoodVars {} {
+ global wv
+ global general_vars msglist_vars composer_vars folder_vars address_vars msgview_vars rule_vars
+
+ switch -- $wv {
+ msgl {
+ set goodvars $msglist_vars
+ }
+ msgv {
+ set goodvars $msgview_vars
+ }
+ address {
+ set goodvars $address_vars
+ }
+ composer {
+ set goodvars $composer_vars
+ }
+ folder {
+ set goodvars $folder_vars
+ }
+ rule {
+ set goodvars $rule_vars
+ }
+ general {
+ set goodvars $general_vars
+ }
+ }
+ return $goodvars
+}
+
+proc fieldPos {fmt field} {
+ for {set i 0} {$i < [llength $fmt]} {incr i} {
+ if {[string compare [string toupper [lindex [lindex $fmt $i] 0]] [string toupper $field]] == 0} {
+ return $i
+ }
+ }
+
+ return -1
+}
+
+proc numberedVar {nvbase nvtotal} {
+ if {[catch {wpGetVarAs $nvtotal nvtot}] == 0} {
+ for {set i 0} {$i < $nvtot} {incr i} {
+ if {[catch {wpGetVar ${nvbase}${i}} nvval] == 0} {
+ return $i
+ }
+ }
+ }
+
+ return -1
+}
+
+set op $cp_op
+if {[catch {WPCmd set conf_page} conftype]} {
+ set conftype general
+}
+if {[string length $wv]} {
+ set conftype $wv
+ set op tab
+}
+if {$save == 1 || [string compare $save Save] == 0} {
+ set op tab
+ set subop save
+} elseif {$newconf} {
+ set op noop
+} elseif {$gtab} {
+ set op "tab"
+ set conftype "general"
+} elseif {$mltab} {
+ set op "tab"
+ set conftype "msgl"
+} elseif {$mvtab} {
+ set op "tab"
+ set conftype "msgv"
+} elseif {$ctab} {
+ set op "tab"
+ set conftype "composer"
+} elseif {$abtab} {
+ set op "tab"
+ set conftype "address"
+} elseif {$ftab} {
+ set op "tab"
+ set conftype "folder"
+} elseif {$rtab} {
+ set op "tab"
+ set conftype "rule"
+} elseif {$cancel == 1 || [string compare $cancel Cancel] == 0} {
+ set op "cancel"
+}
+
+proc wpGetRulePattern {} {
+ global pattern_fields
+
+ set patlist {}
+
+ foreach {patvar patfield} $pattern_fields {
+ wpGetVarAs $patvar tval
+
+ switch $patvar {
+ headers {
+ # collect header fields/values into "headers"
+ set headers {}
+ if {[catch {wpGetVarAs header_total hcnt} res] == 0} {
+ for {set i 0} {$i < $hcnt} {incr i} {
+ if {[catch {wpGetVarAs hdrfld${i} fld}] == 0
+ && [catch {wpGetVarAs hdrval${i} val}] == 0} {
+ lappend headers [list $fld $val]
+ }
+ }
+ }
+
+ lappend patlist [list headers $headers]
+ }
+ default {
+ lappend patlist [list $patvar $tval]
+ }
+ }
+ }
+
+
+ return $patlist
+
+}
+
+proc wpGetRuleAction {tosave} {
+ set actlist {}
+ wpGetVar action
+ if {$tosave == 1} {
+ lappend actlist [list "action" $action]
+ } else {
+ switch -- $action {
+ move {lappend actlist [list "kill" 0]}
+ delete {lappend actlist [list "kill" 1]}
+ }
+ }
+ wpGetVar actionfolder
+ lappend actlist [list "folder" $actionfolder]
+ wpGetVarAs moind moind
+ if {[string compare $moind "on"] == 0} {
+ lappend actlist [list [expr {$tosave == 1 ? "moind" : "move_only_if_not_deleted"}] "1"]
+ } else {
+ lappend actlist [list [expr {$tosave == 1 ? "moind" : "move_only_if_not_deleted"}] "0"]
+ }
+}
+
+ #
+ # Meat and potatoes of processing goes on here.
+ # Errors are barfed up as they occur,
+ # otherwise the result is communicated below...
+ #
+ set setfeatures [WPCmd PEConfig featuresettings]
+ set script fr_tconfig.tcl
+ switch -- $op {
+ tab {
+ if {[info exists goodvars] == 0} {
+ set goodvars [wpGetGoodVars]
+ }
+ foreach goodvar $goodvars {
+ set vtypeinp [lindex $goodvar 0]
+ set varname [lindex $goodvar 1]
+ set hlpthisvar 0
+ wpGetVarAs hlp.$varname.x thlp
+ if {[string length $thlp]} {
+ set hlpthisvar 1
+ set helpcancelset conf_process
+ }
+ switch -- $vtypeinp {
+ special {
+ switch -- $varname {
+ wp-columns {
+ if {$hlpthisvar} {
+ set subop varhelp
+ set varhelpname wp-columns
+ } else {
+ wpGetVar columns
+ WPCmd PEConfig columns $columns
+ }
+ }
+ left-column-folders {
+ if {0 == [catch {wpGetVar fcachel}]} {
+ if {$fcachel <= $_wp(fldr_cache_max)} {
+ catch {WPSessionState left_column_folders $fcachel}
+ }
+ }
+ }
+ signature {
+ wpGetVar signature
+ set cursig [string trimright [join [WPCmd PEConfig rawsig] "\n"]]
+ set signature [string trimright $signature]
+ if {[string compare $cursig $signature]} {
+ WPCmd PEConfig rawsig [split $signature "\n"]
+ }
+ }
+ filters {
+ wpGetVarAs $varname-sz sz
+ wpGetVarAs vla.$varname.x fltadd
+ wpGetVarAs hlp.$varname.x do_help
+ if {[string length $do_help]} {
+ set subop varhelp
+ set varhelpname filtconf
+ } elseif {[string length $fltadd]} {
+ set script "fr_filtedit.tcl"
+ set filtedit_add 1
+ set filtedit_onfiltcancel conf_process
+ } else {
+ if {[string length $sz] == 0} {
+ error [list _action "ERROR" "No size given for filters"]
+ }
+ for {set i 0} {$i < $sz} {incr i} {
+ wpGetVarAs vle.$varname.$i.x vle
+ wpGetVarAs vld.$varname.$i.x vld
+ wpGetVarAs vlsu.$varname.$i.x vlsu
+ wpGetVarAs vlsd.$varname.$i.x vlsd
+ set flt_ret 0
+ set flt_res ""
+ if {[string length $vle]} {
+ set script "fr_filtedit.tcl"
+ set filtedit_fno $i
+ set filtedit_onfiltcancel conf_process
+ } elseif {[string length $vld]} {
+ set flt_ret [catch {WPCmd PEConfig ruleset filter delete $i} flt_res]
+ } elseif {[string length $vlsu]} {
+ set flt_ret [catch {WPCmd PEConfig ruleset filter shuffup $i} flt_res]
+ } elseif {[string length $vlsd]} {
+ set flt_ret [catch {WPCmd PEConfig ruleset filter shuffdown $i} flt_res]
+ }
+ if {$flt_ret} {
+ # error
+ } elseif {[string length $flt_res]} {
+ # something wrong here
+ }
+ }
+ }
+ }
+ scores {
+ wpGetVarAs $varname-sz sz
+ wpGetVarAs vla.$varname.x fltadd
+ wpGetVarAs hlp.$varname.x do_help
+ if {[string length $do_help]} {
+ set subop varhelp
+ set varhelpname filtconf
+ } elseif {[string length $fltadd]} {
+ set script "fr_filtedit.tcl"
+ set filtedit_add 1
+ set filtedit_score 1
+ set filtedit_onfiltcancel conf_process
+ } else {
+ if {[string length $sz] == 0} {
+ error [list _action "ERROR" "No size given for scores"]
+ }
+ for {set i 0} {$i < $sz} {incr i} {
+ wpGetVarAs vle.$varname.$i.x vle
+ wpGetVarAs vld.$varname.$i.x vld
+ wpGetVarAs vlsu.$varname.$i.x vlsu
+ wpGetVarAs vlsd.$varname.$i.x vlsd
+ set flt_ret 0
+ set flt_res ""
+ if {[string length $vle]} {
+ set script "fr_filtedit.tcl"
+ set filtedit_score 1
+ set filtedit_fno $i
+ set filtedit_onfiltcancel conf_process
+ } elseif {[string length $vld]} {
+ set flt_ret [catch {WPCmd PEConfig ruleset score delete $i} flt_res]
+ } elseif {[string length $vlsu]} {
+ set flt_ret [catch {WPCmd PEConfig ruleset score shuffup $i} flt_res]
+ } elseif {[string length $vlsd]} {
+ set flt_ret [catch {WPCmd PEConfig ruleset score shuffdown $i} flt_res]
+ }
+ if {$flt_ret} {
+ # error
+ } elseif {[string length $flt_res]} {
+ # something wrong here
+ }
+ }
+ }
+ }
+ indexcolor {
+ wpGetVarAs $varname-sz sz
+ wpGetVarAs vla.$varname.x fltadd
+ wpGetVarAs hlp.$varname.x do_help
+ if {[string length $do_help]} {
+ set subop varhelp
+ set varhelpname filtconf
+ } elseif {[string length $fltadd]} {
+ set script "fr_filtedit.tcl"
+ set filtedit_add 1
+ set filtedit_indexcolor 1
+ set filtedit_onfiltcancel conf_process
+ } else {
+ if {[string length $sz] == 0} {
+ error [list _action "ERROR" "No size given for index colors"]
+ }
+ for {set i 0} {$i < $sz} {incr i} {
+ wpGetVarAs vle.$varname.$i.x vle
+ wpGetVarAs vld.$varname.$i.x vld
+ wpGetVarAs vlsu.$varname.$i.x vlsu
+ wpGetVarAs vlsd.$varname.$i.x vlsd
+ set flt_ret 0
+ set flt_res ""
+ if {[string length $vle]} {
+ set script "fr_filtedit.tcl"
+ set filtedit_indexcolor 1
+ set filtedit_fno $i
+ set filtedit_onfiltcancel conf_process
+ } elseif {[string length $vld]} {
+ set flt_ret [catch {WPCmd PEConfig ruleset indexcolor delete $i} flt_res]
+ } elseif {[string length $vlsu]} {
+ set flt_ret [catch {WPCmd PEConfig ruleset indexcolor shuffup $i} flt_res]
+ } elseif {[string length $vlsd]} {
+ set flt_ret [catch {WPCmd PEConfig ruleset indexcolor shuffdown $i} flt_res]
+ }
+ if {$flt_ret} {
+ # error
+ } elseif {[string length $flt_res]} {
+ # something wrong here
+ }
+ }
+ }
+ }
+ collections {
+ wpGetVarAs $varname-sz sz
+ wpGetVarAs vla.$varname.x cladd
+ if {[string length $cladd]} {
+ set script "fr_cledit.tcl"
+ set cledit_add 1
+ set cledit_onclecancel conf_process
+ } else {
+ if {[string length $sz] == 0} {
+ error [list _action "ERROR" "No size given for collections"]
+ }
+ for {set i 0} {$i < $sz} {incr i} {
+ wpGetVarAs vle.$varname.$i.x vle
+ wpGetVarAs vld.$varname.$i.x vld
+ wpGetVarAs vlsu.$varname.$i.x vlsu
+ wpGetVarAs vlsd.$varname.$i.x vlsd
+ set cle_ret 0
+ set cle_res ""
+ if {[string length $vle]} {
+ set script "fr_cledit.tcl"
+ set cledit_cl $i
+ set cledit_onclecancel conf_process
+ } elseif {[string length $vld]} {
+ set cle_ret [catch {WPCmd PEConfig cldel $i} cle_res]
+ } elseif {[string length $vlsu]} {
+ set cle_ret [catch {WPCmd PEConfig clshuff up $i} cle_res]
+ } elseif {[string length $vlsd]} {
+ set cle_ret [catch {WPCmd PEConfig clshuff down $i} cle_res]
+ }
+ if {$cle_ret} {
+ # error
+ } elseif {[string length $cle_res]} {
+ WPCmd PEInfo statmsg $cle_res
+ # something wrong here
+ }
+ }
+ }
+ }
+ index-format {
+ wpGetVarAs index-format iformat
+
+ set varchanged 0
+
+ if {$hlpthisvar} {
+ set subop varhelp
+ set varhelpname index-format
+ } elseif {[catch {cgi_import hlp.index_tokens.x} result] == 0} {
+ set subop secthelp
+ set topicclass plain
+ set feathelpname h_index_tokens
+ set varhelpname h_index_tokens
+ }
+
+ if {[catch {cgi_import indexadd}] == 0
+ && [string compare "Add Field" $indexadd] == 0
+ && [catch {cgi_import indexaddfield}] == 0} {
+ if {[lsearch $iformat $indexaddfield] < 0} {
+ set iformat [linsert $iformat 0 $indexaddfield]
+ set varchanged 1
+ }
+ } elseif {[catch {cgi_import adjust}] == 0
+ && [string compare Change $adjust] == 0
+ && [catch {cgi_import iop}] == 0
+ && [catch {cgi_import ifield}] == 0
+ && [set pos [fieldPos $iformat $ifield]] >= 0} {
+ switch $iop {
+ left {
+ set iformat [lreplace $iformat $pos $pos]
+ set iformat [linsert $iformat [incr pos -1] $ifield]
+ set varchanged 1
+ }
+ right {
+ set iformat [lreplace $iformat $pos $pos]
+ set iformat [linsert $iformat [incr pos] $ifield]
+ set varchanged 1
+ }
+ widen {
+ set f [lindex [lindex $iformat $pos] 0]
+ set w [lindex [lindex $iformat $pos] 1]
+ set dw [expr {round((100/[llength $iformat]) * [WPTFIndexWidthRatio $iformat $f])}]
+
+ if {[regexp {([0123456789]+)[%]} $w dummy w] == 0} {
+ set w $dw
+ }
+
+ if {$w < 95} {
+ incr w 5
+ } else {
+ set w 99
+ }
+
+ if {$w == $dw} {
+ set ws ""
+ } else {
+ set ws "${w}%"
+ }
+
+ set iformat [lreplace $iformat $pos $pos [list $f $ws]]
+ set varchanged 1
+ }
+ narrow {
+ set f [lindex [lindex $iformat $pos] 0]
+ set w [lindex [lindex $iformat $pos] 1]
+ set dw [expr {round((100/[llength $iformat]) * [WPTFIndexWidthRatio $iformat $f])}]
+
+ if {[regexp {([0123456789]+)[%]} $w dummy w] == 0} {
+ set w $dw
+ }
+
+ if {$w > 5} {
+ incr w -5
+ } else {
+ set w 1
+ }
+
+ if {$w == $dw} {
+ set ws ""
+ } else {
+ set ws "${w}%"
+ }
+
+ set iformat [lreplace $iformat $pos $pos [list $f $ws]]
+ set varchanged 1
+ }
+ remove {
+ set iformat [lreplace $iformat $pos $pos]
+ set varchanged 1
+ }
+ }
+ } else {
+ foreach f $iformat {
+ if {[catch {cgi_import_as shrm.${f}.x shift} result] == 0} {
+ if {[set pos [fieldPos $iformat $f]] >= 0} {
+ set iformat [lreplace $iformat $pos $pos]
+ set varchanged 1
+ }
+ } elseif {[catch {cgi_import_as shlf.${f}.x shift} result] == 0} {
+ if {[set pos [fieldPos $iformat $f]] > 0} {
+ set iformat [lreplace $iformat $pos $pos]
+ set iformat [linsert $iformat [incr pos -1] $f]
+ set varchanged 1
+ }
+ } elseif {[catch {cgi_import_as shrt.${f}.x shift} result] == 0} {
+ if {[set pos [fieldPos $iformat $f]] >= 0} {
+ set iformat [lreplace $iformat $pos $pos]
+ set iformat [linsert $iformat [incr pos] $f]
+ set varchanged 1
+ }
+ }
+ }
+ }
+
+ if {$varchanged} {
+ foreach f $iformat {
+ if {[string length [lindex $f 1]]} {
+ lappend ifv "[lindex $f 0]([lindex $f 1])"
+ } else {
+ lappend ifv [lindex $f 0]
+ }
+ }
+
+ WPCmd PEConfig varset index-format [list $ifv]
+ }
+ }
+ view-colors {
+ if {$hlpthisvar} {
+ set subop varhelp
+ set varhelpname index-format
+ } elseif {[catch {cgi_import_as colormap.x colx}] == 0
+ && [catch {cgi_import_as colormap.y coly}] == 0} {
+ set rgbs {"000" "051" "102" "153" "204" "255"}
+ set xrgbs {"00" "33" "66" "99" "CC" "FF"}
+ set rgblen [llength $rgbs]
+ set imappixwidth 10
+
+ set colx [expr {${colx} / $imappixwidth}]
+ set coly [expr {${coly} / $imappixwidth}]
+ if {($coly >= 0 && $coly < $rgblen)
+ && ($colx >= 0 && $colx < [expr {$rgblen * $rgblen}])} {
+ set ired $coly
+ set igreen [expr {($colx / $rgblen) % $rgblen}]
+ set iblue [expr {$colx % $rgblen}]
+ set rgb "[lindex $rgbs $ired],[lindex $rgbs ${igreen}],[lindex $rgbs ${iblue}]"
+ set xrgb "[lindex $xrgbs $ired][lindex $xrgbs ${igreen}][lindex $xrgbs ${iblue}]"
+
+ if {[catch {cgi_import_as text tt}] == 0} {
+ set type [split $tt .]
+ catch {WPCmd set config_deftext [lindex $type end]}
+
+ if {[catch {cgi_import_as ground ground}] == 0} {
+ switch $ground {
+ f {
+ catch {WPCmd set config_defground f}
+ set fg $xrgb
+ }
+ b {
+ catch {WPCmd set config_defground b}
+ set bg $xrgb
+ }
+ }
+
+ if {[info exists fg] || [info exists bg]} {
+ switch [lindex $type 0] {
+ hdr {
+ set type [lindex $type 1]
+ if {[catch {cgi_import_as add.${type} foo}] == 0} {
+ set colop add
+ } elseif {[catch {cgi_import_as hi.${type} hindex}] == 0} {
+ set colop change
+ }
+
+ if {[info exists colop]} {
+ if {![info exists bg] && [catch {cgi_import_as dbg.$type bg} result]} {
+ WPCmd PEInfo statmsg "Can't import default background: $result"
+ } elseif {![info exists fg] && [catch {cgi_import_as dfg.$type fg} result]} {
+ WPCmd PEInfo statmsg "Can't import default foreground: $result"
+ }
+
+ switch $colop {
+ change {
+ if {[catch {WPCmd PEConfig colorset viewer-hdr-colors update [list $hindex $type ""] [list $fg $bg]} result]} {
+ WPCmd PEInfo statmsg "Problem changing $type color: $result"
+ }
+ }
+ add {
+ if {[catch {WPCmd PEConfig colorset viewer-hdr-colors add [list $type ""] [list $fg $bg]} result]} {
+ WPCmd PEInfo statmsg "Problem adding $type color: $result"
+ }
+ }
+ }
+
+ }
+ }
+ default {
+ if {![info exists bg] && [catch {cgi_import_as dbg.$type bg} result]} {
+ WPCmd PEInfo statmsg "Can't import default background: $result"
+ } elseif {![info exists fg] && [catch {cgi_import_as dfg.$type fg} result]} {
+ WPCmd PEInfo statmsg "Can't import default foreground: $result"
+ } elseif {[catch {WPCmd PEConfig colorset $type [list $fg $bg]} result]} {
+ WPCmd PEInfo statmsg "Can't set $type color: $result"
+ }
+ }
+ }
+ } else {
+ WPCmd PEInfo statmsg "Invalid fore/back ground input!"
+ }
+ } else {
+ WPCmd PEInfo statmsg "Choose foreground or background!"
+ }
+ } else {
+ WPCmd PEInfo statmsg "Choose the type of text to color!"
+ }
+ } else {
+ WPCmd PEInfo statmsg "Invalid RGB Input!"
+ }
+ } elseif {[catch {cgi_import addfield}] == 0
+ && [string compare "add " [string tolower [string range $addfield 0 3]]] == 0
+ && [catch {cgi_import newfield}] == 0
+ && [string length [set newfield [string trim $newfield]]]
+ && [catch {cgi_import_as dfg.normal dfg}] == 0
+ && [catch {cgi_import_as dbg.normal dbg}] == 0} {
+ if {[catch {WPCmd PEConfig colorset viewer-hdr-colors add [list $newfield ""] [list $dfg $dbg]} result]} {
+ WPCmd PEInfo statmsg "Problem adding $type color: $result"
+ }
+ } elseif {[catch {cgi_import reset}] == 0
+ && [string compare "restore " [string tolower [string range $reset 0 7]]] == 0} {
+ if {[catch {cgi_import_as text tt}] == 0} {
+ if {[llength [set type [split $tt .]]] == 2 && [string compare [lindex $type 0] hdr] == 0} {
+ set hdr [lindex $type end]
+ if {[catch {cgi_import_as hi.$hdr hindex}] == 0} {
+ if {[catch {WPCmd PEConfig colorset viewer-hdr-colors delete $hindex} result]} {
+ # bug: reloads cause this error - need better way to report it
+ #WPCmd PEInfo statmsg "Can't reset $hdr ($hindex) text: $result!"
+ } else {
+ catch {WPCmd PEInfo unset config_deftext}
+ }
+ }
+ } elseif {[string compare normal $tt] == 0} {
+ if {[catch {WPCmd PEConfig varset normal-foreground-color ""} result]
+ || [catch {WPCmd PEConfig varset normal-background-color ""} result]} {
+ WPCmd PEInfo statmsg "Can't reset normal text: $result!"
+ }
+ } elseif {[catch {cgi_import_as dfg.normal dfg}] == 0
+ && [catch {cgi_import_as dbg.normal dbg}] == 0} {
+ catch {WPCmd set config_deftext $tt}
+ if {[catch {WPCmd PEConfig colorset $tt [list $dfg $dbg]} result]} {
+ WPCmd PEInfo statmsg "Can't reset $tt text: $result!"
+ }
+ }
+ } else {
+ WPCmd PEInfo statmsg "Choose the type of text to color!"
+ }
+ }
+ }
+ }
+ }
+ var {
+ wpGetVarAs $varname formval
+ set varvals [WPCmd PEConfig varget $varname]
+ set vals [lindex $varvals 0]
+ set vartype [lindex $varvals 1]
+ set formvals [split $formval "\n"]
+ set varchanged 0
+
+ if {$hlpthisvar} {
+ set subop varhelp
+ set varhelpname $varname
+ }
+
+ if {[string compare $vartype textarea] == 0} {
+ wpGetVarAs vla.$varname.x vlavar
+ wpGetVarAs $varname-sz sz
+ wpGetVarAs $varname-add valadd
+ if {[string length $vlavar]} {
+ set fr_tconfig_vlavar $varname
+ }
+ set formvals {}
+ if {[string length $valadd]} {
+ lappend formvals $valadd
+ }
+ if {[string length $sz]} {
+ set prevwassd 0
+ for {set i 0} {$i < $sz} {incr i} {
+ wpGetVarAs vle.$varname.$i fval
+ wpGetVarAs vld.$varname.$i.x fvaldel
+ wpGetVarAs vlsu.$varname.$i.x fvalsu
+ wpGetVarAs vlsd.$varname.$i.x fvalsd
+ set fed 0
+ set fdel 0
+ set fsu 0
+ set fsd 0
+ if {[string length $fval]} {
+ set fed 1
+ }
+ if {[string length $fvaldel]} {
+ set fdel 1
+ } elseif {[string length $fvalsu]} {
+ set fsu 1
+ } elseif {[string length $fvalsd]} {
+ set fsd 1
+ }
+ if {$fed && $fdel == 0 && $prevwassd} {
+ set prevwassd 0
+ set formvals [linsert $formvals [expr {[llength $formvals] - 1}] $fval]
+ } elseif {$fed && $fdel == 0 && $fsu == 0} {
+ lappend formvals $fval
+ if {$fsd} {
+ set prevwassd 1
+ }
+ } elseif {$fed && $fdel == 0 && $fsu} {
+ set fvallen [llength $formvals]
+ if {$fvallen} {
+ set formvals [linsert $formvals [expr {$fvallen - 2}] $fval]
+ } else {
+ lappend formvals $fval
+ }
+ }
+ }
+ }
+ set len [llength $formvals]
+ if {$len != [llength $vals]} {
+ set varchanged 1
+ } else {
+ for {set i 0} {$i < $len} {incr i} {
+ if {[string compare [lindex $formvals $i] [lindex $vals $i]]} {
+ set varchanged 1
+ break
+ }
+ }
+ }
+ } elseif {[llength $formvals] != [llength $vals]} {
+ set varchanged 1
+ } else {
+ set valslength [llength $vals]
+ for {set i 0} {$i < $valslength} {incr i} {
+ if {[string compare [lindex $vals $i] [lindex $formvals $i]]} {
+ set varchanged 1
+ break
+ }
+ }
+ }
+ if {$varchanged} {
+ WPCmd PEConfig varset $varname $formvals
+ }
+ # what about wp-indexheight?
+ }
+ feat {
+ wpGetVarAs $varname tval
+ if {$hlpthisvar} {
+ set subop feathelp
+ set feathelpname $varname
+ }
+ set featset [expr {[lsearch $setfeatures $varname] >= 0}]
+ set formfeatset [expr {[string compare $tval on] == 0}]
+ if {$formfeatset != $featset} {
+ WPCmd PEConfig feature $varname $formfeatset
+ }
+ }
+ }
+ }
+ if {[info exists subop]} {
+ switch -- $subop {
+ varhelp {
+ catch {WPCmd PEInfo unset help_context}
+ catch {WPCmd set oncancel $oncancel}
+ set help_vars [list topic topicclass]
+ set topic $varhelpname
+ set _cgi_uservar(topic) $varhelpname
+ set topicclass variable
+ set _cgi_uservar(topicclass) variable
+ set _cgi_uservar(oncancel) conf_process
+ set script help
+ }
+ feathelp {
+ catch {WPCmd PEInfo unset help_context}
+ catch {WPCmd set oncancel $oncancel}
+ set help_vars [list topic topicclass oncancel]
+ set topic $feathelpname
+ set _cgi_uservar(topic) $feathelpname
+ set topicclass feature
+ set _cgi_uservar(topicclass) feature
+ set _cgi_uservar(oncancel) conf_process
+ set script help
+ }
+ secthelp {
+ catch {WPCmd PEInfo unset help_context}
+ catch {WPCmd set oncancel $oncancel}
+ set help_vars [list topic topicclass oncancel]
+ set topic $feathelpname
+ set _cgi_uservar(topic) $feathelpname
+ set topicclass $topicclass
+ set _cgi_uservar(topicclass) $topicclass
+ set _cgi_uservar(oncancel) conf_process
+ set script help
+ }
+ save {
+ if {$cid != [WPCmd PEInfo key]} {
+ error [list _close "Invalid Operation ID"]
+ }
+ WPCmd PEConfig saveconf
+ set script $oncancel
+ catch {WPCmd PEInfo unset config_deftext}
+ }
+ }
+ }
+ }
+ filtconfig {
+ wpGetVar fno [list _INTEGER_]
+ wpGetVar subop [list edit add]
+
+ if {[catch {wpGetVar filtcancel}]} {
+ if {[catch {wpGetVar filthelp}] == 0} {
+ catch {WPCmd PEInfo unset help_context}
+ catch {WPCmd set oncancel $oncancel}
+
+ set patlist [wpGetRulePattern]
+ set actlist [wpGetRuleAction 0]
+ # we have to save this exactly as it would look when getting it from alpined
+ set ftsadd [expr {[string compare $subop "add"] == 0 ? 1 : 0}]
+ set ftsform [list [list "pattern" $patlist] [list "filtaction" $actlist]]
+ catch {WPCmd set filttmpstate [list $ftsadd $fno $ftsform]}
+
+ set help_vars [list topic]
+ set topic filtedit
+ set _cgi_uservar(topic) filtedit
+
+ if {[string compare $subop "edit"] == 0} {
+ set fakeimg "vle.filters.$fno"
+ set fakesz [expr {$fno + 1}]
+ } else {
+ set fakeimg "vla.filters"
+ set fakesz 1
+ }
+
+ set _cgi_uservar(oncancel) [WPPercentQuote "conf_process&wv=rule&filters-sz=${fakesz}&${fakeimg}.x=1&${fakeimg}.y=1&oncancel=main.tcl"]
+ set script help
+ } elseif {[set nv [numberedVar rmheader header_total]] >= 0} {
+
+ # load all the rules, process "headers"
+ foreach pat [wpGetRulePattern] {
+ set [lindex $pat 0] [lindex $pat 1]
+
+ if {[string compare headers [lindex $pat 0]] == 0} {
+ if {[llength $headers] > $nv} {
+ set headers [lreplace $headers $nv $nv]
+ }
+ }
+ }
+
+ # load all the actions
+ foreach act [wpGetRuleAction 0] {
+ set [lindex $act 0] [lindex $act 1]
+ }
+
+ # load other variables
+ wpGetVarAs nickname nickname
+ wpGetVarAs comment comment
+ wpGetVarAs folder folder
+ wpGetVarAs ftype ftype
+
+ set filterrtext 1
+ set filtedit_fno $fno
+ set filtedit_add [expr {[string compare $subop add] == 0 ? 1 : 0}]
+ set filtedit_onfiltcancel conf_process
+ set script "fr_filtedit.tcl"
+ } elseif {[catch {wpGetVar addheader}] == 0} {
+
+ # load all the rules, process "headers"
+ foreach pat [wpGetRulePattern] {
+ set [lindex $pat 0] [lindex $pat 1]
+ if {[string compare headers [lindex $pat 0]] == 0} {
+ foreach h [set headers [lindex $pat 1]] {
+ if {0 == [string length [lindex $h 0]]
+ && 0 == [string length [lindex $h 1]]} {
+ set emptyheader 1
+ }
+ }
+
+ if {![info exists emptyheader]} {
+ lappend headers [list {} {}]
+ }
+ }
+ }
+
+ # load all the actions
+ foreach act [wpGetRuleAction 0] {
+ set [lindex $act 0] [lindex $act 1]
+ }
+
+ # load other variables
+ wpGetVar nickname
+ wpGetVar comment
+ wpGetVarAs folder folder
+ wpGetVarAs ftype ftype
+
+ set filterrtext 1
+ set filtedit_fno $fno
+ set filtedit_add [expr {[string compare $subop add] == 0 ? 1 : 0}]
+ set filtedit_onfiltcancel conf_process
+ set script "fr_filtedit.tcl"
+ } else {
+ # load other variables
+ wpGetVar nickname
+ wpGetVar comment
+ wpGetVarAs folder folder
+ wpGetVarAs ftype ftype
+
+ set patlist [wpGetRulePattern]
+ set actlist [wpGetRuleAction 1]
+
+ lappend patlist [list nickname $nickname]
+ lappend patlist [list comment $comment]
+
+ set ret [catch {WPCmd PEConfig ruleset filter $subop $fno $patlist $actlist} res]
+ if {$ret} {
+ error [list _action "Filter Set" $res]
+ } elseif {[string length $res]} {
+ WPCmd PEInfo statmsg "Filter setting failed: $res"
+
+ set filtedit_fno $fno
+ set filtedit_add [expr {[string compare $subop add] == 0 ? 1 : 0}]
+ set filtedit_onfiltcancel conf_process
+ set script "fr_filtedit.tcl"
+ }
+ }
+ }
+ }
+ scoreconfig {
+ wpGetVar fno [list _INTEGER_]
+ wpGetVar subop [list edit add]
+
+ if {[catch {wpGetVar filtcancel}]} {
+ if {[catch {wpGetVar filthelp}] == 0} {
+ catch {WPCmd PEInfo unset help_context}
+ catch {WPCmd set oncancel $oncancel}
+ if {[string compare $subop "edit"] == 0 || [string compare $subop "add"] == 0} {
+ set patlist [wpGetRulePattern]
+
+ # we have to save this exactly as it would look when getting it from alpined
+ set ftsadd [expr {[string compare $subop "add"] == 0 ? 1 : 0}]
+ set ftsform [list [list "pattern" $patlist] [list "filtaction" $actlist]]
+ catch {WPCmd set filttmpstate [list $ftsadd $fno $ftsform]}
+ }
+ set help_vars [list topic]
+ set topic scoreedit
+ set _cgi_uservar(topic) scoreedit
+ switch -- $subop {
+ edit {
+ set fakeimg "vle.scores.$fno"
+ set fakesz [expr {$fno + 1}]
+ }
+ add {
+ set fakeimg "vla.scores"
+ set fakesz 1
+ }
+ }
+
+ set _cgi_uservar(oncancel) [WPPercentQuote "conf_process&wv=rule&scores-sz=${fakesz}&${fakeimg}.x=1&${fakeimg}.y=1&oncancel=main.tcl"]
+ set script help
+ } elseif {[set nv [numberedVar rmheader header_total]] >= 0} {
+
+ # load all the rules, process "headers"
+ foreach pat [wpGetRulePattern] {
+ set [lindex $pat 0] [lindex $pat 1]
+
+ if {[string compare headers [lindex $pat 0]] == 0} {
+ if {[llength $headers] > $nv} {
+ set headers [lreplace $headers $nv $nv]
+ }
+ }
+ }
+
+ # load other variables
+ wpGetVar nickname
+ wpGetVar comment
+ wpGetVarAs folder folder
+ wpGetVarAs ftype ftype
+
+ set filterrtext 1
+ set filtedit_score 1
+ set filtedit_fno $fno
+ set filtedit_add [expr {[string compare $subop add] == 0 ? 1 : 0}]
+ set filtedit_onfiltcancel conf_process
+ set script "fr_filtedit.tcl"
+ } elseif {[catch {wpGetVar addheader}] == 0} {
+
+ # load all the rules, process "headers"
+ foreach pat [wpGetRulePattern] {
+ set [lindex $pat 0] [lindex $pat 1]
+ if {[string compare headers [lindex $pat 0]] == 0} {
+ foreach h [set headers [lindex $pat 1]] {
+ if {0 == [string length [lindex $h 0]]
+ && 0 == [string length [lindex $h 1]]} {
+ set emptyheader 1
+ }
+ }
+
+ if {![info exists emptyheader]} {
+ lappend headers [list {} {}]
+ }
+ }
+ }
+
+ # load other variables
+ wpGetVar nickname
+ wpGetVar comment
+ wpGetVarAs folder folder
+ wpGetVarAs ftype ftype
+
+ set filterrtext 1
+ set filtedit_score 1
+ set filtedit_fno $fno
+ set filtedit_add [expr {[string compare $subop add] == 0 ? 1 : 0}]
+ set filtedit_onfiltcancel conf_process
+ set script "fr_filtedit.tcl"
+ } else {
+ switch -- $subop {
+ edit -
+ add {
+ # load other variables
+ wpGetVar nickname
+ wpGetVar comment
+ wpGetVarAs folder folder
+ wpGetVarAs ftype ftype
+
+ set patlist [wpGetRulePattern]
+
+ lappend patlist [list nickname $nickname]
+ lappend patlist [list comment $comment]
+
+ wpGetVar scoreval
+ lappend actlist [list "scoreval" $scoreval]
+
+ wpGetVar scorehdr
+ lappend actlist [list "scorehdr" $scorehdr]
+
+ set ret [catch {WPCmd PEConfig ruleset score $subop $fno $patlist $actlist} res]
+
+ if {$ret} {
+ error [list _action "Score Set" $res]
+ } elseif {[string length $res]} {
+ WPCmd PEInfo statmsg "Score setting failed: $res"
+
+ set filtedit_score 1
+ set filtedit_fno $fno
+ set filtedit_add [expr {[string compare $subop add] == 0 ? 1 : 0}]
+ set filtedit_onfiltcancel conf_process
+ set script "fr_filtedit.tcl"
+ }
+ }
+ }
+ }
+ }
+ }
+ indexcolorconfig {
+ wpGetVar fno [list _INTEGER_]
+ wpGetVar subop [list edit add]
+
+ if {[catch {wpGetVar filtcancel}]} {
+ if {[catch {wpGetVar filthelp}] == 0} {
+ catch {WPCmd PEInfo unset help_context}
+ catch {WPCmd set oncancel $oncancel}
+ if {[string compare $subop "edit"] == 0 || [string compare $subop "add"] == 0} {
+ set patlist [wpGetRulePattern]
+
+ # we have to save this exactly as it would look when getting it from alpined
+ set ftsadd [expr {[string compare $subop "add"] == 0 ? 1 : 0}]
+ set ftsform [list [list "pattern" $patlist] [list "filtaction" $actlist]]
+ catch {WPCmd set filttmpstate [list $ftsadd $fno $ftsform]}
+ }
+ set help_vars [list topic]
+ set topic indexcoloredit
+ set _cgi_uservar(topic) indexcoloredit
+ switch -- $subop {
+ edit {
+ set fakeimg "vle.indexcolor.$fno"
+ set fakesz [expr {$fno + 1}]
+ }
+ add {
+ set fakeimg "vla.indexcolor"
+ set fakesz 1
+ }
+ }
+
+ set _cgi_uservar(oncancel) [WPPercentQuote "conf_process&wv=rule&indexcolor-sz=${fakesz}&${fakeimg}.x=1&${fakeimg}.y=1&oncancel=main.tcl"]
+ set script help
+ } elseif {[set nv [numberedVar rmheader header_total]] >= 0} {
+
+ # load all the rules, process "headers"
+ foreach pat [wpGetRulePattern] {
+ set [lindex $pat 0] [lindex $pat 1]
+
+ if {[string compare headers [lindex $pat 0]] == 0} {
+ if {[llength $headers] > $nv} {
+ set headers [lreplace $headers $nv $nv]
+ }
+ }
+ }
+
+ # load other variables
+ wpGetVar nickname
+ wpGetVar comment
+ wpGetVarAs folder folder
+ wpGetVarAs ftype ftype
+
+ set filterrtext 1
+ set filtedit_indexcolor 1
+ set filtedit_fno $fno
+ set filtedit_add [expr {[string compare $subop add] == 0 ? 1 : 0}]
+ set filtedit_onfiltcancel conf_process
+ set script "fr_filtedit.tcl"
+ } elseif {[catch {wpGetVar addheader}] == 0} {
+ # load all the rules, process "headers"
+ foreach pat [wpGetRulePattern] {
+ set [lindex $pat 0] [lindex $pat 1]
+ if {[string compare headers [lindex $pat 0]] == 0} {
+ foreach h [set headers [lindex $pat 1]] {
+ if {0 == [string length [lindex $h 0]]
+ && 0 == [string length [lindex $h 1]]} {
+ set emptyheader 1
+ }
+ }
+
+ if {![info exists emptyheader]} {
+ lappend headers [list {} {}]
+ }
+ }
+ }
+
+ # load other variables
+ wpGetVar nickname
+ wpGetVar comment
+ wpGetVarAs folder folder
+ wpGetVarAs ftype ftype
+
+ set filterrtext 1
+ set filtedit_indexcolor 1
+ set filtedit_fno $fno
+ set filtedit_add [expr {[string compare $subop add] == 0 ? 1 : 0}]
+ set filtedit_onfiltcancel conf_process
+ set script "fr_filtedit.tcl"
+ } elseif {[catch {cgi_import_as colormap.x colx}] == 0
+ && [catch {cgi_import_as colormap.y coly}] == 0} {
+ set rgbs {"000" "051" "102" "153" "204" "255"}
+ set xrgbs {"00" "33" "66" "99" "CC" "FF"}
+ set rgblen [llength $rgbs]
+ set imappixwidth 10
+
+ set colx [expr {${colx} / $imappixwidth}]
+ set coly [expr {${coly} / $imappixwidth}]
+ if {($coly >= 0 && $coly < $rgblen)
+ && ($colx >= 0 && $colx < [expr {$rgblen * $rgblen}])} {
+ set ired $coly
+ set igreen [expr {($colx / $rgblen) % $rgblen}]
+ set iblue [expr {$colx % $rgblen}]
+ set rgb "[lindex $rgbs $ired],[lindex $rgbs ${igreen}],[lindex $rgbs ${iblue}]"
+ set xrgb "[lindex $xrgbs $ired][lindex $xrgbs ${igreen}][lindex $xrgbs ${iblue}]"
+
+ if {[catch {wpGetVar fgorbg [list fg bg]}]} {
+ WPCmd PEInfo statmsg "Invalid fore/back ground input!"
+ catch {unset xrgb}
+ }
+ } else {
+ WPCmd PEInfo statmsg "Invalid RGB Input!"
+ }
+
+ # relay any other config changes
+ wpGetVar nickname
+ wpGetVar comment
+ wpGetVarAs folder folder
+ wpGetVarAs ftype ftype
+ foreach pat [wpGetRulePattern] {
+ set [lindex $pat 0] [lindex $pat 1]
+ }
+
+ # import previous settings
+ wpGetVarAs fg fg
+ wpGetVarAs bg bg
+
+ # set new value
+ if {[info exists xrgb]} {
+ set $fgorbg $xrgb
+ }
+
+ set filterrtext 1
+ set filtedit_indexcolor 1
+ set filtedit_fno $fno
+ set filtedit_add [expr {[string compare $subop add] == 0 ? 1 : 0}]
+ set filtedit_onfiltcancel conf_process
+ set script "fr_filtedit.tcl"
+ } else {
+ switch -- $subop {
+ edit -
+ add {
+
+ wpGetVar nickname
+ wpGetVar comment
+
+ set patlist [wpGetRulePattern]
+
+ lappend patlist [list nickname $nickname]
+ lappend patlist [list comment $comment]
+
+ # save config?
+ set actlist {}
+ if {[catch {wpGetVar fg}] == 0 && [catch {wpGetVar bg}] == 0} {
+ lappend actlist [list fg $fg]
+ lappend actlist [list bg $bg]
+
+ # save rule
+ set ret [catch {WPCmd PEConfig ruleset indexcolor $subop $fno $patlist $actlist} res]
+ if {$ret} {
+ error [list _action "Color Set Error" $res]
+ } elseif {[string length $res]} {
+ WPCmd PEInfo statmsg "Index Color setting failed: $res"
+
+ set filtedit_indexcolor 1
+ set filtedit_fno $fno
+ set filtedit_add [expr {[string compare $subop add] == 0 ? 1 : 0}]
+ set filtedit_onfiltcancel conf_process
+ set script "fr_filtedit.tcl"
+ }
+ } else {
+ error [list _action "Unset FG/BG" "Internal Error: Unset Color Variables"]
+ }
+ }
+ }
+ }
+ }
+ }
+ clconfig {
+ wpGetVar cl
+ wpGetVar nick
+ wpGetVar server
+ wpGetVar user
+ wpGetVar stype
+ wpGetVar path
+ wpGetVar view
+ wpGetVar add
+ wpGetVarAs cle_cancel.x cle_cancel
+ wpGetVarAs cle_save.x cle_save
+
+ set cledit_add $add
+ set cledit_cl $cl
+ set cledit_onclecancel conf_process
+ if {[string length $cle_save]} {
+ if {[catch {cgi_import_as "ssl" sslval}]} {
+ set ssl 0
+ } else {
+ if {[string compare $sslval on] == 0} {
+ set ssl 1
+ } else {
+ set ssl 0
+ }
+ }
+ regexp "\{?(\[^\}\]*)\}?(.*)" $server match serverb serverrem
+ if {[string length $serverb]} {
+ if {$ssl == 1} {
+ set serverb "$serverb/ssl"
+ }
+ if {[string compare "" "$user"]} {
+ set serverb "$serverb/user=$user"
+ }
+ if {[string compare "imap" [string tolower $stype]]} {
+ set serverb "$serverb/[string tolower $stype]"
+ }
+ if {[string compare "nntp" [string tolower $stype]] == 0} {
+ regsub -nocase {^(#news\.)?(.*)$} "$path" "#news.\\2" path
+ if {[string compare "" $path] == 0} {
+ set path "#news."
+ }
+ }
+ set result ""
+ set ret 0
+ set servera "\{$serverb\}$serverrem"
+ if {$add} {
+ set ret [catch {WPCmd PEConfig cladd $cl $nick $servera $path $view} result]
+ } else {
+ set ret [catch {WPCmd PEConfig cledit $cl $nick $servera $path $view} result]
+ }
+ if {$ret != 0} {
+ error [list _action "Collection List Set" $result]
+ } elseif {[string compare "" $result]} {
+ if {$add} {
+ set clerrtext "Add failed: $result"
+ } else {
+ set clerrtext "Edit failed: $result"
+ }
+ WPCmd PEInfo statmsg $clerrtext
+ set script "fr_cledit.tcl"
+ }
+ } else {
+ set clerrtext "Bad data: Nothing defined for Server"
+ WPCmd PEInfo statmsg $clerrtext
+ set script "fr_cledit.tcl"
+ }
+ }
+ }
+ noop {
+ catch {WPCmd PEInfo noop}
+ }
+ cancel {
+ set script $oncancel
+ catch {WPCmd unset conf_page} res
+ }
+ default {
+ error [list _close "Unknown process operation: $op"]
+ }
+ }
+
+source [WPTFScript $script]
diff --git a/web/cgi/alpine/1.0/detach.tcl b/web/cgi/alpine/1.0/detach.tcl
new file mode 100755
index 00000000..d53ef313
--- /dev/null
+++ b/web/cgi/alpine/1.0/detach.tcl
@@ -0,0 +1,183 @@
+#!./tclsh
+# $Id: detach.tcl 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006-2007 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# detach.tcl
+#
+# Purpose: CGI script to retrieve requested attachment
+#
+# Input:
+set detach_vars {
+ {uid "Unknown Message UID"}
+ {part "Unknown Message Part"}
+ {download "" 0}
+}
+
+#set detach_via_ip_address 1
+#set detach_via_local_hostname 1
+
+# inherit global config
+source ./alpine.tcl
+
+proc WPServerIP {} {
+ global _wp
+
+ catch {
+ set ip 127.0.0.1
+ set sid [socket -async [info hostname] [expr {([string length $_wp(serverport)]) ? $_wp(serverport) : 80}]]
+ set ip [lindex [ fconfigure $sid -sockname ] 0]
+ close $sid
+ }
+
+ return $ip
+}
+
+WPEval $detach_vars {
+ if {[info exists env(PATH_INFO)]} {
+ if {[string index $env(PATH_INFO) 0] == "/"} {
+ set s [string range $env(PATH_INFO) 1 end]
+ if {[set i [string first "/" $s]] >= 0} {
+ set uid [string range $s 0 [expr {$i - 1}]]
+ set s [string range $s [incr i] end]
+ if {[set i [string first "/" $s]] >= 0} {
+ set part [string range $s 0 [expr {$i - 1}]]
+ }
+ }
+ }
+ }
+
+ if {[info exists uid] == 0 || [info exists part] == 0} {
+ error [list _action "Unspecified attachment UID or Part" "Please close this window."]
+ }
+
+ # generate big random string to reference the thing
+
+ # generate filenames to hold detached data and control file
+ for {set n 0} {1} {incr n} {
+
+ set rhandle [WPCmd PESession random 64]
+ set cfile [file join $_wp(fileroot) $_wp(detachpath) detach.${rhandle}-control]
+ set dfile [file join $_wp(fileroot) $_wp(detachpath) detach.${rhandle}-data]
+
+ if {[file exists $cfile] == 0 && [file exists $dfile] == 0} {
+ if {[catch {open $cfile {RDWR CREAT EXCL} [cgi_tmpfile_permissions]} cfd]} {
+ error [list _action Detach "Cannot create control file: [cgi_quote_html $cfd]" "Please close this window"]
+ } else {
+ exec echo ${rhandle}-control | [file join $_wp(cgipath) $_wp(appdir) whackatch.tcl] >& /dev/null &
+ }
+
+ if {[catch {open $dfile {RDWR CREAT EXCL} [cgi_tmpfile_permissions]} dfd]} {
+ catch {close $cfd}
+ error [list _action Detach "Cannot create command file: [cgi_quote_html $dfd]" "Please close this window"]
+ } else {
+ exec echo ${rhandle}-data | [file join $_wp(cgipath) $_wp(appdir) whackatch.tcl] >& /dev/null &
+ }
+
+ # exec chmod [cgi_tmpfile_permissions] $cfile
+ # exec chmod [cgi_tmpfile_permissions] $dfile
+ break
+ } elseif {$n > 4} {
+ error [list _action Detach "Command file creation limit" "Please close this window"]
+ }
+ }
+
+ if {[catch {WPCmd PEMessage $uid detach $part $dfile} attachdata]} {
+ error [list _action Detach $attachdata "Please close this window"]
+ }
+
+ if {[info exists detach_via_ip_address]} {
+ if {[regsub {^(http[s]?://)[A-Za-z0-9\\-\\.]+(.*)$} "[cgi_root]/pub/getach.tcl" "\\1\[[WPServerIP]\]\\2" redirect] != 1} {
+ error [list _action Detach "Cannot determine server address" "Please close this window"]
+ }
+ } elseif {[info exists detach_via_local_hostname]} {
+ if {[regsub {^(http[s]?://)[A-Za-z0-9\\-\\.]+(.*)$} "[cgi_root]/pub/getach.tcl" "\\1\[[info hostname]\]\\2" redirect] != 1} {
+ error [list _action Detach "Cannot determine server address" "Please close this window"]
+ }
+ } else {
+ set redirect "[cgi_root]/pub/getach.tcl"
+ }
+
+ set mimetype [lindex $attachdata 0]
+ set mimesubtype [lindex $attachdata 1]
+ set contentlength [lindex $attachdata 2]
+ set givenname [lindex [lindex $attachdata 3] 0]
+ set tmpfile [lindex $attachdata 4]
+
+ if {[string compare $tmpfile $dfile]} {
+ set straytmp "&straytmp=1"
+ } else {
+ set straytmp ""
+ }
+
+ if {![string length $givenname]} {
+ set givenname "attachment"
+ switch -regexp $mimetype {
+ ^[Tt][Ee][Xx][Tt]$ {
+ switch -regexp $mimesubtype {
+ ^[Pp][Ll][Aa][Ii][Nn]$ {
+ set givenname "attached.txt"
+ }
+ ^[Hh][Tt][Mm][Ll]$ {
+ set givenname "attached.html"
+ }
+ }
+ }
+ }
+ }
+
+ set safegivenname $givenname
+ regsub -all {[/]} $safegivenname {-} safegivenname
+ regsub -all {[ ]} $safegivenname {_} safegivenname
+ regsub -all {[\?]} $safegivenname {X} safegivenname
+ regsub -all {[&]} $safegivenname {X} safegivenname
+ regsub -all {[#]} $safegivenname {X} safegivenname
+ regsub -all {[=]} $safegivenname {X} safegivenname
+ set safegivenname "/[WPPercentQuote $safegivenname {.}]"
+
+ if {$download == 1} {
+ puts $cfd "Content-type: Application/X-Download"
+ puts $cfd "Content-Disposition: attachment; filename=\"$givenname\""
+ } else {
+ puts $cfd "Content-type: ${mimetype}/${mimesubtype}"
+ }
+
+ # side-step the cgi_xxx stuff in this special case because
+ # we don't want to buffer up the downloading attachment...
+
+ puts $cfd "Content-Length: $contentlength"
+ puts $cfd "Expires: [clock format [expr {[clock seconds] + 3600}] -f {%a, %d %b %Y %H:%M:%S GMT} -gmt true]"
+ puts $cfd "Cache-Control: max-age=3600"
+ puts $cfd ""
+
+ puts $cfd $tmpfile
+
+ # exec chmod [cgi_tmpfile_permissions] $tmpfile
+
+ close $cfd
+
+ # prepare to clean up if the brower never redirects
+
+ cgi_http_head {
+ # redirect to the place we stuffed the detach info. use the ip address
+ # to foil spilling any session cookies or the like
+ #cgi_redirect ${redirect}${safegivenname}?h=${rhandle}
+
+ if {[info exists env(SERVER_PROTOCOL)] && [regexp {[Hh][Tt][Tt][PP]/([0-9]+)\.([0-9]+)} $env(SERVER_PROTOCOL) m vmaj vmin] && $vmaj >= 1 && $vmin >= 1} {
+ cgi_puts "Status: 303 Temporary Redirect"
+ } else {
+ cgi_puts "Status: 302 Redirected"
+ }
+
+ cgi_puts "URI: ${redirect}${safegivenname}?h=${rhandle}${straytmp}"
+ cgi_puts "Location: ${redirect}${safegivenname}?h=${rhandle}${straytmp}"
+ }
+}
diff --git a/web/cgi/alpine/1.0/do_help.tcl b/web/cgi/alpine/1.0/do_help.tcl
new file mode 100644
index 00000000..400b3af2
--- /dev/null
+++ b/web/cgi/alpine/1.0/do_help.tcl
@@ -0,0 +1,48 @@
+# $Id: do_help.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+cgi_http_head {
+ WPStdHttpHdrs
+}
+
+cgi_html {
+ if {![string length $topic]} {
+ if {[catch {WPCmd PEInfo set help_context} s] == 0} {
+ set topic $s
+ catch {WPCmd PEInfo unset help_context}
+ }
+ }
+
+ cgi_head {
+ cgi_title "Alpine Help"
+ }
+
+ cgi_frameset "cols=112,*" frameborder=0 framespacing=0 border=0 {
+ set parms ""
+
+ foreach v $help_vars {
+ set val [subst $[lindex $v 0]]
+ if {[string length $val]} {
+ if {[string length $parms]} {
+ append parms "&"
+ } else {
+ append parms "?"
+ }
+
+ append parms "[lindex $v 0]=$val"
+ }
+ }
+
+ cgi_frame bodindx=helpindex.tcl$parms title="Help Navigation"
+ cgi_frame bodtext=helpbody.tcl$parms title="Help Text"
+ }
+}
diff --git a/web/cgi/alpine/1.0/do_open.tcl b/web/cgi/alpine/1.0/do_open.tcl
new file mode 100755
index 00000000..22599a97
--- /dev/null
+++ b/web/cgi/alpine/1.0/do_open.tcl
@@ -0,0 +1,126 @@
+# $Id: do_open.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+if {$cid != [WPCmd PEInfo key]} {
+ error [list _action open "Invalid Operation ID" "Click Back button to try again."]
+}
+
+if {[catch {WPLoadCGIVar cancel}] == 0 && [string compare Cancel $cancel] == 0} {
+ catch {WPCmd PEInfo statmsg "Authentication Cancelled"}
+ cgi_http_head {
+ cgi_redirect [cgi_root]/$_wp(appdir)/$_wp(ui1dir)/wp.tcl?page=${oncancel}.tcl
+ }
+} else {
+
+ if {[catch {WPLoadCGIVar path}]} {
+ WPLoadCGIVar colid
+ WPLoadCGIVar folder
+ set path [list $colid $folder]
+ } else {
+ set colid [lindex $path 0]
+ set folder [lrange $path 1 end]
+ }
+
+ if {[info exists src] == 0 && [catch {eval WPCmd PEMailbox open $path} reason]} {
+ if {[string first ": no such folder" $reason] > 0} {
+ catch {WPCmd PEInfo statmsg "Folder [lrange $path 1 end] doesn't exist"}
+ set src $oncancel
+ } elseif {[string compare BADPASSWD [string range $reason 0 8]] == 0 || [string compare $reason "Login Error"] == 0} {
+ # control error messages
+ set statmsgs [WPCmd PEInfo statmsgs]
+ WPCmd PEMailbox newmailreset
+ if {[catch {WPCmd PESession creds $colid $folder} creds] == 0 && $creds != 0} {
+ catch {
+ WPCmd PEInfo statmsg "Invalid Username or Password"
+ WPCmd PESession nocred $colid $folder
+ }
+ }
+
+ if {[catch {WPCmd PEFolder clextended} coln]} {
+ WPCmd set reason "Can't get Collection Info: $coln"
+ } else {
+ set coln [lindex $coln $colid]
+ if {[regexp {^([a-zA-Z\.]+).*\/user=([^ /]*)} [lindex $coln 4] dummy srvname authuser]} {
+ WPCmd set reason "Opening folder [cgi_bold $folder] first requires that you log in o the server [cgi_bold "$srvname"]."
+ WPCmd set authuser $authuser
+ } elseif {[WPCmd PEFolder isincoming $colid]} {
+ WPCmd set reason "Incoming folder [cgi_bold $folder] requires you log into the the server."
+ } else {
+ WPCmd set reason "Opening [cgi_bold $folder] in [cgi_bold [lindex $coln 1]] requires you log into the the server."
+ }
+ }
+
+ WPCmd set cid [WPCmd PEInfo key]
+ WPCmd set authcol $colid
+ WPCmd set authfolder $folder
+ WPCmd set authpage [WPPercentQuote "[cgi_root]/$_wp(appdir)/$_wp(ui1dir)/open.tcl?folder=${folder}&colid=${colid}"]
+ WPCmd set authcancel [WPPercentQuote "[cgi_root]/$_wp(appdir)/$_wp(ui1dir)/wp.tcl?page=folders"]
+
+ set src [file join $_wp(cgipath) $_wp(appdir) $_wp(ui1dir) fr_queryauth.tcl]
+
+ } else {
+ catch {WPCmd PEInfo statmsg "Cannot open $path: $reason"}
+ set src folders
+ }
+ } else {
+
+ # manage caching last folder opened
+ if {0 == [catch {WPCmd set wp_open_folder} last_folder]} {
+ WPTFAddFolderCache [lindex $last_folder 0] [lindex $last_folder 1]
+ }
+
+ catch {WPCmd set wp_open_folder [list $colid $folder]}
+
+ # start with the message indicated by the
+ # incoming-startup-rule' in the current index
+ set firstmsg 1
+ if {![catch {WPCmd PEMailbox firstinteresting} firstint] && $firstint > 0} {
+ set messagecount [WPCmd PEMailbox messagecount]
+ if {[catch {WPCmd PEInfo indexlines} ppg] || $ppg == 0} {
+ set ppg $_wp(indexlines)
+ }
+
+ for {set i 1} {$i < $messagecount} {incr i $ppg} {
+ if {$i >= $firstint} {
+ break
+ }
+
+ set firstmsg $i
+ }
+
+ # show whole last page
+ if {$firstmsg + $ppg > $messagecount} {
+ if {[set n [expr {($messagecount + 1) - $ppg}]] > 0} {
+ set firstmsg $n
+ } else {
+ set firstmsg 1
+ }
+ }
+ }
+
+ if {[catch {WPCmd PEMailbox uid $firstmsg} exp]} {
+ set exp 1
+ }
+
+ WPCmd set first $firstmsg
+ WPCmd set top $exp
+ WPCmd set uid $exp
+
+ WPCmd set width $_wp(width)
+ WPCmd set wp_spec_script fr_index.tcl
+ set src main.tcl
+ }
+}
+
+if {[info exists src]} {
+ source [WPTFScript $src]
+}
diff --git a/web/cgi/alpine/1.0/do_quit.tcl b/web/cgi/alpine/1.0/do_quit.tcl
new file mode 100755
index 00000000..0c013e4b
--- /dev/null
+++ b/web/cgi/alpine/1.0/do_quit.tcl
@@ -0,0 +1,98 @@
+#!./tclsh
+# $Id: do_quit.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# queryquit.tcl
+#
+# Purpose: CGI script to handle quit query, either redirecting
+# to session logout or returning to message listn
+#
+# Input:
+set quit_vars {
+ {cid "Command ID"}
+ {quit {} ""}
+ {expinbox {} 0}
+ {expcurrent {} 0}
+ {cancel {} ""}
+}
+
+# Output:
+#
+# HTML/Javascript/CSS data representing the message specified
+# by the 'uid' argument
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+WPEval $quit_vars {
+ if {$cid != [WPCmd PEInfo key]} {
+ error "Invalid Command ID"
+ }
+
+ switch -regexp -- $quit {
+ "^Yes, .*" {
+
+ set exps ""
+
+ if {[string compare $expinbox "on"] == 0} {
+ append exps "&expinbox=1"
+ }
+
+ if {[string compare $expcurrent "on"] == 0} {
+ append exps "&expcurrent=1"
+ }
+
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ cgi_http_equiv Refresh "0; url=$_wp(serverpath)/session/logout.tcl?cid=[WPCmd PEInfo key]&sessid=${sessid}${exps}"
+ }
+
+ cgi_body {
+ cgi_table height="20%" {
+ cgi_table_row {
+ cgi_table_data {
+ cgi_puts [cgi_nbspace]
+ }
+ }
+ }
+
+ cgi_center {
+ cgi_table border=0 width=500 cellpadding=3 {
+ cgi_table_row {
+ cgi_table_data align=center rowspan=2 {
+ cgi_put [cgi_imglink logo]
+ }
+
+ cgi_table_data rowspan=2 {
+ put [nbspace]
+ put [nbspace]
+ }
+
+ cgi_table_data {
+ cgi_puts [cgi_font size=+2 face=Helvetica "Quitting Alpine ..."]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ default {
+ source [WPTFScript main]
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/do_view.tcl b/web/cgi/alpine/1.0/do_view.tcl
new file mode 100755
index 00000000..fb8c5af6
--- /dev/null
+++ b/web/cgi/alpine/1.0/do_view.tcl
@@ -0,0 +1,190 @@
+# $Id: do_view.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# do_view.tcl
+#
+# Purpose: CGI script to serve as the frame-work for including
+# supplied script snippets that generate the various
+# javascript-free webpine pages
+#
+# Input:
+set view_vars {
+ {uid {} 0}
+ {op {} ""}
+ {f_colid {} ""}
+ {f_name {} ""}
+ {savecancel {} ""}
+ {sid {} ""}
+ {auths {} 0}
+ {user {} ""}
+ {pass {} ""}
+ {create {} 0}
+}
+
+## read vars
+foreach item $view_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} errstr]} {
+ error [list _action "Impart Variable" $errstr]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+WPCmd PEInfo set wp_spec_script fr_view.tcl
+
+proc statmsg {msg} {
+ catch {WPCmd PEInfo statmsg $msg}
+}
+
+if {$uid} {
+ # commit to servlet, view will retrieve it
+ WPCmd set uid $uid
+}
+
+set uidparm ""
+
+# If there's an "op" (meaning we got here from a comview.tcl
+# reference) trust "uid" is set within alpined's interpreter.
+if {[string length $op]} {
+# Set "op" only after we've concluded the folder's ready
+# for view.tcl to do the actual copy
+ if {[string compare save [string tolower $op]]} {
+ append uidparm "&op=$op"
+ }
+} else {
+ append uidparm "&uid=$uid"
+}
+
+if {[string length $savecancel]} {
+ append uidparm "&savecancel=$savecancel"
+}
+
+
+# handle doing the actual save mechanics here rather than
+# view.tcl since the result will have to be reflected
+# in comview.tcl's Save dropdown.
+if {[string compare save [string tolower $op]] == 0} {
+ if {[string length $savecancel] == 0} {
+ if {[string length [set folder [string trim $f_name]]]} {
+ switch -exact -- $folder {
+ __folder__prompt__ {
+ set uid 0
+ _cgi_set_uservar onselect {fr_view op=save}
+ _cgi_set_uservar oncancel fr_view
+ _cgi_set_uservar target spec
+ _cgi_set_uservar controls 0
+ source [WPTFScript savecreate]
+ set nopage 1
+ }
+ __folder__list__ {
+ set uid 0
+ _cgi_set_uservar onselect {fr_view op=save}
+ _cgi_set_uservar oncancel fr_view
+ _cgi_set_uservar target spec
+ _cgi_set_uservar controls 0
+ source [WPTFScript savebrowse]
+ set nopage 1
+ }
+ default {
+ if {$uid > 0} {
+ if {$auths} {
+ catch {WPCmd PESession nocred $f_colid $folder}
+ if {[catch {WPCmd PESession creds $f_colid $folder $user $pass} result]} {
+ statmsg "Cannot set credentials ($f_colid) $folder: result"
+ }
+ }
+
+ if {[catch {WPCmd PEFolder exists $f_colid $folder} reason]} {
+ if {[string compare BADPASSWD [string range $reason 0 8]] == 0} {
+ set oncancel "view.tcl&uid=$uid&savecancel=1"
+ set conftext "Create Folder '$folder'?"
+ lappend params [list page fr_view]
+ lappend params [list uid $uid]
+ lappend params [list op save]
+ lappend params [list f_name $folder]
+ lappend params [list f_colid $f_colid]
+ source [WPTFScript auth]
+ set nopage 1
+ } else {
+ statmsg "Existance test failed: $reason"
+ }
+ } elseif {$reason == 0} {
+ if {$create == 1 || [string compare create [string tolower $create]] == 0} {
+ if {[catch {WPCmd PEFolder create $f_colid $folder} reason]} {
+ statmsg "Create failed: $reason"
+ } else {
+ set dosave 1
+ }
+ } else {
+ #set oncancel "view&uid=$uid&savecancel=1"
+ set qstate [list $folder]
+ set params [list [list page fr_view]]
+ lappend params [list uid $uid]
+ lappend params [list sid [clock seconds]]
+ lappend params [list op save]
+ lappend params [list f_name $folder]
+ lappend params [list f_colid $f_colid]
+ lappend qstate $params
+
+ if {[catch {WPCmd PEInfo set querycreate_state $qstate}] == 0} {
+ source [WPTFScript querycreate]
+ set nopage 1
+ } else {
+ statmsg "Error saving creation state"
+ }
+ }
+ } else {
+ set dosave 1
+ }
+
+ if {[info exists dosave]} {
+ append uidparm "&op=save"
+ }
+ } else {
+ statmsg "Cannot Save unknown message ID"
+ }
+ }
+ }
+ } else {
+ statmsg "Cannot Save to emtpy folder name"
+ }
+ }
+}
+
+
+if {![info exists nopage]} {
+ cgi_http_head {
+ WPStdHttpHdrs {} 60
+ }
+
+ cgi_html {
+ cgi_head {
+ }
+
+ cgi_frameset "rows=38,*" border=0 frameborder=0 framespacing=0 {
+
+ if {[string length $f_colid] && [string length $f_name]} {
+ set parms "&f_colid=${f_colid}&f_name=[WPPercentQuote ${f_name}]"
+ if {[string length $sid]} {
+ append parms "&sid=$sid"
+ }
+ } else {
+ set parms ""
+ }
+
+ cgi_frame cmds=comview.tcl?c=[WPCmd PEInfo key]${parms} scrolling=no title="Message Commands"
+ cgi_frame body=wp.tcl?page=view${uidparm}${parms} title="Message Text"
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/docancel.tcl b/web/cgi/alpine/1.0/docancel.tcl
new file mode 100755
index 00000000..e3d0aab5
--- /dev/null
+++ b/web/cgi/alpine/1.0/docancel.tcl
@@ -0,0 +1,56 @@
+# $Id: docancel.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# post.tcl
+#
+# Purpose: CGI script to perform message posting via compose.tcl
+# generated form
+#
+# Input:
+set post_vars {
+ {cid "Missing Command ID"}
+ {postpost "" main}
+}
+
+# Output:
+#
+
+## read vars
+foreach item $post_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} result]} {
+ error [list _action "Impart Variable" $result]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+if {$cid != [WPCmd PEInfo key]} {
+ error [list _action Postpone "Invalid Operation ID" "Click Back button to try again."]
+}
+
+# clean up attachments
+WPCmd PEInfo statmsg "Message cancelled"
+catch {WPCmd PEInfo unset suspended_composition}
+
+source [WPTFScript $postpost]
diff --git a/web/cgi/alpine/1.0/dosend.tcl b/web/cgi/alpine/1.0/dosend.tcl
new file mode 100755
index 00000000..44130572
--- /dev/null
+++ b/web/cgi/alpine/1.0/dosend.tcl
@@ -0,0 +1,99 @@
+# $Id: dosend.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# post.tcl
+#
+# Purpose: CGI script to perform message posting via compose.tcl
+# generated form
+#
+# Input:
+set post_vars {
+ {cid "Missing Command ID"}
+ {postpost "" "main.tcl"}
+ {user "" ""}
+ {pass "" ""}
+ {server "" ""}
+}
+
+# Output:
+#
+
+## read vars
+foreach item $post_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} result]} {
+ error [list _action "Impart Variable" $result]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+if {$cid != [WPCmd PEInfo key]} {
+ error [list _action Postpone "Invalid Operation ID" "Click Back button to try again."]
+}
+
+if {[string length $user] && [string length $pass] && [string length $server]} {
+ set cclientname "\{$server\}"
+ catch {WPCmd PESession nocred 0 $cclientname}
+ if {[catch {WPCmd PESession creds 0 $cclientname $user $pass} result]} {
+ WPCmd PEInfo statmsg "Cannot set credentials for $server"
+ }
+}
+
+if {[catch {WPCmd PEInfo set suspended_composition} msgdata] == 0} {
+ if {[catch {WPCmd PECompose post $msgdata} errstr]} {
+ if {[string compare BADPASSWD [string range $errstr 0 8]] == 0
+ && [catch {WPCmd PEInfo set suspended_composition $msgdata} errstr] == 0} {
+ set oncancel "page=compose&restore=1&cid=$cid"
+ set conftext "Send Messsage?"
+ set reason "The server used to send this message requires authentication.[cgi_nl][cgi_nl]Enter Username and Password to connect to $server"
+ _cgi_set_uservar params [WPPercentQuote [list [list server $server] [list page dosend] [list postpost $postpost]]]
+ set src auth
+ } else {
+ # regurgitate the compose window
+ _cgi_set_uservar style ""
+ #set style ""
+ set title "Send Error: [cgi_font class=notice "$errstr"]"
+ if {[string length $errstr]} {
+ set notice "Send FAILED: $errstr"
+ } else {
+ set notice "Send FAILED: [WPCmd PEInfo statmsg]"
+ }
+ WPCmd PEInfo statmsg "$notice"
+
+ if {[info exists attachments]} {
+ set a [split $attachments ","]
+ unset attachments
+ foreach id $a {
+ # id file size type/subtype
+ if {[catch {WPCmd PECompose attachinfo $id} result]} {
+ WPCmd PEInfo statmsg $result
+ } else {
+ lappend attachments [list $id [lindex $result 0] [lindex $result 1] [lindex $result 2]]
+ }
+ }
+ }
+
+ set src compose
+ }
+ } else {
+ catch {WPCmd PEInfo unset suspended_composition}
+ WPCmd PEInfo statmsg "Message Sent!"
+ set src $postpost
+ }
+} else {
+ WPCmd PEInfo statmsg "Internal Error: $msgdata"
+ set src $postpost
+}
+
+source [WPTFScript $src]
diff --git a/web/cgi/alpine/1.0/export.tcl b/web/cgi/alpine/1.0/export.tcl
new file mode 100755
index 00000000..338e928c
--- /dev/null
+++ b/web/cgi/alpine/1.0/export.tcl
@@ -0,0 +1,165 @@
+#!./tclsh
+# $Id: export.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# export.tcl
+#
+# Purpose: CGI script to download exported folder
+#
+# Input:
+set export_vars {
+ {cid "Unknown Command ID"}
+ {fid "Unknown Collection ID"}
+}
+
+#set export_via_ip_address 1
+#set export_via_local_hostname 1
+
+# inherit global config
+source ./alpine.tcl
+
+set mailextension ".mbx"
+
+proc WPServerIP {} {
+ global _wp
+
+ catch {
+ set ip 127.0.0.1
+ set sid [socket -async [info hostname] [expr {([string length $_wp(serverport)]) ? $_wp(serverport) : 80}]]
+ set ip [lindex [ fconfigure $sid -sockname ] 0]
+ close $sid
+ }
+
+ return $ip
+}
+
+
+WPEval $export_vars {
+ # generate filenames to hold exported folder and control file
+ for {set n 0} {1} {incr n} {
+
+ set rhandle [WPCmd PESession random 64]
+ set cfile [file join $_wp(detachpath) detach.${rhandle}.control]
+ set dfile [file join $_wp(detachpath) detach.${rhandle}.data]
+
+ if {[file exists $cfile] == 0 && [file exists $dfile] == 0} {
+ if {[catch {open $cfile {RDWR CREAT EXCL} [cgi_tmpfile_permissions]} cfd]
+ || [catch {open $dfile {RDWR CREAT EXCL} [cgi_tmpfile_permissions]} dfd]} {
+ if {[info exists dfd]} {
+ catch {close $cfd}
+ catch {file delete -force $cfile}
+ set errstr $dfd
+ } else {
+ set errstr $cfd
+ }
+
+ error [list _action Export "Cannot create command/control files: [cgi_quote_html $errstr]" "Please close this window"]
+ } else {
+ close $dfd
+ break
+ }
+ } elseif {$n > 4} {
+ error [list _action Export "Command file creation limit" "Please close this window"]
+ }
+ }
+
+ set colid [lindex $fid 0]
+ set fldr [eval "file join [lrange $fid 1 end]"]
+
+ catch {file delete $dfile}
+
+ if {[catch {WPCmd PEFolder export $colid $fldr $dfile} result]} {
+ WPCmd PEInfo statmsg $result
+ } else {
+ if {[set dfilesize [file size $dfile]] > 0
+ && ([info exists _wp(uplim_bytes)] && $_wp(uplim_bytes) > 0)
+ && $dfilesize > $_wp(uplim_bytes)} {
+ if {$_wp(uplim_bytes) > (1000000)} {
+ set dfs [format {%s.%.2s MB} [WPcomma [expr {$dfilesize / 1000000}]] [expr {$dfilesize % 1000000}]]
+ set esl [format {%s.%.2s MB} [WPcomma [expr {$_wp(uplim_bytes) / 1000000}]] [expr {$_wp(uplim_bytes) % 1000000}]]
+ } else {
+ set dfs "[WPcomma $dfs] KB"
+ set esl "[WPcomma $_wp(uplim_bytes)] KB"
+ }
+
+ WPCmd PEInfo statmsg "Exported folder size ($dfs) exceeds the maximum ($esl) size that can be imported.<br>If you wish to import this folder back into Web Alpine at a later time,<br>you should break it up into smaller folders"
+ }
+
+ if {[info exists export_via_ip_address]} {
+ if {[regsub {^(http[s]?://)[A-Za-z0-9\\-\\.]+(.*)$} "[cgi_root]/pub/getach.tcl" "\\1\[[WPServerIP]\]\\2" redirect] != 1} {
+ WPCmd PEInfo statmsg "Cannot determine server address"
+ catch {unset redirect}
+ }
+ } elseif {[info exists export_via_local_hostname]} {
+ if {[regsub {^(http[s]?://)[A-Za-z0-9\\-\\.]+(.*)$} "[cgi_root]/pub/getach.tcl" "\\1\[[info hostname]\]\\2" redirect] != 1} {
+ WPCmd PEInfo statmsg "Cannot determine server address"
+ catch {unset redirect}
+ }
+ } else {
+ set redirect "[cgi_root]/pub/getach.tcl"
+ }
+
+ set givenname "[file tail $fldr]${mailextension}"
+ set safegivenname $givenname
+ regsub -all {[/]} $safegivenname {-} safegivenname
+ regsub -all {[ ]} $safegivenname {_} safegivenname
+ regsub -all {[\?]} $safegivenname {X} safegivenname
+ regsub -all {[&]} $safegivenname {X} safegivenname
+ regsub -all {[#]} $safegivenname {X} safegivenname
+ regsub -all {[=]} $safegivenname {X} safegivenname
+ set safegivenname "/$safegivenname"
+
+ puts $cfd "Content-type: Application/X-Mail-Folder"
+ puts $cfd "Content-Disposition: attachment; filename=\"$givenname\""
+
+ # side-step the cgi_xxx stuff in this special case because
+ # we don't want to buffer up the downloading attachment...
+
+ puts $cfd "Content-Length: $dfilesize"
+ puts $cfd "Expires: [clock format [expr {[clock seconds] + 3600}] -f {%a, %d %b %Y %H:%M:%S GMT} -gmt true]"
+ puts $cfd "Cache-Control: max-age=3600"
+ puts $cfd "Refresh: 0; URL=\"$_wp(serverpath)/$_wp(appdir)/$_wp(ui1dir)/wp.tcl?page=folders\""
+ puts $cfd ""
+
+ puts $cfd $dfile
+
+ # exec chmod [cgi_tmpfile_permissions] $dfile
+
+ close $cfd
+
+ exec /bin/chmod [cgi_tmpfile_permissions] $cfile
+ exec /bin/chmod [cgi_tmpfile_permissions] $dfile
+ }
+
+ # prepare to clean up if the brower never redirects
+ if {[info exists redirect]} {
+ set redirect "${redirect}${safegivenname}?h=${rhandle}"
+ } else {
+ set redirect "wp.tcl?page=folders&cid=$cid"
+ }
+
+ cgi_http_head {
+ # redirect to the place we stuffed the export info. use the ip address
+ # to foil spilling any session cookies or the like
+
+ if {[info exists env(SERVER_PROTOCOL)] && [regexp {[Hh][Tt][Tt][PP]/([0-9]+)\.([0-9]+)} $env(SERVER_PROTOCOL) m vmaj vmin] && $vmaj >= 1 && $vmin >= 1} {
+ cgi_puts "Status: 303 Temporary Redirect"
+ } else {
+ cgi_puts "Status: 302 Redirected"
+ }
+
+ cgi_puts "URI: $redirect"
+ cgi_puts "Location: $redirect"
+ }
+
+ exec echo $rhandle | [file join $_wp(cgipath) [WPCmd PEInfo set wp_ver_dir] whackatch.tcl] >& /dev/null &
+}
diff --git a/web/cgi/alpine/1.0/exporting.tcl b/web/cgi/alpine/1.0/exporting.tcl
new file mode 100644
index 00000000..b1242843
--- /dev/null
+++ b/web/cgi/alpine/1.0/exporting.tcl
@@ -0,0 +1,187 @@
+# $Id: exporting.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# exporting.tcl
+#
+# Purpose: CGI script to generate html output associated with folder
+# exporting explanation text
+#
+# Input:
+set export_vars {
+ {fid "Missing Collection ID"}
+ {cid "Missing Command ID"}
+}
+
+# Output:
+#
+# HTML/Javascript/CSS data representing the message specified
+# by the 'uid' argument
+
+# Command Menu definition for Message View Screen
+set export_menu {
+}
+
+set common_menu {
+ {
+ {}
+ {
+ {
+ # * * * * Cancel * * * *
+ cgi_put [cgi_url "Folder List" wp.tcl?page=folders&cid=[WPCmd PEInfo key] target=_top class=navbar]
+ }
+ }
+ }
+}
+
+## read vars
+foreach item $export_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} errstr]} {
+ error [list _action "Impart Variable" $errstr]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+if {[catch {WPCmd PEInfo key} key]} {
+ error [list _action "command ID" $key]
+}
+
+# massage fid, strip leading "f_"
+set fid [string range [lindex $fid 0] 2 end]
+set digfid [cgi_unquote_input $fid]
+set colid [lindex $digfid 0]
+if {[set l [llength $digfid]] > 2} {
+ set fpath [eval "file join [lrange $digfid 1 [expr {[llength $digfid] - 1}]]"]
+} else {
+ set fpath ""
+}
+set fldr [lindex $digfid end]
+
+# paint the page
+cgi_http_head {
+ WPStdHttpHdrs text/html
+}
+
+cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "Folder Exporting"
+ WPStyleSheets
+ cgi_http_equiv Refresh "0; url=$_wp(serverpath)/$_wp(appdir)/$_wp(ui1dir)/export.tcl?fid=${fid}&cid=$cid"
+ }
+
+ cgi_body bgcolor=$_wp(bordercolor) {
+
+ set mbox [WPCmd PEMailbox mailboxname]
+
+ WPTFTitle "Folder Export"
+
+ cgi_table border=0 cellspacing=0 cellpadding=2 width="100%" height="100%" {
+
+ cgi_table_row {
+ cgi_table_data rowspan=2 valign=top class=navbar {
+ cgi_table bgcolor=$_wp(menucolor) border=0 cellspacing=0 cellpadding=2 {
+ cgi_table_row {
+ cgi_table_data class=navbar style=padding-top:6 {
+ cgi_puts "Current Folder :"
+ cgi_division align=center "style=margin-top:4;margin-bottom:4" {
+ cgi_put [cgi_url [WPCmd PEMailbox mailboxname] fr_main.tcl target=_top class=navbar]
+
+ switch -exact -- [WPCmd PEMailbox state] {
+ readonly {
+ cgi_br
+ cgi_put [cgi_span "style=color: pink; font-weight: bold" "(Read Only)"]
+ }
+ closed {
+ cgi_br
+ cgi_put [cgi_span "style=color: pink; font-weight: bold" "(Closed)"]
+ }
+ ok -
+ default {}
+ }
+
+ cgi_br
+ }
+
+ cgi_hr "width=75%"
+ }
+ }
+
+ # next comes the menu down the left side, with suitable
+ cgi_table_row {
+ eval {
+ cgi_table_data $_wp(menuargs) class=navbar style=padding-bottom:10 {
+ WPTFCommandMenu export_menu common_menu
+ }
+ }
+ }
+ }
+ }
+
+ # down the right side of the table is the window's contents
+ cgi_table_data width="100%" valign=top class=dialog {
+
+ cgi_division "style=\"margin-left: 12%; margin-right: 12%\"" {
+
+ cgi_division align=center "style=\"padding: 18; font-size: bigger \"" {
+ cgi_puts "Export Folder"
+ }
+
+ cgi_puts "WebPine is preparing the folder [cgi_bold $fldr] for download. "
+ cgi_puts "You should see your browser's File Open Dialog appear any momment."
+
+ cgi_p
+
+ cgi_puts "The exported file will contain all of the messages in the folder separated "
+ cgi_puts "by a traditional mail message delimiter, and should be recognizable by "
+ cgi_puts "a variety of desktop mail programs."
+
+ cgi_p
+
+ cgi_puts "Be sure to pick a good name for the downloaded mail folder."
+ cgi_puts "If you are sure the folder has been exported properly (that is, "
+ cgi_puts "there were no error messages or other such problems, you can "
+ if {[string compare inbox [string tolower $mbox]]} {
+ cgi_puts "delete the folder from the collection."
+ } else {
+ cgi_puts "delete and expunge the messages from your INBOX."
+ }
+
+ cgi_p
+
+ cgi_puts "WebPine's [cgi_span "style=font-weight: bold; font-style: italic" Import] command, found to the right of each collection and "
+ cgi_puts "directory entry in the folder list, can be used to transfer the exported "
+ cgi_puts "mail folder from your computer back into a folder collection "
+ cgi_puts "suitable for viewing within WebPine "
+
+ cgi_p
+
+ cgi_puts "If your browser does not automatically return to the Folder List page after the download is complete, click the button below."
+
+ cgi_p
+
+ cgi_division align=center {
+ cgi_form $_wp(serverpath)/$_wp(appdir)/$_wp(ui1dir)/wp.tcl method=get {
+ cgi_text "page=folders" type=hidden notab
+ cgi_text "cid=[WPCmd PEInfo key]" type=hidden notab
+
+ cgi_submit_button "done=Return to Folder List"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
diff --git a/web/cgi/alpine/1.0/filtedit.tcl b/web/cgi/alpine/1.0/filtedit.tcl
new file mode 100755
index 00000000..34b9bd0f
--- /dev/null
+++ b/web/cgi/alpine/1.0/filtedit.tcl
@@ -0,0 +1,704 @@
+#!./tclsh
+# $Id: filtedit.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# filtedit.tcl
+#
+# Purpose: CGI script to generate html form editing of a single filter
+
+#
+# include common filter info
+source filter.tcl
+
+#
+# Input:
+set filtedit_vars {
+ {cid "No cid"}
+ {oncancel "No oncancel"}
+ {onfiltcancel {} ""}
+ {fno {} -1}
+ {add {} 0}
+ {filterrtext {} ""}
+ {filtedit_score {} 0}
+ {filtedit_indexcolor {} 0}
+ {fg {} ""}
+ {bg {} ""}
+}
+
+# Output:
+#
+
+## read vars
+foreach item $filtedit_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} result]} {
+ if {[llength $item] > 2} {
+ set [lindex $item 0] [lindex $item 2]
+ } else {
+ error [list _action [lindex $item 1] $result]
+ }
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+if {$filtedit_score} {
+ set filttype score
+ set filttypename Score
+} elseif {$filtedit_indexcolor} {
+ set filttype indexcolor
+ set filttypename "Index Color"
+} else {
+ set filttype filt
+ set filttypename Filter
+}
+
+if {[info exists filtedit_add]} {
+ set add $filtedit_add
+}
+
+if {[info exists filtedit_fno]} {
+ set fno $filtedit_fno
+}
+
+if {[info exists filtedit_onfiltcancel]} {
+ set onfiltcancel $filtedit_onfiltcancel
+}
+
+if {[info exists filtedit_filterrtext]} {
+ set filterrtext $filtedit_filterrtext
+}
+
+set filterr 0
+if {[string length $filterrtext]} {
+ set filterr 1
+}
+
+set filtedit_menu {
+ {
+ {}
+ {
+ {
+ # * * * * OK * * * *
+ #cgi_image_button filt_save=[WPimg but_save] border=0 alt="Save Config"
+ cgi_submit_button "${filttype}_save=Save"
+ }
+ }
+ }
+ {
+ {}
+ {
+ {
+ # * * * * CANCEL * * * *
+ cgi_submit_button filtcancel=Cancel
+ }
+ }
+ }
+ {
+ {}
+ {
+ {
+ # * * * * HELP * * * *
+ cgi_submit_button "${filttype}help=Get Help"
+ }
+ }
+ }
+}
+
+
+proc wpGetVarAs {_var _varas} {
+ upvar $_varas varas
+
+ if {[catch {cgi_import_as $_var varas} result]} {
+ set varas ""
+ }
+}
+
+proc freetext_cell {intro varname varval} {
+ cgi_table_data align=right {
+ cgi_puts [cgi_bold "$intro :[cgi_nbspace][cgi_nbspace]"]
+ }
+ cgi_table_data align=left {
+ cgi_text "$varname=$varval" "style=margin:2"
+ }
+}
+
+
+array set idvarnames $pattern_id
+array set idvarvals {}
+array set patvarnames $pattern_fields
+array set patvarvals {}
+array set actionvarnames $pattern_actions
+array set colvarnames $pattern_colors
+array set scorevarnames $pattern_scores
+
+array set actionvals {}
+
+cgi_http_head {
+ cgi_content_type
+ cgi_pragma no-cache
+}
+
+cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "$filttypename List Configuration"
+ WPStyleSheets
+ }
+
+ if {$add == 0} {
+ if {$filtedit_score} {
+ set actions $pattern_scores
+ set fext [WPCmd PEConfig scoreextended $fno]
+ } elseif {$filtedit_indexcolor} {
+ set actions $pattern_colors
+ set fext [WPCmd PEConfig indexcolorextended $fno]
+ } else {
+ set actions $pattern_actions
+ set fext [WPCmd PEConfig filtextended $fno]
+ }
+
+ foreach fvar $fext {
+ switch -- [lindex $fvar 0] {
+ id {
+ foreach idvar [lindex $fvar 1] {
+ set idname [lindex $idvar 0]
+ if {[info exists idvarnames($idname)]} {
+ set idvarvals($idname) [lindex $idvar 1]
+ }
+ }
+ }
+ pattern {
+ foreach patternvar [lindex $fvar 1] {
+ set patname [lindex $patternvar 0]
+ if {[info exists patvarnames($patname)]} {
+ set patvarvals($patname) [lindex $patternvar 1]
+ }
+ }
+ }
+ filtaction {
+ foreach actionvar [lindex $fvar 1] {
+ set actionname [lindex $actionvar 0]
+ if {[info exists actionvarnames($actionname)]} {
+ set actionvals($actionname) [lindex $actionvar 1]
+ }
+ }
+ }
+ indexcolors {
+ foreach colvar [lindex $fvar 1] {
+ set colname [lindex $colvar 0]
+ if {[info exists colvarnames($colname)]} {
+ set actionvals($colname) [lindex $colvar 1]
+ }
+ }
+ }
+ scores {
+ foreach colvar [lindex $fvar 1] {
+ set actionvals([lindex $colvar 0]) [lindex $colvar 1]
+ }
+ }
+ }
+ }
+ } else {
+ if {$filtedit_score} {
+ set actions $pattern_scores
+ } elseif {$filtedit_indexcolor} {
+ set actions $pattern_colors
+ } else {
+ set actions $pattern_actions
+ }
+ }
+
+ cgi_body BGCOLOR="$_wp(bordercolor)" {
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=get name=filtconfig target=_top {
+ cgi_text "page=conf_process" type=hidden notab
+ cgi_text "cp_op=${filttype}config" type=hidden notab
+ cgi_text "cid=$cid" type=hidden notab
+ cgi_text "oncancel=$oncancel" type=hidden notab
+ cgi_table border=0 cellspacing=0 cellpadding=0 width="100%" height="100%" {
+ cgi_table_row {
+ #
+ # next comes the menu down the left side
+ #
+ eval {
+ cgi_table_data $_wp(menuargs) rowspan=4 {
+ WPTFCommandMenu filtedit_menu {}
+ }
+ }
+
+ #
+ # In main body of screen goes confg list
+ #
+ cgi_table_data valign=top width="100%" class=dialog {
+ if {[string length $onfiltcancel]} {
+ cgi_text "onfiltcancel=$onfiltcancel" type=hidden notab
+ }
+ cgi_text "fno=$fno" type=hidden notab
+ cgi_text "subop=[expr {$add ? "add" : "edit"}]" type=hidden notab
+ cgi_table border=0 cellspacing=0 cellpadding=0 {
+ # pattern title
+ cgi_table_row {
+ cgi_table_data {
+ cgi_puts "<fieldset>"
+ cgi_puts "<legend style=\"font-weight:bold;font-size:bigger\">$filttypename Identification</legend>"
+ cgi_table {
+ foreach {idname idtype} $pattern_id {
+ if {[info exists idvarvals($idname)]} {
+ set val $idvarvals($idname)
+ } else {
+ wpGetVarAs $idname val
+ }
+ cgi_table_row {
+ freetext_cell "$filttypename [lindex $idtype 0]" $idname $val
+ }
+ }
+ }
+ cgi_puts "</fieldset>"
+ }
+ }
+
+ # Folder Conditions
+ wpGetVarAs folder folder
+ wpGetVarAs ftype ftype
+ cgi_table_row {
+ cgi_table_data colspan=2 {
+ cgi_puts "<fieldset>"
+ cgi_puts "<legend style=\"font-weight:bold;font-size:bigger\">Folder Conditions</legend>"
+ cgi_table {
+ cgi_table_row {
+ cgi_table_data align=right valign=top {
+ cgi_puts [cgi_bold "Current Folder Type :"]
+ }
+ cgi_table_data align=left {
+ cgi_table border=0 cellpadding=0 cellspacing=0 {
+ cgi_table_row {
+ cgi_table_data width=50 {
+ cgi_puts "[cgi_nbspace]"
+ }
+ cgi_table_data {
+ cgi_table border=0 cellpadding=0 cellspacing=0 {
+ foreach type {any news email specific} {
+ cgi_table_row {
+ cgi_table_data {
+ cgi_radio_button ftype=$type [expr {[string compare $ftype $type] == 0 ? "checked" : ""}] style="background-color:$_wp(dialogcolor)"
+ }
+ cgi_table_data {
+ switch -- $type {
+ any -
+ news -
+ email {
+ cgi_puts "[string toupper [string range $type 0 0]][string range $type 1 end]"
+ }
+ specific {
+ cgi_puts "Specific Folder List :"
+ cgi_text "folder=$folder"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ cgi_puts "</fieldset>"
+ }
+ }
+
+ # Message Conditions
+
+ cgi_table_row {
+ cgi_table_data {
+ cgi_puts "<fieldset>"
+ cgi_puts "<legend style=\"font-weight:bold;font-size:bigger\">Message Conditions</legend>"
+ cgi_table border=0 {
+ foreach {pvarname parvarval} $pattern_fields {
+
+ if {$filterr} {
+ wpGetVarAs $pvarname pvarval
+ } elseif {[info exists patvarvals($pvarname)]} {
+ set pvarval $patvarvals($pvarname)
+ } else {
+ set pvarval ""
+ }
+
+ cgi_table_row {
+ switch -- [lindex $patvarnames($pvarname) 1] {
+ freetext {
+ freetext_cell [lindex $patvarnames($pvarname) 0] $pvarname $pvarval
+ }
+ status {
+ cgi_table_data align=right {
+ cgi_puts "[cgi_bold [lindex $patvarnames($pvarname) 0]] :[cgi_nbspace][cgi_nbspace]"
+ }
+ cgi_table_data align=left {
+ cgi_select $pvarname "style=margin:2" {
+ cgi_option "Don't care, always matches" "value=either"
+ cgi_option "Yes" "value=yes" [expr {[string compare $pvarval "yes"] == 0 ? "selected" : ""}]
+ cgi_option "No" "value=no" [expr {[string compare $pvarval "no"] == 0 ? "selected" : ""}]
+ }
+ }
+ }
+ addrbook {
+ cgi_table_data align=right valign=top {
+ cgi_puts "[cgi_bold [lindex $patvarnames($pvarname) 0]] :[cgi_nbspace][cgi_nbspace]"
+ }
+ cgi_table_data align=left {
+ cgi_select addrbook "style=margin:2" {
+ cgi_option "Don't care, always matches" "value=either" [expr {[string compare $pvarval "either"] == 0 ? "selected" : ""}]
+ cgi_option "Yes, in any address book" "value=yes" [expr {[string compare $pvarval "yes"] == 0 ? "selected" : ""}]
+ cgi_option "No, not in any addressbook" "value=no" [expr {[string compare $pvarval "no"] == 0 ? "selected" : ""}]
+ cgi_option "Yes, in specific address book" "value=yesspecific" [expr {[string compare $pvarval "yesspecific"] == 0 ? "selected" : ""}]
+ cgi_option "No, not in specific address book" "value=nospecific" [expr {[string compare $pvarval "nospecific"] == 0 ? "selected" : ""}]
+ }
+ cgi_br
+ cgi_puts "Specific Addressbook:"
+ cgi_text "specificabook=" "style=margin:4"
+ cgi_br
+ cgi_puts "Types of addresses to check for in address book:"
+ cgi_table style=margin-left:30 {
+ cgi_table_row {
+ cgi_table_data nowrap {
+ cgi_checkbox abookfrom [expr {[string compare $pvarval "no"] == 0 ? "selected" : ""}]
+ cgi_puts "From"
+ }
+
+ cgi_table_data nowrap {
+ cgi_checkbox abookreplyto
+ cgi_puts "Reply-To"
+ }
+ }
+ cgi_table_row {
+ cgi_table_data nowrap {
+ cgi_checkbox abooksender
+ cgi_puts "Sender"
+ }
+
+ cgi_table_data nowrap {
+ cgi_checkbox abookto
+ cgi_puts "To"
+ }
+ }
+ cgi_table_row {
+ cgi_table_data nowrap {
+ cgi_checkbox abookcc
+ cgi_puts "Cc"
+ }
+ }
+ }
+ }
+ }
+ headers {
+ cgi_table_data align=right valign=top {
+ cgi_put "[cgi_bold [lindex $patvarnames($pvarname) 0]] :[cgi_nbspace][cgi_nbspace]"
+ }
+ cgi_table_data align=left {
+ cgi_table {
+ set hdrnum 0
+
+ if {[llength $pvarval] > 0} {
+ for {set n 0} {$n < [llength $pvarval]} {incr n} {
+ cgi_table_row {
+ cgi_table_data align=left nowrap {
+ cgi_text "hdrfld${n}=[lindex [lindex $pvarval $n] 0]" "style=margin:2"
+ cgi_put ":"
+ cgi_text "hdrval${n}=[lindex [lindex $pvarval $n] 1]" "style=margin:2"
+ cgi_submit_button "rmheader${n}=Remove"
+ }
+ }
+ }
+
+ cgi_text "header_total=$n" type=hidden notab
+ }
+
+ cgi_table_row {
+ cgi_table_data align=left nowrap {
+ cgi_submit_button "addheader=Add Header"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ cgi_puts "</fieldset>"
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data "style=\"padding-bottom: 40\"" {
+ cgi_puts "<fieldset>"
+ cgi_puts "<legend style=\"font-weight:bold;font-size:bigger\">Filter Actions</legend>"
+ foreach {avarname patval} $actions {
+ switch -- $avarname {
+ indexcolor {
+ set ih [WPIndexLineHeight]
+ set iformat [WPCmd PEMailbox indexformat]
+ set num 0
+ cgi_division "width=100%" align=center "style=\"font-size: bigger; font-weight: bold; margin: 0 0 12 0 \"" {
+ cgi_puts "Choose Index Line Colors"
+ }
+
+ cgi_table width=100% align=center cellpadding=0 cellspacing=0 "style=\"font-family: geneva, arial, sans-serif; height: ${ih}pix; width: 90%; background-color: white ; border: 1px solid black\"" {
+ foreach l [list "Line One" "Sample Message" "Line Three"] {
+ set iclass [lindex {i0 i1} [expr ([incr num] % 2)]]
+ set istyle ""
+ if {$num == 2} {
+ wpGetVarAs fg fg
+ if {[string length $fg] == 0} {
+ if {[info exists actionvals($avarname)]} {
+ set fg [lindex $actionvals($avarname) 0]
+ }
+ }
+
+ cgi_text "fg=$fg" type=hidden notab
+ append istyle "; color: $fg"
+
+ wpGetVarAs bg bg
+ if {[string length $bg] == 0} {
+ if {[info exists actionvals($avarname)]} {
+ set bg [lindex $actionvals($avarname) 1]
+ }
+ }
+
+ cgi_text "bg=$bg" type=hidden notab
+ append istyle "; background-color: $bg"
+ }
+
+ cgi_table_row {
+ if {[WPCmd PEInfo feature enable-aggregate-command-set]} {
+ cgi_table_data height=${ih}pix class=$iclass "style=\"$istyle\"" {
+ cgi_checkbox bogus
+ }
+ }
+
+ foreach fmt $iformat {
+ cgi_table_data height=${ih}pix width=[lindex $fmt 1]% nowrap class=$iclass "style=\"$istyle\"" {
+ switch -regex [string tolower [lindex $fmt 0]] {
+ number {
+ cgi_puts "$num"
+ }
+ status {
+ set n [expr {(int((10 * rand()))) % 5}]
+ cgi_puts [lindex {N D F A { }} $n]
+ }
+ .*size.* {
+ cgi_puts "([expr int((10000 * rand()))])"
+ }
+ from.* {
+ cgi_puts "Some Sender"
+ }
+ subject {
+ cgi_puts $l
+ }
+ date {
+ cgi_puts [clock format [clock seconds] -format "%d %b"]
+ }
+ default {
+ cgi_puts [lindex $fmt 0]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ cgi_table width=80% align=center {
+ cgi_table_row {
+ cgi_table_data align=center {
+ cgi_table "style=\"background-color: #ffcc66\"" {
+ wpGetVarAs fgorbg fgorbg
+ cgi_table_row {
+ cgi_table_data {
+ if {[string length $fgorbg] == 0 || [string compare $fgorbg fg] == 0} {
+ set checked checked=1
+ } else {
+ set checked ""
+ }
+
+ cgi_radio_button fgorbg=fg $checked
+ }
+ cgi_table_data "style=\"align: left; padding-left: 12\"" {
+ cgi_puts "Foreground"
+ }
+ }
+ cgi_table_row {
+ cgi_table_data {
+ if {[string length $checked]} {
+ set checked ""
+ } else {
+ set checked checked=1
+ }
+
+ cgi_radio_button fgorbg=bg $checked
+ }
+ cgi_table_data "style=\"align: left; padding-left: 12\"" {
+ cgi_puts "Background"
+ }
+ }
+ }
+ }
+ cgi_table_data align=center {
+ cgi_image_button "colormap=[WPimg nondither10x10]" alt="Color Pattern" "style=\"border: 1px solid black\""
+ }
+ }
+ }
+ }
+ folder {
+ if {[info exists actionvals(kill)]} {
+ set killit $actionvals(kill)
+ } else {
+ set killit 0
+ }
+
+ if {$filterr} {
+ wpGetVarAs action tval
+ set killit [expr {([string compare $tval "delete"] == 0) ? 1 : 0}]
+ }
+ cgi_table border=0 cellpadding=0 cellspacing=0 {
+ cgi_table_row {
+ cgi_table_data width=50 align=right valign=top {
+ cgi_puts [cgi_bold "Action:[cgi_nbspace]"]
+ }
+ cgi_table_data {
+ cgi_table border=0 cellpadding=0 cellspacing=0 {
+ cgi_table_row {
+ cgi_table_data valign=top {
+ cgi_radio_button action=status [expr {$killit ? "checked" : ""}] style="background-color:$_wp(dialogcolor)"
+ }
+ cgi_table_data {
+ cgi_puts "Set Message Status:"
+ cgi_division "style=\"margin-left: .5in\"" {
+ cgi_select actsetimp {
+ cgi_option "Don't change Important Status" "value=leave" [expr {[string compare $pvarval "either"] == 0 ? "selected" : ""}]
+ cgi_option "Set Important status" "value=set" [expr {[string compare $pvarval "yes"] == 0 ? "selected" : ""}]
+ cgi_option "Clear Important status" "value=clear" [expr {[string compare $pvarval "no"] == 0 ? "selected" : ""}]
+ }
+
+ cgi_br
+
+ cgi_select actsetnew {
+ cgi_option "Don't change New Status" "value=leave" [expr {[string compare $pvarval "either"] == 0 ? "selected" : ""}]
+ cgi_option "Set New status" "value=set" [expr {[string compare $pvarval "yes"] == 0 ? "selected" : ""}]
+ cgi_option "Clear New status" "value=clear" [expr {[string compare $pvarval "no"] == 0 ? "selected" : ""}]
+ }
+
+ cgi_br
+
+ cgi_select actsetdel {
+ cgi_option "Don't change Deleted Status" "value=leave" [expr {[string compare $pvarval "either"] == 0 ? "selected" : ""}]
+ cgi_option "Set Deleted status" "value=set" [expr {[string compare $pvarval "yes"] == 0 ? "selected" : ""}]
+ cgi_option "Clear Deleted status" "value=clear" [expr {[string compare $pvarval "no"] == 0 ? "selected" : ""}]
+ }
+
+ cgi_br
+
+ cgi_select actsetans {
+ cgi_option "Don't change Answered Status" "value=leave" [expr {[string compare $pvarval "either"] == 0 ? "selected" : ""}]
+ cgi_option "Set Answered status" "value=set" [expr {[string compare $pvarval "yes"] == 0 ? "selected" : ""}]
+ cgi_option "Clear Answered status" "value=clear" [expr {[string compare $pvarval "no"] == 0 ? "selected" : ""}]
+ }
+ }
+ }
+ }
+ cgi_table_row {
+ cgi_table_data valign=top {
+ cgi_radio_button action=delete [expr {$killit ? "checked" : ""}] style="background-color:$_wp(dialogcolor)"
+ }
+ cgi_table_data {
+ cgi_puts "Delete"
+ }
+ }
+ cgi_table_row {
+ cgi_table_data valign=top {
+ cgi_radio_button action=move [expr {$killit == 0 ? "checked" : ""}] style="background-color:$_wp(dialogcolor)"
+ }
+ cgi_table_data {
+ cgi_puts "Move to Folder :"
+ if {$filterr} {
+ wpGetVarAs actionfolder tval
+ cgi_text "actionfolder=$tval"
+ } else {
+ if {[info exists actionvals($avarname)]} {
+ set av $actionvals($avarname)
+ } else {
+ set av 0
+ }
+
+ cgi_text "actionfolder=$av"
+ }
+ cgi_br
+ if {$filterr} {
+ wpGetVarAs moind moinval
+ set tval [expr {([string compare $moinval on] == 0) ? "checked" : ""}]
+ } else {
+ if {[info exists actionvals($avarname)] && $actionvals($avarname) == 1} {
+ set tval checked
+ } else {
+ set tval ""
+ }
+ }
+ cgi_checkbox moind $tval
+ cgi_puts "Move only if not deleted."
+ }
+ }
+ }
+ }
+ }
+ cgi_table_row {
+ wpGetVarAs setkeywords setkeywords
+ freetext_cell "Set these Keywoards" setkeywords $setkeywords
+ }
+ cgi_table_row {
+ wpGetVarAs clearkeywords clearkeywords
+ freetext_cell "Clear these Keywoards" clearkeywords $clearkeywords
+ }
+ }
+ }
+ scores {
+ cgi_table border=0 cellpadding=4 cellspacing=0 align=center {
+ cgi_table_row {
+ wpGetVarAs scoreval scoreval
+ if {[string length $scoreval] == 0} {
+ set scoreval $actionvals(scoreval)
+ }
+
+ freetext_cell "Score Value" scoreval $scoreval
+ }
+ cgi_table_row {
+ wpGetVarAs scorehdr scorehdr
+ if {[string length $scorehdr] == 0} {
+ set scorehdr $actionvals(scorehdr)
+ }
+
+ freetext_cell "Score Header" scorehdr $scorehdr
+ }
+ }
+ }
+ }
+ }
+
+ cgi_puts "</fieldset>"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/filter.tcl b/web/cgi/alpine/1.0/filter.tcl
new file mode 100755
index 00000000..2974f6ca
--- /dev/null
+++ b/web/cgi/alpine/1.0/filter.tcl
@@ -0,0 +1,63 @@
+# $Id: filter.tcl 391 2007-01-25 03:53:59Z mikes@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# filter.tcl
+#
+# Purpose: Common filter management data/routines
+
+set pattern_id {
+ nickname {"Nickname" freetext}
+ comment {"Comment" freetext}
+}
+
+set pattern_fields {
+ to {"To" freetext}
+ from {"From" freetext}
+ sender {"Sender" freetext}
+ cc {"Cc" freetext}
+ recip {"Recipients" freetext}
+ partic {"Participants" freetext}
+ news {"Newsgroups" freetext}
+ subj {"Subject" freetext}
+ alltext {"All Text" freetext}
+ bodytext {"Body Text" freetext}
+ age {"Age Interval" freetext}
+ size {"Size Interval" freetext}
+ score {"Score Interval" freetext}
+ keyword {"Keyword" freetext}
+ charset {"Character Set" freetext}
+ headers {"Extra Headers" headers}
+ stat_new {"Message is New" status}
+ stat_rec {"Message is Recent" status}
+ stat_del {"Message is Deleted" status}
+ stat_imp {"Message is Important" status}
+ stat_ans {"Message is Answered" status}
+ stat_8bitsubj {"Subject contains raw 8bit characters" status}
+ stat_bom {"Beginning of Month" status}
+ stat_boy {"Beginning of Year" status}
+ addrbook {"Address in address book" addrbook}
+}
+
+
+set pattern_actions {
+ kill {"kill"}
+ folder {"Folder"}
+ move_only_if_not_deleted {"moind"}
+}
+
+set pattern_colors {
+ indexcolor {indexcolor}
+}
+
+set pattern_scores {
+ scores {scores}
+}
diff --git a/web/cgi/alpine/1.0/flags.tcl b/web/cgi/alpine/1.0/flags.tcl
new file mode 100755
index 00000000..7cc83962
--- /dev/null
+++ b/web/cgi/alpine/1.0/flags.tcl
@@ -0,0 +1,112 @@
+#!./tclsh
+# $Id: flags.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# queryexpunge.tcl
+#
+# Purpose: CGI script to generate html form used to confirm
+# deleted message expunge
+#
+# Input:
+set flag_vars {
+ {uid "Missing UID"}
+}
+
+# Output:
+#
+# HTML/CSS data representing the message specified
+# by the 'uid' argument
+
+# inherit global config
+source ./alpine.tcl
+
+WPEval $flag_vars {
+
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "Set Flags"
+ WPStyleSheets
+ }
+
+ cgi_body BGCOLOR="$_wp(bordercolor)" {
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=get name=confirm target=body {
+ cgi_text "page=index" type=hidden notab
+ cgi_text "sessid=$sessid" type=hidden notab
+ cgi_text "uid=$uid" type=hidden notab
+
+ cgi_table border=0 cellspacing=0 cellpadding=2 width="100%" height="100%" {
+ cgi_table_row {
+ cgi_table_data valign=top align=center class=dialog {
+ cgi_table border=0 cellspacing=0 cellpadding=2 width="75%" {
+ cgi_table_row {
+ cgi_table_data align=center {
+ cgi_puts [cgi_nl][cgi_nl]
+ cgi_puts "Status flags are attributes you can assign to messages that help you associate certain meanings (for example, [cgi_bold Important]) or indicate to Web Alpine"
+ cgi_puts "how you would like to operate on the message (for example, the [cgi_bold Deleted] flag tells WebPine to permanently remove the"
+ cgi_puts "message from the folder when you [cgi_bold Expunge])."
+ cgi_puts [cgi_nl][cgi_nl]
+ cgi_puts "Set or unset desired flags for message [WPCmd PEMessage $uid number] below then"
+ cgi_puts "click [cgi_italic "Set Flags"], or [cgi_italic Cancel] to return to the list of messages"
+ cgi_puts "in [WPCmd PEMailbox mailboxname]."
+ cgi_br
+ cgi_br
+
+ set flaglist [WPCmd PEInfo flaglist]
+ set setflags [WPCmd PEMessage $uid status]
+
+ cgi_table class=dialog {
+ foreach item $flaglist {
+ cgi_table_row {
+ cgi_table_data valign=top align=right width="30%" {
+ if {[lsearch $setflags $item] >= 0} {
+ set checked checked
+ } else {
+ set checked ""
+ }
+
+ cgi_checkbox $item style="background-color:$_wp(dialogcolor)" $checked
+ }
+
+ cgi_table_data valign=top align=left {
+ switch -- $item {
+ New { set text "New" }
+ Answered { set text "Answered" }
+ Deleted { set text "Deleted" }
+ default { set text $item }
+ }
+ cgi_puts $text
+ }
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data colspan=2 height=50 {
+ cgi_br
+ cgi_submit_button "op=Set Flags"
+ cgi_submit_button cancel=Cancel
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/fldrbrowse.tcl b/web/cgi/alpine/1.0/fldrbrowse.tcl
new file mode 100755
index 00000000..b9021a4d
--- /dev/null
+++ b/web/cgi/alpine/1.0/fldrbrowse.tcl
@@ -0,0 +1,335 @@
+# $Id: fldrbrowse.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# folders.tcl
+#
+# Purpose: CGI script to generate html output associated with folder
+# and collection management
+#
+# Input:
+set folder_vars {
+ {uid {} 0}
+ {show {} ""}
+ {expand {} ""}
+ {contract {} ""}
+ {target {} ""}
+ {onselect {} "compose"}
+ {oncancel {} "index"}
+ {controls {} 1}
+ {reload}
+}
+
+# Output:
+#
+# HTML/Javascript/CSS data representing the message specified
+# by the 'uid' argument
+
+# the name of this script
+set me [file tail [info script]]
+
+set indention 18
+
+proc pretty_folder_name {collections folder} {
+ set fcolid [lindex $folder 0]
+ for {set i 0} {$i < [llength $collections]} {incr i} {
+ set col [lindex $collections $i]
+ if {$fcolid == [lindex $col 0]} {
+ set coltext [lindex $col 1]
+ }
+ }
+ return "${coltext}:[join [lrange $folder 1 end] /]"
+}
+
+
+set key [WPCmd PEInfo key]
+
+
+#
+# Display the given folder list in a table (w/ mihodge mods)
+#
+proc blat_folder_list {colid flist shown path baseurl scroll anchorcntref onselect depth} {
+
+ global key border indention _wp target
+
+ set mbox [WPCmd PEMailbox mailboxname]
+
+ upvar $anchorcntref anchorcnt
+
+ set rownum 0
+
+ foreach folder $flist {
+ set t [lindex $folder 0]
+ set f [lindex $folder 1]
+ set ff [linsert $path [llength $path] $f]
+ set index -1
+
+ # initial pad=12, expand/contract control is 9px wide
+ set cellpad [expr {12 + ($depth * $indention)}]
+
+ if {[string first F $t] >= 0} {
+ set delim [WPCmd PEFolder delimiter $colid]
+ set fullpath [join [lrange $ff 1 end] $delim]
+ regsub -all {(')} [lrange $ff 1 end] {\\\\\1} ef
+ set celldata [cgi_url $f wp.tcl?page=[join ${onselect} {&}]&f_colid=${colid}&f_name=[WPPercentQuote [join $ef $delim]]&target=${target}&cid=$key target=${target}]
+ } else {
+ set celldata $f
+ }
+
+ if {[string first D $t] >= 0} {
+
+ if {[set index [lsearch $shown $ff]] < 0} {
+ set control expand
+ } else {
+ set control contract
+ }
+
+ set celldata "[cgi_url [cgi_imglink $control] "${baseurl}${control}=[WPPercentQuote $ff]#f_[WPPercentQuote $ff]" name=f_[WPPercentQuote $ff] "style=\"padding-right:10px\""]$celldata"
+ incr cellpad -19
+ }
+
+ cgi_table_row {
+ cgi_table_data {
+ cgi_put [cgi_img [WPimg dot2] height=1]
+ }
+
+ cgi_table_data align=left "style=\"padding-left: ${cellpad}px\"" nowrap {
+ cgi_put $celldata
+ }
+
+ cgi_table_data valign=top nowrap {
+ cgi_puts [cgi_nbspace]
+ }
+ }
+
+ if {[string first D $t] >= 0 && $index >= 0} {
+ set nflist [eval WPCmd PEFolder list $ff]
+ set newpath $path
+ lappend newpath $f
+ blat_folder_list $colid $nflist $shown $newpath $baseurl $scroll anchorcnt $onselect [expr {$depth + 1}]
+ }
+
+ catch {unset control}
+ }
+}
+
+#
+# Command Menu definition for Message View Screen
+#
+set folder_menu {
+}
+
+set common_menu {
+ {
+ {expr {$controls == 1}}
+ {
+ {
+ # * * * * CANCEL * * * *
+ cgi_puts [cgi_url Cancel wp.tcl?page=$oncancel&cid=[WPCmd PEInfo key] class=navbar target=_top]
+ }
+ }
+ }
+ {}
+ {
+ {}
+ {
+ {
+ cgi_puts "Get Help"
+ }
+ }
+ }
+}
+
+
+## read vars
+foreach item $folder_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} errstr]} {
+ error [list _action "Impart Variable" $errstr]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+if {[catch {WPNewMail $reload} newmail]} {
+ error [list _action "new mail" $newmail]
+}
+
+
+# perform any requested actions
+
+# preserve vars that my have been overridden with cgi parms
+
+if {[catch {WPCmd PEFolder collections} collections]} {
+ error [list _action "Collectoin list" $collections]
+}
+
+set shown [split $show ,]
+set scroll {}
+set anchorcnt 0
+
+# mihodge: process actions
+if {[llength $expand]} {
+ lappend shown $expand
+ set scroll $expand
+}
+
+if {[llength $contract]} {
+ if {[set index [lsearch $shown $contract]] >= 0} {
+ set shown [lreplace $shown $index $index]
+ set scroll $contract
+ }
+}
+
+set baseurl wp.tcl?page=fldrbrowse&onselect=[WPPercentQuote ${onselect}]&oncancel=${oncancel}&controls=${controls}&target=${target}&
+
+if {[llength $shown]} {
+ append baseurl "show=[WPPercentQuote [join $shown ,]]&"
+}
+
+
+# paint the page
+cgi_http_head {
+ WPStdHttpHdrs text/html
+}
+
+cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "Folder List" folders
+ if {$controls == 1} {
+ WPHtmlHdrReload "$_wp(appdir)/$_wp(ui1dir)/wp.tcl?page=folders"
+ }
+
+ WPStyleSheets
+ }
+
+ cgi_body bgcolor=$_wp(bordercolor) {
+
+ catch {WPCmd PEInfo set help_context folders}
+
+ # prepare context and navigation information
+
+ set navops ""
+
+ if {$controls == 1} {
+ WPTFTitle "Browse Folder" $newmail
+ }
+
+ cgi_table border=0 cellspacing=0 cellpadding=0 width="100%" height="100%" {
+ cgi_table_row {
+ if {$controls > 0} {
+ cgi_table_data rowspan=2 valign=top class=navbar {
+ cgi_table bgcolor=$_wp(menucolor) border=0 cellspacing=0 cellpadding=0 {
+ # next comes the menu down the left side, with suitable
+ cgi_table_row {
+ eval {
+ cgi_table_data $_wp(menuargs) class=navbar {
+ WPTFCommandMenu folder_menu common_menu
+ }
+ }
+ }
+ }
+ }
+ }
+
+ # down the right side of the table is the window's contents
+ cgi_table_data width="100%" align=center valign=top class=dialog {
+
+ # then the table representing the folders
+ cgi_table width=75% border=0 cellspacing=0 cellpadding=2 align=center {
+
+ cgi_table_row {
+ cgi_table_data height=80 align=center valign=middle class=dialog {
+ switch $controls {
+ 0 -
+ 2 { set task "the Saved message" }
+ default { set task "your composition's [cgi_italic "File Carbon Copy"] (Fcc)" }
+ }
+
+ cgi_puts "Click a folder name below to use it as the destination for $task, or click [cgi_italic Cancel]"
+ cgi_puts "to return without choosing anything."
+ }
+ }
+
+ if {$controls != 1} {
+ cgi_table_row {
+ cgi_table_data align=center valign=top height=40 class=navbar {
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=get target=$target {
+ cgi_text page=$oncancel type=hidden notab
+ cgi_text cid=[WPCmd PEInfo key] type=hidden notab
+ cgi_submit_button cancel=Cancel
+ }
+ }
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data {
+ cgi_table border=0 cellspacing=0 cellpadding=2 align=center {
+ for {set i 0} {$i < [llength $collections]} {incr i} {
+ set col [lindex $collections $i]
+ set colid [lindex $col 0]
+
+ cgi_table_row {
+ cgi_table_data width=18 valign=top {
+ if {[llength $collections] > 1} {
+ if {[set index [lsearch $shown $colid]] < 0} {
+ cgi_puts [cgi_url [cgi_imglink expand] "${baseurl}expand=$colid"]
+ } else {
+ cgi_puts [cgi_url [cgi_imglink contract] "${baseurl}contract=$colid"]
+ }
+ } else {
+ cgi_puts [cgi_nbspace]
+ }
+ }
+
+ cgi_table_data align=left {
+ if {[llength $collections] == 1} {
+ set menu "c"
+ set flist 1
+ } else {
+ if {[set index [lsearch $shown $colid]] < 0} {
+ set menu "ce"
+ set flist {}
+ } else {
+ set menu "cc"
+ set flist 1
+ }
+ }
+
+ if {[llength $flist]} {
+ set flist [WPCmd PEFolder list $colid]
+ }
+
+ if {$scroll == $colid} {
+ #cgi_javascript {cgi_puts {scroll = window.document.anchors.length;}}
+ }
+
+ cgi_puts [cgi_font face=Helvetica size=+1 "[lindex $col 1]<A NAME=\"f_$colid\">&nbsp;</A>"]
+
+ incr anchorcnt
+
+ if {[llength $flist]} {
+ blat_folder_list $colid $flist $shown $colid $baseurl $scroll anchorcnt $onselect 1
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/fldrsavenew.tcl b/web/cgi/alpine/1.0/fldrsavenew.tcl
new file mode 100755
index 00000000..a2a5f457
--- /dev/null
+++ b/web/cgi/alpine/1.0/fldrsavenew.tcl
@@ -0,0 +1,202 @@
+# $Id: fldrsavenew.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fldrsavenew.tcl
+#
+# Purpose: CGI script to generate html output associated with message
+# Save to a new folder
+#
+# Input:
+set folder_vars {
+ {onselect "" "index"}
+ {oncancel "" "index"}
+ {target "" ""}
+ {controls "" 1}
+ {reload}
+}
+
+# Output:
+#
+# HTML/Javascript/CSS data representing the message specified
+# by the 'uid' argument
+
+set key [WPCmd PEInfo key]
+
+#
+# Command Menu definition for Message View Screen
+#
+set folder_menu {
+}
+
+set common_menu {
+ {
+ {expr {$controls == 1}}
+ {
+ {
+ # * * * * CANCEL * * * *
+ cgi_puts [cgi_url Cancel wp.tcl?page=$oncancel&cid=[WPCmd PEInfo key] class=navbar target=_top]
+ }
+ }
+ }
+ {}
+ {
+ {}
+ {
+ {
+ cgi_puts "Get Help"
+ }
+ }
+ }
+}
+
+
+## read vars
+foreach item $folder_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} errstr]} {
+ error [list _action "Impart Variable" $errstr]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+if {[catch {WPNewMail $reload} newmail]} {
+ error [list _action "new mail" $newmail]
+}
+
+# perform any requested actions
+
+# preserve vars that my have been overridden with cgi parms
+
+# paint the page
+cgi_http_head {
+ WPStdHttpHdrs text/html
+}
+
+cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "Folder Create for Save" folders
+ if {$controls == 1} {
+ WPHtmlHdrReload "$_wp(appdir)/$_wp(ui1dir)/wp.tcl?page=fldrsavenew"
+ }
+
+ WPStyleSheets
+ }
+
+ cgi_body onload=document.savepmt.f_name.focus() bgcolor=$_wp(bordercolor) {
+
+ catch {WPCmd PEInfo set help_context fldrsave}
+
+ # prepare context and navigation information
+
+ set navops ""
+
+ if {$controls == 1} {
+ WPTFTitle "Browse Folder" $newmail
+ }
+
+ cgi_table border=0 cellspacing=0 cellpadding=2 width="100%" height="100%" {
+ cgi_table_row {
+ if {$controls > 0} {
+ cgi_table_data rowspan=2 valign=top class=navbar {
+ cgi_table bgcolor=$_wp(menucolor) border=0 cellspacing=0 cellpadding=2 {
+ # next comes the menu down the left side, with suitable
+ cgi_table_row {
+ eval {
+ cgi_table_data $_wp(menuargs) class=navbar {
+ WPTFCommandMenu folder_menu common_menu
+ }
+ }
+ }
+ }
+ }
+ }
+
+ # down the right side of the table is the window's contents
+ cgi_table_data width="100%" align=center valign=top class=dialog {
+
+ if {[string length $target] == 0} {
+ set target _top
+ }
+
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=get target=$target name=savepmt {
+ cgi_text page=[lindex $onselect 0] type=hidden notab
+ for {set i 1} {$i < [llength $onselect]} {incr i} {
+ set a [split [lindex $onselect $i] {=}]
+ cgi_text [lindex $a 0]=[lindex $a 1] type=hidden notab
+ }
+
+ cgi_text cid=[WPCmd PEInfo key] type=hidden notab
+
+ # then the table representing the folders
+ cgi_table width=75% border=0 cellspacing=0 cellpadding=2 align=center {
+
+ cgi_table_row {
+ cgi_table_data height=140 align=center valign=middle class=dialog {
+ cgi_p "Enter the name of the folder to save to below, and then click [cgi_italic OK]."
+ cgi_p "You may enter either the name of an existing folder, or the name of a new folder name to have it created. You may also specify a directory path in front of the folder name."
+ cgi_p "Click [cgi_italic Cancel] to return without saving (or creating) anything."
+ }
+ }
+
+ if {[WPCmd PEFolder isincoming 0]} {
+ set f_colid 1
+ } else {
+ set f_colid 0
+ }
+
+ cgi_table_row {
+ cgi_table_data align=center valign=top height=40 class=dialog {
+ cgi_put "Folder name : "
+ cgi_text f_name= type=text size=20 maxlength=256 style=vertical-align:middle onFocus=this.select()
+
+ if {[catch {WPCmd PEFolder collections} collections] == 0 && [llength $collections] > 1} {
+
+ cgi_put "within "
+
+ cgi_select f_colid style=vertical-align:middle {
+ set j 0
+ foreach i $collections {
+ if {$j == $f_colid} {
+ set selected selected
+ } else {
+ set selected {}
+ }
+ if {[string length [set f [lindex $i 1]]] > 12} {
+ set f "[string range $f 0 10]..."
+ }
+
+ cgi_option $f value=$j $selected
+ incr j;
+ }
+ }
+ } else {
+ cgi_text f_colid=0 type=hidden notab
+ }
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data align=center valign=middle height=60 class=dialog {
+ cgi_submit_button "ok= OK "
+ cgi_submit_button cancel=Cancel
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
diff --git a/web/cgi/alpine/1.0/folders.tcl b/web/cgi/alpine/1.0/folders.tcl
new file mode 100755
index 00000000..a173b778
--- /dev/null
+++ b/web/cgi/alpine/1.0/folders.tcl
@@ -0,0 +1,727 @@
+# $Id: folders.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# folders.tcl
+#
+# Purpose: CGI script to generate html output associated with folder
+# and collection management
+#
+# Input:
+set folder_vars {
+ {uid "" 0}
+ {cid {} ""}
+ {show {} ""}
+ {expand {} ""}
+ {contract {} ""}
+ {oncancel "" main}
+ {frestore "" 0}
+ {delquery {} ""}
+ {dwnquery {} ""}
+ {delete {} ""}
+ {renquery {} ""}
+ {rename {} ""}
+ {newfolder {} ""}
+ {folder {} ""}
+ {newdir {} ""}
+ {directory {} ""}
+ {import {} ""}
+ {cancelled {} ""}
+ {fid {} ""}
+ {reload}
+}
+
+set indention 18
+
+# Output:
+#
+# HTML/Javascript/CSS data representing the message specified
+# by the 'uid' argument
+
+proc linecolor {linenum} {
+ if {$linenum % 2} {
+ return "#EEEEEE"
+ } else {
+ return "#FFFFFF"
+ }
+}
+
+proc pretty_folder_name {collections folder} {
+ set fcolid [lindex $folder 0]
+ for {set i 0} {$i < [llength $collections]} {incr i} {
+ set col [lindex $collections $i]
+ if {$fcolid == [lindex $col 0]} {
+ set coltext [lindex $col 1]
+ }
+ }
+ return "${coltext}:[join [lrange $folder 1 end] /]"
+}
+
+#
+# Display the given folder list in a table (w/ mihodge mods)
+#
+proc blat_folder_list {colid flist shown path baseurl scroll anchorcntref depth} {
+ global key border indention mbox _wp
+
+ upvar $anchorcntref anchorcnt
+
+ set rownum 0
+
+ foreach folder $flist {
+ set t [lindex $folder 0]
+ set f [lindex $folder 1]
+ set ff [linsert $path [llength $path] $f]
+ set index -1
+
+ set bgcolor [linecolor [incr anchorcnt]]
+
+ # initial pad=12, expand/contract control is 9px wide
+ set cellpad [expr {12 + ($depth * $indention)}]
+ set delim [WPCmd PEFolder delimiter $colid]
+ set fullpath [join [lrange $ff 1 end] $delim]
+
+ if {[string first F $t] >= 0} {
+ regsub -all {(')} [lrange $ff 1 end] {\\\\\1} ef
+ set celldata [cgi_url $f open.tcl?colid=${colid}&folder=[WPPercentQuote $fullpath]&oncancel=folders&cid=$key target=_top]
+ } else {
+ set celldata $f
+ }
+
+ if {[string first D $t] >= 0} {
+
+ if {[set index [lsearch $shown $ff]] < 0} {
+ set control expand
+ } else {
+ set control contract
+ }
+
+ set celldata "[cgi_url [cgi_imglink $control] "${baseurl}${control}=[WPPercentQuote $ff]#f_[WPPercentQuote $ff]" name=f_[WPPercentQuote $ff] "style=\"padding-right:10px\""]$celldata"
+ incr cellpad -19
+ }
+
+ cgi_table_row bgcolor=$bgcolor {
+
+ cgi_table_data align=center {
+ if {[string first F $t] >= 0 || ([WPCmd PEFolder isincoming $colid] == 0 && [string compare $mbox $fullpath])} {
+ cgi_radio_button "fid=f_[WPPercentQuote $ff]"
+ }
+ }
+
+ cgi_table_data align=left "style=\"padding-left: ${cellpad}px\"" nowrap {
+ cgi_put $celldata
+ }
+
+ cgi_table_data valign=top nowrap {
+ if {[info exists control] && [string compare $control contract] == 0} {
+ cgi_submit_button "new_[WPPercentQuote $ff]=Create New..."
+ cgi_submit_button "imp_[WPPercentQuote $ff]=Import..."
+ } else {
+ cgi_puts [cgi_nbspace]
+ }
+ }
+ }
+
+ if {[string first D $t] >= 0 && $index >= 0} {
+ set nflist [eval WPCmd PEFolder list $ff]
+ set newpath $path
+ lappend newpath $f
+ blat_folder_list $colid $nflist $shown $newpath $baseurl $scroll anchorcnt [expr {$depth + 1}]
+ }
+
+ catch {unset control}
+ }
+}
+
+
+#
+# Command Menu definition for Message View Screen
+#
+set folder_menu {
+}
+
+set common_menu {
+ {
+ {}
+ {
+ {
+ # * * * * Ubiquitous INBOX link * * * *
+ if {[string compare inbox [string tolower $mbox]]} {
+ cgi_put [cgi_url INBOX [cgi_root]/$_wp(appdir)/$_wp(ui1dir)/open.tcl?folder=INBOX&colid=0&cid=[WPCmd PEInfo key] target=_top class=navbar]
+ } else {
+ cgi_put [cgi_url INBOX fr_main.tcl target=_top class=navbar]
+ }
+ }
+ }
+ }
+ {
+ {}
+ {
+ {
+ # * * * * COMPOSE * * * *
+ cgi_puts [cgi_url Compose wp.tcl?page=compose&oncancel=folders&cid=$key target=_top class=navbar]
+ }
+ }
+ }
+ {
+ {}
+ {
+ {
+ # * * * * RESUME * * * *
+ #set button [cgi_img [WPimg but_resume] border=0 alt="Resume"]
+ set button Resume
+ cgi_puts [cgi_url $button wp.tcl?page=resume&oncancel=folders&cid=$key class=navbar]
+ }
+ }
+ }
+ {
+ {}
+ {
+ {
+ # * * * * Addr books * * * *
+ #set button [cgi_img [WPimg but_abook] border=0 alt="Address Book"]
+ set button "Address Book"
+ cgi_puts [cgi_url $button wp.tcl?page=addrbook&oncancel=folders class=navbar]
+ }
+ }
+ }
+ {{cgi_puts [cgi_nbspace]}}
+ {
+ {}
+ {
+ {
+ cgi_put [cgi_url "Configure" wp.tcl?page=conf_process&newconf=1&oncancel=folders&cid=[WPCmd PEInfo key] class=navbar target=_top]
+ }
+ }
+ }
+ {
+ {}
+ {
+ {
+ cgi_put [cgi_url "Get Help" wp.tcl?page=help&oncancel=folders class=navbar target=_top]
+ }
+ }
+ }
+ {{cgi_puts [cgi_nbspace]}}
+ {
+ {}
+ {
+ {
+ # * * * * LOGOUT * * * *
+ if {[WPCmd PEInfo feature quit-without-confirm]} {
+ cgi_puts [cgi_url "Quit $_wp(appname)" $_wp(serverpath)/session/logout.tcl?cid=[WPCmd PEInfo key]&sessid=$sessid target=_top class=navbar]
+ } else {
+ cgi_puts [cgi_url "Quit $_wp(appname)" wp.tcl?page=quit&cid=[WPCmd PEInfo key] target=_top class=navbar]
+ }
+ }
+ }
+ }
+}
+
+## read vars
+foreach item $folder_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} errstr]} {
+ error [list _action "Impart Variable" $errstr]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+if {[catch {WPCmd PEInfo key} key]} {
+ error [list _action "command ID" $key]
+}
+
+# perform requested op
+if {$delquery == 1 || [string compare $delquery Delete] == 0} {
+ if {[string length $fid]} {
+ set fid [string range $fid 2 end]
+ source [file join $_wp(cgipath) $_wp(appdir) $_wp(ui1dir) fr_querydelfldr.tcl]
+ set nopage 1
+ } else {
+ lappend newmail [list "Click button next to folder name then Click [cgi_italic Delete]"]
+ }
+} elseif {$delete == 1 || [string compare $delete Delete] == 0} {
+ if {$cid != $key} {
+ lappend newmail [list "Invalid Command ID"]
+ } elseif {[string length $fid]} {
+ if {[catch [concat WPCmd PEFolder delete $fid] result] == 0} {
+ lappend newmail [list "'[lindex $fid end]' permanently removed"]
+ }
+ } else {
+ lappend newmail [list "Click button next to folder name then Click [cgi_italic Delete]"]
+ }
+} elseif {[string compare $delete Cancel] == 0} {
+ catch {WPCmd PEInfo unset wp_folder_script}
+ lappend newmail [list "Folder Delete Cancelled"]
+} elseif {[string compare $rename Cancel] == 0} {
+ catch {WPCmd PEInfo unset wp_folder_script}
+ lappend newmail [list "Folder Rename Cancelled"]
+} elseif {$renquery == 1 || [string compare $renquery Rename] == 0} {
+ if {$cid != $key} {
+ lappend newmail [list "Invalid Command ID"]
+ } elseif {[string length $fid]} {
+ set fid [string range $fid 2 end]
+ source [file join $_wp(cgipath) $_wp(appdir) $_wp(ui1dir) fr_queryrenfldr.tcl]
+ set nopage 1
+ } else {
+ lappend newmail [list "Click button next to folder name then Click [cgi_italic Rename]"]
+ }
+} elseif {$dwnquery == 1 || [string compare $dwnquery Export] == 0} {
+ if {$cid != $key} {
+ lappend newmail [list "Invalid Command ID"]
+ } elseif {[string length $fid] <= 0} {
+ lappend newmail [list "Click button next to folder name then Click [cgi_italic Export]"]
+ } elseif {[file isdirectory $_wp(detachpath)] <= 0} {
+ lappend newmail [list "Server Configuration Problem: $_wp(detachpath) does not exist"]
+ } else {
+ source [file join $_wp(cgipath) $_wp(appdir) $_wp(ui1dir) exporting.tcl]
+ set nopage 1
+ }
+} elseif {[string compare $rename Rename] == 0} {
+ if {[string length $folder]} {
+ if {[catch [concat WPCmd PEFolder rename $fid [list $folder]] result]} {
+ } else {
+ lappend newmail [list "'[lindex $fid end]' renamed to '$folder'"]
+ }
+ } else {
+ lappend newmail [list "Rename failed: no new name provided"]
+ }
+} elseif {[string compare [string range $newfolder 0 5] Create] == 0} {
+ if {$cid != $key} {
+ lappend newmail [list "Invalid Command ID"]
+ } elseif {[string length $folder]} {
+ set fpath [lrange $fid 1 end]
+ lappend fpath $folder
+ if {[catch {WPCmd PEFolder delimiter [lindex $fid 0]} result]
+ || [catch {WPCmd PEFolder create [lindex $fid 0] [join $fpath $result]} result]} {
+ lappend newmail [list "Create failed: $result"]
+ } else {
+ lappend newmail [list "Folder $folder created"]
+ }
+ } else {
+ lappend newmail [list "Folder creation failed: no folder name provided"]
+ }
+} elseif {[string compare $newfolder Cancel] == 0} {
+ catch {WPCmd PEInfo unset wp_folder_script}
+ lappend newmail [list "Folder Create Cancelled"]
+} elseif {[string compare [string range $import 0 5] Import] == 0} {
+ if {[catch {WPImport file "Missing File Upload"} errstr] == 0} {
+ set local_file [lindex $file 0]
+
+ if {[catch {WPImport iname "import name"} errstr] == 0} {
+ set iname [string trim $iname]
+
+ if {[string length $iname]} {
+
+ set colid [lindex $fid 0]
+ set fldr [eval "file join [lrange $fid 1 end] $iname"]
+
+ if {[catch {WPCmd PEFolder import $local_file $colid $fldr} errstr] == 0} {
+ lappend newmail [list "Imported folder $iname"]
+ } else {
+ lappend newmail [list "Can't Import File: $errstr"]
+ }
+ } else {
+ lappend newmail [list "Must provide uploaded folder name"]
+ }
+ } else {
+ lappend newmail [list "Can't get uploaded folder name"]
+ }
+
+ catch {file delete -force $local_file}
+ } else {
+ lappend newmail [list "Problem uploading file"]
+ }
+} elseif {[string compare [string range $newdir 0 5] Create] == 0} {
+ if {$cid != $key} {
+ lappend newmail [list "Invalid Command ID"]
+ } elseif {[string length $directory]} {
+ set fpath [lrange $fid 1 end]
+ lappend fpath "${directory}/"
+ if {[catch {WPCmd PEFolder delimiter [lindex $fid 0]} result]
+ || [catch {WPCmd PEFolder create [lindex $fid 0] [join $fpath $result]} result]} {
+ lappend newmail [list "Create failed: $result"]
+ } else {
+ lappend newmail [list "Folder $directory created"]
+ }
+ } else {
+ lappend newmail [list "Directory Create failed: no name provided"]
+ }
+} elseif {[string compare $newdir Cancel] == 0} {
+ catch {WPCmd PEInfo unset wp_folder_script}
+ lappend newmail [list "Directory Creation Cancelled"]
+} elseif {[string compare $cancelled Cancel] == 0} {
+ catch {WPCmd PEInfo unset wp_folder_script}
+ lappend newmail [list "New Folder or Directory Creation Cancelled"]
+} elseif {[catch {WPCmd PEInfo set wp_folder_script} script] == 0} {
+ catch {WPCmd PEInfo unset wp_folder_script}
+ set uid 0
+ source [file join $_wp(cgipath) $_wp(appdir) $_wp(ui1dir) $script]
+ set nopage 1
+} else {
+ foreach i [cgi_import_list] {
+ switch -regexp -- $i {
+ ^new_[a-zA-Z0-9%_]*$ {
+ set fid [string range $i 4 end]
+ catch {WPCmd PEInfo set fid $fid}
+ catch {WPCmd PEInfo set wp_folder_script fr_querynewdir.tcl}
+ source [file join $_wp(cgipath) $_wp(appdir) $_wp(ui1dir) fr_querynewfoldir.tcl]
+ set nopage 1
+ }
+ ^nd_[a-zA-Z0-9%_]*$ {
+ set fid [string range $i 3 end]
+ catch {WPCmd PEInfo set fid $fid}
+ catch {WPCmd PEInfo set wp_folder_script fr_querynewdir.tcl}
+ source [file join $_wp(cgipath) $_wp(appdir) $_wp(ui1dir) fr_querynewdir.tcl]
+ set nopage 1
+ }
+ ^nf_[a-zA-Z0-9%_]*$ {
+ set fid [string range $i 3 end]
+ catch {WPCmd set fid $fid}
+ catch {WPCmd set wp_folder_script fr_querynewfldr.tcl}
+ source [file join $_wp(cgipath) $_wp(appdir) $_wp(ui1dir) fr_querynewfldr.tcl]
+ set nopage 1
+ }
+ ^imp_[a-zA-Z0-9%_]*$ {
+ set fid [string range $i 4 end]
+ source [file join $_wp(cgipath) $_wp(appdir) $_wp(ui1dir) fr_queryimport.tcl]
+ set nopage 1
+ }
+ default {
+ }
+ }
+
+ catch {WPCmd PEInfo unset fid}
+ catch {WPCmd PEInfo unset wp_folder_script}
+ }
+}
+
+if {[info exists nopage] == 0} {
+ if {$reload || $frestore || ([string length $show] == 0 && [string length $expand] == 0 && [string length $contract] == 0)} {
+ catch {set show [WPCmd PEInfo set fr_show]
+ set expand [WPCmd PEInfo set fr_expand]
+ set contract [WPCmd PEInfo set fr_contract]}
+ } else {
+ WPCmd set fr_show $show
+ WPCmd set fr_expand $expand
+ WPCmd set fr_contract $contract
+ }
+
+ # collect top level folder lists
+ if {[catch {WPCmd PEFolder collections} collections]} {
+ error [list _action "Collection list" $collections]
+ }
+
+ set shown [split $show ,]
+ set scroll {}
+ set anchorcnt 0
+
+ # mihodge: process actions
+ if {[llength $expand]} {
+ lappend shown $expand
+ set scroll $expand
+ }
+
+ if {[llength $contract]} {
+ if {[set index [lsearch $shown $contract]] >= 0} {
+ set shown [lreplace $shown $index $index]
+ set scroll $contract
+ }
+ }
+
+ set baseurl wp.tcl?page=folders&
+
+ if {[llength $shown]} {
+ append baseurl "show=[WPPercentQuote [join $shown ,]]&"
+ }
+
+ # build top-level collection's folder list
+ for {set i 0} {$i < [llength $collections]} {incr i} {
+ set col [lindex $collections $i]
+ set colid [lindex $col 0]
+
+ if {[llength $collections] == 1} {
+ set flist 1
+ } else {
+ if {[set index [lsearch $shown $colid]] < 0} {
+ set flist {}
+ } else {
+ set flist 1
+ }
+ }
+
+ if {[llength $flist]} {
+ if {[catch {WPCmd PEFolder list $colid} flist]} {
+ if {[string compare BADPASSWD [string range $flist 0 8]] == 0} {
+ # control error messages
+ set statmsgs [WPCmd PEInfo statmsgs]
+ WPCmd PEMailbox newmailreset
+ if {[catch {WPCmd PESession creds [lindex $expand 0] folder} creds] == 0 && $creds != 0} {
+ catch {WPCmd PEInfo statmsg "Invalid Username or Password"}
+ WPCmd PESession nocred $expand folder
+ }
+
+ if {[catch {WPCmd PEFolder clextended} coln]} {
+ WPCmd set reason "Can't get Collection Info: $coln"
+ } else {
+ set coln [lindex $coln $expand]
+ if {[regexp {^([a-zA-Z\.]+).*\/user=([^ /]*)} [lindex $coln 4] dummy srvname authuser]} {
+ WPCmd set reason "Listing folders in the [cgi_bold [lindex $coln 1]] collection first requires that you log in to the server [cgi_bold "$srvname"]."
+ WPCmd set authuser $authuser
+ } else {
+ WPCmd set reason "Folders in the [cgi_bold [lindex $coln 1]] collection are on a server that must be logged into."
+ }
+ }
+
+ WPCmd set cid [WPCmd PEInfo key]
+ WPCmd set authcol [lindex $expand 0]
+ WPCmd set authfolder folder
+ WPCmd set authpage [WPPercentQuote "[cgi_root]/$_wp(appdir)/$_wp(ui1dir)/wp.tcl?page=folders&expand=$expand"]
+ WPCmd set authcancel [WPPercentQuote "[cgi_root]/$_wp(appdir)/$_wp(ui1dir)/wp.tcl?page=folders"]
+
+ source [file join $_wp(cgipath) $_wp(appdir) $_wp(ui1dir) fr_queryauth.tcl]
+
+ catch {WPCmd unset fr_expand}
+
+ set nopage 1
+ } else {
+ set flist {}
+ }
+ }
+ }
+
+ lappend collectionfolders $flist
+ }
+}
+
+if {[info exists nopage] == 0} {
+ # collect new mail message and errors
+ if {[catch {WPNewMail $reload} newmailmsg]} {
+ error [list _action "new mail" $newmailmsg]
+ } else {
+ foreach m $newmailmsg {
+ lappend newmail $m
+ }
+
+ if {[info exists newmail] == 0} {
+ set newmail ""
+ }
+ }
+
+ # paint the page
+ cgi_http_head {
+ WPStdHttpHdrs text/html
+ }
+
+ cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "Folder List" folders
+
+ set onload "onLoad="
+ set onunload "onUnload="
+ set normalreload [cgi_buffer {WPHtmlHdrReload "$_wp(appdir)/$_wp(ui1dir)/wp.tcl?page=folders"}]
+
+ if {[info exists _wp(exitonclose)]} {
+ WPExitOnClose
+ append onload "wpLoad();"
+ append onunload "wpUnLoad();"
+
+ cgi_script type="text/javascript" language="JavaScript" {
+ cgi_put "function viewReloadTimer(t){"
+ cgi_put " reloadtimer = window.setInterval('wpLink(); window.location.replace(\\'[cgi_root]/$_wp(appdir)/$_wp(ui1dir)/wp.tcl?page=folders&reload=1\\')', t * 1000);"
+ cgi_puts "}"
+ }
+
+ append onload "viewReloadTimer($_wp(refresh));"
+ cgi_noscript {
+ cgi_puts $normalreload
+ }
+ } else {
+ cgi_puts $normalreload
+ }
+
+ WPStyleSheets
+ if {$_wp(keybindings)} {
+ set kequiv {
+ {{i} {top.location = 'fr_main.tcl'}}
+ {{a} {top.location = 'wp.tcl?page=addrbook'}}
+ {{?} {top.location = 'wp.tcl?page=help&oncancel=folders'}}
+ }
+
+ lappend kequiv [list {c} "top.location = 'wp.tcl?page=compose&oncancel=folders&cid=$key'"]
+
+ append onload [WPTFKeyEquiv $kequiv]
+ }
+ }
+
+ cgi_body bgcolor=$_wp(bordercolor) background=[file join $_wp(imagepath) logo $_wp(logodir) back.gif] "style=\"background-repeat: repeat-x\"" $onload $onunload {
+
+ catch {WPCmd PEInfo set help_context folders}
+
+ # prepare context and navigation information
+
+ set mbox [WPCmd PEMailbox mailboxname]
+
+ lappend pagehier [list "Folder List"]
+ lappend pagehier [list [cgi_bold "\[Return to $mbox\]"] fr_main.tcl "View list of messages"]
+ if {[string compare $oncancel view] == 0} {
+ if {$uid} {
+ set num [WPCmd PEMessage $uid number]
+ } else {
+ set num View
+ }
+
+ lappend pagehier [list [cgi_bold "\[Return to Message $num\]"] view.tcl "View Message"]
+ }
+
+ set navops ""
+
+ WPTFTitle "Folder List" $newmail 0 "folders"
+
+ cgi_table border=0 cellspacing=0 cellpadding=0 width="100%" height="100%" {
+
+ cgi_table_row {
+ cgi_table_data valign=top class=navbar {
+ cgi_table bgcolor=$_wp(menucolor) border=0 cellspacing=0 cellpadding=0 {
+ cgi_table_row {
+ cgi_table_data class=navbar "style=\"padding: 6 0 0 4\"" {
+ cgi_puts [cgi_span "style=font-weight: bold" "Current Folder"]
+ cgi_division align=center "style=margin-top:4;margin-bottom:4" {
+ set mbn [WPCmd PEMailbox mailboxname]
+ if {[string length $mbn] > 16} {
+ set mbn "[string range $mbn 0 14]..."
+ }
+
+ cgi_put [cgi_url $mbn fr_main.tcl target=_top class=navbar]
+
+ switch -exact -- [WPCmd PEMailbox state] {
+ readonly {
+ cgi_br
+ #cgi_put [cgi_span "style=color: black; border: 1px solid red; background-color: pink; font-weight: bold" "Read Only"]
+ cgi_put [cgi_span "style=color: pink; font-weight: bold" "(Read Only)"]
+ }
+ closed {
+ cgi_br
+ #cgi_put [cgi_span "style=color: black; border: 1px solid red; background-color: pink; font-weight: bold" "Closed"]
+ cgi_put [cgi_span "style=color: pink; font-weight: bold" "(Closed)"]
+ }
+ ok -
+ default {}
+ }
+
+ cgi_br
+ }
+
+ cgi_hr "width=75%"
+ }
+ }
+
+ # next comes the menu down the left side, with suitable
+ cgi_table_row {
+ eval {
+ cgi_table_data $_wp(menuargs) class=navbar style=padding-bottom:10 {
+ WPTFCommandMenu folder_menu common_menu
+ }
+ }
+ }
+ }
+ }
+
+ # down the right side of the table is the window's contents
+ cgi_table_data width="100%" bgcolor=#ffffff valign=top {
+
+ if {[llength $collections] > 1} {
+ cgi_division "style=\"font-family: helvetica; padding: 18; text-align: center\"" {
+ cgi_puts [cgi_span "style=font-weight: bold; font-size: 14pt; vertical-align: middle" "Folder Collections"]
+ cgi_br
+ cgi_puts [cgi_span "style=font-size: smaller" "(click to expand, open, delete, etc.)"]
+ }
+ }
+
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=post target=_top {
+ cgi_text "page=folders" type=hidden notab
+ cgi_text "cid=$key" type=hidden notab
+ cgi_text "frestore=1" type=hidden notab
+ # then the table representing the folders
+ cgi_table border=0 cellspacing=0 cellpadding=2 align=center {
+ for {set i 0} {$i < [llength $collections]} {incr i} {
+ set col [lindex $collections $i]
+ set colid [lindex $col 0]
+
+ if {[llength $collections] == 1} {
+ set menu "c"
+ set flist 1
+ } else {
+ if {[set index [lsearch $shown $colid]] < 0} {
+ set menu "ce"
+ set flist {}
+ } else {
+ set menu "cc"
+ set flist 1
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data nowrap valign=middle {
+ if {[WPCmd PEFolder isincoming $colid] == 0 && [llength $flist]} {
+ cgi_image_button delquery=[WPimg but_folddel] border=0 alt=Delete
+ cgi_image_button renquery=[WPimg but_foldren] border=0 alt=Rename style=margin-left:4
+ cgi_image_button dwnquery=[WPimg but_foldexp] border=0 alt=Export style=margin-left:4
+ }
+ }
+
+ if {[llength $collections] > 1} {
+ if {[set index [lsearch $shown $colid]] < 0} {
+ set colpref [cgi_url [cgi_imglink expand] "${baseurl}expand=$colid" name=f_$colid]
+ } else {
+ set colpref [cgi_url [cgi_imglink contract] "${baseurl}contract=$colid" name=f_$colid]
+ }
+ } else {
+ set colpref ""
+ }
+
+ cgi_table_data align=left "style=\"padding-left:10\"" nowrap {
+ if {[llength [lindex $collectionfolders $i]]} {
+ set flist [lindex $collectionfolders $i]
+ }
+
+ cgi_puts ${colpref}[cgi_span "style=font-family:Helvetica; size: large; padding-left:10" "[lindex $col 1]"]
+ }
+
+ if {[llength $flist]} {
+ if {[WPCmd PEFolder isincoming $colid] == 0} {
+ cgi_table_data valign=middle nowrap {
+ cgi_submit_button "new_$colid=Create New..."
+ cgi_submit_button "imp_$colid=Import..."
+ }
+ }
+ }
+ }
+
+ if {[llength $flist]} {
+ blat_folder_list $colid $flist $shown $colid $baseurl $scroll anchorcnt 1
+ }
+
+ cgi_table_row {
+ cgi_table_data height=12 {
+ cgi_put [cgi_nbspace]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/fr_addrbrowse.tcl b/web/cgi/alpine/1.0/fr_addrbrowse.tcl
new file mode 100755
index 00000000..2e1d2504
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_addrbrowse.tcl
@@ -0,0 +1,66 @@
+# $Id: fr_addrbrowse.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_addrbrowse.tcl
+#
+# Purpose: CGI script to generate frame set for selecting addresses
+# in webpine-lite pages. the idea is that this
+# page specifies a frameset that loads a "header" page
+# used to keep the servlet alive via
+# periodic reloads and a "body" page containing static/form
+# elements that can't/needn't be periodically reloaded or
+# is blocked on user input.
+
+# Input:
+set frame_vars {
+ {op "" ""}
+ {field "" ""}
+}
+
+# Output:
+#
+
+## read vars
+foreach item $frame_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} errstr]} {
+ error [list _action "Impart Variable" $errstr]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),*" resize=yes border=0 frameborder=0 framespacing=0 {
+
+ set parms ""
+ if {[info exists frame_vars]} {
+ foreach v $frame_vars {
+ if {[string length [subst $[lindex $v 0]]]} {
+ append parms "&[lindex $v 0]=[subst $[lindex $v 0]]"
+ }
+ }
+ }
+
+ cgi_frame subhdr=header.tcl?title=73
+ cgi_frame subbody=wp.tcl?page=addrbook${parms}
+ }
+ }
diff --git a/web/cgi/alpine/1.0/fr_addredit.tcl b/web/cgi/alpine/1.0/fr_addredit.tcl
new file mode 100644
index 00000000..5836c001
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_addredit.tcl
@@ -0,0 +1,86 @@
+# $Id: fr_addredit.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_addredit.tcl
+#
+# Purpose: CGI script to generate frame set for address book operations
+# in webpine-lite pages. the idea is that this
+# page specifies a frameset that loads a "header" page
+# used to keep the servlet alive via
+# periodic reloads and a "body" page containing static/form
+# elements that can't/needn't be periodically reloaded or
+# is blocked on user input.
+
+# Input:
+set frame_vars {
+ {book {} -1}
+ {nick {} ""}
+ {add {} 0}
+ {fn {} ""}
+ {addrs {} ""}
+ {fcc {} ""}
+ {comment {} ""}
+ {take {} 0}
+ {newnick {} ""}
+ {ai {} -1}
+}
+
+# Output:
+#
+
+## read vars
+foreach item $frame_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} errstr]} {
+ error [list _action "Impart Variable" $errstr]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+
+cgi_http_head {
+ WPStdHttpHdrs
+}
+
+cgi_html {
+ cgi_head {
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),*" resize=yes border=0 frameborder=0 framespacing=0 {
+ set parms ""
+
+ if {[info exists frame_vars]} {
+ foreach v $frame_vars {
+ if {[string length [subst $[lindex $v 0]]]} {
+ if {[string length $parms]} {
+ append parms "&"
+ } else {
+ append parms "?"
+ }
+
+ append parms "[lindex $v 0]=[WPPercentQuote [subst $[lindex $v 0]]]"
+ }
+ }
+ }
+
+ if {$add} {
+ set title 72
+ } else {
+ set title 71
+ }
+
+ cgi_frame hdr=header.tcl?title=${title}
+ cgi_frame body=addredit.tcl${parms}
+ }
+}
diff --git a/web/cgi/alpine/1.0/fr_cledit.tcl b/web/cgi/alpine/1.0/fr_cledit.tcl
new file mode 100755
index 00000000..60ae3909
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_cledit.tcl
@@ -0,0 +1,79 @@
+#!./tclsh
+# $Id: fr_cledit.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_conf_process.tcl
+#
+# Purpose: CGI script to generate frame set for config operations
+# in webpine-lite pages.
+# This page assumes that it was loaded by conf_process.tcl
+
+# Input:
+set frame_vars {
+}
+
+# Output:
+#
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+cgi_http_head {
+ WPStdHttpHdrs
+}
+
+ set add 0
+ if {[info exists cledit_add] && $cledit_add == 1} {
+ set add 1
+ }
+ cgi_html {
+ cgi_head {
+ WPStdHtmlHdr [expr {$add == 1 ? "Add Collection Configuration" : "Edit Collection Configuration"}]
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),*" resize=yes border=0 frameborder=0 framespacing=0 {
+ if {0} {
+ set parms ""
+
+ if {[info exists frame_vars]} {
+ foreach v $frame_vars {
+ if {[string length [subst $[lindex $v 0]]]} {
+ if {[string length $parms]} {
+ append parms "&"
+ } else {
+ append parms "?"
+ }
+
+ append parms "[lindex $v 0]=[subst $[lindex $v 0]]"
+ }
+ }
+ }
+ }
+
+ set title [expr {$add == 1 ? 151 : 152}]
+ set parms ""
+ if {[info exists cledit_add]} {
+ set parms "${parms}&add=${cledit_add}"
+ }
+ if {[info exists cledit_cl]} {
+ set parms "${parms}&cl=${cledit_cl}"
+ }
+ if {[info exists cledit_onclecancel]} {
+ set parms "${parms}&onclecancel=${cledit_onclecancel}"
+ }
+
+ cgi_frame hdr=header.tcl?title=${title}
+ cgi_frame body=wp.tcl?page=cledit&cid=$cid&oncancel=$oncancel$parms
+ }
+ }
+
diff --git a/web/cgi/alpine/1.0/fr_compose.tcl b/web/cgi/alpine/1.0/fr_compose.tcl
new file mode 100755
index 00000000..bcdd3c35
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_compose.tcl
@@ -0,0 +1,144 @@
+# $Id: fr_compose.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_comp.tcl
+#
+# Purpose: CGI script to generate frame set for composer in
+# webpine-lite pages. the idea is that this
+# page specifies a frameset that loads a "header" page
+# used to keep the servlet alive via
+# periodic reloads and a "body" page containing static/form
+# elements that can't/needn't be periodically reloaded or
+# is blocked on user input.
+
+# Input:
+set frame_vars {
+ {uid "" 0}
+ {part "" ""}
+ {style "" ""}
+ {nickto "" ""}
+ {book "" 0}
+ {ai "" -1}
+ {notice "" ""}
+ {repall "" 0}
+ {reptext "" 0}
+ {repqstr "" ""}
+ {restore "" 0}
+ {f_name "" ""}
+ {f_colid "" 0}
+ {cid "Missing Command ID"}
+ {spell "" ""}
+ {oncancel "" "main.tcl"}
+}
+
+# Output:
+#
+
+## read vars
+foreach item $frame_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} errstr]} {
+ error [list _action "Import Variable" $errstr]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+switch -- $style {
+ Reply {
+ set title "40,[WPCmd PEMessage $uid number]"
+ }
+ Forward {
+ set title "30,[WPCmd PEMessage $uid number]"
+ }
+ Postponed {
+ set title "20,"
+ }
+ Spell {
+ # validate input
+ set spellresult {}
+ if {[catch {cgi_import_as last_line last} result] == 0 && [regexp {^[0-9]+$} $last] && $last < 10000} {
+ array set replace {}
+
+ for {set n 0} {$n < $last} {incr n} {
+ if {[catch {cgi_import_as l_$n locs} result] == 0} {
+
+ set words {}
+
+ foreach l [split $locs {,}] {
+ if {[regexp {[0-9]+_([0-9]+)_([0-9]+)} $l match o len]} {
+ if {[info exists replace($l)]} {
+ lappend words [list $o $len $replace($l)]
+ } elseif {([catch {cgi_import_as r_$l newword} result] == 0 && [string length [set newword [string trim $newword { }]]])
+ || ([catch {cgi_import_as s_$l newword} result] == 0 && [string length [set newword [string trim $newword { }]]])} {
+ lappend words [list $o $len $newword]
+ if {[catch {cgi_import_as a_$l allofem} result] == 0
+ && [string compare $allofem on] == 0
+ && [catch {cgi_import_as e_$l allofem} result] == 0} {
+ foreach e [split $allofem {,}] {
+ set replace($e) $newword
+ }
+ }
+ }
+ }
+ }
+
+ if {[llength $words]} {
+ lappend spellresult [list $n $words]
+ }
+ }
+ }
+
+ if {[llength $spellresult]} {
+ catch {WPCmd PEInfo set wp_spellresult $spellresult}
+ }
+ }
+
+ set title "10,"
+ }
+ default {
+ set title "10,"
+ }
+}
+
+cgi_http_head {
+ WPStdHttpHdrs
+}
+
+cgi_html {
+ cgi_head {
+ WPStdHtmlHdr Compose
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),*" resize=yes border=0 frameborder=0 framespacing=0 {
+
+ set parms ""
+
+ if {[info exists frame_vars]} {
+ foreach v $frame_vars {
+ if {[string length [subst $[lindex $v 0]]]} {
+ if {[string length $parms]} {
+ append parms &
+ } else {
+ append parms ?
+ }
+
+ append parms "[lindex $v 0]=[WPPercentQuote [subst $[lindex $v 0]]]"
+ }
+ }
+ }
+
+ cgi_frame hdr=header.tcl?title=$title title="Composer Title"
+ cgi_frame body=compose.tcl${parms} title="Compose Form"
+ }
+}
diff --git a/web/cgi/alpine/1.0/fr_filtedit.tcl b/web/cgi/alpine/1.0/fr_filtedit.tcl
new file mode 100755
index 00000000..41abee9b
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_filtedit.tcl
@@ -0,0 +1,136 @@
+#!./tclsh
+# $Id: fr_filtedit.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_conf_process.tcl
+#
+# Purpose: CGI script to generate frame set for config operations
+# in webpine-lite pages.
+# This page assumes that it was loaded by conf_process.tcl
+
+# Input:
+set frame_vars {
+}
+
+# Output:
+#
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+source filter.tcl
+
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "Filter Configuration"
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),*" resize=yes border=0 frameborder=0 framespacing=0 {
+ if {[info exists filtedit_add] && 1 == $filtedit_add} {
+ set title 153
+ } else {
+ set title 154
+ }
+
+ set parms ""
+ if {[info exists filtedit_add]} {
+ set parms "${parms}&add=${filtedit_add}"
+ }
+ if {[info exists filtedit_fno]} {
+ set parms "${parms}&fno=${filtedit_fno}"
+ }
+ if {[info exists filtedit_onfiltcancel]} {
+ set parms "${parms}&onfiltcancel=${filtedit_onfiltcancel}"
+ }
+ if {[info exists filtedit_indexcolor]} {
+ set parms "${parms}&filtedit_indexcolor=1"
+ if {[info exists fg] && [string length $fg]} {
+ set parms "${parms}&fg=$fg"
+ }
+ if {[info exists bg] && [string length $bg]} {
+ set parms "${parms}&bg=$bg"
+ }
+ if {[info exists fgorbg]} {
+ set parms "${parms}&fgorbg=$fgorbg"
+ }
+ } elseif {[info exists filtedit_score]} {
+ set parms "${parms}&filtedit_score=1"
+ if {[info exists scoreval] && [string length $scoreval]} {
+ set parms "${parms}&scoreval=$scoreval"
+ }
+ if {[info exists scorehdr] && [string length $scorehdr]} {
+ set parms "${parms}&scoreval=$scorehdr"
+ }
+ }
+
+ if {[info exists filterrtext]} {
+ # relay pattern elements
+ set parms "${parms}&filterrtext=${filterrtext}"
+ foreach {pvar pexp} $pattern_id {
+ if {[info exists $pvar]} {
+ if {[string length [set pval [subst $$pvar]]]} {
+ append parms "&${pvar}=${pval}"
+ }
+ }
+ }
+
+ foreach {pvar pexp} $pattern_fields {
+ if {[info exists $pvar]} {
+ if {[string length [set pval [subst $$pvar]]]} {
+ append parms "&${pvar}=${pval}"
+ }
+ }
+ }
+
+ # relay various pattern actions
+ if {[info exists action] || 0 == [catch {WPImport action}]} {
+ append parms "&action=$action"
+ }
+
+ if {0 == [catch {WPImport actionfolder}]} {
+ append parms "&actionfolder=$actionfolder"
+ }
+
+ if {0 == [catch {WPImport moind}] && [string length $moind]} {
+ append parms "&moind=$moind"
+ }
+
+ if {0 == [catch {WPImport actionfolder}]} {
+ append parms "&actionfolder=$actionfolder"
+ }
+
+ if {[info exists folder] == 0} {
+ wpGetVarAs folder folder
+ }
+
+ if {[string length $folder]} {
+ append parms "&folder=$folder"
+ }
+
+ if {[info exists ftype] == 0} {
+ wpGetVarAs ftype ftype
+ }
+
+ if {[string length $ftype]} {
+ append parms "&ftype=$ftype"
+ }
+ }
+
+ cgi_frame hdr=header.tcl?title=${title}
+ cgi_frame body=wp.tcl?page=filtedit&cid=$cid&oncancel=$oncancel$parms
+ }
+ }
+
diff --git a/web/cgi/alpine/1.0/fr_flags.tcl b/web/cgi/alpine/1.0/fr_flags.tcl
new file mode 100755
index 00000000..febc2eaf
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_flags.tcl
@@ -0,0 +1,67 @@
+#!./tclsh
+# $Id: fr_flags.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_flags.tcl
+#
+# Purpose: CGI script to generate frame set for flag setting
+# in webpine-lite pages. the idea is that this
+# page specifies a frameset that loads a "header" page
+# used to keep the servlet alive via
+# periodic reloads and a "body" page containing static/form
+# elements that can't/needn't be periodically reloaded or
+# is blocked on user input.
+
+# Input:
+set frame_vars {
+ {uid "Missing UID"}
+}
+
+# Output:
+#
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+WPEval $frame_vars {
+
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),*" resize=yes border=0 frameborder=0 framespacing=0 {
+
+ set parms ""
+ if {[info exists frame_vars]} {
+ foreach v $frame_vars {
+ if {[string length [subst $[lindex $v 0]]]} {
+ if {[string length $parms]} {
+ append parms "&"
+ } else {
+ append parms "?"
+ }
+
+ append parms "[lindex $v 0]=[subst $[lindex $v 0]]"
+ }
+ }
+ }
+
+ cgi_frame subhdr=header.tcl?title=160
+ cgi_frame subbody=flags.tcl${parms}
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/fr_fldrbrowse.tcl b/web/cgi/alpine/1.0/fr_fldrbrowse.tcl
new file mode 100644
index 00000000..9373e5ff
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_fldrbrowse.tcl
@@ -0,0 +1,71 @@
+# $Id: fr_fldrbrowse.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_fldrbrowse.tcl
+#
+# Purpose: CGI script to generate frame set for selecting a folder from the user's
+# defined collections in webpine-lite pages. the idea is that this
+# page specifies a frameset that loads a "header" page
+# used to keep the servlet alive via
+# periodic reloads and a "body" page containing static/form
+# elements that can't/needn't be periodically reloaded or
+# is blocked on user input.
+
+# Input:
+set frame_vars {
+ {onselect "" main}
+ {oncancel "" main}
+ {target "" ""}
+ {controls "" 0}
+}
+
+# Output:
+#
+
+## read vars
+foreach item $frame_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} errstr]} {
+ error [list _action "Impart Variable" $errstr]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+
+cgi_http_head {
+ WPStdHttpHdrs
+}
+
+cgi_html {
+ cgi_head {
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),*" resize=yes border=0 frameborder=0 framespacing=0 {
+
+ set parms ""
+ foreach v $frame_vars {
+ if {[string length [subst $[lindex $v 0]]]} {
+ append parms "&[lindex $v 0]=[WPPercentQuote [subst $[lindex $v 0]]]"
+ }
+ }
+
+ switch $controls {
+ 2 { set tnum 221 }
+ default {set tnum 220}
+ }
+
+ cgi_frame subhdr=header.tcl?title=${tnum} title="Folder Selection for Save"
+ cgi_frame subbody=wp.tcl?page=fldrbrowse${parms} title="Folder Selection Frame"
+ }
+}
diff --git a/web/cgi/alpine/1.0/fr_fldrsavenew.tcl b/web/cgi/alpine/1.0/fr_fldrsavenew.tcl
new file mode 100644
index 00000000..f59a8914
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_fldrsavenew.tcl
@@ -0,0 +1,65 @@
+# $Id: fr_fldrsavenew.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_fldrcreate.tcl
+#
+# Purpose: CGI script to generate frame set for creating a new folder to save to
+
+# Input:
+set frame_vars {
+ {onselect "" main}
+ {oncancel "" main}
+ {target "" ""}
+ {controls "" 0}
+}
+
+# Output:
+#
+
+## read vars
+foreach item $frame_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} errstr]} {
+ error [list _action "Impart Variable" $errstr]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+
+cgi_http_head {
+ WPStdHttpHdrs
+}
+
+cgi_html {
+ cgi_head {
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),*" resize=yes border=0 frameborder=0 framespacing=0 {
+
+ set parms ""
+ foreach v $frame_vars {
+ if {[string length [subst $[lindex $v 0]]]} {
+ append parms "&[lindex $v 0]=[WPPercentQuote [subst $[lindex $v 0]]]"
+ }
+ }
+
+ switch $controls {
+ 2 { set tnum 223 }
+ default {set tnum 222}
+ }
+
+ cgi_frame subhdr=header.tcl?title=${tnum} title="Folder Creation for Save"
+ cgi_frame subbody=wp.tcl?page=fldrsavenew${parms} title="Folder Creation Frame"
+ }
+}
diff --git a/web/cgi/alpine/1.0/fr_help.tcl b/web/cgi/alpine/1.0/fr_help.tcl
new file mode 100755
index 00000000..a6f7e388
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_help.tcl
@@ -0,0 +1,91 @@
+# $Id: fr_help.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_help.tcl
+#
+# Purpose: CGI script to generate frame set for display of help text
+# in webpine-lite pages. the idea is that this
+# page specifies a frameset that loads a "header" page
+# used to keep the servlet alive via
+# periodic reloads and a "body" page containing static/form
+# elements that can't/needn't be periodically reloaded or
+# is blocked on user input.
+
+# Input:
+set frame_vars {
+ {topic "" ""}
+ {index "" ""}
+ {oncancel "" ""}
+}
+
+# Output:
+#
+
+## read vars
+foreach item $frame_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} errstr]} {
+ error [list _action "Impart Variable" $errstr]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+
+cgi_http_head {
+ WPStdHttpHdrs
+}
+
+cgi_html {
+ cgi_head {
+ cgi_title "WebPine Help"
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),0,*" resize=yes border=0 frameborder=0 framespacing=0 {
+
+ set parms ""
+ if {[info exists help_vars]} {
+ foreach v $help_vars {
+ if {[string length [subst $[lindex $v 0]]]} {
+ if {[string length $parms]} {
+ append parms "&"
+ } else {
+ append parms "?"
+ }
+
+ lappend parmlist [lindex $v 0]
+
+ append parms "[lindex $v 0]=[WPPercentQuote [subst $[lindex $v 0]]]"
+ }
+ }
+ }
+
+ if {[info exists frame_vars]} {
+ foreach v $frame_vars {
+ if {(![info exists parmlist] || [lsearch -exact $parmlist [lindex $v 0]] < 0) && [string length [subst $[lindex $v 0]]]} {
+ if {[string length $parms]} {
+ append parms "&"
+ } else {
+ append parms "?"
+ }
+
+ append parms "[lindex $v 0]=[WPPercentQuote [subst $[lindex $v 0]]]"
+ }
+ }
+ }
+
+ cgi_frame hdr=header.tcl?title=140 title="Help Title"
+ cgi_frame noop=wp.tcl?page=noop
+ cgi_frame body=help.tcl$parms title="Help Body"
+ }
+}
diff --git a/web/cgi/alpine/1.0/fr_index.tcl b/web/cgi/alpine/1.0/fr_index.tcl
new file mode 100755
index 00000000..92b34184
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_index.tcl
@@ -0,0 +1,58 @@
+#!./tclsh
+# $Id: fr_index.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_index.tcl
+#
+# Purpose: CGI script to serve as the frame-work for including
+# supplied script snippets that generate the various
+# javascript-free webpine pages
+
+# Input:
+set index_vars {
+ {expunge ""}
+ {emptyit ""}
+ {f_colid {} ""}
+ {f_name {} ""}
+ {cid {} 0}
+ {split {} 0}
+}
+
+# Output:
+#
+
+# read config
+source ./alpine.tcl
+
+WPEval $index_vars {
+ cgi_http_head {
+ WPStdHttpHdrs {} 10
+ }
+
+ cgi_html {
+ cgi_head {
+ }
+
+ cgi_frameset "rows=100%,*" border=0 frameborder=0 framespacing=0 {
+ set parms ""
+ if {[info exists index_vars]} {
+ foreach v $index_vars {
+ if {[string length [subst $[lindex $v 0]]]} {
+ append parms "&[lindex $v 0]=[subst $[lindex $v 0]]"
+ }
+ }
+ }
+
+ cgi_frame body=wp.tcl?page=index${parms} title="Message List"
+ }
+ }
+} \ No newline at end of file
diff --git a/web/cgi/alpine/1.0/fr_ldapbrowse.tcl b/web/cgi/alpine/1.0/fr_ldapbrowse.tcl
new file mode 100755
index 00000000..c6ac8c59
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_ldapbrowse.tcl
@@ -0,0 +1,61 @@
+# $Id: fr_ldapbrowse.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_ldapbrowse.tcl
+#
+# Purpose: CGI script to generate frame set for selecting addresses
+# from an LDAP query in webpine-lite pages. the idea is that this
+# page specifies a frameset that loads a "header" page
+# used to keep the servlet alive via
+# periodic reloads and a "body" page containing static/form
+# elements that can't/needn't be periodically reloaded or
+# is blocked on user input.
+
+# Input: (NOTE: these are expected to be set when we get here)
+set frame_vars {
+ {ldapquery "" ""}
+ {addresses "" ""}
+ {field "" ""}
+}
+
+# Output:
+#
+
+cgi_http_head {
+ WPStdHttpHdrs
+}
+
+cgi_html {
+ cgi_head {
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),*" resize=yes border=0 frameborder=0 framespacing=0 {
+
+ set parms ""
+ if {[info exists frame_vars]} {
+ foreach v $frame_vars {
+ if {[string length [subst $[lindex $v 0]]]} {
+ if {[string length $parms]} {
+ append parms "&"
+ } else {
+ append parms "?"
+ }
+
+ append parms "[lindex $v 0]=[subst $[lindex $v 0]]"
+ }
+ }
+ }
+
+ cgi_frame subhdr=header.tcl?title=74
+ cgi_frame subbody=ldapbrowse.tcl${parms}
+ }
+}
diff --git a/web/cgi/alpine/1.0/fr_ldapquery.tcl b/web/cgi/alpine/1.0/fr_ldapquery.tcl
new file mode 100644
index 00000000..b2b90e24
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_ldapquery.tcl
@@ -0,0 +1,75 @@
+# $Id: fr_ldapquery.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_ldapquery.tcl
+#
+# Purpose: CGI script to generate frame set for LDAP server query
+# handling in webpine-lite pages. the idea is that this
+# page specifies a frameset that loads a "header" page
+# used to keep the servlet alive via
+# periodic reloads and a "body" page containing static/form
+# elements that can't/needn't be periodically reloaded or
+# is blocked on user input.
+
+# Input:
+set frame_vars {
+ {dir "Missing Directory Index"}
+ {srchstr {} ""}
+ {field {} ""}
+ {op {} ""}
+ {searchtype {} ""}
+}
+
+# Output:
+#
+
+## read vars
+foreach item $frame_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} errstr]} {
+ error [list _action "Impart Variable" $errstr]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+
+cgi_http_head {
+ WPStdHttpHdrs
+}
+
+cgi_html {
+ cgi_head {
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),*" resize=yes border=0 frameborder=0 framespacing=0 {
+ set parms ""
+
+ if {[info exists frame_vars]} {
+ foreach v $frame_vars {
+ if {[string length [subst $[lindex $v 0]]]} {
+ if {[string length $parms]} {
+ append parms "&"
+ } else {
+ append parms "?"
+ }
+
+ append parms "[lindex $v 0]=[WPPercentQuote [subst $[lindex $v 0]]]"
+ }
+ }
+ }
+
+ cgi_frame hdr=header.tcl?title=240
+ cgi_frame body=ldapquery.tcl${parms}
+ }
+}
diff --git a/web/cgi/alpine/1.0/fr_main.tcl b/web/cgi/alpine/1.0/fr_main.tcl
new file mode 100755
index 00000000..8654ab69
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_main.tcl
@@ -0,0 +1,34 @@
+#!./tclsh
+# $Id: fr_main.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_main.tcl - frame set for Index and View
+#
+# Purpose: CGI script to serve as the frame-work for including
+# supplied script snippets that generate the various
+# javascript-free webpine pages
+
+# Input:
+set frame_vars {
+ {expunge {} 0}
+}
+
+# Output:
+#
+
+# inherit global config
+source ./alpine.tcl
+
+
+WPEval $frame_vars {
+ source main.tcl
+}
diff --git a/web/cgi/alpine/1.0/fr_promptsave.tcl b/web/cgi/alpine/1.0/fr_promptsave.tcl
new file mode 100755
index 00000000..4bbf040b
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_promptsave.tcl
@@ -0,0 +1,68 @@
+# $Id: fr_promptsave.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_promptsave.tcl
+#
+# Purpose: CGI script to generate frame set for resume composition form
+# in webpine-lite pages. the idea is that this
+# page specifies a frameset that loads a "header" page
+# used to keep the servlet alive via
+# periodic reloads and a "body" page containing static/form
+# elements that can't/needn't be periodically reloaded or
+# is blocked on user input.
+
+# Input:
+set frame_vars {
+}
+
+# Output:
+#
+
+## read vars
+foreach item $frame_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} errstr]} {
+ error [list _action "Impart Variable" $errstr]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+cgi_http_head {
+ WPStdHttpHdrs
+}
+
+cgi_html {
+ cgi_head {
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),*" resize=yes border=3 frameborder=0 framespacing=0 {
+
+ set parms ""
+ if {[info exists frame_vars]} {
+ foreach v $frame_vars {
+ if {[string length [subst $[lindex $v 0]]]} {
+ if {[string length $parms]} {
+ append parms "&"
+ } else {
+ append parms "?"
+ }
+
+ append parms "[lindex $v 0]=[subst $[lindex $v 0]]"
+ }
+ }
+ }
+
+ cgi_frame subhdr=header.tcl?title=190
+ cgi_frame subbody=promptsave.tcl${parms}
+ }
+}
diff --git a/web/cgi/alpine/1.0/fr_queryattach.tcl b/web/cgi/alpine/1.0/fr_queryattach.tcl
new file mode 100755
index 00000000..8cbdc015
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_queryattach.tcl
@@ -0,0 +1,70 @@
+# $Id: fr_queryattach.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_queryattach.tcl
+#
+# Purpose: CGI script to generate frame set for specifying an attachment
+# in webpine-lite pages. the idea is that this
+# page specifies a frameset that loads a "header" page
+# used to keep the servlet alive via
+# periodic reloads and a "body" page containing static/form
+# elements that can't/needn't be periodically reloaded or
+# is blocked on user input.
+
+# Input:
+set frame_vars {
+}
+
+# Output:
+#
+
+## read vars
+foreach item $frame_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} errstr]} {
+ error [list _action "Impart Variable" $errstr]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+
+cgi_http_head {
+ WPStdHttpHdrs
+}
+
+cgi_html {
+ cgi_head {
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),*" resize=yes border=0 frameborder=0 framespacing=0 {
+
+ set parms ""
+ if {[info exists frame_vars]} {
+ foreach v $frame_vars {
+ if {[string length [subst $[lindex $v 0]]]} {
+ if {[string length $parms]} {
+ append parms "&"
+ } else {
+ append parms "?"
+ }
+
+ append parms "[lindex $v 0]=[subst $[lindex $v 0]]"
+ }
+ }
+ }
+
+ cgi_frame subhdr=header.tcl?title=110
+ cgi_frame subbody=queryattach.tcl${parms}
+ }
+}
diff --git a/web/cgi/alpine/1.0/fr_queryauth.tcl b/web/cgi/alpine/1.0/fr_queryauth.tcl
new file mode 100755
index 00000000..ad5d13d1
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_queryauth.tcl
@@ -0,0 +1,71 @@
+# $Id: fr_queryauth.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_queryauth.tcl
+#
+# Purpose: CGI script to generate frame set for input of user/passwd
+# in webpine-lite pages. the idea is that this
+# page specifies a frameset that loads a "header" page
+# used to keep the servlet alive via
+# periodic reloads and a "body" page containing static/form
+# elements that can't/needn't be periodically reloaded or
+# is blocked on user input.
+
+# Input:
+set frame_vars {
+ {cid "Missing Command ID"}
+ {authcol "Missing Authenticaion Collection"}
+ {authfolder "Missing Authentication Folder"}
+ {authpage "Missing Post Authorization Instructions"}
+ {authcancel "Missing Auth Cancel Instructions"}
+ {authuser "" ""}
+ {reason "" ""}
+}
+
+# Output:
+#
+
+## read vars
+foreach item $frame_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} errstr]} {
+ error [list _action "Impart Variable" $errstr]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+
+cgi_http_head {
+ WPStdHttpHdrs
+}
+
+cgi_html {
+ cgi_head {
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),*" resize=yes border=0 frameborder=0 framespacing=0 {
+
+ set parms ""
+ if {[info exists frame_vars]} {
+ foreach v $frame_vars {
+ if {[string length [subst $[lindex $v 0]]]} {
+ append parms "&[lindex $v 0]=[subst $[lindex $v 0]]"
+ }
+ }
+ }
+
+ cgi_frame hdr=header.tcl?title=130 title="Status Frame"
+ cgi_frame body=$_wp(serverpath)/session/queryauth.tcl?sessid=$_wp(sessid)${parms} title="User and Password Prompt"
+ }
+}
diff --git a/web/cgi/alpine/1.0/fr_querycreate.tcl b/web/cgi/alpine/1.0/fr_querycreate.tcl
new file mode 100755
index 00000000..b3ff332c
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_querycreate.tcl
@@ -0,0 +1,42 @@
+# $Id: fr_querycreate.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_querycreate.tcl
+#
+# Purpose: CGI script to generate frame set for creation confirmation
+# in webpine-lite pages. the idea is that this
+# page specifies a frameset that loads a "header" page
+# used to keep the servlet alive via
+# periodic reloads and a "body" page containing static/form
+# elements that can't/needn't be periodically reloaded or
+# is blocked on user input.
+
+# Input:
+set frame_vars {
+}
+
+# Output:
+#
+
+cgi_http_head {
+ WPStdHttpHdrs
+}
+
+cgi_html {
+ cgi_head {
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),*" resize=yes border=0 frameborder=0 framespacing=0 {
+ cgi_frame subhdr=header.tcl?title=120 title="Status Frame"
+ cgi_frame subbody=querycreate.tcl title="Creation Confirmation Prompt"
+ }
+}
diff --git a/web/cgi/alpine/1.0/fr_querydelfldr.tcl b/web/cgi/alpine/1.0/fr_querydelfldr.tcl
new file mode 100755
index 00000000..653c52cb
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_querydelfldr.tcl
@@ -0,0 +1,43 @@
+# $Id: fr_querydelfldr.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_querydelfldr.tcl
+#
+# Purpose: CGI script to generate frame set for folder delete confirmation
+# in webpine-lite pages. the idea is that this
+# page specifies a frameset that loads a "header" page
+# used to keep the servlet alive via
+# periodic reloads and a "body" page containing static/form
+# elements that can't/needn't be periodically reloaded or
+# is blocked on user input.
+
+# Input:
+set frame_vars {
+ {fid "No Folder Specified"}
+}
+
+# Output:
+#
+
+cgi_http_head {
+ WPStdHttpHdrs
+}
+
+cgi_html {
+ cgi_head {
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),*" resize=yes border=0 frameborder=0 framespacing=0 {
+ cgi_frame subhdr=header.tcl?title=170
+ cgi_frame subbody=querydelfldr.tcl?fid=$fid
+ }
+}
diff --git a/web/cgi/alpine/1.0/fr_queryexpunge.tcl b/web/cgi/alpine/1.0/fr_queryexpunge.tcl
new file mode 100755
index 00000000..704c12c7
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_queryexpunge.tcl
@@ -0,0 +1,71 @@
+# $Id: fr_queryexpunge.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_expunge.tcl
+#
+# Purpose: CGI script to generate frame set for expunge confirmation
+# in webpine-lite pages. the idea is that this
+# page specifies a frameset that loads a "header" page
+# used to keep the servlet alive via
+# periodic reloads and a "body" page containing static/form
+# elements that can't/needn't be periodically reloaded or
+# is blocked on user input.
+
+# Input:
+set frame_vars {
+}
+
+# Output:
+#
+
+## read vars
+foreach item $frame_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} errstr]} {
+ error [list _action "Impart Variable" $errstr]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+cgi_http_head {
+ WPStdHttpHdrs
+}
+
+cgi_html {
+ cgi_head {
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),*" resize=yes border=0 frameborder=0 framespacing=0 {
+
+ WPCmd PEInfo set wp_spec_script fr_queryexpunge.tcl
+
+ set parms ""
+ if {[info exists frame_vars]} {
+ foreach v $frame_vars {
+ if {[string length [subst $[lindex $v 0]]]} {
+ if {[string length $parms]} {
+ append parms "&"
+ } else {
+ append parms "?"
+ }
+
+ append parms "[lindex $v 0]=[subst $[lindex $v 0]]"
+ }
+ }
+ }
+
+ cgi_frame subhdr=header.tcl?title=90
+ cgi_frame subbody=queryexpunge.tcl${parms}
+ }
+}
diff --git a/web/cgi/alpine/1.0/fr_queryimport.tcl b/web/cgi/alpine/1.0/fr_queryimport.tcl
new file mode 100644
index 00000000..d4e7ee28
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_queryimport.tcl
@@ -0,0 +1,44 @@
+# $Id: fr_queryimport.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_queryimport.tcl
+#
+# Purpose: CGI script to generate frame set for specifying a folder
+# to upload and import to a collection. the idea is that this
+# page specifies a frameset that loads a "header" page
+# used to keep the servlet alive via
+# periodic reloads and a "body" page containing static/form
+# elements that can't/needn't be periodically reloaded or
+# is blocked on user input.
+
+# Input:
+set frame_vars {
+ {fid "No Folder Specified"}
+}
+
+# Output:
+#
+
+cgi_http_head {
+ WPStdHttpHdrs
+}
+
+cgi_html {
+ cgi_head {
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),*" resize=yes border=0 frameborder=0 framespacing=0 {
+
+ cgi_frame subhdr=header.tcl?title=250 title="Folder Import Dialog"
+ cgi_frame subbody=queryimport.tcl?fid=$fid
+ }
+}
diff --git a/web/cgi/alpine/1.0/fr_querynewdir.tcl b/web/cgi/alpine/1.0/fr_querynewdir.tcl
new file mode 100755
index 00000000..a8f54899
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_querynewdir.tcl
@@ -0,0 +1,43 @@
+# $Id: fr_querynewdir.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_querynewdir.tcl
+#
+# Purpose: CGI script to generate frame set for Directory create confirmation
+# in webpine-lite pages. the idea is that this
+# page specifies a frameset that loads a "header" page
+# used to keep the servlet alive via
+# periodic reloads and a "body" page containing static/form
+# elements that can't/needn't be periodically reloaded or
+# is blocked on user input.
+
+# Input:
+set frame_vars {
+ {fid "No Folder Specified"}
+}
+
+# Output:
+#
+
+cgi_http_head {
+ WPStdHttpHdrs
+}
+
+cgi_html {
+ cgi_head {
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),*" resize=yes border=0 frameborder=0 framespacing=0 {
+ cgi_frame subhdr=header.tcl?title=172
+ cgi_frame subbody=querynewdir.tcl?fid=$fid
+ }
+}
diff --git a/web/cgi/alpine/1.0/fr_querynewfldr.tcl b/web/cgi/alpine/1.0/fr_querynewfldr.tcl
new file mode 100755
index 00000000..6bc17b42
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_querynewfldr.tcl
@@ -0,0 +1,43 @@
+# $Id: fr_querynewfldr.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_querynewfldr.tcl
+#
+# Purpose: CGI script to generate frame set for folder create confirmation
+# in webpine-lite pages. the idea is that this
+# page specifies a frameset that loads a "header" page
+# used to keep the servlet alive via
+# periodic reloads and a "body" page containing static/form
+# elements that can't/needn't be periodically reloaded or
+# is blocked on user input.
+
+# Input:
+set frame_vars {
+ {fid "No Folder Specified"}
+}
+
+# Output:
+#
+
+cgi_http_head {
+ WPStdHttpHdrs
+}
+
+cgi_html {
+ cgi_head {
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),*" resize=yes border=0 frameborder=0 framespacing=0 {
+ cgi_frame subhdr=header.tcl?title=171
+ cgi_frame subbody=querynewfldr.tcl?fid=$fid
+ }
+}
diff --git a/web/cgi/alpine/1.0/fr_querynewfoldir.tcl b/web/cgi/alpine/1.0/fr_querynewfoldir.tcl
new file mode 100755
index 00000000..fc6a61b7
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_querynewfoldir.tcl
@@ -0,0 +1,43 @@
+# $Id: fr_querynewfoldir.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_querynewfoldir.tcl
+#
+# Purpose: CGI script to generate frame set for Folder/Directory create confirmation
+# in webpine-lite pages. the idea is that this
+# page specifies a frameset that loads a "header" page
+# used to keep the servlet alive via
+# periodic reloads and a "body" page containing static/form
+# elements that can't/needn't be periodically reloaded or
+# is blocked on user input.
+
+# Input:
+set frame_vars {
+ {fid "No Folder Specified"}
+}
+
+# Output:
+#
+
+cgi_http_head {
+ WPStdHttpHdrs
+}
+
+cgi_html {
+ cgi_head {
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),*" resize=yes border=0 frameborder=0 framespacing=0 {
+ cgi_frame subhdr=header.tcl?title=174
+ cgi_frame subbody=querynewfoldir.tcl?fid=$fid
+ }
+}
diff --git a/web/cgi/alpine/1.0/fr_queryprune.tcl b/web/cgi/alpine/1.0/fr_queryprune.tcl
new file mode 100644
index 00000000..20e9638c
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_queryprune.tcl
@@ -0,0 +1,66 @@
+# $Id: fr_queryprune.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_queryprune.tcl
+#
+# Purpose: CGI script to generate frame set for pruning dialog
+# in webpine.
+
+# Input:
+set frame_vars {
+ {cid "Missing Command ID"}
+ {start "Missing Start Page"}
+ {nojs "" 0}
+}
+
+# Output:
+#
+
+## read vars
+foreach item $frame_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} errstr]} {
+ error [list _action "Impart Variable" $errstr]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+
+cgi_http_head {
+ WPStdHttpHdrs
+}
+
+cgi_html {
+ cgi_head {
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),*" resize=yes border=0 frameborder=0 framespacing=0 {
+
+ set parms ""
+ if {[info exists frame_vars]} {
+ foreach v $frame_vars {
+ if {[string length [subst $[lindex $v 0]]]} {
+ if {[string length $parms]} {
+ append parms "&"
+ }
+
+ append parms "[lindex $v 0]=[WPPercentQuote [subst $[lindex $v 0]]]"
+ }
+ }
+ }
+
+ cgi_frame hdr=header.tcl?title=260 title="Folder Pruning Frame"
+ cgi_frame body=queryprune.tcl?sessid=$_wp(sessid)&${parms} title="Folder Pruning Dialog"
+ }
+}
diff --git a/web/cgi/alpine/1.0/fr_queryquit.tcl b/web/cgi/alpine/1.0/fr_queryquit.tcl
new file mode 100644
index 00000000..4f12e8ba
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_queryquit.tcl
@@ -0,0 +1,70 @@
+# $Id: fr_queryquit.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_queryquit.tcl
+#
+# Purpose: CGI script to generate frame set for logging out of a session
+# in webpine-lite pages. the idea is that this
+# page specifies a frameset that loads a "header" page
+# used to keep the servlet alive via
+# periodic reloads and a "body" page containing static/form
+# elements that can't/needn't be periodically reloaded or
+# is blocked on user input.
+
+# Input:
+set frame_vars {
+ {cid "Missing Command ID"}
+}
+
+# Output:
+#
+
+## read vars
+foreach item $frame_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} errstr]} {
+ error [list _action "Impart Variable" $errstr]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+cgi_http_head {
+ WPStdHttpHdrs
+}
+
+cgi_html {
+ cgi_head {
+ cgi_http_equiv Refresh "$_wp(logoutpause); url=$_wp(serverpath)/session/logout.tcl?cid=[WPCmd PEInfo key]&sessid=${sessid}"
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),*" resize=yes border=3 frameborder=0 framespacing=0 {
+
+ set parms ""
+ if {[info exists frame_vars]} {
+ foreach v $frame_vars {
+ if {[string length [subst $[lindex $v 0]]]} {
+ if {[string length $parms]} {
+ append parms "&"
+ } else {
+ append parms "?"
+ }
+
+ append parms "[lindex $v 0]=[subst $[lindex $v 0]]"
+ }
+ }
+ }
+
+ cgi_frame subhdr=header.tcl?title=230
+ cgi_frame subbody=queryquit.tcl${parms}
+ }
+}
diff --git a/web/cgi/alpine/1.0/fr_queryrenfldr.tcl b/web/cgi/alpine/1.0/fr_queryrenfldr.tcl
new file mode 100755
index 00000000..a498700c
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_queryrenfldr.tcl
@@ -0,0 +1,43 @@
+# $Id: fr_queryrenfldr.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_queryrenfldr.tcl
+#
+# Purpose: CGI script to generate frame set for folder rename confirmation
+# in webpine-lite pages. the idea is that this
+# page specifies a frameset that loads a "header" page
+# used to keep the servlet alive via
+# periodic reloads and a "body" page containing static/form
+# elements that can't/needn't be periodically reloaded or
+# is blocked on user input.
+
+# Input:
+set frame_vars {
+ {fid "No Folder Specified"}
+}
+
+# Output:
+#
+
+cgi_http_head {
+ WPStdHttpHdrs
+}
+
+cgi_html {
+ cgi_head {
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),*" resize=yes border=0 frameborder=0 framespacing=0 {
+ cgi_frame subhdr=header.tcl?title=173
+ cgi_frame subbody=queryrenfldr.tcl?fid=$fid
+ }
+}
diff --git a/web/cgi/alpine/1.0/fr_querysave.tcl b/web/cgi/alpine/1.0/fr_querysave.tcl
new file mode 100755
index 00000000..af8e8c88
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_querysave.tcl
@@ -0,0 +1,68 @@
+# $Id: fr_querysave.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_querysave.tcl
+#
+# Purpose: CGI script to generate frame set for resume composition form
+# in webpine-lite pages. the idea is that this
+# page specifies a frameset that loads a "header" page
+# used to keep the servlet alive via
+# periodic reloads and a "body" page containing static/form
+# elements that can't/needn't be periodically reloaded or
+# is blocked on user input.
+
+# Input:
+set frame_vars {
+}
+
+# Output:
+#
+
+## read vars
+foreach item $frame_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} errstr]} {
+ error [list _action "Impart Variable" $errstr]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+cgi_http_head {
+ WPStdHttpHdrs
+}
+
+cgi_html {
+ cgi_head {
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),*" resize=yes border=3 frameborder=0 framespacing=0 {
+
+ set parms ""
+ if {[info exists frame_vars]} {
+ foreach v $frame_vars {
+ if {[string length [subst $[lindex $v 0]]]} {
+ if {[string length $parms]} {
+ append parms "&"
+ } else {
+ append parms "?"
+ }
+
+ append parms "[lindex $v 0]=[subst $[lindex $v 0]]"
+ }
+ }
+ }
+
+ cgi_frame subhdr=header.tcl?title=90
+ cgi_frame subbody=querysave.tcl${parms}
+ }
+}
diff --git a/web/cgi/alpine/1.0/fr_resume.tcl b/web/cgi/alpine/1.0/fr_resume.tcl
new file mode 100755
index 00000000..3e332cae
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_resume.tcl
@@ -0,0 +1,104 @@
+# $Id: fr_resume.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_resume.tcl
+#
+# Purpose: CGI script to generate frame set for resume composition form
+# in webpine-lite pages. the idea is that this
+# page specifies a frameset that loads a "header" page
+# used to keep the servlet alive via
+# periodic reloads and a "body" page containing static/form
+# elements that can't/needn't be periodically reloaded or
+# is blocked on user input.
+
+# Input:
+set frame_vars {
+ {oncancel "" main}
+}
+
+# Output:
+#
+
+## read vars
+foreach item $frame_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} errstr]} {
+ error [list _action "Impart Variable" $errstr]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+
+proc ppnd_uid {ppnd} {
+ foreach i $ppnd {
+ switch -- [lindex $i 0] {
+ uid {
+ return [lindex $i 1]
+ }
+ }
+ }
+
+ error "No Valid UID for Postponed message"
+}
+
+
+if {[catch {WPLoadCGIVar cid} result]} {
+ catch {WPCmd PEInfo statmsg "Missing Command ID: $result"}
+ source [WPTFScript $oncancel]
+} elseif {$cid != [WPCmd PEInfo key]} {
+ catch {WPCmd PEInfo statmsg "Invalid Command ID"}
+ source [WPTFScript $oncancel]
+} elseif {[catch {WPCmd PEPostpone count} postponed]} {
+ catch {WPCmd PEInfo statmsg "Resume Error: $postponed"}
+ source [WPTFScript $oncancel]
+} else {
+ switch -- $postponed {
+ -1 -
+ 0 {
+ catch {WPCmd PEInfo statmsg "No Postponed Messages"}
+ source [WPTFScript $oncancel]
+ }
+ default {
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),*" resize=yes border=0 frameborder=0 framespacing=0 {
+ set parms ""
+
+ if {[info exists frame_vars]} {
+ foreach v $frame_vars {
+ if {[string length [subst $[lindex $v 0]]]} {
+ if {[string length $parms]} {
+ append parms "&"
+ } else {
+ append parms "?"
+ }
+
+ append parms "[lindex $v 0]=[subst $[lindex $v 0]]"
+ }
+ }
+ }
+
+ cgi_frame hdr=header.tcl?title=80 title="Status Frame"
+ cgi_frame body=resume.tcl${parms} title="Resumable Message List"
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/fr_seldate.tcl b/web/cgi/alpine/1.0/fr_seldate.tcl
new file mode 100755
index 00000000..99f0a933
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_seldate.tcl
@@ -0,0 +1,70 @@
+# $Id: fr_seldate.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_seldate.tcl
+#
+# Purpose: CGI script to generate frame set for selecting by date
+# in webpine-lite pages. the idea is that this
+# page specifies a frameset that loads a "header" page
+# used to keep the servlet alive via
+# periodic reloads and a "body" page containing static/form
+# elements that can't/needn't be periodically reloaded or
+# is blocked on user input.
+
+# Input:
+set frame_vars {
+ {uid "" 0}
+}
+
+# Output:
+#
+
+## read vars
+foreach item $frame_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} errstr]} {
+ error [list _action "Impart Variable" $errstr]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+cgi_http_head {
+ WPStdHttpHdrs
+}
+
+cgi_html {
+ cgi_head {
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),*" resize=yes border=0 frameborder=0 framespacing=0 {
+
+ set parms ""
+ if {[info exists frame_vars]} {
+ foreach v $frame_vars {
+ if {[string length [subst $[lindex $v 0]]]} {
+ if {[string length $parms]} {
+ append parms "&"
+ } else {
+ append parms "?"
+ }
+
+ append parms "[lindex $v 0]=[subst $[lindex $v 0]]"
+ }
+ }
+ }
+
+ cgi_frame subhdr=header.tcl?title=101
+ cgi_frame subbody=seldate.tcl${parms}
+ }
+}
diff --git a/web/cgi/alpine/1.0/fr_select.tcl b/web/cgi/alpine/1.0/fr_select.tcl
new file mode 100755
index 00000000..8b70d23a
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_select.tcl
@@ -0,0 +1,71 @@
+# $Id: fr_select.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_select.tcl
+#
+# Purpose: CGI script to generate frame set for selecting by the various methods
+# in webpine-lite pages. the idea is that this
+# page specifies a frameset that loads a "header" page
+# used to keep the servlet alive via
+# periodic reloads and a "body" page containing static/form
+# elements that can't/needn't be periodically reloaded or
+# is blocked on user input.
+
+# Input:
+set frame_vars {
+ {uid "" 0}
+}
+
+# Output:
+#
+
+## read vars
+foreach item $frame_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} errstr]} {
+ error [list _action "Impart Variable" $errstr]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+
+cgi_http_head {
+ WPStdHttpHdrs
+}
+
+cgi_html {
+ cgi_head {
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),*" resize=yes border=0 frameborder=0 framespacing=0 {
+
+ set parms ""
+ if {[info exists frame_vars]} {
+ foreach v $frame_vars {
+ if {[string length [subst $[lindex $v 0]]]} {
+ if {[string length $parms]} {
+ append parms "&"
+ } else {
+ append parms "?"
+ }
+
+ append parms "[lindex $v 0]=[subst $[lindex $v 0]]"
+ }
+ }
+ }
+
+ cgi_frame subhdr=header.tcl?title=104 title="Status Frame"
+ cgi_frame subbody=select.tcl${parms} title="Search Criteria"
+ }
+}
diff --git a/web/cgi/alpine/1.0/fr_selstat.tcl b/web/cgi/alpine/1.0/fr_selstat.tcl
new file mode 100755
index 00000000..317684fd
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_selstat.tcl
@@ -0,0 +1,71 @@
+# $Id: fr_selstat.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_selstat.tcl
+#
+# Purpose: CGI script to generate frame set for selecting by status
+# in webpine-lite pages. the idea is that this
+# page specifies a frameset that loads a "header" page
+# used to keep the servlet alive via
+# periodic reloads and a "body" page containing static/form
+# elements that can't/needn't be periodically reloaded or
+# is blocked on user input.
+
+# Input:
+set frame_vars {
+ {uid "" 0}
+}
+
+# Output:
+#
+
+## read vars
+foreach item $frame_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} errstr]} {
+ error [list _action "Impart Variable" $errstr]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),*" resize=yes border=0 frameborder=0 framespacing=0 {
+
+ set parms ""
+ if {[info exists frame_vars]} {
+ foreach v $frame_vars {
+ if {[string length [subst $[lindex $v 0]]]} {
+ if {[string length $parms]} {
+ append parms "&"
+ } else {
+ append parms "?"
+ }
+
+ append parms "[lindex $v 0]=[subst $[lindex $v 0]]"
+ }
+ }
+ }
+
+ cgi_frame subhdr=header.tcl?title=103
+ cgi_frame subbody=selstat.tcl${parms}
+ }
+ }
diff --git a/web/cgi/alpine/1.0/fr_seltext.tcl b/web/cgi/alpine/1.0/fr_seltext.tcl
new file mode 100755
index 00000000..644ed353
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_seltext.tcl
@@ -0,0 +1,71 @@
+# $Id: fr_seltext.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_seltext.tcl
+#
+# Purpose: CGI script to generate frame set for selecting by text
+# in webpine-lite pages. the idea is that this
+# page specifies a frameset that loads a "header" page
+# used to keep the servlet alive via
+# periodic reloads and a "body" page containing static/form
+# elements that can't/needn't be periodically reloaded or
+# is blocked on user input.
+
+# Input:
+set frame_vars {
+ {uid "" 0}
+}
+
+# Output:
+#
+
+## read vars
+foreach item $frame_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} errstr]} {
+ error [list _action "Impart Variable" $errstr]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+
+cgi_http_head {
+ WPStdHttpHdrs
+}
+
+cgi_html {
+ cgi_head {
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),*" resize=yes border=0 frameborder=0 framespacing=0 {
+
+ set parms ""
+ if {[info exists frame_vars]} {
+ foreach v $frame_vars {
+ if {[string length [subst $[lindex $v 0]]]} {
+ if {[string length $parms]} {
+ append parms "&"
+ } else {
+ append parms "?"
+ }
+
+ append parms "[lindex $v 0]=[subst $[lindex $v 0]]"
+ }
+ }
+ }
+
+ cgi_frame subhdr=header.tcl?title=102
+ cgi_frame subbody=seltext.tcl${parms}
+ }
+}
diff --git a/web/cgi/alpine/1.0/fr_spellcheck.tcl b/web/cgi/alpine/1.0/fr_spellcheck.tcl
new file mode 100755
index 00000000..bdd64e07
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_spellcheck.tcl
@@ -0,0 +1,71 @@
+# $Id: fr_spellcheck.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_spellcheck.tcl
+#
+# Purpose: CGI script to generate frame set for spell checking composition
+# in webpine-lite pages. the idea is that this
+# page specifies a frameset that loads a "header" page
+# used to keep the servlet alive via
+# periodic reloads and a "body" page containing static/form
+# elements that can't/needn't be periodically reloaded or
+# is blocked on user input.
+
+# Input:
+set frame_vars {
+ {repqstr "" ""}
+}
+
+# Output:
+#
+
+## read vars
+foreach item $frame_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} errstr]} {
+ error [list _action "Impart Variable" $errstr]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+
+cgi_http_head {
+ WPStdHttpHdrs
+}
+
+cgi_html {
+ cgi_head {
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),*" resize=yes border=0 frameborder=0 framespacing=0 {
+
+ set parms ""
+ if {[info exists frame_vars]} {
+ foreach v $frame_vars {
+ if {[string length [subst $[lindex $v 0]]]} {
+ if {[string length $parms]} {
+ append parms "&"
+ } else {
+ append parms "?"
+ }
+
+ append parms "[lindex $v 0]=[subst $[lindex $v 0]]"
+ }
+ }
+ }
+
+ cgi_frame hdr=header.tcl?title=180
+ cgi_frame body=spellcheck.tcl${parms}
+ }
+}
diff --git a/web/cgi/alpine/1.0/fr_split.tcl b/web/cgi/alpine/1.0/fr_split.tcl
new file mode 100644
index 00000000..1861ad35
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_split.tcl
@@ -0,0 +1,58 @@
+# $Id: fr_split.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_split.tcl
+#
+# Purpose: CGI script to serve as the frame-work for including
+# supplied script snippets that generate the various
+# javascript-free webpine pages
+
+# Input:
+set split_vars {
+}
+
+# Output:
+#
+
+## read vars
+foreach item $split_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} errstr]} {
+ error [list _action "Impart Variable" $errstr]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+cgi_http_head {
+ WPStdHttpHdrs {} 10
+}
+
+cgi_html {
+ cgi_head {
+ }
+
+ cgi_frameset "rows=33%,*" {
+ set parms ""
+ if {[info exists split_vars]} {
+ foreach v $split_vars {
+ if {[string length [subst $[lindex $v 0]]]} {
+ append parms "&[lindex $v 0]=[subst $[lindex $v 0]]"
+ }
+ }
+ }
+
+ cgi_frame fr_top=wp.tcl?page=index&split=1${parms} title="Message List"
+ cgi_frame fr_bottom=fr_view.tcl?split=1${parms} title="Message View"
+ }
+}
diff --git a/web/cgi/alpine/1.0/fr_take.tcl b/web/cgi/alpine/1.0/fr_take.tcl
new file mode 100644
index 00000000..b5a37ebb
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_take.tcl
@@ -0,0 +1,70 @@
+# $Id: fr_take.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_take.tcl
+#
+# Purpose: CGI script to generate frame set for taking addresses
+# in webpine-lite pages. the idea is that this
+# page specifies a frameset that loads a "header" page
+# used to keep the servlet alive via
+# periodic reloads and a "body" page containing static/form
+# elements that can't/needn't be periodically reloaded or
+# is blocked on user input.
+
+# Input:
+set frame_vars {
+ {uid "Missing UID"}
+}
+
+# Output:
+#
+
+## read vars
+foreach item $frame_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} errstr]} {
+ error [list _action "Impart Variable" $errstr]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+cgi_http_head {
+ WPStdHttpHdrs
+}
+
+cgi_html {
+ cgi_head {
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),*" resize=yes border=0 frameborder=0 framespacing=0 {
+
+ set parms ""
+ if {[info exists frame_vars]} {
+ foreach v $frame_vars {
+ if {[string length [subst $[lindex $v 0]]]} {
+ if {[string length $parms]} {
+ append parms "&"
+ } else {
+ append parms "?"
+ }
+
+ append parms "[lindex $v 0]=[subst $[lindex $v 0]]"
+ }
+ }
+ }
+
+ cgi_frame subhdr=header.tcl?title=210
+ cgi_frame subbody=takeaddr.tcl${parms}
+ }
+}
diff --git a/web/cgi/alpine/1.0/fr_takeedit.tcl b/web/cgi/alpine/1.0/fr_takeedit.tcl
new file mode 100644
index 00000000..a339bafe
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_takeedit.tcl
@@ -0,0 +1,79 @@
+# $Id: fr_takeedit.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_takeedit.tcl
+#
+# Purpose: CGI script to generate frame set for taking addresses
+# in webpine-lite pages. the idea is that this
+# page specifies a frameset that loads a "header" page
+# used to keep the servlet alive via
+# periodic reloads and a "body" page containing static/form
+# elements that can't/needn't be periodically reloaded or
+# is blocked on user input.
+
+# Input:
+set frame_vars {
+ {book "Missing address book"}
+ {nick {} ""}
+ {add {} 0}
+ {fn {} ""}
+ {addrs {} ""}
+ {fcc {} ""}
+ {comment {} ""}
+ {take {} 0}
+ {newnick {} ""}
+ {ai {} -1}
+}
+
+# Output:
+#
+
+## read vars
+foreach item $frame_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} errstr]} {
+ error [list _action "Impart Variable" $errstr]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+cgi_http_head {
+ WPStdHttpHdrs
+}
+
+cgi_html {
+ cgi_head {
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),*" resize=yes border=0 frameborder=0 framespacing=0 {
+
+ set parms ""
+ if {[info exists frame_vars]} {
+ foreach v $frame_vars {
+ if {[string length [subst $[lindex $v 0]]]} {
+ if {[string length $parms]} {
+ append parms "&"
+ } else {
+ append parms "?"
+ }
+
+ append parms "[lindex $v 0]=[subst $[lindex $v 0]]"
+ }
+ }
+ }
+
+ cgi_frame subhdr=header.tcl?title=211
+ cgi_frame subbody=addredit.tcl${parms}
+ }
+}
diff --git a/web/cgi/alpine/1.0/fr_takesame.tcl b/web/cgi/alpine/1.0/fr_takesame.tcl
new file mode 100644
index 00000000..fb69a4aa
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_takesame.tcl
@@ -0,0 +1,79 @@
+# $Id: fr_takesame.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_takeedit.tcl
+#
+# Purpose: CGI script to generate frame set for taking addresses
+# in webpine-lite pages. the idea is that this
+# page specifies a frameset that loads a "header" page
+# used to keep the servlet alive via
+# periodic reloads and a "body" page containing static/form
+# elements that can't/needn't be periodically reloaded or
+# is blocked on user input.
+
+# Input:
+set frame_vars {
+ {book "Missing address book"}
+ {nick {} ""}
+ {add {} 0}
+ {fn {} ""}
+ {addrs {} ""}
+ {fcc {} ""}
+ {comment {} ""}
+ {take {} 0}
+ {newnick {} ""}
+ {ai {} -1}
+}
+
+# Output:
+#
+
+## read vars
+foreach item $frame_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} errstr]} {
+ error [list _action "Impart Variable" $errstr]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+cgi_http_head {
+ WPStdHttpHdrs
+}
+
+cgi_html {
+ cgi_head {
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),*" resize=yes border=0 frameborder=0 framespacing=0 {
+
+ set parms ""
+ if {[info exists frame_vars]} {
+ foreach v $frame_vars {
+ if {[string length [subst $[lindex $v 0]]]} {
+ if {[string length $parms]} {
+ append parms "&"
+ } else {
+ append parms "?"
+ }
+
+ append parms "[lindex $v 0]=[subst $[lindex $v 0]]"
+ }
+ }
+ }
+
+ cgi_frame subhdr=header.tcl?title=212
+ cgi_frame subbody=querynick.tcl${parms}
+ }
+}
diff --git a/web/cgi/alpine/1.0/fr_tconfig.tcl b/web/cgi/alpine/1.0/fr_tconfig.tcl
new file mode 100755
index 00000000..a7d62244
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_tconfig.tcl
@@ -0,0 +1,69 @@
+#!./tclsh
+# $Id: fr_tconfig.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_conf_process.tcl
+#
+# Purpose: CGI script to generate frame set for config operations
+# in webpine-lite pages.
+# This page assumes that it was loaded by conf_process.tcl
+
+# Intput:
+set frame_vars {
+}
+
+# Output:
+#
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "Configuration"
+ }
+
+ cgi_frameset "rows=$_wp(titleheight),*" resize=yes border=0 frameborder=0 framespacing=0 {
+ if {0} {
+ set parms ""
+
+ if {[info exists frame_vars]} {
+ foreach v $frame_vars {
+ if {[string length [subst $[lindex $v 0]]]} {
+ if {[string length $parms]} {
+ append parms "&"
+ } else {
+ append parms "?"
+ }
+
+ append parms "[lindex $v 0]=[subst $[lindex $v 0]]"
+ }
+ }
+ }
+ }
+
+ set title 150
+
+ set vlavarstr ""
+ if {[info exists fr_tconfig_vlavar]} {
+ set vlavarstr "&vlavar=$fr_tconfig_vlavar"
+ }
+ cgi_frame hdr=header.tcl?title=${title} title="Status Frame"
+ cgi_frame body=wp.tcl?page=tconfig&newconf=$newconf&oncancel=$oncancel&wv=$conftype$vlavarstr title="Configuration options"
+ }
+ }
+
diff --git a/web/cgi/alpine/1.0/fr_view.tcl b/web/cgi/alpine/1.0/fr_view.tcl
new file mode 100755
index 00000000..a4258baa
--- /dev/null
+++ b/web/cgi/alpine/1.0/fr_view.tcl
@@ -0,0 +1,30 @@
+#!./tclsh
+# $Id: fr_view.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# fr_view.tcl - wrapper around do_view.tcl to start tcl interp
+#
+# Purpose: CGI script to serve as the frame-work for including
+# supplied script snippets that generate the various
+# javascript-free webpine pages
+
+# Input:
+
+# Output:
+#
+
+# inherit global config
+source ./alpine.tcl
+
+WPEval {} {
+ source do_view.tcl
+}
diff --git a/web/cgi/alpine/1.0/genvars.tcl b/web/cgi/alpine/1.0/genvars.tcl
new file mode 100755
index 00000000..b982aee9
--- /dev/null
+++ b/web/cgi/alpine/1.0/genvars.tcl
@@ -0,0 +1,114 @@
+# $Id: genvars.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+set general_vars {
+ {var personal-name "Name"}
+ {var user-domain "User Domain"}
+ {var inbox-path "Inbox Location"}
+ {var default-fcc "Default Fcc"}
+ {var postponed-folder "Default Postponed Folder"}
+ {var alt-addresses "Other Addresses"}
+ {var wp-indexheight "Font Size"}
+ {var smtp-server "SMTP Server"}
+ {feat enable-newmail-sound "Enable Background Sound on New Mail Arrival"}
+ {feat enable-flag-cmd "Enable Flag Command"}
+ {feat auto-move-read-msgs "Automatically Move Read Messages"}
+ {feat expunge-without-confirm "Expunge INBOX Without Confirming"}
+ {feat expunge-without-confirm-everywhere "Expunge Everywhere Without Confirming"}
+ {feat quit-without-confirm "Quit Without Confirming"}
+ {feat enable-jump-cmd "Enable Jump Command"}
+ {special wp-columns "Display Width"}
+ {special left-column-folders "Message View/List Folder List Count"}
+}
+
+set msglist_vars {
+ {special index-format "Message Line Format"}
+ {var sort-key "Sort By"}
+ {var incoming-startup-rule "Start At"}
+ {var wp-indexlines "Message Lines Displayed"}
+ {feat mark-for-cc "Mark Messages For Cc"}
+ {feat enable-aggregate-command-set "Enable Aggregate Commands"}
+ {feat auto-zoom-after-select "Zoom View after Search"}
+ {feat auto-unselect-after-apply "Unmark Messages After Command"}
+}
+
+set composer_vars {
+ {special signature "Signature"}
+ {var default-composer-hdrs "Default Composer Headers"}
+ {var customized-hdrs "Customized Headers"}
+ {var fcc-name-rule "Fcc name rule"}
+ {var empty-header-message "Empty Header Message"}
+ {var posting-character-set "Posting Character Set"}
+ {feat compose-rejects-unqualified-addrs "Compose Rejects Unqualified Addresses"}
+ {feat enable-sigdashes "Enable Sigdashes"}
+ {feat quell-user-lookup-in-passwd-file "Don't Look Up Users in passwd File"}
+ {var reply-indent-string "Reply Indent String"}
+ {feat enable-reply-indent-string-editing "Enable Reply Indent String Editing"}
+ {var reply-leadin "Reply Leadin"}
+ {feat include-attachments-in-reply "Include Attachments in Reply"}
+ {feat include-header-in-reply "Include Header in Reply"}
+ {feat include-text-in-reply "Include Text in Reply"}
+ {feat signature-at-bottom "Signature at Bottom"}
+ {feat strip-from-sigdashes-on-reply "Strip From Sigdashes on Reply"}
+ {feat fcc-without-attachments "Fcc Without Attachments"}
+ {feat enable-8bit-esmtp-negotiation "Enable 8bit ESMTP Negotiation"}
+ {feat enable-delivery-status-notification "Enable Delivery Status Notification"}
+ {feat enable-verbose-smtp-posting "Enable Verbose SMTP Posting"}
+ {feat use-sender-not-x-sender "Use Sender, Not X-Sender"}
+ {feat send-confirms-only-expanded "Confirm Send only if Addresses Expanded"}
+ {feat quell-content-id "Prevent Content-ID header in Attachments"}
+ {feat quell-format-flowed "Do Not Send Flowed Text"}
+ {feat forward-as-attachment "Forward message as attachment"}
+}
+
+set folder_vars {
+ {special collections "Folder Collections"}
+ {var default-fcc "Default Fcc"}
+ {var default-saved-msg-folder "Default Saved Message Folder"}
+ {var saved-msg-name-rule "Saved Message Name Rule"}
+ {var postponed-folder "Postponed Folder"}
+ {var read-message-folder "Read Messages Folder"}
+ {var form-letter-folder "Form Letter Folder"}
+ {var folder-sort-rule "Folder Sort Rule"}
+ {var incoming-folders "Incoming Folders"}
+ {var pruned-folders "Pruned Folders"}
+ {var pruning-rule "Pruning Rule"}
+ {feat prune-uses-yyyy-mm "Prune Uses YYYY-MM"}
+ {feat enable-dot-folders "Enable Hidden Folders"}
+ {feat enable-lame-list-mode "Enable Lame List Mode"}
+ {feat try-alternative-authentication-driver-first "Try Alternative Authentication First"}
+ {feat quell-empty-directories "Do Not Display Empty Directores"}
+}
+
+set address_vars {
+ {var address-book "Address Books"}
+ {var global-address-book "Global Address Books"}
+ {var addrbook-sort-rule "Address Book Sort Rule"}
+ {var ldap-servers "Directory Servers"}
+}
+
+set msgview_vars {
+ {special view-colors "Message View Color Settings"}
+ {var viewer-hdrs "Viewer Headers"}
+ {feat enable-msg-view-urls "Enable URLs"}
+ {feat enable-msg-view-web-hostnames "Enable Web Hostnames"}
+ {feat enable-msg-view-addresses "Enable Address Links"}
+ {feat enable-msg-view-attachments "Enable Attachments View"}
+ {feat enable-full-header-cmd "Enable Full Headers"}
+ {feat quell-host-after-url "Hide server name display after links in HTML"}
+}
+
+set rule_vars {
+ {special filters "Filters"}
+ {special scores "Scoring"}
+ {special indexcolor "Index Colors"}
+}
diff --git a/web/cgi/alpine/1.0/header.tcl b/web/cgi/alpine/1.0/header.tcl
new file mode 100755
index 00000000..8bff53e6
--- /dev/null
+++ b/web/cgi/alpine/1.0/header.tcl
@@ -0,0 +1,226 @@
+#!./tclsh
+# $Id: header.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# header.tcl
+#
+# Purpose: CGI script to generate generic header for
+# webpine-lite pages. the idea is that this
+# page goes in the {title,nav}-bar portion of a
+# framed page so we keep the servlet alive via
+# periodic reloads while more static/form stuff
+# is displayed in the "body" frame.
+
+# Input:
+set header_vars {
+ {title "" "0,0"}
+ {reload}
+}
+
+# Output:
+#
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+set nologo 0
+set about {}
+
+WPEval $header_vars {
+
+ set tv [split $title ","]
+ switch -- [lindex $tv 0] {
+ 10 {
+ set title_text "Compose New Message"
+ }
+ 20 {
+ set title_text "Postponed Message"
+ }
+ 30 {
+ set title_text "Forward Message"
+ if {[regexp {^[0-9]*$} [lindex $tv 1]]} {
+ append title_text " [lindex $tv 1]"
+ }
+ }
+ 40 {
+ set title_text "Reply to Message"
+ if {[regexp {^[0-9]*$} [lindex $tv 1]]} {
+ append title_text " [lindex $tv 1]"
+ }
+ }
+ 50 {
+ set title_text "Message Index"
+ set nologo 1
+ }
+ 60 {
+ set title_text "Help System"
+ }
+ 70 {
+ set title_text "Address Books"
+ }
+ 71 {
+ set title_text "Edit Address Book Entry"
+ }
+ 72 {
+ set title_text "New Address Book Entry"
+ }
+ 73 {
+ set title_text "Address Selection"
+ }
+ 74 {
+ set title_text "LDAP Query Result Selection"
+ }
+ 80 {
+ set title_text "Select Postponed Message"
+ }
+ 90 {
+ set title_text "Expunge Deleted Messages"
+ set nologo 1
+ }
+ 101 {
+ set title_text "Select Messages by Date"
+ set nologo 1
+ }
+ 102 {
+ set title_text "Select Messages by Text"
+ set nologo 1
+ }
+ 103 {
+ set title_text "Select Messages by Status"
+ set nologo 1
+ }
+ 104 {
+ set title_text "Search and Mark Messages"
+ set nologo 1
+ }
+ 110 {
+ set title_text "Attach a File"
+ }
+ 120 {
+ set title_text "File Creation Confirmation"
+ set nologo 1
+ }
+ 130 {
+ set title_text "Authentication Required"
+ }
+ 140 {
+ set title_text "Web Alpine Help"
+ }
+ 150 {
+ set title_text "Configuration"
+ }
+ 151 {
+ set title_text "Add Collection Configuration"
+ }
+ 152 {
+ set title_text "Edit Collection Configuration"
+ }
+ 153 {
+ set title_text "Add Filter Configuration"
+ }
+ 154 {
+ set title_text "Edit Filter Configuration"
+ }
+ 160 {
+ set title_text "Set Message Flags"
+ set nologo 1
+ }
+ 170 {
+ set title_text "Confirm Folder Delete"
+ }
+ 171 {
+ set title_text "New Folder Creation"
+ }
+ 172 {
+ set title_text "New Directory Creation"
+ }
+ 173 {
+ set title_text "Folder Rename"
+ }
+ 174 {
+ set title_text "Create New Folder or Directory"
+ }
+ 180 {
+ set title_text "Spell Check Composition"
+ }
+ 190 {
+ set title_text "Save Messages"
+ set nologo 1
+ }
+ 200 {
+ set title_text "Attachment Display"
+ }
+ 210 {
+ set title_text "Take Addresses"
+ }
+ 211 {
+ set title_text "Take Address Edit"
+ }
+ 212 {
+ set title_text "Take Address Same Nickname"
+ }
+ 220 {
+ set title_text "Folder List for Save"
+ set nologo 1
+ }
+ 221 {
+ set title_text "Folder List for Save"
+ }
+ 222 {
+ set title_text "Folder To Save To"
+ set nologo 1
+ }
+ 223 {
+ set title_text "Folder To Save To"
+ }
+ 230 {
+ set title_text "Quitting Web Alpine"
+ }
+ 240 {
+ set title_text "LDAP Query"
+ }
+ 250 {
+ set title_text "Folder Upload and Import"
+ }
+ 260 {
+ set title_text "Monthly Folder Clean Up"
+ }
+ default {
+ if {[catch {WPCmd PEInfo set wp_header_title} title_text]} {
+ set title_text Untitled
+ }
+ }
+ }
+
+ WPCmd PEInfo set wp_header_title $title_text
+
+ if {[catch {WPNewMail $reload} newmail]} {
+ error [list _action "new mail" $newmail]
+ }
+
+ cgi_http_head {
+ WPStdHttpHdrs text/html
+ }
+
+ cgi_html {
+ cgi_head {
+ WPStdHtmlHdr Header
+ WPHtmlHdrReload "$_wp(appdir)/$_wp(ui1dir)/header.tcl?sessid=$sessid"
+ WPStyleSheets
+ }
+
+ cgi_body bgcolor=$_wp(bordercolor) background=[file join $_wp(imagepath) logo $_wp(logodir) back.gif] "style=\"background-repeat: repeat-x\"" {
+ WPTFTitle $title_text $newmail $nologo $about
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/help.tcl b/web/cgi/alpine/1.0/help.tcl
new file mode 100755
index 00000000..6881895d
--- /dev/null
+++ b/web/cgi/alpine/1.0/help.tcl
@@ -0,0 +1,36 @@
+#!./tclsh
+# $Id: help.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# help.tcl
+#
+# Purpose: CGI script to generate html help text for Alpine
+
+# Input:
+set help_vars {
+ {topic {} ""}
+ {topicclass {} ""}
+ {index {} ""}
+ {oncancel {} main}
+ {params {} ""}
+}
+
+# Output:
+#
+# HTML/Javascript/CSS help text for alpine
+
+# inherit global config
+source ./alpine.tcl
+
+WPEval $help_vars {
+ source do_help.tcl
+}
diff --git a/web/cgi/alpine/1.0/help/about.tcl b/web/cgi/alpine/1.0/help/about.tcl
new file mode 100644
index 00000000..44d28a12
--- /dev/null
+++ b/web/cgi/alpine/1.0/help/about.tcl
@@ -0,0 +1,30 @@
+# $Id: about.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+cgi_h2 "About Web Alpine"
+cgi_p "Version [WPCmd PEInfo version].[WPCmd PEInfo revision] (basic HTML interface)"
+cgi_p {
+Web Alpine is a mail user agent, built on the Alpine Mail System, designed to
+provide email access and management facilities via the World Wide Web.
+As a web-based application, Alpine provides universal, convenient, and
+secure access to your email environment.
+}
+cgi_p {Because its foundation is shared
+with the Alpine Mail System, Web Alpine can easily provide a view of your email
+environment consistent with that of Alpine and PC-Alpine. However, due to
+inherent speed and efficiency limitations of web email, it is not intended
+to replace any other email programs. Web Alpine is designed specifically for
+those away from their own desktop computers and for people with light duty
+email needs.
+}
+cgi_p {
+}
+cgi_p "Send comments and suggestions to [cgi_quote_html "<$_wp(comments)>"]."
diff --git a/web/cgi/alpine/1.0/help/addrbook.html b/web/cgi/alpine/1.0/help/addrbook.html
new file mode 100644
index 00000000..11399f33
--- /dev/null
+++ b/web/cgi/alpine/1.0/help/addrbook.html
@@ -0,0 +1,61 @@
+<!--
+# $Id: addrbook.html 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+-->
+<font size="+2"><b>Address Books</b></font>
+
+<p>The Address Books screen provides a listing of addresses and contact information. If you have
+more than one address book, your "Read-Only" books will be listed directly beneath your personal
+listing.</p>
+
+<ul type="square">
+<li><a href="#add"><b>Adding an entry</b></a></li>
+<li><a href="#remove"><b>Removing an entry</b></a></li>
+<li><a href="#edit"><b>Editing an entry</b></a></li>
+<li><a href="#create"><b>Creating a distribution list</b></a></li>
+<li><a href="#compose"><b>Going directly to Message Compose</b></a></li>
+</ul>
+
+<a name="add"><b>Adding an entry</b></a><br>
+Click <input type=submit value="Add New Entry"> to go to a screen that includes the following
+fields: "Nickname:", "Full Name:", "Addresses:", "Fcc:", "Comments:".
+Complete the information that you want to save in your address book and
+click <input type=submit value="Save Entry"> to add an entry to your personal addressbook.
+
+<p>For more information about these fields, see the <a href="addrentry.html">Address Book Entry</a>
+help text.</p>
+
+<p>
+<a name="remove"><b>Removing an entry</b></a><br>
+Click the nickname of an entry you would like to remove. On the next
+screen, click <input type=submit value="Delete Entry">.
+<p>
+
+<a name="edit"><b>Editing an entry</b></a><br>
+Click the nickname of an entry you would like to edit. On the next
+screen, make your desired changes and click <input type=submit value="Save Entry">.
+<p>
+
+<a name="create"><b>Creating a distribution list</b></a><br>
+Click <input type=submit value="Add New Entry"> to go to a screen that includes the following
+fields: "Nickname:", "Full Name:", "Addresses:", "Fcc:", "Comments:".
+Complete the information that you want to save in your
+address book. Include the addresses for your distribution (or group
+mailing) list in the "Addresses:" field, separating each address by a
+comma. Use a Nickname and Full Name that will be meaningful for you for
+this list. <p>
+
+<a name="compose"><b>Going directly to Message Compose</b></a><br>
+Click a highlighted address to go directly to your Message Compose page.
+The address you select will be in the "To:" field ready for your new
+message. In the case of distribution lists, select any one of the
+addresses in that list and they will all be included in the "To:" field.
diff --git a/web/cgi/alpine/1.0/help/addrbrowse.html b/web/cgi/alpine/1.0/help/addrbrowse.html
new file mode 100644
index 00000000..3e9abf2e
--- /dev/null
+++ b/web/cgi/alpine/1.0/help/addrbrowse.html
@@ -0,0 +1,27 @@
+<!--
+# $Id: addrbrowse.html 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+-->
+<font size="+2"><b>Addressing Messages</b></font>
+
+<p>Select addresses for your message composition in your address book after you
+click <img src="../../images/book.gif" alt="Address book icon" style="vertical-align:middle"> on your Message Compose
+screen.</p>
+
+<p>Click the checkbox next to address or addresses to which you want to send your
+message.</p>
+
+<p>Click <input type=submit value="Address"> to return to your Message Compose screen.
+Your message will now include the addresses you have selected.
+
+<p>Click <input type=submit value="Cancel"> to return to your Message Compose screen without
+adding an address.</p>
diff --git a/web/cgi/alpine/1.0/help/addredit.html b/web/cgi/alpine/1.0/help/addredit.html
new file mode 100644
index 00000000..01defdcc
--- /dev/null
+++ b/web/cgi/alpine/1.0/help/addredit.html
@@ -0,0 +1,59 @@
+<!--
+# $Id: addredit.html 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+-->
+<font size="+2"><b>Address Book Entry</b></font>
+
+<p>You can add and edit individual and group entries to your address book by adding information to
+the following fields:</p>
+
+<P>
+<ul type="square">
+ <li><a href="#nick"><b>Nickname</b></a></li>
+ <li><a href="#fullname"><b>Full Name</b></a></li>
+ <li><a href="#addresses"><b>Addresses</b></a></li>
+ <li><a href="#fcc"><b>Fcc</b></a></li>
+ <li><a href="#comments"><b>Comments</b></a></li>
+</ul>
+
+<a name="nick"><b>Nickname</b></a><br>
+Enter a nickname which will identify this address book entry. Every
+address book entry needs to have a unique nickname associated with it.
+While composing a message, you can use the nickname in place of the address.
+By clicking <input type=submit value="Expand">, the nickname will automatically by replace by
+the full address or addresses for this entry. The nickname is not seen in the outgoing message.
+
+<p><a name="fullname"><b>Full Name</b></a><br>
+Enter the full name for this entry. For an
+individual entry, you will ordinarily use that person's name. For a group
+of addresses (or distribution list), use a descriptive word or phrase
+that describes the group.
+
+<p><a name="addresses"><b>Addresses</b></a><br>
+Enter the email address or addresses for this entry. You must complete this field for all your
+address book entries. You can create group mailing lists for sending
+email messages to several addresses at once by listing the addresses
+separated by commas, just as you would enter them when composing a
+message. For those addresses already in your address book, you can just
+add the nicknames separated by commas.
+
+<p><a name="fcc"><b>Fcc</b></a><br>
+When the "Fcc:" (Folder carbon copy) field is used, a copy of all messages
+that you send with this addressbook entry as the first address in the
+"To:" field will be saved in the folder specified in the "Fcc:" field
+rather than your default Fcc folder (usually "sent-mail").
+
+<p><a name="comments"><b>Comments</b></a><br>
+
+This optional field allows you to enter any comments about the entry. This field is not used in your
+outgoing messages.
+
diff --git a/web/cgi/alpine/1.0/help/attach.html b/web/cgi/alpine/1.0/help/attach.html
new file mode 100644
index 00000000..b632c9f4
--- /dev/null
+++ b/web/cgi/alpine/1.0/help/attach.html
@@ -0,0 +1,37 @@
+<!--
+# $Id: attach.html 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+-->
+<font size="+2"><b>Add an Attachment</b></font>
+
+<p>Add a file (such as a document, image, audio clip) to your message after
+you click <input type=submit value="Attach"> on your Message Compose screen.</p>
+
+<p>Click <input type=submit value="Browse..."> to find and select the file you want to attach.
+If you know the exact path to the file, simply type in that path.</p>
+
+<p>You have the option of entering a short description of the attachment
+in the second text box. If you choose to include a description, your
+recipients will see that text alongside the file name. Descriptions
+can be particularly helpful when you include several attachments to your
+message.</p>
+
+<p>Click <input type=submit value="Add Attachment"> to return to your Message Compose screen.
+Your attached file will be listed in the "Attachments:" field. To add
+another attachment, from Message Compose, click <input type=submit value="Attach"> again.</p>
+
+<p>If you have attached a file that you do not want to send, return to the Message
+Compose screen by clicking <input type=submit value="Cancel">. Then click the corresponding <img
+src="../../images/but_remove.gif" alt="remove icon"> to remove it.</p>
+
+<p>Click <input type=submit value="Cancel"> to return to your Message Compose screen without
+adding an attachment.</p>
diff --git a/web/cgi/alpine/1.0/help/compose.html b/web/cgi/alpine/1.0/help/compose.html
new file mode 100644
index 00000000..aad7ac40
--- /dev/null
+++ b/web/cgi/alpine/1.0/help/compose.html
@@ -0,0 +1,147 @@
+<!--
+# $Id: compose.html 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+-->
+<form action="alpine/wp.tcl" target="noop" method=get>
+<input name="page" value="noop" type=hidden notab>
+<font size="+2"><b>Message Compose</b></font>
+
+<p>The Message Compose screen is where you will write and send your email
+messages.</p>
+
+<p>
+<ul type="square">
+ <li><a href="#header"><b>Using your header fields</b></a></li>
+ <li><a href="#write"><b>Writing your messages</b></a></li>
+ <li><a href="#send"><b>Sending, postponing and canceling</b></a></li>
+ <li><a href="#note"><b>Differences from Pine and PC-Pine</b></a></li>
+</ul>
+
+<a name="header"><b>Using your headers</b></a><p>
+
+The message header area is used for addressing your message and providing
+other information that may be useful to you or the message recipient.
+
+<p>By default, your messages headers will include: To, Cc,
+Attachments, Subject. Go to <b>Configure</b> from the Message View or List
+page and click the Composer tab to change
+the defaults or customize your settings. If you have additional headers
+configured, click <img src="../../images/hdrmore.gif" alt="More Headers"
+style="background-color:#ffffcc;padding:4;vertical-align:middle; xborder: 1px solid brown"> to
+display them.
+Below is a description of commonly used headers:
+
+<ul>
+<li>
+<b>To:</b> Use this field to specify intended email recipients. This can
+be done in any of three ways:
+<ul type="square">
+ <li>Type the full email address of the intended message recipient.
+Separate multiple addresses with a comma.</li>
+
+ <li>Type a name or nickname specified in your address book and click
+<input type=submit value="Expand">. Alpine will not automatically expand
+your addresses without this step. Separate multiple names or nicknames
+with a comma.</li>
+
+ <li>Click <img src="../../images/book.gif" alt="Address book icon"
+style="vertical-align:middle"> to go to your address book to select an
+address.</li>
+</ul></p></li>
+
+<li> <b>Attachments:</b> Use this field to attach files (such as
+documents,
+images, audio clips) to your message. Click <input type=submit
+value="Attach"> to find and attach a file
+from your local system. If you have attached a file that you do not want
+to send,
+click the corresponding <img src="../../images/but_remove.gif" alt="remove
+icon" style="vertical-align:middle"> to
+remove it. <p></li>
+
+<li><b>Subject:</b> Use this field to provide a few words that summarize
+the message.<p></li>
+
+<li><b>Cc:</b> (Carbon copy) This field is similar to the "To:" field, but
+it is used to send a copy of the message to one or more people. These
+email
+addresses will be seen by all message recipients.<p></li>
+
+<li><b>Bcc:</b> (Blind carbon copy) This field is similar to the "To:" and
+"Cc:" fields, but it is
+used to send a copy of the message to one or more people whose
+addresses you do not want disclosed to other message recipients.<p></li>
+
+<li><b>Fcc:</b> (File carbon copy) Use this field to specify the folder
+to which a copy of your
+outgoing message will be saved. When this header is visible, you can
+change or remove the Fcc by editing the folder name.<p></li>
+
+<li><b>Reply-To:</b> Use this field when you would like replies to your
+messages to be directed to an address other than your usual "From:"
+address.<p></li>
+
+</ul>
+
+<a name="write"><b>Writing your messages</b></a><p>
+
+Type your message in the large text box beneath the headers. Depending on
+your browser, messages you send may contain a single long line rather than
+the nicely wrapping text shown in the text box. In those cases,
+recipients will have to scroll horizontally to read your message. To
+avoid this problem, use the Return key at the end of each line.
+
+<p>To spell check your message, click the
+<input type=submit value="Spell Check"> button. The resulting page will list each
+potentially misspelled word. Each word will have a text box for you to
+enter a correction. Additionally, some words will also have
+a list of suggested corrections. Click the <i>Apply</i> button to have
+your changes appear in the composer, or
+<i>Cancel</i> to return to the composer with the original text.
+</p>
+
+<p>Do you want to add a personalized signature line to your messages? Go
+to <b>Configure</b> from the Message View or List page and click the Composer tab.</p>
+
+
+<a name="send"><b>Sending, postponing, canceling</b></a><p>
+To send your message, postpone it to resume later, or to cancel it
+completely, click the appropriate radio button and then click <input
+type=submit value="OK">.
+<ul>
+ <li><b>Send</b> - Use this option to send your message to the
+recipients.<p></li>
+ <li><b>Postpone</b> - Use this option to store an unfinished message
+without sending it. To finish the message at a later time, click
+<b>Resume</b> in the far left column of the Message List.<p></li>
+ <li><b>Cancel</b> - Use this option to throw out the current composition.
+The message will not be sent and you will not be able to resume
+it.<p></li>
+</ul>
+
+<p><a name="note"><b>Differences from Pine and PC-Pine</b></a><p>
+
+<p>Alpine is designed primarily for easy access to email. It is not
+intended to replace any other heavy duty email programs. Accordingly, it
+is missing some of the more advanced features you will find in Pine and
+PC-Pine. As development continues, features will be added as long as they
+do not make a large impact on Alpine's speed or efficiency.
+
+<p> Some of the features you will not currently find in Alpine Compose:
+<ul>
+<li>Lcc (List carbon copy)
+<li>Where is (instead, use your browser's "find" command)
+<li>Read a file into the composition
+</ul>
+
+</ul>
+</form>
diff --git a/web/cgi/alpine/1.0/help/create_save.tcl b/web/cgi/alpine/1.0/help/create_save.tcl
new file mode 100644
index 00000000..f2a07b5c
--- /dev/null
+++ b/web/cgi/alpine/1.0/help/create_save.tcl
@@ -0,0 +1,17 @@
+# $Id: create_save.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+cgi_h2 "Create Folder Confirmation Help"
+cgi_p "Help pages for the create file to save in confirmation page will appear here."
+cgi_p {
+ Someday.
+
+}
diff --git a/web/cgi/alpine/1.0/help/expunge.html b/web/cgi/alpine/1.0/help/expunge.html
new file mode 100644
index 00000000..f1602175
--- /dev/null
+++ b/web/cgi/alpine/1.0/help/expunge.html
@@ -0,0 +1,20 @@
+<!--
+# $Id: expunge.html 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+-->
+<font size="+2"><b>Expunge Deleted Messages</b></font>
+
+<p>Click <input type=submit value="Expunge"> to permanently remove the messages you have flagged for deletion. You will
+not be able to retrieve your messages once you have expunged them. </p>
+
+<p>Click <input type=submit value="Cancel"> to return to your list of messages. The messages you previously flagged for
+deletion will remain in your folder, still flagged for deletion.</p>
diff --git a/web/cgi/alpine/1.0/help/filtconf.html b/web/cgi/alpine/1.0/help/filtconf.html
new file mode 100644
index 00000000..67654e35
--- /dev/null
+++ b/web/cgi/alpine/1.0/help/filtconf.html
@@ -0,0 +1,62 @@
+<HTML>
+<!--
+# $Id: filtconf.html 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+-->
+<BODY>
+<FONT SIZE=+2><B>Message Filtering</B></FONT>
+
+
+<p>Filtering is a way to automatically move certain messages from one
+folder to another or to delete messages, according to rules that you
+define. For each filtering rule you define, you will provide a
+<i>Pattern</i> to establish the messages you want filtered, and an
+<i>Action</i>, to control what to do with those messages.</p>
+
+<p>Click <img src="../../images/cf_add.gif" alt="Add" align=middle> to go to a screen where you can define a set of
+new filter rules.
+
+<p>Click <img src="../../images/cf_edit.gif" alt="Edit" align=middle> next to a filter that you would like to change. You
+will go to an edit screen. You will only see this option if you have
+already created filters.</p>
+
+
+<p>Click <img src="../../images/cf_delete.gif" alt="Delete" align=middle> next to a filter rule you would like to remove.
+That filter will disappear. You will only see this option if
+you have already created filter rules.</p>
+
+<p>When you have more than one filter defined, you will see the <img src="../../images/cf_shup.gif" xalign=middle> (shuffle up),
+and <img src="../../images/cf_shdown.gif" xalign=middle> (shuffle down), options next to each filter. Use these to
+move your filters up or down in the list. The order is important. Your
+messages will be checked for filtering against the top filter first and,
+if the filter is set up for that message, it will be filtered. If no
+matching information is found, the message will be checked against the
+second filter and so on down the list. Messages that do not match any
+filter rules will be delivered to your Inbox.</p>
+
+<p>Performance considerations:</p>
+<ul>
+ <li>The more filters you have defined the longer it will take to run
+ down the list. Deleting unused filters is a good
+ idea.
+ <li>Filtering in newsgroups served by an NNTP server will be slow if your
+ patterns include tests other than "From:" or
+ "Subject:".
+ <li>Filtering a folder served by an IMAP server based on protocol
+ versions prior to 4.
+</ul>
+
+
+
+</BODY>
+</HTML>
+
diff --git a/web/cgi/alpine/1.0/help/filtedit.html b/web/cgi/alpine/1.0/help/filtedit.html
new file mode 100644
index 00000000..0adb42f7
--- /dev/null
+++ b/web/cgi/alpine/1.0/help/filtedit.html
@@ -0,0 +1,240 @@
+<html>
+<head>
+<!--
+# $Id: filtedit.html 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+-->
+</head>
+<body>
+<font size="+2"><b>Filter Configuration</b></font>
+<p>This Filter Configuration screen is where you can add new filters
+or modify existing ones.</p>
+
+<p>In order to determine whether or not a message matches a filter, the
+message is compared with the Filter's Pattern. Once a filter match is
+found for a particular message, there are some actions which may be
+taken.</p>
+
+<p>NOTE: When you set up a Pattern to delete messages, it is recommended
+that you test the Pattern first with a "Move" folder in case unintended
+matches occur. Messages that are deleted will be removed from the folder
+and may be unrecoverable.</p>
+
+<ul type="square">
+<li><a href="#patterns"><b>Filter Patterns</b></a>
+</li><li><a href="#actions"><b>Filter Actions</b></a>
+</li><li><a href="#examples"><b>Examples</b></a>
+</li></ul>
+
+<a name="patterns"><b>Filter Patterns</b></a><br>
+
+<ul>
+<li><b>Nickname:</b><br>
+This is a nickname to help you. You should have a different nickname for
+each filtering rule you define. The nickname will be used in the Message
+Filtering screen to allow you to pick a rule to edit.<p></p>
+
+</li><li><b>To:</b><br>
+Any text you enter as the "To pattern" will be compared to the recipients
+from the To: line of messages when WebPine
+opens folders. When the text you entered matches all or part of the To:
+line of a message, then the Filter Action you
+have specified will be carried out. (Any other non-blank parts of the
+Pattern must match, too.)
+
+<p>You may enter a complete email address, part of an address, or a
+comma-separated list of addresses or partial
+addresses. For example:</p>
+
+<pre> To: = friend@public.com
+ To: = rated.net
+ To: = admin@public.com, admin@msn.com, fool@motleyfool.com
+</pre>
+
+<p>Each of those are valid "To patterns".</p>
+
+<p>Messages match those patterns if any of the addresses in the To: line
+of the message contains the pattern. If the pattern is a comma-separated
+list of patterns (like the last example above) then it is a match if any
+of the patterns in the list match any of the addresses in the To: line.
+(It is not possible to specify two addresses which must BOTH be present
+for a match. It is only possible to specify that EITHER address1 OR
+address2 must be present. That is exactly what the comma-separated list
+does.)</p>
+
+<p>Some messages may be "bounced" to you, and will have a "Resent-To:"
+header line. If the message contains a Resent-To: line, WebPine will look
+for matches to your "To: patterns" there, and NOT in the original To:
+line.</p>
+
+</li><li><b>From:</b><br>
+This is just like the "To pattern" except that it is compared with the
+address in the From: line of the message instead of the addresses from the
+To: line. See the help for the To pattern for more information on header
+patterns.<p></p>
+
+</li><li><b>Sender:</b><br>
+This is just like the "To pattern" except that it is compared with the
+address from the Sender: line of the message
+instead of the addresses from the To: line. See the help for the To
+pattern for more information on header patterns.<p></p>
+
+</li><li>Cc:</li><br>
+This is just like the "To pattern" except that it is compared with the
+addresses from the Cc: line of the message instead of the addresses from
+the To: line. See the help for the To pattern for more information on
+header patterns.<p></p>
+
+
+<li><b>Recipient:</b><br>
+This is just like the "To pattern" except that it is compared with the
+addresses from both the To: line and the Cc:
+line of the message instead of just the addresses from the To: line. In
+other words, it is considered a match if the
+pattern matches EITHER an address in the To: line OR an address in the Cc:
+line. (Notice that defining the Recipient
+pattern does not have the same effect as defining both the To and Cc
+patterns. Recipient is To OR Cc; not To AND Cc.
+It is equivalent to having two different rules; one with a To pattern and
+the other with the same Cc pattern.)<p></p>
+
+
+</li><li><b>Participants:</b><br>
+This is just like the "To pattern" except that it is compared with the
+addresses from the From: line, the To: line, and the Cc: line of the
+message instead of just the addresses from the To: line. In other words,
+it is considered a match if the pattern matches EITHER an address in the
+From: line, OR an address in the To: line, OR an address in the Cc: line.
+(Notice that defining the Participant pattern does not have the same
+effect as defining all of the From, To, and Cc patterns. Participant is
+From OR To OR Cc; not From AND To AND Cc. It is equivalent to having
+three different rules; one with a From pattern, another with the same To
+pattern, and a third with the same Cc pattern.)<p></p>
+
+
+</li><li><b>Newsgroups:</b><br>
+If this pattern is non-blank, then for this rule to be considered a match,
+at least one of the newsgroups from the
+Newsgroups line of the message must match this pattern. If this pattern is
+a comma-separated list of patterns, then at
+least one of the newsgroups must match at least one of the patterns. (Any
+other non-blank parts of the Pattern must
+match, too.)<p></p>
+
+</li><li><b>Subject:</b><br>
+This is similar to the other parts of the Pattern. It is compared with the
+contents from the Subject of the message.
+
+<p>If you enter non-ASCII characters in this field then the search will be
+done using the character set you have defined with the "character-set"
+configuration variable. (The truly sophisticated may use an alternate
+character set for a search by entering the MIME encoding of the header
+string here.)</p>
+
+
+</li><li><b>All Text:</b><br>
+This is similar to the header patterns. Instead of comparing with text in
+a particular header field it is compared with all of the text in the
+message header and body.
+
+<p>If you enter non-ASCII characters in this field then the search will be
+done using the character set you have defined
+with the "character-set" configuration variable. (The truly sophisticated
+may use an alternate character set for a
+search by entering the MIME encoding of the header string here.)</p>
+
+<p>It is possible that you may notice degraded performance when using
+AllText Patterns.</p>
+
+
+</li><li><b>Message is New</b><br>
+This part of the Pattern may have one of three possible values. The
+default value is "Don't care", which matches any message. The other two
+values are "Yes", which means the message must be "New" in order to be a
+match; or "No", which means the message must not be "New" in order to be a
+match. "New" is the same as Unseen and not "New" is the same as Seen.<p></p>
+
+</li><li><b>Message is Deleted</b><br>
+This part of the Pattern may have one of three possible values. The
+default value is "Don't care", which matches any message. The other two
+values are "Yes", which means the message must be marked "Deleted" in
+order to be a match; or "No", which means the message must not be marked
+"Deleted" in order to be a match.
+
+<p>If you are thinking of using this part of the Pattern as a way to
+prevent messages from being filtered more than once in a Filter Pattern,
+take a look at the Filter Option "move-only-if-not-deleted" instead. It
+should work better than using this field since it will hide the filtered
+messages even if they are already Deleted. That option is at the bottom of
+the Filter configuration screen.</p>
+
+</li><li><b>Message is Important</b><br>
+This part of the Pattern may have one of three possible values. The
+default value is "Don't care", which matches any message. The other two
+values are "Yes", which means the message must be flagged "Important" in
+order to be a match; or "No", which means the message must not be flagged
+"Important" in order to be a match.<p></p>
+
+</li><li><b>Message is Answered</b><br>
+This part of the Pattern may have one of three possible values. The
+default value is "Don't care", which matches any message. The other two
+values are "Yes", which means the message must be marked "Answered" in
+order to be a match; or "No", which means the message must not be marked
+"Answered" in order to be a match.
+</li></ul>
+
+<a name="actions"><b>Filter Actions</b></a><br>
+The Filter Action specifies the action to be taken when the Pattern is a
+match. It may be either <b>Delete</b> or <b>Move</b>
+
+<p>If you set it to "Move", then provide the name of the folder to which
+the matching message should be moved in the corresponding text box. You
+may type a list of folders separated by commas, in which case the message
+will be copied to all of the folders in the list before it is deleted.</p>
+
+<b>Move only if not deleted</b><br> If you set this option then a message
+will be moved into the specified folder only if it is not marked for
+deletion. This is useful if you have multiple Pine or WebPine sessions
+running simultaneously and you don't want messages to be filtered into a
+folder more than once. It is also useful if you want to filter only the
+"undeleted" messages in a newsgroup into a folder. This method is not
+foolproof. There may be cases where a message gets marked deleted and so
+it is never filtered into the folder. For example, if you deleted it in
+another Pine or WebPine session or another mail program that didn't use
+the filtering rule.<br>
+
+This option has no effect if the Filter Action is not set to Move.<p></p>
+
+<a name="examples"><b>Examples</b></a><br>
+To create a filter to move all incoming messages from
+"sue@travelworld.com" to your "Vacation" folder, add the following to the
+Filter Configuration screen:<br>
+<b>Nickname:</b> Travelworld<br>
+<b>From:</b> sue@travelworld.com<br>
+<b>Move filter action:</b>Vacation<br>
+
+Click <b>Save</b> to add the filter.<br>
+
+<p>To create a filter to delete all incoming messages that contain the
+text "Make Money fast!" in the subject line, add the following to your
+Filter Configuration screen:<br>
+<b>Nickname:</b> Make Money<br>
+<b>Subject:</b> Make Money Fast!<br>
+<b>Delete:</b> (Click next to this option)<br>
+Reminder: It is a very good idea to test this with the "Move to Folder"
+option in case unintended matches occur.<br>
+
+Click <b>Save</b> to add the filter.
+
+
+
+</p></body></html> \ No newline at end of file
diff --git a/web/cgi/alpine/1.0/help/folders.html b/web/cgi/alpine/1.0/help/folders.html
new file mode 100644
index 00000000..6e23973b
--- /dev/null
+++ b/web/cgi/alpine/1.0/help/folders.html
@@ -0,0 +1,39 @@
+<!--
+# $Id: folders.html 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+-->
+<font size="+2"><b>Folder List</b></font>
+
+<p> Your email messages are stored in folders. The Folder List screen allows you to
+get to those folders by clicking on the folder name. Manage your folders by doing
+the following:</p>
+
+<ul>
+<li>Delete a folder: Click the radio button corresponding to the correct
+folder and click <img src="../../images/but_folddel.gif" alt="delete icon" style="vertical-align:middle">.
+You will be prompted for confirmation before the folder is actually deleted.</li>
+<li>Rename a folder: Click the radio button corresponding to the correct
+folder and click
+<img src="../../images/but_foldren.gif" alt="folder rename icon" style="vertical-align:middle">.
+You will be prompted for confirmation before the folder is actually renamed.</li>
+<li>Add a folder: Click <input type=submit value="Create New..."> to go to the Folder Creation
+screen.</li>
+</ul>
+
+<p>If your folder list includes directories (used to contain folders) or
+collections (used to organize folders on different email hosts), you will
+see the following icons:</p>
+
+<ul>
+<li>Click <img src="../../images/b_plus.gif" alt="expand icon"> to expand the listing to see the contents</li>
+<li>Click <img src="../../images/b_minus.gif" alt="contract icon"> to collapse the listing and see the top-level only</li>
+</ul>
diff --git a/web/cgi/alpine/1.0/help/foldiradd.html b/web/cgi/alpine/1.0/help/foldiradd.html
new file mode 100644
index 00000000..2823ea72
--- /dev/null
+++ b/web/cgi/alpine/1.0/help/foldiradd.html
@@ -0,0 +1,30 @@
+<!--
+# $Id: foldiradd.html 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+-->
+<font size="+2"><b>Creating Folders</b></font>
+
+<p>Folders are used to contain messages. Directories are used to contain folders.
+<p>
+After you click <input type=submit value="Create New..."> on the Folder List page, you can create either
+one.
+
+<ul>
+ <li>To add a folder: enter the name of the folder you want to add to
+ in the "New folder name:" text box. Click <input type=submit value="Create New Folder">.</li>
+ <li>To add a directory: enter the name of the directory you want to add to
+ in the "Create New Directory:" text box. Click <input type=submit value="Create New Directory">.</li>
+</ul>
+
+<p>Click <input type=submit value="Cancel"> to return to the Folder List screen without creating a new
+directory or folder.</p>
+
diff --git a/web/cgi/alpine/1.0/help/forward.tcl b/web/cgi/alpine/1.0/help/forward.tcl
new file mode 100644
index 00000000..c016ca23
--- /dev/null
+++ b/web/cgi/alpine/1.0/help/forward.tcl
@@ -0,0 +1,16 @@
+# $Id: forward.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+cgi_h2 "Forward Help"
+cgi_p "Help pages for the Forward compose form will appear here."
+cgi_p {
+ Someday.
+}
diff --git a/web/cgi/alpine/1.0/help/index.html b/web/cgi/alpine/1.0/help/index.html
new file mode 100644
index 00000000..58bcd323
--- /dev/null
+++ b/web/cgi/alpine/1.0/help/index.html
@@ -0,0 +1,281 @@
+<!--
+# $Id: index.html 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+-->
+<form action="alpine/wp.tcl" target="noop" method=get>
+<input name="page" value="noop" type=hidden notab>
+<font size="+2"><b>Message List</b></font>
+
+<p>Your Message List contains summary information about each of the messages in
+your current Alpine folder and provides ways for you to take actions on
+those messages.</p>
+
+<p>To compose a new message, resume a postponed one, or go to another folder,
+click the appropriate option in the far left column of your page.
+You will also see options to change your Alpine configuration and to
+manage your address book.</p>
+
+<ul type="square">
+ <li><a href="#read"><b>Reading your mail</b></a></li>
+ <li><a href="#navigate"><b>Navigating your list</b></a></li>
+ <li><a href="#scroll"><b>Scrolling through messages</b></a></li>
+ <li><a href="#sort"><b>Sorting your mail</b></a></li>
+ <li><a href="#delete"><b>Deleting messages</b></a></li>
+ <li><a href="#expunge"><b>Removing deleted messages</b></a></li>
+ <li><a href="#save"><b>Saving messages to another folder</b></a></li>
+ <li><a href="#flags"><b>Setting status flags</b></a></li>
+ <li><a href="#search"><b>Searching</b></a></li>
+ <li><a href="#jump"><b>Jump Command (optional)</b></a></li>
+ <li><a href="#note"><b>Differences in Pine and PC-Pine</b></a></li>
+</ul>
+
+<a name="read"><b>Reading your mail</b></a><p>
+
+After logging into Alpine, you will see your Message List INBOX. This is the default folder for new
+mail. To read a message, click its subject.
+
+<p>Click <b>Folder List</b> if you want to read mail within a different folder.</p>
+
+
+<a name="navigate"><b>Navigating your list</b></a><p>
+In the column on the left side of your Message List page, you will
+see four navigation options: <b>Previous</b>, <b>First</b>, <b>Next</b>,
+<b>Last</b>.
+<table border=0 cellpadding=4 style="margin-left:24">
+ <tr>
+ <td><img src="../../images/but_rnd_prev3.gif" alt="Previous"></td>
+ <td>to display messages in the current folder before those currently listed</td>
+ </tr>
+ <tr>
+ <td><img src="../../images/but_rnd_first3.gif" alt="First"></td>
+ <td>to display messages in the current folder beginning with message 1</td>
+ </tr>
+ <tr>
+ <td><img src="../../images/but_rnd_next3.gif" alt="Next"></td>
+ <td>to display messages in the current folder after those currently listed</td>
+ </tr>
+ <tr>
+ <td><img src="../../images/but_rnd_last3.gif" alt="Last"></td>
+ <td>to display messages in the current folder at the end of the list</td>
+ </tr>
+</table>
+
+<p>With most browsers and the usual settings, you also have the option of
+using keyboard controls for some of your navigation. You may, however,
+need to first click in your Alpine window to make sure that your system
+knows which screen is active.
+
+<p>The keyboard controls are:
+<table border=0 cellspacing=12 cellpadding=3>
+<tr><td text align="center">n</td>
+ <td>next message</td></tr>
+<tr><td text align="center">p</td>
+ <td>previous message</td></tr>
+<tr><td text align="center">i</td>
+ <td>return to message index</td></tr>
+<tr><td text align="center">c</td>
+ <td>compose new message</td></tr>
+<tr><td text align="center">l</td>
+ <td>list folder</td></tr>
+<tr><td text align="center">d</td>
+ <td>flag message as deleted</td></tr>
+<tr><td text align="center">u</td>
+ <td>unflag message as deleted</td></tr>
+<tr><td text align="center">a</td>
+ <td>go to address book</td></tr>
+<tr><td text align="center">r</td>
+ <td>reply to current message</td></tr>
+<tr><td text align="center">f</td>
+ <td>forward current message</td></tr>
+</table>
+
+
+<a name="scroll"><b>Scrolling through messages</b></a><p>
+You have the option of scrolling, rather than paging using <i>Next</i> and
+<i>Previous</i>, through most of your messages in your Message List. Go
+to <b>Configure</b> on the left side of your Message List page, click the
+Message List tab and enter a higher number in the <i>Message Lines
+Displayed</i> field. Bear in mind that the higher
+number you choose, the longer it may take Alpine to open your folders.
+
+
+<p><a name="sort"><b>Sorting your mail</b></a><p>
+To sort your mail, the List Headers Bar must first be displayed. Display the bar by clicking
+the <img src="../../images/baropen.gif" alt="small arrow"> icon
+near the top of the screen.
+
+<p>Using the Sort options, you can arrange your mail by <b>Date</b>,
+<b>From</b>, <b>To</b>, <b>Size</b>, <b>Subject</b>, <b>Ordered
+Subject</b>, and <b>Thread</b>. Click:
+<ul>
+ <li><b>Date</b> to sort by the date your messages were sent.</li>
+ <li><b>From</b> to sort alphabetically by the email address of the sender of the message.</li>
+ <li><b>To</b> to sort alphabetically according to the "To:" line.</li>
+ <li><b>Size</b> to sort by the number of characters in the message.</li>
+ <li><b>Subject</b> to sort alphabetically according to the "Subject:" line.</li>
+ <li><b>Ordered Subject</b> to group messages with the same "Subject:" line and put them into date order.</li>
+ <li><b>Thread</b> to present messages as a continuous chain grouped by "Subject:" line. This sort is particularly helpful when viewing newsgroups.</li>
+</ul>
+
+<p>Once you have sorted your mail, the name of that sort (i.e. the column
+heading) will be highlighted and an arrow will appear next to the name. To
+reverse the sort order, click the arrow.
+By default, your Message List is set to sort your mail by arrival time.
+</p>
+
+<!--<p><i>{Do you want to change your default sort order? <a href="">Click
+here</a> for that configuration option.[not implemented yet]}</i></p>-->
+
+<a name="delete"><b>Deleting messages</b></a><p>
+<ul>
+ <li>Click the checkboxes next to the appropriate messages</li>
+ <li>Click <input type=submit value="Delete">. All messages with a mark in the checkbox will be flagged for deletion. (If you notice that some messages are mistakenly flagged for deletion, click their associated checkboxes and click <input type=submit value="Undelete">)</li>
+ <li>Click <input type=submit value="Expunge"> to permanently remove from your folder all the messages that have been flagged for deletion</li>
+</ul>
+
+<p>If you want to delete several messages at once that share a similar
+attribute, you might want to first perform a search. See the
+<a href="#search">Searching</a> text for more information.</p>
+
+<p>Note: if marking a message for deletion fails to work it may be because
+you recently used your browser's "Back" button. To avoid this, be sure to
+Alpine links and navigation options.
+
+
+<!-- <p><i>{Do you want to Expunge without being prompted for a
+confirmation? <a href="">Click here</a> to change that configuration
+option. [not implemented yet]}</i>
+<p>{<i><a href="">Click here</a> to enable aggregate commands to have
+this option available on this page. [not implemented yet]</i>}</p> -->
+
+<p>
+<a name="expunge"><b>Removing deleted messages</b></a><p>
+Flagging messages for deletion is only half the process of actually removing the messages from the folder altogether.
+Click <input type=submit value="Expunge"> to permanently remove messages flagged for deletion. Because removed messages cannot
+be restored, you will be prompted to confirm your desire. At the prompting click:</p>
+
+<p><input type=submit value="Expunge"> to permanently remove the messages you have flagged for deletion. You will
+not be able to retrieve your messages once you have expunged them. </p>
+
+<p><input type=submit value="Cancel"> to return to your list of messages. The messages you previously flagged for
+deletion will remain in your folder, still flagged for deletion.</p>
+
+
+<p> <a name="save"><b>Saving messages to another folder</b></a>
+<p>
+
+To save messages from your Message List, the Message Command Bar must
+first be displayed. Display the bar by clicking the <img
+src="../../images/baropen.gif" alt="small arrow"> icon near the top of the
+screen. Then, click the checkboxes next to the messages you want to
+save to another folder (or click just one checkbox if you are only
+saving that one message).
+
+<p>
+Save the chosen messages by choosing from the options in the dropdown menu next to the
+<input type=submit value="Save"> button. Among the Save options, you'll find:
+
+<ul>
+ <li> The name of the default folder to save to, typically <font face="courier">saved-messages</font>.
+ <li> Several, typically six, names of folders you have most recently saved messages to
+ <li> An option to type in directly the name of the folder you wish to save to (either a new folder you wish to create, or one
+already containing messages)
+ <li> An option to choose the folder from the list of all your folders
+</ul>
+
+If Alpine does not act on your choice immediately, click the <input type=submit value="Save">
+button to initiate saving. Once saved, the chosen messages will be marked for deletion.
+
+<p>
+
+<!---Config option for Collection lists?-->
+
+<a name="flags"><b>Setting status flags</b></a><p>
+By default, the area immediatly to the left of the message numbers is reserved for the following status flags:
+<table border=0 cellpadding=4 style="margin-left:24">
+ <tr>
+ <td><font face=Courier><b>+</b></font></td>
+ <td>message has your address in the To: line [Note: this is a fixed value]</td>
+ </tr>
+ <tr>
+ <td><font face=Courier><b>*</b></font></td>
+ <td>(Important) - You have previously set the status of this message to important</td>
+ <tr>
+ </tr>
+ <td><font face=Courier><b>A</b></font></td>
+ <td>(answered) - You have replied to this message</td>
+ </tr>
+ <tr>
+ <td><font face=Courier><b>N</b></font></td>
+ <td>(new) - You have not yet looked at the text of this message</td>
+ </tr>
+ <tr>
+ <td><font face=Courier><b>D</b></font></td>
+ <td>(deleted) - You have marked this message for deletion, but not yet expunged it</td>
+ </tr>
+</table>
+
+To add or remove the status flags, the Message Command Bar must first be
+displayed. Display the bar by clicking the <img src="../../images/baropen.gif"
+alt="small arrow"> icon near the top of the screen. Then: <ul>
+ <li>Click the checkboxes next to the appropriate messages</li>
+ <li>Choose the appropriate value from the dropdown menu</li>
+ <li>Click <input type=submit value="Set"></li>
+</ul>
+
+
+<!--Config option for enabling status w/click-->
+
+<a name="search"><b>Searching</b></a><p>
+
+To search, the Message Command Bar must first be displayed. Display the bar
+by clicking the <img src="../../images/baropen.gif" alt="small arrow"> icon
+near the top of the screen.
+
+<p>Click <input type=submit value="Search"> to get to a page where you can find messages by
+text, date, or status.</p>
+
+After you specify your search criteria, you will be returned to your
+Message List. If matches were found, your Message List will display your
+found messages and those messages will be marked. (Be aware: by default
+Search will include messages marked before the search along with your
+search results.) Click <input type=submit value="Show All Messages"> to display your entire Message
+List.
+
+<p><a name="jump"><b>Jump Command (optional)</b></a><p>
+
+Depending on your configuration, you may also see a text box next to a
+<input type=submit value="Jump"> button on the left side of the page. Enter a
+message number into the text box and click the button to redisplay
+the Message List with the chosen message at the top of the page.
+
+<p>
+You may find message numbers on the Message List lines make this command more useful.
+If not displayed in the Message List, you can add message numbers to each line by
+going to <b>Configure</b> on the left of the Message List page, clicking the
+Message List tab and adjusting the Message Line Format accordingly.
+
+<p><a name="note"><b>Differences from Alpine and PC-Alpine</b></a><p>
+
+<p>Alpine is designed primarily for easy access to email. It is not
+intended to replace any other heavy duty email programs. Accordingly, it
+is missing some of the more advanced features you will find in Pine and
+PC-Pine. As development continues, features will be added as long as they
+do not make a large impact on Alpine's speed or efficiency.
+
+<p> Some of the features you will not currently find in Alpine Message
+List: <ul>
+<li>Go directly to a folder you type in (may be added soon)
+<li>Export message
+
+</ul>
+<!--Go to Configure to change the view display after search default-->
diff --git a/web/cgi/alpine/1.0/help/index.tcl.1 b/web/cgi/alpine/1.0/help/index.tcl.1
new file mode 100644
index 00000000..67cc941d
--- /dev/null
+++ b/web/cgi/alpine/1.0/help/index.tcl.1
@@ -0,0 +1,301 @@
+# $Id$
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+cgi_put [cgi_font size=+2 [cgi_bold "Message List"]]
+
+cgi_p
+cgi_puts [cgi_font size=-1 color=red "Note: This text may be out-of-date; changes to the Message List page are in process."]
+
+cgi_p {
+ Your Message List contains summary information about each of the
+ messages in your current WebPine-lite folder and provides ways for you to
+ take actions on those messages.
+}
+
+cgi_p {
+ To compose a new message, resume a postponed one, or go to another
+ folder, select the appropriate option in the column on the left-hand side
+ of your page. You will also see options to change your WebPine-lite
+ configuration and to manage your address book.
+}
+
+cgi_bullet_list type=square {
+ cgi_li [cgi_url [cgi_bold "Reading your mail"] "#read"]
+ cgi_li [cgi_url [cgi_bold "Navigating your list"] "#navigate"]
+ cgi_li [cgi_url [cgi_bold "Sorting your mail"] "#sort"]
+ cgi_li [cgi_url [cgi_bold "Operating on a set of messages"] "#aggregate"]
+ cgi_li [cgi_url [cgi_bold "Deleting messages"] "#delete"]
+ cgi_li [cgi_url [cgi_bold "Saving messages to another folder"] "#save"]
+ cgi_li [cgi_url [cgi_bold "Setting status flags"] "#flags"]
+ cgi_li [cgi_url [cgi_bold "Searching"] "#search"]
+}
+
+cgi_anchor_name "read"
+cgi_puts [cgi_bold "Reading your mail"][cgi_nl]"
+cgi_puts {
+ Once you log into WebPine-lite, you will see your Message List INBOX. To
+ read a message within any folder, select its subject line.
+}
+
+cgi_p {
+ Go to [cgi_bold "Folder List"] if you want to read mail within another
+ folder.
+}
+
+cgi_p {
+ Your INBOX is where your new mail is delivered unless you filter some
+ mail to another folder.
+}
+
+if {0} {
+<!-- <p>{<i><a href="">Click here</a> to configure mail filters.[not
+implemented yet]}</i></p> -->
+}
+
+cgi_anchor_name "navigate"
+cgi_puts [cgi_bold "Navigating your list"][cgi_nl]
+cgi_puts {
+ In the column on the left-hand side of your Message List page, you will
+ see four navigation options: [cgi_bold Previous], [cgi_bold First], [cgi_bold Next],
+ [cgi_bold Last].
+}
+
+cgi_puts "Select:[cgi_nl]"
+cgi_bullet_list {
+ cgi_li {
+ [cgi_bold Previous] to download your Message List page that contains
+ your previous set of messages. For example, if you see messages 101-150
+ out of 280, select [cgi_bold Previous] to go to messages 50-100.[cgi_nl]
+ }
+ cgi_li {
+ [cgi_bold First] to view your Message List beginning with message number 1.
+ }
+ cgi_li {
+ [cgi_bold Next] to download your Message List page that contains
+ your next set of messages. For example, if you see messages 101-150 out
+ of 280, select [cgi_bold Next] to go to messages 151-200.
+ }
+ cgi_li {
+ [cgi_bold Last to view your Message List page that ends with your last
+ message in that folder.
+ }
+}
+
+cgi_p {
+ Use your browser scrollbar to see messages that are downloaded, but are
+ out of eyesight due to screen size.
+}
+
+if {0} {
+<!-- <p>{<i>Do you want to change the number of messages WebPine-lite
+downloads to your Message List? <a href="">Click here</a> to make that
+configuration change.[not implemented yet]}</i></p> -->
+}
+
+cgi_p {
+ [cgi_anchor_name "sort"][cgi_bold "Sorting your mail"][cgi_nl]
+ Using the Sort options you can arrange your mail by [cgi_bold "Date"],
+ [cgi_bold "From"], [cgi_bold "To"], [cgi_bold "Size"], [cgi_bold "Subject"], [cgi_bold "Ordered Subject"],
+ and [cgi_bold "Thread"]. By default, your Message List is set to
+ sort your mail by arrival time. Most of the time, arrival time and date
+ (effectively departure time) are nearly the same. They may differ,
+ however, because of messages may take a long time in delivery or because
+ of time zone differences.
+}
+
+
+cgi_puts "Select:[cgi_nl]"
+cgi_button_list {
+ cgi_li "[cgi_bold "Date"] to sort by the date your messages were sent.<br>
+ cgi_li "[cgi_bold "From"] to sort by the name of the sender of the
+message.<br>
+ cgi_li "[cgi_bold "To"] to sort your messages alphabetically according to the "To:"
+line.<br>
+ cgi_li "[cgi_bold "Size"] to sort by the number of characters in the message.<br>
+ cgi_li "[cgi_bold "Subject"] to sort your messages alphabetically according to the
+subject line.<br>
+ cgi_li "[cgi_bold "Ordered Subject"] to group messages with the same subject name and
+put them into date order.<br>
+ cgi_li "[cgi_bold "Thread"] to present messages as a continuous chain grouped by
+Subject. This sort is particularly helpful when viewing newsgroups.</ul>
+
+cgi_p
+Once you have sorted your mail, the name of that sort (i.e. the column
+heading) will be highlighted and an arrow will appear next to the name. To
+reverse the sort order, click the arrow.</p>
+
+<!-- <p><i>{Do you want to change your default sort order? <a href="">Click
+here</a> for that configuration option.[not implemented yet]}</i></p> -->
+
+cgi_p
+cgi_anchor_name "aggregate">[cgi_bold "Operating on a set of messages"]</a><br>
+
+cgi_p
+Operating on a set of messages is referred to as [cgi_italic "aggregate operations"].
+The way you perform aggregate operations is by placing a mark in the checkbox on
+the line associated with the desired message,
+and then choosing the operation to perform such as deleting or setting
+other message flags, or saving them to a folder.
+
+cgi_p
+The set of aggregate operations are either hidden or exposed. When
+hidden you will see a <img src=../../images/slideout.gif style="vertical-align:middle"> tab on the left edge of the line
+containing the sort names. Click the tab to expose the aggregate
+commands and the checkboxes next to each message. Similarly, when aggregate operations are visible, you will see a
+<img src=../../images/slidein.gif style="vertical-align:middle"> tab on the line containg the sort names. You can click this
+tab to hide aggregate operations.
+
+cgi_p
+When aggregate commands are exposed, the set of commands available is either
+simple or complete. The simple set consists of only the ability to [cgi_bold "Delete"]
+messages. When the simple mode is in effect, you will see a [cgi_bold "Delete"] button
+and next to it the <img src=../../images/tabmore.gif style="vertical-align:middle"> tab
+above the column of checkboxes. Click the <img src=../../images/tabmore.gif style="vertical-align:middle"> tab
+to expose the complete set of commands.
+
+cgi_p
+When the complete set of commands is visible, a <img src=../../images/tabless.gif style="vertical-align:middle">
+tab will be on the sort name row. Click it to return to the simple aggregate command set.
+
+cgi_p
+The complete set of aggregate operations adds an additional display line above the sort names row.
+It contains the [cgi_bold "Flag"] and [cgi_bold "Save"] commands. The choices above the checkbox column will change
+as well into a dropdown list of alternative methods for setting or unsetting checkboxes.
+
+cgi_p
+There are three ways to mark a message when the complete mode is in effect:</p>
+<ul>
+ cgi_li "Select the checkbox next to the message or messages
+ cgi_li "Perform a Search using the [cgi_bold "Search/Mark..."] menu
+ cgi_li "Select [cgi_italic "Mark All"] from the [cgi_bold "Search/Mark..."] menu.
+</ul>
+
+cgi_p
+Lastly, the dropdown will offer a [cgi_bold "Zoom"] option. Choosing it will change the Message List by
+hiding all of the unchecked messages from view. This is useful, for example, to examine a Search result
+that may have marked only a few messages in a large folder.
+
+<!-- <p><i>{<a href="">Click here</a> to enable or disable aggregate
+commands from this page. [not implemented yet]}</i></p> -->
+
+cgi_p
+cgi_anchor_name "delete">[cgi_bold "Deleting Messages"]</a><br>
+When you see the [cgi_bold "Delete"] option in the upper-right corner of
+your Message List page, to delete messages:
+<blockquote>
+<ul>
+ cgi_li "Select the checkboxes next to the appropriate messages
+ cgi_li "Select [cgi_bold "Delete"]
+ cgi_li "Select [cgi_bold "Expunge"] to remove the messages from your folder
+</ul>
+</blockquote>
+
+When you are showing all of your aggregate operations (i.e. you
+will see the [cgi_bold "Search/Mark..."] menu and the "Set Message Status" and
+"Save Messages" operations), to delete:
+cgi_division style=
+<ul>
+ cgi_li "Select the checkboxes next to the appropriate messages by either
+selecting them individually or use the [cgi_bold "Search/Mark..."]
+menu options
+ cgi_li "Select [cgi_bold "Flag"] messages as Deleted
+ cgi_li "Select [cgi_bold "Expunge"] to remove the messages from your folder.
+</ul>
+</blockquote>
+
+cgi_p
+If you do not see checkboxes to the left of your messages, all of your
+aggregate operations are hidden. Click the tab to the left of your column
+sort names to show [cgi_bold "Delete"], then click the tab next to [cgi_bold "Delete"]
+if you want to see all the aggregate operations.</p>
+
+<!-- <p><i>{Do you want to Expunge without being prompted for a
+confirmation? <a href="">Click here</a> to change that configuration
+option. [not implemented yet]}</i>
+cgi_p
+{<i><a href="">Click here</a> to enable aggregate commands to have
+this option available on this page. [not implemented yet]</i>}</p> -->
+
+
+cgi_anchor_name "save">[cgi_bold "Saving messages to another folder"]</a><br>
+"Save Messages" is only available from your Message List page when all of
+your aggregate operations are showing. If you do not see "Save Messages"
+on the top right-hand side of your page, some of your aggregate operations
+are hidden. Click the tab to the left of your sort column headings to show
+all of them.
+
+Select the checkboxes next to the messages you want to save to
+another folder. Alongside the [cgi_bold "Save"] button, type in the name of the
+folder to which you want the messages saved. The folder name can be one
+that already exists, or you can create a new folder by typing in a new
+name. If you have folder collections, select the correct one using the
+dropdown menu. Select [cgi_bold "Save"] to save the message(s) into the folder
+you have typed.
+
+cgi_p
+The messages in your current folder that you have saved to another
+will be flagged for deletion.</p>
+
+
+cgi_anchor_name "flags">[cgi_bold "Setting status flags"]</a><br>
+Immediately to the left of your message numbers is an underscore
+reserved for your status flags:
+cgi_button_list {
+ cgi_li "[cgi_bold "+"] message sent directly to your address (your address was in the To: line)"
+ cgi_li "[cgi_bold "*"] important message"
+ cgi_li "[cgi_bold "A"] answered message"
+ cgi_li "[cgi_bold "N"] new (unread) message"
+ cgi_li "[cgi_bold "D"] flagged for deletion (additionally the subject line will have a strike through it)"
+</ul>
+
+cgi_p {
+ Select the underscore to go to a page where you can set or unset any of
+ your message status flags.
+}
+
+cgi_anchor_name "search"
+cgi_puts {
+ [cgi_bold "Searching"][cgi_nl]
+ You can search through your messages in your Message List using the
+ [cgi_bold "Search/Mark..."] dropdown menu. If you do not see the menu
+ (to the left of your sort column headings), some of your aggregate
+ commands are hidden. Click the tab in that area to show more commands.
+}
+
+cgi_p {
+ Select one of the menu items under [cgi_bold "Search/Mark..."] to
+ find messages:
+}
+cgi_bullet_list {
+ cgi_li [cgi_italic "...by text"]
+ cgi_li [cgi_italic "...by date"]
+ cgi_li [cgi_italic "...by status"]
+}
+
+cgi_puts {
+ Specify your search criteria on the next page. After you make your
+ selection, you will return to your Message List and the checkbox will be
+ selected for those messages found in your search.
+}
+
+cgi_p {
+ If you want to display just those messages that were found in your
+ search, next select [cgi_bold "Zoom"] from the [cgi_bold "Search/Mark..."] menu list.
+ (Be aware that if you had other messages checked for selection before your
+ search, they will also be included in your zoomed view).
+}
+
+if {0} {
+<!-- <p><i>Note: If your browser is not javascript-enabled, after you make
+your selection from the dropdown menu you will need to select the
+<b>Do</b> button.</i></p> -->
+}
+
diff --git a/web/cgi/alpine/1.0/help/release.html b/web/cgi/alpine/1.0/help/release.html
new file mode 100644
index 00000000..3396a3b3
--- /dev/null
+++ b/web/cgi/alpine/1.0/help/release.html
@@ -0,0 +1,83 @@
+<HTML>
+<HEAD>
+
+<!--
+# $Id: release.html 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+-->
+
+<TITLE>Alpine Release Information</TITLE>
+
+<STYLE TYPE='TEXT/CSS'>
+BODY { background-color: #FEFAC9 ; font-family: Arial, sans-serif }
+DL { padding: 0 18 }
+DT { font-weight: bold }
+DD { padding-bottom: 12 }
+</STYLE>
+
+</HEAD>
+
+<BODY>
+
+<DIV STYLE="font-weight: bold; font-size: 16pt ; padding: 12" ALIGN="CENTER">
+<IMG SRC="../../images/logo/alpine/small.gif" STYLE="float: left; padding-left: 20">
+Alpine Release Highlights
+</DIV>
+
+<DIV STYLE="background-color: white; padding: 24; margin: 20; border: 1px solid black">
+This page is used to publish the highlights of periodic updates. Bug fixes, explanations
+for new features and so forth can typically be found here.
+<P>
+<H2>December 2007:</H2>
+
+<DL>
+<DT>Web Alpine General Release</DT>
+<DD>
+<P>
+This is the first release to the general public of the Web Alpine IMAP
+client. It's built on the Alpine Mail System which itself is derived
+from the Pine Mail System. Externally, besides numerous new features,
+the most significant change is likely the new licensing. The Alpine Mail System
+has been release under the Apache 2.0 license.
+
+<P>
+Internally, there are significant changes. The code has been
+restructured to partition mail data access routines from those
+functions that provide the user interface. This allows development on
+the mail engine proper to be easily reflected across the supported
+interfaces. Similarly, each interface, Unix, Windows, and Web, can be
+developed reasponably independently.
+<P>
+Similarly, significant work has been done to make Alpine more
+international. Internally, message text and so forth is represented in
+Unicode, and the groundwork has been laid to support status and
+command translations.
+
+<P>
+As for the Web Alpine IMAP client, it is an updated, evolutionary
+improvement upon the Pine-based version that has been serving tens of
+thousands of users daily on the University of Washington campus for
+several years. It is not quite as fully featured as the other Alpine
+mail tools, but is a reasonably solid and complete IMAP client. We
+also tend to agree with many of its critics that it is a somewhat homely
+interface, and we are taking significant steps toward addressing that
+criticism. Stay tuned!
+
+</DD>
+</DL>
+
+<P>
+
+</DIV>
+
+</BODY>
+</HTML>
diff --git a/web/cgi/alpine/1.0/help/reply.tcl b/web/cgi/alpine/1.0/help/reply.tcl
new file mode 100644
index 00000000..32fb66ae
--- /dev/null
+++ b/web/cgi/alpine/1.0/help/reply.tcl
@@ -0,0 +1,17 @@
+# $Id: reply.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+cgi_p
+cgi_h2 "Reply Help"
+cgi_p "Help pages for the Reply compose form will appear here."
+cgi_p {
+ Someday.
+}
diff --git a/web/cgi/alpine/1.0/help/resume.html b/web/cgi/alpine/1.0/help/resume.html
new file mode 100644
index 00000000..7a59e908
--- /dev/null
+++ b/web/cgi/alpine/1.0/help/resume.html
@@ -0,0 +1,28 @@
+<!--
+# $Id: resume.html 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+-->
+<font size="+2"><b>Message Resume</b></font>
+
+<p>At the Message Resume screen, you can return to composing an unfinished
+message that you have previously postponed. </p>
+
+<p>Click the radio button next to the message that you want to continue composing. Click
+<input type=submit value="Resume Chosen Message"> to go to a Message Compose screen that
+contains your postponed message.</p>
+
+<p>If you decide not to resume a message, Click <input type=submit value="Cancel">. Note, cancelling
+on this screen will not remove any of your messages. To
+remove a postponed message you must first click <input type=submit value="Resume Chosen Message">
+and then, on the Message Compose screen, click
+<input type=submit value="Cancel">.
+
diff --git a/web/cgi/alpine/1.0/help/secure.html b/web/cgi/alpine/1.0/help/secure.html
new file mode 100644
index 00000000..f44df381
--- /dev/null
+++ b/web/cgi/alpine/1.0/help/secure.html
@@ -0,0 +1,119 @@
+<HTML>
+<HEAD>
+<!--
+# $Id: secure.html 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+-->
+<TITLE>Alpine with Session Encryption</TITLE>
+<STYLE TYPE='TEXT/CSS'>
+BODY { background-color: #F3F3F3 ; font-family: Arial, sans-serif }
+</STYLE>
+</HEAD>
+
+<BODY>
+
+<DIV STYLE="font-weight: bold; font-size: 16pt ; padding: 12" ALIGN="CENTER">
+<IMG SRC="../../images/logo/alpine-w-logo-small.gif" STYLE="float: left; padding-left: 20">
+Alpine with Session Encryption
+</DIV>
+
+<DIV STYLE="background-color: white; padding: 24; margin: 20">
+
+Web browser access to email is inherently slower than a desktop email
+application.&nbsp; And while Alpine is designed to be as efficient as
+possible, there remain certain performance obstacles that cannot be
+easily overcome.&nbsp;
+
+<p>
+
+One obstacle is session encryption (also known as
+<a href="http://www.washington.edu/computing/web/publishing/ssl.html">Secure
+Socket Layer</a> or SSL).&nbsp; When session encryption is enabled, all
+communication between the browser and the Alpine server is scrambled
+such that an eavesdropper would find it extremely difficult to observe
+the contents.&nbsp; When disabled, all communication, except for your
+username and password, between the browser and Alpine server takes
+place in clear text and is easily observable.&nbsp;
+
+<p>
+
+While encryption and decryption require some extra computing
+resources, the heaviest performance cost has to do with default
+browser behavior.&nbsp; Usually browsers retain a copy of pages and
+their various elements, such as images, for a time with the intent of
+avoiding costly network communication next time that page or a page
+containing the same elements is requested.&nbsp; However, to be extra
+careful of potentially exposing sensitive information, many browsers
+default behavior is not to retain copies of elements retrieved over
+encrypted connections.&nbsp; Thus, Alpine's performance cost has to
+do with the browser re-requesting common elements on the various
+Alpine pages.&nbsp;
+
+<p>
+
+Typically, from a computer connected directly to the campus network
+(or other high-speed network such as DSL or cable-modem) this
+performance cost is not noticeable.&nbsp; However, a user on a slower,
+modem-connected computer can be severely effected.&nbsp;
+
+<p>
+
+There are two ways to work around this situation.&nbsp; One is to alter the
+browser's configuration to retain elements on pages served securely.&nbsp;
+The downside is that this setting often will then apply to all secure
+pages, not just Alpine.&nbsp; The other work around is to disable session
+encryption for Alpine.&nbsp; The downside is that communication between
+the Alpine server and your computer, except for passwords, is
+visible.&nbsp;
+
+<p>
+
+The likelihood that the communication will be observed depends on the
+path the communication takes over the network.&nbsp; For example, while
+using a computer in a campus lab or laptop connected to a wireless
+hub, it is not unthinkable that someone on the local network may be
+watching traffic.&nbsp; In such situations the communication speed is high
+enough to offset the performance cost, so session encryption is a pretty
+good idea.&nbsp; Similarly, communication between the Alpine server and a
+computer connected to a campus modem, is not likely to travel
+over a publicly accessible network, so malicious eavesdropping is much
+less likely.&nbsp; Given the slower communication speed and reduced risk of
+observation, disabling session encryption while connected via modem
+is reasonable.&nbsp;
+
+<p>
+
+Alpine knows about the campus modem pools, and will adjust the
+&quot;Session Encryption&quot; default setting based on whether or not
+the browser you are using appears to be running on a computer
+connected to a campus modem pool.&nbsp; Modem pool connections will
+default to <b>not</b> using session encryption.&nbsp; All other connections
+will default to using session encryption.&nbsp; That is, a computer on the
+campus network or connected to a network or dial-in service outside
+the UW will have its Alpine session encrypted unless you uncheck the
+box labelled "SSL Session Encryption."&nbsp; If you are on a slow
+connection and confident that your connection is not likely to travel
+any hostile path, or otherwise decide the performance benefit
+outweighs the risk of observation, then unchecking the box may be a
+reasonable option for you.&nbsp;
+
+<p>
+
+Please note: Session encryption <i>only</i> ensures that communication
+between your browser and the Alpine server is secure. It does not
+mean email messages you send from Alpine are in any way safe from
+observation or otherwise confidential.
+
+
+</DIV>
+</BODY>
+</HTML>
diff --git a/web/cgi/alpine/1.0/help/takeaddr.html b/web/cgi/alpine/1.0/help/takeaddr.html
new file mode 100644
index 00000000..053b9063
--- /dev/null
+++ b/web/cgi/alpine/1.0/help/takeaddr.html
@@ -0,0 +1,34 @@
+<!--
+# $Id: takeaddr.html 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+-->
+<FONT SIZE=+2><B>Take</B></FONT>
+
+<P>
+
+Use <b>Take</b> to capture addresses from your current email message and
+put them into your address book. All the email addresses contained within
+your message will be listed as choices for entry into your address book.
+
+<p>
+
+<p> Click the check-box next to the address or addresses you would like to
+take to your address book. Click one check-box if you want to add just
+that single address. Click more than one check-box to create a group
+mailing list, which will link those addresses to one nickname in
+your address book. After you make your selection, click <b>Take
+Address</b> to then complete your address book entry, or click
+<b>Cancel</b> to return to your email message without altering anything.
+
+<p> If you have more than one address book, you will see a pull-down menu.
+Use that menu to select among your defined address books.
+</p>
diff --git a/web/cgi/alpine/1.0/help/takeedit.html b/web/cgi/alpine/1.0/help/takeedit.html
new file mode 100644
index 00000000..18d18392
--- /dev/null
+++ b/web/cgi/alpine/1.0/help/takeedit.html
@@ -0,0 +1,63 @@
+<!--
+# $Id: takeedit.html 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+-->
+<FONT SIZE=+2><B>Address Book Entry - Take</B></FONT>
+<P>
+
+To complete your entry, supply the following information for
+your address book:
+
+<P>
+<UL TYPE=square>
+<LI><a href="#nick"><b>Nickname</b></a>
+<LI><a href="#fullname"><b>Full Name</b></a>
+<LI><a href="#addresses"><b>Addresses</b></a>
+<LI><a href="#fcc"><b>Fcc</b></a>
+<LI><a href="#comments"><b>Comments</b></a>
+</UL>
+
+<a name="nick"><b>Nickname</b></a><br>
+The nickname you choose will identify this address book entry. Every
+address book entry is required to have a nickname associated with it.
+Use the nickname in place of an address on the Message Compose page and
+<b>Expand</b> will fill in the address or addresses for this entry. The
+nickname is not seen in the outgoing message.
+
+<p>
+<a name="fullname"><b>Full Name</b></a><br>
+This optional field is for the full name for this entry. For an individual
+entry, you will ordinarily use that person's name. For a group of
+addresses (or distribution list), use a descriptive word or phrase that
+describes the group. Edit existing names by typing in the field.
+
+<p>
+<a name="addresses"><b>Addresses</b></a><br>
+This required field will be filled in with the addresses you selected from
+your email message. Group mailing lists will have addresses separated by
+commas.
+
+<p>
+<a name="fcc"><b>Fcc</b></a><br>
+The "Folder carbon copy" field gives you the ability to save a copy of the
+messages you send to the folder name you put here. The Fcc name will be
+used instead of your default Fcc folder (usually "sent-mail") when you
+use this address book entry as the first address in To: line of messages
+you compose.
+
+<p>
+<a name="comments"><b>Comments</b></a><br>
+This is a comment you can add for your convenience. This field is not
+used in your outgoing messages.
+
+<p>When finished, click <b>Save</b> to update your address book, or
+<b>Cancel</b> to return to the message view without making any changes.
diff --git a/web/cgi/alpine/1.0/help/tech-notes.html b/web/cgi/alpine/1.0/help/tech-notes.html
new file mode 100644
index 00000000..7f275620
--- /dev/null
+++ b/web/cgi/alpine/1.0/help/tech-notes.html
@@ -0,0 +1,471 @@
+<html>
+<head>
+<!--
+# $Id: tech-notes.html 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+-->
+</head>
+<body>
+<font size=+3><b>Web Alpine Technical Notes</b></font>
+
+<!-- history: originally prepared for AIT, 22 October 2002 -->
+<!-- history: updated for initial campus release, 19 Apr 2004 -->
+
+<h2>Why Web Alpine</h2>
+
+Web Alpine was originally conceived as a means of providing reliable,
+ubiquitous, and consistent access to UW email facilites via the Interactive
+Message Access Protocol (IMAP). As with Pine, it is intended to present a
+simple, approachable interface that can be easily navigated with
+minimal familiarity. It is also intended to be as efficient as
+possible, in both page-service and user navigation while meshing
+as neatly as possible with the central campus IMAP infrastructure.
+
+<p>
+
+<h2>How Web Alpine</h2>
+
+<p>
+
+Web Alpine's foundation rests firmly on Unix Alpine's many years of
+development and deployment. It's core element is a per-session server
+(or serverette) that provides data to the scripts generating the web
+pages the users sees and manages the connection to the IMAP mail
+server where the user's mail resides. This serverette is literally
+built from the same Alpine sources used to build the Unix Alpine and
+PC-Alpine mail user agents. Thus, it inherits most of the efficiency,
+such as data caching and request ordering and grouping, that has been
+built into Alpine, as well as many of the features and functions.
+
+<p>
+
+<h3>Components</h3>
+<p>
+
+<table width=80% align=center border=0 cellpadding=8>
+<tr>
+ <td valign=top align=left><b>Browser</b></td>
+ <td>Performs usual browser role: sends requests, renders responses and displays or hands off for display non-HTML data</td>
+</tr>
+<tr>
+ <td valign=top align=left nowrap><b>Web Server</b></td>
+ <td>Performs extraordinary web server role: produces HTML responses to requests, maintains relationship between user's browser and IMAP server for the life of the user's session.</td>
+</tr>
+<tr>
+ <td valign=top align=left nowrap><b>IMAP Server</b></td>
+ <td>Performs usual mail server role: provides access to the various bits and pieces of mail data via Interactive Message Access Protocol</td>
+</tr>
+</table>
+<p>
+<div align=center>
+<img src=wpsys.jpeg>
+</div>
+<p>
+
+<h3>Protocols, Services, Technologies</h3>
+<p>
+
+<table width=80% align=center>
+<tr><td>
+ <dl>
+ <dt><b>HTTP(s)</b></dt>
+ <dd>Protocol between browser and web server. Common language allowing users to get at their mail from the widest variety of platforms and locations.</dd>
+ <dt><b>IMAP(s)</b></dt>
+ <dd>Broadly implemented distributed mail protocol which permits users to access mail from a variety of
+ clients: web alpine, pine, pc-pine, outlook express, eudora.</dd>
+ <dt><b>Pubcookie</b></dt>
+ <dd>Secure, distributed web-based authentication service optionally used as the mechanism to authenticate users in the Web Alpine login process.</dd>
+ <dt><b>GSSAPI</b></dt>
+ <dd>SASL authentication mechanism used to establish session between Web Alpine serverette and IMAP server on behalf of Pubcookie authenticated user.</dd>
+ <dt><b>Tcl</b></dt>
+ <dd>Tool Command Language. Chosen CGI scripting language for the Web Alpine page templates. Reasonable
+ language, reasonably implemented, reasonably supported, particularly well suited for exporting functionality
+ in pre-existing C-based tools.</dd>
+ <dt><b>Unix Domain Sockets</b></dt>
+ <dd>Communication channel used to pass requests/responses between Tcl interpreters processing scripts and web alpine serverette.</dd>
+</dl>
+</td></tr>
+</table>
+<p>
+
+<h3>Web Alpine Distribution Layout</h3>
+<p>
+The Web Alpine application consists of three main components; the CGI scripts that generate pages containing the user's email data,
+a serverette running on the http server spanning the life of the user's session, and a couple of libraries
+to aid page generation and serverette communciation.
+
+<p>
+Web Alpine is packaged as part of the Alpine Distribution and it's source resides
+within the <tt>web/</tt> subdirectory. Subdirectories are organized by the service
+the provide, and breakdown as follows:
+<p>
+<table border=0 xbgcolor="#dddddd" align=center width="90%" cellpadding=2>
+<tr>
+ <td valign=top><tt>bin/</tt></td>
+ <td></td>
+ <td></td>
+ <td valign=top>Contains scripts and generated binaries that initiate and maintain Web Alpine sessions.
+ </td>
+</tr>
+<tr>
+ <td valign=top><tt>cgi/</tt></td>
+ <td></td>
+ <td></td>
+ <td valign=top>Contains scripts referenced by the browser to generate the Web Alpine interface.
+ Subdirectories within organize scripts by function, and allow for suitable scoping
+ of session key.
+ </td>
+</tr>
+<tr>
+ <td></td>
+ <td valign=top><tt>alpine/</tt></td>
+ <td></td>
+ <td valign=top>Browser's view of Web Alpine application. Contains scripts to generate pages
+ the user interacts with.
+ </td>
+</tr>
+<tr>
+ <td></td>
+ <td valign=top><tt>session/</tt></td>
+ <td></td>
+ <td valign=top>Scripts referenced by the browser to manage session initiation.
+ </td>
+</tr>
+<tr>
+ <td></td>
+ <td valign=top><tt>images/</tt><br><tt>sounds/</tt><br><tt>pub/</tt></td>
+ <td></td>
+ <td valign=top>Scripts and data that don't require restricted access control.
+ </td>
+</tr>
+<tr>
+ <td valign=top><tt>config/</tt></td>
+ <td></td>
+ <td></td>
+ <td valign=top>Server configuration scripts, default settings.
+ </td>
+</tr>
+<tr>
+ <td valign=top><tt>lib/</tt></td>
+ <td></td>
+ <td></td>
+ <td valign=top>Contains components to support IPC, CGI processing and HTML generation.
+ </td>
+</tr>
+<tr>
+ <td valign=top><tt>src/</tt></td>
+ <td></td>
+ <td></td>
+ <td valign=top>Contains source files for Web Alpine's binary components which will be linked
+ against the <tt>pith/</tt> libarary components.
+ </td>
+ </td>
+</tr>
+<tr>
+ <td></td>
+ <td valign=top><tt>alpined.d/</tt></td>
+ <td></td>
+ <td valign=top>Source files for <tt>alpined</tt> serverette. This is a per-user, per-session
+ server that services requests for email data from the CGI scripts via UNIX domain
+ socket.
+ </td>
+</tr>
+<tr>
+ <td></td>
+ <td valign=top><tt>pubcookie/</tt></td>
+ <td></td>
+ <td valign=top>Source files providing UW Pubcookie web authentication support.
+ </td>
+</tr>
+<tr>
+ <td></td>
+ <td valign=top><tt>cgi.tcl-1.10/</tt></td>
+ <td></td>
+ <td valign=top>Source for TCL library providing CGI/HTML support
+ </td>
+</tr>
+<tr>
+ <td valign=top><tt>detach@</tt></td>
+ <td></td>
+ <td></td>
+ <td valign=top>Typically a symbolic link to a subdirectory within <tt>/tmp</tt>. It is used to hold temporary
+ copies of message attachments as they're downloaded to the browser.
+<table width="100%" align=center bgcolor="#dddddd"><tr><td>
+In the pubcookie case, it must have world read/write/execute mode set due to
+<tt>alpined</tt> pseudo-uid partitioning.
+</td></tr></table>
+
+</tr>
+</table>
+
+<p>
+
+<h2>Web Alpine Configuration</h2>
+<h3>CGI Script Configuration</h3>
+<p>
+Most Web Alpine configuration is contained in the <tt>config/alpine.tcl</tt> configuration file.
+Most of the interesting settings are toward the top of the file and pretty much suggest
+what they set. The most important settings, though, are probably:
+
+<table width="80%" align=center>
+<tr><td>
+<dl width=80%>
+<dt>
+ <tt>_wp(fileroot)</tt>
+</dt>
+<dd>
+that defines where the Web Alpine application was unpacked on your system, and
+</dd>
+<dt>
+<tt>_wp(urlprefix)</tt>
+</dt>
+<dd>
+which defines where browsers can find Web Alpine's CGI scripts. This is set to
+the null string if the server's DocumentRoot is synonymous with the root of
+Web Alpine's CGI directory. Othewise, it's typically set to the Alias accessed
+subdirectory in the web server's configuration.
+
+</dd>
+</dl>
+</td></tr>
+</table>
+
+
+<h3>Web Server Configuration</h3>
+<p>
+Typically, a Web Alpine server is used solely to serve Web Alpine pages. That is, no other hosting is
+done by the server. Thus, it is usually convenient to configure the web server to treat
+the <tt>web/cgi/</tt> directory within the distribution as the root directory of the pages
+it serves (or to move those files and directories into the web server's document root).
+Similarly, it may be necessary to configure the web server to process CGI scripts from
+it's root (since this <em>should</em> be a dedicated server this shouldn't matter).
+
+<h3>IMAP Server Configuration</h3>
+<p>
+Genarally, no configuration is required of the IMAP server.
+<table width="75%" align=center bgcolor="#dddddd"><tr><td>
+However, in the Pubcookie case the Web and IMAP servers need to coordinate the
+existence of a meta-user, such as <tt>webalpine</tt>, used for SASL proxy authentication. For UW imapd this
+means creating an account on the IMAP server that is a member of the <tt>&quot;mailadm&quot;</tt>
+group. A SASL GSSAPI authentication handshake is used between the
+Web and IMAP server when the web server initiates a session on behalf of
+a particular user.
+</td></tr></table>
+
+<h3>User Configuration</h3>
+<p>
+Since no user-initiated local file or mailbox access is permitted by (much less compiled into) the <tt>alpined</tt>,
+user configuration and data files are stored using Pine's remote pinerc and address book capabilites. The configuration
+settings in <tt>web/config/</tt> are used to set per-user defaults and direct Web Alpine toward the user's configuration
+settings on the IMAP server. Similarly, the default addressbook is stored as an IMAP folder on the server as well.
+Concurrent Web Alpine, Unix Pine and PC-Pine users that would like a consistent mail environment can easily configure their
+other Pine's to use the <tt>remote_pinerc</tt> and <tt>remote_addrbook</tt> on their IMAP server.
+
+<h3>Browser Configuration</h3>
+<p>
+A Web Alpine goal is to run reasonably on as many browsers as possible. Toward that end, little beyond basic table and form support
+is required of the browser. And while Javascript is not a requirement to access Web Alpine functions, when enabled in the browser
+some enhanced capability is available such as keyboard accessible commands and implicit selection of various listbox choices.
+
+<h2>Session Lifecycle</h2>
+<p>
+
+<ol>
+ <li>User requests <tt>greeting.tcl</tt> which consists of a form to be filled out with any necessary authentication tokens and mail server choice.
+ <table width="75%" align=center bgcolor="#dddddd"><tr><td>
+In Pubcookie case user is not presented the username/password option unless they have chosen to
+connect to a mail server outside the locally managed, predefined set.
+ </td></tr></table>
+ <li>User submits form with authentication tokens and initial mail server
+ <table width="75%" align=center bgcolor="#dddddd"><tr><td>
+By default, the submitted authentication tokens consist of a username/password pair. When Pubcookie is
+in use, the browser sends the pubcookie-specific authentication token with the form submission.
+ </td></tr></table>
+
+ <li>Web Alpine CGI logon script processes form and instantiates serverette. The logon script:
+ <ol>
+ <li>validates form data
+ <li>generates session key
+ <li>launches the Web Alpine serverette, <tt>alpined</tt>, passing session key via stdin
+ <table width="75%" align=center bgcolor="#dddddd"><tr><td>
+<tt>serverette</tt> reads session key, creates Unix domain socket, and enters command loop
+waiting for input on the fresh socket
+ </td></tr></table>
+ <li>sends serverette the command to establish a session with the requested IMAP server on behalf of the given user
+ <table width="75%" align=center bgcolor="#dddddd"><tr><td>
+By default, the login script simply passes the username/password pair to the serverette where
+it's the serverette's job to present them to the IMAP server for validation. If the IMAP server
+declines, a "bad user or password" error page is generated and sent to the browser and the serverette
+ exits.
+<p>
+The Pubcookie case is a bit more involved. The CGI scripts rely on the netid specified in the REMOTE_USER
+environment variable which is set as a side effect of pubcookie module processing. The trusted netid
+is not passed directly to the serverette, rather all CGI processing is done via a setuid Tcl interpreter.
+The uid is unique to each netid on the system, but not related to any netid/uid binding on general access
+systems. Running the CGI scripts and serverette under a netid-bound uid provides a convenient way to implement
+the authentication mechanism between the serverette and the IMAP server as well as a useful way to partition
+serverettes such that one compromised serverette can't affect others.
+ </td></tr></table>
+
+ <li>With a valid IMAP session established, the logon script redirects the user's browser to the initial
+ Message List page.
+ </ol>
+ <li>The user navigates/manipulates their email environment based on web pages generated by Tcl script
+templates which were fleshed out via requests to the <tt>alpined</tt> serverette. The serverette in turn
+may draw on its cache of IMAP data, make new requests of the IMAP server, post messages via SMTP or
+the local mail queue, formulate LDAP queries, or perform other tasks as required.
+ <table width="75%" align=center bgcolor="#dddddd"><tr><td>
+Note: Web Alpine sessions run as long as the user's browser requests pages. In the absense of user interaction
+Web Alpine will self-refresh every few minutes to mainain the session. Sessions only end when the user logs out
+or closes the browser.
+ </td></tr></table>
+ <li>User ends session and confirms
+ <table width="75%" align=center bgcolor="#dddddd"><tr><td>
+ Note: If the user simply closes their browser, the serverette will self-exit after 30 minutes.
+ </td></tr></table>
+</ol>
+
+<h3>Web Server Considerations</h3>
+<p>
+Web Alpine has been developed under Apache (versions 1.x thru 2.x). However, because the intent was to be as flexible
+and manageable as possible, little aside from SSL and basic CGI services are required of the web server. It's
+conceivable Web Alpine could be made to run under another server, or even Windows and IIS modulo the UNIX-Sockets communication
+issues between the CGI scripts and <tt>alpined</tt>.
+
+<p>
+The downside, of course, is that this requires somewhat redundant
+parses of the configuration and CGI-helper library with each page request. It's a trade-off. A slightly more efficient approach might be to create
+an apache module that understands requests and passes them directly to the corresponding <tt>alpined</tt> which would execute the script and return
+HTML directly. However, the additional cost in installation and management complexity stands to offset those gains.
+
+<p>
+Similarly, it is <bold>assumed</bold> that the Web Alpine service is provided on a black box server. That is, a host
+that has no general user accounts. Unmodified, Web Alpine creates the UNIX-domain sockets corresponding
+pretty directly to the user's session key in the <tt>/tmp</tt> directory. In addition, depending on the nature
+of the connection, the session key may also be exposed via oridinary httpd logging. <em>Important safety tip:</em> make
+sure ordinary users do not have access to the Web Alpine system or httpd log files. In the future those sockets may be moved into
+a access-restricted subdirectory, but the httpd log file record may be harder, and less reliably concealed.
+
+<h3>CGI Considerations</h3>
+<p>
+
+Most Web Alpine pages are generated via CGI scripts written in Tcl. A
+library of Tcl functions called <tt>cgi.tcl</tt> is used heavily to
+help with the HTML generation. Of course, this means that a web
+developer that might wish to change or enhance Web Alpine pages, will have
+to acquire some Tcl knowledge. Additionally, the library has one or two
+interface inconsistencies (not unlike Tcl, but that's another
+discussion), which will mean a bit steeper learning curve, but we
+think this is only slightly more difficult than the amount of Tcl
+one would have to learn in a more template-oriented approach. We think
+the scipt's logic flow and such is much easier to understand
+and maintain than the substitution and recursion necessary in an
+html-template approach.
+
+<p>
+
+
+<h3><span style="font-family: monospace; font-size: big">alpined</span> Considerations</h3>
+<p>
+Tcl, incidentally, is also the language used to move data in and out
+of the Web Alpine serverette, <tt>alpined</tt>. Tcl lends itself nicely
+to string oriented data, and provides a convenient, simple interface
+to export functionality contained in C-based utilities.
+
+<h3>HTML Considerations</h3>
+<p>
+Much of the HTML generated by Web Alpine does layout based on tables. This somewhat sub-optimal state
+mostly has to do with when the Web Alpine development effort was initiated and the concurrent browser
+chaos. The goal is to move scripts toward generating more CSS-oriented layout over time.
+
+<p>
+Similarly, earlier versions of Web Alpine relied heavily on Javascript in a misguided attempt to make the
+browser-based experience feel as familiar as possible to a dedicated desktop application. Beyond the fact that
+Javascript support varied widely across browsers at the time, it should have also been obvious that
+by presenting a familiar desktop-like interface, we also set desktop-like performance
+expectations which we had no hope of meeting.
+
+<h3>Clustering Considerations</h3>
+<p>
+Since <tt>alpined</tt> persists for the life of the user's session, the session is bound to the particular
+server that initiated it. In order to provide service to a sizeable constituency, it may be necessary to spread usage across
+a group or cluster of servers. There exist numerous strategies to distribute connecting users across a cluster, such
+as an initial server that redirects randomly to one of the servers in a cluster, DNS-based randomizing, or load-balancing
+strategies. The former can lead to web server names users find distracting (though this doesn't appear to be to
+much of an issue) and the latter, of course, could lead to misdirected requests over time (or as loads change) so
+it is necessary for servers to either redirect or proxy requests to the appropriate server.
+<p>
+As a basic allowance for such installations, Web Alpine's session key
+contains the hostname of the server that created it. Similarly, the
+access routines that parse the key for access to the appropriate
+<tt>alpined</tt> are aware of the hostname and will redirect
+misguided requests to the appropriate server. This isn't particularly
+satisfying in terms of network RTTs.
+<p>
+One alternative that saves network performance
+at the expense of slightly increased server load is to introduce a
+directory above the <tt>web alpine/</tt> script directory and then add one
+along side that new directory for each server in the cluster. It's then possible to use the Apache
+directive to proxy requests within the scope of those directories to
+the corresponding server.
+
+<h3>Security Considerations</h3>
+<p>
+<ul>
+<li>Session keys only valid for life of session. Can't acquire increased or prolonged rights based on key.
+<li>Link layer (ssl) encryption available (and likely the default in most situations)
+<li><tt>alpined</tt> pseudo-uid partitioning is employed in the pubcookie context
+</ul>
+<p>
+
+<h2>Future Plans</h2>
+<p>
+Through the semi-formal usability testing process, early testing
+phases and regular campus use, overall response has been very favorable.
+Usability testing concurrent with ongoing feature development and interface adjustments
+continues to hone rough edges, particularly where the drive for performance has led to
+less intuitive interface choices. We plan to continue emphasis on the refine/feedback
+loop as we roll in many of the features Pine users have come to appreciate.
+
+<p>
+Performance in terms of both user perceived response time and users per web server are
+always a concern, but must, of course, be balanced against additional maintenance and complexity costs.
+Less obvious complicating factors must be considered, such as <tt>alpined</tt> process partitioning
+and session-key containing cookie exposure in the face of malicous HTML attachments.
+We plan, of course, to continue exploring various methods to improve performance.
+
+<h2>Appendix: Installation Tests</h2>
+<p>
+For the most part, if you can get the login greeting page and
+then log into a session, things should be working for the
+most part. Some things you might try to verify
+a complete installation include:
+
+<ul>
+<li>Open a secondary folder</li>
+<li>Go back to Inbox</li>
+<li>View or Save an attachment</li>
+<li>Send a message</li>
+<li>Send a message with an attachment</li>
+<li>Spell check a message (if is ispell installed on the web server)</li>
+<li>Create an address book entry</li>
+<li>Delete an address book entry</li>
+<li>Save a message to a new folder</li>
+<li>Verify the new folder appears in the cached folder drop down</li>
+<li>Logout, Verify the folder appears in drop down list of subsequent session</li>
+<li>Try configuration settings such as Enable Full Headers</li>
+<li>Logout, Verify the setting change in subsequent session</li>
+</ul>
+
+
+</body>
+</html>
diff --git a/web/cgi/alpine/1.0/help/view.html b/web/cgi/alpine/1.0/help/view.html
new file mode 100644
index 00000000..0560ad2b
--- /dev/null
+++ b/web/cgi/alpine/1.0/help/view.html
@@ -0,0 +1,195 @@
+<!--
+# $Id: view.html 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+-->
+<form action="alpine/wp.tcl" target="noop" method=get>
+<input name="page" value="noop" type=hidden notab>
+<font size="+2"><b>Message View</b></font>
+
+<p>The Message View is where you will read, forward, and reply to your mail.</p>
+
+<UL TYPE=square>
+ <li><a href="#navigate"><b>Navigating your messages</b></a></li>
+ <li><a href="#attach"><b>Viewing attachments</b></a></li>
+ <li><a href="#reply"><b>Replying to the message</b></a></li>
+ <li><a href="#forward"><b>Forwarding the message</b></a></li>
+ <li><a href="#save"><b>Saving the message to another folder</b></a></li>
+ <li><a href="#take"><b>Adding addresses directly to address book</b></a></li>
+ <li><a href="#header"><b>Displaying full headers</b></a></li>
+ <li><a href="#print"><b>Printing</b></a></li>
+ <li><a href="#jump"><b>Jump Command (optional)</b></a></li>
+ <li><a href="#note"><b>Differences from Pine and PC-Pine</b></a></li>
+</ul>
+
+<a name="navigate"><b>Navigating your messages</b></a><p>
+In the far left column of the Message View screen, there are four navigation options: <b>Previous</b>, <b>First</b>, <b>Next</b>,
+<b>Last</b>. Click:
+<table border=0 cellpadding=4 style="margin-left:24">
+ <tr>
+ <td><img src="../../images/but_rnd_prev3.gif" alt="Previous"></td>
+ <td>to go to the previous message in this folder</td>
+ </tr>
+ <tr>
+ <td><img src="../../images/but_rnd_first3.gif" alt="First"></td>
+ <td>to go to the first message in this folder</td>
+ </tr>
+ <tr>
+ <td><img src="../../images/but_rnd_next3.gif" alt="Next"></td>
+ <td>to go the next message in this folder</td>
+ </tr>
+ <tr>
+ <td><img src="../../images/but_rnd_last3.gif" alt="Last"></td>
+ <td> to go to the last message in this folder</td>
+ </tr>
+</table>
+<p>
+
+With most broswsers and the usual settings, you also have the option of
+using keyboard controls for some of your navigation. You may, however,
+need to first click in your Alpine window to make sure that your system
+knows which screen is active.
+
+<p>The keyboard controls are:
+<table border=0 cellspacing=12 cellpadding=3>
+<tr><td text align="center">n</td>
+ <td>next message</td></tr>
+<tr><td text align="center">p</td>
+ <td>previous message</td></tr>
+<tr><td text align="center">i</td>
+ <td>return to message index</td></tr>
+<tr><td text align="center">c</td>
+ <td>compose new message</td></tr>
+<tr><td text align="center">l</td>
+ <td>list folder</td></tr>
+<tr><td text align="center">d</td>
+ <td>flag message as deleted</td></tr>
+<tr><td text align="center">u</td>
+ <td>unflag message as deleted</td></tr>
+<tr><td text align="center">a</td>
+ <td>go to address book</td></tr>
+<tr><td text align="center">r</td>
+ <td>reply to current message</td></tr>
+<tr><td text align="center">f</td>
+ <td>forward current message</td></tr>
+</table>
+
+<p>
+<a name="attach"><b>Viewing attachments</b></a><p>
+When a message includes an attachment, you will see highlighted links
+under the heading "Parts/Attachments." Click either "View" or the link
+description to see the attachment. Click "Save" to
+download the attachment.
+
+<p>The attachment links also allow you to read messages delivered as HTML.
+Simply view the second attachment to display the message as intended.
+
+<p>If you cannot view your attachments, go to
+<b>Configuration</b>
+ and click the Message View tab. Make sure "Enable Attachments View" is marked.</p>
+<p>
+
+<a name="reply"><b>Replying to the message</b></a><p>
+Use <input type=submit value="Reply"> to respond to the author of the message and/or to the
+other recipients who were sent the original message.
+
+<p>To respond to all original recipients, check "<input type=checkbox checked> To All" and then click
+<input type=submit value="Reply">.</p>
+
+<p>By default, "Include text" will be checked. If you do not want to
+include the previous text in your reply, remove that checkmark before you
+click <input type=submit value="Reply">.</p>
+
+
+<a name="forward"><b>Forwarding the message</b></a><p>
+Use <input type=submit value="Forward"> to send the message you are
+viewing to someone else.
+You will need to supply the email address of the new recipient on the
+subsequent Message Compose screen.</p>
+
+
+
+<a name="save"><b>Saving the message to another folder</b></a><p>
+
+Save the message you are viewing to another folder by choosing from
+the options in the dropdown menu next to the
+<input type=submit value="Save"> button. Among the Save options,
+you'll find
+
+<ul>
+ <li> The name of the default folder to save to, typically <font face="courier">saved-messages</font>.
+ <li> Several, typically six, names of folders you have most recently saved messages to
+ <li> An option to type in directly the name of the folder you wish to save to (either a new folder you wish to create, or one
+already containing messages)
+ <li> An option to choose the folder from the list of all your folders
+</ul>
+
+If Alpine does not act on your choice immediately, click the <input type=submit value="Save">
+button to initiate saving. Once saved, the chosen messages will be marked for deletion.
+
+
+<p>
+<a name="take"><b>Adding addresses directly to address book</b></a></p>
+Click <input type=submit value="Take"> on the top panel of
+your screen to go to a listing of all the email addresses contained within
+this message. Click next to any or all of those addresses to add them
+to your addressbook.<p>
+
+<a name="header"><b>Displaying full headers</b></a><p>
+Click the <img src="../../images/hdr.gif" alt="display full headers"> icon
+near the top right of your screen to view your full headers. The full
+headers contain information about the path taken by this email message. To
+return to digested headers, click <img src="../../images/hdrnon.gif" alt="display digested headers">.<p>
+
+
+<a name="print"><b>Printing</b></a><p>
+Click the <img src="../../images/printer2.gif" alt="print"> icon near the top
+right
+of your screen to print your message.
+
+<p>If you lose the right edge of your message, you may want to decrease
+your font size:
+
+<ol>
+<li>Click <b>Configure</b>
+<li>Click the "General" tab on the subsequent screen
+<li>Choose "small font" or "smallest font" from the drop down box next to
+<b>Font Size?</b>
+</ol>
+
+
+<p><a name="jump"><b>Jump Command (optional)</b></a><p>
+
+Depending on your configuration, you may also see a text box next to a
+<input type=submit value="Jump"> button on the left side of the page. Enter a
+message number into the text box and click the button to have that message
+number's text displayed.
+
+
+<p><a name="note"><b>Differences from Pine and PC-Pine</b></a><p>
+
+<p>Alpine is designed primarily for easy access to email. It is not
+intended to replace any other heavy duty email programs. Accordingly, it
+is missing some of the more advanced features you will find in Pine and
+PC-Pine. As development continues, features will be added as long as they
+do not make a large impact on Alpine's speed or efficiency.
+
+<p> Some of the features you will not currently find in Alpine Message
+View:
+<ul>
+<li>Change message status (available on Message List page)
+<li>Bounce
+<li>Go directly to a specific folder (may be added soon)
+<li>Where is (instead, use your browser's "find" command)
+<li>Export message
+</ul>
+</form>
+
diff --git a/web/cgi/alpine/1.0/help/wpsys.jpeg b/web/cgi/alpine/1.0/help/wpsys.jpeg
new file mode 100644
index 00000000..ba601d85
--- /dev/null
+++ b/web/cgi/alpine/1.0/help/wpsys.jpeg
Binary files differ
diff --git a/web/cgi/alpine/1.0/helpbody.tcl b/web/cgi/alpine/1.0/helpbody.tcl
new file mode 100755
index 00000000..1adbf1e3
--- /dev/null
+++ b/web/cgi/alpine/1.0/helpbody.tcl
@@ -0,0 +1,200 @@
+#!./tclsh
+# $Id: helpbody.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# helpbody.tcl
+#
+# Purpose: CGI script to generate html help text for Web Alpine
+
+# Input:
+set help_vars {
+ {topic "" about}
+ {topicclass "" ""}
+}
+
+# Output:
+#
+# HTML/Javascript/CSS data representing the message specified
+# by the 'uid' argument
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+
+set sections {
+ {about 0 "About Web Alpine" about}
+ {index 0 "Message List" index}
+ {view 0 "Message View" view}
+ {take 1 "Take Address" takeaddr}
+ {takeedit 1 "Take Address Edit" takeedit}
+ {folders 0 "Folder List" folders}
+ {foldiradd 1 "New Folder or Directory" foldiradd}
+ {compose 0 Compose compose}
+ {addrbrowse 1 "Address Browse" addrbrowse}
+ {attach 1 "Attach" attach}
+ {resume 0 "Resume" resume}
+ {addrbook 0 "Address Books" addrbook}
+ {addredit 1 "Edit Entry" addredit}
+}
+
+set hidden_sections {
+ {filtconf 0 "Filter Configuration" filtconf}
+ {filtedit 0 "Filter Editor" filtedit}
+}
+
+proc WPHelpTopic {topic} {
+ source genvars.tcl
+ foreach g [list general_vars msglist_vars composer_vars folder_vars address_vars msgview_vars rule_vars] {
+ foreach m [set $g] {
+ if {[string compare $topic [lindex $m 1]] == 0} {
+ return [lindex $m 2]
+ }
+ }
+ }
+
+ return $topic
+}
+
+WPEval $help_vars {
+ foreach s $sections {
+ if {[string compare $topic [lindex $s 0]] == 0} {
+ set helpfile [file join $_wp(cgipath) $_wp(appdir) $_wp(ui1dir) help [lindex $s 3]]
+ }
+ }
+
+ if {![info exists helpfile]} {
+ foreach s $hidden_sections {
+ if {[string compare $topic [lindex $s 0]] == 0} {
+ set helpfile [file join $_wp(cgipath) $_wp(appdir) $_wp(ui1dir) help [lindex $s 3]]
+ }
+ }
+ }
+
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "Web Alpine Help"
+ WPStyleSheets
+ }
+
+ cgi_body_args
+ cgi_body BGCOLOR=white "style=margin:16;margin-right:40;margin-bottom:64" {
+ if {[info exists helpfile]} {
+ if {[file exists ${helpfile}.tcl]} {
+ source ${helpfile}.tcl
+ } elseif {[file exists ${helpfile}.html]} {
+
+ cgi_puts "<form action=\"[cgi_root]/$_wp(appdir)/$_wp(ui1dir)/wp.tcl\" target=\"noop\" method=get>"
+ cgi_puts {<input name="page" value="noop" type=hidden notab>}
+
+ if {[catch {open ${helpfile}.html r} fid]} {
+ unset helpfile
+ } else {
+ cgi_put [read $fid]
+ close $fid
+ }
+ } else {
+ unset helpfile
+ }
+
+ cgi_puts "</form>"
+ }
+
+ if {[info exists helpfile] == 0} {
+ if {[string length $topicclass] && [catch {WPCmd PEInfo help $topic $topicclass} helptext] == 0 && [llength $helptext]} {
+ set shownvarname [WPHelpTopic $topic]
+
+ # rough once-over to digest it for suitable display in the alpine context
+ set show 1
+ foreach line $helptext {
+ switch -regexp -- $line {
+ {^<[/]?[hH][tT][mM][lL]>.*} -
+ {^<[/]?[tT][iI][tT][lL][eE]>.*} -
+ {^<[/]?[bB][oO][dD][yY]>.*} {
+ continue
+ }
+ {^<!--chtml if pinemode=.*} {
+ if {[regexp {^<!--chtml if pinemode=["]([^"]*).*} $line dummy mode]
+ && [string compare $mode web] == 0} {
+ set show 1
+ } else {
+ set show 0
+ }
+ }
+ {^<!--chtml else[ ]*-->} {
+ set show [expr {$show == 0}]
+ }
+ {^<!--chtml endif-->} {
+ set show 1
+ }
+ default {
+ if {$show} {
+ set urls {}
+ while {[regexp {<[aA] ([^>]*)>} $line urlargs]} {
+ lappend urls $urlargs
+ if {[regsub {(.*)<[aA] ([^>]*)>(.*)} $line "\\1___URL___\\3" line] == 0} {
+ break
+ }
+ }
+
+ # special locally expanded markup
+ set exp {<!--[#]echo var=([^ ]*)-->}
+ while {[regexp $exp $line dummy locexp]} {
+ set locexp [string trim $locexp {"}]
+ switch -regex $locexp {
+ ^FEAT_* {
+ set locexp [WPHelpTopic [string range $locexp 5 end]]
+ }
+ ^VAR_* {
+ set locexp [WPHelpTopic [string range $locexp 4 end]]
+ }
+ }
+
+ if {[regsub $exp $line $locexp line] == 0} {
+ break
+ }
+ }
+
+ if {[info exists shownvarname]} {
+ regsub -all -nocase $topic $line $shownvarname line
+ }
+
+ foreach url $urls {
+ if {[regsub {.*[Hh][Rr][Ee][Ff]=[\"]?([^ \">]*)[\"]?.*} $url {\1} href]} {
+ set newurl "<a href=helpbody.tcl?topic=$href\\&topicclass=plain>"
+ } else {
+ set newurl ""
+ }
+ if {[regsub {(.*)___URL___(.*)} $line "\\1$newurl\\2" line] == 0} {
+ break
+ }
+ }
+
+ cgi_puts $line
+ }
+ }
+ }
+ }
+ } else {
+ cgi_center {
+ cgi_put [cgi_img [WPimg dot2] height=50]
+ cgi_put [cgi_font class=notice "Help text for [cgi_italic $topic] doesn't exist yet, but when it does it'll appear here."]
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/helpindex.tcl b/web/cgi/alpine/1.0/helpindex.tcl
new file mode 100755
index 00000000..235e619d
--- /dev/null
+++ b/web/cgi/alpine/1.0/helpindex.tcl
@@ -0,0 +1,102 @@
+#!./tclsh
+# $Id: helpindex.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+#
+# helpindx.tcl
+#
+# Purpose: CGI script to generate html help text for Alpine
+#
+# Input:
+
+set help_vars {
+ {topic "" about}
+ {index "" ""}
+ {oncancel "" main}
+ {params "" ""}
+}
+
+#
+#
+# Output:
+#
+# HTML/Javascript/CSS help text for Alpine
+#
+
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+
+set help_menu {
+}
+
+set sections {
+ {about 0 "About Alpine" about}
+ {index 0 "Message List" index}
+ {view 0 "Message View" view}
+ {take 1 "Take Address" takeaddr}
+ {takeedit 1 "Take Address Edit" takeedit}
+ {folders 0 "Folder List" folders}
+ {foldiradd 1 "New Folder or Directory" foldiradd}
+ {compose 0 Compose compose}
+ {addrbrowse 1 "Address Browse" addrbrowse}
+ {attach 1 "Attach" attach}
+ {resume 0 "Resume" resume}
+ {addrbook 0 "Address Books" addrbook}
+ {addredit 1 "Edit Entry" addredit}
+}
+
+
+WPEval $help_vars {
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "Web Alpine Help"
+ WPStyleSheets
+ }
+
+ cgi_body BGCOLOR="$_wp(bordercolor)" {
+ lappend help_menu [list {} [list {cgi_put [cgi_nl][cgi_url [cgi_bold "Exit Help"] wp.tcl?page=${oncancel}&cid=[WPCmd PEInfo key]&x=[clock seconds] target=_top class=navbar]}]]
+ lappend help_menu {}
+
+ if {[string compare [string tolower $index] full] == 0} {
+ foreach s $sections {
+ set prefix ""
+ if {[lindex $s 1]} {
+ for {set j 0} {$j < [lindex $s 1]} {incr j} {
+ append prefix [cgi_nbspace][cgi_nbspace]
+ }
+
+ append prefix {- }
+ }
+
+ if {[string compare $topic [lindex $s 0]] == 0} {
+ lappend help_menu [list {} [list "cgi_puts \"<table width=\\\"100%\\\" cellspacing=0 cellpadding=0><tr><td class=navbar bgcolor=#000000>${prefix}[lindex $s 2]</td></tr></table>\""]]
+ } else {
+ lappend help_menu [list {} [list "cgi_puts \"${prefix}\[cgi_url \"[lindex $s 2]\" \"help.tcl?topic=[lindex $s 0]&oncancel=[WPPercentQuote $oncancel]&index=${index}\" class=navbar target=body\]\""]]
+ }
+ }
+ } else {
+ lappend help_menu [list {} [list "cgi_puts \"\[cgi_url \"About Web Alpine\" \"helpbody.tcl?topic=about&oncancel=[WPPercentQuote $oncancel]\" class=navbar target=bodtext\]\""]]
+ if {[string compare [string tolower $index] none]} {
+ lappend help_menu [list {} [list "cgi_puts \"\[cgi_url \"Other Topics\" \"helpindex.tcl?topic=${topic}&index=full&oncancel=[WPPercentQuote $oncancel]\" class=navbar target=bodindx\]\""]]
+ }
+ }
+
+ WPTFCommandMenu help_menu {}
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/index.tcl b/web/cgi/alpine/1.0/index.tcl
new file mode 100755
index 00000000..6a0fa543
--- /dev/null
+++ b/web/cgi/alpine/1.0/index.tcl
@@ -0,0 +1,1972 @@
+# $Id: index.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# index.tcl
+#
+# Purpose: CGI script snippet to generate html output associated
+# with the WebPine message list (index) body frame
+#
+# Input:
+set index_vars {
+ {sort {} date}
+ {rev {} 0}
+ {top {} 0}
+ {uid {} 0}
+ {width {} $_wp(width)}
+ {op {} none}
+ {bod_next {} 0}
+ {bod_last {} 0}
+ {bod_first {} 0}
+ {bod_prev {} 0}
+ {growpage {} 0}
+ {shrinkpage {} 0}
+ {grownum {} 0}
+ {goto {} ""}
+ {gonum {} ""}
+ {select {} ""}
+ {selectop {} ""}
+ {doselect {} 0}
+ {auths {} 0}
+ {user {} ""}
+ {pass {} ""}
+ {create {} 0}
+ {save {} ""}
+ {browse {} ""}
+ {f_name {} ""}
+ {f_colid {} ""}
+ {savecancel {} ""}
+ {promptsave {} ""}
+ {setflag {} ""}
+ {flags {} ""}
+ {repall {} 0}
+ {reptext {} 0}
+ {sortrev {} 0}
+ {sortto {} 0}
+ {sortfrom {} 0}
+ {sortdate {} 0}
+ {sortsize {} 0}
+ {sortsubject {} 0}
+ {sortorderedsubj {} 0}
+ {sortthread {} 0}
+ {queryexpunge {} 0}
+ {expunge {} 0}
+ {cid {} 0}
+ {folders {} 0}
+ {compose {} 0}
+ {submitted {} 0}
+ {zoom {} ""}
+ {mark {} ""}
+ {unmark {} ""}
+ {search {} ""}
+ {aggon {} ""}
+ {aggoff {} ""}
+ {hdron {} ""}
+ {hdroff {} ""}
+ {split {} 0}
+ {spamit {} ""}
+ {reload}
+}
+
+# Output:
+#
+
+# FOR TESTING VARIOUS LAYOUTS AND SUCH
+set do_status_icons 1
+
+set selectverb Search
+
+set growmax 50
+set growverb Increase
+set shrinkverb Decrease
+
+# various color defs
+set color(sortfg) "#FFFFFF"
+set color(sortbg) "#BFBFBF"
+set color(line1) "#EEEEEE"
+set color(line2) "#FFFFFF"
+set color(greyout) "#888888"
+set sb_width 13
+set sb_dir bar
+
+set aggbar 1
+set sortbar 2
+
+set gonum ""
+
+if {[catch {WPCmd PEInfo indexlines} ppg] || $ppg <= 0} {
+ set ppg $_wp(indexlines)
+}
+
+# make sure any caching doesn't screw this setting
+catch {WPCmd PEInfo set wp_spec_script fr_index.tcl}
+
+proc statmsg {s} {
+ global newmail
+
+ lappend newmail [list $s]
+}
+
+proc selectresponse {type num prevnum zoomref topref uidref} {
+ upvar $zoomref zoomed
+ upvar $topref top
+ upvar $uidref uid
+
+ if {$num == 0} {
+ if {$prevnum} {
+ set statmsg "$type search found no additional messages. Set of marked messages unchanged"
+ } else {
+ set statmsg "$type search found no matching messages"
+ }
+ } elseif {$num > 0} {
+ if {$prevnum} {
+ set statmsg "$type search found $num messages. [expr {$num + $prevnum}] total messages now marked."
+ } else {
+ set statmsg "$type search found and marked $num messages"
+ }
+
+ if {!$zoomed} {
+ # force reframing
+ set top "0+0"
+ set uid 0
+ }
+ } else {
+ set statmsg "[set num [expr abs($num)]] messages Unmarked."
+ if {$prevnum > $num} {
+ append statmsg " [expr {$prevnum - $num}] remain Marked."
+ }
+ }
+
+ # update zoomed count or zoom if necessary
+ if {$zoomed} {
+ set zoomed [WPCmd PEMailbox selected]
+ } elseif {[WPCmd PEInfo feature auto-zoom-after-select] && [WPCmd PEMailbox selected]} {
+ set zoomed [WPCmd PEMailbox zoom 1]
+ #append statmsg ". Those not matching excluded from view."
+ }
+
+ statmsg $statmsg
+ catch {WPCmd PEInfo unset wp_def_search_text}
+}
+
+
+proc sortname {name {current 0}} {
+ global rev me
+
+ switch -- $name {
+ Number { set newname "#" }
+ OrderedSubj { set newname "Ordered Subject" }
+ Arrival { set newname Arv }
+ Status { set newname "&nbsp;" }
+ default { set newname $name }
+ }
+
+ if {$current} {
+ if {$rev > 0} {
+ set text [cgi_imglink increas]
+ set args rev=0
+ } else {
+ set text [cgi_imglink decreas]
+ set args rev=1
+ }
+
+ append newname [cgi_url $text "wp.tcl?page=index&sortrev=1" "title=Reverse $newname ordering" target=body]
+ }
+
+ return $newname
+}
+
+proc lineclass {linenum} {
+ if {$linenum % 2} {
+ return i0
+ } else {
+ return i1
+ }
+}
+
+proc uid_framed {u mv} {
+ foreach m $mv {
+ if {$u == [lindex $m 1]} {
+ return 1
+ }
+ }
+ return 0
+}
+
+proc index_quote {text} {
+ set text [cgi_quote_html $text]
+ regsub -all { } $text {\&nbsp;} text
+
+ return $text
+}
+
+proc index_part_color {text color} {
+ if {[llength $color] == 2} {
+ set fg [lindex $color 0]
+ set bg [lindex $color 1]
+ return [cgi_span "style=color: $fg; background-color: $bg" $text]
+ } else {
+ return $text
+ }
+}
+
+## read vars
+foreach item $index_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} errstr]} {
+ error [list _action "Impart Variable" $errstr]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+# check for new mail first since counts
+# and sort order might change...
+if {[catch {WPNewMail $reload ""} newmail]} {
+ error [list _action "new mail" $newmail]
+}
+
+set cid [WPCmd PEInfo key]
+
+set messagecount [WPCmd PEMailbox messagecount]
+set delcount [WPCmd PEMailbox flagcount deleted]
+set flagcmd [WPCmd PEInfo feature enable-flag-cmd]
+set aggops [WPCmd PEInfo feature enable-aggregate-command-set]
+set aggtabstate [WPCmd PEInfo aggtabstate]
+set zoomed [WPCmd PEMailbox zoom]
+
+set indexheight [WPIndexLineHeight]
+
+if {$split != "0"} {
+ set vtarget fr_bottom
+} else {
+ set vtarget spec
+}
+
+# perform any requested actions
+if {$queryexpunge == 1 || [string compare [string tolower $queryexpunge] expunge] == 0} {
+ if {$delcount > 0} {
+ set fn [WPCmd PEMailbox mailboxname]
+ set oncancel index
+ # delcount, messagecount set above
+ source [WPTFScript expunge]
+ set nopage 1
+ } else {
+ statmsg "No deleted messages to Expunge"
+ }
+} elseif {$expunge == 1
+ || [string compare [string tolower $expunge] expunge] == 0
+ || [string compare [string range [string tolower $expunge] 0 10] "yes, remove"] == 0} {
+ if {$delcount > 0} {
+ if {$delcount < $messagecount
+ || ($delcount == $messagecount
+ && 0 == [catch {cgi_import emptyit}]
+ && 1 == $emptyit)} {
+ set setflags [WPCmd PEMessage $top status]
+ if {[lsearch $setflags "Deleted"] != -1} {
+ set curmsg [WPCmd PEMessage $top number]
+ set nextmsg [WPCmd PEMailbox next $curmsg]
+ set done 0
+ while {$nextmsg > $curmsg && $done == 0} {
+ set nextuid [WPCmd PEMailbox uid $nextmsg]
+ set setflags [WPCmd PEMessage $nextuid status]
+ if {[lsearch $setflags "Deleted"] == -1} {
+ set uid $nextuid
+ set top $uid
+ set done 1
+ } else {
+ set curmsg $nextmsg
+ set nextmsg [WPCmd PEMailbox next $curmsg]
+ }
+ }
+
+ if {$done == 0} {
+ set curmsg [WPCmd PEMessage $top number]
+ set prevmsg [WPCmd PEMailbox next $curmsg -1]
+ while {$prevmsg < $curmsg && $done == 0} {
+ set prevuid [WPCmd PEMailbox uid $prevmsg]
+ set setflags [WPCmd PEMessage $prevuid status]
+ if {[lsearch $setflags "Deleted"] == -1} {
+ set uid $prevuid
+ set top $uid
+ set done 1
+ } else {
+ set curmsg $prevmsg
+ set prevmsg [WPCmd PEMailbox next $curmsg -1]
+ }
+ }
+ }
+ }
+
+ if {[catch {WPCmd PEMailbox expunge} blasted] || [string length $blasted]} {
+ statmsg "Expunge problem: $blasted"
+ } else {
+ set delcount 0
+ set messagecount [WPCmd PEMailbox messagecount]
+ if {$messagecount < 1} {
+ set uid 0
+ set top 0
+ set first 1
+ if {$zoomed} {
+ WPCmd PEMailbox zoom 0
+ }
+ } else {
+ if {$top > 0 && ([catch {WPCmd PEMessage $top number} n] || $n <= 0)} {
+ # previous top message is gone, figure new one below
+ set top 0
+ }
+
+ if {$uid > 0 && ([catch {WPCmd PEMessage $uid number} n] || $n <= 0)} {
+ # recently viewed message is gone
+ set uid 0
+ }
+ }
+ }
+ } else {
+ statmsg "<div style=\"width: 75%; background-color: pink; border: 1px solid red; padding: 0 8\"><span style=\"font-size: 12pt; color: white; background-color: red; padding: 0 5\">No Messages Expunged!</span><div style=\"font-weight: normal; padding: 4 8\">To prevent unintended deletions, you must check the box on the Expunge Confirmation page to indicate that you acknowledge expunge will leave the folder <b>[WPCmd PEMailbox mailboxname]</b> empty.</div></div>"
+ }
+ } else {
+ statmsg "No deleted messages to Expunge"
+ }
+} elseif {$growpage == 1 || [string compare $growverb $growpage] == 0} {
+ if {$grownum <= 0 || $grownum > $growmax} {
+ set grownum 5
+ }
+
+ incr ppg $grownum
+ catch {WPCmd PEInfo set grownum $grownum}
+ WPCmd PEInfo indexlines $ppg
+} elseif {$shrinkpage == 1 || [string compare $shrinkverb $shrinkpage] == 0} {
+ if {$grownum <= 0 || $grownum > $growmax} {
+ set grownum 5
+ }
+
+ incr ppg -$grownum
+ catch {WPCmd PEInfo set grownum $grownum}
+
+ if {$ppg < 1} {
+ set ppg 1
+ }
+
+ WPCmd PEInfo indexlines $ppg
+} elseif {$bod_prev} {
+ set top "$top-$ppg"
+ set uid 0
+} elseif {$bod_first} {
+ set first 1
+ if {$messagecount > 0} {
+ set top [WPCmd PEMailbox uid $first]
+ }
+
+ set uid 0
+} elseif {$bod_next} {
+ set top "$top $ppg"
+ set uid 0
+} elseif {$bod_last} {
+ if {$messagecount > 0} {
+ if {$zoomed} {
+ if {$zoomed > $ppg} {
+ if {[catch {WPCmd PEMailbox uid [WPCmd PEMailbox next [expr {[WPCmd PEMailbox last] - [expr {${ppg} - 1}]}]]} top]} {
+ set first [WPCmd PEMailbox first]
+ set top [WPCmd PEMailbox uid $first]
+ } else {
+ set first [WPCmd PEMessage $top number]
+ }
+ }
+ } else {
+ if {[set first [expr {$messagecount - $ppg + 1}]] < 0} {
+ set first 1
+ }
+
+ set top [WPCmd PEMailbox uid $first]
+ }
+ }
+
+ set uid 0
+} elseif {[string length $goto]} {
+ if {[regexp {^([0-9]+)$} $gonum n]} {
+ if {$n > 0 && $n <= [WPCmd PEMailbox last]} {
+ set first $n
+ set uid [WPCmd PEMailbox uid $first]
+ set top $uid
+ set gonum ""
+ } else {
+ statmsg "Jump value $gonum out of range"
+ set goto ""
+ }
+ } else {
+ if {[string length $gonum]} {
+ statmsg "Unrecognized Jump value: $gonum"
+ } else {
+ statmsg "Enter a message number, then click 'Jump'"
+ }
+ }
+} elseif {$promptsave == 1 || [string compare [string tolower $promptsave] save] == 0} {
+ if {[WPCmd PEMailbox selected] == 0} {
+ statmsg "Place checkmarks in the box next to desired messages, then click 'Save'"
+ } else {
+ set uid 0
+ source [file join $_wp(cgipath) $_wp(appdir) $_wp(ui1dir) fr_promptsave.tcl]
+ set nopage 1
+ }
+} elseif {$savecancel == 1 || [string compare cancel [string tolower $savecancel]] == 0} {
+ catch {WPCmd PEInfo unset wp_index_script}
+ lappend newmail [list "Save cancelled. Folder not created."]
+} elseif {[string length $browse] && [string compare $browse Browse] == 0} {
+ set uid 0
+ _cgi_set_uservar onselect main
+ _cgi_set_uservar oncancel main
+ _cgi_set_uservar controls 0
+ source [WPTFScript savebrowse]
+ set nopage 1
+} elseif {([string length $save] && ([string compare [string trim $save] OK] == 0 || [string compare [string trim $save] Save] == 0)) || [string compare save [string tolower $op]] == 0} {
+ if {[WPCmd PEMailbox selected] == 0} {
+ statmsg "Place checkmarks in the box next to desired messages, then click 'Save'"
+ } elseif {[catch {cgi_import cancel}] == 0} {
+ statmsg "Save cancelled"
+ } else {
+ set f_name [string trim $f_name]
+ if {[string length $f_name]} {
+ if {[regexp {^([0-9]+)$} $f_colid]} {
+ switch -exact -- $f_name {
+ __folder__prompt__ {
+ set uid 0
+ _cgi_set_uservar onselect {index save=OK}
+ _cgi_set_uservar oncancel index
+ _cgi_set_uservar target body
+ _cgi_set_uservar controls 0
+ source [WPTFScript savecreate]
+ set nopage 1
+ }
+ __folder__list__ {
+ set uid 0
+ _cgi_set_uservar onselect {index save=OK}
+ _cgi_set_uservar oncancel index
+ _cgi_set_uservar target body
+ _cgi_set_uservar controls 0
+ source [WPTFScript savebrowse]
+ set nopage 1
+ }
+ default {
+ if {$auths} {
+ catch {WPCmd PESession nocred $f_colid $f_name}
+ if {[catch {WPCmd PESession creds $f_colid $f_name $user $pass} result]} {
+ lappend newmail ["Cannot set credentials ($f_colid) $f_name: result"]
+ }
+ }
+
+ if {[catch {WPCmd PEFolder exists $f_colid $f_name} reason]} {
+ if {[string compare BADPASSWD [string range $reason 0 8]] == 0} {
+ set oncancel "index.tcl&savecancel=1"
+ set conftext "Create Folder '$f_name'?"
+ lappend params [list page index]
+ lappend params [list save Save]
+ lappend params [list f_name $f_name]
+ lappend params [list f_colid $f_colid]
+ source [WPTFScript auth]
+ set nopage 1
+ } else {
+ lappend newmail [list "Existance test failed: $reason"]
+ }
+ } elseif {$reason == 0} {
+ if {$create == 1 || [string compare create [string tolower $create]] == 0} {
+ if {[catch {WPCmd PEFolder create $f_colid $f_name} reason]} {
+ lappend newmail [list "Create failed: $reason"]
+ } else {
+ set dosave 1
+ }
+ } else {
+ set qstate [list $f_name]
+ set params [list [list page index]]
+ lappend params [list save OK]
+ lappend params [list f_name $f_name]
+ lappend params [list f_colid $f_colid]
+ lappend qstate $params
+
+ if {[catch {WPCmd PEInfo set querycreate_state $qstate}] == 0} {
+ source [file join $_wp(cgipath) $_wp(appdir) $_wp(ui1dir) fr_querycreate.tcl]
+ set nopage 1
+ } else {
+ statmsg "Error saving creation state"
+ }
+ }
+ } else {
+ set dosave 1
+ }
+
+ if {[info exists dosave]} {
+ if {[catch {WPCmd PEMailbox apply save $f_colid $f_name} reason]} {
+ error [list _action "Cannot save to $f_name in $f_colid" $reason]
+ } else {
+ set statmsg "Saved $reason message[WPplural $reason] to $f_name"
+
+ set savedef [WPTFSaveDefault $uid]
+ if {[lindex $savedef 0] == $f_colid} {
+ WPTFAddSaveCache $f_name
+ }
+
+ if {[WPCmd PEInfo feature auto-unselect-after-apply]} {
+ if {[catch {WPCmd PEMailbox select none} result]} {
+ set statmsg "Cannot clear all message marks: $result"
+ } else {
+ if {$result == 0} {
+ set statmsg "No Marked messages to Unmark"
+ } else {
+ append statmsg " and unmarked"
+ }
+ }
+ }
+
+ statmsg $statmsg
+ }
+ }
+ }
+ }
+ } else {
+ statmsg "Unrecognized collection id"
+ }
+ } else {
+ statmsg "Must provide a folder name to Save to"
+ }
+ }
+} elseif {[string compare Set $setflag] == 0 || [string compare Delete $setflag] == 0 || [string compare Undelete $setflag] == 0} {
+ switch -- $setflag {
+ Delete {
+ set flagverb Delete
+ set flags delete
+ }
+ Undelete {
+ set flagverb Undelete
+ set flags undeleted
+ }
+ default {
+ set flagverb Set
+ }
+ }
+
+ if {[WPCmd PEMailbox selected] == 0} {
+ statmsg "Place checkmarks in the box next to desired messages, then click '$flagverb'"
+ } else {
+ switch -- $flags {
+ delete {
+ set flagverb "for deletion"
+ if {[catch {WPCmd PEMailbox apply delete} reason]} {
+ set statmsg "Problem deleting: $reason"
+ }
+ }
+ undeleted {
+ set flagverb Undeleted
+ if {[catch {WPCmd PEMailbox apply undelete} reason]} {
+ set statmsg "Problem undeleting: $reason"
+ }
+ }
+ new {
+ set flagverb New
+ if {[catch {WPCmd PEMailbox apply flag ton new} reason]} {
+ set statmsg "Problem setting New flag: $reason"
+ }
+ }
+ read {
+ set flagverb Read
+ if {[catch {WPCmd PEMailbox apply flag not new} reason]} {
+ set statmsg "Problem Unsetting New flag: $reason"
+ }
+ }
+ answered {
+ set flagverb Answered
+ if {[catch {WPCmd PEMailbox apply flag ton ans} reason]} {
+ set statmsg "Problem setting Answered flag: $reason"
+ }
+ }
+ unanswered {
+ set flagverb "Not Answered"
+ if {[catch {WPCmd PEMailbox apply flag not ans} reason]} {
+ set statmsg "Problem Unsetting Answered flag: $reason"
+ }
+ }
+ important {
+ set flagverb "Important"
+ if {[catch {WPCmd PEMailbox apply flag ton imp} reason]} {
+ set statmsg "Problem setting Important flag: $reason"
+ }
+ }
+ unimportant {
+ set flagverb "Not Important"
+ if {[catch {WPCmd PEMailbox apply flag not imp} reason]} {
+ set statmsg "Problem Unsetting Important flag: $reason"
+ }
+ }
+ default {
+ statmsg "Unknown flag set request: $flags"
+ }
+ }
+
+ if {![info exists statmsg]} {
+ set statmsg "$reason message[WPplural $reason] flagged $flagverb"
+ if {[WPCmd PEInfo feature auto-unselect-after-apply]} {
+ if {[catch {WPCmd PEMailbox select none} result]} {
+ set statmsg "Cannot clear all message marks: $result"
+ } else {
+ if {$result == 0} {
+ set statmsg "No Selected messages to Unmark"
+ } else {
+ append statmsg " and unmarked"
+ }
+ }
+ }
+ }
+
+ statmsg $statmsg
+ }
+} elseif {[string length $mark]} {
+ if {[string compare mark [string tolower [lindex $mark 0]]] == 0} {
+ if {[catch {WPCmd PEMailbox select all} result]} {
+ statmsg "Cannot mark all messages: $result"
+ } else {
+ set zm ""
+ if {$zoomed} {
+ set zoomed [WPCmd PEMailbox zoom 0]
+ set zm ". Message List now displaying all messages."
+ }
+ statmsg "All messages in folder '[WPCmd PEMailbox mailboxname]' marked$zm"
+ }
+ } elseif {[string compare $mark 1] == 0} {
+ if {$zoomed} {
+ statmsg "Messages on page already marked"
+ } elseif {$messagecount > 0} {
+ set p [split $uidpage ","]
+ set s [WPCmd PEMessage [lindex $p 0] number]
+ set e [WPCmd PEMessage [lindex $p end] number]
+
+ if {[catch {WPCmd PEMailbox select broad num $s $e} result]} {
+ statmsg "Cannot mark messages: $result"
+ } else {
+ statmsg "Marked all messages [cgi_underline "on this page"]"
+ }
+ }
+ }
+} elseif {[string length $unmark]} {
+ if {[string compare unmark [lindex [string tolower $unmark] 0]] == 0} {
+ if {[catch {WPCmd PEMailbox select none} result]} {
+ statmsg "Cannot clear all message marks: $result"
+ } else {
+ if {$result == 0} {
+ statmsg "No marked messages to Unmark"
+ } else {
+ if {[regexp {[0-9]+} $result] && $result == $messagecount} {
+ set result All
+ }
+
+ statmsg "Unmarked $result Messages"
+ }
+ }
+ } elseif {[string compare $unmark 1] == 0} {
+ if {[catch {WPCmd PEMailbox select clear [split $uidpage ","]} result]} {
+ statmsg "Cannot clear message marks: $result"
+ } else {
+ if {$result == 0} {
+ statmsg "No marked messages to Unmark"
+ } else {
+ statmsg "Unmarked all messages [cgi_underline "on this page"]"
+ }
+ }
+ }
+} elseif {[string length $select] && [string compare $select $selectverb] == 0} {
+ switch -- $selectop {
+ mark {
+ if {[catch {WPCmd PEMailbox select all} result]} {
+ statmsg "Cannot mark all messages: $result"
+ } elseif {0} {
+ statmsg "All messages marked"
+ }
+ }
+ clear {
+ if {[catch {WPCmd PEMailbox select none} result]} {
+ statmsg "Cannot clear all message marks: $result"
+ } elseif {0} {
+ if {$result == 0} {
+ statmsg "No marked messages to Unmark"
+ } else {
+ statmsg "Unmarked $result messages"
+ }
+ }
+ }
+ text {
+ set uid 0
+ source [file join $_wp(cgipath) $_wp(appdir) $_wp(ui1dir) fr_seltext.tcl]
+ set nopage 1
+ }
+ date {
+ set uid 0
+ source [file join $_wp(cgipath) $_wp(appdir) $_wp(ui1dir) fr_seldate.tcl]
+ set nopage 1
+ }
+ stat {
+ set uid 0
+ source [file join $_wp(cgipath) $_wp(appdir) $_wp(ui1dir) fr_selstat.tcl]
+ set nopage 1
+ }
+ zoom {
+ if {[WPCmd PEMailbox selected] == 0} {
+ statmsg "No marked messages to Zoom on"
+ } elseif {$zoomed == 0} {
+ set zoomed [WPCmd PEMailbox zoom 1]
+ }
+ }
+ unzoom {
+ set zoomed [WPCmd PEMailbox zoom 0]
+ }
+ null {
+ }
+ default {
+ set uid 0
+ source [file join $_wp(cgipath) $_wp(appdir) $_wp(ui1dir) fr_select.tcl]
+ set nopage 1
+ }
+ }
+} elseif {$zoom != ""} {
+ set z 0
+ if {[WPCmd PEMailbox selected] == 0} {
+ if {[string compare [string tolower [string range $zoom 0 7]] "show all"]} {
+ statmsg "No marked messages to Zoom in on!"
+ }
+ } elseif {$zoomed == 0} {
+ set z 1
+ }
+
+ set zoomed [WPCmd PEMailbox zoom $z]
+} elseif {$doselect == 1} {
+ WPLoadCGIVar result
+
+ if {[string compare new [string tolower $result]] == 0} {
+ catch {WPCmd PEMailbox select none}
+ catch {WPCmd PEMailbox zoom 0}
+ set zoomed 0
+ set result broad
+ }
+
+ set selected [WPCmd PEMailbox selected]
+
+ if {[catch {WPLoadCGIVar cancel}] || [string compare cancel [string tolower $cancel]] != 0} {
+ if {[catch {WPLoadCGIVar by}]} {
+ if {[string length $selectop]} {
+ set by [string tolower [lindex $selectop 1]]
+ } else {
+ statmsg "Unknown Search Option -- Click button associated with desired search"
+ set by unset
+ }
+ }
+
+ switch -- $by {
+ date {
+ WPLoadCGIVar datecase ;# on, since or before
+ WPLoadCGIVar datemon ;# month abbrev: jan, feb etc
+ WPLoadCGIVar dateday ;# day num
+ WPLoadCGIVar dateyear ;# year num
+
+ if {[catch {WPCmd PEMailbox select $result date $datecase $dateyear $datemon $dateday} reason]} {
+ statmsg "Date Search Failed: $reason"
+ } else {
+ selectresponse Date $reason $selected zoomed top uid
+ }
+ }
+ text {
+ WPLoadCGIVar textcase ;# ton, not
+ WPLoadCGIVar field ;# to from cc recip partic subj any
+ WPLoadCGIVar text ;# search string
+
+ if {[catch {WPCmd PEMailbox select $result text $textcase $field $text} reason]} {
+ statmsg "Text Search Failed: $reason"
+ } else {
+ switch -- $field {
+ subj {set fieldtext "\"Subject:\""}
+ from {set fieldtext "\"From:\""}
+ to {set fieldtext "\"To:\""}
+ cc {set fieldtext "\"Cc:\""}
+ recip {set fieldtext "Recipient"}
+ partic {set fieldtext "Participant"}
+ default {set fieldtext "Full text"}
+ }
+
+ selectresponse $fieldtext $reason $selected zoomed top uid
+ }
+ }
+ status {
+ if {[catch {WPLoadCGIVar flag}]} {
+ statmsg "Choose a Status value and then click [cgi_bold "Search Status"]"
+ } else {
+ WPLoadCGIVar statcase
+ if {[catch {WPCmd PEMailbox select $result status $statcase $flag} reason]} {
+ statmsg "Flag Search Failed: $reason"
+ } else {
+ switch -- $statcase {
+ not { set casetext "NOT " }
+ default { set casetext "" }
+ }
+ switch -- $flag {
+ imp {set flagtext Important}
+ new {set flagtext "New status"}
+ ans {set flagtext Answered}
+ del {set flagtext Deleted}
+ }
+ selectresponse "${casetext}${flagtext} status" $reason $selected zoomed top uid
+ }
+ }
+ }
+ unset {
+ if {[catch {WPLoadCGIVar text}] == 0} {
+ catch {WPCmd PEInfo set wp_def_search_text $text}
+ }
+ }
+ default {
+ WPCmd PEInfo statmsg "Unknown Search Option"
+ }
+ }
+ } else {
+ catch {WPCmd PEInfo unset wp_def_search_text}
+ }
+
+ catch {WPCmd PEInfo unset wp_index_script}
+} elseif {$sortrev} {
+ if {$rev} {
+ set rev 0
+ } else {
+ set rev 1
+ }
+} elseif {$sortto} {
+ set sort To
+} elseif {$sortfrom} {
+ set sort From
+} elseif {$sortdate} {
+ set sort Date
+} elseif {$sortsize} {
+ set sort siZe
+} elseif {$sortsubject} {
+ set sort Subject
+} elseif {$sortorderedsubj} {
+ set sort OrderedSubj
+} elseif {$sortthread} {
+ set sort tHread
+} elseif {$folders} {
+ source [file join $_wp(cgipath) $_wp(appdir) $_wp(ui1dir) folders.tcl]
+ set nopage 1
+} elseif {[string compare reply [string tolower $op]] == 0} {
+ if {0} {
+ set oncancel index
+ set cid [WPCmd PEInfo key]
+ #_cgi_set_uservar style Reply
+ source [WPTFScript compose]
+ set nopage 1
+ }
+ statmsg "Aggregate Reply not implemented yet"
+} elseif {[string compare forward [string tolower $op]] == 0} {
+ statmsg "Aggregate Forward not implemented yet"
+} elseif {[string length $aggon]} {
+ set aggtabstate [expr {$aggtabstate | $aggbar}]
+ WPCmd PEInfo aggtabstate $aggtabstate
+} elseif {[string length $aggoff]} {
+ if {$zoomed} {
+ set zoomed [WPCmd PEMailbox zoom 0]
+ statmsg "Message List now displaying all (marked and unmarked) messages"
+ }
+
+ set aggtabstate [expr {$aggtabstate & ~$aggbar}]
+ WPCmd PEInfo aggtabstate $aggtabstate
+} elseif {[string length $hdron]} {
+ set aggtabstate [expr {$aggtabstate | $sortbar}]
+ WPCmd PEInfo aggtabstate $aggtabstate
+} elseif {[string length $hdroff]} {
+ set aggtabstate [expr {$aggtabstate & ~$sortbar}]
+ WPCmd PEInfo aggtabstate $aggtabstate
+} elseif {[catch {WPCmd PEInfo set wp_index_script} script] == 0} {
+ catch {WPCmd PEInfo unset wp_index_script}
+ set uid 0
+ source [file join $_wp(cgipath) $_wp(appdir) $_wp(ui1dir) $script]
+ set nopage 1
+} elseif {[string length $spamit]} {
+ if {[WPCmd PEMailbox selected] == 0} {
+ statmsg "Place checkmarks in the box next to desired messages, then click '$spamit'"
+ } else {
+ # create
+
+ # aggregate save
+ if {[info exists _wp(spamsubj)] && [string length $_wp(spamsubj)]} {
+ set spamsubj $_wp(spamsubj)
+ } else {
+ set spamsubj "Spam Report"
+ }
+
+ # aggregate delete
+ if {[info exists _wp(spamfolder)] && [string length $_wp(spamfolder)]
+ && [catch {
+ set savedef [WPCmd PEMailbox savedefault]
+ if {[WPCmd PEFolder exists [lindex $savedef 0] $_wp(spamfolder)] == 0} {
+ WPCmd PEFolder create [lindex $savedef 0] $_wp(spamfolder)
+ }
+
+ WPCmd PEMailbox apply save [lindex $savedef 0] $_wp(spamfolder)
+ } result]} {
+ statmsg "Error Reporting Spam: $result"
+ } elseif {[info exists _wp(spamaddr)] && [string length $_wp(spamaddr)]
+ && [catch {WPCmd PEMailbox apply spam $_wp(spamaddr) $spamsubj} reason]} {
+ statmsg "Error Sending Spam Notice: $reason"
+ } elseif {[catch {WPCmd PEMailbox apply delete} reason]} {
+ statmsg "Error marking Spam Deleted: $reason"
+ } else {
+ set seld [WPCmd PEMailbox selected]
+ statmsg "$seld spam message[WPplural $seld] reported and flagged for deletion"
+ catch {WPCmd PEMailbox select none}
+ }
+ }
+} elseif {[string compare "set flags" [string tolower $op]] == 0} {
+ if {[catch {WPCmd PEInfo flaglist} flags]} {
+ statmsg "Can't get flags: $flags"
+ } else {
+ # then go thru flag list setting and clearing as needed
+ foreach flag $flags {
+ if {[catch {cgi_import_as $flag val}]} {
+ if {[catch {WPCmd PEMessage $uid flag $flag 0} result]} {
+ statmsg "Can't set $flag: $result"
+ break
+ }
+ } else {
+ set newval [expr {[string compare $val on] == 0}]
+ if {[catch {WPCmd PEMessage $uid flag $flag $newval} result]} {
+ statmsg "Can't set $flag: $result"
+ break
+ }
+ }
+ }
+ }
+}
+
+if {![info exists nopage]} {
+ # set the specified sort order
+ if {[catch {WPCmd PEMailbox sort $sort $rev} currentsort]} {
+ error [list _action Sort $currentsort]
+ }
+
+ if {$aggops} {
+ set selected [WPCmd PEMailbox selected]
+ if {$selected < 1 && $zoomed} {
+ set zoomed 0
+ WPCmd PEMailbox zoom 0
+ statmsg "Message List now displaying all (marked and unmarked) messages"
+ }
+ }
+
+ # "top" is uid of first message in index. n after '+' is relative offset
+ if {$messagecount <= 0} {
+ set first 1
+ set top 0
+ set miv ""
+ } else {
+ if {[regexp {^([0-9]+)([\+\ -])([0-9]+)$} $top dummy u sign offset]} {
+ if {$u == 0 || [catch {WPCmd PEMessage $u number} first]} {
+ set first [WPCmd PEMailbox first]
+ set top [WPCmd PEMailbox uid $first]
+ } else {
+ set top $u
+ }
+
+ if {$offset != 0} {
+ if {[catch {WPCmd PEMailbox next [WPCmd PEMessage $top number] ${sign}${offset}} newfirst] == 0} {
+ set first $newfirst
+ set top [WPCmd PEMailbox uid $newfirst]
+ }
+ }
+ } elseif {[regexp {^[0-9]*$} $top]} {
+ if {$top == 0 || [catch {WPCmd PEMessage $top number} first]} {
+ set first [WPCmd PEMailbox first]
+ set top [WPCmd PEMailbox uid $first]
+ }
+ } else {
+ statmsg "Invalid UID for first message"
+ set first [WPCmd PEMailbox first]
+ set top [WPCmd PEMailbox uid $first]
+ }
+
+ # validate first is in range
+ if {$messagecount && $first < [set f [WPCmd PEMailbox first]]} {
+ set first $f
+ set top [WPCmd PEMailbox uid $f]
+ }
+
+ # check framing
+ if {$zoomed} {
+ if {$zoomed < $ppg || [WPCmd PEMessage $top select] == 0} {
+ set first [WPCmd PEMailbox first]
+ set top [WPCmd PEMailbox uid $first]
+ } else {
+ set first [WPCmd PEMessage $top number]
+ }
+
+ set uid $top
+ } else {
+ if {$messagecount < $ppg} {
+ if {![string length $goto]} {
+ set first [WPCmd PEMailbox first]
+ set top [WPCmd PEMailbox uid $first]
+ }
+ } elseif {$first > $messagecount} {
+ if {[set mdiff [expr {$messagecount - $ppg + 1}]] < 0} {
+ set first [WPCmd PEMailbox first]
+ set top [WPCmd PEMailbox uid $first]
+ } else {
+ set first $mdiff
+ set top [WPCmd PEMailbox uid $mdiff]
+ }
+ }
+ }
+
+ # validate uid
+ # use "size" instead of "number" to work around temporary bug in pinetcld
+ if {$uid != 0 && ([catch {WPCmd PEMessage $uid size} n] || $n <= 0)} {
+ set uid 0
+ }
+
+ set nv [WPCmd PEMailbox nextvector $first $ppg [list indexparts indexcolor status statusbits]]
+ set miv [lindex $nv 0]
+ set charset [lindex $nv 1]
+
+ # hook to keep last viewed message in current message list
+ if {$uid > 0} {
+ if {[catch {WPCmd PEMessage $uid number} n] == 0 && [uid_framed $uid $miv] == 0} {
+ set first $n
+ set top $uid
+ set nv [WPCmd PEMailbox nextvector $first $ppg [list indexparts indexcolor status statusbits ]]
+ set miv [lindex $nv 0]
+ set charset [lindex $nv 1]
+ }
+
+ #set uid 0 ;# no longer "last viewed"
+ #catch {WPCmd PEInfo unset uid}
+ }
+
+ # remember for next time
+ catch {
+ WPCmd PEInfo set top $top
+ WPCmd PEInfo set sort $sort
+ WPCmd PEInfo set rev $rev
+ }
+ }
+
+ if {[llength $miv] == 0
+ || (!([info exists charset] && [string length $charset])
+ && ([catch {WPCmd PEConfig varget character-set} charset]
+ || [string length [set charset [lindex $charset 0]]] == 0
+ || [string compare [string tolower $charset] "us-ascii"] == 0))} {
+ set charset "UTF-8"
+ }
+
+ catch {fconfigure stdout -encoding binary}
+
+ # start writing page
+ cgi_http_head {
+ WPStdHttpHdrs "text/html; charset=$charset"
+ }
+
+ cgi_html {
+ cgi_head {
+ cgi_http_equiv Content-Type "text/html; charset=$charset"
+
+ set onload "onLoad="
+ set onunload "onUnload="
+ if {[info exists _wp(exitonclose)]} {
+ WPExitOnClose
+ append onload "wpLoad();"
+ append onunload "wpUnLoad();"
+ }
+
+ if {[info exists _wp(timing)]} {
+ set onsubmit "onSubmit=return submitTimestamp()"
+ cgi_script type="text/javascript" language="JavaScript" {
+ cgi_puts "var loadtime = new Date();"
+ cgi_put "function submitTimestamp(){"
+ cgi_put " var now = new Date();"
+ cgi_put " document.index.submitted.value = now.getTime();"
+ cgi_put " return true;"
+ cgi_puts "}"
+ cgi_put "function fini() {"
+ cgi_put " var now = new Date();"
+ cgi_put " var t_load = (now.getTime() - loadtime.getTime())/1000;"
+ if {$submitted} {
+ cgi_put " var t_submit = (now.getTime() - $submitted)/1000;"
+ set rtt ", rtt = '+t_submit+', cumulative = '+(t_submit + t_load)"
+ } else {
+ set rtt "'"
+ }
+ cgi_put " window.status = 'Page loaded in '+t_load+' seconds${rtt};"
+ cgi_puts "}"
+ }
+ append onload "fini();"
+ } else {
+ set onsubmit ""
+ }
+
+ set normalreload [cgi_buffer {WPHtmlHdrReload "$_wp(appdir)/$_wp(ui1dir)/wp.tcl?page=index"}]
+ if {[info exists _wp(exitonclose)]} {
+ cgi_script type="text/javascript" language="JavaScript" {
+ cgi_put "function indexReloadTimer(t){"
+ cgi_put " reloadtimer = window.setInterval('wpLink(); window.location.replace(\\'[cgi_root]/$_wp(appdir)/$_wp(ui1dir)/wp.tcl?page=index&reload=1\\')', t * 1000);"
+ cgi_puts "}"
+ }
+
+ append onload "indexReloadTimer($_wp(refresh));"
+ cgi_noscript {
+ cgi_puts $normalreload
+ }
+ } else {
+ cgi_puts $normalreload
+ }
+
+ WPStyleSheets
+ cgi_puts "<style type='text/css'>"
+ cgi_puts ".marked { font-weight: bold ; color: black }"
+ cgi_puts ".marked0 { font-size: 7pt; font-family: arial, sans-serif ; font-weight: bold ; color: black ; background-color: gold ; padding: 1 ; border: 1px solid black }"
+ cgi_puts ".aggop { font-family: arial, sans-serif ; font-size: 7pt }"
+ cgi_puts "A.aggop { color: white ; font-size: 7pt }"
+ cgi_puts ".navbutton { font-family: arial, sans-serif ; font-size: 7pt; overflow: visible; width: auto; padding: 0 2px; margin-right: 2px }"
+ cgi_puts ".itable { font-family: geneva, arial, sans-serif }"
+ cgi_puts ".gradient { background-image: url('[WPimg indexhdr]') ; background-repeat: repeat-x }"
+ cgi_puts ".icell { white-space: nowrap; padding-right: 8px }"
+ cgi_puts ".icell0 { white-space: nowrap }"
+ cgi_puts "</style>"
+
+ cgi_script type="text/javascript" language="JavaScript" {
+ cgi_put "function flip(d){"
+ cgi_put " var f = document.index;"
+ cgi_put " if(f && document.implementation){"
+ cgi_put " var e = document.createElement('input');"
+ cgi_puts " var ver = navigator.appVersion;"
+ cgi_put " if(!((ver.indexOf('MSIE')+1) && (ver.indexOf('Macintosh')+1))) e.type = 'hidden';"
+ cgi_put " e.name = 'bod_'+d;"
+ cgi_put " e.value = 1;"
+ cgi_put " f.appendChild(e);"
+ cgi_put " f.submit();"
+ cgi_put " return false;"
+ cgi_put " }"
+ cgi_put " return true;"
+ cgi_puts "}"
+
+ cgi_put "function view(u) {"
+ cgi_put " var f = document.index;"
+ cgi_put " f.target = '$vtarget';"
+ cgi_put " f.page.value = 'fr_view';"
+ cgi_put " f.uid.value = u;"
+ cgi_puts " f.submit();"
+ cgi_puts " return false;"
+ cgi_puts "}"
+
+ cgi_put "function toggleMarks(elobj){"
+ cgi_put " var i, ckd = 1;"
+ cgi_put " for(i = 0; i < document.index.uidList.length; i++)"
+ cgi_put " if(!document.index.uidList\[i\].checked){"
+ cgi_put " ckd = 0;"
+ cgi_put " break;"
+ cgi_put " }"
+ cgi_put " for(i = 0; i < document.index.uidList.length; i++) document.index.uidList\[i\].checked = !ckd;"
+ cgi_put " elobj.src = (ckd) ? '[WPimg markall3]' : '[WPimg marknone3]';"
+ cgi_put " return false;"
+ cgi_puts "}"
+ }
+
+ if {$_wp(keybindings)} {
+ set kequiv {
+ {{?} {top.location = 'wp.tcl?page=help'}}
+ {{l} {top.location = 'wp.tcl?page=folders'}}
+ {{a} {top.location = 'wp.tcl?page=addrbook'}}
+ {{n} {if(flip('next')) location = 'wp.tcl?page=body&bod_next=1'}}
+ {{p} {if(flip('prev')) location = 'wp.tcl?page=body&bod_prev=1'}}
+ {{ } {if(flip('next')) location = 'wp.tcl?page=body&bod_next=1'}}
+ {{-} {if(flip('prev')) location = 'wp.tcl?page=body&bod_prev=1'}}
+ {{;} {if(document.index.select) document.index.select.click(); else document.index.aggon.click()}}
+ {{z} {if(document.index.zoom) document.index.zoom.click()}}
+ }
+
+ lappend kequiv [list {c} "top.location = 'wp.tcl?page=compose&oncancel=main.tcl&cid=[WPCmd PEInfo key]'"]
+
+ if {$aggops && ($aggtabstate & $aggbar)} {
+ set exclusions document.index.f_name
+ } else {
+ set exclusions ""
+ }
+
+ append onload [WPTFKeyEquiv $kequiv $exclusions]
+ }
+ }
+
+ cgi_body bgcolor=$_wp(bordercolor) background=[file join $_wp(imagepath) logo $_wp(logodir) back.gif] "style=\"background-repeat: repeat-x\"" $onload $onunload {
+
+ catch {WPCmd set help_context index}
+
+ # check for status msg updates
+ foreach s [WPStatusMsgs] {
+ lappend newmail [list $s]
+ }
+
+ if {[llength $newmail]} {
+ WPTFStatusTable $newmail 1 "style=\"padding: 6px 0; color: white\""
+
+ if {!$reload} {
+ WPCmd PEMailbox newmailreset
+ }
+ }
+
+ if {$ppg > 50} {
+ set postmethod post
+ set enctype "multipart/form-data"
+ } else {
+ set postmethod get
+ set enctype "application/x-www-form-urlencoded"
+ }
+
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=$postmethod enctype=$enctype name=index $onsubmit target=body {
+
+ # context line
+ cgi_table border=0 cellspacing=0 cellpadding=0 width="100%" class="context" {
+ if {[llength $newmail]} {
+ cgi_table_row {
+ cgi_table_data height=1 bgcolor="#000000" width="100%" colspan=8 {
+ cgi_put [cgi_img [WPimg dot2] border=0 height=1]
+ }
+ }
+ }
+
+ cgi_table_row bgcolor="#999999" {
+ if {$zoomed} {
+ if {[catch {WPCmd PEMailbox messagecount after [lindex [lindex $miv end] 0]} messagesremaining]} {
+ unset messagesremaining
+ }
+
+ set z "[cgi_span class="marked" marked] "
+ set t $zoomed
+ } else {
+ set messagesremaining [expr {$messagecount - ($first + $ppg - 1)}]
+ set t $messagecount
+ set z ""
+ }
+
+ if {[catch {WPCmd PEMailbox messagecount before [lindex [lindex $miv 0] 0]} messagesbefore] == 0 && $messagesbefore > 0} {
+ if {$messagesbefore > $ppg} {
+ set b $ppg
+ } else {
+ set b $messagesbefore
+ }
+
+ switch $b {
+ 1 {
+ set c One
+ set p ""
+ }
+ default {
+ set c $b
+ set p s
+ }
+ }
+
+ set prevtext " [cgi_unbreakable_string "($c on [cgi_url previous wp.tcl?page=body&bod_prev=1 "onClick=return flip('prev')"] page)"]"
+ } else {
+ set messagesbefore 0
+ set prevtext ""
+ }
+
+ if {$messagesbefore <= 0 && $messagesremaining <= 0} {
+ switch $t {
+ 0 {
+ set counttext "[cgi_bold No] ${z}messages in folder"
+ }
+ 1 {
+ set counttext "[cgi_bold Only] ${z}message in folder"
+ }
+ 2 {
+ set counttext "[cgi_bold Both] ${z}messages in folder"
+ }
+ default {
+ set counttext "[cgi_bold All] $t ${z}messages in folder"
+ }
+ }
+ } elseif {$zoomed} {
+ if {$messagesbefore > 0} {
+ if {$messagesremaining > 0} {
+ set counttext "[expr {$messagesbefore + 1}] thru [expr {$zoomed - $messagesremaining}] of $zoomed [cgi_span class="marked" marked] messages"
+ } else {
+ if {[set p [llength $miv]] == 1} {
+ set lasttext ""
+ } else {
+ set lasttext " $p"
+ }
+
+ set counttext "[cgi_bold Last]${lasttext} of $zoomed [cgi_span class="marked" marked] messages"
+ }
+ } else {
+ set counttext "[cgi_bold First] $ppg of $zoomed [cgi_span class="marked" marked] messages"
+ }
+ } else {
+ set n [WPcomma $messagecount]
+
+ if {[lindex [lindex $miv 0] 0] == 1} {
+ set counttext "[cgi_bold First] $ppg of $n messages"
+ } else {
+ set l [lindex [lindex $miv end] 0]
+ if {$l == $messagecount} {
+ if {[set p [llength $miv]] == 1} {
+ set lasttext ""
+ } else {
+ set lasttext " $p"
+ }
+
+ set counttext "[cgi_bold Last]${lasttext} of $n messages"
+ } else {
+ set counttext "Message [WPcomma $first] - [WPcomma $l] of $n"
+ }
+ }
+ }
+
+ cgi_table_data align=left class="context" "style=\"padding-left: 4\"" {
+ cgi_unbreakable {
+ cgi_put "${counttext}[cgi_breakable]${prevtext}"
+ }
+ }
+
+ cgi_table_data align=right {
+ cgi_unbreakable {
+ if {[WPCmd PEInfo feature expunge-without-confirm-everywhere]
+ || ([WPCmd PEInfo feature expunge-without-confirm]
+ && [string compare [string tolower [WPCmd PEMailbox mailboxname]] inbox] == 0)} {
+ set butname expunge
+ cgi_text "emptyit=1" type=hidden notab
+ } else {
+ set butname queryexpunge
+ }
+
+ if {$aggops} {
+ cgi_submit_button "mark=Mark All in Folder" class="navbutton"
+ cgi_submit_button "unmark=Unmark All" class="navbutton"
+
+ cgi_put [cgi_breakable]
+ cgi_submit_button "setflag=Delete" class="navbutton"
+ cgi_submit_button "setflag=Undelete" class="navbutton"
+ if {([info exists _wp(spamaddr)] && [string length $_wp(spamaddr)])
+ || ([info exists _wp(spamfolder)] && [string length $_wp(spamfolder)])} {
+ cgi_submit_button "spamit=Report Spam" class="navbutton" "style=\" color: white; background-color: black\""
+ }
+ }
+
+ cgi_submit_button "${butname}=Expunge" class="navbutton"
+ }
+ }
+ }
+ }
+
+ cgi_text "page=index" type=hidden notab
+ cgi_text "cid=$cid" type=hidden notab
+ cgi_text "first=$first" type=hidden notab
+ cgi_text "uid=0" type=hidden notab
+ cgi_text "top=$top" type=hidden notab
+ cgi_text "frestore=1" type=hidden notab
+ cgi_text "submitted=0" type=hidden notab
+
+ if {$aggops} {
+ if {[llength $miv]} {
+ foreach v $miv {
+ lappend uv [lindex $v 1]
+ }
+
+ set uidpage [join $uv ","]
+ } else {
+ set uidpage ""
+ }
+
+ cgi_text "uidpage=${uidpage}" type=hidden notab
+
+ if {$aggtabstate & $aggbar} {
+ # commands
+ cgi_table border=0 cellspacing=0 cellpadding=0 width="100%" class=ops {
+ cgi_table_row {
+ cgi_table_data height=1 "bgcolor=#000000" colspan=12 align=left {
+ cgi_put [cgi_img [WPimg dot2] height=1]
+ }
+ }
+ cgi_table_row {
+ cgi_put "<td height=100% width=11px align=left valign=bottom background=[WPimg barclose_mid]><input type=image name=\"aggoff\" src=[WPimg barclose] border=0 alt=\"Hide Message Commands\"></td>"
+
+ cgi_table_data align=center valign=middle nowrap class=aggop width=30% {
+ cgi_puts "<fieldset>"
+ cgi_puts "<legend style=\"color: white\">Search and Mark</legend>"
+ cgi_submit_button "select=${selectverb}" class=aggop "style=\"vertical-align: middle; margin-right: 4\""
+
+ if {$zoomed} {
+ cgi_submit_button "zoom=Show All Messages" class=aggop "style=\"vertical-align: middle\""
+ } else {
+ cgi_submit_button "zoom=Show Only Marked" class=aggop "style=\"vertical-align: middle\""
+ }
+ cgi_puts "</fieldset>"
+ }
+
+ cgi_table_data align=center valign=middle nowrap class=aggop width=30% {
+ cgi_puts "<fieldset>"
+ cgi_puts "<legend style=\"color: white\">Message Status</legend>"
+ cgi_submit_button setflag=Set class=aggop "style=\"vertical-align:middle\""
+ cgi_put "[cgi_nbspace]status to "
+ cgi_select flags class=aggop "style=\"vertical-align:middle\"" {
+ #cgi_option Deleted value=delete
+ #cgi_option Undeleted value=undeleted
+ cgi_option New value=new selected
+ cgi_option Read value=read
+ cgi_option Important value=important
+ cgi_option Unimportant value=unimportant
+ cgi_option Answered value=answered
+ cgi_option Unanswered value=unanswered
+ }
+
+ cgi_puts "</fieldset>"
+ }
+
+ cgi_table_data align=center valign=middle class=aggop width=40% {
+ cgi_puts "<fieldset>"
+ cgi_puts "<legend style=\"color: white\">Save Messages</legend>"
+ # * * * * Save * * * *
+
+ cgi_submit_button "save=Save" class=aggop "style=\"vertical-align:middle\""
+ cgi_put "[cgi_nbspace]to "
+
+ set savedef [WPTFSaveDefault $uid]
+
+ cgi_text f_colid=[lindex $savedef 0] type=hidden notab
+
+ cgi_select f_name class=aggop "style=\"vertical-align:middle\"" "onchange=document.index.save.click(); return false;" {
+ foreach {oname oval} [WPTFGetSaveCache [lindex $savedef 1]] {
+ cgi_option $oname value=$oval
+ }
+ }
+
+ #cgi_put "[cgi_nbspace]"
+ #cgi_submit_button "save=OK" class=aggop "style=\"vertical-align:middle;margin-right:2\""
+ cgi_puts "</fieldset>"
+ }
+ }
+ cgi_table_row {
+ cgi_table_data height=1 "bgcolor=#000000" colspan=12 {
+ cgi_put [cgi_img [WPimg dot2] height=1]
+ }
+ }
+ }
+ } elseif {$aggtabstate & $sortbar} {
+ # aggop bar
+ cgi_table border=0 cellspacing=0 cellpadding=0 width="100%" class=ops {
+ cgi_table_row {
+ cgi_table_data height=1 "bgcolor=#000000" colspan=12 align=left {
+ cgi_put [cgi_img [WPimg dot2] height=1]
+ }
+ }
+ cgi_table_row {
+ cgi_table_data {
+ cgi_image_button aggon=[WPimg baropen] border=0 alt=\"Show Search/Save Commands\"
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data height=1 "bgcolor=#000000" colspan=12 {
+ cgi_put [cgi_img [WPimg dot2] height=1]
+ }
+ }
+ }
+ }
+ }
+
+ cgi_table cellspacing=0 cellpadding=0 width="100%" class="itable" {
+ if {$messagecount < 1} {
+ # special case for mailbox with no messges
+ cgi_table_row {
+ cgi_table_data class=body valign=middle height=[expr {($ppg + 1) * $indexheight}] {
+ cgi_center {
+ cgi_puts [cgi_font size=+2 "\[Folder \"[WPCmd PEMailbox mailboxname]\" contains no messages\]"]
+ }
+ }
+ }
+ } else {
+
+ # get the desired index item order and spacing
+ set iformat [WPCmd PEMailbox indexformat]
+
+ set colspan [expr {[llength $iformat] + 1}]
+
+ # setup per-column layout parameters
+ foreach fmt $iformat {
+
+ if {[info exists oncethru]} {
+ if {![info exists firstcell]} {
+ set class icell0
+ set firstcell 1
+ } else {
+ set class icell
+ }
+
+ append layout " $class "
+ }
+
+ set oncethru 1
+
+ switch -regexp -- [lindex $fmt 0] {
+ [Ss]tatus {
+ # fixed with "envelope" icon
+ if {[string length [lindex $fmt 2]]} {
+ set width "1%"
+ } else {
+ set width "42px"
+ }
+ }
+ default {
+ # proportion computed by pith (may be user specified)
+ if {[regexp {[0123456789]+[%]} [lindex $fmt 1]]} {
+ set width [lindex $fmt 1]
+ } else {
+ set width "1%"
+ }
+ }
+ }
+
+ append layout "$width"
+ }
+
+ append layout " icell0"
+
+ #set doscrollbar [expr {($zoomed && $zoomed > $ppg) || (!$zoomed && $messagecount > $ppg)}]
+ set linenum 1
+
+ # paint the index line column headers
+ cgi_table_row class=\"gradient\" {
+ if {($aggtabstate & $sortbar) == 0} {
+ if {!([info exists headertab] && [string length $headertab])} {
+ cgi_table_data class=indexhdr align=left width=48px background=[WPimg baropen_mid] {
+ if {$aggops && ($aggtabstate & $aggbar) == 0} {
+ cgi_image_button aggon=[WPimg baropen] border=0 alt=\"Show Search/Save Commands\"
+ }
+
+ cgi_image_button hdron=[WPimg baropen] border=0 alt=\"Show List Headers\"
+ }
+
+ foreach fmt $iformat {width class} $layout {
+ cgi_table_data class=indexhdr align=left width=$width background=[WPimg baropen_mid] {
+ cgi_put [cgi_img [WPimg dot2] height=1]
+ }
+ }
+ }
+ } else {
+ if {$aggops} {
+ cgi_table_data height=100% align=left valign=bottom class=indexhdr width=\"40px\" {
+ cgi_table width=\"100%\" cellpadding=0 cellspacing=0 border=0 {
+ cgi_table_row {
+ cgi_table_data align=left valign=bottom {
+ cgi_image_button hdroff=[WPimg barclose] border=0 "alt=\"Hide List Headers\""
+ }
+ cgi_table_data align=center valign=middle "style=\"padding-right: 14px\"" {
+ set marked 1
+ foreach v $miv {
+ set u [lindex $v 1]
+ if {$u == 0 || ![WPCmd PEMessage $u select]} {
+ set marked 0
+ break
+ }
+ }
+
+ if {$marked} {
+ cgi_image_button unmark=[WPimg marknone3] border=0 alt=\"Unmark Messages on Page\" "onClick=return toggleMarks(this);"
+ } else {
+ cgi_image_button mark=[WPimg markall3] border=0 alt=\"Mark Messages on Page\" "onClick=return toggleMarks(this);"
+ }
+ }
+ }
+ }
+ }
+ } else {
+ cgi_td class=indexhdr [cgi_nbspace]
+ }
+
+ set sorts [string tolower [WPCmd PEMailbox sortstyles]]
+
+ foreach fmt $iformat {width class} $layout {
+ set title [lindex $fmt 0]
+ catch {unset sortlist}
+
+ if {[lsearch -exact $sorts [string tolower $title]] >= 0} {
+ if {[string compare [string tolower [lindex $currentsort 0]] [string tolower $title]] == 0} {
+ lappend sortlist [list indexsort [sortname $title 1]]
+ } else {
+ lappend sortlist [list indexhdr [cgi_url [sortname $title] wp.tcl?page=index&sort[string tolower $title]=1 class=indexhdr title='Sort by $title' target=body]]
+ }
+
+ # special subject sort handling
+ switch -regexp -- $title {
+ [Ss]ubject { lappend extrasort OrderedSubj ; lappend extrasort Thread }
+ [Ff]rom { lappend extrasort To }
+ x[Dd]ate { lappend extrasort Arrival }
+ }
+
+ if {[info exists extrasort]} {
+ foreach s $extrasort {
+ lappend sortlist [list indexhdr [cgi_bold "|"]]
+ # append text [cgi_nbspace][cgi_bold "|"][cgi_nbspace]
+ if {[string compare [string tolower [lindex $currentsort 0]] [string tolower $s]] == 0} {
+ if {[string compare [string tolower $s] thread] == 0} {
+ set threadsort 1
+ }
+ lappend sortlist [list indexsort [sortname $s 1]]
+ } else {
+ lappend sortlist [list indexhdr [cgi_url [sortname $s] wp.tcl?page=index&sort[string tolower $s]=1 class=indexhdr title='Sort by $s' target=body]]
+ }
+ }
+
+ # clear for next time
+ unset extrasort
+ }
+ } else {
+ lappend sortlist [list indexhdr [sortname $title]]
+ }
+
+ cgi_table_data class=indexhdr valign=middle class=$class width="$width" {
+ cgi_table border=0 cellspacing=0 cellpadding=2 {
+ cgi_table_row {
+ foreach s $sortlist {
+ cgi_table_data class=[lindex $s 0] nowrap {
+ cgi_puts [lindex $s 1]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if {[info exists use_plus_minus_to_grow_shrink]} {
+ # grow/shrink controls
+ cgi_table_data class=indexhdr {
+ cgi_image_button growpage=[WPimg plus2] height=12 border=0 alt="Grow"
+ cgi_image_button shrinkpage=[WPimg minus2] height=12 border=0 alt="Shrink"
+ }
+ }
+ }
+ }
+
+ if {$zoomed} {
+ cgi_table_row {
+ cgi_table_data class=marked0 colspan=$colspan align=center {
+ cgi_puts "Unmarked Messages are NOT Shown - Click [cgi_url "Show All Messages" wp.tcl?page=body&zoom=0] to View All"
+ #cgi_puts "Unmarked Messages Are Excluded From View"
+ }
+ }
+ }
+
+ # get ready to map thread images
+ if {[string compare [string tolower [lindex $currentsort 0]] thread] == 0} {
+ set barblank [WPThreadImageLink barblank $indexheight]
+ set barvert [WPThreadImageLink barvert $indexheight]
+ if {[lindex $currentsort 1]} {
+ set barmsg [WPThreadImageLink ibarmsg $indexheight]
+ } else {
+ set barmsg [WPThreadImageLink barmsg $indexheight]
+ }
+
+ if {[lindex $currentsort 1]} {
+ set barvertmsg [WPThreadImageLink ibarvertmsg $indexheight]
+ } else {
+ set barvertmsg [WPThreadImageLink barvertmsg $indexheight]
+ }
+ }
+
+ foreach v $miv {
+ set n [lindex $v 0]
+ set u [lindex $v 1]
+ set msg [lindex [lindex $v 2] 0]
+ set linecolor [lindex [lindex $v 2] 1]
+ set stat [lindex [lindex $v 2] 2]
+ set statbits [lindex [lindex $v 2] 3]
+
+ set class [lineclass [incr linenum]]
+
+ if {$n > $messagecount} {
+ break
+ }
+
+ if {[llength $linecolor] == 2 && [string compare [lindex $linecolor 0] [lindex $linecolor 1]]} {
+ set style "color: #[lindex $linecolor 0] ; background-color: #[lindex $linecolor 1]"
+ } else {
+ set style ""
+ }
+
+ cgi_table_row class=$class "style=\"$style\"" {
+ if {$u == 0} {
+ cgi_table_data colspan=$colspan height=$indexheight {
+ cgi_put "Data for message $n no longer available"
+ }
+ } else {
+ if {$aggops} {
+ cgi_table_data valign=middle align=center height=$indexheight {
+ if {[WPCmd PEMessage $u select]} {
+ set checked checked
+ } else {
+ set checked ""
+ }
+
+ cgi_checkbox "uidList=$u" $checked class=$class id=cb$u "style=\"$style; margin-left: 16\""
+ }
+ } else {
+ cgi_td height=$indexheight width=2% [cgi_nbspace]
+ }
+
+ set deleted [string index $stat 0]
+ set recent [expr {[string range $stat 0 2] == "010"}]
+
+ foreach part $msg fmt $iformat {width class} $layout {
+
+ set align ""
+ set onclick ""
+
+ switch -exact -- [lindex $fmt 0] {
+ Subject {
+ set parttext ""
+ set leading ""
+ foreach p $part {
+ switch -- [lindex $p 2] {
+ threadinfo {
+ append leading [lindex $p 0]
+ regsub -all {[ ][ ]} $leading $barblank leading
+ regsub -all {[|][-]} $leading $barvertmsg leading
+ regsub -all {[\\][-]} $leading $barmsg leading
+ regsub -all {[|]} $leading $barvert leading
+ }
+ xthreadinfo {
+ set t [lindex $p 0]
+ append leading "$t"
+ for {set i 0} {$i < [string length $t]} {incr i} {
+ switch -- [string index $t $i] {
+ " " {
+ append leading [WPThreadImageLink barblank $indexheight]
+ }
+ ">" {
+ }
+ "-" {
+ if {[lindex $currentsort 1]} {
+ append leading [WPThreadImageLink ibarmsg $indexheight]
+ } else {
+ append leading [WPThreadImageLink barmsg $indexheight]
+ }
+ }
+ "|" {
+ append leading [WPThreadImageLink barvert $indexheight]
+ }
+ }
+ }
+ }
+ default {
+ append parttext [index_part_color [index_quote [lindex $p 0]] [lindex $p 1]]
+ }
+ }
+ }
+
+ if {[string length [set label $parttext]] == 0} {
+ set label {[Empty Subject]}
+ }
+
+ set text [cgi_url $label "fr_view.tcl?&uid=$u&c=[string range $cid 0 5]" target=$vtarget "onClick=return view($u)"]
+
+ if {![info exists do_status_icons] && $deleted} {
+ set text [cgi_span "style=text-decoration: line-through" $text]
+ }
+
+ set text [cgi_buffer {
+ cgi_division "style=\"height: $indexheight; overflow: hidden;\"" {
+ cgi_put "${leading}${text}"
+ }
+ }]
+ }
+ Status {
+ if {[string length [lindex $fmt 2]]} {
+ set text ""
+ foreach i $part {
+ regsub -all { } [lindex $i 0] {\&nbsp;} statstr
+ if {[llength [lindex $i 1]]} {
+ append text [cgi_span "style=background-color: #[lindex [lindex $i 1] 1]; color: #[lindex [lindex $i 1] 0]" $statstr]
+ } else {
+ append text $statstr
+ }
+ }
+
+ set text [cgi_buffer {
+ cgi_division "style=\"font-family: monospace\"" {
+ cgi_put $text
+ }
+ }]
+ } elseif {[info exists do_status_icons]} {
+ set text [WPStatusImg $u]
+ } else {
+ set text [lindex [WPStatusIcon $u gif $statbits] 2]
+ set align align=center
+ }
+
+ if {$flagcmd} {
+ set text [cgi_url $text fr_flags.tcl?uid=$u target=body]
+ }
+ }
+ Size {
+ set text [index_part_color [index_quote [lindex [lindex $part 0] 0]] [lindex $part 1]]
+ set class isize
+ set onclick "onclick=\"flipCheck('cb$u')\""
+ }
+ Number {
+ set text [index_part_color [index_quote [WPcomma [string trim [lindex [lindex $part 0] 0]]]] [lindex $part 1]]
+ set onclick "onclick=\"flipCheck('cb$u')\""
+ }
+ From -
+ To {
+ set t [index_quote [lindex [lindex $part 0] 0]]
+ if {$recent} {
+ set t [cgi_bold $t]
+ }
+
+ set text [cgi_buffer {
+ cgi_division "style=\"height: $indexheight; overflow: hidden;\"" {
+ cgi_put [index_part_color $t [lindex $part 1]]
+ }
+ }]
+
+ set onclick "onclick=\"flipCheck('cb$u')\""
+ }
+ default {
+ set text [index_part_color [index_quote [lindex [lindex $part 0] 0]] [lindex $part 1]]
+ set onclick "onclick=\"flipCheck('cb$u')\""
+ }
+ }
+
+ if {![info exists text]} {
+ set text doh
+ }
+
+ cgi_td $align class="$class" $onclick "$text"
+ }
+
+ if {[info exists use_plus_minus_to_grow_shrink]} {
+ cgi_td [cgi_nbspace]
+ }
+ }
+ }
+ }
+
+ for {} {$linenum <= $ppg} {incr linenum} {
+ cgi_table_row class="[lineclass [expr $linenum + 1]]" {
+ cgi_table_data colspan=$colspan height=$indexheight {
+ cgi_puts [cgi_nbspace]
+ }
+ }
+ }
+ }
+
+ if {[info exists use_bottom_text_to_grow_shrink]} {
+ cgi_table_row height=1 {
+ cgi_table_data bgcolor=#000000 colspan=$colspan {
+ cgi_put [cgi_img [WPimg blackdot] height=1]
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data align=center valign=middle colspan=$colspan class=indexhdr {
+ cgi_put "[WPcomma $ppg] messages are in the list above. Press either "
+ cgi_submit_button growpage=$growverb class=indexhdr "style=\"vertical-align:middle\""
+ cgi_put " or "
+ cgi_submit_button shrinkpage=$shrinkverb class=indexhdr "style=\"vertical-align:middle\""
+ cgi_put " to change this by "
+ cgi_select grownum class=indexhdr "style=\"vertical-align:middle\"" {
+ set growsizes {1 5 10 25 50}
+
+ if {[catch {WPCmd PEInfo set grownum} lastsize]} {
+ set lastsize 0
+ }
+
+ foreach size $growsizes {
+ if {$size == $lastsize} {
+ set sel selected
+ } else {
+ set sel ""
+ }
+
+ cgi_option $size value=$size $sel
+ }
+ }
+ cgi_put "."
+ }
+ }
+ }
+ }
+ }
+
+ cgi_table width=100% cellpadding=0 cellspacing=0 border=0 {
+ cgi_table_row {
+ cgi_table_data align=left class=context {
+ if {[info exists messagesremaining] && $messagesremaining > 0} {
+ if {$messagesremaining > $ppg} {
+ set moretext ", $ppg"
+ } else {
+ set moretext ""
+ }
+
+ if {$zoomed} {
+ set marked " [cgi_bold marked]"
+ } else {
+ set marked ""
+ }
+
+ set nexttext " ([WPcomma $messagesremaining] more${marked} message[WPplural $messagesremaining]${moretext} on [cgi_url next wp.tcl?page=body&bod_next=1 "onClick=return flip('next')"] page)"
+ } else {
+ set nexttext ""
+ }
+
+ cgi_puts "[cgi_nbspace]${counttext}${nexttext}"
+ }
+ cgi_table_data align=right class=context {
+ cgi_put "Powered by [cgi_url Alpine "http://www.washington.edu/alpine/" target="_blank"] - [cgi_copyright] 2007 University of Washington"
+ if {[info exists _wp(ui2dir)]} {
+ cgi_puts " - [cgi_url "Standard Version " "$_wp(serverpath)/$_wp(appdir)/$_wp(ui2dir)/browse" target=_top]"
+ }
+ }
+ }
+ }
+
+ if {[info exists _wp(cumulative)]} {
+ set l [string length $_wp(cumulative)]
+ if {$l < 6} {
+ set sl "."
+ while {$l < 6} {
+ append sl "0"
+ incr l
+ }
+ append sl $_wp(cumulative)
+ } else {
+ set sl "[string range $_wp(cumulative) 0 [expr $l - 7]].[string range $_wp(cumulative) [expr $l - 6] end]"
+ }
+
+ set servlettime "servlet = $sl"
+
+ if {[info exists wp_global_loadtime]} {
+ set clickdiff [expr {[clock clicks] - $wp_global_loadtime}]
+ # 500165 clicks/second
+ set st [expr ([string range $clickdiff 0 [expr [string length $clickdiff] - 4]] * 1000) / 500]
+ set l [string length $st]
+ set scripttime "tcl = [string range $st 0 [expr $l - 4]].[string range $st [expr $l - 3] end], "
+ } else {
+ set scripttime ""
+ }
+
+ cgi_puts [cgi_font size=-2 "style=font-family:arial;font-weight:bold" "\[time: ${scripttime}${servlettime}\]"]
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/ldapbrowse.tcl b/web/cgi/alpine/1.0/ldapbrowse.tcl
new file mode 100755
index 00000000..e8436fd3
--- /dev/null
+++ b/web/cgi/alpine/1.0/ldapbrowse.tcl
@@ -0,0 +1,220 @@
+#!./tclsh
+# $Id: ldapbrowse.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# ldapbrowse.tcl
+#
+# Purpose: CGI script to browse ldap results
+
+# Input: [expected to be set when we get here]
+set browse_vars {
+ {field "Missing Field Name"}
+ {ldapquery "Missing LDAP Query Number"}
+ {addresses "" ""}
+ {oncancel "" ""}
+}
+
+# Output:
+#
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+set ldapres_cmds {
+ {
+ {}
+ {
+ {
+ # * * * * USE ADDRESSES * * * *
+ cgi_submit_button "address=Address" class="navtext"
+ }
+ }
+ }
+ {
+ {}
+ {
+ {
+ # * * * * CANCEL * * * *
+ cgi_submit_button "cancel=Cancel" class="navtext"
+ }
+ }
+ }
+}
+
+WPEval $browse_vars {
+
+ if {[catch {WPCmd PEInfo noop} result]} {
+ error [list _action "No Op" $result]
+ }
+
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ if {$ldapquery != 0} {
+ if {[catch {WPCmd PELdap results $ldapquery} results]} {
+ WPCmd PEInfo statmsg "Some sort of ldap problem"
+ }
+ }
+
+ cgi_head {
+ WPStyleSheets
+ }
+
+ cgi_body BGCOLOR="$_wp(bordercolor)" {
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=post name=ldapaddr target=_top {
+ cgi_text "page=ldappick" type=hidden notab
+ cgi_text "ldapquery=$ldapquery" type=hidden notab
+ cgi_text "field=$field" type=hidden notab
+ cgi_text "addresses=[cgi_unquote_input $addresses]" type=hidden notab
+ cgi_text "cid=[WPCmd PEInfo key]" type=hidden notab
+
+ cgi_table border=0 cellspacing=0 cellpadding=0 width="100%" height="100%" {
+ cgi_table_row {
+ # next comes the menu down the left side
+ eval {
+ cgi_table_data $_wp(menuargs) {
+ WPTFCommandMenu ldapres_cmds {}
+ }
+ }
+
+ cgi_table_data valign=top width=100% class=dialog {
+ if {$ldapquery == 0} {
+ cgi_puts [cgi_italic "No matches found"]
+ } else {
+ cgi_table align=center width="75%" {
+ cgi_table_row {
+ cgi_table_data align=center valign=middle {
+ cgi_br
+ cgi_put "Below are directory entries matching text expanded from your message's $field field. Choose the addressees you want, then Click [cgi_bold Address]."
+ cgi_br
+ cgi_br
+ }
+ }
+ }
+
+ set numboxes 0
+ set srchindex 0
+ cgi_table border=0 cellpadding=0 cellspacing=0 align=center width=96% {
+ foreach searchres $results {
+ set srchstr [lindex $searchres 0]
+ set retdata [lindex $searchres 1]
+ set expstr ""
+ cgi_table_row {
+ cgi_table_data {
+
+ if {$srchstr != ""} {
+ set expstr " for \"[cgi_bold $srchstr]\""
+ }
+
+ cgi_text "str${srchindex}=${srchstr}" type=hidden notab
+ cgi_table border=0 cellspacing=0 cellpadding=1 width=100% {
+ cgi_table_row {
+ cgi_table_data colspan=32 valign=middle height=20 class=ops {
+ cgi_puts "[cgi_nbspace]Directory Search Results${expstr}"
+ }
+ }
+
+ set whitebg 1
+ set nameindex 0
+ set onetruebox 0
+ set numsrchboxes 0
+ foreach litem $retdata {
+ if {[llength [lindex $litem 4]] > 0} {
+ incr numsrchboxes
+ if {$numsrchboxes > 1} {
+ break
+ }
+ }
+ }
+ if {$numsrchboxes == 1} {
+ set onetruebox 1
+ }
+ foreach litem $retdata {
+ set name [lindex $litem 0]
+ set email [lindex $litem 4]
+ set nomail 0
+ if {$whitebg == 1} {
+ set bgcolor #FFFFFF
+ set whitebg 0
+ } else {
+ set bgcolor #EEEEEE
+ set whitebg 1
+ }
+ if {[llength $email] < 1} {
+ incr nameindex
+ continue
+ set nomail 1
+ }
+ cgi_table_row bgcolor=$bgcolor {
+
+ cgi_table_data valign=top nowrap {
+ if {$nomail == 1} {
+ cgi_puts "&nbsp;"
+ } else {
+ if {$onetruebox == 1} {
+ set checked checked
+ } else {
+ set checked ""
+ }
+ cgi_checkbox "ldapList=${srchindex}.${nameindex}" style="background-color:$bgcolor" $checked
+ incr numboxes
+ }
+ }
+
+ cgi_table_data valign=top nowrap {
+ regsub -all "<" $name "\\&lt;" name
+ regsub -all ">" $name "\\&gt;" name
+ cgi_puts "$name"
+ }
+ cgi_table_data valign=top nowrap {
+ if {[llength $email] > 1} {
+ cgi_table width=100% {
+ foreach eaddr $email {
+ cgi_table_row {
+ cgi_table_data {
+ cgi_puts [cgi_font size=-1 "style=font-family:courier_new,monospace" "[cgi_lt]${eaddr}[cgi_gt]"]
+ }
+ }
+ }
+ }
+ } else {
+ if {$nomail == 1} {
+ cgi_puts "\[No email information\]"
+ } else {
+ cgi_puts [cgi_font size=-1 "style=font-family:courier_new,monospace" "[cgi_lt][lindex $email 0][cgi_gt]"]
+ }
+ }
+ }
+ }
+ incr nameindex
+ }
+ }
+
+ cgi_br
+ cgi_br
+ }
+ }
+ incr srchindex
+ }
+ }
+ cgi_text "numboxes=${numboxes}" type=hidden notab
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/ldapentry.tcl b/web/cgi/alpine/1.0/ldapentry.tcl
new file mode 100755
index 00000000..a483f165
--- /dev/null
+++ b/web/cgi/alpine/1.0/ldapentry.tcl
@@ -0,0 +1,315 @@
+#!./tclsh
+# $Id: ldapentry.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# ldapentry.tcl
+#
+# Purpose: CGI script to submit ldap search
+
+# Input:
+set ldap_vars {
+ {dir "Missing Directory Index"}
+ {qn "Missing Query Number"}
+ {si "Missing Search Index"}
+ {ni "Missing Name Index"}
+ {email "" 0}
+}
+
+# Output:
+#
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+
+# Command Menu definition for Message View Screen
+set ldap_menu {
+}
+
+set common_menu {
+ {
+ {}
+ {
+ {
+ # * * * * Ubiquitous INBOX link * * * *
+ if {[string compare inbox [string tolower [WPCmd PEMailbox mailboxname]]]} {
+ cgi_put [cgi_url INBOX open.tcl?folder=INBOX&colid=0&cid=[WPCmd PEInfo key] target=_top class=navbar]
+ } else {
+ cgi_put [cgi_url INBOX fr_main.tcl target=_top class=navbar]
+ }
+ }
+ }
+ }
+ {
+ {}
+ {
+ {
+ # * * * * FOLDER LIST * * * *
+ cgi_puts [cgi_url "Folder List" "wp.tcl?page=folders&cid=[WPCmd PEInfo key]" target=_top class=navbar]
+ }
+ }
+ }
+ {
+ {}
+ {
+ {
+ # * * * * COMPOSE * * * *
+ cgi_puts [cgi_url Compose wp.tcl?page=compose&oncancel=addrbook&cid=[WPCmd PEInfo key] target=_top class=navbar]
+ }
+ }
+ }
+ {
+ {}
+ {
+ {
+ # * * * * RESUME * * * *
+ cgi_puts [cgi_url Resume wp.tcl?page=resume&oncancel=addrbook&cid=[WPCmd PEInfo key] class=navbar]
+ }
+ }
+ }
+ {
+ {}
+ {
+ {
+ # * * * * Addr books * * * *
+ cgi_puts [cgi_url "Address Book" wp.tcl?page=addrbook&oncancel=main.tcl target=_top class=navbar]
+ }
+ }
+ }
+ {{cgi_puts [cgi_nbspace]}}
+ {
+ {}
+ {
+ {
+ # * * * * ldap Query * * * *
+ cgi_puts [cgi_url "Back to Search Results" ldapresult.tcl?dir=${dir}&qn=${qn} class=navbar]
+ }
+ }
+ }
+ {{cgi_puts [cgi_nbspace]}}
+ {
+ {expr {$email > 0}}
+ {
+ {
+ # * * * * Compose To * * * *
+ cgi_puts [cgi_url "Send Mail To This Person" compose.tcl?ldap=1&dir=${dir}&qn=${qn}&si=${si}&ni=${ni}&cid=[WPCmd PEInfo key]&oncancel=addrbook class=navbar]
+ }
+ }
+ }
+ {{cgi_puts [cgi_nbspace]}}
+ {
+ {}
+ {
+ {
+ # * * * * QUIT * * * *
+ cgi_puts [cgi_url "Quit Web Alpine" "$_wp(serverpath)/session/logout.tcl?cid=[WPCmd PEInfo key]&sessid=$sessid class=navbar" target=_top class=navbar]
+ }
+ }
+ }
+}
+
+
+WPEval $ldap_vars {
+
+ if {[catch {WPCmd PEInfo noop} result]} {
+ error [list _action "No Op" $result]
+ }
+
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ if {$qn != 0} {
+ if {[catch {WPCmd PELdap results $qn} results]} {
+ WPCmd PEInfo statmsg "Some sort of ldap problem"
+ }
+ }
+
+ cgi_head {
+ WPStdHtmlHdr "LDAP Entry"
+ WPStyleSheets
+ }
+
+ cgi_body BGCOLOR="$_wp(bordercolor)" {
+ cgi_table border=0 cellspacing=0 cellpadding=2 width="100%" height="100%" {
+ cgi_table_row {
+ #
+ # next comes the menu down the left side
+ #
+ cgi_table_data valign=top rowspan=4 class=navbar {
+ cgi_table bgcolor=$_wp(menucolor) border=0 cellspacing=0 cellpadding=2 {
+ cgi_table_row {
+ cgi_table_data class=navbar style=padding-top:6 {
+ cgi_puts "Current Folder :"
+ cgi_division align=center "style=\"margin-top:4;margin-bottom:4\"" {
+ cgi_put [cgi_url [WPCmd PEMailbox mailboxname] fr_main.tcl target=_top class=navbar]
+ switch -exact -- [WPCmd PEMailbox state] {
+ readonly {
+ cgi_br
+ cgi_put [cgi_span "style=color: pink; font-weight: bold" "(Read Only)"]
+ }
+ closed {
+ cgi_br
+ cgi_put [cgi_span "style=color: pink; font-weight: bold" "(Closed)"]
+ }
+ ok -
+ default {}
+ }
+
+ cgi_br
+ }
+
+ cgi_hr "width=75%"
+ }
+ }
+
+ cgi_table_row {
+ eval {
+ cgi_table_data $_wp(menuargs) class=navbar style=padding-bottom:10 {
+ WPTFCommandMenu ldap_menu common_menu
+ }
+ }
+ }
+ }
+ }
+
+
+ cgi_table_data valign=top align=center class=dialog width=100% {
+ if {$qn == 0} {
+ cgi_puts [cgi_italic "No matches found"]
+ } else {
+ if {[catch {WPCmd PELdap ldapext $qn "${si}.${ni}"} leinfo]} {
+ cgi_br
+ cgi_puts [cgi_italic "Error getting entry: $leinfo"]
+ } else {
+
+ set lehead [lindex $leinfo 0]
+ set ledata [lindex $leinfo 1]
+
+ foreach item $ledata {
+ if {[string compare [string tolower [lindex $item 0]] name] == 0} {
+ set entry_name [lindex [lindex $item 1] 0]
+ break
+ }
+ }
+
+ cgi_division "style=\"padding:10\"" {
+ cgi_puts [cgi_font size=+1 "Directory Entry for \"$entry_name\""]
+ }
+
+ cgi_table border=0 cellspacing=0 cellpadding=0 width=80% "style=\"border: 1px solid goldenrod; padding: 2\"" {
+
+ set bgwhite 1
+ foreach item $ledata {
+ switch -exact -- [string tolower [lindex $item 0]] {
+ name {
+ continue;
+ }
+ voicemailtelephonenumber {
+ set fieldname "Voice Mail"
+ }
+ "email address" {
+ set do_email 1
+ set fieldname [lindex $item 0]
+ }
+ "fax telephone" {
+ set do_fax 1
+ set fieldname [lindex $item 0]
+ }
+ default {
+ set fieldname [lindex $item 0]
+ }
+ }
+
+ set itematt ""
+ if {[llength $item] > 2} {
+ set itematt [lindex $item 2]
+ }
+ if {$itematt == "objectclass"} {
+ set vals [lindex $item 1]
+ continue
+ }
+
+ if {$bgwhite == 1} {
+ set bgcolor #ffffff
+ set bgwhite 0
+ } else {
+ set bgcolor #eeeeee
+ set bgwhite 1
+ }
+
+ set vals [lindex $item 1]
+
+ cgi_table_row bgcolor=$bgcolor {
+ cgi_table_data width=25% nowrap valign=top rowspan=[llength $vals] {
+ cgi_division "style=\"padding-top:2\"" {
+ cgi_puts [cgi_bold $fieldname]
+ }
+ }
+
+ cgi_table_data rowspan=[llength $vals] {
+ cgi_puts [cgi_img [WPimg dot2] width=8]
+ }
+
+ cgi_table_data height=20px {
+ if {[info exists do_fax]} {
+ set n {[0-9]}
+ set n3 $n$n$n
+ set n4 $n$n$n$n
+ if {[regexp "^\\\+1 ($n3) ($n3)-($n4)\$" [lindex $vals 0] dummy areacode prefix number] && [lsearch -exact {206 425} $areacode] >= 0} {
+ cgi_puts [cgi_url [lindex $vals 0] compose.tcl?ldap=1&fax=yes&dir=${dir}&qn=${qn}&si=${si}&ni=${ni}&cid=[WPCmd PEInfo key]&oncancel=addrbook]
+ } else {
+ cgi_puts [lindex $vals 0]
+ }
+
+ unset do_fax
+ } elseif {[info exists do_email]} {
+ cgi_puts [cgi_url [cgi_font size=-1 face=courier [lindex $vals 0]] compose.tcl?ldap=1&dir=${dir}&qn=${qn}&si=${si}&ni=${ni}&ei=0&cid=[WPCmd PEInfo key]&oncancel=addrbook]
+ } else {
+ cgi_puts [lindex $vals 0]
+ }
+
+ set extrarows [lrange $vals 1 end]
+ }
+ }
+
+ if {[info exists extrarows]} {
+ cgi_table_row bgcolor=$bgcolor {
+ set ei 0
+ foreach extra $extrarows {
+ cgi_table_data height=20px {
+ if {[info exists do_email]} {
+ cgi_puts [cgi_url [cgi_font size=-1 face=courier $extra] compose.tcl?ldap=1&dir=${dir}&qn=${qn}&si=${si}&ni=${ni}&ei=[incr ei]&cid=[WPCmd PEInfo key]&oncancel=addrbook]
+ } else {
+ cgi_puts $extra
+ }
+ }
+ }
+ }
+
+ unset extrarows
+ }
+
+ catch {unset do_email}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
diff --git a/web/cgi/alpine/1.0/ldappick.tcl b/web/cgi/alpine/1.0/ldappick.tcl
new file mode 100755
index 00000000..4a485266
--- /dev/null
+++ b/web/cgi/alpine/1.0/ldappick.tcl
@@ -0,0 +1,76 @@
+# $Id: ldappick.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# ldappick.tcl
+#
+# Purpose: CGI script to handle LDAP result choices from the
+# via the LDAP result browser generated form
+
+# Input:
+set pick_vars {
+ {cid "Missing Command ID"}
+ {field "Missing Field Name"}
+ {ldapquery "Missing LDAP Query"}
+ {ldapList "" ""}
+ {addresses "" ""}
+ {addrop {} ""}
+ {cancel {} 0}
+}
+
+# Output:
+#
+
+# read vars
+foreach item $pick_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} result]} {
+ error [list _action "Impart Variable" $result]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+if {$cid != [WPCmd PEInfo key]} {
+ catch {WPCmd PEInfo statmsg "Invalid Command ID"}
+}
+
+if {$cancel != 1 && !([string compare cancel [string tolower $cancel]] == 0 || [string compare cancel [string tolower $addrop]] == 0) && [string length $ldapList] > 0} {
+ set ldapListStr [join $ldapList ","]
+ if {[catch {WPCmd PELdap setaddrs $ldapquery $ldapListStr $addresses 0} newaddrlist]} {
+ WPCmd PEInfo statmsg "LDAP Error: $newaddrlist"
+ } else {
+ regsub -all "'" $newaddrlist "\\'" newaddrs
+
+ if {[catch {WPCmd PEInfo set suspended_composition} msgdata]} {
+ WPCmd PEInfo statmsg "Cannot read message data: $msgdata"
+ } else {
+ if {[info exists newaddrs]} {
+ for {set i 0} {$i < [llength $msgdata]} {incr i} {
+ set orig_field [lindex [lindex $msgdata $i] 0]
+ regsub -all -- - [string tolower $orig_field] _ fn
+
+ if {[string compare $fn $field] == 0} {
+ set msgdata [lreplace $msgdata $i $i [list $orig_field $newaddrs]]
+ break
+ }
+ }
+
+ if {[catch {WPCmd PEInfo set suspended_composition $msgdata} result]} {
+ WPCmd PEInfo statmsg "Cannot Update $field field: $result"
+ }
+ }
+ }
+ }
+}
+
+source [WPTFScript compose]
diff --git a/web/cgi/alpine/1.0/ldapquery.tcl b/web/cgi/alpine/1.0/ldapquery.tcl
new file mode 100755
index 00000000..c9ca6936
--- /dev/null
+++ b/web/cgi/alpine/1.0/ldapquery.tcl
@@ -0,0 +1,144 @@
+#!./tclsh
+# $Id: ldapquery.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# ldapquery.tcl
+#
+# Purpose: CGI script to handle ldap query
+
+# Input:
+set ldap_vars {
+ {dir "Missing Directory Index"}
+ {srchstr {} ""}
+ {field {} ""}
+ {op {} ""}
+ {searchtype {} ""}
+}
+
+# Output:
+#
+# HTML/Javascript/CSS data representing the message specified
+# by the 'uid' argument
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+
+set ldap_menu {
+}
+
+set common_menu {
+ {
+ {}
+ {
+ {
+ # * * * * Ubiquitous INBOX link * * * *
+ if {[string compare inbox [string tolower [WPCmd PEMailbox mailboxname]]]} {
+ cgi_put [cgi_url INBOX open.tcl?folder=INBOX&colid=0&cid=[WPCmd PEInfo key] target=_top class=navbar]
+ } else {
+ cgi_put [cgi_url INBOX fr_main.tcl target=_top class=navbar]
+ }
+ }
+ }
+ }
+ {
+ {}
+ {
+ {
+ # * * * * FOLDER LIST * * * *
+ cgi_puts [cgi_url "Folder List" "wp.tcl?page=folders&cid=[WPCmd PEInfo key]" target=_top class=navbar]
+ }
+ }
+ }
+ {
+ {}
+ {
+ {
+ # * * * * COMPOSE * * * *
+ cgi_puts [cgi_url Compose wp.tcl?page=compose&oncancel=addrbook&cid=[WPCmd PEInfo key] target=_top class=navbar]
+ }
+ }
+ }
+ {
+ {}
+ {
+ {
+ # * * * * RESUME * * * *
+ cgi_puts [cgi_url Resume wp.tcl?page=resume&oncancel=addrbook&cid=[WPCmd PEInfo key] class=navbar]
+ }
+ }
+ }
+ {
+ {}
+ {
+ {
+ # * * * * Addr books * * * *
+ cgi_puts [cgi_url "Address Book" wp.tcl?page=addrbook&oncancel=main.tcl target=_top class=navbar]
+ }
+ }
+ }
+ {{cgi_puts [cgi_nbspace]}}
+ {
+ {}
+ {
+ {
+ # * * * * HELP * * * *
+ cgi_puts [cgi_url "Get Help" "wp.tcl?page=help&oncancel=addrbook" target=_top class=navbar]
+ }
+ }
+ }
+ {{cgi_puts [cgi_nbspace]}}
+ {
+ {}
+ {
+ {
+ # * * * * QUIT * * * *
+ cgi_puts [cgi_url "Quit Web Alpine" "$_wp(serverpath)/session/logout.tcl?cid=[WPCmd PEInfo key]&sessid=$sessid class=navbar" target=_top class=navbar]
+ }
+ }
+ }
+}
+
+
+WPEval $ldap_vars {
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ cgi_http_equiv Refresh "0; url=$_wp(serverpath)/$_wp(appdir)/$_wp(ui1dir)/ldapresult.tcl?dir=${dir}&srchstr=[WPPercentQuote ${srchstr}]&field=${field}&op=${op}&searchtype=${searchtype}&sessid=${sessid}"
+ }
+
+ cgi_body BGCOLOR="$_wp(bordercolor)" {
+
+ cgi_table border=0 cellspacing=0 cellpadding=2 width="100%" height="100%" {
+ cgi_table_row {
+ cgi_table_data valign=top rclass=navbar {
+ WPTFCommandMenu {} {}
+ }
+
+ cgi_table_data valign=top bgcolor=#ffffff width=100% {
+ cgi_table border=0 width=500 cellpadding=3 {
+ cgi_table_row {
+ cgi_table_data align=center "style=\"padding-top:120px\"" {
+ cgi_put "[cgi_font "style=\"font-family: arial, sans-serif; font-size:18pt; font-weight: bold\"" "Searching Directory "][cgi_img [WPimg dotblink]]"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/ldapresult.tcl b/web/cgi/alpine/1.0/ldapresult.tcl
new file mode 100755
index 00000000..efdce665
--- /dev/null
+++ b/web/cgi/alpine/1.0/ldapresult.tcl
@@ -0,0 +1,375 @@
+#!./tclsh
+# $Id: ldapresult.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# ldapquery.tcl
+#
+# Purpose: CGI script to submit ldap search
+
+# Input:
+set ldap_vars {
+ {dir "Missing Directory Index"}
+ {srchstr {} ""}
+ {field {} ""}
+ {op {} ""}
+ {searchtype {} ""}
+ {qn {} -1}
+}
+
+# Output:
+#
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+# Command Menu definition for Message View Screen
+set ldap_menu {
+}
+
+set common_menu {
+ {
+ {}
+ {
+ {
+ # * * * * Ubiquitous INBOX link * * * *
+ if {[string compare inbox [string tolower [WPCmd PEMailbox mailboxname]]]} {
+ cgi_put [cgi_url INBOX open.tcl?folder=INBOX&colid=0&cid=[WPCmd PEInfo key] target=_top class=navbar]
+ } else {
+ cgi_put [cgi_url INBOX fr_main.tcl target=_top class=navbar]
+ }
+ }
+ }
+ }
+ {
+ {}
+ {
+ {
+ # * * * * FOLDER LIST * * * *
+ cgi_puts [cgi_url "Folder List" "wp.tcl?page=folders&cid=[WPCmd PEInfo key]" target=_top class=navbar]
+ }
+ }
+ }
+ {
+ {}
+ {
+ {
+ # * * * * COMPOSE * * * *
+ cgi_puts [cgi_url Compose wp.tcl?page=compose&oncancel=addrbook&cid=[WPCmd PEInfo key] target=_top class=navbar]
+ }
+ }
+ }
+ {
+ {}
+ {
+ {
+ # * * * * RESUME * * * *
+ cgi_puts [cgi_url Resume wp.tcl?page=resume&oncancel=addrbook&cid=[WPCmd PEInfo key] class=navbar]
+ }
+ }
+ }
+ {
+ {}
+ {
+ {
+ # * * * * Addr books * * * *
+ cgi_puts [cgi_url "Address Book" wp.tcl?page=addrbook&oncancel=main.tcl target=_top class=navbar]
+ }
+ }
+ }
+ {{cgi_puts [cgi_nbspace]}}
+ {
+ {}
+ {
+ {
+ # * * * * HELP * * * *
+ cgi_puts [cgi_url "Get Help" "wp.tcl?page=help&oncancel=addrbook" target=_top class=navbar]
+ }
+ }
+ }
+ {{cgi_puts [cgi_nbspace]}}
+ {
+ {}
+ {
+ {
+ # * * * * QUIT * * * *
+ cgi_puts [cgi_url "Quit Web Alpine" "$_wp(serverpath)/session/logout.tcl?cid=[WPCmd PEInfo key]&sessid=$sessid class=navbar" target=_top class=navbar]
+ }
+ }
+ }
+}
+
+
+WPEval $ldap_vars {
+
+ if {[catch {WPCmd PEInfo noop} result]} {
+ error [list _action "No Op" $result]
+ }
+
+ if {$qn == -1} {
+ set ldapfilt ""
+ set numfields 0
+ if {$searchtype == 1} {
+ set srchstr ""
+ source ldapadvsrch.tcl
+ foreach item $ldap_advanced_search {
+ WPLoadCGIVarAs [lindex $item 1] tmpval
+ regsub {^ *([^ ]|[^ ].*[^ ]) *$} $tmpval "\\1" tmpval
+ if {$tmpval != ""} {
+ set ldapfilt "${ldapfilt}([lindex $item 2]=${tmpval})"
+ incr numfields
+ }
+ }
+ if {$numfields > 1} {
+ set ldapfilt "(&${ldapfilt})"
+ }
+ }
+ }
+
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "LDAP Query Result"
+ WPStyleSheets
+ cgi_puts "<style type='text/css'>"
+ cgi_puts ".gradient { background-image: url('[WPimg indexhdr]') ; background-repeat: repeat-x }"
+ cgi_puts "</style>"
+
+ if {$_wp(keybindings)} {
+ set kequiv {
+ {{i} {top.location = 'fr_main.tcl'}}
+ {{l} {top.location = 'wp.tcl?page=folders'}}
+ {{?} {top.location = 'wp.tcl?page=help&oncancel=addrbook'}}
+ }
+
+ lappend kequiv
+
+ set onload "onLoad=[WPTFKeyEquiv $kequiv]"
+ } else {
+ set onload ""
+ }
+ }
+
+ cgi_body BGCOLOR="$_wp(bordercolor)" $onload {
+
+ cgi_table border=0 cellspacing=0 cellpadding=2 width="100%" height="100%" {
+ cgi_table_row {
+ #
+ # next comes the menu down the left side
+ #
+ cgi_table_data valign=top rowspan=4 class=navbar {
+ cgi_table bgcolor=$_wp(menucolor) border=0 cellspacing=0 cellpadding=2 {
+ cgi_table_row {
+ cgi_table_data class=navbar style=padding-top:6 {
+ cgi_puts "Current Folder :"
+ cgi_division align=center "style=\"margin-top:4;margin-bottom:4\"" {
+ cgi_put [cgi_url [WPCmd PEMailbox mailboxname] fr_main.tcl target=_top class=navbar]
+ switch -exact -- [WPCmd PEMailbox state] {
+ readonly {
+ cgi_br
+ cgi_put [cgi_span "style=color: pink; font-weight: bold" "(Read Only)"]
+ }
+ closed {
+ cgi_br
+ cgi_put [cgi_span "style=color: pink; font-weight: bold" "(Closed)"]
+ }
+ ok -
+ default {}
+ }
+
+ cgi_br
+ }
+
+ cgi_hr "width=75%"
+ }
+ }
+
+ # next comes the menu down the left side, with suitable
+ cgi_table_row {
+ eval {
+ cgi_table_data $_wp(menuargs) class=navbar style=padding-bottom:10 {
+ WPTFCommandMenu ldap_menu common_menu
+ }
+ }
+ }
+ }
+ }
+
+ cgi_table_data valign=top align=center class=dialog width=100% {
+
+ if {$qn == -1 && [catch {WPCmd PELdap query $dir $srchstr $ldapfilt} qn]} {
+ cgi_division align=center "style=\"background-color:white; border: 1px solid goldenrod; margin: 10; padding: 4\"" {
+ cgi_puts "A problem has occured while trying to search the directory server."
+ cgi_br
+ cgi_br
+ cgi_puts [cgi_italic [cgi_bold "$qn"]]
+ cgi_br
+ cgi_br
+ cgi_puts "Try searching again by clicking [cgi_url "Address Book" wp.tcl?page=addrbook target=_top] at the left."
+ }
+ } elseif {$qn == 0} {
+ cgi_division align=center "style=\"background-color:white; border: 1px solid goldenrod; margin: 10; padding: 4\"" {
+ cgi_puts [cgi_bold "No matches for \"$srchstr\" found."]
+ cgi_br
+ cgi_br
+ cgi_puts "You can try another search below, or click a link at the left to continue your WebPine session."
+ cgi_br
+ cgi_br
+
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=post enctype=multipart/form-data name=ldapsearch target=_top {
+ cgi_text "sessid=$_wp(sessid)" type=hidden notab
+ cgi_text "page=ldapquery" type=hidden notab
+ cgi_text "searchtype=0" type=hidden notab
+ cgi_text "dir=$dir" type=hidden notab
+
+ cgi_puts "Search Directory :"
+ cgi_text "srchstr=${srchstr}" size=35
+ cgi_submit_button "search=Search"
+ }
+
+ cgi_br
+ cgi_br
+ }
+ } elseif {[catch {WPCmd PELdap results $qn} results]} {
+ cgi_division align=center "style=\"background-color:white; border: 1px solid goldenrod; margin: 10; padding: 4\"" {
+ cgi_puts "A problem has occured while trying to retrieve the results of your directory search."
+ cgi_br
+ cgi_br
+ cgi_puts [cgi_italic [cgi_bold "$results"]]
+ cgi_br
+ cgi_br
+ cgi_puts "Try searching again by clicking [cgi_url "Address Book" wp.tcl?page=addrbook target=_top] at the left."
+ }
+ } else {
+ set numboxes 0
+ set srchindex 0
+ cgi_table border=0 cellpadding=0 cellspacing=10 width=100% {
+
+ foreach searchres $results {
+ set srchstr [lindex $searchres 0]
+ set retdata [lindex $searchres 1]
+ set expstr ""
+ cgi_table_row {
+ cgi_table_data valign=middle align=center {
+
+ if {$srchstr != ""} {
+ set expstr " for \"[cgi_bold $srchstr]\""
+ }
+
+ cgi_puts [cgi_font size=+1 "Directory Search Results${expstr}"]
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data {
+ cgi_table border=0 bgcolor=white cellspacing=0 cellpadding=0 width=90% "style=\"border: 1px solid goldenrod; padding: 1\"" {
+ set whitebg 1
+ set nameindex 0
+ set onetruebox 0
+ set numsrchboxes 0
+ foreach litem $retdata {
+ if {[llength [lindex $litem 4]] > 0} {
+ incr numsrchboxes
+ if {$numsrchboxes > 1} {
+ break
+ }
+ }
+ }
+ if {$numsrchboxes == 1} {
+ set onetruebox 1
+ }
+
+ cgi_table_row class=\"gradient\" {
+ cgi_table_data align=left class=indexhdr colspan=2 {
+ cgi_put "Full Name"
+ }
+ cgi_table_data align=left class=indexhdr height=30 {
+ cgi_put "Address (Click to Compose To)"
+ }
+ }
+
+ foreach litem $retdata {
+ set name [lindex $litem 0]
+ set email [lindex $litem 4]
+ set nomail 0
+ if {$whitebg == 1} {
+ set bgcolor #FFFFFF
+ set whitebg 0
+ } else {
+ set bgcolor #EEEEEE
+ set whitebg 1
+ }
+
+ if {[llength $email] < 1} {
+ set nomail 1
+ }
+
+ if {[llength $email]} {
+ set rowspan rowspan=[llength $email]
+ } else {
+ set rowspan ""
+ }
+
+ cgi_table_row bgcolor=$bgcolor {
+
+ cgi_table_data valign=top nowrap $rowspan {
+ regsub -all "<" $name "\\&lt;" name
+ regsub -all ">" $name "\\&gt;" name
+ # cgi_puts "$name"
+ cgi_puts "[WPurl "ldapentry.tcl?dir=${dir}&qn=${qn}&si=${srchindex}&ni=${nameindex}&email=[llength $email]" "" "$name" ""]"
+ }
+
+ cgi_table_data $rowspan {
+ cgi_puts [cgi_img [WPimg dot2] width=8]
+ }
+
+ cgi_table_data nowrap height=20px {
+ if {[llength $email] >= 1} {
+ set extrarows [lrange $email 1 end]
+ cgi_put [cgi_url [cgi_font size=-1 face=courier [lindex $email 0]] compose.tcl?ldap=1&dir=${dir}&qn=${qn}&si=${srchindex}&ni=${nameindex}&ei=0&cid=[WPCmd PEInfo key]&oncancel=addrbook]
+ } else {
+ cgi_puts [cgi_italic "&lt;No email information&gt;"]
+ }
+ }
+ }
+
+ if {[info exists extrarows] && [llength $extrarows]} {
+ cgi_table_row bgcolor=$bgcolor {
+ set ei 0
+ foreach extra $extrarows {
+ cgi_table_data height=20px {
+ cgi_put [cgi_url [cgi_font size=-1 face=courier $extra] compose.tcl?ldap=1&dir=${dir}&qn=${qn}&si=${srchindex}&ni=${nameindex}&ei=0&cid=[WPCmd PEInfo key]&oncancel=addrbook]
+ }
+ }
+ }
+
+ unset extrarows
+ }
+
+ incr nameindex
+ }
+ }
+ }
+ }
+ incr srchindex
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/main.tcl b/web/cgi/alpine/1.0/main.tcl
new file mode 100755
index 00000000..24881868
--- /dev/null
+++ b/web/cgi/alpine/1.0/main.tcl
@@ -0,0 +1,84 @@
+# $Id: main.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+
+# Input:
+
+# Output:
+#
+
+proc save_hack {} {
+ if {[catch {WPImport f_name "x"}] == 0 && [catch {WPImport f_colid "x"}] == 0} {
+ append parms "&f_name=${f_name}&f_colid=${f_colid}"
+
+ if {[catch {WPImport send "x"}] == 0} {
+ append parms "&send=${send}"
+ }
+
+ return $parms
+ }
+
+ error "not saving"
+}
+
+cgi_http_head {
+ WPStdHttpHdrs
+ WPExportCookie sessid "$_wp(sessid)@[info hostname]" $_wp(appdir)/$_wp(ui1dir)
+}
+
+cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "WebPine"
+ }
+
+ cgi_frameset "cols=112,*" frameborder=0 framespacing=0 {
+ cgi_frame gen=common.tcl?m=[WPCmd PEMailbox mailboxname]&c=[WPCmd PEInfo key]&v=[WPScriptVersion common]&q=[WPCmd PEInfo feature quit-without-confirm] title="Navigation Commands"
+
+ if {[catch {WPCmd PEInfo set wp_spec_script} script]} {
+ set script fr_index.tcl
+ }
+
+ set parms ""
+
+ if {[info exists frame_vars]} {
+ foreach v $frame_vars {
+ if {[string length [subst $[lindex $v 0]]]} {
+ append parms "&[lindex $v 0]=[subst $[lindex $v 0]]"
+ }
+ }
+ }
+
+ switch -regexp $script {
+ ^fr_view.tcl$ {
+ if {[catch {save_hack} x] == 0} {
+ append parms "&$x"
+ }
+
+ if {[catch {WPCmd PEInfo set uid} uid] == 0} {
+ append parms "&uid=$uid"
+ }
+
+ if {[catch {WPCmd PEInfo set op} op] == 0} {
+ append parms "&op=$op"
+ }
+ }
+ ^fr_index.tcl$ -
+ ^fr_main.tcl$ {
+ if {[catch {save_hack} x] == 0} {
+ append parms "&$x"
+ }
+ }
+ }
+
+ cgi_frame spec=${script}?c=[WPCmd PEInfo key]${parms} frameborder=0 title="Message List and View"
+ }
+}
diff --git a/web/cgi/alpine/1.0/open.tcl b/web/cgi/alpine/1.0/open.tcl
new file mode 100755
index 00000000..2add6db5
--- /dev/null
+++ b/web/cgi/alpine/1.0/open.tcl
@@ -0,0 +1,33 @@
+#!./tclsh
+# $Id: open.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# open.tcl
+#
+# Purpose: CGI script to perform folder opening via folders.tcl
+
+# Input:
+set open_vars {
+ {cid "Missing Command ID"}
+ {oncancel "" "folders"}
+}
+
+# Output:
+#
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+WPEval $open_vars {
+ source do_open.tcl
+}
diff --git a/web/cgi/alpine/1.0/post.tcl b/web/cgi/alpine/1.0/post.tcl
new file mode 100755
index 00000000..dcd7c37b
--- /dev/null
+++ b/web/cgi/alpine/1.0/post.tcl
@@ -0,0 +1,651 @@
+# $Id: post.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# post.tcl
+#
+# Purpose: CGI script to perform message posting via compose.tcl
+# generated form
+
+# Input:
+set post_vars {
+ {cid "Missing Command ID"}
+ {action {} ""}
+ {send {} 0}
+ {postpone {} 0}
+ {cancel {} 0}
+ {check {} 0}
+ {br_to {} 0}
+ {br_cc {} 0}
+ {br_bcc {} 0}
+ {br_reply_to {} 0}
+ {br_fcc {} 0}
+ {ex_to {} ""}
+ {ex_cc {} ""}
+ {ex_bcc {} ""}
+ {ex_reply_to {} ""}
+ {sendop {} ""}
+ {queryattach {} 0}
+ {attach {} 0}
+ {detach {} 0}
+ {extrahdrs {} ""}
+ {help {} ""}
+ {postpost {} "main.tcl"}
+ {fccattach {} 0}
+ {form_charset {} ""}
+ {form_flowed {} ""}
+}
+
+# NOT Input
+catch {
+ unset src
+}
+
+# Output:
+#
+
+proc fieldname {name} {
+ regsub -all -- {-} [string tolower $name] {_} fieldname
+ return $fieldname
+}
+
+proc expand_address_field {field _msgdata} {
+ global has_fcc
+
+ upvar 1 $_msgdata msgdata
+
+ set fn [fieldname $field]
+ for {set i 0} {$i < [llength $msgdata]} {incr i} {
+ if {[string length [lindex [lindex $msgdata $i] 1]]} {
+ set fld [lindex $msgdata $i]
+ if {[string compare [fieldname [lindex $fld 0]] $fn] == 0} {
+ if {[catch {WPCmd PEAddress expand [lindex $fld 1] fcc} expaddr]} {
+ WPCmd PEInfo statmsg "Can't expand $field: $expaddr"
+ } else {
+ if {[lindex $expaddr 1] != 0} {
+ if {[catch {WPCmd PEInfo set suspended_composition $msgdata} errstr] == 0} {
+ # addresses and ldapaddrs should be set at this point
+ upvar 1 addresses a
+ upvar 1 ldapquery l
+ upvar 1 field f
+ set a [lindex $expaddr 0]
+ set l [lindex $expaddr 1]
+ set f $fn
+ return 1
+ } else {
+ # else fall thru back into composer
+ WPCmd PEInfo statmsg "Compose Error: $errstr"
+ break
+ }
+ } elseif {[string compare [lindex $expaddr 0] [lindex $fld 1]]} {
+ set msgdata [lreplace $msgdata $i $i [list [lindex $fld 0] [lindex $expaddr 0]]]
+
+ # set fcc?
+ set fccfn [lindex $expaddr 2]
+ set fccdef [WPCmd PECompose fccdefault]
+ if {[string compare to [string tolower $fn]] == 0 && [string length $fccfn]
+ && (![info exists has_fcc] || 0 == [string compare [lindex $fccdef 1] [lindex $has_fcc 1]])} {
+ for {set j 0} {$j < [llength $msgdata]} {incr j} {
+ if {[string compare fcc [fieldname [lindex [lindex $msgdata $j] 0]]] == 0} {
+ set fcc_index $j
+ break
+ }
+ }
+
+ set colid [lindex $fccdef 0]
+ if {[info exists fcc_index]} {
+ if {[string compare $fccfn [lindex [lindex [lindex $msgdata $fcc_index] 1] 1]]} {
+ lappend msgdata [list postoption [list fcc-set-by-addrbook 1]]
+ }
+
+ set msgdata [lreplace $msgdata $fcc_index $fcc_index [list Fcc [list $colid $fccfn]]]
+ } else {
+ lappend msgdata [list Fcc [list $colid $fccfn]]
+ lappend msgdata [list postoption [list fcc-set-by-addrbook 1]]
+ }
+
+ set has_fcc [list $colid $fccfn]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return 0
+}
+
+proc chartest_value {entity} {
+ global _cgi
+
+ if {[catch {cgi_import_as ke_${entity} tc}] == 0} {
+ set tcval ""
+ if {[set j [string length $tc]]} {
+ for {set i 0} {$i < $j} {incr i} {
+ binary scan [string index $tc $i] c x
+ set x [expr ($x & 0xff)]
+ lappend tcval [format {%o} $x]
+ }
+ }
+
+ return $tcval
+ } else {
+ error "Unset testchar_$entity"
+ }
+}
+
+## read vars
+foreach item $post_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} errstr]} {
+ error [list _action "Impart Variable" $errstr]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+if {$cid != [WPCmd PEInfo key]} {
+ error [list _action Postpone "Invalid Operation ID" "Click Back button to try again."]
+}
+
+# collect message data
+
+# For now the input headers have to match the postheaders
+# list. Any outside the list are ignored (and probably should
+# be to avoid hostile input). Note, postheaders is a
+# super-set of composeheaders as not all headers are meant
+# to be shown the user for composition
+if {[catch {WPCmd PECompose userhdrs} headers]} {
+ error [list _action "User Headers" $headers "Click browser's Back button to try again."]
+}
+
+if {[catch {WPCmd PECompose syshdrs} otherhdrs]} {
+ error [list _action "System Headers" $otherhdrs "Click browser's Back button to try again."]
+} else {
+ eval "lappend headers $otherhdrs"
+}
+
+foreach field $headers {
+ set hdr [string tolower [lindex $field 0]]
+ regsub -all -- {-} $hdr {_} hdr
+ WPLoadCGIVarAs $hdr val
+ switch -- $hdr {
+ attach {
+ # disregard: u/i convenience (attachments marshalled below)
+ }
+ fcc {
+ if {[string length $val]} {
+ WPLoadCGIVar colid
+ set has_fcc [list $colid $val]
+ lappend msgdata [list Fcc $has_fcc]
+ }
+ }
+ default {
+ if {[string length $val] || [lsearch -exact {subject} $hdr] >= 0} {
+ set hdrvals($hdr) $val
+ lappend msgdata [list [lindex $field 0] $val]
+ if {[lsearch -exact {to cc bcc} $hdr] >= 0} {
+ set has_$hdr 1
+ }
+ }
+ }
+ }
+}
+
+if {[info exists env(REMOTE_ADDR)]} {
+ lappend msgdata [list x-auth-received "from \[$env(REMOTE_ADDR)\] by [info hostname] via HTTP; [clock format [clock seconds] -format "%a, %d %b %Y %H:%M:%S %Z"]"]
+}
+
+if {[catch {cgi_import attachments}] == 0} {
+ foreach id [split $attachments ","] {
+ lappend msgdata [list attach $id]
+ }
+}
+
+WPLoadCGIVar body
+lappend msgdata [list body [split $body "\n"]]
+
+
+switch -exact -- $fccattach {
+ 0 -
+ 1 {
+ lappend msgdata [list postoption [list fcc-without-attachments [expr {!$fccattach}]]]
+ }
+}
+
+# pass on form's charset?
+# TURNED OFF since all compose form interaction BETTER be UTF-8
+if {0 && [string length $form_charset]} {
+ # messy charset heuristics
+ # idea is to look for planted HTML entities and see if known
+ # encoding transliterations have occured. inspired by:
+ # <http://www.cs.tut.fi/~jkorpela/chars.html#encinfo>
+
+ # test for:
+ # entity values
+ # euro (#8364)
+ # cyrillic shcha (#1060)
+ # iso-8859-15 (Latin0): euro IS 200
+ # iso-8859-1 (Latin1): thorn IS 376 or U+C3BE BUT NOT &#8220; &#254; OR &thorn;
+ # Unicode literal full width yen: U+FFE5 IS 215F (ISO-2022-JP), A1EF (EUC-JP), or 818F (Shift-JIS) and so on
+
+ # remember, the first element of each group MUST appear in compose.tcl, too
+ set cstests {}
+ set xcstests {
+ {#8364 {{{40 254} ISO-10646} {{342 202 254} UTF-8} {244 ISO-8859-15} {325 IBM-850}} {}}
+ {#1066 {{{377} KOI8-R} {312 ISO-8859-5}} {}}
+ {thorn {{376 ISO-8859-1}} {{303 276} UTF-8} {iso-8859-1 {{46 43 70 62 62 60 73} {46 43 62 65 64 73} {46 164 150 157 162 156 73}}}}
+ {tcedil {{376 ISO-8859-2}} {{46 164 143 145 144 151 154 73}}}
+ {#65509 {{{302 245} UTF-8} {{241 315} EUC-KR} {{243 244} GB2312} {{242 104} BIG5} {{241 357} EUC-JP} {{201 217} Shift-JIS} {{33 44 102 41 157 33 50 102} ISO-2022-JP}} {}}
+ }
+
+ catch {unset test_charset}
+ foreach cs $cstests {
+ # asked for test entity available?
+ if {[catch {chartest_value [lindex $cs 0]} ctest] == 0} {
+ # test for positive [re]encoding assertions
+ foreach testpos [lindex $cs 1] {
+ if {[regexp "^[lindex $testpos 0]\$" $ctest]} {
+ set test_charset [lindex $testpos 1]
+ break
+ }
+ }
+
+ if {![info exists test_charset]} {
+ set csneg [lindex [lindex $cs 2] 0]
+ foreach testneg [lindex [lindex $cs 2] 1] {
+ if {[regexp "^$testneg\$" $ctest]} {
+ if {[info exists form_charset]
+ && [string compare [string tolower $form_charset] $csneg] == 0} {
+ unset form_charset
+ break
+ }
+ }
+ }
+ } else {
+ break
+ }
+ }
+ }
+
+ if {[info exists test_charset]} {
+ lappend msgdata [list postoption [list charset $test_charset]]
+ } elseif {[info exists form_charset]} {
+ lappend msgdata [list postoption [list charset $form_charset]]
+ } else {
+ lappend msgdata [list postoption [list charset "X-UNKNOWN"]]
+ }
+} else {
+ lappend msgdata [list postoption [list charset "UTF-8"]]
+}
+
+# pass on text fomat=flowed?
+if {[string length $form_flowed]} {
+ lappend msgdata [list postoption [list flowed yes]]
+}
+
+# figure out what to do with data
+if {[string compare OK [string trim $action]] == 0 && ($send || [string compare $sendop send] == 0)} {
+ if {[info exists has_to] || [info exists has_cc] || [info exists has_bcc] || [info exists has_fcc]} {
+ # expand any nicknames
+ if {[catch {
+ set fccdef [WPCmd PECompose fccdefault]
+ for {set i 0} {$i < [llength $msgdata]} {incr i} {
+ if {[string length [lindex [lindex $msgdata $i] 1]]} {
+ set fld [lindex $msgdata $i]
+ set fn [string tolower [lindex $fld 0]]
+ switch -- $fn {
+ [Ff]cc {
+ if {[string length [lindex [lindex $fld 1] 1]]} {
+ # setup for send confirmation
+ set colidval [lindex [lindex $fld 1] 0]
+ set fccval [lindex [lindex $fld 1] 1]
+ }
+ }
+ to -
+ cc -
+ bcc -
+ reply-to {
+ set expaddr [WPCmd PEAddress expand [lindex $fld 1] {}]
+ if {[string compare [lindex $expaddr 0] [lindex $fld 1]]} {
+ set msgdata [lreplace $msgdata $i $i [list [lindex $fld 0] [lindex $expaddr 0]]]
+
+ # if expanded, update fcc?
+ if {[string compare to $fn] == 0 && [string length $fn]} {
+ set expanded_fcc [lindex $expaddr 2]
+ }
+ }
+ }
+ body {
+ if {[string length $form_flowed]} {
+ set ws "\[ \t]"
+ set nws "\[^ \t]"
+
+ set nextline [lindex [lindex $fld 1] 0]
+ for {set j 1} {$j <= [llength [lindex $fld 1]]} {incr j} {
+ set line $nextline
+ # space stuff?
+ if {[regexp "^${ws}+" $line]} {
+ set line " $line"
+ }
+
+ set nextline [lindex [lindex $fld 1] $j]
+ if {[regexp {^-- $} $line] == 0} {
+ catch {unset linetext}
+ # trim trailing WS from lines preceding those with LWS (space-stuff as needed)
+ if {[string length $nextline] == 0 || [regexp "^${ws}+(${nws}?.*)\$" $nextline dummy linetext]} {
+ set line [string trimright $line]
+ if {[info exists linetext] == 0 || [string length $linetext] == 0} {
+ set nextline ""
+ }
+ }
+
+ # break overly long lines in a flowed way
+ if {[regexp {^[^>]} $line] && [string length $line] > 1000} {
+ while {[regexp "^(${ws}*${nws}+${ws}+)$nws" [string range $line 900 end] dummy linex]} {
+ set cliplen [expr {900 + [string length $linex]}]
+ lappend newbody [string range $line 0 [expr {$cliplen - 1}]]
+ set line [string range $line $cliplen end]
+ }
+ }
+ }
+
+ lappend newbody $line
+ }
+
+ set msgdata [lreplace $msgdata $i $i [list body $newbody]]
+ }
+ }
+ default {
+ }
+ }
+ }
+ }
+ } result]} {
+ WPCmd PEInfo statmsg "Address problem: $result"
+ } else {
+ # update fcc?
+ if {[info exists expanded_fcc]
+ && (![info exists has_fcc] || 0 == [string compare [lindex $fccdef 1] [lindex $has_fcc 1]])} {
+ for {set j 0} {$j < [llength $msgdata]} {incr j} {
+ if {[string compare fcc [fieldname [lindex [lindex $msgdata $j] 0]]] == 0} {
+ set fcc_index $j
+ break
+ }
+ }
+
+ set colid [lindex $fccdef 0]
+ if {[info exists fcc_index]} {
+ set msgdata [lreplace $msgdata $fcc_index $fcc_index [list Fcc [list $colid $expanded_fcc]]]
+ } else {
+ lappend msgdata [list Fcc [list $colid $expanded_fcc]]
+ }
+ }
+
+ # do the sending...
+ set verb Send
+ set verbpast Sent
+ set postcmd PECompose
+ set postcmdopt post
+ }
+ } else {
+ WPCmd PEInfo statmsg "Send MUST include Recipients (To, Cc, Bcc, or Fcc)"
+ }
+} elseif {[string compare OK [string trim $action]] == 0 && ($postpone || [string compare $sendop postpone] == 0)} {
+ set verb Postpone
+ set verbpast Postponed
+ set postcmd PEPostpone
+ set postcmdopt append
+} elseif {$help == 1 || [string compare "get help" [string tolower $help]] == 0} {
+ # save msgdata to servlet
+ if {[catch {WPCmd PEInfo set suspended_composition $msgdata} errstr] == 0} {
+ # fake cgi input for script
+ _cgi_set_uservar oncancel "compose&restore=1"
+ set src help
+ } else {
+ # else fall thru back into composer
+ WPCmd PEInfo statmsg "Compose Error: $errstr"
+ }
+} elseif {$check == 1 || [string compare spell [string tolower [string range $check 0 4]]] == 0} {
+ # save msgdata to servlet
+ if {[catch {WPCmd PEInfo set suspended_composition $msgdata} errstr] == 0} {
+ set src spell
+ } else {
+ # else fall thru back into composer
+ WPCmd PEInfo statmsg "Compose Error: $errstr"
+ }
+} elseif {$queryattach == 1 || [string compare "add attachment" [string tolower $queryattach]] == 0 || [string compare "attach" [string tolower $queryattach]] == 0} {
+ # save msgdata to servlet
+ if {[catch {WPCmd PEInfo set suspended_composition $msgdata} errstr] == 0} {
+ # fake cgi input for script
+ set src askattach
+ } else {
+ # else fall thru back into composer
+ WPCmd PEInfo statmsg "Compose Error: $errstr"
+ }
+} elseif {$br_to == 1 || [string compare browse [string tolower $br_to]] == 0 || [string compare to [string tolower $br_to]] == 0} {
+ # save msgdata to servlet
+ if {[catch {WPCmd PEInfo set suspended_composition $msgdata} errstr] == 0} {
+ # fake cgi input for script
+ set oncancel compose
+ _cgi_set_uservar op browse
+ _cgi_set_uservar field to
+ set src addrbrowse
+ } else {
+ # else fall thru back into composer
+ WPCmd PEInfo statmsg "Compose Error: $errstr"
+ }
+} elseif {$br_cc == 1 || [string compare browse [string tolower $br_cc]] == 0 || [string compare cc [string tolower $br_cc]] == 0} {
+ # save msgdata to servlet
+ if {[catch {WPCmd PEInfo set suspended_composition $msgdata} errstr] == 0} {
+ # fake cgi input for script
+ set oncancel compose
+ _cgi_set_uservar op browse
+ _cgi_set_uservar field cc
+ set src addrbrowse
+ } else {
+ # else fall thru back into composer
+ WPCmd PEInfo statmsg "Compose Error: $errstr"
+ }
+} elseif {$br_bcc == 1 || [string compare browse [string tolower $br_bcc]] == 0 || [string compare bcc [string tolower $br_bcc]] == 0} {
+ # save msgdata to servlet
+ if {[catch {WPCmd PEInfo set suspended_composition $msgdata} errstr] == 0} {
+ # fake cgi input for script
+ set oncancel compose
+ _cgi_set_uservar op browse
+ _cgi_set_uservar field bcc
+ set src addrbrowse
+ } else {
+ # else fall thru back into composer
+ WPCmd PEInfo statmsg "Compose Error: $errstr"
+ }
+} elseif {$br_reply_to == 1 || [string compare browse [string tolower $br_reply_to]] == 0 || [string compare "reply_to" [string tolower $br_reply_to]] == 0} {
+ # save msgdata to servlet
+ if {[catch {WPCmd PEInfo set suspended_composition $msgdata} errstr] == 0} {
+ # fake cgi input for script
+ set oncancel compose
+ _cgi_set_uservar op browse
+ _cgi_set_uservar field reply-to
+ set src addrbrowse
+ } else {
+ # else fall thru back into composer
+ WPCmd PEInfo statmsg "Compose Error: $errstr"
+ }
+} elseif {$br_fcc == 1 || ($br_fcc > 0 && [string length $br_fcc] > 0)} {
+ # save msgdata to servlet
+ if {[catch {WPCmd PEInfo set suspended_composition $msgdata} errstr] == 0} {
+ # fake cgi input for script
+ _cgi_set_uservar onselect compose
+ _cgi_set_uservar oncancel compose
+ set src fldrbrowse
+ } else {
+ # else fall thru back into composer
+ WPCmd PEInfo statmsg "Compose Error: $errstr"
+ }
+} elseif {[string compare expand [string tolower $ex_to]] == 0} {
+ if {[expand_address_field To msgdata]} {
+ set src ldapbrowse
+ }
+} elseif {[string compare expand [string tolower $ex_cc]] == 0} {
+ if {[expand_address_field Cc msgdata]} {
+ set src ldapbrowse
+ }
+} elseif {[string compare expand [string tolower $ex_bcc]] == 0} {
+ if {[expand_address_field Bcc msgdata]} {
+ set src ldapbrowse
+ }
+} elseif {[string compare expand [string tolower $ex_reply_to]] == 0} {
+ if {[expand_address_field Reply-To msgdata]} {
+ set src ldapbrowse
+ }
+} elseif {[string length $extrahdrs] > 0} {
+ # save msgdata to servlet
+ if {[catch {WPCmd PEInfo set suspended_composition $msgdata} errstr] == 0} {
+ if {[catch {WPCmd PEInfo set wp_extra_hdrs} extras] || $extras == 1} {
+ set toggle 0
+ } else {
+ set toggle 1
+ }
+
+ catch {WPCmd PEInfo set wp_extra_hdrs $toggle}
+
+ _cgi_set_uservar restore 1
+ set src compose
+ } else {
+ # else fall thru back into composer
+ WPCmd PEInfo statmsg "Compose Error: $errstr"
+ }
+} elseif {[string compare OK [string trim $action]] == 0 && ($cancel || [string compare $sendop cancel] == 0)} {
+ # clean up attachments
+ WPCmd PEInfo statmsg "Message cancelled"
+ catch {WPCmd PEInfo unset suspended_composition}
+ catch {WPCmd PEInfo unset wp_extra_hdrs}
+ set src ""
+} else {
+ # check for per-attachment ops
+ if {[info exists attachments]} {
+ set a [split $attachments ","]
+ for {set i 0} {$i < [llength $a]} {incr i} {
+ if {[catch {cgi_import detach_[lindex $a $i].x}] == 0} {
+ if {[catch {WPCmd PECompose unattach [lindex $a $i]} result]} {
+ WPCmd PEInfo statmsg "Unattach: $result"
+ } else {
+ set attachment_deleted [lindex $a $i]
+
+ set a [lreplace $a $i $i]
+ set attachments [join $a ","]
+
+ for {set i 0} {$i < [llength $msgdata]} {incr i} {
+ if {[string compare attach [lindex [lindex $msgdata $i] 0]] == 0 && [lindex [lindex $msgdata $i] 1] == $attachment_deleted} {
+ set msgdata [lreplace $msgdata $i $i]
+ break
+ }
+ }
+
+ WPCmd PEInfo statmsg "Attachment Removed"
+ }
+
+ break
+ }
+ }
+ }
+
+ if {![info exists attachment_deleted]} {
+ WPCmd PEInfo statmsg "Unrecognized Action"
+ }
+}
+
+#do what was asked
+if {[info exists postcmd]} {
+ if {[info exists msgdata]} {
+ if {[catch {WPCmd $postcmd $postcmdopt $msgdata} errstr]} {
+ # if auth problem, save msgdata for after we ask for credentials
+ if {([string compare NOPASSWD [string range $errstr 0 7]] == 0 || [string compare BADPASSWD [string range $errstr 0 8]] == 0)
+ && [catch {WPCmd PEInfo set suspended_composition $msgdata} errstr] == 0} {
+
+ if {[catch {WPCmd PEInfo authrequestor} server]} {
+ append reason "Unknown server asking for authentication. Press cancel to abort if you think this message is in error."
+ } else {
+ append reason "[cgi_nl]Enter Username and Password to connect to [cgi_bold $server]"
+ lappend params [list server $server]
+ }
+
+ if {[catch {WPCmd PESession creds 0 "{$server}"} creds] == 0 && $creds != 0} {
+ catch {WPCmd PEInfo statmsg "Invalid Username or Password"}
+ WPCmd PESession nocred 0 "{$server}"
+ }
+
+ WPCmd set reason "The server ($server) used to send this message requires authentication.[cgi_nl]"
+
+ WPCmd set cid [WPCmd PEInfo key]
+ WPCmd set authcol 0
+ WPCmd set authfolder "{$server}"
+ WPCmd set authpage [WPPercentQuote "[cgi_root]/$_wp(appdir)/$_wp(ui1dir)/wp.tcl?page=dosend"]
+ WPCmd set authcancel [WPPercentQuote "[cgi_root]/$_wp(appdir)/$_wp(ui1dir)/wp.tcl?page=compose&restore=1&cid=$cid"]
+
+ set src auth
+
+ } else {
+ # regurgitate the compose window
+ set style ""
+ set title "$verb Error: [cgi_font class=notice "$errstr"]"
+ if {[string length $errstr]} {
+ set notice "$verb FAILED: $errstr"
+ } else {
+ set notice "$verb FAILED: [WPCmd PEInfo statmsg]"
+ }
+
+ WPCmd PEInfo statmsg "$notice"
+
+ # regurgitate the compose window
+ if {[catch {WPCmd PEInfo set suspended_composition $msgdata} errstr] == 0} {
+ _cgi_set_uservar restore 1
+ set src compose
+
+ unset body
+ } else {
+ }
+
+ set src compose
+ }
+ } else {
+ catch {WPCmd PEInfo unset suspended_composition}
+ WPCmd PEInfo statmsg "Message $verbpast!"
+ }
+ } else {
+ WPCmd PEInfo statmsg "No Message $verbpast!"
+ }
+
+ if {[info exists delete_me]} {
+ foreach i $delete_me {
+ catch {file delete $i}
+ }
+ }
+} elseif {![info exists src]} {
+ set style ""
+ set title "Compose Message"
+ catch {unset attachments}
+
+ # regurgitate the compose window
+ if {[catch {WPCmd PEInfo set suspended_composition $msgdata} errstr] == 0} {
+ _cgi_set_uservar restore 1
+ set src compose
+
+ unset body
+ }
+}
+
+if {[info exists src] && [string length $src]} {
+ source [WPTFScript $src]
+} else {
+ cgi_redirect "[cgi_root]/$_wp(appdir)/$_wp(ui1dir)/wp.tcl?page=$postpost"
+}
diff --git a/web/cgi/alpine/1.0/promptsave.tcl b/web/cgi/alpine/1.0/promptsave.tcl
new file mode 100755
index 00000000..c7e7c2d8
--- /dev/null
+++ b/web/cgi/alpine/1.0/promptsave.tcl
@@ -0,0 +1,149 @@
+#!./tclsh
+# $Id: promptsave.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# promptsave.tcl
+#
+# Purpose: CGI script to generate html form used to gather folder
+# name and collection for aggregate save
+
+# Input:
+# conftext :
+# params : array of key/value pairs to submit with form
+# oncancel : url to reference should user cancel dialog
+set psave_vars {
+ {uid "" 0}
+}
+
+# Output:
+#
+# HTML/CSS data representing the form for save folder dialog
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+set query_menu {
+ {
+ {}
+ {
+ {
+ # * * * * OK * * * *
+ cgi_image_button save=[WPimg but_save] border=0 alt="Save"
+ }
+ }
+ }
+ {
+ {}
+ {
+ {
+ # * * * * CANCEL * * * *
+ cgi_puts [cgi_url [cgi_img [WPimg but_cancel] border=0 alt="Cancel"] wp.tcl?${oncancel}]
+ }
+ }
+ }
+}
+
+WPEval $psave_vars {
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "Aggregate Save"
+ WPStyleSheets
+ }
+
+ cgi_body BGCOLOR="$_wp(bordercolor)" {
+
+ catch {WPCmd PEInfo set help_context promptsave}
+ catch {WPCmd PEInfo set wp_index_script fr_promptsave.tcl}
+
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=get name=auth target=body {
+ cgi_text page=body type=hidden notab
+
+ cgi_table border=0 cellspacing=0 cellpadding=2 width="100%" height="100%" {
+ cgi_table_row {
+ cgi_table_data align=center valign=top class=dialog {
+ cgi_table width="80%" {
+ cgi_table_row {
+ cgi_table_data colspan=2 {
+ cgi_center {
+ cgi_puts "[cgi_nl][cgi_nl]This page provides a way to save messages to a folder"
+ }
+ }
+
+ cgi_table_row class=dialog {
+ cgi_table_data valign=top align=center nowrap class=dialog colspan=2 {
+ cgi_puts [cgi_font face=tahoma,verdana,geneva "Save for messages "]
+
+ cgi_put "Save "
+
+ if {!$uid} {
+ set n [WPCmd PEMailbox selected]
+ cgi_put "all [cgi_bold [WPcomma $n]] marked message[WPplural $n] "
+ if {[catch {WPCmd PEMessage $uid savedefault} savedefault]} {
+ set savedefault [list 1 saved-messages]
+ }
+ } else {
+ set savedefault [list 1 saved-messages]
+ }
+
+ cgi_put "to "
+ cgi_br
+
+ cgi_text "savename=[lindex $savedefault 1]" type=text size=14 maxlength=256 class=aggop style=vertical-align:middle onFocus=this.select()
+ if {[catch {WPCmd PEFolder collections} collections] == 0 && [llength $collections] > 1} {
+ cgi_put "[cgi_nbspace]in "
+ cgi_select savecolid class=aggop style=vertical-align:middle {
+ set defcol [lindex $savedefault 0]
+ set j 0
+ foreach i $collections {
+ if {$j == $defcol} {
+ set selected selected
+ } else {
+ set selected {}
+ }
+ if {[string length [set f [lindex $i 1]]] > 12} {
+ set f "[string range $f 0 10]..."
+ }
+
+ cgi_option $f value=$j $selected
+ incr j;
+ }
+ }
+ } else {
+ cgi_text "savecolid=0" type=hidden notab
+ }
+
+ cgi_br
+ cgi_puts "[cgi_nl]Click [cgi_italic Save] to save the message the folder, or [cgi_italic Cancel] to abort the save."
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data class=dialog align=center colspan=2 {
+ cgi_br
+ cgi_submit_button save=Save
+ cgi_submit_button savecancel=Cancel
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/prune.tcl b/web/cgi/alpine/1.0/prune.tcl
new file mode 100755
index 00000000..c35552e3
--- /dev/null
+++ b/web/cgi/alpine/1.0/prune.tcl
@@ -0,0 +1,74 @@
+# $Id: prune.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# post.tcl
+#
+# Purpose: CGI script to perform monthly message pruning via prunetime.tcl
+# generated form
+
+# Input:
+set prune_vars {
+ {cid "Missing Command ID"}
+ {mvcnt "Missing Move Count"}
+ {delList "" ""}
+}
+
+# Output:
+#
+
+## read vars
+foreach item $prune_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} errstr]} {
+ error [list _action "Impart Variable" $errstr]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+if {$cid != [WPCmd PEInfo key]} {
+ error [list _action Postpone "Invalid Operation ID" "Click Back button to try again."]
+}
+
+set mvvals {}
+for {set i 0} {$i < $mvcnt} {incr i} {
+ WPLoadCGIVarAs "mv${i}" tmpmv
+ if {[string compare $tmpmv ""]} {
+ lappend mvvals $tmpmv
+ }
+}
+
+foreach mvval $mvvals {
+ set mvfrm [lindex $mvval 1]
+ set mvto [lindex $mvval 2]
+
+ if {[catch {WPCmd PEFolder rename default $mvfrm $mvto} result]} {
+ set msg "Can't Rename $mvfrm: $result"
+ } else {
+ set msg "Renaming \"${mvfrm}\" at start of month"
+ catch {WPCmd PEFolder create default $mvfrm} result
+ }
+ WPCmd PEInfo statmsg $msg
+}
+
+foreach delfldr $delList {
+ set msg ""
+ if {[catch {WPCmd PEFolder delete default $delfldr} result]} {
+ set msg "Can't delete ${delfldr}: $result"
+ } else {
+ set msg "Deleted $delfldr"
+ }
+ WPCmd PEInfo statmsg $msg
+}
+
+source [WPTFScript main]
diff --git a/web/cgi/alpine/1.0/queryattach.tcl b/web/cgi/alpine/1.0/queryattach.tcl
new file mode 100755
index 00000000..9cd7028c
--- /dev/null
+++ b/web/cgi/alpine/1.0/queryattach.tcl
@@ -0,0 +1,145 @@
+#!./tclsh
+# $Id: queryattach.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# queryattach.tcl
+#
+# Purpose: CGI script to generate html form used to ask for
+# attachment to composition
+
+# Input:
+
+# Output:
+#
+# HTML/CSS data representing the form
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+# make sure form's in Unicode
+set charset "UTF-8"
+
+set query_menu {
+ {
+ {}
+ {
+ {
+ cgi_puts "Get Help"
+ }
+ }
+ }
+ {
+ {expr 0}
+ {
+ {
+ # * * * * OK * * * *
+ cgi_submit_button "attach=Add Attachment" class="navbar"
+ }
+ }
+ }
+ {
+ {expr 0}
+ {
+ {
+ # * * * * CANCEL * * * *
+ cgi_submit_button cancel=Cancel class="navbar"
+ }
+ }
+ }
+ {
+ {expr 0}
+ {
+ {
+ # * * * * Address/Cancel * * * *
+ cgi_submit_button doit=Done class="navbar"
+ cgi_br
+ cgi_select attachop class=navtext {
+ cgi_option "Action..." value=null
+ cgi_option Attach value=attach
+ cgi_option Cancel value=cancel
+ }
+ }
+ }
+ }
+}
+
+WPEval {} {
+
+ cgi_http_head {
+ WPStdHttpHdrs "text/html; charset=\"$charset\""
+ }
+
+ cgi_html {
+ cgi_head {
+ cgi_http_equiv Content-Type "text/html; charset=$charset"
+ WPStdHtmlHdr "Attach"
+ WPStyleSheets
+ cgi_put "<style type='text/css'>"
+ cgi_put ".filename { font-family: Courier, monospace ; font-size: 10pt }"
+ cgi_puts "</style>"
+ }
+
+ cgi_body BGCOLOR="$_wp(bordercolor)" {
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=post enctype=multipart/form-data target=_top {
+ cgi_text page=attach type=hidden notab
+ cgi_text cid=[WPCmd PEInfo key] type=hidden notab
+ if {[info exists params]} {
+ foreach p $params {
+ cgi_text "[lindex $p 0]=[lindex $p 1]" type=hidden notab
+ }
+ }
+
+ cgi_table border=0 cellpadding=0 cellspacing=0 width="100%" height="100%" {
+ cgi_table_row {
+ eval {
+ cgi_table_data $_wp(menuargs) {
+ WPTFCommandMenu query_menu {}
+ }
+ }
+
+ cgi_table_data align=center valign=top class=dialog {
+ cgi_table border=0 width=75% cellpadding=15 {
+ cgi_table_row {
+ cgi_table_data align=center {
+ cgi_puts "To attach a file to your message, enter its path and file name below, or use the [cgi_italic Browse] button to choose the file, then click [cgi_italic "Add Attachment"], or click [cgi_italic Cancel] to return to your composition without attaching anything."
+ }
+ }
+ cgi_table_row {
+ cgi_table_data align=center {
+ cgi_file_button file "accept=*/*" size=30 class=filename
+ }
+ }
+ cgi_table_row {
+ cgi_table_data align=center {
+ cgi_puts "You can also provide a short description to help the message's recipient figure out what the attachment is :"
+ }
+ }
+ cgi_table_row {
+ cgi_table_data align=center {
+ cgi_text description= maxlength=256 size=40 class=filename
+ }
+ }
+ cgi_table_row {
+ cgi_table_data align=center {
+ cgi_submit_button "attach=Add Attachment"
+ cgi_submit_button cancel=Cancel
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/querycreate.tcl b/web/cgi/alpine/1.0/querycreate.tcl
new file mode 100755
index 00000000..b3057ab3
--- /dev/null
+++ b/web/cgi/alpine/1.0/querycreate.tcl
@@ -0,0 +1,114 @@
+#!./tclsh
+# $Id: querycreate.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# querycreate.tcl
+#
+# Purpose: CGI script to generate html form used to confirm folder
+# creation for Save
+
+# Input:
+# conftext :
+# params : array of key/value pairs to submit with form
+# oncancel : url to reference should user cancel confirmation
+set qcreate_vars {
+}
+
+# Output:
+#
+# HTML/Javascript/CSS data representing the message specified
+# by the 'uid' argument
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+set query_menu {
+ {
+ {}
+ {
+ {
+ # * * * * OK * * * *
+ cgi_image_button create=[WPimg but_create] border=0 alt="Create"
+ }
+ }
+ }
+ {
+ {}
+ {
+ {
+ # * * * * CANCEL * * * *
+ cgi_puts [cgi_url [cgi_img [WPimg but_cancel] border=0 alt="Cancel"] wp.tcl?${oncancel}]
+ }
+ }
+ }
+}
+
+WPEval $qcreate_vars {
+
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "Confirm Creation"
+ WPStyleSheets
+ }
+
+ cgi_body BGCOLOR="$_wp(bordercolor)" {
+
+ if {[catch {WPCmd PEInfo set querycreate_state} qstate]} {
+
+ } else {
+ catch {WPCmd PEInfo unset querycreate_state}
+
+ set folder [lindex $qstate 0]
+ set params [lindex $qstate 1]
+
+ catch {WPCmd PEInfo set help_context create_save}
+
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=get name=confirm target=spec {
+ if {[info exists params]} {
+ foreach p $params {
+ cgi_text "[lindex $p 0]=[lindex $p 1]" type=hidden notab
+ }
+ }
+
+ cgi_table border=0 cellspacing=0 cellpadding=30 width="100%" height="100%" class=dialog {
+ cgi_table_row {
+ cgi_table_data align=center valign=top {
+ cgi_table width="80%" border=0 {
+ cgi_table_row {
+ cgi_table_data valign=top align=center {
+ cgi_puts "You are attempting to Save to a folder, [cgi_bold $folder], that does not exist."
+ cgi_br
+ cgi_puts "[cgi_nl]Click [cgi_italic Create] to create [cgi_bold $folder] and save the message, or [cgi_italic Cancel] to create nothing and return to the Message View."
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data align=center {
+ cgi_br
+ cgi_submit_button create=Create
+ cgi_submit_button savecancel=Cancel
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/querydelfldr.tcl b/web/cgi/alpine/1.0/querydelfldr.tcl
new file mode 100755
index 00000000..e0fa960a
--- /dev/null
+++ b/web/cgi/alpine/1.0/querydelfldr.tcl
@@ -0,0 +1,121 @@
+#!./tclsh
+# $Id: querydelfldr.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# querydelfldr.tcl
+#
+# Purpose: CGI script to generate html form used to confirm
+# folder deletion
+
+# Input:
+set fldr_vars {
+ {fid "No Folder Specified"}
+}
+
+# Output:
+#
+# HTML/CSS data representing the message specified
+# by the 'uid' argument
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+set query_menu {
+ {
+ {}
+ {
+ {
+ # * * * * HELP * * * *
+ cgi_put "Get Help"
+ }
+ }
+ }
+}
+
+WPEval $fldr_vars {
+ if {[catch {WPCmd PEFolder collections} collections]} {
+ error [list _action "Collection list" $collections]
+ }
+
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "Confirm Delete"
+ WPStyleSheets
+ }
+
+ cgi_body BGCOLOR="$_wp(bordercolor)" {
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=get name=confirm target=_top {
+ cgi_text "page=folders" type=hidden notab
+ cgi_text "fid=$fid" type=hidden notab
+ cgi_text "cid=[WPCmd PEInfo key]" type=hidden notab
+ cgi_text "frestore=1" type=hidden notab
+
+ cgi_table border=0 cellspacing=0 cellpadding=2 width="100%" height="100%" {
+ cgi_table_row {
+ eval {
+ cgi_table_data $_wp(menuargs) {
+ WPTFCommandMenu query_menu {}
+ }
+ }
+
+ cgi_table_data valign=top align=center class=dialog {
+ cgi_table border=0 cellspacing=0 cellpadding=2 width="75%" {
+ cgi_table_row {
+ cgi_table_data align=center {
+ cgi_puts [cgi_nl][cgi_nl][cgi_nl][cgi_nl]
+
+ regsub -all { } [lindex $fid end] {\&nbsp;} dfn
+
+ cgi_puts "Please confirm that you would like to permanently remove [cgi_bold $dfn]"
+
+ if {[llength $fid] > 2} {
+ if {[catch {WPCmd PEFolder delimiter [lindex $fid 0]} delim]} {
+ set delim /
+ }
+
+ set dirname ""
+ for {set i 1} {$i < ([llength $fid] - 1)} {incr i} {
+ append dirname "[lindex $fid $i]$delim"
+ }
+
+ if {[string length $dirname]} {
+ cgi_put " from the directory [cgi_bold $dirname] "
+ }
+ }
+ if {[llength $collections] > 1} {
+ cgi_put "in the collection '[lindex [lindex $collections [lindex $fid 0]] 1]'."
+ } else {
+ cgi_put "."
+ }
+
+ cgi_br
+ cgi_br
+ cgi_puts "Click [cgi_italic Delete] to remove the folder permanently, or [cgi_italic Cancel] to return to the Folder List."
+ cgi_br
+ cgi_br
+ cgi_submit_button delete=Delete
+ cgi_submit_button delete=Cancel
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/queryexpunge.tcl b/web/cgi/alpine/1.0/queryexpunge.tcl
new file mode 100755
index 00000000..c3e552d6
--- /dev/null
+++ b/web/cgi/alpine/1.0/queryexpunge.tcl
@@ -0,0 +1,203 @@
+#!./tclsh
+# $Id: queryexpunge.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# queryexpunge.tcl
+#
+# Purpose: CGI script to generate html form used to confirm
+# deleted message expunge
+
+# Input (Assumed set by sourcing script):
+# fn : Name of folder getting expunged
+# delcount : Number of deleted messages
+
+# Output:
+#
+# HTML/CSS data representing the message specified
+# by the 'uid' argument
+
+# inherit global config
+source ./alpine.tcl
+
+WPEval {} {
+
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "Confirm Expunge"
+ WPStyleSheets
+ }
+
+ cgi_body BGCOLOR="$_wp(bordercolor)" {
+
+ catch {WPCmd PEInfo set help_context expunge}
+
+ cgi_table border=0 cellspacing=0 cellpadding=2 width="100%" height="100%" {
+ cgi_table_row {
+ cgi_table_data valign=top align=center class=dialog {
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/fr_index method=get name=confirm target=spec {
+ cgi_text "cid=[WPCmd PEInfo key]" type=hidden notab
+
+ set mbn [WPCmd PEMailbox mailboxname]
+ cgi_table border=0 cellspacing=8 cellpadding=8 width="75%" {
+
+ if {[catch {WPCmd PEMailbox flagcount deleted} delcount] == 0 && $delcount > 0
+ && [catch {WPCmd PEMailbox messagecount} messcount] == 0} {
+
+ cgi_table_row {
+ cgi_table_data align=center valign=middle height=50 {
+ cgi_table bgcolor=yellow background=[WPimg dstripe] cellpadding=6 {
+ cgi_table_row {
+ cgi_table_data {
+ cgi_table bgcolor=black cellpadding=6 {
+ cgi_table_row {
+ cgi_table_data {
+ cgi_puts [cgi_font size=+2 color=yellow [cgi_bold "CAUTION!"]]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if {$delcount == $messcount} {
+ switch $delcount {
+ 1 {
+ set m1 "The [cgi_bold only] message in the folder [cgi_bold $mbn] is deleted."
+ set m2 "that [cgi_bold single] message"
+ }
+ 2 {
+ set m1 "[cgi_bold Both] messages in the folder [cgi_bold $mbn] are deleted."
+ set m2 "[cgi_bold "both"] messages"
+ }
+ default {
+ set m1 "[cgi_span "style=font-weight: bold; color: red" "All $messcount messages"] in the folder [cgi_bold $mbn] are marked for deletion. This includes any messages that might <u>not</u> be <u>visible</u> on the screen."
+ set m2 "[cgi_bold [cgi_span "style=font-size: bigger; color: red; text-decoration: underline" "all messages"]]"
+ }
+ }
+
+ append m1 "[cgi_nl][cgi_nl]Expunge now will leave this folder "
+ append m1 "[cgi_span "style=font-weight: bold; color: red" empty]. "
+ #append m1 "[cgi_nl][cgi_nl]Please acknowledge below that you understand there will be [cgi_span "style=font-weight: bold; color: red" "no more messages"] within this folder when the expunge is complete."
+ append m1 "[cgi_nl][cgi_nl][cgi_buffer {cgi_checkbox "emptyit=1"}] "
+ append m1 "I acknowledge expunge will leave folder [cgi_bold $mbn] [cgi_span "style=font-weight: bold; color: red" empty]."
+ set style "style=\"border: 1px solid #663333; background-color: #ffcc66;\""
+ set m3 ALL
+ } else {
+ if {$delcount > 1} {
+ set whch are
+ set plrl "s"
+ } else {
+ set whch is
+ set plrl ""
+ }
+
+ set m1 "Your folder [cgi_bold $mbn] contains $messcount messages, of which [cgi_bold $delcount] $whch deleted."
+ set m2 "[cgi_span "style=font-size: bigger; font-weight: bold" $delcount] message${plrl}"
+ set m3 $delcount
+ set style ""
+ }
+
+ cgi_table_row {
+ cgi_table_data align=center $style {
+ cgi_puts $m1
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data align=center {
+ cgi_puts "Do you wish to [cgi_span "style=color: red ; font-weight: bold" "permanently remove"] $m2 now?"
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data align=center {
+ cgi_submit_button "expunge=Yes, Remove $m3 message[WPplural $delcount]" tabindex=2
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data align=center {
+ cgi_submit_button "cancel=No, Return to '$mbn'" tabindex=1 checked selected default
+ }
+ }
+ } else {
+ cgi_table_row {
+ cgi_table_data align=center {
+ cgi_puts "There are [cgi_bold no] messages currently marked for deletion in the folder [cgi_bold [WPCmd PEMailbox mailboxname]]."
+ }
+ }
+
+ if {[WPCmd PEInfo feature enable-aggregate-command-set]} {
+ switch [WPCmd PEInfo aggtabstate] {
+ 0 {
+ lappend methods "Click the [cgi_img [WPimg slideout] style=vertical-align:middle] tab to expose aggregate operations"
+ lappend methods "Place a mark in the checkbox next to each desired message"
+ lappend methods "Click the [cgi_italic Delete] button"
+ }
+ 1 {
+ lappend methods "Place a mark in the checkbox next to each desired message"
+ lappend methods "Click the [cgi_italic Delete] button"
+ }
+ 2 {
+ lappend methods "Place a mark in the checkbox next to each desired message"
+ lappend methods "Within the Message Status box, choose [cgi_bold Deleted] from the drop-down list of flag choices"
+ lappend methods "Click the [cgi_italic Set] button"
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data align=center {
+ cgi_puts "To mark a message for deletion while viewing it, simply click the [cgi_italic Delete] button at the top of the Message View page."
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data align=center {
+ cgi_puts "To mark messages for deletion in the Message List:"
+ cgi_number_list {
+ foreach i $methods {
+ cgi_li $i
+ }
+ }
+ }
+ }
+ } else {
+ cgi_table_row {
+ cgi_table_data align=center {
+ cgi_puts "To mark a message for deletion, click the [cgi_italic Delete] button while viewing the message."
+ }
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data align=center {
+ cgi_puts "Click [cgi_italic OK] to return to the Message List."
+ cgi_br
+ cgi_br
+ cgi_submit_button cancel=OK
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/queryimport.tcl b/web/cgi/alpine/1.0/queryimport.tcl
new file mode 100755
index 00000000..1f40bad2
--- /dev/null
+++ b/web/cgi/alpine/1.0/queryimport.tcl
@@ -0,0 +1,146 @@
+#!./tclsh
+# $Id: queryimport.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# queryimport.tcl
+#
+# Purpose: CGI script to generate html form used to ask for
+# importing a folder
+
+# Input:
+set import_vars {
+ {fid "No Collection Specified"}
+}
+
+# Output:
+#
+# HTML/CSS data representing the form
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+set query_menu {
+ {
+ {expr {0}}
+ {
+ {
+ cgi_puts "Get Help"
+ }
+ }
+ }
+}
+
+WPEval $import_vars {
+
+ set colid [lindex $fid 0]
+ if {[llength $fid] > 1} {
+ set fpath [eval "file join [lrange $fid 1 end]"]
+ } else {
+ set fpath ""
+ }
+
+ if {[catch {WPCmd PEFolder collections} collections]} {
+ catch {WPCmd PEInfo statmsg "Can't Import: $collections"}
+ cgi_http_head {
+ cgi_redirect [cgi_root]/$_wp(appdir)/$_wp(ui1dir)/wp.tcl?page=folders.tcl
+ }
+ } elseif {$colid < 0 || $colid > [llength $collections]} {
+ catch {WPCmd PEInfo statmsg "Can't Import: Invalid collection: $colid"}
+ cgi_http_head {
+ cgi_redirect [cgi_root]/$_wp(appdir)/$_wp(ui1dir)/wp.tcl?page=folders.tcl
+ }
+ } else {
+
+ if {[string length $fpath]} {
+ set coldesc "the directory [cgi_bold $fpath] within "
+ }
+
+ append coldesc "the collection [cgi_bold [lindex [lindex $collections $colid] 1]]"
+
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "Import"
+ WPStyleSheets
+ cgi_put "<style type='text/css'>"
+ cgi_put ".filename { font-family: Courier, monospace ; font-size: 10pt }"
+ cgi_puts "</style>"
+ }
+
+ cgi_body BGCOLOR="$_wp(bordercolor)" {
+
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=post enctype=multipart/form-data target=_top {
+ cgi_text page=folders type=hidden notab
+ cgi_text cid=[WPCmd PEInfo key] type=hidden notab
+ cgi_text fid=$fid type=hidden notab
+
+ cgi_table border=0 cellpadding=0 cellspacing=0 width="100%" height="100%" {
+ cgi_table_row {
+ eval {
+ cgi_table_data $_wp(menuargs) {
+ WPTFCommandMenu query_menu {}
+ }
+ }
+
+ cgi_table_data align=center valign=top class=dialog {
+ cgi_table border=0 width=75% cellpadding=15 {
+ cgi_table_row {
+ cgi_table_data align=center "style=\"padding-top:30\"" {
+ cgi_puts "Folder Import copies a mail folder, typically created by the Export command, from the computer your browser is running on into a new Web Alpine folder. [cgi_nbspace]Successful Import consists of three steps."
+ cgi_p
+ cgi_puts "First, enter the path and filename of the folder below. Use the [cgi_italic Browse] button to help choose the file."
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data align=center {
+ cgi_file_button file "accept=*/*" size=30 class=filename
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data align=center {
+ cgi_puts "Second, provide a [cgi_bold unique] name for the imported folder to be assigned within $coldesc:"
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data align=center {
+ cgi_text iname= maxlength=256 size=40 class=filename
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data align=center {
+ cgi_puts "Finally, click [cgi_italic "Import File"] to copy the folder into WebPine, or [cgi_italic Cancel] to return to the folder list."
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data align=center {
+ cgi_submit_button "import=Import File" class=navtext
+ cgi_submit_button cancel=Cancel class=navtext
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/querynewdir.tcl b/web/cgi/alpine/1.0/querynewdir.tcl
new file mode 100755
index 00000000..e2adeaae
--- /dev/null
+++ b/web/cgi/alpine/1.0/querynewdir.tcl
@@ -0,0 +1,113 @@
+#!./tclsh
+# $Id: querynewdir.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# querynewfldr.tcl
+#
+# Purpose: CGI script to generate html form used to confirm
+# folder creation
+
+# Input:
+set fldr_vars {
+ {fid "No Collection Specified"}
+}
+
+# Output:
+#
+# HTML/CSS data representing the message specified
+# by the 'uid' argument
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+set query_menu {
+ {
+ {}
+ {
+ {
+ # * * * * HELP * * * *
+ cgi_put "Get Help"
+ }
+ }
+ }
+}
+
+WPEval $fldr_vars {
+
+ if {[catch {WPCmd PEFolder collections} collections]} {
+ error [list _action "Collection list" $collections]
+ }
+
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "Folder Creation"
+ WPStyleSheets
+ }
+
+ cgi_body BGCOLOR="$_wp(bordercolor)" {
+
+ catch {WPCmd PEInfo set help_context diradd}
+
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=get name=confirm target=_top {
+ cgi_text "page=folders" type=hidden notab
+ cgi_text "cid=[WPCmd PEInfo key]" type=hidden
+ cgi_text "fid=$fid" type=hidden
+ cgi_text "frestore=1" type=hidden
+
+ cgi_table border=0 cellspacing=0 cellpadding=2 width="100%" height="100%" {
+ cgi_table_row {
+ eval {
+ cgi_table_data $_wp(menuargs) {
+ WPTFCommandMenu query_menu {}
+ }
+ }
+
+ cgi_table_data valign=top align=center class=dialog {
+ cgi_table border=0 cellspacing=0 cellpadding=2 width="70%" {
+ cgi_table_row {
+ cgi_table_data align=center {
+ cgi_puts [cgi_nl][cgi_nl][cgi_nl][cgi_nl]
+ cgi_puts "Please enter the name of the directory you would like to add"
+ if {[llength $fid] > 1} {
+ cgi_put " to the directory '[join [lrange $fid 1 end] /]'"
+ }
+ if {[llength $collections] > 1} {
+ cgi_put "in the collection '[lindex [lindex $collections [lindex $fid 0]] 1]'."
+ } else {
+ cgi_put "."
+ }
+ cgi_br
+ cgi_br
+ cgi_put "New directory name: "
+ cgi_text directory= maxlength=64 size=25%
+ cgi_br
+ cgi_br
+ cgi_puts "Click 'Create' to add a new directory by that name, or 'Cancel' to return to the Folder List."
+ cgi_br
+ cgi_br
+ cgi_submit_button newdir=Create
+ cgi_submit_button newdir=Cancel
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/querynewfldr.tcl b/web/cgi/alpine/1.0/querynewfldr.tcl
new file mode 100755
index 00000000..598b6db6
--- /dev/null
+++ b/web/cgi/alpine/1.0/querynewfldr.tcl
@@ -0,0 +1,112 @@
+#!./tclsh
+# $Id: querynewfldr.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# querynewfldr.tcl
+#
+# Purpose: CGI script to generate html form used to confirm
+# folder creation
+
+# Input:
+set fldr_vars {
+ {fid "No Collection Specified"}
+}
+
+# Output:
+#
+# HTML/CSS data representing the message specified
+# by the 'uid' argument
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+set query_menu {
+ {
+ {}
+ {
+ {
+ # * * * * HELP * * * *
+ cgi_put [cgi_url "Get Help" wp.tcl?page=help&oncancel=folders class=navbar target=_top]
+ }
+ }
+ }
+}
+
+WPEval $fldr_vars {
+ if {[catch {WPCmd PEFolder collections} collections]} {
+ error [list _action "Collection list" $collections]
+ }
+
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "Folder Creation"
+ WPStyleSheets
+ }
+
+ cgi_body BGCOLOR="$_wp(bordercolor)" {
+
+ catch {WPCmd PEInfo set help_context foldadd}
+
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=get name=confirm target=_top {
+ cgi_text "page=folders" type=hidden notab
+ cgi_text "fid=$fid" type=hidden
+ cgi_text "cid=[WPCmd PEInfo key]" type=hidden
+ cgi_text "frestore=1" type=hidden
+
+ cgi_table border=0 cellspacing=0 cellpadding=2 width="100%" height="100%" {
+ cgi_table_row {
+ eval {
+ cgi_table_data $_wp(menuargs) {
+ WPTFCommandMenu query_menu {}
+ }
+ }
+
+ cgi_table_data valign=top align=center class=dialog {
+ cgi_table border=0 cellspacing=0 cellpadding=2 width="70%" {
+ cgi_table_row {
+ cgi_table_data align=center {
+ cgi_puts [cgi_nl][cgi_nl][cgi_nl][cgi_nl]
+ cgi_puts "Please enter the name of the folder you would like to add"
+ if {[llength $fid] > 1} {
+ cgi_put " to the directory '[join [lrange $fid 1 end] /]'"
+ }
+ if {[llength $collections] > 1} {
+ cgi_put " in the collection '[lindex [lindex $collections [lindex $fid 0]] 1]'."
+ } else {
+ cgi_put "."
+ }
+ cgi_br
+ cgi_br
+ cgi_put "New folder name: "
+ cgi_text folder= maxlength=64 size=25%
+ cgi_br
+ cgi_br
+ cgi_puts "Click 'Create' to add a new folder by that name, or 'Cancel' to return to the Folder List."
+ cgi_br
+ cgi_br
+ cgi_submit_button newfolder=Create
+ cgi_submit_button newfolder=Cancel
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/querynewfoldir.tcl b/web/cgi/alpine/1.0/querynewfoldir.tcl
new file mode 100755
index 00000000..13c20126
--- /dev/null
+++ b/web/cgi/alpine/1.0/querynewfoldir.tcl
@@ -0,0 +1,131 @@
+#!./tclsh
+# $Id: querynewfoldir.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# querynewfoldir.tcl
+#
+# Purpose: CGI script to generate html form used to confirm
+# folder and directory creation
+# Input:
+set fldr_vars {
+ {fid "No Collection Specified"}
+}
+
+# Output:
+#
+# HTML/CSS data representing the message specified
+# by the 'uid' argument
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+set query_menu {
+ {
+ {}
+ {
+ {
+ # * * * * HELP * * * *
+ cgi_put [cgi_url "Get Help" wp.tcl?page=help&oncancel=folders class=navbar target=_top]
+ }
+ }
+ }
+}
+
+WPEval $fldr_vars {
+ if {[catch {WPCmd PEFolder collections} collections]} {
+ error [list _action "Collection list" $collections]
+ }
+
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "Folder Creation"
+ WPStyleSheets
+ }
+
+ cgi_body BGCOLOR="$_wp(bordercolor)" {
+
+ catch {WPCmd PEInfo set help_context foldiradd}
+
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=get name=confirm target=_top {
+ cgi_text "page=folders" type=hidden notab
+ cgi_text "fid=$fid" type=hidden notab
+ cgi_text "cid=[WPCmd PEInfo key]" type=hidden notab
+ cgi_text "frestore=1" type=hidden notab
+
+ cgi_table border=0 cellspacing=0 cellpadding=2 width="100%" height="100%" {
+ cgi_table_row {
+ eval {
+ cgi_table_data $_wp(menuargs) {
+ WPTFCommandMenu query_menu {}
+ }
+ }
+
+ if {[llength $fid] > 1} {
+ set Dirpref "Subd"
+ set dirpref "subd"
+ } else {
+ set Dirpref "D"
+ set dirpref "d"
+ }
+
+ cgi_table_data valign=top align=center class=dialog {
+ cgi_table border=0 cellspacing=0 cellpadding=2 width="70%" {
+
+ cgi_table_row {
+ cgi_table_data align=center {
+ cgi_br
+ cgi_put "Folders are used to contain messages. Typically, messages are placed in folders when you [cgi_italic Save] them from the Message View"
+ if {[WPCmd PEInfo feature enable-aggregate-command-set]} {
+ cgi_puts " or Message List pages."
+ } else {
+ cgi_puts "page."
+ }
+
+ cgi_put "To create a new folder"
+
+ if {[llength $fid] > 1} {
+ cgi_put " within the directory [cgi_bold [join [lrange $fid 1 end] /]]"
+ }
+ if {[llength $collections] > 1} {
+ cgi_put " in the collection [cgi_bold [lindex [lindex $collections [lindex $fid 0]] 1]]"
+ }
+
+ cgi_put ", enter the name below and click [cgi_italic "Create New Folder"]."
+ cgi_br
+ cgi_br
+
+ cgi_put "Furthermore, folders can be created within directories. The directory can either be one that now exists "
+ cgi_put " or one that you wish to create along with the new folder. "
+ cgi_put "Simply specify the directory name before the folder name separating the two with a &quot;[WPCmd PEFolder delimiter [lindex $fid 0]]&quot; character."
+ cgi_br
+ cgi_br
+ cgi_put "New folder name: "
+ cgi_text folder= maxlength=64 size=25%
+ cgi_br
+ cgi_br
+ cgi_submit_button "newfolder=Create New Folder" "style=\"margin-right: 10px\""
+ cgi_submit_button cancelled=Cancel
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/querynick.tcl b/web/cgi/alpine/1.0/querynick.tcl
new file mode 100755
index 00000000..1e794822
--- /dev/null
+++ b/web/cgi/alpine/1.0/querynick.tcl
@@ -0,0 +1,171 @@
+#!./tclsh
+# $Id: querynick.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# querynick.tcl
+#
+# Purpose: CGI script to generate html form used to deal
+# with existing/taken nickname collision
+#
+
+# Input:
+set nick_vars {
+ {book "Missing address book"}
+ {nick {} ""}
+ {add {} 0}
+ {fn {} ""}
+ {addrs {} ""}
+ {fcc {} ""}
+ {comment {} ""}
+ {take {} 0}
+ {newnick {} ""}
+ {ai {} -1}
+}
+
+# Output:
+#
+
+# Command Menu
+set nick_menu {
+}
+
+set common_menu {
+ {
+ {}
+ {
+ {
+ cgi_puts "Get Help"
+ }
+ }
+ }
+}
+
+
+# Output:
+# Query prompt to deal with existing/taken nickname collision
+#
+#
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+WPEval $nick_vars {
+
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "Take to New Entry or List"
+ WPStyleSheets
+ }
+
+ cgi_body BGCOLOR="$_wp(bordercolor)" {
+
+ #catch {WPCmd PEInfo set help_context samenick}
+
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=get name=samenick target=_top {
+
+ cgi_table border=0 cellspacing=0 cellpadding=2 height="100%" {
+ cgi_table_row {
+ cgi_table_data valign=top class=navbar {
+ cgi_table bgcolor=$_wp(menucolor) border=0 cellspacing=0 cellpadding=2 {
+ # next comes the menu down the left side, with suitable
+ cgi_table_row {
+ eval {
+ cgi_table_data $_wp(menuargs) class=navbar {
+ WPTFCommandMenu nick_menu common_menu
+ }
+ }
+ }
+ }
+ }
+
+ cgi_table_data valign=top class=navbar {
+
+ cgi_text "page=addrsave" type=hidden notab
+ cgi_text "oncancel=main" type=hidden notab
+ cgi_text "take=1" type=hidden notab
+ cgi_text "ai=${ai}" type=hidden notab
+ cgi_text "book=${book}" type=hidden notab
+ cgi_text "nick=${nick}" type=hidden notab
+ cgi_text "fn=${fn}" type=hidden notab
+ cgi_text "addrs=${addrs}" type=hidden notab
+ cgi_text "fcc=${fcc}" type=hidden notab
+ cgi_text "comment=${comment}" type=hidden notab
+ cgi_text "newnick=${newnick}" type=hidden notab
+ cgi_text "cid=[WPCmd PEInfo key]" type=hidden notab
+
+ cgi_table border=0 cellspacing=0 cellpadding=2 width="100%" height="100%" {
+ cgi_table_row {
+ cgi_table_data valign=top align=center class=dialog {
+ cgi_table border=0 cellspacing=0 cellpadding=15 width="75%" {
+ cgi_table_row {
+ cgi_table_data align=center colspan=2 "xstyle=padding-top:20;padding-bottom:20" {
+ cgi_puts "An address book entry with the nickname \"[cgi_bold $newnick]\" already exists. At this point you may click either:"
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data align=right {
+ cgi_submit_button "replace=Replace Entry"
+ }
+ cgi_table_data "xstyle=padding:15" {
+ cgi_puts "Replace the \"[cgi_bold $newnick]\" address book entry with your [cgi_italic Take] selection."
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data align=right {
+ cgi_submit_button "replace=Add to Entry"
+ }
+ cgi_table_data "xstyle=padding:15" {
+ if {[string first "," $addrs] >= 0} {
+ set plur "es"
+ } else {
+ set plur ""
+ }
+
+ cgi_puts "Add the address${plur} from your [cgi_italic Take] selection to the existing entry's addresses to create a list."
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data align=right {
+ cgi_submit_button "replace=Edit"
+ }
+ cgi_table_data "xstyle=padding:15" {
+ cgi_puts "Go back to editing your [cgi_italic Take] selection."
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data align=right {
+ cgi_submit_button "cancel=Cancel"
+ }
+ cgi_table_data "xstyle=padding:15" {
+ cgi_puts "Or, Cancel your [cgi_italic Take] selection altogether."
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/queryprune.tcl b/web/cgi/alpine/1.0/queryprune.tcl
new file mode 100755
index 00000000..896bc333
--- /dev/null
+++ b/web/cgi/alpine/1.0/queryprune.tcl
@@ -0,0 +1,170 @@
+#!./tclsh
+# $Id: queryprune.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# queryprune.tcl
+#
+# After we've already determined that it's the
+# beginning of the month (logon.tcl), we check
+# what folders need pruning and offer them to
+# the user. Currently doesn't do automatic
+# reload.
+
+# Input:
+set prunetime_vars {
+ {cid "Missing Command ID"}
+ {start "Missing Start Page"}
+ {nojs "" 0}
+}
+
+# Output:
+#
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+set prune_menu {
+ {
+ {}
+ {
+ {
+ # * * * * DONE * * * *
+ cgi_submit_button prune=Continue
+ }
+ }
+ }
+}
+
+
+WPEval $prunetime_vars {
+ catch {WPCmd PEInfo prunetime} prunefldrs
+ set allclean 1
+ set delstuff 0
+ set askstuff 0
+ foreach prunefldr $prunefldrs {
+ if {[llength [lindex $prunefldr 1]] > 0 || [llength [lindex $prunefldr 2]] > 0} {
+ set allclean 0
+ }
+ }
+
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "Monthly Folder Pruning"
+ WPStdScripts
+ }
+
+ cgi_body BGCOLOR="$_wp(bordercolor)" {
+
+ cgi_form [file join $_wp(appdir) $_wp(ui1dir) wp] method=post name=pruneit target=_top {
+
+ cgi_table border=0 cellspacing=0 cellpadding=2 width="100%" height="100%" {
+
+ cgi_table_row {
+ # next comes the menu down the left side
+ eval {
+ cgi_table_data $_wp(menuargs) {
+ WPTFCommandMenu prune_menu {}
+ }
+ }
+
+ cgi_table_data valign=top align=center class=dialog "style=\"padding: 20\"" {
+
+ if {$allclean == 1} {
+ catch {WPCmd PEInfo statmsg "Pruning Failed: $prunefldrs"}
+ cgi_puts "No folders appear to need cleaning up this month."
+ cgi_br
+ cgi_puts "Please click [cgi_url "here" $start target=_top] to continue your session."
+ } else {
+ cgi_puts "At the beginning of every month, you are asked if you would like to clean up your sent-mail folder(s). Please answer the following questions and click [cgi_italic Continue]."
+ cgi_text "sessid=$_wp(sessid)" type=hidden notab
+ cgi_text "op=pruneit" type=hidden notab
+ cgi_text "cid=${cid}" type=hidden notab
+ cgi_text "page=prune" type=hidden notab
+ set cnt 0
+ foreach prunefldr $prunefldrs {
+ set type [lindex $prunefldr 0]
+ set mv [lindex $prunefldr 1]
+ set dellist [lindex $prunefldr 2]
+
+ cgi_table border=0 cellpadding=8 cellspacing=0 "style=\"padding-top: 8\"" {
+ if {[llength $mv] > 1} {
+ cgi_table_row {
+ cgi_table_data {
+ cgi_puts [cgi_bold "Move current &quot;[lindex $mv 0]&quot; to &quot;[lindex $mv 1]&quot;?"]
+ }
+ }
+ cgi_table_row {
+ cgi_table_data {
+ cgi_table "style=\"padding-left: 20\"" {
+ cgi_table_data {
+ cgi_radio_button "mv${cnt}=mv [lindex $mv 0] [lindex $mv 1]" checked class=body
+ }
+ cgi_table_data {
+ cgi_puts "Yes"
+ }
+ cgi_table_data {
+ cgi_radio_button "mv${cnt}=" class=body
+ }
+ cgi_table_data {
+ cgi_puts "No"
+ }
+ }
+ }
+ }
+ incr cnt
+ }
+ if {[llength $dellist] > 0} {
+ cgi_table_row {
+ cgi_table_data {
+ set plurtxt ""
+ set typetxt ""
+ if {[llength $dellist] > 1} {
+ set plurtxt "s"
+ }
+ if {[string compare $type ""] != 0} {
+ set typetxt "[string toupper $type] "
+ }
+ cgi_puts "[cgi_bold "To save disk space, delete the following ${typetxt}mail folder${plurtxt}:"] (Check to delete)"
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data {
+ cgi_table "style=\"padding-left: 20\"" {
+ foreach del $dellist {
+ cgi_table_row {
+ cgi_table_data {
+ cgi_checkbox "delList=$del" "style=\"background-color: #FFFFFF; padding-right: 8\""
+ cgi_puts "$del "
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ cgi_text "mvcnt=${cnt}" type=hidden notab
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/queryquit.tcl b/web/cgi/alpine/1.0/queryquit.tcl
new file mode 100755
index 00000000..8c86078c
--- /dev/null
+++ b/web/cgi/alpine/1.0/queryquit.tcl
@@ -0,0 +1,196 @@
+#!./tclsh
+# $Id: queryquit.tcl 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# queryquit.tcl
+#
+# Purpose: CGI script to generate html form used to confirm quitting
+# webpine while offering to expunge deleted
+
+# Input:
+# conftext :
+# params : array of key/value pairs to submit with form
+# oncancel : url to reference should user cancel confirmation
+set quit_vars {
+ {cid "Command ID"}
+}
+
+# Output:
+#
+# HTML/Javascript/CSS data representing the message specified
+# by the 'uid' argument
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+set query_menu {
+ {
+ {}
+ {
+ {
+ # * * * * OK * * * *
+ cgi_image_button quit=[WPimg but_create] border=0 alt="Create"
+ }
+ }
+ }
+ {
+ {}
+ {
+ {
+ # * * * * CANCEL * * * *
+ cgi_puts [cgi_url [cgi_img [WPimg but_cancel] border=0 alt="Cancel"] wp.tcl?${oncancel}]
+ }
+ }
+ }
+}
+
+WPEval $quit_vars {
+
+ if {$cid != [WPCmd PEInfo key]} {
+ error "Invalid Command ID"
+ }
+
+ catch {WPCmd PESession expungecheck quit} prompts
+
+ set qhid ""
+ set delsexist 0
+ set askinbox 1
+ set askcurrent 1
+ set ewc [WPCmd PEInfo feature expunge-without-confirm]
+ set ewce [WPCmd PEInfo feature expunge-without-confirm-everywhere]
+
+ foreach prompt $prompts {
+ if {[lindex $prompt 1] > 0} {
+ set delsexist 1
+ }
+ if {[lindex $prompt 1] > 0 && ($ewc || $ewce) && [lindex $prompt 2] == 1} {
+ set askinbox 0
+ lappend qhid [cgi_buffer {cgi_text expinbox=1 type=hidden notab}]
+ } elseif {[lindex $prompt 1] > 0 && [lindex $prompt 2] == 0 && ($ewce || ($ewc && [lindex $prompt 3] == 1))} {
+ set askcurrent 0
+ lappend qhid [cgi_buffer {cgi_text expcurrent=1 type=hidden notab}]
+ }
+ }
+
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "Quitting Web Alpine"
+ WPStdScripts
+ WPStyleSheets
+ cgi_put "<style type='text/css'>"
+ cgi_put ".expungebox { background-color:AntiqueWhite }"
+ cgi_put ".clickit { cursor: pointer }"
+ cgi_puts "</style>"
+ }
+
+ cgi_body BGCOLOR="$_wp(bordercolor)" {
+
+ #catch {WPCmd PEInfo set help_context quit}
+ cgi_table width=100% height=100% cellspacing=0 cellpadding=0 {
+ cgi_table_row {
+ cgi_table_data width=112 bgcolor=$_wp(bordercolor) {
+ cgi_put [cgi_img [WPimg dot2]]
+ }
+
+ cgi_table_data align=center valign=top bgcolor="$_wp(dialogcolor)" {
+
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/do_quit method=get id=quitting target=_top {
+ cgi_text cid=$cid type=hidden notab
+ cgi_text sessid=$sessid type=hidden notab
+ foreach q $qhid {
+ cgi_puts $q
+ }
+
+ cgi_table border=0 cellspacing=0 cellpadding=10 width="70%" class=dialog "style=padding-top:12%" {
+ cgi_table_row {
+ cgi_table_data valign=top {
+ cgi_puts [cgi_font size=+1 "Really Quit WebPine?"]
+ }
+ }
+ cgi_table_row {
+ cgi_table_data valign=top {
+ cgi_table cellpadding=10 border=0 {
+ if {$delsexist} {
+ cgi_table_row {
+ cgi_table_data valign=middle class=expungebox "style=\"border: 1px solid red\"" {
+ if {[llength $prompts] > 1} {
+ set ftext "[lindex [lindex $prompts 0] 0] and [lindex [lindex $prompts 1] 0]"
+ } else {
+ set ftext "[lindex [lindex $prompts 0] 0]"
+ }
+
+ cgi_put "This is a good opportunity to permanently remove from ${ftext} all of the messages you have marked for deletion."
+ cgi_br
+ cgi_br
+ cgi_table border=0 cellpadding=4 {
+ set expiexists 0
+ set expcexists 0
+ set inbhit 0
+ set curhit 0
+ foreach prompt $prompts {
+ set numdels [lindex $prompt 1]
+ set fname [lindex $prompt 0]
+ set inboxflag [lindex $prompt 2]
+ set incflag [lindex $prompt 3]
+ if {$inboxflag} {
+ incr inbhit
+ } else {
+ incr curhit
+ }
+ if {$numdels && (($askinbox && $inboxflag) || ($askcurrent && $inboxflag == 0))} {
+ cgi_table_row {
+ set cbn "cb[expr {$inbhit + $curhit}]"
+ cgi_table_data align=right valign=top {
+ if {$inboxflag} {
+ cgi_checkbox "expinbox" class=expungebox id=$cbn checked
+ incr expiexists
+ } else {
+ cgi_checkbox "expcurrent" class=expungebox id=$cbn checked
+ incr expcexists
+ }
+ }
+ cgi_table_data align=left {
+ set t "Expunge ${numdels} deleted message[expr {($numdels > 1) ? "s" : ""}] from ${fname}."
+ cgi_put [cgi_span class=clickit onclick=\"flipCheck('$cbn')\" $t]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data align=center valign=middle {
+ cgi_br
+ cgi_submit_button "quit=Yes, Quit Now"
+ cgi_put [cgi_img [WPimg dot2] width=10]
+ cgi_submit_button "cancel=No, Return to [WPCmd PEMailbox mailboxname]"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/queryrenfldr.tcl b/web/cgi/alpine/1.0/queryrenfldr.tcl
new file mode 100755
index 00000000..d84b303d
--- /dev/null
+++ b/web/cgi/alpine/1.0/queryrenfldr.tcl
@@ -0,0 +1,109 @@
+#!./tclsh
+# $Id: queryrenfldr.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# queryrenfldr.tcl
+#
+# Purpose: CGI script to generate html form used to confirm
+# folder creation
+
+# Input:
+set fldr_vars {
+ {fid "No Collection Specified"}
+}
+
+# Output:
+#
+# HTML/CSS data representing the message specified
+# by the 'uid' argument
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+set query_menu {
+ {
+ {}
+ {
+ {
+ # * * * * HELP * * * *
+ cgi_put "Get Help"
+ }
+ }
+ }
+}
+
+WPEval $fldr_vars {
+ if {[catch {WPCmd PEFolder collections} collections]} {
+ error [list _action "Collection list" $collections]
+ }
+
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "Folder Creation"
+ WPStyleSheets
+ }
+
+ cgi_body BGCOLOR="$_wp(bordercolor)" {
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=get name=confirm target=_top {
+ cgi_text "page=folders" type=hidden notab
+ cgi_text "cid=[WPCmd PEInfo key]" type=hidden
+ cgi_text "fid=$fid" type=hidden
+ cgi_text "frestore=1" type=hidden
+
+ cgi_table border=0 cellspacing=0 cellpadding=2 width="100%" height="100%" {
+ cgi_table_row {
+ eval {
+ cgi_table_data $_wp(menuargs) {
+ WPTFCommandMenu query_menu {}
+ }
+ }
+
+ cgi_table_data valign=top align=center class=dialog {
+ cgi_table border=0 cellspacing=0 cellpadding=2 width="70%" {
+ cgi_table_row {
+ cgi_table_data align=center {
+ cgi_puts [cgi_nl][cgi_nl][cgi_nl][cgi_nl]
+ cgi_puts "Please enter the new name of the folder '[lindex $fid end]'"
+ if {[llength $fid] > 2} {
+ cgi_put " in the directory '[join [lrange $fid 1 [expr {[llength $fid] - 2}]]]'"
+ }
+ if {[llength $collections] > 1} {
+ cgi_put " in the collection '[lindex [lindex $collections [lindex $fid 0]] 1]'."
+ } else {
+ cgi_put "."
+ }
+ cgi_br
+ cgi_br
+ cgi_put "Rename [cgi_bold [lindex $fid end]] to: "
+ cgi_text folder= maxlength=64 size=25%
+ cgi_br
+ cgi_br
+ cgi_puts "Click 'Rename' to permanently change the folder name, or 'Cancel' to return to the Folder List."
+ cgi_br
+ cgi_br
+ cgi_submit_button rename=Rename
+ cgi_submit_button rename=Cancel
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/querysave.tcl b/web/cgi/alpine/1.0/querysave.tcl
new file mode 100755
index 00000000..0bfdf59d
--- /dev/null
+++ b/web/cgi/alpine/1.0/querysave.tcl
@@ -0,0 +1,98 @@
+#!./tclsh
+# $Id: querysave.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# querysave.tcl
+#
+# Purpose: CGI script to generate html form used to gather folder
+# name and collection for aggregate save
+
+# Input:
+# conftext :
+# params : array of key/value pairs to submit with form
+# oncancel : url to reference should user cancel dialog
+set qsave_vars {
+}
+
+# Output:
+#
+# HTML/CSS data representing the form for save folder dialog
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+set query_menu {
+ {
+ {}
+ {
+ {
+ # * * * * OK * * * *
+ cgi_image_button save=[WPimg but_save] border=0 alt="Save"
+ }
+ }
+ }
+ {
+ {}
+ {
+ {
+ # * * * * CANCEL * * * *
+ cgi_puts [cgi_url [cgi_img [WPimg but_cancel] border=0 alt="Cancel"] wp.tcl?${oncancel}]
+ }
+ }
+ }
+}
+
+WPEval $qsave_vars {
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "Aggregate Save"
+ WPStyleSheets
+ }
+
+ cgi_body BGCOLOR="$_wp(bordercolor)" {
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=get {
+ if {[info exists params]} {
+ foreach p $params {
+ cgi_text "[lindex $p 0]=[lindex $p 1]" type=hidden notab
+ }
+ }
+
+ cgi_table border=0 cellspacing=0 cellpadding=2 width="100%" height="100%" {
+ cgi_table_row {
+ cgi_table_data valign=top align=center class=dialog {
+ cgi_text "page=selsave" type=hidden notab
+ cgi_text "by=text" type=hidden notab
+ cgi_text "postpage=index" type=hidden notab
+
+ cgi_puts [cgi_nl][cgi_nl][cgi_nl][cgi_nl]
+ cgi_puts "You are attempting to Save to a folder, '$folder', that does not exist."
+ cgi_br
+ cgi_puts "[cgi_nl]Click 'Create' to create the folder and save the message, or 'Cancel' to abort the save."
+ }
+ cgi_table_row {
+ cgi_table_data {
+ cgi_submit_button save=Save
+ cgi_submit_button cancel=Cancel
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/web/cgi/alpine/1.0/resume.tcl b/web/cgi/alpine/1.0/resume.tcl
new file mode 100755
index 00000000..7a45333a
--- /dev/null
+++ b/web/cgi/alpine/1.0/resume.tcl
@@ -0,0 +1,169 @@
+#!./tclsh
+# $Id: resume.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# resume.tcl
+#
+# Purpose: CGI script to browse postponed messages
+
+# Input:
+set resume_vars {
+ {oncancel "" main.tcl}
+}
+
+# Output:
+#
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+
+set resume_cmds {
+ {
+ {}
+ {
+ {
+ cgi_puts [cgi_url "Get Help" wp.tcl?page=help&oncancel=resume target=_top class=navbar]
+ }
+ }
+ }
+}
+
+WPEval $resume_vars {
+
+ if {![info exists oncancel]} {
+ WPLoadCGIVarAs oncancel oncancel
+ }
+
+ if {[catch {WPCmd PEPostpone list} postponed]} {
+ error [list _action Resume $postponed "Click Back button to try again."]
+ }
+
+ set charset [lindex $postponed 1]
+
+ cgi_http_head {
+ WPStdHttpHdrs "text/html; charset=\"$charset\""
+ }
+
+ cgi_html {
+ cgi_head {
+ cgi_http_equiv Content-Type "text/html; charset=$charset"
+ WPStdHtmlHdr "Postponed Messages"
+ WPStyleSheets
+ cgi_puts "<style type='text/css'>"
+ cgi_puts ".gradient { background-image: url('[WPimg indexhdr]') ; background-repeat: repeat-x }"
+ cgi_puts "</style>"
+ }
+
+ cgi_body BGCOLOR="$_wp(bordercolor)" {
+
+ catch {WPCmd PEInfo set help_context resume}
+
+ cgi_table border=0 cellspacing=0 cellpadding=0 width="100%" height="100%" {
+ cgi_table_row {
+ # next comes the menu down the left side
+ eval {
+ cgi_table_data $_wp(menuargs) {
+ WPTFCommandMenu resume_cmds {}
+ }
+ }
+
+ cgi_table_data width=100% valign=top class=dialog {
+
+ cgi_table border=0 cellspacing=0 cellpadding=0 width=100% {
+
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=post target=_top {
+ cgi_text "page=compose" type=hidden notab
+ cgi_text "style=Postponed" type=hidden notab
+ cgi_text "cid=[WPCmd PEInfo key]" type=hidden notab
+ cgi_text "oncancel=$oncancel" type=hidden notab
+
+
+ set n 1
+ set fmt {{to Recipient} {date Date} {subj Subject}}
+ cgi_table_row class=\"gradient\" {
+ cgi_table_data class=indexhdr height=$_wp(indexheight) {
+ cgi_puts [cgi_nbspace]
+ }
+
+ foreach i $fmt {
+ cgi_table_data class=indexhdr {
+ cgi_puts [lindex $i 1]
+ }
+ }
+ }
+
+ set checked ""
+ set last [llength [lindex $postponed 0]]
+ for {set i 0} {$i < $last} {incr i} {
+
+ # cgi_html_comment [lindex [lindex $postponed 0] $i]
+ array set pa [join [lindex [lindex $postponed 0] $i]]
+
+ if {[info exists pa(uid)] == 0} {
+ continue;
+ }
+
+ if {$i % 2} {
+ set bgcolor #EEEEEE
+ } else {
+ set bgcolor #FFFFFF
+ }
+
+ if {[expr $i + 1] == $last} {
+ set checked checked
+ }
+
+ cgi_table_row bgcolor=$bgcolor {
+ cgi_table_data valign=top nowrap bgcolor=$bgcolor {
+ cgi_radio_button "uid=$pa(uid)" style="background-color:$bgcolor" $checked
+ }
+
+ foreach j $fmt {
+ cgi_table_data bgcolor=$bgcolor {
+ if {[info exists pa([lindex $j 0])]} {
+ cgi_puts $pa([lindex $j 0])
+ } else {
+ cgi_puts [cgi_nbspace]
+ }
+ }
+ }
+ }
+
+ set checked ""
+ }
+
+ cgi_table_row {
+ cgi_table_data align=center colspan=24 {
+ cgi_br
+ cgi_submit_button "resume=Resume Chosen Message"
+ }
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data align=center colspan=24 {
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=get target=_top {
+ cgi_text "page=$oncancel" type=hidden notab
+ cgi_br
+ cgi_submit_button "cancel=Cancel"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/ripcord.tcl b/web/cgi/alpine/1.0/ripcord.tcl
new file mode 100755
index 00000000..3514d20e
--- /dev/null
+++ b/web/cgi/alpine/1.0/ripcord.tcl
@@ -0,0 +1,64 @@
+#!./tclsh
+# $Id: ripcord.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# ripcord.tcl
+#
+# Purpose: CGI script to generate html page used to arm
+# short server timeout
+
+# Input:
+set rip_vars {
+ {t "" 10}
+ {cid "Command ID"}
+}
+
+# Output:
+#
+# HTML/CSS data representing the form
+
+# inherit global config
+source ./alpine.tcl
+
+WPEval $rip_vars {
+
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "Adjusting Session Timeout"
+ }
+
+ cgi_body BGCOLOR=#ffffff {
+ if {[string compare $cid [WPCmd PEInfo key]]} {
+ cgi_put "Messing around, heh?"
+ } else {
+ cgi_put "Making Web Alpine Server Adjustments."
+ cgi_br
+ cgi_put "This should only take a momment..."
+ if {[catch {WPCmd PESession abandon 10}] == 0} {
+ set gonow 1
+ }
+ }
+
+ cgi_script type="text/javascript" language="JavaScript" {
+ if {[info exists gonow]} {
+ cgi_puts "window.close();"
+ } else {
+ cgi_puts "window.setInterval('window.close()',5000);"
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/seldate.tcl b/web/cgi/alpine/1.0/seldate.tcl
new file mode 100755
index 00000000..e8e07c6f
--- /dev/null
+++ b/web/cgi/alpine/1.0/seldate.tcl
@@ -0,0 +1,193 @@
+#!./tclsh
+# $Id: seldate.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# seldate.tcl
+#
+# Purpose: CGI script to generate html form used to gather info
+# for date selection
+
+# Input:
+set select_vars {
+ {uid "" 0}
+}
+
+# Output:
+#
+# HTML/CSS data representing form for date select input
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+WPEval $select_vars {
+
+ if {$uid} {
+ if {[catch {WPCmd PEMessage $uid number} thisnum]} {
+ set uid 0
+ }
+ } else {
+ set thisnum ""
+ }
+
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "Search By Date"
+ WPStyleSheets
+ }
+
+ cgi_body BGCOLOR="$_wp(bordercolor)" {
+
+ catch {WPCmd PEInfo set wp_index_script fr_seldate.tcl}
+ catch {WPCmd PEInfo set help_context seldate}
+
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=get name=auth target=body {
+ cgi_text page=index type=hidden notab
+ cgi_text doselect=1 type=hidden notab
+ cgi_text by=date type=hidden notab
+ if {![WPCmd PEMailbox selected]} {
+ cgi_text result=broad type=hidden notab
+ }
+
+ cgi_table border=0 cellspacing=0 cellpadding=2 width="100%" height="100%" {
+ cgi_table_row {
+ cgi_table_data align=center valign=top class=dialog {
+ cgi_table width="80%" {
+ cgi_table_row {
+ cgi_table_data {
+ cgi_center {
+ cgi_puts "[cgi_nl]This page provides a way to search for messages in [cgi_bold [WPCmd PEMailbox mailboxname]] based on arrival time."
+ cgi_puts "[cgi_nl][cgi_nl]Messages arriving [cgi_italic On] the date entered below will be marked with a check in the box next to their line in the Message List. Choosing [cgi_italic Since] marks messages arriving between today and the giving date (including the given date). Choosing [cgi_italic Before] marks messages arriving before (but not on) the given date."
+ cgi_puts "[cgi_nl][cgi_nl]Choose a date below and click 'Search' to choose messages, or 'Cancel' to return to the Message List.[cgi_nl][cgi_nl]"
+ }
+ }
+ }
+
+ if {[WPCmd PEMailbox selected]} {
+ cgi_table_row class=dialog {
+ cgi_table_data colspan=2 align=center valign=middle class=dialog {
+ cgi_put [cgi_font face=tahoma,verdana,geneva "Since some messages are already marked, choose whether criteria specified here should "]
+ cgi_select result {
+ cgi_option "search all messages in '[WPCmd PEMailbox mailboxname]'" value=broad selected
+ cgi_option "search within marked messages only." value=narrow
+ cgi_option "discard previous marks and search anew." value=new
+ }
+
+ cgi_br
+ cgi_br
+ cgi_br
+ }
+ }
+ }
+
+ cgi_table_row class=dialog {
+ cgi_table_data valign=top align=center class=dialog {
+ cgi_put [cgi_font face=tahoma,verdana,geneva "Messages dated "]
+
+ cgi_select datecase {
+ foreach i {On Since Before} {
+ cgi_option $i value=[string tolower $i]
+ }
+ }
+
+ cgi_br
+ cgi_br
+
+ cgi_select datemon {
+ if {$uid} {
+ set today [string tolower [WPCmd PEMessage $uid date month]]
+ } else {
+ set today [string tolower [clock format [clock seconds] -format %b]]
+ }
+
+ set months {
+ January jan
+ February feb
+ March mar
+ April Apr
+ May may
+ June jun
+ July jul
+ August aug
+ September sep
+ October oct
+ November nov
+ December dec
+ }
+
+ foreach {x y} $months {
+ if {$y == $today} {
+ cgi_option $x value=$y selected
+ } else {
+ cgi_option $x value=$y
+ }
+ }
+ }
+
+ cgi_select dateday {
+ if {$uid} {
+ set today [WPCmd PEMessage $uid date day]
+ } else {
+ set today [clock format [clock seconds] -format %d]
+ }
+
+ for {set i 1} {$i <= 31} {incr i} {
+ set v [format "%.2d" $i]
+ if {$v == $today} {
+ cgi_option $i value=$v selected
+ } else {
+ cgi_option $i value=$v
+ }
+ }
+ }
+
+ cgi_put ",[cgi_nbspace]"
+ cgi_select dateyear {
+ if {$uid} {
+ set now [WPCmd PEMessage $uid date year]
+ } else {
+ set now [clock format [clock seconds] -format "%Y"]
+ }
+
+ cgi_option $now value=$now selected
+ for {set n [expr $now - 1]} {$n >= 1970} {incr n -1} {
+ cgi_option $n value=$n
+ }
+ }
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data align=center {
+ cgi_br
+ cgi_submit_button ok=Search
+ cgi_submit_button cancel=Cancel
+ }
+ }
+
+ cgi_table_row class=dialog {
+ cgi_table_data valign=top align=center class=dialog {
+ cgi_puts [cgi_nl][cgi_nl][cgi_font size=-1 "Note, if the number of messages in this folder is larger than the number of lines in the Message List, then some matching messages may not be visible without paging/scrolling."]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/select.tcl b/web/cgi/alpine/1.0/select.tcl
new file mode 100755
index 00000000..915254c7
--- /dev/null
+++ b/web/cgi/alpine/1.0/select.tcl
@@ -0,0 +1,303 @@
+#!./tclsh
+# $Id: select.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# select.tcl
+#
+# Purpose: CGI script to generate html form used to gather info
+# for message searching selection
+
+# Input:
+set select_vars {
+}
+
+# Output:
+#
+# HTML/CSS data representing form for text select input
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+WPEval $select_vars {
+ set selcount [WPCmd PEMailbox selected]
+
+ # given a uid, called from View page so use it for defaults
+ # otherwise if only one selected, use it for defaults
+ set thisuid 0
+ # leave disabled for now
+ if {0 && $uid > 0} {
+ set thisuid $uid
+ } elseif {$selcount == 1} {
+ }
+
+ if {$thisuid} {
+ if {[catch {WPCmd PEMessage $thisuid number} thisnum]} {
+ set thisuid 0
+ }
+ } else {
+ set thisnum ""
+ }
+
+
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "Select By Text"
+ WPStyleSheets
+ cgi_put "<style type='text/css'>"
+ cgi_put ".standout { width: 90%; text-align: center; font-size: smaller; border: 1px solid #663333; background-color: #ffcc66; padding-bottom: 8; margin-top: 8; margin-bottom: 12 }"
+ cgi_puts "</style>"
+ }
+
+ cgi_body BGCOLOR="$_wp(bordercolor)" class=dialog "style=\"padding-left: 8%; padding-right: 8%\"" {
+
+ #catch {WPCmd PEInfo set wp_index_script fr_select.tcl}
+ catch {WPCmd PEInfo set help_context select}
+
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=get name=auth target=body {
+ cgi_text "page=index" type=hidden notab
+ cgi_text "doselect=1" type=hidden notab
+ set mailboxname [WPCmd PEMailbox mailboxname]
+ cgi_division align=center class=dialog "style=\"padding-top:6; padding-bottom:8\"" {
+ cgi_puts "This page provides a way to search for specific messages within the currently open folder, [cgi_bold $mailboxname]. Simply fill in the criteria below and click the associated [cgi_italic Search] button. All messages matching the criteria will be marked with a check in the box next to their line in the Message List.[cgi_nl][cgi_nl]"
+ cgi_puts "Click [cgi_italic Cancel] to return to the Message List without searching."
+ }
+
+ if {$selcount > 0} {
+ cgi_center {
+ cgi_division class=standout {
+ cgi_put "The folder '$mailboxname' has ${selcount} message"
+ if {[string length [WPplural $selcount]]} {
+ cgi_put "s with their checkboxes marked."
+ } else {
+ cgi_put " with its checkbox marked."
+ }
+
+ cgi_put "[cgi_nl]The Search specified below should"
+
+ cgi_select result {
+ cgi_option "apply to entire folder, adding result to those now marked" value=broad selected
+ cgi_option "apply only to marked messages, unmarking messages not matched" value=narrow
+ cgi_option "discard previous marks and search anew" value=new
+ }
+ }
+ }
+ } else {
+ cgi_text result=broad type=hidden notab
+ }
+
+ cgi_put "<fieldset>"
+ cgi_put "<legend>[cgi_bold "Search for Text in Message Headers or Body"]</legend>"
+ cgi_center {
+ cgi_put [cgi_font class=dialog "Select messages with text "]
+ cgi_select textcase {
+ cgi_option "in" value=ton
+ cgi_option "NOT in" value=not
+ }
+
+ cgi_br
+ cgi_br
+
+ cgi_put [cgi_font face=tahoma,verdana,geneva "the message's "]
+ if {$thisuid} {
+ set fromaddr [WPCmd PEMessage $thisuid fromaddr]
+ set deftext $fromaddr
+ } else {
+ if {[catch {WPCmd PEInfo set wp_def_search_text} deftext]} {
+ set deftext ""
+ }
+ }
+
+ set fields {
+ {Subject: field} subj ""
+ {From: field} from selected
+ {To: field} to ""
+ {Cc: field} cc ""
+ {recipient fields} recip ""
+ {participant fields} partic ""
+ {text, anywhere} any ""
+ }
+
+ cgi_select field {
+ foreach {x y z} $fields {
+ cgi_option $x value=$y $z
+ }
+ }
+
+ cgi_br
+ cgi_br
+ cgi_put [cgi_font face=tahoma,verdana,geneva "matching "]
+ cgi_text text=$deftext size=20 maxlength=256
+
+ if {$thisuid} {
+ set ft [WPJSQuote $fromaddr]
+ set tt [WPJSQuote [WPCmd PEMessage $thisuid toaddr]]
+ set st [WPJSQuote [WPCmd PEMessage $thisuid subject]]
+ if {[string length $ft] || [string length $tt] || [string length $st]} {
+ cgi_put "[cgi_nl]Using "
+ cgi_select defs {
+ cgi_option "- Nothing -" ""
+ if {[string length $ft]} {
+ cgi_option "From Address" value=$ft selected
+ }
+ if {[string length $tt]} {
+ cgi_option "To Address" value=$tt
+ }
+ if {[string length $st]} {
+ cgi_option "Subject Text" value=$st
+ }
+ }
+ cgi_put " of message ${thisnum}"
+ }
+ }
+
+ cgi_br
+ cgi_br
+ cgi_submit_button "selectop=Search Text"
+ cgi_submit_button cancel=Cancel
+ }
+ cgi_put "</fieldset>"
+
+ cgi_put "<fieldset>"
+ cgi_put "<legend>[cgi_bold "Search for Messages by Date"]</legend>"
+ cgi_center {
+ cgi_put "Messages dated "
+
+ cgi_select datecase {
+ foreach i {On Since Before} {
+ cgi_option $i value=[string tolower $i]
+ }
+ }
+
+ cgi_br
+ cgi_br
+
+ cgi_select datemon {
+ if {$thisuid} {
+ set today [string tolower [WPCmd PEMessage $thisuid date month]]
+ } else {
+ set today [string tolower [clock format [clock seconds] -format %b]]
+ }
+
+ set months {
+ January jan
+ February feb
+ March mar
+ April Apr
+ May may
+ June jun
+ July jul
+ August aug
+ September sep
+ October oct
+ November nov
+ December dec
+ }
+
+ foreach {x y} $months {
+ if {$y == $today} {
+ cgi_option $x value=$y selected
+ } else {
+ cgi_option $x value=$y
+ }
+ }
+ }
+
+ cgi_select dateday {
+ if {$thisuid} {
+ set today [WPCmd PEMessage $thisuid date day]
+ } else {
+ set today [clock format [clock seconds] -format %d]
+ }
+
+ for {set i 1} {$i <= 31} {incr i} {
+ set v [format "%.2d" $i]
+ if {$v == $today} {
+ cgi_option $i value=$v selected
+ } else {
+ cgi_option $i value=$v
+ }
+ }
+ }
+
+ cgi_put ",[cgi_nbspace]"
+ cgi_select dateyear {
+ if {$thisuid} {
+ set now [WPCmd PEMessage $thisuid date year]
+ } else {
+ set now [clock format [clock seconds] -format "%Y"]
+ }
+
+ cgi_option $now value=$now selected
+ for {set n [expr $now - 1]} {$n >= 1970} {incr n -1} {
+ cgi_option $n value=$n
+ }
+ }
+
+ cgi_br
+ cgi_br
+ cgi_submit_button "selectop=Search Date"
+ cgi_submit_button cancel=Cancel
+ }
+ cgi_put "</fieldset>"
+
+ cgi_put "<fieldset>"
+ cgi_put "<legend>[cgi_bold "Search for Messages with Certain Status Settings"]</legend>"
+ cgi_center {
+ cgi_puts [cgi_font face=tahoma,verdana,geneva "Messages "]
+ cgi_select statcase {
+ cgi_option "flagged" value=ton
+ cgi_option "NOT flagged" value=not
+ }
+
+ cgi_puts "[cgi_font face=tahoma,verdana,geneva " :"][cgi_nl][cgi_nl]"
+
+ cgi_table border=0 cellpadding=2 cellspacing=0 {
+ set statuses {
+ Important imp
+ New new
+ Answered ans
+ Deleted del
+ }
+
+ foreach {x y} $statuses {
+ cgi_table_row {
+ cgi_table_data align=right width="42%" {
+ cgi_radio_button flag=$y
+ }
+
+ cgi_table_data align=left {
+ cgi_put $x
+ }
+ }
+ }
+ }
+
+ cgi_br
+ cgi_submit_button "selectop=Search Status"
+ cgi_submit_button cancel=Cancel
+ }
+ cgi_put "</fieldset>"
+
+ cgi_center {
+ cgi_division class=standout {
+ cgi_puts "Note, if the number of messages in this folder is larger than the number of lines in the Message[cgi_nbspace]List, then some matching messages may not be visible without paging/scrolling."
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/select2.tcl b/web/cgi/alpine/1.0/select2.tcl
new file mode 100755
index 00000000..745254e7
--- /dev/null
+++ b/web/cgi/alpine/1.0/select2.tcl
@@ -0,0 +1,318 @@
+#!./tclsh
+# $Id: select2.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# select.tcl
+#
+# Purpose: CGI script to generate html form used to gather info
+# for message searching selection
+
+# Input:
+set select_vars {
+}
+
+# Output:
+#
+# HTML/CSS data representing form for text select input
+
+# coerce uid to zero since there's not method in WPL yet to initiate
+# a search from a particular message.
+set uid 0
+
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+WPEval $select_vars {
+ if {$uid} {
+ if {[catch {WPCmd PEMessage $uid number} thisnum]} {
+ set uid 0
+ }
+ } else {
+ set thisnum ""
+ }
+
+
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "Select By Text"
+ WPStyleSheets
+ }
+
+ cgi_body BGCOLOR="$_wp(bordercolor)" {
+
+ catch {WPCmd PEInfo set wp_index_script fr_select.tcl}
+ catch {WPCmd PEInfo set help_context select}
+
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=get name=auth target=body {
+ cgi_text "page=index" type=hidden notab
+ cgi_text "doselect=1" type=hidden notab
+ cgi_table border=0 cellspacing=0 cellpadding=2 width="100%" height="100%" {
+ cgi_table_row {
+ cgi_table_data align=center valign=top class=dialog {
+ cgi_table width="80%" {
+ cgi_table_row {
+ cgi_table_data {
+ cgi_center {
+ cgi_puts "[cgi_nl][cgi_nl]This page provides a way to search for specific messsages within the currently open folder, [cgi_bold [WPCmd PEMailbox mailboxname]]. Simply fill in the criteria below and click the assoicated [cgi_italic Search] button. All messages matching the criteria will be marked with a check in the box next to their line in the Message List.[cgi_nl][cgi_nl]"
+ cgi_puts "Click [cgi_italic Cancel] to return to the Message List without searching.[cgi_nl]"
+ }
+ }
+ }
+
+ if {[WPCmd PEMailbox selected]} {
+ cgi_table_row class=dialog {
+ cgi_table_data colspan=2 align=center valign=middle class=dialog {
+ cgi_put [cgi_font face=tahoma,verdana,geneva "Since some messages are already marked, choose whether criteria specified here should "]
+ cgi_select result {
+ cgi_option "search all messages in '[WPCmd PEMailbox mailboxname]'" value=broad selected
+ cgi_option "search within marked messages only." value=narrow
+ cgi_option "discard previous marks and search anew." value=new
+ }
+
+ cgi_br
+ cgi_br
+ }
+ }
+ } else {
+ cgi_text result=broad type=hidden notab
+ }
+
+ cgi_table_row class=dialog {
+ cgi_table_data {
+ cgi_table width=100% border=2 cellpadding=8 {
+ cgi_table_row {
+ cgi_table_data bgcolor=#CC9900 {
+ cgi_radio_button by=text
+ }
+
+ cgi_table_data valign=top align=center nowrap class=dialog {
+ cgi_put [cgi_bold "Search for Text in Message Headers or Body"]
+ cgi_br
+ cgi_br
+ cgi_put "Select messages with text "
+ cgi_select textcase {
+ cgi_option "in" value=ton
+ cgi_option "NOT in" value=not
+ }
+
+ cgi_br
+ cgi_br
+
+ cgi_put [cgi_font face=tahoma,verdana,geneva "the message's "]
+ if {$uid} {
+ set fromaddr [WPCmd PEMessage $uid fromaddr]
+ set deftext $fromaddr
+ } else {
+ set deftext ""
+ }
+
+ set fields {
+ {Subject: field} subj ""
+ {From: field} from selected
+ {To: field} to ""
+ {Cc: field} cc ""
+ {recipient fields} recip ""
+ {participant fields} partic ""
+ {text, anywhere} any ""
+ }
+
+ cgi_select field {
+ foreach {x y z} $fields {
+ cgi_option $x value=$y $z
+ }
+ }
+
+ cgi_br
+ cgi_br
+ cgi_put [cgi_font face=tahoma,verdana,geneva "matching "]
+ cgi_text text=$deftext size=20 maxlength=256
+
+ if {$uid} {
+ set ft [WPJSQuote $fromaddr]
+ set tt [WPJSQuote [WPCmd PEMessage $uid toaddr]]
+ set st [WPJSQuote [WPCmd PEMessage $uid subject]]
+ if {[string length $ft] || [string length $tt] || [string length $st]} {
+ cgi_put "[cgi_nl]Using "
+ cgi_select defs {
+ cgi_option "- Nothing -" ""
+ if {[string length $ft]} {
+ cgi_option "From Address" value=$ft selected
+ }
+ if {[string length $tt]} {
+ cgi_option "To Address" value=$tt
+ }
+ if {[string length $st]} {
+ cgi_option "Subject Text" value=$st
+ }
+ }
+ cgi_put " of message ${thisnum}"
+ }
+ }
+ }
+ }
+
+ cgi_table_row class=dialog {
+ cgi_table_data bgcolor=#CC9900 {
+ cgi_radio_button by=date
+ }
+
+ cgi_table_data valign=top align=center class=dialog {
+ cgi_put [cgi_bold "Search for Messages by Date"]
+ cgi_br
+ cgi_br
+ cgi_put "Messages dated "
+
+ cgi_select datecase {
+ foreach i {On Since Before} {
+ cgi_option $i value=[string tolower $i]
+ }
+ }
+
+ cgi_br
+ cgi_br
+
+ cgi_select datemon {
+ if {$uid} {
+ set today [string tolower [WPCmd PEMessage $uid date month]]
+ } else {
+ set today [string tolower [clock format [clock seconds] -format %b]]
+ }
+
+ set months {
+ January jan
+ February feb
+ March mar
+ April Apr
+ May may
+ June jun
+ July jul
+ August aug
+ September sep
+ October oct
+ November nov
+ December dec
+ }
+
+ foreach {x y} $months {
+ if {$y == $today} {
+ cgi_option $x value=$y selected
+ } else {
+ cgi_option $x value=$y
+ }
+ }
+ }
+
+ cgi_select dateday {
+ if {$uid} {
+ set today [WPCmd PEMessage $uid date day]
+ } else {
+ set today [clock format [clock seconds] -format %d]
+ }
+
+ for {set i 1} {$i <= 31} {incr i} {
+ set v [format "%.2d" $i]
+ if {$v == $today} {
+ cgi_option $i value=$v selected
+ } else {
+ cgi_option $i value=$v
+ }
+ }
+ }
+
+ cgi_put ",[cgi_nbspace]"
+ cgi_select dateyear {
+ if {$uid} {
+ set now [WPCmd PEMessage $uid date year]
+ } else {
+ set now [clock format [clock seconds] -format "%Y"]
+ }
+
+ cgi_option $now value=$now selected
+ for {set n [expr $now - 1]} {$n >= 1970} {incr n -1} {
+ cgi_option $n value=$n
+ }
+ }
+ }
+ }
+
+ cgi_table_row class=dialog {
+ cgi_table_data bgcolor=#CC9900 {
+ cgi_radio_button by=status
+ }
+
+ cgi_table_data class=dialog align=center {
+ cgi_put [cgi_bold "Search for Messages with Certain Flag Settings"]
+ cgi_br
+ cgi_br
+ cgi_puts [cgi_font face=tahoma,verdana,geneva "Messages "]
+ cgi_select statcase {
+ cgi_option "flagged" value=ton
+ cgi_option "NOT flagged" value=not
+ }
+
+ cgi_puts [cgi_font face=tahoma,verdana,geneva " :"]
+
+ cgi_table {
+ set statuses {
+ Important imp
+ New new
+ Answered ans
+ Deleted del
+ }
+
+ foreach {x y} $statuses {
+ cgi_table_row {
+ cgi_table_data align=right width="42%" {
+ cgi_radio_button flag=$y
+ }
+
+ cgi_table_data align=left {
+ cgi_put $x
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ cgi_table_row class=dialog {
+ cgi_table_data valign=top align=center nowrap class=dialog {
+ cgi_br
+ cgi_submit_button ok=Search
+ cgi_submit_button cancel=Cancel
+ }
+ }
+
+ cgi_table_row class=dialog {
+ cgi_table_data colspan=2 valign=top align=center class=dialog {
+ cgi_br
+ cgi_br
+ cgi_puts [cgi_nl][cgi_nl][cgi_font size=-1 "Note, if the number of messages in this folder is larger than the number of lines in the Message List, then some matching messages may not be visible without paging/scrolling."]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/selstat.tcl b/web/cgi/alpine/1.0/selstat.tcl
new file mode 100755
index 00000000..0af0be61
--- /dev/null
+++ b/web/cgi/alpine/1.0/selstat.tcl
@@ -0,0 +1,156 @@
+#!./tclsh
+# $Id: selstat.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# selstat.tcl
+#
+# Purpose: CGI script to generate html form used to gather info
+# for status selection
+
+# Input:
+set select_vars {
+ {uid "" 0}
+}
+
+# Output:
+#
+# HTML/CSS data representing form for status select input
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+WPEval $select_vars {
+ if {$uid} {
+ if {[catch {WPCmd PEMessage $uid number} thisnum]} {
+ set uid 0
+ }
+ } else {
+ set thisnum ""
+ }
+
+
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "Search By Status"
+ WPStyleSheets
+ }
+
+ cgi_body BGCOLOR="$_wp(bordercolor)" {
+
+ catch {WPCmd PEInfo set help_context selstat}
+ catch {WPCmd PEInfo set wp_index_script fr_selstat.tcl}
+
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=get name=auth target=body {
+ cgi_text page=index type=hidden notab
+ cgi_text doselect=1 type=hidden notab
+ cgi_text by=status type=hidden notab
+ cgi_table border=0 cellspacing=0 cellpadding=2 width="100%" height="100%" {
+ cgi_table_row {
+ cgi_table_data align=center valign=top class=dialog {
+ cgi_table width="80%" {
+ cgi_table_row {
+ cgi_table_data colspan=2 {
+ cgi_center {
+ cgi_puts "[cgi_nl][cgi_nl]This page provides a way to search for messages in [cgi_bold [WPCmd PEMailbox mailboxname]] based on their status flags. Messages matching the status selected below will be marked with a check in the box next to their line in the Message List."
+ cgi_puts "[cgi_nl][cgi_nl]Choose the status criteria below and click 'Search', or 'Cancel' to return to the Message List.[cgi_nl][cgi_nl]"
+ }
+ }
+
+ if {[WPCmd PEMailbox selected]} {
+ cgi_table_row class=dialog {
+ cgi_table_data colspan=2 align=center valign=middle class=dialog colspan=2 {
+ cgi_put [cgi_font face=tahoma,verdana,geneva "Since some messages are already marked, choose whether criteria specified here should "]
+ cgi_select result {
+ cgi_option "search all messages in '[WPCmd PEMailbox mailboxname]'" value=broad selected
+ cgi_option "search within marked messages only." value=narrow
+ cgi_option "discard previous marks and search anew." value=new
+ }
+
+ cgi_br
+ cgi_br
+ }
+ }
+ } else {
+ cgi_text result=broad type=hidden notab
+ }
+
+ cgi_table_row class=dialog {
+ cgi_table_data valign=top align=center nowrap class=dialog colspan=2 {
+ cgi_puts [cgi_font face=tahoma,verdana,geneva "Search for messages "]
+ cgi_select statcase {
+ cgi_option "flagged" value=ton
+ cgi_option "NOT flagged" value=not
+ }
+
+ cgi_puts [cgi_font face=tahoma,verdana,geneva " :"]
+ }
+ }
+
+ set statuses {
+ Important imp
+ New new
+ Answered ans
+ Deleted del
+ }
+
+ if {0} {
+ cgi_table_row {
+ cgi_table_data align=center colspan=2 {
+ cgi_select flag {
+ foreach {x y} $statuses {
+ cgi_option $x value=$y
+ }
+ }
+ }
+ }
+ } else {
+ foreach {x y} $statuses {
+ cgi_table_row {
+ cgi_table_data align=right width="42%" {
+ cgi_radio_button flag=$y
+ }
+
+ cgi_table_data align=left {
+ cgi_put $x
+ }
+ }
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data class=dialog align=center colspan=2 {
+ cgi_br
+ cgi_submit_button ok=Search
+ cgi_submit_button cancel=Cancel
+ }
+ }
+ }
+
+ cgi_table_row class=dialog {
+ cgi_table_data valign=top align=center class=dialog colspan=2 {
+ cgi_puts [cgi_nl][cgi_nl][cgi_font size=-1 "Note, if the number of messages in this folder is larger than the number of lines in the Message List, then some matching messages may not be visible without paging/scrolling."]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
diff --git a/web/cgi/alpine/1.0/seltext.tcl b/web/cgi/alpine/1.0/seltext.tcl
new file mode 100755
index 00000000..e0ce33bb
--- /dev/null
+++ b/web/cgi/alpine/1.0/seltext.tcl
@@ -0,0 +1,181 @@
+#!./tclsh
+# $Id: seltext.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# seltext.tcl
+#
+# Purpose: CGI script to generate html form used to gather info
+# for text selection
+
+# Input:
+set select_vars {
+}
+
+# Output:
+#
+# HTML/CSS data representing form for text select input
+
+# coerce uid to zero since there's not method in WPL yet to initiate
+# a search from a particular message.
+set uid 0
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+WPEval $select_vars {
+ if {$uid} {
+ if {[catch {WPCmd PEMessage $uid number} thisnum]} {
+ set uid 0
+ }
+ } else {
+ set thisnum ""
+ }
+
+
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "Search for Text"
+ WPStyleSheets
+ }
+
+ cgi_body BGCOLOR="$_wp(bordercolor)" {
+
+ catch {WPCmd PEInfo set wp_index_script fr_seltext.tcl}
+ catch {WPCmd PEInfo set help_context seltext}
+
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=get name=auth target=body {
+ cgi_text "page=index" type=hidden notab
+ cgi_text "doselect=1" type=hidden notab
+ cgi_text "by=text" type=hidden notab
+ cgi_table border=0 cellspacing=0 cellpadding=2 width="100%" height="100%" {
+ cgi_table_row {
+ cgi_table_data align=center valign=top class=dialog {
+ cgi_table width="80%" {
+ cgi_table_row {
+ cgi_table_data {
+ cgi_center {
+ cgi_puts "[cgi_nl][cgi_nl]This page provides a way to search for specific text in the messsages contained in [cgi_bold [WPCmd PEMailbox mailboxname]]. Enter your selection criteria below, and click [cgi_italic Search]. All messages matching the criteria will be marked with a check in the box next to their line in the Message List."
+ cgi_br
+ cgi_br
+ cgi_puts "Click [cgi_italic Cancel] to return to the Message List without searching.[cgi_nl][cgi_nl]"
+ }
+ }
+ }
+
+
+ if {[WPCmd PEMailbox selected]} {
+ cgi_table_row class=dialog {
+ cgi_table_data colspan=2 align=center valign=middle class=dialog {
+ cgi_put [cgi_font face=tahoma,verdana,geneva "Since some messages are already marked, choose whether criteria specified here should "]
+ cgi_select result {
+ cgi_option "search all messages in '[WPCmd PEMailbox mailboxname]'" value=broad selected
+ cgi_option "search within marked messages only." value=narrow
+ cgi_option "discard previous marks and search anew." value=new
+ }
+
+ cgi_br
+ cgi_br
+ }
+ }
+ } else {
+ cgi_text result=broad type=hidden notab
+ }
+
+ cgi_table_row class=dialog {
+ cgi_table_data valign=top align=center nowrap class=dialog {
+ cgi_put [cgi_font face=tahoma,verdana,geneva "Search for messages with text "]
+ cgi_select textcase {
+ cgi_option "in" value=ton
+ cgi_option "NOT in" value=not
+ }
+
+ cgi_br
+ cgi_br
+
+ cgi_put [cgi_font face=tahoma,verdana,geneva "the message's "]
+ if {$uid} {
+ set fromaddr [WPCmd PEMessage $uid fromaddr]
+ set deftext $fromaddr
+ } else {
+ set deftext ""
+ }
+
+ set fields {
+ {Subject: field} subj ""
+ {From: field} from selected
+ {To: field} to ""
+ {Cc: field} cc ""
+ {recipient fields} recip ""
+ {participant fields} partic ""
+ {text, anywhere} any ""
+ }
+
+ cgi_select field {
+ foreach {x y z} $fields {
+ cgi_option $x value=$y $z
+ }
+ }
+
+ cgi_br
+ cgi_br
+ cgi_put [cgi_font face=tahoma,verdana,geneva "matching "]
+ cgi_text text=$deftext size=30 maxlength=256
+
+ if {$uid} {
+ set ft [WPJSQuote $fromaddr]
+ set tt [WPJSQuote [WPCmd PEMessage $uid toaddr]]
+ set st [WPJSQuote [WPCmd PEMessage $uid subject]]
+ if {[string length $ft] || [string length $tt] || [string length $st]} {
+ cgi_put "[cgi_nl]Using "
+ cgi_select defs {
+ cgi_option "- Nothing -" ""
+ if {[string length $ft]} {
+ cgi_option "From Address" value=$ft selected
+ }
+ if {[string length $tt]} {
+ cgi_option "To Address" value=$tt
+ }
+ if {[string length $st]} {
+ cgi_option "Subject Text" value=$st
+ }
+ }
+ cgi_put " of message ${thisnum}"
+ }
+ }
+ }
+ }
+ cgi_table_row class=dialog {
+ cgi_table_data valign=top align=center nowrap class=dialog {
+ cgi_br
+ cgi_submit_button ok=Search
+ cgi_submit_button cancel=Cancel
+ }
+ }
+
+ cgi_table_row class=dialog {
+ cgi_table_data valign=top align=center class=dialog {
+ cgi_puts [cgi_nl][cgi_nl][cgi_font size=-1 "Note, if the number of messages in this folder is larger than the number of lines in the Message List, then some matching messages may not be visible without paging/scrolling."]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/spellcheck.tcl b/web/cgi/alpine/1.0/spellcheck.tcl
new file mode 100755
index 00000000..ac1f9fe5
--- /dev/null
+++ b/web/cgi/alpine/1.0/spellcheck.tcl
@@ -0,0 +1,399 @@
+#!./tclsh
+# $Id: spellcheck.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# spellcheck.tcl
+#
+# Purpose: CGI script to generate html form used to check
+# body text spelling in the webpine-lite composer
+
+# Input:
+set query_vars {
+ {repqstr "" ""}
+}
+
+# Output:
+#
+# HTML/Javascript/CSS data representing the page to correct spelling
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+
+set check_menu {
+ {
+ {expr 0}
+ {
+ {
+ # * * * * DONE * * * *
+ cgi_puts "<fieldset>"
+
+ cgi_puts [cgi_font size=-1 "When finished, choose action below, then click [cgi_italic OK].[cgi_nl][cgi_nl]"]
+ cgi_radio_button spell=spell
+ cgi_puts "[cgi_nbspace][cgi_font color=white face=Helvetica size=-1 Correct][cgi_nl]"
+ cgi_radio_button spell=cancel
+ cgi_puts "[cgi_nbspace][cgi_font color=white face=Helvetica size=-1 Cancel][cgi_nl]"
+ cgi_br
+ #cgi_image_button action=[WPimg but_s_do] border=0 alt="Do"
+ cgi_division "style=padding-bottom:4" align=center {
+ cgi_submit_button "action=OK" class=navtext
+ }
+ cgi_puts "</fieldset>"
+ }
+ }
+ }
+ {
+ {}
+ {
+ {
+ # * * * * Done * * * *
+ cgi_submit_button "spell=Apply" class=navtext
+ }
+ }
+ }
+ {
+ {}
+ {
+ {
+ # * * * * Cancel * * * *
+ cgi_submit_button "spell=Cancel" class=navtext
+ }
+ }
+ }
+ {
+ {expr 0}
+ {
+ {
+ # * * * * Help * * * *
+ cgi_submit_button "help=Get Help" class=navtext
+ }
+ }
+ }
+}
+
+set done 0
+set first 1
+set spellresult {}
+set line {}
+
+proc spelled {pipe} {
+ global done spellresult line first
+
+ if {[eof $pipe]} {
+ catch {close $pipe}
+ set done 1
+ return
+ }
+
+ gets $pipe w
+
+ if {$first == 0} {
+ if {[string length $w]} {
+ lappend line $w
+ } else {
+ lappend spellresult $line
+ set line {}
+ }
+ } else {
+ set first 0
+ }
+}
+
+
+WPEval $query_vars {
+ if {[catch {WPCmd PEInfo set suspended_composition} msgdata]} {
+ set problem "Can't read message text"
+ } else {
+ foreach p $msgdata {
+ if {[string compare [lindex $p 0] body] == 0} {
+ set body [lindex $p 1]
+ break
+ }
+ }
+
+ if {![info exists body]} {
+ set problem "Can't find body in message text"
+ } else {
+ # spell check and gather results
+ # set tmpfile
+ for {set i 0} {$i < 5} {incr i} {
+ set tmpfile [file join $_wp(sockdir) "sc[pid][expr rand()]"]
+ if {[file exists $tmpfile] == 0} {
+ if {[catch {open $tmpfile w} ofp] == 0} {
+ break
+ }
+ }
+ unset tmpfile
+ }
+
+ if {![info exists tmpfile]} {
+ set problem "Can't create temporary file"
+ }
+ }
+ }
+
+ if {![info exists problem]} {
+ if {[string length $repqstr]} {
+ set quoter $repqstr
+ } else {
+ set quoter "> "
+ }
+
+ foreach l $body {
+ if {[string compare $l "---------- Forwarded message ----------"] == 0} {
+ break;
+ } elseif {[regexp "^$quoter" $l]} {
+ puts $ofp ""
+ } else {
+# regsub -all {\$} $l {\$} l
+ puts $ofp "^${l}"
+ }
+ }
+
+ close $ofp
+
+ set cmd [list $_wp(ispell) "-a"]
+ set pipe [open "|$cmd < $tmpfile 2> /dev/null" r]
+
+ fileevent $pipe readable [list spelled $pipe]
+
+ vwait done
+
+ catch {file delete $tmpfile}
+ }
+
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "Check Spelling"
+ WPStyleSheets
+ cgi_put "<style type='text/css'>"
+ cgi_put ".correction { font-family: geneva, arial, sans-serif ; font-size: 9pt }"
+ cgi_puts "</style>"
+ }
+
+ cgi_body BGCOLOR="$_wp(bordercolor)" {
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=post enctype=multipart/form-data target=_top {
+ cgi_text "page=compose" type=hidden notab
+ cgi_text "cid=[WPCmd PEInfo key]" type=hidden notab
+ cgi_text "restore=1" type=hidden notab
+ cgi_text "style=Spell" type=hidden notab
+ cgi_text "last_line=[llength $spellresult]" type=hidden notab
+ if {[string length $repqstr]} {
+ cgi_text "repqstr=$repqstr" type=hidden notab
+ }
+
+ cgi_table border=0 cellspacing=0 cellpadding=2 width="100%" height="100%" {
+ cgi_table_row {
+ eval {
+ cgi_table_data $_wp(menuargs) {
+ WPTFCommandMenu check_menu {}
+ }
+ }
+
+ cgi_table_data valign=top class=dialog {
+
+ set badlines {}
+ array set badwords {}
+ set badcount 0
+ for {set n 0} {$n < [llength $spellresult]} {incr n} {
+ set words {}
+ foreach ms [lindex $spellresult $n] {
+ if {[regexp {& ([a-zA-Z0-9]*) [0-9]+ ([0-9]+):[ ]?(.*)$} $ms match w o s]} {
+ incr badcount
+ if {[regsub -all {, } $s { } sugs] < 0} {
+ continue
+ }
+
+ lappend words [list $w [expr {$o - 1}] $sugs]
+ if {[info exists badwords($w)]} {
+ incr badwords($w)
+ } else {
+ set badwords($w) 1
+ }
+ } elseif {[regexp {# ([a-zA-Z0-9]*) ([0-9]+)$} $ms match w o]} {
+ incr badcount
+ lappend words [list $w [expr {$o - 1}] {}]
+ if {[info exists badwords($w)]} {
+ incr badwords($w)
+ } else {
+ set badwords($w) 1
+ }
+ }
+ }
+
+ if {[llength $words]} {
+ lappend badlines [list $n $words]
+ }
+ }
+
+ if {[info exists problem] || $badcount <= 0} {
+ cgi_table align=center valign=top height="100%" {
+ cgi_table_row {
+ cgi_table_data align=center valign=bottom heigh="20%" {
+ if {[info exists problem]} {
+ cgi_puts "Problem detected: $problem"
+ } else {
+ cgi_puts "No misspelled words found."
+ }
+ }
+ }
+ cgi_table_row {
+ cgi_table_data align=center valign=top {
+ cgi_put "Click "
+ cgi_submit_button "cancel=Continue" class=navtext
+ cgi_put " to return to your composition."
+ }
+ }
+ }
+ } else {
+ cgi_table width="95%" border=0 align=center valign=top {
+ cgi_table_row {
+ cgi_table_data align=center "style=padding-top:10;padding-bottom:10" {
+ cgi_puts "Web Alpine found [cgi_bold $badcount] possibly misspelled word[WPplural $badcount]."
+ cgi_puts "Grouped by the line on which they were found, misspelled words can be corrected by either selecting from the list of suggestions, when available (note, first option always blank), or entering the corrected spelling directly."
+ cgi_puts "When finished click [cgi_italic Apply] to correct the text, or [cgi_italic Cancel] to return to the composition unchanged."
+ }
+ }
+
+ foreach sl $badlines {
+ set lnum [lindex $sl 0]
+ set locs {}
+
+ cgi_table_row {
+ cgi_table_data bgcolor=white align=left height=20 class=view "style=font-family:courier;padding:8" {
+ set ol [lindex $body $lnum]
+ set l ""
+ set o 0
+ foreach w [lindex $sl 1] {
+ set offset [lindex $w 1]
+ set word [lindex $w 0]
+ set wordlen [string length $word]
+ append l "[cgi_quote_html [string range $ol $o [expr {$offset - 1}]]][cgi_url $word "#${lnum}_[lindex $w 1]_${wordlen}" class=mispell]"
+ set o [expr {$offset + $wordlen}]
+ }
+
+ append l [string range $ol $o end]
+
+ cgi_put $l
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data {
+ cgi_table width=90% align=center border=0 {
+
+ foreach w [lindex $sl 1] {
+ set word [lindex $w 0]
+ set wordlen [string length $word]
+ set wordloc "${lnum}_[lindex $w 1]_${wordlen}"
+
+ cgi_table_row {
+
+ if {[llength [lindex $w 2]] > 0} {
+ cgi_table_data align=left class=correction nowrap {
+ cgi_put "Replace [cgi_anchor_name $wordloc][cgi_bold $word] with "
+ }
+
+ cgi_table_data align=left class=correction nowrap {
+ cgi_select s_${wordloc} class=correction {
+ cgi_option "" value=
+ foreach sug [lindex $w 2] {
+ cgi_option $sug value=$sug
+ }
+ }
+ }
+
+ cgi_table_data align=left class=correction nowrap {
+ cgi_put " or change to "
+ }
+ } else {
+ cgi_table_data align=left class=correction nowrap {
+ cgi_put "Change [cgi_anchor_name $wordloc][cgi_bold $word] to "
+ }
+ }
+
+ cgi_table_data align=left class=correction nowrap {
+ cgi_text r_${wordloc}= "size=[expr {$wordlen + 4}]" maxlength=64 class=correction
+ }
+
+ cgi_table_data align=left class=correction width=90% colspan=3 {
+
+ if {$badwords($word) > 1} {
+ if {![info exists badseen($word)]} {
+ switch $badwords($word) {
+ 2 { set badtimes both }
+ default { set badtimes "all $badwords($word)" }
+ }
+
+ cgi_put " and "
+ cgi_checkbox a_${wordloc}
+ cgi_put " apply to $badtimes occurrences"
+ set badseencount($word) 1
+ } else {
+ incr badseencount($word)
+ switch $badseencount($word) {
+ 2 { set bad1 "second " ; set bad2 "" }
+ 3 { set bad1 "third " ; set bad2 "" }
+ 4 { set bad1 "fourth " ; set bad2 "" }
+ 5 { set bad1 "fifth " ; set bad2 "" }
+ 6 { set bad1 "sixth " ; set bad2 "" }
+ default { set bad1 "" ; set bad2 " $badseencount($word)" }
+ }
+ cgi_put "(${bad1}occurrence${bad2})"
+ }
+
+ lappend badseen($word) $wordloc
+ } else {
+ cgi_put [cgi_img [WPimg dot2]]
+ }
+ }
+
+ lappend locs $wordloc
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data height=8 {
+ cgi_text "l_$lnum=[join $locs {,}]" type=hidden notab
+ }
+ }
+ }
+ }
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data align=center height=50 {
+ foreach l [array names badseen] {
+ set m $badseen($l)
+ cgi_text "e_[lindex $m 0]=[join [lrange $m 1 end] {,}]" type=hidden notab
+ }
+
+ cgi_submit_button "spell=Apply" class=navtext
+ cgi_submit_button "spell=Cancel" class=navtext
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
diff --git a/web/cgi/alpine/1.0/takeaddr.tcl b/web/cgi/alpine/1.0/takeaddr.tcl
new file mode 100755
index 00000000..328febb6
--- /dev/null
+++ b/web/cgi/alpine/1.0/takeaddr.tcl
@@ -0,0 +1,215 @@
+#!./tclsh
+# $Id: takeaddr.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# takeaddr.tcl
+#
+# Purpose: CGI script to take addresses to address book
+
+# Input:
+set takeaddr_vars {
+ {uid "Missing UID"}
+}
+
+# Output:
+#
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+# Command Menu definition for Message View Screen
+set take_menu {
+}
+
+set common_menu {
+ {
+ {}
+ {
+ {
+ cgi_put [cgi_url "Get Help" wp.tcl?page=help&topic=take&index=none&oncancel=view%2526op%253DTake class=navbar target=_top]
+ }
+ }
+ }
+}
+
+WPEval $takeaddr_vars {
+
+ if {[catch {WPCmd PEInfo noop} result]} {
+ error [list _action "No Op" $result "Please close this window."]
+ }
+
+ if {$uid > 0 && [catch {WPCmd PEMessage $uid takeaddr} tainfo]} {
+ error [list _action "takeaddr $uid" $tainfo "Click Browsers Back Button."]
+ }
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "Take Address"
+ WPStyleSheets
+ }
+
+ cgi_body bgcolor=$_wp(bordercolor) {
+ cgi_table border=0 cellspacing=0 cellpadding=0 height="100%" {
+ cgi_table_row {
+ cgi_table_data valign=top class=navbar width=112 {
+ cgi_table bgcolor=$_wp(menucolor) border=0 "style=\"padding-left: 2\"" {
+ # next comes the menu down the left side, with suitable
+ cgi_table_row {
+ eval {
+ cgi_table_data $_wp(menuargs) class=navbar {
+ WPTFCommandMenu take_menu common_menu
+ }
+ }
+ }
+ }
+ }
+
+ cgi_table_data valign=top class=navbar {
+ cgi_table border=0 cellspacing=0 cellpadding=2 align=center valign=top height="100%" {
+ cgi_table_row {
+ cgi_table_data valign=top class=dialog {
+ cgi_table align=center border=0 width=75% height=80 class=dialog {
+ cgi_table_row {
+ cgi_table_data class=dialog align=center valign=middle {
+ set txt "Select the address that you would like to take to your address book"
+ if {[llength $tainfo] > 1} {
+ set txt "$txt, or select multiple addresses to create a list."
+ } else {
+ set txt "$txt."
+ }
+ append txt "When finished, click [cgi_italic "Take Address"] to create an entry"
+ append txt " or [cgi_italic "Cancel"] to return, creating nothing."
+ #cgi_puts [cgi_bold $txt ]
+ cgi_puts $txt
+ }
+ }
+ }
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data valign=top height=99% class=dialog {
+
+ set books [WPCmd PEAddress books]
+
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=post target=_top name=takeaddr {
+ cgi_text page=view type=hidden notab
+ cgi_text uid=$uid type=hidden notab
+ cgi_text cid=[WPCmd PEInfo key] type=hidden notab
+ if {[llength $books] <= 1} {
+ cgi_text "book=0" type=hidden notab
+ }
+
+ cgi_table border=0 cellspacing=0 cellpadding=2 align=center valign=top width="70%" height="100%" {
+ if {[llength $books] > 1} {
+ cgi_table_row {
+ cgi_table_data colspan=2 valign=top align=center class=dialog {
+ cgi_table border=0 cellspacing=0 cellpadding=0 "style=padding-top:20;padding-bottom:20" {
+ cgi_table_row {
+ cgi_table_data align=right {
+ cgi_puts [cgi_bold "Take to address book"]
+ }
+ cgi_table_data align=right valign=top {
+ cgi_puts [cgi_bold "[cgi_nbspace]:[cgi_nbspace]"]
+ }
+ cgi_table_data align=left {
+ cgi_select book {
+ foreach book $books {
+ cgi_option [lindex $book 1] value=[lindex $book 0]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ set linenum 1
+ foreach taline $tainfo {
+ set printstr [lindex $taline 0]
+ set addr [lindex $taline 1]
+ set sugs [lindex $taline 2]
+ if {[llength $addr] == 0 && [llength $sugs] == 0} {
+ cgi_table_row {
+ cgi_table_data align=center class=dialog colspan=2 "style=xpadding-left:8%;xpadding-right:8%;padding-top:20;padding-bottom:20" {
+ cgi_put $printstr
+ }
+ }
+
+ continue
+ }
+
+ if {[incr linenum] % 2} {
+ set bgcolor #EEEEEE
+ } else {
+ set bgcolor #FFFFFF
+ }
+
+ cgi_table_row bgcolor=$bgcolor {
+ cgi_table_data align=right {
+ cgi_checkbox "taList=tl${linenum}" style=background-color:$bgcolor
+ if {[llength $addr] > 0} {
+ if {[set tmp [lindex $addr 0]] != {}} {
+ cgi_text "tl${linenum}p=$tmp" type=hidden notab
+ }
+ if {[set tmp [lindex $addr 1]] != {}} {
+ cgi_text "tl${linenum}m=$tmp" type=hidden notab
+ }
+ if {[set tmp [lindex $addr 2]] != {}} {
+ cgi_text "tl${linenum}h=$tmp" type=hidden notab
+ }
+ }
+ if {[llength $sugs] > 0} {
+ if {[set tmp [lindex $sugs 0]] != {}} {
+ cgi_text "tl${linenum}n=$tmp" type=hidden notab
+ }
+ if {[set tmp [lindex $sugs 1]] != {}} {
+ cgi_text "tl${linenum}fn=$tmp" type=hidden notab
+ }
+ if {[set tmp [lindex $sugs 2]] != {}} {
+ cgi_text "tl${linenum}fcc=$tmp" type=hidden notab
+ }
+ if {[set tmp [lindex $sugs 3]] != {}} {
+ cgi_text "tl${linenum}c=$tmp" type=hidden notab
+ }
+ }
+ }
+ cgi_table_data align=left {
+ regsub -all "<" $printstr "\\&lt;" printstr
+ regsub -all ">" $printstr "\\&gt;" printstr
+ cgi_puts $printstr
+ }
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data height="99%" colspan=2 valign=top align=center class=dialog "style=padding-top:16" {
+ cgi_submit_button "op=Take Address"
+ cgi_submit_button takecancel=Cancel
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/tclsh b/web/cgi/alpine/1.0/tclsh
new file mode 120000
index 00000000..385fc6c6
--- /dev/null
+++ b/web/cgi/alpine/1.0/tclsh
@@ -0,0 +1 @@
+../tclsh \ No newline at end of file
diff --git a/web/cgi/alpine/1.0/tconfig.tcl b/web/cgi/alpine/1.0/tconfig.tcl
new file mode 100755
index 00000000..6274e849
--- /dev/null
+++ b/web/cgi/alpine/1.0/tconfig.tcl
@@ -0,0 +1,1183 @@
+# $Id: tconfig.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# tconfig.tcl
+#
+# Purpose: CGI script to configure features/variables
+
+# Input:
+set conf_vars {
+ {newconf {} 0}
+ {oncancel "Nothing set for oncancel"}
+ {wv "" general}
+ {vlavar "" ""}
+}
+
+# Output:
+#
+
+# read config
+source genvars.tcl
+
+set confs {
+ {general General g genconf}
+ {msgl "Message List" ml mlconf}
+ {msgv "Message View" mv mvconf}
+ {composer Composer c compconf}
+ {address "Address Books" ab abconf}
+ {folder "Folders" f fldrconf}
+ {rule "Rules" r ruleconf}
+}
+
+
+set var_menu {
+ {
+ {}
+ {
+ {
+ # * * * * Save Config * * * *
+ #cgi_image_button save=[WPimg but_save] border=0 alt="Save Config"
+ cgi_submit_button save=Save
+ }
+ }
+ }
+ {
+ {}
+ {
+ {
+ # * * * * Close * * * *
+ #cgi_image_button cancel=[WPimg but_cancel] border=0 alt="Cancel"
+ cgi_submit_button cancel=Cancel
+ }
+ }
+ }
+}
+
+## read vars
+foreach item $conf_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} result]} {
+ if {[llength $item] > 2} {
+ set [lindex $item 0] [lindex $item 2]
+ } else {
+ error [list _action [lindex $item 1] $result]
+ }
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+set type $wv
+WPCmd set conf_page $type
+switch -- $type {
+ msgl {
+ set goodvars $msglist_vars
+ }
+ msgv {
+ set goodvars $msgview_vars
+ }
+ address {
+ set goodvars $address_vars
+ }
+ folder {
+ set goodvars $folder_vars
+ }
+ composer {
+ set goodvars $composer_vars
+ }
+ general {
+ set goodvars $general_vars
+ }
+ rule {
+ set goodvars $rule_vars
+ }
+}
+set typeexp "General"
+
+proc button_text {but butno fg bg text} {
+ if {[string length $fg]} {
+ set fg "color: #${fg};"
+ }
+
+ if {[string length $bg]} {
+ set bg "background-color: #${bg}"
+ }
+
+ cgi_puts "<script><!-- "
+ cgi_puts "document.write('<a href=\\\'#\\\' style=\\\"text-decoration: none;\\\" onClick=\"return setop(\\\'${but}\\\',$butno)\">');// -->"
+ cgi_puts "</script>"
+
+ cgi_put [cgi_span "style=$fg $bg" $text]
+
+ cgi_puts "<script><!-- "
+ cgi_puts "document.write('</a>');// -->"
+ cgi_puts "</script>"
+}
+
+proc button_checked {def but} {
+ if {[string compare $def $but]} {
+ return ""
+ } else {
+ return checked
+ }
+}
+
+foreach conf $confs {
+ if {[string compare $type [lindex $conf 0]] == 0} {
+ append ttitle [cgi_imglink "[lindex $conf 2]tab"]
+ lappend conftitle [list [cgi_imglink "[lindex $conf 2]tab"] {} {}]
+ set typeexp [lindex $conf 1]
+ set _wp(helpname) [lindex $conf 3]
+ } else {
+ append ttitle "<input type=image name=\"[lindex $conf 2]tab\" src=\"[WPimg "tabs/[lindex $conf 2]dtab"]\" border=0 alt=\"[lindex $conf 1]\">";
+ }
+}
+
+cgi_http_head {
+ WPStdHttpHdrs
+}
+
+if {$newconf == 1} {
+ WPCmd PEConfig newconf
+}
+
+cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "Configuration"
+ WPStyleSheets
+
+ cgi_put "<style type='text/css'>"
+ cgi_put ".vtext { font-size: 10pt; font-family: courier, 'new courier', monospace ; font-weight: medium ; padding-left: 6; border-left: 1px solid black}"
+ cgi_put ".instr { font-size: 10pt; font-weight: bold}"
+ cgi_puts "</style>"
+
+ cgi_javascript {
+ cgi_put "function setop(b,i){"
+ cgi_put " eval('document.varconfig.'+b+'\['+i+'\].checked = true');"
+ cgi_put " return false;"
+ cgi_put "}"
+ }
+ }
+
+ cgi_body BGCOLOR="$_wp(bordercolor)" {
+ set postjs ""
+
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=post name=varconfig enctype=multipart/form-data target=_top {
+ cgi_text "oncancel=$oncancel" type=hidden notab
+ cgi_text "cid=[WPCmd PEInfo key]" type=hidden notab
+ cgi_text "page=conf_process" type=hidden notab
+
+ cgi_table width="100%" cellpadding=0 cellspacing=0 {
+ cgi_table_row {
+ cgi_table_data width=112 bgcolor=$_wp(menucolor) {
+ cgi_puts [cgi_img [WPimg dot2] width=1 height=1]
+ }
+ cgi_table_data align=right nowrap background="[WPimg [file join tabs tabbg]]" {
+ cgi_put $ttitle
+ }
+ }
+ }
+ if {0} {
+ cgi_division align=right {
+ cgi_put $ttitle
+ }
+ }
+ cgi_table border=0 cellspacing=0 cellpadding=0 width="100%" height="100%" {
+
+ cgi_table_row {
+ #
+ # next comes the menu down the left side
+ #
+ eval {
+ cgi_table_data $_wp(menuargs) rowspan=4 {
+ WPTFCommandMenu var_menu {}
+ }
+ }
+
+ #
+ # In main body of screen goe confg list
+ #
+ cgi_table_data valign=top width="100%" class=dialog "style=\"padding-left: 6\"" {
+ cgi_h2 [cgi_bold "${typeexp} configuration settings:"]
+ switch -- $type {
+ msgl {
+ cgi_text "wv=msgl" type=hidden notab
+ }
+ msgv {
+ cgi_text "wv=msgv" type=hidden notab
+ }
+ address {
+ cgi_text "wv=address" type=hidden notab
+ }
+ folder {
+ cgi_text "wv=folder" type=hidden notab
+ }
+ composer {
+ cgi_text "wv=composer" type=hidden notab
+ }
+ general {
+ cgi_text "wv=general" type=hidden notab
+ }
+ rule {
+ cgi_text "wv=rule" type=hidden notab
+ }
+ }
+ set setfeatures [WPCmd PEConfig featuresettings]
+ set icnt 0
+ cgi_table border=0 cellspacing=0 cellpadding=5 width=98% {
+ foreach tmpvar $goodvars {
+ set vtypeinp [lindex $tmpvar 0]
+ set varname [lindex $tmpvar 1]
+ set vardesc [lindex $tmpvar 2]
+
+ if {[catch {WPCmd PEConfig varget $varname} section] == 0} {
+ set varvals [lindex $section 0]
+ set vartype [lindex $section 1]
+ set vtvals [lindex $section 2]
+ set v_is_default [lindex $section 3]
+ set v_is_fixed [lindex $section 4]
+ } else {
+ # UNKNOWN VAR: configure disabled?
+ continue
+ }
+
+ switch -- $vtypeinp {
+ var {
+ cgi_table_row {
+
+ cgi_table_data align=right valign=top nowrap width=50% {
+ if {[info exists varname]} {
+ # set script "help.tcl?vn=$varname"
+ # set js "cOpen('$script', 'help', 'scrollbars=yes', 600, 400); return false;"
+ # set js "return vh('$varname')"
+ # cgi_puts [WPurl null "" $varname "" onClick=$js]
+ # cgi_puts [WPurl null "" $vardesc "" onClick=$js]
+ # cgi_submit_button varhelp=$vardesc class=lnkbt
+ cgi_puts [cgi_font class=cfvn $vardesc]
+ }
+ }
+ cgi_table_data valign=top {
+ cgi_image_button "hlp.$varname=[WPimg cf_help]" alt="Help for $vardesc"
+ }
+ cgi_table_data align=left colspan=2 width=50% {
+ switch -- $vartype {
+ listbox {
+ cgi_select $varname align=left {
+ foreach tmpvt $vtvals {
+ set tmpvttxt [lindex $tmpvt 0]
+ set tmpvtval $tmpvttxt
+ if {[llength $tmpvt] > 1} {
+ set tmpvtval [lindex $tmpvt 1]
+ }
+ if {[string compare $tmpvtval [lindex $varvals 0]]} {
+ if {[llength $tmpvt] > 1} {
+ cgi_option "${tmpvttxt}" "value=${tmpvtval}"
+ } else {
+ cgi_option "${tmpvttxt}" "value=${tmpvttxt}"
+ }
+ } else {
+ if {[llength $tmpvt] > 1} {
+ cgi_option "$tmpvttxt" "value=${tmpvtval}" selected
+ } else {
+ cgi_option "$tmpvttxt" selected
+ }
+ }
+ }
+ }
+ }
+ textarea {
+ cgi_table border=0 cellpadding=0 cellspacing=0 {
+ set addfield 0
+ set tiwidth 30
+ foreach varval $varvals {
+ if {[string length $varval] > [expr {[info exists maxwidth] ? $maxwidth : 0}]} {
+ set maxwidth [string length $varval]
+ incr maxwidth 5
+ }
+ }
+ if {[info exists maxwidth]} {
+ set tiwidth $maxwidth
+ }
+ if {$tiwidth < 20} {
+ set tiwidth 20
+ } elseif {$tiwidth > 50} {
+ set tiwidth 50
+ }
+ cgi_table_row {
+ cgi_table_data colspan=4 {
+ cgi_image_button vla.$varname=[WPimg cf_add] alt="Add Value"
+ }
+ }
+ if {[string compare $vlavar $varname] == 0} {
+ set addfield 1
+ cgi_table_row {
+ cgi_table_data {
+ cgi_text "varlistadd=$varname" type=hidden notab
+ cgi_text "$varname-add=" type=text size=$tiwidth
+ }
+ cgi_table_data colspan=3 {
+ cgi_puts [cgi_font class=cfntc "(The value entered here will be added.)"]
+ }
+ }
+ }
+ set i 0
+ set vvsz [llength $varvals]
+ foreach varval $varvals {
+ cgi_table_row {
+ cgi_table_data {
+ cgi_text vle.$varname.$i=$varval type=text size=$tiwidth
+ }
+ cgi_table_data {
+ cgi_image_button vld.$varname.$i=[WPimg cf_delete] alt="Delete Value"
+ }
+ cgi_table_data {
+ if {$i < [expr {$vvsz - 1}]} {
+ cgi_image_button vlsd.$varname.$i=[WPimg cf_shdown] alt="Shuffle Down"
+ } else {
+ cgi_puts [cgi_nbspace]
+ }
+ }
+ cgi_table_data width=50% {
+ if {$i || $addfield} {
+ cgi_image_button vlsu.$varname.$i=[WPimg cf_shup] alt="Shuffle Up"
+ } else {
+ cgi_puts [cgi_nbspace]
+ }
+ }
+ }
+ incr i
+ }
+ cgi_text "$varname-sz=$i" type=hidden notab
+ }
+ }
+ default {
+ set size [string length [lindex $varvals 0]]
+ if {$size == 0} {
+ set size 20
+ } else {
+ incr size 5
+ }
+ cgi_text "$varname=[lindex $varvals 0]" type=text size=$size tableindex=1
+ }
+ }
+ }
+ }
+ }
+ feat {
+ cgi_table_row {
+ cgi_table_data align=right width=50% {
+ if {[info exists varname]} {
+ # cgi_submit_button feathelp=$vardesc class=lnkbt
+ cgi_puts [cgi_font class=cfvn $vardesc]
+ }
+ }
+ cgi_table_data {
+ cgi_image_button "hlp.$varname=[WPimg cf_help]" alt="Help for $vardesc"
+ }
+ cgi_table_data align=left colspan=3 width=50% {
+ if {[lsearch $setfeatures $varname] >= 0} {
+ cgi_checkbox $varname checked class=dialog
+ } else {
+ cgi_checkbox $varname class=dialog
+ }
+ }
+ }
+
+ }
+ special {
+ switch -- $varname {
+ wp-columns {
+ cgi_table_row {
+ cgi_table_data align=right valign=top nowrap {
+ cgi_puts [cgi_font class=cfvn $vardesc]
+ }
+ cgi_table_data valign=top {
+ cgi_image_button "hlp.${varname}=[WPimg cf_help]" alt="Help for $vardesc"
+ }
+ cgi_table_data align=left colspan=2 {
+ set cols [WPCmd PEConfig columns]
+ cgi_select columns align=left {
+ for {set i 20} {$i <= 128} {incr i 4} {
+ if {$i == $cols} {
+ cgi_option $i "value=$i" selected
+ } else {
+ cgi_option $i "value=$i"
+ }
+ }
+ }
+ }
+ }
+ }
+ left-column-folders {
+ cgi_table_row {
+ cgi_table_data align=right valign=top nowrap {
+ cgi_puts [cgi_font class=cfvn $vardesc]
+ }
+ cgi_table_data valign=top {
+ cgi_puts [cgi_nbspace]
+ }
+ cgi_table_data align=left colspan=2 {
+ if {[catch {WPSessionState left_column_folders} cols]} {
+ set cols $_wp(fldr_cache_def)
+ }
+
+ cgi_select fcachel align=left {
+ for {set i 0} {$i <= $_wp(fldr_cache_max)} {incr i} {
+ if {$i == $cols} {
+ cgi_option $i "value=$i" selected
+ } else {
+ cgi_option $i "value=$i"
+ }
+ }
+ }
+ }
+ }
+ }
+ signature {
+ cgi_table_row {
+ cgi_table_data colspan=4 align=center {
+ cgi_puts "<fieldset>"
+ cgi_puts "<legend>Signature</legend>"
+
+ set rawsig [join [WPCmd PEConfig rawsig] "\n"]
+ cgi_textarea signature=$rawsig rows=8 cols=80 wrap=off
+
+ cgi_puts "</fieldset>"
+ }
+ }
+
+ }
+ filters -
+ scores -
+ indexcolor -
+ collections {
+ set flt 0
+ set cll 0
+ switch $varname {
+ filters {
+ set flt 1
+ set descsing "Filter"
+ set filts [WPCmd PEConfig filters]
+ set lvals $filts
+ set varhelp 1
+ }
+ scores {
+ set flt 1
+ set descsing "Scores"
+ set filts [WPCmd PEConfig scores]
+ set lvals $filts
+ set varhelp 1
+ }
+ indexcolor {
+ set flt 1
+ set descsing "Index Colors"
+ set filts [WPCmd PEConfig indexcolors]
+ set lvals $filts
+ set varhelp 1
+ }
+ default {
+ set cll 1
+ set descsing "Collection"
+ set colls [WPCmd PEConfig collections]
+ set lvals $colls
+ }
+ }
+ set tasize [llength $lvals]
+ cgi_table_row {
+ cgi_table_data align=center colspan=4 width=50% {
+ cgi_table border=0 cellpadding=3 cellspacing=0 width=100% {
+ cgi_table_row {
+ cgi_table_data width=50% {
+ cgi_puts [cgi_bold $vardesc]
+ }
+ if {[info exists varhelp]} {
+ cgi_table_data valign=top {
+ cgi_image_button "hlp.$varname=[WPimg cf_help]" alt="Help for $vardesc"
+ }
+ }
+ cgi_table_data "style=\"padding-left: 10px\"" {
+ cgi_image_button vla.$varname=[WPimg cf_add] alt="Add $descsing"
+ }
+ }
+ set i 0
+ foreach lval $lvals {
+ cgi_table_row {
+ cgi_table_data "style=\"padding-left: 8px\"" {
+ if {$flt} {
+ cgi_puts [cgi_font class=cfvn "[cgi_nbspace]$lval"]
+ } else {
+ cgi_puts "[cgi_font class=cfvn [lindex $lval 0]]<br>[cgi_span class=cfval "style=margin-left: 8px" [lindex $lval 1]]"
+ }
+ }
+
+ cgi_table_data "style=\"padding-left: 10px\"" valign=top width=30% {
+ cgi_unbreakable {
+ cgi_image_button vle.$varname.$i=[WPimg cf_edit] alt="Edit $descsing"
+
+ cgi_image_button vld.$varname.$i=[WPimg cf_delete] alt="Delete $descsing"
+
+ if {$i < [expr {$tasize - 1}]} {
+ cgi_image_button vlsd.$varname.$i=[WPimg cf_shdown] alt="Shuffle Down"
+ } else {
+ cgi_puts [cgi_img [WPimg dot2] width=18]
+ }
+
+ if {$i} {
+ cgi_image_button vlsu.$varname.$i=[WPimg cf_shup] alt="Shuffle Up"
+ }
+ }
+ }
+ }
+ incr i
+ }
+ cgi_text "$varname-sz=$i" type=hidden notab
+ }
+ }
+ }
+ }
+ index-format {
+ cgi_table_row {
+ cgi_table_data align=left colspan=4 {
+ cgi_puts "<fieldset style=\"margin-left:1%; margin-right:1%\">"
+ #set helpbut [cgi_buffer {cgi_image_button "hlp.$varname=[WPimg cf_help]" alt="Help for $vardesc"}]
+ cgi_puts "<legend>Message Line Format</legend>"
+
+ set varval [WPCmd PEConfig varget $varname]
+ if {[llength [lindex $varval 0]]} {
+ set fmt ""
+ foreach fms [lindex [lindex $varval 0] 0] {
+ if {[regexp {^([a-zA-Z]+[0-9]*)\(([0-9]+[%]?)\)$} $fms dummy f w]} {
+ lappend fmt [list $f $w]
+ } elseif {[regexp {^([a-zA-Z]+[0-9]*)$} $fms dummy f]} {
+ lappend fmt [list $f ""]
+ }
+ }
+ } else {
+ set fmt [WPCmd PEMailbox indexformat]
+ }
+
+ cgi_text "index-format=$fmt" type=hidden notab
+
+ cgi_table cellpadding=0 cellspacing=0 width=100% align=center "bgcolor=#999999" "style=\"border: 1px solid black \"" border=0 {
+ cgi_table_row "bgcolor=#999999" {
+ if {[WPCmd PEInfo feature enable-aggregate-command-set]} {
+ cgi_td [cgi_img [WPimg dot2]]
+ cgi_td width=1 [cgi_img [WPimg dot2] width=1]
+ }
+
+ foreach fme $fmt {
+ set f [lindex $fme 0]
+ set w [lindex $fme 1]
+ cgi_td xcolspan=3 [cgi_span "style=font-size: 10px; color: white;" $f]
+ if {[regexp {[0123456789]+[%]} $w]} {
+ cgi_td xcolspan=2 align=right [cgi_span "style=font-size: 10px; color: white; padding-right: 4" "(${w})"]
+ } else {
+ cgi_td width=1 [cgi_img [WPimg dot2] width=1]
+ }
+
+ cgi_td width=1 [cgi_img [WPimg dot2] width=1]
+ }
+ }
+
+ cgi_table_row {
+ if {[WPCmd PEInfo feature enable-aggregate-command-set]} {
+ cgi_table_data background="[WPimg bg_index]" align=center valign=middle "style=\"padding-left: 4; padding-right: 4\"" {
+ cgi_checkbox bogus
+ }
+
+ cgi_td width=1 [cgi_img [WPimg dot2] width=1]
+ }
+
+ foreach fme $fmt {
+ set f [lindex $fme 0]
+ set w [lindex $fme 1]
+
+ switch -regexp $w {
+ [0123456789]+[%] {
+ set width width=$w
+ }
+ "" {
+ set r [WPTFIndexWidthRatio $fmt $f]
+ switch $r {
+ 1 { set width "" }
+ default {
+ set width "width=[expr {round((100/[llength $fmt]) * $r)}]%"
+ }
+ }
+ }
+ }
+
+ set align ""
+ set class ""
+ switch [string toupper [lindex $f 0]] {
+ TO {
+ set varval [WPCmd PEConfig varget personal-name]
+ if {[string length [lindex $varval 0]]} {
+ set ftext "To: [lindex $varval 0]"
+ } else {
+ set ftext "To: <sender@foo.bar.com>"
+ }
+ }
+ FROM -
+ FROMORTO {
+ set varval [WPCmd PEConfig varget personal-name]
+ if {[string length [lindex $varval 0]]} {
+ set ftext [lindex $varval 0]
+ } else {
+ set ftext "<sender@foo.bar.com>"
+ }
+ }
+ FULLSTATUS -
+ IMAPSTATUS -
+ STATUS {
+ set ftext "+N"
+ set align "align=center"
+ }
+ MSGNO {
+ set ftext [WPcomma [WPCmd PEMailbox messagecount]]
+ set align "align=center"
+ }
+ SIZE {
+ set ftext [cgi_span class=isize "(1234)"]
+ set class class=isize
+ set align "align=center"
+ }
+ KSIZE {
+ set ftext [cgi_span class=isize "(1K)"]
+ set class class=isize
+ set align "align=center"
+ }
+ SIZECOMMA {
+ set ftext [cgi_span class=isize "(1,234)"]
+ set class class=isize
+ set align "align=center"
+ }
+ DESCRIPSIZE {
+ set ftext "(short+)"
+ set align "align=center"
+ }
+ SIZENARROW {
+ set ftext (1K)
+ }
+ DATE {
+ set ftext [clock format [clock seconds] -format "%b %d"]
+ set align "align=center"
+ }
+ DATEISO {
+ set ftext [clock format [clock seconds] -format "%Y-%m-%d"]
+ set align "align=center"
+ }
+ SHORTDATE1 {
+ set ftext [clock format [clock seconds] -format "%m/%d/%y"]
+ set align "align=center"
+ }
+ SHORTDATE2 {
+ set ftext [clock format [clock seconds] -format "%d/%m/%y"]
+ set align "align=center"
+ }
+ SHORTDATE3 {
+ set ftext [clock format [clock seconds] -format "%d.%m.%y"]
+ set align "align=center"
+ }
+ SHORTDATE4 {
+ set ftext [clock format [clock seconds] -format "%y.%m.%d"]
+ set align "align=center"
+ }
+ SHORTDATEISO {
+ set ftext [clock format [clock seconds] -format "%y-%m-%d"]
+ set align "align=center"
+ }
+ SMARTTIME -
+ SMARTDATETIME -
+ TIME12 {
+ regsub {^0*(.*)$} [string tolower [clock format [clock seconds] -format "%I:%M%p"]] {\1} ftext
+ set align "align=center"
+ }
+ TIME24 {
+ set ftext [clock format [clock seconds] -format "%H:%M"]
+ set align "align=center"
+ }
+ TIMEZONE {
+ set ftext [clock format [clock seconds] -format "%z"]
+ set align "align=center"
+ }
+ SUBJECT {
+ set ftext "Re: Config changes..."
+ }
+ ATT {
+ set ftext "1"
+ }
+ CC {
+ set ftext "user@domain"
+ }
+ FROMORTONOTNEWS {
+ set ftext "news.group"
+ }
+ LONGDATE {
+ set ftext [clock format [clock seconds] -format "%b %d, %Y"]
+ set align "align=center"
+ }
+ MONTHABBREV {
+ set ftext [clock format [clock seconds] -format "%b"]
+ set align "align=center"
+ }
+ NEWS {
+ set ftext "news.group"
+ }
+ SENDER -
+ RECIPS -
+ NEWSANDTO -
+ RECIPSANDNEWS -
+ NEWSANDRECIPS {
+ set ftext "user@domain"
+ }
+ SCORE {
+ set ftext "50"
+ }
+ SMARTDATE {
+ set ftext "Today"
+ }
+ TOANDNEWS {
+ set ftext TOANDNEWS
+ }
+ default {
+ set ftext [lindex $f 0]
+ }
+ }
+ cgi_td $align $class $width nowrap height=34 colspan=2 "style=\"padding-right: 4; padding-left: 4\"" background="[WPimg bg_index]" $ftext
+ cgi_td width=1 [cgi_img [WPimg dot2] width=1]
+ }
+ }
+
+ cgi_table_row {
+ if {[WPCmd PEInfo feature enable-aggregate-command-set]} {
+ cgi_table_data "bgcolor=#ffffff" {
+ cgi_put [cgi_img [WPimg dot2]]
+ }
+
+ cgi_td width=1 [cgi_img [WPimg dot2] width=1]
+
+ set cols 2
+ } else {
+ set cols 0
+ }
+
+ set fmturl "wp.tcl?page=conf_process&wv=msgl&adjust=Change&cid=[WPCmd PEInfo key]&oncancel=$oncancel&index-format=[WPPercentQuote $fmt]"
+ foreach fme $fmt {
+ cgi_table_data colspan=2 nowrap "bgcolor=#ffffff" align=center valign=middle {
+ set cellurl "${fmturl}&ifield=[lindex $fme 0]"
+ cgi_puts [cgi_url [cgi_img [WPimg if_left] border=0 "alt=Move Field Left" height=11 width=11] "${cellurl}&iop=left" target=_top]
+ cgi_puts [cgi_url [cgi_img [WPimg if_wider] border=0 "alt=Widen Field" height=11 width=11] "${cellurl}&iop=widen" target=_top]
+ cgi_puts [cgi_url [cgi_img [WPimg if_remove] border=0 "alt=Remove Field" height=11 width=11] "${cellurl}&iop=remove" target=_top]
+ cgi_puts [cgi_url [cgi_img [WPimg if_narrow2] border=0 "alt=Narrow Field" height=11 width=11] "${cellurl}&iop=narrow" target=_top]
+ cgi_puts [cgi_url [cgi_img [WPimg if_right] border=0 "alt=Move Field Right" height=11 width=11] "${cellurl}&iop=right" target=_top]
+ }
+
+ cgi_td width=1 [cgi_img [WPimg dot2] width=1]
+ }
+ }
+ }
+
+ if {[catch {WPCmd PEConfig indextokens} tokens] == 0} {
+ cgi_division align=center {style="margin-top: 16; margin-bottom: 10"} {
+ cgi_submit_button "indexadd=Add Field"
+ cgi_image_button "hlp.index_tokens=[WPimg cf_help]" alt="Help for $vardesc"
+ cgi_select indexaddfield {
+ cgi_option {[ Choose Field to Insert ]} "value="
+
+ foreach af [lsort -dictionary $tokens] {
+ if {[lsearch $fmt $af] < 0} {
+ if {[string compare $af MSGNO]} {
+ cgi_option $af "value=$af"
+ } else {
+ cgi_option $af "value=$af" selected
+ }
+ }
+ }
+ }
+ }
+ }
+
+ cgi_puts "</fieldset>"
+ }
+ }
+ }
+ view-colors {
+ cgi_table_row {
+ cgi_table_data colspan=4 {
+ cgi_puts "<fieldset style=\"margin-left:1%; margin-right:1%\">"
+ #set helpbut [cgi_buffer {cgi_image_button "hlp.$varname=[WPimg cf_help]" alt="Help for $vardesc"}]
+ cgi_puts "<legend>Color Settings </legend>"
+
+ cgi_division class=instr "style=\"padding-bottom: 6\"" {
+ cgi_put [cgi_span "style=padding: 2; background-color: #ffcc66; border: 1px solid black" "1"]
+ cgi_put " Choose text below to color, or[cgi_nbspace]enter[cgi_nbspace]field[cgi_nbspace]"
+ cgi_text newfield= class=instr size=12 maxlength=32
+ cgi_put "[cgi_nbspace]to[cgi_nbspace]"
+ cgi_submit_button "addfield=add to colored headers"
+ }
+
+ set std_hdrs [list Date From To Cc Subject]
+ set samp_hdrs $std_hdrs
+ set samp_text [list normal quote1 quote2 quote3]
+ set colors {}
+
+ foreach goodkolor $samp_text {
+ set tcolor [WPCmd PEConfig colorget "${goodkolor}"]
+ lappend colors [list $goodkolor [lindex $tcolor 0] [lindex $tcolor 1]]
+ }
+
+ set hcolors [WPCmd PEConfig colorget "viewer-hdr-colors"]
+ set hi 0
+ foreach hcolor $hcolors {
+ set i 0
+ set dhdr 0
+ foreach samp_hdr $samp_hdrs {
+ if {[string compare [string tolower [lindex $hcolor 0]] [string tolower [lindex $samp_hdr 0]]] == 0} {
+ if {[string length [lindex $hcolor 3]] == 0} {
+ switch -- $samp_hdr {
+ Date {
+ set samptxt [clock format [clock seconds] -format "%a, %d %b %Y %H:%M:%S %Z"]
+ }
+ From {
+ set samptxt [WPCmd PECompose from]
+ }
+ Subject {
+ set samptxt "Your colors are Fabulous!"
+ }
+ default {
+ set samptxt Sample
+ }
+ }
+ lappend samp_hdr [list [lindex $hcolor 1] [lindex $hcolor 2] $samptxt "" $hi]
+ set samp_hdrs [lreplace $samp_hdrs $i $i $samp_hdr]
+ set dhdr 1
+ break
+ } else {
+ lappend samp_hdr [list [lindex $hcolor 1] [lindex $hcolor 2] [lindex $hcolor 3] [lindex $hcolor 3] $hi]
+ set samp_hdrs [lreplace $samp_hdrs $i $i $samp_hdr]
+ set dhdr 1
+ break
+ }
+ }
+ incr i
+ }
+ if {$dhdr == 0} {
+ set smptxt "Sample"
+ if {[string compare "" [lindex $hcolor 3]]} {
+ set smptxt [lindex $hcolor 3]
+ }
+ lappend samp_hdrs [list [lindex $hcolor 0] [list [lindex $hcolor 1] [lindex $hcolor 2] $smptxt [lindex $hcolor 3] $hi]]
+ }
+ incr hi
+ }
+
+ set dfgc "ffffff"
+ set dbgc "000000"
+ set dq1fgc "ffffff"
+ set dq1bgc "000000"
+ set dq2fgc "ffffff"
+ set dq2bgc "000000"
+ set dq3fgc "ffffff"
+ set dq3bgc "000000"
+
+ foreach color $colors {
+ switch -- [lindex $color 0] {
+ normal {
+ set dfgc [lindex $color 1]
+ set dbgc [lindex $color 2]
+ }
+ quote1 {
+ set dq1fgc [lindex $color 1]
+ set dq1bgc [lindex $color 2]
+ }
+ quote2 {
+ set dq2fgc [lindex $color 1]
+ set dq2bgc [lindex $color 2]
+ }
+ quote3 {
+ set dq3fgc [lindex $color 1]
+ set dq3bgc [lindex $color 2]
+ }
+ }
+
+ set colarr([lindex $color 0]) [list [lindex $color 1] [lindex $color 2]]
+ }
+
+ foreach hcolor $hcolors {
+ set colarr([lindex $color 0]) [list [lindex $color 1] [lindex $color 2]]
+ }
+
+ set butno -1
+ if {[catch {WPCmd PEInfo set config_defground} defground] || ![string length $defground]} {
+ set defground f
+ }
+
+ if {[catch {WPCmd PEInfo set config_deftext} deftext] || ![string length $deftext]} {
+ set deftext normal
+ }
+
+ # paint example text
+ cgi_table cellpadding=0 cellspacing=0 width=100% align=center border=0 {
+ cgi_table_row {
+ cgi_table_data align=center {
+
+ cgi_table border=0 cellpadding=0 cellspacing=0 width=100% align=center bgcolor=#${dbgc} "style=\"border-right: 1px solid black\"" {
+
+ cgi_table_row {
+ cgi_td class=dialog [cgi_img [WPimg dot2]]
+ cgi_td height=1 "bgcolor=#000000" [cgi_img [WPimg dot2] height=1]
+ }
+
+ foreach samp_hdr $samp_hdrs {
+ set field [string tolower [lindex $samp_hdr 0]]
+ incr butno
+ cgi_table_row {
+ cgi_table_data class=dialog align=center nowrap {
+ cgi_radio_button text=hdr.${field} [button_checked $deftext ${field}]
+ if {[llength $samp_hdr] == 1} {
+ set hdrfg $dfgc
+ set hdrbg $dbgc
+ } else {
+ set cp [lindex $samp_hdr end]
+ set hdrfg [lindex $cp 0]
+ set hdrbg [lindex $cp 1]
+ }
+
+ cgi_text "dfg.${field}=$hdrfg" type=hidden notab
+ cgi_text "dbg.${field}=$hdrbg" type=hidden notab
+ }
+
+ cgi_table_data class=vtext "style=\"color: #${dfgc}\; background-color: #${dbgc};\"" {
+ cgi_put "[lindex $samp_hdr 0]: "
+
+ if {[llength $samp_hdr] == 1} {
+ switch -- [lindex $samp_hdr 0] {
+ Date {
+ set samptxt [clock format [clock seconds] -format "%a, %d %b %Y %H:%M:%S %Z"]
+ }
+ From {
+ set samptxt [WPCmd PECompose from]
+ }
+ default {
+ set samptxt Sample
+ }
+ }
+
+ button_text text $butno $dfgc $dbgc [cgi_quote_html $samptxt]
+ cgi_text "add.${field}=1" type=hidden notab
+ } else {
+ cgi_text "hi.${field}=[lindex [lindex $samp_hdr end] end]" type=hidden notab
+
+ set pref ""
+ foreach cp [lrange $samp_hdr 1 end] {
+ button_text text $butno $hdrfg $hdrbg [cgi_quote_html "${pref}[lindex $cp 2]"]
+ set pref ", "
+ }
+ }
+ }
+ }
+ }
+
+ if {0} {
+ cgi_table_row {
+ cgi_td class=dialog [cgi_nbspace]
+ cgi_table_data {
+ cgi_text newfield= class=vtext
+ cgi_submit_button "addfield=Add New Header Field" class=vtext
+ }
+ }
+ }
+
+ cgi_table_row {
+ cgi_td class=dialog [cgi_nbspace]
+ cgi_td class=vtext [cgi_nbspace]
+ }
+
+ cgi_table_row {
+ cgi_table_data class=dialog align=center {
+ incr butno
+ cgi_radio_button text=normal [button_checked $deftext normal]
+ cgi_text "dfg.normal=$dfgc" type=hidden notab
+ cgi_text "dbg.normal=$dbgc" type=hidden notab
+ }
+
+ cgi_table_data class=vtext "style=\"color: #${dfgc}\; background-color: #${dbgc}\"" {
+ button_text text $butno $dfgc $dbgc "This is a rather silly message."
+ }
+ }
+
+ cgi_table_row {
+ cgi_td class=dialog [cgi_nbspace]
+ cgi_td class=vtext [cgi_nbspace]
+ }
+
+ cgi_table_row {
+ cgi_td class=dialog [cgi_img [WPimg dot2]]
+ cgi_table_data colspan=2 class=vtext "style=\"color: #${dfgc}\; background-color: #${dbgc}\"" {
+ cgi_put "On Apr 1, 2001, Sample wrote:</a>"
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data class=dialog align=center {
+ incr butno
+ cgi_radio_button text=quote3 [button_checked $deftext quote3]
+ cgi_text "dfg.quote3=[lindex $colarr(quote3) 0]" type=hidden notab
+ cgi_text "dbg.quote3=[lindex $colarr(quote3) 1]" type=hidden notab
+ }
+
+ cgi_table_data class=vtext "style=\"color: #${dfgc}\; background-color: #${dbgc}\"" {
+ cgi_put [cgi_span "style=color: #[lindex $colarr(quote1) 0]; background-color: #[lindex $colarr(quote1) 1]" "&gt; "]
+ cgi_put [cgi_span "style=color: #[lindex $colarr(quote2) 0]; background-color: #[lindex $colarr(quote2) 1]" "&gt; "]
+ button_text text $butno [lindex $colarr(quote3) 0] [lindex $colarr(quote3) 1] "&gt; This is an example of text that is quoted 3 times"
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data class=dialog align=center {
+ incr butno
+ cgi_radio_button text=quote2 [button_checked $deftext quote2]
+ cgi_text "dfg.quote2=[lindex $colarr(quote2) 0]" type=hidden notab
+ cgi_text "dbg.quote2=[lindex $colarr(quote2) 1]" type=hidden notab
+ }
+
+ cgi_table_data class=vtext "style=\"color: #${dfgc}\; background-color: #${dbgc}\"" {
+ cgi_put [cgi_span "style=color: #[lindex $colarr(quote1) 0]; background-color: #[lindex $colarr(quote1) 1]" "&gt; "]
+ button_text text $butno [lindex $colarr(quote2) 0] [lindex $colarr(quote2) 1] "&gt; This is an example of text that is quoted 2 times"
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data class=dialog align=center {
+ incr butno
+ cgi_radio_button text=quote1 [button_checked $deftext quote1]
+ cgi_text "dfg.quote1=[lindex $colarr(quote1) 0]" type=hidden notab
+ cgi_text "dbg.quote1=[lindex $colarr(quote1) 1]" type=hidden notab
+ }
+ cgi_table_data class=vtext "style=\"color: #${dfgc}\; background-color: #${dbgc}\"" {
+ button_text text $butno [lindex $colarr(quote1) 0] [lindex $colarr(quote1) 1] "&gt; This is an example of text that is quoted 1 time"
+ }
+ }
+
+ if {[llength [WPCmd PEInfo signature]]} {
+ cgi_table_row "style=\"border-right: 1px solid black\"" {
+ cgi_td class=dialog [cgi_img [WPimg dot2]]
+ cgi_table_data class=vtext "style=\"color: #${dfgc}\; background-color: #${dbgc}\"" {
+ foreach i [WPCmd PEInfo signature] {
+ cgi_puts "$i[cgi_nl]"
+ }
+ }
+ }
+ }
+
+ cgi_table_row {
+ cgi_td class=dialog [cgi_img [WPimg dot2]]
+ cgi_td height=1 "bgcolor=#000000" [cgi_img [WPimg dot2] height=1]
+ }
+ }
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data align=center "style=\"padding-top: 16\"" {
+ cgi_table width=100% border=0 {
+
+ cgi_table_row colspan=2 {
+ cgi_td valign=top align=left xrowspan=3 class=instr "style=\"padding-right: 4; background-color: #ffcc66; border: 1px solid black\"" [cgi_span "style=padding: 2;" "2"]
+ cgi_td width=30% colspan=2 valign=top class=instr "Choose fore- or background..."
+ cgi_td rowspan=3 width=3% [cgi_img [WPimg dot2]]
+ cgi_td valign=top align=left xrowspan=3 class=instr "style=\"padding-right: 4; background-color: #ffcc66; border: 1px solid black\"" [cgi_span "style=padding: 2;" "3"]
+ cgi_table_data valign=middle class=instr nowrap {
+ cgi_put "Choose item's color below, or[cgi_nbspace]"
+ cgi_submit_button "reset=restore its default colors"
+ }
+ }
+
+ cgi_table_row {
+ cgi_td rowspan=2 [cgi_img [WPimg dot2]]
+ cgi_table_data align=right {
+ if {[string compare $defground f] == 0} {
+ set checked checked
+ } else {
+ set checked ""
+ }
+
+ cgi_radio_button ground=f $checked
+ }
+
+ cgi_table_data "style=\"font-size: 10pt\"" {
+ button_text ground 0 "" "" Foreground
+ }
+
+ cgi_td rowspan=2 [cgi_img [WPimg dot2]]
+ cgi_table_data rowspan=2 {
+ cgi_image_button "colormap=[WPimg nondither10x10]" alt="Color Pattern" "style=\"border: 1px solid black\""
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data align=right {
+ if {[string compare $defground b] == 0} {
+ set checked checked
+ } else {
+ set checked ""
+ }
+
+ cgi_radio_button ground=b $checked
+ }
+
+ cgi_table_data "style=\"font-size: 10pt\"" {
+ button_text ground 1 "" "" [cgi_span "style=color: black; background-color: #FFCC66; padding: 4" "Background"]
+ }
+ }
+ }
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data colspan=2 align=center class=instr nowrap "style=\"padding-top: 16; padding-bottom: 10;\"" {
+ cgi_puts "Alternatively, you can also "
+ cgi_submit_button "reset=Restore All Color Defaults"
+ }
+ }
+
+ if {0} {
+ cgi_table_row {
+ cgi_table_data class=instr nowrap "style=\"padding-top: 16; padding-bottom: 10;\"" {
+ cgi_put [cgi_span "style=padding: 2; background-color: #ffcc66; border: 1px solid black" "OR"]
+ cgi_put " Enter header name[cgi_nbspace]"
+ cgi_text newfield= class=vtext
+ cgi_put "[cgi_nbspace]to[cgi_nbspace]"
+ cgi_submit_button "addfield=Add to Those Being Colored"
+ }
+ }
+ }
+ }
+
+ cgi_puts "</fieldset>"
+ }
+ }
+ }
+ }
+ }
+ }
+ incr icnt
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/1.0/view.tcl b/web/cgi/alpine/1.0/view.tcl
new file mode 100755
index 00000000..cf290e43
--- /dev/null
+++ b/web/cgi/alpine/1.0/view.tcl
@@ -0,0 +1,920 @@
+# $Id: view.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# view.tcl
+#
+# Purpose: CGI script to generate html output associated
+# with a message's particular text displayed in
+# the "body" frame
+
+# Input:
+set view_vars {
+ {message {} 0}
+ {first {} 1}
+ {top {} 0}
+ {bod_first {} 0}
+ {bod_prev {} 0}
+ {bod_next {} 0}
+ {bod_last {} 0}
+ {fullhdr {} ""}
+ {reload {} 0}
+ {uid {} 0}
+ {cid {} 0}
+ {goto {} ""}
+ {gonum {} 0}
+ {save {} ""}
+ {f_name {} ""}
+ {f_colid {} 0}
+ {takecancel {} ""}
+ {savecancel {} 0}
+ {auths {} 0}
+ {user {} ""}
+ {pass {} ""}
+ {create {} 0}
+ {flipdelete {} 0}
+ {delete {} ""}
+ {undelete {} ""}
+ {replyall {} ""}
+ {replytext {} ""}
+ {submitted {} 0}
+ {printable {} 0}
+ {op {} ""}
+ {showimg {} ""}
+}
+
+# Output:
+#
+# HTML/Javascript/CSS data representing the message specified
+# by the 'uid' argument
+
+#set use_icon_for_status 1
+
+set hacktoken __URL__TEXT__
+
+## read vars
+foreach item $view_vars {
+ if {[catch {cgi_import [lindex $item 0].x}]} {
+ if {[catch {eval WPImport $item} errstr]} {
+ error [list _action "Impart Variable" $errstr]
+ }
+ } else {
+ set [lindex $item 0] 1
+ }
+}
+
+if {[catch {WPCmd PEInfo key} webpine_key]} {
+ error [list _action "command ID" $webpine_key]
+}
+
+# make sure message to be "viewed" is still available
+catch {unset errstr}
+if {[catch {WPCmd PEMailbox messagecount} messagecount]} {
+ set errstr $messagecount
+} elseif {$uid > 0 && [catch {WPCmd PEMessage $uid number} message]} {
+ set errstr $message
+} elseif {$message > 0 && $message <= $messagecount && [catch {WPCmd PEMailbox uid $message} uid]} {
+ set errstr $uid
+} elseif {!($message && $uid)} {
+ set errstr "Unknown message number"
+}
+
+if {[info exists errstr]} {
+ switch $op {
+ Reply -
+ Forward {
+ catch {WPCmd set wp_spec_script fr_view.tcl}
+ catch {WPCmd set uid $uid}
+ source [WPTFScript main]
+ }
+ default {
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ WPHtmlHdrReload "$_wp(appdir)/$_wp(ui1dir)/wp.tcl?page=view"
+ WPStyleSheets
+ }
+
+ cgi_body bgcolor=#ffffff {
+ cgi_table border=0 width="100%" height="100%" {
+ cgi_table_row {
+ cgi_table_data width="20%" {
+ cgi_put [cgi_img [WPimg dot2] border=0 height=1]
+ }
+ cgi_table_data bgcolor=#ffffff class=notice {
+ cgi_puts "\[[cgi_nbspace]"
+ if {[string first "PEMessage: UID " $errstr] >= 0} {
+ cgi_puts "The message referred to is no"
+ cgi_puts "longer available."
+ cgi_p
+ cgi_puts "An error has been detected that indicates you may"
+ cgi_puts "have WebPine running in multiple browser windows."
+ cgi_p
+ cgi_puts "Please close all other windows that are displaying"
+ cgi_puts "WebPine pages."
+ } else {
+ cgi_puts "Message Unavailable: $errstr."
+ }
+ cgi_p
+ cgi_puts "Click \"[cgi_url [WPCmd PEMailbox mailboxname] fr_index.tcl target=spec]\" in the column on the left to return to the updated Message List.[cgi_nbspace]\]"
+ }
+ cgi_table_data width="20%" {
+ cgi_put [cgi_img [WPimg dot2] border=0 height=1]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+} else {
+
+ # make sure any caching doesn't screw this setting
+ catch {WPCmd set wp_spec_script fr_view.tcl}
+
+ set zoomed [WPCmd PEMailbox zoom]
+
+ set onload "onLoad="
+ set onunload "onUnload="
+
+ if {[info exists _wp(exitonclose)]} {
+ set closescript [cgi_buffer WPExitOnClose]
+ append onload "wpLoad();"
+ append onunload "wpUnLoad();"
+ }
+
+ # perform any requested actions
+ if {$flipdelete == 1} {
+ if {$cid != $webpine_key} {
+ lappend newmail [list "Can't Delete: Invalid command ID!"]
+ } elseif {[WPCmd PEMessage $uid flag deleted]} {
+ if {[catch {WPCmd PEMessage $uid flag deleted 0} result]} {
+ lappend newmail [list "Error UNdeleting $message: $result"]
+ }
+ } else {
+ if {[catch {WPCmd PEMessage $uid flag deleted 1} result]} {
+ lappend newmail [list "Error deleting $message: $result"]
+ } else {
+ # bug: handle delete-skips-deleted
+ set message [WPCmd PEMailbox next [WPCmd PEMessage $uid number]]
+ set uid [WPCmd PEMailbox uid $message]
+ }
+ }
+ } elseif {[string compare "report spam" [string tolower $op]] == 0} {
+ if {[info exists _wp(spamaddr)] && [string length $_wp(spamaddr)]} {
+ if {$cid != $webpine_key} {
+ lappend newmail [list "Can't Spamify: Invalid command ID!"]
+ } else {
+ if {[info exists _wp(spamfolder)] && [string length $_wp(spamfolder)]
+ && [catch {
+ set savedef [WPCmd PEMailbox savedefault]
+ if {[WPCmd PEFolder exists [lindex $savedef 0] $_wp(spamfolder)] == 0} {
+ WPCmd PEFolder create [lindex $savedef 0] $_wp(spamfolder)
+ }
+
+ WPCmd PEMessage $uid save [lindex $savedef 0] $_wp(spamfolder)
+ } result]} {
+ lappend newmail [list "Error spamifying message $message: $result"]
+ } elseif {[catch {WPCmd PEMessage $uid flag deleted 1} result]} {
+ lappend newmail [list "Error spamifying message $message: $result"]
+ } else {
+ set n [WPCmd PEMessage $uid number]
+ if {[info exists _wp(spamsubj)]} {
+ set subj $_wp(spamsubj)
+ } else {
+ set subj ""
+ }
+
+ if {[catch {WPCmd PEMessage $uid spam $_wp(spamaddr) $subj} result]} {
+ lappend newmail [list "Can't Report Spam: $result"]
+ } else {
+ lappend newmail [list "Message $n reported as Spam and flagged for deletion"]
+ }
+ }
+ }
+ }
+ } elseif {$delete == 1 || [string compare delete [string tolower $op]] == 0} {
+ if {$cid != $webpine_key} {
+ lappend newmail [list "Can't Delete: Invalid command ID!"]
+ } elseif {[catch {WPCmd PEMessage $uid flag deleted 1} result]} {
+ lappend newmail [list "Error deleting message $message: $result"]
+ } else {
+ set n [WPCmd PEMessage $uid number]
+ # lappend newmail [list "Message $n deleted"]
+ # bug: handle delete-skips-deleted
+ set message [WPCmd PEMailbox next $n]
+ set uid [WPCmd PEMailbox uid $message]
+ lappend newmail [list "Message $n flagged for deletion"]
+ }
+ } elseif {$delete == 0 || $undelete == 1 || [string compare undelete [string tolower $op]] == 0} {
+ if {$cid != $webpine_key} {
+ catch {WPCmd PEInfo statmsg "Invalid command ID!"}
+ } elseif {[catch {WPCmd PEMessage $uid flag deleted 0}]} {
+ lappend newmail [list "Error UNdeleting message $message"]
+ } else {
+ lappend newmail [list "Deletion mark for message [WPCmd PEMessage $uid number] removed"]
+ }
+ } elseif {[string length $goto]} {
+ if {[regexp {^([0-9]+)$} $gonum n]} {
+ if {$n > 0 && $n <= [WPCmd PEMailbox last]} {
+ set message $n
+ set uid [WPCmd PEMailbox uid $message]
+ } else {
+ lappend newmail [list "Jump value out of range: $gonum"]
+ }
+ } else {
+ if {[string length $gonum]} {
+ lappend newmail [list "Unrecognized Jump value: $gonum"]
+ } else {
+ lappend newmail [list "Enter message number, then click 'Jump'"]
+ }
+ }
+ } elseif {$savecancel == 1 || [string compare cancel [string tolower $savecancel]] == 0} {
+ lappend newmail [list "Save cancelled. Folder not created."]
+ } elseif {[string compare browse [string tolower $op]] == 0} {
+ _cgi_set_uservar onselect main
+ _cgi_set_uservar oncancel main
+ _cgi_set_uservar controls 2
+ source [WPTFScript savebrowse]
+ set nopage 1
+ } elseif {[string compare take [string tolower $op]] == 0} {
+ source [WPTFScript take]
+ set nopage 1
+ } elseif {[string compare "take address" [string tolower $op]] == 0} {
+ _cgi_set_uservar take 1
+ set addrs ""
+ if {[catch {cgi_import taList}] == 0} {
+ foreach ta $taList {
+ if {[catch {cgi_import_as ${ta}p xp}]} {
+ set xp ""
+ }
+
+ if {[catch {cgi_import_as ${ta}m xm}]} {
+ set xm ""
+ }
+
+ if {[catch {cgi_import_as ${ta}h xh}]} {
+ set xh ""
+ }
+
+ lappend alist [list $xp $xm $xh]
+ }
+
+ switch [llength $alist] {
+ 0 {
+ _cgi_set_uservar addrs ""
+ }
+ 1 {
+ set a [lindex $alist 0]
+ _cgi_set_uservar fn [lindex $a 0]
+ _cgi_set_uservar addrs [WPPercentQuote "[lindex $a 1]@[lindex $a 2]"]
+ }
+ default {
+ set addrs ""
+ foreach a $alist {
+ if {[string length $addrs]} {
+ append addrs ","
+ }
+
+ append addrs [WPPercentQuote "[lindex $a 0] <[lindex $a 1]@[lindex $a 2]>"]
+ }
+
+ _cgi_set_uservar addrs $addrs
+ }
+ }
+
+ source [WPTFScript takeedit]
+ } else {
+ WPCmd PEInfo statmsg "Take Address cancelled. No addresses selected."
+ source [WPTFScript main]
+ }
+
+ set nopage 1
+ } elseif {[string compare cancel [string tolower $takecancel]] == 0} {
+ source [WPTFScript main]
+ set nopage 1
+ } elseif {[string compare forward [string tolower $op]] == 0} {
+ _cgi_set_uservar style Forward
+ source [WPTFScript compose]
+ set nopage 1
+ } elseif {[string compare reply [string tolower $op]] == 0} {
+ _cgi_set_uservar style Reply
+ _cgi_set_uservar repqstr [WPCmd PEMessage $uid replyquote]
+ source [WPTFScript compose]
+ set nopage 1
+ } elseif {[string compare save [string tolower $op]] == 0} {
+ if {[catch {WPCmd PEMessage $uid save $f_colid $f_name} reason]} {
+ #statmsg "Save failed: $reason"
+ lappend newmail [list "Save failed: $reason"]
+ } else {
+ set savedef [WPTFSaveDefault $uid]
+ if {[lindex $savedef 0] == $f_colid} {
+ WPTFAddSaveCache $f_name
+ }
+
+ if {[WPCmd PEInfo feature save-will-not-delete] == 0} {
+ if {[catch {WPCmd PEMessage $uid flag deleted 1} reason]} {
+ # statmsg "Cannot delete saved message: $reason"
+ lappend newmail [list "Cannot delete saved message: $reason"]
+ }
+ }
+ }
+
+ } elseif {$bod_first} {
+ set u $uid
+ set message [WPCmd PEMailbox first]
+ set uid [WPCmd PEMailbox uid $message]
+ if {$zoomed && $u == $uid} {
+ lappend newmail [list "Already on first of the Marked messages"]
+ }
+ } elseif {$bod_prev} {
+ set u $uid
+ set message [WPCmd PEMailbox next [WPCmd PEMessage $uid number] -1]
+ set uid [WPCmd PEMailbox uid $message]
+ if {$zoomed && $u == $uid} {
+ lappend newmail [list "Already on first of the Marked messages"]
+ }
+ } elseif {$bod_next} {
+ set u $uid
+ set message [WPCmd PEMailbox next [WPCmd PEMessage $uid number]]
+ set uid [WPCmd PEMailbox uid $message]
+ if {$zoomed && $u == $uid} {
+ lappend newmail [list "Already on last of the Marked messages"]
+ }
+ } elseif {$bod_last} {
+ set u $uid
+ set message [WPCmd PEMailbox last]
+ set uid [WPCmd PEMailbox uid $message]
+ if {$zoomed && $u == $uid} {
+ lappend newmail [list "Already on last of the Marked messages"]
+ }
+ }
+
+ if {![info exists nopage]} {
+ switch -exact -- [WPCmd PEMailbox state] {
+ readonly {
+ lappend newmail [list [cgi_span "style=color: black; margin: 2 ; border: 1px solid red; background-color: pink; font-weight: bold" "Deleted messages may reapper because [WPCmd PEMailbox mailboxname] is Read Only"]]
+ }
+ closed {
+ lappend newmail [list [cgi_span "style=color: black; border: 1px solid red; background-color: pink; font-weight: bold" "Message body may be blank because [WPCmd PEMailbox mailboxname] is Closed"]]
+ }
+ ok -
+ default {}
+ }
+
+ if {[catch {WPNewMail $reload ""} newmailmsg]} {
+ error [list _action "new mail" $newmailmsg]
+ } else {
+ foreach i $newmailmsg {
+ lappend newmail $i
+ }
+
+ if {[info exists newmail] == 0} {
+ set newmail ""
+ }
+ }
+
+ if {$uid <= 0} {
+ lappend newmail [list "Message $message has UID 0!"]
+ }
+
+ if {[WPCmd PEInfo feature enable-full-header-cmd]} {
+ if {[string length $fullhdr]} {
+ if {[WPCmd PEInfo mode full-header-mode]} {
+ if {$fullhdr == 0 || [string compare $fullhdr "off"] == 0} {
+ WPCmd PEInfo mode full-header-mode 0
+ }
+ } else {
+ if {$fullhdr == 1 || [string compare $fullhdr "on"] == 0} {
+ WPCmd PEInfo mode full-header-mode 2
+ }
+ }
+ }
+
+ if {[WPCmd PEInfo mode full-header-mode]} {
+ set hmode "fullhdr=off"
+ set text [cgi_imglink nofullhdr]
+ } else {
+ set hmode "fullhdr=on"
+ set text [cgi_imglink fullhdr]
+ }
+
+ set hmode_url [cgi_url $text "wp.tcl?page=body&$hmode" name=hmode target=body style=vertical-align:middle]
+ }
+
+ if {$uid} {
+ # preserve any new uid val
+ WPCmd set uid $uid
+ }
+
+ if {[catch {WPCmd PEMessage $uid charset} charset]
+ || [string length $charset] == 0
+ || [string compare us-ascii [string tolower $charset]] == 0} {
+ set charset "ISO-8859-1"
+ }
+
+ catch {fconfigure stdout -encoding binary}
+
+ cgi_http_head {
+ WPStdHttpHdrs "text/html; charset=$charset"
+ }
+
+ cgi_html {
+ cgi_head {
+
+ cgi_http_equiv Content-Type "text/html; charset=$charset"
+
+ set normalreload [cgi_buffer {WPHtmlHdrReload "$_wp(appdir)/$_wp(ui1dir)/wp.tcl?page=body&uid=$uid"}]
+ if {[info exists _wp(exitonclose)]} {
+ cgi_puts $closescript
+
+ cgi_script type="text/javascript" language="JavaScript" {
+ cgi_put "function viewReloadTimer(t){"
+ cgi_put " reloadtimer = window.setInterval('wpLink(); window.location.replace(\\'[cgi_root]/$_wp(appdir)/$_wp(ui1dir)/wp.tcl?page=body&reload=1\\')', t * 1000);"
+ cgi_puts "}"
+ }
+
+ append onload "viewReloadTimer($_wp(refresh));"
+ cgi_noscript {
+ cgi_puts $normalreload
+ }
+ } else {
+ cgi_puts $normalreload
+ }
+
+ if {[info exists _wp(timing)]} {
+ cgi_script type="text/javascript" language="JavaScript" {
+ cgi_puts "var loadtime = new Date();"
+ cgi_puts "var submitted = $submitted;"
+ cgi_puts "function fini() {var now = new Date(); window.status = 'Page loaded in '+(now.getTime() - loadtime.getTime())/1000+' seconds';}"
+ }
+
+ append onload "fini();"
+ }
+
+ WPStyleSheets
+
+ if {$_wp(keybindings)} {
+ set kequiv {
+ {{?} {top.location = 'wp.tcl?page=help'}}
+ {{l} {top.location = 'wp.tcl?page=folders'}}
+ {{a} {top.location = 'wp.tcl?page=addrbook'}}
+ {{n} {top.spec.body.location = 'wp.tcl?page=view&bod_next=1'}}
+ {{p} {top.spec.body.location = 'wp.tcl?page=view&bod_prev=1'}}
+ {{i} {top.spec.location = 'fr_index.tcl'}}
+ {{s} {top.spec.cmds.document.saveform.f_name.focus()}}
+ {{d} {top.spec.cmds.document.delform.op[0].click()}}
+ {{u} {top.spec.cmds.document.delform.op[1].click()}}
+ {{r} {top.spec.cmds.document.replform.op.click()}}
+ {{f} {top.spec.cmds.document.forwform.op.click()}}
+ }
+
+ lappend kequiv [list {c} "top.location = 'wp.tcl?page=compose&oncancel=main.tcl&cid=[WPCmd PEInfo key]'"]
+
+ if {[info exists hmode_url]} {
+ lappend kequiv [list {h} "top.spec.body.location = 'wp.tcl?page=view&$hmode'"]
+ }
+
+ append onload [WPTFKeyEquiv $kequiv]
+ }
+ }
+
+ set fgcolor [set normal_fgcolor [WPCmd PEInfo foreground]]
+ set bgcolor [WPCmd PEInfo background]
+
+ cgi_body bgcolor=white $onload $onunload {
+ catch {WPCmd set help_context view}
+
+ set infont 0
+ set inurl 0
+
+ if {[llength $newmail]} {
+ cgi_division align=center "style=\"border: 1px solid black; background: $_wp(bordercolor)\"" {
+ WPTFStatusTable $newmail 1 "style=\"padding: 6px 0\""
+ }
+ }
+
+ cgi_table width="100%" border=0 cellpadding=2 cellspacing=0 {
+
+ # Context
+ cgi_table_row {
+ if {$zoomed} {
+ set marked " [cgi_bold marked]"
+ set c $zoomed
+ } else {
+ set marked ""
+ set c $messagecount
+ }
+
+ cgi_table_data align=left valign=middle class=context {
+ # write message number
+
+ if {[catch {WPCmd PEMailbox messagecount before [WPCmd PEMessage $uid number]} prior]} {
+ set prior 0
+ }
+
+ if {[catch {WPCmd PEMailbox messagecount after [WPCmd PEMessage $uid number]} remaining]} {
+ set remaining 0
+ }
+
+ if {$c == 1} {
+ set msgnotext "Only${marked} Message in [WPCmd PEMailbox mailboxname]"
+ } elseif {$remaining == 0} {
+ set msgnotext "[cgi_bold Last] of [WPcomma $c]${marked} message[WPplural $c]"
+ } elseif {$prior == 0} {
+ set msgnotext "[cgi_bold First] of [WPcomma $c]${marked} message[WPplural $c]"
+ } else {
+ if {[string length $marked]} {
+ append marked " messages"
+ set n [expr {$zoomed - $remaining}]
+ set msgnotext "Message [WPcomma $message] ($n of [WPcomma $c] [cgi_bold marked] messages)"
+ } else {
+ set msgnotext "Message [WPcomma $message] of [WPcomma $c]"
+ }
+ }
+
+ if {[info exists use_icon_for_status]} {
+ set staticon "[WPStatusImg $uid] "
+ set stattext ""
+ } else {
+ set staticon ""
+ switch -regexp [lindex [WPStatusIcon $uid] 0] {
+ del {
+ set stattext " (Deleted)"
+ }
+ new {
+ set stattext " (New)"
+ }
+ default {
+ set stattext ""
+ }
+ }
+ }
+
+ cgi_put "${staticon}${msgnotext}${stattext}"
+ }
+
+ if {$printable} {
+ cgi_table_data align=right valign=top bgcolor=#bfbfbf class=context {
+ cgi_puts "Folder: [WPCmd PEMailbox mailboxname]"
+ }
+ } else {
+ if {[info exists common_goto_in_view_specific_frame]} {
+ cgi_table_data align=right valign=top bgcolor=#bfbfbf class=context {
+ cgi_form $_wp(appdir)/$_wp(ui1dir)/wp method=get target=body {
+ cgi_text "page=view" type=hidden notab
+ cgi_text gonum= type=text size=4 maxlength=4 class=navtext
+ cgi_submit_button "goto=Jump to Msg" class=navtext
+ cgi_put [cgi_nbspace]
+ }
+ }
+ }
+
+ cgi_table_data align=right valign=top bgcolor=#bfbfbf class=context {
+ cgi_put [cgi_url [cgi_img [WPimg printer2] border=0 style=vertical-align:baseline] wp.tcl?page=view&uid=$uid&printable=1 target=_blank "onClick=if(window.print){window.print();return false;}else return true;"]
+ cgi_put [cgi_img [WPimg dot2] height=1 width=4]
+ if {[info exists hmode_url]} {
+ cgi_put $hmode_url
+ } else {
+ cgi_put [cgi_imglink dot]
+ }
+ }
+ }
+ }
+
+
+ cgi_table_row {
+ cgi_table_data valign=top bgcolor=#${bgcolor} class=view width="100%" colspan=2 {
+ cgi_preformatted "style=\"color:#$normal_fgcolor\"" {
+ #cgi_division "style=\"color: #$normal_fgcolor; font-family: courier\""
+
+ set msgtext [WPCmd PEMessage $uid text]
+
+ # pre-scan message text for anything interesting
+ foreach i $msgtext {
+ foreach j $i {
+ switch -exact [lindex $j 0] {
+ img {
+ if {![info exists showimages]} {
+ # ONLY IMG tags in HTML text that reference ATTACHED IMAGE files are allowed
+ if {[catch {WPCmd PEMessage $uid cid "<[lindex [lindex $j 1] 0]>"} cidpart] == 0
+ && [string length $cidpart]
+ && [catch {WPCmd PEMessage $uid attachinfo $cidpart} attachinfo] == 0
+ && [string compare [string tolower [lindex $attachinfo 1]] image] == 0} {
+
+ if {[catch {WPCmd PEMessage $uid fromaddr} fromaddr]} {
+ set fromaddr ""
+ } elseif {$showimg == 0} {
+ if {[catch {WPSessionState allow_cid_images} friends] == 0} {
+ while {[set findex [lsearch -exact $friends $fromaddr]] >= 0} {
+ set friends [lreplace $friends $findex $findex]
+ if {[catch {WPSessionState allow_cid_images $friends} friends]} {
+ catch {WPCmd PEInfo statmsg "Cannot forget image sender: $friends"}
+ break;
+ }
+ }
+ }
+
+ set showimg {}
+ }
+
+ if {[string length $showimg]
+ || ([catch {WPSessionState allow_cid_images} friends] == 0
+ && [lsearch -exact $friends $fromaddr] >= 0)} {
+
+ set showimages 1
+
+ if {[string length $fromaddr]} {
+ set bodyleadin "\[ Attached images ARE being displayed \]"
+ if {$showimg == 1} {
+ append bodyleadin "[cgi_nl]\[ Always show images from [cgi_url "$fromaddr" "wp.tcl?page=body&showimg=[WPPercentQuote $fromaddr]"] \]"
+ } else {
+ append bodyleadin "[cgi_nl]\[ Never show images from [cgi_url "$fromaddr" "wp.tcl?page=body&showimg=0"] \]"
+
+ if {[catch {WPSessionState allow_cid_images} friends] || [llength $friends] == 0} {
+ set friends $fromaddr
+ } elseif {[lsearch -exact $friends $fromaddr] < 0} {
+ lappend friends $fromaddr
+ }
+
+ if {[catch {WPSessionState allow_cid_images $friends} friends]} {
+ catch {WPCmd PEInfo statmsg "Cannot remember image sender: $friends"}
+ }
+ }
+ }
+ } else {
+
+ set showimages 0
+
+ set bodyleadin "\[ Attached images are NOT being displayed \]"
+ append bodyleadin "[cgi_nl]\[ Show images [cgi_url "below" "wp.tcl?page=body&showimg=1"]"
+ if {[string length $fromaddr]} {
+ append bodyleadin ", or always show images from [cgi_url "$fromaddr" "wp.tcl?page=body&showimg=[WPPercentQuote $fromaddr]"] \]"
+ } else {
+ append bodyleadin " \]"
+ }
+
+ }
+ }
+ }
+ }
+ default {}
+ }
+ }
+ }
+
+ set inbody 0
+ foreach i $msgtext {
+
+ if {!$inbody
+ && [llength $i] == 1
+ && [lindex [lindex $i 0] 0] == "t"
+ && 0 == [string length [lindex [lindex $i 0] 1]]} {
+ set inbody 1
+ if {[info exists bodyleadin]} {
+ cgi_puts $bodyleadin
+ }
+ }
+
+ foreach j $i {
+ set ttype [lindex $j 0]
+ set tdata [lindex $j 1]
+
+ # write anchors by hand
+ switch -- $ttype {
+ urlstart {
+ set href [lindex $tdata 0]
+ set name [lindex $tdata 1]
+
+ # build links by hand since we don't know where
+ # they'll terminate
+ set linktext "<a "
+ switch -- [lindex [split $href :] 0] {
+ XXX_mailto {
+ append linktext "href=\"null.tcl\" onClick=\"composeMsg('mailto','[lindex [split $href :] 1]','$webpine_key'); return false;\""
+ }
+ default {
+ if {[regexp -- "^\[a-zA-Z\]+:" $href ]} {
+ append linktext "href=\"$href\" target=_blank"
+ } ;# no relative links for security
+
+ if {[string length $name]} {
+ append linktext "name=\"$name\""
+ }
+ }
+ }
+
+ cgi_put "${linktext}>"
+ set inurl 1
+ }
+ urlend {
+ if {$inurl} {
+ cgi_put "</a>"
+ }
+ }
+ attach {
+ set attachuid [lindex $tdata 0]
+ set part [lindex $tdata 1]
+ set mimetype [lindex $tdata 2]
+ set mimesubtype [lindex $tdata 3]
+ if {[string length [lindex $tdata 4]]} {
+ set file [lindex $tdata 4]
+ } else {
+ if {[string length [lindex $tdata 5]]} {
+ set file "attachment.[lindex $tdata 5]"
+ } else {
+ set file "unknown.txt"
+ }
+ }
+
+ set attachurl "detach.tcl?uid=${attachuid}&part=${part}"
+ set saveurl "${attachurl}&download=1"
+ if {0 == [string compare -nocase $mimetype "text"]
+ && 0 == [string compare -nocase $mimesubtype "html"]} {
+ append attachurl "&download=1"
+ }
+
+ set attachexp "View ${mimetype}/${mimesubtype} Attachment"
+
+ if {0 == [string compare message [string tolower ${mimetype}]]
+ && 0 == [string compare rfc822 [string tolower ${mimesubtype}]]} {
+ set attmsgurl "wp.tcl?page=compose&oncancel=main.tcl&cid=[WPCmd PEInfo key]&uid=${attachuid}&part=${part}&style="
+ cgi_put [cgi_url [cgi_font size=-1 Fwd] "${attmsgurl}Forward" target=_top]
+ cgi_put "|[cgi_url [cgi_font size=-1 Reply] "${attmsgurl}Reply&reptext=1&repall=1&repqstr=[WPPercentQuote [WPCmd PEMessage $uid replyquote]]" target=_top]"
+
+ } else {
+ cgi_put [cgi_url [cgi_font size=-1 View] $attachurl target=_blank]
+ cgi_put "|[cgi_url [cgi_font size=-1 Save] $saveurl]"
+ }
+
+ if {[info exists attachurl]} {
+ set attachtext [WPurl $attachurl {} $hacktoken $attachexp target=_blank]
+ }
+ }
+ img {
+ if {[info exists showimages] && $showimages == 1
+ && [catch {WPCmd PEMessage $uid cid "<[lindex [lindex $j 1] 0]>"} cidpart] == 0
+ && [string length $cidpart]
+ && [catch {WPCmd PEMessage $uid attachinfo $cidpart} attachinfo] == 0
+ && [string compare [string tolower [lindex $attachinfo 1]] image] == 0} {
+ cgi_put [cgi_img detach.tcl?uid=${uid}&part=${cidpart} "alt=\[[lindex $tdata 1]\]"]
+ } else {
+ cgi_put "\[[lindex $tdata 1]\]"
+ }
+ }
+ fgcolor {
+ if {$infont} {
+ cgi_put "</font>"
+ set infont 0
+ }
+
+ if {[string compare $tdata $fgcolor]} {
+ set fgcolor $tdata
+ if {[string compare $fgcolor $normal_fgcolor]} {
+ cgi_put "<font color=#${tdata}>"
+ set infont 1
+ }
+ }
+ }
+ bgcolor {
+ if {$infont} {
+ cgi_put "</font>"
+ set infont 0
+ }
+
+ if {[string compare $tdata $bgcolor]} {
+ cgi_put "<font style=\"background: #${tdata}\">"
+ set bgcolor $tdata
+ set infont 1
+ }
+ }
+ color {
+ if {$infont} {
+ cgi_put "</font>"
+ set infont 0
+ }
+
+ if {[string compare [lindex $tdata 0] $fgcolor] || [string compare [lindex $tdata 1] $bgcolor]} {
+ cgi_put "<font color=#[lindex $tdata 0] style=\"background: #[lindex $tdata 1]\">"
+ set bgcolor $tdata
+ set infont 1
+ }
+ }
+ italic {
+ switch $tdata {
+ on { cgi_put "<i>" }
+ off { cgi_put "</i>" }
+ }
+ }
+ bold {
+ switch $tdata {
+ on { cgi_put "<b>" }
+ off { cgi_put "</b>" }
+ }
+ }
+ underline {
+ switch $tdata {
+ on { cgi_put "<u>" }
+ off { cgi_put "</u>" }
+ }
+ }
+ strikethru {
+ switch $tdata {
+ on { cgi_put "<s>" }
+ off { cgi_put "</s>" }
+ }
+ }
+ bigfont {
+ switch $tdata {
+ on { cgi_put "<font size=\"+1\">" }
+ off { cgi_put "</font>" }
+ }
+ }
+ smallfont {
+ switch $tdata {
+ on { cgi_put "<font size=\"-1\">" }
+ off { cgi_put "</font>" }
+ }
+ }
+ default {
+ if {[info exists attachtext] && [set ht [string first $hacktoken $attachtext]] >= 0} {
+ set firstbit [string range $attachtext 0 [expr {$ht - 1}]]
+ set lastbit [string range $attachtext [expr {$ht + [string length $hacktoken]}] end]
+ cgi_put " $firstbit[cgi_quote_html [string trimleft $tdata]]$lastbit"
+ unset attachtext
+ } else {
+ cgi_put [cgi_quote_html $tdata]
+ }
+ }
+ }
+ }
+
+ cgi_puts ""
+ }
+ }
+ }
+ }
+
+ if {![info exists attachuid]} {
+ if {!([catch {WPCmd PEMessage $uid attachinfo 1} typing]
+ || [string compare [string tolower [lindex $typing 1]] text]
+ || [string compare [string tolower [lindex $typing 2]] html])} {
+ cgi_table_row {
+ cgi_table_data align=center {
+ cgi_puts [cgi_font font-family=fixed "\[Note: you may also [cgi_url view "detach.tcl?uid=${uid}&part=1" target=_blank] HTML message directly in your browser\]"]
+ }
+ }
+ }
+ }
+ }
+
+ if {$infont} {
+ cgi_put "</font>"
+ }
+
+ if {$inurl} {
+ cgi_put "</a>"
+ }
+
+ if {[info exists _wp(cumulative)]} {
+ set l [string length $_wp(cumulative)]
+ if {$l < 6} {
+ set sl "."
+ while {$l < 6} {
+ append sl "0"
+ incr l
+ }
+ append sl $_wp(cumulative)
+ } else {
+ set sl "[string range $_wp(cumulative) 0 [expr $l - 7]].[string range $_wp(cumulative) [expr $l - 6] end]"
+ }
+
+ set servlettime "servlet = $sl"
+
+ if {[info exists wp_global_loadtime]} {
+ set clickdiff [expr {[clock clicks] - $wp_global_loadtime}]
+ # 500165 clicks/second
+ set st [expr ([string range $clickdiff 0 [expr [string length $clickdiff] - 4]] * 1000) / 500]
+ set l [string length $st]
+ set scripttime "tcl = [string range $st 0 [expr $l - 4]].[string range $st [expr $l - 3] end], "
+ } else {
+ set scripttime ""
+ }
+
+ cgi_puts [cgi_font size=-2 "style=font-family:sans-serif;font-weight:bold" "\[time: ${scripttime}${servlettime}\]"]
+ }
+ }
+ }
+ }
+ } \ No newline at end of file
diff --git a/web/cgi/alpine/1.0/wp.tcl b/web/cgi/alpine/1.0/wp.tcl
new file mode 100755
index 00000000..f649ca0a
--- /dev/null
+++ b/web/cgi/alpine/1.0/wp.tcl
@@ -0,0 +1,135 @@
+#!./tclsh
+# $Id: wp.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# wp.tcl
+#
+# Purpose: CGI script to serve as the frame-work for including
+# supplied script snippets that generate the various
+# javascript-free webpine pages
+#
+# Input:
+set wp_vars {
+ {page {} "main"}
+ {uidList {} ""}
+ {uidpage {} ""}
+}
+
+set wp_global_loadtime [clock clicks]
+
+# inherit global config
+source ./alpine.tcl
+source cmdfunc.tcl
+
+proc sortname {name {current 0}} {
+ global rev me
+
+ switch -- $name {
+ Number { set newname "#" }
+ OrderedSubj { set newname "Ordered Subject" }
+ Arrival { set newname Arv }
+ Status { set newname "&nbsp;" }
+ default { set newname $name }
+ }
+
+ if {$current} {
+ if {$rev > 0} {
+ set text [cgi_imglink increas]
+ set args rev=0
+ } else {
+ set text [cgi_imglink decreas]
+ set args rev=1
+ }
+
+ append newname [cgi_url $text "wp.tcl?page=body&sortrev=1" "title=Reverse $newname ordering" target=body]
+ }
+
+ return $newname
+}
+
+proc linecolor {linenum} {
+ global color
+
+ if {$linenum % 2} {
+ return $color(line1)
+ } else {
+ return $color(line2)
+ }
+}
+
+proc lineclass {linenum} {
+ if {$linenum % 2} {
+ return i0
+ } else {
+ return i1
+ }
+}
+
+proc uid_framed {u mv} {
+ foreach m $mv {
+ if {$u == [lindex $m 1]} {
+ return 1
+ }
+ }
+ return 0
+}
+
+WPEval $wp_vars {
+
+ # Resolve checked uidList
+ foreach uid [split $uidpage ","] {
+ WPCmd PEMessage $uid select [expr [lsearch $uidList $uid] >= 0]
+ }
+
+ # sourced "page" get's CGI parms from environment
+ if {[catch {WPTFScript $page} source]} {
+ switch -regexp -- $page {
+ addredit {
+ set source fr_addredit.tcl
+ }
+ addrsave {
+ set source addrsave.tcl
+ }
+ addrpick {
+ set source addrpick.tcl
+ }
+ ldappick {
+ set source ldappick.tcl
+ }
+ post {
+ set source post.tcl
+ }
+ prune {
+ set source prune.tcl
+ }
+ noop {
+ cgi_html {
+ cgi_head
+ cgi_body {}
+ }
+ unset source
+ }
+ default {
+ WPInfoPage "Web Alpine Error" [font size=+2 "Unknown WebPine page reference: $page."] \
+ "Please complain to the [cgi_link Admin]. Click Back button to return to previous page."
+ }
+ }
+
+ if {[info exists source]} {
+ set source [file join $_wp(cgipath) $_wp(appdir) $_wp(ui1dir) $source]
+ }
+ }
+
+ if {[info exists source]} {
+ source $source
+ }
+}
diff --git a/web/cgi/alpine/2.0/.htaccess b/web/cgi/alpine/2.0/.htaccess
new file mode 100644
index 00000000..dd9da02d
--- /dev/null
+++ b/web/cgi/alpine/2.0/.htaccess
@@ -0,0 +1,7 @@
+# set up default page
+DirectoryIndex ./browse
+
+# extensionless files are scripts
+<FilesMatch "^([^\.]+)$">
+ SetHandler cgi-script
+</FilesMatch>
diff --git a/web/cgi/alpine/2.0/alpine.tcl b/web/cgi/alpine/2.0/alpine.tcl
new file mode 120000
index 00000000..5ad8d42f
--- /dev/null
+++ b/web/cgi/alpine/2.0/alpine.tcl
@@ -0,0 +1 @@
+../alpine.tcl \ No newline at end of file
diff --git a/web/cgi/alpine/2.0/browse b/web/cgi/alpine/2.0/browse
new file mode 100755
index 00000000..9669fd8c
--- /dev/null
+++ b/web/cgi/alpine/2.0/browse
@@ -0,0 +1,336 @@
+#!./tclsh
+# $Id: browse 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# browse
+#
+# Purpose: CGI script that generates a page displaying a message
+# list of the indicated folder.
+#
+# Input: PATH_INFO: [/<col_number>]/<folder_name>[/<uid_of_first_msg>
+# along with possible search parameters:
+set browse_args {
+ {u {} 0}
+ {pageFirst {} ""}
+ {pagePrev {} ""}
+ {pageNext {} ""}
+ {pageLast {} ""}
+ {sort {} ""}
+ {rev {} ""}
+}
+
+# inherit global config
+source ./alpine.tcl
+source ./common.tcl
+source ./foldercache.tcl
+
+# default browse state
+set c 0
+set f INBOX
+
+# TEST:
+proc cgi_suffix {args} {
+ return ""
+}
+
+# for inserting debug comments at end of page
+set dmsgs ""
+proc dm {s} {
+ global dmsgs
+ lappend dmsgs $s
+}
+
+WPEval $browse_args {
+ # grok PATH_INFO for collection, 'c', and folder 'f'
+ if {[info exists env(PATH_INFO)] && [string length $env(PATH_INFO)]} {
+ if {![regexp {^/([0-9]+)/(.*)$} $env(PATH_INFO) dummy c f]} {
+ WPCmd PEInfo statmsg "Cannot open invalid path: $env(PATH_INFO)"
+ set c 0
+ set f INBOX
+ }
+ } elseif {[catch {WPCmd PEFolder current} curf]
+ || [lindex $curf 0] != 0
+ || [string compare -nocase [lindex $curf 1] inbox]} {
+ WPCmd PEInfo statmsg "No Folder Specified, opening INBOX"
+ }
+
+ # verify or visit specified collection/folder
+
+ if {[catch {setCurrentFolder c f u} result]} {
+ set authlist [wpHandleAuthException $result [list $c "open folder $f"] $f]
+ if {0 == [llength $authlist]} {
+ WPCmd PEInfo statmsg "$result"
+ set c 0
+ set f INBOX
+ } else {
+ set opening_folder 1
+ }
+ }
+
+ if {[info exists opening_folder]} {
+ set n 1
+ set u 0
+ } else {
+ # move anything deleted to Trash
+ if {[catch {WPCmd PEMailbox trashdeleted current} result]} {
+ WPCmd PEInfo statmsg "Trash move Failed: $result"
+ }
+
+ # set uid to "current" message?
+ if {$u <= 0 && 0 == [catch {WPCmd PEMailbox current} cm]} {
+ set u [lindex $cm 1]
+ }
+ }
+
+ # get default sort
+ if {[catch {WPCmd PEMailbox sort} cursort]} {
+ set sort date
+ set rev 1
+ }
+
+ # load index drawing routine for this session
+ # save perpage source, proc overhead
+ # call to reinstall during debugging:
+ #catch {WPCmd rename drawMessageList {}}
+ if {0 == [llength [WPCmd info commands drawMessageList]]} {
+ set cgidir [file join $_wp(cgipath) $_wp(appdir) $_wp(ui2dir)]
+ if {[catch {
+ WPCmd source "$_wp(confdir)/alpine.tcl"
+ WPCmd source "${cgidir}/common.tcl"
+ WPCmd source "${cgidir}/messagelist.tcl"
+ WPCmd source "${cgidir}/messageview.tcl"
+ WPCmd rename WPCmd {}
+ WPCmd rename WPEval {}
+ WPCmd rename WPGetInputAndID {}
+ WPCmd rename WPCmdEval WPCmd
+ } result]} {
+ error [list _action browse "cannot load index lister: $result"]
+ }
+ }
+
+ # current context or folder changed?
+ if {$c == 0 && 0 == [string compare -nocase $f inbox]} {
+ set f INBOX
+ }
+
+ # gain situational awareness, UID and number of message that must be on page
+ if {[catch {WPCmd PEMailbox focus} focused]} {
+ set mc 0
+ set focused 0
+ } elseif {$focused > 0} {
+ set mc $focused
+ } else {
+ set mc [WPCmd PEMailbox messagecount]
+ }
+
+ if {$u == 0} {
+ # non given, set to first message in folder
+ set n 1
+ if {[catch {WPCmd PEMailbox uid 1} u]} {
+ set n 0
+ set u 0
+ }
+ } elseif {[catch {WPCmd PEMessage $u number} n]} {
+ set n 0
+ set u 0
+ }
+
+ # lines per page
+ if {[catch {WPCmd PEInfo indexlines} ppg] || $ppg <= 0} {
+ set ppg $_wp(indexlines)
+ } elseif {$ppg > $_wp(indexlinesmax)} {
+ set ppg $_wp(indexlinesmax)
+ }
+
+ # look for image button clicks
+ foreach item $browse_args {
+ if {0 == [catch {cgi_import [lindex $item 0].x}]} {
+ set [lindex $item 0] 1
+ }
+ }
+
+ # preform actions specfied in browse_args
+ # NOTE: basic stuff only to be executed in absense of JS
+ if {[string length $pageFirst]} {
+ set n 1
+ set u [WPCmd PEMailbox uid 1]
+ } elseif {[string length $pagePrev]} {
+ if {$ppg >= $n} {
+ set n 1
+ } else {
+ set n [expr {$n - $ppg}]
+ }
+
+ if {[catch {WPCmd PEMailbox uid $n} u]} {
+ set u 0
+ }
+ } elseif {[string length $pageNext]} {
+ if {($n + $ppg) <= $mc} {
+ incr n $ppg
+ if {[catch {WPCmd PEMailbox uid $n} u]} {
+ set u 0
+ }
+ }
+ } elseif {[string length $pageLast]} {
+ set n $mc
+ if {[catch {WPCmd PEMailbox uid $n} u]} {
+ set u 0
+ }
+ } elseif {[string length $sort]} {
+ switch $rev {
+ 0 -
+ 1 {
+ }
+ default {
+ set rev [lindex $cursort 1]
+ }
+ }
+
+ if {[catch {WPCmd PEMailbox sort $sort $rev} cursort]} {
+ WPCmd PEInfo statmsg "Cannot set sort: $cursort"
+ set cursort [list nonsense 0]
+ } else {
+ # store result
+ WPCmd set sort [list $sort $rev]
+ }
+ }
+
+ # page framing
+ if {[catch {
+ set nm [WPCmd PEMailbox flagcount [list unseen undeleted]]
+ set defcol [WPCmd PEFolder defaultcollection]
+ wpInitPageFraming u n mc ppg pn pt
+ }]} {
+ set pn 1
+ set pt 0
+ set nm 0
+ set defcol 0
+ }
+
+ cgi_http_head {
+ WPStdHttpHdrs "text/html; charset=UTF-8"
+ }
+
+ cgi_html {
+ cgi_head {
+ # WPStdHttpHdrs "text/html; charset=UTF-8"
+ cgi_title [wpPageTitle "$f, $pn of $pt ($nm)"]
+ cgi_base "href=$_wp(serverpath)/$_wp(appdir)/$_wp(ui2dir)/"
+ cgi_stylesheet css/menu.css
+ cgi_stylesheet css/cbn/screen.css
+ cgi_stylesheet css/cbn/folderdialog.css
+ cgi_stylesheet $_wp(yui)/build/container/assets/container-core.css
+ cgi_stylesheet $_wp(yui)/build/menu/assets/skins/sam/menu.css
+ cgi_stylesheet $_wp(yui)/build/button/assets/skins/sam/button.css
+ # YahooUI libraries
+ cgi_script type=text/javascript language="JavaScript" src="$_wp(yui)/build/utilities/utilities.js" {}
+ cgi_script type=text/javascript language="JavaScript" src="$_wp(yui)/build/container/container-min.js" {}
+ cgi_script type=text/javascript language="JavaScript" src="$_wp(yui)/build/datasource/datasource-min.js" {}
+ cgi_script type=text/javascript language="JavaScript" src="$_wp(yui)/build/menu/menu-min.js" {}
+ cgi_script type=text/javascript language="JavaScript" src="$_wp(yui)/build/button/button-min.js" {}
+ # local libraries
+ cgi_script type=text/javascript language="JavaScript" src="lib/common.js" {}
+ cgi_script type=text/javascript language="JavaScript" src="lib/mailbox.js" {}
+ # page specfic JS
+ cgi_javascript {
+ cgi_puts "YAHOO.alpine.cgi_root = '$_wp(serverpath)';"
+ cgi_puts "YAHOO.alpine.app_root = '$_wp(serverpath)/$_wp(appdir)/$_wp(ui2dir)';"
+ cgi_puts "YAHOO.alpine.current.c = $c;"
+ cgi_puts "YAHOO.alpine.current.f = \"$f\";"
+ cgi_puts "YAHOO.alpine.current.u = $u;"
+ cgi_puts "YAHOO.alpine.current.page = $pn;"
+ cgi_puts "YAHOO.alpine.current.count = $mc;"
+ if {[catch {
+ set mb_sel [WPCmd PEMailbox selected]
+ set mb_srch [WPCmd PEMailbox searched]
+ }]} {
+ set mb_sel 0
+ set mb_srch 0
+ }
+
+ cgi_puts "YAHOO.alpine.current.selected = $mb_sel;"
+ cgi_puts "YAHOO.alpine.current.searched = $mb_srch;"
+ cgi_puts "YAHOO.alpine.current.focused = $focused;"
+ cgi_puts "function bodyOnLoad(){"
+ cgi_puts " initMenus();"
+ cgi_puts " initMorcButton('listMorcButton');"
+ cgi_puts " initSelection();"
+ cgi_puts " if(YAHOO.env.ua.gecko > 0){ sizeVPHeight(); window.onresize = resizeVPHeight; }"
+ wpSetMessageListNewMailCheck
+ wpStatusAndNewmailJavascript
+ wpSaveMenuJavascript "browse" $c $f $defcol morcInBrowseDone
+ cgi_puts " if(self.loadDDElements) loadDDElements();"
+
+ if {[info exists authlist]} {
+ cgi_puts "YAHOO.alpine.resubmitRequest = function(){ window.location.href = 'browse/$c/$f'; };"
+ cgi_puts "YAHOO.alpine.cancelRequest = function(){ window.location.href = 'browse/0/INBOX'; };"
+ reportAuthException $authlist
+ }
+ cgi_puts "}"
+ cgi_puts "browserDetect();"
+ }
+ }
+
+ cgi_body class="wap" "onLoad=bodyOnLoad()" {
+ cgi_division id="skip" {
+ cgi_put [cgi_url "Skip to Next Page" "\#" "onClick=return newMessageList({control:this,parms:{op:'next'}});"]
+ cgi_put [cgi_url "Skip to Folders" "folders"]
+ cgi_put [cgi_url "Skip to Compose" "compose"]
+ }
+
+ cgi_division id=msgDragProxy {
+ cgi_put "<b class=b1></b><b class=b2></b><b class=b3></b><b class=b4></b><div id=msgDragProxyText></div><b class=b4></b><b class=b3></b><b class=b2></b><b class=b1></b>"
+ }
+
+ if {$focused} {
+ set context "Search Results in [cgi_quote_html $f]"
+ } else {
+ set context [cgi_quote_html $f]
+ }
+ wpCommonPageLayout browse $c $f $u $context [list [cgi_cgi "$_wp(appdir)/$_wp(ui2dir)/browse/${c}/${f}"] "$f" 1 mailboxSearch()] "" {
+ cgi_division class=hdrBtns {
+ cgi_javascript {
+ cgi_put "if(window.print) document.write('[cgi_buffer {cgi_put [cgi_url "[cgi_span "class=sp hdrBtnImg hbi1" ""][cgi_span "class=hdrBtnText" Print]" "print" "onClick=return printContent()"]}]');"
+ }
+ cgi_put [cgi_url "[cgi_span "class=sp hdrBtnImg hbi2" ""][cgi_span "class=hdrBtnText" Settings]" "settings"]
+ cgi_put [cgi_url "[cgi_span "class=sp hdrBtnImg hbi3" ""][cgi_span "class=hdrBtnText" Help]" \# "onClick=return openMailboxHelp();" class=wap]
+ cgi_put [cgi_url "[cgi_span "class=sp hdrBtnImg hbi4" ""][cgi_span "class=hdrBtnText" "Sign out"]" "../../session/logout.tcl?cid=[WPCmd PEInfo key]&sessid=${sessid}"]
+ }
+ } {
+ cgi_division id=listTopMenubar {
+ cgi_puts [WPCmd cgi_buffer "drawTopListMenuBar $c {$f}"]
+ }
+ cgi_division id=viewTopMenubar "style=\"display: none;\"" {
+ cgi_puts [WPCmd cgi_buffer "drawTopViewMenuBar $c {$f} $u $n"]
+ }
+ } {
+ if {[info exists opening_folder]} {
+ cgi_puts "Opening $f..."
+ } else {
+ cgi_puts [WPCmd cgi_buffer "drawMessageList $c {$f} $n $ppg"]
+ }
+ } {
+ cgi_division id=listBottomMenubar {
+ cgi_puts [WPCmd cgi_buffer "drawBottomListMenuBar $c {$f} $pn $pt $mc"]
+ }
+ cgi_division id=viewBottomMenubar "style=\"display: none;\"" {
+ cgi_puts [WPCmd cgi_buffer "drawBottomViewMenuBar $c {$f} $u $n $mc"]
+ }
+ }
+
+ foreach dmsg $dmsgs {
+ cgi_html_comment "DEBUG: $dmsg"
+ cgi_puts ""
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/2.0/common.tcl b/web/cgi/alpine/2.0/common.tcl
new file mode 100644
index 00000000..41e58a7a
--- /dev/null
+++ b/web/cgi/alpine/2.0/common.tcl
@@ -0,0 +1,905 @@
+# $Id: common 391 2007-01-25 03:53:59Z mikes@u.washington.edu $
+# ========================================================================
+# Copyright 2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# common.tcl
+#
+# Purpose: TCL script snippets that the various CGI script generating pages
+# have in common
+#
+
+proc wpSelectedClass {seld unread defclass} {
+ set class $defclass
+ if {[expr $seld]} {
+ append class " sel"
+ }
+
+ if {$unread} {
+ append class " bld"
+ }
+
+ return $class
+}
+
+proc wpPageTitle {page} {
+ return "$page - Web Alpine 2.0"
+}
+
+proc current_context {page c f cc cf} {
+ set ct {[lsearch {browse view} $page] >= 0 && }
+ if {0 == $cc && 0 == [string compare -nocase inbox $cf]} {
+ append ct {0 == $c && 0 == [string compare -nocase inbox $f]}
+ } else {
+ append ct {$cc == $c && 0 == [string compare $cf $f]}
+ }
+
+ set current [expr $ct]
+}
+
+proc folder_link {current page c f u unread {ficon ""}} {
+ set url "browse/$c/[WPPercentQuote $f {/}]"
+ set clickurl $url
+ set urlid ""
+ set fclass ""
+ set fid ""
+ if {$current} {
+ set urlid "id=\"gFolder\""
+ set urid "id=\"unreadCurrent\""
+ set fid "id=\"fCurrent\""
+ set onclick "onClick=return newMessageList({parms:{op:'unfocus',page:'new'}});"
+ } elseif {[string length $ficon]} {
+ set fq [cgi_quote_html $f]
+ set urid "id=\"unread${fq}\""
+ set fid "id=\"f${fq}\""
+ set onclick [onClick $clickurl]
+ } else {
+ set urid ""
+ set onclick ""
+ }
+
+ if {$unread} {
+ set fclass "class=bld"
+ set urc " ($unread)"
+ } elseif {[string length $ficon] || $current} {
+ set urc ""
+ }
+
+ if {[info exists urc]} {
+ set urt [cgi_span "class=wap unrd" $urid $urc]
+ } else {
+ set urt ""
+ }
+
+ if {![string length $ficon]} {
+ set ficon [cgi_span "class=sp splc splc7" ""]
+ }
+
+ if {[string length $f] > 20} {
+ set f "...[string range $f end-18 end]"
+ }
+
+ return "[cgi_url "$ficon[cgi_span $fid $fclass [cgi_quote_html $f]]" $url class=wap $urlid $onclick]$urt"
+}
+
+proc empty_link {current falias c} {
+ if {$current} {
+ set emptyfunc emptyCurrent
+ } else {
+ switch $falias {
+ Trash { set emptyfunc emptyTrash}
+ Junk { set emptyfunc emptyJunk }
+ }
+ }
+ if {[info exists emptyfunc]} {
+ return "\[[cgi_url "Empty" "#" "onClick=panelConfirm('Are you sure you want to permanently delete the contents of the $falias folder?<p>Deleted messages are gone forever.',{text:'Empty $falias',fn:$emptyfunc}); return false;" title="Permanently delete all messages in the $falias folder" class=wap]\]"
+ }
+
+ return ""
+}
+
+proc context_class {current focus} {
+ if {$current != 0 && $focus == 0} {
+ return "fld sel"
+ }
+
+ return "fld"
+}
+
+proc div_folder {page c f u cc cf unread focus {ficon ""}} {
+ set current [current_context $page $c $f $cc $cf]
+ cgi_division class="[context_class $current $focus]" {
+ cgi_put [folder_link $current $page $cc $cf $u $unread $ficon]
+ }
+}
+
+proc wpStatusAndNewmailJavascript {} {
+ foreach sm [WPCmd PEInfo statmsgs] {
+ regsub -all {'} $sm {\'} sm
+ cgi_puts " addStatusMessage('$sm',15);"
+ }
+
+ if {[catch {WPCmd PESession mailcheck 0} newmail]} {
+ regsub -all {'} $newmail {\'} newmail
+ cgi_puts " addStatusMessage('New Mail Error: $newmail',10);"
+ } else {
+ foreach nm $newmail {
+ set text [lindex $nm 2]
+ regsub -all {'} $text {\'} text
+ if {[string length $text]} {
+ cgi_puts " addStatusMessage('${text}',10);"
+ }
+ }
+ }
+
+ cgi_puts " displayStatusMessages();"
+ catch {WPCmd PEMailbox newmailreset}
+}
+
+proc wpSetMessageListNewMailCheck {} {
+ cgi_puts " setCheckMailFunction('gCheck', function(){ newMailCheck(0,'newMessageList()'); });"
+ cgi_puts " setNewMailCheckInterval([WPCmd PEInfo inputtimeout], 'newMessageList()');"
+}
+
+set clicktest ""
+
+proc onClick {dest} {
+ global clicktest
+
+ if {[string length $clicktest]} {
+ return "onClick=[$clicktest $dest]"
+ }
+
+ return ""
+}
+
+proc var_value {var} {
+ set varval [WPCmd PEConfig varget $var]
+ switch [lindex $varval 1] {
+ textarea {
+ return [join [lindex $varval 0] "\n"]
+ }
+ listbox {
+ return [lindex $varval 0]
+ }
+ default {
+ return [lindex [lindex $varval 0] 0]
+ }
+ }
+
+ return ""
+}
+
+# table representing common overall page layout
+#
+# NOTES: bodyform doesn't flow thru cgi.tcl,but it keeps menu bar's
+# easier for caller to keep stright
+#
+proc wpCommonPageLayout {curpage c f u context searchform leavetest cmds menubar_top content menubar_bottom} {
+ global _wp clicktest
+
+ set thispage [lindex $curpage 0]
+
+ set clicktest $leavetest
+
+ if {[catch {WPCmd PEMailbox focus} focused]} {
+ set focused 0
+ }
+
+ # various positioned elements
+
+ # busy cue
+ cgi_division id="bePatient" {
+ cgi_put "Working..."
+ }
+
+ cgi_table class="page" cellpadding="0" cellspacing="0" {
+ cgi_puts "<tbody>"
+
+ cgi_table_row {
+ cgi_table_data class="spc" colspan="3" {
+ cgi_put [cgi_span "class=sp trans" "style=height:1px;width:2px;" [cgi_span " "]]
+ }
+ }
+
+ # CONTEXT
+ cgi_table_row {
+ cgi_table_data class="wap topHdr" {
+ cgi_division id=hdrLogo {
+ cgi_puts [cgi_img "img/cbn/alpinelogo.gif" name="logo" class="logo" "title=Web Alpine [WPCmd PEInfo version]" class=wap]
+ cgi_puts [cgi_span "class=logo wap" "v [WPCmd PEInfo version].[WPCmd PEInfo revision]"]
+ }
+ }
+
+ cgi_table_data class="topHdr" {
+ cgi_put [cgi_span "class=sp trans" "style=height:2px;width:5px;" " "]
+ }
+
+ cgi_table_data class="wap topHdr" {
+ cgi_division class=hdrContent {
+ # RSS INFO STREAM
+ if {[catch {WPCmd PERss news} news]} {
+ cgi_put [cgi_nbspace]
+ cgi_html_comment "RSS FAILURE: $news"
+ } else {
+ if {[llength $news] > 0} {
+ set style ""
+ set n 0
+ foreach item $news {
+ cgi_put [cgi_span class=RSS $style "[cgi_span "class=sp newsImg" "onClick=\"return rotateNews(this);\"" ""][cgi_url [lindex $item 0] [lindex $item 1] "onClick=this.blur();" id=newsItem$n target=_blank]"]
+ set style "style=display: none;"
+ }
+ } else {
+ cgi_put [cgi_nbspace]
+ }
+ }
+
+ # STATUS LINE
+ cgi_division class=wbar id=statusMessage {
+ cgi_division class=status {
+ cgi_division "class=\"flt edge\"" {
+ cgi_put [cgi_span "class=sp spsm sm1" ""]
+ }
+ cgi_division "class=\"frt cap\"" {}
+ cgi_division "class=\"frt edge\"" {
+ cgi_put [cgi_url [cgi_span "class=sp spsm sm2" ""] "" class=wap "onClick=hideStatusMessage(); return false;"]
+ }
+ cgi_division class=center id=statusMessageText {}
+ }
+ }
+
+ # WEATHER/USAGE/STATUS
+ cgi_division class=wbar id=weatherBar {
+ # RSS WEATHER
+ cgi_division class=weather id=rssWeather {
+ if {[catch {WPCmd PERss weather} weather]} {
+ cgi_html_comment "RSS FAILURE: $weather"
+ cgi_put [cgi_nbspace]
+ } else {
+ if {[llength $weather] > 0} {
+ set item [lindex $weather 0]
+ cgi_put [cgi_url [lindex $item 0] [lindex $item 1] "onClick=this.blur();" target=_blank]
+ } else {
+ cgi_put [cgi_nbspace]
+ }
+ }
+ }
+ if {[info exists _wp(usage)]
+ && 0 == [catch {exec -- $_wp(usage) [WPCmd set env(WPUSER)]} usage]
+ && [regexp {^([0-9]+)[ ]+([0-9]+)$} $usage dummy use_current use_total]
+ && $use_total > 0
+ && $use_current <= $use_total} {
+ cgi_division class=usage {
+ cgi_table width="180px" cellpadding="0" cellspacing="0" {
+ cgi_table_row {
+ cgi_table_data class=wap width="1%" align=right {
+ cgi_put [cgi_nbspace]
+ }
+ cgi_table_data class=wap width="98%" {
+
+ set useperc [expr {($use_current * 100) / $use_total}]
+
+ cgi_table border=0 width="100%" height="12px" cellspacing=0 cellpadding=0 align=right {
+ set percentage [expr {($use_current * 100)/$use_total}]
+ cgi_table_row {
+ cgi_table_data class=wap align=right "style=\"border: 1px solid black; border-right: 0; background-color: #408040;\"" width="$useperc%" {
+ cgi_put [cgi_span "class=sp trans" "style=height:1px;width:1px;" [cgi_span " "]]
+ }
+ cgi_table_data class=wap align=right "style=\"border: 1px solid black; background-color: #ffffff;\"" width="[expr {100 - $useperc}]%" {
+ cgi_put [cgi_span "class=sp trans" "style=height:1px;width:1px;" [cgi_span " "]]
+ }
+ }
+ }
+ }
+ cgi_table_data class=wap width="1%" align=right {
+ set units MB
+ if {[info exists _wp(usage_link)]} {
+ set txt [cgi_url $use_total $_wp(usage_link) "onClick=this.blur();" title="Detailed usage report" target="_blank"]
+ } else {
+ set txt $use_total
+ }
+
+ cgi_put [cgi_span "style=margin-left: .25em" $txt]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ cgi_division class=pageTitle id=pageTitle {
+ cgi_put $context
+ }
+
+ cgi_division class=commands {
+ uplevel 1 $cmds
+ }
+ }
+ }
+ }
+ cgi_noscript {
+ cgi_table_row {
+ cgi_table_data class=noscript colspan="3" {
+ cgi_put "This version of Web Alpine requires Javascript. Please enable Javascript, or use the [cgi_url "HTML Version" "$_wp(serverpath)/$_wp(appdir)/$_wp(ui1dir)/wp.tcl"] of Web Alpine"
+ }
+ }
+ }
+ cgi_table_row {
+ cgi_table_data id=leftColumn class="wap checkMailandCompose" {
+ cgi_table class="toolbarTbl" cellpadding="0" cellspacing="0" {
+ cgi_put "<tbody>"
+ cgi_table_row {
+ cgi_table_data class="wap" {
+ set nUrl "browse/"
+ if {0 == [regexp {^[0-9]+$} $c]} {
+ append nUrl "/0'"
+ } else {
+ append nUrl "/$c'"
+ }
+
+ if {0 == [string length $f]} {
+ append nUrl "/INBOX'"
+ } else {
+ append nUrl "/[WPPercentQuote $f]"
+ }
+
+ set nUrl "browse/$c/$f"
+ cgi_put [cgi_url [cgi_span "class=sp spmbi spmb15" "Check Mail"] $nUrl title="Check for New Mail" id=gCheck "onClick=this.blur(); return false;"]
+ }
+ cgi_table_data class="wap dv1" {
+ cgi_puts [cgi_span "class=sp spmb spmb3" ""]
+ }
+ cgi_table_data class="wap" {
+ set cUrl compose
+ if {[string compare compose $thispage]} {
+ switch $thispage {
+ browse { append cUrl "?pop=browse/$c/$f" }
+ view { append cUrl "?pop=view/$c/$f/$u" }
+ default { append cUrl "?pop=$thispage" }
+ }
+ }
+
+ cgi_put [cgi_url [cgi_span "class=sp spmbi spmb16" "Compose"] $cUrl id=composeLink title="Compose New Message" [onClick $cUrl]]
+ }
+ }
+ cgi_put "</tbody>"
+ }
+
+ cgi_division class=searchFormDiv {
+ cgi_form [lindex $searchform 0] id=searchForm method=post enctype=multipart/form-data {
+ if {[string length [lindex $searchform 3]]} {
+ set sclick [lindex $searchform 3]
+ } else {
+ set sclick "showStatusMessage('Search is NOT implemented yet',3)"
+ }
+
+ cgi_text "searchText=Search in [lindex $searchform 1]" class=wap id=searchField title="Click here to search" "onBlur=recallTextField(this, 'Search in [lindex $searchform 1]')" "onClick=\"clearTextField(this, 'Search in [lindex $searchform 1]')\"" "onKeyPress=\"return searchOnEnter(event,'searchButton');\"" maxlength="256"
+ cgi_put "<input alt=\"Search\" name=\"search\" class=\"sp searchBtn\" type=\"submit\" value=\"\" src=\"\" id=\"searchButton\" onClick=\"${sclick}; this.blur(); return false;\" />"
+
+ set srclass "fld"
+ set searched 0
+ if {[lsearch {browse view} $thispage] >= 0 && 0 == [catch {WPCmd PEMailbox searched} searched] && $searched} {
+ set style ""
+ if {$focused} {
+ append srclass " sel"
+ }
+ } else {
+ set style "display:none;"
+ }
+
+ cgi_division id="searchRefine" style=\"$style\" {
+ cgi_select scope id=searchScope {
+ cgi_option "Search within Results" value=narrow
+ cgi_option "Add to Search Results" value=broad
+ cgi_option "New Search" value=new selected
+ }
+ }
+ }
+ }
+
+ if {[lindex $searchform 2]} {
+ cgi_division id=searchAdvance {
+ cgi_puts [cgi_url "Advanced Search" "browse/$c/$f?search=1" class="wap" "onClick=return advanceSearch();"]
+ }
+ cgi_division id=searchClear style=$style {
+ cgi_puts "\[[cgi_url Clear # class="wap" "onClick=return newMessageList({parms:{op:'search',type:'none',page:'new'}});"]\]"
+ }
+ }
+
+ cgi_division class=searchFormDiv {cgi_puts [cgi_nbspace]}
+
+ cgi_division id=searchResult class="$srclass" style=\"$style\" {
+ cgi_puts [cgi_url "[cgi_span "class=sp splc splc9" ""][cgi_span class=bld id=searchResultText [cgi_quote_html "Search Result ($searched)"]]" browse/$c/$f?search=1 class=wap "onClick=return listSearchResults();"]
+ }
+
+ if {0 == [string compare [lindex $curpage 0] settings]} {
+ uplevel 1 [lindex $curpage 1]
+ } else {
+ cgi_division class="folderPane" {
+ cgi_anchor_name folders
+ cgi_division class=folderList {
+ set defc [WPCmd PEFolder defaultcollection]
+
+ cgi_javascript {
+ cgi_puts "function emptyCurrent(){"
+ cgi_puts " doEmpty(null,'all');"
+ cgi_puts "}"
+ cgi_puts "function emptyTrash(){"
+ cgi_puts " emptyFolder('$defc','[var_value trash-folder]','all',{status:true,fn:'fixupUnreadCount(\"Trash\",0)'});"
+ cgi_puts "}"
+ if {[info exists _wp(spamfolder)]} {
+ cgi_puts "function emptyJunk(){"
+ cgi_puts " emptyFolder('$defc','$_wp(spamfolder)','all',{status:true,fn:'fixupUnreadCount(\"Junk\",0)'});"
+ cgi_puts "}"
+ }
+ }
+
+ set current [current_context $thispage $c $f 0 INBOX]
+ cgi_division class="[context_class $current $focused]" id=targetInbox {
+ cgi_put [folder_link $current $thispage 0 INBOX $u [WPCmd PEFolder unread 0 INBOX] [cgi_span "class=sp splc splc1" ""]]
+ if {0 == [string compare browse $thispage] && [string compare -nocase inbox $f]} {
+ lappend ddtargets [list targetInbox 0 INBOX]
+ }
+ }
+
+ set draftf [var_value postponed-folder]
+ if {0 == [catch {WPCmd PEFolder exists $defc $draftf} result] && $result} {
+ div_folder $thispage $c $f $u $defc Drafts [WPCmd PEFolder unread $defc $draftf] $focused [cgi_span "class=sp splc splc2" ""]
+ }
+
+ div_folder $thispage $c $f $u $defc Sent [WPCmd PEFolder unread $defc [var_value default-fcc]] $focused [cgi_span "class=sp splc splc3" ""]
+
+ if {[info exists _wp(spamfolder)]} {
+ set current [current_context $thispage $c [wpSpecialFolder $c $f] $defc Junk]
+ cgi_division class="[context_class $current $focused]" {
+ cgi_put [cgi_span "xclass=left" [folder_link $current $thispage $defc Junk $u [WPCmd PEFolder unread $defc $_wp(spamfolder)] [cgi_span "class=sp splc splc4" ""]]]
+ cgi_put [cgi_span "class=right" [empty_link $current Junk $defc]]
+ }
+ }
+
+ set current [current_context $thispage $c [wpSpecialFolder $c $f] $defc Trash]
+ cgi_division class="[context_class $current $focused]" id=targetTrash {
+ set trashf [var_value trash-folder]
+ cgi_put [cgi_span class=left [folder_link $current $thispage $defc Trash $u [WPCmd PEFolder unread $defc $trashf] [cgi_span "class=sp splc splc5" id=targetTrashIcon [cgi_span [cgi_nbspace]]]]]
+ cgi_put [cgi_span class=right [empty_link $current Trash $defc]]
+ if {0 == [string compare browse $thispage] && [string compare Trash $f]} {
+ lappend ddtargets [list targetTrash $defc $trashf]
+ }
+ }
+ cgi_division class="wap fld" {
+ cgi_put [cgi_nbspace]
+ }
+
+ cgi_division class="[wpSelectedClass "0 == [string compare $thispage contacts]" 0 "fld"]" id=targetContacts {
+ set cUrl contacts
+ cgi_put [cgi_url "[cgi_span "class=sp splc splc6" ""][cgi_span Contacts]" $cUrl title="Contact List" class=wap [onClick $cUrl]]
+ if {0 == [string compare browse $thispage]} {
+ if {0 == [catch {WPCmd PEAddress books} booklist]} {
+ set tAFargs "\{books:\["
+ set comma ""
+ foreach b $booklist {
+ regsub -all {'} [lindex $b 1] {\'} bname
+ append tAFargs "${comma}\{book:[lindex $b 0],name:'$bname'\}"
+ set comma ","
+ }
+
+ append tAFargs "\]\}"
+ } else {
+ set tAFargs {{}}
+ }
+
+ # pass address book list
+ cgi_puts "<script>setDragTarget('targetContacts',takeAddressFrom,$tAFargs);</script>"
+ }
+ }
+ cgi_division class="fld" {
+ cgi_put [cgi_nbspace]
+ }
+ cgi_division class="ftitle bld" {
+ cgi_put "Recent Folders"
+ }
+
+ if {[catch {WPSessionState left_column_folders} fln]} {
+ set fln $_wp(fldr_cache_def)
+ }
+
+ set nfl 0
+
+ foreach fce [getFolderCache] {
+ set fccol [lindex $fce 0]
+ set fcname [lindex $fce 1]
+ if {0 == [catch {WPCmd PEFolder exists $fccol $fcname} result] && $result} {
+ set current [current_context $thispage $c $f $fccol $fcname]
+ set folderID target$nfl
+ cgi_division class="[context_class $current $focused]" id=$folderID {
+ cgi_put [folder_link $current $thispage $fccol $fcname $u [WPCmd PEFolder unread $fccol $fcname] [cgi_span "class=sp splc splc7" id=${folderID}Icon [cgi_span [cgi_nbspace]]]]
+ if {0 == [string compare browse $thispage] && !($c == $fccol && 0 == [string compare $f $fcname])} {
+ lappend ddtargets [list $folderID $fccol $fcname]
+ }
+ }
+ }
+
+ if {[incr nfl] >= $fln} {
+ break
+ }
+ }
+
+ cgi_division class="wap fld" {
+ cgi_put [cgi_nbspace]
+ }
+ cgi_division class="[wpSelectedClass "0 == [string compare $thispage folders]" 0 "fld"]" {
+ set fUrl "folders"
+ cgi_put [cgi_url "[cgi_span "class=sp splc splc8" ""][cgi_span "View/Manage Folders..."]" $fUrl title="View, Create, Rename, and Delete Folders" class=wap [onClick $fUrl]]
+ }
+ }
+ }
+ }
+ }
+
+ cgi_table_data class="spc" rowspan="2" {
+ cgi_put [cgi_span "class=sp trans" "style=height:2px;width:5px;" [cgi_span " "]]
+ }
+
+ cgi_table_data width="100%" valign="top" {
+ cgi_table class="wap content" cellpadding="0" cellspacing="0" {
+ cgi_puts "<tbody>"
+ cgi_table_row {
+ cgi_table_data id=topToolbar class=wap {
+ cgi_anchor_name toolbar
+ uplevel 1 $menubar_top
+ }
+ }
+
+ # display page content
+ cgi_table_row {
+ cgi_table_data height="100%" valign="top" {
+ cgi_division id=alpineContent {
+ uplevel 1 $content
+ }
+ }
+ }
+
+ cgi_puts "</tbody>"
+ }
+ }
+ }
+ cgi_table_row {
+ cgi_table_data {
+ cgi_table class="toolbarTbl" cellpadding="0" cellspacing="0" {
+ cgi_puts "<tbody>"
+ cgi_table_row {
+ cgi_table_data class="wap txt" {
+ cgi_put "[cgi_span id=blankyblank "[cgi_nbspace]"]"
+ }
+ }
+ cgi_puts "</tbody>"
+ }
+ }
+
+ cgi_table_data id=bottomToolbar class=wap valign="bottom" {
+ uplevel 1 $menubar_bottom
+ }
+ }
+ cgi_table_row {
+ cgi_table_data id=ftrContent colspan="3" class="wap footer" align="center" {
+ set ft "Powered by [cgi_url Alpine "http://www.washington.edu/alpine/" target="_blank"] - [cgi_copyright] 2007 University of Washington"
+ if {0 == [string compare browse $thispage]} {
+ append ft " - [cgi_url "HTML Version" "$_wp(serverpath)/$_wp(appdir)/$_wp(ui1dir)/wp.tcl"]"
+ }
+
+ if {[info exists _wp(comments)] && [lsearch {browse view} $thispage] >= 0} {
+ append ft " - [cgi_url "Comments?" "mailto?to=$_wp(comments)&pop=browse/$c/[WPPercentQuote $f]"]"
+ }
+
+ cgi_puts $ft
+ }
+ }
+ cgi_puts "</tbody>"
+ }
+
+ if {[info exists ddtargets]} {
+ cgi_puts "<script>"
+ foreach t $ddtargets {
+ regsub -all {'} [lindex $t 2] {\'} fn
+ cgi_puts "setDragTarget('[lindex $t 0]',dragOntoFolder,{c:'[lindex $t 1]',f:'$fn'});"
+ }
+ cgi_puts "</script>"
+ }
+}
+
+proc setCurrentFolder {_c _f _u} {
+ global _wp
+
+ upvar 1 $_c c
+ upvar 1 $_f f
+ upvar 1 $_u u
+
+ # verify current collection/folder
+ if {[catch {WPCmd PEFolder current} curfold]} {
+ set curc -1
+ set curf ""
+ } else {
+ set curc [lindex $curfold 0]
+ set curf [lindex $curfold 1]
+ }
+
+ # "current" folder's context
+ if {$c < 0} {
+ set c $curc
+ }
+
+ # "current" folder
+ if {0 == [string length $f]} {
+ set f $curf
+ }
+
+ if {[catch {WPCmd PEFolder defaultcollection} defc]} {
+ set defc 0
+ }
+
+ # open a different folder?
+ if {!($c == $curc && 0 == [string compare $f $curf])} {
+ # weed out special use folders
+ if {0 == $c && 0 == [string compare -nocase $f inbox]} {
+ set f INBOX
+ set truef INBOX
+ } else {
+ if {$c == $defc} {
+ switch -regexp -- $f {
+ {^Drafts$} {
+ set mode draft
+ catch {WPCmd set pre_draft_folder [WPCmd PEFolder current]}
+ set pf [var_value postponed-folder]
+ if {[string compare $curf $pf]} {
+ set truef $pf
+ if {[catch {
+ if {[WPCmd PEFolder exists $c $pf] <= 0} {
+ WPCmd PEFolder create $c $pf
+ }
+ } result]} {
+ WPCmd PEInfo statmsg $result
+ }
+ }
+ }
+ {^Trash$} {
+ set mode trash
+ set pf [var_value trash-folder]
+ if {[string compare $curf $pf]} {
+ set truef $pf
+ if {[catch {
+ if {[WPCmd PEFolder exists $c "$pf"] <= 0} {
+ WPCmd PEFolder create $c "$pf"
+ }
+ } result]} {
+ WPCmd PEInfo statmsg $result
+ }
+ }
+ }
+ {^Junk$} {
+ if {[info exists _wp(spamfolder)]} {
+ set mode junk
+ set pf $_wp(spamfolder)
+ if {[string compare $curf $pf]} {
+ set truef $pf
+ if {[catch {
+ if {[WPCmd PEFolder exists $c $pf] <= 0} {
+ WPCmd PEFolder create $c $pf
+ }
+ } result]} {
+ WPCmd PEInfo statmsg $result
+ }
+ }
+ } else {
+ set truef $f
+ }
+ }
+ {^Sent$} {
+ set mode sent
+ set pf [var_value default-fcc]
+ if {[string compare $curf $pf]} {
+ set truef $pf
+ if {[catch {
+ if {[WPCmd PEFolder exists $c $pf] <= 0} {
+ WPCmd PEFolder create $c $pf
+ }
+ } result]} {
+ WPCmd PEInfo statmsg $result
+ }
+ }
+ }
+ default {
+ set truef $f
+ }
+ }
+ } else {
+ set truef $f
+ }
+ }
+
+ if {[info exists truef]} {
+ if {[catch {eval WPCmd PEMailbox open [list $c $truef]} reason]} {
+ error $reason
+ } else {
+ # do_broach handled this: WPCmd PEInfo statmsg "$f opened with [WPCmd PEMailbox messagecount] messages"
+
+ if {![info exists mode]} {
+ addFolderCache $c $f
+ }
+
+ if {[catch {WPCmd PEMailbox trashdeleted current} result]} {
+ WPCmd PEInfo statmsg "Detete FAILURE: $result"
+ }
+ }
+ }
+ } else {
+ # verify $c $f (and $u if present) exists
+ if {!($c == 0 && 0 == [string compare -nocase inbox $f])} {
+ if {[catch {WPCmd PEFolder exists $c $f} result]} {
+ WPCmd PEInfo statmsg "Cannot test $f for existance: $result"
+ } elseif {$result <= 0} {
+ WPCmd PEInfo statmsg "Folder $f in collection $c does not exist"
+ }
+ }
+
+ if {$u > 0 && [catch {WPCmd PEMessage $u number} result]} {
+ WPCmd PEInfo statmsg "Message $u does not exist: $result"
+ }
+ }
+}
+
+proc wpFolderMode {c f} {
+ if {$c == [WPCmd PEFolder defaultcollection]} {
+ switch -exact -- $f {
+ Drafts { return draft }
+ Sent { return sent }
+ Junk { return junk }
+ Trash { return trash }
+ }
+ }
+
+ return ""
+}
+
+proc wpInitPageFraming {_u _n _mc _ppg _pn _pt} {
+ upvar 1 $_u u
+ upvar 1 $_n n
+ upvar 1 $_mc mc
+ upvar 1 $_ppg ppg
+ upvar 1 $_pn pn
+ upvar 1 $_pt pt
+
+ if {[catch {WPCmd PEMailbox trashdeleted current} result]} {
+ WPCmd PEInfo statmsg "Deleted move to Trash failed: $result"
+ }
+
+ if {[catch {WPCmd PEMailbox focus} focused]} {
+ set focused 0
+ }
+
+ if {$n > 0} {
+ set pt [expr {$mc / $ppg}]
+
+ if {$pt < 1} {
+ set pt 1
+ } elseif {$mc % $ppg} {
+ incr pt
+ }
+
+ if {$focused} {
+ set nth [WPCmd PEMailbox messagecount before $n]
+ incr nth
+ } else {
+ set nth $n
+ }
+
+ if {$nth > $ppg} {
+ set pn [expr {$nth / $ppg}]
+ if {$nth % $ppg} {
+ incr pn
+ }
+
+ set n [expr {(($pn - 1) * $ppg) + 1}]
+ if {$focused} {
+ set n [WPCmd PEMailbox next [WPCmd PEMailbox first] $n]
+ }
+ } else {
+ set pn 1
+ set n [WPCmd PEMailbox first]
+ }
+
+ set u [WPCmd PEMailbox uid $n]
+ } else {
+ set mc 0
+ set pt 1
+ set pn 1
+ }
+}
+
+proc wpHandleAuthException {err c {f ""}} {
+ global _wp
+
+ if {[regexp {^CERTQUERY ([^+]+)\+\+(.*)$} $err dummy server reason]} {
+ return "code:'CERTQUERY',server:\"${server}\",reason:\"${reason}\""
+ } elseif {[regexp {^CERTFAIL ([^+]+)\+\+(.*)$} $err dummy server reason]} {
+ return "code:'CERTFAIL',server:\"${server}\",reason:\"${reason}\""
+ } elseif {[regexp {^NOPASSWD (.*)$} $err dummy server]} {
+ return "code:'NOPASSWD',c:[lindex $c 0],f:\"${f}\",server:\"{${server}/tls}\",reason:\"[lindex $c 1]\",sessid:\"$_wp(sessid)\""
+ } elseif {[regexp {^BADPASSWD (.*)$} $err dummy server]} {
+ return "code:'BADPASSWD',c:[lindex $c 0],f:\"${f}\",server:\"{${server}/tls}\",reason:\"[lindex $c 1]\",sessid:\"$_wp(sessid)\""
+ }
+
+ return {}
+}
+
+proc reportAuthException {exp} {
+ cgi_put "processAuthException(\{$exp\});"
+}
+
+proc wpSaveMenuJavascript {p c f dc onDone {df ""}} {
+ set nn 0;
+ cgi_put "updateSaveCache(\"$p\",$c,\"$f\",$dc,$onDone,\["
+ foreach {oname oval} [getSaveCache $df] {
+ if {[incr nn] > 1} {
+ cgi_put ","
+ }
+
+ cgi_put "{fn:\"$oname\",fv:\"$oval\"}"
+ }
+ cgi_puts "\]);"
+}
+
+proc wpLiteralFolder {c f} {
+ global _wp
+
+ if {$c == [WPCmd PEFolder defaultcollection]} {
+ switch -exact -- $f {
+ Draft {
+ return [var_value postponed-folder]
+ }
+ Sent {
+ return [var_value default-fcc]
+ }
+ Junk {
+ if {[info exists _wp(spamfolder)]} {
+ return $_wp(spamfolder)
+ }
+ }
+ Trash {
+ return [var_value trash-folder]
+ }
+ }
+ }
+
+ return $f
+}
+
+proc wpSpecialFolder {c f} {
+ global _wp
+
+ if {$c == [WPCmd PEFolder defaultcollection]} {
+ if {0 == [string compare $f [var_value postponed-folder]]} {
+ return Draft
+ }
+
+ if {0 == [string compare $f [var_value default-fcc]]} {
+ return Sent
+ }
+ if {[info exists _wp(spamfolder)]} {
+ if {0 == [string compare $f $_wp(spamfolder)]} {
+ return Junk
+ }
+ }
+ if {0 == [string compare $f [var_value trash-folder]]} {
+ return Trash
+ }
+ }
+
+ return $f
+}
diff --git a/web/cgi/alpine/2.0/compose b/web/cgi/alpine/2.0/compose
new file mode 100755
index 00000000..bf6ce9ec
--- /dev/null
+++ b/web/cgi/alpine/2.0/compose
@@ -0,0 +1,817 @@
+#!./tclsh
+# $Id: compose 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# compose
+#
+# Purpose: CGI script template for Web Alpine 2.0 pages
+#
+# Input: PATH_INFO: [/<col_number>]/<folder_name>[/<uid_of_viewed_msg>
+# along with possible search parameters:
+set compose_args {
+ {pop {} browse/0/INBOX}
+ {repqstr "" "_NONE_SET_"}
+ {contacts "" ""}
+}
+
+# inherit global config
+source ./alpine.tcl
+source ./foldercache.tcl
+source ./common.tcl
+
+set script_base "$_wp(serverpath)/$_wp(appdir)/$_wp(ui2dir)/"
+
+# TEST
+proc cgi_suffix {args} {
+ return ""
+}
+
+if {[info exists debugging]} {
+ # WHILE TESTING/DEBUGGING
+ set dmsgs ""
+ proc dm {s} {
+ global dmsgs
+
+ lappend dmsgs $s
+ }
+}
+
+# On input failure, redirect to home page "browse"
+proc browse_redirect {} {
+ cgi_http_head {
+ cgi_redirect browse
+ }
+}
+
+proc url {d} {
+ global _wp
+
+ return [file join / $_wp(urlprefix) $_wp(appdir) $_wp(ui2dir) $d]
+}
+
+proc leave_test {d} {
+ return "return cancelComposition('[url $d]');"
+}
+
+proc fieldname {name} {
+ regsub -all -- {-} [string tolower $name] {_} fieldname
+ return $fieldname
+}
+
+proc default_fcc {itemval} {
+ global fccdefault f_name f_colid
+
+ if {[catch {WPCmd PEFolder collections} collections]} {
+ set collections ""
+ }
+
+ # fcc's itemval should be a list of collection-id and folder-name
+ if {[llength $itemval] == 2} {
+ set fcccol [lindex $itemval 0]
+ set fccname [lindex $itemval 1]
+ } elseif {[info exists f_name] && [string length $f_name]} {
+ set fcccol $f_colid
+ set fccname $f_name
+ } elseif {[info exists fccdefault] || [catch {WPCmd PECompose fccdefault} fccdefault] == 0} {
+ set fcccol [lindex $fccdefault 0]
+ set fccname [lindex $fccdefault 1]
+ unset fccdefault
+ } else {
+ set fccname sent-mail
+ if {[llength $collections] > 1} {
+ set fcccol 1
+ } else {
+ set fcccol 0
+ }
+ }
+
+ return [list $fccname $fcccol $collections]
+}
+
+set firstheader 1
+proc rowfield {item itemval} {
+ global _wp addrheaders firstheader compose_mode
+
+ if {0 == [string compare [string tolower $item] attach]} {
+ return
+ }
+
+ set isaddr [lsearch -exact $addrheaders [string tolower $item]]
+
+ cgi_table_row {
+ cgi_table_data class="spc" colspan="3" {}
+ }
+ cgi_table_row {
+ cgi_table_data class="wap lbl" {
+ cgi_anchor_name "to"
+ if {$isaddr >= 0} {
+ cgi_button "${item}..." title="Select email addresses from Contacts or Directory" "onClick=return pickContact('$item','field${item}');"
+ } elseif {0 == [string compare [string tolower $item] fcc]} {
+ cgi_button "Fcc..." title="Carbon Copy to Folder" "onClick=this.blur(); return pickFolder('folderList','Set Fcc','[WPCmd PEFolder defaultcollection]',pickFccDone);"
+ } else {
+ cgi_put "<label for=\"[string tolower $item]\">$item:</label>"
+
+ }
+ }
+ cgi_table_data class="wap mid" {
+ if {$isaddr >= 0} {
+ cgi_textarea [string tolower $item]=$itemval class="mid" id="field$item"
+ cgi_division id="container$item" class="yui-skin-sam" {}
+ } else {
+ switch -- [string tolower $item] {
+ fcc {
+ set deffcc [default_fcc $itemval]
+
+ set fccname [lindex $deffcc 0]
+ set fcccol [lindex $deffcc 1]
+
+ cgi_text "[fieldname $item]=$fccname" id="field$item" class="mid" "onBlur=fccExists(false);"
+ cgi_text "colid=$fcccol" type=hidden "notab" id="fieldFccCollection"
+ }
+ default {
+ cgi_text "[fieldname $item]=$itemval" id="field$item" class="mid"
+ }
+ }
+ }
+ }
+ cgi_table_data class="wap rgt" {
+ if {$firstheader} {
+ set firstheader 0
+ cgi_put "[cgi_span id="moreComposeHeaderText" [cgi_url "More Headers" "#" "onClick=showMoreComposeHeaders(); return false;" title="Show Bcc:, Fcc:, ..."]]"
+ cgi_put "[cgi_span id="lessComposeHeaderText" [cgi_url "Fewer Headers" "#" "onClick=showLessComposeHeaders(); return false;"]]"
+ }
+ if {0 && 0 == [string compare [string tolower $item] fcc]} {
+ cgi_puts [cgi_url "[cgi_nbspace]Select Folder" "#" "onClick=this.blur(); pickFolder('folderList','Set Fcc','[WPCmd PEFolder defaultcollection]',pickFccDone); return false;"]
+ } elseif {0 == [string compare [string tolower $item] subject]} {
+ if {[string compare -nocase rich $compose_mode]} {
+ set flipto Rich
+ } else {
+ set flipto Plain
+ }
+ cgi_put [cgi_url "$flipto Text" "" id=flipRich "onClick=return flipRichText();"]
+ }
+ }
+ }
+}
+
+set c -1
+set f ""
+set u 0
+set p ""
+set body ""
+set defaultheaders {to cc subject}
+set addrheaders {to cc bcc}
+set entryfocus fieldTo
+set title Compose
+
+WPEval $compose_args {
+
+ set compose_mode plain
+
+ # figure out compose mode and collect necessary stuff
+ if {[regexp {/(reply|replyall|forward|resume|mailto)$} $env(SCRIPT_NAME) dummy mode]} {
+ if {0 == [string compare resume $mode]} {
+ if {[info exists env(PATH_INFO)]} {
+ if {[regexp {^/([0-9]+)$} $env(PATH_INFO) dummy u]} {
+ set title "Resume Draft"
+ set c [WPCmd PEFolder defaultcollection]
+ set f Drafts
+ if {[catch {WPCmd PEPostpone extract $u {html}} postponed]} {
+ WPCmd PEInfo statmsg "Cannot get Draft: $postponed"
+ } else {
+ foreach h [lindex $postponed 0] {
+ append hdrvals([fieldname [lindex $h 0]]) [lindex $h 1]
+ }
+
+ # return to message's edit mode
+ set compose_mode plain
+
+ foreach opt [lindex $postponed 3] {
+ switch [lindex $opt 0] {
+ charset {
+ set charset [lindex $opt 1]
+ }
+ subtype {
+ if {0 == [string compare -nocase [lindex $opt 1] html]} {
+ set compose_mode rich
+ }
+ }
+ }
+ }
+
+ set body [join [lindex $postponed 1] "\r\n"]
+ set attachments [lindex $postponed 2]
+
+ unset postponed
+
+ catch {WPCmd set help_context compose}
+ if {[catch {WPCmd set pre_draft_folder} curfold]} {
+ set pop "browse/0/INBOX"
+ } else {
+ set pop "browse/[lindex $curfold 0]/[wpSpecialFolder [lindex $curfold 0] [lindex $curfold 1]]"
+ }
+ }
+ } else {
+ WPCmd PEInfo statmsg "Invalid Draft UID: $env(PATH_INFO)"
+ browse_redirect
+ cgi_exit
+ }
+ } else {
+ WPCmd PEInfo statmsg "Missing Draft UID"
+ browse_redirect
+ cgi_exit
+ }
+ } elseif {0 == [string compare mailto $mode]} {
+ set title "Compose"
+
+ if {[catch {cgi_import to}] == 0} {
+ set hdrvals(to) $to
+ }
+
+ if {[catch {cgi_import cc}] == 0} {
+ set hdrvals(to) $cc
+ }
+
+ if {[catch {cgi_import subject}] == 0} {
+ set hdrvals(subject) $subject
+ }
+
+ catch {cgi_import body}
+ } else {
+ if {!([info exists env(PATH_INFO)] && [regexp {^/([0-9]+)/(.+)/([0-9]+)[/]?([\.0-9]*)*} $env(PATH_INFO) dummy c f u p])} {
+ WPCmd PEInfo statmsg "Invalid Invocation: $env(SCRIPT_NAME) (info = $env(PATH_INFO)"
+ browse_redirect
+ cgi_exit
+ }
+
+ # process action specfied by compose_args
+ switch $mode {
+ reply -
+ replyall {
+ set title "Reply to Message [WPCmd PEMessage $u number]"
+ if {[string length $p]} {
+ append title ", part $p"
+ }
+
+ foreach h [WPCmd PEMessage $u replyheaders $p] {
+ set hdrvals([fieldname [lindex $h 0]]) [lindex $h 1]
+ }
+
+ if {[string compare $mode replyall]} {
+ catch {unset hdrvals(cc)}
+ }
+
+ if {[WPCmd PEInfo feature quell-format-flowed] == 0} {
+ set flowed 1
+ }
+
+ if {0 == [string compare $repqstr "_NONE_SET_"]} {
+ set repqstr [WPCmd PEMessage $u replyquote]
+ }
+
+ set replytext [WPCmd PEMessage $u replytext $repqstr $p]
+
+ if {[string compare $compose_mode plain]} {
+ set replytext [cgi_quote_html $replytext]
+ set body [join [lindex $replytext 0] "<br />"]
+ } else {
+ set body [join [lindex $replytext 0] "\n"]
+ }
+
+ if {[WPCmd PEInfo feature include-attachments-in-reply]} {
+ set attachments [lindex $replytext 1]
+ }
+
+ catch {WPCmd set help_context reply}
+ set cuid $u
+ set entryfocus composeText
+ }
+ forward {
+ set title "Forward [WPCmd PEMessage $u number]"
+ if {[string length $p]} {
+ append title ", part $p"
+ }
+
+ foreach h [WPCmd PEMessage $u forwardheaders $p] {
+ set hdrvals([fieldname [lindex $h 0]]) [lindex $h 1]
+ }
+
+ foreach line [WPCmd PEInfo signature] {
+ append body "$line\r\n"
+ }
+
+ if {[WPCmd PEInfo feature quell-format-flowed] == 0} {
+ set flowed 1
+ }
+
+ set forwardtext [WPCmd PEMessage $u forwardtext $p]
+
+ if {[string compare $compose_mode plain]} {
+ set forwardtext [cgi_quote_html $forwardtext]
+ append body [join [lindex $forwardtext 0] "<br />"]
+ } else {
+ append body [join [lindex $forwardtext 0] "\r\n"]
+ }
+
+ set attachments [lindex $forwardtext 1]
+ catch {WPCmd set help_context forward}
+ set cuid $u
+ }
+ }
+ }
+ } else {
+ foreach line [WPCmd PEInfo signature] {
+ append body "$line\r\n"
+ }
+
+ if {[string length $contacts] && [regexp {^[0-9\.,]+$} $contacts]} {
+ set hdrvals(to) ""
+ foreach c [split $contacts ","] {
+ if {[regexp {^([0-9]+)\.([0-9]+)$} $c dummy abook aindex]} {
+ # compose to address book entry
+ if {0 == [catch {WPCmd PEAddress entry $abook "" $aindex} entryval]} {
+ if {[string length $hdrvals(to)]} {
+ append hdrvals(to) ", "
+ }
+
+ append hdrvals(to) [lindex $entryval 0]
+ set newfcc [lindex $entryval 1]
+ if {[string length $newfcc]} {
+ global fccdefault
+ if {[string compare $newfcc "\"\""] == 0} {
+ set newfcc ""
+ }
+ if {[catch {WPCmd PEFolder collections} collections]} {
+ set collections ""
+ }
+ set fccdefault [list [expr {[llength $collections] > 1 ? 1 : 0}] $newfcc]
+ }
+ }
+ }
+ }
+ }
+
+ WPCmd PECompose noattach
+ }
+
+ if {[catch {WPCmd PECompose userhdrs} headers]} {
+ error [list _action "Header Retrieval Failure" $headers]
+ }
+
+ if {![info exists charset]} {
+ set charset UTF-8
+ }
+
+ cgi_http_head {
+ WPStdHttpHdrs "text/html; charset=\"$charset\""
+ }
+
+ cgi_html {
+ cgi_head {
+ cgi_http_equiv Content-Type "text/html; charset=$charset"
+ cgi_title [wpPageTitle "$title"]
+ cgi_base "href=$script_base"
+ # yui css
+ cgi_stylesheet "$_wp(yui)/build/autocomplete/assets/skins/sam/autocomplete.css"
+ cgi_stylesheet "$_wp(yui)/build/tabview/assets/tabview-core.css"
+ cgi_stylesheet "$_wp(yui)/build/button/assets/skins/sam/button.css"
+ cgi_stylesheet "$_wp(yui)/build/container/assets/skins/sam/container.css"
+ cgi_stylesheet "$_wp(yui)/build/editor/assets/skins/sam/simpleeditor.css"
+ # local css
+ cgi_stylesheet css/cbn/screen.css
+ cgi_stylesheet css/menu.css
+ cgi_stylesheet css/cbn/folderdialog.css
+ cgi_stylesheet css/cbn/contactdialog.css
+ cgi_puts "<style type='text/css'>"
+ cgi_puts ".yui-skin-sam .yui-ac-content{ width:70%; }"
+ cgi_puts ".yui-navset .yui-nav li { margin-right:0.16em; padding-top:1px; zoom:1; }"
+ cgi_puts ".yui-navset .yui-nav .selected { margin-bottom:-1px; }"
+ cgi_puts ".yui-navset .yui-nav a { background:#dadbdb url($_wp(yui)/build/assets/skins/sam/sprite.png) repeat-x; border:solid #a3a3a3; border-width:0 1px; color:#000; text-decoration:none; }"
+ cgi_puts ".yui-navset .yui-nav li a em { border-top:solid 1px #a3a3a3; border-bottom:0; cursor:hand; padding:0.2em 0.5em; top:-1px; position:relative; }"
+ cgi_puts ".yui-navset .yui-nav .selected a, .yui-navset .yui-nav a:focus, .yui-navset .yui-nav a:hover { background:#214197 url($_wp(yui)/build/assets/skins/sam/sprite.png) repeat-x left -1400px; color:#fff; }"
+ cgi_puts ".yui-navset .yui-nav .selected a em { padding:0.3em 0.5em; }"
+ cgi_puts ".yui-navset .yui-nav .selected a, .yui-navset .yui-nav a:hover, .yui-navset .yui-nav a:focus { border-color:#243356; }"
+ cgi_puts ".yui-navset .yui-nav a:hover em, .yui-navset .yui-nav a:focus em, .yui-navset .yui-nav .selected a em { border-color:#233356 #406ed9; }"
+ cgi_puts ".yui-navset .yui-nav { border-bottom:1px solid #243356; position:relative; zoom:1; }"
+ cgi_puts ".yui-navset .yui-content { border-top:5px solid #214095; }"
+ cgi_puts "</style>"
+ # Yahoo UI Libraries
+ cgi_script type=text/javascript language="JavaScript" src="$_wp(yui)/build/utilities/utilities.js" {}
+ cgi_script type=text/javascript language="JavaScript" src="$_wp(yui)/build/container/container-min.js" {}
+ cgi_script type=text/javascript language="JavaScript" src="$_wp(yui)/build/datasource/datasource-min.js" {}
+ cgi_script type=text/javascript language="JavaScript" src="$_wp(yui)/build/get/get-min.js" {}
+ cgi_script type=text/javascript language="JavaScript" src="$_wp(yui)/build/autocomplete/autocomplete-min.js" {}
+ cgi_script type=text/javascript language="JavaScript" src="$_wp(yui)/build/button/button-min.js" {}
+ cgi_script type=text/javascript language="JavaScript" src="$_wp(yui)/build/tabview/tabview-min.js" {}
+ cgi_script type=text/javascript language="JavaScript" src="$_wp(yui)/build/editor/simpleeditor-min.js" {}
+ # local libraries
+ cgi_script language="JavaScript" src="lib/common.js" {}
+ cgi_script language="JavaScript" src="lib/compose.js" {}
+ # page/content specific
+ cgi_javascript {
+ cgi_puts "YAHOO.alpine.cgi_root = '$_wp(serverpath)';"
+ cgi_puts "YAHOO.alpine.cgi_base = '$script_base';"
+ if {0 == [catch {WPCmd PEFolder current} curfold]} {
+ cgi_puts "YAHOO.alpine.current.c = [lindex $curfold 0];"
+ cgi_puts "YAHOO.alpine.current.f = \"[lindex $curfold 1]\";"
+ }
+ cgi_puts "var gRichState = ('$compose_mode' == 'rich');"
+ cgi_puts "var gRichtextEditor, gRichtextRendered = false;"
+ cgi_puts "var gFieldHeight = 0;"
+ cgi_puts "var gDefCol = [WPCmd PEFolder defaultcollection];"
+ cgi_puts "var gDraftFolder = '[var_value postponed-folder]';"
+
+ # set up contacts dialog
+ set markup [cgi_buffer {
+ cgi_division id="contactsDialogFramework" {
+ set dirs [catch {WPCmd PELdap directories} directories]
+ if {0 == $dirs} {
+ set class "class=yui-navset"
+ } else {
+ set class ""
+ }
+
+ cgi_division id="contactDialog" $class {
+ if {0 == $dirs} {
+ cgi_bullet_list class="yui-nav" {
+ cgi_li class="selected" [cgi_url [cgi_emphasis Contacts] "#contactList" "onClick=this.blur();"]
+ cgi_li [cgi_url [cgi_emphasis Directory] "#directoryQuery" "onClick=this.blur();"]
+ }
+ cgi_division class=yui-content {
+ cgi_division id="contactList" class="contactContent" {
+ cgi_put "Loading..."
+ }
+ cgi_division id="directoryQuery" class=contactContent {
+ cgi_division class="clistContext" "style=\"text-align: center;\"" {
+ cgi_form $_wp(appdir)/$_wp(ui2dir)/conduit/query.tcl method=get target=formResponse {
+ if {[llength $directories] > 1} {
+ cgi_select dir class=wap {
+ for {set i 0} {$i < [llength $directories]} {incr i} {
+ set dname [lindex [lindex $directories $i] 0]
+ if {0 == [string length $dname]} {
+ set dname "Directory [expr {$i + 1}]"
+ }
+ cgi_option $dname value=$i
+ }
+ }
+ } else {
+ if {[string length [lindex [lindex $directories 0] 0]]} {
+ cgi_put [cgi_span [lindex [lindex $directories 0] 0]]
+ }
+
+ cgi_text "dir=0" type=hidden "notab"
+ }
+
+ cgi_text "query=" size="20" length="40" id="dirQuery"
+ cgi_submit_button go=Search
+ }
+ }
+ cgi_division id="dirResult" class="clistContacts" {
+ cgi_put [cgi_span "Search results appear here..."]
+ }
+ }
+ }
+ } else {
+ cgi_division id="contactList" class="contactContent" {
+ cgi_put "Loading..."
+ }
+ }
+ }
+ }
+ }]
+
+ regsub -all {'} $markup {\'} markup
+ regsub -all {\n} $markup {} markup
+ cgi_puts "YAHOO.alpine.pickcontact.markup = '$markup';"
+ cgi_puts "function bodyOnLoad() {"
+ cgi_puts " initMenus();"
+ cgi_puts " if(gRichState){ initSimpleEditor([expr {0 == [string compare $entryfocus composeText]}]); if(gRichtextEditor){ gRichtextEditor.render(); gRichtextRendered = true; }}"
+ cgi_puts " autoSizeAddressField('fieldTo');"
+ cgi_puts " autoSizeAddressField('fieldCc');"
+ cgi_puts " autoSizeAddressField('fieldBcc');"
+ cgi_puts " setCheckMailFunction('gCheck', newMailCheck);"
+ cgi_puts " setNewMailCheckInterval([WPCmd PEInfo inputtimeout]);"
+ if {[info exists _wp(autodraft)]} {
+ cgi_puts " setAutoDraftInterval($_wp(autodraft));"
+ }
+ wpStatusAndNewmailJavascript
+ cgi_puts " setCursorPosition('$entryfocus',0);"
+ cgi_puts " fccExists(true);"
+ cgi_puts "}"
+
+ cgi_puts "browserDetect();"
+ }
+ }
+
+ cgi_body class="wap" "onLoad=bodyOnLoad()" {
+ cgi_division id="skip" {
+ cgi_put [cgi_url "Skip to Compose Body" "#to"]
+ cgi_put [cgi_url "Skip to Message List" "browse#messages"]
+ cgi_put [cgi_url "Skip to Folders" "#folders"]
+ }
+
+ cgi_puts {<iframe name="formResponse" id="formResponse" src="img/cbn/spritelib.gif"></iframe>}
+
+ wpCommonPageLayout compose "$c" "$f" "$u" \
+ $title \
+ [list [cgi_cgi "$_wp(appdir)/$_wp(ui2dir)/browse/${c}/${f}"] Composition 0] \
+ leave_test\
+ {
+ cgi_division class=hdrBtns {
+ cgi_javascript {
+ cgi_put "if(window.print) document.write('[cgi_buffer {cgi_put [cgi_url "[cgi_span "class=sp hdrBtnImg hbi1" ""][cgi_span "class=hdrBtnText" Print]" "print" "onClick=return printContent()"]}]');"
+ }
+
+ cgi_put [cgi_url "[cgi_span "class=sp hdrBtnImg hbi3" ""][cgi_span "class=hdrBtnText" Help]" "javascript:openHelpWindow('compose.html');" class=wap]
+ cgi_put [cgi_url "[cgi_span "class=sp hdrBtnImg hbi4" ""][cgi_span "class=hdrBtnText" "Sign out"]" "" "onClick=[leave_test "../../session/logout.tcl?cid=[WPCmd PEInfo key]&sessid=${sessid}"]"]
+ }
+ } {
+ cgi_anchor_name "toolbar"
+ cgi_table class="toolbarTbl" cellpadding="0" cellspacing="0" {
+ cgi_puts "<tbody>"
+ cgi_table_row {
+ cgi_table_data class="wap" {
+ cgi_put [cgi_url [cgi_span "class=sp spmbi spmb9" "Send"] "post" "onClick=return sendComposition();"]
+ }
+ cgi_table_data class="wap" {
+ cgi_put [cgi_url [cgi_span "class=sp spmbi spmb11" "Attach File"] "" "onClick=this.blur(); return addAttachField();"]
+ }
+ cgi_table_data class="wap" {
+ cgi_put [cgi_url [cgi_span "class=sp spmbi spmb10" "Save Draft"] post/draft "onClick=return saveDraft();"]
+ }
+ cgi_table_data class="wap dv1" {
+ cgi_put [cgi_img "img/cbn/div.gif"]
+ }
+ cgi_table_data class="wap yui-skin-sam yuimenu" id="priorityButtonContainer" {
+ cgi_bullet_list class="wap menu" {
+ cgi_put "<li class=\"menuHdr\">[cgi_url "Priority [cgi_img "img/cbn/menu.gif" class="wap menuDn menuImg"]" "#" "onClick=return false;"]"
+ cgi_division {
+ cgi_bullet_list class=priority {
+ cgi_li [cgi_url "[cgi_span "class=sp spfcl blank" ""]Highest" "" id="pri5" "onClick=return setPriority(this,'highest')"]
+ cgi_li [cgi_url "[cgi_span "class=sp spfcl blank" ""]High" "" id="pri4" "onClick=return setPriority(this,'high')"]
+ cgi_li [cgi_url "[cgi_span "class=sp spfcl blank" ""]Normal" "" id="pri3" "onClick=return setPriority(this,'normal')"]
+ cgi_li [cgi_url "[cgi_span "class=sp spfcl blank" ""]Low" "" id="pri2" "onClick=return setPriority(this,'low')"]
+ cgi_li [cgi_url "[cgi_span "class=sp spfcl blank" ""]Lowest" "" id="pri1" "onClick=return setPriority(this,'lowest')"]
+ cgi_li "<hr />[cgi_url "[cgi_span "class=sp spfcl spfcl3" ""]No[cgi_nbspace]Priority" "" id="pri0" "onClick=return setPriority(this,'')"]"
+ }
+ }
+ cgi_put "</li>"
+ }
+ }
+ cgi_table_data class="wap dv1" {
+ cgi_put [cgi_img "img/cbn/div.gif"]
+ }
+ cgi_table_data class="wap" {
+ cgi_put [cgi_url [cgi_span "class=sp spmbi spmb12" "Cancel"] "$pop" "onClick=return cancelComposition('[url $pop]');" title="Cancel Compose and lose unsaved changes"]
+ }
+ cgi_table_data class="wap" width="100%" {
+ cgi_put [cgi_nbspace]
+ }
+ cgi_table_data class="wap tbPad" align="right" {
+ cgi_put [cgi_nbspace]
+ }
+ }
+ cgi_puts "</tbody>"
+ }
+ } {
+ cgi_form $_wp(appdir)/$_wp(ui2dir)/conduit/post.tcl "enctype=multipart/form-data" id=composeForm target=formResponse {
+ cgi_table width="100%" height="100%" cellpadding="0" cellspacing="0" id=compositionBody {
+ cgi_puts "<tbody>"
+ cgi_table_row {
+ cgi_table_data {
+ cgi_table class="compose" cellpadding="0" cellspacing="0" {
+ cgi_puts "<tbody>"
+ cgi_table_row {
+ cgi_table_data class="spc" colspan="3" {
+ # various bits of what we need to pass on
+ cgi_text "cid=[WPCmd PEInfo key]" type=hidden "notab"
+ cgi_text "sessid=$sessid" type=hidden "notab"
+ cgi_text "postpost=$pop" type=hidden "notab"
+ cgi_text "subtype=plain" type=hidden id=contentSubtype notab
+ cgi_text "priority=0" type=hidden id=priority "notab"
+
+ # TEST stuff we have to feed post.tcl to get it working
+ cgi_text "action=OK" type=hidden "notab"
+ cgi_text "send=1" type=hidden "notab"
+ cgi_text "sendop=send" type=hidden id=sendOp notab
+ cgi_text "autodraftuid=0" type=hidden id=autoDraftUid notab
+
+ if {[string length $repqstr]} {
+ cgi_text "repqstr=$repqstr" type=hidden "notab"
+ }
+
+ if {[info exists flowed]} {
+ cgi_text "form_flowed=yes" type=hidden "notab"
+ }
+
+ foreach field [WPCmd PECompose syshdrs] {
+ set hdr [fieldname [lindex $field 0]]
+ if {[info exists hdrvals($hdr)]} {
+ cgi_text "${hdr}=$hdrvals($hdr)" type=hidden "notab"
+ }
+ }
+ }
+ }
+
+ set extrahdrs {}
+
+ foreach field $headers {
+ set item [lindex $field 0]
+
+ if {[string length $item] == 0} {
+ continue
+ }
+
+ set itemvaldef [lindex $field 1]
+ set litem [string tolower $item]
+ set fn [fieldname $item]
+
+ if {[info exists hdrvals($fn)]} {
+ set itemval $hdrvals($fn)
+ } elseif {[info exists $litem] && [string length [subst $$litem]]} {
+ set itemval [subst $$litem]
+ } elseif {[string length $itemvaldef]} {
+ set itemval $itemvaldef
+ } else {
+ set itemval ""
+ }
+
+ if {[catch {WPCmd PECompose composehdrs} h] == 0 && [llength $h] > 0} {
+ set display_headers [string tolower $h]
+ } else {
+ set display_headers $defaultheaders
+ }
+
+ if {![info exists extrahdrs]} {
+ if {[info exists postoption(fcc-set-by-addrbook)]} {
+ if {[lsearch -exact $display_headers fcc] < 0} {
+ lappend display_headers fcc
+ }
+ }
+ }
+
+ if {[lsearch -exact $display_headers [string tolower $item]] >= 0} {
+ rowfield $item $itemval
+ } elseif {[info exists extrahdrs]} {
+ lappend extrahdrs [list $item $itemval]
+ } else {
+ switch -- [string tolower $item] {
+ fcc {
+ set deffcc [default_fcc $itemval]
+
+ set fccname [lindex $deffcc 0]
+ set fcccol [lindex $deffcc 1]
+
+ cgi_text [fieldname $item]=$fccname type=hidden "notab"
+ cgi_text "colid=$fcccol" type=hidden "notab"
+ }
+ default {
+ cgi_text "[fieldname $item]=$itemval" type=hidden "notab"
+ }
+ }
+ }
+ }
+
+ cgi_puts "</tbody>"
+ }
+
+ if {[info exists extrahdrs]} {
+ cgi_division id="moreComposeHeaders" {
+ cgi_table class="wap compose" cellpadding="0" cellspacing="0" {
+ cgi_puts "<tbody>"
+
+ foreach pb $extrahdrs {
+ rowfield [lindex $pb 0] [lindex $pb 1]
+ }
+
+ cgi_puts "</tbody>"
+ }
+ }
+ }
+
+ cgi_division id="composeAttachments" {
+ cgi_table class="wap compose" cellpadding="0" cellspacing="0" {
+ cgi_puts "<tbody>"
+ cgi_table_row {
+ cgi_table_data class="wap spc" colspan="2" {}
+ }
+ cgi_table_row {
+ cgi_table_data class="wap lbl" {
+ cgi_puts "<label>Attachments: </label>"
+ }
+ cgi_table_data class="wap attach" {
+ cgi_text "attachments=" type=hidden id=attachments notab
+ cgi_division id=attachList {
+ }
+ cgi_division id=fileUpload {
+ }
+ }
+ }
+ cgi_table_row {
+ cgi_table_data class="wap spc" colspan="2" {
+
+ if {[info exists postoption(fcc-without-attachments)]} {
+ if {$postoption(fcc-without-attachments)} {
+ set checked ""
+ } else {
+ set checked checked
+ }
+ } elseif {[WPCmd PEInfo feature "fcc-without-attachments"]} {
+ set checked ""
+ } else {
+ set checked checked
+ }
+
+ cgi_checkbox fccattach=1 $checked id="fcc_attachment"
+ cgi_puts "<label for='fcc_attachment'>Include attachments in copy of message saved to Fcc (i.e. sent-mail)</label>"
+ }
+ }
+ }
+ }
+ cgi_javascript {
+ if {[info exists attachments]} {
+ set comma ""
+ cgi_puts "drawAttachmentList({attachments:\["
+ foreach a $attachments {
+ # {4137457288 bunny.gif 2514 Image/GIF}
+ cgi_puts "${comma}{id:'[lindex $a 0]',fn:'[lindex $a 1]',size:'[lindex $a 2]',type:'[lindex $a 3]'}"
+ set comma ","
+ }
+ cgi_puts "\]});"
+ }
+ }
+ }
+ }
+ cgi_table_row {
+ cgi_table_data height="100%" {
+ cgi_table class="composeBody" cellpadding="0" cellspacing="0" {
+ cgi_puts "<tbody>"
+ cgi_table_row {
+ cgi_table_data class="yui-skin-sam textBody" {
+ cgi_textarea body=$body id=composeText title="Message Body" rows="25"
+ }
+ }
+ cgi_puts "</tbody>"
+ }
+ }
+ }
+ cgi_puts "</tbody>"
+ }
+ }
+ } {
+ cgi_table class="wap toolbarTbl" cellpadding="0" cellspacing="0" {
+ cgi_puts "<tbody>"
+ cgi_table_row {
+ cgi_table_data id=lastAutoDraft class="wap pageText" {
+ }
+ cgi_table_data class=wap width="100%" {
+ cgi_put [cgi_nbspace]
+ }
+ cgi_table_data class="wap tbPad" align="right" {
+ cgi_put [cgi_nbspace]
+ }
+ }
+ cgi_puts "</tbody>"
+ }
+ }
+
+ # autosize and autocompletion
+ cgi_javascript {
+ cgi_puts "var gACDataServer = '$_wp(serverpath)/$_wp(appdir)/$_wp(ui2dir)/conduit/complete.tcl';"
+ cgi_puts {
+ gACDataSource = new YAHOO.widget.DS_XHR(gACDataServer,['Result','Email','Nickname','Fcc']);
+ gACDataSource.responseType = YAHOO.widget.DS_XHR.TYPE_XML;
+ }
+ if {[info exists cuid] || [regexp {view/.*/([0-9]+)$} $pop dummy cuid]} {
+ cgi_puts " gACDataSource.scriptQueryAppend = 'uid=$cuid';"
+ }
+ cgi_puts {
+ var gAutoCompleteTo = new YAHOO.widget.AutoComplete('fieldTo','containerTo',gACDataSource);
+ autoCompleteDefaults(gAutoCompleteTo);
+ var gAutoCompleteCc = new YAHOO.widget.AutoComplete('fieldCc','containerCc',gACDataSource);
+ autoCompleteDefaults(gAutoCompleteCc);
+ var gAutoCompleteBcc = new YAHOO.widget.AutoComplete('fieldBcc','containerBcc',gACDataSource);
+ autoCompleteDefaults(gAutoCompleteBcc);
+ }
+ }
+
+ if {[info exists debugging]} {
+ # any debugging info to insert?
+
+ foreach dmsg $dmsgs {
+ cgi_html_comment "DEBUG: $dmsg"
+ cgi_puts ""
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/2.0/conduit/apply.tcl b/web/cgi/alpine/2.0/conduit/apply.tcl
new file mode 100755
index 00000000..d93ab0d7
--- /dev/null
+++ b/web/cgi/alpine/2.0/conduit/apply.tcl
@@ -0,0 +1,76 @@
+#!./tclsh
+# $Id: apply.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# apply
+#
+# Purpose: CGI script generating response to xmlHttpRequest
+#
+# Input:
+#
+set apply_args {
+ {f {} ""}
+ {s {} ""}
+}
+
+# inherit global config
+source ../alpine.tcl
+
+
+# Import data validate it and get session id
+if {[catch {WPGetInputAndID sessid}]} {
+ return
+}
+
+# grok parameters
+foreach item $apply_args {
+ if {[catch {eval WPImport $item} errstr]} {
+ WPInfoPage "Web Alpine Error" [font size=+2 $errstr] "Please close this window."
+ exit
+ }
+}
+
+switch -- $f {
+ {new} -
+ {imp} -
+ {del} {
+ set flag $f
+ }
+ default {
+ }
+}
+
+switch -- $s {
+ ton -
+ not {
+ set state $s
+ }
+ default {
+ }
+}
+
+cgi_puts "Content-type: text/html; charset=\"UTF-8\""
+cgi_puts ""
+if {[catch {
+ if {[info exists flag] && [info exists state]} {
+ if {[catch {WPCmd PEMailbox apply flag $state $flag} result]} {
+ error "Apply $state ${flag}: $result"
+ } else {
+ set response "$result [WPCmd PEMailbox selected] [WPCmd PEMailbox messagecount]"
+ cgi_puts $response
+ }
+ } else {
+ WPCmd PEInfo statmsg "Unknown flag ($f) or state ($s)"
+ }
+} result]} {
+ cgi_puts "failed: $result"
+}
diff --git a/web/cgi/alpine/2.0/conduit/attach.tcl b/web/cgi/alpine/2.0/conduit/attach.tcl
new file mode 100755
index 00000000..924fd148
--- /dev/null
+++ b/web/cgi/alpine/2.0/conduit/attach.tcl
@@ -0,0 +1,101 @@
+#!./tclsh
+# $Id: attach.tcl 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# attach.tcl
+#
+# Purpose: CGI script to handle attaching attachment
+# to composition via queryattach generated form
+#
+# Input:
+set attach_vars {
+ {file "" ""}
+ {description "" ""}
+ {op "" ""}
+ {id "" 0}
+}
+
+# Output:
+
+# inherit global config
+source ../alpine.tcl
+
+# Import data validate it and get session id
+if {[catch {WPGetInputAndID sessid}]} {
+ return
+}
+
+# grok parameters
+foreach item $attach_vars {
+ if {[catch {eval WPImport $item} errstr]} {
+ lappend errs $errstr
+ }
+}
+
+if {[string length $file] && [string length [lindex $file 1]]} {
+
+ # "file" is a list: local_file remote_file content-type/content-subtype
+ # trim path from file name on remote system
+ # since we can't be certain what the delimiter is,
+ # try the usual suspects
+ set delims [list "\\" "/" ":"]
+ set native [lindex $file 1]
+ if {[string length $native]} {
+ foreach delim $delims {
+ if {[set crop [string last $delim $native]] >= 0} {
+ set native [string range $native [expr {$crop + 1}] [string length $native]]
+ break;
+ }
+ }
+
+ regsub -all "'" $native "\\'" jsnative
+
+ if {0 == [string length [lindex $file 2]]} {
+ set conttype [list text plain]
+ } else {
+ set conttype [split [lindex $file 2] "/"]
+ }
+
+ set id [WPCmd PECompose attach [lindex $file 0] [lindex $conttype 0] [lindex $conttype 1] $native $description]
+ } else {
+ lappend errs "Requested attachment does not exist"
+ }
+} elseif {![string compare delete $op]} {
+ if {[catch {WPCmd PECompose unattach $id} result]} {
+ lappend errs $result
+ }
+}
+
+
+
+# return attachment list
+puts stdout "Content-type: text/html;\n\n<html><head><script>window.parent.drawAttachmentList(\{"
+if {0 == [catch {WPCmd PECompose attachments} attachments]} {
+ puts stdout "attachments:\["
+ set comma ""
+ foreach a $attachments {
+ # {4137457288 bunny.gif 2514 Image/GIF}
+ puts stdout "${comma}\{id:\"[lindex $a 0]\",fn:\"[lindex $a 1]\",size:\"[lindex $a 2]\",type:\"[lindex $a 3]\"\}"
+ set comma ","
+ }
+ puts stdout "\]"
+ set comma ","
+} else {
+ set comma ""
+ lappend errs $attachments
+}
+
+if {[info exists errs]} {
+ puts stdout "${comma}error:\"[join $errs {, }]\""
+}
+
+puts stdout "\});</script></head><body></body></html>"
diff --git a/web/cgi/alpine/2.0/conduit/cert.tcl b/web/cgi/alpine/2.0/conduit/cert.tcl
new file mode 100755
index 00000000..a3a4a28c
--- /dev/null
+++ b/web/cgi/alpine/2.0/conduit/cert.tcl
@@ -0,0 +1,48 @@
+#!./tclsh
+# $Id: cert.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# cert.tcl
+#
+# Purpose: CGI script generating response to xmlHttpRequest
+#
+# Input:
+#
+set cert_args {
+ {server {} ""}
+ {accept {} "no"}
+}
+
+# inherit global config
+source ../alpine.tcl
+
+
+# Import data validate it and get session id
+if {[catch {WPGetInputAndID sessid}]} {
+ return
+}
+
+# grok parameters
+foreach item $cert_args {
+ if {[catch {eval WPImport $item} errstr]} {
+ WPInfoPage "Web Alpine Error" [font size=+2 $errstr] "Please close this window."
+ return
+ }
+}
+
+cgi_puts "Content-type: text/html; charset=\"UTF-8\"\n"
+set answer "Server certificate declined"
+if {[string compare $accept yes] || [catch {WPCmd PESession acceptcert $server} answer]} {
+ cgi_puts "PROBLEM: $answer"
+} else {
+ cgi_puts "$answer"
+}
diff --git a/web/cgi/alpine/2.0/conduit/complete.tcl b/web/cgi/alpine/2.0/conduit/complete.tcl
new file mode 100755
index 00000000..1fd009a0
--- /dev/null
+++ b/web/cgi/alpine/2.0/conduit/complete.tcl
@@ -0,0 +1,54 @@
+#!./tclsh
+# $Id: complete.tcl 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# complete
+#
+# Purpose: CGI script to supply data to YUI AutoComplete
+#
+# Input:
+# along with possible search parameters:
+set complete_args {
+ {param {} ""}
+ {query {} ""}
+ {uid {} 0}
+}
+
+# inherit global config
+source ../alpine.tcl
+
+# Import data validate it and get session id
+if {[catch {WPGetInputAndID sessid}]} {
+ return
+}
+
+# grok parameters
+foreach item $complete_args {
+ if {[catch {eval WPImport $item} errstr]} {
+ error $errstr
+ }
+}
+
+if {[catch {WPCmd PEAddress complete $query $uid} result]} {
+ error "complete: $result"
+}
+
+puts stdout "Content-type: text/xml; charset=\"UTF-8\""
+puts stdout ""
+puts stdout {<?xml version="1.0" encoding="UTF-8"?>}
+puts stdout "<ResultSet totalResultsAvailable=\"[llength $result]\">"
+if {[string length $query]} {
+ foreach abe $result {
+ puts stdout "<Result><Nickname>[cgi_quote_html [lindex $abe 0]]</Nickname><Email>[cgi_quote_html [lindex $abe 1]]</Email><Fcc>[cgi_quote_html [lindex $abe 2]]</Fcc></Result>"
+ }
+}
+puts stdout {</ResultSet>}
diff --git a/web/cgi/alpine/2.0/conduit/contactlist.tcl b/web/cgi/alpine/2.0/conduit/contactlist.tcl
new file mode 100755
index 00000000..efac458b
--- /dev/null
+++ b/web/cgi/alpine/2.0/conduit/contactlist.tcl
@@ -0,0 +1,306 @@
+#!./tclsh
+# $Id: contactlist.tcl 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# contactlist.tcl
+#
+# Purpose: CGI script that generates a page displaying a
+# list of contacts in the requested address book
+#
+# Input: PATH_INFO: /booknumber
+# along with possible search parameters:
+set contactlist_args {
+ {op {} "noop"}
+ {book "" 0}
+ {ai "" -1}
+ {entryList "" ""}
+ {contactNick "" ""}
+ {origNick "" ""}
+ {contactName "" ""}
+ {contactEmail "" ""}
+ {contactFcc "" ""}
+ {contactNotes "" ""}
+ {hdr {} "off"}
+ {sendto {} "off"}
+ {canedit {} "off"}
+}
+
+# inherit global config
+source ../alpine.tcl
+source ../common.tcl
+
+# TEST
+proc cgi_suffix {args} {
+ return ""
+}
+
+proc deleteByBook {_delbooks} {
+ upvar 1 $_delbooks delbooks
+ foreach dbn [array names delbooks] {
+ foreach dbi [lsort -integer -decreasing $delbooks($dbn)] {
+ if {[catch {WPCmd PEAddress delete $dbn "" $dbi} result]} {
+ error "Address Delete Failure: $result"
+ }
+ }
+ }
+}
+
+proc newContactJSObject {} {
+ uplevel 1 {
+ set json {}
+
+ foreach k {hdr sendto canedit} {
+ set v [subst $$k]
+
+ if {[string length $json]} {
+ append json ","
+ }
+
+ if {[string length $v]} {
+ append json "${k}:'${v}'"
+ }
+ }
+
+ return "\{${json}\}"
+ }
+}
+
+if {[info exists env(PATH_INFO)] && [string length $env(PATH_INFO)]} {
+ if {[regexp {^/([0-9]+|-1)$} $env(PATH_INFO) dummy abook]} {
+ # Import data validate it and get session id
+ if {[catch {WPGetInputAndID sessid}]} {
+ set harderr "No Session ID: $sessid"
+ } else {
+ # grok parameters
+ foreach item $contactlist_args {
+ if {[catch {eval WPImport $item} importerr]} {
+ set harderr "Cannot init session: $importerr"
+ break
+ }
+ }
+ }
+ } else {
+ set harderr "Bad Address Book Request: $env(PATH_INFO)"
+ }
+} else {
+ set harderr "No Address Book Specified"
+}
+
+puts stdout "Content-type: text/html; charset=\"UTF-8\"\n"
+
+if {[info exists harderr]} {
+ puts stdout "<b>ERROR: $harderr</b>"
+ exit
+}
+
+switch -- $op {
+ delete {
+ set result ""
+ # grok delete format: \[(book)index\],
+ if {[regexp {^[0-9\.,]+$} $entryList]} {
+ # collect by book
+ set entryList [split $entryList ","]
+ foreach e $entryList {
+ if {[regexp {^([0-9]+)\.([0-9]+)} $e dummy eb ei]} {
+ lappend delbooks($eb) $ei
+ }
+ }
+
+ # delete by book in DECREASING index order
+ if {[catch {deleteByBook delbooks} result] || [string length $result]} {
+ WPCmd PEInfo statmsg "$result"
+ } else {
+ if {[llength $entryList] > 1} {
+ WPCmd PEInfo statmsg "Contacts Deleted"
+ } else {
+ WPCmd PEInfo statmsg "Contact Deleted"
+ }
+ }
+ } else {
+ WPCmd PEInfo statmsg "Invalid entry list format: >>>>$entryList<<<<"
+ }
+ }
+ add {
+ if {[catch {WPCmd PEAddress edit $book $contactNick $ai $contactName $contactEmail $contactFcc $contactNotes 1} result]} {
+ WPCmd PEInfo statmsg "Add failed: $result"
+ }
+ }
+ change {
+ if {[catch {WPCmd PEAddress edit $book $contactNick $ai $contactName $contactEmail $contactFcc $contactNotes 0 $origNick} result]} {
+ WPCmd PEInfo statmsg "Change failed: $result"
+ }
+ }
+ noop {
+ }
+ default {
+ WPCmd PEInfo statmsg "Unrecognized option: $op"
+ }
+}
+
+# remainder is director to list
+if {[catch {WPCmd PEAddress books} booklist]} {
+ WPCmd PEInfo statmsg "Cannot get list of Addressbooks"
+} else {
+ set books [llength $booklist]
+ if {$abook < 0} {
+ cgi_division class="clistContext" {
+ cgi_put "[cgi_span "class=sp spfcl spfcl1" [cgi_span "style=display:none;" "Contacts: "]][cgi_span "Contact Lists"]"
+ }
+ cgi_division class=clistContacts id=clistContacts {
+ cgi_table cellspacing="0" cellpadding="0" class="clt" {
+ for {set i 0} {$i < $books} {incr i} {
+ set thisbook [lindex $booklist $i]
+ cgi_table_row {
+ cgi_table_data class="cli" {
+ cgi_puts [cgi_url [cgi_span "class=sp spfcl spfcl1" "style=border-bottom: 1px solid #003399;" [cgi_span "style=display:none;" [cgi_gt]]] "contacts/${i}/" title="View Contacts in [lindex $thisbook 1]" "onClick=newContactList(YAHOO.alpine.containers.contactlist,null,[lindex $thisbook 0],[newContactJSObject]); return false;"]
+ }
+ cgi_table_data class="cln" {
+ cgi_put [lindex $thisbook 1]
+ }
+ }
+ }
+ }
+ }
+ } else {
+ for {set i 0} {$i < $books} {incr i} {
+ if {$i == $abook} {
+ set thisbook [lindex $booklist $i]
+ if {[catch {WPCmd PEAddress format [lindex $thisbook 0]} format]} {
+ WPCmd PEInfo statmsg "Cannot get Contacts format: $format"
+ }
+
+ if {[catch {WPCmd PEAddress list [lindex $thisbook 0]} clist]} {
+ WPCmd PEInfo statmsg "Cannot get Contacts list: $clist"
+ }
+
+ break
+ }
+ }
+
+ if {[info exists thisbook]} {
+ cgi_division class="clistContext" {
+ if {$books == 1} {
+ cgi_put "[cgi_span "class=sp spfcl spfcl1" [cgi_span "style=display:none;" "Contacts: "]][cgi_span "Contact List"]"
+ } else {
+ cgi_put "[cgi_span "class=sp spfcl spfcl1" "style=border-bottom: 1px solid #003399;" "title=\"Show All Contact Lists\"" "onClick=\"newContactList(YAHOO.alpine.containers.contactlist,null,-1,[newContactJSObject]);\"" [cgi_span "style=display:none;" "Contacts: "]]"
+ cgi_put [cgi_span id=clistName [lindex $thisbook 1]]
+ }
+ }
+
+ cgi_division class=clistContacts id=clistContacts {
+ cgi_table cellspacing="0" cellpadding="0" "class=\"listTbl divider\"" {
+ cgi_puts "<tbody>"
+ if {![string compare $hdr on]} {
+ cgi_table_row "class=\"contactHeader\"" {
+ cgi_table_head "class=\"wap colHdr\"" {}
+ foreach field $format {
+ cgi_table_head "class=\"wap colHdr lt\"" {
+ switch -- [lindex $field 0] {
+ nick {
+ cgi_puts "Nickname"
+ }
+ full {
+ cgi_puts "Display Name"
+ if {0 == [string compare $canedit on]} {
+ cgi_puts " (click to edit)"
+ }
+ }
+ addr {
+ cgi_put "Email"
+ if {0 == [string compare $sendto on]} {
+ cgi_puts " (click email to send)"
+ }
+ }
+ default {
+ cgi_puts [index $field 0]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ set aindex 0
+ foreach contact $clist {
+ cgi_table_row "class=\"clr\"" {
+ set nfields [llength $format]
+
+ cgi_table_data class=wap {
+ set label "ab${abook}.${aindex}"
+ cgi_checkbox "nickList=$abook.$aindex.[lindex [lindex $contact 0] 0]" id=$label "onclick=\"return boxChecked(this);\""
+ }
+
+ for {set i 0} {$i < $nfields} {incr i} {
+ set field [lindex $format $i]
+ set data [lindex $contact [expr $i + 1]]
+
+ cgi_table_data "class=\"wap clcd\"" {
+ switch -- [lindex $field 0] {
+ addr {
+ switch -- [lindex [lindex $contact 0] 1] {
+ single {
+ set addr [cgi_quote_html $data]
+ if {0 == [string compare $sendto on]} {
+ cgi_puts [cgi_url $addr compose?contacts=${abook}.${aindex} class=wap]
+ } else {
+ cgi_puts "<label for=$label>$addr</label>"
+ }
+ }
+ list {
+ foreach addr $data {
+ set addr [cgi_quote_html $addr]
+ if {0 == [string compare $sendto on]} {
+ cgi_puts "[cgi_url $addr compose?contacts=${abook}.${aindex} class=wap][cgi_nl]"
+ }
+ }
+ }
+ default {
+ cgi_puts "Unknown contact type"
+ }
+ }
+ }
+ full {
+ if {[string length $data]} {
+ if {0 == [string compare $canedit on]} {
+ cgi_puts [cgi_url [cgi_quote_html $data] \# "onClick=return editContact({book:${abook},index:${aindex}});" class=wap]
+ } else {
+ cgi_puts "<label for=$label>[cgi_quote_html $data]</label>"
+ }
+ } else {
+ cgi_puts [cgi_nbspace]
+ }
+ }
+ default {
+ if {[string length $data]} {
+ cgi_puts "<label for=$label>[cgi_quote_html $data]</label>"
+ } else {
+ cgi_puts [cgi_nbspace]
+ }
+ }
+ }
+ }
+ }
+ }
+ incr aindex
+ }
+ cgi_puts "</tbody>"
+ }
+ }
+ } else {
+ WPCmd PEInfo statmsg "Unknown Address Book"
+ }
+ }
+}
+cgi_puts "<script>"
+wpStatusAndNewmailJavascript
+cgi_puts "if(window.updateContactCount) updateContactCount('$books','[llength $clist]');"
+cgi_puts "</script>"
diff --git a/web/cgi/alpine/2.0/conduit/empty.tcl b/web/cgi/alpine/2.0/conduit/empty.tcl
new file mode 100755
index 00000000..f30864fe
--- /dev/null
+++ b/web/cgi/alpine/2.0/conduit/empty.tcl
@@ -0,0 +1,73 @@
+#!./tclsh
+# $Id: empty.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# empty.tcl
+#
+# Purpose: CGI script generating response to xmlHttpRequest
+#
+# Input:
+#
+set empty_args {
+ {c "Unspecified Collection"}
+ {f "Unspecified Folder"}
+ {u "" 0}
+}
+
+# inherit global config
+source ../alpine.tcl
+source ../common.tcl
+
+# Import data validate it and get session id
+if {[catch {WPGetInputAndID sessid}]} {
+ return
+}
+
+# grok parameters
+foreach item $empty_args {
+ if {[catch {eval WPImport $item} errstr]} {
+ WPInfoPage "Web Alpine Error" [font size=+2 $errstr] "Please close this window."
+ exit
+ }
+}
+
+
+cgi_puts "Content-type: text/html; charset=\"UTF-8\""
+cgi_puts ""
+
+# ONLY ever empty Junk, Trash or Drafts
+set defc [WPCmd PEFolder defaultcollection]
+
+set tf [lindex [WPCmd PEConfig varget trash-folder] 0]
+set df [lindex [WPCmd PEConfig varget postponed-folder] 0]
+set f [wpLiteralFolder $c $f]
+if {$c == $defc
+ && (([info exists _wp(spamfolder)] && 0 == [string compare $f $_wp(spamfolder)])
+ || ([string length $tf] && 0 == [string compare $f $tf])
+ || ([string length $tf] && 0 == [string compare $f $df]))} {
+ if {[catch {
+ switch -regexp $u {
+ ^([0-9]+)$ -
+ ^selected$ -
+ ^all$ {
+ cgi_puts [WPCmd PEFolder empty $c $f $u]
+ }
+ default {
+ error "Unknown option"
+ }
+ }
+ } result]} {
+ cgi_puts "$result"
+ }
+} else {
+ cgi_puts "Empty $f NOT permitted"
+}
diff --git a/web/cgi/alpine/2.0/conduit/exists.tcl b/web/cgi/alpine/2.0/conduit/exists.tcl
new file mode 100755
index 00000000..9f5c2168
--- /dev/null
+++ b/web/cgi/alpine/2.0/conduit/exists.tcl
@@ -0,0 +1,65 @@
+#!./tclsh
+# $Id: exists.tcl 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# exists
+#
+# Purpose: CGI script generating response to xmlHttpRequest
+#
+# Input:
+#
+set exists_args {
+ {c {} ""}
+ {f {} ""}
+ {create {} "no"}
+}
+
+# inherit global config
+source ../alpine.tcl
+
+puts stdout "Content-type: application/json\n"
+
+if {[catch {WPGetInputAndID sessid}]} {
+ set harderr "No Session ID: $sessid"
+} else {
+ # grok parameters
+ foreach item $exists_args {
+ if {[catch {eval WPImport $item} importerr]} {
+ set harderr "Cannot init session: $importerr"
+ break
+ }
+ }
+}
+
+if {[info exists harderr]} {
+ puts -nonewline stdout "{error: '$harderr'}"
+} elseif {![regexp {^([0-9]+)$} $c]} {
+ puts -nonewline stdout "{error: 'Missing collection ID: $c'}"
+} elseif {[string length $f] <= 0} {
+ puts -nonewline stdout "{error: 'Missing folder name'}"
+} elseif {0 == [catch {WPCmd PEFolder exists $c $f} result]} {
+ if {1 == $result} {
+ puts -nonewline stdout "{exists:1}"
+ } else {
+ if {0 == [string compare $create yes]} {
+ if {0 == [catch {WPCmd PEFolder create $c $f} result]} {
+ puts -nonewline stdout "{exists:1}"
+ } else {
+ puts -nonewline stdout "{error: '$result'}"
+ }
+ } else {
+ puts -nonewline stdout "{exists:0}"
+ }
+ }
+} else {
+ puts -nonewline stdout "{error: '$result'}"
+}
diff --git a/web/cgi/alpine/2.0/conduit/expand.tcl b/web/cgi/alpine/2.0/conduit/expand.tcl
new file mode 100755
index 00000000..4599a984
--- /dev/null
+++ b/web/cgi/alpine/2.0/conduit/expand.tcl
@@ -0,0 +1,84 @@
+#!./tclsh
+# $Id: expand.tcl 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# expand.tcl
+#
+# Purpose: CGI script to supply data to composer
+#
+# Input:
+# along with possible search parameters:
+set expand_args {
+ {book {} -1}
+ {index {} -1}
+ {addrs {} ""}
+}
+
+# inherit global config
+source ../alpine.tcl
+
+# Import data validate it and get session id
+if {[catch {WPGetInputAndID sessid} result]} {
+ error "expand.tcl: $result"
+}
+
+# grok parameters
+foreach item $expand_args {
+ if {[catch {eval WPImport $item} result]} {
+ error "expand.tcl: $result"
+ }
+}
+
+catch (unset errstr}
+set errlist {}
+set expaddr {}
+
+if {[regexp {^[0-9]*$} $book] && [regexp {^[0-9,]*$} $index]} {
+ foreach i [split $index ","] {
+ if {[catch {WPCmd PEAddress entry $book "" $i} entry]} {
+ lappend errlist $entry
+ } else {
+ regsub -all "'" [lindex $entry 0] "\\'" resaddr
+ lappend expaddr $resaddr
+ set fcc ""
+ }
+ }
+} elseif {[string length $addrs]} {
+ if {[catch {WPCmd PEAddress expand $addrs [lindex [WPCmd PECompose fccdefault] 1]} result]} {
+ set errstr $result
+ } else {
+ set expaddr [list [lindex $result 0]]
+ set fcc [lindex $result 2]
+ }
+} else {
+ set errstr "Unknown expand options"
+}
+
+puts stdout "Content-type: text/xml; charset=\"UTF-8\"\n"
+puts stdout {<?xml version="1.0" encoding="UTF-8"?>}
+
+if {[info exists errstr] && [string length $errstr]} {
+ puts stdout "<ResultSet totalResultsAvailable=\"1\"><Result>"
+ puts stdout "<Error>$errstr</Error>"
+ puts stdout "</Result></ResultSet>"
+} else {
+ puts stdout "<ResultSet totalResultsAvailable=\"[expr {[llength $expaddr] + [llength $errlist]}]\">"
+ foreach e $errlist {
+ puts stdout "<Result><Error>[cgi_quote_html $e]</Error></Result>"
+ }
+
+ foreach a $expaddr {
+ puts stdout "<Result><Address>[cgi_quote_html $a]</Address><Fcc>[cgi_quote_html $fcc]</Fcc></Result>"
+ }
+
+ puts stdout "</ResultSet>"
+}
diff --git a/web/cgi/alpine/2.0/conduit/export b/web/cgi/alpine/2.0/conduit/export
new file mode 100755
index 00000000..42a9f82b
--- /dev/null
+++ b/web/cgi/alpine/2.0/conduit/export
@@ -0,0 +1,168 @@
+#!./tclsh
+# $Id: export 391 2007-01-25 03:53:59Z mikes@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# export
+#
+# Purpose: CGI script to download exported folder
+#
+# Input:
+set export_vars {
+}
+
+#set export_via_ip_address 1
+#set export_via_local_hostname 1
+
+# inherit global config
+source ../alpine.tcl
+
+set mailextension ".mbx"
+
+proc WPServerIP {} {
+ global _wp
+
+ catch {
+ set ip 127.0.0.1
+ set sid [socket -async [info hostname] [expr {([string length $_wp(serverport)]) ? $_wp(serverport) : 80}]]
+ set ip [lindex [ fconfigure $sid -sockname ] 0]
+ close $sid
+ }
+
+ return $ip
+}
+
+
+WPEval $export_vars {
+ # grok PATH_INFO for collection 'c' and folder 'f' uid 'u' and part 'p'
+ if {!([info exists env(PATH_INFO)] && [string length $env(PATH_INFO)]
+ && [regexp {^/([0-9]+)/(.*)$} $env(PATH_INFO) dummy c f])} {
+ WPCmd PEInfo statmsg "Invalid Detach: $env(SCRIPT_NAME)"
+ cgi_exit
+ }
+
+ # generate filenames to hold exported folder and control file
+ for {set n 0} {1} {incr n} {
+
+ set rhandle [WPCmd PESession random 64]
+ set cfile [file join $_wp(fileroot) $_wp(detachpath) detach.${rhandle}-control]
+ set dfile [file join $_wp(fileroot) $_wp(detachpath) detach.${rhandle}-data]
+
+ if {[file exists $cfile] == 0 && [file exists $dfile] == 0} {
+ if {[catch {open $cfile {RDWR CREAT EXCL} [cgi_tmpfile_permissions]} cfd]
+ || [catch {open $dfile {RDWR CREAT EXCL} [cgi_tmpfile_permissions]} dfd]} {
+ if {[info exists dfd]} {
+ catch {close $cfd}
+ catch {file delete -force $cfile}
+ set errstr $dfd
+ } else {
+ set errstr $cfd
+ }
+
+ error [list _action Export "Cannot create command/control files: [cgi_quote_html $errstr]" "Please close this window"]
+ } else {
+ close $dfd
+ break
+ }
+ } elseif {$n > 4} {
+ error [list _action Export "Command file creation limit" "Please close this window"]
+ }
+ }
+
+ catch {file delete $dfile}
+
+ if {[catch {WPCmd PEFolder export $c $f $dfile} result]} {
+ WPCmd PEInfo statmsg $result
+ } else {
+ if {[set dfilesize [file size $dfile]] > 0
+ && ([info exists _wp(uplim_bytes)] && $_wp(uplim_bytes) > 0)
+ && $dfilesize > $_wp(uplim_bytes)} {
+ if {$_wp(uplim_bytes) > (1000000)} {
+ set dfs [format {%s.%.2s MB} [WPcomma [expr {$dfilesize / 1000000}]] [expr {$dfilesize % 1000000}]]
+ set esl [format {%s.%.2s MB} [WPcomma [expr {$_wp(uplim_bytes) / 1000000}]] [expr {$_wp(uplim_bytes) % 1000000}]]
+ } else {
+ set dfs "[WPcomma $dfs] KB"
+ set esl "[WPcomma $_wp(uplim_bytes)] KB"
+ }
+
+ WPCmd PEInfo statmsg "Exported folder size ($dfs) exceeds the maximum ($esl) size that can be imported.<br>If you wish to import this folder back into Web Alpine at a later time,<br>you should break it up into smaller folders"
+ }
+
+ if {[info exists export_via_ip_address]} {
+ if {[regsub {^(http[s]?://)[A-Za-z0-9\\-\\.]+(.*)$} "[cgi_root]/pub/getach.tcl" "\\1\[[WPServerIP]\]\\2" redirect] != 1} {
+ WPCmd PEInfo statmsg "Cannot determine server address"
+ catch {unset redirect}
+ }
+ } elseif {[info exists export_via_local_hostname]} {
+ if {[regsub {^(http[s]?://)[A-Za-z0-9\\-\\.]+(.*)$} "[cgi_root]/pub/getach.tcl" "\\1\[[info hostname]\]\\2" redirect] != 1} {
+ WPCmd PEInfo statmsg "Cannot determine server address"
+ catch {unset redirect}
+ }
+ } else {
+ set redirect "[cgi_root]/pub/getach.tcl"
+ }
+
+ set givenname "[file tail $f]${mailextension}"
+ set safegivenname $givenname
+ regsub -all {[/]} $safegivenname {-} safegivenname
+ regsub -all {[ ]} $safegivenname {_} safegivenname
+ regsub -all {[\?]} $safegivenname {X} safegivenname
+ regsub -all {[&]} $safegivenname {X} safegivenname
+ regsub -all {[#]} $safegivenname {X} safegivenname
+ regsub -all {[=]} $safegivenname {X} safegivenname
+ set safegivenname "/$safegivenname"
+
+ puts $cfd "Content-type: Application/X-Mail-Folder"
+ puts $cfd "Content-Disposition: attachment; filename=\"$givenname\""
+
+ # side-step the cgi_xxx stuff in this special case because
+ # we don't want to buffer up the downloading attachment...
+
+ puts $cfd "Content-Length: $dfilesize"
+ puts $cfd "Expires: [clock format [expr {[clock seconds] + 3600}] -f {%a, %d %b %Y %H:%M:%S GMT} -gmt true]"
+ puts $cfd "Cache-Control: max-age=3600"
+ puts $cfd ""
+
+ puts $cfd $dfile
+
+ # exec chmod [cgi_tmpfile_permissions] $dfile
+
+ close $cfd
+
+ exec /bin/chmod [cgi_tmpfile_permissions] $cfile
+ exec /bin/chmod [cgi_tmpfile_permissions] $dfile
+ }
+
+ # prepare to clean up if the brower never redirects
+ if {[info exists redirect]} {
+ set redirect "${redirect}${safegivenname}?h=${rhandle}"
+ } else {
+ set redirect "[cgi_root]/$_wp(appdir)/$_wp(ui2dir)/folders/"
+ }
+
+ cgi_http_head {
+ # redirect to the place we stuffed the export info. use the ip address
+ # to foil spilling any session cookies or the like
+
+ if {[info exists env(SERVER_PROTOCOL)] && [regexp {[Hh][Tt][Tt][PP]/([0-9]+)\.([0-9]+)} $env(SERVER_PROTOCOL) m vmaj vmin] && $vmaj >= 1 && $vmin >= 1} {
+ cgi_puts "Status: 303 Temporary Redirect"
+ } else {
+ cgi_puts "Status: 302 Redirected"
+ }
+
+ cgi_puts "URI: $redirect"
+ cgi_puts "Location: $redirect"
+ }
+
+ cgi_body {}
+
+ exec echo $rhandle | [file join $_wp(cgipath) [WPCmd PEInfo set wp_ver_dir] whackatch.tcl] >& /dev/null &
+}
diff --git a/web/cgi/alpine/2.0/conduit/flag.tcl b/web/cgi/alpine/2.0/conduit/flag.tcl
new file mode 100755
index 00000000..63018125
--- /dev/null
+++ b/web/cgi/alpine/2.0/conduit/flag.tcl
@@ -0,0 +1,77 @@
+#!./tclsh
+# $Id: flag.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# flag
+#
+# Purpose: CGI script generating response to xmlHttpRequest
+#
+# Input:
+#
+set flag_args {
+ {u {} ""}
+ {f {} ""}
+ {s {} ""}
+}
+
+# inherit global config
+source ../alpine.tcl
+
+WPEval $flag_args {
+ cgi_body {
+ switch -- $f {
+ {new} -
+ {imp} {
+ set flag $f
+ }
+ default {
+ }
+ }
+
+ switch -- $s {
+ ton -
+ not {
+ set state $s
+ }
+ default {
+ }
+ }
+
+ if {[info exists flag] && [info exists state]} {
+ regsub -all {,} $u { } u
+ if {[regexp {^[ 0123456789]*$} $u]} {
+ switch $state {
+ ton { set state 1 }
+ not { set state 0 }
+ }
+ switch $flag {
+ imp { set flag important }
+ }
+
+ foreach eu $u {
+ if {[catch {WPCmd PEMessage $eu flag $flag $state} result]} {
+ set result "FALURE: setting $eu to $setting : $result"
+ break
+ }
+ }
+ } elseif {0 == [string compare $u all]} {
+ if {[catch {WPCmd PEMailbox flag $state $flag} result]} {
+ set result "FAILURE: flag $state $flag : $result"
+ }
+ }
+ } else {
+ set result "FAILURE: unkown flag ($f) or state ($s)"
+ }
+
+ cgi_puts $result
+ }
+}
diff --git a/web/cgi/alpine/2.0/conduit/folderlist.tcl b/web/cgi/alpine/2.0/conduit/folderlist.tcl
new file mode 100755
index 00000000..c8e73d2d
--- /dev/null
+++ b/web/cgi/alpine/2.0/conduit/folderlist.tcl
@@ -0,0 +1,255 @@
+#!./tclsh
+# $Id: folderlist.tcl 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# folderlist.tcl
+#
+# Purpose: CGI script that generates a page snippet that displays
+# the XHR requested folder list
+#
+# Input: PATH_INFO: [/<col_number>]/<directory_path>
+# along with possible search parameters:
+set folderlist_args {
+ {op {} "noop"}
+}
+
+# inherit global config
+source ../alpine.tcl
+source ../foldercache.tcl
+source ../common.tcl
+
+
+# default folderlist.tcl state
+set c 0
+
+# TEST
+proc cgi_suffix {args} {
+ return ""
+}
+
+if {[info exists env(PATH_INFO)] && [string length $env(PATH_INFO)]} {
+ if {0 == [string compare $env(PATH_INFO) "/"] || 0 == [string compare $env(PATH_INFO) "//"]} {
+ set c -1
+ set dir ""
+ }
+
+ if {$c < 0 || [regexp {^/([0-9]+)/(([^/]*/)*)$} $env(PATH_INFO) dummy c dir]} {
+ # Import data validate it and get session id
+ if {[catch {WPGetInputAndID sessid} result]} {
+ set harderr "Cannot init session: $result"
+ } else {
+ # grok parameters
+ foreach item $folderlist_args {
+ if {[catch {eval WPImport $item} result]} {
+ set harderr "Cannot read input: $result"
+ break
+ }
+ }
+ }
+ } else {
+ set harderr "BOTCH: Invalid Request: $env(PATH_INFO)"
+ }
+} else {
+ set harderr "BOTCH: No Folder Specified"
+}
+cgi_puts "Content-type: text/html; charset=\"UTF-8\"\n"
+
+if {[info exists harderr]} {
+ cgi_puts "<b>ERROR: $harderr</b>"
+ exit
+}
+
+set cs [WPCmd PEFolder collections]
+
+if {$c < 0} {
+ cgi_division class="flistContext" {
+ cgi_put "[cgi_span "class=sp spfcl spfcl2" [cgi_span "style=display:none;" "Folders: "]][cgi_span "Folder Collections"]"
+ set folderpath ""
+ }
+
+ cgi_division class=flistFolders {
+ cgi_table cellspacing="0" cellpadding="0" class="flt" {
+ foreach col $cs {
+ set ci [lindex $col 0]
+ set onclick "onClick=return newFolderList(this.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode,null,$ci);"
+ cgi_table_row {
+ cgi_table_data class="fli" {
+ cgi_puts [cgi_url [cgi_span "class=sp spfl spfl1" [cgi_span "style=display:none;" [cgi_gt]]] "list/${ci}/" title="View folders in collection." $onclick]
+ }
+ cgi_table_data class="fln" {
+ cgi_puts "[lindex $col 1]"
+ }
+ }
+ cgi_table_row {
+ cgi_table_data colspan=2 class="flcd" {
+ cgi_puts "[cgi_span class=flcd [lindex $col 2]]"
+ }
+ }
+ }
+ }
+ }
+ cgi_puts "<script>"
+ cgi_puts "updateElementValue('pickFolderCollection', '-1');"
+ cgi_puts "updateElementValue('pickFolderPath', '/');"
+ wpStatusAndNewmailJavascript
+ cgi_puts "setPanelBodyWidth('flistFolders');"
+ cgi_puts "</script>"
+} else {
+ set delim [WPCmd PEFolder delimiter $c]
+
+ switch -- $op {
+ delete {
+ if {0 == [catch {cgi_import_as f delf}]} {
+ if {0 == [string length $dir] && 0 == [string compare -nocase inbox $delf]} {
+ WPCmd PEInfo statmsg "Cannot delete INBOX"
+ } else {
+ regsub -all "${delim}" $dir { } deld
+ if {[catch [concat WPCmd PEFolder delete $c $deld \"$delf\"] result]} {
+ WPCmd PEInfo statmsg "Cannot delete $result"
+ } else {
+ WPCmd PEInfo statmsg "Folder \"$delf\" permanently deleted"
+ removeFolderCache $c [join [list $dir $delf] $delim]
+ }
+ }
+ } else {
+ WPCmd PEInfo statmsg "Delete which folder???"
+ }
+ }
+ add {
+ if {0 == [catch {cgi_import_as f addf}]} {
+ if {0 == [string length $dir] && 0 == [string compare -nocase inbox $delf]} {
+ WPCmd PEInfo statmsg "Cannot add INBOX"
+ } else {
+ regsub -all "${delim}" $dir { } addd
+ if {[catch [concat WPCmd PEFolder create $c $addd \"$addf\"] result]} {
+ WPCmd PEInfo statmsg "Folder Cannot add: $result"
+ } else {
+ WPCmd PEInfo statmsg "Folder \"$addf\" added"
+ }
+ }
+ } else {
+ WPCmd PEInfo statmsg "No foldername provided"
+ }
+ }
+ rename {
+ if {0 == [catch {cgi_import sf}] && 0 == [catch {cgi_import df}]} {
+ if {[string compare -nocase inbox $sf] && [string compare -nocase inbox $df]} {
+ if {[catch {WPCmd PEFolder rename $c [join [list $dir $sf] $delim] [join [list $dir $df] $delim]} result]} {
+ WPCmd PEInfo statmsg "Cannot rename: $result"
+ } else {
+ WPCmd PEInfo statmsg "Renamed \"$sf\" to \"$df\""
+ }
+ } else {
+ WPCmd PEInfo statmsg "Cannot rename INBOX"
+ }
+ } else {
+ WPCmd PEInfo statmsg "Foldernames not provided"
+ }
+ }
+ noop {
+ }
+ default {
+ WPCmd PEInfo statmsg "Unrecognized option: $op"
+ }
+ }
+
+ if {0 == [string length $dir] || 0 == [string compare $dir {/}]} {
+ if {[catch {WPCmd PEFolder list $c} flist]} {
+ set authlist [wpHandleAuthException $flist [list [lindex [lindex $cs $c] 0] "list folders in collection [lindex [lindex $cs $c] 1]"] INBOX]
+ if {0 == [llength $authlist]} {
+ WPCmd PEInfo statmsg $flist
+ }
+ }
+ } else {
+ if {[catch {WPCmd PEFolder list $c $dir} flist]} {
+ set col [lindex [lindex $cs $c] 1]
+
+ set authlist [wpHandleAuthException $flist [list [lindex [lindex $cs $c] 0] "list folders in collection [lindex [lindex $cs $c] 1]"] INBOX]
+ if {0 == [llength $authlist]} {
+ WPCmd PEInfo statmsg $flist
+ }
+ }
+ }
+
+ cgi_division class="flistContext" {
+
+ if {[llength $cs] > 1} {
+ cgi_put "[cgi_span "class=sp spfcl spfcl2" "style=border-bottom: 1px solid #003399;" "title=\"Show Folder Collections\"" "onClick=\"newFolderList(this.parentNode.parentNode,null,'');\"" [cgi_span "style=display:none;" "Folders: "]]"
+ } else {
+ cgi_put [cgi_span "class=sp spfcl spfcl2" [cgi_span "style=display:none;" "Folders: "]]
+ }
+
+ set dp ""
+ set col [lindex [lindex $cs $c] 1]
+
+ if {[regexp {^(.*)/$} $dir dummy folderpath]} {
+ cgi_puts [cgi_url $col "list/$c/" title="List folders in this directory" "onClick=return newFolderList(this.parentNode.parentNode,null,$c);"]
+ set ds [split $folderpath {/}]
+ for {set i 0} {$i < [llength $ds]} {incr i} {
+ set d [lindex $ds $i]
+ if {[string length $d]} {
+ lappend dp $d
+ set path [join $dp {/}]
+ if {$i < ([llength $ds] - 1)} {
+ set d [cgi_url "${d}" "list/$c/$path/" title="List subfolders" "onClick=return newFolderList(this.parentNode.parentNode,null,$c,'$path');"]
+ } else {
+ set d [span class=flistFolder $d]
+ }
+
+ cgi_puts "[cgi_span "&raquo;"]$d"
+ }
+ }
+ } else {
+ set folderpath ""
+ cgi_puts [cgi_span $col]
+ }
+ }
+
+ cgi_division class=flistFolders id=flistFolders {
+ cgi_table cellspacing="0" cellpadding="0" class="flt" {
+ foreach f $flist {
+ set t [lindex $f 0]
+ set fn [lindex $f 1]
+ set onclick "onClick=return newFolderList(this.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode,null,${c},'[file join ${folderpath} ${fn}]');"
+ cgi_table_row class="flr" {
+ cgi_table_data class="fli" {
+ if {[string first D $t] >= 0} {
+ cgi_puts [cgi_url [cgi_span "class=sp spfl spfl1" [cgi_span "style=display:none;" [cgi_gt]]] "folders/${c}/[WPPercentQuote "${dir}${fn}" {/}]/" title="List subfolders" $onclick]
+ } else {
+ cgi_puts [cgi_nbspace]
+ }
+ }
+ cgi_table_data class="fln" {
+ set hfn [cgi_quote_html $fn]
+ if {[string first F $t] >= 0} {
+ cgi_puts [cgi_url "$hfn" "browse/${c}/[WPPercentQuote "${dir}${fn}" {/}]" class="fln" title="Click to select. Double-click to view messages." "onClick=return flistPick(this,'[WPPercentQuote $fn]');" "onDblClick=return flistPickPick(this);"]
+ } else {
+ cgi_puts "$hfn"
+ }
+ }
+ }
+ }
+ }
+ }
+ cgi_puts "<script>"
+ if {$c >= 0} {
+ cgi_puts "YAHOO.alpine.current.incoming = [WPCmd PEFolder isincoming $c];"
+ }
+ cgi_puts "updateElementValue('pickFolderCollection', '$c');"
+ cgi_puts "updateElementValue('pickFolderPath', '$folderpath');"
+ wpStatusAndNewmailJavascript
+ cgi_puts "setPanelBodyWidth('flistFolders');"
+ if {[info exists authlist]} {
+ reportAuthException $authlist
+ }
+ cgi_puts "</script>"
+}
diff --git a/web/cgi/alpine/2.0/conduit/getcontact.tcl b/web/cgi/alpine/2.0/conduit/getcontact.tcl
new file mode 100755
index 00000000..f859a21d
--- /dev/null
+++ b/web/cgi/alpine/2.0/conduit/getcontact.tcl
@@ -0,0 +1,53 @@
+#!./tclsh
+# $Id: getcontact.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# getcontact.tcl
+#
+# Purpose: CGI script to handle saving new/edited contacts
+#
+# Input:
+set contact_vars {
+ {book "" 0}
+ {index "" -1}
+}
+
+# Output:
+
+# inherit global config
+source ../alpine.tcl
+
+# Import data validate it and get session id
+if {[catch {WPGetInputAndID sessid} result]} {
+ error "getcontact.tcl $result"
+}
+
+# grok parameters
+foreach item $contact_vars {
+ if {[catch {eval WPImport $item} result]} {
+ error "getcontact.tcl $result"
+ }
+}
+
+if {[catch {WPCmd PEAddress fullentry $book "" $index} addrinfo]} {
+ error "getcontact.tcl $addrinfo"
+}
+
+puts stdout "Content-type: text/xml; charset=\"UTF-8\"\n"
+puts stdout {<?xml version="1.0" encoding="UTF-8"?>}
+puts stdout "<ResultSet totalResultsAvailable=\"1\"><Result>"
+puts stdout "<Nickname>[cgi_quote_html [lindex $addrinfo 0]]</Nickname>"
+puts stdout "<Personal>[cgi_quote_html [lindex $addrinfo 1]]</Personal>"
+puts stdout "<Mailbox>[cgi_quote_html [join [lindex $addrinfo 2] ", "]]</Mailbox>"
+puts stdout "<Fcc>[cgi_quote_html [lindex $addrinfo 3]]</Fcc>"
+puts stdout "<Note>[cgi_quote_html [lindex $addrinfo 4]]</Note>"
+puts stdout "</Result></ResultSet>"
diff --git a/web/cgi/alpine/2.0/conduit/import b/web/cgi/alpine/2.0/conduit/import
new file mode 100755
index 00000000..04326b14
--- /dev/null
+++ b/web/cgi/alpine/2.0/conduit/import
@@ -0,0 +1,73 @@
+#!./tclsh
+# $Id: import 391 2007-01-25 03:53:59Z mikes@u.washington.edu $
+# ========================================================================
+# Copyright 2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# import
+#
+# Purpose: CGI script that generates a page displaying a message
+# list of the indicated folder.
+#
+# Input: PATH_INFO: [/<col_number>]/<directory_path>
+# along with possible search parameters:
+set import_args {
+}
+
+# inherit global config
+source ../alpine.tcl
+
+# TEST
+proc cgi_suffix {args} {
+ return ""
+}
+
+WPEval $import_args {
+
+ # grok PATH_INFO for collection 'c' and folder path 'p'
+ if {!([info exists env(PATH_INFO)] && [string length $env(PATH_INFO)]
+ && [regexp {^/([0-9]+)/(.*)$} $env(PATH_INFO) dummy c p])} {
+ WPCmd PEInfo statmsg "Invalid Detach: $env(SCRIPT_NAME)"
+ cgi_exit
+ }
+
+ if {[catch {WPImport file "Missing File Upload"} result] == 0} {
+ set local_file [lindex $file 0]
+ if {[catch {WPImport newFolder "import name"} result] == 0} {
+ set iname [string trim $newFolder]
+ if {[string length $iname]} {
+ set fldr [eval "file join $p $iname"]
+ if {[catch {WPCmd PEFolder import $local_file $c $fldr} result] == 0} {
+ WPCmd PEInfo statmsg "Imported folder $iname"
+ } else {
+ WPCmd PEInfo statmsg "Can't Import File: $result"
+ }
+ } else {
+ WPCmd PEInfo statmsg "Must provide uploaded folder name"
+ }
+ } else {
+ WPCmd PEInfo statmsg "No Import Folder Name: $result"
+ }
+
+ catch {file delete -force $local_file}
+ } else {
+ WPCmd PEInfo statmsg "Cannot Import: $result"
+ }
+
+ cgi_html {
+ cgi_head {
+ cgi_javascript {
+ cgi_puts "window.parent.hideLoading();"
+ cgi_puts "window.parent.redrawFolderList();"
+ }
+ }
+ cgi_body {}
+ }
+}
diff --git a/web/cgi/alpine/2.0/conduit/mark.tcl b/web/cgi/alpine/2.0/conduit/mark.tcl
new file mode 100755
index 00000000..2ac5a2d7
--- /dev/null
+++ b/web/cgi/alpine/2.0/conduit/mark.tcl
@@ -0,0 +1,77 @@
+#!./tclsh
+# $Id: mark.tcl 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# mark.tcl
+#
+# Purpose: CGI script generating response to xmlHttpRequest
+#
+# Input:
+#
+set mark_args {
+ {u {} ""}
+ {mark {} ""}
+}
+
+# inherit global config
+source ../alpine.tcl
+
+WPEval $mark_args {
+ cgi_body {
+ switch -- $mark {
+ false {
+ set setting 0
+ }
+ true {
+ set setting 1
+ }
+ default {
+ }
+ }
+
+ if {[info exists setting]} {
+ regsub -all {,} $u { } u
+ if {[regexp {^[ 0123456789]*$} $u]} {
+ foreach eu $u {
+ if {[catch {WPCmd PEMessage $eu select $setting} result]} {
+ set result "FAILURE: setting $eu to $setting : $result"
+ break
+ }
+ }
+ } elseif {0 == [string compare $u all]} {
+ if {$setting} {
+ set setting all
+ } else {
+ set setting none
+ }
+
+ if {[catch {WPCmd PEMailbox select $setting} result]} {
+ set result "FAILURE: $result"
+ }
+ } elseif {0 == [string compare $u searched]} {
+ if {$setting} {
+ set setting searched
+ } else {
+ set setting unsearched
+ }
+
+ if {[catch {WPCmd PEMailbox select $setting} result]} {
+ set result "FAILURE: $result"
+ }
+ }
+ } else {
+ set reult "FAILURE: Unknown mark value: $mark"
+ }
+
+ cgi_puts $result
+ }
+}
diff --git a/web/cgi/alpine/2.0/conduit/newmail.tcl b/web/cgi/alpine/2.0/conduit/newmail.tcl
new file mode 100755
index 00000000..cecd3466
--- /dev/null
+++ b/web/cgi/alpine/2.0/conduit/newmail.tcl
@@ -0,0 +1,56 @@
+#!./tclsh
+# $Id: newmail.tcl 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# newmail.tcl
+#
+# Purpose: CGI script generating response to xmlHttpRequest
+#
+# Input:
+#
+set newmail_args {
+ {reload {} 0}
+}
+
+# inherit global config
+source ../alpine.tcl
+
+
+# Import data validate it and get session id
+if {[catch {WPGetInputAndID sessid}]} {
+ return
+}
+
+# grok parameters
+foreach item $newmail_args {
+ if {[catch {eval WPImport $item} errstr]} {
+ WPInfoPage "Web Alpine Error" [font size=+2 $errstr] "Please close this window."
+ return
+ }
+}
+
+puts stdout "Content-type: application/json\n"
+
+if {[catch {WPCmd PESession mailcheck $reload} newmail]} {
+ puts -nonewline stdout "{error: '$newmail'}"
+} else {
+ puts -nonewline "\["
+ set comma ""
+ foreach nm $newmail {
+ set text [lindex $nm 2]
+ regsub -all {'} $text {\'} text
+ puts -nonewline "${comma}\{newcount:[lindex $nm 0],uid:[lindex $nm 1],verbiage:'${text}'\}"
+ set comma ","
+ }
+
+ puts -nonewline "\]"
+}
diff --git a/web/cgi/alpine/2.0/conduit/post.tcl b/web/cgi/alpine/2.0/conduit/post.tcl
new file mode 100755
index 00000000..9757baca
--- /dev/null
+++ b/web/cgi/alpine/2.0/conduit/post.tcl
@@ -0,0 +1,333 @@
+#!./tclsh
+# $Id: post.tcl 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# post.tcl
+#
+# Purpose: CGI script to do the job of posting message supplied by compose form
+#
+
+# Input:
+set post_vars {
+ {cid "Missing Command ID"}
+ {check {} 0}
+ {sendop {} ""}
+ {extrahdrs {} ""}
+ {fccattach {} 0}
+ {form_flowed {} ""}
+ {subtype {} "plain"}
+ {priority {} ""}
+ {autodraftuid {} 0}
+}
+
+# inherit global config
+source ../alpine.tcl
+source ../common.tcl
+
+# Output: HTML containing javascript calls to functions of parent window
+#
+
+
+proc fieldname {name} {
+ regsub -all -- {-} [string tolower $name] {_} fieldname
+ return $fieldname
+}
+
+proc removeAutoDraftMsg {uid} {
+ if {$uid != 0} {
+ if {[regexp {^[0-9]+$} $uid]} {
+ if {[catch {WPCmd PEPostpone delete $uid} result]} {
+ catch {WPCmd PEInfo statmsg "Stale autodraft UID: $result"}
+ }
+ } else {
+ catch {WPCmd PEInfo statmsg "Invalid autodraft uid: $uid"}
+ }
+ }
+}
+
+# Import data validate it and get session id
+if {[catch {WPGetInputAndID sessid}]} {
+ return
+}
+
+# grok parameters
+foreach item $post_vars {
+ if {[catch {eval WPImport $item} errstr]} {
+ lappend errs $errstr
+ }
+}
+
+
+# collect message data
+
+# For now the input headers have to match the postheaders
+# list. Any outside the list are ignored (and probably should
+# be to avoid hostile input). Note, postheaders is a
+# super-set of composeheaders as not all headers are meant
+# to be shown the user for composition
+if {[catch {WPCmd PECompose userhdrs} headers]} {
+ error [list _action "User Headers" $headers "Click browser's Back button to try again."]
+}
+
+if {[catch {WPCmd PECompose syshdrs} otherhdrs]} {
+ error [list _action "System Headers" $otherhdrs "Click browser's Back button to try again."]
+} else {
+ eval "lappend headers $otherhdrs"
+}
+
+foreach field $headers {
+ set hdr [string tolower [lindex $field 0]]
+ regsub -all -- {-} $hdr {_} hdr
+ WPLoadCGIVarAs $hdr val
+ switch -- $hdr {
+ attach {
+ # disregard: u/i convenience (attachments marshalled below)
+ }
+ fcc {
+ if {[string length $val]} {
+ WPLoadCGIVar colid
+ set has_fcc [list $colid $val]
+ lappend msgdata [list Fcc $has_fcc]
+ }
+ }
+ default {
+ if {[string length $val] || [lsearch -exact {subject} $hdr] >= 0} {
+ # join lines
+ regsub -all {\n} $val { } val
+ if {[lsearch -exact {to cc bcc} $hdr] >= 0} {
+ # strip trailing whitespace and commas
+ regsub {[ ,]*$} $val {} val
+ set has_$hdr 1
+ }
+ lappend msgdata [list [lindex $field 0] $val]
+ }
+ }
+ }
+}
+
+if {[info exists env(REMOTE_ADDR)]} {
+ lappend msgdata [list x-auth-received "from \[$env(REMOTE_ADDR)\] by [info hostname] via HTTP; [clock format [clock seconds] -format "%a, %d %b %Y %H:%M:%S %Z"]"]
+}
+
+if {[catch {cgi_import attachments}] == 0} {
+ foreach id [split $attachments ","] {
+ lappend msgdata [list attach $id]
+ }
+}
+
+cgi_import body
+
+# pass body text options, dress as necessary
+if {0 == [string compare -nocase $subtype html]} {
+ lappend msgdata [list postoption [list subtype html]]
+ set body "<html><head><title></title></head>\n<body>\n${body}\n</body></html>"
+ set compose_mode rich
+} else {
+ set compose_mode plain
+ if {[string length $form_flowed]} {
+ lappend msgdata [list postoption [list flowed yes]]
+ }
+}
+
+if {[regexp {^(lowest|low|normal|high|highest)$} $priority]} {
+ lappend msgdata [list postoption [list priority $priority]]
+}
+
+lappend msgdata [list body [split $body "\n"]]
+
+switch -exact -- $fccattach {
+ 0 -
+ 1 {
+ lappend msgdata [list postoption [list fcc-without-attachments [expr {!$fccattach}]]]
+ }
+}
+
+lappend msgdata [list postoption [list charset "utf-8"]]
+
+# figure out what to do with data
+if {[string compare $sendop send] == 0} {
+ if {[info exists has_to] || [info exists has_cc] || [info exists has_bcc] || [info exists has_fcc]} {
+ # expand any nicknames
+ if {[catch {
+ set fccdef [WPCmd PECompose fccdefault]
+ for {set i 0} {$i < [llength $msgdata]} {incr i} {
+ if {[string length [lindex [lindex $msgdata $i] 1]]} {
+ set fld [lindex $msgdata $i]
+ set fn [string tolower [lindex $fld 0]]
+ switch -- $fn {
+ [Ff]cc {
+ if {[string length [lindex [lindex $fld 1] 1]]} {
+ # setup for send confirmation
+ set colidval [lindex [lindex $fld 1] 0]
+ set fccval [lindex [lindex $fld 1] 1]
+ }
+ }
+ to -
+ cc -
+ bcc -
+ reply-to {
+ set expaddr [WPCmd PEAddress expand [lindex $fld 1] {}]
+ if {[string compare [lindex $expaddr 0] [lindex $fld 1]]} {
+ set msgdata [lreplace $msgdata $i $i [list [lindex $fld 0] [lindex $expaddr 0]]]
+
+ # if expanded, update fcc?
+ if {[string compare to $fn] == 0 && [string length $fn]} {
+ set expanded_fcc [lindex $expaddr 2]
+ }
+ }
+ }
+ body {
+ if {[string length $form_flowed]} {
+ set ws "\[ \t]"
+ set nws "\[^ \t]"
+
+ set nextline [lindex [lindex $fld 1] 0]
+ for {set j 1} {$j <= [llength [lindex $fld 1]]} {incr j} {
+ set line $nextline
+ # space stuff?
+ if {[regexp "^${ws}+" $line]} {
+ set line " $line"
+ }
+
+ set nextline [lindex [lindex $fld 1] $j]
+ if {[regexp {^-- $} $line] == 0} {
+ catch {unset linetext}
+ # trim trailing WS from lines preceding those with LWS (space-stuff as needed)
+ if {[string length $nextline] == 0 || [regexp "^${ws}+(${nws}?.*)\$" $nextline dummy linetext]} {
+ set line [string trimright $line]
+ if {[info exists linetext] == 0 || [string length $linetext] == 0} {
+ set nextline ""
+ }
+ }
+
+ # break overly long lines in a flowed way
+ if {[regexp {^[^>]} $line] && [string length $line] > 1000} {
+ while {[regexp "^(${ws}*${nws}+${ws}+)$nws" [string range $line 900 end] dummy linex]} {
+ set cliplen [expr {900 + [string length $linex]}]
+ lappend newbody [string range $line 0 [expr {$cliplen - 1}]]
+ set line [string range $line $cliplen end]
+ }
+ }
+ }
+
+ lappend newbody $line
+ }
+
+ set msgdata [lreplace $msgdata $i $i [list body $newbody]]
+ }
+ }
+ default {
+ }
+ }
+ }
+ }
+ } result]} {
+ set reportfunc sendFailure
+ set postresult "Address problem: $result"
+ } else {
+ # update fcc?
+ if {[info exists expanded_fcc]
+ && (![info exists has_fcc] || 0 == [string compare [lindex $fccdef 1] [lindex $has_fcc 1]])} {
+ for {set j 0} {$j < [llength $msgdata]} {incr j} {
+ if {[string compare fcc [fieldname [lindex [lindex $msgdata $j] 0]]] == 0} {
+ set fcc_index $j
+ break
+ }
+ }
+
+ set colid [lindex $fccdef 0]
+ if {[info exists fcc_index]} {
+ set msgdata [lreplace $msgdata $fcc_index $fcc_index [list Fcc [list $colid $expanded_fcc]]]
+ } else {
+ lappend msgdata [list Fcc [list $colid $expanded_fcc]]
+ }
+ }
+
+ removeAutoDraftMsg $autodraftuid
+
+ # do the sending...
+ set verb Send
+ set verbpast Sent
+ set postcmd PECompose
+ set postcmdopt post
+ if {[info exists compose_mode]} {
+ catch {WPSessionState compose_mode $compose_mode}
+ }
+ }
+ } else {
+ set reportfunc sendFailure
+ set postresult "Send MUST include Recipients (To, Cc, Bcc, or Fcc)"
+ }
+} elseif {[string compare $sendop postpone] == 0} {
+ removeAutoDraftMsg $autodraftuid
+ set verb "Save to Drafts"
+ set verbpast "Saved to Drafts"
+ set postcmd PEPostpone
+ set postcmdopt append
+} elseif {[string compare $sendop autodraft] == 0} {
+ removeAutoDraftMsg $autodraftuid
+
+ set verb "Save to Drafts"
+ set verbpast "Saved to Drafts"
+ set postcmd PEPostpone
+ set postcmdopt draft
+ set reportfunc reportAutoDraft
+} else {
+ set reportfunc sendFailure
+ set postresult "Unrecognized Action"
+}
+
+#do what was asked
+if {[info exists postcmd]} {
+ if {[info exists msgdata]} {
+ if {[catch {WPCmd $postcmd $postcmdopt $msgdata} postresult]} {
+ set auth [wpHandleAuthException $postresult [list 0 "send"]]
+ if {[string length $auth]} {
+ set reportcall "processPostAuthException(\{${auth}\});"
+ } else {
+ set reportfunc sendFailure
+ }
+ } elseif {0 == [string compare $postcmd PECompose]} {
+ WPCmd PEInfo statmsg "Message $verbpast"
+ }
+ } else {
+ WPCmd PEInfo statmsg "No Message $verbpast!"
+ }
+
+ if {[info exists delete_me]} {
+ foreach i $delete_me {
+ catch {file delete $i}
+ }
+ }
+}
+
+puts stdout "Content-type: text/html;\n\n<html><head><script>"
+
+if {[info exists reportfunc]} {
+ puts stdout "window.parent.${reportfunc}(\"${postresult}\");"
+ foreach sm [WPCmd PEInfo statmsgs] {
+ regsub -all {'} $sm {\'} sm
+ puts stdout "window.parent.sendFailure(\"${sm}\");"
+ }
+} elseif {[info exists reportcall]} {
+ puts stdout "window.parent.${reportcall};"
+ foreach sm [WPCmd PEInfo statmsgs] {
+ regsub -all {'} $sm {\'} sm
+ if {0 == [regexp {Authentication cancelled} $sm]} {
+ puts stdout "window.parent.sendFailure(\"${sm}\");"
+ }
+ }
+} else {
+ puts stdout "window.parent.sendSuccess(\"${postresult}\");"
+}
+
+puts stdout "</script></head><body></body></html>"
diff --git a/web/cgi/alpine/2.0/conduit/query.tcl b/web/cgi/alpine/2.0/conduit/query.tcl
new file mode 100755
index 00000000..eba4ee77
--- /dev/null
+++ b/web/cgi/alpine/2.0/conduit/query.tcl
@@ -0,0 +1,83 @@
+#!./tclsh
+# $Id: query.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# query.tcl
+#
+# Purpose: CGI script to handle querying LDAP directory
+#
+# Input:
+set query_vars {
+ {dir "" "0"}
+ {query "" ""}
+}
+
+# Output:
+
+# inherit global config
+source ../alpine.tcl
+
+# Import data validate it and get session id
+if {[catch {WPGetInputAndID sessid}]} {
+ return
+}
+
+# grok parameters
+foreach item $query_vars {
+ if {[catch {eval WPImport $item} errstr]} {
+ lappend errs $errstr
+ }
+}
+
+set qresult ""
+
+# return attachment list
+puts stdout "Content-type: text/html;\n\n<html><head><script>window.parent.drawLDAPResult({"
+if {[string length $query]} {
+ if {[catch {WPCmd PELdap query $dir $query ""} qn]} {
+ regsub -all {'} $qn {\'} qn
+ puts stdout "error:'Search failed: $qn'"
+ } else {
+ switch $qn {
+ 0 { puts stdout "error:'Search found no matching entries'" }
+ default {
+ if {[catch {WPCmd PELdap results $qn} results]} {
+ regsub -all {'} $results {\'} results
+ puts stdout "error:'Problem with results: $results'"
+ } else {
+ puts stdout "results:\["
+ foreach result $results {
+ set res [lindex $result 1]
+ set comma ""
+ foreach r $res {
+ regsub -all {'} $r {\'} r
+ puts -nonewline stdout "${comma}{personal:'[lindex $r 0]',email:\["
+ set comma2 ""
+ foreach a [lindex $r 4] {
+ puts -nonewline stdout "$comma2'$a'"
+ set comma2 ","
+ }
+ puts -nonewline stdout "\]}"
+ set comma ","
+ }
+ }
+
+ puts stdout "\]"
+ }
+ }
+ }
+ }
+} else {
+ puts stdout "error:'Empty search request'"
+}
+
+puts stdout "});</script></head><body></body></html>"
diff --git a/web/cgi/alpine/2.0/conduit/settings.tcl b/web/cgi/alpine/2.0/conduit/settings.tcl
new file mode 100755
index 00000000..f4f30922
--- /dev/null
+++ b/web/cgi/alpine/2.0/conduit/settings.tcl
@@ -0,0 +1,184 @@
+#!./tclsh
+# $Id: settings.tcl 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# settings.tcl
+#
+# Purpose: CGI script to do the job of updating submitted settings
+#
+
+# Input:
+set settings_vars {
+ {restore "" false}
+}
+
+# inherit global config
+source ../alpine.tcl
+source ../common.tcl
+
+# Output: HTML containing javascript calls to functions of parent window
+#
+
+
+# Import data validate it and get session id
+if {[catch {WPGetInputAndID sessid}]} {
+ return
+}
+
+foreach item $settings_vars {
+ if {[catch {eval WPImport $item} result]} {
+ regsub -all {"} $result {\"} errstr
+ break
+ }
+}
+
+set response "Settings Updated"
+
+# feature settings
+if {0 == [string compare restore $restore]} {
+ WPCmd PEConfig reset pinerc
+ set response "Default Settings Restored"
+} elseif {[catch {
+ WPCmd PEConfig newconf
+
+ # collect feature settings
+ set setfeatures [WPCmd PEConfig featuresettings]
+ foreach feature {
+ include-header-in-reply
+ include-attachments-in-reply
+ signature-at-bottom
+ strip-from-sigdashes-on-reply
+ fcc-without-attachments
+ auto-move-read-msgs
+ enable-msg-view-urls
+ feature enable-msg-view-web-hostnames
+ enable-msg-view-addresses
+ render-html-internally
+ quell-server-after-link-in-html
+ quell-flowed-text
+ } {
+ if {[catch {cgi_import_as $feature setting}]} {
+ if {[lsearch $setfeatures $feature] >= 0} {
+ WPCmd PEConfig feature $feature 0
+ }
+ } else {
+ if {[lsearch $setfeatures $feature] < 0} {
+ WPCmd PEConfig feature $feature 1
+ }
+ }
+ }
+
+ # collect single-line text variable settings
+ foreach variable {
+ wp-indexlines
+ default-fcc
+ personal-name
+ user-domain
+ sort-key
+ incoming-startup-rule
+ posting-character-set
+ reply-leadin
+ reply-indent-string
+ postponed-folder
+ trash-folder
+ inbox-path
+ rss-news
+ rss-weather
+ read-message-folder
+ } {
+ set varvals [WPCmd PEConfig varget $variable]
+ set varvalue [lindex $varvals 0]
+ cgi_import_as $variable value
+ set value [split $value "\n"]
+ if {[string compare $varvalue $value]} {
+ WPCmd PEConfig varset $variable $value
+ }
+ }
+
+ # collect list-style variable settings
+ foreach {variable handle} {
+ alt-addresses altAddr
+ viewer-hdrs viewerHdr
+ default-composer-hdrs composeHdr
+ smtp-server smtpServer
+ ldap-servers ldapServer
+ } {
+ if {0 == [catch {cgi_import_as ${handle}s lcount}]} {
+ set l {}
+ for {set i 1} {$i <= $lcount} {incr i} {
+ if {0 == [catch {cgi_import_as ${handle}${i} value}] && [string length $value]} {
+ lappend l $value
+ }
+ }
+
+ WPCmd PEConfig varset $variable $l
+ }
+ }
+
+ # special-case variable settings
+ if {0 == [catch {cgi_import customHdrFields}]} {
+ set hdrs {}
+ for {set i 1} {$i <= $customHdrFields} {incr i} {
+ if {0 == [catch {cgi_import_as customHdrField${i} field}]} {
+ set hdr "${field}:"
+ if {0 == [catch {cgi_import_as customHdrData${i} value}]} {
+ append hdr " $value"
+ }
+
+ lappend hdrs $hdr
+ }
+ }
+
+ if {[llength $hdrs]} {
+ WPCmd PEConfig varset customized-hdrs $hdrs
+ }
+ }
+
+ cgi_import wrapColumn
+ WPCmd PEConfig columns $wrapColumn
+
+ cgi_import folderCache
+ WPSessionState left_column_folders $folderCache
+
+ cgi_import forwardAs
+ if {0 == [string compare attached $forwardAs]} {
+ if {[lsearch $setfeatures $feature] < 0} {
+ WPCmd PEConfig feature forward-as-attachment 1
+ }
+ } else {
+ if {[lsearch $setfeatures $feature] >= 0} {
+ WPCmd PEConfig feature forward-as-attachment 0
+ }
+ }
+
+ cgi_import signature
+ set cursig [string trimright [join [WPCmd PEConfig rawsig] "\n"]]
+ set signature [string trimright $signature]
+ if {[string compare $cursig $signature]} {
+ WPCmd PEConfig rawsig [split $signature "\n"]
+ }
+
+ WPCmd PEConfig saveconf
+} result]} {
+ regsub -all {"} $result {\"} errstr
+}
+
+puts stdout "Content-type: text/html;\n\n<html><head><script>"
+
+if {[info exists errstr]} {
+ puts stdout "window.parent.settingsFailure(\"${errstr}\");"
+} else {
+ WPCmd PEInfo statmsg $response
+ puts stdout "window.parent.settingsSuccess();"
+}
+
+puts stdout "</script></head><body></body></html>"
diff --git a/web/cgi/alpine/2.0/conduit/storecontact.tcl b/web/cgi/alpine/2.0/conduit/storecontact.tcl
new file mode 100755
index 00000000..36836f54
--- /dev/null
+++ b/web/cgi/alpine/2.0/conduit/storecontact.tcl
@@ -0,0 +1,67 @@
+#!./tclsh
+# $Id: storecontact.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# storecontact.tcl
+#
+# Purpose: CGI script to handle saving new/edited contacts
+#
+# Input:
+set store_vars {
+ {book "" 0}
+ {ai "" -1}
+ {contactNick "" ""}
+ {contactName "" ""}
+ {contactEmail "" ""}
+ {contactFcc "" ""}
+ {contactNotes "" ""}
+}
+
+# Output:
+
+# inherit global config
+source ../alpine.tcl
+
+# Import data validate it and get session id
+if {[catch {WPGetInputAndID sessid}]} {
+ return
+}
+
+# grok parameters
+foreach item $store_vars {
+ if {[catch {eval WPImport $item} errstr]} {
+ lappend errs $errstr
+ }
+}
+
+if {[string length $contactNick] || [string length $contactName] || [string length $contactEmail]} {
+ set result ""
+ if {[catch {WPCmd PEAddress edit $book $contactNick $ai $contactName $contactEmail $contactFcc $contactNotes 1} result]} {
+ lappend status "Address Set Failure: $result"
+ } elseif {[string length $result]} {
+ lappend status "$result"
+ } else {
+ lappend status "Contact Added"
+ }
+} else {
+ lappend status "No Contact Added: Must contain Display Name, Address or Nickname"
+}
+
+# return response text
+puts stdout "Content-type: text/xml; charset=\"UTF-8\"\n"
+puts stdout {<?xml version="1.0" encoding="UTF-8"?>}
+puts stdout "<ResultSet totalResultsAvailable=\"[llength $status]\">"
+foreach sm $status {
+ regsub {'} $sm {\'} sm
+ puts stdout "<Result><StatusText>$sm</StatusText></Result>"
+}
+puts stdout "</ResultSet>"
diff --git a/web/cgi/alpine/2.0/conduit/take.tcl b/web/cgi/alpine/2.0/conduit/take.tcl
new file mode 100755
index 00000000..4f5f1342
--- /dev/null
+++ b/web/cgi/alpine/2.0/conduit/take.tcl
@@ -0,0 +1,84 @@
+#!./tclsh
+# $Id: take.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# take.tcl
+#
+# Purpose: CGI script to supply data to YUI AutoTake
+#
+# Input:
+# along with possible search parameters:
+set take_args {
+ {op {} "noop"}
+ {u {} 0}
+}
+
+# inherit global config
+source ../alpine.tcl
+
+# Import data validate it and get session id
+if {[catch {WPGetInputAndID sessid}]} {
+ error "take.tcl $result"
+}
+
+# grok parameters
+foreach item $take_args {
+ if {[catch {eval WPImport $item} result]} {
+ set errstr $result
+ break;
+ }
+}
+
+switch -- $op {
+ from {
+ if {[catch {WPCmd PEMessage $u takefrom} result]} {
+ set errstr $result
+ }
+ }
+ all {
+ if {[catch {WPCmd PEMessage $u takeaddr} result]} {
+ set errstr $result
+ }
+ }
+ default {
+ set errstr "Unrecognized Take option: $op"
+ }
+}
+
+puts stdout "Content-type: text/xml; charset=\"UTF-8\"\n"
+puts stdout {<?xml version="1.0" encoding="UTF-8"?>}
+puts stdout "<ResultSet totalResultsAvailable=\"[llength $result]\">"
+if {[info exists errstr]} {
+ puts -nonewline stdout "<Result><Error>$errstr</Error></Result>"
+} else {
+ foreach r $result {
+ set ar [lindex $r 1]
+ set sr [lindex $r 2]
+ if {[string length [lindex $sr 0]] > 0
+ || [string length [lindex $sr 2]] > 0
+ || [string length [lindex $sr 3]] > 0} {
+ set type "edit"
+ } else {
+ set type "add"
+ }
+ # there is also an addrbook Fullname in sr 1
+ puts -nonewline stdout "<Result>"
+ puts -nonewline stdout "<Type>[cgi_quote_html $type]</Type>"
+ puts -nonewline stdout "<Nickname>[cgi_quote_html [lindex $sr 0]]</Nickname>"
+ puts -nonewline stdout "<Personal>[cgi_quote_html [lindex $ar 0]]</Personal>"
+ puts -nonewline stdout "<Email>[cgi_quote_html "[lindex $ar 1]@[lindex $ar 2]"]</Email>"
+ puts -nonewline stdout "<Fcc>[cgi_quote_html [lindex $sr 2]]</Fcc>"
+ puts -nonewline stdout "<Note>[cgi_quote_html [lindex $sr 3]]</Note>"
+ puts stdout "</Result>"
+ }
+}
+puts stdout {</ResultSet>}
diff --git a/web/cgi/alpine/2.0/conduit/tclsh b/web/cgi/alpine/2.0/conduit/tclsh
new file mode 120000
index 00000000..385fc6c6
--- /dev/null
+++ b/web/cgi/alpine/2.0/conduit/tclsh
@@ -0,0 +1 @@
+../tclsh \ No newline at end of file
diff --git a/web/cgi/alpine/2.0/contacts b/web/cgi/alpine/2.0/contacts
new file mode 100755
index 00000000..1cbec957
--- /dev/null
+++ b/web/cgi/alpine/2.0/contacts
@@ -0,0 +1,201 @@
+#!./tclsh
+# $Id: contacts 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# view.tcl
+#
+# Purpose: CGI script contacts for Web Alpine 2.0 pages
+#
+# Input: PATH_INFO: [/<col_number>]/<folder_name>[/<uid_of_viewed_msg>
+# along with possible search parameters:
+set contacts_args {
+}
+
+# inherit global config
+source ./alpine.tcl
+source ./foldercache.tcl
+source ./common.tcl
+
+# TEST
+proc cgi_suffix {args} {
+ return ""
+}
+
+# WHILE TESTING/DEBUGGING
+proc noimp {s} {
+ return "onClick=return noImp('${s}');"
+}
+
+set dmsgs ""
+proc dm {s} {
+ global dmsgs
+
+ lappend dmsgs $s
+}
+
+
+WPEval $contacts_args {
+ # verify current collection/folder
+ if {[catch {WPCmd PEFolder current} curfold]} {
+ error [list _action browse "cannot determine default folder: $curfold"]
+ } else {
+ set c [lindex $curfold 0]
+ set f [lindex $curfold 1]
+ }
+
+ set charset "UTF-8"
+ set u 0
+
+ cgi_http_head {
+ WPStdHttpHdrs "text/html; charset=$charset"
+ }
+
+ cgi_html {
+ cgi_head {
+ cgi_content_type "text/html; charset=$charset"
+ # WPStdHttpHdrs "text/html; charset=$charset"
+ cgi_title [wpPageTitle "Contacts"]
+ cgi_base "href=$_wp(serverpath)/$_wp(appdir)/$_wp(ui2dir)/"
+
+ cgi_stylesheet css/menu.css
+ cgi_stylesheet css/cbn/screen.css
+ cgi_stylesheet css/cbn/contacts.css
+ # Yahoo Styles
+ cgi_stylesheet $_wp(yui)/build/container/assets/container-core.css
+ cgi_stylesheet $_wp(yui)/build/menu/assets/skins/sam/menu.css
+ cgi_stylesheet $_wp(yui)/build/button/assets/skins/sam/button.css
+ # YahooUI libraries
+ cgi_script type=text/javascript language="JavaScript" src="$_wp(yui)/build/utilities/utilities.js" {}
+ cgi_script type=text/javascript language="JavaScript" src="$_wp(yui)/build/button/button-min.js" {}
+ cgi_script type=text/javascript language="JavaScript" src="$_wp(yui)/build/container/container.js" {}
+ cgi_script type=text/javascript language="JavaScript" src="$_wp(yui)/build/datasource/datasource-min.js" {}
+ cgi_script type=text/javascript language="JavaScript" src="$_wp(yui)/build/menu/menu-min.js" {}
+ # local libraries
+ cgi_script language="JavaScript" src="lib/common.js" {}
+ cgi_script language="JavaScript" src="lib/contacts.js" {}
+
+ cgi_javascript {
+ cgi_puts "var gCurrentAbook = 0;"
+ cgi_puts "YAHOO.alpine.current.c = '$c';"
+ cgi_puts "YAHOO.alpine.current.f = '$f';"
+ cgi_puts "YAHOO.alpine.current.u = $u;"
+ cgi_puts "function updateContactCount(nBooks,nContacts){"
+ cgi_puts " var el = document.getElementById('contactCount');"
+ cgi_puts " if(el) el.innerHTML = nContacts + ' Contacts';"
+ cgi_puts "}"
+ cgi_puts "function bodyOnLoad() {"
+ cgi_puts " initMenus();"
+ cgi_puts " if(YAHOO.env.ua.gecko > 0){ sizeVPHeight(); window.onresize = resizeVPHeight; }"
+ cgi_puts " setCheckMailFunction('gCheck', newMailCheck);"
+ cgi_puts " setNewMailCheckInterval([WPCmd PEInfo inputtimeout]);"
+ cgi_puts " drawContactList('alpineContent',gCurrentAbook,{hdr:'on',sendto:'on',canedit:'on'});"
+ cgi_puts "}"
+
+ # WHILE TESTING/DEBUGGING
+ cgi_puts "var impi = new Array(); function noImp(m){ var sm; if(impi\[m\]) {impi\[m\]++; if(impi\[m\] > 4) sm = '<b>Seriously,</b> ' + m + ' will <b>never</b> get implemented if you keep bothering me!'; else sm = m + ' is <b>still</b> not implemented!'; } else { sm = m + ' is not implemented yet!' ; impi\[m\] = 1 ;} showStatusMessage(sm,3); return false; }"
+
+ cgi_puts "browserDetect();"
+ }
+ }
+
+ cgi_body class=wap "onLoad=bodyOnLoad()" {
+
+ wpCommonPageLayout contacts "$c" "$f" "$u" "Contacts" [list [cgi_cgi "$_wp(appdir)/$_wp(ui2dir)/contacts"] Contacts 0 searchContent('contacts','clistContacts')] "" {
+ # CONTEXT COMMANDS
+ cgi_division class=hdrBtns {
+ cgi_javascript {
+ cgi_put "if(window.print) document.write('[cgi_buffer {cgi_put [cgi_url "[cgi_span "class=sp hdrBtnImg hbi1" ""][cgi_span "class=hdrBtnText" Print]" "print" "onClick=return printContent()"]}]');"
+ }
+
+ cgi_put [cgi_url "[cgi_span "class=sp hdrBtnImg hbi2" ""][cgi_span "class=hdrBtnText" Settings]" "settings"]
+ cgi_put [cgi_url "[cgi_span "class=sp hdrBtnImg hbi3" ""][cgi_span "class=hdrBtnText" Help]" "javascript:openHelpWindow('contacts.html');" class=wap]
+ cgi_put [cgi_url "[cgi_span "class=sp hdrBtnImg hbi4" ""][cgi_span "class=hdrBtnText" "Sign out"]" "../../session/logout.tcl?cid=[WPCmd PEInfo key]&sessid=${sessid}"]
+ }
+ } {
+ # TO MENUBAR
+ cgi_anchor_name "toolbar"
+ cgi_table class="toolbarTbl" cellpadding="0" cellspacing="0" {
+ cgi_puts "<tbody>"
+ cgi_table_row {
+ cgi_table_data {
+ cgi_table class="toolbarTbl" cellpadding="0" cellspacing="0" {
+ cgi_puts "<tbody>"
+ cgi_table_row {
+ cgi_table_data class=wap {
+ cgi_puts [cgi_url [cgi_span "class=sp spmbi spmb13" "New Contact"] "contacts/new" title="Create new contact" "onClick=return contactEditor({which:'add'},storeNewContact);"]
+
+ }
+ cgi_table_data class=wap {
+ cgi_puts [cgi_url [cgi_span "class=sp spmbi spmb14" "New Group"] "contacts/new/group" title="Create new group contact" "onClick=return contactEditor({which:'add',group:true},storeNewContact);"]
+ }
+ cgi_table_data class="dv1" {
+ cgi_puts [cgi_img "img/cbn/div.gif"]
+ }
+ cgi_table_data class=wap {
+ cgi_puts [cgi_url [cgi_span "class=sp spmbi spmb9" "Send Email"] "send" title="Send email to selected contact" "onClick=return sendToContact();"]
+ }
+ cgi_table_data class=wap {
+ cgi_puts [cgi_url "[cgi_img "img/cbn/edit.gif" class=wap] Edit" "#" title="Edit selected contact" "onClick=return editCheckedContact();"]
+ }
+ cgi_table_data class=wap {
+ cgi_puts [cgi_url "[cgi_img "img/cbn/delete.gif" class=wap] Delete" "#" title="Delete selected contact" "onClick=return contactDelete();"]
+ }
+ cgi_table_data class="dv1" {
+ cgi_puts [cgi_img "img/cbn/div.gif"]
+ }
+ cgi_table_data class=wap {
+ cgi_bullet_list class="menu" {
+ cgi_put "<li class=\"menuHdr\">[cgi_url "More Actions [cgi_img "img/cbn/menu.gif" class="wap menuDn menuImg"]" "#" "onClick=return false;"]<div>"
+ cgi_bullet_list {
+ cgi_li [cgi_url "Import vCards" "#" [noimp "vCard import"]]
+ cgi_li [cgi_url "Export vCards" "#" [noimp "vCard export"]]
+ }
+ cgi_puts "</div></li>"
+ }
+ }
+ cgi_table_data width="100%" {
+ cgi_puts [cgi_nbspace]
+ }
+ }
+ cgi_puts "</tbody>"
+ }
+ }
+ }
+ cgi_puts "</tbody>"
+ }
+ } {
+ # MAIN PAGE CONTENT
+ cgi_puts "Loading..."
+ } {
+ # BOTTOM MENUBAR
+ cgi_table class="wap toolbarTbl" cellpadding="0" cellspacing="0" {
+ cgi_puts "<tbody>"
+ cgi_table_row {
+ cgi_table_data class="wap pageText" id=contactCount {
+ cgi_puts ""
+ }
+ cgi_table_data width="100%" {
+ cgi_puts [cgi_nbspace]
+ }
+ }
+ cgi_puts "</tbody>"
+ }
+ }
+
+ # any debugging info to insert?
+ foreach dmsg $dmsgs {
+ cgi_html_comment "DEBUG: $dmsg"
+ cgi_puts ""
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/2.0/css/cbn/contactdialog.css b/web/cgi/alpine/2.0/css/cbn/contactdialog.css
new file mode 100644
index 00000000..8f9d64c9
--- /dev/null
+++ b/web/cgi/alpine/2.0/css/cbn/contactdialog.css
@@ -0,0 +1,108 @@
+/*
+ * $Id: contactdialog.css 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+div#contactsDialogFramework
+{
+ display: none;
+}
+
+div#contactDialog
+{
+ overflow: hidden;
+}
+
+div.clistInstructions
+{
+ padding: 15px 5px 20px 5px;
+ text-align: center;
+}
+
+div.contactContent
+{
+ font-size: .8em;
+ height: 14.5em;
+ border: 1px solid #aaaaaa;
+}
+
+div.clistContext
+{
+ border-bottom: 1px solid #36c;
+ padding: .5em 0;
+ font-weight: bold;
+ color: #003399;
+ background-color: #e7f0ff;
+ overflow: hidden;
+}
+
+div.clistContext *
+{
+ margin-left: 5px;
+}
+
+div.clistContext img
+{
+ vertical-align: text-bottom;
+}
+
+div.clistContext a
+{
+ color: #003399;
+}
+
+span.clistContact
+{
+ color: black;
+}
+
+div.clistContacts
+{
+ overflow: auto;
+ height: 12em;
+}
+
+input#dirQuery
+{
+ padding: 0 2px;
+}
+
+div.clistContacts table tr td
+{
+ padding: none;
+ border: none;
+ font-size: .8em;
+ white-space: nowrap;
+/* width: 100%;
+*/
+}
+
+td.fli
+{
+ width: 24px;
+ text-align: center;
+}
+
+tr#flPick, tr#flPick a
+{
+ color: #ffffff;
+ background-color: #417bd9;
+}
+
+a.fln
+{
+ text-decoration: none;
+}
+
+td.clcd
+{
+}
diff --git a/web/cgi/alpine/2.0/css/cbn/contacts.css b/web/cgi/alpine/2.0/css/cbn/contacts.css
new file mode 100644
index 00000000..29a1148e
--- /dev/null
+++ b/web/cgi/alpine/2.0/css/cbn/contacts.css
@@ -0,0 +1,87 @@
+/*
+ * $Id: contacts.css 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+div.clistInstructions
+{
+ padding: 15px 5px 20px 5px;
+ text-align: center;
+}
+
+div#alpineContent
+{
+ font-size: .9em;
+}
+
+div.clistContext
+{
+ border-bottom: 1px solid #36c;
+ padding: .5em 0;
+ font-weight: bold;
+ color: #003399;
+ background-color: #e7f0ff;
+ overflow: auto;
+}
+
+div.clistContext *
+{
+ margin-left: 5px;
+}
+
+div.clistContext img
+{
+ vertical-align: text-bottom;
+}
+
+div.clistContext a
+{
+ color: #003399;
+}
+
+div.clistContacts
+{
+ width: 100%;
+ padding-top: 3px;
+ overflow-y: auto;
+}
+
+div.clistContacts table
+{
+ width: 100%;
+ padding: none;
+ border: none;
+}
+
+div.clistContacts table tbody
+{
+ border-bottom: 1px solid #D4D4D4;
+}
+
+div.clistContacts table tbody tr
+{
+ border-bottom: 1px solid #36c;
+}
+
+table.clt
+{
+}
+
+td.cli
+{
+ width: 12mm;
+ padding: 5mm 0 5mm 10mm;
+}
+
+td.cln
+{
+} \ No newline at end of file
diff --git a/web/cgi/alpine/2.0/css/cbn/folderdialog.css b/web/cgi/alpine/2.0/css/cbn/folderdialog.css
new file mode 100644
index 00000000..9f09c647
--- /dev/null
+++ b/web/cgi/alpine/2.0/css/cbn/folderdialog.css
@@ -0,0 +1,96 @@
+/*
+ * $Id: folderdialog.css 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+div.flistInstructions
+{
+ padding: 15px 5px 20px 5px;
+ text-align: center;
+}
+
+div#folderList
+{
+ font-size: .8em;
+ height: 14.5em;
+ border: 1px solid #aaaaaa;
+}
+
+div.flistContext
+{
+ border-bottom: 1px solid #36c;
+ padding: .5em 0;
+ font-weight: bold;
+ color: #003399;
+ background-color: #e7f0ff;
+}
+
+div.flistContext *
+{
+ margin-left: 5px;
+}
+
+div.flistContext img
+{
+ vertical-align: text-bottom;
+}
+
+div.flistContext a
+{
+ color: #003399;
+}
+
+span.flistFolder
+{
+ color: black;
+}
+
+div.flistFolders
+{
+ overflow: auto;
+ height: 12em;
+}
+
+div.flistFolders a
+{
+ font-size: .8em;
+}
+
+table.flt
+{
+ border: none;
+ width: 100%;
+}
+
+td.fli
+{
+ width: 24px;
+ text-align: center;
+}
+
+tr#flPick, tr#flPick a
+{
+ color: #ffffff;
+ background-color: #417bd9;
+}
+
+a.fln
+{
+ text-decoration: none;
+}
+
+td.flcd
+{
+ font-size: .9em;
+ padding-left: 3em;
+ padding-bottom: 1.5em;
+}
diff --git a/web/cgi/alpine/2.0/css/cbn/folders.css b/web/cgi/alpine/2.0/css/cbn/folders.css
new file mode 100644
index 00000000..5c190f75
--- /dev/null
+++ b/web/cgi/alpine/2.0/css/cbn/folders.css
@@ -0,0 +1,99 @@
+/*
+ * $Id: folders.css 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+div.flistInstructions
+{
+ padding: 15px 5px 20px 5px;
+ text-align: center;
+}
+
+div#alpineContent
+{
+ font-size: .9em;
+}
+
+div.flistContext
+{
+ border-bottom: 1px solid #36c;
+ padding: .5em 0;
+ font-weight: bold;
+ color: #003399;
+ background-color: #e7f0ff;
+ overflow: auto;
+}
+
+div.flistContext *
+{
+ margin-left: 5px;
+}
+
+div.flistContext img
+{
+ vertical-align: text-bottom;
+}
+
+div.flistContext a
+{
+ color: #003399;
+}
+
+span.flistFolder
+{
+ color: black;
+ vertical-align: middle;
+}
+
+div.flistFolders
+{
+ padding-top: 3px;
+ overflow-y: auto;
+}
+
+table.flt
+{
+ padding: none;
+ border: none;
+ width: 100%;
+}
+
+td.fli
+{
+ width: 24px;
+ text-align: center;
+ padding-left: 12px;
+}
+
+tr.flr
+{
+ border-bottom: 1px solid #36c;
+}
+
+tr#flPick, tr#flPick a
+{
+ color: #ffffff;
+ background-color: #417bd9;
+}
+
+.fln
+{
+ text-decoration: none;
+ font-size: .9em;
+}
+
+td.flcd
+{
+ font-size: .9em;
+ padding-left: 3em;
+ padding-bottom: 1.5em;
+}
diff --git a/web/cgi/alpine/2.0/css/cbn/screen.css b/web/cgi/alpine/2.0/css/cbn/screen.css
new file mode 100644
index 00000000..ec2107aa
--- /dev/null
+++ b/web/cgi/alpine/2.0/css/cbn/screen.css
@@ -0,0 +1,2084 @@
+/*
+ * $Id: screen.css 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+ * ========================================================================
+ * Copyright 2006-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*** GLOBAL ***/
+body.wap
+{
+ font-family: Tahoma, Helvetica, sans-serif;
+ margin: 0 0;
+}
+form.wap
+{
+ display: inline;
+}
+select.wap, input.wap, textarea.wap, button.wap, label.wap
+{
+ font-family: Tahomaf, Helvetica, sans-serif;
+ font-size: 1.0em; /* required by IE */
+}
+td.wap, th.wap, div.wap
+{
+ white-space: nowrap;
+ font-size: .7em;
+}
+h1.wap
+{
+ display: inline;
+ font-weight: normal;
+ font-size: 1.0em;
+}
+h2.wap, h3.wap, h4.wap, h5.wap, h6.wap
+{
+ display: inline;
+}
+a.wap
+{
+ color: #003399;
+ text-decoration: none;
+}
+a.wap:hover
+{
+ text-decoration: underline;
+}
+img.wap
+{
+ border-style: none;
+}
+.rt
+{
+ text-align: right;
+}
+.lt
+{
+ text-align: left;
+}
+.frt
+{
+ float: right;
+}
+.flt
+{
+ float: left;
+}
+.right
+{
+ padding-right: 10px;
+ float: right;
+}
+.left
+{
+ float: left;
+}
+.bld
+{
+ font-weight: bold;
+}
+.top
+{
+ vertical-align: top;
+}
+.black
+{
+ color: black;
+}
+span.sp, input.sp
+{
+ background: url(../../img/cbn/spritelib.gif) no-repeat;
+}
+span.sp span
+{
+ display: none;
+}
+span.trans
+{
+ display: block;
+ width: 24px;
+ height: 24px;
+}
+
+/*** STATUS MESSAGES ***/
+div#statusMessage
+{
+ display: none;
+ position: relative;
+}
+div.status
+{
+
+ color: black;
+ margin: 0 10mm 0 20mm;
+ height: 15px;
+ overflow: hidden;
+ vertical-align: middle;
+ background: #ffffa6 url(../../img/cbn/spritelib.gif) no-repeat 0 -1104px;
+}
+
+div.status div.center
+{
+ text-align: center;
+ margin-left: 36px;
+ margin-right: 36px;
+}
+
+div.status div.edge
+{
+ text-align: left;
+ width: 32px;
+ height: 15px;
+}
+
+div.status div.cap
+{
+ width: 1px;
+ height: 15px;
+ background: #ffffa6 url(../../img/cbn/spritelib.gif) no-repeat 0 -1104px;
+}
+
+a#statuslink
+{
+ color: black;
+ text-decoration: none;
+}
+a#statuslink:hover
+{
+ text-decoration: underline;
+}
+
+/*** STATUS MESSAGE ICONS ***/
+span.spsm
+{
+ display: block;
+ width: 12px;
+ height: 12px;
+ overflow: hidden;
+ margin: 1px 0 0 10px;
+}
+span.sm1
+{
+ background-position: 0 -1056px;
+}
+span.sm2
+{
+ background-position: 0 -1080px;
+}
+
+/*** MESSAGE PRIORITY STYLES ***/
+span.prioHigh
+{
+ font-weight: bold;
+ color: #DD0000;
+}
+span.prioNorm
+{
+ color: #008800;
+}
+span.prioLow
+{
+ color: #000088;
+}
+
+/*** SKIP TO MESSAGE LIST (for screen readers and mobility impaired) ***/
+#skip a, #skip a:visited
+{
+ position: absolute;
+ top: 0;
+ left: -999px;
+ padding: 0 5px 1px 5px;
+ margin: 2px 0 0 1px;
+}
+
+#ie6Fix:active, #skip a:active, #skip a:focus, #skip a:hover
+{
+ position: absolute;
+ top: 0;
+ left: 0;
+ font-family: Verdana, Arial, Helvetica, sans-serif;
+ font-size: 10pt;
+ color: #00FF00;
+ background-color: #000000;
+}
+
+/*** PAGE LAYOUT ***/
+table.page
+{
+ background-color: #FFFFFF;
+ width: 100%;
+ height: 100%;
+}
+table.page td.checkMailandCompose
+{
+ height: 100%;
+ vertical-align: top;
+}
+table.page td.spc
+{
+ background-color: #000000;
+ width: 5px;
+}
+table.page td.topHdr
+{
+ vertical-align: bottom;
+ background: #000000 url(../../img/cbn/btnbg.gif) repeat;
+}
+table.content
+{
+ width: 100%;
+ height: 100%;
+}
+div.contentBody
+{
+ width: 100%;
+}
+div.contentBodyHTML
+{
+ padding: 6px;
+}
+div.contentDeadSession
+{
+ margin-top: 1.75em;
+ text-align: center;
+ font-size: 1.75em;
+ font-weight: bold;
+}
+table.page img.logo
+{
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 182px;
+ height: 72px;
+ margin-bottom: 5px;
+}
+table.page span.logo
+{
+ position: absolute;
+ top: 50px;
+ left: 14px;
+ color: white;
+ font-size: .8em;
+}
+
+/*** NO SCRIPT WARNING ***/
+.noscript
+{
+ text-align: center;
+ font-size: .8em;
+ line-height: 2em;
+ background-color: #ffffa6;
+ border-left: 1px solid black;
+ border-right: 1px solid black;
+}
+
+
+/*** HEADER BUTTONS ***/
+div.hdrBtns
+{
+ float: right;
+}
+span.hdrBtnText
+{
+ display: block;
+ float: left;
+ cursor: pointer;
+}
+span.hdrBtnImg
+{
+ display: block;
+ float: left;
+ height: 16px;
+ width: 16px;
+ margin: 0 6px 0 14px;
+ cursor: pointer;
+}
+span.hbi1 { background-position: 0 -768px; }
+span.hbi2 { background-position: 0 -792px; }
+span.hbi3 { background-position: 0 -816px; }
+span.hbi4 { background-position: 0 -840px; }
+
+span.newsImg
+{
+ display: block;
+ float: left;
+ height: 16px;
+ width: 16px;
+ margin-top: 3px;
+ margin-right: 6px;
+ cursor: pointer;
+ background-position: 0 -1800px;
+}
+
+/*** SEARCH ***/
+.searchFormDiv
+{
+ padding: 2mm 2mm 0 2mm;
+ margin-top: 0;
+ margin-bottom: 0;
+}
+#searchForm
+{
+ margin-bottom: 0;
+ margin-top: 0;
+}
+#searchField
+{
+ width: 80%;
+}
+input.searchBtn
+{
+ width: 19px;
+ height: 19px;
+ margin-left: 2mm;
+ border: none;
+ background-position: 0 -1152px;
+}
+#searchAdvance
+{
+ float: left;
+ padding-top: 1mm;
+ padding-left: 2mm;
+}
+#searchClear
+{
+ float: right;
+ width: 2em;
+ padding-top: 1mm;
+ padding-right: 4mm;
+}
+#searchRefine
+{
+ clear: both;
+ padding-top: 1.5mm;
+ text-align: left;
+}
+#searchResult
+{
+ clear: both;
+ padding-left: 10px;
+ padding-top: 3px;
+}
+
+span.searchMatch
+{
+ background-color: yellow;
+ font-size: larger;
+}
+
+/*** TOOLBAR ***/
+div.toolBar
+{
+ border: 1px solid yellow;
+ height: 3em;
+ background: #000000 url(../../img/cbn/btnbg.gif) repeat;
+ color: #ffffff;
+ float: left;
+ padding: 0 0 1px 0;
+}
+span.toolBarText
+{
+ display: block;
+ float: left;
+ margin: .75em 4px 0 4px;
+}
+span.toolBarImg
+{
+ display: inline-block;
+ height: 24px;
+ width: 24px;
+}
+span.toolBarBtn
+{
+ float: left;
+ margin-top: .4em;
+ margin-left: .4em;
+}
+.toolBarBtnLeft
+{
+ margin-left: 1mm;
+}
+.toolBarBtnRight
+{
+ position: absolute;
+ right: 0;
+}
+span.toolBarNav
+{
+ float: right;
+ cursor: pointer;
+ margin-top: .4em;
+}
+span.toolBarSep
+{
+ display: block;
+ height: 24px;
+ margin: 4px 2px 0 2px;
+}
+span.toolBarSepBtn
+{
+ float: left;
+}
+span.toolBarSepNav
+{
+ float: right;
+}
+div.toolBar a
+{
+ height: 3em;
+ display: block;
+ float: left;
+ color: #FFFFFF;
+}
+div.toolBar a:hover
+{
+ cursor: pointer;
+ text-decoration: none;
+ background: #000000 url(../../img/cbn/btnhi.gif) repeat;
+ color: #FFFFFF;
+}
+div.toolBarMenu
+{
+ float: left;
+}
+
+span.tbi1 { background-position: 0 -120px; }
+span.tbi2 { background-position: 0 -312px; }
+span.tbi3 { background-position: 0 -336px; }
+span.tbi4 { background-position: 0 -864px; }
+span.tbi5 { background-position: 0 -888px; }
+
+
+table.toolbarTbl
+{
+ background: #000000 url(../../img/cbn/btnbg.gif) repeat;
+ border-left: 1px solid #777777;
+ border-top: 1px solid #777777;
+ height: 38px;
+ width: 100%;
+}
+table.toolbarTbl td
+{
+ border-bottom: 1px solid #777777;
+}
+table.toolbarTbl a
+{
+ color: #FFFFFF;
+ margin: 0 0;
+}
+table.toolbarTbl a:hover
+{
+ text-decoration: none;
+ background: #000000 url(../../img/cbn/btnhi.gif) repeat;
+ color: #FFFFFF;
+}
+table.toolbarTbl td.tbPad
+{
+ border-right: 1px solid #777777;
+ padding: 0;
+ vertical-align: top;
+}
+table.toolbarTbl a
+{
+ display: block;
+ padding: 3px 8px 4px 0;
+ text-decoration: none;
+}
+table.toolbarTbl a img
+{
+ vertical-align: middle;
+}
+table.toolbarTbl td.pageText
+{
+ color: #ffffff;
+ padding-left: 5px;
+}
+table.toolbarTbl td.pageBtns
+{
+ color: #ffffff;
+}
+table.toolbarTbl td.pageBtns a, table.toolbarTbl td.pageBtns a img
+{
+ padding: 0;
+ margin: 0;
+}
+table.toolbarTbl td.pageBtns a:hover
+{
+ background: inherit;
+}
+
+
+/**** Header Context ****/
+div.wapContext {
+ height: 5em;
+}
+
+/**** Menu Bars ****/
+div.wapMenuLeft
+{
+ float: left;
+ width: 14em; /* sidebar width */
+}
+
+div.wapMenuRight
+{
+ height: 100%;
+ margin-left: 14em;
+ border-left: 4px solid black;
+}
+
+/*** LEFT COLUMN ***/
+table.leftColumn
+{
+ clear: left;
+ width: 100%;
+}
+
+/*** Sidebar ***/
+div.wapSidebar
+{
+ float: left;
+ width: 14em; /* sidebar width */
+}
+
+/*** FOLDER PANE ***/
+div.folderPane
+{
+ margin: 5px 0;
+ padding: 10px 0;
+ border-top: thin groove;
+ border-bottom: thin groove;
+ overflow: hidden;
+}
+div.folderList div
+{
+ clear: both;
+}
+div.fld
+{
+ height: 20px;
+ line-height: 18px;
+ padding-left: 10px;
+ margin-top: 3px;
+}
+
+div.ftitle
+{
+ padding: 0 0 4px 12px;
+ font-weight: bold;
+}
+div.fld span
+{
+ color: #000000;
+}
+div.fld span.unrd
+{
+ color: #707070;
+}
+div.sel span.unrd, div.sel span, div.sel span a
+{
+ color: #ffffff;
+}
+div.sel
+{
+ color: #ffffff;
+ background: #dae7fc url(../../img/cbn/spritelib.gif) repeat-x 0 -912px;
+}
+div.sel a, div.sel a span, div.sel a:hover
+{
+ color: #ffffff;
+}
+div.folderList span.left /* requires .left class */
+{
+ text-align: left;
+}
+div.folderList span.right /* requires .right class */
+{
+ font-weight: normal;
+}
+div.folderList span.right a /* requires .right class */
+{
+ color: #003399;
+}
+div.folderList div.lnk a
+{
+ color: #003399;
+}
+
+/*** SIMPLE LIST ***/
+table.listTbl
+{
+ width: 100%;
+}
+table.listTbl td, table.listTbl th
+{
+ padding: 0 3px;
+}
+table.listTbl td img, table.listTbl th img
+{
+ vertical-align: middle;
+ width: 17px;
+ height: 17px;
+}
+table.listTbl tr.unread, h1.unread
+{
+ font-weight: bold;
+}
+table.listTbl tr.ac, table.listTbl tr.ac a
+{
+ background-color: #f4f6f8;
+}
+table.listTbl tr#sd, table.listTbl tr#sd a, tr.choice
+{
+ color: #ffffff;
+ background-color: #417bd9;
+}
+table.listTbl tbody
+{
+ overflow: hidden;
+}
+table.listTbl th.colHdr
+{
+ border-top: 0;
+ height: 17px;
+ background: #f3f3f3 url(../../img/cbn/spritelib.gif) repeat-x 0 -936px;
+}
+table.listTbl th.selColHdr a
+{
+ color: #000000;
+}
+table.listTbl th.colHdr a
+{
+ color: #000000;
+}
+table.listTbl th a:hover
+{
+ text-decoration: none;
+}
+table.listTbl th.selColHdr
+{
+ border-top: 0px;
+ height: 17px;
+ background: #d4d4d4 url(../../img/cbn/spritelib.gif) repeat-x 0 -960px;
+}
+table.listTbl th.selColHdr img.selectedDn
+{
+ width: 8px;
+ height: 8px;
+ vertical-align: middle;
+}
+table.foldermgmt td, table.foldermgmt th
+{
+ border-bottom: 1px solid #eeeeee;
+}
+table.foldermgmt a
+{
+ font-weight: bold;
+}
+table.foldermgmt td.parent a
+{
+ font-weight: normal;
+}
+table.foldermgmt img
+{
+ padding-right: 5px;
+}
+table.divider td, table.divider th
+{
+ border-bottom: 1px solid #eeeeee;
+}
+table.listTbl td.nodivider
+{
+ border-bottom: none;
+}
+
+/*** FOOTER ***/
+table.page td.footer
+{
+ color: #FFFFFF;
+ background-color: #000000;
+ height: 30px;
+}
+table.page td.footer img
+{
+ margin-right: 6px;
+ vertical-align: bottom;
+}
+table.page td.footer a
+{
+ color: white;
+}
+
+/*** MESSAGE VIEW ***/
+
+table.msgHead
+{
+ background-color: #f9f9f9;
+ border-left: 1px solid #aaaaaa;
+ border-right: 1px solid #aaaaaa;
+ border-bottom: 1px solid #aaaaaa;
+ width: 100%;
+}
+table.msgHead td.subText
+{
+ padding: 5px 0 0 6px;
+ font-size: 1.1em;
+ font-weight: normal;
+}
+table.msgHead span.right
+{
+ font-weight: normal;
+}
+table.msgHead span.right a
+{
+ color: #003399;
+}
+table.msgHead td.hdrLabel
+{
+ padding: 4px 0 0 6px;
+ /* breaks on IE if width is set to 90px or less */
+ width: 100px;
+ vertical-align: top;
+ font-weight: bold;
+}
+table.msgHead td.hdrText
+{
+ width: 100%;
+ padding: 4px 0 0 4px;
+ text-align: left;
+ white-space: normal;
+}
+table.msgHead td.hdrText span.return img
+{
+ vertical-align: top;
+}
+table.msgHead td.hdrText span.return a
+{
+ font-weight: bold;
+}
+table.msgHead td.hdrText span.contactAddr
+{
+ float: left;
+ padding-right: 5px;
+}
+
+td.contentBody
+{
+ white-space: normal;
+ padding: 10px 0 0 6px;
+}
+table.msgHead a.addContact
+{
+ color: #003399;
+ padding-left: 10px;
+ vertical-align: text-top;
+}
+table.msgHead a.addContact img
+{
+ vertical-align: text-top;
+}
+table.msgHead span.emailAddr, table.msgHead span.attach
+{
+ white-space: nowrap;
+}
+table.msgHead span.attach img
+{
+ vertical-align: middle;
+}
+table.msgHead span.attach a
+{
+ text-decoration: underline;
+ float: left;
+ padding-right: 10px;
+}
+td.msgRawHead
+{
+ font-family: courier, monospace;
+}
+
+
+
+/*** COMPOSE ***/
+table.compose
+{
+ padding: 0px 10px;
+ background-color: #f9f9f9;
+ border-left: 1px solid #aaaaaa;
+ border-right: 1px solid #aaaaaa;
+ width: 100%;
+}
+table.compose td.spc
+{
+ background-color: #f9f9f9;
+ height: 6px;
+ width: 100%;
+}
+table.compose td.spc *
+{
+ vertical-align: middle;
+}
+table.compose td.lbl
+{
+ padding-right: 5px;
+ vertical-align: top;
+ width: 1%;
+}
+table.compose td.lbl input
+{
+ width: 40px;
+}
+table.compose td.lbl label
+{
+ padding-left: 2px;
+}
+table.compose td.lbl *
+{
+ vertical-align: top;
+}
+table.compose td.mid
+{
+ width: 100%;
+ padding: 0px;
+}
+table.compose td.mid *
+{
+ vertical-align: top;
+}
+table.compose td.mid textarea, table.compose td.mid input
+{
+ border: 1px solid #aaaaaa;
+ margin: 0;
+ padding: 1px 3px 1px 3px;
+ xheight: 1.7em;
+ width: 100%;
+ overflow-y: auto;
+ word-wrap: break-word;
+}
+textarea.mid, input.mid
+{
+ height: 1.7em;
+}
+table.compose td.rgt
+{
+ padding-left: 5px;
+ vertical-align: top;
+ width: 1%;
+}
+table.compose td.rgt *
+{
+ vertical-align: top;
+}
+table.compose td.attach *
+{
+ vertical-align: middle;
+}
+table.compose img.attachment
+{
+ width: 17px;
+ height: 17px;
+}
+table.compose img.rmAttach
+{
+ vertical-align: top;
+ width: 15px;
+ height: 15px;
+}
+table.composeBody
+{
+ background-color: #f9f9f9;
+ border-left: 1px solid #aaaaaa;
+ border-right: 1px solid #aaaaaa;
+ width: 100%;
+ height: 100%;
+}
+table.composeBody td.textBody
+{
+ height: 100%;
+ padding: 10px 6px 0 6px;
+ vertical-align: top;
+}
+table.composeBody td.textBody textarea
+{
+ border: 1px solid #aaaaaa;
+ width: 100%;
+ height: 98%;
+ padding: 4px 6px;
+ word-wrap: break-word;
+}
+span#lessComposeHeaderText
+{
+ display: none;
+}
+div#moreComposeHeaders
+{
+ display: none;
+}
+div#composeAttachments
+{
+ display: none;
+}
+span.attachmentName
+{
+ white-space: nowrap;
+ padding-right: .25em;
+}
+
+/*** GENERAL FIELDS (e.g. settings, advance search, contact details, etc.) ***/
+table.fields
+{
+ font-size: .7em;
+ width: 95%;
+ margin: 10px 20px;
+}
+table.fields td.title
+{
+ width: 20%;
+ font-weight: bold;
+ vertical-align: top;
+ color: #555555;
+}
+table.fields td.body
+{
+ vertical-align: top;
+ width: 80%;
+}
+table.fields td.body *
+{
+ vertical-align: middle;
+}
+table.fields td.title, table.fields td.body
+{
+ padding: 8px 0;
+ border-bottom: 1px solid #CCCCCC;
+}
+table.fields td.nodivider
+{
+ border-bottom: none;
+}
+table.fields td span.label
+{
+ font-weight: bold;
+}
+table.fields td a
+{
+ font-weight: normal;
+}
+table.fields td *
+{
+ vertical-align: bottom;
+}
+table.fields span.tips
+{
+ color: #000000;
+ font-weight: normal;
+}
+
+
+/*** SETTINGS (requires "fields" class) ***/
+table.settings td.title, table.settings td.body
+{
+ padding: 12px 12px 12px 0;
+}
+
+div#advancedSettings
+{
+ display: none;
+}
+
+/*** CONTACTS (requires "fields" class) ***/
+table.contacts *.contactText
+{
+ width: 90%;
+}
+
+/*** CONTACTS EDITOR ***/
+form.contactEditor
+{
+ color: #555555;
+ font-size: .7em;
+ padding: 0 12px;
+}
+form.contactEditor div.contactSection
+{
+ overflow: auto;
+ padding: 8px 0;
+ border-bottom: 1px solid silver;
+}
+form.contactEditor div.context
+{
+ font-size: 1.7em;
+ font-weight: bold;
+ padding-bottom: 8px;
+}
+form.contactEditor div.contactField
+{
+ float: left;
+ font-weight: bold;
+ width: 10em;
+}
+form.contactEditor input, form.contactEditor select
+{
+ width: 370px;
+}
+form.contactEditor textarea
+{
+ height: 6em;
+ width: 370px;
+}
+
+/*** ADVANCED SEARCH ***/
+form.advanceSearch
+{
+ color: #555555;
+ font-size: .7em;
+ padding: 0 12px;
+}
+form.advanceSearch div.searchSection
+{
+ overflow: auto;
+ padding: 8px 0;
+ border-bottom: 1px solid silver;
+}
+form.advanceSearch div.context
+{
+ font-size: 1.7em;
+ font-weight: bold;
+ padding-bottom: 8px;
+}
+form.advanceSearch div.scope
+{
+ text-align: center;
+ padding: 6px 0;
+}
+form.advanceSearch div div.searchField
+{
+ line-height: 2em;
+ font-weight: bold;
+ float: left;
+ width: 8em;
+}
+form.advanceSearch div div.searchType
+{
+ float: left;
+ padding-right: 10px;
+}
+form.advanceSearch div div.searchType select
+{
+ width: 12em;
+}
+form.advanceSearch div div.searchTerm
+{
+ height: 2em;
+ line-height: 2em;
+}
+form.advanceSearch div div.searchTerm input
+{
+ width: 18em;
+}
+
+/*** CONTACTS (requires "fields" class) ***/
+div.attachfiles
+{
+ margin: 20px 20px;
+}
+div.attachfiles td
+{
+ vertical-align: middle;
+ padding: 3px 0;
+}
+
+
+/*** MAIN HEADER (i.e. RSS news feed, weather bar, storage meter) ***/
+div.hdrContent
+{
+ overflow: hidden;
+}
+div.hdrContent, div.hdrContent a
+{
+ text-decoration: none;
+ color: #FFFFFF;
+}
+div.hdrContent a:hover
+{
+ text-decoration: underline;
+}
+div.hdrContent span.RSS
+{
+ line-height: 2em;
+ clear: both;
+}
+div.wbar
+{
+ height: 15px;
+ line-height: 15px;
+ padding-left: 5px;
+ padding-right: 5px;
+ background: #000000 url(../../img/cbn/spritelib.gif) repeat-x 0 -1128px;
+
+}
+div.wbar div.weather
+{
+ text-align: center;
+ float: left;
+ padding-left: 20em;
+}
+div.wbar div.usage
+{
+ float: right;
+ width: 18em;
+}
+div.hdrContent div.pageTitle
+{
+ width: 55%;
+ float: left;
+ clear: left;
+ text-align: left;
+ color: #FFFFFF;
+ font-size: 1.8em;
+ font-weight: normal;
+ padding: 4px 0 4px 5px;
+}
+div.hdrContent div.commands
+{
+ float: right;
+ margin: 8px 7px 0 0;
+}
+
+/*** INLINE MESSAGES ***/
+div#bannerConfirm
+{
+ font-weight: bold;
+ text-align: center;
+ padding: 3px 5px;
+ color: #000000;
+ background-color: #ffecda;
+ border-bottom: 1px solid #aaaaaa;
+}
+div#bannerConfirm img
+{
+ vertical-align: middle;
+}
+
+div#bannerError
+{
+ font-weight: bold;
+ text-align: center;
+ padding: 3px 5px;
+ color: #ffffff;
+ background-color: #ff0000;
+ border-bottom: 1px solid #aaaaaa;
+}
+div#bannerError img
+{
+ vertical-align: middle;
+}
+
+div.bannerPrivacy
+{
+ font-weight: bold;
+ line-height: 1.4em;
+ padding: 3px 5px;
+ background-color: #ffffd2;
+ border-bottom: 1px solid #aaaaaa;
+}
+div.bannerPrivacy a
+{
+ font-weight: normal;
+}
+div.bannerPrivacy img
+{
+ padding-right: 8px;
+ float: left;
+}
+
+
+div.bannerSearch
+{
+ text-align: center;
+ padding: 5px 60px 5px 5px;
+ background-color: #eeeeee;
+ border-bottom: 1px solid #aaaaaa;
+}
+div.bannerSearch *
+{
+ vertical-align: middle;
+}
+div.bannerSearch input.text
+{
+ width: 15em;
+}
+
+
+div#bannerSelection
+{
+ font-family: Verdana, Arial, Helvetica, sans-serif;
+ font-size: .7em;
+ display: none;
+ text-align: center;
+ padding: 4px 5px;
+ color: #000000;
+ border-bottom: 1px solid #aaaaaa;
+}
+
+
+table.bannerFolderSearch
+{
+ width: 100%;
+ padding: 5px 5px 5px 5px;
+ background-color: #e7f0ff;
+ border-bottom: 1px solid #aaaaaa;
+}
+table.bannerFolderSearch td.title
+{
+ text-align: left;
+}
+table.bannerFolderSearch td.body
+{
+ text-align: right;
+}
+table.bannerFolderSearch img
+{
+ margin-right: 5px;
+ vertical-align: middle;
+}
+table.bannerFolderSearch a
+{
+ color: #003399;
+ font-weight: bold;
+ text-decoration: underline;
+}
+
+
+/*** MOVE/COPY TO FOLDER UI ***/
+.moveToFolder *
+{
+ vertical-align: middle;
+}
+.moveToFolder input
+{
+ width: .9em;
+ height: .9em;
+}
+.moveToFolder input
+{
+ margin-top: 2px; /* fix for IE */
+}
+.moveToFolder label
+{
+ padding-right: 4px;
+ color: #FFFFFF;
+}
+.moveToFolder select
+{
+ position: relative;
+ bottom: 8px;
+}
+
+.attachFiles
+{
+ margin: 15px;
+}
+
+.attachFiles input[type="button"]
+{
+ margin-left: 5px;
+}
+
+div.attachInput
+{
+ margin: 2px 2px 2px 0;
+}
+div.attachInput form
+{
+ margin-bottom: 0;
+}
+
+div.drop
+{
+ background-color: #66ffff;
+}
+
+/*** Loading Notification ***/
+div#bePatient
+{
+ font-family: Verdana, Arial, Helvetica, sans-serif;
+ font-size: .6em;
+ font-weight: bold;
+ background-color: #ffffa6;
+ color: #000000;
+ padding: 4px 5px;
+ border-bottom: 2px solid black;
+ border-right: 2px solid black;
+ left: -999px;
+ top: -999px;
+ margin-left: -1px;
+ position: absolute;
+}
+
+/*** Normal Message Text ***/
+.messageText
+{
+ margin: 10px 0 0 6px;
+ font-family: monospace;
+ white-space: pre;
+}
+
+/*** Tool Tip ***/
+div#tipDrag {
+ border: 1px solid black;
+ padding: 1px 3px;
+ background-color: #fffacd;
+ font-size: 0.7em;
+}
+
+/*** Panel Framework ***/
+/*#panel_c.yui-panel-container.shadow .underlay
+{
+ left:3px;
+ right:-3px;
+ top:3px;
+ bottom:-3px;
+ position:absolute;
+ background-color:#000;
+ opacity:0.12;
+ filter:alpha(opacity=12);
+}*/
+
+.yui-panel
+{
+ position:relative;
+ border:none;
+ overflow:visible;
+ background:transparent url(../../img/cbn/border-rt.gif) repeat-y top right;
+}
+
+.yui-panel .container-close
+{
+ position:absolute;
+ top:5px;
+ right:10px;
+ height:14px;
+ width:14px;
+ background:url(../../img/cbn/spritelib.gif) no-repeat 0 -1224px;
+}
+
+.yui-panel .hd
+{
+ border:none;
+ background:url(../../img/cbn/spritelib.gif) repeat-x 0 -1248px;
+ color:#FFF;
+ height:24px;
+ margin: 0 4px;
+ text-align:left;
+ vertical-align:middle;
+ overflow:visible;
+}
+
+.yui-panel .bd
+{
+ overflow:hidden;
+ padding: 10px;
+ border:none;
+ background:#FFF url(../../img/cbn/border-lt.gif) repeat-y;
+ margin-right:4px;
+}
+
+.yui-panel .ft
+{
+ background:url(../../img/cbn/spritelib.gif) repeat-x 0 -1272px;
+ font-size:4px;
+ height:4px;
+ padding:0px 10px;
+ border:none
+}
+
+.yui-panel .hd span
+{
+ padding-left: 4px;
+ line-height:24px;
+ vertical-align:middle;
+ font-weight:bold;
+}
+
+.yui-panel .hd .tl
+{
+ width:4px;
+ height:24px;
+ top:0;
+ left:0;
+ background:url(../../img/cbn/spritelib.gif) no-repeat 0 -1296px;
+ position:absolute;
+}
+
+.yui-panel .hd .tr
+{
+ width:4px;
+ height:24px;
+ top:0;
+ right:0;
+ background:url(../../img/cbn/spritelib.gif) no-repeat 0 -1320px;
+ position:absolute;
+}
+
+.yui-panel .ft .bl
+{
+ width:4px;
+ height:4px;
+ bottom:0;
+ left:0;
+ background:url(../../img/cbn/spritelib.gif) no-repeat 0 -1344px;
+ position:absolute;
+}
+
+.yui-panel .ft .br
+{
+ width:4px;
+ height:4px;
+ bottom:0;
+ right:0;
+ background:url(../../img/cbn/spritelib.gif) no-repeat 0 -1348px;
+ position:absolute;
+}
+
+/*** Internal Panel Elements ***/
+div.dialogIcon
+{
+ width: 60px;
+ height:60px;
+ float: left;
+}
+
+div.alert
+{
+ background:url(../../img/cbn/alert.gif) no-repeat top center;
+}
+
+div.prompt
+{
+ background:url(../../img/cbn/question2.jpg) no-repeat top center;
+}
+
+div.dialog
+{
+ background:url(../../img/cbn/question2.jpg) no-repeat center top;
+}
+
+div#alertBody
+{
+ margin-left: 60px;
+ padding: 5px;
+}
+
+div#promptBody
+{
+ margin-left: 60px;
+ padding: 5px;
+}
+
+div#dialogBody
+{
+ margin-left: 60px;
+ width: 540px;
+ padding: 5px;
+ overflow: auto;
+}
+
+div.dialogButtons
+{
+ padding-top: 10px;
+ background-color: #ffffff;
+ text-align:right;
+}
+
+div.panelExplanation
+{
+ width: 80%;
+ text-align: center;
+ padding: 1em 0;
+}
+
+div.panelInput
+{
+ text-align: center;
+ overflow: auto;
+}
+
+div.panelInput input
+{
+ padding: 0 2px;
+ margin-left: 8px;
+}
+
+/*** Viewport Height Limiter ***/
+div#alpineContent
+{
+ height: 100%;
+ overflow: auto;
+}
+
+/*** Compose Checkpoint Result ***/
+#formResponse
+{
+ position: absolute;
+ top: 0;
+ left: -999px;
+}
+
+/*** Sprite based images ****/
+
+/* left column icons */
+span.splci
+{
+ display:block;
+ line-height: 24px;
+ padding-left: 30px;
+ margin-left: 4px;
+ cursor: pointer;
+}
+
+span.splcs
+{
+ display:block;
+ line-height: 20px;
+ padding-left: 30px;
+ margin-left: 4px;
+ cursor: pointer;
+}
+
+span.splc
+{
+ width: 17px;
+ height: 17px;
+ float: left;
+ margin-right: 8px;
+ margin-top: 1px;
+}
+
+span.splc1
+{
+ background-position: 0 -24px;
+}
+
+span.splc2
+{
+ background-position: 0 -48px;
+}
+
+span.splc3
+{
+ background-position: 0 -72px;
+}
+
+span.splc4
+{
+ background-position: 0 -96px;
+}
+
+span.splc5
+{
+ background-position: -4px -122px;
+}
+
+span.splc6
+{
+ background-position: -3 -146px;
+}
+
+span.splc7
+{
+ background-position: 0 -168px;
+}
+
+span.splc8
+{
+ background-position: 0 -192px;
+}
+
+span.splc9
+{
+ background-position: 0 -1632px;
+}
+
+span.splc10
+{
+ background-position: 0 -1656px;
+}
+span.splc11
+{
+ background-position: 0 -1680px;
+}
+span.splc12
+{
+ background-position: 0 -1776px;
+}
+
+/* menu bar icons */
+span.spmb
+{
+ display: block;
+ width: 24px;
+ height: 24px;
+ cursor: pointer;
+}
+
+span.spmbi
+{
+ display:block;
+ line-height: 28px;
+ padding-left: 28px;
+ margin-left: 4px;
+ cursor: pointer;
+}
+
+span.spmbf
+{
+ background-position: 0 -216px;
+}
+
+span.spmbp
+{
+ background-position: 0 -240px;
+}
+
+span.spmbn
+{
+ background-position: 0 -264px;
+}
+
+span.spmbl
+{
+ background-position: 0 -288px;
+}
+
+span.spmbu
+{
+ background-position: 0 -1008px;
+}
+
+span.spmbd
+{
+ background-position: 0 -1032px;
+}
+
+span.spmb1
+{
+ margin-left: 6px;
+ background-position: 0 -312px;
+}
+
+span.spmb2
+{
+ background-position: 0 -336px;
+}
+
+span.spmb3
+{
+ width: 1px;
+ background-position: 0 -360px;
+}
+span.spmb4
+{
+ background-position: 0 -1368px;
+}
+span.spmb5
+{
+ background-position: 0 -1392px;
+}
+span.spmb6
+{
+ background-position: 0 -1416px;
+}
+span.spmb7
+{
+ background-position: 0 -116px;
+}
+span.spmb7
+{
+ background-position: 0 -116px;
+}
+span.spmb8
+{
+ background-position: 0 -1438px;
+}
+span.spmb9
+{
+ background-position: 0 -1462px;
+}
+span.spmb10
+{
+ background-position: 0 -1486px;
+}
+span.spmb11
+{
+ background-position: 0 -570px;
+}
+span.spmb12
+{
+ background-position: 0 -1510px;
+}
+span.spmb13
+{
+ background-position: 0 -140px;
+}
+span.spmb14
+{
+ background-position: 0 -1172px;
+}
+span.spmb15
+{
+ background-position: 0 -310px;
+}
+span.spmb16
+{
+ background-position: 0 -334px;
+}
+span.spmb17
+{
+ background-position: 0 -884px;
+}
+span.spmb18
+{
+ background-position: 0 -1534px;
+}
+span.spmb19
+{
+ background-position: 0 -1556px;
+}
+span.spmb20
+{
+ background-position: 0 -1582px;
+}
+span.spmb21
+{
+ background-position: 0 -1752px;
+}
+
+span.rof
+{
+ display: block;
+ float: right;
+}
+
+/*** Priority Radio Button ***/
+
+/* radio button icons */
+span.sprb
+{
+ width: 20px;
+ height: 20px;
+ display: block;
+ float: left;
+ padding-right: .3em;
+ background-position: 0 -384px;
+}
+
+span.prbc
+{
+ background-position: 0 -408px;
+}
+
+/* message list icons */
+span.spml
+{
+ display: block;
+ width: 17px;
+ height: 17px;
+}
+
+span.blank, a.mlstat span.nostar
+{
+ background-position: 0 0;
+}
+
+span.spml1
+{
+ background-position: 0 -456px;
+}
+
+span.spml2
+{
+ background-position: 0 -480px;
+}
+
+span.spml3
+{
+ background-position: 0 -504px;
+}
+
+span.spml4
+{
+ background-position: 0 -528px;
+}
+
+span.spml5
+{
+ background-position: 0 -552px;
+}
+
+span.spml6
+{
+ background-position: 0 -576px;
+}
+
+span.spml7
+{
+ background-position: 0 -600px;
+}
+
+a.mlstat:hover
+{
+ background-position: 0 0;
+}
+
+span.spml8, a.mlstat:hover span.nostar, a.mlstat:hover span.prihi, a.mlstat:hover span.prihier
+{
+ background-position: 0 -624px;
+}
+
+span.spml9, span.star
+{
+ background-position: 0 -648px;
+}
+
+span.spml10, a.mlstat span.prihi
+{
+ background-position: 0 -672px;
+}
+
+span.spml11, a.mlstat span.prihier
+{
+ background-position: 0 -696px;
+}
+
+span.spml12
+{
+ background-position: 0 -720px;
+}
+
+span.spml13
+{
+ background-position: 0 -744px;
+}
+
+/* message view icons */
+span.spmv
+{
+ display: block;
+ width: 15px;
+ height: 15px;
+}
+span.spmv1
+{
+ float: left;
+ background-position: 0 -984px;
+}
+span.spmv2
+{
+ float: left;
+ margin: 0 5px 0 5px;
+ background-position: 0 -576px;
+}
+span.spmv3
+{
+ margin-left: 3px;
+ display: inline-block;
+ background-position: 0 -1704px;
+}
+
+/* folderlist icons */
+span.spfl
+{
+ display:block;
+ height: 18px;
+ width: 18px;
+ margin-left: 4px;
+}
+span.spfl1
+{
+ background-position: 0 -1608px;
+}
+
+ul.priority li
+{
+ width: 10em;
+}
+
+/*** Folder/Contacts List Context Banner ***/
+span.spfcl
+{
+ display: block;
+ width: 20px;
+ height: 20px;
+ float: left;
+ margin-right: 8px;
+ margin-top: 1px;
+ cursor: pointer;
+}
+
+span.spfcl1
+{
+ background-position: 0 -1176px;
+}
+
+span.spfcl2
+{
+ background-position: 0 -1200px;
+}
+
+span.spfcl3
+{
+ background-position: 0 -1728px;
+}
+
+ul.sortList li
+{
+ width: 13em;
+}
+
+div.getAuth div
+{
+ padding: 6px 30px 26px 30px;
+}
+
+div.getAuth span
+{
+ width:40%;
+ display: block;
+ float: left;
+ clear: left;
+ padding-right: 10px;
+ text-align: right;
+}
+
+div.getAuth hr
+{
+ color: #ffffff;
+ background-color: #ffffff;
+ border: 0 none;
+}
+
+div.getAuth input
+{
+ width:50%;
+ display: block;
+ clear: right;
+ width: 12em;
+ text-align: left;
+}
+
+/*** Message Drag Proxy ***/
+#msgDragProxy
+{
+ position: absolute;
+ visibility: hidden;
+ font-size: .7em;
+ top: -100px;
+ cursor: move;
+ filter:alpha(opacity=80);
+ -moz-opacity: 0.8;
+ opacity: 0.8;
+}
+#msgDragProxy div
+{
+ padding: 0 6px;
+ background-color: #d8d8d8;
+ white-space: nowrap;
+}
+.b1, .b2, .b3, .b4
+{
+ background-color: #d8d8d8;
+ font-size: 1px;
+ display:block;
+ overflow:hidden;
+}
+.b1
+{
+ height: 1px;
+ margin: 0 5px;
+}
+.b2
+{
+ height: 1px;
+ margin: 0 3px;
+}
+.b3
+{
+ height: 1px;
+ margin: 0 2px;
+}
+.b4
+{
+ height: 2px;
+ margin: 0 1px;
+}
+
+/*** Take Dialog ***/
+div.takeInstructions
+{
+ padding: 15px 5px 20px 5px;
+ text-align: center;
+}
+
+div.takeList
+{
+ font-size: .8em;
+ height: 14.5em;
+ border: 1px solid #aaaaaa;
+ overflow: auto;
+}
+
+
+/*** PRINTER FORMATTING ***/
+.printInfo
+{
+ display: none;
+}
+
+.fullHdrBtn
+{
+ vertical-align: top;
+ padding-right: 10px;
+ float: right;
+}
+
+@media print
+{
+ #leftColumn,
+ #toolBar,
+ .hdrContent,
+ #hdrLogo,
+ #ftrContent,
+ .toolbarTbl,
+ .fullHdrBtn,
+ .addContact,
+ .spc,
+ div.bannerPrivacy
+ {
+ display: none;
+ }
+ div.printInfo
+ {
+ background-color: grey;
+ border-bottom: 2px groove black;
+ display: block;
+ }
+ div#alpineContent
+ {
+ height: 100% !important;
+ overflow: visible;
+ }
+ table.msgHead
+ {
+ background-color: #f9f9f9;
+ border: 1px solid #aaaaaa;
+ width: 100%;
+ }
+}
diff --git a/web/cgi/alpine/2.0/css/help.css b/web/cgi/alpine/2.0/css/help.css
new file mode 100644
index 00000000..240e5d44
--- /dev/null
+++ b/web/cgi/alpine/2.0/css/help.css
@@ -0,0 +1,119 @@
+/* ========================================================================
+ * Copyright 2006-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+* {
+ font-family: Tahoma, Helvetica, sans-serif;
+ font-size: 1em;
+ text-align: justify;
+}
+tt {
+ font-family: Courier, "Courier New";
+}
+body {
+ background: #fff;
+ font-size: 0.7em;
+ margin: 0.9em;
+}
+#header {
+ height: 4.0em;
+ position: relative;
+}
+#header .logo {
+ background: url(../img/cbn/logo.gif) left center no-repeat;
+ height: 4.0em;
+ width: 230px;
+}
+#header .nav {
+ position: absolute;
+ right: .815em;
+ top: 4.537em;
+}
+#header .nav a {
+ color: #00f;
+ font-weight: bold;
+ text-decoration: none;
+}
+#header .nav a:hover {
+ color: #00f;
+ text-decoration: underline;
+}
+h2 {
+ background: #ccc;
+ border-color: #ddd #999 #999 #ddd;
+ border-style: solid;
+ border-width: 1px;
+ margin: 0 0 .9em 0;
+ padding: .181em .725em;
+}
+dl {
+ margin: 0;
+ padding: 0;
+}
+dt {
+ font-weight: bold;
+ margin: 0 0 0.3em 0;
+}
+dd {
+ border-bottom: dotted 1px #ccc;
+ margin: 0 0 .9em 0;
+ padding-bottom: .9em;
+}
+ul, ol {
+ margin: 0;
+ padding: 0;
+}
+ul.wide li, ol.wide li {
+ margin-top: 1em;
+}
+ul {
+ list-style: square;
+}
+ul li {
+ margin-left: 1.5em;
+}
+ol {
+ list-style: decimal;
+}
+ol li {
+ margin-left: 2.5em;
+}
+a {
+ color: #00f;
+ text-decoration: none;
+}
+a:hover {
+ color: #00f;
+ text-decoration: underline;
+}
+div.also, div.note {
+ border-style: solid;
+ border-width: 1px;
+ padding: .7em;
+}
+div.also {
+ background: #ddd;
+ border-color: #eee #aaa #aaa #eee;
+}
+div.note {
+ background: #ffa;
+ border-color: #eef #aaf #aaf #eef;
+}
+div.note h4, div.also h4 {
+ float: left;
+ margin: 0;
+}
+div.note div {
+ margin-left: 3em;
+}
+div.also ul {
+ margin-left: 5em;
+}
diff --git a/web/cgi/alpine/2.0/css/help_popup.css b/web/cgi/alpine/2.0/css/help_popup.css
new file mode 100644
index 00000000..11be8213
--- /dev/null
+++ b/web/cgi/alpine/2.0/css/help_popup.css
@@ -0,0 +1,114 @@
+/* ========================================================================
+ * Copyright 2006-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+* {
+ font-family: Tahoma, Helvetica, sans-serif;
+ font-size: 1em;
+ text-align: justify;
+}
+tt {
+ font-family: Courier, "Courier New";
+}
+body {
+ background: #fff;
+ font-size: 0.7em;
+ margin: 0.9em;
+}
+#header {
+ height: 0em;
+ position: relative;
+}
+#header .nav {
+ position: absolute;
+ right: .815em;
+ top: 0.3em;
+}
+#header .nav a {
+ color: #00f;
+ font-weight: bold;
+ text-decoration: none;
+}
+#header .nav a:hover {
+ color: #00f;
+ text-decoration: underline;
+}
+h2 {
+ background: #ccc;
+ border-color: #ddd #999 #999 #ddd;
+ border-style: solid;
+ border-width: 1px;
+ margin: 0 0 .9em 0;
+ padding: .181em .725em;
+}
+dl {
+ margin: 0;
+ padding: 0;
+}
+dt {
+ font-weight: bold;
+ margin: 0 0 0.3em 0;
+}
+dd {
+ border-bottom: dotted 1px #ccc;
+ margin: 0 0 .9em 0;
+ padding-bottom: .9em;
+}
+ul, ol {
+ margin: 0;
+ padding: 0;
+}
+ul.wide li, ol.wide li {
+ margin-top: 1em;
+}
+ul {
+ list-style: square;
+}
+ul li {
+ margin-left: 1.5em;
+}
+ol {
+ list-style: decimal;
+}
+ol li {
+ margin-left: 2.5em;
+}
+a {
+ color: #00f;
+ text-decoration: none;
+}
+a:hover {
+ color: #00f;
+ text-decoration: underline;
+}
+div.also, div.note {
+ border-style: solid;
+ border-width: 1px;
+ padding: .7em;
+}
+div.also {
+ background: #ddd;
+ border-color: #eee #aaa #aaa #eee;
+}
+div.note {
+ background: #ffa;
+ border-color: #eef #aaf #aaf #eef;
+}
+div.note h4, div.also h4 {
+ float: left;
+ margin: 0;
+}
+div.note div {
+ margin-left: 3em;
+}
+div.also ul {
+ margin-left: 5em;
+}
diff --git a/web/cgi/alpine/2.0/css/menu.css b/web/cgi/alpine/2.0/css/menu.css
new file mode 100644
index 00000000..ecf01ee6
--- /dev/null
+++ b/web/cgi/alpine/2.0/css/menu.css
@@ -0,0 +1,71 @@
+/* ========================================================================
+ * Copyright 2006-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+.menu a, .menu a:link, .menu a:visited {
+ white-space: nowrap;
+ color: #ffffff;
+ display: block;
+ margin: 0;
+ padding: 0 8px;
+ line-height: 34px;
+ text-decoration: none;
+}
+
+.menu a img
+{
+ border: none;
+ margin: 10px 0;
+}
+
+.menu a span
+{
+ margin-top: 10px;
+}
+
+.menu a:focus, .menu a:hover, .menu a:active {
+ background-color: #555555;
+ color: #ffffff;
+}
+
+.menu li {
+ list-style: none;
+}
+
+.menu li hr {
+ margin: 2px 0;
+}
+
+.menu li:focus ul, .menu li:hover ul, .menu li:active ul, .menu li.sfhover ul {
+ left: auto;
+ top: auto;
+ z-index: 99;
+}
+
+.menu ul {
+ background-color: #333333;
+ left: -999px;
+ top: -999px;
+ margin-left: -1px;
+ position: absolute;
+}
+
+.menu ul li {
+ border: 0 none;
+ clear: left;
+ margin-right: 0;
+}
+
+.menu, .menu ul {
+ color: #ffffff;
+ margin: 0;
+ padding: 0;
+}
diff --git a/web/cgi/alpine/2.0/css/print.css b/web/cgi/alpine/2.0/css/print.css
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/web/cgi/alpine/2.0/css/print.css
diff --git a/web/cgi/alpine/2.0/detach b/web/cgi/alpine/2.0/detach
new file mode 100755
index 00000000..780560cd
--- /dev/null
+++ b/web/cgi/alpine/2.0/detach
@@ -0,0 +1,172 @@
+#!./tclsh
+# $Id: detach 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006-2007 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# detach
+#
+# Purpose: CGI script to retrieve requested attachment
+#
+# Input: PATH_INFO: /<col_number>/<folder_name>/<uid_of__msg>/<part_of_msg>
+# along with possible search parameters:
+set detach_vars {
+ {download "" 0}
+}
+
+#set detach_via_ip_address 1
+#set detach_via_local_hostname 1
+
+# inherit global config
+source ./alpine.tcl
+
+proc WPServerIP {} {
+ global _wp
+
+ catch {
+ set ip 127.0.0.1
+ set sid [socket -async [info hostname] [expr {([string length $_wp(serverport)]) ? $_wp(serverport) : 80}]]
+ set ip [lindex [ fconfigure $sid -sockname ] 0]
+ close $sid
+ }
+
+ return $ip
+}
+
+WPEval $detach_vars {
+ # grok PATH_INFO for collection 'c' and folder 'f' uid 'u' and part 'p'
+ if {!([info exists env(PATH_INFO)] && [string length $env(PATH_INFO)]
+ && [regexp {^/([0-9]+)/(.*)/([0-9]+)/([\.0-9]+)$} $env(PATH_INFO) dummy c f u p])} {
+ WPCmd PEInfo statmsg "Invalid Detach: $env(SCRIPT_NAME)"
+ cgi_exit
+ }
+
+ # generate big random string to reference the thing
+
+ # generate filenames to hold detached data and control file
+ for {set n 0} {1} {incr n} {
+
+ set rhandle [WPCmd PESession random 64]
+ set cfile [file join $_wp(fileroot) $_wp(detachpath) detach.${rhandle}-control]
+ set dfile [file join $_wp(fileroot) $_wp(detachpath) detach.${rhandle}-data]
+
+ if {[file exists $cfile] == 0 && [file exists $dfile] == 0} {
+ if {[catch {open $cfile {RDWR CREAT EXCL} [cgi_tmpfile_permissions]} cfd]} {
+ error [list _action Detach "Cannot create control file: [cgi_quote_html $cfd]" "Please close this window"]
+ } else {
+ exec echo ${rhandle}-control | [file join $_wp(cgipath) $_wp(appdir) whackatch.tcl] >& /dev/null &
+ }
+
+ if {[catch {open $dfile {RDWR CREAT EXCL} [cgi_tmpfile_permissions]} dfd]} {
+ catch {close $cfd}
+ error [list _action Detach "Cannot create command file: [cgi_quote_html $dfd]" "Please close this window"]
+ } else {
+ exec echo ${rhandle}-data | [file join $_wp(cgipath) $_wp(appdir) whackatch.tcl] >& /dev/null &
+ }
+
+ # exec chmod [cgi_tmpfile_permissions] $cfile
+ # exec chmod [cgi_tmpfile_permissions] $dfile
+ break
+ } elseif {$n > 4} {
+ error [list _action Detach "Command file creation limit" "Please close this window"]
+ }
+ }
+
+ if {[catch {WPCmd PEMessage $u detach $p $dfile} attachdata]} {
+ error [list _action Detach $attachdata "Please close this window"]
+ }
+
+ if {[info exists detach_via_ip_address]} {
+ if {[regsub {^(http[s]?://)[A-Za-z0-9\\-\\.]+(.*)$} "[cgi_root]/pub/getach.tcl" "\\1\[[WPServerIP]\]\\2" redirect] != 1} {
+ error [list _action Detach "Cannot determine server address" "Please close this window"]
+ }
+ } elseif {[info exists detach_via_local_hostname]} {
+ if {[regsub {^(http[s]?://)[A-Za-z0-9\\-\\.]+(.*)$} "[cgi_root]/pub/getach.tcl" "\\1\[[info hostname]\]\\2" redirect] != 1} {
+ error [list _action Detach "Cannot determine server address" "Please close this window"]
+ }
+ } else {
+ set redirect "[cgi_root]/pub/getach.tcl"
+ }
+
+ set mimetype [lindex $attachdata 0]
+ set mimesubtype [lindex $attachdata 1]
+ set contentlength [lindex $attachdata 2]
+ set givenname [lindex [lindex $attachdata 3] 0]
+ set tmpfile [lindex $attachdata 4]
+
+ if {[string compare $tmpfile $dfile]} {
+ set straytmp "&straytmp=1"
+ } else {
+ set straytmp ""
+ }
+
+ if {![string length $givenname]} {
+ set givenname "attachment"
+ switch -regexp $mimetype {
+ ^[Tt][Ee][Xx][Tt]$ {
+ switch -regexp $mimesubtype {
+ ^[Pp][Ll][Aa][Ii][Nn]$ {
+ set givenname "attached.txt"
+ }
+ ^[Hh][Tt][Mm][Ll]$ {
+ set givenname "attached.html"
+ }
+ }
+ }
+ }
+ }
+
+ set safegivenname $givenname
+ regsub -all {[/]} $safegivenname {-} safegivenname
+ regsub -all {[ ]} $safegivenname {_} safegivenname
+ regsub -all {[\?]} $safegivenname {X} safegivenname
+ regsub -all {[&]} $safegivenname {X} safegivenname
+ regsub -all {[#]} $safegivenname {X} safegivenname
+ regsub -all {[=]} $safegivenname {X} safegivenname
+ set safegivenname "/[WPPercentQuote $safegivenname {.}]"
+
+ if {$download == 1} {
+ puts $cfd "Content-type: Application/X-Download"
+ puts $cfd "Content-Disposition: attachment; filename=\"$givenname\""
+ } else {
+ puts $cfd "Content-type: ${mimetype}/${mimesubtype}"
+ }
+
+ # side-step the cgi_xxx stuff in this special case because
+ # we don't want to buffer up the downloading attachment...
+
+ puts $cfd "Content-Length: $contentlength"
+ puts $cfd "Expires: [clock format [expr {[clock seconds] + 3600}] -f {%a, %d %b %Y %H:%M:%S GMT} -gmt true]"
+ puts $cfd "Cache-Control: max-age=3600"
+ puts $cfd ""
+
+ puts $cfd $tmpfile
+
+ # exec chmod [cgi_tmpfile_permissions] $tmpfile
+
+ close $cfd
+
+ # prepare to clean up if the brower never redirects
+
+ cgi_http_head {
+ # redirect to the place we stuffed the detach info. use the ip address
+ # to foil spilling any session cookies or the like
+ #cgi_redirect ${redirect}${safegivenname}?h=${rhandle}
+
+ if {[info exists env(SERVER_PROTOCOL)] && [regexp {[Hh][Tt][Tt][PP]/([0-9]+)\.([0-9]+)} $env(SERVER_PROTOCOL) m vmaj vmin] && $vmaj >= 1 && $vmin >= 1} {
+ cgi_puts "Status: 303 Temporary Redirect"
+ } else {
+ cgi_puts "Status: 302 Redirected"
+ }
+
+ cgi_puts "URI: ${redirect}${safegivenname}?h=${rhandle}${straytmp}"
+ cgi_puts "Location: ${redirect}${safegivenname}?h=${rhandle}${straytmp}"
+ }
+}
diff --git a/web/cgi/alpine/2.0/foldercache.tcl b/web/cgi/alpine/2.0/foldercache.tcl
new file mode 100644
index 00000000..a491d5d5
--- /dev/null
+++ b/web/cgi/alpine/2.0/foldercache.tcl
@@ -0,0 +1,177 @@
+# Web Alpine folder cache routines
+# $Id$
+# ========================================================================
+# Copyright 2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+
+
+
+proc saveDefault {{uid 0}} {
+ # "size" rather than "number" to work around temporary alpined bug
+ if {$uid == 0
+ || [catch {WPCmd PEMessage $uid size} n]
+ || $n == 0
+ || [catch {WPCmd PEMessage $uid savedefault} savedefault]} {
+ if {[WPCmd PEFolder isincoming 0]} {
+ set colid 1
+ } else {
+ set colid 0
+ }
+
+ return [list $colid [lindex [WPCmd PEConfig varget default-saved-msg-folder] 0]]
+ }
+
+ return $savedefault
+}
+
+# add given folder name to the cache of saved-to folders
+proc addSaveCache {f_name} {
+ global _wp
+
+ if {[catch {WPSessionState save_cache} flist] == 0} {
+ if {[set i [lsearch -exact $flist $f_name]] < 0} {
+ set flist [lrange $flist 0 [expr {$_wp(save_cache_max) - 2}]]
+ } else {
+ set flist [lreplace $flist $i $i]
+ }
+
+ set flist [linsert $flist 0 $f_name]
+ } else {
+ set flist [list $f_name]
+ }
+
+ catch {WPSessionState save_cache $flist}
+}
+
+# return the list of cached saved-to folders and make sure given
+# default is somewhere in the list
+proc getSaveCache {{def_name ""}} {
+
+ if {![string length $def_name]} {
+ set savedef [saveDefault 0]
+ set def_name [lindex $savedef 1]
+ }
+
+ set seen ""
+
+ if {[catch {WPSessionState save_cache} flist] == 0} {
+ foreach f $flist {
+ if {[string compare $def_name $f] == 0} {
+ set def_listed 1
+ }
+
+ if {[string length $f] && [lsearch -exact $seen $f] < 0} {
+ lappend options $f
+ lappend options $f
+ lappend seen $f
+ }
+ }
+ }
+
+ if {!([info exists options] && [info exists def_listed])} {
+ lappend options $def_name
+ lappend options $def_name
+ }
+
+ if {[catch {WPCmd set wp_cache_folder} wp_cache_folder]
+ || [string compare $wp_cache_folder [WPCmd PEMailbox mailboxname]]} {
+ # move default to top on new folder
+ switch -- [set x [lsearch -exact $options $def_name]] {
+ 0 { }
+ default {
+ if {$x > 0} {
+ set options [lreplace $options $x [expr {$x + 1}]]
+ }
+
+ set options [linsert $options 0 $def_name]
+ set options [linsert $options 0 $def_name]
+ }
+ }
+
+ catch {WPCmd set wp_cache_folder [WPCmd PEMailbox mailboxname]}
+ }
+
+ return $options
+}
+
+# add given folder name to the visited folder cache
+proc addFolderCache {f_col f_name} {
+ global _wp
+
+ if {$f_col != 0 || [string compare [string tolower $f_name] inbox]} {
+ if {0 == [catch {WPSessionState folder_cache} flist]} {
+
+ if {[catch {WPSessionState left_column_folders} fln]} {
+ set fln $_wp(fldr_cache_def)
+ }
+
+ for {set i 0} {$i < [llength $flist]} {incr i} {
+ set f [lindex $flist $i]
+ if {$f_col == [lindex $f 0] && 0 == [string compare [lindex $f 1] $f_name]} {
+ break
+ }
+ }
+
+ if {$i >= [llength $flist]} {
+ set flist [lrange $flist 0 $fln]
+ } else {
+ set flist [lreplace $flist $i $i]
+ }
+
+ set flist [linsert $flist 0 [list $f_col $f_name]]
+ # let users of data know it's changed (cheaper than hash)
+ WPScriptVersion common 1
+ } else {
+ catch {unset flist}
+ lappend flist [list $f_col $f_name] [list [saveDefault 0]]
+ # ditto
+ WPScriptVersion common 1
+ }
+
+ catch {WPSessionState folder_cache $flist}
+ }
+}
+
+proc removeFolderCache {f_col f_name} {
+ global _wp
+
+ if {$f_col != 0 || [string compare [string tolower $f_name] inbox]} {
+ if {0 == [catch {WPSessionState folder_cache} flist]} {
+
+ if {[catch {WPSessionState left_column_folders} fln]} {
+ set fln $_wp(fldr_cache_def)
+ }
+
+ for {set i 0} {$i < [llength $flist]} {incr i} {
+ set f [lindex $flist $i]
+ if {$f_col == [lindex $f 0] && 0 == [string compare [lindex $f 1] $f_name]} {
+ set flist [lreplace $flist $i $i]
+ WPScriptVersion common 1
+ catch {WPSessionState folder_cache $flist}
+ return
+ }
+ }
+ }
+ }
+}
+
+# return the list of cached visited folders and make sure given
+# default is somewhere in the list
+proc getFolderCache {} {
+ if {[catch {WPSessionState folder_cache} flist]} {
+ catch {unset flist}
+ lappend flist [saveDefault 0]
+ catch {WPSessionState folder_cache $flist}
+ }
+
+ return $flist
+}
+
diff --git a/web/cgi/alpine/2.0/folders b/web/cgi/alpine/2.0/folders
new file mode 100755
index 00000000..e98197ff
--- /dev/null
+++ b/web/cgi/alpine/2.0/folders
@@ -0,0 +1,240 @@
+#!./tclsh
+# $Id: folders 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# view.tcl
+#
+# Purpose: CGI script folders for Web Alpine 2.0 pages
+#
+# Input: PATH_INFO: [/<col_number>]/<folder_name>[/<uid_of_viewed_msg>
+# along with possible search parameters:
+set folders_args {
+}
+
+# inherit global config
+source ./alpine.tcl
+source ./foldercache.tcl
+source ./common.tcl
+
+# TEST
+proc cgi_suffix {args} {
+ return ""
+}
+
+# WHILE TESTING/DEBUGGING
+proc noimp {s} {
+ return "onClick=return noImp('${s}');"
+}
+
+set dmsgs ""
+proc dm {s} {
+ global dmsgs
+
+ lappend dmsgs $s
+}
+
+# default location state
+set c -1
+set f ""
+
+# grok PATH_INFO for collection 'c' and folder 'f'
+if {[info exists env(PATH_INFO)] && [string length $env(PATH_INFO)]} {
+ # look for collection number, c
+ if {[regexp {^/([0-9]+)(/.*)} $env(PATH_INFO) dummy c r]} {
+ if {![string length $r]} {
+ set f $c
+ set c -1
+ }
+ } else {
+ set r $env(PATH_INFO)
+ }
+
+ # look for folder name (f) in remainder, r
+ if {[regexp {^/([^/]+)((/.*)|$)} $r dummy f r]} {
+ # look for uid of page's top message, u
+ if {[regexp {^/([0-9a-fA-F]+)} $r dummy u]} {
+ } else {
+ }
+ } else {
+ if {[string length $r]} {
+ # can't grok, reset context
+ set c -1
+ }
+ }
+}
+
+WPEval $folders_args {
+ # verify current collection/folder
+ if {[catch {WPCmd PEFolder current} curfold]} {
+ error [list _action browse "cannot determine default folder: $curfold"]
+ } else {
+ set curc [lindex $curfold 0]
+ set curf [lindex $curfold 1]
+ }
+
+ # "current" folder's context
+ if {$c < 0} {
+ set c $curc
+ }
+
+ # "current" folder
+ if {0 == [string length $f]} {
+ set f $curf
+ }
+
+ # open a different folder?
+ if {$c != $curc || [string compare $f $curf]} {
+ # BUG: validate $u?
+
+ error [list _action browse "NOT PREPARED TO OPEN NEW FOLDER $f YET"]
+ if {[catch {eval WPCmd PEMailbox open [list $c $f]} reason]} {
+ } else {
+ # STATUS: Opened Folder $f
+ }
+ }
+
+ set defc [WPCmd PEFolder defaultcollection]
+ set cols [WPCmd PEFolder collections]
+
+ set charset "UTF-8"
+ set u 0
+
+ cgi_http_head {
+ WPStdHttpHdrs "text/html; charset=$charset"
+ }
+
+ cgi_html {
+ cgi_head {
+ cgi_content_type "text/html; charset=$charset"
+ cgi_title [wpPageTitle "Folder Management"]
+ cgi_base "href=$_wp(serverpath)/$_wp(appdir)/$_wp(ui2dir)/"
+ cgi_stylesheet css/menu.css
+ cgi_stylesheet css/cbn/screen.css
+ cgi_stylesheet css/cbn/folders.css
+ # Yahoo Styles
+ cgi_stylesheet $_wp(yui)/build/container/assets/container-core.css
+ cgi_stylesheet $_wp(yui)/build/menu/assets/skins/sam/menu.css
+ cgi_stylesheet $_wp(yui)/build/button/assets/skins/sam/button.css
+ # YahooUI libraries
+ cgi_script type=text/javascript language="JavaScript" src="$_wp(yui)/build/utilities/utilities.js" {}
+ cgi_script type=text/javascript language="JavaScript" src="$_wp(yui)/build/container/container-min.js" {}
+ cgi_script type=text/javascript language="JavaScript" src="$_wp(yui)/build/datasource/datasource-min.js" {}
+ cgi_script type=text/javascript language="JavaScript" src="$_wp(yui)/build/menu/menu-min.js" {}
+ cgi_script type=text/javascript language="JavaScript" src="$_wp(yui)/build/button/button-min.js" {}
+ # local libraries
+ cgi_script language="JavaScript" src="lib/common.js" {}
+ cgi_script language="JavaScript" src="lib/folders.js" {}
+ cgi_javascript {
+ cgi_puts "YAHOO.alpine.cgi_root = '$_wp(serverpath)';"
+ cgi_puts "YAHOO.alpine.current.incoming = [WPCmd PEFolder isincoming $c];"
+ cgi_puts "YAHOO.alpine.current.c = $c;"
+ cgi_puts "YAHOO.alpine.current.f = \"$f\";"
+ foreach col $cols {
+ regsub {'} [lindex $col 1] {\'} colname
+ lappend colnames "'$colname'"
+ }
+ cgi_puts "YAHOO.alpine.current.u = $u;"
+ cgi_puts "YAHOO.alpine.current.collections = \[[join $colnames {,}]\];"
+ cgi_puts "function bodyOnLoad() {"
+ cgi_puts " initMenus();"
+ cgi_puts " if(YAHOO.env.ua.gecko > 0){ sizeVPHeight(); window.onresize = resizeVPHeight; }"
+ cgi_puts " setCheckMailFunction('gCheck', newMailCheck);"
+ cgi_puts " setNewMailCheckInterval([WPCmd PEInfo inputtimeout]);"
+ cgi_puts " drawFolderList('alpineContent',$defc);"
+ cgi_puts "}"
+
+ # WHILE TESTING/DEBUGGING
+ cgi_puts "var impi = new Array(); function noImp(m){ var sm; if(impi\[m\]) {impi\[m\]++; if(impi\[m\] > 4) sm = '<b>Seriously,</b> ' + m + ' will <b>never</b> get implemented if you keep bothering me!'; else sm = m + ' is <b>still</b> not implemented!'; } else { sm = m + ' is not implemented yet!' ; impi\[m\] = 1 ;} showStatusMessage(sm,3); return false; }"
+
+ cgi_puts "browserDetect();"
+ }
+ }
+
+ cgi_body class=wap "onLoad=bodyOnLoad()" {
+ cgi_puts {<iframe name="formResponse" id="formResponse" src="img/cbn/spritelib.gif"></iframe>}
+ wpCommonPageLayout folders "$c" "$f" "$u" "View and Manage Folders" [list [cgi_cgi "$_wp(appdir)/$_wp(ui2dir)/browse/${c}/${f}"] "Folder List" 0 searchContent('folders','flistFolders')] "" {
+ # CONTEXT COMMANDS
+ cgi_division class=hdrBtns {
+ cgi_put [cgi_url "[cgi_span "class=sp hdrBtnImg hbi1" ""][cgi_span "class=hdrBtnText" Print]" "print" "onClick=return printContent()"]
+ cgi_put [cgi_url "[cgi_span "class=sp hdrBtnImg hbi2" ""][cgi_span "class=hdrBtnText" Settings]" "settings"]
+ cgi_put [cgi_url "[cgi_span "class=sp hdrBtnImg hbi3" ""][cgi_span "class=hdrBtnText" Help]" "javascript:openHelpWindow('folders.html');" class=wap]
+ cgi_put [cgi_url "[cgi_span "class=sp hdrBtnImg hbi4" ""][cgi_span "class=hdrBtnText" "Sign out"]" "../../session/logout.tcl?cid=[WPCmd PEInfo key]&sessid=${sessid}"]
+ }
+ } {
+ # TO MENUBAR
+ cgi_anchor_name "toolbar"
+ cgi_table class="toolbarTbl" cellpadding="0" cellspacing="0" {
+ cgi_puts "<tbody>"
+ cgi_table_row {
+ cgi_table_data {
+ cgi_table class="toolbarTbl" cellpadding="0" cellspacing="0" {
+ cgi_puts "<tbody>"
+ cgi_table_row {
+ cgi_table_data class=wap {
+ cgi_puts [cgi_url [cgi_span "class=sp spmbi spmb18" "New Folder"] "#" title="Create new folder in this folder" "onClick=return addFolder();"]
+ }
+ cgi_table_data class="dv1" {
+ cgi_puts [cgi_img "img/cbn/div.gif" class=wap]
+ }
+ cgi_table_data class=wap {
+ cgi_puts [cgi_url [cgi_span "class=sp spmbi spmb19" "View Messages"] "#" title="View Messages in selected folder" id=butChoose "onClick=return doOpen();"]
+ }
+ cgi_table_data class=wap {
+ cgi_puts [cgi_url [cgi_span "class=sp spmbi spmb7" "Delete"] "#" title="Delete selected folder" "onClick=return queryDelete();"]
+ }
+ cgi_table_data class=wap {
+ cgi_puts [cgi_url [cgi_span "class=sp spmbi spmb20" "Rename"] "#" title="Rename selected folder" "onClick=return renameFolder();"]
+ }
+ cgi_table_data class="dv1" {
+ cgi_puts [cgi_img "img/cbn/div.gif" class=wap]
+ }
+ cgi_table_data class=wap {
+ cgi_bullet_list class="menu" {
+ cgi_put "<li class=\"menuHdr\">[cgi_url "More Actions [cgi_img "img/cbn/menu.gif" class="wap menuDn menuImg"]" "#" "onClick=this.blur(); return false;"]<div>"
+ cgi_bullet_list {
+ cgi_li [cgi_url "Import Email into Folder" "#" "onClick=return importFolder();"]
+ cgi_li [cgi_url "Export Email from Folder" "#" "onClick=return exportFolder();"]
+ }
+ cgi_puts "</div></li>"
+ }
+ }
+ cgi_table_data width="100%" {
+ cgi_puts [cgi_nbspace]
+ }
+ }
+ cgi_puts "</tbody>"
+ }
+ }
+ }
+ cgi_puts "</tbody>"
+ }
+ } {
+ cgi_puts "Loading..."
+ } {
+ # BOTTOM MENUBAR
+ cgi_table class="wap toolbarTbl" cellpadding="0" cellspacing="0" {
+ cgi_puts "<tbody><tr><td>&nbsp;</td></tr></tbody>"
+ }
+ }
+
+ cgi_text "pickFolderCollection=$defc" type=hidden id=pickFolderCollection notab
+ cgi_text "pickFolderPath=" type=hidden id=pickFolderPath notab
+ cgi_text "pickFolderName=" type=hidden id=pickFolderName notab
+
+ # any debugging info to insert?
+ foreach dmsg $dmsgs {
+ cgi_html_comment "DEBUG: $dmsg"
+ cgi_puts ""
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/2.0/forward b/web/cgi/alpine/2.0/forward
new file mode 120000
index 00000000..872d4cb2
--- /dev/null
+++ b/web/cgi/alpine/2.0/forward
@@ -0,0 +1 @@
+compose \ No newline at end of file
diff --git a/web/cgi/alpine/2.0/help/alpha-index.html b/web/cgi/alpine/2.0/help/alpha-index.html
new file mode 100644
index 00000000..40380c77
--- /dev/null
+++ b/web/cgi/alpine/2.0/help/alpha-index.html
@@ -0,0 +1,225 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=iso-8859-1">
+<link type="text/css" rel="stylesheet" href="../css/help.css">
+<title>Alpine Help Index</title></head>
+<body>
+<div id="header">
+ <div class="logo"> </div>
+ <div class="nav"><a href="topic-list.html">All Help Topics</a> &nbsp;&nbsp;|&nbsp;&nbsp;<a href="javascript:history.back()">Go Back</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href="javascript:self.close();">Close</a></div>
+</div>
+
+<div id="content">
+
+<h2>Alpine Help Index</h2>
+
+<div class="noprint"><a href="#A">A</a> <a href="#B">B</a> <a href="#C">C</a>
+<a href="#D">D</a> <a href="#E">E</a> <a href="#F">F</a> <a href="#G">G</a> <a href="#H">H</a> <a href="#I">I</a> <a href="#L">L</a> <a href="#M">M</a> <a href="#N">N</a> <a href="#O">O</a> <a href="#P">P</a> <a href="#Q">Q</a> <a href="#R">R</a> <a href="#S">S</a> <a href="#T">T</a> <a href="#U">U</a> <a href="#W">W</a> <a href="#X">X</a> <a href="#Y">Y</a> <a href="#Z">Z</a></div>
+
+<p><strong><a name="A" id="A"></a>A</strong></p>
+<p>address book, see <a href="contacts.html">Contacts</a></p>
+<p><a href="settings.html#msgview">addresses as links</a> (Settings)</p>
+<p><a href="settings.html#personal">Alternate Addresses</a> (Settings)</p>
+<p><a href="settings.html#msgview">Anti-phishing option</a> (Settings)</p>
+<p><a href="settings.html#fcc">archive sent mail</a> (Settings)</p>
+<p>attachments</p>
+<ul>
+ <li><a href="settings.html#reply">including in replies</a> (Settings)</li>
+ <li><a href="compose.html#attachments">including with messages</a></li>
+ <li><a href="read.html#attachments">viewing and downloading</a></li>
+</ul>
+
+<p><strong><a name="B" id="B"></a>B</strong></p>
+
+<p><strong><a name="C" id="C"></a>C</strong></p>
+<p><a href="settings.html#compose">compose encoding</a> (Settings)</p>
+
+<p><a href="compose.html">Compose</a> (sending mail)</p>
+<ul>
+ <li><a href="compose.html#attachments">attachments</a></li>
+ <li><a href="compose.html#autosave">automatic saves</a></li>
+ <li><a href="settings.html#compose">Compose Headers</a> (Settings)</li>
+ <li><a href="compose.html#headers">headers</a></li>
+ <li><a href="compose.html#resuming">resuming drafts</a></li>
+ <li><a href="compose.html#sending">saving drafts</a></li>
+ <li><a href="compose.html#sending">sending</a></li>
+ <li><a href="compose.html#writing">writing messages</a></li>
+</ul>
+
+<p><a href="contacts.html">Contacts</a> (address book)</p>
+<ul>
+ <li><a href="contacts.html#add">adding</a></li>
+ <li><a href="contacts.html#edit">editing</a></li>
+ <li><a href="contacts.html#group">group contacts</a></li>
+ <li><a href="contacts.html#remove">removing</a></li>
+</ul>
+
+<p><a href="settings.html#compose">Custom Headers</a> (Settings)</p>
+<p><a href="settings.html">customizing Alpine</a></p>
+
+<p><strong><a name="D" id="D"></a>D</strong></p>
+<p><a href="inbox.html#delete">deleting messages</a></p>
+<p><a href="settings.html#dirservers">Directory Server</a> (Settings)</p>
+<p><a href="settings.html#display">Display</a> (Settings)</p>
+<p><a href="settings.html#msgview">Display Deaders</a> (Settings)</p>
+<p><a href="settings.html#personal">Display Name</a> (Settings)</p>
+<p><a href="settings.html#foldernames">Draft Folder</a> (Settings)</p>
+<p><a href="inbox.html#drag">drag and drop shortcuts</a></p>
+
+<p><strong><a name="E" id="E"></a>E</strong></p>
+<p><a href="settings.html#personal">Email Signature</a> (Settings)</p>
+<p><a href="quick-start.html#trash">Emptying trash</a> (how to)</p>
+<p><a href="settings.html#foldernames">Emptying Trash</a> (Settings)</p>
+<p><a href="settings.html#compose">Encoding of outgoing messages</a> (Settings)</p>
+
+<p><strong><a name="F" id="F"></a>F</strong></p>
+<p><a href="settings.html#fcc">Fcc (sent mail folder)</a> (Settings)</p>
+<p><a href="settings.html#compose">Flowed Text Handling</a> (Settings)</p>
+
+<p><a href="folders.html">Folders</a></p>
+<ul>
+ <li><a href="folders.html#add">adding</a></li>
+ <li><a href="folders.html#delete">deleting</a></li>
+ <li><a href="folders.html#rename">renaming</a></li>
+ <li><a href="folders.html#route">routing mail to</a></li>
+ <li><a href="inbox.html#folder">saving messages to</a></li>
+ <li><a href="settings.html#folders">settings</a></li>
+ <li><a href="folders.html#view">viewing a list of</a></li>
+ <li><a href="folders.html#viewing">viewing messages in</a></li>
+</ul>
+
+<p><a href="read.html#forward">forwarding mail</a></p>
+<p><a href="settings.html#forward">forwarding options</a> (Settings)</p>
+
+<p><strong><a name="G" id="G"></a>G</strong></p>
+
+<p><strong><a name="H" id="H"></a>H</strong></p>
+<p><a href="compose.html#headers">headers</a> (for sending mail)</p>
+<p><a href="read.html#full">headers, full</a> (in received messages)</p>
+<p><a href="settings.html#reply">headers in replies</a> (Settings)</p>
+<p><a href="settings.html#newsweather">Headline News</a> (Settings)</p>
+
+<p><strong><a name="I" id="I"></a>I</strong></p>
+<p><a href="settings.html#mailservers">Inbox Server</a> (Settings)</p>
+<p><a href="inbox.html#icons">index icons</a></p>
+
+<p><strong><a name="J" id="J"></a>J</strong></p>
+
+<p><strong><a name="K" id="K"></a>K</strong></p>
+
+<p><strong><a name="L" id="L"></a>L</strong></p>
+<p><a href="settings.html#dirservers">LDAP Server</a> (Settings)</p>
+<p><a href="settings.html#display">lines per page in index</a> (Settings)</p>
+<p><a href="settings.html#msgview">links, display</a> (Settings)</p>
+
+<p><strong><a name="M" id="M"></a>M</strong></p>
+<p><a href="inbox.html#stars">marking messages with a star</a></p>
+<p><a href="settings.html#compose">Message Encoding</a> (Settings)</p>
+
+<p><strong><a name="N" id="N"></a>N</strong></p>
+<p><a href="quick-start.html#check">new mail check</a></p>
+<p><a href="settings.html#newsweather">news</a> (Settings)</p>
+
+<p><strong><a name="O" id="O"></a>O</strong></p>
+
+<p><strong><a name="P" id="P"></a>P</strong></p>
+<p><a href="settings.html#personal">personal name</a> (Settings)</p>
+<p>preferences, see <a href="settings.html">Settings</a></p>
+
+<p><strong><a name="Q" id="Q"></a>Q</strong></p>
+<p><a href="quick-start.html">Quick Start</a></p>
+
+<p><strong><a name="R" id="R"></a>R</strong></p>
+<p><a href="read.html">reading mail</a></p>
+<ul>
+ <li><a href="read.html#contacts">adding contacts</a></li>
+ <li><a href="read.html#delete">deleting</a></li>
+ <li><a href="read.html#header">displaying full headers</a></li>
+ <li><a href="read.html#forward">forwarding</a></li>
+ <li><a href="inbox.html#stars">marking with stars</a></li>
+ <li><a href="read.html#print">printing</a></li>
+ <li><a href="read.html#reply">replying</a></li>
+ <li><a href="inbox.html#spam">reporting a message as spam</a></li>
+ <li><a href="read.html#folder">saving to a folder</a></li>
+ <li><a href="inbox.html#sort">sorting</a></li>
+ <li><a href="read.html#view">viewing a message</a></li>
+ <li><a href="read.html#attachments">viewing and downloading attachments</a></li>
+</ul>
+
+<p><a href="settings.html#msglist">read messages, automove </a> (Settings)</p>
+<p><a href="settings.html#folders">recent folders displayed</a> (Settings)</p>
+<p><a href="read.html#reply">replying</a></p>
+<p><a href="settings.html#compose">Reply intro string</a> (Settings)</p>
+<p><a href="settings.html#reply">Reply Options</a> (Settings)</p>
+<p><a href="settings.html#compose">Reply prefix</a> (Settings)</p>
+<p><a href="settings.html#msgview">Rich Text Display</a> (Settings)</p>
+
+<p><strong><a name="S" id="S"></a>S</strong></p>
+<p><a href="inbox.html#folder">saving messages to a folder</a></p>
+<p><a href="settings.html#save">Save Sent</a> (Settings)</p>
+<p><a href="compose.html">sending mail</a></p>
+<p><a href="search.html">Search</a></p>
+<ul>
+ <li><a href="search.html#inbox">Basic Index Search</a></li>
+ <li><a href="search.html#advanced">Advanced Index Search</a></li>
+ <li><a href="search.html#example">Example</a></li>
+ <li><a href="search.html#other">searching Contacts and Folder List</a></li>
+</ul>
+
+<p><a href="settings.html">Settings</a></p>
+<ul>
+ <li><a href="settings.html#compose">Compose</a></li>
+ <li><a href="settings.html#dirservers">Directory Servers</a></li>
+ <li><a href="settings.html#folders">Folders</a></li>
+ <li><a href="settings.html#foldernames">Folder Names</a></li>
+ <li><a href="settings.html#forward">Forwarding options</a></li>
+ <li><a href="settings.html#mailservers">Mail Servers</a></li>
+ <li><a href="settings.html#display">Message Display</a></li>
+ <li><a href="settings.html#msglist">Message List</a></li>
+ <li><a href="settings.html#msgview">Message View</a></li>
+ <li><a href="settings.html#newsweather">News and Weather</a></li>
+ <li><a href="settings.html#personal">Personal preferences</a></li>
+ <li><a href="settings.html#reply">Reply Options</a></li>
+ <li><a href="settings.html#fcc">Sent Message Options</a></li>
+</ul>
+
+<p><a href="settings.html#fcc">Sent mail folder</a> (Settings)</p>
+<p><a href="settings.html#personal">signature content</a> (Settings)</p>
+<p><a href="settings.html#reply">signature options</a> (Settings)</p>
+<p><a href="settings.html#mailservers">SMTP Server</a> (Settings)</p>
+<p><a href="inbox.html#spam">spam</a> (reporting)</p>
+<p><a href="settings.html#msglist">Sort Order, default</a> (Settings)</p>
+<p><a href="inbox.html#sort">sorting message index</a></p>
+<p><a href="inbox.html#stars">star</a></p>
+<p><a href="settings.html#msglist">Start display at</a> (Settings)</p>
+
+<p><strong><a name="T" id="T"></a>T</strong></p>
+<p>trash</p>
+<ul>
+ <li><a href="quick-start.html#trash">emptying, deleting, and recovering</a> (how to)</li>
+ <li><a href="inbox.html#trash">Emptying</a></li>
+ <li><a href="settings.html#foldernames">Trash Folder</a> (Settings)</li>
+</ul>
+
+<p><strong><a name="U" id="U"></a>U</strong></p>
+<p><a href="settings.html#msgview">URLs as links</a> (Settings)</p>
+
+<p><strong><a name="V" id="V"></a>V</strong></p>
+<p><a href="inbox.html#view">viewing messages</a></p>
+
+<p><strong><a name="W" id="W"></a>W</strong></p>
+<p><a href="settings.html#newsweather">Weather Bar</a> (Settings)</p>
+<p><a href="settings.html#msgview">web hostnames as links</a> (Settings)</p>
+<p><a href="settings.html#display">Wrap Plain Text column</a> (Settings)</p>
+
+<p><strong><a name="X" id="X"></a>X</strong></p>
+
+<p><strong><a name="Y" id="Y"></a>Y</strong></p>
+
+<p><strong><a name="Z" id="Z"></a>Z</strong></p>
+
+<p>&nbsp;</p>
+</div>
+</body>
+</html>
diff --git a/web/cgi/alpine/2.0/help/compose.html b/web/cgi/alpine/2.0/help/compose.html
new file mode 100644
index 00000000..6a2b7f78
--- /dev/null
+++ b/web/cgi/alpine/2.0/help/compose.html
@@ -0,0 +1,102 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=iso-8859-1">
+<link type="text/css" rel="stylesheet" href="../css/help.css">
+<title>Compose: sending mail</title>
+</head>
+<body>
+<div id="header">
+ <div class="logo"> </div>
+ <div class="nav"><a href="topic-list.html">All Help Topics</a> &nbsp;&nbsp;| &nbsp;&nbsp;<a href="alpha-index.html">Index</a> &nbsp;&nbsp;| &nbsp;&nbsp;<a href="javascript:self.close();">Close</a></div>
+</div>
+<div id="content">
+
+
+
+<h2>Compose: sending mail</h2>
+
+<ul>
+ <li><a href="#writing">Writing messages</a></li>
+ <li><a href="#attachments">Including attachments</a></li>
+ <li><a href="#sending">Sending, canceling, and saving drafts</a></li>
+ <li><a href="#resuming">Resuming drafts</a></li>
+ <li><a href="#headers">Headers</a></li>
+ <li><a href="#autosave">Automatic saves</a></li>
+</ul>
+
+<p>The <strong>Compose</strong> screen is where you write and send messages. The <strong>Compose</strong> screen is divided into two sections:</p>
+<ul>
+ <li>The <em>header</em> section is at the top. You use the header section to address the mail and control the text format.</li>
+ <li>The <em>message</em> section is below the header. The message section is where you enter and format the message text.</li>
+<br /><br /></ul>
+<h2><a name="writing" id="writing"></a>Writing messages</h2>
+
+<ol>
+ <li>On the menu bar, click <strong>Compose</strong> (or <strong>Reply</strong> or <strong>Forward</strong>).</li>
+ <li>In the header section, enter the addressing information. By default, the headers displayed are the <strong>To</strong>, <strong>Cc</strong>, and <strong>Subject</strong> fields. For information on displaying more header fields, see <a href="#headers">Headers</a>.</li>
+ <li>To enter rich-text click on <strong>Rich Text</strong> to the right of the Subject header. Click <strong>Plain Text</strong> to toggle this back to plain text.</li>
+ <li>In the message section, enter your message text. If you are entering Rich Text, a <em>formatting toolbar</em> at the top of the message section allows you to apply rich-text formatting to message text. You can also insert links and graphics using the formatting toolbar.</li>
+ <li>If desired, you can mark your message with a priority level. On the menu bar, point to <strong>Priority</strong>, and then select the level that you want for this message.</li>
+<br /><br /></ol>
+<h2><a name="attachments" id="attachments"></a>Including attachments</h2>
+<ol>
+ <li>On the menu bar, click <strong>Attach File</strong>.</li>
+ <li>Enter the path and file name for the file that you want to attach, or click <strong>Browse</strong> to browse for the file and select it.</li>
+<br /><br /></ol>
+<h2><a name="sending" id="sending"></a>Sending, canceling, and saving drafts</h2>
+
+<p><strong>To send mail</strong></p>
+<ul>
+ <li>On the menu bar, click <strong>Send</strong>.</li>
+</ul>
+
+<p><strong>To cancel an editing session</strong></p>
+<ol>
+ <li>On the menu bar, click <strong>Cancel</strong>, or click any item in the left pane, such as your <strong>Inbox</strong>.</li>
+ <li>Click <strong>Discard</strong> to confirm. The <strong>Compose</strong> screen closes and your <strong>Inbox</strong> appears.</li>
+</ol>
+<p><strong>To save a draft of a message</strong></p>
+<ul>
+ <li>On the menu bar, click <strong>Save Draft</strong>. A copy of the message is saved to the Drafts folder. The <strong>Compose</strong> screen closes and you are returned to the screen that was displayed before the editing session began.</li><br /><br />
+</ul>
+<div class="note">
+<h4>Note:</h4>
+<div> If you open a previously saved draft, make more changes, and then cancel the editing session and discard changes, the entire draft is deleted, not just the most recent changes. </div>
+</div>
+
+<br /><br />
+<h2><a name="resuming" id="resuming"></a>Resuming drafts</h2>
+<p>To resume editing a draft message that you previously saved go to the <strong>Drafts</strong> folder by clicking on <strong>Drafts</strong> in the left pane. Find the message you want to resume in the list and click on its Subject to resume composing.
+<h2><a name="headers" id="headers"></a>Headers</h2>
+<p> The header section is where you enter addressing information. By default, the header displays the <strong>To</strong>, <strong>Cc</strong>, and <strong>Subject</strong> fields. </p>
+<p><strong>To display all headers</strong></p>
+<ul>
+ <li>To the right of the <strong>To</strong> field, click <strong>More Headers</strong>.</li>
+</ul>
+<p><strong>To display only the default headers</strong></p>
+<ul>
+ <li>To the right of the <strong>To</strong> field, click <strong>Fewer Headers</strong>.</li>
+</ul>
+<h3>Header fields</h3>
+<p>The following header fields are available:</p>
+<ul>
+ <li><strong>To.</strong> Use this field to specify email recipients:
+ <ul>
+ <li> Type the full email address of the recipient. Separate multiple addresses with commas.</li>
+ <li> Type a name or nickname specified in your contact list and click. Alpine expands the name or nickname to the full address. Separate multiple addresses with commas.</li>
+ <li>Click <strong>To</strong> to display to your contact list and select an address or to select an address from the directory server.</li>
+ </ul>
+ </li>
+ <li><strong>Attachments.</strong> Lists any files that have been attached to the message.</li>
+ <li> <strong>Subject.</strong> Enter a brief phrase describing the topic of the message. </li>
+ <li><strong>Cc</strong> (Carbon copy). Similar to the <strong>To</strong> field. Enter one or more addresses to receive a copy of the message. These addresses are seen by all message recipients.</li>
+ <li> <strong>Bcc</strong> (Blind carbon copy). Similar to the <strong>To</strong> and <strong>Cc</strong> fields. Enter one or more addresses to receive a copy of the message. The Bcc header does not show up in the sent mail so these addresses are hidden from other recipients.</li>
+ <li><strong>Fcc</strong> (File carbon copy). Specifies a folder to which a copy of your outgoing message is saved. You can control whether messages are saved with or without attachments. For more information, see <a href="settings.html#fcc">Settings</a>.</li>
+<br /><br /></ul>
+
+<h2><a name="autosave" id="autosave"></a>Automatic saves</h2>
+<p>Alpine saves a draft every 5 minutes, overwriting the draft each time, so that only the most recent saved version is preserved. </p>
+</div>
+</body>
+</html>
diff --git a/web/cgi/alpine/2.0/help/contacts.html b/web/cgi/alpine/2.0/help/contacts.html
new file mode 100644
index 00000000..690dd76c
--- /dev/null
+++ b/web/cgi/alpine/2.0/help/contacts.html
@@ -0,0 +1,80 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=iso-8859-1">
+<link type="text/css" rel="stylesheet" href="../css/help.css">
+<title>Contacts: your address book</title>
+</head>
+<body>
+<div id="header">
+ <div class="logo"> </div>
+ <div class="nav"><a href="topic-list.html">All Help Topics</a> &nbsp;&nbsp;| &nbsp;&nbsp;<a href="alpha-index.html">Index</a> &nbsp;&nbsp;| &nbsp;&nbsp;<a href="javascript:self.close();">Close</a></div>
+</div>
+<div id="content">
+
+<h1>Contacts: your address book</h1>
+
+<ul>
+ <li><a href="#add">Adding a contact</a></li>
+ <li><a href="#remove">Removing a contact</a></li>
+ <li><a href="#edit">Editing a contact</a></li>
+ <li><a href="#group">Creating a group contact</a></li>
+<br /><br /></ul>
+
+<p>Contacts allow you to store names, email addresses, and other information in a contact list, which serves as an address book. You can quickly send mail to a contact by retreiving information from your contact list.</p>
+
+<h2><a name="add" id="add"></a>Adding a contact</h2>
+
+<p><strong>To add a contact</strong></p>
+<ol>
+ <li>In the left pane, click <strong>Contacts</strong>. Your contact list appears in the right pane.</li>
+ <li>On the menu bar, click <strong>New Contact</strong>.</li>
+ <li>Fill in the contact information:</li>
+ <ul>
+ <li><strong>Nickname.</strong> A short name that you can use for quick addressing. If you type the nickname instead of the full display name or email address, Alpine will offer a list of matching choices from your contacts list and the University of Washington directory. You can select the contact from the list.</li>
+ <li><strong>Display Name.</strong> The name that appears in the message header.</li>
+ <li><strong>Email.</strong> The contact's email address.</li>
+ <li><strong>Notes.</strong> Any text you want. Used only by you.</li>
+ <li><strong>Fcc Folder.</strong> A folder that will receive copies of all messages that you send directly to this contact.</li>
+ </ul>
+ <li>Click <strong>Add</strong>.</li>
+<br /><br /></ol>
+<p><strong>To add a contact from a message</strong></p>
+<ul>
+<li>Drag the contact's email address from the<strong> From</strong> field to the <strong>Contacts</strong> folder.
+<p><em>or</em></p></li>
+<li>To the right of the <strong>From</strong> field, click <strong>Add</strong>.</li>
+<br /><br /></ul>
+<p><strong>To add multiple contacts from a message</strong></p>
+<ul>
+ <li>On the menu bar, click <strong>More Actions</strong>, and then select <strong>Extract</strong>. Alpine extracts everything in the message that is in the format of an email address and presents a list of contacts. You can confirm each contact.</li>
+<br /><br /></ul>
+<h2><a name="remove" id="remove"></a>Removing a contact</h2>
+<ol>
+ <li>In the left pane, click <strong>Contacts</strong>. Your contact list appears in the right pane.</li>
+ <li>Select the contacts you wish to remove by checking the boxes to the left of the Display Names.</li>
+ <li>On the menu bar, click <strong>Delete</strong>.</li>
+ <li>Confirm that you want to <strong>Delete</strong> the contacts <strong>Forever</strong>.</li>
+<br /><br /></ol>
+
+<h2><a name="edit" id="edit"></a>Editing a contact</h2>
+
+<p><strong>To edit a contact</strong></p>
+<ol>
+ <li>In the left pane, click <strong>Contacts</strong>. Your contact list appears in the right pane.</li>
+ <li>Click on the <strong>Display Name</strong> to edit the corresponding entry.</li>
+<br /><br /></ol>
+
+<h2><a name="group" id="group"></a>Creating a group contact</h2>
+<p>A group contact is simply a contact with more than one email address.</p>
+<p><strong>To add a group contact</strong></p>
+<ol>
+ <li>In the left pane, click <strong>Contacts</strong>. Your contact list appears in the right pane.</li>
+ <li>On the menu bar, click <strong>New Group</strong>.</li>
+ <li>Fill in the contact information like you would for a regular contact, except</li>
+ <li><strong>Group Email.</strong> Enter all of the addresses or nicknames separated by commas.</li>
+ <li>Click <strong>Add</strong>.</li>
+<br /><br /></ol>
+</div>
+</body>
+</html>
diff --git a/web/cgi/alpine/2.0/help/folders.html b/web/cgi/alpine/2.0/help/folders.html
new file mode 100644
index 00000000..dc05df0b
--- /dev/null
+++ b/web/cgi/alpine/2.0/help/folders.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=iso-8859-1">
+<link type="text/css" rel="stylesheet" href="../css/help.css">
+<title>Folders: organizing your mail</title>
+</head>
+<body>
+<div id="header">
+ <div class="logo"> </div>
+ <div class="nav"><a href="topic-list.html">All Help Topics</a> &nbsp;&nbsp;| &nbsp;&nbsp;<a href="alpha-index.html">Index</a> &nbsp;&nbsp;| &nbsp;&nbsp;<a href="javascript:self.close();">Close</a></div>
+</div>
+<div id="content">
+
+<h2>Folders: organizing your mail</h2>
+
+<ul>
+ <li><a href="#view">Viewing your list of folders</a></li>
+ <li><a href="#viewing">Viewing the messages in a folder</a></li>
+ <li><a href="#add">Adding folders</a></li>
+ <li><a href="#delete">Deleting folders</a></li>
+ <li><a href="#rename">Renaming folders</a></li>
+ <li><a href="#route">Routing mail to folders</a></li>
+</ul>
+
+<p>You can organize your mail into folders. Folders are displayed in the left pane. Some folders are provided by default:</p>
+<ul>
+ <li><strong>Inbox.</strong> Stores and displays your mail messages when they arrive, unless you have routed messages to another folder. You can drag and drop messages from the Inbox to other folders.</li>
+ <li><strong>Drafts.</strong> Stores any messages that have been saved during an editing session.</li>
+ <li><strong>Sent.</strong> By default, stores copies of all of your sent mail.</li>
+ <li><strong>Junk.</strong> Stores messages that have been caught by spam filtering and messages that you add by clicking <strong>Report Spam</strong> on the menu bar. On a weekly basis, Alpine deletes everything in the Junk folder that is dated before the previous week.</li>
+ <li><strong>Trash.</strong> Stores deleted messages, where thay can be recovered until they are permanently removed. For more information, see <a href="quick-start.html#trash">Trash</a>.</li>
+ <li><strong>Recent Folders.</strong> This is a listing of the folders that you have opened recently. You can set the number of recent folders displayed in the left pane. For more information, see <a href="settings.html#folders">Settings</a>.<br />
+ </li><br />
+ </ul>
+<div class="note">
+<h4>Note:</h4>
+<div> When viewed in the folder list displayed by clicking <strong>View/Manage Folders</strong>, the default folders named Junk and Sent in the left pane appear under the names sent-mail and junk-mail.</div>
+</div>
+
+<br />
+
+<h2><a name="view" id="view"></a>Viewing your list of folders</h2>
+<ul>
+ <li>In the left pane, click <strong>View/Manage Folders</strong>. A list of your folders appears in the right pane.</li><br /><br />
+</ul>
+<h2><a name="viewing" id="viewing"></a>Viewing the messages in a folder</h2>
+<p><strong>To view the messages contained in a folder</strong></p>
+<ol>
+ <li>In the left pane, click <strong>View/Manage Folders</strong>. A list of your folders appears in the right pane.</li>
+ <li>Select the folder that you want to view by clicking its name. On the menu bar, click <strong>View Messages</strong>.</li>
+ <li>Alternatively, double click the folder name.</li>
+<br /><br /></ol>
+<h2><a name="add" id="add"></a>Adding folders</h2>
+<ol>
+ <li>In the left pane, click <strong>View/Manage Folders</strong>. A list of your folders appears in the right pane.</li>
+ <li>On the menu bar, click <strong>New Folder</strong>.</li>
+ <li>Enter a name for the new folder and click <strong>New Folder</strong>. If you want this new folder to be a subfolder within an existing folder, enter the name of the parent folder, followed by a slash ( / ), followed by the new folder name. For example, <strong>subdir/foldername</strong>.</li>
+ <li>The new folder appears in the list.</li>
+<br /><br /></ol>
+<h2><a name="delete" id="delete"></a>Deleting folders</h2>
+<div class="note">
+<h4>Note:</h4>
+<div>Unlike deleted <strong>messages</strong>, deleted <strong>folders</strong> are not moved to the Trash, and they cannot be retrieved.</div>
+</div>
+<p><strong>To delete a folder</strong></p>
+<ol>
+ <li>In the left pane, click <strong>View/Manage Folders</strong>. A list of your folders appears in the right pane.</li>
+ <li>Click the folder that you want to delete. On the menu bar, click <strong>Delete</strong>.</li>
+ <li>Click <strong>Delete Forever</strong> to confirm.</li>
+<br /><br /></ol>
+<h2><a name="rename" id="rename"></a>Renaming folders</h2>
+
+<ol>
+ <li>In the left pane, click <strong>View/Manage Folders</strong>. A list of your folders appears in the right pane.</li>
+ <li>Click the folder that you want to rename. On the menu bar, click <strong>Rename</strong>.</li>
+ <li>Enter the new name.</li>
+ <li>Click <strong>Rename Folder</strong> to confirm.</li>
+<br /><br /></ol>
+
+<h2><a name="route" id="route"></a>Routing mail to folders</h2>
+
+<p>You can set up Alpine to route incoming and outgoing messages to designated folders.</p>
+<p><strong>To route a copy of an outgoing message to a folder</strong></p>
+<ul>
+ <li>Enter the folder name in the <strong>Fcc</strong> field in the composer.</li>
+</ul>
+
+<p><strong>To route incoming messages to a folder</strong></p>
+<ul>
+ <li>See <a href="settings.html">Settings</a>. </li>
+<br /><br /></ul>
+
+</div>
+</body>
+</html>
diff --git a/web/cgi/alpine/2.0/help/graphics/attach_sm.gif b/web/cgi/alpine/2.0/help/graphics/attach_sm.gif
new file mode 100644
index 00000000..cdf108cc
--- /dev/null
+++ b/web/cgi/alpine/2.0/help/graphics/attach_sm.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/help/graphics/compose.gif b/web/cgi/alpine/2.0/help/graphics/compose.gif
new file mode 100644
index 00000000..608eb4fb
--- /dev/null
+++ b/web/cgi/alpine/2.0/help/graphics/compose.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/help/graphics/fwd.gif b/web/cgi/alpine/2.0/help/graphics/fwd.gif
new file mode 100644
index 00000000..546ff6a3
--- /dev/null
+++ b/web/cgi/alpine/2.0/help/graphics/fwd.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/help/graphics/new.gif b/web/cgi/alpine/2.0/help/graphics/new.gif
new file mode 100644
index 00000000..abc16be0
--- /dev/null
+++ b/web/cgi/alpine/2.0/help/graphics/new.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/help/graphics/parts.gif b/web/cgi/alpine/2.0/help/graphics/parts.gif
new file mode 100644
index 00000000..90fbb390
--- /dev/null
+++ b/web/cgi/alpine/2.0/help/graphics/parts.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/help/graphics/replied.gif b/web/cgi/alpine/2.0/help/graphics/replied.gif
new file mode 100644
index 00000000..8ff06835
--- /dev/null
+++ b/web/cgi/alpine/2.0/help/graphics/replied.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/help/graphics/replied_and_fwd.gif b/web/cgi/alpine/2.0/help/graphics/replied_and_fwd.gif
new file mode 100644
index 00000000..b3dd5a1b
--- /dev/null
+++ b/web/cgi/alpine/2.0/help/graphics/replied_and_fwd.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/help/graphics/screen-header.gif b/web/cgi/alpine/2.0/help/graphics/screen-header.gif
new file mode 100644
index 00000000..f8c95dee
--- /dev/null
+++ b/web/cgi/alpine/2.0/help/graphics/screen-header.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/help/graphics/star.gif b/web/cgi/alpine/2.0/help/graphics/star.gif
new file mode 100644
index 00000000..5a89f298
--- /dev/null
+++ b/web/cgi/alpine/2.0/help/graphics/star.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/help/help_home.htm b/web/cgi/alpine/2.0/help/help_home.htm
new file mode 100644
index 00000000..559591a1
--- /dev/null
+++ b/web/cgi/alpine/2.0/help/help_home.htm
@@ -0,0 +1,46 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=iso-8859-1">
+<link type="text/css" rel="stylesheet" href="../css/help.css">
+<title>Help: WebMail</title>
+</head>
+<body>
+<div id="header">
+ <div class="logo"> </div>
+ <div class="nav"><a href="help_index.htm">Index</a> &nbsp;&nbsp;| &nbsp;&nbsp;<a href="javascript:self.close();">Close</a></div>
+</div>
+<div id="content">
+ <h2>WebMail Help</h2>
+ <dl>
+ <dd>Lorem ipsum dolor sit amet, con sectetuer adipiscing elit, sed diam nonnumy nibh eeuismod tempor inci dunt ut labore et dolore magna ali quam erat volupat. <br>
+ <br>
+ <div class="note">
+ <h4>Note</h4>
+ <div>Lorem ipsum dolor sit amet, con sectetuer adipiscing elit, sed diam nonnumy nibh eeuismod tempor inci dunt ut labore et dolore magna ali quam erat volupat.</div>
+ </div>
+ </dd>
+ <dt>Message List (e.g. INBOX)</dt>
+ <dd>Ut wisi enim ad minim veniam.</dd>
+ <dt>Folders</dt>
+ <dd>Con sectetuer adipiscing elit, sed diam nonnumy nibh.</dd>
+ <dt>Compose</dt>
+ <dd>Ut wisi enim ad minim veniam.</dd>
+ <dt>Print</dt>
+ <dd>Ut wisi enim ad minim veniam.</dd>
+ <dt>Search</dt>
+ <dd>Ut wisi enim ad minim veniam.</dd>
+ <dt>News and Weather</dt>
+ <dd>Ut wisi enim ad minim veniam.</dd>
+ </dl>
+</div>
+<div class="also">
+ <h4>See also</h4>
+ <ul>
+ <li><a href="#">View/Manage Folders</a></li>
+ <li><a href="#">Contacts</a></li>
+ <li><a href="#">Settings</a></li>
+ </ul>
+</div>
+</body>
+</html>
diff --git a/web/cgi/alpine/2.0/help/help_index.htm b/web/cgi/alpine/2.0/help/help_index.htm
new file mode 100644
index 00000000..ee694f73
--- /dev/null
+++ b/web/cgi/alpine/2.0/help/help_index.htm
@@ -0,0 +1,67 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=iso-8859-1">
+<link type="text/css" rel="stylesheet" href="../css/help.css">
+<title>Help: Index</title>
+</head>
+<body>
+<div id="header">
+ <div class="logo"> </div>
+ <div class="nav"><a href="javascript:history.back()">Go Back</a> &nbsp;&nbsp;| &nbsp;&nbsp;<a href="javascript:self.close();">Close</a></div>
+</div>
+<div id="content">
+ <h2>Index</h2>
+ <dl>
+ <dt>Message List: your inbox</dt>
+ <dd>
+ <ul>
+ <li><a href="#">INBOX</a></li>
+ <li><a href="#">Message Threading</a></li>
+ <li><a href="#">Magna Ali Quam</a></li>
+ </ul>
+ </dd>
+ <dt>Message View: reading email</dt>
+ <dd>
+ <ul>
+ <li><a href="#">Nonnumy Nibh</a></li>
+ <li><a href="#">Labore et Dolore</a></li>
+ <li><a href="#">Magna Ali Quam</a></li>
+ </ul>
+ </dd>
+ <dt>Compose: writing email</dt>
+ <dd>
+ <ul>
+ <li><a href="#">Nonnumy Nibh</a></li>
+ <li><a href="#">Labore et Dolore</a></li>
+ <li><a href="#">Magna Ali Quam</a></li>
+ </ul>
+ </dd>
+ <dt>Folders: organizing email</dt>
+ <dd>
+ <ul>
+ <li><a href="#">Nonnumy Nibh</a></li>
+ <li><a href="#">Labore et Dolore</a></li>
+ <li><a href="#">Magna Ali Quam</a></li>
+ </ul>
+ </dd>
+ <dt>Contacts: your address book</dt>
+ <dd>
+ <ul>
+ <li><a href="#">Nonnumy Nibh</a></li>
+ <li><a href="#">Labore et Dolore</a></li>
+ <li><a href="#">Magna Ali Quam</a></li>
+ </ul>
+ </dd>
+ <dt>Settings: customizing your webmail</dt>
+ <dd>
+ <ul>
+ <li><a href="#">Nonnumy Nibh</a></li>
+ <li><a href="#">Labore et Dolore</a></li>
+ <li><a href="#">Magna Ali Quam</a></li>
+ </ul>
+ </dd>
+ </dl>
+</div>
+</body>
+</html>
diff --git a/web/cgi/alpine/2.0/help/help_settings.htm b/web/cgi/alpine/2.0/help/help_settings.htm
new file mode 100644
index 00000000..46cab725
--- /dev/null
+++ b/web/cgi/alpine/2.0/help/help_settings.htm
@@ -0,0 +1,66 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=iso-8859-1">
+<link type="text/css" rel="stylesheet" href="../css/help.css">
+<title>Help: Settings</title>
+</head>
+<body>
+<div id="header">
+ <div class="logo"> </div>
+ <div class="nav"><a href="help_index.htm">Index</a> &nbsp;&nbsp;| &nbsp;&nbsp;<a href="javascript:self.close();">Close</a></div>
+</div>
+<div id="content">
+ <h2>Settings: General Preferences </h2>
+ <dl>
+ <dd>Lorem ipsum dolor sit amet, con sectetuer adipiscing elit, sed diam nonnumy nibh eeuismod tempor inci dunt ut labore et dolore magna ali quam erat volupat. <br>
+ <br>
+ <div class="note">
+ <h4>Note:</h4>
+ <div>You <b>must</b> save your changes before going to another section.</div>
+ </div>
+ <div class="note">
+ <h4>Note:</h4>
+ <div>The "Reset to Default Settings" button, found at the bottom of some pages, will reset your setting to the default settings.</div>
+ </div>
+ </dd>
+ <dt>Display</dt>
+ <dd>Lorem ipsum dolor sit amet, con sectetuer adipiscing elit, sed diam nonnumy nibh eeuismod tempor inci dunt ut labore et dolore magna ali quam.</dd>
+ <dt>Folders</dt>
+ <dd>Con sectetuer adipiscing elit, sed diam nonnumy nibh.</dd>
+ <dt>Compose Encoding</dt>
+ <dd>Ut wisi enim ad minim veniam.</dd>
+ <dt>Reply Options</dt>
+ <dd>Ut wisi enim ad minim veniam.</dd>
+ <dt>Forwarding Options</dt>
+ <dd>Ut wisi enim ad minim veniam.</dd>
+ <dt>Flag Personal Email</dt>
+ <dd>Ut wisi enim ad minim veniam.</dd>
+ <dt>New Mail Notification</dt>
+ <dd>Ut wisi enim ad minim veniam.</dd>
+ <dt>Auto-Contacts</dt>
+ <dd>Ut wisi enim ad minim veniam.</dd>
+ <dt>Save Sent</dt>
+ <dd>Ut wisi enim ad minim veniam.</dd>
+ <dt>Archive Sent</dt>
+ <dd>Ut wisi enim ad minim veniam.</dd>
+ <dt>Emptying Trash</dt>
+ <dd>Ut wisi enim ad minim veniam.</dd>
+ <dt>Fcc Options</dt>
+ <dd>Set whether to save with or without attachments when auto-saving messages to the Fcc (Folder carbon copy) folder.</dd>
+ <dt>Keyboard Shortcuts</dt>
+ <dd>Ut wisi enim ad minim veniam.</dd>
+ </dl>
+</div>
+<div class="also">
+ <h4>See also</h4>
+ <ul>
+ <li><a href="#">Personal Information</a></li>
+ <li><a href="#">News and Weather Bar</a></li>
+ <li><a href="#">Spam Filter</a></li>
+ <li><a href="#">Vacation Auto-Reply</a></li>
+ <li><a href="#">Advanced Settings</a></li>
+ </ul>
+</div>
+</body>
+</html>
diff --git a/web/cgi/alpine/2.0/help/inbox.html b/web/cgi/alpine/2.0/help/inbox.html
new file mode 100644
index 00000000..716a19f1
--- /dev/null
+++ b/web/cgi/alpine/2.0/help/inbox.html
@@ -0,0 +1,145 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=iso-8859-1">
+<link type="text/css" rel="stylesheet" href="../css/cbn/screen.css">
+<link type="text/css" rel="stylesheet" href="../css/help.css">
+<title>Your Inbox</title>
+</head>
+<body>
+<div id="header">
+ <div class="logo"> </div>
+ <div class="nav"><a href="topic-list.html">All Help Topics</a> &nbsp;&nbsp;| &nbsp;&nbsp;<a href="alpha-index.html">Index</a> &nbsp;&nbsp;| &nbsp;&nbsp;<a href="javascript:self.close();">Close</a></div>
+</div>
+<div id="content">
+
+
+<h2>Your Inbox</h2>
+
+<ul>
+ <li><a href="#view">Viewing messages</a></li>
+ <li><a href="#sort">Sorting messages</a></li>
+ <li><a href="#delete">Deleting messages</a></li>
+ <li><a href="#folder">Saving messages to a folder</a></li>
+ <li><a href="#icons">Icons</a></li>
+ <li><a href="#stars">Marking messages with stars</a></li>
+ <li><a href="#spam">Reporting messages as spam</a></li>
+ <li><a href="#trash">Emptying trash</a></li>
+ <li><a href="#drag">Drag and Drop Shortcuts</a></li>
+</ul>
+
+<p>When you first log in to your mail account, you see your Inbox. Your Inbox stores and displays your mail messages.</p>
+<p>The main body of the display consists of one line per message. By default, your Inbox displays 25 messages on each page. Use the arrows at the right end of the menu bar or at the bottom of the browser window to move from page to page in the list.</p>
+<p>To change the number of messages displayed per page, see <a href="settings.html#display">Settings</a>.</p>
+<p>There is a checkbox at the start of each message line. To select a set of messages on which to perform an action (for example, Deleting) click in the checkbox. The checkbox at the top of the column selects/unselects all of the messages on the page.</p>
+<h2><a name="view" id="view"></a>Viewing messages</h2>
+<p><strong>To read a message</strong></p>
+<ul>
+ <li>Click the <b>Subject</b> of the message.</li>
+</ul>
+<p><strong>To read messages in other folders</strong></p>
+<ul>
+ <li>Click the desired folder in the left pane.</li>
+<br /><br /></ul>
+<h2><a name="sort" id="sort"></a>Sorting messages</h2>
+<p>By default the message index is sorted by <strong>Date</strong>, with the most recent messages coming first and the oldest messages at the bottom of the index. You can change this default sort order in <a href="settings.html">Settings</a></p>
+<ul>
+ <li><strong>Sort by columns on the list.</strong> Click a column heading, such as <strong>From</strong>, <strong>Subject</strong>, or <strong>Date</strong> to sort by that field. Click on the small triangle next to the sort heading to reverse the sort order.</li>
+
+ <li><strong>Group by Thread.</strong> Groups all messages in the same conversation together, in a a tree view. This option is available in the <strong>Arrange</strong> menu bar above the column headings.</li>
+
+ <li><strong>Group by Subject.</strong> Groups all messages with the same subject together. This option is also only available in the <strong>Arrange</strong> menu.</li>
+
+ <li><strong>Sort by Arrival.</strong> Sometimes it is convenient to sort in the order that the messages were added to the folder instead of by the Date, which is usually similar but a little different. Select this option from the <strong>Arrange</strong> menu.</li>
+<br /><br /></ul>
+
+<h2><a name="delete" id="delete"></a>Deleting messages</h2>
+<ol>
+ <li>Select the messages you wish to delete by checking the boxes to the left of the messages.</li>
+ <li>On the menu bar, click <strong>Delete</strong>.</li>
+</ol>
+
+<p>When you click <strong>Delete</strong>, the selected messages are moved to the Trash<a href="#trash"></a>, where you can delete them later. </p>
+
+<h2><a name="folder" id="folder"></a>Saving messages to a folder</h2>
+<p>You can move or copy messages from the Inbox to a folder. Moving a message removes it from the Inbox while creating a copy in the designated folder. Copying a message leaves the message in the Inbox while creating a copy in the designated folder.</p>
+<ol>
+ <li>Select the box to the left of the message or messages that you wish to put in a folder.</li>
+ <li>On the menu bar, locate the <strong>Move</strong> or <strong>Copy</strong> button. Skip this step if the one you want is already displayed. If necessary, click the button and pull down the menu to select <strong>Move</strong> or <strong>Copy</strong>.</li>
+ <li>On the menu bar, click <strong>to Folder</strong> and select the target folder from the list, or select <strong>More Folders</strong> to see a list of all folders. If you select <strong>More Folders</strong>, click the target folder in the list, and then click <strong>Copy</strong> or <strong>Move</strong>.</li>
+<br /><br /></ol>
+
+<h2><a name="icons" id="icon"></a>Icons</h2>
+<p>Several informative icons may appear to the left of the <strong>From</strong> column.</p>
+
+<table border="1">
+ <tr>
+ <th width="15%" >Icon</th>
+ <th width="80%" >Meaning</th>
+ </tr>
+ <tr>
+ <td><span class="sp spml spml1">&nbsp;</span></div></td>
+ <td>New message</td>
+ </tr>
+ <tr>
+ <td><span class="sp spml spml6">&nbsp;</span></div></td>
+ <td>The message includes an attachment</td>
+ </tr>
+ <tr>
+ <td><span class="sp spml spml2">&nbsp;</span></div></td>
+ <td>You have replied to the message</td>
+ </tr>
+ <tr>
+ <td><span class="sp spml spml3">&nbsp;</span></div></td>
+ <td>You have forwarded the message</td>
+ </tr>
+ <tr>
+ <td><span class="sp spml spml4">&nbsp;</span></div></td>
+ <td>The message is addressed directly to you</td>
+ </tr>
+ <tr>
+ <td><span class="sp spml spml5">&nbsp;</span></div></td>
+ <td>The message is cc'd to you</td>
+ </tr>
+</table>
+<p>In addition, messages that have not been read are displayed in bold text.</p>
+<h2><a name="stars" id="stars"></a>Marking messages with stars</h2>
+<p>You can mark messages with stars &nbsp;<img src="graphics/star.gif" alt="Star" width="17" height="17" />&nbsp; that appear in the far right column of the list. The stars can indicate anything that you like. For example, you might mark the most important messages with a star.</p>
+
+<p><strong>To mark a message with a star</strong></p>
+<ul>
+ <li>Click in the rightmost column of the list on the row for that message or select <strong>Set Star</strong> from the <strong>More Actions</strong> menu in the menu bar.</li>
+<br /><br /></ul>
+<h2><a name="spam" id="spam"></a>Reporting messages as spam</h2>
+<p>If you get a message that you think should have been identified by the spam detection processes but wasn't, you may submit it for review, which may help improve the effectiveness of spam detection. </p>
+<p><strong>To report spam</strong></p>
+<ol>
+ <li>Select the message or messages that you wish to report as spam by checking the boxes in the left column.</li>
+ <li>On the menu bar, click <strong>Report Spam</strong>. A copy of the message is sent to the University of Washington spam detection process and the message is moved to your <strong>Junk</strong> folder.</li>
+<br /><br /></ol>
+<h2><a name="trash" id="trash"></a>Emptying trash</h2>
+<p>When messages are deleted they are actually moved to the <strong>Trash</strong> folder and not immediately deleted. To permanently remove the messages you must empty the trash.</p>
+<p><strong>To empty the entire contents of the trash</strong></p>
+<ul>
+ <li>Find the <strong>Trash</strong> folder in the left pane. If there are messages in the Trash folder you may click on the word <strong>Empty</strong> next to the Trash folder to empty it. This is also true of the <strong>Junk</strong> folder, which may be emptied in the same way.</li>
+</ul>
+<p><strong>To permanently delete selected messages from the trash</strong></p>
+<ol>
+ <li>In the left pane, click <strong>Trash</strong>.</li>
+ <li>Select the box to the left of the messages that you want to delete.</li>
+ <li>On the menu bar, click <strong>Delete Forever</strong>. Be sure that you have selected the right messages; there is no confirmation screen for this action.</li>
+</ol>
+<p><strong>To recover messages from the trash</strong></p>
+<ol>
+ <li>In the left pane, click Trash.</li>
+ <li>Select the box to the left of the messages that you want to recover.</li>
+ <li>On the menu bar, locate the <strong>Move</strong> or <strong>Copy</strong> button. Skip this step if the one you want is already dislayed. If necessary, click the button and pull down the menu to select <strong>Move</strong> or <strong>Copy</strong>.</li>
+ <li>On the menu bar, click <strong>to Folder</strong> and select the target folder from the list, or select <strong>More Folders</strong> to see a list of all folders. If you select <strong>More Folders</strong>, click the target folder in the list, and then click <strong>Copy</strong> or <strong>Move</strong>.</li>
+<br /><br /></ol>
+<h2><a name="drag" id="drag"></a>Drag and Drop Shortcuts</h2>
+<p>You may grab a message by its From field and drag it over a folder in the left pane in order to move it to the folder. Alternatively, if you drag it to the Contacts list in the left pane and drop it there the From address will be added to your list of Contacts.</p>
+
+
+</div>
+</body>
+</html>
diff --git a/web/cgi/alpine/2.0/help/popup/_notes/dwsync.xml b/web/cgi/alpine/2.0/help/popup/_notes/dwsync.xml
new file mode 100644
index 00000000..e659193f
--- /dev/null
+++ b/web/cgi/alpine/2.0/help/popup/_notes/dwsync.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<dwsync>
+<file name="help_set_display.htm" local="128395568460000000" remote="128413808830000000" testing="0" />
+</dwsync> \ No newline at end of file
diff --git a/web/cgi/alpine/2.0/help/popup/help_set_display.htm b/web/cgi/alpine/2.0/help/popup/help_set_display.htm
new file mode 100644
index 00000000..574ba8db
--- /dev/null
+++ b/web/cgi/alpine/2.0/help/popup/help_set_display.htm
@@ -0,0 +1,36 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=iso-8859-1">
+<link type="text/css" rel="stylesheet" href="../../css/help_popup.css">
+<title>Help: WebMail</title>
+</head>
+<body>
+<div id="header">
+ <div class="nav"><a href="javascript:self.close();">Close</a></div>
+</div>
+<div id="content">
+ <h2>Settings: Display</h2>
+ <dl>
+ <dd>Lorem ipsum dolor sit amet, con sectetuer adipiscing elit, sed diam nonnumy nibh eeuismod tempor inci dunt ut labore et dolore magna ali quam erat volupat. <br>
+ <br>
+ <div class="note">
+ <h4>Note</h4>
+ <div>Lorem ipsum dolor sit amet, con sectetuer adipiscing elit, sed diam nonnumy nibh eeuismod tempor inci dunt ut labore et dolore magna ali quam erat volupat.</div>
+ </div>
+ </dd>
+ <dt>Themes</dt>
+ <dd>Ut wisi enim ad minim veniam.</dd>
+ <dt>Number of messages per page</dt>
+ <dd>Con sectetuer adipiscing elit, sed diam nonnumy nibh.</dd>
+ </dl>
+</div>
+<div class="also">
+ <h4>See also</h4>
+ <ul>
+ <li><a href="#">Lorem ipsum dolor sit</a></li>
+ <li><a href="#">con sectetuer</a></li>
+ </ul>
+</div>
+</body>
+</html>
diff --git a/web/cgi/alpine/2.0/help/quick-start.html b/web/cgi/alpine/2.0/help/quick-start.html
new file mode 100644
index 00000000..68ce79b3
--- /dev/null
+++ b/web/cgi/alpine/2.0/help/quick-start.html
@@ -0,0 +1,136 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=iso-8859-1">
+<link type="text/css" rel="stylesheet" href="../css/help.css">
+<title>Alpine Help Pages</title>
+</head>
+<body>
+<div id="header">
+ <div class="logo"> </div>
+ <div class="nav"><a href="topic-list.html">All Help Topics</a> &nbsp;&nbsp;| &nbsp;&nbsp;<a href="alpha-index.html">Index</a> &nbsp;&nbsp;| &nbsp;&nbsp;<a href="javascript:self.close();">Close</a></div>
+</div>
+<div id="content">
+
+
+<h2>Alpine Quick Start</h2>
+
+
+<p>This page provides a quick overview of basic Alpine features and how to access them.</p>
+<ul>
+ <li><a href="#main">Main parts of the Alpine screen</a></li>
+ <li><a href="#compose">Parts of the Compose window</a></li>
+ <li><a href="#basic">Basic Alpine Features</a></li><br /><br />
+</ul>
+<h2><a name="main" id="main"></a>Main parts of the Alpine screen</h2>
+<p>The following figure shows a portion of the Alpine screen with the main parts identified. </p>
+<p><img src="graphics/parts.gif" alt="Main parts of the screen" width="426" height="186" /></p>
+<h3>Screen header<br /><br /><hr /></h3>
+<p><img src="graphics/screen-header.gif" alt="Screen header" width="445" height="54" /></p>
+<ul>
+ <li>The top portion provides links to University of Washington news and local weather. The top portion also displays error messages and comfirmation messages, such as confirming that you have moved or copied a message to a folder.</li>
+ <li>The right side provides a bar graph representing the portion of your account's disk space allocation that is currently used.</li>
+ <li>Below the bar graph are links for Settings (for customizing your mail), Help (for displaying this Help system), and Sign out.</li>
+</ul>
+<h3>Menu bar<br /><br /><hr /></h3>
+<p>The menu bar provides commands needed for each Alpine screen. The two leftmost commands, <a href="#check">Check Mail</a> and <a href="compose.html">Compose</a>, are the same in all Alpine screens. The other commands change according to which Alpine screen is dislayed.</p>
+<h3>Left pane<br /><br /><hr /></h3>
+<p>The left pane displays:</p>
+<ul>
+ <li><strong>Search.</strong> For a basic search, type a word in the search box and click the search icon. For more search options, click Advanced Search.</li>
+ <li><strong>Inbox and other default folders.</strong> Your inbox contains your mail as it arrives, you can use other folders to organize your mail.</li>
+ <li><strong>Contacts.</strong> Click to view and manage your contacts.</li>
+ <li><strong>Recent Folders.</strong> A list of the folders that you have recently accessed. You can control how many recent folders are listed (see Settings).</li>
+ <li><strong>View/Manage Folders.</strong> Click to view a list of all of your folders. From this list, you can add, delete, and rename folders.</li>
+ </ul>
+ <h3>Right pane<br /><br /><hr />
+ </h3>
+
+
+<p>Depending on what you are doing, the right pane displays one of five things:</p>
+<ul>
+ <li> <strong><a name="mlists" id="mlists"></a>Message lists.</strong> Display the contents of the folders in the left pane: Inbox, Drafts, Sent, and so forth. The message lists share a common format that allows you to view and manage your messages.</li>
+ <li><strong>The Compose window.</strong> Allows you to write and reply to messages.</li>
+ <li><strong>Your folder list</strong>. A list of all Alpine folders, both the standard Alpine folders such as Inbox, and any folders that you create. You can manage your folders from this list.</li>
+ <li><strong>Your contacts list.</strong> A list of all of the contacts that you have created. The contacts list functions as your address book.</li>
+ <li><strong>The Settings window.</strong> Allows you to customize the appearance and behavior of Alpine for your account.</li><br /><br />
+</ul>
+
+<h2><a name="compose" id="compose"></a>Parts of the Compose window</h2>
+<p>In the Compose window, you can write, send, and reply to messages. The following figure shows the main parts of the Compose window. </p>
+<p><img src="graphics/compose.gif" alt="Compose screen" /></p>
+<ul>
+ <li> <strong>Header section</strong> (at the top). You use the header section to address the mail and control the text format. </li>
+
+ <li><strong>Message section</strong> (below the header). The message section is where you enter and format message text. </li><br /><br />
+
+</ul>
+<h2><a name="basic" id="basic"></a>Basic Alpine Features</h2>
+<h3>Viewing messages<br /><hr /></h3>
+<p>When you first log in, your Inbox is displayed in the right pane, showing a list of your mail messages. </p>
+<ul>
+ <li>By default, the list contains 25 messages per page. Click the arrows on the right side of the menu bar to move from page to page.</li>
+ <li>You can sort messages in the list by clicking the column headings.</li>
+ <li>You can drag and drop messages from the right pane to the folders in the left pane or to your contacts: click and hold on the name or email address in the From column to drag a message.</li>
+ </ul>
+<p>For more information, see <a href="inbox.html">Your Inbox</a>.</p>
+<h3>Reading mail<br /><hr /></h3>
+
+<p>To read a message, click on its <strong>Subject</strong>. For more information, see <a href="read.html">Reading mail</a>.</p>
+
+<h3>Sending mail<br /><hr /></h3>
+
+To send a message, click <strong>Compose</strong> (or <strong>Reply</strong> or <strong>Forward</strong>) on the menu bar. For more information, see <a href="compose.html">Compose: sending mail</a>.
+
+<h3>Search<br /><hr /></h3>
+
+<p>To search for a message, type a search string in the <strong>Search</strong> box, at the top of the <a href="#main">left pane</a>. Click <strong>Advanced Search</strong> for more search options. For more information, see <a href="search.html">Search</a>.</p>
+
+<h3>Folders<br /><hr /></h3>
+
+<p>Folders help you keep your mail organized. Default folders, such as <strong>Inbox</strong> and <strong>Junk</strong>, are always there. You can also create your own folders by clicking <strong>View/Manage Folders</strong>. For more information, see <a href="folders.html">Folders</a>.</p>
+
+
+<h3>Contacts<br /><hr /></h3>
+
+<p>You can create a contact list,which is a list of addresses that are important or that you frequently use. Click <strong>Contacts</strong> to add items to your contact list. For more information, click <a href="contacts.html">Contacts</a>.</p>
+
+
+<h3><a name="check" id="check"></a>Checking for new mail<br /><hr /></h3>
+
+<p>You can check for new mail from any Alpine window or folder without returning to your Inbox.</p>
+
+<p><strong>To check for new mail </strong></p>
+<ul>
+ <li>On the menu bar, click <strong>Check Mail</strong>. Alpine checks for new mail. If you have any new mail, a message appears in the middle of the <a href="#main">screen header</a>.</li>
+ </ul>
+
+<h3>Settings<br /><hr /></h3>
+<p>You can control the appearance and behavior of many parts of the Alpine screen. For more information, see <a href="settings.html">Settings</a>.</p>
+<ul>
+
+</ul>
+<h3><a name="trash" id="trash"></a>Trash<br /><hr /></h3>
+<p>When you delete a message, it is moved to the trash. When you empty the trash or delete selected messages from the trash, they are removed permanently. You can also recover messages from the trash.</p>
+<p><strong>To empty the entire contents of the trash</strong></p>
+<ol>
+ <li>In the left pane, next to <strong>Trash</strong>, click <strong>[Empty]</strong>. </li>
+ <li>Click <strong>Empty Trash</strong> to confirm.</li>
+</ol>
+<p>This permanently deletes all of the messages in the trash.</p>
+<p><strong>To permanently delete selected messages from the trash</strong></p>
+<ol>
+ <li>In the left pane, click <strong>Trash</strong>.</li>
+ <li>Select the box to the left of the messages that you want to delete.</li>
+ <li>On the menu bar, click <strong>Delete Forever</strong>. Be sure that you have selected the right messages; there is no confirmation screen for this action.</li>
+</ol>
+<p><strong>To recover messages from the trash</strong></p>
+<ol>
+ <li>In the left pane, click Trash.</li>
+ <li>Select the box to the left of the messages that you want to recover.</li>
+ <li>On the menu bar, locate the <strong>Move</strong> or <strong>Copy</strong> button. Skip this step if the one you want is already dislayed. If necessary, click the button and pull down the menu to select <strong>Move</strong> or <strong>Copy</strong>.</li>
+ <li>On the menu bar, click <strong>to Folder</strong> and select the target folder from the list, or select <strong>More Folders</strong> to see a list of all folders. If you select <strong>More Folders</strong>, click the target folder in the list, and then click <strong>Copy</strong> or <strong>Move</strong>.</li>
+</ol>
+</div>
+</body>
+</html>
diff --git a/web/cgi/alpine/2.0/help/read.html b/web/cgi/alpine/2.0/help/read.html
new file mode 100644
index 00000000..155f4caf
--- /dev/null
+++ b/web/cgi/alpine/2.0/help/read.html
@@ -0,0 +1,145 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=iso-8859-1">
+<link type="text/css" rel="stylesheet" href="../css/help.css">
+<title>Reading mail</title>
+</head>
+<body>
+<div id="header">
+ <div class="logo"> </div>
+ <div class="nav"><a href="topic-list.html">All Help Topics</a> &nbsp;&nbsp;| &nbsp;&nbsp;<a href="alpha-index.html">Index</a> &nbsp;&nbsp;| &nbsp;&nbsp;<a href="javascript:self.close();">Close</a></div>
+</div>
+<div id="content">
+<h2>Reading mail</h2>
+
+<ul>
+ <li><a href="#view">Viewing a message</a></li>
+ <li><a href="#attachments">Viewing and downloading attachments</a></li>
+ <li><a href="#full">Displaying full headers</a></li>
+ <li><a href="#reply">Replying </a></li>
+ <li><a href="#forward">Forwarding </a></li>
+ <li><a href="#contacts">Adding contacts</a></li>
+ <li><a href="#folder">Saving messages to a folder</a></li>
+ <li><a href="#print">Printing </a></li>
+ <li><a href="#delete">Deleting a message</a></li>
+ <li><a href="#spam">Reporting a message as spam</a></li>
+ <br /><br />
+</ul>
+
+
+
+<h2><a name="view" id="view"></a>Viewing a message</h2>
+ <p><strong>To view a message</strong></p>
+<ul>
+
+ <li>Click its <strong>Subject</strong> in the list of messages.</li>
+<br /><br /></ul>
+<h2><a name="attachments" id="attachments"></a>Viewing and downloading attachments</h2>
+
+<p>Attachments are listed in the header area at the top of the message. Attachments are marked with the paper clip &nbsp;<img src="graphics/attach_sm.gif" alt="Attachment" width="17" height="17" />&nbsp; icon.</p>
+<p><strong>To open an attachment</strong></p>
+<ul>
+ <li>Click the file name.</li>
+</ul>
+<p><strong>To download an attachment</strong></p>
+<ol>
+ <li>Open the attachment.</li>
+ <li>In the application that opens the attachment, save a copy of the attachment to your computer.</li>
+ <br /><br />
+</ol>
+
+<h2><a name="full" id="header"></a>Displaying full headers</h2>
+<p>When you are viewing a message, the header section at the top of the message displays a standard set of headers including the message Subject, From, To, Cc, Date, and Attachments (if there are any). It is possible to show the full set of headers in the message, which includes a complete list of all routing from the message source to your Inbox, along with all other headers. These full headers may be useful in troubleshooting.</p>
+<p><strong>To display full headers</strong></p>
+<ul>
+ <li>In the top right corner of the message, click <strong>Show Full Headers</strong>. </li>
+</ul>
+<p><strong>To cancel full headers</strong></p>
+<ul>
+ <li>In the top right corner of the message, click <strong>Show Filtered Headers</strong>. </li>
+<br /><br /></ul>
+<h2><a name="reply" id="reply"></a>Replying </h2>
+<ol>
+ <li>On the menu bar, click <strong>Reply</strong> or <strong>Reply All</strong>.
+ <ul>
+ <li><strong>Reply.</strong> Sends a reply to the address or addresses in the To field.<br />
+ or</li>
+ <li><strong>Reply All.</strong> Sends a reply to the addresses in the <strong>To</strong> and <strong>Cc</strong> fields.</li>
+ <br /></ul>
+ </li>
+ <li>Proceed as for <a href="compose.html">sending mail</a><strong></strong>.</li>
+ </ol>
+
+<p>In either case the reply is not sent immediately. Instead, you are placed in the message composer with the addresses filled in.</p>
+<p>You can control the message format and other features of your replies. The <a href="settings.html#reply">Reply settings</a> allow you to select:</p>
+<ul>
+ <li>Whether or not to include headers</li>
+ <li>Whether or not to include attachments</li>
+ <li>Whether to place your signature above or below the replied to text</li>
+ <li>Whether or not to remove the signature from the replied to text</li>
+ <br />
+ <br />
+
+</ul>
+<h2><a name="forward" id="forward"></a>Forwarding </h2>
+<ol>
+ <li>On the menu bar, click <strong>Forward</strong>.</li>
+ <li>Proceed as for <a href="compose.html">sending mail</a>.</li>
+ </ol>
+
+<p>You can control whether to forward messages as inline text or as attachments. For more information, see <a href="settings.html#forward">Settings</a>.</p>
+
+<h2><a name="contacts" id="contacts"></a>Adding contacts</h2>
+<p>When you are reading a message, you can add the sender to your list of contacts.</p>
+
+<p><strong>To add a contact</strong></p>
+
+<ul>
+<li>Drag the contact's email address from the <strong>From</strong> field to the <strong>Contacts</strong> folder.
+<p><em>or</em></p></li>
+<li>To the right of the <strong>From</strong> field, click <strong>Add</strong>.</li>
+<br /><br /></ul>
+<p><strong>To add multiple contacts from a message</strong></p>
+<ul>
+ <li>On the menu bar, click <strong>More Actions</strong>, and then select <strong>Extract</strong>. Alpine extracts everything in the message that is in the format of an email address and presents a list of contacts. You can confirm each contact.</li>
+<br /><br /></ul>
+<p><strong>To finish adding a contact</strong></p>
+<ol>
+ <li>Enter the contact information:
+ <ul>
+ <li><strong>Nickname.</strong> A short name that you can use for quick addressing. If you type the nickname instead of the full display name or email address, Alpine will offer a list of matching choices from your contacts list and the University of Washington directory. You can select the contact from the list.</li>
+ <li><strong>Display Name.</strong> The name that appears in the message header.</li>
+ <li><strong>Email.</strong> The contact's email address.</li>
+ <li><strong>Notes.</strong> Any text you want. Used only by you.</li>
+ <li><strong>Fcc folder.</strong> A folder that will receive copies of all messages that you send directly to this contact.</li>
+ <br /></ul>
+ </li>
+ <li>Click <strong>Add</strong> to commit the contact to your address book.</li>
+<br /><br /></ol>
+<h2><a name="folder" id="folder"></a>Saving messages to a folder</h2>
+<p>You can move or copy messages to folders. Moving a message removes it from the Inbox while creating a copy in the designated folder. Copying a message leaves the message in the Inbox while creating a copy in the designated folder.</p>
+<ol>
+ <li>On the menu bar, locate the <strong>Move</strong> or <strong>Copy</strong> button. Skip this step if the one you want is already displayed. If necessary, click the button and pull down the menu to select <strong>Move</strong> or <strong>Copy</strong>.</li>
+ <li>On the menu bar, click <strong>to Folder</strong> and select the target folder from the list, or select <strong>More Folders</strong> to see a list of all folders. If you select <strong>More Folders</strong>, click the target folder in the list, and then click <strong>Copy</strong> or <strong>Move</strong>.</li>
+<br /><br /></ol>
+<h2><a name="print" id="print"></a>Printing</h2>
+<p><strong>To print a message</strong></p>
+<ul>
+ <li>In the area above the menu bar on the right click <strong>Print</strong>.</li>
+</ul>
+<h2><a name="delete" id="delete"></a>Deleting a message</h2>
+<ol>
+ <li>On the menu bar, click <strong>Delete</strong>.</li>
+</ol>
+
+<p>When you click <strong>Delete</strong>, the message you are viewing is moved to the Trash folder and you will be viewing the next message in your Inbox.</p>
+<h2><a name="spam" id="spam"></a>Reporting a message as spam</h2>
+<p>If you are reading a message that you think should have been identified by the spam detection processes but wasn't, you may submit it for review, which may help improve the effectiveness of spam detection. </p>
+<p><strong>To report spam</strong></p>
+<ol>
+ <li>On the menu bar, click <strong>Report Spam</strong>. A copy of the message is sent to the University of Washington spam detection process and the message is moved to your <strong>Junk</strong> folder.</li>
+<br /><br /></ol>
+</div>
+</body>
+</html>
diff --git a/web/cgi/alpine/2.0/help/search.html b/web/cgi/alpine/2.0/help/search.html
new file mode 100644
index 00000000..3518058e
--- /dev/null
+++ b/web/cgi/alpine/2.0/help/search.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=iso-8859-1">
+<link type="text/css" rel="stylesheet" href="../css/help.css">
+<title>Search</title>
+</head>
+<body>
+<div id="header">
+ <div class="logo"> </div>
+ <div class="nav"><a href="topic-list.html">All Help Topics</a> &nbsp;&nbsp;| &nbsp;&nbsp;<a href="alpha-index.html">Index</a> &nbsp;&nbsp;| &nbsp;&nbsp;<a href="javascript:self.close();">Close</a></div>
+</div>
+<div id="content">
+<h2>Search</h2>
+
+<ul>
+ <li><a href="#inbox">Basic Index Search</a></li>
+ <li><a href="#advanced">Advanced Index Search</a></li>
+ <li><a href="#example">Example</a></li>
+ <li><a href="#other">Other Searches (Contacts and Folder List)</a></li>
+<br /><br /></ul>
+
+<p>The <strong>Search</strong> area is in the upper portion of the left pane, just below the menu bar. At a minimum, there will be a text box for you to enter text to be searched for. If the right pane contains an index of messages there will also be an <strong>Advanced Search</strong> button. If you've already completed an index search and the right pane contains the Search Results then there are some additional button available. These are described below.</p>
+
+<h2><a name="search" id="search"></a>Basic Index Search</h2>
+
+<p>You may perform a basic search of all the messages in the index by typing some text into the search text box and clicking the search icon next to the box. This will be a search of the entire text of the messages, not just the Subject.</p>
+
+<p><strong>To search for messages</strong></p>
+<ul>
+ <li>Find the <strong>Search</strong> text box in the upper part of the left pane and click in it.</li>
+ <li>Type the search text into the box.</li>
+</ul>
+
+<p>The result of the search will be a message index containing only the subset of messages that match the search criterion. A message about the number of matches will be displayed in the Screen Header area and the heading will change from <strong>INBOX</strong> to <strong>Search Results in INBOX</strong> to show you that you are viewing a subset of the messages. Near the bottom of the page the total number of messages in the Search Result will be shown instead of the total number of messages in the INBOX.</p>
+
+<p>There will be a couple new items in the Search area of the screen. The highlighted <em>folder</em> in the search area will be called <strong>Search Results</strong>. You may go back and forth between the INBOX and the Search Results by clicking on the INBOX or on the Search Results. There will be a new button next to Advanced Search labeled <strong>Clear</strong>. Clicking that will clear the results of the search.</p>
+
+<p>You can start a brand new search by typing in more text and searching again. As long as the menu in the search area says <strong>New Search</strong> you will be searching the entire INBOX again, just like with the initial search. However, you also have the option of refining the search you have done so far by setting the menu to either <strong>Search within Results</strong> or <strong>Add to Search Results</strong>. A <strong>Search within Results</strong> search will reduce the size of the Search Results, showing you the subset of the current Search Results that also match your new search. An <strong>Add to Search Results</strong> search will increase the size of the Search Results, adding all of the new matches to the existing Search Results.</p>
+
+<p>If you are refining your Search Results and there are no matches, the Search Results will remain the same instead of showing nothing, so that you won't lose the previous results. A message in the Screen Header will say <strong>No messages matched your search</strong>.</p>
+
+<h2><a name="advanced" id="advanced"></a>Advanced Index Search</h2>
+<p>Clicking on <strong>Advanced Search</strong> allows you to more tightly control the method of searching for matching messages. For example, you could search for all messages where the Subject contains (or doesn't contain) a certain word. If you fill in more than one of the criteria they all have to match. For example, if you fill in some text that the Subject must contain <strong>and</strong> also type in some text in that the From must contain; the <strong>Search Results</strong> will only contain messages that both have a matching From and have a matching Subject.</p>
+
+<p>At the top of the <strong>Advanced Search</strong> panel is a menu similar to the one in the Basic search area where you say what sort of refinement on the current search you want to perform. This can be either a brand new search, a narrowing down of the current Search Results (<strong>search within</strong>), or a widening of the current Search Results (<strong>add results</strong>).</p>
+
+<h2><a name="example" id="example"></a>Example</h2>
+<p>Searching is often done iteratively, a step at a time, until you locate the message you are looking for.</p>
+<p>You might start out looking for a message that you can recall contains the word <strong>firefox</strong>.</p>
+
+<ul>
+ <li>Type <strong>firefox</strong> in the search text box click to search.</li>
+</ul>
+
+<p>You got way more matches than you expected so you try to think of some way to refine the search. You remember that the message also contains the word <strong>awesome</strong>.</p>
+
+<ul>
+ <li>Set the search menu to <strong>Search within Results</strong>.</li>
+ <li>Type <strong>awesome</strong> in the search text box and click to search.</li>
+</ul>
+
+<p>Maybe you see your message now or maybe you need to refine further. Suppose you know the message came within the past week.</p>
+
+<ul>
+ <li>Click <strong>Advanced Search</strong>.
+ <li>Be sure the choice at the top of the Search panel is set to <strong>Perform this search within current search results</strong>.
+ <li>Set the <strong>Date</strong> menu to <strong>Past week</strong>.
+ <li>Click <strong>Search</strong>.</li>
+<br /><br /></ul>
+
+<h2><a name="other" id="other"></a>Other Searches (Contacts and Folder List)</h2>
+<p>You can also perform basic searches while viewing your Folder List and while viewing your Contacts. In both cases, the search is a simple text search to match what you type, and only the text that is displayed on the screen is searched. The results are simply highlighted on the screen. You have to look for them yourself in order to use them.</p>
+
+</div>
+</body>
+</html>
diff --git a/web/cgi/alpine/2.0/help/settings.html b/web/cgi/alpine/2.0/help/settings.html
new file mode 100644
index 00000000..04dfa616
--- /dev/null
+++ b/web/cgi/alpine/2.0/help/settings.html
@@ -0,0 +1,124 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=iso-8859-1">
+<link type="text/css" rel="stylesheet" href="../css/help.css">
+<title>Settings</title>
+</head>
+<body>
+<div id="header">
+ <div class="logo"> </div>
+ <div class="nav"><a href="topic-list.html">All Help Topics</a> &nbsp;&nbsp;| &nbsp;&nbsp;<a href="alpha-index.html">Index</a> &nbsp;&nbsp;| &nbsp;&nbsp;<a href="javascript:self.close();">Close</a></div>
+</div>
+<div id="content">
+
+<h2>Settings: customizing Alpine</h2>
+
+<ul>
+ <li><a href="#display">Message Display</a></li>
+ <li><a href="#folders">Folders</a></li>
+ <li><a href="#reply">Reply Options</a></li>
+ <li><a href="#forward">Forwarding Options</a></li>
+ <li><a href="#fcc">Sent Message Options</a></li>
+ <li><a href="#personal">Personal Preferences</a></li>
+ <li><a href="#newsweather">News and Weather</a></li>
+ <li><a href="#msglist">Message List</a></li>
+ <li><a href="#msgview">Message View</a></li>
+ <li><a href="#foldernames">Folder Names</a></li>
+ <li><a href="#compose">Compose</a></li>
+ <li><a href="#mailservers">Mail Servers</a></li>
+ <li><a href="#dirservers">Directory Servers</a></li>
+</ul>
+
+
+<p>The settings pages allow you to customize the behavior and appearance of Alpine.</p>
+<p><a name="show" id="show"></a><strong>To display the Settings pages</strong></p>
+<ul><li>In the upper right-corner of the screen, above the menu bar, click <strong>Settings</strong>.</li>
+<br /><br />
+</ul>
+
+<h2><a name="display" id="display"></a>Message Display</h2>
+<ul>
+ <li><strong>Display ___ messages per page.</strong> Controls the number of messages displayed on each page.</li>
+ <li> <strong>Wrap Plain Text message at ___ characters.</strong> Controls the overall width of the text block in plain text messages.</li>
+<br /><br /></ul>
+<h2><a name="folders" id="folders"></a>Folders</h2>
+<ul>
+ <li><strong>Display ___ recent folders in left column.</strong> Controls how many folders are displayed under <strong>Recent Folders</strong> in the left pane. <strong>Recent Folders</strong> are the folders that you have recently accessed, listed with the most recent at the top.</li>
+<br /><br /></ul>
+<h2><a name="reply" id="reply"></a>Reply Options</h2>
+<ul>
+ <li><strong>Include headers in replies.</strong> Includes the header section of the original message in the reply.</li>
+ <li><strong>Include attachments in replies.</strong> The recipient of the reply gets the attachments as well as the original message.</li>
+ <li><strong>Append signature below reply text.</strong> Adds your signature to the bottom of the reply instead of the top.</li>
+ <li><strong>Strip signatures when replying.</strong> Removes the signature that was in the message being replied to (if it can be identified).</li>
+<br /><br /></ul>
+<h2><a name="forward" id="forward"></a>Forwarding Options</h2>
+<ul>
+ <li> <strong>Forward messages inline.</strong> The recipient gets an ordinary text message.</li>
+ <li><strong>Forward messages as attachments.</strong> The recipient gets a message with the forwarded message attached.</li>
+<br /><br /></ul>
+<h2><a name="fcc" id="fcc"></a>Sent Message Options</h2>
+<ul>
+ <li><strong>Name of your Sent mail folder.</strong> Save messages you send to this folder (default is sent-mail). To disable the saving of sent mail set this to double double quotes ("").</li>
+ <li><strong>Save sent messages to Sent folder without attachments.</strong> Remove any attachments being sent before saving the message in your Sent mail folder.</li>
+<br /><br /></ul>
+<h2><a name="personal" id="personal"></a>Personal Preferences</h2>
+<ul>
+ <li><strong>Display Name.</strong> This is your full name (e.g., Fred Flintstone). It will be included as part of your From address in outgoing mail.</li>
+ <li><strong>Email Signature.</strong> This signature is automatically included in messages you compose. You will have the opportunity to edit it before sending.</li>
+ <li><strong>Alternate Addresses.</strong> You may enter a list of alternate addresses so that Alpine can tell when an address is you. This will affect the index display (messages will be marked with an icon showing it is to you when it is addressed to one of your addresses), the From address displayed in the index display (when the From address is one of your addresses the To address will be shown instead), and the <strong>Reply All</strong> command (addresses listed here will not be included).</li>
+</ul>
+<p><strong>To add Alternate Addresses</strong></p>
+<ul><li>On the Personal Preferences page find the <strong>Alternate Addresses</strong> option. Type alternate addresses into the text field. The <strong>Add</strong> button can be used to add additional addresses and the <strong>X</strong> buttons can be used to remove addresses. The addresses you enter should be the actual email address part of an address without the fullname or brackets. For example, <strong>user@example.com</strong>. The matching is not sensitive to upper or lower case differences, all will match. For the advanced user, a regular expression may be entered.</li>
+<br /><br />
+</ul>
+<h2><a name="newsweather" id="newsweather"></a>News and Weather</h2>
+<ul>
+ <li><strong>Headline News.</strong> RSS URL for news.</li>
+ <li><strong>Weather Bar.</strong> RSS URL for weather.</li>
+<br /><br /></ul>
+<h2><a name="msglist" id="msglist"></a>Message List</h2>
+<ul>
+ <li><strong>Default Sort Order.</strong> Sets the default sort order for the Message List. By default the sort order is set to <strong>Date/Reverse</strong>. The <strong>/Reverse</strong> part means that the newest messages will be at the top. It is also possible to temporarily change the sort order in the message list screen itself.</li>
+ <li><strong>Start display at.</strong> This just tells Alpine where to start in the index of messages. The message you specify here will be included in the first page of messages you see.</li>
+ <li><strong>Automatically Move Read Messages.</strong> Automatically move read messages to a Read Messages folder.</li>
+<br /><br /></ul>
+
+<h2><a name="msgview" id="msgview"></a>Message View</h2>
+<ul>
+ <li><strong>Display Headers.</strong> Specify which headers are to be displayed in the Message View. If you specify any headers here you must list all of the headers you wish to have displayed, not just the additional headers.</li>
+ <li><strong>Display complete URLs as links.</strong> URLs contained in a message are shown as active links when viewing the message.</li>
+ <li><strong>Display hostnames and incomplete URLs as links.</strong> Hostnames that begin with <strong>www</strong> and incomplete URLs contained in a message are shown as active links when viewing the message.</li>
+ <li><strong>Display email addresses as links.</strong> Email addresses contained in a message are displayed as active links that can be used for sending mail.</li>
+ <li><strong>Rich Text Display.</strong> Turning on this option will cause rich text messages to be shown as plain text.</li>
+ <li><strong>Anti-phishing.</strong> Usually the real hostname is displayed after links to make it easier to see if the link looks suspicious or not. Turning on this option will hide that information and is not recommended.</li>
+<br /><br /></ul>
+<h2><a name="foldernames" id="foldernames"></a>Folder Names</h2>
+<ul>
+ <li><strong>Draft Folder.</strong> Actual name of the folder that is usually referred to in Alpine as the <strong>Drafts</strong> folder. The default is <strong>postponed-msgs</strong>.</li>
+ <li><strong>Trash Folder.</strong> Actual name of the folder that is usually referred to in Alpine as the <strong>Trash</strong> folder. The default is <strong>Trash</strong>.</li>
+<br /><br /></ul>
+<h2><a name="compose" id="compose"></a>Compose</h2>
+<ul>
+ <li><strong>Compose Headers.</strong> You can control which headers you want visible when composing outgoing email using this option. You can specify any of the regular set, any of the Headers you see with More Headers, or any Customized Headers that you have already defined. If you use this setting at all, you must specify all the headers you want to see, you can't just add to the regular header set. The default set is To:, Cc:, and Subject:.</li>
+ <li><strong>Custom Headers.</strong> You may add your own custom headers to outgoing messages. Each header you specify here must include the header tag (Reply-To:, Approved:, etc.) and may optionally include a value for that header. If you want to see these custom headers each time you compose a message, you must add them to your Compose Headers list, otherwise they become part of the rich header set that you only see when you press the More Headers command. (If you are looking for a way to change which headers are displayed when you view a message, take a look at the Display Headers option in the Message View section instead.)</li>
+ <li><strong>Message Encoding.</strong> Send messages using the character encoding you set here.</li>
+ <li><strong>Reply intro string.</strong> This option is used to customize the content of the introduction line that is included when replying to a message and including the original message in the reply. It is not yet described here.</li>
+ <li><strong>Reply prefix.</strong> When a message is replied to and the text of the message is included, the included text usually has the text &quot;> &quot; prepended to each line, to indicate it is quoted text. It is recommended that you leave this option set to that value since that is a standard that other email reading programs will recognize and treat specially.</li>
+ <li><strong>Flowed Text Handling.</strong> Normally, when sending a message, Alpine generates flowed text where possible. The method for generating flowed text is defined in RFC 3676, the benefit of doing so is to send message text that can properly be viewed both on normal width displays and on displays with smaller or larger than normal screen widths. This feature turns off the generation of flowed text, as it might be desired to more tightly control how a message is displayed on the receiving end.</li>
+<br /><br /></ul>
+<h2><a name="mailservers" id="mailservers"></a>Mail Servers</h2>
+<ul>
+ <li><strong>Inbox Server.</strong> Technical name of the folder that is your <strong>INBOX</strong> folder. This will usually have to be set for you. An example is <strong>{example.com}inbox</strong>. The server name comes inside the brackets and the folder name comes after the brackets.</li>
+ <li><strong>SMTP Server.</strong> A list (usually a list of size one) consisting of the names of your SMTP servers. An SMTP server is used for sending mail. This is a domainname such as<strong>smtp.example.com</strong> and it may contain some options after the hostname. The options are introduced with a slash (/) and the name of the option. To use the submit port append <strong>/submit</strong> after the hostname. To use authentication append <strong>/user=username</strong> where <strong>username</strong> is the name you use to authenticate to the server. A more complicated example might look something like <strong>smtp.example.com/submit/user=myusername</strong>.<li>
+<br /><br /></ul>
+<h2><a name="dirservers" id="dirservers"></a>Directory Servers</h2>
+<ul>
+ <li><strong>LDAP Server.</strong> This option is currently too complicated to be set from within Web Alpine. The value it is looking for has the format of a raw ldap-servers config string found in an Alpine pinerc configuration file.</li>
+<br /><br /></ul>
+
+<p>&nbsp;</p>
+</div>
+</body>
+</html>
diff --git a/web/cgi/alpine/2.0/help/topic-list.html b/web/cgi/alpine/2.0/help/topic-list.html
new file mode 100644
index 00000000..6b75bf07
--- /dev/null
+++ b/web/cgi/alpine/2.0/help/topic-list.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=iso-8859-1">
+<link type="text/css" rel="stylesheet" href="../css/help.css">
+<title>All Help Topics</title>
+</head>
+<body>
+<div id="header">
+ <div class="logo"> </div>
+ <div class="nav"><a href="alpha-index.html">Index</a> &nbsp;&nbsp;| &nbsp;&nbsp;<a href="javascript:history.back()">Go Back</a> &nbsp;&nbsp;| &nbsp;&nbsp;<a href="javascript:self.close();">Close</a></div>
+</div>
+<div id="content">
+
+
+ <h2>All Help Topics</h2>
+ <dl>
+ <dt><a href="quick-start.html">Alpine Quick Start</a></dt>
+ <dd>
+ <a href="quick-start.html#main">Main parts of the Alpine screen</a> -
+ <a href="quick-start.html#compose">Parts of the Compose window</a> -
+ <a href="quick-start.html#basic">Basic Alpine Features</a></li>
+ </dd>
+ <dt><a href="inbox.html">Your Inbox</a></dt>
+ <dd><a href="inbox.html#view">Viewing messages</a> -
+ <a href="inbox.html#sort">Sorting messages</a> -
+ <a href="inbox.html#delete">Deleting messages</a> -
+ <a href="inbox.html#folder">Saving messages to a folder</a> -
+ <a href="inbox.html#icons">Icons</a> -
+ <a href="inbox.html#stars">Marking messages with stars</a> -
+ <a href="inbox.html#spam">Reporting messages as spam</a> -
+ <a href="inbox.html#trash">Emptying trash</a> -
+ <a href="inbox.html#drag">Drag and Drop Shortcuts</a>
+ </dd>
+ <dt><a href="read.html">Reading mail</a></dt>
+ <dd>
+
+ <a href="read.html#view">Viewing a message</a> -
+ <a href="read.html#attachments">Viewing and downloading attachments</a> -
+ <a href="read.html#full">Displaying full headers</a> -
+ <a href="read.html#reply">Replying </a> -
+ <a href="read.html#forward">Forwarding </a> -
+ <a href="read.html#contacts">Adding contacts</a> -
+ <a href="read.html#folder">Saving messages to a folder</a> -
+ <a href="read.html#print">Printing </a> -
+ <a href="read.html#delete">Deleting a message </a> -
+ <a href="read.html#spam">Reporting a message as spam</a>
+ </dd>
+ <dt><a href="compose.html">Compose: sending mail</a></dt>
+ <dd>
+
+ <a href="compose.html#writing">Writing messages</a> -
+ <a href="compose.html#attachments">Including attachments</a> -
+ <a href="compose.html#sending">Sending, canceling, and saving drafts</a> -
+ <a href="compose.html#resuming">Resuming drafts</a> -
+ <a href="compose.html#headers">Headers</a> -
+ <a href="compose.html#autosave">Automatic saves</a>
+
+ </dd>
+ <dt><a href="search.html">Search</a></dt>
+ <dd>
+
+ <a href="search.html#inbox">Basic Index Search</a> -
+ <a href="search.html#advanced">Advanced Index Search</a> -
+ <a href="search.html#example">Example</a> -
+ <a href="search.html#other">Other Searches (Contacts and Folder List)</a>
+
+ </dd>
+ <dt><a href="folders.html">Folders: organizing your mail</a></dt>
+ <dd>
+
+ <a href="folders.html#view">Viewing your list of folders</a> -
+ <a href="folders.html#viewing">Viewing the messages in a folder</a> -
+ <a href="folders.html#add">Adding folders</a> -
+ <a href="folders.html#delete">Deleting folders</a> -
+ <a href="folders.html#rename">Renaming folders</a> -
+ <a href="folders.html#route">Routing mail to folders</a>
+
+ </dd>
+<dt><a href="settings.html">Settings: customizing Alpine</a></dt>
+ <dd>
+ <a href="settings.html#display">Message Display</a> -
+ <a href="settings.html#folders">Folders</a> -
+ <a href="settings.html#reply">Reply Options</a> -
+ <a href="settings.html#forward">Forwarding Options</a> -
+ <a href="settings.html#fcc">Sent Message Options</a> -
+ <a href="settings.html#personal">Personal Preferences</a> -
+ <a href="settings.html#newsweather">News and Weather</a> -
+ <a href="settings.html#msglist">Message List</a> -
+ <a href="settings.html#msgview">Message View</a> -
+ <a href="settings.html#folders">Folders</a> -
+ <a href="settings.html#compose">Compose</a> -
+ <a href="settings.html#mailservers">Mail Servers</a> -
+ <a href="settings.html#dirservers">Direcory Servers</a>
+ </dd>
+<dt><a href="alpha-index.html">Index</a></dt>
+ <dd>&nbsp; </dd>
+ </dl>
+</div>
+</body>
+</html>
diff --git a/web/cgi/alpine/2.0/img/cbn/Thumbs.db b/web/cgi/alpine/2.0/img/cbn/Thumbs.db
new file mode 100644
index 00000000..1c7332e2
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/Thumbs.db
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/_notes/dwsync.xml b/web/cgi/alpine/2.0/img/cbn/_notes/dwsync.xml
new file mode 100644
index 00000000..a15e49fc
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/_notes/dwsync.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<dwsync>
+<file name="addcontact_sm.gif" local="128322025280000000" remote="128413808870000000" testing="0" />
+<file name="addfolder.gif" local="128324595100000000" remote="128413808870000000" testing="0" />
+<file name="attach.gif" local="128408549140000000" remote="128413808880000000" testing="0" />
+<file name="attach_sm.gif" local="128285018180000000" remote="128413808880000000" testing="0" />
+<file name="base.gif" local="126795687600000000" remote="128413808890000000" testing="0" />
+<file name="btnbg.gif" local="128149620340000000" remote="128413808890000000" testing="0" />
+<file name="btnhi.gif" local="128322774920000000" remote="128413808890000000" testing="0" />
+<file name="checkmail.gif" local="128408580420000000" remote="128413808900000000" testing="0" />
+<file name="close.gif" local="128287413900000000" remote="128413808900000000" testing="0" />
+<file name="col.gif" local="128282447240000000" remote="128413808900000000" testing="0" />
+<file name="colsel.gif" local="128407627840000000" remote="128413808910000000" testing="0" />
+<file name="compose.gif" local="128408546020000000" remote="128413808910000000" testing="0" />
+<file name="contacts.gif" local="128408580180000000" remote="128413808910000000" testing="0" />
+<file name="dawg.gif" local="128346358620000000" remote="128413808920000000" testing="0" />
+<file name="delete.gif" local="128408543180000000" remote="128413808920000000" testing="0" />
+<file name="detach.gif" local="128291071260000000" remote="128413808930000000" testing="0" />
+<file name="div.gif" local="128242883360000000" remote="128413808930000000" testing="0" />
+<file name="div2.gif" local="128242883360000000" remote="128413808930000000" testing="0" />
+<file name="dn.gif" local="128311444180000000" remote="128413808940000000" testing="0" />
+<file name="edit.gif" local="128408579240000000" remote="128413808940000000" testing="0" />
+<file name="fldsel.gif" local="128329923120000000" remote="128413808940000000" testing="0" />
+<file name="folder.gif" local="126795687600000000" remote="128413808950000000" testing="0" />
+<file name="folderopen.gif" local="126795687600000000" remote="128413808950000000" testing="0" />
+<file name="forward.gif" local="128408538380000000" remote="128413808950000000" testing="0" />
+<file name="fwd.gif" local="128297060120000000" remote="128413808960000000" testing="0" />
+<file name="f_contacts.gif" local="128408575980000000" remote="128413808960000000" testing="0" />
+<file name="f_delete.gif" local="128335113800000000" remote="128413808970000000" testing="0" />
+<file name="f_drafts.gif" local="128285005940000000" remote="128413808970000000" testing="0" />
+<file name="f_folder.gif" local="128335113040000000" remote="128413808970000000" testing="0" />
+<file name="f_inbox.gif" local="128408820000000000" remote="128413808980000000" testing="0" />
+<file name="f_manage.gif" local="128286587360000000" remote="128413808980000000" testing="0" />
+<file name="f_minus.gif" local="128408616920000000" remote="128413808990000000" testing="0" />
+<file name="f_plus.gif" local="128408615620000000" remote="128413808990000000" testing="0" />
+<file name="f_search.gif" local="128348118840000000" remote="128413808990000000" testing="0" />
+<file name="f_sent.gif" local="128408819900000000" remote="128413809000000000" testing="0" />
+<file name="f_spam.gif" local="128284929720000000" remote="128413809000000000" testing="0" />
+<file name="group_contact.gif" local="128408576620000000" remote="128413809010000000" testing="0" />
+<file name="help.gif" local="128408588340000000" remote="128413809010000000" testing="0" />
+<file name="help_sm.gif" local="128395563540000000" remote="128413809010000000" testing="0" />
+<file name="high.gif" local="128397216540000000" remote="128413809020000000" testing="0" />
+<file name="highest.gif" local="128397216560000000" remote="128413809020000000" testing="0" />
+<file name="inbox.gif" local="128408627500000000" remote="128413809020000000" testing="0" />
+<file name="info.gif" local="128285031840000000" remote="128413809030000000" testing="0" />
+<file name="infomsg.gif" local="126795687600000000" remote="128413809030000000" testing="0" />
+<file name="lo.gif" local="128242883560000000" remote="128413809040000000" testing="0" />
+<file name="logo.gif" local="128268543280000000" remote="128413809040000000" testing="0" />
+<file name="logo.png" local="128268542820000000" remote="128413809040000000" testing="0" />
+<file name="lookup.gif" local="128408547020000000" remote="128413809050000000" testing="0" />
+<file name="low.gif" local="128287410740000000" remote="128413809050000000" testing="0" />
+<file name="menu.gif" local="128294434100000000" remote="128413809060000000" testing="0" />
+<file name="musicfolder.gif" local="126795687600000000" remote="128413809060000000" testing="0" />
+<file name="navbg.gif" local="128149620340000000" remote="128413809070000000" testing="0" />
+<file name="new.gif" local="128297901460000000" remote="128413809070000000" testing="0" />
+<file name="page.gif" local="126795687600000000" remote="128413809070000000" testing="0" />
+<file name="parent.gif" local="128334255560000000" remote="128413809080000000" testing="0" />
+<file name="partly.gif" local="128286602780000000" remote="128413809080000000" testing="0" />
+<file name="pg_down.gif" local="128296919540000000" remote="128413809080000000" testing="0" />
+<file name="pg_first.gif" local="128296917000000000" remote="128413809090000000" testing="0" />
+<file name="pg_last.gif" local="128296918820000000" remote="128413809090000000" testing="0" />
+<file name="pg_next.gif" local="128296918060000000" remote="128413809100000000" testing="0" />
+<file name="pg_prev.gif" local="128296917260000000" remote="128413809100000000" testing="0" />
+<file name="pg_up.gif" local="128296920120000000" remote="128413809100000000" testing="0" />
+<file name="print.gif" local="128408544200000000" remote="128413809110000000" testing="0" />
+<file name="remove.gif" local="128390294960000000" remote="128413809110000000" testing="0" />
+<file name="rename.gif" local="128408558040000000" remote="128413809110000000" testing="0" />
+<file name="replied.gif" local="128297052960000000" remote="128413809120000000" testing="0" />
+<file name="replied_and_fwd.gif" local="128315874020000000" remote="128413809120000000" testing="0" />
+<file name="reply.gif" local="128408531760000000" remote="128413809130000000" testing="0" />
+<file name="replyall.gif" local="128408531880000000" remote="128413809130000000" testing="0" />
+<file name="return.gif" local="128348041200000000" remote="128413809130000000" testing="0" />
+<file name="rt.gif" local="128354170440000000" remote="128413809140000000" testing="0" />
+<file name="save.gif" local="128408602740000000" remote="128413809140000000" testing="0" />
+<file name="search.gif" local="128346326260000000" remote="128413809150000000" testing="0" />
+<file name="send.gif" local="128408579860000000" remote="128413809150000000" testing="0" />
+<file name="settings.gif" local="128322068820000000" remote="128413809160000000" testing="0" />
+<file name="sound.gif" local="128359185200000000" remote="128413809160000000" testing="0" />
+<file name="spam.gif" local="128408542660000000" remote="128413809160000000" testing="0" />
+<file name="spell.gif" local="128287375540000000" remote="128413809170000000" testing="0" />
+<file name="star.gif" local="128407651700000000" remote="128413809180000000" testing="0" />
+<file name="starred.gif" local="128408642720000000" remote="128413809180000000" testing="0" />
+<file name="sunny.gif" local="128286601840000000" remote="128413809180000000" testing="0" />
+<file name="trans.gif" local="128242883360000000" remote="128413809190000000" testing="0" />
+<file name="up.gif" local="128311446360000000" remote="128413809190000000" testing="0" />
+<file name="uw.gif" local="128268544420000000" remote="128413809190000000" testing="0" />
+<file name="uwlogo.gif" local="128300589020000000" remote="128413809200000000" testing="0" />
+<file name="wbar.gif" local="128285214500000000" remote="128413809200000000" testing="0" />
+<file name="msglist.gif" local="128408824100000000" remote="128413809060000000" testing="0" />
+<file name="spam2.gif" local="128408777680000000" remote="128413809170000000" testing="0" />
+</dwsync> \ No newline at end of file
diff --git a/web/cgi/alpine/2.0/img/cbn/addcontact_sm.gif b/web/cgi/alpine/2.0/img/cbn/addcontact_sm.gif
new file mode 100644
index 00000000..cda1baf5
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/addcontact_sm.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/addfolder.gif b/web/cgi/alpine/2.0/img/cbn/addfolder.gif
new file mode 100644
index 00000000..81b5c230
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/addfolder.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/alert.gif b/web/cgi/alpine/2.0/img/cbn/alert.gif
new file mode 100644
index 00000000..e5726c12
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/alert.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/alpinelogo.gif b/web/cgi/alpine/2.0/img/cbn/alpinelogo.gif
new file mode 100644
index 00000000..bb68de83
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/alpinelogo.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/attach.gif b/web/cgi/alpine/2.0/img/cbn/attach.gif
new file mode 100644
index 00000000..ea0829a2
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/attach.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/attach_sm.gif b/web/cgi/alpine/2.0/img/cbn/attach_sm.gif
new file mode 100644
index 00000000..cdf108cc
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/attach_sm.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/base.gif b/web/cgi/alpine/2.0/img/cbn/base.gif
new file mode 100644
index 00000000..eb129763
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/base.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/blank.gif b/web/cgi/alpine/2.0/img/cbn/blank.gif
new file mode 100644
index 00000000..e565824a
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/blank.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/border-bl.gif b/web/cgi/alpine/2.0/img/cbn/border-bl.gif
new file mode 100644
index 00000000..0e27a994
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/border-bl.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/border-br.gif b/web/cgi/alpine/2.0/img/cbn/border-br.gif
new file mode 100644
index 00000000..494e4fdd
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/border-br.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/border-ft.gif b/web/cgi/alpine/2.0/img/cbn/border-ft.gif
new file mode 100644
index 00000000..201d1be3
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/border-ft.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/border-lt-2.gif b/web/cgi/alpine/2.0/img/cbn/border-lt-2.gif
new file mode 100644
index 00000000..445d1984
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/border-lt-2.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/border-lt.gif b/web/cgi/alpine/2.0/img/cbn/border-lt.gif
new file mode 100644
index 00000000..765c028e
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/border-lt.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/border-rt-2.gif b/web/cgi/alpine/2.0/img/cbn/border-rt-2.gif
new file mode 100644
index 00000000..93d40267
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/border-rt-2.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/border-rt.gif b/web/cgi/alpine/2.0/img/cbn/border-rt.gif
new file mode 100644
index 00000000..dc2f5661
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/border-rt.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/border-tl.gif b/web/cgi/alpine/2.0/img/cbn/border-tl.gif
new file mode 100644
index 00000000..64c223e9
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/border-tl.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/border-tr.gif b/web/cgi/alpine/2.0/img/cbn/border-tr.gif
new file mode 100644
index 00000000..10ce91ed
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/border-tr.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/btnbg.gif b/web/cgi/alpine/2.0/img/cbn/btnbg.gif
new file mode 100644
index 00000000..3cbf352a
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/btnbg.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/btnhi.gif b/web/cgi/alpine/2.0/img/cbn/btnhi.gif
new file mode 100644
index 00000000..ac407ae2
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/btnhi.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/but_back.gif b/web/cgi/alpine/2.0/img/cbn/but_back.gif
new file mode 100644
index 00000000..899f9832
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/but_back.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/ccme.gif b/web/cgi/alpine/2.0/img/cbn/ccme.gif
new file mode 100644
index 00000000..3b6de47f
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/ccme.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/check2.gif b/web/cgi/alpine/2.0/img/cbn/check2.gif
new file mode 100644
index 00000000..5f1149ae
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/check2.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/checkmail.gif b/web/cgi/alpine/2.0/img/cbn/checkmail.gif
new file mode 100644
index 00000000..41c37d5c
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/checkmail.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/close.gif b/web/cgi/alpine/2.0/img/cbn/close.gif
new file mode 100644
index 00000000..69183a09
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/close.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/close2.gif b/web/cgi/alpine/2.0/img/cbn/close2.gif
new file mode 100644
index 00000000..8ac7e070
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/close2.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/close3.gif b/web/cgi/alpine/2.0/img/cbn/close3.gif
new file mode 100644
index 00000000..b8d46aa4
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/close3.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/col.gif b/web/cgi/alpine/2.0/img/cbn/col.gif
new file mode 100644
index 00000000..674de394
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/col.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/colsel.gif b/web/cgi/alpine/2.0/img/cbn/colsel.gif
new file mode 100644
index 00000000..fab172f2
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/colsel.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/compose.gif b/web/cgi/alpine/2.0/img/cbn/compose.gif
new file mode 100644
index 00000000..97adb285
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/compose.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/contacts.gif b/web/cgi/alpine/2.0/img/cbn/contacts.gif
new file mode 100644
index 00000000..3992f1d1
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/contacts.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/dawg.gif b/web/cgi/alpine/2.0/img/cbn/dawg.gif
new file mode 100644
index 00000000..7d8fd606
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/dawg.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/dbar.gif b/web/cgi/alpine/2.0/img/cbn/dbar.gif
new file mode 100644
index 00000000..95bcde54
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/dbar.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/delete.gif b/web/cgi/alpine/2.0/img/cbn/delete.gif
new file mode 100644
index 00000000..2a160927
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/delete.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/detach.gif b/web/cgi/alpine/2.0/img/cbn/detach.gif
new file mode 100644
index 00000000..939f8fb1
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/detach.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/div.gif b/web/cgi/alpine/2.0/img/cbn/div.gif
new file mode 100644
index 00000000..51d339b5
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/div.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/div2.gif b/web/cgi/alpine/2.0/img/cbn/div2.gif
new file mode 100644
index 00000000..c4982c4a
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/div2.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/dn.gif b/web/cgi/alpine/2.0/img/cbn/dn.gif
new file mode 100644
index 00000000..0274736c
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/dn.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/edit.gif b/web/cgi/alpine/2.0/img/cbn/edit.gif
new file mode 100644
index 00000000..d2ecc2ef
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/edit.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/exclaim.gif b/web/cgi/alpine/2.0/img/cbn/exclaim.gif
new file mode 100644
index 00000000..e67522cd
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/exclaim.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/f_contacts.gif b/web/cgi/alpine/2.0/img/cbn/f_contacts.gif
new file mode 100644
index 00000000..a85a8ea2
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/f_contacts.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/f_delete.gif b/web/cgi/alpine/2.0/img/cbn/f_delete.gif
new file mode 100644
index 00000000..9c0de43a
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/f_delete.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/f_drafts.gif b/web/cgi/alpine/2.0/img/cbn/f_drafts.gif
new file mode 100644
index 00000000..23a6219d
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/f_drafts.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/f_folder.gif b/web/cgi/alpine/2.0/img/cbn/f_folder.gif
new file mode 100644
index 00000000..6e44b4e8
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/f_folder.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/f_inbox.gif b/web/cgi/alpine/2.0/img/cbn/f_inbox.gif
new file mode 100644
index 00000000..609c6be7
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/f_inbox.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/f_manage.gif b/web/cgi/alpine/2.0/img/cbn/f_manage.gif
new file mode 100644
index 00000000..b242c8a9
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/f_manage.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/f_minus.gif b/web/cgi/alpine/2.0/img/cbn/f_minus.gif
new file mode 100644
index 00000000..7071aa5a
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/f_minus.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/f_plus.gif b/web/cgi/alpine/2.0/img/cbn/f_plus.gif
new file mode 100644
index 00000000..1d35602f
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/f_plus.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/f_search.gif b/web/cgi/alpine/2.0/img/cbn/f_search.gif
new file mode 100644
index 00000000..6193a3b3
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/f_search.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/f_sent.gif b/web/cgi/alpine/2.0/img/cbn/f_sent.gif
new file mode 100644
index 00000000..8812a3f3
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/f_sent.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/f_spam.gif b/web/cgi/alpine/2.0/img/cbn/f_spam.gif
new file mode 100644
index 00000000..db33fe25
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/f_spam.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/fldsel.gif b/web/cgi/alpine/2.0/img/cbn/fldsel.gif
new file mode 100644
index 00000000..2d0181b7
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/fldsel.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/folder.gif b/web/cgi/alpine/2.0/img/cbn/folder.gif
new file mode 100644
index 00000000..eb129763
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/folder.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/folderopen.gif b/web/cgi/alpine/2.0/img/cbn/folderopen.gif
new file mode 100644
index 00000000..c5c31102
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/folderopen.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/foldersopen.gif b/web/cgi/alpine/2.0/img/cbn/foldersopen.gif
new file mode 100644
index 00000000..43ce5978
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/foldersopen.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/forward.gif b/web/cgi/alpine/2.0/img/cbn/forward.gif
new file mode 100644
index 00000000..aa513040
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/forward.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/fwd.gif b/web/cgi/alpine/2.0/img/cbn/fwd.gif
new file mode 100644
index 00000000..546ff6a3
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/fwd.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/group_contact.gif b/web/cgi/alpine/2.0/img/cbn/group_contact.gif
new file mode 100644
index 00000000..fe47f2b9
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/group_contact.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/help.gif b/web/cgi/alpine/2.0/img/cbn/help.gif
new file mode 100644
index 00000000..8eea6cbe
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/help.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/help_sm.gif b/web/cgi/alpine/2.0/img/cbn/help_sm.gif
new file mode 100644
index 00000000..97c369c7
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/help_sm.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/high.gif b/web/cgi/alpine/2.0/img/cbn/high.gif
new file mode 100644
index 00000000..a4d52bf4
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/high.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/highest.gif b/web/cgi/alpine/2.0/img/cbn/highest.gif
new file mode 100644
index 00000000..79df8035
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/highest.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/inbox.gif b/web/cgi/alpine/2.0/img/cbn/inbox.gif
new file mode 100644
index 00000000..dcf5e684
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/inbox.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/info.gif b/web/cgi/alpine/2.0/img/cbn/info.gif
new file mode 100644
index 00000000..9fdf8503
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/info.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/infomsg.gif b/web/cgi/alpine/2.0/img/cbn/infomsg.gif
new file mode 100644
index 00000000..5fa3d9ca
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/infomsg.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/lo.gif b/web/cgi/alpine/2.0/img/cbn/lo.gif
new file mode 100644
index 00000000..817eaeb2
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/lo.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/logo.gif b/web/cgi/alpine/2.0/img/cbn/logo.gif
new file mode 100644
index 00000000..ea072725
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/logo.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/logo.png b/web/cgi/alpine/2.0/img/cbn/logo.png
new file mode 100644
index 00000000..e2ba8952
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/logo.png
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/logoff.gif b/web/cgi/alpine/2.0/img/cbn/logoff.gif
new file mode 100644
index 00000000..cf1fba05
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/logoff.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/lookup.gif b/web/cgi/alpine/2.0/img/cbn/lookup.gif
new file mode 100644
index 00000000..a9ab6dcb
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/lookup.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/low.gif b/web/cgi/alpine/2.0/img/cbn/low.gif
new file mode 100644
index 00000000..f2924496
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/low.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/menu.gif b/web/cgi/alpine/2.0/img/cbn/menu.gif
new file mode 100644
index 00000000..f763c448
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/menu.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/msglist.gif b/web/cgi/alpine/2.0/img/cbn/msglist.gif
new file mode 100644
index 00000000..ad7c9b47
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/msglist.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/msglist.gif.bak b/web/cgi/alpine/2.0/img/cbn/msglist.gif.bak
new file mode 100644
index 00000000..ed77a81f
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/msglist.gif.bak
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/musicfolder.gif b/web/cgi/alpine/2.0/img/cbn/musicfolder.gif
new file mode 100644
index 00000000..f620789f
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/musicfolder.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/navbg.gif b/web/cgi/alpine/2.0/img/cbn/navbg.gif
new file mode 100644
index 00000000..3cbf352a
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/navbg.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/new.gif b/web/cgi/alpine/2.0/img/cbn/new.gif
new file mode 100644
index 00000000..abc16be0
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/new.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/page.gif b/web/cgi/alpine/2.0/img/cbn/page.gif
new file mode 100644
index 00000000..42d7318c
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/page.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/parent.gif b/web/cgi/alpine/2.0/img/cbn/parent.gif
new file mode 100644
index 00000000..48e54a65
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/parent.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/partly.gif b/web/cgi/alpine/2.0/img/cbn/partly.gif
new file mode 100644
index 00000000..858c6dca
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/partly.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/pg_down.gif b/web/cgi/alpine/2.0/img/cbn/pg_down.gif
new file mode 100644
index 00000000..9fba9e1b
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/pg_down.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/pg_first.gif b/web/cgi/alpine/2.0/img/cbn/pg_first.gif
new file mode 100644
index 00000000..fdd4784c
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/pg_first.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/pg_last.gif b/web/cgi/alpine/2.0/img/cbn/pg_last.gif
new file mode 100644
index 00000000..10f45d49
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/pg_last.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/pg_next.gif b/web/cgi/alpine/2.0/img/cbn/pg_next.gif
new file mode 100644
index 00000000..15a3b5fd
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/pg_next.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/pg_prev.gif b/web/cgi/alpine/2.0/img/cbn/pg_prev.gif
new file mode 100644
index 00000000..7db4db96
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/pg_prev.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/pg_up.gif b/web/cgi/alpine/2.0/img/cbn/pg_up.gif
new file mode 100644
index 00000000..e9f48410
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/pg_up.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/print.gif b/web/cgi/alpine/2.0/img/cbn/print.gif
new file mode 100644
index 00000000..69f75b53
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/print.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/question.gif b/web/cgi/alpine/2.0/img/cbn/question.gif
new file mode 100644
index 00000000..90d5be3e
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/question.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/question.jpg b/web/cgi/alpine/2.0/img/cbn/question.jpg
new file mode 100644
index 00000000..a62edb48
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/question.jpg
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/question.png b/web/cgi/alpine/2.0/img/cbn/question.png
new file mode 100644
index 00000000..82f3c144
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/question.png
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/question2.jpg b/web/cgi/alpine/2.0/img/cbn/question2.jpg
new file mode 100644
index 00000000..0a36fe45
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/question2.jpg
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/remove.gif b/web/cgi/alpine/2.0/img/cbn/remove.gif
new file mode 100644
index 00000000..fb1a338a
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/remove.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/rename.gif b/web/cgi/alpine/2.0/img/cbn/rename.gif
new file mode 100644
index 00000000..3cedb691
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/rename.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/replied.gif b/web/cgi/alpine/2.0/img/cbn/replied.gif
new file mode 100644
index 00000000..8ff06835
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/replied.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/replied_and_fwd.gif b/web/cgi/alpine/2.0/img/cbn/replied_and_fwd.gif
new file mode 100644
index 00000000..b3dd5a1b
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/replied_and_fwd.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/reply.gif b/web/cgi/alpine/2.0/img/cbn/reply.gif
new file mode 100644
index 00000000..c51ca60b
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/reply.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/replyall.gif b/web/cgi/alpine/2.0/img/cbn/replyall.gif
new file mode 100644
index 00000000..88d840ec
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/replyall.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/return.gif b/web/cgi/alpine/2.0/img/cbn/return.gif
new file mode 100644
index 00000000..0c04ee77
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/return.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/rt.gif b/web/cgi/alpine/2.0/img/cbn/rt.gif
new file mode 100644
index 00000000..bff6423d
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/rt.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/save.gif b/web/cgi/alpine/2.0/img/cbn/save.gif
new file mode 100644
index 00000000..5f3ef2c7
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/save.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/search.gif b/web/cgi/alpine/2.0/img/cbn/search.gif
new file mode 100644
index 00000000..1bef6fac
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/search.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/send.gif b/web/cgi/alpine/2.0/img/cbn/send.gif
new file mode 100644
index 00000000..2c140bdf
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/send.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/settings.gif b/web/cgi/alpine/2.0/img/cbn/settings.gif
new file mode 100644
index 00000000..9889b4fd
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/settings.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/sound.gif b/web/cgi/alpine/2.0/img/cbn/sound.gif
new file mode 100644
index 00000000..b9bd62aa
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/sound.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/spam.gif b/web/cgi/alpine/2.0/img/cbn/spam.gif
new file mode 100644
index 00000000..6bef9cea
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/spam.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/spam2.gif b/web/cgi/alpine/2.0/img/cbn/spam2.gif
new file mode 100644
index 00000000..569183a1
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/spam2.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/spell.gif b/web/cgi/alpine/2.0/img/cbn/spell.gif
new file mode 100644
index 00000000..79e7a1d3
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/spell.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/spritelib.gif b/web/cgi/alpine/2.0/img/cbn/spritelib.gif
new file mode 100644
index 00000000..56be9494
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/spritelib.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/spritelib.png b/web/cgi/alpine/2.0/img/cbn/spritelib.png
new file mode 100644
index 00000000..4b067efd
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/spritelib.png
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/star.gif b/web/cgi/alpine/2.0/img/cbn/star.gif
new file mode 100644
index 00000000..5a89f298
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/star.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/starred.gif b/web/cgi/alpine/2.0/img/cbn/starred.gif
new file mode 100644
index 00000000..c2622b01
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/starred.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/statbak.gif b/web/cgi/alpine/2.0/img/cbn/statbak.gif
new file mode 100644
index 00000000..a10a496f
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/statbak.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/statbakend.gif b/web/cgi/alpine/2.0/img/cbn/statbakend.gif
new file mode 100644
index 00000000..577c4ad8
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/statbakend.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/statbakl.gif b/web/cgi/alpine/2.0/img/cbn/statbakl.gif
new file mode 100644
index 00000000..4a95ed64
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/statbakl.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/statbakr.gif b/web/cgi/alpine/2.0/img/cbn/statbakr.gif
new file mode 100644
index 00000000..595f6177
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/statbakr.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/sunny.gif b/web/cgi/alpine/2.0/img/cbn/sunny.gif
new file mode 100644
index 00000000..285e7826
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/sunny.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/tome.gif b/web/cgi/alpine/2.0/img/cbn/tome.gif
new file mode 100644
index 00000000..11472f78
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/tome.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/trans.gif b/web/cgi/alpine/2.0/img/cbn/trans.gif
new file mode 100644
index 00000000..93b42998
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/trans.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/up.gif b/web/cgi/alpine/2.0/img/cbn/up.gif
new file mode 100644
index 00000000..0d3f5df5
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/up.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/uw.gif b/web/cgi/alpine/2.0/img/cbn/uw.gif
new file mode 100644
index 00000000..a3834ce8
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/uw.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/uwlogo.gif b/web/cgi/alpine/2.0/img/cbn/uwlogo.gif
new file mode 100644
index 00000000..2f636710
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/uwlogo.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/img/cbn/wbar.gif b/web/cgi/alpine/2.0/img/cbn/wbar.gif
new file mode 100644
index 00000000..73885025
--- /dev/null
+++ b/web/cgi/alpine/2.0/img/cbn/wbar.gif
Binary files differ
diff --git a/web/cgi/alpine/2.0/lib/INSTALL b/web/cgi/alpine/2.0/lib/INSTALL
new file mode 100644
index 00000000..1c8f3bdf
--- /dev/null
+++ b/web/cgi/alpine/2.0/lib/INSTALL
@@ -0,0 +1,3 @@
+
+Before running, the Yahoo U/I Library has to be installed
+so the "yui" link will work.
diff --git a/web/cgi/alpine/2.0/lib/common.js b/web/cgi/alpine/2.0/lib/common.js
new file mode 100644
index 00000000..89587f3a
--- /dev/null
+++ b/web/cgi/alpine/2.0/lib/common.js
@@ -0,0 +1,1586 @@
+/* $Id: common.js 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+ * ========================================================================
+ * Copyright 2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+// Globals
+YAHOO.namespace('alpine');
+YAHOO.alpine.current = { c:0, f:null, u:0 };
+YAHOO.alpine.mailcheck = { interval:null, timer:null };
+YAHOO.alpine.status = { timeout:null, message:[] };
+YAHOO.alpine.panel = { count: 0, width: 0 };
+YAHOO.alpine.containers = {};
+
+
+// INIT
+function browserDetect() {
+ if (YAHOO.env.ua.webkit > 0) {
+ document.write("<style type='text/css'> body {font-family:Helvetica,Tahoma,Sans Serif;} td,th {font-size:.75em;} </style>");
+ }
+}
+
+// GENERAL
+
+function clearTextField(txt, promptText) {
+ if (txt.value == promptText) {
+ txt.value = "";
+ }
+}
+
+function recallTextField(txt, promptText) {
+ if (txt.value == "") {
+ txt.value = promptText;
+ }
+}
+
+function showDisplayDiv(id) {
+ YAHOO.util.Dom.setStyle(id,'display','block');
+}
+
+function hideDisplayDivOrSpan(id) {
+ YAHOO.util.Dom.setStyle(id,'display','none');
+}
+
+function showDisplaySpan(id) {
+ YAHOO.util.Dom.setStyle(id,'display','inline');
+}
+
+function showLoading(){
+ var bp = document.getElementById("bePatient");
+ if(bp){
+ bp.style.top = "auto";
+ bp.style.left = "auto";
+ }
+}
+
+function hideLoading(){
+ var bp = document.getElementById("bePatient");
+ if(bp){
+ bp.style.top = "-999px";
+ bp.style.left = "-999px";
+ }
+}
+
+// MENU
+function menuSwap() {
+ this.className = "sfhover";
+}
+
+function menuBack() {
+ this.className = "menuHdr";
+}
+
+function menuFocus() {
+ this.parentNode.parentNode.parentNode.className = "sfhover";
+}
+
+function menuBlur() {
+ this.parentNode.parentNode.parentNode.className = "menuHdr";
+}
+
+function initMenus() {
+ if (document.getElementById) {
+ var LI = document.getElementsByTagName("li");
+ var zLI = LI.length;
+ for (var i = 0; i < zLI; i++) {
+ if (LI[i].parentNode.parentNode.className == "menuHdr") {
+ if (LI[i].firstChild.href) {
+ LI[i].firstChild.onfocus = menuFocus;
+ LI[i].firstChild.onblur = menuBlur;
+ }
+ }
+ if (LI[i].className == "menuHdr") {
+ LI[i].onmouseover = menuSwap;
+ LI[i].onmouseout = menuBack;
+ }
+ }
+ }
+}
+
+function updateElementHtml(id,html){
+ var el = document.getElementById(id);
+ if(el){
+ el.innerHTML = html;
+ return true;
+ }
+
+ return false;
+}
+
+function updateElementHref(id,href){
+ var el = document.getElementById(id);
+ if(el) el.href = href;
+}
+
+function updateElementValue(id,value){
+ var el = document.getElementById(id);
+ if(el) el.value = value;
+}
+
+function quoteHtml(s){
+ return s.replace(/</g, '&lt;').replace(/>/g, '&gt;');
+}
+
+// SETTINGS
+
+function showAdvancedSettings() {
+ hideDisplayDivOrSpan('normalSettings');
+ showDisplayDiv('advancedSettings');
+ return false;
+}
+
+function showNormalSettings() {
+ hideDisplayDivOrSpan('advancedSettings');
+ showDisplayDiv('normalSettings');
+ return false;
+}
+
+// COMPOSE
+
+function showMoreComposeHeaders() {
+ hideDisplayDivOrSpan('moreComposeHeaderText');
+ showDisplaySpan('lessComposeHeaderText');
+ showDisplayDiv('moreComposeHeaders');
+ return false;
+}
+
+function showLessComposeHeaders() {
+ hideDisplayDivOrSpan('lessComposeHeaderText');
+ showDisplaySpan('moreComposeHeaderText');
+ hideDisplayDivOrSpan('moreComposeHeaders');
+ return false;
+}
+
+// URI encode folder
+
+function encodeURIFolderPath(path) {
+ return encodeURIComponent(path).replace(/%2F/g,'/');
+}
+
+// HELP
+function openMailboxHelp(){
+ var d = YAHOO.util.Dom.getStyle('listTopMenubar','display');
+ if(d && d == 'none') openHelpWindow('read.html');
+ else openHelpWindow('inbox.html');
+ return(false);
+}
+
+function openHelpWindow(url) {
+ var top = 30;
+ var left = Math.floor(screen.availWidth * 0.66) - 10;
+ var width = Math.min(Math.floor(screen.availWidth * 0.33), 400);
+ var height = Math.floor(screen.availHeight * 0.8) - 30;
+ var win = window.open(document.getElementsByTagName('base')[0].href + "help/" + url, "helpwindow", "top=" + top + ",left=" + left + ",width=" + width + ",height=" + height + ",resizable=yes,scrollbars=yes,statusbar=no");
+ win.focus();
+}
+
+// NEW MAIL CHECK FUNCTIONS
+function setCheckMailFunction(elem,f){
+ var event = YAHOO.util.Event;
+ event.removeListener(elem,'click');
+ event.addListener(elem, 'click', function(e,o){ o.f(); }, { f:f });
+}
+
+function setNewMailCheckInterval(interval,onNewMail){
+ YAHOO.alpine.mailcheck.interval = interval;
+ startNewMailTimer(onNewMail);
+}
+
+function startNewMailTimer(onNewMail){
+ stopNewMailTimer();
+ var nmc = "newMailCheck(1,'"+onNewMail+"')";
+ YAHOO.alpine.mailcheck.timer = window.setTimeout(nmc, YAHOO.alpine.mailcheck.interval * 1000);
+}
+
+function stopNewMailTimer(){
+ if(YAHOO.alpine.mailcheck.timer){
+ window.clearTimeout(YAHOO.alpine.mailcheck.timer);
+ YAHOO.alpine.mailcheck.timer = null;
+ }
+}
+
+function newMailCheck(r,onNewMail){
+ stopNewMailTimer();
+ if(!r) r = 0;
+ YAHOO.util.Connect.asyncRequest('GET',
+ 'conduit/newmail.tcl' + encodeURI('?reload=' + r) + urlSalt('&'),
+ {
+ customevents:{
+ onStart:function(eventType){ showLoading(); },
+ onComplete:function(eventType){ hideLoading(); }
+ },
+ success: function(o){
+ eval('var objNM = '+o.responseText);
+ if(objNM.error){
+ addStatusMessage('Mail Check Error: ' + objNM.error,2);
+ }
+ else if(objNM.length > 0){
+ for(var i = 0; i < objNM.length; i++)
+ addStatusMessage(objNM[i].verbiage,10);
+
+ if(onNewMail && onNewMail.length) eval(onNewMail);
+ }
+ else if(!r)
+ addStatusMessage('No new mail has arrived',2);
+
+ displayStatusMessages();
+ },
+ failure: function(o){
+ addStatusMessage('newMailCheck Failure: '+o.statusText,15);
+ displayStatusMessages();
+ }
+ });
+
+ startNewMailTimer(onNewMail);
+ return false;
+}
+
+// STATUS MESSAGES
+
+function showStatusMessage(smText,smTimeout){
+ if(updateElementHtml('statusMessageText', smText)){
+ var dom = YAHOO.util.Dom;
+ dom.setStyle('statusMessage','display','block');
+ dom.setStyle('weatherBar','display','none');
+
+ clearStatusMessageTimeout();
+ // number arg to setTimeout is how long status messages are displayed
+ YAHOO.alpine.status.timeout = window.setTimeout('flipStatusMessages(hideStatusMessageOnInput)', ((smTimeout > 0) ? smTimeout : 10) * 1000);
+ }
+}
+
+function clearStatusMessageLine(){
+ var dom = YAHOO.util.Dom;
+ dom.setStyle('statusMessage','display','none');
+ dom.setStyle('weatherBar','display','block');
+}
+
+function actuallyHideStatusMessage(){
+ var event = YAHOO.util.Event;
+ event.removeListener(window,'click');
+ event.removeListener(window,'keydown');
+ clearStatusMessageLine();
+}
+
+function hideStatusMessageOnInput(){
+ var event = YAHOO.util.Event;
+ event.addListener(window,'keydown',actuallyHideStatusMessage);
+ event.addListener(window,'click',actuallyHideStatusMessage);
+}
+
+function flipStatusMessages(fn){
+ clearStatusMessageTimeout();
+ // nothing else to display, then restore original content
+ if(!displayStatusMessages()) fn();
+}
+
+function hideStatusMessage(){
+ flipStatusMessages(clearStatusMessageLine);
+}
+
+function clearStatusMessageTimeout(){
+ var t = YAHOO.alpine.status.timeout;
+ if(t){
+ window.clearTimeout(t);
+ t = null;
+ }
+}
+
+function displayStatusMessages(){
+ var smq = YAHOO.alpine.status.message;
+ if(smq.length){
+ var statusMessage = smq[0];
+ smq.splice(0,1);
+ showStatusMessage(statusMessage.text,statusMessage.timeout);
+ return true;
+ }
+
+ return false;
+}
+
+function addStatusMessage(smText,smTimeout){
+ if(smText){
+ var smEntry = {};
+ var smq = YAHOO.alpine.status.message;
+ if(typeof smText == 'object') smEntry.text = smText.html;
+ else smEntry.text = smText.replace(/</g,'&lt;');
+ if(smTimeout) smEntry.timeout = smTimeout;
+ smq[smq.length] = smEntry;
+ }
+}
+
+// LIST SELECTION
+function markAll(o,listId,mark) {
+ var chk = document.getElementsByName(listId);
+ var isChecked = o.checked;
+ if (chk) {
+ if (isNaN(chk.length)) {
+ chk.checked = isChecked;
+ mark(chk);
+ }
+ else {
+ for(var i = 0; i < chk.length; i++) {
+ chk[i].checked = isChecked;
+ mark(chk[i]);
+ }
+ }
+ }
+}
+
+function markOne(o) {
+ var elTR = o.parentNode.parentNode;
+ if(elTR.tagName == 'TR'){
+ var dom = YAHOO.util.Dom;
+ if(o.checked){
+ if(!dom.hasClass(elTR, 'choice')) dom.addClass(elTR, 'choice');
+ }
+ else{
+ if(dom.hasClass(elTR, 'choice')) dom.removeClass(elTR, 'choice');
+ }
+ }
+}
+
+// SCRIPT EVAL
+function evalScripts(htmlText){
+ var reScript = '<script[^>]*>([\\S\\s]*?)<\/script>';
+ var scriptOuter = new RegExp(reScript,'img');
+ var scriptInner = new RegExp(reScript,'im');
+ var scripts = htmlText.match(scriptOuter);
+ if(scripts)
+ for(var si = 0; si < scripts.length; si++){
+ var script = scripts[si].match(scriptInner);
+ if(script) eval(script[1]);
+ }
+}
+
+// MAINTAIN UNREAD COUNT
+function incUnreadCount(fl,n){
+ var unreadObj = document.getElementById('unread' + fl);
+ if(unreadObj){
+ var unreadMatch = unreadObj.innerHTML.match(/ \((\d+)\)/m);
+ if(unreadMatch && unreadMatch[1]){
+ var unreadCount = unreadMatch[1];
+ while(n-- > 0) unreadCount++;
+ if(unreadCount > 0) unreadObj.innerHTML = ' (' + unreadCount + ')';
+ else unreadObj.innerHTML = '';
+ }
+ else if(n > 0){
+ unreadObj.innerHTML = ' (' + n + ')';
+ }
+ }
+}
+
+function decUnreadCount(fl,n){
+ var unreadObj = document.getElementById('unread' + fl);
+ if(unreadObj){
+ var unreadMatch = unreadObj.innerHTML.match(/ \((\d+)\)/m);
+ if(unreadMatch && unreadMatch[1]){
+ var unreadCount = unreadMatch[1];
+ while(n-- > 0 && unreadCount > 0) unreadCount--;
+ if(unreadCount > 0) unreadObj.innerHTML = ' (' + unreadCount + ')';
+ else unreadObj.innerHTML = '';
+ }
+ }
+}
+
+function urlSalt(conjunction){
+ var now = new Date();
+ return conjunction + 'salt=' + now.getTime();
+}
+
+// EMPTY TRASH
+function emptyFolder(c,f,u,oDone) {
+ var eUrl = 'conduit/empty.tcl?c=' + encodeURIComponent(c) + '&f=' + encodeURIFolderPath(f);
+ if(u) eUrl += '&u=' + encodeURIComponent(u);
+ YAHOO.util.Connect.asyncRequest('GET',
+ eUrl + urlSalt('&'),
+ {
+ customevents:{
+ onStart:function(eventType){ showLoading(); },
+ onComplete:function(eventType){ hideLoading(); }
+ },
+ success: function(o) {
+ // valid response is "<numApplied> <numSelected> <numTotalMsgs>"
+ var matchResult;
+ if((matchResult = o.responseText.match(/^(\d+)$/m)) == null){
+ showStatusMessage('Error: ' + o.responseText, 10);
+ }
+ else if(oDone){
+ if(oDone.status){
+ showStatusMessage(matchResult[1] + ' messages from ' + f + ' deleted forever', 3);
+ YAHOO.alpine.current.count -= matchResult[1];
+ if(u == 'selected' && matchResult[1] > 0){
+ YAHOO.alpine.current.selected -= matchResult[1];
+ hideSelectAllInfo();
+ }
+ }
+ if(oDone.fn) eval(oDone.fn);
+ }
+ },
+ failure: function(o) { showStatusMessage('Request Failure: ' + o.statusText + '. Please report this.', 10)}
+ });
+ return false;
+}
+
+// ALERTS AND DIALOGS
+function newPanel(width){
+ var dom = YAHOO.util.Dom;
+ YAHOO.alpine.panel.count++;
+ var panelId = 'panel' + YAHOO.alpine.panel.count;
+ var panel = new YAHOO.widget.Panel(panelId,
+ {
+ //fixedcenter: true,
+ xy:[210,120],
+ underlay: 'none',
+ width:width,
+ visible: false,
+ modal:true,
+ dragOnly:true,
+ close: true,
+ constraintoviewport: true
+ });
+
+ // common footer
+ var pFoot = document.createDocumentFragment();
+ var div = document.createElement('div');
+ dom.addClass(div,'bl');
+ pFoot.appendChild(div);
+ div = document.createElement('div');
+ dom.addClass(div,'br');
+ pFoot.appendChild(div);
+ panel.setFooter(pFoot);
+ return(panel);
+}
+
+function closePanel(panel){
+ YAHOO.alpine.panel.button = null;
+ if(YAHOO.alpine.panel.keylistener){
+ YAHOO.alpine.panel.keylistener.disable();
+ YAHOO.alpine.panel.keylistener = null;
+ }
+
+ panel.hide();
+ panel.destroy();
+}
+
+function panelHeader(content){
+ var dom = YAHOO.util.Dom;
+ var pHead = document.createDocumentFragment();
+
+ var el = document.createElement('div');
+ dom.addClass(el,'tl');
+ pHead.appendChild(el);
+
+ el = document.createElement('span');
+ el.innerHTML = content;
+ pHead.appendChild(el);
+
+ el = document.createElement('div');
+ dom.addClass(el,'tr');
+ pHead.appendChild(el);
+
+ return pHead;
+}
+
+function panelBody(kind,content,buttons){
+ var frag, div;
+ var dom = YAHOO.util.Dom;
+
+ var frag = document.createDocumentFragment();
+
+ div = document.createElement('div');
+ dom.addClass(div,'dialogIcon');
+ dom.addClass(div,kind);
+ frag.appendChild(div);
+
+ div = document.createElement('div');
+ div.setAttribute('id',kind+'Body');
+ dom.addClass(div,'dialogBody');
+ if(content.html) div.innerHTML = content.html;
+ else if(content.frag) div.appendChild(content.frag);
+
+ frag.appendChild(div);
+
+ div = document.createElement('div');
+ dom.addClass(div,'yui-skin-sam');
+ dom.addClass(div,'dialogButtons');
+ frag.appendChild(div);
+ if(buttons){
+ for(var n = 0; n < buttons.length; n++){
+ var butCont = document.createElement('span');
+ butCont.setAttribute('id','pb' + (n + 1));
+ dom.addClass(butCont,'yuibutton');
+ div.appendChild(butCont);
+ var oButAtt = buttons[n];
+ oButAtt.container = butCont.id;
+ var b = new YAHOO.widget.Button(oButAtt);
+ if(oButAtt.disabled){
+ YAHOO.alpine.panel.button = b;
+ YAHOO.alpine.panel.keylistener = oButAtt.keylistener;
+ }
+ }
+ }
+
+ return frag;
+}
+
+function panelAlert(s,f){
+ var buttons = [{ id:'butClose',type:'button',label:'OK',onclick:{ fn: function(){ closePanel(panel); }}}];
+ var panel = newPanel('26em');
+ panel.setHeader(panelHeader('Alert!'));
+ if(f) buttons[0].onclick = function(){ f(); closePanel(panel); };
+ panel.setBody(panelBody('alert',{html:s},buttons));
+ panel.render(document.body);
+ getPanelBodyWidth('alert');
+ panel.show();
+ return false;
+}
+
+function panelConfirm(s,oYes,oNo){
+ var buttons = [{id:'butYes',type:'button',label:'OK'},{id:'butClose',label:'Cancel',onclick:{ fn:function(){ closePanel(panel); }}}];
+ var panel = newPanel('30em');
+ panel.setHeader(panelHeader('Confirm'));
+ if(oYes){
+ if(oYes.text) buttons[0].label = oYes.text;
+ if(oYes.url){
+ buttons[0].type = 'link';
+ buttons[0].href = oYes.url;
+ }
+ else if(oYes.fn){
+ buttons[0].type = 'button';
+ buttons[0].onclick = { fn: function() { oYes.fn(oYes.args) ; closePanel(panel); }};
+ }
+ }
+
+ if(oNo){
+ if(oNo.text) buttons[1].label = oNo.text;
+ if(oNo.fn) buttons[1].onclick = { fn: function(){ closePanel(panel); oNo.fn(); }};
+ }
+
+ panel.setBody(panelBody('prompt',{html:s},buttons));
+ panel.render(document.body);
+ panel.show();
+ getPanelBodyWidth('prompt');
+ return false;
+}
+
+function panelDialog(title,body,done,onClose){
+ var buttons = [];
+ var panel = newPanel('640px');
+ panel.setHeader(panelHeader(title));
+ if(done && done.fn){
+ var i = buttons.length;
+ buttons[i] = {
+ type:'button',
+ id:done.label.replace(/ /g,'_').toLowerCase(),
+ label:done.label,
+ disabled:done.disabled,
+ onclick:{
+ fn: function(){
+ done.fn();
+ closePanel(panel);
+ }
+ }
+ };
+
+ if(done.doonreturn){
+ buttons[i].keylistener = new YAHOO.util.KeyListener(document, { keys:13 },
+ {
+ fn:function() { done.fn(); closePanel(panel) },
+ scope:panel,
+ correctScope: true
+ });
+
+ if(!done.disabled) panel.cfg.queueProperty("keylisteners", buttons[i].keylistener);
+ }
+
+ if(done.buttonId) buttons[i].id = done.butId;
+ }
+
+ buttons[buttons.length] = {
+ id:'butClose',
+ type:'button',
+ label:'Cancel',
+ onclick:{ fn: function(){ if(onClose) onClose(); closePanel(panel); } }
+ };
+
+ panel.setBody(panelBody('dialog',{frag:body},buttons));
+ panel.render(document.body);
+ panel.show();
+ getPanelBodyWidth('dialog');
+ return false;
+}
+
+function drawFolderList(idContainer,defCollection){
+ if(idContainer && idContainer.length){
+ var el = document.getElementById(idContainer);
+ if(el){
+ newFolderList(el,null,defCollection);
+ YAHOO.alpine.containers.folderlist = el;
+ }
+ }
+}
+
+function newFolderList(elContainer,controlObj,collection,path,objParm){
+ var alp = YAHOO.alpine;
+ alp.resubmitRequest = function(){ newFolderList(elContainer,controlObj,collection,path,objParm); };
+ alp.cancelRequest = function(){ newFolderList(elContainer,controlObj,alp.current.c,"",objParm); };
+ if(elContainer){
+ var nUrl = 'conduit/folderlist.tcl/' + collection + '/';
+ if(path && path.length) nUrl += encodeURIFolderPath(path) + '/';
+ var conj = '?';
+ var prop, parms = '';
+ if(objParm){
+ for(prop in objParm){
+ parms += conj + prop + '=' + encodeURIComponent(objParm[prop]);
+ conj = '&';
+ }
+ }
+ YAHOO.util.Connect.asyncRequest('GET',
+ nUrl + parms + urlSalt(conj),
+ {
+ customevents:{
+ onStart:function(eventType){ showLoading(); },
+ onComplete:function(eventType){ hideLoading(); }
+ },
+ success: function(o){
+ elContainer.innerHTML = o.responseText;
+ evalScripts(o.responseText);
+ window.scrollTo(0,0);
+ },
+ failure: function(o) { showStatusMessage('Error getting list: '+o.statusText,10); },
+ argument: [elContainer]
+ });
+ if(controlObj) controlObj.blur();
+ }
+
+ return false;
+}
+
+function processAuthException(o){
+ switch(o.code){
+ case 'CERTQUERY' :
+ panelConfirm('The SSL/TLS certificate for the server<p><center>' + o.server + '</center><br>cannot be validated.<p>The problem is: ' + o.reason,
+ {text:'Accept Certificate',fn:acceptCert,args:{server:o.server}},{fn:YAHOO.alpine.cancelRequest});
+ break;
+ case 'CERTFAIL' :
+ panelAlert('The security certificate on server ' + o.server +' is not acceptable<p>The problem is: ' + o.reason);
+ break;
+ case 'NOPASSWD' :
+ getCredentials(o);
+ break;
+ case 'BADPASSWD' :
+ getCredentials(o);
+ break;
+ default :
+ addStatusMessage('Unknown Authorization Code: ' + o.code, 15);
+ displayStatusMessages();
+ break;
+ }
+}
+
+function acceptCert(o){
+ YAHOO.util.Connect.asyncRequest('GET',
+ 'conduit/cert.tcl?server=' + encodeURIComponent(o.server) + '&accept=yes' + urlSalt('&'),
+ {
+ customevents:{
+ onStart:function(eventType){ showLoading(); },
+ onComplete:function(eventType){ hideLoading(); }
+ },
+ success: function(o){ if(YAHOO.alpine.resubmitRequest) YAHOO.alpine.resubmitRequest(); },
+ failure: function(o){
+ addStatusMessage('Certificate Acceptance Failure: '+o.statusText,15);
+ displayStatusMessages();
+ }
+ });
+}
+
+function getCredentials(o){
+ var dom = YAHOO.util.Dom;
+ var alp = YAHOO.alpine;
+
+ var div = document.createElement('div');
+ dom.addClass(div,'getAuth');
+
+ var d = document.createElement('div');
+ var tail = (o.server) ? ' to '+ o.server + '.' : '...';
+ var e = document.createTextNode('Authorization is required to ' + o.reason + '. Please login'+tail);
+ d.appendChild(e);
+ div.appendChild(d);
+
+ e = document.createElement('span');
+ e.innerHTML = 'Username:';
+ div.appendChild(e);
+ var u = createInputElement('text','u');
+ div.appendChild(u);
+
+ e = document.createElement('hr');
+ div.appendChild(e);
+
+ e = document.createElement('span');
+ e.innerHTML = 'Password:';
+ div.appendChild(e);
+ var p = createInputElement('password','p');
+ div.appendChild(p);
+
+ hideLoading();
+
+ panelDialog('Login',
+ div,
+ {
+ label:'Login',
+ doonreturn: true,
+ fn:function(){
+ YAHOO.util.Connect.asyncRequest('POST',
+ alp.cgi_root + '/session/setauth2.tcl',
+ {
+ customevents:{
+ onStart:function(eventType){ showLoading(); },
+ onComplete:function(eventType){ hideLoading(); }
+ },
+ success: function(o){ if(alp.resubmitRequest) alp.resubmitRequest(); },
+ failure: function(o){
+ addStatusMessage('Login Failure: '+o.statusText,15);
+ displayStatusMessages();
+ }
+ },
+ 'auths=Login&user=' + encodeURIComponent(u.value) + '&pass=' + encodeURIComponent(p.value) + '&c=' + encodeURIComponent(o.c) + '&f=' + encodeURIComponent(o.server) + '&sessid=' + encodeURIComponent(o.sessid)+ urlSalt('&'));
+ }
+ },
+ alp.cancelRequest);
+
+ u.focus();
+ return false;
+}
+
+function getSmimePassphrase(o){
+ var dom = YAHOO.util.Dom;
+ var alp = YAHOO.alpine;
+
+ var div = document.createElement('div');
+ dom.addClass(div,'getAuth');
+
+ var d = document.createElement('div');
+ var addr = (o.addr) ? ' for '+ o.addr : '';
+ var e = document.createTextNode('Passphrase is required'+addr+' to decrypt.');
+ d.appendChild(e);
+ div.appendChild(d);
+
+ e = document.createElement('span');
+ e.innerHTML = 'Passphrase:';
+ div.appendChild(e);
+ var p = createInputElement('password','p');
+ div.appendChild(p);
+
+ hideLoading();
+
+ panelDialog('Enter S/MIME Passphrase',
+ div,
+ {
+ label:'Done',
+ fn:function(){
+ YAHOO.util.Connect.asyncRequest('POST',
+ alp.cgi_root + '/session/setpassphrase.tcl',
+ {
+ customevents:{
+ onStart:function(eventType){ showLoading(); },
+ onComplete:function(eventType){ hideLoading(); }
+ },
+ success: function(o){newMessageText(o);},
+ failure: function(o){}
+ },
+ 'auths=Smime&pass=' + encodeURIComponent(p.value) + '&sessid=' + encodeURIComponent(o.sessid)+ urlSalt('&'));
+ }
+ });
+
+ return false;
+}
+
+
+function flistPick(elChoice,fn){
+ elChoice.blur();
+ var el = document.getElementById('flPick');
+ if(el) el.removeAttribute('id');
+ elChoice.parentNode.parentNode.setAttribute('id','flPick');
+ updateElementValue('pickFolderName', decodeURIComponent(fn));
+ panelDialogEnableButton(true);
+ return false;
+}
+
+function flistPickPick(elChoice){
+ var el = document.getElementById('butChoose');
+ if(el){ // two contexts for this element: View/Manage, cp/mv dialog
+ if('A' == el.nodeName) window.location.href = elChoice.href;
+ else if('SPAN' == el.nodeName) el.firstChild.firstChild.click();
+ }
+ return false;
+}
+
+function panelDialogEnableButton(tf){
+ if(YAHOO.alpine.panel.button) YAHOO.alpine.panel.button.set('disabled',!tf);
+ if(YAHOO.alpine.panel.keylistener){
+ if(tf)
+ YAHOO.alpine.panel.keylistener.enable();
+ else
+ YAHOO.alpine.panel.keylistener.disable();
+ }
+}
+
+function panelDialogInputChange(e){
+ panelDialogEnableButton(YAHOO.util.Event.getTarget(e).value.length > 0);
+}
+
+function showFolderList(e){
+ hideDisplayDivOrSpan('showFolderList');
+ showDisplaySpan('hideFolderList');
+ showDisplayDiv('folderList');
+ YAHOO.util.Event.preventDefault(e);
+}
+
+function hideFolderList(e){
+ hideDisplayDivOrSpan('hideFolderList');
+ showDisplaySpan('showFolderList');
+ hideDisplayDivOrSpan('folderList');
+ YAHOO.util.Event.preventDefault(e);
+}
+
+function pickFolder(containerID,label,defCollection,onDone){
+ var dom = YAHOO.util.Dom;
+ var dbody = document.createDocumentFragment();
+ var div = document.createElement('div');
+ dom.addClass(div,'flistInstructions');
+ div.innerHTML = label + ' to: '
+
+ var elFolderName = createInputElement('text','pickFolderName');
+ elFolderName.setAttribute('id','pickFolderName');
+ elFolderName.value = '';
+ div.appendChild(elFolderName);
+ YAHOO.util.Event.addListener(elFolderName,'keyup',panelDialogInputChange);
+
+ el = document.createTextNode(' ');
+ div.appendChild(el);
+
+ var elFolderCollection = createInputElement('hidden','folderCollection');
+ elFolderCollection.value = defCollection;
+ elFolderCollection.setAttribute('id','pickFolderCollection');
+ div.appendChild(elFolderCollection);
+
+ var elFolderPath = createInputElement('hidden','pickFolderPath');
+ elFolderPath.setAttribute('id','pickFolderPath');
+ div.appendChild(elFolderPath);
+
+ dbody.appendChild(div);
+ div = document.createElement('div');
+ div.setAttribute('id',containerID);
+ div.innerHTML = 'Loading...';
+ dbody.appendChild(div);
+
+ panelDialog(label,
+ dbody,
+ {
+ buttonId:'butChoose',
+ label:label,
+ disabled: true,
+ doonreturn: true,
+ fn: function(){
+ var fc = elFolderCollection.value;
+ var fp = elFolderPath.value;
+ var fn = elFolderName.value;
+ if(fp && fp.length) fn = fp + '/' + fn;
+ if(fc >= 0 && fn && fn.length) onDone({c:fc,f:fn});
+ else showStatusMessage('No folder name provided. No messages moved or copied.',5);
+ }
+ });
+
+ elFolderName.focus();
+ drawFolderList(containerID,defCollection);
+ return false;
+}
+
+function getPanelBodyWidth(panelType){
+ var region = YAHOO.util.Dom.getRegion(panelType+'Body');
+ YAHOO.alpine.panel.width = region.right - region.left - 12;
+}
+
+function setPanelBodyWidth(className){
+ if(YAHOO.env.ua.ie > 0 && YAHOO.alpine.panel.width){
+ var dom = YAHOO.util.Dom;
+ var elArray = dom.getElementsByClassName(className,'div');
+ if(elArray){
+ for(var i = 0; i < elArray.length; i++){
+ dom.setStyle(elArray[i],'width', YAHOO.alpine.panel.width);
+ }
+ }
+ }
+}
+
+// CONTACTS EDITOR
+function contactEditor(oMode,onDone){
+ var dom = YAHOO.util.Dom;
+ var form, input, div, label, el;
+
+ function newField(fieldName,input,inputId,defaultValue){
+ var div = document.createElement('div');
+ dom.addClass(div,'contactSection');
+ var el = document.createElement('div');
+ div.appendChild(el);
+ dom.addClass(el,'contactField');
+ el.innerHTML = fieldName +':';
+ input.setAttribute('id',inputId);
+ if(defaultValue) input.value = defaultValue;
+ div.appendChild(input);
+ return(div);
+ }
+
+ function validateFields(){
+ var n = 0;
+ var fields = {book:'',ai:'',contactNick:'',contactName:'',contactEmail:'',contactNotes:'',contactFcc:''};
+
+ for(var f in fields) {
+ var el = document.getElementById(f);
+ if(el && el.value && el.value.length){
+ var val = el.value.replace(/\n/g, '').replace(/^[ \t]*/, '').replace(/[ \t]*$/, '');
+ if(val.length){
+ fields[f] = val;
+ n++;
+ }
+ }
+ }
+
+ return((n > 0) ? fields : null);
+ }
+
+ if(oMode.which == 'add'){
+ label = 'Add';
+ }
+ else if(oMode.which == 'edit'){
+ label = 'Change';
+ }
+ else return false;
+
+ form = document.createElement('form');
+ dom.addClass(form,'contactEditor');
+
+ if(oMode.books == null){
+ el = createInputElement('hidden','book');
+ form.appendChild(el);
+ el.setAttribute('id','book');
+ el.value = (oMode.book > 0) ? oMode.book : 0;
+ }
+
+ el = createInputElement('hidden','ai');
+ form.appendChild(el);
+ el.setAttribute('id','ai');
+ el.value = (oMode.index) ? oMode.index : -1;
+
+ el = createInputElement('hidden','origNick');
+ form.appendChild(el);
+ el.setAttribute('id','origNick');
+ if(oMode.nickname) el.value = oMode.nickname;
+
+ el = document.createElement('div');
+ form.appendChild(el);
+ dom.addClass(el,'context');
+ el.innerHTML = label + ' Contact';
+
+ input = createInputElement('text','contactNick');
+ form.appendChild(newField('Nickname',input,'contactNick',oMode.nickname));
+ form.appendChild(newField('Display Name',createInputElement('text','contactName'),'contactName',oMode.personal));
+
+ if(oMode.group){
+ var elEmail = createNameValueElement('textarea', 'name', 'contactEmail');
+ form.appendChild(newField('Group Email',elEmail,'contactEmail',oMode.email));
+ if(oMode.which){
+ var checked = contactsChecked();
+ if(checked.length){
+ var comma = '';
+ for(var i = 0; i < checked.length; i++){
+ getContact({ book:checked[i].book, index:checked[i].index, f:function(c){ elEmail.value += comma + c.email ; comma = ',\n'; } });
+ }
+ }
+ }
+ }
+ else
+ form.appendChild(newField('Email',createInputElement('text', 'contactEmail'),'contactEmail',oMode.email));
+
+ form.appendChild(newField('Notes',createNameValueElement('textarea', 'name', 'contactNotes'),'contactNotes',oMode.note));
+ form.appendChild(newField('Fcc Folder',createInputElement('text','contactFcc'),'contactFcc',oMode.fcc));
+
+ if(oMode.books != null){
+ var sel = document.createElement('select');
+ sel.setAttribute('name','book');
+ sel.setAttribute('id','book');
+ for(var i = 0; i < oMode.books.length; i++){
+ var opt = new Option(oMode.books[i].name,oMode.books[i].book);
+ sel.options[sel.options.length] = opt;
+ }
+
+ sel.selectedIndex = 0;
+ form.appendChild(newField('Address Book',sel,'book'));
+ }
+
+ if(!onDone) onDone = storeContact;
+ panelDialog(label, form, { label:label, fn: function(){ onDone(validateFields()); }});
+ input.focus();
+ return false;
+}
+
+function getContact(o){
+ o.which = 'edit';
+ var takeDS = new YAHOO.util.DataSource('conduit/getcontact.tcl?book=' + o.book + '&index=');
+ takeDS.responseType = YAHOO.util.DataSource.TYPE_XML;
+ takeDS.responseSchema = {
+ resultNode: 'Result',
+ fields: ['Nickname','Personal','Mailbox','Fcc','Note']
+ };
+ takeDS.sendRequest(o.index,
+ {
+ success: function(oReq,oResp,oPayload){
+ if(oResp.results.length == 1){
+ o.nickname = oResp.results[0].Nickname;
+ o.personal = oResp.results[0].Personal;
+ o.email = oResp.results[0].Mailbox;
+ if(o.email.search(/,/) >= 0) o.group = true;
+ o.note = oResp.results[0].Note;
+ o.fcc = oResp.results[0].Fcc;
+ o.f(o);
+ }
+ else showStatusMessage('Too many entries to Edit', 10);
+ },
+ failure: function(oReq,oResp,oPayload){
+ showStatusMessage('Error Taking Address: ' + oResp.responseText, 10);
+ },
+ scope: this,
+ argument:[o]
+ });
+ return(false);
+}
+
+function storeContact(oFields){
+ if(oFields){
+ var sUrl = 'conduit/storecontact.tcl?';
+ for(var f in oFields) sUrl += '&' + f + '=' + encodeURIComponent(oFields[f]);
+ storeDS = new YAHOO.util.DataSource(sUrl);
+ storeDS.responseType = YAHOO.util.DataSource.TYPE_XML;
+ storeDS.responseSchema = {
+ resultNode: 'Result',
+ fields: ['StatusText']
+ };
+ // bug: if present, store parts should be passed in draw request
+ storeDS.sendRequest('',
+ {
+ success: function(oReq,oResp,oPayload){
+ for(var i = 0; i < oResp.results.length; i++) addStatusMessage(oResp.results[i].StatusText, 10);
+ displayStatusMessages();
+ },
+ failure: function(oReq,oResp,oPayload){
+ showStatusMessage('Error Taking Address: ' + oResp.responseText, 10);
+ },
+ scope: this
+ });
+ }
+}
+
+
+function contactsChecked(gripe){
+ var chkd = [];
+ var i, entry, l = document.getElementsByName('nickList');
+ if(l){
+ for(i = 0; i < l.length; i++){
+ if(l[i].checked){
+ entry = l[i].value.match(/^(\d+)\.(\d+)\.(.*)$/);
+ if(entry) chkd[chkd.length] = { book:entry[1], index:entry[2], nick:entry[3] };
+ }
+ }
+ }
+
+ if(gripe != undefined && 0 == chkd.length){
+ var t = 'No Contacts Selected<p>Choose contacts by clicking checkbox to left of contact.';
+ if(gripe.length) t += 'Then press ' + gripe;
+ panelAlert(t);
+ }
+
+ return(chkd);
+}
+
+// LOAD From: INTO CONTACT EDITOR
+function takeAddressFrom(uid,o){
+ var books = (o) ? o.books : null;
+ // get personal and email based on uid
+ takeDSRequest = 'conduit/take.tcl?op=from&u=';
+ takeDS = new YAHOO.util.DataSource(takeDSRequest);
+ takeDS.responseType = YAHOO.util.DataSource.TYPE_XML;
+ takeDS.responseSchema = {
+ resultNode: 'Result',
+ fields: ['Type','Email','Personal','Nickname','Fcc','Note']
+ };
+ takeDS.sendRequest(uid,
+ {
+ customevents:{
+ onStart:function(eventType){ showLoading(); },
+ onComplete:function(eventType){ hideLoading(); }
+ },
+ success: function(oReq,oResp,oPayload){
+ if(oResp.results.length == 1){
+ var o = {
+ which:oResp.results[0].Type,
+ nickname:oResp.results[0].Nickname,
+ personal:oResp.results[0].Personal,
+ email:oResp.results[0].Email,
+ fcc:oResp.results[0].Fcc,
+ note:oResp.results[0].Note
+ };
+ if(books) o.books = books;
+ contactEditor(o);
+ }
+ else showStatusMessage('Too many From addresses, Use Take', 10);
+ },
+ failure: function(oReq,oResp,oPayload){
+ showStatusMessage('Error Taking Address: ' + oResp.responseText, 10);
+ },
+ scope: this,
+ arguments:[books]
+ });
+
+ return true;
+}
+
+// CONTACT LISTER
+
+function newContactList(elContainer,controlObj,idBook,objParm){
+ if(elContainer){
+ var nUrl = 'conduit/contactlist.tcl/' + idBook;
+ var conj = '?';
+ var prop, parms = '';
+ if(objParm){
+ for(prop in objParm){
+ parms += conj + prop + '=' + encodeURIComponent(objParm[prop]);
+ conj = '&';
+ }
+ }
+ YAHOO.util.Connect.asyncRequest('GET',
+ nUrl + parms + urlSalt(conj),
+ {
+ customevents:{
+ onStart:function(eventType){ showLoading(); },
+ onComplete:function(eventType){ hideLoading(); }
+ },
+ success: function(o){
+ elContainer.innerHTML = o.responseText;
+ evalScripts(o.responseText);
+ setPanelBodyWidth('clistContacts');
+ window.scrollTo(0,0);
+ },
+ failure: function(o){
+ showStatusMessage('Cannot build list: '+o.statusText,10);
+ },
+ argument: [elContainer]
+ });
+ if(controlObj) controlObj.blur();
+ }
+ return false;
+}
+
+function drawContactList(idContainer,idBook,objParm){
+ if(idContainer && idContainer.length){
+ var el = document.getElementById(idContainer);
+ if(el){
+ newContactList(el,null,idBook,objParm);
+ YAHOO.alpine.containers.contactlist = el;
+ }
+ }
+}
+
+// Search within Content
+function searchContent(context,treeId){
+ var elSearch = document.getElementById('searchField');
+ if(elSearch && elSearch.value && elSearch.value.length){
+ var dom = YAHOO.util.Dom;
+ var i, j, node, nodeParent;
+ var oldMatches = dom.getElementsByClassName('searchMatch','span',treeId);
+ for(i = 0; i < oldMatches.length; i++){
+ // BUG: coalesce node's text and prev/next sibling's text into single node
+ node = oldMatches[i];
+ nodeParent = node.parentNode;
+ for(j = 0; j < node.childNodes.length; j++) nodeParent.insertBefore(node.childNodes[j].cloneNode(true),node);
+ nodeParent.removeChild(node);
+ }
+
+ var lcSearch = elSearch.value.toLowerCase();
+ var nodeText, matchOffset, found = {n:0};
+ var removeNodes = [];
+ var nodeArray = function(root){
+ var textNodes = [];
+
+ function appendTextNodes(el){
+ for(var o = el.firstChild; o; o = o.nextSibling){
+ switch(o.nodeType){
+ case 3 : textNodes[textNodes.length] = o; break;
+ case 1 : appendTextNodes(o); break;
+ }
+ }
+ }
+
+ appendTextNodes(root);
+ return(textNodes);
+ }(document.getElementById(treeId));
+
+ var nodeBefore, nodeAfter, span;
+ for(i = 0; i < nodeArray.length; i++){
+ node = nodeArray[i];
+ nodeText = node.nodeValue;
+ matchOffset = nodeText.toLowerCase().indexOf(lcSearch);
+ if(matchOffset >= 0){
+ parentNode = node.parentNode;
+ nodeBefore = document.createTextNode(nodeText.substr(0,matchOffset));
+ nodeAfter = document.createTextNode(nodeText.substr(matchOffset+lcSearch.length));
+ matchText = nodeText.substr(matchOffset, lcSearch.length);
+ span = document.createElement("span");
+ dom.addClass(span,'searchMatch');
+ span.appendChild(document.createTextNode(matchText));
+ parentNode.insertBefore(nodeBefore,node);
+ parentNode.insertBefore(span,node);
+ parentNode.insertBefore(nodeAfter,node);
+ removeNodes[removeNodes.length] = {child: node, parent: parentNode};
+ found.n++;
+ if(!found.o) found.o = parentNode;
+ }
+ }
+
+ for(i = 0; i < removeNodes.length; i++){
+ node = removeNodes[i];
+ node.parent.removeChild(node.child)
+ }
+ }
+
+ if(found.n > 0){
+ var plural = (found.n > 1) ? 'es' : '';
+ showStatusMessage(found.n + ' match' + plural + ' found', 3);
+ found.o.scrollIntoView();
+ }
+ else showStatusMessage('No ' + context + ' found matching your seach', 3);
+
+ return(false);
+}
+
+// Advanced search
+function advanceSearch(){
+ var dom = YAHOO.util.Dom;
+ var i, d, form, el, span, dates;
+ var elScope, elWhichAddr, elWhichAddrText, elWhichSubj, elWhichSubjText, elWhichBody, elWhichBodyText, elWhichStatus, elWhichDate, elMonth, elDay, elYear;
+ function newSearchDiv(cl,el){
+ var div = document.createElement('div');
+ dom.addClass(div,cl);
+ if(el) div.appendChild(el);
+ return(div);
+ }
+
+ function newSearch(title,elSelect,elCriteria){
+ var div = document.createElement('div');
+ dom.addClass(div,'searchSection');
+ div.appendChild(newSearchDiv('searchField',document.createTextNode(title + ':')));
+ div.appendChild(newSearchDiv('searchType',elSelect));
+ div.appendChild(newSearchDiv('searchTerm',elCriteria));
+ return(div);
+ }
+
+ function newSelect(id,opts,onchange){
+ var elSel = document.createElement('select');
+ elSel.setAttribute('name',id);
+ elSel.setAttribute('id',id);
+ if(onchange) YAHOO.util.Event.addListener(elSel,'change',onchange);
+ for(var i = 0; i < opts.length; i++) elSel.options[elSel.options.length] = new Option(opts[i],i);
+ return elSel;
+ }
+
+ function newInput(id){
+ var el = createInputElement('text',id);
+ el.setAttribute('id',id);
+ YAHOO.util.Event.addListener(el,'keypress',searchOnEnter,'_search_');
+ return(el);
+ }
+
+ form = document.createElement('form');
+ dom.addClass(form,'advanceSearch');
+
+ el = document.createElement('div');
+ form.appendChild(el);
+ dom.addClass(el,'context');
+ el.innerHTML = 'Search in ' + quoteHtml(YAHOO.alpine.current.f);
+
+ el = document.getElementById('searchRefine');
+ if(el){
+ d = dom.getStyle(el,'display');
+ if(d && d != 'none'){
+ el = document.createElement('div');
+ form.appendChild(el);
+ dom.addClass(el,'scope');
+ elScope = newSelect('searchScope',['Perform this search within current search results','Add results of this search to current results','Perform a fresh search, abandoning the current results']);
+ el.appendChild(elScope);
+ }
+ }
+
+ elWhichAddr = newSelect('whichAddr',['From:','To:','Cc:','From:, Cc:','From:, To:, Cc:']);
+ elWhichAddrText = newInput('textAddr');
+ form.appendChild(newSearch('Address',elWhichAddr,elWhichAddrText));
+ elWhichSubj = newSelect('whichSubj',['Contains','Does Not Contain']);
+ elWhichSubjText = newInput('textSubj');
+ form.appendChild(newSearch('Subject',elWhichSubj,elWhichSubjText));
+ elWhichBody = newSelect('whichBody',['Contains','Does Not Contain']);
+ elWhichBodyText = newInput('textBody');
+ form.appendChild(newSearch('Message Text',elWhichBody,elWhichBodyText));
+
+ // prep date fields
+ span = document.createElement('span');
+ span.setAttribute('id','searchDates');
+ dom.setStyle(span,'display','none');
+ elMonth = newSelect('searchMonth',['------','January','February','March','April','May','June','July','August','September','October','November','December']);
+ span.appendChild(elMonth);
+ dates = ['--'];
+ for(i = 1; i < 32; i++) dates[dates.length] = i;
+ elDay = newSelect('searchDay', dates);
+ span.appendChild(elDay);
+ el= document.createTextNode(', ');
+ span.appendChild(el);
+ dates = ['----'];
+ d = new Date();
+ for(i = d.getFullYear(); i > 1970; i--) dates[dates.length] = i;
+ elYear = newSelect('searchYear',dates);
+ span.appendChild(elYear);
+ elWhichDate = newSelect('whichDate',['Anytime','Today','Past week','Past month','On','Since','Before'],advanceSearchDate);
+ form.appendChild(newSearch('Date',elWhichDate,span));
+
+ // empty searchTerm
+ elWhichStatus = newSelect('whichStatus',['All Messages','Unread Messages','Read Messages','Starred Messages','Unstarred Messages']);
+ form.appendChild(newSearch('Status',elWhichStatus));
+
+ panelDialog('Advanced Search',
+ form,
+ {
+ label:' Search ',
+ doonreturn: true,
+ fn: function() {
+ var scope = 'new', criteria = '';
+
+ if(elScope){
+ scope = ['narrow','broad'][elScope.selectedIndex];
+ }
+
+ if(elWhichAddrText.value.length){
+ criteria += '{text ton ' + ['from','to','cc','recip','partic'][elWhichAddr.selectedIndex] + ' {' + elWhichAddrText.value + '}} ';
+ }
+
+ if(elWhichSubjText.value.length){
+ criteria += '{text ';
+ criteria += (elWhichSubj.selectedIndex > 0) ? 'not' : 'ton';
+ criteria += ' subj {' + elWhichSubjText.value + '}} ';
+ }
+
+ if(elWhichBodyText.value.length){
+ criteria += '{text ';
+ criteria += (elWhichBody.selectedIndex > 0) ? 'not' : 'ton';
+ criteria += ' body {' + elWhichBodyText.value + '}} ';
+ }
+
+ if(elWhichDate.selectedIndex > 0 && elMonth.selectedIndex > 0 && elDay.selectedIndex > 0 && elYear.selectedIndex > 0){
+ criteria += '{date ' + ['','on','since','since','on','since','before'][elWhichDate.selectedIndex];
+ criteria += ' ' + elYear.options[elYear.selectedIndex].text;
+ criteria += ' ' + elMonth.options[elMonth.selectedIndex].text.substring(0,3).toLowerCase();
+ criteria += (elDay.selectedIndex < 10) ? ' 0' : ' ';
+ criteria += elDay.selectedIndex;
+ criteria += '} ';
+ }
+
+ if(elWhichStatus.selectedIndex > 0){
+ criteria += '{status ' + ['','ton new','not new','ton imp','not imp'][elWhichStatus.selectedIndex] + '} ';
+ }
+
+ newMessageList({parms:{op:'search',type:'compound',scope:scope,criteria:criteria,page:'new'}});
+ }
+ });
+
+ return false;
+}
+
+function advanceSearchDate(e){
+ var y, d = new Date();
+
+ function setSelect(id,i){
+ var el = document.getElementById(id);
+ if(el) el.selectedIndex = i;
+ }
+
+ function setSearchDate(m,d,y){
+ setSelect('searchMonth',m);
+ setSelect('searchDay',d);
+ setSelect('searchYear',y);
+ }
+
+ switch(YAHOO.util.Event.getTarget(e).selectedIndex){
+ case 0 :
+ hideDisplayDivOrSpan('searchDates');
+ setSearchDate(0,0,0);
+ break;
+ case 1:
+ hideDisplayDivOrSpan('searchDates');
+ setSearchDate(d.getMonth() + 1, d.getDate(), 1);
+ break;
+ case 2 :
+ hideDisplayDivOrSpan('searchDates');
+ y = d.getFullYear();
+ d.setDate(d.getDate() - 7);
+ setSearchDate(d.getMonth() + 1,d.getDate(),(y - d.getFullYear()) + 1);
+ break;
+ case 3 :
+ hideDisplayDivOrSpan('searchDates');
+ y = d.getFullYear();
+ if(d.getMonth()) d.setMonth(d.getMonth() - 1); else d.setMonth(11);
+ setSearchDate(d.getMonth() + 1, d.getDate(),(y - d.getFullYear()) + 1);
+ break;
+ default :
+ showDisplaySpan('searchDates');
+ setSearchDate(d.getMonth() + 1, d.getDate(), 1);
+ break;
+ }
+}
+
+function searchOnEnter(e,btn){
+ if(YAHOO.util.Event.getCharCode(e) == 13){
+ var el = document.getElementById(btn);
+ if(el){
+ // dig thru any decoration to find button
+ while(el && !el.click) el = el.firstChild;
+ if(el && el.click){
+ el.click();
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+function rotateNews(el){
+ if(el.parentNode){
+ var dom = YAHOO.util.Dom;
+ var node = el.parentNode;
+
+ dom.setStyle(node, 'display', 'none');
+ if(node.nextSibling && node.nextSibling.tagName == 'SPAN'){
+ node = node.nextSibling;
+ }
+ else{
+ for(var node = el.parentNode; node.previousSibling && node.previousSibling.tagName == 'SPAN'; node = node.previousSibling)
+ ;
+ }
+
+ dom.setStyle(node,'display','inline');
+ }
+
+ return false;
+}
+
+function printContent(){
+ window.print();
+ return false;
+}
+
+// Test to see if a folder exists, create if asked
+function folderExists(o){
+ var eUrl = 'conduit/exists.tcl' + encodeURI('?c=' + o.c + '&f=' + o.f);
+
+ function onYes(o){
+ folderExists(o);
+ if(o.onDone) o.onDone();
+ }
+
+ if (o.create != undefined)
+ eUrl += '&create=' + o.create;
+
+ YAHOO.util.Connect.asyncRequest('GET',
+ eUrl + urlSalt('&'),
+ {
+ success: function(retObj) {
+ eval('var exObj = '+retObj.responseText);
+ if(exObj.exists != undefined){
+ if(!exObj.exists){
+ o.create = 'yes';
+ panelConfirm('Folder does not exist. Create it?',{text:'Create',fn:onYes,args:o},{fn:o.oncancel});
+ }
+ else if(o.onDone){
+ o.onDone();
+ }
+ }
+ else{
+ if(exObj.error != undefined)
+ alert('ERROR: '+exObj.error);
+ else
+ alert('INCONCLUSIVE: '+retObj.responseText);
+ }
+ },
+ failure: function(retObj) { newStatusMessage('Request Failure: ' + retObj.statusText); }
+ });
+}
+
+// Hacks to work around IE
+function createInputElement(type,name){
+ if(YAHOO.env.ua.ie > 0){
+ return(document.createElement('<input type="' + type + '" type="' + name + '" name="' + name + '">'));
+ }
+
+ var el = document.createElement('input');
+ el.setAttribute('type',type);
+ el.setAttribute('id',name);
+ el.setAttribute('name',name);
+ return(el);
+}
+
+function createNameValueElement(tag, name, nameAttr){
+ if(YAHOO.env.ua.ie > 0){
+ return(document.createElement('<' + tag + ' ' + name + '="' + nameAttr + '">'));
+ }
+
+ var el = document.createElement(tag);
+ el.setAttribute(name,nameAttr);
+ return(el);
+}
+
+// HACKS TO MAINTAIN FIX DIV IN VIEWPORT
+function sizeVPHeight(){
+ // force msg txt container to fit in viewport
+ var dom = YAHOO.util.Dom;
+ var docHeight = dom.getDocumentHeight();
+ var topToolbarY = dom.getY('topToolbar');
+ var alpineContentY = dom.getY('alpineContent');
+ var bottomToolbarY = dom.getY('bottomToolbar');
+ var bottomHeight = docHeight - bottomToolbarY;
+ var contentHeight = dom.getViewportHeight() - (alpineContentY + bottomHeight);
+ var leftColumnHeight = contentHeight + (alpineContentY - topToolbarY);
+ if(contentHeight > 0){
+ dom.setStyle('alpineContent','height', Math.round(contentHeight));
+ dom.setStyle('leftColumn','height', Math.round(leftColumnHeight));
+ }
+}
+
+function resizeVPHeight(){
+ var dom = YAHOO.util.Dom;
+ dom.setStyle('alpineContent','height', 'auto');
+ dom.setStyle('leftColumn','height', 'auto');
+ sizeVPHeight();
+}
+
+// DEBUGGING
+function dumpProp(o){
+ var s = '';
+ for(p in o) s += p + ' = ' + o[p] + '\n';
+ return s;
+}
diff --git a/web/cgi/alpine/2.0/lib/compose.js b/web/cgi/alpine/2.0/lib/compose.js
new file mode 100644
index 00000000..52eddea9
--- /dev/null
+++ b/web/cgi/alpine/2.0/lib/compose.js
@@ -0,0 +1,833 @@
+/* $Id: compose.js 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+ * ========================================================================
+ * Copyright 2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+// globals in our namespace
+YAHOO.alpine.autodraft = { timeout:0, timer:null, reporttimer:null };
+YAHOO.alpine.pickcontact = { tabs:null, markup:null };
+YAHOO.alpine.uploading = 0;
+YAHOO.alpine.expandaddress = 1;
+
+
+function initSimpleEditor(focus){
+ var dom = YAHOO.util.Dom;
+ var edRegion = dom.getRegion('composeText');
+ var textareaHeight = edRegion.bottom - edRegion.top;
+ var textareaWidth = edRegion.right - edRegion.left - 10;
+ if(edRegion){
+ var editAttrib = {
+ height:textareaHeight,
+ width: textareaWidth,
+ toolbar: {
+ draggable: false,
+ buttons: [
+ { group: 'fontstyle', label: 'Font Name and Size',
+ buttons: [
+ { type: 'select', label: 'Arial', value: 'fontname', disabled: true,
+ menu: [
+ { text: 'Arial', checked: true },
+ { text: 'Arial Black' },
+ { text: 'Comic Sans MS' },
+ { text: 'Courier New' },
+ { text: 'Lucida Console' },
+ { text: 'Tahoma' },
+ { text: 'Times New Roman' },
+ { text: 'Trebuchet MS' },
+ { text: 'Verdana' }
+ ]
+ },
+ { type: 'spin', label: '13', value: 'fontsize', range: [ 9, 75 ], disabled: true }
+ ]
+ },
+ { type: 'separator' },
+ { group: 'textstyle', label: 'Font Style',
+ buttons: [
+ { type: 'push', label: 'Bold', value: 'bold' },
+ { type: 'push', label: 'Italic', value: 'italic' },
+ { type: 'push', label: 'Underline', value: 'underline' },
+ { type: 'separator' },
+ { type: 'color', label: 'Font Color', value: 'forecolor', disabled: true },
+ { type: 'color', label: 'Background Color', value: 'backcolor', disabled: true }
+ ]
+ },
+ { type: 'separator' },
+ { group: 'indentlist', label: 'Lists',
+ buttons: [
+ { type: 'push', label: 'Create an Unordered List', value: 'insertunorderedlist' },
+ { type: 'push', label: 'Create an Ordered List', value: 'insertorderedlist' }
+ ]
+ },
+ { type: 'separator' },
+ { group: 'alignment', label: 'Alignment',
+ buttons: [
+ { type: 'push', label: 'Align Left', value: 'justifyleft' },
+ { type: 'push', label: 'Align Center', value: 'justifycenter' },
+ { type: 'push', label: 'Align Right', value: 'justifyright' },
+ { type: 'push', label: 'Justify', value: 'justifyfull' }
+ ]
+ },
+ { type: 'separator' },
+ { group: 'insertitem', label: 'Insert Item',
+ buttons: [
+ { type: 'push', label: 'Insert/Edit HTML Link', value: 'createlink', disabled: true },
+ { type: 'push', label: 'Insert Image', value: 'insertimage' }
+ ]
+ }
+ ]
+ }
+ };
+ if(focus){
+ editAttrib.focusAtStart = true;
+ }
+ gRichtextEditor = new YAHOO.widget.SimpleEditor('composeText', editAttrib);
+ gRichtextEditor.textareaHeight = textareaHeight;
+ gRichtextEditor.textareaWidth = textareaWidth;
+ }
+}
+
+
+function flipRichText(){
+ var dom = YAHOO.util.Dom;
+ if(gRichState){
+ gRichState = 0;
+ gRichtextEditor.saveHTML();
+ var stripHTML = /<\S[^><]*>/g;
+ gRichtextEditor.get('textarea').value = gRichtextEditor.get('textarea').value.replace(/<br>/gi, '\n').replace(stripHTML, '').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
+ dom.setStyle(gRichtextEditor.get('element_cont').get('firstChild'), 'position', 'absolute');
+ dom.setStyle(gRichtextEditor.get('element_cont').get('firstChild'), 'top', '-9999px');
+ dom.setStyle(gRichtextEditor.get('element_cont').get('firstChild'), 'left', '-9999px');
+ gRichtextEditor.get('element_cont').removeClass('yui-editor-container');
+ dom.setStyle(gRichtextEditor.get('element'), 'visibility', 'visible');
+ dom.setStyle(gRichtextEditor.get('element'), 'top', '');
+ dom.setStyle(gRichtextEditor.get('element'), 'left', '');
+ dom.setStyle(gRichtextEditor.get('element'), 'height', gRichtextEditor.textareaHeight);
+ dom.setStyle(gRichtextEditor.get('element'), 'width', gRichtextEditor.textareaWidth);
+ dom.setStyle(gRichtextEditor.get('element'), 'position', 'static');
+ document.getElementById('flipRich').innerHTML = 'Rich Text';
+ }
+ else{
+ if(!gRichtextRendered){
+ // fixup initial text
+ var t = document.getElementById('composeText').value.replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\n/g, '<br>');
+ document.getElementById('composeText').value = t;
+ initSimpleEditor(1);
+ gRichtextEditor.render();
+ gRichtextRendered = true;
+ }
+ gRichState = 1;
+ dom.setStyle(gRichtextEditor.get('element_cont').get('firstChild'), 'position', 'static');
+ dom.setStyle(gRichtextEditor.get('element_cont').get('firstChild'), 'top', '0');
+ dom.setStyle(gRichtextEditor.get('element_cont').get('firstChild'), 'left', '0');
+ dom.setStyle(gRichtextEditor.get('element'), 'visibility', 'hidden');
+ dom.setStyle(gRichtextEditor.get('element'), 'top', '-9999px');
+ dom.setStyle(gRichtextEditor.get('element'), 'left', '-9999px');
+ dom.setStyle(gRichtextEditor.get('element'), 'position', 'absolute');
+ gRichtextEditor.get('element_cont').addClass('yui-editor-container');
+ gRichtextEditor._setDesignMode('on');
+ if(gRichtextEditor.get('textarea')) gRichtextEditor.setEditorHTML(gRichtextEditor.get('textarea').value.replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\n/g, '<br>'));
+ document.getElementById('flipRich').innerHTML = 'Plain Text';
+ }
+
+ return false;
+}
+
+function autoSizeThis(e){
+ if(e.keyCode == 13 || e.keyCode == 10) YAHOO.util.Event.preventDefault(e);
+ else autoSize(this);
+}
+
+function autoSize(el){
+ var dom = YAHOO.util.Dom;
+ var r = dom.getRegion(el)
+ var th = r.bottom - r.top;
+ var maxh = 3 * gFieldHeight;
+ if(!el.value.length){
+ dom.setStyle(el,'height',(gFieldHeight + 4)+'px');
+ }
+ else if(th < maxh){
+ if(el.scrollHeight > gFieldHeight){
+ var h = (2 * (gFieldHeight));
+ if(el.scrollHeight > h) h += gFieldHeight;
+ dom.setStyle(el,'height',h + 'px');
+ }
+ }
+}
+
+function autoSizeAddressField(id){
+ var el = document.getElementById(id);
+ if(el){
+ if(!gFieldHeight){
+ var r = YAHOO.util.Dom.getRegion(el);
+ gFieldHeight = r.bottom - r.top - 4;
+ }
+
+ YAHOO.util.Event.addListener(el,'keypress',autoSizeThis);
+ autoSize(el);
+ }
+}
+
+function autoCompleteDefaults(o){
+ if(o){
+ o.delimChar = ',';
+ o.maxResultsDisplayed = 12;
+ o.minQueryLength = 2;
+ o.queryDelay = 0.25;
+ o.autoHighlight = true;
+ o.animSpeed = 0.1;
+ o.formatResult = function(aResults, sQuery) {
+ var r, where;
+ // highlight matching substrings
+ var oREHilite = new RegExp(sQuery,'ig');
+ var sEmail = aResults[0].replace(/[<]/,'&lt;')
+ var sNick = aResults[1].replace(/[<]/,'&lt;')
+ r = sEmail.replace(oREHilite,'<b>$&</b>');
+ where = sNick.replace(oREHilite,'<b>$&</b>');
+ if(where.length) r += ' (' + where + ')';
+ return r;
+ };
+
+ o.textboxBlurEvent.subscribe(function(oSelf){
+ var el = o.getInputEl();
+ if(el.value.length) validateAddressField(el);
+ });
+ o.containerCollapseEvent.subscribe(function(oSelf){
+ var el = o.getInputEl();
+ if(el.value.length) validateAddressField(el);
+ });
+ }
+}
+
+function actuallySend(o){
+ YAHOO.alpine.resubmitRequest = function(){ actuallySend(o); };
+
+ if(YAHOO.alpine.disablePost) return;
+
+ stopAutoDrafting();
+
+ document.getElementById('sendOp').value = o.op;
+
+ if(gRichtextEditor && gRichState){
+ gRichtextEditor.saveHTML();
+ document.getElementById('contentSubtype').value = 'HTML';
+ }
+
+ showLoading();
+
+ while(YAHOO.alpine.uploading > 0)
+ panelAlert('Finishing attachment upload before sending...');
+
+ document.getElementById('composeForm').submit();
+}
+
+function sendSuccess(m){
+ var alp = YAHOO.alpine;
+ alp.disablePost = true;
+ window.location.replace(alp.cgi_base + 'browse/' + alp.current.c + '/' + alp.current.f);
+}
+
+function sendFailure(m){
+ addStatusMessage('Send Failure: ' + m, 10);
+ displayStatusMessages();
+ hideLoading();
+}
+
+function processPostAuthException(o){
+ YAHOO.alpine.cancelRequest = null;
+ processAuthException(o);
+}
+
+function cancelComposition(dest){
+ stopAutoDrafting();
+ var changes = 0;
+ var el, changeFields = ['fieldTo','fieldCc','fieldBcc','fieldSubject'];
+ for (f in changeFields){
+ el = document.getElementById(changeFields[f]);
+ changes += el.value.length;
+ }
+
+ if(!changes){
+ if(gRichState){
+ changes += gRichtextEditor.getEditorHTML().length;
+ }
+ else{
+ el = document.getElementById('composeText');
+ if(el) changes += el.value.length;
+ }
+ }
+
+ if(changes) panelConfirm('You have unsaved changes.<br><br>Click <b>Discard</b> to abandon your text and exit Compose.<br>Click <b>Cancel</b> to continue editing.',{text:'Discard',fn:abandonComposition,args:{url:dest}},{fn:startAutoDrafting});
+ else abandonComposition({url:dest});
+
+ return false;
+}
+
+function sendComposition(){
+ stopAutoDrafting();
+
+ if(!(document.getElementById('fieldTo').value.length
+ || document.getElementById('fieldCc').value.length
+ || document.getElementById('fieldBcc').value.length
+ || document.getElementById('fieldFcc').value.length)){
+ panelAlert('Message must contain at least one To, Cc, Bcc, or Fcc recipient!', startAutoDrafting);
+ }
+ else{
+ //panelConfirm('Are you sure you want to send this message?',{text:'Send',fn:actuallySend,args:{op:'send'}},{fn:startAutoDrafting});
+ actuallySend({op:'send'});
+ }
+
+ return false;
+}
+
+function saveDraft(){
+ actuallySend({op:'postpone'});
+ return false;
+}
+
+function setAutoDraftInterval(iTimeOut){
+ YAHOO.alpine.autodraft.timeout = iTimeOut;
+ startAutoDrafting();
+}
+
+function startAutoDrafting(){
+ var aa = YAHOO.alpine.autodraft;
+ if(aa.timeout) aa.timer = window.setTimeout('autoDraft();', aa.timeout * 1000);
+}
+
+function stopAutoDrafting(){
+ var aa = YAHOO.alpine.autodraft;
+ if(aa.timer) clearTimeout(aa.timer);
+ aa.timer = null;
+}
+
+function autoDraft(){
+ var form = document.getElementById('composeForm');
+ var formTarget = form.getAttribute('target');
+ form.setAttribute('target','formResponse');
+ actuallySend({op:'autodraft'});
+ form.setAttribute('target', formTarget);
+ startAutoDrafting();
+ var date = new Date();
+ YAHOO.alpine.autodraft.drafted = date.getTime();
+}
+
+function reportAutoDraftUpdate(){
+ var aa = YAHOO.alpine.autodraft;
+ var nn = new Date();
+ var drafted = new Date(aa.drafted);
+ var delt = Math.floor((nn.getTime() - aa.drafted) / 60000);
+ var hours = ( drafted.getHours() % 12 );
+ var minutes = drafted.getMinutes();
+ var ispm = ( drafted.getHours() > 12 );
+ if(hours < 1) hours = 12;
+ if(minutes < 10) minutes = '0' + minutes;
+ var t = 'Copy saved to Drafts at ' + hours + ':' + minutes + ' ';
+ if(ispm)
+ t += 'PM';
+ else
+ t += 'AM';
+
+ if(delt > 0){
+ t += ' (' + delt + ' minute';
+ if(delt > 1) t += 's';
+ t += ' ago)';
+ }
+
+ document.getElementById('lastAutoDraft').innerHTML = t;
+}
+
+function reportAutoDraft(result){
+ if(result.match(/^[0-9]+$/)){
+ var aa = YAHOO.alpine.autodraft;
+ var date = new Date();
+ document.getElementById('autoDraftUid').value = result;
+ var drafted = new Date(aa.drafted);
+ var minutes = drafted.getMinutes();
+ if(minutes < 10) minutes = '0' + minutes;
+ document.getElementById('lastAutoDraft').innerHTML = 'Copy saved to Drafts at ' + ( drafted.getHours() % 12 ) + ':' + minutes + ' (less than one minute ago)';
+ if(aa.reporttimer) clearInterval(aa.reporttimer);
+ aa.reporttimer = setInterval('reportAutoDraftUpdate();', + 60000);
+ }
+ else{
+ document.getElementById('lastAutoDraft').innerHTML = 'Copy save to Drafts FAILED: ' + result;
+ addStatusMessage('Copy autosave to Drafts failed: ' + result, 10);
+ displayStatusMessages();
+ }
+}
+
+function abandonComposition(o){
+ var u = document.getElementById('autoDraftUid').value;
+ if(u.match(/^\d+$/) && u > 0){
+ emptyFolder(gDefCol, gDraftFolder, u, {fn:'window.location.href="' + o.url + '"'});
+ }
+ else{
+ window.location.href = o.url;
+ }
+}
+
+function drawAttachmentList(o){
+ var dom = YAHOO.util.Dom;
+ var el, attachList = document.getElementById('attachList');
+ var idList = '';
+ var comma = '';
+ hideLoading();
+ while(attachList.childNodes.length) attachList.removeChild(attachList.childNodes[0]);
+ if(o){
+ if(o.error){
+ addStatusMessage('Attach Error: ' + o.error, 10);
+ displayStatusMessages();
+ }
+
+ if(o.attachments && o.attachments.length){
+ var frag = document.createDocumentFragment();
+ var delim = '', len, size, bytes;
+ for(var i = 0; i < o.attachments.length; i++){
+ idList += comma + o.attachments[i].id;
+
+ if(delim.length){
+ el = document.createTextNode(delim);
+ frag.appendChild(el);
+ }
+
+ el = document.createElement('img');
+ el.setAttribute('src','img/cbn/attach_sm.gif');
+ frag.appendChild(el);
+
+ l = o.attachments[i].size.length;
+ if(l > 6){
+ size = o.attachments[i].size.substr(0, l - 6);
+ bytes = 'MB';
+ }
+ else if(o.attachments[i].size.length > 3){
+ size = o.attachments[i].size.substr(0, l - 3);
+ bytes = 'KB';
+ }
+ else{
+ size = o.attachments[i].size;
+ bytes = 'B';
+ }
+
+ el = document.createElement('span');
+ dom.addClass(el,'attachmentName');
+ el.innerHTML = o.attachments[i].fn + ' ' + '(' + size + ' ' + bytes + ')';
+ frag.appendChild(el);
+
+ el = document.createElement('a');
+ el.setAttribute('href','#');
+ YAHOO.util.Event.addListener(el,'click',removeAttachment,{id:o.attachments[i].id});
+ var img = document.createElement('img');
+ img.setAttribute('src','img/cbn/remove.gif');
+ dom.addClass(img,'wap');
+ el.appendChild(img);
+ frag.appendChild(el);
+
+ delim = ': ';
+ comma = ',';
+ }
+
+ attachList.appendChild(frag);
+ dom.setStyle('composeAttachments','display','block');
+ }
+ else{
+ dom.setStyle('composeAttachments','display','none');
+ if(el) el.innerHTML = '';
+ }
+ }
+
+ el = document.getElementById('attachments');
+ if(el) el.setAttribute('value',idList);
+}
+
+function attachFile(e){
+ var target = YAHOO.util.Event.getTarget(e);
+ if(target.value.length){
+ var elRemove = target.parentNode.parentNode;
+ var elParent = elRemove.parentNode;
+ YAHOO.util.Connect.setForm(target.parentNode,true);
+ YAHOO.alpine.uploading++;
+ YAHOO.util.Connect.asyncRequest('POST','conduit/attach.tcl', {
+ upload: function(response){
+ YAHOO.alpine.uploading--;
+ elParent.removeChild(elRemove);
+ }
+ });
+ }
+ else
+ panelAlert('No File Attached');
+
+ return false;
+}
+
+function addAttachField(){
+ var event = YAHOO.util.Event;
+ var dom = YAHOO.util.Dom;
+
+ dom.setStyle('composeAttachments','display','block');
+
+ var parentDiv = document.getElementById('fileUpload');
+ if(parentDiv.childNodes.length < 20){
+ var div = document.createElement('div');
+ parentDiv.appendChild(div);
+ dom.addClass(div,'attachInput');
+
+ var form = document.createElement('form');
+ div.appendChild(form);
+
+ form.setAttribute('action','conduit/attach.tcl');
+ form.setAttribute('method','POST');
+ form.setAttribute('enctype','multipart/form-data');
+ form.setAttribute('target','formResponse');
+ dom.generateId(form);
+
+ var input = createInputElement('file','file');
+ form.appendChild(input);
+ input.setAttribute('accept','*/*');
+ event.addListener(input,'change',attachFile);
+
+ /*
+ var el = createInputElement('button','Attach')
+ div.appendChild(el);
+ el.setAttribute('value','Attach');
+ event.addListener(el,'click',attachFile);
+ */
+ }
+
+ return false;
+}
+
+function removeAttachment(e,o){
+ var form = document.createElement('form');
+ this.parentNode.appendChild(form);
+ YAHOO.util.Dom.setStyle(form,'display','none');
+ form.setAttribute('action','conduit/attach.tcl');
+ form.setAttribute('method','GET');
+ form.setAttribute('target','formResponse');
+
+ var input = createInputElement('hidden','op');
+ input.setAttribute('value','delete');
+ form.appendChild(input);
+
+ input = createInputElement('hidden','id');
+ input.setAttribute('value',o.id);
+ form.appendChild(input);
+
+ form.submit();
+
+ YAHOO.util.Event.preventDefault(e);
+}
+
+function setCursorPosition(inputId, cursorOffset) {
+ var inputEl = document.getElementById(inputId);
+ if(inputEl){
+ if (inputEl.setSelectionRange) {
+ inputEl.focus();
+ inputEl.setSelectionRange(cursorOffset, cursorOffset);
+ } else if (inputEl.createTextRange) {
+ var range = inputEl.createTextRange();
+ range.collapse(true);
+ range.moveEnd('character', cursorOffset);
+ range.moveStart('character', cursorOffset);
+ range.select();
+ }
+ }
+}
+
+function pickFccDone(o){
+ updateElementValue('fieldFcc', o.f);
+ return false;
+}
+
+function fccExists(force){
+ var elF = document.getElementById('fieldFcc');
+ var elC = document.getElementById('fieldFccCollection');
+ var o = {
+ c:elC.value,
+ f:elF.value,
+ oncancel: function(){
+ elF.focus();
+ elF.select()
+ }
+ };
+
+ if(force)
+ o.create = 'yes';
+
+ if(elF && elC && elF.value.length && elC.value.length)
+ folderExists(o);
+}
+
+function expandAddress(expObj){
+ var eUrl = 'conduit/expand.tcl?';
+ if(expObj.book){
+ eUrl += 'book=' + expObj.book;
+ eUrl += '&index=' + expObj.ai;
+ }
+ else if(expObj.addrs){
+ eUrl += 'addrs=' + encodeURIComponent(expObj.addrs);
+ }
+ else
+ return;
+
+ var expandDS = new YAHOO.util.DataSource(eUrl);
+ expandDS.responseType = YAHOO.util.DataSource.TYPE_XML;
+ expandDS.responseSchema = {
+ resultNode: 'Result',
+ fields: ['Error','Address','Fcc']
+ };
+ expandDS.sendRequest('',
+ {
+ success: function(oReq,oResp,oPayload){
+ var errs = false;
+ if(expObj.addrs && oResp.results[0] && !oResp.results[0].Error) updateElementValue(expObj.fieldId, '');
+ for(var i = 0; i < oResp.results.length; i++){
+ if(oResp.results[i].Error){
+ addStatusMessage('Address Error: '+oResp.results[i].Error, 10);
+ errs++;
+ }
+ else if(oResp.results[i].Address){
+ appendAddress(expObj.fieldId, oResp.results[i].Address);
+ if(expObj.fieldId == 'fieldTo' && oResp.results[i].Fcc && oResp.results[i].Fcc.length) updateElementValue('fieldFcc', oResp.results[i].Fcc);
+ }
+ }
+ if(errs){
+ displayStatusMessages();
+ var el = document.getElementById(expObj.fieldId);
+ if(el) el.focus();
+ }
+ },
+ failure: function(oReq,oResp,oPayload){
+ showStatusMessage('Error expanding Field: ' + oResp.responseText, 10);
+ },
+ scope: this,
+ argument:[expObj]
+ });
+
+}
+
+function donePickContact(fieldId){
+ switch (YAHOO.alpine.pickcontact.tabs.get('activeIndex')){
+ case 0 :
+ var l = document.getElementsByName('nickList');
+ if(l){
+ var i, ai = contactsChecked('Add');
+ for(i in ai) expandAddress({book:ai[i].book,ai:ai[i].index,fieldId:fieldId});
+ }
+ break;
+ case 1 :
+ var l = document.getElementsByName('qrList');
+ if(l){
+ var i, el, addrs = '';
+ for(i = 0; i < l.length; i++){
+ if(l[i].checked){
+ if(addrs.length) addrs += ', ';
+ el = document.getElementById('qrPers'+l[i].value);
+ if(el){
+ var specials = el.innerHTML.match(/[()<>@,;:\\\".\[\]]/);
+ if(specials) addrs += '"' + el.innerHTML + '"';
+ else addrs += el.innerHTML;
+ }
+ el = document.getElementById('qrAddr'+l[i].value);
+ if(el) addrs += ' <' + el.innerHTML + '>';
+ }
+ }
+
+ appendAddress(fieldId,addrs);
+ }
+ break;
+ }
+}
+
+function appendAddress(fieldId, addrs){
+ if(addrs.length){
+ var el = document.getElementById(fieldId);
+ if(el){
+ if(el.value.length) el.value += ', ';
+ el.value += addrs;
+ }
+ }
+}
+
+function validateAddressField(o){
+ if(YAHOO.alpine.expandaddress){
+ if(o.value.length) expandAddress({addrs:o.value,fieldId:o.id});
+ }
+ return(false);
+}
+
+function pickContact(fieldName, fieldId, objParm, markUp){
+ var dom = YAHOO.util.Dom;
+ var alp = YAHOO.alpine.pickcontact;
+ if (alp.markup){
+ var div = document.createElement('div');
+ div.innerHTML = alp.markup;
+ document.body.appendChild(div);
+
+ var contactDialog = document.getElementById('contactDialog');
+ if(contactDialog){
+ alp.tabs = new YAHOO.widget.TabView('contactDialog');
+ alp.tabs.subscribe('activeTabChange',
+ function(e){
+ if(alp.tabs.getTabIndex(e.newValue) == 1){
+ boxChecked(null); // set action button
+ var el = document.getElementById('dirQuery');
+ el.focus();
+ el.select();
+ }
+ });
+ }
+ else contactDialog = document.getElementById('contactList');
+
+ var objDone = {
+ label:'Add ' + fieldName + ' Address',
+ disabled: true,
+ fn: function(){ donePickContact(fieldId); },
+ doonreturn:true
+ };
+
+ panelDialog('Add ' + fieldName + ' Address', contactDialog, objDone);
+ setPanelBodyWidth('clistContacts');
+ drawContactList('contactList',0,objParm);
+ }
+
+ return false;
+}
+
+
+function drawLDAPResult(objResult){
+ var dom = YAHOO.util.Dom;
+ var el = document.getElementById('dirResult');
+ if(el){
+ var elResult;
+ if(objResult.error){
+ elResult = document.createElement('span');
+ elResult.innerHTML = objResult.error;
+ }
+ else if(objResult.results){
+ var o, n, t, qrPers, elTR, elTD, elCB, resId, elLabel;
+ elResult = document.createElement('table');
+ elResult.setAttribute('width','100%');
+ elResult.setAttribute('cellSpacing','0');
+ elResult.setAttribute('cellPadding','0');
+ for(var i = 0; i < objResult.results.length; i++){
+ o = objResult.results[i];
+ n = (o.email) ? o.email.length : 0;
+ if(n){
+ elTR = elResult.insertRow(elResult.rows.length);
+ elTD = elTR.insertCell(elTR.cells.length);
+
+ elCB = createInputElement('checkbox','qrList');
+ elCB.setAttribute('value',elResult.rows.length);
+ resId = 'dirEnt' + elResult.rows.length;
+ elCB.setAttribute('id',resId);
+
+ YAHOO.util.Event.addListener(elCB,'click',boxClicked);
+ elTD.appendChild(elCB);
+
+ elTD = elTR.insertCell(elTR.cells.length);
+ dom.addClass(elTD,'wap');
+ if(o.personal){
+ qrPers = o.personal;
+ }
+ else
+ qrPers = '';
+
+ elLabel = createNameValueElement('label','for',resId);
+ elTD.appendChild(elLabel);
+ elLabel.setAttribute('id','qrPers'+elResult.rows.length);
+ elLabel.innerHTML = o.personal;
+
+
+ for(var j = 0; j < n; j++){
+ if(j > 0){
+ elTR = elResult.insertRow(elResult.rows.length);
+ elTD = elTR.insertCell(elTR.cells.length);
+ elCB = createInputElement('checkbox','qrList');
+ elCB.setAttribute('value',elResult.rows.length);
+ resId = 'dirEnt' + elResult.rows.length;
+ elCB.setAttribute('id',resId);
+
+ YAHOO.util.Event.addListener(elCB,'click',boxClicked);
+ elTD.appendChild(elCB);
+ elTD = elTR.insertCell(elTR.cells.length);
+ dom.addClass(elTD,'wap');
+
+
+ elLabel = createNameValueElement('label','for',resId);
+ elTD.appendChild(elLabel);
+ elLabel.setAttribute('id','qrPers'+elResult.rows.length);
+ elLabel.innerHTML = qrPers;
+ }
+ elTD = elTR.insertCell(elTR.cells.length);
+ dom.addClass(elTD,'wap');
+
+ elLabel = createNameValueElement('label','for',resId);
+ elTD.appendChild(elLabel);
+ elLabel.setAttribute('id','qrAddr'+elResult.rows.length);
+ elLabel.innerHTML = o.email[j];
+ }
+ }
+ }
+ }
+
+ el.replaceChild(elResult,el.firstChild);
+ el = document.getElementById('dirQuery');
+ if(el) el.focus();
+ }
+}
+
+function boxClicked(e){
+ boxChecked(YAHOO.util.Event.getTarget(e));
+}
+
+function boxClear(){
+ var l = (YAHOO.alpine.pickcontact.tabs.get('activeIndex') == 0) ? 'nickList' : 'qrList';
+ var el = document.getElementsByName(l);
+ if(el){
+ for(var i = 0; i < el.length; i++) el[i].checked = false;
+ }
+
+ panelDialogEnableButton(false);
+}
+
+function boxChecked(o){
+ if(o) markOne(o);
+ var l = (YAHOO.alpine.pickcontact.tabs.get('activeIndex') == 0) ? 'nickList' : 'qrList';
+ var el = document.getElementsByName(l);
+ if(el){
+ for(var i = 0; i < el.length; i++){
+ if(el[i].checked){
+ panelDialogEnableButton(true);
+ return;
+ }
+ }
+ }
+
+ panelDialogEnableButton(false);
+}
+
+function setPriority(elClick,priority){
+ var el, pos, dom = YAHOO.util.Dom;
+ for(var i = 0; i <= 5; i++){
+ el = document.getElementById('pri'+i);
+ if(dom.hasClass(el.firstChild,'spfcl3')){
+ dom.removeClass(el.firstChild,'spfcl3');
+ dom.addClass(el.firstChild,'blank');
+ break;
+ }
+ }
+
+ dom.removeClass(elClick.firstChild,'blank');
+ dom.addClass(elClick.firstChild,'spfcl3');
+ el = document.getElementById('priority');
+ el.value = priority;
+ elClick.blur();
+ return false;
+}
diff --git a/web/cgi/alpine/2.0/lib/contacts.js b/web/cgi/alpine/2.0/lib/contacts.js
new file mode 100644
index 00000000..c4d13be1
--- /dev/null
+++ b/web/cgi/alpine/2.0/lib/contacts.js
@@ -0,0 +1,121 @@
+/* $Id: contacts.js 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+ * ========================================================================
+ * Copyright 2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+function boxChecked(o){
+ if(o) markOne(o);
+}
+
+function editContact(o){
+ getContact({ book:o.book, index:o.index, f:function(c){ contactEditor(c,storeEditedContact); }});
+ return(false);
+}
+
+function editCheckedContact(){
+ var checked = contactsChecked('');
+ switch (checked.length){
+ case 1 :
+ getContact({ book:checked[0].book, index:checked[0].index, f:function(c){ contactEditor(c,storeEditedContact); }});
+ break;
+ default :
+ panelAlert('Choose just one contact to Edit');
+ case 0 :
+ break;
+ }
+
+ return(false);
+}
+
+function contactDelete(){
+ var checked = contactsChecked('');
+ var plural = (checked.length > 1) ? 's' : '';
+ var count = (checked.length > 1) ? '<b>' + checked.length + '</b> ' : '';
+ if(checked.length) panelConfirm('Are you sure you want to permanently delete the ' + count + 'selected contact' + plural + '?',{text:'Delete Forever',fn:doContactDelete});
+ return false;
+}
+
+function doContactDelete(o){
+ var checked = contactsChecked('');
+ if(checked.length){
+ var el = YAHOO.alpine.containers.contactlist;
+ var elist = '';
+ for(var i = 0; i < checked.length; i++){
+ if(elist.length) elist += ',';
+ elist += checked[i].book + '.' + checked[i].index;
+ }
+
+ if(el && elist.length){
+ var o = {
+ hdr:'on',
+ sendto:'on',
+ canedit:'on',
+ op:'delete',
+ entryList:elist
+ }
+
+ newContactList(el,null,gCurrentAbook,o);
+ }
+ }
+}
+
+function storeEditedContact(oFields){
+ var el = YAHOO.alpine.containers.contactlist;
+ if(el){
+ var o = {
+ hdr:'on',
+ sendto:'on',
+ canedit:'on',
+ op:'change'
+ }
+
+ for(var f in oFields){
+ o[f] = oFields[f];
+ }
+
+ newContactList(el,null,gCurrentAbook,o);
+ }
+}
+
+function storeNewContact(oFields){
+ var el = YAHOO.alpine.containers.contactlist;
+ if(el){
+ var o = {
+ hdr:'on',
+ sendto:'on',
+ canedit:'on',
+ op:'add'
+ }
+
+ for(var f in oFields){
+ o[f] = oFields[f];
+ }
+
+ newContactList(el,null,gCurrentAbook,o);
+ }
+}
+
+function sendToContact(){
+ var checked = contactsChecked('Send Email');
+ if(checked.length){
+ var cUrl = 'compose?contacts=';
+ var comma = '';
+ for(var i = 0; i < checked.length; i++){
+ cUrl += comma + checked[i].book + '.' + checked[i].index;
+ comma = ',';
+ }
+
+ window.location.href = cUrl;
+ }
+
+ return(false);
+}
diff --git a/web/cgi/alpine/2.0/lib/folders.js b/web/cgi/alpine/2.0/lib/folders.js
new file mode 100644
index 00000000..4f6b9d24
--- /dev/null
+++ b/web/cgi/alpine/2.0/lib/folders.js
@@ -0,0 +1,258 @@
+/* $Id: folders.js 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+ * ========================================================================
+ * Copyright 2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+function doOpen(){
+ var fce = document.getElementById('pickFolderCollection');
+ var fc = (fce) ? fce.value : null;
+ var fpe = document.getElementById('pickFolderPath');
+ var fp = (fpe && fpe.value && fpe.value.length) ? '/' + fpe.value + '/' : '/';
+ var fne = document.getElementById('pickFolderName');
+ var fn = (fne && fne.value && fne.value.length) ? fne.value : null;
+ if(fc && fp && fn) location.href = 'browse/'+ fc + fp + encodeURIComponent(fn);
+ else panelAlert('Which folder would you like to View?<p>Click a folder\'s name to select it, then click <b>View&nbsp;Messages</b>.');
+ return false;
+}
+
+function doDelete(o){
+ var el = document.getElementById('alpineContent');
+ newFolderList(el,null,o.c,o.path,{op:'delete',f:o.folder});
+ var fne = document.getElementById('pickFolderName');
+ if(fne) fne.value = '';
+ var base, link;
+ if((base = document.getElementsByTagName('base')) && base.length) link = base[0].getAttribute('href');
+ else link = '';
+ link += 'browse/'+o.c+'/'+o.path+encodeURIComponent(o.folder);
+ for(var i = 0; i < 20 ; i++){// 20 > fldr_cache_max
+ var target = 'target' + i;
+ el = document.getElementById(target);
+ if(!el) break;
+ if(el.firstChild.href == link){
+ YAHOO.util.Dom.setStyle(target,'display','none');
+ break;
+ }
+ }
+}
+
+function queryDelete(){
+ if(YAHOO.alpine.current.incoming){
+ showStatusMessage('Cannot delete Incoming Folders yet',5);
+ return false;
+ }
+
+ var fc = document.getElementById('pickFolderCollection').value;
+ var fp = document.getElementById('pickFolderPath').value;
+ var fn = document.getElementById('pickFolderName').value;
+ if(fn.length) panelConfirm('Are you sure you want to permanently delete the folder <b>' + fn + '</b> and its contents?<p>Deleted folders are gone forever.',{text:'Delete Forever',fn:doDelete,args:{c:fc,path:fp,folder:fn}});
+ else panelAlert('Which folder would you like to Delete?<p>Click a folder\'s name to select it, then click <b>Delete</b>.');
+
+ return false;
+}
+
+function addFolder(){
+ if(YAHOO.alpine.current.incoming){
+ showStatusMessage('Cannot add new Incoming Folders yet',5);
+ return false;
+ }
+
+ var dom = YAHOO.util.Dom;
+ var fp = document.getElementById('pickFolderPath').value;
+ var fc = document.getElementById('pickFolderCollection').value;
+ if(fc >= 0){
+ var dbody = document.createDocumentFragment();
+ var div = document.createElement('div');
+ dom.addClass(div,'panelExplanation');
+ var lc = 'in', l = 'Name of Folder to create. To create the folder within a subdirectory, simply prepend the subdirectory\'s name.';
+ var el = document.createTextNode(l);
+ div.appendChild(el);
+ dbody.appendChild(div);
+
+ // Folder Name
+ div = document.createElement('div');
+ dom.addClass(div,'panelInput');
+ div.innerHTML = 'Folder Name:';
+ var input = createInputElement('text','');
+ div.appendChild(input);
+ YAHOO.util.Event.addListener(input,'keyup',panelDialogInputChange);
+ dbody.appendChild(div);
+ panelDialog('New Folder',
+ dbody,
+ {
+ label:"New Folder",
+ disabled: true,
+ doonreturn: true,
+ fn: function(){
+ if(input.value.length) newFolderList(document.getElementById('alpineContent'),null,fc,fp,{op:'add',f:input.value});
+ else showStatusMessage('No folder name provided. No folder added.',5);
+ var fne = document.getElementById('pickFolderName');
+ if(fne) fne.value = '';
+ }
+ });
+ input.focus();
+ }
+
+ return false;
+}
+
+function renameFolder(){
+ if(YAHOO.alpine.current.incoming){
+ showStatusMessage('Cannot rename Incoming Folders yet',5);
+ return false;
+ }
+
+ var dom = YAHOO.util.Dom;
+ var fc = document.getElementById('pickFolderCollection').value;
+ var fp = document.getElementById('pickFolderPath').value;
+ var fn = document.getElementById('pickFolderName').value;
+ if(fc >= 0 && fn.length){
+ var dbody = document.createDocumentFragment();
+ var div = document.createElement('div');
+ dom.addClass(div,'panelExplanation');
+ var lc = 'in', l = 'Rename folder ';
+ if(fp.length){
+ l += 'in directory ' + fp + ' ';
+ lc = 'of';
+ }
+ if(YAHOO.alpine.current.collections[fc]) l += lc+' collection ' + YAHOO.alpine.current.collections[fc];
+ div.innerHTML = l;
+ dbody.appendChild(div);
+
+ // Folder Name Input
+ div = document.createElement('div');
+ dom.addClass(div,'panelInput');
+ div.innerHTML = 'New Folder Name:';
+
+ var input = createInputElement('text','');
+ var fnt = fn.match(/[\/]?([^\/]*)$/);
+ input.setAttribute('value',fnt[1]);
+ div.appendChild(input);
+
+ //YAHOO.util.Event.addListener(el,'keyup',panelDialogInputChange);
+ dbody.appendChild(div);
+
+ panelDialog('Rename Folder',
+ dbody,
+ {
+ label:"Rename Folder",
+ doonreturn: true,
+ fn: function(){
+ if(fc >= 0 && fn.length && input.value.length){
+ if(fn != input.value){
+ var el = document.getElementById('alpineContent');
+ newFolderList(el,null,fc,fp,{op:'rename',sf:fn,df:input.value});
+ var fne = document.getElementById('pickFolderName');
+ if(fne) fne.value = '';
+ }
+ else showStatusMessage('Folder name unchanged.',5);
+ }
+ else showStatusMessage('Folder name unchanged.',5);
+ }
+ });
+
+ input.select();
+ }
+ else panelAlert('Which folder would you like to rename?<p>Click a folder\'s name to select it, then click <b>Rename</b>.');
+
+ return false;
+}
+
+function exportFolder(){
+ var dom = YAHOO.util.Dom;
+ var fc = document.getElementById('pickFolderCollection').value;
+ var fp = document.getElementById('pickFolderPath').value;
+ var fn = document.getElementById('pickFolderName').value;
+ var t;
+ if(fc >= 0 && fn.length){
+ var url = 'conduit/export/' + fc + '/';
+ if(fp.length) url += encodeURIFolderPath(fp) + '/';
+ url += encodeURIComponent(fn);
+ window.location.href = document.getElementsByTagName('base')[0].href + url;
+ t = '<h3>Export Email</h3>Web Alpine is preparing folder <b>'+fn+'</b> for download. &nbsp;';
+ t += 'Your browser\'s File Open dialog should appear momentarily.<p>';
+ t += 'The exported file will be in a format compatible with many desktop mail programs. &nbsp;';
+ t += 'You can also use <i>Import Email</i> to copy the folder back into Web Alpine.<p>';
+ t += 'If the download completes without error, you may delete the folder from Web Alpine.<p>';
+ }
+ else
+ t = 'Which folder would you like to export?<p>Click a folder\'s name to select it, then click <b>Export&nbsp;Email...</b>.'
+
+ panelAlert(t);
+ return false;
+}
+
+function redrawFolderList(){
+ var fc = document.getElementById('pickFolderCollection').value;
+ var fp = document.getElementById('pickFolderPath').value;
+ if(fc >= 0){
+ var el = document.getElementById('alpineContent');
+ newFolderList(el,null,fc,fp,{op:'noop'});
+ }
+}
+
+function importFolder(){
+ if(YAHOO.alpine.current.incoming){
+ showStatusMessage('Cannot import Incoming Folders yet',5);
+ return false;
+ }
+
+ var dom = YAHOO.util.Dom;
+ var event = YAHOO.util.Event;
+ var fc = document.getElementById('pickFolderCollection').value;
+ var fp = document.getElementById('pickFolderPath').value;
+ var form = document.createElement('form');
+ form.setAttribute('action','conduit/import/' + fc + '/' + encodeURIFolderPath(fp));
+ form.setAttribute('method','POST');
+ form.setAttribute('enctype','multipart/form-data');
+ form.setAttribute('target','formResponse');
+
+ var div = document.createElement('div');
+ dom.addClass(div,'panelExplanation');
+ div.innerHTML = 'Folder Import copies a mail folder, typically created by the <i>Export</i> command, from the computer your browser is running on into a new Web Alpine folder. Successful Import consists of three steps.<p>First, enter the path and filename of the folder below. Use the Browse button to help choose the file.';
+ form.appendChild(div);
+
+ div = document.createElement('div');
+ dom.addClass(div,'panelInput');
+ var input0 = createInputElement('file','file');
+ event.addListener(input0,'keypress',panelDialogInputChange);
+ event.addListener(input0,'change',panelDialogInputChange);
+ div.appendChild(input0);
+ form.appendChild(div);
+
+ div = document.createElement('div');
+ div.innerHTML = 'Second, provide a <b>unique</b> name to give the uploaded folder';
+ if(YAHOO.alpine.current.collections[fc]) div.innerHTML += ' within the '+ YAHOO.alpine.current.collections[fc]+' collection.';
+ dom.addClass(div,'panelExplanation');
+ form.appendChild(div);
+
+ div = document.createElement('div');
+ dom.addClass(div,'panelInput');
+ div.innerHTML = 'Name the folder:';
+ var input = createInputElement('text','newFolder');
+ div.appendChild(input);
+ form.appendChild(div);
+
+ div = document.createElement('div');
+ div.innerHTML = 'Finally, click <b>Import&nbsp;Folder</b> to copy the folder into Web Alpine.';
+ dom.addClass(div,'panelExplanation');
+ form.appendChild(div);
+
+
+ panelDialog('Import Folder', form,
+ {
+ label:'Import Folder',
+ disabled: true,
+ fn: function(){ showLoading(); form.submit(); }
+ });
+ input0.focus();
+ return false;
+}
diff --git a/web/cgi/alpine/2.0/lib/mailbox.js b/web/cgi/alpine/2.0/lib/mailbox.js
new file mode 100644
index 00000000..9879137f
--- /dev/null
+++ b/web/cgi/alpine/2.0/lib/mailbox.js
@@ -0,0 +1,1076 @@
+/* $Id: mailbox.js 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+ * ========================================================================
+ * Copyright 2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*
+ * Scripts common to mailbox viewing: browse and view
+ */
+
+// Globals
+YAHOO.alpine.morcButton = [];
+YAHOO.alpine.select = { all:false, bannerId:'bannerSelection' };
+
+
+// BROWSE FUNCTIONS
+
+/* priority column display is overloaded. Uses el.title to store state. */
+/* changes to link titles in that column MUST be coordinated with this stuff. */
+/* Tricky */
+var gLabelStarred = 'Starred';
+var gLabelPriHi = 'High Priority';
+var gLabelPriHier = 'Highest Priority';
+
+
+function applyFlag(ctrlObj,flagName,flagState) {
+ switch (flagName) { // validate
+ case 'new' : break; // read (New) flag
+ case 'imp' : break; // important flag
+ case 'del' : break; // delete flag
+ default : return false;
+ }
+ YAHOO.util.Connect.asyncRequest('GET',
+ 'conduit/apply.tcl' + encodeURI('?u=all&f=' + flagName + '&s=' + flagState) + urlSalt('&'),
+ {
+ customevents: {
+ onStart:function(eventType){ showLoading(); },
+ onComplete:function(eventType){ hideLoading(); }
+ },
+ success: function(o) {
+ // valid response is "<numApplied> <numSelected> <numTotalMsgs>"
+ var matchResult;
+ if((matchResult = o.responseText.match(/^(\d+) (\d+) (\d+)$/m)) == null){
+ showStatusMessage('Error: ' + o.responseText, 10);
+ }
+ else{
+ var plural = (matchResult[1] > 1) ? 's' : '';
+ if(flagName == 'del'){
+ showStatusMessage(matchResult[1] + ' message' + plural + ' deleted', 3);
+ YAHOO.alpine.current.selected = matchResult[2];
+ YAHOO.alpine.current.count = matchResult[3];
+ hideSelectAllInfo();
+ }
+ else{
+ var chk = document.getElementsByName('uidList');
+ var sm;
+ if(flagName == 'imp'){
+ var act = (flagState == 'not') ? 'Cleared' : 'Set';
+ sm = act + ' Star on ' + matchResult[1] + ' message' + plural;
+ }
+ else{
+ sm = 'Marked ' + matchResult[1] + ' message' + plural + ' as ';
+ if(flagState == 'not'){
+ decUnreadCount('Current' ,matchResult[1]);
+ sm += 'Read';
+ }
+ else{
+ incUnreadCount('Current',matchResult[1]);
+ sm += 'Unread';
+ }
+ }
+ showStatusMessage(sm, 3);
+ for(var i = 0; i < chk.length; i++){
+ if(chk[i].checked){
+ var dom = YAHOO.util.Dom;
+ if(flagName == 'imp'){
+ var offset = 0;
+ var m = document.getElementById('star'+chk[i].value);
+ if(m){
+ var classAdd = '', classDel = '', newtitle = '';
+ if(flagState == 'not'){
+ classAdd = 'nostar';
+ classDel = 'star';
+ if(m.title.substr(0,gLabelStarred.length) == gLabelStarred){
+ if(m.title.length > gLabelStarred.length){
+ newtitle = m.title.substr(gLabelStarred.length + 5);
+ if(newtitle == gLabelPriHi){
+ classAdd = 'prihi';
+ }
+ else if(newtitle == gLabelPriHier){
+ classAdd = 'prihier';
+ }
+ }
+ else{
+ newtitle = 'Set ' + gLabelStarred;
+ }
+ }
+ }
+ else{
+ classAdd = 'star';
+ switch (m.title) {
+ case 'Set ' + gLabelStarred :
+ newtitle = gLabelStarred;
+ classDel = 'nostar';
+ break;
+ case gLabelPriHi :
+ classDel = 'prihi';
+ newtitle = gLabelStarred + ' and ' + gLabelPriHi;
+ break;
+ case gLabelPriHier :
+ classDel = 'prihier';
+ newtitle = gLabelStarred + ' and ' + gLabelPriHier;
+ break;
+ default :
+ break;
+ }
+ }
+
+ if(!dom.hasClass(m.firstChild,classAdd)) dom.addClass(m.firstChild,classAdd);
+ if(classDel.length && dom.hasClass(m.firstChild,classDel)) dom.removeClass(m.firstChild,classDel);
+ if(newtitle.length) m.title = newtitle;
+ }
+ }
+ else if(flagName == 'new'){
+ var hid = 'h1'+chk[i].value;
+ var mnode = chk[i].parentNode.parentNode;
+ if(flagState == 'not'){
+ if(dom.hasClass(hid,'unread')) dom.removeClass(hid,'unread');
+ if(dom.hasClass(mnode,'unread')) dom.removeClass(mnode,'unread');
+ }
+ else{
+ if(!dom.hasClass(hid,'unread')) dom.addClass(hid,'unread');
+ if(!dom.hasClass(mnode,'unread')) dom.addClass(mnode,'unread');
+ }
+ }
+ }
+ }
+ showSelectAllInfo();
+ }
+ }
+ },
+ failure: function(o) { showStatusMessage('Request Failure: ' + o.statusText + '. Please report this.', 10)}
+ });
+
+ if(ctrlObj) ctrlObj.blur();
+ return false;
+}
+
+
+//
+// SELECTION (e.g. SELECT ALL)
+//
+
+function initSelection() {
+ var ac = YAHOO.alpine.current;
+ if (ac.selected > 0) {
+ if (ac.selected == ac.count) {
+ showSelectAllInfo(4);
+ } else if (ac.selected) {
+ showSelectAllInfo(2);
+ }
+ }
+ else hideSelectAllInfo();
+}
+
+function hideSelectAllInfo() {
+ var banner = document.getElementById(YAHOO.alpine.select.bannerId);
+ if(banner) YAHOO.util.Dom.setStyle(banner,'display','none');
+}
+
+function showSelectAllInfo(num) {
+ var banner = document.getElementById(YAHOO.alpine.select.bannerId);
+ if (banner){
+ setSelectAllInfoText(num);
+ YAHOO.util.Dom.setStyle(banner,'display','block');
+ }
+}
+
+function setSelectAllInfoText(state) {
+ // NOTE: "\u00a0" is the unicode equivalent of no-break space character
+ var ac = YAHOO.alpine.current;
+ var banner = document.getElementById(YAHOO.alpine.select.bannerId);
+ if (!banner) return;
+ var msg;
+ var pluralSelected = (ac.selected > 1) ? 's' : '';
+ var predSelected = (ac.selected > 1) ? 'are' : 'is';
+ var pluralTotal = (ac.count > 1) ? 's' : '';
+ var groupName = (ac.focused) ? 'Search Results' : ac.f;
+ switch (state) {
+ case 1:
+ banner.innerHTML = "All <b>" + ac.selected + "<\/b> message" + pluralSelected + " in " + groupName + " " + predSelected + " selected.";
+ banner.style.backgroundColor = "#ffffa6";
+ break;
+ case 2:
+ banner.innerHTML = "<b>" + ac.selected + "<\/b> message" + pluralSelected + " in " + groupName + " " + predSelected + " selected. \u00a0\u00a0\u00a0\u00a0\u00a0 [<a href='browse/" + ac.c + "/" + ac.f + "/" + ac.u + "?select=all' onClick='return selectAllInFolder();'>Select All <b>" + ac.count + " <\/b> Message" + pluralTotal + " in " + groupName + "<\/a>] \u00a0\u00a0 [<a href='browse/" + ac.c + "/" + ac.f + "/" + ac.u + "?select=none' onClick='return unSelectAllInFolder();'>Unselect All<\/a>]";
+ banner.style.backgroundColor = "#ffffa6";
+ break;
+ case 3:
+ banner.innerHTML = "All <b>" + ac.selected + "<\/b> message" + pluralSelected + " on this page " + predSelected + " selected. \u00a0\u00a0\u00a0\u00a0\u00a0 [<a href='browse/" + ac.c + "/" + ac.f + "/" + ac.u + "?select=all' onClick='return selectAllInFolder();'>Select All <b>" + ac.count + " <\/b> Message" + pluralTotal + " in " + groupName + "<\/a>]";
+ banner.style.backgroundColor = "#ffffa6";
+ break;
+ case 4:
+ banner.innerHTML = "<b>All <u>" + ac.count + "<\/u> message" + pluralTotal + " in " + groupName + " " + predSelected + " selected.<\/b> \u00a0\u00a0\u00a0\u00a0\u00a0 [<a href='browse/" + ac.c + "/" + ac.f + "/" + ac.u + "?select=none' onClick='return unSelectAllInFolder();'>Unselect All<\/a>]";
+ banner.style.backgroundColor = "#ffdebf";
+ break;
+ case 5:
+ banner.innerHTML = "<b>Selection Cleared - No messages selected in " + groupName + ".<\/b>";
+ banner.style.backgroundColor = "#ffdebf";
+ break;
+ }
+}
+
+
+function markMessage(msg) {
+ var ac = YAHOO.alpine.current;
+ var chk = document.getElementsByName('uidList');
+ var selectAll = document.getElementById('selectall');
+ var numCheckbox = isNaN(chk.length) ? 1 : chk.length;
+ msg.parentNode.parentNode.id = msg.checked ? "sd" : "";
+ if (YAHOO.alpine.select.all) {
+ YAHOO.alpine.select.all = false;
+ }
+ if (!msg.checked) {
+ --ac.selected;
+ if (ac.selected) {
+ setSelectAllInfoText(2);
+ } else {
+ hideSelectAllInfo();
+ }
+ selectAll.checked = false;
+ } else {
+ ++ac.selected;
+ if (ac.count <= numCheckbox) {
+ setSelectAllInfoText(1);
+ } else {
+ setSelectAllInfoText(2);
+ }
+ }
+ updateMessageState(msg.value, msg.checked);
+}
+
+function markAllMessages() {
+ var ac = YAHOO.alpine.current;
+ var chk = document.getElementsByName('uidList');
+ var isChecked = document.getElementById('selectall').checked;
+ var numChanged = 0;
+ var numCheckbox;
+ var markedUidList = '';
+ if (chk) {
+ hideDisplayDivOrSpan("bannerConfirm"); // remove any confirmation banner from page
+ numCheckbox = isNaN(chk.length) ? 0 : chk.length;
+ var selectedClass = isChecked ? "sd" : "";
+ for (var i = 0; i < numCheckbox; i++) {
+ if (chk[i].checked != isChecked) {
+ ++numChanged;
+ }
+ chk[i].checked = isChecked;
+ chk[i].parentNode.parentNode.id = selectedClass;
+ markedUidList += ',' + chk[i].value;
+ }
+
+ if (isChecked) { // if Select all
+ ac.selected += numChanged;
+ if (ac.selected > numCheckbox) {
+ showSelectAllInfo(2);
+ } else if (ac.count > numCheckbox) {
+ showSelectAllInfo(3);
+ }
+ } else { // if UNselect all
+ ac.selected -= numChanged;
+ if (ac.selected) {
+ showSelectAllInfo(2);
+ } else {
+ hideSelectAllInfo();
+ }
+ }
+ updateMessageState(markedUidList, isChecked);
+ }
+}
+
+function selectAllInFolder() {
+ var alp = YAHOO.alpine;
+ var chk = document.getElementsByName('uidList');
+ document.getElementById('selectall').checked = true;
+ var numCheckbox;
+ if (chk) {
+ hideDisplayDivOrSpan("bannerConfirm"); // remove any confirmation banner from page
+ numCheckbox = isNaN(chk.length) ? 1 : chk.length;
+ if (numCheckbox == 1) {
+ chk.checked = true;
+ chk.parentNode.parentNode.id = "sd";
+ } else {
+ for (var i = 0; i < chk.length; i++) {
+ chk[i].checked = true;
+ chk[i].parentNode.parentNode.id = "sd";
+ }
+ }
+
+ var all = (alp.current.focused) ? 'searched' : 'all';
+ alp.current.selected = (alp.current.focused) ? alp.current.focused : alp.current.count;
+ updateMessageState(all,'true');
+ }
+ showSelectAllInfo(4);
+ YAHOO.alpine.select.all = (alp.current.focused == 0);
+ return false;
+}
+
+function unSelectAllInFolder() {
+ var alp = YAHOO.alpine;
+ var chk = document.getElementsByName('uidList');
+ var numCheckbox;
+ if (chk) {
+ numCheckbox = isNaN(chk.length) ? 1 : chk.length;
+ YAHOO.alpine.current.selected = 0;
+ // setSelectAllInfoText(5);
+ document.getElementById('selectall').checked = false;
+ if (numCheckbox == 1) {
+ chk.checked = false;
+ chk.parentNode.parentNode.id = "";
+ } else {
+ for (var i = 0; i < chk.length; i++) {
+ chk[i].checked = false;
+ chk[i].parentNode.parentNode.id = "";
+ }
+ }
+
+ var all = (alp.current.focused) ? 'searched' : 'all';
+ alp.select.all = false;
+ updateMessageState(all,'false');
+ }
+ hideSelectAllInfo();
+ return false;
+}
+
+function numCheckedOnPage() {
+ var chk = document.getElementsByName('uidList');
+ var numCheckbox;
+ var numChecked=0;
+ if (chk) {
+ numCheckbox = isNaN(chk.length) ? 1 : chk.length;
+ if ((numCheckbox==1) && (chk.checked)) {
+ numChecked = 1;
+ } else {
+ for (var i = 0; i < chk.length; i++) {
+ if (chk[i].checked) {
+ ++numChecked;
+ }
+ }
+ }
+ }
+
+ return numChecked;
+}
+
+
+// Communicate state changes to server
+function updateMessageState(u,m){
+ YAHOO.util.Connect.asyncRequest('GET',
+ 'conduit/mark.tcl' + encodeURI('?u=' + u + '&mark=' + m) + urlSalt('&'),
+ {
+ success: function(o) {},
+ failure: function(o) {
+ newStatusMessage('Request Failure: ' + o.statusText);
+ }
+ });
+}
+
+// Mailbox Search
+function mailboxSearch(){
+ var elField = document.getElementById('searchField');
+ if(elField && elField.value && elField.value.length){
+ var elScope = document.getElementById('searchScope');
+ var scope = (elScope) ? elScope.options[elScope.selectedIndex].value : 'new';
+ newMessageList({parms:{op:'search',type:'any',scope:scope,criteria:elField.value}});
+ }
+}
+
+// Confirm Toolbar Actions based on Selection
+function confirmDelete() {
+ var ac = YAHOO.alpine.current;
+ var plural = (ac.selected > 1) ? 's' : '';
+ if (ac.selected != numCheckedOnPage())
+ panelConfirm("Move " + ac.selected + " message" + plural + " from " + ac.f + " to the Trash?<p>Some selected messages are on other pages.",{fn:actuallyDelete},'Delete');
+ else
+ actuallyDelete();
+
+ return(false);
+}
+
+function confirmSpam() {
+ var ac = YAHOO.alpine.current;
+ var plural = (ac.selected > 1) ? 's' : '';
+ if (ac.selected != numCheckedOnPage())
+ panelConfirm("Report " + ac.selected + " message" + plural + " as Spam?<p>Some selected messages are on other pages.",{fn:actuallySpam},'Report Spam');
+ else
+ actuallySpam();
+
+ return(false);
+}
+
+// Confirm Toolbar Actions based on Selection
+function deleteForeverString() {
+ if (YAHOO.alpine.current.selected != numCheckedOnPage()) {
+ return confirm( + " forever?\nSome selected messages are on other pages.");
+ }
+
+ return(YAHOO.alpine.current.selected > 0);
+}
+
+// Complain if no messages are selected for Toolbar Action
+function anySelected(act) {
+ if (YAHOO.alpine.current.selected <= 0){
+ panelAlert('No messages selected to ' + act + '.<p>Select one or more messages by checking the box on the line of each desired message.');
+ return false;
+ }
+
+ return true;
+}
+
+function flipStar(el) {
+ var dom = YAHOO.util.Dom;
+ var u = el.id.substring(4);
+ var flagState = '', iClass = '', iTitle = '', iUnClass = '';
+ if(el.title.substr(0,7) == gLabelStarred){
+ flagState = 'not';
+ iUnClass = 'star';
+ if(el.title.length > 7){
+ switch (el.title.substr(12)){
+ case gLabelPriHi :
+ iClass = 'prihi';
+ iTitle = gLabelPriHi;
+ break;
+ case gLabelPriHier :
+ iClass = 'prihier';
+ iTitle = gLabelPriHier;
+ break;
+ default :
+ return false;
+ }
+ }
+ else{
+ iClass = 'nostar';
+ iTitle = 'Set '+ gLabelStarred;
+ }
+ }
+ else{
+ flagState = 'ton';
+ iClass = 'star';
+ switch(el.title){
+ case 'Set ' + gLabelStarred :
+ iUnClass = 'nostar';
+ iTitle = gLabelStarred;
+ break;
+ case gLabelPriHi :
+ iUnClass = 'prihi';
+ iTitle = gLabelStarred + ' and ' + gLabelPriHi;
+ break;
+ case gLabelPriHier :
+ iUnClass = 'prihier';
+ iTitle = gLabelStarred + ' and ' + gLabelPriHier;
+ break;
+ default : return false;
+ }
+ }
+
+ setStar(u,flagState,function(){ if(iTitle.length) el.title = iTitle; if(iUnClass.length) dom.removeClass(el.firstChild,iUnClass); dom.addClass(el.firstChild,iClass); });
+ return false;
+}
+
+function setStar(u,flagState,onDone){
+ if(u < 0) u = YAHOO.alpine.current.u;
+ YAHOO.util.Connect.asyncRequest('GET',
+ 'conduit/flag.tcl?u=' + u + '&f=imp&s=' + flagState + urlSalt('&'),
+ {
+ success: function(o) { if(onDone) onDone(); },
+ failure: function(o) { showStatusMessage('Request Failure: ' + o.statusText + '. Please report this.', 10)}
+ });
+ return false;
+}
+
+// replace message list/view text
+function updateDivText(div, url){
+ YAHOO.util.Connect.asyncRequest('GET', url,
+ {
+ customevents:{
+ onStart:function(eventType){ showLoading(); },
+ onComplete:function(eventType){ hideLoading(); }
+ },
+ success:function(aro){
+ div.innerHTML = aro.responseText;
+ div.scrollTop = 0;
+ evalScripts(aro.responseText);
+ },
+ failure: function(aro) { showStatusMessage('Request Failure: ' + aro.statusText, 10) },
+ argument: [div]
+ });
+ return false;
+}
+
+// Load next message list for viewing relative to current list
+function newMessageList(o){
+ var div = document.getElementById('alpineContent');
+ if(div){
+ var url = 'newlist.tcl/' + YAHOO.alpine.current.c + '/' + encodeURIFolderPath(YAHOO.alpine.current.f);
+ var conj = '?';
+ if(o && o.parms){
+ for(var prop in o.parms){
+ url += conj + prop + '=' + encodeURIComponent(o.parms[prop]);
+ conj = '&';
+ }
+ }
+ else{
+ url += '?op=noop';
+ conj = '&';
+ }
+
+ url += urlSalt(conj);
+
+ updateDivText(div, url);
+
+ if(o && o.control && o.control.blur) o.control.blur();
+ }
+ return false;
+}
+
+function actuallyDelete()
+{
+ if(numCheckedOnPage()) newMessageList({parms:{'op':'delete'}});
+ else applyFlag(null,'del','ton');
+}
+
+function actuallySpam()
+{
+ newMessageList({parms:{'op':'spam'}});
+}
+
+function doEmpty(ctrlObj,listOption){
+ if(listOption == 'all'){
+ newMessageList({control:ctrlObj,parms:{'op':'trashall'}});
+ }
+ else if(listOption == 'message'){
+ newMessageText({control:ctrlObj,parms:{op:'trash'}});
+ }
+ else{
+ var ac = YAHOO.alpine.current;
+ if(listOption == 'selected' && anySelected('Delete Forever')){
+ var plural = (ac.selected > 1) ? 's' : '';
+ var t = "Delete " + ac.selected + " message" + plural + " from " + ac.f + ' forever?';
+ if(ac.selected != numCheckedOnPage()) t += '<p>Some selected messages are on other pages.';
+ panelConfirm(t,
+ {
+ text:'Delete Forever',
+ fn:function(){ newMessageList({control:ctrlObj,parms:{'op':'trash'}}); }
+ });
+ }
+ }
+
+ if(ctrlObj) ctrlObj.blur();
+ return false;
+}
+
+function doSpam(ctrlObj){
+ if(anySelected('Report as Spam')) confirmSpam();
+ if(ctrlObj) ctrlObj.blur();
+ return false;
+}
+
+// Drag Drop Support
+function hiliteDrop(id,on){
+ var dom = YAHOO.util.Dom;
+ if(on){
+ if(!dom.hasClass(id,'drop'))
+ dom.addClass(id, 'drop');
+
+ if(dom.hasClass(id + 'Icon','splc5')){
+ dom.removeClass(id + 'Icon','splc5');
+ dom.addClass(id + 'Icon','splc10');
+ }
+ else if(dom.hasClass(id + 'Icon','splc7')){
+ dom.removeClass(id + 'Icon','splc7');
+ dom.addClass(id + 'Icon','splc11');
+ }
+ }
+ else{
+ if(dom.hasClass(id,'drop'))
+ dom.removeClass(id, 'drop');
+
+ if(dom.hasClass(id + 'Icon','splc10')){
+ dom.removeClass(id + 'Icon','splc10');
+ dom.addClass(id + 'Icon','splc5');
+ }
+ else if(dom.hasClass(id + 'Icon','splc11')){
+ dom.removeClass(id + 'Icon','splc11');
+ dom.addClass(id + 'Icon','splc7');
+ }
+ }
+}
+
+function dragOntoFolder(uid,o){
+ newMessageList({parms:{'op':'movemsg','df':o.c+'/'+o.f,'uid':uid}});
+ return false;
+}
+
+function canDragit(id,uid,tt){
+ var dom = YAHOO.util.Dom;
+ var dd = new YAHOO.util.DDProxy(id,
+ 'message',
+ {
+ dragElId:'msgDragProxy',
+ resizeFrame: false
+ });
+ dd.isTarget = false;
+ dd.b4MouseDown = function(e){
+ var s = '<b>' + this.getEl().innerHTML + '</b>';
+ var r = dom.getRegion(this.getEl());
+ var w = r.right - r.left;
+ var el = document.getElementById('ml'+uid);
+ r = dom.getRegion(el);
+ w = Math.max(w, (r.right - r.left));
+ if(el && el.innerHTML.length) s += '<br>' + el.innerHTML;
+ el = document.getElementById('msgDragProxyText');
+ el.innerHTML = s;
+ dom.setStyle('msgDragProxy','width',w + 12);
+ return true;
+ };
+ dd.endDrag = function(){
+ dom.setStyle('msgDragProxy','visibility','hidden')
+ };
+ dd.onDragEnter = function(e,id){ hiliteDrop(id,true); };
+ dd.onDragOut = function(e,id){ hiliteDrop(id,false); };
+ dd.onDragDrop = function(e,id){
+ hiliteDrop(id,false);
+ var o = dom.get(id);
+ if(o) o.f(uid,o.args);
+ }
+ if(tt){
+ dd.onMouseDown = function(e){
+ tt.hide();
+ tt.cfg.setProperty('disabled',true);
+ };
+ dd.onMouseUp = function(e){
+ tt.cfg.setProperty('disabled',false);
+ };
+ }
+}
+
+function setDragTarget(id,fHandler,oArgs){
+ if(id && id.length){
+ var o = document.getElementById(id);
+ if(o){
+ o.ddObj = new YAHOO.util.DDTarget(id,'message');
+ o.f = fHandler;
+ o.args = oArgs;
+ }
+ }
+}
+
+function cursor(style){
+ document.body.style.cursor = style;
+}
+
+
+// VIEW FUNCTIONS
+
+function newMessageText(o){
+ var div = document.getElementById('alpineContent');
+ var ac = YAHOO.alpine.current;
+ if(div){
+ var uid = (o.uid) ? o.uid : ac.u;
+ var url = 'newview.tcl/' + ac.c + '/' + encodeURIFolderPath(ac.f) + '/' + uid;
+ var conj = '?';
+ if(o && o.parms){
+ for(var prop in o.parms){
+ url += conj + prop + '=' + encodeURIComponent(o.parms[prop]);
+ conj = '&';
+ }
+ }
+
+ updateDivText(div, url);
+
+ if(o && o.control && o.control.blur) o.control.blur();
+ }
+ return false;
+}
+
+// Move or Copy support
+
+function initMorcButton(morcButton){
+ if(YAHOO.alpine.morcButton[morcButton] == null){
+ YAHOO.alpine.morcButton[morcButton] = new YAHOO.widget.Button({
+ type:'menu',
+ label:'Move',
+ id:morcButton+'Choice',
+ menu:[{ text:'Copy', value:'morc', onclick:{ fn:morcClick, obj:{morcButton:morcButton}} }],
+ container:morcButton
+ });
+ }
+}
+
+function morcWhich(morcButton){
+ return YAHOO.alpine.morcButton[morcButton].get('label');
+}
+
+function morcClick(p_sType,p_aArgs,p_oItem){
+ var morc, morcOpt;
+ var button = YAHOO.alpine.morcButton[p_oItem.morcButton];
+ if('copy' == YAHOO.util.Event.getTarget(p_aArgs[0]).innerHTML.toLowerCase()){
+ morc = 'Copy';
+ morcOpt = 'Move';
+ }
+ else{
+ morc = 'Move';
+ morcOpt = 'Copy';
+ }
+
+ button.set('label',morc);
+ button.getMenu().getItem(0).cfg.setProperty('text',morcOpt);
+}
+
+function saveCacheClick(e,o){
+ var event = YAHOO.util.Event;
+ event.getTarget(e).blur();
+ o.onDone(o);
+ event.preventDefault(e);
+}
+
+function updateSaveCache(p,c,f,dc,od,sca){
+ var event = YAHOO.util.Event;
+ var li = document.getElementById(p+'SaveCache');
+ if(li){
+ var cn = li.childNodes;
+ var i, j, node, s = '';
+ for(i = 0; i < cn.length && i < sca.length; i++){
+ node = cn[i].childNodes[0];
+ if(node.tagName.toLowerCase() == 'hr') break;
+ node.href = p + '/' + c + '/' + f + '?save=' + dc + '/' + sca[i].fv;
+ node.innerHTML = sca[i].fn.replace(/</g,'&lt;');
+ event.removeListener(node,'click');
+ event.addListener(node,'click',saveCacheClick,{c:dc,f:sca[i].fv,onDone:od});
+ }
+ for(j = i; j < sca.length; j++){
+ var nli = document.createElement('li');
+ var a = document.createElement('a');
+ a.href = p + '/' + c + '/' + f + '?save=' + dc + '/' + sca[j].fv;
+ a.innerHTML = sca[j].fn.replace(/</g,'&lt;');
+ event.addListener(a,'click',saveCacheClick,{c:dc,f:sca[j].fv,onDone:od});
+ nli.appendChild(a);
+ li.insertBefore(nli,cn[i++]);
+ }
+ }
+}
+
+
+// Move or Copy in mailbox list browse
+function morcInBrowseDone(o){
+ if(anySelected('Move or Copy')){
+ var mvorcp = morcWhich('listMorcButton').toLowerCase();
+ folderExists({c: o.c,
+ f: o.f,
+ onDone: function() { newMessageList({parms:{'op':mvorcp,'df':o.c+'/'+o.f}}) },
+ cancel: function(){
+ addStatusMessage(mvorcp + ' cancelled',4);
+ displayStatusMessages();
+ }
+ });
+ }
+
+ return false;
+}
+
+// Move or Copy in mailbox message view
+function morcInViewDone(o){
+ var mvorcp = morcWhich('viewMorcButton').toLowerCase();
+ folderExists({c: o.c,
+ f: o.f,
+ onDone: function() { newMessageText({parms:{'op':mvorcp,'df':o.c+'/'+o.f}}); },
+ cancel: function(){
+ addStatusMessage(mvorcp + ' cancelled',4);
+ displayStatusMessages();
+ }
+ });
+
+ return false;
+}
+
+function takeAddress(){
+ var dom = YAHOO.util.Dom;
+ var dbody = document.createDocumentFragment();
+ var div = document.createElement('div');
+ dbody.appendChild(div);
+ dom.addClass(div,'takeInstructions');
+ div.innerHTML = 'Choose extracted addresses and save to a new Contact'
+
+ div = document.createElement('div');
+ dbody.appendChild(div);
+ div.setAttribute('id','takeList');
+ dom.addClass(div,'takeList');
+ div.innerHTML = 'Loading...';
+
+ panelDialog('Extract Addresses',
+ dbody,
+ {
+ buttonId:'butChoose',
+ label:'Add to Contacts',
+ disabled: true,
+ doonreturn: true,
+ fn: function(){
+ var o = { which:'add', nickname:'', personal:'', email:'', note:'', fcc:'' };
+ var l = document.getElementsByName('taList');
+ if(l){
+ var i, el;
+ for(i = 0; i < l.length; i++){
+ if(l[i].checked){
+ if(o.email.length){
+ o.email += ',\n';
+ o.group = true;
+ }
+ el = document.getElementById('taPers'+l[i].value);
+ if(el){
+ var specials = el.innerHTML.match(/[()<>@,;:\\\".\[\]]/);
+ if(specials) o.email += '"' + el.innerHTML + '"';
+ else o.email += el.innerHTML;
+ }
+ el = document.getElementById('taEmail'+l[i].value);
+ if(el) o.email += ' <' + el.innerHTML + '>';
+ }
+ }
+
+ contactEditor(o);
+ }
+ }
+ });
+
+ setPanelBodyWidth('takeList');
+ drawExtractedAddresses(div);
+ return false;
+}
+
+function drawExtractedAddresses(elContainer){
+ var takeDS = new YAHOO.util.DataSource('conduit/take.tcl?op=all&u=');
+ takeDS.responseType = YAHOO.util.DataSource.TYPE_XML;
+ takeDS.responseSchema = {
+ resultNode: 'Result',
+ fields: ['Type', 'Nickname','Personal','Email','Fcc','Note','Error']
+ };
+ takeDS.sendRequest(YAHOO.alpine.current.u,
+ {
+ success: function(oReq,oResp,oPayload){
+ var dom = YAHOO.util.Dom;
+ if(oResp.results.length == 1 && oResp.Error){
+ showStatusMessage('Error Taking Address: ' + oResp.Error, 10);
+ }
+ else {
+ var id, el, elTD, elTR, elTable = document.createElement('table');
+ elTable.setAttribute('width','100%');
+ elTable.setAttribute('cellSpacing','0');
+ elTable.setAttribute('cellPadding','0');
+ for(var i = 0; i < oResp.results.length; i++){
+ var o = oResp.results[i];
+ n = (o.Email) ? o.Email.length : 0;
+ if(n){
+ elTR = elTable.insertRow(elTable.rows.length);
+ elTD = elTR.insertCell(elTR.cells.length);
+
+ el = createInputElement('checkbox','taList');
+ el.setAttribute('value',elTable.rows.length);
+ id = 'takeAddr' + elTable.rows.length;
+ el.setAttribute('id',id);
+
+ YAHOO.util.Event.addListener(el,'click',boxClicked);
+ elTD.appendChild(el);
+
+ elTD = elTR.insertCell(elTR.cells.length);
+ dom.addClass(elTD,'wap');
+ el = createNameValueElement('label','for',id);
+ elTD.appendChild(el);
+ el.setAttribute('id','taPers'+elTable.rows.length);
+ el.innerHTML = (o.Personal) ? o.Personal : '';
+
+ elTD = elTR.insertCell(elTR.cells.length);
+ dom.addClass(elTD,'wap');
+ el = createNameValueElement('label','for',id);
+ elTD.appendChild(el);
+ el.setAttribute('id','taEmail'+elTable.rows.length);
+ el.innerHTML = o.Email;
+ }
+
+ //if(oResp.results[i].Nickname) ;
+ //if(oResp.results[i].Note) ;
+ //if(oResp.results[i].Fcc) ;
+ }
+
+ elContainer.replaceChild(elTable, elContainer.firstChild);
+ }
+ },
+ failure: function(oReq,oResp,oPayload){
+ showStatusMessage('Error Taking Address: ' + oResp.responseText, 10);
+ },
+ scope: this,
+ argument:[elContainer]
+ });
+ return(false);
+}
+
+function boxClicked(e){
+ var o = YAHOO.util.Event.getTarget(e);
+
+ if(o) markOne(o);
+ var el = document.getElementsByName('taList');
+ if(el){
+ for(var i = 0; i < el.length; i++){
+ if(el[i].checked){
+ panelDialogEnableButton(true);
+ return;
+ }
+ }
+ }
+
+ panelDialogEnableButton(false);
+}
+
+function boxClear(){
+ var el = document.getElementsByName('taList');
+ if(el){
+ for(var i = 0; i < el.length; i++) el[i].checked = false;
+ }
+
+ panelDialogEnableButton(false);
+}
+
+// focus message list on search results
+function listSearchResults(){
+ newMessageList({parms:{op:'focus',page:'new'}});
+ return false;
+}
+
+function fixupUnreadCount(id,n) {
+ var idObj;
+ if(n) updateElementHtml('unread' + id, ' (' + n + ')');
+ else updateElementHtml('unread' + id, '');
+}
+
+
+// Pagewise Context updaters
+function showBrowseMenus(){
+ var dom = YAHOO.util.Dom;
+ dom.setStyle('viewTopMenubar','display','none');
+ dom.setStyle('listTopMenubar','display','block');
+ dom.setStyle('viewBottomMenubar','display','none');
+ dom.setStyle('listBottomMenubar','display','block');
+}
+
+function updateBrowseLinksAndSuch(o){
+ var dom = YAHOO.util.Dom;
+ var alp = YAHOO.alpine;
+ if(o.u) alp.current.u = o.u;
+ alp.current.count = o.count;
+ alp.current.selected = o.selected;
+ alp.current.searched = o.searched;
+ alp.current.focused = o.focused;
+ if(o.selected != numCheckedOnPage()) showSelectAllInfo(2);
+ if(!o.page || o.page < 1) o.page = 1;
+ if(!o.pages || o.pages < 1) o.pages = 1;
+ document.title = alp.current.f + ', ' + o.page + ' of ' + o.pages + ' (' + o.unread + ') - Web Alpine 2.0';
+ fixupUnreadCount('Current',o.unread);
+ if(o.trashed > 0) incUnreadCount('Trash',o.trashed);
+ else if(o.trashed < 0) decUnreadCount('Trash',(o.trashed * -1));
+ else if(o.cpmv){
+ if(o.cpmv.n > 0) incUnreadCount(o.cpmv.f,o.cpmv.n);
+ else if(o.cpmv.n < 0) decUnreadCount(o.cpmv.f,(o.cpmv.n * -1));
+ }
+ updateElementHtml('listContext', 'Page ' + o.page + ' of ' + o.pages + ' &nbsp;(' + o.count + ' Total Messages)&nbsp;');
+ var bUrl = 'browse/' + alp.current.c + '/' + alp.current.f;
+ updateElementHref('gFolder', bUrl);
+ updateElementHref('composeLink', 'compose?pop=' + bUrl);
+ if(o.sort){
+ var ea = document.getElementsByTagName('span');
+ for(var i = 0; i < ea.length; i++){
+ if(dom.hasClass(ea[i],'spfcl3')){
+ dom.removeClass(ea[i],'spfcl3');
+ break;
+ }
+ }
+
+ var el = document.getElementById('sort'+o.sort);
+ if(el) dom.addClass(el.firstChild,'spfcl3')
+ }
+ if(o.searched > 0){
+ dom.setStyle('searchResult','display','block');
+ dom.setStyle('searchRefine','display','block');
+ dom.setStyle('searchClear','display','block');
+ if(o.focused > 0){
+ updateElementHtml('pageTitle', 'Search Results in ' + quoteHtml(alp.current.f));
+ updateElementHtml('searchResultText', 'Search Results (' + o.focused + ')');
+ if(!dom.hasClass('searchResult','sel')) dom.addClass('searchResult','sel');
+ var el = document.getElementById('gFolder');
+ if(el && dom.hasClass(el.parentNode,'sel')) dom.removeClass(el.parentNode,'sel');
+ }
+ else{
+ updateElementHtml('pageTitle', quoteHtml(alp.current.f));
+ if(dom.hasClass('searchResult','sel')) dom.removeClass('searchResult','sel');
+ var el = document.getElementById('gFolder');
+ if(el && !dom.hasClass(el.parentNode,'sel')) dom.addClass(el.parentNode,'sel');
+ }
+ }
+ else{
+ updateElementHtml('pageTitle', quoteHtml(alp.current.f));
+ dom.setStyle('searchResult','display','none');
+ if(dom.hasClass('searchResult','sel')) dom.removeClass('searchResult','sel');
+ var el = document.getElementById('gFolder');
+ if(el && !dom.hasClass(el.parentNode,'sel')) dom.addClass(el.parentNode,'sel');
+ dom.setStyle('searchRefine','display','none');
+ dom.setStyle('searchClear','display','none');
+ document.getElementById('searchScope').selectedIndex = 2;
+ }
+ if(YAHOO.env.ua.gecko > 0){
+ sizeVPHeight();
+ window.onresize = resizeVPHeight;
+ }
+}
+
+function showViewMenus(){
+ var dom = YAHOO.util.Dom;
+ dom.setStyle('listTopMenubar','display','none');
+ dom.setStyle('viewTopMenubar','display','block');
+ dom.setStyle('listBottomMenubar','display','none');
+ dom.setStyle('viewBottomMenubar','display','block');
+}
+
+function updateViewLinksAndSuch(o){
+ var alp = YAHOO.alpine;
+ var ac = alp.current;
+ if(o.u == 0){
+ window.location = alp.app_root + '/browse/' + ac.c + '/' + ac.f;
+ return;
+ }
+
+ ac.u = o.u;
+ ac.count = o.count;
+ ac.selected = o.selected;
+ fixupUnreadCount('Current',o.unread);
+ var thisFldr = '/' + ac.c + '/' + ac.f + '/';
+ var thisFldrUrl = '/' + ac.c + '/' + encodeURIComponent(ac.f).replace(/%2F/g,'/');
+ var viewUrl = 'view' + thisFldr;
+ var browseUrl = 'browse' + thisFldrUrl;
+ var context = 'Message ' + o.n + ' of ' + o.count;
+ var fullcontext = ac.f + ', ' + context;
+ document.title = context + ' (' + o.unread + ') - Web Alpine 2.0';
+ updateElementHtml('viewContext', context);
+ updateElementHtml('pageTitle', '<a href="' + viewUrl + '" onClick="return newMessageList({parms:{op:\'noop\',page:\'new\'}});">' + quoteHtml(fullcontext) + '');
+ updateElementHref('gFolder', browseUrl);
+ updateElementHref('gCheck', viewUrl + ac.u);
+ updateElementHref('gExtract', 'extract/' + ac.c + '/' + ac.f + '/' + ac.u);
+ updateElementHref('gDelete', viewUrl + o.unext + '?delete=' + ac.u);
+ updateElementHref('gSpam', viewUrl + o.unext + '?spam=' + ac.u);
+ updateElementHref('gUnread', viewUrl + o.unext + '?unread=' + ac.u);
+ updateElementHref('gDecrypt', viewUrl + ac.u);
+ var compUrl = thisFldrUrl + '/' + ac.u + '?pop=view' + thisFldrUrl + '/' + ac.u;
+ updateElementHref('gReply', 'reply' + compUrl);
+ updateElementHref('gReplyAll', 'replyall' + compUrl);
+ updateElementHref('gForward', 'forward' + compUrl);
+ updateElementHref('composeLink', 'compose?pop=view' + compUrl);
+ var oForm = document.getElementById('searchForm');
+ oForm.action = oForm.action.replace(/\/[\d]+$/,'/' + ac.u);
+ if(YAHOO.env.ua.gecko > 0){
+ sizeVPHeight();
+ window.onresize = resizeVPHeight;
+ }
+}
diff --git a/web/cgi/alpine/2.0/lib/settings.js b/web/cgi/alpine/2.0/lib/settings.js
new file mode 100644
index 00000000..2df78d3a
--- /dev/null
+++ b/web/cgi/alpine/2.0/lib/settings.js
@@ -0,0 +1,183 @@
+/* $Id: settings.js 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+ * ========================================================================
+ * Copyright 2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+function settingsPage(el){
+ var dom = YAHOO.util.Dom;
+ for(var i = 1; i <= 20; i++){
+ dom.setStyle('settingsPage' + i,'display','none');
+ var elPage = document.getElementById('Page' + i);
+ if (elPage) dom.removeClass(elPage.parentNode,'sel');
+ }
+
+ dom.setStyle('settings' + el.id,'display','block');
+ dom.addClass(el.parentNode,'sel');
+ el.blur();
+ return false;
+}
+
+function toggleAdvance(el){
+ var dom = YAHOO.util.Dom;
+ var s = dom.getStyle('advancedSettings','display');
+ if(s && s == 'block'){
+ dom.setStyle('advancedSettings','display','none');
+ el.firstChild.src = 'img/cbn/f_plus.gif'
+ }
+ else{
+ dom.setStyle('advancedSettings','display','block');
+ el.firstChild.src = 'img/cbn/f_minus.gif'
+ }
+
+ el.blur();
+ return false;
+}
+
+function validFieldName(e){
+ var ev = YAHOO.util.Event;
+ var cc = ev.getCharCode(e);
+ if((cc >= 32 & cc < 127) && !(cc == 45 || (cc >= 65 && cc <= 90) || (cc >= 97 && cc <= 122))) ev.stopEvent(e);
+}
+
+function customHdrAdd(idTable){
+ var frag = document.createDocumentFragment();
+ var div = document.createElement('div');
+ frag.appendChild(div);
+ div.setAttribute('align','center');
+ div.innerHTML = '<p>Add a new custom header by entering the new header name on the left and an optional value on the left<p>';
+
+ div = document.createElement('div');
+ frag.appendChild(div);
+ div.setAttribute('align','center');
+
+ var elName = createInputElement('text','field');
+ div.appendChild(elName);
+ elName.setAttribute('size','12');
+ elName.setAttribute('maxlength','36');
+ YAHOO.util.Event.addListener(elName,'keypress',validFieldName);
+ YAHOO.util.Event.addListener(elName,'keyup',panelDialogInputChange);
+ el = document.createTextNode(' : ');
+ div.appendChild(el);
+ var elValue = createInputElement('text','value');
+ div.appendChild(elValue);
+ elValue.setAttribute('size','40');
+
+ panelDialog('New Custom Field',
+ frag,
+ {
+ label:' Add ',
+ disabled: true,
+ fn: function() {
+ var elTD, elTR, elTable = document.getElementById(idTable);
+ var el = document.getElementById('customHdrFields');
+ var n = (el.value - 0) + 1;
+ el.value = n;
+ if(elTable && elName.value.length){
+ elTR = elTable.insertRow(elTable.rows.length);
+
+ elTD = elTR.insertCell(elTR.cells.length);
+ el = document.createTextNode(elName.value +':');
+ elTD.appendChild(el);
+ el = createInputElement('hidden','customHdrField'+n);
+ elTD.appendChild(el);
+ el.setAttribute('value',elName.value);
+
+ elTD = elTR.insertCell(elTR.cells.length);
+ el = createInputElement('text','customHdrData' + n);
+ elTD.appendChild(el);
+ el.setAttribute('value',elValue.value);
+ el.setAttribute('size','45');
+
+ el = document.createElement('a');
+ elTD.appendChild(el);
+ el.setAttribute('href','#');
+ YAHOO.util.Event.addListener(el,'click',removeTableRowEvent);
+ var img = document.createElement('img');
+ img.setAttribute('src','img/cbn/remove.gif');
+ YAHOO.util.Dom.addClass(img,'wap');
+ el.appendChild(img);
+ }
+ }
+ });
+
+ elName.focus();
+ return false;
+}
+
+function removeTableRow(el){
+ if(el){
+ var iRow = el.parentNode.parentNode.rowIndex;
+ el.parentNode.parentNode.parentNode.parentNode.deleteRow(iRow);
+ }
+ return false;
+}
+
+function removeTableRowEvent(e){
+ var ev = YAHOO.util.Event;
+ removeTableRow(ev.getTarget(e).parentNode);
+ ev.stopEvent(e);
+}
+
+function listAdd(idTable,listName){
+ var elTD, elTR, elTable = document.getElementById(idTable);
+ var el = document.getElementById(listName+'s');
+ var n = el.value - 0;
+ el.value = ++n;
+ if(elTable.rows.length < 12){
+ if(elTable){
+ elTR = elTable.insertRow(elTable.rows.length);
+ elTD = elTR.insertCell(elTR.cells.length);
+
+ el = createInputElement('text',listName + n);
+ elTD.appendChild(el);
+ el.setAttribute('size','45');
+
+ el = document.createElement('a');
+ elTD.appendChild(el);
+ el.setAttribute('href','#');
+ YAHOO.util.Event.addListener(el,'click',removeTableRowEvent);
+ var img = document.createElement('img');
+ img.setAttribute('src','img/cbn/remove.gif');
+ YAHOO.util.Dom.addClass(img,'wap');
+ el.appendChild(img);
+ }
+ }
+
+ return false;
+}
+
+function saveSettings(){
+ showLoading();
+ document.getElementById('settingsForm').submit();
+ return false;
+}
+
+function restoreDefaultSettings(){
+ var el = document.getElementById('restore');
+ if(el){
+ el.value = 'restore';
+ showLoading();
+ document.getElementById('settingsForm').submit();
+ }
+ return false;
+}
+
+function settingsSuccess(){
+ var alp = YAHOO.alpine;
+ window.location.href = alp.cgi_base + 'browse/' + alp.current.c + '/' + alp.current.f;
+}
+
+function settingsFailure(m){
+ hideLoading();
+ addStatusMessage('Settings Failure: ' + m, 10);
+ displayStatusMessages();
+}
diff --git a/web/cgi/alpine/2.0/lib/yui b/web/cgi/alpine/2.0/lib/yui
new file mode 120000
index 00000000..7f792d58
--- /dev/null
+++ b/web/cgi/alpine/2.0/lib/yui
@@ -0,0 +1 @@
+yui-2.7.0 \ No newline at end of file
diff --git a/web/cgi/alpine/2.0/mailto b/web/cgi/alpine/2.0/mailto
new file mode 120000
index 00000000..872d4cb2
--- /dev/null
+++ b/web/cgi/alpine/2.0/mailto
@@ -0,0 +1 @@
+compose \ No newline at end of file
diff --git a/web/cgi/alpine/2.0/messagelist.tcl b/web/cgi/alpine/2.0/messagelist.tcl
new file mode 100644
index 00000000..9f1306ce
--- /dev/null
+++ b/web/cgi/alpine/2.0/messagelist.tcl
@@ -0,0 +1,622 @@
+# Web Alpine message list painting support
+# $Id$
+# ========================================================================
+# Copyright 2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# messagelist
+#
+# Purpose: TCL procedure to produce HTML-based message list of given
+# folder's contents
+#
+# Input:
+#
+
+proc sort_class {cursort sort} {
+ if {[string compare $sort [lindex $cursort 0]]} {
+ return blank
+ } else {
+ return spfcl3
+ }
+}
+
+proc navListButtons {c f} {
+ cgi_table_data class="wap pageBtns" {
+ cgi_puts [cgi_url [cgi_span "class=sp spmb spmbf" [cgi_span "<<"]] "browse/$c/$f" class=wap title="First Page" "onClick=return newMessageList({control:this,parms:{op:'first'}});"]
+ }
+ cgi_table_data class="pageBtns" {
+ cgi_puts [cgi_url [cgi_span "class=sp spmb spmbp" [cgi_span "<"]] "browse/$c/$f" class=wap title="Previous Page" "onClick=return newMessageList({control:this,parms:{op:'prev'}});"]
+ }
+ cgi_table_data class="dv1" {
+ cgi_puts [cgi_span "class=sp spmb spmb3" ""]
+ }
+ cgi_table_data class="pageBtns" {
+ cgi_puts [cgi_url [cgi_span "class=sp spmb spmbn" [cgi_span ">"]] "browse/$c/$f" class=wap title="Next Page" "onClick=return newMessageList({control:this,parms:{op:'next'}});"]
+ }
+ cgi_table_data class="pageBtns" {
+ cgi_puts [cgi_url [cgi_span "class=sp spmb spmbl" [cgi_span ">>"]] "browse/$c/$f" class=wap title="Last Page" "onClick=return newMessageList({control:this,parms:{op:'last'}});"]
+ }
+}
+
+proc drawTopListMenuBar {c f} {
+ global _wp
+
+ set mode [wpFolderMode $c $f]
+
+ cgi_table class="toolbarTbl" cellpadding="0" cellspacing="0" {
+ cgi_put "<tbody>"
+ cgi_table_row {
+ if {[info exists mode] && [lsearch [list trash junk] $mode] >= 0} {
+ cgi_table_data class=wap {
+ cgi_puts [cgi_url [cgi_span "class=sp spmbi spmb7" "Delete Forever"] "empty/$c/$f" "onClick=return doEmpty(this,'selected');" "title=Permanently remove messages from $f" class=wap]
+ }
+ cgi_table_data class="wap dv1" {
+ cgi_puts [cgi_span "class=sp spmb spmb3" ""]
+ }
+ cgi_table_data class=wap {
+ cgi_puts [cgi_url [cgi_span "class=sp spmbi spmb17" "Move to INBOX"] "inbox" "onClick=if(anySelected('Move to INBOX')) newMessageList({control:this,parms:{op:'move',df:'0/INBOX'}}); return false;" class=wap]
+ }
+ } else {
+ cgi_table_data class=wap {
+ cgi_puts [cgi_url [cgi_span "class=sp spmbi spmb7" "Delete"] "delete" "onClick= if(anySelected('Delete')) confirmDelete(); this.blur(); return false;" "title=Move message to Trash" class=wap]
+ }
+ if {(![info exists mode] || [lsearch [list junk draft sent] $mode] < 0) && [info exists _wp(spamfolder)] && [string compare [string tolower $f] $_wp(spamfolder)]} {
+ cgi_table_data class=wap {
+ cgi_puts [cgi_url [cgi_span "class=sp spmbi spmb8" "Report Spam"] "spam" "onClick=return doSpam(this);" class=wap]
+ }
+ }
+ cgi_table_data class="wap dv1" {
+ cgi_puts [cgi_span "class=sp spmb spmb3" ""]
+ }
+ cgi_table_data class=wap {
+ cgi_bullet_list class="menu" {
+ cgi_put "<li class=menuHdr>[cgi_url "More Actions [cgi_img "img/cbn/menu.gif" "class=menuDn menuImg"]" "" "onClick=return false;"]<div>"
+ cgi_bullet_list {
+ if {![info exists mode] || 0 == [string length $mode]} {
+ cgi_li [cgi_url "Mark as Read" "read" "onClick=if(anySelected('Mark as Read')) return applyFlag(this,'new','not'); else return false;"]
+ cgi_li [cgi_url "Mark as Unread" "unread" "onClick=if(anySelected('Mark as Unread')) return applyFlag(this,'new','ton'); else return false;"]
+ set hr "<hr />"
+ } else {
+ set hr ""
+ }
+
+ cgi_li "${hr}[cgi_url "Set Star" "star" "onClick=if(anySelected('Set Star')) return applyFlag(this,'imp','ton'); else return false;"]"
+ cgi_li [cgi_url "Clear Star" "unstar" "onClick=if(anySelected('Clear Star')) return applyFlag(this,'imp','not'); else return false;"]
+ cgi_li "<hr />[cgi_url "Select All" "browse/$c/$f?select=all" "onClick=return selectAllInFolder();"]"
+ cgi_li [cgi_url "Clear All" "browse/$c/$f?select=none" "onClick=return unSelectAllInFolder();"]
+ }
+ cgi_put "</div></li>"
+ }
+ }
+ }
+ if {![info exists mode] || 0 == [string length $mode]} {
+ cgi_table_data class="wap dv1" {
+ cgi_puts [cgi_span "class=sp spmb spmb3" ""]
+ }
+ cgi_table_data class=wap {
+ cgi_bullet_list class="wap menu" {
+ cgi_put "<li class=menuHdr>[cgi_url "Arrange [cgi_img "img/cbn/menu.gif" "class=menuDn menuImg"]" "" "onClick=return false;"]<div>"
+ cgi_bullet_list class=sortList {
+ if {[catch {PEMailbox sort} cursort]} {
+ set cursort [list Arrival 0]
+ set rev ""
+ } elseif {[lindex $cursort 1]} {
+ set rev Rev
+ } else {
+ set rev ""
+ }
+
+ cgi_li [cgi_url "[cgi_span "class=sp spfcl [sort_class $cursort From]" [cgi_span [cgi_nbspace]]]Sort by From" "#" id=sortFrom "onClick=return newMessageList({control:this,parms:{op:'sort${rev}From'}});"]
+ cgi_li [cgi_url "[cgi_span "class=sp spfcl [sort_class $cursort Subject]" [cgi_span [cgi_nbspace]]]Sort by Subject" "#" id=sortSubject "onClick=return newMessageList({control:this,parms:{op:'sort${rev}Subject'}});"]
+ cgi_li [cgi_url "[cgi_span "class=sp spfcl [sort_class $cursort Date]" [cgi_span [cgi_nbspace]]]Sort by Date" "#" id=sortDate "onClick=return newMessageList({control:this,parms:{op:'sort${rev}Date'}});"]
+ cgi_li [cgi_url "[cgi_span "class=sp spfcl [sort_class $cursort siZe]" [cgi_span [cgi_nbspace]]]Sort by Size" "#" id=sortsiZe "onClick=return newMessageList({control:this,parms:{op:'sort${rev}Size'}});"]
+ cgi_li [cgi_url "[cgi_span "class=sp spfcl [sort_class $cursort Arrival]" [cgi_span [cgi_nbspace]]]Sort by Arrival" "#" id=sortArrival "onClick=return newMessageList({control:this,parms:{op:'sort${rev}Arrival'}});"]
+ cgi_li [cgi_url "[cgi_span "class=sp spfcl [sort_class $cursort tHread]" [cgi_span [cgi_nbspace]]]Group by Thread" "#" id=sorttHread "onClick=return newMessageList({control:this,parms:{op:'sortThread'}});"]
+ cgi_li [cgi_url "[cgi_span "class=sp spfcl [sort_class $cursort OrderedSubj]" [cgi_span [cgi_nbspace]]]Group by Subject" "#" id=sortOrderedSubj "onClick=return newMessageList({control:this,parms:{op:'sortOrderedsubj'}});"]
+ }
+ cgi_put "</div></li>"
+ }
+ }
+ }
+ cgi_table_data class="wap dv1" {
+ cgi_puts [cgi_span "class=sp spmb spmb3" ""]
+ }
+
+ # move/cp FOLDER LIST
+ cgi_table_data id=listMorcButton class="wap yui-skin-sam yuimenu" {}
+ cgi_table_data class=wap {
+ cgi_bullet_list class="menu" {
+ cgi_put "<li class=menuHdr>[cgi_url "to Folder [cgi_img "img/cbn/menu.gif" "class=menuDn menuImg"]" "" "onClick=return false;"]<div>"
+ cgi_bullet_list id=browseSaveCache {
+ cgi_li "<hr />[cgi_url "More Folders..." "save/$c/$f" "onClick=if(anySelected('Move or Copy')){ pickFolder('folderList',morcWhich('listMorcButton'),'[PEFolder defaultcollection]',morcInBrowseDone) }; return false;"]"
+ }
+ cgi_put "</div></li>"
+ }
+ }
+
+ cgi_table_data width="100%" {
+ cgi_put [cgi_nbspace]
+ }
+
+ cgi_puts [navListButtons $c $f]
+
+ cgi_table_data class="tbPad" align="right" {
+ cgi_put [cgi_nbspace]
+ }
+ }
+ cgi_put "</tbody>"
+ }
+}
+
+proc drawBottomListMenuBar {c f pn pt mc} {
+ cgi_table class="toolbarTbl" cellpadding="0" cellspacing="0" {
+ cgi_put "<tbody>"
+ cgi_table_row {
+ cgi_table_data id=listContext class="wap pageText" {
+ cgi_put "Page $pn of $pt [cgi_nbspace]($mc Total Messages)[cgi_nbspace]"
+ }
+ cgi_table_data width="100%" {
+ cgi_put [cgi_nbspace]
+ }
+
+ cgi_puts [navListButtons $c $f]
+
+ cgi_table_data class="tbPad" align="right" {
+ cgi_put [cgi_nbspace]
+ }
+ }
+ cgi_put "</tbody>"
+ }
+}
+
+proc drawMessageList {c f n ppg} {
+ # remember message number at top of list
+ if {$n > 0 && [catch {PEMailbox current number $n} cm]} {
+ set n 1
+ }
+
+ if {[catch {PEMailbox nextvector $n $ppg [list indexparts indexcolor status statuslist]} nv]} {
+ set nv {}
+ set mv {}
+ } else {
+ set mv [lindex $nv 0]
+ }
+
+ cgi_division id="bannerSelection" {}
+
+ # message list header
+ # cells below MUST be reconciled with drawMessageList
+ cgi_table class="listTbl" cellpadding="0" cellspacing="0" {
+
+ set columns 0
+
+ # index header line
+ cgi_put "<thead>"
+ cgi_table_row {
+ incr columns
+ cgi_table_head class="wap colHdr" align="left" {
+ set nchk 0
+ foreach x $mv {
+ if {[lsearch -exact [lindex [lindex $x 2] 3] selected] >= 0} {
+ incr nchk
+ }
+ }
+
+ if {$ppg == $nchk} {
+ set allchecked checked
+ } else {
+ set allchecked ""
+ }
+
+ cgi_checkbox selectall= "onClick=markAllMessages();" $allchecked id=selectall title="Select/Unselect All Messages on this page"
+ cgi_put [cgi_nbspace]
+ }
+
+ if {0 == [catch {PEMailbox sort} sort]} {
+ set cursort [lindex $sort 0]
+ set revsort [lindex $sort 1]
+ } else {
+ set cursort nonsense
+ set revsort 0
+ }
+
+ set iformat [PEMailbox indexformat]
+ foreach fmt $iformat {
+ set iname [lindex $fmt 0]
+ set iwidth [lindex $fmt 1]
+ set idname [lindex $fmt 2]
+
+ if {0 == [string compare -nocase $iname priority]} {
+ continue
+ }
+
+ # cell defaults
+ set align "left"
+ if {[string compare -nocase $iname $cursort]} {
+ set class "wap colHdr"
+ if {$revsort} {
+ set rev "Rev"
+ } else {
+ set rev ""
+ }
+ set sorturl "browse/$c/[WPPercentQuote $f {/}]?sort=[string tolower $iname]&rev=$revsort"
+ set onclick "onClick=return newMessageList({control:this,parms:{'op':'sort${rev}[string tolower $iname]'}});"
+ set sortextra ""
+ } else {
+ set class "wap selColHdr"
+ if {0 == $revsort} {
+ set rev "Rev"
+ set revbin 1
+ } else {
+ set rev ""
+ set revbin 0
+ }
+ set sorturl "browse/$c/[WPPercentQuote $f {/}]?sort=[string tolower $cursort]&rev=$revbin"
+ set onclick "onClick=return newMessageList({control:this,parms:{'op':'sort${rev}[string tolower $cursort]'}});"
+ if {$revsort} {
+ set sortimg "img/cbn/up.gif"
+ #set sortclass "spml13"
+ set sortimgt "Descending"
+ } else {
+ set sortimg "img/cbn/dn.gif"
+ #set sortclass "spml12"
+ set sortimgt "Increasing"
+ }
+
+ set sortextra "[cgi_nbspace][cgi_nbspace][cgi_nbspace][cgi_img $sortimg class="wap selectedDn" title="$sortimgt"][cgi_nbspace][cgi_nbspace]"
+ }
+
+ if {[string length $iwidth]} {
+ set width width="$iwidth"
+ } else {
+ set width ""
+ }
+
+ switch -- [string tolower $iname] {
+ number {
+ set cd "#"
+ }
+ status {
+ set align center
+ #set cd [cgi_img "img/cbn/info.gif" title="Message Information" "alt=Message Information" class=wap]
+ set cd [cgi_span "class=sp spml spml7" "[cgi_nbspace]"]
+ }
+ attachments {
+ set align center
+ #set cd [cgi_img "img/cbn/attach_sm.gif" title="Attachments" "alt=Attachments" class=wap]
+ set cd [cgi_span "class=sp spml spml6" "[cgi_nbspace]"]
+ }
+ subject {
+ if {0 == [string compare $f Drafts]} {
+ append sortextra "[cgi_nbspace][cgi_nbspace](click to resume composition)"
+ }
+ set cd [cgi_url "${iname}[cgi_nbspace]${sortextra}" $sorturl title="Sort by ${iname}" class=wap $onclick]
+ }
+ default {
+ set cd [cgi_url "${iname}[cgi_nbspace]${sortextra}" $sorturl title="Sort by ${iname}" class=wap $onclick]
+ }
+ }
+
+ incr columns
+ cgi_table_head class="$class" align="$align" $width {
+ cgi_put $cd
+ }
+ }
+
+ # fixed priority header
+ incr columns
+ cgi_table_head class="colHdr rt" align="center" {
+ cgi_put [cgi_span "class=sp spml spml8" "[cgi_nbspace]"]
+ }
+ }
+
+ cgi_put "</thead><tbody>"
+ set listaction "view/$c/[WPPercentQuote $f {/}]/"
+ set viewonclick "onClick=return newMessageText({uid:\$ilu,parms:{page:'new'}});"
+ if {$c == [PEFolder defaultcollection] && 0 == [string compare $f Drafts]} {
+ set listaction "resume/"
+ set viewonclick ""
+ }
+
+ if {[set mvl [llength $mv]]} {
+ # write index lines
+ for {set i 0} {$i < $ppg} {incr i} {
+ if {$i >= $mvl} {
+ if {($i % 2) == 0} {
+ set class "class=ac"
+ } else {
+ set class ""
+ }
+
+ cgi_table_row $class {
+ cgi_table_data colspan=$columns {
+ cgi_put [cgi_nbspace]
+ }
+ }
+
+ continue
+ }
+
+ set v [lindex $mv $i]
+
+ set iln [lindex $v 0]
+ set ilu [lindex $v 1]
+ set msg [lindex [lindex $v 2] 0]
+ set linecolor [lindex [lindex $v 2] 1]
+ set stat [lindex [lindex $v 2] 2]
+ set statlist [lindex [lindex $v 2] 3]
+
+ # set background/bolding
+ if {[lsearch -exact $statlist new] < 0} {
+ set class ""
+ set unread 0
+ } else {
+ set class "unread"
+ set unread 1
+ }
+
+ # alternating gray lines
+ if {($i % 2) == 0} {
+ if {[string length $class]} {
+ append class " ac"
+ } else {
+ set class "ac"
+ }
+ }
+
+ if {[string length $class]} {
+ set rc class="$class"
+ } else {
+ set rc ""
+ }
+
+ if {[lsearch -exact $statlist selected] >= 0} {
+ set checked checked
+ set rid "id=sd"
+ } else {
+ set checked ""
+ set rid "id=line$i"
+ }
+
+ cgi_table_row $rc $rid {
+ cgi_table_data class=wap {
+ cgi_checkbox uidList=$ilu $checked "onClick=markMessage(this);"
+ }
+
+ for {set j 0} {$j < [llength $iformat]} {incr j} {
+
+ set iname [lindex [lindex $iformat $j] 0]
+
+ set class wap
+ set align left
+ set cd [cgi_nbspace]
+
+ catch {unset dragit}
+ catch {unset priority}
+
+ switch -- [string tolower $iname] {
+ from -
+ to -
+ cc -
+ sender -
+ recipients -
+ to {
+ set fstr [lindex [lindex [lindex $msg $j] 0] 0]
+ set flen 20
+ if {[string length $fstr] > $flen} {
+ set fstr "[string range $fstr 0 $flen]..."
+ }
+
+ set cd [cgi_quote_html $fstr]
+ set dragit $ilu
+ }
+ subject {
+ if {$i == 0} {
+ cgi_anchor_name messages
+ }
+
+ set st [lindex $msg $j]
+
+ if {[llength $st]} {
+ set sstr ""
+ foreach sts $st {
+ # do something with type info, [lindex $sts 2], like threadinfo, HERE
+ append sstr [lindex $sts 0]
+ }
+
+ set slen 50
+ if {[set sl [string length [string trim $sstr]]]} {
+
+ if {$sl > $slen} {
+ set subtext "[string range $sstr 0 $slen]..."
+ } else {
+ set subtext $sstr
+ }
+ } else {
+ set subtext {[Empty Subject]}
+ }
+ } else {
+ set subtext {[Empty Subject]}
+ }
+
+ if {$unread} {
+ set h1c "class=\"wap unread\""
+ } else {
+ set h1c "class=wap"
+ }
+
+ set cd [cgi_buffer {cgi_h1 $h1c id=h1$ilu [cgi_url [cgi_quote_html $subtext] ${listaction}$ilu id=ml$ilu class=wap [subst $viewonclick]]}]
+ }
+ status {
+ set align center
+ catch {unset statclass}
+ if {[lsearch -exact $statlist recent] >= 0 && [lsearch -exact $statlist new] >= 0} {
+ set staticon "new"
+ set statclass spml1
+ set stattitle "New Mail"
+ set statalt "New Mail"
+ } elseif {[lsearch -exact $statlist answered] >= 0} {
+ set staticon "replied"
+ set statclass spml2
+ set stattitle "Replied to Sender"
+ set statalt "Replied"
+ } elseif {[lsearch -exact $statlist forwarded] >= 0} {
+ set staticon "fwd"
+ set statclass spml3
+ set stattitle "Forwarded"
+ set statalt "Forwarded"
+ } elseif {[lsearch -exact $statlist to_us] >= 0} {
+ set staticon "tome"
+ set statclass spml4
+ set stattitle "Addressed to you"
+ set statalt "Addressed to you"
+ } elseif {[lsearch -exact $statlist cc_us] >= 0} {
+ set staticon "ccme"
+ set statclass spml5
+ set stattitle "Cc'd to you"
+ set statalt "Cc'd to you"
+ } else {
+ set staticon "blank"
+ set stattitle ""
+ set statalt ""
+ }
+
+ if {[info exists statclass]} {
+ set cd [cgi_span "class=sp spml $statclass" "[cgi_nbspace]"]
+ } else {
+ set cd [cgi_nbspace]
+ }
+ }
+ attachments {
+ set align center
+ if {[regexp {^[0-9]+$} [lindex [lindex [lindex $msg $j] 0] 0]]} {
+ set cd [cgi_span "class=sp spml spml6" "[cgi_nbspace]"]
+ }
+ }
+ date {
+ set cd "[lindex [lindex [lindex $msg $j] 0] 0]"
+ }
+ size {
+ set sstr [lindex [lindex [lindex $msg $j] 0] 0]
+ regsub {^\(([^\)]+)\)$} $sstr {\1} sstr
+ set cd "[cgi_quote_html $sstr]"
+ }
+ number {
+ set cd [WPcomma $iln]
+ }
+ priority { ; # priority combined with "impotant"
+ set priority [lindex [lindex [lindex $msg $j] 0] 0]
+ unset cd
+ }
+ default {
+ set cd "[lindex [lindex [lindex $msg $j] 0] 0]"
+ }
+ }
+
+ if {[info exists cd]} {
+ if {[info exists dragit]} {
+ set ddid dd$ilu
+ set ddidid id=$ddid
+ set mouseover "onMouseOver=\"cursor('move');\""
+ set mouseout "onMouseOut=\"cursor('default');\""
+ } else {
+ set ddidid ""
+ set mouseover ""
+ set mouseout ""
+ }
+
+ cgi_table_data align="$align" class="$class" {
+ cgi_division $ddidid $mouseover $mouseout {
+ cgi_put $cd
+ }
+ }
+
+ if {[info exists dragit]} {
+ lappend ddlist "'$ddid':'$ilu'"
+ }
+ }
+ }
+
+ # fixed right column for combined important/priority
+ cgi_table_data class="wap rt" {
+ # WARNING: ititle STORES STATE, KEEP IN SYNC WITH lib/browse.js
+ set iclass nostar
+ set ititle ""
+
+ if {[info exists priority]} {
+ switch -regexp $priority {
+ ^2$ -
+ ^high$ {
+ set iclass prihi
+ set ititle "High Priority"
+ }
+ ^1$ -
+ ^highest$ {
+ set iclass prihier
+ set ititle "Highest Priority"
+ }
+ }
+ }
+
+ if {[lsearch -exact $statlist flagged] >= 0} {
+ set iclass star
+ if {[string length $ititle]} {
+ set ititle "Starred and $ititle"
+ } else {
+ set ititle "Starred"
+ }
+ }
+
+ if {0 == [string length $ititle]} {
+ set ititle "Set Starred"
+ }
+
+ cgi_put [cgi_url [cgi_span "class=sp spml $iclass" ""] "star/$c/$f/$ilu" class=mlstat id=star$ilu "title=$ititle" "onClick=return flipStar(this);"]
+ }
+ }
+ }
+ } else {
+ cgi_table_row {
+ cgi_table_data class=wap colspan=$columns align=center {
+ if {[catch {PEMailbox state} mbstate]} {
+ if {[string length $f]} {
+ set fn "Folder \"[cgi_quote_html $f]\""
+ } else {
+ set fn INBOX
+ }
+
+ cgi_put [cgi_span "style=font-size: large; font-weight: bold ; background-color: yellow;" "[cgi_nbspace]$fn cannot be opened[cgi_nbspace]"]
+ } else {
+ switch -- $mbstate {
+ closed {
+ cgi_put [cgi_span "style=font-size: large; font-weight: bold ; background-color: yellow;" "[cgi_nbspace]Folder \"[cgi_quote_html $f]\" is not opened[cgi_nbspace]"]
+ }
+ default {
+ cgi_put [cgi_span "style=font-size: large; font-weight: bold ; margin: 50px" "Folder \"[cgi_quote_html $f]\" contains no messages"]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ cgi_put "</tbody>"
+ }
+ if {[info exists ddlist]} {
+ cgi_puts "<script>"
+ cgi_puts "function loadDDElements(){"
+ cgi_puts " var prop, ddList = { [join $ddlist ","] }, ddArray = \[\];"
+ cgi_puts " for(prop in ddList) ddArray\[ddArray.length\] = prop;"
+ cgi_puts " var tt = new YAHOO.widget.Tooltip('tipDrag', {context:ddArray, showdelay:500, text:'Drag over folder on left to move, Contacts to add'});"
+ cgi_puts " for(prop in ddList) canDragit(prop,ddList\[prop\],tt);"
+ cgi_puts "}"
+ cgi_puts "</script>"
+ }
+}
diff --git a/web/cgi/alpine/2.0/messageview.tcl b/web/cgi/alpine/2.0/messageview.tcl
new file mode 100644
index 00000000..13f660a2
--- /dev/null
+++ b/web/cgi/alpine/2.0/messageview.tcl
@@ -0,0 +1,731 @@
+# Web Alpine message text painting support
+# $Id$
+# ========================================================================
+# Copyright 2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# messageview
+#
+# Purpose: TCL procedure to produce HTML-based view of a message's
+# header/body text
+#
+# Input:
+#
+
+proc email_addr {pers mailbox} {
+ if {[string length $pers]} {
+ return "[cgi_quote_html $pers] [cgi_lt][cgi_quote_html $mailbox][cgi_gt]"
+ } else {
+ return "[cgi_quote_html $mailbox]"
+ }
+}
+
+proc put_quoted {s} {
+ cgi_put [cgi_quote_html $s]
+}
+
+proc put_quoted_nl {s} {
+ cgi_put "[cgi_quote_html $s][cgi_nl]"
+}
+
+proc navViewButtons {c f u n} {
+ cgi_table_data class="wap pageBtns" {
+ if {[catch {PEMailbox uid [PEMailbox next $n -1]} uprev]} {
+ set prevurl "#"
+ } elseif {$u == $uprev} {
+ set prevurl "javascript:alert('Already viewing first message')"
+ } else {
+ set prevurl "view/$c/$f/$uprev"
+ }
+
+ cgi_put [cgi_url [cgi_span "class=sp spmb spmbu" [cgi_span "Prev"]] $prevurl "title=Previous Message" "onClick=return newMessageText({control:this,parms:{op:'prev'}});"]
+ }
+
+ cgi_table_data class="wap dv1" {
+ cgi_puts [cgi_span "class=sp spmb spmb3" ""]
+ }
+
+ cgi_table_data class="wap pageBtns" {
+ if {[catch {PEMailbox uid [PEMailbox next $n 1]} unext]} {
+ set nexturl "#"
+ } elseif {$u == $unext} {
+ set nexturl "javascript:alert('Already viewing last message')"
+ } else {
+ set nexturl "view/$c/$f/$unext"
+ }
+
+ cgi_put [cgi_url [cgi_span "class=sp spmb spmbd" [cgi_span "Next"]] $nexturl title="Next Message" class=wap "onClick=return newMessageText({control:this,parms:{op:'next'}});"]
+ }
+}
+
+proc drawTopViewMenuBar {c f u n} {
+ cgi_table id=toolBar class="toolbarTbl" cellpadding="0" cellspacing="0" {
+ cgi_puts "<tbody>"
+ cgi_table_row {
+ if {[lsearch -exact [list Junk Trash] $f] >= 0} {
+ cgi_table_data class=wap {
+ cgi_puts [cgi_url "[cgi_img "img/cbn/delete.gif" class=wap] Delete Forever" "empty/$c/$f/$u" "onClick=return doEmpty(this,'message');" "title=Move message to Trash" class=wap]
+ }
+ cgi_table_data class="wap dv1" {
+ cgi_puts [cgi_span "class=sp spmb spmb3" ""]
+ }
+ cgi_table_data class=wap {
+ cgi_puts [cgi_url "[cgi_img img/cbn/inbox.gif class=wap] Move to INBOX" "move/$c/$f/$u?df=0/INBOX" "onClick=return newMessageText({control:this,parms:{op:'move',df:'0/INBOX'}});" class=wap]
+ }
+ } else {
+ cgi_table_data class=wap {
+ cgi_put [cgi_url [cgi_span "class=sp spmbi spmb4" "Reply"] "reply/$c/$f/$u?pop=view/$c/$f/$u" id=gReply class=wap]
+ }
+ cgi_table_data class=wap {
+ cgi_put [cgi_url [cgi_span "class=sp spmbi spmb5" "Reply All"] "replyall/$c/$f/$u?pop=view/$c/$f/$u" id=gReplyAll class=wap]
+ }
+ cgi_table_data class=wap {
+ cgi_put [cgi_url [cgi_span "class=sp spmbi spmb6" "Forward"] "forward/$c/$f/$u?pop=view/$c/$f/$u" id=gForward class=wap]
+ }
+ cgi_table_data class=wap {
+ if {[catch {PEMailbox uid [PEMailbox next $n 1]} unext]} {
+ set delurl "browse/$c/$f?u=$u"
+ } elseif {$u == $unext} {
+ set delurl "browse/$c/$f?u=${u}&delete=$u"
+ } else {
+ set delurl "view/$c/$f/$unext?delete=$u"
+ }
+ cgi_put [cgi_url [cgi_span "class=sp spmbi spmb7" "Delete"] $delurl title="Move message to Trash" class=wap id=gDelete "onClick=return newMessageText({control:this,parms:{op:'delete'}});"]
+ }
+ cgi_table_data class=wap {
+ cgi_put [cgi_url [cgi_span "class=sp spmbi spmb8" "Report Spam"] "view/$c/$f/$unext?spam=$u" title="Report as Spam and move message to Junk folder" class=wap id=gSpam "onClick=return newMessageText({control:this,parms:{op:'spam'}});"]
+ }
+ cgi_table_data class="wap dv1" {
+ cgi_puts [cgi_span "class=sp spmb spmb3" ""]
+ #cgi_put [cgi_img "img/cbn/div.gif" class=wap]
+ }
+ cgi_table_data class=wap {
+ cgi_bullet_list class="menu" {
+ cgi_put "<li class=menuHdr>[cgi_url "More Actions [cgi_img "img/cbn/menu.gif" class="wap menuDn menuImg"]" "#" class=wap "onClick=return false;"]<div>"
+ cgi_bullet_list {
+ cgi_li [cgi_url "Extract Addresses (Take)" "extract/$c/$f/$u" id="gExtract" "onClick=return takeAddress();"]
+ cgi_li "<hr />[cgi_url "Mark as Unread" "view/$c/$f/$unext?unread=$u" "onClick=return newMessageText({control:this,parms:{op:'unread'}});" id=gUnread]"
+ cgi_li [cgi_url "Set Star" "view/$c/$f/$u?star=1" "onClick=return setStar(-1,'ton');"]
+ cgi_li [cgi_url "Clear Star" "view/$c/$f/$u?star=0" "onClick=return setStar(-1,'not');"]
+ }
+ cgi_put "</div></li>"
+ }
+ }
+ }
+
+ cgi_table_data class="wap dv1" {
+ cgi_puts [cgi_span "class=sp spmb spmb3" ""]
+ }
+
+ # move/cp FOLDER LIST
+ cgi_table_data id=viewMorcButton class="wap yui-skin-sam yuimenu" {}
+ cgi_table_data class=wap {
+ cgi_bullet_list class="menu" {
+ cgi_put "<li class=menuHdr>[cgi_url "to Folder [cgi_img "img/cbn/menu.gif" class="menuDn menuImg wap"]" "#" "onClick=return false;"]<div>"
+ cgi_bullet_list id=viewSaveCache {
+ cgi_li "<hr />[cgi_url "More Folders..." "save/$c/$f" "onClick=pickFolder('folderList',morcWhich('viewMorcButton'),'[WPCmd PEFolder defaultcollection]',morcInViewDone); return false;"]"
+ }
+ cgi_put "</div></li>"
+ }
+ }
+
+ cgi_table_data class=wap width="100%" {
+ cgi_put [cgi_nbspace]
+ }
+
+ cgi_puts [navViewButtons $c $f $u $n]
+
+ cgi_table_data class="wap tbPad" align="right" {
+ cgi_put [cgi_nbspace]
+ }
+ }
+ cgi_puts "</tbody>"
+ }
+}
+
+proc drawBottomViewMenuBar {c f u n mc} {
+ cgi_table class="wap toolbarTbl" cellpadding="0" cellspacing="0" {
+ cgi_puts "<tbody>"
+ cgi_table_row {
+ cgi_table_data class="wap pageText" id=viewContext {
+ cgi_put "Message $n of $mc"
+ }
+ cgi_table_data class=wap width="100%" {
+ cgi_put [cgi_nbspace]
+ }
+
+ cgi_puts [navViewButtons $c $f $u $n]
+
+ cgi_table_data class="wap tbPad" align="right" {
+ cgi_put [cgi_nbspace]
+ }
+ }
+ cgi_puts "</tbody>"
+ }
+}
+
+proc drawMessageText {c f u {showimg ""}} {
+ # before getting the list, move anything deleted to Trash
+ if {[catch {PEMailbox trashdeleted current} result]} {
+ PEInfo statmsg "Trash move Failed: $result"
+ }
+
+ # remember "current" message
+ if {$u > 0 && [catch {PEMailbox current uid $u} cm]} {
+ PEInfo statmsg "Set current failed: $cm"
+ set u 0
+ }
+
+ set wasnew [expr {[lsearch -exact [PEMessage $u status] New] >= 0}]
+ set html 0
+
+ catch {unset shownsubtype}
+ catch {unset bodyleadin}
+ catch {unset fromaddr}
+
+ # collect message parts
+ if {[catch {PEMessage $u header} hdrtext]} {
+ set hdrtext "Message Text Fetch Failure: $hdrtext"
+ } else {
+ # fish out From: address
+ foreach {ht hf hd} [join $hdrtext] {
+ if {0 == [string compare addr $ht]} {
+ if {0 == [string compare -nocase from $hf]} {
+ if {[llength $hd] == 1} {
+ set df [lindex [lindex $hd 0] 0]
+ if {0 == [string length $df]} {
+ set df [lindex [lindex $hd 0] 1]
+ }
+
+ set fromaddr [list $df [cgi_quote_html [lindex [lindex $hd 0] 0]] [lindex [lindex $hd 0] 1]]
+ }
+
+ break
+ }
+ }
+ }
+ }
+
+ set attachments 0
+ if {[catch {PEMessage $u attachments} attachmentlist]} {
+ # STATUS "Cannot get attachments: $attachments"
+ } else {
+ foreach att $attachmentlist {
+ # only an attachment if "shown" in multi/alt AND it has a filename
+ if {[string compare [lindex $att 1] shown]} {
+ if {[string length [lindex $att 4]]} {
+ incr attachments
+ }
+ } else {
+ # only use subtype if a single part is intended to be displayed
+ if {[info exists shownsubtype]} {
+ set shownsubtype ""
+ } else {
+ set shownsubtype [string tolower [lindex $att 3]]
+ }
+ }
+ }
+ }
+
+ set bodyopts {}
+ set showimages ""
+
+ if {[info exists shownsubtype] && 0 == [string compare $shownsubtype html]} {
+ if {[WPCmd PEInfo feature render-html-internally] == 0} {
+ set html 1
+ lappend bodyopts html
+ set image_set allow_images
+ } else {
+ set image_set allow_cid_images
+ }
+
+ switch -- $showimg {
+ 1 { ;# show images this once
+ lappend bodyopts images
+ set showimages 1
+ }
+ from { ;# always show images from sender
+ lappend bodyopts images
+ set showimages friend
+ if {[info exists fromaddr] && [string length [lindex $fromaddr 2]]} {
+ if {[catch {WPSessionState $image_set} friends]} { ;# no image_set yet?
+ if {[catch {WPSessionState $image_set [list [lindex $fromaddr 2]]} result]} {
+ catch {PEInfo statmsg "Cannot remember [lindex $fromaddr 2]: $result"}
+ }
+ } elseif {[lsearch -exact $friends [lindex $fromaddr 2]] < 0} {
+ lappend friends [lindex $fromaddr 2]
+ if {[catch {WPSessionState $image_set $friends} result]} {
+ catch {PEInfo statmsg "Cannot remember [lindex $fromaddr 2]: $result"}
+ }
+ }
+ }
+ }
+ 0 { ;# never show images from sender
+ if {[info exists fromaddr] && [catch {WPSessionState $image_set} friends] == 0} {
+ while {[set findex [lsearch -exact $friends [lindex $fromaddr 2]]] >= 0} {
+ set friends [lreplace $friends $findex $findex]
+ if {[catch {WPSessionState $image_set $friends} friends]} {
+ catch {PEInfo statmsg "Cannot forget image sender: $friends"}
+ break;
+ }
+ }
+ }
+ }
+ "" { ;# display if a friend
+ if {[info exists fromaddr]
+ && 0 == [catch {WPSessionState $image_set} friends]
+ && [lsearch -exact $friends [lindex $fromaddr 2]] >= 0} {
+ lappend bodyopts images
+ set showimages friend
+ }
+ }
+ }
+ }
+
+ if {[PEInfo mode full-header-mode] > 0} {
+ set put_tdata put_quoted
+ }
+
+ if {[catch {PEMessage $u body $bodyopts} bodytext]} {
+ set bodytext "Message Text Fetch Failure: $bodytext"
+ }
+
+ # prescan body for interesting stuff
+ foreach i $bodytext {
+ foreach j $i {
+ switch -exact [lindex $j 0] {
+ img { ;# digested HTML mode image
+ if {![info exists bodyleadin]
+ && [catch {PEMessage $uid cid "<[lindex [lindex $j 1] 0]>"} cidpart] == 0
+ && [string length $cidpart]
+ && [catch {PEMessage $uid attachinfo $cidpart} attachinfo] == 0
+ && [string compare [string tolower [lindex $attachinfo 1]] image] == 0} {
+ if {[string length $showimages]} {
+ set bodyleadin "\[ Attached images ARE being displayed \]"
+ if {[info exists fromaddr] && [string length [lindex $fromaddr 2]]} {
+ if {[string compare $showimages friend]} {
+ append bodyleadin "[cgi_nl]\[ Never show images from [cgi_url "[lindex $fromaddr 0]" "view/$c/$f/$u?showimg=0"] \]"
+ } else {
+ append bodyleadin "[cgi_nl]\[ Always show images from [cgi_url "[lindex $fromaddr 0]" "view/$c/$f/$u?showimg=from"] \]"
+ }
+ }
+ } else {
+ set bodyleadin "\[ Attached images are NOT being displayed \]"
+ append bodyleadin "[cgi_nl]\[ Show images [cgi_url "below" "view/$c/$f/$u?showimg=1" "onClick=return newMessageText({control:this,parms:{op:'noop',img:'1'}});"]"
+ if {[info exists fromaddr] && [string length [lindex $fromaddr 2]]} {
+ append bodyleadin ", or always show images from [cgi_url "[lindex $fromaddr 0]" "from/$c/$f/$u?showimg=from"] \]"
+ } else {
+ append bodyleadin " \]"
+ }
+ }
+ }
+ }
+ t {
+ if {$html && ![info exists bodyleadin] && [regexp {<[Ii][Mm][Gg](| ([^>]+))>} [lindex $j 1] dummy img]} {
+ set bodyleadin "[cgi_img "img/cbn/infomsg.gif"] Web-based images "
+ if {![string length $showimages]} {
+ append bodyleadin "have been blocked to to help protect your privacy and reduce spam.[cgi_nl][cgi_url "Display images below" "view/$c/$f/$u?showimg=1" "onClick=return newMessageText({control:this,parms:{op:'noop',img:'1'}});"]"
+ if {[info exists fromaddr] && [string length [lindex $fromaddr 2]]} {
+ append bodyleadin "[cgi_nbspace][cgi_nbspace][cgi_nbspace]-[cgi_nbspace][cgi_nbspace][cgi_nbspace][cgi_url "Display images and always trust email from [lindex $fromaddr 0]" "view/$c/$f/$u?showimg=from" "onClick=return newMessageText({control:this,parms:{op:'noop',img:'from'}});"]"
+ }
+ } else {
+ append bodyleadin "are being displayed below.[cgi_nl]"
+ if {[info exists fromaddr]} {
+ if {[string compare friend $showimages]} {
+ append bodyleadin "[cgi_nbspace][cgi_nbspace][cgi_url "Always display images and trust email from [lindex $fromaddr 0]" "view/$c/$f/$u?showimg=from" "onClick=return newMessageText({control:this,parms:{op:'noop',img:'from'}});"]"
+ } else {
+ append bodyleadin "[cgi_url "No longer automatically display images from [lindex $fromaddr 0]" "view/$c/$f/$u?showimg=0" "onClick=return newMessageText({control:this,parms:{op:'noop',img:'0'}});"]"
+ }
+ }
+ }
+ }
+ }
+ default {}
+ }
+ }
+ }
+
+ set infont 0
+ set inurl 0
+ set fgcolor [set normal_fgcolor [PEInfo foreground]]
+ set bgcolor [PEInfo background]
+
+ # Toss any informational panels up here
+ if {[info exists bodyleadin]} {
+ cgi_division class="wap bannerPrivacy" {
+ cgi_puts $bodyleadin
+ }
+ }
+
+ # put message pieces together
+ # first the header & attachment list
+ cgi_table class="wap msgHead" cellpadding="0" cellspacing="0" {
+ cgi_puts "<tbody>"
+
+ # user headers are selectable, but we control the
+ # first two: subject, from and
+ # last two: date, attachments
+ if {1 == [llength $hdrtext] && 0 == [string compare -nocase raw [lindex [lindex $hdrtext 0] 0]]} {
+ # raw header
+ cgi_table_row {
+ cgi_table_data {
+ cgi_preformatted {
+ foreach hdrline [lindex [lindex $hdrtext 0] 2] {
+ foreach hdrlinepart $hdrline {
+ switch -- [lindex $hdrlinepart 0] {
+ t {
+ cgi_put [cgi_quote_html [lindex $hdrlinepart 1]]
+ }
+ default {
+ }
+ }
+ }
+
+ cgi_br
+ }
+ }
+
+ cgi_table_data "class=\"wap fullHdrBtn\"" {
+ if {[WPCmd PEMailbox focus]} {
+ set retf "Search Result"
+ } else {
+ set retf [cgi_quote_html $f]
+ }
+
+ cgi_put [cgi_url "Return[cgi_nbspace]to[cgi_nbspace]${retf}" "browse/$c/$f" class="wap" "onClick=return newMessageList({contol:this});"]
+ cgi_br
+ cgi_put [cgi_url "Show[cgi_nbspace]Filtered[cgi_nbspace]Headers" "view/$c/$f/$u" class="wap" "onClick=return newMessageText({contol:this,parms:{op:'hdroff'}});"]
+ }
+ }
+ }
+ } else {
+ set hdrmarkup ""
+ set datemarkup ""
+ foreach {ht hf hd} [join $hdrtext] {
+ switch -exact $ht {
+ text {
+ if {0 == [string compare -nocase subject $hf]} {
+ cgi_table_row {
+ cgi_table_data colspan=2 class="subText" {
+ cgi_put [cgi_quote_html $hd]
+ }
+ cgi_table_data "class=\"wap fullHdrBtn\"" {
+ if {[WPCmd PEMailbox focus]} {
+ set retf "Search Result"
+ } else {
+ set retf [cgi_quote_html $f]
+ }
+
+ cgi_put [cgi_url "Return[cgi_nbspace]to[cgi_nbspace]${retf}[cgi_span "class=sp spmv spmv3" [cgi_span [cgi_nbspace]]]" "browse/$c/$f" class="wap" "onClick=return newMessageList({contol:this,parms:{page:'new'}});"]
+ }
+ }
+ } elseif {0 == [string compare -nocase date $hf]} {
+ set datemarkup "<td class=\"wap hdrLabel\">$hf:</td><td class=\"wap hdrText\">[cgi_quote_html $hd]</td>"
+ } else {
+ append hdrmarkup "<td class=\"wap hdrLabel\">$hf:</td><td colspan=2 class=\"wap hdrText\">[cgi_quote_html $hd]</td>"
+ }
+ }
+ addr {
+ if {0 == [string compare -nocase from $hf]} { ;# From: always first
+ set addrs ""
+ if {0 == [catch {PEAddress books} booklist]} {
+ set tAFargs "\{books:\["
+ set comma ""
+ foreach b $booklist {
+ regsub -all {'} [lindex $b 1] {\'} bname
+ append tAFargs "${comma}\{book:[lindex $b 0],name:'$bname'\}"
+ set comma ","
+ }
+
+ append tAFargs "\]\}"
+ } else {
+ set tAFargs {{}}
+ }
+
+ foreach ma $hd {
+ lappend addrs [cgi_span class=emailAddr "[cgi_span "class=contactAddr" [email_addr [lindex $ma 0] [lindex $ma 1]]] [cgi_url "[cgi_span "class=sp spmv spmv1" ""]Add" "" "class=wap addContact" "onClick=takeAddressFrom('$u',$tAFargs); return false;" "title=Add Sender to Contacts"]"]
+ }
+
+ set hdrmarkup [linsert $hdrmarkup 0 "<td class=\"wap hdrLabel\">$hf:</td><td colspan=2 class=\"wap hdrText\">[join $addrs ",[cgi_nl]"]</td>"]
+ } else {
+ set addrs ""
+ foreach ma $hd {
+ lappend addrs [cgi_span class=emailAddr [email_addr [lindex $ma 0] [lindex $ma 1]]]
+ }
+
+ lappend hdrmarkup "<td class=\"wap hdrLabel\">$hf:</td><td colspan=2 class=\"wap hdrText\">[join $addrs ", "]</td>"
+ }
+ }
+ news -
+ rawaddr {
+ lappend hdrmarkup "<td class=\"wap hdrLabel\">$hf:</td><td colspan=2 class=\"wap hdrText\">[cgi_quote_html [join $hd ", "]]</td>"
+ }
+ default {
+ }
+ }
+ }
+
+ foreach hm $hdrmarkup {
+ cgi_table_row {
+ cgi_puts $hm
+ }
+ }
+
+ cgi_table_row {
+ cgi_puts $datemarkup
+ cgi_table_data "class=\"wap fullHdrBtn\"" {
+ cgi_put [cgi_url "Show[cgi_nbspace]Full[cgi_nbspace]Headers" "view/$c/$f/$u?headers=full" class="wap" "onClick=return newMessageText({control:this,parms:{op:'hdron'}});"]
+ }
+ }
+ }
+
+ if {$attachments} {
+ cgi_table_row {
+ cgi_table_data class="wap hdrLabel" {
+ cgi_put "Attachments:"
+ }
+ cgi_table_data class="wap hdrText" {
+ cgi_division "class=attach" {
+ set tail ""
+ foreach att $attachmentlist {
+ # !shown and has supplied file name
+ if {[string compare [lindex $att 1] shown] && [string length [lindex $att 4]]} {
+ if {[string length [lindex $att 4]]} {
+ set attitle [lindex $att 4]
+ } else {
+ set attitle "Attachment [lindex $att 0]"
+ }
+
+ cgi_put [cgi_span "class=attach" "[cgi_span "class=sp spmv spmv2" ""][cgi_url "$attitle" "detach/$c/$f/$u/[lindex $att 0]?download=1" title="Download $attitle"]"]
+ }
+
+ set tail ":[cgi_nbspace]"
+ }
+ }
+ }
+ }
+ }
+ cgi_table_row {
+ cgi_table_data colspan="3" class="hdrLabel" {
+ cgi_put [cgi_span "class=trans" "style=height:5px;width:2px;" [cgi_span " "]]
+ }
+ }
+
+ cgi_puts "</tbody>"
+ }
+
+ # start writing body
+ if {$html} {
+ set class ""
+ set class "class=\"contentBodyHTML\""
+ if {![info exists put_tdata]} {
+ set put_tdata cgi_put
+ }
+ } else {
+ set class "class=\"contentBody messageText\""
+ if {![info exists put_tdata]} {
+ set put_tdata put_quoted
+ }
+ }
+
+ cgi_division $class {
+ foreach i $bodytext {
+ foreach j $i {
+ set ttype [lindex $j 0]
+ set tdata [lindex $j 1]
+
+ # write anchors by hand
+ switch -- $ttype {
+ urlstart {
+ set href [lindex $tdata 0]
+ set name [lindex $tdata 1]
+
+ # build links by hand since we don't know where
+ # they'll terminate
+ set linktext "<a "
+ switch -- [lindex [split $href :] 0] {
+ mailto {
+ regsub {[?]} [lindex [split $href :] 1] {\&} maito
+ append linktext "href=mailto?to=[WPPercentQuote $maito {&=}]&pop=view/$c/[WPPercentQuote $f {/}]/$u"
+ }
+ default {
+ # no relative links, no javascript
+ if {[regexp -- "^(\[a-zA-Z\]+):" $href dummy scheme]
+ && [string compare -nocase $scheme javascript]} {
+ append linktext "href=\"$href\" target=\"_blank\" "
+ }
+
+ if {[string length $name]} {
+ append linktext "name=\"$name\""
+ }
+ }
+ }
+
+ cgi_put "${linktext}>"
+ set inurl 1
+ }
+ urlend {
+ if {$inurl} {
+ cgi_put "</a>"
+ }
+ }
+ attach {
+ set attachuid [lindex $tdata 0]
+ set part [lindex $tdata 1]
+ set mimetype [lindex $tdata 2]
+ set mimesubtype [lindex $tdata 3]
+ if {[string length [lindex $tdata 4]]} {
+ set file [lindex $tdata 4]
+ } else {
+ if {[string length [lindex $tdata 5]]} {
+ set file "attachment.[lindex $tdata 5]"
+ } else {
+ set file "unknown.txt"
+ }
+ }
+
+ set attachurl "detach.tcl?uid=${attachuid}&part=${part}"
+ set saveurl "${attachurl}&download=1"
+ if {0 == [string compare -nocase $mimetype "text"]
+ && 0 == [string compare -nocase $mimesubtype "html"]} {
+ set attachurl [append $attachurl "&download=1"]
+ }
+
+ set attachexp "View ${mimetype}/${mimesubtype} Attachment"
+
+ if {0 == [string compare message [string tolower ${mimetype}]]
+ && 0 == [string compare rfc822 [string tolower ${mimesubtype}]]} {
+ set attmsgurl "/$c/$f/$u/$part"
+ cgi_put [cgi_url [cgi_font size=-1 Fwd] "forward${attmsgurl}" target=_top]
+ cgi_put "|[cgi_url [cgi_font size=-1 Reply] "reply${attmsgurl}?reptext=1&repall=1" target=_top]"
+
+ } else {
+ cgi_put [cgi_url [cgi_font size=-1 View] $attachurl target="_blank"]
+ cgi_put "|[cgi_url [cgi_font size=-1 Save] $saveurl]"
+ }
+
+ if {[info exists attachurl]} {
+ set attachtext [WPurl $attachurl {} $hacktoken $attachexp target="_blank"]
+ }
+ }
+ img {
+ if {[info exists showimages] && [string length $showimages]
+ && [catch {PEMessage $uid cid "<[lindex [lindex $j 1] 0]>"} cidpart] == 0
+ && [string length $cidpart]
+ && [catch {PEMessage $uid attachinfo $cidpart} attachinfo] == 0
+ && [string compare [string tolower [lindex $attachinfo 1]] image] == 0} {
+ cgi_put [cgi_img detach/$c/$f/$u/${cidpart} "alt=\[[lindex $tdata 1]\]"]
+ } else {
+ cgi_put "\[[lindex $tdata 1]\]"
+ }
+ }
+ fgcolor {
+ if {$infont} {
+ cgi_put "</font>"
+ set infont 0
+ }
+
+ if {[string compare $tdata $fgcolor]} {
+ set fgcolor $tdata
+ if {[string compare $fgcolor $normal_fgcolor]} {
+ cgi_put "<font color=#${tdata}>"
+ set infont 1
+ }
+ }
+ }
+ bgcolor {
+ if {$infont} {
+ cgi_put "</font>"
+ set infont 0
+ }
+
+ if {[string compare $tdata $bgcolor]} {
+ cgi_put "<font style=\"background: #${tdata}\">"
+ set bgcolor $tdata
+ set infont 1
+ }
+ }
+ color {
+ if {$infont} {
+ cgi_put "</font>"
+ set infont 0
+ }
+
+ if {[string compare [lindex $tdata 0] $fgcolor] || [string compare [lindex $tdata 1] $bgcolor]} {
+ cgi_put "<font color=#[lindex $tdata 0] style=\"background: #[lindex $tdata 1]\">"
+ set bgcolor $tdata
+ set infont 1
+ }
+ }
+ italic {
+ switch $tdata {
+ on { cgi_put "<i>" }
+ off { cgi_put "</i>" }
+ }
+ }
+ bold {
+ switch $tdata {
+ on { cgi_put "<b>" }
+ off { cgi_put "</b>" }
+ }
+ }
+ underline {
+ switch $tdata {
+ on { cgi_put "<u>" }
+ off { cgi_put "</u>" }
+ }
+ }
+ strikethru {
+ switch $tdata {
+ on { cgi_put "<s>" }
+ off { cgi_put "</s>" }
+ }
+ }
+ bigfont {
+ switch $tdata {
+ on { cgi_put "<font size=\"+1\">" }
+ off { cgi_put "</font>" }
+ }
+ }
+ smallfont {
+ switch $tdata {
+ on { cgi_put "<font size=\"-1\">" }
+ off { cgi_put "</font>" }
+ }
+ }
+ default {
+ if {[info exists attachtext] && [set ht [string first $hacktoken $attachtext]] >= 0} {
+ set firstbit [string range $attachtext 0 [expr {$ht - 1}]]
+ set lastbit [string range $attachtext [expr {$ht + [string length $hacktoken]}] end]
+ cgi_put " $firstbit[cgi_quote_html [string trimleft $tdata]]$lastbit"
+ unset attachtext
+ } elseif {$html} {
+ $put_tdata "$tdata "
+ } else {
+ $put_tdata $tdata
+ }
+ }
+ }
+ }
+
+ if {!$html} {
+ cgi_br
+ }
+ }
+ }
+
+ cgi_division id="skip" {
+ cgi_put [cgi_url "Skip to Toolbar" "#toolbar"]
+ }
+
+ if {$wasnew} {
+ cgi_put "<script>decUnreadCount(1);</script>"
+ }
+}
diff --git a/web/cgi/alpine/2.0/newlist.tcl b/web/cgi/alpine/2.0/newlist.tcl
new file mode 100755
index 00000000..725a418b
--- /dev/null
+++ b/web/cgi/alpine/2.0/newlist.tcl
@@ -0,0 +1,477 @@
+#!./tclsh
+# $Id: newlist.tcl 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# newlist.tcl
+#
+# Purpose: CGI script that generates a page displaying a message
+# list of the indicated folder.
+#
+# Input: PATH_INFO: [/<col_number>]/<folder_name>[/<uid_of_first_msg>
+# along with possible search parameters:
+set newlist_args {
+ {op {} ""}
+ {df {} ""}
+ {uid {} ""}
+ {type {} ""}
+ {zoom {} ""}
+ {page {} ""}
+ {criteria {} ""}
+ {scope {} "new"}
+}
+
+# inherit global config
+source ./alpine.tcl
+source ./common.tcl
+source ./foldercache.tcl
+
+# default newlist state
+set c 0
+set f "INBOX"
+
+# TEST
+proc cgi_suffix {args} {
+ return ""
+}
+
+set dmsgs ""
+proc dm {s} {
+ global dmsgs
+
+ lappend dmsgs $s
+}
+
+proc focusOnResult {_focused} {
+ upvar 1 $_focused focused
+
+ if {[catch {WPCmd PEMailbox focus 1} focused]} {
+ WPCmd PEInfo statmsg "Cannot focus: $focused"
+ set focused 0
+ } else {
+ WPCmd PEInfo statmsg "Displaying $focused search results"
+ }
+}
+
+# grok PATH_INFO for collection, 'c', and folder 'f'
+if {[info exists env(PATH_INFO)] && [string length $env(PATH_INFO)]} {
+ if {[regexp {^/([0-9]+)/(.*)$} $env(PATH_INFO) dummy c f]} {
+ # Import data validate it and get session id
+ if {[catch {WPGetInputAndID sessid} result]} {
+ set harderr "Session Invalid: $result"
+ set deadsession 1
+ } else {
+ # grok parameters
+ foreach item $newlist_args {
+ if {[catch {eval WPImport $item} result]} {
+ set harderr "Cannot Read Input: $result"
+ break;
+ }
+ }
+
+ if {[catch {WPCmd PEMailbox messagecount} mc]} {
+ set harderr $mc
+ if {[regexp {[Ii]nactive [Ss]ession$} $mc]} {
+ set deadsession 1
+ }
+ }
+
+ }
+ } else {
+ set harderr "Invalid Folder: $env(PATH_INFO)"
+ }
+} else {
+ set harderr "No folder specified"
+}
+
+cgi_puts "Content-type: text/html; charset=\"UTF-8\"\n"
+
+if {[info exists harderr]} {
+ if {[info exists deadsession]} {
+ cgi_division class=contentDeadSession {
+ cgi_puts "This Web Alpine session is no longer active.<p>Click [cgi_url "here to restart your session." "$_wp(serverpath)/"] </div>"
+ }
+
+ exit
+ } else {
+ catch { WPCmd PEInfo statmsg "$harderr" }
+ set c 0
+ set f INBOX
+ }
+}
+
+set defc [WPCmd PEFolder defaultcollection]
+set focused [WPCmd PEMailbox focus]
+
+# set uid to "current" message?
+if {0 == [catch {WPCmd PEMailbox current} cm]} {
+ set n [lindex $cm 0]
+ set u [lindex $cm 1]
+} else {
+ if {$mc > 0} {
+ WPCmd PEInfo statmsg "BOTCH: No current message"
+ }
+
+ # non given, set to first message in folder
+ set n 1
+ if {[catch {
+ set n [WPCmd PEMailbox first]
+ set u [WPCmd PEMailbox uid $n]
+ } result]} {
+ if {$mc > 0} {
+ WPCmd PEInfo statmsg "No first message: $result"
+ }
+
+ set n 0
+ set u 0
+ }
+}
+
+# lines per page
+if {[catch {WPCmd PEInfo indexlines} ppg] || $ppg <= 0} {
+ set ppg $_wp(indexlines)
+} elseif {$ppg > $_wp(indexlinesmax)} {
+ set ppg $_wp(indexlinesmax)
+}
+
+# deal with page change
+if {$mc > 0} {
+ switch -regexp -- $op {
+ ^next$ {
+ set n [WPCmd PEMailbox next $n $ppg]
+
+ if {[catch {WPCmd PEMailbox uid $n} u]} {
+ set n [WPCmd PEMailbox first]
+ set u [WPCmd PEMailbox uid $n]
+ }
+ }
+ ^prev$ {
+ if {$ppg >= $n} {
+ set n [WPCmd PEMailbox first]
+ } else {
+ set n [WPCmd PEMailbox next $n -$ppg]
+ }
+
+ if {[catch {WPCmd PEMailbox uid $n} u]} {
+ set n [WPCmd PEMailbox first]
+ set u 0
+ }
+ }
+ ^first$ {
+ set n [WPCmd PEMailbox first]
+ if {[catch {WPCmd PEMailbox uid $n} u]} {
+ set n 1
+ set u 0
+ }
+ }
+ ^last$ {
+ set n [WPCmd PEMailbox last]
+ if {[catch {WPCmd PEMailbox uid $n} u]} {
+ set n 1
+ set u 0
+ }
+ }
+ ^delete$ {
+ if {[catch {WPCmd PEMailbox apply count new} trashed_new]} {
+ set trashed_new 0
+ }
+
+ if {[catch {WPCmd PEMailbox apply flag ton del} result]} {
+ WPCmd PEInfo statmsg "Delete failed: $result"
+ } else {
+ WPCmd PEInfo statmsg "$result message[WPplural $result] moved to Trash"
+ if {$trashed_new > 0} {
+ set trashed $trashed_new
+ }
+ }
+ }
+ ^trash$ {
+ if {[catch {WPCmd PEFolder empty $c [wpLiteralFolder $c $f] selected} result]} {
+ WPCmd PEInfo statmsg "Cannot Remove: $result"
+ } else {
+ WPCmd PEInfo statmsg "$result message[WPplural $result] deleted forever"
+ if {0 == [WPCmd PEMailbox messagecount]} {
+ set n 0
+ set u 0
+ }
+ set trashed [expr {$result * -1}]
+ }
+ }
+ ^trashall$ {
+ if {[catch {WPCmd PEFolder empty $c [wpLiteralFolder $c $f] all} result]} {
+ WPCmd PEInfo statmsg "Cannot Remove: $result"
+ } else {
+ WPCmd PEInfo statmsg "$result message[WPplural $result] deleted forever"
+ set n 0
+ set u 0
+ set trashed [expr {$result * -1}]
+ }
+ }
+ ^spam$ {
+ set numspam [WPCmd PEMailbox selected]
+ if {$numspam > 0} {
+ # aggregate save
+ if {[info exists _wp(spamsubj)] && [string length $_wp(spamsubj)]} {
+ set spamsubj $_wp(spamsubj)
+ } else {
+ set spamsubj "Spam Report"
+ }
+
+ # aggregate delete
+ if {[info exists _wp(spamfolder)] && [string length $_wp(spamfolder)]
+ && [catch {
+ if {[WPCmd PEFolder exists $defc $_wp(spamfolder)] == 0} {
+ WPCmd PEFolder create $defc $_wp(spamfolder)
+ }
+
+ WPCmd PEMailbox apply save $defc $_wp(spamfolder)
+ } result]} {
+ WPCmd PEInfo statmsg "Error Reporting Spam: $result"
+ } elseif {[info exists _wp(spamaddr)] && [string length $_wp(spamaddr)]
+ && [catch {WPCmd PEMailbox apply spam $_wp(spamaddr) $spamsubj} reason]} {
+ WPCmd PEInfo statmsg "Error Sending Spam Notice: $reason"
+ } elseif {[catch {WPCmd PEMailbox apply delete} reason]} {
+ WPCmd PEInfo statmsg "Error marking Spam Deleted: $reason"
+ } else {
+ WPCmd PEInfo statmsg "$numspam spam message[WPplural $numspam] reported"
+ }
+ }
+ }
+ ^copy$ {
+ if {[string length $df] && [regexp {^([0-9]+)/(.*)$} $df dummy dfc dfn] && [string length $dfn]} {
+ if {[catch {WPCmd PEMailbox apply count new} cpmv_new]} {
+ set cpmv_new 0
+ }
+
+ if {[catch {WPCmd PEMailbox apply copy $dfc $dfn} result]} {
+ WPCmd PEInfo statmsg "Cannot copy messages: $result"
+ } else {
+ if {$dfc == $defc
+ && !(([info exists _wp(spamfolder)] && 0 == [string compare $f $_wp(spamfolder)])
+ || 0 == [string compare $f Trash])} {
+ addSaveCache $dfn
+ set savecachechange $dfn
+ set cpmvdest [cgi_quote_html $dfn]
+ if {$result > 0 && $cpmv_new > 0} {
+ set cpmv $cpmv_new
+ }
+ }
+
+ WPCmd PEInfo statmsg "Copied $result message[WPplural $result] to $dfn"
+ }
+ } else {
+ WPCmd PEInfo statmsg "Cannot copy to $df"
+ }
+ }
+ ^move$ {
+ if {[string length $df] && [regexp {^([0-9]+)/(.*)$} $df dummy dfc dfn] && [string length $dfn]} {
+ if {[catch {WPCmd PEMailbox apply count new} cpmv_new]} {
+ set cpmv_new 0
+ }
+
+ if {[catch {WPCmd PEMailbox apply move $dfc $dfn} result]} {
+ WPCmd PEInfo statmsg "Move Failure: $result"
+ } else {
+ # if needed, empty what was moved
+ if {$c == [WPCmd PEFolder defaultcollection]
+ && (([info exists _wp(spamfolder)] && 0 == [string compare $f $_wp(spamfolder)])
+ || 0 == [string compare $f Trash])
+ && [catch {WPCmd PEFolder empty $c $f selected} result]} {
+ WPCmd PEInfo statmsg "Move Failure: $result"
+ } else {
+ if {$dfc == $defc
+ && !(([info exists _wp(spamfolder)] && 0 == [string compare $f $_wp(spamfolder)])
+ || 0 == [string compare $f Trash])} {
+ addSaveCache $dfn
+ set savecachechange $dfn
+ set cpmvdest [cgi_quote_html $dfn]
+ if {$result > 0 && $cpmv_new > 0} {
+ set cpmv $cpmv_new
+ }
+ }
+
+ # clean up moved messages so they don't get tossed in Trash as well
+ if {[catch {WPCmd PEMailbox expunge} blasted] || [string length $blasted]} {
+ WPCmd PEInfo statmsg "Move Failure: $blasted"
+ } else {
+ WPCmd PEInfo statmsg "Moved $result message[WPplural $result] to $dfn"
+ }
+ }
+ }
+ } else {
+ WPCmd PEInfo statmsg "Cannot move: to $df"
+ }
+ }
+ ^movemsg$ {
+ if {[regexp {^[0-9]+$} $uid] && $uid > 0 && [string length $df] && [regexp {^([0-9]+)/(.*)$} $df dummy dfc dfn] && [string length $dfn]} {
+ if {[catch {
+ # destination Trash? just delete and let regular delete process move it
+ if {$dfc == $defc && 0 == [string compare Trash $dfn]} {
+ WPCmd PEMessage $uid flag deleted 1
+ } else {
+ WPCmd PEMessage $uid move $dfc $dfn
+ }
+
+ # source is trash/junk, remove explicitly
+ if {$c == $defc
+ && (([info exists _wp(spamfolder)] && 0 == [string compare $f $_wp(spamfolder)])
+ || 0 == [string compare $f Trash])} {
+ WPCmd PEFolder empty $c $f $uid
+ }
+
+ WPCmd PEInfo statmsg "Moved message to $dfn"
+ set cpmvdest [cgi_quote_html $dfn]
+ if {0 != [WPCmd PEMessage $uid flag new]} {
+ set cpmv 1
+ }
+ } result]} {
+ WPCmd PEInfo statmsg "Move Failure: $result"
+ }
+ } else {
+ WPCmd PEInfo statmsg "Cannot move: to $df"
+ }
+ }
+ ^sort[A-Za-z]+$ {
+ if {[regexp {^sort([[Rr]ev|)(.*)$} $op dummy rev sname]} {
+ set sort [string tolower $sname]
+ set rval [expr {[string length $rev] > 0}]
+ if {[catch {WPCmd PEMailbox sort $sort $rval} cursort]} {
+ WPCmd PEInfo statmsg "Cannot set sort: $cursor"
+ set cursort [list nonsense 0]
+ } else {
+ # store result
+ WPCmd set sort [list $sort $rval]
+ }
+ } else {
+ WPCmd PEInfo statmsg "Unrecognized Sort: $op"
+ }
+ }
+ ^search$ {
+ if {![regexp {broad|narrow} $scope]} {
+ WPCmd PEMailbox focus 0
+ WPCmd PEMailbox search none
+ set scope broad
+ }
+
+ switch -- $type {
+ none {
+ WPCmd PEMailbox focus 0
+ WPCmd PEMailbox search none
+ }
+ any {
+ if {![string length $criteria]} {
+ WPCmd PEInfo statmsg "No search criteria provided"
+ } elseif {[catch {WPCmd PEMailbox search $scope text ton any $criteria} result]} {
+ WPCmd PEInfo statmsg "Search failed: $result"
+ } else {
+ if {$result == 0} {
+ WPCmd PEInfo statmsg "No messages matched your search"
+ cgi_html_comment "SCOPE: $scope"
+ if {0 == [string compare new $scope]} {
+ WPCmd PEMailbox focus 0
+ WPCmd PEMailbox search none
+ }
+ } else {
+ set n [WPCmd PEMailbox first]
+ if {[catch {WPCmd PEMailbox uid $n} u]} {
+ set n 1
+ set u 0
+ }
+
+ focusOnResult focused
+ }
+ }
+ }
+ compound {
+ if {![string length $criteria]} {
+ WPCmd PEInfo statmsg "No search criteria provided"
+ } elseif {[catch {WPCmd PEMailbox search $scope compound $criteria} result]} {
+ WPCmd PEInfo statmsg "Search failed: $result"
+ } else {
+ if {$result == 0} {
+ WPCmd PEInfo statmsg "No messages matched your search"
+ if {0 == [string compare new $scope]} {
+ WPCmd PEMailbox focus 0
+ WPCmd PEMailbox search none
+ }
+ } else {
+ set n [WPCmd PEMailbox first]
+ if {[catch {WPCmd PEMailbox uid $n} u]} {
+ set n 1
+ set u 0
+ }
+
+ focusOnResult focused
+ }
+ }
+ }
+ default {
+ WPCmd PEInfo statmsg "Unrecognized search: $type"
+ }
+ }
+ }
+ ^focus$ {
+ focusOnResult focused
+ }
+ ^unfocus$ {
+ if {[catch {WPCmd PEMailbox focus 0} result]} {
+ WPCmd PEInfo statmsg "Cannot unfocus: $result"
+ } elseif {$focused > 0} {
+ WPCmd PEInfo statmsg "All messages displayed"
+ set focused 0
+ }
+ }
+ noop -
+ ^$ {
+ }
+ default {
+ }
+ }
+}
+
+if {$focused} {
+ set mc $focused
+}
+
+# page framing (note maybe changed by actions above)
+wpInitPageFraming u n mc ppg pn pt
+
+cgi_puts [WPCmd cgi_buffer "drawMessageList $c {$f} $n $ppg"]
+
+cgi_puts "<script>"
+cgi_put "updateBrowseLinksAndSuch(\{"
+cgi_put "u:$u,selected:[WPCmd PEMailbox selected],"
+cgi_put "unread:[WPCmd PEMailbox flagcount [list unseen undeleted]],"
+cgi_put "page:$pn,pages:$pt,count:$mc,"
+cgi_put "searched:[WPCmd PEMailbox searched],focused:$focused,"
+cgi_put "sort:'[lindex [WPCmd PEMailbox sort] 0]'"
+if {[info exists trashed] && $trashed != 0} {
+ cgi_put ",trashed:$trashed"
+}
+if {[info exists cpmv] && $cpmv != 0} {
+ cgi_put ",cpmv:{f:'$cpmvdest',n:$cpmv}"
+}
+cgi_puts "\});"
+if {0 == [string compare $page new]} {
+ cgi_puts "showBrowseMenus();"
+ cgi_puts "initMenus();"
+ cgi_puts "initMorcButton('listMorcButton');"
+ cgi_puts "initSelection();"
+ wpSaveMenuJavascript "browse" $c $f [WPCmd PEFolder defaultcollection] morcInBrowseDone
+ wpSetMessageListNewMailCheck
+ cgi_puts "if(self.loadDDElements) loadDDElements();"
+}
+if {[info exists savecachechange]} {
+ wpSaveMenuJavascript browse $c $f $defc morcInBrowseDone $savecachechange
+}
+wpStatusAndNewmailJavascript
+cgi_puts "if(self.loadDDElements) loadDDElements();"
+cgi_puts "</script>"
diff --git a/web/cgi/alpine/2.0/newview.tcl b/web/cgi/alpine/2.0/newview.tcl
new file mode 100755
index 00000000..afcc2f0b
--- /dev/null
+++ b/web/cgi/alpine/2.0/newview.tcl
@@ -0,0 +1,306 @@
+#!./tclsh
+# $Id: newview.tcl 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# newview.tcl
+#
+# Purpose: CGI script that generates a page displaying a message
+# list of the indicated folder.
+#
+# Input: PATH_INFO: [/<col_number>]/<folder_name>[/<uid_of_first_msg>
+# along with possible search parameters:
+set newview_args {
+ {op {} ""}
+ {df {} ""}
+ {img {} 0}
+ {page {} ""}
+}
+
+# inherit global config
+source ./alpine.tcl
+source ./common.tcl
+source ./foldercache.tcl
+
+# default newview state
+set showimg ""
+
+# TEST
+proc cgi_suffix {args} {
+ return ""
+}
+
+proc next_message {_n _u {i 1}} {
+ upvar 1 $_n n
+ upvar 1 $_u u
+
+ if {[catch {WPCmd PEMailbox next $n $i} nnext]} {
+ WPCmd PEInfo statmsg "Cannot get next message: $nnext"
+ } else {
+ set n $nnext
+ if {[catch {WPCmd PEMailbox uid $n} u]} {
+ WPCmd PEInfo statmsg "Message $nnext has no UID: $u"
+ set n 0
+ set u 0
+ }
+ }
+}
+
+# grok PATH_INFO for collection 'c' and folder 'f'
+if {[info exists env(PATH_INFO)] && [string length $env(PATH_INFO)]} {
+ if {[regexp {^/([0-9]+)/(.*)/([0-9]+)$} $env(PATH_INFO) dummy c f u]} {
+ # Import data validate it and get session id
+ if {[catch {WPGetInputAndID sessid} result]} {
+ set harderr "Invalid Session: $result"
+ set deadsession 1
+ } else {
+ # grok parameters
+ foreach item $newview_args {
+ if {[catch {eval WPImport $item} result]} {
+ set harderr "Cannot read input: $result"
+ break;
+ }
+ }
+
+ if {[catch {WPCmd PEMessage $u number} n]} {
+ set harderr "Message no longer exists in $f"
+ }
+ }
+ } else {
+ set harderr "Invalid path: $env(PATH_INFO)"
+ set nofolder 1
+ }
+} else {
+ set harderr "No Folder Specified!"
+ set nofolder 1
+}
+
+if {[info exists harderr]} {
+ if {[info exists deadsession]} {
+ cgi_puts "Content-type: text/html; charset=\"UTF-8\"\n"
+ cgi_puts "Your Web Alpine Session has been closed."
+ } else {
+ catch { WPCmd PEInfo statmsg "$harderr" }
+ unset harderr
+
+ if {[info exists nofolder]} {
+ set env(PATH_INFO) "/0/INBOX"
+ } else {
+ set env(PATH_INFO) "/${c}/${f}"
+ }
+
+ source newlist.tcl
+ }
+
+ exit
+}
+
+cgi_puts "Content-type: text/html; charset=\"UTF-8\"\n"
+
+if {[catch {WPCmd PEMessage $u number} n]} {
+ WPCmd PEInfo statmsg "Message access error: $n"
+ set n 0
+} else {
+ switch -regexp -- $op {
+ ^next$ {
+ next_message n u
+ }
+ ^prev$ {
+ next_message n u -1
+ }
+ ^hdron$ {
+ WPCmd PEInfo mode full-header-mode 2
+ }
+ ^hdroff$ {
+ WPCmd PEInfo mode full-header-mode 0
+ }
+ ^unread$ {
+ if {[catch {WPCmd PEMessage $u flag new 1} result]} {
+ WPCmd PEInfo statmsg "Delete of $u failed: $result"
+ } else {
+ next_message n u
+ }
+ }
+ ^delete$ {
+ if {[catch {WPCmd PEMessage $u flag deleted 1} result]} {
+ WPCmd PEInfo statmsg "Delete of $u failed: $result"
+ } else {
+ WPCmd PEInfo statmsg "Message moved to Trash"
+ if {$n == 1} {
+ next_message n u
+ set n 1
+ } elseif {$n == [WPCmd PEMailbox messagecount]} {
+ next_message n u -1
+ } else {
+ set nnext $n
+ next_message n u
+ set n $nnext
+ }
+ }
+ }
+ ^trash$ {
+ if {[catch {WPCmd PEFolder empty $c [wpLiteralFolder $c $f] $u} result]} {
+ WPCmd PEInfo statmsg "Cannot delete forever: $result"
+ } else {
+ WPCmd PEInfo statmsg "Message deleted forever"
+ set mc [WPCmd PEMailbox messagecount]
+ if {$mc == 0} {
+ set n 0
+ set u 0
+ } else {
+ if {$n > $mc} {
+ set n $mc
+ }
+
+ if {[catch {WPCmd PEMailbox uid $n} u]} {
+ WPCmd PEInfo statmsg "Cannot get UID of $n: $u"
+ set n 0
+ set u 0
+ }
+ }
+ }
+ }
+ ^move$ -
+ ^copy$ {
+ if {[string length $df] && [regexp {^([0-9]+)/(.*)$} $df dummy dfc dfn] && [string length $dfn]} {
+ if {[catch {WPCmd PEMessage $u $op $dfc [wpLiteralFolder $dfc $dfn]} result]} {
+ WPCmd PEInfo statmsg "Cannot $op message $n: $result"
+ } else {
+ if {0 == [string compare $op move]} {
+ if {$c == [WPCmd PEFolder defaultcollection]
+ && (([info exists _wp(spamfolder)] && 0 == [string compare $f $_wp(spamfolder)])
+ || 0 == [string compare $f Trash])} {
+ if {[catch {WPCmd PEFolder empty $c $f $u} result]} {
+ WPCmd PEInfo statmsg "Cannot empty Trash: $result"
+ } else {
+ set mc [WPCmd PEMailbox messagecount]
+ if {$mc == 0} {
+ set n 0
+ set u 0
+ } else {
+ if {$n > $mc} {
+ set n $mc
+ }
+
+ if {[catch {WPCmd PEMailbox uid $n} u]} {
+ WPCmd PEInfo statmsg "Cannot get UID of $n: $u"
+ set n 0
+ set u 0
+ }
+ }
+ }
+ } else {
+ if {[catch {WPCmd PEMailbox expunge} blasted] || [string length $blasted]} {
+ WPCmd PEInfo statmsg "Move Problem: $blasted"
+ }
+
+ next_message n u
+ }
+ }
+
+ # feedback from alpined
+ if {[string compare -nocase inbox $dfn]} {
+ addSaveCache $dfn
+ set savecachechange $dfn
+ }
+ }
+ } else {
+ WPCmd PEInfo statmsg "Cannot $op to $df"
+ }
+ }
+ ^spam$ {
+ if {[info exists _wp(spamsubj)]} {
+ set spamsubj $_wp(spamsubj)
+ } else {
+ set spamsubj "Spam Report"
+ }
+
+ if {[info exists _wp(spamfolder)] && [string length $_wp(spamfolder)]
+ && [catch {
+ set defc [WPCmd PEFolder defaultcollection]
+ if {[WPCmd PEFolder exists $defc $_wp(spamfolder)] == 0} {
+ WPCmd PEFolder create $defc $_wp(spamfolder)
+ }
+
+ WPCmd PEMessage $u save $defc $_wp(spamfolder)
+ } result]} {
+ WPCmd PEInfo statmsg "Error spamifying message $message: $result"
+
+ } elseif {[info exists _wp(spamaddr)] && [string length $_wp(spamaddr)]
+ && [catch {WPCmd PEMessage $u spam $_wp(spamaddr) $spamsubj} result]} {
+ WPCmd PEInfo statmsg "Can't Report Spam: $result"
+ } elseif {[catch {WPCmd PEMessage $u flag deleted 1} result]} {
+ WPCmd PEInfo statmsg "Error spamifying message $message: $result"
+ } else {
+ WPCmd PEInfo statmsg "Message $n reported as Spam and flagged for deletion"
+ if {$n == 1} {
+ next_message n u
+ set n 1
+ } elseif {$n == [WPCmd PEMailbox messagecount]} {
+ next_message n u -1
+ } else {
+ set nnext $n
+ next_message n u
+ set n $nnext
+ }
+ }
+ }
+ ^$ -
+ ^noop$ {
+ # $img == 0 : remove current from from allow_cid_images list
+ # $img == 1 : allow images for this message this once
+ # $img == "from" : always allow images from this address
+ if {[regexp {0|1|from} $img]} {
+ set showimg $img
+ }
+ }
+ default {
+ WPCmd PEInfo statmsg "Unrecognized option: $op"
+ }
+ }
+}
+
+if {$n > 0} {
+ cgi_puts [WPCmd cgi_buffer "drawMessageText $c {$f} $u $showimg"]
+ cgi_puts "<script>"
+ if {0 == [catch {WPCmd PEMessage $u needpasswd}]} {
+ cgi_put "getSmimePassphrase({sessid:'$_wp(sessid)',control:this,parms:{op:'noop',page:'new'}});"
+ }
+ if {[catch {WPCmd PEMailbox next $n 1} nnext]} {
+ WPCmd PEInfo statmsg "Cannot get next message: $nnext"
+ } else {
+ if {[catch {WPCmd PEMailbox uid $nnext} unext]} {
+ WPCmd PEInfo statmsg "Message $nnext has no UID: $unext"
+ set unext 0
+ }
+ }
+ cgi_puts "updateViewLinksAndSuch(\{u:$u,n:$n,unext:$unext,unread:[WPCmd PEMailbox flagcount [list unseen undeleted]],count:[WPCmd PEMailbox messagecount],selected:[WPCmd PEMailbox selected]\});"
+ if {0 == [string compare new $page]} {
+ cgi_puts "showViewMenus();"
+ cgi_puts "initMenus();"
+ cgi_puts "initMorcButton('viewMorcButton');"
+ wpSaveMenuJavascript "view" $c $f [WPCmd PEFolder defaultcollection] morcInViewDone
+ }
+ if {[info exists savecachechange]} {
+ wpSaveMenuJavascript "view" $c $f [WPCmd PEFolder defaultcollection] morcInViewDone $savecachechange
+ }
+
+ cgi_puts "document.getElementById('alpineContent').scrollTop = 0;"
+ wpStatusAndNewmailJavascript
+ cgi_puts "setCheckMailFunction('gCheck', newMailCheck);"
+ cgi_puts "setNewMailCheckInterval([WPCmd PEInfo inputtimeout]);"
+ cgi_puts "</script>"
+} else {
+ cgi_puts "<script>"
+ cgi_puts "updateViewLinksAndSuch({});"
+ wpStatusAndNewmailJavascript
+ cgi_puts "</script>"
+}
diff --git a/web/cgi/alpine/2.0/reply b/web/cgi/alpine/2.0/reply
new file mode 120000
index 00000000..872d4cb2
--- /dev/null
+++ b/web/cgi/alpine/2.0/reply
@@ -0,0 +1 @@
+compose \ No newline at end of file
diff --git a/web/cgi/alpine/2.0/replyall b/web/cgi/alpine/2.0/replyall
new file mode 120000
index 00000000..872d4cb2
--- /dev/null
+++ b/web/cgi/alpine/2.0/replyall
@@ -0,0 +1 @@
+compose \ No newline at end of file
diff --git a/web/cgi/alpine/2.0/resume b/web/cgi/alpine/2.0/resume
new file mode 120000
index 00000000..872d4cb2
--- /dev/null
+++ b/web/cgi/alpine/2.0/resume
@@ -0,0 +1 @@
+compose \ No newline at end of file
diff --git a/web/cgi/alpine/2.0/settings b/web/cgi/alpine/2.0/settings
new file mode 100755
index 00000000..dc69487d
--- /dev/null
+++ b/web/cgi/alpine/2.0/settings
@@ -0,0 +1,734 @@
+#!./tclsh
+# $Id: settings 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# view.tcl
+#
+# Purpose: CGI script settings for Web Alpine 2.0 settings page
+#
+# Input:
+#
+set settings_args {
+}
+
+# inherit global config
+source ./alpine.tcl
+source ./foldercache.tcl
+source ./common.tcl
+
+set script_base "$_wp(serverpath)/$_wp(appdir)/$_wp(ui2dir)/"
+
+# TEST
+proc cgi_suffix {args} {
+ return ""
+}
+
+set labels 0
+proc set_feature {feature desc} {
+ global labels
+ cgi_checkbox $feature=1 [set_checked [WPCmd PEInfo feature $feature]] id="label_[incr labels]"
+ cgi_put " <label for=\"label_${labels}\">$desc</label>"
+}
+
+proc set_checked {checked {token checked}} {
+ if {$checked > 0} {
+ return $token
+ }
+
+ return ""
+}
+
+proc set_variable {var {desc ""}} {
+ global labels
+ if {[string length $desc]} {
+ cgi_put "<label for=\"label_[incr labels]\">${desc}</label>"
+ }
+ cgi_text ${var}=[var_value $var] id="label_${labels}"
+}
+
+
+WPEval $settings_args {
+
+ if {0 == [catch {WPCmd PEFolder current} curfold]} {
+ set c [lindex $curfold 0]
+ set f [lindex $curfold 1]
+ } else {
+ set c 0
+ set f inbox
+ }
+
+ set charset "UTF-8"
+
+ cgi_http_head {
+ WPStdHttpHdrs "text/html; charset=$charset"
+ }
+
+ cgi_html {
+ cgi_head {
+ cgi_content_type "text/html; charset=$charset"
+ cgi_title [wpPageTitle "Settings"]
+ cgi_base "href=$script_base"
+ cgi_stylesheet css/cbn/screen.css
+ # Yahoo Styles
+ cgi_stylesheet $_wp(yui)/build/container/assets/container-core.css
+ cgi_stylesheet $_wp(yui)/build/menu/assets/skins/sam/menu.css
+ cgi_stylesheet $_wp(yui)/build/button/assets/skins/sam/button.css
+ # YahooUI libraries
+ cgi_script type=text/javascript language="JavaScript" src="$_wp(yui)/build/utilities/utilities.js" {}
+ cgi_script type=text/javascript language="JavaScript" src="$_wp(yui)/build/container/container-min.js" {}
+ cgi_script type=text/javascript language="JavaScript" src="$_wp(yui)/build/datasource/datasource-min.js" {}
+ cgi_script type=text/javascript language="JavaScript" src="$_wp(yui)/build/menu/menu-min.js" {}
+ cgi_script type=text/javascript language="JavaScript" src="$_wp(yui)/build/button/button-min.js" {}
+ # local libraries
+ cgi_script language="JavaScript" src="lib/common.js" {}
+ cgi_script language="JavaScript" src="lib/settings.js" {}
+ cgi_javascript {
+ cgi_puts "YAHOO.alpine.cgi_root = '$_wp(serverpath)';"
+ cgi_puts "YAHOO.alpine.cgi_base = '$script_base';"
+ cgi_puts "YAHOO.alpine.current.incoming = [WPCmd PEFolder isincoming $c];"
+ cgi_puts "YAHOO.alpine.current.c = $c;"
+ cgi_puts "YAHOO.alpine.current.f = \"$f\";"
+ cgi_puts "function bodyOnLoad() {"
+ cgi_puts " if(YAHOO.env.ua.gecko > 0){ sizeVPHeight(); window.onresize = resizeVPHeight; }"
+ cgi_puts " setCheckMailFunction('gCheck', newMailCheck);"
+ cgi_puts " setNewMailCheckInterval([WPCmd PEInfo inputtimeout]);"
+ cgi_puts "}"
+ cgi_puts "browserDetect();"
+ }
+ }
+
+ cgi_body class=wap "onLoad=bodyOnLoad()" {
+ cgi_puts {<iframe name="formResponse" id="formResponse" src="img/cbn/spritelib.gif"></iframe>}
+ wpCommonPageLayout {settings {
+ cgi_hr
+ cgi_division {
+ cgi_put [cgi_url [cgi_span "class=sp splci tbi5" "Back to $f"] browse/$c/$f class=wap title="Return to $f without saving changes"]
+ }
+ cgi_hr
+ cgi_division class="ftitle bld" {
+ cgi_put "Basic Settings"
+ }
+ cgi_division "class=\"bld sel\"" {
+ cgi_put [cgi_url [cgi_span "class=sp splcs splc12" "General Preferences"] # class=wap id=Page1 "onClick=return settingsPage(this);"]
+ }
+ cgi_division class=bld {
+ cgi_put [cgi_url [cgi_span "class=sp splcs splc12" "Personal Preferences"] # class=wap id=Page2 "onClick=return settingsPage(this);"]
+ }
+ cgi_division class=bld {
+ cgi_put [cgi_url [cgi_span "class=sp splcs splc12" "News and Weather"] # class=wap id=Page3 "onClick=return settingsPage(this);"]
+ }
+ cgi_division class="ftitle bld" "style=\"margin-top: 2em;\"" {
+ cgi_put [cgi_url "[cgi_img img/cbn/f_plus.gif class=wap] Advanced Settings" # class=wap "onClick=return toggleAdvance(this);"]
+ }
+ cgi_division id=advancedSettings {
+ cgi_division class=bld {
+ cgi_put [cgi_url [cgi_span "class=sp splcs splc12" "Message List"] # class=wap id=Page4 "onClick=return settingsPage(this);"]
+ }
+ cgi_division class=bld {
+ cgi_put [cgi_url [cgi_span "class=sp splcs splc12" "Message View"] # class=wap id=Page5 "onClick=return settingsPage(this);"]
+ }
+ cgi_division class=bld {
+ cgi_put [cgi_url [cgi_span "class=sp splcs splc12" "Folders"] # class=wap id=Page8 "onClick=return settingsPage(this);"]
+ }
+ cgi_division class=bld {
+ cgi_put [cgi_url [cgi_span "class=sp splcs splc12" "Compose"] # class=wap id=Page6 "onClick=return settingsPage(this);"]
+ }
+ cgi_division class="ftitle bld" "style=\"margin-top: 2em;\"" {
+ cgi_put "Server Settings"
+ }
+ cgi_division class=bld {
+ cgi_put [cgi_url [cgi_span "class=sp splcs splc12" "Mail Servers"] # class=wap id=Page10 "onClick=return settingsPage(this);"]
+ }
+ cgi_division class=bld {
+ cgi_put [cgi_url [cgi_span "class=sp splcs splc12" "Directory Servers"] # class=wap id=Page9 "onClick=return settingsPage(this);"]
+ }
+ }
+ if {[info exists _wp(filter_link)] || [info exists _wp(vacation_link)]} {
+ cgi_division class="ftitle bld" "style=\"margin-top: 2em;\"" {
+ cgi_put "External Settings"
+ }
+ }
+ if {[info exists _wp(filter_link)]} {
+ cgi_division class=bld {
+ cgi_put [cgi_url [cgi_span "class=sp splcs splc12" "Message Filtering"] $_wp(filter_link) class=wap target=_blank]
+ }
+ }
+ if {[info exists _wp(vacation_link)]} {
+ cgi_division class=bld {
+ cgi_put [cgi_url [cgi_span "class=sp splcs splc12" "Vacation Auto-Reply"] $_wp(vacation_link) class=wap target=_blank]
+ }
+ }
+ }} "$c" "$f" 0 Settings \
+ [list [cgi_cgi "$_wp(appdir)/$_wp(ui2dir)/browse/${c}/${f}"] Settings 0 searchContent('settings','alpineContent')] "" {
+ # CONTEXT COMMANDS
+ cgi_division class=hdrBtns {
+ cgi_javascript {
+ cgi_put "if(window.print) document.write('[cgi_buffer {cgi_put [cgi_url "[cgi_span "class=sp hdrBtnImg hbi1" ""][cgi_span "class=hdrBtnText" Print]" "print" "onClick=return printContent()"]}]');"
+ }
+
+ cgi_put [cgi_url "[cgi_span "class=sp hdrBtnImg hbi3" ""][cgi_span "class=hdrBtnText" Help]" "javascript:openHelpWindow('settings.html');" class=wap]
+ cgi_put [cgi_url "[cgi_span "class=sp hdrBtnImg hbi4" ""][cgi_span "class=hdrBtnText" "Sign out"]" "../../session/logout.tcl?cid=[WPCmd PEInfo key]&sessid=${sessid}"]
+ }
+ } {
+ # TOP MENUBAR
+ cgi_anchor_name "toolbar"
+ cgi_table class="toolbarTbl" cellpadding="0" cellspacing="0" {
+ cgi_puts "<tbody>"
+ cgi_table_row {
+ cgi_table_data {
+ cgi_table class="toolbarTbl" cellpadding="0" cellspacing="0" {
+ cgi_puts "<tbody>"
+ cgi_table_row {
+ cgi_table_data class="wap" {
+ cgi_put [cgi_url [cgi_span "class=sp spmbi spmb10" "Save Settings"] "#" title="Save Settings Changes" "onClick=return saveSettings();"]
+ }
+ cgi_table_data class="wap" {
+ cgi_put [cgi_url [cgi_span "class=sp spmbi spmb21" "Reset to Default Settings"] "#" "onClick=panelConfirm('Restoring default settings will erase all custom settings.<p>Are you sure you want to restore default settings?',{text:'Restore Defaults',fn:restoreDefaultSettings}); return false;" title="Reset to Default Settings"]
+ }
+ cgi_table_data class="wap" {
+ cgi_put [cgi_url [cgi_span "class=sp spmbi spmb12" "Cancel"] "browse/$c/$f" title="Cancel Settings Changes"]
+ }
+ cgi_table_data width="100%" {
+ cgi_puts [cgi_nbspace]
+ }
+ }
+ cgi_puts "</tbody>"
+ }
+ }
+ }
+ cgi_puts "</tbody>"
+ }
+ } {
+ cgi_form $_wp(appdir)/$_wp(ui2dir)/conduit/settings.tcl "enctype=multipart/form-data" id=settingsForm target=formResponse {
+ cgi_text "restore=false" id=restore type=hidden notab
+ cgi_table id=settingsPage1 "class=\"fields settings\"" cellpadding="0" cellspacing="0" {
+ cgi_puts "<tbody>"
+ cgi_puts "<tr><td class=title colspan=2><h2>General Preferences</h2></td></tr>"
+ cgi_table_row {
+ cgi_table_data class="title" {
+ cgi_puts "Message Display"
+ #[cgi_br]<a href="#" title="more info..." onClick="help_popup1.showPopup('anchor1');return false;" name="anchor1" id="anchor1"><img src="img/cbn/help_sm.gif"></a>
+ }
+ cgi_table_data class="body" {
+ if {[info exists _wp(themes)]} {
+ cgi_put "Theme: "
+ cgi_select theme {
+ foreach theme $_wp(themes) {
+ cgi_option [lindex $theme 0] value=[lindex $theme 1]
+ }
+ }
+ cgi_br
+ }
+ cgi_put "Display "
+ set n [var_value wp-indexlines]
+ cgi_select wp-indexlines {
+ for {set i 10} {$i <= $_wp(indexlinesmax)} {incr i 5} {
+ cgi_option $i value=$i [set_checked [expr {$i == $n}] selected]
+ }
+ }
+ cgi_put " messages per page"
+ cgi_br
+ cgi_put "Wrap Plain Text message at "
+ set n [WPCmd PEConfig columns]
+ cgi_select wrapColumn {
+ for {set i 20} {$i < 130} {incr i 4} {
+ cgi_option $i value=$i [set_checked [expr {$i == $n}] selected]
+ }
+ }
+ cgi_put " characters"
+ }
+ }
+ cgi_table_row {
+ cgi_table_data class="title" {
+ cgi_put "Folders"
+ #[cgi_br]<a href="#" title="more info..."><img src="img/cbn/help_sm.gif"></a>
+ }
+ cgi_table_data class="body" {
+ cgi_put "Display "
+ if {[catch {WPSessionState left_column_folders} n]} {
+ set n $_wp(fldr_cache_def)
+ }
+
+ cgi_select folderCache {
+ for {set i 5} {$i <= $_wp(fldr_cache_max)} {incr i} {
+ cgi_option $i value=$i [set_checked [expr {$i == $n}] selected]
+ }
+ }
+ cgi_put " recent folders in left column"
+ }
+ }
+ cgi_table_row {
+ cgi_table_data class="title" {
+ cgi_put "Reply[cgi_nbspace]Options"
+ #[cgi_br]<a href="#" title="more info..."><img src="img/cbn/help_sm.gif"></a>
+ }
+ cgi_table_data class="body" {
+ set_feature include-header-in-reply "Include headers in replies"
+ cgi_br
+ set_feature include-attachments-in-reply "Include attachments in replies"
+ cgi_br
+ set_feature signature-at-bottom "Append signature below reply text"
+ cgi_br
+ set_feature strip-from-sigdashes-on-reply "Strip signatures when replying"
+ }
+ }
+ cgi_table_row {
+ cgi_table_data class="title" {
+ cgi_put "Forwarding[cgi_nbspace]Options"
+ #[cgi_br]<a href="#" title="more info..."><img src="img/cbn/help_sm.gif"></a>
+ }
+ cgi_table_data class="body" {
+ set as_attach [WPCmd PEInfo feature forward-as-attachment]
+ cgi_radio_button forwardAs=inline id="forward_inline" [set_checked [expr {$as_attach == 0}]]
+ cgi_put " <label for=forward_inline>Forward messsages inline</label>"
+ cgi_br
+ cgi_radio_button forwardAs=attached id="forward_attachment" [set_checked $as_attach]
+ cgi_put " <label for=forward_attachment>Forward messsages as attachments</label>"
+ }
+ }
+ cgi_table_row {
+ cgi_table_data class="title" {
+ cgi_put "Sent[cgi_nbspace]Message[cgi_nbspace]Options"
+ cgi_put "<p><span class=tips><strong>Tip:</strong> Set folder blank to prevent saving of Sent messages</span>"
+ #[cgi_br]<a href="#" title="more info..."><img src="img/cbn/help_sm.gif"></a>
+
+ }
+ cgi_table_data class="body" {
+ set_variable default-fcc "Name of your Sent mail folder (also called \"Fcc\"): "
+ cgi_br
+ set_feature fcc-without-attachments "Save sent messages to Sent folder without attachments"
+ }
+ }
+ cgi_puts "</tbody>"
+ }
+ cgi_table id=settingsPage2 "class=\"fields settings\"" "style=\"display: none;\"" cellpadding="0" cellspacing="0" {
+ cgi_puts "<tbody>"
+ cgi_puts "<tr><td class=title colspan=2><h2>Personal[cgi_nbspace]Information</h2></td></tr>"
+ cgi_table_row {
+ cgi_table_data class="title" {
+ cgi_put "Display[cgi_nbspace]Name"
+ #<br><a href="#" title="more info..."><img src="img/cbn/help_sm.gif"></a>
+ }
+ cgi_table_data class="body" {
+ set_variable personal-name
+ }
+ }
+ cgi_table_row {
+ cgi_table_data class="title" {
+ cgi_put "User[cgi_nbspace]Domain"
+ #<br><a href="#" title="more info..."><img src="img/cbn/help_sm.gif"></a>
+ }
+ cgi_table_data class="body" {
+ set_variable user-domain
+ }
+ }
+ cgi_table_row {
+ cgi_table_data class="title" {
+ cgi_put "Email[cgi_nbspace]Signature"
+ #<br><a href="#" title="more info..."><img src="img/cbn/help_sm.gif"></a>
+ }
+ cgi_table_data class="body" {
+ #<input name="signature" id="no_sig" type="radio" checked />
+ #<label for="no_sig">No Signature</label><br>
+ #<input name="signature" id="use_sig" type="radio" />
+ #<label for="use_sig">Append Signature when composing messages</label><br>
+ cgi_textarea signature=[join [WPCmd PEInfo rawsig] "\n"] cols="72" rows="5" title=Signature
+ }
+ }
+ cgi_table_row {
+ cgi_table_data class="title" {
+ cgi_put "Alternate[cgi_nbspace]Addresses"
+ cgi_put "<p><span class=tips><b>Tip:</b> List alternate email addresses "
+ cgi_put "you use. Reply All will not include"
+ cgi_put "these email addresses when replying.</span>"
+ #<br><a href="#" title="more info..."><img src="img/cbn/help_sm.gif"></a>
+ }
+ cgi_table_data class="body" {
+ cgi_put "Add/Remove Addresses: <input type=button name=addCH value=Add onClick=\"return listAdd('altAddrTable','altAddr');\"/>"
+ set varval [WPCmd PEConfig varget alt-addresses]
+ cgi_text "altAddrs=[llength [lindex $varval 0]]" id=altAddrs type=hidden notab
+ cgi_table id=altAddrTable {
+ set n 0
+ foreach svr [lindex $varval 0] {
+ incr n
+ cgi_table_row {
+ cgi_table_data {
+ cgi_text altAddr${n}=[string trim $svr] size="45"
+ cgi_put [cgi_url [cgi_img img/cbn/remove.gif class=wap] # "onClick=return removeTableRow(this);"]
+ }
+ }
+ }
+ }
+ }
+ }
+ cgi_puts "</tbody>"
+ }
+ cgi_table id=settingsPage3 "class=\"fields settings\"" "style=\"display: none;\"" cellpadding="0" cellspacing="0" {
+ cgi_puts "<tbody>"
+ cgi_puts "<tr><td class=title colspan=2><h2>News[cgi_nbspace]and[cgi_nbspace]Weather</h2></td></tr>"
+
+ cgi_table_row {
+ cgi_table_data class="title" {
+ cgi_put "Headline[cgi_nbspace]News"
+ #<br><a href="#" title="more info..."><img src="img/cbn/help_sm.gif"></a>
+ }
+ cgi_table_data class="body" {
+ cgi_put "RSS URL: "
+ cgi_text rss-news=[var_value rss-news] size="45"
+ }
+ }
+ cgi_table_row {
+ cgi_table_data class="title" {
+ cgi_put "Weather[cgi_nbspace]Bar"
+ #<br><a href="#" title="more info..."><img src="img/cbn/help_sm.gif"></a>
+ }
+ cgi_table_data class="body" {
+ cgi_put "RSS URL: "
+ cgi_text rss-weather=[var_value rss-weather] size="45"
+ }
+ }
+ cgi_puts "</tbody>"
+ }
+ cgi_table id=settingsPage4 "class=\"fields settings\"" "style=\"display: none;\"" cellpadding="0" cellspacing="0" {
+ cgi_puts "<tbody>"
+ cgi_puts "<tr><td class=title colspan=2><h2>Message[cgi_nbspace]List</h2></td></tr>"
+
+ cgi_table_row {
+ cgi_table_data class="title" {
+ cgi_put "Message List"
+ }
+ cgi_table_data class="body" {
+ cgi_table {
+ cgi_put "<tbody>"
+ cgi_table_row {
+ cgi_table_data {
+ cgi_put "Sort Order (default): "
+ }
+ cgi_table_data {
+ set val [WPCmd PEConfig varget sort-key]
+ cgi_select sort-key {
+ foreach k [lindex $val 2] {
+ cgi_option $k value=$k [set_checked [expr {0 == [string compare -nocase $k [lindex $val 0]]}] selected]
+ }
+ }
+ }
+ }
+ cgi_table_row {
+ cgi_table_data {
+ cgi_put "Start display at:"
+ }
+ cgi_table_data {
+ set val [WPCmd PEConfig varget incoming-startup-rule]
+ cgi_select incoming-startup-rule {
+ foreach k [lindex $val 2] {
+ cgi_option $k value=$k [set_checked [expr {0 == [string compare -nocase $k [lindex $val 0]]}] selected]
+ }
+ }
+ }
+ }
+ cgi_put "</tbody>"
+ }
+ }
+ }
+ cgi_table_row {
+ cgi_table_data class="title" {
+ cgi_put "Message[cgi_nbspace]Handling"
+ }
+ cgi_table_data class="body" {
+ set_variable read-message-folder "Name of the folder to hold your read messages: "
+ cgi_br
+ set_feature auto-move-read-msgs "Automatically Move Read Messages"
+ }
+ }
+ cgi_puts "</tbody>"
+ }
+ cgi_table id=settingsPage5 "class=\"fields settings\"" "style=\"display: none;\"" cellpadding="0" cellspacing="0" {
+ cgi_puts "<tbody>"
+ cgi_puts "<tr><td class=title colspan=2><h2>Message View</h2></td></tr>"
+
+ cgi_table_row {
+ cgi_table_data class="title" {
+ cgi_put "Display[cgi_nbspace]Headers"
+ }
+ cgi_table_data class="body" {
+ cgi_put "Add/Remove Header Names: <input type=button name=addCH value=Add onClick=\"return listAdd('viewerHdrsTable','viewerHdr');\"/>"
+ set varval [WPCmd PEConfig varget viewer-hdrs]
+ cgi_text "viewerHdrs=[llength [lindex $varval 0]]" id=viewerHdrs type=hidden notab
+ cgi_table id=viewerHdrsTable {
+ set n 0
+ foreach svr [lindex $varval 0] {
+ incr n
+ cgi_table_row {
+ cgi_table_data {
+ cgi_text viewerHdr${n}=[string trim $svr] size="45"
+ cgi_put [cgi_url [cgi_img img/cbn/remove.gif class=wap] # "onClick=return removeTableRow(this);"]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data class="title" {
+ cgi_put "Display[cgi_nbspace]Links[cgi_nbspace]in[cgi_nbspace]Messages"
+ cgi_put "<p><span class=tips><b>Tip:</b> Applies only to plain text messages.</span>"
+ #<br><a href="#" title="more info..."><img src="img/cbn/help_sm.gif"></a>
+ }
+ cgi_table_data class="body" {
+ cgi_table {
+ cgi_put "<tbody>"
+ cgi_table_row {
+ cgi_table_data {
+ set_feature enable-msg-view-urls "Display complete URLs as links"
+ }
+ }
+ cgi_table_row {
+ cgi_table_data {
+ set_feature enable-msg-view-web-hostnames "Display hostnames and incomplete URLs as links"
+ }
+ }
+ cgi_table_row {
+ cgi_table_data {
+ set_feature enable-msg-view-addresses "Display Email addresses as links"
+ }
+ }
+ cgi_put "</tbody>"
+ }
+ }
+ }
+ cgi_table_row {
+ cgi_table_data class="title" {
+ cgi_put "Rich[cgi_nbspace]Text[cgi_nbspace]Display"
+ #<br><a href="#" title="more info..."><img src="img/cbn/help_sm.gif"></a>
+ }
+ cgi_table_data class="body" {
+ set_feature render-html-internally "Show rich text messages as plain text"
+ }
+ }
+ cgi_table_row {
+ cgi_table_data class="title" {
+ cgi_put "Anti-phishing"
+ cgi_put "<p><span class=tips><b>Tip:</b> This feature aids in identifying fake links. Applies only when rich text shown as plain text.</span>"
+ #<br><a href="#" title="more info..."><img src="img/cbn/help_sm.gif"></a>
+ }
+ cgi_table_data class="body" {
+ set_feature quell-server-after-link-in-html "Hide appended real hostname after links"
+ }
+ }
+ cgi_puts "</tbody>"
+ }
+ cgi_table id=settingsPage6 "class=\"fields settings\"" "style=\"display: none;\"" cellpadding="0" cellspacing="0" {
+ cgi_puts "<tbody>"
+ cgi_puts "<tr><td class=title colspan=2><h2>Compose</h2></td></tr>"
+ cgi_table_row {
+ cgi_table_data class="title" {
+ cgi_put "Compose[cgi_nbspace]Headers"
+ cgi_put "<p><span class=tips><b>Tip:</b> Default header fields displayed in Compose</span>"
+ }
+ cgi_table_data class="body" {
+ cgi_put "Add/Remove Header Names: <input type=button name=addCH value=Add onClick=\"return listAdd('composeHdrsTable','composeHdr');\"/>"
+ set varval [WPCmd PEConfig varget default-composer-hdrs]
+ cgi_text "composeHdrs=[llength [lindex $varval 0]]" id=composeHdrs type=hidden notab
+ cgi_table id=composeHdrsTable {
+ set n 0
+ foreach svr [lindex $varval 0] {
+ incr n
+ cgi_table_row {
+ cgi_table_data {
+ cgi_text composeHdr${n}=[string trim $svr] size="45"
+ cgi_put [cgi_url [cgi_img img/cbn/remove.gif class=wap] # "onClick=return removeTableRow(this);"]
+ }
+ }
+ }
+ }
+ }
+ }
+ cgi_table_row {
+ cgi_table_data class="title" {
+ cgi_put "Custom[cgi_nbspace]Headers"
+ }
+ cgi_table_data class="body" {
+ cgi_put "Add/Remove Headers: <input type=button name=addhdr value=Add onClick=\"return customHdrAdd('customHdrTbl');\"/>"
+ set varval [WPCmd PEConfig varget customized-hdrs]
+ cgi_text "customHdrFields=[llength [lindex $varval 0]]" id=customHdrFields type=hidden notab
+ cgi_table id=customHdrTbl {
+ set nh 0
+ foreach hdr [lindex $varval 0] {
+ incr nh
+ cgi_table_row {
+ set n [string first {:} $hdr]
+ switch -- $n {
+ -1 -
+ 0 -
+ 1 {
+ cgi_table_data colspan=2 {
+ cgi_text "customHdrField${nh}=[cgi_quote_html $hdr]" type=hidden notab
+ cgi_put "$hdr"
+ }
+ }
+ default {
+ cgi_table_data {
+ set field_name [string range $hdr 0 [expr {$n - 1}]]
+ cgi_put $field_name
+ cgi_text "customHdrField${nh}=[cgi_quote_html $field_name]" type=hidden notab
+ }
+ cgi_table_data {
+ cgi_text customHdrData${nh}=[string trim [string range $hdr [incr n] end]] size="45"
+ cgi_put [cgi_url [cgi_img img/cbn/remove.gif class=wap] # "onClick=return removeTableRow(this);"]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ cgi_table_row {
+ cgi_table_data class="title" {
+ cgi_put "Message[cgi_nbspace]Encoding"
+ #[cgi_br]<a href="#" title="more info..."><img src="img/cbn/help_sm.gif"></a>
+ }
+ cgi_table_data class="body" {
+ set_variable posting-character-set "Send messages using character encoding: "
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data class="title" {
+ cgi_put "Reply[cgi_nbspace]Options"
+ #[cgi_br]<a href="#" title="more info..."><img src="img/cbn/help_sm.gif"></a>
+ }
+ cgi_table_data class="body" {
+ set_variable reply-leadin "Reply intro string: "
+ cgi_br
+ set_variable reply-indent-string "Reply prefix: "
+ }
+ }
+ cgi_table_row {
+ cgi_table_data class="title" {
+ cgi_put "Flowed[cgi_nbspace]Text[cgi_nbspace]Handling"
+ #[cgi_br]<a href="#" title="more info..."><img src="img/cbn/help_sm.gif"></a>
+ }
+ cgi_table_data class="body" {
+ set_feature quell-flowed-text "Do not send text as Flowed Text"
+ }
+ }
+ cgi_puts "</tbody>"
+ }
+ cgi_table id=settingsPage7 "class=\"fields settings\"" "style=\"display: none;\"" cellpadding="0" cellspacing="0" {
+ cgi_puts "<tbody>"
+ cgi_puts "<tr><td class=title colspan=2><h2>Filters</h2></td></tr>"
+
+ cgi_puts "</tbody>"
+ }
+ cgi_table id=settingsPage8 "class=\"fields settings\"" "style=\"display: none;\"" cellpadding="0" cellspacing="0" {
+ cgi_puts "<tbody>"
+ cgi_puts "<tr><td class=title colspan=2><h2>Folders</h2></td></tr>"
+ cgi_table_row {
+ cgi_table_data class="title" {
+ cgi_put "Draft[cgi_nbspace]Folder"
+ #[cgi_br]<a href="#" title="more info..."><img src="img/cbn/help_sm.gif"></a>
+ }
+ cgi_table_data class="body" {
+ set_variable postponed-folder
+ }
+ }
+ cgi_table_row {
+ cgi_table_data class="title" {
+ cgi_put "Trash[cgi_nbspace]Folder"
+ #[cgi_br]<a href="#" title="more info..."><img src="img/cbn/help_sm.gif"></a>
+ }
+ cgi_table_data class="body" {
+ set_variable trash-folder
+ }
+ }
+ cgi_puts "</tbody>"
+ }
+ if {0 == [catch {WPCmd PEConfig varget ldap-servers} varval]} {
+ cgi_table id=settingsPage9 "class=\"fields settings\"" "style=\"display: none;\"" cellpadding="0" cellspacing="0" {
+ cgi_puts "<tbody>"
+ cgi_puts "<tr><td class=title colspan=2><h2>Directory[cgi_nbspace]Servers</h2></td></tr>"
+
+ cgi_table_row {
+ cgi_table_data class="title" {
+ cgi_put "LDAP[cgi_nbspace]Server"
+ }
+ cgi_table_data class="body" {
+ cgi_put "Add/Remove Servers: <input type=button name=addldap value=Add onClick=\"return listAdd('ldapServerTable','ldapServer');\"/>"
+ cgi_text "ldapServers=[llength [lindex $varval 0]]" id=ldapServers type=hidden notab
+ cgi_table id=ldapServerTable {
+ set n 0
+ foreach svr [lindex $varval 0] {
+ incr n
+ cgi_table_row {
+ cgi_table_data {
+ cgi_text ldapServer${n}=[string trim $svr] size="45"
+ cgi_put [cgi_url [cgi_img img/cbn/remove.gif class=wap] # "onClick=return removeTableRow(this);"]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ cgi_puts "</tbody>"
+ }
+ }
+ cgi_table id=settingsPage10 "class=\"fields settings\"" "style=\"display: none;\"" cellpadding="0" cellspacing="0" {
+ cgi_puts "<tbody>"
+ cgi_puts "<tr><td class=title colspan=2><h2>Mail[cgi_nbspace]Servers</h2></td></tr>"
+ cgi_table_row {
+ cgi_table_data class="title" {
+ cgi_put "Inbox[cgi_nbspace]Server"
+ cgi_put "<p><span class=tips><b>Tip:</b> Server name inside brackets, folder name after</span>"
+ }
+ cgi_table_data class="body" {
+ cgi_text inbox-path=[var_value inbox-path] size="45"
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data class="title" {
+ cgi_put "SMTP[cgi_nbspace]Server"
+ }
+ cgi_table_data class="body" {
+ cgi_put "Add/Remove Servers: <input type=button name=addsmtp value=Add onClick=\"return listAdd('smtpServerTable','smtpServer');\"/>"
+ set varval [WPCmd PEConfig varget smtp-server]
+ cgi_text "smtpServers=[llength [lindex $varval 0]]" id=smtpServers type=hidden notab
+ cgi_table id=smtpServerTable {
+ set n 0
+ foreach svr [lindex $varval 0] {
+ incr n
+ cgi_table_row {
+ cgi_table_data {
+ cgi_text smtpServer${n}=[string trim $svr] size="45"
+ cgi_put [cgi_url [cgi_img img/cbn/remove.gif class=wap] # "onClick=return removeTableRow(this);"]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ cgi_puts "</tbody>"
+ }
+ }
+ } {
+ # BOTTOM MENUBAR
+ cgi_table class="wap toolbarTbl" cellpadding="0" cellspacing="0" {
+ cgi_puts "<tbody><tr><td>&nbsp;</td></tr></tbody>"
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/2.0/tclsh b/web/cgi/alpine/2.0/tclsh
new file mode 120000
index 00000000..385fc6c6
--- /dev/null
+++ b/web/cgi/alpine/2.0/tclsh
@@ -0,0 +1 @@
+../tclsh \ No newline at end of file
diff --git a/web/cgi/alpine/2.0/view b/web/cgi/alpine/2.0/view
new file mode 100755
index 00000000..0466c2e2
--- /dev/null
+++ b/web/cgi/alpine/2.0/view
@@ -0,0 +1,237 @@
+#!./tclsh
+# $Id: view 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# view.tcl
+#
+# Purpose: CGI script generating page to display text of requested
+# message
+#
+# Input: PATH_INFO: [/<col_number>]/<folder_name>[/<uid_of_viewed_msg>
+# along with possible search parameters:
+set view_args {
+ {delete {} 0}
+ {spam {} 0}
+ {unread {} 0}
+ {star {} {}}
+ {showimg {} {}}
+ {hideimg {} {}}
+ {searchText {} {}}
+}
+
+
+# On input failure, redirect to home page "browse"
+proc view_redirect {} {
+ global _wp
+
+ cgi_http_head {
+ cgi_redirect "[cgi_root]/$_wp(appdir)/$_wp(ui2dir)/browse"
+ }
+}
+
+# inherit global config
+source ./alpine.tcl
+source ./common.tcl
+source ./foldercache.tcl
+source ./messageview.tcl
+
+# TEST
+proc cgi_suffix {args} {
+ return ""
+}
+
+# for inserting debug comments at end of page
+set dmsgs ""
+proc dm {s} {
+ global dmsgs
+ lappend dmsgs $s
+}
+
+
+WPEval $view_args {
+ # grok PATH_INFO for collection 'c' and folder 'f'
+ if {[info exists env(PATH_INFO)] && [string length $env(PATH_INFO)]} {
+ if {0 == [regexp {^/([0-9]+)/(.*)/([0-9]+)$} $env(PATH_INFO) dummy c f u]} {
+ WPCmd PEInfo statmsg "Cannot open invalid path: $env(PATH_INFO)"
+ error [list _redirect "[cgi_root]/$_wp(appdir)/$_wp(ui2dir)/browse/0/INBOX"]
+ }
+ } else {
+ WPCmd PEInfo statmsg "Cannot view unspecified folder"
+ error [list _redirect "[cgi_root]/$_wp(appdir)/$_wp(ui2dir)/browse/0/INBOX"]
+ }
+
+ # verify or visit specified collection/folder
+ if {[catch {setCurrentFolder c f u} result]} {
+ set authlist [wpHandleAuthException $result [list $c "folders in collection"] $f]
+ if {0 == [llength $authlist]} {
+ WPCmd PEInfo statmsg "$result"
+ error [list _redirect "$_wp(serverpath)/$_wp(appdir)/$_wp(ui2dir)/browse/$c/$f"]
+ }
+ }
+
+ # load message drawing routine for this session
+ # save per-message source, proc overhead
+ # to reinstall on the fly:
+ #catch {WPCmd rename drawMessageText {}}
+ if {0 == [llength [WPCmd info commands drawMessageText]]} {
+ set cgidir [file join $_wp(cgipath) $_wp(appdir) $_wp(ui2dir)]
+ if {[catch {
+ WPCmd source "${cgidir}/messageview.tcl"
+ WPCmd source "${cgidir}/messagelist.tcl"
+ } result]} {
+ error [list _action browse "cannot load message viewer: $result"]
+ }
+ }
+
+ # process any actions specified by view_args
+ if {$delete > 0} {
+ if {0 == [catch [WPCmd PEMessage $delete number] dnum]} {
+
+ }
+ # ELSE already deleted, don't worry about it
+ } elseif {$spam > 0} {
+ if {0 == [catch [WPCmd PEMessage $spam number] snum]} {
+
+ }
+ # ELSE already reported, don't worry about it
+ } elseif {$unread > 0} {
+ if {[catch {WPCmd PEMessage $unread flag new 1} result]} {
+ # ERROR: Cannot set $u unread
+ }
+ } elseif {[string length $star]} {
+ switch -- $star {
+ 0 {
+ if {[catch {WPCmd PEMessage $u flag important 0} result]} {
+ # ERROR: Cannot set Star on message $u
+ }
+ }
+ 1 {
+ if {[catch {WPCmd PEMessage $u flag important 1} result]} {
+ # ERROR: Cannot set Star on message $u
+ }
+ }
+ default {}
+ }
+ }
+
+ if {[catch {WPCmd PEMessage $u charset} charset]
+ || [string length $charset] == 0
+ || [string compare us-ascii [string tolower $charset]] == 0} {
+ set charset "ISO-8859-1"
+ }
+
+ if {[catch {WPCmd PEMessage $u number} n]} {
+ WPCmd PEInfo statmsg "$n"
+ error [list _redirect "$_wp(serverpath)/$_wp(appdir)/$_wp(ui2dir)/browse/$c/$f"]
+ }
+
+ cgi_http_head {
+ WPStdHttpHdrs "text/html; charset=$charset"
+ }
+
+ # counts and so forth
+ set mc [WPCmd PEMailbox messagecount]
+
+ set unext [WPCmd PEMailbox uid [WPCmd PEMailbox next $n 1]]
+ set delim [WPCmd PEFolder delimiter $c]
+
+
+ cgi_html {
+ cgi_head {
+ cgi_content_type "text/html; charset=$charset"
+ cgi_title [wpPageTitle "Message $n of $mc in $f"]
+ cgi_base "href=$_wp(serverpath)/$_wp(appdir)/$_wp(ui2dir)/"
+ cgi_stylesheet css/menu.css
+ cgi_stylesheet css/cbn/screen.css
+ cgi_stylesheet css/cbn/folderdialog.css
+ cgi_stylesheet $_wp(yui)/build/container/assets/container-core.css
+ cgi_stylesheet $_wp(yui)/build/menu/assets/skins/sam/menu.css
+ cgi_stylesheet $_wp(yui)/build/button/assets/skins/sam/button.css
+ # Yahoo UI libraries
+ cgi_script type=text/javascript language="JavaScript" src="$_wp(yui)/build/utilities/utilities.js" {}
+ cgi_script type=text/javascript language="JavaScript" src="$_wp(yui)/build/container/container-min.js" {}
+ cgi_script type=text/javascript language="JavaScript" src="$_wp(yui)/build/datasource/datasource-min.js" {}
+ cgi_script type=text/javascript language="JavaScript" src="$_wp(yui)/build/menu/menu-min.js" {}
+ cgi_script type=text/javascript language="JavaScript" src="$_wp(yui)/build/button/button-min.js" {}
+ # local libraries
+ cgi_script type=text/javascript language="JavaScript" src="lib/common.js" {}
+ cgi_script type=text/javascript language="JavaScript" src="lib/mailbox.js" {}
+ # page specfic JS
+ cgi_javascript {
+ cgi_puts "YAHOO.alpine.cgi_root = '$_wp(serverpath)';"
+ cgi_puts "YAHOO.alpine.app_root = '$_wp(serverpath)/$_wp(appdir)/$_wp(ui2dir)';"
+ cgi_puts "YAHOO.alpine.current.c = $c;"
+ cgi_puts "YAHOO.alpine.current.f = \"$f\";"
+ cgi_puts "YAHOO.alpine.current.u = $u;"
+ cgi_puts "YAHOO.alpine.current.count = $mc;"
+ cgi_puts "YAHOO.alpine.current.selected = [WPCmd PEMailbox selected];"
+ cgi_puts "YAHOO.alpine.current.searched = [WPCmd PEMailbox searched];"
+ cgi_puts "YAHOO.alpine.current.focused = [WPCmd PEMailbox focus];"
+ cgi_puts "function bodyOnLoad() {"
+ cgi_puts " initMenus();"
+ cgi_puts " initMorcButton('viewMorcButton');"
+ cgi_puts " if(YAHOO.env.ua.gecko > 0){ sizeVPHeight(); window.onresize = resizeVPHeight; }"
+ cgi_puts " setCheckMailFunction('gCheck', newMailCheck);"
+ cgi_puts " setNewMailCheckInterval([WPCmd PEInfo inputtimeout]);"
+ wpStatusAndNewmailJavascript
+ wpSaveMenuJavascript "view" $c $f [WPCmd PEFolder defaultcollection] morcInViewDone
+ cgi_puts "}"
+
+ cgi_puts "browserDetect();"
+ }
+ }
+
+ cgi_body class=wap "onLoad=bodyOnLoad()" {
+ cgi_division id="skip" {
+ cgi_put [cgi_url "Skip to Next Message" "#" "onClick=return newMessageText({control:this,parms:{op:'next'}});"]
+ cgi_put [cgi_url "Skip to Message List" "browse"]
+ cgi_put [cgi_url "Skip to Folders" "folders"]
+ cgi_put [cgi_url "Skip to Compose" "compose"]
+ }
+
+ wpCommonPageLayout view $c $f $u [cgi_url "[cgi_quote_html $f], Message $n of $mc" browse/$c/[WPPercentQuote $f $delim] id=gBigContext] [list [cgi_cgi "$_wp(appdir)/$_wp(ui2dir)/browse/${c}/${f}?u=${u}"] "$f" 1 mailboxSearch()] {} {
+ # CONTEXT COMMANDS
+ cgi_division class=hdrBtns {
+ cgi_javascript {
+ cgi_put "if(window.print) document.write('[cgi_buffer {cgi_put [cgi_url "[cgi_span "class=sp hdrBtnImg hbi1" ""][cgi_span "class=hdrBtnText" Print]" "print" "onClick=return printContent()"]}]');"
+ }
+
+ cgi_put [cgi_url "[cgi_span "class=sp hdrBtnImg hbi2" ""][cgi_span "class=hdrBtnText" Settings]" "settings"]
+ cgi_put [cgi_url "[cgi_span "class=sp hdrBtnImg hbi3" ""][cgi_span "class=hdrBtnText" Help]" # "onClick=return openMailboxHelp();" class=wap]
+ cgi_put [cgi_url "[cgi_span "class=sp hdrBtnImg hbi4" ""][cgi_span "class=hdrBtnText" "Sign out"]" "../../session/logout.tcl?cid=[WPCmd PEInfo key]&sessid=${sessid}"]
+ }
+ } {
+ cgi_division id=listTopMenubar "style=\"display: none;\"" {
+ cgi_puts [WPCmd cgi_buffer "drawTopListMenuBar $c {$f}"]
+ }
+ cgi_division id=viewTopMenubar {
+ cgi_puts [WPCmd cgi_buffer "drawTopViewMenuBar $c {$f} $u $n"]
+ }
+ } {
+ cgi_puts [WPCmd cgi_buffer "drawMessageText $c {$f} $u $showimg"]
+ } {
+ cgi_division id=listBottomMenubar "style=\"display: none;\"" {
+ cgi_puts [WPCmd cgi_buffer "drawBottomListMenuBar $c {$f} 0 0 $mc"]
+ }
+ cgi_division id=viewBottomMenubar {
+ cgi_puts [WPCmd cgi_buffer "drawBottomViewMenuBar $c {$f} $u $n $mc"]
+ }
+ }
+
+ # any debugging info to insert?
+ foreach dmsg $dmsgs {
+ cgi_html_comment "DEBUG: $dmsg"
+ cgi_puts ""
+ }
+ }
+ }
+}
diff --git a/web/cgi/alpine/alpine.tcl b/web/cgi/alpine/alpine.tcl
new file mode 120000
index 00000000..5ad8d42f
--- /dev/null
+++ b/web/cgi/alpine/alpine.tcl
@@ -0,0 +1 @@
+../alpine.tcl \ No newline at end of file
diff --git a/web/cgi/alpine/farewell.tcl b/web/cgi/alpine/farewell.tcl
new file mode 100755
index 00000000..e9157bde
--- /dev/null
+++ b/web/cgi/alpine/farewell.tcl
@@ -0,0 +1,40 @@
+#!./tclsh
+# $Id: farewell.tcl 764 2007-10-23 23:44:49Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+
+# Input:
+set fw_vars {
+ {serverid {} 0}
+ {verdir {} "/"}
+}
+
+# Output:
+#
+
+# global config
+source ./alpine.tcl
+
+WPEval $fw_vars {
+ if {[catch {cgi_import logerr}] == 0} {
+ set parms "?logerr=$logerr"
+ } else {
+ set parms ""
+ }
+
+ cgi_http_head {
+ # clear cookies
+ cgi_cookie_set sessid=0 expires=now path=[file join / $verdir]
+
+ cgi_redirect $_wp(serverpath)/session/logout/logout.tcl${parms}
+ }
+}
diff --git a/web/cgi/alpine/tclsh b/web/cgi/alpine/tclsh
new file mode 120000
index 00000000..385fc6c6
--- /dev/null
+++ b/web/cgi/alpine/tclsh
@@ -0,0 +1 @@
+../tclsh \ No newline at end of file
diff --git a/web/cgi/alpine/whackatch.tcl b/web/cgi/alpine/whackatch.tcl
new file mode 100755
index 00000000..cca94724
--- /dev/null
+++ b/web/cgi/alpine/whackatch.tcl
@@ -0,0 +1,46 @@
+#!./tclsh
+# $Id: whackatch.tcl 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# whackatch.tcl
+#
+# Purpose: CGI script to cleanup requested attachment
+
+# Input:
+# ext - attachment file extension
+
+# Output:
+#
+
+# inherit global config
+source ./alpine.tcl
+
+# seconds to pause before rechecking for abandanded attachment files
+set abandoned 300
+
+# no dots allowed
+if {[gets stdin ext] >= 0 && [regexp {^[A-Za-z0-9\-]+$} $ext ext] == 1} {
+
+ set towhack [file join $_wp(fileroot) $_wp(detachpath) detach.${ext}]
+
+ while {1} {
+ set timein [clock seconds]
+
+ after [expr {$abandoned * 1000}]
+
+ if {[catch {file atime $towhack} atime] || ($timein - $atime) > $abandoned} {
+ break
+ }
+ }
+
+ catch {exec /bin/rm -f $towhack}
+}
diff --git a/web/cgi/detach b/web/cgi/detach
new file mode 120000
index 00000000..d1373a3e
--- /dev/null
+++ b/web/cgi/detach
@@ -0,0 +1 @@
+/tmp/webpine \ No newline at end of file
diff --git a/web/cgi/favicon.ico b/web/cgi/favicon.ico
new file mode 100755
index 00000000..2ed8ef56
--- /dev/null
+++ b/web/cgi/favicon.ico
Binary files differ
diff --git a/web/cgi/greeting.tcl b/web/cgi/greeting.tcl
new file mode 100755
index 00000000..c24a65a5
--- /dev/null
+++ b/web/cgi/greeting.tcl
@@ -0,0 +1,21 @@
+#!./tclsh
+
+# ========================================================================
+# Copyright 2006-2007 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+source ./alpine.tcl
+
+cgi_eval {
+
+ cgi_http_head {
+ cgi_redirect [cgi_root]/session/greeting.tcl
+ }
+}
diff --git a/web/cgi/images/Lavender_Chiffon.gif b/web/cgi/images/Lavender_Chiffon.gif
new file mode 100644
index 00000000..19e4d468
--- /dev/null
+++ b/web/cgi/images/Lavender_Chiffon.gif
Binary files differ
diff --git a/web/cgi/images/b_minus.gif b/web/cgi/images/b_minus.gif
new file mode 100644
index 00000000..7b54f750
--- /dev/null
+++ b/web/cgi/images/b_minus.gif
Binary files differ
diff --git a/web/cgi/images/b_plus.gif b/web/cgi/images/b_plus.gif
new file mode 100644
index 00000000..f39a3c01
--- /dev/null
+++ b/web/cgi/images/b_plus.gif
Binary files differ
diff --git a/web/cgi/images/barblank.gif b/web/cgi/images/barblank.gif
new file mode 100644
index 00000000..d98b7b2e
--- /dev/null
+++ b/web/cgi/images/barblank.gif
Binary files differ
diff --git a/web/cgi/images/barclose.gif b/web/cgi/images/barclose.gif
new file mode 100644
index 00000000..f1616283
--- /dev/null
+++ b/web/cgi/images/barclose.gif
Binary files differ
diff --git a/web/cgi/images/barclose_mid.gif b/web/cgi/images/barclose_mid.gif
new file mode 100644
index 00000000..41981b68
--- /dev/null
+++ b/web/cgi/images/barclose_mid.gif
Binary files differ
diff --git a/web/cgi/images/barmsg.gif b/web/cgi/images/barmsg.gif
new file mode 100644
index 00000000..b5d17e1f
--- /dev/null
+++ b/web/cgi/images/barmsg.gif
Binary files differ
diff --git a/web/cgi/images/baropen.gif b/web/cgi/images/baropen.gif
new file mode 100644
index 00000000..49cb3a9b
--- /dev/null
+++ b/web/cgi/images/baropen.gif
Binary files differ
diff --git a/web/cgi/images/baropen_mid.gif b/web/cgi/images/baropen_mid.gif
new file mode 100644
index 00000000..5c02ec12
--- /dev/null
+++ b/web/cgi/images/baropen_mid.gif
Binary files differ
diff --git a/web/cgi/images/barvert.gif b/web/cgi/images/barvert.gif
new file mode 100644
index 00000000..1eed6694
--- /dev/null
+++ b/web/cgi/images/barvert.gif
Binary files differ
diff --git a/web/cgi/images/barvertmsg.gif b/web/cgi/images/barvertmsg.gif
new file mode 100644
index 00000000..243b38b6
--- /dev/null
+++ b/web/cgi/images/barvertmsg.gif
Binary files differ
diff --git a/web/cgi/images/bg_index.gif b/web/cgi/images/bg_index.gif
new file mode 100644
index 00000000..5d0dfeab
--- /dev/null
+++ b/web/cgi/images/bg_index.gif
Binary files differ
diff --git a/web/cgi/images/blackdot.gif b/web/cgi/images/blackdot.gif
new file mode 100644
index 00000000..9e25328f
--- /dev/null
+++ b/web/cgi/images/blackdot.gif
Binary files differ
diff --git a/web/cgi/images/book.gif b/web/cgi/images/book.gif
new file mode 100644
index 00000000..70ce6d5c
--- /dev/null
+++ b/web/cgi/images/book.gif
Binary files differ
diff --git a/web/cgi/images/but_abook.gif b/web/cgi/images/but_abook.gif
new file mode 100644
index 00000000..5155f338
--- /dev/null
+++ b/web/cgi/images/but_abook.gif
Binary files differ
diff --git a/web/cgi/images/but_cancel.gif b/web/cgi/images/but_cancel.gif
new file mode 100644
index 00000000..6a4f7806
--- /dev/null
+++ b/web/cgi/images/but_cancel.gif
Binary files differ
diff --git a/web/cgi/images/but_create.gif b/web/cgi/images/but_create.gif
new file mode 100644
index 00000000..4bea057b
--- /dev/null
+++ b/web/cgi/images/but_create.gif
Binary files differ
diff --git a/web/cgi/images/but_folddel.gif b/web/cgi/images/but_folddel.gif
new file mode 100644
index 00000000..c44e23e7
--- /dev/null
+++ b/web/cgi/images/but_folddel.gif
Binary files differ
diff --git a/web/cgi/images/but_foldexp.gif b/web/cgi/images/but_foldexp.gif
new file mode 100644
index 00000000..64e8842c
--- /dev/null
+++ b/web/cgi/images/but_foldexp.gif
Binary files differ
diff --git a/web/cgi/images/but_foldren.gif b/web/cgi/images/but_foldren.gif
new file mode 100644
index 00000000..5374e9a2
--- /dev/null
+++ b/web/cgi/images/but_foldren.gif
Binary files differ
diff --git a/web/cgi/images/but_remove.gif b/web/cgi/images/but_remove.gif
new file mode 100644
index 00000000..d08ac475
--- /dev/null
+++ b/web/cgi/images/but_remove.gif
Binary files differ
diff --git a/web/cgi/images/but_resume.gif b/web/cgi/images/but_resume.gif
new file mode 100644
index 00000000..8b7df00c
--- /dev/null
+++ b/web/cgi/images/but_resume.gif
Binary files differ
diff --git a/web/cgi/images/but_rnd_block.gif b/web/cgi/images/but_rnd_block.gif
new file mode 100644
index 00000000..dae0aa9f
--- /dev/null
+++ b/web/cgi/images/but_rnd_block.gif
Binary files differ
diff --git a/web/cgi/images/but_rnd_first3.gif b/web/cgi/images/but_rnd_first3.gif
new file mode 100644
index 00000000..cf2e8bf6
--- /dev/null
+++ b/web/cgi/images/but_rnd_first3.gif
Binary files differ
diff --git a/web/cgi/images/but_rnd_last3.gif b/web/cgi/images/but_rnd_last3.gif
new file mode 100644
index 00000000..f21a5e14
--- /dev/null
+++ b/web/cgi/images/but_rnd_last3.gif
Binary files differ
diff --git a/web/cgi/images/but_rnd_next3.gif b/web/cgi/images/but_rnd_next3.gif
new file mode 100644
index 00000000..bb303abf
--- /dev/null
+++ b/web/cgi/images/but_rnd_next3.gif
Binary files differ
diff --git a/web/cgi/images/but_rnd_prev3.gif b/web/cgi/images/but_rnd_prev3.gif
new file mode 100644
index 00000000..54c5c5a8
--- /dev/null
+++ b/web/cgi/images/but_rnd_prev3.gif
Binary files differ
diff --git a/web/cgi/images/but_s_do.gif b/web/cgi/images/but_s_do.gif
new file mode 100644
index 00000000..5bfb78d9
--- /dev/null
+++ b/web/cgi/images/but_s_do.gif
Binary files differ
diff --git a/web/cgi/images/but_save.gif b/web/cgi/images/but_save.gif
new file mode 100644
index 00000000..ef80c73a
--- /dev/null
+++ b/web/cgi/images/but_save.gif
Binary files differ
diff --git a/web/cgi/images/buttons/silver/allmsgs.gif b/web/cgi/images/buttons/silver/allmsgs.gif
new file mode 100644
index 00000000..6a8f6ecb
--- /dev/null
+++ b/web/cgi/images/buttons/silver/allmsgs.gif
Binary files differ
diff --git a/web/cgi/images/buttons/silver/attach.gif b/web/cgi/images/buttons/silver/attach.gif
new file mode 100644
index 00000000..a99063e7
--- /dev/null
+++ b/web/cgi/images/buttons/silver/attach.gif
Binary files differ
diff --git a/web/cgi/images/buttons/silver/cancel.gif b/web/cgi/images/buttons/silver/cancel.gif
new file mode 100644
index 00000000..80680cbc
--- /dev/null
+++ b/web/cgi/images/buttons/silver/cancel.gif
Binary files differ
diff --git a/web/cgi/images/buttons/silver/compose.gif b/web/cgi/images/buttons/silver/compose.gif
new file mode 100644
index 00000000..98795bce
--- /dev/null
+++ b/web/cgi/images/buttons/silver/compose.gif
Binary files differ
diff --git a/web/cgi/images/buttons/silver/compose8.gif b/web/cgi/images/buttons/silver/compose8.gif
new file mode 100644
index 00000000..99b3e233
--- /dev/null
+++ b/web/cgi/images/buttons/silver/compose8.gif
Binary files differ
diff --git a/web/cgi/images/buttons/silver/config.gif b/web/cgi/images/buttons/silver/config.gif
new file mode 100644
index 00000000..3f2373fb
--- /dev/null
+++ b/web/cgi/images/buttons/silver/config.gif
Binary files differ
diff --git a/web/cgi/images/buttons/silver/delete.gif b/web/cgi/images/buttons/silver/delete.gif
new file mode 100644
index 00000000..e7c87799
--- /dev/null
+++ b/web/cgi/images/buttons/silver/delete.gif
Binary files differ
diff --git a/web/cgi/images/buttons/silver/delete2.gif b/web/cgi/images/buttons/silver/delete2.gif
new file mode 100644
index 00000000..a88a5443
--- /dev/null
+++ b/web/cgi/images/buttons/silver/delete2.gif
Binary files differ
diff --git a/web/cgi/images/buttons/silver/done.gif b/web/cgi/images/buttons/silver/done.gif
new file mode 100644
index 00000000..fad888f7
--- /dev/null
+++ b/web/cgi/images/buttons/silver/done.gif
Binary files differ
diff --git a/web/cgi/images/buttons/silver/exit.gif b/web/cgi/images/buttons/silver/exit.gif
new file mode 100644
index 00000000..31f406fb
--- /dev/null
+++ b/web/cgi/images/buttons/silver/exit.gif
Binary files differ
diff --git a/web/cgi/images/buttons/silver/expunge.gif b/web/cgi/images/buttons/silver/expunge.gif
new file mode 100644
index 00000000..33c52f02
--- /dev/null
+++ b/web/cgi/images/buttons/silver/expunge.gif
Binary files differ
diff --git a/web/cgi/images/buttons/silver/firstpage.gif b/web/cgi/images/buttons/silver/firstpage.gif
new file mode 100644
index 00000000..775203b0
--- /dev/null
+++ b/web/cgi/images/buttons/silver/firstpage.gif
Binary files differ
diff --git a/web/cgi/images/buttons/silver/firstpage2.gif b/web/cgi/images/buttons/silver/firstpage2.gif
new file mode 100644
index 00000000..6f8e0b5b
--- /dev/null
+++ b/web/cgi/images/buttons/silver/firstpage2.gif
Binary files differ
diff --git a/web/cgi/images/buttons/silver/firstpage4.gif b/web/cgi/images/buttons/silver/firstpage4.gif
new file mode 100644
index 00000000..6ea0c89a
--- /dev/null
+++ b/web/cgi/images/buttons/silver/firstpage4.gif
Binary files differ
diff --git a/web/cgi/images/buttons/silver/folders.gif b/web/cgi/images/buttons/silver/folders.gif
new file mode 100644
index 00000000..abaeaac7
--- /dev/null
+++ b/web/cgi/images/buttons/silver/folders.gif
Binary files differ
diff --git a/web/cgi/images/buttons/silver/forward.gif b/web/cgi/images/buttons/silver/forward.gif
new file mode 100644
index 00000000..c2196e6b
--- /dev/null
+++ b/web/cgi/images/buttons/silver/forward.gif
Binary files differ
diff --git a/web/cgi/images/buttons/silver/lastpage.gif b/web/cgi/images/buttons/silver/lastpage.gif
new file mode 100644
index 00000000..23811d8b
--- /dev/null
+++ b/web/cgi/images/buttons/silver/lastpage.gif
Binary files differ
diff --git a/web/cgi/images/buttons/silver/lastpage2.gif b/web/cgi/images/buttons/silver/lastpage2.gif
new file mode 100644
index 00000000..39d8a733
--- /dev/null
+++ b/web/cgi/images/buttons/silver/lastpage2.gif
Binary files differ
diff --git a/web/cgi/images/buttons/silver/lastpage4.gif b/web/cgi/images/buttons/silver/lastpage4.gif
new file mode 100644
index 00000000..3f69c059
--- /dev/null
+++ b/web/cgi/images/buttons/silver/lastpage4.gif
Binary files differ
diff --git a/web/cgi/images/buttons/silver/logout.gif b/web/cgi/images/buttons/silver/logout.gif
new file mode 100644
index 00000000..8a1fb6fd
--- /dev/null
+++ b/web/cgi/images/buttons/silver/logout.gif
Binary files differ
diff --git a/web/cgi/images/buttons/silver/messages.gif b/web/cgi/images/buttons/silver/messages.gif
new file mode 100644
index 00000000..0e3543f2
--- /dev/null
+++ b/web/cgi/images/buttons/silver/messages.gif
Binary files differ
diff --git a/web/cgi/images/buttons/silver/nextpage.gif b/web/cgi/images/buttons/silver/nextpage.gif
new file mode 100644
index 00000000..370aafc9
--- /dev/null
+++ b/web/cgi/images/buttons/silver/nextpage.gif
Binary files differ
diff --git a/web/cgi/images/buttons/silver/nextpage2.gif b/web/cgi/images/buttons/silver/nextpage2.gif
new file mode 100644
index 00000000..971ba494
--- /dev/null
+++ b/web/cgi/images/buttons/silver/nextpage2.gif
Binary files differ
diff --git a/web/cgi/images/buttons/silver/ok.gif b/web/cgi/images/buttons/silver/ok.gif
new file mode 100644
index 00000000..470db959
--- /dev/null
+++ b/web/cgi/images/buttons/silver/ok.gif
Binary files differ
diff --git a/web/cgi/images/buttons/silver/prevpage.gif b/web/cgi/images/buttons/silver/prevpage.gif
new file mode 100644
index 00000000..2e312c3c
--- /dev/null
+++ b/web/cgi/images/buttons/silver/prevpage.gif
Binary files differ
diff --git a/web/cgi/images/buttons/silver/prevpage2.gif b/web/cgi/images/buttons/silver/prevpage2.gif
new file mode 100644
index 00000000..2b8a1116
--- /dev/null
+++ b/web/cgi/images/buttons/silver/prevpage2.gif
Binary files differ
diff --git a/web/cgi/images/buttons/silver/quit.gif b/web/cgi/images/buttons/silver/quit.gif
new file mode 100644
index 00000000..7add5b38
--- /dev/null
+++ b/web/cgi/images/buttons/silver/quit.gif
Binary files differ
diff --git a/web/cgi/images/buttons/silver/reply.gif b/web/cgi/images/buttons/silver/reply.gif
new file mode 100644
index 00000000..2b8de31c
--- /dev/null
+++ b/web/cgi/images/buttons/silver/reply.gif
Binary files differ
diff --git a/web/cgi/images/buttons/silver/replyall.gif b/web/cgi/images/buttons/silver/replyall.gif
new file mode 100644
index 00000000..16a352db
--- /dev/null
+++ b/web/cgi/images/buttons/silver/replyall.gif
Binary files differ
diff --git a/web/cgi/images/buttons/silver/save.gif b/web/cgi/images/buttons/silver/save.gif
new file mode 100644
index 00000000..b09e5c0d
--- /dev/null
+++ b/web/cgi/images/buttons/silver/save.gif
Binary files differ
diff --git a/web/cgi/images/buttons/silver/send.gif b/web/cgi/images/buttons/silver/send.gif
new file mode 100644
index 00000000..b4ccfb33
--- /dev/null
+++ b/web/cgi/images/buttons/silver/send.gif
Binary files differ
diff --git a/web/cgi/images/buttons/silver/undelete.gif b/web/cgi/images/buttons/silver/undelete.gif
new file mode 100644
index 00000000..0c585b63
--- /dev/null
+++ b/web/cgi/images/buttons/silver/undelete.gif
Binary files differ
diff --git a/web/cgi/images/caution.gif b/web/cgi/images/caution.gif
new file mode 100644
index 00000000..9d55a384
--- /dev/null
+++ b/web/cgi/images/caution.gif
Binary files differ
diff --git a/web/cgi/images/cf_add.gif b/web/cgi/images/cf_add.gif
new file mode 100644
index 00000000..c3c228a6
--- /dev/null
+++ b/web/cgi/images/cf_add.gif
Binary files differ
diff --git a/web/cgi/images/cf_delete.gif b/web/cgi/images/cf_delete.gif
new file mode 100644
index 00000000..7b3c9a39
--- /dev/null
+++ b/web/cgi/images/cf_delete.gif
Binary files differ
diff --git a/web/cgi/images/cf_edit.gif b/web/cgi/images/cf_edit.gif
new file mode 100644
index 00000000..094c4109
--- /dev/null
+++ b/web/cgi/images/cf_edit.gif
Binary files differ
diff --git a/web/cgi/images/cf_help.gif b/web/cgi/images/cf_help.gif
new file mode 100644
index 00000000..678d6d64
--- /dev/null
+++ b/web/cgi/images/cf_help.gif
Binary files differ
diff --git a/web/cgi/images/cf_shdown.gif b/web/cgi/images/cf_shdown.gif
new file mode 100644
index 00000000..162e93c9
--- /dev/null
+++ b/web/cgi/images/cf_shdown.gif
Binary files differ
diff --git a/web/cgi/images/cf_shup.gif b/web/cgi/images/cf_shup.gif
new file mode 100644
index 00000000..a2acbbd5
--- /dev/null
+++ b/web/cgi/images/cf_shup.gif
Binary files differ
diff --git a/web/cgi/images/decreas4.gif b/web/cgi/images/decreas4.gif
new file mode 100644
index 00000000..9ac0fedb
--- /dev/null
+++ b/web/cgi/images/decreas4.gif
Binary files differ
diff --git a/web/cgi/images/dot.gif b/web/cgi/images/dot.gif
new file mode 100644
index 00000000..a4f37d7e
--- /dev/null
+++ b/web/cgi/images/dot.gif
Binary files differ
diff --git a/web/cgi/images/dot2.gif b/web/cgi/images/dot2.gif
new file mode 100644
index 00000000..e565824a
--- /dev/null
+++ b/web/cgi/images/dot2.gif
Binary files differ
diff --git a/web/cgi/images/dotblink.gif b/web/cgi/images/dotblink.gif
new file mode 100644
index 00000000..500f0bc0
--- /dev/null
+++ b/web/cgi/images/dotblink.gif
Binary files differ
diff --git a/web/cgi/images/dstripe.gif b/web/cgi/images/dstripe.gif
new file mode 100644
index 00000000..6adf1bcb
--- /dev/null
+++ b/web/cgi/images/dstripe.gif
Binary files differ
diff --git a/web/cgi/images/env/d_new.gif b/web/cgi/images/env/d_new.gif
new file mode 100644
index 00000000..f9766a80
--- /dev/null
+++ b/web/cgi/images/env/d_new.gif
Binary files differ
diff --git a/web/cgi/images/env/d_newans.gif b/web/cgi/images/env/d_newans.gif
new file mode 100644
index 00000000..d90c8509
--- /dev/null
+++ b/web/cgi/images/env/d_newans.gif
Binary files differ
diff --git a/web/cgi/images/env/d_newansdel.gif b/web/cgi/images/env/d_newansdel.gif
new file mode 100644
index 00000000..d735bb14
--- /dev/null
+++ b/web/cgi/images/env/d_newansdel.gif
Binary files differ
diff --git a/web/cgi/images/env/d_newdel.gif b/web/cgi/images/env/d_newdel.gif
new file mode 100644
index 00000000..2f758bd5
--- /dev/null
+++ b/web/cgi/images/env/d_newdel.gif
Binary files differ
diff --git a/web/cgi/images/env/d_newimp.gif b/web/cgi/images/env/d_newimp.gif
new file mode 100644
index 00000000..10510595
--- /dev/null
+++ b/web/cgi/images/env/d_newimp.gif
Binary files differ
diff --git a/web/cgi/images/env/d_newimpans.gif b/web/cgi/images/env/d_newimpans.gif
new file mode 100644
index 00000000..0fa8b0c0
--- /dev/null
+++ b/web/cgi/images/env/d_newimpans.gif
Binary files differ
diff --git a/web/cgi/images/env/d_newimpansdel.gif b/web/cgi/images/env/d_newimpansdel.gif
new file mode 100644
index 00000000..872a80d9
--- /dev/null
+++ b/web/cgi/images/env/d_newimpansdel.gif
Binary files differ
diff --git a/web/cgi/images/env/d_newimpdel.gif b/web/cgi/images/env/d_newimpdel.gif
new file mode 100644
index 00000000..dd92dc26
--- /dev/null
+++ b/web/cgi/images/env/d_newimpdel.gif
Binary files differ
diff --git a/web/cgi/images/env/d_newyou.gif b/web/cgi/images/env/d_newyou.gif
new file mode 100644
index 00000000..c227e684
--- /dev/null
+++ b/web/cgi/images/env/d_newyou.gif
Binary files differ
diff --git a/web/cgi/images/env/d_newyouans.gif b/web/cgi/images/env/d_newyouans.gif
new file mode 100644
index 00000000..9f8c24c1
--- /dev/null
+++ b/web/cgi/images/env/d_newyouans.gif
Binary files differ
diff --git a/web/cgi/images/env/d_newyouansdel.gif b/web/cgi/images/env/d_newyouansdel.gif
new file mode 100644
index 00000000..d922a6a3
--- /dev/null
+++ b/web/cgi/images/env/d_newyouansdel.gif
Binary files differ
diff --git a/web/cgi/images/env/d_newyoudel.gif b/web/cgi/images/env/d_newyoudel.gif
new file mode 100644
index 00000000..8ac883c2
--- /dev/null
+++ b/web/cgi/images/env/d_newyoudel.gif
Binary files differ
diff --git a/web/cgi/images/env/d_read.gif b/web/cgi/images/env/d_read.gif
new file mode 100644
index 00000000..2b9aa56e
--- /dev/null
+++ b/web/cgi/images/env/d_read.gif
Binary files differ
diff --git a/web/cgi/images/env/d_readans.gif b/web/cgi/images/env/d_readans.gif
new file mode 100644
index 00000000..dd9b80ae
--- /dev/null
+++ b/web/cgi/images/env/d_readans.gif
Binary files differ
diff --git a/web/cgi/images/env/d_readansdel.gif b/web/cgi/images/env/d_readansdel.gif
new file mode 100644
index 00000000..c98d2395
--- /dev/null
+++ b/web/cgi/images/env/d_readansdel.gif
Binary files differ
diff --git a/web/cgi/images/env/d_readdel.gif b/web/cgi/images/env/d_readdel.gif
new file mode 100644
index 00000000..a922f923
--- /dev/null
+++ b/web/cgi/images/env/d_readdel.gif
Binary files differ
diff --git a/web/cgi/images/env/d_readimp.gif b/web/cgi/images/env/d_readimp.gif
new file mode 100644
index 00000000..81015d7b
--- /dev/null
+++ b/web/cgi/images/env/d_readimp.gif
Binary files differ
diff --git a/web/cgi/images/env/d_readimpans.gif b/web/cgi/images/env/d_readimpans.gif
new file mode 100644
index 00000000..d8bfcb89
--- /dev/null
+++ b/web/cgi/images/env/d_readimpans.gif
Binary files differ
diff --git a/web/cgi/images/env/d_readimpansdel.gif b/web/cgi/images/env/d_readimpansdel.gif
new file mode 100644
index 00000000..02342edd
--- /dev/null
+++ b/web/cgi/images/env/d_readimpansdel.gif
Binary files differ
diff --git a/web/cgi/images/env/d_readimpdel.gif b/web/cgi/images/env/d_readimpdel.gif
new file mode 100644
index 00000000..0d66fcbd
--- /dev/null
+++ b/web/cgi/images/env/d_readimpdel.gif
Binary files differ
diff --git a/web/cgi/images/env/d_readyou.gif b/web/cgi/images/env/d_readyou.gif
new file mode 100644
index 00000000..8dc32802
--- /dev/null
+++ b/web/cgi/images/env/d_readyou.gif
Binary files differ
diff --git a/web/cgi/images/env/d_readyouans.gif b/web/cgi/images/env/d_readyouans.gif
new file mode 100644
index 00000000..dab41d34
--- /dev/null
+++ b/web/cgi/images/env/d_readyouans.gif
Binary files differ
diff --git a/web/cgi/images/env/d_readyouansdel.gif b/web/cgi/images/env/d_readyouansdel.gif
new file mode 100644
index 00000000..98a9b4ae
--- /dev/null
+++ b/web/cgi/images/env/d_readyouansdel.gif
Binary files differ
diff --git a/web/cgi/images/env/d_readyoudel.gif b/web/cgi/images/env/d_readyoudel.gif
new file mode 100644
index 00000000..646c2b4f
--- /dev/null
+++ b/web/cgi/images/env/d_readyoudel.gif
Binary files differ
diff --git a/web/cgi/images/env/new.gif b/web/cgi/images/env/new.gif
new file mode 100644
index 00000000..a34257ed
--- /dev/null
+++ b/web/cgi/images/env/new.gif
Binary files differ
diff --git a/web/cgi/images/env/newans.gif b/web/cgi/images/env/newans.gif
new file mode 100644
index 00000000..e43c377b
--- /dev/null
+++ b/web/cgi/images/env/newans.gif
Binary files differ
diff --git a/web/cgi/images/env/newansdel.gif b/web/cgi/images/env/newansdel.gif
new file mode 100644
index 00000000..ecfbaa83
--- /dev/null
+++ b/web/cgi/images/env/newansdel.gif
Binary files differ
diff --git a/web/cgi/images/env/newdel.gif b/web/cgi/images/env/newdel.gif
new file mode 100644
index 00000000..c1c2f02c
--- /dev/null
+++ b/web/cgi/images/env/newdel.gif
Binary files differ
diff --git a/web/cgi/images/env/newimp.gif b/web/cgi/images/env/newimp.gif
new file mode 100644
index 00000000..12aaf644
--- /dev/null
+++ b/web/cgi/images/env/newimp.gif
Binary files differ
diff --git a/web/cgi/images/env/newimpans.gif b/web/cgi/images/env/newimpans.gif
new file mode 100644
index 00000000..1ae6db2e
--- /dev/null
+++ b/web/cgi/images/env/newimpans.gif
Binary files differ
diff --git a/web/cgi/images/env/newimpansdel.gif b/web/cgi/images/env/newimpansdel.gif
new file mode 100644
index 00000000..df986427
--- /dev/null
+++ b/web/cgi/images/env/newimpansdel.gif
Binary files differ
diff --git a/web/cgi/images/env/newimpdel.gif b/web/cgi/images/env/newimpdel.gif
new file mode 100644
index 00000000..8eff245f
--- /dev/null
+++ b/web/cgi/images/env/newimpdel.gif
Binary files differ
diff --git a/web/cgi/images/env/newyou.gif b/web/cgi/images/env/newyou.gif
new file mode 100644
index 00000000..d376ece8
--- /dev/null
+++ b/web/cgi/images/env/newyou.gif
Binary files differ
diff --git a/web/cgi/images/env/newyouans.gif b/web/cgi/images/env/newyouans.gif
new file mode 100644
index 00000000..f9afe98a
--- /dev/null
+++ b/web/cgi/images/env/newyouans.gif
Binary files differ
diff --git a/web/cgi/images/env/newyouansdel.gif b/web/cgi/images/env/newyouansdel.gif
new file mode 100644
index 00000000..bca5ab7e
--- /dev/null
+++ b/web/cgi/images/env/newyouansdel.gif
Binary files differ
diff --git a/web/cgi/images/env/newyoudel.gif b/web/cgi/images/env/newyoudel.gif
new file mode 100644
index 00000000..5f3305ff
--- /dev/null
+++ b/web/cgi/images/env/newyoudel.gif
Binary files differ
diff --git a/web/cgi/images/env/read.gif b/web/cgi/images/env/read.gif
new file mode 100644
index 00000000..b5325e06
--- /dev/null
+++ b/web/cgi/images/env/read.gif
Binary files differ
diff --git a/web/cgi/images/env/readans.gif b/web/cgi/images/env/readans.gif
new file mode 100644
index 00000000..29e08915
--- /dev/null
+++ b/web/cgi/images/env/readans.gif
Binary files differ
diff --git a/web/cgi/images/env/readansdel.gif b/web/cgi/images/env/readansdel.gif
new file mode 100644
index 00000000..d74cac4c
--- /dev/null
+++ b/web/cgi/images/env/readansdel.gif
Binary files differ
diff --git a/web/cgi/images/env/readdel.gif b/web/cgi/images/env/readdel.gif
new file mode 100644
index 00000000..9fa3f1d3
--- /dev/null
+++ b/web/cgi/images/env/readdel.gif
Binary files differ
diff --git a/web/cgi/images/env/readimp.gif b/web/cgi/images/env/readimp.gif
new file mode 100644
index 00000000..c202ce57
--- /dev/null
+++ b/web/cgi/images/env/readimp.gif
Binary files differ
diff --git a/web/cgi/images/env/readimpans.gif b/web/cgi/images/env/readimpans.gif
new file mode 100644
index 00000000..8658dbe5
--- /dev/null
+++ b/web/cgi/images/env/readimpans.gif
Binary files differ
diff --git a/web/cgi/images/env/readimpansdel.gif b/web/cgi/images/env/readimpansdel.gif
new file mode 100644
index 00000000..c0adfd9d
--- /dev/null
+++ b/web/cgi/images/env/readimpansdel.gif
Binary files differ
diff --git a/web/cgi/images/env/readimpdel.gif b/web/cgi/images/env/readimpdel.gif
new file mode 100644
index 00000000..16e97f72
--- /dev/null
+++ b/web/cgi/images/env/readimpdel.gif
Binary files differ
diff --git a/web/cgi/images/env/readyou.border.gif b/web/cgi/images/env/readyou.border.gif
new file mode 100644
index 00000000..7f0f4a7c
--- /dev/null
+++ b/web/cgi/images/env/readyou.border.gif
Binary files differ
diff --git a/web/cgi/images/env/readyou.gif b/web/cgi/images/env/readyou.gif
new file mode 100644
index 00000000..e1587077
--- /dev/null
+++ b/web/cgi/images/env/readyou.gif
Binary files differ
diff --git a/web/cgi/images/env/readyouans.gif b/web/cgi/images/env/readyouans.gif
new file mode 100644
index 00000000..8a71bf4a
--- /dev/null
+++ b/web/cgi/images/env/readyouans.gif
Binary files differ
diff --git a/web/cgi/images/env/readyouansdel.gif b/web/cgi/images/env/readyouansdel.gif
new file mode 100644
index 00000000..d59d4a6a
--- /dev/null
+++ b/web/cgi/images/env/readyouansdel.gif
Binary files differ
diff --git a/web/cgi/images/env/readyoudel.gif b/web/cgi/images/env/readyoudel.gif
new file mode 100644
index 00000000..c1718d1d
--- /dev/null
+++ b/web/cgi/images/env/readyoudel.gif
Binary files differ
diff --git a/web/cgi/images/hdr.gif b/web/cgi/images/hdr.gif
new file mode 100644
index 00000000..25432375
--- /dev/null
+++ b/web/cgi/images/hdr.gif
Binary files differ
diff --git a/web/cgi/images/hdrless.gif b/web/cgi/images/hdrless.gif
new file mode 100644
index 00000000..dc6f1279
--- /dev/null
+++ b/web/cgi/images/hdrless.gif
Binary files differ
diff --git a/web/cgi/images/hdrmore.gif b/web/cgi/images/hdrmore.gif
new file mode 100644
index 00000000..ce451397
--- /dev/null
+++ b/web/cgi/images/hdrmore.gif
Binary files differ
diff --git a/web/cgi/images/hdrnon.gif b/web/cgi/images/hdrnon.gif
new file mode 100644
index 00000000..26d60632
--- /dev/null
+++ b/web/cgi/images/hdrnon.gif
Binary files differ
diff --git a/web/cgi/images/help_trans.gif b/web/cgi/images/help_trans.gif
new file mode 100644
index 00000000..d2043df4
--- /dev/null
+++ b/web/cgi/images/help_trans.gif
Binary files differ
diff --git a/web/cgi/images/ibarmsg.gif b/web/cgi/images/ibarmsg.gif
new file mode 100644
index 00000000..ba5a25f9
--- /dev/null
+++ b/web/cgi/images/ibarmsg.gif
Binary files differ
diff --git a/web/cgi/images/ibarvertmsg.gif b/web/cgi/images/ibarvertmsg.gif
new file mode 100644
index 00000000..4cf3fb4d
--- /dev/null
+++ b/web/cgi/images/ibarvertmsg.gif
Binary files differ
diff --git a/web/cgi/images/if_blank.gif b/web/cgi/images/if_blank.gif
new file mode 100644
index 00000000..54a6a152
--- /dev/null
+++ b/web/cgi/images/if_blank.gif
Binary files differ
diff --git a/web/cgi/images/if_left.gif b/web/cgi/images/if_left.gif
new file mode 100644
index 00000000..7d750037
--- /dev/null
+++ b/web/cgi/images/if_left.gif
Binary files differ
diff --git a/web/cgi/images/if_narrow.gif b/web/cgi/images/if_narrow.gif
new file mode 100644
index 00000000..e0ef6ec7
--- /dev/null
+++ b/web/cgi/images/if_narrow.gif
Binary files differ
diff --git a/web/cgi/images/if_narrow2.gif b/web/cgi/images/if_narrow2.gif
new file mode 100644
index 00000000..141e7946
--- /dev/null
+++ b/web/cgi/images/if_narrow2.gif
Binary files differ
diff --git a/web/cgi/images/if_remove.gif b/web/cgi/images/if_remove.gif
new file mode 100644
index 00000000..f4f74548
--- /dev/null
+++ b/web/cgi/images/if_remove.gif
Binary files differ
diff --git a/web/cgi/images/if_right.gif b/web/cgi/images/if_right.gif
new file mode 100644
index 00000000..d2490ff6
--- /dev/null
+++ b/web/cgi/images/if_right.gif
Binary files differ
diff --git a/web/cgi/images/if_widen.gif b/web/cgi/images/if_widen.gif
new file mode 100644
index 00000000..5cc84849
--- /dev/null
+++ b/web/cgi/images/if_widen.gif
Binary files differ
diff --git a/web/cgi/images/if_wider.gif b/web/cgi/images/if_wider.gif
new file mode 100644
index 00000000..dc39da1a
--- /dev/null
+++ b/web/cgi/images/if_wider.gif
Binary files differ
diff --git a/web/cgi/images/increas4.gif b/web/cgi/images/increas4.gif
new file mode 100644
index 00000000..8cb8e232
--- /dev/null
+++ b/web/cgi/images/increas4.gif
Binary files differ
diff --git a/web/cgi/images/indexhdr.gif b/web/cgi/images/indexhdr.gif
new file mode 100644
index 00000000..0463a80d
--- /dev/null
+++ b/web/cgi/images/indexhdr.gif
Binary files differ
diff --git a/web/cgi/images/logo/alpine/back.gif b/web/cgi/images/logo/alpine/back.gif
new file mode 100644
index 00000000..cab310df
--- /dev/null
+++ b/web/cgi/images/logo/alpine/back.gif
Binary files differ
diff --git a/web/cgi/images/logo/alpine/big.gif b/web/cgi/images/logo/alpine/big.gif
new file mode 100644
index 00000000..6d4862ee
--- /dev/null
+++ b/web/cgi/images/logo/alpine/big.gif
Binary files differ
diff --git a/web/cgi/images/logo/alpine/small-blank.gif b/web/cgi/images/logo/alpine/small-blank.gif
new file mode 100644
index 00000000..41868d0a
--- /dev/null
+++ b/web/cgi/images/logo/alpine/small-blank.gif
Binary files differ
diff --git a/web/cgi/images/logo/alpine/small.gif b/web/cgi/images/logo/alpine/small.gif
new file mode 100644
index 00000000..af845545
--- /dev/null
+++ b/web/cgi/images/logo/alpine/small.gif
Binary files differ
diff --git a/web/cgi/images/markall3.gif b/web/cgi/images/markall3.gif
new file mode 100644
index 00000000..15b2b82d
--- /dev/null
+++ b/web/cgi/images/markall3.gif
Binary files differ
diff --git a/web/cgi/images/marknone3.gif b/web/cgi/images/marknone3.gif
new file mode 100644
index 00000000..b8845863
--- /dev/null
+++ b/web/cgi/images/marknone3.gif
Binary files differ
diff --git a/web/cgi/images/minus2.gif b/web/cgi/images/minus2.gif
new file mode 100644
index 00000000..928ec8f5
--- /dev/null
+++ b/web/cgi/images/minus2.gif
Binary files differ
diff --git a/web/cgi/images/nondither10x10.gif b/web/cgi/images/nondither10x10.gif
new file mode 100644
index 00000000..cea4b5cf
--- /dev/null
+++ b/web/cgi/images/nondither10x10.gif
Binary files differ
diff --git a/web/cgi/images/plus2.gif b/web/cgi/images/plus2.gif
new file mode 100644
index 00000000..be20fa4b
--- /dev/null
+++ b/web/cgi/images/plus2.gif
Binary files differ
diff --git a/web/cgi/images/postmark.gif b/web/cgi/images/postmark.gif
new file mode 100644
index 00000000..dd384564
--- /dev/null
+++ b/web/cgi/images/postmark.gif
Binary files differ
diff --git a/web/cgi/images/printer2.gif b/web/cgi/images/printer2.gif
new file mode 100644
index 00000000..d752ed33
--- /dev/null
+++ b/web/cgi/images/printer2.gif
Binary files differ
diff --git a/web/cgi/images/slidein.gif b/web/cgi/images/slidein.gif
new file mode 100644
index 00000000..7f5ceefa
--- /dev/null
+++ b/web/cgi/images/slidein.gif
Binary files differ
diff --git a/web/cgi/images/slideout.gif b/web/cgi/images/slideout.gif
new file mode 100644
index 00000000..1ccd330f
--- /dev/null
+++ b/web/cgi/images/slideout.gif
Binary files differ
diff --git a/web/cgi/images/tabless.gif b/web/cgi/images/tabless.gif
new file mode 100644
index 00000000..9f923935
--- /dev/null
+++ b/web/cgi/images/tabless.gif
Binary files differ
diff --git a/web/cgi/images/tabmore.gif b/web/cgi/images/tabmore.gif
new file mode 100644
index 00000000..97f21212
--- /dev/null
+++ b/web/cgi/images/tabmore.gif
Binary files differ
diff --git a/web/cgi/images/tabs/abdtab.gif b/web/cgi/images/tabs/abdtab.gif
new file mode 100644
index 00000000..1e464fc2
--- /dev/null
+++ b/web/cgi/images/tabs/abdtab.gif
Binary files differ
diff --git a/web/cgi/images/tabs/abtab.gif b/web/cgi/images/tabs/abtab.gif
new file mode 100644
index 00000000..b60728a0
--- /dev/null
+++ b/web/cgi/images/tabs/abtab.gif
Binary files differ
diff --git a/web/cgi/images/tabs/blank.gif b/web/cgi/images/tabs/blank.gif
new file mode 100644
index 00000000..8079c962
--- /dev/null
+++ b/web/cgi/images/tabs/blank.gif
Binary files differ
diff --git a/web/cgi/images/tabs/cdtab.gif b/web/cgi/images/tabs/cdtab.gif
new file mode 100644
index 00000000..d55eb0ae
--- /dev/null
+++ b/web/cgi/images/tabs/cdtab.gif
Binary files differ
diff --git a/web/cgi/images/tabs/ctab.gif b/web/cgi/images/tabs/ctab.gif
new file mode 100644
index 00000000..53a1b1f3
--- /dev/null
+++ b/web/cgi/images/tabs/ctab.gif
Binary files differ
diff --git a/web/cgi/images/tabs/fdtab.gif b/web/cgi/images/tabs/fdtab.gif
new file mode 100644
index 00000000..bcbaf36c
--- /dev/null
+++ b/web/cgi/images/tabs/fdtab.gif
Binary files differ
diff --git a/web/cgi/images/tabs/ftab.gif b/web/cgi/images/tabs/ftab.gif
new file mode 100644
index 00000000..163c9f39
--- /dev/null
+++ b/web/cgi/images/tabs/ftab.gif
Binary files differ
diff --git a/web/cgi/images/tabs/gdtab.gif b/web/cgi/images/tabs/gdtab.gif
new file mode 100644
index 00000000..f4651a54
--- /dev/null
+++ b/web/cgi/images/tabs/gdtab.gif
Binary files differ
diff --git a/web/cgi/images/tabs/gtab.gif b/web/cgi/images/tabs/gtab.gif
new file mode 100644
index 00000000..152280cd
--- /dev/null
+++ b/web/cgi/images/tabs/gtab.gif
Binary files differ
diff --git a/web/cgi/images/tabs/mldtab.gif b/web/cgi/images/tabs/mldtab.gif
new file mode 100644
index 00000000..ad10bafb
--- /dev/null
+++ b/web/cgi/images/tabs/mldtab.gif
Binary files differ
diff --git a/web/cgi/images/tabs/mltab.gif b/web/cgi/images/tabs/mltab.gif
new file mode 100644
index 00000000..c2a6833f
--- /dev/null
+++ b/web/cgi/images/tabs/mltab.gif
Binary files differ
diff --git a/web/cgi/images/tabs/mvdtab.gif b/web/cgi/images/tabs/mvdtab.gif
new file mode 100644
index 00000000..2051a898
--- /dev/null
+++ b/web/cgi/images/tabs/mvdtab.gif
Binary files differ
diff --git a/web/cgi/images/tabs/mvtab.gif b/web/cgi/images/tabs/mvtab.gif
new file mode 100644
index 00000000..01b66066
--- /dev/null
+++ b/web/cgi/images/tabs/mvtab.gif
Binary files differ
diff --git a/web/cgi/images/tabs/rdtab.gif b/web/cgi/images/tabs/rdtab.gif
new file mode 100644
index 00000000..c97628bc
--- /dev/null
+++ b/web/cgi/images/tabs/rdtab.gif
Binary files differ
diff --git a/web/cgi/images/tabs/rtab.gif b/web/cgi/images/tabs/rtab.gif
new file mode 100644
index 00000000..06519f2a
--- /dev/null
+++ b/web/cgi/images/tabs/rtab.gif
Binary files differ
diff --git a/web/cgi/images/tabs/tabbg.gif b/web/cgi/images/tabs/tabbg.gif
new file mode 100644
index 00000000..f0382851
--- /dev/null
+++ b/web/cgi/images/tabs/tabbg.gif
Binary files differ
diff --git a/web/cgi/images/tabs/tabmid.gif b/web/cgi/images/tabs/tabmid.gif
new file mode 100644
index 00000000..7d9bd22c
--- /dev/null
+++ b/web/cgi/images/tabs/tabmid.gif
Binary files differ
diff --git a/web/cgi/motd.sample b/web/cgi/motd.sample
new file mode 100644
index 00000000..d3525a3f
--- /dev/null
+++ b/web/cgi/motd.sample
@@ -0,0 +1,5 @@
+<b>Note:</b><br>
+We're always looking to improve Web Alpine! Please send your comments and suggestions to to alpine-info@cac.washington.edu.
+<br>
+Thanks!
+
diff --git a/web/cgi/pub/alpine.tcl b/web/cgi/pub/alpine.tcl
new file mode 120000
index 00000000..5ad8d42f
--- /dev/null
+++ b/web/cgi/pub/alpine.tcl
@@ -0,0 +1 @@
+../alpine.tcl \ No newline at end of file
diff --git a/web/cgi/pub/getach.tcl b/web/cgi/pub/getach.tcl
new file mode 100755
index 00000000..dce5047c
--- /dev/null
+++ b/web/cgi/pub/getach.tcl
@@ -0,0 +1,87 @@
+#!./tclsh
+
+# ========================================================================
+# Copyright 2006-2007 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# getach.tcl
+#
+# Purpose: CGI script to retrieve requested attachment
+#
+# Input:
+#
+# h : handle referring to temp file holding attachment particulars
+
+# inherit global config
+source ./alpine.tcl
+
+cgi_eval {
+
+ # verify that the format of the request is correct. Mainly it's
+ # to reasonably handle relative urls in viewed html attachments. Ugh.
+ if {[regexp {^h=[A-Za-z0-9]*$} $env(QUERY_STRING)]} {
+
+ cgi_input
+
+ WPImport h
+
+ if {[regexp {^[A-Za-z0-9]+$} $h] != 1} {
+ WPInfoPage "Web Alpine Error" [font size=+2 "Bogus Attachment Handle: $h"] "Please close this window."
+ exit
+ }
+
+ set cmdfile [file join $_wp(detachpath) detach.${h}-control]
+
+ if {[file exists $cmdfile] == 0} {
+ WPInfoPage "Webpine Error" [font size=+2 "Stale Handle Reference"] "Please click the attachment link to view this attachment."
+ } elseif {[catch {
+ set cid [open $cmdfile r]
+
+ while {1} {
+ if {[gets $cid line] == 0} {
+ if {[gets $cid tmpfile] != 0} {
+ puts stdout ""
+
+ set fp [open $tmpfile r]
+
+ fconfigure $fp -translation binary
+ fconfigure stdout -translation binary
+
+ fcopy $fp stdout
+
+ close $fp
+ }
+
+ break
+ } else {
+ puts stdout $line
+ }
+ }
+ } errstr]} {
+ WPInfoPage "Webpine Error" [font size=+2 $errstr] "Please close this window."
+ }
+
+ #
+ # Don't delete the temp files immediately because *some* browsers
+ # <cough>IE<cough> will download the attachment, then hand the
+ # URL of the downloaded attachment to the viewer <cough>WMP<cough>
+ # so the viewer can then *REFETCH* the data for display. No,
+ # they don't pay attention to the redirect code.
+ #
+ # The script that created them is responsible for having set in motion
+ # a process to delete them at some point in the future.
+ #
+ # catch {file delete -force $cmdfile}
+ # catch {file delete -force $tmpfile}
+ } else {
+ WPInfoPage "Invalid Attachment Reference" "[font size=+2 "Invalid Attachment Reference"]" \
+ "This page is the result of clicking a link in an attached HTML document. It's author likely used an incomplete hypertext link which resulted in a bogus link to the WebPine server.<p>Clicking your browser's Back button should restore the attachment's display."
+ }
+}
diff --git a/web/cgi/pub/standard.css b/web/cgi/pub/standard.css
new file mode 100644
index 00000000..e2b0fd21
--- /dev/null
+++ b/web/cgi/pub/standard.css
@@ -0,0 +1,45 @@
+TD.nosize { font-size: 16px }
+DIV.fname { font-family: arial, sans-serif; font-size: 10pt }
+.prog { font-family: arial, sans-serif ; font-weight: bold ; font-size: 16px ; color: #50056E }
+.title { font-family: arial, sans-serif ; color: white }
+.flymenu { visibility: hidden; position: absolute; left: 0; top: 0 }
+.flydata { font-family: arial, sans-serif ; font-size: 10pt }
+.flydata:hover { color: #ff0066 }
+TABLE.flydata { background-color: #fefac9 ; border: #006400 2px solid }
+TH.flydata { text-align: center ; color: white ; background-color: #006400 }
+HR.flydata { color: #006400 }
+A.flydata { font-size: 10pt ; text-decoration: none ; color: black }
+.menubar { color: white; font-family: arial, sans-serif; font-size: 8pt }
+.menubar:hover { color:yellow }
+DIV.menubar { position: absolute; visibility: visible; top: 62px; left: 2px; }
+A.menubar { color:black; text-decoration:none; font-weight:bold ; height: 16px ; width: 100px ; vertical-align: middle }
+HR.menubar { border-top: solid; color: black }
+.body { background-color: white }
+input.body { color: white; }
+.indexhdr { font-family: geneva, arial, sans-serif ; font-size: 8pt ; color: black }
+.indexsort { font-family: geneva, arial, sans-serif ; background-color: #999999 ; font-size: 8pt ; color: white }
+.auth { font-family: Arial, Sans-Serif ; font-size: 10pt ; font-weight: bold }
+.extrahdrs { background-color: white; display: none }
+.navtext { font-family: geneva, arial, sans-serif ; font-size: 9pt }
+.navbar { color: white ; font-family: geneva, arial, sans-serif ; font-size: 9pt ; letter-spacing: 0pt }
+FORM { margin-bottom: 0px ; margin-top: 0px }
+.statustext { color: black ; font-family: geneva, arial, sans-serif ; font-size: 8pt }
+.morestatus { color: #ff3333 ; font-size: 10pt ; font-weight : bold }
+.ifakebar { visibility: hidden; position: absolute; left: 0; top: 0 }
+.ithumb { position: relative }
+.iflags { font-family: sans-serif ; color: red }
+.isize { font-size: 9pt }
+.notice { color: red ; font-weight: bold }
+A.notice { color: red }
+.attach { visibility: hidden; position: absolute; left: 0; top: 0 }
+.mispell { background: #ffcccc ; color: #000000; text-decoration: none }
+.spelltext { position: relative }
+.dialog { background-color: #fefac9 ; font-family: arial, sans-serif ; font-size: 12pt }
+.context { background-color: #999999 ; color: black ; font-family: Helvetica, sans-serif ; font-size: 9pt }
+.ops { background-color: #666666 ; color: white ; font-family: Helvetica, sans-serif }
+.i0 { background-color: #eeeeee }
+.i1 { background-color: #ffffff }
+.cfntc { color: red; font-size: 8pt }
+.cfval { font-size: 9pt }
+.cfvn { font-weight: bold; font-size: 10pt }
+.foldpic { background-color: #eeeeee }
diff --git a/web/cgi/pub/standard.js b/web/cgi/pub/standard.js
new file mode 100644
index 00000000..89157e1f
--- /dev/null
+++ b/web/cgi/pub/standard.js
@@ -0,0 +1,414 @@
+var child = new Object();
+child.win = null;
+var reloadtimer = null
+var resizer = null;
+var resized = null;
+var resizex = 0;
+var resizey = 0;
+
+if (navigator.appName.indexOf('Netscape') >= 0) {
+ if (navigator.appVersion.substr(0,1) < 5) {
+ isW3C = false;
+ isIE = false;
+ sEvent='e';
+ sKey='.which';
+ yRef='.top';
+ xRef='.left';
+ xOff='.clip.width';
+ yOff='.clip.height';
+ xEvent='.pageX';
+ yEvent='.pageY';
+ dS='document.';
+ sD='';
+ sHideV="'hide'";
+ sShowV="'show'";
+ dRef='self';
+ hRef='.innerHeight';
+ wRef='.innerWidth';
+ xSRef='.screenX';
+ ySRef='.screenY';
+ xPos=dRef+'.pageXOffset';
+ yPos=dRef+'.pageYOffset';
+ eSrc='.target';
+ } else {
+ isW3C = true;
+ isIE = false;
+ sEvent='e';
+ sKey='.which';
+ yRef='.top';
+ xRef='.left';
+ xOff='.offsetWidth';
+ yOff='.offsetHeight';
+ xEvent='.pageX';
+ yEvent='.pageY';
+ dS='document.';
+ sD='.style';
+ sHideV="'hidden'";
+ sShowV="'visible'";
+ dRef='self';
+ hRef='.innerHeight';
+ wRef='.innerWidth';
+ xSRef='.screenX';
+ ySRef='.screenY';
+ xPos=dRef+'.pageXOffset';
+ yPos=dRef+'.pageYOffset';
+ eSrc='.target';
+ }
+}
+else {
+ isIE = true;
+ isW3C = 0;
+ sEvent='window.event';
+ sKey='.keyCode';
+ yRef='.pixelTop';
+ xRef='.pixelLeft';
+ xOff='.offsetWidth';
+ yOff='.offsetHeight';
+ xEvent='.clientX';
+ yEvent='.clientY';
+ dS='';
+ sD='.style';
+ sHideV="'hidden'";
+ sShowV="'visible'";
+ dRef='document.body';
+ hRef='.clientHeight';
+ wRef='.clientWidth';
+ xPos=dRef+'.scrollLeft';
+ yPos=dRef+'.scrollTop';
+ xSRef='.screenLeft';
+ ySRef='.screenTop';
+ eSrc='.srcElement';
+}
+
+function setLayout() {
+ updateLayout();
+}
+
+function updateLayout() {
+ var yy = eval(yPos);
+ yy += Math.max(64 - yy, 0);
+ moveLayerY(getLayer('tasks'),yy);
+ setTimeout('updateLayout()',100);
+}
+
+function setResize(f,w) {
+ document.resized = (f) ? f : doResize;
+
+ document.resizex = getDisplayWidth();
+ document.resizey = getDisplayHeight();
+
+ if (isIE){
+ document.resizer = (w) ? w : null;
+ window.onresize = armResize;
+ }
+ else{
+ window.onresize = document.resized;
+ }
+}
+
+function armResize(e) {
+ if(document.resizer) document.resizer(e);
+ document.onmouseover = document.resized;
+}
+
+function doResize(e) {
+ if(!(document.resizex == getDisplayWidth() && document.resizey == getDisplayHeight())){
+ var newurl = window.location.href.replace(/\?.*$/, '');
+ document.onmouseover = null;
+ window.location.replace(newurl+'?ppg='+getResizedLines(e));
+ }
+}
+
+function getResizedLines(e) {
+ var h = (isW3C || document.all) ? eval(dRef+hRef) : e.height;
+ return Math.max(Math.floor((h - 66) / getIndexHeight()) - 1, 2);
+}
+
+function getDisplayWidth() {
+ return eval(dRef+wRef);
+}
+
+function getDisplayHeight() {
+ return eval(dRef+hRef);
+}
+
+function getWindowX() {
+ return eval('window'+xSRef);
+}
+
+function getWindowY() {
+ return eval('window'+ySRef);
+}
+
+function showElement(o) {
+ eval('o'+sD+'.visibility = '+sShowV);
+}
+
+function hideElement(o) {
+ eval('o'+sD+'.visibility = '+sHideV);
+}
+
+function isElementDisplayed(o) {
+ var d = eval('o'+sD+'.display');
+ return (d == 'block');
+}
+
+function displayElement(o) {
+ eval('o'+sD+'.display = "block"');
+}
+
+function concealElement(o) {
+ eval('o'+sD+'.display = "none"');
+}
+
+function getScrollAbove() {
+ return(eval(yPos));
+}
+
+function getScrollLeft() {
+ return(eval(xPos));
+}
+
+function layerWalk(l,t) {
+ var o;
+ for(var i = 0; i < l.length; i++){
+ o = eval('l[i]'+t);
+ if(o) return o;
+ o = layerWalk(l[i].document.layers,t);
+ if(o) return o;
+ }
+
+ return null;
+}
+
+function getImage(id) {
+ if(isW3C)
+ return document.getElementById(id);
+ else if(isIE)
+ return document.all[id];
+ else if(window.document.images[id])
+ return window.document.images[id];
+ else
+ return layerWalk(window.document.layers,'.document.images.'+id);
+}
+
+function getImageX(o) {
+ if(isIE || isW3C){
+ var x = o.offsetLeft;
+ for(var p = o.offsetParent; p != null; p = p.offsetParent)
+ x += p.offsetLeft;
+
+ return x;
+ }
+ else
+ return o.x;
+}
+
+function getImageY(o) {
+ if(isIE || isW3C){
+ var y = o.offsetTop;
+ for(var p = o.offsetParent; p != null; p = p.offsetParent)
+ y += p.offsetTop;
+
+ return y;
+ }
+ else
+ return o.y;
+}
+
+function getLayer(id) {
+ if(isW3C)
+ return document.getElementById(id);
+ else if(isIE)
+ return document.all[id];
+ else if(window.document.layers[id])
+ return window.document.layers[id];
+ else
+ return layerWalk(window.document.layers,'.document.layers.'+id);
+}
+
+function getElementWidth(l){
+ return l.width;
+}
+
+function getLayerHeight(l){
+ return eval('l'+yOff);
+}
+
+function getLayerWidth(l){
+ return eval('l'+xOff);
+}
+
+function getLayerX(l) {
+ return eval('l'+sD+xRef);
+}
+
+function getLayerY(l) {
+ return eval('l'+sD+yRef);
+}
+
+function moveLayerX(l,left) {
+ eval ('l'+sD+xRef+'='+left);
+}
+
+function moveLayerY(l,top) {
+ eval ('l'+sD+yRef+'='+top);
+}
+
+function moveLayer(l,left,top) {
+ moveLayerX(l,left);
+ moveLayerY(l,top);
+}
+
+function setLayerText(l,t) {
+ if(isIE || isW3C){
+ l.innerHTML = t;
+ }
+ else{
+ l.document.open();
+ l.document.write(t);
+ l.document.close();
+ }
+}
+
+function getEvent(e) {
+ return eval(sEvent);
+}
+
+function getEventElement(e) {
+ return eval('getEvent(e)'+eSrc);
+}
+
+function getEventX(e) {
+ return eval('getEvent(e)'+xEvent);
+}
+
+function getEventY(e) {
+ return eval('getEvent(e)'+yEvent);
+}
+
+function getKeyCode(e) {
+ return eval('getEvent(e)'+sKey);
+}
+
+function getControlKey(e) {
+ if(isW3C || isIE){
+ return getEvent(e).ctrlKey;
+ }
+ else{
+ return (e.modifiers & Event.CONTROL_MASK) != 0;
+ }
+}
+
+function getKeyStr(e) {
+ return String.fromCharCode(getKeyCode(e)).toLowerCase();
+}
+
+function focuschild() {
+ var rv = false;
+ var ourif = 'child.win.focus(); rv = true;';
+ var ourelse = 'child.win = null; window.onfocus = null;';
+
+ if(isIE && js_version >= 1.3){
+ eval('try {'+ourif+'} catch (all) {'+ourelse+'}');
+ }
+ else{
+ eval('if((child.win == null) || (child.win.closed)){'+ourelse+'}else{'+ourif+'}');
+ }
+
+ return rv;
+}
+
+function cOpen(u,n,a,w,h) {
+ if(!focuschild()){
+ var xWin = getWindowX(), yWin = getWindowY();
+ var wPage = getDisplayWidth(), hPage = getDisplayHeight();;
+ var x = Math.min(window.screen.width - wPage, Math.max(0, xWin + ((wPage - w)/2)));
+ var y = Math.min(window.screen.height - hPage, Math.max(0, yWin + ((hPage - h)/2)));
+ var f = a ? a+',' : '';
+ var now = new Date();
+
+ child.win = window.open(u,n+now.getTime(),f+'width='+w+',height='+h+',screenX='+x+',screenY='+y+',top='+y+',left='+x);
+ window.onfocus = focuschild;
+ }
+
+ return child.win;
+}
+
+function cnOpen(u,n,a,w,h) {
+ var xOffset = (window.screen.width - w)/2, yOffset = (window.screen.height - h)/2;
+ window.open(u,n,a+',width='+w+',height='+h+',screenX='+xOffset+',screenY='+yOffset+',top='+yOffset+',left='+xOffset);
+}
+
+function composeMsg(f,d,k) {
+ var s = 'compose.tcl', replace = 0;
+ switch(f) {
+ //case 'reply' : if(confirm('Include all recipients in Reply') == true) c = '?style=ReplyAll&uid='+d ; else c = '?style=Reply&uid='+d ; break;
+ case 'reply' : s='reply.tcl'; c = '?style=ReplyAll&uid='+d+'&cid='+k ; break;
+ case 'fwd' : c = '?style=Forward&uid='+d+'&cid='+k ; break;
+ case 'mailto' : c = '?to='+d+'&cid='+k ; break;
+ case 'nickto' : c = '?nickto='+d+'&cid='+k ; replace = 1; break;
+ default : c = '?cid='+k ; break;
+ }
+ if(!replace)
+ cOpen(s+c, 'compose', 'scrollbars=yes,resizable=yes', 800, 560);
+ else
+ window.location.href = s + c;
+ return false;
+}
+
+function isanum(s) {
+ if(s.length == 0)
+ return false;
+
+ for(var i = 0; i < s.length; i++)
+ if(s.charCodeAt(i) < 48 || s.charCodeAt(i) > 57)
+ return false;
+
+ return true;
+}
+
+function confOpen(s) {cOpen(s,'config','scrollbars=yes',800,560); return false;}
+function abookOpen(s) {cOpen(s,'addrbook','scrollbars=yes,resizable=yes',800,560); return false;}
+function helpOpen(s) {cOpen(s,'help','scrollbars=yes,resizable=yes',600,600); return false;}
+function aeOpen(s) {cOpen(s, 'addredit', 'scrollbars=yes,resizable=yes', 700, 500); return false}
+function taOpen(s) {cOpen(s, 'takeaddr', 'scrollbars=yes,resizable=yes', 700, 500); return false}
+function quitOpen(s) {cOpen(s, 'quit', '', 420, 200); return false}
+
+function doReload(ival) {
+ if((child.win == null) || (child.win.closed)){
+ var newurl = window.location.href.replace(/\?.*$/, '');
+ window.location.replace(newurl+'?reload=1');
+ }
+ else{
+ var i = getImage('logo');
+ if(i){
+ var now = new Date();
+ var t = new String();
+
+ t += now.getYear();
+ t += now.getMonth();
+ t += now.getDate();
+ t += now.getHours();
+ t += now.getMinutes();
+ t += now.getSeconds();
+ i.src = location.href.replace(/(.*)\/[^\/]*\.tcl.*/,'$1/ping.tcl/'+t+'/'+ival+'/'+(t - (t % ival))+'.gif');
+ }
+ }
+}
+
+function reloadTimer(s) {
+ reloadtimer = window.setInterval('doReload('+s+')', s * 1000);
+}
+
+function wp_escape(s) {
+ var t = escape(s);
+ t = t.replace(/\+/, '%2b');
+ return t;
+}
+
+function flipCheck(eid){
+ var cb = window.document.getElementById(eid);
+ if(cb) cb.checked = !cb.checked;
+ return false;
+}
diff --git a/web/cgi/pub/tclsh b/web/cgi/pub/tclsh
new file mode 120000
index 00000000..385fc6c6
--- /dev/null
+++ b/web/cgi/pub/tclsh
@@ -0,0 +1 @@
+../tclsh \ No newline at end of file
diff --git a/web/cgi/session/.htaccess b/web/cgi/session/.htaccess
new file mode 100644
index 00000000..a4f615c2
--- /dev/null
+++ b/web/cgi/session/.htaccess
@@ -0,0 +1,28 @@
+
+DirectoryIndex greeting.tcl
+
+#
+# mod_rewrite rules to coerce secure (https) access to underlying pages
+#
+
+RewriteEngine on
+
+#
+# If the server's connecting port isn't secure (https), then
+# redirect request to same location but such that the communication
+# is secure. NOTE: this isn't as secure as turning off the unsecure
+# port because any confidential information in the request is exposed
+# in the unsuspecting request on the unsecure port. Shouldn't really
+# be a problem since the secure content should only contain secure
+# references and the likelihood that a client mucks with the url into
+# a reference to secure content is pretty darn small.
+#
+
+RewriteCond %{SERVER_PORT} !=443
+
+#
+# Include SCRIPT_URL incase webpine package isn't in the
+# root of the server's data
+#
+RewriteRule .* https://%{SERVER_NAME}%{REQUEST_URI} [R=permanent,L]
+
diff --git a/web/cgi/session/_htaccess b/web/cgi/session/_htaccess
new file mode 100644
index 00000000..a4f615c2
--- /dev/null
+++ b/web/cgi/session/_htaccess
@@ -0,0 +1,28 @@
+
+DirectoryIndex greeting.tcl
+
+#
+# mod_rewrite rules to coerce secure (https) access to underlying pages
+#
+
+RewriteEngine on
+
+#
+# If the server's connecting port isn't secure (https), then
+# redirect request to same location but such that the communication
+# is secure. NOTE: this isn't as secure as turning off the unsecure
+# port because any confidential information in the request is exposed
+# in the unsuspecting request on the unsecure port. Shouldn't really
+# be a problem since the secure content should only contain secure
+# references and the likelihood that a client mucks with the url into
+# a reference to secure content is pretty darn small.
+#
+
+RewriteCond %{SERVER_PORT} !=443
+
+#
+# Include SCRIPT_URL incase webpine package isn't in the
+# root of the server's data
+#
+RewriteRule .* https://%{SERVER_NAME}%{REQUEST_URI} [R=permanent,L]
+
diff --git a/web/cgi/session/alpine.tcl b/web/cgi/session/alpine.tcl
new file mode 120000
index 00000000..5ad8d42f
--- /dev/null
+++ b/web/cgi/session/alpine.tcl
@@ -0,0 +1 @@
+../alpine.tcl \ No newline at end of file
diff --git a/web/cgi/session/greeting.tcl b/web/cgi/session/greeting.tcl
new file mode 100755
index 00000000..a6acc1e5
--- /dev/null
+++ b/web/cgi/session/greeting.tcl
@@ -0,0 +1,395 @@
+#!./tclsh
+# $Id: greeting.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# suck in any global config
+source ./alpine.tcl
+
+# figure out SSL defaults
+if {[info exists _wp(ssl_default)] && $_wp(ssl_default) == 0} {
+ set ssl 0
+} else {
+ set ssl 1
+ if {[info exists _wp(ssl_safe_domains)]} {
+ if {[info exists env(REMOTE_HOST)]} {
+ foreach d $_wp(ssl_safe_domains) {
+ regsub -all {\.} $d {\.} d
+ if {[regexp -nocase "$d\$" $env(REMOTE_HOST)]} {
+ set ssl 0
+ break
+ }
+ }
+ }
+
+ if {$ssl && [info exists env(REMOTE_ADDR)]} {
+ regexp {([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)} $env(REMOTE_ADDR) dummy a b c d
+ set h "$d.$c.$b.$a.in-addr.arpa"
+ if {[catch {exec nslookup -q=PTR $h "2&>1"} pr] == 0} {
+ foreach l [split $pr \n] {
+ if {[regexp -nocase "^$h\[ \]*name = (.*)\$" $l dummy inaddr]} {
+ break
+ }
+ }
+ }
+
+ if {[info exists inaddr]} {
+ foreach d $_wp(ssl_safe_domains) {
+ if {[set n [expr {[string length $inaddr] - [string length $d] - 1}]] > 1
+ && [string compare [string tolower [string range $inaddr $n end]] [string tolower ".$d"]] == 0} {
+ set ssl 0
+ break
+ }
+ }
+ }
+ }
+ }
+
+ if {$ssl && [info exists _wp(ssl_safe_addrs)]} {
+ set ra [split $env(REMOTE_ADDR) .]
+ foreach a $_wp(ssl_safe_addrs) {
+ if {[llength $a] == 2} {
+ set low [split [lindex $a 0] .]
+ set hi [split [lindex $a 1] .]
+
+ foreach a $ra b $low c $hi {
+ if {$b == $c} {
+ if {$a != $b} {
+ break
+ }
+ } else {
+ if {$a >= $b && $a <= $c} {
+ set ssl 0
+ break
+ }
+ }
+ }
+ } else {
+ foreach a [split $a .] b $ra {
+ if {[string length $a]} {
+ if {$a != $b} {
+ break
+ }
+ } else {
+ set $ssl 0
+ break
+ }
+ }
+ }
+
+ if {!$ssl} {
+ break
+ }
+ }
+ }
+}
+
+cgi_eval {
+
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+
+ cgi_head {
+ WPStdHtmlHdr Logon
+ WPStdScripts $_wp(indexheight)
+
+ if {$_wp(flexserver)} {
+ cgi_javascript {
+ cgi_put "function bogus(f,o) {"
+ cgi_put " if(o.value.length == 0){"
+ cgi_put " alert('What '+f+' will you be using to log in to your IMAP server?\\nPlease fill in the '+f+' field.');"
+ cgi_put " o.focus();"
+ cgi_put " return true;"
+ cgi_put " }"
+ cgi_put " return false;"
+ cgi_puts "}"
+ cgi_put "function doSubmit() {"
+ cgi_put " var f = document.Login;"
+ cgi_put " var l = f.Server;"
+ cgi_put " var o = l.options\[l.selectedIndex\];"
+ cgi_put " if(bogus('Username',f.User) || bogus('Password',f.Pass)) return false;"
+ cgi_put " if(o.value < 0){"
+ cgi_put " o.value = alien_server;"
+ cgi_put " }"
+ cgi_put " return true;"
+ cgi_puts "}"
+ cgi_put "function customServer() {"
+ cgi_put " var f = document.Login;"
+ cgi_put " var l = f.Server;"
+ cgi_put " var o = l.options\[l.selectedIndex\];"
+ if {[info exists env(REMOTE_USER)]} {
+ cgi_put " f.User.value = (o.value < 0) ? alien_user : def_user;"
+ cgi_put " f.Pass.value = (o.value < 0) ? alien_pass : def_pass;"
+ }
+ cgi_put " if(o.value < 0){"
+ cgi_put " if(s = prompt('IMAP Server Name (append \"/ssl\" for secure link)',o.s ? o.s : '')){"
+ cgi_put " alien_server = s;"
+ cgi_put " o.text = 'Your Server: '+alien_server;"
+ cgi_put " f.User.onfocus = null;"
+ cgi_put " f.Pass.onfocus = null;"
+ cgi_put " f.User.focus();"
+ cgi_put " }"
+ cgi_put " else if(alien_server.length == 0){"
+ if {[info exists env(REMOTE_USER)]} {
+ cgi_put " f.User.onfocus = f.User.blur;"
+ cgi_put " f.Pass.onfocus = f.Pass.blur;"
+ cgi_put " f.User.value = def_user;"
+ cgi_put " f.Pass.value = def_pass;"
+ }
+ cgi_put " l.selectedIndex = 0;"
+ cgi_put " }"
+ cgi_put " }"
+ if {[info exists env(REMOTE_USER)]} {
+ cgi_put " else {"
+ cgi_put " f.User.onfocus = f.User.blur;"
+ cgi_put " f.Pass.onfocus = f.Pass.blur;"
+ cgi_put " }"
+ }
+ cgi_puts "}"
+ if {[info exists env(REMOTE_USER)]} {
+ cgi_put "var def_user = new Array('$env(REMOTE_USER)');"
+ cgi_put "var def_pass = new Array('x');"
+ cgi_put "var alien_server = '';"
+ cgi_put "var alien_user = '';"
+ cgi_put "var alien_pass = '';"
+ cgi_put "function customSave() {"
+ cgi_put " var f = document.Login;"
+ cgi_put " var s = f.Server.options\[f.Server.selectedIndex\];"
+ cgi_put " if(s.value < 0){"
+ cgi_put " alien_user = f.User.value;"
+ cgi_put " alien_pass = f.Pass.value;"
+ cgi_put " }"
+ cgi_puts "}"
+ }
+ }
+ }
+ }
+
+ if {[info exists env(REMOTE_USER)]} {
+ set onload ""
+ } else {
+ set onload "onLoad=document.Login.User.focus()"
+ }
+
+ cgi_body $onload {
+ if {$_wp(flexserver)} {
+ set onsubmit "onSubmit=return doSubmit()"
+ } else {
+ set onsubmit ""
+ }
+
+ cgi_form session/init method=post enctype=multipart/form-data name=Login $onsubmit {
+ cgi_javascript {
+ cgi_puts "document.write('<input name=\"hPx\" value='+window.getDisplayHeight()+' type=hidden notab>');"
+ }
+
+ cgi_table height=100% width=100% align=center {
+
+ cgi_table_row {
+ cgi_table_data align=center valign=bottom height=18% colspan=8 {
+ set intro "Welcome to $_wp(appname)"
+ if {[info exists env(REMOTE_USER)]} {
+ set conf [subst [lindex [lindex $_wp(hosts) 0] 2]]
+ set pldap [file join $_wp(bin) $_wp(pldap)]
+ if {[catch {exec $pldap -p $conf -u $env(REMOTE_USER)} pname] == 0} {
+ append intro ", $pname"
+ }
+ }
+
+ cgi_put [font size=+2 face=Helvetica [bold $intro]]
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data width=36% height=35% align=right valign=middle {
+ cgi_put [cgi_imglink logo]
+ }
+
+ cgi_table_data width=5% {
+ cgi_puts [cgi_nbspace]
+ }
+
+ cgi_table_data {
+ cgi_table border=0 cellspacing=8 {
+ cgi_table_row {
+ cgi_table_data align=right valign=bottom {
+ cgi_puts [font size=+1 face=Helvetica "Username:"]
+ }
+
+ cgi_table_data align=left valign=bottom {
+ if {[info exists env(REMOTE_USER)]} {
+ set user "User=$env(REMOTE_USER)"
+ set rdonly "onFocus=this.blur();"
+ } else {
+ set user "User="
+ set rdonly ""
+ }
+
+ if {$_wp(flexserver) && [info exists env(REMOTE_USER)]} {
+ set onblur onBlur=customSave()
+ } else {
+ set onblur ""
+ }
+
+ cgi_text $user type=text maxlength=30 tableindex=1 $rdonly $onblur
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data align=right valign=middle {
+ cgi_puts [font size=+1 face=Helvetica "Password:"]
+ }
+
+ cgi_table_data align=left valign=middle {
+ if {[info exists env(REMOTE_USER)]} {
+ set pass "Pass=*"
+ set rdonly "onFocus=this.blur();"
+ } else {
+ set pass "Pass="
+ set rdonly ""
+ }
+
+ if {$_wp(flexserver) && [info exists env(REMOTE_USER)]} {
+ set onblur onBlur=customSave()
+ } else {
+ set onblur ""
+ }
+
+ cgi_text $pass type=password maxlength=30 tableindex=2 $rdonly $onblur
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data align=right valign=middle {
+ cgi_puts [font face=Helvetica size=+1 "Server:"]
+ }
+
+ cgi_table_data align=left valign=top {
+ if {[info exists _wp(hosts)]} {
+ if {$_wp(flexserver)} {
+ set onchange onChange=customServer()
+ if {[info exists env(REMOTE_USER)]} {
+ set onblur onBlur=customSave()
+ } else {
+ set onblur ""
+ }
+ } else {
+ set onchange ""
+ set onblur ""
+ }
+
+ cgi_select Server align=left $onchange $onblur {
+ for {set j 0} {$j < [llength $_wp(hosts)]} {incr j} {
+ cgi_option [lindex [lindex $_wp(hosts) $j] 0] value=$j
+ }
+
+ if {$_wp(flexserver)} {
+ cgi_javascript {
+ cgi_puts "document.write('<option value=\"-1\">Server of Your Choice');"
+ }
+ }
+ }
+ } else {
+ cgi_text Server= type=text maxlength=256
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if {[info exists env(REMOTE_USER)]} {
+ cgi_table_row {
+ cgi_table_data align=center valign=top height=1% colspan=8 {
+ cgi_puts "Protect your privacy! When you finish, [cgi_url "completely exit your Web browser" "http://www.washington.edu/computing/web/logout.html"]."
+ }
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data align=center valign=top colspan=8 "style=padding-bottom:10" {
+ cgi_submit_button login=Login
+ }
+ }
+
+ if {[info exists _wp(plainservpath)] && [string length $_wp(plainservpath)]} {
+ cgi_table_row {
+ cgi_table_data colspan=8 align=center valign=middle {
+ cgi_table border=0 width=30% {
+ cgi_table_row {
+ cgi_table_data rowspan=2 valign=top {
+ if {$ssl} {
+ set checked checked
+ } else {
+ set checked ""
+ }
+
+ cgi_checkbox ssl=1 $checked
+ }
+
+ cgi_table_data {
+ cgi_put [cgi_font size=-1 face=Helvetica "Use [cgi_url "SSL Session Encryption" "$_wp(serverpath)/$_wp(appdir)/$_wp(ui1dir)/help/secure.html" target=_blank]"]
+ cgi_br
+ set t "Session encryption over low-speed connections may slow WebPine, but prevents eavesdropping. Passwords are safely encrypted though."
+
+ cgi_division style=background-color:#eeeeee {
+ cgi_put [cgi_font size=-2 face=Helvetica $t]
+ }
+ }
+ }
+ }
+ }
+ }
+ } else {
+ cgi_text ssl=1 type=hidden notab
+ }
+
+ if {[info exists _wp(oldserverpath)] && [regexp {^[Hh][Tt][Tt][Pp][Ss]://} $_wp(oldserverpath)]} {
+ cgi_table_row {
+ cgi_table_data colspan=8 align=center valign=middle {
+ cgi_division "style=\"width: 30% ; font-family: Helvetica; font-size: small ; background-color: #EEEEEE; padding: 8 2 \"" {
+ cgi_puts "For the time being, the [cgi_url "old version" $_wp(oldserverpath)] is still available."
+ }
+ }
+ }
+ }
+
+ if {[catch {open [file join $_wp(cgipath) $_wp(motd)] r} id] == 0} {
+ cgi_table_row {
+ cgi_table_data colspan=8 height=20% valign=top {
+ cgi_table width=100% {
+ cgi_table_data height=40 width=18% {
+ # cgi_puts [cgi_imglink bang]
+ cgi_puts [cgi_nbspace]
+ }
+
+ cgi_table_data {
+ cgi_puts [font size=-1 face=Helvetica [read $id]]
+ }
+
+ cgi_table_data width=18% {
+ # cgi_puts [cgi_imglink bang]
+ cgi_puts [cgi_nbspace]
+ }
+ }
+ }
+ }
+
+ close $id
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/session/init.tcl b/web/cgi/session/init.tcl
new file mode 100755
index 00000000..00f0f903
--- /dev/null
+++ b/web/cgi/session/init.tcl
@@ -0,0 +1,218 @@
+#!./tclsh
+# $Id: init.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# init.tcl
+#
+# Purpose: CGI script to establish foundation for webpine session
+
+# and any global config
+source ./alpine.tcl
+
+
+cgi_eval {
+ if {$_wp(debug)} {
+ cgi_debug -on
+ }
+
+ #
+ # Import username and password from pubcookie, if possible.
+ # Otherwise get it from the form that was submitted.
+ #
+ cgi_input
+
+ if {[catch {cgi_import User}] || 0 == [string length $User]} {
+ WPInfoPage "Bogus Username" \
+ "[font size=+2 "Sorry, didn't catch your [bold name]!"]" \
+ "Please click your browser's [bold Back] button to return to the [cgi_link Start], and fill in a [italic Username]..."
+ return
+ }
+
+ if {[catch {cgi_import Pass}]} {
+ set Pass ""
+ }
+
+ if {[catch {cgi_import Server}] || 0 == [string length $Server]} {
+ WPInfoPage "Bogus Server" \
+ "[font size=+2 "Invalid Server specified"]" \
+ "Please click your browser's [bold Back] button to return to the [cgi_link Start], and fill in a [italic Server]..."
+ return
+ }
+
+ catch {cgi_import hPx}
+
+ set defconf [file join $_wp(confdir) $_wp(defconf)]
+ set confloc ""
+
+ if {[string length $Server] < 256 && 0 == [regexp {[[:cntrl:]]} $Server]} {
+ if {[info exists _wp(hosts)] && $Server >= 0 && $Server < [llength $_wp(hosts)]} {
+ set sdata [lindex $_wp(hosts) $Server]
+
+ set env(IMAP_SERVER) "[subst [lindex $sdata 1]]/user=$User"
+
+ if {[llength $sdata] > 2 && [string length [lindex $sdata 2]]} {
+ set defconf [subst [lindex $sdata 2]]
+ } else {
+ #
+ # Validate input?
+ #
+ WPInfoPage "Internal Error" \
+ [font size=+2 "IMAP Server Mismatch"] \
+ "Please complain to the [link Admin] and visit the [cgi_link Start] later."
+ return
+ }
+ } elseif {[regexp {/user=} $Server]} {
+ set env(IMAP_SERVER) "$Server"
+ } else {
+ set env(IMAP_SERVER) "$Server/user=$User"
+ }
+
+ set confloc "\{$env(IMAP_SERVER)\}$_wp(config)"
+
+ regexp {^[^:/]*} $env(IMAP_SERVER) env(IMAP_SERVER_BASE)
+ } else {
+ WPInfoPage "Bad Server Name" [font size=+2 "Server Name too long or has bogus characters."] \
+ "Please click your browser's [bold Back] button to return to the [cgi_link Start] to try again..."
+ return
+ }
+
+ set confloc "\{$env(IMAP_SERVER)\}$_wp(config)"
+
+ if {[catch {regexp {^[^:/]*} $env(IMAP_SERVER) env(IMAP_SERVER_BASE)}]} {
+ set env(IMAP_SERVER_BASE) ""
+ }
+
+ # in less rigid settings, it might make sense to allow
+ # for random input folder names...
+ # cgi_import Folder
+
+ #
+ # Server, folder and credentials in hand, fork the client...
+ # <OL>
+ # <LI> The session is *assumed* to run over SSL.
+ # <LI> The server is *assumed* to be a black box
+ # (no, possibly hostile, user shells)
+ # <LI> We need to run the alpine process as the given user.
+ # Unless we bind to a specific server, http authentication
+ # isn't sufficient as t
+ #
+ # <LI> The session-id connects future requests to the newly
+ # created alpine engine.
+ # <LI> The auth-cookie will tell us the session-id isn't coming from
+ # j. random cracker's client
+ # </OL>
+ #
+
+ if {[catch {exec [file join $_wp(bin) launch.tcl]} _wp(sessid)]} {
+ WPInfoPage "Internal Error" [font size=+2 $_wp(sessid)] \
+ "Please complain to the [link Admin] and visit the [cgi_link Start] later."
+ return
+ } else {
+ WPValidId $_wp(sessid)
+ }
+
+ if {[catch {cgi_import ssl}] || $ssl == 0} {
+ WPCmd set serverroot $_wp(plainservpath)
+ cgi_root $_wp(plainservpath)
+ }
+
+ # stash login credentials away for later
+ if {[catch {
+ WPCmd set nojs 1
+ WPCmd PESession creds 0 $confloc $User $Pass
+ } result]} {
+ WPInfoPage "Initialization Failure" [font size=+2 "Initialization Failure: $result"] \
+ "Please click your browser's [bold Back] button to return to the [cgi_link Start] to try again..."
+ catch {WPCmd exit}
+ return
+ }
+
+ set cookiepath $_wp(appdir)
+
+ # stash session open parms in alpined's interpreter
+ lappend parms User
+ lappend parms $User
+ lappend parms Server
+ lappend parms $Server
+ lappend parms confloc
+ lappend parms $confloc
+ lappend parms defconf
+ lappend parms $defconf
+ lappend parms startpage
+
+ lappend parms "$_wp(appdir)/$_wp(ui2dir)/browse/0/INBOX"
+ lappend parms prunepage
+ lappend parms ""
+
+ if {[info exists hPx]} {
+ lappend parms hPx
+ lappend parms $hPx
+ }
+
+ if {[catch {WPCmd set wp_open_parms $parms} result]} {
+ WPInfoPage "Internal Error" [font size=+2 $result] \
+ "Please complain to the [link Admin] and visit the [cgi_link Start] later."
+ return
+ }
+
+ # return a page that says we're logging in the user
+ # have that page return to opening the session...
+
+ catch {WPCmd set wp_ver_dir $cookiepath}
+
+ set sessid "$_wp(sessid)@[info hostname]"
+
+ cgi_http_head {
+ WPExportCookie sessid $sessid $cookiepath
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ cgi_http_equiv Refresh "0; url=$_wp(serverpath)/session/logon.tcl?sessid=$sessid"
+ }
+
+ cgi_body {
+ cgi_table height="20%" {
+ cgi_table_row {
+ cgi_table_data {
+ cgi_puts [cgi_nbspace]
+ }
+ }
+ }
+
+ cgi_center {
+ cgi_table border=0 width=500 cellpadding=3 {
+ cgi_table_row {
+ cgi_table_data align=center rowspan=2 {
+ cgi_put [cgi_imglink logo]
+ }
+
+ cgi_table_data rowspan=2 {
+ cgi_put [cgi_img [WPimg dot2] border=0 width=18]
+ }
+
+ cgi_table_data {
+ cgi_puts [cgi_font size=+2 "Logging into $_wp(appname)"]
+ }
+ }
+
+ cgi_table_row {
+ cgi_table_data {
+ cgi_puts "Please be patient! Depending on Inbox size, server load and other factors this may take a moment [cgi_img [WPimg dotblink]]"
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/session/logon.tcl b/web/cgi/session/logon.tcl
new file mode 100755
index 00000000..2d675cf1
--- /dev/null
+++ b/web/cgi/session/logon.tcl
@@ -0,0 +1,169 @@
+#!./tclsh
+# $Id: logon.tcl 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# logon.tcl
+#
+# Purpose: CGI script to authenticate user based on provided
+# credentials and launch them into the mailbox index
+
+# and any global config
+source ./alpine.tcl
+
+# don't use WPEval since it'll mask open's credential failure case
+cgi_eval {
+
+ if {$_wp(debug)} {
+ cgi_debug -on
+ }
+
+ #
+ # Import username and password from pubcookie, if possible.
+ # Otherwise get it from the form that was submitted.
+ #
+ cgi_input
+
+ if {[catch {
+ cgi_import sessid
+ WPValidId $sessid
+ } result]} {
+ WPInfoPage "Web Alpine Error" [font size=+2 "$result"] \
+ "Please complain to the [cgi_link Admin] and visit the [cgi_link Start] later."
+ return
+ }
+
+ if {[catch {WPCmd set wp_open_parms} parms]} {
+ WPInfoPage "Internal Error" [font size=+2 $parms] \
+ "Please complain to the [link Admin] and visit the [cgi_link Start] later."
+ } else {
+ catch {WPCmd unset wp_open_parms}
+
+ foreach {p v} $parms {
+ set $p $v
+ }
+
+ if {[catch {WPCmd PESession open $User $confloc $defconf} answer]} {
+ if {0 == [string length $answer] || 0 == [string compare BADPASSWD [lindex $answer 0]]} {
+ set answer "Unknown Username or Incorrect Password"
+ }
+
+ set alerts {}
+ if {[catch {WPCmd PEInfo statmsgs} statmsgs] == 0} {
+ # display any IMAP alerts
+ foreach m $statmsgs {
+ if {[regexp {^Alert received.*\[ALERT\] (.*)$} $m dummy a]} {
+ if {[lsearch -exact $alerts $a] < 0} {
+ lappend alerts $a
+ }
+ }
+ }
+ }
+
+ WPInfoPage "Login Failure" [font size=+2 $answer] \
+ "Please click your browser's [bold Back] button to return to the [cgi_link Start] to try again..." \
+ {} [join $alerts "<br>"]
+
+ # unlaunch the thing
+ catch {WPCmd PESession close}
+ catch {WPCmd exit}
+ return
+ }
+
+ # determine suitable number of index lines for the indicated display size
+ # based on:
+ #
+ # 1. a header length of 72 pixels
+ # 2. a TD font-size plus padding of 24 points
+ #
+
+ set indexheight [WPCmd PEInfo indexheight]
+ if {[string length $indexheight] == 0} { set indexheight $_wp(indexheight)}
+ if {[info exists hPx] && [regexp {^[0-9]+$} $hPx]} {
+ # "66" comes from _wp(titlethick) + _wp(titlesep) + ((index tables cellpaddings * 2) = 8) + some fudge
+ set indexlines [expr (($hPx - 66) / $indexheight) - 1]
+ }
+
+ if {[info exists indexlines] == 0 || $indexlines <= 0} {
+ set indexlines [WPCmd PEInfo indexlines]
+ }
+
+ if {$indexlines <= 0} {
+ set indexlines $_wp(indexlines)
+ }
+
+ # start with the message indicated by the
+ # 'incoming-startup-rule' in the current index
+ set firstmsg 1
+ if {![catch {WPCmd PEMailbox firstinteresting} firstint] && $firstint > 0} {
+ set messagecount [WPCmd PEMailbox messagecount]
+ for {set i 1} {$i < $messagecount} {incr i $indexlines} {
+ if {$i >= $firstint} {
+ break
+ }
+
+ set firstmsg $i
+ }
+
+ # show whole last page
+ if {$firstmsg + $indexlines > $messagecount} {
+ if {[set n [expr ($messagecount + 1) - $indexlines]] > 0} {
+ set firstmsg $n
+ } else {
+ set firstmsg 1
+ }
+ }
+ }
+
+ if {[catch {WPCmd PEInfo sort} defsort]} {
+ set defsort {Date 0}
+ }
+
+ # set these in alpined's interp so they're fished out by WPImport
+ if {[catch {
+ WPCmd set sort [lindex $defsort 0]
+ WPCmd set rev [lindex $defsort 1]
+ WPCmd set ppg $indexlines
+ WPCmd set width $_wp(width)
+ WPCmd set serverid $Server} result]} {
+ WPInfoPage "Initialization Failure" [font size=+2 $result] \
+ "Please click your browser's [bold Back] button to return to the [cgi_link Start] to try again..."
+ catch {WPCmd PESession close}
+ catch {WPCmd exit}
+ return
+ }
+
+ if {[catch {WPCmd PEMailbox uid $firstmsg} exp]} {
+ set exp 1
+ }
+
+ WPCmd set top $exp
+
+ if {[catch {WPCmd set serverroot} serverroot] == 0} {
+ cgi_root $serverroot
+ }
+
+ set startpage "[cgi_root]/${startpage}?sessid=$sessid"
+
+ if {[string length $prunepage] && [WPCmd PEInfo prunecheck] == 1} {
+ set startpage "[cgi_root]/${prunepage}cid=[WPCmd PEInfo key]&sessid=${sessid}&start=[WPPercentQuote ${startpage}]"
+ }
+
+ cgi_http_head {
+ if {[info exists env(REMOTE_USER)]} {
+ # redirect thru intermediate so session id and secured user name can get bound in uidampper
+ cgi_redirect $_wp(serverpath)/session/startup.tcl?sessid=${sessid}&page=[WPPercentQuote $startpage]
+ } else {
+ cgi_redirect $startpage
+ }
+ }
+ }
+}
diff --git a/web/cgi/session/logout.tcl b/web/cgi/session/logout.tcl
new file mode 100755
index 00000000..698e3558
--- /dev/null
+++ b/web/cgi/session/logout.tcl
@@ -0,0 +1,67 @@
+#!./tclsh
+# $Id: logout.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# Imported args
+set logout_vars {
+ {serverid {} 0}
+ {expinbox {} 0}
+ {expcurrent {} 0}
+ {cid "Missing Command ID"}
+}
+
+# and any global config
+source ./alpine.tcl
+
+WPEval $logout_vars {
+
+ if {$cid != [WPCmd PEInfo key]} {
+ error [list _action Logout "Invalid Operation ID" "Please close this window."]
+ }
+
+ if {$expinbox && [catch {WPCmd PEMailbox expunge inbox} result]} {
+ set logouterr $result
+ }
+
+ if {$expcurrent && [catch {WPCmd PEMailbox expunge current} result]} {
+ if {[info exists logouterr] == 0} {
+ set logouterr $result
+ }
+ }
+
+ if {[catch {WPCmd PESession close} result]} {
+ if {[info exists logouterr] == 0} {
+ set logouterr $result
+ }
+ }
+
+ if {[catch {WPCmd set wp_ver_dir} verdir]} {
+ set verdir $_wp(appdir)
+ }
+
+ catch {WPCmd exit}
+
+ cgi_http_head {
+ set parms "?verdir=${verdir}"
+
+ if {[regexp {^[0-9]+$} $serverid]} {
+ append parms "&serverid=$serverid"
+ }
+
+ if {[info exists logouterr]} {
+ append parms "&logerr=[WPPercentQuote $logouterr]"
+ }
+
+ cgi_redirect $_wp(serverpath)/$_wp(appdir)/farewell.tcl${parms}
+ }
+}
+
diff --git a/web/cgi/session/logout/alpine.tcl b/web/cgi/session/logout/alpine.tcl
new file mode 120000
index 00000000..5ad8d42f
--- /dev/null
+++ b/web/cgi/session/logout/alpine.tcl
@@ -0,0 +1 @@
+../alpine.tcl \ No newline at end of file
diff --git a/web/cgi/session/logout/logout.tcl b/web/cgi/session/logout/logout.tcl
new file mode 100755
index 00000000..39587e4e
--- /dev/null
+++ b/web/cgi/session/logout/logout.tcl
@@ -0,0 +1,51 @@
+#!./tclsh
+
+
+#
+# and any global config
+#
+
+source ./alpine.tcl
+
+cgi_eval {
+
+ cgi_input
+
+ if {[catch {cgi_import serverid}]} {
+ set serverid 0
+ }
+
+ catch {cgi_import logerr}
+
+ cgi_http_head {
+ WPStdHttpHdrs
+
+ # clear cookies
+ cgi_cookie_set sessid=0 expires=now
+ }
+
+ if {[info exists env(REMOTE_USER)]} {
+ set log_text [font class=notice "Protect your privacy![cgi_nl]When you finish, [cgi_url "completely exit your Web browser" http://www.washington.edu/computing/web/logout.html class=notice]."]
+ append log_text "[cgi_nl][cgi_nl]Or you may want to:"
+ append log_text "<center><ul>"
+ if {[catch {cgi_import ppg}]} {
+ set perpage ""
+ } else {
+ set perpage "&ppg=$ppg"
+ }
+
+ append log_text "<li>[cgi_url "restart Web Alpine" "http://alpine.washington.edu"]"
+ append log_text "<li>[cgi_url "go to MyUW" "http://myuw.washington.edu"]"
+ append log_text "</ul></center>"
+ set log_url ""
+ } else {
+ set log_text "Please visit the [cgi_link Start] for a new session."
+ set log_url $_wp(serverpath)/
+ }
+
+ if {[info exists logerr] && [string length $logerr]} {
+ set log_text "[cgi_bold "Please Note"]: A problem, \"$logerr\", occurred while ending your session.<p>${log_text}"
+ }
+
+ WPInfoPage "Logged Out" "[font size=+2 face=Helvetica "Thank you for using Alpine"]" $log_text $log_url
+}
diff --git a/web/cgi/session/logout/tclsh b/web/cgi/session/logout/tclsh
new file mode 120000
index 00000000..385fc6c6
--- /dev/null
+++ b/web/cgi/session/logout/tclsh
@@ -0,0 +1 @@
+../tclsh \ No newline at end of file
diff --git a/web/cgi/session/monitor.tcl b/web/cgi/session/monitor.tcl
new file mode 100755
index 00000000..70edf61f
--- /dev/null
+++ b/web/cgi/session/monitor.tcl
@@ -0,0 +1,282 @@
+#!./tclsh
+# $Id: monitor.tcl 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# monitor.tcl
+
+# read config
+source ./alpine.tcl
+
+proc nicetime {timeoutput} {
+ if {[regexp {^[0-9]+ } $timeoutput msec]} {
+ return "[format {%d.%06d} [expr {$msec / 1000000}] [expr {$msec % 1000000}]] seconds"
+ } else {
+ return $timeoutput
+ }
+}
+
+# take process snapshot
+#set cmd "/bin/ps -lC alpined --sort=cutime"
+set cmd "/bin/ps -auxww --sort=cutime"
+if {[catch "exec $cmd" result]} {
+ set prohdr "ps error: $result"
+ set proclist {}
+} else {
+ set r [split $result "\n"]
+ set prochdr [lindex $r 0]
+ set proclist [lrange $r 1 end]
+}
+
+cgi_eval {
+ cgi_html {
+ cgi_head {
+ cgi_title "Web Alpine Monitor"
+ cgi_puts "<style type='text/css'>"
+ cgi_puts ".monsec { text-decoration: underline ; margin: 4}"
+ cgi_puts "</style>"
+ }
+
+ cgi_body {
+ cgi_h2 "WebPine Status // [info hostname] // [clock format [clock seconds]]"
+
+ ##
+ ## system performance monitor
+ ##n
+ cgi_preformatted {
+ # simple server load
+ set cmd "/usr/ucb/uptime"
+ if {[catch "exec $cmd" result]} {
+ cgi_puts "uptime unavailable: $result"
+ } else {
+ cgi_puts [cgi_span class=monsec "Server uptime"]
+ foreach l [split $result "\n"] {
+ cgi_puts " $l"
+ }
+ }
+
+ cgi_br
+
+ # list alpined adapters
+ foreach l $proclist {
+ if {[regexp $_wp(servlet) $l] || [regexp $_wp(pc_servlet) $l]} {
+ lappend adapters $l
+ }
+ }
+
+ cgi_puts [cgi_span class=monsec "WebPine Adapters ([llength $adapters])"]
+ cgi_puts " $prochdr"
+ foreach l $adapters {
+ cgi_puts " $l"
+ }
+
+ cgi_br
+
+ # tmp disc usage
+ cgi_puts [cgi_span class=monsec "Temp Directory Usage ($_wp(tmpdir))"]
+ set cmd "/bin/df $_wp(tmpdir)"
+ if {[catch "exec $cmd" result]} {
+ cgi_puts "usage unavailable: $result"
+ } else {
+ foreach l [split $result "\n"] {
+ cgi_puts " $l"
+ }
+ }
+
+ cgi_br
+
+ # detach staging usage
+ cgi_puts [cgi_span class=monsec "Detach Staging Usage ($_wp(tmpdir))"]
+ set cmd "/bin/df $_wp(detachpath)"
+ if {[catch "exec $cmd" result]} {
+ cgi_puts "usage unavailable: $result"
+ } else {
+ foreach l [split $result "\n"] {
+ cgi_puts " $l"
+ }
+ }
+
+ if {[info exists report_env]} {
+ cgi_br
+
+ cgi_puts [cgi_span class=monsec "Environment:"]
+
+ set cgiv {
+ SERVER_SOFTWARE
+ SERVER_NAME
+ GATEWAY_INTERFACE
+ SERVER_PROTOCOL
+ SERVER_PORT
+ REQUEST_METHOD
+ PATH_INFO
+ PATH_TRANSLATED
+ SCRIPT_NAME
+ QUERY_STRING
+ REMOTE_HOST
+ REMOTE_ADDR
+ AUTH_TYPE
+ REMOTE_USER
+ REMOTE_IDENT
+ CONTENT_TYPE
+ CONTENT_LENGTH
+ HTTP_ACCEPT
+ HTTP_USER_AGENT
+ }
+ foreach v $cgiv {
+ if {[info exists env($v)]} {
+ cgi_puts " $v: $env($v)"
+ }
+ }
+ }
+
+
+ ##
+ ## session specific feedback
+ ##
+ if {[info exists _wp(monitors)]
+ && [info exists env(REMOTE_USER)]
+ && [lsearch -exact $_wp(monitors) $env(REMOTE_USER)] >= 0} {
+
+ cgi_br
+
+ cgi_puts [cgi_span class=monsec "Kerberos ticket cache info"]
+ foreach l [glob "[file join $_wp(tmpdir) krb]*"] {
+ set file [file join $_wp(tmpdir) $l]
+ cgi_put " [exec /bin/ls -l $file]"
+ if {[catch {expr {[clock seconds] - [file mtime $file]}} d]} {
+ } else {
+ cgi_puts " ([expr {$d / 3600}] hours, [expr {($d % 3600) / 60}] minutes old)"
+ }
+ }
+
+ cgi_br
+
+ cgi_puts [cgi_span class=monsec "uid_mapper Process"]
+ # Condition of uid_mapper
+ cgi_puts " $prochdr"
+ foreach l $proclist {
+ if {[regexp uidmapper $l]} {
+ lappend umlist $l
+ }
+ }
+
+ if {[info exists umlist]} {
+ foreach l $umlist {
+ cgi_puts " $l"
+ }
+ } else {
+ cgi_puts " HELP!!! NO UIDMAPPER RUNNING!!!"
+ }
+
+ cgi_br
+
+ if {[info exists _wp(hosts)] && [llength $_wp(hosts)]} {
+ cgi_puts [cgi_span class=monsec "Session Performance (netid: $env(REMOTE_USER))"]
+
+ set sdata [lindex $_wp(hosts) 0]
+ set User $env(REMOTE_USER)
+ set env(IMAP_SERVER) "[subst [lindex $sdata 1]]/user=$env(REMOTE_USER)"
+
+ if {[llength $sdata] > 2 && [string length [lindex $sdata 2]]} {
+ set defconf [subst [lindex $sdata 2]]
+ set confloc "\{$env(IMAP_SERVER)\}$_wp(config)"
+ cgi_puts " User Config: $confloc"
+
+ # launch session
+ cgi_put " alpined Launch: "
+ set ct [time {
+ if {[catch {exec [file join $_wp(bin) launch.tcl]} _wp(sessid)]} {
+ set err "FAILURE: $_wp(sessid)"
+ } else {
+ WPValidId $_wp(sessid)
+ }
+ }]
+
+ if {[info exists err]} {
+ cgi_puts $err
+ } else {
+ cgi_puts [nicetime $ct]
+
+ cgi_put " Open Inbox: "
+ set ct [time {
+ if {[catch {WPCmd PESession open $env(REMOTE_USER) "" $confloc $defconf} answer]} {
+ set err "FAILURE: "
+ if {[info exists answer]} {
+ if {[string length $answer] == 0} {
+ append err "Unknown Username or Incorrect Password"
+ } else {
+ append err $answer
+ }
+ } else {
+ append err "Unknown reason"
+ }
+ }
+ }]
+
+ if {[info exists err]} {
+ cgi_puts $err
+ } else {
+ cgi_puts [nicetime $ct]
+
+ cgi_put " Fetch First Message: "
+
+ set ct [time {
+ if {[catch {
+ set msg [WPCmd PEMailbox first]
+ set uid [WPCmd PEMailbox uid $msg]
+ set txt [WPCmd PEMessage $uid text]
+ } txt]} {
+ set err $txt
+ }
+ }]
+
+ if {[info exists err]} {
+ cgi_puts "FAILURE: $err"
+ } else {
+ cgi_puts [nicetime $ct]
+
+ cgi_put " Fetch Last Message: "
+
+ set ct [time {
+ if {[catch {
+ set msg [WPCmd PEMailbox last]
+ set uid [WPCmd PEMailbox uid $msg]
+ set txt [WPCmd PEMessage $uid text]
+ } txt]} {
+ set err $txt
+ }
+ }]
+
+ if {[info exists err]} {
+ cgi_puts "FAILURE: $err"
+ } else {
+ cgi_puts [nicetime $ct]
+ }
+ }
+ }
+
+ set ct [time {
+ catch {WPCmd PESession close}
+ catch {WPCmd exit}
+ }]
+
+ cgi_puts " Close Session: [nicetime $ct]"
+ }
+ } else {
+ cgi_puts "Invalid host configuration"
+ }
+
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/web/cgi/session/queryauth.tcl b/web/cgi/session/queryauth.tcl
new file mode 100755
index 00000000..27e10c97
--- /dev/null
+++ b/web/cgi/session/queryauth.tcl
@@ -0,0 +1,120 @@
+#!./tclsh
+# $Id: queryauth.tcl 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# queryauth.tcl
+#
+# Purpose: CGI script to generate html form used to ask for authentication
+# credentials
+
+# input:
+set query_vars {
+ {cid "Missing Command ID"}
+ {authcol "Missing Authenticaion Collection"}
+ {authfolder "Missing Authentication Folder"}
+ {authpage "No Post Authorization Instructions"}
+ {authcancel "No Auth Cancel Instructions"}
+ {authuser "" ""}
+ {reason "" ""}
+}
+
+# Output:
+#
+# HTML/Javascript/CSS data representing the message specified
+# by the 'uid' argument
+
+
+# inherit global config
+source ./alpine.tcl
+source ../$_wp(appdir)/$_wp(ui1dir)/cmdfunc.tcl
+
+
+set query_menu {
+ {
+ {}
+ {
+ {
+ # * * * * Help * * * *
+ cgi_put "Get Help"
+ }
+ }
+ }
+}
+
+
+WPEval $query_vars {
+
+ if {$cid != [WPCmd PEInfo key]} {
+ error [list _action open "Invalid Operation ID" "Click Back button to try again."]
+ }
+
+ cgi_http_head {
+ WPStdHttpHdrs
+ }
+
+ cgi_html {
+ cgi_head {
+ WPStdHtmlHdr "Authentication Credentials"
+ WPStyleSheets
+ }
+
+ if {[string length $authuser]} {
+ set onload "onLoad=document.auth.pass.focus()"
+ } else {
+ set onload "onLoad=document.auth.user.focus()"
+ }
+
+ cgi_body BGCOLOR="$_wp(bordercolor)" $onload {
+ cgi_form $_wp(serverpath)/session/setauth.tcl method=post enctype=multipart/form-data name=auth target=_top {
+ cgi_text "sessid=$sessid" type=hidden notab
+ cgi_text "cid=$cid" type=hidden notab
+ cgi_text "authcol=$authcol" type=hidden notab
+ cgi_text "authfolder=$authfolder" type=hidden notab
+ cgi_text "authpage=$authpage" type=hidden notab
+ cgi_text "authcancel=$authcancel" type=hidden notab
+
+ cgi_table border=0 cellspacing=0 cellpadding=2 width="100%" height="100%" {
+ cgi_table_row {
+ eval {
+ cgi_table_data $_wp(menuargs) {
+ WPTFCommandMenu query_menu {}
+ }
+ }
+
+ cgi_table_data valign=top class=dialog {
+ cgi_division align=center class=dialog "style=\"padding:30 12%\"" {
+ if {[info exists reason] && [string compare BADPASSWD [string range $reason 0 8]]} {
+ cgi_puts $reason
+ } else {
+ cgi_puts "Login Required"
+ }
+ }
+
+ cgi_center {
+ cgi_puts [cgi_font size=+1 class=dialog "Username: "]
+ cgi_text user=$authuser maxlength=30 size=25%
+ cgi_br
+ cgi_br
+ cgi_puts [cgi_font size=+1 class=dialog "Password: "]
+ cgi_text pass= type=password maxlength=30 size=25%
+ cgi_br
+ cgi_br
+ cgi_submit_button auths=Login
+ cgi_submit_button cancel=Cancel
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/web/cgi/session/setauth.tcl b/web/cgi/session/setauth.tcl
new file mode 100755
index 00000000..c2da7a6b
--- /dev/null
+++ b/web/cgi/session/setauth.tcl
@@ -0,0 +1,68 @@
+#!./tclsh
+# $Id: setauth.tcl 764 2007-10-23 23:44:49Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# setauth.tcl
+#
+# Purpose: CGI script to generate html form used to ask for authentication
+# credentials
+
+# Input:
+set auth_vars {
+ {cid "Missing Command ID"}
+ {authcol "No Authorization Collection"}
+ {authfolder "No Authorization Folder"}
+ {authpage "No Post Authorization Instructions"}
+ {authcancel "No Auth Cancel Instructions"}
+ {auths "" 0}
+ {user "" 0}
+ {pass "" 0}
+ {cancel "" 0}
+}
+
+# Output:
+#
+# Redirect to specified post-authentication page
+
+# inherit global config
+source ./alpine.tcl
+
+
+WPEval $auth_vars {
+
+ if {$cid != [WPCmd PEInfo key]} {
+ error [list _action open "Invalid Operation ID" "Click Back button to try again."]
+ }
+
+ # if NOT cancelled
+ if {[string compare $auths "Login"] == 0
+ && [string length $user]
+ && [catch {WPCmd PESession creds $authcol $authfolder $user $pass}] == 0} {
+ set redirect $authpage
+ } else {
+ set redirect $authcancel
+ }
+
+ cgi_http_head {
+ # redirect to the place we stuffed the export info. use the ip address
+ # to foil spilling any session cookies or the like
+
+ if {[info exists env(SERVER_PROTOCOL)] && [regexp {[Hh][Tt][Tt][PP]/([0-9]+)\.([0-9]+)} $env(SERVER_PROTOCOL) m vmaj vmin] && $vmaj >= 1 && $vmin >= 1} {
+ cgi_puts "Status: 303 Temporary Redirect"
+ } else {
+ cgi_puts "Status: 302 Redirected"
+ }
+
+ cgi_puts "URI: $redirect"
+ cgi_puts "Location: $redirect"
+ }
+}
diff --git a/web/cgi/session/setauth2.tcl b/web/cgi/session/setauth2.tcl
new file mode 100755
index 00000000..ccb4ce17
--- /dev/null
+++ b/web/cgi/session/setauth2.tcl
@@ -0,0 +1,58 @@
+#!./tclsh
+# $Id: setauth2.tcl 391 2007-01-25 03:53:59Z mikes@u.washington.edu $
+# ========================================================================
+# Copyright 2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# setauth2.tcl
+#
+# Purpose: CGI script to accept user authorization
+# credentials via xmlHttpRequest
+
+# Input:
+set auth_vars {
+ {c "No Authorization Collection"}
+ {f "No Authorization Folder"}
+ {auths "" 0}
+ {user "" 0}
+ {pass "" 0}
+ {cancel "" 0}
+}
+
+# Output:
+#
+
+# inherit global config
+source ./alpine.tcl
+
+# Import data validate it and get session id
+if {[catch {WPGetInputAndID sessid}]} {
+ return
+}
+
+# grok parameters
+foreach item $auth_vars {
+ if {[catch {eval WPImport $item} errstr]} {
+ WPInfoPage "Web Alpine Error" [font size=+2 $errstr] "Please close this window."
+ return
+ }
+}
+
+cgi_puts "Content-type: text/html; charset=\"UTF-8\"\n"
+set answer "Problem setting authorization credentials"
+
+
+if {[string compare $auths "Login"] == 0
+ && [string length $user]
+ && [catch {WPCmd PESession creds $c $f $user $pass} answer] == 0} {
+ cgi_puts "$answer"
+} else {
+ cgi_puts "Cannot accept login: $answer"
+}
diff --git a/web/cgi/session/setpassphrase.tcl b/web/cgi/session/setpassphrase.tcl
new file mode 100755
index 00000000..b4d25e26
--- /dev/null
+++ b/web/cgi/session/setpassphrase.tcl
@@ -0,0 +1,52 @@
+#!./tclsh
+# $Id: setpassphrase.tcl 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# setpassphrase.tcl
+#
+# Purpose: CGI script to accept user passphrase
+# via xmlHttpRequest
+
+# Input:
+set auth_vars {
+ {auths "" 0}
+ {pass "" 0}
+ {cancel "" 0}
+}
+
+# Output:
+#
+
+# inherit global config
+source ./alpine.tcl
+
+# Import data validate it and get session id
+if {[catch {WPGetInputAndID sessid}]} {
+ return
+}
+
+# grok parameters
+foreach item $auth_vars {
+ if {[catch {eval WPImport $item} errstr]} {
+ WPInfoPage "Web Alpine Error" [font size=+2 $errstr] "Please close this window."
+ return
+ }
+}
+
+cgi_puts "Content-type: text/html; charset=\"UTF-8\"\n"
+set answer "Problem setting passphrase"
+
+if {[string compare $auths "Smime"] != 0
+ || [string length $pass] == 0
+ || [catch {WPCmd PESession setpassphrase $pass} answer]} {
+ cgi_puts "Cannot accept passphrase: $answer"
+}
diff --git a/web/cgi/session/startup.tcl b/web/cgi/session/startup.tcl
new file mode 100755
index 00000000..9e3feec8
--- /dev/null
+++ b/web/cgi/session/startup.tcl
@@ -0,0 +1,33 @@
+#!./tclsh
+# $Id: startup.tcl 764 2007-10-23 23:44:49Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# read config
+source ./alpine.tcl
+
+# Input: page
+
+# Output: redirection to given page
+
+cgi_eval {
+ cgi_input
+
+ if {[catch {cgi_import page}]} {
+ WPInfoPage "Bogus Page Request" \
+ "[font size=+2 "Invalid Page Request!"]" \
+ "Please click your browser's [bold Back] button to return to the [cgi_link Start]"
+ } else {
+ cgi_http_head {
+ cgi_redirect $page
+ }
+ }
+} \ No newline at end of file
diff --git a/web/cgi/session/tclsh b/web/cgi/session/tclsh
new file mode 120000
index 00000000..385fc6c6
--- /dev/null
+++ b/web/cgi/session/tclsh
@@ -0,0 +1 @@
+../tclsh \ No newline at end of file
diff --git a/web/cgi/sounds/ding.wav b/web/cgi/sounds/ding.wav
new file mode 100644
index 00000000..5741f232
--- /dev/null
+++ b/web/cgi/sounds/ding.wav
Binary files differ
diff --git a/web/cgi/sounds/mail_msg.wav b/web/cgi/sounds/mail_msg.wav
new file mode 100644
index 00000000..d6370e0a
--- /dev/null
+++ b/web/cgi/sounds/mail_msg.wav
Binary files differ
diff --git a/web/cgi/tclsh b/web/cgi/tclsh
new file mode 120000
index 00000000..922d6309
--- /dev/null
+++ b/web/cgi/tclsh
@@ -0,0 +1 @@
+../bin/tclsh \ No newline at end of file
diff --git a/web/config/alpine.tcl b/web/config/alpine.tcl
new file mode 100644
index 00000000..531dafaf
--- /dev/null
+++ b/web/config/alpine.tcl
@@ -0,0 +1,1148 @@
+# Web Alpine Config options
+# $Id: alpine.tcl 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $
+# ========================================================================
+# Copyright 2006-2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+encoding system "utf-8"
+
+set _wp(appname) Alpine
+set _wp(admin) admin@sample-domain.edu
+set _wp(helpdesk) admin@sample-domain.edu
+set _wp(comments) help@sample-domain.edu
+
+# List of userid's allowed to request the monitor script output
+set _wp(monitors) {}
+
+# directory prefix web server uses for web alpine page requests
+# Note: set to {} if DocumentRoot set to the root of web alpine cgi scripts
+set _wp(urlprefix) webmail
+
+# file system path to CGI application files
+# directory containing web alpine application scripts and supporting tools
+set _wp(fileroot) /usr/local/libexec/alpine
+
+set _wp(tmpdir) /tmp
+
+# NOTE: Make SURE tclsh and alpine.tcl symlinks in this directory
+set _wp(cgipath) [file join $_wp(fileroot) cgi]
+
+# CGI scripts implementing U/I, session cookie scope
+set _wp(appdir) alpine
+
+# UI versions
+set _wp(ui1dir) 1.0
+set _wp(ui2dir) 2.0
+
+# place for CGI scripts not requiring session-key
+set _wp(pubdir) pub
+
+# place for binaries referenced by the CGI scripts
+set _wp(bin) [file join $_wp(fileroot) bin]
+set _wp(servlet) alpined
+set _wp(pc_servlet) pc_alpined
+set _wp(pldap) alpineldap
+
+# place for config files referenced by the CGI scripts
+set _wp(confdir) [file join $_wp(fileroot) config]
+set _wp(conffile) pine.conf
+set _wp(defconf) $_wp(conffile)
+
+# place for library files used by CGI scripts
+set _wp(lib) [file join $_wp(fileroot) lib]
+
+# directory used temporarily to stage attatched and detached files
+set _wp(detachpath) [file join $_wp(fileroot) detach]
+
+set _wp(imagepath) [file join / $_wp(urlprefix) images]
+
+set _wp(buttonpath) [file join $_wp(imagepath) buttons silver]
+
+set _wp(staticondir) env
+
+set _wp(servername) [info hostname]
+
+# MUST specify SSL/TLS connection
+set _wp(serverport) {}
+set _wp(serverpath) https://[file join [join [eval list $_wp(servername) $_wp(serverport)] :] $_wp(urlprefix)]
+
+# MAY specify a plaintext connection (comment out if plain support undesired)
+set _wp(plainport) {}
+set _wp(plainservpath) http://[file join [join [eval list $_wp(servername) $_wp(plainport)] :] $_wp(urlprefix)]
+
+# url of faq page(s) available from initial greeting page
+#set _wp(faq) "http://www.yourserver/faqs/alpine.html"
+
+# url of informational page accessible from initial greeting page
+set _wp(releaseblurb) "$_wp(plainservpath)/alpine/help/release.html"
+
+# url of previous version server to be accessible from initial greeting page
+#set _wp(oldserverpath) "https://previous.version.server.edu:444/"
+
+# session id length: make sure the integer count below matches what's built
+# into the pubcookie: "src/pubcookie/wp_uidmapper_lib.h:#define WP_KEY_LEN 6"
+set _wp(sessidlen) 6
+
+# Where and what format the alpined comm socket should take
+set _wp(sockdir) $_wp(tmpdir)
+set _wp(sockpat) wp%s
+
+# skin settings
+set _wp(bordercolor) #FEFAC9
+set _wp(menucolor) #3E2E6D
+set _wp(dialogcolor) #FEFAC9
+set _wp(titlecolor) #000000
+set _wp(logodir) alpine
+
+# various timerouts, dimensions and feature settings
+set _wp(refresh) 600
+set _wp(timeout) 900
+set _wp(autodraft) 300
+set _wp(logoutpause) 60
+set _wp(indexlines) 20
+set _wp(indexlinesmax) 50
+set _wp(indexheight) 24
+set _wp(navheight) 28
+set _wp(width) 80
+set _wp(titleheight) 34
+set _wp(titlesep) 4
+set _wp(config) remote_pinerc
+set _wp(motd) motd
+set _wp(save_cache_max) 6
+set _wp(fldr_cache_max) 20
+set _wp(fldr_cache_def) 3
+set _wp(statushelp) 0
+set _wp(imgbuttons) 0
+set _wp(keybindings) 1
+set _wp(dictionary) 0
+set _wp(debug) 0
+set _wp(cmdtime) 0
+set _wp(evaltime) 0
+set _wp(menuargs) {width="112" nowrap valign=top}
+set _wp(ispell) /usr/local/bin/ispell
+
+# Yahoo! User Interface Library location
+#set _wp(yui) $_wp(serverpath)/$_wp(appdir)/$_wp(ui2dir)/lib/yui
+set _wp(yui) "http://yui.yahooapis.com/2.7.0"
+
+# usage reporter - input: username as first command line argument
+# output: space separated integers usage and total
+#set _wp(usage) $_wp(bin)/usage.tcl
+#set _wp(usage_link) "https://uwnetid.washington.edu/disk/"
+
+# limit uploads to 1 file at a time, maximum 20MB.
+set _wp(uplim_files) 1
+set _wp(uplim_bytes) 20000000
+
+# verify sessid from consistent REMOTE_ADDR (set to 0 for proxying clusters)
+set _wp(hostcheck) 0
+
+# set to list of domains for which ssl is NOT required
+#set _wp(ssl_safe_domains) {}
+
+# set to list of address blocks or ranges for which ssl is NOT required
+#set _wp(ssl_safe_addrs) {}
+
+# set this value to zero to turn OFF ssl by default
+set _wp(ssl_default) 1
+
+# allow connecting user to specify imap server on greeting page
+set _wp(flexserver) 1
+
+# make sure tmp files and such are ours alone to read/write
+catch {exec umask 044}
+
+#fix up indexheight so it isn't too high or too low
+set _wp(indexheight) [expr {$_wp(indexheight) <= 20 ? 20 : $_wp(indexheight) >= 30 ? 30 : $_wp(indexheight)}]
+
+# SPAM reporting facility, if set "Report Spam" button appears at top of View Page
+#set _wp(spamaddr) spamaddr@sample-domain.edu
+#set _wp(spamfolder) junk-mail
+#set _wp(spamsubj) "ATTACHED SPAM"
+
+# external mail filter config link
+#set _wp(filter_link) http://delivery-filter.sample-domain.edu/filter/config
+
+# external vacation config link
+#set _wp(vacation_link) http://vacation.sample-domain.edu/vacation/config
+
+#
+# Nickname server bindings. If not present, prompt for the
+# destination of the default pinerc location.
+#
+set _wp(hosts) {
+ {
+ Deskmail
+ $User.deskmail.washington.edu/ssl
+ $_wp(confdir)/conf.deskmail
+ }
+}
+
+# Everybody inherits the cgi, comm packages
+lappend auto_path $_wp(lib)
+
+package require cgi
+package require WPComm
+
+# Recipient of bad news bubbling up from cgi.tcl...
+cgi_admin_mail_addr $_wp(admin)
+
+cgi_sendmail {}
+
+#cgi_mail_relay localhost
+cgi_mail_relay smtpserver.sample-domain.edu
+
+# set permissions for owner-only handling
+cgi_tmpfile_permissions 0640
+
+#set upload limits
+cgi_file_limit $_wp(uplim_files) $_wp(uplim_bytes)
+
+# universal body tag parameters
+cgi_body_args link=#0000FF vlink=#000080 alink=#FF0000 marginwidth=0 marginheight=0 topmargin=0 leftmargin=0
+
+# Common Images Image definitions
+cgi_imglink logo [file join $_wp(imagepath) logo $_wp(logodir) big.gif] border=0 "alt=Web Alpine"
+cgi_imglink smalllogo [file join $_wp(imagepath) logo $_wp(logodir) small.gif] border=0 "alt=About Web Alpine"
+cgi_imglink background [file join $_wp(imagepath) logo $_wp(logodir) back.gif] border=0 align=top
+cgi_imglink dot [file join $_wp(imagepath) dot2.gif] border=0 align=top
+cgi_imglink increas [file join $_wp(imagepath) increas4.gif] border=0 align=absmiddle
+cgi_imglink decreas [file join $_wp(imagepath) decreas4.gif] border=0 align=absmiddle
+cgi_imglink expand [file join $_wp(imagepath) b_plus.gif] border=0 "alt=Expand" height=9 width=9
+cgi_imglink contract [file join $_wp(imagepath) b_minus.gif] border=0 "alt=Collapse" height=9 width=9
+cgi_imglink fullhdr [file join $_wp(imagepath) hdr.gif] border=0 "alt=Full Header"
+cgi_imglink nofullhdr [file join $_wp(imagepath) hdrnon.gif] border=0 "alt=Digested Header"
+cgi_imglink bang [file join $_wp(imagepath) caution.gif] border=0 "alt=!"
+cgi_imglink postmark [file join $_wp(imagepath) postmark.gif] border=0 "alt=New Mail"
+cgi_imglink gtab [file join $_wp(imagepath) tabs gtab.gif] border=0 align=top
+cgi_imglink gdtab [file join $_wp(imagepath) tabs gdtab.gif] border=0 align=top
+cgi_imglink abtab [file join $_wp(imagepath) tabs abtab.gif] border=0 align=top
+cgi_imglink abdtab [file join $_wp(imagepath) tabs abdtab.gif] border=0 align=top
+cgi_imglink ctab [file join $_wp(imagepath) tabs ctab.gif] border=0 align=top
+cgi_imglink cdtab [file join $_wp(imagepath) tabs cdtab.gif] border=0 align=top
+cgi_imglink ftab [file join $_wp(imagepath) tabs ftab.gif] border=0 align=top
+cgi_imglink fdtab [file join $_wp(imagepath) tabs fdtab.gif] border=0 align=top
+cgi_imglink mltab [file join $_wp(imagepath) tabs mltab.gif] border=0 align=top
+cgi_imglink mldtab [file join $_wp(imagepath) tabs mldtab.gif] border=0 align=top
+cgi_imglink mvtab [file join $_wp(imagepath) tabs mvtab.gif] border=0 align=top
+cgi_imglink mvdtab [file join $_wp(imagepath) tabs mvdtab.gif] border=0 align=top
+cgi_imglink rtab [file join $_wp(imagepath) tabs rtab.gif] border=0 align=top
+cgi_imglink rdtab [file join $_wp(imagepath) tabs rdtab.gif] border=0 align=top
+
+
+# Link definitions
+cgi_link Admin "Web Alpine Administrator" "mailto:$_wp(admin)"
+cgi_link Start "Web Alpine Home Page" "$_wp(serverpath)/session/greeting.tcl" target=_top
+
+# Internally referenced CGI directory root
+cgi_root $_wp(serverpath)
+cgi_suffix .tcl
+
+# have cgi.tcl convert eols in muiltipart/form-data
+set _cgi(no_binary_upload) 1
+
+proc WPSocketName {sessid} {
+ global _wp
+
+ return [file join $_wp(sockdir) [format $_wp(sockpat) $sessid]]
+}
+
+proc WPValidId {{sessid {}}} {
+ global _wp env
+
+ if {[string length $sessid] == 0} {
+ set created 1
+
+ # Session Handle: a bit reasonably random number. the format
+ # is convenient for pubcookie auth'd support
+ set rnum {}
+ set rsrc /dev/urandom
+ set idbytelength [expr {$_wp(sessidlen) * 4}]
+ if {[file readable $rsrc] && [catch {open $rsrc r} fp] == 0} {
+ while {1} {
+ for {set i 0} {$i < $idbytelength} {incr i} {
+ if {$i && ($i % 4) == 0} {
+ append rnum "."
+ }
+
+ if {[catch {read $fp 1} n] == 0} {
+ binary scan $n c x
+ set x [expr ($x & 0xff)]
+ append rnum [format {%02x} $x]
+ } else {
+ set rnum {}
+ break
+ }
+ }
+
+ if {[file exists [WPSocketName $rnum]]} {
+ set rnum {}
+ } else {
+ break
+ }
+ }
+
+ close $fp
+ }
+
+ # second choice for random numbers
+ if {[string length $rnum] == 0} {
+ expr srand([clock seconds])
+ for {set i 0} {$i < $idbytelength} {incr i 4} {
+ if {$i && ($i % 4) == 0} {
+ append rnum "."
+ }
+
+ append rnum [format {%08x} [expr int((100000000 * rand()))]]
+ }
+ }
+
+ # generate a session ID
+ set _wp(sessid) $rnum
+ } else {
+ set sessidparts [split $sessid {@}]
+ switch [llength $sessidparts] {
+ 1 {
+ set _wp(sessid) $sessid
+ }
+ 2 {
+ if {[string compare [string tolower [lindex $sessidparts 1]] [string tolower [info hostname]]]} {
+ regexp {^([a-zA-Z]*://).*} [cgi_root] match proto
+ error [list redirect "${proto}[lindex $sessidparts 1]:$env(SERVER_PORT)?$env(QUERY_STRING)"]
+ } else {
+ set _wp(sessid) [lindex $sessidparts 0]
+ }
+ }
+ default {
+ error "Malformed Session ID: $sessid"
+ }
+ }
+ }
+
+ set _wp(sockname) [WPSocketName $_wp(sessid)]
+
+ if {[info exists _wp(cumulative)]} {
+ rename WPCmd WPCmd.orig
+ rename WPCmdTimed WPCmd
+ }
+
+ if {[info exists _wp(hostcheck)] && $_wp(hostcheck) == 1 && ![info exists created]
+ && [catch {WPCmd set wp_client} client] == 0
+ && (([info exists env(REMOTE_ADDR)] && [string length $env(REMOTE_ADDR)] && [string compare $client $env(REMOTE_ADDR)])
+ || ([info exists env(REMOTE_HOST)] && [string length $env(REMOTE_HOST)] && [string compare $client $env(REMOTE_HOST)]))} {
+ error "Request from unrecognized client"
+ }
+}
+
+proc WPAbort {} {
+ WPCleanup
+ cgi_exit
+}
+
+proc WPCleanup {} {
+ global _wp
+
+ if {[info exists _wp(cleanup)]} {
+ foreach item $_wp(cleanup) {
+ catch {eval $item}
+ }
+ }
+}
+
+proc WPEval {vars cmd} {
+ global _wp
+
+ if {$_wp(cmdtime) || $_wp(evaltime)} {
+ set _wp(cumulative) 0
+ }
+
+ set _wp(cmd) $cmd
+ set _wp(vars) [linsert $vars 0 [list sessid "Missing Session ID"]]
+
+ uplevel 1 {
+ cgi_eval {
+ if {$_wp(debug) > 1} {
+ cgi_debug -on
+ }
+
+ # Session id?
+ if {[catch {WPGetInputAndID sessid}]} {
+ return
+ }
+
+ foreach item $_wp(vars) {
+ if {[catch {eval WPImport $item} errstr]} {
+ WPInfoPage "Web Alpine Error" [font size=+2 $errstr] "Please close this window."
+ return
+ }
+ }
+
+ # evaluate the given script
+ if {[catch {cgi_buffer $_wp(cmd)} result]} {
+
+ reset_cgi_state
+
+ if {[string index $result 0] == "_"} {
+ switch -- [lindex $result 0] {
+ _info {
+ WPInfoPage [lindex $result 1] [font size=+2 [lindex $result 2]] [lindex $result 3]
+ }
+ _action {
+ switch -regexp -- [lindex $result 2] {
+ "[Ii]nactive [Ss]ession" {
+ WPInactivePage
+ }
+ default {
+ if {[string length [lindex $result 3]]} {
+ set remedy [lindex $result 3]
+ } else {
+ set remedy " Click your browser's Back button to return to previous page."
+ }
+ WPInfoPage "[lindex $result 1] Error" [font size=+2 [lindex $result 2]] \
+ "Please report this to the [cgi_link Admin].$remedy"
+ }
+ }
+ }
+ _redirect {
+ cgi_http_head {
+ cgi_redirect [lindex $result 1]
+ }
+
+ cgi_html { cgi_body {} }
+ }
+ _close {
+ if {[string length [lindex $result 1]] == 0} {
+ set result "Indeterminate error"
+ }
+
+ WPInfoPage "Web Alpine Error" [font size=+2 [lindex $result 1]] "Please close this window."
+ }
+ default {
+ if {[string length $result]} {
+ WPInfoPage "Web Alpine Error" [font size=+2 "Eval Error: $result"] \
+ "Please complain to the [cgi_link Admin]. Click Back button to return to previous page."
+ } else {
+ WPInfoPage "Web Alpine Error" [font size=+2 "Indeterminate error response"] \
+ "Please complain to the [cgi_link Admin] and click Back button to return to previous page."
+ }
+ }
+ }
+ } else {
+ if {[regexp {[Ii]nactive [Ss]ession} $result]} {
+ WPInactivePage
+ } else {
+ WPInfoPage "Web Alpine Error" [font size=+2 "Error: $result"] \
+ "Please report this to the [cgi_link Admin]. Try clicking your browser's Back button to return to a working page."
+ }
+ }
+ } else {
+ catch {cgi_puts $result}
+ }
+ }
+ }
+
+ # cleanup here
+ WPCleanup
+
+ if {[info exists _wp(cumulative)]} {
+ WPdebug "Cumulative Eval: $_wp(cumulative)"
+ unset _wp(cumulative)
+ }
+}
+
+proc WPGetInputAndID {_sessid} {
+ global _wp
+ upvar $_sessid sessid
+
+ # Import data and validate it
+ if {[catch {cgi_input "sessid=8543949466398&"} result]} {
+ WPInfoPage "Web Alpine Error" [font size=+2 $result] "Please close this window."
+ error "Cannot get CGI Input"
+ }
+
+ if {[catch {WPImport sessid "Missing Session ID"} errstr]} {
+ if {[regexp {.*sessid.*no such.*} $errstr]} {
+ WPInactivePage [list "Your browser may have failed to send the necessary <i>cookie</i> information. Please verify your browser configuration has cookies enabled."]
+ } else {
+ WPInfoPage "Web Alpine Error" [font size=+2 $errstr] "Please close this window."
+ }
+
+ error "Session ID Failure"
+ } else {
+ # initialization here
+ if {[catch {WPValidId $sessid} result]} {
+ if {[string compare [lindex $result 0] redirect]} {
+ WPInfoPage "Web Alpine Error" [font size=+2 "$result"] \
+ "Please complain to the [cgi_link Admin] and visit the [cgi_link Start] later."
+ } else {
+ cgi_http_head {
+ cgi_redirect [lindex $result 1]
+ }
+ }
+
+ error "Unrecoverable Error"
+ } elseif {$_wp(sessid) == 0} {
+ WPInactivePage
+ error "Inactive Session"
+ }
+
+ if {[catch {WPCmd set serverroot} serverroot] == 0} {
+ cgi_root $serverroot
+ }
+ }
+}
+
+proc WPCmdEval {args} {
+ return [eval $args]
+}
+
+proc WPCmd {args} {
+ global _wp
+
+ return [WPSend $_wp(sockname) $args]
+}
+
+proc WPCmdTimed {args} {
+ global _wp
+
+ set t [lindex [time {set r [WPSend $_wp(sockname) $args]}] 0]
+ incr _wp(cumulative) $t
+
+ if {$_wp(cmdtime)} {
+ WPdebug "time $t : $args"
+ }
+
+ return $r
+}
+
+proc WPLoadCGIVar {_var} {
+ upvar $_var var
+
+ if {[catch {cgi_import_as $_var var} result]
+ && [catch {WPCmd set $_var} var]
+ && [catch {cgi_import_cookie_as $_var var} result]} {
+ error [list _action "Import Cookie $_var" $result]
+ }
+}
+
+proc WPLoadCGIVarAs {_var _varas} {
+ upvar $_varas varas
+
+ if {[catch {cgi_import_as $_var varas} result]
+ && [catch {WPCmd set $_var} varas]
+ && [catch {cgi_import_cookie_as $_var varas} result]} {
+ set varas ""
+ }
+}
+
+proc WPImport {valname {errstring ""} {default 0}} {
+ upvar $valname val
+
+ if {[catch {cgi_import_as $valname val} result]} {
+ if {[catch {WPCmd set $valname} val]} {
+ if {[catch {cgi_import_cookie_as $valname val} result]} {
+ if {[string length $errstring] > 0} {
+ error "$errstring: $result"
+ } else {
+ set val $default
+ }
+ }
+ }
+ }
+}
+
+
+proc WPExportCookie {name value {scope ""}} {
+ global _wp
+
+ cgi_cookie_set $name=$value "path=[file join / $_wp(urlprefix) $scope]"
+}
+
+
+# handle dynamic sizing of images showing thread relationships
+proc WPThreadImageLink {t h} {
+ global _wp
+
+ return "<img src=\"[file join $_wp(imagepath) ${t}.gif]\" border=0 align=top height=${h} width=14>"
+}
+
+
+proc WPInactivePage {{reasons ""}} {
+ set l {}
+ foreach r $reasons {
+ append l "<li>$r"
+ }
+
+ WPInfoPage "Inactive Session" \
+ "[font size=+2 "Web Alpine Session No Longer Active"]" \
+ "There are several reasons why a session might become inactive.<ul><li>A bookmarked reference to a Web Alpine page.<li>Failed periodic page reload due to browser/system suspension associated with power saving mode, etc.${l}</ul><p>Please visit the [cgi_link Start] to start a new session."
+}
+
+proc WPInfoPage {title exp1 {exp2 ""} {imgurl {}} {exp3 ""}} {
+ global _wp
+
+ catch {
+
+ cgi_html {
+ cgi_head {
+ cgi_title $title
+ cgi_stylesheet [file join / $_wp(urlprefix) $_wp(pubdir) standard.css]
+ }
+
+ cgi_body {
+ cgi_table height="20%" {
+ cgi_table_row {
+ cgi_table_data {
+ cgi_puts [cgi_nbspace]
+ }
+ }
+ }
+
+ cgi_center {
+ cgi_table border=0 width=500 cellpadding=3 {
+ cgi_table_row {
+ cgi_table_data align=center rowspan=3 {
+ if {[string length $imgurl]} {
+ cgi_put [cgi_url [cgi_imglink logo] $imgurl]
+ } else {
+ cgi_put [cgi_imglink logo]
+ }
+ }
+
+ cgi_table_data rowspan=3 {
+ cgi_put [nbspace]
+ cgi_put [nbspace]
+ }
+
+ cgi_table_data {
+ cgi_puts $exp1
+ }
+
+ }
+
+ if {[string length $exp3]} {
+ cgi_table_row {
+ cgi_table_data "style=\"border: 1px solid red; background-color: pink\"" {
+ cgi_puts $exp3
+ }
+ }
+ }
+
+ if {[string length $exp2]} {
+ cgi_table_row {
+ cgi_table_data {
+ cgi_puts $exp2
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+proc WPimg {image {extension gif}} {
+ global _wp
+
+ return [file join $_wp(imagepath) ${image}.${extension}]
+}
+
+proc WPCharValue {c} {
+ scan "$c" %c n
+ return $n
+}
+
+proc WPPercentQuote {arg {exclude {}}} {
+ set t "\[^0-9a-zA-Z_${exclude}\]"
+ if {[regsub -all $t $arg {[format "%%%.2X" [WPCharValue "\\&"]]} subarg]} {
+ set x [subst $subarg]
+ return $x
+ } else {
+ return $arg
+ }
+}
+
+proc WPJSQuote {l} {
+ regsub -all {([\\'])} $l {\\\1} l
+ return $l
+}
+
+proc WPurl {cmd cmdargs text explanation args} {
+ global _wp
+
+ lappend urlargs $text
+ lappend urlargs $cmd
+ if {[regexp "^java*" $cmd] == 0 && [string first . $cmd] < 0} {
+ append urlargs ".tcl"
+ }
+
+ if {[string length $cmdargs]} {
+ if {[set i [string first "?" $cmdargs]] >= 0} {
+ append urlargs "[cgi_quote_url [string range $cmdargs 0 [expr {$i - 1}]]]?[cgi_quote_url [string range $cmdargs [incr i] end]]"
+ } else {
+ append urlargs "?[cgi_quote_url $cmdargs]"
+ }
+ }
+
+ if {$_wp(statushelp)} {
+ lappend urlargs [WPmouseover $explanation]
+ lappend urlargs "onMouseOut=window.status=''"
+ }
+
+ return [eval "cgi_url $urlargs $args"]
+}
+
+proc WPMenuURL {cmd cmdargs text explanation args} {
+ return [WPurl $cmd $cmdargs $text $explanation class=menubar [join $args]]
+}
+
+proc WPGetTDFontSize {{ih 24}} {
+ if {$ih <= 20 } {return 12}
+ if {$ih >= 30 } {return 24}
+ return [expr {$ih - 8}]
+}
+
+proc WPGetviewFontSize {{ih 24}} {
+ if {$ih <= 20 } {return 8}
+ if {$ih >= 30 } {return 13}
+ return [expr {($ih / 2) - 2}]
+}
+
+proc WPIndexLineHeight {{ih 0}} {
+ global _wp
+
+ set ih [WPCmd PEInfo indexheight]
+ if {[string length $ih] == 0 || $ih <= 0} {
+ set ih $_wp(indexheight)
+ }
+
+ return [expr {($ih < 20) ? 20 : $ih}]
+}
+
+proc WPStyleSheets {{ih 0}} {
+ global _wp
+
+ cgi_stylesheet [file join / $_wp(urlprefix) $_wp(pubdir) standard.css]
+
+ if {$ih <= 0} {
+ set ih [WPIndexLineHeight]
+ }
+
+ cgi_puts "<style type='text/css'>\nTD { font-size: [WPGetTDFontSize $ih]px }\n.view {font-size: [WPGetviewFontSize $ih]pt }\n</style>"
+ return $ih
+}
+
+proc WPStdScripts {{ih 0}} {
+ global _wp
+
+ set ih [WPStyleSheets $ih]
+
+ cgi_script language="JavaScript" src="[file join / $_wp(urlprefix) $_wp(pubdir) standard.js]" {}
+ cgi_script language="JavaScript1.3" {cgi_put "js_version = '1.3';"}
+ cgi_javascript {
+ cgi_puts "function getIndexHeight(){return $ih}"
+ }
+}
+
+proc WPStdHttpHdrs {{ctype {}} {expires 0}} {
+ global _wp
+
+ # set date and expires headers the same to prevent caching
+ # Date: Tue, 15 Nov 1994 08:12:31 GMT
+ set doctime [clock seconds]
+
+ if {[string length $ctype]} {
+ cgi_content_type $ctype
+ } else {
+ cgi_content_type
+ }
+
+ cgi_puts "Date: [clock format $doctime -gmt true -format "%a, %d %b %Y %H:%M:%S GMT"]"
+ if {$expires == 0} {
+ set _wp(nocache) 1
+ cgi_puts "Cache-Control: no-cache"
+ cgi_puts "Expires: [clock format [expr {$doctime - 31536000}] -gmt true -format "%a, %d %b %Y %H:%M:%S GMT"]"
+ } elseif {$expires > 0} {
+ cgi_puts "Expires: [clock format [expr {$doctime + ($expires * 60)}] -gmt true -format "%a, %d %b %Y %H:%M:%S GMT"]"
+ }
+}
+
+proc WPStdHtmlHdr {pagetitle {pagescript ""} {newmail 0}} {
+ global _wp
+
+ if {0 && $newmail} {
+ set nm "* "
+ } else {
+ set nm ""
+ }
+
+ cgi_title "${nm}Web Alpine - $pagetitle"
+ # cgi_base "href=$_wp(serverpath)/"
+ if {[info exists _wp(nocache)]} {
+ cgi_http_equiv Pragma no-cache
+ }
+
+ # cgi_http_equiv Expires $_wp(docdate)
+ cgi_meta "name=Web Alpine" content=[clock format [file mtime [info script]] -format "%y%m%d/%H%M"]
+ if {[catch {WPCmd set nojs} nojs] || $nojs != 1} {
+ cgi_script type="text/javascript" language="JavaScript" {
+ cgi_puts "if(self != top) top.location.href = location.href;"
+ cgi_puts "js_version = '1.0';"
+ }
+ }
+
+ cgi_put "<link rel=\"icon\" href=\"[cgi_root]/favicon.ico\" type=\"image/x-icon\">"
+ cgi_put "<link rel=\"shortcut icon\" href=\"[cgi_root]/favicon.ico\" type=\"image/x-icon\"> "
+}
+
+proc WPHtmlHdrReload {pagescript} {
+ global _wp
+
+ if {[regexp {\?} $pagescript]} {
+ set c "&"
+ } else {
+ set c "?"
+ }
+
+ cgi_http_equiv Refresh "$_wp(refresh); url=[cgi_root]/${pagescript}${c}reload=1"
+}
+
+proc WPNewMail {reload {viewpage msgview.tcl}} {
+
+ if {[catch {WPCmd PEMailbox newmail $reload} newmail]} {
+ return -code error $newmail
+ }
+
+ set newref ""
+
+ if {[set msgsnew [lindex $newmail 0]] > 0} {
+ if {[string length $viewpage]} {
+ if {[string first {?} $viewpage] < 0} {
+ set delim ?
+ } else {
+ set delim &
+ }
+
+ set newurl "${viewpage}${delim}uid=[lindex $newmail 1]"
+ } else {
+ set newurl [lindex $newmail 1]
+ }
+
+ set newicon "postmark"
+ set newtext [cgi_quote_html [WPCmd PEMailbox newmailstatmsg]]
+
+ if {[WPCmd PEInfo feature enable-newmail-sound]} {
+ #set audio sounds/mail_msg.wav
+ set audio /sounds/ding.wav
+ if {[isIE]} {
+ set newsound "<bgsound src=\"$audio\" loop=\"1\" volume=\"100\">"
+ } else {
+ set newsound "<embed src=\"$audio\" autostart=\"true\" hidden width=0 height=0 loop=\"false\"><noembed><bgsound src=\"$audio\" loop=\"1\"></noembed>"
+ }
+ } else {
+ set newsound {}
+ }
+
+ if {0 == [string length $newtext]} {
+ set newtext "You have $msgsnew new message[WPplural $msgsnew]"
+ }
+
+ lappend newref [list $newtext $newicon $newurl $newsound]
+ }
+
+ if {[set deleted [lindex $newmail 2]] > 0} {
+ set newtext "$deleted Message[WPplural $deleted] removed from folder"
+ lappend newref [list $newtext "" ""]
+ }
+
+ foreach statmsg [WPStatusMsgs] {
+ lappend newref [list $statmsg "" ""]
+ WPCmd PEInfo statmsg ""
+ }
+
+ if {!$reload} {
+ WPCmd PEMailbox newmailreset
+ }
+
+ return $newref
+}
+
+proc WPStatusMsgs {} {
+ set retmsgs ""
+ set lastmsg ""
+ if {[catch {WPCmd PEInfo statmsgs} statmsgs] == 0} {
+ foreach statmsg $statmsgs {
+ if {[string length $statmsg] > 0 && [string compare $statmsg $lastmsg]} {
+ if {[regexp "^Pinerc \(.+\) NOT saved$" $statmsg]} {
+ lappend retmsgs "Another Pine/WebPine session may be running. Settings cannot be saved."
+ } else {
+ lappend retmsgs $statmsg
+ }
+
+ set lastmsg $statmsg
+ }
+ }
+ }
+
+ return $retmsgs
+}
+
+proc WPStatusIcon {uid {extension gif} {statbits ""}} {
+ global _wp
+
+ if {[string length $statbits] == 0} {
+ set statbits [WPCmd PEMessage $uid statusbits]
+ }
+
+ if {[string index $statbits 0]} {
+ append sicon "new"
+ set alt " N"
+ set fullalt "New "
+ } else {
+ append sicon "read"
+ set alt " "
+ set fullalt "Viewed "
+ }
+
+ if {[string index $statbits 3]} {
+ append sicon "imp"
+ set alt "*[string range $alt 1 end]"
+ set fullaltend ", important message"
+ } elseif {([string index $statbits 4] || [string index $statbits 5])} {
+ append sicon "you"
+ set alt "+[string range $alt 1 end]"
+ set fullaltend " message to you"
+ }
+
+ if {[string index $statbits 2]} {
+ append sicon "ans"
+ set alt "[string range $alt 0 0]A"
+ append fullalt ", answered"
+ }
+
+ if {[string index $statbits 1]} {
+ append sicon "del"
+ set alt "[string range $alt 0 0]D"
+ append fullalt ", deleted"
+ }
+
+ if {[info exists fullaltend]} {
+ append fullalt $fullaltend
+ } else {
+ append fullalt message
+ }
+
+ regsub -all { } $alt {\&nbsp;} alt
+
+ return [list [file join $_wp(imagepath) $_wp(staticondir) ${sicon}.${extension}] i_${uid} $alt $fullalt]
+}
+
+proc WPStatusLabel {uid} {
+ global _wp
+
+ set statbits [WPCmd PEMessage $uid statusbits]
+
+ if {[string index $statbits 0]} {
+ set sl new
+ } else {
+ set sl read
+ }
+
+ if {[string index $statbits 3]} {
+ set sl important
+ }
+
+ if {[string index $statbits 1]} {
+ set sl deleted
+ }
+
+ if {[string index $statbits 2]} {
+ set sl answered
+ }
+
+ return $sl
+}
+
+proc WPStatusImg {uid} {
+ set sicon [WPStatusIcon $uid]
+ return [cgi_img [lindex $sicon 0] name=[lindex $sicon 1] id=[lindex $sicon 1] height=16 width=42 border=0 alt=[lindex $sicon 3]]
+}
+
+proc WPSessionState {args} {
+ switch [llength $args] {
+ 1 -
+ 2 {
+ if {[catch {WPCmd PEInfo alpinestate} state_list] == 0} {
+ array set state_array $state_list
+ if {[llength $args] == 1} {
+ return $state_array([lindex $args 0])
+ } else {
+ set state_array([lindex $args 0]) [lindex $args 1]
+ set state_list [array get state_array]
+ if {[catch {WPCmd PEInfo alpinestate $state_list} result]} {
+ error "Can't set session state : $result"
+ }
+ }
+ } else {
+ error "Can't read session state"
+ }
+ }
+ default {
+ error "Unknown SessionState Parameters: $args"
+ }
+ }
+}
+
+proc WPScriptVersion {tag {inc 0}} {
+ if {[catch {WPCmd set wp_script_version} sv]} {
+ set versions($tag) [expr int((1000 * rand()))]
+ set sv [array get versions]
+ catch {WPCmd set wp_script_version $sv}
+ } else {
+ array set versions $sv
+
+ if {![info exists versions($tag)]} {
+ set versions($tag) [expr int((1000 * rand()))]
+ set sv [array get versions]
+ catch {WPCmd set wp_script_version $sv}
+ } elseif {$inc} {
+ incr versions($tag) $inc
+ set sv [array get versions]
+ catch {WPCmd set wp_script_version $sv}
+ }
+ }
+
+ return $versions($tag)
+}
+
+proc WPplural {count} {
+ if {$count > 1} {
+ return "s"
+ }
+
+ return ""
+}
+
+proc WPcomma {number {dot ,}} {
+ set x ""
+
+ while {[set n [string length $number]] > 3} {
+ set x "${dot}[string range $number [incr n -3] end]$x"
+ set number [string range $number 0 [incr n -1]]
+ }
+
+ return "$number$x"
+}
+
+proc isIE {} {
+ global env
+
+ return [expr {[info exists env(HTTP_USER_AGENT)] == 1 && [string first MSIE $env(HTTP_USER_AGENT)] >= 0}]
+}
+
+proc isW3C {} {
+ global env
+
+ return [expr {[info exists env(HTTP_USER_AGENT)] && (([regexp {^Mozilla/([0-9]).[0-9]+} $env(HTTP_USER_AGENT) match majorversion] && $majorversion > 4) || ([regexp {Opera ([0-9])\.[0-9]+} $env(HTTP_USER_AGENT) match majorversion] && $majorversion > 5))}]
+}
+
+proc WPdebug {args} {
+ global _wp
+
+ switch [lindex $args 0] {
+ level {
+ if {[regexp {^([0-9])+$} [lindex $args 1]]} {
+ WPSend $_wp(sockname) [subst {PEDebug level [lindex $args 1]}]
+ }
+ }
+ imap {
+ switch [lindex $args 1] {
+ on {
+ WPSend $_wp(sockname) [subst {ePEDebug imap 4}]
+ }
+ off {
+ WPSend $_wp(sockname) [subst {PEDebug imap 0}]
+ }
+ }
+ }
+ default {
+ WPSend $_wp(sockname) "PEDebug write [list [list [file tail [info script]]: [lrange $args 0 end]]]"
+ }
+ }
+}
+
+proc WPdebugstack {} {
+ set stack {}
+
+ for {set n [expr {[info level] - 1}]} {$n > 0} {incr n -1} {
+ append stack "$n) [info level $n]\n"
+ }
+ return $stack
+}
+
+
+##############################################################
+# routines to improve integration with cgi.tcl
+##############################################################
+
+# routine exposing some of cgi.tcl's innards.
+# Should be exported by cgi.tcl package.
+proc reset_cgi_state {} {
+ global _cgi
+
+ catch {unset _cgi(http_head_in_progress)}
+ catch {unset _cgi(http_head_done)}
+ catch {unset _cgi(http_status_done)}
+ catch {unset _cgi(html_in_progress)}
+ catch {unset _cgi(head_in_progress)}
+ catch {unset _cgi(head_done)}
+ catch {unset _cgi(html_done)}
+ catch {unset _cgi(head_suppress_tag)}
+ catch {unset _cgi(body_in_progress)}
+ catch {unset _cgi(tag_in_progress)}
+ catch {unset _cgi(form_in_progress)}
+ catch {unset _cgi(close_proc)}
+
+ if {[info exists _cgi(returnIndex)]} {
+ while {[set _cgi(returnIndex)] > 0} {
+ incr _cgi(returnIndex) -1
+ rename cgi_puts ""
+ rename cgi_puts$_cgi(returnIndex) cgi_puts
+ }
+ }
+}
+
+###############################################################
+# routines to process (and be called in) html template files
+###############################################################
+
+proc html_readfile {file} {
+ set x [open $file "r"]
+ set result [read $x]
+ close $x
+ return $result
+}
+
+proc html_eval {_vars_ _this_} {
+ foreach {_i_ _j_} $_vars_ {
+ if {$_i_ == "global"} {global $_j_} {set $_i_ $_j_}
+ }
+ unset _vars_ _i_ _j_
+ return [subst $_this_]
+}
+
+proc html_loop {varslist text} {
+ set result ""
+ foreach {vars} $varslist {
+ append result [html_eval $vars $text]
+ }
+ return $result
+}
diff --git a/web/config/conf.deskmail b/web/config/conf.deskmail
new file mode 100644
index 00000000..4f760c75
--- /dev/null
+++ b/web/config/conf.deskmail
@@ -0,0 +1,55 @@
+#
+# Defaults that make more sense in our browser
+# oriented world...
+#
+inbox-path={${WPUSER}.deskmail.washington.edu/tls}inbox
+
+user-domain=u.washington.edu
+
+folder-collections="Deskmail Folders" {${WPUSER}.deskmail.washington.edu/tls}mail/[]
+
+literal-signature=""
+
+address-book={${WPUSER}.deskmail.washington.edu/tls}remote_addrbook
+
+feature-list=enable-msg-view-urls,
+ enable-msg-view-web-hostnames,
+ enable-msg-view-addresses,
+ enable-msg-view-attachments,
+ compose-rejects-unqualified-addrs,
+ enable-aggregate-command-set,
+ auto-zoom-after-select,
+ auto-unselect-after-apply,
+ fcc-without-attachments,
+ quell-empty-directories
+
+normal-background-color=white
+normal-foreground-color=black
+quote1-foreground-color=000,000,153
+quote1-background-color=white
+quote2-foreground-color=051,051,255
+quote2-background-color=white
+quote3-foreground-color=051,153,255
+quote3-background-color=white
+
+rsh-open-timeout=0
+ssh-open-timeout=0
+
+index-format=ATT STATUS FROMORTO(18%) SUBJECT(70%) SMARTDATETIME(12%) SIZENARROW PRIORITY
+
+viewer-hdr-colors=/HDR=Subject/FG=black/BG=yellow,
+ /HDR=message-id/FG=black/BG=206\x2C206\x2C206,
+ /HDR=In-reply-to/FG=black/BG=206\x2C206\x2C206
+
+wp-indexlines=25
+wp-aggstate=2
+
+sort-key=date/reverse
+
+ldap-servers=people.u.washington.edu "/base=o=University of Washington,c=US/impl=1/rhs=0/ref=0/nosub=0/type=/srch=/time=/size=/cust=/nick=/matr=/catr=/satr=/gatr="
+
+addressbook-formats=FULLNAME NICKNAME ADDRESS
+
+rss-news=http://uwnews.org/apps/uwnews/public/rss.aspx?q=uwnAllCategories&numToShow=10
+
+rss-weather=http://www.weather.gov/xml/current_obs/KBFI.rss
diff --git a/web/config/pine.conf b/web/config/pine.conf
new file mode 100644
index 00000000..e9f4c29c
--- /dev/null
+++ b/web/config/pine.conf
@@ -0,0 +1,52 @@
+#
+# Defaults that make more sense in our browser
+# oriented world...
+#
+inbox-path={${IMAP_SERVER}}inbox
+
+smtp-server=localhost
+
+user-domain=${IMAP_SERVER_BASE}
+
+folder-collections="Folders" {${IMAP_SERVER}}mail/[]
+
+literal-signature=""
+
+address-book={${IMAP_SERVER}}remote_addrbook
+
+feature-list=enable-msg-view-urls,
+ enable-msg-view-web-hostnames,
+ enable-msg-view-addresses,
+ enable-msg-view-attachments,
+ compose-rejects-unqualified-addrs,
+ enable-aggregate-command-set,
+ auto-zoom-after-select,
+ auto-unselect-after-apply,
+ quell-empty-directories
+
+normal-background-color=white
+normal-foreground-color=black
+quote1-foreground-color=000,000,153
+quote1-background-color=white
+quote2-foreground-color=051,051,255
+quote2-background-color=white
+quote3-foreground-color=051,153,255
+quote3-background-color=white
+
+rsh-open-timeout=0
+ssh-open-timeout=0
+
+index-format=ATT STATUS FROMORTO(18%) SUBJECT(70%) SMARTDATETIME(12%) SIZENARROW PRIORITY
+
+viewer-hdr-colors=/HDR=Subject/FG=black/BG=yellow,
+ /HDR=message-id/FG=black/BG=206\x2C206\x2C206,
+ /HDR=In-reply-to/FG=black/BG=206\x2C206\x2C206
+
+sort-key=date
+
+addressbook-formats=FULLNAME NICKNAME ADDRESS
+
+rss-news=http://uwnews.org/apps/uwnews/public/rss.aspx?q=uwnAllCategories&numToShow=10
+
+rss-weather=http://www.weather.gov/xml/current_obs/KBFI.rss
+
diff --git a/web/detach b/web/detach
new file mode 120000
index 00000000..d1373a3e
--- /dev/null
+++ b/web/detach
@@ -0,0 +1 @@
+/tmp/webpine \ No newline at end of file
diff --git a/web/lib/README b/web/lib/README
new file mode 100644
index 00000000..d85c369d
--- /dev/null
+++ b/web/lib/README
@@ -0,0 +1,8 @@
+
+
+ after building alpined, make sure the shared library
+ libwpcomm1.0.0 is in this directory. once it is
+ run the pkgcreate.tcl script by hand which will create
+ the tcl index file pkgIndex.tcl. This only has to be
+ done once.
+
diff --git a/web/lib/pkgcreate b/web/lib/pkgcreate
new file mode 100755
index 00000000..ee75a10b
--- /dev/null
+++ b/web/lib/pkgcreate
@@ -0,0 +1,3 @@
+#!/usr/bin/tclsh
+# allow for earlier versions of Tcl that don't define pkg_mkIndex
+catch {pkg_mkIndex . cgi.tcl *.so}
diff --git a/web/src/Makefile.am b/web/src/Makefile.am
new file mode 100644
index 00000000..5e822d91
--- /dev/null
+++ b/web/src/Makefile.am
@@ -0,0 +1,22 @@
+# ========================================================================
+# Copyright 2006-2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+
+all:
+ cd @abs_top_srcdir@/web/src/cgi.tcl-1.10 && ./configure --prefix=@abs_top_srcdir@/web/lib
+
+install:
+ cd @abs_top_srcdir@/web/src/alpined.d && make install
+ cd @abs_top_srcdir@/web/src/cgi.tcl-1.10 && make install SCRIPTDIR=@abs_top_srcdir@/web/lib
+ cd @abs_top_srcdir@/web/lib && tclsh ./pkgcreate
+ if test -x pubcookie/wp_uidmapper ; then $(LN) -f pubcookie/wp_uidmapper @abs_top_srcdir@/web/bin ; fi
+ if test -x pubcookie/wp_tclsh ; then $(LN) -f pubcookie/wp_tclsh @abs_top_srcdir@/web/bin ; fi
+ if test -x pubcookie/wp_gssapi_proxy ; then $(LN) -f pubcookie/wp_gssapi_proxy @abs_top_srcdir@/web/bin ; fi
diff --git a/web/src/Makefile.in b/web/src/Makefile.in
new file mode 100644
index 00000000..b208cc32
--- /dev/null
+++ b/web/src/Makefile.in
@@ -0,0 +1,421 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# ========================================================================
+# Copyright 2006-2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = web/src
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/acx_pthread.m4 \
+ $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
+ $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/VERSION \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/include/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+SOURCES =
+DIST_SOURCES =
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_CFLAGS = @AM_CFLAGS@
+AM_LDFLAGS = @AM_LDFLAGS@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CP = @CP@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+C_CLIENT_CFLAGS = @C_CLIENT_CFLAGS@
+C_CLIENT_GCCOPTLEVEL = @C_CLIENT_GCCOPTLEVEL@
+C_CLIENT_LDFLAGS = @C_CLIENT_LDFLAGS@
+C_CLIENT_SPECIALS = @C_CLIENT_SPECIALS@
+C_CLIENT_TARGET = @C_CLIENT_TARGET@
+C_CLIENT_WITH_IPV6 = @C_CLIENT_WITH_IPV6@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISPELLPROG = @ISPELLPROG@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN = @LN@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKE = @MAKE@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NPA_PROG = @NPA_PROG@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+POSUB = @POSUB@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+PWPROG = @PWPROG@
+RANLIB = @RANLIB@
+REGEX_BUILD = @REGEX_BUILD@
+RM = @RM@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPELLPROG = @SPELLPROG@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEB_BINDIR = @WEB_BINDIR@
+WEB_BUILD = @WEB_BUILD@
+WEB_PUBCOOKIE_BUILD = @WEB_PUBCOOKIE_BUILD@
+WEB_PUBCOOKIE_LIB = @WEB_PUBCOOKIE_LIB@
+WEB_PUBCOOKIE_LINK = @WEB_PUBCOOKIE_LINK@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+acx_pthread_config = @acx_pthread_config@
+alpine_interactive_spellcheck = @alpine_interactive_spellcheck@
+alpine_simple_spellcheck = @alpine_simple_spellcheck@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign web/src/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign web/src/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+tags: TAGS
+TAGS:
+
+ctags: CTAGS
+CTAGS:
+
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile
+installdirs:
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ distclean distclean-generic distclean-libtool distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am uninstall uninstall-am
+
+
+all:
+ cd @abs_top_srcdir@/web/src/cgi.tcl-1.10 && ./configure --prefix=@abs_top_srcdir@/web/lib
+
+install:
+ cd @abs_top_srcdir@/web/src/alpined.d && make install
+ cd @abs_top_srcdir@/web/src/cgi.tcl-1.10 && make install SCRIPTDIR=@abs_top_srcdir@/web/lib
+ cd @abs_top_srcdir@/web/lib && tclsh ./pkgcreate
+ if test -x pubcookie/wp_uidmapper ; then $(LN) -f pubcookie/wp_uidmapper @abs_top_srcdir@/web/bin ; fi
+ if test -x pubcookie/wp_tclsh ; then $(LN) -f pubcookie/wp_tclsh @abs_top_srcdir@/web/bin ; fi
+ if test -x pubcookie/wp_gssapi_proxy ; then $(LN) -f pubcookie/wp_gssapi_proxy @abs_top_srcdir@/web/bin ; fi
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/web/src/alpined.d/Makefile.am b/web/src/alpined.d/Makefile.am
new file mode 100644
index 00000000..9101e4fb
--- /dev/null
+++ b/web/src/alpined.d/Makefile.am
@@ -0,0 +1,52 @@
+## Process this file with automake to produce Makefile.in
+## Use aclocal -I m4; automake
+
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# This is because alpined, libwpcomm and friends are
+# not intended for system-wide consumption
+locbindir = @abs_srcdir@/../../bin
+loclibdir = @abs_srcdir@/../../lib
+
+locbin_PROGRAMS = alpined alpineldap
+
+alpined_SOURCES = alpined.c busy.c color.c imap.c ldap.c remote.c \
+ signal.c debug.c status.c stubs.c \
+ alpined.h color.h ldap.h
+
+alpineldap_SOURCES = alpineldap.c busy.c color.c imap.c ldap.c remote.c \
+ signal.c debug.c status.c stubs.c \
+ alpined.h color.h ldap.h
+
+LDADD = local.o \
+ @top_srcdir@/pith/libpith.a \
+ @top_srcdir@/pith/osdep/libpithosd.a \
+ @top_srcdir@/pith/charconv/libpithcc.a \
+ @top_srcdir@/c-client/c-client.a \
+ $(WEB_PUBCOOKIE_LIB)
+
+loclib_LTLIBRARIES = libwpcomm.la
+
+libwpcomm_la_SOURCES = wpcomm.c
+
+libwpcomm_la_LDFLAGS = -rpath '$(loclibdir)' -version-info 1:0:0
+
+AM_CPPFLAGS = -I@top_builddir@/include -I@top_srcdir@/include
+
+AM_LDFLAGS = `cat @top_srcdir@/c-client/LDFLAGS`
+
+CLEANFILES = local.c
+
+local.c: alpineldap.c color.c imap.c ldap.c remote.c signal.c \
+ debug.c status.c stubs.c alpined.h color.h ldap.h
+ echo "char datestamp[]="\"`date`\"";" > local.c
+ echo "char hoststamp[]="\"`hostname`\"";" >> local.c
diff --git a/web/src/alpined.d/Makefile.in b/web/src/alpined.d/Makefile.in
new file mode 100644
index 00000000..644fd530
--- /dev/null
+++ b/web/src/alpined.d/Makefile.in
@@ -0,0 +1,695 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+locbin_PROGRAMS = alpined$(EXEEXT) alpineldap$(EXEEXT)
+subdir = web/src/alpined.d
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/acx_pthread.m4 \
+ $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
+ $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/VERSION \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/include/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__installdirs = "$(DESTDIR)$(loclibdir)" "$(DESTDIR)$(locbindir)"
+LTLIBRARIES = $(loclib_LTLIBRARIES)
+libwpcomm_la_LIBADD =
+am_libwpcomm_la_OBJECTS = wpcomm.lo
+libwpcomm_la_OBJECTS = $(am_libwpcomm_la_OBJECTS)
+libwpcomm_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libwpcomm_la_LDFLAGS) $(LDFLAGS) -o $@
+PROGRAMS = $(locbin_PROGRAMS)
+am_alpined_OBJECTS = alpined.$(OBJEXT) busy.$(OBJEXT) color.$(OBJEXT) \
+ imap.$(OBJEXT) ldap.$(OBJEXT) remote.$(OBJEXT) \
+ signal.$(OBJEXT) debug.$(OBJEXT) status.$(OBJEXT) \
+ stubs.$(OBJEXT)
+alpined_OBJECTS = $(am_alpined_OBJECTS)
+alpined_LDADD = $(LDADD)
+am__DEPENDENCIES_1 =
+alpined_DEPENDENCIES = local.o @top_srcdir@/pith/libpith.a \
+ @top_srcdir@/pith/osdep/libpithosd.a \
+ @top_srcdir@/pith/charconv/libpithcc.a \
+ @top_srcdir@/c-client/c-client.a $(am__DEPENDENCIES_1)
+am_alpineldap_OBJECTS = alpineldap.$(OBJEXT) busy.$(OBJEXT) \
+ color.$(OBJEXT) imap.$(OBJEXT) ldap.$(OBJEXT) remote.$(OBJEXT) \
+ signal.$(OBJEXT) debug.$(OBJEXT) status.$(OBJEXT) \
+ stubs.$(OBJEXT)
+alpineldap_OBJECTS = $(am_alpineldap_OBJECTS)
+alpineldap_LDADD = $(LDADD)
+alpineldap_DEPENDENCIES = local.o @top_srcdir@/pith/libpith.a \
+ @top_srcdir@/pith/osdep/libpithosd.a \
+ @top_srcdir@/pith/charconv/libpithcc.a \
+ @top_srcdir@/c-client/c-client.a $(am__DEPENDENCIES_1)
+DEFAULT_INCLUDES =
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+SOURCES = $(libwpcomm_la_SOURCES) $(alpined_SOURCES) \
+ $(alpineldap_SOURCES)
+DIST_SOURCES = $(libwpcomm_la_SOURCES) $(alpined_SOURCES) \
+ $(alpineldap_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_CFLAGS = @AM_CFLAGS@
+AM_LDFLAGS = `cat @top_srcdir@/c-client/LDFLAGS`
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CP = @CP@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+C_CLIENT_CFLAGS = @C_CLIENT_CFLAGS@
+C_CLIENT_GCCOPTLEVEL = @C_CLIENT_GCCOPTLEVEL@
+C_CLIENT_LDFLAGS = @C_CLIENT_LDFLAGS@
+C_CLIENT_SPECIALS = @C_CLIENT_SPECIALS@
+C_CLIENT_TARGET = @C_CLIENT_TARGET@
+C_CLIENT_WITH_IPV6 = @C_CLIENT_WITH_IPV6@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISPELLPROG = @ISPELLPROG@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN = @LN@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKE = @MAKE@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NPA_PROG = @NPA_PROG@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+POSUB = @POSUB@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+PWPROG = @PWPROG@
+RANLIB = @RANLIB@
+REGEX_BUILD = @REGEX_BUILD@
+RM = @RM@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPELLPROG = @SPELLPROG@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEB_BINDIR = @WEB_BINDIR@
+WEB_BUILD = @WEB_BUILD@
+WEB_PUBCOOKIE_BUILD = @WEB_PUBCOOKIE_BUILD@
+WEB_PUBCOOKIE_LIB = @WEB_PUBCOOKIE_LIB@
+WEB_PUBCOOKIE_LINK = @WEB_PUBCOOKIE_LINK@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+acx_pthread_config = @acx_pthread_config@
+alpine_interactive_spellcheck = @alpine_interactive_spellcheck@
+alpine_simple_spellcheck = @alpine_simple_spellcheck@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+
+# This is because alpined, libwpcomm and friends are
+# not intended for system-wide consumption
+locbindir = @abs_srcdir@/../../bin
+loclibdir = @abs_srcdir@/../../lib
+alpined_SOURCES = alpined.c busy.c color.c imap.c ldap.c remote.c \
+ signal.c debug.c status.c stubs.c \
+ alpined.h color.h ldap.h
+
+alpineldap_SOURCES = alpineldap.c busy.c color.c imap.c ldap.c remote.c \
+ signal.c debug.c status.c stubs.c \
+ alpined.h color.h ldap.h
+
+LDADD = local.o \
+ @top_srcdir@/pith/libpith.a \
+ @top_srcdir@/pith/osdep/libpithosd.a \
+ @top_srcdir@/pith/charconv/libpithcc.a \
+ @top_srcdir@/c-client/c-client.a \
+ $(WEB_PUBCOOKIE_LIB)
+
+loclib_LTLIBRARIES = libwpcomm.la
+libwpcomm_la_SOURCES = wpcomm.c
+libwpcomm_la_LDFLAGS = -rpath '$(loclibdir)' -version-info 1:0:0
+AM_CPPFLAGS = -I@top_builddir@/include -I@top_srcdir@/include
+CLEANFILES = local.c
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign web/src/alpined.d/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign web/src/alpined.d/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-loclibLTLIBRARIES: $(loclib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(loclibdir)" || $(MKDIR_P) "$(DESTDIR)$(loclibdir)"
+ @list='$(loclib_LTLIBRARIES)'; test -n "$(loclibdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(loclibdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(loclibdir)"; \
+ }
+
+uninstall-loclibLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(loclib_LTLIBRARIES)'; test -n "$(loclibdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(loclibdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(loclibdir)/$$f"; \
+ done
+
+clean-loclibLTLIBRARIES:
+ -test -z "$(loclib_LTLIBRARIES)" || rm -f $(loclib_LTLIBRARIES)
+ @list='$(loclib_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+libwpcomm.la: $(libwpcomm_la_OBJECTS) $(libwpcomm_la_DEPENDENCIES)
+ $(libwpcomm_la_LINK) -rpath $(loclibdir) $(libwpcomm_la_OBJECTS) $(libwpcomm_la_LIBADD) $(LIBS)
+install-locbinPROGRAMS: $(locbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(locbindir)" || $(MKDIR_P) "$(DESTDIR)$(locbindir)"
+ @list='$(locbin_PROGRAMS)'; test -n "$(locbindir)" || list=; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p || test -f $$p1; \
+ then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(locbindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(locbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-locbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(locbin_PROGRAMS)'; test -n "$(locbindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(locbindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(locbindir)" && rm -f $$files
+
+clean-locbinPROGRAMS:
+ @list='$(locbin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+alpined$(EXEEXT): $(alpined_OBJECTS) $(alpined_DEPENDENCIES)
+ @rm -f alpined$(EXEEXT)
+ $(LINK) $(alpined_OBJECTS) $(alpined_LDADD) $(LIBS)
+alpineldap$(EXEEXT): $(alpineldap_OBJECTS) $(alpineldap_DEPENDENCIES)
+ @rm -f alpineldap$(EXEEXT)
+ $(LINK) $(alpineldap_OBJECTS) $(alpineldap_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alpined.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alpineldap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/busy.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/color.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/debug.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/remote.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/signal.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/status.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stubs.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wpcomm.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(loclibdir)" "$(DESTDIR)$(locbindir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-locbinPROGRAMS \
+ clean-loclibLTLIBRARIES mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-locbinPROGRAMS install-loclibLTLIBRARIES
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-locbinPROGRAMS uninstall-loclibLTLIBRARIES
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libtool clean-locbinPROGRAMS clean-loclibLTLIBRARIES \
+ ctags distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-locbinPROGRAMS \
+ install-loclibLTLIBRARIES install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags uninstall uninstall-am uninstall-locbinPROGRAMS \
+ uninstall-loclibLTLIBRARIES
+
+
+local.c: alpineldap.c color.c imap.c ldap.c remote.c signal.c \
+ debug.c status.c stubs.c alpined.h color.h ldap.h
+ echo "char datestamp[]="\"`date`\"";" > local.c
+ echo "char hoststamp[]="\"`hostname`\"";" >> local.c
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/web/src/alpined.d/alpined.c b/web/src/alpined.d/alpined.c
new file mode 100644
index 00000000..e35ba9e6
--- /dev/null
+++ b/web/src/alpined.d/alpined.c
@@ -0,0 +1,16404 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: alpined.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
+#endif
+
+/* ========================================================================
+ * Copyright 2006-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/* ========================================================================
+ Implement alpine TCL interfaces. Execute TCL interfaces
+ via interpreter reading commands and writing results over
+ UNIX domain socket.
+ ======================================================================== */
+
+
+#include <system.h>
+#include <general.h>
+
+#include "../../../c-client/c-client.h"
+#include "../../../c-client/imap4r1.h"
+
+#include "../../../pith/osdep/color.h" /* color support library */
+#include "../../../pith/osdep/canaccess.h"
+#include "../../../pith/osdep/temp_nam.h"
+#include "../../../pith/osdep/collate.h"
+#include "../../../pith/osdep/filesize.h"
+#include "../../../pith/osdep/writ_dir.h"
+#include "../../../pith/osdep/err_desc.h"
+
+#include "../../../pith/stream.h"
+#include "../../../pith/context.h"
+#include "../../../pith/state.h"
+#include "../../../pith/msgno.h"
+#include "../../../pith/debug.h"
+#include "../../../pith/init.h"
+#include "../../../pith/conf.h"
+#include "../../../pith/conftype.h"
+#include "../../../pith/detoken.h"
+#include "../../../pith/flag.h"
+#include "../../../pith/help.h"
+#include "../../../pith/remote.h"
+#include "../../../pith/status.h"
+#include "../../../pith/mailcmd.h"
+#include "../../../pith/savetype.h"
+#include "../../../pith/save.h"
+#include "../../../pith/reply.h"
+#include "../../../pith/sort.h"
+#include "../../../pith/ldap.h"
+#include "../../../pith/addrbook.h"
+#include "../../../pith/ablookup.h"
+#include "../../../pith/takeaddr.h"
+#include "../../../pith/bldaddr.h"
+#include "../../../pith/copyaddr.h"
+#include "../../../pith/thread.h"
+#include "../../../pith/folder.h"
+#include "../../../pith/mailview.h"
+#include "../../../pith/indxtype.h"
+#include "../../../pith/icache.h"
+#include "../../../pith/mailindx.h"
+#include "../../../pith/mailpart.h"
+#include "../../../pith/mimedesc.h"
+#include "../../../pith/detach.h"
+#include "../../../pith/newmail.h"
+#include "../../../pith/charset.h"
+#include "../../../pith/util.h"
+#include "../../../pith/rfc2231.h"
+#include "../../../pith/string.h"
+#include "../../../pith/send.h"
+#include "../../../pith/options.h"
+#include "../../../pith/list.h"
+#include "../../../pith/mimetype.h"
+#include "../../../pith/mailcap.h"
+#include "../../../pith/sequence.h"
+#include "../../../pith/smime.h"
+#include "../../../pith/url.h"
+#include "../../../pith/charconv/utf8.h"
+
+#include "alpined.h"
+#include "color.h"
+#include "imap.h"
+#include "ldap.h"
+#include "debug.h"
+#include "stubs.h"
+
+#include <tcl.h>
+
+
+/*
+ * Fake screen dimension for word wrap and such
+ */
+#define FAKE_SCREEN_WIDTH 80
+#define FAKE_SCREEN_LENGTH 24
+
+/*
+ * Aribtrary minimum display width (in characters)
+ */
+#define MIN_SCREEN_COLS 20
+
+
+/*
+ * Maximum number of lines allowed in signatures
+ */
+#define SIG_MAX_LINES 24
+#define SIG_MAX_COLS 1024
+
+
+/*
+ * Number of seconds we'll wait before we assume the client has wondered
+ * on to more interesting content
+ */
+#define PE_INPUT_TIMEOUT 1800
+
+
+/*
+ * Posting error lenght max
+ */
+#define WP_MAX_POST_ERROR 128
+
+
+/*
+ * AUTH Response Tokens
+ */
+#define AUTH_EMPTY_STRING "NOPASSWD"
+#define AUTH_FAILURE_STRING "BADPASSWD"
+
+/*
+ * CERT Response Tokens
+ */
+#define CERT_QUERY_STRING "CERTQUERY"
+#define CERT_FAILURE_STRING "CERTFAIL"
+
+
+/*
+ * Charset used within alpined and to communicate with alpined
+ * Note: posting-charset still respected
+ */
+#define WP_INTERNAL_CHARSET "UTF-8"
+
+
+/*
+ * Globals referenced throughout pine...
+ */
+struct pine *ps_global; /* THE global variable! */
+
+
+/*
+ * More global state
+ */
+long gPeITop, gPeICount;
+
+long gPeInputTimeout = PE_INPUT_TIMEOUT;
+long gPEAbandonTimeout = 0;
+
+
+/*
+ * Authorization issues
+ */
+int peNoPassword, peCredentialError;
+int peCertFailure, peCertQuery;
+char peCredentialRequestor[CRED_REQ_SIZE];
+
+char *peSocketName;
+
+char **peTSig;
+
+CONTEXT_S *config_context_list;
+
+STRLIST_S *peCertHosts;
+
+bitmap_t changed_feature_list;
+#define F_CH_ON(feature) (bitnset((feature),changed_feature_list))
+#define F_CH_OFF(feature) (!F_CH_ON(feature))
+#define F_CH_TURN_ON(feature) (setbitn((feature),changed_feature_list))
+#define F_CH_TURN_OFF(feature) (clrbitn((feature),changed_feature_list))
+#define F_CH_SET(feature,value) ((value) ? F_CH_TURN_ON((feature)) \
+ : F_CH_TURN_OFF((feature)))
+
+
+typedef struct _status_msg {
+ time_t posted;
+ unsigned type:3;
+ unsigned seen:1;
+ long id;
+ char *text;
+ struct _status_msg *next;
+} STATMSG_S;
+
+static STATMSG_S *peStatList;
+
+typedef struct _composer_attachment {
+ unsigned file:1;
+ unsigned body:1;
+ char *id;
+ union {
+ struct {
+ char *local;
+ char *remote;
+ char *type;
+ char *subtype;
+ char *description;
+ long size;
+ } f;
+ struct {
+ BODY *body;
+ } b;
+ struct {
+ long msgno;
+ char *part;
+ } msg;
+ } l;
+ struct _composer_attachment *next;
+} COMPATT_S;
+
+static COMPATT_S *peCompAttach;
+
+/*
+ * Holds data passed
+ */
+typedef struct _msg_data {
+ ENVELOPE *outgoing;
+ METAENV *metaenv;
+ PINEFIELD *custom;
+ STORE_S *msgtext;
+ STRLIST_S *attach;
+ char *fcc;
+ int fcc_colid;
+ int postop_fcc_no_attach;
+ char *charset;
+ char *priority;
+ int (*postfunc)(METAENV *, BODY *, char *, CONTEXT_S **, char *);
+ unsigned flowed:1;
+ unsigned html:1;
+ unsigned qualified_addrs:1;
+} MSG_COL_S;
+
+
+/*
+ * locally global structure to keep track of various bits of state
+ * needed to collect filtered output
+ */
+static struct _embedded_data {
+ Tcl_Interp *interp;
+ Tcl_Obj *obj;
+ STORE_S *store;
+ long uid;
+ HANDLE_S *handles;
+ char inhandle;
+ ENVELOPE *env;
+ BODY *body;
+ struct {
+ char fg[7];
+ char bg[7];
+ char fgdef[7];
+ char bgdef[7];
+ } color;
+} peED;
+
+
+/*
+ * RSS stream cache
+ */
+typedef struct _rss_cache_s {
+ char *link;
+ time_t stale;
+ int referenced;
+ RSS_FEED_S *feed;
+} RSS_CACHE_S;
+
+#define RSS_NEWS_CACHE_SIZE 1
+#define RSS_WEATHER_CACHE_SIZE 1
+
+
+#ifdef ENABLE_LDAP
+WPLDAP_S *wpldap_global;
+#endif
+
+/*
+ * random string generator flags
+ */
+#define PRS_NONE 0x0000
+#define PRS_LOWER_CASE 0x0001
+#define PRS_UPPER_CASE 0x0002
+#define PRS_MIXED_CASE 0x0004
+
+/*
+ * peSaveWork flag definitions
+ */
+#define PSW_NONE 0x00
+#define PSW_COPY 0x01
+#define PSW_MOVE 0x02
+
+/*
+ * Message Collector flags
+ */
+#define PMC_NONE 0x00
+#define PMC_FORCE_QUAL 0x01
+#define PMC_PRSRV_ATT 0x02
+
+/*
+ * length of thread info string
+ */
+#define WP_MAX_THRD_S 64
+
+/*
+ * static buf size for putenv() if necessary
+ */
+#define PUTENV_MAX 64
+
+
+
+/*----------------------------------------------------------------------
+ General use big buffer. It is used in the following places:
+ compose_mail: while parsing header of postponed message
+ append_message2: while writing header into folder
+ q_status_messageX: while doing printf formatting
+ addr_book: Used to return expanded address in. (Can only use here
+ because mm_log doesn't q_status on PARSE errors !)
+ alpine.c: When address specified on command line
+ init.c: When expanding variable values
+ and many many more...
+
+ ----*/
+char tmp_20k_buf[20480];
+
+
+
+
+/* Internal prototypes */
+void peReturn(int, char *, char *);
+int peWrite(int, char *);
+char *peCreateUserContext(Tcl_Interp *, char *, char *, char *);
+void peDestroyUserContext(struct pine **);
+char *peLoadConfig(struct pine *);
+int peCreateStream(Tcl_Interp *, CONTEXT_S *, char *, int);
+void peDestroyStream(struct pine *);
+void pePrepareForAuthException(void);
+char *peAuthException(void);
+void peInitVars(struct pine *);
+int peSelect(Tcl_Interp *, int, Tcl_Obj **, int);
+int peSelectNumber(Tcl_Interp *, int, Tcl_Obj **, int);
+int peSelectDate(Tcl_Interp *, int, Tcl_Obj **, int);
+int peSelectText(Tcl_Interp *, int, Tcl_Obj **, int);
+int peSelectStatus(Tcl_Interp *, int, Tcl_Obj **, int);
+char *peSelValTense(Tcl_Obj *);
+char *peSelValYear(Tcl_Obj *);
+char *peSelValMonth(Tcl_Obj *);
+char *peSelValDay(Tcl_Obj *);
+int peSelValCase(Tcl_Obj *);
+int peSelValField(Tcl_Obj *);
+int peSelValFlag(Tcl_Obj *);
+int peSelected(Tcl_Interp *, int, Tcl_Obj **, int);
+int peSelectError(Tcl_Interp *, char *);
+int peApply(Tcl_Interp *, int, Tcl_Obj **);
+char *peApplyFlag(MAILSTREAM *, MSGNO_S *, char, int, long *);
+int peApplyError(Tcl_Interp *, char *);
+int peIndexFormat(Tcl_Interp *);
+int peAppendIndexParts(Tcl_Interp *, imapuid_t, Tcl_Obj *, int *);
+int peAppendIndexColor(Tcl_Interp *, imapuid_t, Tcl_Obj *, int *);
+int peMessageStatusBits(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+char *peMsgStatBitString(struct pine *, MAILSTREAM *, MSGNO_S *, long, long, long, int *);
+Tcl_Obj *peMsgStatNameList(Tcl_Interp *, struct pine *, MAILSTREAM *, MSGNO_S *, long, long, long, int *);
+int peNewMailResult(Tcl_Interp *);
+int peMessageSize(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peMessageDate(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peMessageSubject(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peMessageFromAddr(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peMessageToAddr(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peMessageCcAddr(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peMessageField(Tcl_Interp *, imapuid_t, char *);
+int peMessageStatus(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peMessageCharset(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peMessageBounce(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peMessageSpamNotice(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+char *peSendSpamReport(long, char *, char *, char *);
+int peMsgnoFromUID(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peMessageText(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peMessageHeader(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+void peFormatEnvelope(MAILSTREAM *, long, char *, ENVELOPE *, gf_io_t, long, char *, int);
+void peFormatEnvelopeAddress(MAILSTREAM *, long, char *, char *, ADDRESS *, int, char *, gf_io_t);
+void peFormatEnvelopeNewsgroups(char *, char *, int, gf_io_t);
+void peFormatEnvelopeText(char *, char *);
+int peMessageAttachments(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peMessageBody(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peMessagePartFromCID(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peLocateBodyByCID(char *, char *, BODY *);
+char *peColorStr(char *, char *);
+int peInterpWritec(int);
+int peInterpFlush(void);
+int peNullWritec(int);
+void peGetMimeTyping(BODY *, Tcl_Obj **, Tcl_Obj **, Tcl_Obj **, Tcl_Obj **);
+int peGetFlag(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peIsFlagged(MAILSTREAM *, imapuid_t, char *);
+int peSetFlag(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peMsgSelect(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peReplyHeaders(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peAppListF(Tcl_Interp *, Tcl_Obj *, char *, ...);
+void pePatAppendID(Tcl_Interp *, Tcl_Obj *, PAT_S *);
+void pePatAppendPattern(Tcl_Interp *, Tcl_Obj *, PAT_S *);
+char *pePatStatStr(int);
+int peReplyText(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peSoStrToList(Tcl_Interp *, Tcl_Obj *, STORE_S *);
+int peForwardHeaders(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peForwardText(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peDetach(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peAttachInfo(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peSaveDefault(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peSaveWork(Tcl_Interp *, imapuid_t, int, Tcl_Obj **, long);
+int peSave(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peCopy(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peMove(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peGotoDefault(Tcl_Interp *, imapuid_t, Tcl_Obj **);
+int peTakeaddr(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peTakeFrom(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peAddSuggestedContactInfo(Tcl_Interp *, Tcl_Obj *, ADDRESS *);
+int peReplyQuote(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+long peMessageNumber(imapuid_t);
+long peSequenceNumber(imapuid_t);
+int peMsgCollector(Tcl_Interp *, int, Tcl_Obj **,
+ int (*)(METAENV *, BODY *, char *, CONTEXT_S **, char *), long);
+int peMsgCollected(Tcl_Interp *, MSG_COL_S *, char *, long);
+void peMsgSetParm(PARAMETER **, char *, char *);
+Tcl_Obj *peMsgAttachCollector(Tcl_Interp *, BODY *);
+int peFccAppend(Tcl_Interp *, Tcl_Obj *, char *, int);
+int peDoPost(METAENV *, BODY *, char *, CONTEXT_S **, char *);
+int peDoPostpone(METAENV *, BODY *, char *, CONTEXT_S **, char *);
+int peWriteSig (Tcl_Interp *, char *, Tcl_Obj **);
+int peInitAddrbooks(Tcl_Interp *, int);
+int peRuleStatVal(char *, int *);
+int peRuleSet(Tcl_Interp *, Tcl_Obj **);
+int peAppendCurrentSort(Tcl_Interp *interp);
+int peAppendDefaultSort(Tcl_Interp *interp);
+#if 0
+ADDRESS *peAEToAddress(AdrBk_Entry *);
+char *peAEFcc(AdrBk_Entry *);
+#endif
+NAMEVAL_S *sort_key_rules(int);
+NAMEVAL_S *wp_indexheight_rules(int);
+PINEFIELD *peCustomHdrs(void);
+STATMSG_S *sml_newmsg(int, char *);
+char *sml_getmsg(void);
+char **sml_getmsgs(void);
+void sml_seen(void);
+#ifdef ENABLE_LDAP
+int peLdapQueryResults(Tcl_Interp *);
+int peLdapStrlist(Tcl_Interp *, Tcl_Obj *, char **);
+int init_ldap_pname(struct pine *);
+#endif /* ENABLE_LDAP */
+char *strqchr(char *, int, int *, int);
+Tcl_Obj *wp_prune_folders(CONTEXT_S *, char *, int, char *,
+ unsigned, int *, int, Tcl_Interp *);
+int hex_colorstr(char *, char *);
+int hexval(char);
+int ascii_colorstr(char *, char *);
+COMPATT_S *peNewAttach(void);
+void peFreeAttach(COMPATT_S **);
+COMPATT_S *peGetAttachID(char *);
+char *peFileAttachID(char *, char *, char *, char *, char *, int);
+char *peBodyAttachID(BODY *);
+void peBodyMoveContents(BODY *, BODY *);
+int peClearAttachID(char *);
+char *peRandomString(char *, int, int);
+void ms_init(STRING *, void *, unsigned long);
+char ms_next(STRING *);
+void ms_setpos(STRING *, unsigned long);
+long peAppendMsg(MAILSTREAM *, void *, char **, char **, STRING **);
+int remote_pinerc_failure(void);
+char *peWebAlpinePrefix(void);
+void peNewMailAnnounce(MAILSTREAM *, long, long);
+int peMessageNeedPassphrase(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peRssReturnFeed(Tcl_Interp *, char *, char *);
+int peRssPackageFeed(Tcl_Interp *, RSS_FEED_S *);
+RSS_FEED_S *peRssFeed(Tcl_Interp *, char *, char *);
+RSS_FEED_S *peRssFetch(Tcl_Interp *, char *);
+void peRssComponentFree(char **,char **,char **,char **,char **,char **);
+void peRssClearCacheEntry(RSS_CACHE_S *);
+
+
+/* Prototypes for Tcl-exported methods */
+int PEInit(Tcl_Interp *interp, char *);
+void PEExitCleanup(ClientData);
+int PEInfoCmd(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[]);
+int PEConfigCmd(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[]);
+int PEDebugCmd(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[]);
+int PESessionCmd(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[]);
+int PEMailboxCmd(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[]);
+int PEThreadCmd(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[]);
+int PEMessageCmd(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[]);
+int PEFolderCmd(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[]);
+int PEComposeCmd(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[]);
+int PEPostponeCmd(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[]);
+int PEAddressCmd(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[]);
+int PEClistCmd(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[]);
+int PELdapCmd(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[]);
+int PERssCmd(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[]);
+
+/* Append package */
+typedef struct append_pkg {
+ MAILSTREAM *stream; /* source stream */
+ unsigned long msgno; /* current message number */
+ unsigned long msgmax; /* maximum message number */
+ char *flags; /* current flags */
+ char *date; /* message internal date */
+ STRING *message; /* stringstruct of message */
+} APPEND_PKG;
+
+STRINGDRIVER mstring = {
+ ms_init, /* initialize string structure */
+ ms_next, /* get next byte in string structure */
+ ms_setpos /* set position in string structure */
+};
+
+
+/*----------------------------------------------------------------------
+ main routine -- entry point
+
+ Args: argv, argc -- The command line arguments
+
+
+ Setup c-client drivers and dive into TCL interpreter engine
+
+ ----*/
+
+int
+main(int argc, char *argv[])
+{
+ int ev = 1, s, cs, n, co, o, l, bl = 256, argerr;
+ char *buf, sname[256];
+ struct sockaddr_un name;
+ Tcl_Interp *interp;
+#if PUBCOOKIE
+ extern AUTHENTICATOR auth_gss_proxy;
+#endif
+
+ srandom(getpid() + time(0));
+
+ /*----------------------------------------------------------------------
+ Initialize c-client
+ ----------------------------------------------------------------------*/
+
+ /*
+ * NO LOCAL DRIVERS ALLOWED
+ * For this to change pintecld *MUST* be running under the user's UID and
+ * and signal.[ch] need to get fixed to handle KOD rather than change
+ * the debug level
+ */
+ mail_link (&imapdriver); /* link in the imap driver */
+ mail_link (&unixdriver); /* link in the unix driver */
+ mail_link (&dummydriver); /* link in the dummy driver */
+
+ /* link authentication drivers */
+#if PUBCOOKIE
+ auth_link (&auth_gss_proxy); /* pubcoookie proxy authenticator */
+#endif
+ auth_link (&auth_md5); /* link in the md5 authenticator */
+ auth_link (&auth_pla);
+ auth_link (&auth_log); /* link in the log authenticator */
+ ssl_onceonlyinit ();
+ mail_parameters (NIL,SET_DISABLEPLAINTEXT,(void *) 2);
+
+#if PUBCOOKIE
+ /* if REMOTE_USER set, use it as username */
+ if(buf = getenv("REMOTE_USER"))
+ env_init(buf, "/tmp");
+#endif
+
+ if(!mail_parameters(NULL, DISABLE_DRIVER, "unix")){
+ fprintf(stderr, "Can't disable unix driver");
+ exit(1);
+ }
+
+ /*
+ * Set network timeouts so we don't hang forever
+ * The open timeout can be pretty short since we're
+ * just opening tcp connection. The read timeout needs
+ * to be longer because the response to some actions can
+ * take awhile. Hopefully this is well within httpd's
+ * cgi timeout threshold.
+ */
+ mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long) 30);
+ mail_parameters(NULL, SET_READTIMEOUT, (void *)(long) 60);
+
+ /*----------------------------------------------------------------------
+ Initialize pith library
+ ----------------------------------------------------------------------*/
+ pith_opt_remote_pinerc_failure = remote_pinerc_failure;
+ pith_opt_user_agent_prefix = peWebAlpinePrefix;
+ pith_opt_newmail_announce = peNewMailAnnounce;
+
+ setup_for_index_index_screen();
+
+
+ /*----------------------------------------------------------------------
+ Parse arguments
+ ----------------------------------------------------------------------*/
+ debug = 0;
+ for(argerr = 0; !argerr && ((n = getopt(argc,argv,"d")) != -1); ) {
+ switch(n) {
+ case 'd' : debug++; break;
+ case '?' : argerr = 1; break;
+ }
+ }
+
+ if(argerr || optind != argc){
+ char *p = strrchr(argv[0],'/');
+ fprintf(stderr, "Usage: %s [-d]\n", p ? p + 1 : argv[0]);
+ exit(1);
+ }
+
+ /*----------------------------------------------------------------------
+ Hop into the Tcl processing loop
+ ----------------------------------------------------------------------*/
+
+ buf = (char *) fs_get(bl * sizeof(char));
+
+ if(fgets(sname, 255, stdin) && *sname){
+ if(sname[l = strlen(sname) - 1] == '\n')
+ sname[l] = '\0';
+
+ if((s = socket(AF_UNIX, SOCK_STREAM, 0)) != -1){
+
+ name.sun_family = AF_UNIX;
+ strcpy(name.sun_path, peSocketName = sname);
+ l = sizeof(name);
+
+ if(bind(s, (struct sockaddr *) &name, l) == 0){
+ if(listen(s, 5) == 0){
+ /*
+ * after the groundwork's done, go into the background.
+ * the fork saves the caller from invoking us in the background
+ * which introduces a timing race between the first client
+ * request arrival and our being prepared to accept it.
+ */
+ if(debug < 10){
+ switch(fork()){
+ case -1 : /* error */
+ perror("fork");
+ exit(1);
+
+ case 0 : /* child */
+ close(0); /* disassociate */
+ close(1);
+ close(2);
+ setpgrp(0, 0);
+ break;
+
+ default : /* parent */
+ exit(0);
+ }
+ }
+
+ debug_init();
+ dprint((SYSDBG_INFO, "started"));
+
+ interp = Tcl_CreateInterp();
+
+ PEInit(interp, sname);
+
+ while(1){
+ struct timeval tv;
+ fd_set rfd;
+
+ FD_ZERO(&rfd);
+ FD_SET(s, &rfd);
+ tv.tv_sec = (gPEAbandonTimeout) ? gPEAbandonTimeout : gPeInputTimeout;
+ tv.tv_usec = 0;
+ if((n = select(s+1, &rfd, 0, 0, &tv)) > 0){
+ socklen_t ll = l;
+
+ gPEAbandonTimeout = 0;
+
+ if((cs = accept(s, (struct sockaddr *) &name, &ll)) == -1){
+ dprint((SYSDBG_ERR, "accept failure: %s",
+ error_description(errno)));
+ break;
+ }
+
+ dprint((5, "accept success: %d", cs));
+
+ /*
+ * tcl commands are prefixed with a number representing
+ * the length of the command string and a newline character.
+ * the characters representing the length and the newline
+ * are not included in the command line length calculation.
+ */
+ o = co = 0;
+ while((n = read(cs, buf + o, bl - o - 1)) > 0){
+ o += n;
+ if(!co){
+ int i, x = 0;
+
+ for(i = 0; i < o; i++)
+ if(buf[i] == '\n'){
+ co = ++i;
+ l = x + co;
+ if(bl < l + 1){
+ bl = l + 1;
+ fs_resize((void **) &buf, bl * sizeof(char));
+ }
+
+ break;
+ }
+ else
+ x = (x * 10) + (buf[i] - '0');
+ }
+
+ if(o && o == l)
+ break;
+ }
+
+ if(n == 0){
+ dprint((SYSDBG_ERR, "read EOF"));
+ }
+ else if(n < 0){
+ dprint((SYSDBG_ERR, "read failure: %s", error_description(errno)));
+ }
+ else{
+ buf[o] = '\0';
+
+ /* Log every Eval if somebody *really* wants to see it. */
+ if(debug > 6){
+ char dbuf[5120];
+ int dlim = (debug >= 9) ? 256 : 5120 - 32;
+
+ snprintf(dbuf, sizeof(dbuf), "Tcl_Eval(%.*s)", dlim, &buf[co]);
+
+ /* But DON'T log any clear-text credentials */
+ if(dbuf[9] == 'P'
+ && dbuf[10] == 'E'
+ && dbuf[11] == 'S'
+ && !strncmp(dbuf + 12, "ession creds ", 13)){
+ char *p;
+
+ for(p = &dbuf[25]; *p; p++)
+ *p = 'X';
+ }
+
+ dprint((1, dbuf));
+ }
+
+ switch(Tcl_Eval(interp, &buf[co])){
+ case TCL_OK : peReturn(cs, "OK", interp->result); break;
+ case TCL_ERROR : peReturn(cs, "ERROR", interp->result); break;
+ case TCL_BREAK : peReturn(cs, "BREAK", interp->result); break;
+ case TCL_RETURN : peReturn(cs, "RETURN", interp->result); break;
+ default : peReturn(cs, "BOGUS", "eval returned unexpected value"); break;
+ }
+ }
+
+ close(cs);
+ }
+ else if(errno != EINTR){
+ if(n < 0){
+ dprint((SYSDBG_ALERT, "select failure: %s", error_description(errno)));
+ }
+ else{
+ dprint((SYSDBG_INFO, "timeout after %d seconds", tv.tv_sec));
+ }
+
+ Tcl_Exit(0);
+
+ /* Tcl_Exit should never return. Getting here is an error. */
+ dprint((SYSDBG_ERR, "Tcl_Exit failure"));
+ }
+ }
+ }
+ else
+ perror("listen");
+ }
+ else
+ perror("bind");
+
+ close(s);
+ unlink(sname);
+ }
+ else
+ perror("socket");
+ }
+ else
+ fprintf(stderr, "Can't read socket name\n");
+
+ exit(ev);
+}
+
+
+/*
+ * peReturn - common routine to return TCL result
+ */
+void
+peReturn(int sock, char *status, char *result)
+{
+ if(peWrite(sock, status))
+ if(peWrite(sock, "\n"))
+ peWrite(sock, result);
+}
+
+/*
+ * peWrite - write all the given string on the given socket
+ */
+int
+peWrite(int sock, char *s)
+{
+ int i, n;
+
+ for(i = 0, n = strlen(s); n; n = n - i)
+ if((i = write(sock, s + i, n)) < 0){
+ dprint((SYSDBG_ERR, "write: %s", error_description(errno)));
+ return(0);
+ }
+
+ return(1);
+}
+
+/*
+ * PEInit - Initialize exported TCL functions
+ */
+int
+PEInit(Tcl_Interp *interp, char *sname)
+{
+ dprint((2, "PEInit: %s", sname));
+
+ if(Tcl_Init(interp) == TCL_ERROR) {
+ return(TCL_ERROR);
+ }
+
+ Tcl_CreateObjCommand(interp, "PEInfo", PEInfoCmd,
+ (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
+
+ Tcl_CreateObjCommand(interp, "PEConfig", PEConfigCmd,
+ (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
+
+ Tcl_CreateObjCommand(interp, "PEDebug", PEDebugCmd,
+ (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
+
+ Tcl_CreateObjCommand(interp, "PESession", PESessionCmd,
+ (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
+
+ Tcl_CreateObjCommand(interp, "PEFolder", PEFolderCmd,
+ (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
+
+ Tcl_CreateObjCommand(interp, "PEMailbox", PEMailboxCmd,
+ (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
+
+ Tcl_CreateObjCommand(interp, "PEThread", PEThreadCmd,
+ (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
+
+ Tcl_CreateObjCommand(interp, "PEMessage", PEMessageCmd,
+ (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
+
+ Tcl_CreateObjCommand(interp, "PECompose", PEComposeCmd,
+ (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
+
+ Tcl_CreateObjCommand(interp, "PEPostpone", PEPostponeCmd,
+ (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
+
+ Tcl_CreateObjCommand(interp, "PEAddress", PEAddressCmd,
+ (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
+
+ Tcl_CreateObjCommand(interp, "PEClist", PEClistCmd,
+ (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
+
+ Tcl_CreateObjCommand(interp, "PELdap", PELdapCmd,
+ (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
+
+ Tcl_CreateObjCommand(interp, "PERss", PERssCmd,
+ (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
+
+ Tcl_CreateExitHandler(PEExitCleanup, sname);
+
+#ifdef ENABLE_LDAP
+ wpldap_global = (WPLDAP_S *)fs_get(sizeof(WPLDAP_S));
+ wpldap_global->query_no = 0;
+ wpldap_global->ldap_search_list = NULL;
+#endif /* ENABLE_LDAP */
+
+ return(TCL_OK);
+}
+
+
+void
+PEExitCleanup(ClientData clientData)
+{
+ dprint((4, "PEExitCleanup"));
+
+ if(ps_global){
+ /* destroy any open stream */
+ peDestroyStream(ps_global);
+
+ /* destroy user context */
+ peDestroyUserContext(&ps_global);
+ }
+
+#ifdef ENABLE_LDAP
+ if(wpldap_global){
+ if(wpldap_global->ldap_search_list)
+ free_wpldapres(wpldap_global->ldap_search_list);
+ fs_give((void **)&wpldap_global);
+ }
+#endif /* ENABLE_LDAP */
+
+ if((char *) clientData)
+ unlink((char *) clientData);
+
+ peFreeAttach(&peCompAttach);
+
+ dprint((SYSDBG_INFO, "finished"));
+}
+
+
+/*
+ * PEInfoCmd - export various bits of alpine state
+ */
+int
+PEInfoCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+ char *err = "Unknown PEInfo request";
+
+ dprint((2, "PEInfoCmd"));
+
+ if(objc == 1){
+ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
+ }
+ else{
+ char *s1 = Tcl_GetStringFromObj(objv[1], NULL);
+
+ if(s1){
+ if(!strcmp(s1, "colorset")){
+ char *varname, *fghex, *bghex;
+ char tvname[256], asciicolor[256];
+ struct variable *vtmp;
+ Tcl_Obj **cObj;
+ int cObjc;
+ SPEC_COLOR_S *hcolors, *thc;
+
+ if(!(varname = Tcl_GetStringFromObj(objv[2], NULL))){
+ Tcl_SetResult(interp, "colorset: can't read variable name", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ if(!strcmp(varname, "viewer-hdr-colors")){
+ char *newhdr = NULL, *newpat = NULL, *utype;
+ int hindex, i;
+
+ if(objc < 5){
+ Tcl_SetResult(interp, "colorset: too few view-hdr args", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ hcolors = spec_colors_from_varlist(ps_global->VAR_VIEW_HDR_COLORS, 0);
+ if(!(utype = Tcl_GetStringFromObj(objv[3], NULL))){
+ Tcl_SetResult(interp, "colorset: can't read operation", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ if(!strcmp(utype, "delete")){
+ if(!hcolors){
+ Tcl_SetResult(interp, "colorset: no viewer-hdrs to delete", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ if(Tcl_GetIntFromObj(interp, objv[4], &hindex) == TCL_ERROR){
+ Tcl_SetResult(interp, "colorset: can't read index", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ if(hindex == 0){
+ thc = hcolors;
+ hcolors = hcolors->next;
+ thc->next = NULL;
+ free_spec_colors(&thc);
+ }
+ else{
+ /* zero based */
+ for(thc = hcolors, i = 1; thc && i < hindex; thc = thc->next, i++)
+ ;
+
+ if(thc && thc->next){
+ SPEC_COLOR_S *thc2 = thc->next;
+
+ thc->next = thc2->next;
+ thc2->next = NULL;
+ free_spec_colors(&thc2);
+ }
+ else{
+ Tcl_SetResult(interp, "colorset: invalid index", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ }
+ }
+ else if(!strcmp(utype, "add")){
+ if(objc != 6){
+ Tcl_SetResult(interp, "colorset: wrong number of view-hdr add args", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ if(Tcl_ListObjGetElements(interp, objv[4], &cObjc, &cObj) != TCL_OK)
+ return (TCL_ERROR);
+
+ if(cObjc != 2){
+ Tcl_SetResult(interp, "colorset: wrong number of hdrs for view-hdr add", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ newhdr = Tcl_GetStringFromObj(cObj[0], NULL);
+ newpat = Tcl_GetStringFromObj(cObj[1], NULL);
+ if(Tcl_ListObjGetElements(interp, objv[5], &cObjc, &cObj) != TCL_OK)
+ return (TCL_ERROR);
+
+ if(cObjc != 2){
+ Tcl_SetResult(interp, "colorset: wrong number of colors for view-hdr add", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ fghex = Tcl_GetStringFromObj(cObj[0], NULL);
+ bghex = Tcl_GetStringFromObj(cObj[1], NULL);
+ if(newhdr && newpat && fghex && bghex){
+ SPEC_COLOR_S **hcp;
+
+ for(hcp = &hcolors; *hcp != NULL; hcp = &(*hcp)->next)
+ ;
+
+ *hcp = (SPEC_COLOR_S *)fs_get(sizeof(SPEC_COLOR_S));
+ (*hcp)->inherit = 0;
+ (*hcp)->spec = cpystr(newhdr);
+ (*hcp)->fg = cpystr((ascii_colorstr(asciicolor, fghex) == 0) ? asciicolor : "black");
+ (*hcp)->bg = cpystr((ascii_colorstr(asciicolor, bghex) == 0) ? asciicolor : "white");
+
+ if(newpat && *newpat)
+ (*hcp)->val = string_to_pattern(newpat);
+ else
+ (*hcp)->val = NULL;
+
+ (*hcp)->next = NULL;
+ }
+ else{
+ Tcl_SetResult(interp, "colorset: invalid args for view-hdr add", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ }
+ else if(!strcmp(utype, "update")){
+ if(objc != 6){
+ Tcl_SetResult(interp, "colorset: wrong number of view-hdr update args", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ if(!(Tcl_ListObjGetElements(interp, objv[4], &cObjc, &cObj) == TCL_OK
+ && cObjc == 3
+ && Tcl_GetIntFromObj(interp, cObj[0], &hindex) == TCL_OK
+ && (newhdr = Tcl_GetStringFromObj(cObj[1], NULL))
+ && (newpat = Tcl_GetStringFromObj(cObj[2], NULL)))){
+ Tcl_SetResult(interp, "colorset: view-hdr update can't read index or header", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ if(!(Tcl_ListObjGetElements(interp, objv[5], &cObjc, &cObj) == TCL_OK
+ && cObjc == 2
+ && (fghex = Tcl_GetStringFromObj(cObj[0], NULL))
+ && (bghex = Tcl_GetStringFromObj(cObj[1], NULL)))){
+ Tcl_SetResult(interp, "colorset: view-hdr update can't read colors", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ for(thc = hcolors, i = 0; thc && i < hindex; thc = thc->next, i++)
+ ;
+
+ if(!thc){
+ Tcl_SetResult(interp, "colorset: view-hdr update invalid index", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ if(thc->spec)
+ fs_give((void **)&thc->spec);
+
+ thc->spec = cpystr(newhdr);
+ if(ascii_colorstr(asciicolor, fghex) == 0) {
+ if(thc->fg)
+ fs_give((void **)&thc->fg);
+
+ thc->fg = cpystr(asciicolor);
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid foreground color value %.100s", fghex);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ if(ascii_colorstr(asciicolor, bghex) == 0) {
+ if(thc->bg)
+ fs_give((void **)&thc->bg);
+
+ thc->bg = cpystr(asciicolor);
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background color value %.100s", bghex);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ if(thc->val)
+ fs_give((void **)&thc->val);
+
+ if(newpat && *newpat){
+ thc->val = string_to_pattern(newpat);
+ }
+ }
+ else{
+ Tcl_SetResult(interp, "colorset: unknown operation", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ vtmp = &ps_global->vars[V_VIEW_HDR_COLORS];
+ for(i = 0; vtmp->main_user_val.l && vtmp->main_user_val.l[i]; i++)
+ fs_give((void **)&vtmp->main_user_val.l[i]);
+
+ if(vtmp->main_user_val.l)
+ fs_give((void **)&vtmp->main_user_val.l);
+
+ vtmp->main_user_val.l = varlist_from_spec_colors(hcolors);
+ set_current_val(vtmp, FALSE, FALSE);
+ free_spec_colors(&hcolors);
+ return(TCL_OK);
+ }
+ else {
+ if(objc != 4){
+ Tcl_SetResult(interp, "colorset: Wrong number of args", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ if(!(Tcl_ListObjGetElements(interp, objv[3], &cObjc, &cObj) == TCL_OK
+ && cObjc == 2
+ && (fghex = Tcl_GetStringFromObj(cObj[0], NULL))
+ && (bghex = Tcl_GetStringFromObj(cObj[1], NULL)))){
+ Tcl_SetResult(interp, "colorset: Problem reading fore/back ground colors", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ snprintf(tvname, sizeof(tvname), "%.200s-foreground-color", varname);
+ for(vtmp = &ps_global->vars[V_NORM_FORE_COLOR];
+ vtmp->name && strucmp(vtmp->name, tvname);
+ vtmp++)
+ ;
+
+ if(!vtmp->name || vtmp->is_list){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background var %.100s", varname);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ if(ascii_colorstr(asciicolor, fghex) == 0) {
+ if(vtmp->main_user_val.p)
+ fs_give((void **)&vtmp->main_user_val.p);
+
+ vtmp->main_user_val.p = cpystr(asciicolor);
+ set_current_val(vtmp, FALSE, FALSE);
+ if(!strucmp(varname, "normal"))
+ pico_set_fg_color(asciicolor);
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid color value %.100s", fghex);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ snprintf(tvname, sizeof(tvname), "%.200s%.50s", varname, "-background-color");
+ vtmp++;
+ if((vtmp->name && strucmp(vtmp->name, tvname)) || !vtmp->name)
+ for(vtmp = &ps_global->vars[V_NORM_FORE_COLOR];
+ vtmp->name && strucmp(vtmp->name, tvname);
+ vtmp++)
+ ;
+
+ if(!vtmp->name || vtmp->is_list){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background var %.100s", varname);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ if(ascii_colorstr(asciicolor, bghex) == 0) {
+ if(vtmp->main_user_val.p)
+ fs_give((void **)&vtmp->main_user_val.p);
+
+ vtmp->main_user_val.p = cpystr(asciicolor);
+ set_current_val(vtmp, FALSE, FALSE);
+ if(!strucmp(varname, "normal"))
+ pico_set_bg_color(asciicolor);
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background color value %.100s", bghex);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ Tcl_SetResult(interp, "1", TCL_STATIC);
+ return(TCL_OK);
+ }
+ }
+ else if(!strcmp(s1, "lappend")){
+ if(objc >= 4){
+ Tcl_Obj *dObj;
+ int i;
+
+ if((dObj = Tcl_ObjGetVar2(interp, objv[2], NULL, TCL_LEAVE_ERR_MSG)) != NULL){
+ for(i = 3; i < objc; i++)
+ if(Tcl_ListObjAppendElement(interp, dObj, objv[i]) != TCL_OK)
+ return(TCL_ERROR);
+
+ if(i == objc){
+ return(TCL_OK);
+ }
+ }
+ else
+ err = "PEInfo lappend: Unknown list name";
+ }
+ else
+ err = "PEInfo lappend: Too few args";
+ }
+ else if(objc == 2){
+ if(!strcmp(s1, "version")){
+ char buf[256];
+
+ /*
+ * CMD: version
+ *
+ * Returns: string representing Pine version
+ * engine built on
+ */
+ Tcl_SetResult(interp, ALPINE_VERSION, TCL_STATIC);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "revision")){
+ char buf[16];
+
+ /*
+ * CMD: revision
+ *
+ * Returns: string representing Pine SVN revision
+ * engine built on
+ */
+
+ Tcl_SetResult(interp, get_alpine_revision_number(buf, sizeof(buf)), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "key")){
+ static char key[64];
+
+ if(!key[0])
+ peRandomString(key,32,PRS_UPPER_CASE);
+
+ Tcl_SetResult(interp, key, TCL_STATIC);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "indexheight")){
+ Tcl_SetResult(interp, ps_global->VAR_WP_INDEXHEIGHT ?
+ ps_global->VAR_WP_INDEXHEIGHT : "", TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "indexlines")){
+ Tcl_SetResult(interp, ps_global->VAR_WP_INDEXLINES ?
+ ps_global->VAR_WP_INDEXLINES : "0", TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "aggtabstate")){
+ Tcl_SetResult(interp, ps_global->VAR_WP_AGGSTATE ?
+ ps_global->VAR_WP_AGGSTATE : "0", TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "alpinestate")){
+ char *wps, *p, *q;
+
+ if((wps = ps_global->VAR_WP_STATE) != NULL){
+ wps = p = q = cpystr(wps);
+ do
+ if(*q == '\\' && *(q+1) == '$')
+ q++;
+ while((*p++ = *q++) != '\0');
+ }
+
+ Tcl_SetResult(interp, wps ? wps : "", TCL_VOLATILE);
+
+ if(wps)
+ fs_give((void **) &wps);
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "foreground")){
+ char *color;
+
+ if(!((color = pico_get_last_fg_color())
+ && (color = color_to_asciirgb(color))
+ && (color = peColorStr(color,tmp_20k_buf))))
+ color = "000000";
+
+ Tcl_SetResult(interp, color, TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "background")){
+ char *color;
+
+ if(!((color = pico_get_last_bg_color())
+ && (color = color_to_asciirgb(color))
+ && (color = peColorStr(color,tmp_20k_buf))))
+ color = "FFFFFF";
+
+ Tcl_SetResult(interp, color, TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "flaglist")){
+ int i;
+ char *p;
+ Tcl_Obj *itemObj;
+
+ /*
+ * BUG: This list should get merged with the static list in "cmd_flag"
+ * and exported via some function similar to "feature_list()"
+ */
+ static char *flag_list[] = {
+ "Important", "New", "Answered", "Deleted", NULL
+ };
+
+ /*
+ * CMD: flaglist
+ *
+ * Returns: list of FLAGS available for setting
+ */
+ for(i = 0; (p = flag_list[i]); i++)
+ if((itemObj = Tcl_NewStringObj(p, -1)) != NULL){
+ if(Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ itemObj) != TCL_OK)
+ ;
+ }
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "featurelist")){
+ int i;
+ char *curfeature, *s;
+ FEATURE_S *feature;
+ Tcl_Obj *itemObj, *secObj = NULL, *resObj = NULL;
+
+ /*
+ * CMD: featurelist
+ *
+ * Returns: list of FEATURES available for setting
+ */
+ for(i = 0, curfeature = NULL; (feature = feature_list(i)); i++)
+ if((s = feature_list_section(feature)) != NULL){
+ if(!curfeature || strucmp(s, curfeature)){
+ if(resObj) {
+ Tcl_ListObjAppendElement(interp,
+ secObj,
+ resObj);
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ secObj);
+ }
+
+ secObj = Tcl_NewListObj(0, NULL);
+ resObj = Tcl_NewListObj(0, NULL);
+ if(Tcl_ListObjAppendElement(interp,
+ secObj,
+ Tcl_NewStringObj(s,-1)) != TCL_OK)
+ ;
+
+ curfeature = s;
+ }
+
+ if((itemObj = Tcl_NewStringObj(feature->name, -1)) != NULL){
+ if(Tcl_ListObjAppendElement(interp,
+ resObj,
+ itemObj) != TCL_OK)
+ ;
+ }
+ }
+
+ if(resObj){
+ Tcl_ListObjAppendElement(interp, secObj, resObj);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), secObj);
+ }
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "featuresettings")){
+ int i;
+ FEATURE_S *feature;
+ Tcl_Obj *itemObj;
+
+ /*
+ * CMD: featuresettings
+ *
+ * Returns: list of FEATURES currently SET
+ */
+ for(i = 0; (feature = feature_list(i)); i++)
+ if(feature_list_section(feature)){
+ if(F_ON(feature->id, ps_global)){
+ if((itemObj = Tcl_NewStringObj(feature->name, -1)) != NULL){
+ if(Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ itemObj) != TCL_OK)
+ ;
+ }
+ }
+ }
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "signature")){
+ char *sig;
+
+ if((ps_global->VAR_LITERAL_SIG
+ || (ps_global->VAR_SIGNATURE_FILE
+ && IS_REMOTE(ps_global->VAR_SIGNATURE_FILE)))
+ && (sig = detoken(NULL, NULL, 2, 0, 1, NULL, NULL))){
+ char *p, *q;
+
+ for(p = sig; (q = strindex(p, '\n')); p = q + 1)
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(p, q - p));
+
+ if(*p)
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(p, -1));
+
+ fs_give((void **) &sig);
+ }
+ else
+ Tcl_SetResult(interp, "", TCL_STATIC);
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "rawsig")){
+ char *err = NULL, *sig = NULL, *p, *q;
+
+ if(ps_global->VAR_LITERAL_SIG){
+ char *err = NULL;
+ char **apval;
+
+ if(ps_global->restricted){
+ err = "Alpine demo can't change config file";
+ }
+ else{
+ /* BUG: no "exceptions file" support */
+ if((apval = APVAL(&ps_global->vars[V_LITERAL_SIG], Main)) != NULL){
+ sig = (char *) fs_get((strlen(*apval ? *apval : "") + 1) * sizeof(char));
+ sig[0] = '\0';
+ cstring_to_string(*apval, sig);
+ }
+ else
+ err = "Problem accessing configuration";
+ }
+ }
+ else if(!IS_REMOTE(ps_global->VAR_SIGNATURE_FILE))
+ snprintf(err = tmp_20k_buf, SIZEOF_20KBUF, "Non-Remote signature file: %s",
+ ps_global->VAR_SIGNATURE_FILE ? ps_global->VAR_SIGNATURE_FILE : "<null>");
+ else if(!(sig = simple_read_remote_file(ps_global->VAR_SIGNATURE_FILE, REMOTE_SIG_SUBTYPE)))
+ err = "Can't read remote pinerc";
+
+ if(err){
+ Tcl_SetResult(interp, err, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ for(p = sig; (q = strindex(p, '\n')); p = q + 1)
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(p, q - p));
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(p, -1));
+ fs_give((void **) &sig);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "statmsg")){
+ char *s = sml_getmsg();
+ /* BUG: can this be removed? */
+
+ Tcl_SetResult(interp, s ? s : "", TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "statmsgs")){
+ char **s = sml_getmsgs();
+ char **tmps, *lmsg = NULL;
+
+ for(tmps = s; tmps && *tmps; lmsg = *tmps++)
+ if(!lmsg || strcmp(lmsg, *tmps))
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(*tmps, -1));
+
+ fs_give((void **)&s);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "saveconf")){
+ write_pinerc(ps_global, Main, WRP_NOUSER);
+ return(TCL_OK);
+ }
+ else if(!strucmp(s1, "sort")){
+ return(peAppendDefaultSort(interp));
+ }
+ else if(!strcmp(s1, "ldapenabled")){
+ /*
+ * CMD: ldapenabled
+ *
+ * Returns: 1 if enabled 0 if not
+ */
+#ifdef ENABLE_LDAP
+ Tcl_SetResult(interp, "1", TCL_VOLATILE);
+#else
+ Tcl_SetResult(interp, "0", TCL_VOLATILE);
+#endif
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "prunecheck")){
+ time_t now;
+ struct tm *tm_now;
+ char tmp[50];
+
+ if(!check_prune_time(&now, &tm_now)){
+ Tcl_SetResult(interp, "0", TCL_VOLATILE);
+ return(TCL_OK);
+ } else {
+ /*
+ * We're going to reset the last-time-pruned variable
+ * so that it asks a maximum of 1 time per month.
+ * PROs: Annoying-factor is at its lowest
+ * Can go ahead and move folders right away if
+ * pruning-rule is automatically set to do so
+ * CONs: Annoying-factor is at its lowest, if it's set
+ * later then we can ensure that the questions
+ * actually get answered or it will keep asking
+ */
+ ps_global->last_expire_year = tm_now->tm_year;
+ ps_global->last_expire_month = tm_now->tm_mon;
+ snprintf(tmp, sizeof(tmp), "%d.%d", ps_global->last_expire_year,
+ ps_global->last_expire_month + 1);
+ set_variable(V_LAST_TIME_PRUNE_QUESTION, tmp, 0, 1, Main);
+
+ Tcl_SetResult(interp, "1", TCL_VOLATILE);
+ }
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "prunetime")){
+ time_t now;
+ struct tm *tm_now;
+ CONTEXT_S *prune_cntxt;
+ Tcl_Obj *retObj = NULL;
+ int cur_month, ok = 1;
+ char **p;
+ static int moved_fldrs = 0;
+
+ now = time((time_t *)0);
+ tm_now = localtime(&now);
+ cur_month = (1900 + tm_now->tm_year) * 12 + tm_now->tm_mon;
+
+ if(!(prune_cntxt = default_save_context(ps_global->context_list)))
+ prune_cntxt = ps_global->context_list;
+
+ if(prune_cntxt){
+ if(ps_global->VAR_DEFAULT_FCC && *ps_global->VAR_DEFAULT_FCC
+ && context_isambig(ps_global->VAR_DEFAULT_FCC))
+ if((retObj = wp_prune_folders(prune_cntxt,
+ ps_global->VAR_DEFAULT_FCC,
+ cur_month, "sent",
+ ps_global->pruning_rule, &ok,
+ moved_fldrs, interp)) != NULL)
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ retObj);
+
+ if(ok && ps_global->VAR_READ_MESSAGE_FOLDER
+ && *ps_global->VAR_READ_MESSAGE_FOLDER
+ && context_isambig(ps_global->VAR_READ_MESSAGE_FOLDER))
+ if((retObj = wp_prune_folders(prune_cntxt,
+ ps_global->VAR_READ_MESSAGE_FOLDER,
+ cur_month, "read",
+ ps_global->pruning_rule, &ok,
+ moved_fldrs, interp)) != NULL)
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ retObj);
+ if(ok && (p = ps_global->VAR_PRUNED_FOLDERS)){
+ for(; ok && *p; p++)
+ if(**p && context_isambig(*p))
+ if((retObj = wp_prune_folders(prune_cntxt,
+ *p, cur_month, "",
+ ps_global->pruning_rule, &ok,
+ moved_fldrs, interp)) != NULL)
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ retObj);
+ }
+ }
+ moved_fldrs = 1;
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "authrequestor")){
+ Tcl_SetResult(interp, peCredentialRequestor, TCL_STATIC);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "noop")){
+ /* tickle the imap server too */
+ if(ps_global->mail_stream)
+ pine_mail_ping(ps_global->mail_stream);
+
+ Tcl_SetResult(interp, "NOOP", TCL_STATIC);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "inputtimeout")){
+ Tcl_SetResult(interp, int2string(get_input_timeout()), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ }
+ else if(objc == 3){
+ if(!strcmp(s1, "feature")){
+ char *featurename;
+ int i, isset = 0;
+ FEATURE_S *feature;
+
+ /*
+ * CMD: feature
+ *
+ * ARGS: featurename -
+ *
+ * Returns: 1 if named feature set, 0 otherwise
+ *
+ */
+ if((featurename = Tcl_GetStringFromObj(objv[2], NULL)) != NULL)
+ for(i = 0; (feature = feature_list(i)); i++)
+ if(!strucmp(featurename, feature->name)){
+ isset = F_ON(feature->id, ps_global);
+ break;
+ }
+
+ Tcl_SetResult(interp, int2string(isset), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "colorget")){
+ char *varname;
+ char tvname[256], hexcolor[256];
+ struct variable *vtmp;
+ if(!(varname = Tcl_GetStringFromObj(objv[2], NULL))){
+ return(TCL_ERROR);
+ }
+ if(strcmp("viewer-hdr-colors", varname) == 0){
+ SPEC_COLOR_S *hcolors, *thc;
+ Tcl_Obj *resObj;
+ char hexcolor[256], *tstr = NULL;
+
+ hcolors = spec_colors_from_varlist(ps_global->VAR_VIEW_HDR_COLORS, 0);
+ for(thc = hcolors; thc; thc = thc->next){
+ resObj = Tcl_NewListObj(0,NULL);
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(thc->spec, -1));
+ hex_colorstr(hexcolor, thc->fg);
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(hexcolor, -1));
+ hex_colorstr(hexcolor, thc->bg);
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(hexcolor, -1));
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(thc->val
+ ? tstr = pattern_to_string(thc->val)
+ : "", -1));
+ if(tstr) fs_give((void **)&tstr);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ resObj);
+ }
+ fs_give((void **)&hcolors);
+ return(TCL_OK);
+ }
+ else {
+ snprintf(tvname, sizeof(tvname), "%.200s%.50s", varname, "-foreground-color");
+ for(vtmp = &ps_global->vars[V_NORM_FORE_COLOR];
+ vtmp->name && strucmp(vtmp->name, tvname);
+ vtmp++);
+ if(!vtmp->name) return(TCL_ERROR);
+ if(vtmp->is_list) return(TCL_ERROR);
+ if(!vtmp->current_val.p)
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj("", -1));
+ else{
+ hex_colorstr(hexcolor, vtmp->current_val.p);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(hexcolor, -1));
+ }
+ snprintf(tvname, sizeof(tvname), "%.200s%.50s", varname, "-background-color");
+ vtmp++;
+ if((vtmp->name && strucmp(vtmp->name, tvname)) || !vtmp->name)
+ for(vtmp = &ps_global->vars[V_NORM_FORE_COLOR];
+ vtmp->name && strucmp(vtmp->name, tvname);
+ vtmp++)
+ ;
+
+ if(!vtmp->name) return(TCL_ERROR);
+ if(vtmp->is_list) return(TCL_ERROR);
+ if(!vtmp->current_val.p)
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj("", -1));
+ else{
+ hex_colorstr(hexcolor, vtmp->current_val.p);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(hexcolor, -1));
+ }
+ }
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "varget")){
+ struct variable *vtmp;
+ Tcl_Obj *itemObj, *resObj, *secObj;
+ char *vallist, *varname, tmperrmsg[256];
+ int i;
+ NAMEVAL_S *tmpnv;
+
+ /*
+ * CMD: varget
+ *
+ * Returns: get the values for the requested variable
+ *
+ * The list returned follows this general form:
+ *
+ * char *; variable name
+ * char **; list of set values
+ * char *; display type (listbox, text, textarea, ...)
+ * char **; list of possible values
+ * (so far this is only useful for listboxes)
+ */
+ if(!(varname = Tcl_GetStringFromObj(objv[2], NULL))){
+ Tcl_SetResult(interp, "Can't Tcl_GetStringFromObj",
+ TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ for(vtmp = ps_global->vars;
+ vtmp->name && strucmp(vtmp->name, varname);
+ vtmp++)
+ ;
+
+ if(!vtmp->name){
+ snprintf(tmperrmsg, sizeof(tmperrmsg), "Can't find variable named %s",
+ strlen(varname) < 200 ? varname : "");
+ Tcl_SetResult(interp, tmperrmsg, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ if((itemObj = Tcl_NewStringObj(vtmp->name, -1)) != NULL){
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ itemObj);
+ resObj = Tcl_NewListObj(0, NULL);
+ if(vtmp->is_list){
+ for(i = 0 ; vtmp->current_val.l && vtmp->current_val.l[i]; i++){
+ vallist = vtmp->current_val.l[i];
+ if(*(vallist))
+ itemObj = Tcl_NewStringObj(vallist, -1);
+ else
+ itemObj = Tcl_NewStringObj("", -1);
+ Tcl_ListObjAppendElement(interp, resObj, itemObj);
+ }
+ }
+ else{
+ itemObj = Tcl_NewStringObj(vtmp->current_val.p ?
+ vtmp->current_val.p : "", -1);
+ Tcl_ListObjAppendElement(interp, resObj, itemObj);
+ }
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ resObj);
+ secObj = Tcl_NewListObj(0, NULL);
+ if(vtmp->is_list)
+ itemObj = Tcl_NewStringObj("textarea", -1);
+ else{
+ NAMEVAL_S *(*tmpf)(int);
+ switch(vtmp - ps_global->vars){
+ case V_SAVED_MSG_NAME_RULE:
+ tmpf = save_msg_rules;
+ break;
+ case V_FCC_RULE:
+ tmpf = fcc_rules;
+ break;
+ case V_SORT_KEY:
+ tmpf = sort_key_rules;
+ break;
+ case V_AB_SORT_RULE:
+ tmpf = ab_sort_rules;
+ break;
+ case V_FLD_SORT_RULE:
+ tmpf = fld_sort_rules;
+ break;
+ case V_GOTO_DEFAULT_RULE:
+ tmpf = goto_rules;
+ break;
+ case V_INCOMING_STARTUP:
+ tmpf = incoming_startup_rules;
+ break;
+ case V_PRUNING_RULE:
+ tmpf = pruning_rules;
+ break;
+ case V_WP_INDEXHEIGHT:
+ tmpf = wp_indexheight_rules;
+ break;
+ default:
+ tmpf = NULL;
+ break;
+ }
+ if(tmpf){
+ for(i = 0; (tmpnv = (tmpf)(i)); i++){
+ itemObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, itemObj,
+ Tcl_NewStringObj(tmpnv->name, -1));
+ if(tmpnv->shortname)
+ Tcl_ListObjAppendElement(interp, itemObj,
+ Tcl_NewStringObj(tmpnv->shortname, -1));
+ Tcl_ListObjAppendElement(interp, secObj, itemObj);
+ }
+ itemObj = Tcl_NewStringObj("listbox", -1);
+ }
+ else
+ itemObj = Tcl_NewStringObj("text", -1);
+ }
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ itemObj);
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ secObj);
+ }
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "rawsig")){
+
+ if(ps_global->VAR_LITERAL_SIG){
+ char *cstring_version, *sig, *line;
+ int i, nSig;
+ Tcl_Obj **objSig;
+
+ tmp_20k_buf[0] = '\0';
+ Tcl_ListObjGetElements(interp, objv[2], &nSig, &objSig);
+ for(i = 0; i < nSig && i < SIG_MAX_LINES; i++)
+ if((line = Tcl_GetStringFromObj(objSig[i], NULL)) != NULL)
+ snprintf(tmp_20k_buf + strlen(tmp_20k_buf), SIZEOF_20KBUF - strlen(tmp_20k_buf), "%.*s\n", SIG_MAX_COLS, line);
+
+ sig = cpystr(tmp_20k_buf);
+
+ if((cstring_version = string_to_cstring(sig)) != NULL){
+ set_variable(V_LITERAL_SIG, cstring_version, 0, 0, Main);
+ fs_give((void **)&cstring_version);
+ }
+
+ fs_give((void **) &sig);
+ return(TCL_OK);
+ }
+ else
+ return(peWriteSig(interp, ps_global->VAR_SIGNATURE_FILE,
+ &((Tcl_Obj **)objv)[2]));
+ }
+ else if(!strcmp(s1, "statmsg")){
+ char *msg;
+
+ /*
+ * CMD: statmsg
+ *
+ * ARGS: msg - text to set
+ *
+ * Returns: nothing, but with global status message
+ * buf set to given msg
+ *
+ */
+ if((msg = Tcl_GetStringFromObj(objv[2], NULL)) != NULL)
+ sml_addmsg(0, msg);
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "mode")){
+ char *mode;
+ int rv = 0;
+
+ /*
+ * CMD: mode
+ *
+ * ARGS: <mode>
+ *
+ * Returns: return value of given binary mode
+ *
+ */
+ if((mode = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
+ if(!strcmp(mode, "full-header-mode"))
+ rv = ps_global->full_header;
+ }
+
+ Tcl_SetResult(interp, int2string(rv), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "indexlines")){
+ int n;
+ char *p;
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &n) == TCL_OK){
+ set_variable(V_WP_INDEXLINES, p = int2string(n), 0, 0, Main);
+ Tcl_SetResult(interp, p, TCL_VOLATILE);
+ }
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "aggtabstate")){
+ int n;
+ char *p;
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &n) == TCL_OK){
+ set_variable(V_WP_AGGSTATE, p = int2string(n), 0, 0, Main);
+ Tcl_SetResult(interp, p, TCL_VOLATILE);
+ }
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "alpinestate")){
+ char *wps, *p, *q, *twps = NULL;
+ int dollars = 0;
+
+ if((wps = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
+ for(p = wps; *p; p++)
+ if(*p == '$')
+ dollars++;
+
+ if(dollars){
+ twps = (char *) fs_get(((p - wps) + (dollars + 1)) * sizeof(char));
+ p = wps;
+ q = twps;
+ do{
+ if(*p == '$')
+ *q++ = '\\';
+ }
+ while((*q++ = *p++) != '\0');
+ }
+
+ set_variable(V_WP_STATE, twps ? twps : wps, 0, 1, Main);
+ Tcl_SetResult(interp, wps, TCL_VOLATILE);
+ if(twps)
+ fs_give((void **) &twps);
+ }
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "set")){
+ Tcl_Obj *rObj;
+
+ if((rObj = Tcl_ObjGetVar2(interp, objv[2], NULL, TCL_LEAVE_ERR_MSG)) != NULL){
+ Tcl_SetObjResult(interp, rObj);
+ return(TCL_OK);
+ }
+ else
+ return(TCL_ERROR);
+ }
+ else if(!strcmp(s1, "unset")){
+ char *varname;
+
+ return((varname = Tcl_GetStringFromObj(objv[2], NULL)) ? Tcl_UnsetVar2(interp, varname, NULL, TCL_LEAVE_ERR_MSG) : TCL_ERROR);
+ }
+ }
+ else if(objc == 4){
+ if(!strcmp(s1, "feature")){
+ char *featurename;
+ int i, set, wasset = 0;
+ FEATURE_S *feature;
+
+ /*
+ * CMD: feature
+ *
+ * ARGS: featurename -
+ * value - new value to assign flag
+ *
+ * Returns: 1 if named feature set, 0 otherwise
+ *
+ */
+ if((featurename = Tcl_GetStringFromObj(objv[2], NULL))
+ && Tcl_GetIntFromObj(interp, objv[3], &set) != TCL_ERROR)
+ for(i = 0; (feature = feature_list(i)); i++)
+ if(!strucmp(featurename, feature->name)){
+ if(set != F_ON(feature->id, ps_global)){
+ toggle_feature(ps_global,
+ &ps_global->vars[V_FEATURE_LIST],
+ feature, TRUE, Main);
+
+ if(ps_global->prc)
+ ps_global->prc->outstanding_pinerc_changes = 1;
+ }
+
+ break;
+ }
+
+ Tcl_SetResult(interp, int2string(wasset), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strucmp(s1, "help")){
+ HelpType text;
+ int i;
+ char **help_text, **ptext, *helpname, tmperrmsg[256],
+ *function;
+ Tcl_Obj *itemObj;
+ struct variable *vtmp;
+ FEATURE_S *ftmp;
+
+ if(!(helpname = Tcl_GetStringFromObj(objv[2], NULL))){
+ Tcl_SetResult(interp,
+ "Can't Tcl_GetStringFromObj for helpname",
+ TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ if(!(function = Tcl_GetStringFromObj(objv[3], NULL))){
+ Tcl_SetResult(interp,
+ "Can't Tcl_GetStringFromObj for function",
+ TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ if(strucmp(function, "plain") == 0){
+ if((text = help_name2section(helpname, strlen(helpname)))
+ == NO_HELP)
+ return(TCL_OK);
+ }
+ else if(strucmp(function, "variable") == 0){
+ for(vtmp = ps_global->vars;
+ vtmp->name && strucmp(vtmp->name, helpname);
+ vtmp++);
+ if(!vtmp->name) {
+ snprintf(tmperrmsg, sizeof(tmperrmsg), "Can't find variable named %s",
+ strlen(helpname) < 200 ? helpname : "");
+ Tcl_SetResult(interp, tmperrmsg, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ text = config_help(vtmp - ps_global->vars, 0);
+ if(text == NO_HELP)
+ return(TCL_OK);
+ }
+ else if(strucmp(function, "feature") == 0){
+ for(i = 0; (ftmp = feature_list(i)); i++){
+ if(!strucmp(helpname, ftmp->name)){
+ text = ftmp->help;
+ break;
+ }
+ }
+ if(!ftmp || text == NO_HELP){
+ return(TCL_OK);
+ }
+ }
+ else {
+ snprintf(tmperrmsg, sizeof(tmperrmsg), "Invalid function: %s",
+ strlen(helpname) < 200 ? function : "");
+ Tcl_SetResult(interp, tmperrmsg, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ /* assumption here is that HelpType is char ** */
+ help_text = text;
+ for(ptext = help_text; *ptext; ptext++){
+ itemObj = Tcl_NewStringObj(*ptext, -1);
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ itemObj);
+ }
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "varset")){
+ char *varname, **tmpstrlist, *line;
+ struct variable *vtmp;
+ Tcl_Obj **objVal;
+ int i, numlistvals = 0, strlistpos;
+
+ if((varname = Tcl_GetStringFromObj(objv[2], NULL))
+ && (Tcl_ListObjGetElements(interp, objv[3], &numlistvals,
+ &objVal) == TCL_OK)){
+ for(vtmp = ps_global->vars;
+ vtmp->name && strucmp(vtmp->name, varname);
+ vtmp++);
+ if(!vtmp->name){
+ return(TCL_ERROR);
+ }
+ else{
+ /* found the variable */
+ if(vtmp->is_list){
+ for(i = 0; vtmp->main_user_val.l && vtmp->main_user_val.l[i]; i++)
+ fs_give((void **)&vtmp->main_user_val.l[i]);
+ if(vtmp->main_user_val.l)
+ fs_give((void **)&vtmp->main_user_val.l);
+ if(numlistvals > 0){
+ tmpstrlist = (char **)fs_get((numlistvals + 1) * sizeof(char *));
+ for(i = 0, strlistpos = 0; i < numlistvals; i++){
+ if((line = Tcl_GetStringFromObj(objVal[i], 0)) != NULL){
+ removing_leading_and_trailing_white_space(line);
+ if(*line)
+ tmpstrlist[strlistpos++] = cpystr(line);
+ }
+ }
+ tmpstrlist[strlistpos] = NULL;
+ vtmp->main_user_val.l = (char **)fs_get((strlistpos+1) *
+ sizeof(char *));
+ for(i = 0; i <= strlistpos; i++)
+ vtmp->main_user_val.l[i] = tmpstrlist[i];
+ fs_give((void **)&tmpstrlist);
+ }
+ set_current_val(vtmp, FALSE, FALSE);
+ return(TCL_OK);
+ }
+ else{
+ if((line = Tcl_GetStringFromObj(objVal[0], NULL)) != NULL){
+ if(strucmp(vtmp->name, "reply-indent-string"))
+ removing_leading_and_trailing_white_space(line);
+ if(vtmp->main_user_val.p)
+ fs_give((void **)&vtmp->main_user_val.p);
+ if(*line)
+ vtmp->main_user_val.p = cpystr(line);
+ set_current_val(vtmp, FALSE, FALSE);
+ return(TCL_OK);
+ }
+ }
+ }
+ }
+ return(TCL_ERROR);
+ }
+ else if(!strcmp(s1, "mode")){
+ char *mode;
+ int value, rv = 0;
+
+ /*
+ * CMD: mode
+ *
+ * ARGS: <mode> <value>
+ *
+ * Returns: old value of binary mode we were told to set
+ *
+ */
+ if((mode = Tcl_GetStringFromObj(objv[2], NULL))
+ && Tcl_GetIntFromObj(interp, objv[3], &value) != TCL_ERROR){
+ if(!strcmp(mode, "full-header-mode")){
+ rv = ps_global->full_header;
+ ps_global->full_header = value;
+ }
+ }
+
+ Tcl_SetResult(interp, int2string(rv), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "set")){
+ Tcl_Obj *rObj;
+
+ if((rObj = Tcl_ObjSetVar2(interp, objv[2], NULL, objv[3], TCL_LEAVE_ERR_MSG)) != NULL){
+ Tcl_SetObjResult(interp, rObj);
+ return(TCL_OK);
+ }
+ else
+ return(TCL_ERROR);
+ }
+ }
+ else
+ err = "PEInfo: Too many arguments";
+ }
+ }
+
+ Tcl_SetResult(interp, err, TCL_STATIC);
+ return(TCL_ERROR);
+}
+
+
+/*
+ * PEConfigCmd - edit various alpine config variables
+ *
+ * The goal here is to remember what's changed, but not write to pinerc
+ * until the user's actually chosen to save.
+ */
+int
+PEConfigCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+ char *err = "Unknown PEConfig request";
+ char *s1;
+
+ dprint((2, "PEConfigCmd"));
+
+ if(objc == 1){
+ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
+ Tcl_SetResult(interp, err, TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ s1 = Tcl_GetStringFromObj(objv[1], NULL);
+
+ if(s1){
+ if(!strcmp(s1, "colorset")){
+ char *varname, *fghex, *bghex;
+ char tvname[256], asciicolor[256];
+ struct variable *vtmp;
+ Tcl_Obj **cObj;
+ int cObjc;
+ SPEC_COLOR_S *hcolors, *thc;
+
+ if(!(varname = Tcl_GetStringFromObj(objv[2], NULL))){
+ Tcl_SetResult(interp, "colorset: can't read variable name", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ if(!strcmp(varname, "viewer-hdr-colors")){
+ char *newhdr = NULL, *newpat = NULL, *utype;
+ int hindex, i;
+
+ if(objc < 5){
+ Tcl_SetResult(interp, "colorset: too few view-hdr args", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ if(ps_global->vars[V_VIEW_HDR_COLORS].is_changed_val)
+ hcolors = spec_colors_from_varlist(ps_global->vars[V_VIEW_HDR_COLORS].changed_val.l, 0);
+ else
+ hcolors = spec_colors_from_varlist(ps_global->VAR_VIEW_HDR_COLORS, 0);
+ if(!(utype = Tcl_GetStringFromObj(objv[3], NULL))){
+ Tcl_SetResult(interp, "colorset: can't read operation", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ if(!strcmp(utype, "delete")){
+ if(!hcolors){
+ Tcl_SetResult(interp, "colorset: no viewer-hdrs to delete", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ if(Tcl_GetIntFromObj(interp, objv[4], &hindex) == TCL_ERROR){
+ Tcl_SetResult(interp, "colorset: can't read index", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ if(hindex == 0){
+ thc = hcolors;
+ hcolors = hcolors->next;
+ thc->next = NULL;
+ free_spec_colors(&thc);
+ }
+ else{
+ /* zero based */
+ for(thc = hcolors, i = 1; thc && i < hindex; thc = thc->next, i++)
+ ;
+
+ if(thc && thc->next){
+ SPEC_COLOR_S *thc2 = thc->next;
+
+ thc->next = thc2->next;
+ thc2->next = NULL;
+ free_spec_colors(&thc2);
+ }
+ else{
+ Tcl_SetResult(interp, "colorset: invalid index", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ }
+ }
+ else if(!strcmp(utype, "add")){
+ if(objc != 6){
+ Tcl_SetResult(interp, "colorset: wrong number of view-hdr add args", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ if(Tcl_ListObjGetElements(interp, objv[4], &cObjc, &cObj) != TCL_OK)
+ return (TCL_ERROR);
+
+ if(cObjc != 2){
+ Tcl_SetResult(interp, "colorset: wrong number of hdrs for view-hdr add", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ newhdr = Tcl_GetStringFromObj(cObj[0], NULL);
+ newpat = Tcl_GetStringFromObj(cObj[1], NULL);
+ if(Tcl_ListObjGetElements(interp, objv[5], &cObjc, &cObj) != TCL_OK)
+ return (TCL_ERROR);
+
+ if(cObjc != 2){
+ Tcl_SetResult(interp, "colorset: wrong number of colors for view-hdr add", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ fghex = Tcl_GetStringFromObj(cObj[0], NULL);
+ bghex = Tcl_GetStringFromObj(cObj[1], NULL);
+ if(newhdr && newpat && fghex && bghex){
+ SPEC_COLOR_S **hcp;
+
+ for(hcp = &hcolors; *hcp != NULL; hcp = &(*hcp)->next)
+ ;
+
+ *hcp = (SPEC_COLOR_S *)fs_get(sizeof(SPEC_COLOR_S));
+ (*hcp)->inherit = 0;
+ (*hcp)->spec = cpystr(newhdr);
+ (*hcp)->fg = cpystr((ascii_colorstr(asciicolor, fghex) == 0) ? asciicolor : "black");
+ (*hcp)->bg = cpystr((ascii_colorstr(asciicolor, bghex) == 0) ? asciicolor : "white");
+
+ if(newpat && *newpat)
+ (*hcp)->val = string_to_pattern(newpat);
+ else
+ (*hcp)->val = NULL;
+
+ (*hcp)->next = NULL;
+ }
+ else{
+ Tcl_SetResult(interp, "colorset: invalid args for view-hdr add", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ }
+ else if(!strcmp(utype, "update")){
+ if(objc != 6){
+ Tcl_SetResult(interp, "colorset: wrong number of view-hdr update args", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ if(!(Tcl_ListObjGetElements(interp, objv[4], &cObjc, &cObj) == TCL_OK
+ && cObjc == 3
+ && Tcl_GetIntFromObj(interp, cObj[0], &hindex) == TCL_OK
+ && (newhdr = Tcl_GetStringFromObj(cObj[1], NULL))
+ && (newpat = Tcl_GetStringFromObj(cObj[2], NULL)))){
+ Tcl_SetResult(interp, "colorset: view-hdr update can't read index or header", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ if(!(Tcl_ListObjGetElements(interp, objv[5], &cObjc, &cObj) == TCL_OK
+ && cObjc == 2
+ && (fghex = Tcl_GetStringFromObj(cObj[0], NULL))
+ && (bghex = Tcl_GetStringFromObj(cObj[1], NULL)))){
+ Tcl_SetResult(interp, "colorset: view-hdr update can't read colors", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ for(thc = hcolors, i = 0; thc && i < hindex; thc = thc->next, i++)
+ ;
+
+ if(!thc){
+ Tcl_SetResult(interp, "colorset: view-hdr update invalid index", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ if(thc->spec)
+ fs_give((void **)&thc->spec);
+
+ thc->spec = cpystr(newhdr);
+ if(ascii_colorstr(asciicolor, fghex) == 0) {
+ if(thc->fg)
+ fs_give((void **)&thc->fg);
+
+ thc->fg = cpystr(asciicolor);
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid foreground color value %.100s", fghex);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ if(ascii_colorstr(asciicolor, bghex) == 0) {
+ if(thc->bg)
+ fs_give((void **)&thc->bg);
+
+ thc->bg = cpystr(asciicolor);
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background color value %.100s", bghex);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ if(thc->val)
+ fs_give((void **)&thc->val);
+
+ if(newpat && *newpat){
+ thc->val = string_to_pattern(newpat);
+ }
+ }
+ else{
+ Tcl_SetResult(interp, "colorset: unknown operation", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ vtmp = &ps_global->vars[V_VIEW_HDR_COLORS];
+ for(i = 0; vtmp->changed_val.l && vtmp->changed_val.l[i]; i++)
+ fs_give((void **)&vtmp->changed_val.l[i]);
+
+ if(vtmp->changed_val.l)
+ fs_give((void **)&vtmp->changed_val.l);
+
+ vtmp->changed_val.l = varlist_from_spec_colors(hcolors);
+ vtmp->is_changed_val = 1;
+ free_spec_colors(&hcolors);
+ return(TCL_OK);
+ }
+ else {
+ if(objc != 4){
+ Tcl_SetResult(interp, "colorset: Wrong number of args", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ if(!(Tcl_ListObjGetElements(interp, objv[3], &cObjc, &cObj) == TCL_OK
+ && cObjc == 2
+ && (fghex = Tcl_GetStringFromObj(cObj[0], NULL))
+ && (bghex = Tcl_GetStringFromObj(cObj[1], NULL)))){
+ Tcl_SetResult(interp, "colorset: Problem reading fore/back ground colors", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ snprintf(tvname, sizeof(tvname), "%.200s-foreground-color", varname);
+ for(vtmp = &ps_global->vars[V_NORM_FORE_COLOR];
+ vtmp->name && strucmp(vtmp->name, tvname);
+ vtmp++)
+ ;
+
+ if(!vtmp->name || vtmp->is_list){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background var %.100s", varname);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ if(ascii_colorstr(asciicolor, fghex) == 0) {
+ if(vtmp->changed_val.p)
+ fs_give((void **)&vtmp->changed_val.p);
+
+ vtmp->changed_val.p = cpystr(asciicolor);
+ vtmp->is_changed_val = 1;
+
+ /* We need to handle this in the actual config setting
+ * if(!strucmp(varname, "normal"))
+ * pico_set_fg_color(asciicolor);
+ */
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid color value %.100s", fghex);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ snprintf(tvname, sizeof(tvname), "%.200s%.50s", varname, "-background-color");
+ vtmp++;
+ if((vtmp->name && strucmp(vtmp->name, tvname)) || !vtmp->name)
+ for(vtmp = &ps_global->vars[V_NORM_FORE_COLOR];
+ vtmp->name && strucmp(vtmp->name, tvname);
+ vtmp++)
+ ;
+
+ if(!vtmp->name || vtmp->is_list){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background var %.100s", varname);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ if(ascii_colorstr(asciicolor, bghex) == 0) {
+ if(vtmp->changed_val.p)
+ fs_give((void **)&vtmp->changed_val.p);
+
+ vtmp->changed_val.p = cpystr(asciicolor);
+ vtmp->is_changed_val = 1;
+ /* again, we need to handle this when we actually set the variable
+ * if(!strucmp(varname, "normal"))
+ * pico_set_bg_color(asciicolor);
+ */
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background color value %.100s", bghex);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ Tcl_SetResult(interp, "1", TCL_STATIC);
+ return(TCL_OK);
+ }
+ }
+ else if(!strcmp(s1, "ruleset")){
+ return(peRuleSet(interp, &((Tcl_Obj **)objv)[2]));
+ }
+ else if(objc == 2){
+ if(!strcmp(s1, "featuresettings")){
+ struct variable *vtmp;
+ int i;
+ FEATURE_S *feature;
+
+ vtmp = &ps_global->vars[V_FEATURE_LIST];
+ for(i = 0; (feature = feature_list(i)); i++)
+ if(feature_list_section(feature)){
+ if(vtmp->is_changed_val ? F_CH_ON(feature->id)
+ : F_ON(feature->id, ps_global)){
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(feature->name, -1));
+ }
+ }
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "rawsig")){
+ char *err = NULL, *sig = NULL, *p, *q;
+ int i;
+ struct variable *vtmp;
+
+ vtmp = &ps_global->vars[V_LITERAL_SIG];
+ if(vtmp->is_changed_val ? vtmp->changed_val.p
+ : ps_global->VAR_LITERAL_SIG){
+ char *err = NULL;
+ char **apval;
+
+ if(ps_global->restricted){
+ err = "Alpine demo can't change config file";
+ }
+ else{
+ /* BUG: no "exceptions file" support */
+ apval = (vtmp->is_changed_val ? &vtmp->changed_val.p
+ : APVAL(&ps_global->vars[V_LITERAL_SIG], Main));
+ if(apval){
+ sig = (char *) fs_get((strlen(*apval ? *apval : "") + 1) * sizeof(char));
+ sig[0] = '\0';
+ cstring_to_string(*apval, sig);
+ }
+ else
+ err = "Problem accessing configuration";
+ }
+ }
+ else if((vtmp = &ps_global->vars[V_SIGNATURE_FILE])
+ && !IS_REMOTE(vtmp->is_changed_val ? vtmp->changed_val.p
+ : ps_global->VAR_SIGNATURE_FILE))
+ snprintf(err = tmp_20k_buf, SIZEOF_20KBUF, "Non-Remote signature file: %s",
+ vtmp->is_changed_val ? (vtmp->changed_val.p
+ ? vtmp->changed_val.p : "<null>")
+ : (ps_global->VAR_SIGNATURE_FILE
+ ? ps_global->VAR_SIGNATURE_FILE : "<null>"));
+ else if(!(peTSig || (sig = simple_read_remote_file(vtmp->is_changed_val
+ ? vtmp->changed_val.p
+ : ps_global->VAR_SIGNATURE_FILE, REMOTE_SIG_SUBTYPE))))
+ err = "Can't read remote pinerc";
+
+ if(err){
+ Tcl_SetResult(interp, err, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ if(peTSig){
+ for(i = 0; peTSig[i]; i++)
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(peTSig[i],-1));
+ }
+ else {
+ for(p = sig; (q = strindex(p, '\n')); p = q + 1)
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(p, q - p));
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(p, -1));
+ fs_give((void **) &sig);
+ }
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "filters")){
+ long rflags = ROLE_DO_FILTER | PAT_USE_CHANGED;
+ PAT_STATE pstate;
+ PAT_S *pat;
+
+ close_every_pattern();
+ if(any_patterns(rflags, &pstate)){
+ for(pat = first_pattern(&pstate);
+ pat;
+ pat = next_pattern(&pstate)){
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(pat->patgrp->nick, -1));
+ }
+ }
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "scores")){
+ long rflags = ROLE_DO_SCORES | PAT_USE_CHANGED;
+ PAT_STATE pstate;
+ PAT_S *pat;
+
+ close_every_pattern();
+ if(any_patterns(rflags, &pstate)){
+ for(pat = first_pattern(&pstate);
+ pat;
+ pat = next_pattern(&pstate)){
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(pat->patgrp->nick, -1));
+ }
+ }
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "indexcolors")){
+ long rflags = ROLE_DO_INCOLS | PAT_USE_CHANGED;
+ PAT_STATE pstate;
+ PAT_S *pat;
+
+ close_every_pattern();
+ if(any_patterns(rflags, &pstate)){
+ for(pat = first_pattern(&pstate);
+ pat;
+ pat = next_pattern(&pstate)){
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(pat->patgrp->nick, -1));
+ }
+ }
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "collections")){
+ struct variable *vtmp;
+ int i;
+ CONTEXT_S *new_ctxt;
+
+ vtmp = &ps_global->vars[V_FOLDER_SPEC];
+ for(i = 0; (vtmp->is_changed_val
+ ? vtmp->changed_val.l && vtmp->changed_val.l[i]
+ : vtmp->current_val.l && vtmp->current_val.l[i]);
+ i++){
+ new_ctxt = new_context(vtmp->is_changed_val
+ ? vtmp->changed_val.l[i]
+ : vtmp->current_val.l[i], NULL);
+ peAppListF(interp, Tcl_GetObjResult(interp), "%s%s",
+ new_ctxt->nickname
+ ? new_ctxt->nickname
+ : (new_ctxt->server
+ ? new_ctxt->server
+ : (new_ctxt->label
+ ? new_ctxt->label
+ : "Some Collection")),
+ new_ctxt->label ? new_ctxt->label : "");
+ free_context(&new_ctxt);
+ }
+ vtmp = &ps_global->vars[V_NEWS_SPEC];
+ for(i = 0; (vtmp->is_changed_val
+ ? vtmp->changed_val.l && vtmp->changed_val.l[i]
+ : vtmp->current_val.l && vtmp->current_val.l[i]);
+ i++){
+ new_ctxt = new_context(vtmp->is_changed_val
+ ? vtmp->changed_val.l[i]
+ : vtmp->current_val.l[i], NULL);
+ peAppListF(interp, Tcl_GetObjResult(interp), "%s%s",
+ new_ctxt->nickname
+ ? new_ctxt->nickname
+ : (new_ctxt->server
+ ? new_ctxt->server
+ : (new_ctxt->label
+ ? new_ctxt->label
+ : "Some Collection")),
+ new_ctxt->label ? new_ctxt->label : "");
+ free_context(&new_ctxt);
+ }
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "newconf")){
+ struct variable *vtmp;
+ int i;
+ FEATURE_S *feature;
+
+ for(vtmp = ps_global->vars; vtmp->name; vtmp++)
+ vtmp->is_changed_val = 0;
+
+ for(i = 0; (feature = feature_list(i)); i++)
+ F_CH_SET(feature->id, F_ON(feature->id, ps_global));
+
+ if(peTSig){
+ for(i = 0; peTSig[i]; i++)
+ fs_give((void **)&peTSig[i]);
+ fs_give((void **)&peTSig);
+ }
+
+ close_patterns(ROLE_DO_FILTER | ROLE_DO_INCOLS | ROLE_DO_SCORES | PAT_USE_CHANGED);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "saveconf")){
+ struct variable *vtmp;
+ int i, did_change = 0, def_sort_rev;
+ FEATURE_S *feature;
+
+ if(ps_global->vars[V_FEATURE_LIST].is_changed_val){
+ ps_global->vars[V_FEATURE_LIST].is_changed_val = 0;
+ for(i = 0; (feature = feature_list(i)); i++)
+ if(feature_list_section(feature)){
+ if(F_CH_ON(feature->id) != F_ON(feature->id, ps_global)){
+ did_change = 1;
+ toggle_feature(ps_global,
+ &ps_global->vars[V_FEATURE_LIST],
+ feature, TRUE, Main);
+ }
+ }
+ }
+
+ for(vtmp = ps_global->vars; vtmp->name; vtmp++){
+ if(vtmp->is_changed_val
+ && (vtmp - ps_global->vars != V_FEATURE_LIST)){
+ if(vtmp->is_list){
+ for(i = 0; vtmp->main_user_val.l
+ && vtmp->main_user_val.l[i]; i++)
+ fs_give((void **)&vtmp->main_user_val.l[i]);
+ if(vtmp->main_user_val.l)
+ fs_give((void **)&vtmp->main_user_val.l);
+ vtmp->main_user_val.l = vtmp->changed_val.l;
+ vtmp->changed_val.l = NULL;
+ }
+ else {
+ if(vtmp->main_user_val.p)
+ fs_give((void **)&vtmp->main_user_val.p);
+ vtmp->main_user_val.p = vtmp->changed_val.p;
+ vtmp->changed_val.p = NULL;
+ }
+ set_current_val(vtmp, FALSE, FALSE);
+ vtmp->is_changed_val = 0;
+ did_change = 1;
+ switch (vtmp - ps_global->vars) {
+ case V_USER_DOMAIN:
+ init_hostname(ps_global);
+ case V_FOLDER_SPEC:
+ case V_NEWS_SPEC:
+ free_contexts(&ps_global->context_list);
+ init_folders(ps_global);
+ break;
+ case V_NORM_FORE_COLOR:
+ pico_set_fg_color(vtmp->current_val.p);
+ break;
+ case V_NORM_BACK_COLOR:
+ pico_set_bg_color(vtmp->current_val.p);
+ break;
+ case V_ADDRESSBOOK:
+ case V_GLOB_ADDRBOOK:
+#ifdef ENABLE_LDAP
+ case V_LDAP_SERVERS:
+#endif
+ case V_ABOOK_FORMATS:
+ addrbook_reset();
+ case V_INDEX_FORMAT:
+ init_index_format(ps_global->VAR_INDEX_FORMAT,
+ &ps_global->index_disp_format);
+ clear_index_cache(sp_inbox_stream(), 0);
+ break;
+ case V_PAT_FILTS:
+ close_patterns(ROLE_DO_FILTER | PAT_USE_CURRENT);
+ role_process_filters();
+ break;
+ case V_PAT_INCOLS:
+ close_patterns(ROLE_DO_INCOLS | PAT_USE_CURRENT);
+ clear_index_cache(sp_inbox_stream(), 0);
+ role_process_filters();
+ break;
+ case V_PAT_SCORES:
+ close_patterns(ROLE_DO_SCORES | PAT_USE_CURRENT);
+ role_process_filters();
+ break;
+ case V_DEFAULT_FCC:
+ case V_DEFAULT_SAVE_FOLDER:
+ init_save_defaults();
+ break;
+ case V_SORT_KEY:
+ decode_sort(ps_global->VAR_SORT_KEY, &ps_global->def_sort, &def_sort_rev);
+ break;
+ case V_VIEW_HDR_COLORS :
+ set_custom_spec_colors(ps_global);
+ break;
+ case V_POST_CHAR_SET :
+ update_posting_charset(ps_global, 1);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ if(peTSig){
+ peWriteSig(interp, ps_global->VAR_SIGNATURE_FILE, NULL);
+ }
+ if(did_change){
+ if(write_pinerc(ps_global, Main, WRP_NOUSER) == 0)
+ q_status_message(SM_ORDER, 0, 3, "Configuration changes saved!");
+ }
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "columns")){
+ Tcl_SetResult(interp, int2string(ps_global->ttyo->screen_cols), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "indextokens")){
+ INDEX_PARSE_T *tok;
+ int i;
+
+ for(i = 0; (tok = itoken(i)) != NULL; i++)
+ if(tok->what_for & FOR_INDEX)
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(tok->name, -1));
+
+ return(TCL_OK);
+ }
+ }
+ else if(objc == 3){
+ if(!strcmp(s1, "varget")){
+ char *varname = Tcl_GetStringFromObj(objv[2], NULL);
+ struct variable *vtmp;
+ Tcl_Obj *resObj, *secObj;
+ char *input_type;
+ int is_default, i;
+ NAMEVAL_S *tmpnv;
+
+ if(varname == NULL) return(TCL_ERROR);
+
+ for(vtmp = ps_global->vars;
+ vtmp->name && strucmp(vtmp->name, varname);
+ vtmp++)
+ ;
+
+ if(!vtmp->name){
+ Tcl_SetResult(interp, err, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ resObj = Tcl_NewListObj(0, NULL);
+ if(vtmp->is_list){
+ if(vtmp->is_changed_val){
+ for(i = 0; vtmp->changed_val.l && vtmp->changed_val.l[i]; i++){
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(vtmp->changed_val.l[i], -1));
+ }
+ }
+ else {
+ for(i = 0; vtmp->current_val.l && vtmp->current_val.l[i]; i++){
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(vtmp->current_val.l[i], -1));
+ }
+ }
+ }
+ else {
+ if(vtmp->is_changed_val){
+ if(vtmp->changed_val.p)
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(vtmp->changed_val.p[0]
+ ? vtmp->changed_val.p
+ : "\"\"", -1));
+ }
+ else {
+ if(vtmp->current_val.p)
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(vtmp->current_val.p[0]
+ ? vtmp->current_val.p
+ : "\"\"", -1));
+ }
+ }
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ resObj);
+ secObj = Tcl_NewListObj(0, NULL);
+ if(vtmp->is_list)
+ input_type = cpystr("textarea");
+ else{
+ NAMEVAL_S *(*tmpf)(int);
+ switch(vtmp - ps_global->vars){
+ case V_SAVED_MSG_NAME_RULE:
+ tmpf = save_msg_rules;
+ break;
+ case V_FCC_RULE:
+ tmpf = fcc_rules;
+ break;
+ case V_SORT_KEY:
+ tmpf = sort_key_rules;
+ break;
+ case V_AB_SORT_RULE:
+ tmpf = ab_sort_rules;
+ break;
+ case V_FLD_SORT_RULE:
+ tmpf = fld_sort_rules;
+ break;
+ case V_GOTO_DEFAULT_RULE:
+ tmpf = goto_rules;
+ break;
+ case V_INCOMING_STARTUP:
+ tmpf = incoming_startup_rules;
+ break;
+ case V_PRUNING_RULE:
+ tmpf = pruning_rules;
+ break;
+ case V_WP_INDEXHEIGHT:
+ tmpf = wp_indexheight_rules;
+ break;
+ default:
+ tmpf = NULL;
+ break;
+ }
+ if(tmpf){
+ for(i = 0; (tmpnv = (tmpf)(i)); i++){
+ if(tmpnv->shortname)
+ peAppListF(interp, secObj, "%s%s", tmpnv->name, tmpnv->shortname);
+ else
+ Tcl_ListObjAppendElement(interp, secObj,
+ Tcl_NewStringObj(tmpnv->name, -1));
+ }
+ input_type = cpystr("listbox");
+ }
+ else
+ input_type = cpystr("text");
+ }
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(input_type, -1));
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ secObj);
+ if(vtmp->is_list)
+ is_default = !vtmp->is_changed_val && !vtmp->main_user_val.l;
+ else
+ is_default = !vtmp->is_changed_val && !vtmp->main_user_val.p;
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewIntObj(is_default));
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewIntObj(vtmp->is_fixed));
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "filtextended")){
+ int fl, i;
+ long rflags = ROLE_DO_FILTER | PAT_USE_CHANGED;
+ PAT_STATE pstate;
+ PAT_S *pat;
+ Tcl_Obj *resObj = NULL, *tObj = NULL;
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &fl) == TCL_ERROR)
+ return(TCL_ERROR);
+
+ close_every_pattern();
+ if(any_patterns(rflags, &pstate)){
+ for(pat = first_pattern(&pstate), i = 0;
+ pat && i != fl;
+ pat = next_pattern(&pstate), i++);
+
+ if(!pat)
+ return(TCL_ERROR);
+
+ /* append the pattern ID */
+ tObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("id", -1));
+ pePatAppendID(interp, tObj, pat);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
+
+ /* append the pattern */
+ tObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("pattern", -1));
+ pePatAppendPattern(interp, tObj, pat);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
+
+ /* now append the filter action */
+ resObj = Tcl_NewListObj(0, NULL);
+ peAppListF(interp, resObj, "%s%i", "kill", pat->action->folder ? 0 : 1);
+ peAppListF(interp, resObj, "%s%p", "folder", pat->action->folder);
+ peAppListF(interp, resObj, "%s%i", "move_only_if_not_deleted",
+ pat->action->move_only_if_not_deleted);
+ tObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("filtaction", -1));
+ Tcl_ListObjAppendElement(interp, tObj, resObj);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
+ }
+ else return(TCL_ERROR);
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "indexcolorextended")){
+ int fl, i;
+ long rflags = ROLE_DO_INCOLS | PAT_USE_CHANGED;
+ PAT_STATE pstate;
+ PAT_S *pat;
+ Tcl_Obj *resObj = NULL, *tObj = NULL;
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &fl) == TCL_ERROR)
+ return(TCL_ERROR);
+
+ close_every_pattern();
+ if(any_patterns(rflags, &pstate)){
+ for(pat = first_pattern(&pstate), i = 0;
+ pat && i != fl;
+ pat = next_pattern(&pstate), i++);
+
+ if(!pat)
+ return(TCL_ERROR);
+
+ /* append the pattern ID */
+ tObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("id", -1));
+ pePatAppendID(interp, tObj, pat);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
+
+ /* append the pattern */
+ tObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("pattern", -1));
+ pePatAppendPattern(interp, tObj, pat);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
+
+ /* now append the pattern colors */
+ resObj = Tcl_NewListObj(0, NULL);
+ tObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("indexcolor", -1));
+ if(pat->action->is_a_incol){
+ char *color;
+ Tcl_Obj *colObj = Tcl_NewListObj(0, NULL);
+
+ if(!(pat->action->incol
+ && pat->action->incol->fg
+ && pat->action->incol->fg[0]
+ && (color = color_to_asciirgb(pat->action->incol->fg))
+ && (color = peColorStr(color,tmp_20k_buf))))
+ color = "";
+
+ Tcl_ListObjAppendElement(interp, colObj, Tcl_NewStringObj(color, -1));
+
+ if(!(pat->action->incol
+ && pat->action->incol->bg
+ && pat->action->incol->bg[0]
+ && (color = color_to_asciirgb(pat->action->incol->bg))
+ && (color = peColorStr(color,tmp_20k_buf))))
+ color = "";
+
+ Tcl_ListObjAppendElement(interp, colObj, Tcl_NewStringObj(color, -1));
+ Tcl_ListObjAppendElement(interp, tObj, colObj);
+ }
+ Tcl_ListObjAppendElement(interp, resObj, tObj);
+
+ tObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("indexcolors", -1));
+ Tcl_ListObjAppendElement(interp, tObj, resObj);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
+ }
+ else return(TCL_ERROR);
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "scoreextended")){
+ int fl, i;
+ long rflags = ROLE_DO_SCORES | PAT_USE_CHANGED;
+ char *hdr = NULL;
+ PAT_STATE pstate;
+ PAT_S *pat;
+ Tcl_Obj *resObj = NULL, *tObj = NULL;
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &fl) == TCL_ERROR)
+ return(TCL_ERROR);
+
+ close_every_pattern();
+ if(any_patterns(rflags, &pstate)){
+ for(pat = first_pattern(&pstate), i = 0;
+ pat && i != fl;
+ pat = next_pattern(&pstate), i++);
+
+ if(!pat)
+ return(TCL_ERROR);
+
+ /* append the pattern ID */
+ tObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("id", -1));
+ pePatAppendID(interp, tObj, pat);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
+
+ /* append the pattern */
+ tObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("pattern", -1));
+ pePatAppendPattern(interp, tObj, pat);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
+
+ /* now append the filter action */
+ resObj = Tcl_NewListObj(0, NULL);
+ peAppListF(interp, resObj, "%s%l", "scoreval", pat->action->scoreval);
+ if(pat->action->scorevalhdrtok)
+ hdr = hdrtok_to_stringform(pat->action->scorevalhdrtok);
+
+ peAppListF(interp, resObj, "%s%s", "scorehdr", hdr ? hdr : "");
+
+ if(hdr)
+ fs_give((void **) &hdr);
+
+ tObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("scores", -1));
+ Tcl_ListObjAppendElement(interp, tObj, resObj);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
+ }
+ else return(TCL_ERROR);
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "clextended")){
+ int cl, i, j = 0, in_folder_spec = 0;
+ struct variable *vtmp;
+ char tpath[MAILTMPLEN], *p;
+ CONTEXT_S *ctxt;
+
+ vtmp = &ps_global->vars[V_FOLDER_SPEC];
+ if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR)
+ return(TCL_ERROR);
+ for(i = 0; i < cl && (vtmp->is_changed_val
+ ? (vtmp->changed_val.l
+ && vtmp->changed_val.l[i])
+ : (vtmp->current_val.l
+ && vtmp->current_val.l[i])); i++);
+ if(i == cl && (vtmp->is_changed_val
+ ? vtmp->changed_val.l && vtmp->changed_val.l[i]
+ : vtmp->current_val.l && vtmp->current_val.l[i]))
+ in_folder_spec = 1;
+ else {
+ vtmp = &ps_global->vars[V_NEWS_SPEC];
+ for(j = 0; i + j < cl && (vtmp->is_changed_val
+ ? (vtmp->changed_val.l
+ && vtmp->changed_val.l[j])
+ : (vtmp->current_val.l
+ && vtmp->current_val.l[j])); j++);
+ }
+ if(in_folder_spec || (i + j == cl && (vtmp->is_changed_val
+ ? vtmp->changed_val.l && vtmp->changed_val.l[j]
+ : vtmp->current_val.l && vtmp->current_val.l[j]))){
+ ctxt = new_context(vtmp->is_changed_val ? vtmp->changed_val.l[in_folder_spec ? i : j]
+ : vtmp->current_val.l[in_folder_spec ? i : j], NULL);
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(ctxt->nickname ? ctxt->nickname : "", -1));
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(ctxt->label ? ctxt->label : "", -1));
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(ctxt->server ? ctxt->server : "", -1));
+ tpath[0] = '\0';
+ if(ctxt->context){
+ strncpy(tpath, (ctxt->context[0] == '{'
+ && (p = strchr(ctxt->context, '}')))
+ ? ++p
+ : ctxt->context, sizeof(tpath));
+ tpath[sizeof(tpath)-1] = '\0';
+ if((p = strstr(tpath, "%s")) != NULL)
+ *p = '\0';
+ }
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(tpath, -1));
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(ctxt->dir && ctxt->dir->view.user
+ ? ctxt->dir->view.user : "", -1));
+ free_context(&ctxt);
+
+ return(TCL_OK);
+ }
+ else
+ return(TCL_ERROR);
+ }
+ else if(!strcmp(s1, "rawsig")){
+ struct variable *vtmp;
+ char *cstring_version, *sig, *line;
+ int i, nSig;
+ Tcl_Obj **objSig;
+
+ vtmp = &ps_global->vars[V_LITERAL_SIG];
+ if(vtmp->is_changed_val ? vtmp->changed_val.p
+ : ps_global->VAR_LITERAL_SIG){
+
+ tmp_20k_buf[0] = '\0';
+ Tcl_ListObjGetElements(interp, objv[2], &nSig, &objSig);
+ for(i = 0; i < nSig && i < SIG_MAX_LINES; i++)
+ if((line = Tcl_GetStringFromObj(objSig[i], NULL)) != NULL)
+ snprintf(tmp_20k_buf + strlen(tmp_20k_buf), SIZEOF_20KBUF - strlen(tmp_20k_buf), "%.*s\n", SIG_MAX_COLS, line);
+
+ sig = cpystr(tmp_20k_buf);
+
+ if((cstring_version = string_to_cstring(sig)) != NULL){
+ if(vtmp->changed_val.p)
+ fs_give((void **)&vtmp->changed_val.p);
+ vtmp->is_changed_val = 1;
+ vtmp->changed_val.p = cstring_version;
+ }
+
+ fs_give((void **) &sig);
+ return(TCL_OK);
+ }
+ else {
+ if(peTSig){
+ for(i = 0; peTSig[i]; i++)
+ fs_give((void **)&peTSig[i]);
+ fs_give((void **)&peTSig);
+ }
+ Tcl_ListObjGetElements(interp, objv[2], &nSig, &objSig);
+ peTSig = (char **)fs_get(sizeof(char)*(nSig + 1));
+ for(i = 0; i < nSig; i++){
+ line = Tcl_GetStringFromObj(objSig[i], NULL);
+ peTSig[i] = cpystr(line ? line : "");
+ }
+ peTSig[i] = NULL;
+ return(TCL_OK);
+ }
+ }
+ else if(!strcmp(s1, "colorget")){
+ char *varname;
+ char tvname[256], hexcolor[256];
+ struct variable *vtmp;
+ if(!(varname = Tcl_GetStringFromObj(objv[2], NULL))){
+ return(TCL_ERROR);
+ }
+ if(strcmp("viewer-hdr-colors", varname) == 0){
+ SPEC_COLOR_S *hcolors, *thc;
+ Tcl_Obj *resObj;
+ char hexcolor[256], *tstr = NULL;
+
+ if(ps_global->vars[V_VIEW_HDR_COLORS].is_changed_val)
+ hcolors = spec_colors_from_varlist(ps_global->vars[V_VIEW_HDR_COLORS].changed_val.l, 0);
+ else
+ hcolors = spec_colors_from_varlist(ps_global->VAR_VIEW_HDR_COLORS, 0);
+ for(thc = hcolors; thc; thc = thc->next){
+ resObj = Tcl_NewListObj(0,NULL);
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(thc->spec, -1));
+ hex_colorstr(hexcolor, thc->fg);
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(hexcolor, -1));
+ hex_colorstr(hexcolor, thc->bg);
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(hexcolor, -1));
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(thc->val
+ ? tstr = pattern_to_string(thc->val)
+ : "", -1));
+ if(tstr) fs_give((void **)&tstr);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ resObj);
+ }
+ fs_give((void **)&hcolors);
+ return(TCL_OK);
+ }
+ else {
+ char *colorp;
+
+ snprintf(tvname, sizeof(tvname), "%.200s%.50s", varname, "-foreground-color");
+
+ for(vtmp = &ps_global->vars[V_NORM_FORE_COLOR];
+ vtmp->name && strucmp(vtmp->name, tvname);
+ vtmp++)
+ ;
+
+ if(!vtmp->name) return(TCL_ERROR);
+ if(vtmp->is_list) return(TCL_ERROR);
+
+ colorp = (vtmp->is_changed_val && vtmp->changed_val.p)
+ ? vtmp->changed_val.p
+ : (vtmp->current_val.p) ? vtmp->current_val.p
+ : vtmp->global_val.p;
+
+ if(colorp){
+ hex_colorstr(hexcolor, colorp);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(hexcolor, -1));
+ }
+ else
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj("", -1));
+
+ snprintf(tvname, sizeof(tvname), "%.200s%.50s", varname, "-background-color");
+ vtmp++;
+ if((vtmp->name && strucmp(vtmp->name, tvname)) || !vtmp->name)
+ for(vtmp = &ps_global->vars[V_NORM_FORE_COLOR];
+ vtmp->name && strucmp(vtmp->name, tvname);
+ vtmp++)
+ ;
+
+ if(!vtmp->name) return(TCL_ERROR);
+ if(vtmp->is_list) return(TCL_ERROR);
+
+ colorp = (vtmp->is_changed_val && vtmp->changed_val.p)
+ ? vtmp->changed_val.p
+ : (vtmp->current_val.p) ? vtmp->current_val.p
+ : vtmp->global_val.p;
+
+ if(colorp){
+ hex_colorstr(hexcolor, colorp);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(hexcolor, -1));
+ }
+ else
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj("", -1));
+ }
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "cldel")){
+ int cl, i, j, n;
+ struct variable *vtmp;
+ char **newl;
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR)
+ return(TCL_ERROR);
+ vtmp = &ps_global->vars[V_FOLDER_SPEC];
+ for(i = 0; i < cl && (vtmp->is_changed_val
+ ? (vtmp->changed_val.l && vtmp->changed_val.l[i])
+ : (vtmp->current_val.l && vtmp->current_val.l[i])); i++);
+ if(!(i == cl && (vtmp->is_changed_val
+ ? (vtmp->changed_val.l && vtmp->changed_val.l[i])
+ : (vtmp->current_val.l && vtmp->current_val.l[i])))){
+ vtmp = &ps_global->vars[V_NEWS_SPEC];
+ for(j = 0; i + j < cl && (vtmp->is_changed_val
+ ? (vtmp->changed_val.l && vtmp->changed_val.l[j])
+ : (vtmp->current_val.l && vtmp->current_val.l[j]));
+ j++);
+ if(!(vtmp->is_changed_val
+ ? (vtmp->changed_val.l && vtmp->changed_val.l[j])
+ : (vtmp->current_val.l && vtmp->current_val.l[j])))
+ return(TCL_ERROR);
+ i = j;
+ }
+ for(n = 0; vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[n])
+ : (vtmp->current_val.l && vtmp->current_val.l[n]); n++);
+ newl = (char **)fs_get(n*(sizeof(char *)));
+ for(n = 0; vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[n])
+ : (vtmp->current_val.l && vtmp->current_val.l[n]); n++){
+ if(n < i)
+ newl[n] = cpystr(vtmp->is_changed_val ? vtmp->changed_val.l[n]
+ : vtmp->current_val.l[n]);
+ else if(n > i)
+ newl[n-1] = cpystr(vtmp->is_changed_val ? vtmp->changed_val.l[n]
+ : vtmp->current_val.l[n]);
+ }
+ newl[n-1] = NULL;
+ vtmp->is_changed_val = 1;
+ for(n = 0; vtmp->changed_val.l && vtmp->changed_val.l[n]; n++)
+ fs_give((void **) &vtmp->changed_val.l[n]);
+ if(vtmp->changed_val.l) fs_give((void **)&vtmp->changed_val.l);
+ vtmp->changed_val.l = newl;
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "columns")){
+ int n;
+ char *p;
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &n) != TCL_ERROR
+ && n >= MIN_SCREEN_COLS
+ && n < (MAX_SCREEN_COLS - 1)
+ && ps_global->ttyo->screen_cols != n){
+ clear_index_cache(sp_inbox_stream(), 0);
+ ps_global->ttyo->screen_cols = n;
+ set_variable(V_WP_COLUMNS, p = int2string(n), 0, 0, Main);
+ Tcl_SetResult(interp, p, TCL_VOLATILE);
+ }
+ else
+ Tcl_SetResult(interp, int2string(ps_global->ttyo->screen_cols), TCL_VOLATILE);
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "reset")){
+ char *p;
+
+ if((p = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
+ if(!strcmp(p,"pinerc")){
+ struct variable *var;
+ PINERC_S *prc;
+
+ /* new pinerc structure, copy location pointers */
+ prc = new_pinerc_s(ps_global->prc->name);
+ prc->type = ps_global->prc->type;
+ prc->rd = ps_global->prc->rd;
+ prc->outstanding_pinerc_changes = 1;
+
+ /* tie off original pinerc struct and free it */
+ ps_global->prc->rd = NULL;
+ ps_global->prc->outstanding_pinerc_changes = 0;
+ free_pinerc_s(&ps_global->prc);
+
+ /* set global->prc to new struct with no pinerc_lines
+ * and fool write_pinerc into not writing changed vars
+ */
+ ps_global->prc = prc;
+
+ /*
+ * write at least one var into nearly empty pinerc
+ * and clear user's var settings. clear global cause
+ * they'll get reset in peInitVars
+ */
+ for(var = ps_global->vars; var->name != NULL; var++){
+ var->been_written = ((var - ps_global->vars) != V_LAST_VERS_USED);
+ if(var->is_list){
+ free_list_array(&var->main_user_val.l);
+ free_list_array(&var->global_val.l);
+ }
+ else{
+ fs_give((void **)&var->main_user_val.p);
+ fs_give((void **)&var->global_val.p);
+ }
+ }
+
+ write_pinerc(ps_global, Main, WRP_NOUSER | WRP_PRESERV_WRITTEN);
+
+ peInitVars(ps_global);
+ return(TCL_OK);
+ }
+ }
+ }
+ }
+ else if(objc == 4){
+ if(!strcmp(s1, "varset")){
+ char *varname = Tcl_GetStringFromObj(objv[2], NULL);
+ struct variable *vtmp;
+ char **tstrlist = NULL, *line, *tline;
+ Tcl_Obj **objVal;
+ int i, strlistpos, numlistvals;
+
+ if(varname == NULL) return(TCL_ERROR);
+ for(vtmp = ps_global->vars;
+ vtmp->name && strucmp(vtmp->name, varname);
+ vtmp++)
+ ;
+ if(!vtmp->name){
+ Tcl_SetResult(interp, err, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ if(Tcl_ListObjGetElements(interp, objv[3], &numlistvals,
+ &objVal) != TCL_OK)
+ return(TCL_ERROR);
+ vtmp->is_changed_val = 1;
+ if(vtmp->is_list){
+ if(vtmp->changed_val.l){
+ for(i = 0; vtmp->changed_val.l[i]; i++)
+ fs_give((void **)&vtmp->changed_val.l[i]);
+ fs_give((void **)&vtmp->changed_val.l);
+ }
+ if(numlistvals)
+ tstrlist = (char **)fs_get((numlistvals + 1) * sizeof(char *));
+ for(i = 0, strlistpos = 0; i < numlistvals; i++){
+ if((line = Tcl_GetStringFromObj(objVal[i], 0)) != NULL){
+ tline = cpystr(line);
+ removing_leading_and_trailing_white_space(tline);
+ if(*tline)
+ tstrlist[strlistpos++] = cpystr(tline);
+ fs_give((void **) &tline);
+ }
+ }
+ if(tstrlist)
+ tstrlist[strlistpos] = NULL;
+ vtmp->changed_val.l = tstrlist;
+ }
+ else {
+ if(vtmp->changed_val.p)
+ fs_give((void **)&vtmp->changed_val.p);
+ if(numlistvals){
+ if((line = Tcl_GetStringFromObj(objVal[0], 0)) != NULL){
+ tline = cpystr(line);
+ if(strucmp(vtmp->name, "reply-indent-string"))
+ removing_leading_and_trailing_white_space(tline);
+ if(!strcmp(tline, "\"\"")){
+ tline[0] = '\0';
+ }
+ else if(tline[0] == '\0'){
+ fs_give((void **)&tline);
+ }
+ if(tline){
+ vtmp->changed_val.p = cpystr(tline);
+ fs_give((void **)&tline);
+ }
+ }
+ else
+ vtmp->changed_val.p = cpystr("");
+ }
+ }
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "feature")){
+ char *featurename;
+ int i, set, wasset = 0;
+ FEATURE_S *feature;
+
+ /*
+ * CMD: feature
+ *
+ * ARGS: featurename -
+ * value - new value to assign flag
+ *
+ * Returns: 1 if named feature set, 0 otherwise
+ *
+ */
+ if((featurename = Tcl_GetStringFromObj(objv[2], NULL))
+ && Tcl_GetIntFromObj(interp, objv[3], &set) != TCL_ERROR)
+ for(i = 0; (feature = feature_list(i)); i++)
+ if(!strucmp(featurename, feature->name)){
+ ps_global->vars[V_FEATURE_LIST].is_changed_val = 1;
+ wasset = F_CH_ON(feature->id);
+ F_CH_SET(feature->id, set);
+ break;
+ }
+
+ Tcl_SetResult(interp, int2string(wasset), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "clshuff")){
+ char *dir, *tstr, **newl;
+ int cl, up = 0, fvarn, nvarn, icnt, i;
+ struct variable *fvar, *nvar, *vtmp;
+
+ if(!(dir = Tcl_GetStringFromObj(objv[2], NULL)))
+ return TCL_ERROR;
+ if(Tcl_GetIntFromObj(interp, objv[3], &cl) == TCL_ERROR)
+ return(TCL_ERROR);
+ if(!strcmp(dir, "up"))
+ up = 1;
+ else if(!strcmp(dir, "down"))
+ up = 0;
+ else
+ return(TCL_ERROR);
+ fvar = &ps_global->vars[V_FOLDER_SPEC];
+ nvar = &ps_global->vars[V_NEWS_SPEC];
+ for(fvarn = 0; fvar->is_changed_val ? (fvar->changed_val.l && fvar->changed_val.l[fvarn])
+ : (fvar->current_val.l && fvar->current_val.l[fvarn]); fvarn++);
+ for(nvarn = 0; nvar->is_changed_val ? (nvar->changed_val.l && nvar->changed_val.l[nvarn])
+ : (nvar->current_val.l && nvar->current_val.l[nvarn]); nvarn++);
+ if(cl < fvarn){
+ vtmp = fvar;
+ icnt = cl;
+ }
+ else if(cl >= fvarn && cl < nvarn + fvarn){
+ vtmp = nvar;
+ icnt = cl - fvarn;
+ }
+ else
+ return(TCL_ERROR);
+ if(vtmp == nvar && icnt == 0 && up){
+ newl = (char **)fs_get((fvarn + 2)*sizeof(char *));
+ for(i = 0; fvar->is_changed_val ? (fvar->changed_val.l && fvar->changed_val.l[i])
+ : (fvar->current_val.l && fvar->current_val.l[i]); i++)
+ newl[i] = cpystr(fvar->is_changed_val ? fvar->changed_val.l[i]
+ : fvar->current_val.l[i]);
+ newl[i++] = cpystr(nvar->is_changed_val ? nvar->changed_val.l[0]
+ : nvar->current_val.l[0]);
+ newl[i] = NULL;
+ fvar->is_changed_val = 1;
+ for(i = 0; fvar->changed_val.l && fvar->changed_val.l[i]; i++)
+ fs_give((void **)&fvar->changed_val.l[i]);
+ if(fvar->changed_val.l) fs_give((void **)&fvar->changed_val.l);
+ fvar->changed_val.l = newl;
+ newl = (char **)fs_get(nvarn*sizeof(char *));
+ for(i = 1; nvar->is_changed_val ? (nvar->changed_val.l && nvar->changed_val.l[i])
+ : (nvar->current_val.l && nvar->current_val.l[i]); i++)
+ newl[i-1] = cpystr(nvar->is_changed_val ? nvar->changed_val.l[i]
+ : nvar->current_val.l[i]);
+ newl[i-1] = NULL;
+ nvar->is_changed_val = 1;
+ for(i = 0; nvar->changed_val.l && nvar->changed_val.l[i]; i++)
+ fs_give((void **)&nvar->changed_val.l[i]);
+ if(nvar->changed_val.l) fs_give((void **)&nvar->changed_val.l);
+ nvar->changed_val.l = newl;
+ vtmp = fvar;
+ icnt = fvarn;
+ }
+ else if(vtmp == fvar && icnt == fvarn - 1 && !up){
+ newl = (char **)fs_get(fvarn*sizeof(char *));
+ for(i = 0; fvar->is_changed_val ? (fvar->changed_val.l && fvar->changed_val.l[i+1])
+ : (fvar->current_val.l && fvar->current_val.l[i+1]); i++)
+ newl[i] = cpystr(fvar->is_changed_val ? fvar->changed_val.l[i]
+ : fvar->current_val.l[i]);
+ newl[i] = NULL;
+ tstr = cpystr(fvar->is_changed_val ? fvar->changed_val.l[i]
+ : fvar->current_val.l[i]);
+ fvar->is_changed_val = 1;
+ for(i = 0; fvar->changed_val.l && fvar->changed_val.l[i]; i++)
+ fs_give((void **)&fvar->changed_val.l[i]);
+ if(fvar->changed_val.l) fs_give((void **)&fvar->changed_val.l);
+ fvar->changed_val.l = newl;
+ newl = (char **)fs_get((nvarn+2)*sizeof(char *));
+ newl[0] = tstr;
+ for(i = 0; nvar->is_changed_val ? (nvar->changed_val.l && nvar->changed_val.l[i])
+ : (nvar->current_val.l && nvar->current_val.l[i]); i++)
+ newl[i+1] = cpystr(nvar->is_changed_val ? nvar->changed_val.l[i]
+ : nvar->current_val.l[i]);
+ newl[i+1] = NULL;
+ nvar->is_changed_val = 1;
+ for(i = 0; nvar->changed_val.l && nvar->changed_val.l[i]; i++)
+ fs_give((void **)&nvar->changed_val.l[i]);
+ if(nvar->changed_val.l) fs_give((void **)&nvar->changed_val.l);
+ nvar->changed_val.l = newl;
+ vtmp = nvar;
+ icnt = 0;
+ }
+ else {
+ newl = (char **)fs_get(((vtmp == fvar ? fvarn : nvarn) + 1)*sizeof(char *));
+ for(i = 0; vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[i])
+ : (vtmp->current_val.l && vtmp->current_val.l[i]); i++)
+ newl[i] = cpystr(vtmp->is_changed_val ? vtmp->changed_val.l[i]
+ : vtmp->current_val.l[i]);
+ newl[i] = NULL;
+ vtmp->is_changed_val = 1;
+ for(i = 0; vtmp->changed_val.l && vtmp->changed_val.l[i]; i++)
+ fs_give((void **)&vtmp->changed_val.l[i]);
+ if(vtmp->changed_val.l) fs_give((void **)&vtmp->changed_val.l);
+ vtmp->changed_val.l = newl;
+ }
+ if(up){
+ tstr = vtmp->changed_val.l[icnt-1];
+ vtmp->changed_val.l[icnt-1] = vtmp->changed_val.l[icnt];
+ vtmp->changed_val.l[icnt] = tstr;
+ }
+ else {
+ tstr = vtmp->changed_val.l[icnt+1];
+ vtmp->changed_val.l[icnt+1] = vtmp->changed_val.l[icnt];
+ vtmp->changed_val.l[icnt] = tstr;
+ }
+ return(TCL_OK);
+ }
+ }
+ else if(objc == 7){
+ if(!strcmp(s1, "cledit") || !strcmp(s1, "cladd")){
+ int add = 0, cl, quotes_needed = 0, i, j, newn;
+ char *nick, *server, *path, *view, context_buf[MAILTMPLEN*4];
+ char **newl;
+ struct variable *vtmp;
+
+ if(!strcmp(s1, "cladd")) add = 1;
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR)
+ return(TCL_ERROR);
+ if(!(nick = Tcl_GetStringFromObj(objv[3], NULL)))
+ return TCL_ERROR;
+ if(!(server = Tcl_GetStringFromObj(objv[4], NULL)))
+ return TCL_ERROR;
+ if(!(path = Tcl_GetStringFromObj(objv[5], NULL)))
+ return TCL_ERROR;
+ if(!(view = Tcl_GetStringFromObj(objv[6], NULL)))
+ return TCL_ERROR;
+ removing_leading_and_trailing_white_space(nick);
+ removing_leading_and_trailing_white_space(server);
+ removing_leading_and_trailing_white_space(path);
+ removing_leading_and_trailing_white_space(view);
+ if(strchr(nick, ' '))
+ quotes_needed = 1;
+ if(strlen(nick)+strlen(server)+strlen(path)+strlen(view) >
+ MAILTMPLEN * 4 - 20) { /* for good measure */
+ Tcl_SetResult(interp, "info too long", TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ if(3 + strlen(nick) + strlen(server) + strlen(path) +
+ strlen(view) > MAILTMPLEN + 4){
+ Tcl_SetResult(interp, "collection fields too long", TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ snprintf(context_buf, sizeof(context_buf), "%s%s%s%s%s%s[%s]", quotes_needed ?
+ "\"" : "", nick, quotes_needed ? "\"" : "",
+ strlen(nick) ? " " : "",
+ server, path, view);
+ if(add) {
+ vtmp = &ps_global->vars[V_NEWS_SPEC];
+ if(!(vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[0])
+ : (vtmp->current_val.l && vtmp->current_val.l[0])))
+ vtmp = &ps_global->vars[V_FOLDER_SPEC];
+ for(i = 0; vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[i])
+ : (vtmp->current_val.l && vtmp->current_val.l[i]); i++);
+ newn = i + 1;
+ newl = (char **)fs_get((newn + 1)*sizeof(char *));
+ for(i = 0; vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[i])
+ : (vtmp->current_val.l && vtmp->current_val.l[i]); i++)
+ newl[i] = cpystr(vtmp->is_changed_val ? vtmp->changed_val.l[i]
+ : vtmp->current_val.l[i]);
+ newl[i++] = cpystr(context_buf);
+ newl[i] = NULL;
+ }
+ else {
+ vtmp = &ps_global->vars[V_FOLDER_SPEC];
+ for(i = 0; i < cl && (vtmp->is_changed_val
+ ? (vtmp->changed_val.l && vtmp->changed_val.l[i])
+ : (vtmp->current_val.l && vtmp->current_val.l[i])); i++);
+ if(!(i == cl && (vtmp->is_changed_val
+ ? (vtmp->changed_val.l && vtmp->changed_val.l[i])
+ : (vtmp->current_val.l && vtmp->current_val.l[i])))){
+ vtmp = &ps_global->vars[V_NEWS_SPEC];
+ for(j = 0; i + j < cl && (vtmp->is_changed_val
+ ? (vtmp->changed_val.l && vtmp->changed_val.l[j])
+ : (vtmp->current_val.l && vtmp->current_val.l[j]));
+ j++);
+ if(!(vtmp->is_changed_val
+ ? (vtmp->changed_val.l && vtmp->changed_val.l[j])
+ : (vtmp->current_val.l && vtmp->current_val.l[j])))
+ return(TCL_ERROR);
+ i = j;
+ }
+ for(j = 0; vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[j])
+ : (vtmp->current_val.l && vtmp->current_val.l[j]); j++);
+ newl = (char **)fs_get(j * sizeof(char *));
+ for(j = 0; vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[j])
+ : (vtmp->current_val.l && vtmp->current_val.l[j]); j++){
+ if(j == i)
+ newl[j] = cpystr(context_buf);
+ else
+ newl[j] = cpystr(vtmp->is_changed_val ? vtmp->changed_val.l[j]
+ : vtmp->current_val.l[j]);
+ }
+ newl[j] = NULL;
+ }
+ vtmp->is_changed_val = 1;
+ for(j = 0; vtmp->changed_val.l && vtmp->changed_val.l[j]; j++)
+ fs_give((void **)&vtmp->changed_val.l[j]);
+ if(vtmp->changed_val.l) fs_give((void **)&vtmp->changed_val.l);
+ vtmp->changed_val.l = newl;
+ return TCL_OK;
+ }
+ }
+ else
+ err = "PEInfo: Too many arguments";
+ }
+ Tcl_SetResult(interp, err, TCL_STATIC);
+ return(TCL_ERROR);
+}
+
+
+int
+peWriteSig(Tcl_Interp *interp, char *file, Tcl_Obj **objv)
+{
+ int try_cache, e, i, n, nSig;
+ char datebuf[200], *sig, *line;
+ FILE *fp;
+ REMDATA_S *rd;
+ Tcl_Obj **objSig;
+
+ if(!(file && IS_REMOTE(file))){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Non-Remote signature file: %s",
+ file ? file : "<null>");
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ /*
+ * We could parse the name here to find what type it is. So far we
+ * only have type RemImap.
+ */
+ rd = rd_create_remote(RemImap, file, (void *)REMOTE_SIG_SUBTYPE,
+ NULL, "Error: ", "Can't fetch remote signature.");
+ if(!rd){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Can't create stream for sig file: %s", file);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ try_cache = rd_read_metadata(rd);
+
+ if(rd->access == MaybeRorW){
+ if(rd->read_status == 'R')
+ rd->access = ReadOnly;
+ else
+ rd->access = ReadWrite;
+ }
+
+ if(rd->access != NoExists){
+
+ rd_check_remvalid(rd, 1L);
+
+ /*
+ * If the cached info says it is readonly but
+ * it looks like it's been fixed now, change it to readwrite.
+ */
+ if(rd->read_status == 'R'){
+ /*
+ * We go to this trouble since readonly sigfiles
+ * are likely a mistake. They are usually supposed to be
+ * readwrite so we open it and check if it's been fixed.
+ */
+ rd_check_readonly_access(rd);
+ if(rd->read_status == 'W'){
+ rd->access = ReadWrite;
+ rd->flags |= REM_OUTOFDATE;
+ }
+ else{
+ rd_close_remdata(&rd);
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Readonly sig file: %s", file);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ }
+
+ if(rd->flags & REM_OUTOFDATE){
+ if(rd_update_local(rd) != 0){
+
+ dprint((1, "pinerc_remote_open: rd_update_local failed"));
+ /*
+ * Don't give up altogether. We still may be
+ * able to use a cached copy.
+ */
+ }
+ else{
+ dprint((7, "%s: copied remote to local (%ld)",
+ rd->rn, (long)rd->last_use));
+ }
+ }
+
+ if(rd->access == ReadWrite)
+ rd->flags |= DO_REMTRIM;
+ }
+
+ /* If we couldn't get to remote folder, try using the cached copy */
+ if(rd->access == NoExists || rd->flags & REM_OUTOFDATE){
+ rd_close_remdata(&rd);
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Unavailable sig file: %s", file);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ unlink(rd->lf);
+
+ sig = NULL;
+ tmp_20k_buf[0] = '\0';
+ if(objv){
+ Tcl_ListObjGetElements(interp, objv[0], &nSig, &objSig);
+ for(i = 0; i < nSig && i < SIG_MAX_LINES; i++){
+ if((line = Tcl_GetStringFromObj(objSig[i], NULL)) != NULL)
+ snprintf(tmp_20k_buf + strlen(tmp_20k_buf), SIZEOF_20KBUF - strlen(tmp_20k_buf), "%.*s\n",
+ SIG_MAX_COLS, line);
+ }
+ }
+ else if(peTSig){
+ for(i = 0; peTSig[i] && i < SIG_MAX_LINES; i++) {
+ snprintf(tmp_20k_buf + strlen(tmp_20k_buf), SIZEOF_20KBUF - strlen(tmp_20k_buf), "%.*s\n",
+ SIG_MAX_COLS, peTSig[i]);
+ }
+ for(i = 0; peTSig[i]; i++)
+ fs_give((void **)&peTSig[i]);
+ fs_give((void **)&peTSig);
+ }
+ else
+ return(TCL_ERROR);
+
+ sig = cpystr(tmp_20k_buf);
+
+ if((fp = fopen(rd->lf, "w")) != NULL)
+ n = fwrite(sig, strlen(sig), 1, fp);
+
+ fs_give((void **) &sig);
+
+ if(fp){
+ if(n != 1){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Sig copy failure1: %s: %s",
+ rd->lf, error_description(errno));
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ rd_close_remdata(&rd);
+ }
+
+ fclose(fp);
+ if(n != 1)
+ return(TCL_ERROR);
+ }
+ else {
+ rd_close_remdata(&rd);
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Sig copy open failure2: %s: %s",
+ rd->lf, error_description(errno));
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ datebuf[0] = '\0';
+
+ if(!rd->t.i.stream){
+ long retflags = 0;
+
+ rd->t.i.stream = context_open(NULL, NULL, rd->rn, 0L, &retflags);
+ }
+
+ if((e = rd_update_remote(rd, datebuf)) != 0){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Sig update failure: %s: %s",
+ rd->lf, error_description(errno));
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ rd_close_remdata(&rd);
+ return(TCL_ERROR);
+ }
+
+ rd_update_metadata(rd, datebuf);
+ rd->read_status = 'W';
+ rd_close_remdata(&rd);
+ return(TCL_OK);
+}
+
+
+
+NAMEVAL_S *sort_key_rules(index)
+ int index;
+{
+ static NAMEVAL_S is_rules[] = {
+ {"Arrival", 0},
+ {"Date", 0},
+ {"Subject", 0},
+ {"Cc", 0},
+ {"From", 0},
+ {"To", 0},
+ {"size", 0},
+ {"OrderedSubj", 0},
+ {"tHread", 0},
+ {"Arrival/Reverse", 0},
+ {"Date/Reverse", 0},
+ {"Subject/Reverse", 0},
+ {"Cc/Reverse", 0},
+ {"From/Reverse", 0},
+ {"To/Reverse", 0},
+ {"size/Reverse", 0},
+ {"tHread/Reverse", 0},
+ {"OrderedSubj/Reverse", 0}
+ };
+
+ return((index >= 0 && index < (sizeof(is_rules)/sizeof(is_rules[0])))
+ ? &is_rules[index] : NULL);
+}
+
+NAMEVAL_S *wp_indexheight_rules(index)
+ int index;
+{
+ static NAMEVAL_S is_rules[] = {
+ {"normal font", "24", 0},
+ {"smallest font", "20", 0},
+ {"small font", "22", 0},
+ {"large font", "28", 0},
+ {"largest font", "30", 0}
+ };
+
+ return((index >= 0 && index < (sizeof(is_rules)/sizeof(is_rules[0])))
+ ? &is_rules[index] : NULL);
+}
+
+
+/*
+ * PEDebugCmd - turn on/off and set various debugging options
+ */
+int
+PEDebugCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+ char *s;
+
+ if(!--objc){ /* only one arg? */
+ Tcl_WrongNumArgs(interp, 1, objv, "?args?");
+ }
+ else if((s = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){
+ if(!strucmp(s, "level")){
+ if(objc == 2){
+ int level;
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &level) != TCL_OK)
+ return(TCL_ERROR);
+
+ if(level > 0){
+ if(level > 10)
+ level = 10;
+
+ debug = level;
+ dprint((1, "Debug level %d", level));
+ }
+ else{
+ dprint((1, "PEDebug ending"));
+ debug = 0;
+ }
+ }
+
+ Tcl_SetResult(interp, int2string(debug), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strucmp(s, "write")){
+ if(objc == 2 && (s = Tcl_GetStringFromObj(objv[2], NULL))){
+ /*
+ * script debugging has a high priority since
+ * statements can be added/removed on the fly
+ * AND are NOT present by default
+ */
+ dprint((SYSDBG_INFO, "SCRIPT: %s", s));
+ }
+
+ return(TCL_OK);
+ }
+ else if(!strucmp(s, "imap")){
+ int level;
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &level) != TCL_OK)
+ return(TCL_ERROR);
+
+ if(level == 0){
+ if(ps_global){
+ ps_global->debug_imap = 0;
+ if(ps_global->mail_stream)
+ mail_nodebug(ps_global->mail_stream);
+ }
+ }
+ else if(level > 0 && level < 5){
+ if(ps_global){
+ ps_global->debug_imap = level;
+ if(ps_global->mail_stream)
+ mail_debug(ps_global->mail_stream);
+ }
+ }
+
+ return(TCL_OK);
+ }
+ else
+ Tcl_SetResult(interp, "Unknown PEDebug request", TCL_STATIC);
+ }
+
+ return(TCL_ERROR);
+}
+
+
+/*
+ * PESessionCmd - Export TCL Session-wide command set
+ */
+int
+PESessionCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+ char *op, *err = "Unknown PESession option";
+ char *pe_user, *pe_host;
+ int pe_alt, l;
+
+ dprint((2, "PESessionCmd"));
+
+ if((op = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){
+ if(!strcmp(op, "open")){
+ char *s, *pinerc, *pineconf = NULL;
+
+ /*
+ * CMD: open user remote-pinerc local-default-config
+ *
+ * Initiate a session
+ *
+ * Returns: error string on error, nothing otherwise
+ */
+
+ if(objc < 4 || objc > 5){
+ Tcl_WrongNumArgs(interp, 1, objv, "user password pinerc");
+ return(TCL_ERROR);
+ }
+
+ if(!(s = Tcl_GetStringFromObj(objv[2], &l))){
+ Tcl_SetResult(interp, "Unknown User", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ else{
+ int rv;
+
+ pe_user = cpystr(s);
+
+#if defined(HAVE_SETENV)
+ rv = setenv("WPUSER", pe_user, 1);
+#elif defined(HAVE_PUTENV)
+ {
+ static char putenvbuf[PUTENV_MAX];
+
+ if(l + 8 < PUTENV_MAX){
+ if(putenvbuf[0]) /* only called once, but you never know */
+ snprintf(putenvbuf + 7, PUTENV_MAX - 7, "%s", pe_user);
+ else
+ snprintf(putenvbuf, PUTENV_MAX, "WPUSER=%s", pe_user);
+
+ rv = putenv(putenvbuf);
+ }
+ else
+ rv = 1;
+ }
+#endif
+
+ if(rv){
+ fs_give((void **) &pe_user);
+ Tcl_SetResult(interp, (errno == ENOMEM)
+ ? "Insufficient Environment Space"
+ : "Cannot set WPUSER in environment", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ }
+
+ if((pinerc = Tcl_GetStringFromObj(objv[3], NULL)) != NULL){
+ NETMBX mb;
+
+ if(mail_valid_net_parse(pinerc, &mb)){
+ pe_host = cpystr(mb.host);
+ pe_alt = (mb.sslflag || mb.tlsflag);
+ }
+ else {
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Non-Remote Config: %s", pinerc);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ }
+ else {
+ Tcl_SetResult(interp, "Unknown config location", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ if(objc == 5 && !(pineconf = Tcl_GetStringFromObj(objv[4], NULL))){
+ Tcl_SetResult(interp, "Can't determine global config", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ dprint((SYSDBG_INFO, "session (%s) %s - %s",
+ pe_user, pinerc, pineconf ? pineconf : "<none>"));
+
+ /* credential cache MUST already be seeded */
+
+ /* destroy old user context */
+ if(ps_global){
+ /* destroy open stream */
+ peDestroyStream(ps_global);
+
+ /* destroy old user context */
+ peDestroyUserContext(&ps_global);
+ }
+
+ /* Establish a user context */
+ if((s = peCreateUserContext(interp, pe_user, pinerc, pineconf)) != NULL){
+ Tcl_SetResult(interp, s, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ fs_give((void **) &pe_user);
+ fs_give((void **) &pe_host);
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "close")){
+ if(ps_global){
+ /* destroy any open stream */
+ peDestroyStream(ps_global);
+
+ /* destroy user context */
+ peDestroyUserContext(&ps_global);
+ }
+
+ Tcl_SetResult(interp, "BYE", TCL_STATIC);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "creds")){
+ char *folder;
+ int colid;
+
+ if(objc < 4){
+ err = "creds: insufficient args";
+ }
+ else if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR
+ && (folder = Tcl_GetStringFromObj(objv[3], NULL))){
+ int i;
+ CONTEXT_S *cp;
+
+ /*
+ * CMD: creds <collection-index> <folder> [user passwd]
+ *
+ * Test for valid credentials to access given folder
+ *
+ * Returns: 1 if so, 0 otherwise
+ */
+
+ for(i = 0, cp = ps_global ? ps_global->context_list : NULL;
+ i < 1 || cp != NULL ;
+ i++, cp = cp->next)
+ if(i == colid){
+ int rv = 0;
+ char tmp[MAILTMPLEN], *p;
+
+ if(cp){
+ if(folder[0] == '\0'){
+ if(cp->use & CNTXT_INCMNG)
+ rv = 1;
+ else
+ folder = "fake-fake";
+ }
+ else if((cp->use & CNTXT_INCMNG)
+ && (p = folder_is_nick(folder, FOLDERS(cp), FN_NONE)))
+ folder = p;
+ }
+
+ if(!rv && context_allowed(context_apply(tmp, cp, folder, sizeof(tmp)))){
+ NETMBX mb;
+
+ if(mail_valid_net_parse(tmp, &mb)){
+ if(objc == 4){ /* check creds */
+ if(!*mb.user && (p = alpine_get_user(mb.host, (mb.sslflag || mb.tlsflag))))
+ strcpy(mb.user, p);
+
+ if(alpine_have_passwd(mb.user, mb.host, (mb.sslflag || mb.tlsflag)))
+ rv = 1;
+ }
+ else if(objc == 6){ /* set creds */
+ char *user, *passwd;
+
+ if((user = Tcl_GetStringFromObj(objv[4], NULL))
+ && (passwd = Tcl_GetStringFromObj(objv[5], NULL))){
+ if(*mb.user && strcmp(mb.user, user)){
+ err = "creds: mismatched user names";
+ break;
+ }
+
+ alpine_set_passwd(user, passwd, mb.host,
+ mb.sslflag
+ || mb.tlsflag
+ || (ps_global ? F_ON(F_PREFER_ALT_AUTH, ps_global) : 0));
+ rv = 1;
+ }
+ else {
+ err = "creds: unable to read credentials";
+ break;
+ }
+ }
+ else{
+ err = "creds: invalid args";
+ break;
+ }
+ }
+ }
+
+ (void) Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewIntObj(rv));
+ return(TCL_OK);
+ }
+
+ err = "creds: Unrecognized collection ID";
+ }
+ else
+ err = "creds: failure to acquire folder and collection ID";
+ }
+ else if(!strcmp(op, "nocred")){
+ char *folder;
+ int colid;
+
+ if(!ps_global){
+ err = "No Session active";
+ }
+ else if(objc != 4){
+ err = "nocred: wrong number of args";
+ }
+ else if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR
+ && (folder = Tcl_GetStringFromObj(objv[3], NULL))){
+ int i;
+ CONTEXT_S *cp;
+
+ /*
+ * CMD: nocred <collection-index> <folder>
+ *
+ * Test for valid credentials to access given folder
+ *
+ * Returns: 1 if so, 0 otherwise
+ */
+
+ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
+ if(i == colid){
+ int rv = 0;
+ char tmp[MAILTMPLEN], *p;
+
+ if((cp->use & CNTXT_INCMNG)
+ && (p = folder_is_nick(folder, FOLDERS(cp), FN_NONE)))
+ folder = p;
+
+ if(context_allowed(context_apply(tmp, cp, folder, sizeof(tmp)))){
+ NETMBX mb;
+
+ if(mail_valid_net_parse(tmp, &mb)){
+ if(!*mb.user && (p = alpine_get_user(mb.host, (mb.sslflag || mb.tlsflag))))
+ strcpy(mb.user, p);
+
+ alpine_clear_passwd(mb.user, mb.host);
+ }
+ }
+
+ (void) Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewIntObj(rv));
+ return(TCL_OK);
+ }
+
+ err = "creds: Unrecognized collection ID";
+ }
+ else
+ err = "creds: failure to acquire folder and collection ID";
+ }
+ else if(!strcmp(op, "acceptcert")){
+ char *certhost;
+ STRLIST_S **p;
+
+ if((certhost = Tcl_GetStringFromObj(objv[2], NULL))){
+ for(p = &peCertHosts; *p; p = &(*p)->next)
+ ;
+
+ *p = new_strlist(certhost);
+ }
+
+ err = "PESession: no server name";
+ }
+ else if(!strcmp(op, "random")){
+ if(objc != 3){
+ err = "PESession: random <length>";
+ } else {
+ char s[1025];
+ int l;
+
+ if(Tcl_GetIntFromObj(interp,objv[2],&l) != TCL_ERROR){
+ if(l <= 1024){
+ Tcl_SetResult(interp, peRandomString(s,l,PRS_MIXED_CASE), TCL_STATIC);
+ return(TCL_OK);
+ }
+ else
+ err = "PESession: random length too long";
+ }
+ else
+ err = "PESession: can't get random length";
+ }
+ }
+ else if(!strcmp(op, "authdriver")){
+ if(objc != 4){
+ err = "PESession: authdriver {add | remove} drivername";
+ } else {
+ char *cmd, *driver;
+
+ if((cmd = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
+ if((driver = Tcl_GetStringFromObj(objv[3], NULL)) != NULL){
+ if(!strcmp(cmd,"enable")){
+ err = "PESession: authdriver enable disabled for the nonce";
+ }
+ else if(!strcmp(cmd,"disable")){
+ if(mail_parameters(NULL, DISABLE_AUTHENTICATOR, (void *) driver)){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Authentication driver %.30s disabled", driver);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "PESession: Can't disable %.30s", driver);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ }
+ else
+ err = "PESession: unknown authdriver operation";
+ }
+ else
+ err = "PESession: Can't read driver name";
+ }
+ else
+ err = "PESesions: Can't read authdriver operation";
+ }
+ }
+ else if(!strcmp(op, "abandon")){
+ /*
+ * CMD: abandon [timeout]
+ *
+ * Returns: nothing
+ */
+
+ if(objc != 3){
+ err = "PESession: abandon [timeout]";
+ } else {
+ long t;
+
+ if(Tcl_GetLongFromObj(interp, objv[2], &t) == TCL_OK){
+ /* ten second minimum and max of default */
+ if(t > 0 && t <= PE_INPUT_TIMEOUT){
+ gPEAbandonTimeout = t;
+ return(TCL_OK);
+ }
+ else
+ err = "unrecognized timeout";
+ }
+ else
+ err = "Can't read timeout";
+ }
+ }
+ else if(!strcmp(op, "noexpunge")){
+ /*
+ * CMD: noexpunge <state>
+ *
+ * Returns: nothing
+ */
+
+ if(objc != 3){
+ err = "PESession: noexpunge <state>";
+ } else {
+ int onoff;
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &onoff) == TCL_OK){
+ if(onoff == 0 || onoff == 1){
+ ps_global->noexpunge_on_close = onoff;
+ return(TCL_OK);
+ }
+
+ err = "unrecognized on/off state";
+ }
+ else
+ err = "Can't read on/off state";
+ }
+ }
+ else if(!strcmp(op, "setpassphrase")){
+#ifdef SMIME
+ char *passphrase;
+
+ if(objc != 3){
+ err = "PESession: setpassphrase <state>";
+ }
+ else if((passphrase = Tcl_GetStringFromObj(objv[2], NULL))){
+ if(ps_global && ps_global->smime){
+ strncpy((char *) ps_global->smime->passphrase, passphrase,
+ sizeof(ps_global->smime->passphrase));
+ ps_global->smime->passphrase[sizeof(ps_global->smime->passphrase)-1] = '\0';
+ ps_global->smime->entered_passphrase = 1;
+ ps_global->smime->need_passphrase = 0;
+ peED.uid = 0;
+ return(TCL_OK);
+ }
+ }
+#else
+ err = "S/MIME not configured for this server";
+#endif /* SMIME */
+ }
+ else if(!strcmp(op, "expungecheck")) {
+ /*
+ * Return open folders and how many deleted messages they have
+ *
+ * return looks something like a list of these:
+ * {folder-name number-deleted isinbox isincoming}
+ */
+ char *type;
+ long delete_count;
+ Tcl_Obj *resObj;
+
+ if(objc != 3){
+ err = "PESession: expungecheck <type>";
+ }
+ else {
+ type = Tcl_GetStringFromObj(objv[2], NULL);
+ if(type && (strcmp(type, "current") == 0 || strcmp(type, "quit") == 0)){
+
+ if(ps_global->mail_stream != sp_inbox_stream()
+ || strcmp(type, "current") == 0){
+ delete_count = count_flagged(ps_global->mail_stream, F_DEL);
+ resObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(pretty_fn(ps_global->cur_folder), -1));
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewIntObj(delete_count));
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewIntObj((ps_global->mail_stream
+ == sp_inbox_stream())
+ ? 1 : 0));
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewIntObj((ps_global->context_current->use & CNTXT_INCMNG)
+ ? 1 : 0));
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ resObj);
+ }
+ if(strcmp(type, "quit") == 0){
+ delete_count = count_flagged(sp_inbox_stream(), F_DEL);
+ resObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj("INBOX", -1));
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewIntObj(delete_count));
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewIntObj(1));
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewIntObj(1));
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp), resObj);
+ }
+ return(TCL_OK);
+ }
+ else
+ err = "PESession: expungecheck unknown type";
+ }
+ }
+ else if(!strcmp(op, "mailcheck")) {
+ /*
+ * CMD: mailcheck
+ *
+ * ARGS: reload -- "1" if we're reloading
+ * (vs. just checking newmail as a side effect
+ * of building a new page)
+ *
+ * Return list of folders with new or expunged messages
+ *
+ * return looks something like a list of these:
+ * {new-count newest-uid announcement-msg}
+ */
+ int reload, force = UFU_NONE, rv;
+ time_t now = time(0);
+
+ if(objc <= 3){
+ if(objc < 3 || Tcl_GetIntFromObj(interp, objv[2], &reload) == TCL_ERROR)
+ reload = 0;
+
+ /* minimum 10 second between IMAP pings */
+ if(!time_of_last_input() || now - time_of_last_input() > 10){
+ force = UFU_FORCE;
+ if(!reload)
+ peMarkInputTime();
+ }
+
+ peED.interp = interp;
+
+ /* check for new mail */
+ new_mail(force, reload ? GoodTime : VeryBadTime, NM_STATUS_MSG);
+
+ if(!reload){ /* announced */
+ zero_new_mail_count();
+ }
+
+ return(TCL_OK);
+ }
+ else
+ err = "PESession: mailcheck <reload>";
+ }
+ }
+
+ Tcl_SetResult(interp, err, TCL_STATIC);
+ return(TCL_ERROR);
+}
+
+
+
+/*
+ * PEFolderChange - create context's directory chain
+ * corresponding to list of given obj's
+ *
+ * NOTE: caller should call reset_context_folders(cp) to
+ * clean up data structures this creates before returning
+ */
+int
+PEFolderChange(Tcl_Interp *interp, CONTEXT_S *cp, int objc, Tcl_Obj *CONST objv[])
+{
+ int i;
+ FDIR_S *fp;
+ char *folder;
+
+ for(i = 0; i < objc; i++) {
+ folder = Tcl_GetStringFromObj(objv[i], NULL);
+ if(!folder) {
+ Tcl_SetResult(interp, "PEFolderChange: Can't read folder", TCL_VOLATILE);
+ reset_context_folders(cp);
+ return(TCL_ERROR);
+ }
+
+ fp = next_folder_dir(cp, folder, 0, NULL); /* BUG: mail_stream? */
+ fp->desc = folder_lister_desc(cp, fp);
+ fp->delim = cp->dir->delim;
+ fp->prev = cp->dir;
+ fp->status |= CNTXT_SUBDIR;
+ cp->dir = fp;
+ }
+
+ return(TCL_OK);
+}
+
+/*
+ * PEMakeFolderString:
+ */
+int
+PEMakeFolderString(Tcl_Interp *interp, CONTEXT_S *cp, int objc, Tcl_Obj *CONST objv[], char **ppath)
+{
+ int i;
+ unsigned long size,len;
+ char *portion,*path;
+
+ size = 0;
+ for(i = 0; i < objc; i++) {
+ portion = Tcl_GetStringFromObj(objv[i], NULL);
+ if(!portion) {
+ Tcl_SetResult(interp, "PEMakeFolderString: Can't read folder",
+ TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ if(i) size++;
+ size += strlen(portion);
+ }
+
+ path = (char*) fs_get(size + 1);
+ size = 0;
+ for(i = 0; i < objc; i++) {
+ portion = Tcl_GetStringFromObj(objv[i], NULL);
+ len = strlen(portion);
+ if(i) path[size++] = cp->dir->delim;
+ memcpy(path + size, portion, len);
+ size += len;
+ }
+ path[size] = '\0';
+ if(ppath) *ppath = path; else fs_give((void**) &path);
+ return(TCL_OK);
+}
+
+
+/*
+ * PEFolderCmd - export various bits of folder information
+ */
+int
+PEFolderCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+ char *op, errbuf[256], *err = "Unknown PEFolder request";
+
+ dprint((2, "PEFolderCmd"));
+
+ if(objc == 1){
+ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
+ }
+ else if((op = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){
+ if(ps_global){
+ if(objc == 2){
+ if(!strcmp(op, "current")){
+ CONTEXT_S *cp;
+ int i;
+
+ /*
+ * CMD: current
+ *
+ * Returns: string representing the name of the
+ * current mailbox
+ */
+
+ for(i = 0, cp = ps_global->context_list; cp && cp != ps_global->context_current; i++, cp = cp->next)
+ ;
+
+ if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewIntObj(cp ? i : 0)) == TCL_OK
+ && Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(ps_global->cur_folder,-1)) == TCL_OK)
+ return(TCL_OK);
+
+ return(TCL_ERROR);
+ }
+ else if(!strcmp(op, "collections")){
+ CONTEXT_S *cp;
+ int i;
+
+ /*
+ * CMD: collections
+ *
+ * Returns: List of currently configured collections
+ */
+ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next){
+ Tcl_Obj *objv[3];
+
+ objv[0] = Tcl_NewIntObj(i);
+ objv[1] = Tcl_NewStringObj(cp->nickname ? cp->nickname : "", -1);
+ objv[2] = Tcl_NewStringObj(cp->label ? cp->label : "", -1);
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewListObj(3, objv));
+ }
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "defaultcollection")){
+ int i;
+ CONTEXT_S *cp;
+
+ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
+ if(cp->use & CNTXT_SAVEDFLT){
+ Tcl_SetResult(interp, int2string(i), TCL_STATIC);
+ return(TCL_OK);
+ }
+
+ err = "PEFolder: isincoming: Invalid collection ID";
+ }
+ else if(!strcmp(op, "clextended")){
+ CONTEXT_S *cp;
+ int i;
+ char tpath[MAILTMPLEN], *p;
+
+ /*
+ * CMD: clextended
+ *
+ * Returns: Extended list of current collections
+ *
+ * Format:
+ * 0) Collection Number
+ * 1) Nickname
+ * 2) Label
+ * 3) Basically this is a flag to say if we can edit
+ * 4) Server
+ * 5) Path
+ * 6) View
+ */
+ /*
+ * had to get rid of this cause the args are changed
+ *
+ * if(strcmp("extended",
+ * Tcl_GetStringFromObj(objv[2], NULL))){
+ * Tcl_SetResult(interp, "invalid argument", TCL_VOLATILE);
+ * return(TCL_ERROR);
+ * }
+ */
+ for(i = 0, cp = ps_global->context_list; cp ;
+ i++, cp = cp->next){
+ Tcl_Obj *objv[7];
+
+ objv[0] = Tcl_NewIntObj(i);
+ objv[1] = Tcl_NewStringObj(cp->nickname ?
+ cp->nickname : "", -1);
+ objv[2] = Tcl_NewStringObj(cp->label ?
+ cp->label : "", -1);
+ objv[3] = Tcl_NewIntObj(cp->var.v ? 1 : 0);
+ objv[4] = Tcl_NewStringObj(cp->server ?
+ cp->server : "", -1);
+ tpath[0] = '\0';
+ if(cp->context){
+ strncpy(tpath, (cp->context[0] == '{'
+ && (p = strchr(cp->context, '}')))
+ ? ++p
+ : cp->context, sizeof(tpath));
+ tpath[sizeof(tpath)-1] = '\0';
+ if((p = strstr(tpath, "%s")) != NULL)
+ *p = '\0';
+ }
+ objv[5] = Tcl_NewStringObj(tpath, -1);
+ objv[6] = Tcl_NewStringObj(cp->dir &&
+ cp->dir->view.user ?
+ cp->dir->view.user :
+ "", -1);
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewListObj(7, objv));
+ }
+
+ return(TCL_OK);
+ }
+ }
+ else if(objc == 3 && !strcmp(op, "delimiter")){
+ int colid, i;
+ char delim[2] = {'\0', '\0'};
+ CONTEXT_S *cp;
+
+ if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR){
+ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
+ if(i == colid){
+ if(cp->dir && cp->dir->delim)
+ delim[0] = cp->dir->delim;
+
+ break;
+ }
+
+ Tcl_SetResult(interp, delim[0] ? delim : "/", TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else
+ err = "PEFolder: delimiter: Can't read collection ID";
+ }
+ else if(objc == 3 && !strcmp(op, "isincoming")){
+ int colid, i;
+ CONTEXT_S *cp;
+
+ if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR){
+ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
+ if(i == colid){
+ Tcl_SetResult(interp, int2string(((cp->use & CNTXT_INCMNG) != 0)), TCL_STATIC);
+ return(TCL_OK);
+ }
+
+ err = "PEFolder: isincoming: Invalid collection ID";
+ }
+ else
+ err = "PEFolder: isincoming: Can't read collection ID";
+ }
+ else if(objc == 4 && !strcmp(op, "unread")){
+ char *folder, tmp[MAILTMPLEN];
+ MAILSTREAM *mstream;
+ CONTEXT_S *cp;
+ long colid, i, count = 0, flags = (F_UNSEEN | F_UNDEL);
+ int our_stream = 0;
+ /*
+ * CMD: unread
+ *
+ * Returns: number of unread messages in given
+ * folder
+ */
+ if(Tcl_GetLongFromObj(interp,objv[2],&colid) != TCL_ERROR){
+ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
+ if(i == colid)
+ break;
+
+ if(cp){
+ if((folder = Tcl_GetStringFromObj(objv[3], NULL)) != NULL){
+ /* short circuit INBOX */
+ if(colid == 0 && !strucmp(folder, "inbox")){
+ count = count_flagged(sp_inbox_stream(), flags);
+ }
+ else{
+ /*
+ * BUG: some sort of caching to prevent open() fore each call?
+ * does stream cache offset this?
+ */
+ if(!(context_allowed(context_apply(tmp, cp, folder, sizeof(tmp)))
+ && (mstream = same_stream_and_mailbox(tmp, ps_global->mail_stream)))){
+ long retflags = 0;
+
+ ps_global->noshow_error = 1;
+ our_stream = 1;
+ mstream = context_open(cp, NULL, folder,
+ SP_USEPOOL | SP_TEMPUSE| OP_READONLY | OP_SHORTCACHE,
+ &retflags);
+ ps_global->noshow_error = 0;
+ }
+
+ count = count_flagged(mstream, flags);
+
+ if(our_stream)
+ pine_mail_close(mstream);
+ }
+
+ Tcl_SetResult(interp, long2string(count), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ }
+ else
+ err = "PEFolder: unread: Invalid collection ID";
+ }
+ else
+ err = "PEFolder: unread: Can't read collection ID";
+ }
+ else if(objc == 5 && !strcmp(op, "empty")){
+ /*
+ * CMD: empty
+ *
+ * Returns: number of expunge messages
+ *
+ * Arguments: <colnum> <folder> <what>
+ * where <what> is either <uid>, 'selected', or 'all'
+ */
+ CONTEXT_S *cp;
+ MAILSTREAM *stream = NULL;
+ MESSAGECACHE *mc;
+ MSGNO_S *msgmap;
+ int colid, i, our_stream = 0;
+ long uid, raw, count = 0L;
+ char *errstr = NULL, *what, *folder, *p, tmp[MAILTMPLEN];
+
+ if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR){
+ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
+ if(i == colid) break;
+ }
+
+ if(cp){
+ if((folder = Tcl_GetStringFromObj(objv[3], NULL)) != NULL){
+ if((what = Tcl_GetStringFromObj(objv[4], NULL)) != NULL){
+ /* need to open? */
+ if(!((context_allowed(context_apply(tmp, cp, folder, sizeof(tmp)))
+ && (stream = same_stream_and_mailbox(tmp, ps_global->mail_stream)))
+ || (stream = same_stream_and_mailbox(tmp, sp_inbox_stream())))){
+ long retflags = 0;
+
+ our_stream = 1;
+ stream = context_open(cp, NULL, folder, SP_USEPOOL | SP_TEMPUSE | OP_SHORTCACHE, &retflags);
+ }
+
+ if(stream){
+ msgmap = sp_msgmap(stream);
+
+ if(!strucmp(what, "all")){
+ if(mn_get_total(msgmap)){
+ agg_select_all(stream, msgmap, NULL, 1);
+ errstr = peApplyFlag(stream, msgmap, 'd', 0, &count);
+ if(!errstr)
+ (void) cmd_expunge_work(stream, msgmap);
+ }
+ }
+ else{
+ /* little complicated since we don't display deleted state and
+ * don't want to expunge what's not intended.
+ * remember what's deleted and restore state on the ones left
+ * when we're done. shouldn't happen much.
+ * NOTE: "uid" is NOT a UID in this loop
+ */
+ for(uid = 1L; uid <= mn_get_total(msgmap); uid++){
+ raw = mn_m2raw(msgmap, uid);
+ if(!get_lflag(stream, msgmap, uid, MN_EXLD)
+ && (mc = mail_elt(stream, raw)) != NULL
+ && mc->deleted){
+ set_lflag(stream, msgmap, uid, MN_STMP, 1);
+ mail_flag(stream, long2string(raw), "\\DELETED", 0L);
+ }
+ else
+ set_lflag(stream, msgmap, uid, MN_STMP, 0);
+ }
+
+ if(!strucmp(what,"selected")){
+ if(any_lflagged(msgmap, MN_SLCT)){
+ if(!(errstr = peApplyFlag(stream, msgmap, 'd', 0, &count)))
+ (void) cmd_expunge_work(stream, msgmap);
+ }
+ else
+ count = 0L;
+ }
+ else{
+ uid = 0;
+ for(p = what; *p; p++)
+ if(isdigit((unsigned char) *p)){
+ uid = (uid * 10) + (*p - '0');
+ }
+ else{
+ errstr = "Invalid uid value";
+ break;
+ }
+
+ if(!errstr && uid){
+ /* uid is a UID here */
+ mail_flag(stream, long2string(uid), "\\DELETED", ST_SET | ST_UID);
+ (void) cmd_expunge_work(stream, msgmap);
+ count = 1L;
+ }
+ }
+
+ /* restore deleted on what didn't get expunged */
+ for(uid = 1L; uid <= mn_get_total(msgmap); uid++){
+ raw = mn_m2raw(msgmap, uid);
+ if(get_lflag(stream, msgmap, uid, MN_STMP)){
+ set_lflag(stream, msgmap, uid, MN_STMP, 0);
+ mail_flag(stream, long2string(raw), "\\DELETED", ST_SET);
+ }
+ }
+ }
+
+ if(our_stream)
+ pine_mail_close(stream);
+ }
+ else
+ errstr = "no stream";
+ }
+ else
+ errstr = "Cannot get which ";
+ }
+ else
+ errstr = "Cannot get folder";
+ }
+ else
+ errstr = "Invalid collection";
+
+ if(errstr){
+ Tcl_SetResult(interp, errstr, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ Tcl_SetResult(interp, long2string(count), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "export")){
+ /*
+ * CMD: export
+ *
+ * Returns: success or failure after writing given
+ * folder to given local file.
+ *
+ * Format:
+ * 0) Collection Number
+ * 1) Folder
+ * 2) Destination file
+ */
+ if(objc == 5){
+ CONTEXT_S *cp;
+ MAILSTREAM *src;
+ APPEND_PKG pkg;
+ STRING msg;
+ long colid, i;
+ char *folder, *dfile, seq[64], tmp[MAILTMPLEN];
+ int our_stream = 0;
+
+ if(Tcl_GetLongFromObj(interp,objv[2],&colid) != TCL_ERROR){
+ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
+ if(i == colid)
+ break;
+
+ if(cp){
+ if((folder = Tcl_GetStringFromObj(objv[3], NULL)) != NULL){
+ if((dfile = Tcl_GetStringFromObj(objv[4], NULL)) != NULL){
+ if(mail_parameters(NULL, ENABLE_DRIVER, "unix")){
+
+ snprintf(tmp, sizeof(tmp), "#driver.unix/%s", dfile);
+
+ if(pine_mail_create(NULL, tmp)){
+
+ err = NULL; /* reset error condition */
+
+ /*
+ * if not current folder, open a stream, setup the
+ * stuff to write the raw header/text by hand
+ * with berkeley delimiters since we don't want
+ * a local mailbox driver lunk in.
+ *
+ * comments:
+ * - BUG: what about logins?
+ *
+ */
+ if(!(context_allowed(context_apply(tmp, cp, folder, sizeof(tmp)))
+ && (src = same_stream_and_mailbox(tmp, ps_global->mail_stream)))){
+ long retflags = 0;
+
+ our_stream = 1;
+ src = context_open(cp, NULL, folder,
+ SP_USEPOOL | SP_TEMPUSE | OP_READONLY | OP_SHORTCACHE,
+ &retflags);
+ }
+
+ if(src && src->nmsgs){
+ /* Go to work...*/
+ pkg.stream = src;
+ pkg.msgno = 0;
+ pkg.msgmax = src->nmsgs;
+ pkg.flags = pkg.date = NIL;
+ pkg.message = &msg;
+
+ snprintf (seq,sizeof(seq),"1:%lu",src->nmsgs);
+ mail_fetchfast (src, seq);
+
+ ps_global->noshow_error = 1;
+ if(!mail_append_multiple (NULL, dfile,
+ peAppendMsg, (void *) &pkg)){
+ snprintf(err = errbuf, sizeof(errbuf), "PEFolder: export: %.200s",
+ ps_global->c_client_error);
+ }
+
+ ps_global->noshow_error = 0;
+
+ if(our_stream)
+ pine_mail_close(src);
+ }
+ else
+ err = "PEFolder: export: can't open mail folder";
+
+ if(!err)
+ return(TCL_OK);
+ }
+ else
+ err = "PEFolder: export: can't create destination";
+
+ if(!mail_parameters(NULL, DISABLE_DRIVER, "unix"))
+ err = "PEFolder: export: can't disable driver";
+ }
+ else
+ err = "PEFolder: export: can't enable driver";
+ }
+ else
+ err = "PEFolder: export: can't read file name";
+ }
+ else
+ err = "PEFolder: export: can't read folder name";
+ }
+ else
+ err = "PEFolder: export: Invalid collection ID";
+ }
+ else
+ err = "PEFolder:export: Can't read collection ID";
+ }
+ else
+ err = "PEFolder: export <colid> <folder> <file>";
+ }
+ else if(!strcmp(op, "import")){
+ /*
+ * CMD: import
+ *
+ * Returns: success or failure after writing given
+ * folder to given local file.
+ *
+ * Format:
+ * 0) source file
+ * 1) destination collection number
+ * 2) destination folder
+ */
+ if(objc == 5){
+ CONTEXT_S *cp;
+ MAILSTREAM *src, *dst;
+ APPEND_PKG pkg;
+ STRING msg;
+ long colid, i;
+ char *folder, *sfile, seq[64];
+
+ /* get source file with a little sanity check */
+ if((sfile = Tcl_GetStringFromObj(objv[2], NULL))
+ && *sfile == '/' && !strstr(sfile, "..")){
+ if(mail_parameters(NULL, ENABLE_DRIVER, "unix")){
+
+ ps_global->noshow_error = 1; /* don't queue error msg */
+ err = NULL; /* reset error condition */
+
+ /* make sure sfile contains valid mail */
+ if((src = mail_open(NULL, sfile, 0L)) != NULL){
+
+ if(Tcl_GetLongFromObj(interp,objv[3],&colid) != TCL_ERROR){
+ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
+ if(i == colid)
+ break;
+
+ if(cp){
+ if((folder = Tcl_GetStringFromObj(objv[4], NULL)) != NULL){
+ long retflags = 0;
+
+ if(context_create(cp, NULL, folder)
+ && (dst = context_open(cp, NULL, folder, SP_USEPOOL | SP_TEMPUSE, &retflags))){
+
+ if(src->nmsgs){
+ /* Go to work...*/
+ pkg.stream = src;
+ pkg.msgno = 0;
+ pkg.msgmax = src->nmsgs;
+ pkg.flags = pkg.date = NIL;
+ pkg.message = &msg;
+
+ snprintf (seq,sizeof(seq),"1:%lu",src->nmsgs);
+ mail_fetchfast (src, seq);
+
+ if(!context_append_multiple(cp, dst, folder,
+ peAppendMsg, (void *) &pkg,
+ ps_global->mail_stream)){
+ snprintf(err = errbuf, sizeof(errbuf), "PEFolder: import: %.200s",
+ ps_global->c_client_error);
+ }
+
+ }
+
+ pine_mail_close(dst);
+ }
+ else
+ snprintf(err = errbuf, sizeof(errbuf), "PEFolder: import: %.200s",
+ ps_global->c_client_error);
+ }
+ else
+ err = "PEFolder: import: can't read folder name";
+ }
+ else
+ err = "PEFolder:import: invalid collection id";
+ }
+ else
+ err = "PEFolder: import: can't read collection id";
+
+ mail_close(src);
+
+ }
+ else
+ snprintf(err = errbuf, sizeof(errbuf), "PEFolder: import: %.200s",
+ ps_global->c_client_error);
+
+ ps_global->noshow_error = 0;
+
+ if(!mail_parameters(NULL, DISABLE_DRIVER, "unix") && !err)
+ err = "PEFolder: import: can't disable driver";
+
+ if(!err)
+ return(TCL_OK);
+ }
+ else
+ err = "PEFolder: import: can't enable driver";
+ }
+ else
+ err = "PEFolder: import: can't read file name";
+ }
+ else
+ err = "PEFolder: import <file> <colid> <folder>";
+ }
+ else {
+ int i, colid;
+ char *aes, *colstr;
+ CONTEXT_S *cp;
+
+ /*
+ * 3 or more arguments, 3rd is the collection ID, rest
+ * are a folder name
+ */
+
+ if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR){
+ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
+ if(i == colid) break;
+ }
+ else if((colstr = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
+ if(!strcmp("default", colstr))
+ cp = default_save_context(ps_global->context_list);
+ else
+ cp = NULL;
+ }
+ else
+ cp = NULL;
+
+ if(cp){
+ if(!strcmp(op, "list")){
+ int i, fcount, bflags = BFL_NONE;
+
+ if(PEFolderChange(interp, cp, objc - 3, objv + 3) == TCL_ERROR)
+ return TCL_ERROR;
+
+ if(cp->use & CNTXT_NEWS)
+ bflags |= BFL_LSUB;
+
+ ps_global->c_client_error[0] = ps_global->last_error[0] = '\0';
+
+ pePrepareForAuthException();
+
+ build_folder_list(NULL, cp, "*", NULL, bflags);
+
+ if((aes = peAuthException()) != NULL){
+ Tcl_SetResult(interp, aes, TCL_VOLATILE);
+ reset_context_folders(cp);
+ return(TCL_ERROR);
+ }
+
+ if((fcount = folder_total(FOLDERS(cp))) != 0){
+ for(i = 0; i < fcount; i++){
+ char type[3], *p;
+ FOLDER_S *f = folder_entry(i, FOLDERS(cp));
+
+ p = type;
+ if(f->isdir){
+ *p++ = 'D';
+
+ if(f->hasnochildren && !f->haschildren)
+ *p++ = 'E';
+ }
+
+ if(f->isfolder
+ || f->nickname
+ || (cp->use & CNTXT_INCMNG))
+ *p++ = 'F';
+
+ *p = '\0';
+
+ peAppListF(interp, Tcl_GetObjResult(interp), "%s%s", type,
+ f->nickname ? f->nickname : f->name);
+ }
+ }
+
+ reset_context_folders(cp);
+ return(TCL_OK);
+ }
+ else if(!strucmp(op, "exists")){
+ char *folder, *errstr = NULL;
+ int rv;
+
+ if(objc < 4) {
+ Tcl_SetResult(interp, "PEFolder exists: No folder specified", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ folder = Tcl_GetStringFromObj(objv[objc - 1], NULL);
+ if(!folder) {
+ Tcl_SetResult(interp, "PEFolder exists: Can't read folder", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ if(PEFolderChange(interp, cp, objc - 4, objv + 3) == TCL_ERROR)
+ return TCL_ERROR;
+
+ ps_global->c_client_error[0] = '\0';
+ pePrepareForAuthException();
+
+ rv = folder_name_exists(cp, folder, NULL);
+
+ if(rv & FEX_ERROR){
+ if((errstr = peAuthException()) == NULL){
+ if(ps_global->c_client_error[0])
+ errstr = ps_global->c_client_error;
+ else
+ errstr = "Indeterminate Error";
+ }
+ }
+
+ Tcl_SetResult(interp, errstr ? errstr : int2string((int)(rv & FEX_ISFILE)), TCL_VOLATILE);
+ return(errstr ? TCL_ERROR : TCL_OK);
+ }
+ else if(!strucmp(op, "fullname")){
+ char *folder, *fullname;
+
+ if(objc < 4) {
+ Tcl_SetResult(interp, "PEFolder fullname: No folder specified", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ folder = Tcl_GetStringFromObj(objv[objc - 1], NULL);
+ if(!folder) {
+ Tcl_SetResult(interp, "PEFolder fullname: Can't read folder", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ if(PEFolderChange(interp, cp, objc - 4, objv + 3) == TCL_ERROR)
+ return TCL_ERROR;
+
+#if 0
+ Tcl_Obj *obj = Tcl_NewStringObj((fullname = folder_is_nick(folder, FOLDERS(cp)))
+ ? fullname : folder, -1);
+ (void) Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ obj);
+#else
+ Tcl_SetResult(interp,
+ (fullname = folder_is_nick(folder, FOLDERS(cp), FN_NONE)) ? fullname : folder,
+ TCL_VOLATILE);
+#endif
+
+ return(TCL_OK);
+ }
+ else if(!strucmp(op, "create")){
+ char *aes, *folder;
+
+ folder = Tcl_GetStringFromObj(objv[objc - 1], NULL);
+ if(!folder) {
+ Tcl_SetResult(interp, "PEFolder create: Can't read folder", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ if(PEFolderChange(interp, cp, objc - 4, objv + 3) == TCL_ERROR)
+ return TCL_ERROR;
+
+ ps_global->c_client_error[0] = ps_global->last_error[0] = '\0';
+ pePrepareForAuthException();
+
+ if(!context_create(cp, NULL, folder)){
+ if((aes = peAuthException()) != NULL){
+ Tcl_SetResult(interp, aes, TCL_VOLATILE);
+ }
+ else{
+ Tcl_SetResult(interp,
+ (ps_global->last_error[0])
+ ? ps_global->last_error
+ : (ps_global->c_client_error[0])
+ ? ps_global->c_client_error
+ : "Unable to create folder",
+ TCL_VOLATILE);
+ }
+
+ reset_context_folders(cp);
+ return(TCL_ERROR);
+ }
+
+ Tcl_SetResult(interp, "OK", TCL_STATIC);
+ reset_context_folders(cp);
+ return(TCL_OK);
+ }
+ else if(!strucmp(op, "delete")){
+ int fi, readonly, close_opened = 0;
+ char *folder, *fnamep, *target = NULL, *aes;
+ MAILSTREAM *del_stream = NULL, *strm = NULL;
+ EditWhich ew;
+ PINERC_S *prc = NULL;
+ FOLDER_S *fp;
+
+ folder = Tcl_GetStringFromObj(objv[objc - 1], NULL);
+ if(!folder) {
+ Tcl_SetResult(interp, "PEFolder delete: Can't read folder", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ if(PEFolderChange(interp, cp, objc - 4, objv + 3) == TCL_ERROR)
+ return TCL_ERROR;
+
+ /* so we can check for folder's various properties */
+ build_folder_list(NULL, cp, folder, NULL, BFL_NONE);
+
+ ps_global->c_client_error[0] = ps_global->last_error[0] = '\0';
+
+ pePrepareForAuthException();
+
+ /* close open folder, then delete */
+
+ if((fi = folder_index(folder, cp, FI_FOLDER)) < 0
+ || (fp = folder_entry(fi, FOLDERS(cp))) == NULL){
+ Tcl_SetResult(interp, "Cannot find folder to delete", TCL_STATIC);
+ reset_context_folders(cp);
+ return(TCL_ERROR);
+ }
+
+ if(!((cp->use & CNTXT_INCMNG) && fp->name
+ && check_for_move_mbox(fp->name, NULL, 0, &target))){
+ target = NULL;
+ }
+
+ dprint((4, "=== delete_folder(%s) ===\n", folder ? folder : "?"));
+
+ ew = config_containing_inc_fldr(fp);
+ if(ps_global->restricted)
+ readonly = 1;
+ else{
+ switch(ew){
+ case Main:
+ prc = ps_global->prc;
+ break;
+ case Post:
+ prc = ps_global->post_prc;
+ break;
+ case None:
+ break;
+ }
+
+ readonly = prc ? prc->readonly : 1;
+ }
+
+ if(prc && prc->quit_to_edit && (cp->use & CNTXT_INCMNG)){
+ Tcl_SetResult(interp, "Must Exit Alpine to Change Configuration", TCL_STATIC);
+ reset_context_folders(cp);
+ return(TCL_ERROR);
+ }
+
+ if(cp == ps_global->context_list
+ && !(cp->dir && cp->dir->ref)
+ && strucmp(folder, ps_global->inbox_name) == 0){
+ Tcl_SetResult(interp, "Cannot delete special folder", TCL_STATIC);
+ reset_context_folders(cp);
+ return(TCL_ERROR);
+ }
+ else if(readonly && (cp->use & CNTXT_INCMNG)){
+ Tcl_SetResult(interp, "Folder not in editable config file", TCL_STATIC);
+ reset_context_folders(cp);
+ return(TCL_ERROR);
+ }
+ else if((fp->name
+ && (strm=context_already_open_stream(cp,fp->name,AOS_NONE)))
+ ||
+ (target
+ && (strm=context_already_open_stream(NULL,target,AOS_NONE)))){
+ if(strm == ps_global->mail_stream)
+ close_opened++;
+ }
+ else if(fp->isdir || fp->isdual){ /* NO DELETE if directory isn't EMPTY */
+ FDIR_S *fdirp = next_folder_dir(cp,folder,TRUE,NULL);
+ int ret;
+
+ if(fp->haschildren)
+ ret = 1;
+ else if(fp->hasnochildren)
+ ret = 0;
+ else{
+ ret = folder_total(fdirp->folders) > 0;
+ free_fdir(&fdirp, 1);
+ }
+
+ if(ret){
+ Tcl_SetResult(interp, "Cannot delete non-empty directory", TCL_STATIC);
+ reset_context_folders(cp);
+ return(TCL_ERROR);
+ }
+
+ /*
+ * Folder by the same name exist, so delete both...
+ if(fp->isdual){
+ Tcl_SetResult(interp, "Cannot delete: folder is also a directory", TCL_STATIC);
+ reset_context_folders(cp);
+ return(TCL_ERROR);
+ }
+ */
+ }
+
+ if(cp->use & CNTXT_INCMNG){
+ Tcl_SetResult(interp, "Cannot delete incoming folder", TCL_STATIC);
+ reset_context_folders(cp);
+ return(TCL_ERROR);
+ }
+
+ dprint((2,"deleting \"%s\" (%s) in context \"%s\"\n",
+ fp->name ? fp->name : "?",
+ fp->nickname ? fp->nickname : "",
+ cp->context ? cp->context : "?"));
+ if(strm){
+ /*
+ * Close it, NULL the pointer, and let do_broach_folder fixup
+ * the rest...
+ */
+ pine_mail_actually_close(strm);
+ if(close_opened){
+ do_broach_folder(ps_global->inbox_name,
+ ps_global->context_list,
+ NULL, DB_INBOXWOCNTXT);
+ }
+ }
+
+ /*
+ * Use fp->name since "folder" may be a nickname...
+ */
+ if(ps_global->mail_stream
+ && context_same_stream(cp, fp->name, ps_global->mail_stream))
+ del_stream = ps_global->mail_stream;
+
+ fnamep = fp->name;
+
+ if(!context_delete(cp, del_stream, fnamep)){
+ if((aes = peAuthException()) != NULL){
+ Tcl_SetResult(interp, aes, TCL_VOLATILE);
+ }
+ else{
+ Tcl_SetResult(interp,
+ (ps_global->last_error[0])
+ ? ps_global->last_error
+ : (ps_global->c_client_error[0])
+ ? ps_global->c_client_error
+ : "Unable to delete folder",
+ TCL_VOLATILE);
+ }
+
+ reset_context_folders(cp);
+ return(TCL_ERROR);
+ }
+
+
+ Tcl_SetResult(interp, "OK", TCL_STATIC);
+ reset_context_folders(cp);
+ return(TCL_OK);
+ }
+ /*
+ * must be at least 5 arguments for the next set of commands
+ */
+ else if(objc < 5) {
+ Tcl_SetResult(interp, "PEFolder: not enough arguments", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ else if(!strucmp(op, "rename")){
+ char *folder,*newfolder, *aes;
+
+ folder = Tcl_GetStringFromObj(objv[objc - 2], NULL);
+ if(!folder) {
+ Tcl_SetResult(interp, "PEFolder rename: Can't read folder", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ newfolder = Tcl_GetStringFromObj(objv[objc - 1], NULL);
+ if(!newfolder) {
+ Tcl_SetResult(interp, "PEFolder rename: Can't read folder", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ if(PEFolderChange(interp, cp, objc - 5, objv + 3) == TCL_ERROR)
+ return TCL_ERROR;
+
+ ps_global->c_client_error[0] = ps_global->last_error[0] = '\0';
+ pePrepareForAuthException();
+
+ if(!context_rename(cp, NULL, folder, newfolder)){
+ if((aes = peAuthException()) != NULL){
+ Tcl_SetResult(interp, aes, TCL_VOLATILE);
+ }
+ else{
+ Tcl_SetResult(interp,
+ (ps_global->last_error[0])
+ ? ps_global->last_error
+ : (ps_global->c_client_error[0])
+ ? ps_global->c_client_error
+ : "Unable to rename folder",
+ TCL_VOLATILE);
+ }
+ reset_context_folders(cp);
+ return(TCL_ERROR);
+ }
+ Tcl_SetResult(interp, "OK", TCL_STATIC);
+ reset_context_folders(cp);
+ return(TCL_OK);
+ }
+ }
+ else
+ err = "PEFolder: Unrecognized collection ID";
+ }
+ }
+ else
+ err = "No User Context Established";
+ }
+
+ Tcl_SetResult(interp, err, TCL_VOLATILE);
+ return(TCL_ERROR);
+}
+
+
+/*
+ * PEMailboxCmd - export various bits of mailbox information
+ */
+int
+PEMailboxCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+ char *op, errbuf[256], *err = "Unknown PEMailbox operation";
+
+ dprint((5, "PEMailboxCmd"));
+
+ if(objc == 1){
+ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
+ }
+ else if((op = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){
+ if(!strucmp(op, "open")){
+ int i, colid;
+ char *folder;
+ CONTEXT_S *cp;
+
+ peED.uid = 0; /* forget cached embedded data */
+
+ /*
+ * CMD: open <context-index> <folder>
+ *
+ *
+ */
+ if(objc == 2){
+ Tcl_SetResult(interp, (!sp_dead_stream(ps_global->mail_stream)) ? "0" : "1", TCL_VOLATILE);
+ return(TCL_OK);
+ }
+
+ if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR){
+ if((folder = Tcl_GetStringFromObj(objv[objc - 1], NULL)) != NULL) {
+ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
+ if(i == colid) {
+ if(PEMakeFolderString(interp, cp, objc - 3, objv + 3,
+ &folder))
+ return TCL_ERROR;
+
+ dprint((1, "* PEMailbox open dir=%s folder=%s",cp->dir->ref,folder));
+
+ return(peCreateStream(interp, cp, folder, FALSE));
+ }
+
+ err = "open: Unrecognized collection ID";
+ }
+ else
+ err = "open: Can't read folder";
+ }
+ else
+ err = "open: Can't get collection ID";
+ }
+ else if(!strcmp(op, "indexformat")){
+ /*
+ * CMD: indexformat
+ *
+ * Returns: list of lists where:
+ * * the first element is the name of the
+ * field which may be "From", "Subject"
+ * "Date" or the emtpy string.
+ * * the second element which is either
+ * the percentage width or empty string
+ */
+ if(objc == 2)
+ return(peIndexFormat(interp));
+ }
+ else if(ps_global && ps_global->mail_stream){
+ if(!strcmp(op, "select")){
+ return(peSelect(interp, objc - 2, &((Tcl_Obj **) objv)[2], MN_SLCT));
+ }
+ else if(!strcmp(op, "search")){
+ return(peSelect(interp, objc - 2, &((Tcl_Obj **) objv)[2], MN_SRCH));
+ }
+ else if(!strucmp(op, "apply")){
+ return(peApply(interp, objc - 2, &((Tcl_Obj **) objv)[2]));
+ }
+ else if(!strcmp(op, "expunge")){
+ /*
+ * CMD: expunge
+ *
+ * Returns: OK after having removed deleted messages
+ */
+ char *streamstr = NULL;
+ MAILSTREAM *stream;
+ MSGNO_S *msgmap;
+
+ if(objc == 3) streamstr = Tcl_GetStringFromObj(objv[2], NULL);
+ if(!streamstr
+ || (streamstr && (strcmp(streamstr, "current") == 0))){
+ stream = ps_global->mail_stream;
+ msgmap = sp_msgmap(stream);
+ }
+ else if(streamstr && (strcmp(streamstr, "inbox") == 0)){
+ stream = sp_inbox_stream();
+ msgmap = sp_msgmap(stream);
+ }
+ else return(TCL_ERROR);
+ ps_global->last_error[0] = '\0';
+ if(IS_NEWS(stream)
+ && stream->rdonly){
+ msgno_exclude_deleted(stream, msgmap);
+ clear_index_cache(sp_inbox_stream(), 0);
+
+ /*
+ * This is kind of surprising at first. For most sort
+ * orders, if the whole set is sorted, then any subset
+ * is also sorted. Not so for OrderedSubject sort.
+ * If you exclude the first message of a subject group
+ * then you change the date that group is to be sorted on.
+ */
+ if(mn_get_sort(msgmap) == SortSubject2)
+ refresh_sort(ps_global->mail_stream, msgmap, FALSE);
+ }
+ else
+ (void) cmd_expunge_work(stream, msgmap);
+
+ Tcl_SetResult(interp, ps_global->last_error, TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "trashdeleted")){
+ /*
+ * CMD: trashdeleted
+ *
+ * Returns: OK after moving deleted messages to Trash and expunging
+ */
+ MAILSTREAM *stream;
+ MESSAGECACHE *mc;
+ CONTEXT_S *cp;
+ MSGNO_S *msgmap;
+ char *streamstr = NULL, tmp[MAILTMPLEN];
+ long n, tomove = 0L;
+
+ if(objc == 3) streamstr = Tcl_GetStringFromObj(objv[2], NULL);
+ if(!streamstr
+ || (streamstr && (strcmp(streamstr, "current") == 0))){
+ stream = ps_global->mail_stream;
+ msgmap = sp_msgmap(stream);
+ }
+ else if(streamstr && (strcmp(streamstr, "inbox") == 0)){
+ stream = sp_inbox_stream();
+ msgmap = sp_msgmap(stream);
+ }
+ else return(TCL_ERROR);
+
+ ps_global->last_error[0] = '\0';
+ if(IS_NEWS(stream) && stream->rdonly){
+ msgno_exclude_deleted(stream, msgmap);
+ clear_index_cache(sp_inbox_stream(), 0);
+
+ /*
+ * This is kind of surprising at first. For most sort
+ * orders, if the whole set is sorted, then any subset
+ * is also sorted. Not so for OrderedSubject sort.
+ * If you exclude the first message of a subject group
+ * then you change the date that group is to be sorted on.
+ */
+ if(mn_get_sort(msgmap) == SortSubject2)
+ refresh_sort(ps_global->mail_stream, msgmap, FALSE);
+ }
+ else{
+ if(!(cp = default_save_context(ps_global->context_list)))
+ cp = ps_global->context_list;
+
+ /* copy to trash if we're not in trash */
+ if(ps_global->VAR_TRASH_FOLDER
+ && ps_global->VAR_TRASH_FOLDER[0]
+ && context_allowed(context_apply(tmp, cp, ps_global->VAR_TRASH_FOLDER, sizeof(tmp)))
+ && !same_stream_and_mailbox(tmp, stream)){
+
+ /* save real selected set, and */
+ for(n = 1L; n <= mn_get_total(msgmap); n++){
+ set_lflag(stream, msgmap, n, MN_STMP, get_lflag(stream, msgmap, n, MN_SLCT));
+ /* select deleted */
+ if(!get_lflag(stream, msgmap, n, MN_EXLD)
+ && (mc = mail_elt(stream, mn_m2raw(msgmap,n))) != NULL && mc->deleted){
+ tomove++;
+ set_lflag(stream, msgmap, n, MN_SLCT, 1);
+ }
+ else
+ set_lflag(stream, msgmap, n, MN_SLCT, 0);
+ }
+
+ if(tomove && pseudo_selected(stream, msgmap)){
+
+ /* save delted to Trash */
+ n = save(ps_global, stream,
+ cp, ps_global->VAR_TRASH_FOLDER,
+ msgmap, SV_FOR_FILT | SV_FIX_DELS);
+
+ /* then remove them */
+ if(n == tomove){
+ (void) cmd_expunge_work(stream, msgmap);
+ }
+
+ restore_selected(msgmap);
+ }
+
+ /* restore selected set */
+ for(n = 1L; n <= mn_get_total(msgmap); n++)
+ set_lflag(stream, msgmap, n, MN_SLCT,
+ get_lflag(stream, msgmap, n, MN_STMP));
+ }
+ }
+
+ Tcl_SetResult(interp, ps_global->last_error, TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "nextvector")){
+ long msgno, count, countdown;
+ int i, aObjN = 0;
+ char *errstr = NULL, *s;
+ Tcl_Obj *rvObj, *vObj, *avObj, **aObj;
+
+ /*
+ * CMD: nextvector
+ *
+ * ARGS: msgno - message number "next" is relative to
+ * count - how many msgno slots to return
+ * attrib - (optional) attributes to be returned with each message in vector
+ *
+ * Returns: vector containing next <count> messagenumbers (and optional attributes)
+ */
+ if(objc == 4 || objc == 5){
+ if(Tcl_GetLongFromObj(interp, objv[2], &msgno) == TCL_OK){
+ if(Tcl_GetLongFromObj(interp, objv[3], &count) == TCL_OK){
+
+ /* set index range for efficiency */
+ if(msgno > 0L && msgno <= mn_get_total(sp_msgmap(ps_global->mail_stream))){
+ gPeITop = msgno;
+ gPeICount = count;
+ }
+
+ if(objc == 4 || Tcl_ListObjGetElements(interp, objv[4], &aObjN, &aObj) == TCL_OK){
+
+ if((rvObj = Tcl_NewListObj(0, NULL)) != NULL && count > 0
+ && !(msgno < 1L || msgno > mn_get_total(sp_msgmap(ps_global->mail_stream)))){
+ mn_set_cur(sp_msgmap(ps_global->mail_stream), msgno);
+
+ for(countdown = count; countdown > 0; countdown--){
+ imapuid_t uid = mail_uid(ps_global->mail_stream, mn_m2raw(sp_msgmap(ps_global->mail_stream), msgno));
+ int fetched = 0;
+
+ if((vObj = Tcl_NewListObj(0, NULL)) != NULL){
+ Tcl_ListObjAppendElement(interp, vObj, Tcl_NewLongObj(msgno));
+ peAppListF(interp, vObj, "%lu", uid);
+
+ if(aObjN){
+ if((avObj = Tcl_NewListObj(0, NULL)) != NULL){
+ for(i = 0; i < aObjN; i++){
+ if((s = Tcl_GetStringFromObj(aObj[i], NULL)) != NULL){
+ if(!strcmp(s, "statusbits")){
+ char *s = peMsgStatBitString(ps_global, ps_global->mail_stream,
+ sp_msgmap(ps_global->mail_stream), peMessageNumber(uid),
+ gPeITop, gPeICount, &fetched);
+ Tcl_ListObjAppendElement(interp, avObj, Tcl_NewStringObj(s, -1));
+ }
+ else if(!strcmp(s, "statuslist")){
+ Tcl_Obj *nObj = peMsgStatNameList(interp, ps_global, ps_global->mail_stream,
+ sp_msgmap(ps_global->mail_stream), peMessageNumber(uid),
+ gPeITop, gPeICount, &fetched);
+ Tcl_ListObjAppendElement(interp, avObj, nObj);
+ }
+ else if(!strcmp(s, "status")){
+ long raw;
+ char stat[3];
+ MESSAGECACHE *mc;
+
+ raw = peSequenceNumber(uid);
+
+ if(!((mc = mail_elt(ps_global->mail_stream, raw)) && mc->valid)){
+ mail_fetch_flags(ps_global->mail_stream,
+ ulong2string(uid), FT_UID);
+ mc = mail_elt(ps_global->mail_stream, raw);
+ }
+
+ stat[0] = mc->deleted ? '1' : '0';
+ stat[1] = mc->recent ? '1' : '0';
+ stat[2] = mc->seen ? '1' : '0';
+
+ Tcl_ListObjAppendElement(interp, avObj, Tcl_NewStringObj(stat,3));
+ }
+ else if(!strcmp(s, "indexparts")){
+ Tcl_Obj *iObj;
+
+ if((iObj = Tcl_NewListObj(0, NULL)) != NULL
+ && peAppendIndexParts(interp, uid, iObj, &fetched) == TCL_OK
+ && Tcl_ListObjAppendElement(interp, avObj, iObj) == TCL_OK){
+ }
+ else
+ return(TCL_ERROR);
+ }
+ else if(!strucmp(s, "indexcolor")){
+ if(peAppendIndexColor(interp, uid, avObj, &fetched) != TCL_OK)
+ return(TCL_ERROR);
+ }
+ }
+ else{
+ errstr = "nextvector: can't read attributes";
+ break;
+ }
+ }
+
+ Tcl_ListObjAppendElement(interp, vObj, avObj);
+ }
+ else{
+ errstr = "nextvector: can't allocate attribute return vector";
+ break;
+ }
+ }
+ }
+ else{
+ errstr = "nextvector: can't allocate new vector";
+ break;
+ }
+
+ Tcl_ListObjAppendElement(interp, rvObj, vObj);
+
+ for(++msgno; msgno <= mn_get_total(sp_msgmap(ps_global->mail_stream)) && msgline_hidden(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), msgno, MN_NONE); msgno++)
+ ;
+
+ if(msgno > mn_get_total(sp_msgmap(ps_global->mail_stream)))
+ break;
+ }
+ }
+
+ if(!errstr){
+ /* append result vector */
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), rvObj);
+ /* Everything is coerced to UTF-8 */
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj("UTF-8", -1));
+ return(TCL_OK);
+ }
+ }
+ else
+ errstr = "nextvector: can't read attribute list";
+ }
+ else
+ errstr = "nextvector: can't read count";
+ }
+ else
+ errstr = "nextvector: can't read message number";
+ }
+ else
+ errstr = "nextvector: Incorrect number of arguments";
+
+ if(errstr)
+ Tcl_SetResult(interp, errstr, TCL_STATIC);
+
+ return(TCL_ERROR);
+ }
+ else if(objc == 2){
+ if(!strcmp(op, "messagecount")){
+ /*
+ * CMD: messagecount
+ *
+ * Returns: count of messsages in open mailbox
+ */
+ Tcl_SetResult(interp,
+ long2string(mn_get_total(sp_msgmap(ps_global->mail_stream))),
+ TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "firstinteresting")){
+ /*
+ * CMD: firstinteresting
+ *
+ * Returns: message number associated with
+ * "incoming-startup-rule" which had better
+ * be the "current" message since it was set
+ * in do_broach_folder and shouldn't have been
+ * changed otherwise (expunged screw us?)
+ */
+ Tcl_SetResult(interp,
+ long2string(mn_get_cur(sp_msgmap(ps_global->mail_stream))),
+ TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "selected")){
+ /*
+ * CMD: selected
+ *
+ * Returns: count of selected messsages in open mailbox
+ */
+
+ Tcl_SetResult(interp,
+ long2string(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_SLCT)),
+ TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "searched")){
+ /*
+ * CMD: searched
+ *
+ * Returns: count of searched messsages in open mailbox
+ */
+
+ Tcl_SetResult(interp,
+ long2string(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_SRCH)),
+ TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "mailboxname")){
+ /*
+ * CMD: name
+ *
+ * Returns: string representing the name of the
+ * current mailbox
+ */
+ Tcl_SetResult(interp, ps_global->cur_folder, TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "close")){
+ /*
+ * CMD: close
+ *
+ * Returns: with global mail_stream closed
+ */
+ peDestroyStream(ps_global);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "newmailreset")){
+ sml_seen();
+ zero_new_mail_count();
+ sp_set_mail_box_changed(ps_global->mail_stream, 0);
+ sp_set_expunge_count(ps_global->mail_stream, 0L);
+ peMarkInputTime();
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "newmailstatmsg")){
+ long newest, count;
+ char subject[500], subjtxt[500], from[500], intro[500], *s = "";
+
+ /*
+ * CMD: newmailstatmsg
+ *
+ * ARGS: none
+ *
+ * Returns: text for new mail message
+ *
+ */
+
+ if(sp_mail_box_changed(ps_global->mail_stream)
+ && (count = sp_mail_since_cmd(ps_global->mail_stream))){
+
+ for(newest = ps_global->mail_stream->nmsgs; newest > 1L; newest--)
+ if(!get_lflag(ps_global->mail_stream, NULL, newest, MN_EXLD))
+ break;
+
+ if(newest){
+ format_new_mail_msg(NULL, count,
+ pine_mail_fetchstructure(ps_global->mail_stream,
+ newest, NULL),
+ intro, from, subject, subjtxt, sizeof(subject));
+
+ snprintf(s = tmp_20k_buf, SIZEOF_20KBUF, "%s %s %s", intro, from, subjtxt);
+ }
+ }
+
+ Tcl_SetResult(interp, s, TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "savedefault")){
+ return(peSaveDefault(interp, 0L, 0, NULL));
+ }
+ else if(!strcmp(op, "gotodefault")){
+ return(peGotoDefault(interp, 0L, NULL));
+ }
+ else if(!strcmp(op, "zoom")){
+ Tcl_SetResult(interp,
+ long2string((any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE) > 0L)
+ ? any_lflagged(sp_msgmap(ps_global->mail_stream), MN_SLCT) : 0L),
+ TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "focus")){
+ Tcl_SetResult(interp,
+ long2string((any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE) > 0L)
+ ? any_lflagged(sp_msgmap(ps_global->mail_stream), MN_SRCH) : 0L),
+ TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "first")){
+ if(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE)){
+ long n;
+
+ for(n = 1L; n <= mn_get_total(sp_msgmap(ps_global->mail_stream)); n++)
+ if(!get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, MN_HIDE)){
+ Tcl_SetResult(interp, long2string(n), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+
+ unzoom_index(ps_global, ps_global->mail_stream, sp_msgmap(ps_global->mail_stream));
+
+ }
+
+ Tcl_SetResult(interp, int2string(1), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strucmp(op, "current")){
+ long n = 0;
+ unsigned long u = 0;
+
+ /*
+ * CMD: current
+ *
+ * ARGS:
+ *
+ * Returns: list of current msg {<sequence> <uid>}
+ */
+
+ if(mn_total_cur(sp_msgmap(ps_global->mail_stream)) <= 0
+ || ((n = mn_get_cur(sp_msgmap(ps_global->mail_stream))) > 0
+ && (u = mail_uid(ps_global->mail_stream, mn_m2raw(sp_msgmap(ps_global->mail_stream), n))) > 0)){
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(long2string(n), -1));
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(ulong2string(u), -1));
+ return(TCL_OK);
+ }
+ else
+ err = "Cannot get current";
+ }
+ else if(!strcmp(op, "last")){
+ if(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE)){
+ long n;
+
+ for(n = mn_get_total(sp_msgmap(ps_global->mail_stream)); n > 0L; n--)
+ if(!get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, MN_HIDE)){
+ Tcl_SetResult(interp, long2string(n), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ }
+ else{
+ Tcl_SetResult(interp, long2string(mn_get_total(sp_msgmap(ps_global->mail_stream))), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+
+ Tcl_SetResult(interp, "Can't set last message number", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ else if(!strucmp(op, "sortstyles")){
+ int i;
+ /*
+ * CMD: sortstyles
+ *
+ * Returns: list of supported sort styles
+ */
+
+ for(i = 0; ps_global->sort_types[i] != EndofList; i++)
+ if(Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(sort_name(ps_global->sort_types[i]), -1)) != TCL_OK)
+ return(TCL_ERROR);
+
+ return(TCL_OK);
+ }
+ else if(!strucmp(op, "sort")){
+ return(peAppendCurrentSort(interp));
+ }
+ else if(!strucmp(op, "state")){
+ if(!ps_global->mail_stream || sp_dead_stream(ps_global->mail_stream))
+ Tcl_SetResult(interp, "closed", TCL_STATIC);
+ else if(ps_global->mail_stream->rdonly && !IS_NEWS(ps_global->mail_stream))
+ Tcl_SetResult(interp, "readonly", TCL_STATIC);
+ else
+ Tcl_SetResult(interp, "ok", TCL_STATIC);
+
+ return(TCL_OK);
+ }
+ else if(!strucmp(op, "excludedeleted")){
+ msgno_exclude_deleted(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream));
+ return(TCL_OK);
+ }
+ }
+ else if(objc == 3){
+ if(!strcmp(op, "uid")){
+ long msgno, raw;
+
+ /*
+ * Return uid of given message number
+ *
+ * CMD: uid <msgnumber>
+ */
+
+ if(Tcl_GetLongFromObj(interp, objv[2], &msgno) != TCL_OK)
+ return(TCL_ERROR); /* conversion problem? */
+
+ if((raw = mn_m2raw(sp_msgmap(ps_global->mail_stream), msgno)) > 0L){
+ raw = mail_uid(ps_global->mail_stream, raw);
+ Tcl_SetResult(interp, long2string(raw), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Invalid UID for message %ld", msgno);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ else if(!strcmp(op, "newmail")){
+ int reload, force = UFU_NONE, rv;
+ time_t now = time(0);
+
+ /*
+ * CMD: newmail
+ *
+ * ARGS: reload -- "1" if we're reloading
+ * (vs. just checking newmail as a side effect
+ * of building a new page)
+ *
+ * Returns: count -
+ * mostrecent -
+ */
+ if(Tcl_GetIntFromObj(interp, objv[2], &reload) == TCL_ERROR)
+ reload = 0;
+
+ /* minimum 10 second between IMAP pings */
+ if(!time_of_last_input() || now - time_of_last_input() > 10){
+ force = UFU_FORCE;
+ peMarkInputTime();
+ }
+
+ /* check for new mail */
+ new_mail(force, reload ? GoodTime : VeryBadTime, NM_NONE);
+
+ rv = peNewMailResult(interp);
+
+ if(!reload) /* announced */
+ zero_new_mail_count();
+
+ return(rv);
+ }
+ else if(!strcmp(op, "flagcount")){
+ char *flag;
+ long count = 0L;
+ long flags = 0L;
+ int objlc;
+ Tcl_Obj **objlv;
+
+
+ /*
+ * CMD: flagcount
+ *
+ * ARGS: flags -
+ *
+ * Returns: count - number of message thusly flagged
+ * mostrecent -
+ */
+ if(Tcl_ListObjGetElements(interp, objv[2], &objlc, &objlv) == TCL_OK){
+ while(objlc--)
+ if((flag = Tcl_GetStringFromObj(*objlv++, NULL)) != NULL){
+ if(!strucmp(flag, "deleted")){
+ flags |= F_DEL;
+ }
+ if(!strucmp(flag, "undeleted")){
+ flags |= F_UNDEL;
+ }
+ else if(!strucmp(flag, "seen")){
+ flags |= F_SEEN;
+ }
+ else if(!strucmp(flag, "unseen")){
+ flags |= F_UNSEEN;
+ }
+ else if(!strucmp(flag, "flagged")){
+ flags |= F_FLAG;
+ }
+ else if(!strucmp(flag, "unflagged")){
+ flags |= F_UNFLAG;
+ }
+ else if(!strucmp(flag, "answered")){
+ flags |= F_ANS;
+ }
+ else if(!strucmp(flag, "unanswered")){
+ flags |= F_UNANS;
+ }
+ else if(!strucmp(flag, "recent")){
+ flags |= F_RECENT;
+ }
+ else if(!strucmp(flag, "unrecent")){
+ flags |= F_UNRECENT;
+ }
+ }
+
+ if(flags)
+ count = count_flagged(ps_global->mail_stream, flags);
+ }
+
+ Tcl_SetResult(interp, long2string(count), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "zoom")){
+ int newstate;
+ long n, zoomed = 0L;
+
+ /*
+ * CMD: zoom
+ *
+ * Set/clear HID bits of non SLCT messages as requested.
+ * PEMailbox [first | last | next] are senstive to these flags.
+ *
+ * ARGS: newstate - 1 or 0
+ *
+ * Returns: count of zoomed messages
+ */
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &newstate) != TCL_ERROR){
+ if(newstate > 0){
+ if(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE) != (mn_get_total(sp_msgmap(ps_global->mail_stream)) - (n = any_lflagged(sp_msgmap(ps_global->mail_stream), MN_SLCT)))){
+ zoom_index(ps_global, ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), MN_SLCT);
+ zoomed = n;
+ }
+ }
+ else{
+ if(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE))
+ unzoom_index(ps_global, ps_global->mail_stream, sp_msgmap(ps_global->mail_stream));
+ }
+ }
+
+ Tcl_SetResult(interp, long2string(zoomed), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "focus")){
+ int newstate;
+ long n, zoomed = 0L;
+
+ /*
+ * CMD: focus
+ *
+ * Set/clear HID bits of non MN_SRCH messages as requested.
+ * PEMailbox [first | last | next] are senstive to MN_HIDE flag
+ *
+ * ARGS: newstate - 1 or 0
+ *
+ * Returns: count of zoomed messages
+ */
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &newstate) != TCL_ERROR){
+ if(newstate > 0){
+ if(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE) != (mn_get_total(sp_msgmap(ps_global->mail_stream)) - (n = any_lflagged(sp_msgmap(ps_global->mail_stream), MN_SRCH))))
+ zoom_index(ps_global, ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), MN_SRCH);
+
+ zoomed = n;
+ }
+ else{
+ if(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE))
+ unzoom_index(ps_global, ps_global->mail_stream, sp_msgmap(ps_global->mail_stream));
+ }
+ }
+
+ Tcl_SetResult(interp, long2string(zoomed), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "next")){
+ long msgno;
+
+ /*
+ * CMD: next <msgno>
+ *
+ * ARGS: msgno - message number "next" is relative to
+ *
+ * Returns: previous state
+ */
+
+ if(Tcl_GetLongFromObj(interp, objv[2], &msgno) != TCL_ERROR){
+ mn_set_cur(sp_msgmap(ps_global->mail_stream), msgno);
+ mn_inc_cur(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), MH_NONE);
+ Tcl_SetResult(interp, long2string(mn_get_cur(sp_msgmap(ps_global->mail_stream))), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+
+ Tcl_SetResult(interp, "next can't read message number", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ }
+ else if(objc == 4){
+ if(!strucmp(op, "sort")){
+ int i, reversed = 0;
+ char *sort;
+
+ /*
+ * CMD: sort sortstyle reversed
+ *
+ * Returns: OK with the side-effect of message
+ * numbers now reflecting the requested
+ * sort order.
+ */
+
+ if((sort = Tcl_GetStringFromObj(objv[2], NULL))
+ && Tcl_GetIntFromObj(interp, objv[3], &reversed) != TCL_ERROR){
+ /* convert sort string into */
+ for(i = 0; ps_global->sort_types[i] != EndofList; i++)
+ if(strucmp(sort_name(ps_global->sort_types[i]), sort) == 0){
+ if(sp_unsorted_newmail(ps_global->mail_stream)
+ || !(ps_global->sort_types[i] == mn_get_sort(sp_msgmap(ps_global->mail_stream))
+ && mn_get_revsort(sp_msgmap(ps_global->mail_stream)) == reversed))
+ sort_folder(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream),
+ ps_global->sort_types[i],
+ reversed, 0);
+
+ break;
+ }
+ }
+
+ return(peAppendCurrentSort(interp));
+ }
+ else if(!strucmp(op, "selected")){
+ return(peSelected(interp, objc - 2, &((Tcl_Obj **) objv)[2], MN_SLCT));
+ }
+ else if(!strucmp(op, "searched")){
+ return(peSelected(interp, objc - 2, &((Tcl_Obj **) objv)[2], MN_SRCH));
+ }
+ else if(!strcmp(op, "next")){
+ long msgno, count;
+
+ /*
+ * CMD: next
+ *
+ * ARGS: msgno - message number "next" is relative to
+ * count - how many to increment it
+ *
+ * Returns: previous state
+ */
+
+ if(Tcl_GetLongFromObj(interp, objv[2], &msgno) != TCL_ERROR
+ && Tcl_GetLongFromObj(interp, objv[3], &count) != TCL_ERROR){
+ mn_set_cur(sp_msgmap(ps_global->mail_stream), msgno);
+ while(count)
+ if(count > 0){
+ mn_inc_cur(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), MH_NONE);
+ count--;
+ }
+ else{
+ mn_dec_cur(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), MH_NONE);
+ count++;
+ }
+
+ Tcl_SetResult(interp, long2string(mn_get_cur(sp_msgmap(ps_global->mail_stream))), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+
+ Tcl_SetResult(interp, "next can't read message number", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ else if(!strcmp(op, "x-nextvector")){
+ long msgno, count;
+
+ /*
+ * CMD: nextvector
+ *
+ * ARGS: msgno - message number "next" is relative to
+ * count - how many msgno slots to return
+ *
+ * Returns: vector containing next <count> messagenumbers
+ */
+
+ if(Tcl_GetLongFromObj(interp, objv[2], &msgno) != TCL_ERROR
+ && Tcl_GetLongFromObj(interp, objv[3], &count) != TCL_ERROR){
+ if(count > 0 && !(msgno < 1L || msgno > mn_get_total(sp_msgmap(ps_global->mail_stream)))){
+ mn_set_cur(sp_msgmap(ps_global->mail_stream), msgno);
+
+ while(count--){
+ long n = mn_get_cur(sp_msgmap(ps_global->mail_stream));
+
+ if(peAppListF(interp, Tcl_GetObjResult(interp),
+ "%l%l", n, mail_uid(ps_global->mail_stream,
+ mn_m2raw(sp_msgmap(ps_global->mail_stream), n))) != TCL_OK)
+ return(TCL_ERROR);
+
+ mn_inc_cur(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), MH_NONE);
+
+ if(n == mn_get_cur(sp_msgmap(ps_global->mail_stream)))
+ break;
+ }
+ }
+
+ return(TCL_OK);
+ }
+
+ Tcl_SetResult(interp, "next can't read message number", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ else if(!strcmp(op, "messagecount")){
+ char *relative;
+ long msgno, n, count = 0L;
+
+ /*
+ * CMD: messagecount
+ *
+ * ARGS: [before | after] relative to
+ * msgno
+ *
+ * Returns: count of messsages before or after given message number
+ */
+
+ if((relative = Tcl_GetStringFromObj(objv[2], NULL))
+ && Tcl_GetLongFromObj(interp, objv[3], &msgno) != TCL_ERROR){
+ if(msgno < 1L || msgno > mn_get_total(sp_msgmap(ps_global->mail_stream))){
+ Tcl_SetResult(interp, "relative msgno out of range", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ if(!strucmp(relative, "before")){
+ for(n = msgno - 1; n > 0L; n--)
+ if(!get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, MN_HIDE))
+ count++;
+
+ Tcl_SetResult(interp, long2string(count), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strucmp(relative, "after")){
+ for(n = msgno + 1; n <= mn_get_total(sp_msgmap(ps_global->mail_stream)); n++)
+ if(!get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, MN_HIDE))
+ count++;
+
+ Tcl_SetResult(interp, long2string(count), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ }
+
+ Tcl_SetResult(interp, "can't read range for count", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ else if(!strcmp(op, "selectvector")){
+ long msgno, count;
+
+ /*
+ * CMD: selectvector
+ *
+ * ARGS: msgno - message number "next" is relative to
+ * count - how many msgno slots to return
+ *
+ * Returns: vector containing next <count> messagenumbers
+ */
+
+ if(Tcl_GetLongFromObj(interp, objv[2], &msgno) != TCL_ERROR
+ && Tcl_GetLongFromObj(interp, objv[3], &count) != TCL_ERROR){
+ if(msgno > 0L){
+ mn_set_cur(sp_msgmap(ps_global->mail_stream), msgno);
+ while(count--){
+ msgno = mn_get_cur(sp_msgmap(ps_global->mail_stream));
+
+ if(get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), msgno, MN_SLCT))
+ if(Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewLongObj((long) mail_uid(ps_global->mail_stream, msgno))) != TCL_OK)
+ return(TCL_ERROR);
+
+ mn_inc_cur(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), MH_NONE);
+
+ if(msgno == mn_get_cur(sp_msgmap(ps_global->mail_stream)))
+ break;
+ }
+ }
+
+ return(TCL_OK);
+ }
+
+ Tcl_SetResult(interp, "selectvector: no message number", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ else if(!strucmp(op, "current")){
+ char *which;
+ long x, n = 0, u = 0;
+
+ /*
+ * CMD: current
+ *
+ * ARGS: (number|uid) <msgno>
+ *
+ * Returns: list of current msg {<sequence> <uid>}
+ */
+ if((which = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
+ if(Tcl_GetLongFromObj(interp, objv[3], &x) == TCL_OK){
+ if(!strucmp(which,"uid")){
+ u = x;
+ n = peMessageNumber(u);
+ }
+ else if(!strucmp(which,"number")){
+ n = x;
+ u = mail_uid(ps_global->mail_stream, mn_m2raw(sp_msgmap(ps_global->mail_stream), n));
+ }
+
+ if(n && u){
+ mn_set_cur(sp_msgmap(ps_global->mail_stream), n);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(long2string(n), -1));
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(long2string(u), -1));
+ return(TCL_OK);
+ }
+ else
+ err = "PEMailbox current: invalid number/uid";
+ }
+ else
+ err = "PEMailbox current: cannot get number";
+ }
+ else
+ err = "PEMailbox current: cannot get which";
+ }
+ }
+ else
+ err = "PEMailbox: Too many arguments";
+ }
+ else if(!strucmp(op, "name") || !strcmp(op, "close")){
+ Tcl_SetResult(interp, "", TCL_STATIC);
+ return(TCL_OK);
+ }
+ else
+ snprintf(err = errbuf, sizeof(errbuf), "%s: %s: No open mailbox",
+ Tcl_GetStringFromObj(objv[0], NULL), op);
+ }
+
+ Tcl_SetResult(interp, err, TCL_VOLATILE);
+ return(TCL_ERROR);
+}
+
+
+int
+peAppendCurrentSort(Tcl_Interp *interp)
+{
+ return((Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(sort_name(mn_get_sort(sp_msgmap(ps_global->mail_stream))), -1)) == TCL_OK
+ && Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(mn_get_revsort(sp_msgmap(ps_global->mail_stream)) ? "1" : "0", 1)) == TCL_OK)
+ ? TCL_OK : TCL_ERROR);
+}
+
+
+int
+peAppendDefaultSort(Tcl_Interp *interp)
+{
+ return((Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(sort_name(ps_global->def_sort), -1)) == TCL_OK
+ && Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(ps_global->def_sort_rev ? "1" : "0", 1)) == TCL_OK)
+ ? TCL_OK : TCL_ERROR);
+}
+
+
+int
+peSelect(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag)
+{
+ char *subcmd;
+ long n, i, diff, msgno;
+ int narrow, hidden;
+ MESSAGECACHE *mc;
+ extern MAILSTREAM *mm_search_stream;
+ extern long mm_search_count;
+
+ hidden = any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE) > 0L;
+ mm_search_stream = ps_global->mail_stream;
+ mm_search_count = 0L;
+
+ for(n = 1L; n <= ps_global->mail_stream->nmsgs; n++)
+ if((mc = mail_elt(ps_global->mail_stream, n)) != NULL){
+ mc->searched = 0;
+ mc->spare7 = 1;
+ }
+
+ /*
+ * CMD: select
+ *
+ * ARGS: subcmd subcmdargs
+ *
+ * Returns: flip "matchflag" private bit on all or none
+ * of the messages in the mailbox
+ */
+ if((subcmd = Tcl_GetStringFromObj(objv[0], NULL)) != NULL){
+ if(!strucmp(subcmd, "all")){
+ /*
+ * Args: <none>
+ */
+ if(matchflag & MN_SLCT){
+ if(objc != 1)
+ return(peSelectError(interp, subcmd));
+
+ agg_select_all(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), NULL, 1);
+ }
+ else if(matchflag & MN_SRCH){
+ for(n = 1L; n <= ps_global->mail_stream->nmsgs; n++)
+ set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, matchflag, 1);
+ }
+
+ Tcl_SetResult(interp, "All", TCL_VOLATILE);
+ }
+ else if(!strucmp(subcmd, "none")){
+ /*
+ * Args: <none>
+ */
+ n = 0L;
+
+ if(matchflag & MN_SLCT){
+ if(objc != 1)
+ return(peSelectError(interp, subcmd));
+
+ agg_select_all(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), &n, 0);
+ }
+ else if(matchflag & MN_SRCH){
+ for(n = 1L; n <= ps_global->mail_stream->nmsgs; n++)
+ set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, matchflag, 0);
+ }
+
+ Tcl_SetResult(interp, long2string(n), TCL_VOLATILE);
+ }
+ else if(!strucmp(subcmd, "searched")){
+ /*
+ * Args: <none>
+ */
+ for(n = 1L, i = 0; n <= ps_global->mail_stream->nmsgs; n++)
+ if(get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, MN_SRCH)){
+ i++;
+ set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, MN_SLCT, 1);
+ }
+
+ Tcl_SetResult(interp, long2string(i), TCL_VOLATILE);
+ }
+ else if(!strucmp(subcmd, "unsearched")){
+ /*
+ * Args: <none>
+ */
+ for(n = 1L, i = 0; n <= ps_global->mail_stream->nmsgs; n++)
+ if(get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, MN_SRCH)){
+ i++;
+ set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, MN_SLCT, 0);
+ }
+
+ Tcl_SetResult(interp, long2string(i), TCL_VOLATILE);
+ }
+ else{
+ if(!strucmp(subcmd, "narrow"))
+ narrow = 1;
+ else if(!strucmp(subcmd, "broad"))
+ narrow = 0;
+ else
+ return(peSelectError(interp, "invalid scope request"));
+
+ if(!(subcmd = Tcl_GetStringFromObj(objv[1], NULL)))
+ return(peSelectError(interp, "missing subcommand"));
+
+ if(!strucmp(subcmd, "num")){
+ if((i = peSelectNumber(interp, objc - 2, &objv[2], matchflag)) != TCL_OK)
+ return(i);
+ }
+ else if(!strucmp(subcmd, "date")){
+ if((i = peSelectDate(interp, objc - 2, &objv[2], matchflag)) != TCL_OK)
+ return(i);
+ }
+ else if(!strucmp(subcmd, "text")){
+ if((i = peSelectText(interp, objc - 2, &objv[2], matchflag)) != TCL_OK)
+ return(i);
+ }
+ else if(!strucmp(subcmd, "status")){
+ if((i = peSelectStatus(interp, objc - 2, &objv[2], matchflag)) != TCL_OK)
+ return(i);
+ }
+ else if(!strucmp(subcmd, "compound")){
+ char *s;
+ int nSearchList, nSearch;
+ Tcl_Obj **oSearchList, **oSearch;
+
+ /* BUG: should set up one SEARCHPGM to fit criteria and issue single search */
+
+ if(Tcl_ListObjGetElements(interp, objv[2], &nSearchList, &oSearchList) == TCL_OK){
+ for(i = 0; i < nSearchList; i++){
+ if(Tcl_ListObjGetElements(interp, oSearchList[i], &nSearch, &oSearch) == TCL_OK){
+ if((s = Tcl_GetStringFromObj(oSearch[0], NULL)) != NULL){
+ if(!strucmp(s,"date")){
+ if((n = peSelectDate(interp, nSearch - 1, &oSearch[1], matchflag)) != TCL_OK)
+ return(n);
+ }
+ else if(!strucmp(s,"text")){
+ if((n = peSelectText(interp, nSearch - 1, &oSearch[1], matchflag)) != TCL_OK)
+ return(n);
+ }
+ else if(!strucmp(s,"status")){
+ if((n = peSelectStatus(interp, nSearch - 1, &oSearch[1], matchflag)) != TCL_OK)
+ return(n);
+ }
+ else
+ return(peSelectError(interp, "unknown compound search"));
+
+ /* logical AND the results */
+ mm_search_count = 0L;
+ for(n = 1L; n <= ps_global->mail_stream->nmsgs; n++)
+ if((mc = mail_elt(ps_global->mail_stream, n)) != NULL){
+ if(mc->searched && mc->spare7)
+ mm_search_count++;
+ else
+ mc->searched = mc->spare7 = 0;
+ }
+ }
+ else
+ return(peSelectError(interp, "malformed compound search"));
+ }
+ else
+ return(peSelectError(interp, "malformed compound search"));
+ }
+ }
+ else
+ return(peSelectError(interp, "malformed compound search"));
+ }
+ else
+ return(peSelectError(interp, "cmd cmdargs"));
+
+ /*
+ * at this point all interesting messages should
+ * have searched bit lit
+ */
+
+ if(narrow) /* make sure something was selected */
+ for(i = 1L; i <= mn_get_total(sp_msgmap(ps_global->mail_stream)); i++)
+ if(mail_elt(ps_global->mail_stream,
+ mn_m2raw(sp_msgmap(ps_global->mail_stream), i))->searched){
+ if(get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, matchflag))
+ break;
+ else
+ mm_search_count--;
+ }
+
+ diff = 0L;
+ if(mm_search_count){
+ /*
+ * loop thru all the messages, adjusting local flag bits
+ * based on their "searched" bit...
+ */
+ for(i = 1L, msgno = 0L; i <= mn_get_total(sp_msgmap(ps_global->mail_stream)); i++)
+ if(narrow){
+ /* turning OFF selectedness if the "searched" bit isn't lit. */
+ if(get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, matchflag)){
+ if(!mail_elt(ps_global->mail_stream,
+ mn_m2raw(sp_msgmap(ps_global->mail_stream), i))->searched){
+ diff--;
+ set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, matchflag, 0);
+ if(hidden)
+ set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, MN_HIDE, 1);
+ }
+ else if(msgno < mn_get_cur(sp_msgmap(ps_global->mail_stream)))
+ msgno = i;
+ }
+ }
+ else if(mail_elt(ps_global->mail_stream,mn_m2raw(sp_msgmap(ps_global->mail_stream),i))->searched){
+ /* turn ON selectedness if "searched" bit is lit. */
+ if(!get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, matchflag)){
+ diff++;
+ set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, matchflag, 1);
+ if(hidden)
+ set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, MN_HIDE, 0);
+ }
+ }
+
+ /* if we're zoomed and the current message was unselected */
+ if(narrow && msgno
+ && get_lflag(ps_global->mail_stream,sp_msgmap(ps_global->mail_stream),mn_get_cur(sp_msgmap(ps_global->mail_stream)),MN_HIDE))
+ mn_reset_cur(sp_msgmap(ps_global->mail_stream), msgno);
+ }
+
+ Tcl_SetResult(interp, long2string(diff), TCL_VOLATILE);
+ }
+
+ return(TCL_OK);
+ }
+
+ Tcl_SetResult(interp, "Can't read select option", TCL_STATIC);
+ return(TCL_ERROR);
+}
+
+int
+peSelectNumber(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag)
+{
+ /*
+ * Args: [broad | narrow] firstnumber lastnumber
+ */
+
+ long first = 0L, last = 0L, n;
+
+ if(objc == 2){
+ if(Tcl_GetLongFromObj(interp, objv[0], &first) == TCL_OK
+ && Tcl_GetLongFromObj(interp, objv[1], &last) == TCL_OK){
+ if(last && last < first){
+ n = last;
+ last = first;
+ first = n;
+ }
+
+ if(first >= 1L && first <= mn_get_total(sp_msgmap(ps_global->mail_stream))){
+ if(last){
+ if(last >= 1L && last <= mn_get_total(sp_msgmap(ps_global->mail_stream))){
+ for(n = first; n <= last; n++)
+ mm_searched(ps_global->mail_stream,
+ mn_m2raw(sp_msgmap(ps_global->mail_stream), n));
+ }
+ else
+ return(peSelectError(interp, "last out of range"));
+ }
+ else{
+ mm_searched(ps_global->mail_stream,
+ mn_m2raw(sp_msgmap(ps_global->mail_stream), first));
+ }
+ }
+ else
+ return(peSelectError(interp, "first out of range"));
+ }
+ else
+ return(peSelectError(interp, "can't read first/last"));
+ }
+ else
+ return(peSelectError(interp, "num first last"));
+
+ return(TCL_OK);
+}
+
+int
+peSelectDate(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag)
+{
+ /*
+ * Args: [broad | narrow]
+ * tense - "on", "since", "before"
+ * year - 4 digit year
+ * month - abbreviated month "jan", "feb"...
+ * day - day number
+ */
+
+ char *tense, *year, *month, *day, buf[256];
+
+ if(objc == 4){
+ if((tense = peSelValTense(objv[0])) != NULL){
+ if((year = peSelValYear(objv[1])) != NULL){
+ if((month = peSelValMonth(objv[2])) != NULL){
+ if((day = peSelValDay(objv[3])) != NULL){
+ snprintf(buf, sizeof(buf), "%s %s-%s-%s", tense, day, month, year);
+ pine_mail_search_full(ps_global->mail_stream, NULL,
+ mail_criteria(buf),
+ SE_NOPREFETCH | SE_FREE);
+ }
+ else
+ return(peSelectError(interp, "<with valid day>"));
+ }
+ else
+ return(peSelectError(interp, "<with valid month>"));
+ }
+ else
+ return(peSelectError(interp, "<with valid year>"));
+ }
+ else
+ return(peSelectError(interp, "<with valid tense>"));
+ }
+ else
+ return(peSelectError(interp, "date tense year monthabbrev daynum"));
+
+ return(TCL_OK);
+}
+
+int
+peSelectText(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag)
+{
+ /*
+ * Args: [broad | narrow]
+ * case - in not
+ * field - to from cc recip partic subj any
+ * text - free text search string
+ */
+ int not;
+ char field, *text;
+
+ if(objc == 3){
+ if((not = peSelValCase(objv[0])) >= 0){
+ if((field = peSelValField(objv[1])) != '\0'){
+ if((text = Tcl_GetStringFromObj(objv[2], NULL))
+ && strlen(text) < 1024){
+ /* BUG: fix charset not to be NULL below */
+ if(agg_text_select(ps_global->mail_stream,
+ sp_msgmap(ps_global->mail_stream),
+ field, NULL, not, 0, text, NULL, NULL))
+ /* BUG: plug in "charset" above? */
+ return(peSelectError(interp, "programmer botch"));
+ }
+ else
+ return(peSelectError(interp, "<with search string < 1024>"));
+ }
+ else
+ return(peSelectError(interp, "<with valid field>"));
+ }
+ else
+ return(peSelectError(interp, "<with valid case>"));
+ }
+ else
+ return(peSelectError(interp, "text case field text"));
+
+ return(TCL_OK);
+}
+
+int
+peSelectStatus(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag)
+{
+ /*
+ * Args: [broad | narrow]
+ * case - on not
+ * status - imp new ans del
+ */
+ int not;
+ char flag;
+
+ if(objc == 2){
+ if((not = peSelValCase(objv[0])) >= 0){
+ if((flag = peSelValFlag(objv[1])) != '\0'){
+ if(agg_flag_select(ps_global->mail_stream, not, flag, NULL))
+ return(peSelectError(interp, "programmer botch"));
+ }
+ else
+ return(peSelectError(interp, "<with valid flag>"));
+ }
+ else
+ return(peSelectError(interp, "<with valid case>"));
+ }
+ else
+ return(peSelectError(interp, "status focus case flag"));
+
+ return(TCL_OK);
+}
+
+char *
+peSelValTense(Tcl_Obj *objp)
+{
+ char *tense, **pp;
+
+ if((tense = Tcl_GetStringFromObj(objp, NULL)) != NULL){
+ static char *tenses[] = {"on", "since", "before", NULL};
+
+ for(pp = tenses; *pp; pp++)
+ if(!strucmp(*pp, tense))
+ return(tense);
+ }
+
+ return(NULL);
+}
+
+
+char *
+peSelValYear(Tcl_Obj *objp)
+{
+ char *year;
+
+ return((year = Tcl_GetStringFromObj(objp, NULL))
+ && strlen(year) == 4
+ && isdigit((unsigned char) year[0])
+ && isdigit((unsigned char) year[0])
+ && isdigit((unsigned char) year[0])
+ ? year
+ : NULL);
+}
+
+
+char *
+peSelValMonth(Tcl_Obj *objp)
+{
+ char *month, **pp;
+ static char *mons[] = {"jan","feb","mar","apr",
+ "may","jun","jul","aug",
+ "sep","oct","nov","dec", NULL};
+
+ if((month = Tcl_GetStringFromObj(objp, NULL)) && strlen(month) == 3)
+ for(pp = mons; *pp; pp++)
+ if(!strucmp(month, *pp))
+ return(*pp);
+
+ return(NULL);
+}
+
+
+char *
+peSelValDay(Tcl_Obj *objp)
+{
+ char *day;
+
+ return(((day = Tcl_GetStringFromObj(objp, NULL))
+ && (day[0] == '0' || day[0] == '1'
+ || day[0] == '2' || day[0] == '3')
+ && isdigit((unsigned char) day[1])
+ && day[2] == '\0')
+ ? day
+ : NULL);
+}
+
+
+int
+peSelValCase(Tcl_Obj *objp)
+{
+ char *not;
+
+ if((not = Tcl_GetStringFromObj(objp, NULL)) != NULL){
+ if(!strucmp(not, "ton"))
+ return(0);
+ else if(!strucmp(not, "not"))
+ return(1);
+ }
+
+ return(-1);
+}
+
+
+int
+peSelValField(Tcl_Obj *objp)
+{
+ char *field;
+ int i;
+ static struct {
+ char *field;
+ int type;
+ } fields[] = {{"from", 'f'},
+ {"to", 't'},
+ {"cc", 'c'},
+ {"subj", 's'},
+ {"any", 'a'},
+ {"recip", 'r'},
+ {"partic", 'p'},
+ {"body", 'b'},
+ {NULL,0}};
+
+ if((field = Tcl_GetStringFromObj(objp, NULL)) != NULL)
+ for(i = 0; fields[i].field ; i++)
+ if(!strucmp(fields[i].field, field))
+ return(fields[i].type);
+
+ return(0);
+}
+
+
+int
+peSelValFlag(Tcl_Obj *objp)
+{
+ char *flag;
+ int i;
+ static struct {
+ char *flag;
+ int type;
+ } flags[] = {{"imp", '*'},
+ {"new", 'n'},
+ {"ans", 'a'},
+ {"del", 'd'},
+ {NULL,0}};
+
+ if((flag = Tcl_GetStringFromObj(objp, NULL)) != NULL)
+ for(i = 0; flags[i].flag ; i++)
+ if(!strucmp(flags[i].flag, flag))
+ return(flags[i].type);
+
+ return(0);
+}
+
+int
+peSelected(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag)
+{
+ int rv = 0;
+ long i, n;
+ char *range;
+
+ /*
+ * CMD: searched [before | after] #
+ *
+ * Returns: 1 if criteria is true, 0 otherwise
+ */
+
+ if((range = Tcl_GetStringFromObj(objv[0], NULL))
+ && Tcl_GetLongFromObj(interp, objv[1], &n) != TCL_ERROR){
+ if(!strucmp(range, "before")){
+ for(i = 1L; i < n && i <= mn_get_total(sp_msgmap(ps_global->mail_stream)); i++)
+ if(get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, matchflag)){
+ rv = 1;
+ break;
+ }
+
+ Tcl_SetResult(interp, int2string(rv), TCL_STATIC);
+ return(TCL_OK);
+ }
+ else if(!strucmp(range, "after")){
+ for(i = n + 1L; i <= mn_get_total(sp_msgmap(ps_global->mail_stream)); i++)
+ if(get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, matchflag)){
+ rv = 1;
+ break;
+ }
+
+ Tcl_SetResult(interp, int2string(rv), TCL_STATIC);
+ return(TCL_OK);
+ }
+ }
+
+ Tcl_SetResult(interp, "searched test failed", TCL_STATIC);
+ return(TCL_ERROR);
+}
+
+
+int
+peSelectError(Tcl_Interp *interp, char *usage)
+{
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), "should be select %.128s", usage);
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+}
+
+
+int
+peApply(Tcl_Interp *interp, int objc, Tcl_Obj **objv)
+{
+ char *subcmd;
+ long n;
+
+ if(!(n = any_lflagged(sp_msgmap(ps_global->mail_stream), MN_SLCT))){
+ Tcl_SetResult(interp, "No messages selected", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ else if((subcmd = Tcl_GetStringFromObj(objv[0], NULL)) != NULL){
+ if(objc == 1){
+ if(!strucmp(subcmd, "delete")){
+ /* BUG: is CmdWhere arg always right? */
+ (void) cmd_delete(ps_global, sp_msgmap(ps_global->mail_stream), MCMD_AGG | MCMD_SILENT, NULL);
+ Tcl_SetResult(interp, long2string(n), TCL_STATIC);
+ return(TCL_OK);
+ }
+ else if(!strucmp(subcmd, "undelete")){
+ (void) cmd_undelete(ps_global, sp_msgmap(ps_global->mail_stream), MCMD_AGG | MCMD_SILENT);
+ Tcl_SetResult(interp, long2string(n), TCL_STATIC);
+ return(TCL_OK);
+ }
+ }
+ else if(objc == 2){
+ if(!strucmp(subcmd, "count")){
+ /*
+ * Args: flag
+ */
+ char *flagname;
+ long n, rawno, count = 0;
+
+ if((flagname = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){
+ for(n = 1L; n <= mn_get_total(sp_msgmap(ps_global->mail_stream)); n++){
+ rawno = mn_m2raw(sp_msgmap(ps_global->mail_stream), n);
+ if(get_lflag(ps_global->mail_stream, NULL, rawno, MN_SLCT)
+ && peIsFlagged(ps_global->mail_stream,
+ mail_uid(ps_global->mail_stream, rawno),
+ flagname)){
+ count++;
+ }
+ }
+ }
+
+ Tcl_SetResult(interp, long2string(count), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ }
+ else if(objc == 3){
+ if(!strucmp(subcmd, "flag")){
+ /*
+ * Args: case - on not
+ * flag - imp new ans del
+ */
+ char flag, *result;
+ int not;
+ long flagged;
+
+ if((not = peSelValCase(objv[1])) >= 0){
+ if((flag = peSelValFlag(objv[2])) != '\0'){
+ result = peApplyFlag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), flag, not, &flagged);
+ if(!result){
+ Tcl_SetResult(interp, int2string(flagged), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else
+ return(peApplyError(interp, result));
+ }
+ else
+ return(peApplyError(interp, "invalid flag"));
+ }
+ else
+ return(peApplyError(interp, "invalid case"));
+ }
+ else if(!strucmp(subcmd, "save")){
+ /*
+ * Args: colid -
+ * folder - imp new ans del
+ */
+
+ int colid, flgs = 0, i;
+ char *folder, *err;
+ CONTEXT_S *cp;
+
+ if(Tcl_GetIntFromObj(interp, objv[1], &colid) != TCL_ERROR){
+
+ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
+ if(i == colid){
+ if((folder = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
+ if(pseudo_selected(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream))){
+
+ if(!READONLY_FOLDER(ps_global->mail_stream)
+ && F_OFF(F_SAVE_WONT_DELETE, ps_global))
+ flgs |= SV_DELETE;
+
+ if(colid == 0 && !strucmp(folder, "inbox"))
+ flgs |= SV_INBOXWOCNTXT;
+
+ i = save(ps_global, ps_global->mail_stream,
+ cp, folder, sp_msgmap(ps_global->mail_stream), flgs);
+
+ err = (i == mn_total_cur(sp_msgmap(ps_global->mail_stream))) ? NULL : "problem saving";
+
+ restore_selected(sp_msgmap(ps_global->mail_stream));
+ if(err)
+ return(peApplyError(interp, err));
+
+ Tcl_SetResult(interp, long2string(i), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else
+ return(peApplyError(interp, "can't select"));
+ }
+ else
+ return(peApplyError(interp, "no folder name"));
+ }
+
+ return(peApplyError(interp, "bad colid"));
+ }
+ else
+ return(peApplyError(interp, "invalid case"));
+ }
+ else if(!strucmp(subcmd, "copy")){
+ /*
+ * Args: colid -
+ * folder - imp new ans del
+ */
+
+ int colid, flgs = 0, i;
+ char *folder, *err;
+ CONTEXT_S *cp;
+
+ if(Tcl_GetIntFromObj(interp, objv[1], &colid) != TCL_ERROR){
+
+ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
+ if(i == colid){
+ if((folder = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
+ if(pseudo_selected(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream))){
+
+ if(colid == 0 && !strucmp(folder, "inbox"))
+ flgs |= SV_INBOXWOCNTXT;
+
+ i = save(ps_global, ps_global->mail_stream,
+ cp, folder, sp_msgmap(ps_global->mail_stream), flgs);
+
+ err = (i == mn_total_cur(sp_msgmap(ps_global->mail_stream))) ? NULL : "problem copying";
+
+ restore_selected(sp_msgmap(ps_global->mail_stream));
+ if(err)
+ return(peApplyError(interp, err));
+
+ Tcl_SetResult(interp, long2string(i), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else
+ return(peApplyError(interp, "can't select"));
+ }
+ else
+ return(peApplyError(interp, "no folder name"));
+ }
+
+ return(peApplyError(interp, "bad colid"));
+ }
+ else
+ return(peApplyError(interp, "invalid case"));
+ }
+ else if(!strucmp(subcmd, "move")){
+ /*
+ * Args: colid -
+ * folder - imp new ans del
+ */
+
+ int colid, flgs = 0, i;
+ char *folder, *err;
+ CONTEXT_S *cp;
+
+ if(Tcl_GetIntFromObj(interp, objv[1], &colid) != TCL_ERROR){
+
+ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
+ if(i == colid){
+ if((folder = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
+ if(pseudo_selected(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream))){
+
+ flgs = SV_DELETE;
+
+ if(colid == 0 && !strucmp(folder, "inbox"))
+ flgs |= SV_INBOXWOCNTXT;
+
+ i = save(ps_global, ps_global->mail_stream,
+ cp, folder, sp_msgmap(ps_global->mail_stream), flgs);
+
+ err = (i == mn_total_cur(sp_msgmap(ps_global->mail_stream))) ? NULL : "problem moving";
+
+ restore_selected(sp_msgmap(ps_global->mail_stream));
+ if(err)
+ return(peApplyError(interp, err));
+
+ Tcl_SetResult(interp, long2string(i), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else
+ return(peApplyError(interp, "can't select"));
+ }
+ else
+ return(peApplyError(interp, "no folder name"));
+ }
+
+ return(peApplyError(interp, "bad colid"));
+ }
+ else
+ return(peApplyError(interp, "invalid case"));
+ }
+ else if(!strucmp(subcmd, "spam")){
+ /*
+ * Args: spamaddr -
+ * spamsubj -
+ */
+ char *spamaddr, *spamsubj = NULL;
+ long n, rawno;
+
+ if((spamaddr = Tcl_GetStringFromObj(objv[1], NULL))
+ && (spamsubj = Tcl_GetStringFromObj(objv[2], NULL))){
+ for(n = 1L; n <= mn_get_total(sp_msgmap(ps_global->mail_stream)); n++){
+ rawno = mn_m2raw(sp_msgmap(ps_global->mail_stream), n);
+ if(get_lflag(ps_global->mail_stream, NULL, rawno, MN_SLCT)){
+ char errbuf[WP_MAX_POST_ERROR + 1], *rs = NULL;
+
+ if((rs = peSendSpamReport(rawno, spamaddr, spamsubj, errbuf)) != NULL){
+ Tcl_SetResult(interp, rs, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ }
+ }
+ }
+
+ Tcl_SetResult(interp, "OK", TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ }
+ }
+
+ return(peApplyError(interp, "unknown option"));
+}
+
+
+char *
+peApplyFlag(MAILSTREAM *stream, MSGNO_S *msgmap, char flag, int not, long *flagged)
+{
+ char *seq, *flagstr;
+ long flags, flagid;
+
+ switch (flag) {
+ case '*' :
+ flagstr = "\\FLAGGED";
+ flags = not ? 0L : ST_SET;
+ flagid = not ? F_FLAG : F_UNFLAG;
+ break;
+ case 'n' :
+ flagstr = "\\SEEN";
+ flags = not ? ST_SET : 0L;
+ flagid = not ? F_UNSEEN : F_SEEN;
+ break;
+ case 'a' :
+ flagstr = "\\ANSWERED";
+ flags = not ? 0L : ST_SET;
+ flagid = not ? F_ANS : F_UNANS;
+ break;
+ case 'd':
+ flagstr = "\\DELETED";
+ flags = not ? 0L : ST_SET;
+ flagid = not ? F_DEL : F_UNDEL;
+ break;
+ default :
+ return("unknown flag");
+ break;
+ }
+
+ if(pseudo_selected(stream, msgmap)){
+ if((seq = currentf_sequence(stream, msgmap, flagid, flagged, 1, NULL, NULL)) != NULL){
+ mail_flag(stream, seq, flagstr, flags);
+ fs_give((void **) &seq);
+ }
+
+ restore_selected(msgmap);
+ return(NULL);
+ }
+ else
+ return("can't select");
+}
+
+
+int
+peApplyError(Tcl_Interp *interp, char *usage)
+{
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), "apply error: %.128s", usage);
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+}
+
+
+/*
+ * peIndexFormat - Return with interp's result object set to
+ * represent the index line's format as a list of
+ * index-field-name, percentage-width pairs
+ */
+int
+peIndexFormat(Tcl_Interp *interp)
+{
+ INDEX_COL_S *cdesc = NULL;
+ char *name, wbuf[4], *dname;
+
+ for(cdesc = ps_global->index_disp_format;
+ cdesc->ctype != iNothing;
+ cdesc++) {
+ dname = NULL;
+ switch(cdesc->ctype){
+ case iFStatus:
+ case iIStatus:
+ case iSIStatus:
+ dname = "iStatus";
+ case iStatus:
+ name = "Status";
+ break;
+
+ case iMessNo:
+ name = "Number";
+ break;
+
+ case iPrio:
+ case iPrioAlpha:
+ case iPrioBang:
+ name = "Priority";
+ break;
+
+ case iDate: case iSDate: case iSTime: case iLDate:
+ case iS1Date: case iS2Date: case iS3Date: case iS4Date: case iDateIso:
+ case iDateIsoS:
+ case iSDateIso: case iSDateIsoS:
+ case iSDateS1: case iSDateS2:
+ case iSDateS3: case iSDateS4:
+ case iSDateTime:
+ case iSDateTimeIso: case iSDateTimeIsoS:
+ case iSDateTimeS1: case iSDateTimeS2:
+ case iSDateTimeS3: case iSDateTimeS4:
+ case iSDateTime24:
+ case iSDateTimeIso24: case iSDateTimeIsoS24:
+ case iSDateTimeS124: case iSDateTimeS224:
+ case iSDateTimeS324: case iSDateTimeS424:
+ case iCurDate: case iCurDateIso: case iCurDateIsoS:
+ case iCurTime24: case iCurTime12:
+ case iCurPrefDate:
+ name = "Date";
+ break;
+
+ case iCurDay: case iCurDay2Digit:
+ case iCurDayOfWeek: case iCurDayOfWeekAbb:
+ name = "Day";
+ break;
+
+ case iCurMon: case iCurMon2Digit:
+ case iCurMonLong: case iCurMonAbb:
+ name= "Month";
+ break;
+
+ case iTime24: case iTime12: case iTimezone:
+ case iCurPrefTime:
+ name = "Time";
+ break;
+
+ case iDay2Digit: case iDayOfWeek: case iDayOfWeekAbb:
+ name = "Day";
+ break;
+
+ case iMonAbb: case iMon2Digit:
+ name = "Month";
+ break;
+
+ case iYear: case iYear2Digit:
+ case iCurYear: case iCurYear2Digit:
+ name = "Year";
+ break;
+
+ case iScore :
+ name = "Score";
+ break;
+
+ case iFromTo:
+ case iFromToNotNews:
+ case iFrom:
+ name = "From";
+ break;
+
+ case iTo:
+ case iToAndNews :
+ name = "To";
+ break;
+
+ case iCc:
+ name = "Cc";
+ break;
+
+ case iRecips:
+ name = "Recipients";
+ break;
+
+ case iSender:
+ name = "Sender";
+ break;
+
+ case iSize :
+ case iSizeComma :
+ case iSizeNarrow :
+ case iDescripSize:
+ case iKSize :
+ name = "Size";
+ break;
+
+ case iAtt:
+ name = "Attachments";
+ break;
+
+ case iAddress :
+ name = "Address";
+ break;
+
+ case iMailbox :
+ name = "Mailbox";
+ break;
+
+ case iSubject :
+ case iSubjKey :
+ case iSubjKeyInit :
+ case iSubjectText :
+ case iSubjKeyText :
+ case iSubjKeyInitText :
+ name = "Subject";
+ break;
+
+ case iNews:
+ case iNewsAndTo :
+ name = "News";
+ break;
+
+ case iNewsAndRecips:
+ name = "News/Recip";
+ break;
+
+ case iRecipsAndNews:
+ name = "Recip/News";
+ break;
+
+ default :
+ name = "";
+ break;
+ }
+
+ if(cdesc->width > 0){
+ int p = ((cdesc->width * 100) / FAKE_SCREEN_WIDTH);
+
+ snprintf(wbuf, sizeof(wbuf), "%d%%", p);
+ }
+ else
+ wbuf[0] = '\0';
+
+ if(peAppListF(interp, Tcl_GetObjResult(interp), "%s%s%s", name, wbuf, dname) != TCL_OK)
+ return(TCL_ERROR);
+ }
+
+ return(TCL_OK);
+}
+
+
+int
+peNewMailResult(Tcl_Interp *interp)
+{
+ unsigned long n, uid;
+
+ if(sp_mail_box_changed(ps_global->mail_stream)){
+ if((n = sp_mail_since_cmd(ps_global->mail_stream)) != 0L){
+ /* first element is count of new messages */
+ if(Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewLongObj(n)) != TCL_OK)
+ return(TCL_ERROR);
+
+ /* second element is UID of most recent message */
+ for(uid = ps_global->mail_stream->nmsgs; uid > 1L; uid--)
+ if(!get_lflag(ps_global->mail_stream, NULL, uid, MN_EXLD))
+ break;
+
+ if(!uid){
+ Tcl_ResetResult(interp);
+ Tcl_SetResult(interp, "0 0 0", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ uid = mail_uid(ps_global->mail_stream, uid);
+
+ if(Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewLongObj(uid)) != TCL_OK)
+ return(TCL_ERROR);
+ }
+ else {
+ if(Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewIntObj(0)) != TCL_OK)
+ return(TCL_ERROR);
+
+ /* zero is UID of new message */
+ if(Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewIntObj(0)) != TCL_OK)
+ return(TCL_ERROR);
+ }
+
+ /* third element is expunge count */
+ if(Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewLongObj(sp_expunge_count(ps_global->mail_stream)
+ ? sp_expunge_count(ps_global->mail_stream)
+ : 0L)) != TCL_OK)
+ return(TCL_ERROR);
+
+ }
+ else
+ Tcl_SetResult(interp, "0 0 0", TCL_STATIC);
+
+ return(TCL_OK);
+}
+
+
+/* * * * * * * * Start of Per-Thread/SubThread access functions * * * * * * * */
+
+
+/*
+ * PEThreadCmd - access/manipulate various pieces of thread state
+ */
+int
+PEThreadCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+ char *err, errbuf[256], *cmd, *op;
+ imapuid_t uid;
+
+ dprint((2, "PEThreadCmd"));
+
+ snprintf(err = errbuf, sizeof(errbuf), "Unknown %s request",
+ Tcl_GetStringFromObj(objv[0], NULL));
+
+ if(!(ps_global && ps_global->mail_stream)){
+ snprintf(err = errbuf, sizeof(errbuf), "%s: No open mailbox",
+ Tcl_GetStringFromObj(objv[0], NULL));
+ }
+ else if(objc < 2){
+ Tcl_WrongNumArgs(interp, 1, objv, "uid cmd ?args?");
+ }
+ else if(Tcl_GetLongFromObj(interp, objv[1], &uid) != TCL_OK){
+ return(TCL_ERROR); /* conversion problem? */
+ }
+ else if(!peSequenceNumber(uid)){
+ snprintf(err = errbuf, sizeof(errbuf), "%s: UID %ld doesn't exist",
+ Tcl_GetStringFromObj(objv[0], NULL), uid);
+ }
+ else if((cmd = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
+ if(objc == 3){
+ if(!strucmp(cmd,"info")){
+#define WP_MAX_THRD_PREFIX 256
+ long raw;
+ PINETHRD_S *pthrd;
+ char tstr[WP_MAX_THRD_PREFIX];
+
+ if((raw = peSequenceNumber(uid)) != 0L){
+ /*
+ * translate PINETHRD_S data into
+ */
+ if((pthrd = msgno_thread_info(ps_global->mail_stream, raw, NULL, THD_TOP)) != NULL){
+
+ tstr[0] = '\0';
+/* BUG: build tstr form pthrd */
+
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(tstr, -1));
+ }
+ }
+ else
+ Tcl_SetResult(interp, "0", TCL_STATIC);
+
+ return(TCL_OK);
+ }
+
+ }
+ else if(objc == 5){
+ if(!strucmp(cmd,"flag")){
+ if((op = Tcl_GetStringFromObj(objv[3], NULL)) != NULL){
+ if(!strucmp(op,"deleted")){
+ int value;
+
+ if(Tcl_GetIntFromObj(interp, objv[4], &value) != TCL_ERROR){
+ long n;
+ PINETHRD_S *pthrd;
+ char *flag;
+
+ while(1){
+ if(!(n = peSequenceNumber(uid))){
+ Tcl_SetResult(interp, "Unrecognized UID", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ flag = cpystr("\\DELETED");
+ mail_flag(ps_global->mail_stream, long2string(n), flag, (value ? ST_SET : 0L));
+ fs_give((void **) &flag);
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(ulong2string(uid), -1));
+
+ if(++n <= ps_global->mail_stream->nmsgs){
+ uid = mail_uid(ps_global->mail_stream, n);
+ }
+ else
+ break;
+
+ if((pthrd = msgno_thread_info(ps_global->mail_stream, n, NULL,THD_TOP)) != NULL){
+ }
+ else
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Tcl_SetResult(interp, err, TCL_STATIC);
+ return(TCL_ERROR);
+}
+
+
+
+/* * * * * * * * Start of Per-Message access functions * * * * * * * */
+
+
+
+static struct _message_cmds {
+ char *cmd;
+ int hcount;
+ struct {
+ int argcount;
+ int (*f)(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+ } h[3];
+} message_cmds[] = {
+ {"size", 1, {{3, peMessageSize}}},
+ {"date", 2, {{3, peMessageDate}, {4, peMessageDate}}},
+ {"subject", 1, {{3, peMessageSubject}}},
+ {"fromaddr", 1, {{3, peMessageFromAddr}}},
+ {"toaddr", 1, {{3, peMessageToAddr}}},
+ {"ccaddr", 1, {{3, peMessageCcAddr}}},
+ {"status", 1, {{3, peMessageStatus}}},
+ {"statusbits", 1, {{3, peMessageStatusBits}}},
+ {"charset", 1, {{3, peMessageCharset}}},
+ {"number", 1, {{3, peMsgnoFromUID}}},
+ {"envelope", 0},
+ {"rawenvelope", 0},
+ {"text", 1, {{3, peMessageText}}},
+ {"header", 1, {{3, peMessageHeader}}},
+ {"attachments", 1, {{3, peMessageAttachments}}},
+ {"body", 3, {{3, peMessageBody}, {4, peMessageBody}}},
+ {"cid", 1, {{4, peMessagePartFromCID}}},
+ {"flag", 2, {{4, peGetFlag}, {5, peSetFlag}}},
+ {"replyheaders", 2, {{3, peReplyHeaders},{4, peReplyHeaders}}},
+ {"replytext", 2, {{4, peReplyText}, {5, peReplyText}}},
+ {"forwardheaders", 2, {{3, peForwardHeaders}, {4, peForwardHeaders}}},
+ {"forwardtext", 2, {{3, peForwardText}, {4, peForwardText}}},
+ {"rawbody", 0},
+ {"select", 2, {{3, peMsgSelect}, {4, peMsgSelect}}},
+ {"detach", 1, {{5, peDetach}}},
+ {"attachinfo", 1, {{4, peAttachInfo}}},
+ {"savedefault", 1, {{3, peSaveDefault}}},
+ {"save", 1, {{5, peSave}}},
+ {"copy", 1, {{5, peCopy}}},
+ {"move", 1, {{5, peMove}}},
+ {"takeaddr", 1, {{3, peTakeaddr}}},
+ {"takefrom", 1, {{3, peTakeFrom}}},
+ {"replyquote", 1, {{3, peReplyQuote}}},
+ {"bounce", 2, {{4, peMessageBounce},{5, peMessageBounce}}},
+ {"spam", 1, {{5, peMessageSpamNotice}}},
+ {"needpasswd", 1, {{3, peMessageNeedPassphrase}}},
+ {NULL, 0}
+};
+
+
+
+
+/*
+ * PEMessageCmd - export various bits of message information
+ *
+ * NOTE: all exported commands are of the form:
+ *
+ * PEMessage <uid> <cmd> <args>
+ */
+int
+PEMessageCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+ char *err, errbuf[256], *cmd;
+ int i, j;
+ imapuid_t uid;
+
+ dprint((5, "PEMessageCmd"));
+
+ snprintf(err = errbuf, sizeof(errbuf), "Unknown %s request",
+ Tcl_GetStringFromObj(objv[0], NULL));
+
+ if(!(ps_global && ps_global->mail_stream)){
+ snprintf(err = errbuf, sizeof(errbuf), "%s: No open mailbox",
+ Tcl_GetStringFromObj(objv[0], NULL));
+ }
+ else if(objc < 3){
+ Tcl_WrongNumArgs(interp, 0, objv, "PEMessage <uid> cmd ?args?");
+ }
+ else if(Tcl_GetLongFromObj(interp, objv[1], &uid) != TCL_OK){
+ return(TCL_ERROR); /* conversion problem? */
+ }
+ else if(!peMessageNumber(uid)){
+ snprintf(err = errbuf, sizeof(errbuf), "%s: UID %ld doesn't exist",
+ Tcl_GetStringFromObj(objv[0], NULL), uid);
+ }
+ else if((cmd = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
+ for(i = 0; message_cmds[i].cmd; i++)
+ if(!strcmp(cmd, message_cmds[i].cmd)){
+ for(j = 0; j < message_cmds[i].hcount; j++)
+ if(message_cmds[i].h[j].argcount == objc)
+ return((*message_cmds[i].h[j].f)(interp, uid, objc - 3,
+ &((Tcl_Obj **)objv)[3]));
+
+ snprintf(err = errbuf, sizeof(errbuf),
+ "PEMessage: %s: mismatched argument count", cmd);
+ break;
+ }
+ }
+
+ Tcl_SetResult(interp, err, TCL_STATIC);
+ return(TCL_ERROR);
+}
+
+
+/*
+ * return the uid's ordinal number within the CURRENT SORT
+ */
+long
+peMessageNumber(imapuid_t uid)
+{
+ return(mn_raw2m(sp_msgmap(ps_global->mail_stream), peSequenceNumber(uid)));
+}
+
+/*
+ * return the uid's RAW message number (for c-client reference, primarily)
+ */
+long
+peSequenceNumber(imapuid_t uid)
+{
+ return(mail_msgno(ps_global->mail_stream, uid));
+}
+
+
+int
+peMessageSize(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ long raw;
+
+ if((raw = peSequenceNumber(uid))
+ && pine_mail_fetchstructure(ps_global->mail_stream, raw, NULL)){
+ Tcl_SetResult(interp,
+ long2string(mail_elt(ps_global->mail_stream,
+ raw)->rfc822_size),
+ TCL_VOLATILE);
+ }
+ else
+ Tcl_SetResult(interp, "0", TCL_STATIC);
+
+ return(TCL_OK);
+}
+
+
+int
+peMessageDate(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ char *cmd;
+ long raw;
+ ENVELOPE *env;
+ MESSAGECACHE mc;
+
+ if((raw = peSequenceNumber(uid))
+ && (env = pine_mail_fetchstructure(ps_global->mail_stream, raw, NULL))){
+ if(objc == 1 && objv[0]){
+ if(mail_parse_date(&mc, env->date)){
+ if((cmd = Tcl_GetStringFromObj(objv[0], NULL)) != NULL){
+ if(!strucmp(cmd,"day")){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%02d", mc.day);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strucmp(cmd,"month")){
+ Tcl_SetResult(interp, month_abbrev(mc.month), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strucmp(cmd,"year")){
+ Tcl_SetResult(interp, int2string(mc.year + BASEYEAR), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "peMessageDate cmd: %.20s", cmd);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ }
+ }
+ else
+ Tcl_SetResult(interp, "peMessageDate: can't get command", TCL_STATIC);
+ }
+ else
+ Tcl_SetResult(interp, "peMessageDate: can't parse date", TCL_STATIC);
+ }
+ else{
+ Tcl_SetResult(interp, env->date ? (char *) env->date : "", TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ }
+ else
+ Tcl_SetResult(interp, "Can't get message structure", TCL_STATIC);
+
+ return(TCL_ERROR);
+}
+
+
+int
+peMessageFromAddr(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ return(peMessageField(interp, uid, "from"));
+}
+
+
+int
+peMessageToAddr(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ return(peMessageField(interp, uid, "to"));
+}
+
+
+int
+peMessageCcAddr(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ return(peMessageField(interp, uid, "cc"));
+}
+
+
+int
+peMessageSubject(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ return(peMessageField(interp, uid, "subject"));
+}
+
+
+int
+peMessageField(Tcl_Interp *interp, imapuid_t uid, char *field)
+{
+ long raw;
+ char *s = "";
+ ENVELOPE *env;
+
+ if((raw = peSequenceNumber(uid))
+ && (env = pine_mail_fetchstructure(ps_global->mail_stream, raw, NULL))){
+ if(!strucmp(field, "from")){
+ if(env->from && env->from->mailbox)
+ snprintf(s = tmp_20k_buf, SIZEOF_20KBUF, "%.256s%s%.256s", env->from->mailbox,
+ (env->from->host) ? "@" : "", (env->from->host) ? env->from->host : "");
+ }
+ else if(!strucmp(field, "to")){
+ if(env->to && env->to->mailbox)
+ snprintf(s = tmp_20k_buf, SIZEOF_20KBUF, "%.256s%s%.256s", env->to->mailbox,
+ (env->to->host) ? "@" : "", (env->to->host) ? env->to->host : "");
+ }
+ else if(!strucmp(field, "cc")){
+ if(env->cc && env->cc->mailbox)
+ snprintf(s = tmp_20k_buf, SIZEOF_20KBUF, "%.256s%s%.256s", env->cc->mailbox,
+ (env->cc->host) ? "@" : "", (env->cc->host) ? env->cc->host : "");
+ }
+ else if(!strucmp(field, "subject")){
+ if(env->subject)
+ snprintf(s = tmp_20k_buf, SIZEOF_20KBUF, "%.256s", env->subject);
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Unknown message field: %.20s", field);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ Tcl_SetResult(interp, s, TCL_VOLATILE);
+ return(TCL_OK);
+ }
+
+ Tcl_SetResult(interp, "Can't read message envelope", TCL_STATIC);
+ return(TCL_ERROR);
+}
+
+
+int
+peMessageStatus(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ long raw;
+ MESSAGECACHE *mc;
+
+ if((raw = peSequenceNumber(uid)) != 0L){
+ if(!((mc = mail_elt(ps_global->mail_stream, raw))
+ && mc->valid)){
+ mail_fetch_flags(ps_global->mail_stream,
+ ulong2string(uid), FT_UID);
+ mc = mail_elt(ps_global->mail_stream, raw);
+ }
+
+ if (mc->deleted)
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj("Deleted", -1));
+
+ if (mc->answered)
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj("Answered", -1));
+
+ if (!mc->seen)
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj("New", -1));
+
+ if (mc->flagged)
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj("Important", -1));
+ }
+
+ return(TCL_OK);
+}
+
+
+int
+peMessageCharset(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ /* everthing coming out of pith better be utf-8 */
+ Tcl_SetResult(interp, "UTF-8", TCL_STATIC);
+ return(TCL_OK);
+}
+
+
+int
+peMessageNeedPassphrase(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+#ifdef SMIME
+ return((ps_global && ps_global->smime && ps_global->smime->need_passphrase) ? TCL_OK : TCL_ERROR);
+#else
+ return(TCL_ERROR);
+#endif /* SMIME */
+}
+
+
+int
+peMsgnoFromUID(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ Tcl_SetResult(interp, long2string(peMessageNumber(uid)), TCL_VOLATILE);
+ return(TCL_OK);
+}
+
+
+/*
+ * peInterpWritec - collect filtered output, appending to the
+ * command's result list on each EOL
+ */
+int
+peInterpWritec(int c)
+{
+ unsigned char ch = (unsigned char) (0xff & c);
+
+ if(ch == '\n')
+ return(peInterpFlush() == TCL_OK);
+ else
+ so_writec(ch, peED.store);
+
+ return(1);
+}
+
+
+/*
+ * peInterpFlush - write accumulated line to result object mapping
+ * embedded data into exportable tcl list members
+ *
+ */
+int
+peInterpFlush(void)
+{
+ char *line, *p, *tp, *tp2, col1[32], col2[32];
+ Tcl_Obj *lobjp, *objColor, *objPair;
+
+ line = (char *) so_text(peED.store);
+
+ if((lobjp = Tcl_NewListObj(0, NULL)) != NULL){
+ if((p = strindex(line, TAG_EMBED)) != NULL){
+ do{
+ *p = '\0';
+
+ if(p - line)
+ peAppListF(peED.interp, lobjp, "%s%s", "t", line);
+
+ switch(*++p){
+ case TAG_HANDLE :
+ {
+ int i, n;
+ HANDLE_S *h;
+
+
+ for(n = 0, i = *++p; i > 0; i--)
+ n = (n * 10) + (*++p - '0');
+
+ line = ++p; /* prepare for next section of line */
+
+ if(!peED.inhandle){
+ peED.inhandle = 1;
+
+ if((h = get_handle(peED.handles, n)) != NULL)
+ switch(h->type){
+ case IMG :
+ {
+ Tcl_Obj *llObj, *rObj;
+
+ llObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(peED.interp, llObj, Tcl_NewStringObj("img", -1));
+
+ rObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewStringObj(h->h.img.src ? h->h.img.src : "", -1));
+ Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewStringObj(h->h.img.alt ? h->h.img.alt : "", -1));
+
+ Tcl_ListObjAppendElement(peED.interp, llObj, rObj);
+
+ Tcl_ListObjAppendElement(peED.interp, lobjp, llObj);
+ peED.inhandle = 0;
+ }
+
+ break;
+
+ case URL :
+ {
+ Tcl_Obj *llObj, *rObj;
+
+ llObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(peED.interp, llObj, Tcl_NewStringObj("urlstart", -1));
+
+ rObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewStringObj(h->h.url.path ? h->h.url.path : "", -1));
+ Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewStringObj(h->h.url.name ? h->h.url.name : "", -1));
+
+ Tcl_ListObjAppendElement(peED.interp, llObj, rObj);
+
+ Tcl_ListObjAppendElement(peED.interp, lobjp, llObj);
+ }
+
+ break;
+
+ case Attach :
+ {
+ Tcl_Obj *alObj, *rObj, *tObj, *stObj, *fnObj, *eObj;
+
+ alObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(peED.interp, alObj, Tcl_NewStringObj("attach", -1));
+
+ peGetMimeTyping(mail_body(ps_global->mail_stream,
+ peSequenceNumber(peED.uid),
+ (unsigned char *) h->h.attach->number),
+ &tObj, &stObj, &fnObj, &eObj);
+
+
+ rObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewLongObj(peED.uid));
+ Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewStringObj(h->h.attach->number, -1));
+ Tcl_ListObjAppendElement(peED.interp, rObj, tObj);
+ Tcl_ListObjAppendElement(peED.interp, rObj, stObj);
+ Tcl_ListObjAppendElement(peED.interp, rObj, fnObj);
+ Tcl_ListObjAppendElement(peED.interp, rObj, eObj);
+
+ Tcl_ListObjAppendElement(peED.interp, alObj, rObj);
+
+ Tcl_ListObjAppendElement(peED.interp, lobjp, alObj);
+ }
+
+ break;
+
+ default :
+ break;
+ }
+ }
+ }
+
+ break;
+
+ case TAG_FGCOLOR :
+ if((tp = peColorStr(++p, col1)) && (strcmp(tp, peED.color.fg) || strcmp(tp, peED.color.fgdef))){
+ /* look ahead */
+ if(p[11] == TAG_EMBED
+ && p[12] == TAG_BGCOLOR
+ && (tp2 = peColorStr(p + 13, col2))){
+ objColor = Tcl_NewListObj(0, NULL);
+ objPair = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(peED.interp, objColor, Tcl_NewStringObj("color", -1));
+ Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp, -1));
+ Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp2, -1));
+ Tcl_ListObjAppendElement(peED.interp, objColor, objPair);
+ Tcl_ListObjAppendElement(peED.interp, lobjp, objColor);
+ strcpy(peED.color.bg, tp2);
+ p += 13;
+ }
+ else if(strcmp(peED.color.bg, peED.color.bgdef)){
+ objColor = Tcl_NewListObj(0, NULL);
+ objPair = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(peED.interp, objColor, Tcl_NewStringObj("color", -1));
+ Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp, -1));
+ Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(peED.color.bgdef, -1));
+ Tcl_ListObjAppendElement(peED.interp, objColor, objPair);
+ Tcl_ListObjAppendElement(peED.interp, lobjp, objColor);
+ strcpy(peED.color.bg, peED.color.bgdef);
+ }
+ else
+ peAppListF(peED.interp, lobjp, "%s%s", "fgcolor", tp);
+
+ strcpy(peED.color.fg, tp);
+ }
+
+ line = p + 11;
+ break;
+
+ case TAG_BGCOLOR :
+ if((tp = peColorStr(++p, col1)) && (strcmp(tp, peED.color.bg) || strcmp(tp, peED.color.bgdef))){
+ /* look ahead */
+ if(p[11] == TAG_EMBED
+ && p[12] == TAG_FGCOLOR
+ && (tp2 = peColorStr(p + 13, col2))){
+ objColor = Tcl_NewListObj(0, NULL);
+ objPair = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(peED.interp, objColor, Tcl_NewStringObj("color", -1));
+ Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp2, -1));
+ Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp, -1));
+ Tcl_ListObjAppendElement(peED.interp, objColor, objPair);
+ Tcl_ListObjAppendElement(peED.interp, lobjp, objColor);
+ strcpy(peED.color.fg, tp2);
+ p += 13;
+ }
+ else if(strcmp(peED.color.fg, peED.color.fgdef)){
+ objColor = Tcl_NewListObj(0, NULL);
+ objPair = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(peED.interp, objColor, Tcl_NewStringObj("color", -1));
+ Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(peED.color.fgdef, -1));
+ Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp, -1));
+ Tcl_ListObjAppendElement(peED.interp, objColor, objPair);
+ Tcl_ListObjAppendElement(peED.interp, lobjp, objColor);
+ strcpy(peED.color.fg, peED.color.fgdef);
+ }
+ else
+ peAppListF(peED.interp, lobjp, "%s%s", "bgcolor", tp);
+
+ strcpy(peED.color.bg, tp);
+ }
+
+ line = p + 11;
+ break;
+
+ case TAG_ITALICON :
+ peAppListF(peED.interp, lobjp, "%s%s", "italic", "on");
+ line = p + 1;
+ break;
+
+ case TAG_ITALICOFF :
+ peAppListF(peED.interp, lobjp, "%s%s", "italic", "off");
+ line = p + 1;
+ break;
+
+ case TAG_BOLDON :
+ peAppListF(peED.interp, lobjp, "%s%s", "bold", "on");
+ line = p + 1;
+ break;
+
+ case TAG_BOLDOFF :
+ peAppListF(peED.interp, lobjp, "%s%s", "bold", "off");
+ line = p + 1;
+ break;
+
+ case TAG_ULINEON :
+ peAppListF(peED.interp, lobjp, "%s%s", "underline", "on");
+ line = p + 1;
+ break;
+
+ case TAG_ULINEOFF :
+ peAppListF(peED.interp, lobjp, "%s%s", "underline", "off");
+ line = p + 1;
+ break;
+
+ case TAG_STRIKEON :
+ peAppListF(peED.interp, lobjp, "%s%s", "strikethru", "on");
+ line = p + 1;
+ break;
+
+ case TAG_STRIKEOFF :
+ peAppListF(peED.interp, lobjp, "%s%s", "strikethru", "off");
+ line = p + 1;
+ break;
+
+ case TAG_BIGON :
+ peAppListF(peED.interp, lobjp, "%s%s", "bigfont", "on");
+ line = p + 1;
+ break;
+
+ case TAG_BIGOFF :
+ peAppListF(peED.interp, lobjp, "%s%s", "bigfont", "off");
+ line = p + 1;
+ break;
+
+ case TAG_SMALLON :
+ peAppListF(peED.interp, lobjp, "%s%s", "smallfont", "on");
+ line = p + 1;
+ break;
+
+ case TAG_SMALLOFF :
+ peAppListF(peED.interp, lobjp, "%s%s", "smallfont", "off");
+ line = p + 1;
+ break;
+
+ case TAG_INVOFF :
+ case TAG_HANDLEOFF :
+ if(peED.inhandle){
+ peAppListF(peED.interp, lobjp, "%s%s", "urlend", "");
+ peED.inhandle = 0;
+ }
+ /* fall thru and advance "line" */
+
+ default :
+ line = p + 1;
+ break;
+ }
+
+ }
+ while((p = strindex(line, TAG_EMBED)) != NULL);
+
+ if(*line)
+ peAppListF(peED.interp, lobjp, "%s%s", "t", line);
+ }
+ else
+ peAppListF(peED.interp, lobjp, "%s%s", "t", line);
+ }
+ else
+ peAppListF(peED.interp, lobjp, "%s%s", "t", "");
+
+ if(Tcl_ListObjAppendElement(peED.interp, peED.obj, lobjp) == TCL_OK){
+ so_truncate(peED.store, 0L);
+ return(TCL_OK);
+ }
+
+ return(TCL_ERROR);
+}
+
+
+
+/*
+ * peInterpWritec - collect filtered output, appending to the
+ * command's result list on each EOL
+ */
+int
+peNullWritec(int c)
+{
+ return(1);
+}
+
+
+char *
+peColorStr(char *s, char *b)
+{
+ int i, j, color;
+
+ i = 0;
+ b[0] = '\0';
+ while(1){
+ color = 0;
+ for(j = 0; j < 3; j++, s++)
+ if(isdigit((unsigned char) *s))
+ color = (color * 10) + (*s - '0');
+
+ s++; /* advance past ',' */
+ if(color < 256)
+ sprintf(b + strlen(b), "%2.2x", color);
+ else
+ break;
+
+ if(++i == 3)
+ return(b);
+ }
+
+
+ return(NULL);
+}
+
+
+/*
+ * returns a list of elements
+ */
+int
+peMessageHeader(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ MESSAGECACHE *mc;
+ HEADER_S h;
+ int flags, rv = TCL_OK;
+ long raw;
+#if 0
+ char *color;
+#endif
+
+ /*
+ * ONLY full header mode (raw) output should get written to the
+ * writec function we pass format_header. If there's something
+ * in the store after formatting ,we'll write it to the Tcl result
+ * then, not as its accumulated
+ */
+ peED.interp = interp;
+ peED.obj = Tcl_NewStringObj("", -1);
+
+ if(peED.store)
+ so_seek(peED.store, 0L, 0);
+ else
+ peED.store = so_get(CharStar, NULL, EDIT_ACCESS);
+
+ flags = FM_DISPLAY | FM_NEW_MESS | FM_NOEDITORIAL | FM_NOHTMLREL | FM_HTMLRELATED;
+
+#if 0
+ peED.color.fg[0] = '\0';
+ if((color = pico_get_last_fg_color()) && (color = color_to_asciirgb(color))){
+ peInterpWritec(TAG_EMBED);
+ peInterpWritec(TAG_FGCOLOR);
+ gf_puts(color, peInterpWritec);
+ strcpy(peED.color.fgdef, peColorStr(color, tmp_20k_buf));
+ }
+
+ peED.color.bg[0] = '\0';
+ if((color = pico_get_last_bg_color()) && (color = color_to_asciirgb(color))){
+ peInterpWritec(TAG_EMBED);
+ peInterpWritec(TAG_BGCOLOR);
+ gf_puts(color, peInterpWritec);
+ strcpy(peED.color.bgdef, peColorStr(color,tmp_20k_buf));
+ }
+
+ peInterpFlush();
+#endif
+
+ raw = peSequenceNumber(uid);
+ if(peED.uid != uid){
+ peED.uid = uid;
+ peED.body = NULL;
+
+ ps_global->c_client_error[0] = ps_global->last_error[0] = '\0';
+ if(!((peED.env = pine_mail_fetchstructure(ps_global->mail_stream, raw, &peED.body))
+ && (mc = mail_elt(ps_global->mail_stream, raw)))){
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), "Error getting message %ld: %s", peMessageNumber(uid),
+ ps_global->last_error[0] ? ps_global->last_error : "Indeterminate");
+
+ dprint((1, "ERROR fetching %s of msg %ld: %s",
+ peED.env ? "elt" : "env", mn_get_cur(sp_msgmap(ps_global->mail_stream)),
+ ps_global->last_error[0] ? ps_global->last_error : "Indeterminate"));
+
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ rv = TCL_ERROR;
+ }
+ else{
+ zero_atmts(ps_global->atmts);
+#ifdef SMIME
+ if(ps_global && ps_global->smime && ps_global->smime->need_passphrase)
+ ps_global->smime->need_passphrase = 0;
+
+ fiddle_smime_message(peED.body, raw);
+#endif
+ describe_mime(peED.body, "", 1, 1, 0, flags);
+ }
+ }
+
+ /* NO HANDLES init_handles(&peED.handles);*/
+
+ /*
+ * Collect header pieces into lists via the passed custom formatter. Collect
+ * everything else in the storage object passed. The latter should only end up
+ * with raw header data.
+ *
+ * BUG: DEAL WITH COLORS
+ */
+ if(rv == TCL_OK){
+ HD_INIT(&h, ps_global->VAR_VIEW_HEADERS, ps_global->view_all_except, FE_DEFAULT);
+ if(format_header(ps_global->mail_stream, raw, NULL, peED.env, &h,
+ NULL, NULL, flags, peFormatEnvelope, peInterpWritec) != 0){
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), "Error formatting header %ld", peMessageNumber(uid));
+ dprint((1, buf));
+
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ rv = TCL_ERROR;
+ }
+ }
+
+ peInterpFlush();
+ peAppListF(peED.interp, Tcl_GetObjResult(peED.interp), "%s%s%o", "raw", "", peED.obj);
+
+ so_give(&peED.store);
+ return(rv);
+}
+
+void
+peFormatEnvelope(MAILSTREAM *s, long int n, char *sect, ENVELOPE *e, gf_io_t pc, long int which, char *oacs, int flags)
+{
+ char *p2, buftmp[MAILTMPLEN];
+ Tcl_Obj *objHdr;
+
+ if(!e)
+ return;
+
+ if((which & FE_DATE) && e->date) {
+ if((objHdr = Tcl_NewListObj(0, NULL)) != NULL){
+ snprintf(buftmp, sizeof(buftmp), "%s", (char *) e->date);
+ buftmp[sizeof(buftmp)-1] = '\0';
+ p2 = (char *)rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf, SIZEOF_20KBUF, buftmp);
+ peFormatEnvelopeText("Date", p2);
+ }
+ /* BUG: how does error feedback bubble back up? */
+ }
+
+ if((which & FE_FROM) && e->from)
+ peFormatEnvelopeAddress(s, n, sect, "From", e->from, flags, oacs, pc);
+
+ if((which & FE_REPLYTO) && e->reply_to && (!e->from || !address_is_same(e->reply_to, e->from)))
+ peFormatEnvelopeAddress(s, n, sect, "Reply-To", e->reply_to, flags, oacs, pc);
+
+ if((which & FE_TO) && e->to)
+ peFormatEnvelopeAddress(s, n, sect, "To", e->to, flags, oacs, pc);
+
+ if((which & FE_CC) && e->cc)
+ peFormatEnvelopeAddress(s, n, sect, "Cc", e->cc, flags, oacs, pc);
+
+ if((which & FE_BCC) && e->bcc)
+ peFormatEnvelopeAddress(s, n, sect, "Bcc", e->bcc, flags, oacs, pc);
+
+ if((which & FE_RETURNPATH) && e->return_path)
+ peFormatEnvelopeAddress(s, n, sect, "Return-Path", e->return_path, flags, oacs, pc);
+
+ if((which & FE_NEWSGROUPS) && e->newsgroups)
+ peFormatEnvelopeNewsgroups("Newsgroups", e->newsgroups, flags, pc);
+
+ if((which & FE_FOLLOWUPTO) && e->followup_to)
+ peFormatEnvelopeNewsgroups("Followup-To", e->followup_to, flags, pc);
+
+ if((which & FE_SUBJECT) && e->subject && e->subject[0]){
+ if((objHdr = Tcl_NewListObj(0, NULL)) != NULL){
+ char *freeme = NULL;
+
+ p2 = iutf8ncpy((char *)(tmp_20k_buf+10000),
+ (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->subject),
+ SIZEOF_20KBUF-10000);
+
+ if(flags & FM_DISPLAY
+ && (ps_global->display_keywords_in_subject
+ || ps_global->display_keywordinits_in_subject)){
+
+ /* don't bother if no keywords are defined */
+ if(some_user_flags_defined(s))
+ p2 = freeme = prepend_keyword_subject(s, n, p2,
+ ps_global->display_keywords_in_subject ? KW : KWInit,
+ NULL, ps_global->VAR_KW_BRACES);
+ }
+
+ peFormatEnvelopeText("Subject", p2);
+
+ if(freeme)
+ fs_give((void **) &freeme);
+ }
+ }
+
+ if((which & FE_SENDER) && e->sender && (!e->from || !address_is_same(e->sender, e->from)))
+ peFormatEnvelopeAddress(s, n, sect, "Sender", e->sender, flags, oacs, pc);
+
+ if((which & FE_MESSAGEID) && e->message_id){
+ p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->message_id), SIZEOF_20KBUF-10000);
+ peFormatEnvelopeText("Message-ID", p2);
+ }
+
+ if((which & FE_INREPLYTO) && e->in_reply_to){
+ p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->in_reply_to), SIZEOF_20KBUF-10000);
+ peFormatEnvelopeText("In-Reply-To", p2);
+ }
+
+ if((which & FE_REFERENCES) && e->references) {
+ p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->references), SIZEOF_20KBUF-10000);
+ peFormatEnvelopeText("References", p2);
+ }
+}
+
+
+/*
+ * appends caller's result with: {"text" field_name {field_value}}
+ */
+void
+peFormatEnvelopeText(char *field_name, char *field_value)
+{
+ peAppListF(peED.interp, Tcl_GetObjResult(peED.interp), "%s%s%s", "text", field_name, field_value);
+}
+
+
+/*
+ * appends caller's result with: {"addr" field_name {{{personal} {mailbox}} ... }}
+ * {"rawaddr" field_name {{raw_address} ... }}
+ */
+void
+peFormatEnvelopeAddress(MAILSTREAM *stream, long int msgno, char *section, char *field_name,
+ struct mail_address *addr, int flags, char *oacs, gf_io_t pc)
+{
+ char *ptmp, *mtmp, *atype = "addr";
+ int group = 0;
+ ADDRESS *atmp;
+ Tcl_Obj *objAddrList = NULL;
+ STORE_S *tso;
+ gf_io_t tpc;
+ extern const char *rspecials;
+ extern const char *rspecials_minus_quote_and_dot;
+
+ if(!addr)
+ return;
+
+ /*
+ * quickly run down address list to make sure none are patently bogus.
+ * If so, just blat raw field out.
+ */
+ for(atmp = addr; stream && atmp; atmp = atmp->next)
+ if(atmp->host && atmp->host[0] == '.'){
+ char *field, *fields[2];
+
+ atype = "rawaddr";
+ if((objAddrList = Tcl_NewListObj(0,NULL)) == NULL)
+ return; /* BUG: handle list creation failure */
+
+ fields[1] = NULL;
+ fields[0] = cpystr(field_name);
+ if((ptmp = strchr(fields[0], ':')) != NULL)
+ *ptmp = '\0';
+
+ if((field = pine_fetchheader_lines(stream, msgno, section, fields)) != NULL){
+ char *h, *t;
+
+ for(t = h = field; *h ; t++)
+ if(*t == '\015' && *(t+1) == '\012'){
+ *t = '\0'; /* tie off line */
+
+ Tcl_ListObjAppendElement(peED.interp, objAddrList, Tcl_NewStringObj(h,-1));
+
+ if(!*(h = (++t) + 1)) /* set new h and skip CRLF */
+ break; /* no more to write */
+ }
+ else if(!*t){ /* shouldn't happen much */
+ if(h != t)
+ Tcl_ListObjAppendElement(peED.interp, objAddrList, Tcl_NewStringObj(h,-1));
+
+ break;
+ }
+
+ fs_give((void **)&field);
+ }
+
+ fs_give((void **)&fields[0]);
+ }
+
+ if(!objAddrList){
+ if((objAddrList = Tcl_NewListObj(0,NULL)) == NULL || (tso = so_get(CharStar, NULL, EDIT_ACCESS)) == NULL)
+ return; /* BUG: handle list creation failure */
+
+ gf_set_so_writec(&tpc, tso);
+
+ while(addr){
+
+ atmp = addr->next; /* remember what's next */
+ addr->next = NULL;
+ if(!addr->host && addr->mailbox){
+ mtmp = addr->mailbox;
+ addr->mailbox = cpystr((char *)rfc1522_decode_to_utf8(
+ (unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, addr->mailbox));
+ }
+
+ ptmp = addr->personal; /* RFC 1522 personal name? */
+ addr->personal = iutf8ncpy((char *)tmp_20k_buf, (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+10000), SIZEOF_20KBUF-10000, addr->personal), 10000);
+ tmp_20k_buf[10000-1] = '\0';
+
+
+ /* Logic taken from: pine_rfc822_write_address_noquote(addr, pc, &group); */
+ if (addr->host) { /* ordinary address? */
+ if (!(addr->personal || addr->adl)){
+ so_seek(tso, 0L, 0);
+ pine_rfc822_address (addr, tpc);
+ peAppListF(peED.interp, objAddrList, "%s%o", "", Tcl_NewStringObj((char *) so_text(tso), so_tell(tso)));
+ }
+ else { /* no, must use phrase <route-addr> form */
+ Tcl_Obj *objTmp;
+
+ if (addr->personal){
+ so_seek(tso, 0L, 0);
+ pine_rfc822_cat (addr->personal, rspecials_minus_quote_and_dot, tpc);
+ objTmp = Tcl_NewStringObj((char *) so_text(tso), so_tell(tso));
+ }
+
+ so_seek(tso, 0L, 0);
+ pine_rfc822_address(addr, tpc);
+ peAppListF(peED.interp, objAddrList, "%o%o", objTmp, Tcl_NewStringObj((char *) so_text(tso), so_tell(tso)));
+ }
+
+ if(group)
+ group++;
+ }
+ else if (addr->mailbox) { /* start of group? */
+ so_seek(tso, 0L, 0);
+ /* yes, write group name */
+ pine_rfc822_cat (addr->mailbox, rspecials, tpc);
+ peAppListF(peED.interp, objAddrList, "%o%s", Tcl_NewStringObj((char *) so_text(tso), so_tell(tso)), "");
+ group = 1; /* in a group */
+ }
+ else if (group) { /* must be end of group (but be paranoid) */
+ peAppListF(peED.interp, objAddrList, "%s%s", "", ";");
+ group = 0; /* no longer in that group */
+ }
+
+ addr->personal = ptmp; /* restore old personal ptr */
+ if(!addr->host && addr->mailbox){
+ fs_give((void **)&addr->mailbox);
+ addr->mailbox = mtmp;
+ }
+
+ addr->next = atmp;
+ addr = atmp;
+ }
+
+ gf_clear_so_writec(tso);
+ so_give(&tso);
+ }
+
+ peAppListF(peED.interp, Tcl_GetObjResult(peED.interp), "%s%s%o", atype, field_name, objAddrList);
+}
+
+
+/*
+ * appends caller's result with: {"news" field_name {{newsgroup1} {newsgroup2} ... }}
+ */
+void
+peFormatEnvelopeNewsgroups(char *field_name, char *newsgrps, int flags, gf_io_t pc)
+{
+ char buf[MAILTMPLEN];
+ int llen;
+ char *next_ng;
+ Tcl_Obj *objNewsgroups;
+
+ /* BUG: handle list creation failure */
+ if(!newsgrps || !*newsgrps || (objNewsgroups = Tcl_NewListObj(0,NULL)) == NULL)
+ return;
+
+ llen = strlen(field_name);
+ while(*newsgrps){
+ for(next_ng = newsgrps; *next_ng && *next_ng != ','; next_ng++)
+ ;
+
+ strncpy(buf, newsgrps, MIN(next_ng - newsgrps, sizeof(buf)-1));
+ buf[MIN(next_ng - newsgrps, sizeof(buf)-1)] = '\0';
+
+ Tcl_ListObjAppendElement(peED.interp, objNewsgroups, Tcl_NewStringObj(buf,-1));
+
+ newsgrps = next_ng;
+ if(*newsgrps)
+ newsgrps++;
+ }
+
+ peAppListF(peED.interp, Tcl_GetObjResult(peED.interp), "news", field_name, objNewsgroups);
+}
+
+
+int
+peMessageAttachments(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ MESSAGECACHE *mc;
+ ATTACH_S *a;
+ BODY *body;
+ Tcl_Obj *objAtt, *tObj, *stObj, *fnObj;
+ int flags, rv = TCL_OK;
+ long raw;
+
+ peED.interp = interp;
+ peED.obj = Tcl_GetObjResult(interp);
+
+ flags = FM_DISPLAY | FM_NEW_MESS | FM_NOEDITORIAL | FM_HTML | FM_NOHTMLREL | FM_HTMLRELATED | FM_HIDESERVER;
+
+ raw = peSequenceNumber(uid);
+
+ if(peED.uid != uid){
+ memset(&peED, 0, sizeof(peED));
+
+ peED.uid = uid;
+
+ ps_global->c_client_error[0] = ps_global->last_error[0] = '\0';
+ if(!((peED.env = pine_mail_fetchstructure(ps_global->mail_stream, raw, &peED.body))
+ && (mc = mail_elt(ps_global->mail_stream, raw)))){
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), "Error getting message %ld: %s", peMessageNumber(uid),
+ ps_global->last_error[0] ? ps_global->last_error : "Indeterminate");
+
+ dprint((1, "ERROR fetching %s of msg %ld: %s",
+ peED.env ? "elt" : "env", mn_get_cur(sp_msgmap(ps_global->mail_stream)),
+ ps_global->last_error[0] ? ps_global->last_error : "Indeterminate"));
+
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ rv = TCL_ERROR;
+ }
+ else{
+ zero_atmts(ps_global->atmts);
+#ifdef SMIME
+ if(ps_global && ps_global->smime && ps_global->smime->need_passphrase)
+ ps_global->smime->need_passphrase = 0;
+
+ fiddle_smime_message(peED.body, raw);
+#endif
+ describe_mime(peED.body, "", 1, 1, 0, flags);
+ }
+ }
+
+ /* package up attachment list */
+ for(a = ps_global->atmts; rv == TCL_OK && a->description != NULL; a++)
+ if((objAtt = Tcl_NewListObj(0, NULL)) != NULL
+ && (body = mail_body(ps_global->mail_stream, raw, (unsigned char *) a->number)) != NULL){
+ peGetMimeTyping(body, &tObj, &stObj, &fnObj, NULL);
+
+ if(!(peAppListF(interp, objAtt, "%s", a->number ? a->number : "") == TCL_OK
+ && peAppListF(interp, objAtt, "%s", a->shown ? "shown" : "") == TCL_OK
+ && Tcl_ListObjAppendElement(interp, objAtt, tObj) == TCL_OK
+ && Tcl_ListObjAppendElement(interp, objAtt, stObj) == TCL_OK
+ && Tcl_ListObjAppendElement(interp, objAtt, fnObj) == TCL_OK
+ && peAppListF(interp, objAtt, "%s", a->body->description) == TCL_OK
+ && Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAtt) == TCL_OK))
+ rv = TCL_ERROR;
+ }
+ else
+ rv = TCL_ERROR;
+
+ return(rv);
+}
+
+
+int
+peMessageBody(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ MESSAGECACHE *mc;
+ int flags, rv = TCL_OK;
+ long raw;
+ char *color;
+
+ peED.interp = interp;
+ peED.obj = Tcl_GetObjResult(interp);
+
+ if(peED.store)
+ so_seek(peED.store, 0L, 0);
+ else
+ peED.store = so_get(CharStar, NULL, EDIT_ACCESS);
+
+ flags = FM_DISPLAY | FM_NEW_MESS | FM_NOEDITORIAL | FM_NOHTMLREL | FM_HTMLRELATED;
+
+ if(objc == 1 && objv[0]){ /* flags */
+ int i, nFlags;
+ Tcl_Obj **objFlags;
+ char *flagstr;
+
+ Tcl_ListObjGetElements(interp, objv[0], &nFlags, &objFlags);
+ for(i = 0; i < nFlags; i++){
+ if((flagstr = Tcl_GetStringFromObj(objFlags[i], NULL)) == NULL){
+ rv = TCL_ERROR;
+ }
+
+ if(!strucmp(flagstr, "html"))
+ flags |= (FM_HTML | FM_HIDESERVER);
+ else if(!strucmp(flagstr, "images"))
+ flags |= (FM_HTMLIMAGES);
+ }
+ }
+
+ peED.color.fg[0] = '\0';
+ if((color = pico_get_last_fg_color()) && (color = color_to_asciirgb(color))){
+ peInterpWritec(TAG_EMBED);
+ peInterpWritec(TAG_FGCOLOR);
+ gf_puts(color, peInterpWritec);
+ strcpy(peED.color.fgdef, peColorStr(color, tmp_20k_buf));
+ }
+
+ peED.color.bg[0] = '\0';
+ if((color = pico_get_last_bg_color()) && (color = color_to_asciirgb(color))){
+ peInterpWritec(TAG_EMBED);
+ peInterpWritec(TAG_BGCOLOR);
+ gf_puts(color, peInterpWritec);
+ strcpy(peED.color.bgdef, peColorStr(color,tmp_20k_buf));
+ }
+
+ peInterpFlush();
+
+ init_handles(&peED.handles);
+
+ raw = peSequenceNumber(uid);
+
+ if(peED.uid != uid){
+ peED.uid = uid;
+ peED.body = NULL;
+
+ ps_global->c_client_error[0] = ps_global->last_error[0] = '\0';
+ if(!((peED.env = pine_mail_fetchstructure(ps_global->mail_stream, raw, &peED.body))
+ && (mc = mail_elt(ps_global->mail_stream, raw)))){
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), "Error getting message %ld: %s", peMessageNumber(uid),
+ ps_global->last_error[0] ? ps_global->last_error : "Indeterminate");
+
+ dprint((1, "ERROR fetching %s of msg %ld: %s",
+ peED.env ? "elt" : "env", mn_get_cur(sp_msgmap(ps_global->mail_stream)),
+ ps_global->last_error[0] ? ps_global->last_error : "Indeterminate"));
+
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ rv = TCL_ERROR;
+ }
+ else{
+ zero_atmts(ps_global->atmts);
+#ifdef SMIME
+ if(ps_global && ps_global->smime && ps_global->smime->need_passphrase)
+ ps_global->smime->need_passphrase = 0;
+
+ fiddle_smime_message(peED.body, raw);
+#endif
+ describe_mime(peED.body, "", 1, 1, 0, flags);
+ }
+ }
+
+ /* format message body */
+ if(rv == TCL_OK){
+ HEADER_S h;
+ char *errstr;
+
+ HD_INIT(&h, ps_global->VAR_VIEW_HEADERS, ps_global->view_all_except, FE_DEFAULT);
+#ifdef SMIME
+ /* kind of a hack, the description maybe shouldn't be in the editorial stuff */
+ if(ps_global->smime && ps_global->smime->need_passphrase)
+ flags &= ~FM_NOEDITORIAL;
+#endif
+ if((errstr = format_body(raw, peED.body, &peED.handles, &h, flags, FAKE_SCREEN_WIDTH, peInterpWritec)) != NULL){
+ gf_puts(errstr, peInterpWritec);
+ rv = TCL_ERROR;
+ }
+ }
+
+ peInterpFlush();
+
+ so_give(&peED.store);
+ free_handles(&peED.handles);
+ return(rv);
+}
+
+
+int
+peMessageText(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ MESSAGECACHE *mc;
+ ENVELOPE *env;
+ BODY *body;
+ int flags;
+ long raw;
+ char *color;
+
+ memset(&peED, 0, sizeof(peED));
+ peED.interp = interp;
+ peED.obj = Tcl_GetObjResult(interp);
+ peED.store = so_get(CharStar, NULL, EDIT_ACCESS);
+
+ peED.color.fg[0] = '\0';
+ if((color = pico_get_last_fg_color()) && (color = color_to_asciirgb(color))){
+ peInterpWritec(TAG_EMBED);
+ peInterpWritec(TAG_FGCOLOR);
+ gf_puts(color, peInterpWritec);
+ strcpy(peED.color.fgdef, peColorStr(color, tmp_20k_buf));
+ }
+
+ peED.color.bg[0] = '\0';
+ if((color = pico_get_last_bg_color()) && (color = color_to_asciirgb(color))){
+ peInterpWritec(TAG_EMBED);
+ peInterpWritec(TAG_BGCOLOR);
+ gf_puts(color, peInterpWritec);
+ strcpy(peED.color.bgdef, peColorStr(color,tmp_20k_buf));
+ }
+
+ raw = peSequenceNumber(peED.uid = uid);
+ body = NULL;
+ ps_global->c_client_error[0] = ps_global->last_error[0] = '\0';
+ if(!((env = pine_mail_fetchstructure(ps_global->mail_stream, raw, &body))
+ && (mc = mail_elt(ps_global->mail_stream, raw)))){
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), "Error getting message %ld: %s", peMessageNumber(uid),
+ ps_global->last_error[0] ? ps_global->last_error : "Indeterminate");
+
+ dprint((1, "ERROR fetching %s of msg %ld: %s",
+ env ? "elt" : "env", mn_get_cur(sp_msgmap(ps_global->mail_stream)),
+ ps_global->last_error[0] ? ps_global->last_error : "Indeterminate"));
+
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ flags = FM_DISPLAY | FM_NEW_MESS | FM_NOEDITORIAL | FM_NOHTMLREL | FM_HTMLRELATED;
+
+ init_handles(&peED.handles);
+
+ (void) format_message(raw, env, body, &peED.handles, flags, peInterpWritec);
+
+ peInterpFlush();
+
+ so_give(&peED.store);
+ free_handles(&peED.handles);
+ return(TCL_OK);
+}
+
+
+/*
+ * peMessagePartFromCID - return part number assoc'd with given uid and CID
+ */
+int
+peMessagePartFromCID(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ char *cid, sect_buf[256];
+ long raw;
+ ENVELOPE *env;
+ BODY *body;
+
+ raw = peSequenceNumber(peED.uid = uid);
+ ps_global->c_client_error[0] = ps_global->last_error[0] = '\0';
+
+ if(objv[0] && (cid = Tcl_GetStringFromObj(objv[0], NULL)) && *cid != '\0'){
+ if((env = pine_mail_fetchstructure(ps_global->mail_stream, raw, &body)) != NULL){
+ sect_buf[0] = '\0';
+ if(peLocateBodyByCID(cid, sect_buf, body)){
+ Tcl_SetResult(interp, sect_buf, TCL_VOLATILE);
+ }
+ }
+ else{
+ Tcl_SetResult(interp, ps_global->last_error[0] ? ps_global->last_error : "Error getting CID", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ }
+
+ return(TCL_OK);
+}
+
+
+int
+peLocateBodyByCID(char *cid, char *section, BODY *body)
+{
+ if(body->type == TYPEMULTIPART){
+ char subsection[256], *subp;
+ int n;
+ PART *part = body->nested.part;
+
+ if(!(part = body->nested.part))
+ return(0);
+
+ subp = subsection;
+ if(section && *section){
+ for(n = 0;
+ n < sizeof(subsection)-20 && (*subp = section[n]); n++, subp++)
+ ;
+
+ *subp++ = '.';
+ }
+
+ n = 1;
+ do {
+ sprintf(subp, "%d", n++);
+ if(peLocateBodyByCID(cid, subsection, &part->body)){
+ strcpy(section, subsection);
+ return(1);
+ }
+ }
+ while((part = part->next) != NULL);
+
+ return(0);
+ }
+
+ return((body && body->id) ? !strcmp(cid, body->id) : 0);
+}
+
+
+/*
+ * peGetFlag - Return 1 or 0 based on requested flags current state
+ *
+ * Params: argv[0] == flagname
+ */
+int
+peGetFlag(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ char *flagname;
+
+ Tcl_SetResult(interp,
+ int2string(((flagname = Tcl_GetStringFromObj(objv[0], NULL)) != NULL)
+ ? peIsFlagged(ps_global->mail_stream, uid, flagname)
+ : 0),
+ TCL_VOLATILE);
+ return(TCL_OK);
+}
+
+
+int
+peIsFlagged(MAILSTREAM *stream, imapuid_t uid, char *flagname)
+{
+ MESSAGECACHE *mc;
+ long raw = peSequenceNumber(uid);
+
+ if(!((mc = mail_elt(stream, raw)) && mc->valid)){
+ mail_fetch_flags(stream, ulong2string(uid), FT_UID);
+ mc = mail_elt(stream, raw);
+ }
+
+ if(!strucmp(flagname, "deleted"))
+ return(mc->deleted);
+
+ if(!strucmp(flagname, "new"))
+ return(!mc->seen);
+
+ if(!strucmp(flagname, "important"))
+ return(mc->flagged);
+
+ if(!strucmp(flagname, "answered"))
+ return(mc->answered);
+
+ if(!strucmp(flagname, "recent"))
+ return(mc->recent);
+
+ return(0);
+}
+
+
+/*
+ * peSetFlag - Set requested flags value to 1 or 0
+ *
+ * Params: abjv[0] == flagname
+ * objv[1] == newvalue
+ */
+int
+peSetFlag(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ char *flagname, *flagstr = NULL;
+ int value;
+
+ if((flagname = Tcl_GetStringFromObj(objv[0], NULL))
+ && Tcl_GetIntFromObj(interp, objv[1], &value) != TCL_ERROR){
+ if(!strucmp(flagname, "deleted")){
+ flagstr = "\\DELETED";
+ }
+ else if(!strucmp(flagname, "new")){
+ flagstr = "\\SEEN";
+ value = !value;
+ }
+ else if(!strucmp(flagname, "important")){
+ flagstr = "\\FLAGGED";
+ }
+ else if(!strucmp(flagname, "answered")){
+ flagstr = "\\ANSWERED";
+ }
+ else if(!strucmp(flagname, "recent")){
+ flagstr = "\\RECENT";
+ }
+
+ if(flagstr){
+ ps_global->c_client_error[0] = '\0';
+ mail_flag(ps_global->mail_stream,
+ ulong2string(uid),
+ flagstr, (value ? ST_SET : 0L) | ST_UID);
+ if(ps_global->c_client_error[0] != '\0'){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "peSetFlag: %.40s",
+ ps_global->c_client_error);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ }
+ }
+
+ Tcl_SetResult(interp, value ? "1" : "0", TCL_STATIC);
+ return(TCL_OK);
+}
+
+
+/*
+ * peMsgSelect - Return 1 or 0 based on whether given UID is selected
+ *
+ * Params: argv[0] == selected
+ */
+int
+peMsgSelect(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ int value;
+
+ if(objc == 1 && objv[0]){
+ if(Tcl_GetIntFromObj(interp, objv[0], &value) != TCL_ERROR){
+ if(value){
+ set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream),
+ peMessageNumber(uid), MN_SLCT, 1);
+ set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream),
+ peMessageNumber(uid), MN_HIDE, 0);
+ } else {
+ set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream),
+ peMessageNumber(uid), MN_SLCT, 0);
+ /* if zoomed, lite hidden bit */
+ if(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE))
+ set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream),
+ peMessageNumber(uid), MN_HIDE, 1);
+
+ }
+ }
+ else{
+ Tcl_SetResult(interp, "peMsgSelect: can't get value", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ }
+
+ Tcl_SetResult(interp,
+ (get_lflag(ps_global->mail_stream, NULL,
+ peSequenceNumber(uid),
+ MN_SLCT))
+ ? "1" : "0",
+ TCL_VOLATILE);
+ return(TCL_OK);
+}
+
+
+/*
+ * peAppendIndexParts - append list of digested index pieces to given object
+ *
+ * Params:
+ *
+ */
+int
+peAppendIndexParts(Tcl_Interp *interp, imapuid_t uid, Tcl_Obj *aObj, int *fetched)
+{
+ Tcl_Obj *objField, *objElement, *objp;
+ ICE_S *h;
+ IFIELD_S *f;
+ IELEM_S *ie;
+
+
+ if((h = build_header_work(ps_global, ps_global->mail_stream,
+ sp_msgmap(ps_global->mail_stream), peMessageNumber(uid),
+ gPeITop, gPeICount, fetched)) != NULL){
+ for(f = h->ifield; f; f = f->next){
+
+ if((objField = Tcl_NewListObj(0, NULL)) == NULL)
+ return(TCL_ERROR);
+
+ for(ie = f->ielem; ie ; ie = ie->next){
+
+ if((objElement = Tcl_NewListObj(0, NULL)) == NULL)
+ return(TCL_ERROR);
+
+ if(ie->datalen){
+ /* FIRST: DATA */
+#if INTERNAL_INDEX_TRUNCATE
+ char *ep;
+
+ ep = (char *) fs_get((ie->datalen + 1) * sizeof(char));
+ sprintf(ep, "%.*s", ie->wid, ie->data);
+
+ /* and other stuff to pack trunc'd element into a new object */
+#endif
+
+ objp = Tcl_NewStringObj(ie->data, ie->datalen);
+ }
+ else
+ objp = Tcl_NewStringObj("", -1);
+
+ if(Tcl_ListObjAppendElement(interp, objElement, objp) != TCL_OK)
+ return(TCL_ERROR);
+
+ if(ie->color){
+ Tcl_Obj *objColor;
+ char hexcolor[32];
+
+ if((objp = Tcl_NewListObj(0, NULL)) == NULL)
+ return(TCL_ERROR);
+
+ hex_colorstr(hexcolor, ie->color->fg);
+ objColor = Tcl_NewStringObj(hexcolor, -1);
+ if(Tcl_ListObjAppendElement(interp, objp, objColor) != TCL_OK)
+ return(TCL_ERROR);
+
+ hex_colorstr(hexcolor, ie->color->bg);
+ objColor = Tcl_NewStringObj(hexcolor, -1);
+ if(Tcl_ListObjAppendElement(interp, objp, objColor) != TCL_OK)
+ return(TCL_ERROR);
+ }
+ else
+ objp = Tcl_NewStringObj("", -1);
+
+ if(Tcl_ListObjAppendElement(interp, objElement, objp) != TCL_OK)
+ return(TCL_ERROR);
+
+ /*
+ * IF we ever want to map the thread characters into nice
+ * graphical symbols or take advantage of features like clicking
+ * on a thread element to collapse and such, we need to have
+ * element tagging. That's what the object creation and append
+ * are placeholders for
+ */
+ switch(ie->type){
+ case eThreadInfo :
+ objp = Tcl_NewStringObj("threadinfo", -1);
+ break;
+ case eText :
+ objp = NULL;
+ break;
+ default :
+ objp = Tcl_NewStringObj(int2string(ie->type), -1);
+ break;
+ }
+
+ if(objp && Tcl_ListObjAppendElement(interp, objElement, objp) != TCL_OK)
+ return(TCL_ERROR);
+
+ if(Tcl_ListObjAppendElement(interp, objField, objElement) != TCL_OK)
+ return(TCL_ERROR);
+ }
+
+ if(Tcl_ListObjAppendElement(interp, aObj, objField) != TCL_OK){
+ return(TCL_ERROR);
+ }
+ }
+ }
+
+ return(TCL_OK);
+}
+
+
+/*
+ * peAppendIndexColor - append index line's foreground/background color
+ *
+ * Params:
+ *
+ */
+int
+peAppendIndexColor(Tcl_Interp *interp, imapuid_t uid, Tcl_Obj *aObj, int *fetched)
+{
+ char hexfg[32], hexbg[32];
+ ICE_S *h;
+
+ if((h = build_header_work(ps_global, ps_global->mail_stream,
+ sp_msgmap(ps_global->mail_stream), peMessageNumber(uid),
+ gPeITop, gPeICount, fetched))
+ && h->color_lookup_done
+ && h->linecolor){
+
+ hex_colorstr(hexfg, h->linecolor->fg);
+ hex_colorstr(hexbg, h->linecolor->bg);
+
+ return(peAppListF(interp, aObj, "%s%s", hexfg, hexbg));
+ }
+
+ return(peAppListF(interp, aObj, "%s", ""));
+}
+
+
+/*
+ * peMessageStatusBits - return list flags indicating pine status bits
+ *
+ * Params:
+ *
+ * Returns: list of lists where:
+ * * the first element is the list of
+ * field elements data
+ * * the second element is a two element
+ * list containing the lines foreground
+ * and background colors
+ */
+int
+peMessageStatusBits(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ Tcl_SetResult(interp,
+ peMsgStatBitString(ps_global, ps_global->mail_stream,
+ sp_msgmap(ps_global->mail_stream), peMessageNumber(uid),
+ gPeITop, gPeICount, NULL),
+ TCL_STATIC);
+ return(TCL_OK);
+}
+
+
+char *
+peMsgStatBitString(struct pine *state,
+ MAILSTREAM *stream,
+ MSGNO_S *msgmap,
+ long msgno,
+ long top_msgno,
+ long msgcount,
+ int *fetched)
+{
+ static char buf[36];
+ int i;
+ long raw;
+ MESSAGECACHE *mc;
+ ICE_S *h;
+
+ raw = mn_m2raw(msgmap, msgno);
+ if((h = build_header_work(state, stream, msgmap,
+ msgno, top_msgno, msgcount, fetched))
+ && (mc = mail_elt(stream, raw))){
+ /* return a string representing a bit field where:
+ index meaning
+ ----- -------
+ 0 "New"
+ 1 deleted
+ 2 answered
+ 3 flagged
+ 4 to us
+ 5 cc us
+ 6 recent
+ 7 forwarded
+ 8 attachments
+ */
+ i = 0;
+ buf[i++] = (mc->seen) ? '0' : '1';
+ buf[i++] = (mc->deleted) ? '1' : '0';
+ buf[i++] = (mc->answered) ? '1' : '0';
+ buf[i++] = (mc->flagged) ? '1' : '0';
+ buf[i++] = (h->to_us) ? '1' : '0';
+ buf[i++] = (h->cc_us) ? '1' : '0';
+ buf[i++] = (mc->recent) ? '1' : '0';
+ buf[i++] = (user_flag_is_set(stream, raw, FORWARDED_FLAG)) ? '1' : '0';
+ buf[i++] = '0';
+ buf[i++] = '\0';
+
+ return(buf);
+ }
+
+ return("100000000");
+}
+
+
+Tcl_Obj *
+peMsgStatNameList(Tcl_Interp *interp,
+ struct pine *state,
+ MAILSTREAM *stream,
+ MSGNO_S *msgmap,
+ long msgno,
+ long top_msgno,
+ long msgcount,
+ int *fetched)
+{
+ Tcl_Obj *objList;
+ long raw;
+ MESSAGECACHE *mc;
+ ICE_S *h;
+
+ objList = Tcl_NewListObj(0, NULL);
+ raw = mn_m2raw(msgmap, msgno);
+ if((h = build_header_work(state, stream, msgmap,
+ msgno, top_msgno, msgcount, fetched))
+ && (mc = mail_elt(stream, raw))){
+ if(!mc->seen)
+ Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("new", -1));
+
+ if(mc->deleted)
+ Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("deleted", -1));
+
+ if(mc->answered)
+ Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("answered", -1));
+
+ if(mc->flagged)
+ Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("flagged", -1));
+
+ if(h->to_us)
+ Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("to_us", -1));
+
+ if(h->cc_us)
+ Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("cc_us", -1));
+
+ if(mc->recent)
+ Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("recent", -1));
+
+ if(user_flag_is_set(stream, raw, FORWARDED_FLAG))
+ Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("forwarded", -1));
+
+ if(get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), msgno, MN_SLCT))
+ Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("selected", -1));
+ }
+
+ return(objList);
+}
+
+
+/*
+ * peReplyHeaders - return subject used in reply to given message
+ *
+ * Params:
+ *
+ * Returns: list of header value pairs where headers are:
+ * In-Reply-To:, Subject:, Cc:
+ *
+ */
+int
+peReplyHeaders(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ long raw;
+ int flags = RSF_FORCE_REPLY_TO | RSF_FORCE_REPLY_ALL, err = FALSE;
+ char *errmsg = NULL, *fcc = NULL, *sect = NULL;
+ ENVELOPE *env, *outgoing;
+ BODY *body = NULL;
+ ADDRESS *saved_from, *saved_to, *saved_cc, *saved_resent;
+
+ saved_from = (ADDRESS *) NULL;
+ saved_to = (ADDRESS *) NULL;
+ saved_cc = (ADDRESS *) NULL;
+ saved_resent = (ADDRESS *) NULL;
+
+ raw = peSequenceNumber(uid);
+
+ /* if we're given a valid section number that
+ * corresponds to a valid msg/rfc822 body part
+ * then set up headers in attached message.
+ */
+ if(objc == 1 && objv[0]
+ && (sect = Tcl_GetStringFromObj(objv[0], NULL)) && *sect != '\0'
+ && (body = mail_body(ps_global->mail_stream, raw, (unsigned char *) sect))
+ && body->type == TYPEMESSAGE
+ && !strucmp(body->subtype, "rfc822")){
+ env = body->nested.msg->env;
+ }
+ else{
+ sect = NULL;
+ env = mail_fetchstructure(ps_global->mail_stream, raw, NULL);
+ }
+
+ if(env){
+ if(!reply_harvest(ps_global, raw, sect, env,
+ &saved_from, &saved_to, &saved_cc,
+ &saved_resent, &flags)){
+
+ Tcl_SetResult(interp, "", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ outgoing = mail_newenvelope();
+
+ reply_seed(ps_global, outgoing, env,
+ saved_from, saved_to, saved_cc, saved_resent,
+ &fcc, flags, &errmsg);
+ if(errmsg){
+ if(*errmsg){
+ q_status_message1(SM_ORDER, 3, 3, "%.200s", errmsg);
+ }
+
+ fs_give((void **)&errmsg);
+ }
+
+ env = pine_mail_fetchstructure(ps_global->mail_stream, raw, NULL);
+
+ outgoing->subject = reply_subject(env->subject, NULL, 0);
+ outgoing->in_reply_to = reply_in_reply_to(env);
+
+ err = !(peAppListF(interp, Tcl_GetObjResult(interp),
+ "%s%a", "to", outgoing->to) == TCL_OK
+ && peAppListF(interp, Tcl_GetObjResult(interp),
+ "%s%a", "cc", outgoing->cc) == TCL_OK
+ && peAppListF(interp, Tcl_GetObjResult(interp),
+ "%s%s", "in-reply-to", outgoing->in_reply_to) == TCL_OK
+ && peAppListF(interp, Tcl_GetObjResult(interp),
+ "%s%s", "subject",
+ rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
+ SIZEOF_20KBUF, outgoing->subject)) == TCL_OK
+ && (fcc ? peFccAppend(interp, Tcl_GetObjResult(interp), fcc, -1) : TRUE));
+
+
+ /* Fill in x-reply-uid data and append it */
+ if(!err && ps_global->mail_stream->uid_validity){
+ char *prefix = reply_quote_str(env);
+
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "(%d %s)(1 %lu %lu)%s",
+ strlen(prefix), prefix,
+ ps_global->mail_stream->uid_validity, uid,
+ ps_global->mail_stream->mailbox);
+
+ fs_give((void **) &prefix);
+
+ err = peAppListF(interp, Tcl_GetObjResult(interp), "%s%s",
+ "x-reply-uid", tmp_20k_buf) != TCL_OK;
+ }
+
+ mail_free_envelope(&outgoing);
+
+ if(err)
+ return(TCL_ERROR);
+ }
+ else
+ Tcl_SetResult(interp, "", TCL_VOLATILE);
+
+ return(TCL_OK);
+}
+
+
+
+/*
+ * peReplyText - return subject used in reply to given message
+ *
+ * Params:
+ *
+ * Returns:
+ *
+ */
+int
+peReplyText(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ long msgno;
+ char *prefix, *sect = NULL;
+ int rv = TCL_OK;
+ ENVELOPE *env;
+ BODY *body = NULL, *orig_body;
+ STORE_S *msgtext;
+ REDRAFT_POS_S *redraft_pos = NULL;
+ Tcl_Obj *objBody = NULL, *objAttach = NULL;
+
+ msgno = peSequenceNumber(uid);
+
+ if((msgtext = (void *) so_get(CharStar, NULL, EDIT_ACCESS)) == NULL){
+ Tcl_SetResult(interp, "Unable to create storage for reply text", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ /*--- Grab current envelope ---*/
+ /* if we're given a valid section number that
+ * corresponds to a valid msg/rfc822 body part
+ * then set up to reply the attached message's
+ * text.
+ */
+ if(objc == 2 && objv[1]
+ && (sect = Tcl_GetStringFromObj(objv[1], NULL)) && *sect != '\0'
+ && (body = mail_body(ps_global->mail_stream, msgno, (unsigned char *) sect))
+ && body->type == TYPEMESSAGE
+ && !strucmp(body->subtype, "rfc822")){
+ env = body->nested.msg->env;
+ orig_body = body->nested.msg->body;
+ }
+ else{
+ sect = NULL;
+ env = mail_fetchstructure(ps_global->mail_stream, msgno, &orig_body);
+ if(!(env && orig_body)){
+ Tcl_SetResult(interp, "Unable to fetch message parts", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ }
+
+ if((prefix = Tcl_GetStringFromObj(objv[0], NULL)) != NULL)
+ prefix = cpystr(prefix);
+ else
+ prefix = reply_quote_str(env);
+
+ /*
+ * BUG? Should there be some way to signal to reply_bddy
+ * that we'd like it to produced format=flowed body text?
+ * right now it's hardwired to in pine/reply.c
+ */
+
+ if((body = reply_body(ps_global->mail_stream, env, orig_body,
+ msgno, sect, msgtext, prefix,
+ TRUE, NULL, TRUE, &redraft_pos)) != NULL){
+
+ objBody = Tcl_NewListObj(0, NULL);
+
+ peSoStrToList(interp, objBody, msgtext);
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objBody);
+
+ /* sniff for attachments */
+ objAttach = peMsgAttachCollector(interp, body);
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAttach);
+
+
+ pine_free_body(&body);
+ }
+ else{
+ Tcl_SetResult(interp, "Can't create body text", TCL_VOLATILE);
+ rv = TCL_ERROR;
+ }
+
+ fs_give((void **) &prefix);
+
+ return(rv);
+}
+
+
+int
+peSoStrToList(Tcl_Interp *interp, Tcl_Obj *obj, STORE_S *so)
+{
+ char *sp, *ep;
+ Tcl_Obj *objp;
+
+ for(ep = (char *) so_text(so); *ep; ep++){
+ sp = ep;
+
+ while(*ep && *ep != '\n')
+ ep++;
+
+ objp = Tcl_NewStringObj(sp, ep - sp);
+
+ if(Tcl_ListObjAppendElement(interp, obj, objp) != TCL_OK)
+ return(FALSE);
+ }
+
+ return(TRUE);
+}
+
+
+/*
+ * peForwardHeaders - return subject used in forward of given message
+ *
+ * Params:
+ *
+ * Returns:
+ *
+ */
+int
+peForwardHeaders(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+
+ int result;
+ long raw;
+ char *tmp, *sect = NULL;
+ ENVELOPE *env;
+ BODY *body;
+
+ raw = peSequenceNumber(uid);
+
+ /* if we're given a valid section number that
+ * corresponds to a valid msg/rfc822 body part
+ * then set up headers in attached message.
+ */
+ if(objc == 1 && objv[0]
+ && (sect = Tcl_GetStringFromObj(objv[0], NULL)) && *sect != '\0'
+ && (body = mail_body(ps_global->mail_stream, raw, (unsigned char *) sect))
+ && body->type == TYPEMESSAGE
+ && !strucmp(body->subtype, "rfc822")){
+ env = body->nested.msg->env;
+ }
+ else{
+ sect = NULL;
+ env = mail_fetchstructure(ps_global->mail_stream, raw, NULL);
+ }
+
+ if(env){
+ tmp = forward_subject(env, FS_NONE);
+ result = peAppListF(interp, Tcl_GetObjResult(interp),
+ "%s%s", "subject", tmp);
+ fs_give((void **) &tmp);
+
+ /* Fill in x-reply-uid data and append it */
+ if(result == TCL_OK && ps_global->mail_stream->uid_validity){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "()(1 %lu %lu)%s",
+ ps_global->mail_stream->uid_validity, uid,
+ ps_global->mail_stream->mailbox);
+ result = peAppListF(interp, Tcl_GetObjResult(interp), "%s%s",
+ "x-reply-uid", tmp_20k_buf) != TCL_OK;
+ }
+
+ return(result);
+ }
+
+ Tcl_SetResult(interp, ps_global->last_error, TCL_VOLATILE);
+ return(TCL_ERROR);
+}
+
+
+
+/*
+ * peForwardText - return body of message used in
+ * forward of given message
+ *
+ * Params:
+ *
+ * Returns:
+ *
+ */
+int
+peForwardText(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ long msgno;
+ char *bodtext, *p, *sect = NULL;
+ int rv = TCL_OK;
+ ENVELOPE *env;
+ BODY *body, *orig_body;
+ STORE_S *msgtext;
+ Tcl_Obj *objBody = NULL, *objAttach = NULL;
+
+ msgno = peSequenceNumber(uid);
+
+ if(objc == 1 && objv[0])
+ sect = Tcl_GetStringFromObj(objv[0], NULL);
+
+ if((msgtext = (void *) so_get(CharStar, NULL, EDIT_ACCESS)) == NULL){
+ Tcl_SetResult(interp, "Unable to create storage for forward text", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+
+ if(F_ON(F_FORWARD_AS_ATTACHMENT, ps_global)){
+ PART **pp;
+ long totalsize = 0L;
+
+ /*---- New Body to start with ----*/
+ body = mail_newbody();
+ body->type = TYPEMULTIPART;
+
+ /*---- The TEXT part/body ----*/
+ body->nested.part = mail_newbody_part();
+ body->nested.part->body.type = TYPETEXT;
+ body->nested.part->body.contents.text.data = (unsigned char *) msgtext;
+
+ pp = &(body->nested.part->next);
+
+ /*---- The Message body subparts ----*/
+ env = pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
+
+ if(forward_mime_msg(ps_global->mail_stream, msgno,
+ (sect && *sect != '\0') ? sect : NULL, env, pp, msgtext)){
+ totalsize = (*pp)->body.size.bytes;
+ pp = &((*pp)->next);
+ }
+ }
+ else{
+ /*--- Grab current envelope ---*/
+ /* if we're given a valid section number that
+ * corresponds to a valid msg/rfc822 body part
+ * then set up to forward the attached message's
+ * text.
+ */
+
+ if(sect && *sect != '\0'
+ && (body = mail_body(ps_global->mail_stream, msgno, (unsigned char *) sect))
+ && body->type == TYPEMESSAGE
+ && !strucmp(body->subtype, "rfc822")){
+ env = body->nested.msg->env;
+ orig_body = body->nested.msg->body;
+ }
+ else{
+ sect = NULL;
+ env = mail_fetchstructure(ps_global->mail_stream, msgno, &orig_body);
+ if(!(env && orig_body)){
+ Tcl_SetResult(interp, "Unable to fetch message parts", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ }
+
+ body = forward_body(ps_global->mail_stream, env, orig_body,
+ msgno, sect, msgtext, FWD_NONE);
+ }
+
+ if(body){
+ bodtext = (char *) so_text(msgtext);
+
+ objBody = Tcl_NewListObj(0, NULL);
+
+ for(p = bodtext; *p; p++){
+ Tcl_Obj *objp;
+
+ bodtext = p;
+ while(*p && *p != '\n')
+ p++;
+
+ objp = Tcl_NewStringObj(bodtext, p - bodtext);
+
+ Tcl_ListObjAppendElement(interp, objBody, objp);
+ }
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objBody);
+
+ /* sniff for attachments */
+ objAttach = peMsgAttachCollector(interp, body);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAttach);
+
+ pine_free_body(&body);
+ }
+ else{
+ Tcl_SetResult(interp, "Can't create body text", TCL_VOLATILE);
+ rv = TCL_ERROR;
+ }
+
+ return(rv);
+}
+
+
+
+/*
+ * peDetach -
+ *
+ * Params: argv[0] == attachment part number
+ * argv[1] == directory to hold tmp file
+ *
+ * Returns: list containing:
+ *
+ * 0) response: OK or ERROR
+ * if OK
+ * 1) attachment's mime type
+ * 2) attachment's mime sub-type
+ * 3) attachment's size in bytes (decoded)
+ * 4) attachment's given file name (if any)
+ * 5) tmp file holding raw attachment data
+ */
+int
+peDetach(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ char *part, *err, *tfd, *tfn = NULL, *filename;
+ long raw;
+ gf_io_t pc;
+ BODY *body;
+ STORE_S *store;
+ Tcl_Obj *rvobj, *tObj, *stObj, *fnObj;
+
+ if((part = Tcl_GetStringFromObj(objv[0], NULL))
+ && (raw = peSequenceNumber(uid))
+ && (body = mail_body(ps_global->mail_stream, raw, (unsigned char *) part))){
+
+ peGetMimeTyping(body, &tObj, &stObj, &fnObj, NULL);
+
+ err = NULL;
+ if(!(tfd = Tcl_GetStringFromObj(objv[1], NULL)) || *tfd == '\0'){
+ tfn = temp_nam(tfd = NULL, "pd");
+ }
+ else if(is_writable_dir(tfd) == 0){
+ tfn = temp_nam(tfd, "pd");
+ }
+ else
+ tfn = tfd;
+
+ filename = Tcl_GetStringFromObj(fnObj, NULL);
+ dprint((5, "PEDetach(name: %s, tmpfile: %s)",
+ filename ? filename : "<null>", tfn));
+
+ if((store = so_get(FileStar, tfn, WRITE_ACCESS|OWNER_ONLY)) != NULL){
+ gf_set_so_writec(&pc, store);
+ err = detach(ps_global->mail_stream, raw, part, 0L, NULL, pc, NULL, 0);
+ gf_clear_so_writec(store);
+ so_give(&store);
+ }
+ else
+ err = "Can't allocate internal storage";
+ }
+ else
+ err = "Can't get message data";
+
+ if(err){
+ if(tfn)
+ unlink(tfn);
+
+ dprint((1, "PEDetach FAIL: %d: %s", errno, err));
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Detach (%d): %s", errno, err);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ /* package up response */
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewListObj(1, &tObj));
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewListObj(1, &stObj));
+
+ rvobj = Tcl_NewLongObj(name_file_size(tfn));
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewListObj(1, &rvobj));
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewListObj(1, &fnObj));
+ rvobj = Tcl_NewStringObj(tfn, -1);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewListObj(1, &rvobj));
+
+ return(TCL_OK);
+}
+
+
+/*
+ * peAttachInfo -
+ *
+ * Params: argv[0] == attachment part number
+ *
+ * Returns: list containing:
+ *
+ * 0) response: OK or ERROR
+ * if OK
+ * 1) attachment's mime type
+ * 2) attachment's mime sub-type
+ * 3) attachment's size in bytes (decoded)
+ * 4) attachment's given file name (if any)
+ * 5) tmp file holding raw attachment data
+ */
+int
+peAttachInfo(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ char *part;
+ long raw;
+ BODY *body;
+ PARMLIST_S *plist;
+ Tcl_Obj *tObj, *stObj, *fnObj;
+
+ if((part = Tcl_GetStringFromObj(objv[0], NULL))
+ && (raw = peSequenceNumber(uid))
+ && (body = mail_body(ps_global->mail_stream, raw, (unsigned char *) part))){
+
+ peGetMimeTyping(body, &tObj, &stObj, &fnObj, NULL);
+ }
+ else{
+ Tcl_SetResult(interp, "Can't get message data", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ /* package up response */
+
+ /* filename */
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), fnObj);
+
+ /* type */
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
+
+ /* subtype */
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), stObj);
+
+ /* encoding */
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj((body->encoding < ENCMAX)
+ ? body_encodings[body->encoding]
+ : "Unknown", -1));
+
+ /* parameters */
+ if((plist = rfc2231_newparmlist(body->parameter)) != NULL){
+ Tcl_Obj *lObj = Tcl_NewListObj(0, NULL);
+ Tcl_Obj *pObj[2];
+
+ while(rfc2231_list_params(plist)){
+ pObj[0] = Tcl_NewStringObj(plist->attrib, -1);
+ pObj[1] = Tcl_NewStringObj(plist->value, -1);
+ Tcl_ListObjAppendElement(interp, lObj,
+ Tcl_NewListObj(2, pObj));
+ }
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), lObj);
+ rfc2231_free_parmlist(&plist);
+ }
+
+ /* size guesstimate */
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(comatose((body->encoding == ENCBASE64)
+ ? ((body->size.bytes * 3)/4)
+ : body->size.bytes), -1));
+
+ return(TCL_OK);
+}
+
+
+/*
+ * peSaveDefault - Default saved file name for the given message
+ * specified collection/folder
+ *
+ * Params:
+ *
+ * Returns: name of saved message folder or empty string
+ *
+ */
+int
+peSaveDefault(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ char *folder;
+ CONTEXT_S *cntxt, *cp;
+ int colid;
+ long rawno;
+ ENVELOPE *env;
+
+ if(uid){
+ if(!(env = pine_mail_fetchstructure(ps_global->mail_stream,
+ rawno = peSequenceNumber(uid),
+ NULL))){
+ Tcl_SetResult(interp, ps_global->last_error, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ }
+ else
+ env = NULL;
+
+ if(!(folder = save_get_default(ps_global, env, rawno, NULL, &cntxt))){
+ Tcl_SetResult(interp, "Message expunged!", TCL_VOLATILE);
+ return(TCL_ERROR); /* message expunged! */
+ }
+
+ for(colid = 0, cp = ps_global->context_list; cp && cp != cntxt ; colid++, cp = cp->next)
+ ;
+
+ if(!cp)
+ colid = 0;
+
+ (void) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewIntObj(colid));
+ (void) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(folder, -1));
+ return(TCL_OK);
+}
+
+
+/*
+ * peSaveWork - Save message with given UID in current folder to
+ * specified collection/folder
+ *
+ * Params: argv[0] == destination context number
+ * argv[1] == testination foldername
+ *
+ *
+ */
+int
+peSaveWork(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv, long sflags)
+{
+ int flgs = 0, i, colid;
+ char *folder, *err = NULL;
+ CONTEXT_S *cp;
+
+ if(Tcl_GetIntFromObj(interp, objv[0], &colid) != TCL_ERROR){
+ if((folder = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){
+ mn_set_cur(sp_msgmap(ps_global->mail_stream), peMessageNumber(uid));
+ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
+ if(i == colid)
+ break;
+
+ if(cp){
+ if(!READONLY_FOLDER(ps_global->mail_stream)
+ && (sflags & PSW_COPY) != PSW_COPY
+ && ((sflags & PSW_MOVE) == PSW_MOVE || F_OFF(F_SAVE_WONT_DELETE, ps_global)))
+ flgs |= SV_DELETE;
+
+ if(colid == 0 && !strucmp(folder, "inbox"))
+ flgs |= SV_INBOXWOCNTXT;
+
+ if(sflags & (PSW_COPY | PSW_MOVE))
+ flgs |= SV_FIX_DELS;
+
+ i = save(ps_global, ps_global->mail_stream,
+ cp, folder, sp_msgmap(ps_global->mail_stream), flgs);
+
+ if(i == mn_total_cur(sp_msgmap(ps_global->mail_stream))){
+ if(mn_total_cur(sp_msgmap(ps_global->mail_stream)) <= 1L){
+ if(ps_global->context_list->next
+ && context_isambig(folder)){
+ char *tag = (cp->nickname && strlen(cp->nickname)) ? cp->nickname : (cp->label && strlen(cp->label)) ? cp->label : "Folders";
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,
+ "Message %s %s to \"%.15s%s\" in <%.15s%s>",
+ long2string(mn_get_cur(sp_msgmap(ps_global->mail_stream))),
+ (sflags & PSW_MOVE) ? "moved" : "copied",
+ folder,
+ (strlen(folder) > 15) ? "..." : "",
+ tag,
+ (strlen(tag) > 15) ? "..." : "");
+ }
+ else
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,
+ "Message %s %s to folder \"%.27s%s\"",
+ long2string(mn_get_cur(sp_msgmap(ps_global->mail_stream))),
+ (sflags & PSW_MOVE) ? "moved" : "copied",
+ folder,
+ (strlen(folder) > 27) ? "..." : "");
+ }
+ else{
+ /* with mn_set_cur above, this *should not* happen */
+ Tcl_SetResult(interp, "TOO MANY MESSAGES COPIED", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ if(sflags == PSW_NONE && (flgs & SV_DELETE)){
+ strncat(tmp_20k_buf, " and deleted", SIZEOF_20KBUF-strlen(tmp_20k_buf)-1);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ }
+
+ q_status_message(SM_ORDER, 0, 3, tmp_20k_buf);
+ return(TCL_OK);
+ }
+
+ err = ps_global->last_error;
+ }
+ else
+ err = "open: Unrecognized collection ID";
+ }
+ else
+ err = "open: Can't read folder";
+ }
+ else
+ err = "open: Can't get collection ID";
+
+ Tcl_SetResult(interp, err, TCL_VOLATILE);
+ return(TCL_ERROR);
+}
+
+/*
+ * peSave - Save message with given UID in current folder to
+ * specified collection/folder
+ *
+ * Params: argv[0] == destination context number
+ * argv[1] == testination foldername
+ *
+ * NOTE: just a wrapper around peSaveWork
+ */
+int
+peSave(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ return(peSaveWork(interp, uid, objc, objv, PSW_NONE));
+}
+
+
+/*
+ * peCopy - Copy message with given UID in current folder to
+ * specified collection/folder
+ *
+ * Params: argv[0] == destination context number
+ * argv[1] == testination foldername
+ *
+ * NOTE: just a wrapper around peSaveWork that makes sure
+ * delete-on-save is NOT set
+ */
+int
+peCopy(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ return(peSaveWork(interp, uid, objc, objv, PSW_COPY));
+}
+
+
+/*
+ * peMove - Move message with given UID in current folder to
+ * specified collection/folder
+ *
+ * Params: argv[0] == destination context number
+ * argv[1] == testination foldername
+ *
+ * NOTE: just a wrapper around peSaveWork that makes sure
+ * delete-on-save IS set so it can be expunged
+ */
+int
+peMove(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ return(peSaveWork(interp, uid, objc, objv, PSW_MOVE));
+}
+
+
+/*
+ * peGotoDefault - Default Goto command file name for the given message
+ * specified collection/folder
+ *
+ * Params:
+ *
+ * Returns: name of Goto command default folder or empty string
+ *
+ */
+int
+peGotoDefault(Tcl_Interp *interp, imapuid_t uid, Tcl_Obj **objv)
+{
+ char *folder = NULL;
+ CONTEXT_S *cntxt, *cp;
+ int colid, inbox;
+
+ cntxt = broach_get_folder(ps_global->context_current, &inbox, &folder);
+
+ for(colid = 0, cp = ps_global->context_list; cp != cntxt ; colid++, cp = cp->next)
+ ;
+
+ if(!cp)
+ colid = 0;
+
+ (void) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewIntObj(colid));
+ (void) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(folder ? folder : "", -1));
+ return(TCL_OK);
+}
+
+
+/*
+ * peReplyQuote -
+ *
+ * Params: argv[0] == attachment part number
+ *
+ * Returns: list containing:
+ *
+ */
+int
+peReplyQuote(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ char *quote;
+ ENVELOPE *env;
+
+ if(uid){
+ if((env = pine_mail_fetchstructure(ps_global->mail_stream, peSequenceNumber(uid), NULL)) != NULL){
+ quote = reply_quote_str(env);
+ Tcl_SetResult(interp, quote, TCL_VOLATILE);
+ fs_give((void **) &quote);
+ }
+ else{
+ Tcl_SetResult(interp, ps_global->last_error, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ }
+ else
+ Tcl_SetResult(interp, "> ", TCL_VOLATILE);
+
+ return(TCL_OK);
+}
+
+
+void
+peGetMimeTyping(BODY *body, Tcl_Obj **tObjp, Tcl_Obj **stObjp, Tcl_Obj **fnObjp, Tcl_Obj **extObjp)
+{
+ char *ptype = NULL, *psubtype = NULL, *pfile = NULL;
+
+ /*------- Figure out suggested file name ----*/
+ if(body){
+ if((pfile = get_filename_parameter(NULL, 0, body, NULL)) != NULL){
+ /*
+ * if part is generic, see if we can get anything
+ * more from the suggested filename's extension...
+ */
+ if(body->type == TYPEAPPLICATION
+ && (!body->subtype
+ || !strucmp(body->subtype, "octet-stream"))){
+ BODY *fakebody = mail_newbody();
+
+ if(set_mime_type_by_extension(fakebody, pfile)){
+ ptype = body_type_names(fakebody->type);
+ psubtype = cpystr(fakebody->subtype);
+ }
+
+ mail_free_body(&fakebody);
+ }
+ }
+
+ if(!ptype) {
+ ptype = body_type_names(body->type);
+ psubtype = cpystr(body->subtype
+ ? body->subtype
+ : (body->type == TYPETEXT)
+ ? "plain"
+ : (body->type == TYPEAPPLICATION)
+ ? "octet-stream"
+ : "");
+ }
+ }
+ else{
+ ptype = body_type_names(TYPETEXT);
+ psubtype = cpystr("plain");
+ }
+
+ if(extObjp){
+ *extObjp = Tcl_NewStringObj("", 0);
+
+ if(ptype && psubtype && pfile){
+ size_t l;
+ char *mtype;
+ char extbuf[32]; /* mailcap.c limits to three */
+
+ l = strlen(ptype) + strlen(psubtype) + 1;
+ mtype = (char *) fs_get((l+1) * sizeof(char));
+
+ snprintf(mtype, l+1, "%s/%s", ptype, psubtype);
+
+ if(!set_mime_extension_by_type(extbuf, mtype)){
+ char *dotp, *p;
+
+ for(dotp = NULL, p = pfile; *p; p++)
+ if(*p == '.')
+ dotp = p + 1;
+
+ if(dotp)
+ Tcl_SetStringObj(*extObjp, dotp, -1);
+ }
+ else
+ Tcl_SetStringObj(*extObjp, extbuf, -1);
+
+ fs_give((void **) &mtype);
+ }
+ }
+
+ if(tObjp)
+ *tObjp = Tcl_NewStringObj(ptype, -1);
+
+ if(psubtype){
+ if(stObjp)
+ *stObjp = Tcl_NewStringObj(psubtype, -1);
+
+ fs_give((void **) &psubtype);
+ }
+ else if(stObjp)
+ *stObjp = Tcl_NewStringObj("", 0);
+
+ if(pfile){
+ if(fnObjp)
+ *fnObjp = Tcl_NewStringObj(pfile, -1);
+
+ fs_give((void **) &pfile);
+ }
+ else if(fnObjp)
+ *fnObjp = Tcl_NewStringObj("", 0);
+}
+
+
+/*
+ * peAppListF - generate a list of elements based on fmt string,
+ * then append it to the given list object
+ *
+ */
+int
+peAppListF(Tcl_Interp *interp, Tcl_Obj *lobjp, char *fmt, ...)
+{
+ va_list args;
+ char *p, *sval, nbuf[128];
+ int ival, err = 0;
+ unsigned int uval;
+ long lval;
+ unsigned long luval;
+ PATTERN_S *pval;
+ ADDRESS *aval;
+ INTVL_S *vval;
+ Tcl_Obj *lObj = NULL, *sObj;
+
+ if((lObj = Tcl_NewListObj(0, NULL)) != NULL){
+ va_start(args, fmt);
+ for(p = fmt; *p && !err; p++){
+ sObj = NULL;
+
+ if(*p == '%')
+ switch(*++p){
+ case 'i' : /* int value */
+ ival = va_arg(args, int);
+ if((sObj = Tcl_NewIntObj(ival)) == NULL)
+ err++;
+
+ break;
+
+ case 'u' : /* unsigned int value */
+ uval = va_arg(args, unsigned int);
+ snprintf(nbuf, sizeof(nbuf), "%u", uval);
+ if((sObj = Tcl_NewStringObj(nbuf, -1)) == NULL)
+ err++;
+
+ break;
+
+ case 'l' : /* long value */
+ if(*(p+1) == 'u'){
+ p++;
+ luval = va_arg(args, unsigned long);
+ snprintf(nbuf, sizeof(nbuf), "%lu", luval);
+ if((sObj = Tcl_NewStringObj(nbuf, -1)) == NULL)
+ err++;
+ }
+ else{
+ lval = va_arg(args, long);
+ if((sObj = Tcl_NewLongObj(lval)) == NULL)
+ err++;
+ }
+
+ break;
+
+ case 's' : /* string value */
+ sval = va_arg(args, char *);
+ sObj = Tcl_NewStringObj(sval ? sval : "", -1);
+ if(sObj == NULL)
+ err++;
+
+ break;
+
+ case 'a': /* ADDRESS list */
+ aval = va_arg(args, ADDRESS *);
+ if(aval){
+ char *tmp, *p;
+ RFC822BUFFER rbuf;
+ size_t len;
+
+ len = est_size(aval);
+ tmp = (char *) fs_get(len * sizeof(char));
+ tmp[0] = '\0';
+ rbuf.f = dummy_soutr;
+ rbuf.s = NULL;
+ rbuf.beg = tmp;
+ rbuf.cur = tmp;
+ rbuf.end = tmp+len-1;
+ rfc822_output_address_list(&rbuf, aval, 0L, NULL);
+ *rbuf.cur = '\0';
+ p = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf, SIZEOF_20KBUF, tmp);
+ sObj = Tcl_NewStringObj(p, strlen(p));
+ fs_give((void **) &tmp);
+ }
+ else
+ sObj = Tcl_NewStringObj("", -1);
+
+ break;
+
+ case 'p': /* PATTERN_S * */
+ pval = va_arg(args, PATTERN_S *);
+ sval = pattern_to_string(pval);
+ sObj = Tcl_NewStringObj(sval ? sval : "", -1);
+ break;
+
+ case 'v': /* INTVL_S * */
+ vval = va_arg(args, INTVL_S *);
+ if(vval){
+ for(; vval != NULL; vval = vval->next){
+ peAppListF(interp, sObj, "%l%l", vval->imin, vval->imax);
+ }
+ }
+ else
+ sObj = Tcl_NewListObj(0, NULL);
+
+ break;
+
+ case 'o': /* Tcl_Obj * */
+ sObj = va_arg(args, Tcl_Obj *);
+ break;
+ }
+
+ if(sObj)
+ Tcl_ListObjAppendElement(interp, lObj, sObj);
+ }
+
+ va_end(args);
+ }
+
+ return(lObj ? Tcl_ListObjAppendElement(interp, lobjp, lObj) : TCL_ERROR);
+}
+
+/*
+ * pePatAppendID - append list of pattern identity variables to given object
+ */
+void
+pePatAppendID(Tcl_Interp *interp, Tcl_Obj *patObj, PAT_S *pat)
+{
+ Tcl_Obj *resObj;
+
+ resObj = Tcl_NewListObj(0, NULL);
+ peAppListF(interp, resObj, "%s%s", "nickname", pat->patgrp->nick);
+ peAppListF(interp, resObj, "%s%s", "comment", pat->patgrp->comment);
+ Tcl_ListObjAppendElement(interp, patObj, resObj);
+}
+
+
+/*
+ * pePatAppendPattern - append list of pattern variables to given object
+ */
+void
+pePatAppendPattern(Tcl_Interp *interp, Tcl_Obj *patObj, PAT_S *pat)
+{
+ ARBHDR_S *ah;
+ Tcl_Obj *resObj;
+
+ resObj = Tcl_NewListObj(0, NULL);
+ peAppListF(interp, resObj, "%s%p", "to", pat->patgrp->to);
+ peAppListF(interp, resObj, "%s%p", "from", pat->patgrp->from);
+ peAppListF(interp, resObj, "%s%p", "sender", pat->patgrp->sender);
+ peAppListF(interp, resObj, "%s%p", "cc", pat->patgrp->cc);
+ peAppListF(interp, resObj, "%s%p", "recip", pat->patgrp->recip);
+ peAppListF(interp, resObj, "%s%p", "partic", pat->patgrp->partic);
+ peAppListF(interp, resObj, "%s%p", "news", pat->patgrp->news);
+ peAppListF(interp, resObj, "%s%p", "subj", pat->patgrp->subj);
+ peAppListF(interp, resObj, "%s%p", "alltext", pat->patgrp->alltext);
+ peAppListF(interp, resObj, "%s%p", "bodytext", pat->patgrp->bodytext);
+ peAppListF(interp, resObj, "%s%p", "keyword", pat->patgrp->keyword);
+ peAppListF(interp, resObj, "%s%p", "charset", pat->patgrp->charsets);
+
+ peAppListF(interp, resObj, "%s%v", "score", pat->patgrp->score);
+ peAppListF(interp, resObj, "%s%v", "age", pat->patgrp->age);
+ peAppListF(interp, resObj, "%s%v", "size", pat->patgrp->size);
+
+ if((ah = pat->patgrp->arbhdr) != NULL){
+ Tcl_Obj *hlObj, *hObj;
+
+ hlObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, hlObj, Tcl_NewStringObj("headers", -1));
+
+ for(; ah; ah = ah->next){
+ hObj = Tcl_NewListObj(0, NULL);
+ peAppListF(interp, hObj, "%s%p", ah->field ? ah->field : "", ah->p);
+ Tcl_ListObjAppendElement(interp, hlObj, hObj);
+ }
+
+ Tcl_ListObjAppendElement(interp, resObj, hlObj);
+ }
+
+ switch(pat->patgrp->fldr_type){
+ case FLDR_ANY:
+ peAppListF(interp, resObj, "%s%s", "ftype", "any");
+ break;
+ case FLDR_NEWS:
+ peAppListF(interp, resObj, "%s%s", "ftype", "news");
+ break;
+ case FLDR_EMAIL:
+ peAppListF(interp, resObj, "%s%s", "ftype", "email");
+ break;
+ case FLDR_SPECIFIC:
+ peAppListF(interp, resObj, "%s%s", "ftype", "specific");
+ break;
+ }
+
+ peAppListF(interp, resObj, "%s%p", "folder", pat->patgrp->folder);
+ peAppListF(interp, resObj, "%s%s", "stat_new", pePatStatStr(pat->patgrp->stat_new));
+ peAppListF(interp, resObj, "%s%s", "stat_rec", pePatStatStr(pat->patgrp->stat_rec));
+ peAppListF(interp, resObj, "%s%s", "stat_del", pePatStatStr(pat->patgrp->stat_del));
+ peAppListF(interp, resObj, "%s%s", "stat_imp", pePatStatStr(pat->patgrp->stat_imp));
+ peAppListF(interp, resObj, "%s%s", "stat_ans", pePatStatStr(pat->patgrp->stat_ans));
+ peAppListF(interp, resObj, "%s%s", "stat_8bitsubj", pePatStatStr(pat->patgrp->stat_8bitsubj));
+ peAppListF(interp, resObj, "%s%s", "stat_bom", pePatStatStr(pat->patgrp->stat_bom));
+ peAppListF(interp, resObj, "%s%s", "stat_boy", pePatStatStr(pat->patgrp->stat_boy));
+
+ Tcl_ListObjAppendElement(interp, patObj, resObj);
+}
+
+
+char *
+pePatStatStr(int value)
+{
+ switch(value){
+ case PAT_STAT_EITHER:
+ return("either");
+ break;
+
+ case PAT_STAT_YES:
+ return("yes");
+ break;
+
+ default :
+ return("no");
+ break;
+ }
+}
+
+
+/*
+ * peCreateUserContext - create new ps_global and set it up
+ */
+char *
+peCreateUserContext(Tcl_Interp *interp, char *user, char *config, char *defconf)
+{
+ if(ps_global)
+ peDestroyUserContext(&ps_global);
+
+ set_collation(1, 1);
+
+ ps_global = new_pine_struct();
+
+ /*----------------------------------------------------------------------
+ Place any necessary constraints on pith processing
+ ----------------------------------------------------------------------*/
+
+ /* got thru close procedure without expunging */
+ ps_global->noexpunge_on_close = 1;
+
+ /* do NOT let user set path to local executable */
+ ps_global->vars[V_SENDMAIL_PATH].is_user = 0;
+
+
+ /*----------------------------------------------------------------------
+ Proceed with reading acquiring user settings
+ ----------------------------------------------------------------------*/
+ if(ps_global->pinerc)
+ fs_give((void **) &ps_global->pinerc);
+
+ if(ps_global->prc)
+ free_pinerc_s(&ps_global->prc);
+
+ if(!IS_REMOTE(config)){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Non-Remote config: %s", config);
+ return(tmp_20k_buf);
+ }
+
+ ps_global->prc = new_pinerc_s(config);
+
+ if(defconf){
+ if(ps_global->pconf)
+ free_pinerc_s(&ps_global->pconf);
+
+ ps_global->pconf = new_pinerc_s(defconf);
+ }
+
+ /*
+ * Fake up some user information
+ */
+ ps_global->ui.login = cpystr(user);
+
+#ifdef DEBUG
+ /*
+ * Prep for IMAP debugging
+ */
+ setup_imap_debug();
+#endif
+
+ /* CHECK FOR AND PASS BACK ANY INIT ERRORS */
+ return(peLoadConfig(ps_global));
+}
+
+
+
+void
+peDestroyUserContext(struct pine **pps)
+{
+
+ completely_done_with_adrbks();
+
+ free_pinerc_strings(pps);
+#if 0
+ imap_flush_passwd_cache(TRUE);
+#endif
+ clear_index_cache(sp_inbox_stream(), 0);
+ free_newsgrp_cache();
+ mailcap_free();
+ close_patterns(0L);
+ free_extra_hdrs();
+ free_contexts(&ps_global->context_list);
+
+ pico_endcolor();
+
+ free_strlist(&peCertHosts);
+
+ free_pine_struct(pps);
+}
+
+
+char *
+peLoadConfig(struct pine *pine_state)
+{
+ int rv;
+ char *s, *db = NULL;
+ extern void init_signals(void); /* in signal.c */
+
+ if(!pine_state)
+ return("No global state present");
+
+#if 0
+/*turned off because we don't care about local user*/
+ /* need home directory early */
+ get_user_info(&pine_state->ui);
+
+ pine_state->home_dir = cpystr((getenv("HOME") != NULL)
+ ? getenv("HOME")
+ : pine_state->ui.homedir);
+#endif
+
+ init_pinerc(pine_state, &db);
+
+ fs_give((void **) &db);
+
+ /*
+ * Initial allocation of array of stream pool pointers.
+ * We do this before init_vars so that we can re-use streams used for
+ * remote config files. These sizes may get changed later.
+ */
+ ps_global->s_pool.max_remstream = 2;
+
+ ps_global->c_client_error[0] = '\0';
+
+ pePrepareForAuthException();
+
+ peInitVars(pine_state);
+
+ if(s = peAuthException())
+ return(s);
+ else if(ps_global->c_client_error[0])
+ return(ps_global->c_client_error);
+
+ mail_parameters(NULL, SET_SENDCOMMAND, (void *) pine_imap_cmd_happened);
+ mail_parameters(NULL, SET_FREESTREAMSPAREP, (void *) sp_free_callback);
+ mail_parameters(NULL, SET_FREEELTSPAREP, (void *) free_pine_elt);
+
+ /*
+ * Install callback to handle certificate validation failures,
+ * allowing the user to continue if they wish.
+ */
+ mail_parameters(NULL, SET_SSLCERTIFICATEQUERY, (void *) alpine_sslcertquery);
+ mail_parameters(NULL, SET_SSLFAILURE, (void *) alpine_sslfailure);
+
+ /*
+ * Set up a c-client read timeout and timeout handler. In general,
+ * it shouldn't happen, but a server crash or dead link can cause
+ * pine to appear wedged if we don't set this up...
+ */
+ mail_parameters(NULL, SET_OPENTIMEOUT,
+ (void *)((pine_state->VAR_TCPOPENTIMEO
+ && (rv = atoi(pine_state->VAR_TCPOPENTIMEO)) > 4)
+ ? (long) rv : 30L));
+ mail_parameters(NULL, SET_TIMEOUT, (void *) alpine_tcptimeout);
+
+ if(pine_state->VAR_RSHOPENTIMEO
+ && ((rv = atoi(pine_state->VAR_RSHOPENTIMEO)) == 0 || rv > 4))
+ mail_parameters(NULL, SET_RSHTIMEOUT, (void *) rv);
+
+ if(pine_state->VAR_SSHOPENTIMEO
+ && ((rv = atoi(pine_state->VAR_SSHOPENTIMEO)) == 0 || rv > 4))
+ mail_parameters(NULL, SET_SSHTIMEOUT, (void *) rv);
+
+ /*
+ * Tell c-client not to be so aggressive about uid mappings
+ */
+ mail_parameters(NULL, SET_UIDLOOKAHEAD, (void *) 20);
+
+ /*
+ * Setup referral handling
+ */
+ mail_parameters(NULL, SET_IMAPREFERRAL, (void *) imap_referral);
+ mail_parameters(NULL, SET_MAILPROXYCOPY, (void *) imap_proxycopy);
+
+ /*
+ * Install extra headers to fetch along with all the other stuff
+ * mail_fetch_structure and mail_fetch_overview requests.
+ */
+ calc_extra_hdrs();
+
+ if(get_extra_hdrs())
+ (void) mail_parameters(NULL, SET_IMAPEXTRAHEADERS, (void *) get_extra_hdrs());
+
+ (void) init_username(pine_state);
+
+ (void) init_hostname(ps_global);
+
+#ifdef ENABLE_LDAP
+ (void) init_ldap_pname(ps_global);
+#endif /* ENABLE_LDAP */
+
+ if(ps_global->prc && ps_global->prc->type == Loc &&
+ can_access(ps_global->pinerc, ACCESS_EXISTS) == 0 &&
+ can_access(ps_global->pinerc, EDIT_ACCESS) != 0)
+ ps_global->readonly_pinerc = 1;
+
+ /*
+ * c-client needs USR2 and we might as well
+ * do something sensible with HUP and TERM
+ */
+ init_signals();
+
+ strncpy(pine_state->inbox_name, INBOX_NAME, sizeof(pine_state->inbox_name));
+ pine_state->inbox_name[sizeof(pine_state->inbox_name)-1] = '\0';
+
+ init_folders(pine_state); /* digest folder spec's */
+
+ /*
+ * Various options we want to make sure are set OUR way
+ */
+ F_TURN_ON(F_QUELL_IMAP_ENV_CB, pine_state);
+ F_TURN_ON(F_SLCTBL_ITEM_NOBOLD, pine_state);
+ F_TURN_OFF(F_USE_SYSTEM_TRANS, pine_state);
+
+ /*
+ * Fake screen dimensions for index formatting and
+ * message display wrap...
+ */
+ ps_global->ttyo = (struct ttyo *) fs_get(sizeof(struct ttyo));
+ ps_global->ttyo->screen_rows = FAKE_SCREEN_LENGTH;
+ ps_global->ttyo->screen_cols = FAKE_SCREEN_WIDTH;
+ if(ps_global->VAR_WP_COLUMNS){
+ int w = atoi(ps_global->VAR_WP_COLUMNS);
+ if(w >= 20 && w <= 128)
+ ps_global->ttyo->screen_cols = w;
+ }
+
+ ps_global->ttyo->header_rows = 0;
+ ps_global->ttyo->footer_rows = 0;
+
+
+ /* init colors */
+ if(ps_global->VAR_NORM_FORE_COLOR)
+ pico_nfcolor(ps_global->VAR_NORM_FORE_COLOR);
+
+ if(ps_global->VAR_NORM_BACK_COLOR)
+ pico_nbcolor(ps_global->VAR_NORM_BACK_COLOR);
+
+ if(ps_global->VAR_REV_FORE_COLOR)
+ pico_rfcolor(ps_global->VAR_REV_FORE_COLOR);
+
+ if(ps_global->VAR_REV_BACK_COLOR)
+ pico_rbcolor(ps_global->VAR_REV_BACK_COLOR);
+
+ pico_set_normal_color();
+
+ return(NULL);
+}
+
+
+int
+peCreateStream(Tcl_Interp *interp, CONTEXT_S *context, char *mailbox, int do_inbox)
+{
+ unsigned long flgs = 0L;
+ char *s;
+
+ ps_global->c_client_error[0] = ps_global->last_error[0] = '\0';
+
+ pePrepareForAuthException();
+
+ if(do_inbox)
+ flgs |= DB_INBOXWOCNTXT;
+
+ if(do_broach_folder(mailbox, context, NULL, flgs) && ps_global->mail_stream){
+ dprint((SYSDBG_INFO, "Mailbox open: %s",
+ ps_global->mail_stream->mailbox ? ps_global->mail_stream->mailbox : "<UNKNOWN>"));
+ return(TCL_OK);
+ }
+
+ Tcl_SetResult(interp,
+ (s = peAuthException())
+ ? s
+ : (*ps_global->last_error)
+ ? ps_global->last_error
+ : "Login Error",
+ TCL_VOLATILE);
+ return(TCL_ERROR);
+}
+
+
+void
+peDestroyStream(struct pine *ps)
+{
+ int cur_is_inbox;
+
+ if(ps){
+ cur_is_inbox = (sp_inbox_stream() == ps_global->mail_stream);
+
+ /* clean up open streams */
+ if(ps->mail_stream){
+ expunge_and_close(ps->mail_stream, NULL, EC_NONE);
+ ps_global->mail_stream = NULL;
+ ps_global->cur_folder[0] = '\0';
+ }
+
+ if(ps->msgmap)
+ mn_give(&ps->msgmap);
+
+ if(sp_inbox_stream() && !cur_is_inbox){
+ ps->mail_stream = sp_inbox_stream();
+ ps->msgmap = sp_msgmap(ps->mail_stream);
+ sp_set_expunge_count(ps_global->mail_stream, 0L);
+ expunge_and_close(sp_inbox_stream(), NULL, EC_NONE);
+ mn_give(&ps->msgmap);
+ }
+ }
+}
+
+
+/*
+ * pePrepareForAuthException - set globals to get feedback from bowels of c-client
+ */
+void
+pePrepareForAuthException(void)
+{
+ peNoPassword = peCredentialError = peCertFailure = peCertQuery = 0;
+}
+
+/*
+ * pePrepareForAuthException - check globals getting feedback from bowels of c-client
+ */
+char *
+peAuthException()
+{
+ static char buf[CRED_REQ_SIZE];
+
+ if(peCertQuery){
+ snprintf(buf, CRED_REQ_SIZE, "%s %s", CERT_QUERY_STRING, peCredentialRequestor);
+ return(buf);
+ }
+
+ if(peCertFailure){
+ snprintf(buf, CRED_REQ_SIZE, "%s %s", CERT_FAILURE_STRING, peCredentialRequestor);
+ return(buf);
+ }
+
+ if(peNoPassword){
+ snprintf(buf, CRED_REQ_SIZE, "%s %s", AUTH_EMPTY_STRING, peCredentialRequestor);
+ return(buf);
+ }
+
+ if(peCredentialError){
+ snprintf(buf, CRED_REQ_SIZE, "%s %s", AUTH_FAILURE_STRING, peCredentialRequestor);
+ return(buf);
+ }
+
+ return(NULL);
+}
+
+
+void
+peInitVars(struct pine *ps)
+{
+ init_vars(ps, NULL);
+
+ /*
+ * fix display/keyboard-character-set to utf-8
+ *
+ *
+ */
+
+ if(ps->display_charmap)
+ fs_give((void **) &ps->display_charmap);
+
+ ps->display_charmap = cpystr(WP_INTERNAL_CHARSET);
+
+ if(ps->keyboard_charmap)
+ fs_give((void **) &ps->keyboard_charmap);
+
+ ps->keyboard_charmap = cpystr(WP_INTERNAL_CHARSET);
+
+ (void) setup_for_input_output(FALSE, &ps->display_charmap, &ps->keyboard_charmap, &ps->input_cs, NULL);;
+}
+
+
+
+int
+peMessageBounce(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ char *errstr = NULL, *to, *subj = NULL, errbuf[WP_MAX_POST_ERROR + 1];
+ long rawno;
+ ENVELOPE *env, *outgoing = NULL;
+ METAENV *metaenv;
+ PINEFIELD *custom;
+ BODY *body = NULL;
+
+ if(uid){
+ rawno = peSequenceNumber(uid);
+
+ if(objc > 0 && objv[0] && (to = Tcl_GetStringFromObj(objv[0], NULL))){
+ if(objc == 2 && objv[1]){
+ subj = Tcl_GetStringFromObj(objv[1], NULL);
+ }
+ else if((env = mail_fetchstructure(ps_global->mail_stream, rawno, NULL)) != NULL){
+ subj = env->subject;
+ }
+ else{
+ Tcl_SetResult(interp, ps_global->last_error, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ if((errstr = bounce_msg_body(ps_global->mail_stream, rawno, NULL,
+ &to, subj, &outgoing, &body, NULL))){
+ Tcl_SetResult(interp, errstr, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ metaenv = pine_new_env(outgoing, NULL, NULL, custom = peCustomHdrs());
+
+ if(!outgoing->from)
+ outgoing->from = generate_from();
+
+ rfc822_date(tmp_20k_buf);
+ outgoing->date = (unsigned char *) cpystr(tmp_20k_buf);
+
+ outgoing->return_path = rfc822_cpy_adr(outgoing->from);
+ if(!outgoing->message_id)
+ outgoing->message_id = generate_message_id();
+
+ /* NO FCC */
+
+ if(peDoPost(metaenv, body, NULL, NULL, errbuf) != TCL_OK)
+ errstr = errbuf;
+
+ pine_free_body(&body);
+
+ pine_free_env(&metaenv);
+
+ if(custom)
+ free_customs(custom);
+
+ mail_free_envelope(&outgoing);
+ pine_free_body(&body);
+
+ }
+ }
+
+ Tcl_SetResult(interp, (errstr) ? errstr : "OK", TCL_VOLATILE);
+ return(errstr ? TCL_ERROR : TCL_OK);
+}
+
+
+int
+peMessageSpamNotice(interp, uid, objc, objv)
+ Tcl_Interp *interp;
+ imapuid_t uid;
+ int objc;
+ Tcl_Obj **objv;
+{
+ char *to, *subj = NULL, errbuf[WP_MAX_POST_ERROR + 1], *rs = NULL;
+ long rawno;
+
+ if(uid){
+ rawno = peSequenceNumber(uid);
+
+ if(objv[0] && (to = Tcl_GetStringFromObj(objv[0], NULL)) && strlen(to)){
+
+ if(objv[1])
+ subj = Tcl_GetStringFromObj(objv[1], NULL);
+
+ rs = peSendSpamReport(rawno, to, subj, errbuf);
+ }
+ }
+
+ Tcl_SetResult(interp, (rs) ? rs : "OK", TCL_VOLATILE);
+ return(rs ? TCL_ERROR : TCL_OK);
+}
+
+
+char *
+peSendSpamReport(long rawno, char *to, char *subj, char *errbuf)
+{
+ char *errstr = NULL, *tmp_a_string;
+ ENVELOPE *env, *outgoing;
+ METAENV *metaenv;
+ PINEFIELD *custom;
+ BODY *body;
+ static char *fakedomain = "@";
+ void *msgtext;
+
+
+ if((env = mail_fetchstructure(ps_global->mail_stream, rawno, NULL)) == NULL){
+ return(ps_global->last_error);
+ }
+
+ /* empty subject gets "spam" subject */
+ if(!(subj && *subj))
+ subj = env->subject;
+
+ /*---- New Body to start with ----*/
+ body = mail_newbody();
+ body->type = TYPEMULTIPART;
+
+ /*---- The TEXT part/body ----*/
+ body->nested.part = mail_newbody_part();
+ body->nested.part->body.type = TYPETEXT;
+
+ if((msgtext = (void *)so_get(CharStar, NULL, EDIT_ACCESS)) == NULL){
+ pine_free_body(&body);
+ return("peSendSpamReport: Can't allocate text");
+ }
+ else{
+ sprintf(tmp_20k_buf,
+ "The attached message is being reported to <%s> as Spam\n",
+ to);
+ so_puts((STORE_S *) msgtext, tmp_20k_buf);
+ body->nested.part->body.contents.text.data = msgtext;
+ }
+
+ /*---- Attach the raw message ----*/
+ if(forward_mime_msg(ps_global->mail_stream, rawno, NULL, env,
+ &(body->nested.part->next), msgtext)){
+ outgoing = mail_newenvelope();
+ metaenv = pine_new_env(outgoing, NULL, NULL, custom = peCustomHdrs());
+ }
+ else{
+ pine_free_body(&body);
+ return("peSendSpamReport: Can't generate forwarded message");
+ }
+
+ /* rfc822_parse_adrlist feels free to destroy input so copy */
+ tmp_a_string = cpystr(to);
+ rfc822_parse_adrlist(&outgoing->to, tmp_a_string,
+ (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global))
+ ? fakedomain : ps_global->maildomain);
+ fs_give((void **) &tmp_a_string);
+
+ outgoing->from = generate_from();
+ outgoing->subject = cpystr(subj);
+ outgoing->return_path = rfc822_cpy_adr(outgoing->from);
+ outgoing->message_id = generate_message_id();
+
+ rfc822_date(tmp_20k_buf);
+ outgoing->date = (unsigned char *) cpystr(tmp_20k_buf);
+
+ /* NO FCC for Spam Reporting */
+
+ if(peDoPost(metaenv, body, NULL, NULL, errbuf) != TCL_OK)
+ errstr = errbuf;
+
+ pine_free_body(&body);
+
+ pine_free_env(&metaenv);
+
+ if(custom)
+ free_customs(custom);
+
+ mail_free_envelope(&outgoing);
+ pine_free_body(&body);
+
+ return(errstr);
+}
+
+
+/* * * * * * * * * * * * * Start of Composer Routines * * * * * * * * * * * */
+
+
+/*
+ * PEComposeCmd - export various bits of alpine state
+ */
+int
+PEComposeCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+ char *err = "PECompose: Unknown request";
+
+ dprint((2, "PEComposeCmd"));
+
+ if(objc == 1){
+ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
+ }
+ else if(!ps_global){
+ Tcl_SetResult(interp, "peCompose: no config present", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ else{
+ char *s1 = Tcl_GetStringFromObj(objv[1], NULL);
+
+ if(s1){
+ if(!strcmp(s1, "post")){
+ long flags = PMC_NONE;
+ if(F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global))
+ flags |= PMC_FORCE_QUAL;
+
+ return(peMsgCollector(interp, objc - 2, (Tcl_Obj **) &objv[2], peDoPost, flags));
+ }
+ else if(objc == 2){
+ if(!strcmp(s1, "userhdrs")){
+ int i;
+ char *p;
+ PINEFIELD *custom, *cp;
+ ADDRESS *from;
+ static char *standard[] = {"To", "Cc", "Bcc", "Fcc", "Attach", "Subject", NULL};
+
+ custom = peCustomHdrs();
+
+ for(i = 0; standard[i]; i++){
+ p = NULL;
+ for(cp = custom; cp; cp = cp->next)
+ if(!strucmp(cp->name, standard[i]))
+ p = cp->textbuf;
+
+ peAppListF(interp, Tcl_GetObjResult(interp), "%s%s", standard[i], p);
+ }
+
+ for(cp = custom; cp != NULL; cp = cp->next){
+ if(!strucmp(cp->name, "from")){
+ if(F_OFF(F_ALLOW_CHANGING_FROM, ps_global))
+ continue;
+
+ if(cp->textbuf && strlen(cp->textbuf)){
+ p = cp->textbuf;
+ }
+ else{
+ RFC822BUFFER rbuf;
+
+ tmp_20k_buf[0] = '\0';
+ rbuf.f = dummy_soutr;
+ rbuf.s = NULL;
+ rbuf.beg = tmp_20k_buf;
+ rbuf.cur = tmp_20k_buf;
+ rbuf.end = tmp_20k_buf+SIZEOF_20KBUF-1;
+ rfc822_output_address_list(&rbuf, from = generate_from(), 0L, NULL);
+ *rbuf.cur = '\0';
+ mail_free_address(&from);
+ p = tmp_20k_buf;
+ }
+ }
+ else{
+ p = cp->textbuf;
+ for(i = 0; standard[i]; i++)
+ if(!strucmp(standard[i], cp->name))
+ p = NULL;
+
+ if(!p)
+ continue;
+ }
+
+ peAppListF(interp, Tcl_GetObjResult(interp), "%s%s", cp->name, p);
+ }
+
+ if(custom)
+ free_customs(custom);
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "syshdrs")){
+ int i;
+ static char *extras[] = {"In-Reply-To", "X-Reply-UID", NULL};
+
+ for(i = 0; extras[i]; i++)
+ peAppListF(interp, Tcl_GetObjResult(interp), "%s%s", extras[i], NULL);
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "composehdrs")){
+ char **p, *q;
+
+ if((p = ps_global->VAR_COMP_HDRS) && *p){
+ for(; *p; p++)
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(*p, (q = strchr(*p, ':'))
+ ? (q - *p) : -1));
+ }
+ else
+ Tcl_SetResult(interp, "", TCL_STATIC);
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "fccdefault")){
+ int ci = 0;
+ CONTEXT_S *c = default_save_context(ps_global->context_list), *c2;
+
+ for(c2 = ps_global->context_list; c && c != c2; c2 = c2->next)
+ ci++;
+
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewIntObj(ci));
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(ps_global->VAR_DEFAULT_FCC
+ ? ps_global->VAR_DEFAULT_FCC
+ : "", -1));
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "noattach")){
+ peFreeAttach(&peCompAttach);
+ Tcl_SetResult(interp, "OK", TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "from")){
+ RFC822BUFFER rbuf;
+ ADDRESS *from = generate_from();
+ tmp_20k_buf[0] = '\0';
+ rbuf.f = dummy_soutr;
+ rbuf.s = NULL;
+ rbuf.beg = tmp_20k_buf;
+ rbuf.cur = tmp_20k_buf;
+ rbuf.end = tmp_20k_buf+SIZEOF_20KBUF-1;
+ rfc822_output_address_list(&rbuf, from, 0L, NULL);
+ *rbuf.cur = '\0';
+ mail_free_address(&from);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "attachments")){
+ COMPATT_S *p;
+ Tcl_Obj *objAttach;
+
+ for(p = peCompAttach; p; p = p->next)
+ if(p->file){
+ objAttach = Tcl_NewListObj(0, NULL);
+
+ /* id */
+ Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(p->id,-1));
+
+ /* file name */
+ Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(p->l.f.remote,-1));
+
+ /* file size */
+ Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewLongObj(p->l.f.size));
+
+ /* type/subtype */
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s/%s", p->l.f.type, p->l.f.subtype);
+ Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(tmp_20k_buf,-1));
+
+ /* append to list */
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAttach);
+ }
+ else if(p->body){
+ char *name;
+
+ objAttach = Tcl_NewListObj(0, NULL);
+
+ /* id */
+ Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(p->id,-1));
+
+ /* file name */
+ if((name = get_filename_parameter(NULL, 0, p->l.b.body, NULL)) != NULL){
+ Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(name, -1));
+ fs_give((void **) &name);
+ }
+ else
+ Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj("Unknown", -1));
+
+ /* file size */
+ Tcl_ListObjAppendElement(interp, objAttach,
+ Tcl_NewLongObj((p->l.b.body->encoding == ENCBASE64)
+ ? ((p->l.b.body->size.bytes * 3)/4)
+ : p->l.b.body->size.bytes));
+
+ /* type/subtype */
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s/%s",
+ body_type_names(p->l.b.body->type),
+ p->l.b.body->subtype ? p->l.b.body->subtype : "Unknown");
+ Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(tmp_20k_buf, -1));
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAttach);
+ }
+
+ return(TCL_OK);
+ }
+ }
+ else if(objc == 3){
+ if(!strcmp(s1, "unattach")){
+ char *id;
+
+ if((id = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
+ if(peClearAttachID(id)){
+ Tcl_SetResult(interp, "OK", TCL_STATIC);
+ return(TCL_OK);
+ }
+ else
+ err = "Can't access attachment id";
+ }
+ else
+ err = "Can't read attachment id";
+ }
+ else if(!strcmp(s1, "attachinfo")){
+ COMPATT_S *a;
+ char *id, *s;
+
+ /* return: remote-filename size "type/subtype" */
+ if((id = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
+ if((a = peGetAttachID(id)) != NULL){
+ if(a->file){
+ /* file name */
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(a->l.f.remote,-1));
+
+ /* file size */
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewLongObj(a->l.f.size));
+
+ /* type/subtype */
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s/%s", a->l.f.type, a->l.f.subtype);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(tmp_20k_buf,-1));
+
+ /* description */
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj((a->l.f.description) ? a->l.f.description : "",-1));
+ return(TCL_OK);
+ }
+ else if(a->body){
+ char *name;
+
+ /* file name */
+ if((name = get_filename_parameter(NULL, 0, a->l.b.body, NULL)) != NULL){
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(name, -1));
+ fs_give((void **) &name);
+ }
+ else
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj("Unknown", -1));
+
+ /* file size */
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewLongObj((a->l.b.body->encoding == ENCBASE64)
+ ? ((a->l.b.body->size.bytes * 3)/4)
+ : a->l.b.body->size.bytes));
+
+ /* type/subtype */
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s/%s",
+ body_type_names(a->l.b.body->type),
+ a->l.b.body->subtype ? a->l.b.body->subtype : "Unknown");
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(tmp_20k_buf, -1));
+
+ /* description */
+ if(a->l.b.body->description){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%.*s", 256, a->l.b.body->description);
+ }
+ else if((s = parameter_val(a->l.b.body->parameter, "description")) != NULL){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%.*s", 256, s);
+ fs_give((void **) &s);
+ }
+ else
+ tmp_20k_buf[0] = '\0';
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(tmp_20k_buf, -1));
+
+ return(TCL_OK);
+ }
+
+ err = "Unknown attachment type";
+ }
+ else
+ err = "Can't access attachment id";
+ }
+ else
+ err = "Can't read attachment id";
+ }
+ }
+ else if(objc == 7){
+ if(!strcmp(s1, "attach")){
+ char *file, *remote, *type, *subtype, *desc;
+
+ if((file = Tcl_GetStringFromObj(objv[2], NULL))
+ && (type = Tcl_GetStringFromObj(objv[3], NULL))
+ && (subtype = Tcl_GetStringFromObj(objv[4], NULL))
+ && (remote = Tcl_GetStringFromObj(objv[5], NULL))){
+ int dl;
+
+ desc = Tcl_GetStringFromObj(objv[6], &dl);
+
+ if(desc){
+ Tcl_SetResult(interp, peFileAttachID(file, type, subtype, remote, desc, dl), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else
+ err = "Can't read file description";
+ }
+ else
+ err = "Can't read file name";
+ }
+ }
+ }
+ }
+
+ Tcl_SetResult(interp, err, TCL_STATIC);
+ return(TCL_ERROR);
+}
+
+
+COMPATT_S *
+peNewAttach(void)
+{
+ COMPATT_S *p = (COMPATT_S *) fs_get(sizeof(COMPATT_S));
+ memset(p, 0, sizeof(COMPATT_S));
+ return(p);
+}
+
+
+void
+peFreeAttach(COMPATT_S **a)
+{
+ if(a && *a){
+ fs_give((void **) &(*a)->id);
+
+ if((*a)->file){
+ if((*a)->l.f.type)
+ fs_give((void **) &(*a)->l.f.type);
+
+ if((*a)->l.f.subtype)
+ fs_give((void **) &(*a)->l.f.subtype);
+
+ if((*a)->l.f.remote)
+ fs_give((void **) &(*a)->l.f.remote);
+
+ if((*a)->l.f.local){
+ (void) unlink((*a)->l.f.local);
+ fs_give((void **) &(*a)->l.f.local);
+ }
+
+ if((*a)->l.f.description)
+ fs_give((void **) &(*a)->l.f.description);
+ }
+ else if((*a)->body){
+ pine_free_body(&(*a)->l.b.body);
+ }
+
+ peFreeAttach(&(*a)->next);
+ fs_give((void **) a);
+ }
+}
+
+
+char *
+peFileAttachID(char *f, char *t, char *st, char *r, char *d, int dl)
+{
+ COMPATT_S *ap = peNewAttach(), *p;
+ long hval;
+
+ ap->file = TRUE;
+ ap->l.f.local = cpystr(f);
+ ap->l.f.size = name_file_size(f);
+
+ hval = line_hash(f);
+ while(1) /* collisions? */
+ if(peGetAttachID(ap->id = cpystr(long2string(hval)))){
+ fs_give((void **) &ap->id);
+ hval += 1;
+ }
+ else
+ break;
+
+ ap->l.f.remote = cpystr(r ? r : "");
+ ap->l.f.type = cpystr(t ? t : "Text");
+ ap->l.f.subtype = cpystr(st ? st : "Plain");
+
+ ap->l.f.description = fs_get(dl + 1);
+ snprintf(ap->l.f.description, dl + 1, "%s", d);
+
+ if((p = peCompAttach) != NULL){
+ do
+ if(!p->next){
+ p->next = ap;
+ break;
+ }
+ while((p = p->next) != NULL);
+ }
+ else
+ peCompAttach = ap;
+
+ return(ap->id);
+}
+
+
+char *
+peBodyAttachID(BODY *b)
+{
+ COMPATT_S *ap = peNewAttach(), *p;
+ unsigned long hval;
+
+ ap->body = TRUE;
+ ap->l.b.body = copy_body(NULL, b);
+
+ hval = b->id ? line_hash(b->id) : time(0);
+ while(1) /* collisions? */
+ if(peGetAttachID(ap->id = cpystr(ulong2string(hval)))){
+ fs_give((void **) &ap->id);
+ hval += 1;
+ }
+ else
+ break;
+
+ /* move contents pointer to copy */
+ peBodyMoveContents(b, ap->l.b.body);
+
+ if((p = peCompAttach) != NULL){
+ do
+ if(!p->next){
+ p->next = ap;
+ break;
+ }
+ while((p = p->next) != NULL);
+ }
+ else
+ peCompAttach = ap;
+
+ return(ap->id);
+}
+
+
+void
+peBodyMoveContents(BODY *bs, BODY *bd)
+{
+ if(bs && bd){
+ if(bs->type == TYPEMULTIPART && bd->type == TYPEMULTIPART){
+ PART *ps = bs->nested.part,
+ *pd = bd->nested.part;
+ do /* for each part */
+ peBodyMoveContents(&ps->body, &pd->body);
+ while ((ps = ps->next) && (pd = pd->next)); /* until done */
+ }
+ else if(bs->contents.text.data){
+ bd->contents.text.data = bs->contents.text.data;
+ bs->contents.text.data = NULL;
+ }
+ }
+}
+
+
+
+COMPATT_S *
+peGetAttachID(char *h)
+{
+ COMPATT_S *p;
+
+ for(p = peCompAttach; p; p = p->next)
+ if(!strcmp(p->id, h))
+ return(p);
+
+ return(NULL);
+}
+
+
+int
+peClearAttachID(char *h)
+{
+ COMPATT_S *pp, *pt = NULL;
+
+ for(pp = peCompAttach; pp; pp = pp->next){
+ if(!strcmp(pp->id, h)){
+ if(pt)
+ pt->next = pp->next;
+ else
+ peCompAttach = pp->next;
+
+ pp->next = NULL;
+ peFreeAttach(&pp);
+ return(TRUE);
+ }
+
+ pt = pp;
+ }
+
+ return(FALSE);
+}
+
+
+/*
+ * peDoPost - handle preparing header and body text for posting, prepare
+ * for any Fcc, then call the mailer to send it out
+ */
+int
+peDoPost(METAENV *metaenv, BODY *body, char *fcc, CONTEXT_S **fcc_cntxtp, char *errp)
+{
+ int rv = TCL_OK, recipients;
+ char *s;
+
+ if(commence_fcc(fcc, fcc_cntxtp, TRUE)){
+
+ ps_global->c_client_error[0] = ps_global->last_error[0] = '\0';
+ pePrepareForAuthException();
+
+ if((recipients = (metaenv->env->to || metaenv->env->cc || metaenv->env->bcc))
+ && call_mailer(metaenv, body, NULL, 0, NULL, NULL) < 0){
+ if(s = peAuthException()){
+ strcpy(errp, s);
+ }
+ else if(ps_global->last_error[0]){
+ sprintf(errp, "Send Error: %.*s", 64, ps_global->last_error);
+ }
+ else if(ps_global->c_client_error[0]){
+ sprintf(errp, "Send Error: %.*s", 64, ps_global->c_client_error);
+ }
+ else
+ strcpy(errp, "Sending Failure");
+
+ rv = TCL_ERROR;
+ dprint((1, "call_mailer failed!"));
+ }
+ else if(fcc && fcc_cntxtp && !wrapup_fcc(fcc, *fcc_cntxtp, recipients ? NULL : metaenv, body)){
+ strcpy(errp, "Fcc Failed!. No message saved.");
+ rv = TCL_ERROR;
+ dprint((1, "explicit fcc write failed!"));
+ }
+ else{
+ PINEFIELD *pf;
+ REPLY_S *reply = NULL;
+
+ /* success, now look for x-reply-uid to flip answered flag for? */
+
+ for(pf = metaenv->local; pf && pf->name; pf = pf->next)
+ if(!strucmp(pf->name, "x-reply-uid")){
+ if(pf->textbuf){
+ if((reply = (REPLY_S *) build_reply_uid(pf->textbuf)) != NULL){
+
+ update_answered_flags(reply);
+
+ if(reply->mailbox)
+ fs_give((void **) &reply->mailbox);
+
+ if(reply->prefix)
+ fs_give((void **) &reply->prefix);
+
+ if(reply->data.uid.msgs)
+ fs_give((void **) &reply->data.uid.msgs);
+
+ fs_give((void **) &reply);
+ }
+ }
+
+ break;
+ }
+ }
+ }
+ else{
+ dprint((1,"can't open fcc, cont"));
+
+ strcpy(errp, "Can't open Fcc");
+ rv = TCL_ERROR;
+ }
+
+ return(rv);
+}
+
+
+
+/*
+ * pePostponeCmd - export various bits of alpine state
+ */
+int
+PEPostponeCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+ char *err = "PEPostpone: unknown request";
+ imapuid_t uid;
+
+ dprint((2, "PEPostponeCmd"));
+
+ if(objc == 1){
+ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
+ }
+ else if(!ps_global){
+ Tcl_SetResult(interp, "pePostpone: no config present", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ else{
+ char *s1 = Tcl_GetStringFromObj(objv[1], NULL);
+
+ if(s1){
+ if(!strcmp(s1, "extract")){
+ if(Tcl_GetLongFromObj(interp, objv[2], &uid) == TCL_OK){
+ Tcl_Obj *objHdr = NULL, *objBod = NULL, *objAttach = NULL, *objOpts = NULL;
+ MAILSTREAM *stream;
+ BODY *b;
+ ENVELOPE *env = NULL;
+ PINEFIELD *custom = NULL, *cp;
+ REPLY_S *reply = NULL;
+ ACTION_S *role = NULL;
+ STORE_S *so;
+ long n;
+ int rv = TCL_OK;
+ char *fcc = NULL, *lcc = NULL;
+ unsigned flags = REDRAFT_DEL | REDRAFT_PPND;
+
+ if(objc > 3){ /* optional flags */
+ int i, nFlags;
+ Tcl_Obj **objFlags;
+ char *flagstr;
+
+ Tcl_ListObjGetElements(interp, objv[3], &nFlags, &objFlags);
+ for(i = 0; i < nFlags; i++){
+ if((flagstr = Tcl_GetStringFromObj(objFlags[i], NULL)) == NULL){
+ rv = TCL_ERROR;
+ }
+
+ if(!strucmp(flagstr, "html"))
+ flags |= REDRAFT_HTML;
+ }
+ }
+ /* BUG: should probably complain if argc > 4 */
+
+ if(rv == TCL_OK
+ && postponed_stream(&stream, ps_global->VAR_POSTPONED_FOLDER, "Postponed", 0)
+ && stream){
+ if((so = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
+ if((n = mail_msgno(stream, uid)) > 0L){
+ if(redraft_work(&stream, n, &env, &b, &fcc, &lcc, &reply,
+ NULL, &custom, &role, /* should role be NULL? */
+ flags, so)){
+ char *charset = NULL;
+
+ /* prepare to package up for caller */
+ objHdr = Tcl_NewListObj(0, NULL);
+
+ /* determine body part's charset */
+ if((charset = parameter_val(b->parameter,"charset")) != NULL){
+ objOpts = Tcl_NewListObj(0, NULL);
+ peAppListF(interp, objOpts, "%s%s", "charset", charset);
+ fs_give((void **) &charset);
+ }
+
+ /* body part's MIME subtype */
+ if(b->subtype && strucmp(b->subtype,"plain")){
+ if(!objOpts)
+ objOpts = Tcl_NewListObj(0, NULL);
+
+ peAppListF(interp, objOpts, "%s%s", "subtype", b->subtype);
+ }
+
+ peAppListF(interp, objHdr, "%s%a", "from",
+ role && role->from ? role->from : env->from);
+ peAppListF(interp, objHdr, "%s%a", "to", env->to);
+ peAppListF(interp, objHdr, "%s%a", "cc", env->cc);
+ peAppListF(interp, objHdr, "%s%a", "bcc", env->bcc);
+ peAppListF(interp, objHdr, "%s%s", "in-reply-to", env->in_reply_to);
+ peAppListF(interp, objHdr, "%s%s", "subject",
+ rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
+ SIZEOF_20KBUF, env->subject));
+
+ if(fcc)
+ peFccAppend(interp, objHdr, fcc, -1);
+
+ for(cp = custom; cp && cp->name; cp = cp->next)
+ switch(cp->type){
+ case Address :
+ strncpy(tmp_20k_buf, cp->name, SIZEOF_20KBUF);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ peAppListF(interp, objHdr, "%s%a",
+ lcase((unsigned char *) tmp_20k_buf), *cp->addr);
+ break;
+
+ case Attachment :
+ break;
+
+ case Fcc :
+ case Subject :
+ break; /* ignored */
+
+ default :
+ strncpy(tmp_20k_buf, cp->name, SIZEOF_20KBUF);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ peAppListF(interp, objHdr, "%s%s",
+ lcase((unsigned char *) tmp_20k_buf), cp->textbuf ? cp->textbuf : cp->val);
+ break;
+ }
+
+ if(reply){
+ /* blat x-Reply-UID: for possible use? */
+ if(reply->uid){
+ char uidbuf[MAILTMPLEN], *p;
+ long i;
+
+ for(i = 0L, p = tmp_20k_buf; reply->data.uid.msgs[i]; i++){
+ if(i)
+ sstrncpy(&p, ",", SIZEOF_20KBUF-(p-tmp_20k_buf));
+
+ sstrncpy(&p,ulong2string(reply->data.uid.msgs[i]), SIZEOF_20KBUF-(p-tmp_20k_buf));
+ }
+
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+
+ snprintf(uidbuf, sizeof(uidbuf), "(%s%s%s)(%ld %lu %s)%s",
+ reply->prefix ? int2string(strlen(reply->prefix))
+ : (reply->forwarded) ? "" : "0 ",
+ reply->prefix ? " " : "",
+ reply->prefix ? reply->prefix : "",
+ i, reply->data.uid.validity,
+ tmp_20k_buf, reply->mailbox);
+
+ peAppListF(interp, objHdr, "%s%s", "x-reply-uid", uidbuf);
+ }
+
+ fs_give((void **) &reply->mailbox);
+ fs_give((void **) &reply->prefix);
+ fs_give((void **) &reply->data.uid.msgs);
+ fs_give((void **) &reply);
+ }
+
+ objBod = Tcl_NewListObj(0, NULL);
+ peSoStrToList(interp, objBod, so);
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objHdr);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objBod);
+
+ objAttach = peMsgAttachCollector(interp, b);
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAttach);
+
+ if(objOpts){
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),objOpts);
+ }
+
+ /* clean up */
+ if(fcc)
+ fs_give((void **) &fcc);
+
+ if(lcc)
+ fs_give((void **) &lcc);
+
+ mail_free_envelope(&env);
+ pine_free_body(&b);
+ free_action(&role);
+
+ /* if Drafts got whacked, open INBOX */
+ if(!ps_global->mail_stream)
+ do_broach_folder(ps_global->inbox_name,
+ ps_global->context_list,
+ NULL, DB_INBOXWOCNTXT);
+
+ return(TCL_OK);
+ }
+
+ so_give(&so);
+ }
+ else
+ err = "Unknown UID";
+ }
+ else
+ err = "No internal storage";
+
+ /* redraft_work cleaned up the "stream" */
+ }
+ else
+ err = "No Postponed stream";
+ }
+ else
+ err = "Malformed extract request";
+ }
+ else if(objc == 2){
+ if(!strcmp(s1, "any")){
+ MAILSTREAM *stream;
+
+ if(postponed_stream(&stream, ps_global->VAR_POSTPONED_FOLDER, "Postponed", 0) && stream){
+ Tcl_SetResult(interp, "1", TCL_STATIC);
+
+ if(stream != ps_global->mail_stream)
+ pine_mail_close(stream);
+ }
+ else
+ Tcl_SetResult(interp, "0", TCL_STATIC);
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "count")){
+ MAILSTREAM *stream;
+
+ if(postponed_stream(&stream, ps_global->VAR_POSTPONED_FOLDER, "Postponed", 0) && stream){
+ Tcl_SetResult(interp, long2string(stream->nmsgs), TCL_STATIC);
+
+ if(stream != ps_global->mail_stream)
+ pine_mail_close(stream);
+ }
+ else
+ Tcl_SetResult(interp, "-1", TCL_STATIC);
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "list")){
+ MAILSTREAM *stream;
+ ENVELOPE *env;
+ Tcl_Obj *objEnv = NULL, *objEnvList;
+ long n;
+
+ if(postponed_stream(&stream, ps_global->VAR_POSTPONED_FOLDER, "Postponed", 0) && stream){
+ if(!stream->nmsgs){
+ (void) redraft_cleanup(&stream, FALSE, REDRAFT_PPND);
+ Tcl_SetResult(interp, "", TCL_STATIC);
+ return(TCL_OK);
+ }
+
+ objEnvList = Tcl_NewListObj(0, NULL);
+
+ for(n = 1; n <= stream->nmsgs; n++){
+ if((env = pine_mail_fetchstructure(stream, n, NULL)) != NULL){
+ objEnv = Tcl_NewListObj(0, NULL);
+
+ peAppListF(interp, objEnv, "%s%s", "uid",
+ ulong2string(mail_uid(stream, n)));
+
+ peAppListF(interp, objEnv, "%s%a", "to", env->to);
+
+ date_str((char *)env->date, iSDate, 1, tmp_20k_buf, SIZEOF_20KBUF, 0);
+
+ peAppListF(interp, objEnv, "%s%s", "date", tmp_20k_buf);
+
+ peAppListF(interp, objEnv, "%s%s", "subj",
+ rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
+ SIZEOF_20KBUF, env->subject));
+
+ Tcl_ListObjAppendElement(interp, objEnvList, objEnv);
+ }
+ }
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objEnvList);
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj("utf-8", -1));
+
+ if(stream != ps_global->mail_stream)
+ pine_mail_close(stream);
+ }
+
+ return(TCL_OK);
+ }
+ }
+ else if(objc == 3){
+ if(!strcmp(s1, "append")){
+ int rv;
+
+ if((rv = peMsgCollector(interp, objc - 2, (Tcl_Obj **) &objv[2], peDoPostpone, PMC_NONE)) == TCL_OK)
+ Tcl_SetResult(interp, ulong2string(get_last_append_uid()), TCL_VOLATILE);
+
+ return(rv);
+ }
+ else if(!strcmp(s1, "draft")){
+ int rv;
+
+ if((rv = peMsgCollector(interp, objc - 2, (Tcl_Obj **) &objv[2], peDoPostpone, PMC_PRSRV_ATT)) == TCL_OK)
+ Tcl_SetResult(interp, ulong2string(get_last_append_uid()), TCL_VOLATILE);
+
+ return(rv);
+ }
+ else if(!strcmp(s1, "delete")){
+ if(Tcl_GetLongFromObj(interp, objv[2], &uid) == TCL_OK){
+ MAILSTREAM *stream;
+ long rawno;
+
+ if(postponed_stream(&stream, ps_global->VAR_POSTPONED_FOLDER, "Postponed", 0) && stream){
+ if((rawno = mail_msgno(stream, uid)) > 0L){
+ mail_flag(stream, long2string(rawno), "\\DELETED", ST_SET);
+ ps_global->expunge_in_progress = 1;
+ mail_expunge(stream);
+ ps_global->expunge_in_progress = 0;
+ if(stream != ps_global->mail_stream)
+ pine_mail_actually_close(stream);
+
+ return(TCL_OK);
+ }
+ else
+ err = "PEPostpone delete: UID no longer exists";
+ }
+ else
+ err = "PEPostpone delete: No Postponed stream";
+ }
+ else
+ err = "PEPostpone delete: No uid provided";
+ }
+ }
+ }
+ }
+
+ Tcl_SetResult(interp, err, TCL_STATIC);
+ return(TCL_ERROR);
+}
+
+
+/*
+ * peDoPostpone - handle postponing after message collection
+ */
+int
+peDoPostpone(METAENV *metaenv, BODY *body, char *fcc, CONTEXT_S **fcc_cntxtp, char *errp)
+{
+ PINEFIELD *pf;
+ int rv;
+ appenduid_t *au;
+
+ /*
+ * resolve fcc and store it in fcc custom header field data
+ */
+ if(fcc && *fcc && fcc_cntxtp && *fcc_cntxtp)
+ for(pf = metaenv->local; pf && pf->name; pf = pf->next)
+ if(!strucmp("fcc", pf->name)){
+ char *name, *rs, path_in_context[MAILTMPLEN];
+
+ if(pf->textbuf) /* free old value */
+ fs_give((void **) &pf->textbuf);
+
+ /* replace nickname with full name */
+ if(!(name = folder_is_nick(fcc, FOLDERS(*fcc_cntxtp), FN_NONE)))
+ name = fcc;
+
+ if(context_isambig(name) && !(((*fcc_cntxtp)->use) & CNTXT_SAVEDFLT)){
+ context_apply(path_in_context, *fcc_cntxtp, name, sizeof(path_in_context));
+ rs = IS_REMOTE(path_in_context) ? path_in_context : NULL;
+ }
+ else
+ rs = cpystr(name);
+
+ if(rs){
+ pf->textbuf = cpystr(rs);
+ pf->text = &pf->textbuf;
+ }
+
+ break;
+ }
+
+ au = mail_parameters(NIL, GET_APPENDUID, NIL);
+ mail_parameters(NIL, SET_APPENDUID, (void *) appenduid_cb);
+
+ rv = write_postponed(metaenv, body);
+
+ mail_parameters(NIL, SET_APPENDUID, (void *) au);
+
+ return((rv < 0) ? TCL_ERROR : TCL_OK);
+}
+
+
+/*
+ * peMsgCollector - Collect message parts and call specified handler
+ */
+int
+peMsgCollector(Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj **objv,
+ int (*postfunc)(METAENV *, BODY *, char *, CONTEXT_S **, char *),
+ long flags)
+{
+ Tcl_Obj **objMsg, **objField, **objBody;
+ int i, j, vl, nMsg, nField, nBody;
+ char *field, *value, *err = NULL;
+ MSG_COL_S md;
+ PINEFIELD *pf;
+ STRLIST_S *tp, *lp;
+ static char *fakedomain = "@";
+
+ memset(&md, 0, sizeof(MSG_COL_S));
+ md.postop_fcc_no_attach = -1;
+ md.postfunc = postfunc;
+ md.qualified_addrs = ((flags & PMC_FORCE_QUAL) == PMC_FORCE_QUAL);
+
+ if(objc != 1){
+ Tcl_SetResult(interp, "Malformed message data", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ else if(!ps_global){
+ Tcl_SetResult(interp, "No open folder", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ md.outgoing = mail_newenvelope();
+
+ md.metaenv = pine_new_env(md.outgoing, NULL, NULL, md.custom = peCustomHdrs());
+
+ Tcl_ListObjGetElements(interp, objv[0], &nMsg, &objMsg);
+ for(i = 0; i < nMsg; i++){
+ if(Tcl_ListObjGetElements(interp, objMsg[i], &nField, &objField) != TCL_OK){
+ err = ""; /* interp's result object has error message */
+ return(peMsgCollected(interp, &md, err, flags));
+ }
+
+ if(nField && (field = Tcl_GetStringFromObj(objField[0], NULL))){
+ if(!strcmp(field, "body")){
+ if(md.msgtext){
+ err = "Too many bodies";
+ return(peMsgCollected(interp, &md, err, flags));
+ }
+ else if((md.msgtext = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
+ /* mark storage object as user edited */
+ (void) so_attr(md.msgtext, "edited", "1");
+
+ Tcl_ListObjGetElements(interp, objField[1], &nBody, &objBody);
+ for(j = 0; j < nBody; j++){
+ value = Tcl_GetStringFromObj(objBody[j], &vl);
+ if(value){
+ so_nputs(md.msgtext, value, vl);
+ so_puts(md.msgtext, "\n");
+ }
+ else{
+ err = "Value read failure";
+ return(peMsgCollected(interp, &md, err, flags));
+ }
+ }
+ }
+ else {
+ err = "Can't acquire body storage";
+ return(peMsgCollected(interp, &md, err, flags));
+ }
+ }
+ else if(!strucmp(field, "attach")){
+ char *id;
+ COMPATT_S *a;
+
+ if(nField == 2
+ && (id = Tcl_GetStringFromObj(objField[1], NULL))
+ && (a = peGetAttachID(id))){
+ tp = new_strlist(id);
+ if((lp = md.attach) != NULL){
+ do
+ if(!lp->next){
+ lp->next = tp;
+ break;
+ }
+ while((lp = lp->next) != NULL);
+ }
+ else
+ md.attach = tp;
+ }
+ else{
+ strcpy(err = tmp_20k_buf, "Unknown attachment ID");
+ return(peMsgCollected(interp, &md, err, flags));
+ }
+ }
+ else if(!strucmp(field, "fcc")){
+ Tcl_Obj **objFcc;
+ int nFcc;
+
+ if(Tcl_ListObjGetElements(interp, objField[1], &nFcc, &objFcc) == TCL_OK
+ && nFcc == 2
+ && Tcl_GetIntFromObj(interp, objFcc[0], &md.fcc_colid) == TCL_OK
+ && (value = Tcl_GetStringFromObj(objFcc[1], NULL))){
+ if(md.fcc)
+ fs_give((void **) &md.fcc);
+
+ md.fcc = cpystr(value);
+ }
+ else {
+ strcpy(err = tmp_20k_buf, "Unrecognized Fcc specification");
+ return(peMsgCollected(interp, &md, err, flags));
+ }
+ }
+ else if(!strucmp(field, "postoption")){
+ Tcl_Obj **objPO;
+ int nPO, ival;
+
+ value = NULL;
+ if(Tcl_ListObjGetElements(interp, objField[1], &nPO, &objPO) == TCL_OK
+ && nPO == 2
+ && (value = Tcl_GetStringFromObj(objPO[0], NULL))){
+ if(!strucmp(value,"fcc-without-attachments")){
+ if(Tcl_GetIntFromObj(interp, objPO[1], &ival) == TCL_OK){
+ md.postop_fcc_no_attach = (ival != 0);
+ }
+ else{
+ sprintf(err = tmp_20k_buf, "Malformed Post Option: fcc-without-attachments");
+ return(peMsgCollected(interp, &md, err, flags));
+ }
+ }
+ else if(!strucmp(value, "charset")){
+ if((value = Tcl_GetStringFromObj(objPO[1], NULL)) != NULL){
+ char *p;
+
+ for(p = value; ; p++){ /* sanity check */
+ if(!*p){
+ md.charset = cpystr(value);
+ break;
+ }
+
+ if(isspace((unsigned char ) *p)
+ || !isprint((unsigned char) *p))
+ break;
+
+ if(p - value > 255)
+ break;
+ }
+ }
+ else{
+ err = "Post option read failure";
+ return(peMsgCollected(interp, &md, err, flags));
+ }
+ }
+ else if(!strucmp(value, "flowed")){
+ if(F_OFF(F_QUELL_FLOWED_TEXT,ps_global)){
+ if((value = Tcl_GetStringFromObj(objPO[1], NULL)) != NULL){
+ if(!strucmp(value, "yes"))
+ md.flowed = 1;
+ }
+ else{
+ err = "Post option read failure";
+ return(peMsgCollected(interp, &md, err, flags));
+ }
+ }
+ }
+ else if(!strucmp(value, "subtype")){
+ if((value = Tcl_GetStringFromObj(objPO[1], NULL)) != NULL){
+ if(!strucmp(value, "html"))
+ md.html = 1;
+ }
+ else{
+ err = "Post option read failure";
+ return(peMsgCollected(interp, &md, err, flags));
+ }
+ }
+ else if(!strucmp(value, "priority")){
+ if((value = Tcl_GetStringFromObj(objPO[1], NULL)) != NULL){
+ char *priority = NULL;
+
+ if(!strucmp(value, "highest"))
+ priority = "Highest";
+ else if(!strucmp(value, "high"))
+ priority = "High";
+ else if(!strucmp(value, "normal"))
+ priority = "Normal";
+ else if(!strucmp(value, "low"))
+ priority = "Low";
+ else if(!strucmp(value, "lowest"))
+ priority = "Lowest";
+
+ if(priority){
+ if(pf = set_priority_header(md.metaenv, priority))
+ pf->text = &pf->textbuf;
+ }
+ }
+ else{
+ err = "Post option read failure";
+ return(peMsgCollected(interp, &md, err, flags));
+ }
+ }
+ else{
+ sprintf(err = tmp_20k_buf, "Unknown Post Option: %s", value);
+ return(peMsgCollected(interp, &md, err, flags));
+ }
+ }
+ else{
+ sprintf(err = tmp_20k_buf, "Malformed Post Option");
+ return(peMsgCollected(interp, &md, err, flags));
+ }
+ }
+ else {
+ if(nField != 2){
+ sprintf(err = tmp_20k_buf, "Malformed header (%s)", field);
+ return(peMsgCollected(interp, &md, err, flags));
+ }
+
+ if((value = Tcl_GetStringFromObj(objField[1], &vl)) != NULL){
+ ADDRESS **addrp = NULL;
+ char **valp = NULL, *valcpy;
+
+ if(!strucmp(field, "from")){
+ addrp = &md.outgoing->from;
+ }
+ else if(!strucmp(field, "reply-to")){
+ addrp = &md.outgoing->reply_to;
+ }
+ else if(!strucmp(field, "to")){
+ addrp = &md.outgoing->to;
+ }
+ else if(!strucmp(field, "cc")){
+ addrp = &md.outgoing->cc;
+ }
+ else if(!strucmp(field, "bcc")){
+ addrp = &md.outgoing->bcc;
+ }
+ else if(!strucmp(field, "subject")){
+ valp = &md.outgoing->subject;
+ }
+ else if(!strucmp(field, "in-reply-to")){
+ valp = &md.outgoing->in_reply_to;
+ }
+ else if(!strucmp(field, "newsgroups")){
+ valp = &md.outgoing->newsgroups;
+ }
+ else if(!strucmp(field, "followup-to")){
+ valp = &md.outgoing->followup_to;
+ }
+ else if(!strucmp(field, "references")){
+ valp = &md.outgoing->references;
+ }
+ else if(!strucmp(field, "x-reply-uid")){
+ for(pf = md.metaenv->local; pf && pf->name; pf = pf->next)
+ if(!strucmp(pf->name, "x-reply-uid")){
+ valp = pf->text = &pf->textbuf;
+ break;
+ }
+ }
+ else if(!strucmp(field, "x-auth-received")){
+ for(pf = md.metaenv->local; pf && pf->name; pf = pf->next)
+ if(!strucmp(pf->name, "x-auth-received")){
+ valp = pf->text = &pf->textbuf;
+ break;
+ }
+ }
+ else{
+ for(pf = md.metaenv->custom; pf && pf->name; pf = pf->next)
+ if(!strucmp(field, pf->name)){
+ if(pf->type == Address)
+ addrp = pf->addr;
+ else if(vl)
+ valp = &pf->textbuf;
+ else if(pf->textbuf)
+ fs_give((void **) &pf->textbuf);
+
+ break;
+ }
+
+ if(!pf)
+ dprint((2, "\nPOST: unrecognized field - %s\n", field));
+ }
+
+ if(valp){
+ if(*valp)
+ fs_give((void **) valp);
+
+ sprintf(*valp = fs_get((vl + 1) * sizeof(char)), "%.*s", vl, value);
+ }
+
+ if(addrp){
+ sprintf(valcpy = fs_get((vl + 1) * sizeof(char)), "%.*s", vl, value);
+
+ for(; *addrp; addrp = &(*addrp)->next)
+ ;
+
+ rfc822_parse_adrlist(addrp, valcpy,
+ (flags & PMC_FORCE_QUAL)
+ ? fakedomain : ps_global->maildomain);
+ fs_give((void **) &valcpy);
+ }
+ }
+ else{
+ err = "Value read failure";
+ return(peMsgCollected(interp, &md, err, flags));
+ }
+ }
+ }
+ }
+
+ return(peMsgCollected(interp, &md, err, flags));
+}
+
+
+/*
+ * peMsgCollected - Dispatch collected message data and cleanup
+ */
+int
+peMsgCollected(Tcl_Interp *interp, MSG_COL_S *md, char *err, long flags)
+{
+ int rv = TCL_OK, non_ascii = FALSE;
+ unsigned char c;
+ BODY *body = NULL, *tbp = NULL;
+ char errbuf[WP_MAX_POST_ERROR + 1], *charset;
+ STRLIST_S *lp;
+
+ if(err){
+ if(md->msgtext)
+ so_give(&md->msgtext);
+
+ rv = TCL_ERROR;
+ }
+ else if(md->qualified_addrs && check_addresses(md->metaenv) == CA_BAD){
+ sprintf(err = tmp_20k_buf, "Address must be fully qualified.");
+ rv = TCL_ERROR;
+ }
+ else{
+ /* sniff body for possible multipart wrapping to protect encoding */
+ so_seek(md->msgtext, 0L, 0);
+
+ while(so_readc(&c, md->msgtext))
+ if(!c || c & 0x80){
+ non_ascii = TRUE;
+ break;
+ }
+
+ if(!md->outgoing->from)
+ md->outgoing->from = generate_from();
+
+ rfc822_date(tmp_20k_buf);
+ md->outgoing->date = (unsigned char *) cpystr(tmp_20k_buf);
+ md->outgoing->return_path = rfc822_cpy_adr(md->outgoing->from);
+ md->outgoing->message_id = generate_message_id();
+
+ body = mail_newbody();
+
+ /* wire any attachments to body */
+ if(md->attach || (non_ascii && F_OFF(F_COMPOSE_ALWAYS_DOWNGRADE, ps_global))){
+ PART **np;
+ PARAMETER **pp;
+ COMPATT_S *a;
+
+ /* setup slot for message text */
+ body->type = TYPEMULTIPART;
+ body->nested.part = mail_newbody_part();
+ tbp = &body->nested.part->body;
+
+ /* link in attachments */
+ for(lp = md->attach, np = &body->nested.part->next; lp; lp = lp->next, np = &(*np)->next){
+ if(!(a = peGetAttachID(lp->name))){
+ err = "Unknown Attachment ID";
+ rv = TCL_ERROR;
+ break;
+ }
+
+ *np = mail_newbody_part();
+
+ if(a->file){
+ (*np)->body.id = generate_message_id();
+ (*np)->body.description = cpystr(a->l.f.description);
+
+ /* set name parameter */
+ for(pp = &(*np)->body.parameter; *pp; )
+ if(!struncmp((*pp)->attribute, "name", 4)
+ && (!*((*pp)->attribute + 4)
+ || *((*pp)->attribute + 4) == '*')){
+ PARAMETER *free_me = *pp;
+ *pp = (*pp)->next;
+ free_me->next = NULL;
+ mail_free_body_parameter(&free_me);
+ }
+ else
+ pp = &(*pp)->next;
+
+ *pp = NULL;
+ set_parameter(pp, "name", a->l.f.remote);
+
+ /* Then set the Content-Disposition ala RFC1806 */
+ if(!(*np)->body.disposition.type){
+ (*np)->body.disposition.type = cpystr("attachment");
+ for(pp = &(*np)->body.disposition.parameter; *pp; )
+ if(!struncmp((*pp)->attribute, "filename", 4)
+ && (!*((*pp)->attribute + 4)
+ || *((*pp)->attribute + 4) == '*')){
+ PARAMETER *free_me = *pp;
+ *pp = (*pp)->next;
+ free_me->next = NULL;
+ mail_free_body_parameter(&free_me);
+ }
+ else
+ pp = &(*pp)->next;
+
+ *pp = NULL;
+ set_parameter(pp, "filename", a->l.f.remote);
+ }
+
+ if(((*np)->body.contents.text.data = (void *) so_get(FileStar, a->l.f.local, READ_ACCESS)) != NULL){
+ (*np)->body.type = mt_translate_type(a->l.f.type);
+ (*np)->body.subtype = cpystr(a->l.f.subtype);
+ (*np)->body.encoding = ENCBINARY;
+ (*np)->body.size.bytes = name_file_size(a->l.f.local);
+
+ if((*np)->body.type == TYPEOTHER
+ && !set_mime_type_by_extension(&(*np)->body, a->l.f.local))
+ set_mime_type_by_grope(&(*np)->body);
+
+ so_release((STORE_S *)(*np)->body.contents.text.data);
+ }
+ else{
+ /* unravel here */
+ err = "Can't open uploaded attachment";
+ rv = TCL_ERROR;
+ break;
+ }
+ }
+ else if(a->body){
+ BODY *newbody = copy_body(NULL, a->l.b.body);
+ (*np)->body = *newbody;
+ fs_give((void **) &newbody);
+ peBodyMoveContents(a->l.b.body, &(*np)->body);
+ }
+ else{
+ err = "BOTCH: Unknown attachment type";
+ rv = TCL_ERROR;
+ break;
+ }
+ }
+ }
+ else
+ tbp = body;
+
+ /* assign MIME parameters to text body part */
+ tbp->type = TYPETEXT;
+ if(md->html) tbp->subtype = cpystr("HTML");
+
+ tbp->contents.text.data = (void *) md->msgtext;
+ tbp->encoding = ENCOTHER;
+
+ /* set any text flowed param */
+ if(md->flowed)
+ peMsgSetParm(&tbp->parameter, "format", "flowed");
+
+ if(rv == TCL_OK){
+ CONTEXT_S *fcc_cntxt = ps_global->context_list;
+
+ while(md->fcc_colid--)
+ if(fcc_cntxt->next)
+ fcc_cntxt = fcc_cntxt->next;
+
+ if(md->postop_fcc_no_attach >= 0){
+ int oldval = F_ON(F_NO_FCC_ATTACH, ps_global);
+ F_SET(F_NO_FCC_ATTACH, ps_global, md->postop_fcc_no_attach);
+ md->postop_fcc_no_attach = oldval;
+ }
+
+ pine_encode_body(body);
+
+ rv = (*md->postfunc)(md->metaenv, body, md->fcc, &fcc_cntxt, errbuf);
+
+ if(md->postop_fcc_no_attach >= 0){
+ F_SET(F_NO_FCC_ATTACH, ps_global, md->postop_fcc_no_attach);
+ }
+
+ if(rv == TCL_OK){
+ if((flags & PMC_PRSRV_ATT) == 0)
+ peFreeAttach(&peCompAttach);
+ }
+ else{
+ /* maintain pointers to attachments */
+ (void) peMsgAttachCollector(NULL, body);
+ err = errbuf;
+ }
+ }
+
+ pine_free_body(&body);
+ }
+
+ if(md->charset)
+ fs_give((void **) &md->charset);
+
+ free_strlist(&md->attach);
+
+ pine_free_env(&md->metaenv);
+
+ if(md->custom)
+ free_customs(md->custom);
+
+ mail_free_envelope(&md->outgoing);
+
+ if(err && *err)
+ Tcl_SetResult(interp, err, TCL_VOLATILE);
+
+ return(rv);
+}
+
+
+void
+peMsgSetParm(PARAMETER **pp, char *pa, char *pv)
+{
+ for(; *pp; pp = &(*pp)->next)
+ if(!strucmp(pa, (*pp)->attribute)){
+ if((*pp)->value)
+ fs_give((void **) &(*pp)->value);
+
+ break;
+ }
+
+ if(!*pp){
+ *pp = mail_newbody_parameter();
+ (*pp)->attribute = cpystr(pa);
+ }
+
+ (*pp)->value = cpystr(pv);
+}
+
+
+Tcl_Obj *
+peMsgAttachCollector(Tcl_Interp *interp, BODY *b)
+{
+ char *id, *name = NULL;
+ PART *part;
+ Tcl_Obj *aListObj = NULL, *aObj = NULL;
+
+ peFreeAttach(&peCompAttach);
+
+ if(interp)
+ aListObj = Tcl_NewListObj(0, NULL);
+
+ if(b->type == TYPEMULTIPART){
+ /*
+ * Walk first level, clipping branches and adding them
+ * to the attachment list...
+ */
+ for(part = b->nested.part->next; part; part = part->next) {
+ id = peBodyAttachID(&part->body);
+ aObj = Tcl_NewListObj(0, NULL);
+
+ if(interp){
+ Tcl_ListObjAppendElement(interp, aObj, Tcl_NewStringObj(id, -1));
+
+ /* name */
+ if((name = get_filename_parameter(NULL, 0, &part->body, NULL)) != NULL){
+ Tcl_ListObjAppendElement(interp, aObj, Tcl_NewStringObj(name, -1));
+ fs_give((void **) &name);
+ }
+ else
+ Tcl_ListObjAppendElement(interp, aObj, Tcl_NewStringObj("Unknown", -1));
+
+ /* size */
+ Tcl_ListObjAppendElement(interp, aObj,
+ Tcl_NewLongObj((part->body.encoding == ENCBASE64)
+ ? ((part->body.size.bytes * 3)/4)
+ : part->body.size.bytes));
+
+ /* type */
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s/%s",
+ body_type_names(part->body.type),
+ part->body.subtype ? part->body.subtype : rfc822_default_subtype (part->body.type));
+ Tcl_ListObjAppendElement(interp, aObj, Tcl_NewStringObj(tmp_20k_buf, -1));
+ Tcl_ListObjAppendElement(interp, aListObj, aObj);
+ }
+ }
+ }
+
+ return (aListObj);
+}
+
+
+int
+peFccAppend(Tcl_Interp *interp, Tcl_Obj *obj, char *fcc, int colid)
+{
+ Tcl_Obj *objfcc = NULL;
+
+ if(colid < 0)
+ colid = (ps_global->context_list && (ps_global->context_list->use & CNTXT_INCMNG)) ? 1 : 0;
+
+ return((objfcc = Tcl_NewListObj(0, NULL))
+ && Tcl_ListObjAppendElement(interp, objfcc, Tcl_NewStringObj("fcc", -1)) == TCL_OK
+ && peAppListF(interp, objfcc, "%i%s", colid, fcc) == TCL_OK
+ && Tcl_ListObjAppendElement(interp, obj, objfcc) == TCL_OK);
+}
+
+
+/* * * * * * * * * * * * * Start of Address Management Routines * * * * * * * * * * * */
+
+
+/*
+ * PEAddressCmd - export various bits of address book/directory access
+ */
+int
+PEAddressCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+ char *op;
+
+ dprint((2, "PEAddressCmd"));
+
+ if(objc == 1){
+ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
+ return(TCL_ERROR);
+ }
+ else if(!ps_global){
+ Tcl_SetResult(interp, "PEAddress: no open folder", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ else if((op = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){
+ if(objc == 2){
+ if(!strcmp(op, "safecheck")){
+ if(peInitAddrbooks(interp, 1) != TCL_OK)
+ return(TCL_ERROR);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "books")){
+ int i;
+
+ /*
+ * return the list of configured address books
+ */
+
+ if(peInitAddrbooks(interp, 0) != TCL_OK)
+ return(TCL_ERROR);
+
+ for(i = 0; i < as.n_addrbk; i++){
+ Tcl_Obj *objmv[4];
+
+ objmv[0] = Tcl_NewIntObj(i);
+ if(as.adrbks[i].abnick){
+ objmv[1] = Tcl_NewStringObj(as.adrbks[i].abnick, -1);
+ }
+ else {
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), "Address book number %d", i + 1);
+ objmv[1] = Tcl_NewStringObj(buf, -1);
+ }
+
+ objmv[2] = Tcl_NewStringObj(as.adrbks[i].filename ? as.adrbks[i].filename : "", -1);
+
+ objmv[3] = Tcl_NewIntObj(as.adrbks[i].access == ReadWrite);
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewListObj(4, objmv));
+ }
+
+ return(TCL_OK);
+ }
+ }
+ else if(objc == 3){
+ if(!strcmp(op, "parselist")){
+ char *addrstr;
+ ADDRESS *addrlist = NULL, *atmp, *anextp;
+ static char *fakedomain = "@";
+
+ if((addrstr = Tcl_GetStringFromObj(objv[2], NULL)) != NULL)
+ addrstr = cpystr(addrstr); /* can't munge tcl copy */
+
+ ps_global->c_client_error[0] = '\0';
+ rfc822_parse_adrlist(&addrlist, addrstr,
+ (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global))
+ ? fakedomain : ps_global->maildomain);
+
+ fs_give((void **) &addrstr);
+ if(ps_global->c_client_error[0]){
+ Tcl_SetResult(interp, ps_global->c_client_error, TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ for(atmp = addrlist; atmp; ){
+ RFC822BUFFER rbuf;
+
+ anextp = atmp->next;
+ atmp->next = NULL;
+ tmp_20k_buf[0] = '\0';
+ rbuf.f = dummy_soutr;
+ rbuf.s = NULL;
+ rbuf.beg = tmp_20k_buf;
+ rbuf.cur = tmp_20k_buf;
+ rbuf.end = tmp_20k_buf+SIZEOF_20KBUF-1;
+ rfc822_output_address_list(&rbuf, atmp, 0L, NULL);
+ *rbuf.cur = '\0';
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(tmp_20k_buf, -1));
+ atmp = anextp;
+ }
+
+ mail_free_address(&addrlist);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "xlookup")){
+ char *addrstr;
+ ADDRESS *addrlist = NULL, *atmp, *anextp;
+ static char *fakedomain = "@";
+
+ if((addrstr = Tcl_GetStringFromObj(objv[2], NULL)) != NULL)
+ addrstr = cpystr(addrstr); /* can't munge tcl copy */
+
+ ps_global->c_client_error[0] = '\0';
+ rfc822_parse_adrlist(&addrlist, addrstr,
+ (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global))
+ ? fakedomain : ps_global->maildomain);
+
+ fs_give((void **) &addrstr);
+ if(ps_global->c_client_error[0]){
+ Tcl_SetResult(interp, ps_global->c_client_error, TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ for(atmp = addrlist; atmp; ){
+ anextp = atmp->next;
+ atmp->next = NULL;
+ tmp_20k_buf[0] = '\0';
+ if(atmp->host){
+ if(atmp->host[0] == '@'){
+ /* leading ampersand means "missing-hostname" */
+ }
+ else{
+ RFC822BUFFER rbuf;
+
+ rbuf.f = dummy_soutr;
+ rbuf.s = NULL;
+ rbuf.beg = tmp_20k_buf;
+ rbuf.cur = tmp_20k_buf;
+ rbuf.end = tmp_20k_buf+SIZEOF_20KBUF-1;
+ rfc822_output_address_list(&rbuf, atmp, 0L, NULL);
+ *rbuf.cur = '\0';
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(tmp_20k_buf, -1));
+ }
+ } /* else group syntax, move on */
+
+ atmp = anextp;
+ }
+
+ mail_free_address(&addrlist);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "format")){
+ int i, booknum;
+ char buf[256], *s;
+
+ if(peInitAddrbooks(interp, 0) != TCL_OK)
+ return(TCL_ERROR);
+
+ /*
+ *
+ */
+ if(Tcl_GetIntFromObj(interp, objv[2], &booknum) == TCL_OK)
+ for(i = 0; i < as.n_addrbk; i++)
+ if(i == booknum){
+ addrbook_new_disp_form(&as.adrbks[booknum], ps_global->VAR_ABOOK_FORMATS, booknum, NULL);
+
+ for(i = 0; i < NFIELDS && as.adrbks[booknum].disp_form[i].type != Notused; i++){
+ switch(as.adrbks[booknum].disp_form[i].type){
+ case Nickname :
+ s = "nick";
+ break;
+ case Fullname :
+ s = "full";
+ break;
+ case Addr :
+ s = "addr";
+ break;
+ case Filecopy :
+ s = "fcc";
+ break;
+ case Comment :
+ s = "comment";
+ break;
+ default :
+ s = NULL;
+ break;
+ }
+
+ if(s){
+ Tcl_Obj *objmv[2];
+
+ objmv[0] = Tcl_NewStringObj(s, -1);
+ objmv[1] = Tcl_NewIntObj((100 * as.adrbks[booknum].disp_form[i].width) / ps_global->ttyo->screen_cols);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewListObj(2, objmv));
+ }
+ }
+
+
+ return(TCL_OK);
+ }
+
+ snprintf(buf, sizeof(buf), "PEAddress list: unknown address book number \"%d\"", booknum);
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ else if(!strcmp(op, "list")){
+ int i, j, k, n, booknum;
+ char buf[256], *s;
+ AdrBk_Entry *ae;
+ Tcl_Obj *objev[NFIELDS + 1], *objhv[2];
+
+ if(peInitAddrbooks(interp, 0) != TCL_OK)
+ return(TCL_ERROR);
+
+ /*
+ *
+ */
+ if(Tcl_GetIntFromObj(interp, objv[2], &booknum) == TCL_OK)
+ for(i = 0; i < as.n_addrbk; i++)
+ if(i == booknum){
+ addrbook_new_disp_form(&as.adrbks[booknum], ps_global->VAR_ABOOK_FORMATS, booknum, NULL);
+
+ for(i = 0;
+ (ae = adrbk_get_ae(as.adrbks[booknum].address_book, i));
+ i++){
+
+ /* first member is type: Single, List or Lookup */
+ switch(ae->tag){
+ case Single :
+ s = "single";
+ break;
+ case List :
+ s = "list";
+ break;
+ default : /* not set!?! */
+ continue;
+ }
+
+ if(!ae->nickname)
+ continue;
+
+ objhv[0] = Tcl_NewStringObj(ae->nickname, -1);
+ objhv[1] = Tcl_NewStringObj(s, -1);
+ objev[n = 0] = Tcl_NewListObj(2, objhv);
+
+ /*
+ * set fields based on VAR_ABOOK_FORMATS
+ */
+
+ for(j = 0; j < NFIELDS && as.adrbks[booknum].disp_form[j].type != Notused; j++){
+ switch(as.adrbks[booknum].disp_form[j].type){
+ case Nickname :
+ objev[++n] = Tcl_NewStringObj(ae->nickname, -1);
+ break;
+ case Fullname :
+ objev[++n] = Tcl_NewStringObj(ae->fullname, -1);
+ break;
+ case Addr :
+ if(ae->tag == Single){
+ objev[++n] = Tcl_NewStringObj(ae->addr.addr, -1);
+ }
+ else{
+ Tcl_Obj **objav;
+
+ for(k = 0; ae->addr.list[k]; k++)
+ ;
+
+ objav = (Tcl_Obj **) fs_get(k * sizeof(Tcl_Obj *));
+ for(k = 0; ae->addr.list[k]; k++)
+ objav[k] = Tcl_NewStringObj(ae->addr.list[k], -1);
+
+ objev[++n] = Tcl_NewListObj(k, objav);
+ fs_give((void **) &objav);
+ }
+ break;
+ case Filecopy :
+ objev[++n] = Tcl_NewStringObj(ae->fcc ? ae->fcc : "", -1);
+ break;
+ case Comment :
+ objev[++n] = Tcl_NewStringObj(ae->extra ? ae->extra : "", -1);
+ break;
+ default :
+ s = NULL;
+ break;
+ }
+ }
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewListObj(n + 1, objev));
+ }
+
+ return(TCL_OK);
+ }
+
+ snprintf(buf, sizeof(buf), "PEAddress list: unknown address book number \"%d\"", booknum);
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ }
+ else if(objc == 4){
+ if(!strcmp(op, "verify")){
+ /*
+ * The object here is to check the following list of field values
+ * to see that they are valid address list, expanding if necessary.
+ * The first argument is the list of field values, with "to" being
+ * first. The second arg is the current fcc value.
+ *
+ * The return value is of the following form:
+ *
+ * { {{errstr {{oldstr newstr {ldap-opts ...}} ...}} ...} newfcc}
+ */
+ Tcl_Obj **objVal;
+ char *addrstr, *newaddr = NULL, *error = NULL,
+ *tstr1, *tstr2, *fcc, *newfcc = NULL;
+ BuildTo toaddr;
+ int rv, badadrs, i , numlistvals,
+ numldapqueries = 0;
+ Tcl_Obj *resObj = NULL, *secObj, *strObj, *adrObj, *res2Obj;
+#ifdef ENABLE_LDAP
+ WPLDAPRES_S **tsl;
+
+ wpldap_global->query_no++;
+ if(wpldap_global->ldap_search_list){
+ wpldap_global->ldap_search_list =
+ free_wpldapres(wpldap_global->ldap_search_list);
+ }
+
+ tsl = &(wpldap_global->ldap_search_list);
+#endif /* ENABLE_LDAP */
+
+ if(Tcl_ListObjGetElements(interp, objv[2], &numlistvals,
+ &objVal) == TCL_OK){
+ if((fcc = Tcl_GetStringFromObj(objv[3], NULL)) == NULL)
+ return TCL_ERROR;
+ res2Obj = Tcl_NewListObj(0, NULL);
+ for(i = 0; i < numlistvals; i++){
+ size_t l;
+
+ if((addrstr = Tcl_GetStringFromObj(objVal[i], NULL)) == NULL)
+ return TCL_ERROR;
+
+ addrstr = cpystr(addrstr); /* can't munge tcl copy */
+ toaddr.type = Str;
+ toaddr.arg.str = cpystr(addrstr);
+ l = strlen(addrstr);
+ badadrs = 0;
+ resObj = Tcl_NewListObj(0, NULL);
+ secObj = Tcl_NewListObj(0, NULL);
+ for(tstr1 = addrstr; tstr1; tstr1 = tstr2){
+ tstr2 = strqchr(tstr1, ',', 0, -1);
+ if(tstr2)
+ *tstr2 = '\0';
+
+ strncpy(toaddr.arg.str, tstr1, l);
+ toaddr.arg.str[l] = '\0';
+
+ removing_leading_and_trailing_white_space(toaddr.arg.str);
+ if(*toaddr.arg.str){
+ if(i == 0 && tstr1 == addrstr)
+ newfcc = cpystr(fcc);
+
+ rv = our_build_address(toaddr, &newaddr, &error, &newfcc, NULL);
+
+ if(rv == 0){
+ strObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, strObj,
+ Tcl_NewStringObj(toaddr.arg.str, -1));
+ Tcl_ListObjAppendElement(interp, strObj,
+ Tcl_NewStringObj(newaddr,-1));
+ /* append whether or not ldap stuff
+ * was returned
+ */
+ adrObj = Tcl_NewListObj(0,NULL);
+#ifdef ENABLE_LDAP
+ if(*tsl) {
+ LDAP_CHOOSE_S *tres;
+ LDAP_SERV_RES_S *trl;
+ LDAPMessage *e;
+ ADDRESS *newadr;
+ char *ret_to;
+
+ tres = (LDAP_CHOOSE_S *)fs_get(sizeof(LDAP_CHOOSE_S));
+ for(trl = (*tsl)->reslist; trl;
+ trl = trl->next){
+ for(e = ldap_first_entry(trl->ld,
+ trl->res);
+ e != NULL;
+ e = ldap_next_entry(trl->ld, e)){
+ tres->ld = trl->ld;
+ tres->selected_entry = e;
+ tres->info_used = trl->info_used;
+ tres->serv = trl->serv;
+ if((newadr = address_from_ldap(tres)) != NULL){
+ if(newadr->mailbox && newadr->host){
+ RFC822BUFFER rbuf;
+ size_t len;
+
+ len = est_size(newadr);
+ ret_to = (char *)fs_get(len * sizeof(char));
+ ret_to[0] = '\0';
+ rbuf.f = dummy_soutr;
+ rbuf.s = NULL;
+ rbuf.beg = ret_to;
+ rbuf.cur = ret_to;
+ rbuf.end = ret_to+len-1;
+ rfc822_output_address_list(&rbuf, newadr, 0L, NULL);
+ *rbuf.cur = '\0';
+ Tcl_ListObjAppendElement(interp,
+ adrObj, Tcl_NewStringObj(ret_to, -1));
+ fs_give((void **)&ret_to);
+ }
+ mail_free_address(&newadr);
+ }
+ }
+ }
+ fs_give((void **)&tres);
+ numldapqueries++;
+ tsl = &((*tsl)->next);
+ }
+#endif /* ENABLE_LDAP */
+ Tcl_ListObjAppendElement(interp, strObj, adrObj);
+ Tcl_ListObjAppendElement(interp, secObj, strObj);
+ }
+ else {
+ badadrs = 1;
+ break;
+ }
+ }
+ if(tstr2){
+ *tstr2 = ',';
+ tstr2++;
+ }
+ }
+ resObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(badadrs
+ ? (error ? error : "Unknown")
+ : "", -1));
+ Tcl_ListObjAppendElement(interp, resObj, secObj);
+ Tcl_ListObjAppendElement(interp, res2Obj, resObj);
+ fs_give((void **) &addrstr);
+ fs_give((void **) &toaddr.arg.str);
+ }
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), res2Obj);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(newfcc ? newfcc
+ : (fcc ? fcc : ""), -1));
+ if(newfcc) fs_give((void **)&newfcc);
+ return(TCL_OK);
+ }
+ return(TCL_ERROR);
+ }
+ else if(!strcmp(op, "expand")){
+ BuildTo toaddr;
+ char *addrstr, *newaddr = NULL, *error = NULL, *fcc, *newfcc = NULL;
+ int rv;
+
+ /*
+ * Return value will be of the form:
+ * {"addrstr",
+ * ldap-query-number,
+ * "fcc"
+ * }
+ *
+ * ldap-query-number will be nonzero if
+ * there is something interesting to display as a result
+ * of an ldap query.
+ */
+
+ /*
+ * Given what looks like an rfc822 address line, parse the
+ * contents and expand any tokens that look like they ought
+ * to be.
+ */
+
+ if((addrstr = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
+ toaddr.type = Str;
+ toaddr.arg.str = cpystr(addrstr); /* can't munge tcl copy */
+ fcc = Tcl_GetStringFromObj(objv[3], NULL);
+#ifdef ENABLE_LDAP
+ wpldap_global->query_no++;
+ if(wpldap_global->ldap_search_list){
+ wpldap_global->ldap_search_list =
+ free_wpldapres(wpldap_global->ldap_search_list);
+ }
+#endif /* ENABLE_LDAP */
+ newfcc = cpystr(fcc);
+ rv = our_build_address(toaddr, &newaddr, &error, &newfcc, NULL);
+ fs_give((void **) &toaddr.arg.str);
+ if(rv == 0){
+#ifdef ENABLE_LDAP
+ /*
+ * c-client quotes results with spaces in them, so we'll go
+ * through and unquote them.
+ */
+ if(wpldap_global->ldap_search_list){
+ WPLDAPRES_S *tres;
+ char *tstr1, *tstr2;
+ char *qstr1, *newnewaddr;
+ int qstr1len;
+
+ for(tres = wpldap_global->ldap_search_list;
+ tres; tres = tres->next){
+ if(strqchr(tres->str, ' ', 0, -1)){
+ qstr1len = strlen(tres->str) + 3;
+ qstr1 = (char *)fs_get(qstr1len*sizeof(char));
+ snprintf(qstr1, qstr1len, "\"%.*s\"", qstr1len, tres->str);
+ for(tstr1 = newaddr; tstr1; tstr1 = tstr2){
+ tstr2 = strqchr(tstr1, ',', 0, -1);
+ if(strncmp(qstr1, tstr1, tstr2 ? tstr2 - tstr1
+ : strlen(tstr1)) == 0){
+ size_t l;
+ l = strlen(newaddr) + strlen(tres->str) + 2
+ + (tstr2 ? strlen(tstr2) : 0);
+ newnewaddr = (char *) fs_get(l * sizeof(char));
+ snprintf(newnewaddr, l, "%.*s%s%s", tstr1 - newaddr,
+ newaddr, tres->str, tstr2 ? tstr2 : "");
+ fs_give((void **)&newaddr);
+ newaddr = newnewaddr;
+ break;
+ }
+ if(tstr2)
+ tstr2++;
+ if(tstr2 && *tstr2 == ' ')
+ tstr2++;
+ }
+ if(qstr1)
+ fs_give((void **) &qstr1);
+ }
+ }
+ }
+#endif /* ENABLE_LDAP */
+ if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(newaddr, -1)) != TCL_OK)
+ return(TCL_ERROR);
+#ifdef ENABLE_LDAP
+ if(wpldap_global->ldap_search_list){
+ if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewIntObj(wpldap_global->query_no)) != TCL_OK)
+ return(TCL_ERROR);
+ }
+ else
+#endif /* ENABLE_LDAP */
+ if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewIntObj(0)) != TCL_OK)
+ return(TCL_ERROR);
+ if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(newfcc ? newfcc
+ : (fcc ? fcc : ""), -1)) != TCL_OK)
+ return(TCL_ERROR);
+ if(newfcc) fs_give((void **)&newfcc);
+
+ return(TCL_OK);
+ }
+ else{
+ Tcl_SetResult(interp, error ? error : "Indeterminate error", TCL_VOLATILE);
+ if(newfcc) fs_give((void **)&newfcc);
+ return(TCL_ERROR);
+ }
+ }
+ }
+ else if(!strcmp(op, "complete")){
+ /*
+ * CMD: complete uid
+ *
+ * Look for possible completions for
+ * given query_string.
+ *
+ * ARGS: <query_string> <uid>
+ *
+ * Returns: candidate list: {nickname {personal mailbox}}
+ */
+ char *query, *errstr;
+ long uid;
+ COMPLETE_S *completions, *cp;
+
+ if(peInitAddrbooks(interp, 0) == TCL_OK){
+ if((query = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
+ if(Tcl_GetLongFromObj(interp, objv[3], &uid) == TCL_OK){
+
+ completions = adrbk_list_of_completions(query,
+ ps_global->mail_stream,
+ uid,
+#ifdef ENABLE_LDAP
+ ((strlen(query) >= 5) ? ALC_INCLUDE_LDAP : 0) |
+#endif /* ENABLE_LDAP */
+ 0);
+
+ if(completions){
+ for(cp = completions; cp; cp = cp->next)
+ peAppListF(interp, Tcl_GetObjResult(interp), "%s %s %s",
+ cp->nickname ? cp->nickname : "",
+ cp->full_address ? cp->full_address : "",
+ cp->fcc ? cp->fcc : "");
+
+ free_complete_s(&completions);
+ }
+ else
+ Tcl_SetResult(interp, "", TCL_STATIC);
+
+ return(TCL_OK);
+ }
+ else
+ errstr = "PEAddress: Cannot read UID";
+ }
+ else
+ errstr = "PEAddress: Cannot get completion query";
+ }
+ else
+ errstr = "PEAddress: Address Book initialization failed";
+
+ Tcl_SetResult(interp, errstr, TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ }
+ else if(objc == 5){
+ if(!strcmp(op, "entry")){
+ int booknum, i, aindex;
+ char *nick, *astr = NULL, *errstr = NULL, *fccstr = NULL, buf[128];
+ AdrBk_Entry *ae;
+ BuildTo bldto;
+
+ if(peInitAddrbooks(interp, 0) != TCL_OK)
+ return(TCL_ERROR);
+
+ /*
+ * Given an address book handle and nickname, return address
+ */
+ if(Tcl_GetIntFromObj(interp, objv[2], &booknum) == TCL_OK)
+ for(i = 0; i < as.n_addrbk; i++)
+ if(i == booknum){
+ if((nick = Tcl_GetStringFromObj(objv[3], NULL)) == NULL){
+ Tcl_SetResult(interp, "PEAddress list: Can't get nickname", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ if(Tcl_GetIntFromObj(interp, objv[4], &aindex) != TCL_OK
+ || (*nick == '\0' && aindex < 0)){
+ Tcl_SetResult(interp, "PEAddress list: Can't get aindex", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ if((*nick)
+ ? (ae = adrbk_lookup_by_nick(as.adrbks[booknum].address_book, nick, NULL))
+ : (ae = adrbk_get_ae(as.adrbks[booknum].address_book, aindex))){
+ bldto.type = Abe;
+ bldto.arg.abe = ae;
+
+ (void) our_build_address(bldto, &astr, &errstr, &fccstr, NULL);
+
+ if(errstr){
+ if(astr)
+ fs_give((void **) &astr);
+
+ Tcl_SetResult(interp, errstr, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ if(astr){
+ char *p;
+ int l;
+
+ l = (4*strlen(astr) + 1) * sizeof(char);
+ p = (char *) fs_get(l);
+ if(rfc1522_decode_to_utf8((unsigned char *) p, l, astr) == (unsigned char *) p){
+ fs_give((void **) &astr);
+ astr = p;
+ }
+ else
+ fs_give((void **)&p);
+ }
+ }
+
+ if(astr){
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(astr, -1));
+ fs_give((void **) &astr);
+
+ if(fccstr){
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(*fccstr ? fccstr : "\"\"", -1));
+ fs_give((void **) &fccstr);
+ }
+ else
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj("", -1));
+ }
+ else
+ Tcl_SetResult(interp, "", TCL_STATIC);
+
+ return(TCL_OK);
+ }
+
+ snprintf(buf, sizeof(buf), "PEAddress list: unknown address book ID %d", booknum);
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ else if(!strcmp(op, "fullentry")){
+ int booknum, j, aindex;
+ char *nick;
+ AdrBk_Entry *ae;
+ Tcl_Obj *resObj;
+
+ if(peInitAddrbooks(interp, 0) != TCL_OK)
+ return(TCL_ERROR);
+
+ /*
+ * Given an address book handle and nickname, return
+ * nickname, fullname, address(es), fcc, and comments
+ */
+ if(Tcl_GetIntFromObj(interp, objv[2], &booknum) == TCL_OK){
+ if(booknum >= 0 && booknum < as.n_addrbk){
+ if((nick = Tcl_GetStringFromObj(objv[3], NULL)) == NULL)
+ return(TCL_ERROR);
+ if(Tcl_GetIntFromObj(interp, objv[4], &aindex) != TCL_OK
+ || (*nick == '\0' && aindex < 0))
+ return(TCL_ERROR);
+ if((*nick)
+ ? (ae = adrbk_lookup_by_nick(as.adrbks[booknum].address_book, nick, NULL))
+ : (ae = adrbk_get_ae(as.adrbks[booknum].address_book, aindex))){
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(ae->nickname ? ae->nickname : "", -1));
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(ae->fullname ? ae->fullname : "", -1));
+ resObj = Tcl_NewListObj(0,NULL);
+ if(ae->tag == Single)
+ Tcl_ListObjAppendElement(interp,
+ resObj,
+ Tcl_NewStringObj(ae->addr.addr ? ae->addr.addr : "", -1));
+ else {
+ for(j = 0; ae->addr.list[j]; j++)
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(ae->addr.list[j] ? ae->addr.list[j] : "", -1));
+ }
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), resObj);
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(ae->fcc ? ae->fcc : "", -1));
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(ae->extra ? ae->extra : "", -1));
+ return(TCL_OK);
+ }
+ }
+ }
+ return(TCL_ERROR);
+ }
+ else if(!strcmp(op, "delete")){
+ char *nick, buf[256];
+ int booknum, aindex;
+ adrbk_cntr_t old_entry;
+ AdrBk *ab;
+ if(peInitAddrbooks(interp, 0) != TCL_OK){
+ snprintf(buf, sizeof(buf), "PEAddress delete: couldn't init addressbooks");
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ if(Tcl_GetIntFromObj(interp, objv[2], &booknum) == TCL_OK){
+ nick = Tcl_GetStringFromObj(objv[3], NULL);
+ removing_leading_and_trailing_white_space(nick);
+ }
+ else
+ return(TCL_ERROR);
+ if(booknum >= 0 && booknum < as.n_addrbk) {
+ if(as.adrbks[booknum].access != ReadWrite) return TCL_ERROR;
+ ab = as.adrbks[booknum].address_book;
+ }
+ else{
+ snprintf(buf, sizeof(buf), "PEAddress delete: Book number out of range");
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ if((Tcl_GetIntFromObj(interp, objv[4], &aindex) != TCL_OK)
+ || (*nick == '\0' && aindex < 0))
+ return(TCL_ERROR);
+ adrbk_check_validity(ab, 1L);
+ if(ab->flags & FILE_OUTOFDATE ||
+ (ab->rd && ab->rd->flags & REM_OUTOFDATE)){
+ Tcl_SetResult(interp,
+ "Address book out of sync. Cannot update at this moment",
+ TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ if(!nick){
+ snprintf(buf, sizeof(buf), "PEAddress delete: No nickname");
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ if((*nick)
+ ? (!adrbk_lookup_by_nick(ab, nick, &old_entry))
+ : ((old_entry = (adrbk_cntr_t)aindex) == -1)){
+ snprintf(buf, sizeof(buf), "PEAddress delete: Nickname \"%.128s\" not found", nick);
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ if(adrbk_delete(ab, old_entry, 0, 0, 1, 1)){
+ snprintf(buf, sizeof(buf), "PEAddress delete: Couldn't delete addressbook entry");
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ return(TCL_OK);
+ }
+ }
+ else if((objc == 10 || objc == 11) && !strcmp(op, "edit")){
+ if(!strcmp(op, "edit")){
+ int booknum, adri, add, rv, aindex;
+ char *nick, *fn, *fcc, *comment, *addrfield,
+ buf[256], **addrs, *orignick = NULL;
+ AdrBk_Entry *ae = NULL;
+ AdrBk *ab;
+ adrbk_cntr_t old_entry = NO_NEXT, new_entry;
+ Tag tag;
+ ADDRESS *adr = NULL;
+
+
+ if(peInitAddrbooks(interp, 0) != TCL_OK)
+ return(TCL_ERROR);
+ if(Tcl_GetIntFromObj(interp, objv[2], &booknum) == TCL_OK){
+ if(as.adrbks[booknum].access != ReadWrite) return TCL_ERROR;
+ nick = Tcl_GetStringFromObj(objv[3], NULL);
+ removing_leading_and_trailing_white_space(nick);
+ if(Tcl_GetIntFromObj(interp, objv[4], &aindex) != TCL_OK){
+ Tcl_SetResult(interp, "No Address Handle", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ fn = Tcl_GetStringFromObj(objv[5], NULL);
+ removing_leading_and_trailing_white_space(fn);
+ if(!*fn) fn = NULL;
+ addrfield = Tcl_GetStringFromObj(objv[6], NULL);
+ removing_leading_and_trailing_white_space(addrfield);
+ if(!*addrfield) addrfield = NULL;
+ /*
+ if(Tcl_ListObjGetElements(interp, objv[7], &numlistvals, &objVal) != TCL_OK)
+ return(TCL_ERROR);
+ */
+ fcc = Tcl_GetStringFromObj(objv[7], NULL);
+ removing_leading_and_trailing_white_space(fcc);
+ if(!*fcc) fcc = NULL;
+ comment = Tcl_GetStringFromObj(objv[8], NULL);
+ removing_leading_and_trailing_white_space(comment);
+ if(!*comment) comment = NULL;
+ if(Tcl_GetIntFromObj(interp, objv[9], &add) != TCL_OK)
+ return(TCL_ERROR);
+ if(objc == 11) {
+ /*
+ * if objc == 11 then that means that they changed the
+ * value of nick to something else, and this one is the
+ * original nick
+ */
+ orignick = Tcl_GetStringFromObj(objv[10], NULL);
+ removing_leading_and_trailing_white_space(orignick);
+ }
+ if((addrs = parse_addrlist(addrfield)) != NULL){
+ int tbuflen = strlen(addrfield);
+ char *tbuf;
+ if(!(tbuf = (char *) fs_get(sizeof(char) * (tbuflen+128)))){
+ Tcl_SetResult(interp, "malloc error", TCL_VOLATILE);
+ fs_give((void **) &addrs);
+ return(TCL_ERROR);
+ }
+ for(adri = 0; addrs[adri]; adri++){
+ if(*(addrs[adri])){
+ ps_global->c_client_error[0] = '\0';
+ strncpy(tbuf, addrs[adri], tbuflen+128);
+ tbuf[tbuflen+128-1] = '\0';
+ rfc822_parse_adrlist(&adr, tbuf, "@");
+ if(adr) mail_free_address(&adr);
+ adr = NULL;
+ if(ps_global->c_client_error[0]){
+ snprintf(buf, sizeof(buf),"Problem with address %.10s%s: %s",
+ addrs[adri], strlen(addrs[adri]) > 10 ?
+ "..." : "", ps_global->c_client_error);
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ if(tbuf)
+ fs_give((void **) &tbuf);
+ fs_give((void **) &addrs);
+ return(TCL_ERROR);
+ }
+ }
+ }
+ if(tbuf) fs_give((void **)&tbuf);
+ }
+ else adri = 0;
+
+ /* addrs[adri] = NULL; */
+
+ if(adri > 1) tag = List;
+ else tag = Single;
+
+ if(booknum >= 0 && booknum < as.n_addrbk) {
+ ab = as.adrbks[booknum].address_book;
+ }
+ else{
+ if(addrs)
+ fs_give((void **) &addrs);
+ return(TCL_ERROR);
+ }
+ adrbk_check_validity(ab, 1L);
+ if(ab->flags & FILE_OUTOFDATE ||
+ (ab->rd && ab->rd->flags & REM_OUTOFDATE)){
+ Tcl_SetResult(interp,
+ "Address book out of sync. Cannot update at this moment",
+ TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ if(aindex >= 0){
+ ae = adrbk_get_ae(as.adrbks[booknum].address_book, aindex);
+ if(ae){
+ old_entry = (adrbk_cntr_t) aindex;
+ }
+ else{
+ Tcl_SetResult(interp, "No Address Handle!", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ }
+ else if(nick && *nick && adrbk_lookup_by_nick(ab, nick, NULL)){
+ snprintf(buf, sizeof(buf), "Entry with nickname %.128s already exists.",
+ nick);
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ if(addrs)
+ fs_give((void **) &addrs);
+ return(TCL_ERROR);
+ }
+ if(ae &&
+ ((tag == List && ae->tag == Single) ||
+ (tag == Single && ae->tag == List))){
+ if(adrbk_delete(ab, old_entry, 0,0,1,0)){
+ snprintf(buf, sizeof(buf), "Problem updating from %s to %s.",
+ ae->tag == Single ? "Single" : "List",
+ tag == List ? "List" : "Single");
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ if(addrs)
+ fs_give((void **) &addrs);
+ return(TCL_ERROR);
+ }
+ old_entry = NO_NEXT;
+ }
+ if((rv = adrbk_add(ab, old_entry,
+ nick ? nick : "",
+ fn ? fn : "",
+ tag == List ? (char *)addrs :
+ (addrs && *addrs) ? *addrs : "",
+ fcc ? fcc : "",
+ comment ? comment : "",
+ tag, &new_entry, NULL, 0, 1,
+ tag == List ? 0 : 1)) != 0){
+ snprintf(buf, sizeof(buf), "Couldn't add entry! rv=%d.", rv);
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ if(addrs)
+ fs_give((void **) &addrs);
+ return(TCL_ERROR);
+ }
+ if(tag == List) {
+ adrbk_listdel_all(ab, new_entry);
+ adrbk_nlistadd(ab, new_entry, NULL, NULL, addrs, 0, 1, 1);
+ }
+ return(TCL_OK);
+ }
+ snprintf(buf, sizeof(buf), "Unknown address book ID %d", booknum);
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ }
+ }
+
+ Tcl_SetResult(interp, "PEAddress: unrecognized command", TCL_STATIC);
+ return(TCL_ERROR);
+}
+
+
+int
+peInitAddrbooks(Tcl_Interp *interp, int safe)
+{
+ if(ps_global->remote_abook_validity > 0)
+ (void)adrbk_check_and_fix_all(safe, 0, 0);
+
+ if(!init_addrbooks(NoDisplay, 1, 1, 0)){
+ Tcl_SetResult(interp, "No Address Book Configured", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ return(TCL_OK);
+}
+
+
+
+int
+peRuleStatVal(char *str, int *n)
+{
+ if(!str)
+ return(1);
+
+ if(!strcmp(str, "either"))
+ *n = PAT_STAT_EITHER;
+ else if(!strcmp(str, "yes"))
+ *n = PAT_STAT_YES;
+ else if(!strcmp(str, "no"))
+ *n = PAT_STAT_NO;
+ else
+ return 1;
+
+ return 0;
+}
+
+
+#define RS_RULE_EDIT 0x0001
+#define RS_RULE_ADD 0x0002
+#define RS_RULE_DELETE 0x0004
+#define RS_RULE_SHUFFUP 0x0008
+#define RS_RULE_SHUFFDOWN 0x0010
+#define RS_RULE_GETPAT 0x0100
+#define RS_RULE_FINDPAT 0x0200
+
+int
+peRuleSet(Tcl_Interp *interp, Tcl_Obj **objv)
+{
+ char *rule, *patvar, *patval, *actvar, *actval, *tstr, *ruleaction;
+ int rno, nPat, nPatEmnt, nAct, nActEmnt, i, rv = 0;
+ Tcl_Obj **objPat, **objPatEmnt, **objAct, **objActEmnt;
+ long rflags = PAT_USE_CHANGED, aflags = 0;
+ PAT_STATE pstate;
+ PAT_S *pat, *new_pat;
+
+ if(!(rule = Tcl_GetStringFromObj(objv[0], NULL)))
+ return(TCL_ERROR);
+
+ if(!(ruleaction = Tcl_GetStringFromObj(objv[1], NULL)))
+ return(TCL_ERROR);
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &rno) == TCL_ERROR)
+ return(TCL_ERROR);
+
+ if(!(strcmp(rule, "filter")))
+ rflags |= ROLE_DO_FILTER;
+ else if(!(strcmp(rule, "score")))
+ rflags |= ROLE_DO_SCORES;
+ else if(!(strcmp(rule, "indexcolor")))
+ rflags |= ROLE_DO_INCOLS;
+ else
+ return(TCL_ERROR);
+
+ if(!(strcmp(ruleaction, "edit"))){
+ aflags |= RS_RULE_EDIT;
+ aflags |= RS_RULE_GETPAT;
+ aflags |= RS_RULE_FINDPAT;
+ }
+ else if(!(strcmp(ruleaction, "add"))){
+ aflags |= RS_RULE_ADD;
+ aflags |= RS_RULE_GETPAT;
+ }
+ else if(!(strcmp(ruleaction, "delete"))){
+ aflags |= RS_RULE_DELETE;
+ aflags |= RS_RULE_FINDPAT;
+ }
+ else if(!(strcmp(ruleaction, "shuffup"))){
+ aflags |= RS_RULE_SHUFFUP;
+ aflags |= RS_RULE_FINDPAT;
+ }
+ else if(!(strcmp(ruleaction, "shuffdown"))){
+ aflags |= RS_RULE_SHUFFDOWN;
+ aflags |= RS_RULE_FINDPAT;
+ }
+ else return(TCL_ERROR);
+
+ if(aflags & RS_RULE_FINDPAT){
+ if(any_patterns(rflags, &pstate)){
+ for(pat = first_pattern(&pstate), i = 0;
+ pat && i != rno;
+ pat = next_pattern(&pstate), i++);
+ if(i != rno) return(TCL_ERROR);
+ }
+ }
+ if(aflags & RS_RULE_GETPAT){
+ int tcl_error = 0;
+
+ Tcl_ListObjGetElements(interp, objv[3], &nPat, &objPat);
+ Tcl_ListObjGetElements(interp, objv[4], &nAct, &objAct);
+
+ new_pat = (PAT_S *)fs_get(sizeof(PAT_S));
+ memset(new_pat, 0, sizeof(PAT_S));
+ new_pat->patgrp = (PATGRP_S *)fs_get(sizeof(PATGRP_S));
+ memset(new_pat->patgrp, 0, sizeof(PATGRP_S));
+ new_pat->action = (ACTION_S *)fs_get(sizeof(ACTION_S));
+ memset(new_pat->action, 0, sizeof(ACTION_S));
+
+ /* Set up the pattern group */
+ for(i = 0; i < nPat; i++){
+ Tcl_ListObjGetElements(interp, objPat[i], &nPatEmnt, &objPatEmnt);
+ if(nPatEmnt != 2) return(TCL_ERROR);
+ patvar = Tcl_GetStringFromObj(objPatEmnt[0], NULL);
+ patval = Tcl_GetStringFromObj(objPatEmnt[1], NULL);
+ if(!patvar || !patval) return(TCL_ERROR);
+
+ tstr = NULL;
+ if(*patval){
+ tstr = cpystr(patval);
+ removing_leading_and_trailing_white_space(tstr);
+ if(!(*tstr))
+ fs_give((void **) &tstr);
+ }
+
+ if(!(strcmp(patvar, "nickname"))){
+ new_pat->patgrp->nick = tstr;
+ tstr = NULL;
+ }
+ else if(!(strcmp(patvar, "comment"))){
+ new_pat->patgrp->comment = tstr;
+ tstr = NULL;
+ }
+ else if(!(strcmp(patvar, "to"))){
+ new_pat->patgrp->to = string_to_pattern(tstr);
+ }
+ else if(!(strcmp(patvar, "from"))){
+ new_pat->patgrp->from = string_to_pattern(tstr);
+ }
+ else if(!(strcmp(patvar, "sender"))){
+ new_pat->patgrp->sender = string_to_pattern(tstr);
+ }
+ else if(!(strcmp(patvar, "cc"))){
+ new_pat->patgrp->cc = string_to_pattern(tstr);
+ }
+ else if(!(strcmp(patvar, "recip"))){
+ new_pat->patgrp->recip = string_to_pattern(tstr);
+ }
+ else if(!(strcmp(patvar, "partic"))){
+ new_pat->patgrp->partic = string_to_pattern(tstr);
+ }
+ else if(!(strcmp(patvar, "news"))){
+ new_pat->patgrp->news = string_to_pattern(tstr);
+ }
+ else if(!(strcmp(patvar, "subj"))){
+ new_pat->patgrp->subj = string_to_pattern(tstr);
+ }
+ else if(!(strcmp(patvar, "bodytext"))){
+ new_pat->patgrp->bodytext = string_to_pattern(tstr);
+ }
+ else if(!(strcmp(patvar, "alltext"))){
+ new_pat->patgrp->alltext = string_to_pattern(tstr);
+ }
+ else if(!(strcmp(patvar, "keyword"))){
+ new_pat->patgrp->keyword = string_to_pattern(tstr);
+ }
+ else if(!(strcmp(patvar, "charset"))){
+ new_pat->patgrp->charsets = string_to_pattern(tstr);
+ }
+ else if(!(strcmp(patvar, "ftype"))){
+ if(!tstr) return(TCL_ERROR);
+
+ if(!(strcmp(tstr, "any")))
+ new_pat->patgrp->fldr_type = FLDR_ANY;
+ else if(!(strcmp(tstr, "news")))
+ new_pat->patgrp->fldr_type = FLDR_NEWS;
+ else if(!(strcmp(tstr, "email")))
+ new_pat->patgrp->fldr_type = FLDR_EMAIL;
+ else if(!(strcmp(tstr, "specific")))
+ new_pat->patgrp->fldr_type = FLDR_SPECIFIC;
+ else{
+ free_pat(&new_pat);
+ return(TCL_ERROR);
+ }
+ }
+ else if(!(strcmp(patvar, "folder"))){
+ new_pat->patgrp->folder = string_to_pattern(tstr);
+ }
+ else if(!(strcmp(patvar, "stat_new"))){
+ if(peRuleStatVal(tstr, &new_pat->patgrp->stat_new)){
+ free_pat(&new_pat);
+ tcl_error++;
+ }
+ }
+ else if(!(strcmp(patvar, "stat_rec"))){
+ if(peRuleStatVal(tstr, &new_pat->patgrp->stat_rec)){
+ free_pat(&new_pat);
+ tcl_error++;
+ }
+ }
+ else if(!(strcmp(patvar, "stat_del"))){
+ if(peRuleStatVal(tstr, &new_pat->patgrp->stat_del)){
+ free_pat(&new_pat);
+ tcl_error++;
+ }
+ }
+ else if(!(strcmp(patvar, "stat_imp"))){
+ if(peRuleStatVal(tstr, &new_pat->patgrp->stat_imp)){
+ free_pat(&new_pat);
+ tcl_error++;
+ }
+ }
+ else if(!(strcmp(patvar, "stat_ans"))){
+ if(peRuleStatVal(tstr, &new_pat->patgrp->stat_ans)){
+ free_pat(&new_pat);
+ tcl_error++;
+ }
+ }
+ else if(!(strcmp(patvar, "stat_8bitsubj"))){
+ if(peRuleStatVal(tstr, &new_pat->patgrp->stat_8bitsubj)){
+ free_pat(&new_pat);
+ tcl_error++;
+ }
+ }
+ else if(!(strcmp(patvar, "stat_bom"))){
+ if(peRuleStatVal(tstr, &new_pat->patgrp->stat_bom)){
+ free_pat(&new_pat);
+ tcl_error++;
+ }
+ }
+ else if(!(strcmp(patvar, "stat_boy"))){
+ if(peRuleStatVal(tstr, &new_pat->patgrp->stat_boy)){
+ free_pat(&new_pat);
+ tcl_error++;
+ }
+ }
+ else if(!(strcmp(patvar, "age"))){
+ new_pat->patgrp->age = parse_intvl(tstr);
+ }
+ else if(!(strcmp(patvar, "size"))){
+ new_pat->patgrp->size = parse_intvl(tstr);
+ }
+ else if(!(strcmp(patvar, "score"))){
+ new_pat->patgrp->score = parse_intvl(tstr);
+ }
+ else if(!(strcmp(patvar, "addrbook"))){
+ if(tstr){
+ if(!strcmp(tstr, "either"))
+ new_pat->patgrp->inabook = IAB_EITHER;
+ else if(!strcmp(tstr, "yes"))
+ new_pat->patgrp->inabook = IAB_YES;
+ else if(!strcmp(tstr, "no"))
+ new_pat->patgrp->inabook = IAB_NO;
+ else if(!strcmp(tstr, "yesspecific"))
+ new_pat->patgrp->inabook = IAB_SPEC_YES;
+ else if(!strcmp(tstr, "nospecific"))
+ new_pat->patgrp->inabook = IAB_SPEC_NO;
+ else
+ tcl_error++;
+ }
+ else
+ tcl_error++;
+
+ if(tcl_error)
+ free_pat(&new_pat);
+ }
+ else if(!(strcmp(patvar, "specificabook"))){
+ new_pat->patgrp->abooks = string_to_pattern(tstr);
+ }
+ else if(!(strcmp(patvar, "headers"))){
+ ARBHDR_S **ahp;
+ int nHdrList, nHdrPair, n;
+ Tcl_Obj **objHdrList, **objHdrPair;
+
+ Tcl_ListObjGetElements(interp, objPatEmnt[1], &nHdrList, &objHdrList);
+
+ for(ahp = &new_pat->patgrp->arbhdr; *ahp; ahp = &(*ahp)->next)
+ ;
+
+ for (n = 0; n < nHdrList; n++){
+ char *hdrfld;
+ char *hdrval;
+
+ Tcl_ListObjGetElements(interp, objHdrList[n], &nHdrPair, &objHdrPair);
+ if(nHdrPair != 2)
+ continue;
+
+ hdrfld = Tcl_GetStringFromObj(objHdrPair[0], NULL);
+ hdrval = Tcl_GetStringFromObj(objHdrPair[1], NULL);
+
+ if(hdrfld){
+ *ahp = (ARBHDR_S *) fs_get(sizeof(ARBHDR_S));
+ memset(*ahp, 0, sizeof(ARBHDR_S));
+
+ (*ahp)->field = cpystr(hdrfld);
+ if(hdrval){
+ (*ahp)->p = string_to_pattern(hdrval);
+ }
+ else
+ (*ahp)->isemptyval = 1;
+
+ ahp = &(*ahp)->next;
+ }
+ }
+ }
+ else{
+ free_pat(&new_pat);
+ tcl_error++;
+ }
+
+ if(tstr)
+ fs_give((void **) &tstr);
+
+ if(tcl_error)
+ return(TCL_ERROR);
+ }
+
+ if((new_pat->patgrp->inabook & (IAB_SPEC_YES | IAB_SPEC_NO)) == 0
+ && new_pat->patgrp->abooks)
+ free_pattern(&new_pat->patgrp->abooks);
+
+ if(new_pat->patgrp->fldr_type != FLDR_SPECIFIC && new_pat->patgrp->folder)
+ free_pattern(&new_pat->patgrp->folder);
+
+ /* set up the action */
+ if(!(strcmp(rule, "filter")))
+ new_pat->action->is_a_filter = 1;
+ else if(!(strcmp(rule, "role")))
+ new_pat->action->is_a_role = 1;
+ else if(!(strcmp(rule, "score")))
+ new_pat->action->is_a_score = 1;
+ else if(!(strcmp(rule, "indexcolor")))
+ new_pat->action->is_a_incol = 1;
+ else{
+ free_pat(&new_pat);
+ return(TCL_ERROR);
+ }
+
+ for(i = 0; i < nAct; i++){
+ Tcl_ListObjGetElements(interp, objAct[i], &nActEmnt, &objActEmnt);
+ if(nActEmnt !=2){
+ free_pat(&new_pat);
+ return(TCL_ERROR);
+ }
+
+ actvar = Tcl_GetStringFromObj(objActEmnt[0], NULL);
+ actval = Tcl_GetStringFromObj(objActEmnt[1], NULL);
+ if(!actvar || !actval){
+ free_pat(&new_pat);
+ return(TCL_ERROR);
+ }
+
+ if(new_pat->action->is_a_filter && !(strcmp(actvar, "action"))){
+ if(!strcmp(actval, "delete"))
+ new_pat->action->kill = 1;
+ else if(!strcmp(actval, "move"))
+ new_pat->action->kill = 0;
+ else{
+ free_pat(&new_pat);
+ return(TCL_ERROR);
+ }
+ }
+ else if(new_pat->action->is_a_filter && !(strcmp(actvar, "folder"))){
+ tstr = cpystr(actval);
+ removing_leading_and_trailing_white_space(tstr);
+ if(!(*tstr)) fs_give((void **)&tstr);
+ new_pat->action->folder = string_to_pattern(tstr);
+ if(tstr) fs_give((void **)&tstr);
+ }
+ else if(new_pat->action->is_a_filter && !(strcmp(actvar, "moind"))){
+ if(!strcmp(actval, "1"))
+ new_pat->action->move_only_if_not_deleted = 1;
+ else if(!strcmp(actval, "0"))
+ new_pat->action->move_only_if_not_deleted = 0;
+ else{
+ free_pat(&new_pat);
+ return(TCL_ERROR);
+ }
+ }
+ else if(new_pat->action->is_a_incol && !(strcmp(actvar, "fg"))){
+ char asciicolor[256];
+
+ if(ascii_colorstr(asciicolor, actval) == 0) {
+ if(!new_pat->action->incol){
+ new_pat->action->incol = new_color_pair(asciicolor,NULL);
+ }
+ else
+ snprintf(new_pat->action->incol->fg,
+ sizeof(new_pat->action->incol->fg), "%s", asciicolor);
+ }
+ }
+ else if(new_pat->action->is_a_incol && !(strcmp(actvar, "bg"))){
+ char asciicolor[256];
+
+ if(ascii_colorstr(asciicolor, actval) == 0) {
+ if(!new_pat->action->incol){
+ new_pat->action->incol = new_color_pair(NULL, asciicolor);
+ }
+ else
+ snprintf(new_pat->action->incol->bg,
+ sizeof(new_pat->action->incol->bg), "%s", asciicolor);
+ }
+ }
+ else if(new_pat->action->is_a_score && !(strcmp(actvar, "scoreval"))){
+ long scoreval = (long) atoi(actval);
+
+ if(scoreval >= SCORE_MIN && scoreval <= SCORE_MAX)
+ new_pat->action->scoreval = scoreval;
+ }
+ else if(new_pat->action->is_a_score && !(strcmp(actvar, "scorehdr"))){
+ HEADER_TOK_S *hdrtok;
+
+ if((hdrtok = stringform_to_hdrtok(actval)) != NULL)
+ new_pat->action->scorevalhdrtok = hdrtok;
+ }
+ else{
+ free_pat(&new_pat);
+ return(TCL_ERROR);
+ }
+ }
+
+ if(new_pat->action->is_a_filter && new_pat->action->kill && new_pat->action->folder)
+ fs_give((void **)&new_pat->action->folder);
+ else if(new_pat->action->is_a_filter && new_pat->action->kill == 0 && new_pat->action->folder == 0){
+ free_pat(&new_pat);
+ Tcl_SetResult(interp, "No folder set for Move", TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ }
+
+ if(aflags & RS_RULE_EDIT)
+ rv = edit_pattern(new_pat, rno, rflags);
+ else if(aflags & RS_RULE_ADD)
+ rv = add_pattern(new_pat, rflags);
+ else if(aflags & RS_RULE_DELETE)
+ rv = delete_pattern(rno, rflags);
+ else if(aflags & RS_RULE_SHUFFUP)
+ rv = shuffle_pattern(rno, 1, rflags);
+ else if(aflags & RS_RULE_SHUFFDOWN)
+ rv = shuffle_pattern(rno, -1, rflags);
+ else
+ rv = 1;
+
+ return(rv ? TCL_ERROR : TCL_OK);
+}
+
+
+#if 0
+ADDRESS *
+peAEToAddress(AdrBk_Entry *ae)
+{
+ char *list, *l1, *l2;
+ int length;
+ BuildTo bldto;
+ ADDRESS *addr = NULL;
+
+ if(ae->tag == List){
+ length = 0;
+ for(l2 = ae->addr.list; *l2; l2++)
+ length += (strlen(*l2) + 1);
+
+ list = (char *) fs_get(length + 1);
+ list[0] = '\0';
+ l1 = list;
+ for(l2 = ae->addr.list; *l2; l2++){
+ if(l1 != list && l1-list < length+1)
+ *l1++ = ',';
+
+ strncpy(l1, *l2, length+1-(l1-list));
+ l1 += strlen(l1);
+ }
+
+ list[length] = '\0';
+
+ bldto.type = Str;
+ bldto.arg.str = list;
+ adr2 = expand_address(bldto, userdomain, localdomain,
+ loop_detected, fcc, did_set,
+ lcc, error, 1, simple_verify,
+ mangled);
+
+ fs_give((void **) &list);
+ }
+ else if(ae->tag == Single){
+ if(strucmp(ae->addr.addr, a->mailbox)){
+ bldto.type = Str;
+ bldto.arg.str = ae->addr.addr;
+ adr2 = expand_address(bldto, userdomain,
+ localdomain, loop_detected,
+ fcc, did_set, lcc,
+ error, 1, simple_verify,
+ mangled);
+ }
+ else{
+ /*
+ * A loop within plain single entry is ignored.
+ * Set up so later code thinks we expanded.
+ */
+ adr2 = mail_newaddr();
+ adr2->mailbox = cpystr(ae->addr.addr);
+ adr2->host = cpystr(userdomain);
+ adr2->adl = cpystr(a->adl);
+ }
+ }
+
+ /*
+ * Personal names: If the expanded address has a personal
+ * name and the address book entry is a list with a fullname,
+ * tack the full name from the address book on in front.
+ * This mainly occurs with a distribution list where the
+ * list has a full name, and the first person in the list also
+ * has a full name.
+ *
+ * This algorithm doesn't work very well if lists are
+ * included within lists, but it's not clear what would
+ * be better.
+ */
+ if(ae->fullname && ae->fullname[0]){
+ if(adr2->personal && adr2->personal[0]){
+ if(ae->tag == List){
+ /* combine list name and existing name */
+ char *name;
+
+ if(!simple_verify){
+ size_t l;
+ l = strlen(adr2->personal) + strlen(ae->fullname) + 4;
+ name = (char *)fs_get((l+1) * sizeof(char));
+ snprintf(name, l+1, "%s -- %s", ae->fullname,
+ adr2->personal);
+ fs_give((void **)&adr2->personal);
+ adr2->personal = name;
+ }
+ }
+ else{
+ /* replace with nickname fullname */
+ fs_give((void **)&adr2->personal);
+ adr2->personal = adrbk_formatname(ae->fullname,
+ NULL, NULL);
+ }
+ }
+ else{
+ if(abe-p>tag != List || !simple_verify){
+ if(adr2->personal)
+ fs_give((void **)&adr2->personal);
+
+ adr2->personal = adrbk_formatname(abe->fullname,
+ NULL, NULL);
+ }
+ }
+ }
+
+ return(addr);
+}
+
+
+
+char *
+peAEFcc(AdrBk_Entry *ae)
+{
+ char *fcc = NULL;
+
+ if(ae->fcc && ae->fcc[0]){
+
+ if(!strcmp(ae->fcc, "\"\""))
+ fcc = cpystr("");
+ else
+ fcc = cpystr(ae->fcc);
+
+ }
+ else if(ae->nickname && ae->nickname[0] &&
+ (ps_global->fcc_rule == FCC_RULE_NICK ||
+ ps_global->fcc_rule == FCC_RULE_NICK_RECIP)){
+ /*
+ * else if fcc-rule=fcc-by-nickname, use that
+ */
+
+ fcc = cpystr(ae->nickname);
+ }
+
+ return(fcc);
+}
+#endif
+
+
+PINEFIELD *
+peCustomHdrs(void)
+{
+ extern PINEFIELD *parse_custom_hdrs(char **, CustomType);
+
+ return(parse_custom_hdrs(ps_global->VAR_CUSTOM_HDRS, UseAsDef));
+}
+
+
+
+/*
+ * PEClistCmd - Collection list editing tools
+ */
+int
+PEClistCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+ char *err = "Unknown PEClist request";
+
+ dprint((2, "PEClistCmd"));
+
+ if(objc == 1){
+ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
+ }
+ else{
+ char *s1 = Tcl_GetStringFromObj(objv[1], NULL);
+
+ if(s1){
+ if(objc == 3){ /* delete */
+ if(!strcmp(s1, "delete")){
+ int cl, i, n, deln;
+ char **newl;
+ CONTEXT_S *del_ctxt, *tmp_ctxt, *new_ctxt;
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR){
+ Tcl_SetResult(interp,
+ "cledit malformed: first arg must be int",
+ TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ for(i = 0, del_ctxt = ps_global->context_list;
+ del_ctxt && i < cl; i++, del_ctxt = del_ctxt->next);
+ if(!del_ctxt) return(TCL_ERROR);
+ for(n = 0; del_ctxt->var.v->current_val.l[n]; n++);
+ n--;
+ newl = (char **) fs_get((n + 1) * sizeof(char *));
+ newl[n] = NULL;
+ deln = del_ctxt->var.i;
+ for(i = 0; del_ctxt->var.v->current_val.l[i]; i++){
+ if(i < deln)
+ newl[i] = cpystr(del_ctxt->var.v->current_val.l[i]);
+ else if(i > deln)
+ newl[i-1] = cpystr(del_ctxt->var.v->current_val.l[i]);
+ }
+ n = set_variable_list(del_ctxt->var.v - ps_global->vars,
+ *newl ? newl : NULL, TRUE, Main);
+ free_list_array(&newl);
+ set_current_val(del_ctxt->var.v, TRUE, FALSE);
+ if(n){
+ Tcl_SetResult(interp,
+ "Error saving changes",
+ TCL_VOLATILE);
+ return TCL_OK;
+ }
+ for(tmp_ctxt = del_ctxt->next; tmp_ctxt && tmp_ctxt->var.v ==
+ del_ctxt->var.v; tmp_ctxt = tmp_ctxt->next)
+ tmp_ctxt->var.i--;
+ if((tmp_ctxt = del_ctxt->next) != NULL)
+ tmp_ctxt->prev = del_ctxt->prev;
+ if((tmp_ctxt = del_ctxt->prev) != NULL)
+ tmp_ctxt->next= del_ctxt->next;
+ if(!del_ctxt->prev && !del_ctxt->next){
+ new_ctxt = new_context(del_ctxt->var.v->current_val.l[0], NULL);
+ ps_global->context_list = new_ctxt;
+ if(!new_ctxt->var.v)
+ new_ctxt->var = del_ctxt->var;
+ }
+ else if(ps_global->context_list == del_ctxt){
+ ps_global->context_list = del_ctxt->next;
+ if(!ps_global->context_list)
+ return TCL_ERROR; /* this shouldn't happen */
+ }
+ if(ps_global->context_last == del_ctxt)
+ ps_global->context_last = NULL;
+ if(ps_global->context_current == del_ctxt){
+ strncpy(ps_global->cur_folder,
+ ps_global->mail_stream->mailbox,
+ sizeof(ps_global->cur_folder));
+ ps_global->cur_folder[sizeof(ps_global->cur_folder)-1] = '\0';
+ ps_global->context_current = ps_global->context_list;
+ }
+ del_ctxt->prev = NULL;
+ del_ctxt->next = NULL;
+ free_context(&del_ctxt);
+ init_inbox_mapping(ps_global->VAR_INBOX_PATH,
+ ps_global->context_list);
+ return TCL_OK;
+ }
+ else if(!strcmp(s1, "shuffdown")){
+ int cl, i, shn, n;
+ CONTEXT_S *sh_ctxt, *nsh_ctxt, *tctxt;
+ char **newl, *tmpch;
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR){
+ Tcl_SetResult(interp,
+ "cledit malformed: first arg must be int",
+ TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ for(sh_ctxt = ps_global->context_list, i = 0;
+ sh_ctxt && i < cl ; i++, sh_ctxt = sh_ctxt->next);
+ if(!sh_ctxt || !sh_ctxt->next){
+ Tcl_SetResult(interp,
+ "invalid context list number",
+ TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ if(sh_ctxt->var.v == sh_ctxt->next->var.v){
+ shn = sh_ctxt->var.i;
+ for(n = 0; sh_ctxt->var.v->current_val.l[n]; n++);
+ newl = (char **) fs_get((n + 1) * sizeof(char *));
+ newl[n] = NULL;
+ for(i = 0; sh_ctxt->var.v->current_val.l[i]; i++){
+ if(i == shn)
+ newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i+1]);
+ else if(i == shn + 1)
+ newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i-1]);
+ else
+ newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i]);
+ }
+ n = set_variable_list(sh_ctxt->var.v - ps_global->vars,
+ newl, TRUE, Main);
+ free_list_array(&newl);
+ set_current_val(sh_ctxt->var.v, TRUE, FALSE);
+ if(n){
+ Tcl_SetResult(interp,
+ "Error saving changes",
+ TCL_VOLATILE);
+ return TCL_OK;
+ }
+ nsh_ctxt = sh_ctxt->next;
+ nsh_ctxt->var.i--;
+ sh_ctxt->var.i++;
+ }
+ else{
+ nsh_ctxt = sh_ctxt->next;
+ shn = sh_ctxt->var.i;
+ tmpch = cpystr(sh_ctxt->var.v->current_val.l[shn]);
+ for(n = 0; sh_ctxt->var.v->current_val.l[n]; n++);
+ n--;
+ newl = (char **) fs_get((n + 1) * sizeof(char *));
+ newl[n] = NULL;
+ for(i = 0; sh_ctxt->var.v->current_val.l[i+1]; i++)
+ newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i]);
+ n = set_variable_list(sh_ctxt->var.v - ps_global->vars,
+ newl, FALSE, Main);
+ free_list_array(&newl);
+ set_current_val(sh_ctxt->var.v, TRUE, FALSE);
+ for(n = 0; nsh_ctxt->var.v->current_val.l[n]; n++);
+ n++;
+ newl = (char **) fs_get((n + 1) * sizeof(char *));
+ newl[n] = NULL;
+ newl[0] = cpystr(nsh_ctxt->var.v->current_val.l[0]);
+ newl[1] = tmpch;
+ for(i = 2; nsh_ctxt->var.v->current_val.l[i-1]; i++)
+ newl[i] = cpystr(nsh_ctxt->var.v->current_val.l[i-1]);
+ n = set_variable_list(nsh_ctxt->var.v - ps_global->vars,
+ newl, TRUE, Main);
+ free_list_array(&newl);
+ set_current_val(nsh_ctxt->var.v, TRUE, FALSE);
+ sh_ctxt->var.v = nsh_ctxt->var.v;
+ sh_ctxt->var.i = 1;
+ /* this for loop assumes that there are only two variable lists,
+ * folder-collections and news-collections, a little more will
+ * have to be done if we want to accomodate for the INHERIT
+ * option introduced in 4.30.
+ */
+ for(tctxt = nsh_ctxt->next; tctxt; tctxt = tctxt->next)
+ tctxt->var.i++;
+ }
+ if(sh_ctxt->prev) sh_ctxt->prev->next = nsh_ctxt;
+ nsh_ctxt->prev = sh_ctxt->prev;
+ sh_ctxt->next = nsh_ctxt->next;
+ nsh_ctxt->next = sh_ctxt;
+ sh_ctxt->prev = nsh_ctxt;
+ if(sh_ctxt->next) sh_ctxt->next->prev = sh_ctxt;
+ if(ps_global->context_list == sh_ctxt)
+ ps_global->context_list = nsh_ctxt;
+ init_inbox_mapping(ps_global->VAR_INBOX_PATH,
+ ps_global->context_list);
+ return TCL_OK;
+ }
+ else if(!strcmp(s1, "shuffup")){
+ int cl, i, shn, n;
+ CONTEXT_S *sh_ctxt, *psh_ctxt, *tctxt;
+ char **newl, *tmpch;
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR){
+ Tcl_SetResult(interp,
+ "cledit malformed: first arg must be int",
+ TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ for(sh_ctxt = ps_global->context_list, i = 0;
+ sh_ctxt && i < cl ; i++, sh_ctxt = sh_ctxt->next);
+ if(!sh_ctxt || !sh_ctxt->prev){
+ Tcl_SetResult(interp,
+ "invalid context list number",
+ TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ if(sh_ctxt->var.v == sh_ctxt->prev->var.v){
+ shn = sh_ctxt->var.i;
+ for(n = 0; sh_ctxt->var.v->current_val.l[n]; n++);
+ newl = (char **) fs_get((n + 1) * sizeof(char *));
+ newl[n] = NULL;
+ for(i = 0; sh_ctxt->var.v->current_val.l[i]; i++){
+ if(i == shn)
+ newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i-1]);
+ else if(i == shn - 1)
+ newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i+1]);
+ else
+ newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i]);
+ }
+ i = set_variable_list(sh_ctxt->var.v - ps_global->vars,
+ newl, TRUE, Main);
+ free_list_array(&newl);
+ set_current_val(sh_ctxt->var.v, TRUE, FALSE);
+ if(i){
+ Tcl_SetResult(interp,
+ "Error saving changes",
+ TCL_VOLATILE);
+ return TCL_OK;
+ }
+ psh_ctxt = sh_ctxt->prev;
+ psh_ctxt->var.i++;
+ sh_ctxt->var.i--;
+ }
+ else{
+ psh_ctxt = sh_ctxt->prev;
+ shn = sh_ctxt->var.i;
+ tmpch = cpystr(sh_ctxt->var.v->current_val.l[shn]);
+ for(n = 0; sh_ctxt->var.v->current_val.l[n]; n++);
+ n--;
+ newl = (char **) fs_get((n + 1) * sizeof(char *));
+ newl[n] = NULL;
+ for(i = 1; sh_ctxt->var.v->current_val.l[i]; i++)
+ newl[i-1] = cpystr(sh_ctxt->var.v->current_val.l[i]);
+ i = set_variable_list(sh_ctxt->var.v - ps_global->vars,
+ newl, FALSE, Main);
+ free_list_array(&newl);
+ if(i){
+ Tcl_SetResult(interp,
+ "Error saving changes",
+ TCL_VOLATILE);
+ return TCL_OK;
+ }
+ set_current_val(sh_ctxt->var.v, TRUE, FALSE);
+ for(n = 0; psh_ctxt->var.v->current_val.l[n]; n++);
+ n++;
+ newl = (char **) fs_get((n + 1) * sizeof(char *));
+ newl[n] = NULL;
+ for(i = 0; psh_ctxt->var.v->current_val.l[i+1]; i++)
+ newl[i] = cpystr(psh_ctxt->var.v->current_val.l[i]);
+ newl[i++] = tmpch;
+ newl[i] = cpystr(psh_ctxt->var.v->current_val.l[i-1]);
+ i = set_variable_list(psh_ctxt->var.v - ps_global->vars,
+ newl, TRUE, Main);
+ free_list_array(&newl);
+ if(i){
+ Tcl_SetResult(interp,
+ "Error saving changes",
+ TCL_VOLATILE);
+ return TCL_OK;
+ }
+ set_current_val(psh_ctxt->var.v, TRUE, FALSE);
+ for(tctxt = sh_ctxt->next ; tctxt; tctxt = tctxt->next)
+ tctxt->var.i--;
+ sh_ctxt->var.v = psh_ctxt->var.v;
+ sh_ctxt->var.i = n - 2;
+ /* There MUST be at least 2 collections in the list */
+ psh_ctxt->var.i++;
+ }
+ if(sh_ctxt->next) sh_ctxt->next->prev = psh_ctxt;
+ psh_ctxt->next = sh_ctxt->next;
+ sh_ctxt->prev = psh_ctxt->prev;
+ psh_ctxt->prev = sh_ctxt;
+ sh_ctxt->next = psh_ctxt;
+ if(sh_ctxt->prev) sh_ctxt->prev->next = sh_ctxt;
+ if(ps_global->context_list == psh_ctxt)
+ ps_global->context_list = sh_ctxt;
+ init_inbox_mapping(ps_global->VAR_INBOX_PATH,
+ ps_global->context_list);
+ return TCL_OK;
+ }
+ }
+ else if(objc == 7){
+ if(!strcmp(s1, "edit") || !strcmp(s1, "add")){
+ int cl, quotes_needed = 0, i, add = 0, n = 0;
+ char *nick, *server, *path, *view,
+ context_buf[MAILTMPLEN*4], **newl;
+ CONTEXT_S *new_ctxt, *tmp_ctxt;
+
+ if(!strcmp(s1, "add")) add = 1;
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR){
+ Tcl_SetResult(interp,
+ "cledit malformed: first arg must be int",
+ TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ if(!(nick = Tcl_GetStringFromObj(objv[3], NULL))){
+ Tcl_SetResult(interp,
+ "Error1",
+ TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ if(!(server = Tcl_GetStringFromObj(objv[4], NULL))){
+ Tcl_SetResult(interp,
+ "Error2",
+ TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ if(!(path = Tcl_GetStringFromObj(objv[5], NULL))){
+ Tcl_SetResult(interp,
+ "Error3",
+ TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ if(!(view = Tcl_GetStringFromObj(objv[6], NULL))){
+ Tcl_SetResult(interp,
+ "Error4",
+ TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ removing_leading_and_trailing_white_space(nick);
+ removing_leading_and_trailing_white_space(server);
+ removing_leading_and_trailing_white_space(path);
+ removing_leading_and_trailing_white_space(view);
+ if(strchr(nick, ' '))
+ quotes_needed = 1;
+ if(strlen(nick)+strlen(server)+strlen(path)+strlen(view) >
+ MAILTMPLEN * 4 - 20) { /* for good measure */
+ Tcl_SetResult(interp,
+ "info too long",
+ TCL_VOLATILE);
+
+ return TCL_ERROR;
+ }
+ if(3 + strlen(nick) + strlen(server) + strlen(path) +
+ strlen(view) > MAILTMPLEN + 4){
+ Tcl_SetResult(interp,
+ "collection fields too long",
+ TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ snprintf(context_buf, sizeof(context_buf), "%s%s%s%s%s%s[%s]", quotes_needed ?
+ "\"" : "", nick, quotes_needed ? "\"" : "",
+ strlen(nick) ? " " : "",
+ server, path, view);
+ new_ctxt = new_context(context_buf, NULL);
+ if(!add){
+ for(tmp_ctxt = ps_global->context_list, i = 0;
+ tmp_ctxt && i < cl; i++, tmp_ctxt = tmp_ctxt->next);
+ if(!tmp_ctxt){
+ Tcl_SetResult(interp,
+ "invalid context list number",
+ TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ new_ctxt->next = tmp_ctxt->next;
+ new_ctxt->prev = tmp_ctxt->prev;
+ if(tmp_ctxt->prev && tmp_ctxt->prev->next == tmp_ctxt)
+ tmp_ctxt->prev->next = new_ctxt;
+ if(tmp_ctxt->next && tmp_ctxt->next->prev == tmp_ctxt)
+ tmp_ctxt->next->prev = new_ctxt;
+ if(ps_global->context_list == tmp_ctxt)
+ ps_global->context_list = new_ctxt;
+ if(ps_global->context_current == tmp_ctxt){
+ strncpy(ps_global->cur_folder,
+ ps_global->mail_stream->mailbox,
+ sizeof(ps_global->cur_folder));
+ ps_global->cur_folder[sizeof(ps_global->cur_folder)-1] = '\0';
+ ps_global->context_current = new_ctxt;
+ }
+ if(ps_global->context_last == tmp_ctxt)
+ ps_global->context_last = new_ctxt;
+ new_ctxt->var = tmp_ctxt->var;
+ tmp_ctxt->next = tmp_ctxt->prev = NULL;
+ free_context(&tmp_ctxt);
+ }
+ else {
+ for(tmp_ctxt = ps_global->context_list;
+ tmp_ctxt->next; tmp_ctxt = tmp_ctxt->next);
+ new_ctxt->prev = tmp_ctxt;
+ tmp_ctxt->next = new_ctxt;
+ new_ctxt->var.v = tmp_ctxt->var.v;
+ new_ctxt->var.i = tmp_ctxt->var.i + 1;
+ }
+ if(!new_ctxt->var.v){
+ Tcl_SetResult(interp,
+ "Error5",
+ TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ for(n = 0; new_ctxt->var.v->current_val.l[n]; n++);
+ if(add) n++;
+ newl = (char **) fs_get((n + 1) * sizeof(char *));
+ newl[n] = NULL;
+ for(n = 0; new_ctxt->var.v->current_val.l[n]; n++)
+ newl[n] = (n == new_ctxt->var.i)
+ ? cpystr(context_buf)
+ : cpystr(new_ctxt->var.v->current_val.l[n]);
+ if(add) newl[n++] = cpystr(context_buf);
+ n = set_variable_list(new_ctxt->var.v - ps_global->vars,
+ newl, TRUE, Main);
+ free_list_array(&newl);
+ set_current_val(new_ctxt->var.v, TRUE, FALSE);
+ init_inbox_mapping(ps_global->VAR_INBOX_PATH,
+ ps_global->context_list);
+ if(n){
+ Tcl_SetResult(interp,
+ "Error saving changes",
+ TCL_VOLATILE);
+ return TCL_OK;
+ }
+ return TCL_OK;
+
+ }
+ }
+ }
+ }
+ Tcl_SetResult(interp, err, TCL_STATIC);
+ return(TCL_ERROR);
+}
+
+
+/*
+ * peTakeaddr - Take Address
+ */
+int
+peTakeaddr(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ TA_S *talist = NULL, *current, *head;
+ Tcl_Obj *itemObj, *secObj = NULL, *resObj = NULL;
+ int anum = 0;
+
+ mn_set_cur(sp_msgmap(ps_global->mail_stream), peMessageNumber(uid));
+
+ if(set_up_takeaddr('a', ps_global, sp_msgmap(ps_global->mail_stream),
+ &talist, &anum, TA_NOPROMPT, NULL) < 0
+ || (talist == NULL)){
+ Tcl_SetResult(interp,
+ "Take address failed to set up",
+ TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ for(head = talist ; head->prev; head = head->prev);
+ /*
+ * Return value will be of the form:
+ * {
+ * { "line to print",
+ * {"personal", "mailbox", "host"} # addr
+ * {"nick", "fullname", "fcc", "comment"} # suggested
+ * }
+ * ...
+ * }
+ *
+ * The two list items will be empty if that line is
+ * just informational.
+ */
+ itemObj = Tcl_NewListObj(0, NULL);
+ for(current = head; current ; current = current->next){
+ if(current->skip_it && !current->print) continue;
+ secObj = Tcl_NewListObj(0, NULL);
+ if(Tcl_ListObjAppendElement(interp, secObj,
+ Tcl_NewStringObj(current->strvalue,-1)) != TCL_OK)
+ return(TCL_ERROR);
+ resObj = Tcl_NewListObj(0, NULL);
+ /* append the address information */
+ if(current->addr && !current->print){
+ if(Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(current->addr->personal
+ ? current->addr->personal
+ : "", -1)) != TCL_OK)
+ return(TCL_ERROR);
+ if(Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(current->addr->mailbox
+ ? current->addr->mailbox
+ : "", -1)) != TCL_OK)
+ return(TCL_ERROR);
+ if(Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(current->addr->host
+ ? current->addr->host
+ : "", -1)) != TCL_OK)
+ return(TCL_ERROR);
+ }
+ if(Tcl_ListObjAppendElement(interp, secObj,
+ resObj) != TCL_OK)
+ return(TCL_ERROR);
+ resObj = Tcl_NewListObj(0, NULL);
+ /* append the suggested possible entries */
+ if(!current->print
+ && (current->nickname || current->fullname
+ || current->fcc || current->comment)){
+ if(Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(current->nickname
+ ? current->nickname
+ : "", -1)) != TCL_OK)
+ return(TCL_ERROR);
+ if(Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(current->fullname
+ ? current->fullname
+ : "", -1)) != TCL_OK)
+ return(TCL_ERROR);
+ if(Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(current->fcc
+ ? current->fcc
+ : "", -1)) != TCL_OK)
+ return(TCL_ERROR);
+ if(Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(current->comment
+ ? current->comment
+ : "", -1)) != TCL_OK)
+ return(TCL_ERROR);
+ }
+ if(Tcl_ListObjAppendElement(interp, secObj, resObj) != TCL_OK)
+ return(TCL_ERROR);
+ if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ secObj) != TCL_OK)
+ return(TCL_ERROR);
+ }
+
+ free_talines(&talist);
+ return(TCL_OK);
+}
+
+
+/*
+ * peTakeFrom - Take only From Address
+ */
+int
+peTakeFrom(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ char *err = NULL;
+ Tcl_Obj *objItem;
+ ADDRESS *ap;
+ ENVELOPE *env;
+ long rawno;
+
+ /*
+ * Return value will be of the form:
+ * {
+ * { "line to print",
+ * {"personal", "mailbox", "host"} # addr
+ * {"nick", "fullname", "fcc", "comment"} # suggested
+ * }
+ * ...
+ * }
+ *
+ * The two list items will be empty if that line is
+ * just informational.
+ */
+
+ if(uid){
+ if((env = pine_mail_fetchstructure(ps_global->mail_stream,
+ rawno = peSequenceNumber(uid),
+ NULL))){
+ /* append the address information */
+ for(ap = env->from; ap; ap = ap->next){
+ objItem = Tcl_NewListObj(0, NULL);
+ /* append EMPTY "line to print" */
+ if(Tcl_ListObjAppendElement(interp, objItem, Tcl_NewStringObj("",-1)) != TCL_OK)
+ return(TCL_ERROR);
+
+ /* append address info */
+ peAppListF(interp, objItem, "%s%s%s",
+ ap->personal ? (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf, SIZEOF_20KBUF, ap->personal) : "",
+ ap->mailbox ? ap->mailbox : "",
+ ap->host ? ap->host : "");
+
+ /* append suggested info */
+ peAddSuggestedContactInfo(interp, objItem, ap);
+
+ if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objItem) != TCL_OK)
+ return(TCL_ERROR);
+ }
+
+ return(TCL_OK);
+ }
+ else
+ err = ps_global->last_error;
+ }
+ else
+ err = "Invalid UID";
+
+ return(TCL_ERROR);
+}
+
+
+int
+peAddSuggestedContactInfo(Tcl_Interp *interp, Tcl_Obj *lobjp, ADDRESS *addr)
+{
+ char *nick = NULL, *full = NULL, *fcc = NULL, *comment = NULL;
+
+ get_contactinfo_from_addr(addr, &nick, &full, &fcc, &comment);
+
+ peAppListF(interp, lobjp, "%s%s%s%s",
+ nick ? nick : "",
+ full ? full : "",
+ fcc ? fcc : "",
+ comment ? comment : "");
+
+ if(nick)
+ fs_give((void **) &nick);
+
+ if(full)
+ fs_give((void **) &full);
+
+ if(fcc)
+ fs_give((void **) &fcc);
+
+ if(comment)
+ fs_give((void **) &comment);
+}
+
+
+/* * * * * * * * * Status message ring management * * * * * * * * * * * * */
+
+STATMSG_S *
+sml_newmsg(int priority, char *text)
+{
+ static long id = 1;
+ STATMSG_S *smp;
+
+ smp = (STATMSG_S *) fs_get(sizeof(STATMSG_S));
+ memset(smp, 0, sizeof(STATMSG_S));
+ smp->id = id++;
+ smp->posted = time(0);
+ smp->type = priority;
+ smp->text = cpystr(text);
+ return(smp);
+}
+
+
+void
+sml_addmsg(int priority, char *text)
+{
+ STATMSG_S *smp = sml_newmsg(priority, text);
+
+ if(peStatList){
+ smp->next = peStatList;
+ peStatList = smp;
+ }
+ else
+ peStatList = smp;
+}
+
+
+char **
+sml_getmsgs(void)
+{
+ int n;
+ STATMSG_S *smp;
+ char **retstrs = NULL, **tmpstrs;
+
+ for(n = 0, smp = peStatList; smp && !smp->seen; n++, smp = smp->next)
+ ;
+
+ if(n == 0) return NULL;
+ retstrs = (char **)fs_get((n+1)*sizeof(char *));
+ for(tmpstrs = retstrs, smp = peStatList; smp && !smp->seen; smp = smp->next){
+ *tmpstrs = smp->text;
+ tmpstrs++;
+ }
+
+ *tmpstrs = NULL;
+ return(retstrs);
+}
+
+
+char *
+sml_getmsg(void)
+{
+ return(peStatList ? peStatList->text : "");
+}
+
+void
+sml_seen(void)
+{
+ STATMSG_S *smp;
+
+ for(smp = peStatList; smp; smp = smp->next)
+ smp->seen = 1;
+}
+
+
+
+/* * * * * * * * * LDAP Support Routines * * * * * * * * * * * */
+
+
+/*
+ * PELdapCmd - LDAP TCL interface
+ */
+int
+PELdapCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+#ifndef ENABLE_LDAP
+ char *err = "Call to PELdap when LDAP not enabled";
+#else
+ char *err = "Unknown PELdap request";
+ char *s1;
+
+ dprint((2, "PELdapCmd"));
+
+ if(objc == 1){
+ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
+ Tcl_SetResult(interp, err, TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ s1 = Tcl_GetStringFromObj(objv[1], NULL);
+
+ if(s1){
+ int qn;
+ if(!strcmp(s1, "directories")){
+ int i;
+ LDAP_SERV_S *info;
+ Tcl_Obj *secObj;
+
+ if(objc != 2){
+ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
+ Tcl_SetResult(interp, err, TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ if(ps_global->VAR_LDAP_SERVERS){
+ for(i = 0; ps_global->VAR_LDAP_SERVERS[i] &&
+ ps_global->VAR_LDAP_SERVERS[i][0]; i++){
+ info = break_up_ldap_server(ps_global->VAR_LDAP_SERVERS[i]);
+ secObj = Tcl_NewListObj(0, NULL);
+ if(Tcl_ListObjAppendElement(interp, secObj,
+ Tcl_NewStringObj(info->nick ? info->nick
+ : "", -1)) != TCL_OK)
+ return(TCL_ERROR);
+ if(Tcl_ListObjAppendElement(interp, secObj,
+ Tcl_NewStringObj(info->serv ? info->serv
+ : "", -1)) != TCL_OK)
+ return(TCL_ERROR);
+
+ if(info)
+ free_ldap_server_info(&info);
+ if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ secObj) != TCL_OK)
+ return(TCL_ERROR);
+ }
+ }
+ else
+ Tcl_SetResult(interp, "", TCL_STATIC);
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "query")){
+ int dir;
+ char *srchstr, *filtstr;
+ LDAP_CHOOSE_S *winning_e = NULL;
+ LDAP_SERV_RES_S *results = NULL;
+ WP_ERR_S wp_err;
+ CUSTOM_FILT_S *filter = NULL;
+
+ if(objc != 5){
+ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
+ Tcl_SetResult(interp, err, TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ if(Tcl_GetIntFromObj(interp, objv[2], &dir) == TCL_ERROR){
+ Tcl_SetResult(interp,
+ "PELdap results malformed: first arg must be int",
+ TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ wpldap_global->query_no++;
+ if(wpldap_global->ldap_search_list){
+ wpldap_global->ldap_search_list =
+ free_wpldapres(wpldap_global->ldap_search_list);
+ }
+ srchstr = Tcl_GetStringFromObj(objv[3], NULL);
+ filtstr = Tcl_GetStringFromObj(objv[4], NULL);
+ if(!srchstr) return(TCL_ERROR);
+ if(!filtstr) return(TCL_ERROR);
+ if(*filtstr){
+ filter = (CUSTOM_FILT_S *)fs_get(sizeof(CUSTOM_FILT_S));
+ filter->filt = cpystr(filtstr);
+ filter->combine = 0;
+ }
+ memset(&wp_err, 0, sizeof(wp_err));
+ ldap_lookup_all(srchstr, dir, 0, AlwaysDisplay, filter, &winning_e,
+ &wp_err, &results);
+ if(filter){
+ fs_give((void **)&filter->filt);
+ fs_give((void **)&filter);
+ }
+ Tcl_SetResult(interp, int2string(wpldap_global->ldap_search_list
+ ? wpldap_global->query_no : 0),
+ TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ /*
+ * First argument has always got to be the query number for now.
+ * Might need to rething that when setting up queries.
+ */
+ if(objc == 2){
+ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
+ Tcl_SetResult(interp, err, TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ if(Tcl_GetIntFromObj(interp, objv[2], &qn) == TCL_ERROR){
+ Tcl_SetResult(interp,
+ "PELdap results malformed: first arg must be int",
+ TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ if(qn != wpldap_global->query_no){
+ Tcl_SetResult(interp,
+ "Query is no longer valid", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ if(objc == 3){
+ if(!strcmp(s1, "results")){
+ return(peLdapQueryResults(interp));
+ }
+ }
+ else if(objc == 4){
+ if(!strcmp(s1, "ldapext")){
+ /*
+ * Returns a list of the form:
+ * {"dn" {{attrib {val, ...}}, ...}}
+ */
+ char *whichrec = Tcl_GetStringFromObj(objv[3], NULL);
+ char *tmpstr, *tmp, *tmp2, **vals, *a;
+ WPLDAPRES_S *curres;
+ LDAP_CHOOSE_S *winning_e = NULL;
+ LDAP_SERV_RES_S *trl;
+ Tcl_Obj *secObj = NULL, *resObj = NULL, *itemObj;
+ BerElement *ber;
+ LDAPMessage *e;
+ int i, j, whichi, whichj;
+
+ if(whichrec == NULL){
+ Tcl_SetResult(interp, "Ldap ldapext error 1", TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ tmpstr = cpystr(whichrec);
+ tmp = tmpstr;
+ for(tmp2 = tmp; *tmp2 >= '0' && *tmp2 <= '9'; tmp2++);
+ if(*tmp2 != '.'){
+ Tcl_SetResult(interp, "Ldap ldapext error 2", TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ *tmp2 = '\0';
+ whichi = atoi(tmp);
+ *tmp2 = '.';
+ tmp2++;
+ for(tmp = tmp2; *tmp2 >= '0' && *tmp2 <= '9'; tmp2++);
+ if(*tmp2 != '\0'){
+ Tcl_SetResult(interp, "Ldap ldapext error 3", TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ whichj = atoi(tmp);
+ fs_give((void **)&tmpstr);
+ for(curres = wpldap_global->ldap_search_list, i = 0;
+ i < whichi && curres; i++, curres = curres->next);
+ if(!curres){
+ Tcl_SetResult(interp, "Ldap ldapext error 4", TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ for(trl = curres->reslist, j = 0; trl; trl = trl->next){
+ for(e = ldap_first_entry(trl->ld, trl->res);
+ e != NULL && j < whichj;
+ e = ldap_next_entry(trl->ld, e), j++);
+ if(e != NULL && j == whichj)
+ break;
+ }
+ if(e == NULL || trl == NULL){
+ Tcl_SetResult(interp, "Ldap ldapext error 5", TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ winning_e = (LDAP_CHOOSE_S *)fs_get(sizeof(LDAP_CHOOSE_S));
+ winning_e->ld = trl->ld;
+ winning_e->selected_entry = e;
+ winning_e->info_used = trl->info_used;
+ winning_e->serv = trl->serv;
+ a = ldap_get_dn(winning_e->ld, winning_e->selected_entry);
+ if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(a ? a : "", -1)) != TCL_OK)
+ return(TCL_ERROR);
+ if(a)
+ our_ldap_dn_memfree(a);
+
+ itemObj = Tcl_NewListObj(0, NULL);
+ for(a = ldap_first_attribute(winning_e->ld, winning_e->selected_entry, &ber);
+ a != NULL;
+ a = ldap_next_attribute(winning_e->ld, winning_e->selected_entry, ber)){
+ if(a && *a){
+ secObj = Tcl_NewListObj(0, NULL);
+ if(Tcl_ListObjAppendElement(interp, secObj,
+ Tcl_NewStringObj(ldap_translate(a,
+ winning_e->info_used), -1)) != TCL_OK)
+ return(TCL_ERROR);
+ resObj = Tcl_NewListObj(0, NULL);
+ vals = ldap_get_values(winning_e->ld, winning_e->selected_entry, a);
+ if(vals){
+ for(i = 0; vals[i]; i++){
+ if(Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(vals[i], -1)) != TCL_OK)
+ return(TCL_ERROR);
+ }
+ ldap_value_free(vals);
+ if(Tcl_ListObjAppendElement(interp, secObj, resObj) != TCL_OK)
+ return(TCL_ERROR);
+ }
+ if(!strcmp(a,"objectclass")){
+ if(Tcl_ListObjAppendElement(interp, secObj,
+ Tcl_NewStringObj("objectclass", -1)) != TCL_OK)
+ return(TCL_ERROR);
+ }
+ if(Tcl_ListObjAppendElement(interp, itemObj, secObj) != TCL_OK)
+ return(TCL_ERROR);
+ }
+ our_ldap_memfree(a);
+ }
+
+ if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ itemObj) != TCL_OK)
+ return(TCL_ERROR);
+
+ fs_give((void **)&winning_e);
+ return(TCL_OK);
+ }
+ }
+ else if(objc == 6){
+ if(!strcmp(s1, "setaddrs")){
+ char *listset = Tcl_GetStringFromObj(objv[3], NULL);
+ char *addrstr = Tcl_GetStringFromObj(objv[4], NULL);
+ char *tmp, *tmp2, *tmplistset, was_char, *ret_to,
+ *tmpaddrstr;
+ int **lset, noreplace = 0;
+ ADDRESS *adr = NULL, *curadr, *prevadr, *newadr,
+ *curnewadr, *newadrs;
+ int curi, i, j, numsrchs, numset, setit;
+ LDAP_CHOOSE_S *tres;
+ LDAP_SERV_RES_S *trl;
+ WPLDAPRES_S *curres;
+ LDAPMessage *e;
+ RFC822BUFFER rbuf;
+ size_t len;
+
+ if(Tcl_GetIntFromObj(interp, objv[5], &noreplace) == TCL_ERROR){
+ Tcl_SetResult(interp,
+ "PELdap results malformed: first arg must be int",
+ TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ if(listset == NULL || addrstr == NULL) return TCL_ERROR;
+ tmpaddrstr = cpystr(addrstr);
+
+ if(!noreplace){
+ mail_parameters(NIL, SET_PARSEPHRASE, (void *)massage_phrase_addr);
+ rfc822_parse_adrlist(&adr, tmpaddrstr, "@");
+ mail_parameters(NIL, SET_PARSEPHRASE, NULL);
+ }
+
+ tmplistset = cpystr(listset);
+ for(curres = wpldap_global->ldap_search_list, numsrchs = 0;
+ curres; curres = curres->next, numsrchs++);
+ lset = (int **)fs_get((numsrchs+1)*sizeof(int *));
+ for(i = 0; i < numsrchs; i++){
+ for(tmp = tmplistset, numset = 0; *tmp;){
+ for(tmp2 = tmp; *tmp2 >= '0' && *tmp2 <= '9'; tmp2++);
+ if(*tmp2 != '.'){
+ Tcl_SetResult(interp, "Ldap error 1", TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ if(atoi(tmp) == i) numset++;
+ tmp2++;
+ for(tmp = tmp2; *tmp2 >= '0' && *tmp2 <= '9'; tmp2++);
+ if(*tmp2 != ',' && *tmp2 != '\0'){
+ Tcl_SetResult(interp, "Ldap error 2", TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ if(*tmp2) tmp2++;
+ tmp = tmp2;
+ }
+ lset[i] = (int *)fs_get((numset+1)*sizeof(int));
+ for(tmp = tmplistset, j = 0; *tmp && j < numset;){
+ setit = 0;
+ for(tmp2 = tmp; *tmp2 >= '0' && *tmp2 <= '9'; tmp2++);
+ if(*tmp2 != '.'){
+ Tcl_SetResult(interp, "Ldap error 3", TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ *tmp2 = '\0';
+ if(atoi(tmp) == i) setit++;
+ *tmp2 = '.';
+ tmp2++;
+ for(tmp = tmp2; *tmp2 >= '0' && *tmp2 <= '9'; tmp2++);
+ if(*tmp2 != ',' && *tmp2 != '\0'){
+ Tcl_SetResult(interp, "Ldap error 4", TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ if(setit){
+ was_char = *tmp2;
+ *tmp2 = '\0';
+ lset[i][j++] = atoi(tmp);
+ *tmp2 = was_char;
+ }
+ if(*tmp2) tmp2++;
+ tmp = tmp2;
+ }
+ lset[i][j] = -1;
+ }
+ lset[i] = NULL;
+ for(i = 0, curres = wpldap_global->ldap_search_list;
+ i < numsrchs && curres; i++, curres = curres->next){
+ prevadr = NULL;
+ for(curadr = adr; curadr; curadr = curadr->next){
+ if(strcmp(curadr->mailbox, curres->str) == 0
+ && curadr->host && *curadr->host == '@')
+ break;
+ prevadr = curadr;
+ }
+ if(!curadr && !noreplace){
+ Tcl_SetResult(interp, "Ldap error 5", TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ newadrs = newadr = curnewadr = NULL;
+ for(trl = curres->reslist, j = 0, curi = 0; trl; trl = trl->next){
+ for(e = ldap_first_entry(trl->ld, trl->res);
+ e != NULL && lset[i][curi] != -1;
+ e = ldap_next_entry(trl->ld, e), j++){
+ if(j == lset[i][curi]){
+ tres = (LDAP_CHOOSE_S *)fs_get(sizeof(LDAP_CHOOSE_S));
+ tres->ld = trl->ld;
+ tres->selected_entry = e;
+ tres->info_used = trl->info_used;
+ tres->serv = trl->serv;
+ newadr = address_from_ldap(tres);
+ fs_give((void **)&tres);
+
+ if(newadrs == NULL){
+ newadrs = curnewadr = newadr;
+ }
+ else {
+ curnewadr->next = newadr;
+ curnewadr = newadr;
+ }
+ curi++;
+ }
+ }
+ }
+ if(newadrs == NULL || curnewadr == NULL){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "No Result Selected for \"%s\"", curadr->mailbox ? curadr->mailbox : "noname");
+ q_status_message(SM_ORDER, 0, 3, tmp_20k_buf);
+ newadr = copyaddr(curadr);
+ if(newadrs == NULL){
+ newadrs = curnewadr = newadr;
+ }
+ else {
+ curnewadr->next = newadr;
+ curnewadr = newadr;
+ }
+ }
+ curnewadr->next = curadr ? curadr->next : NULL;
+ if(curadr) curadr->next = NULL;
+ if(curadr == adr)
+ adr = newadrs;
+ else{
+ prevadr->next = newadrs;
+ if(curadr)
+ mail_free_address(&curadr);
+ }
+ }
+
+ len = est_size(adr);
+ ret_to = (char *)fs_get(len * sizeof(char));
+ ret_to[0] = '\0';
+ strip_personal_quotes(adr);
+ rbuf.f = dummy_soutr;
+ rbuf.s = NULL;
+ rbuf.beg = ret_to;
+ rbuf.cur = ret_to;
+ rbuf.end = ret_to+len-1;
+ rfc822_output_address_list(&rbuf, adr, 0L, NULL);
+ *rbuf.cur = '\0';
+ Tcl_SetResult(interp, ret_to, TCL_VOLATILE);
+ fs_give((void **)&ret_to);
+ fs_give((void **)&tmpaddrstr);
+ fs_give((void **)&tmplistset);
+ for(i = 0; lset[i]; i++)
+ fs_give((void **)&lset[i]);
+ fs_give((void **)&lset);
+ if(adr)
+ mail_free_address(&adr);
+
+ return(TCL_OK);
+ }
+ }
+ }
+#endif /* ENABLE_LDAP */
+ Tcl_SetResult(interp, err, TCL_STATIC);
+ return(TCL_ERROR);
+}
+
+
+#ifdef ENABLE_LDAP
+int
+peLdapQueryResults(Tcl_Interp *interp)
+{
+ WPLDAPRES_S *tsl;
+ Tcl_Obj *secObj = NULL, *resObj = NULL, *itemObj;
+ LDAPMessage *e;
+ LDAP_SERV_RES_S *trl;
+ /* returned list will be of the form:
+ *
+ * {
+ * {search-string
+ * {name, {title, ...}, {unit, ...},
+ * {org, ...}, {email, ...}},
+ * ...
+ * },
+ * ...
+ * }
+ */
+
+ for(tsl = wpldap_global->ldap_search_list;
+ tsl; tsl = tsl->next){
+ secObj = Tcl_NewListObj(0, NULL);
+ if(Tcl_ListObjAppendElement(interp, secObj,
+ Tcl_NewStringObj(tsl->str ? tsl->str
+ : "", -1)) != TCL_OK)
+ return(TCL_ERROR);
+ resObj = Tcl_NewListObj(0, NULL);
+ for(trl = tsl->reslist; trl; trl = trl->next){
+ for(e = ldap_first_entry(trl->ld, trl->res);
+ e != NULL;
+ e = ldap_next_entry(trl->ld, e)){
+ char *dn;
+ char **cn, **org, **unit, **title, **mail, **sn;
+
+ dn = NULL;
+ cn = org = title = unit = mail = sn = NULL;
+
+ itemObj = Tcl_NewListObj(0, NULL);
+ peLdapEntryParse(trl, e, &cn, &org, &unit, &title,
+ &mail, &sn);
+ if(cn){
+ if(Tcl_ListObjAppendElement(interp, itemObj,
+ Tcl_NewStringObj(cn[0], -1)) != TCL_OK)
+ return(TCL_ERROR);
+ ldap_value_free(cn);
+ }
+ else if(sn){
+ if(Tcl_ListObjAppendElement(interp, itemObj,
+ Tcl_NewStringObj(sn[0], -1)) != TCL_OK)
+ return(TCL_ERROR);
+ ldap_value_free(sn);
+ }
+ else{
+ dn = ldap_get_dn(trl->ld, e);
+
+ if(dn && !dn[0]){
+ our_ldap_dn_memfree(dn);
+ dn = NULL;
+ }
+
+ if(Tcl_ListObjAppendElement(interp, itemObj,
+ Tcl_NewStringObj(dn ? dn : "", -1)) != TCL_OK)
+ return(TCL_ERROR);
+
+ if(dn)
+ our_ldap_dn_memfree(dn);
+ }
+ if(peLdapStrlist(interp, itemObj, title) == TCL_ERROR)
+ return(TCL_ERROR);
+ if(peLdapStrlist(interp, itemObj, unit) == TCL_ERROR)
+ return(TCL_ERROR);
+ if(peLdapStrlist(interp, itemObj, org) == TCL_ERROR)
+ return(TCL_ERROR);
+ if(peLdapStrlist(interp, itemObj, mail) == TCL_ERROR)
+ return(TCL_ERROR);
+ if(Tcl_ListObjAppendElement(interp, resObj, itemObj) != TCL_OK)
+ return(TCL_ERROR);
+ if(title)
+ ldap_value_free(title);
+ if(unit)
+ ldap_value_free(unit);
+ if(org)
+ ldap_value_free(org);
+ if(mail)
+ ldap_value_free(mail);
+ }
+ }
+ if(Tcl_ListObjAppendElement(interp, secObj, resObj) != TCL_OK)
+ return(TCL_ERROR);
+ if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ secObj) != TCL_OK)
+ return(TCL_ERROR);
+ }
+ return(TCL_OK);
+}
+
+int
+peLdapStrlist(Tcl_Interp *interp, Tcl_Obj *itemObj, char **strl)
+{
+ Tcl_Obj *strlObj;
+ int i;
+
+ strlObj = Tcl_NewListObj(0, NULL);
+ if(strl){
+ for(i = 0; strl[i] && strl[i][0]; i++){
+ if(Tcl_ListObjAppendElement(interp, strlObj,
+ Tcl_NewStringObj(strl[i], -1)) != TCL_OK)
+ return(TCL_ERROR);
+ }
+ }
+ if(Tcl_ListObjAppendElement(interp, itemObj, strlObj) != TCL_OK)
+ return(TCL_ERROR);
+ return(TCL_OK);
+}
+
+
+int
+init_ldap_pname(struct pine *ps)
+{
+ if(!ps_global->VAR_PERSONAL_NAME
+ || ps_global->VAR_PERSONAL_NAME[0] == '\0'){
+ char *pname;
+ struct variable *vtmp;
+
+ if(ps->maildomain && *ps->maildomain
+ && ps->VAR_USER_ID && *ps->VAR_USER_ID){
+ pname = peLdapPname(ps->VAR_USER_ID, ps->maildomain);
+ if(pname){
+ vtmp = &ps->vars[V_PERSONAL_NAME];
+ if((vtmp->fixed_val.p && vtmp->fixed_val.p[0] == '\0')
+ || (vtmp->is_fixed && !vtmp->fixed_val.p)){
+ if(vtmp->fixed_val.p)
+ fs_give((void **)&vtmp->fixed_val.p);
+ vtmp->fixed_val.p = cpystr(pname);
+ }
+ else {
+ if(vtmp->global_val.p)
+ fs_give((void **)&vtmp->global_val.p);
+ vtmp->global_val.p = cpystr(pname);
+ }
+ fs_give((void **)&pname);
+ set_current_val(vtmp, FALSE, FALSE);
+ }
+ }
+ }
+ return 0;
+}
+#endif /* ENABLE_LDAP */
+
+/*
+ * Note: this is taken straight out of pico/composer.c
+ *
+ * strqchr - returns pointer to first non-quote-enclosed occurance of ch in
+ * the given string. otherwise NULL.
+ * s -- the string
+ * ch -- the character we're looking for
+ * q -- q tells us if we start out inside quotes on entry and is set
+ * correctly on exit.
+ * m -- max characters we'll check for ch (set to -1 for no check)
+ */
+char *
+strqchr(char *s, int ch, int *q, int m)
+{
+ int quoted = (q) ? *q : 0;
+
+ for(; s && *s && m != 0; s++, m--){
+ if(*s == '"'){
+ quoted = !quoted;
+ if(q)
+ *q = quoted;
+ }
+
+ if(!quoted && *s == ch)
+ return(s);
+ }
+
+ return(NULL);
+}
+
+
+Tcl_Obj *
+wp_prune_folders(CONTEXT_S *ctxt,
+ char *fcc,
+ int cur_month,
+ char *type,
+ unsigned pr,
+ int *ok,
+ int moved_fldrs,
+ Tcl_Interp *interp)
+{
+ Tcl_Obj *resObj = NULL, *secObj = NULL;
+ char path2[MAXPATH+1], tmp[21];
+ int exists, month_to_use;
+ struct sm_folder *mail_list, *sm;
+
+ mail_list = get_mail_list(ctxt, fcc);
+
+ for(sm = mail_list; sm != NULL && sm->name != NULL; sm++)
+ if(sm->month_num == cur_month - 1)
+ break; /* matched a month */
+
+ month_to_use = (sm == NULL || sm->name == NULL) ? cur_month - 1 : 0;
+
+ if(!(month_to_use == 0 || pr == PRUNE_NO_AND_ASK || pr == PRUNE_NO_AND_NO)){
+ strncpy(path2, fcc, sizeof(path2)-1);
+ path2[sizeof(path2)-1] = '\0';
+ strncpy(tmp, month_abbrev((month_to_use % 12)+1), sizeof(tmp)-1);
+ tmp[sizeof(tmp)-1] = '\0';
+ lcase((unsigned char *) tmp);
+ snprintf(path2 + strlen(path2), sizeof(path2)-strlen(path2), "-%.20s-%d", tmp, month_to_use/12);
+
+ if((exists = folder_exists(ctxt, fcc)) == FEX_ERROR){
+ (*ok) = 0;
+ return(NULL);
+ }
+ else if(exists & FEX_ISFILE){
+ if(pr == PRUNE_YES_AND_ASK || (pr == PRUNE_YES_AND_NO && !moved_fldrs)){
+ prune_move_folder(fcc, path2, ctxt);
+ } else {
+ resObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(type, -1));
+ secObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, secObj, Tcl_NewStringObj(fcc, -1));
+ Tcl_ListObjAppendElement(interp, secObj, Tcl_NewStringObj(path2, -1));
+ Tcl_ListObjAppendElement(interp, resObj, secObj);
+ }
+ }
+ }
+ if(pr == PRUNE_ASK_AND_ASK || pr == PRUNE_YES_AND_ASK
+ || pr == PRUNE_NO_AND_ASK){
+ sm = mail_list;
+ if(!resObj && sm && sm->name && sm->name[0] != '\0'){
+ resObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(type, -1));
+ Tcl_ListObjAppendElement(interp, resObj, Tcl_NewListObj(0, NULL));
+ }
+ if(resObj)
+ secObj = Tcl_NewListObj(0, NULL);
+ for(sm = mail_list; sm != NULL && sm->name != NULL; sm++){
+ if(sm->name[0] == '\0') /* can't happen */
+ continue;
+ Tcl_ListObjAppendElement(interp, secObj, Tcl_NewStringObj(sm->name, -1));
+ }
+ if(resObj)
+ Tcl_ListObjAppendElement(interp, resObj, secObj);
+ } else if(resObj)
+ Tcl_ListObjAppendElement(interp, resObj, Tcl_NewListObj(0, NULL));
+
+ free_folder_list(ctxt);
+
+ if((sm = mail_list) != NULL){
+ while(sm->name){
+ fs_give((void **)&(sm->name));
+ sm++;
+ }
+
+ fs_give((void **)&mail_list);
+ }
+
+ return(resObj);
+}
+
+
+int
+hex_colorstr(char *hexcolor, char *str)
+{
+ char *tstr, *p, *p2, tbuf[256];
+ int i;
+
+ strcpy(hexcolor, "000000");
+ tstr = color_to_asciirgb(str);
+ p = tstr;
+ p2 = strindex(p, ',');
+ if(p2 == NULL) return 0;
+ strncpy(tbuf, p, min(50, p2-p));
+ i = atoi(tbuf);
+ sprintf(hexcolor, "%2.2x", i);
+ p = p2+1;
+ p2 = strindex(p, ',');
+ if(p2 == NULL) return 0;
+ strncpy(tbuf, p, min(50, p2-p));
+ i = atoi(tbuf);
+ sprintf(hexcolor+2, "%2.2x", i);
+ p = p2+1;
+ strncpy(tbuf, p, 50);
+ i = atoi(tbuf);
+ sprintf(hexcolor+4, "%2.2x", i);
+
+ return 0;
+}
+
+int
+hexval(char ch)
+{
+ if(ch >= '0' && ch <= '9')
+ return (ch - '0');
+ else if (ch >= 'A' && ch <= 'F')
+ return (10 + (ch - 'A'));
+ else if (ch >= 'a' && ch <= 'f')
+ return (10 + (ch - 'a'));
+ return -1;
+}
+
+int
+ascii_colorstr(char *acolor, char *hexcolor)
+{
+ int i, hv;
+
+ if(strlen(hexcolor) > 6) return 1;
+ /* red value */
+ if((hv = hexval(hexcolor[0])) == -1) return 1;
+ i = 16 * hv;
+ if((hv = hexval(hexcolor[1])) == -1) return 1;
+ i += hv;
+ sprintf(acolor, "%3.3d,", i);
+ /* green value */
+ if((hv = hexval(hexcolor[2])) == -1) return 1;
+ i = 16 * hv;
+ if((hv = hexval(hexcolor[3])) == -1) return 1;
+ i += hv;
+ sprintf(acolor+4, "%3.3d,", i);
+ /* blue value */
+ if((hv = hexval(hexcolor[4])) == -1) return 1;
+ i = 16 * hv;
+ if((hv = hexval(hexcolor[5])) == -1) return 1;
+ i += hv;
+ sprintf(acolor+8, "%3.3d", i);
+
+ return 0;
+}
+
+
+char *
+peRandomString(char *b, int l, int f)
+{
+ static char *kb = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+ char *s = b;
+ int j;
+ long n;
+
+ while(1){
+ n = random();
+ for(j = 0; j < ((sizeof(long) * 8) / 5); j++){
+ if(l-- <= 0){
+ *s = '\0';
+ return(b);
+ }
+
+ switch(f){
+ case PRS_LOWER_CASE :
+ *s++ = (char) tolower((unsigned char) kb[(n & 0x1F)]);
+ break;
+
+ case PRS_MIXED_CASE :
+ if(random() % 2){
+ *s++ = (char) tolower((unsigned char) kb[(n & 0x1F)]);
+ break;
+ }
+
+ default :
+ *s++ = kb[(n & 0x1F)];
+ break;
+ }
+
+ n = n >> 5;
+ }
+ }
+}
+
+
+long
+peAppendMsg(MAILSTREAM *stream, void *data, char **flags, char **date, STRING **message)
+{
+ char *t,*t1,tmp[MAILTMPLEN];
+ unsigned long u;
+ MESSAGECACHE *elt;
+ APPEND_PKG *ap = (APPEND_PKG *) data;
+ *flags = *date = NIL; /* assume no flags or date */
+ if (ap->flags) fs_give ((void **) &ap->flags);
+ if (ap->date) fs_give ((void **) &ap->date);
+ mail_gc (ap->stream,GC_TEXTS);
+ if (++ap->msgno <= ap->msgmax) {
+ /* initialize flag string */
+ memset (t = tmp,0,MAILTMPLEN);
+ /* output system flags */
+ if ((elt = mail_elt (ap->stream,ap->msgno))->seen) {strncat (t," \\Seen", sizeof(tmp)-(t-tmp)-1); tmp[sizeof(tmp)-1] = '\0';}
+ if (elt->deleted) {strncat (t," \\Deleted", sizeof(tmp)-(t-tmp)-1); tmp[sizeof(tmp)-1] = '\0';}
+ if (elt->flagged) {strncat (t," \\Flagged", sizeof(tmp)-(t-tmp)-1); tmp[sizeof(tmp)-1] = '\0';}
+ if (elt->answered) {strncat (t," \\Answered", sizeof(tmp)-(t-tmp)-1); tmp[sizeof(tmp)-1] = '\0';}
+ if (elt->draft) {strncat (t," \\Draft", sizeof(tmp)-(t-tmp)-1); tmp[sizeof(tmp)-1] = '\0';}
+ if ((u = elt->user_flags) != 0L) do /* any user flags? */
+ if ((MAILTMPLEN - ((t += strlen (t)) - tmp)) > (long)
+ (2 + strlen
+ (t1 = ap->stream->user_flags[find_rightmost_bit (&u)]))) {
+ if(t-tmp < sizeof(tmp))
+ *t++ = ' '; /* space delimiter */
+ strncpy (t,t1,sizeof(tmp)-(t-tmp)); /* copy the user flag */
+ }
+ while (u); /* until no more user flags */
+ tmp[sizeof(tmp)-1] = '\0';
+ *flags = ap->flags = cpystr (tmp + 1);
+ *date = ap->date = cpystr (mail_date (tmp,elt));
+ *message = ap->message; /* message stringstruct */
+ INIT (ap->message,mstring,(void *) ap,elt->rfc822_size);
+ }
+ else *message = NIL; /* all done */
+ return LONGT;
+}
+
+
+/* Initialize file string structure for file stringstruct
+* Accepts: string structure
+ * pointer to message data structure
+ * size of string
+ */
+
+void
+ms_init(STRING *s, void *data, unsigned long size)
+{
+ APPEND_PKG *md = (APPEND_PKG *) data;
+ s->data = data; /* note stream/msgno and header length */
+ mail_fetchheader_full (md->stream,md->msgno,NIL,&s->data1,FT_PREFETCHTEXT);
+ mail_fetchtext_full (md->stream,md->msgno,&s->size,NIL);
+ s->size += s->data1; /* header + body size */
+ SETPOS (s,0);
+}
+
+
+/* Get next character from file stringstruct
+ * Accepts: string structure
+ * Returns: character, string structure chunk refreshed
+ */
+char
+ms_next(STRING *s)
+{
+ char c = *s->curpos++; /* get next byte */
+ SETPOS (s,GETPOS (s)); /* move to next chunk */
+ return c; /* return the byte */
+}
+
+
+/* Set string pointer position for file stringstruct
+ * Accepts: string structure
+ * new position
+ */
+void
+ms_setpos(STRING *s, unsigned long i)
+{
+ APPEND_PKG *md = (APPEND_PKG *) s->data;
+ if (i < s->data1) { /* want header? */
+ s->chunk = mail_fetchheader (md->stream,md->msgno);
+ s->chunksize = s->data1; /* header length */
+ s->offset = 0; /* offset is start of message */
+ }
+ else if (i < s->size) { /* want body */
+ s->chunk = mail_fetchtext (md->stream,md->msgno);
+ s->chunksize = s->size - s->data1;
+ s->offset = s->data1; /* offset is end of header */
+ }
+ else { /* off end of message */
+ s->chunk = NIL; /* make sure that we crack on this then */
+ s->chunksize = 1; /* make sure SNX cracks the right way... */
+ s->offset = i;
+ }
+ /* initial position and size */
+ s->curpos = s->chunk + (i -= s->offset);
+ s->cursize = s->chunksize - i;
+}
+
+
+int
+remote_pinerc_failure(void)
+{
+ snprintf(ps_global->last_error, sizeof(ps_global->last_error), "%s",
+ ps_global->c_client_error[0]
+ ? ps_global->c_client_error
+ : _("Unable to read remote configuration"));
+
+ return(TRUE);
+}
+
+char *
+peWebAlpinePrefix(void)
+{
+ return("Web ");
+}
+
+
+void peNewMailAnnounce(MAILSTREAM *stream, long n, long t_nm_count){
+ char subject[MAILTMPLEN+1], subjtext[MAILTMPLEN+1], from[MAILTMPLEN+1],
+ *folder = NULL, intro[MAILTMPLEN+1];
+ long number;
+ ENVELOPE *e = NULL;
+ Tcl_Obj *resObj;
+
+ if(n && (resObj = Tcl_NewListObj(0, NULL)) != NULL){
+
+ Tcl_ListObjAppendElement(peED.interp, resObj, Tcl_NewLongObj(number = sp_mail_since_cmd(stream)));
+ Tcl_ListObjAppendElement(peED.interp, resObj, Tcl_NewLongObj(mail_uid(stream, n)));
+
+ if(stream){
+ e = pine_mail_fetchstructure(stream, n, NULL);
+
+ if(sp_flagged(stream, SP_INBOX))
+ folder = NULL;
+ else{
+ folder = STREAMNAME(stream);
+ if(folder[0] == '?' && folder[1] == '\0')
+ folder = NULL;
+ }
+ }
+
+ format_new_mail_msg(folder, number, e, intro, from, subject, subjtext, sizeof(intro));
+
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,
+ "%s%s%s%.80s%.80s", intro,
+ from ? ((number > 1L) ? " Most recent f" : " F") : "",
+ from ? "rom " : "",
+ from ? from : "",
+ subjtext);
+
+ Tcl_ListObjAppendElement(peED.interp, resObj, Tcl_NewStringObj(tmp_20k_buf,-1));
+
+ Tcl_ListObjAppendElement(peED.interp, Tcl_GetObjResult(peED.interp), resObj);
+ }
+}
+
+
+/* * * * * * * * * RSS 2.0 Support Routines * * * * * * * * * * * */
+
+/*
+ * PERssCmd - RSS TCL interface
+ */
+int
+PERssCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+ char *s1;
+
+ dprint((2, "PERssCmd"));
+
+ if(objc == 1){
+ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
+ return(TCL_ERROR);
+ }
+ s1 = Tcl_GetStringFromObj(objv[1], NULL);
+
+ if(s1){
+ if(!strcmp(s1, "news")){
+ return(peRssReturnFeed(interp, "news", ps_global->VAR_RSS_NEWS));
+ }
+ else if(!strcmp(s1, "weather")){
+ return(peRssReturnFeed(interp, "weather", ps_global->VAR_RSS_WEATHER));
+ }
+ }
+
+ Tcl_SetResult(interp, "Unknown PERss command", TCL_STATIC);
+ return(TCL_ERROR);
+}
+
+/*
+ * peRssReturnFeed - fetch feed contents and package Tcl response
+ */
+int
+peRssReturnFeed(Tcl_Interp *interp, char *type, char *link)
+{
+ RSS_FEED_S *feed;
+ char *errstr = "UNKNOWN";
+
+ if(link){
+ ps_global->c_client_error[0] = '\0';
+
+ if((feed = peRssFeed(interp, type, link)) != NULL)
+ return(peRssPackageFeed(interp, feed));
+
+ if(ps_global->mm_log_error)
+ errstr = ps_global->c_client_error;
+ }
+ else
+ errstr = "missing setting";
+
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s feed fail: %s", type, errstr);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+}
+
+/*
+ * peRssPackageFeed - build a list of feed item elements
+ *
+ * LIST ORDER: {title} {link} {description} {image}
+ */
+int
+peRssPackageFeed(Tcl_Interp *interp, RSS_FEED_S *feed)
+{
+ RSS_ITEM_S *item;
+
+ for(item = feed->items; item; item = item->next)
+ if(peAppListF(interp, Tcl_GetObjResult(interp), "%s %s %s %s",
+ (item->title && *item->title)? item->title : "Feed Provided No Title",
+ item->link ? item->link : "",
+ item->description ? item->description : "",
+ feed->image ? feed->image : "") != TCL_OK)
+ return(TCL_ERROR);
+
+ return(TCL_OK);
+}
+
+
+/*
+ * peRssFeed - return cached feed struct or fetch a new one
+ */
+RSS_FEED_S *
+peRssFeed(Tcl_Interp *interp, char *type, char *link)
+{
+ int i, cache_l, cp_ref;
+ time_t now = time(0);
+ RSS_FEED_S *feed = NULL;
+ RSS_CACHE_S *cache, *cp;
+ static RSS_CACHE_S news_cache[RSS_NEWS_CACHE_SIZE], weather_cache[RSS_WEATHER_CACHE_SIZE];
+
+ if(!strucmp(type,"news")){
+ cache = &news_cache[0];
+ cache_l = RSS_NEWS_CACHE_SIZE;
+ }
+ else{
+ cache = &weather_cache[0];
+ cache_l = RSS_WEATHER_CACHE_SIZE;
+ }
+
+ /* search/purge cache */
+ for(i = 0; i < cache_l; i++)
+ if(cache[i].link){
+ if(now > cache[i].stale){
+ peRssClearCacheEntry(&cache[i]);
+ }
+ else if(!strcmp(link, cache[i].link)){
+ cache[i].referenced++;
+ return(cache[i].feed); /* HIT! */
+ }
+ }
+
+ if((feed = peRssFetch(interp, link)) != NULL){
+ /* find cache slot, and insert feed into cache */
+ for(i = 0, cp_ref = 0; i < cache_l; i++)
+ if(!cache[i].feed){
+ cp = &cache[i];
+ break;
+ }
+ else if(cache[i].referenced >= cp_ref)
+ cp = &cache[i];
+
+ if(!cp)
+ cp = &cache[0]; /* failsafe */
+
+ peRssClearCacheEntry(cp); /* make sure */
+
+ cp->link = cpystr(link);
+ cp->feed = feed;
+ cp->referenced = 0;
+ cp->stale = now + (((feed->ttl > 0) ? feed->ttl : 60) * 60);
+ }
+
+ return(feed);
+}
+
+/*
+ * peRssFetch - follow the provided link an return the resulting struct
+ */
+RSS_FEED_S *
+peRssFetch(Tcl_Interp *interp, char *link)
+{
+ char *scheme = NULL, *loc = NULL, *path = NULL, *parms = NULL, *query = NULL, *frag = NULL;
+ char *buffer = NULL, *bp, *p, *q;
+ int ttl = 60;
+ unsigned long port = 0L, buffer_len = 0L;
+ time_t theirdate = 0;
+ STORE_S *feed_so = NULL;
+ TCPSTREAM *tcp_stream;
+
+ if(link){
+ /* grok url */
+ rfc1808_tokens(link, &scheme, &loc, &path, &parms, &query, &frag);
+ if(scheme && loc && path){
+ if((p = strchr(loc,':')) != NULL){
+ *p++ = '\0';
+ while(*p && isdigit((unsigned char) *p))
+ port = ((port * 10) + (*p++ - '0'));
+
+ if(*p){
+ Tcl_SetResult(interp, "Bad RSS port number", TCL_STATIC);
+ peRssComponentFree(&scheme,&loc,&path,&parms,&query,&frag);
+ return(NULL);
+ }
+ }
+
+ if(scheme && !strucmp(scheme, "feed")){
+ fs_give((void **) &scheme);
+ scheme = cpystr("http");
+ }
+
+ mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long) 5);
+ tcp_stream = tcp_open (loc, scheme, port | NET_NOOPENTIMEOUT);
+ mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long) 30);
+
+ if(tcp_stream != NULL){
+ char rev[128];
+
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "GET /%s%s%s%s%s HTTP/1.1\r\nHost: %s\r\nAccept: application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\nUser-Agent: Web-Alpine/%s (%s %s)\r\n\r\n",
+ path, parms ? ":" : "", parms ? parms : "",
+ query ? "?" : "", query ? query : "", loc,
+ ALPINE_VERSION, SYSTYPE, get_alpine_revision_string(rev, sizeof(rev)));
+
+ mail_parameters(NULL, SET_WRITETIMEOUT, (void *)(long) 5);
+ mail_parameters(NULL, SET_READTIMEOUT, (void *)(long) 5);
+
+ if(tcp_sout(tcp_stream, tmp_20k_buf, strlen(tmp_20k_buf))){
+ int ok = 0, chunked = FALSE;
+
+ while((p = tcp_getline(tcp_stream)) != NULL){
+ if(!ok){
+ ok++;
+ if(strucmp(p,"HTTP/1.1 200 OK")){
+ fs_give((void **) &p);
+ break; /* bail */
+ }
+ }
+ else if(*p == '\0'){ /* first blank line, start of body */
+ if(buffer || feed_so){
+ fs_give((void **) &p);
+ break; /* bail */
+ }
+
+ if(buffer_len){
+ buffer = fs_get(buffer_len + 16);
+ if(!tcp_getbuffer(tcp_stream, buffer_len, buffer))
+ fs_give((void **) &buffer);
+
+ fs_give((void **) &p);
+ break; /* bail */
+ }
+ else if((feed_so = so_get(CharStar, NULL, EDIT_ACCESS)) == NULL){
+ fs_give((void **) &p);
+ break; /* bail */
+ }
+ }
+ else if(feed_so){ /* collect body */
+ if(chunked){
+ int chunk_len = 0, gotbuf;
+
+ /* first line is chunk size in hex */
+ for(q = p; *q && isxdigit((unsigned char) *q); q++)
+ chunk_len = (chunk_len * 16) + XDIGIT2C(*q);
+
+ if(chunk_len > 0){ /* collect chunk */
+ char *tbuf = fs_get(chunk_len + 16);
+ gotbuf = tcp_getbuffer(tcp_stream, chunk_len, tbuf);
+ if(gotbuf)
+ so_nputs(feed_so, tbuf, chunk_len);
+
+ fs_give((void **) &tbuf);
+
+ if(!gotbuf){
+ fs_give((void **) &p);
+ break; /* bail */
+ }
+ }
+
+ /* collect trailing CRLF */
+ gotbuf = ((q = tcp_getline(tcp_stream)) != NULL && *q == '\0');
+ if(q)
+ fs_give((void **) &q);
+
+ if(chunk_len == 0 || !gotbuf){
+ fs_give((void **) &p);
+ break; /* bail */
+ }
+ }
+ else
+ so_puts(feed_so, p);
+ }
+ else{ /* in header, grok fields */
+ if(q = strchr(p,':')){
+ int l = q - p;
+
+ *q++ = '\0';
+ while(isspace((unsigned char ) *q))
+ q++;
+
+ /* content-length */
+ if(l == 4 && !strucmp(p, "date")){
+ theirdate = date_to_local_time_t(q);
+ }
+ else if(l == 7 && !strucmp(p, "expires")){
+ time_t expires = date_to_local_time_t(q) - ((theirdate > 0) ? theirdate : time(0));
+
+ if(expires > 0 && expires < (8 * 60 * 60))
+ ttl = expires;
+ }
+ else if(l == 12 && !strucmp(p, "content-type")
+ && struncmp(q,"text/xml", 8)
+ && struncmp(q,"application/xhtml+xml", 21)
+ && struncmp(q,"application/rss+xml", 19)
+ && struncmp(q,"application/xml", 15)){
+ fs_give((void **) &p);
+ break; /* bail */
+ }
+ else if(l == 13 && !strucmp(p, "cache-control")){
+ if(!struncmp(q,"max-age=",8)){
+ int secs = 0;
+
+ for(q += 8; *q && isdigit((unsigned char) *q); q++)
+ secs = ((secs * 10) + (*q - '0'));
+
+ if(secs > 0)
+ ttl = secs;
+ }
+ }
+ else if(l == 14 && !strucmp(p,"content-length")){
+ while(*q && isdigit((unsigned char) *q))
+ buffer_len = ((buffer_len * 10) + (*q++ - '0'));
+
+ if(*q){
+ fs_give((void **) &p);
+ break; /* bail */
+ }
+ }
+ else if(l == 17 && !strucmp(p, "transfer-encoding")){
+ if(!struncmp(q,"chunked", 7)){
+ chunked = TRUE;
+ }
+ else{ /* unknown encoding */
+ fs_give((void **) &p);
+ break; /* bail */
+ }
+ }
+ }
+ }
+
+ fs_give((void **) &p);
+ }
+ }
+ else{
+ Tcl_SetResult(interp, "RSS send failure", TCL_STATIC);
+ peRssComponentFree(&scheme,&loc,&path,&parms,&query,&frag);
+ }
+
+ tcp_close(tcp_stream);
+ mail_parameters(NULL, SET_READTIMEOUT, (void *)(long) 60);
+ mail_parameters(NULL, SET_WRITETIMEOUT, (void *)(long) 60);
+ peRssComponentFree(&scheme,&loc,&path,&parms,&query,&frag);
+
+ if(feed_so){
+ buffer = (char *) so_text(feed_so);
+ buffer_len = (int) so_tell(feed_so);
+ }
+
+ if(buffer && buffer_len){
+ RSS_FEED_S *feed;
+ char *err;
+ STORE_S *bucket;
+ gf_io_t gc, pc;
+
+ /* grok response */
+ bucket = so_get(CharStar, NULL, EDIT_ACCESS);
+ gf_set_readc(&gc, buffer, buffer_len, CharStar, 0);
+ gf_set_so_writec(&pc, bucket);
+ gf_filter_init();
+ gf_link_filter(gf_html2plain, gf_html2plain_rss_opt(&feed,0));
+ if((err = gf_pipe(gc, pc)) != NULL){
+ gf_html2plain_rss_free(&feed);
+ Tcl_SetResult(interp, "RSS connection failure", TCL_STATIC);
+ }
+
+ so_give(&bucket);
+
+ if(feed_so)
+ so_give(&feed_so);
+ else
+ fs_give((void **) &buffer);
+
+ return(feed);
+ }
+ else
+ Tcl_SetResult(interp, "RSS response error", TCL_STATIC);
+ }
+ else
+ Tcl_SetResult(interp, "RSS connection failure", TCL_STATIC);
+ }
+ else
+ Tcl_SetResult(interp, "RSS feed missing scheme", TCL_STATIC);
+ }
+ else
+ Tcl_SetResult(interp, "No RSS Feed Defined", TCL_STATIC);
+
+ return(NULL);
+}
+
+
+void
+peRssComponentFree(char **scheme,char **loc,char **path,char **parms,char **query,char **frag)
+{
+ if(scheme) fs_give((void **) scheme);
+ if(loc) fs_give((void **) loc);
+ if(path) fs_give((void **) path);
+ if(parms) fs_give((void **) parms);
+ if(query) fs_give((void **) query);
+ if(frag) fs_give((void **) frag);
+}
+
+void
+peRssClearCacheEntry(RSS_CACHE_S *entry)
+{
+ if(entry){
+ if(entry->link)
+ fs_give((void **) &entry->link);
+
+ gf_html2plain_rss_free(&entry->feed);
+ memset(entry, 0, sizeof(RSS_CACHE_S));
+ }
+}
diff --git a/web/src/alpined.d/alpined.h b/web/src/alpined.d/alpined.h
new file mode 100644
index 00000000..b16fd5bc
--- /dev/null
+++ b/web/src/alpined.d/alpined.h
@@ -0,0 +1,49 @@
+/*-----------------------------------------------------------------------
+ $Id: alpined.h 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $
+ -----------------------------------------------------------------------*/
+
+/* ========================================================================
+ * Copyright 2006-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*
+ * Various constants
+ */
+#define WP_MAXSTATUS 1024
+
+/*
+ * Seconds afterwhich we bail on imap connections
+ */
+#define WP_TCP_TIMEOUT (10 * 60)
+
+/*
+ * buf to hold hostname for auth/cert
+ */
+#define CRED_REQ_SIZE 256
+
+/*
+ * Various external definitions
+ */
+extern int peNoPassword;
+extern int peCredentialError;
+extern char peCredentialRequestor[];
+extern int peCertQuery;
+extern int peCertFailure;
+extern char *peSocketName;
+extern STRLIST_S *peCertHosts;
+
+/*
+ * Protoypes for various functions
+ */
+
+/* alpined.c */
+void sml_addmsg(int, char *);
+
diff --git a/web/src/alpined.d/alpineldap.c b/web/src/alpined.d/alpineldap.c
new file mode 100644
index 00000000..c29ef7f4
--- /dev/null
+++ b/web/src/alpined.d/alpineldap.c
@@ -0,0 +1,181 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: alpineldap.c 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $";
+#endif
+
+/* ========================================================================
+ * Copyright 2006-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "../../../c-client/c-client.h"
+#include "../../../c-client/imap4r1.h"
+
+#include "../../../pith/osdep/color.h" /* color support library */
+#include "../../../pith/osdep/canaccess.h"
+#include "../../../pith/osdep/temp_nam.h"
+
+#include "../../../pith/stream.h"
+#include "../../../pith/context.h"
+#include "../../../pith/state.h"
+#include "../../../pith/msgno.h"
+#include "../../../pith/debug.h"
+#include "../../../pith/init.h"
+#include "../../../pith/conf.h"
+#include "../../../pith/conftype.h"
+#include "../../../pith/detoken.h"
+#include "../../../pith/flag.h"
+#include "../../../pith/help.h"
+#include "../../../pith/remote.h"
+#include "../../../pith/status.h"
+#include "../../../pith/mailcmd.h"
+#include "../../../pith/savetype.h"
+#include "../../../pith/save.h"
+#include "../../../pith/reply.h"
+#include "../../../pith/sort.h"
+#include "../../../pith/ldap.h"
+#include "../../../pith/addrbook.h"
+#include "../../../pith/takeaddr.h"
+#include "../../../pith/bldaddr.h"
+#include "../../../pith/copyaddr.h"
+#include "../../../pith/thread.h"
+#include "../../../pith/folder.h"
+#include "../../../pith/mailview.h"
+#include "../../../pith/indxtype.h"
+#include "../../../pith/mailindx.h"
+#include "../../../pith/mailpart.h"
+#include "../../../pith/mimedesc.h"
+#include "../../../pith/detach.h"
+#include "../../../pith/newmail.h"
+#include "../../../pith/charset.h"
+#include "../../../pith/util.h"
+#include "../../../pith/rfc2231.h"
+#include "../../../pith/string.h"
+#include "../../../pith/send.h"
+
+#include "alpined.h"
+#include "ldap.h"
+
+struct pine *ps_global; /* THE global variable! */
+char tmp_20k_buf[20480];
+
+char *peSocketName;
+
+#ifdef ENABLE_LDAP
+WPLDAP_S *wpldap_global;
+#endif
+
+int peNoPassword, peCredentialError;
+int peCertQuery, peCertFailure;
+char peCredentialRequestor[CRED_REQ_SIZE];
+STRLIST_S *peCertHosts;
+
+void
+sml_addmsg(priority, text)
+ int priority;
+ char *text;
+{
+}
+
+void
+peDestroyUserContext(pps)
+ struct pine **pps;
+{
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+#ifdef ENABLE_LDAP
+ struct pine *pine_state;
+ char *p = NULL, *userid = NULL, *domain = NULL, *pname;
+ struct variable *vars;
+ int i, usage = 0, rv = 0;
+
+ pine_state = new_pine_struct();
+ ps_global = pine_state;
+ vars = ps_global->vars;
+ debug = 0;
+
+ for(i = 1 ; i < argc; i++){
+ if(*argv[i] == '-'){
+ switch (argv[i++][1]) {
+ case 'p':
+ p = argv[i];
+ break;
+ case 'u':
+ userid = argv[i];
+ break;
+ case 'd':
+ domain = argv[i];
+ break;
+ default:
+ usage = rv = 1;
+ break;
+ }
+ }
+ else
+ usage = rv = 1;
+ if(usage == 1) break;
+ }
+ if(argc == 1) usage = rv = 1;
+ if (usage == 1 || !p || !userid){
+ usage = rv = 1;
+ goto done;
+ }
+ wpldap_global = (WPLDAP_S *)fs_get(sizeof(WPLDAP_S));
+ wpldap_global->query_no = 0;
+ wpldap_global->ldap_search_list = NULL;
+
+ ps_global->pconf = new_pinerc_s(p);
+ if(ps_global->pconf)
+ read_pinerc(ps_global->pconf, vars, ParseGlobal);
+ else {
+ fprintf(stderr, "Failed to read pineconf\n");
+ rv = 1;
+ goto done;
+ }
+ set_current_val(&ps_global->vars[V_LDAP_SERVERS], FALSE, FALSE);
+ set_current_val(&ps_global->vars[V_USER_DOMAIN], FALSE, FALSE);
+ if(!ps_global->VAR_USER_DOMAIN && !domain){
+ fprintf(stderr, "No domain set in pineconf\n");
+ usage = 1;
+ goto done;
+ }
+ if((pname = peLdapPname(userid, domain ? domain : ps_global->VAR_USER_DOMAIN)) != NULL){
+ fprintf(stdout, "%s\n", pname);
+ fs_give((void **)&pname);
+ }
+ else
+ fprintf(stdout, "\n");
+
+done:
+ if(usage)
+ fprintf(stderr, "usage: pineldap -u userid -p pineconf [-d domain]\n");
+ if(wpldap_global){
+ if(wpldap_global->ldap_search_list)
+ free_wpldapres(wpldap_global->ldap_search_list);
+ fs_give((void **)&wpldap_global);
+ }
+ if(ps_global->pconf)
+ free_pinerc_s(&ps_global->pconf);
+ free_pine_struct(&pine_state);
+
+ exit(rv);
+#else
+ fprintf(stderr, "%s: Not built with LDAP support\n", argv[0]);
+ exit(-1);
+#endif
+}
+
diff --git a/web/src/alpined.d/busy.c b/web/src/alpined.d/busy.c
new file mode 100644
index 00000000..6f10fa7f
--- /dev/null
+++ b/web/src/alpined.d/busy.c
@@ -0,0 +1,49 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: signal.c 91 2006-07-28 19:02:07Z mikes@u.washington.edu $";
+#endif
+
+/* ========================================================================
+ * Copyright 2006-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "../../../pith/status.h"
+#include "../../../pith/busy.h"
+
+
+/*
+ * Turn on a busy alarm.
+ */
+int
+busy_cue(char *msg, percent_done_t pc_func, int init_msg)
+{
+ if(msg && !strncmp("Moving", msg, 6)){
+ strncpy(msg+1, "Moved", 5);
+ q_status_message(SM_ORDER, 3, 3, msg+1);
+ }
+
+ return(0);
+}
+
+
+/*
+ * If final_message was set when busy_cue was called:
+ * and message_pri = -1 -- no final message queued
+ * else final message queued with min equal to message_pri
+ */
+void
+cancel_busy_cue(int message_pri)
+{
+}
+
+
diff --git a/web/src/alpined.d/color.c b/web/src/alpined.d/color.c
new file mode 100644
index 00000000..e8d0af86
--- /dev/null
+++ b/web/src/alpined.d/color.c
@@ -0,0 +1,678 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: color.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/* ========================================================================
+ * Copyright 2006-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "../../../pith/osdep/color.h"
+#include "../../../pith/osdep/collate.h"
+
+
+static COLOR_PAIR *the_rev_color;
+static char *_nfcolor, *_nbcolor, *_rfcolor, *_rbcolor;
+static char *_last_fg_color, *_last_bg_color;
+static int _force_fg_color_change, _force_bg_color_change;
+
+
+/* * * * * * * PITH-REQUIRED COLOR ROUTINES * * * * * * */
+
+/* internal prototypes */
+char *alpine_color_name(char *);
+int alpine_valid_rgb(char *s);
+
+int
+pico_usingcolor(void)
+{
+ return(TRUE);
+}
+
+
+int
+pico_hascolor(void)
+{
+ return(TRUE);
+}
+
+
+/*
+ * Web Alpine Color Table
+ */
+static struct color_table {
+ int number;
+ char *rgb;
+ struct {
+ char *s,
+ l;
+ } name;
+} webcoltab[] = {
+ {COL_BLACK, " 0, 0, 0", {"black", 5}},
+ {COL_RED, "255, 0, 0", {"red", 3}},
+ {COL_GREEN, " 0,255, 0", {"green", 5}},
+ {COL_YELLOW, "255,255, 0", {"yellow", 6}},
+ {COL_BLUE, " 0, 0,255", {"blue", 4}},
+ {COL_MAGENTA, "255, 0,255", {"magenta", 7}},
+ {COL_CYAN, " 0,255,255", {"cyan", 4}},
+ {COL_WHITE, "255,255,255", {"white", 5}},
+ {8, "192,192,192", {"color008", 8}}, /* light gray */
+ {9, "128,128,128", {"color009", 8}}, /* gray */
+ {10, " 64, 64, 64", {"color010", 8}}, /* dark gray */
+ {COL_YELLOW, "255,255, 0", {"color011", 8}},
+ {COL_BLUE, " 0, 0,255", {"color012", 8}},
+ {COL_MAGENTA, "255, 0,255", {"color013", 8}},
+ {COL_CYAN, " 0,255,255", {"color014", 8}},
+ {COL_WHITE, "255,255,255", {"color015", 8}},
+ {8, "192,192,192", {"colorlgr", 8}}, /* light gray */
+ {9, "128,128,128", {"colormgr", 8}}, /* gray */
+ {10, " 64, 64, 64", {"colordgr", 8}}, /* dark gray */
+ {16, "000,000,000", {"color016", 8}},
+ {17, "000,000,095", {"color017", 8}},
+ {18, "000,000,135", {"color018", 8}},
+ {19, "000,000,175", {"color019", 8}},
+ {20, "000,000,215", {"color020", 8}},
+ {21, "000,000,255", {"color021", 8}},
+ {22, "000,095,000", {"color022", 8}},
+ {23, "000,095,095", {"color023", 8}},
+ {24, "000,095,135", {"color024", 8}},
+ {25, "000,095,175", {"color025", 8}},
+ {26, "000,095,215", {"color026", 8}},
+ {27, "000,095,255", {"color027", 8}},
+ {28, "000,135,000", {"color028", 8}},
+ {29, "000,135,095", {"color029", 8}},
+ {30, "000,135,135", {"color030", 8}},
+ {31, "000,135,175", {"color031", 8}},
+ {32, "000,135,215", {"color032", 8}},
+ {33, "000,135,255", {"color033", 8}},
+ {34, "000,175,000", {"color034", 8}},
+ {35, "000,175,095", {"color035", 8}},
+ {36, "000,175,135", {"color036", 8}},
+ {37, "000,175,175", {"color037", 8}},
+ {38, "000,175,215", {"color038", 8}},
+ {39, "000,175,255", {"color039", 8}},
+ {40, "000,215,000", {"color040", 8}},
+ {41, "000,215,095", {"color041", 8}},
+ {42, "000,215,135", {"color042", 8}},
+ {43, "000,215,175", {"color043", 8}},
+ {44, "000,215,215", {"color044", 8}},
+ {45, "000,215,255", {"color045", 8}},
+ {46, "000,255,000", {"color046", 8}},
+ {47, "000,255,095", {"color047", 8}},
+ {48, "000,255,135", {"color048", 8}},
+ {49, "000,255,175", {"color049", 8}},
+ {50, "000,255,215", {"color050", 8}},
+ {51, "000,255,255", {"color051", 8}},
+ {52, "095,000,000", {"color052", 8}},
+ {53, "095,000,095", {"color053", 8}},
+ {54, "095,000,135", {"color054", 8}},
+ {55, "095,000,175", {"color055", 8}},
+ {56, "095,000,215", {"color056", 8}},
+ {57, "095,000,255", {"color057", 8}},
+ {58, "095,095,000", {"color058", 8}},
+ {59, "095,095,095", {"color059", 8}},
+ {60, "095,095,135", {"color060", 8}},
+ {61, "095,095,175", {"color061", 8}},
+ {62, "095,095,215", {"color062", 8}},
+ {63, "095,095,255", {"color063", 8}},
+ {64, "095,135,000", {"color064", 8}},
+ {65, "095,135,095", {"color065", 8}},
+ {66, "095,135,135", {"color066", 8}},
+ {67, "095,135,175", {"color067", 8}},
+ {68, "095,135,215", {"color068", 8}},
+ {69, "095,135,255", {"color069", 8}},
+ {70, "095,175,000", {"color070", 8}},
+ {71, "095,175,095", {"color071", 8}},
+ {72, "095,175,135", {"color072", 8}},
+ {73, "095,175,175", {"color073", 8}},
+ {74, "095,175,215", {"color074", 8}},
+ {75, "095,175,255", {"color075", 8}},
+ {76, "095,215,000", {"color076", 8}},
+ {77, "095,215,095", {"color077", 8}},
+ {78, "095,215,135", {"color078", 8}},
+ {79, "095,215,175", {"color079", 8}},
+ {80, "095,215,215", {"color080", 8}},
+ {81, "095,215,255", {"color081", 8}},
+ {82, "095,255,000", {"color082", 8}},
+ {83, "095,255,095", {"color083", 8}},
+ {84, "095,255,135", {"color084", 8}},
+ {85, "095,255,175", {"color085", 8}},
+ {86, "095,255,215", {"color086", 8}},
+ {87, "095,255,255", {"color087", 8}},
+ {88, "135,000,000", {"color088", 8}},
+ {89, "135,000,095", {"color089", 8}},
+ {90, "135,000,135", {"color090", 8}},
+ {91, "135,000,175", {"color091", 8}},
+ {92, "135,000,215", {"color092", 8}},
+ {93, "135,000,255", {"color093", 8}},
+ {94, "135,095,000", {"color094", 8}},
+ {95, "135,095,095", {"color095", 8}},
+ {96, "135,095,135", {"color096", 8}},
+ {97, "135,095,175", {"color097", 8}},
+ {98, "135,095,215", {"color098", 8}},
+ {99, "135,095,255", {"color099", 8}},
+ {100, "135,135,000", {"color100", 8}},
+ {101, "135,135,095", {"color101", 8}},
+ {102, "135,135,135", {"color102", 8}},
+ {103, "135,135,175", {"color103", 8}},
+ {104, "135,135,215", {"color104", 8}},
+ {105, "135,135,255", {"color105", 8}},
+ {106, "135,175,000", {"color106", 8}},
+ {107, "135,175,095", {"color107", 8}},
+ {108, "135,175,135", {"color108", 8}},
+ {109, "135,175,175", {"color109", 8}},
+ {110, "135,175,215", {"color110", 8}},
+ {111, "135,175,255", {"color111", 8}},
+ {112, "135,215,000", {"color112", 8}},
+ {113, "135,215,095", {"color113", 8}},
+ {114, "135,215,135", {"color114", 8}},
+ {115, "135,215,175", {"color115", 8}},
+ {116, "135,215,215", {"color116", 8}},
+ {117, "135,215,255", {"color117", 8}},
+ {118, "135,255,000", {"color118", 8}},
+ {119, "135,255,095", {"color119", 8}},
+ {120, "135,255,135", {"color120", 8}},
+ {121, "135,255,175", {"color121", 8}},
+ {122, "135,255,215", {"color122", 8}},
+ {123, "135,255,255", {"color123", 8}},
+ {124, "175,000,000", {"color124", 8}},
+ {125, "175,000,095", {"color125", 8}},
+ {126, "175,000,135", {"color126", 8}},
+ {127, "175,000,175", {"color127", 8}},
+ {128, "175,000,215", {"color128", 8}},
+ {129, "175,000,255", {"color129", 8}},
+ {130, "175,095,000", {"color130", 8}},
+ {131, "175,095,095", {"color131", 8}},
+ {132, "175,095,135", {"color132", 8}},
+ {133, "175,095,175", {"color133", 8}},
+ {134, "175,095,215", {"color134", 8}},
+ {135, "175,095,255", {"color135", 8}},
+ {136, "175,135,000", {"color136", 8}},
+ {137, "175,135,095", {"color137", 8}},
+ {138, "175,135,135", {"color138", 8}},
+ {139, "175,135,175", {"color139", 8}},
+ {140, "175,135,215", {"color140", 8}},
+ {141, "175,135,255", {"color141", 8}},
+ {142, "175,175,000", {"color142", 8}},
+ {143, "175,175,095", {"color143", 8}},
+ {144, "175,175,135", {"color144", 8}},
+ {145, "175,175,175", {"color145", 8}},
+ {146, "175,175,215", {"color146", 8}},
+ {147, "175,175,255", {"color147", 8}},
+ {148, "175,215,000", {"color148", 8}},
+ {149, "175,215,095", {"color149", 8}},
+ {150, "175,215,135", {"color150", 8}},
+ {151, "175,215,175", {"color151", 8}},
+ {152, "175,215,215", {"color152", 8}},
+ {153, "175,215,255", {"color153", 8}},
+ {154, "175,255,000", {"color154", 8}},
+ {155, "175,255,095", {"color155", 8}},
+ {156, "175,255,135", {"color156", 8}},
+ {157, "175,255,175", {"color157", 8}},
+ {158, "175,255,215", {"color158", 8}},
+ {159, "175,255,255", {"color159", 8}},
+ {160, "215,000,000", {"color160", 8}},
+ {161, "215,000,095", {"color161", 8}},
+ {162, "215,000,135", {"color162", 8}},
+ {163, "215,000,175", {"color163", 8}},
+ {164, "215,000,215", {"color164", 8}},
+ {165, "215,000,255", {"color165", 8}},
+ {166, "215,095,000", {"color166", 8}},
+ {167, "215,095,095", {"color167", 8}},
+ {168, "215,095,135", {"color168", 8}},
+ {169, "215,095,175", {"color169", 8}},
+ {170, "215,095,215", {"color170", 8}},
+ {171, "215,095,255", {"color171", 8}},
+ {172, "215,135,000", {"color172", 8}},
+ {173, "215,135,095", {"color173", 8}},
+ {174, "215,135,135", {"color174", 8}},
+ {175, "215,135,175", {"color175", 8}},
+ {176, "215,135,215", {"color176", 8}},
+ {177, "215,135,255", {"color177", 8}},
+ {178, "215,175,000", {"color178", 8}},
+ {179, "215,175,095", {"color179", 8}},
+ {180, "215,175,135", {"color180", 8}},
+ {181, "215,175,175", {"color181", 8}},
+ {182, "215,175,215", {"color182", 8}},
+ {183, "215,175,255", {"color183", 8}},
+ {184, "215,215,000", {"color184", 8}},
+ {185, "215,215,095", {"color185", 8}},
+ {186, "215,215,135", {"color186", 8}},
+ {187, "215,215,175", {"color187", 8}},
+ {188, "215,215,215", {"color188", 8}},
+ {189, "215,215,255", {"color189", 8}},
+ {190, "215,255,000", {"color190", 8}},
+ {191, "215,255,095", {"color191", 8}},
+ {192, "215,255,135", {"color192", 8}},
+ {193, "215,255,175", {"color193", 8}},
+ {194, "215,255,215", {"color194", 8}},
+ {195, "215,255,255", {"color195", 8}},
+ {196, "255,000,000", {"color196", 8}},
+ {197, "255,000,095", {"color197", 8}},
+ {198, "255,000,135", {"color198", 8}},
+ {199, "255,000,175", {"color199", 8}},
+ {200, "255,000,215", {"color200", 8}},
+ {201, "255,000,255", {"color201", 8}},
+ {202, "255,095,000", {"color202", 8}},
+ {203, "255,095,095", {"color203", 8}},
+ {204, "255,095,135", {"color204", 8}},
+ {205, "255,095,175", {"color205", 8}},
+ {206, "255,095,215", {"color206", 8}},
+ {207, "255,095,255", {"color207", 8}},
+ {208, "255,135,000", {"color208", 8}},
+ {209, "255,135,095", {"color209", 8}},
+ {210, "255,135,135", {"color210", 8}},
+ {211, "255,135,175", {"color211", 8}},
+ {212, "255,135,215", {"color212", 8}},
+ {213, "255,135,255", {"color213", 8}},
+ {214, "255,175,000", {"color214", 8}},
+ {215, "255,175,095", {"color215", 8}},
+ {216, "255,175,135", {"color216", 8}},
+ {217, "255,175,175", {"color217", 8}},
+ {218, "255,175,215", {"color218", 8}},
+ {219, "255,175,255", {"color219", 8}},
+ {220, "255,215,000", {"color220", 8}},
+ {221, "255,215,095", {"color221", 8}},
+ {222, "255,215,135", {"color222", 8}},
+ {223, "255,215,175", {"color223", 8}},
+ {224, "255,215,215", {"color224", 8}},
+ {225, "255,215,255", {"color225", 8}},
+ {226, "255,255,000", {"color226", 8}},
+ {227, "255,255,095", {"color227", 8}},
+ {228, "255,255,135", {"color228", 8}},
+ {229, "255,255,175", {"color229", 8}},
+ {230, "255,255,215", {"color230", 8}},
+ {231, "255,255,255", {"color231", 8}}
+};
+
+
+char *
+colorx(int color)
+{
+ int i;
+ static char cbuf[12];
+
+ for(i = 0; i < sizeof(webcoltab) / sizeof(struct color_table); i++)
+ if(color == webcoltab[i].number)
+ return(webcoltab[i].rgb);
+
+ sprintf(cbuf, "color%3.3d", color);
+ return(cbuf);
+}
+
+
+/*
+ * Return a pointer to an rgb string for the input color. The output is 11
+ * characters long and looks like rrr,ggg,bbb.
+ *
+ * Args colorName -- The color to convert to ascii rgb.
+ *
+ * Returns Pointer to a static buffer containing the rgb string.
+ */
+char *
+color_to_asciirgb(char *colorName)
+{
+ int i;
+ static char c_to_a_buf[3][RGBLEN+1];
+ static int whichbuf = 0;
+
+ whichbuf = (whichbuf + 1) % 3;
+
+ for(i = 0; i < sizeof(webcoltab) / sizeof(struct color_table); i++)
+ if(!strucmp(webcoltab[i].name.s, colorName))
+ return(webcoltab[i].rgb);
+
+ /*
+ * If we didn't find the color it could be that it is the
+ * normal color (MATCH_NORM_COLOR) or the none color
+ * (MATCH_NONE_COLOR). If that is the case, this strncpy thing
+ * will work out correctly because those two strings are
+ * RGBLEN long. Otherwise we're in a bit of trouble. This
+ * most likely means that the user is using the same pinerc on
+ * two terminals, one with more colors than the other. We didn't
+ * find a match because this color isn't present on this terminal.
+ * Since the return value of this function is assumed to be
+ * RGBLEN long, we'd better make it that long.
+ * It still won't work correctly because colors will be screwed up,
+ * but at least the embedded colors in filter.c will get properly
+ * sucked up when they're encountered.
+ */
+ strncpy(c_to_a_buf[whichbuf], "xxxxxxxxxxx", RGBLEN); /* RGBLEN is 11 */
+ i = strlen(colorName);
+ strncpy(c_to_a_buf[whichbuf], colorName, (i < RGBLEN) ? i : RGBLEN);
+ c_to_a_buf[whichbuf][RGBLEN] = '\0';
+ return(c_to_a_buf[whichbuf]);
+}
+
+
+
+int
+pico_is_good_color(char *s)
+{
+ return(alpine_color_name(s) != NULL || alpine_valid_rgb(s));
+}
+
+
+int
+alpine_valid_rgb(char *s)
+{
+ int i, j;
+
+ /* has to be three spaces or decimal digits followed by a dot.*/
+
+ for(i = 0; i < 3; i++){
+ int n = 0;
+
+ for(j = 0; j < 3; j++, s++) {
+ if(*s == ' '){
+ if(n)
+ return(FALSE);
+ }
+ else if(isdigit((unsigned char) *s)){
+ n = (n * 10) + (*s - '0');
+ }
+ else
+ return(FALSE);
+ }
+
+ if (i < 2 && *s++ != ',')
+ return(FALSE);
+ }
+
+ return (TRUE);
+}
+
+
+
+char *
+alpine_color_name(char *s)
+{
+ if(s){
+ int i;
+
+ if(!struncmp(s, MATCH_NORM_COLOR, RGBLEN) || !struncmp(s, MATCH_NONE_COLOR, RGBLEN))
+ return(s);
+ else if(*s == ' ' || isdigit(*s)){
+ /* check for rgb string instead of name */
+ for(i = 0; i < sizeof(webcoltab) / sizeof(struct color_table); i++)
+ if(!strncmp(webcoltab[i].rgb, s, RGBLEN))
+ return(webcoltab[i].name.s);
+ }
+ else{
+ for(i = 0; i < sizeof(webcoltab) / sizeof(struct color_table); i++)
+ if(!struncmp(webcoltab[i].name.s, s, webcoltab[i].name.l))
+ return(webcoltab[i].name.s);
+ }
+ }
+
+ return(NULL);
+}
+
+
+/*
+ * Sets color to (fg,bg).
+ * Flags == PSC_NONE No alternate default if fg,bg fails.
+ * == PSC_NORM Set it to Normal color on failure.
+ * == PSC_REV Set it to Reverse color on failure.
+ *
+ * If flag PSC_RET is set, returns an allocated copy of the previous
+ * color pair, otherwise returns NULL.
+ */
+COLOR_PAIR *
+pico_set_colors(char *fg, char *bg, int flags)
+{
+ int uc;
+ COLOR_PAIR *cp = NULL, *rev = NULL;
+
+ if(flags & PSC_RET)
+ cp = pico_get_cur_color();
+
+ if(!((uc = pico_usingcolor())
+ && fg && bg
+ && pico_set_fg_color(fg) && pico_set_bg_color(bg))){
+
+ if(uc && flags & PSC_NORM){
+ pico_set_normal_color();
+ }
+ else if(flags & PSC_REV){
+ if((rev = pico_get_rev_color()) != NULL){
+ pico_set_fg_color(rev->fg); /* these will succeed */
+ pico_set_bg_color(rev->bg);
+ }
+ }
+ }
+
+ return(cp);
+}
+
+
+
+void
+pico_nfcolor(char *s)
+{
+ if(_nfcolor){
+ free(_nfcolor);
+ _nfcolor = NULL;
+ }
+
+ if(s){
+ _nfcolor = (char *)malloc(strlen(s)+1);
+ if(_nfcolor)
+ strcpy(_nfcolor, s);
+ }
+}
+
+
+void
+pico_nbcolor(char *s)
+{
+ if(_nbcolor){
+ free(_nbcolor);
+ _nbcolor = NULL;
+ }
+
+ if(s){
+ _nbcolor = (char *)malloc(strlen(s)+1);
+ if(_nbcolor)
+ strcpy(_nbcolor, s);
+ }
+}
+
+void
+pico_rfcolor(char *s)
+{
+ if(_rfcolor){
+ free(_rfcolor);
+ _rfcolor = NULL;
+ }
+
+ if(s){
+ _rfcolor = (char *)malloc(strlen(s)+1);
+ if(_rfcolor)
+ strcpy(_rfcolor, s);
+
+ if(the_rev_color)
+ strcpy(the_rev_color->fg, _rfcolor);
+ }
+ else if(the_rev_color)
+ free_color_pair(&the_rev_color);
+}
+
+void
+pico_rbcolor(char *s)
+{
+ if(_rbcolor){
+ free(_rbcolor);
+ _rbcolor = NULL;
+ }
+
+ if(s){
+ _rbcolor = (char *)malloc(strlen(s)+1);
+ if(_rbcolor)
+ strcpy(_rbcolor, s);
+
+ if(the_rev_color)
+ strcpy(the_rev_color->bg, _rbcolor);
+ }
+ else if(the_rev_color)
+ free_color_pair(&the_rev_color);
+}
+
+
+void
+pico_endcolor(void)
+{
+ if(_nfcolor){
+ free(_nfcolor);
+ _nfcolor = NULL;
+ }
+
+ if(_nbcolor){
+ free(_nbcolor);
+ _nbcolor = NULL;
+ }
+
+ if(_rfcolor){
+ free(_rfcolor);
+ _rfcolor = NULL;
+ }
+
+ if(_rbcolor){
+ free(_rbcolor);
+ _rbcolor = NULL;
+ }
+
+ if(the_rev_color)
+ free_color_pair(&the_rev_color);
+}
+
+
+COLOR_PAIR *
+pico_get_cur_color(void)
+{
+ return(new_color_pair(_last_fg_color, _last_bg_color));
+}
+
+/*
+ * If inverse is a color, returns a pointer to that color.
+ * If not, returns NULL.
+ *
+ * NOTE: Don't free this!
+ */
+COLOR_PAIR *
+pico_get_rev_color(void)
+{
+ if(pico_usingcolor() && _rfcolor && _rbcolor &&
+ pico_is_good_color(_rfcolor) && pico_is_good_color(_rbcolor)){
+ if(!the_rev_color)
+ the_rev_color = new_color_pair(_rfcolor, _rbcolor);
+
+ return(the_rev_color);
+ }
+ else
+ return(NULL);
+}
+
+
+int
+pico_set_fg_color(char *s)
+{
+ if(pico_is_good_color(s)){
+ if(!struncmp(s, MATCH_NORM_COLOR, RGBLEN))
+ s = _nfcolor;
+ else if(!struncmp(s, MATCH_NONE_COLOR, RGBLEN))
+ return(TRUE);
+
+ /* already set correctly */
+ if(!_force_fg_color_change
+ && _last_fg_color
+ && !strcmp(_last_fg_color, s))
+ return(TRUE);
+
+ _force_fg_color_change = 0;
+ if(_last_fg_color)
+ free(_last_fg_color);
+
+ if((_last_fg_color = (char *) malloc(strlen(s) + 1)) != NULL)
+ strcpy(_last_fg_color, s);
+
+ return(TRUE);
+ }
+
+ return(FALSE);
+}
+
+
+int
+pico_set_bg_color(char *s)
+{
+ if(pico_is_good_color(s)){
+ if(!struncmp(s, MATCH_NORM_COLOR, RGBLEN))
+ s = _nbcolor;
+ else if(!struncmp(s, MATCH_NONE_COLOR, RGBLEN))
+ return(TRUE);
+
+ /* already set correctly */
+ if(!_force_bg_color_change
+ && _last_bg_color
+ && !strcmp(_last_bg_color, s))
+ return(TRUE);
+
+ _force_bg_color_change = 0;
+ if(_last_bg_color)
+ free(_last_bg_color);
+
+ if((_last_bg_color = (char *) malloc(strlen(s) + 1)) != NULL)
+ strcpy(_last_bg_color, s);
+
+ return(TRUE);
+ }
+
+ return(FALSE);
+}
+
+
+void
+pico_set_normal_color(void)
+{
+ if(!_nfcolor || !_nbcolor ||
+ !pico_set_fg_color(_nfcolor) || !pico_set_bg_color(_nbcolor)){
+ (void)pico_set_fg_color(DEFAULT_NORM_FORE_RGB);
+ (void)pico_set_bg_color(DEFAULT_NORM_BACK_RGB);
+ }
+}
+
+
+char *
+pico_get_last_fg_color(void)
+{
+ char *ret = NULL;
+
+ if(_last_fg_color)
+ if((ret = (char *)malloc(strlen(_last_fg_color)+1)) != NULL)
+ strcpy(ret, _last_fg_color);
+
+ return(ret);
+}
+
+char *
+pico_get_last_bg_color(void)
+{
+ char *ret = NULL;
+
+ if(_last_bg_color)
+ if((ret = (char *)malloc(strlen(_last_bg_color)+1)) != NULL)
+ strcpy(ret, _last_bg_color);
+
+ return(ret);
+}
diff --git a/web/src/alpined.d/color.h b/web/src/alpined.d/color.h
new file mode 100644
index 00000000..3040ffbc
--- /dev/null
+++ b/web/src/alpined.d/color.h
@@ -0,0 +1,25 @@
+/*-----------------------------------------------------------------------
+ $Id: color.h 764 2007-10-23 23:44:49Z hubert@u.washington.edu $
+ -----------------------------------------------------------------------*/
+
+/* ========================================================================
+ * Copyright 2006-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef _WEB_ALPINE_COLOR_INCLUDED
+#define _WEB_ALPINE_COLOR_INCLUDED
+
+void pico_endcolor(void);
+
+#endif /* _WEB_ALPINE_COLOR_INCLUDED */
+
+
+
diff --git a/web/src/alpined.d/debug.c b/web/src/alpined.d/debug.c
new file mode 100644
index 00000000..d3c4d2b7
--- /dev/null
+++ b/web/src/alpined.d/debug.c
@@ -0,0 +1,151 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: wimap.c 73 2006-06-13 16:46:59Z hubert@u.washington.edu $";
+#endif
+
+/* ========================================================================
+ * Copyright 2006-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ debug.c
+ Provide debug support routines
+ ====*/
+
+#include <system.h>
+#include <general.h>
+
+#include "../../../c-client/c-client.h"
+
+#include "../../../pith/state.h"
+#include "../../../pith/debug.h"
+
+#include "debug.h"
+
+
+#define MAX_DEBUG_FMT 1024
+
+
+#ifndef DEBUG
+/*
+ * Preserve debug for syslog trace
+ */
+int debug;
+#endif
+
+
+void
+debug_init(void)
+{
+#if HAVE_SYSLOG
+ openlog("alpined", LOG_PID, LOG_MAIL);
+#endif
+}
+
+
+void
+output_debug_msg(int dlevel, char *fmt, ...)
+{
+ /* always write SYSDBG */
+ if((dlevel & SYSDBG) || dlevel <= debug){
+#if HAVE_SYSLOG
+ va_list args;
+ char fmt2[MAX_DEBUG_FMT], *p, *q, *trailing = NULL;
+ int priority = LOG_DEBUG, leading = 1;
+
+ /* whack nl's */
+ for(p = fmt, q = fmt2; *p && p - fmt < MAX_DEBUG_FMT - 2; p++){
+ if(*p == '\n'){
+ if(!leading && !trailing)
+ trailing = q;
+ }
+ else{
+ leading = 0;
+ if(trailing){
+ *q++ = '_';
+ trailing = NULL;
+ }
+
+ *q++ = *p;
+ }
+ }
+
+ *q = '\0';
+ if(trailing)
+ *trailing = '\0';
+
+ if(dlevel & SYSDBG)
+ switch(dlevel){
+ case SYSDBG_ALERT : priority = LOG_ALERT; break;
+ case SYSDBG_ERR : priority = LOG_ERR; break;
+ case SYSDBG_INFO : priority = LOG_INFO; break;
+ default : priority = LOG_DEBUG; break;
+ }
+
+
+ va_start(args, fmt);
+ vsyslog(priority, fmt2, args);
+ va_end(args);
+#else
+# error Write something to record error/debugging output
+#endif
+ }
+}
+
+#ifdef DEBUG
+
+void
+dump_configuration(int brief)
+{
+ dprint((8, "asked to dump_configuration"));
+}
+
+
+void
+dump_contexts(void)
+{
+ dprint((8, "asked to dump_contexts"));
+}
+
+
+void
+setup_imap_debug(void)
+{
+ int olddebug;
+
+ olddebug = debug;
+
+ if(debug > 7)
+ ps_global->debug_imap = 4;
+ else if(debug > 6)
+ ps_global->debug_imap = 3;
+ else if(debug > 4)
+ ps_global->debug_imap = 2;
+ else if(debug > 2)
+ ps_global->debug_imap = 1;
+ else
+ ps_global->debug_imap = 0;
+
+ if(ps_global->mail_stream){
+ if(ps_global->debug_imap > 0){
+ mail_debug(ps_global->mail_stream);
+ }
+ else{
+ mail_nodebug(ps_global->mail_stream);
+ }
+ }
+
+ if(debug > 7 && olddebug <= 7)
+ mail_parameters(NULL, SET_TCPDEBUG, (void *) TRUE);
+ else if(debug <= 7 && olddebug > 7 && !ps_global->debugmem)
+ mail_parameters(NULL, SET_TCPDEBUG, (void *) FALSE);
+
+}
+#endif /* DEBUG */
diff --git a/web/src/alpined.d/debug.h b/web/src/alpined.d/debug.h
new file mode 100644
index 00000000..9a44ba81
--- /dev/null
+++ b/web/src/alpined.d/debug.h
@@ -0,0 +1,52 @@
+/*-----------------------------------------------------------------------
+ $Id: debug.h 130 2006-09-22 04:39:36Z mikes@u.washington.edu $
+ -----------------------------------------------------------------------*/
+
+/* ========================================================================
+ * Copyright 2006-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef _WEB_ALPINE_DEBUG_INCLUDED
+#define _WEB_ALPINE_DEBUG_INCLUDED
+
+
+#ifndef DEBUG
+/*
+ * support dprint regardless so we leave at least a few
+ * footsteps in syslog
+ */
+#undef dprint
+#define dprint(x) { output_debug_msg x ; }
+
+/* alpined-scoped debugging level */
+extern int debug;
+
+void output_debug_msg(int, char *fmt, ...);
+#endif
+
+
+/*
+ * Use these to for dprint() debug level arg to force
+ * debug output (typically to syslog())
+ */
+#define SYSDBG 0x8000
+#define SYSDBG_ALERT SYSDBG+1
+#define SYSDBG_ERR SYSDBG+2
+#define SYSDBG_INFO SYSDBG+3
+#define SYSDBG_DEBUG SYSDBG+4
+
+
+/* exported prototypes */
+void debug_init(void);
+void setup_imap_debug(void);
+
+
+#endif /* _WEB_ALPINE_DEBUG_INCLUDED */
diff --git a/web/src/alpined.d/imap.c b/web/src/alpined.d/imap.c
new file mode 100644
index 00000000..6872d085
--- /dev/null
+++ b/web/src/alpined.d/imap.c
@@ -0,0 +1,516 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: imap.c 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $";
+#endif
+
+/* ========================================================================
+ * Copyright 2006-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ imap.c
+ The call back routines for the c-client/imap
+ - handles error messages and other notification
+ - handles prelimirary notification of new mail and expunged mail
+ - prompting for imap server login and password
+
+ ====*/
+
+#include <system.h>
+#include <general.h>
+
+#include "../../../c-client/c-client.h"
+
+#include "../../../pith/state.h"
+#include "../../../pith/debug.h"
+#include "../../../pith/string.h"
+#include "../../../pith/flag.h"
+#include "../../../pith/imap.h"
+#include "../../../pith/status.h"
+#include "../../../pith/osdep/collate.h"
+
+#include "debug.h"
+#include "alpined.h"
+
+
+
+/*
+ * Internal prototypes
+ */
+long imap_seq_exec(MAILSTREAM *, char *,long (*)(MAILSTREAM *, long, void *), void *);
+long imap_seq_exec_append(MAILSTREAM *, long, void *);
+
+
+/*
+ * Exported globals setup by searching functions to tell mm_searched
+ * where to put message numbers that matched the search criteria,
+ * and to allow mm_searched to return number of matches.
+ */
+MAILSTREAM *mm_search_stream;
+
+MM_LIST_S *mm_list_info;
+
+
+
+/*----------------------------------------------------------------------
+ Queue imap log message for display in the message line
+
+ Args: string -- The message
+ errflg -- flag set to 1 if pertains to an error
+
+ Result: Message queued for display
+
+ The c-client/imap reports most of it's status and errors here
+ ---*/
+void
+mm_log(char *string, long errflg)
+{
+ char message[300];
+ char *occurance;
+ int was_capitalized;
+ time_t now;
+ struct tm *tm_now;
+
+ if(errflg == ERROR){
+ dprint((SYSDBG_ERR, "%.*s (%ld)", 128, string, errflg));
+ }
+
+ now = time((time_t *)0);
+ tm_now = localtime(&now);
+
+ dprint((ps_global->debug_imap ? 0 : (errflg == ERROR ? 1 : 2),
+ "IMAP %2.2d:%2.2d:%2.2d %d/%d mm_log %s: %s\n",
+ tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec, tm_now->tm_mon+1,
+ tm_now->tm_mday,
+ (errflg == ERROR)
+ ? "ERROR"
+ : (errflg == WARN)
+ ? "warn"
+ : (errflg == PARSE)
+ ? "parse"
+ : "babble",
+ string));
+
+ if(errflg == ERROR && !strncmp(string, "[TRYCREATE]", 11)){
+ ps_global->try_to_create = 1;
+ return;
+ }
+ else if(ps_global->try_to_create
+ || (sp_dead_stream(ps_global->mail_stream)
+ && (!strncmp(string, "[CLOSED]", 8) || strstr(string, "No-op"))))
+ /*
+ * Don't display if creating new folder OR
+ * warning about a dead stream ...
+ */
+ return;
+
+ /*---- replace all "mailbox" with "folder" ------*/
+ strncpy(message, string, sizeof(message));
+ message[sizeof(message) - 1] = '\0';
+ occurance = srchstr(message, "mailbox");
+ while(occurance) {
+ if(!*(occurance+7) || isspace((unsigned char)*(occurance+7))){
+ was_capitalized = isupper((unsigned char)*occurance);
+ rplstr(occurance, 7, 7, (errflg == PARSE ? "address" : "folder"));
+ if(was_capitalized)
+ *occurance = (errflg == PARSE ? 'A' : 'F');
+ }
+ else
+ occurance += 7;
+
+ occurance = srchstr(occurance, "mailbox");
+ }
+
+ if(errflg == ERROR)
+ ps_global->mm_log_error = 1;
+
+ if(errflg == PARSE || (errflg == ERROR && ps_global->noshow_error)){
+ strncpy(ps_global->c_client_error, message, sizeof(ps_global->c_client_error));
+ ps_global->c_client_error[sizeof(ps_global->c_client_error)-1] = '\0';
+ }
+
+ if(ps_global->noshow_error
+ || (ps_global->noshow_warn && errflg == WARN)
+ || !(errflg == ERROR || errflg == WARN))
+ return; /* Only care about errors; don't print when asked not to */
+
+ /*---- Display the message ------*/
+ q_status_message((errflg == ERROR) ? (SM_ORDER | SM_DING) : SM_ORDER,
+ 3, 5, message);
+ if(errflg == ERROR){
+ strncpy(ps_global->last_error, message, sizeof(ps_global->last_error));
+ ps_global->last_error[sizeof(ps_global->last_error)-1] = '\0';
+ }
+}
+
+
+
+/*----------------------------------------------------------------------
+ recieve notification from IMAP
+
+ Args: stream -- Mail stream message is relavant to
+ string -- The message text
+ errflag -- Set if it is a serious error
+
+ Result: message displayed in status line
+
+ The facility is for general notices, such as connection to server;
+ server shutting down etc... It is used infrequently.
+ ----------------------------------------------------------------------*/
+void
+mm_notify(MAILSTREAM *stream, char *string, long errflag)
+{
+ if(errflag == ERROR){
+ dprint((SYSDBG_ERR, "mm_notify: %s (%ld)", string, errflag));
+ }
+
+ /* be sure to log the message... */
+#ifdef DEBUG
+ if(ps_global->debug_imap)
+ dprint((0, "IMAP mm_notify %s : %s (%s) : %s\n",
+ (!errflag) ? "NIL" :
+ (errflag == ERROR) ? "error" :
+ (errflag == WARN) ? "warning" :
+ (errflag == BYE) ? "bye" : "unknown",
+ (stream && stream->mailbox) ? stream->mailbox : "-no folder-",
+ (stream && stream == sp_inbox_stream()) ? "inboxstream" :
+ (stream && stream == ps_global->mail_stream) ? "mailstream" :
+ (stream) ? "abookstream?" : "nostream",
+ string));
+#endif
+
+ strncpy(ps_global->last_error, string, 500);
+ ps_global->last_error[499] = '\0';
+
+ /*
+ * Then either set special bits in the pine struct or
+ * display the message if it's tagged as an "ALERT" or
+ * its errflag > NIL (i.e., WARN, or ERROR)
+ */
+ if(errflag == BYE){
+ if(stream == ps_global->mail_stream){
+ if(sp_dead_stream(ps_global->mail_stream))
+ return;
+ else
+ sp_set_dead_stream(ps_global->mail_stream, 1);
+ }
+ else if(stream && stream == sp_inbox_stream()){
+ if(sp_dead_stream(stream))
+ return;
+ else
+ sp_set_dead_stream(stream, 1);
+ }
+ }
+ else if(!strncmp(string, "[TRYCREATE]", 11))
+ ps_global->try_to_create = 1;
+ else if(!strncmp(string, "[ALERT]", 7))
+ q_status_message2(SM_MODAL, 3, 3, "Alert received while accessing \"%s\": %s",
+ (stream && stream->mailbox)
+ ? stream->mailbox : "-no folder-",
+ rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf, SIZEOF_20KBUF, string));
+ else if(!strncmp(string, "[UNSEEN ", 8)){
+ char *p;
+ long n = 0;
+
+ for(p = string + 8; isdigit(*p); p++)
+ n = (n * 10) + (*p - '0');
+
+ sp_set_first_unseen(ps_global->mail_stream, n);
+ }
+ else if(!strncmp(string, "[READ-ONLY]", 11)
+ && !(stream && stream->mailbox && IS_NEWS(stream)))
+ q_status_message2(SM_ORDER | SM_DING, 3, 3, "%s : %s",
+ (stream && stream->mailbox)
+ ? stream->mailbox : "-no folder-",
+ string + 11);
+ else if(errflag && (errflag == WARN || errflag == ERROR))
+ q_status_message(SM_ORDER | ((errflag == ERROR) ? SM_DING : 0),
+ 3, 6, ps_global->last_error);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Do work of getting login and password from user for IMAP login
+
+ Args: mb -- The mail box property struct
+ user -- Buffer to return the user name in
+ passwd -- Buffer to return the passwd in
+ trial -- The trial number or number of attempts to login
+
+ Result: username and password passed back to imap
+ ----*/
+void
+mm_login_work(NETMBX *mb, char *user, char *pwd, long trial, char *usethisprompt, char *altuserforcache)
+{
+ STRLIST_S hostlist[2];
+ NETMBX cmb;
+ int l;
+
+ pwd[0] = '\0';
+
+ if((l = strlen(mb->orighost)) > 0 && l < CRED_REQ_SIZE)
+ strcpy(peCredentialRequestor, mb->orighost);
+
+ if(trial){ /* one shot only! */
+ user[0] = '\0';
+ peCredentialError = 1;
+ return;
+ }
+
+#if 0
+ if(ps_global && ps_global->anonymous) {
+ /*------ Anonymous login mode --------*/
+ if(trial < 1) {
+ strcpy(user, "anonymous");
+ sprintf(pwd, "%s@%s", ps_global->VAR_USER_ID,
+ ps_global->hostname);
+ }
+ else
+ user[0] = pwd[0] = '\0';
+
+ return;
+ }
+#endif
+
+#if WEB_REQUIRE_SECURE_IMAP
+ /* we *require* secure authentication */
+ if(!(mb->sslflag || mb->tlsflag) && strcmp("localhost",mb->host)){
+ user[0] = pwd[0] = '\0';
+ return;
+ }
+#endif
+
+ /*
+ * heavily paranoid about offering password to server
+ * that the users hasn't also indicated the remote user
+ * name
+ */
+ if(*mb->user){
+ strcpy(user, mb->user);
+ }
+ else if(ps_global->prc
+ && ps_global->prc->name
+ && mail_valid_net_parse(ps_global->prc->name,&cmb)
+ && cmb.user){
+ strcpy(user, cmb.user);
+ }
+ else{
+ /*
+ * don't blindly offer user/pass
+ */
+ user[0] = pwd[0] = '\0';
+ return;
+ }
+
+ /*
+ * set up host list for sybil servers...
+ */
+ hostlist[0].name = mb->host;
+ if(mb->orighost[0] && strucmp(mb->host, mb->orighost)){
+ hostlist[0].next = &hostlist[1];
+ hostlist[1].name = mb->orighost;
+ hostlist[1].next = NULL;
+ }
+ else
+ hostlist[0].next = NULL;
+
+ /* try last working password associated with this host. */
+ if(!imap_get_passwd(mm_login_list, pwd, user, hostlist, (mb->sslflag || mb->tlsflag))){
+ peNoPassword = 1;
+ user[0] = pwd[0] = '\0';
+ }
+}
+
+
+
+/*----------------------------------------------------------------------
+ Receive notification of an error writing to disk
+
+ Args: stream -- The stream the error occured on
+ errcode -- The system error code (errno)
+ serious -- Flag indicating error is serious (mail may be lost)
+
+Result: If error is non serious, the stream is marked as having an error
+ and deletes are disallowed until error clears
+ If error is serious this returns with syslogging if possible
+ ----*/
+long
+mm_diskerror (MAILSTREAM *stream, long errcode, long serious)
+{
+ if(!serious && stream == ps_global->mail_stream) {
+ sp_set_io_error_on_stream(ps_global->mail_stream, 1);
+ }
+
+ dprint((SYSDBG_ERR, "mm_diskerror: mailbox: %s, errcode: %ld, serious: %ld\n",
+ (stream && stream->mailbox) ? stream->mailbox : "", errcode, serious));
+
+ return(1);
+}
+
+
+/*
+ * alpine_tcptimeout - C-client callback to handle tcp-related timeouts.
+ */
+long
+alpine_tcptimeout(long elapsed, long sincelast)
+{
+ long rv = 1L; /* keep trying by default */
+
+ dprint((SYSDBG_INFO, "tcptimeout: waited %s seconds\n", long2string(elapsed)));
+
+ if(elapsed > WP_TCP_TIMEOUT){
+ dprint((SYSDBG_ERR, "tcptimeout: BAIL after %s seconds\n", long2string(elapsed)));
+ rv = 0L;
+ }
+
+ return(rv);
+}
+
+
+/*
+ * C-client callback to handle SSL/TLS certificate validation failures
+ *
+ * Returning 0 means error becomes fatal
+ * Non-zero means certificate problem is ignored and SSL session is
+ * established
+ *
+ * We remember the answer and won't re-ask for subsequent open attempts to
+ * the same hostname.
+ */
+long
+alpine_sslcertquery(char *reason, char *host, char *cert)
+{
+ static char buf[256];
+ STRLIST_S *p;
+
+ for(p = peCertHosts; p; p = p->next)
+ if(!strucmp(p->name, host))
+ return(1);
+
+ peCertQuery = 1;
+ snprintf(peCredentialRequestor, CRED_REQ_SIZE, "%s++%s", host ? host : "?", reason ? reason : "UNKNOWN");
+ q_status_message(SM_ORDER, 0, 3, "SSL Certificate Problem");
+ dprint((SYSDBG_INFO, "sslcertificatequery: host=%s reason=%s cert=%s\n",
+ host ? host : "?", reason ? reason : "?",
+ cert ? cert : "?"));
+ return(0);
+}
+
+
+/*
+ * C-client callback to handle SSL/TLS certificate validation failures
+ */
+void
+alpine_sslfailure(char *host, char *reason, unsigned long flags)
+{
+ peCertFailure = 1;
+ snprintf(peCredentialRequestor, CRED_REQ_SIZE, "%s++%s", host ? host : "?", reason ? reason : "UNKNOWN");
+ q_status_message1(SM_ORDER, 0, 3, "SSL Certificate Failure: %s", reason ? reason : "?");
+ dprint((SYSDBG_INFO, "SSL Invalid Cert (%s) : %s", host, reason));
+}
+
+
+
+/*----------------------------------------------------------------------
+ This can be used to prevent the flickering of the check_cue char
+ caused by numerous (5000+) fetches by c-client. Right now, the only
+ practical use found is newsgroup subsciption.
+
+ check_cue_display will check if this global is set, and won't clear
+ the check_cue_char if set.
+ ----*/
+void
+set_read_predicted(int i)
+{
+}
+
+/*----------------------------------------------------------------------
+ Exported method to display status of mail check
+
+ Args: putstr -- should be NO LONGER THAN 2 bytes
+
+ Result: putstr displayed at upper-left-hand corner of screen
+ ----*/
+void
+check_cue_display(char *putstr)
+{
+}
+
+
+
+void
+alpine_set_passwd(char *user, char *passwd, char *host, int altflag)
+{
+ STRLIST_S hostlist[1];
+
+ hostlist[0].name = host;
+ hostlist[0].next = NULL;
+
+ imap_set_passwd(&mm_login_list, passwd, user, hostlist, altflag, 1, 0);
+}
+
+
+void
+alpine_clear_passwd(char *user, char *host)
+{
+ MMLOGIN_S **lp, *l;
+ STRLIST_S hostlist[1];
+
+ hostlist[0].name = host;
+ hostlist[0].next = NULL;
+
+ for(lp = &mm_login_list; *lp; lp = &(*lp)->next)
+ if(imap_same_host((*lp)->hosts, hostlist)
+ && (!*user || !strcmp(user, (*lp)->user))){
+ l = *lp;
+ *lp = (*lp)->next;
+
+ if(l->user)
+ fs_give((void **) &l->user);
+
+ free_strlist(&l->hosts);
+
+ if(l->passwd){
+ char *p = l->passwd;
+
+ while(*p)
+ *p++ = '\0';
+ }
+
+ fs_give((void **) &l);
+
+ break;
+ }
+}
+
+
+int
+alpine_have_passwd(char *user, char *host, int altflag)
+{
+ STRLIST_S hostlist[1];
+
+ hostlist[0].name = host;
+ hostlist[0].next = NULL;
+
+ return(imap_get_passwd(mm_login_list, NULL, user, hostlist, altflag));
+}
+
+
+char *
+alpine_get_user(char *host)
+{
+ STRLIST_S hostlist[1];
+
+ hostlist[0].name = host;
+ hostlist[0].next = NULL;
+
+ return(imap_get_user(mm_login_list, hostlist));
+}
diff --git a/web/src/alpined.d/imap.h b/web/src/alpined.d/imap.h
new file mode 100644
index 00000000..024e1781
--- /dev/null
+++ b/web/src/alpined.d/imap.h
@@ -0,0 +1,23 @@
+/*-----------------------------------------------------------------------
+ $Id: imap.h 82 2006-07-12 23:36:59Z mikes@u.washington.edu $
+ -----------------------------------------------------------------------*/
+
+#ifndef _WEB_ALPINE_IMAP_INCLUDED
+#define _WEB_ALPINE_IMAP_INCLUDED
+
+
+#include "../../../pith/imap.h"
+
+
+/* exported protoypes */
+long alpine_tcptimeout(long, long);
+long alpine_sslcertquery(char *, char *, char *);
+void alpine_sslfailure(char *, char *, unsigned long);
+void alpine_set_passwd(char *, char *, char *, int);
+void alpine_clear_passwd(char *, char *);
+int alpine_have_passwd(char *, char *, int);
+char *alpine_get_user(char *, int);
+
+
+
+#endif /* _WEB_ALPINE_IMAP_INCLUDED */
diff --git a/web/src/alpined.d/ldap.c b/web/src/alpined.d/ldap.c
new file mode 100644
index 00000000..3e6bf99f
--- /dev/null
+++ b/web/src/alpined.d/ldap.c
@@ -0,0 +1,241 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: ldap.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
+#endif
+
+/* ========================================================================
+ * Copyright 2006-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "../../../c-client/c-client.h"
+
+#include "../../../pith/state.h"
+#include "../../../pith/debug.h"
+#include "../../../pith/adrbklib.h"
+#include "../../../pith/ldap.h"
+
+#include "ldap.h"
+
+
+#ifdef ENABLE_LDAP
+
+int
+ldap_addr_select(ps, ac, result, style, wp_err, srchstr)
+ struct pine *ps;
+ ADDR_CHOOSE_S *ac;
+ LDAP_CHOOSE_S **result;
+ LDAPLookupStyle style;
+ WP_ERR_S *wp_err;
+ char *srchstr;
+{
+ LDAP_SERV_RES_S *res_list, *tmp_rl;
+ LDAPMessage *e, *tmp_e;
+ char **mail = NULL, *a;
+ int got_n_entries = 0, retval = -5;
+ BerElement *ber;
+
+ dprint((7, "ldap_addr_select, srchstr: %s", srchstr));
+ for(res_list = ac->res_head; res_list; res_list = res_list->next){
+ tmp_rl = res_list;
+ for(e = ldap_first_entry(res_list->ld, res_list->res);
+ e != NULL;
+ e = ldap_next_entry(res_list->ld, e)){
+ tmp_e = e;
+ got_n_entries++;
+ }
+ }
+ if(got_n_entries == 1){
+ for(a = ldap_first_attribute(tmp_rl->ld, tmp_e, &ber);
+ a != NULL;
+ a = ldap_next_attribute(tmp_rl->ld, tmp_e, ber)){
+ if(strcmp(a, tmp_rl->info_used->mailattr) == 0){
+ mail = ldap_get_values(tmp_rl->ld, tmp_e, a);
+ break;
+ }
+ }
+ if(mail && mail[0] && mail[0][0]){
+ retval = 0;
+ if(result){
+ (*result) =
+ (LDAP_CHOOSE_S *)fs_get(sizeof(LDAP_CHOOSE_S));
+ (*result)->ld = tmp_rl->ld;
+ (*result)->selected_entry = tmp_e;
+ (*result)->info_used = tmp_rl->info_used;
+ (*result)->serv = tmp_rl->serv;
+ }
+ }
+ else{
+ retval = -2;
+ }
+ }
+ else
+ retval = -3;
+
+ return(retval);
+}
+
+
+char *
+peLdapPname(mailbox, host)
+ char *mailbox;
+ char *host;
+{
+ char *retstr = NULL;
+ char adrstr[1024], **cn;
+ int ecnt;
+ CUSTOM_FILT_S *filter;
+ WP_ERR_S wp_err;
+ LDAP_CHOOSE_S *winning_e = NULL;
+ LDAP_SERV_RES_S *results = NULL;
+ LDAP_SERV_RES_S *trl;
+ LDAPMessage *e;
+
+ sprintf(adrstr, "(mail=%.500s@%.500s)", mailbox, host);
+ filter = (CUSTOM_FILT_S *)fs_get(sizeof(CUSTOM_FILT_S));
+ filter->filt = cpystr(adrstr);
+ filter->combine = 0;
+ memset(&wp_err, 0, sizeof(wp_err));
+ wpldap_global->query_no++;
+ if(wpldap_global->ldap_search_list){
+ wpldap_global->ldap_search_list =
+ free_wpldapres(wpldap_global->ldap_search_list);
+ }
+ ldap_lookup_all("", 0, 0, AlwaysDisplay, filter, &winning_e,
+ &wp_err, &results);
+ if(filter){
+ fs_give((void **)&filter->filt);
+ fs_give((void **)&filter);
+ }
+ if(wpldap_global->ldap_search_list){
+ trl = wpldap_global->ldap_search_list->reslist;
+ for(ecnt = 0, e = ldap_first_entry(trl->ld, trl->res);
+ e != NULL; e = ldap_next_entry(trl->ld, e), ecnt++);
+ if(ecnt == 1) { /* found the one true name */
+ e = ldap_first_entry(trl->ld, trl->res);
+ peLdapEntryParse(trl, e, &cn, NULL, NULL, NULL,
+ NULL, NULL);
+ if(cn){
+ retstr = cpystr(cn[0]);
+ ldap_value_free(cn);
+ }
+ }
+ }
+ return(retstr);
+}
+
+int
+peLdapEntryParse(trl, e, ret_cn, ret_org, ret_unit,
+ ret_title, ret_mail, ret_sn)
+ LDAP_SERV_RES_S *trl;
+ LDAPMessage *e;
+ char ***ret_cn;
+ char ***ret_org;
+ char ***ret_unit;
+ char ***ret_title;
+ char ***ret_mail;
+ char ***ret_sn;
+{
+ char *a, **cn, **org, **unit, **title, **mail, **sn;
+ BerElement *ber;
+
+ cn = org = title = unit = mail = sn = NULL;
+
+ for(a = ldap_first_attribute(trl->ld, e, &ber);
+ a != NULL;
+ a = ldap_next_attribute(trl->ld, e, ber)){
+ dprint((9, " %s", a));
+ if(strcmp(a, trl->info_used->cnattr) == 0){
+ if(!cn)
+ cn = ldap_get_values(trl->ld, e, a);
+
+ if(cn && !(cn[0] && cn[0][0])){
+ ldap_value_free(cn);
+ cn = NULL;
+ }
+ }
+ else if(strcmp(a, trl->info_used->mailattr) == 0){
+ if(!mail)
+ mail = ldap_get_values(trl->ld, e, a);
+ }
+ else if(strcmp(a, "o") == 0){
+ if(!org)
+ org = ldap_get_values(trl->ld, e, a);
+ }
+ else if(strcmp(a, "ou") == 0){
+ if(!unit)
+ unit = ldap_get_values(trl->ld, e, a);
+ }
+ else if(strcmp(a, "title") == 0){
+ if(!title)
+ title = ldap_get_values(trl->ld, e, a);
+ }
+
+ our_ldap_memfree(a);
+ }
+
+ if(!cn){
+ for(a = ldap_first_attribute(trl->ld, e, &ber);
+ a != NULL;
+ a = ldap_next_attribute(trl->ld, e, ber)){
+
+ if(strcmp(a, trl->info_used->snattr) == 0){
+ if(!sn)
+ sn = ldap_get_values(trl->ld, e, a);
+
+ if(sn && !(sn[0] && sn[0][0])){
+ ldap_value_free(sn);
+ sn = NULL;
+ }
+ }
+ our_ldap_memfree(a);
+ }
+ }
+ if(ret_cn)
+ (*ret_cn) = cn;
+ else if(cn) ldap_value_free(cn);
+ if(ret_org)
+ (*ret_org) = org;
+ else if(org) ldap_value_free(org);
+ if(ret_unit)
+ (*ret_unit) = unit;
+ else if(unit) ldap_value_free(unit);
+ if(ret_title)
+ (*ret_title) = title;
+ else if(title) ldap_value_free(title);
+ if(ret_mail)
+ (*ret_mail) = mail;
+ else if(mail) ldap_value_free(mail);
+ if(ret_sn)
+ (*ret_sn) = sn;
+ else if(sn) ldap_value_free(sn);
+
+ return 0;
+}
+
+WPLDAPRES_S *
+free_wpldapres(wpldapr)
+ WPLDAPRES_S *wpldapr;
+{
+ WPLDAPRES_S *tmp1, *tmp2;
+
+ for(tmp1 = wpldapr; tmp1; tmp1 = tmp2){
+ tmp2 = tmp1->next;
+ if(tmp1->str)
+ fs_give((void **)&tmp1->str);
+ if(tmp1->reslist)
+ free_ldap_result_list(&tmp1->reslist);
+ }
+ fs_give((void **)&wpldapr);
+ return(NULL);
+}
+#endif /* ENABLE_LDAP */
diff --git a/web/src/alpined.d/ldap.h b/web/src/alpined.d/ldap.h
new file mode 100644
index 00000000..2ada2908
--- /dev/null
+++ b/web/src/alpined.d/ldap.h
@@ -0,0 +1,48 @@
+/*-----------------------------------------------------------------------
+ $Id: ldap.h 5 2006-01-04 17:53:54Z hubert $
+ -----------------------------------------------------------------------*/
+
+/* ========================================================================
+ * Copyright 2006-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef _WEB_ALPINE_LDAP_INCLUDED
+#define _WEB_ALPINE_LDAP_INCLUDED
+
+
+#ifdef ENABLE_LDAP
+
+#include "../../../pith/ldap.h"
+
+typedef struct wpldapres {
+ char *str;
+ LDAP_SERV_RES_S *reslist;
+ struct wpldapres *next;
+} WPLDAPRES_S;
+
+typedef struct wpldap {
+ int query_no;
+ WPLDAPRES_S *ldap_search_list;
+} WPLDAP_S;
+
+
+extern WPLDAP_S *wpldap_global;
+
+
+char *peLdapPname(char *, char *);
+int peLdapEntryParse(LDAP_SERV_RES_S *, LDAPMessage *,
+ char ***, char ***, char ***, char ***,
+ char ***, char ***);
+WPLDAPRES_S *free_wpldapres(WPLDAPRES_S *);
+
+#endif /* ENABLE_LDAP */
+
+#endif /* _WEB_ALPINE_LDAP_INCLUDED */
diff --git a/web/src/alpined.d/remote.c b/web/src/alpined.d/remote.c
new file mode 100644
index 00000000..3269a978
--- /dev/null
+++ b/web/src/alpined.d/remote.c
@@ -0,0 +1,78 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: remote.c 101 2006-08-10 22:53:04Z mikes@u.washington.edu $";
+#endif
+
+/* ========================================================================
+ * Copyright 2006-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "../../../c-client/c-client.h"
+
+#include "../../../pith/remote.h"
+#include "../../../pith/msgno.h"
+#include "../../../pith/filter.h"
+#include "../../../pith/util.h"
+#include "../../../pith/debug.h"
+#include "../../../pith/osdep/collate.h"
+
+
+/*
+ * Internal prototypes
+ */
+
+int
+rd_prompt_about_forged_remote_data(reason, rd, extra)
+ int reason;
+ REMDATA_S *rd;
+ char *extra;
+{
+ char tmp[2000];
+ char *unknown = "<unknown>";
+ char *foldertype, *foldername, *special;
+
+ /*
+ * Since we're web based the user doesn't have much recourse in the event of one of these
+ * weird errors, so we just report what happened and forge ahead
+ */
+
+ foldertype = (rd && rd->t.i.special_hdr && !strucmp(rd->t.i.special_hdr, REMOTE_ABOOK_SUBTYPE))
+ ? "address book"
+ : (rd && rd->t.i.special_hdr && !strucmp(rd->t.i.special_hdr, REMOTE_PINERC_SUBTYPE))
+ ? "configuration"
+ : "data";
+ foldername = (rd && rd->rn) ? rd->rn : unknown;
+ special = (rd && rd->t.i.special_hdr) ? rd->t.i.special_hdr : unknown;
+
+ dprint((1, "rd_check_out_forged_remote_data: reason: %d, type: $s, name: %s",
+ reason, foldertype ? foldertype : "?", foldername ? foldername : "?"));
+
+ if(rd && rd->flags & USER_SAID_NO)
+ return(-1);
+
+ if(reason == -2){
+ snprintf(tmp, sizeof(tmp), _("Missing \"%s\" header in remote pinerc"), special);
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+ else if(reason == -1){
+ snprintf(tmp, sizeof(tmp), _("Unexpected \"Received\" header in remote pinerc"));
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+ else if(reason >= 0){
+ snprintf(tmp, sizeof(tmp), _("Unexpected value \"%s: %s\" in remote pinerc"), special, (extra && *extra) ? extra : "?");
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+
+ rd->flags |= USER_SAID_YES;
+ return(1);
+}
diff --git a/web/src/alpined.d/signal.c b/web/src/alpined.d/signal.c
new file mode 100644
index 00000000..3bdb7b41
--- /dev/null
+++ b/web/src/alpined.d/signal.c
@@ -0,0 +1,280 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: signal.c 91 2006-07-28 19:02:07Z mikes@u.washington.edu $";
+#endif
+
+/* ========================================================================
+ * Copyright 2006-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "../../../c-client/c-client.h"
+
+#include "../../../pith/conf.h"
+#include "../../../pith/status.h"
+#include "../../../pith/signal.h"
+#include "../../../pith/debug.h"
+#include "../../../pith/adrbklib.h"
+#include "../../../pith/remote.h"
+#include "../../../pith/imap.h"
+
+#include "alpined.h"
+#include "debug.h"
+
+
+static int cleanup_called_from_sig_handler;
+
+static RETSIGTYPE hup_signal(int);
+static RETSIGTYPE term_signal(int);
+static RETSIGTYPE auger_in_signal(int);
+int fast_clean_up();
+void end_signals(int);
+#if defined(DEBUG) && defined(SIGUSR1) && defined(SIGUSR2)
+static RETSIGTYPE usr1_signal(int);
+static RETSIGTYPE usr2_signal(int);
+#endif
+
+
+/*----------------------------------------------------------------------
+ Install handlers for all the signals we care to catch
+ ----------------------------------------------------------------------*/
+void
+init_signals(void)
+{
+ /* prepare for unexpected exit */
+ signal(SIGHUP, hup_signal);
+ signal(SIGTERM, term_signal);
+
+ /* prepare for unforseen problems */
+ signal(SIGILL, auger_in_signal);
+ signal(SIGTRAP, auger_in_signal);
+#ifdef SIGEMT
+ signal(SIGEMT, auger_in_signal);
+#endif
+ signal(SIGBUS, auger_in_signal);
+ signal(SIGSEGV, auger_in_signal);
+ signal(SIGSYS, auger_in_signal);
+
+#if defined(DEBUG) && defined(SIGUSR1) && defined(SIGUSR2)
+ /* Set up SIGUSR2 to {in,de}crement debug level */
+ signal(SIGUSR1, usr1_signal);
+ signal(SIGUSR2, usr2_signal);
+#endif
+}
+
+
+/*----------------------------------------------------------------------
+ handle hang up signal -- SIGHUP
+
+Not much to do. Rely on periodic mail file check pointing.
+ ----------------------------------------------------------------------*/
+static RETSIGTYPE
+hup_signal(int sig)
+{
+ end_signals(1);
+ cleanup_called_from_sig_handler = 1;
+
+ while(!fast_clean_up())
+ sleep(1);
+
+ if(peSocketName) /* clean up unix domain socket */
+ (void) unlink(peSocketName);
+
+ exceptional_exit("SIGHUP received", 0);
+}
+
+
+/*----------------------------------------------------------------------
+ handle terminate signal -- SIGTERM
+
+Not much to do. Rely on periodic mail file check pointing.
+ ----------------------------------------------------------------------*/
+static RETSIGTYPE
+term_signal(int sig)
+{
+ end_signals(1);
+ cleanup_called_from_sig_handler = 1;
+
+ while(!fast_clean_up())
+ sleep(1);
+
+ if(peSocketName) /* clean up unix domain socket */
+ (void) unlink(peSocketName);
+
+ exceptional_exit("SIGTERM received", 0);
+}
+
+
+/*----------------------------------------------------------------------
+ Handle signals caused by aborts -- SIGSEGV, SIGILL, etc
+
+Call panic which cleans up tty modes and then core dumps
+ ----------------------------------------------------------------------*/
+static RETSIGTYPE
+auger_in_signal(int sig)
+{
+ end_signals(1);
+
+ if(peSocketName) /* clean up unix domain socket */
+ (void) unlink(peSocketName);
+
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Abort: signal %d", sig);
+ panic(tmp_20k_buf); /* clean up and get out */
+ exit(-1); /* in case panic doesn't kill us */
+}
+
+
+/*----------------------------------------------------------------------
+ Handle cleaning up mail streams and tty modes...
+Not much to do. Rely on periodic mail file check pointing. Don't try
+cleaning up screen or flushing output since stdout is likely already
+gone. To be safe, though, we'll at least restore the original tty mode.
+Also delete any remnant _DATAFILE_ from sending-filters.
+ ----------------------------------------------------------------------*/
+int
+fast_clean_up(void)
+{
+ int i;
+ MAILSTREAM *m;
+
+ if(ps_global->expunge_in_progress)
+ return(0);
+
+ /*
+ * This gets rid of temporary cache files for remote addrbooks.
+ */
+ completely_done_with_adrbks();
+
+ /*
+ * This flushes out deferred changes and gets rid of temporary cache
+ * files for remote config files.
+ */
+ if(ps_global->prc){
+ if(ps_global->prc->outstanding_pinerc_changes)
+ write_pinerc(ps_global, Main,
+ cleanup_called_from_sig_handler ? WRP_NOUSER : WRP_NONE);
+
+ if(ps_global->prc->rd)
+ rd_close_remdata(&ps_global->prc->rd);
+
+ free_pinerc_s(&ps_global->prc);
+ }
+
+ /* as does this */
+ if(ps_global->post_prc){
+ if(ps_global->post_prc->outstanding_pinerc_changes)
+ write_pinerc(ps_global, Post,
+ cleanup_called_from_sig_handler ? WRP_NOUSER : WRP_NONE);
+
+ if(ps_global->post_prc->rd)
+ rd_close_remdata(&ps_global->post_prc->rd);
+
+ free_pinerc_s(&ps_global->post_prc);
+ }
+
+ /*
+ * Can't figure out why this section is inside the ifdef, but no
+ * harm leaving it that way, I guess.
+ */
+#if !defined(DOS) && !defined(OS2)
+ for(i = 0; i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ if(m && !m->lock)
+ pine_mail_actually_close(m);
+ }
+#endif /* !DOS */
+
+ imap_flush_passwd_cache(TRUE);
+
+ if(peSocketName) /* clean up unix domain socket */
+ (void) unlink(peSocketName);
+
+ dprint((1, "done with fast_clean_up\n"));
+ return(1);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Return all signal handling back to normal
+ ----------------------------------------------------------------------*/
+void
+end_signals(int blockem)
+{
+#ifndef SIG_ERR
+#define SIG_ERR (RETSIGTYPE (*)())-1
+#endif
+ if(signal(SIGILL, blockem ? SIG_IGN : SIG_DFL) != SIG_ERR){
+ signal(SIGTRAP, blockem ? SIG_IGN : SIG_DFL);
+#ifdef SIGEMT
+ signal(SIGEMT, blockem ? SIG_IGN : SIG_DFL);
+#endif
+ signal(SIGBUS, blockem ? SIG_IGN : SIG_DFL);
+ signal(SIGSEGV, blockem ? SIG_IGN : SIG_DFL);
+ signal(SIGSYS, blockem ? SIG_IGN : SIG_DFL);
+ signal(SIGHUP, blockem ? SIG_IGN : SIG_DFL);
+ signal(SIGTERM, blockem ? SIG_IGN : SIG_DFL);
+ signal(SIGINT, blockem ? SIG_IGN : SIG_DFL);
+ }
+}
+
+
+#if defined(DEBUG) && defined(SIGUSR1) && defined(SIGUSR2)
+/*----------------------------------------------------------------------
+ handle -- SIGUSR1
+
+ Increment debug level
+ ----------------------------------------------------------------------*/
+static RETSIGTYPE
+usr1_signal(int sig)
+{
+ if(debug < 11)
+ debug++;
+
+ setup_imap_debug();
+
+ dprint((SYSDBG_INFO, "Debug level now %d", debug));
+}
+
+/*----------------------------------------------------------------------
+ handle -- SIGUSR2
+
+ Decrement debug level
+ ----------------------------------------------------------------------*/
+static RETSIGTYPE
+usr2_signal(int sig)
+{
+ if(debug > 0)
+ debug--;
+
+ setup_imap_debug();
+
+ dprint((SYSDBG_INFO, "Debug level now %d", debug));
+
+}
+#endif
+
+
+/*
+ * Command interrupt support.
+ */
+int
+intr_handling_on(void)
+{
+ return 0;
+}
+
+
+void
+intr_handling_off(void)
+{
+}
diff --git a/web/src/alpined.d/signal.h b/web/src/alpined.d/signal.h
new file mode 100644
index 00000000..3ae6bd50
--- /dev/null
+++ b/web/src/alpined.d/signal.h
@@ -0,0 +1,15 @@
+/*-----------------------------------------------------------------------
+ $Id: signal.h 82 2006-07-12 23:36:59Z mikes@u.washington.edu $
+ -----------------------------------------------------------------------*/
+
+#ifndef _WEB_ALPINE_SIGNAL_INCLUDED
+#define _WEB_ALPINE_SIGNAL_INCLUDED
+
+
+/* exported protoypes */
+void init_signals(void);
+
+
+
+
+#endif /* _WEB_ALPINE_SIGNAL_INCLUDED */
diff --git a/web/src/alpined.d/status.c b/web/src/alpined.d/status.c
new file mode 100644
index 00000000..3c546d6f
--- /dev/null
+++ b/web/src/alpined.d/status.c
@@ -0,0 +1,78 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: status.c 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $";
+#endif
+
+/* ========================================================================
+ * Copyright 2006-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ status.c
+ Functions that manage the status line (third from the bottom)
+ - put messages on the queue to be displayed
+ - display messages on the queue with timers
+ - check queue to figure out next timeout
+ - prompt for yes/no type of questions
+ ====*/
+
+#include <system.h>
+#include <general.h>
+
+#include "../../../pith/status.h"
+#include "../../../pith/helptext.h"
+#include "../../../pith/debug.h"
+#include "../../../pith/string.h"
+
+#include "alpined.h"
+
+
+
+
+/*----------------------------------------------------------------------
+ Put a message for the status line on the queue
+ ----------*/
+void
+q_status_message(int flags, int min_time, int max_time, char *message)
+{
+ if(!(flags & SM_INFO))
+ sml_addmsg(0, message);
+}
+
+
+/*----------------------------------------------------------------------
+ Time remaining for current message's minimum display
+ ----*/
+int
+status_message_remaining(void)
+{
+ return(0);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Update status line, clearing or displaying a message
+----------------------------------------------------------------------*/
+int
+display_message(UCS command)
+{
+ return(0);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Display all the messages on the queue as quickly as possible
+ ----*/
+void
+flush_status_messages(int skip_last_pause)
+{
+}
diff --git a/web/src/alpined.d/stubs.c b/web/src/alpined.d/stubs.c
new file mode 100644
index 00000000..299c7c81
--- /dev/null
+++ b/web/src/alpined.d/stubs.c
@@ -0,0 +1,169 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: stubs.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/* ========================================================================
+ * Copyright 2006-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "../../../c-client/c-client.h"
+
+#include "../../../pith/takeaddr.h"
+#include "../../../pith/ldap.h"
+#include "../../../pith/debug.h"
+#include "../../../pith/osdep/coredump.h"
+
+#include "alpined.h"
+
+
+/* let cleanup calls know we're screwed */
+static int in_panic = 0;
+
+/* input timeout */
+static int input_timeout = 0;
+
+/* time of last user-initiated newmail check */
+static time_t time_of_input;
+
+
+void
+peMarkInputTime(void)
+{
+ time_of_input = time((time_t *)0);
+}
+
+
+/********* ../../../pith/newmail.c stub **********/
+time_t
+time_of_last_input()
+{
+ return(time_of_input);
+}
+
+
+/********* ../../../pith/conf.c stub **********/
+int
+set_input_timeout(t)
+ int t;
+{
+ int old_t = input_timeout;
+
+ input_timeout = t;
+ return(old_t);
+}
+
+
+int
+get_input_timeout()
+{
+ return(input_timeout);
+}
+
+
+int
+unexpected_pinerc_change()
+{
+ dprint((1, "Unexpected pinerc change"));
+ return(0); /* always overwrite */
+}
+
+/********* ../../../pith/mailcap.c stub **********/
+int
+exec_mailcap_test_cmd(cmd)
+ char *cmd;
+{
+ return(-1); /* never succeeds on web server */
+}
+
+
+/****** various other stuff ******/
+/*----------------------------------------------------------------------
+ panic - call on detected programmatic errors to exit pine
+
+ Args: message -- message to record in debug file and to be printed for user
+
+ Result: The various tty modes are restored
+ If debugging is active a core dump will be generated
+ Exits Pine
+
+ This is also called from imap routines and fs_get and fs_resize.
+ ----*/
+void
+panic(message)
+ char *message;
+{
+ in_panic = 1;
+
+ syslog(LOG_ERR, message); /* may not work, but try */
+
+#if 0
+ if(ps_global)
+ peDestroyUserContext(&ps_global);
+#endif
+
+#ifdef DEBUG
+ if(debug > 1)
+ coredump(); /*--- If we're debugging get a core dump --*/
+#endif
+
+ exit(-1);
+ fatal("ffo"); /* BUG -- hack to get fatal out of library in right order*/
+}
+
+
+/*----------------------------------------------------------------------
+ panicking - called to test whether we're sunk
+
+ Args: none
+
+ ----*/
+int
+panicking()
+{
+ return(in_panic);
+}
+
+
+/*----------------------------------------------------------------------
+ exceptional_exit - called to exit under unusual conditions (with no core)
+
+ Args: message -- message to record in debug file and to be printed for user
+ ev -- exit value
+
+ ----*/
+void
+exceptional_exit(message, ev)
+ char *message;
+ int ev;
+{
+ syslog(LOG_ALERT, message);
+ exit(ev);
+}
+
+
+/*----------------------------------------------------------------------
+ write argument error to the display...
+
+ Args: none
+
+ Result: prints help messages
+ ----------------------------------------------------------------------*/
+void
+display_args_err(s, a, err)
+ char *s;
+ char **a;
+ int err;
+{
+ syslog(LOG_INFO, "Arg Error: %s", s);
+}
diff --git a/web/src/alpined.d/stubs.h b/web/src/alpined.d/stubs.h
new file mode 100644
index 00000000..8128a4aa
--- /dev/null
+++ b/web/src/alpined.d/stubs.h
@@ -0,0 +1,25 @@
+/*-----------------------------------------------------------------------
+ $Id: stubs.h 130 2006-09-22 04:39:36Z mikes@u.washington.edu $
+ -----------------------------------------------------------------------*/
+
+/* ========================================================================
+ * Copyright 2006-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef _WEB_ALPINE_STUBS_INCLUDED
+#define _WEB_ALPINE_STUBS_INCLUDED
+
+
+/* exported prototypes */
+void peMarkInputTime(void);
+
+
+#endif /* _WEB_ALPINE_STUBS_INCLUDED */
diff --git a/web/src/alpined.d/wpcomm.c b/web/src/alpined.d/wpcomm.c
new file mode 100644
index 00000000..b9f17073
--- /dev/null
+++ b/web/src/alpined.d/wpcomm.c
@@ -0,0 +1,197 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: wpcomm.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
+#endif
+
+/* ========================================================================
+ * Copyright 2006-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#include <string.h>
+#include <tcl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+
+#define VERSION "0.1"
+
+#define READBUF 4096
+#define RESULT_MAX 16
+
+int append_rbuf(char *, char *, int);
+int WPSendCmd(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST []);
+
+
+/*
+ * WPComm_Init - entry point for Web Alpine servlet communications.
+ *
+ * returns: TCL defined values for success or failure
+ *
+ */
+
+int
+Wpcomm_Init(Tcl_Interp *interp)
+{
+ if(Tcl_PkgRequire(interp, "Tcl", TCL_VERSION, 0) == NULL
+ && TCL_VERSION[0] == '7'
+ && Tcl_PkgRequire(interp, "Tcl", "8.0", 0) == NULL)
+ return TCL_ERROR;
+
+ if(Tcl_PkgProvide(interp, "WPComm", VERSION) != TCL_OK)
+ return TCL_ERROR;
+
+ Tcl_CreateObjCommand(interp, "WPSend", WPSendCmd,
+ (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
+
+ return TCL_OK;
+}
+
+
+
+/*
+ * WPSendCmd - establish communication with the specified device,
+ * send the given command and return results to caller.
+ *
+ */
+int
+WPSendCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+ char buf[READBUF], lbuf[32], *errbuf = NULL, rbuf[RESULT_MAX], *fname, *cmd;
+ int s, i, n, b, rs, rv = TCL_ERROR, wlen;
+ struct sockaddr_un name;
+ Tcl_Obj *lObj;
+
+ errno = 0;
+
+ if(objc == 3
+ && (fname = Tcl_GetStringFromObj(objv[1], NULL))
+ && (cmd = Tcl_GetByteArrayFromObj(objv[2], &wlen))){
+ if((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1){
+ snprintf(errbuf = buf, sizeof(buf), "WPC: socket: %s", strerror(errno));
+ }
+ else{
+ name.sun_family = AF_UNIX;
+ strcpy(name.sun_path, fname);
+
+ if(connect(s, (struct sockaddr *) &name, sizeof(name)) == -1){
+ if(errno == ECONNREFUSED || errno == ENOENT)
+ snprintf(errbuf = buf, sizeof(buf), "WPC: Inactive session");
+ else
+ snprintf(errbuf = buf, sizeof(buf), "WPC: connect: %s", strerror(errno));
+ }
+ else if((n = wlen) != 0){
+ if(n < 0x7fffffff){
+ snprintf(lbuf, sizeof(lbuf), "%d\n", n);
+ i = strlen(lbuf);
+ if(write(s, lbuf, i) == i){
+ for(i = 0; n; n = n - i)
+ if((i = write(s, cmd + i, n)) == -1){
+ snprintf(errbuf = buf, sizeof(buf), "WPC: write: %s", strerror(errno));
+ break;
+ }
+ }
+ else
+ snprintf(errbuf = buf, sizeof(buf), "Can't write command length.");
+ }
+ else
+ snprintf(errbuf = buf, sizeof(buf), "Command too long.");
+
+ rbuf[0] = '\0';
+ rs = 0;
+ lObj = NULL;
+ while((n = read(s, buf, READBUF)) > 0)
+ if(!errbuf){
+ for(i = b = 0; i < n; i++)
+ if(buf[i] == '\n'){
+ if(rs){
+ Tcl_AppendToObj(Tcl_GetObjResult(interp), &buf[b], i - b);
+ Tcl_AppendToObj(Tcl_GetObjResult(interp), " ", 1);
+ }
+ else{
+ rs = 1;
+ if(append_rbuf(rbuf, &buf[b], i - b) < 0)
+ snprintf(errbuf = buf, sizeof(buf), "WPC: Response Code Overrun");
+ else if(!strcasecmp(rbuf,"OK"))
+ rv = TCL_OK;
+ else if(!strcasecmp(rbuf,"ERROR"))
+ rv = TCL_ERROR;
+ else if(!strcasecmp(rbuf,"BREAK"))
+ rv = TCL_BREAK;
+ else if(!strcasecmp(rbuf,"RETURN"))
+ rv = TCL_RETURN;
+ else
+ snprintf(errbuf = buf, sizeof(buf), "WPC: Unexpected response: %s", rbuf);
+ }
+
+ b = i + 1;
+ }
+
+ if(i - b > 0){
+ if(rs)
+ Tcl_AppendToObj(Tcl_GetObjResult(interp), &buf[b], i - b);
+ else if(append_rbuf(rbuf, &buf[b], i - b) < 0)
+ snprintf(errbuf = buf, sizeof(buf), "WPC: Response Code Overrun");
+ }
+ }
+
+ if(!errbuf){
+ if(n < 0){
+ snprintf(errbuf = buf, sizeof(buf), "WPC: read: %s", strerror(errno));
+ rv = TCL_ERROR;
+ }
+ else if(!rs){
+ if(n == 0)
+ snprintf(errbuf = buf, sizeof(buf), "WPC: Server connection closed (%d)", errno);
+ else
+ snprintf(errbuf = buf, sizeof(buf), "WPC: Invalid Response to \"%.*s\" (len %d) (%d): %.*s", 12, cmd, wlen, errno, RESULT_MAX, rbuf);
+
+ rv = TCL_ERROR;
+ }
+ else if(rv == TCL_ERROR){
+ char *s = Tcl_GetStringFromObj(Tcl_GetObjResult(interp), NULL);
+ if(!(s && *s))
+ snprintf(errbuf = buf, sizeof(buf), "WPC: Empty ERROR Response");
+ }
+ }
+ }
+
+ close(s);
+ }
+ }
+ else
+ snprintf(errbuf = buf, sizeof(buf), "Usage: %s path cmd", Tcl_GetStringFromObj(objv[0], NULL));
+
+ if(errbuf)
+ Tcl_SetResult(interp, errbuf, TCL_VOLATILE);
+
+ return(rv);
+}
+
+
+int
+append_rbuf(char *rbuf, char *b, int l)
+{
+ int i;
+
+ if(l + (i = strlen(rbuf)) > RESULT_MAX)
+ return(-1);
+
+ rbuf += i;
+
+ while(l--)
+ *rbuf++ = *b++;
+
+ *rbuf = '\0';
+ return(0);
+}
diff --git a/web/src/cgi.tcl-1.10/HISTORY b/web/src/cgi.tcl-1.10/HISTORY
new file mode 100644
index 00000000..3fc80020
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/HISTORY
@@ -0,0 +1,644 @@
+This is the HISTORY file for cgi.tcl.
+
+Date Version Description
+------- ------- ------------------------------------------------------
+5/1/05 1.10.0 Odd Arne Jensen <odd@bibsyst.xno> observed that <input>-related
+ tags were producing </input> which is forbidden by the HTML
+ spec. We agreed to go with .../> for max compatibility with
+ both HTML and XHTML.
+
+ Noticed hackers submitting filenames with leading hyphens to
+ display to do interesting things. It did fail gracefully
+ fortunately but it shouldn't fail at all. Added -- to switch.
+
+ Evan Mezeske <emezeske@enflex.xnet> recommended settable limits
+ on file uploads to avoid denial-of-service attacks. Added
+ cgi_file_limit.
+
+12/14/05 1.9.0 Tore Morkemo noted backward test in cgi_noscript used puts
+ instead of cgi_puts.
+
+ Aravindo Wingeier <wingeier@glue.ch> provided patches to choose
+ an encoding or use UTF-8 encoding by default.
+
+ Rolf Ade <pointsmen@gmx.xnet> noted an initialization proc
+ would be useful for things like tclhttpd.
+
+ Modified display.cgi to deal more gracefully with people
+ experimenting with tildes.
+
+ De Clarke noted cgi_input failed if whitespace appeared in a
+ client filename. Due to assumption that Content-Disposition
+ line would be a proper list.
+
+ De also noted that lynx simply wasn't providing quotes in
+ multipart input variable names. Relaxed code to accept that.
+
+ De also noted that the binary file upload wasn't working in
+ pre-Tcl 8.1 but neither of us were interested in spending the
+ time to find out why. Unless someone bothers to figure it, for
+ now, I'm just dropping support for such old versions.
+
+ Due to DoC policy, changed cookie example from persistent to
+ non-persistent.
+
+ Added utf.cgi example to demonstrate UTF works for
+ mel@redolive.com.
+
+10/16/02 1.8.0 To accomodate open-ended forms in echo, modified cgi_input so
+ that it accepts multiple values even in variables that don't
+ end with "List". This opens a possible ambiguity (see
+ documentation) but only one that would've existed before had
+ the diagnostic not prevented it.
+
+ Added example: echo.cgi
+
+ Added cgierror anchor to cgi_eval.
+
+ Tian-Hsiang Huang <xiang@phys.ncku.edu.tw> noted bug in testing
+ for error of passwd.cgi example.
+
+4/27/02 1.7.0 David Kuehling <dvdkhlng@gmx.de> noted that cgi_content_type
+ needed to protect args in call to cgi_http_head.
+
+4/15/02 1.6.1 Darren New fixed my attempt at 1.6.0!
+
+4/15/02 1.6.0 Darren New <dnew@san.rr.com> noted that quote_url needed to
+ translate +.
+
+3/16/02 1.6.0 Added test for checked=bool to radio/checkbox to avoid having
+ user have to do "eval ... [expr ...?"checked":""]" nonsense.
+
+ Added version.cgi script to make it easier for people to figure
+ out whether examples on NIST server are using the same version
+ of cgi.tcl that they're using.
+
+ Tore Morkemo noted backward test in cgi_refresh.
+
+9/19/01 1.5.0 De Clarke noted that file upload broke with Opera. Problem
+ was lack of a \r at the end of the input file. At the same
+ time protected regexp-specials in the boundary string.
+ Both of were due to unusual but legal interpretations.
+
+ Added HELO interaction to smtp dialogue.
+
+ Jonathan Lennox <lennox@cs.columbia.edu> provided fix for
+ finding sendmail on BSD 4.4 systems.
+
+ Modified cgi_html and _start to support optional attributes.
+
+ Extended auto-attr quoting for XHTML support.
+ Added end tags for XHTML support.
+
+ Added cgi_link_url/label per John Koontz and rewrote cgi_link
+ to accomodate them. Also added his cgi_button_link
+ temporarily. Will experiment with this to test merit.
+
+11/3/00 1.4.3 Removed HTML formatted manual. It lags the text version and
+ just caused more confusion than it's worth.
+
+11/2/00 1.4.2 John Koontz observed that the versions between home page and
+ package didn't match. Added version.in to fix this once and
+ for all.
+
+10/19/00 1.4.1 Carlos Vidal <carlos@tarkus.se> fixed cgi_cookie_get -all.
+
+ Anton Osennikov <ant@ccl.ru> provided patch to propagate
+ errorCode back through cgi_body.
+
+ Fixed display_in_frame to allow app_body_end to execute.
+
+6/14/00 1.4.0 Tomas Soucek requested a mechanism for saving content-type
+ from file uploads. Seemed appropriate to do this by renaming
+ cgi_import_filename to ..._file and adding -type flag.
+
+ Petrus Vloet noted vote.cnt missing from distrib. He also
+ noted getting warnings regarding missing newlines.
+
+5/19/00 1.3.0 Changed many cgi_puts to cgi_put in hopes of addressing more
+ of Zygo's 5/9/00 complaint (i.e., same problem in tables).
+
+5/9/00 1.2.2 Zygo Blaxell <zblaxell@feedme.hungrycats.org> provided signif.
+ cgi_input regexp speedup for long x=y-style values. He also
+ noted that some browsers are sensitive to leading/following
+ \n's in textarea and provided a patch to avoid adding them in
+ the first place.
+
+ James Ward <jew@rincon.com> noted absence of pkgIndex.tcl
+ (presumably due to really old Tcl) broke installation. Fixed
+ pkgcreate to create stub file to allow Make to complete.
+
+ Robin Lauren <robin.lauren@novostore.com> contributed the doc
+ in HTML form. Really need to automate this now, sigh.
+
+ Jan.Vlcek@sasprg.cz observed that converting %XX to \u00XX
+ and then using subst is only good for us-ascii and corrupts
+ iso8859-1, iso8859-2, etc. He provided a patch for
+ cgi_unquote_input.
+
+ Ross Mohn <rpmohn@waxandwane.com> corrected syntax error in
+ cgi_span and made <hr> handle width= better.
+
+ Tore Morkemo provided a patch to his prior patch for cgi_eval.
+
+ Asari Hirotsugu <asari@math.uiuc.edu> provided additional
+ installation advice for Mac.
+
+12/27/99 1.2.1 Tore Morkemo noted expires=never value of 9999 inappropriate
+ as Netscape ignores anything beyond mid-January 2038.
+
+ Tore also provided patch for cgi_eval when running inside of
+ a proc.
+
+12/20/99 1.2.0 Keith Lea <keith@cs.oswego.edu> noted 2-digit years as per
+ RFC2109. Despite RFC, Netscape now accepts 4-digit years.
+ Some browsers won't like this but it hardly matters anyway
+ since they'll do the wrong thing on old 2-digit years come
+ Y2K anyway.
+
+ Petrus Vloet noted example/nistguest missing from distrib.
+
+12/18/99 1.1.0 Tomas Soucek" <tomas.soucek@sasprg.cz> noted cgi_input was
+ adding eol characters to uploaded files if they didn't
+ contain them. Fixed this and also enhanced file upload
+ example so that it could do both cat/od and also warn when
+ Tcl couldn't do binary upload.
+
+ Added braces around unprotected expressions.
+
+ Added check to unimail example for HTTP_REFERER.
+
+ Petrus Vloet requested Makefile install example data files.
+
+ Added img.cgi example and modified frame example so it accepts
+ "example=whatever" so that I can post URLs that go right to a
+ particular example and have it framed.
+
+9/12/99 1.0.0 Bumped version to 1 to pacify management.
+
+ Jeffrey Hobbs rewrote cgi_unquote_input to take advantage of
+ 8.2 features. 300% speed improvement!
+
+7/16/99 0.8.1 Douglas Ridgway provided mod to make cgi_image_button handle
+ optional args.
+
+ Made code use straight cgi_input_multipart if on Tcl 8.1.
+
+ Jeffrey Hobbs provided cgi_unquote_input that works better for
+ 8.1.
+
+ Petrus Vloet <petrus.vloet@siemens.at> requested sample data
+ files for examples that need them; ability to change example
+ install destination.
+
+2/22/98 0.8.0 Tore Morkemo noted that cookied_encode needed to convert \n's.
+
+ Added example of how to produce a raw image.
+
+ Upon suggestion of Jean-Yves Terrien
+ <jeanyves.terrien@cnet.francetelecom.fr> experimented with
+ version of cgi_puts that would indent HTML for readability.
+ Code looked something like this:
+
+ proc NEW_cgi_puts {args} {
+ global _cgi
+
+ if {$_cgi(preformatted)} {
+ eval puts $args
+ } else {
+ set indent_full [format "%-$_cgi(indent)s" ""]
+
+ # if we're at the beginning of the line, use a full indent
+ if {$_cgi(bol)} {
+ set indent $indent_full
+ } else {
+ set indent ""
+ }
+
+ set tail [lindex $args end]
+ set output ""
+
+ # loop through output, breaking into \n-delimited lines
+ while {[string length $tail]} {
+ # this regexp will always match
+ regexp "^(\[^\n]*)(\n?)(.*)" $tail dummy line nl tail
+ append output $indent$line
+ if {$nl == ""} {
+ set _cgi(bol) 0
+ break
+ }
+ append output \n
+ set $_cgi(bol) 1
+ set indent $indent_full
+ }
+
+ # handle optional -nonewline
+ if {[llength $args] == 1} {
+ append output \n
+ set _cgi(bol) 1
+ }
+ puts -nonewline $output
+ }
+ }
+
+ Unfortunately, some tags are whitespace-sensitive such as
+ textarea. If you do cgi_buffer {p;textarea ...} how can cgi_
+ puts possibly know that part of the buffer shouldn't be
+ modified? A user-level flag could be added to avoid corrupting
+ the HTML but the source display could never be made perfect.
+ Considering that programmers would have to be aware of these
+ issues (via flags or options) and the payoff is so minute, it's
+ just not worth it. I think. As an aside, does regexp on every
+ line of output cost too much? I doubt anyone would notice but
+ it's something to ponder.
+
+ Renamed internal cgi routines with _ prefix.
+
+ Matthew Levine <mlevine@cisco.com> observed that cgi_eval
+ did a #0 eval but a plain eval is more appropriate.
+
+ Anthony Martin <anthony.n.martin@marconicomms.com> noted that
+ cgi_cgi_set needs to rewrite #.
+
+ Removed cgi_http_get.
+
+ Added registry support to uid_check.
+
+ Eddy Kim <ehkim@ibm.net> noted bug in doc regarding cgi_puts.
+
+ cgi_file_button now verifies that form has correct encoding.
+
+ Fixed doc to reflect more precise use of selected_if_equal.
+
+ Ralph Billes <echo@iinet.net.au> fixed bug in cgi_uid_check.
+
+ Began adding stylesheet support. Made cgi_stylesheet and
+ cgi_span. Not yet documented.
+
+ Douglas Ridgway <ridgway@gmcl.com> noted that lynx doesn't
+ respond correctly to multipart form requests. Added check and
+ diagnostic. Doug also found a bug in the multipart_binary
+ because I forgot to turn off the timeout.
+
+ Stefan Hornburg <racke@gundel.han.de> writes:
+ "I've made a RPM package for cgi.tcl. Available at:
+ http://www.han.de/~racke/linuxia/noarch/cgi.tcl-0.7.5-1.noarch.rpm
+ http://www.han.de/~racke/linuxia/srpms/cgi.tcl-0.7.5-1.src.rpm
+ http://www.han.de/~racke/linuxia/specs/cgitcl.spec
+ and hopefully soon at Redhat/Contrib."
+ He also noted a missing example: download.cgi.
+
+6/25/98 0.7.5 Henk-Jan Kooiman <hjk@cable.a2000.nl> fixed bugs in
+ cgi_relationship and also provided MacOS instructions.
+
+ Martin Schwenke <Martin.Schwenke@cs.anu.edu.au> and Ryan
+ McCormack <ryanm@cam.nist.gov> both reported textarea lost
+ newlines when using multipart.
+
+ At urging of Robert Baptista <Robert.Baptista@vanmail.amd.com>,
+ began adding HTML 4.0 support. Changed cgi_center to avoid
+ <center>.
+
+ Added download.cgi example.
+
+ John Koontz noted example in file upload documentation still
+ used deprecated flags.
+
+ 0.7.4 Fixed bug in cgi_refresh.
+
+ Added alt attr to cgi_area.
+
+ Added vote.cgi example.
+
+ Localized some init-time vars to avoid stomping on user's.
+
+ Made cgi_quote_url also quote {"} so URLs. This isn't strictly
+ necessary, but since URLs will usually be embedded inside HTML,
+ this simplifies embedding and doesn't hurt anything else.
+
+ Tom Poindexter <tpoindex@nyx.net> added cgi_suffix.
+
+ Removed reference to sendmail from documentation since sendmail
+ is no longer required.
+
+12/16/97 0.7.3 Provided SMTP dialogue to replace call to sendmail and removed
+ all other UNIXisms (exec and direct refs to file system).
+
+ Rewrote LUHN proc in credit card example to work correctly!
+
+ Added type tag support to cgi_relationship.
+
+11/10/97 0.7.2 Massaged documentation to describe installation on W95/NT....
+ Made example.tcl and date.cgi portable.
+
+ Added rm.cgi example.
+
+ Beat Jucker <bj@glue.ch> fixed syntax error in oratcl.cgi
+ example.
+
+10/20/97 0.7.1 Fixed cgi_option so that value strings are quoted. Also made
+ selected_if_equal check value rather than visible string.
+
+ Tore Morkemo <tore@bibsyst.no> provided patch so that cgi_set
+ translated "=".
+
+ Fixed buffering bug in push example.
+
+ Added credit card example.
+
+ Paul Saxe <saxe@connectnet.com> noted cgi_embed broke due to
+ unprotected regexp pattern beginning with -.
+
+
+8/22/97 0.7.0 INCOMPATIBILITY: cgi_anchor_name now returns the anchor tag
+ instead of printing it. Only after some experience do I see
+ now that anchors always need to be embedded in something else
+ for accuracy in the jump.
+
+ Josh Lubell noted that file upload failed if there was
+ whitespace in filename. Problem was that filename-encoding
+ doesn't encode whitespace or otherwise protect it. There is
+ no perfect fix since the spec doesn't explain how to know when
+ fields end. The implication is that they are delimited by
+ " however an embedded " is not encoded either! For now, assume
+ filename field is at end of line. This isn't the greatest idea
+ for it allows future encoding changes to break the code, but
+ for now it will pick up all filenames including really weird
+ ones like 'a b"; foo="bar' that would otherwise appear to be
+ additional fields!
+
+ Jon Leech <leech@cs.unc.edu> provided code to allow user to
+ temporarily use existing links but displaying differently.
+
+ Alvaro Santamaria <alvaro.santamaria@stest.ch> noted that I
+ forgot to include kill.cgi and oratcl.cgi in example directory.
+
+ John Koontz noted cgi_buffer's defn of cgi_puts assumed "usual"
+ newline generation - interacted badly inside of <pre> tags.
+ Added cgi_buffer_nl to control this.
+
+ James Ward requested option to change ".cgi" suffix. Added
+ -suffix to cgi_cgi.
+
+ Someone's browser (Mozilla/4.01 [en] (WinNT; I) via proxy
+ gateway CERN-HTTPD/3.0 libwww/2.17) supplied a boundary defn
+ but no actual boundary. Added test to
+ cgi_input_multipart_binary.
+
+ James Ward found cgi_import_list didn't return List vars.
+
+ Diagnostics about broken multipart responses are now returned
+ to users (who are using old browsers).
+
+ Changed nbsp to numeric equiv to support NS2.
+
+ Made cgi_debug always return old value.
+
+6/4/97 0.6.6 Added cgi_buffer - force commands that normally generate output
+ to return strings.
+
+ Deprecated -local & -remote flags to import_filename. Added
+ support for -server & -client which are less confusing.
+
+ Improved examples in numerous ways.
+
+ James Ward <jew@mcmuse.mc.maricopa.edu> noted I forgot to
+ document cgi_img.
+
+ Upgraded cgi_quote as per HTML 3.2
+
+ Mark Diekhans <markd@Grizzly.COM> provided bug fix to cgi_tr
+ and optional level arg to all uplevel/upvar calls to avoid
+ misinterpretation. Also added cgi_th.
+
+ Appears that NS3.0 has a bug wrt multipart. It won't display
+ last fragment on page unless followed by a <br> (even if it
+ sees </body>). Added cgi_br to multipart support code.
+
+5/26/97 0.6.5 Added server-push example which required mods to cgi_title
+ (bug fix actually), cgi_head, and related code to detect and
+ handle multipart mime type.
+
+ Added HTTP_HOST to list of things reported_HOST during errors.
+
+ Looks like MS IE can generate cookies without an "=val" part.
+
+ Added Oratcl example.
+
+ Documented cgi_url.
+
+ Fixed search for Tcl executable when look in Tcl's srcdir.
+
+ Added support for detection/reporting of client errors. For
+ now this means missing CONTENT_LENGTH.
+
+ Created cgi_redirect and deprecated cgi_location. cgi_redirect
+ is better named and more functional. Added cgi_status.
+
+ Modified unimail example so that it shows diagnostics about
+ form problems to client - doesn't make sense to send them to
+ unimail admin.
+
+ Improved form-tour in various ways including making a backend
+ that actually reports value.
+
+ Fixed bug in textvar that caused args to be generated twice.
+
+4/8/97 0.6.4 John LoVerso fixed regsub quote bug in cgi_quote_url. Fixed
+ similar bug cgi_cgi_set.
+
+ Most general path cookies were being returned. Changed to most
+ specific and added -all to cookie_get so that all values could
+ be retrieved. Added cookie support to passwd example since
+ that's a very reasonable use of it.
+
+4/7/97 0.6.3 John LoVerso fixed bugs in cgi_meta and cgi_map.
+
+ INCOMPATIBILITY: cgi_quote_url was inappropriate for non-cgi
+ urls. cgi_cgi and cgi_cgi_set should now be used for building
+ GET-style cgi urls.
+
+ Forgot to switch to cgi.tcl.in in distribution.
+
+3/28/97 0.6.2 Made cgi.tcl.in autohandle embedded version number (see 0.6.1).
+
+ Fixed display.cgi to prevent display of "." or ".." or "".
+
+ Added cgi.tcl version to diagnostics.
+ Fixed diagnostics to correctly print input. (Totally missing!)
+
+ John Koontz noted extra \r\n at end of uploaded text files.
+
+3/24/97 0.6.1 Updated package provide version. (Gotta automate this!)
+
+3/20/97 0.6.0 Added kill.cgi example to kill/suspend runaway CGI processes.
+
+ Modified passwd.cgi example to quote shell arg.
+
+ Kevin.Christian requested hook for defining doctypes.
+
+ John Robert LoVerso <john@loverso.southborough.ma.us> provided
+ fixes to allow array-style variables names to be rewritten by
+ cgi_input. Parens had been left with embedded %s substituted.
+ My research indicates cookies have similar problems but file-
+ style encoding does not.
+
+ Josh Lubell found bug in binary upload that would drop \r\n
+ and \r*.
+
+2/28/97 0.5.5 Combined many regexps that had parameterizably-common actions.
+ The old regexps had some bugs in them, so it's a good thing.
+
+ M. Katherine Pagoaga <pagoaga@boulder.nist.gov> and John
+ Koontz <koontz@boulder.nist.gov> noted cgi_uid_check used
+ whoami which isn't very portable. Switched to "who am i".
+ Its output isn't very portable but at least its existence is!
+
+ Removed default call to cgi_uid_check. It's my impression that
+ most people turn this off anyway. If you want it back, just
+ call it yourself.
+
+ Added cgi_import_filename to guarantee that a filename really
+ did come from an upload. This removes the security check that
+ the user was responsible for - and which I forgot in the
+ upload examples!
+
+ Rolf Ade <rolf@storz.de> noted that configure could look for
+ tcl executable in a few more locations.
+
+2/26/97 0.5.4 Backed out type=text from 0.4.4. This ruined type=password.
+ If people demand, I'll make the code put in type=text but I now
+ regret the detour.
+
+ Made cgi_import_list return variables in order originally
+ appearing in form.
+
+2/24/97 0.5.3 Added tr/td as shortcut for simple table rows.
+
+ Made cookie code look in COOKIE (O'Reilly's web server uses
+ that instead of HTTP_COOKIE).
+
+ Kevin.Christian@symbio.com requested warning when form
+ incorrectly uses select without "List" suffix. Also provided
+ more obvious diagnostics when applying cgi_import* to non-
+ existent variable. At same time, I also noticed bug in
+ cgi_import_cookie* preventing it from seeing user variables.
+
+ Forgot to have cgi_root return root with no args.
+
+2/11/97 0.5.2 Josh Lubell found binary upload broke, due to one send missing
+ "--" protection.
+
+ Massaged home page so that it works more intelligently with
+ frames. Massaged examples for both function and appearance.
+
+2/10/97 0.5.1 Fixed display of validate example.
+
+ Added cgi_unbreakable_string so that it returns a string.
+ Compare with cgi_unbreakable.
+
+ Added cgi_javascript and noscript. javascript is like script
+ but does the javascript-compatible hiding of <script> tags for
+ older browsers. Added some javascript examples. Added support
+ for java event types. Netscape documentation is very unclear
+ as to which tags support which attributions - so I guessed a
+ lot!
+
+2/4/97 0.5.0 Added cgi_body_args to make it easier to share and change body
+ attributes.
+
+ Fixed bug in cookie encoder that caused double %-expansion.
+
+ INCOMPATIBILITY: Modified cgi_preformatted to evaluate last
+ argument. More flexible this way. Since <pre> forces line
+ breaks, it's not like inline formatting commands.
+ Removed cgi_code since it used the xmp tag which is gone.
+ See documentation for workaround.
+
+ INCOMPATIBILITY: Changed cgi_division to take a command list
+ rather than a string.
+
+ Added cgi_nl for inline breaks. (Compare with cgi_br.)
+
+2/3/97 0.4.9 Added support for package loading, now you just say:
+ "package require cgi"
+
+ Added/modified examples so that they could be put up on
+ web servers.
+
+ Added support to suppress binary upload when using Expect.
+
+1/31/97 0.4.8 Andreas Kupries <a.kupries@westend.com> needed dynamic control
+ of ouput. Added user-defineable cgi_puts.
+
+ Josh Lubell <lubell@cme.nist.gov> reported binary file upload
+ was losing chars on large files. Fixed.
+
+ Fixed cgi_input to understand REQUEST_METHOD == HEAD should
+ set input to "".
+
+1/13/97 0.4.7 Braces in password script was wrong.
+
+ Simplified command aliasing mechanism.
+
+ Add some more bulletproofing to catch broken boundary defns...
+
+ Evidentally QUERY_STRING doesn't have to be set even with GET.
+ CGI spec is vague enough to permit this.
+
+12/16/96 0.4.6 Giorgetti Federico <gio@egeo.unipg.it> noted text file upload
+ wasn't working - forgot "_binary" on new upload proc name.
+
+12/10/96 0.4.5 Added support for binary file upload if using Expect extension.
+
+ Removed check for missing filenames in file upload. This
+ can evidentally happen. It is now the programmer's
+ reptysponsibility to check for the length if null filenames are
+ unwanted.
+
+ Created HTML home page for cgi.tcl.
+
+11/26/96 0.4.4 Added type=text for pedantic html checkers.
+ Bob Lipman noted new if_equal handlers used name instead of
+ value.
+
+10/24/96 0.4.3 Forgot global _cgi decls in cgi_refresh and some other procs.
+
+ Bob Lipman noted cgi_radio_button needs checked_if_equal.
+ Added that and same to cgi_checkbox.
+
+10/15/96 0.4.2 Remove line-delimiters in non-file elements in multipart enc.
+
+ Added support for additional args to cgi_file_button.
+
+9/19/96 0.4.1 Bob Lipman noted cookie containing = could not be imported.
+
+ Fixed examples to reference .4 source.
+
+9/9/96 0.4.0 Added cgi_error_occurred.
+ In multipart handler, added test for "--" because some
+ servers(?) don't provide explicit eof, but just hang.
+ Bugs in close_procs support.
+ Added auto-htmlquoting to cgi_option.
+ Removed auto-quoting in cgi_cgi.
+ Made cgi_root also return root if no arg.
+ Messed up quoting in cgi_anchor_name and cgi_applet.
+ cgi_parray was missing <xmp> tag. How odd.
+
+8/12/96 0.3.5 Mark Pahlavan <mark@vestek.com> provided new cgi_caption defn
+ after noting that it must allow executable code.
+
+ File upload broke in Tcl 7.5 due to auto-eol translation
+
+6/19/96 0.3.4 Mark Harrison <mharriso@spdmail.spd.dsccc.com> noted ref to
+ obsolete cgi_uid_ignore.
+
+6/19/96 0.3.3 Was wrongly cookie-encoding cookie expiration. cgi_imglink
+ was confused. Thanks again, Bob.
+
+6/17/96 0.3.2 Reports from Bob Lipman <lipman@cam.nist.gov> about bad
+ tables and missing </head>. Fixed.
+
+6/13/96 0.3.1 Added ";" to &-escapes. Fixed bugs in cgi_unquote_input.
+ Added cgi_embed. Added doc directory with ref manual.
+
diff --git a/web/src/cgi.tcl-1.10/INSTALL b/web/src/cgi.tcl-1.10/INSTALL
new file mode 100644
index 00000000..4a7f8a76
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/INSTALL
@@ -0,0 +1,96 @@
+This file is INSTALL. It contains installation instructions for cgi.tcl.
+
+If you do not have Tcl, get it (the README explains how) and install
+it. The rest of these instructions assume that you have Tcl
+installed.
+
+--------------------
+Installation
+--------------------
+
+By default, the Tcl source directory is assumed to be in the same
+directory as the cgi.tcl source directory. For example, in this
+listing, cgi.tcl and Tcl are both stored in /usr/local/src:
+
+ /usr/local/src/tcl7.5 (actual version may be different)
+ /usr/local/src/cgi.tcl-1.0 (actual version may be different)
+
+If Tcl is stored elsewhere, the easiest way to deal with this is to
+create a symbolic link to its real directory. For example, from the
+cgi.tcl directory, type:
+
+ ln -s /some/where/else/src/tcl7.5 ..
+
+Run "./configure". This will generate a Makefile (from a prototype
+called "Makefile.in") appropriate to your system. Make sure you run
+configure with the same arguments as when you ran Tcl's configure
+script. (If you don't, package loading won't work.)
+
+Most people will not need to make any changes to the generated
+Makefile and can go on to the next step. If you want though, you can
+edit the Makefile and change any definitions as appropriate for your
+site. All the definitions you are likely to want to change are
+clearly identified and described at the beginning of the file.
+
+Run "make".
+
+It is useful (although not necessary) for cgi.tcl to understand how to
+send mail. By default, cgi.tcl tries to use /usr/lib/sendmail,
+otherwise it falls back to carrying out the raw SMTP dialogue itself.
+Any mailer can be substituted by modifying cgi.tcl appropriately. It
+is easy to do. Edit cgi.tcl and look at the cgi_mail_end procedure.
+It should be obvious what to do at that point. The ability to send
+mail isn't required for basic use of cgi.tcl, but it is especially
+useful for in-the-field debugging so I encourage you to enable it.
+
+You can now "source cgi.tcl" if you want to try things out by hand
+before installing (or if you want to use the package without
+installing it). Example:
+
+ $ tclsh7.6 (or whatever your Tcl interpreter is called)
+ % source cgi.tcl
+ % h4 "Don Libes"
+ <h4>Don Libes</h4>
+ %
+
+Once you're done playing, go ahead and install it. To install everything:
+
+ make install
+
+You're done! Now you can use cgi.tcl.
+
+--------------------
+Examples
+--------------------
+
+The example directory has some examples. See the README in there.
+
+--------------------
+Test Suite
+--------------------
+
+There is no test suite.
+
+--------------------
+Uninstalling
+--------------------
+
+"make uninstall" removes all the files that "make install" creates
+(excluding those in the current directory).
+
+--------------------
+Cleaning Up
+--------------------
+
+Several "clean" targets are available to reduce space consumption of
+the cgi.tcl source. The two most useful are as follows:
+
+"make clean" deletes all files from the current directory that were
+created by "make"
+
+"make distclean" is like "make clean", but it also deletes files
+created by "configure"
+
+Other targets can be found in the Makefile. They follow the GNU
+Makefile conventions.
+
diff --git a/web/src/cgi.tcl-1.10/Makefile.in b/web/src/cgi.tcl-1.10/Makefile.in
new file mode 100644
index 00000000..3f818f2c
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/Makefile.in
@@ -0,0 +1,273 @@
+#
+# Makefile for Don Libes' cgi.tcl - routines for writing CGI scripts in Tcl
+#
+
+VERSION = \"@CGI_VERSION_FULL@\"
+SHORT_VERSION = @CGI_VERSION@
+
+srcdir = @srcdir@
+VPATH = @srcdir@
+SUBDIRS = @subdirs@
+
+######################################################################
+# The following lines are things you may want to change
+######################################################################
+
+# By default, "make install" will install the appropriate files in
+# /usr/local/bin, /usr/local/lib, /usr/local/man, etc. By changing this
+# variable, you can specify an installation prefix other than /usr/local.
+# You may find it preferable to call configure with the --prefix option
+# to control this information. This is especially handy if you are
+# installing this several times (perhaps on a number of machines or
+# in different places). Then you don't have to hand-edit this file.
+# See the INSTALL file for more information. (Analogous information
+# applies to the next variable as well.)
+prefix = @prefix@
+
+# You can specify a separate installation prefix for architecture-specific
+# files such as binaries and libraries.
+exec_prefix = @exec_prefix@
+
+# Short directory path where binaries can be found to support #! hack.
+# This directory path can be the same as the directory in which the binary
+# actually sits except when the path is so long that the #! mechanism breaks
+# (usually at 32 characters).
+# The solution is to create a directory with a very short name, which consists
+# only of symbolic links back to the true binaries. Subtracting two for "#!"
+# and a couple more for arguments (typically " -f" or " --") gives you 27
+# characters. Pathnames over this length won't be able to use the #! magic.
+# For more info on this, see the execve(2) man page.
+SHORT_BINDIR = $(exec_prefix)/bin
+
+# Tcl interpreter for utility work.
+CGI_TCL_EXECUTABLE = @CGI_TCL_EXECUTABLE@
+
+# Where to put the examples - a directory in which your web server has
+# permission to execute CGI scripts.
+exampledir = /tmp/cgi-bin/cgi-tcl-examples
+
+######################################################################
+# End of things you may want to change
+#
+# Do not change anything after this
+######################################################################
+
+bindir_arch_indep = $(prefix)/bin
+libdir = $(exec_prefix)/lib
+datadir = $(prefix)/lib
+
+mandir = $(prefix)/man
+man1dir = $(mandir)/man1
+man3dir = $(mandir)/man3
+docdir = $(datadir)/doc
+
+# utility script directories - arch-independent and arch-non-
+# independent.
+SCRIPTDIR = $(datadir)/cgi$(SHORT_VERSION)
+EXECSCRIPTDIR = $(execdatadir)/cgi$(SHORT_VERSION)
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+
+# To install the following examples: make examples
+EXAMPLES=cookie.cgi creditcard.cgi \
+ display.cgi display-in-frame.cgi download.cgi \
+ echo.cgi error.cgi evaljs.cgi example.tcl examples.cgi \
+ form-tour.cgi form-tour-result.cgi format-tour.cgi frame.cgi \
+ image.cgi img.cgi kill.cgi nistguest.cgi oratcl.cgi \
+ parray.cgi passwd.tcl passwd-form.cgi passwd.cgi \
+ push.cgi rm.cgi stopwatch.cgi \
+ unimail.cgi upload.cgi uploadbin.cgi \
+ validate.cgi vclock.cgi vclock.pl vclock-src-frame.cgi visitor.cgi \
+ vote.cgi
+
+EXAMPLES_DATA=nistguest vote.cnt
+
+all: cgi.tcl pkgIndex.tcl
+
+info:
+dvi:
+
+# Delete all the installed files that the `install' target creates
+# (but not the noninstalled files such as `make all' creates)
+uninstall:
+ -rm -f $(SCRIPTDIR)/cgi.tcl
+ -rm -f $(man3dir)/cgi.tcl.3
+ -rm -f $(SCRIPTDIR)/pkgIndex.tcl
+
+.PHONY: install-info install info
+install-info:
+
+install: all
+ ${srcdir}/mkinstalldirs $(man3dir) $(SCRIPTDIR) $(exampledir) $(exampledir)/data
+# install scripts
+ $(INSTALL_DATA) cgi.tcl $(SCRIPTDIR)
+# install library man page
+ $(INSTALL_DATA) cgi.tcl.man $(man3dir)/cgi.tcl.3
+ $(INSTALL_DATA) pkgIndex.tcl $(SCRIPTDIR)
+
+examples:
+ for i in $(EXAMPLES) ; do \
+ $(CGI_TCL_EXECUTABLE) $(srcdir)/fixline1 $(SHORT_BINDIR) < $(srcdir)/example/$$i > $$i ; \
+ $(INSTALL_PROGRAM) $$i $(exampledir) ; \
+ rm -f $$i ; \
+ done
+ for i in $(EXAMPLES_DATA) ; do \
+ $(INSTALL) -m 666 $(srcdir)/example/$$i $(exampledir)/data ; \
+ done
+
+cgi.tcl: $(srcdir)/cgi.tcl.in
+ @echo "Rebuilding cgi.tcl..."
+ $(SHELL) ./config.status
+
+###################################
+# Targets for Makefile and configure
+###################################
+
+Makefile: $(srcdir)/Makefile.in $(host_makefile_frag) config.status
+ @echo "Rebuilding the Makefile..."
+ $(SHELL) ./config.status
+
+# Let "make -f Makefile.in" produce a configure file
+configure: $(srcdir)/configure.in $(srcdir)/Makefile.in
+ @echo "Rebuilding configure..."
+ if [ x"${srcdir}" = x"@srcdir@" ] ; then \
+ srcdir=. ; export srcdir ; \
+ else true ; fi ; \
+ (cd $${srcdir}; autoconf)
+
+config.status: $(srcdir)/configure
+ @echo "Rebuilding config.status..."
+ $(SHELL) ./config.status --recheck
+
+check:
+ @if [ -f testsuite/Makefile ]; then \
+ cd testsuite && $(MAKE) $(FLAGS_TO_PASS) check; \
+ else true; fi
+
+# updating of pkgIndex.tcl has not yet been totally automated
+# Instructions:
+# 1) Replace version # in cgi.tcl with updated version.
+# (Gad, I'd have to turn the whole source into .in file to automate this
+# and it'd be a major nuisance when developing!)
+pkgIndex.tcl: cgi.tcl
+ $(CGI_TCL_EXECUTABLE) pkgcreate
+
+################################################
+# Various "clean" targets follow GNU conventions
+################################################
+
+# delete all files from current directory that are created by "make"
+clean:
+ -rm -f *~ *.o core
+
+# like "clean", but also delete files created by "configure"
+distclean: clean
+ -rm -f Makefile config.status config.cache config.log
+ -rm -f Dbg_cf.h
+
+# like "clean", but doesn't delete test utilities or massaged scripts
+# because most people don't have to worry about them
+mostlyclean:
+ -rm -f *~ *.o core
+
+# delete everything from current directory that can be reconstructed
+# except for configure
+realclean: distclean
+
+######################################
+# Targets for pushing out releases
+######################################
+
+FTPDIR = /itl/www/div826/subject/expect/cgi.tcl
+
+# make a private tar file for myself
+tar: cgi.tcl-$(SHORT_VERSION).tar
+ mv cgi.tcl-$(SHORT_VERSION).tar cgi.tcl.tar
+
+# Make a release and install it on ftp server
+# Note that we run configure on our end to make sure that cgi.tcl is usable
+# for people who don't have configure at the destination end (non-UNIX folks).
+ftp: cgi.tcl cgi.tcl-$(SHORT_VERSION).tar.Z cgi.tcl-$(SHORT_VERSION).tar.gz install-html
+ cp cgi.tcl-$(SHORT_VERSION).tar.Z $(FTPDIR)/cgi.tcl.tar.Z
+ cp cgi.tcl-$(SHORT_VERSION).tar.gz $(FTPDIR)/cgi.tcl.tar.gz
+ cp HISTORY $(FTPDIR)
+ cp README $(FTPDIR)/README.distribution
+ cp doc/ref.txt $(FTPDIR)
+ cp doc/ref.html $(FTPDIR)
+ cp example/README $(FTPDIR)/example
+ cp `pubfile example` $(FTPDIR)/example
+ ls -l $(FTPDIR)/cgi.tcl.tar*
+# delete temp files
+ rm cgi.tcl-$(SHORT_VERSION).tar*
+
+cgi.tcl-$(SHORT_VERSION).tar: configure
+ rm -f ../cgi.tcl-$(SHORT_VERSION)
+ ln -s `pwd` ../cgi.tcl-$(SHORT_VERSION)
+ cd ..;tar cvfh $@ `pubfile cgi.tcl-$(SHORT_VERSION)`
+ mv ../$@ .
+
+cgi.tcl-$(SHORT_VERSION).tar.Z: cgi.tcl-$(SHORT_VERSION).tar
+ compress -fc cgi.tcl-$(SHORT_VERSION).tar > $@
+
+cgi.tcl-$(SHORT_VERSION).tar.gz: cgi.tcl-$(SHORT_VERSION).tar
+ gzip -fc cgi.tcl-$(SHORT_VERSION).tar > $@
+
+###########################
+# Targets for producing FAQ and homepage
+###########################
+
+NISTCGI_PREFIX = /local
+NISTCGI_HOST = ats.nist.gov
+NISTCGI_INTERPDIR = $(NISTCGI_PREFIX)/bin
+NISTCGI_LIBDIR = $(NISTCGI_PREFIX)/lib/cgi$(SHORT_VERSION)
+NISTCGI_EXAMPLEDIR = $(NISTCGI_HOST):/private/home/http/cgi-bin/cgi.tcl
+NISTCGI_HTMLDIR = /itl/www/div826/subject/expect/cgi.tcl
+
+# create the FAQ in html form
+FAQ.html: FAQ.src FAQ.tcl
+ FAQ.src > FAQ.html
+
+# create the FAQ in text form
+#FAQ: FAQ.src FAQ.tcl
+# FAQ.src text > FAQ
+
+# generate home page
+homepage.html: homepage.src common.tcl configure
+ homepage.src > homepage.html
+
+realapps.html: realapps.src common.tcl
+ realapps.src > realapps.html
+
+RSH = ssh
+RCP = scp
+
+# install various html docs on our web server
+install-html: FAQ.html homepage.html realapps.html pkgIndex.tcl cgi.tcl
+ cp FAQ.html $(NISTCGI_HTMLDIR)/
+ cp homepage.html $(NISTCGI_HTMLDIR)/index.html
+ cp realapps.html $(NISTCGI_HTMLDIR)/
+ -$(RSH) $(NISTCGI_HOST) mkdir $(NISTCGI_LIBDIR)/
+ $(RCP) cgi.tcl pkgIndex.tcl $(NISTCGI_HOST):$(NISTCGI_LIBDIR)/
+ for i in $(EXAMPLES) ; do \
+ $(CGI_TCL_EXECUTABLE) $(srcdir)/fixline1 $(NISTCGI_INTERPDIR) < $(srcdir)/example/$$i > $$i ; \
+ $(RCP) $$i $(NISTCGI_EXAMPLEDIR)/ ; \
+ rm -f $$i ; \
+ done
+
+# add recursive support to the build process.
+subdir_do: force
+ @for i in $(SUBDIRS); do \
+ echo "Making $(DO) in $${i}..." ; \
+ if [ -d ./$$i ] ; then \
+ if (rootme=`pwd`/ ; export rootme ; \
+ rootsrc=`cd $(srcdir); pwd`/ ; export rootsrc ; \
+ cd ./$$i; \
+ $(MAKE) $(FLAGS_TO_PASS) $(DO)) ; then true ; \
+ else exit 1 ; fi ; \
+ else true ; fi ; \
+ done
+force:
+
+## dependencies will be put after this line... ##
diff --git a/web/src/cgi.tcl-1.10/PATCH.UW b/web/src/cgi.tcl-1.10/PATCH.UW
new file mode 100644
index 00000000..0288cb72
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/PATCH.UW
@@ -0,0 +1,230 @@
+*** ./cgi.tcl.in.orig 2006-05-01 11:15:52.000000000 -0700
+--- ./cgi.tcl.in 2006-11-14 16:01:51.000000000 -0800
+***************
+*** 52,58 ****
+
+ if {[info exists _cgi(http_status_done)]} return
+ set _cgi(http_status_done) 1
+! puts "Status: $num $str"
+ }
+
+ # If these are called manually, they automatically generate the extra newline
+--- 52,58 ----
+
+ if {[info exists _cgi(http_status_done)]} return
+ set _cgi(http_status_done) 1
+! cgi_puts "Status: $num $str"
+ }
+
+ # If these are called manually, they automatically generate the extra newline
+***************
+*** 1342,1348 ****
+ set dbg_filename [file join $_cgi(tmpdir) CGIdbg.[pid]]
+ # explicitly flush all writes to fout, because sometimes the writer
+ # can hang and we won't get to the termination code
+! set dbg_fout [open $dbg_filename w]
+ set _cgi(input) $dbg_filename
+ catch {fconfigure $dbg_fout -translation binary}
+ }
+--- 1342,1348 ----
+ set dbg_filename [file join $_cgi(tmpdir) CGIdbg.[pid]]
+ # explicitly flush all writes to fout, because sometimes the writer
+ # can hang and we won't get to the termination code
+! set dbg_fout [open $dbg_filename w $_cgi(tmpperms)]
+ set _cgi(input) $dbg_filename
+ catch {fconfigure $dbg_fout -translation binary}
+ }
+***************
+*** 1409,1415 ****
+
+ # read the part into a file
+ set foutname [file join $_cgi(tmpdir) CGI[pid].[incr _cgi(file,filecount)]]
+! set fout [open $foutname w]
+ # "catch" permits this to work with Tcl 7.4
+ catch {fconfigure $fout -translation binary}
+ _cgi_set_uservar $varname [list $foutname $filename $conttype]
+--- 1409,1415 ----
+
+ # read the part into a file
+ set foutname [file join $_cgi(tmpdir) CGI[pid].[incr _cgi(file,filecount)]]
+! set fout [open $foutname w $_cgi(tmpperms)]
+ # "catch" permits this to work with Tcl 7.4
+ catch {fconfigure $fout -translation binary}
+ _cgi_set_uservar $varname [list $foutname $filename $conttype]
+***************
+*** 1452,1457 ****
+--- 1452,1458 ----
+ } else {
+ # read the part into a variable
+ set val ""
++ set blanks 0
+ while {1} {
+ if {-1 == [gets $fin buf]} break
+ if {[info exists dbg_fout]} {puts $dbg_fout $buf; flush $dbg_fout}
+***************
+*** 1463,1468 ****
+--- 1464,1479 ----
+ append val \n
+ }
+ regexp (.*)\r$ $buf dummy buf
++ if {[info exists blanks]} {
++ if {0!=[string compare $buf ""]} {
++ if {$blanks} {
++ append val [string repeat \n [incr blanks]]
++ }
++ unset blanks
++ } else {
++ incr blanks
++ }
++ }
+ append val $buf
+ }
+ _cgi_set_uservar $varname $val
+***************
+*** 1482,1488 ****
+ # save file for debugging purposes
+ set dbg_filename [file join $_cgi(tmpdir) CGIdbg.[pid]]
+ set _cgi(input) $dbg_filename
+! spawn -open [open $dbg_filename w]
+ set dbg_sid $spawn_id
+ }
+ spawn -open $fin
+--- 1493,1499 ----
+ # save file for debugging purposes
+ set dbg_filename [file join $_cgi(tmpdir) CGIdbg.[pid]]
+ set _cgi(input) $dbg_filename
+! spawn -open [open $dbg_filename w $_cgi(tmpperms)]
+ set dbg_sid $spawn_id
+ }
+ spawn -open $fin
+***************
+*** 1579,1585 ****
+
+ # read the part into a file
+ set foutname [file join $_cgi(tmpdir) CGI[pid].[incr _cgi(file,filecount)]]
+! spawn -open [open $foutname w]
+ set fout_sid $spawn_id
+
+ _cgi_set_uservar $varname [list $foutname $filename $conttype]
+--- 1590,1596 ----
+
+ # read the part into a file
+ set foutname [file join $_cgi(tmpdir) CGI[pid].[incr _cgi(file,filecount)]]
+! spawn -open [open $foutname w $_cgi(tmpperms)]
+ set fout_sid $spawn_id
+
+ _cgi_set_uservar $varname [list $foutname $filename $conttype]
+***************
+*** 2187,2202 ****
+
+ flush $_cgi(mailfid)
+
+! if {[file executable /usr/lib/sendmail]} {
+! exec /usr/lib/sendmail -t -odb < $_cgi(mailfile)
+! # Explanation:
+! # -t means: pick up recipient from body
+! # -odb means: deliver in background
+! # note: bogus local address cause sendmail to fail immediately
+! } elseif {[file executable /usr/sbin/sendmail]} {
+! exec /usr/sbin/sendmail -t -odb < $_cgi(mailfile)
+! # sendmail is in /usr/sbin on some BSD4.4-derived systems.
+! } else {
+ # fallback for sites without sendmail
+
+ if {0==[info exists _cgi(mail_relay)]} {
+--- 2198,2215 ----
+
+ flush $_cgi(mailfid)
+
+! foreach sendmail in $_cgi(sendmail) {
+! if {[file executable $sendmail]} {
+! exec $sendmail -t -odb < $_cgi(mailfile)
+! # Explanation:
+! # -t means: pick up recipient from body
+! # -odb means: deliver in background
+! # note: bogus local address cause sendmail to fail immediately
+! set sent 1
+! }
+! }
+!
+! if {0==[info exists sent]} {
+ # fallback for sites without sendmail
+
+ if {0==[info exists _cgi(mail_relay)]} {
+***************
+*** 2241,2246 ****
+--- 2254,2265 ----
+ set _cgi(mail_relay) $host
+ }
+
++ proc cgi_sendmail {path} {
++ global _cgi
++
++ set _cgi(sendmail) $path
++ }
++
+ ##################################################
+ # cookie support
+ ##################################################
+***************
+*** 2416,2422 ****
+ ##################################################
+
+ proc cgi_stylesheet {href} {
+! puts "<link rel=stylesheet href=\"$href\" type=\"text/css\"/>"
+ }
+
+ proc cgi_span {args} {
+--- 2435,2441 ----
+ ##################################################
+
+ proc cgi_stylesheet {href} {
+! cgi_puts "<link rel=stylesheet href=\"$href\" type=\"text/css\"/>"
+ }
+
+ proc cgi_span {args} {
+***************
+*** 2545,2550 ****
+--- 2564,2584 ----
+ }
+
+ ##################################################
++ # temporary file procedures
++ ##################################################
++
++ # set appropriate temporary file modes
++ proc cgi_tmpfile_permissions {{mode ""}} {
++ global _cgi
++
++ if {[string length $mode]} {
++ set _cgi(tmpperms) $mode
++ }
++
++ return $_cgi(tmpperms)
++ }
++
++ ##################################################
+ # user-defined procedures
+ ##################################################
+
+***************
+*** 2604,2615 ****
+--- 2638,2655 ----
+ switch $tcl_platform(platform) {
+ unix {
+ set _cgi(tmpdir) /tmp
++ set _cgi(tmpperms) 0644
++ set _cgi(sendmail) [list /usr/lib/sendmail /usr/sbin/sendmail]
+ } macintosh {
+ set _cgi(tmpdir) [pwd]
++ set _cgi(tmpperms) {}
++ set _cgi(sendmail) {}
+ } default {
+ set _cgi(tmpdir) [pwd]
+ catch {set _cgi(tmpdir) $env(TMP)}
+ catch {set _cgi(tmpdir) $env(TEMP)}
++ set _cgi(tmpperms) {}
++ set _cgi(sendmail) {}
+ }
+ }
+
diff --git a/web/src/cgi.tcl-1.10/README b/web/src/cgi.tcl-1.10/README
new file mode 100644
index 00000000..4c7381fe
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/README
@@ -0,0 +1,140 @@
+This is the README to cgi.tcl, a library of Tcl procedures to assist
+in writing CGI scripts. Review the HISTORY file for significant changes.
+
+The cgi.tcl home page is http://expect.nist.gov/cgi.tcl
+
+--------------------
+Introduction
+--------------------
+
+This is the README file for cgi.tcl, a set of procedures for writing
+CGI scripts in Tcl. The procedures implement the code described in
+the paper "Writing CGI scripts in Tcl" which was published in the
+Proceedings of the Fourth Tcl Workshop (Tcl '96).
+
+--------------------
+Getting Started
+--------------------
+
+First, read the paper "Writing CGI Scripts in Tcl", from Tcl '96. If
+you can't find the paper in this archive, it can also be found at:
+
+ http://www.nist.gov/msidlibrary/doc/libes96c.ps
+
+That paper will give you a lot of good ideas for using Tcl, not only
+with CGI but plain everyday HTML as well.
+
+Next, try some of the examples in the example directory. Please read
+the "Instructions" section in example/README first.
+
+A rough draft of complete documentation of the individual functions
+can be found in ref.txt in the doc directory.
+
+Note that you are expected to understand Tcl. I'm not going to
+explain how to write Tcl scripts here. (If you're looking for a Tcl
+tutorial, please consider my Expect book which includes a very nice
+tutorial on Tcl.)
+
+Similarly, you are expected to understand HTML. There are plenty of
+web tutorials and books on it. Go read one. You don't have to become
+an expert on HTML, but it is important to get a feel for it. (The
+cgi.tcl package will take care of the details.) If you plan to do
+CGI, you should know a couple more basic things. Here's a simple CGI
+intro:
+
+ http://hoohoo.ncsa.uiuc.edu/cgi/intro.html
+
+
+--------------------
+Status
+--------------------
+
+The library is reasonably complete. It supports forms, tables,
+cookies, Netscape extensions, file upload, plug-ins, etc, etc. On the
+other hand, there are some things missing - such as certain deprecated
+things in HTML, things I can't believe anyone would use, things that
+are special extensions to a browser I'm not familiar with, etc.
+
+This library should run on any system (UNIX, Win, or Mac) which
+supports Tcl 8.1 or later.
+
+----------------------
+Examples
+----------------------
+
+This distribution contains example scripts. They can be found in the
+example directory of this distribution. Please read the
+"Instructions" section in example/README first.
+
+--------------------
+Installation
+--------------------
+
+If you are on UNIX, read the INSTALL file.
+If you are on W95/NT, read the install.win file.
+If you are on Mac, read the install.mac file.
+
+--------------------
+How to get the latest version of this code
+--------------------
+
+The latest version of this code may be received from:
+
+ http://expect.nist.gov/cgi.tcl/cgi.tcl.tar.gz
+or ftp://ftp.nist.gov/mel/div826/subject/expect/cgi.tcl/cgi.tcl.tar.gz
+
+--------------------
+Support from Don Libes or NIST
+--------------------
+
+Although I can't promise anything in the way of support, I'd be
+interested to hear about your experiences using it (good or bad). I'm
+also interested in hearing bug reports and suggestions for improvement
+even though I can't promise to implement them.
+
+If you send me a bug, fix, or question, include the version of
+cgi.tcl, version of Tcl, and name and version of the OS that you are
+using. Before sending mail, it may be helpful to verify that your
+problem still exists in the latest version. You can check on the
+current release and whether it addresses your problems by retrieving
+the latest HISTORY file (see "History" above).
+
+
+Awards, love letters, and bug reports may be sent to:
+
+Don Libes
+National Institute of Standards and Technology
+Bldg 220, Rm A-127
+Gaithersburg, MD 20899
+(301) 975-3535
+libes@nist.gov
+
+I hereby place this software in the public domain. NIST and I would
+appreciate credit if this program or parts of it are used.
+
+Design and implementation of this program was funded primarily by
+myself. Funding contributors include the NIST Automated Manufacturing
+Research Facility (funded by the Navy Manufacturing Technology
+Program), the NIST Scientific and Technical Research Services, the
+ARPA Persistent Object Bases project and the Computer-aided
+Acquisition and the Logistic Support (CALS) program of the Office of
+the Secretary of Defense.
+
+--------------------
+Support for Don Libes or NIST
+--------------------
+
+NIST accepts external funding and other resources (hardware, software,
+and personnel). This can be a fine way to work more closely with NIST
+and encourage particular areas of research.
+
+Funding can be earmarked for specific purposes or for less-specific
+purposes. For example, if you simply like the work I do, you can
+contribute directly to my funding which will reduce the amount of time
+I have to spend writing proposals and submitting them to other people
+for funding on my own.
+
+I can also participate in the NIST Fellows program allowing me to
+spend several months to a year working directly with your company and
+potentially even at your location.
+
diff --git a/web/src/cgi.tcl-1.10/README.UW b/web/src/cgi.tcl-1.10/README.UW
new file mode 100644
index 00000000..7dfe161c
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/README.UW
@@ -0,0 +1,23 @@
+This is the README.UW of the cgi.tcl library as included with the Web
+Alpine distribution.
+
+Web Alpine page generation is based heavily on the routines provided
+by the cgi.tcl library. It's shown to be a robust and flexible
+platform for generating dynamic web-based content.
+
+This cgi.tcl library as included with the Web Alpine application has
+been slightly modified. The small changes, which can be found in
+PATCH.UW, amount to a few things:
+
+ 1) A hook to allow for temporary file permission setting
+
+ 2) A hook to allow for resetting (and specifically, unsetting)
+ the path to sendmail used for error reporting
+
+ 3) A few fixes for misdirected output ("puts" --> "cgi_puts")
+
+ 4) Change to _cgi_input_multipart to preserve leading
+ newlines
+
+For comments or questions regarding the Web Alpine application
+send comments to <alpine-contact@cac.washington.edu>
diff --git a/web/src/cgi.tcl-1.10/cgi.tcl.in b/web/src/cgi.tcl-1.10/cgi.tcl.in
new file mode 100644
index 00000000..df426c08
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/cgi.tcl.in
@@ -0,0 +1,2659 @@
+##################################################
+#
+# cgi.tcl - routines for writing CGI scripts in Tcl
+# Author: Don Libes <libes@nist.gov>, January '95
+#
+# These routines implement the code described in the paper
+# "Writing CGI scripts in Tcl" which appeared in the Tcl '96 conference.
+# Please read the paper before using this code. The paper is:
+# http://expect.nist.gov/doc/cgi.pdf
+#
+##################################################
+
+##################################################
+# http header support
+##################################################
+
+proc cgi_http_head {args} {
+ global _cgi env errorInfo
+
+ if {[info exists _cgi(http_head_done)]} return
+
+ set _cgi(http_head_in_progress) 1
+
+ if {0 == [llength $args]} {
+ cgi_content_type
+ } else {
+ if {[catch {uplevel 1 [lindex $args 0]} errMsg]} {
+ set savedInfo $errorInfo
+ cgi_content_type
+ }
+ }
+ cgi_puts ""
+
+ unset _cgi(http_head_in_progress)
+ set _cgi(http_head_done) 1
+
+ if {[info exists savedInfo]} {
+ error $errMsg $savedInfo
+ }
+}
+
+# avoid generating http head if not in CGI environment
+# to allow generation of pure HTML files
+proc _cgi_http_head_implicit {} {
+ global env
+
+ if {[info exists env(REQUEST_METHOD)]} cgi_http_head
+}
+
+proc cgi_status {num str} {
+ global _cgi
+
+ if {[info exists _cgi(http_status_done)]} return
+ set _cgi(http_status_done) 1
+ cgi_puts "Status: $num $str"
+}
+
+# If these are called manually, they automatically generate the extra newline
+
+proc cgi_content_type {args} {
+ global _cgi
+
+ if {0==[llength $args]} {
+ set t text/html
+ } else {
+ set t [lindex $args 0]
+ if {[regexp ^multipart/ $t]} {
+ set _cgi(multipart) 1
+ }
+ }
+
+ if {[info exists _cgi(http_head_in_progress)]} {
+ cgi_puts "Content-type: $t"
+ } else {
+ cgi_http_head [list cgi_content_type $t]
+ }
+}
+
+proc cgi_redirect {t} {
+ global _cgi
+
+ if {[info exists _cgi(http_head_in_progress)]} {
+ cgi_status 302 Redirected
+ cgi_puts "Uri: $t"
+ cgi_puts "Location: $t"
+ } else {
+ cgi_http_head {
+ cgi_redirect $t
+ }
+ }
+}
+
+# deprecated, use cgi_redirect
+proc cgi_location {t} {
+ global _cgi
+
+ if {[info exists _cgi(http_head_in_progress)]} {
+ cgi_puts "Location: $t"
+ } else {
+ cgi_http_head "cgi_location $t"
+ }
+}
+
+proc cgi_target {t} {
+ global _cgi
+
+ if {![info exists _cgi(http_head_in_progress)]} {
+ error "cgi_target must be set from within cgi_http_head."
+ }
+ cgi_puts "Window-target: $t"
+}
+
+# Make client retrieve url in this many seconds ("client pull").
+# With no 2nd arg, current url is retrieved.
+proc cgi_refresh {seconds {url ""}} {
+ global _cgi
+
+ if {![info exists _cgi(http_head_in_progress)]} {
+ error "cgi_refresh must be set from within cgi_http_head. Try using cgi_http_equiv instead."
+ }
+ cgi_put "Refresh: $seconds"
+
+ if {0!=[string compare $url ""]} {
+ cgi_put "; $url"
+ }
+ cgi_puts ""
+}
+
+# Example: cgi_pragma no-cache
+proc cgi_pragma {arg} {
+ global _cgi
+
+ if {![info exists _cgi(http_head_in_progress)]} {
+ error "cgi_pragma must be set from within cgi_http_head."
+ }
+ cgi_puts "Pragma: $arg"
+}
+
+##################################################
+# support for debugging or other crucial things we need immediately
+##################################################
+
+proc cgi_comment {args} {} ;# need this asap
+
+proc cgi_html_comment {args} {
+ regsub -all {>} $args {\&gt;} args
+ cgi_put "<!--[_cgi_list_to_string $args] -->"
+}
+
+set _cgi(debug) -off
+proc cgi_debug {args} {
+ global _cgi
+
+ set old $_cgi(debug)
+ set arg [lindex $args 0]
+ if {$arg == "-on"} {
+ set _cgi(debug) -on
+ set args [lrange $args 1 end]
+ } elseif {$arg == "-off"} {
+ set _cgi(debug) -off
+ set args [lrange $args 1 end]
+ } elseif {[regexp "^-t" $arg]} {
+ set temp 1
+ set _cgi(debug) -on
+ set args [lrange $args 1 end]
+ } elseif {[regexp "^-noprint$" $arg]} {
+ set noprint 1
+ set args [lrange $args 1 end]
+ }
+
+ set arg [lindex $args 0]
+ if {$arg == "--"} {
+ set args [lrange $args 1 end]
+ }
+
+ if {[llength $args]} {
+ if {$_cgi(debug) == "-on"} {
+
+ _cgi_close_tag
+ # force http head and open html, head, body
+ catch {
+ if {[info exists noprint]} {
+ uplevel 1 [lindex $args 0]
+ } else {
+ cgi_html {
+ cgi_head {
+ cgi_title "debugging before complete HTML head"
+ }
+ # force body open and leave open
+ _cgi_body_start
+ uplevel 1 [lindex $args 0]
+ # bop back out to catch, so we don't close body
+ error "ignore"
+ }
+ }
+ }
+ }
+ }
+
+ if {[info exists temp]} {
+ set _cgi(debug) $old
+ }
+ return $old
+}
+
+proc cgi_uid_check {user} {
+ global env
+
+ # leave in so old scripts don't blow up
+ if {[regexp "^-off$" $user]} return
+
+ if {[info exists env(USER)]} {
+ set whoami $env(USER)
+ } elseif {0==[catch {exec whoami} whoami]} {
+ # "who am i" on some Linux hosts returns "" so try whoami first
+ } elseif {0==[catch {exec who am i} whoami]} {
+ # skip over "host!"
+ regexp "(.*!)?(\[^ \t]*)" $whoami dummy dummy whoami
+ } elseif {0==[catch {package require registry}]} {
+ set whoami [registry get HKEY_LOCAL_MACHINE\\Network\\Logon username]
+ } else {
+ set whoami $user ;# give up and let go
+ }
+ if {$whoami != "$user"} {
+ error "Warning: This CGI script expects to run with uid \"$user\". However, this script is running as \"$whoami\"."
+ }
+}
+
+# print out elements of an array
+# like Tcl's parray, but formatted for browser
+proc cgi_parray {a {pattern *}} {
+ upvar 1 $a array
+ if {![array exists array]} {
+ error "\"$a\" isn't an array"
+ }
+
+ set maxl 0
+ foreach name [lsort [array names array $pattern]] {
+ if {[string length $name] > $maxl} {
+ set maxl [string length $name]
+ }
+ }
+ cgi_preformatted {
+ set maxl [expr {$maxl + [string length $a] + 2}]
+ foreach name [lsort [array names array $pattern]] {
+ set nameString [format %s(%s) $a $name]
+ cgi_puts [cgi_quote_html [format "%-*s = %s" $maxl $nameString $array($name)]]
+ }
+ }
+}
+
+proc cgi_eval {cmd} {
+ global env _cgi
+
+ # put cmd somewhere that uplevel can find it
+ set _cgi(body) $cmd
+
+ uplevel 1 {
+ global env _cgi errorInfo
+
+ if {1==[catch $_cgi(body) errMsg]} {
+ # error occurred, handle it
+ set _cgi(errorInfo) $errorInfo
+
+ if {![info exists env(REQUEST_METHOD)]} {
+ puts stderr $_cgi(errorInfo)
+ return
+ }
+ # the following code is all to force browsers into a state
+ # such that diagnostics can be reliably shown
+
+ # close irrelevant things
+ _cgi_close_procs
+ # force http head and open html, head, body
+ cgi_html {
+ cgi_body {
+ if {[info exists _cgi(client_error)]} {
+ cgi_h3 "Client Error"
+ cgi_p "$errMsg Report this to your system administrator or browser vendor."
+ } else {
+ cgi_put [cgi_anchor_name cgierror]
+ cgi_h3 "An internal error was detected in the service\
+ software. The diagnostics are being emailed to\
+ the service system administrator ($_cgi(admin_email))."
+
+ if {$_cgi(debug) == "-on"} {
+ cgi_puts "Heck, since you're debugging, I'll show you the\
+ errors right here:"
+ # suppress formatting
+ cgi_preformatted {
+ cgi_puts [cgi_quote_html $_cgi(errorInfo)]
+ }
+ } else {
+ cgi_mail_start $_cgi(admin_email)
+ cgi_mail_add "Subject: [cgi_name] CGI problem"
+ cgi_mail_add
+ cgi_mail_add "CGI environment:"
+ cgi_mail_add "REQUEST_METHOD: $env(REQUEST_METHOD)"
+ cgi_mail_add "SCRIPT_NAME: $env(SCRIPT_NAME)"
+ # this next few things probably don't need
+ # a catch but I'm not positive
+ catch {cgi_mail_add "HTTP_USER_AGENT: $env(HTTP_USER_AGENT)"}
+ catch {cgi_mail_add "HTTP_REFERER: $env(HTTP_REFERER)"}
+ catch {cgi_mail_add "HTTP_HOST: $env(HTTP_HOST)"}
+ catch {cgi_mail_add "REMOTE_HOST: $env(REMOTE_HOST)"}
+ catch {cgi_mail_add "REMOTE_ADDR: $env(REMOTE_ADDR)"}
+ cgi_mail_add "cgi.tcl version: @CGI_VERSION_FULL@"
+ cgi_mail_add "input:"
+ catch {cgi_mail_add $_cgi(input)}
+ cgi_mail_add "cookie:"
+ catch {cgi_mail_add $env(HTTP_COOKIE)}
+ cgi_mail_add "errorInfo:"
+ cgi_mail_add "$_cgi(errorInfo)"
+ cgi_mail_end
+ }
+ }
+ } ;# end cgi_body
+ } ;# end cgi_html
+ } ;# end catch
+ } ;# end uplevel
+}
+
+# return true if cgi_eval caught an error
+proc cgi_error_occurred {} {
+ global _cgi
+
+ return [info exists _cgi(errorInfo)]
+}
+
+##################################################
+# CGI URL creation
+##################################################
+
+# declare location of root of CGI files
+# this allows all CGI references to be relative in the source
+# making it easy to move everything in the future
+# If you have multiple roots, just don't call this.
+proc cgi_root {args} {
+ global _cgi
+
+ if {[llength $args]} {
+ set _cgi(root) [lindex $args 0]
+ } else {
+ set _cgi(root)
+ }
+}
+
+# make a URL for a CGI script
+proc cgi_cgi {args} {
+ global _cgi
+
+ set root $_cgi(root)
+ if {0!=[string compare $root ""]} {
+ if {![regexp "/$" $root]} {
+ append root "/"
+ }
+ }
+
+ set suffix [cgi_suffix]
+
+ set arg [lindex $args 0]
+ if {0==[string compare $arg "-suffix"]} {
+ set suffix [lindex $args 1]
+ set args [lrange $args 2 end]
+ }
+
+ if {[llength $args]==1} {
+ return $root[lindex $args 0]$suffix
+ } else {
+ return $root[lindex $args 0]$suffix?[join [lrange $args 1 end] &]
+ }
+}
+
+proc cgi_suffix {args} {
+ global _cgi
+ if {[llength $args] > 0} {
+ set _cgi(suffix) [lindex $args 0]
+ }
+ if {![info exists _cgi(suffix)]} {
+ return .cgi
+ } else {
+ return $_cgi(suffix)
+ }
+}
+
+proc cgi_cgi_set {variable value} {
+ regsub -all {%} $value "%25" value
+ regsub -all {&} $value "%26" value
+ regsub -all {\+} $value "%2b" value
+ regsub -all { } $value "+" value
+ regsub -all {=} $value "%3d" value
+ regsub -all {#} $value "%23" value
+ regsub -all {/} $value "%2f" value ;# Added...
+ return $variable=$value
+}
+
+##################################################
+# URL dictionary support
+##################################################
+
+proc cgi_link {args} {
+ global _cgi_link
+
+ set tag [lindex $args 0]
+ switch -- [llength $args] {
+ 1 {
+ set label $_cgi_link($tag,label)
+ } 2 {
+ set label [lindex $args end]
+ } default {
+ set _cgi_link($tag,label) [set label [lindex $args 1]]
+ set _cgi_link($tag,url) [lrange $args 2 end]
+ }
+ }
+
+ return [eval cgi_url [list $label] $_cgi_link($tag,url)]
+}
+
+# same as above but for images
+# note: uses different namespace
+proc cgi_imglink {args} {
+ global _cgi_imglink
+
+ set tag [lindex $args 0]
+ if {[llength $args] >= 2} {
+ set _cgi_imglink($tag) [eval cgi_img [lrange $args 1 end]]
+ }
+ return $_cgi_imglink($tag)
+}
+
+proc cgi_link_label {tag} {
+ global _cgi_link
+ return $_cgi_link($tag,label)
+}
+
+proc cgi_link_url {tag} {
+ global _cgi_link
+ return $_cgi_link($tag,url)
+}
+
+##################################################
+# hyperlink support
+##################################################
+
+# construct a hyperlink labeled "display"
+# last arg is the link destination
+# any other args are passed through into <a> display
+proc cgi_url {display args} {
+ global _cgi
+
+ set buf "<a href=\"[lindex $args 0]\""
+ foreach a [lrange $args 1 end] {
+ if {[regexp $_cgi(attr,regexp) $a dummy attr str]} {
+ append buf " $attr=\"$str\""
+ } else {
+ append buf " $a"
+ }
+ }
+ return "$buf>$display</a>"
+}
+
+# generate an image reference (<img ...>)
+# first arg is image url
+# other args are passed through into <img> tag
+proc cgi_img {args} {
+ global _cgi
+
+ set buf "<img src=\"[lindex $args 0]\""
+ foreach a [lrange $args 1 end] {
+ if {[regexp "^(alt|lowsrc|usemap)=(.*)" $a dummy attr str]} {
+ append buf " $attr=[cgi_dquote_html $str]"
+ } elseif {[regexp $_cgi(attr,regexp) $a dummy attr str]} {
+ append buf " $attr=\"$str\""
+ } else {
+ append buf " $a"
+ }
+ }
+ return "$buf />"
+}
+
+# names an anchor so that it can be linked to
+proc cgi_anchor_name {name} {
+ return "<a name=\"$name\"/>"
+}
+
+proc cgi_base {args} {
+ global _cgi
+
+ cgi_put "<base"
+ foreach a $args {
+ if {[regexp "^href=(.*)" $a dummy str]} {
+ cgi_put " href=[cgi_dquote_html $str]"
+ } elseif {[regexp $_cgi(attr,regexp) $a dummy attr str]} {
+ cgi_put " $attr=\"$str\""
+ } else {
+ cgi_put " $a"
+ }
+ }
+ cgi_puts " />"
+}
+
+##################################################
+# quoting support
+##################################################
+
+if {[info tclversion] >= 8.2} {
+ proc cgi_unquote_input buf {
+ # rewrite "+" back to space
+ # protect \ from quoting another \ and throwing off other things
+ # replace line delimiters with newlines
+ set buf [string map -nocase [list + { } "\\" "\\\\" %0d%0a \n] $buf]
+
+ # prepare to process all %-escapes
+ regsub -all -nocase {%([a-f0-9][a-f0-9])} $buf {\\u00\1} buf
+
+ # process \u unicode mapped chars
+ encoding convertfrom $::_cgi(queryencoding) \
+ [subst -novar -nocommand $buf]
+ }
+} elseif {[info tclversion] >= 8.1} {
+ proc cgi_unquote_input buf {
+ # rewrite "+" back to space
+ regsub -all {\+} $buf { } buf
+ # protect \ from quoting another \ and throwing off other things
+ regsub -all {\\} $buf {\\\\} buf
+
+ # replace line delimiters with newlines
+ regsub -all -nocase "%0d%0a" $buf "\n" buf
+
+ # prepare to process all %-escapes
+ regsub -all -nocase {%([a-f0-9][a-f0-9])} $buf {\\u00\1} buf
+ # process \u unicode mapped chars
+ return [subst -novar -nocommand $buf]
+ }
+} else {
+ proc cgi_unquote_input {buf} {
+ # rewrite "+" back to space
+ regsub -all {\+} $buf { } buf
+ # protect \ from quoting another \ and throwing off other things first
+ # protect $ from doing variable expansion
+ # protect [ from doing evaluation
+ # protect " from terminating string
+ regsub -all {([\\["$])} $buf {\\\1} buf
+
+ # replace line delimiters with newlines
+ regsub -all -nocase "%0d%0a" $buf "\n" buf
+ # Mosaic sends just %0A. This is handled in the next command.
+
+ # prepare to process all %-escapes
+ regsub -all -nocase {%([a-f0-9][a-f0-9])} $buf {[format %c 0x\1]} buf
+ # process %-escapes and undo all protection
+ eval return \"$buf\"
+ }
+}
+
+# return string but with html-special characters escaped,
+# necessary if you want to send unknown text to an html-formatted page.
+proc cgi_quote_html {s} {
+ regsub -all {&} $s {\&amp;} s ;# must be first!
+ regsub -all {"} $s {\&quot;} s
+ regsub -all {<} $s {\&lt;} s
+ regsub -all {>} $s {\&gt;} s
+ return $s
+}
+
+proc cgi_dquote_html {s} {
+ return \"[cgi_quote_html $s]\"
+}
+
+# return string quoted appropriately to appear in a url
+proc cgi_quote_url {in} {
+ regsub -all {%} $in "%25" in
+ regsub -all {\+} $in "%2b" in
+ regsub -all { } $in "%20" in
+ regsub -all {"} $in "%22" in
+ regsub -all {\?} $in "%3f" in
+ return $in
+}
+
+##################################################
+# short or single paragraph support
+##################################################
+
+proc cgi_br {args} {
+ cgi_put "<br"
+ if {[llength $args]} {
+ cgi_put "[_cgi_list_to_string $args]"
+ }
+ cgi_put " />"
+}
+
+# generate cgi_h1 and others
+for {set _cgi(tmp) 1} {$_cgi(tmp)<8} {incr _cgi(tmp)} {
+ proc cgi_h$_cgi(tmp) {{args}} "eval cgi_h $_cgi(tmp) \$args"
+}
+proc cgi_h {num args} {
+ cgi_put "<h$num"
+ if {[llength $args] > 1} {
+ cgi_put "[_cgi_lrange $args 0 [expr [llength $args]-2]]"
+ set args [lrange $args end end]
+ }
+ cgi_put ">[lindex $args 0]</h$num>"
+}
+
+proc cgi_p {args} {
+ cgi_put "<p"
+ if {[llength $args] > 1} {
+ cgi_put "[_cgi_lrange $args 0 [expr [llength $args]-2]]"
+ set args [lrange $args end end]
+ }
+ cgi_put ">[lindex $args 0]</p>"
+}
+
+proc cgi_address {s} {cgi_put <address>$s</address>}
+proc cgi_blockquote {s} {cgi_puts <blockquote>$s</blockquote>}
+
+##################################################
+# long or multiple paragraph support
+##################################################
+
+# Shorthand for <div align=center>. We used to use <center> tags but that
+# is now officially unsupported.
+proc cgi_center {cmd} {
+ uplevel 1 "cgi_division align=center [list $cmd]"
+}
+
+proc cgi_division {args} {
+ cgi_put "<div"
+ _cgi_close_proc_push "cgi_put </div>"
+
+ if {[llength $args]} {
+ cgi_put "[_cgi_lrange $args 0 [expr {[llength $args]-2}]]"
+ }
+ cgi_put ">"
+ uplevel 1 [lindex $args end]
+ _cgi_close_proc
+}
+
+proc cgi_preformatted {args} {
+ cgi_put "<pre"
+ _cgi_close_proc_push "cgi_put </pre>"
+
+ if {[llength $args]} {
+ cgi_put "[_cgi_lrange $args 0 [expr [llength $args]-2]]"
+ }
+ cgi_put ">"
+ uplevel 1 [lindex $args end]
+ _cgi_close_proc
+}
+
+##################################################
+# list support
+##################################################
+
+proc cgi_li {args} {
+ cgi_put <li
+ if {[llength $args] > 1} {
+ cgi_put "[_cgi_lrange $args 0 [expr [llength $args]-2]]"
+ }
+ cgi_put ">[lindex $args end]</li>"
+}
+
+proc cgi_number_list {args} {
+ cgi_put "<ol"
+ _cgi_close_proc_push "cgi_put </ol>"
+
+ if {[llength $args] > 1} {
+ cgi_put "[_cgi_lrange $args 0 [expr [llength $args]-2]]"
+ }
+ cgi_put ">"
+ uplevel 1 [lindex $args end]
+
+ _cgi_close_proc
+}
+
+proc cgi_bullet_list {args} {
+ cgi_put "<ul"
+ _cgi_close_proc_push "cgi_put </ul>"
+
+ if {[llength $args] > 1} {
+ cgi_put "[_cgi_lrange $args 0 [expr [llength $args]-2]]"
+ }
+ cgi_put ">"
+ uplevel 1 [lindex $args end]
+
+ _cgi_close_proc
+}
+
+# Following two are normally used from within definition lists
+# but are actually paragraph types on their own.
+proc cgi_term {s} {cgi_put <dt>$s</dt>}
+proc cgi_term_definition {s} {cgi_put <dd>$s</dd>}
+
+proc cgi_definition_list {cmd} {
+ cgi_put "<dl>"
+ _cgi_close_proc_push "cgi_put </dl>"
+
+ uplevel 1 $cmd
+ _cgi_close_proc
+}
+
+proc cgi_menu_list {cmd} {
+ cgi_put "<menu>"
+ _cgi_close_proc_push "cgi_put </menu>"
+
+ uplevel 1 $cmd
+ _cgi_close_proc
+}
+proc cgi_directory_list {cmd} {
+ cgi_put "<dir>"
+ _cgi_close_proc_push "cgi_put </dir>"
+
+ uplevel 1 $cmd
+ _cgi_close_proc
+}
+
+##################################################
+# text support
+##################################################
+
+proc cgi_put {s} {cgi_puts -nonewline $s}
+
+# some common special characters
+proc cgi_lt {} {return "&lt;"}
+proc cgi_gt {} {return "&gt;"}
+proc cgi_amp {} {return "&amp;"}
+proc cgi_quote {} {return "&quot;"}
+proc cgi_enspace {} {return "&ensp;"}
+proc cgi_emspace {} {return "&emsp;"}
+proc cgi_nbspace {} {return "&#160;"} ;# nonbreaking space
+proc cgi_tm {} {return "&#174;"} ;# registered trademark
+proc cgi_copyright {} {return "&#169;"}
+proc cgi_isochar {n} {return "&#$n;"}
+proc cgi_breakable {} {return "<wbr />"}
+
+proc cgi_unbreakable_string {s} {return "<nobr>$s</nobr>"}
+proc cgi_unbreakable {cmd} {
+ cgi_put "<nobr>"
+ _cgi_close_proc_push "cgi_put </nobr>"
+ uplevel 1 $cmd
+ _cgi_close_proc
+}
+
+proc cgi_nl {args} {
+ set buf "<br"
+ if {[llength $args]} {
+ append buf "[_cgi_list_to_string $args]"
+ }
+ return "$buf />"
+}
+
+proc cgi_bold {s} {return "<b>$s</b>"}
+proc cgi_italic {s} {return "<i>$s</i>"}
+proc cgi_underline {s} {return "<u>$s</u>"}
+proc cgi_strikeout {s} {return "<s>$s</s>"}
+proc cgi_subscript {s} {return "<sub>$s</sub>"}
+proc cgi_superscript {s} {return "<sup>$s</sup>"}
+proc cgi_typewriter {s} {return "<tt>$s</tt>"}
+proc cgi_blink {s} {return "<blink>$s</blink>"}
+proc cgi_emphasis {s} {return "<em>$s</em>"}
+proc cgi_strong {s} {return "<strong>$s</strong>"}
+proc cgi_cite {s} {return "<cite>$s</cite>"}
+proc cgi_sample {s} {return "<samp>$s</samp>"}
+proc cgi_keyboard {s} {return "<kbd>$s</kbd>"}
+proc cgi_variable {s} {return "<var>$s</var>"}
+proc cgi_definition {s} {return "<dfn>$s</dfn>"}
+proc cgi_big {s} {return "<big>$s</big>"}
+proc cgi_small {s} {return "<small>$s</small>"}
+
+proc cgi_basefont {size} {cgi_put "<basefont size=$size />"}
+
+proc cgi_font {args} {
+ global _cgi
+
+ set buf "<font"
+ foreach a [lrange $args 0 [expr [llength $args]-2]] {
+ if {[regexp $_cgi(attr,regexp) $a dummy attr str]} {
+ append buf " $attr=\"$str\""
+ } else {
+ append buf " $a"
+ }
+ }
+ return "$buf>[lindex $args end]</font>"
+}
+
+# take a cgi func and have it return what would normally print
+# This command is reentrant (that's why it's so complex).
+proc cgi_buffer {cmd} {
+ global _cgi
+
+ if {0==[info exists _cgi(returnIndex)]} {
+ set _cgi(returnIndex) 0
+ }
+
+ rename cgi_puts cgi_puts$_cgi(returnIndex)
+ incr _cgi(returnIndex)
+ set _cgi(return[set _cgi(returnIndex)]) ""
+
+ proc cgi_puts args {
+ global _cgi
+ upvar #0 _cgi(return[set _cgi(returnIndex)]) buffer
+
+ append buffer [lindex $args end]
+ if {[llength $args] == 1} {
+ append buffer $_cgi(buffer_nl)
+ }
+ }
+
+ # must restore things before allowing the eval to fail
+ # so catch here and rethrow later
+ if {[catch {uplevel 1 $cmd} errMsg]} {
+ global errorInfo
+ set savedInfo $errorInfo
+ }
+
+ # not necessary to put remainder of code in close_proc_push since it's
+ # all buffered anyway and hasn't yet put browser into a funky state.
+
+ set buffer $_cgi(return[set _cgi(returnIndex)])
+
+ incr _cgi(returnIndex) -1
+ rename cgi_puts ""
+ rename cgi_puts$_cgi(returnIndex) cgi_puts
+
+ if {[info exists savedInfo]} {
+ error $errMsg $savedInfo
+ }
+ return $buffer
+}
+
+set _cgi(buffer_nl) "\n"
+proc cgi_buffer_nl {nl} {
+ global _cgi
+
+ set old $_cgi(buffer_nl)
+ set _cgi(buffer_nl) $nl
+ return $old
+}
+
+##################################################
+# html and tags that can appear in html top-level
+##################################################
+
+proc cgi_html {args} {
+ set html [lindex $args end]
+ set argc [llength $args]
+ if {$argc > 1} {
+ eval _cgi_html_start [lrange $args 0 [expr {$argc-2}]]
+ } else {
+ _cgi_html_start
+ }
+ uplevel 1 $html
+ _cgi_html_end
+}
+
+proc _cgi_html_start {args} {
+ global _cgi
+
+ if {[info exists _cgi(html_in_progress)]} return
+ _cgi_http_head_implicit
+
+ set _cgi(html_in_progress) 1
+ cgi_doctype
+
+ append buf "<html"
+ foreach a $args {
+ if {[regexp $_cgi(attr,regexp) $a dummy attr str]} {
+ append buf " $attr=\"$str\""
+ } else {
+ append buf " $a"
+ }
+ }
+ cgi_puts "$buf>"
+}
+
+proc _cgi_html_end {} {
+ global _cgi
+ unset _cgi(html_in_progress)
+ set _cgi(html_done) 1
+ cgi_puts "</html>"
+}
+
+# force closure of all tags and exit without going through normal returns.
+# Very useful if you want to call exit from a deeply stacked CGI script
+# and still have the HTML be correct.
+proc cgi_exit {} {
+ _cgi_close_procs
+ cgi_html {cgi_body {}}
+ exit
+}
+
+##################################################
+# head support
+##################################################
+
+proc cgi_head {{head {}}} {
+ global _cgi
+
+ if {[info exists _cgi(head_done)]} {
+ return
+ }
+
+ # allow us to be recalled so that we can display errors
+ if {0 == [info exists _cgi(head_in_progress)]} {
+ _cgi_http_head_implicit
+ set _cgi(head_in_progress) 1
+ cgi_puts "<head>"
+ }
+
+ # prevent cgi_html (during error handling) from generating html tags
+ set _cgi(html_in_progress) 1
+ # don't actually generate html tags since there's nothing to clean
+ # them up
+
+ if {0 == [string length $head]} {
+ if {[catch {cgi_title}]} {
+ set head "cgi_title untitled"
+ }
+ }
+ uplevel 1 $head
+ if {![info exists _cgi(head_suppress_tag)]} {
+ cgi_puts "</head>"
+ } else {
+ unset _cgi(head_suppress_tag)
+ }
+
+ set _cgi(head_done) 1
+
+ # debugging can unset this in the uplevel above
+ catch {unset _cgi(head_in_progress)}
+}
+
+# with one arg: set, print, and return title
+# with no args: return title
+proc cgi_title {args} {
+ global _cgi
+
+ set title [lindex $args 0]
+
+ if {[llength $args]} {
+ _cgi_http_head_implicit
+
+ # we could just generate <head></head> tags, but head-level commands
+ # might follow so just suppress the head tags entirely
+ if {![info exists _cgi(head_in_progress)]} {
+ set _cgi(head_in_progress) 1
+ set _cgi(head_suppress_tag) 1
+ }
+
+ set _cgi(title) $title
+ cgi_puts "<title>$title</title>"
+ }
+ return $_cgi(title)
+}
+
+# This tag can only be called from with cgi_head.
+# example: cgi_http_equiv Refresh 1
+# There's really no reason to call this since it can be done directly
+# from cgi_http_head.
+proc cgi_http_equiv {type contents} {
+ _cgi_http_head_implicit
+ cgi_puts "<meta http-equiv=\"$type\" content=[cgi_dquote_html $contents]/>"
+}
+
+# Do whatever you want with meta tags.
+# Example: <meta name="author" content="Don Libes">
+proc cgi_meta {args} {
+ cgi_put "<meta"
+ foreach a $args {
+ if {[regexp "^(name|content|http-equiv)=(.*)" $a dummy attr str]} {
+ cgi_put " $attr=[cgi_dquote_html $str]"
+ } else {
+ cgi_put " $a"
+ }
+ }
+ cgi_puts " />"
+}
+
+proc cgi_relationship {rel href args} {
+ cgi_puts "<link rel=$rel href=\"$href\""
+ foreach a $args {
+ if {[regexp "^title=(.*)" $a dummy str]} {
+ cgi_put " title=[cgi_dquote_html $str]"
+ } elseif {[regexp "^type=(.*)" $a dummy str]} {
+ cgi_put " type=[cgi_dquote_html $str]"
+ } else {
+ cgi_put " $a"
+ }
+ }
+ cgi_puts "/>"
+}
+
+proc cgi_name {args} {
+ global _cgi
+
+ if {[llength $args]} {
+ set _cgi(name) [lindex $args 0]
+ }
+ return $_cgi(name)
+}
+
+##################################################
+# body and other top-level support
+##################################################
+
+proc cgi_body {args} {
+ global errorInfo errorCode _cgi
+
+ # allow user to "return" from the body without missing _cgi_body_end
+ if {1==[catch {
+ eval _cgi_body_start [lrange $args 0 [expr [llength $args]-2]]
+ uplevel 1 [lindex $args end]
+ } errMsg]} {
+ set savedInfo $errorInfo
+ set savedCode $errorCode
+ error $errMsg $savedInfo $savedCode
+ }
+ _cgi_body_end
+}
+
+proc _cgi_body_start {args} {
+ global _cgi
+ if {[info exists _cgi(body_in_progress)]} return
+
+ cgi_head
+
+ set _cgi(body_in_progress) 1
+
+ cgi_put "<body"
+ foreach a "$args $_cgi(body_args)" {
+ if {[regexp "^(background|bgcolor|text|link|vlink|alink|onLoad|onUnload)=(.*)" $a dummy attr str]} {
+ cgi_put " $attr=\"$str\""
+ } else {
+ cgi_put " $a"
+ }
+ }
+ cgi_puts ">"
+
+ cgi_debug {
+ global env
+ catch {cgi_puts "Input: <pre>$_cgi(input)</pre>"}
+ catch {cgi_puts "Cookie: <pre>$env(HTTP_COOKIE)</pre>"}
+ }
+
+ if {![info exists _cgi(errorInfo)]} {
+ uplevel 2 app_body_start
+ }
+}
+
+proc _cgi_body_end {} {
+ global _cgi
+ if {![info exists _cgi(errorInfo)]} {
+ uplevel 2 app_body_end
+ }
+ unset _cgi(body_in_progress)
+ cgi_puts "</body>"
+
+ if {[info exists _cgi(multipart)]} {
+ unset _cgi(http_head_done)
+ catch {unset _cgi(http_status_done)}
+ unset _cgi(head_done)
+ catch {unset _cgi(head_suppress_tag)}
+ }
+}
+
+proc cgi_body_args {args} {
+ global _cgi
+
+ set _cgi(body_args) $args
+}
+
+proc cgi_script {args} {
+ cgi_puts "<script[_cgi_lrange $args 0 [expr [llength $args]-2]]>"
+ _cgi_close_proc_push "cgi_puts </script>"
+
+ uplevel 1 [lindex $args end]
+
+ _cgi_close_proc
+}
+
+proc cgi_javascript {args} {
+ cgi_puts "<script[_cgi_lrange $args 0 [expr [llength $args]-2]]>"
+ cgi_puts "<!--- Hide script from browsers that don't understand JavaScript"
+ _cgi_close_proc_push {cgi_puts "// End hiding -->\n</script>"}
+
+ uplevel 1 [lindex $args end]
+
+ _cgi_close_proc
+}
+
+proc cgi_noscript {args} {
+ cgi_puts "<noscript[_cgi_lrange $args 0 [expr [llength $args]-2]]>"
+ _cgi_close_proc_push {cgi_puts "</noscript>"}
+
+ uplevel 1 [lindex $args end]
+
+ _cgi_close_proc
+}
+
+proc cgi_applet {args} {
+ cgi_puts "<applet[_cgi_lrange $args 0 [expr [llength $args]-2]]>"
+ _cgi_close_proc_push "cgi_puts </applet>"
+
+ uplevel 1 [lindex $args end]
+ _cgi_close_proc
+}
+
+proc cgi_param {nameval} {
+ regexp "(\[^=]*)(=?)(.*)" $nameval dummy name q value
+
+ if {$q != "="} {
+ set value ""
+ }
+ cgi_puts "<param name=\"$name\" value=[cgi_dquote_html $value]/>"
+}
+
+# record any proc's that must be called prior to displaying an error
+proc _cgi_close_proc_push {p} {
+ global _cgi
+ if {![info exists _cgi(close_proc)]} {
+ set _cgi(close_proc) ""
+ }
+ set _cgi(close_proc) "$p; $_cgi(close_proc)"
+}
+
+proc _cgi_close_proc_pop {} {
+ global _cgi
+ regexp "^(\[^;]*);(.*)" $_cgi(close_proc) dummy lastproc _cgi(close_proc)
+ return $lastproc
+}
+
+# generic proc to close whatever is on the top of the stack
+proc _cgi_close_proc {} {
+ eval [_cgi_close_proc_pop]
+}
+
+proc _cgi_close_procs {} {
+ global _cgi
+
+ _cgi_close_tag
+ if {[info exists _cgi(close_proc)]} {
+ uplevel #0 $_cgi(close_proc)
+ }
+}
+
+proc _cgi_close_tag {} {
+ global _cgi
+
+ if {[info exists _cgi(tag_in_progress)]} {
+ cgi_put ">"
+ unset _cgi(tag_in_progress)
+ }
+}
+
+##################################################
+# hr support
+##################################################
+
+proc cgi_hr {args} {
+ set buf "<hr"
+ foreach a $args {
+ if {[regexp "^width=(.*)" $a dummy str]} {
+ append buf " width=\"$str\""
+ } else {
+ append buf " $a"
+ }
+ }
+ cgi_put "$buf />"
+}
+
+##################################################
+# form & isindex
+##################################################
+
+proc cgi_form {action args} {
+ global _cgi
+
+ _cgi_form_multiple_check
+ set _cgi(form_in_progress) 1
+
+ _cgi_close_proc_push _cgi_form_end
+ cgi_put "<form action="
+ if {[regexp {^[a-z]*:} $action]} {
+ cgi_put "\"$action\""
+ } else {
+ cgi_put "\"[cgi_cgi $action]\""
+ }
+ set method "method=post"
+ foreach a [lrange $args 0 [expr [llength $args]-2]] {
+ if {[regexp "^method=" $a]} {
+ set method $a
+ } elseif {[regexp "^(target|onReset|onSubmit)=(.*)" $a dummy attr str]} {
+ cgi_put " $attr=\"$str\""
+ } elseif {[regexp "^enctype=(.*)" $a dummy str]} {
+ cgi_put " enctype=\"$str\""
+ set _cgi(form,enctype) $str
+ } else {
+ cgi_put " $a"
+ }
+ }
+ cgi_put " $method>"
+ uplevel 1 [lindex $args end]
+ catch {unset _cgi(form,enctype)}
+ _cgi_close_proc
+}
+
+proc _cgi_form_end {} {
+ global _cgi
+ unset _cgi(form_in_progress)
+ cgi_put "</form>"
+}
+
+proc _cgi_form_multiple_check {} {
+ global _cgi
+ if {[info exists _cgi(form_in_progress)]} {
+ error "Cannot create form (or isindex) with form already in progress."
+ }
+}
+
+proc cgi_isindex {args} {
+ _cgi_form_multiple_check
+
+ cgi_put "<isindex"
+ foreach a $args {
+ if {[regexp "^href=(.*)" $a dummy str]} {
+ cgi_put " href=\"$str\""
+ } elseif {[regexp "^prompt=(.*)" $a dummy str]} {
+ cgi_put " prompt=[cgi_dquote_html $str]"
+ } else {
+ cgi_put " $a"
+ }
+ }
+ cgi_put "/>"
+}
+
+##################################################
+# argument handling
+##################################################
+
+proc cgi_input {{fakeinput {}} {fakecookie {}}} {
+ global env _cgi _cgi_uservar _cgi_cookie _cgi_cookie_shadowed
+
+ set _cgi(uservars) {}
+ set _cgi(uservars,autolist) {}
+
+ if {[info exists env(CONTENT_TYPE)] && [regexp ^multipart/form-data $env(CONTENT_TYPE)]} {
+ if {![info exists env(REQUEST_METHOD)]} {
+ # running by hand
+ set fid [open $fakeinput]
+ } else {
+ set fid stdin
+ }
+ if {([info tclversion] >= 8.1) || [catch exp_version] || [info exists _cgi(no_binary_upload)]} {
+ _cgi_input_multipart $fid
+ } else {
+ _cgi_input_multipart_binary $fid
+ }
+ } else {
+ if {![info exists env(REQUEST_METHOD)]} {
+ set input $fakeinput
+ set env(HTTP_COOKIE) $fakecookie
+ } elseif { $env(REQUEST_METHOD) == "GET" } {
+ set input ""
+ catch {set input $env(QUERY_STRING)} ;# doesn't have to be set
+ } elseif { $env(REQUEST_METHOD) == "HEAD" } {
+ set input ""
+ } elseif {![info exists env(CONTENT_LENGTH)]} {
+ set _cgi(client_error) 1
+ error "Your browser failed to generate the content-length during a POST method."
+ } else {
+ set length $env(CONTENT_LENGTH)
+ if {0!=[string compare $length "-1"]} {
+ set input [read stdin $env(CONTENT_LENGTH)]
+ } else {
+ set _cgi(client_error) 1
+ error "Your browser generated a content-length of -1 during a POST method."
+ }
+ if {[info tclversion] >= 8.1} {
+ # guess query encoding from Content-Type header
+ if {[info exists env(CONTENT_TYPE)] \
+ && [regexp -nocase -- {charset=([^[:space:]]+)} $env(CONTENT_TYPE) m cs]} {
+ if {[regexp -nocase -- {iso-?8859-([[:digit:]]+)} $cs m d]} {
+ set _cgi(queryencoding) "iso8859-$d"
+ } elseif {[regexp -nocase -- {windows-([[:digit:]]+)} $cs m d]} {
+ set _cgi(queryencoding) "cp$d"
+ } elseif {0==[string compare -nocase $cs "utf-8"]} {
+ set _cgi(queryencoding) "utf-8"
+ } elseif {0==[string compare -nocase $cs "utf-16"]} {
+ set _cgi(queryencoding) "unicode"
+ }
+ } else {
+ set _cgi(queryencoding) [encoding system]
+ }
+ }
+ }
+ # save input for possible diagnostics later
+ set _cgi(input) $input
+
+ set pairs [split $input &]
+ foreach pair $pairs {
+ if {0 == [regexp "^(\[^=]*)=(.*)$" $pair dummy varname val]} {
+ # if no match, unquote and leave it at that
+ # this is typical of <isindex>-style queries
+ set varname anonymous
+ set val $pair
+ }
+
+ set varname [cgi_unquote_input $varname]
+ set val [cgi_unquote_input $val]
+ _cgi_set_uservar $varname $val
+ }
+ }
+
+ # O'Reilly's web server incorrectly uses COOKIE
+ catch {set env(HTTP_COOKIE) $env(COOKIE)}
+ if {![info exists env(HTTP_COOKIE)]} return
+ foreach pair [split $env(HTTP_COOKIE) ";"] {
+ # pairs are actually split by "; ", sigh
+ set pair [string trimleft $pair " "]
+ # spec is not clear but seems to allow = unencoded
+ # only sensible interpretation is to assume no = in var names
+ # appears MS IE can omit "=val"
+ set val ""
+ regexp (\[^=]*)=?(.*) $pair dummy varname val
+
+ set varname [cgi_unquote_input $varname]
+ set val [cgi_unquote_input $val]
+
+ if {[info exists _cgi_cookie($varname)]} {
+ lappend _cgi_cookie_shadowed($varname) $val
+ } else {
+ set _cgi_cookie($varname) $val
+ }
+ }
+}
+
+proc _cgi_input_multipart {fin} {
+ global env _cgi _cgi_uservar _cgi_userfile
+
+ cgi_debug -noprint {
+ # save file for debugging purposes
+ set dbg_filename [file join $_cgi(tmpdir) CGIdbg.[pid]]
+ # explicitly flush all writes to fout, because sometimes the writer
+ # can hang and we won't get to the termination code
+ set dbg_fout [open $dbg_filename w $_cgi(tmpperms)]
+ set _cgi(input) $dbg_filename
+ catch {fconfigure $dbg_fout -translation binary}
+ }
+
+ # figure out boundary
+ if {0==[regexp boundary=(.*) $env(CONTENT_TYPE) dummy boundary]} {
+ set _cgi(client_error) 1
+ error "Your browser failed to generate a \"boundary=\" line in a multipart response (CONTENT_TYPE: $env(CONTENT_TYPE)). Please upgrade (or fix) your browser."
+ }
+
+ # make boundary into a legal regsub pattern by protecting #
+ # legal boundary characters include ()+.? (among others)
+ regsub -all "\\(" $boundary "\\(" boundary
+ regsub -all "\\)" $boundary "\\)" boundary
+ regsub -all "\\+" $boundary "\\+" boundary
+ regsub -all "\\." $boundary "\\." boundary
+ regsub -all "\\?" $boundary "\\?" boundary
+
+ set boundary --$boundary
+
+ # don't corrupt or modify uploads yet allow Tcl 7.4 to work
+ catch {fconfigure $fin -translation binary}
+
+ # get first boundary line
+ gets $fin buf
+ if {[info exists dbg_fout]} {puts $dbg_fout $buf; flush $dbg_fout}
+
+ set _cgi(file,filecount) 0
+
+ while {1} {
+ # process Content-Disposition:
+ if {-1 == [gets $fin buf]} break
+ if {[info exists dbg_fout]} {puts $dbg_fout $buf; flush $dbg_fout}
+ catch {unset filename}
+ regexp {name="([^"]*)"} $buf dummy varname
+ if {0==[info exists varname]} {
+ # lynx violates spec and doesn't use quotes, so try again but
+ # assume space is delimiter
+ regexp {name=([^ ]*)} $buf dummy varname
+ if {0==[info exists varname]} {
+ set _cgi(client_error) 1
+ error "In response to a request for a multipart form, your browser generated a part header without a name field. Please upgrade (or fix) your browser."
+ }
+ }
+ # Lame-o encoding (on Netscape at least) doesn't escape field
+ # delimiters (like quotes)!! Since all we've ever seen is filename=
+ # at end of line, assuming nothing follows. Sigh.
+ regexp {filename="(.*)"} $buf dummy filename
+
+ # Skip remaining headers until blank line.
+ # Content-Type: can appear here.
+ set conttype ""
+ while {1} {
+ if {-1 == [gets $fin buf]} break
+ if {[info exists dbg_fout]} {puts $dbg_fout $buf; flush $dbg_fout}
+ if {0==[string compare $buf "\r"]} break
+ regexp -nocase "^Content-Type:\[ \t]+(.*)\r" $buf x conttype
+ }
+
+ if {[info exists filename]} {
+ if {$_cgi(file,filecount) > $_cgi(file,filelimit)} {
+ error "Too many files submitted. Max files allowed: $_cgi(file,filelimit)"
+ }
+
+ # read the part into a file
+ set foutname [file join $_cgi(tmpdir) CGI[pid].[incr _cgi(file,filecount)]]
+ set fout [open $foutname w $_cgi(tmpperms)]
+ # "catch" permits this to work with Tcl 7.4
+ catch {fconfigure $fout -translation binary}
+ _cgi_set_uservar $varname [list $foutname $filename $conttype]
+ set _cgi_userfile($varname) [list $foutname $filename $conttype]
+
+ #
+ # Look for a boundary line preceded by \r\n.
+ #
+ # To do this, we buffer line terminators that might
+ # be the start of the special \r\n$boundary sequence.
+ # The buffer is called "leftover" and is just inserted
+ # into the front of the next output (assuming it's
+ # not a boundary line).
+
+ set leftover ""
+ while {1} {
+ if {-1 == [gets $fin buf]} break
+ if {[info exists dbg_fout]} {puts $dbg_fout $buf; flush $dbg_fout}
+
+ if {0 == [string compare "\r\n" $leftover]} {
+ if {[regexp ^[set boundary](--)?\r?$ $buf dummy dashdash]} {
+ if {$dashdash == "--"} {set eof 1}
+ break
+ }
+ }
+ if {[regexp (.*)\r$ $buf x data]} {
+ puts -nonewline $fout $leftover$data
+ set leftover "\r\n"
+ } else {
+ puts -nonewline $fout $leftover$buf
+ set leftover "\n"
+ }
+ if {[file size $foutname] > $_cgi(file,charlimit)} {
+ error "File size exceeded. Max file size allowed: $_cgi(file,charlimit)"
+ }
+ }
+
+ close $fout
+ unset fout
+ } else {
+ # read the part into a variable
+ set val ""
+ set blanks 0
+ while {1} {
+ if {-1 == [gets $fin buf]} break
+ if {[info exists dbg_fout]} {puts $dbg_fout $buf; flush $dbg_fout}
+ if {[regexp ^[set boundary](--)?\r?$ $buf dummy dashdash]} {
+ if {$dashdash == "--"} {set eof 1}
+ break
+ }
+ if {0!=[string compare $val ""]} {
+ append val \n
+ }
+ regexp (.*)\r$ $buf dummy buf
+ if {[info exists blanks]} {
+ if {0!=[string compare $buf ""]} {
+ if {$blanks} {
+ append val [string repeat \n [incr blanks]]
+ }
+ unset blanks
+ } else {
+ incr blanks
+ }
+ }
+ append val $buf
+ }
+ _cgi_set_uservar $varname $val
+ }
+ if {[info exists eof]} break
+ }
+ if {[info exists dbg_fout]} {close $dbg_fout}
+}
+
+proc _cgi_input_multipart_binary {fin} {
+ global env _cgi _cgi_uservar _cgi_userfile
+
+ log_user 0
+ set timeout -1
+
+ cgi_debug -noprint {
+ # save file for debugging purposes
+ set dbg_filename [file join $_cgi(tmpdir) CGIdbg.[pid]]
+ set _cgi(input) $dbg_filename
+ spawn -open [open $dbg_filename w $_cgi(tmpperms)]
+ set dbg_sid $spawn_id
+ }
+ spawn -open $fin
+ set fin_sid $spawn_id
+ remove_nulls 0
+
+ if {0} {
+ # dump input to screen
+ cgi_debug {
+ puts "<xmp>"
+ expect {
+ -i $fin_sid
+ -re ^\r {puts -nonewline "CR"; exp_continue}
+ -re ^\n {puts "NL"; exp_continue}
+ -re . {puts -nonewline $expect_out(buffer); exp_continue}
+ }
+ puts "</xmp>"
+ exit
+ }
+ }
+
+ # figure out boundary
+ if {0==[regexp boundary=(.*) $env(CONTENT_TYPE) dummy boundary]} {
+ set _cgi(client_error) 1
+ error "Your browser failed to generate a \"boundary=\" definition in a multipart response (CONTENT_TYPE: $env(CONTENT_TYPE)). Please upgrade (or fix) your browser."
+ }
+
+ # make boundary into a legal regsub pattern by protecting #
+ # legal boundary characters include ()+.? (among others)
+ regsub -all "\\(" $boundary "\\(" boundary
+ regsub -all "\\)" $boundary "\\)" boundary
+ regsub -all "\\+" $boundary "\\+" boundary
+ regsub -all "\\." $boundary "\\." boundary
+ regsub -all "\\?" $boundary "\\?" boundary
+
+ set boundary --$boundary
+ set linepat "(\[^\r]*)\r\n"
+
+ # get first boundary line
+ expect {
+ -i $fin_sid
+ -re $linepat {
+ set buf $expect_out(1,string)
+ if {[info exists dbg_sid]} {send -i $dbg_sid -- $buf\n}
+ }
+ eof {
+ set _cgi(client_error) 1
+ error "Your browser failed to provide an initial boundary ($boundary) in a multipart response. Please upgrade (or fix) your browser."
+ }
+ }
+
+ set _cgi(file,filecount) 0
+
+ while {1} {
+ # process Content-Disposition:
+ expect {
+ -i $fin_sid
+ -re $linepat {
+ set buf $expect_out(1,string)
+ if {[info exists dbg_sid]} {send -i $dbg_sid -- $buf\n}
+ }
+ eof break
+ }
+ catch {unset filename}
+ regexp {name="([^"]*)"} $buf dummy varname
+ if {0==[info exists varname]} {
+ set _cgi(client_error) 1
+ error "In response to a request for a multipart form, your browser generated a part header without a name field. Please upgrade (or fix) your browser."
+ }
+
+ # Lame-o encoding (on Netscape at least) doesn't escape field
+ # delimiters (like quotes)!! Since all we've ever seen is filename=
+ # at end of line, assuming nothing follows. Sigh.
+ regexp {filename="(.*)"} $buf dummy filename
+
+ # Skip remaining headers until blank line.
+ # Content-Type: can appear here.
+ set conttype ""
+ expect {
+ -i $fin_sid
+ -re $linepat {
+ set buf $expect_out(1,string)
+ if {[info exists dbg_sid]} {send -i $dbg_sid -- $buf\n}
+ if {0!=[string compare $buf ""]} exp_continue
+ regexp -nocase "^Content-Type:\[ \t]+(.*)\r" $buf x conttype
+ }
+ eof break
+ }
+
+ if {[info exists filename]} {
+ if {$_cgi(file,filecount) > $_cgi(file,filelimit)} {
+ error "Too many files submitted. Max files allowed: $_cgi(file,filelimit)"
+ }
+
+ # read the part into a file
+ set foutname [file join $_cgi(tmpdir) CGI[pid].[incr _cgi(file,filecount)]]
+ spawn -open [open $foutname w $_cgi(tmpperms)]
+ set fout_sid $spawn_id
+
+ _cgi_set_uservar $varname [list $foutname $filename $conttype]
+ set _cgi_userfile($varname) [list $foutname $filename $conttype]
+
+ # This is tricky stuff - be very careful changing anything here!
+ # In theory, all we have to is record everything up to
+ # \r\n$boundary\r\n. Unfortunately, we can't simply wait on
+ # such a pattern because the input can overflow any possible
+ # buffer we might choose. We can't simply catch buffer_full
+ # because the boundary might straddle a buffer. I doubt that
+ # doing my own buffering would be any faster than taking the
+ # approach I've done here.
+ #
+ # The code below basically implements a simple scanner that
+ # keeps track of whether it's seen crlfs or pieces of them.
+ # The idea is that we look for crlf pairs, separated by
+ # things that aren't crlfs (or pieces of them). As we encounter
+ # things that aren't crlfs (or pieces of them), or when we decide
+ # they can't be, we mark them for output and resume scanning for
+ # new pairs.
+ #
+ # The scanner runs tolerably fast because the [...]+ pattern picks
+ # up most things. The \r and \n are ^-anchored so the pattern
+ # match is pretty fast and these don't happen that often so the
+ # huge \n action is executed rarely (once per line on text files).
+ # The null pattern is, of course, only used when everything
+ # else fails.
+
+ # crlf == "\r\n" if we've seen one, else == ""
+ # cr == "\r" if we JUST saw one, else == ""
+ # Yes, strange, but so much more efficient
+ # that I'm willing to sacrifice readability, sigh.
+ # buf accumulated data between crlf pairs
+
+ set buf ""
+ set cr ""
+ set crlf ""
+
+ expect {
+ -i $fin_sid
+ -re "^\r" {
+ if {$cr == "\r"} {
+ append buf "\r"
+ }
+ set cr \r
+ exp_continue
+ } -re "^\n" {
+ if {$cr == "\r"} {
+ if {$crlf == "\r\n"} {
+ # do boundary test
+ if {[regexp ^[set boundary](--)?$ $buf dummy dashdash]} {
+ if {$dashdash == "--"} {
+ set eof 1
+ }
+ } else {
+ # boundary test failed
+ if {[info exists dbg_sid]} {send -i $dbg_sid -- \r\n$buf}
+ send -i $fout_sid \r\n$buf ; set buf ""
+ set cr ""
+ exp_continue
+ }
+ } else {
+ set crlf "\r\n"
+ set cr ""
+ if {[info exists dbg_sid]} {send -i $dbg_sid -- $buf}
+ send -i $fout_sid -- $buf ; set buf ""
+ exp_continue
+ }
+ } else {
+ if {[info exists dbg_sid]} {send -i $dbg_sid -- $crlf$buf\n}
+ send -i $fout_sid -- $crlf$buf\n ; set buf ""
+ set crlf ""
+ exp_continue
+ }
+ } -re "\[^\r\n]+" {
+ if {$cr == "\r"} {
+ set buf $crlf$buf\r$expect_out(buffer)
+ set crlf ""
+ set cr ""
+ } else {
+ append buf $expect_out(buffer)
+ }
+ exp_continue
+ } null {
+ if {[info exists dbg_sid]} {
+ send -i $dbg_sid -- $crlf$buf$cr
+ send -i $dbg_sid -null
+ }
+ send -i $fout_sid -- $crlf$buf$cr ; set buf ""
+ send -i $fout_sid -null
+ set cr ""
+ set crlf ""
+ exp_continue
+ } eof {
+ set _cgi(client_error) 1
+ error "Your browser failed to provide an ending boundary ($boundary) in a multipart response. Please upgrade (or fix) your browser."
+ }
+ }
+ exp_close -i $fout_sid ;# implicitly closes fout
+ exp_wait -i $fout_sid
+ unset fout_sid
+ } else {
+ # read the part into a variable
+ set val ""
+ expect {
+ -i $fin_sid
+ -re $linepat {
+ set buf $expect_out(1,string)
+ if {[info exists dbg_sid]} {send -i $dbg_sid -- $buf\n}
+ if {[regexp ^[set boundary](--)?$ $buf dummy dashdash]} {
+ if {$dashdash == "--"} {set eof 1}
+ } else {
+ regexp (.*)\r$ $buf dummy buf
+ if {0!=[string compare $val ""]} {
+ append val \n
+ }
+ append val $buf
+ exp_continue
+ }
+ }
+ }
+ _cgi_set_uservar $varname $val
+ }
+ if {[info exists eof]} break
+ }
+ if {[info exists fout]} {
+ exp_close -i $dbg_sid
+ exp_wait -i $dbg_sid
+ }
+
+ # no need to close fin, fin_sid, or dbg_sid
+}
+
+# internal routine for defining user variables
+proc _cgi_set_uservar {varname val} {
+ global _cgi _cgi_uservar
+
+ set exists [info exists _cgi_uservar($varname)]
+ set isList $exists
+ # anything we've seen before and is being set yet again necessarily
+ # has to be (or become a list)
+
+ if {!$exists} {
+ lappend _cgi(uservars) $varname
+ }
+
+ if {[regexp List$ $varname]} {
+ set isList 1
+ } elseif {$exists} {
+ # vars that we've seen before but aren't marked as lists
+ # need to be "listified" so we can do appends later
+ if {-1 == [lsearch $_cgi(uservars,autolist) $varname]} {
+ # remember that we've listified it
+ lappend _cgi(uservars,autolist) $varname
+ set _cgi_uservar($varname) [list $_cgi_uservar($varname)]
+ }
+ }
+ if {$isList} {
+ lappend _cgi_uservar($varname) $val
+ } else {
+ set _cgi_uservar($varname) $val
+ }
+}
+
+# export named variable
+proc cgi_export {nameval} {
+ regexp "(\[^=]*)(=?)(.*)" $nameval dummy name q value
+
+ if {$q != "="} {
+ set value [uplevel 1 set [list $name]]
+ }
+
+ cgi_put "<input type=hidden name=\"$name\" value=[cgi_dquote_html $value]/>"
+}
+
+proc cgi_export_cookie {name args} {
+ upvar 1 $name x
+ eval cgi_cookie_set [list $name=$x] $args
+}
+
+# return list of variables available for import
+# Explicit list is used to keep items in order originally found in form.
+proc cgi_import_list {} {
+ global _cgi
+
+ return $_cgi(uservars)
+}
+
+# import named variable
+proc cgi_import {name} {
+ global _cgi_uservar
+ upvar 1 $name var
+
+ set var $_cgi_uservar($name)
+}
+
+proc cgi_import_as {name tclvar} {
+ global _cgi_uservar
+ upvar 1 $tclvar var
+
+ set var $_cgi_uservar($name)
+}
+
+# like cgi_import but if not available, try cookie
+proc cgi_import_cookie {name} {
+ global _cgi_uservar
+ upvar 1 $name var
+
+ if {0==[catch {set var $_cgi_uservar($name)}]} return
+ set var [cgi_cookie_get $name]
+}
+
+# like cgi_import but if not available, try cookie
+proc cgi_import_cookie_as {name tclvar} {
+ global _cgi_uservar
+ upvar 1 $tclvar var
+
+ if {0==[catch {set var $_cgi_uservar($name)}]} return
+ set var [cgi_cookie_get $name]
+}
+
+proc cgi_import_file {type name} {
+ global _cgi_userfile
+ upvar 1 $name var
+
+ set var $_cgi_userfile($name)
+ switch -- $type {
+ "-server" {
+ lindex $var 0
+ } "-client" {
+ lindex $var 1
+ } "-type" {
+ lindex $var 2
+ }
+ }
+}
+
+# deprecated, use cgi_import_file
+proc cgi_import_filename {type name} {
+ global _cgi_userfile
+ upvar 1 $name var
+
+ set var $_cgi_userfile($name)
+ if {$type == "-server" || $type == "-local"} {
+ # -local is deprecated
+ lindex $var 0
+ } else {
+ lindex $var 1
+ }
+}
+
+# set the urlencoding
+proc cgi_urlencoding {{encoding ""}} {
+ global _cgi
+
+ set result [expr {[info exists _cgi(queryencoding)]
+ ? $_cgi(queryencoding)
+ : ""}]
+
+ # check if the encoding is available
+ if {[info tclversion] >= 8.1
+ && [lsearch -exact [encoding names] $encoding] != -1 } {
+ set _cgi(queryencoding) $encoding
+ }
+
+ return $result
+}
+
+##################################################
+# button support
+##################################################
+
+# not sure about arg handling, do we need to support "name="?
+proc cgi_button {value args} {
+ cgi_put "<input type=button value=[cgi_dquote_html $value]"
+ foreach a $args {
+ if {[regexp "^onClick=(.*)" $a dummy str]} {
+ cgi_put " onClick=\"$str\""
+ } else {
+ cgi_put " $a"
+ }
+ }
+ cgi_put "/>"
+}
+
+# Derive a button from a link predefined by cgi_link
+proc cgi_button_link {args} {
+ global _cgi_link
+
+ set tag [lindex $args 0]
+ if {[llength $args] == 2} {
+ set label [lindex $args end]
+ } else {
+ set label $_cgi_link($tag,label)
+ }
+
+ cgi_button $label onClick=$_cgi_link($tag,url)
+}
+
+proc cgi_submit_button {{nameval {=Submit Query}} args} {
+ regexp "(\[^=]*)=(.*)" $nameval dummy name value
+ cgi_put "<input type=submit"
+ if {0!=[string compare "" $name]} {
+ cgi_put " name=\"$name\""
+ }
+ cgi_put " value=[cgi_dquote_html $value]"
+ foreach a $args {
+ if {[regexp "^onClick=(.*)" $a dummy str]} {
+ cgi_put " onClick=\"$str\""
+ } else {
+ cgi_put " $a"
+ }
+ }
+ cgi_put "/>"
+}
+
+
+proc cgi_reset_button {{value Reset} args} {
+ cgi_put "<input type=reset value=[cgi_dquote_html $value]"
+
+ foreach a $args {
+ if {[regexp "^onClick=(.*)" $a dummy str]} {
+ cgi_put " onClick=\"$str\""
+ } else {
+ cgi_put " $a"
+ }
+ }
+ cgi_put "/>"
+}
+
+proc cgi_radio_button {nameval args} {
+ regexp "(\[^=]*)=(.*)" $nameval dummy name value
+
+ cgi_put "<input type=radio name=\"$name\" value=[cgi_dquote_html $value]"
+
+ foreach a $args {
+ if {[regexp "^checked_if_equal=(.*)" $a dummy default]} {
+ if {0==[string compare $default $value]} {
+ cgi_put " checked"
+ }
+ } elseif {[regexp "^checked=(.*)" $a dummy checked]} {
+ # test explicitly to avoid forcing user eval
+ if {$checked} {
+ cgi_put " checked"
+ }
+ } elseif {[regexp "^onClick=(.*)" $a dummy str]} {
+ cgi_put " onClick=\"$str\""
+ } else {
+ cgi_put " $a"
+ }
+ }
+ cgi_put "/>"
+}
+
+proc cgi_image_button {nameval args} {
+ regexp "(\[^=]*)=(.*)" $nameval dummy name value
+ cgi_put "<input type=image"
+ if {0!=[string compare "" $name]} {
+ cgi_put " name=\"$name\""
+ }
+ cgi_put " src=\"$value\""
+ foreach a $args {
+ if {[regexp "^onClick=(.*)" $a dummy str]} {
+ cgi_put " onClick=\"$str\""
+ } else {
+ cgi_put " $a"
+ }
+ }
+ cgi_put "/>"
+}
+
+# map/area implement client-side image maps
+proc cgi_map {name cmd} {
+ cgi_put "<map name=\"$name\">"
+ _cgi_close_proc_push "cgi_put </map>"
+
+ uplevel 1 $cmd
+ _cgi_close_proc
+}
+
+proc cgi_area {args} {
+ cgi_put "<area"
+ foreach a $args {
+ if {[regexp "^(coords|shape|href|target|onMouseOut|alt)=(.*)" $a dummy attr str]} {
+ cgi_put " $attr=\"$str\""
+ } else {
+ cgi_put " $a"
+ }
+ }
+ cgi_put "/>"
+}
+
+##################################################
+# checkbox support
+##################################################
+
+proc cgi_checkbox {nameval args} {
+ regexp "(\[^=]*)(=?)(.*)" $nameval dummy name q value
+ cgi_put "<input type=checkbox name=\"$name\""
+
+ if {0!=[string compare "" $value]} {
+ cgi_put " value=[cgi_dquote_html $value]"
+ }
+
+ foreach a $args {
+ if {[regexp "^checked_if_equal=(.*)" $a dummy default]} {
+ if {0==[string compare $default $value]} {
+ cgi_put " checked"
+ }
+ } elseif {[regexp "^checked=(.*)" $a dummy checked]} {
+ # test explicitly to avoid forcing user eval
+ if {$checked} {
+ cgi_put " checked"
+ }
+ } elseif {[regexp "^onClick=(.*)" $a dummy str]} {
+ cgi_put " onClick=\"$str\""
+ } else {
+ cgi_put " $a"
+ }
+ }
+ cgi_put "/>"
+}
+
+##################################################
+# textentry support
+##################################################
+
+proc cgi_text {nameval args} {
+ regexp "(\[^=]*)(=?)(.*)" $nameval dummy name q value
+
+ cgi_put "<input name=\"$name\""
+
+ if {$q != "="} {
+ set value [uplevel 1 set [list $name]]
+ }
+ cgi_put " value=[cgi_dquote_html $value]"
+
+ foreach a $args {
+ if {[regexp "^on(Select|Focus|Blur|Change)=(.*)" $a dummy event str]} {
+ cgi_put " on$event=\"$str\""
+ } else {
+ cgi_put " $a"
+ }
+ }
+ cgi_put "/>"
+}
+
+##################################################
+# textarea support
+##################################################
+
+proc cgi_textarea {nameval args} {
+ regexp "(\[^=]*)(=?)(.*)" $nameval dummy name q value
+
+ cgi_put "<textarea name=\"$name\""
+ foreach a $args {
+ if {[regexp "^on(Select|Focus|Blur|Change)=(.*)" $a dummy event str]} {
+ cgi_put " on$event=\"$str\""
+ } else {
+ cgi_put " $a"
+ }
+ }
+ cgi_put ">"
+
+ if {$q != "="} {
+ set value [uplevel 1 set [list $name]]
+ }
+ cgi_put "[cgi_quote_html $value]</textarea>"
+}
+
+##################################################
+# file upload support
+##################################################
+
+# for this to work, pass enctype=multipart/form-data to cgi_form
+proc cgi_file_button {name args} {
+ global _cgi
+ if {[info exists _cgi(formtype)] && ("multipart/form-data" != $_cgi(form,enctype))} {
+ error "cgi_file_button requires that cgi_form have the argument enctype=multipart/form-data"
+ }
+ cgi_put "<input type=file name=\"$name\"[_cgi_list_to_string $args]/>"
+}
+
+# establish a per-file limit for uploads
+
+proc cgi_file_limit {files chars} {
+ global _cgi
+
+ set _cgi(file,filelimit) $files
+ set _cgi(file,charlimit) $chars
+}
+
+##################################################
+# select support
+##################################################
+
+proc cgi_select {name args} {
+ cgi_put "<select name=\"$name\""
+ _cgi_close_proc_push "cgi_put </select>"
+ foreach a [lrange $args 0 [expr [llength $args]-2]] {
+ if {[regexp "^on(Focus|Blur|Change)=(.*)" $a dummy event str]} {
+ cgi_put " on$event=\"$str\""
+ } else {
+ if {0==[string compare multiple $a]} {
+ ;# sanity check
+ if {![regexp "List$" $name]} {
+ cgi_puts ">" ;# prevent error from being absorbed
+ error "When selecting multiple options, select variable \
+ must end in \"List\" to allow the value to be \
+ recognized as a list when it is processed later."
+ }
+ }
+ cgi_put " $a"
+ }
+ }
+ cgi_put ">"
+ uplevel 1 [lindex $args end]
+ _cgi_close_proc
+}
+
+proc cgi_option {o args} {
+ cgi_put "<option"
+ set value $o
+ set selected 0
+ foreach a $args {
+ if {[regexp "^selected_if_equal=(.*)" $a dummy selected_if_equal]} {
+ } elseif {[regexp "^value=(.*)" $a dummy value]} {
+ cgi_put " value=[cgi_dquote_html $value]"
+ } else {
+ cgi_put " $a"
+ }
+ }
+ if {[info exists selected_if_equal]} {
+ if {0 == [string compare $selected_if_equal $value]} {
+ cgi_put " selected"
+ }
+ }
+ cgi_puts ">[cgi_quote_html $o]</option>"
+}
+
+##################################################
+# plug-in support
+##################################################
+
+proc cgi_embed {src wh args} {
+ regexp (.*)x(.*) $wh dummy width height
+ cgi_put "<embed src=[cgi_dquote_html $src] width=\"$width\" height=\"$height\""
+ foreach a $args {
+ if {[regexp "^palette=(.*)" $a dummy str]} {
+ cgi_put " palette=\"$str\""
+ } elseif {[regexp -- "-quote" $a]} {
+ set quote 1
+ } else {
+ if {[info exists quote]} {
+ regexp "(\[^=]*)=(.*)" $a dummy var val
+ cgi_put " var=[cgi_dquote_html $var]"
+ } else {
+ cgi_put " $a"
+ }
+ }
+ }
+ cgi_put "/>"
+}
+
+##################################################
+# mail support
+##################################################
+
+# mail to/from the service itself
+proc cgi_mail_addr {args} {
+ global _cgi
+
+ if {[llength $args]} {
+ set _cgi(email) [lindex $args 0]
+ }
+ return $_cgi(email)
+}
+
+proc cgi_mail_start {to} {
+ global _cgi
+
+ set _cgi(mailfile) [file join $_cgi(tmpdir) cgimail.[pid]]
+ set _cgi(mailfid) [open $_cgi(mailfile) w+]
+ set _cgi(mailto) $to
+
+ # mail is actually sent by "nobody". To force bounce messages
+ # back to us, override the default return-path.
+ cgi_mail_add "Return-Path: <$_cgi(email)>"
+ cgi_mail_add "From: [cgi_name] <$_cgi(email)>"
+ cgi_mail_add "To: $to"
+}
+
+# add another line to outgoing mail
+# if no arg, add a blank line
+proc cgi_mail_add {{arg {}}} {
+ global _cgi
+
+ puts $_cgi(mailfid) $arg
+}
+
+# end the outgoing mail and send it
+proc cgi_mail_end {} {
+ global _cgi
+
+ flush $_cgi(mailfid)
+
+ foreach sendmail in $_cgi(sendmail) {
+ if {[file executable $sendmail]} {
+ exec $sendmail -t -odb < $_cgi(mailfile)
+ # Explanation:
+ # -t means: pick up recipient from body
+ # -odb means: deliver in background
+ # note: bogus local address cause sendmail to fail immediately
+ set sent 1
+ }
+ }
+
+ if {0==[info exists sent]} {
+ # fallback for sites without sendmail
+
+ if {0==[info exists _cgi(mail_relay)]} {
+ regexp "@(.*)" $_cgi(mailto) dummy _cgi(mail_relay)
+ }
+
+ set s [socket $_cgi(mail_relay) 25]
+ gets $s answer
+ if {[lindex $answer 0] != 220} {error $answer}
+
+ puts $s "HELO [info host]";flush $s
+ gets $s answer
+ if {[lindex $answer 0] != 250} {error $answer}
+
+ puts $s "MAIL FROM:<$_cgi(email)>";flush $s
+ gets $s answer
+ if {[lindex $answer 0] != 250} {error $answer}
+
+ puts $s "RCPT TO:<$_cgi(mailto)>";flush $s
+ gets $s answer
+ if {[lindex $answer 0] != 250} {error $answer}
+
+ puts $s DATA;flush $s
+ gets $s answer
+ if {[lindex $answer 0] != 354} {error $answer}
+
+ seek $_cgi(mailfid) 0 start
+ puts $s [read $_cgi(mailfid)];flush $s
+ puts $s .;flush $s
+ gets $s answer
+ if {[lindex $answer 0] != 250} {error $answer}
+
+ close $s
+ }
+ close $_cgi(mailfid)
+ file delete -force $_cgi(mailfile)
+}
+
+proc cgi_mail_relay {host} {
+ global _cgi
+
+ set _cgi(mail_relay) $host
+}
+
+proc cgi_sendmail {path} {
+ global _cgi
+
+ set _cgi(sendmail) $path
+}
+
+##################################################
+# cookie support
+##################################################
+
+# calls to cookie_set look like this:
+# cgi_cookie_set user=don domain=nist.gov expires=never
+# cgi_cookie_set user=don domain=nist.gov expires=now
+# cgi_cookie_set user=don domain=nist.gov expires=...actual date...
+
+proc cgi_cookie_set {nameval args} {
+ global _cgi
+
+ if {![info exists _cgi(http_head_in_progress)]} {
+ error "Cookies must be set from within cgi_http_head."
+ }
+ cgi_puts -nonewline "Set-Cookie: [cgi_cookie_encode $nameval];"
+
+ foreach a $args {
+ if {[regexp "^expires=(.*)" $a dummy expiration]} {
+ if {0==[string compare $expiration "never"]} {
+ set expiration "Friday, 11-Jan-2038 23:59:59 GMT"
+ } elseif {0==[string compare $expiration "now"]} {
+ set expiration "Friday, 31-Dec-1990 23:59:59 GMT"
+ }
+ cgi_puts -nonewline " expires=$expiration;"
+ } elseif {[regexp "^(domain|path)=(.*)" $a dummy attr str]} {
+ cgi_puts -nonewline " $attr=[cgi_cookie_encode $str];"
+ } elseif {[regexp "^secure$" $a]} {
+ cgi_puts -nonewline " secure;"
+ }
+ }
+ cgi_puts ""
+}
+
+# return list of cookies available for import
+proc cgi_cookie_list {} {
+ global _cgi_cookie
+
+ array names _cgi_cookie
+}
+
+proc cgi_cookie_get {args} {
+ global _cgi_cookie
+
+ set all 0
+
+ set flag [lindex $args 0]
+ if {$flag == "-all"} {
+ set args [lrange $args 1 end]
+ set all 1
+ }
+ set name [lindex $args 0]
+
+ if {$all} {
+ global _cgi_cookie_shadowed
+
+ if {[info exists _cgi_cookie_shadowed($name)]} {
+ return [concat $_cgi_cookie($name) $_cgi_cookie_shadowed($name)]
+ } else {
+ return [concat $_cgi_cookie($name)]
+ }
+ }
+ return $_cgi_cookie($name)
+}
+
+proc cgi_cookie_encode {in} {
+ regsub -all " " $in "+" in
+ regsub -all "%" $in "%25" in ;# must preceed other subs that produce %
+ regsub -all ";" $in "%3B" in
+ regsub -all "," $in "%2C" in
+ regsub -all "\n" $in "%0D%0A" in
+ return $in
+}
+
+##################################################
+# table support
+##################################################
+
+proc cgi_table {args} {
+ cgi_put "<table"
+ _cgi_close_proc_push "cgi_put </table>"
+
+ if {[llength $args]} {
+ cgi_put "[_cgi_lrange $args 0 [expr [llength $args]-2]]"
+ }
+ cgi_put ">"
+ uplevel 1 [lindex $args end]
+ _cgi_close_proc
+}
+
+proc cgi_caption {args} {
+ cgi_put "<caption"
+ _cgi_close_proc_push "cgi_put </caption>"
+
+ if {[llength $args]} {
+ cgi_put "[_cgi_lrange $args 0 [expr [llength $args]-2]]"
+ }
+ cgi_put ">"
+ uplevel 1 [lindex $args end]
+ _cgi_close_proc
+}
+
+proc cgi_table_row {args} {
+ cgi_put "<tr"
+ _cgi_close_proc_push "cgi_put </tr>"
+ if {[llength $args]} {
+ cgi_put "[_cgi_lrange $args 0 [expr [llength $args]-2]]"
+ }
+ cgi_put ">"
+ uplevel 1 [lindex $args end]
+ _cgi_close_proc
+}
+
+# like table_row but without eval
+proc cgi_tr {args} {
+ cgi_put <tr
+ if {[llength $args] > 1} {
+ cgi_put "[_cgi_lrange $args 0 [expr [llength $args]-2]]"
+ }
+ cgi_put ">"
+ foreach i [lindex $args end] {
+ cgi_td $i
+ }
+ cgi_put </tr>
+}
+
+proc cgi_table_head {args} {
+ cgi_put "<th"
+ _cgi_close_proc_push "cgi_put </th>"
+
+ if {[llength $args]} {
+ cgi_put "[_cgi_lrange $args 0 [expr [llength $args]-2]]"
+ }
+ cgi_put ">"
+ uplevel 1 [lindex $args end]
+ _cgi_close_proc
+}
+
+# like table_head but without eval
+proc cgi_th {args} {
+ cgi_put "<th"
+
+ if {[llength $args] > 1} {
+ cgi_put "[_cgi_lrange $args 0 [expr [llength $args]-2]]"
+ }
+ cgi_put ">[lindex $args end]</th>"
+}
+
+proc cgi_table_data {args} {
+ cgi_put "<td"
+ _cgi_close_proc_push "cgi_put </td>"
+
+ if {[llength $args]} {
+ cgi_put "[_cgi_lrange $args 0 [expr [llength $args]-2]]"
+ }
+ cgi_put ">"
+ uplevel 1 [lindex $args end]
+ _cgi_close_proc
+}
+
+# like table_data but without eval
+proc cgi_td {args} {
+ cgi_put "<td"
+
+ if {[llength $args] > 1} {
+ cgi_put "[_cgi_lrange $args 0 [expr [llength $args]-2]]"
+ }
+ cgi_put ">[lindex $args end]</td>"
+}
+
+##################################################
+# stylesheets - not yet documented
+##################################################
+
+proc cgi_stylesheet {href} {
+ cgi_puts "<link rel=stylesheet href=\"$href\" type=\"text/css\"/>"
+}
+
+proc cgi_span {args} {
+ set buf "<span"
+ foreach a [lrange $args 0 [expr [llength $args]-2]] {
+ if {[regexp "style=(.*)" $a dummy str]} {
+ append buf " style=\"$str\""
+ } elseif {[regexp "class=(.*)" $a dummy str]} {
+ append buf " class=\"$str\""
+ } else {
+ append buf " $a"
+ }
+ }
+ return "$buf>[lindex $args end]</span>"
+}
+
+##################################################
+# frames
+##################################################
+
+proc cgi_frameset {args} {
+ cgi_head ;# force it out, just in case none
+
+ cgi_put "<frameset"
+ _cgi_close_proc_push "cgi_puts </frameset>"
+
+ foreach a [lrange $args 0 [expr [llength $args]-2]] {
+ if {[regexp "^(rows|cols|onUnload|onLoad|onBlur)=(.*)" $a dummy attr str]} {
+ cgi_put " $attr=\"$str\""
+ } else {
+ cgi_put " $a"
+ }
+ }
+ cgi_puts ">"
+ uplevel 1 [lindex $args end]
+ _cgi_close_proc
+}
+
+proc cgi_frame {namesrc args} {
+ cgi_put "<frame"
+
+ regexp "(\[^=]*)(=?)(.*)" $namesrc dummy name q src
+
+ if {$name != ""} {
+ cgi_put " name=\"$name\""
+ }
+
+ if {$src != ""} {
+ cgi_put " src=\"$src\""
+ }
+
+ foreach a $args {
+ if {[regexp "^(marginwidth|marginheight|scrolling|onFocus)=(.*)" $a dummy attr str]} {
+ cgi_put " $attr=\"$str\""
+ } else {
+ cgi_put " $a"
+ }
+ }
+ cgi_puts "/>"
+}
+
+proc cgi_noframes {args} {
+ cgi_puts "<noframes>"
+ _cgi_close_proc_push "cgi_puts </noframes>"
+ uplevel 1 [lindex $args end]
+ _cgi_close_proc
+}
+
+##################################################
+# admin support
+##################################################
+
+# mail address of the administrator
+proc cgi_admin_mail_addr {args} {
+ global _cgi
+
+ if {[llength $args]} {
+ set _cgi(admin_email) [lindex $args 0]
+ }
+ return $_cgi(admin_email)
+}
+
+##################################################
+# if possible, make each cmd available without cgi_ prefix
+##################################################
+
+if {[info tclversion] >= 7.5} {
+ foreach _cgi(old) [info procs cgi_*] {
+ regexp "^cgi_(.*)" $_cgi(old) _cgi(dummy) _cgi(new)
+ if {[llength [info commands $_cgi(new)]]} continue
+ interp alias {} $_cgi(new) {} $_cgi(old)
+ }
+} else {
+ foreach _cgi(old) [info procs cgi_*] {
+ regexp "^cgi_(.*)" $_cgi(old) _cgi(dummy) _cgi(new)
+ if {[llength [info commands $_cgi(new)]]} continue
+ proc $_cgi(new) {args} "uplevel 1 $_cgi(old) \$args"
+ }
+}
+
+##################################################
+# internal utilities
+##################################################
+
+# undo Tcl's quoting due to list protection
+# This leaves a space at the beginning if the string is non-null
+# but this is always desirable in the HTML context in which it is called
+# and the resulting HTML looks more readable.
+# (It makes the Tcl callers a little less readable - however, there aren't
+# more than a handful and they're all right here, so we'll live with it.)
+proc _cgi_list_to_string {list} {
+ set string ""
+ foreach l $list {
+ append string " $l"
+ }
+ # remove first space if possible
+ # regexp "^ ?(.*)" $string dummy string
+ return $string
+}
+
+# do lrange but return as string
+# needed for stuff like: cgi_puts "[_cgi_lrange $args ...]
+# Like _cgi_list_to_string, also returns string with initial blank if non-null
+proc _cgi_lrange {list i1 i2} {
+ _cgi_list_to_string [lrange $list $i1 $i2]
+}
+
+##################################################
+# temporary file procedures
+##################################################
+
+# set appropriate temporary file modes
+proc cgi_tmpfile_permissions {{mode ""}} {
+ global _cgi
+
+ if {[string length $mode]} {
+ set _cgi(tmpperms) $mode
+ }
+
+ return $_cgi(tmpperms)
+}
+
+##################################################
+# user-defined procedures
+##################################################
+
+# User-defined procedure called immediately after <body>
+# Good mechanism for controlling things such as if all of your pages
+# start with the same graphic or other boilerplate.
+proc app_body_start {} {}
+
+# User-defined procedure called just before </body>
+# Good place to generate signature lines, last-updated-by, etc.
+proc app_body_end {} {}
+
+proc cgi_puts {args} {
+ eval puts $args
+}
+
+# User-defined procedure to generate DOCTYPE declaration
+proc cgi_doctype {} {}
+
+##################################################
+# do some initialization
+##################################################
+
+# cgi_init initializes to a known state.
+
+proc cgi_init {} {
+ global _cgi
+ unset _cgi
+
+ # set explicitly for speed
+ set _cgi(debug) -off
+ set _cgi(buffer_nl) "\n"
+
+ cgi_name ""
+ cgi_root ""
+ cgi_body_args ""
+ cgi_file_limit 10 100000000
+
+ if {[info tclversion] >= 8.1} {
+ # set initial urlencoding
+ if { [lsearch -exact [encoding names] "utf-8"] != -1} {
+ cgi_urlencoding "utf-8"
+ } else {
+ cgi_urlencoding [encoding system]
+ }
+ }
+
+ # email addr of person responsible for this service
+ cgi_admin_mail_addr "root" ;# you should override this!
+
+ # most services won't have an actual email addr
+ cgi_mail_addr "CGI script - do not reply"
+}
+cgi_init
+
+# deduce tmp directory
+switch $tcl_platform(platform) {
+ unix {
+ set _cgi(tmpdir) /tmp
+ set _cgi(tmpperms) 0644
+ set _cgi(sendmail) [list /usr/lib/sendmail /usr/sbin/sendmail]
+ } macintosh {
+ set _cgi(tmpdir) [pwd]
+ set _cgi(tmpperms) {}
+ set _cgi(sendmail) {}
+ } default {
+ set _cgi(tmpdir) [pwd]
+ catch {set _cgi(tmpdir) $env(TMP)}
+ catch {set _cgi(tmpdir) $env(TEMP)}
+ set _cgi(tmpperms) {}
+ set _cgi(sendmail) {}
+ }
+}
+
+# regexp for matching attr=val
+set _cgi(attr,regexp) "^(\[^=]*)=(\[^\"].*)"
+
+package provide cgi @CGI_VERSION_FULL@
diff --git a/web/src/cgi.tcl-1.10/cgi.tcl.man b/web/src/cgi.tcl-1.10/cgi.tcl.man
new file mode 100644
index 00000000..c121800b
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/cgi.tcl.man
@@ -0,0 +1,36 @@
+.TH CGI.TCL 3 "12 December 1995"
+.SH NAME
+cgi.tcl \- procedures for CGI scripting in Tcl
+.SH DESCRIPTION
+
+These routines implement the code described in the paper "Writing CGI
+scripts in Tcl" which appeared in the Tcl '96 conference.
+
+This man page is really just a placeholder. See the README for more
+info.
+
+.SH SYNOPSIS
+.nf
+
+source cgi.tcl
+
+more to come...
+
+.fi
+No attempt is made to explain all aspects of the code. The paper is
+the right way to get started. After that, read the code \- the code
+really is quite straightforward. Feel free to make changes to it.
+Experiment. No claims of completeness are made. (In fact, I can
+assure you that there are missing pieces - there are things in CGI
+that I find utterly useless so I didn't bother to support them.)
+
+More to come...
+
+.SH SEE ALSO
+.SH AUTHOR
+Don Libes, libes@nist.gov, National Institute of Standards and Technology
+.SH ACKNOWLEDGEMENTS
+Design and implementation of the this software was paid for by the
+U.S. government and is therefore in the public domain. However the
+author and NIST would like credit if this program and documentation or
+portions of them are used.
diff --git a/web/src/cgi.tcl-1.10/configure b/web/src/cgi.tcl-1.10/configure
new file mode 100755
index 00000000..1c167432
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/configure
@@ -0,0 +1,2291 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.59.
+#
+# Copyright (C) 2003 Free Software Foundation, Inc.
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## --------------------- ##
+## M4sh Initialization. ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+ set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ as_unset=unset
+else
+ as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+ LC_TELEPHONE LC_TIME
+do
+ if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+ eval $as_var=C; export $as_var
+ else
+ $as_unset $as_var
+ fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)$' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+ /^X\/\(\/\/\)$/{ s//\1/; q; }
+ /^X\/\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" || {
+ # Find who we are. Look in the path if we contain no path at all
+ # relative or not.
+ case $0 in
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+ ;;
+ esac
+ # We did not find ourselves, most probably we were run as `sh COMMAND'
+ # in which case we are not to be found in the path.
+ if test "x$as_myself" = x; then
+ as_myself=$0
+ fi
+ if test ! -f "$as_myself"; then
+ { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2
+ { (exit 1); exit 1; }; }
+ fi
+ case $CONFIG_SHELL in
+ '')
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for as_base in sh bash ksh sh5; do
+ case $as_dir in
+ /*)
+ if ("$as_dir/$as_base" -c '
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then
+ $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+ $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+ CONFIG_SHELL=$as_dir/$as_base
+ export CONFIG_SHELL
+ exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+ fi;;
+ esac
+ done
+done
+;;
+ esac
+
+ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+ # uniformly replaced by the line number. The first 'sed' inserts a
+ # line-number line before each line; the second 'sed' does the real
+ # work. The second script uses 'N' to pair each line-number line
+ # with the numbered line, and appends trailing '-' during
+ # substitution so that $LINENO is not a special case at line end.
+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+ # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-)
+ sed '=' <$as_myself |
+ sed '
+ N
+ s,$,-,
+ : loop
+ s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+ t loop
+ s,-$,,
+ s,^['$as_cr_digits']*\n,,
+ ' >$as_me.lineno &&
+ chmod +x $as_me.lineno ||
+ { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+ { (exit 1); exit 1; }; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensible to this).
+ . ./$as_me.lineno
+ # Exit status is that of the last command.
+ exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+ *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T=' ' ;;
+ *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+ *) ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+ # We could just check for DJGPP; but this test a) works b) is more generic
+ # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+ if test -f conf$$.exe; then
+ # Don't use ln at all; we don't have any links
+ as_ln_s='cp -p'
+ else
+ as_ln_s='ln -s'
+ fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p=:
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" $as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+
+# Name of the host.
+# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+exec 6>&1
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_config_libobj_dir=.
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+# Maximum number of lines to put in a shell here document.
+# This variable seems obsolete. It should probably be removed, and
+# only ac_max_sed_lines should be used.
+: ${ac_max_here_lines=38}
+
+# Identity of this package.
+PACKAGE_NAME=
+PACKAGE_TARNAME=
+PACKAGE_VERSION=
+PACKAGE_STRING=
+PACKAGE_BUGREPORT=
+
+ac_unique_file="Makefile.in"
+ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CGI_MAJOR_VERSION CGI_MINOR_VERSION CGI_MICRO_VERSION CGI_VERSION_FULL CGI_VERSION CGI_LIB_FILE CGI_LIB_FILES CGI_TCL_EXECUTABLE LIBOBJS LTLIBOBJS'
+ac_subst_files=''
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+ac_prev=
+for ac_option
+do
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval "$ac_prev=\$ac_option"
+ ac_prev=
+ continue
+ fi
+
+ ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'`
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case $ac_option in
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir=$ac_optarg ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build_alias ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build_alias=$ac_optarg ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file=$ac_optarg ;;
+
+ --config-cache | -C)
+ cache_file=config.cache ;;
+
+ -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+ | --da=*)
+ datadir=$ac_optarg ;;
+
+ -disable-* | --disable-*)
+ ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+ { (exit 1); exit 1; }; }
+ ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+ eval "enable_$ac_feature=no" ;;
+
+ -enable-* | --enable-*)
+ ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+ { (exit 1); exit 1; }; }
+ ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+ case $ac_option in
+ *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "enable_$ac_feature='$ac_optarg'" ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix=$ac_optarg ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he | -h)
+ ac_init_help=long ;;
+ -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+ ac_init_help=recursive ;;
+ -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+ ac_init_help=short ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host_alias ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host_alias=$ac_optarg ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir=$ac_optarg ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir=$ac_optarg ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir=$ac_optarg ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir=$ac_optarg ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst \
+ | --locals | --local | --loca | --loc | --lo)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+ | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+ localstatedir=$ac_optarg ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir=$ac_optarg ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c | -n)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir=$ac_optarg ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix=$ac_optarg ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix=$ac_optarg ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix=$ac_optarg ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name=$ac_optarg ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir=$ac_optarg ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir=$ac_optarg ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site=$ac_optarg ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir=$ac_optarg ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir=$ac_optarg ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target_alias ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target_alias=$ac_optarg ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers | -V)
+ ac_init_version=: ;;
+
+ -with-* | --with-*)
+ ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid package name: $ac_package" >&2
+ { (exit 1); exit 1; }; }
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ case $ac_option in
+ *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "with_$ac_package='$ac_optarg'" ;;
+
+ -without-* | --without-*)
+ ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid package name: $ac_package" >&2
+ { (exit 1); exit 1; }; }
+ ac_package=`echo $ac_package | sed 's/-/_/g'`
+ eval "with_$ac_package=no" ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes=$ac_optarg ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries=$ac_optarg ;;
+
+ -*) { echo "$as_me: error: unrecognized option: $ac_option
+Try \`$0 --help' for more information." >&2
+ { (exit 1); exit 1; }; }
+ ;;
+
+ *=*)
+ ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
+ { echo "$as_me: error: invalid variable name: $ac_envvar" >&2
+ { (exit 1); exit 1; }; }
+ ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`
+ eval "$ac_envvar='$ac_optarg'"
+ export $ac_envvar ;;
+
+ *)
+ # FIXME: should be removed in autoconf 3.0.
+ echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+ expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+ : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+ { echo "$as_me: error: missing argument to $ac_option" >&2
+ { (exit 1); exit 1; }; }
+fi
+
+# Be sure to have absolute paths.
+for ac_var in exec_prefix prefix
+do
+ eval ac_val=$`echo $ac_var`
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* | NONE | '' ) ;;
+ *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+ { (exit 1); exit 1; }; };;
+ esac
+done
+
+# Be sure to have absolute paths.
+for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \
+ localstatedir libdir includedir oldincludedir infodir mandir
+do
+ eval ac_val=$`echo $ac_var`
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* ) ;;
+ *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+ { (exit 1); exit 1; }; };;
+ esac
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+ if test "x$build_alias" = x; then
+ cross_compiling=maybe
+ echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
+ If a cross compiler is detected then cross compile mode will be used." >&2
+ elif test "x$build_alias" != "x$host_alias"; then
+ cross_compiling=yes
+ fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then its parent.
+ ac_confdir=`(dirname "$0") 2>/dev/null ||
+$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$0" : 'X\(//\)[^/]' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$0" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ srcdir=$ac_confdir
+ if test ! -r $srcdir/$ac_unique_file; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+ if test "$ac_srcdir_defaulted" = yes; then
+ { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2
+ { (exit 1); exit 1; }; }
+ else
+ { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
+ { (exit 1); exit 1; }; }
+ fi
+fi
+(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null ||
+ { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2
+ { (exit 1); exit 1; }; }
+srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'`
+ac_env_build_alias_set=${build_alias+set}
+ac_env_build_alias_value=$build_alias
+ac_cv_env_build_alias_set=${build_alias+set}
+ac_cv_env_build_alias_value=$build_alias
+ac_env_host_alias_set=${host_alias+set}
+ac_env_host_alias_value=$host_alias
+ac_cv_env_host_alias_set=${host_alias+set}
+ac_cv_env_host_alias_value=$host_alias
+ac_env_target_alias_set=${target_alias+set}
+ac_env_target_alias_value=$target_alias
+ac_cv_env_target_alias_set=${target_alias+set}
+ac_cv_env_target_alias_value=$target_alias
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat <<_ACEOF
+\`configure' configures this package to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE. See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+ -h, --help display this help and exit
+ --help=short display options specific to this package
+ --help=recursive display the short help of all the included packages
+ -V, --version display version information and exit
+ -q, --quiet, --silent do not print \`checking...' messages
+ --cache-file=FILE cache test results in FILE [disabled]
+ -C, --config-cache alias for \`--cache-file=config.cache'
+ -n, --no-create do not create output files
+ --srcdir=DIR find the sources in DIR [configure dir or \`..']
+
+_ACEOF
+
+ cat <<_ACEOF
+Installation directories:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+ --bindir=DIR user executables [EPREFIX/bin]
+ --sbindir=DIR system admin executables [EPREFIX/sbin]
+ --libexecdir=DIR program executables [EPREFIX/libexec]
+ --datadir=DIR read-only architecture-independent data [PREFIX/share]
+ --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --libdir=DIR object code libraries [EPREFIX/lib]
+ --includedir=DIR C header files [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc [/usr/include]
+ --infodir=DIR info documentation [PREFIX/info]
+ --mandir=DIR man documentation [PREFIX/man]
+_ACEOF
+
+ cat <<\_ACEOF
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+
+ cat <<\_ACEOF
+
+_ACEOF
+fi
+
+if test "$ac_init_help" = "recursive"; then
+ # If there are subdirs, report their specific --help.
+ ac_popdir=`pwd`
+ for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+ test -d $ac_dir || continue
+ ac_builddir=.
+
+if test "$ac_dir" != .; then
+ ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+ ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+ .) # No --srcdir option. We are building in place.
+ ac_srcdir=.
+ if test -z "$ac_top_builddir"; then
+ ac_top_srcdir=.
+ else
+ ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+ fi ;;
+ [\\/]* | ?:[\\/]* ) # Absolute path.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir ;;
+ *) # Relative path.
+ ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+ case "$ac_dir" in
+ .) ac_abs_builddir=`pwd`;;
+ [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+ *) ac_abs_builddir=`pwd`/"$ac_dir";;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+ case ${ac_top_builddir}. in
+ .) ac_abs_top_builddir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+ *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+ case $ac_srcdir in
+ .) ac_abs_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+ *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+ case $ac_top_srcdir in
+ .) ac_abs_top_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+ *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+ esac;;
+esac
+
+ cd $ac_dir
+ # Check for guested configure; otherwise get Cygnus style configure.
+ if test -f $ac_srcdir/configure.gnu; then
+ echo
+ $SHELL $ac_srcdir/configure.gnu --help=recursive
+ elif test -f $ac_srcdir/configure; then
+ echo
+ $SHELL $ac_srcdir/configure --help=recursive
+ elif test -f $ac_srcdir/configure.ac ||
+ test -f $ac_srcdir/configure.in; then
+ echo
+ $ac_configure --help
+ else
+ echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+ fi
+ cd $ac_popdir
+ done
+fi
+
+test -n "$ac_init_help" && exit 0
+if $ac_init_version; then
+ cat <<\_ACEOF
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+ exit 0
+fi
+exec 5>config.log
+cat >&5 <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by $as_me, which was
+generated by GNU Autoconf 2.59. Invocation command line was
+
+ $ $0 $@
+
+_ACEOF
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`
+
+/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+hostinfo = `(hostinfo) 2>/dev/null || echo unknown`
+/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`
+/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ echo "PATH: $as_dir"
+done
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_sep=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+ for ac_arg
+ do
+ case $ac_arg in
+ -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ continue ;;
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+ ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ case $ac_pass in
+ 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;
+ 2)
+ ac_configure_args1="$ac_configure_args1 '$ac_arg'"
+ if test $ac_must_keep_next = true; then
+ ac_must_keep_next=false # Got value, back to normal.
+ else
+ case $ac_arg in
+ *=* | --config-cache | -C | -disable-* | --disable-* \
+ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+ | -with-* | --with-* | -without-* | --without-* | --x)
+ case "$ac_configure_args0 " in
+ "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+ esac
+ ;;
+ -* ) ac_must_keep_next=true ;;
+ esac
+ fi
+ ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'"
+ # Get rid of the leading space.
+ ac_sep=" "
+ ;;
+ esac
+ done
+done
+$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }
+$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log. We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Be sure not to use single quotes in there, as some shells,
+# such as our DU 5.0 friend, will then `close' the trap.
+trap 'exit_status=$?
+ # Save into config.log some information that might help in debugging.
+ {
+ echo
+
+ cat <<\_ASBOX
+## ---------------- ##
+## Cache variables. ##
+## ---------------- ##
+_ASBOX
+ echo
+ # The following way of writing the cache mishandles newlines in values,
+{
+ (set) 2>&1 |
+ case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in
+ *ac_space=\ *)
+ sed -n \
+ "s/'"'"'/'"'"'\\\\'"'"''"'"'/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p"
+ ;;
+ *)
+ sed -n \
+ "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+ ;;
+ esac;
+}
+ echo
+
+ cat <<\_ASBOX
+## ----------------- ##
+## Output variables. ##
+## ----------------- ##
+_ASBOX
+ echo
+ for ac_var in $ac_subst_vars
+ do
+ eval ac_val=$`echo $ac_var`
+ echo "$ac_var='"'"'$ac_val'"'"'"
+ done | sort
+ echo
+
+ if test -n "$ac_subst_files"; then
+ cat <<\_ASBOX
+## ------------- ##
+## Output files. ##
+## ------------- ##
+_ASBOX
+ echo
+ for ac_var in $ac_subst_files
+ do
+ eval ac_val=$`echo $ac_var`
+ echo "$ac_var='"'"'$ac_val'"'"'"
+ done | sort
+ echo
+ fi
+
+ if test -s confdefs.h; then
+ cat <<\_ASBOX
+## ----------- ##
+## confdefs.h. ##
+## ----------- ##
+_ASBOX
+ echo
+ sed "/^$/d" confdefs.h | sort
+ echo
+ fi
+ test "$ac_signal" != 0 &&
+ echo "$as_me: caught signal $ac_signal"
+ echo "$as_me: exit $exit_status"
+ } >&5
+ rm -f core *.core &&
+ rm -rf conftest* confdefs* conf$$* $ac_clean_files &&
+ exit $exit_status
+ ' 0
+for ac_signal in 1 2 13 15; do
+ trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo >confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+ if test "x$prefix" != xNONE; then
+ CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+ else
+ CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+ fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+ if test -r "$ac_site_file"; then
+ { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5
+echo "$as_me: loading site script $ac_site_file" >&6;}
+ sed 's/^/| /' "$ac_site_file" >&5
+ . "$ac_site_file"
+ fi
+done
+
+if test -r "$cache_file"; then
+ # Some versions of bash will fail to source /dev/null (special
+ # files actually), so we avoid doing that.
+ if test -f "$cache_file"; then
+ { echo "$as_me:$LINENO: loading cache $cache_file" >&5
+echo "$as_me: loading cache $cache_file" >&6;}
+ case $cache_file in
+ [\\/]* | ?:[\\/]* ) . $cache_file;;
+ *) . ./$cache_file;;
+ esac
+ fi
+else
+ { echo "$as_me:$LINENO: creating cache $cache_file" >&5
+echo "$as_me: creating cache $cache_file" >&6;}
+ >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in `(set) 2>&1 |
+ sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do
+ eval ac_old_set=\$ac_cv_env_${ac_var}_set
+ eval ac_new_set=\$ac_env_${ac_var}_set
+ eval ac_old_val="\$ac_cv_env_${ac_var}_value"
+ eval ac_new_val="\$ac_env_${ac_var}_value"
+ case $ac_old_set,$ac_new_set in
+ set,)
+ { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,set)
+ { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,);;
+ *)
+ if test "x$ac_old_val" != "x$ac_new_val"; then
+ { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
+echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+ { echo "$as_me:$LINENO: former value: $ac_old_val" >&5
+echo "$as_me: former value: $ac_old_val" >&2;}
+ { echo "$as_me:$LINENO: current value: $ac_new_val" >&5
+echo "$as_me: current value: $ac_new_val" >&2;}
+ ac_cache_corrupted=:
+ fi;;
+ esac
+ # Pass precious variables to config.status.
+ if test "$ac_new_set" = set; then
+ case $ac_new_val in
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+ ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+ *) ac_arg=$ac_var=$ac_new_val ;;
+ esac
+ case " $ac_configure_args " in
+ *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
+ *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+ esac
+ fi
+done
+if $ac_cache_corrupted; then
+ { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
+echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+ { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
+echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+CGI_MAJOR_VERSION=1
+CGI_MINOR_VERSION=10
+CGI_MICRO_VERSION=0
+CGI_VERSION=$CGI_MAJOR_VERSION.$CGI_MINOR_VERSION
+CGI_VERSION_FULL=$CGI_VERSION.$CGI_MICRO_VERSION
+
+# If `configure' is invoked (in)directly via `make', ensure that it
+# encounters no `make' conflicts.
+#
+unset MFLAGS MAKEFLAGS
+
+# this'll use a BSD compatible install or our included install-sh
+ac_aux_dir=
+for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
+ if test -f $ac_dir/install-sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f $ac_dir/install.sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ elif test -f $ac_dir/shtool; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/shtool install -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5
+echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;}
+ { (exit 1); exit 1; }; }
+fi
+ac_config_guess="$SHELL $ac_aux_dir/config.guess"
+ac_config_sub="$SHELL $ac_aux_dir/config.sub"
+ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure.
+
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5
+echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6
+if test -z "$INSTALL"; then
+if test "${ac_cv_path_install+set}" = set; then
+ echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in
+ ./ | .// | /cC/* | \
+ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+ ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \
+ /usr/ucb/* ) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ # Don't use installbsd from OSF since it installs stuff as root
+ # by default.
+ for ac_prog in ginstall scoinst install; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
+ if test $ac_prog = install &&
+ grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ :
+ elif test $ac_prog = install &&
+ grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # program-specific install script used by HP pwplus--don't use.
+ :
+ else
+ ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+ break 3
+ fi
+ fi
+ done
+ done
+ ;;
+esac
+done
+
+
+fi
+ if test "${ac_cv_path_install+set}" = set; then
+ INSTALL=$ac_cv_path_install
+ else
+ # As a last resort, use the slow shell script. We don't cache a
+ # path for INSTALL within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the path is relative.
+ INSTALL=$ac_install_sh
+ fi
+fi
+echo "$as_me:$LINENO: result: $INSTALL" >&5
+echo "${ECHO_T}$INSTALL" >&6
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+
+# Find a usable Tcl executable so that we can run Tcl utilities
+# For simplicity, assume Tcl is completely installed.
+echo "$as_me:$LINENO: checking for usable Tcl executable" >&5
+echo $ECHO_N "checking for usable Tcl executable... $ECHO_C" >&6
+for i in \
+ ${exec_prefix}/bin/tclsh \
+ `ls -r ${exec_prefix}/bin/tclsh[8-9]* 2>/dev/null` \
+ ${prefix}/bin/tclsh \
+ `ls -r ${prefix}/bin/tclsh[8-9]* 2>/dev/null` \
+ ${srcdir}/../tcl/unix/tclsh \
+ `ls -dr ${srcdir}/../tcl[8-9]*/unix/tclsh 2>/dev/null` \
+ /usr/local/bin/tclsh \
+ /usr/bin/tclsh ; do
+ if test -x "$i" ; then
+ CGI_TCL_EXECUTABLE=$i
+ break
+ fi
+done
+if test "x$CGI_TCL_EXECUTABLE" = "x" ; then
+ { { echo "$as_me:$LINENO: error: no tcl executable found, cannot install" >&5
+echo "$as_me: error: no tcl executable found, cannot install" >&2;}
+ { (exit 1); exit 1; }; }
+else
+ echo "$as_me:$LINENO: result: $CGI_TCL_EXECUTABLE" >&5
+echo "${ECHO_T}$CGI_TCL_EXECUTABLE" >&6
+fi
+
+#
+# Set up makefile substitutions
+#
+
+
+
+
+
+
+
+
+ ac_config_files="$ac_config_files Makefile cgi.tcl version"
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems. If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+{
+ (set) 2>&1 |
+ case `(ac_space=' '; set | grep ac_space) 2>&1` in
+ *ac_space=\ *)
+ # `set' does not quote correctly, so add quotes (double-quote
+ # substitution turns \\\\ into \\, and sed turns \\ into \).
+ sed -n \
+ "s/'/'\\\\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+ ;;
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n \
+ "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+ ;;
+ esac;
+} |
+ sed '
+ t clear
+ : clear
+ s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+ t end
+ /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+ : end' >>confcache
+if diff $cache_file confcache >/dev/null 2>&1; then :; else
+ if test -w $cache_file; then
+ test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file"
+ cat confcache >$cache_file
+ else
+ echo "not updating unwritable cache $cache_file"
+ fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# VPATH may cause trouble with some makes, so we remove $(srcdir),
+# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=/{
+s/:*\$(srcdir):*/:/;
+s/:*\${srcdir}:*/:/;
+s/:*@srcdir@:*/:/;
+s/^\([^=]*=[ ]*\):*/\1/;
+s/:*$//;
+s/^[^=]*=[ ]*$//;
+}'
+fi
+
+# Transform confdefs.h into DEFS.
+# Protect against shell expansion while executing Makefile rules.
+# Protect against Makefile macro expansion.
+#
+# If the first sed substitution is executed (which looks for macros that
+# take arguments), then we branch to the quote section. Otherwise,
+# look for a macro that doesn't take arguments.
+cat >confdef2opt.sed <<\_ACEOF
+t clear
+: clear
+s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\),-D\1=\2,g
+t quote
+s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\),-D\1=\2,g
+t quote
+d
+: quote
+s,[ `~#$^&*(){}\\|;'"<>?],\\&,g
+s,\[,\\&,g
+s,\],\\&,g
+s,\$,$$,g
+p
+_ACEOF
+# We use echo to avoid assuming a particular line-breaking character.
+# The extra dot is to prevent the shell from consuming trailing
+# line-breaks from the sub-command output. A line-break within
+# single-quotes doesn't work because, if this script is created in a
+# platform that uses two characters for line-breaks (e.g., DOS), tr
+# would break.
+ac_LF_and_DOT=`echo; echo .`
+DEFS=`sed -n -f confdef2opt.sed confdefs.h | tr "$ac_LF_and_DOT" ' .'`
+rm -f confdef2opt.sed
+
+
+ac_libobjs=
+ac_ltlibobjs=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+ # 1. Remove the extension, and $U if already installed.
+ ac_i=`echo "$ac_i" |
+ sed 's/\$U\././;s/\.o$//;s/\.obj$//'`
+ # 2. Add them.
+ ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext"
+ ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: ${CONFIG_STATUS=./config.status}
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
+echo "$as_me: creating $CONFIG_STATUS" >&6;}
+cat >$CONFIG_STATUS <<_ACEOF
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+## --------------------- ##
+## M4sh Initialization. ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+ set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ as_unset=unset
+else
+ as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+ LC_TELEPHONE LC_TIME
+do
+ if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+ eval $as_var=C; export $as_var
+ else
+ $as_unset $as_var
+ fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)$' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+ /^X\/\(\/\/\)$/{ s//\1/; q; }
+ /^X\/\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" || {
+ # Find who we are. Look in the path if we contain no path at all
+ # relative or not.
+ case $0 in
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+ ;;
+ esac
+ # We did not find ourselves, most probably we were run as `sh COMMAND'
+ # in which case we are not to be found in the path.
+ if test "x$as_myself" = x; then
+ as_myself=$0
+ fi
+ if test ! -f "$as_myself"; then
+ { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5
+echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ case $CONFIG_SHELL in
+ '')
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for as_base in sh bash ksh sh5; do
+ case $as_dir in
+ /*)
+ if ("$as_dir/$as_base" -c '
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then
+ $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+ $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+ CONFIG_SHELL=$as_dir/$as_base
+ export CONFIG_SHELL
+ exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+ fi;;
+ esac
+ done
+done
+;;
+ esac
+
+ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+ # uniformly replaced by the line number. The first 'sed' inserts a
+ # line-number line before each line; the second 'sed' does the real
+ # work. The second script uses 'N' to pair each line-number line
+ # with the numbered line, and appends trailing '-' during
+ # substitution so that $LINENO is not a special case at line end.
+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+ # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-)
+ sed '=' <$as_myself |
+ sed '
+ N
+ s,$,-,
+ : loop
+ s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+ t loop
+ s,-$,,
+ s,^['$as_cr_digits']*\n,,
+ ' >$as_me.lineno &&
+ chmod +x $as_me.lineno ||
+ { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5
+echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;}
+ { (exit 1); exit 1; }; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensible to this).
+ . ./$as_me.lineno
+ # Exit status is that of the last command.
+ exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+ *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T=' ' ;;
+ *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+ *) ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+ # We could just check for DJGPP; but this test a) works b) is more generic
+ # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+ if test -f conf$$.exe; then
+ # Don't use ln at all; we don't have any links
+ as_ln_s='cp -p'
+ else
+ as_ln_s='ln -s'
+ fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+else
+ as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p=:
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" $as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+exec 6>&1
+
+# Open the log real soon, to keep \$[0] and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling. Logging --version etc. is OK.
+exec 5>>config.log
+{
+ echo
+ sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+} >&5
+cat >&5 <<_CSEOF
+
+This file was extended by $as_me, which was
+generated by GNU Autoconf 2.59. Invocation command line was
+
+ CONFIG_FILES = $CONFIG_FILES
+ CONFIG_HEADERS = $CONFIG_HEADERS
+ CONFIG_LINKS = $CONFIG_LINKS
+ CONFIG_COMMANDS = $CONFIG_COMMANDS
+ $ $0 $@
+
+_CSEOF
+echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5
+echo >&5
+_ACEOF
+
+# Files that config.status was made for.
+if test -n "$ac_config_files"; then
+ echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_headers"; then
+ echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_links"; then
+ echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_commands"; then
+ echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+ac_cs_usage="\
+\`$as_me' instantiates files from templates according to the
+current configuration.
+
+Usage: $0 [OPTIONS] [FILE]...
+
+ -h, --help print this help, then exit
+ -V, --version print version number, then exit
+ -q, --quiet do not print progress messages
+ -d, --debug don't remove temporary files
+ --recheck update $as_me by reconfiguring in the same conditions
+ --file=FILE[:TEMPLATE]
+ instantiate the configuration file FILE
+
+Configuration files:
+$config_files
+
+Report bugs to <bug-autoconf@gnu.org>."
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+ac_cs_version="\\
+config.status
+configured by $0, generated by GNU Autoconf 2.59,
+ with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\"
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+srcdir=$srcdir
+INSTALL="$INSTALL"
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+# If no file are specified by the user, then we need to provide default
+# value. By we need to know if files were specified by the user.
+ac_need_defaults=:
+while test $# != 0
+do
+ case $1 in
+ --*=*)
+ ac_option=`expr "x$1" : 'x\([^=]*\)='`
+ ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'`
+ ac_shift=:
+ ;;
+ -*)
+ ac_option=$1
+ ac_optarg=$2
+ ac_shift=shift
+ ;;
+ *) # This is not an option, so the user has probably given explicit
+ # arguments.
+ ac_option=$1
+ ac_need_defaults=false;;
+ esac
+
+ case $ac_option in
+ # Handling of the options.
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ ac_cs_recheck=: ;;
+ --version | --vers* | -V )
+ echo "$ac_cs_version"; exit 0 ;;
+ --he | --h)
+ # Conflict between --help and --header
+ { { echo "$as_me:$LINENO: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&2;}
+ { (exit 1); exit 1; }; };;
+ --help | --hel | -h )
+ echo "$ac_cs_usage"; exit 0 ;;
+ --debug | --d* | -d )
+ debug=: ;;
+ --file | --fil | --fi | --f )
+ $ac_shift
+ CONFIG_FILES="$CONFIG_FILES $ac_optarg"
+ ac_need_defaults=false;;
+ --header | --heade | --head | --hea )
+ $ac_shift
+ CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg"
+ ac_need_defaults=false;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil | --si | --s)
+ ac_cs_silent=: ;;
+
+ # This is an error.
+ -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&2;}
+ { (exit 1); exit 1; }; } ;;
+
+ *) ac_config_targets="$ac_config_targets $1" ;;
+
+ esac
+ shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+ exec 6>/dev/null
+ ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+if \$ac_cs_recheck; then
+ echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6
+ exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+fi
+
+_ACEOF
+
+
+
+
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_config_target in $ac_config_targets
+do
+ case "$ac_config_target" in
+ # Handling of arguments.
+ "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+ "cgi.tcl" ) CONFIG_FILES="$CONFIG_FILES cgi.tcl" ;;
+ "version" ) CONFIG_FILES="$CONFIG_FILES version" ;;
+ *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
+echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
+ { (exit 1); exit 1; }; };;
+ esac
+done
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used. Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+ test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+fi
+
+# Have a temporary directory for convenience. Make it in the build tree
+# simply because there is no reason to put it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Create a temporary directory, and hook for its removal unless debugging.
+$debug ||
+{
+ trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0
+ trap '{ (exit 1); exit 1; }' 1 2 13 15
+}
+
+# Create a (secure) tmp directory for tmp files.
+
+{
+ tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` &&
+ test -n "$tmp" && test -d "$tmp"
+} ||
+{
+ tmp=./confstat$$-$RANDOM
+ (umask 077 && mkdir $tmp)
+} ||
+{
+ echo "$me: cannot create a temporary directory in ." >&2
+ { (exit 1); exit 1; }
+}
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+
+#
+# CONFIG_FILES section.
+#
+
+# No need to generate the scripts if there are no CONFIG_FILES.
+# This happens for instance when ./config.status config.h
+if test -n "\$CONFIG_FILES"; then
+ # Protect against being on the right side of a sed subst in config.status.
+ sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g;
+ s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF
+s,@SHELL@,$SHELL,;t t
+s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t
+s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t
+s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t
+s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t
+s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t
+s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t
+s,@exec_prefix@,$exec_prefix,;t t
+s,@prefix@,$prefix,;t t
+s,@program_transform_name@,$program_transform_name,;t t
+s,@bindir@,$bindir,;t t
+s,@sbindir@,$sbindir,;t t
+s,@libexecdir@,$libexecdir,;t t
+s,@datadir@,$datadir,;t t
+s,@sysconfdir@,$sysconfdir,;t t
+s,@sharedstatedir@,$sharedstatedir,;t t
+s,@localstatedir@,$localstatedir,;t t
+s,@libdir@,$libdir,;t t
+s,@includedir@,$includedir,;t t
+s,@oldincludedir@,$oldincludedir,;t t
+s,@infodir@,$infodir,;t t
+s,@mandir@,$mandir,;t t
+s,@build_alias@,$build_alias,;t t
+s,@host_alias@,$host_alias,;t t
+s,@target_alias@,$target_alias,;t t
+s,@DEFS@,$DEFS,;t t
+s,@ECHO_C@,$ECHO_C,;t t
+s,@ECHO_N@,$ECHO_N,;t t
+s,@ECHO_T@,$ECHO_T,;t t
+s,@LIBS@,$LIBS,;t t
+s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t
+s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t
+s,@INSTALL_DATA@,$INSTALL_DATA,;t t
+s,@CGI_MAJOR_VERSION@,$CGI_MAJOR_VERSION,;t t
+s,@CGI_MINOR_VERSION@,$CGI_MINOR_VERSION,;t t
+s,@CGI_MICRO_VERSION@,$CGI_MICRO_VERSION,;t t
+s,@CGI_VERSION_FULL@,$CGI_VERSION_FULL,;t t
+s,@CGI_VERSION@,$CGI_VERSION,;t t
+s,@CGI_LIB_FILE@,$CGI_LIB_FILE,;t t
+s,@CGI_LIB_FILES@,$CGI_LIB_FILES,;t t
+s,@CGI_TCL_EXECUTABLE@,$CGI_TCL_EXECUTABLE,;t t
+s,@LIBOBJS@,$LIBOBJS,;t t
+s,@LTLIBOBJS@,$LTLIBOBJS,;t t
+CEOF
+
+_ACEOF
+
+ cat >>$CONFIG_STATUS <<\_ACEOF
+ # Split the substitutions into bite-sized pieces for seds with
+ # small command number limits, like on Digital OSF/1 and HP-UX.
+ ac_max_sed_lines=48
+ ac_sed_frag=1 # Number of current file.
+ ac_beg=1 # First line for current file.
+ ac_end=$ac_max_sed_lines # Line after last line for current file.
+ ac_more_lines=:
+ ac_sed_cmds=
+ while $ac_more_lines; do
+ if test $ac_beg -gt 1; then
+ sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+ else
+ sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+ fi
+ if test ! -s $tmp/subs.frag; then
+ ac_more_lines=false
+ else
+ # The purpose of the label and of the branching condition is to
+ # speed up the sed processing (if there are no `@' at all, there
+ # is no need to browse any of the substitutions).
+ # These are the two extra sed commands mentioned above.
+ (echo ':t
+ /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed"
+ else
+ ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed"
+ fi
+ ac_sed_frag=`expr $ac_sed_frag + 1`
+ ac_beg=$ac_end
+ ac_end=`expr $ac_end + $ac_max_sed_lines`
+ fi
+ done
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds=cat
+ fi
+fi # test -n "$CONFIG_FILES"
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case $ac_file in
+ - | *:- | *:-:* ) # input from stdin
+ cat >$tmp/stdin
+ ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+ ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+ *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+ ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+ * ) ac_file_in=$ac_file.in ;;
+ esac
+
+ # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories.
+ ac_dir=`(dirname "$ac_file") 2>/dev/null ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_file" : 'X\(//\)[^/]' \| \
+ X"$ac_file" : 'X\(//\)$' \| \
+ X"$ac_file" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ { if $as_mkdir_p; then
+ mkdir -p "$ac_dir"
+ else
+ as_dir="$ac_dir"
+ as_dirs=
+ while test ! -d "$as_dir"; do
+ as_dirs="$as_dir $as_dirs"
+ as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ done
+ test ! -n "$as_dirs" || mkdir $as_dirs
+ fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+ { (exit 1); exit 1; }; }; }
+
+ ac_builddir=.
+
+if test "$ac_dir" != .; then
+ ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+ ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+ .) # No --srcdir option. We are building in place.
+ ac_srcdir=.
+ if test -z "$ac_top_builddir"; then
+ ac_top_srcdir=.
+ else
+ ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+ fi ;;
+ [\\/]* | ?:[\\/]* ) # Absolute path.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir ;;
+ *) # Relative path.
+ ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+ case "$ac_dir" in
+ .) ac_abs_builddir=`pwd`;;
+ [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+ *) ac_abs_builddir=`pwd`/"$ac_dir";;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+ case ${ac_top_builddir}. in
+ .) ac_abs_top_builddir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+ *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+ case $ac_srcdir in
+ .) ac_abs_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+ *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+ case $ac_top_srcdir in
+ .) ac_abs_top_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+ *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+ esac;;
+esac
+
+
+ case $INSTALL in
+ [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+ *) ac_INSTALL=$ac_top_builddir$INSTALL ;;
+ esac
+
+ if test x"$ac_file" != x-; then
+ { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+ rm -f "$ac_file"
+ fi
+ # Let's still pretend it is `configure' which instantiates (i.e., don't
+ # use $as_me), people would be surprised to read:
+ # /* config.h. Generated by config.status. */
+ if test x"$ac_file" = x-; then
+ configure_input=
+ else
+ configure_input="$ac_file. "
+ fi
+ configure_input=$configure_input"Generated from `echo $ac_file_in |
+ sed 's,.*/,,'` by configure."
+
+ # First look for the input files in the build tree, otherwise in the
+ # src tree.
+ ac_file_inputs=`IFS=:
+ for f in $ac_file_in; do
+ case $f in
+ -) echo $tmp/stdin ;;
+ [\\/$]*)
+ # Absolute (can't be DOS-style, as IFS=:)
+ test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+ { (exit 1); exit 1; }; }
+ echo "$f";;
+ *) # Relative
+ if test -f "$f"; then
+ # Build tree
+ echo "$f"
+ elif test -f "$srcdir/$f"; then
+ # Source tree
+ echo "$srcdir/$f"
+ else
+ # /dev/null tree
+ { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+ { (exit 1); exit 1; }; }
+ fi;;
+ esac
+ done` || { (exit 1); exit 1; }
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+ sed "$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s,@configure_input@,$configure_input,;t t
+s,@srcdir@,$ac_srcdir,;t t
+s,@abs_srcdir@,$ac_abs_srcdir,;t t
+s,@top_srcdir@,$ac_top_srcdir,;t t
+s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t
+s,@builddir@,$ac_builddir,;t t
+s,@abs_builddir@,$ac_abs_builddir,;t t
+s,@top_builddir@,$ac_top_builddir,;t t
+s,@abs_top_builddir@,$ac_abs_top_builddir,;t t
+s,@INSTALL@,$ac_INSTALL,;t t
+" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out
+ rm -f $tmp/stdin
+ if test x"$ac_file" != x-; then
+ mv $tmp/out $ac_file
+ else
+ cat $tmp/out
+ rm -f $tmp/out
+ fi
+
+done
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+{ (exit 0); exit 0; }
+_ACEOF
+chmod +x $CONFIG_STATUS
+ac_clean_files=$ac_clean_files_save
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded. So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status. When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+ ac_cs_success=:
+ ac_config_status_args=
+ test "$silent" = yes &&
+ ac_config_status_args="$ac_config_status_args --quiet"
+ exec 5>/dev/null
+ $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+ exec 5>>config.log
+ # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+ # would make configure fail if this is the last instruction.
+ $ac_cs_success || { (exit 1); exit 1; }
+fi
+
diff --git a/web/src/cgi.tcl-1.10/configure.in b/web/src/cgi.tcl-1.10/configure.in
new file mode 100644
index 00000000..7b7f6880
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/configure.in
@@ -0,0 +1,52 @@
+# Process this file with autoconf to produce a configure script.
+
+AC_INIT(Makefile.in)
+CGI_MAJOR_VERSION=1
+CGI_MINOR_VERSION=10
+CGI_MICRO_VERSION=0
+CGI_VERSION=$CGI_MAJOR_VERSION.$CGI_MINOR_VERSION
+CGI_VERSION_FULL=$CGI_VERSION.$CGI_MICRO_VERSION
+
+# If `configure' is invoked (in)directly via `make', ensure that it
+# encounters no `make' conflicts.
+#
+unset MFLAGS MAKEFLAGS
+
+# this'll use a BSD compatible install or our included install-sh
+AC_PROG_INSTALL
+
+# Find a usable Tcl executable so that we can run Tcl utilities
+# For simplicity, assume Tcl is completely installed.
+AC_MSG_CHECKING([for usable Tcl executable])
+for i in \
+ ${exec_prefix}/bin/tclsh \
+ `ls -r ${exec_prefix}/bin/tclsh[[8-9]]* 2>/dev/null` \
+ ${prefix}/bin/tclsh \
+ `ls -r ${prefix}/bin/tclsh[[8-9]]* 2>/dev/null` \
+ ${srcdir}/../tcl/unix/tclsh \
+ `ls -dr ${srcdir}/../tcl[[8-9]]*/unix/tclsh 2>/dev/null` \
+ /usr/local/bin/tclsh \
+ /usr/bin/tclsh ; do
+ if test -x "$i" ; then
+ CGI_TCL_EXECUTABLE=$i
+ break
+ fi
+done
+if test "x$CGI_TCL_EXECUTABLE" = "x" ; then
+ AC_MSG_ERROR([no tcl executable found, cannot install])
+else
+ AC_MSG_RESULT($CGI_TCL_EXECUTABLE)
+fi
+
+#
+# Set up makefile substitutions
+#
+AC_SUBST(CGI_MAJOR_VERSION)
+AC_SUBST(CGI_MINOR_VERSION)
+AC_SUBST(CGI_MICRO_VERSION)
+AC_SUBST(CGI_VERSION_FULL)
+AC_SUBST(CGI_VERSION)
+AC_SUBST(CGI_LIB_FILE)
+AC_SUBST(CGI_LIB_FILES)
+AC_SUBST(CGI_TCL_EXECUTABLE)
+AC_OUTPUT(Makefile cgi.tcl version)
diff --git a/web/src/cgi.tcl-1.10/doc/ref.txt b/web/src/cgi.tcl-1.10/doc/ref.txt
new file mode 100644
index 00000000..79ddd721
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/doc/ref.txt
@@ -0,0 +1,1651 @@
+cgi.tcl - A Reference Manual (Draft)
+by Don Libes
+
+This document contains technical notes on using cgi.tcl. This
+document is a draft and has not been officially reviewed.
+
+This document assumes that you have read the Tcl '96 paper "CGI
+Scripting in Tcl". That document will give you the feel for what this
+code is all about. In contrast, this document provides the details.
+
+This document assumes you know HTML. I'm not going to explain what
+particular tags do or how to use them effectively, except as necessary
+to understand the document.
+
+Some of the commands may not work with all browsers. For example, the
+cgi_center command is generally understood only by some Netscape
+browsers because it produces <center></center> tags which are not
+commonly supported. You'll have to use your judgement. Remember:
+Just because a command exists to produce the HTML doesn't mean your
+browser will do anything meaningful with it. In that sense, using
+this code is no different than handcoding HTML.
+
+**************************************************
+A NOTE ABOUT PROCEDURE NAMES
+**************************************************
+
+All procedures are named cgi_XXX. You can also call them without the
+cgi_ prefix. Using the cgi_XXX form is no big deal for rarely used
+procedures, but the aliases are particularly convenient for things
+like hr and br. Aliases are suppressed if procedures exist by those
+names already. (Thus, cgi_eval cannot be invoked as "eval"!)
+Similarly, you can overwrite the aliases with impunity. Internally,
+references are only made to the cgi_ names.
+
+I'm still thinking about this. If you have strong feelings about it,
+let me know.
+
+**************************************************
+SECURITY
+**************************************************
+
+I frequently see statements saying that Tcl (and other interpretive
+languages, for that matter) are insecure and should not be used for
+writing CGI.
+
+I disagree. It is possible to use Tcl securely and it really isn't
+very hard. The key, of course, is to not blindly evaluate user input.
+There really isn't much reason to. For instance, this library itself
+is pretty big and does lots of things, but nothing in it evaluates
+user input. (It does do a lot of evaluation of *programmer* input,
+but that's quite acceptable.)
+
+The two classes of commands you should pay close attention to are
+commands that do eval operations and commands that invoke external
+programs.
+
+**************************************************
+GENERAL NOTES ABOUT PARAMETERS
+**************************************************
+
+** There are several basic styles of parameter passing.
+
+-- Container commands (e.g., <body></body>, <div></div>, etc.)
+
+The last argument is typically a block of code. Other arguments
+become attributes. For example:
+
+ cgi_body bgcolor=red background=white {
+ h4 "hello"
+ }
+
+produces:
+
+ <body bgcolor="red" background="white">
+ <h4>hello</h4>
+ </body>
+
+-- Commands with required arguments
+
+Some commands have required arguments. Required arguments which are
+relatively long, are passed *after* all others. For example:
+
+ cgi_h4 align=left "Foo's Bar & Grill"
+
+Another example:
+
+ cgi_body bgcolor=red background=white {
+ cgi_h4 "Foo's Bar & Grill"
+ }
+
+Commands with relatively short arguments have the short arguments
+passed *before* all others. This avoids many redundant keywords. In
+the following example, the "img=" is omitted because it is already
+implied by the command. The "alt=" is not optional (although the
+entire argument is).
+
+ cgi_img foo.gif "alt=Foo's Bar & Grill"
+
+Note the quotes around the alt argument. This is only necessary if
+the argument has whitespace in it - a consequence of Tcl's normal
+scanning rules. The resulting HTML automatically includes quotes
+around the value attribute. (See Case-sensitivity.)
+
+** Case-sensitivity and Quotes and Whitespace
+
+Attribute names are case sensitive. Use lowercase names for "normal"
+handling. For example, values for attributes such as "url" and
+"value" are quoted and encoded. Use uppercase names to suppress the
+usual processing. (For example, you might want to test how a browser
+responds to incorrect HTML.)
+
+Consider:
+
+ cgi_body bgcolor=#123456 {
+ p [cgi_img foo.gif "alt=Foo's Bar & Grill"]
+ }
+
+This is translated to
+
+ <body bgcolor="#123456">
+ <p><img src="foo.gif" alt="Foo's Bar &amp; Grill"></p>
+ </body>
+
+Notice how the ampersand in the alt value has been encoded. Also
+notice how quotes have been added to the values of bgcolor, url, and
+alt. Thus you no longer have to add quotes all over the place (or
+remember when you need to).
+
+Embedded whitespace is protected by quoting in the usual Tcl fashion
+rather than typical HTML fashion.
+
+So instead of:
+ img foo.gif alt="foo bar"
+do this:
+ img foo.gif "alt=foo bar"
+which will return HTML that is properly quoted:
+ <img src="foo.gif" alt="foo bar">
+
+-- Name-value commands
+
+Many commands produce tags that use "name" and "value" attributes.
+Because these attributes are almost always used in such commands, the
+first argument is always of the form name=value so the literal "name"
+and "value" can be omitted. For example:
+
+ cgi_text color=Red size=5
+
+produces:
+
+ <input name="color" value="Red" size=5>
+
+Reasonable defaults exist. For example, if you don't need the value
+of a submit button (but the user still needs to see it appear as the
+label), omit the name:
+
+ cgi_submit_button =Execute
+
+If no "=" is present, the string is assumed to be the "name"
+attribute. For example:
+
+ cgi_checkbox Vegies
+
+produces a checkbox associated with the variable named "Vegies".
+(With no specified value, it will be set to "on" if checked.)
+
+Most of the commands have reasonable defaults. For example, to
+quickly script a submit button, the following suffices:
+
+ cgi_submit_button
+
+Certain constructions make no sense and are therefore disallowed. For
+example, a submit button with a name but no value makes no sense, so
+cgi_submit_button doesn't allow it. (In other words, if you provide
+an argument, it has to have "=" in it.)
+
+**************************************************
+JAVASCRIPT ARGUMENTS
+**************************************************
+
+JavaScript event attributes such as onClick are handled just like any
+other attributes in terms of quoting and case sensitivity. Because
+there are so many attributes on so many tags, they are not documented
+explicitly in this manual. However, they are all supported. Here is
+an example:
+
+ cgi_text age=18 onChange=MinimumAge(this.form.age)
+
+**************************************************
+PROCEDURES TO ASSIST IN DEBUGGING
+**************************************************
+
+** User-id differences
+
+You can interactively run and debug CGI scripts by simply running them
+from the shell, or a Tcl or C debugger. (For convenience, I use
+Expect instead of tclsh just so that I get the debugger.) In fact,
+I've never had to resort to the C debugger. However, simply watching
+the raw output from a shell is very handy. This catches the really
+basic errors such as incorrect protections, incorrect #! line, etc.
+Once your script is actually executing, you can rely on cgi_eval (see
+below).
+
+cgi_uid_check user
+
+Typically, a CGI script is intended to run from a particular uid, such
+as "nobody". If you run such scripts interactively, you can end up
+with conflicts. For example, if the script creates files, files can
+accidentally end up being owned by you.
+
+cgi_uid_check is a convenient mechanism to warn against this problem.
+Simply call cgi_uid_check in your script. The argument is the uid
+under which the script should be running. If the given uid does
+not match the actual uid, cgi_uid_check will generate an error.
+
+** Trapping error messages
+
+cgi_eval
+
+cgi_eval is the primary error catching/reporting mechanism. Execute
+commands from cgi_eval so that errors can be caught and reported in a
+nice way. By default, errors are emailed back to the administrator.
+"cgi_debug -on" makes errors be immediately viewable in the browser.
+
+If an error is caught when debugging is enabled, the diagnostic is
+anchored with #cgierror. This is useful in scripts that produce
+voluminous output.
+
+cgi_error_occurred
+
+cgi_error_occurred returns 1 if an error occurred and was caught by
+cgi_eval. This separate function simplifies exit handling - for
+example, rather than always checking the return value of cgi_eval, an
+app can just test this from a redefined exit.
+
+cgi_admin_mail_addr addr
+
+cgi_admin_mail_addr sets the administrator's email address.
+Diagnostics are sent via email if a problem is encountered with the
+script and debugging is not enabled.
+
+cgi_name name
+
+cgi_name defines a name for the service. If called with no arguments,
+the name is returned. The name is currently used in the following
+places:
+
+ If email is sent, it comes from [cgi_name].
+ If errors are emailed, the subject is "[cgi_name]: CGI problem"
+
+** Generating debugging output and other commands
+
+cgi_debug args
+
+cgi_debug provides rudimentary support for generating debugging
+messages. Here are some example calls:
+
+cgi_debug cmd
+ If debugging is on, the command is evaluated. For example:
+
+ cgi_debug {
+ h2 "completed initialization"
+ }
+ or
+ cgi_debug {h2 "completed initialization"}
+
+ Note this is more than simply calling h2. Context is rewound
+ or forwarded to get to a place where this is safe. (And
+ conversely, this call can suppress things such as header
+ elements that haven't been processed yet.) The flag
+ "-noprint" suppresses this manipulation - this is useful for
+ early code that causes no printing.
+
+ The -- flag causes the next argument to treated as a command
+ even if it looks like another flag.
+cgi_debug -on
+ Enables debugging messages. This includes debugging messages
+ generated by explicit calls to cgi_debug as well as implicit
+ diagnostics, for example, that report on form input.
+cgi_debug -temp text
+ Enable debugging for this one line.
+cgi_debug -off
+ Disable debugging.
+
+cgi_debug always returns the old setting ("-on" or "-off"). The
+initial value is -off.
+
+
+** Printing arrays
+
+cgi_parray arrayname
+
+cgi_parray prints out the elements of a Tcl array. cgi_parray is just
+like Tcl's parray except that its output is appropriately formatted
+for a browser.
+
+**************************************************
+BASIC STRUCTURE OF A CGI SCRIPT
+**************************************************
+
+Typically, the basic structure of most CGI scripts is:
+
+ package require cgi
+
+ cgi_eval {
+ cgi_http_head {cmds}
+ cgi_html {
+ cgi_head {cmds}
+ cgi_body {cmds}
+ }
+ }
+
+Much of this can be omitted, however. In fact, a typical script looks
+more like this:
+
+ package require cgi
+
+ cgi_eval {
+ cgi_title "title"
+ cgi_body {
+ cmds
+ }
+ }
+
+(If you're not using the Tcl package support, replace the 'package
+require' command with a 'source' command of the specific file
+containing the cgi.tcl source.)
+
+(The "...." in the examples above should be replaced by the true path
+to the cgi.tcl file.)
+
+I'll now go through each of these in more detail as well as some other
+possibilities for the overall structure.
+
+**************************************************
+HTTP HEADERS
+**************************************************
+
+cgi_http_head cmds
+
+CGI scripts must produce various headers to explain how the remainder
+of the output is to be interpreted. No other output may preceed this!
+
+With no argument, an HTML content type is produced if the script is
+running in the CGI environment. This means that most people need not
+bother calling cgi_http_head. However, if you want to see the HTTP
+headers and you are not running in the CGI environment, you should
+call cgi_http_head explicitly. (Alternatively, you can make it appear
+that you are in the CGI environment by defining the environment
+variable REQUEST_METHOD.)
+
+The remaining commands in this section may be used in cgi_http_head.
+Most should be intuitively obvious and thus need no explanation.
+
+cgi_content_type type
+ Generates a "Content-type:" header.
+
+With no argument, cgi_content_type generates a declaration for HTML.
+If specified, the 'type' argument should be the full MIME-style
+type/subtype declaration. Any MIME-style parameters should be
+included in the type argument.
+
+cgi_redirect location
+ Generates a redirect header (Status:/Location:/URI:)
+cgi_target
+ Generates a "Window-target:" header.
+cgi_refresh seconds url
+ Generates a "Refresh:" header. The url argument is optional.
+cgi_pragma pragma
+ Generates a "Pragma:" header.
+cgi_status number string
+ Generates a "Status:" header.
+
+** Cookies
+
+cgi_cookie_set name=val args
+ Define a cookie with given name, value, and other arguments.
+ Cookie values are automatically encoded to protect odd characters.
+ A couple expirations are predefined (with intuitive meaning):
+ expires=never
+ expires=now
+ expires=...actual date...
+
+ Here are some examples:
+ cgi_cookie_set user=don domain=nist.gov expires=never
+ cgi_cookie_set user=don expires=now secure
+
+cgi_export_cookie name args
+ Export the named Tcl variable as a cookie. Other arguments are
+ processed as with cgi_cookie_set.
+
+Cookies may be read only after calling cgi_input. The following
+routines read cookie names or specific cookies.
+
+cgi_cookie_list
+ Returns the list of all cookies supplied by the server.
+
+cgi_cookie_get name
+ Returns the value of the named cookie. If multiple values exists
+ the most specific path mapping is used.
+
+ If the "-all" flag is used (before the cookie name), a list is
+ returned containing all cookie values for the given name. The
+ list is ordered most-specific (path mapping) to least. I.e.,
+ the first value on the list is the same one returned by
+ calling cgi_cookie get without a flag.
+
+cgi_import_cookie name
+ Define a Tcl variable with the value of the cookie of the same
+ name. For example, the following command retrieves the cookie
+ named "Password" and stores it in the Tcl variable "Password".
+
+ cgi_import_cookie Password
+
+
+**************************************************
+GENERATING HTML
+**************************************************
+
+cgi_html
+
+<html></html> tags can be generated using cgi_html. An argument to
+cgi_html is evaluated to produce the actual HTML code.
+
+In practice, it is not necessary to use cgi_html. CGI.tcl will
+automatically generate the tags when appropriate. (Oddly, modern HTML
+specs don't require it and most if not all browsers never cared
+anyway.)
+
+cgi_doctype
+
+cgi_doctype is a user-defined procedure that produces a SGML DOCTYPE
+declaration. If it exists, cgi_doctype is automatically invoked at
+the beginning of cgi_html. (This library does not automatically create
+DOCTYPE declarations since the library is not restricted to generating
+SGML for any single DTD. Realistically, DOCTYPEs are pointless for
+HTML generation since web browsers don't require DOCTYPE declarations.
+However, if you are creating pages for some other purpose that
+requires such a declaration, use cgi_doctype.)
+
+cgi_head
+
+<head></head> tags can be generated using cgi_head. An argument to
+cgi_head is evaluated to produce the actual headers.
+
+In practice, it is not necessary to use cgi_head. CGI.tcl will
+automatically generate the tags when appropriate. (Oddly, modern HTML
+specs don't require it and most if not all browsers never cared
+anyway. So for example:
+
+ cgi_head {
+ cgi_title "my page"
+ }
+
+is equivalent to:
+
+ cgi_title "my page"
+
+Note that cgi_title will be called automatically if you omit
+cgi_title, cgi_head, or call cgi_head with no arguments.
+
+cgi_title title
+
+cgi_title defines the title of a page. It is called from within a
+<head></head> pair. If not called from within cgi_head, it implicitly
+forces it to occur.
+
+cgi_title always returns the title. With no argument, cgi_title
+returns the old title without changing it.
+
+cgi_http_equiv
+
+cgi_http_equiv is equivalent to cgi_http_head but from within
+cgi_head. This procedure is defined for completeness - there is no
+reason to use it. In fact, it doesn't allow all cgi_http_head
+declarations, so it should be avoided.
+
+cgi_meta
+
+cgi_meta generates a <meta> tag. You can do whatever you want with
+these. (Read some an HTML document for more info.) For example:
+
+ meta name=author {content="Don Libes"}
+
+cgi_script cmd
+
+cgi_script evaluates its arguments inside of <script></script> tags.
+This is appropriate for putting in client-side scripting. Optional
+arguments are passed as attributes to the <script> tag.
+
+Note that the cmd argument is a Tcl command, not a command in the
+other scripting language. So if all you want to do is print out some
+script, use cgi_puts:
+
+ cgi_script {
+ cgi_puts {
+ some scripting stuff
+ in whatever weird funky
+ language you want
+ }
+}
+
+cgi_javascript cmd
+
+cgi_javascript is a version of cgi_script specialized for javascript.
+At present, all it does is add the comment hacks so that the
+javascript can't be seen by old browsers.
+
+cgi_noscript cmd
+
+cgi_noscript evaluates its argument to generate code for browsers that
+do not understand cgi_script or its variants.
+
+cgi_body
+
+<body></body> tags are generated using cgi_body. An argument to
+cgi_body is evaluated to produce the actual body.
+
+Executing "return" from within cgi_body causes cgi_body to return.
+This is useful if more code follows the cgi_body within the cgi_eval.
+Compare to cgi_exit (see elsewhere).
+
+cgi_body_args
+
+Arguments to cgi_body_args are made available to cgi_body as if they
+had been specified in the call to cgi_body. This provides a
+convenient way for using the same colors, backgrounds, etc, in a set
+of pages.
+
+cgi_exit
+
+cgi_exit provides a fast way of cleanly exiting a CGI script without
+having to manually unwind procedures. In particular, cgi_exit forces
+closure of all open tags. It then calls exit. This is useful if you
+want to exit from a CGI script at any point and still have the HTML be
+correct.
+
+** Frames
+
+cgi_frameset cmd
+
+Instead of cgi_body, you can call cgi_frameset to create framed
+documents. This produces <frameset></frameset> tags with the content
+filled by evaluation of cmd. Optional arguments are passed on as
+attributes.
+
+cgi_frame name=url
+
+cgi_frame defines a frame with the given name and url. The argument
+handling is the same as for other name-value commands (even though the
+value here is a url). The url is automatically double-quoted. Other
+optional arguments are passed on as attributes.
+
+cgi_noframes cmd
+
+cgi_noframes produces <noframes></noframes> tags with the content
+filled evaluation of cmd. Optional arguments are passed on as
+attributes.
+
+**************************************************
+CONTAINER SUPPORT
+**************************************************
+
+cgi_division
+
+cgi_division evaluates its last argument, grouping it together. This
+is useful for acting on a group of paragraphs, such as for alignment
+purposes.
+
+cgi_center
+
+cgi_center is similar to "cgi_division align=center".
+
+**************************************************
+SINGLE PARAGRAPH SUPPORT
+**************************************************
+
+Everything in this section generates a single paragraph or line break.
+Most of these take a string as the last argument which is
+appropriately formatted. Any other arguments are used as tag
+attributes.
+
+cgi_p
+cgi_address
+cgi_blockquote
+cgi_h1 through h7
+
+Most of these procedures should be intuitive. They all take a string
+and display it in the appropriate way. For example, a level 2
+heading:
+
+ h2 "Paragraph Support"
+
+Here's a paragraph with some formatting (see next section for more
+info):
+
+ p "I [bold love] Tcl but hate [blink "blinking text"]"
+
+Note that some of these generate tags that are not supported by all
+browsers. See the format-tour.cgi script to see these in use.
+
+cgi_br
+
+cgi_br causes a paragraph break to be printed. Additional arguments
+are passed on as attributes.
+
+To embed a paragraph break (rather than printing it), use cgi_nl. In
+the following example, it is much more convenient to call cgi_br than
+cgi_nl:
+
+ radio_button "version=1"
+ br
+ radio_button "version=2"
+
+See cgi_nl for more info.
+
+**************************************************
+TEXT SUPPORT
+**************************************************
+
+The following procedures take a string and return an appropriately
+formatted version. The string is always the last argument. Any other
+arguments are used as tag attributes.
+
+cgi_bold
+cgi_italic
+cgi_underline
+cgi_strikeout
+cgi_subscript
+cgi_superscript
+cgi_typewriter
+cgi_blink
+cgi_emphasis
+cgi_strong
+cgi_cite
+cgi_sample
+cgi_keyboard
+cgi_variable
+cgi_definition
+cgi_big
+cgi_small
+cgi_font
+
+ p "I [bold love] Tcl but hate [blink "blinking text"]"
+
+Note that some of these generate tags that are not supported by all
+browsers. See the format-tour.cgi script to see these in use.
+
+cgi_basefont
+
+cgi_basefont defines the base font.
+
+**************************************************
+SPECIAL CHARACTERS OR CHARACTER SEQUENCES
+**************************************************
+
+The following procedures produce characters such that when interpreted
+by a browser returns the indicated character.
+
+ Returns
+cgi_lt <
+cgi_gt >
+cgi_amp &
+cgi_quote "
+cgi_enspace en space
+cgi_emspace em space
+cgi_nbspace nonbreaking space
+cgi_tm registered trademark
+cgi_copyright copyright
+cgi_isochar n ISO character #n
+
+cgi_nl
+
+cgi_nl returns a paragraph break string suitable for embedding in a
+string just as you would embed a newline in a Tcl string via \n.
+
+To print a paragraph break rather than returning it, use cgi_br. In
+the following example, it is much more convenient to call cgi_nl than
+than cgi_br:
+
+ h2 "This appears[nl]on two lines."
+
+See cgi_br for more info.
+
+cgi_breakable
+
+cgi_breakable indicates a place in a word at which the browser can
+break a string across two lines.
+
+cgi_unbreakable cmd
+
+cgi_unbreakable evaluates a cmd in such a way that the output will not
+be broken across lines by the browser just because the screen width is
+exceeded. Instead a horizontal scrollbar will appear so that the
+browser can be manually scrolled to see the long line.
+n
+cgi_unbreakable_string string
+
+cgi_unbreakable_string returns its arguments so that it will not be
+broken across lines by the browser just because the screen width is
+exceeded. Instead a horizontal scrollbar will appear so that the
+browser can be manually scrolled to see the long line.
+
+Notes:
+
+- It is my assumption that cgi_unbreakable will be much more commonly
+used than the _string version, hence the choice of names. Feel free
+to let me know what I'm wrong.
+
+- I have seen browsers handle unbreakables incorrectly, particularly
+in interaction with other features. If you can't get your
+unbreakables to behave correctly, consider alternative layouts or
+alternative HTML. For example, unbreakable table data should be done
+using "table_data nowrap". I have no idea why but it works whereas
+unbreakable causes the table rows to overlap. Clearly, this is a
+browser bug.
+
+**************************************************
+FORMS
+**************************************************
+
+cgi_form action args cmd
+
+cgi_form defines a form. The form is populated by executing the
+command (last argument of cgi_form). action defines the url to
+process the form. Any other arguments are passed as attributes.
+A typical call looks like this:
+
+ cgi_form response {
+ ....
+ }
+
+Here "response" names the URL to process the form. If the URL does
+not begin with a protocol name (such as "http:"), a common root is
+prepended and ".cgi" is appended. This can be changed by redefining
+the procedure cgi_cgi.
+
+cgi_root
+
+cgi_root defines the common root used by cgi_form (see above).
+For example:
+
+ cgi_root "http://www.nist.gov/cgi-bin/cgi.tcl-examples"
+
+With one argument, cgi_root returns the new root. With no arguments,
+cgi_root returns the old root.
+
+cgi_suffix
+
+cgi_suffix defines the common suffix used by cgi_cgi and anything that
+uses it such as cgi_form. The default suffix is ".cgi".
+
+cgi_cgi
+cgi_cgi_set
+
+cgi_cgi controls exactly how cgi_form creates URLs from its action
+argument. By default, cgi_cgi takes an argument, prepends [cgi_root]
+and appends [cgi_suffix]. The suffix can be overridden by using the
+-suffix flag and an argument to be used instead of [cgi_suffix].
+
+Any additional arguments are joined together in the style required for
+a GET style request. These arguments should be preformatted using
+cgi_cgi_set to guarantee proper encoding. For example:
+
+ cgi_cgi myscript \
+ [cgi_cgi_set owner "Don"] \
+ [cgi_cgi_set color "black & white"]
+
+generates: ....?owner=Don&color=black+%26+white
+
+cgi_isindex
+
+cgi_isindex generates an <isindex> tag. Optional arguments are passed
+on as attributes. In the processing CGI script, the value of the
+isindex query is found in the "anonymous" variable.
+
+cgi_relationship rel url
+
+cgi_relationship expresses a relationship between this document and
+another. For example, the following says that the url named by
+homepage is the home document of the current document.
+
+ cgi_relationship home $homepage
+
+Optional arguments are passed on as additional attributes. Here's an
+example that references an external style sheet that is a CSS type
+(cascading style sheet).
+
+ cgi_relationship stylesheet basic.css type=text/css
+
+
+**************************************************
+INPUT
+**************************************************
+
+cgi_input
+
+CGI input means "cookies, files, and get/post data". cgi_input reads
+in all input, decodes it, and makes it available to a variety of other
+routines.
+
+For debugging, cgi_input can be given arguments to fake input. This
+allows you to run your cgi_script interactively or under a debugger
+(either Tcl or C debugger). Provide GET/POST data as the first
+argument. Provide cookie data as the second argument. The arguments
+should be encoded (see cgi_cgi_set). For example:
+
+ cgi_input "name=libes&old=foo&new1=bar&new2=hello"
+
+This is convenient because when you run into a misbehaving CGI script,
+the first thing it does is tell you the input in exactly this format.
+Simply cut and paste it into your program and you can then
+interactively debug it without using the real form or the CGI server.
+
+If cgi_input is invoked from the CGI environment, the fake inputs are
+ignored. (If you want to force fake inputs in the CGI environment,
+unset env(REQUEST_METHOD).
+
+Forms encoded as multipart/form-data are usually used to handle file
+input. Since file data usually implies a large amount of data, the
+data is saved to /tmp/CGIdbg.[pid] if debugging is enabled. This file
+can be fed back to cgi_input by providing the filename as the first
+argument of cgi_input. In addition, env(CONTENT_TYPE) must be set to
+the appropriate content type. This can found in env(CONTENT_TYPE).
+Removal of the debugging file is the responsibility of the script or
+the script programmer. (Typically, I examine the file after my CGI
+script is over and then delete them by hand.)
+
+Execs before cgi_input reads POST data should have standard input
+redirected ("< /dev/null" for instance) so that the exec'd process
+doesn't inherit the CGI script's standard input.
+
+** Form elements that generate lists
+
+Variable names should only end with "List" if they correspond to form
+elements which generate multiple values (some but not all uses of
+select, checkbox, etc). List variables will be given Tcl-style list
+values.
+
+If cgi_input encounters multiple values for variables that do not end
+with List, it will provide the values in a Tcl-style list. However,
+this leaves an ambiguity in the case of a single value that "looks"
+like a list, such as "a b". Although a form author can usually "know"
+whether a string is a list or not, it is simpler to stick to the
+convention stated earlier.
+
+Here are examples:
+
+ # pull-down menu
+ cgi_select Foo {
+ cgi_option "a"
+ cgi_option "a b"
+ }
+
+ # scrolled list, allow multiple selections
+ cgi_select FooList multiple {
+ cgi_option "a"
+ cgi_option "a b"
+ }
+
+** Getting at the input
+
+Input is made available in two ways: variables, files, and cookies.
+
+-- Variables and Cookies
+
+cgi_import_list
+
+cgi_import_list returns a list of variable names supplied as input to
+the script.
+
+cgi_cookie_list
+
+cgi_cookie_list returns a list of cookie names supplied to the script.
+
+cgi_import name
+
+cgi_import retrieves the value of the named variable and places it in
+a Tcl variable of the same name. The value is also returned as the
+return value.
+
+cgi_import_as name tclvar
+
+cgi_import_as is similar to cgi_import but the value is assigned to
+the Tcl variable named by the second argument.
+
+cgi_import_cookie name
+
+cgi_import is similar to cgi_import, however if the cgi variable does
+not exist, the value is fetched from the cookie by that name. (This
+allows the user to override a cookie if the form allows it.)
+
+cgi_import_cookie_as name tclvar
+
+cgi_import_cookie_as is similar to cgi_import_cookie but the value is
+assigned to the Tcl variable named by the second argument.
+
+cgi_cookie_get name
+
+cgi_cookie_get returns the value of the named cookie.
+
+-- Files
+
+cgi_import_file -server name
+cgi_import_file -client name
+cgi_import_file -type name
+
+cgi_import_file returns information about an uploaded file. "name" is
+the string from the original form. The Content-type is returned via
+-type. (This may be empty or even undefined in which case
+cgi_import_file should be caught.)
+
+Uploaded files are saved on the CGI server. To avoid collisions with
+other file upload instances, files are not stored in their original
+names. The name of the file as it is stored on the CGI server is
+retrieved using the "-server" flag. The original name of the file as
+it was stored on the user's host is retrieved using the "-client"
+flag.
+
+Uploaded files are the responsibility of the CGI programmer. In
+particular, if you do not delete them, they will remain until /tmp is
+cleaned up in some other way.
+
+If the user does not enter a filename, an empty file will be delivered
+with a null remote filename.
+
+cgi_file_limit files chars
+
+cgi_file_limit establishes limits on the number of files and their
+size. This is provided to prevent denial of service attacks. If the
+limit is exceeded, an error is raised from within cgi_input.
+
+Note that when the limit is exceeded, cgi_input fails immediately. So
+if you just want to check file sizes that are not security related -
+for example, you just want to accept gifs under 10K - it's better to
+accept the gifs and then check the size manually (i.e., [file size
+...]. That way, cgi_input will completely read all the variables and
+you can give more appropriate diagnostics.
+
+The default limit is 10 100MB files. If you were to set this
+yourself, it would look this way:
+
+cgi_file_limit 10 100000000
+
+
+-- File example
+
+The following code echos the contents of a file that was
+uploaded using the variable "file":
+
+ cgi_input
+ cgi_body {
+ set server [cgi_import_filename -server $v]
+ set client [cgi_import_filename -client $v]
+ if [string length $client] {
+ h4 "Uploaded: $client, contents:"
+ cgi_preformatted {puts [exec cat $server]}
+ }
+ file delete $server
+ }
+
+The present implementation supports binary upload if you are using Tcl
+8.1 (or later) or if you are using the Expect extension. If you are
+using a version of Tcl earlier than 8.1 with Expect but want to
+suppress binary loading, create the global variable
+_cgi(no_binary_upload). (The reason you might want to suppress binary
+loading is that it is noticably slower.)
+
+**************************************************
+EXPORT
+**************************************************
+
+Form elements automatically export their values. See FORM ELEMENTS
+for more information.
+
+cgi_export name=value
+
+cgi_export makes the named variable available with the given value.
+The "=value" is optional. If not present, the value of the Tcl
+variable by the same name is used.
+
+cgi_export is implemented with variables of type=hidden.
+
+cgi_export is implemented as an all-or-nothing operation. In
+particular, no HTML is emitted if the variable does not exist. That
+means it is not necessary to test for existence in situations where
+you would like to export a variable IF it exists. Rather, it is
+sufficient to embed cgi_export within a catch. For example, the
+following generates nothing if xyz doesn't exist and it generates the
+appropriate HTML if xyz does exist.
+
+ catch {cgi_export xyz}
+
+** Cookies
+
+cgi_export_cookie name
+
+cgi_export_cookie is similar to cgi_export except that the value is
+made available as a cookie. Additional arguments are handled as with
+cgi_cookie_set (see below).
+
+cgi_cookie_set name=val
+
+cgi_cookie_set sets the named cookie. All optional arguments are
+handled specially. All arguments are encoded appropriately. The
+expires keyword is handled specially to simplify common cases. In
+particular, the values "now" and "never" produce appropriate GMT
+values.
+
+Here are some example of cgi_cookie_set:
+
+ cgi_cookie_set user=don domain=nist.gov expires=never
+ cgi_cookie_set user=don domain=nist.gov expires=now
+ cgi_cookie_set user=don domain=nist.gov expires=...actual date...
+
+Note that cookie setting must be done during http head generation.
+
+**************************************************
+URL/IMG DICTIONARY SUPPORT
+**************************************************
+
+cgi_link tag
+cgi_link tag display url
+
+cgi_link provides a convenient mechanism for maintaining and
+referencing from a set of URLs.
+
+cgi_link returns the string <A>...</A> corresponding to the given tag.
+A tag is defined by calling cgi_link with the tag, the clickable text
+that the use should see, and the url.
+
+For example, suppose you want to produce the following (where _xyz_
+indicates xyz is a hyperlink):
+
+ I am married to _Don Libes_ who works in the _Manufacturing
+ Collaboration Technologies Group_ at _NIST_.
+
+Using cgi_link with appropriate link definitions, the scripting to
+produce this is:
+
+ p "I am married to [link Libes] who works in the [link MCTG]
+ at [link NIST]."
+
+This expands to:
+
+ I am married to <A HREF="http://elib.cme.nist.gov/msid/staff
+ /libes/ libes.don.html">Don Libes</A> who works in the <A
+ HREF="http:// elib.cme.nist.gov/msid/groups/mctg.htm">
+ Manufacturing Collaboration Technologies Group</A> at <A
+ HREF="http:// www.nist.gov">NIST</A>.
+
+The links themselves are defined thusly:
+
+ link Libes "Don Libes" http://www.cme.nist.gov/msid/staff/libes
+ link MCTG "$MCT Group" http://www.cme.nist.gov/msid/mctg
+ link NIST "NIST" http://www.nist.gov
+
+Now if my home page ever changes, rather than updating every
+occurrence, I just have to edit the one definition.
+
+Tcl variables can further simplify updates. For instance, URLs for
+Libes and MCTG are in a common directory. It makes sense to store
+that in a single variable. Rewritten this appears:
+
+set MSID http://www.cme.nist.gov/msid
+
+ link Libes "Don Libes" $MSID/staff/libes
+ link MCTG "$MCT Group" $MSID/mctg
+ link NIST "NIST" http://www.nist.gov
+
+Then if the MSID directory ever moves, only one line need be updated.
+This may seem like no big deal here, but if you have many links and
+many uses of them, this pays off handsomely.
+
+Optional attributes can be provided as additional arguments (see IMG
+example below).
+
+An existing link can be given a different "display" temporarily by
+calling cgi_link with the different display and omitting the url.
+
+cgi_imglink
+
+imglink works similar to cgi_link (see that documentation for more
+info) except that no display argument is used and the second argument
+is assumed to be the image source. Example:
+
+ imglink taj tajmahal.gif
+
+Other attributes can be provided as additional arguments.
+
+ imglink taj tajmahal.gif "alt=The Taj Mahal"
+
+cgi_url display href args
+
+By using cgi_url, URLs can be generated immediately (without using
+cgi_link first). This is convenient when you need a URL that will
+only appear once - so that there is no point in storing it in a
+dictionary. For example:
+
+ cgi_li "[cgi_url "Plume" http://pastime.anu.edu.au/Plume]
+ is a Tcl-based WWW browser written by Steve Ball,
+ Australian National University. Among its interesting
+ features is the ability to execute Tcl applets and the
+ ability to dynamically extend the browser at runtime."
+
+cgi_img href args
+
+cgi_img returns a formatted <img> tag. It is useful for one-time tags.
+Tags that are used multiple times should use cgi_imglink. Example:
+
+ cgi_img foo.gif "alt=Foo's Bar & Grill"
+
+cgi_anchor_name name
+
+cgi_anchor_name returns an anchor that can be used in an HTML body
+that it can be linked to using the #name syntax. For example, to make
+a heading that you want to be able to link to:
+
+ h2 "[cgi_anchor_name future]Future Improvements"
+
+Then to reference the "future" tag:
+
+ p "Look for [cgi_url "improvements" #future] in the future."
+
+cgi_base args
+
+cgi_base defines a base or window target for urls.
+
+**************************************************
+QUOTING
+**************************************************
+
+cgi_unquote_input string
+
+cgi_unquote_input undoes "url-encoding" and returns the result. This
+is normally applied automatically to input sources including URLs and
+cookies. So you shouldn't have to call this manually.
+
+cgi_quote_html string
+
+cgi_quote_html returns the string but with any html-special characters
+escaped. For example, "<" is replaced by "\&lt". This is useful for
+displaying a literal "<" in the browser.
+
+cgi_dquote_html string
+
+cgi_dquote_html does the same thing as cgi_quote_html but also adds on
+double quotes. cgi_quote_html is called automatically for implicit
+value attributes.
+
+cgi_quote_url string
+
+cgi_quote_url quotes strings appropriately to appear in a url, cookie,
+etc. This is useful if you want to publish a url by hand (and must do
+the conversion manually that the client normally does for you).
+
+If you are generating cgi-style URLs for forms, use cgi_cgi_set.
+
+cgi_preformatted cmd
+
+cgi_preformatted evaluates its last argument to produce fixed-width
+preformatted output. Optional arguments are passed as attributes to
+the tags.
+
+cgi_preformatted allows a subset of tags to be interpreted by the
+browser. For example, the <a> tag is interpreted but font change tags
+are not. To prevent all interpretation, use cgi_quote_html. For
+example, the following prints a file that might contain HTML but
+without any risk to throwing off formatting.
+
+ cgi_preformatted {
+ puts [cgi_quote_html [read $fid]]
+ }
+
+**************************************************
+LIST SUPPORT
+**************************************************
+
+** List elements
+
+cgi_li string
+
+cgi_li prints its string as a list element. Optional arguments are
+passed through as attributes. cgi_li does not have to appear in a
+list container, but it can.
+
+cgi_term text
+cgi_term_definition text
+
+cgi_term and cgi_term_definition are usually paired up (although they
+need not be) to creates terms and defintions. They do not have to
+appear in a list container, but usually appear in a cgi_definition_list.
+
+** List containers
+
+cgi_number_list cmd
+cgi_bullet_list cmd
+
+cgi_number_list and cgi_bullet_list take their cmd argument and
+evaluate it in a list container context. (I don't know about you but
+I could never remember <ol>, <ul>, and all the other ones. This names
+seem much easier to remember.)
+
+cgi_li is a typical command to call inside of a list container, but
+you can use regular paragraphs (or anything else) as well.
+
+cgi_definition_list
+
+cgi_definition_list is the usual list container for cgi_term and
+cgi_term_definition. It may contain other things as well.
+
+cgi_menu_list
+cgi_directory_list
+
+cgi_menu_list and cgi_directory are more list containers with the
+obvious semantics. Previous remarks about other list containers
+apply.
+
+**************************************************
+TABLE
+**************************************************
+
+cgi_table cmd
+
+cgi_table produces <table></table> tags with the content filled by
+evaluation of cmd. Optional arguments are passed on as attributes.
+
+cgi_caption cmd
+
+cgi_caption produces <caption></caption> tags with the content filled
+by evaluation of cmd. Optional arguments are passed on as attributes.
+
+cgi_table_row cmd
+cgi_table_head cmd
+cgi_table_data cmd
+
+These functions all produce the appropriate tags with the content
+filled by evaluation of cmd. Optional arguments are passed on as
+attributes.
+
+cgi_tr table_data
+cgi_td table_data
+cgi_th table_data
+
+cgi_tr, cgi_td, and cgi_th are shortcuts for relatively simple rows.
+
+cgi_td outputs a table element. Unlike cgi_table_data, the argument
+is not evalled. This allows more terse specification of simple rows.
+The following example produces a table with three elements, the last
+of which is prevented from wrapping:
+
+ table_row {td Don;td Steve;td nowrap "Really Long Name"}
+
+As the example suggests, optional arguments are passed on as
+data-specific attributes.
+
+cgi_th is identical to cgi_td except that it produces table heading
+elements.
+
+cgi_tr outputs a row of elements without having to call cgi_td or
+cgi_table_data. As with td, eval is not called. Data-specific
+attributes cannot be provided. All the elements are passed as a
+single argument. For example:
+
+ tr {Don Steve {Really Long Name}}
+or
+ tr [list Don Steve $reallylongname]
+
+Optional arguments are passed on as row-specific attributes.
+
+**************************************************
+BUTTON
+**************************************************
+
+cgi_submit_button name=value
+cgi_radio_button name=value
+cgi_image_button name=value
+
+These procedure create buttons. The first argument indicates the
+variable name and value. (See notes on "Name-value" commands earlier
+to understand behavior of omitted names/values.) Unless otherwise
+mentioned below, additional arguments are passed on as attributes.
+
+ cgi_submit_button "=Submit Form"
+ cgi_submit_button "Action=Pay Raise"
+
+ cgi_radio_button "version=1"
+ cgi_radio_button "version=2" checked=1
+
+ cgi_image_button "=http://www.cme.nist.gov/images/title.gif"
+ cgi_image_button "Map=http://www.cme.nist.gov/images/msid3.gif"
+
+Groups of radio buttons must share the same variable name. To address
+the obvious question: No, there is no single command to produce a
+group of radio buttons because you might very well want to do
+arbitrarily-complex calculations in between them. And with a
+long-enough line of buttons, the obvious behavior (laying them all out
+in a line like CGI.pm does) makes it hard to tell at a glance if the
+buttons associate with the label to the left or the right of them.
+Anyway, you'll almost certainly want to write another procedure to
+call cgi_radio_button and that can control the variable name.
+
+The radio button argument "checked_if_equal=xxx" indicates that the
+button should be shown selected if its associated value is xxx. This
+is handy if you are creating radio buttons by iterating over a list.
+
+The radio button "checked=value" indicates that the button should be
+shown selected if the value is a boolean of value true.
+
+All other arguments are passed on as attributes.
+
+cgi_file_button name
+
+cgi_file_button provides a filename entry box. When the form is
+submitted, the file is "uploaded" (sent from the client to the
+server). The argument enctype=multipart/form-data must be given to
+the cgi_form command when using cgi_file_button.
+
+After uploading, the file is the responsibility of the CGI programmer.
+In particular, if you do not delete it, it will remain until /tmp is
+cleaned up in some other way.
+
+For example, to upload a single file, the form might look like this:
+
+ cgi_form upload height=3 enctype=multipart/form-data {
+ cgi_file_button file
+ cgi_submit_button =Upload
+ }
+
+Uploaded files are automatically made available to the CGI script
+using cgi_import_filename. (See elsewhere for more information.)
+
+cgi_reset_button value
+
+cgi_reset_button creates a a reset button. An argument overrides the
+default label. For example:
+
+ cgi_reset_button
+ cgi_reset_button "Not the Default Reset Button"
+
+cgi_map name cmd
+cgi_area args
+
+These procedures are used to specify client-side image maps. The
+first argument of cgi_map is the map name. The last argument is
+evaluated to fill the contents of <map></map> tags. cgi_area's
+arguments are embedded as arguments in an <area> tag.
+
+Warning: These two commands will likely be redefined as I get more
+familiar with how they are typically used.
+
+**************************************************
+CHECKBOX
+**************************************************
+
+cgi_checkbox name=value
+
+cgi_checkbox is similar to cgi_radio_button (see above) except that
+multiple values can be checked at the same time. As explained
+earlier, the variable name must end with "List" in order for it to
+group all the values as a list in the resulting CGI script.
+
+The argument "checked_if_equal=xxx" which indicates that the current
+name should be shown selected if its associated value is xxx. This is
+handy if you are creating checkboxes by iterating over a list.
+
+The argument "checked=value" indicates that the checkbox should be
+shown selected if the value is a boolean of value true.
+
+Other arguments are passed on as attributes.
+
+
+**************************************************
+TEXTENTRY AND TEXTAREA
+**************************************************
+
+cgi_text name=value
+
+cgi_text provides a one-line text entry box. It works similarly to
+other form elements. (Read "Name-value commands" elsewhere.)
+Additional arguments are passed on as attributes. Examples:
+
+ cgi_text Foo
+ cgi_text Foo=
+ cgi_text Foo=value2
+ cgi_text Foo=value2 size=5
+ cgi_text Foo=value2 size=5 maxlength=10
+ cgi_text Foo=value2 size=10 maxlength=5
+ cgi_text Foo=value2 maxlength=5
+
+cgi_textarea name=value
+
+cgi_textarea provides a multiline text entry box. It works similarly
+to other form elements. (Read "Name-value commands" elsewhere.)
+Additional arguments are passed on as attributes.
+
+ set value "A really long line so that we can compare the\
+ effect of wrap options."
+
+ cgi_textarea Foo
+ cgi_textarea Foo=
+ cgi_textarea Foo=$value
+ cgi_textarea Foo=$value rows=3
+ cgi_textarea Foo=$value rows=3 cols=7
+ cgi_textarea Foo=$value rows=3 cols=7 wrap=virtual
+
+**************************************************
+SELECT
+**************************************************
+
+cgi_select name cmd
+
+cgi_select can be used to produce pull-down menus and scrolled lists.
+(And depending upon the future of HTML, I may provide better-named
+commands to indicate this.) Its behavior is controlled by additional
+arguments which are simply the appropriate attributes.
+
+cgi_select evaluates cmd which typically contains multiple calls to
+cgi_option.
+
+cgi_option string
+
+cgi_option adds options to cgi_select. By default, the string is
+displayed and sent back as the value of the cgi_select variable. The
+value can be overridden by an explicit "value=" argument. Additional
+options are passed on as attributes, except for "selected_if_equal".
+
+"selected_if_equal=xxx" indicates that the current option should be
+shown selected if the value is equal to xxx. This is useful if you
+are generating cgi_options in a loop rather than manually.
+
+Here are examples:
+
+ # pull-down menu
+ cgi_select Foo {
+ cgi_option one selected
+ cgi_option two
+ cgi_option many value=hello
+ }
+
+ # scrolled list, allow multiple selections, show all elements
+ cgi_select FooList multiple {
+ cgi_option one selected
+ cgi_option two selected
+ cgi_option many
+ }
+
+ # scrolled list, allow multiple selections, show 2 elements max
+ cgi_select FooList multiple size=2 {
+ cgi_option one selected
+ cgi_option two selected
+ cgi_option many
+ }
+
+ # choose "selected" dynamically
+ # example: list all Tcl command and select "exit" automatically
+ cgi_select FooList multiple size=5 {
+ foreach o [info comm] {
+ cgi_option $o selected_if_equal=exit
+ }
+ }
+
+Note: If both value= and selected_if_equal= appear, the test for
+selection is made against the last value string (implicit or explicit)
+that appears before the selected_if_equal argument. In other words,
+if you want selected_if_equal= to be tested against the explicit
+value= argument, put the selected_if_equal= *after* the value=.
+
+**************************************************
+APPLET
+**************************************************
+
+cgi_applet parameters cmd
+
+cgi_applet produces <applet></applet> tags such as for Java.
+
+cgi_param name=value
+
+cgi_param produces <param> tags for passing parameters to applets.
+
+For example:
+
+ cgi_applet "codebase=../" "code=some.class" width=640 height=480 {
+ cgi_param parm=value
+ }
+
+**************************************************
+PLUG-IN
+**************************************************
+
+cgi_embed src widthxheight
+
+cgi_embed creates an <embed> tag. The first argument is the source.
+The second argument is the width and height in the style "WxH".
+Optional arguments are passed on to the tag. For example:
+
+ cgi_embed myavi.avi 320x200 autostart=true
+
+produces:
+
+ <embed src="myavi.avi" width="320" height="200" autostart=true>
+
+Notice the autostart value is unquoted because autostart is not
+specifically defined by the spec. The argument "-quote" causes all
+remaining attributes to be url encoded and quoted. For example:
+
+ cgi_embed myavi.avi 320x200 a=b -quote c=d e=f
+
+produces:
+
+ <embed src="myavi.avi" width="320" height="200" a=b c="d" e="f">
+
+**************************************************
+MISC
+**************************************************
+
+cgi_hr
+
+cgi_hr produces horizontal rules. Optional arguments are passed on as
+attributes.
+
+**************************************************
+COMMENTS
+**************************************************
+
+cgi_comment stuff
+
+cgi_comment can comment out anything including blocks of code.
+
+cgi_html_comment stuff
+
+cgi_html_comment comments out things in such a way that the comment
+appears in the final html itself.
+
+**************************************************
+OUTPUT
+**************************************************
+
+cgi_put string
+
+cgi_put prints the string with no new terminating newline. This is
+simply a shorthand for puts -nonewline.
+
+cgi_puts
+
+Many routines in this library send output to the standard output by
+default. This is convenient for CGI scripts. However, if you want to
+generate multiple files, it is useful to be able to redirect output
+dynamically. Output can be redirected by redefining cgi_puts. The
+default definition of cgi_puts is:
+
+ proc cgi_puts {args} {
+ eval puts $args
+ }
+
+cgi_puts must allow for an optional -nonewline argument. For example,
+here is a definition that writes to a file identified by the global
+"fd".
+
+ proc cgi_puts {args} {
+ global fd
+
+ puts -nonewline $fd [lindex $args end]
+ if {[llength $args] > 1} {
+ puts $fd ""
+ }
+ }
+
+cgi_buffer cmd
+
+cgi_buffer evaluates its argument in such a way that output (through
+explicit or implicit calls to cgi_puts) is not produced. Instead, the
+output is returned.
+
+For example, the following cmd generates a link with the hyperlinked
+portion being a header and two paragraphs.
+
+ link tag [cgi_buffer {
+ h3 "Level 3 header"
+ p "New paragraph"
+ p "Another paragraph"
+ }] $URL
+
+cgi_buffer can be called recursively.
+
+cgi_buffer_nl string
+
+By default, cgi_buffer generates newlines at the end of every line in
+the style of puts. It is occasionally useful to be able to disable
+this - for example, when calling it inside cgi_preformatted. The
+newline definition can be changed via cgi_buffer_nl. (There is no
+point to redefining cgi_puts since cgi_buffer doesn't use it.) A null
+argument suppresses any newline. For example:
+
+ cgi_buffer_nl ""
+
+cgi_buffer_nl returns the previous definition.
+
+**************************************************
+MAIL
+**************************************************
+
+Rudimentary email support is provided, in part, because it is useful
+for trapping errors - if debugging is disabled (i.e., during actual
+use) and an error is encountered, errors are emailed to the service
+admin.
+
+The current implementation only permits one email transaction at a
+time.
+
+cgi_mail_addr addr
+
+cgi_mail_addr defines the email address that mail comes from. Your
+email system must allow this, of course. (If your system doesn't
+allow it, the request is usually ignored.)
+
+cgi_mail_start addr
+
+cgi_mail_start creates a mail message to be delivered the given addr.
+cgi_mail_add should be used to provide a subject and body.
+
+cgi_mail_add string
+
+cgi_mail_add adds strings to the current mail message. No argument
+causes a blank line to be added.
+
+cgi_mail_end
+
+cgi_mail_end queues the the current mail message for delivery.
+
+ cgi_mail_start libes@nist.gov
+ cgi_mail_add "Subject: [cgi_name] request succeeded
+ cgi_mail_add
+ cgi_mail_add "Your request has been processed."
+ cgi_mail_add "Thanks for using [cgi_name].
+ cgi_mail_end
+
+cgi_mail_relay host
+
+cgi_mail_relay identifies a host to be used for mail relay services.
+(This is currently only used when sendmail support is not available.)
+If a relay is not defined, email is sent directly to the recipient.
+
+**************************************************
+INITIALIZATION
+**************************************************
+
+cgi_init
+
+The package initializes itself upon initial loading, however it can be
+explicitly initialized by calling cgi_init. This is useful in
+environments such as tclhttpd.
diff --git a/web/src/cgi.tcl-1.10/example/README b/web/src/cgi.tcl-1.10/example/README
new file mode 100644
index 00000000..6bbbb80c
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/README
@@ -0,0 +1,77 @@
+This file is cgi.tcl/README. It contains brief descriptions of the
+examples in this directory. You are welcome to send me additional
+scripts.
+
+--------------------
+Instructions on running scripts
+--------------------
+
+The "examples.cgi" script provides a page of clickable links to all
+the other example scripts. "frame.cgi" is a framed version - it's not
+particularly good looking but it does work and - anyway - I needed a
+frame example!
+
+There are three ways of running these scripts:
+
+1) Point your browser at http://expect.nist.gov/cgi.tcl
+
+2) Run them by hand, such as by typing "tclsh scriptname" at the
+command line. This is appropriate if you just want to study the raw
+HTML output or see how fast they run.
+
+3) Install them in your own web and run them through your browser.
+(If you're on a UNIX host, the "make examples" target in the Makefile
+fixes up the #! line of each examples and installs them in a public
+demo directory.) You'll want to edit the example.tcl file to redefine
+cgi_root appropriately.
+
+If you run the examples locally (options 2 and 3):
+
+ - In order to get the submit buttons to take you back to your
+ site (instead of on to my site!), check the cgi_root call in
+ example.tcl to point back to your site.
+
+ - Some of these examples may do things that are not portable
+ and therefore may not run on all systems. For example, some
+ of them call "exec". The "unimail" script assumes the
+ existence of sendmail through cgi.tcl's built-in mail
+ support. See the ../install.win file for more info.
+
+--------------------
+Source files in this directory
+--------------------
+
+ cookie.cgi - demonstrates cookies
+
+ display.cgi - display a CGI script
+
+ error.cgi - demonstrates error handing
+
+ examples.cgi - Provides a page with clickable links to all
+ these examples.
+
+ form-tour.cgi - demonstrates most form elements (Note that these
+ don't do anything - they're just for looks.)
+
+ format-tour.cgi - demonstrates many formats, unrelated to forms
+
+ frame.cgi - a friend's home page that demonstrates frames
+
+ kill.cgi - kill runaway CGI processes
+
+ parray.cgi - displays an array (or the environment by default)
+
+ passwd-form.cgi - creates a form for changing a password
+ passwd.cgi - backend to passwd-form where password is actually changed
+ Note that this script is an Expect script.
+ passwd.tcl - common definitions for passwd*.cgi scripts
+
+ upload.cgi - file upload
+
+ vclock.pl - Lincoln Stein's virtual clock from CGI.pm paper
+ vclock.cgi - Lincoln Stein's virtual clock (but in Tcl)
+
+ visitor.cgi - implements a visitor counter
+ visitor.cnt - file containing the count
+
+ example.tcl - common definitions for most of the examples
diff --git a/web/src/cgi.tcl-1.10/example/cookie.cgi b/web/src/cgi.tcl-1.10/example/cookie.cgi
new file mode 100755
index 00000000..ea0709bc
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/cookie.cgi
@@ -0,0 +1,45 @@
+#!/depot/path/tclsh
+
+# This CGI script shows how to create a cookie.
+
+package require cgi
+
+cgi_eval {
+ source example.tcl
+
+ cgi_input
+
+ cgi_http_head {
+ cgi_content_type text/html
+ if {0==[catch {cgi_import Name}]} {
+ cgi_export_cookie Name ;# expires=never
+ # For a persistent cookie, uncomment the "expires=never"
+ } else {
+ catch {cgi_import_cookie Name}
+ }
+ }
+ cgi_head {
+ cgi_title "Cookie Form Example"
+ }
+ cgi_body {
+ p "This form finds a value from a previous submission of \
+ the form either directly or through a cookie."
+ set Name ""
+
+ if {0==[catch {cgi_import Name}]} {
+ p "The value was found from the form submission and the cookie
+ has now been set. Bookmark this form, surf somewhere else
+ and then return to this page to get the value via the cookie."
+ } elseif {0==[catch {cgi_import_cookie Name}]} {
+ p "The value was found from the cookie."
+ } else {
+ p "No cookie is currently set. To set a cookie, enter a value
+ and press return."
+ }
+
+ cgi_form cookie {
+ puts "Value: "
+ cgi_text Name
+ }
+ }
+}
diff --git a/web/src/cgi.tcl-1.10/example/creditcard.cgi b/web/src/cgi.tcl-1.10/example/creditcard.cgi
new file mode 100755
index 00000000..a205b452
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/creditcard.cgi
@@ -0,0 +1,137 @@
+#!/depot/path/tclsh
+
+package require cgi
+
+cgi_eval {
+ source example.tcl
+
+ set cardtypes {
+ "American Express"
+ "Carte Blanche"
+ "Diners Card"
+ "Discover"
+ "Enroute"
+ "JCB"
+ "Mastercard"
+ "Novus"
+ "Visa"
+ }
+
+ # My own version of the LUHN check
+ proc LUHNFormula {cardnumber} {
+ if {0==[regexp "(.*)(.)$" $cardnumber dummy cardnumber check]} {
+ user_error "No card number entered"
+ }
+ set evenodd [expr [string length $cardnumber] % 2]
+
+ set sum 0
+ foreach digit [split $cardnumber ""] {
+ incr sum $digit
+ if {$evenodd} {
+ incr sum [lindex {0 1 2 3 4 -4 -3 -2 -1 0} $digit]
+ }
+ set evenodd [expr !$evenodd]
+ }
+ set computed [expr {(10 - ($sum % 10)) % 10}]
+ if {$computed != $check} {
+ user_error "Invalid card number. (Failed LUHN test - try changing last digit to $computed.)"
+ }
+ }
+
+ # generate digit patterns of length n
+ proc d {n} {
+ for {set i 0} {$i < $n} {incr i} {
+ append buf {[0-9]}
+ }
+ return $buf
+ }
+
+ cgi_input
+
+ cgi_title "Check Credit Card"
+
+ cgi_body {
+ if {0 == [catch {cgi_import cardtype}]} {
+ if {[catch {cgi_import cardnumber}]} {
+ user_error "No card number entered"
+ }
+ # Save original version for clearer diagnostics
+ set originalcardnumber [cgi_quote_html $cardnumber]
+
+ if {[catch {cgi_import expiration}]} {
+ user_error "You must enter an expiration."
+ }
+ if {-1 == [lsearch $cardtypes $cardtype]} {
+ user_error "Unknown card type: $cardtype"
+ }
+
+ # Remove any spaces or dashes in card number
+ regsub -all "\[- ]" $cardnumber "" cardnumber
+
+ # Make sure that only digits are left
+ if {[regexp "\[^0-9]" $cardnumber invalid]} {
+ user_error "Invalid character ([cgi_quote_html $invalid]) in credit card number: $originalcardnumber"
+ }
+
+ if {$cardtype != "Enroute"} {
+ LUHNFormula $cardnumber
+ }
+
+ # Verify correct length and prefix for each card type
+ switch $cardtype {
+ Visa {
+ regexp "^4[d 12]([d 3])?$" $cardnumber match
+ } Mastercard {
+ regexp "^5\[1-5][d 14]$" $cardnumber match
+ } "American Express" {
+ regexp "^3\[47][d 13]$" $cardnumber match
+ } "Diners Club" {
+ regexp "^3(0\[0-5]|\[68][d 1])[d 11]$" $cardnumber match
+ } "Carte Blanche" {
+ regexp "^3(0\[0-5]|\[68][d 1])[d 11]$" $cardnumber match
+ } Discover {
+ regexp "^6011[d 12]$" $cardnumber match
+ } Enroute {
+ regexp "^(2014|2149)[d 11]$" $cardnumber match
+ } JCB {
+ regexp "^(2131|1800)[d 11]$" $cardnumber match
+ regexp "^3(088|096|112|158|337|528)[d 12]$" $cardnumber match
+ } Novus {
+ if {[string length $cardnumber] == 16} {
+ set match 1
+ }
+ }
+ }
+
+ if 0==[info exists match] {
+ user_error "Invalid card number: $originalcardnumber"
+ }
+ h3 "Your card appears to be valid. Thanks!"
+ return
+ }
+
+ cgi_form creditcard {
+ h3 "Select a card type, enter the card number and expiration."
+ puts "Card type: "
+ cgi_select cardtype {
+ foreach t $cardtypes {
+ cgi_option $t
+ }
+ }
+ puts "[nl]Card number: "
+ cgi_text cardnumber=
+ puts "(blanks and dashes are ignored)"
+
+ puts "[nl]Expiration: "
+ cgi_text expiration=
+
+ br
+ submit_button "=Confirm purchase"
+ reset_button
+ h5 "This script will perform all of the known syntactic
+ checks for each card type but will not actually contact
+ a credit bureau. The expiration field is not presently
+ checked at all."
+ }
+ }
+}
diff --git a/web/src/cgi.tcl-1.10/example/display-in-frame.cgi b/web/src/cgi.tcl-1.10/example/display-in-frame.cgi
new file mode 100755
index 00000000..6366957e
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/display-in-frame.cgi
@@ -0,0 +1,31 @@
+#!/depot/path/tclsh
+
+# This is a CGI script that displays the results of other CGI scripts
+# in a separate frame. It is only used for a few rare examples such
+# as image.cgi
+
+package require cgi
+
+cgi_eval {
+ source example.tcl
+
+ cgi_input
+
+ cgi_head {
+ set scriptname image.cgi
+ catch {cgi_import scriptname}
+ set scriptname [file tail $scriptname]
+ cgi_title $scriptname
+ }
+ cgi_frameset rows=50%,50% {
+ cgi_frame =$scriptname?header=1
+ cgi_frame =$scriptname
+ }
+ cgi_noframes {
+ cgi_h1 "uh oh"
+
+ p "This document is designed to be viewed by a Frames-capable
+ browser. If you see this message your browser is not
+ Frames-capable."
+ }
+}
diff --git a/web/src/cgi.tcl-1.10/example/display.cgi b/web/src/cgi.tcl-1.10/example/display.cgi
new file mode 100755
index 00000000..efadbe05
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/display.cgi
@@ -0,0 +1,44 @@
+#!/depot/path/tclsh
+
+# This is a CGI script that displays another CGI script
+# and massages "source" commands into hyperlinks
+
+package require cgi
+
+cgi_eval {
+ source example.tcl
+
+ cgi_input
+
+ cgi_head {
+ set scriptname [info script]; # display self by default!
+ catch {cgi_import scriptname}
+
+ # strip tildes to gracefully handle experimenters
+ set scriptname [string trimleft $scriptname ~]
+
+ set scriptname [file tail $scriptname]
+ cgi_title "Source for $scriptname"
+ }
+ cgi_body {
+ # gracefully handle hackers trying to opening directories
+ switch -- $scriptname . - .. - "" {
+ h3 "No such file: $scriptname"
+ return
+ }
+ if {[catch {set fid [open $scriptname]}]} {
+ h3 "No such file: $scriptname"
+ return
+ }
+ cgi_preformatted {
+ while {-1 != [gets $fid buf]} {
+ if {[regexp "^(\[ \t]*)source (.*)" $buf ignore space filename]} {
+ puts "[set space]source [cgi_url $filename [cgi_cgi display scriptname=$filename]]"
+ } else {
+ puts [cgi_quote_html $buf]
+ }
+ }
+ }
+ }
+}
+
diff --git a/web/src/cgi.tcl-1.10/example/download.cgi b/web/src/cgi.tcl-1.10/example/download.cgi
new file mode 100755
index 00000000..c82cb3f5
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/download.cgi
@@ -0,0 +1,36 @@
+#!/depot/path/tclsh
+
+package require cgi
+
+set msg "Very funny, Scotty. Now beam down my clothes."
+set filename "scotty.msg"
+
+cgi_eval {
+ source example.tcl
+
+ cgi_input
+
+ if {[catch {cgi_import style}]} {
+ cgi_title "download example"
+ body {
+ cgi_suffix ""
+ form download.cgi/$filename {
+ puts "This example demonstrates how to force files to be
+ downloaded into a separate file via the popup file browser."
+ br
+ puts "Download data"
+ submit_button "style=in window"
+ submit_button "style=in file using popup file browser"
+ }
+ }
+ } else {
+ if {[regexp "in window" $style]} {
+ title "Display data in browser window"
+ } else {
+ cgi_http_head {
+ content_type application/x-download
+ }
+ }
+ puts $msg
+ }
+}
diff --git a/web/src/cgi.tcl-1.10/example/error.cgi b/web/src/cgi.tcl-1.10/example/error.cgi
new file mode 100755
index 00000000..0b97c99a
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/error.cgi
@@ -0,0 +1,31 @@
+#!/depot/path/tclsh
+
+# This is a CGI script that demonstrates error processing.
+
+package require cgi
+
+cgi_eval {
+ source example.tcl
+
+ cgi_input
+
+ cgi_title "This CGI script contains an intentional error."
+
+ cgi_body {
+ p "The page that you are now reading is being generated by a
+ CGI script that contains an intentional error."
+
+ cgi_debug -on
+
+ p "Debugging is enabled, so the error message will be shown in
+the browser window (below). Disable this by commenting out the
+cgi_debug command and reloading this script."
+
+ cgi_number_list {
+ cgi_li "List item 1"
+ cgi_li "List item 2"
+ cgi_lix "List item 3 - intentionally misspelled"
+ cgi_li "List item 4"
+ }
+ }
+}
diff --git a/web/src/cgi.tcl-1.10/example/evaljs.cgi b/web/src/cgi.tcl-1.10/example/evaljs.cgi
new file mode 100755
index 00000000..ea732e9d
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/evaljs.cgi
@@ -0,0 +1,36 @@
+#!/depot/path/tclsh
+
+# This CGI script uses JavaScript to evaluate an expression.
+
+package require cgi
+
+cgi_eval {
+ source example.tcl
+
+ cgi_head {
+ title "Using JavaScript to evaluate an expression"
+
+ javascript {
+ puts {
+ function compute(f) {
+ f.result.value = eval(f.expr.value)
+ }
+ }
+ }
+ noscript {
+ puts "Sorry - your browser doesn't understand JavaScript."
+ }
+ }
+
+ cgi_body {
+ cgi_form dummy {
+ cgi_unbreakable {
+ cgi_button "Evaluate" onClick=compute(this.form)
+ cgi_text expr=Math.sqrt(2)*10000
+ puts "="
+ cgi_text result=
+ }
+ p "Feel free to enter and evaluate your own JavaScript expression."
+ }
+ }
+}
diff --git a/web/src/cgi.tcl-1.10/example/example.tcl b/web/src/cgi.tcl-1.10/example/example.tcl
new file mode 100644
index 00000000..c1246621
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/example.tcl
@@ -0,0 +1,82 @@
+# common definitions for all examples
+
+# cgi_debug -on
+
+set NIST_HOST http://www.nist.gov
+set MSID_HOST http://www.nist.gov
+set EXPECT_HOST http://expect.nist.gov
+set EXPECT_ART $EXPECT_HOST/art
+set MSID_STAFF $MSID_HOST/msidstaff
+set CGITCL $EXPECT_HOST/cgi.tcl
+set DATADIR data
+
+set domainname "unknown"
+catch {set domainname [exec domainname]}
+
+# prevent everyone in the world from sending specious mail to Don!
+if {($domainname == "cme.nist.gov") || ([info hostname] == "ats.nist.gov")} {
+ cgi_admin_mail_addr libes@nist.gov
+}
+
+set TOP target=_top
+cgi_link NIST "NIST" $NIST_HOST $TOP
+cgi_link Don "Don Libes" $MSID_STAFF/libes $TOP
+cgi_link admin "your system administrator" mailto:[cgi_admin_mail_addr]
+cgi_link CGITCL "cgi.tcl homepage" $CGITCL $TOP
+cgi_link examples "list of examples" [cgi_cgi examples] $TOP
+cgi_link realapps "real applications" $CGITCL/realapps.html $TOP
+cgi_link Expect "Expect" $EXPECT_HOST $TOP
+cgi_link Oratcl "Oratcl" http://www.nyx.net/~tpoindex/tcl.html#Oratcl $TOP
+
+cgi_imglink logo $EXPECT_ART/cgitcl-powered-feather.gif align=right "alt=powered-by-cgi.tcl logo"
+cgi_link logolink [cgi_imglink logo] $CGITCL $TOP
+
+# Allow for both my development and production environment. And people
+# who copy this to their own server and fail to change cgi_root will get
+# my production environment!
+if {$domainname == "cme.nist.gov"} {
+ cgi_root "http://www-i.cme.nist.gov/cgi-bin/cgi-tcl-examples"
+} else {
+ cgi_root "http://ats.nist.gov/cgi-bin/cgi.tcl"
+}
+
+proc scriptlink {} {
+ if {0==[catch {cgi_import framed}]} {
+ set target "target=script"
+ } else {
+ set target ""
+ }
+
+ cgi_url "the Tcl script" [cgi_cgi display scriptname=[info script]] $target
+}
+
+proc app_body_start {} {
+ h2 [cgi_title]
+ puts "See [scriptlink] that created this page."
+ hr
+}
+
+proc app_body_end {} {
+ hr; puts "[cgi_link logolink]"
+ puts "Report problems with this script to [link admin]."
+ br; puts "CGI script author: [link Don], [link NIST]"
+ br; puts "Go back to [link CGITCL] or [link examples]."
+}
+
+cgi_body_args bgcolor=#00b0b0 text=#ffffff
+
+proc user_error {msg} {
+ h3 "Error: $msg"
+ cgi_exit
+}
+
+# support for rare examples that must be explicitly framed
+proc describe_in_frame {title msg} {
+ if {0 == [catch {cgi_import header}]} {
+ cgi_title $title
+ cgi_body {
+ p $msg
+ }
+ exit
+ }
+}
diff --git a/web/src/cgi.tcl-1.10/example/examples.cgi b/web/src/cgi.tcl-1.10/example/examples.cgi
new file mode 100755
index 00000000..75afb1e9
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/examples.cgi
@@ -0,0 +1,81 @@
+#!/depot/path/tclsh
+
+package require cgi
+
+cgi_eval {
+ source example.tcl
+
+ input
+
+ title "cgi.tcl examples"
+
+ # use targets if we are framed
+ if {0==[catch {import framed}]} {
+ set target "target=script"
+ } else {
+ set target ""
+ }
+
+ # create a hyperlink to run a script
+ proc run {name} {
+ url $name.cgi [cgi $name] [uplevel {set target}]
+ }
+
+ # create hyperlink to show script source
+ proc display {name} {
+ url $name.cgi [cgi display scriptname=$name.cgi] [uplevel {set target}]
+ }
+
+ body bgcolor=#d0a0a0 text=#000000 {
+ p "These are examples of cgi.tcl, a CGI support library for Tcl
+ programmers. If you would like to show off what you've
+ written with cgi.tcl, give me an html-formatted blurb and I'll
+ add it to a page of [link realapps]. For more information,
+ visit the [link CGITCL]."
+
+ bullet_list {
+ li "[run cookie] - Cookie example. Also see [run passwd-form] to see cookies in the context of a real application."
+ li "[run creditcard] - Check a credit card."
+ li "[run download] - Demonstrate file downloading. Also see [run upload]."
+ li "[run echo] - Echo everything - good for debugging forms."
+ li "[run error] - Error handling example."
+ li "[run evaljs] - Evaluate an expression using JavaScript."
+ li "[run examples] - Generate the page you are now reading."
+ li "[run form-tour] and [display form-tour-result] - Show
+ many different form elements and the backend to process them."
+ li "[run format-tour] - Demonstrate many formats."
+ li "[run frame] - Framed example (of this page)."
+ li "[url "image.cgi" [cgi display-in-frame [cgi_cgi_set scriptname \
+ image.cgi]] $target] - Produce a raw image."
+ li "[run img] - Examples of images embedded in a page."
+ li "[run kill] - Allow anyone to kill runaway CGI processes."
+ li "[display oratcl] - Use [link Oratcl] to query an Oracle database."
+ li "[run nistguest] - Specialized guestbook"
+ li "[run parray] - Demonstrate parray (print array elements)."
+ li "[run passwd-form] and [display passwd] - Form for
+ changing a password and its backend. Note that this CGI script
+ is [bold not] setuid because it is written using [link Expect].
+ The script also demonstrates a nice use of cookies."
+ li "[run push] - Demonstrate server-push."
+ li "[run rm] - Allow anyone to remove old CGI files from /tmp."
+ li "[run stopwatch] - A stopwatch written as a Tcl applet."
+ li "[display unimail] - A universal mail backend that mails the
+ values of any form back to the form owner."
+ li "[run upload] - Demonstrate file uploading. Also see [run download]."
+ li "[run utf] - Demonstrate UTF output."
+ li "[run validate] - Validate input fields using JavaScript."
+ li "[run vclock] - Lincoln Stein's virtual clock.
+ This was the big example in his CGI.pm paper. Examine the
+ source to [eval url [list "his Perl version"] \
+ [cgi display scriptname=vclock.pl] $target] or compare them
+ [url "side by side" [cgi vclock-src-frame] target=script2]."
+ li "[run visitor] - Example of a visitor counter."
+ li "[run version] - Show version information."
+ li "[run vote] - Vote for a quote."
+ li "[run display] - Display a CGI script with clickable
+ source commands. Not a particularly interesting application -
+ just a utility to help demo these other CGI scripts! But it is
+ written using cgi.tcl so it might as well be listed here."
+ }
+ }
+}
diff --git a/web/src/cgi.tcl-1.10/example/form-tour-result.cgi b/web/src/cgi.tcl-1.10/example/form-tour-result.cgi
new file mode 100755
index 00000000..d1278ecf
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/form-tour-result.cgi
@@ -0,0 +1,69 @@
+#!/depot/path/tclsh
+
+# This is a CGI script that shows the result of the form tour.
+
+package require cgi
+
+cgi_eval {
+ source example.tcl
+
+ cgi_input
+ cgi_title "Form Element Tour Results"
+
+ cgi_body {
+ h4 "This is a report of variables set by the form tour."
+
+ if {0!=[catch {cgi_import Foo0}]} {
+ h5 "Error: It appears that you have invoked this script
+ without going through [cgi_url "the intended form" [cgi_cgi form-tour]]."
+ cgi_exit
+ }
+
+ catch {
+ cgi_import Map.x
+ cgi_import Map.y
+ puts "image button coordinates = ${Map.x},${Map.y}[nl]"
+ }
+
+ catch {
+ cgi_import Action
+ puts "submit button Action=$Action[nl]"
+ br
+ }
+
+ foreach x {version A B C D} {
+ catch {
+ cgi_import $x
+ puts "radio button \"$x\": [set $x][nl]"
+ }
+ }
+ catch {
+ cgi_import VegieList
+ puts "checkbox Vegielist = $VegieList[nl]"
+ }
+ for {set i 0} {$i<=6} {incr i} {
+ set var Foo$i
+ cgi_import $var
+ puts "text $var:"
+ cgi_preformatted {
+ puts [set $var]
+ }
+ }
+ for {set i 0} {$i<=9} {incr i} {
+ set var Foo1$i
+ cgi_import $var
+ puts "textvar $var:"
+ cgi_preformatted {
+ puts [set $var]
+ }
+ }
+ catch {
+ cgi_import Foo
+ puts "select pull-down Foo: $Foo[nl]"
+ }
+ catch {
+ cgi_import FooList
+ puts "select scrolled list FooList: $FooList[nl]"
+ }
+ }
+}
diff --git a/web/src/cgi.tcl-1.10/example/form-tour.cgi b/web/src/cgi.tcl-1.10/example/form-tour.cgi
new file mode 100755
index 00000000..7e5e49d5
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/form-tour.cgi
@@ -0,0 +1,123 @@
+#!/depot/path/tclsh
+
+# This is a CGI script that shows a selection of form elements
+# This script doesn't actually DO anything.
+
+package require cgi
+
+cgi_eval {
+ source example.tcl
+
+ cgi_title "A Tour of Form Elements"
+
+ cgi_body {
+ form form-tour-result {
+ h4 "Samples of image button"
+ cgi_put "image as button"
+ cgi_image_button "=http://www.nist.gov/msidimages/title.gif"
+ br
+ cgi_put "image as button (and will return coords)"
+ cgi_image_button "Map=http://www.nist.gov/public_affairs/gallery/fireseat.jpg"
+
+ h4 "Samples of submit button"
+ cgi_submit_button
+ cgi_submit_button ="Submit"
+ cgi_submit_button "=Submit Form"
+ cgi_submit_button "Action=Pay Raise"
+
+ h4 "Samples of reset button"
+ cgi_reset_button
+ cgi_put "Default Reset Button"
+ br
+ cgi_reset_button "Not the Default Reset Button"
+ cgi_put "Not the Default Reset Button"
+
+ h4 "Samples of radio button"
+ cgi_radio_button "version=1"
+ cgi_put "Version 1"
+ br
+ cgi_radio_button "version=2"
+ cgi_put "Version 2"
+ br
+ cgi_radio_button "version=3" checked
+ cgi_put "Version 3"
+
+ br
+ foreach x {A B C D} {
+ cgi_radio_button "$x=" checked_if_equal=B
+ cgi_put "$x"
+ br
+ }
+
+ h4 "Samples of checkbox"
+ cgi_checkbox VegieList=carrot
+ cgi_put "Carrot"
+ br
+ cgi_checkbox VegieList=rutabaga checked
+ cgi_put "Rutabaga"
+ br
+ cgi_checkbox VegieList=
+ cgi_put "Vegie"
+
+ h4 "Samples of textentry"
+ set Foo0 "value1"
+ set Foo1 "value1"
+ cgi_text Foo0
+ br;cgi_text Foo1=
+ br;cgi_text Foo2=value2
+ br;cgi_text Foo3=value2 size=5
+ br;cgi_text Foo4=value2 size=5 maxlength=10
+ br;cgi_text Foo5=value2 size=10 maxlength=5
+ br;cgi_text Foo6=value2 maxlength=5
+
+ h4 "Samples of textarea"
+
+ set value "A really long line so that we can compare the\
+ effect of wrap options."
+
+ set Foo10 "value1"
+ set Foo11 "value1"
+ cgi_textarea Foo10
+ br;cgi_textarea Foo11=
+ br;cgi_textarea Foo12=$value
+ br;cgi_textarea Foo13=$value rows=3
+ br;cgi_textarea "Foo14=default wrap" rows=3 cols=7
+ br;cgi_textarea Foo15=wrap=off rows=3 cols=7 wrap=off
+ br;cgi_textarea Foo16=wrap=soft rows=3 cols=7 wrap=soft
+ br;cgi_textarea Foo17=wrap=hard rows=3 cols=7 wrap=hard
+ br;cgi_textarea Foo18=wrap=physical rows=3 cols=7 wrap=physical
+ br;cgi_textarea Foo19=wrap=virtual rows=3 cols=7 wrap=virtual
+
+ h4 "Samples of select as pull-down menu"
+ cgi_select Foo {
+ cgi_option one selected
+ cgi_option two
+ cgi_option many value=hello
+ }
+
+ h4 "Samples of select as scrolled list"
+ cgi_select FooList multiple {
+ cgi_option one selected
+ cgi_option two selected
+ cgi_option many
+ }
+ br
+ cgi_select FooList multiple size=2 {
+ cgi_option two selected
+ cgi_option three selected
+ cgi_option manymore
+ }
+ br
+ # choose "selected" dynamically
+ cgi_select FooList multiple size=5 {
+ foreach o [info comm] {
+ cgi_option $o selected_if_equal=exit
+ }
+ }
+ h4 "Samples of isindex"
+ }
+ cgi_isindex
+ cgi_isindex "prompt=Enter some delicious keywords: "
+ }
+}
+
diff --git a/web/src/cgi.tcl-1.10/example/format-tour.cgi b/web/src/cgi.tcl-1.10/example/format-tour.cgi
new file mode 100755
index 00000000..b399279f
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/format-tour.cgi
@@ -0,0 +1,101 @@
+#!/depot/path/tclsh
+
+# This is a CGI script that shows a selection of format elements
+
+package require cgi
+
+cgi_eval {
+ source example.tcl
+
+ cgi_title "A Tour of HTML Elements"
+ cgi_body {
+
+ definition_list {
+ term "term"
+ term_definition "definition of term"
+ }
+
+ h4 "menu list"
+ menu_list {
+ li item1
+ li item2
+ }
+
+ h4 "directory list"
+ directory_list {
+ li item1
+ li item2
+ }
+
+ h4 "a list item by itself"
+ li "item"
+
+ h4 "number list (roman, starting from 4)"
+ number_list type=i value=4 {
+ li "first element"
+ li "second"
+ li value=9 "third, start numbering from 9"
+ li type=A "fourth, switch to upper-arabic"
+ }
+
+ h4 "bullet list"
+ bullet_list {
+ p "plain text"
+ li "plain item"
+ h4 "nested list (disc, starting from 4)"
+ bullet_list type=disc value=4 {
+ li "first element"
+ li "second"
+ li type=circle "third, type=circle"
+ li type=square "fourth, type=square"
+ li "fifth, should remain square"
+ }
+ }
+
+ h4 "Character formatting samples"
+ cgi_put "[bold bold]\
+ [italic italic]\
+ [underline underline]\
+ [strikeout strikeout]\
+ [subscript subscript]\
+ [superscript superscript]\
+ [typewriter typewriter]\
+ [blink blink]
+ [emphasis emphasis]\
+ [strong strong]\
+ [cite cite]\
+ [sample sample]\
+ [keyboard keyboard]\
+ [variable variable]\
+ [definition definition]\
+ [big big]\
+ [small small]\
+ [font color=#4499cc "color=#4499cc"]\
+ "
+ for {set i 1} {$i<8} {incr i} {
+ puts [cgi_font size=$i "size=$i"]
+ }
+
+ h4 "Paragraph formatting samples"
+
+ cgi_h1 h1
+ cgi_h2 h2
+ cgi_h3 h3
+ cgi_h4 h4
+ cgi_h5 h5
+ cgi_h6 h6
+ cgi_h7 "h7 (beyond the spec, what the heck)"
+ cgi_h6 align=right "right-aligned h6"
+ cgi_p align=right "right-aligned paragraph"
+ cgi_put put
+ cgi_blockquote "blockquote"
+ cgi_address address
+ cgi_division {
+ puts "division"
+ }
+ cgi_preformatted {
+ puts "preformatted"
+ }
+ }
+}
+
diff --git a/web/src/cgi.tcl-1.10/example/frame.cgi b/web/src/cgi.tcl-1.10/example/frame.cgi
new file mode 100755
index 00000000..f8338b47
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/frame.cgi
@@ -0,0 +1,32 @@
+#!/depot/path/tclsh
+
+# This is a CGI script that creates some frames.
+
+package require cgi
+
+cgi_eval {
+ source example.tcl
+ cgi_input
+
+ cgi_title "Frame example"
+
+ # allow URL's of the form ;# frame.cgi?example=....
+ # so as to override default
+ set example examples ;# default is the examples page itself!!
+ catch {cgi_import example}
+
+ cgi_frameset rows=100,* {
+ cgi_frame =$CGITCL
+ cgi_frameset cols=200,* {
+ cgi_frame =examples.cgi?framed=yes
+ cgi_frame script=$example.cgi
+ }
+ }
+ cgi_noframes {
+ cgi_h1 "uh oh"
+
+ p "This document is designed to be viewed by a Frames-capable
+ browser. If you see this message your browser is not
+ Frames-capable."
+ }
+}
diff --git a/web/src/cgi.tcl-1.10/example/image.cgi b/web/src/cgi.tcl-1.10/example/image.cgi
new file mode 100755
index 00000000..e1321523
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/image.cgi
@@ -0,0 +1,29 @@
+#!/depot/path/tclsh
+# See description below.
+
+package require cgi
+
+cgi_eval {
+ source example.tcl
+ cgi_input
+
+ describe_in_frame "raw image" "This CGI script generates a raw
+ image. The script could be much more complicated - the point is
+ merely to show the framework. (The picture is of the US National
+ Prototype Kilogram. It is made of 90% platinum, 10% iridium. It
+ was assigned to the US in 1889 and is periodically recertified and
+ traceable to [italic "The Kilogram"] held at
+ [url "Bureau International des Poids et Mesures" http://www.bipm.fr" $TOP]
+ in France.)"
+
+ # ignore the junk above this line - the crucial stuff is below
+
+ cgi_content_type "image/jpeg"
+
+ set fh [open $DATADIR/kg.jpg r]
+ fconfigure stdout -translation binary
+ fconfigure $fh -translation binary
+ fcopy $fh stdout
+ close $fh
+}
+
diff --git a/web/src/cgi.tcl-1.10/example/img.cgi b/web/src/cgi.tcl-1.10/example/img.cgi
new file mode 100755
index 00000000..4c84787d
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/img.cgi
@@ -0,0 +1,39 @@
+#!/depot/path/tclsh
+
+# This is a CGI script that shows how to do simple images.
+
+package require cgi
+
+cgi_eval {
+ source example.tcl
+
+ cgi_title "Images"
+
+ set NIST_IMAGES http://www.nist.gov/images
+
+ cgi_imglink ball $NIST_IMAGES/ball.gif alt=ball
+ cgi_imglink birdies $NIST_IMAGES/brd-ln.gif alt=birdies
+ cgi_imglink daemon $NIST_IMAGES/bsd_daemon.gif "alt=Kirk McKusick's BSD deamon"
+
+ # use white background because some of these images require it
+ cgi_body bgcolor=#ffffff text=#00b0b0 {
+
+ p "Here are some birdies:[cgi_imglink birdies]"
+ p "and here is your basic ball [cgi_imglink ball] and here is
+ the BSD daemon [cgi_imglink daemon]."
+
+ p "I like using the same picture"
+ p "[cgi_imglink ball] over"
+ p "[cgi_imglink ball] and over"
+ p "[cgi_imglink ball] and over"
+ p "[cgi_imglink ball] so I use the cgi_imglink command to make it easy."
+
+ proc ball {} {return [cgi_imglink ball]}
+
+ p "[cgi_imglink birdies]"
+
+ p "[ball]I can make it even easier [ball] by making a ball
+ procedure [ball] which I've just done. [ball] You could
+ tell, eh? [ball]"
+ }
+}
diff --git a/web/src/cgi.tcl-1.10/example/kill.cgi b/web/src/cgi.tcl-1.10/example/kill.cgi
new file mode 100755
index 00000000..f3ffe6a3
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/kill.cgi
@@ -0,0 +1,69 @@
+#!/depot/path/tclsh
+
+package require cgi
+
+cgi_eval {
+ source example.tcl
+
+ cgi_input
+
+ cgi_title "Kill runaway CGI processes"
+
+ cgi_body {
+ if {0==[catch {cgi_import PidList}]} {
+ catch {cgi_import Refresh;set PidList {}}
+ cgi_import Sig
+ h4 "If this were not a demo, the following commands would be executed:"
+ foreach pid $PidList {
+ # to undemoize this and allow processes to be killed,
+ # change h5 to exec and remove the quotes
+ if {[catch {h5 "kill -$Sig $pid"} msg]} {
+ h4 "$msg"
+ }
+ }
+ }
+
+ cgi_form kill {
+ set ps /bin/ps
+ if {[file exists /usr/ucb/ps]} {
+ set ps /usr/ucb/ps
+ }
+
+ set f [open "|$ps -auxww" r]
+ table border=2 {
+ table_row {
+ table_data {puts kill}
+ table_data {cgi_preformatted {puts [gets $f]}}
+ }
+ while {-1 != [gets $f buf]} {
+ if {[regexp "$argv0" $buf]} continue
+ if {![regexp "^http" $buf]} continue
+ table_row {
+ table_data {
+ scan $buf "%*s %d" pid
+ cgi_checkbox PidList=$pid
+ }
+ table_data {
+ cgi_preformatted {puts $buf}
+ }
+ }
+ }
+
+ }
+
+ submit_button "=Send signal to selected processes"
+ submit_button "Refresh=Refresh listing"
+ reset_button
+
+ br; radio_button "Sig=TERM" checked; puts "SIGTERM: terminate gracefully"
+ br; radio_button "Sig=KILL"; puts "SIGKILL: terminate ungracefully"
+ br; radio_button "Sig=STOP"; puts "SIGSTOP: suspend"
+ br; radio_button "Sig=CONT"; puts "SIGCONT: continue"
+
+ p "SIGSTOP and SIGCONT are particularly useful if the
+ processes aren't yours and the owner isn't around to ask.
+ Suspend them and let the owner decide later whether to
+ continue or kill them."
+ }
+ }
+}
diff --git a/web/src/cgi.tcl-1.10/example/nistguest b/web/src/cgi.tcl-1.10/example/nistguest
new file mode 100644
index 00000000..651efc9a
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/nistguest
@@ -0,0 +1,102 @@
+Alabama
+{} {} x
+Alaska
+{} {} {}
+Arizona
+{} {} {}
+Arkansas
+{} {} x
+California
+{} {} {}
+Colorado
+{} {} x
+Connecticut
+{} {} x
+Delaware
+{} {} x
+Florida
+{} {} x
+Georgia
+{} {} {}
+Hawaii
+{} {} {}
+Idaho
+{} {} {}
+Illinois
+{} {} {}
+Indiana
+{} {} x
+Iowa
+{} {} x
+Kansas
+{} {} {}
+Kentucky
+{} {} {}
+Louisiana
+{} {} {}
+Maine
+{} {} x
+Maryland
+{} {} x
+Massachusetts
+{} {} {}
+Michigan
+{} {} x
+Minnesota
+{} {} x
+Mississippi
+{} {} {}
+Missouri
+{} {} x
+Montana
+{} {} {}
+Nebraska
+{} {} x
+Nevada
+{} {} {}
+New Hampshire
+{} {} x
+New Jersey
+{} {} x
+New Mexico
+{} {} {}
+New York
+{} {} x
+North Carolina
+{} {} x
+North Dakota
+{} {} {}
+Ohio
+{} {} {}
+Oklahoma
+{} {} x
+Oregon
+{} {} {}
+Pennsylvania
+{} {} {}
+Rhode Island
+{} {} {}
+South Carolina
+{} {} x
+South Dakota
+{} {} x
+Tennessee
+{} {} x
+Texas
+{} {} x
+Utah
+{} {} x
+Vermont
+{} {} {}
+Virginia
+{} {} x
+Washington
+{} {} x
+Washington DC
+{} {} {}
+West Virginia
+{} {} {}
+Wisconsin
+{} {} x
+Wyoming
+{} {} {}
diff --git a/web/src/cgi.tcl-1.10/example/nistguest.cgi b/web/src/cgi.tcl-1.10/example/nistguest.cgi
new file mode 100755
index 00000000..b41d456b
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/nistguest.cgi
@@ -0,0 +1,130 @@
+#!/depot/path/tclsh
+
+package require cgi
+
+cgi_eval {
+ source example.tcl
+ cgi_input
+
+ cgi_title "NIST Guest Book"
+
+ cgi_uid_check http
+
+ set BarURL $EXPECT_ART/pink.gif
+
+ set Q(filename) "$DATADIR/nistguest"
+
+ set statesNeeded {}
+
+ proc poll_read {} {
+ global Q
+
+ set fid [open $Q(filename) r]
+ while {-1!=[gets $fid StateFullName]} {
+ regsub " " $StateFullName "" State
+ set Q($State) $StateFullName
+ gets $fid buf
+ foreach "Q($State,1) Q($State,2) Q($State,3)" $buf {}
+ lappend Q(statesAll) $State
+ if {0 == [string compare "" "$Q($State,3)"]} {
+ lappend Q(statesNeeded) $State
+ }
+ }
+ close $fid
+ }
+
+ proc poll_write {} {
+ global Q
+
+ # No file locking or real database handy so we can't guarantee that
+ # simultaneous votes aren't dropped, but we'll at least avoid
+ # corruption of the file by working on a private copy.
+ set tmpfile $Q(filename).[pid]
+ set fid [open $tmpfile w]
+ foreach state $Q(statesAll) {
+ set data [list $Q($state,1) $Q($state,2) $Q($state,3)]
+ puts $fid $Q($state)\n$data
+ }
+ close $fid
+ exec mv $tmpfile $Q(filename)
+ }
+
+ cgi_body {
+ poll_read
+
+ if {0 == [catch {cgi_import State}]} {
+ if {![info exists Q($State)]} {
+ user_error "There is no such state: $State"
+ }
+
+ set Name ""
+ catch {import Name}
+ if {0==[string length $Name]} {
+ user_error "You didn't provide your name."
+ }
+
+ set Email ""
+ catch {import Email}
+
+ set Description ""
+ catch {import Description}
+ if {0==[string length $Description]} {
+ user_error "You didn't provide a description."
+ }
+
+ set data "<Name $Name><Email $Email><Description $Description>"
+ # simplify poll_read by making each entry a single line
+ regsub -all \n $data " " data
+
+ if {0 == [string compare "" $Q($State,1)]} {
+ set Q($State,1) $data
+ } elseif {0 == [string compare "" $Q($State,2)]} {
+ set Q($State,2) $data
+ } else {
+ set Q($State,3) $data
+ }
+
+ poll_write
+ puts "Thanks for your submission!"
+
+ return
+ }
+
+ form nistguest {
+ puts "In the spirit of Scriptics' request for Tcl success
+stories, our group at NIST is looking for some too. It's time for our
+annual report to Congress. So if your state appears in the list below
+and you can provide a brief description of how our work has helped
+you, we would appreciate hearing from you."
+ hr
+ br;puts "If your state does not appear in this list, then we
+already have enough entries for your state. Thanks anyway!"
+ br;puts "State:"
+ cgi_select State {
+ foreach state $Q(statesNeeded) {
+ option "$Q($state)" value=$state
+ }
+ }
+
+ br;puts "Full name:"
+ cgi_text Name=
+
+ br;puts "We probably won't need to contact you; But just in
+case, please provide some means of doing so. Email info will remain
+confidential - you will NOT be put on any mailing lists."
+ br;puts "Email:"
+ cgi_text Email=
+ puts "(optional)"
+
+ p "Please describe a significant impact (e.g., goals
+accomplished, hours/money saved, user expectations met or exceeded)
+that NIST's Tcl-based work (Expect, cgi.tcl, APDE, APIB, EXPRESS
+server, ...) has had on your organization. A brief paragraph is
+fine."
+
+ cgi_textarea Description= rows=10 cols=80
+ br
+ submit_button "=Submit"
+ }
+ }
+}
diff --git a/web/src/cgi.tcl-1.10/example/oratcl.cgi b/web/src/cgi.tcl-1.10/example/oratcl.cgi
new file mode 100644
index 00000000..de4a7626
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/oratcl.cgi
@@ -0,0 +1,33 @@
+#!/depot/path/tclsh
+
+# This is a CGI script that demonstrates how easy it is to use a web
+# page to query an Oracle server - using cgi.tcl and Oratcl.
+# This example fetches the date from Oracle.
+
+# I wish we had a public account on our Oracle server so that I could
+# allow anyone to run this, but alas we don't. So you'll have to
+# trust me that it works. - Don
+
+package require cgi
+package require Oratcl
+
+cgi_eval {
+ source example.tcl
+
+ cgi_title "Oracle Example"
+ cgi_input
+
+ cgi_body {
+ set env(ORACLE_SID) fork
+ set env(ORACLE_HOME) /u01/oracle/product/7322
+
+ set logon [oralogon [import user] [import password]]
+ set cursor [oraopen $logon]
+
+ orasql $cursor "select SysDate from Dual"
+ h4 "Oracle's date is [orafetch $cursor]"
+
+ oraclose $cursor
+ oralogoff $logon
+ }
+}
diff --git a/web/src/cgi.tcl-1.10/example/parray.cgi b/web/src/cgi.tcl-1.10/example/parray.cgi
new file mode 100755
index 00000000..a4c23316
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/parray.cgi
@@ -0,0 +1,48 @@
+#!/depot/path/tclsh
+
+# This is a CGI script that displays the environment or another array.
+
+package require cgi
+
+cgi_eval {
+ source example.tcl
+
+ proc arrays {} {
+ uplevel #0 {
+ set buf {}
+ foreach a [info globals] {
+ if {[array exists $a]} {
+ lappend buf $a
+ }
+ }
+ return $buf
+ }
+ }
+
+ cgi_input
+
+ cgi_title "Display environment or another array"
+
+ cgi_body {
+ p "This script displays the environment or another array."
+ if {[catch {cgi_import Name}]} {
+ set Name env
+ }
+
+ cgi_form parray {
+ cgi_select Name {
+ foreach a [arrays] {
+ cgi_option $a selected_if_equal=$Name
+ }
+ }
+ cgi_submit_button
+ }
+
+ global $Name
+ if {[array exist $Name]} {
+ cgi_parray $Name
+ } else {
+ puts "$Name: no such array"
+ }
+ }
+}
diff --git a/web/src/cgi.tcl-1.10/example/passwd-form.cgi b/web/src/cgi.tcl-1.10/example/passwd-form.cgi
new file mode 100755
index 00000000..9d99b716
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/passwd-form.cgi
@@ -0,0 +1,39 @@
+#!/depot/path/tclsh
+
+# This is a CGI script to present a form in which to change a password.
+
+# This form doesn't actually have to be written as a CGI script,
+# however, it is done so here to demonstrate the procedures described
+# in the Tcl '96 paper by Don Libes. You can find this same form
+# written as static html in the example directory of the Expect
+# package.
+
+package require cgi
+
+cgi_eval {
+ source example.tcl
+ source passwd.tcl
+
+ cgi_input
+
+ # Use a cookie so that if user has already entered name, don't make them
+ # do it again. If you dislike cookies, simply remove the next two
+ # lines - cookie use here is simply a convenience for users.
+ set login ""
+ catch {cgi_import_cookie login}
+
+ cgi_title "Change your login password"
+ cgi_body {
+ cgi_form passwd {
+ put "Username: "; cgi_text login size=16
+ password "Old" old
+ password "New" new1
+ password "New" new2
+
+ p "(The new password must be entered twice to avoid typos.)"
+
+ cgi_submit_button "=Change password"
+ cgi_reset_button
+ }
+ }
+}
diff --git a/web/src/cgi.tcl-1.10/example/passwd.cgi b/web/src/cgi.tcl-1.10/example/passwd.cgi
new file mode 100755
index 00000000..85c18174
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/passwd.cgi
@@ -0,0 +1,119 @@
+#!/depot/path/expect --
+
+# This is a CGI script to process requests created by the accompanying
+# passwd.html form. This script is pretty basic, although it is
+# reasonably robust. (Purposely intent users can make the script bomb
+# by mocking up their own HTML form, however they can't expose or steal
+# passwords or otherwise open any security holes.) This script doesn't
+# need any special permissions or ownership.
+#
+# With a little more code, the script can do much more exotic things -
+# for example, you could have the script:
+#
+# - telnet to another host first (useful if you run CGI scripts on a
+# firewall), or
+#
+# - change passwords on multiple password server hosts, or
+#
+# - verify that passwords aren't in the dictionary, or
+#
+# - verify that passwords are at least 8 chars long and have at least 2
+# digits, 2 uppercase, 2 lowercase, or whatever restrictions you like,
+# or
+#
+# - allow short passwords by responding appropriately to passwd
+#
+# and so on. Have fun!
+#
+# Don Libes, NIST
+
+package require cgi
+
+cgi_eval {
+ source example.tcl
+ source passwd.tcl
+
+ cgi_input
+
+ # Save username as cookie (see comment in passwd-form script) so that the
+ # next time users load the form, their username will already be filled in.
+ # If cookies bother you, simply remove this entire cgi_http_head command.
+ cgi_http_head {
+ cgi_content_type text/html
+ if {0==[catch {cgi_import login}]} {
+ cgi_export_cookie login expires=never
+ }
+ }
+
+ cgi_title "Password Change Acknowledgment"
+ cgi_body {
+ if {(![info exists login])
+ || [catch {cgi_import old}]
+ || [catch {cgi_import new1}]
+ || [catch {cgi_import new2}]} {
+ errormsg "This page has been called with input missing. Please \
+ visit this form by filling out the password change \
+ request form."
+ return
+ }
+
+ # Prevent user from sneaking in commands (albeit under their own uid).
+ if {[regexp "\[^a-zA-Z0-9]" $login char]} {
+ errormsg "Illegal character ($char) in username."
+ return
+ }
+
+ log_user 0
+
+ # Need to su first to get around passwd's requirement that
+ # passwd cannot be run by a totally unrelated user. Seems
+ # rather pointless since it's so easy to satisfy, eh?
+
+ # Change following line appropriately for your site.
+ # (We use yppasswd, but you might use something else.)
+ spawn /bin/su $login -c "/bin/yppasswd $login"
+ # This fails on SunOS 4.1.3 (passwd says "you don't have a
+ # login name") so run on (or telnet first to) host running
+ # SunOS 4.1.4 or later.
+
+ expect {
+ -re "Unknown (login|id):" {
+ errormsg "unknown user: $login"
+ return
+ } default {
+ errormsg "$expect_out(buffer)"
+ return
+ } "Password:"
+ }
+ send "$old\r"
+ expect {
+ "unknown user" {
+ errormsg "unknown user: $login"
+ return
+ } "Sorry" {
+ errormsg "Old password incorrect"
+ return
+ } default {
+ errormsg "$expect_out(buffer)"
+ return
+ } "Old password:"
+ }
+ send "$old\r"
+ expect "New password:"
+ send "$new1\r"
+ expect "New password:"
+ send "$new2\r"
+ expect -re (.+)\r\n {
+ set error $expect_out(1,string)
+ }
+ close
+ wait
+
+ if {[info exists error]} {
+ errormsg "$error"
+ } else {
+ successmsg "Password changed successfully."
+ }
+ }
+}
+
diff --git a/web/src/cgi.tcl-1.10/example/passwd.tcl b/web/src/cgi.tcl-1.10/example/passwd.tcl
new file mode 100644
index 00000000..31de1e17
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/passwd.tcl
@@ -0,0 +1,10 @@
+# common definitions for all password-related pages
+
+proc password {prompt varname} {
+ br
+ put "$prompt password: "
+ cgi_text $varname= type=password size=16
+}
+
+proc successmsg {s} {h3 "$s"}
+proc errormsg {s} {h3 "Error: $s"}
diff --git a/web/src/cgi.tcl-1.10/example/push.cgi b/web/src/cgi.tcl-1.10/example/push.cgi
new file mode 100755
index 00000000..75aeb98c
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/push.cgi
@@ -0,0 +1,37 @@
+#!/depot/path/tclsh
+
+package require cgi
+
+cgi_eval {
+ source example.tcl
+
+ set boundary "ThisRandomString"
+
+ cgi_http_head {
+ cgi_content_type "multipart/x-mixed-replace;boundary=$boundary"
+
+ puts \n--$boundary
+ cgi_content_type
+ }
+
+ cgi_title "Multipart example - 1st page"
+ cgi_body {
+ h4 "This is an example of [italic server-push] as implemented
+ by the multipart MIME type. In contrast with client-pull, push
+ leaves the connection open and the CGI script remains in control
+ as to send more information. The additional information can
+ be anything - this example demonstrates an entire page being
+ replaced."
+ }
+
+ puts \n--$boundary
+ after 5000
+
+ cgi_content_type
+
+ cgi_title "Multipart example - 2nd page"
+ cgi_body {
+ h4 "This page replaced the previous page with no action on the
+ client side."
+ }
+}
diff --git a/web/src/cgi.tcl-1.10/example/rm.cgi b/web/src/cgi.tcl-1.10/example/rm.cgi
new file mode 100644
index 00000000..bc9534af
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/rm.cgi
@@ -0,0 +1,59 @@
+#!/depot/path/tclsh
+
+package require cgi
+
+cgi_eval {
+ source example.tcl
+
+ cgi_input
+
+ cgi_title "Remove old CGI files from /tmp"
+
+ cgi_body {
+ if {0==[catch {cgi_import FileList}]} {
+ catch {cgi_import Refresh;set FileList {}}
+ h4 "If this were not a demo, the following commands would have been executed:"
+ foreach File $FileList {
+ # prevent deletion of this dir or anything outside it
+ set File [file tail $File]
+ switch $File . - .. - "" {
+ h3 "Illegal filename: $File"
+ continue
+ }
+
+ # to undemoize this and allow files to be killed,
+ # remove h5 and quotes
+ if {[catch {h5 "file delete -force /tmp/$File"} msg]} {
+ h4 "$msg"
+ }
+ }
+ }
+
+ cgi_form rm {
+ set f [open "|/bin/ls -Alt /tmp" r]
+ table border=2 {
+ table_row {
+ table_data {puts "rm -rf"}
+ table_data {cgi_preformatted {puts "permissions ln owner group size date filename"}}
+ }
+ while {-1 != [gets $f buf]} {
+ if {![regexp " http " $buf]} continue
+ table_row {
+ table_data {
+ regexp ".* (\[^ ]+)$" $buf dummy File
+ cgi_checkbox FileList=$File
+ }
+ table_data {
+ cgi_preformatted {puts $buf}
+ }
+ }
+ }
+
+ }
+
+ submit_button "=Removed selected files"
+ submit_button "Refresh=Refresh listing"
+ reset_button
+ }
+ }
+}
diff --git a/web/src/cgi.tcl-1.10/example/stopwatch.cgi b/web/src/cgi.tcl-1.10/example/stopwatch.cgi
new file mode 100755
index 00000000..4fb4f8c3
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/stopwatch.cgi
@@ -0,0 +1,64 @@
+#!/depot/path/tclsh
+
+# This is a CGI script that demonstrates a simple Tcl applet
+
+package require cgi
+
+source example.tcl
+
+set srcdir http://www.nist.gov/mel/div826/src/stopwatch
+set plugins http://www.sunlabs.com/research/tcl/plugin
+
+cgi_link source "source" $srcdir/stopwatch.tcl.html"
+cgi_link gz "complete distribution" $srcdir/stopwatch.tar.gz
+cgi_link moreplugins "More info on Tcl plugins" $plugins
+cgi_link homepage "homepage" $EXPECT_HOST/stopwatch
+
+cgi_eval {
+
+ cgi_input
+
+ cgi_head {
+ cgi_title "Stopwatch implemented via Tcl applet"
+ }
+ cgi_body {
+ h3 "Description"
+
+ p "This tclet provides a stopwatch. I wrote it to help me
+ time talks and individual slides within a talk. Stopwatch can
+ also be run as a Tk script outside the browser - which is the
+ way I normally use it. If you want to use it outside the browser, grab the distribution from the stopwatch [cgi_link homepage]."
+
+ h3 "Directions"
+
+ p {Press "start" to start the stopwatch and "stop" to stop it.
+ "zero" resets the time. You can also edit/cut/paste the time
+ by hand and set it to any valid time. A second timer is
+ provided as well. It works just like a normal lap timer.}
+
+ cgi_embed http://www.nist.gov/mel/div826/src/stopwatch/stopwatch.tcl \
+ 450x105
+
+ p "This is the first Tclet I've ever written. Actually, I
+ just took an existing Tk script I had already written and
+ wrapped it in an HTML page. It took about 30 minutes to write
+ the original Tk script (about 80 lines) and 1 minute to embed
+ it in an HTML page."
+
+ p "Stopwatch is not intended for timing less than one second
+ or longer than 99 hours. It's easy to make make it show more
+ but the code doesn't do it as distributed and I have no
+ interest in adding more and more features until it reads mail.
+ It's just a nice, convenient stopwatch."
+
+ h3 "For more info"
+
+ cgi_bullet_list {
+ cgi_li "Stopwatch [cgi_link homepage]."
+ cgi_li "Stopwatch [cgi_link source] and [cgi_link gz]."
+ cgi_li "[cgi_link moreplugins]."
+ }
+ }
+}
+
+
diff --git a/web/src/cgi.tcl-1.10/example/unimail.cgi b/web/src/cgi.tcl-1.10/example/unimail.cgi
new file mode 100755
index 00000000..9fca7cb0
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/unimail.cgi
@@ -0,0 +1,58 @@
+#!/depot/path/tclsh
+
+# This script is a universal mail backend. It's handy for creating
+# forms that simply email all their elements to someone else. You
+# wouldn't use it in a fancy application, but non-programmers like it
+# since they can use it to process forms with no CGI scripting at all.
+#
+# To use, make your form look something like this:
+#
+# <form action="http://ats.nist.gov/cgi-bin/cgi.tcl/unimail.cgi" method=post>
+# <input type=hidden name=mailto value="YOUR EMAIL ADDRESS HERE">
+# ...rest of your form...
+# </form>
+#
+# Note: You can use our action URL to try this out, but please switch
+# to using your own local unimail script for production use. Thanks!
+#
+# Author: Don Libes, NIST
+
+package require cgi
+
+cgi_eval {
+ source example.tcl
+
+ cgi_title "Universal mail backend"
+
+ cgi_body {
+ if {[catch cgi_input errormsg]} {
+ h2 "Form Error"
+ p "An error was detected in the form. Please send the
+ following diagnostics to the form author."
+ cgi_preformatted {puts $errormsg}
+ return
+ }
+ if {[catch {cgi_import mailto}]} {
+ h2 "Error: No mailto variable in form."
+ return
+ }
+ if {![info exists env(HTTP_REFERER)]} {
+ set env(HTTP_REFERER) "unknown"
+ }
+ cgi_mail_start $mailto
+ cgi_mail_add "Subject: submission from web form: $env(HTTP_REFERER)"
+ cgi_mail_add
+ catch {cgi_mail_add "Remote addr: $env(REMOTE_ADDR)"}
+ catch {cgi_mail_add "Remote host: $env(REMOTE_HOST)"}
+
+ foreach item [cgi_import_list] {
+ cgi_mail_add "$item: [cgi_import $item]"
+ }
+ cgi_mail_end
+
+ if {[catch {cgi_import thanks}]} {
+ set thanks [cgi_buffer {h2 "Thanks for your submission."}]
+ }
+ cgi_put $thanks
+ }
+}
diff --git a/web/src/cgi.tcl-1.10/example/upload.cgi b/web/src/cgi.tcl-1.10/example/upload.cgi
new file mode 100755
index 00000000..9841aa97
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/upload.cgi
@@ -0,0 +1,59 @@
+#!/depot/path/tclsh
+
+# This is a CGI script that demonstrates file uploading.
+
+package require cgi
+
+cgi_eval {
+ source example.tcl
+
+ proc showfile {v} {
+ catch {
+ set server [cgi_import_file -server $v]
+ set client [cgi_import_file -client $v]
+ set type [cgi_import_file -type $v]
+ if {[string length $client]} {
+ h4 "Uploaded: $client"
+ if {0 != [string compare $type ""]} {
+ h4 "Content-type: $type"
+ }
+ cgi_import showList
+ foreach show $showList {
+ switch $show {
+ "od -c" - "cat" {
+ h5 "Contents shown using $show"
+ cgi_preformatted {puts [eval exec $show [list $server]]}
+ }
+ }
+ }
+ }
+ exec /bin/rm -f $server
+ }
+ }
+
+ cgi_input
+
+ cgi_head {
+ cgi_title "File upload demo"
+ }
+ cgi_body {
+ if {[info tcl] < 8.1} {
+ h4 "Warning: This script can not perform binary uploads because the server is running a pre-8.1 Tcl ([info tcl])."
+ }
+
+ showfile file1
+ showfile file2
+
+ cgi_form upload enctype=multipart/form-data {
+ p "Select up to two files to upload"
+ cgi_file_button file1; br
+ cgi_file_button file2; br
+ checkbox "showList=cat" checked;
+ put "show contents using cat" ;br
+ checkbox "showList=od -c"
+ put "show contents using od -c" ;br
+ cgi_submit_button =Upload
+ }
+ }
+}
+
diff --git a/web/src/cgi.tcl-1.10/example/utf.cgi b/web/src/cgi.tcl-1.10/example/utf.cgi
new file mode 100644
index 00000000..1a5a1243
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/utf.cgi
@@ -0,0 +1,17 @@
+#!/local/bin/tclsh
+
+# Test UTF encoding
+
+package require cgi
+
+cgi_eval {
+ source example.tcl
+
+ cgi_title "Study utf encoding"
+ cgi_html {
+ cgi_body {
+ p "I'm going to be living on the Straüße."
+ }
+ }
+}
+
diff --git a/web/src/cgi.tcl-1.10/example/validate.cgi b/web/src/cgi.tcl-1.10/example/validate.cgi
new file mode 100755
index 00000000..c104b7b5
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/validate.cgi
@@ -0,0 +1,76 @@
+#!/depot/path/tclsh
+
+# This CGI script uses JavaScript to validate a form before submission.
+
+package require cgi
+
+cgi_eval {
+ source example.tcl
+
+ cgi_input
+
+ cgi_head {
+ title "Using JavaScript to validate a form before submission"
+
+ javascript {
+ puts {
+ function odd(num) {
+ if (num.value % 2 == 0) {
+ alert("Please enter an odd number!")
+ num.value = ""
+ return false
+ }
+ return true
+ }
+ }
+ }
+ noscript {
+ puts "Sorry - your browser doesn't understand JavaScript."
+ }
+ }
+
+ set rownum 0
+ proc row {msg {event {}}} {
+ global rownum
+
+ incr rownum
+ table_row {
+ table_data nowrap {
+ put "Odd number: "
+ text num$rownum= size=4 $event
+ }
+ table_data {
+ puts $msg
+ }
+ }
+ }
+
+ cgi_body {
+ set more ""
+ if {0 == [catch {import num3}]} {
+ set count [scan $num3 %d num]
+ if {($count != 1) || ($num % 2 == 0)} {
+ p "Hey, you didn't enter an odd number!"
+ } else {
+ p "Thanks for entering odd numbers!"
+ set more " more"
+ }
+ }
+
+ puts "Please enter$more odd numbers - thanks!"
+
+ cgi_form validate "onSubmit=return odd(this.num2)" {
+ table {
+ row "This number will be validated when it is entered." onChange=odd(this.form.num1)
+ row "This number will be validated when the form is submitted."
+ row "This number will be validated after the form is submitted."
+ }
+ submit_button =Submit
+ }
+
+ h5 "Note: JavaScript validation should always be accompanied
+ by validation in the backend (CGI script) since browsers
+ cannot be relied upon to have JavaScript enabled (or supported
+ in the first place). Sigh."
+ }
+}
diff --git a/web/src/cgi.tcl-1.10/example/vclock-src-frame.cgi b/web/src/cgi.tcl-1.10/example/vclock-src-frame.cgi
new file mode 100755
index 00000000..37cd6d4b
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/vclock-src-frame.cgi
@@ -0,0 +1,23 @@
+#!/depot/path/tclsh
+
+# This CGI script displays the Tcl and Perl vclock source side by side.
+
+package require cgi
+
+cgi_eval {
+ source example.tcl
+
+ cgi_title "Comparison of vclock source"
+
+ cgi_frameset cols=50%,* {
+ cgi_frame =[cgi_cgi display scriptname=vclock.cgi]
+ cgi_frame =[cgi_cgi display scriptname=vclock.pl]
+ }
+ cgi_noframes {
+ cgi_h1 "uh oh"
+
+ p "This document is designed to be viewed by a Frames-capable
+ browser. If you see this message your browser is not
+ Frames-capable."
+ }
+}
diff --git a/web/src/cgi.tcl-1.10/example/vclock.cgi b/web/src/cgi.tcl-1.10/example/vclock.cgi
new file mode 100755
index 00000000..4b925b9f
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/vclock.cgi
@@ -0,0 +1,73 @@
+#!/depot/path/tclsh
+
+# This script implements the "Virtual Clock" used as the example in the
+# paper describing CGI.pm, a perl module for generating CGI.
+# Stein, L., "CGI.pm: A Perl Module for Creating Dynamic HTML Documents
+# with CGI Scripts", SANS 96, May '96.
+
+# Do you think it is more readable than the other version?
+# (If you remove the comments and blank lines, it's exactly
+# the same number of lines.) See other comments after script. - Don
+
+package require cgi
+
+cgi_eval {
+ source example.tcl
+
+ cgi_input
+
+ set format ""
+ if {[llength [cgi_import_list]]} {
+ if 0==[catch {cgi_import time}] {
+ append format [expr {[cgi_import type] == "12-hour"?"%r ":"%T "}]
+ }
+ catch {cgi_import day; append format "%A "}
+ catch {cgi_import month; append format "%B "}
+ catch {cgi_import day-of-month; append format "%d "}
+ catch {cgi_import year; append format "%Y "}
+ } else {
+ append format "%r %A %B %d %Y"
+ }
+
+ set time [clock format [clock seconds] -format $format]
+
+ cgi_title "Virtual Clock"
+
+ cgi_body {
+ puts "At the tone, the time will be [strong $time]"
+ hr
+ h2 "Set Clock Format"
+
+ cgi_form vclock {
+ puts "Show: "
+ foreach x {time day month day-of-month year} {
+ cgi_checkbox $x checked
+ put $x
+ }
+ br
+ puts "Time style: "
+ cgi_radio_button type=12-hour checked;put "12-hour"
+ cgi_radio_button type=24-hour ;put "24-hour"
+ br
+ cgi_reset_button
+ cgi_submit_button =Set
+ }
+ }
+}
+
+# Notes:
+
+# Time/date generation is built-in to Tcl. Thus, no extra processes
+# are necessary and the result is portable. In contrast, CGI.pm
+# only runs on UNIX.
+
+# Displaying checkboxes side by side the way that CGI.pm does by
+# default is awful. The problem is that with enough buttons, it's not
+# immediately clear if the button goes with the label on the right or
+# left. So cgi.tcl does not supply a proc to generate such a
+# grouping. I've followed CGI.pm's style here only to show that it's
+# trivial to get the same affect, but the formatting in any real form
+# is more wisely left to the user.
+
+# Footer generation (<hr><address>... at end of CGI.pm) is replaced
+# by "source example.tcl". Both take one line.
diff --git a/web/src/cgi.tcl-1.10/example/vclock.pl b/web/src/cgi.tcl-1.10/example/vclock.pl
new file mode 100644
index 00000000..0605f0c7
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/vclock.pl
@@ -0,0 +1,59 @@
+#!/usr/local/bin/perl
+
+# This script is the "Virtual Clock" example in the seminal
+# paper describing CGI.pm, a perl module for generating CGI.
+# Stein, L., "CGI.pm: A Perl Module for Creating Dynamic HTML Documents
+# with CGI Scripts", SANS 96, May '96.
+
+# Do you think it is more readable than the other version?
+# (If you remove the comments and blank lines, it's exactly
+# the same number of lines.) - Don
+
+use CGI;
+$q = new CGI;
+
+if ($q->param) {
+ if ($q->param('time')) {
+ $format = ($q->param('type') eq '12-hour') ? '%r ' : '%T';
+ }
+
+ $format .= '%A ' if $q->param('day');
+ $format .= '%B ' if $q->param('month');
+ $format .= '%d ' if $q->param('day-of-month');
+ $format .= '%Y ' if $q->param('year');
+} else {
+ $format = '%r %A %B %d %Y';
+}
+
+$time = `date '+$format'`;
+
+# print the HTTP header and the HTML document
+print $q->header;
+print $q->start_html('Virtual Clock');
+print <<END;
+<H1>Virtual Clock</H1>
+At the tone, the time will be <STRONG>$time</STRONG>.
+END
+
+print <<END;
+<HR>
+<H2>Set Clock Format</H2>
+END
+
+# Create the clock settings form
+print $q->start_form;
+print "Show: ";
+print $q->checkbox(-name->'time',-checked=>1);
+print $q->checkbox(-name->'day',-checked=>1);
+print $q->checkbox(-name->'month',-checked=>1);
+print $q->checkbox(-name->'day-of-month',-checked=>1);
+print $q->checkbox(-name->'year',-checked=>1);
+print "<P>Time style:";
+print $q->radio_group(-name=>'type',
+ -values=>['12-hour','24-hour']),"<P>";
+print $q->reset(-name=>'Reset'),
+ $q->submit(-name=>'Set');
+print $q->end_form;
+
+print '<HR><ADDRESS>webmaster@ferrets.com</ADDRESS>'
+print $q->end_html;
diff --git a/web/src/cgi.tcl-1.10/example/version.cgi b/web/src/cgi.tcl-1.10/example/version.cgi
new file mode 100644
index 00000000..b266acfb
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/version.cgi
@@ -0,0 +1,30 @@
+#!/depot/path/tclsh
+
+# This is a CGI script that displays some version information that I
+# find useful for debugging.
+
+set v [package require cgi]
+
+proc row {var val} {
+ table_row {
+ td $var
+ td $val
+ }
+}
+
+cgi_eval {
+ source example.tcl
+
+ title "Version info"
+
+ cgi_body {
+ table border=2 {
+ row "cgi.tcl" $v
+ row "Tcl" [info patchlevel]
+ row "uname -a" [exec uname -a]
+ catch {row "SERVER_SOFTWARE" $env(SERVER_SOFTWARE)}
+ catch {row "HTTP_USER_AGENT" $env(HTTP_USER_AGENT)}
+ catch {row "SERVER_PROTOCOL" $env(SERVER_PROTOCOL)}
+ }
+ }
+}
diff --git a/web/src/cgi.tcl-1.10/example/visitor.cgi b/web/src/cgi.tcl-1.10/example/visitor.cgi
new file mode 100755
index 00000000..ee73922e
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/visitor.cgi
@@ -0,0 +1,30 @@
+#!/depot/path/tclsh
+
+package require cgi
+
+cgi_eval {
+ source example.tcl
+
+ cgi_title "Visitor count example"
+
+ cgi_body {
+ cgi_put "This page demonstrates how easy it is to do visitor counts."
+
+ cgi_uid_check http
+
+ cgi_form visitor {
+ set cfname "$DATADIR/visitor.cnt"
+
+ if {[catch {set fid [open $cfname r+]} errormsg]} {
+ h4 "Couldn't open $cfname to maintain visitor count: $errormsg"
+ return
+ }
+ gets $fid count
+ seek $fid 0 start
+ puts $fid [incr count]
+ close $fid
+ h4 "You are visitor $count. Revisit soon!"
+ cgi_submit_button "=Revisit"
+ }
+ }
+}
diff --git a/web/src/cgi.tcl-1.10/example/visitor.cnt b/web/src/cgi.tcl-1.10/example/visitor.cnt
new file mode 100755
index 00000000..5595fa46
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/visitor.cnt
@@ -0,0 +1 @@
+95
diff --git a/web/src/cgi.tcl-1.10/example/vote.cgi b/web/src/cgi.tcl-1.10/example/vote.cgi
new file mode 100755
index 00000000..6d06b977
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/vote.cgi
@@ -0,0 +1,190 @@
+#!/depot/path/tclsh
+
+package require cgi
+
+cgi_eval {
+ source example.tcl
+ cgi_input
+
+ cgi_title "Vote for a t-shirt design!"
+
+ cgi_uid_check http
+
+ set BarURL $EXPECT_ART/pink.gif
+
+ set Q(filename) "$DATADIR/vote.cnt"
+
+ proc votes_read {} {
+ global Q
+
+ set Q(Max) 0
+ set Q(Votes) 0
+ set Q(Indices) ""
+
+ set fid [open $Q(filename) r]
+ while {-1!=[gets $fid i]} {
+ gets $fid buf
+ foreach "Q(Votes,$i) Q(Entry,$i) Q(Name,$i) Q(Email,$i)" $buf {}
+ lappend Q(Indices) $i
+ set Q(Unvotable,$i) [catch {incr Q(Votes) $Q(Votes,$i)}]
+ set Q(Max) $i
+ }
+ close $fid
+ }
+
+ proc votes_write {} {
+ global Q
+
+ # No file locking (or real database) handy so we can't guarantee that
+ # simultaneous votes aren't dropped, but we'll at least avoid
+ # corruption of the vote file by working on a private copy.
+ set tmpfile $Q(filename).[pid]
+ set fid [open $tmpfile w]
+ foreach i $Q(Indices) {
+ set data [list $Q(Votes,$i) $Q(Entry,$i) $Q(Name,$i) $Q(Email,$i)]
+ # simplify votes_read by making each entry a single line
+ regsub -all \n $data <br> data
+ puts $fid $i\n$data
+ }
+ close $fid
+ exec mv $tmpfile $Q(filename)
+ }
+
+ proc vote_other_show {} {
+ global Q
+
+ h3 "Other suggestions"
+ table border=2 {
+ table_row {
+ th width=300 "Entry"
+ th width=300 "Judge's Comments"
+ }
+ foreach i $Q(Indices) {
+ if {!$Q(Unvotable,$i)} continue
+ table_row {
+ td width=300 "$Q(Entry,$i)"
+ td width=300 "$Q(Votes,$i)"
+ }
+ }
+ }
+ }
+
+
+ cgi_body {
+ votes_read
+
+ if {[regexp "Vote(\[0-9]+)" [cgi_import_list] dummy i]} {
+ if {[catch {incr Q(Votes,$i)}]} {
+ user_error "There is no such entry to vote for."
+ }
+ incr Q(Votes)
+ votes_write
+
+ h3 "Thanks for voting! See you at the next Tcl conference!"
+ set ShowVotes 1
+ }
+ catch {cgi_import ShowVotes}
+ if {[info exists ShowVotes]} {
+ table border=2 {
+ table_row {
+ th width=300 "Entry"
+ th "Votes"
+ th width=140 "Percent" ;# 100 + room for pct
+ }
+ foreach i $Q(Indices) {
+ if {!$Q(Unvotable,$i)} {
+ table_row {
+ td width=300 "$Q(Entry,$i)"
+ td align=right "$Q(Votes,$i)"
+ table_data width=140 {
+ table {
+ table_row {
+ set pct [expr 100*$Q(Votes,$i)/$Q(Votes)]
+ # avoid 0-width Netscape bug
+ set pct_bar [expr $pct==0?1:$pct]
+ td [img $BarURL align=left width=$pct_bar height=15]
+ td $pct
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ form vote {
+ submit_button "=Submit entry or vote"
+ submit_button "ShowVotes=Show votes"
+ }
+ vote_other_show
+ return
+ }
+
+ if 0==[catch {import Entry}] {
+ if {[string length $Entry] == 0} {
+ user_error "No entry found."
+ }
+ if {[string length $Entry] > 500} {
+ user_error "Your entry is too long. Keep it under 500 characters!"
+ }
+ set Name ""
+ catch {import Name}
+ if 0==[string length $Name] {
+ user_error "You must supply your name. How are we going to know who you are if you win?"
+ }
+ set Email ""
+ catch {import Email}
+ if 0==[string length $Email] {
+ user_error "You must supply your email. How are we going to contact you if you win?"
+ }
+
+ set i [incr Q(Max)]
+ set Q(Entry,$i) $Entry
+ set Q(Name,$i) $Name
+ set Q(Email,$i) $Email
+ set Q(Votes,$i) 1
+ lappend Q(Indices) $i
+
+ votes_write
+
+ h3 "Thanks for your new entry!"
+ p "No need to go back and vote for it - a vote has already
+ been recorded for you."
+ form vote {
+ submit_button "=Submit another entry or vote"
+ submit_button "ShowVotes=Show votes"
+ }
+ return
+ }
+
+ p "Vote for what will go on the next Tcl conference t-shirt! (Feel free to vote for several entries.)"
+
+ cgi_form vote {
+ table border=2 {
+ foreach i $Q(Indices) {
+ if {$Q(Unvotable,$i)} continue
+ table_row {
+ table_data {
+ cgi_submit_button Vote$i=Vote
+ }
+ td "$Q(Entry,$i)"
+ }
+ }
+ }
+ br
+ cgi_submit_button "ShowVotes=Just show me the votes"
+ hr
+ p "The author of the winning entry gets fame and glory (and a free t-shirt)! Submit a new entry:"
+ cgi_text Entry= size=80
+ p "Entries may use embedded HTML. Images or concepts are fine - for artwork, we have the same artist who did the [url "'96 conference shirt" $MSID_STAFF/libes/t.html]). The [url judges mailto:tclchairs@usenix.org] reserve the right to delete entries. (Do us a favor and use common sense and good taste!)"
+ puts "Name: "
+ cgi_text Name=
+ br
+ puts "Email: "
+ cgi_text Email=
+ br
+ cgi_submit_button "=Submit new entry"
+
+ vote_other_show
+ }
+ }
+}
diff --git a/web/src/cgi.tcl-1.10/example/vote.cnt b/web/src/cgi.tcl-1.10/example/vote.cnt
new file mode 100644
index 00000000..73bc9061
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/example/vote.cnt
@@ -0,0 +1,30 @@
+1
+179 {Tcl/Tk: The best-kept secret in the software industry} {Michael McLennan} mmc
+2
+{it's got potential, needs more work} {Tcl/Tk: Saving the world one proc at a time} {Michael McLennan} mmc
+3
+24 {Tcl/Tk: Programmers by day, heros by the end of the quarter} {Michael McLennan} mmc
+4
+39 {Tcl/Tk: The struggle between good and eval} {Michael McLennan} mmc
+6
+{already dated} {I love Tcl...but <i>puleeez</i> don't call me Elmo!} {mark pernal} mpernal@sprintmail.com
+7
+{surely we have more imagination?} spec-TCL-ular {Joerg Reiberg} reiberg@uni-muenster.de
+8
+48 {Tcl/Tk: Less Code, More Results} {Bob Jackson} jackson@stsci.edu
+9
+{committed alright} {True Committed Lover} {Stephan Uyttebroeck} stephan@frontierd.com
+12
+{may we suggest you invest five minutes reading the manual...} {Tcl/Tk: Welcome to string quoting hell!} {Harry Bovik} bovik+junk@cs.cmu.edu
+13
+{We'll make up a special one-of-a-kind t-shirt for you, ok?} {I'd rather be hacking Perl.} psu psu@jprc.com
+15
+{has potential but needs more work} {Tcl/Tk: A Rabid Prototyping Language} {Ralph Melton} ralph@cs.cmu.edu
+16
+{the important thing is, you were using it!} {Tcl/Tk: If at first you don't succeed, you must have been using it} {Dushyanth Narayanan} bumba=roc_tcltk@cs.cmu.edu
+17
+{the Howard Stern / People Magazine version} {perl: the angry drunken scripting language} {John Prevost} visigoth+www@cs.cmu.edu
+18
+{already taken by Perl, shucks} {Perl: a fast and powerful tool for creating completely inconsistent, incoherent, and unmaintainable code.} {Don} don@libes.com
+19
+{ok, enough Perl bashing!} {Perl: an awesome collection of special cases, side-effects, neato punctuation, and enough ambiguity to make a Ouija board blush.} {Don} don@libes.com
diff --git a/web/src/cgi.tcl-1.10/fixline1 b/web/src/cgi.tcl-1.10/fixline1
new file mode 100755
index 00000000..1cb57972
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/fixline1
@@ -0,0 +1,13 @@
+#!/depot/path/expect --
+# Synopsis: fixline1 newpath < input > output
+# Author: Don Libes
+
+# Description: change first line of script to reflect new binary
+# try to match any of the following first lines
+#!expect ...
+#!../expect ...
+#!expectk ...
+#!foo/bar/expectk ...
+#
+regsub "^#!(.*/)*(.*)" [gets stdin] "#!$argv/\\2" line1
+puts -nonewline "$line1\n[read stdin]"
diff --git a/web/src/cgi.tcl-1.10/install-sh b/web/src/cgi.tcl-1.10/install-sh
new file mode 100755
index 00000000..89fc9b09
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/install-sh
@@ -0,0 +1,238 @@
+#! /bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+#
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+tranformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+else
+ true
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d $dst ]; then
+ instcmd=:
+ else
+ instcmd=mkdir
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f $src -o -d $src ]
+ then
+ true
+ else
+ echo "install: $src does not exist"
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "install: no destination specified"
+ exit 1
+ else
+ true
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d $dst ]
+ then
+ dst="$dst"/`basename $src`
+ else
+ true
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp="${pathcomp}${1}"
+ shift
+
+ if [ ! -d "${pathcomp}" ] ;
+ then
+ $mkdirprog "${pathcomp}"
+ else
+ true
+ fi
+
+ pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd $dst &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ dstfile=`basename $dst $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ true
+ fi
+
+# Make a temp file name in the proper directory.
+
+ dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd $src $dsttmp &&
+
+ trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing. If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+ $doit $rmcmd -f $dstdir/$dstfile &&
+ $doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0
diff --git a/web/src/cgi.tcl-1.10/install.mac b/web/src/cgi.tcl-1.10/install.mac
new file mode 100644
index 00000000..a79c6b72
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/install.mac
@@ -0,0 +1,70 @@
+This file is install.mac. It contains installation instructions for
+cgi.tcl on MacOS.
+
+If you do not have Tcl, get it (the README explains how) and install
+it. The rest of these instructions assume that you have Tcl
+installed.
+
+--------------------
+Installation
+--------------------
+
+These instructions are based on contributions courtesy of Henk-Jan
+Kooiman <hjk@cable.a2000.nl>. Send fixes to me (libes@nist.gov).
+
+If you just want to experiment with cgi.tcl, you can simply source it
+into your Tcl files by saying "source cgi.tcl".
+
+Once you're done playing, go ahead and install it. To install it:
+
+1) Make a package index. (This will create pkgIndex.tcl which will
+make it possible to use "package require cgi" in scripts.) Asari
+Hirotsugu <asari@math.uiuc.edu> has supplied the following
+elaboration of this step:
+
+ 1a) Put the cgi.tcl folder in the "Tool Command Language" folder
+ inside the Extensions folder. (Don't make an alias for the Tool
+ Command Language folder since Tcl Shell doesn't resolve aliases as
+ of 8.2.1.)
+
+ 1b) Launch the Tcl Shell (or Wish) and move to the Tool Command
+ Language folder by entering:
+
+ cd "Macintosh HD:System Folder:Extensions:Tool Command Language"
+
+ (You may have to modify this command depending upon the names
+ and structure of your file system.)
+
+ Issue the pkg_mkIndex command:
+
+ pkg_mkIndex cgi.tcl*
+
+ 1c) Test if the package comand works by trying:
+
+ package require cgi
+
+2) You may want to edit some things in cgi.tcl at this time.
+
+ 2a) Upon errors in production code, cgi.tcl sends mail to an
+ administrator. This can be set on a per-script basis but it
+ defaults to "root". You'll probably want to change this for your
+ site. To do that, search cgi.tcl for cgi_admin_mail_addr "root" and
+ change the argument to whatever you prefer.
+
+ 2b) The cgi_mail_end procedure attempts to do mail delivery using
+ SMTP (the de facto Internet mail protocol). However, this mechanism
+ is not robust. For example, if the mail gateway is down, the mail
+ will not be requeued for later delivery. If you have a robust
+ mailer program or some other interface, you should switch to using
+ it. The ability to send mail isn't required for basic use of
+ cgi.tcl, but this ability is especially useful for in-the-field
+ debugging so I encourage you to use it.
+
+You're done! Now you can use cgi.tcl.
+
+--------------------
+Examples
+--------------------
+
+The example directory has some examples. See the README in there.
+
diff --git a/web/src/cgi.tcl-1.10/install.win b/web/src/cgi.tcl-1.10/install.win
new file mode 100644
index 00000000..424fc649
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/install.win
@@ -0,0 +1,105 @@
+This file is install.win. It contains installation instructions for
+cgi.tcl on Win95/NT.
+
+If you do not have Tcl, get it (the README explains how) and install
+it. The rest of these instructions assume that you have Tcl
+installed.
+
+--------------------
+Installation
+--------------------
+
+These instructions are based on contributions courtesy of Maan
+M. Hamze <mmhamze@pleiades.net> and Martin Meuer
+<martin.meuer@frz.de>. Send fixes to me (libes@nist.gov).
+
+If you just want to experiment with cgi.tcl, you can simply source it
+into your Tcl files by saying "source cgi.tcl".
+
+Once you're done playing, go ahead and install it. To install it:
+
+1) Open wish8.0 and run the following:
+
+ pkg_mkIndex c:\\tcl\\lib\\tcl8.0 cgi.tcl
+
+(assuming that you have Tcl libraries in the above directory). This
+This will create pkgIndex.tcl which will make it possible to use
+"package require cgi" in scripts.
+
+2) The only files actually necessary for production use are
+pkgIndex.tcl and cgi.tcl. Make sure those files and their directory
+have execute permission for the web server scripts.
+
+3) You may want to edit some things in cgi.tcl at this time.
+
+3a) The default extension of the files that work with cgi.tcl is .cgi.
+In case that extension is already being used by another program for
+your cgi programs, you may want to change that extension into
+something else, say, .cgt. To do that, search cgi.tcl for the string
+.cgi and replace with .cgt or whatever you prefer.
+
+3b) Upon errors in production code, cgi.tcl sends mail to an
+administrator. This can be set on a per-script basis but it defaults
+to "root". You'll probably want to change this for your site. To do
+that, search cgi.tcl for cgi_admin_mail_addr "root" and change the
+argument to whatever you prefer.
+
+3c) The cgi_mail_end procedure attempts to do mail delivery using SMTP
+(the de facto Internet mail protocol). However, this mechanism is not
+robust. For example, if the mail gateway is down, the mail will not
+be requeued for later delivery. If you have a robust mailer program
+or some other interface, you should switch to using it. The ability
+to send mail isn't required for basic use of cgi.tcl, but this ability
+is especially useful for in-the-field debugging so I encourage you to
+use it.
+
+4) Tcl/Tk 8.0 should have automatically created a file type to run
+files with the .tcl extension. Make sure by running:
+Explorer...Options....File Types. .tcl files should be associated
+with Wish8.0.
+
+5) The web server must now be told about Tcl. The exact details
+depend on your particular server. Here are instructions that people
+have sent to me for different servers. (Feel free to send more.)
+
+5a) If you are using Personal Web Server for Win 95 or MS IIS ver 3
+for Win NT:
+
+ Instructions according to Maan M. Hamze <mmhamze@pleiades.net>
+
+Run the Registry Editor.
+Go to:
+HKEY_LOCAL_MACHINE
+ System
+ CurrentControlSet
+ Services
+ W3SVC
+ Parameters
+ Script Map
+Create a new String Value through Edit:
+First enter the name of the extension (.cgi or the extension you want to use as
+in .cgt). Associate the extension with:
+FullPathToTclExecutable\tclsh80.exe %s
+
+5b) If you are using Netscape Enterprise Server:
+
+ Instructions according to Martin Meuer <martin.meuer@frz.de>.
+
+5b1) Create an association from NT-Explorer (View-Options-Filetypes) for
+the extension .cgt to tell NT to open .cgt with "/fullpath/tclsh80.exe
+%1"
+5b2) From the Server Manager of the Enterprise Server create o new MIME
+type for .cnt with type "magnus-internal/shellcgi"
+(Alternatively one can create a special shellCGI-Directory instead,
+but I rather like to place my scripts anywhere.)
+
+6) Kill and restart your web server.
+
+You're done! Now you can use cgi.tcl.
+
+--------------------
+Examples
+--------------------
+
+The example directory has some examples. See the README in there.
+
diff --git a/web/src/cgi.tcl-1.10/mkinstalldirs b/web/src/cgi.tcl-1.10/mkinstalldirs
new file mode 100755
index 00000000..0801ec2c
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/mkinstalldirs
@@ -0,0 +1,32 @@
+#! /bin/sh
+# mkinstalldirs --- make directory hierarchy
+# Author: Noah Friedman <friedman@prep.ai.mit.edu>
+# Created: 1993-05-16
+# Last modified: 1994-03-25
+# Public domain
+
+errstatus=0
+
+for file in ${1+"$@"} ; do
+ set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
+ shift
+
+ pathcomp=
+ for d in ${1+"$@"} ; do
+ pathcomp="$pathcomp$d"
+ case "$pathcomp" in
+ -* ) pathcomp=./$pathcomp ;;
+ esac
+
+ if test ! -d "$pathcomp"; then
+ echo "mkdir $pathcomp" 1>&2
+ mkdir "$pathcomp" || errstatus=$?
+ fi
+
+ pathcomp="$pathcomp/"
+ done
+done
+
+exit $errstatus
+
+# mkinstalldirs ends here
diff --git a/web/src/cgi.tcl-1.10/pkgcreate b/web/src/cgi.tcl-1.10/pkgcreate
new file mode 100755
index 00000000..877b6cb3
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/pkgcreate
@@ -0,0 +1,9 @@
+#!/depot/path/tclsh
+# allow for earlier versions of Tcl that don't define pkg_mkIndex
+if {[catch {pkg_mkIndex . cgi.tcl}]} {
+ set f [open pkgIndex.tcl w]
+ puts $f "error \"please rebuild cgi.tcl with a modern version of Tcl (for package support)\""
+ close $f
+}
+
+
diff --git a/web/src/cgi.tcl-1.10/version.in b/web/src/cgi.tcl-1.10/version.in
new file mode 100644
index 00000000..86adc5dc
--- /dev/null
+++ b/web/src/cgi.tcl-1.10/version.in
@@ -0,0 +1,2 @@
+set versionFull @CGI_VERSION_FULL@
+set version @CGI_VERSION@
diff --git a/web/src/pubcookie/INSTALL b/web/src/pubcookie/INSTALL
new file mode 100644
index 00000000..e4685c40
--- /dev/null
+++ b/web/src/pubcookie/INSTALL
@@ -0,0 +1,90 @@
+alpine.tar.z web/src/pubcookie/INSTALL
+$id$
+/* ========================================================================
+ * Copyright 2006-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+STEPS TO ADD PUBCOOKIE SUPPORT TO WEB ALPINE
+--------------------------------------------
+
+UW Pubcookie <http://www.pubcookie.org> provides single-sign-on
+service for web-based applications. Web Alpine can be built to use UW
+Pubcookie within a Kerberos authorization framework.
+
+Building Web Alpine to use pubcookie authentication should be
+accomplished by simply adding:
+
+ --with-pubcookie
+
+and:
+
+ --with-web-bin=/usr/local/libexec/alpine/bin
+
+to the configure script's command line. Note, the value you supply in
+the second configure option is the directory where ultimately the Web
+Alpine's binary support tools will be installed. In addition,
+Kerberos 5 must be available on the Alpine web server.
+
+Installation of the extra binary components for pubcookie support
+should happen automatically. After the "make install" command typed
+in web/src directory completes successfully, verify that:
+
+ web/bin/wp_uidmapper
+ web/bin/wp_tclsh
+ web/bin/wp_gssapi_proxy
+
+all exist. Then simply follow the normal Web Alpine installation
+steps described in the web/INSTALL document.
+
+Once Web Alpine is installed, there is some additional configuration
+required. First, you'll need to change permissions on a couple of the
+binary components as they do make use of the setuid() system call. It
+should be simply a matter of:
+
+ cd /usr/local/libexec/alpine/bin
+ sudo chmod 4755 wp_gssapi_proxy wp_tclsh
+
+Next, you'll need to:
+
+ cd /usr/local/libexec/alpine/cgi/session
+
+In that directory you'll need to edit the ".htaccess" file, adding the
+lines contained in the example htaccess file in the distribution's
+"web/src/pubcookie/_htaccess_session".
+
+Then,
+
+ cd /usr/local/libexec/alpine/cgi/session
+
+and edit the ".htaccess" file therein, adding the lines contained in
+the example file "web/src/pubcookie/_htaccess_session_logout".
+
+Running Web Alpine with pubcookie requires some extra care and
+feeding. First, the service provided by "wp_uidmapper" must be
+started and maintained as long as the web server is providing Web
+Alpine service. It must be run under the same uid as the web server.
+The helper script "debug.cgi" can be used to conveniently
+start/restart the wp_uidmapper service. Make sure the path defined
+within that script is correct for your system.
+
+Finally, you'll need to create within the Kerberos 5 system the ID of
+the "IMAP Superuser". This userid is used by the web server to log
+into the UW IMAP server via SASL proxy authentication. That is, to
+establish an IMAP session, the web server logs into the IMAP server
+via Kerberos as the IMAP Superuser (which must be configured on the
+IMAP server separately) and specifies in that SASL exchange that login
+in being performed on behalf of the UW Pubcookie-provided userid.
+
+With the IMAP Superuser ID established and configured on the IMAP
+server, you'll need to acquire a Kerbero ticket on the web server.
+Typically, you'll want to install a crontab entry to periodically
+refresh the ticket. See web/src/pubcookie/README.
+
diff --git a/web/src/pubcookie/Makefile.am b/web/src/pubcookie/Makefile.am
new file mode 100644
index 00000000..e4da1c39
--- /dev/null
+++ b/web/src/pubcookie/Makefile.am
@@ -0,0 +1,37 @@
+## Process this file with automake to produce Makefile.in
+## Use aclocal -I m4; automake
+
+# ========================================================================
+# Copyright 2006-2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+
+WP_UIDMAPPER_SOCKET = "/tmp/wp_uidmapper"
+WEBSERVER_UID = 65534
+
+CFLAGS = '-DWP_UIDMAPPER_SOCKET=$(WP_UIDMAPPER_SOCKET)' \
+ '-DWEBSERVER_UID=$(WEBSERVER_UID)' \
+ '-DAUTH_GSS_PROXY_PATH="$(WEB_BINDIR)/wp_gssapi_proxy"'
+
+bin_PROGRAMS = wp_uidmapper wp_tclsh wp_gssapi_proxy wp_umc
+
+noinst_LIBRARIES = libauthgssproxy.a
+
+libauthgssproxy_a_SOURCES = auth_gss_proxy.c
+
+wp_uidmapper_SOURCES = wp_uidmapper.c id_table.c id_table.h
+
+wp_tclsh_SOURCES = wp_tclsh.c wp_uidmapper_lib.c
+
+wp_gssapi_proxy_SOURCES = wp_gssapi_proxy.c wp_uidmapper_lib.c
+
+wp_umc_SOURCES = wp_umc.c wp_uidmapper_lib.c
+
+AM_CPPFLAGS = -I@top_builddir@/include -I@top_srcdir@/include
diff --git a/web/src/pubcookie/Makefile.in b/web/src/pubcookie/Makefile.in
new file mode 100644
index 00000000..c353b8ba
--- /dev/null
+++ b/web/src/pubcookie/Makefile.in
@@ -0,0 +1,621 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# ========================================================================
+# Copyright 2006-2008 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+bin_PROGRAMS = wp_uidmapper$(EXEEXT) wp_tclsh$(EXEEXT) \
+ wp_gssapi_proxy$(EXEEXT) wp_umc$(EXEEXT)
+subdir = web/src/pubcookie
+DIST_COMMON = README $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
+ INSTALL
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/acx_pthread.m4 \
+ $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
+ $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/VERSION \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/include/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+ARFLAGS = cru
+libauthgssproxy_a_AR = $(AR) $(ARFLAGS)
+libauthgssproxy_a_LIBADD =
+am_libauthgssproxy_a_OBJECTS = auth_gss_proxy.$(OBJEXT)
+libauthgssproxy_a_OBJECTS = $(am_libauthgssproxy_a_OBJECTS)
+am__installdirs = "$(DESTDIR)$(bindir)"
+PROGRAMS = $(bin_PROGRAMS)
+am_wp_gssapi_proxy_OBJECTS = wp_gssapi_proxy.$(OBJEXT) \
+ wp_uidmapper_lib.$(OBJEXT)
+wp_gssapi_proxy_OBJECTS = $(am_wp_gssapi_proxy_OBJECTS)
+wp_gssapi_proxy_LDADD = $(LDADD)
+am_wp_tclsh_OBJECTS = wp_tclsh.$(OBJEXT) wp_uidmapper_lib.$(OBJEXT)
+wp_tclsh_OBJECTS = $(am_wp_tclsh_OBJECTS)
+wp_tclsh_LDADD = $(LDADD)
+am_wp_uidmapper_OBJECTS = wp_uidmapper.$(OBJEXT) id_table.$(OBJEXT)
+wp_uidmapper_OBJECTS = $(am_wp_uidmapper_OBJECTS)
+wp_uidmapper_LDADD = $(LDADD)
+am_wp_umc_OBJECTS = wp_umc.$(OBJEXT) wp_uidmapper_lib.$(OBJEXT)
+wp_umc_OBJECTS = $(am_wp_umc_OBJECTS)
+wp_umc_LDADD = $(LDADD)
+DEFAULT_INCLUDES =
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+SOURCES = $(libauthgssproxy_a_SOURCES) $(wp_gssapi_proxy_SOURCES) \
+ $(wp_tclsh_SOURCES) $(wp_uidmapper_SOURCES) $(wp_umc_SOURCES)
+DIST_SOURCES = $(libauthgssproxy_a_SOURCES) $(wp_gssapi_proxy_SOURCES) \
+ $(wp_tclsh_SOURCES) $(wp_uidmapper_SOURCES) $(wp_umc_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_CFLAGS = @AM_CFLAGS@
+AM_LDFLAGS = @AM_LDFLAGS@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = '-DWP_UIDMAPPER_SOCKET=$(WP_UIDMAPPER_SOCKET)' \
+ '-DWEBSERVER_UID=$(WEBSERVER_UID)' \
+ '-DAUTH_GSS_PROXY_PATH="$(WEB_BINDIR)/wp_gssapi_proxy"'
+
+CP = @CP@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+C_CLIENT_CFLAGS = @C_CLIENT_CFLAGS@
+C_CLIENT_GCCOPTLEVEL = @C_CLIENT_GCCOPTLEVEL@
+C_CLIENT_LDFLAGS = @C_CLIENT_LDFLAGS@
+C_CLIENT_SPECIALS = @C_CLIENT_SPECIALS@
+C_CLIENT_TARGET = @C_CLIENT_TARGET@
+C_CLIENT_WITH_IPV6 = @C_CLIENT_WITH_IPV6@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISPELLPROG = @ISPELLPROG@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN = @LN@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKE = @MAKE@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NPA_PROG = @NPA_PROG@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+POSUB = @POSUB@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+PWPROG = @PWPROG@
+RANLIB = @RANLIB@
+REGEX_BUILD = @REGEX_BUILD@
+RM = @RM@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPELLPROG = @SPELLPROG@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEB_BINDIR = @WEB_BINDIR@
+WEB_BUILD = @WEB_BUILD@
+WEB_PUBCOOKIE_BUILD = @WEB_PUBCOOKIE_BUILD@
+WEB_PUBCOOKIE_LIB = @WEB_PUBCOOKIE_LIB@
+WEB_PUBCOOKIE_LINK = @WEB_PUBCOOKIE_LINK@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+acx_pthread_config = @acx_pthread_config@
+alpine_interactive_spellcheck = @alpine_interactive_spellcheck@
+alpine_simple_spellcheck = @alpine_simple_spellcheck@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+WP_UIDMAPPER_SOCKET = "/tmp/wp_uidmapper"
+WEBSERVER_UID = 65534
+noinst_LIBRARIES = libauthgssproxy.a
+libauthgssproxy_a_SOURCES = auth_gss_proxy.c
+wp_uidmapper_SOURCES = wp_uidmapper.c id_table.c id_table.h
+wp_tclsh_SOURCES = wp_tclsh.c wp_uidmapper_lib.c
+wp_gssapi_proxy_SOURCES = wp_gssapi_proxy.c wp_uidmapper_lib.c
+wp_umc_SOURCES = wp_umc.c wp_uidmapper_lib.c
+AM_CPPFLAGS = -I@top_builddir@/include -I@top_srcdir@/include
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign web/src/pubcookie/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign web/src/pubcookie/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLIBRARIES:
+ -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+libauthgssproxy.a: $(libauthgssproxy_a_OBJECTS) $(libauthgssproxy_a_DEPENDENCIES)
+ -rm -f libauthgssproxy.a
+ $(libauthgssproxy_a_AR) libauthgssproxy.a $(libauthgssproxy_a_OBJECTS) $(libauthgssproxy_a_LIBADD)
+ $(RANLIB) libauthgssproxy.a
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p || test -f $$p1; \
+ then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(bindir)" && rm -f $$files
+
+clean-binPROGRAMS:
+ @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+wp_gssapi_proxy$(EXEEXT): $(wp_gssapi_proxy_OBJECTS) $(wp_gssapi_proxy_DEPENDENCIES)
+ @rm -f wp_gssapi_proxy$(EXEEXT)
+ $(LINK) $(wp_gssapi_proxy_OBJECTS) $(wp_gssapi_proxy_LDADD) $(LIBS)
+wp_tclsh$(EXEEXT): $(wp_tclsh_OBJECTS) $(wp_tclsh_DEPENDENCIES)
+ @rm -f wp_tclsh$(EXEEXT)
+ $(LINK) $(wp_tclsh_OBJECTS) $(wp_tclsh_LDADD) $(LIBS)
+wp_uidmapper$(EXEEXT): $(wp_uidmapper_OBJECTS) $(wp_uidmapper_DEPENDENCIES)
+ @rm -f wp_uidmapper$(EXEEXT)
+ $(LINK) $(wp_uidmapper_OBJECTS) $(wp_uidmapper_LDADD) $(LIBS)
+wp_umc$(EXEEXT): $(wp_umc_OBJECTS) $(wp_umc_DEPENDENCIES)
+ @rm -f wp_umc$(EXEEXT)
+ $(LINK) $(wp_umc_OBJECTS) $(wp_umc_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/auth_gss_proxy.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/id_table.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wp_gssapi_proxy.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wp_tclsh.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wp_uidmapper.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wp_uidmapper_lib.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wp_umc.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LIBRARIES) $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(bindir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-generic clean-libtool \
+ clean-noinstLIBRARIES mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-binPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \
+ clean-generic clean-libtool clean-noinstLIBRARIES ctags \
+ distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-binPROGRAMS \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags uninstall uninstall-am uninstall-binPROGRAMS
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/web/src/pubcookie/README b/web/src/pubcookie/README
new file mode 100644
index 00000000..9ca8a493
--- /dev/null
+++ b/web/src/pubcookie/README
@@ -0,0 +1,137 @@
+alpine.tar.z web/src/pubcookie/README
+/* ========================================================================
+ * Copyright 2006-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+WEB ALPINE WITH PUBCOOKIE SUPPORT
+---------------------------------
+
+UW Pubcookie <http://www.pubcookie.org> provides single-sign-on
+service for web-based applications.
+
+For building and installation comments, see web/src/pubcookie/INSTALL.
+
+
+WEB ALPINE PUBCOOKIE COMPONENTS
+-------------------------------
+
+Below are the extra binary helper applications and their descriptions
+necessary to implement UW Pubcookie authentication within Web Alpine.
+
+bin/wp_uidmapper:
+ wp_uidmapper runs in background. Keeps pubcookie
+ username <-> uid tables. Handles requests from
+ wp_tclsh and wp_gssapi_proxy through the named socket
+ /tmp/wp_uidmapper. This needs to be manually started,
+ should never stop running, and should run as the same
+ uid as the web server (nobody).
+
+bin/wp_tclsh:
+
+ wp_tclsh is a modified version of tclsh (8.0.5) that
+ does a setuid before doing the tcl stuff. The tcl
+ scripts directly run by the web server should use this
+ as their #! interpreter. If REMOTE_USER is set
+ (pubcookie in use) and the calling uid is the web
+ server (nobody), it calls wp_uidmapper to obtain its
+ destination uid. Otherwise, it just changes back to
+ the calling uid.
+
+
+bin/wp_gssapi_proxy:
+
+ wp_gssapi_proxy is called by the c-client
+ auth_gss_proxy.c routine, and does the GSSAPI/SASL
+ dance with the imap server. Looks up the username
+ corresponding to the calling uid via wp_uidmapper, and
+ will fail if the calling program is requesting access
+ to a different username's mail on the imap
+ server. Compile time options for wp_gssapi_proxy:
+
+ -DDDEBUG: outputs extra info to the syslog mail log.
+ -DNO_UIDMAPPER: calls getpwuid(getuid()) to look up
+ username of calling uid.
+
+bin/alpined
+
+ auth_gss_proxy.c is the c-client authenticator that calls
+ wp_gssapi_proxy. Stick this in the imap/src/c-client directory
+ of the pinetcl source tree. Make sure the
+ AUTH_GSS_PROXY_PATH #define points to the location of
+ the installed wp_gssapi_proxy. The following lines
+ should be added to main() function in pine/pinetcl.c:
+
+ /* put this auth_link at the beginning of the list */
+ auth_link(&auth_gss_proxy);
+ /* try to get username from REMOTE_USER (pubcookie) */
+ if(user = getenv("REMOTE_USER")) env_init(user,"/");
+
+*.tcl:
+
+ The scripts directly run by the web server must be
+ changed to point to wp_tclsh instead of the normal
+ tclsh. If for some reason you want to create a script
+ that should be run as the web server uid, use the
+ default tclsh interpreter. There is a script
+ bin/chscriptinterp, which you can run as follows to
+ change *.tcl to use /www/test/bin/wp_tclsh instead of
+ whatever they currently use.
+
+.htaccess:
+
+ AuthType UWNetID
+ AuthName "Webpine"
+ PubcookieAppId "Webpine"
+ require valid-user
+
+ NOTE: to properly scope the pubcookie cookie for the web server,
+ remove the PubcookieAppId directive
+
+logout/.htaccess:
+
+ PubcookieEndSesion redirect
+
+etc/webpine.keytab:
+
+ Should be owned by nobody.nobody with 600 permissions. A cron
+ entry for user nobody should run kinit often enough so that
+ the ticket never expires:
+
+ [root@server /]# crontab -u nobody -l
+ # DO NOT EDIT THIS FILE - edit the master and reinstall.
+ # (/var/spool/cron.new/nobody installed on Tue Dec 5 16:26:14 2000)
+ # (Cron version -- $Id: README 910 2008-01-14 22:28:38Z hubert@u.washington.edu $)
+ MAILTO=root@your-server-name
+ 0 3,11,19 * * * /usr/local/bin/kinit -k -t /www/test/etc/webpine.keytab webpine
+
+debug.cgi:
+
+ If you are having weird problems, run this via your web
+ browser, and it might help you figure things out. Runs as the web
+ server uid (nobody) and displays the following:
+
+ - output of 'klist'
+ - output of 'ps auxww |grep wp_uidmapper'
+ - the environment
+ - also lists any processes running as uids with no
+ corresponding usernames, which should tell you if your
+ pinetcl process is crashing.
+
+ It also will restart wp_uidmapper if /tmp/wp_uidmapper does not
+ exist, should that have crashed for some reason.
+
+ Finally, visit debug.cgi?stop (via the web browser) and it
+ will stop a currently running wp_uidmapper, so that you can
+ restart it in case you do something like move the location of
+ the binary.
+
+--
+$Id: README 910 2008-01-14 22:28:38Z hubert@u.washington.edu $
diff --git a/web/src/pubcookie/_htaccess_session b/web/src/pubcookie/_htaccess_session
new file mode 100644
index 00000000..24855aab
--- /dev/null
+++ b/web/src/pubcookie/_htaccess_session
@@ -0,0 +1,4 @@
+AuthType UWNetID
+AuthName "Webpine"
+PubcookieAppId "Webpine"
+require valid-user
diff --git a/web/src/pubcookie/_htaccess_session_logout b/web/src/pubcookie/_htaccess_session_logout
new file mode 100644
index 00000000..ecc269e1
--- /dev/null
+++ b/web/src/pubcookie/_htaccess_session_logout
@@ -0,0 +1,8 @@
+#
+# mod_rewrite rules to coerce secure (https) access to underlying pages
+#
+
+AddHandler cgi-script tcl
+
+PubcookieAppId "Webpine"
+PubcookieEndSession redirect
diff --git a/web/src/pubcookie/auth_gss_proxy.c b/web/src/pubcookie/auth_gss_proxy.c
new file mode 100644
index 00000000..ea5daf69
--- /dev/null
+++ b/web/src/pubcookie/auth_gss_proxy.c
@@ -0,0 +1,380 @@
+/* ========================================================================
+ * Copyright 2006-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include "../../../c-client/mail.h"
+#include "../../../c-client/misc.h"
+#include "../../../c-client/osdep.h"
+
+#define PROTOTYPE(x) x
+#include <gssapi/gssapi_generic.h>
+#include <gssapi/gssapi_krb5.h>
+
+long auth_gssapi_proxy_valid (void);
+long auth_gssapi_proxy_client (authchallenge_t challenger,
+ authrespond_t responder,
+ char *service,
+ NETMBX *mb,void *stream,unsigned long *trial,
+ char *user);
+char *auth_gssapi_proxy_server (authresponse_t responder,int argc,char *argv[]);
+
+AUTHENTICATOR auth_gss_proxy = {
+ AU_SECURE | AU_AUTHUSER, /* secure authenticator */
+ "GSSAPI", /* authenticator name */
+ auth_gssapi_proxy_valid, /* check if valid */
+ auth_gssapi_proxy_client, /* client method */
+ auth_gssapi_proxy_server, /* server method */
+ NIL /* next authenticator */
+};
+
+#define AUTH_GSSAPI_P_NONE 1
+#define AUTH_GSSAPI_P_INTEGRITY 2
+#define AUTH_GSSAPI_P_PRIVACY 4
+
+#define AUTH_GSSAPI_C_MAXSIZE 8192
+
+#define SERVER_LOG(x,y) syslog (LOG_ALERT,x,y)
+
+extern char *krb5_defkeyname; /* sneaky way to get this name */
+
+
+/* Placate const declarations */
+
+static gss_OID auth_gss_proxy_mech;
+static gss_OID_set auth_gss_proxy_mech_set;
+
+/* Check if GSSAPI valid on this system
+ * Returns: T if valid, NIL otherwise
+ */
+
+long auth_gssapi_proxy_valid (void)
+{
+ char *s,tmp[MAILTMPLEN];
+ OM_uint32 smn;
+ gss_buffer_desc buf;
+ gss_name_t name;
+ struct stat sbuf;
+ sprintf (tmp,"host@%s",mylocalhost ());
+ buf.length = strlen (buf.value = tmp) + 1;
+ memcpy (&auth_gss_proxy_mech,&gss_mech_krb5,sizeof (gss_OID));
+ memcpy (&auth_gss_proxy_mech_set,&gss_mech_set_krb5,sizeof (gss_OID_set));
+ /* see if can build a name */
+ if (gss_import_name (&smn,&buf,gss_nt_service_name,&name) != GSS_S_COMPLETE)
+ return NIL; /* failed */
+ if ((s = strchr (krb5_defkeyname,':')) && stat (++s,&sbuf))
+ auth_gss_proxy.server = NIL; /* can't do server if no keytab */
+ gss_release_name (&smn,&name);/* finished with name */
+ return LONGT;
+}
+
+/* Proxy client authenticator (mihodge)
+ * Accepts: challenger function
+ * responder function
+ * parsed network mailbox structure
+ * stream argument for functions
+ * pointer to current trial count
+ * returned user name
+ * Returns: T if success, NIL otherwise, number of trials incremented if retry
+ */
+
+#ifndef AUTH_GSS_PROXY_PATH
+#define AUTH_GSS_PROXY_PATH "/usr/local/libexec/alpine/bin/wp_gssapi_proxy"
+#endif
+#define AUTH_GSS_PROXY_MESSAGE 1
+#define AUTH_GSS_PROXY_READ 2
+#define AUTH_GSS_PROXY_SUCCESS 3
+#define AUTH_GSS_PROXY_WRITE 4
+#define AUTH_GSS_PROXY_WRITE_NIL 5
+#define AUTH_GSS_PROXY_EXEC_FAILED 6
+
+static unsigned long read_full(int fd,void *buf,unsigned long size) {
+ unsigned long total,s;
+ for(total = 0; total < size; total += s) {
+ s = read(fd,(char*)buf + total,size - total);
+ if(s == -1) {
+ if((errno == EAGAIN) || (errno == EINTR)) s = 0;
+ else return -1;
+ } else if(s == 0) break;
+ }
+ return total;
+}
+
+static unsigned long write_full(int fd,void *buf,unsigned long size) {
+ unsigned long total,s;
+ for(total = 0; total < size; total += s) {
+ s = write(fd,(char*)buf + total,size - total);
+ if(s == -1) {
+ if((errno == EAGAIN) || (errno == EINTR)) s = 0;
+ else return -1;
+ }
+ }
+ return total;
+}
+
+long auth_gssapi_proxy_client (authchallenge_t challenger,
+ authrespond_t responder,
+ char *service,
+ NETMBX *mb,void *stream,unsigned long *trial,
+ char *user)
+{
+ char err[MAILTMPLEN];
+ int status, ipipe[2], opipe[2];
+ unsigned long len,cmd[2],maxlogintrials;
+ char *buf;
+ pid_t pid;
+ long ret = NIL;
+
+ imap_parameters(GET_MAXLOGINTRIALS, (void *) &maxlogintrials);
+ *trial = maxlogintrials + 1;
+ err[0] = 0;
+ strcpy (user,mb->user[0] ? mb->user : myusername ());
+
+ /* open pipe to gss proxy process */
+ if(pipe(ipipe)) {
+ sprintf (err,"auth_gss_proxy: create pipe error: %s",strerror(errno));
+ } else if(pipe(opipe)) {
+ sprintf (err,"auth_gss_proxy: create pipe error: %s",strerror(errno));
+ close(ipipe[0]);
+ close(ipipe[1]);
+ } else if((pid = fork()) == -1) {
+ sprintf (err,"auth_gss_proxy: fork error: %s",strerror(errno));
+ close(ipipe[0]);
+ close(ipipe[1]);
+ close(opipe[0]);
+ close(opipe[1]);
+
+ } else if(pid == 0) { /* child process */
+ close(ipipe[0]);
+ close(opipe[1]);
+ dup2(opipe[0],0);
+ dup2(ipipe[1],1);
+ close(opipe[0]);
+ close(ipipe[1]);
+
+ /* a little overloading of the "err" field here */
+ sprintf (err,"%s@%s",service,mb->host);
+ execlp(AUTH_GSS_PROXY_PATH,AUTH_GSS_PROXY_PATH,err,user,0);
+
+ /* tell parent that exec failed and exit */
+ cmd[0] = AUTH_GSS_PROXY_EXEC_FAILED;
+ cmd[1] = 0;
+ write_full(1,cmd,sizeof(cmd));
+ exit(-1);
+
+ } else { /* parent process */
+ close(ipipe[1]);
+ close(opipe[0]);
+
+ while(len = read_full(ipipe[0],cmd,sizeof(cmd))) {
+ if (len == -1) {
+ sprintf (err,"auth_gss_proxy: read error: %s",strerror(errno));
+ break;
+ } else if (len != sizeof(cmd)) {
+ sprintf (err,"auth_gss_proxy: read error: %lu out of %lu",
+ len,sizeof(cmd));
+ break;
+
+ } else if(cmd[0] == AUTH_GSS_PROXY_EXEC_FAILED) {
+ sprintf (err,"auth_gss_proxy: could not spawn proxy process");
+ break;
+
+ } else if(cmd[0] == AUTH_GSS_PROXY_MESSAGE) {
+ if(cmd[1]) {
+ buf = fs_get(cmd[1]);
+ len = read_full(ipipe[0],buf,cmd[1]);
+ if(len == -1) {
+ sprintf (err,"auth_gss_proxy: read error: %s",strerror(errno));
+ break;
+ } else if(len != cmd[1]) {
+ sprintf (err,"auth_gss_proxy: read error: %lu out of %lu",
+ len,cmd[1]);
+ break;
+ } else {
+ mm_log(buf,WARN);
+ }
+ fs_give ((void **) &buf);
+ }
+
+ } else if(cmd[0] == AUTH_GSS_PROXY_READ) {
+ buf = (*challenger) (stream,&len);
+ if(!buf) len = 0;
+ if(write_full(opipe[1],&len,sizeof(len)) == -1) {
+ sprintf (err,"auth_gss_proxy: write error: %s",strerror(errno));
+ break;
+ } else if (buf && (write_full(opipe[1],buf,len) == -1)) {
+ sprintf (err,"auth_gss_proxy: write error: %s",strerror(errno));
+ break;
+ }
+ if(buf) fs_give ((void **) &buf);
+
+ } else if(cmd[0] == AUTH_GSS_PROXY_SUCCESS) {
+ ret = T;
+
+ } else if(cmd[0] == AUTH_GSS_PROXY_WRITE) {
+ if(cmd[1]) {
+ buf = fs_get(cmd[1]);
+ len = read_full(ipipe[0],buf,cmd[1]);
+ if(len == -1) {
+ sprintf (err,"auth_gss_proxy: read error: %s",strerror(errno));
+ break;
+ } else if(len != cmd[1]) {
+ sprintf (err,"auth_gss_proxy: read error: %lu out of %lu",
+ len,cmd[1]);
+ break;
+ } else {
+ (*responder) (stream,buf,cmd[1]);
+ }
+ fs_give ((void **) &buf);
+ } else {
+ (*responder) (stream,"",0);
+ }
+
+ } else if(cmd[0] == AUTH_GSS_PROXY_WRITE_NIL) {
+ (*responder) (stream,NIL,0);
+
+ } else {
+ sprintf (err,"auth_gss_proxy: unknown command: %lu",cmd[0]);
+ break;
+ }
+ }
+
+ /* close pipes and wait for process to die */
+ close(ipipe[0]);
+ waitpid(pid,&status,0);
+ close(opipe[1]);
+ }
+
+ if(err[0]) mm_log(err,WARN);
+ return ret;
+}
+
+/* Server authenticator
+ * Accepts: responder function
+ * argument count
+ * argument vector
+ * Returns: authenticated user name or NIL
+ */
+
+char *auth_gssapi_proxy_server (authresponse_t responder,int argc,char *argv[])
+{
+ char *ret = NIL;
+ char *s,tmp[MAILTMPLEN];
+ unsigned long maxsize = htonl (AUTH_GSSAPI_C_MAXSIZE);
+ int conf;
+ OM_uint32 smj,smn,dsmj,dsmn,flags;
+ OM_uint32 mctx = 0;
+ gss_name_t crname,name;
+ gss_OID mech;
+ gss_buffer_desc chal,resp,buf;
+ gss_cred_id_t crd;
+ gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
+ gss_qop_t qop = GSS_C_QOP_DEFAULT;
+ /* make service name */
+ sprintf (tmp,"%s@%s",(char *) mail_parameters (NIL,GET_SERVICENAME,NIL),
+ tcp_serverhost ());
+ buf.length = strlen (buf.value = tmp) + 1;
+ /* acquire credentials */
+ if ((gss_import_name (&smn,&buf,gss_nt_service_name,&crname)) ==
+ GSS_S_COMPLETE) {
+ if ((smj = gss_acquire_cred (&smn,crname,0,auth_gss_proxy_mech_set,GSS_C_ACCEPT,
+ &crd,NIL,NIL)) == GSS_S_COMPLETE) {
+ if (resp.value = (*responder) ("",0,(unsigned long *) &resp.length)) {
+ do { /* negotiate authentication */
+ smj = gss_accept_sec_context (&smn,&ctx,crd,&resp,
+ GSS_C_NO_CHANNEL_BINDINGS,&name,&mech,
+ &chal,&flags,NIL,NIL);
+ /* don't need response any more */
+ fs_give ((void **) &resp.value);
+ switch (smj) { /* how did it go? */
+ case GSS_S_COMPLETE: /* successful */
+ /* paranoia */
+ if (memcmp (mech->elements,auth_gss_proxy_mech->elements,mech->length))
+ fatal ("GSSAPI is bogus");
+ case GSS_S_CONTINUE_NEEDED:
+ if (chal.value) { /* send challenge, get next response */
+ resp.value = (*responder) (chal.value,chal.length,
+ (unsigned long *) &resp.length);
+ gss_release_buffer (&smn,&chal);
+ }
+ break;
+ }
+ }
+ while (resp.value && resp.length && (smj == GSS_S_CONTINUE_NEEDED));
+
+ /* successful exchange? */
+ if ((smj == GSS_S_COMPLETE) &&
+ (gss_display_name (&smn,name,&buf,&mech) == GSS_S_COMPLETE)) {
+ /* extract authentication ID from principal */
+ if (s = strchr ((char *) buf.value,'@')) *s = '\0';
+ /* send security and size */
+ memcpy (resp.value = tmp,(void *) &maxsize,resp.length = 4);
+ tmp[0] = AUTH_GSSAPI_P_NONE;
+ if (gss_wrap (&smn,ctx,NIL,qop,&resp,&conf,&chal) == GSS_S_COMPLETE){
+ resp.value = (*responder) (chal.value,chal.length,
+ (unsigned long *) &resp.length);
+ gss_release_buffer (&smn,&chal);
+ if (gss_unwrap (&smn,ctx,&resp,&chal,&conf,&qop)==GSS_S_COMPLETE) {
+ /* client request valid */
+ if (chal.value && (chal.length > 4) && (chal.length < MAILTMPLEN)
+ && memcpy (tmp,chal.value,chal.length) &&
+ (tmp[0] & AUTH_GSSAPI_P_NONE)) {
+ /* tie off authorization ID */
+ tmp[chal.length] = '\0';
+ if (authserver_login (tmp+4,buf.value,argc,argv) ||
+ authserver_login (lcase (tmp+4),buf.value,argc,argv))
+ ret = myusername ();
+ }
+ /* done with user name */
+ gss_release_buffer (&smn,&chal);
+ }
+ /* finished with response */
+ fs_give ((void **) &resp.value);
+ }
+ /* don't need name buffer any more */
+ gss_release_buffer (&smn,&buf);
+ }
+ /* don't need client name any more */
+ gss_release_name (&smn,&name);
+ /* don't need context any more */
+ if (ctx != GSS_C_NO_CONTEXT) gss_delete_sec_context (&smn,&ctx,NIL);
+ }
+ /* finished with credentials */
+ gss_release_cred (&smn,&crd);
+ }
+
+ else { /* can't acquire credentials! */
+ if (gss_display_name (&dsmn,crname,&buf,&mech) == GSS_S_COMPLETE)
+ SERVER_LOG ("Failed to acquire credentials for %s",buf.value);
+ if (smj != GSS_S_FAILURE) do
+ switch (dsmj = gss_display_status (&dsmn,smj,GSS_C_GSS_CODE,
+ GSS_C_NO_OID,&mctx,&resp)) {
+ case GSS_S_COMPLETE:
+ mctx = 0;
+ case GSS_S_CONTINUE_NEEDED:
+ SERVER_LOG ("Unknown GSSAPI failure: %s",resp.value);
+ gss_release_buffer (&dsmn,&resp);
+ }
+ while (dsmj == GSS_S_CONTINUE_NEEDED);
+ do switch (dsmj = gss_display_status (&dsmn,smn,GSS_C_MECH_CODE,
+ GSS_C_NO_OID,&mctx,&resp)) {
+ case GSS_S_COMPLETE:
+ case GSS_S_CONTINUE_NEEDED:
+ SERVER_LOG ("GSSAPI mechanism status: %s",resp.value);
+ gss_release_buffer (&dsmn,&resp);
+ }
+ while (dsmj == GSS_S_CONTINUE_NEEDED);
+ }
+ /* finished with credentials name */
+ gss_release_name (&smn,&crname);
+ }
+ return ret; /* return status */
+}
diff --git a/web/src/pubcookie/debug.cgi b/web/src/pubcookie/debug.cgi
new file mode 100755
index 00000000..dd998380
--- /dev/null
+++ b/web/src/pubcookie/debug.cgi
@@ -0,0 +1,58 @@
+#!/usr/bin/perl
+$| = 1;
+$wp_uidmapper_bin = "/usr/local/libexec/alpine/bin/wp_uidmapper";
+$wp_uidmapper_socket = "/tmp/wp_uidmapper";
+
+print "Content-type: text/plain\n\n";
+print "klist:\n";
+system("/usr/local/bin/klist");
+print "\n";
+
+#if($ENV{'QUERY_STRING'} eq 'stop') {
+# foreach $line (ps("axww")) {
+# ($line =~ m/^(\d+).*wp_uidmapper/) && kill(15,$1);
+# }
+# sleep(1);
+# if(-e $wp_uidmapper_socket) {
+# print "Could not kill wp_uidmapper\n";
+# exit(1);
+# }
+# print "wp_uidmapper stopped\n";
+# exit 0;
+#}
+
+if (! -e $wp_uidmapper_socket) {
+ print "Yikes! need to spawn new wp_uidmapper process\n";
+ if(!fork()) {
+ close(STDIN);
+ close(STDOUT);
+ close(STDERR);
+ setpgrp(0,0);
+ exec("$wp_uidmapper_bin 60000-64999");
+ }
+ sleep 1;
+}
+
+@ps = ps("auxww");
+print "wp_uidmapper:\n";
+foreach $line (@ps) { ($line =~ m/wp_uidmapper/) && print $line; }
+
+print "\nEnvironment:\n";
+foreach $key (sort { $a cmp $b } keys(%ENV)) {
+ print "$key: $ENV{$key}\n";
+}
+
+print "\nAlpine user processes:\n";
+foreach $line (@ps) { ($line =~ m/^\#/) && print $line; }
+exit 0;
+
+sub ps {
+ my ($args) = @_;
+ my (@a);
+ open(PS,"ps $args|") || return;
+ @a = <PS>;
+ close(PS);
+ return @a;
+}
+
+
diff --git a/web/src/pubcookie/id_table.c b/web/src/pubcookie/id_table.c
new file mode 100644
index 00000000..1076524e
--- /dev/null
+++ b/web/src/pubcookie/id_table.c
@@ -0,0 +1,294 @@
+#include "id_table.h"
+#include "wp_uidmapper_lib.h"
+
+#include <sys/types.h> /* opendir */
+#include <sys/stat.h> /* stat */
+#include <stdlib.h> /* mallloc,free,strtol */
+#include <string.h> /* memcpy */
+#include <errno.h> /* errno */
+
+#include <dirent.h> /* opendir */
+#include <unistd.h> /* stat */
+
+unsigned long hash_func(char *string,unsigned long num_buckets) {
+ unsigned long i;
+ char *p;
+ for(i = 0, p = string; *p; p++) i = (9 * i) + *p;
+ return i % num_buckets;
+}
+
+struct id_table_entry {
+ struct id_table_entry **pself;
+ struct id_table_entry *next;
+ int id;
+ char tmp;
+ char name[1];
+};
+
+struct key_hash_entry {
+ unsigned key[WP_KEY_LEN];
+ id_table_entry *e;
+ struct key_hash_entry **pself;
+ struct key_hash_entry *next;
+};
+
+id_table_range *id_table_range_new(char *str) {
+ id_table_range *rhead,*rtail;
+ char *p,*pn;
+ long s,e;
+
+ rhead = rtail = 0;
+ for(p = str; *p; p = *pn ? pn + 1 : pn) {
+ s = strtoul(p,&pn,0);
+ if(pn > p) {
+ if(*pn == '-') {
+ p = pn + 1;
+ e = strtoul(p,&pn,0);
+ if(pn == p) e = s;
+ } else {
+ e = s;
+ }
+
+ if(rtail) {
+ rtail->next = (id_table_range*)malloc(sizeof(id_table_range));
+ rtail = rtail->next;
+ } else {
+ rhead = rtail = (id_table_range*)malloc(sizeof(id_table_range));
+ }
+ rtail->start = s;
+ rtail->end = e;
+ }
+ }
+ if(rtail) rtail->next = 0;
+ return rhead;
+}
+
+void id_table_range_delete(id_table_range *range) {
+ id_table_range *r;
+ while(r = range) {
+ range = range->next;
+ free(r);
+ }
+}
+
+int id_table_init(id_table *t,id_table_range *range) {
+ id_table_range *r;
+ unsigned long array_end,u;
+
+ if(!range) return -1;
+ t->array_start = range->start;
+ array_end = range->end;
+ for(r = range->next; r; r = r->next) {
+ if(r->start < t->array_start) t->array_start = r->start;
+ if(r->end > array_end) array_end = r->end;
+ }
+ if(t->array_start >= array_end) return -1;
+ t->array_size = array_end + 1 - t->array_start;
+ t->hash_size = t->array_size * 2 + 1;
+ t->key_hash_size = t->array_size * 2 + 1;
+
+ t->array = (id_table_entry**)malloc(sizeof(id_table_entry*) * t->array_size);
+ t->hash = (id_table_entry**)malloc(sizeof(id_table_entry*) * t->hash_size);
+ t->key_hash = (key_hash_entry**)malloc(sizeof(key_hash_entry *) * t->key_hash_size);
+ if(!t->hash || !t->array || !t->key_hash) {
+ if(t->hash) free(t->hash);
+ if(t->key_hash) free(t->key_hash);
+ if(t->array) free(t->array);
+ return -1;
+ }
+ for(u = 0; u < t->array_size; u++) t->array[u] = (id_table_entry*)-1;
+ t->max_fill = 0;
+ for(r = range; r; r = r->next) {
+ t->max_fill += r->end + 1 - r->start;
+ for(u = r->start; u <= r->end; u++) t->array[u - t->array_start] = 0;
+ }
+ bzero(t->hash,sizeof(id_table_entry*) * t->hash_size);
+ bzero(t->key_hash, sizeof(key_hash_entry*) * t->key_hash_size);
+
+ t->array_ptr = 0;
+ t->fill = 0;
+ return 0;
+}
+
+void id_table_destroy(id_table *t) {
+ unsigned long u;
+ for(u = 0; u < t->array_size; u++)
+ if(t->array[u] && (t->array[u] != (id_table_entry*)-1)) free(t->array[u]);
+ free(t->hash);
+ free(t->array);
+}
+
+int id_table_create_id(id_table *t,char *name,unsigned *key) {
+ id_table_entry **pe,*e;
+ unsigned long size;
+
+ if(name && strlen(name)){
+ for(pe = t->hash + hash_func(name,t->hash_size); *pe; pe = &(*pe)->next)
+ if(!strcmp(name,(*pe)->name)){
+ if(key[0]){
+ if((e = key_hash_get_entry(t, key)) == NULL){
+ if(key_hash_create_entry(t, *pe, key) == NULL){
+ return -1;
+ }
+ }
+ else if(e->id != (*pe)->id){
+ return -1;
+ }
+ }
+
+ return (*pe)->id;
+ }
+
+ /* no matching name found */
+ }
+ else {
+ if(e = key_hash_get_entry(t,key))
+ return e->id;
+
+ /* MUST have seen name/key pair at least once */
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* need to add new entry */
+ if(t->fill == t->max_fill) {
+ id_table_remove_stale(t);
+ if(t->fill == t->max_fill) {
+ errno = ENOSPC;
+ return -1;
+ }
+ for(pe = t->hash + hash_func(name,t->hash_size); *pe; pe = &(*pe)->next);
+ }
+ size = strlen(name);
+ e = (id_table_entry*)malloc(sizeof(id_table_entry) + size);
+ if(!e) return -1;
+
+ while(t->array[t->array_ptr])
+ t->array_ptr = (t->array_ptr + 1) % t->array_size;
+
+ e->pself = pe;
+ e->next = 0;
+ e->id = (int)(t->array_ptr + t->array_start);
+ memcpy(e->name,name,size + 1);
+ if(key[0] && key_hash_create_entry(t, e,key) == NULL)
+ return -1;
+
+ *pe = e;
+ t->array[t->array_ptr] = e;
+ t->array_ptr = (t->array_ptr + 1) % t->array_size;
+ t->fill++;
+ return e->id;
+}
+
+static id_table_entry *id_table_get_entry(id_table *t,unsigned long id) {
+ if(id >= t->array_start) {
+ id -= t->array_start;
+ if(id < t->array_size)
+ if(t->array[id] && (t->array[id] != (id_table_entry*)-1))
+ return t->array[id];
+ }
+ return 0;
+}
+
+char *id_table_get_name(id_table *t,int id) {
+ id_table_entry *e = id_table_get_entry(t,(unsigned long)id);
+ return e ? e->name : 0;
+}
+
+int id_table_remove_stale(id_table *t) {
+ id_table_entry *e;
+ unsigned long u;
+ DIR *d;
+ struct dirent *de;
+ struct stat st;
+ char path[NAME_MAX + 7];
+
+ /*
+ * flag all uids inactive
+ */
+
+ for(u = 0; u < t->array_size; u++)
+ if(t->array[u] && (t->array[u] != (id_table_entry*)-1))
+ t->array[u]->tmp = 0;
+
+ /*
+ * go through list of processes, finding active uids
+ */
+
+ memcpy(path,"/proc/",6);
+ if(d = opendir("/proc")) {
+ while(de = readdir(d)) {
+ strcpy(path + 6,de->d_name);
+ if(!stat(path,&st)) {
+ if(e = id_table_get_entry(t,(unsigned long)st.st_uid)) e->tmp = 1;
+ }
+ }
+ closedir(d);
+ }
+
+ /*
+ * remove ones still marked inactive
+ */
+
+ for(u = 0; u < t->array_size; u++) {
+ e = t->array[u];
+ if(e && (e != (id_table_entry*)-1)) {
+ if(!e->tmp) {
+ if(e->next) e->next->pself = e->pself;
+ *e->pself = e->next;
+ t->array[u] = 0;
+ t->array_ptr = u;
+ t->fill--;
+ key_hash_delete_keys(t, e);
+ free(e);
+ }
+ }
+ }
+ return 0;
+}
+
+id_table_entry *key_hash_get_entry(id_table *t, unsigned *key) {
+ key_hash_entry **ke;
+
+ for(ke = t->key_hash + (key[0] % t->key_hash_size); *ke; ke = &(*ke)->next)
+ if(key[0] && (*ke)->key[0] == key[0] && !memcmp(key,(*ke)->key,WP_KEY_LEN * sizeof(unsigned)))
+ return (*ke)->e;
+
+ return NULL;
+}
+
+key_hash_entry *key_hash_create_entry(id_table *t, id_table_entry *pe, unsigned *key) {
+ key_hash_entry **ke, *kep;
+
+ for(ke = &t->key_hash[key[0] % t->key_hash_size]; *ke; ke = &(*ke)->next)
+ if(!memcmp(key,(*ke)->key,WP_KEY_LEN * sizeof(unsigned)))
+ return NULL;
+
+ if(kep = malloc(sizeof(key_hash_entry))){
+ memcpy(kep->key,key,(WP_KEY_LEN * sizeof(unsigned int)));
+ kep->e = pe;
+ kep->pself = ke;
+ kep->next = NULL;
+ *ke = kep;
+ }
+
+ return kep;
+}
+
+void key_hash_delete_keys(id_table *t, id_table_entry *e) {
+ key_hash_entry *kp, *kd;
+ int u;
+
+ for(u = 0; u < t->key_hash_size; u++){
+ for(kp = t->key_hash[u]; kp; )
+ if (kp->e == e){
+ kd = kp;
+ if(kp->next) kp->next->pself = kp->pself;
+ *kp->pself = kp->next;
+ kp = kp->next;
+ free(kd);
+ }
+ else
+ kp = kp->next;
+ }
+}
diff --git a/web/src/pubcookie/id_table.h b/web/src/pubcookie/id_table.h
new file mode 100644
index 00000000..2ba3f2b4
--- /dev/null
+++ b/web/src/pubcookie/id_table.h
@@ -0,0 +1,58 @@
+/* ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef _ID_TABLE_H_
+#define _ID_TABLE_H_
+
+struct id_table_entry;
+typedef struct id_table_entry id_table_entry;
+
+struct key_hash_entry;
+typedef struct key_hash_entry key_hash_entry;
+
+typedef struct id_table_range {
+ unsigned long start;
+ unsigned long end;
+ struct id_table_range *next;
+} id_table_range;
+
+typedef struct id_table {
+ id_table_entry **array;
+ unsigned long array_start;
+ unsigned long array_size;
+ unsigned long array_ptr;
+
+ id_table_entry **hash;
+ unsigned long hash_size;
+
+ key_hash_entry **key_hash;
+ unsigned long key_hash_size;
+
+ unsigned long max_fill;
+ unsigned long fill;
+} id_table;
+
+id_table_range *id_table_range_new(char *str);
+void id_table_range_delete(id_table_range *range);
+
+int id_table_init(id_table *t,id_table_range *range);
+void id_table_destroy(id_table *t);
+
+int id_table_create_id(id_table *t,char *name,unsigned int *key);
+char *id_table_get_name(id_table *t,int id);
+int id_table_remove_stale(id_table *t);
+
+id_table_entry *key_hash_get_entry(id_table *, unsigned *key);
+key_hash_entry *key_hash_create_entry(id_table *, id_table_entry *pe, unsigned *key);
+void key_hash_delete_keys(id_table *t, id_table_entry *e);
+
+#endif
diff --git a/web/src/pubcookie/wp_gssapi_proxy.c b/web/src/pubcookie/wp_gssapi_proxy.c
new file mode 100644
index 00000000..f23d6098
--- /dev/null
+++ b/web/src/pubcookie/wp_gssapi_proxy.c
@@ -0,0 +1,412 @@
+/* ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+/* #define PROTOTYPE(x) x */
+
+#include <system.h>
+#include <general.h>
+
+#include "wp_uidmapper_lib.h"
+
+#include <gssapi/gssapi_generic.h>
+#include <gssapi/gssapi_krb5.h>
+
+#define AUTH_GSS_PROXY_MESSAGE 1
+#define AUTH_GSS_PROXY_READ 2
+#define AUTH_GSS_PROXY_SUCCESS 3
+#define AUTH_GSS_PROXY_WRITE 4
+#define AUTH_GSS_PROXY_WRITE_NIL 5
+
+#define AUTH_GSSAPI_P_NONE 1
+#define AUTH_GSSAPI_P_INTEGRITY 2
+#define AUTH_GSSAPI_P_PRIVACY 4
+
+#ifdef NO_UIDMAPPER
+int get_calling_username(int uid,char *name,int namelen) {
+ struct passwd *pw;
+ unsigned long len;
+
+ pw = getpwuid(uid);
+ if(!pw) return -1;
+ len = strlen(pw->pw_name);
+ if(len >= namelen) len = namelen - 1;
+ memcpy(name,pw->pw_name,len);
+ name[len] = 0;
+ return len;
+}
+#else
+#define get_calling_username wp_uidmapper_getname
+#endif
+
+static unsigned long read_full(int fd,void *buf,unsigned long size) {
+ unsigned long total,s;
+ for(total = 0; total < size; total += s) {
+ s = read(fd,(char*)buf + total,size - total);
+ if(s == -1) {
+ if((errno == EAGAIN) || (errno == EINTR)) s = 0;
+ else return -1;
+ } else if(s == 0) break;
+ }
+ return total;
+}
+
+static unsigned long write_full(int fd,void *buf,unsigned long size) {
+ unsigned long total,s;
+ for(total = 0; total < size; total += s) {
+ s = write(fd,(char*)buf + total,size - total);
+ if(s == -1) {
+ if((errno == EAGAIN) || (errno == EINTR)) s = 0;
+ else return -1;
+ }
+ }
+ return total;
+}
+
+int cmd_message(char *str1, ...) {
+ va_list list;
+ unsigned long cmd[2],size;
+ char *str;
+
+ for(size = 0,str = str1,va_start(list,str1); str; str = va_arg(list,char*))
+ size += strlen(str);
+ va_end(list);
+
+ cmd[0] = AUTH_GSS_PROXY_MESSAGE;
+ cmd[1] = size;
+ if(write_full(1,cmd,sizeof(cmd)) == -1) return -1;
+ for(str = str1, va_start(list,str1); str; str = va_arg(list,char*))
+ if(size = strlen(str)) if(write_full(1,str,size) == -1) {
+ va_end(list);
+ return -1;
+ }
+ va_end(list);
+ return 0;
+}
+
+int cmd_read(gss_buffer_desc *pbuf) {
+ unsigned long cmd[2],len,size;
+ void *buf;
+
+ cmd[0] = AUTH_GSS_PROXY_READ;
+ cmd[1] = 0;
+ if(write_full(1,cmd,sizeof(cmd)) == -1) return -1;
+
+ len = read_full(0,&size,sizeof(size));
+ if(len != sizeof(size)) return -1;
+ if(size == 0) {
+ pbuf->value = 0;
+ pbuf->length = 0;
+ return 0;
+ }
+
+ buf = malloc(size);
+ len = read_full(0,buf,size);
+ if(len != size) {
+ free(buf);
+ return -1;
+ }
+ pbuf->value = buf;
+ pbuf->length = size;
+ return 0;
+}
+
+int cmd_success() {
+ unsigned long cmd[2];
+ cmd[0] = AUTH_GSS_PROXY_SUCCESS;
+ cmd[1] = 0;
+ if(write_full(1,cmd,sizeof(cmd)) == -1) return -1;
+ return 0;
+}
+
+int cmd_write(gss_buffer_desc *buf) {
+ unsigned long cmd[2];
+ cmd[0] = AUTH_GSS_PROXY_WRITE;
+ cmd[1] = buf->length;
+ if(write_full(1,cmd,sizeof(cmd)) == -1) return -1;
+ if(buf->length) if(write_full(1,buf->value,buf->length) == -1) return -1;
+ return 0;
+}
+
+int cmd_write_nil() {
+ unsigned long cmd[2];
+ cmd[0] = AUTH_GSS_PROXY_WRITE_NIL;
+ cmd[1] = 0;
+ if(write_full(1,cmd,sizeof(cmd)) == -1) return -1;
+ return 0;
+}
+
+/*
+ * service principal in argv[1]
+ * reqested username in argv[2]
+ */
+
+int main(int argc,char *argv[])
+{
+ char *user = 0;
+ char userbuf[WP_BUF_SIZE];
+ char *prog;
+ OM_uint32 smj,smn,dsmn,mctx;
+ gss_name_t crname = GSS_C_NO_NAME;
+ gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
+ gss_buffer_desc chal,resp,buf;
+ int conf,i;
+ gss_qop_t qop;
+ gss_OID oid;
+ int calling_uid,eff_uid;
+
+ if((prog = strrchr(argv[0], '/')) == NULL)
+ prog = argv[0];
+ else
+ prog++;
+
+ openlog(prog,LOG_PID,LOG_MAIL);
+
+ calling_uid = getuid();
+ eff_uid = geteuid();
+#ifdef DEBUG
+ syslog(LOG_INFO,"uid = %d, euid=%d\n",calling_uid,eff_uid);
+#endif
+
+#ifdef WEBSERVER_UID
+ /* if euid != uid, change to web server user */
+ if(calling_uid != eff_uid) if(setuid(WEBSERVER_UID)) {
+ syslog(LOG_ERR,"setuid ((%d != %d) -> %d) failed: %s",
+ calling_uid,eff_uid,WEBSERVER_UID,strerror(errno));
+ cmd_write_nil();
+ goto cleanup;
+ }
+#endif
+
+ if(argc < 2) {
+ syslog(LOG_WARNING,"not enough arguments");
+ cmd_write_nil();
+ goto cleanup;
+ }
+ if(get_calling_username(calling_uid,userbuf,WP_BUF_SIZE) == -1) {
+ syslog(LOG_WARNING,"cannot determine calling username");
+ cmd_write_nil();
+ goto cleanup;
+ }
+ if(argc == 2) {
+ user = userbuf;
+#ifdef DEBUG
+ syslog(LOG_INFO,"calling=%s\n",user);
+#endif
+ } else if(argc > 2) {
+ user = argv[2];
+#ifdef DEBUG
+ syslog(LOG_INFO,"requested=%s calling=%s\n",user,userbuf);
+#endif
+#ifndef NO_NAME_CHECK
+ if(strcmp(user,userbuf)) {
+ syslog(LOG_WARNING,"cannot act on behalf of user %s (%s)",user,userbuf);
+ cmd_write_nil();
+ goto cleanup;
+ }
+#endif
+ }
+
+ /* expect empty challenge from server */
+ if(cmd_read(&chal)) {
+ syslog(LOG_WARNING,"cmd_read[initial] failed");
+ goto cleanup;
+ } else if(chal.length) {
+ free(chal.value);
+ syslog(LOG_WARNING,"cmd_read[initial] not empty");
+ goto cleanup;
+ }
+
+ /*
+ * obtain credentials for requested service
+ */
+
+ buf.value = argv[1];
+ buf.length = strlen(argv[1]);
+ if(gss_import_name (&smn,&buf,gss_nt_service_name,&crname) !=
+ GSS_S_COMPLETE) {
+ syslog(LOG_WARNING,"gss_import_name(%s) failed",buf.value);
+ cmd_write_nil();
+ goto cleanup;
+ }
+
+ /* initial init_sec_context call, and send data */
+ memcpy(&oid,&gss_mech_krb5,sizeof(oid));
+ smj = gss_init_sec_context
+ (&smn,GSS_C_NO_CREDENTIAL,&ctx,crname,oid,
+ GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,0,GSS_C_NO_CHANNEL_BINDINGS,
+ GSS_C_NO_BUFFER,0,&resp,0,0);
+ if((smj == GSS_S_COMPLETE) || (smj == GSS_S_CONTINUE_NEEDED)) {
+ i = cmd_write(&resp);
+ gss_release_buffer (&smn,&resp);
+ if(i) {
+ syslog(LOG_WARNING,"cmd_write[init_sec_context] failed");
+ goto cleanup;
+ }
+ }
+
+ /* loop until init_sec_context is done */
+ while(smj == GSS_S_CONTINUE_NEEDED) {
+ if(cmd_read(&chal)) {
+ syslog(LOG_WARNING,"cmd_read[init_sec_context] failed");
+ goto cleanup;
+ } else if(!chal.length) {
+ syslog(LOG_WARNING,"cmd_read[init_sec_context] empty");
+ goto cleanup;
+ } else {
+ smj = gss_init_sec_context (&smn,GSS_C_NO_CREDENTIAL,&ctx,crname,
+ GSS_C_NO_OID,
+ GSS_C_MUTUAL_FLAG|GSS_C_REPLAY_FLAG,0,
+ GSS_C_NO_CHANNEL_BINDINGS,&chal,0,
+ &resp,0,0);
+ if(chal.value) free(chal.value);
+ if((smj == GSS_S_COMPLETE) || (smj == GSS_S_CONTINUE_NEEDED)) {
+ i = cmd_write(&resp);
+ gss_release_buffer (&smn,&resp);
+ if(i) {
+ syslog(LOG_WARNING,"cmd_write[init_sec_context] failed");
+ goto cleanup;
+ }
+ }
+ }
+ }
+
+ switch(smj) {
+ case GSS_S_COMPLETE:
+ /* get challenge and unwrap it */
+ if(cmd_read(&chal)) {
+ syslog(LOG_WARNING,"cmd_read[gss_unwrap] failed");
+ goto cleanup;
+ }
+ smj = gss_unwrap (&smn,ctx,&chal,&resp,&conf,&qop);
+ if(chal.value) free(chal.value);
+ if(smj != GSS_S_COMPLETE) {
+ syslog(LOG_WARNING,"gss_unwrap failed");
+ cmd_write_nil();
+ goto cleanup;
+ } else if(resp.length < 4) {
+ syslog(LOG_WARNING,"challenge too short");
+ gss_release_buffer (&smn,&resp);
+ cmd_write_nil();
+ goto cleanup;
+ } else if(!( ((char*)resp.value)[0] & AUTH_GSSAPI_P_NONE)) {
+ syslog(LOG_WARNING,"invalid challenge");
+ gss_release_buffer (&smn,&resp);
+ cmd_write_nil();
+ goto cleanup;
+ }
+
+ /* prepare response to challenge */
+ buf.length = 4 + (user ? strlen(user) : 0);
+ buf.value = malloc(buf.length);
+ memcpy (buf.value,resp.value,4);
+ gss_release_buffer (&smn,&resp);
+ *(char*)buf.value = AUTH_GSSAPI_P_NONE;
+ if(user) memcpy((char*)buf.value + 4, user, buf.length - 4);
+
+ /* wrap response and send */
+ smj = gss_wrap (&smn,ctx,0,qop,&buf,&conf,&resp);
+ free(buf.value);
+ if(smj != GSS_S_COMPLETE) {
+ syslog(LOG_WARNING,"gss_unwrap failed");
+ cmd_write_nil();
+ goto cleanup;
+ }
+ i = cmd_write(&resp);
+ gss_release_buffer (&smn,&resp);
+ if(i) {
+ syslog(LOG_WARNING,"cmd_write[gss_wrap] failed");
+ goto cleanup;
+ }
+
+ /* success! */
+ if(cmd_success()) syslog(LOG_WARNING,"cmd_success failed");
+ goto cleanup;
+
+ case GSS_S_CREDENTIALS_EXPIRED:
+#ifdef DEBUG
+ syslog(LOG_INFO,"Kerberos credentials expired (try running kinit)");
+#endif
+ if(cmd_message("Kerberos credentials expired (try running kinit)",0)) {
+ syslog(LOG_WARNING,"cmd_message[credentials expired] failed");
+ goto cleanup;
+ }
+ cmd_write_nil();
+ goto cleanup;
+
+ case GSS_S_FAILURE:
+ if (smn == (OM_uint32) KRB5_FCC_NOFILE) {
+#ifdef DEBUG
+ syslog(LOG_INFO,"No credentials cache found (try running kinit)");
+#endif
+ if(cmd_message("No credentials cache found (try running kinit)",0)) {
+ syslog(LOG_WARNING,"cmd_message[no cache file found] failed");
+ goto cleanup;
+ }
+ } else {
+ mctx = 0;
+ do {
+ gss_display_status (&dsmn,smn,GSS_C_MECH_CODE,
+ GSS_C_NO_OID,&mctx,&resp);
+#ifdef DEBUG
+ syslog(LOG_INFO,"GSSAPI failure: %s",resp.value);
+#endif
+ i = cmd_message("GSSAPI failure: ",resp.value,0);
+ gss_release_buffer (&dsmn,&resp);
+ if(i) {
+ syslog(LOG_WARNING,"cmd_message[failure] failed");
+ goto cleanup;
+ }
+ } while(mctx);
+ }
+ cmd_write_nil();
+ goto cleanup;
+
+ default:
+ mctx = 0;
+ do {
+ gss_display_status (&dsmn,smn,GSS_C_GSS_CODE,
+ GSS_C_NO_OID,&mctx,&resp);
+#ifdef DEBUG
+ syslog(LOG_INFO,"GSSAPI failure: %s",resp.value);
+#endif
+ i = cmd_message("Unknown GSSAPI failure: ",resp.value,0);
+ gss_release_buffer (&dsmn,&resp);
+ if(i) {
+ syslog(LOG_WARNING,"cmd_message[unknown failure] failed");
+ goto cleanup;
+ }
+ } while(mctx);
+ if(!mctx) do {
+ gss_display_status (&dsmn,smn,GSS_C_MECH_CODE,
+ GSS_C_NO_OID,&mctx,&resp);
+#ifdef DEBUG
+ syslog(LOG_INFO,"GSSAPI mechanism status: %s",resp.value);
+#endif
+ i = cmd_message("GSSAPI mechanism status: ",resp.value,0);
+ gss_release_buffer (&dsmn,&resp);
+ if(i) {
+ syslog(LOG_WARNING,"cmd_message[unknown failure 2] failed");
+ goto cleanup;
+ }
+ } while(mctx);
+ cmd_write_nil();
+ goto cleanup;
+ }
+
+ cleanup:
+ if(ctx != GSS_C_NO_CONTEXT) gss_delete_sec_context (&smn,&ctx,0);
+ if(crname != GSS_C_NO_NAME) gss_release_name (&smn,&crname);
+ closelog();
+ exit(0);
+ return 0;
+}
diff --git a/web/src/pubcookie/wp_tclsh.c b/web/src/pubcookie/wp_tclsh.c
new file mode 100644
index 00000000..e7fe14a7
--- /dev/null
+++ b/web/src/pubcookie/wp_tclsh.c
@@ -0,0 +1,189 @@
+/*
+ * tclAppInit.c --
+ *
+ * Provides a default version of the main program and Tcl_AppInit
+ * procedure for Tcl applications (without Tk).
+ *
+ * Copyright (c) 1993 The Regents of the University of California.
+ * Copyright (c) 1994-1997 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id: tclAppInit.c,v 1.4 1999/02/03 02:58:26 stanton Exp $
+ */
+
+/* ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifdef TCL_XT_TEST
+#include <X11/Intrinsic.h>
+#endif
+
+#include "tcl.h"
+
+/********** (start mihodge) *************************************/
+#include "wp_uidmapper_lib.h"
+#include <errno.h>
+#include <stdlib.h> /* getenv */
+/********** (end mihodge) *************************************/
+
+/*
+ * The following variable is a special hack that is needed in order for
+ * Sun shared libraries to be used for Tcl.
+ */
+
+extern int matherr();
+int *tclDummyMathPtr = (int *) matherr;
+
+
+#ifdef TCL_TEST
+extern int Procbodytest_Init _ANSI_ARGS_((Tcl_Interp *interp));
+extern int Procbodytest_SafeInit _ANSI_ARGS_((Tcl_Interp *interp));
+extern int TclObjTest_Init _ANSI_ARGS_((Tcl_Interp *interp));
+extern int Tcltest_Init _ANSI_ARGS_((Tcl_Interp *interp));
+#endif /* TCL_TEST */
+#ifdef TCL_XT_TEST
+extern int Tclxttest_Init _ANSI_ARGS_((Tcl_Interp *interp));
+#endif
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * main --
+ *
+ * This is the main program for the application.
+ *
+ * Results:
+ * None: Tcl_Main never returns here, so this procedure never
+ * returns either.
+ *
+ * Side effects:
+ * Whatever the application does.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+main(argc, argv)
+ int argc; /* Number of command-line arguments. */
+ char **argv; /* Values of command-line arguments. */
+{
+ /********** PUBCOOKIE-Specific Inclusion **************************/
+ char *user,sessid[WP_BUF_SIZE],*cookie;
+ int uid;
+ unsigned key[WP_KEY_LEN];
+
+ memset((void *) key, 0, sizeof(unsigned int) * WP_KEY_LEN);
+ sessid[0] = '\0';
+ user = getenv("REMOTE_USER");
+ if(!((((cookie = getenv("QUERY_STRING"))
+ && wp_parse_cookie(cookie,"sessid","&@% ",sessid,WP_BUF_SIZE))
+ || ((cookie = getenv("HTTP_COOKIE"))
+ && wp_parse_cookie(cookie,"sessid",";@ ",sessid,WP_BUF_SIZE)))
+ && wp_sessid2key(sessid,key)))
+ cookie = NULL;
+
+ if((getuid() == WEBSERVER_UID) && (user || cookie)){
+ if(wp_uidmapper_getuid(user ? user : "",key,&uid) == -1) {
+ fprintf(stderr,"wp_uidmapper_getname(%s,%s) failed\n",user ? user : "",sessid);
+ return 1;
+ } else if(setuid(uid)) {
+ fprintf(stderr,"setuid(%i) failed: %s\n",uid,strerror(errno));
+ return 1;
+ }
+ } else {
+ setuid(getuid());
+ }
+ /********** (end PUBCOOKIE) *************************************/
+
+#ifdef TCL_XT_TEST
+ XtToolkitInitialize();
+#endif
+ Tcl_Main(argc, argv, Tcl_AppInit);
+ return 0; /* Needed only to prevent compiler warning. */
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_AppInit --
+ *
+ * This procedure performs application-specific initialization.
+ * Most applications, especially those that incorporate additional
+ * packages, will have their own version of this procedure.
+ *
+ * Results:
+ * Returns a standard Tcl completion code, and leaves an error
+ * message in interp->result if an error occurs.
+ *
+ * Side effects:
+ * Depends on the startup script.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_AppInit(interp)
+ Tcl_Interp *interp; /* Interpreter for application. */
+{
+ if (Tcl_Init(interp) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+
+#ifdef TCL_TEST
+#ifdef TCL_XT_TEST
+ if (Tclxttest_Init(interp) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+#endif
+ if (Tcltest_Init(interp) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+ Tcl_StaticPackage(interp, "Tcltest", Tcltest_Init,
+ (Tcl_PackageInitProc *) NULL);
+ if (TclObjTest_Init(interp) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+ if (Procbodytest_Init(interp) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+ Tcl_StaticPackage(interp, "procbodytest", Procbodytest_Init,
+ Procbodytest_SafeInit);
+#endif /* TCL_TEST */
+
+ /*
+ * Call the init procedures for included packages. Each call should
+ * look like this:
+ *
+ * if (Mod_Init(interp) == TCL_ERROR) {
+ * return TCL_ERROR;
+ * }
+ *
+ * where "Mod" is the name of the module.
+ */
+
+ /*
+ * Call Tcl_CreateCommand for application-specific commands, if
+ * they weren't already created by the init procedures called above.
+ */
+
+ /*
+ * Specify a user-specific startup file to invoke if the application
+ * is run interactively. Typically the startup file is "~/.apprc"
+ * where "app" is the name of the application. If this line is deleted
+ * then no user-specific startup file will be run under any conditions.
+ */
+
+ Tcl_SetVar(interp, "tcl_rcFileName", "~/.tclshrc", TCL_GLOBAL_ONLY);
+ return TCL_OK;
+}
diff --git a/web/src/pubcookie/wp_uidmapper.c b/web/src/pubcookie/wp_uidmapper.c
new file mode 100644
index 00000000..662e9b3a
--- /dev/null
+++ b/web/src/pubcookie/wp_uidmapper.c
@@ -0,0 +1,312 @@
+/* ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "id_table.h"
+#include "wp_uidmapper_lib.h"
+
+
+/* Makefile should #define:
+ *
+ * WP_UIDMAPPER_SOCKET
+ */
+
+unsigned long send_full(int fd,void *buf,unsigned long len,int flags) {
+ unsigned long total,i;
+ for(total = 0; total < len; total += i) {
+ i = send(fd,(char*)buf + total, len - total,flags);
+ if(i == -1) return -1;
+ }
+ return total;
+}
+
+static char *socketname = WP_UIDMAPPER_SOCKET;
+static int socketname_remove = 0;
+
+void socketname_cleanup(void) {
+ unlink(socketname);
+}
+
+void quit_handler(int signal) {
+ exit(signal);
+}
+
+int main(int argc, char *argv[]) {
+ extern char *optarg;
+ extern int optind, opterr, optopt;
+
+ int debug,log_opt;
+ mode_t sockmode;
+
+ id_table_range *range;
+ id_table table;
+ struct sockaddr_un sun,rsun;
+ struct sigaction sa;
+ int is_err,i,ssock,uid;
+ unsigned int kbuf[WP_KEY_LEN];
+ char rbuf[WP_BUF_SIZE],cbuf[WP_BUF_SIZE],rcmd;
+ struct msghdr rmh,smh;
+ struct iovec riov[3],siov[1];
+#ifndef DGRAM_MODE
+ int csock,rsun_len;
+#endif
+ struct ucred cred;
+
+ /*
+ * process command line arguments
+ */
+
+ debug = 0;
+ log_opt = 0;
+ sockmode = 0600;
+
+ for(is_err = 0; !is_err && ((i = getopt(argc,argv,"dlrm:s:u:")) != -1); ) {
+ switch(i) {
+ case 'd': debug++; break;
+ case 'l': log_opt |= LOG_PERROR; break;
+ case 'm': sockmode = strtol(optarg,NULL,0); break;
+ case 'r': socketname_remove = 1; break;
+ case 's': socketname = optarg; break;
+ case 'u': umask(strtol(optarg,NULL,0)); break;
+ case '?': is_err = 1; break;
+ }
+ }
+ if((optind + 1) == argc) {
+ range = id_table_range_new(argv[optind]);
+ if(!range) is_err = 1;
+ } else {
+ is_err = 1;
+ }
+ if(is_err) {
+ fprintf(stderr,"Usage: uidmapper [-d] [-l] [-r] [-m mode] [-s socketname] [-u umask] uidranges\n");
+ exit(1);
+ }
+
+ /*
+ * main initialization
+ */
+
+ openlog(argv[0],log_opt,LOG_MAIL);
+
+ if(id_table_init(&table,range)) {
+ syslog(LOG_ERR,"could not initialize tables: %s\n",strerror(errno));
+ exit(1);
+ }
+ id_table_range_delete(range);
+
+ sa.sa_handler = quit_handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction(SIGHUP,&sa,0);
+ sigaction(SIGINT,&sa,0);
+ sigaction(SIGQUIT,&sa,0);
+ sigaction(SIGTERM,&sa,0);
+
+ if(socketname_remove)
+ socketname_cleanup();
+
+ /*
+ * open socket
+ */
+
+#ifdef DGRAM_MODE
+ ssock = socket(AF_UNIX,SOCK_DGRAM,0);
+#else
+ ssock = socket(AF_UNIX,SOCK_STREAM,0);
+#endif
+ if(ssock < 0) {
+ syslog(LOG_ERR,"%s: socket: %s\n",socketname,strerror(errno));
+ exit(1);
+ }
+ /* sun.sun_len = strlen(socketname) + 1; */
+ sun.sun_family = AF_UNIX;
+ strcpy(sun.sun_path,socketname);
+ if(bind(ssock,(struct sockaddr*)&sun,sizeof(sun))) {
+ syslog(LOG_ERR,"%s: bind: %s\n",socketname,strerror(errno));
+ exit(1);
+ }
+ atexit(socketname_cleanup);
+ chmod(ssock,sockmode);
+
+#ifndef DGRAM_MODE
+ if(listen(ssock,8)) {
+ syslog(LOG_ERR,"%s: listen: %s\n",socketname,strerror(errno));
+ exit(1);
+ }
+#endif
+
+#ifdef DGRAM_MODE
+ if(debug >= 1) syslog(LOG_INFO,"SOCK_DGRAM socket opened");
+#else
+ if(debug >= 1) syslog(LOG_INFO,"SOCK_STREAM socket opened");
+#endif
+
+ /*
+ * accept commands
+ */
+
+ riov[0].iov_base = &rcmd;
+ riov[0].iov_len = 1;
+ riov[1].iov_base = kbuf;
+ riov[1].iov_len = WP_KEY_LEN * sizeof(unsigned int);
+ riov[2].iov_base = rbuf;
+ riov[2].iov_len = WP_BUF_SIZE - 1;
+ rmh.msg_name = &rsun;
+ rmh.msg_namelen = sizeof(rsun);
+ rmh.msg_iov = riov;
+ rmh.msg_iovlen = 3;
+ rmh.msg_control = cbuf;
+ rmh.msg_controllen = riov[0].iov_len + riov[1].iov_len + riov[2].iov_len;
+ rmh.msg_flags = 0;
+
+ /* siov[0].iov_base */
+ /* siov[0].iov_len */
+ smh.msg_name = NULL;
+ smh.msg_namelen = 0;
+ smh.msg_iov = siov;
+ /* smh.msg_iovlen */
+ smh.msg_control = NULL;
+ smh.msg_controllen = 0;
+ smh.msg_flags = 0;
+
+#ifndef DGRAM_MODE
+ csock = -1;
+#endif
+
+ while(1) {
+#ifdef DGRAM_MODE
+ i = recvmsg(ssock,&rmh,0);
+#else
+ if(csock >= 0) close(csock);
+ rsun.sun_family = AF_UNIX;
+ rsun_len = sizeof(rsun);
+ csock = accept(ssock,(struct sockaddr*)&rsun,&rsun_len);
+ if(csock == -1) {
+ syslog(LOG_ERR,"accept: %s\n",strerror(errno));
+ break;
+ }
+ if(debug >= 2) {
+ i = sizeof(cred);
+ if(getsockopt(csock,SOL_SOCKET,SO_PEERCRED,&cred,&i) == -1) {
+ syslog(LOG_INFO,"getsockopt(SO_PEERCRED) failed: %s",strerror(errno));
+ } else {
+ syslog(LOG_INFO,"connection from pid=%i uid=%i gid=%i\n",
+ cred.pid,cred.uid,cred.gid);
+ }
+ }
+ i = recvmsg(csock,&rmh,0);
+#endif
+
+ if(i == -1) {
+ syslog(LOG_ERR,"recvmsg: %s\n",strerror(errno));
+ break;
+ }
+ if(debug >= 2)
+ syslog(LOG_INFO,"recvd datagram [size=%i] from %s [size=%i]\n",
+ i,rsun.sun_path,rmh.msg_namelen);
+
+ /* check that datagram is well formed */
+ if(i < 1) {
+ syslog(LOG_WARNING,"recv: recvd datagram that is too small\n");
+ continue;
+ }
+ i--;
+#ifdef DGRAM_MODE
+ smh.msg_name = rmh.msg_name;
+ smh.msg_namelen = rmh.msg_namelen;
+#endif
+
+ if(rcmd == 'u') {
+ if(!i) {
+ syslog(LOG_WARNING,"recv: recvd 'u' datagram with no payload\n");
+ continue;
+ }
+ rbuf[i - (WP_KEY_LEN * sizeof(unsigned int))] = 0;
+ uid = id_table_create_id(&table,rbuf,kbuf);
+ if(debug >= 1) syslog(LOG_INFO,"request uid(%s) = %i\n",rbuf,uid);
+ if(uid == -1) {
+ char sbuf[2 * WP_BUF_SIZE],*sep = strerror(errno);
+ sprintf(sbuf,"id_table_create_id(%s,[",rbuf);
+ for(i = 0; i < WP_KEY_LEN; i++)
+ sprintf(sbuf + strlen(sbuf), "%u,", kbuf[i]);
+
+ sprintf(sbuf + strlen(sbuf) - 1, "]): %s\n",sep);
+ syslog(LOG_ERR,sbuf);
+ /* break; hobble along rather than die */
+ }
+
+ siov[0].iov_base = &uid;
+ siov[0].iov_len = sizeof(uid);
+ smh.msg_iovlen = 1;
+#ifdef DGRAM_MODE
+ if(sendmsg(ssock,&smh,0) == -1)
+#else
+ if(sendmsg(csock,&smh,0) == -1)
+#endif
+ syslog(LOG_WARNING,"sendmsg: %s\n",strerror(errno));
+
+ } else if(rcmd == 'n') {
+ if(i != sizeof(uid)) {
+ syslog(LOG_WARNING,"recv: recvd 'n' datagram with invalid payload\n");
+ continue;
+ }
+
+ memcpy(&uid,kbuf,sizeof(uid));
+ siov[0].iov_base = id_table_get_name(&table,uid);
+ if(debug >= 1)
+ syslog(LOG_INFO,"request name(%d) = %s\n",uid,siov[0].iov_base);
+
+ if(siov[0].iov_base) {
+ siov[0].iov_len = strlen(siov[0].iov_base);
+ smh.msg_iovlen = 1;
+ } else {
+ smh.msg_iovlen = 0;
+ }
+#ifdef DGRAM_MODE
+ if(sendmsg(ssock,&smh,0) == -1)
+#else
+ if(sendmsg(csock,&smh,0) == -1)
+#endif
+ syslog(LOG_WARNING,"sendmsg: %s\n",strerror(errno));
+
+ } else if(rcmd == 'c') {
+ if(debug >= 1) syslog(LOG_INFO,"request clear()");
+ if(id_table_remove_stale(&table)) {
+ syslog(LOG_WARNING,"id_table_remove_stale: %s\n",strerror(errno));
+ break;
+ }
+ }
+#ifdef HONOR_QUIT
+ else if(rcmd == 'q') {
+ if(debug >= 1) syslog(LOG_INFO,"quit requested");
+ break;
+ }
+#endif
+ else {
+ syslog(LOG_WARNING,"invalid request received");
+ }
+ }
+#ifndef DGRAM_MODE
+ if(csock >= 0) close(csock);
+#endif
+
+ /*
+ * clean up (never really get this far)
+ */
+
+ id_table_destroy(&table);
+ close(ssock);
+ exit(0);
+ return 0;
+}
diff --git a/web/src/pubcookie/wp_uidmapper_lib.c b/web/src/pubcookie/wp_uidmapper_lib.c
new file mode 100644
index 00000000..ddaf5175
--- /dev/null
+++ b/web/src/pubcookie/wp_uidmapper_lib.c
@@ -0,0 +1,178 @@
+/* ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "wp_uidmapper_lib.h"
+
+
+static char *xdigits = "0123456789ABCDEF";
+
+static int do_handshake(char *sockname,
+ struct iovec *out,int outlen,int *routbytes,
+ struct iovec *in,int inlen,int *rinbytes) {
+ int sock,i;
+ struct msghdr mh;
+ struct sockaddr_un sun;
+
+#ifdef DGRAM_MODE
+ sock = socket(AF_UNIX,SOCK_DGRAM,0);
+#else
+ sock = socket(AF_UNIX,SOCK_STREAM,0);
+#endif
+ if(sock < 0) return -1;
+
+ sun.sun_family = AF_UNIX;
+ strcpy(sun.sun_path,sockname);
+ if(connect(sock,(struct sockaddr*)&sun,sizeof(sun))) return -1;
+
+ mh.msg_name = NULL;
+ mh.msg_namelen = 0;
+ mh.msg_iov = out;
+ mh.msg_iovlen = outlen;
+ mh.msg_control = NULL;
+ mh.msg_controllen = 0;
+ mh.msg_flags = 0;
+
+ if((i = sendmsg(sock,&mh,0)) == -1) {
+ close(sock);
+ return -1;
+ }
+ if(routbytes) *routbytes = i;
+
+ if(in) {
+ mh.msg_iov = in;
+ mh.msg_iovlen = inlen;
+ mh.msg_flags = 0;
+ if((i = recvmsg(sock,&mh,0)) == -1) {
+ close(sock);
+ return -1;
+ }
+ if(rinbytes) *rinbytes = i;
+ }
+ close(sock);
+ return 0;
+}
+
+int wp_uidmapper_getuid(char *name,unsigned int *key,int *puid) {
+ int uid,outbytes,inbytes;
+ char cmd;
+ struct iovec out[3],in[1];
+
+ cmd = 'u';
+ out[0].iov_base = &cmd;
+ out[0].iov_len = 1;
+ out[1].iov_base = key;
+ out[1].iov_len = WP_KEY_LEN * sizeof(unsigned int);
+ out[2].iov_base = name ? name : "";
+ out[2].iov_len = name ? strlen(name) : 0;
+ in[0].iov_base = &uid;
+ in[0].iov_len = sizeof(uid);
+
+ if(do_handshake(WP_UIDMAPPER_SOCKET,out,3,&outbytes,in,1,&inbytes))
+ return -1;
+ if((outbytes != (1 + out[1].iov_len) + out[2].iov_len) || (inbytes != in[0].iov_len))
+ return -1;
+ *puid = uid;
+ return 0;
+}
+
+int wp_uidmapper_getname(int uid,char *name,int namelen) {
+ int outbytes,inbytes;
+ char cmd;
+ struct iovec out[2],in[1];
+
+ cmd = 'n';
+ out[0].iov_base = &cmd;
+ out[0].iov_len = 1;
+ out[1].iov_base = &uid;
+ out[1].iov_len = sizeof(uid);
+ in[0].iov_base = name;
+ in[0].iov_len = namelen - 1;
+
+ if(do_handshake(WP_UIDMAPPER_SOCKET,out,2,&outbytes,in,1,&inbytes))
+ return -1;
+ if(outbytes != (1 + out[1].iov_len)) return -1;
+ name[inbytes] = 0;
+ return 0;
+}
+
+int wp_uidmapper_clear() {
+ int outbytes;
+ char cmd;
+ struct iovec out[1];
+
+ cmd = 'c';
+ out[0].iov_base = &cmd;
+ out[0].iov_len = 1;
+
+ if(do_handshake(WP_UIDMAPPER_SOCKET,out,1,&outbytes,NULL,0,NULL)) return -1;
+ if(outbytes != 1) return -1;
+ return 0;
+}
+
+int wp_uidmapper_quit() {
+ int outbytes;
+ char cmd;
+ struct iovec out[1];
+
+ cmd = 'q';
+ out[0].iov_base = &cmd;
+ out[0].iov_len = 1;
+
+ if(do_handshake(WP_UIDMAPPER_SOCKET,out,1,&outbytes,NULL,0,NULL)) return -1;
+ if(outbytes != 1) return -1;
+ return 0;
+}
+
+int wp_sessid2key(char *ids,unsigned int *ida) {
+ int i, j, k;
+ unsigned int n;
+ char *p;
+
+ i = j = 0;
+ while(1){
+ n = 0;
+ for(k = 0; k < 8; k++)
+ if((p = strchr(xdigits, toupper((int) ids[j++]))) != NULL)
+ n = (n << 4) | (p - xdigits);
+ else
+ return(0);
+
+ ida[i] = n;
+ if(++i == WP_KEY_LEN)
+ return(ids[j] == '\0');
+ else if(ids[j++] != '.')
+ return(0);
+ }
+}
+
+int wp_parse_cookie (char *cookie,char *cname,char *terms,char *cvalue,int vmax) {
+ char *p, *q;
+ int i;
+
+ if((p = strstr(cookie, cname)) != NULL){
+ p += strlen(cname) + 1; /* skip cname and equals */
+ q = cvalue;
+ while(*p && !strchr(terms, *p)){
+ *q++ = *p++;
+ if((q - cvalue) >= vmax)
+ return(0);
+ }
+
+ *q = '\0';
+ return(1);
+ }
+
+ return(0);
+}
diff --git a/web/src/pubcookie/wp_uidmapper_lib.h b/web/src/pubcookie/wp_uidmapper_lib.h
new file mode 100644
index 00000000..e1832454
--- /dev/null
+++ b/web/src/pubcookie/wp_uidmapper_lib.h
@@ -0,0 +1,25 @@
+/* ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef _WP_UIDMAPPER_LIB_H_
+#define _WP_UIDMAPPER_LIB_H_
+
+int wp_uidmapper_getuid(char *name,unsigned int *key,int *puid);
+int wp_uidmapper_getname(int uid,char *name,int namelen);
+int wp_uidmapper_clear();
+int wp_uidmapper_quit();
+int wp_sessid2key(char *ids,unsigned int *ida);
+
+#define WP_BUF_SIZE 1024
+#define WP_KEY_LEN 6
+
+#endif
diff --git a/web/src/pubcookie/wp_umc.c b/web/src/pubcookie/wp_umc.c
new file mode 100644
index 00000000..a55d2127
--- /dev/null
+++ b/web/src/pubcookie/wp_umc.c
@@ -0,0 +1,63 @@
+/* ========================================================================
+ * Copyright 2006 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "wp_uidmapper_lib.h"
+
+
+int main(int argc,char *argv[]) {
+ char name[WP_BUF_SIZE],sessid[WP_BUF_SIZE];
+ int uid,i,key[WP_KEY_LEN];
+
+ if(argc >= 2) {
+ if(*argv[1] == 'u') {
+ if(argc >= 3) {
+ memset(key,0,WP_KEY_LEN * sizeof(int));
+ if(argc >= 4){
+ if(wp_parse_cookie(argv[3],"sessid",";@",sessid,WP_BUF_SIZE))
+ (void)wp_sessid2key(sessid,key);
+ }
+
+ if(wp_uidmapper_getuid((argv[2][0] == '\0') ? NULL : argv[2],key,&uid) != -1) {
+ printf("uid = %i\n",uid);
+ return 0;
+ } else fprintf(stderr,"wp_uidmapper_getuid error: %s\n",
+ strerror(errno));
+ }
+ } else if(*argv[1] == 'n') {
+ if(argc >= 3) {
+ i = wp_uidmapper_getname(strtol(argv[2],NULL,0),name,WP_BUF_SIZE);
+ if(i != -1) {
+ printf("name = %s\n",name);
+ return 0;
+ } else fprintf(stderr,"wp_uidmapper_getname error: %s\n",
+ strerror(errno));
+ }
+ } else if(*argv[1] == 'c') {
+ if(wp_uidmapper_clear() != -1) {
+ printf("clear command sent\n");
+ return 0;
+ } else fprintf(stderr,"wp_uidmapper_clear error: %s\n",
+ strerror(errno));
+ } else if(*argv[1] == 'q') {
+ if(wp_uidmapper_quit() != -1) {
+ printf("quit command sent\n");
+ return 0;
+ } else fprintf(stderr,"wp_uidmapper_quit error: %s\n",
+ strerror(errno));
+ }
+ }
+ fprintf(stderr,"Usage: wp_umc [u name [key] ] [n uid] [c] [q]\n");
+ return -1;
+}